summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2011-06-24 13:49:54 -0700
committerRobert Mustacchi <rm@joyent.com>2011-06-24 13:49:54 -0700
commit68396ea9c0fe4f75ce30b1eba2c44c43c13344bb (patch)
tree802587d411d9db461e6500c5b635043315f81c27
downloadillumos-kvm-cmd-68396ea9c0fe4f75ce30b1eba2c44c43c13344bb.tar.gz
Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
-rw-r--r--.gitignore64
-rw-r--r--.gitmodules6
-rw-r--r--CODING_STYLE81
-rw-r--r--COPYING339
-rw-r--r--COPYING.LIB504
-rw-r--r--Changelog574
-rw-r--r--HACKING125
-rw-r--r--LICENSE18
-rw-r--r--MAINTAINERS489
-rw-r--r--Makefile377
-rw-r--r--Makefile.dis23
-rw-r--r--Makefile.hw24
-rw-r--r--Makefile.objs322
-rw-r--r--Makefile.target412
-rw-r--r--Makefile.user23
-rw-r--r--QMP/README88
-rw-r--r--QMP/qmp-events.txt266
-rwxr-xr-xQMP/qmp-shell259
-rw-r--r--QMP/qmp-spec.txt272
-rw-r--r--QMP/qmp.py131
-rw-r--r--README3
-rw-r--r--TODO37
-rw-r--r--VERSION1
-rw-r--r--a.out.h430
-rw-r--r--acl.c185
-rw-r--r--acl.h74
-rw-r--r--aes.c1314
-rw-r--r--aes.h26
-rw-r--r--aio.c230
-rw-r--r--alpha-dis.c1920
-rw-r--r--alpha.ld127
-rw-r--r--arch_init.c726
-rw-r--r--arch_init.h34
-rw-r--r--arm-dis.c4136
-rw-r--r--arm-semi.c488
-rw-r--r--arm.ld153
-rw-r--r--async.c216
-rw-r--r--audio/alsaaudio.c1260
-rw-r--r--audio/audio.c2066
-rw-r--r--audio/audio.h166
-rw-r--r--audio/audio_int.h282
-rw-r--r--audio/audio_pt_int.c175
-rw-r--r--audio/audio_pt_int.h22
-rw-r--r--audio/audio_template.h560
-rw-r--r--audio/audio_win_int.c108
-rw-r--r--audio/audio_win_int.h10
-rw-r--r--audio/coreaudio.c549
-rw-r--r--audio/dsound_template.h293
-rw-r--r--audio/dsoundaudio.c1030
-rw-r--r--audio/esdaudio.c557
-rw-r--r--audio/fmodaudio.c687
-rw-r--r--audio/mixeng.c360
-rw-r--r--audio/mixeng.h51
-rw-r--r--audio/mixeng_template.h152
-rw-r--r--audio/noaudio.c173
-rw-r--r--audio/ossaudio.c944
-rw-r--r--audio/paaudio.c525
-rw-r--r--audio/rate_template.h111
-rw-r--r--audio/sdlaudio.c459
-rw-r--r--audio/spiceaudio.c345
-rw-r--r--audio/wavaudio.c262
-rw-r--r--audio/wavcapture.c161
-rw-r--r--audio/winwaveaudio.c723
-rw-r--r--balloon.c148
-rw-r--r--balloon.h33
-rwxr-xr-xbin/qemu-system-x86_64bin0 -> 7169848 bytes
-rw-r--r--block-migration.c732
-rw-r--r--block-migration.h23
-rw-r--r--block.c2946
-rw-r--r--block.h299
-rw-r--r--block/blkdebug.c471
-rw-r--r--block/blkverify.c383
-rw-r--r--block/bochs.c230
-rw-r--r--block/cloop.c171
-rw-r--r--block/cow.c324
-rw-r--r--block/curl.c564
-rw-r--r--block/dmg.c312
-rw-r--r--block/nbd.c221
-rw-r--r--block/parallels.c157
-rw-r--r--block/qcow.c975
-rw-r--r--block/qcow2-cache.c314
-rw-r--r--block/qcow2-cluster.c975
-rw-r--r--block/qcow2-refcount.c1166
-rw-r--r--block/qcow2-snapshot.c451
-rw-r--r--block/qcow2.c1397
-rw-r--r--block/qcow2.h242
-rw-r--r--block/qed-check.c210
-rw-r--r--block/qed-cluster.c154
-rw-r--r--block/qed-gencb.c32
-rw-r--r--block/qed-l2-cache.c173
-rw-r--r--block/qed-table.c319
-rw-r--r--block/qed.c1372
-rw-r--r--block/qed.h301
-rw-r--r--block/raw-posix-aio.h43
-rw-r--r--block/raw-posix.c1446
-rw-r--r--block/raw-win32.c430
-rw-r--r--block/raw.c156
-rw-r--r--block/rbd.c1059
-rw-r--r--block/rbd_types.h71
-rw-r--r--block/sheepdog.c2037
-rw-r--r--block/vdi.c961
-rw-r--r--block/vmdk.c870
-rw-r--r--block/vpc.c654
-rw-r--r--block/vvfat.c2888
-rw-r--r--block_int.h265
-rw-r--r--blockdev.c800
-rw-r--r--blockdev.h70
-rw-r--r--bsd-user/bsd-mman.h121
-rw-r--r--bsd-user/bsdload.c202
-rw-r--r--bsd-user/elfload.c1577
-rw-r--r--bsd-user/errno_defs.h149
-rw-r--r--bsd-user/freebsd/strace.list171
-rw-r--r--bsd-user/freebsd/syscall_nr.h373
-rw-r--r--bsd-user/i386/syscall.h161
-rw-r--r--bsd-user/i386/target_signal.h20
-rw-r--r--bsd-user/main.c1131
-rw-r--r--bsd-user/mmap.c559
-rw-r--r--bsd-user/netbsd/strace.list145
-rw-r--r--bsd-user/netbsd/syscall_nr.h373
-rw-r--r--bsd-user/openbsd/strace.list187
-rw-r--r--bsd-user/openbsd/syscall_nr.h225
-rw-r--r--bsd-user/qemu-types.h24
-rw-r--r--bsd-user/qemu.h394
-rw-r--r--bsd-user/signal.c38
-rw-r--r--bsd-user/sparc/syscall.h9
-rw-r--r--bsd-user/sparc/target_signal.h27
-rw-r--r--bsd-user/sparc64/syscall.h10
-rw-r--r--bsd-user/sparc64/target_signal.h27
-rw-r--r--bsd-user/strace.c191
-rw-r--r--bsd-user/syscall.c560
-rw-r--r--bsd-user/syscall_defs.h114
-rw-r--r--bsd-user/uaccess.c65
-rw-r--r--bsd-user/x86_64/syscall.h116
-rw-r--r--bsd-user/x86_64/target_signal.h19
-rw-r--r--bswap.h240
-rw-r--r--bt-host.c199
-rw-r--r--bt-host.h9
-rw-r--r--bt-vhci.c168
-rw-r--r--buffered_file.c282
-rw-r--r--buffered_file.h30
-rw-r--r--cache-utils.c97
-rw-r--r--cache-utils.h62
-rw-r--r--check-qdict.c401
-rw-r--r--check-qfloat.c76
-rw-r--r--check-qint.c113
-rw-r--r--check-qjson.c794
-rw-r--r--check-qlist.c153
-rw-r--r--check-qstring.c134
-rw-r--r--cmd.c610
-rw-r--r--cmd.h79
-rw-r--r--compat/sys/eventfd.h13
-rw-r--r--compatfd.c117
-rw-r--r--compatfd.h43
-rw-r--r--config.h2
-rwxr-xr-xconfigure3432
-rw-r--r--console.c1707
-rw-r--r--console.h381
-rw-r--r--cpu-all.h972
-rw-r--r--cpu-common.h144
-rw-r--r--cpu-defs.h227
-rw-r--r--cpu-exec.c1279
-rw-r--r--cpus.c991
-rw-r--r--cpus.h21
-rw-r--r--cris-dis.c2893
-rw-r--r--cursor.c210
-rw-r--r--cursor_hidden.xpm37
-rw-r--r--cursor_left_ptr.xpm39
-rw-r--r--cutils.c416
-rw-r--r--darwin-user/commpage.c357
-rw-r--r--darwin-user/ioctls.h4
-rw-r--r--darwin-user/ioctls_types.h1
-rw-r--r--darwin-user/machload.c902
-rw-r--r--darwin-user/main.c1015
-rw-r--r--darwin-user/mmap.c409
-rw-r--r--darwin-user/qemu.h178
-rw-r--r--darwin-user/signal.c457
-rw-r--r--darwin-user/syscall.c1566
-rw-r--r--darwin-user/syscalls.h384
-rw-r--r--def-helper.h240
-rw-r--r--default-configs/alpha-linux-user.mak1
-rw-r--r--default-configs/arm-linux-user.mak3
-rw-r--r--default-configs/arm-softmmu.mak28
-rw-r--r--default-configs/armeb-linux-user.mak3
-rw-r--r--default-configs/cris-linux-user.mak1
-rw-r--r--default-configs/cris-softmmu.mak5
-rw-r--r--default-configs/i386-bsd-user.mak1
-rw-r--r--default-configs/i386-darwin-user.mak1
-rw-r--r--default-configs/i386-linux-user.mak1
-rw-r--r--default-configs/i386-softmmu.mak20
-rw-r--r--default-configs/m68k-linux-user.mak3
-rw-r--r--default-configs/m68k-softmmu.mak5
-rw-r--r--default-configs/microblaze-linux-user.mak1
-rw-r--r--default-configs/microblaze-softmmu.mak4
-rw-r--r--default-configs/mips-linux-user.mak1
-rw-r--r--default-configs/mips-softmmu.mak28
-rw-r--r--default-configs/mips64-softmmu.mak28
-rw-r--r--default-configs/mips64el-softmmu.mak30
-rw-r--r--default-configs/mipsel-linux-user.mak1
-rw-r--r--default-configs/mipsel-softmmu.mak28
-rw-r--r--default-configs/pci.mak16
-rw-r--r--default-configs/ppc-darwin-user.mak3
-rw-r--r--default-configs/ppc-linux-user.mak3
-rw-r--r--default-configs/ppc-softmmu.mak33
-rw-r--r--default-configs/ppc64-linux-user.mak3
-rw-r--r--default-configs/ppc64-softmmu.mak33
-rw-r--r--default-configs/ppc64abi32-linux-user.mak3
-rw-r--r--default-configs/ppcemb-softmmu.mak33
-rw-r--r--default-configs/s390x-softmmu.mak1
-rw-r--r--default-configs/sh4-linux-user.mak1
-rw-r--r--default-configs/sh4-softmmu.mak7
-rw-r--r--default-configs/sh4eb-linux-user.mak1
-rw-r--r--default-configs/sh4eb-softmmu.mak7
-rw-r--r--default-configs/sparc-bsd-user.mak1
-rw-r--r--default-configs/sparc-linux-user.mak1
-rw-r--r--default-configs/sparc-softmmu.mak10
-rw-r--r--default-configs/sparc32plus-linux-user.mak1
-rw-r--r--default-configs/sparc64-bsd-user.mak1
-rw-r--r--default-configs/sparc64-linux-user.mak1
-rw-r--r--default-configs/sparc64-softmmu.mak13
-rw-r--r--default-configs/x86_64-bsd-user.mak1
-rw-r--r--default-configs/x86_64-linux-user.mak1
-rw-r--r--default-configs/x86_64-softmmu.mak20
-rw-r--r--device_tree.c110
-rw-r--r--device_tree.h26
-rw-r--r--dis-asm.h476
-rw-r--r--disas.c432
-rw-r--r--disas.h42
-rw-r--r--dma-helpers.c188
-rw-r--r--dma.h41
-rw-r--r--docs/blkverify.txt69
-rw-r--r--docs/bootindex.txt43
-rw-r--r--docs/migration.txt303
-rw-r--r--docs/qdev-device-use.txt368
-rw-r--r--docs/specs/acpi_pci_hotplug.txt37
-rw-r--r--docs/specs/ivshmem_device_spec.txt96
-rw-r--r--docs/specs/qed_spec.txt130
-rw-r--r--docs/tracing.txt189
-rw-r--r--dyngen-exec.h84
-rw-r--r--elf.h1240
-rw-r--r--envlist.c246
-rw-r--r--envlist.h22
-rw-r--r--etc/qemu/target-x86_64.conf86
-rw-r--r--exec-all.h349
-rw-r--r--exec.c4356
-rw-r--r--fpu/softfloat-macros.h719
-rw-r--r--fpu/softfloat-native.c515
-rw-r--r--fpu/softfloat-native.h492
-rw-r--r--fpu/softfloat-specialize.h755
-rw-r--r--fpu/softfloat.c6119
-rw-r--r--fpu/softfloat.h583
-rw-r--r--fsdev/qemu-fsdev.c100
-rw-r--r--fsdev/qemu-fsdev.h55
-rw-r--r--gdb-xml/arm-core.xml31
-rw-r--r--gdb-xml/arm-neon.xml88
-rw-r--r--gdb-xml/arm-vfp.xml29
-rw-r--r--gdb-xml/arm-vfp3.xml45
-rw-r--r--gdb-xml/cf-core.xml29
-rw-r--r--gdb-xml/cf-fp.xml22
-rw-r--r--gdb-xml/power-altivec.xml57
-rw-r--r--gdb-xml/power-core.xml49
-rw-r--r--gdb-xml/power-fpu.xml44
-rw-r--r--gdb-xml/power-spe.xml45
-rw-r--r--gdb-xml/power64-core.xml49
-rw-r--r--gdbstub.c2695
-rw-r--r--gdbstub.h44
-rw-r--r--gen-icount.h48
-rw-r--r--hmp-commands.hx1382
-rw-r--r--host-utils.c105
-rw-r--r--host-utils.h236
-rw-r--r--hpet.h22
-rw-r--r--hppa-dis.c2831
-rw-r--r--hppa.ld213
-rw-r--r--hw/9p.h24
-rw-r--r--hw/a9mpcore.c29
-rw-r--r--hw/ac97.c1351
-rw-r--r--hw/acpi.c202
-rw-r--r--hw/acpi.h78
-rw-r--r--hw/acpi_piix4.c739
-rw-r--r--hw/adb.c480
-rw-r--r--hw/adlib.c338
-rw-r--r--hw/ads7846.c170
-rw-r--r--hw/alpha_palcode.c1048
-rw-r--r--hw/an5206.c101
-rw-r--r--hw/apb_pci.c482
-rw-r--r--hw/apb_pci.h9
-rw-r--r--hw/apic.c1149
-rw-r--r--hw/apic.h28
-rw-r--r--hw/apm.c86
-rw-r--r--hw/apm.h22
-rw-r--r--hw/applesmc.c241
-rw-r--r--hw/arm-misc.h46
-rw-r--r--hw/arm11mpcore.c112
-rw-r--r--hw/arm_boot.c283
-rw-r--r--hw/arm_gic.c749
-rw-r--r--hw/arm_pic.c49
-rw-r--r--hw/arm_sysctl.c264
-rw-r--r--hw/arm_timer.c357
-rw-r--r--hw/armv7m.c263
-rw-r--r--hw/armv7m_nvic.c409
-rw-r--r--hw/audiodev.h20
-rw-r--r--hw/axis_dev88.c357
-rw-r--r--hw/baum.c642
-rw-r--r--hw/baum.h26
-rw-r--r--hw/bitbang_i2c.c228
-rw-r--r--hw/bitbang_i2c.h14
-rw-r--r--hw/blizzard.c998
-rw-r--r--hw/blizzard_template.h136
-rw-r--r--hw/boards.h38
-rw-r--r--hw/bonito.c813
-rw-r--r--hw/bt-hci-csr.c454
-rw-r--r--hw/bt-hci.c2221
-rw-r--r--hw/bt-hid.c571
-rw-r--r--hw/bt-l2cap.c1362
-rw-r--r--hw/bt-sdp.c967
-rw-r--r--hw/bt.c121
-rw-r--r--hw/bt.h2183
-rw-r--r--hw/cbus.c618
-rw-r--r--hw/cdrom.c155
-rw-r--r--hw/cirrus_vga.c3171
-rw-r--r--hw/cirrus_vga_rop.h208
-rw-r--r--hw/cirrus_vga_rop2.h281
-rw-r--r--hw/cris-boot.c97
-rw-r--r--hw/cris-boot.h11
-rw-r--r--hw/cris_pic_cpu.c50
-rw-r--r--hw/cs4231.c175
-rw-r--r--hw/cs4231a.c686
-rw-r--r--hw/cuda.c769
-rw-r--r--hw/debugcon.c107
-rw-r--r--hw/dec_pci.c133
-rw-r--r--hw/dec_pci.h8
-rw-r--r--hw/device-assignment.c1926
-rw-r--r--hw/device-assignment.h120
-rw-r--r--hw/device-hotplug.c46
-rw-r--r--hw/devices.h70
-rw-r--r--hw/dma.c555
-rw-r--r--hw/dp8393x.c914
-rw-r--r--hw/ds1225y.c182
-rw-r--r--hw/ds1338.c132
-rw-r--r--hw/dummy_m68k.c80
-rw-r--r--hw/e1000.c1183
-rw-r--r--hw/e1000_hw.h864
-rw-r--r--hw/ecc.c88
-rw-r--r--hw/eccmemctl.c332
-rw-r--r--hw/eepro100.c2066
-rw-r--r--hw/eeprom93xx.c337
-rw-r--r--hw/eeprom93xx.h40
-rw-r--r--hw/elf_ops.h297
-rw-r--r--hw/empty_slot.c93
-rw-r--r--hw/empty_slot.h2
-rw-r--r--hw/es1370.c1053
-rw-r--r--hw/escc.c961
-rw-r--r--hw/escc.h8
-rw-r--r--hw/esp.c751
-rw-r--r--hw/esp.h13
-rw-r--r--hw/etraxfs.c160
-rw-r--r--hw/etraxfs.h28
-rw-r--r--hw/etraxfs_dma.c756
-rw-r--r--hw/etraxfs_dma.h22
-rw-r--r--hw/etraxfs_eth.c613
-rw-r--r--hw/etraxfs_pic.c169
-rw-r--r--hw/etraxfs_ser.c220
-rw-r--r--hw/etraxfs_timer.c336
-rw-r--r--hw/event_notifier.c62
-rw-r--r--hw/event_notifier.h16
-rw-r--r--hw/extboot.c123
-rw-r--r--hw/fdc.c2110
-rw-r--r--hw/fdc.h16
-rw-r--r--hw/file-op-9p.h107
-rw-r--r--hw/firmware_abi.h73
-rw-r--r--hw/flash.h54
-rw-r--r--hw/fmopl.c1391
-rw-r--r--hw/fmopl.h174
-rw-r--r--hw/framebuffer.c116
-rw-r--r--hw/framebuffer.h22
-rw-r--r--hw/fw_cfg.c407
-rw-r--r--hw/fw_cfg.h70
-rw-r--r--hw/g364fb.c615
-rw-r--r--hw/grackle_pci.c147
-rw-r--r--hw/grlib.h126
-rw-r--r--hw/grlib_apbuart.c187
-rw-r--r--hw/grlib_gptimer.c395
-rw-r--r--hw/grlib_irqmp.c376
-rw-r--r--hw/gt64xxx.c1174
-rw-r--r--hw/gumstix.c141
-rw-r--r--hw/gus.c322
-rw-r--r--hw/gusemu.h105
-rw-r--r--hw/gusemu_hal.c554
-rw-r--r--hw/gusemu_mixer.c240
-rw-r--r--hw/gustate.h132
-rw-r--r--hw/hda-audio.c926
-rw-r--r--hw/heathrow_pic.c230
-rw-r--r--hw/hpet.c752
-rw-r--r--hw/hpet_emul.h71
-rw-r--r--hw/hw.h837
-rw-r--r--hw/i2c.c196
-rw-r--r--hw/i2c.h82
-rw-r--r--hw/i8254-kvm.c122
-rw-r--r--hw/i8254.c548
-rw-r--r--hw/i8254.h69
-rw-r--r--hw/i8259.c682
-rw-r--r--hw/ide.h31
-rw-r--r--hw/ide/ahci.c1152
-rw-r--r--hw/ide/ahci.h333
-rw-r--r--hw/ide/cmd646.c298
-rw-r--r--hw/ide/core.c2813
-rw-r--r--hw/ide/ich.c148
-rw-r--r--hw/ide/internal.h571
-rw-r--r--hw/ide/isa.c120
-rw-r--r--hw/ide/macio.c329
-rw-r--r--hw/ide/microdrive.c551
-rw-r--r--hw/ide/mmio.c141
-rw-r--r--hw/ide/pci.c467
-rw-r--r--hw/ide/pci.h48
-rw-r--r--hw/ide/piix.c214
-rw-r--r--hw/ide/qdev.c172
-rw-r--r--hw/ide/via.c200
-rw-r--r--hw/integratorcp.c545
-rw-r--r--hw/intel-hda-defs.h717
-rw-r--r--hw/intel-hda.c1308
-rw-r--r--hw/intel-hda.h62
-rw-r--r--hw/ioapic.c439
-rw-r--r--hw/ioapic.h20
-rw-r--r--hw/ioh3420.c246
-rw-r--r--hw/ioh3420.h10
-rw-r--r--hw/ipf.c707
-rw-r--r--hw/irq.c77
-rw-r--r--hw/irq.h35
-rw-r--r--hw/isa-bus.c194
-rw-r--r--hw/isa.h52
-rw-r--r--hw/isa_mmio.c82
-rw-r--r--hw/ivshmem.c829
-rw-r--r--hw/jazz_led.c328
-rw-r--r--hw/lan9118.c1190
-rw-r--r--hw/lance.c160
-rw-r--r--hw/leon3.c217
-rw-r--r--hw/lm832x.c512
-rw-r--r--hw/loader.c795
-rw-r--r--hw/loader.h48
-rw-r--r--hw/lsi53c895a.c2276
-rw-r--r--hw/m48t59.c762
-rw-r--r--hw/mac_dbdma.c852
-rw-r--r--hw/mac_dbdma.h43
-rw-r--r--hw/mac_nvram.c195
-rw-r--r--hw/macio.c118
-rw-r--r--hw/mainstone.c158
-rw-r--r--hw/mainstone.h38
-rw-r--r--hw/marvell_88w8618_audio.c299
-rw-r--r--hw/max111x.c188
-rw-r--r--hw/max7310.c220
-rw-r--r--hw/mc146818rtc.c657
-rw-r--r--hw/mc146818rtc.h12
-rw-r--r--hw/mcf.h21
-rw-r--r--hw/mcf5206.c541
-rw-r--r--hw/mcf5208.c306
-rw-r--r--hw/mcf_fec.c481
-rw-r--r--hw/mcf_intc.c157
-rw-r--r--hw/mcf_uart.c310
-rw-r--r--hw/microblaze_pic_cpu.c50
-rw-r--r--hw/mips-bios.h8
-rw-r--r--hw/mips.h40
-rw-r--r--hw/mips_addr.c34
-rw-r--r--hw/mips_cpudevs.h15
-rw-r--r--hw/mips_fulong2e.c408
-rw-r--r--hw/mips_int.c65
-rw-r--r--hw/mips_jazz.c314
-rw-r--r--hw/mips_malta.c978
-rw-r--r--hw/mips_mipssim.c212
-rw-r--r--hw/mips_r4k.c318
-rw-r--r--hw/mips_timer.c147
-rw-r--r--hw/mipsnet.c288
-rw-r--r--hw/mpcore.c286
-rw-r--r--hw/msi.c344
-rw-r--r--hw/msi.h41
-rw-r--r--hw/msix.c654
-rw-r--r--hw/msix.h38
-rw-r--r--hw/msmouse.c78
-rw-r--r--hw/msmouse.h2
-rw-r--r--hw/mst_fpga.c240
-rw-r--r--hw/multiboot.c339
-rw-r--r--hw/multiboot.h12
-rw-r--r--hw/musicpal.c1665
-rw-r--r--hw/nand.c661
-rw-r--r--hw/ne2000-isa.c125
-rw-r--r--hw/ne2000.c781
-rw-r--r--hw/ne2000.h39
-rw-r--r--hw/nseries.c1417
-rw-r--r--hw/nvram.h42
-rw-r--r--hw/omap.h1141
-rw-r--r--hw/omap1.c3894
-rw-r--r--hw/omap2.c2616
-rw-r--r--hw/omap_clk.c1260
-rw-r--r--hw/omap_dma.c2082
-rw-r--r--hw/omap_dss.c1068
-rw-r--r--hw/omap_gpio.c725
-rw-r--r--hw/omap_gpmc.c419
-rw-r--r--hw/omap_gptimer.c484
-rw-r--r--hw/omap_i2c.c470
-rw-r--r--hw/omap_intc.c598
-rw-r--r--hw/omap_l4.c272
-rw-r--r--hw/omap_lcd_template.h175
-rw-r--r--hw/omap_lcdc.c461
-rw-r--r--hw/omap_mmc.c641
-rw-r--r--hw/omap_sdrc.c165
-rw-r--r--hw/omap_spi.c346
-rw-r--r--hw/omap_sx1.c254
-rw-r--r--hw/omap_synctimer.c96
-rw-r--r--hw/omap_tap.c112
-rw-r--r--hw/omap_uart.c196
-rw-r--r--hw/onenand.c661
-rw-r--r--hw/openpic.c1686
-rw-r--r--hw/openpic.h18
-rw-r--r--hw/palm.c289
-rw-r--r--hw/parallel.c609
-rw-r--r--hw/pc.c1186
-rw-r--r--hw/pc.h191
-rw-r--r--hw/pc_piix.c407
-rw-r--r--hw/pci-hotplug.c292
-rw-r--r--hw/pci-stub.c50
-rw-r--r--hw/pci.c2227
-rw-r--r--hw/pci.h496
-rw-r--r--hw/pci_bridge.c275
-rw-r--r--hw/pci_bridge.h66
-rw-r--r--hw/pci_host.c159
-rw-r--r--hw/pci_host.h53
-rw-r--r--hw/pci_ids.h110
-rw-r--r--hw/pci_internals.h47
-rw-r--r--hw/pci_regs.h672
-rw-r--r--hw/pcie.c544
-rw-r--r--hw/pcie.h132
-rw-r--r--hw/pcie_aer.c1031
-rw-r--r--hw/pcie_aer.h106
-rw-r--r--hw/pcie_host.c177
-rw-r--r--hw/pcie_host.h49
-rw-r--r--hw/pcie_port.c124
-rw-r--r--hw/pcie_port.h51
-rw-r--r--hw/pcie_regs.h156
-rw-r--r--hw/pckbd.c499
-rw-r--r--hw/pcmcia.h51
-rw-r--r--hw/pcnet-pci.c346
-rw-r--r--hw/pcnet.c1748
-rw-r--r--hw/pcnet.h42
-rw-r--r--hw/pcspk.c195
-rw-r--r--hw/petalogix_s3adsp1800_mmu.c225
-rw-r--r--hw/pflash_cfi01.c726
-rw-r--r--hw/pflash_cfi02.c741
-rw-r--r--hw/piix4.c127
-rw-r--r--hw/piix_pci.c392
-rw-r--r--hw/pixel_ops.h53
-rw-r--r--hw/pl011.c332
-rw-r--r--hw/pl022.c312
-rw-r--r--hw/pl031.c237
-rw-r--r--hw/pl050.c187
-rw-r--r--hw/pl061.c317
-rw-r--r--hw/pl080.c407
-rw-r--r--hw/pl110.c422
-rw-r--r--hw/pl110_template.h307
-rw-r--r--hw/pl181.c473
-rw-r--r--hw/pl190.c278
-rw-r--r--hw/pm_smbus.c176
-rw-r--r--hw/pm_smbus.h21
-rw-r--r--hw/ppc.c1288
-rw-r--r--hw/ppc.h56
-rw-r--r--hw/ppc405.h76
-rw-r--r--hw/ppc405_boards.c653
-rw-r--r--hw/ppc405_uc.c2544
-rw-r--r--hw/ppc440.c101
-rw-r--r--hw/ppc440.h21
-rw-r--r--hw/ppc440_bamboo.c204
-rw-r--r--hw/ppc4xx.h60
-rw-r--r--hw/ppc4xx_devs.c691
-rw-r--r--hw/ppc4xx_pci.c393
-rw-r--r--hw/ppc_mac.h113
-rw-r--r--hw/ppc_newworld.c429
-rw-r--r--hw/ppc_oldworld.c332
-rw-r--r--hw/ppc_prep.c771
-rw-r--r--hw/ppce500.h22
-rw-r--r--hw/ppce500_mpc8544ds.c298
-rw-r--r--hw/ppce500_pci.c326
-rw-r--r--hw/prep_pci.c144
-rw-r--r--hw/prep_pci.h8
-rw-r--r--hw/primecell.h14
-rw-r--r--hw/ps2.c614
-rw-r--r--hw/ps2.h9
-rw-r--r--hw/ptimer.c244
-rw-r--r--hw/pxa.h212
-rw-r--r--hw/pxa2xx.c2292
-rw-r--r--hw/pxa2xx_dma.c549
-rw-r--r--hw/pxa2xx_gpio.c340
-rw-r--r--hw/pxa2xx_keypad.c336
-rw-r--r--hw/pxa2xx_lcd.c982
-rw-r--r--hw/pxa2xx_mmci.c547
-rw-r--r--hw/pxa2xx_pcmcia.c215
-rw-r--r--hw/pxa2xx_pic.c313
-rw-r--r--hw/pxa2xx_template.h435
-rw-r--r--hw/pxa2xx_timer.c490
-rw-r--r--hw/qdev-addr.c32
-rw-r--r--hw/qdev-addr.h5
-rw-r--r--hw/qdev-properties.c777
-rw-r--r--hw/qdev.c926
-rw-r--r--hw/qdev.h328
-rw-r--r--hw/qxl-logger.c248
-rw-r--r--hw/qxl-render.c226
-rw-r--r--hw/qxl.c1531
-rw-r--r--hw/qxl.h108
-rw-r--r--hw/r2d.c341
-rw-r--r--hw/rc4030.c830
-rw-r--r--hw/realview.c459
-rw-r--r--hw/realview_gic.c79
-rw-r--r--hw/rtl8139.c3431
-rw-r--r--hw/s390-virtio-bus.c409
-rw-r--r--hw/s390-virtio-bus.h69
-rw-r--r--hw/s390-virtio.c289
-rw-r--r--hw/sb16.c1420
-rw-r--r--hw/sbi.c148
-rw-r--r--hw/scsi-bus.c547
-rw-r--r--hw/scsi-defs.h166
-rw-r--r--hw/scsi-disk.c1309
-rw-r--r--hw/scsi-generic.c576
-rw-r--r--hw/scsi.h110
-rw-r--r--hw/sd.c1660
-rw-r--r--hw/sd.h79
-rw-r--r--hw/serial.c991
-rw-r--r--hw/sh.h54
-rw-r--r--hw/sh7750.c818
-rw-r--r--hw/sh7750_regnames.c97
-rw-r--r--hw/sh7750_regnames.h6
-rw-r--r--hw/sh7750_regs.h1277
-rw-r--r--hw/sh_intc.c481
-rw-r--r--hw/sh_intc.h80
-rw-r--r--hw/sh_pci.c161
-rw-r--r--hw/sh_serial.c401
-rw-r--r--hw/sh_timer.c327
-rw-r--r--hw/sharpsl.h17
-rw-r--r--hw/shix.c104
-rw-r--r--hw/slavio_intctl.c463
-rw-r--r--hw/slavio_misc.c499
-rw-r--r--hw/slavio_timer.c424
-rw-r--r--hw/sm501.c1457
-rw-r--r--hw/sm501_template.h145
-rw-r--r--hw/smbios.c239
-rw-r--r--hw/smbios.h162
-rw-r--r--hw/smbus.c318
-rw-r--r--hw/smbus.h68
-rw-r--r--hw/smbus_eeprom.c127
-rw-r--r--hw/smc91c111.c798
-rw-r--r--hw/soc_dma.c366
-rw-r--r--hw/soc_dma.h113
-rw-r--r--hw/sparc32_dma.c305
-rw-r--r--hw/sparc32_dma.h12
-rw-r--r--hw/spitz.c1117
-rw-r--r--hw/ssd0303.c312
-rw-r--r--hw/ssd0323.c355
-rw-r--r--hw/ssi-sd.c256
-rw-r--r--hw/ssi.c70
-rw-r--r--hw/ssi.h45
-rw-r--r--hw/stellaris.c1474
-rw-r--r--hw/stellaris_enet.c443
-rw-r--r--hw/stellaris_input.c91
-rw-r--r--hw/sun4c_intctl.c224
-rw-r--r--hw/sun4m.c1821
-rw-r--r--hw/sun4m.h36
-rw-r--r--hw/sun4m_iommu.c378
-rw-r--r--hw/sun4u.c942
-rw-r--r--hw/syborg.c112
-rw-r--r--hw/syborg.h18
-rw-r--r--hw/syborg_fb.c551
-rw-r--r--hw/syborg_interrupt.c238
-rw-r--r--hw/syborg_keyboard.c244
-rw-r--r--hw/syborg_pointer.c243
-rw-r--r--hw/syborg_rtc.c150
-rw-r--r--hw/syborg_serial.c359
-rw-r--r--hw/syborg_timer.c245
-rw-r--r--hw/syborg_virtio.c314
-rw-r--r--hw/sysbus.c203
-rw-r--r--hw/sysbus.h67
-rw-r--r--hw/tc58128.c183
-rw-r--r--hw/tc6393xb.c608
-rw-r--r--hw/tc6393xb_template.h67
-rw-r--r--hw/tcx.c651
-rw-r--r--hw/testdev.c142
-rw-r--r--hw/tmp105.c244
-rw-r--r--hw/tosa.c277
-rw-r--r--hw/tsc2005.c593
-rw-r--r--hw/tsc210x.c1293
-rw-r--r--hw/tusb6010.c766
-rw-r--r--hw/twl92230.c876
-rw-r--r--hw/unin_pci.c391
-rw-r--r--hw/usb-bt.c568
-rw-r--r--hw/usb-bus.c394
-rw-r--r--hw/usb-desc.c406
-rw-r--r--hw/usb-desc.h92
-rw-r--r--hw/usb-hid.c1006
-rw-r--r--hw/usb-hub.c590
-rw-r--r--hw/usb-msd.c621
-rw-r--r--hw/usb-musb.c1495
-rw-r--r--hw/usb-net.c1441
-rw-r--r--hw/usb-ohci.c1792
-rw-r--r--hw/usb-ohci.h9
-rw-r--r--hw/usb-serial.c611
-rw-r--r--hw/usb-uhci.c1229
-rw-r--r--hw/usb-uhci.h10
-rw-r--r--hw/usb-wacom.c370
-rw-r--r--hw/usb.c261
-rw-r--r--hw/usb.h367
-rw-r--r--hw/versatile_pci.c160
-rw-r--r--hw/versatilepb.c362
-rw-r--r--hw/vga-isa-mm.c128
-rw-r--r--hw/vga-isa.c49
-rw-r--r--hw/vga-pci.c125
-rw-r--r--hw/vga.c2429
-rw-r--r--hw/vga_int.h232
-rw-r--r--hw/vga_template.h525
-rw-r--r--hw/vhost.c727
-rw-r--r--hw/vhost.h50
-rw-r--r--hw/vhost_net.c229
-rw-r--r--hw/vhost_net.h20
-rw-r--r--hw/virtex_ml507.c276
-rw-r--r--hw/virtio-9p-debug.c645
-rw-r--r--hw/virtio-9p-debug.h6
-rw-r--r--hw/virtio-9p-local.c563
-rw-r--r--hw/virtio-9p-posix-acl.c140
-rw-r--r--hw/virtio-9p-xattr-user.c109
-rw-r--r--hw/virtio-9p-xattr.c159
-rw-r--r--hw/virtio-9p-xattr.h102
-rw-r--r--hw/virtio-9p.c3744
-rw-r--r--hw/virtio-9p.h507
-rw-r--r--hw/virtio-balloon.c284
-rw-r--r--hw/virtio-balloon.h55
-rw-r--r--hw/virtio-blk.c588
-rw-r--r--hw/virtio-blk.h106
-rw-r--r--hw/virtio-console.c141
-rw-r--r--hw/virtio-net.c1077
-rw-r--r--hw/virtio-net.h189
-rw-r--r--hw/virtio-pci.c1026
-rw-r--r--hw/virtio-serial-bus.c908
-rw-r--r--hw/virtio-serial.h202
-rw-r--r--hw/virtio.c882
-rw-r--r--hw/virtio.h229
-rw-r--r--hw/vmmouse.c291
-rw-r--r--hw/vmport.c111
-rw-r--r--hw/vmware_vga.c1330
-rw-r--r--hw/vmware_vga.h9
-rw-r--r--hw/vt82c686.c594
-rw-r--r--hw/vt82c686.h11
-rw-r--r--hw/watchdog.c147
-rw-r--r--hw/watchdog.h43
-rw-r--r--hw/wdt_i6300esb.c448
-rw-r--r--hw/wdt_ib700.c137
-rw-r--r--hw/wm8750.c707
-rw-r--r--hw/xen.h21
-rw-r--r--hw/xen_backend.c714
-rw-r--r--hw/xen_backend.h106
-rw-r--r--hw/xen_blkif.h103
-rw-r--r--hw/xen_common.h34
-rw-r--r--hw/xen_console.c269
-rw-r--r--hw/xen_devconfig.c173
-rw-r--r--hw/xen_disk.c788
-rw-r--r--hw/xen_domainbuild.c300
-rw-r--r--hw/xen_domainbuild.h13
-rw-r--r--hw/xen_machine_pv.c124
-rw-r--r--hw/xen_nic.c421
-rw-r--r--hw/xenfb.c1013
-rw-r--r--hw/xilinx.h50
-rw-r--r--hw/xilinx_ethlite.c254
-rw-r--r--hw/xilinx_intc.c176
-rw-r--r--hw/xilinx_timer.c235
-rw-r--r--hw/xilinx_uartlite.c220
-rw-r--r--hw/xio3130_downstream.c211
-rw-r--r--hw/xio3130_downstream.h11
-rw-r--r--hw/xio3130_upstream.c186
-rw-r--r--hw/xio3130_upstream.h10
-rw-r--r--hw/zaurus.c268
-rw-r--r--i386-dis.c6562
-rw-r--r--i386.ld153
-rw-r--r--ia64-dis.c10599
-rw-r--r--ia64.ld209
-rw-r--r--ia64intrin.h150
-rw-r--r--input.c288
-rw-r--r--ioport-user.c60
-rw-r--r--ioport.c306
-rw-r--r--ioport.h55
-rw-r--r--iorange.h30
-rw-r--r--iov.c70
-rw-r--r--iov.h19
-rw-r--r--json-lexer.c339
-rw-r--r--json-lexer.h50
-rw-r--r--json-parser.c577
-rw-r--r--json-parser.h22
-rw-r--r--json-streamer.c88
-rw-r--r--json-streamer.h39
-rw-r--r--kvm-all.c1415
-rw-r--r--kvm-stub.c193
-rw-r--r--kvm-tpr-opt.c380
-rw-r--r--kvm.h237
-rw-r--r--kvm/.gitignore66
-rw-r--r--kvm/Makefile125
-rwxr-xr-xkvm/configure142
-rw-r--r--kvm/extboot/Makefile41
-rw-r--r--kvm/extboot/STATUS6
-rw-r--r--kvm/extboot/signrom.c79
-rw-r--r--kvm/include/ia64/asm/kvm.h264
-rw-r--r--kvm/include/ia64/asm/kvm_para.h31
-rw-r--r--kvm/include/linux/compiler.h2
-rw-r--r--kvm/include/linux/config.h43
-rw-r--r--kvm/include/linux/kvm.h784
-rw-r--r--kvm/include/linux/kvm_para.h41
-rw-r--r--kvm/include/linux/vhost.h130
-rw-r--r--kvm/include/powerpc/asm/kvm.h62
-rw-r--r--kvm/include/powerpc/asm/kvm_para.h37
-rw-r--r--kvm/include/x86/asm/kvm.h324
-rw-r--r--kvm/include/x86/asm/kvm_para.h149
-rw-r--r--kvm/kvm.spec139
-rwxr-xr-xkvm/kvm_stat412
-rw-r--r--kvm/libfdt/Makefile19
-rw-r--r--kvm/libfdt/README3
-rw-r--r--kvm/libfdt/fdt.c194
-rw-r--r--kvm/libfdt/fdt.h60
-rw-r--r--kvm/libfdt/fdt_ro.c476
-rw-r--r--kvm/libfdt/fdt_rw.c467
-rw-r--r--kvm/libfdt/fdt_strerror.c96
-rw-r--r--kvm/libfdt/fdt_sw.c258
-rw-r--r--kvm/libfdt/fdt_wip.c144
-rw-r--r--kvm/libfdt/libfdt.h1076
-rw-r--r--kvm/libfdt/libfdt_env.h22
-rw-r--r--kvm/libfdt/libfdt_internal.h96
-rw-r--r--kvm/libkvm/Makefile54
-rw-r--r--kvm/libkvm/config-i386.mak6
-rw-r--r--kvm/libkvm/config-ia64.mak5
-rw-r--r--kvm/libkvm/config-ppc.mak4
-rw-r--r--kvm/libkvm/config-s390.mak3
-rw-r--r--kvm/libkvm/config-s390x.mak3
-rw-r--r--kvm/libkvm/config-x86_64.mak6
-rw-r--r--kvm/libkvm/kvm-common.h94
-rw-r--r--kvm/libkvm/kvm-ia64.h31
-rw-r--r--kvm/libkvm/kvm-powerpc.h36
-rw-r--r--kvm/libkvm/kvm-s390.h31
-rw-r--r--kvm/libkvm/kvm-x86.h55
-rw-r--r--kvm/libkvm/libkvm-ia64.c82
-rw-r--r--kvm/libkvm/libkvm-powerpc.c100
-rw-r--r--kvm/libkvm/libkvm-s390.c110
-rw-r--r--kvm/libkvm/libkvm-x86.c637
-rw-r--r--kvm/libkvm/libkvm.c1497
-rw-r--r--kvm/libkvm/libkvm.h838
-rw-r--r--kvm/scripts/65-kvm.rules1
-rwxr-xr-xkvm/scripts/kvm226
-rwxr-xr-xkvm/scripts/make-combined-release36
-rwxr-xr-xkvm/scripts/make-release81
-rwxr-xr-xkvm/scripts/mkbootdisk30
-rwxr-xr-xkvm/scripts/qemu-ifup5
-rwxr-xr-xkvm/scripts/run_img4
-rwxr-xr-xkvm/scripts/vmxcap216
-rw-r--r--kvm/vgabios/.cvsignore1
-rw-r--r--kvm/vgabios/BUGS3
-rw-r--r--kvm/vgabios/COPYING504
-rw-r--r--kvm/vgabios/ChangeLog1264
-rw-r--r--kvm/vgabios/Makefile87
-rw-r--r--kvm/vgabios/Notes11
-rw-r--r--kvm/vgabios/README219
-rw-r--r--kvm/vgabios/TODO26
-rw-r--r--kvm/vgabios/biossums.c282
-rw-r--r--kvm/vgabios/clext.c1688
-rwxr-xr-xkvm/vgabios/dataseghack23
-rw-r--r--kvm/vgabios/tests/lfbprof/Makefile5
-rw-r--r--kvm/vgabios/tests/lfbprof/lfbprof.c594
-rw-r--r--kvm/vgabios/tests/lfbprof/lfbprof.h149
-rw-r--r--kvm/vgabios/tests/testbios.c353
-rw-r--r--kvm/vgabios/vbe.c1432
-rw-r--r--kvm/vgabios/vbe.h313
-rw-r--r--kvm/vgabios/vbetables-gen.c264
-rw-r--r--kvm/vgabios/vgabios.c3853
-rw-r--r--kvm/vgabios/vgabios.h47
-rw-r--r--kvm/vgabios/vgafonts.h784
-rw-r--r--kvm/vgabios/vgatables.h622
-rw-r--r--libfdt_env.h40
-rw-r--r--linux-aio.c261
-rw-r--r--linux-user/alpha/syscall.h253
-rw-r--r--linux-user/alpha/syscall_nr.h421
-rw-r--r--linux-user/alpha/target_signal.h56
-rw-r--r--linux-user/alpha/termbits.h264
-rw-r--r--linux-user/arm/nwfpe/double_cpdo.c295
-rw-r--r--linux-user/arm/nwfpe/extended_cpdo.c272
-rw-r--r--linux-user/arm/nwfpe/fpa11.c237
-rw-r--r--linux-user/arm/nwfpe/fpa11.h130
-rw-r--r--linux-user/arm/nwfpe/fpa11.inl50
-rw-r--r--linux-user/arm/nwfpe/fpa11_cpdo.c112
-rw-r--r--linux-user/arm/nwfpe/fpa11_cpdt.c381
-rw-r--r--linux-user/arm/nwfpe/fpa11_cprt.c283
-rw-r--r--linux-user/arm/nwfpe/fpopcode.c112
-rw-r--r--linux-user/arm/nwfpe/fpopcode.h390
-rw-r--r--linux-user/arm/nwfpe/fpsr.h107
-rw-r--r--linux-user/arm/nwfpe/single_cpdo.c252
-rw-r--r--linux-user/arm/syscall.h42
-rw-r--r--linux-user/arm/syscall_nr.h367
-rw-r--r--linux-user/arm/target_signal.h29
-rw-r--r--linux-user/arm/termbits.h216
-rw-r--r--linux-user/cpu-uname.c72
-rw-r--r--linux-user/cpu-uname.h1
-rw-r--r--linux-user/cris/syscall.h36
-rw-r--r--linux-user/cris/syscall_nr.h335
-rw-r--r--linux-user/cris/target_signal.h29
-rw-r--r--linux-user/cris/termbits.h213
-rw-r--r--linux-user/elfload.c2496
-rw-r--r--linux-user/errno_defs.h141
-rw-r--r--linux-user/flat.h67
-rw-r--r--linux-user/flatload.c815
-rw-r--r--linux-user/i386/syscall.h146
-rw-r--r--linux-user/i386/syscall_nr.h337
-rw-r--r--linux-user/i386/target_signal.h29
-rw-r--r--linux-user/i386/termbits.h226
-rw-r--r--linux-user/ioctls.h332
-rw-r--r--linux-user/linux_loop.h95
-rw-r--r--linux-user/linuxload.c207
-rw-r--r--linux-user/m68k-sim.c170
-rw-r--r--linux-user/m68k/syscall.h21
-rw-r--r--linux-user/m68k/syscall_nr.h330
-rw-r--r--linux-user/m68k/target_signal.h29
-rw-r--r--linux-user/m68k/termbits.h227
-rw-r--r--linux-user/main.c3375
-rw-r--r--linux-user/microblaze/syscall.h45
-rw-r--r--linux-user/microblaze/syscall_nr.h369
-rw-r--r--linux-user/microblaze/target_signal.h29
-rw-r--r--linux-user/microblaze/termbits.h213
-rw-r--r--linux-user/mips/syscall.h227
-rw-r--r--linux-user/mips/syscall_nr.h334
-rw-r--r--linux-user/mips/target_signal.h29
-rw-r--r--linux-user/mips/termbits.h245
-rw-r--r--linux-user/mips64/syscall.h221
-rw-r--r--linux-user/mips64/syscall_nr.h293
-rw-r--r--linux-user/mips64/target_signal.h29
-rw-r--r--linux-user/mips64/termbits.h245
-rw-r--r--linux-user/mipsn32/syscall.h221
-rw-r--r--linux-user/mipsn32/syscall_nr.h297
-rw-r--r--linux-user/mipsn32/target_signal.h29
-rw-r--r--linux-user/mipsn32/termbits.h245
-rw-r--r--linux-user/mmap.c759
-rw-r--r--linux-user/ppc/syscall.h64
-rw-r--r--linux-user/ppc/syscall_nr.h334
-rw-r--r--linux-user/ppc/target_signal.h29
-rw-r--r--linux-user/ppc/termbits.h236
-rw-r--r--linux-user/qemu-types.h24
-rw-r--r--linux-user/qemu.h440
-rw-r--r--linux-user/sh4/syscall.h12
-rw-r--r--linux-user/sh4/syscall_nr.h336
-rw-r--r--linux-user/sh4/target_signal.h29
-rw-r--r--linux-user/sh4/termbits.h274
-rw-r--r--linux-user/signal.c4981
-rw-r--r--linux-user/socket.h147
-rw-r--r--linux-user/sparc/syscall.h9
-rw-r--r--linux-user/sparc/syscall_nr.h287
-rw-r--r--linux-user/sparc/target_signal.h36
-rw-r--r--linux-user/sparc/termbits.h279
-rw-r--r--linux-user/sparc64/syscall.h10
-rw-r--r--linux-user/sparc64/syscall_nr.h324
-rw-r--r--linux-user/sparc64/target_signal.h36
-rw-r--r--linux-user/sparc64/termbits.h279
-rw-r--r--linux-user/strace.c1356
-rw-r--r--linux-user/strace.list1526
-rw-r--r--linux-user/syscall.c7514
-rw-r--r--linux-user/syscall_defs.h2207
-rw-r--r--linux-user/syscall_types.h183
-rw-r--r--linux-user/uaccess.c65
-rw-r--r--linux-user/vm86.c481
-rw-r--r--linux-user/x86_64/syscall.h98
-rw-r--r--linux-user/x86_64/syscall_nr.h295
-rw-r--r--linux-user/x86_64/target_signal.h29
-rw-r--r--linux-user/x86_64/termbits.h247
-rw-r--r--m68k-dis.c5051
-rw-r--r--m68k-semi.c407
-rw-r--r--m68k.ld175
-rw-r--r--microblaze-dis.c1100
-rw-r--r--migration-exec.c145
-rw-r--r--migration-fd.c130
-rw-r--r--migration-tcp.c204
-rw-r--r--migration-unix.c215
-rw-r--r--migration.c482
-rw-r--r--migration.h142
-rw-r--r--mips-dis.c4873
-rw-r--r--mips.ld224
-rw-r--r--module.c80
-rw-r--r--module.h38
-rw-r--r--monitor.c5261
-rw-r--r--monitor.h65
-rw-r--r--nbd.c765
-rw-r--r--nbd.h64
-rw-r--r--net-checksum.c86
-rw-r--r--net.c1439
-rw-r--r--net.h184
-rw-r--r--net/checksum.c85
-rw-r--r--net/checksum.h29
-rw-r--r--net/dump.c159
-rw-r--r--net/dump.h33
-rw-r--r--net/queue.c260
-rw-r--r--net/queue.h71
-rw-r--r--net/slirp.c771
-rw-r--r--net/slirp.h51
-rw-r--r--net/socket.c600
-rw-r--r--net/socket.h33
-rw-r--r--net/tap-aix.c61
-rw-r--r--net/tap-bsd.c131
-rw-r--r--net/tap-haiku.c61
-rw-r--r--net/tap-linux.c195
-rw-r--r--net/tap-linux.h63
-rw-r--r--net/tap-solaris.c227
-rw-r--r--net/tap-win32.c751
-rw-r--r--net/tap.c525
-rw-r--r--net/tap.h60
-rw-r--r--net/util.c60
-rw-r--r--net/util.h32
-rw-r--r--net/vde.c131
-rw-r--r--net/vde.h36
-rw-r--r--notify.c39
-rw-r--r--notify.h43
-rw-r--r--os-posix.c384
-rw-r--r--os-win32.c266
-rw-r--r--osdep.c169
-rw-r--r--osdep.h130
-rw-r--r--oslib-posix.c161
-rw-r--r--oslib-win32.c121
-rw-r--r--path.c165
-rw-r--r--pc-bios/Makefile19
-rw-r--r--pc-bios/README29
-rw-r--r--pc-bios/bamboo.dtbbin0 -> 3179 bytes
-rw-r--r--pc-bios/bamboo.dts234
-rw-r--r--pc-bios/bios-vista.diff17
-rw-r--r--pc-bios/bios.binbin0 -> 131072 bytes
-rw-r--r--pc-bios/bochs-manifest24
-rw-r--r--pc-bios/gpxe-eepro100-80861209.rombin0 -> 56832 bytes
-rw-r--r--pc-bios/keymaps/ar98
-rw-r--r--pc-bios/keymaps/bepo333
-rw-r--r--pc-bios/keymaps/common157
-rw-r--r--pc-bios/keymaps/da120
-rw-r--r--pc-bios/keymaps/de114
-rw-r--r--pc-bios/keymaps/de-ch169
-rw-r--r--pc-bios/keymaps/en-gb119
-rw-r--r--pc-bios/keymaps/en-us35
-rw-r--r--pc-bios/keymaps/es105
-rw-r--r--pc-bios/keymaps/et85
-rw-r--r--pc-bios/keymaps/fi124
-rw-r--r--pc-bios/keymaps/fo76
-rw-r--r--pc-bios/keymaps/fr181
-rw-r--r--pc-bios/keymaps/fr-be134
-rw-r--r--pc-bios/keymaps/fr-ca50
-rw-r--r--pc-bios/keymaps/fr-ch114
-rw-r--r--pc-bios/keymaps/hr125
-rw-r--r--pc-bios/keymaps/hu115
-rw-r--r--pc-bios/keymaps/is139
-rw-r--r--pc-bios/keymaps/it115
-rw-r--r--pc-bios/keymaps/ja109
-rw-r--r--pc-bios/keymaps/lt57
-rw-r--r--pc-bios/keymaps/lv128
-rw-r--r--pc-bios/keymaps/mk101
-rw-r--r--pc-bios/keymaps/modifiers18
-rw-r--r--pc-bios/keymaps/nl59
-rw-r--r--pc-bios/keymaps/nl-be3
-rw-r--r--pc-bios/keymaps/no119
-rw-r--r--pc-bios/keymaps/pl122
-rw-r--r--pc-bios/keymaps/pt113
-rw-r--r--pc-bios/keymaps/pt-br69
-rw-r--r--pc-bios/keymaps/ru109
-rw-r--r--pc-bios/keymaps/sl110
-rw-r--r--pc-bios/keymaps/sv81
-rw-r--r--pc-bios/keymaps/th131
-rw-r--r--pc-bios/keymaps/tr123
-rw-r--r--pc-bios/linuxboot.binbin0 -> 1025 bytes
-rw-r--r--pc-bios/mpc8544ds.dtbbin0 -> 12288 bytes
-rw-r--r--pc-bios/mpc8544ds.dts122
-rw-r--r--pc-bios/multiboot.binbin0 -> 1024 bytes
-rw-r--r--pc-bios/ohw.diff1843
-rw-r--r--pc-bios/openbios-ppcbin0 -> 729876 bytes
-rw-r--r--pc-bios/openbios-sparcbin0 -> 506966 bytes
-rw-r--r--pc-bios/openbios-sparc32bin0 -> 377388 bytes
-rw-r--r--pc-bios/openbios-sparc64bin0 -> 1598328 bytes
-rw-r--r--pc-bios/optionrom/Makefile31
-rw-r--r--pc-bios/optionrom/extboot.S691
-rw-r--r--pc-bios/optionrom/linuxboot.S137
-rw-r--r--pc-bios/optionrom/multiboot.S184
-rw-r--r--pc-bios/optionrom/optionrom.h126
-rw-r--r--pc-bios/optionrom/vapic.S330
-rw-r--r--pc-bios/petalogix-s3adsp1800.dtbbin0 -> 8259 bytes
-rw-r--r--pc-bios/ppc_rom.binbin0 -> 524288 bytes
-rw-r--r--pc-bios/pxe-e1000.binbin0 -> 72192 bytes
-rw-r--r--pc-bios/pxe-ne2k_pci.binbin0 -> 56320 bytes
-rw-r--r--pc-bios/pxe-pcnet.binbin0 -> 56832 bytes
-rw-r--r--pc-bios/pxe-rtl8139.binbin0 -> 56320 bytes
-rw-r--r--pc-bios/pxe-virtio.binbin0 -> 56320 bytes
-rw-r--r--pc-bios/s390-zipl.rombin0 -> 3336 bytes
-rwxr-xr-xpc-bios/vapic.binbin0 -> 8960 bytes
-rw-r--r--pc-bios/vgabios-cirrus.binbin0 -> 35840 bytes
-rw-r--r--pc-bios/vgabios-qxl.binbin0 -> 40448 bytes
-rw-r--r--pc-bios/vgabios-stdvga.binbin0 -> 40448 bytes
-rw-r--r--pc-bios/vgabios-vmware.binbin0 -> 40448 bytes
-rw-r--r--pc-bios/vgabios.binbin0 -> 40448 bytes
-rw-r--r--pci-ids.txt31
-rw-r--r--pflib.c213
-rw-r--r--pflib.h20
-rw-r--r--poison.h50
-rw-r--r--posix-aio-compat.c655
-rw-r--r--ppc-dis.c5412
-rw-r--r--ppc.ld227
-rw-r--r--ppc64.ld220
-rw-r--r--qbool.c68
-rw-r--r--qbool.h29
-rw-r--r--qdict-test-data.txt4999
-rw-r--r--qdict.c456
-rw-r--r--qdict.h67
-rw-r--r--qemu-aio.h59
-rw-r--r--qemu-barrier.h10
-rw-r--r--qemu-char.c2629
-rw-r--r--qemu-char.h123
-rw-r--r--qemu-common.h368
-rw-r--r--qemu-config.c677
-rw-r--r--qemu-config.h19
-rw-r--r--qemu-doc.texi2713
-rw-r--r--qemu-error.c209
-rw-r--r--qemu-error.h40
-rw-r--r--qemu-img-cmds.hx59
-rw-r--r--qemu-img.c1528
-rw-r--r--qemu-img.texi266
-rw-r--r--qemu-io.c1831
-rw-r--r--qemu-kvm-ia64.c145
-rw-r--r--qemu-kvm-x86.c814
-rw-r--r--qemu-kvm.c1784
-rw-r--r--qemu-kvm.h771
-rw-r--r--qemu-lock.h237
-rw-r--r--qemu-log.h93
-rw-r--r--qemu-malloc.c98
-rw-r--r--qemu-nbd.c499
-rw-r--r--qemu-nbd.texi66
-rw-r--r--qemu-objects.h25
-rw-r--r--qemu-option.c977
-rw-r--r--qemu-option.h135
-rw-r--r--qemu-options.h41
-rw-r--r--qemu-options.hx2372
-rw-r--r--qemu-os-posix.h54
-rw-r--r--qemu-os-win32.h63
-rw-r--r--qemu-queue.h449
-rw-r--r--qemu-sockets.c675
-rw-r--r--qemu-tech.texi697
-rw-r--r--qemu-thread.c192
-rw-r--r--qemu-thread.h44
-rw-r--r--qemu-timer-common.c63
-rw-r--r--qemu-timer.c1122
-rw-r--r--qemu-timer.h313
-rw-r--r--qemu-tool.c113
-rw-r--r--qemu-x509.h9
-rw-r--r--qemu.sasl34
-rw-r--r--qemu_socket.h62
-rw-r--r--qerror.c463
-rw-r--r--qerror.h174
-rw-r--r--qfloat.c68
-rw-r--r--qfloat.h29
-rw-r--r--qint.c67
-rw-r--r--qint.h28
-rw-r--r--qjson.c294
-rw-r--r--qjson.h28
-rw-r--r--qlist.c157
-rw-r--r--qlist.h53
-rw-r--r--qmp-commands.hx1776
-rw-r--r--qobject.h112
-rw-r--r--qstring.c141
-rw-r--r--qstring.h35
-rw-r--r--range.h29
-rw-r--r--readline.c476
-rw-r--r--readline.h55
-rw-r--r--roms/seabios/config.mak8
-rw-r--r--roms/vgabios/config.mak8
-rw-r--r--rules.mak64
-rw-r--r--rwhandler.c87
-rw-r--r--rwhandler.h27
-rw-r--r--s390-dis.c1796
-rw-r--r--s390.ld201
-rw-r--r--savevm.c2172
-rwxr-xr-xscripts/checkpatch.pl2910
-rwxr-xr-xscripts/create_config103
-rw-r--r--scripts/feature_to_c.sh78
-rw-r--r--scripts/hxtool102
-rw-r--r--scripts/make_device_config.sh28
-rw-r--r--scripts/qemu-binfmt-conf.sh66
-rwxr-xr-xscripts/signrom.sh45
-rwxr-xr-xscripts/simpletrace.py93
-rwxr-xr-xscripts/texi2pod.pl477
-rwxr-xr-xscripts/tracetool629
-rw-r--r--sh4-dis.c2077
-rw-r--r--share/qemu/bamboo.dtbbin0 -> 3179 bytes
-rw-r--r--share/qemu/bios.binbin0 -> 131072 bytes
-rw-r--r--share/qemu/gpxe-eepro100-80861209.rombin0 -> 56832 bytes
-rw-r--r--share/qemu/keymaps/ar98
-rw-r--r--share/qemu/keymaps/common157
-rw-r--r--share/qemu/keymaps/da120
-rw-r--r--share/qemu/keymaps/de114
-rw-r--r--share/qemu/keymaps/de-ch169
-rw-r--r--share/qemu/keymaps/en-gb119
-rw-r--r--share/qemu/keymaps/en-us35
-rw-r--r--share/qemu/keymaps/es105
-rw-r--r--share/qemu/keymaps/et85
-rw-r--r--share/qemu/keymaps/fi124
-rw-r--r--share/qemu/keymaps/fo76
-rw-r--r--share/qemu/keymaps/fr181
-rw-r--r--share/qemu/keymaps/fr-be134
-rw-r--r--share/qemu/keymaps/fr-ca50
-rw-r--r--share/qemu/keymaps/fr-ch114
-rw-r--r--share/qemu/keymaps/hr125
-rw-r--r--share/qemu/keymaps/hu115
-rw-r--r--share/qemu/keymaps/is139
-rw-r--r--share/qemu/keymaps/it115
-rw-r--r--share/qemu/keymaps/ja109
-rw-r--r--share/qemu/keymaps/lt57
-rw-r--r--share/qemu/keymaps/lv128
-rw-r--r--share/qemu/keymaps/mk101
-rw-r--r--share/qemu/keymaps/modifiers18
-rw-r--r--share/qemu/keymaps/nl59
-rw-r--r--share/qemu/keymaps/nl-be3
-rw-r--r--share/qemu/keymaps/no119
-rw-r--r--share/qemu/keymaps/pl122
-rw-r--r--share/qemu/keymaps/pt113
-rw-r--r--share/qemu/keymaps/pt-br69
-rw-r--r--share/qemu/keymaps/ru109
-rw-r--r--share/qemu/keymaps/sl110
-rw-r--r--share/qemu/keymaps/sv81
-rw-r--r--share/qemu/keymaps/th131
-rw-r--r--share/qemu/keymaps/tr123
-rw-r--r--share/qemu/linuxboot.binbin0 -> 1025 bytes
-rw-r--r--share/qemu/multiboot.binbin0 -> 1024 bytes
-rw-r--r--share/qemu/openbios-ppcbin0 -> 729876 bytes
-rw-r--r--share/qemu/openbios-sparc32bin0 -> 377388 bytes
-rw-r--r--share/qemu/openbios-sparc64bin0 -> 1598328 bytes
-rw-r--r--share/qemu/petalogix-s3adsp1800.dtbbin0 -> 8259 bytes
-rw-r--r--share/qemu/ppc_rom.binbin0 -> 524288 bytes
-rw-r--r--share/qemu/pxe-e1000.binbin0 -> 72192 bytes
-rw-r--r--share/qemu/pxe-ne2k_pci.binbin0 -> 56320 bytes
-rw-r--r--share/qemu/pxe-pcnet.binbin0 -> 56832 bytes
-rw-r--r--share/qemu/pxe-rtl8139.binbin0 -> 56320 bytes
-rw-r--r--share/qemu/pxe-virtio.binbin0 -> 56320 bytes
-rw-r--r--share/qemu/s390-zipl.rombin0 -> 3336 bytes
-rw-r--r--share/qemu/vapic.binbin0 -> 8960 bytes
-rw-r--r--share/qemu/vgabios-cirrus.binbin0 -> 35840 bytes
-rw-r--r--share/qemu/vgabios-qxl.binbin0 -> 40448 bytes
-rw-r--r--share/qemu/vgabios-stdvga.binbin0 -> 40448 bytes
-rw-r--r--share/qemu/vgabios-vmware.binbin0 -> 40448 bytes
-rw-r--r--share/qemu/vgabios.binbin0 -> 40448 bytes
-rw-r--r--simpletrace.c255
-rw-r--r--simpletrace.h40
-rw-r--r--slirp/COPYRIGHT61
-rw-r--r--slirp/bootp.c314
-rw-r--r--slirp/bootp.h122
-rw-r--r--slirp/cksum.c139
-rw-r--r--slirp/debug.h34
-rw-r--r--slirp/if.c209
-rw-r--r--slirp/if.h25
-rw-r--r--slirp/ip.h253
-rw-r--r--slirp/ip_icmp.c351
-rw-r--r--slirp/ip_icmp.h161
-rw-r--r--slirp/ip_input.c682
-rw-r--r--slirp/ip_output.c172
-rw-r--r--slirp/libslirp.h56
-rw-r--r--slirp/main.h46
-rw-r--r--slirp/mbuf.c218
-rw-r--r--slirp/mbuf.h127
-rw-r--r--slirp/misc.c406
-rw-r--r--slirp/misc.h72
-rw-r--r--slirp/sbuf.c181
-rw-r--r--slirp/sbuf.h30
-rw-r--r--slirp/slirp.c1113
-rw-r--r--slirp/slirp.h320
-rw-r--r--slirp/slirp_config.h188
-rw-r--r--slirp/socket.c726
-rw-r--r--slirp/socket.h95
-rw-r--r--slirp/tcp.h164
-rw-r--r--slirp/tcp_input.c1487
-rw-r--r--slirp/tcp_output.c492
-rw-r--r--slirp/tcp_subr.c915
-rw-r--r--slirp/tcp_timer.c292
-rw-r--r--slirp/tcp_timer.h127
-rw-r--r--slirp/tcp_var.h161
-rw-r--r--slirp/tcpip.h77
-rw-r--r--slirp/tftp.c422
-rw-r--r--slirp/tftp.h43
-rw-r--r--slirp/udp.c384
-rw-r--r--slirp/udp.h86
-rw-r--r--softmmu-semi.h70
-rw-r--r--softmmu_defs.h22
-rw-r--r--softmmu_exec.h153
-rw-r--r--softmmu_header.h198
-rw-r--r--softmmu_template.h332
-rw-r--r--sparc-dis.c3275
-rw-r--r--sparc.ld150
-rw-r--r--sparc64.ld138
-rw-r--r--spice-qemu-char.c190
-rw-r--r--sysconfigs/target/target-x86_64.conf86
-rw-r--r--sysemu.h197
-rw-r--r--target-alpha/STATUS28
-rw-r--r--target-alpha/cpu.h538
-rw-r--r--target-alpha/exec.h61
-rw-r--r--target-alpha/helper.c570
-rw-r--r--target-alpha/helper.h126
-rw-r--r--target-alpha/op_helper.c1385
-rw-r--r--target-alpha/translate.c3374
-rw-r--r--target-arm/cpu.h508
-rw-r--r--target-arm/exec.h58
-rw-r--r--target-arm/helper.c2687
-rw-r--r--target-arm/helpers.h458
-rw-r--r--target-arm/iwmmxt_helper.c681
-rw-r--r--target-arm/machine.c211
-rw-r--r--target-arm/neon_helper.c1603
-rw-r--r--target-arm/op_addsub.h103
-rw-r--r--target-arm/op_helper.c426
-rw-r--r--target-arm/translate.c9409
-rw-r--r--target-cris/cpu.h268
-rw-r--r--target-cris/crisv10-decode.h107
-rw-r--r--target-cris/crisv32-decode.h128
-rw-r--r--target-cris/exec.h53
-rw-r--r--target-cris/helper.c271
-rw-r--r--target-cris/helper.h26
-rw-r--r--target-cris/machine.c90
-rw-r--r--target-cris/mmu.c368
-rw-r--r--target-cris/mmu.h17
-rw-r--r--target-cris/op_helper.c655
-rw-r--r--target-cris/opcode-cris.h365
-rw-r--r--target-cris/translate.c3611
-rw-r--r--target-cris/translate_v10.c1245
-rw-r--r--target-i386/TODO32
-rw-r--r--target-i386/cpu.h990
-rw-r--r--target-i386/cpuid.c1266
-rw-r--r--target-i386/exec.h335
-rw-r--r--target-i386/fake-exec.c52
-rw-r--r--target-i386/helper.c1253
-rw-r--r--target-i386/helper.h220
-rw-r--r--target-i386/helper_template.h334
-rw-r--r--target-i386/kvm.c1957
-rw-r--r--target-i386/kvm_x86.h25
-rw-r--r--target-i386/libkvm.h28
-rw-r--r--target-i386/machine.c516
-rw-r--r--target-i386/op_helper.c5669
-rw-r--r--target-i386/ops_sse.h2075
-rw-r--r--target-i386/ops_sse_header.h349
-rw-r--r--target-i386/svm.h222
-rw-r--r--target-i386/translate.c7915
-rw-r--r--target-ia64/cpu.h85
-rw-r--r--target-ia64/exec.h61
-rw-r--r--target-ia64/fake-exec.c50
-rw-r--r--target-ia64/firmware.c715
-rw-r--r--target-ia64/firmware.h62
-rw-r--r--target-ia64/helper.c5
-rw-r--r--target-ia64/libkvm.c82
-rw-r--r--target-ia64/libkvm.h31
-rw-r--r--target-ia64/machine.c36
-rw-r--r--target-ia64/op.c22
-rw-r--r--target-ia64/op_helper.c104
-rw-r--r--target-ia64/translate.c39
-rw-r--r--target-m68k/cpu.h257
-rw-r--r--target-m68k/exec.h50
-rw-r--r--target-m68k/helper.c924
-rw-r--r--target-m68k/helpers.h54
-rw-r--r--target-m68k/m68k-qreg.h11
-rw-r--r--target-m68k/machine.c0
-rw-r--r--target-m68k/op_helper.c225
-rw-r--r--target-m68k/qregs.def13
-rw-r--r--target-m68k/translate.c3120
-rw-r--r--target-microblaze/cpu.h336
-rw-r--r--target-microblaze/exec.h52
-rw-r--r--target-microblaze/helper.c282
-rw-r--r--target-microblaze/helper.h36
-rw-r--r--target-microblaze/machine.c11
-rw-r--r--target-microblaze/microblaze-decode.h52
-rw-r--r--target-microblaze/mmu.c303
-rw-r--r--target-microblaze/mmu.h91
-rw-r--r--target-microblaze/op_helper.c491
-rw-r--r--target-microblaze/translate.c1906
-rw-r--r--target-mips/TODO52
-rw-r--r--target-mips/cpu.h659
-rw-r--r--target-mips/exec.h98
-rw-r--r--target-mips/helper.c696
-rw-r--r--target-mips/helper.h290
-rw-r--r--target-mips/machine.c308
-rw-r--r--target-mips/mips-defs.h72
-rw-r--r--target-mips/op_helper.c3044
-rw-r--r--target-mips/translate.c12746
-rw-r--r--target-mips/translate_init.c594
-rw-r--r--target-ppc/STATUS559
-rw-r--r--target-ppc/cpu.h1634
-rw-r--r--target-ppc/exec.h57
-rw-r--r--target-ppc/fake-exec.c104
-rw-r--r--target-ppc/helper.c2822
-rw-r--r--target-ppc/helper.h404
-rw-r--r--target-ppc/helper_regs.h111
-rw-r--r--target-ppc/kvm.c406
-rw-r--r--target-ppc/kvm_ppc.c105
-rw-r--r--target-ppc/kvm_ppc.h33
-rw-r--r--target-ppc/libkvm.c102
-rw-r--r--target-ppc/libkvm.h36
-rw-r--r--target-ppc/machine.c180
-rw-r--r--target-ppc/mfrom_table.c79
-rw-r--r--target-ppc/mfrom_table_gen.c33
-rw-r--r--target-ppc/op_helper.c4172
-rw-r--r--target-ppc/translate.c9141
-rw-r--r--target-ppc/translate_init.c9768
-rw-r--r--target-s390x/cpu.h269
-rw-r--r--target-s390x/exec.h53
-rw-r--r--target-s390x/helper.c84
-rw-r--r--target-s390x/kvm.c507
-rw-r--r--target-s390x/machine.c30
-rw-r--r--target-s390x/op_helper.c73
-rw-r--r--target-s390x/translate.c61
-rw-r--r--target-sh4/README.sh4150
-rw-r--r--target-sh4/cpu.h364
-rw-r--r--target-sh4/exec.h56
-rw-r--r--target-sh4/helper.c843
-rw-r--r--target-sh4/helper.h54
-rw-r--r--target-sh4/machine.c0
-rw-r--r--target-sh4/op_helper.c817
-rw-r--r--target-sh4/translate.c2077
-rw-r--r--target-sparc/TODO88
-rw-r--r--target-sparc/cpu.h659
-rw-r--r--target-sparc/exec.h41
-rw-r--r--target-sparc/helper.c1546
-rw-r--r--target-sparc/helper.h166
-rw-r--r--target-sparc/machine.c199
-rw-r--r--target-sparc/op_helper.c4535
-rw-r--r--target-sparc/translate.c5106
-rw-r--r--targphys.h21
-rw-r--r--tcg-runtime.c85
-rw-r--r--tcg/LICENSE3
-rw-r--r--tcg/README511
-rw-r--r--tcg/TODO14
-rw-r--r--tcg/arm/tcg-target.c1843
-rw-r--r--tcg/arm/tcg-target.h93
-rw-r--r--tcg/hppa/tcg-target.c1683
-rw-r--r--tcg/hppa/tcg-target.h119
-rw-r--r--tcg/i386/tcg-target.c1982
-rw-r--r--tcg/i386/tcg-target.h126
-rw-r--r--tcg/ia64/tcg-target.c2390
-rw-r--r--tcg/ia64/tcg-target.h156
-rw-r--r--tcg/mips/tcg-target.c1533
-rw-r--r--tcg/mips/tcg-target.h110
-rw-r--r--tcg/ppc/tcg-target.c1922
-rw-r--r--tcg/ppc/tcg-target.h98
-rw-r--r--tcg/ppc64/tcg-target.c1699
-rw-r--r--tcg/ppc64/tcg-target.h109
-rw-r--r--tcg/s390/tcg-target.c2324
-rw-r--r--tcg/s390/tcg-target.h109
-rw-r--r--tcg/sparc/tcg-target.c1569
-rw-r--r--tcg/sparc/tcg-target.h150
-rw-r--r--tcg/tcg-op.h2533
-rw-r--r--tcg/tcg-opc.h310
-rw-r--r--tcg/tcg-runtime.h18
-rw-r--r--tcg/tcg.c2175
-rw-r--r--tcg/tcg.h489
-rw-r--r--tests/Makefile147
-rw-r--r--tests/alpha/Makefile35
-rw-r--r--tests/alpha/crt.s26
-rw-r--r--tests/alpha/hello-alpha.c5
-rw-r--r--tests/alpha/test-cond.c87
-rw-r--r--tests/alpha/test-ovf.c29
-rw-r--r--tests/cris/Makefile155
-rw-r--r--tests/cris/README1
-rw-r--r--tests/cris/check_abs.c40
-rw-r--r--tests/cris/check_addc.c58
-rw-r--r--tests/cris/check_addcm.c85
-rw-r--r--tests/cris/check_addi.s57
-rw-r--r--tests/cris/check_addiv32.s62
-rw-r--r--tests/cris/check_addm.s96
-rw-r--r--tests/cris/check_addo.c125
-rw-r--r--tests/cris/check_addoq.c44
-rw-r--r--tests/cris/check_addq.s47
-rw-r--r--tests/cris/check_addr.s96
-rw-r--r--tests/cris/check_addxc.s91
-rw-r--r--tests/cris/check_addxm.s106
-rw-r--r--tests/cris/check_addxr.s96
-rw-r--r--tests/cris/check_andc.s80
-rw-r--r--tests/cris/check_andm.s90
-rw-r--r--tests/cris/check_andq.s46
-rw-r--r--tests/cris/check_andr.s95
-rw-r--r--tests/cris/check_asr.s230
-rw-r--r--tests/cris/check_ba.s93
-rw-r--r--tests/cris/check_bas.s102
-rw-r--r--tests/cris/check_bcc.s197
-rw-r--r--tests/cris/check_bound.c142
-rw-r--r--tests/cris/check_boundc.s101
-rw-r--r--tests/cris/check_boundr.s125
-rw-r--r--tests/cris/check_btst.s96
-rw-r--r--tests/cris/check_clearfv32.s19
-rw-r--r--tests/cris/check_clrjmp1.s36
-rw-r--r--tests/cris/check_cmp-2.s15
-rw-r--r--tests/cris/check_cmpc.s86
-rw-r--r--tests/cris/check_cmpm.s96
-rw-r--r--tests/cris/check_cmpq.s75
-rw-r--r--tests/cris/check_cmpr.s102
-rw-r--r--tests/cris/check_cmpxc.s92
-rw-r--r--tests/cris/check_cmpxm.s106
-rw-r--r--tests/cris/check_dstep.s42
-rw-r--r--tests/cris/check_ftag.c37
-rw-r--r--tests/cris/check_gcctorture_pr28634-1.c15
-rw-r--r--tests/cris/check_gcctorture_pr28634.c15
-rw-r--r--tests/cris/check_glibc_kernelversion.c116
-rw-r--r--tests/cris/check_hello.c7
-rw-r--r--tests/cris/check_int64.c47
-rw-r--r--tests/cris/check_jsr.s85
-rw-r--r--tests/cris/check_lapc.s78
-rw-r--r--tests/cris/check_lsl.s217
-rw-r--r--tests/cris/check_lsr.s218
-rw-r--r--tests/cris/check_lz.c49
-rw-r--r--tests/cris/check_mapbrk.c39
-rw-r--r--tests/cris/check_mcp.s49
-rw-r--r--tests/cris/check_mmap1.c48
-rw-r--r--tests/cris/check_mmap2.c48
-rw-r--r--tests/cris/check_mmap3.c33
-rw-r--r--tests/cris/check_movdelsr1.s33
-rw-r--r--tests/cris/check_movecr.s37
-rw-r--r--tests/cris/check_movei.s50
-rw-r--r--tests/cris/check_movemr.s78
-rw-r--r--tests/cris/check_movemrv32.s96
-rw-r--r--tests/cris/check_moveq.c51
-rw-r--r--tests/cris/check_mover.s28
-rw-r--r--tests/cris/check_moverm.s45
-rw-r--r--tests/cris/check_movmp.s131
-rw-r--r--tests/cris/check_movpmv32.s35
-rw-r--r--tests/cris/check_movpr.s28
-rw-r--r--tests/cris/check_movprv32.s21
-rw-r--r--tests/cris/check_movscr.s29
-rw-r--r--tests/cris/check_movsm.s44
-rw-r--r--tests/cris/check_movsr.s46
-rw-r--r--tests/cris/check_movucr.s33
-rw-r--r--tests/cris/check_movum.s40
-rw-r--r--tests/cris/check_movur.s45
-rw-r--r--tests/cris/check_mulv32.s51
-rw-r--r--tests/cris/check_mulx.s246
-rw-r--r--tests/cris/check_neg.s104
-rw-r--r--tests/cris/check_not.s31
-rw-r--r--tests/cris/check_openpf1.c38
-rw-r--r--tests/cris/check_openpf2.c16
-rw-r--r--tests/cris/check_openpf3.c49
-rw-r--r--tests/cris/check_openpf4.c5
-rw-r--r--tests/cris/check_openpf5.c56
-rw-r--r--tests/cris/check_orc.s71
-rw-r--r--tests/cris/check_orm.s75
-rw-r--r--tests/cris/check_orq.s41
-rw-r--r--tests/cris/check_orr.s84
-rw-r--r--tests/cris/check_ret.s25
-rw-r--r--tests/cris/check_scc.s95
-rw-r--r--tests/cris/check_settls1.c45
-rw-r--r--tests/cris/check_sigalrm.c26
-rw-r--r--tests/cris/check_stat1.c16
-rw-r--r--tests/cris/check_stat2.c20
-rw-r--r--tests/cris/check_stat3.c25
-rw-r--r--tests/cris/check_stat4.c27
-rw-r--r--tests/cris/check_subc.s87
-rw-r--r--tests/cris/check_subm.s96
-rw-r--r--tests/cris/check_subq.s52
-rw-r--r--tests/cris/check_subr.s102
-rw-r--r--tests/cris/check_swap.c76
-rw-r--r--tests/cris/check_time1.c46
-rw-r--r--tests/cris/check_time2.c18
-rw-r--r--tests/cris/check_xarith.s72
-rw-r--r--tests/cris/crisutils.h71
-rw-r--r--tests/cris/crt.s13
-rw-r--r--tests/cris/sys.c51
-rw-r--r--tests/cris/sys.h16
-rw-r--r--tests/cris/testutils.inc117
-rw-r--r--tests/hello-arm.c113
-rw-r--r--tests/hello-i386.c26
-rw-r--r--tests/hello-mips.c64
-rw-r--r--tests/linux-test.c535
-rw-r--r--tests/pi_10.combin0 -> 54 bytes
-rw-r--r--tests/qruncom.c284
-rw-r--r--tests/runcom.c192
-rw-r--r--tests/sha1.c240
-rw-r--r--tests/test-arm-iwmmxt.s49
-rw-r--r--tests/test-i386-code16.S79
-rw-r--r--tests/test-i386-muldiv.h76
-rw-r--r--tests/test-i386-shift.h185
-rw-r--r--tests/test-i386-ssse3.c57
-rw-r--r--tests/test-i386-vm86.S103
-rw-r--r--tests/test-i386.c2764
-rw-r--r--tests/test-i386.h152
-rw-r--r--tests/test-mmap.c476
-rw-r--r--tests/test_path.c160
-rw-r--r--tests/testthread.c51
-rw-r--r--thunk.c288
-rw-r--r--thunk.h161
-rw-r--r--trace-events256
-rw-r--r--translate-all.c167
-rw-r--r--uboot_image.h158
-rw-r--r--ui/cocoa.m1014
-rw-r--r--ui/curses.c368
-rw-r--r--ui/curses_keys.h509
-rw-r--r--ui/d3des.c424
-rw-r--r--ui/d3des.h51
-rw-r--r--ui/keymaps.c210
-rw-r--r--ui/keymaps.h77
-rw-r--r--ui/qemu-spice.h57
-rw-r--r--ui/sdl.c885
-rw-r--r--ui/sdl_keysym.h277
-rw-r--r--ui/sdl_zoom.c95
-rw-r--r--ui/sdl_zoom.h25
-rw-r--r--ui/sdl_zoom_template.h225
-rw-r--r--ui/spice-core.c672
-rw-r--r--ui/spice-display.c412
-rw-r--r--ui/spice-display.h68
-rw-r--r--ui/spice-input.c217
-rw-r--r--ui/vnc-auth-sasl.c639
-rw-r--r--ui/vnc-auth-sasl.h74
-rw-r--r--ui/vnc-auth-vencrypt.c175
-rw-r--r--ui/vnc-auth-vencrypt.h33
-rw-r--r--ui/vnc-enc-hextile-template.h211
-rw-r--r--ui/vnc-enc-hextile.c116
-rw-r--r--ui/vnc-enc-tight.c1718
-rw-r--r--ui/vnc-enc-tight.h183
-rw-r--r--ui/vnc-enc-zlib.c152
-rw-r--r--ui/vnc-jobs-async.c335
-rw-r--r--ui/vnc-jobs-sync.c73
-rw-r--r--ui/vnc-jobs.h87
-rw-r--r--ui/vnc-palette.c136
-rw-r--r--ui/vnc-palette.h63
-rw-r--r--ui/vnc-tls.c445
-rw-r--r--ui/vnc-tls.h76
-rw-r--r--ui/vnc.c2768
-rw-r--r--ui/vnc.h502
-rw-r--r--ui/vnc_keysym.h324
-rw-r--r--ui/x_keymap.c168
-rw-r--r--ui/x_keymap.h32
-rw-r--r--usb-bsd.c643
-rw-r--r--usb-linux.c1722
-rw-r--r--usb-stub.c52
-rw-r--r--version.rc28
-rw-r--r--vgafont.h4611
-rw-r--r--vl.c3208
-rw-r--r--x86_64.ld182
1627 files changed, 711652 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a29c1a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,64 @@
+config-devices.*
+config-all-devices.*
+config-host.*
+config-target.*
+trace.h
+trace.c
+trace-dtrace.h
+trace-dtrace.dtrace
+*-timestamp
+*-softmmu
+*-darwin-user
+*-linux-user
+*-bsd-user
+libdis*
+libhw32
+libhw64
+libuser
+qemu-doc.html
+qemu-tech.html
+qemu-doc.info
+qemu-tech.info
+qemu.1
+qemu.pod
+qemu-img.1
+qemu-img.pod
+qemu-img
+qemu-nbd
+qemu-nbd.8
+qemu-nbd.pod
+qemu-options.def
+qemu-options.texi
+qemu-img-cmds.texi
+qemu-img-cmds.h
+qemu-io
+qemu-monitor.texi
+QMP/qmp-commands.txt
+.gdbinit
+*.a
+*.aux
+*.cp
+*.dvi
+*.exe
+*.fn
+*.ky
+*.log
+*.pdf
+*.pg
+*.pyc
+*.toc
+*.tp
+*.vr
+*.d
+*.o
+.pc
+patches
+pc-bios/bios-pq/status
+pc-bios/vgabios-pq/status
+pc-bios/optionrom/linuxboot.bin
+pc-bios/optionrom/multiboot.bin
+pc-bios/optionrom/multiboot.raw
+pc-bios/optionrom/extboot.bin
+pc-bios/optionrom/vapic.bin
+.stgit-*
+cscope.*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..cb52264
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "roms/vgabios"]
+ path = roms/vgabios
+ url = git://git.kernel.org/pub/scm/virt/kvm/vgabios.git/
+[submodule "roms/seabios"]
+ path = roms/seabios
+ url = git://git.qemu.org/seabios.git/
diff --git a/CODING_STYLE b/CODING_STYLE
new file mode 100644
index 0000000..5ecfa22
--- /dev/null
+++ b/CODING_STYLE
@@ -0,0 +1,81 @@
+Qemu Coding Style
+=================
+
+Please use the script checkpatch.pl in the scripts directory to check
+patches before submitting.
+
+1. Whitespace
+
+Of course, the most important aspect in any coding style is whitespace.
+Crusty old coders who have trouble spotting the glasses on their noses
+can tell the difference between a tab and eight spaces from a distance
+of approximately fifteen parsecs. Many a flamewar have been fought and
+lost on this issue.
+
+QEMU indents are four spaces. Tabs are never used, except in Makefiles
+where they have been irreversibly coded into the syntax.
+Spaces of course are superior to tabs because:
+
+ - You have just one way to specify whitespace, not two. Ambiguity breeds
+ mistakes.
+ - The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
+ - Tab indents push your code to the right, making your screen seriously
+ unbalanced.
+ - Tabs will be rendered incorrectly on editors who are misconfigured not
+ to use tab stops of eight positions.
+ - Tabs are rendered badly in patches, causing off-by-one errors in almost
+ every line.
+ - It is the QEMU coding style.
+
+Do not leave whitespace dangling off the ends of lines.
+
+2. Line width
+
+Lines are 80 characters; not longer.
+
+Rationale:
+ - Some people like to tile their 24" screens with a 6x4 matrix of 80x24
+ xterms and use vi in all of them. The best way to punish them is to
+ let them keep doing it.
+ - Code and especially patches is much more readable if limited to a sane
+ line length. Eighty is traditional.
+ - It is the QEMU coding style.
+
+3. Naming
+
+Variables are lower_case_with_underscores; easy to type and read. Structured
+type names are in CamelCase; harder to type but standing out. Scalar type
+names are lower_case_with_underscores_ending_with_a_t, like the POSIX
+uint64_t and family. Note that this last convention contradicts POSIX
+and is therefore likely to be changed.
+
+When wrapping standard library functions, use the prefix qemu_ to alert
+readers that they are seeing a wrapped version; otherwise avoid this prefix.
+
+4. Block structure
+
+Every indented statement is braced; even if the block contains just one
+statement. The opening brace is on the line that contains the control
+flow statement that introduces the new block; the closing brace is on the
+same line as the else keyword, or on a line by itself if there is no else
+keyword. Example:
+
+ if (a == 5) {
+ printf("a was 5.\n");
+ } else if (a == 6) {
+ printf("a was 6.\n");
+ } else {
+ printf("a was something else entirely.\n");
+ }
+
+An exception is the opening brace for a function; for reasons of tradition
+and clarity it comes on a line by itself:
+
+ void a_function(void)
+ {
+ do_something();
+ }
+
+Rationale: a consistent (except for functions...) bracing style reduces
+ambiguity and avoids needless churn when lines are added or removed.
+Furthermore, it is the QEMU coding style.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..00ccfbb
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..48afc2e
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..152feaa
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,574 @@
+version 0.12.0:
+
+ - Update to SeaBIOS 0.5.0
+ - e1000: fix device link status in Linux (Anthony Liguori)
+ - monitor: fix QMP for balloon command (Luiz Capitulino)
+ - QMP: Return an empty dict by default (Luiz Capitulino)
+ - QMP: Only handle converted commands (Luiz Capitulino)
+ - pci: support PCI based option rom loading (Gerd Hoffman/Anthony Liguori)
+ - Fix backcompat for hotplug of SCSI controllers (Daniel P. Berrange)
+ - fdc: fix migration from 0.11 (Juan Quintela)
+ - vmware-vga: fix segv on cursor resize. (Dave Airlie)
+ - vmware-vga: various fixes (Dave Airlie/Anthony Liguori)
+ - qdev: improve property error reporting. (Gerd Hoffmann)
+ - fix vga names in default_list (Gerd Hoffmann)
+ - usb-host: check mon before using it. (Gerd Hoffmann)
+ - usb-net: use qdev for -usbdevice (Gerd Hoffmann)
+ - monitor: Catch printing to non-existent monitor (Luiz Capitulino)
+ - Avoid permanently disabled QEMU monitor when UNIX migration fails (Daniel P. Berrange)
+ - Fix loading of ELF multiboot kernels (Kevin Wolf)
+ - qemu-io: Fix memory leak (Kevin Wolf)
+ - Fix thinko in linuxboot.S (Paolo Bonzini)
+ - target-i386: Fix evaluation of DR7 register (Jan Kiszka)
+ - vnc: hextile: do not generate ForegroundSpecified and SubrectsColoured tiles (Anthony Liguori)
+ - S390: Bail out without KVM (Alexander Graf)
+ - S390: Don't tell guest we're updating config space (Alexander Graf)
+ - target-s390: Fail on unknown instructions (Alexander Graf)
+ - osdep: Fix runtime failure on older Linux kernels (Andre Przywara)
+ - Fix a make -j race (Juergen Lock)
+ - target-alpha: Fix generic ctz64. (Richard Henderson)
+ - s390: Fix buggy assignment (Stefan Weil)
+ - target-mips: fix user-mode emulation startup (Nathan Froyd)
+ - target-i386: Update CPUID feature set for TCG (Andre Przywara)
+ - s390: fix build on 32 bit host (Michael S. Tsirkin)
+
+version 0.12.0-rc2:
+
+ - v2: properly save kvm system time msr registers (Glauber Costa)
+ - convert more monitor commands to qmp (Luiz Capitulino)
+ - vnc: fix capslock tracking logic. (Gerd Hoffmann)
+ - QemuOpts: allow larger option values. (Gerd Hoffmann)
+ - scsi: fix drive hotplug. (Gerd Hoffmann)
+ - pci: don't hw_error() when no slot is available. (Gerd Hoffmann)
+ - pci: don't abort() when trying to hotplug with acpi off. (Gerd Hoffmann)
+ - allow default devices to be implemented in config file (Gerd Hoffman)
+ - vc: colorize chardev title line with blue background. (Gerd Hoffmann)
+ - chardev: make chardevs specified in config file work. (Gerd Hoffmann)
+ - qdev: also match bus name for global properties (Gerd Hoffmann)
+ - qdev: add command line option to set global defaults for properties. (Gerd Hoffmann)
+ - kvm: x86: Save/restore exception_index (Jan Kiszka)
+ - qdev: Replace device names containing whitespace (Markus Armbruster)
+ - fix rtc-td-hack on host without high-res timers (Gleb Natapov)
+ - virtio: verify features on load (Michael S. Tsirkin)
+ - vmware_vga: add rom file so that it boots. (Dave Airlie)
+ - Do not abort on qemu_malloc(0) in production builds (Anthony Liguori)
+ - Fix ARM userspace strex implementation. (Paul Brook)
+ - qemu: delete rule target on error (Michael S. Tsirkin)
+ - QMP: add human-readable description to error response (Markus Armbruster)
+ - convert more monitor commands to QError (Markus Armbruster)
+ - monitor: Fix double-prompt after "change vnc passwd BLA" (Markus Armbruster)
+ - monitor: do_cont(): Don't ask for passwords (Luiz Capitulino)
+ - monitor: Introduce 'block_passwd' command (Luiz Capitulino)
+ - pci: interrupt disable bit support (Michael S. Tsirkin)
+ - pci: interrupt status bit implementation (Michael S. Tsirkin)
+ - pci: prepare irq code for interrupt state (Michael S. Tsirkin)
+ - msix: function mask support (Michael S. Tsirkin)
+ - msix: macro rename for function mask support (Michael S. Tsirkin)
+ - cpuid: Fix multicore setup on Intel (Andre Przywara)
+ - kvm: x86: Fix initial kvm_has_msr_star (Jan Kiszka)
+ - Update OpenBIOS images to r640 (Aurelien Jarno)
+
+version 0.10.2:
+
+ - fix savevm/loadvm (Anthony Liguori)
+ - live migration: fix dirty tracking windows (Glauber Costa)
+ - live migration: improve error propogation (Glauber Costa)
+ - qcow2: fix image creation for > ~2TB images (Chris Wright)
+ - hotplug: fix error handling for if= parameter (Eduardo Habkost)
+ - qcow2: fix data corruption (Nolan Leake)
+ - virtio: fix guest oops with 2.6.25 kernels (Rusty Russell)
+ - SH4: add support for -kernel (Takashi Yoshii, Aurelien Jarno)
+ - hotplug: fix closing of char devices (Jan Kiszka)
+ - hotplug: remove incorrect check for device name (Eduardo Habkost)
+ - enable -k on win32 (Herve Poussineau)
+ - configure: use LANG=C for grep (Andreas Faerber)
+ - fix VGA regression (malc)
+
+version 0.10.1:
+
+ - virtio-net: check right return size on sg list (Alex Williamson)
+ - Make qemu_announce_self handle holes (live migration after hotplug)
+ (Marcelo Tosatti)
+ - Revert r6804-r6808 (qcow2 allocation info). This series of changes added
+ a high cost to startup for large qcow2 images (Anthony Liguori)
+ - qemu-img: fix help message (Aurelien Jarno)
+ - Fix build for non-default installs of SDL (Anthony Liguori)
+ - Fix race condition in env->interrupt_request. When using TCG and a dynticks
+ host timer, this condition could cause TCG to get stuck in an infinite
+ loop (Aurelien Jarno)
+ - Fix reading encrypted hard disk passwords during early startup (Jan Kiszka)
+ - Fix encrypted disk reporting in 'info block' (Jan Kiszka)
+ - Fix console size with tiny displays (MusicPal) (Jan Kiszka)
+ - Improve error handling in bdrv_open2 (Jan Kiszka)
+ - Avoid leaking data in mux'ed character devices (Jan Kiszka)
+ - Fix initial character device reset (no banner in monitor) (Jan Kiszka)
+ - Fix cpuid KVM crash on i386 host (Lubomir Rintel)
+ - Fix SLES10sp2 installation by adding ISTAT1 register to LSI SCSI emulation
+ (Ryan Harper)
+
+version 0.10.0:
+
+ - TCG support (No longer requires GCC 3.x)
+ - Kernel Virtual Machine acceleration support
+ - BSD userspace emulation
+ - Bluetooth emulation and host passthrough support
+ - GDB XML register description support
+ - Intel e1000 emulation
+ - HPET emulation
+ - VirtIO paravirtual device support
+ - Marvell 88w8618 / MusicPal emulation
+ - Nokia N-series tablet emulation / OMAP2 processor emulation
+ - PCI hotplug support
+ - Live migration and new save/restore formats
+ - Curses display support
+ - qemu-nbd utility to mount supported block formats
+ - Altivec support in PPC emulation and new firmware (OpenBIOS)
+ - Multiple VNC clients are now supported
+ - TLS encryption is now supported in VNC
+ - MIPS Magnum R4000 machine (Hervé Poussineau)
+ - Braille support (Samuel Thibault)
+ - Freecom MusicPal system emulation (Jan Kiszka)
+ - OMAP242x and Nokia N800, N810 machines (Andrzej Zaborowski)
+ - EsounD audio driver (Frederick Reeve)
+ - Gravis Ultrasound GF1 sound card (Tibor "TS" Schütz)
+ - Many, many, bug fixes and new features
+
+version 0.9.1:
+
+ - TFTP booting from host directory (Anthony Liguori, Erwan Velu)
+ - Tap device emulation for Solaris (Sittichai Palanisong)
+ - Monitor multiplexing to several I/O channels (Jason Wessel)
+ - ds1225y nvram support (Herve Poussineau)
+ - CPU model selection support (J. Mayer, Paul Brook, Herve Poussineau)
+ - Several Sparc fixes (Aurelien Jarno, Blue Swirl, Robert Reif)
+ - MIPS 64-bit FPU support (Thiemo Seufer)
+ - Xscale PDA emulation (Andrzej Zaborowski)
+ - ColdFire system emulation (Paul Brook)
+ - Improved SH4 support (Magnus Damm)
+ - MIPS64 support (Aurelien Jarno, Thiemo Seufer)
+ - Preliminary Alpha guest support (J. Mayer)
+ - Read-only support for Parallels disk images (Alex Beregszaszi)
+ - SVM (x86 virtualization) support (Alexander Graf)
+ - CRIS emulation (Edgar E. Iglesias)
+ - SPARC32PLUS execution support (Blue Swirl)
+ - MIPS mipssim pseudo machine (Thiemo Seufer)
+ - Strace for Linux userland emulation (Stuart Anderson, Thayne Harbaugh)
+ - OMAP310 MPU emulation plus Palm T|E machine (Andrzej Zaborowski)
+ - ARM v6, v7, NEON SIMD and SMP emulation (Paul Brook/CodeSourcery)
+ - Gumstix boards: connex and verdex emulation (Thorsten Zitterell)
+ - Intel mainstone II board emulation (Armin Kuster)
+ - VMware SVGA II graphics card support (Andrzej Zaborowski)
+
+version 0.9.0:
+
+ - Support for relative paths in backing files for disk images
+ - Async file I/O API
+ - New qcow2 disk image format
+ - Support of multiple VM snapshots
+ - Linux: specific host CDROM and floppy support
+ - SMM support
+ - Moved PCI init, MP table init and ACPI table init to Bochs BIOS
+ - Support for MIPS32 Release 2 instruction set (Thiemo Seufer)
+ - MIPS Malta system emulation (Aurelien Jarno, Stefan Weil)
+ - Darwin userspace emulation (Pierre d'Herbemont)
+ - m68k user support (Paul Brook)
+ - several x86 and x86_64 emulation fixes
+ - Mouse relative offset VNC extension (Anthony Liguori)
+ - PXE boot support (Anthony Liguori)
+ - '-daemonize' option (Anthony Liguori)
+
+version 0.8.2:
+
+ - ACPI support
+ - PC VGA BIOS fixes
+ - switch to OpenBios for SPARC targets (Blue Swirl)
+ - VNC server fixes
+ - MIPS FPU support (Marius Groeger)
+ - Solaris/SPARC host support (Juergen Keil)
+ - PPC breakpoints and single stepping (Jason Wessel)
+ - USB updates (Paul Brook)
+ - UDP/TCP/telnet character devices (Jason Wessel)
+ - Windows sparse file support (Frediano Ziglio)
+ - RTL8139 NIC TCP segmentation offloading (Igor Kovalenko)
+ - PCNET NIC support (Antony T Curtis)
+ - Support for variable frequency host CPUs
+ - Workaround for win32 SMP hosts
+ - Support for AMD Flash memories (Jocelyn Mayer)
+ - Audio capture to WAV files support (malc)
+
+version 0.8.1:
+
+ - USB tablet support (Brad Campbell, Anthony Liguori)
+ - win32 host serial support (Kazu)
+ - PC speaker support (Joachim Henke)
+ - IDE LBA48 support (Jens Axboe)
+ - SSE3 support
+ - Solaris port (Juergen Keil)
+ - Preliminary SH4 target (Samuel Tardieu)
+ - VNC server (Anthony Liguori)
+ - slirp fixes (Ed Swierk et al.)
+ - USB fixes
+ - ARM Versatile Platform Baseboard emulation (Paul Brook)
+
+version 0.8.0:
+
+ - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s
+ cpu (Paul Brook)
+ - SMP support
+ - Mac OS X cocoa improvements (Mike Kronenberg)
+ - Mac OS X CoreAudio driver (Mike Kronenberg)
+ - DirectSound driver (malc)
+ - ALSA audio driver (malc)
+ - new audio options: '-soundhw' and '-audio-help' (malc)
+ - ES1370 PCI audio device (malc)
+ - Initial USB support
+ - Linux host serial port access
+ - Linux host low level parallel port access
+ - New network emulation code supporting VLANs.
+ - MIPS and MIPSel User Linux emulation
+ - MIPS fixes to boot Linux (Daniel Jacobowitz)
+ - NX bit support
+ - Initial SPARC SMP support (Blue Swirl)
+ - Major overhaul of the virtual FAT driver for read/write support
+ (Johannes Schindelin)
+
+version 0.7.2:
+
+ - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
+ - merge self modifying code handling in dirty ram page mecanism.
+ - MIPS fixes (Ralf Baechle)
+ - better user net performances
+
+version 0.7.1:
+
+ - read-only Virtual FAT support (Johannes Schindelin)
+ - Windows 2000 install disk full hack (original idea from Vladimir
+ N. Oleynik)
+ - VMDK disk image creation (Filip Navara)
+ - SPARC64 progress (Blue Swirl)
+ - initial MIPS support (Jocelyn mayer)
+ - MIPS improvements (Ralf Baechle)
+ - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne)
+ - IOAPIC support (Filip Navara)
+
+version 0.7.0:
+
+ - better BIOS translation and HDD geometry auto-detection
+ - user mode networking bug fix
+ - undocumented FPU ops support
+ - Cirrus VGA: support for 1280x1024x[8,15,16] modes
+ - 'pidfile' option
+ - .dmg disk image format support (Johannes Schindelin)
+ - keymaps support (initial patch by Johannes Schindelin)
+ - big endian ARM support (Lennert Buytenhek)
+ - added generic 64 bit target support
+ - x86_64 target support
+ - initial APIC support
+ - MMX/SSE/SSE2/PNI support
+ - PC parallel port support (Mark Jonckheere)
+ - initial SPARC64 support (Blue Swirl)
+ - SPARC target boots Linux (Blue Swirl)
+ - armv5te user mode support (Paul Brook)
+ - ARM VFP support (Paul Brook)
+ - ARM "Angel" semihosting syscalls (Paul Brook)
+ - user mode gdb stub support (Paul Brook)
+ - Samba 3 support
+ - initial Cocoa support (Pierre d'Herbemont)
+ - generic FPU emulation code
+ - Virtual PC read-only disk image support (Alex Beregszaszi)
+
+version 0.6.1:
+
+ - Mac OS X port (Pierre d'Herbemont)
+ - Virtual console support
+ - Better monitor line edition
+ - New block device layer
+ - New 'qcow' growable disk image support with AES encryption and
+ transparent decompression
+ - VMware 3 and 4 read-only disk image support (untested)
+ - Support for up to 4 serial ports
+ - TFTP server support (Magnus Damm)
+ - Port redirection support in user mode networking
+ - Support for not executable data sections
+ - Compressed loop disk image support (Johannes Schindelin)
+ - Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve
+ Wormley)
+ - Fixed Fedora Core 2 problems (now you can run qemu without any
+ LD_ASSUME_KERNEL tricks on FC2)
+ - DHCP fix for Windows (accept DHCPREQUEST alone)
+ - SPARC system emulation (Blue Swirl)
+ - Automatic Samba configuration for host file access from Windows.
+ - '-loadvm' and '-full-screen' options
+ - ne2000 savevm support (Johannes Schindelin)
+ - Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to
+ the virtual consoles.
+ - BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert)
+ - Floppy fixes for NT4 and NT5 (Mike Nordell)
+ - NT4 IDE fixes (Ben Pfaf, Mike Nordell)
+ - SDL Audio support and SB16 fixes (malc)
+ - ENTER instruction bug fix (initial patch by Stefan Kisdaroczi)
+ - VGA font change fix
+ - VGA read-only CRTC register fix
+
+version 0.6.0:
+
+ - minimalist FPU exception support (NetBSD FPU probe fix)
+ - cr0.ET fix (Win95 boot)
+ - *BSD port (Markus Niemisto)
+ - I/O access fix (signaled by Mark Jonckheere)
+ - IDE drives serial number fix (Mike Nordell)
+ - int13 CDROM BIOS fix (aka Solaris x86 install CD fix)
+ - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix)
+ - BSR/BSF "undefined behaviour" fix
+ - vmdk2raw: convert VMware disk images to raw images
+ - PCI support
+ - NE2K PCI support
+ - dummy VGA PCI support
+ - VGA font selection fix (Daniel Serpell)
+ - PIC reset fix (Hidemi KAWAI)
+ - PIC spurious irq support (aka Solaris install bug)
+ - added '-localtime' option
+ - Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu))
+ - APM and system shutdown support
+ - Fixed system reset
+ - Support for other PC BIOSes
+ - Initial PowerMac hardware emulation
+ - PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer)
+ - initial IDE BMDMA support (needed for Darwin x86)
+ - Set the default memory size for PC emulation to 128 MB
+
+version 0.5.5:
+
+ - SDL full screen support (initial patch by malc)
+ - VGA support on PowerPC PREP
+ - VBE fixes (Matthew Mastracci)
+ - PIT fixes (aka Win98 hardware probe and "VGA slowness" bug)
+ - IDE master only fixes (aka Win98 CD-ROM probe bug)
+ - ARM load/store half word fix (Ulrich Hecht)
+ - FDC fixes for Win98
+
+version 0.5.4:
+
+ - qemu-fast fixes
+ - BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell)
+ - keyboard/mouse fix (Mike Nordell)
+ - IDE fixes (Linux did not recognized slave drivers)
+ - VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell)
+ - QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer)
+ - User mode network stack
+ - imul imm8 fix + 0x82 opcode support (Hidemi KAWAI)
+ - precise self modifying code (aka BeOS install bug)
+
+version 0.5.3:
+
+ - added Bochs VESA VBE support
+ - VGA memory map mode 3 access fix (OS/2 install fix)
+ - IDE fixes (Jens Axboe)
+ - CPU interrupt fixes
+ - fixed various TLB invalidation cases (NT install)
+ - fixed cr0.WP semantics (XP install)
+ - direct chaining support for SPARC and PowerPC (faster)
+ - ARM NWFPE support (initial patch by Ulrich Hecht)
+ - added specific x86 to x86 translator (close to native performance
+ in qemu-i386 and qemu-fast)
+ - shm syscalls support (Paul McKerras)
+ - added accurate CR0.MP/ME/TS emulation
+ - fixed DMA memory write access (Win95 boot floppy fix)
+ - graphical x86 linux loader
+ - command line monitor
+ - generic removable device support
+ - support of CD-ROM change
+ - multiple network interface support
+ - initial x86-64 host support (Gwenole Beauchesne)
+ - lret to outer priviledge fix (OS/2 install fix)
+ - task switch fixes (SkyOS boot)
+ - VM save/restore commands
+ - new timer API
+ - more precise RTC emulation (periodic timers + time updates)
+ - Win32 port (initial patch by Kazu)
+
+version 0.5.2:
+
+ - improved soft MMU speed (assembly functions and specializing)
+ - improved multitasking speed by avoiding flushing TBs when
+ switching tasks
+ - improved qemu-fast speed
+ - improved self modifying code handling (big performance gain in
+ softmmu mode).
+ - fixed IO checking
+ - fixed CD-ROM detection (win98 install CD)
+ - fixed addseg real mode bug (GRUB boot fix)
+ - added ROM memory support (win98 boot)
+ - fixed 'call Ev' in case of paging exception
+ - updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically
+ when launching executables for the supported target CPUs.
+ - PowerPC system emulation update (Jocelyn Mayer)
+ - PC floppy emulation and DMA fixes (Jocelyn Mayer)
+ - polled mode for PIC (Jocelyn Mayer)
+ - fixed PTE dirty bit handling
+ - fixed xadd same reg bug
+ - fixed cmpxchg exception safeness
+ - access to virtual memory in gdb stub
+ - task gate and NT flag fixes
+ - eflags optimisation fix for string operations
+
+version 0.5.1:
+
+ - float access fixes when using soft mmu
+ - PC emulation support on PowerPC
+ - A20 support
+ - IDE CD-ROM emulation
+ - ARM fixes (Ulrich Hecht)
+ - SB16 emulation (malc)
+ - IRET and INT fixes in VM86 mode with IOPL=3
+ - Port I/Os use TSS io map
+ - Full task switching/task gate support
+ - added verr, verw, arpl, fcmovxx
+ - PowerPC target support (Jocelyn Mayer)
+ - Major SPARC target fixes (dynamically linked programs begin to work)
+
+version 0.5.0:
+
+ - full hardware level VGA emulation
+ - graphical display with SDL
+ - added PS/2 mouse and keyboard emulation
+ - popw (%esp) fix
+ - mov to/from segment data width fix
+ - added real mode support
+ - added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu
+ - m68k host port (Richard Zidlicky)
+ - partial soft MMU support for memory mapped I/Os
+ - multi-target build
+ - fixed: no error code in hardware interrupts
+ - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
+ - correct single stepping thru string operations
+ - preliminary SPARC target support (Thomas M. Ogrisegg)
+ - tun-fd option (Rusty Russell)
+ - automatic IDE geometry detection
+ - renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}.
+ - added man page
+ - added full soft mmu mode to launch unpatched OSes.
+
+version 0.4.3:
+
+ - x86 exception fix in case of nop instruction.
+ - gcc 3.2.2 bug workaround (RedHat 9 fix)
+ - sparc and Alpha host fixes
+ - many ARM target fixes: 'ls' and 'bash' can be launched.
+
+version 0.4.2:
+
+ - many exception handling fixes (can compile a Linux kernel inside vl)
+ - IDE emulation support
+ - initial GDB stub support
+ - deferred update support for disk images (Rusty Russell)
+ - accept User Mode Linux Copy On Write disk images
+ - SMP kernels can at least be booted
+
+version 0.4.1:
+
+ - more accurate timer support in vl.
+ - more reliable NE2000 probe in vl.
+ - added 2.5.66 kernel in vl-test.
+ - added VLTMPDIR environment variable in vl.
+
+version 0.4:
+
+ - initial support for ring 0 x86 processor emulation
+ - fixed signal handling for correct dosemu DPMI emulation
+ - fast x86 MMU emulation with mmap()
+ - fixed popl (%esp) case
+ - Linux kernel can be executed by QEMU with the 'vl' command.
+
+version 0.3:
+
+ - initial support for ARM emulation
+ - added fnsave, frstor, fnstenv, fldenv FPU instructions
+ - added FPU register save in signal emulation
+ - initial ARM port
+ - Sparc and Alpha ports work on the regression test
+ - generic ioctl number conversion
+ - fixed ioctl type conversion
+
+version 0.2:
+
+ - PowerPC disassembly and ELF symbols output (Rusty Russell)
+ - flock support (Rusty Russell)
+ - ugetrlimit support (Rusty Russell)
+ - fstat64 fix (Rusty Russell)
+ - initial Alpha port (Falk Hueffner)
+ - initial IA64 port (Matt Wilson)
+ - initial Sparc and Sparc64 port (David S. Miller)
+ - added HLT instruction
+ - LRET instruction fix.
+ - added GPF generation for I/Os.
+ - added INT3 and TF flag support.
+ - SHL instruction C flag fix.
+ - mmap emulation for host page size > 4KB
+ - self-modifying code support
+ - better VM86 support (dosemu works on non trivial programs)
+ - precise exception support (EIP is computed correctly in most cases)
+ - more precise LDT/GDT/IDT emulation
+ - faster segment load in vm86 mode
+ - direct chaining of basic blocks (faster emulation)
+
+version 0.1.6:
+
+ - automatic library search system. QEMU can now work with unpatched
+ ELF dynamic loader and libc (Rusty Russell).
+ - ISO C warning fixes (Alistair Strachan)
+ - first self-virtualizable version (works only as long as the
+ translation cache is not flushed)
+ - RH9 fixes
+
+version 0.1.5:
+
+ - ppc64 support + personality() patch (Rusty Russell)
+ - first Alpha CPU patches (Falk Hueffner)
+ - removed bfd.h dependancy
+ - fixed shrd, shld, idivl and divl on PowerPC.
+ - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).
+
+version 0.1.4:
+
+ - more accurate VM86 emulation (can launch small DOS 16 bit
+ executables in wine).
+ - fixed push/pop fs/gs
+ - added iret instruction.
+ - added times() syscall and SIOCATMARK ioctl.
+
+version 0.1.3:
+
+ - S390 support (Ulrich Weigand)
+ - glibc 2.3.x compile fix (Ulrich Weigand)
+ - socketcall endian fix (Ulrich Weigand)
+ - struct sockaddr endian fix (Ulrich Weigand)
+ - sendmsg/recvmsg endian fix (Ulrich Weigand)
+ - execve endian fix (Ulrich Weigand)
+ - fdset endian fix (Ulrich Weigand)
+ - partial setsockopt syscall support (Ulrich Weigand)
+ - more accurate pushf/popf emulation
+ - first partial vm86() syscall support (can be used with runcom example).
+ - added bound, cmpxchg8b, cpuid instructions
+ - added 16 bit addressing support/override for string operations
+ - poll() fix
+
+version 0.1.2:
+
+ - compile fixes
+ - xlat instruction
+ - xchg instruction memory lock
+ - added simple vm86 example (not working with QEMU yet). The 54 byte
+ DOS executable 'pi_10.com' program was released by Bertram
+ Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
+
+version 0.1.1:
+
+ - glibc 2.2 compilation fixes
+ - added -s and -L options
+ - binary distribution of x86 glibc and wine
+ - big endian fixes in ELF loader and getdents.
+
+version 0.1:
+
+ - initial public release.
diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000..6ba9d7e
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,125 @@
+1. Preprocessor
+
+For variadic macros, stick with this C99-like syntax:
+
+#define DPRINTF(fmt, ...) \
+ do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
+
+2. C types
+
+It should be common sense to use the right type, but we have collected
+a few useful guidelines here.
+
+2.1. Scalars
+
+If you're using "int" or "long", odds are good that there's a better type.
+If a variable is counting something, it should be declared with an
+unsigned type.
+
+If it's host memory-size related, size_t should be a good choice (use
+ssize_t only if required). Guest RAM memory offsets must use ram_addr_t,
+but only for RAM, it may not cover whole guest address space.
+
+If it's file-size related, use off_t.
+If it's file-offset related (i.e., signed), use off_t.
+If it's just counting small numbers use "unsigned int";
+(on all but oddball embedded systems, you can assume that that
+type is at least four bytes wide).
+
+In the event that you require a specific width, use a standard type
+like int32_t, uint32_t, uint64_t, etc. The specific types are
+mandatory for VMState fields.
+
+Don't use Linux kernel internal types like u32, __u32 or __le32.
+
+Use target_phys_addr_t for guest physical addresses except pcibus_t
+for PCI addresses. In addition, ram_addr_t is a QEMU internal address
+space that maps guest RAM physical addresses into an intermediate
+address space that can map to host virtual address spaces. Generally
+speaking, the size of guest memory can always fit into ram_addr_t but
+it would not be correct to store an actual guest physical address in a
+ram_addr_t.
+
+Use target_ulong (or abi_ulong) for CPU virtual addresses, however
+devices should not need to use target_ulong.
+
+Of course, take all of the above with a grain of salt. If you're about
+to use some system interface that requires a type like size_t, pid_t or
+off_t, use matching types for any corresponding variables.
+
+Also, if you try to use e.g., "unsigned int" as a type, and that
+conflicts with the signedness of a related variable, sometimes
+it's best just to use the *wrong* type, if "pulling the thread"
+and fixing all related variables would be too invasive.
+
+Finally, while using descriptive types is important, be careful not to
+go overboard. If whatever you're doing causes warnings, or requires
+casts, then reconsider or ask for help.
+
+2.2. Pointers
+
+Ensure that all of your pointers are "const-correct".
+Unless a pointer is used to modify the pointed-to storage,
+give it the "const" attribute. That way, the reader knows
+up-front that this is a read-only pointer. Perhaps more
+importantly, if we're diligent about this, when you see a non-const
+pointer, you're guaranteed that it is used to modify the storage
+it points to, or it is aliased to another pointer that is.
+
+2.3. Typedefs
+Typedefs are used to eliminate the redundant 'struct' keyword.
+
+2.4. Reserved namespaces in C and POSIX
+Underscore capital, double underscore, and underscore 't' suffixes should be
+avoided.
+
+3. Low level memory management
+
+Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign
+APIs is not allowed in the QEMU codebase. Instead of these routines,
+use the replacement qemu_malloc/qemu_mallocz/qemu_realloc/qemu_free or
+qemu_vmalloc/qemu_memalign/qemu_vfree APIs.
+
+Please note that NULL check for the qemu_malloc result is redundant and
+that qemu_malloc() call with zero size is not allowed.
+
+Memory allocated by qemu_vmalloc or qemu_memalign must be freed with
+qemu_vfree, since breaking this will cause problems on Win32 and user
+emulators.
+
+4. String manipulation
+
+Do not use the strncpy function. According to the man page, it does
+*not* guarantee a NULL-terminated buffer, which makes it extremely dangerous
+to use. Instead, use functionally equivalent function:
+void pstrcpy(char *buf, int buf_size, const char *str)
+
+Don't use strcat because it can't check for buffer overflows, but:
+char *pstrcat(char *buf, int buf_size, const char *s)
+
+The same limitation exists with sprintf and vsprintf, so use snprintf and
+vsnprintf.
+
+QEMU provides other useful string functions:
+int strstart(const char *str, const char *val, const char **ptr)
+int stristart(const char *str, const char *val, const char **ptr)
+int qemu_strnlen(const char *s, int max_len)
+
+There are also replacement character processing macros for isxyz and toxyz,
+so instead of e.g. isalnum you should use qemu_isalnum.
+
+Because of the memory management rules, you must use qemu_strdup/qemu_strndup
+instead of plain strdup/strndup.
+
+5. Printf-style functions
+
+Whenever you add a new printf-style function, i.e., one with a format
+string argument and following "..." in its prototype, be sure to use
+gcc's printf attribute directive in the prototype.
+
+This makes it so gcc's -Wformat and -Wformat-security options can do
+their jobs and cross-check format strings with the number and types
+of arguments.
+
+Currently many functions in QEMU are not following this rule but
+patches to add the attribute would be very much appreciated.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cbd92c0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,18 @@
+The following points clarify the QEMU license:
+
+1) QEMU as a whole is released under the GNU General Public License
+
+2) Parts of QEMU have specific licenses which are compatible with the
+GNU General Public License. Hence each source file contains its own
+licensing information.
+
+In particular, the QEMU virtual CPU core library (libqemu.a) is
+released under the GNU Lesser General Public License. Many hardware
+device emulation sources are released under the BSD license.
+
+3) The Tiny Code Generator (TCG) is released under the BSD license
+ (see license headers in files).
+
+4) QEMU is a trademark of Fabrice Bellard.
+
+Fabrice Bellard.
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644
index 0000000..ab48380
--- /dev/null
+++ b/MAINTAINERS
@@ -0,0 +1,489 @@
+QEMU Maintainers
+================
+
+The intention of this file is not to establish who owns what portions of the
+code base, but to provide a set of names that developers can consult when they
+have a question about a particular subset and also to provide a set of names
+to be CC'd when submitting a patch to obtain appropriate review.
+
+In general, if you have a question about inclusion of a patch, you should
+consult qemu-devel and not any specific individual privately.
+
+Descriptions of section entries:
+
+ M: Mail patches to: FullName <address@domain>
+ L: Mailing list that is relevant to this area
+ W: Web-page with status/info
+ Q: Patchwork web based patch tracking system site
+ T: SCM tree type and location. Type is one of: git, hg, quilt, stgit.
+ S: Status, one of the following:
+ Supported: Someone is actually paid to look after this.
+ Maintained: Someone actually looks after it.
+ Odd Fixes: It has a maintainer but they don't have time to do
+ much other than throw the odd patch in. See below..
+ Orphan: No current maintainer [but maybe you could take the
+ role as you write your new code].
+ Obsolete: Old code. Something tagged obsolete generally means
+ it has been replaced by a better system and you
+ should be using that.
+ F: Files and directories with wildcard patterns.
+ A trailing slash includes all files and subdirectory files.
+ F: drivers/net/ all files in and below drivers/net
+ F: drivers/net/* all files in drivers/net, but not below
+ F: */net/* all files in "any top level directory"/net
+ One pattern per line. Multiple F: lines acceptable.
+ X: Files and directories that are NOT maintained, same rules as F:
+ Files exclusions are tested before file matches.
+ Can be useful for excluding a specific subdirectory, for instance:
+ F: net/
+ X: net/ipv6/
+ matches all files in and below net excluding net/ipv6/
+ K: Keyword perl extended regex pattern to match content in a
+ patch or file. For instance:
+ K: of_get_profile
+ matches patches or files that contain "of_get_profile"
+ K: \b(printk|pr_(info|err))\b
+ matches patches or files that contain one or more of the words
+ printk, pr_info or pr_err
+ One regex pattern per line. Multiple K: lines acceptable.
+
+
+General Project Administration
+------------------------------
+M: Anthony Liguori <aliguori@us.ibm.com>
+M: Paul Brook <paul@codesourcery.com>
+
+Guest CPU cores (TCG):
+----------------------
+Alpha
+M: qemu-devel@nongnu.org
+S: Orphan
+F: target-alpha/
+
+ARM
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: target-arm/
+
+CRIS
+M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
+S: Maintained
+F: target-cris/
+
+M68K
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: target-m68k/
+
+MicroBlaze
+M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
+S: Maintained
+F: target-microblaze/
+
+MIPS
+M: Aurelien Jarno <aurelien@aurel32.net>
+S: Maintained
+F: target-mips/
+
+PowerPC
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: target-ppc/
+
+S390
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: target-s390x/
+
+SH4
+M: Aurelien Jarno <aurelien@aurel32.net>
+S: Maintained
+F: target-sh4/
+
+SPARC
+M: Blue Swirl <blauwirbel@gmail.com>
+S: Maintained
+F: target-sparc/
+
+X86
+M: qemu-devel@nongnu.org
+S: Odd Fixes
+F: target-i386/
+
+Guest CPU Cores (KVM):
+----------------------
+
+Overall
+M: Avi Kivity <avi@redhat.com>
+M: Marcelo Tosatti <mtosatti@redhat.com>
+L: kvm@vger.kernel.org
+S: Supported
+F: kvm-*
+F: */kvm.*
+
+PPC
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: target-ppc/kvm.c
+
+S390
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: target-s390x/kvm.c
+
+X86
+M: Avi Kivity <avi@redhat.com>
+M: Marcelo Tosatti <mtosatti@redhat.com>
+L: kvm@vger.kernel.org
+S: Supported
+F: target-i386/kvm.c
+
+ARM Machines
+------------
+Gumstix
+M: qemu-devel@nongnu.org
+S: Orphan
+F: hw/gumstix.c
+
+Integrator CP
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/integratorcp.c
+
+Mainstone
+M: qemu-devel@nongnu.org
+S: Orphan
+F: hw/mainstone.c
+
+Musicpal
+M: Jan Kiszka <jan.kiszka@web.de>
+S: Maintained
+F: hw/musicpal.c
+
+nSeries
+M: Andrzej Zaborowski <balrogg@gmail.com>
+S: Maintained
+F: hw/nseries.c
+
+Palm
+M: Andrzej Zaborowski <balrogg@gmail.com>
+S: Maintained
+F: hw/palm.c
+
+Real View
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/realview*
+
+Spitz
+M: Andrzej Zaborowski <balrogg@gmail.com>
+S: Maintained
+F: hw/spitz.c
+
+Stellaris
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/stellaris.c
+
+Versatile PB
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/versatilepb.c
+
+CRIS Machines
+-------------
+Axis Dev88
+M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
+S: Maintained
+F: hw/axis_dev88.c
+
+etraxfs
+M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
+S: Maintained
+F: hw/etraxfs.c
+
+M68K Machines
+-------------
+an5206
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/an5206.c
+
+dummy_m68k
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/dummy_m68k.c
+
+mcf5208
+M: Paul Brook <paul@codesourcery.com>
+S: Maintained
+F: hw/mcf5208.c
+
+MicroBlaze Machines
+-------------------
+petalogix_s3adsp1800
+M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
+S: Maintained
+F: hw/petalogix_s3adsp1800.c
+
+MIPS Machines
+-------------
+Jazz
+M: Hervé Poussineau <hpoussin@reactos.org>
+S: Maintained
+F: hw/mips_jazz.c
+
+Malta
+M: Aurelien Jarno <aurelien@aurel32.net>
+S: Maintained
+F: hw/mips_malta.c
+
+Mipssim
+M: qemu-devel@nongnu.org
+S: Orphan
+F: hw/mips_mipssim.c
+
+R4000
+M: Aurelien Jarno <aurelien@aurel32.net>
+S: Maintained
+F: hw/mips_r4k.c
+
+PowerPC Machines
+----------------
+405
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: hw/ppc405_boards.c
+
+New World
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: hw/ppc_newworld.c
+
+Old World
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: hw/ppc_oldworld.c
+
+Prep
+M: qemu-devel@nongnu.org
+S: Orphan
+F: hw/ppc_prep.c
+
+SH4 Machines
+------------
+R2D
+M: Magnus Damm <magnus.damm@gmail.com>
+S: Maintained
+F: hw/r2d.c
+
+Shix
+M: Magnus Damm <magnus.damm@gmail.com>
+S: Orphan
+F: hw/shix.c
+
+SPARC Machines
+--------------
+Sun4m
+M: Blue Swirl <blauwirbel@gmail.com>
+S: Maintained
+F: hw/sun4m.c
+
+Sun4u
+M: Blue Swirl <blauwirbel@gmail.com>
+S: Maintained
+F: hw/sun4u.c
+
+S390 Machines
+-------------
+S390 Virtio
+M: Alexander Graf <agraf@suse.de>
+S: Maintained
+F: hw/s390-*.c
+
+X86 Machines
+------------
+PC
+M: Anthony Liguori <aliguori@us.ibm.com>
+S: Supported
+F: hw/pc.[ch] hw/pc_piix.c
+
+Devices
+-------
+IDE
+M: Kevin Wolf <kwolf@redhat.com>
+S: Odd Fixes
+F: hw/ide/
+
+PCI
+M: Michael S. Tsirkin <mst@redhat.com>
+S: Supported
+F: hw/pci*
+F: hw/piix*
+
+SCSI
+M: Paul Brook <paul@codesourcery.com>
+M: Kevin Wolf <kwolf@redhat.com>
+S: Odd Fixes
+F: hw/lsi53c895a.c
+F: hw/scsi*
+
+USB
+M: Gerd Hoffmann <kraxel@redhat.com>
+S: Maintained
+F: hw/usb*
+
+vhost
+M: Michael S. Tsirkin <mst@redhat.com>
+S: Supported
+F: hw/vhost*
+
+virtio
+M: Anthony Liguori <aliguori@us.ibm.com>
+S: Supported
+F: hw/virtio*
+
+virtio-9p
+M: Venkateswararao Jujjuri (JV) <jvrao@linux.vnet.ibm.com>
+S: Supported
+F: hw/virtio-9p*
+
+virtio-blk
+M: Kevin Wolf <kwolf@redhat.com>
+S: Supported
+F: hw/virtio-blk*
+
+virtio-serial
+M: Amit Shah <amit.shah@redhat.com>
+S: Supported
+F: hw/virtio-serial*
+F: hw/virtio-console*
+
+Subsystems
+----------
+Audio
+M: Vassili Karpov (malc) <av1474@comtv.ru>
+S: Maintained
+F: audio/
+
+Block
+M: Kevin Wolf <kwolf@redhat.com>
+S: Supported
+F: block*
+F: block/
+
+Character Devices
+M: Anthony Liguori <aliguori@us.ibm.com>
+S: Maintained
+F: qemu-char.c
+
+GDB stub
+M: qemu-devel@nongnu.org
+S: Odd Fixes
+F: gdbstub*
+F: gdb-xml/
+
+SPICE
+M: Gerd Hoffmann <kraxel@redhat.com>
+S: Supported
+F: ui/qemu-spice.h
+F: ui/spice-*.c
+F: audio/spiceaudio.c
+F: hw/qxl*
+
+Graphics
+M: Anthony Liguori <aliguori@us.ibm.com>
+S: Maintained
+F: ui/
+
+Main loop
+M: Anthony Liguori <aliguori@us.ibm.com>
+S: Supported
+F: vl.c
+
+Monitor (QMP/HMP)
+M: Luiz Capitulino <lcapitulino@redhat.com>
+M: Markus Armbruster <armbru@redhat.com>
+S: Supported
+F: monitor.c
+
+Network device layer
+M: Anthony Liguori <aliguori@us.ibm.com>
+M: Mark McLoughlin <markmc@redhat.com>
+S: Maintained
+F: net/
+
+SLIRP
+M: qemu-devel@nongnu.org
+S: Orphan
+F: slirp/
+
+Usermode Emulation
+------------------
+BSD user
+M: Blue Swirl <blauwirbel@gmail.com>
+S: Maintained
+F: bsd-user/
+
+Darwin user
+M: qemu-devel@nongnu.org
+S: Orphan
+F: darwin-user/
+
+Linux user
+M: Riku Voipio <riku.voipio@iki.fi>
+S: Maintained
+F: linux-user/
+
+Tiny Code Generator (TCG)
+-------------------------
+Common code
+M: qemu-devel@nongnu.org
+S: Maintained
+F: tcg/
+
+ARM target
+M: Andrzej Zaborowski <balrogg@gmail.com>
+S: Maintained
+F: tcg/arm/
+
+HPPA target
+M: Richard Henderson <rth@twiddle.net>
+S: Maintained
+F: tcg/hppa/
+
+i386 target
+M: qemu-devel@nongnu.org
+S: Maintained
+F: tcg/i386/
+
+IA64 target
+M: Aurelien Jarno <aurelien@aurel32.net>
+S: Maintained
+F: tcg/ia64/
+
+MIPS target
+M: Aurelien Jarno <aurelien@aurel32.ne>
+S: Maintained
+F: tcg/mips/
+
+PPC
+M: Vassili Karpov (malc) <av1474@comtv.ru>
+S: Maintained
+F: tcg/ppc/
+
+PPC64 target
+M: Vassili Karpov (malc) <av1474@comtv.ru>
+S: Maintained
+F: tcg/ppc64/
+
+S390 target
+M: Alexander Graf <agraf@suse.de>
+M: Richard Henderson <rth@twiddle.net>
+S: Maintained
+F: tcg/s390/
+
+SPARC target
+M: Blue Swirl <blauwirbel@gmail.com>
+S: Maintained
+F: tcg/sparc/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c70070d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,377 @@
+# Makefile for QEMU.
+
+GENERATED_HEADERS = config-host.h trace.h qemu-options.def
+ifeq ($(TRACE_BACKEND),dtrace)
+GENERATED_HEADERS += trace-dtrace.h
+endif
+
+ifneq ($(wildcard config-host.mak),)
+# Put the all: rule here so that config-host.mak can contain dependencies.
+all: build-all
+include config-host.mak
+include $(SRC_PATH)/rules.mak
+config-host.mak: $(SRC_PATH)/configure
+ @echo $@ is out-of-date, running configure
+ @sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh
+else
+config-host.mak:
+ @echo "Please call configure before running make!"
+ @exit 1
+endif
+
+# Don't try to regenerate Makefile or configure
+# We don't generate any of them
+Makefile: ;
+configure: ;
+
+.PHONY: all clean cscope distclean dvi html info install install-doc \
+ pdf recurse-all speed tar tarbin test build-all
+
+$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
+
+LIBS+=-lz $(LIBS_TOOLS)
+
+ifdef BUILD_DOCS
+DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt
+else
+DOCS=
+endif
+
+SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory)
+SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
+SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS))
+
+config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
+ $(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@")
+
+-include $(SUBDIR_DEVICES_MAK_DEP)
+
+%/config-devices.mak: default-configs/%.mak
+ $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, " GEN $@")
+ @if test -f $@; then \
+ if cmp -s $@.old $@; then \
+ mv $@.tmp $@; \
+ cp -p $@ $@.old; \
+ else \
+ if test -f $@.old; then \
+ echo "WARNING: $@ (user modified) out of date.";\
+ else \
+ echo "WARNING: $@ out of date.";\
+ fi; \
+ echo "Run \"make defconfig\" to regenerate."; \
+ rm $@.tmp; \
+ fi; \
+ else \
+ mv $@.tmp $@; \
+ cp -p $@ $@.old; \
+ fi
+
+defconfig:
+ rm -f config-all-devices.mak $(SUBDIR_DEVICES_MAK)
+
+-include config-all-devices.mak
+
+build-all: $(DOCS) $(TOOLS) recurse-all
+
+config-host.h: config-host.h-timestamp
+config-host.h-timestamp: config-host.mak
+qemu-options.def: $(SRC_PATH)/qemu-options.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
+
+SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
+
+subdir-%: $(GENERATED_HEADERS)
+ $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,)
+
+ifneq ($(wildcard config-host.mak),)
+include $(SRC_PATH)/Makefile.objs
+endif
+
+$(common-obj-y): $(GENERATED_HEADERS)
+$(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis
+
+$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser
+
+ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
+romsubdir-%:
+ $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pc-bios/$* V="$(V)" TARGET_DIR="$*/",)
+
+ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS))
+
+recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
+
+audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
+
+QEMU_CFLAGS+=$(CURL_CFLAGS)
+
+ui/cocoa.o: ui/cocoa.m
+
+ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
+
+ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
+
+bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
+
+ifeq ($(TRACE_BACKEND),dtrace)
+trace.h: trace.h-timestamp trace-dtrace.h
+else
+trace.h: trace.h-timestamp
+endif
+trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak
+ $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h")
+ @cmp -s $@ trace.h || cp $@ trace.h
+
+trace.c: trace.c-timestamp
+trace.c-timestamp: $(SRC_PATH)/trace-events config-host.mak
+ $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c")
+ @cmp -s $@ trace.c || cp $@ trace.c
+
+trace.o: trace.c $(GENERATED_HEADERS)
+
+trace-dtrace.h: trace-dtrace.dtrace
+ $(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.h")
+
+# Normal practice is to name DTrace probe file with a '.d' extension
+# but that gets picked up by QEMU's Makefile as an external dependancy
+# rule file. So we use '.dtrace' instead
+trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp
+trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events config-host.mak
+ $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace")
+ @cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace
+
+trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
+ $(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
+
+simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
+
+version.o: $(SRC_PATH)/version.rc config-host.mak
+ $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
+
+version-obj-$(CONFIG_WIN32) += version.o
+######################################################################
+
+qemu-img.o: qemu-img-cmds.h
+qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS)
+
+qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
+
+qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
+
+qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
+
+qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
+
+check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS)
+
+CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y)
+
+check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS)
+check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS)
+check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS)
+check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
+check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
+check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS)
+
+clean:
+# avoid old build problems by removing potentially incorrect old files
+ rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
+ rm -f qemu-options.def
+ rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
+ rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
+ rm -f qemu-img-cmds.h
+ rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
+ rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
+ rm -f trace-dtrace.h trace-dtrace.h-timestamp
+ $(MAKE) -C tests clean
+ for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
+ rm -f $$d/qemu-options.def; \
+ done
+
+distclean: clean
+ rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
+ rm -f config-all-devices.mak
+ rm -f roms/seabios/config.mak roms/vgabios/config.mak
+ rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
+ rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
+ for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+ rm -rf $$d || exit 1 ; \
+ done
+
+KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
+ar de en-us fi fr-be hr it lv nl pl ru th \
+common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
+
+ifdef INSTALL_BLOBS
+BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \
+vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
+ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
+gpxe-eepro100-80861209.rom \
+pxe-e1000.bin \
+pxe-ne2k_pci.bin pxe-pcnet.bin \
+pxe-rtl8139.bin pxe-virtio.bin \
+bamboo.dtb petalogix-s3adsp1800.dtb \
+multiboot.bin linuxboot.bin \
+s390-zipl.rom
+BLOBS += extboot.bin
+BLOBS += vapic.bin
+else
+BLOBS=
+endif
+
+install-doc: $(DOCS)
+ $(INSTALL_DIR) "$(DESTDIR)$(docdir)"
+ $(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
+ifdef CONFIG_POSIX
+ $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
+ $(INSTALL_DATA) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
+ $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
+ $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
+endif
+
+install-sysconfig:
+ $(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu"
+ $(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu"
+
+install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig
+ $(INSTALL_DIR) "$(DESTDIR)$(bindir)"
+ifneq ($(TOOLS),)
+ $(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
+endif
+ifneq ($(BLOBS),)
+ $(INSTALL_DIR) "$(DESTDIR)$(datadir)"
+ set -e; for x in $(BLOBS); do \
+ if [ -f $(SRC_PATH)/pc-bios/$$x ];then \
+ $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
+ fi \
+ ; if [ -f pc-bios/optionrom/$$x ];then \
+ $(INSTALL_DATA) pc-bios/optionrom/$$x "$(DESTDIR)$(datadir)"; \
+ fi \
+ done
+endif
+ $(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps"
+ set -e; for x in $(KEYMAPS); do \
+ $(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
+ done
+ for d in $(TARGET_DIRS); do \
+ $(MAKE) -C $$d $@ || exit 1 ; \
+ done
+
+# various test targets
+test speed: all
+ $(MAKE) -C tests $@
+
+.PHONY: TAGS
+TAGS:
+ find "$(SRC_PATH)" -name '*.[hc]' -print0 | xargs -0 etags
+
+cscope:
+ rm -f ./cscope.*
+ find . -name "*.[ch]" -print | sed 's,^\./,,' > ./cscope.files
+ cscope -b
+
+# documentation
+MAKEINFO=makeinfo
+MAKEINFOFLAGS=--no-headers --no-split --number-sections
+TEXIFLAG=$(if $(V),,--quiet)
+%.dvi: %.texi
+ $(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<," GEN $@")
+
+%.html: %.texi
+ $(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
+ " GEN $@")
+
+%.info: %.texi
+ $(call quiet-command,$(MAKEINFO) $< -o $@," GEN $@")
+
+%.pdf: %.texi
+ $(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<," GEN $@")
+
+qemu-options.texi: $(SRC_PATH)/qemu-options.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
+
+qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
+
+QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@")
+
+qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
+
+qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
+ $(call quiet-command, \
+ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
+ pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \
+ " GEN $@")
+
+qemu-img.1: qemu-img.texi qemu-img-cmds.texi
+ $(call quiet-command, \
+ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \
+ pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \
+ " GEN $@")
+
+qemu-nbd.8: qemu-nbd.texi
+ $(call quiet-command, \
+ perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \
+ pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
+ " GEN $@")
+
+dvi: qemu-doc.dvi qemu-tech.dvi
+html: qemu-doc.html qemu-tech.html
+info: qemu-doc.info qemu-tech.info
+pdf: qemu-doc.pdf qemu-tech.pdf
+
+qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
+ qemu-img.texi qemu-nbd.texi qemu-options.texi \
+ qemu-monitor.texi qemu-img-cmds.texi
+
+VERSION ?= $(shell cat VERSION)
+FILE = qemu-$(VERSION)
+
+# tar release (use 'make -k tar' on a checkouted tree)
+tar:
+ rm -rf /tmp/$(FILE)
+ cp -r . /tmp/$(FILE)
+ cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn
+ rm -rf /tmp/$(FILE)
+
+SYSTEM_TARGETS=$(filter %-softmmu,$(TARGET_DIRS))
+SYSTEM_PROGS=$(patsubst qemu-system-i386,qemu, \
+ $(patsubst %-softmmu,qemu-system-%, \
+ $(SYSTEM_TARGETS)))
+
+USER_TARGETS=$(filter %-user,$(TARGET_DIRS))
+USER_PROGS=$(patsubst %-bsd-user,qemu-%, \
+ $(patsubst %-darwin-user,qemu-%, \
+ $(patsubst %-linux-user,qemu-%, \
+ $(USER_TARGETS))))
+
+# generate a binary distribution
+tarbin:
+ cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \
+ $(patsubst %,$(bindir)/%, $(SYSTEM_PROGS)) \
+ $(patsubst %,$(bindir)/%, $(USER_PROGS)) \
+ $(bindir)/qemu-img \
+ $(bindir)/qemu-nbd \
+ $(datadir)/bios.bin \
+ $(datadir)/vgabios.bin \
+ $(datadir)/vgabios-cirrus.bin \
+ $(datadir)/ppc_rom.bin \
+ $(datadir)/openbios-sparc32 \
+ $(datadir)/openbios-sparc64 \
+ $(datadir)/openbios-ppc \
+ $(datadir)/pxe-ne2k_pci.bin \
+ $(datadir)/pxe-rtl8139.bin \
+ $(datadir)/pxe-pcnet.bin \
+ $(datadir)/pxe-e1000.bin \
+ $(datadir)/extboot.bin \
+ $(docdir)/qemu-doc.html \
+ $(docdir)/qemu-tech.html \
+ $(mandir)/man1/qemu.1 \
+ $(mandir)/man1/qemu-img.1 \
+ $(mandir)/man8/qemu-nbd.8
+
+# Include automatically generated dependency files
+-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)
diff --git a/Makefile.dis b/Makefile.dis
new file mode 100644
index 0000000..3e1fcaf
--- /dev/null
+++ b/Makefile.dis
@@ -0,0 +1,23 @@
+# Makefile for disassemblers.
+
+include ../config-host.mak
+include config.mak
+include $(SRC_PATH)/rules.mak
+
+.PHONY: all
+
+$(call set-vpath, $(SRC_PATH))
+
+QEMU_CFLAGS+=-I..
+
+include $(SRC_PATH)/Makefile.objs
+
+all: $(libdis-y)
+# Dummy command so that make thinks it has done something
+ @true
+
+clean:
+ rm -f *.o *.d *.a *~
+
+# Include automatically generated dependency files
+-include $(wildcard *.d */*.d)
diff --git a/Makefile.hw b/Makefile.hw
new file mode 100644
index 0000000..b9181ab
--- /dev/null
+++ b/Makefile.hw
@@ -0,0 +1,24 @@
+# Makefile for qemu target independent devices.
+
+include ../config-host.mak
+include ../config-all-devices.mak
+include config.mak
+include $(SRC_PATH)/rules.mak
+
+.PHONY: all
+
+$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
+
+QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu
+
+include $(SRC_PATH)/Makefile.objs
+
+all: $(hw-obj-y)
+# Dummy command so that make thinks it has done something
+ @true
+
+clean:
+ rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~
+
+# Include automatically generated dependency files
+-include $(wildcard *.d */*.d)
diff --git a/Makefile.objs b/Makefile.objs
new file mode 100644
index 0000000..a647c45
--- /dev/null
+++ b/Makefile.objs
@@ -0,0 +1,322 @@
+#######################################################################
+# QObject
+qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
+qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
+qobject-obj-y += qerror.o
+
+#######################################################################
+# oslib-obj-y is code depending on the OS (win32 vs posix)
+oslib-obj-y = osdep.o
+oslib-obj-$(CONFIG_WIN32) += oslib-win32.o
+oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
+
+#######################################################################
+# block-obj-y is code used by both qemu system emulation and qemu-img
+
+block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
+block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o
+block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
+block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
+block-obj-$(CONFIG_POSIX) += compatfd.o
+
+block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
+block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
+block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
+block-nested-y += qed-check.o
+block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
+block-nested-$(CONFIG_WIN32) += raw-win32.o
+block-nested-$(CONFIG_POSIX) += raw-posix.o
+block-nested-$(CONFIG_CURL) += curl.o
+block-nested-$(CONFIG_RBD) += rbd.o
+
+block-obj-y += $(addprefix block/, $(block-nested-y))
+
+net-obj-y = net.o
+net-nested-y = queue.o checksum.o util.o
+net-nested-y += socket.o
+net-nested-y += dump.o
+net-nested-$(CONFIG_POSIX) += tap.o
+net-nested-$(CONFIG_LINUX) += tap-linux.o
+net-nested-$(CONFIG_WIN32) += tap-win32.o
+net-nested-$(CONFIG_BSD) += tap-bsd.o
+net-nested-$(CONFIG_SOLARIS) += tap-solaris.o
+net-nested-$(CONFIG_AIX) += tap-aix.o
+net-nested-$(CONFIG_HAIKU) += tap-haiku.o
+net-nested-$(CONFIG_SLIRP) += slirp.o
+net-nested-$(CONFIG_VDE) += vde.o
+net-obj-y += $(addprefix net/, $(net-nested-y))
+
+ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS),yy)
+# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
+# only pull in the actual virtio-9p device if we also enabled virtio.
+CONFIG_REALLY_VIRTFS=y
+endif
+fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o
+fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
+
+######################################################################
+# libqemu_common.a: Target independent part of system emulation. The
+# long term path is to suppress *all* target specific code in case of
+# system emulation, i.e. a single QEMU executable should support all
+# CPUs and machines.
+
+common-obj-y = $(block-obj-y) blockdev.o
+common-obj-y += $(net-obj-y)
+common-obj-y += $(qobject-obj-y)
+common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
+common-obj-y += readline.o console.o cursor.o async.o qemu-error.o
+common-obj-y += $(oslib-obj-y)
+common-obj-$(CONFIG_WIN32) += os-win32.o
+common-obj-$(CONFIG_POSIX) += os-posix.o
+
+common-obj-y += tcg-runtime.o host-utils.o
+common-obj-y += irq.o ioport.o input.o
+common-obj-$(CONFIG_PTIMER) += ptimer.o
+common-obj-$(CONFIG_MAX7310) += max7310.o
+common-obj-$(CONFIG_WM8750) += wm8750.o
+common-obj-$(CONFIG_TWL92230) += twl92230.o
+common-obj-$(CONFIG_TSC2005) += tsc2005.o
+common-obj-$(CONFIG_LM832X) += lm832x.o
+common-obj-$(CONFIG_TMP105) += tmp105.o
+common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
+common-obj-$(CONFIG_SSD0303) += ssd0303.o
+common-obj-$(CONFIG_SSD0323) += ssd0323.o
+common-obj-$(CONFIG_ADS7846) += ads7846.o
+common-obj-$(CONFIG_MAX111X) += max111x.o
+common-obj-$(CONFIG_DS1338) += ds1338.o
+common-obj-y += i2c.o smbus.o smbus_eeprom.o
+common-obj-y += eeprom93xx.o
+common-obj-y += scsi-disk.o cdrom.o
+common-obj-y += scsi-generic.o scsi-bus.o
+common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
+common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
+common-obj-$(CONFIG_SSI) += ssi.o
+common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
+common-obj-$(CONFIG_SD) += sd.o
+common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
+common-obj-y += bt-hci-csr.o
+common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o
+common-obj-y += qemu-char.o savevm.o #aio.o
+common-obj-y += msmouse.o ps2.o
+common-obj-y += qdev.o qdev-properties.o
+common-obj-y += block-migration.o
+common-obj-y += pflib.o
+
+common-obj-$(CONFIG_BRLAPI) += baum.o
+common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
+common-obj-$(CONFIG_WIN32) += version.o
+
+common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o
+
+audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+audio-obj-$(CONFIG_SDL) += sdlaudio.o
+audio-obj-$(CONFIG_OSS) += ossaudio.o
+audio-obj-$(CONFIG_SPICE) += spiceaudio.o
+audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
+audio-obj-$(CONFIG_ALSA) += alsaaudio.o
+audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
+audio-obj-$(CONFIG_FMOD) += fmodaudio.o
+audio-obj-$(CONFIG_ESD) += esdaudio.o
+audio-obj-$(CONFIG_PA) += paaudio.o
+audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o
+audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
+audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
+audio-obj-y += wavcapture.o
+common-obj-y += $(addprefix audio/, $(audio-obj-y))
+
+ui-obj-y += keymaps.o
+ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+ui-obj-$(CONFIG_CURSES) += curses.o
+ui-obj-y += vnc.o d3des.o
+ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
+ui-obj-y += vnc-enc-tight.o vnc-palette.o
+ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
+ui-obj-$(CONFIG_COCOA) += cocoa.o
+ifdef CONFIG_VNC_THREAD
+ui-obj-y += vnc-jobs-async.o
+else
+ui-obj-y += vnc-jobs-sync.o
+endif
+common-obj-y += $(addprefix ui/, $(ui-obj-y))
+
+common-obj-y += iov.o acl.o
+common-obj-$(CONFIG_THREAD) += qemu-thread.o
+common-obj-$(CONFIG_IOTHREAD) += compatfd.o
+common-obj-y += notify.o event_notifier.o
+common-obj-y += qemu-timer.o qemu-timer-common.o
+
+slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o
+slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
+slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o
+common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y))
+
+# xen backend driver support
+common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o
+common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o
+
+######################################################################
+# libuser
+
+user-obj-y =
+user-obj-y += envlist.o path.o
+user-obj-y += tcg-runtime.o host-utils.o
+user-obj-y += cutils.o cache-utils.o
+
+######################################################################
+# libhw
+
+hw-obj-y =
+hw-obj-y += loader.o
+hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
+hw-obj-y += fw_cfg.o
+hw-obj-$(CONFIG_PCI) += pci_bridge.o
+hw-obj-$(CONFIG_PCI) += msix.o msi.o
+hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
+hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
+hw-obj-y += watchdog.o
+hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
+hw-obj-$(CONFIG_ECC) += ecc.o
+hw-obj-$(CONFIG_NAND) += nand.o
+hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
+hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
+
+hw-obj-$(CONFIG_M48T59) += m48t59.o
+hw-obj-$(CONFIG_ESCC) += escc.o
+hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
+
+hw-obj-$(CONFIG_SERIAL) += serial.o
+hw-obj-$(CONFIG_PARALLEL) += parallel.o
+# Moved back to Makefile.target due to #include qemu-kvm.h:
+#hw-obj-$(CONFIG_I8254) += i8254.o
+#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_FDC) += fdc.o
+# needs fixes for cpu hotplug, so moved to Makefile.target:
+# hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
+hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
+hw-obj-$(CONFIG_DMA) += dma.o
+
+# PPC devices
+hw-obj-$(CONFIG_OPENPIC) += openpic.o
+hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
+# Mac shared devices
+hw-obj-$(CONFIG_MACIO) += macio.o
+hw-obj-$(CONFIG_CUDA) += cuda.o
+hw-obj-$(CONFIG_ADB) += adb.o
+hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
+hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
+# OldWorld PowerMac
+hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
+hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o
+# NewWorld PowerMac
+hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o
+hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o
+# PowerPC E500 boards
+hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
+
+# MIPS devices
+hw-obj-$(CONFIG_PIIX4) += piix4.o
+
+# PCI watchdog devices
+hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
+
+hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
+
+# PCI network cards
+hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
+hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
+hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
+hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
+hw-obj-$(CONFIG_E1000_PCI) += e1000.o
+hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
+
+hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
+hw-obj-$(CONFIG_LAN9118) += lan9118.o
+hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
+
+# IDE
+hw-obj-$(CONFIG_IDE_CORE) += ide/core.o
+hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o
+hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o
+hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o
+hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o
+hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o
+hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o
+hw-obj-$(CONFIG_IDE_VIA) += ide/via.o
+hw-obj-$(CONFIG_AHCI) += ide/ahci.o
+hw-obj-$(CONFIG_AHCI) += ide/ich.o
+
+# SCSI layer
+hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
+hw-obj-$(CONFIG_ESP) += esp.o
+
+hw-obj-y += dma-helpers.o sysbus.o isa-bus.o
+hw-obj-y += qdev-addr.o
+
+# VGA
+hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o
+hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o
+hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
+hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
+
+hw-obj-$(CONFIG_RC4030) += rc4030.o
+hw-obj-$(CONFIG_DP8393X) += dp8393x.o
+hw-obj-$(CONFIG_DS1225Y) += ds1225y.o
+hw-obj-$(CONFIG_MIPSNET) += mipsnet.o
+
+# Sound
+sound-obj-y =
+sound-obj-$(CONFIG_SB16) += sb16.o
+sound-obj-$(CONFIG_ES1370) += es1370.o
+sound-obj-$(CONFIG_AC97) += ac97.o
+sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
+sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
+sound-obj-$(CONFIG_CS4231A) += cs4231a.o
+sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
+
+adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
+hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
+
+hw-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p-debug.o
+hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o
+hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
+
+######################################################################
+# libdis
+# NOTE: the disassembler code is only needed for debugging
+
+libdis-y =
+libdis-$(CONFIG_ALPHA_DIS) += alpha-dis.o
+libdis-$(CONFIG_ARM_DIS) += arm-dis.o
+libdis-$(CONFIG_CRIS_DIS) += cris-dis.o
+libdis-$(CONFIG_HPPA_DIS) += hppa-dis.o
+libdis-$(CONFIG_I386_DIS) += i386-dis.o
+libdis-$(CONFIG_IA64_DIS) += ia64-dis.o
+libdis-$(CONFIG_M68K_DIS) += m68k-dis.o
+libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o
+libdis-$(CONFIG_MIPS_DIS) += mips-dis.o
+libdis-$(CONFIG_PPC_DIS) += ppc-dis.o
+libdis-$(CONFIG_S390_DIS) += s390-dis.o
+libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
+libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
+
+######################################################################
+# trace
+
+ifeq ($(TRACE_BACKEND),dtrace)
+trace-obj-y = trace-dtrace.o
+else
+trace-obj-y = trace.o
+ifeq ($(TRACE_BACKEND),simple)
+trace-obj-y += simpletrace.o
+user-obj-y += qemu-timer-common.o
+endif
+endif
+
+vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
+
+vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
+
diff --git a/Makefile.target b/Makefile.target
new file mode 100644
index 0000000..e3d4951
--- /dev/null
+++ b/Makefile.target
@@ -0,0 +1,412 @@
+# -*- Mode: makefile -*-
+
+GENERATED_HEADERS = config-target.h
+CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
+CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
+
+include ../config-host.mak
+include config-devices.mak
+include config-target.mak
+include $(SRC_PATH)/rules.mak
+ifneq ($(HWDIR),)
+include $(HWDIR)/config.mak
+endif
+
+TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
+$(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw)
+QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H
+
+include $(SRC_PATH)/Makefile.objs
+
+ifdef CONFIG_USER_ONLY
+# user emulator name
+QEMU_PROG=qemu-$(TARGET_ARCH2)
+else
+# system emulator name
+ifeq ($(TARGET_ARCH), i386)
+QEMU_PROG=qemu$(EXESUF)
+else
+QEMU_PROG=qemu-system-$(TARGET_ARCH2)$(EXESUF)
+endif
+endif
+
+PROGS=$(QEMU_PROG)
+STPFILES=
+
+ifndef CONFIG_HAIKU
+LIBS+=-lm
+endif
+
+kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS)
+
+CFLAGS += $(KVM_CFLAGS)
+
+config-target.h: config-target.h-timestamp
+config-target.h-timestamp: config-target.mak
+
+ifdef CONFIG_SYSTEMTAP_TRACE
+stap: $(QEMU_PROG).stp
+
+ifdef CONFIG_USER_ONLY
+TARGET_TYPE=user
+else
+TARGET_TYPE=system
+endif
+
+$(QEMU_PROG).stp:
+ $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \
+ --$(TRACE_BACKEND) \
+ --binary $(bindir)/$(QEMU_PROG) \
+ --target-arch $(TARGET_ARCH) \
+ --target-type $(TARGET_TYPE) \
+ --stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp")
+else
+stap:
+endif
+
+all: $(PROGS) stap
+
+# Dummy command so that make thinks it has done something
+ @true
+
+#########################################################
+# cpu emulator library
+libobj-y = exec.o cpu-exec.o
+libobj-$(CONFIG_NO_CPU_EMULATION) += fake-exec.o
+libobj-$(CONFIG_CPU_EMULATION) += translate-all.o translate.o
+libobj-$(CONFIG_CPU_EMULATION) += tcg/tcg.o
+libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o
+libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
+libobj-y += op_helper.o helper.o
+ifeq ($(TARGET_BASE_ARCH), i386)
+libobj-y += cpuid.o
+endif
+libobj-$(CONFIG_NEED_MMU) += mmu.o
+
+libobj-$(CONFIG_KVM) += kvm-tpr-opt.o
+
+libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
+
+libobj-y += disas.o
+
+$(libobj-y): $(GENERATED_HEADERS)
+
+# libqemu
+
+translate.o: translate.c cpu.h
+
+translate-all.o: translate-all.c cpu.h
+
+tcg/tcg.o: cpu.h
+
+# HELPER_CFLAGS is used for all the code compiled with static register
+# variables
+op_helper.o cpu-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.
+signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
+
+qemu-kvm-helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
+
+#########################################################
+# Linux user emulator target
+
+ifdef CONFIG_LINUX_USER
+
+$(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR))
+
+QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
+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)
+
+obj-$(TARGET_HAS_BFLT) += flatload.o
+
+obj-$(TARGET_I386) += vm86.o
+
+obj-i386-y += ioport-user.o
+
+nwfpe-obj-y = fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o fpopcode.o
+nwfpe-obj-y += single_cpdo.o double_cpdo.o extended_cpdo.o
+obj-arm-y += $(addprefix nwfpe/, $(nwfpe-obj-y))
+obj-arm-y += arm-semi.o
+
+obj-m68k-y += m68k-sim.o m68k-semi.o
+
+$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
+
+obj-y += $(addprefix ../libuser/, $(user-obj-y))
+obj-y += $(addprefix ../libdis-user/, $(libdis-y))
+obj-y += $(libobj-y)
+
+endif #CONFIG_LINUX_USER
+
+#########################################################
+# Darwin user emulator target
+
+ifdef CONFIG_DARWIN_USER
+
+$(call set-vpath, $(SRC_PATH)/darwin-user)
+
+QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
+
+# Leave some space for the regular program loading zone
+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
+
+obj-i386-y += ioport-user.o
+
+$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
+
+obj-y += $(addprefix ../libuser/, $(user-obj-y))
+obj-y += $(addprefix ../libdis-user/, $(libdis-y))
+obj-y += $(libobj-y)
+
+endif #CONFIG_DARWIN_USER
+
+#########################################################
+# BSD user emulator target
+
+ifdef CONFIG_BSD_USER
+
+$(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
+
+obj-i386-y += ioport-user.o
+
+$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
+
+obj-y += $(addprefix ../libuser/, $(user-obj-y))
+obj-y += $(addprefix ../libdis-user/, $(libdis-y))
+obj-y += $(libobj-y)
+
+endif #CONFIG_BSD_USER
+
+#########################################################
+# System emulator target
+ifdef CONFIG_SOFTMMU
+
+obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o vl.o balloon.o
+# virtio has to be here due to weird dependency between PCI and virtio-net.
+# need to fix this properly
+obj-$(CONFIG_NO_PCI) += pci-stub.o
+obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
+obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
+obj-y += vhost_net.o
+obj-$(CONFIG_VHOST_NET) += vhost.o
+obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o
+obj-y += rwhandler.o
+obj-$(CONFIG_KVM) += kvm.o kvm-all.o
+obj-$(CONFIG_NO_KVM) += kvm-stub.o
+
+LIBS+=-lz
+
+QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
+QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
+QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
+QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
+
+# xen backend driver support
+obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
+
+# Inter-VM PCI shared memory
+obj-$(CONFIG_KVM) += ivshmem.o
+
+# Hardware support
+obj-i386-y += vga.o
+obj-i386-y += mc146818rtc.o i8259.o pc.o
+obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
+obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o
+obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
+obj-i386-y += extboot.o
+obj-i386-y += debugcon.o multiboot.o
+obj-i386-y += pc_piix.o
+obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+obj-i386-y += testdev.o
+obj-i386-y += acpi.o acpi_piix4.o
+
+obj-i386-y += pcspk.o i8254.o
+obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
+obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o
+
+# Hardware support
+obj-ia64-y += ide.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
+obj-ia64-y += fdc.o mc146818rtc.o serial.o i8259.o ipf.o
+obj-ia64-y += cirrus_vga.o parallel.o acpi.o piix_pci.o
+obj-ia64-y += usb-uhci.o
+obj-ia64-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o
+
+# shared objects
+obj-ppc-y = ppc.o
+obj-ppc-y += vga.o
+# PREP target
+obj-ppc-y += i8259.o mc146818rtc.o
+obj-ppc-y += ppc_prep.o
+# OldWorld PowerMac
+obj-ppc-y += ppc_oldworld.o
+# NewWorld PowerMac
+obj-ppc-y += ppc_newworld.o
+# PowerPC 4xx boards
+obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
+obj-ppc-y += ppc440.o ppc440_bamboo.o
+# PowerPC E500 boards
+obj-ppc-y += ppce500_mpc8544ds.o
+# PowerPC 440 Xilinx ML507 reference board.
+obj-ppc-y += virtex_ml507.o
+obj-ppc-$(CONFIG_KVM) += kvm_ppc.o
+obj-ppc-$(CONFIG_FDT) += device_tree.o
+
+# Xilinx PPC peripherals
+obj-ppc-y += xilinx_intc.o
+obj-ppc-y += xilinx_timer.o
+obj-ppc-y += xilinx_uartlite.o
+obj-ppc-y += xilinx_ethlite.o
+
+obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
+obj-mips-y += pcspk.o i8254.o
+obj-mips-y += acpi.o acpi_piix4.o
+obj-mips-y += mips_addr.o mips_timer.o mips_int.o
+obj-mips-y += vga.o i8259.o
+obj-mips-y += g364fb.o jazz_led.o
+obj-mips-y += gt64xxx.o mc146818rtc.o
+obj-mips-y += cirrus_vga.o
+obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
+
+obj-microblaze-y = petalogix_s3adsp1800_mmu.o
+
+obj-microblaze-y += microblaze_pic_cpu.o
+obj-microblaze-y += xilinx_intc.o
+obj-microblaze-y += xilinx_timer.o
+obj-microblaze-y += xilinx_uartlite.o
+obj-microblaze-y += xilinx_ethlite.o
+
+obj-microblaze-$(CONFIG_FDT) += device_tree.o
+
+# Boards
+obj-cris-y = cris_pic_cpu.o
+obj-cris-y += cris-boot.o
+obj-cris-y += etraxfs.o axis_dev88.o
+obj-cris-y += axis_dev88.o
+
+# IO blocks
+obj-cris-y += etraxfs_dma.o
+obj-cris-y += etraxfs_pic.o
+obj-cris-y += etraxfs_eth.o
+obj-cris-y += etraxfs_timer.o
+obj-cris-y += etraxfs_ser.o
+
+ifeq ($(TARGET_ARCH), sparc64)
+obj-sparc-y = sun4u.o apb_pci.o
+obj-sparc-y += vga.o
+obj-sparc-y += mc146818rtc.o
+obj-sparc-y += cirrus_vga.o
+else
+obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
+obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
+obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
+
+# GRLIB
+obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
+endif
+
+obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
+obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
+obj-arm-y += versatile_pci.o
+obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
+obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
+obj-arm-y += pl061.o
+obj-arm-y += arm-semi.o
+obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
+obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
+obj-arm-y += gumstix.o
+obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o
+obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
+ omap_gpio.o omap_intc.o omap_uart.o
+obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
+ omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
+obj-arm-y += omap_sx1.o palm.o tsc210x.o
+obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
+obj-arm-y += mst_fpga.o mainstone.o
+obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
+obj-arm-y += framebuffer.o
+obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
+obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
+obj-arm-y += syborg_virtio.o
+
+obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
+obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
+obj-sh4-y += ide/mmio.o
+
+obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
+obj-m68k-y += m68k-semi.o dummy_m68k.o
+
+obj-s390x-y = s390-virtio-bus.o s390-virtio.o
+
+obj-alpha-y = alpha_palcode.o
+
+ifeq ($(TARGET_ARCH), ia64)
+firmware.o: firmware.c
+ $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+endif
+
+main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
+
+monitor.o: hmp-commands.h qmp-commands.h
+
+$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
+
+obj-y += $(addprefix ../, $(common-obj-y))
+obj-y += $(addprefix ../libdis/, $(libdis-y))
+obj-y += $(libobj-y)
+obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
+
+endif # CONFIG_SOFTMMU
+
+obj-y += $(addprefix ../, $(trace-obj-y))
+obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
+
+$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
+ $(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y))
+
+
+gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh
+ $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
+
+hmp-commands.h: $(SRC_PATH)/hmp-commands.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
+
+qmp-commands.h: $(SRC_PATH)/qmp-commands.hx
+ $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
+
+clean:
+ rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
+ rm -f *.d */*.d tcg/*.o ide/*.o
+ rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c
+ifdef CONFIG_SYSTEMTAP_TRACE
+ rm -f *.stp
+endif
+
+install: all
+ifneq ($(PROGS),)
+ $(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
+ifneq ($(STRIP),)
+ $(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS))
+endif
+endif
+ifdef CONFIG_SYSTEMTAP_TRACE
+ $(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset"
+ $(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset"
+endif
+
+# Include automatically generated dependency files
+-include $(wildcard *.d */*.d)
diff --git a/Makefile.user b/Makefile.user
new file mode 100644
index 0000000..024b773
--- /dev/null
+++ b/Makefile.user
@@ -0,0 +1,23 @@
+# Makefile for qemu target independent user files.
+
+include ../config-host.mak
+include $(SRC_PATH)/rules.mak
+-include config.mak
+
+.PHONY: all
+
+$(call set-vpath, $(SRC_PATH))
+
+QEMU_CFLAGS+=-I..
+
+include $(SRC_PATH)/Makefile.objs
+
+all: $(user-obj-y)
+# Dummy command so that make thinks it has done something
+ @true
+
+clean:
+ rm -f *.o *.d *.a *~
+
+# Include automatically generated dependency files
+-include $(wildcard *.d */*.d)
diff --git a/QMP/README b/QMP/README
new file mode 100644
index 0000000..c95a08c
--- /dev/null
+++ b/QMP/README
@@ -0,0 +1,88 @@
+ QEMU Monitor Protocol
+ =====================
+
+Introduction
+-------------
+
+The QEMU Monitor Protocol (QMP) allows applications to communicate with
+QEMU's Monitor.
+
+QMP is JSON[1] based and currently has the following features:
+
+- Lightweight, text-based, easy to parse data format
+- Asynchronous messages support (ie. events)
+- Capabilities Negotiation
+
+For detailed information on QMP's usage, please, refer to the following files:
+
+o qmp-spec.txt QEMU Monitor Protocol current specification
+o qmp-commands.txt QMP supported commands (auto-generated at build-time)
+o qmp-events.txt List of available asynchronous events
+
+There is also a simple Python script called 'qmp-shell' available.
+
+IMPORTANT: It's strongly recommended to read the 'Stability Considerations'
+section in the qmp-commands.txt file before making any serious use of QMP.
+
+
+[1] http://www.json.org
+
+Usage
+-----
+
+To enable QMP, you need a QEMU monitor instance in "control mode". There are
+two ways of doing this.
+
+The simplest one is using the '-qmp' command-line option. The following
+example makes QMP available on localhost port 4444:
+
+ $ qemu [...] -qmp tcp:localhost:4444,server
+
+However, in order to have more complex combinations, like multiple monitors,
+the '-mon' command-line option should be used along with the '-chardev' one.
+For instance, the following example creates one user monitor on stdio and one
+QMP monitor on localhost port 4444.
+
+ $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \
+ -chardev socket,id=mon1,host=localhost,port=4444,server \
+ -mon chardev=mon1,mode=control
+
+Please, refer to QEMU's manpage for more information.
+
+Simple Testing
+--------------
+
+To manually test QMP one can connect with telnet and issue commands by hand:
+
+$ telnet localhost 4444
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}}
+{ "execute": "qmp_capabilities" }
+{"return": {}}
+{ "execute": "query-version" }
+{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}}
+
+Development Process
+-------------------
+
+When changing QMP's interface (by adding new commands, events or modifying
+existing ones) it's mandatory to update the relevant documentation, which is
+one (or more) of the files listed in the 'Introduction' section*.
+
+Also, it's strongly recommended to send the documentation patch first, before
+doing any code change. This is so because:
+
+ 1. Avoids the code dictating the interface
+
+ 2. Review can improve your interface. Letting that happen before
+ you implement it can save you work.
+
+* The qmp-commands.txt file is generated from the qmp-commands.hx one, which
+ is the file that should be edited.
+
+Homepage
+--------
+
+http://wiki.qemu.org/QMP
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
new file mode 100644
index 0000000..0ce5d4e
--- /dev/null
+++ b/QMP/qmp-events.txt
@@ -0,0 +1,266 @@
+ QEMU Monitor Protocol Events
+ ============================
+
+BLOCK_IO_ERROR
+--------------
+
+Emitted when a disk I/O error occurs.
+
+Data:
+
+- "device": device name (json-string)
+- "operation": I/O operation (json-string, "read" or "write")
+- "action": action that has been taken, it's one of the following (json-string):
+ "ignore": error has been ignored
+ "report": error has been reported to the device
+ "stop": error caused VM to be stopped
+
+Example:
+
+{ "event": "BLOCK_IO_ERROR",
+ "data": { "device": "ide0-hd1",
+ "operation": "write",
+ "action": "stop" },
+ "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+
+Note: If action is "stop", a STOP event will eventually follow the
+BLOCK_IO_ERROR event.
+
+RESET
+-----
+
+Emitted when the Virtual Machine is reseted.
+
+Data: None.
+
+Example:
+
+{ "event": "RESET",
+ "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
+
+RESUME
+------
+
+Emitted when the Virtual Machine resumes execution.
+
+Data: None.
+
+Example:
+
+{ "event": "RESUME",
+ "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
+
+RTC_CHANGE
+----------
+
+Emitted when the guest changes the RTC time.
+
+Data:
+
+- "offset": delta against the host UTC in seconds (json-number)
+
+Example:
+
+{ "event": "RTC_CHANGE",
+ "data": { "offset": 78 },
+ "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
+
+SHUTDOWN
+--------
+
+Emitted when the Virtual Machine is powered down.
+
+Data: None.
+
+Example:
+
+{ "event": "SHUTDOWN",
+ "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
+
+Note: If the command-line option "-no-shutdown" has been specified, a STOP
+event will eventually follow the SHUTDOWN event.
+
+STOP
+----
+
+Emitted when the Virtual Machine is stopped.
+
+Data: None.
+
+Example:
+
+{ "event": "STOP",
+ "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
+
+VNC_CONNECTED
+-------------
+
+Emitted when a VNC client establishes a connection.
+
+Data:
+
+- "server": Server information (json-object)
+ - "host": IP address (json-string)
+ - "service": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+ - "host": IP address (json-string)
+ - "service": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+
+Example:
+
+{ "event": "VNC_CONNECTED",
+ "data": {
+ "server": { "auth": "sasl", "family": "ipv4",
+ "service": "5901", "host": "0.0.0.0" },
+ "client": { "family": "ipv4", "service": "58425",
+ "host": "127.0.0.1" } },
+ "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
+
+
+Note: This event is emitted before any authentication takes place, thus
+the authentication ID is not provided.
+
+VNC_DISCONNECTED
+----------------
+
+Emitted when the conection is closed.
+
+Data:
+
+- "server": Server information (json-object)
+ - "host": IP address (json-string)
+ - "service": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+ - "host": IP address (json-string)
+ - "service": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "x509_dname": TLS dname (json-string, optional)
+ - "sasl_username": SASL username (json-string, optional)
+
+Example:
+
+{ "event": "VNC_DISCONNECTED",
+ "data": {
+ "server": { "auth": "sasl", "family": "ipv4",
+ "service": "5901", "host": "0.0.0.0" },
+ "client": { "family": "ipv4", "service": "58425",
+ "host": "127.0.0.1", "sasl_username": "luiz" } },
+ "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
+
+VNC_INITIALIZED
+---------------
+
+Emitted after authentication takes place (if any) and the VNC session is
+made active.
+
+Data:
+
+- "server": Server information (json-object)
+ - "host": IP address (json-string)
+ - "service": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+ - "host": IP address (json-string)
+ - "service": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "x509_dname": TLS dname (json-string, optional)
+ - "sasl_username": SASL username (json-string, optional)
+
+Example:
+
+{ "event": "VNC_INITIALIZED",
+ "data": {
+ "server": { "auth": "sasl", "family": "ipv4",
+ "service": "5901", "host": "0.0.0.0"},
+ "client": { "family": "ipv4", "service": "46089",
+ "host": "127.0.0.1", "sasl_username": "luiz" } },
+ "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
+
+SPICE_CONNECTED, SPICE_DISCONNECTED
+-----------------------------------
+
+Emitted when a SPICE client connects or disconnects.
+
+Data:
+
+- "server": Server information (json-object)
+ - "host": IP address (json-string)
+ - "port": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+- "client": Client information (json-object)
+ - "host": IP address (json-string)
+ - "port": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+
+Example:
+
+{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
+ "event": "SPICE_CONNECTED",
+ "data": {
+ "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
+ "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
+}}
+
+
+SPICE_INITIALIZED
+-----------------
+
+Emitted after initial handshake and authentication takes place (if any)
+and the SPICE channel is up'n'running
+
+Data:
+
+- "server": Server information (json-object)
+ - "host": IP address (json-string)
+ - "port": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "auth": authentication method (json-string, optional)
+- "client": Client information (json-object)
+ - "host": IP address (json-string)
+ - "port": port number (json-string)
+ - "family": address family (json-string, "ipv4" or "ipv6")
+ - "connection-id": spice connection id. All channels with the same id
+ belong to the same spice session (json-int)
+ - "channel-type": channel type. "1" is the main control channel, filter for
+ this one if you want track spice sessions only (json-int)
+ - "channel-id": channel id. Usually "0", might be different needed when
+ multiple channels of the same type exist, such as multiple
+ display channels in a multihead setup (json-int)
+ - "tls": whevener the channel is encrypted (json-bool)
+
+Example:
+
+{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
+ "event": "SPICE_INITIALIZED",
+ "data": {"server": {"auth": "spice", "port": "5921",
+ "family": "ipv4", "host": "127.0.0.1"},
+ "client": {"port": "49004", "family": "ipv4", "channel-type": 3,
+ "connection-id": 1804289383, "host": "127.0.0.1",
+ "channel-id": 0, "tls": true}
+}}
+
+
+WATCHDOG
+--------
+
+Emitted when the watchdog device's timer is expired.
+
+Data:
+
+- "action": Action that has been taken, it's one of the following (json-string):
+ "reset", "shutdown", "poweroff", "pause", "debug", or "none"
+
+Example:
+
+{ "event": "WATCHDOG",
+ "data": { "action": "reset" },
+ "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
+
+Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
+followed respectively by the RESET, SHUTDOWN, or STOP events.
diff --git a/QMP/qmp-shell b/QMP/qmp-shell
new file mode 100755
index 0000000..42dabc8
--- /dev/null
+++ b/QMP/qmp-shell
@@ -0,0 +1,259 @@
+#!/usr/bin/python
+#
+# Low-level QEMU shell on top of QMP.
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+# Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+#
+# Usage:
+#
+# Start QEMU with:
+#
+# # qemu [...] -qmp unix:./qmp-sock,server
+#
+# Run the shell:
+#
+# $ qmp-shell ./qmp-sock
+#
+# Commands have the following format:
+#
+# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+#
+# For example:
+#
+# (QEMU) device_add driver=e1000 id=net1
+# {u'return': {}}
+# (QEMU)
+
+import qmp
+import readline
+import sys
+
+class QMPCompleter(list):
+ def complete(self, text, state):
+ for cmd in self:
+ if cmd.startswith(text):
+ if not state:
+ return cmd
+ else:
+ state -= 1
+
+class QMPShellError(Exception):
+ pass
+
+class QMPShellBadPort(QMPShellError):
+ pass
+
+# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
+# _execute_cmd()). Let's design a better one.
+class QMPShell(qmp.QEMUMonitorProtocol):
+ def __init__(self, address):
+ qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
+ self._greeting = None
+ self._completer = None
+
+ def __get_address(self, arg):
+ """
+ Figure out if the argument is in the port:host form, if it's not it's
+ probably a file path.
+ """
+ addr = arg.split(':')
+ if len(addr) == 2:
+ try:
+ port = int(addr[1])
+ except ValueError:
+ raise QMPShellBadPort
+ return ( addr[0], port )
+ # socket path
+ return arg
+
+ def _fill_completion(self):
+ for cmd in self.cmd('query-commands')['return']:
+ self._completer.append(cmd['name'])
+
+ def __completer_setup(self):
+ self._completer = QMPCompleter()
+ self._fill_completion()
+ readline.set_completer(self._completer.complete)
+ readline.parse_and_bind("tab: complete")
+ # XXX: default delimiters conflict with some command names (eg. query-),
+ # clearing everything as it doesn't seem to matter
+ readline.set_completer_delims('')
+
+ def __build_cmd(self, cmdline):
+ """
+ Build a QMP input object from a user provided command-line in the
+ following format:
+
+ < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+ """
+ cmdargs = cmdline.split()
+ qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
+ for arg in cmdargs[1:]:
+ opt = arg.split('=')
+ try:
+ value = int(opt[1])
+ except ValueError:
+ value = opt[1]
+ qmpcmd['arguments'][opt[0]] = value
+ return qmpcmd
+
+ def _execute_cmd(self, cmdline):
+ try:
+ qmpcmd = self.__build_cmd(cmdline)
+ except:
+ print 'command format: <command-name> ',
+ print '[arg-name1=arg1] ... [arg-nameN=argN]'
+ return True
+ resp = self.cmd_obj(qmpcmd)
+ if resp is None:
+ print 'Disconnected'
+ return False
+ print resp
+ return True
+
+ def connect(self):
+ self._greeting = qmp.QEMUMonitorProtocol.connect(self)
+ self.__completer_setup()
+
+ def show_banner(self, msg='Welcome to the QMP low-level shell!'):
+ print msg
+ version = self._greeting['QMP']['version']['qemu']
+ print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
+
+ def read_exec_command(self, prompt):
+ """
+ Read and execute a command.
+
+ @return True if execution was ok, return False if disconnected.
+ """
+ try:
+ cmdline = raw_input(prompt)
+ except EOFError:
+ print
+ return False
+ if cmdline == '':
+ for ev in self.get_events():
+ print ev
+ self.clear_events()
+ return True
+ else:
+ return self._execute_cmd(cmdline)
+
+class HMPShell(QMPShell):
+ def __init__(self, address):
+ QMPShell.__init__(self, address)
+ self.__cpu_index = 0
+
+ def __cmd_completion(self):
+ for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
+ if cmd and cmd[0] != '[' and cmd[0] != '\t':
+ name = cmd.split()[0] # drop help text
+ if name == 'info':
+ continue
+ if name.find('|') != -1:
+ # Command in the form 'foobar|f' or 'f|foobar', take the
+ # full name
+ opt = name.split('|')
+ if len(opt[0]) == 1:
+ name = opt[1]
+ else:
+ name = opt[0]
+ self._completer.append(name)
+ self._completer.append('help ' + name) # help completion
+
+ def __info_completion(self):
+ for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
+ if cmd:
+ self._completer.append('info ' + cmd.split()[1])
+
+ def __other_completion(self):
+ # special cases
+ self._completer.append('help info')
+
+ def _fill_completion(self):
+ self.__cmd_completion()
+ self.__info_completion()
+ self.__other_completion()
+
+ def __cmd_passthrough(self, cmdline, cpu_index = 0):
+ return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
+ { 'command-line': cmdline,
+ 'cpu-index': cpu_index } })
+
+ def _execute_cmd(self, cmdline):
+ if cmdline.split()[0] == "cpu":
+ # trap the cpu command, it requires special setting
+ try:
+ idx = int(cmdline.split()[1])
+ if not 'return' in self.__cmd_passthrough('info version', idx):
+ print 'bad CPU index'
+ return True
+ self.__cpu_index = idx
+ except ValueError:
+ print 'cpu command takes an integer argument'
+ return True
+ resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
+ if resp is None:
+ print 'Disconnected'
+ return False
+ assert 'return' in resp or 'error' in resp
+ if 'return' in resp:
+ # Success
+ if len(resp['return']) > 0:
+ print resp['return'],
+ else:
+ # Error
+ print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
+ return True
+
+ def show_banner(self):
+ QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
+
+def die(msg):
+ sys.stderr.write('ERROR: %s\n' % msg)
+ sys.exit(1)
+
+def fail_cmdline(option=None):
+ if option:
+ sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
+ sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
+ sys.exit(1)
+
+def main():
+ addr = ''
+ try:
+ if len(sys.argv) == 2:
+ qemu = QMPShell(sys.argv[1])
+ addr = sys.argv[1]
+ elif len(sys.argv) == 3:
+ if sys.argv[1] != '-H':
+ fail_cmdline(sys.argv[1])
+ qemu = HMPShell(sys.argv[2])
+ addr = sys.argv[2]
+ else:
+ fail_cmdline()
+ except QMPShellBadPort:
+ die('bad port number in command-line')
+
+ try:
+ qemu.connect()
+ except qmp.QMPConnectError:
+ die('Didn\'t get QMP greeting message')
+ except qmp.QMPCapabilitiesError:
+ die('Could not negotiate capabilities')
+ except qemu.error:
+ die('Could not connect to %s' % addr)
+
+ qemu.show_banner()
+ while qemu.read_exec_command('(QEMU) '):
+ pass
+ qemu.close()
+
+if __name__ == '__main__':
+ main()
diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt
new file mode 100644
index 0000000..9d30a8c
--- /dev/null
+++ b/QMP/qmp-spec.txt
@@ -0,0 +1,272 @@
+ QEMU Monitor Protocol Specification - Version 0.1
+
+1. Introduction
+===============
+
+This document specifies the QEMU Monitor Protocol (QMP), a JSON-based protocol
+which is available for applications to control QEMU at the machine-level.
+
+To enable QMP support, QEMU has to be run in "control mode". This is done by
+starting QEMU with the appropriate command-line options. Please, refer to the
+QEMU manual page for more information.
+
+2. Protocol Specification
+=========================
+
+This section details the protocol format. For the purpose of this document
+"Client" is any application which is communicating with QEMU in control mode,
+and "Server" is QEMU itself.
+
+JSON data structures, when mentioned in this document, are always in the
+following format:
+
+ json-DATA-STRUCTURE-NAME
+
+Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
+the JSON standard:
+
+http://www.ietf.org/rfc/rfc4627.txt
+
+For convenience, json-object members and json-array elements mentioned in
+this document will be in a certain order. However, in real protocol usage
+they can be in ANY order, thus no particular order should be assumed.
+
+2.1 General Definitions
+-----------------------
+
+2.1.1 All interactions transmitted by the Server are json-objects, always
+ terminating with CRLF
+
+2.1.2 All json-objects members are mandatory when not specified otherwise
+
+2.2 Server Greeting
+-------------------
+
+Right when connected the Server will issue a greeting message, which signals
+that the connection has been successfully established and that the Server is
+ready for capabilities negotiation (for more information refer to section
+'4. Capabilities Negotiation').
+
+The format is:
+
+{ "QMP": { "version": json-object, "capabilities": json-array } }
+
+ Where,
+
+- The "version" member contains the Server's version information (the format
+ is the same of the 'query-version' command)
+- The "capabilities" member specify the availability of features beyond the
+ baseline specification
+
+2.3 Issuing Commands
+--------------------
+
+The format for command execution is:
+
+{ "execute": json-string, "arguments": json-object, "id": json-value }
+
+ Where,
+
+- The "execute" member identifies the command to be executed by the Server
+- The "arguments" member is used to pass any arguments required for the
+ execution of the command, it is optional when no arguments are required
+- The "id" member is a transaction identification associated with the
+ command execution, it is optional and will be part of the response if
+ provided
+
+2.4 Commands Responses
+----------------------
+
+There are two possible responses which the Server will issue as the result
+of a command execution: success or error.
+
+2.4.1 success
+-------------
+
+The success response is issued when the command execution has finished
+without errors.
+
+The format is:
+
+{ "return": json-object, "id": json-value }
+
+ Where,
+
+- The "return" member contains the command returned data, which is defined
+ in a per-command basis or an empty json-object if the command does not
+ return data
+- The "id" member contains the transaction identification associated
+ with the command execution (if issued by the Client)
+
+2.4.2 error
+-----------
+
+The error response is issued when the command execution could not be
+completed because of an error condition.
+
+The format is:
+
+{ "error": { "class": json-string, "data": json-object, "desc": json-string },
+ "id": json-value }
+
+ Where,
+
+- The "class" member contains the error class name (eg. "ServiceUnavailable")
+- The "data" member contains specific error data and is defined in a
+ per-command basis, it will be an empty json-object if the error has no data
+- The "desc" member is a human-readable error message. Clients should
+ not attempt to parse this message.
+- The "id" member contains the transaction identification associated with
+ the command execution (if issued by the Client)
+
+NOTE: Some errors can occur before the Server is able to read the "id" member,
+in these cases the "id" member will not be part of the error response, even
+if provided by the client.
+
+2.5 Asynchronous events
+-----------------------
+
+As a result of state changes, the Server may send messages unilaterally
+to the Client at any time. They are called 'asynchronous events'.
+
+The format is:
+
+{ "event": json-string, "data": json-object,
+ "timestamp": { "seconds": json-number, "microseconds": json-number } }
+
+ Where,
+
+- The "event" member contains the event's name
+- The "data" member contains event specific data, which is defined in a
+ per-event basis, it is optional
+- The "timestamp" member contains the exact time of when the event occurred
+ in the Server. It is a fixed json-object with time in seconds and
+ microseconds
+
+For a listing of supported asynchronous events, please, refer to the
+qmp-events.txt file.
+
+3. QMP Examples
+===============
+
+This section provides some examples of real QMP usage, in all of them
+'C' stands for 'Client' and 'S' stands for 'Server'.
+
+3.1 Server greeting
+-------------------
+
+S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}}
+
+3.2 Simple 'stop' execution
+---------------------------
+
+C: { "execute": "stop" }
+S: {"return": {}}
+
+3.3 KVM information
+-------------------
+
+C: { "execute": "query-kvm", "id": "example" }
+S: {"return": {"enabled": true, "present": true}, "id": "example"}
+
+3.4 Parsing error
+------------------
+
+C: { "execute": }
+S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data":
+{}}}
+
+3.5 Powerdown event
+-------------------
+
+S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event":
+"POWERDOWN"}
+
+4. Capabilities Negotiation
+----------------------------
+
+When a Client successfully establishes a connection, the Server is in
+Capabilities Negotiation mode.
+
+In this mode only the 'qmp_capabilities' command is allowed to run, all
+other commands will return the CommandNotFound error. Asynchronous messages
+are not delivered either.
+
+Clients should use the 'qmp_capabilities' command to enable capabilities
+advertised in the Server's greeting (section '2.2 Server Greeting') they
+support.
+
+When the 'qmp_capabilities' command is issued, and if it does not return an
+error, the Server enters in Command mode where capabilities changes take
+effect, all commands (except 'qmp_capabilities') are allowed and asynchronous
+messages are delivered.
+
+5 Compatibility Considerations
+------------------------------
+
+All protocol changes or new features which modify the protocol format in an
+incompatible way are disabled by default and will be advertised by the
+capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
+that array and enable the capabilities they support.
+
+Additionally, Clients must not assume any particular:
+
+- Size of json-objects or length of json-arrays
+- Order of json-object members or json-array elements
+- Amount of errors generated by a command, that is, new errors can be added
+ to any existing command in newer versions of the Server
+
+6. Downstream extension of QMP
+------------------------------
+
+We recommend that downstream consumers of QEMU do *not* modify QMP.
+Management tools should be able to support both upstream and downstream
+versions of QMP without special logic, and downstream extensions are
+inherently at odds with that.
+
+However, we recognize that it is sometimes impossible for downstreams to
+avoid modifying QMP. Both upstream and downstream need to take care to
+preserve long-term compatibility and interoperability.
+
+To help with that, QMP reserves JSON object member names beginning with
+'__' (double underscore) for downstream use ("downstream names"). This
+means upstream will never use any downstream names for its commands,
+arguments, errors, asynchronous events, and so forth.
+
+Any new names downstream wishes to add must begin with '__'. To
+ensure compatibility with other downstreams, it is strongly
+recommended that you prefix your downstram names with '__RFQDN_' where
+RFQDN is a valid, reverse fully qualified domain name which you
+control. For example, a qemu-kvm specific monitor command would be:
+
+ (qemu) __org.linux-kvm_enable_irqchip
+
+Downstream must not change the server greeting (section 2.2) other than
+to offer additional capabilities. But see below for why even that is
+discouraged.
+
+Section '5 Compatibility Considerations' applies to downstream as well
+as to upstream, obviously. It follows that downstream must behave
+exactly like upstream for any input not containing members with
+downstream names ("downstream members"), except it may add members
+with downstream names to its output.
+
+Thus, a client should not be able to distinguish downstream from
+upstream as long as it doesn't send input with downstream members, and
+properly ignores any downstream members in the output it receives.
+
+Advice on downstream modifications:
+
+1. Introducing new commands is okay. If you want to extend an existing
+ command, consider introducing a new one with the new behaviour
+ instead.
+
+2. Introducing new asynchronous messages is okay. If you want to extend
+ an existing message, consider adding a new one instead.
+
+3. Introducing new errors for use in new commands is okay. Adding new
+ errors to existing commands counts as extension, so 1. applies.
+
+4. New capabilities are strongly discouraged. Capabilities are for
+ evolving the basic protocol, and multiple diverging basic protocol
+ dialects are most undesirable.
diff --git a/QMP/qmp.py b/QMP/qmp.py
new file mode 100644
index 0000000..14ce8b0
--- /dev/null
+++ b/QMP/qmp.py
@@ -0,0 +1,131 @@
+# QEMU Monitor Protocol Python class
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+# Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+import json
+import errno
+import socket
+
+class QMPError(Exception):
+ pass
+
+class QMPConnectError(QMPError):
+ pass
+
+class QMPCapabilitiesError(QMPError):
+ pass
+
+class QEMUMonitorProtocol:
+ def __init__(self, address):
+ """
+ Create a QEMUMonitorProtocol class.
+
+ @param address: QEMU address, can be either a unix socket path (string)
+ or a tuple in the form ( address, port ) for a TCP
+ connection
+ @note No connection is established, this is done by the connect() method
+ """
+ self.__events = []
+ self.__address = address
+ self.__sock = self.__get_sock()
+ self.__sockfile = self.__sock.makefile()
+
+ def __get_sock(self):
+ if isinstance(self.__address, tuple):
+ family = socket.AF_INET
+ else:
+ family = socket.AF_UNIX
+ return socket.socket(family, socket.SOCK_STREAM)
+
+ def __json_read(self):
+ while True:
+ data = self.__sockfile.readline()
+ if not data:
+ return
+ resp = json.loads(data)
+ if 'event' in resp:
+ self.__events.append(resp)
+ continue
+ return resp
+
+ error = socket.error
+
+ def connect(self):
+ """
+ Connect to the QMP Monitor and perform capabilities negotiation.
+
+ @return QMP greeting dict
+ @raise socket.error on socket connection errors
+ @raise QMPConnectError if the greeting is not received
+ @raise QMPCapabilitiesError if fails to negotiate capabilities
+ """
+ self.__sock.connect(self.__address)
+ greeting = self.__json_read()
+ if greeting is None or not greeting.has_key('QMP'):
+ raise QMPConnectError
+ # Greeting seems ok, negotiate capabilities
+ resp = self.cmd('qmp_capabilities')
+ if "return" in resp:
+ return greeting
+ raise QMPCapabilitiesError
+
+ def cmd_obj(self, qmp_cmd):
+ """
+ Send a QMP command to the QMP Monitor.
+
+ @param qmp_cmd: QMP command to be sent as a Python dict
+ @return QMP response as a Python dict or None if the connection has
+ been closed
+ """
+ try:
+ self.__sock.sendall(json.dumps(qmp_cmd))
+ except socket.error, err:
+ if err[0] == errno.EPIPE:
+ return
+ raise socket.error(err)
+ return self.__json_read()
+
+ def cmd(self, name, args=None, id=None):
+ """
+ Build a QMP command and send it to the QMP Monitor.
+
+ @param name: command name (string)
+ @param args: command arguments (dict)
+ @param id: command id (dict, list, string or int)
+ """
+ qmp_cmd = { 'execute': name }
+ if args:
+ qmp_cmd['arguments'] = args
+ if id:
+ qmp_cmd['id'] = id
+ return self.cmd_obj(qmp_cmd)
+
+ def get_events(self):
+ """
+ Get a list of available QMP events.
+ """
+ self.__sock.setblocking(0)
+ try:
+ self.__json_read()
+ except socket.error, err:
+ if err[0] == errno.EAGAIN:
+ # No data available
+ pass
+ self.__sock.setblocking(1)
+ return self.__events
+
+ def clear_events(self):
+ """
+ Clear current list of pending events.
+ """
+ self.__events = []
+
+ def close(self):
+ self.__sock.close()
+ self.__sockfile.close()
diff --git a/README b/README
new file mode 100644
index 0000000..dfd56f2
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+Read the documentation in qemu-doc.html.
+
+Fabrice Bellard.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..1d4c638
--- /dev/null
+++ b/TODO
@@ -0,0 +1,37 @@
+General:
+-------
+- cycle counter for all archs
+- cpu_interrupt() win32/SMP fix
+- merge PIC spurious interrupt patch
+- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
+- config file (at least for windows/Mac OS X)
+- update doc: PCI infos.
+- basic VGA optimizations
+- better code fetch
+- do not resize vga if invalid size.
+- TLB code protection support for PPC
+- disable SMC handling for ARM/SPARC/PPC (not finished)
+- see undefined flags for BTx insn
+- keyboard output buffer filling timing emulation
+- tests for each target CPU
+- fix all remaining thread lock issues (must put TBs in a specific invalid
+ state, find a solution for tb_flush()).
+
+ppc specific:
+------------
+- TLB invalidate not needed if msr_pr changes
+- enable shift optimizations ?
+
+linux-user specific:
+-------------------
+- remove threading support as it cannot work at this point
+- improve IPC syscalls
+- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
+ issues, fix 16 bit uid issues)
+- use kernel traps for unaligned accesses on ARM ?
+
+
+lower priority:
+--------------
+- int15 ah=86: use better timing
+- use -msoft-float on ARM
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..930e300
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.14.1
diff --git a/a.out.h b/a.out.h
new file mode 100644
index 0000000..dfc104e
--- /dev/null
+++ b/a.out.h
@@ -0,0 +1,430 @@
+/* a.out.h
+
+ Copyright 1997, 1998, 1999, 2001 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _A_OUT_H_
+#define _A_OUT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define COFF_IMAGE_WITH_PE
+#define COFF_LONG_SECTION_NAMES
+
+/*** coff information for Intel 386/486. */
+
+
+/********************** FILE HEADER **********************/
+
+struct external_filehdr {
+ short f_magic; /* magic number */
+ short f_nscns; /* number of sections */
+ host_ulong f_timdat; /* time & date stamp */
+ host_ulong f_symptr; /* file pointer to symtab */
+ host_ulong f_nsyms; /* number of symtab entries */
+ short f_opthdr; /* sizeof(optional hdr) */
+ short f_flags; /* flags */
+};
+
+/* Bits for f_flags:
+ * F_RELFLG relocation info stripped from file
+ * F_EXEC file is executable (no unresolved external references)
+ * F_LNNO line numbers stripped from file
+ * F_LSYMS local symbols stripped from file
+ * F_AR32WR file has byte ordering of an AR32WR machine (e.g. vax)
+ */
+
+#define F_RELFLG (0x0001)
+#define F_EXEC (0x0002)
+#define F_LNNO (0x0004)
+#define F_LSYMS (0x0008)
+
+
+
+#define I386MAGIC 0x14c
+#define I386PTXMAGIC 0x154
+#define I386AIXMAGIC 0x175
+
+/* This is Lynx's all-platform magic number for executables. */
+
+#define LYNXCOFFMAGIC 0415
+
+#define I386BADMAG(x) (((x).f_magic != I386MAGIC) \
+ && (x).f_magic != I386AIXMAGIC \
+ && (x).f_magic != I386PTXMAGIC \
+ && (x).f_magic != LYNXCOFFMAGIC)
+
+#define FILHDR struct external_filehdr
+#define FILHSZ 20
+
+
+/********************** AOUT "OPTIONAL HEADER"=
+ **********************/
+
+
+typedef struct
+{
+ unsigned short magic; /* type of file */
+ unsigned short vstamp; /* version stamp */
+ host_ulong tsize; /* text size in bytes, padded to FW bdry*/
+ host_ulong dsize; /* initialized data " " */
+ host_ulong bsize; /* uninitialized data " " */
+ host_ulong entry; /* entry pt. */
+ host_ulong text_start; /* base of text used for this file */
+ host_ulong data_start; /* base of data used for this file=
+ */
+}
+AOUTHDR;
+
+#define AOUTSZ 28
+#define AOUTHDRSZ 28
+
+#define OMAGIC 0404 /* object files, eg as output */
+#define ZMAGIC 0413 /* demand load format, eg normal ld output */
+#define STMAGIC 0401 /* target shlib */
+#define SHMAGIC 0443 /* host shlib */
+
+
+/* define some NT default values */
+/* #define NT_IMAGE_BASE 0x400000 moved to internal.h */
+#define NT_SECTION_ALIGNMENT 0x1000
+#define NT_FILE_ALIGNMENT 0x200
+#define NT_DEF_RESERVE 0x100000
+#define NT_DEF_COMMIT 0x1000
+
+/********************** SECTION HEADER **********************/
+
+
+struct external_scnhdr {
+ char s_name[8]; /* section name */
+ host_ulong s_paddr; /* physical address, offset
+ of last addr in scn */
+ host_ulong s_vaddr; /* virtual address */
+ host_ulong s_size; /* section size */
+ host_ulong s_scnptr; /* file ptr to raw data for section */
+ host_ulong s_relptr; /* file ptr to relocation */
+ host_ulong s_lnnoptr; /* file ptr to line numbers */
+ unsigned short s_nreloc; /* number of relocation entries */
+ unsigned short s_nlnno; /* number of line number entries*/
+ host_ulong s_flags; /* flags */
+};
+
+#define SCNHDR struct external_scnhdr
+#define SCNHSZ 40
+
+/*
+ * names of "special" sections
+ */
+#define _TEXT ".text"
+#define _DATA ".data"
+#define _BSS ".bss"
+#define _COMMENT ".comment"
+#define _LIB ".lib"
+
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+struct external_lineno {
+ union {
+ host_ulong l_symndx; /* function name symbol index, iff l_lnno 0 */
+ host_ulong l_paddr; /* (physical) address of line number */
+ } l_addr;
+ unsigned short l_lnno; /* line number */
+};
+
+#define LINENO struct external_lineno
+#define LINESZ 6
+
+/********************** SYMBOLS **********************/
+
+#define E_SYMNMLEN 8 /* # characters in a symbol name */
+#define E_FILNMLEN 14 /* # characters in a file name */
+#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */
+
+struct __attribute__((packed)) external_syment
+{
+ union {
+ char e_name[E_SYMNMLEN];
+ struct {
+ host_ulong e_zeroes;
+ host_ulong e_offset;
+ } e;
+ } e;
+ host_ulong e_value;
+ unsigned short e_scnum;
+ unsigned short e_type;
+ char e_sclass[1];
+ char e_numaux[1];
+};
+
+#define N_BTMASK (0xf)
+#define N_TMASK (0x30)
+#define N_BTSHFT (4)
+#define N_TSHIFT (2)
+
+union external_auxent {
+ struct {
+ host_ulong x_tagndx; /* str, un, or enum tag indx */
+ union {
+ struct {
+ unsigned short x_lnno; /* declaration line number */
+ unsigned short x_size; /* str/union/array size */
+ } x_lnsz;
+ host_ulong x_fsize; /* size of function */
+ } x_misc;
+ union {
+ struct { /* if ISFCN, tag, or .bb */
+ host_ulong x_lnnoptr;/* ptr to fcn line # */
+ host_ulong x_endndx; /* entry ndx past block end */
+ } x_fcn;
+ struct { /* if ISARY, up to 4 dimen. */
+ char x_dimen[E_DIMNUM][2];
+ } x_ary;
+ } x_fcnary;
+ unsigned short x_tvndx; /* tv index */
+ } x_sym;
+
+ union {
+ char x_fname[E_FILNMLEN];
+ struct {
+ host_ulong x_zeroes;
+ host_ulong x_offset;
+ } x_n;
+ } x_file;
+
+ struct {
+ host_ulong x_scnlen; /* section length */
+ unsigned short x_nreloc; /* # relocation entries */
+ unsigned short x_nlinno; /* # line numbers */
+ host_ulong x_checksum; /* section COMDAT checksum */
+ unsigned short x_associated;/* COMDAT associated section index */
+ char x_comdat[1]; /* COMDAT selection number */
+ } x_scn;
+
+ struct {
+ host_ulong x_tvfill; /* tv fill value */
+ unsigned short x_tvlen; /* length of .tv */
+ char x_tvran[2][2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+
+};
+
+#define SYMENT struct external_syment
+#define SYMESZ 18
+#define AUXENT union external_auxent
+#define AUXESZ 18
+
+#define _ETEXT "etext"
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct external_reloc {
+ char r_vaddr[4];
+ char r_symndx[4];
+ char r_type[2];
+};
+
+#define RELOC struct external_reloc
+#define RELSZ 10
+
+/* end of coff/i386.h */
+
+/* PE COFF header information */
+
+#ifndef _PE_H
+#define _PE_H
+
+/* NT specific file attributes */
+#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
+#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
+#define IMAGE_FILE_32BIT_MACHINE 0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
+#define IMAGE_FILE_SYSTEM 0x1000
+#define IMAGE_FILE_DLL 0x2000
+#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
+
+/* additional flags to be set for section headers to allow the NT loader to
+ read and write to the section data (to replace the addresses of data in
+ dlls for one thing); also to execute the section in .text's case=
+ */
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+#define IMAGE_SCN_MEM_WRITE 0x80000000
+
+/*
+ * Section characteristics added for ppc-nt
+ */
+
+#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* Reserved. */
+
+#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */
+
+#define IMAGE_SCN_LNK_OTHER 0x00000100 /* Reserved. */
+#define IMAGE_SCN_LNK_INFO 0x00000200 /* Section contains comments or some other type of information. */
+#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* Section contents will not become part of image. */
+#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* Section contents comdat. */
+
+#define IMAGE_SCN_MEM_FARDATA 0x00008000
+
+#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
+#define IMAGE_SCN_MEM_16BIT 0x00020000
+#define IMAGE_SCN_MEM_LOCKED 0x00040000
+#define IMAGE_SCN_MEM_PRELOAD 0x00080000
+
+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000
+#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
+#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
+#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 /* Default alignment if no others are specified. */
+#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
+#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
+
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* Section contains extended relocations. */
+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* Section is not cachable. */
+#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* Section is not pageable. */
+#define IMAGE_SCN_MEM_SHARED 0x10000000 /* Section is shareable. */
+
+/* COMDAT selection codes. */
+
+#define IMAGE_COMDAT_SELECT_NODUPLICATES (1) /* Warn if duplicates. */
+#define IMAGE_COMDAT_SELECT_ANY (2) /* No warning. */
+#define IMAGE_COMDAT_SELECT_SAME_SIZE (3) /* Warn if different size. */
+#define IMAGE_COMDAT_SELECT_EXACT_MATCH (4) /* Warn if different. */
+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE (5) /* Base on other section. */
+
+/* Magic values that are true for all dos/nt implementations */
+#define DOSMAGIC 0x5a4d
+#define NT_SIGNATURE 0x00004550
+
+/* NT allows long filenames, we want to accommodate this. This may break
+ some of the bfd functions */
+#undef FILNMLEN
+#define FILNMLEN 18 /* # characters in a file name */
+
+
+#ifdef COFF_IMAGE_WITH_PE
+/* The filehdr is only weired in images */
+
+#undef FILHDR
+struct external_PE_filehdr
+{
+ /* DOS header fields */
+ unsigned short e_magic; /* Magic number, 0x5a4d */
+ unsigned short e_cblp; /* Bytes on last page of file, 0x90 */
+ unsigned short e_cp; /* Pages in file, 0x3 */
+ unsigned short e_crlc; /* Relocations, 0x0 */
+ unsigned short e_cparhdr; /* Size of header in paragraphs, 0x4 */
+ unsigned short e_minalloc; /* Minimum extra paragraphs needed, 0x0 */
+ unsigned short e_maxalloc; /* Maximum extra paragraphs needed, 0xFFFF */
+ unsigned short e_ss; /* Initial (relative) SS value, 0x0 */
+ unsigned short e_sp; /* Initial SP value, 0xb8 */
+ unsigned short e_csum; /* Checksum, 0x0 */
+ unsigned short e_ip; /* Initial IP value, 0x0 */
+ unsigned short e_cs; /* Initial (relative) CS value, 0x0 */
+ unsigned short e_lfarlc; /* File address of relocation table, 0x40 */
+ unsigned short e_ovno; /* Overlay number, 0x0 */
+ char e_res[4][2]; /* Reserved words, all 0x0 */
+ unsigned short e_oemid; /* OEM identifier (for e_oeminfo), 0x0 */
+ unsigned short e_oeminfo; /* OEM information; e_oemid specific, 0x0 */
+ char e_res2[10][2]; /* Reserved words, all 0x0 */
+ host_ulong e_lfanew; /* File address of new exe header, 0x80 */
+ char dos_message[16][4]; /* other stuff, always follow DOS header */
+ unsigned int nt_signature; /* required NT signature, 0x4550 */
+
+ /* From standard header */
+
+ unsigned short f_magic; /* magic number */
+ unsigned short f_nscns; /* number of sections */
+ host_ulong f_timdat; /* time & date stamp */
+ host_ulong f_symptr; /* file pointer to symtab */
+ host_ulong f_nsyms; /* number of symtab entries */
+ unsigned short f_opthdr; /* sizeof(optional hdr) */
+ unsigned short f_flags; /* flags */
+};
+
+
+#define FILHDR struct external_PE_filehdr
+#undef FILHSZ
+#define FILHSZ 152
+
+#endif
+
+typedef struct
+{
+ unsigned short magic; /* type of file */
+ unsigned short vstamp; /* version stamp */
+ host_ulong tsize; /* text size in bytes, padded to FW bdry*/
+ host_ulong dsize; /* initialized data " " */
+ host_ulong bsize; /* uninitialized data " " */
+ host_ulong entry; /* entry pt. */
+ host_ulong text_start; /* base of text used for this file */
+ host_ulong data_start; /* base of all data used for this file */
+
+ /* NT extra fields; see internal.h for descriptions */
+ host_ulong ImageBase;
+ host_ulong SectionAlignment;
+ host_ulong FileAlignment;
+ unsigned short MajorOperatingSystemVersion;
+ unsigned short MinorOperatingSystemVersion;
+ unsigned short MajorImageVersion;
+ unsigned short MinorImageVersion;
+ unsigned short MajorSubsystemVersion;
+ unsigned short MinorSubsystemVersion;
+ char Reserved1[4];
+ host_ulong SizeOfImage;
+ host_ulong SizeOfHeaders;
+ host_ulong CheckSum;
+ unsigned short Subsystem;
+ unsigned short DllCharacteristics;
+ host_ulong SizeOfStackReserve;
+ host_ulong SizeOfStackCommit;
+ host_ulong SizeOfHeapReserve;
+ host_ulong SizeOfHeapCommit;
+ host_ulong LoaderFlags;
+ host_ulong NumberOfRvaAndSizes;
+ /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
+ char DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
+
+} PEAOUTHDR;
+
+
+#undef AOUTSZ
+#define AOUTSZ (AOUTHDRSZ + 196)
+
+#undef E_FILNMLEN
+#define E_FILNMLEN 18 /* # characters in a file name */
+#endif
+
+/* end of coff/pe.h */
+
+#define DT_NON (0) /* no derived type */
+#define DT_PTR (1) /* pointer */
+#define DT_FCN (2) /* function */
+#define DT_ARY (3) /* array */
+
+#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
+#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
+#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _A_OUT_H_ */
diff --git a/acl.c b/acl.c
new file mode 100644
index 0000000..311dade
--- /dev/null
+++ b/acl.c
@@ -0,0 +1,185 @@
+/*
+ * QEMU access control list management
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "acl.h"
+
+#ifdef CONFIG_FNMATCH
+#include <fnmatch.h>
+#endif
+
+
+static unsigned int nacls = 0;
+static qemu_acl **acls = NULL;
+
+
+
+qemu_acl *qemu_acl_find(const char *aclname)
+{
+ int i;
+ for (i = 0 ; i < nacls ; i++) {
+ if (strcmp(acls[i]->aclname, aclname) == 0)
+ return acls[i];
+ }
+
+ return NULL;
+}
+
+qemu_acl *qemu_acl_init(const char *aclname)
+{
+ qemu_acl *acl;
+
+ acl = qemu_acl_find(aclname);
+ if (acl)
+ return acl;
+
+ acl = qemu_malloc(sizeof(*acl));
+ acl->aclname = qemu_strdup(aclname);
+ /* Deny by default, so there is no window of "open
+ * access" between QEMU starting, and the user setting
+ * up ACLs in the monitor */
+ acl->defaultDeny = 1;
+
+ acl->nentries = 0;
+ QTAILQ_INIT(&acl->entries);
+
+ acls = qemu_realloc(acls, sizeof(*acls) * (nacls +1));
+ acls[nacls] = acl;
+ nacls++;
+
+ return acl;
+}
+
+int qemu_acl_party_is_allowed(qemu_acl *acl,
+ const char *party)
+{
+ qemu_acl_entry *entry;
+
+ QTAILQ_FOREACH(entry, &acl->entries, next) {
+#ifdef CONFIG_FNMATCH
+ if (fnmatch(entry->match, party, 0) == 0)
+ return entry->deny ? 0 : 1;
+#else
+ /* No fnmatch, so fallback to exact string matching
+ * instead of allowing wildcards */
+ if (strcmp(entry->match, party) == 0)
+ return entry->deny ? 0 : 1;
+#endif
+ }
+
+ return acl->defaultDeny ? 0 : 1;
+}
+
+
+void qemu_acl_reset(qemu_acl *acl)
+{
+ qemu_acl_entry *entry;
+
+ /* Put back to deny by default, so there is no window
+ * of "open access" while the user re-initializes the
+ * access control list */
+ acl->defaultDeny = 1;
+ QTAILQ_FOREACH(entry, &acl->entries, next) {
+ QTAILQ_REMOVE(&acl->entries, entry, next);
+ free(entry->match);
+ free(entry);
+ }
+ acl->nentries = 0;
+}
+
+
+int qemu_acl_append(qemu_acl *acl,
+ int deny,
+ const char *match)
+{
+ qemu_acl_entry *entry;
+
+ entry = qemu_malloc(sizeof(*entry));
+ entry->match = qemu_strdup(match);
+ entry->deny = deny;
+
+ QTAILQ_INSERT_TAIL(&acl->entries, entry, next);
+ acl->nentries++;
+
+ return acl->nentries;
+}
+
+
+int qemu_acl_insert(qemu_acl *acl,
+ int deny,
+ const char *match,
+ int index)
+{
+ qemu_acl_entry *entry;
+ qemu_acl_entry *tmp;
+ int i = 0;
+
+ if (index <= 0)
+ return -1;
+ if (index >= acl->nentries)
+ return qemu_acl_append(acl, deny, match);
+
+
+ entry = qemu_malloc(sizeof(*entry));
+ entry->match = qemu_strdup(match);
+ entry->deny = deny;
+
+ QTAILQ_FOREACH(tmp, &acl->entries, next) {
+ i++;
+ if (i == index) {
+ QTAILQ_INSERT_BEFORE(tmp, entry, next);
+ acl->nentries++;
+ break;
+ }
+ }
+
+ return i;
+}
+
+int qemu_acl_remove(qemu_acl *acl,
+ const char *match)
+{
+ qemu_acl_entry *entry;
+ int i = 0;
+
+ QTAILQ_FOREACH(entry, &acl->entries, next) {
+ i++;
+ if (strcmp(entry->match, match) == 0) {
+ QTAILQ_REMOVE(&acl->entries, entry, next);
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * End:
+ */
diff --git a/acl.h b/acl.h
new file mode 100644
index 0000000..0ef7804
--- /dev/null
+++ b/acl.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU access control list management
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __QEMU_ACL_H__
+#define __QEMU_ACL_H__
+
+#include "qemu-queue.h"
+
+typedef struct qemu_acl_entry qemu_acl_entry;
+typedef struct qemu_acl qemu_acl;
+
+struct qemu_acl_entry {
+ char *match;
+ int deny;
+
+ QTAILQ_ENTRY(qemu_acl_entry) next;
+};
+
+struct qemu_acl {
+ char *aclname;
+ unsigned int nentries;
+ QTAILQ_HEAD(,qemu_acl_entry) entries;
+ int defaultDeny;
+};
+
+qemu_acl *qemu_acl_init(const char *aclname);
+
+qemu_acl *qemu_acl_find(const char *aclname);
+
+int qemu_acl_party_is_allowed(qemu_acl *acl,
+ const char *party);
+
+void qemu_acl_reset(qemu_acl *acl);
+
+int qemu_acl_append(qemu_acl *acl,
+ int deny,
+ const char *match);
+int qemu_acl_insert(qemu_acl *acl,
+ int deny,
+ const char *match,
+ int index);
+int qemu_acl_remove(qemu_acl *acl,
+ const char *match);
+
+#endif /* __QEMU_ACL_H__ */
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * End:
+ */
diff --git a/aes.c b/aes.c
new file mode 100644
index 0000000..eb37adb
--- /dev/null
+++ b/aes.c
@@ -0,0 +1,1314 @@
+/**
+ *
+ * aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project.
+ */
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "qemu-common.h"
+#include "aes.h"
+
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+/* This controls loop-unrolling in aes_core.c */
+#undef FULL_UNROLL
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key) {
+
+ u32 *rk;
+ int i = 0;
+ u32 temp;
+
+ if (!userKey || !key)
+ return -1;
+ if (bits != 128 && bits != 192 && bits != 256)
+ return -2;
+
+ rk = key->rd_key;
+
+ if (bits==128)
+ key->rounds = 10;
+ else if (bits==192)
+ key->rounds = 12;
+ else
+ key->rounds = 14;
+
+ rk[0] = GETU32(userKey );
+ rk[1] = GETU32(userKey + 4);
+ rk[2] = GETU32(userKey + 8);
+ rk[3] = GETU32(userKey + 12);
+ if (bits == 128) {
+ while (1) {
+ temp = rk[3];
+ rk[4] = rk[0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[5] = rk[1] ^ rk[4];
+ rk[6] = rk[2] ^ rk[5];
+ rk[7] = rk[3] ^ rk[6];
+ if (++i == 10) {
+ return 0;
+ }
+ rk += 4;
+ }
+ }
+ rk[4] = GETU32(userKey + 16);
+ rk[5] = GETU32(userKey + 20);
+ if (bits == 192) {
+ while (1) {
+ temp = rk[ 5];
+ rk[ 6] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 7] = rk[ 1] ^ rk[ 6];
+ rk[ 8] = rk[ 2] ^ rk[ 7];
+ rk[ 9] = rk[ 3] ^ rk[ 8];
+ if (++i == 8) {
+ return 0;
+ }
+ rk[10] = rk[ 4] ^ rk[ 9];
+ rk[11] = rk[ 5] ^ rk[10];
+ rk += 6;
+ }
+ }
+ rk[6] = GETU32(userKey + 24);
+ rk[7] = GETU32(userKey + 28);
+ if (bits == 256) {
+ while (1) {
+ temp = rk[ 7];
+ rk[ 8] = rk[ 0] ^
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp >> 24) ] & 0x000000ff) ^
+ rcon[i];
+ rk[ 9] = rk[ 1] ^ rk[ 8];
+ rk[10] = rk[ 2] ^ rk[ 9];
+ rk[11] = rk[ 3] ^ rk[10];
+ if (++i == 7) {
+ return 0;
+ }
+ temp = rk[11];
+ rk[12] = rk[ 4] ^
+ (Te4[(temp >> 24) ] & 0xff000000) ^
+ (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(temp ) & 0xff] & 0x000000ff);
+ rk[13] = rk[ 5] ^ rk[12];
+ rk[14] = rk[ 6] ^ rk[13];
+ rk[15] = rk[ 7] ^ rk[14];
+
+ rk += 8;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key) {
+
+ u32 *rk;
+ int i, j, status;
+ u32 temp;
+
+ /* first, start with an encryption schedule */
+ status = AES_set_encrypt_key(userKey, bits, key);
+ if (status < 0)
+ return status;
+
+ rk = key->rd_key;
+
+ /* invert the order of the round keys: */
+ for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+ temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+ }
+ /* apply the inverse MixColumn transform to all round keys but the first and the last: */
+ for (i = 1; i < (key->rounds); i++) {
+ rk += 4;
+ rk[0] =
+ Td0[Te4[(rk[0] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[0] ) & 0xff] & 0xff];
+ rk[1] =
+ Td0[Te4[(rk[1] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[1] ) & 0xff] & 0xff];
+ rk[2] =
+ Td0[Te4[(rk[2] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[2] ) & 0xff] & 0xff];
+ rk[3] =
+ Td0[Te4[(rk[3] >> 24) ] & 0xff] ^
+ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^
+ Td3[Te4[(rk[3] ) & 0xff] & 0xff];
+ }
+ return 0;
+}
+
+#ifndef AES_ASM
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key) {
+
+ const u32 *rk;
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ assert(in && out && key);
+ rk = key->rd_key;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(in ) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+ if (key->rounds > 10) {
+ /* round 10: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+ if (key->rounds > 12) {
+ /* round 12: */
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+ }
+ }
+ rk += key->rounds << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 =
+ Te0[(s0 >> 24) ] ^
+ Te1[(s1 >> 16) & 0xff] ^
+ Te2[(s2 >> 8) & 0xff] ^
+ Te3[(s3 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Te0[(s1 >> 24) ] ^
+ Te1[(s2 >> 16) & 0xff] ^
+ Te2[(s3 >> 8) & 0xff] ^
+ Te3[(s0 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Te0[(s2 >> 24) ] ^
+ Te1[(s3 >> 16) & 0xff] ^
+ Te2[(s0 >> 8) & 0xff] ^
+ Te3[(s1 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Te0[(s3 >> 24) ] ^
+ Te1[(s0 >> 16) & 0xff] ^
+ Te2[(s1 >> 8) & 0xff] ^
+ Te3[(s2 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Te0[(t0 >> 24) ] ^
+ Te1[(t1 >> 16) & 0xff] ^
+ Te2[(t2 >> 8) & 0xff] ^
+ Te3[(t3 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Te0[(t1 >> 24) ] ^
+ Te1[(t2 >> 16) & 0xff] ^
+ Te2[(t3 >> 8) & 0xff] ^
+ Te3[(t0 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Te0[(t2 >> 24) ] ^
+ Te1[(t3 >> 16) & 0xff] ^
+ Te2[(t0 >> 8) & 0xff] ^
+ Te3[(t1 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Te0[(t3 >> 24) ] ^
+ Te1[(t0 >> 16) & 0xff] ^
+ Te2[(t1 >> 8) & 0xff] ^
+ Te3[(t2 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Te4[(t0 >> 24) ] & 0xff000000) ^
+ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out , s0);
+ s1 =
+ (Te4[(t1 >> 24) ] & 0xff000000) ^
+ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 =
+ (Te4[(t2 >> 24) ] & 0xff000000) ^
+ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 =
+ (Te4[(t3 >> 24) ] & 0xff000000) ^
+ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Te4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key) {
+
+ const u32 *rk;
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+ int r;
+#endif /* ?FULL_UNROLL */
+
+ assert(in && out && key);
+ rk = key->rd_key;
+
+ /*
+ * map byte array block to cipher state
+ * and add initial round key:
+ */
+ s0 = GETU32(in ) ^ rk[0];
+ s1 = GETU32(in + 4) ^ rk[1];
+ s2 = GETU32(in + 8) ^ rk[2];
+ s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+ /* round 1: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+ /* round 2: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+ /* round 3: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+ /* round 4: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+ /* round 5: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+ /* round 6: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+ /* round 7: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+ /* round 8: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+ /* round 9: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+ if (key->rounds > 10) {
+ /* round 10: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+ /* round 11: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+ if (key->rounds > 12) {
+ /* round 12: */
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+ /* round 13: */
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+ }
+ }
+ rk += key->rounds << 2;
+#else /* !FULL_UNROLL */
+ /*
+ * Nr - 1 full rounds:
+ */
+ r = key->rounds >> 1;
+ for (;;) {
+ t0 =
+ Td0[(s0 >> 24) ] ^
+ Td1[(s3 >> 16) & 0xff] ^
+ Td2[(s2 >> 8) & 0xff] ^
+ Td3[(s1 ) & 0xff] ^
+ rk[4];
+ t1 =
+ Td0[(s1 >> 24) ] ^
+ Td1[(s0 >> 16) & 0xff] ^
+ Td2[(s3 >> 8) & 0xff] ^
+ Td3[(s2 ) & 0xff] ^
+ rk[5];
+ t2 =
+ Td0[(s2 >> 24) ] ^
+ Td1[(s1 >> 16) & 0xff] ^
+ Td2[(s0 >> 8) & 0xff] ^
+ Td3[(s3 ) & 0xff] ^
+ rk[6];
+ t3 =
+ Td0[(s3 >> 24) ] ^
+ Td1[(s2 >> 16) & 0xff] ^
+ Td2[(s1 >> 8) & 0xff] ^
+ Td3[(s0 ) & 0xff] ^
+ rk[7];
+
+ rk += 8;
+ if (--r == 0) {
+ break;
+ }
+
+ s0 =
+ Td0[(t0 >> 24) ] ^
+ Td1[(t3 >> 16) & 0xff] ^
+ Td2[(t2 >> 8) & 0xff] ^
+ Td3[(t1 ) & 0xff] ^
+ rk[0];
+ s1 =
+ Td0[(t1 >> 24) ] ^
+ Td1[(t0 >> 16) & 0xff] ^
+ Td2[(t3 >> 8) & 0xff] ^
+ Td3[(t2 ) & 0xff] ^
+ rk[1];
+ s2 =
+ Td0[(t2 >> 24) ] ^
+ Td1[(t1 >> 16) & 0xff] ^
+ Td2[(t0 >> 8) & 0xff] ^
+ Td3[(t3 ) & 0xff] ^
+ rk[2];
+ s3 =
+ Td0[(t3 >> 24) ] ^
+ Td1[(t2 >> 16) & 0xff] ^
+ Td2[(t1 >> 8) & 0xff] ^
+ Td3[(t0 ) & 0xff] ^
+ rk[3];
+ }
+#endif /* ?FULL_UNROLL */
+ /*
+ * apply last round and
+ * map cipher state to byte array block:
+ */
+ s0 =
+ (Td4[(t0 >> 24) ] & 0xff000000) ^
+ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t1 ) & 0xff] & 0x000000ff) ^
+ rk[0];
+ PUTU32(out , s0);
+ s1 =
+ (Td4[(t1 >> 24) ] & 0xff000000) ^
+ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t2 ) & 0xff] & 0x000000ff) ^
+ rk[1];
+ PUTU32(out + 4, s1);
+ s2 =
+ (Td4[(t2 >> 24) ] & 0xff000000) ^
+ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t3 ) & 0xff] & 0x000000ff) ^
+ rk[2];
+ PUTU32(out + 8, s2);
+ s3 =
+ (Td4[(t3 >> 24) ] & 0xff000000) ^
+ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^
+ (Td4[(t0 ) & 0xff] & 0x000000ff) ^
+ rk[3];
+ PUTU32(out + 12, s3);
+}
+
+#endif /* AES_ASM */
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ const unsigned long length, const AES_KEY *key,
+ unsigned char *ivec, const int enc)
+{
+
+ unsigned long n;
+ unsigned long len = length;
+ unsigned char tmp[AES_BLOCK_SIZE];
+
+ assert(in && out && key && ivec);
+
+ if (enc) {
+ while (len >= AES_BLOCK_SIZE) {
+ for(n=0; n < AES_BLOCK_SIZE; ++n)
+ tmp[n] = in[n] ^ ivec[n];
+ AES_encrypt(tmp, out, key);
+ memcpy(ivec, out, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (len) {
+ for(n=0; n < len; ++n)
+ tmp[n] = in[n] ^ ivec[n];
+ for(n=len; n < AES_BLOCK_SIZE; ++n)
+ tmp[n] = ivec[n];
+ AES_encrypt(tmp, tmp, key);
+ memcpy(out, tmp, AES_BLOCK_SIZE);
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+ } else {
+ while (len >= AES_BLOCK_SIZE) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(in, out, key);
+ for(n=0; n < AES_BLOCK_SIZE; ++n)
+ out[n] ^= ivec[n];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ len -= AES_BLOCK_SIZE;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ }
+ if (len) {
+ memcpy(tmp, in, AES_BLOCK_SIZE);
+ AES_decrypt(tmp, tmp, key);
+ for(n=0; n < len; ++n)
+ out[n] = tmp[n] ^ ivec[n];
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);
+ }
+ }
+}
diff --git a/aes.h b/aes.h
new file mode 100644
index 0000000..a0167eb
--- /dev/null
+++ b/aes.h
@@ -0,0 +1,26 @@
+#ifndef QEMU_AES_H
+#define QEMU_AES_H
+
+#define AES_MAXNR 14
+#define AES_BLOCK_SIZE 16
+
+struct aes_key_st {
+ uint32_t rd_key[4 *(AES_MAXNR + 1)];
+ int rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key);
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+ AES_KEY *key);
+
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key);
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+ const AES_KEY *key);
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ const unsigned long length, const AES_KEY *key,
+ unsigned char *ivec, const int enc);
+
+#endif
diff --git a/aio.c b/aio.c
new file mode 100644
index 0000000..2f08655
--- /dev/null
+++ b/aio.c
@@ -0,0 +1,230 @@
+/*
+ * QEMU aio implementation
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "block.h"
+#include "qemu-queue.h"
+#include "qemu_socket.h"
+
+typedef struct AioHandler AioHandler;
+
+/* The list of registered AIO handlers */
+static QLIST_HEAD(, AioHandler) aio_handlers;
+
+/* This is a simple lock used to protect the aio_handlers list. Specifically,
+ * it's used to ensure that no callbacks are removed while we're walking and
+ * dispatching callbacks.
+ */
+static int walking_handlers;
+
+struct AioHandler
+{
+ int fd;
+ IOHandler *io_read;
+ IOHandler *io_write;
+ AioFlushHandler *io_flush;
+ AioProcessQueue *io_process_queue;
+ int deleted;
+ void *opaque;
+ QLIST_ENTRY(AioHandler) node;
+};
+
+static AioHandler *find_aio_handler(int fd)
+{
+ AioHandler *node;
+
+ QLIST_FOREACH(node, &aio_handlers, node) {
+ if (node->fd == fd)
+ if (!node->deleted)
+ return node;
+ }
+
+ return NULL;
+}
+
+int qemu_aio_set_fd_handler(int fd,
+ IOHandler *io_read,
+ IOHandler *io_write,
+ AioFlushHandler *io_flush,
+ AioProcessQueue *io_process_queue,
+ void *opaque)
+{
+ AioHandler *node;
+
+ node = find_aio_handler(fd);
+
+ /* Are we deleting the fd handler? */
+ if (!io_read && !io_write) {
+ if (node) {
+ /* If the lock is held, just mark the node as deleted */
+ if (walking_handlers)
+ node->deleted = 1;
+ else {
+ /* Otherwise, delete it for real. We can't just mark it as
+ * deleted because deleted nodes are only cleaned up after
+ * releasing the walking_handlers lock.
+ */
+ QLIST_REMOVE(node, node);
+ qemu_free(node);
+ }
+ }
+ } else {
+ if (node == NULL) {
+ /* Alloc and insert if it's not already there */
+ node = qemu_mallocz(sizeof(AioHandler));
+ node->fd = fd;
+ QLIST_INSERT_HEAD(&aio_handlers, node, node);
+ }
+ /* Update handler with latest information */
+ node->io_read = io_read;
+ node->io_write = io_write;
+ node->io_flush = io_flush;
+ node->io_process_queue = io_process_queue;
+ node->opaque = opaque;
+ }
+
+ qemu_set_fd_handler2(fd, NULL, io_read, io_write, opaque);
+
+ return 0;
+}
+
+void qemu_aio_flush(void)
+{
+ AioHandler *node;
+ int ret;
+
+ do {
+ ret = 0;
+
+ /*
+ * If there are pending emulated aio start them now so flush
+ * will be able to return 1.
+ */
+ qemu_aio_wait();
+
+ QLIST_FOREACH(node, &aio_handlers, node) {
+ if (node->io_flush) {
+ ret |= node->io_flush(node->opaque);
+ }
+ }
+ } while (qemu_bh_poll() || ret > 0);
+}
+
+int qemu_aio_process_queue(void)
+{
+ AioHandler *node;
+ int ret = 0;
+
+ walking_handlers = 1;
+
+ QLIST_FOREACH(node, &aio_handlers, node) {
+ if (node->io_process_queue) {
+ if (node->io_process_queue(node->opaque)) {
+ ret = 1;
+ }
+ }
+ }
+
+ walking_handlers = 0;
+
+ return ret;
+}
+
+void qemu_aio_wait(void)
+{
+ int ret;
+
+ if (qemu_bh_poll())
+ return;
+
+ /*
+ * If there are callbacks left that have been queued, we need to call then.
+ * Return afterwards to avoid waiting needlessly in select().
+ */
+ if (qemu_aio_process_queue())
+ return;
+
+ do {
+ AioHandler *node;
+ fd_set rdfds, wrfds;
+ int max_fd = -1;
+
+ walking_handlers = 1;
+
+ FD_ZERO(&rdfds);
+ FD_ZERO(&wrfds);
+
+ /* fill fd sets */
+ QLIST_FOREACH(node, &aio_handlers, node) {
+ /* If there aren't pending AIO operations, don't invoke callbacks.
+ * Otherwise, if there are no AIO requests, qemu_aio_wait() would
+ * wait indefinitely.
+ */
+ if (node->io_flush && node->io_flush(node->opaque) == 0)
+ continue;
+
+ if (!node->deleted && node->io_read) {
+ FD_SET(node->fd, &rdfds);
+ max_fd = MAX(max_fd, node->fd + 1);
+ }
+ if (!node->deleted && node->io_write) {
+ FD_SET(node->fd, &wrfds);
+ max_fd = MAX(max_fd, node->fd + 1);
+ }
+ }
+
+ walking_handlers = 0;
+
+ /* No AIO operations? Get us out of here */
+ if (max_fd == -1)
+ break;
+
+ /* wait until next event */
+ ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
+ if (ret == -1 && errno == EINTR)
+ continue;
+
+ /* if we have any readable fds, dispatch event */
+ if (ret > 0) {
+ walking_handlers = 1;
+
+ /* we have to walk very carefully in case
+ * qemu_aio_set_fd_handler is called while we're walking */
+ node = QLIST_FIRST(&aio_handlers);
+ while (node) {
+ AioHandler *tmp;
+
+ if (!node->deleted &&
+ FD_ISSET(node->fd, &rdfds) &&
+ node->io_read) {
+ node->io_read(node->opaque);
+ }
+ if (!node->deleted &&
+ FD_ISSET(node->fd, &wrfds) &&
+ node->io_write) {
+ node->io_write(node->opaque);
+ }
+
+ tmp = node;
+ node = QLIST_NEXT(node, node);
+
+ if (tmp->deleted) {
+ QLIST_REMOVE(tmp, node);
+ qemu_free(tmp);
+ }
+ }
+
+ walking_handlers = 0;
+ }
+ } while (ret == 0);
+}
diff --git a/alpha-dis.c b/alpha-dis.c
new file mode 100644
index 0000000..8a2411e
--- /dev/null
+++ b/alpha-dis.c
@@ -0,0 +1,1920 @@
+/* alpha-dis.c -- Disassemble Alpha AXP instructions
+ Copyright 1996, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ Contributed by Richard Henderson <rth@tamu.edu>,
+ patterned after the PPC opcode handling written by Ian Lance Taylor.
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include "dis-asm.h"
+
+/* MAX is redefined below, so remove any previous definition. */
+#undef MAX
+
+/* The opcode table is an array of struct alpha_opcode. */
+
+struct alpha_opcode
+{
+ /* The opcode name. */
+ const char *name;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ unsigned opcode;
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ unsigned mask;
+
+ /* One bit flags for the opcode. These are primarily used to
+ indicate specific processors and environments support the
+ instructions. The defined values are listed below. */
+ unsigned flags;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ unsigned char operands[4];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+ in the order in which the disassembler should consider
+ instructions. */
+extern const struct alpha_opcode alpha_opcodes[];
+extern const unsigned alpha_num_opcodes;
+
+/* Values defined for the flags field of a struct alpha_opcode. */
+
+/* CPU Availability */
+#define AXP_OPCODE_BASE 0x0001 /* Base architecture -- all cpus. */
+#define AXP_OPCODE_EV4 0x0002 /* EV4 specific PALcode insns. */
+#define AXP_OPCODE_EV5 0x0004 /* EV5 specific PALcode insns. */
+#define AXP_OPCODE_EV6 0x0008 /* EV6 specific PALcode insns. */
+#define AXP_OPCODE_BWX 0x0100 /* Byte/word extension (amask bit 0). */
+#define AXP_OPCODE_CIX 0x0200 /* "Count" extension (amask bit 1). */
+#define AXP_OPCODE_MAX 0x0400 /* Multimedia extension (amask bit 8). */
+
+#define AXP_OPCODE_NOPAL (~(AXP_OPCODE_EV4|AXP_OPCODE_EV5|AXP_OPCODE_EV6))
+
+/* A macro to extract the major opcode from an instruction. */
+#define AXP_OP(i) (((i) >> 26) & 0x3F)
+
+/* The total number of major opcodes. */
+#define AXP_NOPS 0x40
+
+
+/* The operands table is an array of struct alpha_operand. */
+
+struct alpha_operand
+{
+ /* The number of bits in the operand. */
+ unsigned int bits : 5;
+
+ /* How far the operand is left shifted in the instruction. */
+ unsigned int shift : 5;
+
+ /* The default relocation type for this operand. */
+ signed int default_reloc : 16;
+
+ /* One bit syntax flags. */
+ unsigned int flags : 16;
+
+ /* Insertion function. This is used by the assembler. To insert an
+ operand value into an instruction, check this field.
+
+ If it is NULL, execute
+ i |= (op & ((1 << o->bits) - 1)) << o->shift;
+ (i is the instruction which we are filling in, o is a pointer to
+ this structure, and op is the opcode value; this assumes twos
+ complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction and the operand value. It will return the new value
+ of the instruction. If the ERRMSG argument is not NULL, then if
+ the operand value is illegal, *ERRMSG will be set to a warning
+ string (the operand will be inserted in any case). If the
+ operand value is legal, *ERRMSG will be unchanged (most operands
+ can accept any value). */
+ unsigned (*insert) (unsigned instruction, int op,
+ const char **errmsg);
+
+ /* Extraction function. This is used by the disassembler. To
+ extract this operand type from an instruction, check this field.
+
+ If it is NULL, compute
+ op = ((i) >> o->shift) & ((1 << o->bits) - 1);
+ if ((o->flags & AXP_OPERAND_SIGNED) != 0
+ && (op & (1 << (o->bits - 1))) != 0)
+ op -= 1 << o->bits;
+ (i is the instruction, o is a pointer to this structure, and op
+ is the result; this assumes twos complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction value. It will return the value of the operand. If
+ the INVALID argument is not NULL, *INVALID will be set to
+ non-zero if this operand type can not actually be extracted from
+ this operand (i.e., the instruction does not match). If the
+ operand is valid, *INVALID will not be changed. */
+ int (*extract) (unsigned instruction, int *invalid);
+};
+
+/* Elements in the table are retrieved by indexing with values from
+ the operands field of the alpha_opcodes table. */
+
+extern const struct alpha_operand alpha_operands[];
+extern const unsigned alpha_num_operands;
+
+/* Values defined for the flags field of a struct alpha_operand. */
+
+/* Mask for selecting the type for typecheck purposes */
+#define AXP_OPERAND_TYPECHECK_MASK \
+ (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA | AXP_OPERAND_IR | \
+ AXP_OPERAND_FPR | AXP_OPERAND_RELATIVE | AXP_OPERAND_SIGNED | \
+ AXP_OPERAND_UNSIGNED)
+
+/* This operand does not actually exist in the assembler input. This
+ is used to support extended mnemonics, for which two operands fields
+ are identical. The assembler should call the insert function with
+ any op value. The disassembler should call the extract function,
+ ignore the return value, and check the value placed in the invalid
+ argument. */
+#define AXP_OPERAND_FAKE 01
+
+/* The operand should be wrapped in parentheses rather than separated
+ from the previous by a comma. This is used for the load and store
+ instructions which want their operands to look like "Ra,disp(Rb)". */
+#define AXP_OPERAND_PARENS 02
+
+/* Used in combination with PARENS, this suppresses the suppression of
+ the comma. This is used for "jmp Ra,(Rb),hint". */
+#define AXP_OPERAND_COMMA 04
+
+/* This operand names an integer register. */
+#define AXP_OPERAND_IR 010
+
+/* This operand names a floating point register. */
+#define AXP_OPERAND_FPR 020
+
+/* This operand is a relative branch displacement. The disassembler
+ prints these symbolically if possible. */
+#define AXP_OPERAND_RELATIVE 040
+
+/* This operand takes signed values. */
+#define AXP_OPERAND_SIGNED 0100
+
+/* This operand takes unsigned values. This exists primarily so that
+ a flags value of 0 can be treated as end-of-arguments. */
+#define AXP_OPERAND_UNSIGNED 0200
+
+/* Suppress overflow detection on this field. This is used for hints. */
+#define AXP_OPERAND_NOOVERFLOW 0400
+
+/* Mask for optional argument default value. */
+#define AXP_OPERAND_OPTIONAL_MASK 07000
+
+/* This operand defaults to zero. This is used for jump hints. */
+#define AXP_OPERAND_DEFAULT_ZERO 01000
+
+/* This operand should default to the first (real) operand and is used
+ in conjunction with AXP_OPERAND_OPTIONAL. This allows
+ "and $0,3,$0" to be written as "and $0,3", etc. I don't like
+ it, but it's what DEC does. */
+#define AXP_OPERAND_DEFAULT_FIRST 02000
+
+/* Similarly, this operand should default to the second (real) operand.
+ This allows "negl $0" instead of "negl $0,$0". */
+#define AXP_OPERAND_DEFAULT_SECOND 04000
+
+
+/* Register common names */
+
+#define AXP_REG_V0 0
+#define AXP_REG_T0 1
+#define AXP_REG_T1 2
+#define AXP_REG_T2 3
+#define AXP_REG_T3 4
+#define AXP_REG_T4 5
+#define AXP_REG_T5 6
+#define AXP_REG_T6 7
+#define AXP_REG_T7 8
+#define AXP_REG_S0 9
+#define AXP_REG_S1 10
+#define AXP_REG_S2 11
+#define AXP_REG_S3 12
+#define AXP_REG_S4 13
+#define AXP_REG_S5 14
+#define AXP_REG_FP 15
+#define AXP_REG_A0 16
+#define AXP_REG_A1 17
+#define AXP_REG_A2 18
+#define AXP_REG_A3 19
+#define AXP_REG_A4 20
+#define AXP_REG_A5 21
+#define AXP_REG_T8 22
+#define AXP_REG_T9 23
+#define AXP_REG_T10 24
+#define AXP_REG_T11 25
+#define AXP_REG_RA 26
+#define AXP_REG_PV 27
+#define AXP_REG_T12 27
+#define AXP_REG_AT 28
+#define AXP_REG_GP 29
+#define AXP_REG_SP 30
+#define AXP_REG_ZERO 31
+
+#define bfd_mach_alpha_ev4 0x10
+#define bfd_mach_alpha_ev5 0x20
+#define bfd_mach_alpha_ev6 0x30
+
+enum bfd_reloc_code_real {
+ BFD_RELOC_23_PCREL_S2,
+ BFD_RELOC_ALPHA_HINT
+};
+
+/* This file holds the Alpha AXP opcode table. The opcode table includes
+ almost all of the extended instruction mnemonics. This permits the
+ disassembler to use them, and simplifies the assembler logic, at the
+ cost of increasing the table size. The table is strictly constant
+ data, so the compiler should be able to put it in the text segment.
+
+ This file also holds the operand table. All knowledge about inserting
+ and extracting operands from instructions is kept in this file.
+
+ The information for the base instruction set was compiled from the
+ _Alpha Architecture Handbook_, Digital Order Number EC-QD2KB-TE,
+ version 2.
+
+ The information for the post-ev5 architecture extensions BWX, CIX and
+ MAX came from version 3 of this same document, which is also available
+ on-line at http://ftp.digital.com/pub/Digital/info/semiconductor
+ /literature/alphahb2.pdf
+
+ The information for the EV4 PALcode instructions was compiled from
+ _DECchip 21064 and DECchip 21064A Alpha AXP Microprocessors Hardware
+ Reference Manual_, Digital Order Number EC-Q9ZUA-TE, preliminary
+ revision dated June 1994.
+
+ The information for the EV5 PALcode instructions was compiled from
+ _Alpha 21164 Microprocessor Hardware Reference Manual_, Digital
+ Order Number EC-QAEQB-TE, preliminary revision dated April 1995. */
+
+/* Local insertion and extraction functions */
+
+static unsigned insert_rba (unsigned, int, const char **);
+static unsigned insert_rca (unsigned, int, const char **);
+static unsigned insert_za (unsigned, int, const char **);
+static unsigned insert_zb (unsigned, int, const char **);
+static unsigned insert_zc (unsigned, int, const char **);
+static unsigned insert_bdisp (unsigned, int, const char **);
+static unsigned insert_jhint (unsigned, int, const char **);
+static unsigned insert_ev6hwjhint (unsigned, int, const char **);
+
+static int extract_rba (unsigned, int *);
+static int extract_rca (unsigned, int *);
+static int extract_za (unsigned, int *);
+static int extract_zb (unsigned, int *);
+static int extract_zc (unsigned, int *);
+static int extract_bdisp (unsigned, int *);
+static int extract_jhint (unsigned, int *);
+static int extract_ev6hwjhint (unsigned, int *);
+
+
+/* The operands table */
+
+const struct alpha_operand alpha_operands[] =
+{
+ /* The fields are bits, shift, insert, extract, flags */
+ /* The zero index is used to indicate end-of-list */
+#define UNUSED 0
+ { 0, 0, 0, 0, 0, 0 },
+
+ /* The plain integer register fields */
+#define RA (UNUSED + 1)
+ { 5, 21, 0, AXP_OPERAND_IR, 0, 0 },
+#define RB (RA + 1)
+ { 5, 16, 0, AXP_OPERAND_IR, 0, 0 },
+#define RC (RB + 1)
+ { 5, 0, 0, AXP_OPERAND_IR, 0, 0 },
+
+ /* The plain fp register fields */
+#define FA (RC + 1)
+ { 5, 21, 0, AXP_OPERAND_FPR, 0, 0 },
+#define FB (FA + 1)
+ { 5, 16, 0, AXP_OPERAND_FPR, 0, 0 },
+#define FC (FB + 1)
+ { 5, 0, 0, AXP_OPERAND_FPR, 0, 0 },
+
+ /* The integer registers when they are ZERO */
+#define ZA (FC + 1)
+ { 5, 21, 0, AXP_OPERAND_FAKE, insert_za, extract_za },
+#define ZB (ZA + 1)
+ { 5, 16, 0, AXP_OPERAND_FAKE, insert_zb, extract_zb },
+#define ZC (ZB + 1)
+ { 5, 0, 0, AXP_OPERAND_FAKE, insert_zc, extract_zc },
+
+ /* The RB field when it needs parentheses */
+#define PRB (ZC + 1)
+ { 5, 16, 0, AXP_OPERAND_IR|AXP_OPERAND_PARENS, 0, 0 },
+
+ /* The RB field when it needs parentheses _and_ a preceding comma */
+#define CPRB (PRB + 1)
+ { 5, 16, 0,
+ AXP_OPERAND_IR|AXP_OPERAND_PARENS|AXP_OPERAND_COMMA, 0, 0 },
+
+ /* The RB field when it must be the same as the RA field */
+#define RBA (CPRB + 1)
+ { 5, 16, 0, AXP_OPERAND_FAKE, insert_rba, extract_rba },
+
+ /* The RC field when it must be the same as the RB field */
+#define RCA (RBA + 1)
+ { 5, 0, 0, AXP_OPERAND_FAKE, insert_rca, extract_rca },
+
+ /* The RC field when it can *default* to RA */
+#define DRC1 (RCA + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_IR|AXP_OPERAND_DEFAULT_FIRST, 0, 0 },
+
+ /* The RC field when it can *default* to RB */
+#define DRC2 (DRC1 + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_IR|AXP_OPERAND_DEFAULT_SECOND, 0, 0 },
+
+ /* The FC field when it can *default* to RA */
+#define DFC1 (DRC2 + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_FPR|AXP_OPERAND_DEFAULT_FIRST, 0, 0 },
+
+ /* The FC field when it can *default* to RB */
+#define DFC2 (DFC1 + 1)
+ { 5, 0, 0,
+ AXP_OPERAND_FPR|AXP_OPERAND_DEFAULT_SECOND, 0, 0 },
+
+ /* The unsigned 8-bit literal of Operate format insns */
+#define LIT (DFC2 + 1)
+ { 8, 13, -LIT, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The signed 16-bit displacement of Memory format insns. From here
+ we can't tell what relocation should be used, so don't use a default. */
+#define MDISP (LIT + 1)
+ { 16, 0, -MDISP, AXP_OPERAND_SIGNED, 0, 0 },
+
+ /* The signed "23-bit" aligned displacement of Branch format insns */
+#define BDISP (MDISP + 1)
+ { 21, 0, BFD_RELOC_23_PCREL_S2,
+ AXP_OPERAND_RELATIVE, insert_bdisp, extract_bdisp },
+
+ /* The 26-bit PALcode function */
+#define PALFN (BDISP + 1)
+ { 26, 0, -PALFN, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The optional signed "16-bit" aligned displacement of the JMP/JSR hint */
+#define JMPHINT (PALFN + 1)
+ { 14, 0, BFD_RELOC_ALPHA_HINT,
+ AXP_OPERAND_RELATIVE|AXP_OPERAND_DEFAULT_ZERO|AXP_OPERAND_NOOVERFLOW,
+ insert_jhint, extract_jhint },
+
+ /* The optional hint to RET/JSR_COROUTINE */
+#define RETHINT (JMPHINT + 1)
+ { 14, 0, -RETHINT,
+ AXP_OPERAND_UNSIGNED|AXP_OPERAND_DEFAULT_ZERO, 0, 0 },
+
+ /* The 12-bit displacement for the ev[46] hw_{ld,st} (pal1b/pal1f) insns */
+#define EV4HWDISP (RETHINT + 1)
+#define EV6HWDISP (EV4HWDISP)
+ { 12, 0, -EV4HWDISP, AXP_OPERAND_SIGNED, 0, 0 },
+
+ /* The 5-bit index for the ev4 hw_m[ft]pr (pal19/pal1d) insns */
+#define EV4HWINDEX (EV4HWDISP + 1)
+ { 5, 0, -EV4HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 8-bit index for the oddly unqualified hw_m[tf]pr insns
+ that occur in DEC PALcode. */
+#define EV4EXTHWINDEX (EV4HWINDEX + 1)
+ { 8, 0, -EV4EXTHWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 10-bit displacement for the ev5 hw_{ld,st} (pal1b/pal1f) insns */
+#define EV5HWDISP (EV4EXTHWINDEX + 1)
+ { 10, 0, -EV5HWDISP, AXP_OPERAND_SIGNED, 0, 0 },
+
+ /* The 16-bit index for the ev5 hw_m[ft]pr (pal19/pal1d) insns */
+#define EV5HWINDEX (EV5HWDISP + 1)
+ { 16, 0, -EV5HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 16-bit combined index/scoreboard mask for the ev6
+ hw_m[ft]pr (pal19/pal1d) insns */
+#define EV6HWINDEX (EV5HWINDEX + 1)
+ { 16, 0, -EV6HWINDEX, AXP_OPERAND_UNSIGNED, 0, 0 },
+
+ /* The 13-bit branch hint for the ev6 hw_jmp/jsr (pal1e) insn */
+#define EV6HWJMPHINT (EV6HWINDEX+ 1)
+ { 8, 0, -EV6HWJMPHINT,
+ AXP_OPERAND_RELATIVE|AXP_OPERAND_DEFAULT_ZERO|AXP_OPERAND_NOOVERFLOW,
+ insert_ev6hwjhint, extract_ev6hwjhint }
+};
+
+const unsigned alpha_num_operands = sizeof(alpha_operands)/sizeof(*alpha_operands);
+
+/* The RB field when it is the same as the RA field in the same insn.
+ This operand is marked fake. The insertion function just copies
+ the RA field into the RB field, and the extraction function just
+ checks that the fields are the same. */
+
+/*ARGSUSED*/
+static unsigned
+insert_rba(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static int
+extract_rba(unsigned insn, int *invalid)
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+
+/* The same for the RC field */
+
+/*ARGSUSED*/
+static unsigned
+insert_rca(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | ((insn >> 21) & 0x1f);
+}
+
+static int
+extract_rca(unsigned insn, int *invalid)
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != (insn & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+
+/* Fake arguments in which the registers must be set to ZERO */
+
+/*ARGSUSED*/
+static unsigned
+insert_za(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (31 << 21);
+}
+
+static int
+extract_za(unsigned insn, int *invalid)
+{
+ if (invalid != (int *) NULL && ((insn >> 21) & 0x1f) != 31)
+ *invalid = 1;
+ return 0;
+}
+
+/*ARGSUSED*/
+static unsigned
+insert_zb(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (31 << 16);
+}
+
+static int
+extract_zb(unsigned insn, int *invalid)
+{
+ if (invalid != (int *) NULL && ((insn >> 16) & 0x1f) != 31)
+ *invalid = 1;
+ return 0;
+}
+
+/*ARGSUSED*/
+static unsigned
+insert_zc(unsigned insn, int value ATTRIBUTE_UNUSED, const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | 31;
+}
+
+static int
+extract_zc(unsigned insn, int *invalid)
+{
+ if (invalid != (int *) NULL && (insn & 0x1f) != 31)
+ *invalid = 1;
+ return 0;
+}
+
+
+/* The displacement field of a Branch format insn. */
+
+static unsigned
+insert_bdisp(unsigned insn, int value, const char **errmsg)
+{
+ if (errmsg != (const char **)NULL && (value & 3))
+ *errmsg = _("branch operand unaligned");
+ return insn | ((value / 4) & 0x1FFFFF);
+}
+
+/*ARGSUSED*/
+static int
+extract_bdisp(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
+{
+ return 4 * (((insn & 0x1FFFFF) ^ 0x100000) - 0x100000);
+}
+
+
+/* The hint field of a JMP/JSR insn. */
+
+static unsigned
+insert_jhint(unsigned insn, int value, const char **errmsg)
+{
+ if (errmsg != (const char **)NULL && (value & 3))
+ *errmsg = _("jump hint unaligned");
+ return insn | ((value / 4) & 0x3FFF);
+}
+
+/*ARGSUSED*/
+static int
+extract_jhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
+{
+ return 4 * (((insn & 0x3FFF) ^ 0x2000) - 0x2000);
+}
+
+/* The hint field of an EV6 HW_JMP/JSR insn. */
+
+static unsigned
+insert_ev6hwjhint(unsigned insn, int value, const char **errmsg)
+{
+ if (errmsg != (const char **)NULL && (value & 3))
+ *errmsg = _("jump hint unaligned");
+ return insn | ((value / 4) & 0x1FFF);
+}
+
+/*ARGSUSED*/
+static int
+extract_ev6hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED)
+{
+ return 4 * (((insn & 0x1FFF) ^ 0x1000) - 0x1000);
+}
+
+
+/* Macros used to form opcodes */
+
+/* The main opcode */
+#define OP(x) (((x) & 0x3F) << 26)
+#define OP_MASK 0xFC000000
+
+/* Branch format instructions */
+#define BRA_(oo) OP(oo)
+#define BRA_MASK OP_MASK
+#define BRA(oo) BRA_(oo), BRA_MASK
+
+/* Floating point format instructions */
+#define FP_(oo,fff) (OP(oo) | (((fff) & 0x7FF) << 5))
+#define FP_MASK (OP_MASK | 0xFFE0)
+#define FP(oo,fff) FP_(oo,fff), FP_MASK
+
+/* Memory format instructions */
+#define MEM_(oo) OP(oo)
+#define MEM_MASK OP_MASK
+#define MEM(oo) MEM_(oo), MEM_MASK
+
+/* Memory/Func Code format instructions */
+#define MFC_(oo,ffff) (OP(oo) | ((ffff) & 0xFFFF))
+#define MFC_MASK (OP_MASK | 0xFFFF)
+#define MFC(oo,ffff) MFC_(oo,ffff), MFC_MASK
+
+/* Memory/Branch format instructions */
+#define MBR_(oo,h) (OP(oo) | (((h) & 3) << 14))
+#define MBR_MASK (OP_MASK | 0xC000)
+#define MBR(oo,h) MBR_(oo,h), MBR_MASK
+
+/* Operate format instructions. The OPRL variant specifies a
+ literal second argument. */
+#define OPR_(oo,ff) (OP(oo) | (((ff) & 0x7F) << 5))
+#define OPRL_(oo,ff) (OPR_((oo),(ff)) | 0x1000)
+#define OPR_MASK (OP_MASK | 0x1FE0)
+#define OPR(oo,ff) OPR_(oo,ff), OPR_MASK
+#define OPRL(oo,ff) OPRL_(oo,ff), OPR_MASK
+
+/* Generic PALcode format instructions */
+#define PCD_(oo) OP(oo)
+#define PCD_MASK OP_MASK
+#define PCD(oo) PCD_(oo), PCD_MASK
+
+/* Specific PALcode instructions */
+#define SPCD_(oo,ffff) (OP(oo) | ((ffff) & 0x3FFFFFF))
+#define SPCD_MASK 0xFFFFFFFF
+#define SPCD(oo,ffff) SPCD_(oo,ffff), SPCD_MASK
+
+/* Hardware memory (hw_{ld,st}) instructions */
+#define EV4HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12))
+#define EV4HWMEM_MASK (OP_MASK | 0xF000)
+#define EV4HWMEM(oo,f) EV4HWMEM_(oo,f), EV4HWMEM_MASK
+
+#define EV5HWMEM_(oo,f) (OP(oo) | (((f) & 0x3F) << 10))
+#define EV5HWMEM_MASK (OP_MASK | 0xF800)
+#define EV5HWMEM(oo,f) EV5HWMEM_(oo,f), EV5HWMEM_MASK
+
+#define EV6HWMEM_(oo,f) (OP(oo) | (((f) & 0xF) << 12))
+#define EV6HWMEM_MASK (OP_MASK | 0xF000)
+#define EV6HWMEM(oo,f) EV6HWMEM_(oo,f), EV6HWMEM_MASK
+
+#define EV6HWMBR_(oo,h) (OP(oo) | (((h) & 7) << 13))
+#define EV6HWMBR_MASK (OP_MASK | 0xE000)
+#define EV6HWMBR(oo,h) EV6HWMBR_(oo,h), EV6HWMBR_MASK
+
+/* Abbreviations for instruction subsets. */
+#define BASE AXP_OPCODE_BASE
+#define EV4 AXP_OPCODE_EV4
+#define EV5 AXP_OPCODE_EV5
+#define EV6 AXP_OPCODE_EV6
+#define BWX AXP_OPCODE_BWX
+#define CIX AXP_OPCODE_CIX
+#define MAX AXP_OPCODE_MAX
+
+/* Common combinations of arguments */
+#define ARG_NONE { 0 }
+#define ARG_BRA { RA, BDISP }
+#define ARG_FBRA { FA, BDISP }
+#define ARG_FP { FA, FB, DFC1 }
+#define ARG_FPZ1 { ZA, FB, DFC1 }
+#define ARG_MEM { RA, MDISP, PRB }
+#define ARG_FMEM { FA, MDISP, PRB }
+#define ARG_OPR { RA, RB, DRC1 }
+#define ARG_OPRL { RA, LIT, DRC1 }
+#define ARG_OPRZ1 { ZA, RB, DRC1 }
+#define ARG_OPRLZ1 { ZA, LIT, RC }
+#define ARG_PCD { PALFN }
+#define ARG_EV4HWMEM { RA, EV4HWDISP, PRB }
+#define ARG_EV4HWMPR { RA, RBA, EV4HWINDEX }
+#define ARG_EV5HWMEM { RA, EV5HWDISP, PRB }
+#define ARG_EV6HWMEM { RA, EV6HWDISP, PRB }
+
+/* The opcode table.
+
+ The format of the opcode table is:
+
+ NAME OPCODE MASK { OPERANDS }
+
+ NAME is the name of the instruction.
+
+ OPCODE is the instruction opcode.
+
+ MASK is the opcode mask; this is used to tell the disassembler
+ which bits in the actual opcode must match OPCODE.
+
+ OPERANDS is the list of operands.
+
+ The preceding macros merge the text of the OPCODE and MASK fields.
+
+ The disassembler reads the table in order and prints the first
+ instruction which matches, so this table is sorted to put more
+ specific instructions before more general instructions.
+
+ Otherwise, it is sorted by major opcode and minor function code.
+
+ There are three classes of not-really-instructions in this table:
+
+ ALIAS is another name for another instruction. Some of
+ these come from the Architecture Handbook, some
+ come from the original gas opcode tables. In all
+ cases, the functionality of the opcode is unchanged.
+
+ PSEUDO a stylized code form endorsed by Chapter A.4 of the
+ Architecture Handbook.
+
+ EXTRA a stylized code form found in the original gas tables.
+
+ And two annotations:
+
+ EV56 BUT opcodes that are officially introduced as of the ev56,
+ but with defined results on previous implementations.
+
+ EV56 UNA opcodes that were introduced as of the ev56 with
+ presumably undefined results on previous implementations
+ that were not assigned to a particular extension.
+*/
+
+const struct alpha_opcode alpha_opcodes[] = {
+ { "halt", SPCD(0x00,0x0000), BASE, ARG_NONE },
+ { "draina", SPCD(0x00,0x0002), BASE, ARG_NONE },
+ { "bpt", SPCD(0x00,0x0080), BASE, ARG_NONE },
+ { "bugchk", SPCD(0x00,0x0081), BASE, ARG_NONE },
+ { "callsys", SPCD(0x00,0x0083), BASE, ARG_NONE },
+ { "chmk", SPCD(0x00,0x0083), BASE, ARG_NONE },
+ { "imb", SPCD(0x00,0x0086), BASE, ARG_NONE },
+ { "rduniq", SPCD(0x00,0x009e), BASE, ARG_NONE },
+ { "wruniq", SPCD(0x00,0x009f), BASE, ARG_NONE },
+ { "gentrap", SPCD(0x00,0x00aa), BASE, ARG_NONE },
+ { "call_pal", PCD(0x00), BASE, ARG_PCD },
+ { "pal", PCD(0x00), BASE, ARG_PCD }, /* alias */
+
+ { "lda", MEM(0x08), BASE, { RA, MDISP, ZB } }, /* pseudo */
+ { "lda", MEM(0x08), BASE, ARG_MEM },
+ { "ldah", MEM(0x09), BASE, { RA, MDISP, ZB } }, /* pseudo */
+ { "ldah", MEM(0x09), BASE, ARG_MEM },
+ { "ldbu", MEM(0x0A), BWX, ARG_MEM },
+ { "unop", MEM_(0x0B) | (30 << 16),
+ MEM_MASK, BASE, { ZA } }, /* pseudo */
+ { "ldq_u", MEM(0x0B), BASE, ARG_MEM },
+ { "ldwu", MEM(0x0C), BWX, ARG_MEM },
+ { "stw", MEM(0x0D), BWX, ARG_MEM },
+ { "stb", MEM(0x0E), BWX, ARG_MEM },
+ { "stq_u", MEM(0x0F), BASE, ARG_MEM },
+
+ { "sextl", OPR(0x10,0x00), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "sextl", OPRL(0x10,0x00), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "addl", OPR(0x10,0x00), BASE, ARG_OPR },
+ { "addl", OPRL(0x10,0x00), BASE, ARG_OPRL },
+ { "s4addl", OPR(0x10,0x02), BASE, ARG_OPR },
+ { "s4addl", OPRL(0x10,0x02), BASE, ARG_OPRL },
+ { "negl", OPR(0x10,0x09), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negl", OPRL(0x10,0x09), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subl", OPR(0x10,0x09), BASE, ARG_OPR },
+ { "subl", OPRL(0x10,0x09), BASE, ARG_OPRL },
+ { "s4subl", OPR(0x10,0x0B), BASE, ARG_OPR },
+ { "s4subl", OPRL(0x10,0x0B), BASE, ARG_OPRL },
+ { "cmpbge", OPR(0x10,0x0F), BASE, ARG_OPR },
+ { "cmpbge", OPRL(0x10,0x0F), BASE, ARG_OPRL },
+ { "s8addl", OPR(0x10,0x12), BASE, ARG_OPR },
+ { "s8addl", OPRL(0x10,0x12), BASE, ARG_OPRL },
+ { "s8subl", OPR(0x10,0x1B), BASE, ARG_OPR },
+ { "s8subl", OPRL(0x10,0x1B), BASE, ARG_OPRL },
+ { "cmpult", OPR(0x10,0x1D), BASE, ARG_OPR },
+ { "cmpult", OPRL(0x10,0x1D), BASE, ARG_OPRL },
+ { "addq", OPR(0x10,0x20), BASE, ARG_OPR },
+ { "addq", OPRL(0x10,0x20), BASE, ARG_OPRL },
+ { "s4addq", OPR(0x10,0x22), BASE, ARG_OPR },
+ { "s4addq", OPRL(0x10,0x22), BASE, ARG_OPRL },
+ { "negq", OPR(0x10,0x29), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negq", OPRL(0x10,0x29), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subq", OPR(0x10,0x29), BASE, ARG_OPR },
+ { "subq", OPRL(0x10,0x29), BASE, ARG_OPRL },
+ { "s4subq", OPR(0x10,0x2B), BASE, ARG_OPR },
+ { "s4subq", OPRL(0x10,0x2B), BASE, ARG_OPRL },
+ { "cmpeq", OPR(0x10,0x2D), BASE, ARG_OPR },
+ { "cmpeq", OPRL(0x10,0x2D), BASE, ARG_OPRL },
+ { "s8addq", OPR(0x10,0x32), BASE, ARG_OPR },
+ { "s8addq", OPRL(0x10,0x32), BASE, ARG_OPRL },
+ { "s8subq", OPR(0x10,0x3B), BASE, ARG_OPR },
+ { "s8subq", OPRL(0x10,0x3B), BASE, ARG_OPRL },
+ { "cmpule", OPR(0x10,0x3D), BASE, ARG_OPR },
+ { "cmpule", OPRL(0x10,0x3D), BASE, ARG_OPRL },
+ { "addl/v", OPR(0x10,0x40), BASE, ARG_OPR },
+ { "addl/v", OPRL(0x10,0x40), BASE, ARG_OPRL },
+ { "negl/v", OPR(0x10,0x49), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negl/v", OPRL(0x10,0x49), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subl/v", OPR(0x10,0x49), BASE, ARG_OPR },
+ { "subl/v", OPRL(0x10,0x49), BASE, ARG_OPRL },
+ { "cmplt", OPR(0x10,0x4D), BASE, ARG_OPR },
+ { "cmplt", OPRL(0x10,0x4D), BASE, ARG_OPRL },
+ { "addq/v", OPR(0x10,0x60), BASE, ARG_OPR },
+ { "addq/v", OPRL(0x10,0x60), BASE, ARG_OPRL },
+ { "negq/v", OPR(0x10,0x69), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "negq/v", OPRL(0x10,0x69), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "subq/v", OPR(0x10,0x69), BASE, ARG_OPR },
+ { "subq/v", OPRL(0x10,0x69), BASE, ARG_OPRL },
+ { "cmple", OPR(0x10,0x6D), BASE, ARG_OPR },
+ { "cmple", OPRL(0x10,0x6D), BASE, ARG_OPRL },
+
+ { "and", OPR(0x11,0x00), BASE, ARG_OPR },
+ { "and", OPRL(0x11,0x00), BASE, ARG_OPRL },
+ { "andnot", OPR(0x11,0x08), BASE, ARG_OPR }, /* alias */
+ { "andnot", OPRL(0x11,0x08), BASE, ARG_OPRL }, /* alias */
+ { "bic", OPR(0x11,0x08), BASE, ARG_OPR },
+ { "bic", OPRL(0x11,0x08), BASE, ARG_OPRL },
+ { "cmovlbs", OPR(0x11,0x14), BASE, ARG_OPR },
+ { "cmovlbs", OPRL(0x11,0x14), BASE, ARG_OPRL },
+ { "cmovlbc", OPR(0x11,0x16), BASE, ARG_OPR },
+ { "cmovlbc", OPRL(0x11,0x16), BASE, ARG_OPRL },
+ { "nop", OPR(0x11,0x20), BASE, { ZA, ZB, ZC } }, /* pseudo */
+ { "clr", OPR(0x11,0x20), BASE, { ZA, ZB, RC } }, /* pseudo */
+ { "mov", OPR(0x11,0x20), BASE, { ZA, RB, RC } }, /* pseudo */
+ { "mov", OPR(0x11,0x20), BASE, { RA, RBA, RC } }, /* pseudo */
+ { "mov", OPRL(0x11,0x20), BASE, { ZA, LIT, RC } }, /* pseudo */
+ { "or", OPR(0x11,0x20), BASE, ARG_OPR }, /* alias */
+ { "or", OPRL(0x11,0x20), BASE, ARG_OPRL }, /* alias */
+ { "bis", OPR(0x11,0x20), BASE, ARG_OPR },
+ { "bis", OPRL(0x11,0x20), BASE, ARG_OPRL },
+ { "cmoveq", OPR(0x11,0x24), BASE, ARG_OPR },
+ { "cmoveq", OPRL(0x11,0x24), BASE, ARG_OPRL },
+ { "cmovne", OPR(0x11,0x26), BASE, ARG_OPR },
+ { "cmovne", OPRL(0x11,0x26), BASE, ARG_OPRL },
+ { "not", OPR(0x11,0x28), BASE, ARG_OPRZ1 }, /* pseudo */
+ { "not", OPRL(0x11,0x28), BASE, ARG_OPRLZ1 }, /* pseudo */
+ { "ornot", OPR(0x11,0x28), BASE, ARG_OPR },
+ { "ornot", OPRL(0x11,0x28), BASE, ARG_OPRL },
+ { "xor", OPR(0x11,0x40), BASE, ARG_OPR },
+ { "xor", OPRL(0x11,0x40), BASE, ARG_OPRL },
+ { "cmovlt", OPR(0x11,0x44), BASE, ARG_OPR },
+ { "cmovlt", OPRL(0x11,0x44), BASE, ARG_OPRL },
+ { "cmovge", OPR(0x11,0x46), BASE, ARG_OPR },
+ { "cmovge", OPRL(0x11,0x46), BASE, ARG_OPRL },
+ { "eqv", OPR(0x11,0x48), BASE, ARG_OPR },
+ { "eqv", OPRL(0x11,0x48), BASE, ARG_OPRL },
+ { "xornot", OPR(0x11,0x48), BASE, ARG_OPR }, /* alias */
+ { "xornot", OPRL(0x11,0x48), BASE, ARG_OPRL }, /* alias */
+ { "amask", OPR(0x11,0x61), BASE, ARG_OPRZ1 }, /* ev56 but */
+ { "amask", OPRL(0x11,0x61), BASE, ARG_OPRLZ1 }, /* ev56 but */
+ { "cmovle", OPR(0x11,0x64), BASE, ARG_OPR },
+ { "cmovle", OPRL(0x11,0x64), BASE, ARG_OPRL },
+ { "cmovgt", OPR(0x11,0x66), BASE, ARG_OPR },
+ { "cmovgt", OPRL(0x11,0x66), BASE, ARG_OPRL },
+ { "implver", OPRL_(0x11,0x6C)|(31<<21)|(1<<13),
+ 0xFFFFFFE0, BASE, { RC } }, /* ev56 but */
+
+ { "mskbl", OPR(0x12,0x02), BASE, ARG_OPR },
+ { "mskbl", OPRL(0x12,0x02), BASE, ARG_OPRL },
+ { "extbl", OPR(0x12,0x06), BASE, ARG_OPR },
+ { "extbl", OPRL(0x12,0x06), BASE, ARG_OPRL },
+ { "insbl", OPR(0x12,0x0B), BASE, ARG_OPR },
+ { "insbl", OPRL(0x12,0x0B), BASE, ARG_OPRL },
+ { "mskwl", OPR(0x12,0x12), BASE, ARG_OPR },
+ { "mskwl", OPRL(0x12,0x12), BASE, ARG_OPRL },
+ { "extwl", OPR(0x12,0x16), BASE, ARG_OPR },
+ { "extwl", OPRL(0x12,0x16), BASE, ARG_OPRL },
+ { "inswl", OPR(0x12,0x1B), BASE, ARG_OPR },
+ { "inswl", OPRL(0x12,0x1B), BASE, ARG_OPRL },
+ { "mskll", OPR(0x12,0x22), BASE, ARG_OPR },
+ { "mskll", OPRL(0x12,0x22), BASE, ARG_OPRL },
+ { "extll", OPR(0x12,0x26), BASE, ARG_OPR },
+ { "extll", OPRL(0x12,0x26), BASE, ARG_OPRL },
+ { "insll", OPR(0x12,0x2B), BASE, ARG_OPR },
+ { "insll", OPRL(0x12,0x2B), BASE, ARG_OPRL },
+ { "zap", OPR(0x12,0x30), BASE, ARG_OPR },
+ { "zap", OPRL(0x12,0x30), BASE, ARG_OPRL },
+ { "zapnot", OPR(0x12,0x31), BASE, ARG_OPR },
+ { "zapnot", OPRL(0x12,0x31), BASE, ARG_OPRL },
+ { "mskql", OPR(0x12,0x32), BASE, ARG_OPR },
+ { "mskql", OPRL(0x12,0x32), BASE, ARG_OPRL },
+ { "srl", OPR(0x12,0x34), BASE, ARG_OPR },
+ { "srl", OPRL(0x12,0x34), BASE, ARG_OPRL },
+ { "extql", OPR(0x12,0x36), BASE, ARG_OPR },
+ { "extql", OPRL(0x12,0x36), BASE, ARG_OPRL },
+ { "sll", OPR(0x12,0x39), BASE, ARG_OPR },
+ { "sll", OPRL(0x12,0x39), BASE, ARG_OPRL },
+ { "insql", OPR(0x12,0x3B), BASE, ARG_OPR },
+ { "insql", OPRL(0x12,0x3B), BASE, ARG_OPRL },
+ { "sra", OPR(0x12,0x3C), BASE, ARG_OPR },
+ { "sra", OPRL(0x12,0x3C), BASE, ARG_OPRL },
+ { "mskwh", OPR(0x12,0x52), BASE, ARG_OPR },
+ { "mskwh", OPRL(0x12,0x52), BASE, ARG_OPRL },
+ { "inswh", OPR(0x12,0x57), BASE, ARG_OPR },
+ { "inswh", OPRL(0x12,0x57), BASE, ARG_OPRL },
+ { "extwh", OPR(0x12,0x5A), BASE, ARG_OPR },
+ { "extwh", OPRL(0x12,0x5A), BASE, ARG_OPRL },
+ { "msklh", OPR(0x12,0x62), BASE, ARG_OPR },
+ { "msklh", OPRL(0x12,0x62), BASE, ARG_OPRL },
+ { "inslh", OPR(0x12,0x67), BASE, ARG_OPR },
+ { "inslh", OPRL(0x12,0x67), BASE, ARG_OPRL },
+ { "extlh", OPR(0x12,0x6A), BASE, ARG_OPR },
+ { "extlh", OPRL(0x12,0x6A), BASE, ARG_OPRL },
+ { "mskqh", OPR(0x12,0x72), BASE, ARG_OPR },
+ { "mskqh", OPRL(0x12,0x72), BASE, ARG_OPRL },
+ { "insqh", OPR(0x12,0x77), BASE, ARG_OPR },
+ { "insqh", OPRL(0x12,0x77), BASE, ARG_OPRL },
+ { "extqh", OPR(0x12,0x7A), BASE, ARG_OPR },
+ { "extqh", OPRL(0x12,0x7A), BASE, ARG_OPRL },
+
+ { "mull", OPR(0x13,0x00), BASE, ARG_OPR },
+ { "mull", OPRL(0x13,0x00), BASE, ARG_OPRL },
+ { "mulq", OPR(0x13,0x20), BASE, ARG_OPR },
+ { "mulq", OPRL(0x13,0x20), BASE, ARG_OPRL },
+ { "umulh", OPR(0x13,0x30), BASE, ARG_OPR },
+ { "umulh", OPRL(0x13,0x30), BASE, ARG_OPRL },
+ { "mull/v", OPR(0x13,0x40), BASE, ARG_OPR },
+ { "mull/v", OPRL(0x13,0x40), BASE, ARG_OPRL },
+ { "mulq/v", OPR(0x13,0x60), BASE, ARG_OPR },
+ { "mulq/v", OPRL(0x13,0x60), BASE, ARG_OPRL },
+
+ { "itofs", FP(0x14,0x004), CIX, { RA, ZB, FC } },
+ { "sqrtf/c", FP(0x14,0x00A), CIX, ARG_FPZ1 },
+ { "sqrts/c", FP(0x14,0x00B), CIX, ARG_FPZ1 },
+ { "itoff", FP(0x14,0x014), CIX, { RA, ZB, FC } },
+ { "itoft", FP(0x14,0x024), CIX, { RA, ZB, FC } },
+ { "sqrtg/c", FP(0x14,0x02A), CIX, ARG_FPZ1 },
+ { "sqrtt/c", FP(0x14,0x02B), CIX, ARG_FPZ1 },
+ { "sqrts/m", FP(0x14,0x04B), CIX, ARG_FPZ1 },
+ { "sqrtt/m", FP(0x14,0x06B), CIX, ARG_FPZ1 },
+ { "sqrtf", FP(0x14,0x08A), CIX, ARG_FPZ1 },
+ { "sqrts", FP(0x14,0x08B), CIX, ARG_FPZ1 },
+ { "sqrtg", FP(0x14,0x0AA), CIX, ARG_FPZ1 },
+ { "sqrtt", FP(0x14,0x0AB), CIX, ARG_FPZ1 },
+ { "sqrts/d", FP(0x14,0x0CB), CIX, ARG_FPZ1 },
+ { "sqrtt/d", FP(0x14,0x0EB), CIX, ARG_FPZ1 },
+ { "sqrtf/uc", FP(0x14,0x10A), CIX, ARG_FPZ1 },
+ { "sqrts/uc", FP(0x14,0x10B), CIX, ARG_FPZ1 },
+ { "sqrtg/uc", FP(0x14,0x12A), CIX, ARG_FPZ1 },
+ { "sqrtt/uc", FP(0x14,0x12B), CIX, ARG_FPZ1 },
+ { "sqrts/um", FP(0x14,0x14B), CIX, ARG_FPZ1 },
+ { "sqrtt/um", FP(0x14,0x16B), CIX, ARG_FPZ1 },
+ { "sqrtf/u", FP(0x14,0x18A), CIX, ARG_FPZ1 },
+ { "sqrts/u", FP(0x14,0x18B), CIX, ARG_FPZ1 },
+ { "sqrtg/u", FP(0x14,0x1AA), CIX, ARG_FPZ1 },
+ { "sqrtt/u", FP(0x14,0x1AB), CIX, ARG_FPZ1 },
+ { "sqrts/ud", FP(0x14,0x1CB), CIX, ARG_FPZ1 },
+ { "sqrtt/ud", FP(0x14,0x1EB), CIX, ARG_FPZ1 },
+ { "sqrtf/sc", FP(0x14,0x40A), CIX, ARG_FPZ1 },
+ { "sqrtg/sc", FP(0x14,0x42A), CIX, ARG_FPZ1 },
+ { "sqrtf/s", FP(0x14,0x48A), CIX, ARG_FPZ1 },
+ { "sqrtg/s", FP(0x14,0x4AA), CIX, ARG_FPZ1 },
+ { "sqrtf/suc", FP(0x14,0x50A), CIX, ARG_FPZ1 },
+ { "sqrts/suc", FP(0x14,0x50B), CIX, ARG_FPZ1 },
+ { "sqrtg/suc", FP(0x14,0x52A), CIX, ARG_FPZ1 },
+ { "sqrtt/suc", FP(0x14,0x52B), CIX, ARG_FPZ1 },
+ { "sqrts/sum", FP(0x14,0x54B), CIX, ARG_FPZ1 },
+ { "sqrtt/sum", FP(0x14,0x56B), CIX, ARG_FPZ1 },
+ { "sqrtf/su", FP(0x14,0x58A), CIX, ARG_FPZ1 },
+ { "sqrts/su", FP(0x14,0x58B), CIX, ARG_FPZ1 },
+ { "sqrtg/su", FP(0x14,0x5AA), CIX, ARG_FPZ1 },
+ { "sqrtt/su", FP(0x14,0x5AB), CIX, ARG_FPZ1 },
+ { "sqrts/sud", FP(0x14,0x5CB), CIX, ARG_FPZ1 },
+ { "sqrtt/sud", FP(0x14,0x5EB), CIX, ARG_FPZ1 },
+ { "sqrts/suic", FP(0x14,0x70B), CIX, ARG_FPZ1 },
+ { "sqrtt/suic", FP(0x14,0x72B), CIX, ARG_FPZ1 },
+ { "sqrts/suim", FP(0x14,0x74B), CIX, ARG_FPZ1 },
+ { "sqrtt/suim", FP(0x14,0x76B), CIX, ARG_FPZ1 },
+ { "sqrts/sui", FP(0x14,0x78B), CIX, ARG_FPZ1 },
+ { "sqrtt/sui", FP(0x14,0x7AB), CIX, ARG_FPZ1 },
+ { "sqrts/suid", FP(0x14,0x7CB), CIX, ARG_FPZ1 },
+ { "sqrtt/suid", FP(0x14,0x7EB), CIX, ARG_FPZ1 },
+
+ { "addf/c", FP(0x15,0x000), BASE, ARG_FP },
+ { "subf/c", FP(0x15,0x001), BASE, ARG_FP },
+ { "mulf/c", FP(0x15,0x002), BASE, ARG_FP },
+ { "divf/c", FP(0x15,0x003), BASE, ARG_FP },
+ { "cvtdg/c", FP(0x15,0x01E), BASE, ARG_FPZ1 },
+ { "addg/c", FP(0x15,0x020), BASE, ARG_FP },
+ { "subg/c", FP(0x15,0x021), BASE, ARG_FP },
+ { "mulg/c", FP(0x15,0x022), BASE, ARG_FP },
+ { "divg/c", FP(0x15,0x023), BASE, ARG_FP },
+ { "cvtgf/c", FP(0x15,0x02C), BASE, ARG_FPZ1 },
+ { "cvtgd/c", FP(0x15,0x02D), BASE, ARG_FPZ1 },
+ { "cvtgq/c", FP(0x15,0x02F), BASE, ARG_FPZ1 },
+ { "cvtqf/c", FP(0x15,0x03C), BASE, ARG_FPZ1 },
+ { "cvtqg/c", FP(0x15,0x03E), BASE, ARG_FPZ1 },
+ { "addf", FP(0x15,0x080), BASE, ARG_FP },
+ { "negf", FP(0x15,0x081), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subf", FP(0x15,0x081), BASE, ARG_FP },
+ { "mulf", FP(0x15,0x082), BASE, ARG_FP },
+ { "divf", FP(0x15,0x083), BASE, ARG_FP },
+ { "cvtdg", FP(0x15,0x09E), BASE, ARG_FPZ1 },
+ { "addg", FP(0x15,0x0A0), BASE, ARG_FP },
+ { "negg", FP(0x15,0x0A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subg", FP(0x15,0x0A1), BASE, ARG_FP },
+ { "mulg", FP(0x15,0x0A2), BASE, ARG_FP },
+ { "divg", FP(0x15,0x0A3), BASE, ARG_FP },
+ { "cmpgeq", FP(0x15,0x0A5), BASE, ARG_FP },
+ { "cmpglt", FP(0x15,0x0A6), BASE, ARG_FP },
+ { "cmpgle", FP(0x15,0x0A7), BASE, ARG_FP },
+ { "cvtgf", FP(0x15,0x0AC), BASE, ARG_FPZ1 },
+ { "cvtgd", FP(0x15,0x0AD), BASE, ARG_FPZ1 },
+ { "cvtgq", FP(0x15,0x0AF), BASE, ARG_FPZ1 },
+ { "cvtqf", FP(0x15,0x0BC), BASE, ARG_FPZ1 },
+ { "cvtqg", FP(0x15,0x0BE), BASE, ARG_FPZ1 },
+ { "addf/uc", FP(0x15,0x100), BASE, ARG_FP },
+ { "subf/uc", FP(0x15,0x101), BASE, ARG_FP },
+ { "mulf/uc", FP(0x15,0x102), BASE, ARG_FP },
+ { "divf/uc", FP(0x15,0x103), BASE, ARG_FP },
+ { "cvtdg/uc", FP(0x15,0x11E), BASE, ARG_FPZ1 },
+ { "addg/uc", FP(0x15,0x120), BASE, ARG_FP },
+ { "subg/uc", FP(0x15,0x121), BASE, ARG_FP },
+ { "mulg/uc", FP(0x15,0x122), BASE, ARG_FP },
+ { "divg/uc", FP(0x15,0x123), BASE, ARG_FP },
+ { "cvtgf/uc", FP(0x15,0x12C), BASE, ARG_FPZ1 },
+ { "cvtgd/uc", FP(0x15,0x12D), BASE, ARG_FPZ1 },
+ { "cvtgq/vc", FP(0x15,0x12F), BASE, ARG_FPZ1 },
+ { "addf/u", FP(0x15,0x180), BASE, ARG_FP },
+ { "subf/u", FP(0x15,0x181), BASE, ARG_FP },
+ { "mulf/u", FP(0x15,0x182), BASE, ARG_FP },
+ { "divf/u", FP(0x15,0x183), BASE, ARG_FP },
+ { "cvtdg/u", FP(0x15,0x19E), BASE, ARG_FPZ1 },
+ { "addg/u", FP(0x15,0x1A0), BASE, ARG_FP },
+ { "subg/u", FP(0x15,0x1A1), BASE, ARG_FP },
+ { "mulg/u", FP(0x15,0x1A2), BASE, ARG_FP },
+ { "divg/u", FP(0x15,0x1A3), BASE, ARG_FP },
+ { "cvtgf/u", FP(0x15,0x1AC), BASE, ARG_FPZ1 },
+ { "cvtgd/u", FP(0x15,0x1AD), BASE, ARG_FPZ1 },
+ { "cvtgq/v", FP(0x15,0x1AF), BASE, ARG_FPZ1 },
+ { "addf/sc", FP(0x15,0x400), BASE, ARG_FP },
+ { "subf/sc", FP(0x15,0x401), BASE, ARG_FP },
+ { "mulf/sc", FP(0x15,0x402), BASE, ARG_FP },
+ { "divf/sc", FP(0x15,0x403), BASE, ARG_FP },
+ { "cvtdg/sc", FP(0x15,0x41E), BASE, ARG_FPZ1 },
+ { "addg/sc", FP(0x15,0x420), BASE, ARG_FP },
+ { "subg/sc", FP(0x15,0x421), BASE, ARG_FP },
+ { "mulg/sc", FP(0x15,0x422), BASE, ARG_FP },
+ { "divg/sc", FP(0x15,0x423), BASE, ARG_FP },
+ { "cvtgf/sc", FP(0x15,0x42C), BASE, ARG_FPZ1 },
+ { "cvtgd/sc", FP(0x15,0x42D), BASE, ARG_FPZ1 },
+ { "cvtgq/sc", FP(0x15,0x42F), BASE, ARG_FPZ1 },
+ { "addf/s", FP(0x15,0x480), BASE, ARG_FP },
+ { "negf/s", FP(0x15,0x481), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subf/s", FP(0x15,0x481), BASE, ARG_FP },
+ { "mulf/s", FP(0x15,0x482), BASE, ARG_FP },
+ { "divf/s", FP(0x15,0x483), BASE, ARG_FP },
+ { "cvtdg/s", FP(0x15,0x49E), BASE, ARG_FPZ1 },
+ { "addg/s", FP(0x15,0x4A0), BASE, ARG_FP },
+ { "negg/s", FP(0x15,0x4A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subg/s", FP(0x15,0x4A1), BASE, ARG_FP },
+ { "mulg/s", FP(0x15,0x4A2), BASE, ARG_FP },
+ { "divg/s", FP(0x15,0x4A3), BASE, ARG_FP },
+ { "cmpgeq/s", FP(0x15,0x4A5), BASE, ARG_FP },
+ { "cmpglt/s", FP(0x15,0x4A6), BASE, ARG_FP },
+ { "cmpgle/s", FP(0x15,0x4A7), BASE, ARG_FP },
+ { "cvtgf/s", FP(0x15,0x4AC), BASE, ARG_FPZ1 },
+ { "cvtgd/s", FP(0x15,0x4AD), BASE, ARG_FPZ1 },
+ { "cvtgq/s", FP(0x15,0x4AF), BASE, ARG_FPZ1 },
+ { "addf/suc", FP(0x15,0x500), BASE, ARG_FP },
+ { "subf/suc", FP(0x15,0x501), BASE, ARG_FP },
+ { "mulf/suc", FP(0x15,0x502), BASE, ARG_FP },
+ { "divf/suc", FP(0x15,0x503), BASE, ARG_FP },
+ { "cvtdg/suc", FP(0x15,0x51E), BASE, ARG_FPZ1 },
+ { "addg/suc", FP(0x15,0x520), BASE, ARG_FP },
+ { "subg/suc", FP(0x15,0x521), BASE, ARG_FP },
+ { "mulg/suc", FP(0x15,0x522), BASE, ARG_FP },
+ { "divg/suc", FP(0x15,0x523), BASE, ARG_FP },
+ { "cvtgf/suc", FP(0x15,0x52C), BASE, ARG_FPZ1 },
+ { "cvtgd/suc", FP(0x15,0x52D), BASE, ARG_FPZ1 },
+ { "cvtgq/svc", FP(0x15,0x52F), BASE, ARG_FPZ1 },
+ { "addf/su", FP(0x15,0x580), BASE, ARG_FP },
+ { "subf/su", FP(0x15,0x581), BASE, ARG_FP },
+ { "mulf/su", FP(0x15,0x582), BASE, ARG_FP },
+ { "divf/su", FP(0x15,0x583), BASE, ARG_FP },
+ { "cvtdg/su", FP(0x15,0x59E), BASE, ARG_FPZ1 },
+ { "addg/su", FP(0x15,0x5A0), BASE, ARG_FP },
+ { "subg/su", FP(0x15,0x5A1), BASE, ARG_FP },
+ { "mulg/su", FP(0x15,0x5A2), BASE, ARG_FP },
+ { "divg/su", FP(0x15,0x5A3), BASE, ARG_FP },
+ { "cvtgf/su", FP(0x15,0x5AC), BASE, ARG_FPZ1 },
+ { "cvtgd/su", FP(0x15,0x5AD), BASE, ARG_FPZ1 },
+ { "cvtgq/sv", FP(0x15,0x5AF), BASE, ARG_FPZ1 },
+
+ { "adds/c", FP(0x16,0x000), BASE, ARG_FP },
+ { "subs/c", FP(0x16,0x001), BASE, ARG_FP },
+ { "muls/c", FP(0x16,0x002), BASE, ARG_FP },
+ { "divs/c", FP(0x16,0x003), BASE, ARG_FP },
+ { "addt/c", FP(0x16,0x020), BASE, ARG_FP },
+ { "subt/c", FP(0x16,0x021), BASE, ARG_FP },
+ { "mult/c", FP(0x16,0x022), BASE, ARG_FP },
+ { "divt/c", FP(0x16,0x023), BASE, ARG_FP },
+ { "cvtts/c", FP(0x16,0x02C), BASE, ARG_FPZ1 },
+ { "cvttq/c", FP(0x16,0x02F), BASE, ARG_FPZ1 },
+ { "cvtqs/c", FP(0x16,0x03C), BASE, ARG_FPZ1 },
+ { "cvtqt/c", FP(0x16,0x03E), BASE, ARG_FPZ1 },
+ { "adds/m", FP(0x16,0x040), BASE, ARG_FP },
+ { "subs/m", FP(0x16,0x041), BASE, ARG_FP },
+ { "muls/m", FP(0x16,0x042), BASE, ARG_FP },
+ { "divs/m", FP(0x16,0x043), BASE, ARG_FP },
+ { "addt/m", FP(0x16,0x060), BASE, ARG_FP },
+ { "subt/m", FP(0x16,0x061), BASE, ARG_FP },
+ { "mult/m", FP(0x16,0x062), BASE, ARG_FP },
+ { "divt/m", FP(0x16,0x063), BASE, ARG_FP },
+ { "cvtts/m", FP(0x16,0x06C), BASE, ARG_FPZ1 },
+ { "cvttq/m", FP(0x16,0x06F), BASE, ARG_FPZ1 },
+ { "cvtqs/m", FP(0x16,0x07C), BASE, ARG_FPZ1 },
+ { "cvtqt/m", FP(0x16,0x07E), BASE, ARG_FPZ1 },
+ { "adds", FP(0x16,0x080), BASE, ARG_FP },
+ { "negs", FP(0x16,0x081), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subs", FP(0x16,0x081), BASE, ARG_FP },
+ { "muls", FP(0x16,0x082), BASE, ARG_FP },
+ { "divs", FP(0x16,0x083), BASE, ARG_FP },
+ { "addt", FP(0x16,0x0A0), BASE, ARG_FP },
+ { "negt", FP(0x16,0x0A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subt", FP(0x16,0x0A1), BASE, ARG_FP },
+ { "mult", FP(0x16,0x0A2), BASE, ARG_FP },
+ { "divt", FP(0x16,0x0A3), BASE, ARG_FP },
+ { "cmptun", FP(0x16,0x0A4), BASE, ARG_FP },
+ { "cmpteq", FP(0x16,0x0A5), BASE, ARG_FP },
+ { "cmptlt", FP(0x16,0x0A6), BASE, ARG_FP },
+ { "cmptle", FP(0x16,0x0A7), BASE, ARG_FP },
+ { "cvtts", FP(0x16,0x0AC), BASE, ARG_FPZ1 },
+ { "cvttq", FP(0x16,0x0AF), BASE, ARG_FPZ1 },
+ { "cvtqs", FP(0x16,0x0BC), BASE, ARG_FPZ1 },
+ { "cvtqt", FP(0x16,0x0BE), BASE, ARG_FPZ1 },
+ { "adds/d", FP(0x16,0x0C0), BASE, ARG_FP },
+ { "subs/d", FP(0x16,0x0C1), BASE, ARG_FP },
+ { "muls/d", FP(0x16,0x0C2), BASE, ARG_FP },
+ { "divs/d", FP(0x16,0x0C3), BASE, ARG_FP },
+ { "addt/d", FP(0x16,0x0E0), BASE, ARG_FP },
+ { "subt/d", FP(0x16,0x0E1), BASE, ARG_FP },
+ { "mult/d", FP(0x16,0x0E2), BASE, ARG_FP },
+ { "divt/d", FP(0x16,0x0E3), BASE, ARG_FP },
+ { "cvtts/d", FP(0x16,0x0EC), BASE, ARG_FPZ1 },
+ { "cvttq/d", FP(0x16,0x0EF), BASE, ARG_FPZ1 },
+ { "cvtqs/d", FP(0x16,0x0FC), BASE, ARG_FPZ1 },
+ { "cvtqt/d", FP(0x16,0x0FE), BASE, ARG_FPZ1 },
+ { "adds/uc", FP(0x16,0x100), BASE, ARG_FP },
+ { "subs/uc", FP(0x16,0x101), BASE, ARG_FP },
+ { "muls/uc", FP(0x16,0x102), BASE, ARG_FP },
+ { "divs/uc", FP(0x16,0x103), BASE, ARG_FP },
+ { "addt/uc", FP(0x16,0x120), BASE, ARG_FP },
+ { "subt/uc", FP(0x16,0x121), BASE, ARG_FP },
+ { "mult/uc", FP(0x16,0x122), BASE, ARG_FP },
+ { "divt/uc", FP(0x16,0x123), BASE, ARG_FP },
+ { "cvtts/uc", FP(0x16,0x12C), BASE, ARG_FPZ1 },
+ { "cvttq/vc", FP(0x16,0x12F), BASE, ARG_FPZ1 },
+ { "adds/um", FP(0x16,0x140), BASE, ARG_FP },
+ { "subs/um", FP(0x16,0x141), BASE, ARG_FP },
+ { "muls/um", FP(0x16,0x142), BASE, ARG_FP },
+ { "divs/um", FP(0x16,0x143), BASE, ARG_FP },
+ { "addt/um", FP(0x16,0x160), BASE, ARG_FP },
+ { "subt/um", FP(0x16,0x161), BASE, ARG_FP },
+ { "mult/um", FP(0x16,0x162), BASE, ARG_FP },
+ { "divt/um", FP(0x16,0x163), BASE, ARG_FP },
+ { "cvtts/um", FP(0x16,0x16C), BASE, ARG_FPZ1 },
+ { "cvttq/vm", FP(0x16,0x16F), BASE, ARG_FPZ1 },
+ { "adds/u", FP(0x16,0x180), BASE, ARG_FP },
+ { "subs/u", FP(0x16,0x181), BASE, ARG_FP },
+ { "muls/u", FP(0x16,0x182), BASE, ARG_FP },
+ { "divs/u", FP(0x16,0x183), BASE, ARG_FP },
+ { "addt/u", FP(0x16,0x1A0), BASE, ARG_FP },
+ { "subt/u", FP(0x16,0x1A1), BASE, ARG_FP },
+ { "mult/u", FP(0x16,0x1A2), BASE, ARG_FP },
+ { "divt/u", FP(0x16,0x1A3), BASE, ARG_FP },
+ { "cvtts/u", FP(0x16,0x1AC), BASE, ARG_FPZ1 },
+ { "cvttq/v", FP(0x16,0x1AF), BASE, ARG_FPZ1 },
+ { "adds/ud", FP(0x16,0x1C0), BASE, ARG_FP },
+ { "subs/ud", FP(0x16,0x1C1), BASE, ARG_FP },
+ { "muls/ud", FP(0x16,0x1C2), BASE, ARG_FP },
+ { "divs/ud", FP(0x16,0x1C3), BASE, ARG_FP },
+ { "addt/ud", FP(0x16,0x1E0), BASE, ARG_FP },
+ { "subt/ud", FP(0x16,0x1E1), BASE, ARG_FP },
+ { "mult/ud", FP(0x16,0x1E2), BASE, ARG_FP },
+ { "divt/ud", FP(0x16,0x1E3), BASE, ARG_FP },
+ { "cvtts/ud", FP(0x16,0x1EC), BASE, ARG_FPZ1 },
+ { "cvttq/vd", FP(0x16,0x1EF), BASE, ARG_FPZ1 },
+ { "cvtst", FP(0x16,0x2AC), BASE, ARG_FPZ1 },
+ { "adds/suc", FP(0x16,0x500), BASE, ARG_FP },
+ { "subs/suc", FP(0x16,0x501), BASE, ARG_FP },
+ { "muls/suc", FP(0x16,0x502), BASE, ARG_FP },
+ { "divs/suc", FP(0x16,0x503), BASE, ARG_FP },
+ { "addt/suc", FP(0x16,0x520), BASE, ARG_FP },
+ { "subt/suc", FP(0x16,0x521), BASE, ARG_FP },
+ { "mult/suc", FP(0x16,0x522), BASE, ARG_FP },
+ { "divt/suc", FP(0x16,0x523), BASE, ARG_FP },
+ { "cvtts/suc", FP(0x16,0x52C), BASE, ARG_FPZ1 },
+ { "cvttq/svc", FP(0x16,0x52F), BASE, ARG_FPZ1 },
+ { "adds/sum", FP(0x16,0x540), BASE, ARG_FP },
+ { "subs/sum", FP(0x16,0x541), BASE, ARG_FP },
+ { "muls/sum", FP(0x16,0x542), BASE, ARG_FP },
+ { "divs/sum", FP(0x16,0x543), BASE, ARG_FP },
+ { "addt/sum", FP(0x16,0x560), BASE, ARG_FP },
+ { "subt/sum", FP(0x16,0x561), BASE, ARG_FP },
+ { "mult/sum", FP(0x16,0x562), BASE, ARG_FP },
+ { "divt/sum", FP(0x16,0x563), BASE, ARG_FP },
+ { "cvtts/sum", FP(0x16,0x56C), BASE, ARG_FPZ1 },
+ { "cvttq/svm", FP(0x16,0x56F), BASE, ARG_FPZ1 },
+ { "adds/su", FP(0x16,0x580), BASE, ARG_FP },
+ { "negs/su", FP(0x16,0x581), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subs/su", FP(0x16,0x581), BASE, ARG_FP },
+ { "muls/su", FP(0x16,0x582), BASE, ARG_FP },
+ { "divs/su", FP(0x16,0x583), BASE, ARG_FP },
+ { "addt/su", FP(0x16,0x5A0), BASE, ARG_FP },
+ { "negt/su", FP(0x16,0x5A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subt/su", FP(0x16,0x5A1), BASE, ARG_FP },
+ { "mult/su", FP(0x16,0x5A2), BASE, ARG_FP },
+ { "divt/su", FP(0x16,0x5A3), BASE, ARG_FP },
+ { "cmptun/su", FP(0x16,0x5A4), BASE, ARG_FP },
+ { "cmpteq/su", FP(0x16,0x5A5), BASE, ARG_FP },
+ { "cmptlt/su", FP(0x16,0x5A6), BASE, ARG_FP },
+ { "cmptle/su", FP(0x16,0x5A7), BASE, ARG_FP },
+ { "cvtts/su", FP(0x16,0x5AC), BASE, ARG_FPZ1 },
+ { "cvttq/sv", FP(0x16,0x5AF), BASE, ARG_FPZ1 },
+ { "adds/sud", FP(0x16,0x5C0), BASE, ARG_FP },
+ { "subs/sud", FP(0x16,0x5C1), BASE, ARG_FP },
+ { "muls/sud", FP(0x16,0x5C2), BASE, ARG_FP },
+ { "divs/sud", FP(0x16,0x5C3), BASE, ARG_FP },
+ { "addt/sud", FP(0x16,0x5E0), BASE, ARG_FP },
+ { "subt/sud", FP(0x16,0x5E1), BASE, ARG_FP },
+ { "mult/sud", FP(0x16,0x5E2), BASE, ARG_FP },
+ { "divt/sud", FP(0x16,0x5E3), BASE, ARG_FP },
+ { "cvtts/sud", FP(0x16,0x5EC), BASE, ARG_FPZ1 },
+ { "cvttq/svd", FP(0x16,0x5EF), BASE, ARG_FPZ1 },
+ { "cvtst/s", FP(0x16,0x6AC), BASE, ARG_FPZ1 },
+ { "adds/suic", FP(0x16,0x700), BASE, ARG_FP },
+ { "subs/suic", FP(0x16,0x701), BASE, ARG_FP },
+ { "muls/suic", FP(0x16,0x702), BASE, ARG_FP },
+ { "divs/suic", FP(0x16,0x703), BASE, ARG_FP },
+ { "addt/suic", FP(0x16,0x720), BASE, ARG_FP },
+ { "subt/suic", FP(0x16,0x721), BASE, ARG_FP },
+ { "mult/suic", FP(0x16,0x722), BASE, ARG_FP },
+ { "divt/suic", FP(0x16,0x723), BASE, ARG_FP },
+ { "cvtts/suic", FP(0x16,0x72C), BASE, ARG_FPZ1 },
+ { "cvttq/svic", FP(0x16,0x72F), BASE, ARG_FPZ1 },
+ { "cvtqs/suic", FP(0x16,0x73C), BASE, ARG_FPZ1 },
+ { "cvtqt/suic", FP(0x16,0x73E), BASE, ARG_FPZ1 },
+ { "adds/suim", FP(0x16,0x740), BASE, ARG_FP },
+ { "subs/suim", FP(0x16,0x741), BASE, ARG_FP },
+ { "muls/suim", FP(0x16,0x742), BASE, ARG_FP },
+ { "divs/suim", FP(0x16,0x743), BASE, ARG_FP },
+ { "addt/suim", FP(0x16,0x760), BASE, ARG_FP },
+ { "subt/suim", FP(0x16,0x761), BASE, ARG_FP },
+ { "mult/suim", FP(0x16,0x762), BASE, ARG_FP },
+ { "divt/suim", FP(0x16,0x763), BASE, ARG_FP },
+ { "cvtts/suim", FP(0x16,0x76C), BASE, ARG_FPZ1 },
+ { "cvttq/svim", FP(0x16,0x76F), BASE, ARG_FPZ1 },
+ { "cvtqs/suim", FP(0x16,0x77C), BASE, ARG_FPZ1 },
+ { "cvtqt/suim", FP(0x16,0x77E), BASE, ARG_FPZ1 },
+ { "adds/sui", FP(0x16,0x780), BASE, ARG_FP },
+ { "negs/sui", FP(0x16,0x781), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subs/sui", FP(0x16,0x781), BASE, ARG_FP },
+ { "muls/sui", FP(0x16,0x782), BASE, ARG_FP },
+ { "divs/sui", FP(0x16,0x783), BASE, ARG_FP },
+ { "addt/sui", FP(0x16,0x7A0), BASE, ARG_FP },
+ { "negt/sui", FP(0x16,0x7A1), BASE, ARG_FPZ1 }, /* pseudo */
+ { "subt/sui", FP(0x16,0x7A1), BASE, ARG_FP },
+ { "mult/sui", FP(0x16,0x7A2), BASE, ARG_FP },
+ { "divt/sui", FP(0x16,0x7A3), BASE, ARG_FP },
+ { "cvtts/sui", FP(0x16,0x7AC), BASE, ARG_FPZ1 },
+ { "cvttq/svi", FP(0x16,0x7AF), BASE, ARG_FPZ1 },
+ { "cvtqs/sui", FP(0x16,0x7BC), BASE, ARG_FPZ1 },
+ { "cvtqt/sui", FP(0x16,0x7BE), BASE, ARG_FPZ1 },
+ { "adds/suid", FP(0x16,0x7C0), BASE, ARG_FP },
+ { "subs/suid", FP(0x16,0x7C1), BASE, ARG_FP },
+ { "muls/suid", FP(0x16,0x7C2), BASE, ARG_FP },
+ { "divs/suid", FP(0x16,0x7C3), BASE, ARG_FP },
+ { "addt/suid", FP(0x16,0x7E0), BASE, ARG_FP },
+ { "subt/suid", FP(0x16,0x7E1), BASE, ARG_FP },
+ { "mult/suid", FP(0x16,0x7E2), BASE, ARG_FP },
+ { "divt/suid", FP(0x16,0x7E3), BASE, ARG_FP },
+ { "cvtts/suid", FP(0x16,0x7EC), BASE, ARG_FPZ1 },
+ { "cvttq/svid", FP(0x16,0x7EF), BASE, ARG_FPZ1 },
+ { "cvtqs/suid", FP(0x16,0x7FC), BASE, ARG_FPZ1 },
+ { "cvtqt/suid", FP(0x16,0x7FE), BASE, ARG_FPZ1 },
+
+ { "cvtlq", FP(0x17,0x010), BASE, ARG_FPZ1 },
+ { "fnop", FP(0x17,0x020), BASE, { ZA, ZB, ZC } }, /* pseudo */
+ { "fclr", FP(0x17,0x020), BASE, { ZA, ZB, FC } }, /* pseudo */
+ { "fabs", FP(0x17,0x020), BASE, ARG_FPZ1 }, /* pseudo */
+ { "fmov", FP(0x17,0x020), BASE, { FA, RBA, FC } }, /* pseudo */
+ { "cpys", FP(0x17,0x020), BASE, ARG_FP },
+ { "fneg", FP(0x17,0x021), BASE, { FA, RBA, FC } }, /* pseudo */
+ { "cpysn", FP(0x17,0x021), BASE, ARG_FP },
+ { "cpyse", FP(0x17,0x022), BASE, ARG_FP },
+ { "mt_fpcr", FP(0x17,0x024), BASE, { FA, RBA, RCA } },
+ { "mf_fpcr", FP(0x17,0x025), BASE, { FA, RBA, RCA } },
+ { "fcmoveq", FP(0x17,0x02A), BASE, ARG_FP },
+ { "fcmovne", FP(0x17,0x02B), BASE, ARG_FP },
+ { "fcmovlt", FP(0x17,0x02C), BASE, ARG_FP },
+ { "fcmovge", FP(0x17,0x02D), BASE, ARG_FP },
+ { "fcmovle", FP(0x17,0x02E), BASE, ARG_FP },
+ { "fcmovgt", FP(0x17,0x02F), BASE, ARG_FP },
+ { "cvtql", FP(0x17,0x030), BASE, ARG_FPZ1 },
+ { "cvtql/v", FP(0x17,0x130), BASE, ARG_FPZ1 },
+ { "cvtql/sv", FP(0x17,0x530), BASE, ARG_FPZ1 },
+
+ { "trapb", MFC(0x18,0x0000), BASE, ARG_NONE },
+ { "draint", MFC(0x18,0x0000), BASE, ARG_NONE }, /* alias */
+ { "excb", MFC(0x18,0x0400), BASE, ARG_NONE },
+ { "mb", MFC(0x18,0x4000), BASE, ARG_NONE },
+ { "wmb", MFC(0x18,0x4400), BASE, ARG_NONE },
+ { "fetch", MFC(0x18,0x8000), BASE, { ZA, PRB } },
+ { "fetch_m", MFC(0x18,0xA000), BASE, { ZA, PRB } },
+ { "rpcc", MFC(0x18,0xC000), BASE, { RA } },
+ { "rc", MFC(0x18,0xE000), BASE, { RA } },
+ { "ecb", MFC(0x18,0xE800), BASE, { ZA, PRB } }, /* ev56 una */
+ { "rs", MFC(0x18,0xF000), BASE, { RA } },
+ { "wh64", MFC(0x18,0xF800), BASE, { ZA, PRB } }, /* ev56 una */
+ { "wh64en", MFC(0x18,0xFC00), BASE, { ZA, PRB } }, /* ev7 una */
+
+ { "hw_mfpr", OPR(0x19,0x00), EV4, { RA, RBA, EV4EXTHWINDEX } },
+ { "hw_mfpr", OP(0x19), OP_MASK, EV5, { RA, RBA, EV5HWINDEX } },
+ { "hw_mfpr", OP(0x19), OP_MASK, EV6, { RA, ZB, EV6HWINDEX } },
+ { "hw_mfpr/i", OPR(0x19,0x01), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/a", OPR(0x19,0x02), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/ai", OPR(0x19,0x03), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/p", OPR(0x19,0x04), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/pi", OPR(0x19,0x05), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/pa", OPR(0x19,0x06), EV4, ARG_EV4HWMPR },
+ { "hw_mfpr/pai", OPR(0x19,0x07), EV4, ARG_EV4HWMPR },
+ { "pal19", PCD(0x19), BASE, ARG_PCD },
+
+ { "jmp", MBR_(0x1A,0), MBR_MASK | 0x3FFF, /* pseudo */
+ BASE, { ZA, CPRB } },
+ { "jmp", MBR(0x1A,0), BASE, { RA, CPRB, JMPHINT } },
+ { "jsr", MBR(0x1A,1), BASE, { RA, CPRB, JMPHINT } },
+ { "ret", MBR_(0x1A,2) | (31 << 21) | (26 << 16) | 1,/* pseudo */
+ 0xFFFFFFFF, BASE, { 0 } },
+ { "ret", MBR(0x1A,2), BASE, { RA, CPRB, RETHINT } },
+ { "jcr", MBR(0x1A,3), BASE, { RA, CPRB, RETHINT } }, /* alias */
+ { "jsr_coroutine", MBR(0x1A,3), BASE, { RA, CPRB, RETHINT } },
+
+ { "hw_ldl", EV4HWMEM(0x1B,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_ldl", EV5HWMEM(0x1B,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_ldl", EV6HWMEM(0x1B,0x8), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/a", EV4HWMEM(0x1B,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/a", EV5HWMEM(0x1B,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/a", EV6HWMEM(0x1B,0xC), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/al", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/ar", EV4HWMEM(0x1B,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/av", EV5HWMEM(0x1B,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/avl", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/aw", EV5HWMEM(0x1B,0x18), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/awl", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/awv", EV5HWMEM(0x1B,0x1a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/awvl", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/p", EV4HWMEM(0x1B,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/p", EV5HWMEM(0x1B,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/p", EV6HWMEM(0x1B,0x0), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/pa", EV4HWMEM(0x1B,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/pa", EV5HWMEM(0x1B,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pal", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/par", EV4HWMEM(0x1B,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/pav", EV5HWMEM(0x1B,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pavl", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/paw", EV5HWMEM(0x1B,0x38), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pawl", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pawv", EV5HWMEM(0x1B,0x3a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pawvl", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pl", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pr", EV4HWMEM(0x1B,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/pv", EV5HWMEM(0x1B,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pvl", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pw", EV5HWMEM(0x1B,0x28), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pwl", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pwv", EV5HWMEM(0x1B,0x2a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/pwvl", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/r", EV4HWMEM(0x1B,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_ldl/v", EV5HWMEM(0x1B,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/v", EV6HWMEM(0x1B,0x4), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/vl", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/w", EV5HWMEM(0x1B,0x08), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/w", EV6HWMEM(0x1B,0xA), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/wa", EV6HWMEM(0x1B,0xE), EV6, ARG_EV6HWMEM },
+ { "hw_ldl/wl", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/wv", EV5HWMEM(0x1B,0x0a), EV5, ARG_EV5HWMEM },
+ { "hw_ldl/wvl", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/a", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/av", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/aw", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/awv", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/p", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/p", EV6HWMEM(0x1B,0x2), EV6, ARG_EV6HWMEM },
+ { "hw_ldl_l/pa", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pav", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/paw", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pawv", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pv", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pw", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/pwv", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/v", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/w", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM },
+ { "hw_ldl_l/wv", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM },
+ { "hw_ldq", EV4HWMEM(0x1B,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_ldq", EV5HWMEM(0x1B,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_ldq", EV6HWMEM(0x1B,0x9), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/a", EV4HWMEM(0x1B,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/a", EV5HWMEM(0x1B,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/a", EV6HWMEM(0x1B,0xD), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/al", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/ar", EV4HWMEM(0x1B,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/av", EV5HWMEM(0x1B,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/avl", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/aw", EV5HWMEM(0x1B,0x1c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/awl", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/awv", EV5HWMEM(0x1B,0x1e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/awvl", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/l", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/p", EV4HWMEM(0x1B,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/p", EV5HWMEM(0x1B,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/p", EV6HWMEM(0x1B,0x1), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/pa", EV4HWMEM(0x1B,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/pa", EV5HWMEM(0x1B,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pal", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/par", EV4HWMEM(0x1B,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/pav", EV5HWMEM(0x1B,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pavl", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/paw", EV5HWMEM(0x1B,0x3c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pawl", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pawv", EV5HWMEM(0x1B,0x3e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pawvl", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pl", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pr", EV4HWMEM(0x1B,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/pv", EV5HWMEM(0x1B,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pvl", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pw", EV5HWMEM(0x1B,0x2c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pwl", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pwv", EV5HWMEM(0x1B,0x2e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/pwvl", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/r", EV4HWMEM(0x1B,0x3), EV4, ARG_EV4HWMEM },
+ { "hw_ldq/v", EV5HWMEM(0x1B,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/v", EV6HWMEM(0x1B,0x5), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/vl", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/w", EV5HWMEM(0x1B,0x0c), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/w", EV6HWMEM(0x1B,0xB), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/wa", EV6HWMEM(0x1B,0xF), EV6, ARG_EV6HWMEM },
+ { "hw_ldq/wl", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/wv", EV5HWMEM(0x1B,0x0e), EV5, ARG_EV5HWMEM },
+ { "hw_ldq/wvl", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/a", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/av", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/aw", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/awv", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/p", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/p", EV6HWMEM(0x1B,0x3), EV6, ARG_EV6HWMEM },
+ { "hw_ldq_l/pa", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pav", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/paw", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pawv", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pv", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pw", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/pwv", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/v", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/w", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM },
+ { "hw_ldq_l/wv", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM },
+ { "hw_ld", EV4HWMEM(0x1B,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_ld", EV5HWMEM(0x1B,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_ld/a", EV4HWMEM(0x1B,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_ld/a", EV5HWMEM(0x1B,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_ld/al", EV5HWMEM(0x1B,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aq", EV4HWMEM(0x1B,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_ld/aq", EV5HWMEM(0x1B,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aql", EV5HWMEM(0x1B,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aqv", EV5HWMEM(0x1B,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aqvl", EV5HWMEM(0x1B,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_ld/ar", EV4HWMEM(0x1B,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_ld/arq", EV4HWMEM(0x1B,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_ld/av", EV5HWMEM(0x1B,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_ld/avl", EV5HWMEM(0x1B,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_ld/aw", EV5HWMEM(0x1B,0x18), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awl", EV5HWMEM(0x1B,0x19), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awq", EV5HWMEM(0x1B,0x1c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awql", EV5HWMEM(0x1B,0x1d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awqv", EV5HWMEM(0x1B,0x1e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awqvl", EV5HWMEM(0x1B,0x1f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awv", EV5HWMEM(0x1B,0x1a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/awvl", EV5HWMEM(0x1B,0x1b), EV5, ARG_EV5HWMEM },
+ { "hw_ld/l", EV5HWMEM(0x1B,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_ld/p", EV4HWMEM(0x1B,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_ld/p", EV5HWMEM(0x1B,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pa", EV4HWMEM(0x1B,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pa", EV5HWMEM(0x1B,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pal", EV5HWMEM(0x1B,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paq", EV4HWMEM(0x1B,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_ld/paq", EV5HWMEM(0x1B,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paql", EV5HWMEM(0x1B,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paqv", EV5HWMEM(0x1B,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paqvl", EV5HWMEM(0x1B,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_ld/par", EV4HWMEM(0x1B,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_ld/parq", EV4HWMEM(0x1B,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pav", EV5HWMEM(0x1B,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pavl", EV5HWMEM(0x1B,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_ld/paw", EV5HWMEM(0x1B,0x38), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawl", EV5HWMEM(0x1B,0x39), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawq", EV5HWMEM(0x1B,0x3c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawql", EV5HWMEM(0x1B,0x3d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawqv", EV5HWMEM(0x1B,0x3e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawqvl", EV5HWMEM(0x1B,0x3f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawv", EV5HWMEM(0x1B,0x3a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pawvl", EV5HWMEM(0x1B,0x3b), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pl", EV5HWMEM(0x1B,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pq", EV4HWMEM(0x1B,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pq", EV5HWMEM(0x1B,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pql", EV5HWMEM(0x1B,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pqv", EV5HWMEM(0x1B,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pqvl", EV5HWMEM(0x1B,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pr", EV4HWMEM(0x1B,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_ld/prq", EV4HWMEM(0x1B,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_ld/pv", EV5HWMEM(0x1B,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pvl", EV5HWMEM(0x1B,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pw", EV5HWMEM(0x1B,0x28), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwl", EV5HWMEM(0x1B,0x29), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwq", EV5HWMEM(0x1B,0x2c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwql", EV5HWMEM(0x1B,0x2d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwqv", EV5HWMEM(0x1B,0x2e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwqvl", EV5HWMEM(0x1B,0x2f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwv", EV5HWMEM(0x1B,0x2a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/pwvl", EV5HWMEM(0x1B,0x2b), EV5, ARG_EV5HWMEM },
+ { "hw_ld/q", EV4HWMEM(0x1B,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_ld/q", EV5HWMEM(0x1B,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_ld/ql", EV5HWMEM(0x1B,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_ld/qv", EV5HWMEM(0x1B,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_ld/qvl", EV5HWMEM(0x1B,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_ld/r", EV4HWMEM(0x1B,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_ld/rq", EV4HWMEM(0x1B,0x3), EV4, ARG_EV4HWMEM },
+ { "hw_ld/v", EV5HWMEM(0x1B,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_ld/vl", EV5HWMEM(0x1B,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_ld/w", EV5HWMEM(0x1B,0x08), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wl", EV5HWMEM(0x1B,0x09), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wq", EV5HWMEM(0x1B,0x0c), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wql", EV5HWMEM(0x1B,0x0d), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wqv", EV5HWMEM(0x1B,0x0e), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wqvl", EV5HWMEM(0x1B,0x0f), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wv", EV5HWMEM(0x1B,0x0a), EV5, ARG_EV5HWMEM },
+ { "hw_ld/wvl", EV5HWMEM(0x1B,0x0b), EV5, ARG_EV5HWMEM },
+ { "pal1b", PCD(0x1B), BASE, ARG_PCD },
+
+ { "sextb", OPR(0x1C, 0x00), BWX, ARG_OPRZ1 },
+ { "sextw", OPR(0x1C, 0x01), BWX, ARG_OPRZ1 },
+ { "ctpop", OPR(0x1C, 0x30), CIX, ARG_OPRZ1 },
+ { "perr", OPR(0x1C, 0x31), MAX, ARG_OPR },
+ { "ctlz", OPR(0x1C, 0x32), CIX, ARG_OPRZ1 },
+ { "cttz", OPR(0x1C, 0x33), CIX, ARG_OPRZ1 },
+ { "unpkbw", OPR(0x1C, 0x34), MAX, ARG_OPRZ1 },
+ { "unpkbl", OPR(0x1C, 0x35), MAX, ARG_OPRZ1 },
+ { "pkwb", OPR(0x1C, 0x36), MAX, ARG_OPRZ1 },
+ { "pklb", OPR(0x1C, 0x37), MAX, ARG_OPRZ1 },
+ { "minsb8", OPR(0x1C, 0x38), MAX, ARG_OPR },
+ { "minsb8", OPRL(0x1C, 0x38), MAX, ARG_OPRL },
+ { "minsw4", OPR(0x1C, 0x39), MAX, ARG_OPR },
+ { "minsw4", OPRL(0x1C, 0x39), MAX, ARG_OPRL },
+ { "minub8", OPR(0x1C, 0x3A), MAX, ARG_OPR },
+ { "minub8", OPRL(0x1C, 0x3A), MAX, ARG_OPRL },
+ { "minuw4", OPR(0x1C, 0x3B), MAX, ARG_OPR },
+ { "minuw4", OPRL(0x1C, 0x3B), MAX, ARG_OPRL },
+ { "maxub8", OPR(0x1C, 0x3C), MAX, ARG_OPR },
+ { "maxub8", OPRL(0x1C, 0x3C), MAX, ARG_OPRL },
+ { "maxuw4", OPR(0x1C, 0x3D), MAX, ARG_OPR },
+ { "maxuw4", OPRL(0x1C, 0x3D), MAX, ARG_OPRL },
+ { "maxsb8", OPR(0x1C, 0x3E), MAX, ARG_OPR },
+ { "maxsb8", OPRL(0x1C, 0x3E), MAX, ARG_OPRL },
+ { "maxsw4", OPR(0x1C, 0x3F), MAX, ARG_OPR },
+ { "maxsw4", OPRL(0x1C, 0x3F), MAX, ARG_OPRL },
+ { "ftoit", FP(0x1C, 0x70), CIX, { FA, ZB, RC } },
+ { "ftois", FP(0x1C, 0x78), CIX, { FA, ZB, RC } },
+
+ { "hw_mtpr", OPR(0x1D,0x00), EV4, { RA, RBA, EV4EXTHWINDEX } },
+ { "hw_mtpr", OP(0x1D), OP_MASK, EV5, { RA, RBA, EV5HWINDEX } },
+ { "hw_mtpr", OP(0x1D), OP_MASK, EV6, { ZA, RB, EV6HWINDEX } },
+ { "hw_mtpr/i", OPR(0x1D,0x01), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/a", OPR(0x1D,0x02), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/ai", OPR(0x1D,0x03), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/p", OPR(0x1D,0x04), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/pi", OPR(0x1D,0x05), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/pa", OPR(0x1D,0x06), EV4, ARG_EV4HWMPR },
+ { "hw_mtpr/pai", OPR(0x1D,0x07), EV4, ARG_EV4HWMPR },
+ { "pal1d", PCD(0x1D), BASE, ARG_PCD },
+
+ { "hw_rei", SPCD(0x1E,0x3FF8000), EV4|EV5, ARG_NONE },
+ { "hw_rei_stall", SPCD(0x1E,0x3FFC000), EV5, ARG_NONE },
+ { "hw_jmp", EV6HWMBR(0x1E,0x0), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_jsr", EV6HWMBR(0x1E,0x2), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_ret", EV6HWMBR(0x1E,0x4), EV6, { ZA, PRB } },
+ { "hw_jcr", EV6HWMBR(0x1E,0x6), EV6, { ZA, PRB } },
+ { "hw_coroutine", EV6HWMBR(0x1E,0x6), EV6, { ZA, PRB } }, /* alias */
+ { "hw_jmp/stall", EV6HWMBR(0x1E,0x1), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_jsr/stall", EV6HWMBR(0x1E,0x3), EV6, { ZA, PRB, EV6HWJMPHINT } },
+ { "hw_ret/stall", EV6HWMBR(0x1E,0x5), EV6, { ZA, PRB } },
+ { "hw_jcr/stall", EV6HWMBR(0x1E,0x7), EV6, { ZA, PRB } },
+ { "hw_coroutine/stall", EV6HWMBR(0x1E,0x7), EV6, { ZA, PRB } }, /* alias */
+ { "pal1e", PCD(0x1E), BASE, ARG_PCD },
+
+ { "hw_stl", EV4HWMEM(0x1F,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_stl", EV5HWMEM(0x1F,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_stl", EV6HWMEM(0x1F,0x4), EV6, ARG_EV6HWMEM }, /* ??? 8 */
+ { "hw_stl/a", EV4HWMEM(0x1F,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_stl/a", EV5HWMEM(0x1F,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_stl/a", EV6HWMEM(0x1F,0xC), EV6, ARG_EV6HWMEM },
+ { "hw_stl/ac", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_stl/ar", EV4HWMEM(0x1F,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_stl/av", EV5HWMEM(0x1F,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_stl/avc", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_stl/c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_stl/p", EV4HWMEM(0x1F,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_stl/p", EV5HWMEM(0x1F,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_stl/p", EV6HWMEM(0x1F,0x0), EV6, ARG_EV6HWMEM },
+ { "hw_stl/pa", EV4HWMEM(0x1F,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_stl/pa", EV5HWMEM(0x1F,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pac", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pav", EV5HWMEM(0x1F,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pavc", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pc", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pr", EV4HWMEM(0x1F,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_stl/pv", EV5HWMEM(0x1F,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_stl/pvc", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_stl/r", EV4HWMEM(0x1F,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_stl/v", EV5HWMEM(0x1F,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_stl/vc", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/a", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/av", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/p", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/p", EV6HWMEM(0x1F,0x2), EV6, ARG_EV6HWMEM },
+ { "hw_stl_c/pa", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/pav", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/pv", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_stl_c/v", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM },
+ { "hw_stq", EV4HWMEM(0x1F,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_stq", EV5HWMEM(0x1F,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_stq", EV6HWMEM(0x1F,0x5), EV6, ARG_EV6HWMEM }, /* ??? 9 */
+ { "hw_stq/a", EV4HWMEM(0x1F,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_stq/a", EV5HWMEM(0x1F,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_stq/a", EV6HWMEM(0x1F,0xD), EV6, ARG_EV6HWMEM },
+ { "hw_stq/ac", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_stq/ar", EV4HWMEM(0x1F,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_stq/av", EV5HWMEM(0x1F,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_stq/avc", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_stq/c", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_stq/p", EV4HWMEM(0x1F,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_stq/p", EV5HWMEM(0x1F,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_stq/p", EV6HWMEM(0x1F,0x1), EV6, ARG_EV6HWMEM },
+ { "hw_stq/pa", EV4HWMEM(0x1F,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_stq/pa", EV5HWMEM(0x1F,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pac", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_stq/par", EV4HWMEM(0x1F,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_stq/par", EV4HWMEM(0x1F,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_stq/pav", EV5HWMEM(0x1F,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pavc", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pc", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pr", EV4HWMEM(0x1F,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_stq/pv", EV5HWMEM(0x1F,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_stq/pvc", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_stq/r", EV4HWMEM(0x1F,0x3), EV4, ARG_EV4HWMEM },
+ { "hw_stq/v", EV5HWMEM(0x1F,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_stq/vc", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/a", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/av", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/p", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/p", EV6HWMEM(0x1F,0x3), EV6, ARG_EV6HWMEM },
+ { "hw_stq_c/pa", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/pav", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/pv", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_stq_c/v", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_st", EV4HWMEM(0x1F,0x0), EV4, ARG_EV4HWMEM },
+ { "hw_st", EV5HWMEM(0x1F,0x00), EV5, ARG_EV5HWMEM },
+ { "hw_st/a", EV4HWMEM(0x1F,0x4), EV4, ARG_EV4HWMEM },
+ { "hw_st/a", EV5HWMEM(0x1F,0x10), EV5, ARG_EV5HWMEM },
+ { "hw_st/ac", EV5HWMEM(0x1F,0x11), EV5, ARG_EV5HWMEM },
+ { "hw_st/aq", EV4HWMEM(0x1F,0x5), EV4, ARG_EV4HWMEM },
+ { "hw_st/aq", EV5HWMEM(0x1F,0x14), EV5, ARG_EV5HWMEM },
+ { "hw_st/aqc", EV5HWMEM(0x1F,0x15), EV5, ARG_EV5HWMEM },
+ { "hw_st/aqv", EV5HWMEM(0x1F,0x16), EV5, ARG_EV5HWMEM },
+ { "hw_st/aqvc", EV5HWMEM(0x1F,0x17), EV5, ARG_EV5HWMEM },
+ { "hw_st/ar", EV4HWMEM(0x1F,0x6), EV4, ARG_EV4HWMEM },
+ { "hw_st/arq", EV4HWMEM(0x1F,0x7), EV4, ARG_EV4HWMEM },
+ { "hw_st/av", EV5HWMEM(0x1F,0x12), EV5, ARG_EV5HWMEM },
+ { "hw_st/avc", EV5HWMEM(0x1F,0x13), EV5, ARG_EV5HWMEM },
+ { "hw_st/c", EV5HWMEM(0x1F,0x01), EV5, ARG_EV5HWMEM },
+ { "hw_st/p", EV4HWMEM(0x1F,0x8), EV4, ARG_EV4HWMEM },
+ { "hw_st/p", EV5HWMEM(0x1F,0x20), EV5, ARG_EV5HWMEM },
+ { "hw_st/pa", EV4HWMEM(0x1F,0xC), EV4, ARG_EV4HWMEM },
+ { "hw_st/pa", EV5HWMEM(0x1F,0x30), EV5, ARG_EV5HWMEM },
+ { "hw_st/pac", EV5HWMEM(0x1F,0x31), EV5, ARG_EV5HWMEM },
+ { "hw_st/paq", EV4HWMEM(0x1F,0xD), EV4, ARG_EV4HWMEM },
+ { "hw_st/paq", EV5HWMEM(0x1F,0x34), EV5, ARG_EV5HWMEM },
+ { "hw_st/paqc", EV5HWMEM(0x1F,0x35), EV5, ARG_EV5HWMEM },
+ { "hw_st/paqv", EV5HWMEM(0x1F,0x36), EV5, ARG_EV5HWMEM },
+ { "hw_st/paqvc", EV5HWMEM(0x1F,0x37), EV5, ARG_EV5HWMEM },
+ { "hw_st/par", EV4HWMEM(0x1F,0xE), EV4, ARG_EV4HWMEM },
+ { "hw_st/parq", EV4HWMEM(0x1F,0xF), EV4, ARG_EV4HWMEM },
+ { "hw_st/pav", EV5HWMEM(0x1F,0x32), EV5, ARG_EV5HWMEM },
+ { "hw_st/pavc", EV5HWMEM(0x1F,0x33), EV5, ARG_EV5HWMEM },
+ { "hw_st/pc", EV5HWMEM(0x1F,0x21), EV5, ARG_EV5HWMEM },
+ { "hw_st/pq", EV4HWMEM(0x1F,0x9), EV4, ARG_EV4HWMEM },
+ { "hw_st/pq", EV5HWMEM(0x1F,0x24), EV5, ARG_EV5HWMEM },
+ { "hw_st/pqc", EV5HWMEM(0x1F,0x25), EV5, ARG_EV5HWMEM },
+ { "hw_st/pqv", EV5HWMEM(0x1F,0x26), EV5, ARG_EV5HWMEM },
+ { "hw_st/pqvc", EV5HWMEM(0x1F,0x27), EV5, ARG_EV5HWMEM },
+ { "hw_st/pr", EV4HWMEM(0x1F,0xA), EV4, ARG_EV4HWMEM },
+ { "hw_st/prq", EV4HWMEM(0x1F,0xB), EV4, ARG_EV4HWMEM },
+ { "hw_st/pv", EV5HWMEM(0x1F,0x22), EV5, ARG_EV5HWMEM },
+ { "hw_st/pvc", EV5HWMEM(0x1F,0x23), EV5, ARG_EV5HWMEM },
+ { "hw_st/q", EV4HWMEM(0x1F,0x1), EV4, ARG_EV4HWMEM },
+ { "hw_st/q", EV5HWMEM(0x1F,0x04), EV5, ARG_EV5HWMEM },
+ { "hw_st/qc", EV5HWMEM(0x1F,0x05), EV5, ARG_EV5HWMEM },
+ { "hw_st/qv", EV5HWMEM(0x1F,0x06), EV5, ARG_EV5HWMEM },
+ { "hw_st/qvc", EV5HWMEM(0x1F,0x07), EV5, ARG_EV5HWMEM },
+ { "hw_st/r", EV4HWMEM(0x1F,0x2), EV4, ARG_EV4HWMEM },
+ { "hw_st/v", EV5HWMEM(0x1F,0x02), EV5, ARG_EV5HWMEM },
+ { "hw_st/vc", EV5HWMEM(0x1F,0x03), EV5, ARG_EV5HWMEM },
+ { "pal1f", PCD(0x1F), BASE, ARG_PCD },
+
+ { "ldf", MEM(0x20), BASE, ARG_FMEM },
+ { "ldg", MEM(0x21), BASE, ARG_FMEM },
+ { "lds", MEM(0x22), BASE, ARG_FMEM },
+ { "ldt", MEM(0x23), BASE, ARG_FMEM },
+ { "stf", MEM(0x24), BASE, ARG_FMEM },
+ { "stg", MEM(0x25), BASE, ARG_FMEM },
+ { "sts", MEM(0x26), BASE, ARG_FMEM },
+ { "stt", MEM(0x27), BASE, ARG_FMEM },
+
+ { "ldl", MEM(0x28), BASE, ARG_MEM },
+ { "ldq", MEM(0x29), BASE, ARG_MEM },
+ { "ldl_l", MEM(0x2A), BASE, ARG_MEM },
+ { "ldq_l", MEM(0x2B), BASE, ARG_MEM },
+ { "stl", MEM(0x2C), BASE, ARG_MEM },
+ { "stq", MEM(0x2D), BASE, ARG_MEM },
+ { "stl_c", MEM(0x2E), BASE, ARG_MEM },
+ { "stq_c", MEM(0x2F), BASE, ARG_MEM },
+
+ { "br", BRA(0x30), BASE, { ZA, BDISP } }, /* pseudo */
+ { "br", BRA(0x30), BASE, ARG_BRA },
+ { "fbeq", BRA(0x31), BASE, ARG_FBRA },
+ { "fblt", BRA(0x32), BASE, ARG_FBRA },
+ { "fble", BRA(0x33), BASE, ARG_FBRA },
+ { "bsr", BRA(0x34), BASE, ARG_BRA },
+ { "fbne", BRA(0x35), BASE, ARG_FBRA },
+ { "fbge", BRA(0x36), BASE, ARG_FBRA },
+ { "fbgt", BRA(0x37), BASE, ARG_FBRA },
+ { "blbc", BRA(0x38), BASE, ARG_BRA },
+ { "beq", BRA(0x39), BASE, ARG_BRA },
+ { "blt", BRA(0x3A), BASE, ARG_BRA },
+ { "ble", BRA(0x3B), BASE, ARG_BRA },
+ { "blbs", BRA(0x3C), BASE, ARG_BRA },
+ { "bne", BRA(0x3D), BASE, ARG_BRA },
+ { "bge", BRA(0x3E), BASE, ARG_BRA },
+ { "bgt", BRA(0x3F), BASE, ARG_BRA },
+};
+
+const unsigned alpha_num_opcodes = sizeof(alpha_opcodes)/sizeof(*alpha_opcodes);
+
+/* OSF register names. */
+
+static const char * const osf_regnames[64] = {
+ "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
+ "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp",
+ "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9",
+ "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero",
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
+};
+
+/* VMS register names. */
+
+static const char * const vms_regnames[64] = {
+ "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7",
+ "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
+ "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23",
+ "R24", "AI", "RA", "PV", "AT", "FP", "SP", "RZ",
+ "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
+ "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15",
+ "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23",
+ "F24", "F25", "F26", "F27", "F28", "F29", "F30", "FZ"
+};
+
+/* Disassemble Alpha instructions. */
+
+int
+print_insn_alpha (bfd_vma memaddr, struct disassemble_info *info)
+{
+ static const struct alpha_opcode *opcode_index[AXP_NOPS+1];
+ const char * const * regnames;
+ const struct alpha_opcode *opcode, *opcode_end;
+ const unsigned char *opindex;
+ unsigned insn, op, isa_mask;
+ int need_comma;
+
+ /* Initialize the majorop table the first time through */
+ if (!opcode_index[0])
+ {
+ opcode = alpha_opcodes;
+ opcode_end = opcode + alpha_num_opcodes;
+
+ for (op = 0; op < AXP_NOPS; ++op)
+ {
+ opcode_index[op] = opcode;
+ while (opcode < opcode_end && op == AXP_OP (opcode->opcode))
+ ++opcode;
+ }
+ opcode_index[op] = opcode;
+ }
+
+ if (info->flavour == bfd_target_evax_flavour)
+ regnames = vms_regnames;
+ else
+ regnames = osf_regnames;
+
+ isa_mask = AXP_OPCODE_NOPAL;
+ switch (info->mach)
+ {
+ case bfd_mach_alpha_ev4:
+ isa_mask |= AXP_OPCODE_EV4;
+ break;
+ case bfd_mach_alpha_ev5:
+ isa_mask |= AXP_OPCODE_EV5;
+ break;
+ case bfd_mach_alpha_ev6:
+ isa_mask |= AXP_OPCODE_EV6;
+ break;
+ }
+
+ /* Read the insn into a host word */
+ {
+ bfd_byte buffer[4];
+ int status = (*info->read_memory_func) (memaddr, buffer, 4, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ insn = bfd_getl32 (buffer);
+ }
+
+ /* Get the major opcode of the instruction. */
+ op = AXP_OP (insn);
+
+ /* Find the first match in the opcode table. */
+ opcode_end = opcode_index[op + 1];
+ for (opcode = opcode_index[op]; opcode < opcode_end; ++opcode)
+ {
+ if ((insn ^ opcode->opcode) & opcode->mask)
+ continue;
+
+ if (!(opcode->flags & isa_mask))
+ continue;
+
+ /* Make two passes over the operands. First see if any of them
+ have extraction functions, and, if they do, make sure the
+ instruction is valid. */
+ {
+ int invalid = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ const struct alpha_operand *operand = alpha_operands + *opindex;
+ if (operand->extract)
+ (*operand->extract) (insn, &invalid);
+ }
+ if (invalid)
+ continue;
+ }
+
+ /* The instruction is valid. */
+ goto found;
+ }
+
+ /* No instruction found */
+ (*info->fprintf_func) (info->stream, ".long %#08x", insn);
+
+ return 4;
+
+found:
+ (*info->fprintf_func) (info->stream, "%s", opcode->name);
+ if (opcode->operands[0] != 0)
+ (*info->fprintf_func) (info->stream, "\t");
+
+ /* Now extract and print the operands. */
+ need_comma = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ const struct alpha_operand *operand = alpha_operands + *opindex;
+ int value;
+
+ /* Operands that are marked FAKE are simply ignored. We
+ already made sure that the extract function considered
+ the instruction to be valid. */
+ if ((operand->flags & AXP_OPERAND_FAKE) != 0)
+ continue;
+
+ /* Extract the value from the instruction. */
+ if (operand->extract)
+ value = (*operand->extract) (insn, (int *) NULL);
+ else
+ {
+ value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+ if (operand->flags & AXP_OPERAND_SIGNED)
+ {
+ int signbit = 1 << (operand->bits - 1);
+ value = (value ^ signbit) - signbit;
+ }
+ }
+
+ if (need_comma &&
+ ((operand->flags & (AXP_OPERAND_PARENS | AXP_OPERAND_COMMA))
+ != AXP_OPERAND_PARENS))
+ {
+ (*info->fprintf_func) (info->stream, ",");
+ }
+ if (operand->flags & AXP_OPERAND_PARENS)
+ (*info->fprintf_func) (info->stream, "(");
+
+ /* Print the operand as directed by the flags. */
+ if (operand->flags & AXP_OPERAND_IR)
+ (*info->fprintf_func) (info->stream, "%s", regnames[value]);
+ else if (operand->flags & AXP_OPERAND_FPR)
+ (*info->fprintf_func) (info->stream, "%s", regnames[value + 32]);
+ else if (operand->flags & AXP_OPERAND_RELATIVE)
+ (*info->print_address_func) (memaddr + 4 + value, info);
+ else if (operand->flags & AXP_OPERAND_SIGNED)
+ (*info->fprintf_func) (info->stream, "%d", value);
+ else
+ (*info->fprintf_func) (info->stream, "%#x", value);
+
+ if (operand->flags & AXP_OPERAND_PARENS)
+ (*info->fprintf_func) (info->stream, ")");
+ need_comma = 1;
+ }
+
+ return 4;
+}
diff --git a/alpha.ld b/alpha.ld
new file mode 100644
index 0000000..906d76b
--- /dev/null
+++ b/alpha.ld
@@ -0,0 +1,127 @@
+OUTPUT_FORMAT("elf64-alpha", "elf64-alpha",
+ "elf64-alpha")
+OUTPUT_ARCH(alpha)
+ENTRY(__start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/arch_init.c b/arch_init.c
new file mode 100644
index 0000000..cc56f0f
--- /dev/null
+++ b/arch_init.c
@@ -0,0 +1,726 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+#include "config.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "arch_init.h"
+#include "audio/audio.h"
+#include "hw/pc.h"
+#include "hw/pci.h"
+#include "hw/audiodev.h"
+#include "kvm.h"
+#include "migration.h"
+#include "net.h"
+#include "gdbstub.h"
+#include "hw/smbios.h"
+
+#ifdef TARGET_SPARC
+int graphic_width = 1024;
+int graphic_height = 768;
+int graphic_depth = 8;
+#else
+int graphic_width = 800;
+int graphic_height = 600;
+int graphic_depth = 15;
+#endif
+
+const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf";
+
+#if defined(TARGET_ALPHA)
+#define QEMU_ARCH QEMU_ARCH_ALPHA
+#elif defined(TARGET_ARM)
+#define QEMU_ARCH QEMU_ARCH_ARM
+#elif defined(TARGET_CRIS)
+#define QEMU_ARCH QEMU_ARCH_CRIS
+#elif defined(TARGET_I386)
+#define QEMU_ARCH QEMU_ARCH_I386
+#elif defined(TARGET_M68K)
+#define QEMU_ARCH QEMU_ARCH_M68K
+#elif defined(TARGET_MICROBLAZE)
+#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
+#elif defined(TARGET_MIPS)
+#define QEMU_ARCH QEMU_ARCH_MIPS
+#elif defined(TARGET_PPC)
+#define QEMU_ARCH QEMU_ARCH_PPC
+#elif defined(TARGET_S390X)
+#define QEMU_ARCH QEMU_ARCH_S390X
+#elif defined(TARGET_SH4)
+#define QEMU_ARCH QEMU_ARCH_SH4
+#elif defined(TARGET_SPARC)
+#define QEMU_ARCH QEMU_ARCH_SPARC
+#endif
+
+const uint32_t arch_type = QEMU_ARCH;
+
+/***********************************************************/
+/* ram save/restore */
+
+#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
+#define RAM_SAVE_FLAG_COMPRESS 0x02
+#define RAM_SAVE_FLAG_MEM_SIZE 0x04
+#define RAM_SAVE_FLAG_PAGE 0x08
+#define RAM_SAVE_FLAG_EOS 0x10
+#define RAM_SAVE_FLAG_CONTINUE 0x20
+
+static int is_dup_page(uint8_t *page, uint8_t ch)
+{
+ uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch;
+ uint32_t *array = (uint32_t *)page;
+ int i;
+
+ for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) {
+ if (array[i] != val) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static RAMBlock *last_block;
+static ram_addr_t last_offset;
+
+static int ram_save_block(QEMUFile *f)
+{
+ RAMBlock *block = last_block;
+ ram_addr_t offset = last_offset;
+ ram_addr_t current_addr;
+ int bytes_sent = 0;
+
+ if (!block)
+ block = QLIST_FIRST(&ram_list.blocks);
+
+ current_addr = block->offset + offset;
+
+ do {
+ if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) {
+ uint8_t *p;
+ int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
+
+ cpu_physical_memory_reset_dirty(current_addr,
+ current_addr + TARGET_PAGE_SIZE,
+ MIGRATION_DIRTY_FLAG);
+
+ p = block->host + offset;
+
+ if (is_dup_page(p, *p)) {
+ qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
+ if (!cont) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr,
+ strlen(block->idstr));
+ }
+ qemu_put_byte(f, *p);
+ bytes_sent = 1;
+ } else {
+ qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE);
+ if (!cont) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr,
+ strlen(block->idstr));
+ }
+ qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
+ bytes_sent = TARGET_PAGE_SIZE;
+ }
+
+ break;
+ }
+
+ offset += TARGET_PAGE_SIZE;
+ if (offset >= block->length) {
+ offset = 0;
+ block = QLIST_NEXT(block, next);
+ if (!block)
+ block = QLIST_FIRST(&ram_list.blocks);
+ }
+
+ current_addr = block->offset + offset;
+
+ } while (current_addr != last_block->offset + last_offset);
+
+ last_block = block;
+ last_offset = offset;
+
+ return bytes_sent;
+}
+
+static uint64_t bytes_transferred;
+
+static ram_addr_t ram_save_remaining(void)
+{
+ RAMBlock *block;
+ ram_addr_t count = 0;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ ram_addr_t addr;
+ for (addr = block->offset; addr < block->offset + block->length;
+ addr += TARGET_PAGE_SIZE) {
+ if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+uint64_t ram_bytes_remaining(void)
+{
+ return ram_save_remaining() * TARGET_PAGE_SIZE;
+}
+
+uint64_t ram_bytes_transferred(void)
+{
+ return bytes_transferred;
+}
+
+uint64_t ram_bytes_total(void)
+{
+ RAMBlock *block;
+ uint64_t total = 0;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next)
+ total += block->length;
+
+ return total;
+}
+
+static int block_compar(const void *a, const void *b)
+{
+ RAMBlock * const *ablock = a;
+ RAMBlock * const *bblock = b;
+ if ((*ablock)->offset < (*bblock)->offset) {
+ return -1;
+ } else if ((*ablock)->offset > (*bblock)->offset) {
+ return 1;
+ }
+ return 0;
+}
+
+static void sort_ram_list(void)
+{
+ RAMBlock *block, *nblock, **blocks;
+ int n;
+ n = 0;
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ ++n;
+ }
+ blocks = qemu_malloc(n * sizeof *blocks);
+ n = 0;
+ QLIST_FOREACH_SAFE(block, &ram_list.blocks, next, nblock) {
+ blocks[n++] = block;
+ QLIST_REMOVE(block, next);
+ }
+ qsort(blocks, n, sizeof *blocks, block_compar);
+ while (--n >= 0) {
+ QLIST_INSERT_HEAD(&ram_list.blocks, blocks[n], next);
+ }
+ qemu_free(blocks);
+}
+
+int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
+{
+ ram_addr_t addr;
+ uint64_t bytes_transferred_last;
+ double bwidth = 0;
+ uint64_t expected_time = 0;
+
+ if (stage < 0) {
+ cpu_physical_memory_set_dirty_tracking(0);
+ return 0;
+ }
+
+ if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
+ qemu_file_set_error(f);
+ return 0;
+ }
+
+ if (stage == 1) {
+ RAMBlock *block;
+ bytes_transferred = 0;
+ last_block = NULL;
+ last_offset = 0;
+ sort_ram_list();
+
+ /* Make sure all dirty bits are set */
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ for (addr = block->offset; addr < block->offset + block->length;
+ addr += TARGET_PAGE_SIZE) {
+ if (!cpu_physical_memory_get_dirty(addr,
+ MIGRATION_DIRTY_FLAG)) {
+ cpu_physical_memory_set_dirty(addr);
+ }
+ }
+ }
+
+ /* Enable dirty memory tracking */
+ cpu_physical_memory_set_dirty_tracking(1);
+
+ qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ qemu_put_byte(f, strlen(block->idstr));
+ qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
+ qemu_put_be64(f, block->length);
+ }
+ }
+
+ bytes_transferred_last = bytes_transferred;
+ bwidth = qemu_get_clock_ns(rt_clock);
+
+ while (!qemu_file_rate_limit(f)) {
+ int bytes_sent;
+
+ bytes_sent = ram_save_block(f);
+ bytes_transferred += bytes_sent;
+ if (bytes_sent == 0) { /* no more blocks */
+ break;
+ }
+ }
+
+ bwidth = qemu_get_clock_ns(rt_clock) - bwidth;
+ bwidth = (bytes_transferred - bytes_transferred_last) / bwidth;
+
+ /* if we haven't transferred anything this round, force expected_time to a
+ * a very high value, but without crashing */
+ if (bwidth == 0) {
+ bwidth = 0.000001;
+ }
+
+ /* try transferring iterative blocks of memory */
+ if (stage == 3) {
+ int bytes_sent;
+
+ /* flush all remaining blocks regardless of rate limiting */
+ while ((bytes_sent = ram_save_block(f)) != 0) {
+ bytes_transferred += bytes_sent;
+ }
+ cpu_physical_memory_set_dirty_tracking(0);
+ }
+
+ qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+
+ expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
+
+ return (stage == 2) && (expected_time <= migrate_max_downtime());
+}
+
+static inline void *host_from_stream_offset(QEMUFile *f,
+ ram_addr_t offset,
+ int flags)
+{
+ static RAMBlock *block = NULL;
+ char id[256];
+ uint8_t len;
+
+ if (flags & RAM_SAVE_FLAG_CONTINUE) {
+ if (!block) {
+ fprintf(stderr, "Ack, bad migration stream!\n");
+ return NULL;
+ }
+
+ return block->host + offset;
+ }
+
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)id, len);
+ id[len] = 0;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (!strncmp(id, block->idstr, sizeof(id)))
+ return block->host + offset;
+ }
+
+ fprintf(stderr, "Can't find block %s!\n", id);
+ return NULL;
+}
+
+int ram_load(QEMUFile *f, void *opaque, int version_id)
+{
+ ram_addr_t addr;
+ int flags;
+
+ if (version_id < 3 || version_id > 4) {
+ return -EINVAL;
+ }
+
+ do {
+ addr = qemu_get_be64(f);
+
+ flags = addr & ~TARGET_PAGE_MASK;
+ addr &= TARGET_PAGE_MASK;
+
+ if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
+ if (version_id == 3) {
+ if (addr != ram_bytes_total()) {
+ return -EINVAL;
+ }
+ } else {
+ /* Synchronize RAM block list */
+ char id[256];
+ ram_addr_t length;
+ ram_addr_t total_ram_bytes = addr;
+
+ while (total_ram_bytes) {
+ RAMBlock *block;
+ uint8_t len;
+
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)id, len);
+ id[len] = 0;
+ length = qemu_get_be64(f);
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (!strncmp(id, block->idstr, sizeof(id))) {
+ if (block->length != length)
+ return -EINVAL;
+ break;
+ }
+ }
+
+ if (!block) {
+ fprintf(stderr, "Unknown ramblock \"%s\", cannot "
+ "accept migration\n", id);
+ return -EINVAL;
+ }
+
+ total_ram_bytes -= length;
+ }
+ }
+ }
+
+ if (flags & RAM_SAVE_FLAG_COMPRESS) {
+ void *host;
+ uint8_t ch;
+
+ if (version_id == 3)
+ host = qemu_get_ram_ptr(addr);
+ else
+ host = host_from_stream_offset(f, addr, flags);
+ if (!host) {
+ return -EINVAL;
+ }
+
+ ch = qemu_get_byte(f);
+ memset(host, ch, TARGET_PAGE_SIZE);
+#ifndef _WIN32
+ if (ch == 0 &&
+ (!kvm_enabled() || kvm_has_sync_mmu())) {
+ qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED);
+ }
+#endif
+ } else if (flags & RAM_SAVE_FLAG_PAGE) {
+ void *host;
+
+ if (version_id == 3)
+ host = qemu_get_ram_ptr(addr);
+ else
+ host = host_from_stream_offset(f, addr, flags);
+
+ qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+ }
+ if (qemu_file_has_error(f)) {
+ return -EIO;
+ }
+ } while (!(flags & RAM_SAVE_FLAG_EOS));
+
+ return 0;
+}
+
+void qemu_service_io(void)
+{
+ qemu_notify_event();
+}
+
+#ifdef HAS_AUDIO
+struct soundhw {
+ const char *name;
+ const char *descr;
+ int enabled;
+ int isa;
+ union {
+ int (*init_isa) (qemu_irq *pic);
+ int (*init_pci) (PCIBus *bus);
+ } init;
+};
+
+static struct soundhw soundhw[] = {
+#ifdef HAS_AUDIO_CHOICE
+#if defined(TARGET_I386) || defined(TARGET_MIPS)
+ {
+ "pcspk",
+ "PC speaker",
+ 0,
+ 1,
+ { .init_isa = pcspk_audio_init }
+ },
+#endif
+
+#ifdef CONFIG_SB16
+ {
+ "sb16",
+ "Creative Sound Blaster 16",
+ 0,
+ 1,
+ { .init_isa = SB16_init }
+ },
+#endif
+
+#ifdef CONFIG_CS4231A
+ {
+ "cs4231a",
+ "CS4231A",
+ 0,
+ 1,
+ { .init_isa = cs4231a_init }
+ },
+#endif
+
+#ifdef CONFIG_ADLIB
+ {
+ "adlib",
+#ifdef HAS_YMF262
+ "Yamaha YMF262 (OPL3)",
+#else
+ "Yamaha YM3812 (OPL2)",
+#endif
+ 0,
+ 1,
+ { .init_isa = Adlib_init }
+ },
+#endif
+
+#ifdef CONFIG_GUS
+ {
+ "gus",
+ "Gravis Ultrasound GF1",
+ 0,
+ 1,
+ { .init_isa = GUS_init }
+ },
+#endif
+
+#ifdef CONFIG_AC97
+ {
+ "ac97",
+ "Intel 82801AA AC97 Audio",
+ 0,
+ 0,
+ { .init_pci = ac97_init }
+ },
+#endif
+
+#ifdef CONFIG_ES1370
+ {
+ "es1370",
+ "ENSONIQ AudioPCI ES1370",
+ 0,
+ 0,
+ { .init_pci = es1370_init }
+ },
+#endif
+
+#ifdef CONFIG_HDA
+ {
+ "hda",
+ "Intel HD Audio",
+ 0,
+ 0,
+ { .init_pci = intel_hda_and_codec_init }
+ },
+#endif
+
+#endif /* HAS_AUDIO_CHOICE */
+
+ { NULL, NULL, 0, 0, { NULL } }
+};
+
+void select_soundhw(const char *optarg)
+{
+ struct soundhw *c;
+
+ if (*optarg == '?') {
+ show_valid_cards:
+
+ printf("Valid sound card names (comma separated):\n");
+ for (c = soundhw; c->name; ++c) {
+ printf ("%-11s %s\n", c->name, c->descr);
+ }
+ printf("\n-soundhw all will enable all of the above\n");
+ exit(*optarg != '?');
+ }
+ else {
+ size_t l;
+ const char *p;
+ char *e;
+ int bad_card = 0;
+
+ if (!strcmp(optarg, "all")) {
+ for (c = soundhw; c->name; ++c) {
+ c->enabled = 1;
+ }
+ return;
+ }
+
+ p = optarg;
+ while (*p) {
+ e = strchr(p, ',');
+ l = !e ? strlen(p) : (size_t) (e - p);
+
+ for (c = soundhw; c->name; ++c) {
+ if (!strncmp(c->name, p, l) && !c->name[l]) {
+ c->enabled = 1;
+ break;
+ }
+ }
+
+ if (!c->name) {
+ if (l > 80) {
+ fprintf(stderr,
+ "Unknown sound card name (too big to show)\n");
+ }
+ else {
+ fprintf(stderr, "Unknown sound card name `%.*s'\n",
+ (int) l, p);
+ }
+ bad_card = 1;
+ }
+ p += l + (e != NULL);
+ }
+
+ if (bad_card) {
+ goto show_valid_cards;
+ }
+ }
+}
+
+void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
+{
+ struct soundhw *c;
+
+ for (c = soundhw; c->name; ++c) {
+ if (c->enabled) {
+ if (c->isa) {
+ if (isa_pic) {
+ c->init.init_isa(isa_pic);
+ }
+ } else {
+ if (pci_bus) {
+ c->init.init_pci(pci_bus);
+ }
+ }
+ }
+ }
+}
+#else
+void select_soundhw(const char *optarg)
+{
+}
+void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
+{
+}
+#endif
+
+int qemu_uuid_parse(const char *str, uint8_t *uuid)
+{
+ int ret;
+
+ if (strlen(str) != 36) {
+ return -1;
+ }
+
+ ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3],
+ &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9],
+ &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14],
+ &uuid[15]);
+
+ if (ret != 16) {
+ return -1;
+ }
+#ifdef TARGET_I386
+ smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid);
+#endif
+ return 0;
+}
+
+void do_acpitable_option(const char *optarg)
+{
+#ifdef TARGET_I386
+ if (acpi_table_add(optarg) < 0) {
+ fprintf(stderr, "Wrong acpi table provided\n");
+ exit(1);
+ }
+#endif
+}
+
+void do_smbios_option(const char *optarg)
+{
+#ifdef TARGET_I386
+ if (smbios_entry_add(optarg) < 0) {
+ fprintf(stderr, "Wrong smbios provided\n");
+ exit(1);
+ }
+#endif
+}
+
+void cpudef_init(void)
+{
+#if defined(cpudef_setup)
+ cpudef_setup(); /* parse cpu definitions in target config file */
+#endif
+}
+
+int audio_available(void)
+{
+#ifdef HAS_AUDIO
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int kvm_available(void)
+{
+#ifdef CONFIG_KVM
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int xen_available(void)
+{
+#ifdef CONFIG_XEN
+ return 1;
+#else
+ return 0;
+#endif
+}
diff --git a/arch_init.h b/arch_init.h
new file mode 100644
index 0000000..17c9164
--- /dev/null
+++ b/arch_init.h
@@ -0,0 +1,34 @@
+#ifndef QEMU_ARCH_INIT_H
+#define QEMU_ARCH_INIT_H
+
+extern const char arch_config_name[];
+
+enum {
+ QEMU_ARCH_ALL = -1,
+ QEMU_ARCH_ALPHA = 1,
+ QEMU_ARCH_ARM = 2,
+ QEMU_ARCH_CRIS = 4,
+ QEMU_ARCH_I386 = 8,
+ QEMU_ARCH_M68K = 16,
+ QEMU_ARCH_MICROBLAZE = 32,
+ QEMU_ARCH_MIPS = 64,
+ QEMU_ARCH_PPC = 128,
+ QEMU_ARCH_S390X = 256,
+ QEMU_ARCH_SH4 = 512,
+ QEMU_ARCH_SPARC = 1024,
+};
+
+extern const uint32_t arch_type;
+
+void select_soundhw(const char *optarg);
+int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque);
+int ram_load(QEMUFile *f, void *opaque, int version_id);
+void do_acpitable_option(const char *optarg);
+void do_smbios_option(const char *optarg);
+void cpudef_init(void);
+int audio_available(void);
+void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus);
+int kvm_available(void);
+int xen_available(void);
+
+#endif
diff --git a/arm-dis.c b/arm-dis.c
new file mode 100644
index 0000000..3ece02c
--- /dev/null
+++ b/arm-dis.c
@@ -0,0 +1,4136 @@
+/* Instruction printing code for the ARM
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+ 2007, Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modification by James G. Smith (jsmith@cygnus.co.uk)
+
+ This file is part of libopcodes.
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 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/>. */
+
+/* Start of qemu specific additions. Mostly this is stub definitions
+ for things we don't care about. */
+
+#include "dis-asm.h"
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#define ISSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\n')
+
+#define ARM_EXT_V1 0
+#define ARM_EXT_V2 0
+#define ARM_EXT_V2S 0
+#define ARM_EXT_V3 0
+#define ARM_EXT_V3M 0
+#define ARM_EXT_V4 0
+#define ARM_EXT_V4T 0
+#define ARM_EXT_V5 0
+#define ARM_EXT_V5T 0
+#define ARM_EXT_V5ExP 0
+#define ARM_EXT_V5E 0
+#define ARM_EXT_V5J 0
+#define ARM_EXT_V6 0
+#define ARM_EXT_V6K 0
+#define ARM_EXT_V6Z 0
+#define ARM_EXT_V6T2 0
+#define ARM_EXT_V7 0
+#define ARM_EXT_DIV 0
+
+/* Co-processor space extensions. */
+#define ARM_CEXT_XSCALE 0
+#define ARM_CEXT_MAVERICK 0
+#define ARM_CEXT_IWMMXT 0
+
+#define FPU_FPA_EXT_V1 0
+#define FPU_FPA_EXT_V2 0
+#define FPU_VFP_EXT_NONE 0
+#define FPU_VFP_EXT_V1xD 0
+#define FPU_VFP_EXT_V1 0
+#define FPU_VFP_EXT_V2 0
+#define FPU_MAVERICK 0
+#define FPU_VFP_EXT_V3 0
+#define FPU_NEON_EXT_V1 0
+
+/* Assume host uses ieee float. */
+static void floatformat_to_double (unsigned char *data, double *dest)
+{
+ union {
+ uint32_t i;
+ float f;
+ } u;
+ u.i = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ *dest = u.f;
+}
+
+/* End of qemu specific additions. */
+
+/* FIXME: Belongs in global header. */
+#ifndef strneq
+#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0)
+#endif
+
+#ifndef NUM_ELEM
+#define NUM_ELEM(a) (sizeof (a) / sizeof (a)[0])
+#endif
+
+struct opcode32
+{
+ unsigned long arch; /* Architecture defining this insn. */
+ unsigned long value, mask; /* Recognise insn if (op&mask)==value. */
+ const char *assembler; /* How to disassemble this insn. */
+};
+
+struct opcode16
+{
+ unsigned long arch; /* Architecture defining this insn. */
+ unsigned short value, mask; /* Recognise insn if (op&mask)==value. */
+ const char *assembler; /* How to disassemble this insn. */
+};
+
+/* print_insn_coprocessor recognizes the following format control codes:
+
+ %% %
+
+ %c print condition code (always bits 28-31 in ARM mode)
+ %q print shifter argument
+ %u print condition code (unconditional in ARM mode)
+ %A print address for ldc/stc/ldf/stf instruction
+ %B print vstm/vldm register list
+ %C print vstr/vldr address operand
+ %I print cirrus signed shift immediate: bits 0..3|4..6
+ %F print the COUNT field of a LFM/SFM instruction.
+ %P print floating point precision in arithmetic insn
+ %Q print floating point precision in ldf/stf insn
+ %R print floating point rounding mode
+
+ %<bitfield>r print as an ARM register
+ %<bitfield>d print the bitfield in decimal
+ %<bitfield>k print immediate for VFPv3 conversion instruction
+ %<bitfield>x print the bitfield in hex
+ %<bitfield>X print the bitfield as 1 hex digit without leading "0x"
+ %<bitfield>f print a floating point constant if >7 else a
+ floating point register
+ %<bitfield>w print as an iWMMXt width field - [bhwd]ss/us
+ %<bitfield>g print as an iWMMXt 64-bit register
+ %<bitfield>G print as an iWMMXt general purpose or control register
+ %<bitfield>D print as a NEON D register
+ %<bitfield>Q print as a NEON Q register
+
+ %y<code> print a single precision VFP reg.
+ Codes: 0=>Sm, 1=>Sd, 2=>Sn, 3=>multi-list, 4=>Sm pair
+ %z<code> print a double precision VFP reg
+ Codes: 0=>Dm, 1=>Dd, 2=>Dn, 3=>multi-list
+
+ %<bitfield>'c print specified char iff bitfield is all ones
+ %<bitfield>`c print specified char iff bitfield is all zeroes
+ %<bitfield>?ab... select from array of values in big endian order
+
+ %L print as an iWMMXt N/M width field.
+ %Z print the Immediate of a WSHUFH instruction.
+ %l like 'A' except use byte offsets for 'B' & 'H'
+ versions.
+ %i print 5-bit immediate in bits 8,3..0
+ (print "32" when 0)
+ %r print register offset address for wldt/wstr instruction
+*/
+
+/* Common coprocessor opcodes shared between Arm and Thumb-2. */
+
+static const struct opcode32 coprocessor_opcodes[] =
+{
+ /* XScale instructions. */
+ {ARM_CEXT_XSCALE, 0x0e200010, 0x0fff0ff0, "mia%c\tacc0, %0-3r, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e280010, 0x0fff0ff0, "miaph%c\tacc0, %0-3r, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e2c0010, 0x0ffc0ff0, "mia%17'T%17`B%16'T%16`B%c\tacc0, %0-3r, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0c400000, 0x0ff00fff, "mar%c\tacc0, %12-15r, %16-19r"},
+ {ARM_CEXT_XSCALE, 0x0c500000, 0x0ff00fff, "mra%c\t%12-15r, %16-19r, acc0"},
+
+ /* Intel Wireless MMX technology instructions. */
+#define FIRST_IWMMXT_INSN 0x0e130130
+#define IWMMXT_INSN_COUNT 73
+ {ARM_CEXT_IWMMXT, 0x0e130130, 0x0f3f0fff, "tandc%22-23w%c\t%12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e400010, 0x0ff00f3f, "tbcst%6-7w%c\t%16-19g, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e130170, 0x0f3f0ff8, "textrc%22-23w%c\t%12-15r, #%0-2d"},
+ {ARM_CEXT_XSCALE, 0x0e100070, 0x0f300ff0, "textrm%3?su%22-23w%c\t%12-15r, %16-19g, #%0-2d"},
+ {ARM_CEXT_XSCALE, 0x0e600010, 0x0ff00f38, "tinsr%6-7w%c\t%16-19g, %12-15r, #%0-2d"},
+ {ARM_CEXT_XSCALE, 0x0e000110, 0x0ff00fff, "tmcr%c\t%16-19G, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0c400000, 0x0ff00ff0, "tmcrr%c\t%0-3g, %12-15r, %16-19r"},
+ {ARM_CEXT_XSCALE, 0x0e2c0010, 0x0ffc0e10, "tmia%17?tb%16?tb%c\t%5-8g, %0-3r, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e200010, 0x0fff0e10, "tmia%c\t%5-8g, %0-3r, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e280010, 0x0fff0e10, "tmiaph%c\t%5-8g, %0-3r, %12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e100030, 0x0f300fff, "tmovmsk%22-23w%c\t%12-15r, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e100110, 0x0ff00ff0, "tmrc%c\t%12-15r, %16-19G"},
+ {ARM_CEXT_XSCALE, 0x0c500000, 0x0ff00ff0, "tmrrc%c\t%12-15r, %16-19r, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e130150, 0x0f3f0fff, "torc%22-23w%c\t%12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e130190, 0x0f3f0fff, "torvsc%22-23w%c\t%12-15r"},
+ {ARM_CEXT_XSCALE, 0x0e2001c0, 0x0f300fff, "wabs%22-23w%c\t%12-15g, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e0001c0, 0x0f300fff, "wacc%22-23w%c\t%12-15g, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e000180, 0x0f000ff0, "wadd%20-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e2001a0, 0x0f300ff0, "waddbhus%22?ml%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ea001a0, 0x0ff00ff0, "waddsubhx%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000020, 0x0f800ff0, "waligni%c\t%12-15g, %16-19g, %0-3g, #%20-22d"},
+ {ARM_CEXT_XSCALE, 0x0e800020, 0x0fc00ff0, "walignr%20-21d%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e200000, 0x0fe00ff0, "wand%20'n%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e800000, 0x0fa00ff0, "wavg2%22?hb%20'r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e400000, 0x0fe00ff0, "wavg4%20'r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000060, 0x0f300ff0, "wcmpeq%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e100060, 0x0f100ff0, "wcmpgt%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0xfc500100, 0xfe500f00, "wldrd\t%12-15g, %r"},
+ {ARM_CEXT_XSCALE, 0xfc100100, 0xfe500f00, "wldrw\t%12-15G, %A"},
+ {ARM_CEXT_XSCALE, 0x0c100000, 0x0e100e00, "wldr%L%c\t%12-15g, %l"},
+ {ARM_CEXT_XSCALE, 0x0e400100, 0x0fc00ff0, "wmac%21?su%20'z%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e800100, 0x0fc00ff0, "wmadd%21?su%20'x%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ec00100, 0x0fd00ff0, "wmadd%21?sun%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000160, 0x0f100ff0, "wmax%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000080, 0x0f100fe0, "wmerge%c\t%12-15g, %16-19g, %0-3g, #%21-23d"},
+ {ARM_CEXT_XSCALE, 0x0e0000a0, 0x0f800ff0, "wmia%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e800120, 0x0f800ff0, "wmiaw%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e100160, 0x0f100ff0, "wmin%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000100, 0x0fc00ff0, "wmul%21?su%20?ml%23'r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ed00100, 0x0fd00ff0, "wmul%21?sumr%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ee000c0, 0x0fe00ff0, "wmulwsm%20`r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ec000c0, 0x0fe00ff0, "wmulwum%20`r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0eb000c0, 0x0ff00ff0, "wmulwl%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e8000a0, 0x0f800ff0, "wqmia%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e100080, 0x0fd00ff0, "wqmulm%21'r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ec000e0, 0x0fd00ff0, "wqmulwm%21'r%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000000, 0x0ff00ff0, "wor%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000080, 0x0f000ff0, "wpack%20-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0xfe300040, 0xff300ef0, "wror%22-23w\t%12-15g, %16-19g, #%i"},
+ {ARM_CEXT_XSCALE, 0x0e300040, 0x0f300ff0, "wror%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e300140, 0x0f300ff0, "wror%22-23wg%c\t%12-15g, %16-19g, %0-3G"},
+ {ARM_CEXT_XSCALE, 0x0e000120, 0x0fa00ff0, "wsad%22?hb%20'z%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e0001e0, 0x0f000ff0, "wshufh%c\t%12-15g, %16-19g, #%Z"},
+ {ARM_CEXT_XSCALE, 0xfe100040, 0xff300ef0, "wsll%22-23w\t%12-15g, %16-19g, #%i"},
+ {ARM_CEXT_XSCALE, 0x0e100040, 0x0f300ff0, "wsll%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e100148, 0x0f300ffc, "wsll%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"},
+ {ARM_CEXT_XSCALE, 0xfe000040, 0xff300ef0, "wsra%22-23w\t%12-15g, %16-19g, #%i"},
+ {ARM_CEXT_XSCALE, 0x0e000040, 0x0f300ff0, "wsra%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e000148, 0x0f300ffc, "wsra%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"},
+ {ARM_CEXT_XSCALE, 0xfe200040, 0xff300ef0, "wsrl%22-23w\t%12-15g, %16-19g, #%i"},
+ {ARM_CEXT_XSCALE, 0x0e200040, 0x0f300ff0, "wsrl%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e200148, 0x0f300ffc, "wsrl%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"},
+ {ARM_CEXT_XSCALE, 0xfc400100, 0xfe500f00, "wstrd\t%12-15g, %r"},
+ {ARM_CEXT_XSCALE, 0xfc000100, 0xfe500f00, "wstrw\t%12-15G, %A"},
+ {ARM_CEXT_XSCALE, 0x0c000000, 0x0e100e00, "wstr%L%c\t%12-15g, %l"},
+ {ARM_CEXT_XSCALE, 0x0e0001a0, 0x0f000ff0, "wsub%20-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0ed001c0, 0x0ff00ff0, "wsubaddhx%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e1001c0, 0x0f300ff0, "wabsdiff%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e0000c0, 0x0fd00fff, "wunpckeh%21?sub%c\t%12-15g, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e4000c0, 0x0fd00fff, "wunpckeh%21?suh%c\t%12-15g, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e8000c0, 0x0fd00fff, "wunpckeh%21?suw%c\t%12-15g, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e0000e0, 0x0f100fff, "wunpckel%21?su%22-23w%c\t%12-15g, %16-19g"},
+ {ARM_CEXT_XSCALE, 0x0e1000c0, 0x0f300ff0, "wunpckih%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e1000e0, 0x0f300ff0, "wunpckil%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+ {ARM_CEXT_XSCALE, 0x0e100000, 0x0ff00ff0, "wxor%c\t%12-15g, %16-19g, %0-3g"},
+
+ /* Floating point coprocessor (FPA) instructions */
+ {FPU_FPA_EXT_V1, 0x0e000100, 0x0ff08f10, "adf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e100100, 0x0ff08f10, "muf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e200100, 0x0ff08f10, "suf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e300100, 0x0ff08f10, "rsf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e400100, 0x0ff08f10, "dvf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e500100, 0x0ff08f10, "rdf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e600100, 0x0ff08f10, "pow%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e700100, 0x0ff08f10, "rpw%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e800100, 0x0ff08f10, "rmf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e900100, 0x0ff08f10, "fml%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ea00100, 0x0ff08f10, "fdv%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0eb00100, 0x0ff08f10, "frd%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ec00100, 0x0ff08f10, "pol%c%P%R\t%12-14f, %16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e008100, 0x0ff08f10, "mvf%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e108100, 0x0ff08f10, "mnf%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e208100, 0x0ff08f10, "abs%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e308100, 0x0ff08f10, "rnd%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e408100, 0x0ff08f10, "sqt%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e508100, 0x0ff08f10, "log%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e608100, 0x0ff08f10, "lgn%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e708100, 0x0ff08f10, "exp%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e808100, 0x0ff08f10, "sin%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e908100, 0x0ff08f10, "cos%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ea08100, 0x0ff08f10, "tan%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0eb08100, 0x0ff08f10, "asn%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ec08100, 0x0ff08f10, "acs%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ed08100, 0x0ff08f10, "atn%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ee08100, 0x0ff08f10, "urd%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ef08100, 0x0ff08f10, "nrm%c%P%R\t%12-14f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0e000110, 0x0ff00f1f, "flt%c%P%R\t%16-18f, %12-15r"},
+ {FPU_FPA_EXT_V1, 0x0e100110, 0x0fff0f98, "fix%c%R\t%12-15r, %0-2f"},
+ {FPU_FPA_EXT_V1, 0x0e200110, 0x0fff0fff, "wfs%c\t%12-15r"},
+ {FPU_FPA_EXT_V1, 0x0e300110, 0x0fff0fff, "rfs%c\t%12-15r"},
+ {FPU_FPA_EXT_V1, 0x0e400110, 0x0fff0fff, "wfc%c\t%12-15r"},
+ {FPU_FPA_EXT_V1, 0x0e500110, 0x0fff0fff, "rfc%c\t%12-15r"},
+ {FPU_FPA_EXT_V1, 0x0e90f110, 0x0ff8fff0, "cmf%c\t%16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0eb0f110, 0x0ff8fff0, "cnf%c\t%16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ed0f110, 0x0ff8fff0, "cmfe%c\t%16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0ef0f110, 0x0ff8fff0, "cnfe%c\t%16-18f, %0-3f"},
+ {FPU_FPA_EXT_V1, 0x0c000100, 0x0e100f00, "stf%c%Q\t%12-14f, %A"},
+ {FPU_FPA_EXT_V1, 0x0c100100, 0x0e100f00, "ldf%c%Q\t%12-14f, %A"},
+ {FPU_FPA_EXT_V2, 0x0c000200, 0x0e100f00, "sfm%c\t%12-14f, %F, %A"},
+ {FPU_FPA_EXT_V2, 0x0c100200, 0x0e100f00, "lfm%c\t%12-14f, %F, %A"},
+
+ /* Register load/store */
+ {FPU_NEON_EXT_V1, 0x0d200b00, 0x0fb00f01, "vstmdb%c\t%16-19r%21'!, %B"},
+ {FPU_NEON_EXT_V1, 0x0d300b00, 0x0fb00f01, "vldmdb%c\t%16-19r%21'!, %B"},
+ {FPU_NEON_EXT_V1, 0x0c800b00, 0x0f900f01, "vstmia%c\t%16-19r%21'!, %B"},
+ {FPU_NEON_EXT_V1, 0x0c900b00, 0x0f900f01, "vldmia%c\t%16-19r%21'!, %B"},
+ {FPU_NEON_EXT_V1, 0x0d000b00, 0x0f300f00, "vstr%c\t%12-15,22D, %C"},
+ {FPU_NEON_EXT_V1, 0x0d100b00, 0x0f300f00, "vldr%c\t%12-15,22D, %C"},
+
+ /* Data transfer between ARM and NEON registers */
+ {FPU_NEON_EXT_V1, 0x0e800b10, 0x0ff00f70, "vdup%c.32\t%16-19,7D, %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0e800b30, 0x0ff00f70, "vdup%c.16\t%16-19,7D, %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0ea00b10, 0x0ff00f70, "vdup%c.32\t%16-19,7Q, %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0ea00b30, 0x0ff00f70, "vdup%c.16\t%16-19,7Q, %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0ec00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7D, %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0ee00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7Q, %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0c400b10, 0x0ff00fd0, "vmov%c\t%0-3,5D, %12-15r, %16-19r"},
+ {FPU_NEON_EXT_V1, 0x0c500b10, 0x0ff00fd0, "vmov%c\t%12-15r, %16-19r, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0x0e000b10, 0x0fd00f70, "vmov%c.32\t%16-19,7D[%21d], %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0e100b10, 0x0f500f70, "vmov%c.32\t%12-15r, %16-19,7D[%21d]"},
+ {FPU_NEON_EXT_V1, 0x0e000b30, 0x0fd00f30, "vmov%c.16\t%16-19,7D[%6,21d], %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0e100b30, 0x0f500f30, "vmov%c.%23?us16\t%12-15r, %16-19,7D[%6,21d]"},
+ {FPU_NEON_EXT_V1, 0x0e400b10, 0x0fd00f10, "vmov%c.8\t%16-19,7D[%5,6,21d], %12-15r"},
+ {FPU_NEON_EXT_V1, 0x0e500b10, 0x0f500f10, "vmov%c.%23?us8\t%12-15r, %16-19,7D[%5,6,21d]"},
+
+ /* Floating point coprocessor (VFP) instructions */
+ {FPU_VFP_EXT_V1xD, 0x0ef1fa10, 0x0fffffff, "fmstat%c"},
+ {FPU_VFP_EXT_V1xD, 0x0ee00a10, 0x0fff0fff, "fmxr%c\tfpsid, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0ee10a10, 0x0fff0fff, "fmxr%c\tfpscr, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0ee60a10, 0x0fff0fff, "fmxr%c\tmvfr1, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0ee70a10, 0x0fff0fff, "fmxr%c\tmvfr0, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0ee80a10, 0x0fff0fff, "fmxr%c\tfpexc, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0ee90a10, 0x0fff0fff, "fmxr%c\tfpinst, %12-15r\t@ Impl def"},
+ {FPU_VFP_EXT_V1xD, 0x0eea0a10, 0x0fff0fff, "fmxr%c\tfpinst2, %12-15r\t@ Impl def"},
+ {FPU_VFP_EXT_V1xD, 0x0ef00a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpsid"},
+ {FPU_VFP_EXT_V1xD, 0x0ef10a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpscr"},
+ {FPU_VFP_EXT_V1xD, 0x0ef60a10, 0x0fff0fff, "fmrx%c\t%12-15r, mvfr1"},
+ {FPU_VFP_EXT_V1xD, 0x0ef70a10, 0x0fff0fff, "fmrx%c\t%12-15r, mvfr0"},
+ {FPU_VFP_EXT_V1xD, 0x0ef80a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpexc"},
+ {FPU_VFP_EXT_V1xD, 0x0ef90a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst\t@ Impl def"},
+ {FPU_VFP_EXT_V1xD, 0x0efa0a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst2\t@ Impl def"},
+ {FPU_VFP_EXT_V1, 0x0e000b10, 0x0ff00fff, "fmdlr%c\t%z2, %12-15r"},
+ {FPU_VFP_EXT_V1, 0x0e100b10, 0x0ff00fff, "fmrdl%c\t%12-15r, %z2"},
+ {FPU_VFP_EXT_V1, 0x0e200b10, 0x0ff00fff, "fmdhr%c\t%z2, %12-15r"},
+ {FPU_VFP_EXT_V1, 0x0e300b10, 0x0ff00fff, "fmrdh%c\t%12-15r, %z2"},
+ {FPU_VFP_EXT_V1xD, 0x0ee00a10, 0x0ff00fff, "fmxr%c\t<impl def %16-19x>, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0ef00a10, 0x0ff00fff, "fmrx%c\t%12-15r, <impl def %16-19x>"},
+ {FPU_VFP_EXT_V1xD, 0x0e000a10, 0x0ff00f7f, "fmsr%c\t%y2, %12-15r"},
+ {FPU_VFP_EXT_V1xD, 0x0e100a10, 0x0ff00f7f, "fmrs%c\t%12-15r, %y2"},
+ {FPU_VFP_EXT_V1xD, 0x0eb50a40, 0x0fbf0f70, "fcmp%7'ezs%c\t%y1"},
+ {FPU_VFP_EXT_V1, 0x0eb50b40, 0x0fbf0f70, "fcmp%7'ezd%c\t%z1"},
+ {FPU_VFP_EXT_V1xD, 0x0eb00a40, 0x0fbf0fd0, "fcpys%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0eb00ac0, 0x0fbf0fd0, "fabss%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0eb00b40, 0x0fbf0fd0, "fcpyd%c\t%z1, %z0"},
+ {FPU_VFP_EXT_V1, 0x0eb00bc0, 0x0fbf0fd0, "fabsd%c\t%z1, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0eb10a40, 0x0fbf0fd0, "fnegs%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0eb10ac0, 0x0fbf0fd0, "fsqrts%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0eb10b40, 0x0fbf0fd0, "fnegd%c\t%z1, %z0"},
+ {FPU_VFP_EXT_V1, 0x0eb10bc0, 0x0fbf0fd0, "fsqrtd%c\t%z1, %z0"},
+ {FPU_VFP_EXT_V1, 0x0eb70ac0, 0x0fbf0fd0, "fcvtds%c\t%z1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0eb70bc0, 0x0fbf0fd0, "fcvtsd%c\t%y1, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0eb80a40, 0x0fbf0fd0, "fuitos%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0eb80ac0, 0x0fbf0fd0, "fsitos%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0eb80b40, 0x0fbf0fd0, "fuitod%c\t%z1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0eb80bc0, 0x0fbf0fd0, "fsitod%c\t%z1, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0eb40a40, 0x0fbf0f50, "fcmp%7'es%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0eb40b40, 0x0fbf0f50, "fcmp%7'ed%c\t%z1, %z0"},
+ {FPU_VFP_EXT_V3, 0x0eba0a40, 0x0fbe0f50, "f%16?us%7?lhtos%c\t%y1, #%5,0-3k"},
+ {FPU_VFP_EXT_V3, 0x0eba0b40, 0x0fbe0f50, "f%16?us%7?lhtod%c\t%z1, #%5,0-3k"},
+ {FPU_VFP_EXT_V1xD, 0x0ebc0a40, 0x0fbe0f50, "fto%16?sui%7'zs%c\t%y1, %y0"},
+ {FPU_VFP_EXT_V1, 0x0ebc0b40, 0x0fbe0f50, "fto%16?sui%7'zd%c\t%y1, %z0"},
+ {FPU_VFP_EXT_V3, 0x0ebe0a40, 0x0fbe0f50, "fto%16?us%7?lhs%c\t%y1, #%5,0-3k"},
+ {FPU_VFP_EXT_V3, 0x0ebe0b40, 0x0fbe0f50, "fto%16?us%7?lhd%c\t%z1, #%5,0-3k"},
+ {FPU_VFP_EXT_V1, 0x0c500b10, 0x0fb00ff0, "fmrrd%c\t%12-15r, %16-19r, %z0"},
+ {FPU_VFP_EXT_V3, 0x0eb00a00, 0x0fb00ff0, "fconsts%c\t%y1, #%0-3,16-19d"},
+ {FPU_VFP_EXT_V3, 0x0eb00b00, 0x0fb00ff0, "fconstd%c\t%z1, #%0-3,16-19d"},
+ {FPU_VFP_EXT_V2, 0x0c400a10, 0x0ff00fd0, "fmsrr%c\t%y4, %12-15r, %16-19r"},
+ {FPU_VFP_EXT_V2, 0x0c400b10, 0x0ff00fd0, "fmdrr%c\t%z0, %12-15r, %16-19r"},
+ {FPU_VFP_EXT_V2, 0x0c500a10, 0x0ff00fd0, "fmrrs%c\t%12-15r, %16-19r, %y4"},
+ {FPU_VFP_EXT_V1xD, 0x0e000a00, 0x0fb00f50, "fmacs%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0e000a40, 0x0fb00f50, "fnmacs%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1, 0x0e000b00, 0x0fb00f50, "fmacd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1, 0x0e000b40, 0x0fb00f50, "fnmacd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0e100a00, 0x0fb00f50, "fmscs%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0e100a40, 0x0fb00f50, "fnmscs%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1, 0x0e100b00, 0x0fb00f50, "fmscd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1, 0x0e100b40, 0x0fb00f50, "fnmscd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0e200a00, 0x0fb00f50, "fmuls%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0e200a40, 0x0fb00f50, "fnmuls%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1, 0x0e200b00, 0x0fb00f50, "fmuld%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1, 0x0e200b40, 0x0fb00f50, "fnmuld%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0e300a00, 0x0fb00f50, "fadds%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1xD, 0x0e300a40, 0x0fb00f50, "fsubs%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1, 0x0e300b00, 0x0fb00f50, "faddd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1, 0x0e300b40, 0x0fb00f50, "fsubd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0e800a00, 0x0fb00f50, "fdivs%c\t%y1, %y2, %y0"},
+ {FPU_VFP_EXT_V1, 0x0e800b00, 0x0fb00f50, "fdivd%c\t%z1, %z2, %z0"},
+ {FPU_VFP_EXT_V1xD, 0x0d200a00, 0x0fb00f00, "fstmdbs%c\t%16-19r!, %y3"},
+ {FPU_VFP_EXT_V1xD, 0x0d200b00, 0x0fb00f00, "fstmdb%0?xd%c\t%16-19r!, %z3"},
+ {FPU_VFP_EXT_V1xD, 0x0d300a00, 0x0fb00f00, "fldmdbs%c\t%16-19r!, %y3"},
+ {FPU_VFP_EXT_V1xD, 0x0d300b00, 0x0fb00f00, "fldmdb%0?xd%c\t%16-19r!, %z3"},
+ {FPU_VFP_EXT_V1xD, 0x0d000a00, 0x0f300f00, "fsts%c\t%y1, %A"},
+ {FPU_VFP_EXT_V1, 0x0d000b00, 0x0f300f00, "fstd%c\t%z1, %A"},
+ {FPU_VFP_EXT_V1xD, 0x0d100a00, 0x0f300f00, "flds%c\t%y1, %A"},
+ {FPU_VFP_EXT_V1, 0x0d100b00, 0x0f300f00, "fldd%c\t%z1, %A"},
+ {FPU_VFP_EXT_V1xD, 0x0c800a00, 0x0f900f00, "fstmias%c\t%16-19r%21'!, %y3"},
+ {FPU_VFP_EXT_V1xD, 0x0c800b00, 0x0f900f00, "fstmia%0?xd%c\t%16-19r%21'!, %z3"},
+ {FPU_VFP_EXT_V1xD, 0x0c900a00, 0x0f900f00, "fldmias%c\t%16-19r%21'!, %y3"},
+ {FPU_VFP_EXT_V1xD, 0x0c900b00, 0x0f900f00, "fldmia%0?xd%c\t%16-19r%21'!, %z3"},
+
+ /* Cirrus coprocessor instructions. */
+ {ARM_CEXT_MAVERICK, 0x0d100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0d400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0c400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"},
+ {ARM_CEXT_MAVERICK, 0x0e000450, 0x0ff00ff0, "cfmvsr%c\tmvf%16-19d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e100450, 0x0ff00ff0, "cfmvrs%c\t%12-15r, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000410, 0x0ff00ff0, "cfmvdlr%c\tmvd%16-19d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e100410, 0x0ff00ff0, "cfmvrdl%c\t%12-15r, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000430, 0x0ff00ff0, "cfmvdhr%c\tmvd%16-19d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e100430, 0x0ff00fff, "cfmvrdh%c\t%12-15r, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000510, 0x0ff00fff, "cfmv64lr%c\tmvdx%16-19d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e100510, 0x0ff00fff, "cfmvr64l%c\t%12-15r, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000530, 0x0ff00fff, "cfmv64hr%c\tmvdx%16-19d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e100530, 0x0ff00fff, "cfmvr64h%c\t%12-15r, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e200440, 0x0ff00fff, "cfmval32%c\tmvax%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e100440, 0x0ff00fff, "cfmv32al%c\tmvfx%12-15d, mvax%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e200460, 0x0ff00fff, "cfmvam32%c\tmvax%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e100460, 0x0ff00fff, "cfmv32am%c\tmvfx%12-15d, mvax%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e200480, 0x0ff00fff, "cfmvah32%c\tmvax%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e100480, 0x0ff00fff, "cfmv32ah%c\tmvfx%12-15d, mvax%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e2004a0, 0x0ff00fff, "cfmva32%c\tmvax%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e1004a0, 0x0ff00fff, "cfmv32a%c\tmvfx%12-15d, mvax%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e2004c0, 0x0ff00fff, "cfmva64%c\tmvax%12-15d, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e1004c0, 0x0ff00fff, "cfmv64a%c\tmvdx%12-15d, mvax%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e2004e0, 0x0fff0fff, "cfmvsc32%c\tdspsc, mvdx%12-15d"},
+ {ARM_CEXT_MAVERICK, 0x0e1004e0, 0x0fff0fff, "cfmv32sc%c\tmvdx%12-15d, dspsc"},
+ {ARM_CEXT_MAVERICK, 0x0e000400, 0x0ff00fff, "cfcpys%c\tmvf%12-15d, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000420, 0x0ff00fff, "cfcpyd%c\tmvd%12-15d, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000460, 0x0ff00fff, "cfcvtsd%c\tmvd%12-15d, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000440, 0x0ff00fff, "cfcvtds%c\tmvf%12-15d, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000480, 0x0ff00fff, "cfcvt32s%c\tmvf%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e0004a0, 0x0ff00fff, "cfcvt32d%c\tmvd%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e0004c0, 0x0ff00fff, "cfcvt64s%c\tmvf%12-15d, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e0004e0, 0x0ff00fff, "cfcvt64d%c\tmvd%12-15d, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e100580, 0x0ff00fff, "cfcvts32%c\tmvfx%12-15d, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e1005a0, 0x0ff00fff, "cfcvtd32%c\tmvfx%12-15d, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e1005c0, 0x0ff00fff, "cftruncs32%c\tmvfx%12-15d, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e1005e0, 0x0ff00fff, "cftruncd32%c\tmvfx%12-15d, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e000550, 0x0ff00ff0, "cfrshl32%c\tmvfx%16-19d, mvfx%0-3d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e000570, 0x0ff00ff0, "cfrshl64%c\tmvdx%16-19d, mvdx%0-3d, %12-15r"},
+ {ARM_CEXT_MAVERICK, 0x0e000500, 0x0ff00f10, "cfsh32%c\tmvfx%12-15d, mvfx%16-19d, #%I"},
+ {ARM_CEXT_MAVERICK, 0x0e200500, 0x0ff00f10, "cfsh64%c\tmvdx%12-15d, mvdx%16-19d, #%I"},
+ {ARM_CEXT_MAVERICK, 0x0e100490, 0x0ff00ff0, "cfcmps%c\t%12-15r, mvf%16-19d, mvf%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e1004b0, 0x0ff00ff0, "cfcmpd%c\t%12-15r, mvd%16-19d, mvd%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100590, 0x0ff00ff0, "cfcmp32%c\t%12-15r, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e1005b0, 0x0ff00ff0, "cfcmp64%c\t%12-15r, mvdx%16-19d, mvdx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e300400, 0x0ff00fff, "cfabss%c\tmvf%12-15d, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300420, 0x0ff00fff, "cfabsd%c\tmvd%12-15d, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300440, 0x0ff00fff, "cfnegs%c\tmvf%12-15d, mvf%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300460, 0x0ff00fff, "cfnegd%c\tmvd%12-15d, mvd%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300480, 0x0ff00ff0, "cfadds%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e3004a0, 0x0ff00ff0, "cfaddd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e3004c0, 0x0ff00ff0, "cfsubs%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e3004e0, 0x0ff00ff0, "cfsubd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100400, 0x0ff00ff0, "cfmuls%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100420, 0x0ff00ff0, "cfmuld%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e300500, 0x0ff00fff, "cfabs32%c\tmvfx%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300520, 0x0ff00fff, "cfabs64%c\tmvdx%12-15d, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300540, 0x0ff00fff, "cfneg32%c\tmvfx%12-15d, mvfx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300560, 0x0ff00fff, "cfneg64%c\tmvdx%12-15d, mvdx%16-19d"},
+ {ARM_CEXT_MAVERICK, 0x0e300580, 0x0ff00ff0, "cfadd32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e3005a0, 0x0ff00ff0, "cfadd64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e3005c0, 0x0ff00ff0, "cfsub32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e3005e0, 0x0ff00ff0, "cfsub64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100500, 0x0ff00ff0, "cfmul32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100520, 0x0ff00ff0, "cfmul64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100540, 0x0ff00ff0, "cfmac32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100560, 0x0ff00ff0, "cfmsc32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e000600, 0x0ff00f10, "cfmadd32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e100600, 0x0ff00f10, "cfmsub32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e200600, 0x0ff00f10, "cfmadda32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"},
+ {ARM_CEXT_MAVERICK, 0x0e300600, 0x0ff00f10, "cfmsuba32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"},
+
+ /* Generic coprocessor instructions */
+ {ARM_EXT_V2, 0x0c400000, 0x0ff00000, "mcrr%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+ {ARM_EXT_V2, 0x0c500000, 0x0ff00000, "mrrc%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+ {ARM_EXT_V2, 0x0e000000, 0x0f000010, "cdp%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {ARM_EXT_V2, 0x0e100010, 0x0f100010, "mrc%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {ARM_EXT_V2, 0x0e000010, 0x0f100010, "mcr%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {ARM_EXT_V2, 0x0c000000, 0x0e100000, "stc%22'l%c\t%8-11d, cr%12-15d, %A"},
+ {ARM_EXT_V2, 0x0c100000, 0x0e100000, "ldc%22'l%c\t%8-11d, cr%12-15d, %A"},
+
+ /* V6 coprocessor instructions */
+ {ARM_EXT_V6, 0xfc500000, 0xfff00000, "mrrc2%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+ {ARM_EXT_V6, 0xfc400000, 0xfff00000, "mcrr2%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+
+ /* V5 coprocessor instructions */
+ {ARM_EXT_V5, 0xfc100000, 0xfe100000, "ldc2%22'l%c\t%8-11d, cr%12-15d, %A"},
+ {ARM_EXT_V5, 0xfc000000, 0xfe100000, "stc2%22'l%c\t%8-11d, cr%12-15d, %A"},
+ {ARM_EXT_V5, 0xfe000000, 0xff000010, "cdp2%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {ARM_EXT_V5, 0xfe000010, 0xff100010, "mcr2%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+ {ARM_EXT_V5, 0xfe100010, 0xff100010, "mrc2%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+
+ {0, 0, 0, 0}
+};
+
+/* Neon opcode table: This does not encode the top byte -- that is
+ checked by the print_insn_neon routine, as it depends on whether we are
+ doing thumb32 or arm32 disassembly. */
+
+/* print_insn_neon recognizes the following format control codes:
+
+ %% %
+
+ %c print condition code
+ %A print v{st,ld}[1234] operands
+ %B print v{st,ld}[1234] any one operands
+ %C print v{st,ld}[1234] single->all operands
+ %D print scalar
+ %E print vmov, vmvn, vorr, vbic encoded constant
+ %F print vtbl,vtbx register list
+
+ %<bitfield>r print as an ARM register
+ %<bitfield>d print the bitfield in decimal
+ %<bitfield>e print the 2^N - bitfield in decimal
+ %<bitfield>D print as a NEON D register
+ %<bitfield>Q print as a NEON Q register
+ %<bitfield>R print as a NEON D or Q register
+ %<bitfield>Sn print byte scaled width limited by n
+ %<bitfield>Tn print short scaled width limited by n
+ %<bitfield>Un print long scaled width limited by n
+
+ %<bitfield>'c print specified char iff bitfield is all ones
+ %<bitfield>`c print specified char iff bitfield is all zeroes
+ %<bitfield>?ab... select from array of values in big endian order */
+
+static const struct opcode32 neon_opcodes[] =
+{
+ /* Extract */
+ {FPU_NEON_EXT_V1, 0xf2b00840, 0xffb00850, "vext%c.8\t%12-15,22R, %16-19,7R, %0-3,5R, #%8-11d"},
+ {FPU_NEON_EXT_V1, 0xf2b00000, 0xffb00810, "vext%c.8\t%12-15,22R, %16-19,7R, %0-3,5R, #%8-11d"},
+
+ /* Move data element to all lanes */
+ {FPU_NEON_EXT_V1, 0xf3b40c00, 0xffb70f90, "vdup%c.32\t%12-15,22R, %0-3,5D[%19d]"},
+ {FPU_NEON_EXT_V1, 0xf3b20c00, 0xffb30f90, "vdup%c.16\t%12-15,22R, %0-3,5D[%18-19d]"},
+ {FPU_NEON_EXT_V1, 0xf3b10c00, 0xffb10f90, "vdup%c.8\t%12-15,22R, %0-3,5D[%17-19d]"},
+
+ /* Table lookup */
+ {FPU_NEON_EXT_V1, 0xf3b00800, 0xffb00c50, "vtbl%c.8\t%12-15,22D, %F, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf3b00840, 0xffb00c50, "vtbx%c.8\t%12-15,22D, %F, %0-3,5D"},
+
+ /* Two registers, miscellaneous */
+ {FPU_NEON_EXT_V1, 0xf2880a10, 0xfebf0fd0, "vmovl%c.%24?us8\t%12-15,22Q, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2900a10, 0xfebf0fd0, "vmovl%c.%24?us16\t%12-15,22Q, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2a00a10, 0xfebf0fd0, "vmovl%c.%24?us32\t%12-15,22Q, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf3b00500, 0xffbf0f90, "vcnt%c.8\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00580, 0xffbf0f90, "vmvn%c\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b20000, 0xffbf0f90, "vswp%c\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b20200, 0xffb30fd0, "vmovn%c.i%18-19T2\t%12-15,22D, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf3b20240, 0xffb30fd0, "vqmovun%c.s%18-19T2\t%12-15,22D, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf3b20280, 0xffb30fd0, "vqmovn%c.s%18-19T2\t%12-15,22D, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf3b202c0, 0xffb30fd0, "vqmovn%c.u%18-19T2\t%12-15,22D, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf3b20300, 0xffb30fd0, "vshll%c.i%18-19S2\t%12-15,22Q, %0-3,5D, #%18-19S2"},
+ {FPU_NEON_EXT_V1, 0xf3bb0400, 0xffbf0e90, "vrecpe%c.%8?fu%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3bb0480, 0xffbf0e90, "vrsqrte%c.%8?fu%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00000, 0xffb30f90, "vrev64%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00080, 0xffb30f90, "vrev32%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00100, 0xffb30f90, "vrev16%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00400, 0xffb30f90, "vcls%c.s%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00480, 0xffb30f90, "vclz%c.i%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00700, 0xffb30f90, "vqabs%c.s%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00780, 0xffb30f90, "vqneg%c.s%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b20080, 0xffb30f90, "vtrn%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b20100, 0xffb30f90, "vuzp%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b20180, 0xffb30f90, "vzip%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b10000, 0xffb30b90, "vcgt%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+ {FPU_NEON_EXT_V1, 0xf3b10080, 0xffb30b90, "vcge%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+ {FPU_NEON_EXT_V1, 0xf3b10100, 0xffb30b90, "vceq%c.%10?fi%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+ {FPU_NEON_EXT_V1, 0xf3b10180, 0xffb30b90, "vcle%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+ {FPU_NEON_EXT_V1, 0xf3b10200, 0xffb30b90, "vclt%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+ {FPU_NEON_EXT_V1, 0xf3b10300, 0xffb30b90, "vabs%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b10380, 0xffb30b90, "vneg%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00200, 0xffb30f10, "vpaddl%c.%7?us%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b00600, 0xffb30f10, "vpadal%c.%7?us%18-19S2\t%12-15,22R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3b30600, 0xffb30e10, "vcvt%c.%7-8?usff%18-19Sa.%7-8?ffus%18-19Sa\t%12-15,22R, %0-3,5R"},
+
+ /* Three registers of the same length */
+ {FPU_NEON_EXT_V1, 0xf2000110, 0xffb00f10, "vand%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2100110, 0xffb00f10, "vbic%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2200110, 0xffb00f10, "vorr%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2300110, 0xffb00f10, "vorn%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000110, 0xffb00f10, "veor%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3100110, 0xffb00f10, "vbsl%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3200110, 0xffb00f10, "vbit%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3300110, 0xffb00f10, "vbif%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000d00, 0xffa00f10, "vadd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000d10, 0xffa00f10, "vmla%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000e00, 0xffa00f10, "vceq%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000f00, 0xffa00f10, "vmax%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000f10, 0xffa00f10, "vrecps%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2200d00, 0xffa00f10, "vsub%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2200d10, 0xffa00f10, "vmls%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2200f00, 0xffa00f10, "vmin%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2200f10, 0xffa00f10, "vrsqrts%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000d00, 0xffa00f10, "vpadd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000d10, 0xffa00f10, "vmul%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000e00, 0xffa00f10, "vcge%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000e10, 0xffa00f10, "vacge%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000f00, 0xffa00f10, "vpmax%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3200d00, 0xffa00f10, "vabd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3200e00, 0xffa00f10, "vcgt%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3200e10, 0xffa00f10, "vacgt%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3200f00, 0xffa00f10, "vpmin%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000800, 0xff800f10, "vadd%c.i%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000810, 0xff800f10, "vtst%c.%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000900, 0xff800f10, "vmla%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000b00, 0xff800f10, "vqdmulh%c.s%20-21S6\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000b10, 0xff800f10, "vpadd%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000800, 0xff800f10, "vsub%c.i%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000810, 0xff800f10, "vceq%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000900, 0xff800f10, "vmls%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf3000b00, 0xff800f10, "vqrdmulh%c.s%20-21S6\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000000, 0xfe800f10, "vhadd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000010, 0xfe800f10, "vqadd%c.%24?us%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000100, 0xfe800f10, "vrhadd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000200, 0xfe800f10, "vhsub%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000210, 0xfe800f10, "vqsub%c.%24?us%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000300, 0xfe800f10, "vcgt%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000310, 0xfe800f10, "vcge%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000400, 0xfe800f10, "vshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+ {FPU_NEON_EXT_V1, 0xf2000410, 0xfe800f10, "vqshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+ {FPU_NEON_EXT_V1, 0xf2000500, 0xfe800f10, "vrshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+ {FPU_NEON_EXT_V1, 0xf2000510, 0xfe800f10, "vqrshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+ {FPU_NEON_EXT_V1, 0xf2000600, 0xfe800f10, "vmax%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000610, 0xfe800f10, "vmin%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000700, 0xfe800f10, "vabd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000710, 0xfe800f10, "vaba%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000910, 0xfe800f10, "vmul%c.%24?pi%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000a00, 0xfe800f10, "vpmax%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+ {FPU_NEON_EXT_V1, 0xf2000a10, 0xfe800f10, "vpmin%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+
+ /* One register and an immediate value */
+ {FPU_NEON_EXT_V1, 0xf2800e10, 0xfeb80fb0, "vmov%c.i8\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800e30, 0xfeb80fb0, "vmov%c.i64\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800f10, 0xfeb80fb0, "vmov%c.f32\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800810, 0xfeb80db0, "vmov%c.i16\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800830, 0xfeb80db0, "vmvn%c.i16\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800910, 0xfeb80db0, "vorr%c.i16\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800930, 0xfeb80db0, "vbic%c.i16\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800c10, 0xfeb80eb0, "vmov%c.i32\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800c30, 0xfeb80eb0, "vmvn%c.i32\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800110, 0xfeb809b0, "vorr%c.i32\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800130, 0xfeb809b0, "vbic%c.i32\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800010, 0xfeb808b0, "vmov%c.i32\t%12-15,22R, %E"},
+ {FPU_NEON_EXT_V1, 0xf2800030, 0xfeb808b0, "vmvn%c.i32\t%12-15,22R, %E"},
+
+ /* Two registers and a shift amount */
+ {FPU_NEON_EXT_V1, 0xf2880810, 0xffb80fd0, "vshrn%c.i16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880850, 0xffb80fd0, "vrshrn%c.i16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880810, 0xfeb80fd0, "vqshrun%c.s16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880850, 0xfeb80fd0, "vqrshrun%c.s16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880910, 0xfeb80fd0, "vqshrn%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880950, 0xfeb80fd0, "vqrshrn%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880a10, 0xfeb80fd0, "vshll%c.%24?us8\t%12-15,22D, %0-3,5Q, #%16-18d"},
+ {FPU_NEON_EXT_V1, 0xf2900810, 0xffb00fd0, "vshrn%c.i32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900850, 0xffb00fd0, "vrshrn%c.i32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2880510, 0xffb80f90, "vshl%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18d"},
+ {FPU_NEON_EXT_V1, 0xf3880410, 0xffb80f90, "vsri%c.8\t%12-15,22R, %0-3,5R, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf3880510, 0xffb80f90, "vsli%c.8\t%12-15,22R, %0-3,5R, #%16-18d"},
+ {FPU_NEON_EXT_V1, 0xf3880610, 0xffb80f90, "vqshlu%c.s8\t%12-15,22R, %0-3,5R, #%16-18d"},
+ {FPU_NEON_EXT_V1, 0xf2900810, 0xfeb00fd0, "vqshrun%c.s32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900850, 0xfeb00fd0, "vqrshrun%c.s32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900910, 0xfeb00fd0, "vqshrn%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900950, 0xfeb00fd0, "vqrshrn%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900a10, 0xfeb00fd0, "vshll%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-19d"},
+ {FPU_NEON_EXT_V1, 0xf2880010, 0xfeb80f90, "vshr%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880110, 0xfeb80f90, "vsra%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880210, 0xfeb80f90, "vrshr%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880310, 0xfeb80f90, "vrsra%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+ {FPU_NEON_EXT_V1, 0xf2880710, 0xfeb80f90, "vqshl%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18d"},
+ {FPU_NEON_EXT_V1, 0xf2a00810, 0xffa00fd0, "vshrn%c.i64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2a00850, 0xffa00fd0, "vrshrn%c.i64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2900510, 0xffb00f90, "vshl%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19d"},
+ {FPU_NEON_EXT_V1, 0xf3900410, 0xffb00f90, "vsri%c.16\t%12-15,22R, %0-3,5R, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf3900510, 0xffb00f90, "vsli%c.16\t%12-15,22R, %0-3,5R, #%16-19d"},
+ {FPU_NEON_EXT_V1, 0xf3900610, 0xffb00f90, "vqshlu%c.s16\t%12-15,22R, %0-3,5R, #%16-19d"},
+ {FPU_NEON_EXT_V1, 0xf2a00a10, 0xfea00fd0, "vshll%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-20d"},
+ {FPU_NEON_EXT_V1, 0xf2900010, 0xfeb00f90, "vshr%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900110, 0xfeb00f90, "vsra%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900210, 0xfeb00f90, "vrshr%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900310, 0xfeb00f90, "vrsra%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+ {FPU_NEON_EXT_V1, 0xf2900710, 0xfeb00f90, "vqshl%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19d"},
+ {FPU_NEON_EXT_V1, 0xf2800810, 0xfec00fd0, "vqshrun%c.s64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2800850, 0xfec00fd0, "vqrshrun%c.s64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2800910, 0xfec00fd0, "vqshrn%c.%24?us64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2800950, 0xfec00fd0, "vqrshrn%c.%24?us64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2a00510, 0xffa00f90, "vshl%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20d"},
+ {FPU_NEON_EXT_V1, 0xf3a00410, 0xffa00f90, "vsri%c.32\t%12-15,22R, %0-3,5R, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf3a00510, 0xffa00f90, "vsli%c.32\t%12-15,22R, %0-3,5R, #%16-20d"},
+ {FPU_NEON_EXT_V1, 0xf3a00610, 0xffa00f90, "vqshlu%c.s32\t%12-15,22R, %0-3,5R, #%16-20d"},
+ {FPU_NEON_EXT_V1, 0xf2a00010, 0xfea00f90, "vshr%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2a00110, 0xfea00f90, "vsra%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2a00210, 0xfea00f90, "vrshr%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2a00310, 0xfea00f90, "vrsra%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+ {FPU_NEON_EXT_V1, 0xf2a00710, 0xfea00f90, "vqshl%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20d"},
+ {FPU_NEON_EXT_V1, 0xf2800590, 0xff800f90, "vshl%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21d"},
+ {FPU_NEON_EXT_V1, 0xf3800490, 0xff800f90, "vsri%c.64\t%12-15,22R, %0-3,5R, #%16-21e"},
+ {FPU_NEON_EXT_V1, 0xf3800590, 0xff800f90, "vsli%c.64\t%12-15,22R, %0-3,5R, #%16-21d"},
+ {FPU_NEON_EXT_V1, 0xf3800690, 0xff800f90, "vqshlu%c.s64\t%12-15,22R, %0-3,5R, #%16-21d"},
+ {FPU_NEON_EXT_V1, 0xf2800090, 0xfe800f90, "vshr%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+ {FPU_NEON_EXT_V1, 0xf2800190, 0xfe800f90, "vsra%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+ {FPU_NEON_EXT_V1, 0xf2800290, 0xfe800f90, "vrshr%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+ {FPU_NEON_EXT_V1, 0xf2800390, 0xfe800f90, "vrsra%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+ {FPU_NEON_EXT_V1, 0xf2800790, 0xfe800f90, "vqshl%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21d"},
+ {FPU_NEON_EXT_V1, 0xf2a00e10, 0xfea00e90, "vcvt%c.%24,8?usff32.%24,8?ffus32\t%12-15,22R, %0-3,5R, #%16-20e"},
+
+ /* Three registers of different lengths */
+ {FPU_NEON_EXT_V1, 0xf2800e00, 0xfea00f50, "vmull%c.p%20S0\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800400, 0xff800f50, "vaddhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf2800600, 0xff800f50, "vsubhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf2800900, 0xff800f50, "vqdmlal%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800b00, 0xff800f50, "vqdmlsl%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800d00, 0xff800f50, "vqdmull%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf3800400, 0xff800f50, "vraddhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf3800600, 0xff800f50, "vrsubhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+ {FPU_NEON_EXT_V1, 0xf2800000, 0xfe800f50, "vaddl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800100, 0xfe800f50, "vaddw%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7Q, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800200, 0xfe800f50, "vsubl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800300, 0xfe800f50, "vsubw%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7Q, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800500, 0xfe800f50, "vabal%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800700, 0xfe800f50, "vabdl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800800, 0xfe800f50, "vmlal%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800a00, 0xfe800f50, "vmlsl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+ {FPU_NEON_EXT_V1, 0xf2800c00, 0xfe800f50, "vmull%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+
+ /* Two registers and a scalar */
+ {FPU_NEON_EXT_V1, 0xf2800040, 0xff800f50, "vmla%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800140, 0xff800f50, "vmla%c.f%20-21Sa\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800340, 0xff800f50, "vqdmlal%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800440, 0xff800f50, "vmls%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800540, 0xff800f50, "vmls%c.f%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800740, 0xff800f50, "vqdmlsl%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800840, 0xff800f50, "vmul%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800940, 0xff800f50, "vmul%c.f%20-21Sa\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800b40, 0xff800f50, "vqdmull%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800c40, 0xff800f50, "vqdmulh%c.s%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800d40, 0xff800f50, "vqrdmulh%c.s%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800040, 0xff800f50, "vmla%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800140, 0xff800f50, "vmla%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800440, 0xff800f50, "vmls%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800540, 0xff800f50, "vmls%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800840, 0xff800f50, "vmul%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800940, 0xff800f50, "vmul%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800c40, 0xff800f50, "vqdmulh%c.s%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf3800d40, 0xff800f50, "vqrdmulh%c.s%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800240, 0xfe800f50, "vmlal%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800640, 0xfe800f50, "vmlsl%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+ {FPU_NEON_EXT_V1, 0xf2800a40, 0xfe800f50, "vmull%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+
+ /* Element and structure load/store */
+ {FPU_NEON_EXT_V1, 0xf4a00fc0, 0xffb00fc0, "vld4%c.32\t%C"},
+ {FPU_NEON_EXT_V1, 0xf4a00c00, 0xffb00f00, "vld1%c.%6-7S2\t%C"},
+ {FPU_NEON_EXT_V1, 0xf4a00d00, 0xffb00f00, "vld2%c.%6-7S2\t%C"},
+ {FPU_NEON_EXT_V1, 0xf4a00e00, 0xffb00f00, "vld3%c.%6-7S2\t%C"},
+ {FPU_NEON_EXT_V1, 0xf4a00f00, 0xffb00f00, "vld4%c.%6-7S2\t%C"},
+ {FPU_NEON_EXT_V1, 0xf4000200, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000300, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000400, 0xff900f00, "v%21?ls%21?dt3%c.%6-7S2\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000500, 0xff900f00, "v%21?ls%21?dt3%c.%6-7S2\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000600, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000700, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000800, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000900, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000a00, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4000000, 0xff900e00, "v%21?ls%21?dt4%c.%6-7S2\t%A"},
+ {FPU_NEON_EXT_V1, 0xf4800000, 0xff900300, "v%21?ls%21?dt1%c.%10-11S2\t%B"},
+ {FPU_NEON_EXT_V1, 0xf4800100, 0xff900300, "v%21?ls%21?dt2%c.%10-11S2\t%B"},
+ {FPU_NEON_EXT_V1, 0xf4800200, 0xff900300, "v%21?ls%21?dt3%c.%10-11S2\t%B"},
+ {FPU_NEON_EXT_V1, 0xf4800300, 0xff900300, "v%21?ls%21?dt4%c.%10-11S2\t%B"},
+
+ {0,0 ,0, 0}
+};
+
+/* Opcode tables: ARM, 16-bit Thumb, 32-bit Thumb. All three are partially
+ ordered: they must be searched linearly from the top to obtain a correct
+ match. */
+
+/* print_insn_arm recognizes the following format control codes:
+
+ %% %
+
+ %a print address for ldr/str instruction
+ %s print address for ldr/str halfword/signextend instruction
+ %b print branch destination
+ %c print condition code (always bits 28-31)
+ %m print register mask for ldm/stm instruction
+ %o print operand2 (immediate or register + shift)
+ %p print 'p' iff bits 12-15 are 15
+ %t print 't' iff bit 21 set and bit 24 clear
+ %B print arm BLX(1) destination
+ %C print the PSR sub type.
+ %U print barrier type.
+ %P print address for pli instruction.
+
+ %<bitfield>r print as an ARM register
+ %<bitfield>d print the bitfield in decimal
+ %<bitfield>W print the bitfield plus one in decimal
+ %<bitfield>x print the bitfield in hex
+ %<bitfield>X print the bitfield as 1 hex digit without leading "0x"
+
+ %<bitfield>'c print specified char iff bitfield is all ones
+ %<bitfield>`c print specified char iff bitfield is all zeroes
+ %<bitfield>?ab... select from array of values in big endian order
+
+ %e print arm SMI operand (bits 0..7,8..19).
+ %E print the LSB and WIDTH fields of a BFI or BFC instruction.
+ %V print the 16-bit immediate field of a MOVT or MOVW instruction. */
+
+static const struct opcode32 arm_opcodes[] =
+{
+ /* ARM instructions. */
+ {ARM_EXT_V1, 0xe1a00000, 0xffffffff, "nop\t\t\t(mov r0,r0)"},
+ {ARM_EXT_V4T | ARM_EXT_V5, 0x012FFF10, 0x0ffffff0, "bx%c\t%0-3r"},
+ {ARM_EXT_V2, 0x00000090, 0x0fe000f0, "mul%20's%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V2, 0x00200090, 0x0fe000f0, "mla%20's%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V2S, 0x01000090, 0x0fb00ff0, "swp%22'b%c\t%12-15r, %0-3r, [%16-19r]"},
+ {ARM_EXT_V3M, 0x00800090, 0x0fa000f0, "%22?sumull%20's%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V3M, 0x00a00090, 0x0fa000f0, "%22?sumlal%20's%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+
+ /* V7 instructions. */
+ {ARM_EXT_V7, 0xf450f000, 0xfd70f000, "pli\t%P"},
+ {ARM_EXT_V7, 0x0320f0f0, 0x0ffffff0, "dbg%c\t#%0-3d"},
+ {ARM_EXT_V7, 0xf57ff050, 0xfffffff0, "dmb\t%U"},
+ {ARM_EXT_V7, 0xf57ff040, 0xfffffff0, "dsb\t%U"},
+ {ARM_EXT_V7, 0xf57ff060, 0xfffffff0, "isb\t%U"},
+
+ /* ARM V6T2 instructions. */
+ {ARM_EXT_V6T2, 0x07c0001f, 0x0fe0007f, "bfc%c\t%12-15r, %E"},
+ {ARM_EXT_V6T2, 0x07c00010, 0x0fe00070, "bfi%c\t%12-15r, %0-3r, %E"},
+ {ARM_EXT_V6T2, 0x00600090, 0x0ff000f0, "mls%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V6T2, 0x006000b0, 0x0f7000f0, "strht%c\t%12-15r, %s"},
+ {ARM_EXT_V6T2, 0x00300090, 0x0f300090, "ldr%6's%5?hbt%c\t%12-15r, %s"},
+ {ARM_EXT_V6T2, 0x03000000, 0x0ff00000, "movw%c\t%12-15r, %V"},
+ {ARM_EXT_V6T2, 0x03400000, 0x0ff00000, "movt%c\t%12-15r, %V"},
+ {ARM_EXT_V6T2, 0x06ff0f30, 0x0fff0ff0, "rbit%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6T2, 0x07a00050, 0x0fa00070, "%22?usbfx%c\t%12-15r, %0-3r, #%7-11d, #%16-20W"},
+
+ /* ARM V6Z instructions. */
+ {ARM_EXT_V6Z, 0x01600070, 0x0ff000f0, "smc%c\t%e"},
+
+ /* ARM V6K instructions. */
+ {ARM_EXT_V6K, 0xf57ff01f, 0xffffffff, "clrex"},
+ {ARM_EXT_V6K, 0x01d00f9f, 0x0ff00fff, "ldrexb%c\t%12-15r, [%16-19r]"},
+ {ARM_EXT_V6K, 0x01b00f9f, 0x0ff00fff, "ldrexd%c\t%12-15r, [%16-19r]"},
+ {ARM_EXT_V6K, 0x01f00f9f, 0x0ff00fff, "ldrexh%c\t%12-15r, [%16-19r]"},
+ {ARM_EXT_V6K, 0x01c00f90, 0x0ff00ff0, "strexb%c\t%12-15r, %0-3r, [%16-19r]"},
+ {ARM_EXT_V6K, 0x01a00f90, 0x0ff00ff0, "strexd%c\t%12-15r, %0-3r, [%16-19r]"},
+ {ARM_EXT_V6K, 0x01e00f90, 0x0ff00ff0, "strexh%c\t%12-15r, %0-3r, [%16-19r]"},
+
+ /* ARM V6K NOP hints. */
+ {ARM_EXT_V6K, 0x0320f001, 0x0fffffff, "yield%c"},
+ {ARM_EXT_V6K, 0x0320f002, 0x0fffffff, "wfe%c"},
+ {ARM_EXT_V6K, 0x0320f003, 0x0fffffff, "wfi%c"},
+ {ARM_EXT_V6K, 0x0320f004, 0x0fffffff, "sev%c"},
+ {ARM_EXT_V6K, 0x0320f000, 0x0fffff00, "nop%c\t{%0-7d}"},
+
+ /* ARM V6 instructions. */
+ {ARM_EXT_V6, 0xf1080000, 0xfffffe3f, "cpsie\t%8'a%7'i%6'f"},
+ {ARM_EXT_V6, 0xf10a0000, 0xfffffe20, "cpsie\t%8'a%7'i%6'f,#%0-4d"},
+ {ARM_EXT_V6, 0xf10C0000, 0xfffffe3f, "cpsid\t%8'a%7'i%6'f"},
+ {ARM_EXT_V6, 0xf10e0000, 0xfffffe20, "cpsid\t%8'a%7'i%6'f,#%0-4d"},
+ {ARM_EXT_V6, 0xf1000000, 0xfff1fe20, "cps\t#%0-4d"},
+ {ARM_EXT_V6, 0x06800010, 0x0ff00ff0, "pkhbt%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06800010, 0x0ff00070, "pkhbt%c\t%12-15r, %16-19r, %0-3r, lsl #%7-11d"},
+ {ARM_EXT_V6, 0x06800050, 0x0ff00ff0, "pkhtb%c\t%12-15r, %16-19r, %0-3r, asr #32"},
+ {ARM_EXT_V6, 0x06800050, 0x0ff00070, "pkhtb%c\t%12-15r, %16-19r, %0-3r, asr #%7-11d"},
+ {ARM_EXT_V6, 0x01900f9f, 0x0ff00fff, "ldrex%c\tr%12-15d, [%16-19r]"},
+ {ARM_EXT_V6, 0x06200f10, 0x0ff00ff0, "qadd16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06200f90, 0x0ff00ff0, "qadd8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06200f30, 0x0ff00ff0, "qaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06200f70, 0x0ff00ff0, "qsub16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06200ff0, 0x0ff00ff0, "qsub8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06200f50, 0x0ff00ff0, "qsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06100f10, 0x0ff00ff0, "sadd16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06100f90, 0x0ff00ff0, "sadd8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06100f30, 0x0ff00ff0, "saddaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06300f10, 0x0ff00ff0, "shadd16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06300f90, 0x0ff00ff0, "shadd8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06300f30, 0x0ff00ff0, "shaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06300f70, 0x0ff00ff0, "shsub16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06300ff0, 0x0ff00ff0, "shsub8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06300f50, 0x0ff00ff0, "shsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06100f70, 0x0ff00ff0, "ssub16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06100ff0, 0x0ff00ff0, "ssub8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06100f50, 0x0ff00ff0, "ssubaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06500f10, 0x0ff00ff0, "uadd16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06500f90, 0x0ff00ff0, "uadd8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06500f30, 0x0ff00ff0, "uaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06700f10, 0x0ff00ff0, "uhadd16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06700f90, 0x0ff00ff0, "uhadd8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06700f30, 0x0ff00ff0, "uhaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06700f70, 0x0ff00ff0, "uhsub16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06700ff0, 0x0ff00ff0, "uhsub8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06700f50, 0x0ff00ff0, "uhsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06600f10, 0x0ff00ff0, "uqadd16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06600f90, 0x0ff00ff0, "uqadd8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06600f30, 0x0ff00ff0, "uqaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06600f70, 0x0ff00ff0, "uqsub16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06600ff0, 0x0ff00ff0, "uqsub8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06600f50, 0x0ff00ff0, "uqsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06500f70, 0x0ff00ff0, "usub16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06500ff0, 0x0ff00ff0, "usub8%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06500f50, 0x0ff00ff0, "usubaddx%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06bf0f30, 0x0fff0ff0, "rev%c\t\%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06bf0fb0, 0x0fff0ff0, "rev16%c\t\%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06ff0fb0, 0x0fff0ff0, "revsh%c\t\%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0xf8100a00, 0xfe50ffff, "rfe%23?id%24?ba\t\%16-19r%21'!"},
+ {ARM_EXT_V6, 0x06bf0070, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06bf0470, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06bf0870, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06bf0c70, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x068f0070, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x068f0470, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x068f0870, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x068f0c70, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06af0070, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06af0470, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06af0870, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06af0c70, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06ff0070, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06ff0470, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06ff0870, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06ff0c70, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06cf0070, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06cf0470, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06cf0870, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06cf0c70, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06ef0070, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V6, 0x06ef0470, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06ef0870, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06ef0c70, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06b00070, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06b00470, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06b00870, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06b00c70, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06800070, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06800470, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06800870, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06800c70, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06a00070, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06a00470, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06a00870, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06a00c70, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06f00070, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06f00470, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06f00870, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06f00c70, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06c00070, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06c00470, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06c00870, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06c00c70, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ROR #24"},
+ {ARM_EXT_V6, 0x06e00070, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0x06e00470, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+ {ARM_EXT_V6, 0x06e00870, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+ {ARM_EXT_V6, 0x06e00c70, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+ {ARM_EXT_V6, 0x06800fb0, 0x0ff00ff0, "sel%c\t%12-15r, %16-19r, %0-3r"},
+ {ARM_EXT_V6, 0xf1010000, 0xfffffc00, "setend\t%9?ble"},
+ {ARM_EXT_V6, 0x0700f010, 0x0ff0f0d0, "smuad%5'x%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x0700f050, 0x0ff0f0d0, "smusd%5'x%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x07000010, 0x0ff000d0, "smlad%5'x%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V6, 0x07400010, 0x0ff000d0, "smlald%5'x%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x07000050, 0x0ff000d0, "smlsd%5'x%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V6, 0x07400050, 0x0ff000d0, "smlsld%5'x%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x0750f010, 0x0ff0f0d0, "smmul%5'r%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x07500010, 0x0ff000d0, "smmla%5'r%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V6, 0x075000d0, 0x0ff000d0, "smmls%5'r%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V6, 0xf84d0500, 0xfe5fffe0, "srs%23?id%24?ba\t%16-19r%21'!, #%0-4d"},
+ {ARM_EXT_V6, 0x06a00010, 0x0fe00ff0, "ssat%c\t%12-15r, #%16-20W, %0-3r"},
+ {ARM_EXT_V6, 0x06a00010, 0x0fe00070, "ssat%c\t%12-15r, #%16-20W, %0-3r, lsl #%7-11d"},
+ {ARM_EXT_V6, 0x06a00050, 0x0fe00070, "ssat%c\t%12-15r, #%16-20W, %0-3r, asr #%7-11d"},
+ {ARM_EXT_V6, 0x06a00f30, 0x0ff00ff0, "ssat16%c\t%12-15r, #%16-19W, %0-3r"},
+ {ARM_EXT_V6, 0x01800f90, 0x0ff00ff0, "strex%c\t%12-15r, %0-3r, [%16-19r]"},
+ {ARM_EXT_V6, 0x00400090, 0x0ff000f0, "umaal%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x0780f010, 0x0ff0f0f0, "usad8%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V6, 0x07800010, 0x0ff000f0, "usada8%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V6, 0x06e00010, 0x0fe00ff0, "usat%c\t%12-15r, #%16-20d, %0-3r"},
+ {ARM_EXT_V6, 0x06e00010, 0x0fe00070, "usat%c\t%12-15r, #%16-20d, %0-3r, lsl #%7-11d"},
+ {ARM_EXT_V6, 0x06e00050, 0x0fe00070, "usat%c\t%12-15r, #%16-20d, %0-3r, asr #%7-11d"},
+ {ARM_EXT_V6, 0x06e00f30, 0x0ff00ff0, "usat16%c\t%12-15r, #%16-19d, %0-3r"},
+
+ /* V5J instruction. */
+ {ARM_EXT_V5J, 0x012fff20, 0x0ffffff0, "bxj%c\t%0-3r"},
+
+ /* V5 Instructions. */
+ {ARM_EXT_V5, 0xe1200070, 0xfff000f0, "bkpt\t0x%16-19X%12-15X%8-11X%0-3X"},
+ {ARM_EXT_V5, 0xfa000000, 0xfe000000, "blx\t%B"},
+ {ARM_EXT_V5, 0x012fff30, 0x0ffffff0, "blx%c\t%0-3r"},
+ {ARM_EXT_V5, 0x016f0f10, 0x0fff0ff0, "clz%c\t%12-15r, %0-3r"},
+
+ /* V5E "El Segundo" Instructions. */
+ {ARM_EXT_V5E, 0x000000d0, 0x0e1000f0, "ldrd%c\t%12-15r, %s"},
+ {ARM_EXT_V5E, 0x000000f0, 0x0e1000f0, "strd%c\t%12-15r, %s"},
+ {ARM_EXT_V5E, 0xf450f000, 0xfc70f000, "pld\t%a"},
+ {ARM_EXT_V5ExP, 0x01000080, 0x0ff000f0, "smlabb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V5ExP, 0x010000a0, 0x0ff000f0, "smlatb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V5ExP, 0x010000c0, 0x0ff000f0, "smlabt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V5ExP, 0x010000e0, 0x0ff000f0, "smlatt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+
+ {ARM_EXT_V5ExP, 0x01200080, 0x0ff000f0, "smlawb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+ {ARM_EXT_V5ExP, 0x012000c0, 0x0ff000f0, "smlawt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+
+ {ARM_EXT_V5ExP, 0x01400080, 0x0ff000f0, "smlalbb%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x014000a0, 0x0ff000f0, "smlaltb%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x014000c0, 0x0ff000f0, "smlalbt%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x014000e0, 0x0ff000f0, "smlaltt%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+
+ {ARM_EXT_V5ExP, 0x01600080, 0x0ff0f0f0, "smulbb%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x016000a0, 0x0ff0f0f0, "smultb%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x016000c0, 0x0ff0f0f0, "smulbt%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x016000e0, 0x0ff0f0f0, "smultt%c\t%16-19r, %0-3r, %8-11r"},
+
+ {ARM_EXT_V5ExP, 0x012000a0, 0x0ff0f0f0, "smulwb%c\t%16-19r, %0-3r, %8-11r"},
+ {ARM_EXT_V5ExP, 0x012000e0, 0x0ff0f0f0, "smulwt%c\t%16-19r, %0-3r, %8-11r"},
+
+ {ARM_EXT_V5ExP, 0x01000050, 0x0ff00ff0, "qadd%c\t%12-15r, %0-3r, %16-19r"},
+ {ARM_EXT_V5ExP, 0x01400050, 0x0ff00ff0, "qdadd%c\t%12-15r, %0-3r, %16-19r"},
+ {ARM_EXT_V5ExP, 0x01200050, 0x0ff00ff0, "qsub%c\t%12-15r, %0-3r, %16-19r"},
+ {ARM_EXT_V5ExP, 0x01600050, 0x0ff00ff0, "qdsub%c\t%12-15r, %0-3r, %16-19r"},
+
+ /* ARM Instructions. */
+ {ARM_EXT_V1, 0x00000090, 0x0e100090, "str%6's%5?hb%c\t%12-15r, %s"},
+ {ARM_EXT_V1, 0x00100090, 0x0e100090, "ldr%6's%5?hb%c\t%12-15r, %s"},
+ {ARM_EXT_V1, 0x00000000, 0x0de00000, "and%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00200000, 0x0de00000, "eor%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00400000, 0x0de00000, "sub%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00600000, 0x0de00000, "rsb%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00800000, 0x0de00000, "add%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00a00000, 0x0de00000, "adc%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00c00000, 0x0de00000, "sbc%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x00e00000, 0x0de00000, "rsc%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V3, 0x0120f000, 0x0db0f000, "msr%c\t%22?SCPSR%C, %o"},
+ {ARM_EXT_V3, 0x010f0000, 0x0fbf0fff, "mrs%c\t%12-15r, %22?SCPSR"},
+ {ARM_EXT_V1, 0x01000000, 0x0de00000, "tst%p%c\t%16-19r, %o"},
+ {ARM_EXT_V1, 0x01200000, 0x0de00000, "teq%p%c\t%16-19r, %o"},
+ {ARM_EXT_V1, 0x01400000, 0x0de00000, "cmp%p%c\t%16-19r, %o"},
+ {ARM_EXT_V1, 0x01600000, 0x0de00000, "cmn%p%c\t%16-19r, %o"},
+ {ARM_EXT_V1, 0x01800000, 0x0de00000, "orr%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x03a00000, 0x0fef0000, "mov%20's%c\t%12-15r, %o"},
+ {ARM_EXT_V1, 0x01a00000, 0x0def0ff0, "mov%20's%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V1, 0x01a00000, 0x0def0060, "lsl%20's%c\t%12-15r, %q"},
+ {ARM_EXT_V1, 0x01a00020, 0x0def0060, "lsr%20's%c\t%12-15r, %q"},
+ {ARM_EXT_V1, 0x01a00040, 0x0def0060, "asr%20's%c\t%12-15r, %q"},
+ {ARM_EXT_V1, 0x01a00060, 0x0def0ff0, "rrx%20's%c\t%12-15r, %0-3r"},
+ {ARM_EXT_V1, 0x01a00060, 0x0def0060, "ror%20's%c\t%12-15r, %q"},
+ {ARM_EXT_V1, 0x01c00000, 0x0de00000, "bic%20's%c\t%12-15r, %16-19r, %o"},
+ {ARM_EXT_V1, 0x01e00000, 0x0de00000, "mvn%20's%c\t%12-15r, %o"},
+ {ARM_EXT_V1, 0x052d0004, 0x0fff0fff, "push%c\t{%12-15r}\t\t; (str%c %12-15r, %a)"},
+ {ARM_EXT_V1, 0x04000000, 0x0e100000, "str%22'b%t%c\t%12-15r, %a"},
+ {ARM_EXT_V1, 0x06000000, 0x0e100ff0, "str%22'b%t%c\t%12-15r, %a"},
+ {ARM_EXT_V1, 0x04000000, 0x0c100010, "str%22'b%t%c\t%12-15r, %a"},
+ {ARM_EXT_V1, 0x06000010, 0x0e000010, "undefined"},
+ {ARM_EXT_V1, 0x049d0004, 0x0fff0fff, "pop%c\t{%12-15r}\t\t; (ldr%c %12-15r, %a)"},
+ {ARM_EXT_V1, 0x04100000, 0x0c100000, "ldr%22'b%t%c\t%12-15r, %a"},
+ {ARM_EXT_V1, 0x092d0000, 0x0fff0000, "push%c\t%m"},
+ {ARM_EXT_V1, 0x08800000, 0x0ff00000, "stm%c\t%16-19r%21'!, %m%22'^"},
+ {ARM_EXT_V1, 0x08000000, 0x0e100000, "stm%23?id%24?ba%c\t%16-19r%21'!, %m%22'^"},
+ {ARM_EXT_V1, 0x08bd0000, 0x0fff0000, "pop%c\t%m"},
+ {ARM_EXT_V1, 0x08900000, 0x0f900000, "ldm%c\t%16-19r%21'!, %m%22'^"},
+ {ARM_EXT_V1, 0x08100000, 0x0e100000, "ldm%23?id%24?ba%c\t%16-19r%21'!, %m%22'^"},
+ {ARM_EXT_V1, 0x0a000000, 0x0e000000, "b%24'l%c\t%b"},
+ {ARM_EXT_V1, 0x0f000000, 0x0f000000, "svc%c\t%0-23x"},
+
+ /* The rest. */
+ {ARM_EXT_V1, 0x00000000, 0x00000000, "undefined instruction %0-31x"},
+ {0, 0x00000000, 0x00000000, 0}
+};
+
+/* print_insn_thumb16 recognizes the following format control codes:
+
+ %S print Thumb register (bits 3..5 as high number if bit 6 set)
+ %D print Thumb register (bits 0..2 as high number if bit 7 set)
+ %<bitfield>I print bitfield as a signed decimal
+ (top bit of range being the sign bit)
+ %N print Thumb register mask (with LR)
+ %O print Thumb register mask (with PC)
+ %M print Thumb register mask
+ %b print CZB's 6-bit unsigned branch destination
+ %s print Thumb right-shift immediate (6..10; 0 == 32).
+ %c print the condition code
+ %C print the condition code, or "s" if not conditional
+ %x print warning if conditional an not at end of IT block"
+ %X print "\t; unpredictable <IT:code>" if conditional
+ %I print IT instruction suffix and operands
+ %<bitfield>r print bitfield as an ARM register
+ %<bitfield>d print bitfield as a decimal
+ %<bitfield>H print (bitfield * 2) as a decimal
+ %<bitfield>W print (bitfield * 4) as a decimal
+ %<bitfield>a print (bitfield * 4) as a pc-rel offset + decoded symbol
+ %<bitfield>B print Thumb branch destination (signed displacement)
+ %<bitfield>c print bitfield as a condition code
+ %<bitnum>'c print specified char iff bit is one
+ %<bitnum>?ab print a if bit is one else print b. */
+
+static const struct opcode16 thumb_opcodes[] =
+{
+ /* Thumb instructions. */
+
+ /* ARM V6K no-argument instructions. */
+ {ARM_EXT_V6K, 0xbf00, 0xffff, "nop%c"},
+ {ARM_EXT_V6K, 0xbf10, 0xffff, "yield%c"},
+ {ARM_EXT_V6K, 0xbf20, 0xffff, "wfe%c"},
+ {ARM_EXT_V6K, 0xbf30, 0xffff, "wfi%c"},
+ {ARM_EXT_V6K, 0xbf40, 0xffff, "sev%c"},
+ {ARM_EXT_V6K, 0xbf00, 0xff0f, "nop%c\t{%4-7d}"},
+
+ /* ARM V6T2 instructions. */
+ {ARM_EXT_V6T2, 0xb900, 0xfd00, "cbnz\t%0-2r, %b%X"},
+ {ARM_EXT_V6T2, 0xb100, 0xfd00, "cbz\t%0-2r, %b%X"},
+ {ARM_EXT_V6T2, 0xbf00, 0xff00, "it%I%X"},
+
+ /* ARM V6. */
+ {ARM_EXT_V6, 0xb660, 0xfff8, "cpsie\t%2'a%1'i%0'f%X"},
+ {ARM_EXT_V6, 0xb670, 0xfff8, "cpsid\t%2'a%1'i%0'f%X"},
+ {ARM_EXT_V6, 0x4600, 0xffc0, "mov%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xba00, 0xffc0, "rev%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xba40, 0xffc0, "rev16%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xbac0, 0xffc0, "revsh%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xb650, 0xfff7, "setend\t%3?ble%X"},
+ {ARM_EXT_V6, 0xb200, 0xffc0, "sxth%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xb240, 0xffc0, "sxtb%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xb280, 0xffc0, "uxth%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V6, 0xb2c0, 0xffc0, "uxtb%c\t%0-2r, %3-5r"},
+
+ /* ARM V5 ISA extends Thumb. */
+ {ARM_EXT_V5T, 0xbe00, 0xff00, "bkpt\t%0-7x"}, /* Is always unconditional. */
+ /* This is BLX(2). BLX(1) is a 32-bit instruction. */
+ {ARM_EXT_V5T, 0x4780, 0xff87, "blx%c\t%3-6r%x"}, /* note: 4 bit register number. */
+ /* ARM V4T ISA (Thumb v1). */
+ {ARM_EXT_V4T, 0x46C0, 0xFFFF, "nop%c\t\t\t(mov r8, r8)"},
+ /* Format 4. */
+ {ARM_EXT_V4T, 0x4000, 0xFFC0, "and%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4040, 0xFFC0, "eor%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4080, 0xFFC0, "lsl%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x40C0, 0xFFC0, "lsr%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4100, 0xFFC0, "asr%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4140, 0xFFC0, "adc%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4180, 0xFFC0, "sbc%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x41C0, 0xFFC0, "ror%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4200, 0xFFC0, "tst%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4240, 0xFFC0, "neg%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4280, 0xFFC0, "cmp%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x42C0, 0xFFC0, "cmn%c\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4300, 0xFFC0, "orr%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4340, 0xFFC0, "mul%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x4380, 0xFFC0, "bic%C\t%0-2r, %3-5r"},
+ {ARM_EXT_V4T, 0x43C0, 0xFFC0, "mvn%C\t%0-2r, %3-5r"},
+ /* format 13 */
+ {ARM_EXT_V4T, 0xB000, 0xFF80, "add%c\tsp, #%0-6W"},
+ {ARM_EXT_V4T, 0xB080, 0xFF80, "sub%c\tsp, #%0-6W"},
+ /* format 5 */
+ {ARM_EXT_V4T, 0x4700, 0xFF80, "bx%c\t%S%x"},
+ {ARM_EXT_V4T, 0x4400, 0xFF00, "add%c\t%D, %S"},
+ {ARM_EXT_V4T, 0x4500, 0xFF00, "cmp%c\t%D, %S"},
+ {ARM_EXT_V4T, 0x4600, 0xFF00, "mov%c\t%D, %S"},
+ /* format 14 */
+ {ARM_EXT_V4T, 0xB400, 0xFE00, "push%c\t%N"},
+ {ARM_EXT_V4T, 0xBC00, 0xFE00, "pop%c\t%O"},
+ /* format 2 */
+ {ARM_EXT_V4T, 0x1800, 0xFE00, "add%C\t%0-2r, %3-5r, %6-8r"},
+ {ARM_EXT_V4T, 0x1A00, 0xFE00, "sub%C\t%0-2r, %3-5r, %6-8r"},
+ {ARM_EXT_V4T, 0x1C00, 0xFE00, "add%C\t%0-2r, %3-5r, #%6-8d"},
+ {ARM_EXT_V4T, 0x1E00, 0xFE00, "sub%C\t%0-2r, %3-5r, #%6-8d"},
+ /* format 8 */
+ {ARM_EXT_V4T, 0x5200, 0xFE00, "strh%c\t%0-2r, [%3-5r, %6-8r]"},
+ {ARM_EXT_V4T, 0x5A00, 0xFE00, "ldrh%c\t%0-2r, [%3-5r, %6-8r]"},
+ {ARM_EXT_V4T, 0x5600, 0xF600, "ldrs%11?hb%c\t%0-2r, [%3-5r, %6-8r]"},
+ /* format 7 */
+ {ARM_EXT_V4T, 0x5000, 0xFA00, "str%10'b%c\t%0-2r, [%3-5r, %6-8r]"},
+ {ARM_EXT_V4T, 0x5800, 0xFA00, "ldr%10'b%c\t%0-2r, [%3-5r, %6-8r]"},
+ /* format 1 */
+ {ARM_EXT_V4T, 0x0000, 0xF800, "lsl%C\t%0-2r, %3-5r, #%6-10d"},
+ {ARM_EXT_V4T, 0x0800, 0xF800, "lsr%C\t%0-2r, %3-5r, %s"},
+ {ARM_EXT_V4T, 0x1000, 0xF800, "asr%C\t%0-2r, %3-5r, %s"},
+ /* format 3 */
+ {ARM_EXT_V4T, 0x2000, 0xF800, "mov%C\t%8-10r, #%0-7d"},
+ {ARM_EXT_V4T, 0x2800, 0xF800, "cmp%c\t%8-10r, #%0-7d"},
+ {ARM_EXT_V4T, 0x3000, 0xF800, "add%C\t%8-10r, #%0-7d"},
+ {ARM_EXT_V4T, 0x3800, 0xF800, "sub%C\t%8-10r, #%0-7d"},
+ /* format 6 */
+ {ARM_EXT_V4T, 0x4800, 0xF800, "ldr%c\t%8-10r, [pc, #%0-7W]\t(%0-7a)"}, /* TODO: Disassemble PC relative "LDR rD,=<symbolic>" */
+ /* format 9 */
+ {ARM_EXT_V4T, 0x6000, 0xF800, "str%c\t%0-2r, [%3-5r, #%6-10W]"},
+ {ARM_EXT_V4T, 0x6800, 0xF800, "ldr%c\t%0-2r, [%3-5r, #%6-10W]"},
+ {ARM_EXT_V4T, 0x7000, 0xF800, "strb%c\t%0-2r, [%3-5r, #%6-10d]"},
+ {ARM_EXT_V4T, 0x7800, 0xF800, "ldrb%c\t%0-2r, [%3-5r, #%6-10d]"},
+ /* format 10 */
+ {ARM_EXT_V4T, 0x8000, 0xF800, "strh%c\t%0-2r, [%3-5r, #%6-10H]"},
+ {ARM_EXT_V4T, 0x8800, 0xF800, "ldrh%c\t%0-2r, [%3-5r, #%6-10H]"},
+ /* format 11 */
+ {ARM_EXT_V4T, 0x9000, 0xF800, "str%c\t%8-10r, [sp, #%0-7W]"},
+ {ARM_EXT_V4T, 0x9800, 0xF800, "ldr%c\t%8-10r, [sp, #%0-7W]"},
+ /* format 12 */
+ {ARM_EXT_V4T, 0xA000, 0xF800, "add%c\t%8-10r, pc, #%0-7W\t(adr %8-10r, %0-7a)"},
+ {ARM_EXT_V4T, 0xA800, 0xF800, "add%c\t%8-10r, sp, #%0-7W"},
+ /* format 15 */
+ {ARM_EXT_V4T, 0xC000, 0xF800, "stmia%c\t%8-10r!, %M"},
+ {ARM_EXT_V4T, 0xC800, 0xF800, "ldmia%c\t%8-10r!, %M"},
+ /* format 17 */
+ {ARM_EXT_V4T, 0xDF00, 0xFF00, "svc%c\t%0-7d"},
+ /* format 16 */
+ {ARM_EXT_V4T, 0xDE00, 0xFE00, "undefined"},
+ {ARM_EXT_V4T, 0xD000, 0xF000, "b%8-11c.n\t%0-7B%X"},
+ /* format 18 */
+ {ARM_EXT_V4T, 0xE000, 0xF800, "b%c.n\t%0-10B%x"},
+
+ /* The E800 .. FFFF range is unconditionally redirected to the
+ 32-bit table, because even in pre-V6T2 ISAs, BL and BLX(1) pairs
+ are processed via that table. Thus, we can never encounter a
+ bare "second half of BL/BLX(1)" instruction here. */
+ {ARM_EXT_V1, 0x0000, 0x0000, "undefined"},
+ {0, 0, 0, 0}
+};
+
+/* Thumb32 opcodes use the same table structure as the ARM opcodes.
+ We adopt the convention that hw1 is the high 16 bits of .value and
+ .mask, hw2 the low 16 bits.
+
+ print_insn_thumb32 recognizes the following format control codes:
+
+ %% %
+
+ %I print a 12-bit immediate from hw1[10],hw2[14:12,7:0]
+ %M print a modified 12-bit immediate (same location)
+ %J print a 16-bit immediate from hw1[3:0,10],hw2[14:12,7:0]
+ %K print a 16-bit immediate from hw2[3:0],hw1[3:0],hw2[11:4]
+ %S print a possibly-shifted Rm
+
+ %a print the address of a plain load/store
+ %w print the width and signedness of a core load/store
+ %m print register mask for ldm/stm
+
+ %E print the lsb and width fields of a bfc/bfi instruction
+ %F print the lsb and width fields of a sbfx/ubfx instruction
+ %b print a conditional branch offset
+ %B print an unconditional branch offset
+ %s print the shift field of an SSAT instruction
+ %R print the rotation field of an SXT instruction
+ %U print barrier type.
+ %P print address for pli instruction.
+ %c print the condition code
+ %x print warning if conditional an not at end of IT block"
+ %X print "\t; unpredictable <IT:code>" if conditional
+
+ %<bitfield>d print bitfield in decimal
+ %<bitfield>W print bitfield*4 in decimal
+ %<bitfield>r print bitfield as an ARM register
+ %<bitfield>c print bitfield as a condition code
+
+ %<bitfield>'c print specified char iff bitfield is all ones
+ %<bitfield>`c print specified char iff bitfield is all zeroes
+ %<bitfield>?ab... select from array of values in big endian order
+
+ With one exception at the bottom (done because BL and BLX(1) need
+ to come dead last), this table was machine-sorted first in
+ decreasing order of number of bits set in the mask, then in
+ increasing numeric order of mask, then in increasing numeric order
+ of opcode. This order is not the clearest for a human reader, but
+ is guaranteed never to catch a special-case bit pattern with a more
+ general mask, which is important, because this instruction encoding
+ makes heavy use of special-case bit patterns. */
+static const struct opcode32 thumb32_opcodes[] =
+{
+ /* V7 instructions. */
+ {ARM_EXT_V7, 0xf910f000, 0xff70f000, "pli%c\t%a"},
+ {ARM_EXT_V7, 0xf3af80f0, 0xfffffff0, "dbg%c\t#%0-3d"},
+ {ARM_EXT_V7, 0xf3bf8f50, 0xfffffff0, "dmb%c\t%U"},
+ {ARM_EXT_V7, 0xf3bf8f40, 0xfffffff0, "dsb%c\t%U"},
+ {ARM_EXT_V7, 0xf3bf8f60, 0xfffffff0, "isb%c\t%U"},
+ {ARM_EXT_DIV, 0xfb90f0f0, 0xfff0f0f0, "sdiv%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_DIV, 0xfbb0f0f0, 0xfff0f0f0, "udiv%c\t%8-11r, %16-19r, %0-3r"},
+
+ /* Instructions defined in the basic V6T2 set. */
+ {ARM_EXT_V6T2, 0xf3af8000, 0xffffffff, "nop%c.w"},
+ {ARM_EXT_V6T2, 0xf3af8001, 0xffffffff, "yield%c.w"},
+ {ARM_EXT_V6T2, 0xf3af8002, 0xffffffff, "wfe%c.w"},
+ {ARM_EXT_V6T2, 0xf3af8003, 0xffffffff, "wfi%c.w"},
+ {ARM_EXT_V6T2, 0xf3af9004, 0xffffffff, "sev%c.w"},
+ {ARM_EXT_V6T2, 0xf3af8000, 0xffffff00, "nop%c.w\t{%0-7d}"},
+
+ {ARM_EXT_V6T2, 0xf3bf8f2f, 0xffffffff, "clrex%c"},
+ {ARM_EXT_V6T2, 0xf3af8400, 0xffffff1f, "cpsie.w\t%7'a%6'i%5'f%X"},
+ {ARM_EXT_V6T2, 0xf3af8600, 0xffffff1f, "cpsid.w\t%7'a%6'i%5'f%X"},
+ {ARM_EXT_V6T2, 0xf3c08f00, 0xfff0ffff, "bxj%c\t%16-19r%x"},
+ {ARM_EXT_V6T2, 0xe810c000, 0xffd0ffff, "rfedb%c\t%16-19r%21'!"},
+ {ARM_EXT_V6T2, 0xe990c000, 0xffd0ffff, "rfeia%c\t%16-19r%21'!"},
+ {ARM_EXT_V6T2, 0xf3ef8000, 0xffeff000, "mrs%c\t%8-11r, %D"},
+ {ARM_EXT_V6T2, 0xf3af8100, 0xffffffe0, "cps\t#%0-4d%X"},
+ {ARM_EXT_V6T2, 0xe8d0f000, 0xfff0fff0, "tbb%c\t[%16-19r, %0-3r]%x"},
+ {ARM_EXT_V6T2, 0xe8d0f010, 0xfff0fff0, "tbh%c\t[%16-19r, %0-3r, lsl #1]%x"},
+ {ARM_EXT_V6T2, 0xf3af8500, 0xffffff00, "cpsie\t%7'a%6'i%5'f, #%0-4d%X"},
+ {ARM_EXT_V6T2, 0xf3af8700, 0xffffff00, "cpsid\t%7'a%6'i%5'f, #%0-4d%X"},
+ {ARM_EXT_V6T2, 0xf3de8f00, 0xffffff00, "subs%c\tpc, lr, #%0-7d"},
+ {ARM_EXT_V6T2, 0xf3808000, 0xffe0f000, "msr%c\t%C, %16-19r"},
+ {ARM_EXT_V6T2, 0xe8500f00, 0xfff00fff, "ldrex%c\t%12-15r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xe8d00f4f, 0xfff00fef, "ldrex%4?hb%c\t%12-15r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xe800c000, 0xffd0ffe0, "srsdb%c\t%16-19r%21'!, #%0-4d"},
+ {ARM_EXT_V6T2, 0xe980c000, 0xffd0ffe0, "srsia%c\t%16-19r%21'!, #%0-4d"},
+ {ARM_EXT_V6T2, 0xfa0ff080, 0xfffff0c0, "sxth%c.w\t%8-11r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa1ff080, 0xfffff0c0, "uxth%c.w\t%8-11r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa2ff080, 0xfffff0c0, "sxtb16%c\t%8-11r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa3ff080, 0xfffff0c0, "uxtb16%c\t%8-11r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa4ff080, 0xfffff0c0, "sxtb%c.w\t%8-11r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa5ff080, 0xfffff0c0, "uxtb%c.w\t%8-11r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xe8400000, 0xfff000ff, "strex%c\t%8-11r, %12-15r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xe8d0007f, 0xfff000ff, "ldrexd%c\t%12-15r, %8-11r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xfa80f000, 0xfff0f0f0, "sadd8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa80f010, 0xfff0f0f0, "qadd8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa80f020, 0xfff0f0f0, "shadd8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa80f040, 0xfff0f0f0, "uadd8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa80f050, 0xfff0f0f0, "uqadd8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa80f060, 0xfff0f0f0, "uhadd8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa80f080, 0xfff0f0f0, "qadd%c\t%8-11r, %0-3r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa80f090, 0xfff0f0f0, "qdadd%c\t%8-11r, %0-3r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa80f0a0, 0xfff0f0f0, "qsub%c\t%8-11r, %0-3r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa80f0b0, 0xfff0f0f0, "qdsub%c\t%8-11r, %0-3r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa90f000, 0xfff0f0f0, "sadd16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa90f010, 0xfff0f0f0, "qadd16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa90f020, 0xfff0f0f0, "shadd16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa90f040, 0xfff0f0f0, "uadd16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa90f050, 0xfff0f0f0, "uqadd16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa90f060, 0xfff0f0f0, "uhadd16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa90f080, 0xfff0f0f0, "rev%c.w\t%8-11r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa90f090, 0xfff0f0f0, "rev16%c.w\t%8-11r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa90f0a0, 0xfff0f0f0, "rbit%c\t%8-11r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfa90f0b0, 0xfff0f0f0, "revsh%c.w\t%8-11r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfaa0f000, 0xfff0f0f0, "saddsubx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfaa0f010, 0xfff0f0f0, "qaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfaa0f020, 0xfff0f0f0, "shaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfaa0f040, 0xfff0f0f0, "uaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfaa0f050, 0xfff0f0f0, "uqaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfaa0f060, 0xfff0f0f0, "uhaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfaa0f080, 0xfff0f0f0, "sel%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfab0f080, 0xfff0f0f0, "clz%c\t%8-11r, %16-19r"},
+ {ARM_EXT_V6T2, 0xfac0f000, 0xfff0f0f0, "ssub8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfac0f010, 0xfff0f0f0, "qsub8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfac0f020, 0xfff0f0f0, "shsub8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfac0f040, 0xfff0f0f0, "usub8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfac0f050, 0xfff0f0f0, "uqsub8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfac0f060, 0xfff0f0f0, "uhsub8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfad0f000, 0xfff0f0f0, "ssub16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfad0f010, 0xfff0f0f0, "qsub16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfad0f020, 0xfff0f0f0, "shsub16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfad0f040, 0xfff0f0f0, "usub16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfad0f050, 0xfff0f0f0, "uqsub16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfad0f060, 0xfff0f0f0, "uhsub16%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfae0f000, 0xfff0f0f0, "ssubaddx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfae0f010, 0xfff0f0f0, "qsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfae0f020, 0xfff0f0f0, "shsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfae0f040, 0xfff0f0f0, "usubaddx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfae0f050, 0xfff0f0f0, "uqsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfae0f060, 0xfff0f0f0, "uhsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfb00f000, 0xfff0f0f0, "mul%c.w\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfb70f000, 0xfff0f0f0, "usad8%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa00f000, 0xffe0f0f0, "lsl%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa20f000, 0xffe0f0f0, "lsr%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa40f000, 0xffe0f0f0, "asr%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa60f000, 0xffe0f0f0, "ror%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xe8c00f40, 0xfff00fe0, "strex%4?hb%c\t%0-3r, %12-15r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xf3200000, 0xfff0f0e0, "ssat16%c\t%8-11r, #%0-4d, %16-19r"},
+ {ARM_EXT_V6T2, 0xf3a00000, 0xfff0f0e0, "usat16%c\t%8-11r, #%0-4d, %16-19r"},
+ {ARM_EXT_V6T2, 0xfb20f000, 0xfff0f0e0, "smuad%4'x%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfb30f000, 0xfff0f0e0, "smulw%4?tb%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfb40f000, 0xfff0f0e0, "smusd%4'x%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfb50f000, 0xfff0f0e0, "smmul%4'r%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfa00f080, 0xfff0f0c0, "sxtah%c\t%8-11r, %16-19r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa10f080, 0xfff0f0c0, "uxtah%c\t%8-11r, %16-19r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa20f080, 0xfff0f0c0, "sxtab16%c\t%8-11r, %16-19r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa30f080, 0xfff0f0c0, "uxtab16%c\t%8-11r, %16-19r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa40f080, 0xfff0f0c0, "sxtab%c\t%8-11r, %16-19r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfa50f080, 0xfff0f0c0, "uxtab%c\t%8-11r, %16-19r, %0-3r%R"},
+ {ARM_EXT_V6T2, 0xfb10f000, 0xfff0f0c0, "smul%5?tb%4?tb%c\t%8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xf36f0000, 0xffff8020, "bfc%c\t%8-11r, %E"},
+ {ARM_EXT_V6T2, 0xea100f00, 0xfff08f00, "tst%c.w\t%16-19r, %S"},
+ {ARM_EXT_V6T2, 0xea900f00, 0xfff08f00, "teq%c\t%16-19r, %S"},
+ {ARM_EXT_V6T2, 0xeb100f00, 0xfff08f00, "cmn%c.w\t%16-19r, %S"},
+ {ARM_EXT_V6T2, 0xebb00f00, 0xfff08f00, "cmp%c.w\t%16-19r, %S"},
+ {ARM_EXT_V6T2, 0xf0100f00, 0xfbf08f00, "tst%c.w\t%16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf0900f00, 0xfbf08f00, "teq%c\t%16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1100f00, 0xfbf08f00, "cmn%c.w\t%16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1b00f00, 0xfbf08f00, "cmp%c.w\t%16-19r, %M"},
+ {ARM_EXT_V6T2, 0xea4f0000, 0xffef8000, "mov%20's%c.w\t%8-11r, %S"},
+ {ARM_EXT_V6T2, 0xea6f0000, 0xffef8000, "mvn%20's%c.w\t%8-11r, %S"},
+ {ARM_EXT_V6T2, 0xe8c00070, 0xfff000f0, "strexd%c\t%0-3r, %12-15r, %8-11r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xfb000000, 0xfff000f0, "mla%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb000010, 0xfff000f0, "mls%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb700000, 0xfff000f0, "usada8%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb800000, 0xfff000f0, "smull%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfba00000, 0xfff000f0, "umull%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfbc00000, 0xfff000f0, "smlal%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfbe00000, 0xfff000f0, "umlal%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfbe00060, 0xfff000f0, "umaal%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xe8500f00, 0xfff00f00, "ldrex%c\t%12-15r, [%16-19r, #%0-7W]"},
+ {ARM_EXT_V6T2, 0xf7f08000, 0xfff0f000, "smc%c\t%K"},
+ {ARM_EXT_V6T2, 0xf04f0000, 0xfbef8000, "mov%20's%c.w\t%8-11r, %M"},
+ {ARM_EXT_V6T2, 0xf06f0000, 0xfbef8000, "mvn%20's%c.w\t%8-11r, %M"},
+ {ARM_EXT_V6T2, 0xf810f000, 0xff70f000, "pld%c\t%a"},
+ {ARM_EXT_V6T2, 0xfb200000, 0xfff000e0, "smlad%4'x%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb300000, 0xfff000e0, "smlaw%4?tb%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb400000, 0xfff000e0, "smlsd%4'x%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb500000, 0xfff000e0, "smmla%4'r%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfb600000, 0xfff000e0, "smmls%4'r%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfbc000c0, 0xfff000e0, "smlald%4'x%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xfbd000c0, 0xfff000e0, "smlsld%4'x%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xeac00000, 0xfff08030, "pkhbt%c\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xeac00020, 0xfff08030, "pkhtb%c\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xf3400000, 0xfff08020, "sbfx%c\t%8-11r, %16-19r, %F"},
+ {ARM_EXT_V6T2, 0xf3c00000, 0xfff08020, "ubfx%c\t%8-11r, %16-19r, %F"},
+ {ARM_EXT_V6T2, 0xf8000e00, 0xff900f00, "str%wt%c\t%12-15r, %a"},
+ {ARM_EXT_V6T2, 0xfb100000, 0xfff000c0, "smla%5?tb%4?tb%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+ {ARM_EXT_V6T2, 0xfbc00080, 0xfff000c0, "smlal%5?tb%4?tb%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+ {ARM_EXT_V6T2, 0xf3600000, 0xfff08020, "bfi%c\t%8-11r, %16-19r, %E"},
+ {ARM_EXT_V6T2, 0xf8100e00, 0xfe900f00, "ldr%wt%c\t%12-15r, %a"},
+ {ARM_EXT_V6T2, 0xf3000000, 0xffd08020, "ssat%c\t%8-11r, #%0-4d, %16-19r%s"},
+ {ARM_EXT_V6T2, 0xf3800000, 0xffd08020, "usat%c\t%8-11r, #%0-4d, %16-19r%s"},
+ {ARM_EXT_V6T2, 0xf2000000, 0xfbf08000, "addw%c\t%8-11r, %16-19r, %I"},
+ {ARM_EXT_V6T2, 0xf2400000, 0xfbf08000, "movw%c\t%8-11r, %J"},
+ {ARM_EXT_V6T2, 0xf2a00000, 0xfbf08000, "subw%c\t%8-11r, %16-19r, %I"},
+ {ARM_EXT_V6T2, 0xf2c00000, 0xfbf08000, "movt%c\t%8-11r, %J"},
+ {ARM_EXT_V6T2, 0xea000000, 0xffe08000, "and%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xea200000, 0xffe08000, "bic%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xea400000, 0xffe08000, "orr%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xea600000, 0xffe08000, "orn%20's%c\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xea800000, 0xffe08000, "eor%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xeb000000, 0xffe08000, "add%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xeb400000, 0xffe08000, "adc%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xeb600000, 0xffe08000, "sbc%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xeba00000, 0xffe08000, "sub%20's%c.w\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xebc00000, 0xffe08000, "rsb%20's%c\t%8-11r, %16-19r, %S"},
+ {ARM_EXT_V6T2, 0xe8400000, 0xfff00000, "strex%c\t%8-11r, %12-15r, [%16-19r, #%0-7W]"},
+ {ARM_EXT_V6T2, 0xf0000000, 0xfbe08000, "and%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf0200000, 0xfbe08000, "bic%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf0400000, 0xfbe08000, "orr%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf0600000, 0xfbe08000, "orn%20's%c\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf0800000, 0xfbe08000, "eor%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1000000, 0xfbe08000, "add%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1400000, 0xfbe08000, "adc%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1600000, 0xfbe08000, "sbc%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1a00000, 0xfbe08000, "sub%20's%c.w\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xf1c00000, 0xfbe08000, "rsb%20's%c\t%8-11r, %16-19r, %M"},
+ {ARM_EXT_V6T2, 0xe8800000, 0xffd00000, "stmia%c.w\t%16-19r%21'!, %m"},
+ {ARM_EXT_V6T2, 0xe8900000, 0xffd00000, "ldmia%c.w\t%16-19r%21'!, %m"},
+ {ARM_EXT_V6T2, 0xe9000000, 0xffd00000, "stmdb%c\t%16-19r%21'!, %m"},
+ {ARM_EXT_V6T2, 0xe9100000, 0xffd00000, "ldmdb%c\t%16-19r%21'!, %m"},
+ {ARM_EXT_V6T2, 0xe9c00000, 0xffd000ff, "strd%c\t%12-15r, %8-11r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xe9d00000, 0xffd000ff, "ldrd%c\t%12-15r, %8-11r, [%16-19r]"},
+ {ARM_EXT_V6T2, 0xe9400000, 0xff500000, "strd%c\t%12-15r, %8-11r, [%16-19r, #%23`-%0-7W]%21'!"},
+ {ARM_EXT_V6T2, 0xe9500000, 0xff500000, "ldrd%c\t%12-15r, %8-11r, [%16-19r, #%23`-%0-7W]%21'!"},
+ {ARM_EXT_V6T2, 0xe8600000, 0xff700000, "strd%c\t%12-15r, %8-11r, [%16-19r], #%23`-%0-7W"},
+ {ARM_EXT_V6T2, 0xe8700000, 0xff700000, "ldrd%c\t%12-15r, %8-11r, [%16-19r], #%23`-%0-7W"},
+ {ARM_EXT_V6T2, 0xf8000000, 0xff100000, "str%w%c.w\t%12-15r, %a"},
+ {ARM_EXT_V6T2, 0xf8100000, 0xfe100000, "ldr%w%c.w\t%12-15r, %a"},
+
+ /* Filter out Bcc with cond=E or F, which are used for other instructions. */
+ {ARM_EXT_V6T2, 0xf3c08000, 0xfbc0d000, "undefined (bcc, cond=0xF)"},
+ {ARM_EXT_V6T2, 0xf3808000, 0xfbc0d000, "undefined (bcc, cond=0xE)"},
+ {ARM_EXT_V6T2, 0xf0008000, 0xf800d000, "b%22-25c.w\t%b%X"},
+ {ARM_EXT_V6T2, 0xf0009000, 0xf800d000, "b%c.w\t%B%x"},
+
+ /* These have been 32-bit since the invention of Thumb. */
+ {ARM_EXT_V4T, 0xf000c000, 0xf800d000, "blx%c\t%B%x"},
+ {ARM_EXT_V4T, 0xf000d000, 0xf800d000, "bl%c\t%B%x"},
+
+ /* Fallback. */
+ {ARM_EXT_V1, 0x00000000, 0x00000000, "undefined"},
+ {0, 0, 0, 0}
+};
+
+static const char *const arm_conditional[] =
+{"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt", "gt", "le", "al", "<und>", ""};
+
+static const char *const arm_fp_const[] =
+{"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0"};
+
+static const char *const arm_shift[] =
+{"lsl", "lsr", "asr", "ror"};
+
+typedef struct
+{
+ const char *name;
+ const char *description;
+ const char *reg_names[16];
+}
+arm_regname;
+
+static const arm_regname regnames[] =
+{
+ { "raw" , "Select raw register names",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}},
+ { "gcc", "Select register names used by GCC",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc" }},
+ { "std", "Select register names used in ARM's ISA documentation",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc" }},
+ { "apcs", "Select register names used in the APCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "sl", "fp", "ip", "sp", "lr", "pc" }},
+ { "atpcs", "Select register names used in the ATPCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "IP", "SP", "LR", "PC" }},
+ { "special-atpcs", "Select special register names used in the ATPCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL", "FP", "IP", "SP", "LR", "PC" }},
+};
+
+static const char *const iwmmxt_wwnames[] =
+{"b", "h", "w", "d"};
+
+static const char *const iwmmxt_wwssnames[] =
+{"b", "bus", "bc", "bss",
+ "h", "hus", "hc", "hss",
+ "w", "wus", "wc", "wss",
+ "d", "dus", "dc", "dss"
+};
+
+static const char *const iwmmxt_regnames[] =
+{ "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7",
+ "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15"
+};
+
+static const char *const iwmmxt_cregnames[] =
+{ "wcid", "wcon", "wcssf", "wcasf", "reserved", "reserved", "reserved", "reserved",
+ "wcgr0", "wcgr1", "wcgr2", "wcgr3", "reserved", "reserved", "reserved", "reserved"
+};
+
+/* Default to GCC register name set. */
+static unsigned int regname_selected = 1;
+
+#define NUM_ARM_REGNAMES NUM_ELEM (regnames)
+#define arm_regnames regnames[regname_selected].reg_names
+
+static bfd_boolean force_thumb = false;
+
+/* Current IT instruction state. This contains the same state as the IT
+ bits in the CPSR. */
+static unsigned int ifthen_state;
+/* IT state for the next instruction. */
+static unsigned int ifthen_next_state;
+/* The address of the insn for which the IT state is valid. */
+static bfd_vma ifthen_address;
+#define IFTHEN_COND ((ifthen_state >> 4) & 0xf)
+
+/* Cached mapping symbol state. */
+enum map_type {
+ MAP_ARM,
+ MAP_THUMB,
+ MAP_DATA
+};
+
+enum map_type last_type;
+int last_mapping_sym = -1;
+bfd_vma last_mapping_addr = 0;
+
+/* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?.
+ Returns pointer to following character of the format string and
+ fills in *VALUEP and *WIDTHP with the extracted value and number of
+ bits extracted. WIDTHP can be NULL. */
+
+static const char *
+arm_decode_bitfield (const char *ptr, unsigned long insn,
+ unsigned long *valuep, int *widthp)
+{
+ unsigned long value = 0;
+ int width = 0;
+
+ do
+ {
+ int start, end;
+ int bits;
+
+ for (start = 0; *ptr >= '0' && *ptr <= '9'; ptr++)
+ start = start * 10 + *ptr - '0';
+ if (*ptr == '-')
+ for (end = 0, ptr++; *ptr >= '0' && *ptr <= '9'; ptr++)
+ end = end * 10 + *ptr - '0';
+ else
+ end = start;
+ bits = end - start;
+ if (bits < 0)
+ abort ();
+ value |= ((insn >> start) & ((2ul << bits) - 1)) << width;
+ width += bits + 1;
+ }
+ while (*ptr++ == ',');
+ *valuep = value;
+ if (widthp)
+ *widthp = width;
+ return ptr - 1;
+}
+
+static void
+arm_decode_shift (long given, fprintf_function func, void *stream,
+ int print_shift)
+{
+ func (stream, "%s", arm_regnames[given & 0xf]);
+
+ if ((given & 0xff0) != 0)
+ {
+ if ((given & 0x10) == 0)
+ {
+ int amount = (given & 0xf80) >> 7;
+ int shift = (given & 0x60) >> 5;
+
+ if (amount == 0)
+ {
+ if (shift == 3)
+ {
+ func (stream, ", rrx");
+ return;
+ }
+
+ amount = 32;
+ }
+
+ if (print_shift)
+ func (stream, ", %s #%d", arm_shift[shift], amount);
+ else
+ func (stream, ", #%d", amount);
+ }
+ else if (print_shift)
+ func (stream, ", %s %s", arm_shift[(given & 0x60) >> 5],
+ arm_regnames[(given & 0xf00) >> 8]);
+ else
+ func (stream, ", %s", arm_regnames[(given & 0xf00) >> 8]);
+ }
+}
+
+/* Print one coprocessor instruction on INFO->STREAM.
+ Return true if the instuction matched, false if this is not a
+ recognised coprocessor instruction. */
+
+static bfd_boolean
+print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given,
+ bfd_boolean thumb)
+{
+ const struct opcode32 *insn;
+ void *stream = info->stream;
+ fprintf_function func = info->fprintf_func;
+ unsigned long mask;
+ unsigned long value;
+ int cond;
+
+ for (insn = coprocessor_opcodes; insn->assembler; insn++)
+ {
+ if (insn->value == FIRST_IWMMXT_INSN
+ && info->mach != bfd_mach_arm_XScale
+ && info->mach != bfd_mach_arm_iWMMXt
+ && info->mach != bfd_mach_arm_iWMMXt2)
+ insn = insn + IWMMXT_INSN_COUNT;
+
+ mask = insn->mask;
+ value = insn->value;
+ if (thumb)
+ {
+ /* The high 4 bits are 0xe for Arm conditional instructions, and
+ 0xe for arm unconditional instructions. The rest of the
+ encoding is the same. */
+ mask |= 0xf0000000;
+ value |= 0xe0000000;
+ if (ifthen_state)
+ cond = IFTHEN_COND;
+ else
+ cond = 16;
+ }
+ else
+ {
+ /* Only match unconditional instuctions against unconditional
+ patterns. */
+ if ((given & 0xf0000000) == 0xf0000000)
+ {
+ mask |= 0xf0000000;
+ cond = 16;
+ }
+ else
+ {
+ cond = (given >> 28) & 0xf;
+ if (cond == 0xe)
+ cond = 16;
+ }
+ }
+ if ((given & mask) == value)
+ {
+ const char *c;
+
+ for (c = insn->assembler; *c; c++)
+ {
+ if (*c == '%')
+ {
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'A':
+ func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+
+ if ((given & (1 << 24)) != 0)
+ {
+ int offset = given & 0xff;
+
+ if (offset)
+ func (stream, ", #%s%d]%s",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * 4,
+ ((given & 0x00200000) != 0 ? "!" : ""));
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ int offset = given & 0xff;
+
+ func (stream, "]");
+
+ if (given & (1 << 21))
+ {
+ if (offset)
+ func (stream, ", #%s%d",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * 4);
+ }
+ else
+ func (stream, ", {%d}", offset);
+ }
+ break;
+
+ case 'B':
+ {
+ int regno = ((given >> 12) & 0xf) | ((given >> (22 - 4)) & 0x10);
+ int offset = (given >> 1) & 0x3f;
+
+ if (offset == 1)
+ func (stream, "{d%d}", regno);
+ else if (regno + offset > 32)
+ func (stream, "{d%d-<overflow reg d%d>}", regno, regno + offset - 1);
+ else
+ func (stream, "{d%d-d%d}", regno, regno + offset - 1);
+ }
+ break;
+
+ case 'C':
+ {
+ int rn = (given >> 16) & 0xf;
+ int offset = (given & 0xff) * 4;
+ int add = (given >> 23) & 1;
+
+ func (stream, "[%s", arm_regnames[rn]);
+
+ if (offset)
+ {
+ if (!add)
+ offset = -offset;
+ func (stream, ", #%d", offset);
+ }
+ func (stream, "]");
+ if (rn == 15)
+ {
+ func (stream, "\t; ");
+ /* FIXME: Unsure if info->bytes_per_chunk is the
+ right thing to use here. */
+ info->print_address_func (offset + pc
+ + info->bytes_per_chunk * 2, info);
+ }
+ }
+ break;
+
+ case 'c':
+ func (stream, "%s", arm_conditional[cond]);
+ break;
+
+ case 'I':
+ /* Print a Cirrus/DSP shift immediate. */
+ /* Immediates are 7bit signed ints with bits 0..3 in
+ bits 0..3 of opcode and bits 4..6 in bits 5..7
+ of opcode. */
+ {
+ int imm;
+
+ imm = (given & 0xf) | ((given & 0xe0) >> 1);
+
+ /* Is ``imm'' a negative number? */
+ if (imm & 0x40)
+ imm |= (-1 << 7);
+
+ func (stream, "%d", imm);
+ }
+
+ break;
+
+ case 'F':
+ switch (given & 0x00408000)
+ {
+ case 0:
+ func (stream, "4");
+ break;
+ case 0x8000:
+ func (stream, "1");
+ break;
+ case 0x00400000:
+ func (stream, "2");
+ break;
+ default:
+ func (stream, "3");
+ }
+ break;
+
+ case 'P':
+ switch (given & 0x00080080)
+ {
+ case 0:
+ func (stream, "s");
+ break;
+ case 0x80:
+ func (stream, "d");
+ break;
+ case 0x00080000:
+ func (stream, "e");
+ break;
+ default:
+ func (stream, _("<illegal precision>"));
+ break;
+ }
+ break;
+ case 'Q':
+ switch (given & 0x00408000)
+ {
+ case 0:
+ func (stream, "s");
+ break;
+ case 0x8000:
+ func (stream, "d");
+ break;
+ case 0x00400000:
+ func (stream, "e");
+ break;
+ default:
+ func (stream, "p");
+ break;
+ }
+ break;
+ case 'R':
+ switch (given & 0x60)
+ {
+ case 0:
+ break;
+ case 0x20:
+ func (stream, "p");
+ break;
+ case 0x40:
+ func (stream, "m");
+ break;
+ default:
+ func (stream, "z");
+ break;
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int width;
+ unsigned long value;
+
+ c = arm_decode_bitfield (c, given, &value, &width);
+
+ switch (*c)
+ {
+ case 'r':
+ func (stream, "%s", arm_regnames[value]);
+ break;
+ case 'D':
+ func (stream, "d%ld", value);
+ break;
+ case 'Q':
+ if (value & 1)
+ func (stream, "<illegal reg q%ld.5>", value >> 1);
+ else
+ func (stream, "q%ld", value >> 1);
+ break;
+ case 'd':
+ func (stream, "%ld", value);
+ break;
+ case 'k':
+ {
+ int from = (given & (1 << 7)) ? 32 : 16;
+ func (stream, "%ld", from - value);
+ }
+ break;
+
+ case 'f':
+ if (value > 7)
+ func (stream, "#%s", arm_fp_const[value & 7]);
+ else
+ func (stream, "f%ld", value);
+ break;
+
+ case 'w':
+ if (width == 2)
+ func (stream, "%s", iwmmxt_wwnames[value]);
+ else
+ func (stream, "%s", iwmmxt_wwssnames[value]);
+ break;
+
+ case 'g':
+ func (stream, "%s", iwmmxt_regnames[value]);
+ break;
+ case 'G':
+ func (stream, "%s", iwmmxt_cregnames[value]);
+ break;
+
+ case 'x':
+ func (stream, "0x%lx", value);
+ break;
+
+ case '`':
+ c++;
+ if (value == 0)
+ func (stream, "%c", *c);
+ break;
+ case '\'':
+ c++;
+ if (value == ((1ul << width) - 1))
+ func (stream, "%c", *c);
+ break;
+ case '?':
+ func (stream, "%c", c[(1 << width) - (int)value]);
+ c += 1 << width;
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case 'y':
+ case 'z':
+ {
+ int single = *c++ == 'y';
+ int regno;
+
+ switch (*c)
+ {
+ case '4': /* Sm pair */
+ func (stream, "{");
+ /* Fall through. */
+ case '0': /* Sm, Dm */
+ regno = given & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 5) & 1;
+ }
+ else
+ regno += ((given >> 5) & 1) << 4;
+ break;
+
+ case '1': /* Sd, Dd */
+ regno = (given >> 12) & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 22) & 1;
+ }
+ else
+ regno += ((given >> 22) & 1) << 4;
+ break;
+
+ case '2': /* Sn, Dn */
+ regno = (given >> 16) & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 7) & 1;
+ }
+ else
+ regno += ((given >> 7) & 1) << 4;
+ break;
+
+ case '3': /* List */
+ func (stream, "{");
+ regno = (given >> 12) & 0x0000000f;
+ if (single)
+ {
+ regno <<= 1;
+ regno += (given >> 22) & 1;
+ }
+ else
+ regno += ((given >> 22) & 1) << 4;
+ break;
+
+ default:
+ abort ();
+ }
+
+ func (stream, "%c%d", single ? 's' : 'd', regno);
+
+ if (*c == '3')
+ {
+ int count = given & 0xff;
+
+ if (single == 0)
+ count >>= 1;
+
+ if (--count)
+ {
+ func (stream, "-%c%d",
+ single ? 's' : 'd',
+ regno + count);
+ }
+
+ func (stream, "}");
+ }
+ else if (*c == '4')
+ func (stream, ", %c%d}", single ? 's' : 'd',
+ regno + 1);
+ }
+ break;
+
+ case 'L':
+ switch (given & 0x00400100)
+ {
+ case 0x00000000: func (stream, "b"); break;
+ case 0x00400000: func (stream, "h"); break;
+ case 0x00000100: func (stream, "w"); break;
+ case 0x00400100: func (stream, "d"); break;
+ default:
+ break;
+ }
+ break;
+
+ case 'Z':
+ {
+ int value;
+ /* given (20, 23) | given (0, 3) */
+ value = ((given >> 16) & 0xf0) | (given & 0xf);
+ func (stream, "%d", value);
+ }
+ break;
+
+ case 'l':
+ /* This is like the 'A' operator, except that if
+ the width field "M" is zero, then the offset is
+ *not* multiplied by four. */
+ {
+ int offset = given & 0xff;
+ int multiplier = (given & 0x00000100) ? 4 : 1;
+
+ func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+
+ if (offset)
+ {
+ if ((given & 0x01000000) != 0)
+ func (stream, ", #%s%d]%s",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * multiplier,
+ ((given & 0x00200000) != 0 ? "!" : ""));
+ else
+ func (stream, "], #%s%d",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * multiplier);
+ }
+ else
+ func (stream, "]");
+ }
+ break;
+
+ case 'r':
+ {
+ int imm4 = (given >> 4) & 0xf;
+ int puw_bits = ((given >> 22) & 6) | ((given >> 21) & 1);
+ int ubit = (given >> 23) & 1;
+ const char *rm = arm_regnames [given & 0xf];
+ const char *rn = arm_regnames [(given >> 16) & 0xf];
+
+ switch (puw_bits)
+ {
+ case 1:
+ /* fall through */
+ case 3:
+ func (stream, "[%s], %c%s", rn, ubit ? '+' : '-', rm);
+ if (imm4)
+ func (stream, ", lsl #%d", imm4);
+ break;
+
+ case 4:
+ /* fall through */
+ case 5:
+ /* fall through */
+ case 6:
+ /* fall through */
+ case 7:
+ func (stream, "[%s, %c%s", rn, ubit ? '+' : '-', rm);
+ if (imm4 > 0)
+ func (stream, ", lsl #%d", imm4);
+ func (stream, "]");
+ if (puw_bits == 5 || puw_bits == 7)
+ func (stream, "!");
+ break;
+
+ default:
+ func (stream, "INVALID");
+ }
+ }
+ break;
+
+ case 'i':
+ {
+ long imm5;
+ imm5 = ((given & 0x100) >> 4) | (given & 0xf);
+ func (stream, "%ld", (imm5 == 0) ? 32 : imm5);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+ else
+ func (stream, "%c", *c);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
+{
+ void *stream = info->stream;
+ fprintf_function func = info->fprintf_func;
+
+ if (((given & 0x000f0000) == 0x000f0000)
+ && ((given & 0x02000000) == 0))
+ {
+ int offset = given & 0xfff;
+
+ func (stream, "[pc");
+
+ if (given & 0x01000000)
+ {
+ if ((given & 0x00800000) == 0)
+ offset = - offset;
+
+ /* Pre-indexed. */
+ func (stream, ", #%d]", offset);
+
+ offset += pc + 8;
+
+ /* Cope with the possibility of write-back
+ being used. Probably a very dangerous thing
+ for the programmer to do, but who are we to
+ argue ? */
+ if (given & 0x00200000)
+ func (stream, "!");
+ }
+ else
+ {
+ /* Post indexed. */
+ func (stream, "], #%d", offset);
+
+ /* ie ignore the offset. */
+ offset = pc + 8;
+ }
+
+ func (stream, "\t; ");
+ info->print_address_func (offset, info);
+ }
+ else
+ {
+ func (stream, "[%s",
+ arm_regnames[(given >> 16) & 0xf]);
+ if ((given & 0x01000000) != 0)
+ {
+ if ((given & 0x02000000) == 0)
+ {
+ int offset = given & 0xfff;
+ if (offset)
+ func (stream, ", #%s%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ }
+ else
+ {
+ func (stream, ", %s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""));
+ arm_decode_shift (given, func, stream, 1);
+ }
+
+ func (stream, "]%s",
+ ((given & 0x00200000) != 0) ? "!" : "");
+ }
+ else
+ {
+ if ((given & 0x02000000) == 0)
+ {
+ int offset = given & 0xfff;
+ if (offset)
+ func (stream, "], #%s%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ func (stream, "], %s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""));
+ arm_decode_shift (given, func, stream, 1);
+ }
+ }
+ }
+}
+
+/* Print one neon instruction on INFO->STREAM.
+ Return true if the instuction matched, false if this is not a
+ recognised neon instruction. */
+
+static bfd_boolean
+print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
+{
+ const struct opcode32 *insn;
+ void *stream = info->stream;
+ fprintf_function func = info->fprintf_func;
+
+ if (thumb)
+ {
+ if ((given & 0xef000000) == 0xef000000)
+ {
+ /* move bit 28 to bit 24 to translate Thumb2 to ARM encoding. */
+ unsigned long bit28 = given & (1 << 28);
+
+ given &= 0x00ffffff;
+ if (bit28)
+ given |= 0xf3000000;
+ else
+ given |= 0xf2000000;
+ }
+ else if ((given & 0xff000000) == 0xf9000000)
+ given ^= 0xf9000000 ^ 0xf4000000;
+ else
+ return false;
+ }
+
+ for (insn = neon_opcodes; insn->assembler; insn++)
+ {
+ if ((given & insn->mask) == insn->value)
+ {
+ const char *c;
+
+ for (c = insn->assembler; *c; c++)
+ {
+ if (*c == '%')
+ {
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'c':
+ if (thumb && ifthen_state)
+ func (stream, "%s", arm_conditional[IFTHEN_COND]);
+ break;
+
+ case 'A':
+ {
+ static const unsigned char enc[16] =
+ {
+ 0x4, 0x14, /* st4 0,1 */
+ 0x4, /* st1 2 */
+ 0x4, /* st2 3 */
+ 0x3, /* st3 4 */
+ 0x13, /* st3 5 */
+ 0x3, /* st1 6 */
+ 0x1, /* st1 7 */
+ 0x2, /* st2 8 */
+ 0x12, /* st2 9 */
+ 0x2, /* st1 10 */
+ 0, 0, 0, 0, 0
+ };
+ int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4);
+ int rn = ((given >> 16) & 0xf);
+ int rm = ((given >> 0) & 0xf);
+ int align = ((given >> 4) & 0x3);
+ int type = ((given >> 8) & 0xf);
+ int n = enc[type] & 0xf;
+ int stride = (enc[type] >> 4) + 1;
+ int ix;
+
+ func (stream, "{");
+ if (stride > 1)
+ for (ix = 0; ix != n; ix++)
+ func (stream, "%sd%d", ix ? "," : "", rd + ix * stride);
+ else if (n == 1)
+ func (stream, "d%d", rd);
+ else
+ func (stream, "d%d-d%d", rd, rd + n - 1);
+ func (stream, "}, [%s", arm_regnames[rn]);
+ if (align)
+ func (stream, ", :%d", 32 << align);
+ func (stream, "]");
+ if (rm == 0xd)
+ func (stream, "!");
+ else if (rm != 0xf)
+ func (stream, ", %s", arm_regnames[rm]);
+ }
+ break;
+
+ case 'B':
+ {
+ int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4);
+ int rn = ((given >> 16) & 0xf);
+ int rm = ((given >> 0) & 0xf);
+ int idx_align = ((given >> 4) & 0xf);
+ int align = 0;
+ int size = ((given >> 10) & 0x3);
+ int idx = idx_align >> (size + 1);
+ int length = ((given >> 8) & 3) + 1;
+ int stride = 1;
+ int i;
+
+ if (length > 1 && size > 0)
+ stride = (idx_align & (1 << size)) ? 2 : 1;
+
+ switch (length)
+ {
+ case 1:
+ {
+ int amask = (1 << size) - 1;
+ if ((idx_align & (1 << size)) != 0)
+ return false;
+ if (size > 0)
+ {
+ if ((idx_align & amask) == amask)
+ align = 8 << size;
+ else if ((idx_align & amask) != 0)
+ return false;
+ }
+ }
+ break;
+
+ case 2:
+ if (size == 2 && (idx_align & 2) != 0)
+ return false;
+ align = (idx_align & 1) ? 16 << size : 0;
+ break;
+
+ case 3:
+ if ((size == 2 && (idx_align & 3) != 0)
+ || (idx_align & 1) != 0)
+ return false;
+ break;
+
+ case 4:
+ if (size == 2)
+ {
+ if ((idx_align & 3) == 3)
+ return false;
+ align = (idx_align & 3) * 64;
+ }
+ else
+ align = (idx_align & 1) ? 32 << size : 0;
+ break;
+
+ default:
+ abort ();
+ }
+
+ func (stream, "{");
+ for (i = 0; i < length; i++)
+ func (stream, "%sd%d[%d]", (i == 0) ? "" : ",",
+ rd + i * stride, idx);
+ func (stream, "}, [%s", arm_regnames[rn]);
+ if (align)
+ func (stream, ", :%d", align);
+ func (stream, "]");
+ if (rm == 0xd)
+ func (stream, "!");
+ else if (rm != 0xf)
+ func (stream, ", %s", arm_regnames[rm]);
+ }
+ break;
+
+ case 'C':
+ {
+ int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4);
+ int rn = ((given >> 16) & 0xf);
+ int rm = ((given >> 0) & 0xf);
+ int align = ((given >> 4) & 0x1);
+ int size = ((given >> 6) & 0x3);
+ int type = ((given >> 8) & 0x3);
+ int n = type + 1;
+ int stride = ((given >> 5) & 0x1);
+ int ix;
+
+ if (stride && (n == 1))
+ n++;
+ else
+ stride++;
+
+ func (stream, "{");
+ if (stride > 1)
+ for (ix = 0; ix != n; ix++)
+ func (stream, "%sd%d[]", ix ? "," : "", rd + ix * stride);
+ else if (n == 1)
+ func (stream, "d%d[]", rd);
+ else
+ func (stream, "d%d[]-d%d[]", rd, rd + n - 1);
+ func (stream, "}, [%s", arm_regnames[rn]);
+ if (align)
+ {
+ int align = (8 * (type + 1)) << size;
+ if (type == 3)
+ align = (size > 1) ? align >> 1 : align;
+ if (type == 2 || (type == 0 && !size))
+ func (stream, ", :<bad align %d>", align);
+ else
+ func (stream, ", :%d", align);
+ }
+ func (stream, "]");
+ if (rm == 0xd)
+ func (stream, "!");
+ else if (rm != 0xf)
+ func (stream, ", %s", arm_regnames[rm]);
+ }
+ break;
+
+ case 'D':
+ {
+ int raw_reg = (given & 0xf) | ((given >> 1) & 0x10);
+ int size = (given >> 20) & 3;
+ int reg = raw_reg & ((4 << size) - 1);
+ int ix = raw_reg >> size >> 2;
+
+ func (stream, "d%d[%d]", reg, ix);
+ }
+ break;
+
+ case 'E':
+ /* Neon encoded constant for mov, mvn, vorr, vbic */
+ {
+ int bits = 0;
+ int cmode = (given >> 8) & 0xf;
+ int op = (given >> 5) & 0x1;
+ unsigned long value = 0, hival = 0;
+ unsigned shift;
+ int size = 0;
+ int isfloat = 0;
+
+ bits |= ((given >> 24) & 1) << 7;
+ bits |= ((given >> 16) & 7) << 4;
+ bits |= ((given >> 0) & 15) << 0;
+
+ if (cmode < 8)
+ {
+ shift = (cmode >> 1) & 3;
+ value = (unsigned long)bits << (8 * shift);
+ size = 32;
+ }
+ else if (cmode < 12)
+ {
+ shift = (cmode >> 1) & 1;
+ value = (unsigned long)bits << (8 * shift);
+ size = 16;
+ }
+ else if (cmode < 14)
+ {
+ shift = (cmode & 1) + 1;
+ value = (unsigned long)bits << (8 * shift);
+ value |= (1ul << (8 * shift)) - 1;
+ size = 32;
+ }
+ else if (cmode == 14)
+ {
+ if (op)
+ {
+ /* bit replication into bytes */
+ int ix;
+ unsigned long mask;
+
+ value = 0;
+ hival = 0;
+ for (ix = 7; ix >= 0; ix--)
+ {
+ mask = ((bits >> ix) & 1) ? 0xff : 0;
+ if (ix <= 3)
+ value = (value << 8) | mask;
+ else
+ hival = (hival << 8) | mask;
+ }
+ size = 64;
+ }
+ else
+ {
+ /* byte replication */
+ value = (unsigned long)bits;
+ size = 8;
+ }
+ }
+ else if (!op)
+ {
+ /* floating point encoding */
+ int tmp;
+
+ value = (unsigned long)(bits & 0x7f) << 19;
+ value |= (unsigned long)(bits & 0x80) << 24;
+ tmp = bits & 0x40 ? 0x3c : 0x40;
+ value |= (unsigned long)tmp << 24;
+ size = 32;
+ isfloat = 1;
+ }
+ else
+ {
+ func (stream, "<illegal constant %.8x:%x:%x>",
+ bits, cmode, op);
+ break;
+ }
+ switch (size)
+ {
+ case 8:
+ func (stream, "#%ld\t; 0x%.2lx", value, value);
+ break;
+
+ case 16:
+ func (stream, "#%ld\t; 0x%.4lx", value, value);
+ break;
+
+ case 32:
+ if (isfloat)
+ {
+ unsigned char valbytes[4];
+ double fvalue;
+
+ /* Do this a byte at a time so we don't have to
+ worry about the host's endianness. */
+ valbytes[0] = value & 0xff;
+ valbytes[1] = (value >> 8) & 0xff;
+ valbytes[2] = (value >> 16) & 0xff;
+ valbytes[3] = (value >> 24) & 0xff;
+
+ floatformat_to_double (valbytes, &fvalue);
+
+ func (stream, "#%.7g\t; 0x%.8lx", fvalue,
+ value);
+ }
+ else
+ func (stream, "#%ld\t; 0x%.8lx",
+ (long) ((value & 0x80000000)
+ ? value | ~0xffffffffl : value), value);
+ break;
+
+ case 64:
+ func (stream, "#0x%.8lx%.8lx", hival, value);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ break;
+
+ case 'F':
+ {
+ int regno = ((given >> 16) & 0xf) | ((given >> (7 - 4)) & 0x10);
+ int num = (given >> 8) & 0x3;
+
+ if (!num)
+ func (stream, "{d%d}", regno);
+ else if (num + regno >= 32)
+ func (stream, "{d%d-<overflow reg d%d}", regno, regno + num);
+ else
+ func (stream, "{d%d-d%d}", regno, regno + num);
+ }
+ break;
+
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int width;
+ unsigned long value;
+
+ c = arm_decode_bitfield (c, given, &value, &width);
+
+ switch (*c)
+ {
+ case 'r':
+ func (stream, "%s", arm_regnames[value]);
+ break;
+ case 'd':
+ func (stream, "%ld", value);
+ break;
+ case 'e':
+ func (stream, "%ld", (1ul << width) - value);
+ break;
+
+ case 'S':
+ case 'T':
+ case 'U':
+ /* various width encodings */
+ {
+ int base = 8 << (*c - 'S'); /* 8,16 or 32 */
+ int limit;
+ unsigned low, high;
+
+ c++;
+ if (*c >= '0' && *c <= '9')
+ limit = *c - '0';
+ else if (*c >= 'a' && *c <= 'f')
+ limit = *c - 'a' + 10;
+ else
+ abort ();
+ low = limit >> 2;
+ high = limit & 3;
+
+ if (value < low || value > high)
+ func (stream, "<illegal width %d>", base << value);
+ else
+ func (stream, "%d", base << value);
+ }
+ break;
+ case 'R':
+ if (given & (1 << 6))
+ goto Q;
+ /* FALLTHROUGH */
+ case 'D':
+ func (stream, "d%ld", value);
+ break;
+ case 'Q':
+ Q:
+ if (value & 1)
+ func (stream, "<illegal reg q%ld.5>", value >> 1);
+ else
+ func (stream, "q%ld", value >> 1);
+ break;
+
+ case '`':
+ c++;
+ if (value == 0)
+ func (stream, "%c", *c);
+ break;
+ case '\'':
+ c++;
+ if (value == ((1ul << width) - 1))
+ func (stream, "%c", *c);
+ break;
+ case '?':
+ func (stream, "%c", c[(1 << width) - (int)value]);
+ c += 1 << width;
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+ else
+ func (stream, "%c", *c);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Print one ARM instruction from PC on INFO->STREAM. */
+
+static void
+print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given)
+{
+ const struct opcode32 *insn;
+ void *stream = info->stream;
+ fprintf_function func = info->fprintf_func;
+
+ if (print_insn_coprocessor (pc, info, given, false))
+ return;
+
+ if (print_insn_neon (info, given, false))
+ return;
+
+ for (insn = arm_opcodes; insn->assembler; insn++)
+ {
+ if (insn->value == FIRST_IWMMXT_INSN
+ && info->mach != bfd_mach_arm_XScale
+ && info->mach != bfd_mach_arm_iWMMXt)
+ insn = insn + IWMMXT_INSN_COUNT;
+
+ if ((given & insn->mask) == insn->value
+ /* Special case: an instruction with all bits set in the condition field
+ (0xFnnn_nnnn) is only matched if all those bits are set in insn->mask,
+ or by the catchall at the end of the table. */
+ && ((given & 0xF0000000) != 0xF0000000
+ || (insn->mask & 0xF0000000) == 0xF0000000
+ || (insn->mask == 0 && insn->value == 0)))
+ {
+ const char *c;
+
+ for (c = insn->assembler; *c; c++)
+ {
+ if (*c == '%')
+ {
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'a':
+ print_arm_address (pc, info, given);
+ break;
+
+ case 'P':
+ /* Set P address bit and use normal address
+ printing routine. */
+ print_arm_address (pc, info, given | (1 << 24));
+ break;
+
+ case 's':
+ if ((given & 0x004f0000) == 0x004f0000)
+ {
+ /* PC relative with immediate offset. */
+ int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+
+ if ((given & 0x00800000) == 0)
+ offset = -offset;
+
+ func (stream, "[pc, #%d]\t; ", offset);
+ info->print_address_func (offset + pc + 8, info);
+ }
+ else
+ {
+ func (stream, "[%s",
+ arm_regnames[(given >> 16) & 0xf]);
+ if ((given & 0x01000000) != 0)
+ {
+ /* Pre-indexed. */
+ if ((given & 0x00400000) == 0x00400000)
+ {
+ /* Immediate. */
+ int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+ if (offset)
+ func (stream, ", #%s%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ }
+ else
+ {
+ /* Register. */
+ func (stream, ", %s%s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""),
+ arm_regnames[given & 0xf]);
+ }
+
+ func (stream, "]%s",
+ ((given & 0x00200000) != 0) ? "!" : "");
+ }
+ else
+ {
+ /* Post-indexed. */
+ if ((given & 0x00400000) == 0x00400000)
+ {
+ /* Immediate. */
+ int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+ if (offset)
+ func (stream, "], #%s%d",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""), offset);
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ /* Register. */
+ func (stream, "], %s%s",
+ (((given & 0x00800000) == 0)
+ ? "-" : ""),
+ arm_regnames[given & 0xf]);
+ }
+ }
+ }
+ break;
+
+ case 'b':
+ {
+ int disp = (((given & 0xffffff) ^ 0x800000) - 0x800000);
+ info->print_address_func (disp*4 + pc + 8, info);
+ }
+ break;
+
+ case 'c':
+ if (((given >> 28) & 0xf) != 0xe)
+ func (stream, "%s",
+ arm_conditional [(given >> 28) & 0xf]);
+ break;
+
+ case 'm':
+ {
+ int started = 0;
+ int reg;
+
+ func (stream, "{");
+ for (reg = 0; reg < 16; reg++)
+ if ((given & (1 << reg)) != 0)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ func (stream, "}");
+ }
+ break;
+
+ case 'q':
+ arm_decode_shift (given, func, stream, 0);
+ break;
+
+ case 'o':
+ if ((given & 0x02000000) != 0)
+ {
+ int rotate = (given & 0xf00) >> 7;
+ int immed = (given & 0xff);
+ immed = (((immed << (32 - rotate))
+ | (immed >> rotate)) & 0xffffffff);
+ func (stream, "#%d\t; 0x%x", immed, immed);
+ }
+ else
+ arm_decode_shift (given, func, stream, 1);
+ break;
+
+ case 'p':
+ if ((given & 0x0000f000) == 0x0000f000)
+ func (stream, "p");
+ break;
+
+ case 't':
+ if ((given & 0x01200000) == 0x00200000)
+ func (stream, "t");
+ break;
+
+ case 'A':
+ func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+
+ if ((given & (1 << 24)) != 0)
+ {
+ int offset = given & 0xff;
+
+ if (offset)
+ func (stream, ", #%s%d]%s",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * 4,
+ ((given & 0x00200000) != 0 ? "!" : ""));
+ else
+ func (stream, "]");
+ }
+ else
+ {
+ int offset = given & 0xff;
+
+ func (stream, "]");
+
+ if (given & (1 << 21))
+ {
+ if (offset)
+ func (stream, ", #%s%d",
+ ((given & 0x00800000) == 0 ? "-" : ""),
+ offset * 4);
+ }
+ else
+ func (stream, ", {%d}", offset);
+ }
+ break;
+
+ case 'B':
+ /* Print ARM V5 BLX(1) address: pc+25 bits. */
+ {
+ bfd_vma address;
+ bfd_vma offset = 0;
+
+ if (given & 0x00800000)
+ /* Is signed, hi bits should be ones. */
+ offset = (-1) ^ 0x00ffffff;
+
+ /* Offset is (SignExtend(offset field)<<2). */
+ offset += given & 0x00ffffff;
+ offset <<= 2;
+ address = offset + pc + 8;
+
+ if (given & 0x01000000)
+ /* H bit allows addressing to 2-byte boundaries. */
+ address += 2;
+
+ info->print_address_func (address, info);
+ }
+ break;
+
+ case 'C':
+ func (stream, "_");
+ if (given & 0x80000)
+ func (stream, "f");
+ if (given & 0x40000)
+ func (stream, "s");
+ if (given & 0x20000)
+ func (stream, "x");
+ if (given & 0x10000)
+ func (stream, "c");
+ break;
+
+ case 'U':
+ switch (given & 0xf)
+ {
+ case 0xf: func(stream, "sy"); break;
+ case 0x7: func(stream, "un"); break;
+ case 0xe: func(stream, "st"); break;
+ case 0x6: func(stream, "unst"); break;
+ default:
+ func(stream, "#%d", (int)given & 0xf);
+ break;
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int width;
+ unsigned long value;
+
+ c = arm_decode_bitfield (c, given, &value, &width);
+
+ switch (*c)
+ {
+ case 'r':
+ func (stream, "%s", arm_regnames[value]);
+ break;
+ case 'd':
+ func (stream, "%ld", value);
+ break;
+ case 'b':
+ func (stream, "%ld", value * 8);
+ break;
+ case 'W':
+ func (stream, "%ld", value + 1);
+ break;
+ case 'x':
+ func (stream, "0x%08lx", value);
+
+ /* Some SWI instructions have special
+ meanings. */
+ if ((given & 0x0fffffff) == 0x0FF00000)
+ func (stream, "\t; IMB");
+ else if ((given & 0x0fffffff) == 0x0FF00001)
+ func (stream, "\t; IMBRange");
+ break;
+ case 'X':
+ func (stream, "%01lx", value & 0xf);
+ break;
+ case '`':
+ c++;
+ if (value == 0)
+ func (stream, "%c", *c);
+ break;
+ case '\'':
+ c++;
+ if (value == ((1ul << width) - 1))
+ func (stream, "%c", *c);
+ break;
+ case '?':
+ func (stream, "%c", c[(1 << width) - (int)value]);
+ c += 1 << width;
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case 'e':
+ {
+ int imm;
+
+ imm = (given & 0xf) | ((given & 0xfff00) >> 4);
+ func (stream, "%d", imm);
+ }
+ break;
+
+ case 'E':
+ /* LSB and WIDTH fields of BFI or BFC. The machine-
+ language instruction encodes LSB and MSB. */
+ {
+ long msb = (given & 0x001f0000) >> 16;
+ long lsb = (given & 0x00000f80) >> 7;
+
+ long width = msb - lsb + 1;
+ if (width > 0)
+ func (stream, "#%lu, #%lu", lsb, width);
+ else
+ func (stream, "(invalid: %lu:%lu)", lsb, msb);
+ }
+ break;
+
+ case 'V':
+ /* 16-bit unsigned immediate from a MOVT or MOVW
+ instruction, encoded in bits 0:11 and 15:19. */
+ {
+ long hi = (given & 0x000f0000) >> 4;
+ long lo = (given & 0x00000fff);
+ long imm16 = hi | lo;
+ func (stream, "#%lu\t; 0x%lx", imm16, imm16);
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+ else
+ func (stream, "%c", *c);
+ }
+ return;
+ }
+ }
+ abort ();
+}
+
+/* Print one 16-bit Thumb instruction from PC on INFO->STREAM. */
+
+static void
+print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given)
+{
+ const struct opcode16 *insn;
+ void *stream = info->stream;
+ fprintf_function func = info->fprintf_func;
+
+ for (insn = thumb_opcodes; insn->assembler; insn++)
+ if ((given & insn->mask) == insn->value)
+ {
+ const char *c = insn->assembler;
+ for (; *c; c++)
+ {
+ int domaskpc = 0;
+ int domasklr = 0;
+
+ if (*c != '%')
+ {
+ func (stream, "%c", *c);
+ continue;
+ }
+
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'c':
+ if (ifthen_state)
+ func (stream, "%s", arm_conditional[IFTHEN_COND]);
+ break;
+
+ case 'C':
+ if (ifthen_state)
+ func (stream, "%s", arm_conditional[IFTHEN_COND]);
+ else
+ func (stream, "s");
+ break;
+
+ case 'I':
+ {
+ unsigned int tmp;
+
+ ifthen_next_state = given & 0xff;
+ for (tmp = given << 1; tmp & 0xf; tmp <<= 1)
+ func (stream, ((given ^ tmp) & 0x10) ? "e" : "t");
+ func (stream, "\t%s", arm_conditional[(given >> 4) & 0xf]);
+ }
+ break;
+
+ case 'x':
+ if (ifthen_next_state)
+ func (stream, "\t; unpredictable branch in IT block\n");
+ break;
+
+ case 'X':
+ if (ifthen_state)
+ func (stream, "\t; unpredictable <IT:%s>",
+ arm_conditional[IFTHEN_COND]);
+ break;
+
+ case 'S':
+ {
+ long reg;
+
+ reg = (given >> 3) & 0x7;
+ if (given & (1 << 6))
+ reg += 8;
+
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ break;
+
+ case 'D':
+ {
+ long reg;
+
+ reg = given & 0x7;
+ if (given & (1 << 7))
+ reg += 8;
+
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ break;
+
+ case 'N':
+ if (given & (1 << 8))
+ domasklr = 1;
+ /* Fall through. */
+ case 'O':
+ if (*c == 'O' && (given & (1 << 8)))
+ domaskpc = 1;
+ /* Fall through. */
+ case 'M':
+ {
+ int started = 0;
+ int reg;
+
+ func (stream, "{");
+
+ /* It would be nice if we could spot
+ ranges, and generate the rS-rE format: */
+ for (reg = 0; (reg < 8); reg++)
+ if ((given & (1 << reg)) != 0)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, "%s", arm_regnames[reg]);
+ }
+
+ if (domasklr)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, "%s", arm_regnames[14] /* "lr" */);
+ }
+
+ if (domaskpc)
+ {
+ if (started)
+ func (stream, ", ");
+ func (stream, "%s", arm_regnames[15] /* "pc" */);
+ }
+
+ func (stream, "}");
+ }
+ break;
+
+ case 'b':
+ /* Print ARM V6T2 CZB address: pc+4+6 bits. */
+ {
+ bfd_vma address = (pc + 4
+ + ((given & 0x00f8) >> 2)
+ + ((given & 0x0200) >> 3));
+ info->print_address_func (address, info);
+ }
+ break;
+
+ case 's':
+ /* Right shift immediate -- bits 6..10; 1-31 print
+ as themselves, 0 prints as 32. */
+ {
+ long imm = (given & 0x07c0) >> 6;
+ if (imm == 0)
+ imm = 32;
+ func (stream, "#%ld", imm);
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int bitstart = *c++ - '0';
+ int bitend = 0;
+
+ while (*c >= '0' && *c <= '9')
+ bitstart = (bitstart * 10) + *c++ - '0';
+
+ switch (*c)
+ {
+ case '-':
+ {
+ long reg;
+
+ c++;
+ while (*c >= '0' && *c <= '9')
+ bitend = (bitend * 10) + *c++ - '0';
+ if (!bitend)
+ abort ();
+ reg = given >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+ switch (*c)
+ {
+ case 'r':
+ func (stream, "%s", arm_regnames[reg]);
+ break;
+
+ case 'd':
+ func (stream, "%ld", reg);
+ break;
+
+ case 'H':
+ func (stream, "%ld", reg << 1);
+ break;
+
+ case 'W':
+ func (stream, "%ld", reg << 2);
+ break;
+
+ case 'a':
+ /* PC-relative address -- the bottom two
+ bits of the address are dropped
+ before the calculation. */
+ info->print_address_func
+ (((pc + 4) & ~3) + (reg << 2), info);
+ break;
+
+ case 'x':
+ func (stream, "0x%04lx", reg);
+ break;
+
+ case 'B':
+ reg = ((reg ^ (1 << bitend)) - (1 << bitend));
+ info->print_address_func (reg * 2 + pc + 4, info);
+ break;
+
+ case 'c':
+ func (stream, "%s", arm_conditional [reg]);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ break;
+
+ case '\'':
+ c++;
+ if ((given & (1 << bitstart)) != 0)
+ func (stream, "%c", *c);
+ break;
+
+ case '?':
+ ++c;
+ if ((given & (1 << bitstart)) != 0)
+ func (stream, "%c", *c++);
+ else
+ func (stream, "%c", *++c);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ return;
+ }
+
+ /* No match. */
+ abort ();
+}
+
+/* Return the name of an V7M special register. */
+static const char *
+psr_name (int regno)
+{
+ switch (regno)
+ {
+ case 0: return "APSR";
+ case 1: return "IAPSR";
+ case 2: return "EAPSR";
+ case 3: return "PSR";
+ case 5: return "IPSR";
+ case 6: return "EPSR";
+ case 7: return "IEPSR";
+ case 8: return "MSP";
+ case 9: return "PSP";
+ case 16: return "PRIMASK";
+ case 17: return "BASEPRI";
+ case 18: return "BASEPRI_MASK";
+ case 19: return "FAULTMASK";
+ case 20: return "CONTROL";
+ default: return "<unknown>";
+ }
+}
+
+/* Print one 32-bit Thumb instruction from PC on INFO->STREAM. */
+
+static void
+print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
+{
+ const struct opcode32 *insn;
+ void *stream = info->stream;
+ fprintf_function func = info->fprintf_func;
+
+ if (print_insn_coprocessor (pc, info, given, true))
+ return;
+
+ if (print_insn_neon (info, given, true))
+ return;
+
+ for (insn = thumb32_opcodes; insn->assembler; insn++)
+ if ((given & insn->mask) == insn->value)
+ {
+ const char *c = insn->assembler;
+ for (; *c; c++)
+ {
+ if (*c != '%')
+ {
+ func (stream, "%c", *c);
+ continue;
+ }
+
+ switch (*++c)
+ {
+ case '%':
+ func (stream, "%%");
+ break;
+
+ case 'c':
+ if (ifthen_state)
+ func (stream, "%s", arm_conditional[IFTHEN_COND]);
+ break;
+
+ case 'x':
+ if (ifthen_next_state)
+ func (stream, "\t; unpredictable branch in IT block\n");
+ break;
+
+ case 'X':
+ if (ifthen_state)
+ func (stream, "\t; unpredictable <IT:%s>",
+ arm_conditional[IFTHEN_COND]);
+ break;
+
+ case 'I':
+ {
+ unsigned int imm12 = 0;
+ imm12 |= (given & 0x000000ffu);
+ imm12 |= (given & 0x00007000u) >> 4;
+ imm12 |= (given & 0x04000000u) >> 15;
+ func (stream, "#%u\t; 0x%x", imm12, imm12);
+ }
+ break;
+
+ case 'M':
+ {
+ unsigned int bits = 0, imm, imm8, mod;
+ bits |= (given & 0x000000ffu);
+ bits |= (given & 0x00007000u) >> 4;
+ bits |= (given & 0x04000000u) >> 15;
+ imm8 = (bits & 0x0ff);
+ mod = (bits & 0xf00) >> 8;
+ switch (mod)
+ {
+ case 0: imm = imm8; break;
+ case 1: imm = ((imm8<<16) | imm8); break;
+ case 2: imm = ((imm8<<24) | (imm8 << 8)); break;
+ case 3: imm = ((imm8<<24) | (imm8 << 16) | (imm8 << 8) | imm8); break;
+ default:
+ mod = (bits & 0xf80) >> 7;
+ imm8 = (bits & 0x07f) | 0x80;
+ imm = (((imm8 << (32 - mod)) | (imm8 >> mod)) & 0xffffffff);
+ }
+ func (stream, "#%u\t; 0x%x", imm, imm);
+ }
+ break;
+
+ case 'J':
+ {
+ unsigned int imm = 0;
+ imm |= (given & 0x000000ffu);
+ imm |= (given & 0x00007000u) >> 4;
+ imm |= (given & 0x04000000u) >> 15;
+ imm |= (given & 0x000f0000u) >> 4;
+ func (stream, "#%u\t; 0x%x", imm, imm);
+ }
+ break;
+
+ case 'K':
+ {
+ unsigned int imm = 0;
+ imm |= (given & 0x000f0000u) >> 16;
+ imm |= (given & 0x00000ff0u) >> 0;
+ imm |= (given & 0x0000000fu) << 12;
+ func (stream, "#%u\t; 0x%x", imm, imm);
+ }
+ break;
+
+ case 'S':
+ {
+ unsigned int reg = (given & 0x0000000fu);
+ unsigned int stp = (given & 0x00000030u) >> 4;
+ unsigned int imm = 0;
+ imm |= (given & 0x000000c0u) >> 6;
+ imm |= (given & 0x00007000u) >> 10;
+
+ func (stream, "%s", arm_regnames[reg]);
+ switch (stp)
+ {
+ case 0:
+ if (imm > 0)
+ func (stream, ", lsl #%u", imm);
+ break;
+
+ case 1:
+ if (imm == 0)
+ imm = 32;
+ func (stream, ", lsr #%u", imm);
+ break;
+
+ case 2:
+ if (imm == 0)
+ imm = 32;
+ func (stream, ", asr #%u", imm);
+ break;
+
+ case 3:
+ if (imm == 0)
+ func (stream, ", rrx");
+ else
+ func (stream, ", ror #%u", imm);
+ }
+ }
+ break;
+
+ case 'a':
+ {
+ unsigned int Rn = (given & 0x000f0000) >> 16;
+ unsigned int U = (given & 0x00800000) >> 23;
+ unsigned int op = (given & 0x00000f00) >> 8;
+ unsigned int i12 = (given & 0x00000fff);
+ unsigned int i8 = (given & 0x000000ff);
+ bfd_boolean writeback = false, postind = false;
+ int offset = 0;
+
+ func (stream, "[%s", arm_regnames[Rn]);
+ if (U) /* 12-bit positive immediate offset */
+ offset = i12;
+ else if (Rn == 15) /* 12-bit negative immediate offset */
+ offset = -(int)i12;
+ else if (op == 0x0) /* shifted register offset */
+ {
+ unsigned int Rm = (i8 & 0x0f);
+ unsigned int sh = (i8 & 0x30) >> 4;
+ func (stream, ", %s", arm_regnames[Rm]);
+ if (sh)
+ func (stream, ", lsl #%u", sh);
+ func (stream, "]");
+ break;
+ }
+ else switch (op)
+ {
+ case 0xE: /* 8-bit positive immediate offset */
+ offset = i8;
+ break;
+
+ case 0xC: /* 8-bit negative immediate offset */
+ offset = -i8;
+ break;
+
+ case 0xF: /* 8-bit + preindex with wb */
+ offset = i8;
+ writeback = true;
+ break;
+
+ case 0xD: /* 8-bit - preindex with wb */
+ offset = -i8;
+ writeback = true;
+ break;
+
+ case 0xB: /* 8-bit + postindex */
+ offset = i8;
+ postind = true;
+ break;
+
+ case 0x9: /* 8-bit - postindex */
+ offset = -i8;
+ postind = true;
+ break;
+
+ default:
+ func (stream, ", <undefined>]");
+ goto skip;
+ }
+
+ if (postind)
+ func (stream, "], #%d", offset);
+ else
+ {
+ if (offset)
+ func (stream, ", #%d", offset);
+ func (stream, writeback ? "]!" : "]");
+ }
+
+ if (Rn == 15)
+ {
+ func (stream, "\t; ");
+ info->print_address_func (((pc + 4) & ~3) + offset, info);
+ }
+ }
+ skip:
+ break;
+
+ case 'A':
+ {
+ unsigned int P = (given & 0x01000000) >> 24;
+ unsigned int U = (given & 0x00800000) >> 23;
+ unsigned int W = (given & 0x00400000) >> 21;
+ unsigned int Rn = (given & 0x000f0000) >> 16;
+ unsigned int off = (given & 0x000000ff);
+
+ func (stream, "[%s", arm_regnames[Rn]);
+ if (P)
+ {
+ if (off || !U)
+ func (stream, ", #%c%u", U ? '+' : '-', off * 4);
+ func (stream, "]");
+ if (W)
+ func (stream, "!");
+ }
+ else
+ {
+ func (stream, "], ");
+ if (W)
+ func (stream, "#%c%u", U ? '+' : '-', off * 4);
+ else
+ func (stream, "{%u}", off);
+ }
+ }
+ break;
+
+ case 'w':
+ {
+ unsigned int Sbit = (given & 0x01000000) >> 24;
+ unsigned int type = (given & 0x00600000) >> 21;
+ switch (type)
+ {
+ case 0: func (stream, Sbit ? "sb" : "b"); break;
+ case 1: func (stream, Sbit ? "sh" : "h"); break;
+ case 2:
+ if (Sbit)
+ func (stream, "??");
+ break;
+ case 3:
+ func (stream, "??");
+ break;
+ }
+ }
+ break;
+
+ case 'm':
+ {
+ int started = 0;
+ int reg;
+
+ func (stream, "{");
+ for (reg = 0; reg < 16; reg++)
+ if ((given & (1 << reg)) != 0)
+ {
+ if (started)
+ func (stream, ", ");
+ started = 1;
+ func (stream, "%s", arm_regnames[reg]);
+ }
+ func (stream, "}");
+ }
+ break;
+
+ case 'E':
+ {
+ unsigned int msb = (given & 0x0000001f);
+ unsigned int lsb = 0;
+ lsb |= (given & 0x000000c0u) >> 6;
+ lsb |= (given & 0x00007000u) >> 10;
+ func (stream, "#%u, #%u", lsb, msb - lsb + 1);
+ }
+ break;
+
+ case 'F':
+ {
+ unsigned int width = (given & 0x0000001f) + 1;
+ unsigned int lsb = 0;
+ lsb |= (given & 0x000000c0u) >> 6;
+ lsb |= (given & 0x00007000u) >> 10;
+ func (stream, "#%u, #%u", lsb, width);
+ }
+ break;
+
+ case 'b':
+ {
+ unsigned int S = (given & 0x04000000u) >> 26;
+ unsigned int J1 = (given & 0x00002000u) >> 13;
+ unsigned int J2 = (given & 0x00000800u) >> 11;
+ int offset = 0;
+
+ offset |= !S << 20;
+ offset |= J2 << 19;
+ offset |= J1 << 18;
+ offset |= (given & 0x003f0000) >> 4;
+ offset |= (given & 0x000007ff) << 1;
+ offset -= (1 << 20);
+
+ info->print_address_func (pc + 4 + offset, info);
+ }
+ break;
+
+ case 'B':
+ {
+ unsigned int S = (given & 0x04000000u) >> 26;
+ unsigned int I1 = (given & 0x00002000u) >> 13;
+ unsigned int I2 = (given & 0x00000800u) >> 11;
+ int offset = 0;
+
+ offset |= !S << 24;
+ offset |= !(I1 ^ S) << 23;
+ offset |= !(I2 ^ S) << 22;
+ offset |= (given & 0x03ff0000u) >> 4;
+ offset |= (given & 0x000007ffu) << 1;
+ offset -= (1 << 24);
+ offset += pc + 4;
+
+ /* BLX target addresses are always word aligned. */
+ if ((given & 0x00001000u) == 0)
+ offset &= ~2u;
+
+ info->print_address_func (offset, info);
+ }
+ break;
+
+ case 's':
+ {
+ unsigned int shift = 0;
+ shift |= (given & 0x000000c0u) >> 6;
+ shift |= (given & 0x00007000u) >> 10;
+ if (given & 0x00200000u)
+ func (stream, ", asr #%u", shift);
+ else if (shift)
+ func (stream, ", lsl #%u", shift);
+ /* else print nothing - lsl #0 */
+ }
+ break;
+
+ case 'R':
+ {
+ unsigned int rot = (given & 0x00000030) >> 4;
+ if (rot)
+ func (stream, ", ror #%u", rot * 8);
+ }
+ break;
+
+ case 'U':
+ switch (given & 0xf)
+ {
+ case 0xf: func(stream, "sy"); break;
+ case 0x7: func(stream, "un"); break;
+ case 0xe: func(stream, "st"); break;
+ case 0x6: func(stream, "unst"); break;
+ default:
+ func(stream, "#%d", (int)given & 0xf);
+ break;
+ }
+ break;
+
+ case 'C':
+ if ((given & 0xff) == 0)
+ {
+ func (stream, "%cPSR_", (given & 0x100000) ? 'S' : 'C');
+ if (given & 0x800)
+ func (stream, "f");
+ if (given & 0x400)
+ func (stream, "s");
+ if (given & 0x200)
+ func (stream, "x");
+ if (given & 0x100)
+ func (stream, "c");
+ }
+ else
+ {
+ func (stream, "%s", psr_name (given & 0xff));
+ }
+ break;
+
+ case 'D':
+ if ((given & 0xff) == 0)
+ func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C');
+ else
+ func (stream, "%s", psr_name (given & 0xff));
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int width;
+ unsigned long val;
+
+ c = arm_decode_bitfield (c, given, &val, &width);
+
+ switch (*c)
+ {
+ case 'd': func (stream, "%lu", val); break;
+ case 'W': func (stream, "%lu", val * 4); break;
+ case 'r': func (stream, "%s", arm_regnames[val]); break;
+
+ case 'c':
+ func (stream, "%s", arm_conditional[val]);
+ break;
+
+ case '\'':
+ c++;
+ if (val == ((1ul << width) - 1))
+ func (stream, "%c", *c);
+ break;
+
+ case '`':
+ c++;
+ if (val == 0)
+ func (stream, "%c", *c);
+ break;
+
+ case '?':
+ func (stream, "%c", c[(1 << width) - (int)val]);
+ c += 1 << width;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ return;
+ }
+
+ /* No match. */
+ abort ();
+}
+
+/* Print data bytes on INFO->STREAM. */
+
+static void
+print_insn_data (bfd_vma pc ATTRIBUTE_UNUSED, struct disassemble_info *info,
+ long given)
+{
+ switch (info->bytes_per_chunk)
+ {
+ case 1:
+ info->fprintf_func (info->stream, ".byte\t0x%02lx", given);
+ break;
+ case 2:
+ info->fprintf_func (info->stream, ".short\t0x%04lx", given);
+ break;
+ case 4:
+ info->fprintf_func (info->stream, ".word\t0x%08lx", given);
+ break;
+ default:
+ abort ();
+ }
+}
+
+/* Search back through the insn stream to determine if this instruction is
+ conditionally executed. */
+static void
+find_ifthen_state (bfd_vma pc, struct disassemble_info *info,
+ bfd_boolean little)
+{
+ unsigned char b[2];
+ unsigned int insn;
+ int status;
+ /* COUNT is twice the number of instructions seen. It will be odd if we
+ just crossed an instruction boundary. */
+ int count;
+ int it_count;
+ unsigned int seen_it;
+ bfd_vma addr;
+
+ ifthen_address = pc;
+ ifthen_state = 0;
+
+ addr = pc;
+ count = 1;
+ it_count = 0;
+ seen_it = 0;
+ /* Scan backwards looking for IT instructions, keeping track of where
+ instruction boundaries are. We don't know if something is actually an
+ IT instruction until we find a definite instruction boundary. */
+ for (;;)
+ {
+ if (addr == 0 || info->symbol_at_address_func(addr, info))
+ {
+ /* A symbol must be on an instruction boundary, and will not
+ be within an IT block. */
+ if (seen_it && (count & 1))
+ break;
+
+ return;
+ }
+ addr -= 2;
+ status = info->read_memory_func (addr, (bfd_byte *)b, 2, info);
+ if (status)
+ return;
+
+ if (little)
+ insn = (b[0]) | (b[1] << 8);
+ else
+ insn = (b[1]) | (b[0] << 8);
+ if (seen_it)
+ {
+ if ((insn & 0xf800) < 0xe800)
+ {
+ /* Addr + 2 is an instruction boundary. See if this matches
+ the expected boundary based on the position of the last
+ IT candidate. */
+ if (count & 1)
+ break;
+ seen_it = 0;
+ }
+ }
+ if ((insn & 0xff00) == 0xbf00 && (insn & 0xf) != 0)
+ {
+ /* This could be an IT instruction. */
+ seen_it = insn;
+ it_count = count >> 1;
+ }
+ if ((insn & 0xf800) >= 0xe800)
+ count++;
+ else
+ count = (count + 2) | 1;
+ /* IT blocks contain at most 4 instructions. */
+ if (count >= 8 && !seen_it)
+ return;
+ }
+ /* We found an IT instruction. */
+ ifthen_state = (seen_it & 0xe0) | ((seen_it << it_count) & 0x1f);
+ if ((ifthen_state & 0xf) == 0)
+ ifthen_state = 0;
+}
+
+/* NOTE: There are no checks in these routines that
+ the relevant number of data bytes exist. */
+
+int
+print_insn_arm (bfd_vma pc, struct disassemble_info *info)
+{
+ unsigned char b[4];
+ long given;
+ int status;
+ int is_thumb = false;
+ int is_data = false;
+ unsigned int size = 4;
+ void (*printer) (bfd_vma, struct disassemble_info *, long);
+#if 0
+ bfd_boolean found = false;
+
+ if (info->disassembler_options)
+ {
+ parse_disassembler_options (info->disassembler_options);
+
+ /* To avoid repeated parsing of these options, we remove them here. */
+ info->disassembler_options = NULL;
+ }
+
+ /* First check the full symtab for a mapping symbol, even if there
+ are no usable non-mapping symbols for this address. */
+ if (info->symtab != NULL
+ && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour)
+ {
+ bfd_vma addr;
+ int n;
+ int last_sym = -1;
+ enum map_type type = MAP_ARM;
+
+ if (pc <= last_mapping_addr)
+ last_mapping_sym = -1;
+ is_thumb = (last_type == MAP_THUMB);
+ found = false;
+ /* Start scanning at the start of the function, or wherever
+ we finished last time. */
+ n = info->symtab_pos + 1;
+ if (n < last_mapping_sym)
+ n = last_mapping_sym;
+
+ /* Scan up to the location being disassembled. */
+ for (; n < info->symtab_size; n++)
+ {
+ addr = bfd_asymbol_value (info->symtab[n]);
+ if (addr > pc)
+ break;
+ if ((info->section == NULL
+ || info->section == info->symtab[n]->section)
+ && get_sym_code_type (info, n, &type))
+ {
+ last_sym = n;
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ n = info->symtab_pos;
+ if (n < last_mapping_sym - 1)
+ n = last_mapping_sym - 1;
+
+ /* No mapping symbol found at this address. Look backwards
+ for a preceeding one. */
+ for (; n >= 0; n--)
+ {
+ if (get_sym_code_type (info, n, &type))
+ {
+ last_sym = n;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ last_mapping_sym = last_sym;
+ last_type = type;
+ is_thumb = (last_type == MAP_THUMB);
+ is_data = (last_type == MAP_DATA);
+
+ /* Look a little bit ahead to see if we should print out
+ two or four bytes of data. If there's a symbol,
+ mapping or otherwise, after two bytes then don't
+ print more. */
+ if (is_data)
+ {
+ size = 4 - (pc & 3);
+ for (n = last_sym + 1; n < info->symtab_size; n++)
+ {
+ addr = bfd_asymbol_value (info->symtab[n]);
+ if (addr > pc)
+ {
+ if (addr - pc < size)
+ size = addr - pc;
+ break;
+ }
+ }
+ /* If the next symbol is after three bytes, we need to
+ print only part of the data, so that we can use either
+ .byte or .short. */
+ if (size == 3)
+ size = (pc & 1) ? 1 : 2;
+ }
+ }
+
+ if (info->symbols != NULL)
+ {
+ if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour)
+ {
+ coff_symbol_type * cs;
+
+ cs = coffsymbol (*info->symbols);
+ is_thumb = ( cs->native->u.syment.n_sclass == C_THUMBEXT
+ || cs->native->u.syment.n_sclass == C_THUMBSTAT
+ || cs->native->u.syment.n_sclass == C_THUMBLABEL
+ || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC
+ || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC);
+ }
+ else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour
+ && !found)
+ {
+ /* If no mapping symbol has been found then fall back to the type
+ of the function symbol. */
+ elf_symbol_type * es;
+ unsigned int type;
+
+ es = *(elf_symbol_type **)(info->symbols);
+ type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
+
+ is_thumb = (type == STT_ARM_TFUNC) || (type == STT_ARM_16BIT);
+ }
+ }
+#else
+ int little;
+
+ little = (info->endian == BFD_ENDIAN_LITTLE);
+ is_thumb |= (pc & 1);
+ pc &= ~(bfd_vma)1;
+#endif
+
+ if (force_thumb)
+ is_thumb = true;
+
+ info->bytes_per_line = 4;
+
+ if (is_data)
+ {
+ int i;
+
+ /* size was already set above. */
+ info->bytes_per_chunk = size;
+ printer = print_insn_data;
+
+ status = info->read_memory_func (pc, (bfd_byte *)b, size, info);
+ given = 0;
+ if (little)
+ for (i = size - 1; i >= 0; i--)
+ given = b[i] | (given << 8);
+ else
+ for (i = 0; i < (int) size; i++)
+ given = b[i] | (given << 8);
+ }
+ else if (!is_thumb)
+ {
+ /* In ARM mode endianness is a straightforward issue: the instruction
+ is four bytes long and is either ordered 0123 or 3210. */
+ printer = print_insn_arm_internal;
+ info->bytes_per_chunk = 4;
+ size = 4;
+
+ status = info->read_memory_func (pc, (bfd_byte *)b, 4, info);
+ if (little)
+ given = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
+ else
+ given = (b[3]) | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
+ }
+ else
+ {
+ /* In Thumb mode we have the additional wrinkle of two
+ instruction lengths. Fortunately, the bits that determine
+ the length of the current instruction are always to be found
+ in the first two bytes. */
+ printer = print_insn_thumb16;
+ info->bytes_per_chunk = 2;
+ size = 2;
+
+ status = info->read_memory_func (pc, (bfd_byte *)b, 2, info);
+ if (little)
+ given = (b[0]) | (b[1] << 8);
+ else
+ given = (b[1]) | (b[0] << 8);
+
+ if (!status)
+ {
+ /* These bit patterns signal a four-byte Thumb
+ instruction. */
+ if ((given & 0xF800) == 0xF800
+ || (given & 0xF800) == 0xF000
+ || (given & 0xF800) == 0xE800)
+ {
+ status = info->read_memory_func (pc + 2, (bfd_byte *)b, 2, info);
+ if (little)
+ given = (b[0]) | (b[1] << 8) | (given << 16);
+ else
+ given = (b[1]) | (b[0] << 8) | (given << 16);
+
+ printer = print_insn_thumb32;
+ size = 4;
+ }
+ }
+
+ if (ifthen_address != pc)
+ find_ifthen_state(pc, info, little);
+
+ if (ifthen_state)
+ {
+ if ((ifthen_state & 0xf) == 0x8)
+ ifthen_next_state = 0;
+ else
+ ifthen_next_state = (ifthen_state & 0xe0)
+ | ((ifthen_state & 0xf) << 1);
+ }
+ }
+
+ if (status)
+ {
+ info->memory_error_func (status, pc, info);
+ return -1;
+ }
+ if (info->flags & INSN_HAS_RELOC)
+ /* If the instruction has a reloc associated with it, then
+ the offset field in the instruction will actually be the
+ addend for the reloc. (We are using REL type relocs).
+ In such cases, we can ignore the pc when computing
+ addresses, since the addend is not currently pc-relative. */
+ pc = 0;
+
+ /* We include the hexdump of the instruction. The format here
+ matches that used by objdump and the ARM ARM (in particular,
+ 32 bit Thumb instructions are displayed as pairs of halfwords,
+ not as a single word.) */
+ if (is_thumb)
+ {
+ if (size == 2)
+ {
+ info->fprintf_func(info->stream, "%04lx ",
+ ((unsigned long)given) & 0xffff);
+ }
+ else
+ {
+ info->fprintf_func(info->stream, "%04lx %04lx ",
+ (((unsigned long)given) >> 16) & 0xffff,
+ ((unsigned long)given) & 0xffff);
+ }
+ }
+ else
+ {
+ info->fprintf_func(info->stream, "%08lx ",
+ ((unsigned long)given) & 0xffffffff);
+ }
+
+ printer (pc, info, given);
+
+ if (is_thumb)
+ {
+ ifthen_state = ifthen_next_state;
+ ifthen_address += size;
+ }
+ return size;
+}
diff --git a/arm-semi.c b/arm-semi.c
new file mode 100644
index 0000000..1d5179b
--- /dev/null
+++ b/arm-semi.c
@@ -0,0 +1,488 @@
+/*
+ * Arm "Angel" semihosting syscalls
+ *
+ * Copyright (c) 2005, 2007 CodeSourcery.
+ * Written by Paul Brook.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "cpu.h"
+#ifdef CONFIG_USER_ONLY
+#include "qemu.h"
+
+#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
+#else
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#endif
+
+#define SYS_OPEN 0x01
+#define SYS_CLOSE 0x02
+#define SYS_WRITEC 0x03
+#define SYS_WRITE0 0x04
+#define SYS_WRITE 0x05
+#define SYS_READ 0x06
+#define SYS_READC 0x07
+#define SYS_ISTTY 0x09
+#define SYS_SEEK 0x0a
+#define SYS_FLEN 0x0c
+#define SYS_TMPNAM 0x0d
+#define SYS_REMOVE 0x0e
+#define SYS_RENAME 0x0f
+#define SYS_CLOCK 0x10
+#define SYS_TIME 0x11
+#define SYS_SYSTEM 0x12
+#define SYS_ERRNO 0x13
+#define SYS_GET_CMDLINE 0x15
+#define SYS_HEAPINFO 0x16
+#define SYS_EXIT 0x18
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define GDB_O_RDONLY 0x000
+#define GDB_O_WRONLY 0x001
+#define GDB_O_RDWR 0x002
+#define GDB_O_APPEND 0x008
+#define GDB_O_CREAT 0x200
+#define GDB_O_TRUNC 0x400
+#define GDB_O_BINARY 0
+
+static int gdb_open_modeflags[12] = {
+ GDB_O_RDONLY,
+ GDB_O_RDONLY | GDB_O_BINARY,
+ GDB_O_RDWR,
+ GDB_O_RDWR | GDB_O_BINARY,
+ GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
+ GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
+ GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
+ GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
+ GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
+ GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
+ GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
+ GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
+};
+
+static int open_modeflags[12] = {
+ O_RDONLY,
+ O_RDONLY | O_BINARY,
+ O_RDWR,
+ O_RDWR | O_BINARY,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ O_RDWR | O_CREAT | O_TRUNC,
+ O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
+ O_WRONLY | O_CREAT | O_APPEND,
+ O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
+ O_RDWR | O_CREAT | O_APPEND,
+ O_RDWR | O_CREAT | O_APPEND | O_BINARY
+};
+
+#ifdef CONFIG_USER_ONLY
+static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
+{
+ if (code == (uint32_t)-1)
+ ts->swi_errno = errno;
+ return code;
+}
+#else
+static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
+{
+ return code;
+}
+
+#include "softmmu-semi.h"
+#endif
+
+static target_ulong arm_semi_syscall_len;
+
+#if !defined(CONFIG_USER_ONLY)
+static target_ulong syscall_err;
+#endif
+
+static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+#ifdef CONFIG_USER_ONLY
+ TaskState *ts = env->opaque;
+#endif
+
+ if (ret == (target_ulong)-1) {
+#ifdef CONFIG_USER_ONLY
+ ts->swi_errno = err;
+#else
+ syscall_err = err;
+#endif
+ env->regs[0] = ret;
+ } else {
+ /* Fixup syscalls that use nonstardard return conventions. */
+ switch (env->regs[0]) {
+ case SYS_WRITE:
+ case SYS_READ:
+ env->regs[0] = arm_semi_syscall_len - ret;
+ break;
+ case SYS_SEEK:
+ env->regs[0] = 0;
+ break;
+ default:
+ env->regs[0] = ret;
+ break;
+ }
+ }
+}
+
+static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+ /* The size is always stored in big-endian order, extract
+ the value. We assume the size always fit in 32 bits. */
+ uint32_t size;
+ cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
+ env->regs[0] = be32_to_cpu(size);
+#ifdef CONFIG_USER_ONLY
+ ((TaskState *)env->opaque)->swi_errno = err;
+#else
+ syscall_err = err;
+#endif
+}
+
+#define ARG(n) \
+({ \
+ target_ulong __arg; \
+ /* FIXME - handle get_user() failure */ \
+ get_user_ual(__arg, args + (n) * 4); \
+ __arg; \
+})
+#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
+uint32_t do_arm_semihosting(CPUState *env)
+{
+ target_ulong args;
+ char * s;
+ int nr;
+ uint32_t ret;
+ uint32_t len;
+#ifdef CONFIG_USER_ONLY
+ TaskState *ts = env->opaque;
+#else
+ CPUState *ts = env;
+#endif
+
+ nr = env->regs[0];
+ args = env->regs[1];
+ switch (nr) {
+ case SYS_OPEN:
+ if (!(s = lock_user_string(ARG(0))))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ if (ARG(1) >= 12)
+ return (uint32_t)-1;
+ if (strcmp(s, ":tt") == 0) {
+ if (ARG(1) < 4)
+ return STDIN_FILENO;
+ else
+ return STDOUT_FILENO;
+ }
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
+ (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
+ return env->regs[0];
+ } else {
+ ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+ }
+ unlock_user(s, ARG(0), 0);
+ return ret;
+ case SYS_CLOSE:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
+ return env->regs[0];
+ } else {
+ return set_swi_errno(ts, close(ARG(0)));
+ }
+ case SYS_WRITEC:
+ {
+ char c;
+
+ if (get_user_u8(c, args))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ /* Write to debug console. stderr is near enough. */
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
+ return env->regs[0];
+ } else {
+ return write(STDERR_FILENO, &c, 1);
+ }
+ }
+ case SYS_WRITE0:
+ if (!(s = lock_user_string(args)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ len = strlen(s);
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
+ ret = env->regs[0];
+ } else {
+ ret = write(STDERR_FILENO, s, len);
+ }
+ unlock_user(s, args, 0);
+ return ret;
+ case SYS_WRITE:
+ len = ARG(2);
+ if (use_gdb_syscalls()) {
+ arm_semi_syscall_len = len;
+ gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
+ return env->regs[0];
+ } else {
+ if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ ret = set_swi_errno(ts, write(ARG(0), s, len));
+ unlock_user(s, ARG(1), 0);
+ if (ret == (uint32_t)-1)
+ return -1;
+ return len - ret;
+ }
+ case SYS_READ:
+ len = ARG(2);
+ if (use_gdb_syscalls()) {
+ arm_semi_syscall_len = len;
+ gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
+ return env->regs[0];
+ } else {
+ if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ do
+ ret = set_swi_errno(ts, read(ARG(0), s, len));
+ while (ret == -1 && errno == EINTR);
+ unlock_user(s, ARG(1), len);
+ if (ret == (uint32_t)-1)
+ return -1;
+ return len - ret;
+ }
+ case SYS_READC:
+ /* XXX: Read from debug cosole. Not implemented. */
+ return 0;
+ case SYS_ISTTY:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
+ return env->regs[0];
+ } else {
+ return isatty(ARG(0));
+ }
+ case SYS_SEEK:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
+ return env->regs[0];
+ } else {
+ ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
+ if (ret == (uint32_t)-1)
+ return -1;
+ return 0;
+ }
+ case SYS_FLEN:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
+ ARG(0), env->regs[13]-64);
+ return env->regs[0];
+ } else {
+ struct stat buf;
+ ret = set_swi_errno(ts, fstat(ARG(0), &buf));
+ if (ret == (uint32_t)-1)
+ return -1;
+ return buf.st_size;
+ }
+ case SYS_TMPNAM:
+ /* XXX: Not implemented. */
+ return -1;
+ case SYS_REMOVE:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
+ ret = env->regs[0];
+ } else {
+ if (!(s = lock_user_string(ARG(0))))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ ret = set_swi_errno(ts, remove(s));
+ unlock_user(s, ARG(0), 0);
+ }
+ return ret;
+ case SYS_RENAME:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
+ ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
+ return env->regs[0];
+ } else {
+ char *s2;
+ s = lock_user_string(ARG(0));
+ s2 = lock_user_string(ARG(2));
+ if (!s || !s2)
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ ret = (uint32_t)-1;
+ else
+ ret = set_swi_errno(ts, rename(s, s2));
+ if (s2)
+ unlock_user(s2, ARG(2), 0);
+ if (s)
+ unlock_user(s, ARG(0), 0);
+ return ret;
+ }
+ case SYS_CLOCK:
+ return clock() / (CLOCKS_PER_SEC / 100);
+ case SYS_TIME:
+ return set_swi_errno(ts, time(NULL));
+ case SYS_SYSTEM:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
+ return env->regs[0];
+ } else {
+ if (!(s = lock_user_string(ARG(0))))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ ret = set_swi_errno(ts, system(s));
+ unlock_user(s, ARG(0), 0);
+ return ret;
+ }
+ case SYS_ERRNO:
+#ifdef CONFIG_USER_ONLY
+ return ts->swi_errno;
+#else
+ return syscall_err;
+#endif
+ case SYS_GET_CMDLINE:
+#ifdef CONFIG_USER_ONLY
+ /* Build a commandline from the original argv. */
+ {
+ char *arm_cmdline_buffer;
+ const char *host_cmdline_buffer;
+
+ unsigned int i;
+ unsigned int arm_cmdline_len = ARG(1);
+ unsigned int host_cmdline_len =
+ ts->info->arg_end-ts->info->arg_start;
+
+ if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) {
+ return -1; /* not enough space to store command line */
+ }
+
+ if (!host_cmdline_len) {
+ /* We special-case the "empty command line" case (argc==0).
+ Just provide the terminating 0. */
+ arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0);
+ arm_cmdline_buffer[0] = 0;
+ unlock_user(arm_cmdline_buffer, ARG(0), 1);
+
+ /* Adjust the commandline length argument. */
+ SET_ARG(1, 0);
+ return 0;
+ }
+
+ /* lock the buffers on the ARM side */
+ arm_cmdline_buffer =
+ lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0);
+ host_cmdline_buffer =
+ lock_user(VERIFY_READ, ts->info->arg_start,
+ host_cmdline_len, 1);
+
+ if (arm_cmdline_buffer && host_cmdline_buffer)
+ {
+ /* the last argument is zero-terminated;
+ no need for additional termination */
+ memcpy(arm_cmdline_buffer, host_cmdline_buffer,
+ host_cmdline_len);
+
+ /* separate arguments by white spaces */
+ for (i = 0; i < host_cmdline_len-1; i++) {
+ if (arm_cmdline_buffer[i] == 0) {
+ arm_cmdline_buffer[i] = ' ';
+ }
+ }
+
+ /* Adjust the commandline length argument. */
+ SET_ARG(1, host_cmdline_len-1);
+ }
+
+ /* Unlock the buffers on the ARM side. */
+ unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len);
+ unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0);
+
+ /* Return success if we could return a commandline. */
+ return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1;
+ }
+#else
+ return -1;
+#endif
+ case SYS_HEAPINFO:
+ {
+ uint32_t *ptr;
+ uint32_t limit;
+
+#ifdef CONFIG_USER_ONLY
+ /* Some C libraries assume the heap immediately follows .bss, so
+ allocate it using sbrk. */
+ if (!ts->heap_limit) {
+ long ret;
+
+ ts->heap_base = do_brk(0);
+ limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
+ /* Try a big heap, and reduce the size if that fails. */
+ for (;;) {
+ ret = do_brk(limit);
+ if (ret != -1)
+ break;
+ limit = (ts->heap_base >> 1) + (limit >> 1);
+ }
+ ts->heap_limit = limit;
+ }
+
+ if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ ptr[0] = tswap32(ts->heap_base);
+ ptr[1] = tswap32(ts->heap_limit);
+ ptr[2] = tswap32(ts->stack_base);
+ ptr[3] = tswap32(0); /* Stack limit. */
+ unlock_user(ptr, ARG(0), 16);
+#else
+ limit = ram_size;
+ if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+ /* FIXME - should this error code be -TARGET_EFAULT ? */
+ return (uint32_t)-1;
+ /* TODO: Make this use the limit of the loaded application. */
+ ptr[0] = tswap32(limit / 2);
+ ptr[1] = tswap32(limit);
+ ptr[2] = tswap32(limit); /* Stack base */
+ ptr[3] = tswap32(0); /* Stack limit. */
+ unlock_user(ptr, ARG(0), 16);
+#endif
+ return 0;
+ }
+ case SYS_EXIT:
+ gdb_exit(env, 0);
+ exit(0);
+ default:
+ fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+}
diff --git a/arm.ld b/arm.ld
new file mode 100644
index 0000000..12b3edb
--- /dev/null
+++ b/arm.ld
@@ -0,0 +1,153 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",
+ "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
+ __exidx_start = .;
+ .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
+ __exidx_end = .;
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.gen_code)
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .data1 : { *(.data1) }
+ .preinit_array :
+ {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ }
+ .init_array :
+ {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/async.c b/async.c
new file mode 100644
index 0000000..57ac3a8
--- /dev/null
+++ b/async.c
@@ -0,0 +1,216 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu-aio.h"
+
+/*
+ * An AsyncContext protects the callbacks of AIO requests and Bottom Halves
+ * against interfering with each other. A typical example is qcow2 that accepts
+ * asynchronous requests, but relies for manipulation of its metadata on
+ * synchronous bdrv_read/write that doesn't trigger any callbacks.
+ *
+ * However, these functions are often emulated using AIO which means that AIO
+ * callbacks must be run - but at the same time we must not run callbacks of
+ * other requests as they might start to modify metadata and corrupt the
+ * internal state of the caller of bdrv_read/write.
+ *
+ * To achieve the desired semantics we switch into a new AsyncContext.
+ * Callbacks must only be run if they belong to the current AsyncContext.
+ * Otherwise they need to be queued until their own context is active again.
+ * This is how you can make qemu_aio_wait() wait only for your own callbacks.
+ *
+ * The AsyncContexts form a stack. When you leave a AsyncContexts, you always
+ * return to the old ("parent") context.
+ */
+struct AsyncContext {
+ /* Consecutive number of the AsyncContext (position in the stack) */
+ int id;
+
+ /* Anchor of the list of Bottom Halves belonging to the context */
+ struct QEMUBH *first_bh;
+
+ /* Link to parent context */
+ struct AsyncContext *parent;
+};
+
+/* The currently active AsyncContext */
+static struct AsyncContext *async_context = &(struct AsyncContext) { 0 };
+
+/*
+ * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks
+ * won't be called until this context is left again.
+ */
+void async_context_push(void)
+{
+ struct AsyncContext *new = qemu_mallocz(sizeof(*new));
+ new->parent = async_context;
+ new->id = async_context->id + 1;
+ async_context = new;
+}
+
+/* Run queued AIO completions and destroy Bottom Half */
+static void bh_run_aio_completions(void *opaque)
+{
+ QEMUBH **bh = opaque;
+ qemu_bh_delete(*bh);
+ qemu_free(bh);
+ qemu_aio_process_queue();
+}
+/*
+ * Leave the currently active AsyncContext. All Bottom Halves belonging to the
+ * old context are executed before changing the context.
+ */
+void async_context_pop(void)
+{
+ struct AsyncContext *old = async_context;
+ QEMUBH **bh;
+
+ /* Flush the bottom halves, we don't want to lose them */
+ while (qemu_bh_poll());
+
+ /* Switch back to the parent context */
+ async_context = async_context->parent;
+ qemu_free(old);
+
+ if (async_context == NULL) {
+ abort();
+ }
+
+ /* Schedule BH to run any queued AIO completions as soon as possible */
+ bh = qemu_malloc(sizeof(*bh));
+ *bh = qemu_bh_new(bh_run_aio_completions, bh);
+ qemu_bh_schedule(*bh);
+}
+
+/*
+ * Returns the ID of the currently active AsyncContext
+ */
+int get_async_context_id(void)
+{
+ return async_context->id;
+}
+
+/***********************************************************/
+/* bottom halves (can be seen as timers which expire ASAP) */
+
+struct QEMUBH {
+ QEMUBHFunc *cb;
+ void *opaque;
+ int scheduled;
+ int idle;
+ int deleted;
+ QEMUBH *next;
+};
+
+QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
+{
+ QEMUBH *bh;
+ bh = qemu_mallocz(sizeof(QEMUBH));
+ bh->cb = cb;
+ bh->opaque = opaque;
+ bh->next = async_context->first_bh;
+ async_context->first_bh = bh;
+ return bh;
+}
+
+int qemu_bh_poll(void)
+{
+ QEMUBH *bh, **bhp;
+ int ret;
+
+ ret = 0;
+ for (bh = async_context->first_bh; bh; bh = bh->next) {
+ if (!bh->deleted && bh->scheduled) {
+ bh->scheduled = 0;
+ if (!bh->idle)
+ ret = 1;
+ bh->idle = 0;
+ bh->cb(bh->opaque);
+ }
+ }
+
+ /* remove deleted bhs */
+ bhp = &async_context->first_bh;
+ while (*bhp) {
+ bh = *bhp;
+ if (bh->deleted) {
+ *bhp = bh->next;
+ qemu_free(bh);
+ } else
+ bhp = &bh->next;
+ }
+
+ return ret;
+}
+
+void qemu_bh_schedule_idle(QEMUBH *bh)
+{
+ if (bh->scheduled)
+ return;
+ bh->scheduled = 1;
+ bh->idle = 1;
+}
+
+void qemu_bh_schedule(QEMUBH *bh)
+{
+ if (bh->scheduled)
+ return;
+ bh->scheduled = 1;
+ bh->idle = 0;
+ /* stop the currently executing CPU to execute the BH ASAP */
+ qemu_notify_event();
+}
+
+void qemu_bh_cancel(QEMUBH *bh)
+{
+ bh->scheduled = 0;
+}
+
+void qemu_bh_delete(QEMUBH *bh)
+{
+ bh->scheduled = 0;
+ bh->deleted = 1;
+}
+
+void qemu_bh_update_timeout(int *timeout)
+{
+ QEMUBH *bh;
+
+ for (bh = async_context->first_bh; bh; bh = bh->next) {
+ if (!bh->deleted && bh->scheduled) {
+ if (bh->idle) {
+ /* idle bottom halves will be polled at least
+ * every 10ms */
+ *timeout = MIN(10, *timeout);
+ } else {
+ /* non-idle bottom halves will be executed
+ * immediately */
+ *timeout = 0;
+ break;
+ }
+ }
+ }
+}
+
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
new file mode 100644
index 0000000..4d72014
--- /dev/null
+++ b/audio/alsaaudio.c
@@ -0,0 +1,1260 @@
+/*
+ * QEMU ALSA audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <alsa/asoundlib.h>
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "audio.h"
+
+#if QEMU_GNUC_PREREQ(4, 3)
+#pragma GCC diagnostic ignored "-Waddress"
+#endif
+
+#define AUDIO_CAP "alsa"
+#include "audio_int.h"
+
+struct pollhlp {
+ snd_pcm_t *handle;
+ struct pollfd *pfds;
+ int count;
+ int mask;
+};
+
+typedef struct ALSAVoiceOut {
+ HWVoiceOut hw;
+ int wpos;
+ int pending;
+ void *pcm_buf;
+ snd_pcm_t *handle;
+ struct pollhlp pollhlp;
+} ALSAVoiceOut;
+
+typedef struct ALSAVoiceIn {
+ HWVoiceIn hw;
+ snd_pcm_t *handle;
+ void *pcm_buf;
+ struct pollhlp pollhlp;
+} ALSAVoiceIn;
+
+static struct {
+ int size_in_usec_in;
+ int size_in_usec_out;
+ const char *pcm_name_in;
+ const char *pcm_name_out;
+ unsigned int buffer_size_in;
+ unsigned int period_size_in;
+ unsigned int buffer_size_out;
+ unsigned int period_size_out;
+ unsigned int threshold;
+
+ int buffer_size_in_overridden;
+ int period_size_in_overridden;
+
+ int buffer_size_out_overridden;
+ int period_size_out_overridden;
+ int verbose;
+} conf = {
+ .buffer_size_out = 4096,
+ .period_size_out = 1024,
+ .pcm_name_out = "default",
+ .pcm_name_in = "default",
+};
+
+struct alsa_params_req {
+ int freq;
+ snd_pcm_format_t fmt;
+ int nchannels;
+ int size_in_usec;
+ int override_mask;
+ unsigned int buffer_size;
+ unsigned int period_size;
+};
+
+struct alsa_params_obt {
+ int freq;
+ audfmt_e fmt;
+ int endianness;
+ int nchannels;
+ snd_pcm_uframes_t samples;
+};
+
+static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
+}
+
+static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
+ int err,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
+}
+
+static void alsa_fini_poll (struct pollhlp *hlp)
+{
+ int i;
+ struct pollfd *pfds = hlp->pfds;
+
+ if (pfds) {
+ for (i = 0; i < hlp->count; ++i) {
+ qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
+ }
+ qemu_free (pfds);
+ }
+ hlp->pfds = NULL;
+ hlp->count = 0;
+ hlp->handle = NULL;
+}
+
+static void alsa_anal_close1 (snd_pcm_t **handlep)
+{
+ int err = snd_pcm_close (*handlep);
+ if (err) {
+ alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
+ }
+ *handlep = NULL;
+}
+
+static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp)
+{
+ alsa_fini_poll (hlp);
+ alsa_anal_close1 (handlep);
+}
+
+static int alsa_recover (snd_pcm_t *handle)
+{
+ int err = snd_pcm_prepare (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Failed to prepare handle %p\n", handle);
+ return -1;
+ }
+ return 0;
+}
+
+static int alsa_resume (snd_pcm_t *handle)
+{
+ int err = snd_pcm_resume (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Failed to resume handle %p\n", handle);
+ return -1;
+ }
+ return 0;
+}
+
+static void alsa_poll_handler (void *opaque)
+{
+ int err, count;
+ snd_pcm_state_t state;
+ struct pollhlp *hlp = opaque;
+ unsigned short revents;
+
+ count = poll (hlp->pfds, hlp->count, 0);
+ if (count < 0) {
+ dolog ("alsa_poll_handler: poll %s\n", strerror (errno));
+ return;
+ }
+
+ if (!count) {
+ return;
+ }
+
+ /* XXX: ALSA example uses initial count, not the one returned by
+ poll, correct? */
+ err = snd_pcm_poll_descriptors_revents (hlp->handle, hlp->pfds,
+ hlp->count, &revents);
+ if (err < 0) {
+ alsa_logerr (err, "snd_pcm_poll_descriptors_revents");
+ return;
+ }
+
+ if (!(revents & hlp->mask)) {
+ if (conf.verbose) {
+ dolog ("revents = %d\n", revents);
+ }
+ return;
+ }
+
+ state = snd_pcm_state (hlp->handle);
+ switch (state) {
+ case SND_PCM_STATE_SETUP:
+ alsa_recover (hlp->handle);
+ break;
+
+ case SND_PCM_STATE_XRUN:
+ alsa_recover (hlp->handle);
+ break;
+
+ case SND_PCM_STATE_SUSPENDED:
+ alsa_resume (hlp->handle);
+ break;
+
+ case SND_PCM_STATE_PREPARED:
+ audio_run ("alsa run (prepared)");
+ break;
+
+ case SND_PCM_STATE_RUNNING:
+ audio_run ("alsa run (running)");
+ break;
+
+ default:
+ dolog ("Unexpected state %d\n", state);
+ }
+}
+
+static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
+{
+ int i, count, err;
+ struct pollfd *pfds;
+
+ count = snd_pcm_poll_descriptors_count (handle);
+ if (count <= 0) {
+ dolog ("Could not initialize poll mode\n"
+ "Invalid number of poll descriptors %d\n", count);
+ return -1;
+ }
+
+ pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds));
+ if (!pfds) {
+ dolog ("Could not initialize poll mode\n");
+ return -1;
+ }
+
+ err = snd_pcm_poll_descriptors (handle, pfds, count);
+ if (err < 0) {
+ alsa_logerr (err, "Could not initialize poll mode\n"
+ "Could not obtain poll descriptors\n");
+ qemu_free (pfds);
+ return -1;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (pfds[i].events & POLLIN) {
+ err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler,
+ NULL, hlp);
+ }
+ if (pfds[i].events & POLLOUT) {
+ if (conf.verbose) {
+ dolog ("POLLOUT %d %d\n", i, pfds[i].fd);
+ }
+ err = qemu_set_fd_handler (pfds[i].fd, NULL,
+ alsa_poll_handler, hlp);
+ }
+ if (conf.verbose) {
+ dolog ("Set handler events=%#x index=%d fd=%d err=%d\n",
+ pfds[i].events, i, pfds[i].fd, err);
+ }
+
+ if (err) {
+ dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n",
+ pfds[i].events, i, pfds[i].fd, err);
+
+ while (i--) {
+ qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
+ }
+ qemu_free (pfds);
+ return -1;
+ }
+ }
+ hlp->pfds = pfds;
+ hlp->count = count;
+ hlp->handle = handle;
+ hlp->mask = mask;
+ return 0;
+}
+
+static int alsa_poll_out (HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+ return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT);
+}
+
+static int alsa_poll_in (HWVoiceIn *hw)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+ return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
+}
+
+static int alsa_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
+{
+ switch (fmt) {
+ case AUD_FMT_S8:
+ return SND_PCM_FORMAT_S8;
+
+ case AUD_FMT_U8:
+ return SND_PCM_FORMAT_U8;
+
+ case AUD_FMT_S16:
+ if (endianness) {
+ return SND_PCM_FORMAT_S16_BE;
+ }
+ else {
+ return SND_PCM_FORMAT_S16_LE;
+ }
+
+ case AUD_FMT_U16:
+ if (endianness) {
+ return SND_PCM_FORMAT_U16_BE;
+ }
+ else {
+ return SND_PCM_FORMAT_U16_LE;
+ }
+
+ case AUD_FMT_S32:
+ if (endianness) {
+ return SND_PCM_FORMAT_S32_BE;
+ }
+ else {
+ return SND_PCM_FORMAT_S32_LE;
+ }
+
+ case AUD_FMT_U32:
+ if (endianness) {
+ return SND_PCM_FORMAT_U32_BE;
+ }
+ else {
+ return SND_PCM_FORMAT_U32_LE;
+ }
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+ abort ();
+#endif
+ return SND_PCM_FORMAT_U8;
+ }
+}
+
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+ int *endianness)
+{
+ switch (alsafmt) {
+ case SND_PCM_FORMAT_S8:
+ *endianness = 0;
+ *fmt = AUD_FMT_S8;
+ break;
+
+ case SND_PCM_FORMAT_U8:
+ *endianness = 0;
+ *fmt = AUD_FMT_U8;
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case SND_PCM_FORMAT_U16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case SND_PCM_FORMAT_S16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case SND_PCM_FORMAT_U16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case SND_PCM_FORMAT_S32_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_S32;
+ break;
+
+ case SND_PCM_FORMAT_U32_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_U32;
+ break;
+
+ case SND_PCM_FORMAT_S32_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_S32;
+ break;
+
+ case SND_PCM_FORMAT_U32_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_U32;
+ break;
+
+ default:
+ dolog ("Unrecognized audio format %d\n", alsafmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void alsa_dump_info (struct alsa_params_req *req,
+ struct alsa_params_obt *obt,
+ snd_pcm_format_t obtfmt)
+{
+ dolog ("parameter | requested value | obtained value\n");
+ dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
+ dolog ("channels | %10d | %10d\n",
+ req->nchannels, obt->nchannels);
+ dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
+ dolog ("============================================\n");
+ dolog ("requested: buffer size %d period size %d\n",
+ req->buffer_size, req->period_size);
+ dolog ("obtained: samples %ld\n", obt->samples);
+}
+
+static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
+{
+ int err;
+ snd_pcm_sw_params_t *sw_params;
+
+ snd_pcm_sw_params_alloca (&sw_params);
+
+ err = snd_pcm_sw_params_current (handle, sw_params);
+ if (err < 0) {
+ dolog ("Could not fully initialize DAC\n");
+ alsa_logerr (err, "Failed to get current software parameters\n");
+ return;
+ }
+
+ err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
+ if (err < 0) {
+ dolog ("Could not fully initialize DAC\n");
+ alsa_logerr (err, "Failed to set software threshold to %ld\n",
+ threshold);
+ return;
+ }
+
+ err = snd_pcm_sw_params (handle, sw_params);
+ if (err < 0) {
+ dolog ("Could not fully initialize DAC\n");
+ alsa_logerr (err, "Failed to set software parameters\n");
+ return;
+ }
+}
+
+static int alsa_open (int in, struct alsa_params_req *req,
+ struct alsa_params_obt *obt, snd_pcm_t **handlep)
+{
+ snd_pcm_t *handle;
+ snd_pcm_hw_params_t *hw_params;
+ int err;
+ int size_in_usec;
+ unsigned int freq, nchannels;
+ const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
+ snd_pcm_uframes_t obt_buffer_size;
+ const char *typ = in ? "ADC" : "DAC";
+ snd_pcm_format_t obtfmt;
+
+ freq = req->freq;
+ nchannels = req->nchannels;
+ size_in_usec = req->size_in_usec;
+
+ snd_pcm_hw_params_alloca (&hw_params);
+
+ err = snd_pcm_open (
+ &handle,
+ pcm_name,
+ in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
+ return -1;
+ }
+
+ err = snd_pcm_hw_params_any (handle, hw_params);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_access (
+ handle,
+ hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set access type\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
+ if (err < 0 && conf.verbose) {
+ alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
+ }
+
+ err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_set_channels_near (
+ handle,
+ hw_params,
+ &nchannels
+ );
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
+ req->nchannels);
+ goto err;
+ }
+
+ if (nchannels != 1 && nchannels != 2) {
+ alsa_logerr2 (err, typ,
+ "Can not handle obtained number of channels %d\n",
+ nchannels);
+ goto err;
+ }
+
+ if (req->buffer_size) {
+ unsigned long obt;
+
+ if (size_in_usec) {
+ int dir = 0;
+ unsigned int btime = req->buffer_size;
+
+ err = snd_pcm_hw_params_set_buffer_time_near (
+ handle,
+ hw_params,
+ &btime,
+ &dir
+ );
+ obt = btime;
+ }
+ else {
+ snd_pcm_uframes_t bsize = req->buffer_size;
+
+ err = snd_pcm_hw_params_set_buffer_size_near (
+ handle,
+ hw_params,
+ &bsize
+ );
+ obt = bsize;
+ }
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
+ size_in_usec ? "time" : "size", req->buffer_size);
+ goto err;
+ }
+
+ if ((req->override_mask & 2) && (obt - req->buffer_size))
+ dolog ("Requested buffer %s %u was rejected, using %lu\n",
+ size_in_usec ? "time" : "size", req->buffer_size, obt);
+ }
+
+ if (req->period_size) {
+ unsigned long obt;
+
+ if (size_in_usec) {
+ int dir = 0;
+ unsigned int ptime = req->period_size;
+
+ err = snd_pcm_hw_params_set_period_time_near (
+ handle,
+ hw_params,
+ &ptime,
+ &dir
+ );
+ obt = ptime;
+ }
+ else {
+ int dir = 0;
+ snd_pcm_uframes_t psize = req->period_size;
+
+ err = snd_pcm_hw_params_set_period_size_near (
+ handle,
+ hw_params,
+ &psize,
+ &dir
+ );
+ obt = psize;
+ }
+
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
+ size_in_usec ? "time" : "size", req->period_size);
+ goto err;
+ }
+
+ if (((req->override_mask & 1) && (obt - req->period_size)))
+ dolog ("Requested period %s %u was rejected, using %lu\n",
+ size_in_usec ? "time" : "size", req->period_size, obt);
+ }
+
+ err = snd_pcm_hw_params (handle, hw_params);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to get buffer size\n");
+ goto err;
+ }
+
+ err = snd_pcm_hw_params_get_format (hw_params, &obtfmt);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to get format\n");
+ goto err;
+ }
+
+ if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
+ dolog ("Invalid format was returned %d\n", obtfmt);
+ goto err;
+ }
+
+ err = snd_pcm_prepare (handle);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
+ goto err;
+ }
+
+ if (!in && conf.threshold) {
+ snd_pcm_uframes_t threshold;
+ int bytes_per_sec;
+
+ bytes_per_sec = freq << (nchannels == 2);
+
+ switch (obt->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ bytes_per_sec <<= 1;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ bytes_per_sec <<= 2;
+ break;
+ }
+
+ threshold = (conf.threshold * bytes_per_sec) / 1000;
+ alsa_set_threshold (handle, threshold);
+ }
+
+ obt->nchannels = nchannels;
+ obt->freq = freq;
+ obt->samples = obt_buffer_size;
+
+ *handlep = handle;
+
+ if (conf.verbose &&
+ (obtfmt != req->fmt ||
+ obt->nchannels != req->nchannels ||
+ obt->freq != req->freq)) {
+ dolog ("Audio parameters for %s\n", typ);
+ alsa_dump_info (req, obt, obtfmt);
+ }
+
+#ifdef DEBUG
+ alsa_dump_info (req, obt, obtfmt);
+#endif
+ return 0;
+
+ err:
+ alsa_anal_close1 (&handle);
+ return -1;
+}
+
+static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
+{
+ snd_pcm_sframes_t avail;
+
+ avail = snd_pcm_avail_update (handle);
+ if (avail < 0) {
+ if (avail == -EPIPE) {
+ if (!alsa_recover (handle)) {
+ avail = snd_pcm_avail_update (handle);
+ }
+ }
+
+ if (avail < 0) {
+ alsa_logerr (avail,
+ "Could not obtain number of available frames\n");
+ return -1;
+ }
+ }
+
+ return avail;
+}
+
+static void alsa_write_pending (ALSAVoiceOut *alsa)
+{
+ HWVoiceOut *hw = &alsa->hw;
+
+ while (alsa->pending) {
+ int left_till_end_samples = hw->samples - alsa->wpos;
+ int len = audio_MIN (alsa->pending, left_till_end_samples);
+ char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
+
+ while (len) {
+ snd_pcm_sframes_t written;
+
+ written = snd_pcm_writei (alsa->handle, src, len);
+
+ if (written <= 0) {
+ switch (written) {
+ case 0:
+ if (conf.verbose) {
+ dolog ("Failed to write %d frames (wrote zero)\n", len);
+ }
+ return;
+
+ case -EPIPE:
+ if (alsa_recover (alsa->handle)) {
+ alsa_logerr (written, "Failed to write %d frames\n",
+ len);
+ return;
+ }
+ if (conf.verbose) {
+ dolog ("Recovering from playback xrun\n");
+ }
+ continue;
+
+ case -ESTRPIPE:
+ /* stream is suspended and waiting for an
+ application recovery */
+ if (alsa_resume (alsa->handle)) {
+ alsa_logerr (written, "Failed to write %d frames\n",
+ len);
+ return;
+ }
+ if (conf.verbose) {
+ dolog ("Resuming suspended output stream\n");
+ }
+ continue;
+
+ case -EAGAIN:
+ return;
+
+ default:
+ alsa_logerr (written, "Failed to write %d frames from %p\n",
+ len, src);
+ return;
+ }
+ }
+
+ alsa->wpos = (alsa->wpos + written) % hw->samples;
+ alsa->pending -= written;
+ len -= written;
+ }
+ }
+}
+
+static int alsa_run_out (HWVoiceOut *hw, int live)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ int decr;
+ snd_pcm_sframes_t avail;
+
+ avail = alsa_get_avail (alsa->handle);
+ if (avail < 0) {
+ dolog ("Could not get number of available playback frames\n");
+ return 0;
+ }
+
+ decr = audio_MIN (live, avail);
+ decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
+ alsa->pending += decr;
+ alsa_write_pending (alsa);
+ return decr;
+}
+
+static void alsa_fini_out (HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+ ldebug ("alsa_fini\n");
+ alsa_anal_close (&alsa->handle, &alsa->pollhlp);
+
+ if (alsa->pcm_buf) {
+ qemu_free (alsa->pcm_buf);
+ alsa->pcm_buf = NULL;
+ }
+}
+
+static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ struct alsa_params_req req;
+ struct alsa_params_obt obt;
+ snd_pcm_t *handle;
+ struct audsettings obt_as;
+
+ req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.period_size = conf.period_size_out;
+ req.buffer_size = conf.buffer_size_out;
+ req.size_in_usec = conf.size_in_usec_out;
+ req.override_mask =
+ (conf.period_size_out_overridden ? 1 : 0) |
+ (conf.buffer_size_out_overridden ? 2 : 0);
+
+ if (alsa_open (0, &req, &obt, &handle)) {
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = obt.fmt;
+ obt_as.endianness = obt.endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = obt.samples;
+
+ alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
+ if (!alsa->pcm_buf) {
+ dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ alsa_anal_close1 (&handle);
+ return -1;
+ }
+
+ alsa->handle = handle;
+ return 0;
+}
+
+#define VOICE_CTL_PAUSE 0
+#define VOICE_CTL_PREPARE 1
+#define VOICE_CTL_START 2
+
+static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
+{
+ int err;
+
+ if (ctl == VOICE_CTL_PAUSE) {
+ err = snd_pcm_drop (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Could not stop %s\n", typ);
+ return -1;
+ }
+ }
+ else {
+ err = snd_pcm_prepare (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Could not prepare handle for %s\n", typ);
+ return -1;
+ }
+ if (ctl == VOICE_CTL_START) {
+ err = snd_pcm_start(handle);
+ if (err < 0) {
+ alsa_logerr (err, "Could not start handle for %s\n", typ);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ ldebug ("enabling voice\n");
+ if (poll_mode && alsa_poll_out (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+ return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
+ }
+
+ case VOICE_DISABLE:
+ ldebug ("disabling voice\n");
+ if (hw->poll_mode) {
+ hw->poll_mode = 0;
+ alsa_fini_poll (&alsa->pollhlp);
+ }
+ return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
+ }
+
+ return -1;
+}
+
+static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+ struct alsa_params_req req;
+ struct alsa_params_obt obt;
+ snd_pcm_t *handle;
+ struct audsettings obt_as;
+
+ req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.period_size = conf.period_size_in;
+ req.buffer_size = conf.buffer_size_in;
+ req.size_in_usec = conf.size_in_usec_in;
+ req.override_mask =
+ (conf.period_size_in_overridden ? 1 : 0) |
+ (conf.buffer_size_in_overridden ? 2 : 0);
+
+ if (alsa_open (1, &req, &obt, &handle)) {
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = obt.fmt;
+ obt_as.endianness = obt.endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = obt.samples;
+
+ alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!alsa->pcm_buf) {
+ dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ alsa_anal_close1 (&handle);
+ return -1;
+ }
+
+ alsa->handle = handle;
+ return 0;
+}
+
+static void alsa_fini_in (HWVoiceIn *hw)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+ alsa_anal_close (&alsa->handle, &alsa->pollhlp);
+
+ if (alsa->pcm_buf) {
+ qemu_free (alsa->pcm_buf);
+ alsa->pcm_buf = NULL;
+ }
+}
+
+static int alsa_run_in (HWVoiceIn *hw)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+ int hwshift = hw->info.shift;
+ int i;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ int decr;
+ struct {
+ int add;
+ int len;
+ } bufs[2] = {
+ { .add = hw->wpos, .len = 0 },
+ { .add = 0, .len = 0 }
+ };
+ snd_pcm_sframes_t avail;
+ snd_pcm_uframes_t read_samples = 0;
+
+ if (!dead) {
+ return 0;
+ }
+
+ avail = alsa_get_avail (alsa->handle);
+ if (avail < 0) {
+ dolog ("Could not get number of captured frames\n");
+ return 0;
+ }
+
+ if (!avail) {
+ snd_pcm_state_t state;
+
+ state = snd_pcm_state (alsa->handle);
+ switch (state) {
+ case SND_PCM_STATE_PREPARED:
+ avail = hw->samples;
+ break;
+ case SND_PCM_STATE_SUSPENDED:
+ /* stream is suspended and waiting for an application recovery */
+ if (alsa_resume (alsa->handle)) {
+ dolog ("Failed to resume suspended input stream\n");
+ return 0;
+ }
+ if (conf.verbose) {
+ dolog ("Resuming suspended input stream\n");
+ }
+ break;
+ default:
+ if (conf.verbose) {
+ dolog ("No frames available and ALSA state is %d\n", state);
+ }
+ return 0;
+ }
+ }
+
+ decr = audio_MIN (dead, avail);
+ if (!decr) {
+ return 0;
+ }
+
+ if (hw->wpos + decr > hw->samples) {
+ bufs[0].len = (hw->samples - hw->wpos);
+ bufs[1].len = (decr - (hw->samples - hw->wpos));
+ }
+ else {
+ bufs[0].len = decr;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ void *src;
+ struct st_sample *dst;
+ snd_pcm_sframes_t nread;
+ snd_pcm_uframes_t len;
+
+ len = bufs[i].len;
+
+ src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
+ dst = hw->conv_buf + bufs[i].add;
+
+ while (len) {
+ nread = snd_pcm_readi (alsa->handle, src, len);
+
+ if (nread <= 0) {
+ switch (nread) {
+ case 0:
+ if (conf.verbose) {
+ dolog ("Failed to read %ld frames (read zero)\n", len);
+ }
+ goto exit;
+
+ case -EPIPE:
+ if (alsa_recover (alsa->handle)) {
+ alsa_logerr (nread, "Failed to read %ld frames\n", len);
+ goto exit;
+ }
+ if (conf.verbose) {
+ dolog ("Recovering from capture xrun\n");
+ }
+ continue;
+
+ case -EAGAIN:
+ goto exit;
+
+ default:
+ alsa_logerr (
+ nread,
+ "Failed to read %ld frames from %p\n",
+ len,
+ src
+ );
+ goto exit;
+ }
+ }
+
+ hw->conv (dst, src, nread);
+
+ src = advance (src, nread << hwshift);
+ dst += nread;
+
+ read_samples += nread;
+ len -= nread;
+ }
+ }
+
+ exit:
+ hw->wpos = (hw->wpos + read_samples) % hw->samples;
+ return read_samples;
+}
+
+static int alsa_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ ldebug ("enabling voice\n");
+ if (poll_mode && alsa_poll_in (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+
+ return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
+ }
+
+ case VOICE_DISABLE:
+ ldebug ("disabling voice\n");
+ if (hw->poll_mode) {
+ hw->poll_mode = 0;
+ alsa_fini_poll (&alsa->pollhlp);
+ }
+ return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
+ }
+
+ return -1;
+}
+
+static void *alsa_audio_init (void)
+{
+ return &conf;
+}
+
+static void alsa_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option alsa_options[] = {
+ {
+ .name = "DAC_SIZE_IN_USEC",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.size_in_usec_out,
+ .descr = "DAC period/buffer size in microseconds (otherwise in frames)"
+ },
+ {
+ .name = "DAC_PERIOD_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.period_size_out,
+ .descr = "DAC period size (0 to go with system default)",
+ .overriddenp = &conf.period_size_out_overridden
+ },
+ {
+ .name = "DAC_BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.buffer_size_out,
+ .descr = "DAC buffer size (0 to go with system default)",
+ .overriddenp = &conf.buffer_size_out_overridden
+ },
+ {
+ .name = "ADC_SIZE_IN_USEC",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.size_in_usec_in,
+ .descr =
+ "ADC period/buffer size in microseconds (otherwise in frames)"
+ },
+ {
+ .name = "ADC_PERIOD_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.period_size_in,
+ .descr = "ADC period size (0 to go with system default)",
+ .overriddenp = &conf.period_size_in_overridden
+ },
+ {
+ .name = "ADC_BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.buffer_size_in,
+ .descr = "ADC buffer size (0 to go with system default)",
+ .overriddenp = &conf.buffer_size_in_overridden
+ },
+ {
+ .name = "THRESHOLD",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.threshold,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "DAC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.pcm_name_out,
+ .descr = "DAC device name (for instance dmix)"
+ },
+ {
+ .name = "ADC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.pcm_name_in,
+ .descr = "ADC device name"
+ },
+ {
+ .name = "VERBOSE",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.verbose,
+ .descr = "Behave in a more verbose way"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops alsa_pcm_ops = {
+ .init_out = alsa_init_out,
+ .fini_out = alsa_fini_out,
+ .run_out = alsa_run_out,
+ .write = alsa_write,
+ .ctl_out = alsa_ctl_out,
+
+ .init_in = alsa_init_in,
+ .fini_in = alsa_fini_in,
+ .run_in = alsa_run_in,
+ .read = alsa_read,
+ .ctl_in = alsa_ctl_in,
+};
+
+struct audio_driver alsa_audio_driver = {
+ .name = "alsa",
+ .descr = "ALSA http://www.alsa-project.org",
+ .options = alsa_options,
+ .init = alsa_audio_init,
+ .fini = alsa_audio_fini,
+ .pcm_ops = &alsa_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (ALSAVoiceOut),
+ .voice_size_in = sizeof (ALSAVoiceIn)
+};
diff --git a/audio/audio.c b/audio/audio.c
new file mode 100644
index 0000000..1729c0b
--- /dev/null
+++ b/audio/audio.c
@@ -0,0 +1,2066 @@
+/*
+ * QEMU Audio subsystem
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "audio.h"
+#include "monitor.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+#define AUDIO_CAP "audio"
+#include "audio_int.h"
+
+/* #define DEBUG_PLIVE */
+/* #define DEBUG_LIVE */
+/* #define DEBUG_OUT */
+/* #define DEBUG_CAPTURE */
+/* #define DEBUG_POLL */
+
+#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
+
+
+/* Order of CONFIG_AUDIO_DRIVERS is import.
+ The 1st one is the one used by default, that is the reason
+ that we generate the list.
+*/
+static struct audio_driver *drvtab[] = {
+#ifdef CONFIG_SPICE
+ &spice_audio_driver,
+#endif
+ CONFIG_AUDIO_DRIVERS
+ &no_audio_driver,
+ &wav_audio_driver
+};
+
+struct fixed_settings {
+ int enabled;
+ int nb_voices;
+ int greedy;
+ struct audsettings settings;
+};
+
+static struct {
+ struct fixed_settings fixed_out;
+ struct fixed_settings fixed_in;
+ union {
+ int hertz;
+ int64_t ticks;
+ } period;
+ int plive;
+ int log_to_monitor;
+ int try_poll_in;
+ int try_poll_out;
+} conf = {
+ .fixed_out = { /* DAC fixed settings */
+ .enabled = 1,
+ .nb_voices = 1,
+ .greedy = 1,
+ .settings = {
+ .freq = 44100,
+ .nchannels = 2,
+ .fmt = AUD_FMT_S16,
+ .endianness = AUDIO_HOST_ENDIANNESS,
+ }
+ },
+
+ .fixed_in = { /* ADC fixed settings */
+ .enabled = 1,
+ .nb_voices = 1,
+ .greedy = 1,
+ .settings = {
+ .freq = 44100,
+ .nchannels = 2,
+ .fmt = AUD_FMT_S16,
+ .endianness = AUDIO_HOST_ENDIANNESS,
+ }
+ },
+
+ .period = { .hertz = 250 },
+ .plive = 0,
+ .log_to_monitor = 0,
+ .try_poll_in = 1,
+ .try_poll_out = 1,
+};
+
+static AudioState glob_audio_state;
+
+const struct mixeng_volume nominal_volume = {
+ .mute = 0,
+#ifdef FLOAT_MIXENG
+ .r = 1.0,
+ .l = 1.0,
+#else
+ .r = 1ULL << 32,
+ .l = 1ULL << 32,
+#endif
+};
+
+#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
+#error No its not
+#else
+static void audio_print_options (const char *prefix,
+ struct audio_option *opt);
+
+int audio_bug (const char *funcname, int cond)
+{
+ if (cond) {
+ static int shown;
+
+ AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
+ if (!shown) {
+ struct audio_driver *d;
+
+ shown = 1;
+ AUD_log (NULL, "Save all your work and restart without audio\n");
+ AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
+ AUD_log (NULL, "I am sorry\n");
+ d = glob_audio_state.drv;
+ if (d) {
+ audio_print_options (d->name, d->options);
+ }
+ }
+ AUD_log (NULL, "Context:\n");
+
+#if defined AUDIO_BREAKPOINT_ON_BUG
+# if defined HOST_I386
+# if defined __GNUC__
+ __asm__ ("int3");
+# elif defined _MSC_VER
+ _asm _emit 0xcc;
+# else
+ abort ();
+# endif
+# else
+ abort ();
+# endif
+#endif
+ }
+
+ return cond;
+}
+#endif
+
+static inline int audio_bits_to_index (int bits)
+{
+ switch (bits) {
+ case 8:
+ return 0;
+
+ case 16:
+ return 1;
+
+ case 32:
+ return 2;
+
+ default:
+ audio_bug ("bits_to_index", 1);
+ AUD_log (NULL, "invalid bits %d\n", bits);
+ return 0;
+ }
+}
+
+void *audio_calloc (const char *funcname, int nmemb, size_t size)
+{
+ int cond;
+ size_t len;
+
+ len = nmemb * size;
+ cond = !nmemb || !size;
+ cond |= nmemb < 0;
+ cond |= len < size;
+
+ if (audio_bug ("audio_calloc", cond)) {
+ AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
+ funcname);
+ AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len);
+ return NULL;
+ }
+
+ return qemu_mallocz (len);
+}
+
+static char *audio_alloc_prefix (const char *s)
+{
+ const char qemu_prefix[] = "QEMU_";
+ size_t len, i;
+ char *r, *u;
+
+ if (!s) {
+ return NULL;
+ }
+
+ len = strlen (s);
+ r = qemu_malloc (len + sizeof (qemu_prefix));
+
+ u = r + sizeof (qemu_prefix) - 1;
+
+ pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
+ pstrcat (r, len + sizeof (qemu_prefix), s);
+
+ for (i = 0; i < len; ++i) {
+ u[i] = qemu_toupper(u[i]);
+ }
+
+ return r;
+}
+
+static const char *audio_audfmt_to_string (audfmt_e fmt)
+{
+ switch (fmt) {
+ case AUD_FMT_U8:
+ return "U8";
+
+ case AUD_FMT_U16:
+ return "U16";
+
+ case AUD_FMT_S8:
+ return "S8";
+
+ case AUD_FMT_S16:
+ return "S16";
+
+ case AUD_FMT_U32:
+ return "U32";
+
+ case AUD_FMT_S32:
+ return "S32";
+ }
+
+ dolog ("Bogus audfmt %d returning S16\n", fmt);
+ return "S16";
+}
+
+static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
+ int *defaultp)
+{
+ if (!strcasecmp (s, "u8")) {
+ *defaultp = 0;
+ return AUD_FMT_U8;
+ }
+ else if (!strcasecmp (s, "u16")) {
+ *defaultp = 0;
+ return AUD_FMT_U16;
+ }
+ else if (!strcasecmp (s, "u32")) {
+ *defaultp = 0;
+ return AUD_FMT_U32;
+ }
+ else if (!strcasecmp (s, "s8")) {
+ *defaultp = 0;
+ return AUD_FMT_S8;
+ }
+ else if (!strcasecmp (s, "s16")) {
+ *defaultp = 0;
+ return AUD_FMT_S16;
+ }
+ else if (!strcasecmp (s, "s32")) {
+ *defaultp = 0;
+ return AUD_FMT_S32;
+ }
+ else {
+ dolog ("Bogus audio format `%s' using %s\n",
+ s, audio_audfmt_to_string (defval));
+ *defaultp = 1;
+ return defval;
+ }
+}
+
+static audfmt_e audio_get_conf_fmt (const char *envname,
+ audfmt_e defval,
+ int *defaultp)
+{
+ const char *var = getenv (envname);
+ if (!var) {
+ *defaultp = 1;
+ return defval;
+ }
+ return audio_string_to_audfmt (var, defval, defaultp);
+}
+
+static int audio_get_conf_int (const char *key, int defval, int *defaultp)
+{
+ int val;
+ char *strval;
+
+ strval = getenv (key);
+ if (strval) {
+ *defaultp = 0;
+ val = atoi (strval);
+ return val;
+ }
+ else {
+ *defaultp = 1;
+ return defval;
+ }
+}
+
+static const char *audio_get_conf_str (const char *key,
+ const char *defval,
+ int *defaultp)
+{
+ const char *val = getenv (key);
+ if (!val) {
+ *defaultp = 1;
+ return defval;
+ }
+ else {
+ *defaultp = 0;
+ return val;
+ }
+}
+
+void AUD_vlog (const char *cap, const char *fmt, va_list ap)
+{
+ if (conf.log_to_monitor) {
+ if (cap) {
+ monitor_printf(default_mon, "%s: ", cap);
+ }
+
+ monitor_vprintf(default_mon, fmt, ap);
+ }
+ else {
+ if (cap) {
+ fprintf (stderr, "%s: ", cap);
+ }
+
+ vfprintf (stderr, fmt, ap);
+ }
+}
+
+void AUD_log (const char *cap, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (cap, fmt, ap);
+ va_end (ap);
+}
+
+static void audio_print_options (const char *prefix,
+ struct audio_option *opt)
+{
+ char *uprefix;
+
+ if (!prefix) {
+ dolog ("No prefix specified\n");
+ return;
+ }
+
+ if (!opt) {
+ dolog ("No options\n");
+ return;
+ }
+
+ uprefix = audio_alloc_prefix (prefix);
+
+ for (; opt->name; opt++) {
+ const char *state = "default";
+ printf (" %s_%s: ", uprefix, opt->name);
+
+ if (opt->overriddenp && *opt->overriddenp) {
+ state = "current";
+ }
+
+ switch (opt->tag) {
+ case AUD_OPT_BOOL:
+ {
+ int *intp = opt->valp;
+ printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
+ }
+ break;
+
+ case AUD_OPT_INT:
+ {
+ int *intp = opt->valp;
+ printf ("integer, %s = %d\n", state, *intp);
+ }
+ break;
+
+ case AUD_OPT_FMT:
+ {
+ audfmt_e *fmtp = opt->valp;
+ printf (
+ "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
+ state,
+ audio_audfmt_to_string (*fmtp)
+ );
+ }
+ break;
+
+ case AUD_OPT_STR:
+ {
+ const char **strp = opt->valp;
+ printf ("string, %s = %s\n",
+ state,
+ *strp ? *strp : "(not set)");
+ }
+ break;
+
+ default:
+ printf ("???\n");
+ dolog ("Bad value tag for option %s_%s %d\n",
+ uprefix, opt->name, opt->tag);
+ break;
+ }
+ printf (" %s\n", opt->descr);
+ }
+
+ qemu_free (uprefix);
+}
+
+static void audio_process_options (const char *prefix,
+ struct audio_option *opt)
+{
+ char *optname;
+ const char qemu_prefix[] = "QEMU_";
+ size_t preflen, optlen;
+
+ if (audio_bug (AUDIO_FUNC, !prefix)) {
+ dolog ("prefix = NULL\n");
+ return;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !opt)) {
+ dolog ("opt = NULL\n");
+ return;
+ }
+
+ preflen = strlen (prefix);
+
+ for (; opt->name; opt++) {
+ size_t len, i;
+ int def;
+
+ if (!opt->valp) {
+ dolog ("Option value pointer for `%s' is not set\n",
+ opt->name);
+ continue;
+ }
+
+ len = strlen (opt->name);
+ /* len of opt->name + len of prefix + size of qemu_prefix
+ * (includes trailing zero) + zero + underscore (on behalf of
+ * sizeof) */
+ optlen = len + preflen + sizeof (qemu_prefix) + 1;
+ optname = qemu_malloc (optlen);
+
+ pstrcpy (optname, optlen, qemu_prefix);
+
+ /* copy while upper-casing, including trailing zero */
+ for (i = 0; i <= preflen; ++i) {
+ optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]);
+ }
+ pstrcat (optname, optlen, "_");
+ pstrcat (optname, optlen, opt->name);
+
+ def = 1;
+ switch (opt->tag) {
+ case AUD_OPT_BOOL:
+ case AUD_OPT_INT:
+ {
+ int *intp = opt->valp;
+ *intp = audio_get_conf_int (optname, *intp, &def);
+ }
+ break;
+
+ case AUD_OPT_FMT:
+ {
+ audfmt_e *fmtp = opt->valp;
+ *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
+ }
+ break;
+
+ case AUD_OPT_STR:
+ {
+ const char **strp = opt->valp;
+ *strp = audio_get_conf_str (optname, *strp, &def);
+ }
+ break;
+
+ default:
+ dolog ("Bad value tag for option `%s' - %d\n",
+ optname, opt->tag);
+ break;
+ }
+
+ if (!opt->overriddenp) {
+ opt->overriddenp = &opt->overridden;
+ }
+ *opt->overriddenp = !def;
+ qemu_free (optname);
+ }
+}
+
+static void audio_print_settings (struct audsettings *as)
+{
+ dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ AUD_log (NULL, "S8");
+ break;
+ case AUD_FMT_U8:
+ AUD_log (NULL, "U8");
+ break;
+ case AUD_FMT_S16:
+ AUD_log (NULL, "S16");
+ break;
+ case AUD_FMT_U16:
+ AUD_log (NULL, "U16");
+ break;
+ case AUD_FMT_S32:
+ AUD_log (NULL, "S32");
+ break;
+ case AUD_FMT_U32:
+ AUD_log (NULL, "U32");
+ break;
+ default:
+ AUD_log (NULL, "invalid(%d)", as->fmt);
+ break;
+ }
+
+ AUD_log (NULL, " endianness=");
+ switch (as->endianness) {
+ case 0:
+ AUD_log (NULL, "little");
+ break;
+ case 1:
+ AUD_log (NULL, "big");
+ break;
+ default:
+ AUD_log (NULL, "invalid");
+ break;
+ }
+ AUD_log (NULL, "\n");
+}
+
+static int audio_validate_settings (struct audsettings *as)
+{
+ int invalid;
+
+ invalid = as->nchannels != 1 && as->nchannels != 2;
+ invalid |= as->endianness != 0 && as->endianness != 1;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ break;
+ default:
+ invalid = 1;
+ break;
+ }
+
+ invalid |= as->freq <= 0;
+ return invalid ? -1 : 0;
+}
+
+static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as)
+{
+ int bits = 8, sign = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ sign = 1;
+ case AUD_FMT_U8:
+ break;
+
+ case AUD_FMT_S16:
+ sign = 1;
+ case AUD_FMT_U16:
+ bits = 16;
+ break;
+
+ case AUD_FMT_S32:
+ sign = 1;
+ case AUD_FMT_U32:
+ bits = 32;
+ break;
+ }
+ return info->freq == as->freq
+ && info->nchannels == as->nchannels
+ && info->sign == sign
+ && info->bits == bits
+ && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
+}
+
+void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
+{
+ int bits = 8, sign = 0, shift = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ sign = 1;
+ case AUD_FMT_U8:
+ break;
+
+ case AUD_FMT_S16:
+ sign = 1;
+ case AUD_FMT_U16:
+ bits = 16;
+ shift = 1;
+ break;
+
+ case AUD_FMT_S32:
+ sign = 1;
+ case AUD_FMT_U32:
+ bits = 32;
+ shift = 2;
+ break;
+ }
+
+ info->freq = as->freq;
+ info->bits = bits;
+ info->sign = sign;
+ info->nchannels = as->nchannels;
+ info->shift = (as->nchannels == 2) + shift;
+ info->align = (1 << info->shift) - 1;
+ info->bytes_per_second = info->freq << info->shift;
+ info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
+}
+
+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
+{
+ if (!len) {
+ return;
+ }
+
+ if (info->sign) {
+ memset (buf, 0x00, len << info->shift);
+ }
+ else {
+ switch (info->bits) {
+ case 8:
+ memset (buf, 0x80, len << info->shift);
+ break;
+
+ case 16:
+ {
+ int i;
+ uint16_t *p = buf;
+ int shift = info->nchannels - 1;
+ short s = INT16_MAX;
+
+ if (info->swap_endianness) {
+ s = bswap16 (s);
+ }
+
+ for (i = 0; i < len << shift; i++) {
+ p[i] = s;
+ }
+ }
+ break;
+
+ case 32:
+ {
+ int i;
+ uint32_t *p = buf;
+ int shift = info->nchannels - 1;
+ int32_t s = INT32_MAX;
+
+ if (info->swap_endianness) {
+ s = bswap32 (s);
+ }
+
+ for (i = 0; i < len << shift; i++) {
+ p[i] = s;
+ }
+ }
+ break;
+
+ default:
+ AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n",
+ info->bits);
+ break;
+ }
+ }
+}
+
+/*
+ * Capture
+ */
+static void noop_conv (struct st_sample *dst, const void *src, int samples)
+{
+ (void) src;
+ (void) dst;
+ (void) samples;
+}
+
+static CaptureVoiceOut *audio_pcm_capture_find_specific (
+ struct audsettings *as
+ )
+{
+ CaptureVoiceOut *cap;
+ AudioState *s = &glob_audio_state;
+
+ for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+ if (audio_pcm_info_eq (&cap->hw.info, as)) {
+ return cap;
+ }
+ }
+ return NULL;
+}
+
+static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
+{
+ struct capture_callback *cb;
+
+#ifdef DEBUG_CAPTURE
+ dolog ("notification %d sent\n", cmd);
+#endif
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ cb->ops.notify (cb->opaque, cmd);
+ }
+}
+
+static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
+{
+ if (cap->hw.enabled != enabled) {
+ audcnotification_e cmd;
+ cap->hw.enabled = enabled;
+ cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
+ audio_notify_capture (cap, cmd);
+ }
+}
+
+static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
+{
+ HWVoiceOut *hw = &cap->hw;
+ SWVoiceOut *sw;
+ int enabled = 0;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active) {
+ enabled = 1;
+ break;
+ }
+ }
+ audio_capture_maybe_changed (cap, enabled);
+}
+
+static void audio_detach_capture (HWVoiceOut *hw)
+{
+ SWVoiceCap *sc = hw->cap_head.lh_first;
+
+ while (sc) {
+ SWVoiceCap *sc1 = sc->entries.le_next;
+ SWVoiceOut *sw = &sc->sw;
+ CaptureVoiceOut *cap = sc->cap;
+ int was_active = sw->active;
+
+ if (sw->rate) {
+ st_rate_stop (sw->rate);
+ sw->rate = NULL;
+ }
+
+ QLIST_REMOVE (sw, entries);
+ QLIST_REMOVE (sc, entries);
+ qemu_free (sc);
+ if (was_active) {
+ /* We have removed soft voice from the capture:
+ this might have changed the overall status of the capture
+ since this might have been the only active voice */
+ audio_recalc_and_notify_capture (cap);
+ }
+ sc = sc1;
+ }
+}
+
+static int audio_attach_capture (HWVoiceOut *hw)
+{
+ AudioState *s = &glob_audio_state;
+ CaptureVoiceOut *cap;
+
+ audio_detach_capture (hw);
+ for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+ SWVoiceCap *sc;
+ SWVoiceOut *sw;
+ HWVoiceOut *hw_cap = &cap->hw;
+
+ sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
+ if (!sc) {
+ dolog ("Could not allocate soft capture voice (%zu bytes)\n",
+ sizeof (*sc));
+ return -1;
+ }
+
+ sc->cap = cap;
+ sw = &sc->sw;
+ sw->hw = hw_cap;
+ sw->info = hw->info;
+ sw->empty = 1;
+ sw->active = hw->enabled;
+ sw->conv = noop_conv;
+ sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
+ sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
+ if (!sw->rate) {
+ dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
+ qemu_free (sw);
+ return -1;
+ }
+ QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
+ QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);
+#ifdef DEBUG_CAPTURE
+ asprintf (&sw->name, "for %p %d,%d,%d",
+ hw, sw->info.freq, sw->info.bits, sw->info.nchannels);
+ dolog ("Added %s active = %d\n", sw->name, sw->active);
+#endif
+ if (sw->active) {
+ audio_capture_maybe_changed (cap, 1);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Hard voice (capture)
+ */
+static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
+{
+ SWVoiceIn *sw;
+ int m = hw->total_samples_captured;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active) {
+ m = audio_MIN (m, sw->total_hw_samples_acquired);
+ }
+ }
+ return m;
+}
+
+int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
+{
+ int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+ return live;
+}
+
+int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
+ int live, int pending)
+{
+ int left = hw->samples - pending;
+ int len = audio_MIN (left, live);
+ int clipped = 0;
+
+ while (len) {
+ struct st_sample *src = hw->mix_buf + hw->rpos;
+ uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
+ int samples_till_end_of_buf = hw->samples - hw->rpos;
+ int samples_to_clip = audio_MIN (len, samples_till_end_of_buf);
+
+ hw->clip (dst, src, samples_to_clip);
+
+ hw->rpos = (hw->rpos + samples_to_clip) % hw->samples;
+ len -= samples_to_clip;
+ clipped += samples_to_clip;
+ }
+ return clipped;
+}
+
+/*
+ * Soft voice (capture)
+ */
+static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
+{
+ HWVoiceIn *hw = sw->hw;
+ int live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+ int rpos;
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+
+ rpos = hw->wpos - live;
+ if (rpos >= 0) {
+ return rpos;
+ }
+ else {
+ return hw->samples + rpos;
+ }
+}
+
+int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
+{
+ HWVoiceIn *hw = sw->hw;
+ int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
+ struct st_sample *src, *dst = sw->buf;
+
+ rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
+
+ live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live_in=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+
+ samples = size >> sw->info.shift;
+ if (!live) {
+ return 0;
+ }
+
+ swlim = (live * sw->ratio) >> 32;
+ swlim = audio_MIN (swlim, samples);
+
+ while (swlim) {
+ src = hw->conv_buf + rpos;
+ isamp = hw->wpos - rpos;
+ /* XXX: <= ? */
+ if (isamp <= 0) {
+ isamp = hw->samples - rpos;
+ }
+
+ if (!isamp) {
+ break;
+ }
+ osamp = swlim;
+
+ if (audio_bug (AUDIO_FUNC, osamp < 0)) {
+ dolog ("osamp=%d\n", osamp);
+ return 0;
+ }
+
+ st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
+ swlim -= osamp;
+ rpos = (rpos + isamp) % hw->samples;
+ dst += osamp;
+ ret += osamp;
+ total += isamp;
+ }
+
+ mixeng_volume (sw->buf, ret, &sw->vol);
+
+ sw->clip (buf, sw->buf, ret);
+ sw->total_hw_samples_acquired += total;
+ return ret << sw->info.shift;
+}
+
+/*
+ * Hard voice (playback)
+ */
+static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
+{
+ SWVoiceOut *sw;
+ int m = INT_MAX;
+ int nb_live = 0;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active || !sw->empty) {
+ m = audio_MIN (m, sw->total_hw_samples_mixed);
+ nb_live += 1;
+ }
+ }
+
+ *nb_livep = nb_live;
+ return m;
+}
+
+static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
+{
+ int smin;
+ int nb_live1;
+
+ smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
+ if (nb_live) {
+ *nb_live = nb_live1;
+ }
+
+ if (nb_live1) {
+ int live = smin;
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ return 0;
+ }
+ return live;
+ }
+ return 0;
+}
+
+/*
+ * Soft voice (playback)
+ */
+int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
+{
+ int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
+ int ret = 0, pos = 0, total = 0;
+
+ if (!sw) {
+ return size;
+ }
+
+ hwsamples = sw->hw->samples;
+
+ live = sw->total_hw_samples_mixed;
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){
+ dolog ("live=%d hw->samples=%d\n", live, hwsamples);
+ return 0;
+ }
+
+ if (live == hwsamples) {
+#ifdef DEBUG_OUT
+ dolog ("%s is full %d\n", sw->name, live);
+#endif
+ return 0;
+ }
+
+ wpos = (sw->hw->rpos + live) % hwsamples;
+ samples = size >> sw->info.shift;
+
+ dead = hwsamples - live;
+ swlim = ((int64_t) dead << 32) / sw->ratio;
+ swlim = audio_MIN (swlim, samples);
+ if (swlim) {
+ sw->conv (sw->buf, buf, swlim);
+ mixeng_volume (sw->buf, swlim, &sw->vol);
+ }
+
+ while (swlim) {
+ dead = hwsamples - live;
+ left = hwsamples - wpos;
+ blck = audio_MIN (dead, left);
+ if (!blck) {
+ break;
+ }
+ isamp = swlim;
+ osamp = blck;
+ st_rate_flow_mix (
+ sw->rate,
+ sw->buf + pos,
+ sw->hw->mix_buf + wpos,
+ &isamp,
+ &osamp
+ );
+ ret += isamp;
+ swlim -= isamp;
+ pos += isamp;
+ live += osamp;
+ wpos = (wpos + osamp) % hwsamples;
+ total += osamp;
+ }
+
+ sw->total_hw_samples_mixed += total;
+ sw->empty = sw->total_hw_samples_mixed == 0;
+
+#ifdef DEBUG_OUT
+ dolog (
+ "%s: write size %d ret %d total sw %d\n",
+ SW_NAME (sw),
+ size >> sw->info.shift,
+ ret,
+ sw->total_hw_samples_mixed
+ );
+#endif
+
+ return ret << sw->info.shift;
+}
+
+#ifdef DEBUG_AUDIO
+static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
+{
+ dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n",
+ cap, info->bits, info->sign, info->freq, info->nchannels);
+}
+#endif
+
+#define DAC
+#include "audio_template.h"
+#undef DAC
+#include "audio_template.h"
+
+/*
+ * Timer
+ */
+static int audio_is_timer_needed (void)
+{
+ HWVoiceIn *hwi = NULL;
+ HWVoiceOut *hwo = NULL;
+
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+ if (!hwo->poll_mode) return 1;
+ }
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+ if (!hwi->poll_mode) return 1;
+ }
+ return 0;
+}
+
+static void audio_reset_timer (AudioState *s)
+{
+ if (audio_is_timer_needed ()) {
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
+ }
+ else {
+ qemu_del_timer (s->ts);
+ }
+}
+
+static void audio_timer (void *opaque)
+{
+ audio_run ("timer");
+ audio_reset_timer (opaque);
+}
+
+/*
+ * Public API
+ */
+int AUD_write (SWVoiceOut *sw, void *buf, int size)
+{
+ int bytes;
+
+ if (!sw) {
+ /* XXX: Consider options */
+ return size;
+ }
+
+ if (!sw->hw->enabled) {
+ dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
+ return 0;
+ }
+
+ bytes = sw->hw->pcm_ops->write (sw, buf, size);
+ return bytes;
+}
+
+int AUD_read (SWVoiceIn *sw, void *buf, int size)
+{
+ int bytes;
+
+ if (!sw) {
+ /* XXX: Consider options */
+ return size;
+ }
+
+ if (!sw->hw->enabled) {
+ dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
+ return 0;
+ }
+
+ bytes = sw->hw->pcm_ops->read (sw, buf, size);
+ return bytes;
+}
+
+int AUD_get_buffer_size_out (SWVoiceOut *sw)
+{
+ return sw->hw->samples << sw->hw->info.shift;
+}
+
+void AUD_set_active_out (SWVoiceOut *sw, int on)
+{
+ HWVoiceOut *hw;
+
+ if (!sw) {
+ return;
+ }
+
+ hw = sw->hw;
+ if (sw->active != on) {
+ AudioState *s = &glob_audio_state;
+ SWVoiceOut *temp_sw;
+ SWVoiceCap *sc;
+
+ if (on) {
+ hw->pending_disable = 0;
+ if (!hw->enabled) {
+ hw->enabled = 1;
+ if (s->vm_running) {
+ hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
+ audio_reset_timer (s);
+ }
+ }
+ }
+ else {
+ if (hw->enabled) {
+ int nb_active = 0;
+
+ for (temp_sw = hw->sw_head.lh_first; temp_sw;
+ temp_sw = temp_sw->entries.le_next) {
+ nb_active += temp_sw->active != 0;
+ }
+
+ hw->pending_disable = nb_active == 1;
+ }
+ }
+
+ for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ sc->sw.active = hw->enabled;
+ if (hw->enabled) {
+ audio_capture_maybe_changed (sc->cap, 1);
+ }
+ }
+ sw->active = on;
+ }
+}
+
+void AUD_set_active_in (SWVoiceIn *sw, int on)
+{
+ HWVoiceIn *hw;
+
+ if (!sw) {
+ return;
+ }
+
+ hw = sw->hw;
+ if (sw->active != on) {
+ AudioState *s = &glob_audio_state;
+ SWVoiceIn *temp_sw;
+
+ if (on) {
+ if (!hw->enabled) {
+ hw->enabled = 1;
+ if (s->vm_running) {
+ hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
+ audio_reset_timer (s);
+ }
+ }
+ sw->total_hw_samples_acquired = hw->total_samples_captured;
+ }
+ else {
+ if (hw->enabled) {
+ int nb_active = 0;
+
+ for (temp_sw = hw->sw_head.lh_first; temp_sw;
+ temp_sw = temp_sw->entries.le_next) {
+ nb_active += temp_sw->active != 0;
+ }
+
+ if (nb_active == 1) {
+ hw->enabled = 0;
+ hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
+ }
+ }
+ }
+ sw->active = on;
+ }
+}
+
+static int audio_get_avail (SWVoiceIn *sw)
+{
+ int live;
+
+ if (!sw) {
+ return 0;
+ }
+
+ live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+ dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+ return 0;
+ }
+
+ ldebug (
+ "%s: get_avail live %d ret %" PRId64 "\n",
+ SW_NAME (sw),
+ live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift
+ );
+
+ return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
+}
+
+static int audio_get_free (SWVoiceOut *sw)
+{
+ int live, dead;
+
+ if (!sw) {
+ return 0;
+ }
+
+ live = sw->total_hw_samples_mixed;
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+ dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+ return 0;
+ }
+
+ dead = sw->hw->samples - live;
+
+#ifdef DEBUG_OUT
+ dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
+ SW_NAME (sw),
+ live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);
+#endif
+
+ return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
+}
+
+static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
+{
+ int n;
+
+ if (hw->enabled) {
+ SWVoiceCap *sc;
+
+ for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ SWVoiceOut *sw = &sc->sw;
+ int rpos2 = rpos;
+
+ n = samples;
+ while (n) {
+ int till_end_of_hw = hw->samples - rpos2;
+ int to_write = audio_MIN (till_end_of_hw, n);
+ int bytes = to_write << hw->info.shift;
+ int written;
+
+ sw->buf = hw->mix_buf + rpos2;
+ written = audio_pcm_sw_write (sw, NULL, bytes);
+ if (written - bytes) {
+ dolog ("Could not mix %d bytes into a capture "
+ "buffer, mixed %d\n",
+ bytes, written);
+ break;
+ }
+ n -= to_write;
+ rpos2 = (rpos2 + to_write) % hw->samples;
+ }
+ }
+ }
+
+ n = audio_MIN (samples, hw->samples - rpos);
+ mixeng_clear (hw->mix_buf + rpos, n);
+ mixeng_clear (hw->mix_buf, samples - n);
+}
+
+static void audio_run_out (AudioState *s)
+{
+ HWVoiceOut *hw = NULL;
+ SWVoiceOut *sw;
+
+ while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) {
+ int played;
+ int live, free, nb_live, cleanup_required, prev_rpos;
+
+ live = audio_pcm_hw_get_live_out (hw, &nb_live);
+ if (!nb_live) {
+ live = 0;
+ }
+
+ if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+ continue;
+ }
+
+ if (hw->pending_disable && !nb_live) {
+ SWVoiceCap *sc;
+#ifdef DEBUG_OUT
+ dolog ("Disabling voice\n");
+#endif
+ hw->enabled = 0;
+ hw->pending_disable = 0;
+ hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
+ for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ sc->sw.active = 0;
+ audio_recalc_and_notify_capture (sc->cap);
+ }
+ continue;
+ }
+
+ if (!live) {
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (sw->active) {
+ free = audio_get_free (sw);
+ if (free > 0) {
+ sw->callback.fn (sw->callback.opaque, free);
+ }
+ }
+ }
+ continue;
+ }
+
+ prev_rpos = hw->rpos;
+ played = hw->pcm_ops->run_out (hw, live);
+ if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
+ dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
+ hw->rpos, hw->samples, played);
+ hw->rpos = 0;
+ }
+
+#ifdef DEBUG_OUT
+ dolog ("played=%d\n", played);
+#endif
+
+ if (played) {
+ hw->ts_helper += played;
+ audio_capture_mix_and_clear (hw, prev_rpos, played);
+ }
+
+ cleanup_required = 0;
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (!sw->active && sw->empty) {
+ continue;
+ }
+
+ if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) {
+ dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
+ played, sw->total_hw_samples_mixed);
+ played = sw->total_hw_samples_mixed;
+ }
+
+ sw->total_hw_samples_mixed -= played;
+
+ if (!sw->total_hw_samples_mixed) {
+ sw->empty = 1;
+ cleanup_required |= !sw->active && !sw->callback.fn;
+ }
+
+ if (sw->active) {
+ free = audio_get_free (sw);
+ if (free > 0) {
+ sw->callback.fn (sw->callback.opaque, free);
+ }
+ }
+ }
+
+ if (cleanup_required) {
+ SWVoiceOut *sw1;
+
+ sw = hw->sw_head.lh_first;
+ while (sw) {
+ sw1 = sw->entries.le_next;
+ if (!sw->active && !sw->callback.fn) {
+#ifdef DEBUG_PLIVE
+ dolog ("Finishing with old voice\n");
+#endif
+ audio_close_out (sw);
+ }
+ sw = sw1;
+ }
+ }
+ }
+}
+
+static void audio_run_in (AudioState *s)
+{
+ HWVoiceIn *hw = NULL;
+
+ while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) {
+ SWVoiceIn *sw;
+ int captured, min;
+
+ captured = hw->pcm_ops->run_in (hw);
+
+ min = audio_pcm_hw_find_min_in (hw);
+ hw->total_samples_captured += captured - min;
+ hw->ts_helper += captured;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ sw->total_hw_samples_acquired -= min;
+
+ if (sw->active) {
+ int avail;
+
+ avail = audio_get_avail (sw);
+ if (avail > 0) {
+ sw->callback.fn (sw->callback.opaque, avail);
+ }
+ }
+ }
+ }
+}
+
+static void audio_run_capture (AudioState *s)
+{
+ CaptureVoiceOut *cap;
+
+ for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+ int live, rpos, captured;
+ HWVoiceOut *hw = &cap->hw;
+ SWVoiceOut *sw;
+
+ captured = live = audio_pcm_hw_get_live_out (hw, NULL);
+ rpos = hw->rpos;
+ while (live) {
+ int left = hw->samples - rpos;
+ int to_capture = audio_MIN (live, left);
+ struct st_sample *src;
+ struct capture_callback *cb;
+
+ src = hw->mix_buf + rpos;
+ hw->clip (cap->buf, src, to_capture);
+ mixeng_clear (src, to_capture);
+
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ cb->ops.capture (cb->opaque, cap->buf,
+ to_capture << hw->info.shift);
+ }
+ rpos = (rpos + to_capture) % hw->samples;
+ live -= to_capture;
+ }
+ hw->rpos = rpos;
+
+ for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+ if (!sw->active && sw->empty) {
+ continue;
+ }
+
+ if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
+ dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
+ captured, sw->total_hw_samples_mixed);
+ captured = sw->total_hw_samples_mixed;
+ }
+
+ sw->total_hw_samples_mixed -= captured;
+ sw->empty = sw->total_hw_samples_mixed == 0;
+ }
+ }
+}
+
+void audio_run (const char *msg)
+{
+ AudioState *s = &glob_audio_state;
+
+ audio_run_out (s);
+ audio_run_in (s);
+ audio_run_capture (s);
+#ifdef DEBUG_POLL
+ {
+ static double prevtime;
+ double currtime;
+ struct timeval tv;
+
+ if (gettimeofday (&tv, NULL)) {
+ perror ("audio_run: gettimeofday");
+ return;
+ }
+
+ currtime = tv.tv_sec + tv.tv_usec * 1e-6;
+ dolog ("Elapsed since last %s: %f\n", msg, currtime - prevtime);
+ prevtime = currtime;
+ }
+#endif
+}
+
+static struct audio_option audio_options[] = {
+ /* DAC */
+ {
+ .name = "DAC_FIXED_SETTINGS",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.fixed_out.enabled,
+ .descr = "Use fixed settings for host DAC"
+ },
+ {
+ .name = "DAC_FIXED_FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_out.settings.freq,
+ .descr = "Frequency for fixed host DAC"
+ },
+ {
+ .name = "DAC_FIXED_FMT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.fixed_out.settings.fmt,
+ .descr = "Format for fixed host DAC"
+ },
+ {
+ .name = "DAC_FIXED_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_out.settings.nchannels,
+ .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "DAC_VOICES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_out.nb_voices,
+ .descr = "Number of voices for DAC"
+ },
+ {
+ .name = "DAC_TRY_POLL",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.try_poll_out,
+ .descr = "Attempt using poll mode for DAC"
+ },
+ /* ADC */
+ {
+ .name = "ADC_FIXED_SETTINGS",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.fixed_in.enabled,
+ .descr = "Use fixed settings for host ADC"
+ },
+ {
+ .name = "ADC_FIXED_FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_in.settings.freq,
+ .descr = "Frequency for fixed host ADC"
+ },
+ {
+ .name = "ADC_FIXED_FMT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.fixed_in.settings.fmt,
+ .descr = "Format for fixed host ADC"
+ },
+ {
+ .name = "ADC_FIXED_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_in.settings.nchannels,
+ .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "ADC_VOICES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_in.nb_voices,
+ .descr = "Number of voices for ADC"
+ },
+ {
+ .name = "ADC_TRY_POLL",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.try_poll_in,
+ .descr = "Attempt using poll mode for ADC"
+ },
+ /* Misc */
+ {
+ .name = "TIMER_PERIOD",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.period.hertz,
+ .descr = "Timer period in HZ (0 - use lowest possible)"
+ },
+ {
+ .name = "PLIVE",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.plive,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "LOG_TO_MONITOR",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.log_to_monitor,
+ .descr = "Print logging messages to monitor instead of stderr"
+ },
+ { /* End of list */ }
+};
+
+static void audio_pp_nb_voices (const char *typ, int nb)
+{
+ switch (nb) {
+ case 0:
+ printf ("Does not support %s\n", typ);
+ break;
+ case 1:
+ printf ("One %s voice\n", typ);
+ break;
+ case INT_MAX:
+ printf ("Theoretically supports many %s voices\n", typ);
+ break;
+ default:
+ printf ("Theoretically supports upto %d %s voices\n", nb, typ);
+ break;
+ }
+
+}
+
+void AUD_help (void)
+{
+ size_t i;
+
+ audio_process_options ("AUDIO", audio_options);
+ for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+ struct audio_driver *d = drvtab[i];
+ if (d->options) {
+ audio_process_options (d->name, d->options);
+ }
+ }
+
+ printf ("Audio options:\n");
+ audio_print_options ("AUDIO", audio_options);
+ printf ("\n");
+
+ printf ("Available drivers:\n");
+
+ for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+ struct audio_driver *d = drvtab[i];
+
+ printf ("Name: %s\n", d->name);
+ printf ("Description: %s\n", d->descr);
+
+ audio_pp_nb_voices ("playback", d->max_voices_out);
+ audio_pp_nb_voices ("capture", d->max_voices_in);
+
+ if (d->options) {
+ printf ("Options:\n");
+ audio_print_options (d->name, d->options);
+ }
+ else {
+ printf ("No options\n");
+ }
+ printf ("\n");
+ }
+
+ printf (
+ "Options are settable through environment variables.\n"
+ "Example:\n"
+#ifdef _WIN32
+ " set QEMU_AUDIO_DRV=wav\n"
+ " set QEMU_WAV_PATH=c:\\tune.wav\n"
+#else
+ " export QEMU_AUDIO_DRV=wav\n"
+ " export QEMU_WAV_PATH=$HOME/tune.wav\n"
+ "(for csh replace export with setenv in the above)\n"
+#endif
+ " qemu ...\n\n"
+ );
+}
+
+static int audio_driver_init (AudioState *s, struct audio_driver *drv)
+{
+ if (drv->options) {
+ audio_process_options (drv->name, drv->options);
+ }
+ s->drv_opaque = drv->init ();
+
+ if (s->drv_opaque) {
+ audio_init_nb_voices_out (drv);
+ audio_init_nb_voices_in (drv);
+ s->drv = drv;
+ return 0;
+ }
+ else {
+ dolog ("Could not init `%s' audio driver\n", drv->name);
+ return -1;
+ }
+}
+
+static void audio_vm_change_state_handler (void *opaque, int running,
+ int reason)
+{
+ AudioState *s = opaque;
+ HWVoiceOut *hwo = NULL;
+ HWVoiceIn *hwi = NULL;
+ int op = running ? VOICE_ENABLE : VOICE_DISABLE;
+
+ s->vm_running = running;
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+ hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
+ }
+
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+ hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
+ }
+ audio_reset_timer (s);
+}
+
+static void audio_atexit (void)
+{
+ AudioState *s = &glob_audio_state;
+ HWVoiceOut *hwo = NULL;
+ HWVoiceIn *hwi = NULL;
+
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+ SWVoiceCap *sc;
+
+ hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
+ hwo->pcm_ops->fini_out (hwo);
+
+ for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+ CaptureVoiceOut *cap = sc->cap;
+ struct capture_callback *cb;
+
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ cb->ops.destroy (cb->opaque);
+ }
+ }
+ }
+
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+ hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
+ hwi->pcm_ops->fini_in (hwi);
+ }
+
+ if (s->drv) {
+ s->drv->fini (s->drv_opaque);
+ }
+}
+
+static const VMStateDescription vmstate_audio = {
+ .name = "audio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void audio_init (void)
+{
+ size_t i;
+ int done = 0;
+ const char *drvname;
+ VMChangeStateEntry *e;
+ AudioState *s = &glob_audio_state;
+
+ if (s->drv) {
+ return;
+ }
+
+ QLIST_INIT (&s->hw_head_out);
+ QLIST_INIT (&s->hw_head_in);
+ QLIST_INIT (&s->cap_head);
+ atexit (audio_atexit);
+
+ s->ts = qemu_new_timer (vm_clock, audio_timer, s);
+ if (!s->ts) {
+ hw_error("Could not create audio timer\n");
+ }
+
+ audio_process_options ("AUDIO", audio_options);
+
+ s->nb_hw_voices_out = conf.fixed_out.nb_voices;
+ s->nb_hw_voices_in = conf.fixed_in.nb_voices;
+
+ if (s->nb_hw_voices_out <= 0) {
+ dolog ("Bogus number of playback voices %d, setting to 1\n",
+ s->nb_hw_voices_out);
+ s->nb_hw_voices_out = 1;
+ }
+
+ if (s->nb_hw_voices_in <= 0) {
+ dolog ("Bogus number of capture voices %d, setting to 0\n",
+ s->nb_hw_voices_in);
+ s->nb_hw_voices_in = 0;
+ }
+
+ {
+ int def;
+ drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
+ }
+
+ if (drvname) {
+ int found = 0;
+
+ for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+ if (!strcmp (drvname, drvtab[i]->name)) {
+ done = !audio_driver_init (s, drvtab[i]);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dolog ("Unknown audio driver `%s'\n", drvname);
+ dolog ("Run with -audio-help to list available drivers\n");
+ }
+ }
+
+ if (!done) {
+ for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
+ if (drvtab[i]->can_be_default) {
+ done = !audio_driver_init (s, drvtab[i]);
+ }
+ }
+ }
+
+ if (!done) {
+ done = !audio_driver_init (s, &no_audio_driver);
+ if (!done) {
+ hw_error("Could not initialize audio subsystem\n");
+ }
+ else {
+ dolog ("warning: Using timer based audio emulation\n");
+ }
+ }
+
+ if (conf.period.hertz <= 0) {
+ if (conf.period.hertz < 0) {
+ dolog ("warning: Timer period is negative - %d "
+ "treating as zero\n",
+ conf.period.hertz);
+ }
+ conf.period.ticks = 1;
+ } else {
+ conf.period.ticks =
+ muldiv64 (1, get_ticks_per_sec (), conf.period.hertz);
+ }
+
+ e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
+ if (!e) {
+ dolog ("warning: Could not register change state handler\n"
+ "(Audio can continue looping even after stopping the VM)\n");
+ }
+
+ QLIST_INIT (&s->card_head);
+ vmstate_register (NULL, 0, &vmstate_audio, s);
+}
+
+void AUD_register_card (const char *name, QEMUSoundCard *card)
+{
+ audio_init ();
+ card->name = qemu_strdup (name);
+ memset (&card->entries, 0, sizeof (card->entries));
+ QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
+}
+
+void AUD_remove_card (QEMUSoundCard *card)
+{
+ QLIST_REMOVE (card, entries);
+ qemu_free (card->name);
+}
+
+
+CaptureVoiceOut *AUD_add_capture (
+ struct audsettings *as,
+ struct audio_capture_ops *ops,
+ void *cb_opaque
+ )
+{
+ AudioState *s = &glob_audio_state;
+ CaptureVoiceOut *cap;
+ struct capture_callback *cb;
+
+ if (audio_validate_settings (as)) {
+ dolog ("Invalid settings were passed when trying to add capture\n");
+ audio_print_settings (as);
+ goto err0;
+ }
+
+ cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
+ if (!cb) {
+ dolog ("Could not allocate capture callback information, size %zu\n",
+ sizeof (*cb));
+ goto err0;
+ }
+ cb->ops = *ops;
+ cb->opaque = cb_opaque;
+
+ cap = audio_pcm_capture_find_specific (as);
+ if (cap) {
+ QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+ return cap;
+ }
+ else {
+ HWVoiceOut *hw;
+ CaptureVoiceOut *cap;
+
+ cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
+ if (!cap) {
+ dolog ("Could not allocate capture voice, size %zu\n",
+ sizeof (*cap));
+ goto err1;
+ }
+
+ hw = &cap->hw;
+ QLIST_INIT (&hw->sw_head);
+ QLIST_INIT (&cap->cb_head);
+
+ /* XXX find a more elegant way */
+ hw->samples = 4096 * 4;
+ hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
+ sizeof (struct st_sample));
+ if (!hw->mix_buf) {
+ dolog ("Could not allocate capture mix buffer (%d samples)\n",
+ hw->samples);
+ goto err2;
+ }
+
+ audio_pcm_init_info (&hw->info, as);
+
+ cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!cap->buf) {
+ dolog ("Could not allocate capture buffer "
+ "(%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ goto err3;
+ }
+
+ hw->clip = mixeng_clip
+ [hw->info.nchannels == 2]
+ [hw->info.sign]
+ [hw->info.swap_endianness]
+ [audio_bits_to_index (hw->info.bits)];
+
+ QLIST_INSERT_HEAD (&s->cap_head, cap, entries);
+ QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+
+ hw = NULL;
+ while ((hw = audio_pcm_hw_find_any_out (hw))) {
+ audio_attach_capture (hw);
+ }
+ return cap;
+
+ err3:
+ qemu_free (cap->hw.mix_buf);
+ err2:
+ qemu_free (cap);
+ err1:
+ qemu_free (cb);
+ err0:
+ return NULL;
+ }
+}
+
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
+{
+ struct capture_callback *cb;
+
+ for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+ if (cb->opaque == cb_opaque) {
+ cb->ops.destroy (cb_opaque);
+ QLIST_REMOVE (cb, entries);
+ qemu_free (cb);
+
+ if (!cap->cb_head.lh_first) {
+ SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;
+
+ while (sw) {
+ SWVoiceCap *sc = (SWVoiceCap *) sw;
+#ifdef DEBUG_CAPTURE
+ dolog ("freeing %s\n", sw->name);
+#endif
+
+ sw1 = sw->entries.le_next;
+ if (sw->rate) {
+ st_rate_stop (sw->rate);
+ sw->rate = NULL;
+ }
+ QLIST_REMOVE (sw, entries);
+ QLIST_REMOVE (sc, entries);
+ qemu_free (sc);
+ sw = sw1;
+ }
+ QLIST_REMOVE (cap, entries);
+ qemu_free (cap);
+ }
+ return;
+ }
+ }
+}
+
+void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
+{
+ if (sw) {
+ sw->vol.mute = mute;
+ sw->vol.l = nominal_volume.l * lvol / 255;
+ sw->vol.r = nominal_volume.r * rvol / 255;
+ }
+}
+
+void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
+{
+ if (sw) {
+ sw->vol.mute = mute;
+ sw->vol.l = nominal_volume.l * lvol / 255;
+ sw->vol.r = nominal_volume.r * rvol / 255;
+ }
+}
diff --git a/audio/audio.h b/audio/audio.h
new file mode 100644
index 0000000..a70fda9
--- /dev/null
+++ b/audio/audio.h
@@ -0,0 +1,166 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_AUDIO_H
+#define QEMU_AUDIO_H
+
+#include "config-host.h"
+#include "qemu-queue.h"
+
+typedef void (*audio_callback_fn) (void *opaque, int avail);
+
+typedef enum {
+ AUD_FMT_U8,
+ AUD_FMT_S8,
+ AUD_FMT_U16,
+ AUD_FMT_S16,
+ AUD_FMT_U32,
+ AUD_FMT_S32
+} audfmt_e;
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define AUDIO_HOST_ENDIANNESS 1
+#else
+#define AUDIO_HOST_ENDIANNESS 0
+#endif
+
+struct audsettings {
+ int freq;
+ int nchannels;
+ audfmt_e fmt;
+ int endianness;
+};
+
+typedef enum {
+ AUD_CNOTIFY_ENABLE,
+ AUD_CNOTIFY_DISABLE
+} audcnotification_e;
+
+struct audio_capture_ops {
+ void (*notify) (void *opaque, audcnotification_e cmd);
+ void (*capture) (void *opaque, void *buf, int size);
+ void (*destroy) (void *opaque);
+};
+
+struct capture_ops {
+ void (*info) (void *opaque);
+ void (*destroy) (void *opaque);
+};
+
+typedef struct CaptureState {
+ void *opaque;
+ struct capture_ops ops;
+ QLIST_ENTRY (CaptureState) entries;
+} CaptureState;
+
+typedef struct SWVoiceOut SWVoiceOut;
+typedef struct CaptureVoiceOut CaptureVoiceOut;
+typedef struct SWVoiceIn SWVoiceIn;
+
+typedef struct QEMUSoundCard {
+ char *name;
+ QLIST_ENTRY (QEMUSoundCard) entries;
+} QEMUSoundCard;
+
+typedef struct QEMUAudioTimeStamp {
+ uint64_t old_ts;
+} QEMUAudioTimeStamp;
+
+void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
+void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
+
+void AUD_help (void);
+void AUD_register_card (const char *name, QEMUSoundCard *card);
+void AUD_remove_card (QEMUSoundCard *card);
+CaptureVoiceOut *AUD_add_capture (
+ struct audsettings *as,
+ struct audio_capture_ops *ops,
+ void *opaque
+ );
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
+
+SWVoiceOut *AUD_open_out (
+ QEMUSoundCard *card,
+ SWVoiceOut *sw,
+ const char *name,
+ void *callback_opaque,
+ audio_callback_fn callback_fn,
+ struct audsettings *settings
+ );
+
+void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
+int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
+int AUD_get_buffer_size_out (SWVoiceOut *sw);
+void AUD_set_active_out (SWVoiceOut *sw, int on);
+int AUD_is_active_out (SWVoiceOut *sw);
+
+void AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+
+void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
+void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
+
+SWVoiceIn *AUD_open_in (
+ QEMUSoundCard *card,
+ SWVoiceIn *sw,
+ const char *name,
+ void *callback_opaque,
+ audio_callback_fn callback_fn,
+ struct audsettings *settings
+ );
+
+void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
+int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
+void AUD_set_active_in (SWVoiceIn *sw, int on);
+int AUD_is_active_in (SWVoiceIn *sw);
+
+void AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+
+static inline void *advance (void *p, int incr)
+{
+ uint8_t *d = p;
+ return (d + incr);
+}
+
+#ifdef __GNUC__
+#define audio_MIN(a, b) ( __extension__ ({ \
+ __typeof (a) ta = a; \
+ __typeof (b) tb = b; \
+ ((ta)>(tb)?(tb):(ta)); \
+}))
+
+#define audio_MAX(a, b) ( __extension__ ({ \
+ __typeof (a) ta = a; \
+ __typeof (b) tb = b; \
+ ((ta)<(tb)?(tb):(ta)); \
+}))
+#else
+#define audio_MIN(a, b) ((a)>(b)?(b):(a))
+#define audio_MAX(a, b) ((a)<(b)?(b):(a))
+#endif
+
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+ int bits, int nchannels);
+
+#endif /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
new file mode 100644
index 0000000..2003f8b
--- /dev/null
+++ b/audio/audio_int.h
@@ -0,0 +1,282 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_AUDIO_INT_H
+#define QEMU_AUDIO_INT_H
+
+#ifdef CONFIG_COREAUDIO
+#define FLOAT_MIXENG
+/* #define RECIPROCAL */
+#endif
+#include "mixeng.h"
+
+struct audio_pcm_ops;
+
+typedef enum {
+ AUD_OPT_INT,
+ AUD_OPT_FMT,
+ AUD_OPT_STR,
+ AUD_OPT_BOOL
+} audio_option_tag_e;
+
+struct audio_option {
+ const char *name;
+ audio_option_tag_e tag;
+ void *valp;
+ const char *descr;
+ int *overriddenp;
+ int overridden;
+};
+
+struct audio_callback {
+ void *opaque;
+ audio_callback_fn fn;
+};
+
+struct audio_pcm_info {
+ int bits;
+ int sign;
+ int freq;
+ int nchannels;
+ int align;
+ int shift;
+ int bytes_per_second;
+ int swap_endianness;
+};
+
+typedef struct SWVoiceCap SWVoiceCap;
+
+typedef struct HWVoiceOut {
+ int enabled;
+ int poll_mode;
+ int pending_disable;
+ struct audio_pcm_info info;
+
+ f_sample *clip;
+
+ int rpos;
+ uint64_t ts_helper;
+
+ struct st_sample *mix_buf;
+
+ int samples;
+ QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
+ QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
+ struct audio_pcm_ops *pcm_ops;
+ QLIST_ENTRY (HWVoiceOut) entries;
+} HWVoiceOut;
+
+typedef struct HWVoiceIn {
+ int enabled;
+ int poll_mode;
+ struct audio_pcm_info info;
+
+ t_sample *conv;
+
+ int wpos;
+ int total_samples_captured;
+ uint64_t ts_helper;
+
+ struct st_sample *conv_buf;
+
+ int samples;
+ QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
+ struct audio_pcm_ops *pcm_ops;
+ QLIST_ENTRY (HWVoiceIn) entries;
+} HWVoiceIn;
+
+struct SWVoiceOut {
+ QEMUSoundCard *card;
+ struct audio_pcm_info info;
+ t_sample *conv;
+ int64_t ratio;
+ struct st_sample *buf;
+ void *rate;
+ int total_hw_samples_mixed;
+ int active;
+ int empty;
+ HWVoiceOut *hw;
+ char *name;
+ struct mixeng_volume vol;
+ struct audio_callback callback;
+ QLIST_ENTRY (SWVoiceOut) entries;
+};
+
+struct SWVoiceIn {
+ QEMUSoundCard *card;
+ int active;
+ struct audio_pcm_info info;
+ int64_t ratio;
+ void *rate;
+ int total_hw_samples_acquired;
+ struct st_sample *buf;
+ f_sample *clip;
+ HWVoiceIn *hw;
+ char *name;
+ struct mixeng_volume vol;
+ struct audio_callback callback;
+ QLIST_ENTRY (SWVoiceIn) entries;
+};
+
+struct audio_driver {
+ const char *name;
+ const char *descr;
+ struct audio_option *options;
+ void *(*init) (void);
+ void (*fini) (void *);
+ struct audio_pcm_ops *pcm_ops;
+ int can_be_default;
+ int max_voices_out;
+ int max_voices_in;
+ int voice_size_out;
+ int voice_size_in;
+};
+
+struct audio_pcm_ops {
+ int (*init_out)(HWVoiceOut *hw, struct audsettings *as);
+ void (*fini_out)(HWVoiceOut *hw);
+ int (*run_out) (HWVoiceOut *hw, int live);
+ int (*write) (SWVoiceOut *sw, void *buf, int size);
+ int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
+
+ int (*init_in) (HWVoiceIn *hw, struct audsettings *as);
+ void (*fini_in) (HWVoiceIn *hw);
+ int (*run_in) (HWVoiceIn *hw);
+ int (*read) (SWVoiceIn *sw, void *buf, int size);
+ int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
+};
+
+struct capture_callback {
+ struct audio_capture_ops ops;
+ void *opaque;
+ QLIST_ENTRY (capture_callback) entries;
+};
+
+struct CaptureVoiceOut {
+ HWVoiceOut hw;
+ void *buf;
+ QLIST_HEAD (cb_listhead, capture_callback) cb_head;
+ QLIST_ENTRY (CaptureVoiceOut) entries;
+};
+
+struct SWVoiceCap {
+ SWVoiceOut sw;
+ CaptureVoiceOut *cap;
+ QLIST_ENTRY (SWVoiceCap) entries;
+};
+
+struct AudioState {
+ struct audio_driver *drv;
+ void *drv_opaque;
+
+ QEMUTimer *ts;
+ QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
+ QLIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
+ QLIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
+ QLIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
+ int nb_hw_voices_out;
+ int nb_hw_voices_in;
+ int vm_running;
+};
+
+extern struct audio_driver no_audio_driver;
+extern struct audio_driver oss_audio_driver;
+extern struct audio_driver sdl_audio_driver;
+extern struct audio_driver wav_audio_driver;
+extern struct audio_driver fmod_audio_driver;
+extern struct audio_driver alsa_audio_driver;
+extern struct audio_driver coreaudio_audio_driver;
+extern struct audio_driver dsound_audio_driver;
+extern struct audio_driver esd_audio_driver;
+extern struct audio_driver pa_audio_driver;
+extern struct audio_driver spice_audio_driver;
+extern struct audio_driver winwave_audio_driver;
+extern const struct mixeng_volume nominal_volume;
+
+void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
+
+int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
+int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
+
+int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
+
+int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
+ int live, int pending);
+
+int audio_bug (const char *funcname, int cond);
+void *audio_calloc (const char *funcname, int nmemb, size_t size);
+
+void audio_run (const char *msg);
+
+#define VOICE_ENABLE 1
+#define VOICE_DISABLE 2
+
+static inline int audio_ring_dist (int dst, int src, int len)
+{
+ return (dst >= src) ? (dst - src) : (len - src + dst);
+}
+
+static void GCC_ATTR dolog (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+}
+
+#ifdef DEBUG
+static void GCC_ATTR ldebug (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+}
+#else
+#if defined NDEBUG && defined __GNUC__
+#define ldebug(...)
+#elif defined NDEBUG && defined _MSC_VER
+#define ldebug __noop
+#else
+static void GCC_ATTR ldebug (const char *fmt, ...)
+{
+ (void) fmt;
+}
+#endif
+#endif
+
+#undef GCC_ATTR
+
+#define AUDIO_STRINGIFY_(n) #n
+#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
+
+#if defined _MSC_VER || defined __GNUC__
+#define AUDIO_FUNC __FUNCTION__
+#else
+#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
+#endif
+
+#endif /* audio_int.h */
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
new file mode 100644
index 0000000..908c569
--- /dev/null
+++ b/audio/audio_pt_int.c
@@ -0,0 +1,175 @@
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "audio-pt"
+
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+#include <signal.h>
+
+static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (pt->drv, fmt, ap);
+ va_end (ap);
+
+ AUD_log (NULL, "\n");
+ AUD_log (pt->drv, "Reason: %s\n", strerror (err));
+}
+
+int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
+ void *opaque, const char *drv, const char *cap)
+{
+ int err, err2;
+ const char *efunc;
+ sigset_t set, old_set;
+
+ p->drv = drv;
+
+ err = sigfillset (&set);
+ if (err) {
+ logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+
+ err = pthread_mutex_init (&p->mutex, NULL);
+ if (err) {
+ efunc = "pthread_mutex_init";
+ goto err0;
+ }
+
+ err = pthread_cond_init (&p->cond, NULL);
+ if (err) {
+ efunc = "pthread_cond_init";
+ goto err1;
+ }
+
+ err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+ if (err) {
+ efunc = "pthread_sigmask";
+ goto err2;
+ }
+
+ err = pthread_create (&p->thread, NULL, func, opaque);
+
+ err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed",
+ cap, AUDIO_FUNC);
+ /* We have failed to restore original signal mask, all bets are off,
+ so terminate the process */
+ exit (EXIT_FAILURE);
+ }
+
+ if (err) {
+ efunc = "pthread_create";
+ goto err2;
+ }
+
+ return 0;
+
+ err2:
+ err2 = pthread_cond_destroy (&p->cond);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+ }
+
+ err1:
+ err2 = pthread_mutex_destroy (&p->mutex);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+ }
+
+ err0:
+ logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
+ return -1;
+}
+
+int audio_pt_fini (struct audio_pt *p, const char *cap)
+{
+ int err, ret = 0;
+
+ err = pthread_cond_destroy (&p->cond);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+ ret = -1;
+ }
+
+ err = pthread_mutex_destroy (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+ ret = -1;
+ }
+ return ret;
+}
+
+int audio_pt_lock (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_lock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_unlock (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_wait (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_cond_wait (&p->cond, &p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ err = pthread_cond_signal (&p->cond);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
+{
+ int err;
+ void *ret;
+
+ err = pthread_join (p->thread, &ret);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ *arg = ret;
+ return 0;
+}
diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h
new file mode 100644
index 0000000..0dfff76
--- /dev/null
+++ b/audio/audio_pt_int.h
@@ -0,0 +1,22 @@
+#ifndef QEMU_AUDIO_PT_INT_H
+#define QEMU_AUDIO_PT_INT_H
+
+#include <pthread.h>
+
+struct audio_pt {
+ const char *drv;
+ pthread_t thread;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+};
+
+int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
+ const char *, const char *);
+int audio_pt_fini (struct audio_pt *, const char *);
+int audio_pt_lock (struct audio_pt *, const char *);
+int audio_pt_unlock (struct audio_pt *, const char *);
+int audio_pt_wait (struct audio_pt *, const char *);
+int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
+int audio_pt_join (struct audio_pt *, void **, const char *);
+
+#endif /* audio_pt_int.h */
diff --git a/audio/audio_template.h b/audio/audio_template.h
new file mode 100644
index 0000000..fd4469e
--- /dev/null
+++ b/audio/audio_template.h
@@ -0,0 +1,560 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef DAC
+#define NAME "playback"
+#define HWBUF hw->mix_buf
+#define TYPE out
+#define HW HWVoiceOut
+#define SW SWVoiceOut
+#else
+#define NAME "capture"
+#define TYPE in
+#define HW HWVoiceIn
+#define SW SWVoiceIn
+#define HWBUF hw->conv_buf
+#endif
+
+static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
+{
+ AudioState *s = &glob_audio_state;
+ int max_voices = glue (drv->max_voices_, TYPE);
+ int voice_size = glue (drv->voice_size_, TYPE);
+
+ if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
+ if (!max_voices) {
+#ifdef DAC
+ dolog ("Driver `%s' does not support " NAME "\n", drv->name);
+#endif
+ }
+ else {
+ dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
+ drv->name,
+ glue (s->nb_hw_voices_, TYPE),
+ max_voices);
+ }
+ glue (s->nb_hw_voices_, TYPE) = max_voices;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
+ dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
+ drv->name, max_voices);
+ glue (s->nb_hw_voices_, TYPE) = 0;
+ }
+
+ if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
+ dolog ("drv=`%s' voice_size=%d max_voices=0\n",
+ drv->name, voice_size);
+ }
+}
+
+static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
+{
+ if (HWBUF) {
+ qemu_free (HWBUF);
+ }
+
+ HWBUF = NULL;
+}
+
+static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
+{
+ HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (struct st_sample));
+ if (!HWBUF) {
+ dolog ("Could not allocate " NAME " buffer (%d samples)\n",
+ hw->samples);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
+{
+ if (sw->buf) {
+ qemu_free (sw->buf);
+ }
+
+ if (sw->rate) {
+ st_rate_stop (sw->rate);
+ }
+
+ sw->buf = NULL;
+ sw->rate = NULL;
+}
+
+static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
+{
+ int samples;
+
+ samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
+
+ sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
+ if (!sw->buf) {
+ dolog ("Could not allocate buffer for `%s' (%d samples)\n",
+ SW_NAME (sw), samples);
+ return -1;
+ }
+
+#ifdef DAC
+ sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
+#else
+ sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
+#endif
+ if (!sw->rate) {
+ qemu_free (sw->buf);
+ sw->buf = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+static int glue (audio_pcm_sw_init_, TYPE) (
+ SW *sw,
+ HW *hw,
+ const char *name,
+ struct audsettings *as
+ )
+{
+ int err;
+
+ audio_pcm_init_info (&sw->info, as);
+ sw->hw = hw;
+ sw->active = 0;
+#ifdef DAC
+ sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
+ sw->total_hw_samples_mixed = 0;
+ sw->empty = 1;
+#else
+ sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
+#endif
+
+#ifdef DAC
+ sw->conv = mixeng_conv
+#else
+ sw->clip = mixeng_clip
+#endif
+ [sw->info.nchannels == 2]
+ [sw->info.sign]
+ [sw->info.swap_endianness]
+ [audio_bits_to_index (sw->info.bits)];
+
+ sw->name = qemu_strdup (name);
+ err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
+ if (err) {
+ qemu_free (sw->name);
+ sw->name = NULL;
+ }
+ return err;
+}
+
+static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
+{
+ glue (audio_pcm_sw_free_resources_, TYPE) (sw);
+ if (sw->name) {
+ qemu_free (sw->name);
+ sw->name = NULL;
+ }
+}
+
+static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
+{
+ QLIST_INSERT_HEAD (&hw->sw_head, sw, entries);
+}
+
+static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
+{
+ QLIST_REMOVE (sw, entries);
+}
+
+static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
+{
+ AudioState *s = &glob_audio_state;
+ HW *hw = *hwp;
+
+ if (!hw->sw_head.lh_first) {
+#ifdef DAC
+ audio_detach_capture (hw);
+#endif
+ QLIST_REMOVE (hw, entries);
+ glue (s->nb_hw_voices_, TYPE) += 1;
+ glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
+ glue (hw->pcm_ops->fini_, TYPE) (hw);
+ qemu_free (hw);
+ *hwp = NULL;
+ }
+}
+
+static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
+{
+ AudioState *s = &glob_audio_state;
+ return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
+}
+
+static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
+{
+ while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
+ if (hw->enabled) {
+ return hw;
+ }
+ }
+ return NULL;
+}
+
+static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
+ HW *hw,
+ struct audsettings *as
+ )
+{
+ while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
+ if (audio_pcm_info_eq (&hw->info, as)) {
+ return hw;
+ }
+ }
+ return NULL;
+}
+
+static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
+{
+ HW *hw;
+ AudioState *s = &glob_audio_state;
+ struct audio_driver *drv = s->drv;
+
+ if (!glue (s->nb_hw_voices_, TYPE)) {
+ return NULL;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !drv)) {
+ dolog ("No host audio driver\n");
+ return NULL;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
+ dolog ("Host audio driver without pcm_ops\n");
+ return NULL;
+ }
+
+ hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
+ if (!hw) {
+ dolog ("Can not allocate voice `%s' size %d\n",
+ drv->name, glue (drv->voice_size_, TYPE));
+ return NULL;
+ }
+
+ hw->pcm_ops = drv->pcm_ops;
+ QLIST_INIT (&hw->sw_head);
+#ifdef DAC
+ QLIST_INIT (&hw->cap_head);
+#endif
+ if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
+ goto err0;
+ }
+
+ if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
+ dolog ("hw->samples=%d\n", hw->samples);
+ goto err1;
+ }
+
+#ifdef DAC
+ hw->clip = mixeng_clip
+#else
+ hw->conv = mixeng_conv
+#endif
+ [hw->info.nchannels == 2]
+ [hw->info.sign]
+ [hw->info.swap_endianness]
+ [audio_bits_to_index (hw->info.bits)];
+
+ if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
+ goto err1;
+ }
+
+ QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
+ glue (s->nb_hw_voices_, TYPE) -= 1;
+#ifdef DAC
+ audio_attach_capture (hw);
+#endif
+ return hw;
+
+ err1:
+ glue (hw->pcm_ops->fini_, TYPE) (hw);
+ err0:
+ qemu_free (hw);
+ return NULL;
+}
+
+static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
+{
+ HW *hw;
+
+ if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+ hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
+ if (hw) {
+ return hw;
+ }
+ }
+
+ hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
+ if (hw) {
+ return hw;
+ }
+
+ hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
+ if (hw) {
+ return hw;
+ }
+
+ return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
+}
+
+static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
+ const char *sw_name,
+ struct audsettings *as
+ )
+{
+ SW *sw;
+ HW *hw;
+ struct audsettings hw_as;
+
+ if (glue (conf.fixed_, TYPE).enabled) {
+ hw_as = glue (conf.fixed_, TYPE).settings;
+ }
+ else {
+ hw_as = *as;
+ }
+
+ sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
+ if (!sw) {
+ dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
+ sw_name ? sw_name : "unknown", sizeof (*sw));
+ goto err1;
+ }
+
+ hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
+ if (!hw) {
+ goto err2;
+ }
+
+ glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
+
+ if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
+ goto err3;
+ }
+
+ return sw;
+
+err3:
+ glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+ glue (audio_pcm_hw_gc_, TYPE) (&hw);
+err2:
+ qemu_free (sw);
+err1:
+ return NULL;
+}
+
+static void glue (audio_close_, TYPE) (SW *sw)
+{
+ glue (audio_pcm_sw_fini_, TYPE) (sw);
+ glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+ glue (audio_pcm_hw_gc_, TYPE) (&sw->hw);
+ qemu_free (sw);
+}
+
+void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
+{
+ if (sw) {
+ if (audio_bug (AUDIO_FUNC, !card)) {
+ dolog ("card=%p\n", card);
+ return;
+ }
+
+ glue (audio_close_, TYPE) (sw);
+ }
+}
+
+SW *glue (AUD_open_, TYPE) (
+ QEMUSoundCard *card,
+ SW *sw,
+ const char *name,
+ void *callback_opaque ,
+ audio_callback_fn callback_fn,
+ struct audsettings *as
+ )
+{
+ AudioState *s = &glob_audio_state;
+#ifdef DAC
+ int live = 0;
+ SW *old_sw = NULL;
+#endif
+
+ ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
+ name, as->freq, as->nchannels, as->fmt);
+
+ if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
+ dolog ("card=%p name=%p callback_fn=%p as=%p\n",
+ card, name, callback_fn, as);
+ goto fail;
+ }
+
+ if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
+ audio_print_settings (as);
+ goto fail;
+ }
+
+ if (audio_bug (AUDIO_FUNC, !s->drv)) {
+ dolog ("Can not open `%s' (no host audio driver)\n", name);
+ goto fail;
+ }
+
+ if (sw && audio_pcm_info_eq (&sw->info, as)) {
+ return sw;
+ }
+
+#ifdef DAC
+ if (conf.plive && sw && (!sw->active && !sw->empty)) {
+ live = sw->total_hw_samples_mixed;
+
+#ifdef DEBUG_PLIVE
+ dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
+ dolog ("Old %s freq %d, bits %d, channels %d\n",
+ SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
+ dolog ("New %s freq %d, bits %d, channels %d\n",
+ name,
+ as->freq,
+ (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8,
+ as->nchannels);
+#endif
+
+ if (live) {
+ old_sw = sw;
+ old_sw->callback.fn = NULL;
+ sw = NULL;
+ }
+ }
+#endif
+
+ if (!glue (conf.fixed_, TYPE).enabled && sw) {
+ glue (AUD_close_, TYPE) (card, sw);
+ sw = NULL;
+ }
+
+ if (sw) {
+ HW *hw = sw->hw;
+
+ if (!hw) {
+ dolog ("Internal logic error voice `%s' has no hardware store\n",
+ SW_NAME (sw));
+ goto fail;
+ }
+
+ glue (audio_pcm_sw_fini_, TYPE) (sw);
+ if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
+ goto fail;
+ }
+ }
+ else {
+ sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
+ if (!sw) {
+ dolog ("Failed to create voice `%s'\n", name);
+ return NULL;
+ }
+ }
+
+ sw->card = card;
+ sw->vol = nominal_volume;
+ sw->callback.fn = callback_fn;
+ sw->callback.opaque = callback_opaque;
+
+#ifdef DAC
+ if (live) {
+ int mixed =
+ (live << old_sw->info.shift)
+ * old_sw->info.bytes_per_second
+ / sw->info.bytes_per_second;
+
+#ifdef DEBUG_PLIVE
+ dolog ("Silence will be mixed %d\n", mixed);
+#endif
+ sw->total_hw_samples_mixed += mixed;
+ }
+#endif
+
+#ifdef DEBUG_AUDIO
+ dolog ("%s\n", name);
+ audio_pcm_print_info ("hw", &sw->hw->info);
+ audio_pcm_print_info ("sw", &sw->info);
+#endif
+
+ return sw;
+
+ fail:
+ glue (AUD_close_, TYPE) (card, sw);
+ return NULL;
+}
+
+int glue (AUD_is_active_, TYPE) (SW *sw)
+{
+ return sw ? sw->active : 0;
+}
+
+void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
+{
+ if (!sw) {
+ return;
+ }
+
+ ts->old_ts = sw->hw->ts_helper;
+}
+
+uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
+{
+ uint64_t delta, cur_ts, old_ts;
+
+ if (!sw) {
+ return 0;
+ }
+
+ cur_ts = sw->hw->ts_helper;
+ old_ts = ts->old_ts;
+ /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */
+
+ if (cur_ts >= old_ts) {
+ delta = cur_ts - old_ts;
+ }
+ else {
+ delta = UINT64_MAX - old_ts + cur_ts;
+ }
+
+ if (!delta) {
+ return 0;
+ }
+
+ return muldiv64 (delta, sw->hw->info.freq, 1000000);
+}
+
+#undef TYPE
+#undef HW
+#undef SW
+#undef HWBUF
+#undef NAME
diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c
new file mode 100644
index 0000000..5869052
--- /dev/null
+++ b/audio/audio_win_int.c
@@ -0,0 +1,108 @@
+/* public domain */
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "win-int"
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "audio.h"
+#include "audio_int.h"
+#include "audio_win_int.h"
+
+int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as)
+{
+ memset (wfx, 0, sizeof (*wfx));
+
+ wfx->wFormatTag = WAVE_FORMAT_PCM;
+ wfx->nChannels = as->nchannels;
+ wfx->nSamplesPerSec = as->freq;
+ wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
+ wfx->nBlockAlign = 1 << (as->nchannels == 2);
+ wfx->cbSize = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ wfx->wBitsPerSample = 8;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ wfx->wBitsPerSample = 16;
+ wfx->nAvgBytesPerSec <<= 1;
+ wfx->nBlockAlign <<= 1;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ wfx->wBitsPerSample = 32;
+ wfx->nAvgBytesPerSec <<= 2;
+ wfx->nBlockAlign <<= 2;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", as->freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as)
+{
+ if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
+ dolog ("Invalid wave format, tag is not PCM, but %d\n",
+ wfx->wFormatTag);
+ return -1;
+ }
+
+ if (!wfx->nSamplesPerSec) {
+ dolog ("Invalid wave format, frequency is zero\n");
+ return -1;
+ }
+ as->freq = wfx->nSamplesPerSec;
+
+ switch (wfx->nChannels) {
+ case 1:
+ as->nchannels = 1;
+ break;
+
+ case 2:
+ as->nchannels = 2;
+ break;
+
+ default:
+ dolog (
+ "Invalid wave format, number of channels is not 1 or 2, but %d\n",
+ wfx->nChannels
+ );
+ return -1;
+ }
+
+ switch (wfx->wBitsPerSample) {
+ case 8:
+ as->fmt = AUD_FMT_U8;
+ break;
+
+ case 16:
+ as->fmt = AUD_FMT_S16;
+ break;
+
+ case 32:
+ as->fmt = AUD_FMT_S32;
+ break;
+
+ default:
+ dolog ("Invalid wave format, bits per sample is not "
+ "8, 16 or 32, but %d\n",
+ wfx->wBitsPerSample);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/audio/audio_win_int.h b/audio/audio_win_int.h
new file mode 100644
index 0000000..fa5b3cb
--- /dev/null
+++ b/audio/audio_win_int.h
@@ -0,0 +1,10 @@
+#ifndef AUDIO_WIN_INT_H
+#define AUDIO_WIN_INT_H
+
+int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as);
+
+int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as);
+
+#endif /* AUDIO_WIN_INT_H */
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
new file mode 100644
index 0000000..0a26413
--- /dev/null
+++ b/audio/coreaudio.c
@@ -0,0 +1,549 @@
+/*
+ * QEMU OS X CoreAudio audio driver
+ *
+ * Copyright (c) 2005 Mike Kronenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#include <string.h> /* strerror */
+#include <pthread.h> /* pthread_X */
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "coreaudio"
+#include "audio_int.h"
+
+struct {
+ int buffer_frames;
+ int nbuffers;
+ int isAtexit;
+} conf = {
+ .buffer_frames = 512,
+ .nbuffers = 4,
+ .isAtexit = 0
+};
+
+typedef struct coreaudioVoiceOut {
+ HWVoiceOut hw;
+ pthread_mutex_t mutex;
+ int isAtexit;
+ AudioDeviceID outputDeviceID;
+ UInt32 audioDevicePropertyBufferFrameSize;
+ AudioStreamBasicDescription outputStreamBasicDescription;
+ int live;
+ int decr;
+ int rpos;
+} coreaudioVoiceOut;
+
+static void coreaudio_logstatus (OSStatus status)
+{
+ char *str = "BUG";
+
+ switch(status) {
+ case kAudioHardwareNoError:
+ str = "kAudioHardwareNoError";
+ break;
+
+ case kAudioHardwareNotRunningError:
+ str = "kAudioHardwareNotRunningError";
+ break;
+
+ case kAudioHardwareUnspecifiedError:
+ str = "kAudioHardwareUnspecifiedError";
+ break;
+
+ case kAudioHardwareUnknownPropertyError:
+ str = "kAudioHardwareUnknownPropertyError";
+ break;
+
+ case kAudioHardwareBadPropertySizeError:
+ str = "kAudioHardwareBadPropertySizeError";
+ break;
+
+ case kAudioHardwareIllegalOperationError:
+ str = "kAudioHardwareIllegalOperationError";
+ break;
+
+ case kAudioHardwareBadDeviceError:
+ str = "kAudioHardwareBadDeviceError";
+ break;
+
+ case kAudioHardwareBadStreamError:
+ str = "kAudioHardwareBadStreamError";
+ break;
+
+ case kAudioHardwareUnsupportedOperationError:
+ str = "kAudioHardwareUnsupportedOperationError";
+ break;
+
+ case kAudioDeviceUnsupportedFormatError:
+ str = "kAudioDeviceUnsupportedFormatError";
+ break;
+
+ case kAudioDevicePermissionsError:
+ str = "kAudioDevicePermissionsError";
+ break;
+
+ default:
+ AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
+ return;
+ }
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
+ OSStatus status,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_log (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ coreaudio_logstatus (status);
+}
+
+static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
+ OSStatus status,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ coreaudio_logstatus (status);
+}
+
+static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
+{
+ OSStatus status;
+ UInt32 result = 0;
+ UInt32 propertySize = sizeof(outputDeviceID);
+ status = AudioDeviceGetProperty(
+ outputDeviceID, 0, 0,
+ kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr(status,
+ "Could not determine whether Device is playing\n");
+ }
+ return result;
+}
+
+static void coreaudio_atexit (void)
+{
+ conf.isAtexit = 1;
+}
+
+static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_lock (&core->mutex);
+ if (err) {
+ dolog ("Could not lock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&core->mutex);
+ if (err) {
+ dolog ("Could not unlock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int coreaudio_run_out (HWVoiceOut *hw, int live)
+{
+ int decr;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ if (coreaudio_lock (core, "coreaudio_run_out")) {
+ return 0;
+ }
+
+ if (core->decr > live) {
+ ldebug ("core->decr %d live %d core->live %d\n",
+ core->decr,
+ live,
+ core->live);
+ }
+
+ decr = audio_MIN (core->decr, live);
+ core->decr -= decr;
+
+ core->live = live - decr;
+ hw->rpos = core->rpos;
+
+ coreaudio_unlock (core, "coreaudio_run_out");
+ return decr;
+}
+
+/* callback to feed audiooutput buffer */
+static OSStatus audioDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* hwptr)
+{
+ UInt32 frame, frameCount;
+ float *out = outOutputData->mBuffers[0].mData;
+ HWVoiceOut *hw = hwptr;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
+ int rpos, live;
+ struct st_sample *src;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ const float scale = 1.f / UINT_MAX;
+#else
+ const float scale = UINT_MAX;
+#endif
+#endif
+
+ if (coreaudio_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
+ }
+
+ frameCount = core->audioDevicePropertyBufferFrameSize;
+ live = core->live;
+
+ /* if there are not enough samples, set signal and return */
+ if (live < frameCount) {
+ inInputTime = 0;
+ coreaudio_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
+ }
+
+ rpos = core->rpos;
+ src = hw->mix_buf + rpos;
+
+ /* fill buffer */
+ for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+ *out++ = src[frame].l; /* left channel */
+ *out++ = src[frame].r; /* right channel */
+#else
+#ifdef RECIPROCAL
+ *out++ = src[frame].l * scale; /* left channel */
+ *out++ = src[frame].r * scale; /* right channel */
+#else
+ *out++ = src[frame].l / scale; /* left channel */
+ *out++ = src[frame].r / scale; /* right channel */
+#endif
+#endif
+ }
+
+ rpos = (rpos + frameCount) % hw->samples;
+ core->decr += frameCount;
+ core->rpos = rpos;
+
+ coreaudio_unlock (core, "audioDeviceIOProc");
+ return 0;
+}
+
+static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ OSStatus status;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ UInt32 propertySize;
+ int err;
+ const char *typ = "playback";
+ AudioValueRange frameRange;
+
+ /* create mutex */
+ err = pthread_mutex_init(&core->mutex, NULL);
+ if (err) {
+ dolog("Could not create mutex\nReason: %s\n", strerror (err));
+ return -1;
+ }
+
+ audio_pcm_init_info (&hw->info, as);
+
+ /* open default output device */
+ propertySize = sizeof(core->outputDeviceID);
+ status = AudioHardwareGetProperty(
+ kAudioHardwarePropertyDefaultOutputDevice,
+ &propertySize,
+ &core->outputDeviceID);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get default output Device\n");
+ return -1;
+ }
+ if (core->outputDeviceID == kAudioDeviceUnknown) {
+ dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
+ return -1;
+ }
+
+ /* get minimum and maximum buffer frame sizes */
+ propertySize = sizeof(frameRange);
+ status = AudioDeviceGetProperty(
+ core->outputDeviceID,
+ 0,
+ 0,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &propertySize,
+ &frameRange);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame range\n");
+ return -1;
+ }
+
+ if (frameRange.mMinimum > conf.buffer_frames) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
+ dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
+ }
+ else if (frameRange.mMaximum < conf.buffer_frames) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
+ dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
+ }
+ else {
+ core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
+ }
+
+ /* set Buffer Frame Size */
+ propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+ status = AudioDeviceSetProperty(
+ core->outputDeviceID,
+ NULL,
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSize,
+ propertySize,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not set device buffer frame size %ld\n",
+ core->audioDevicePropertyBufferFrameSize);
+ return -1;
+ }
+
+ /* get Buffer Frame Size */
+ propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+ status = AudioDeviceGetProperty(
+ core->outputDeviceID,
+ 0,
+ false,
+ kAudioDevicePropertyBufferFrameSize,
+ &propertySize,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame size\n");
+ return -1;
+ }
+ hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
+
+ /* get StreamFormat */
+ propertySize = sizeof(core->outputStreamBasicDescription);
+ status = AudioDeviceGetProperty(
+ core->outputDeviceID,
+ 0,
+ false,
+ kAudioDevicePropertyStreamFormat,
+ &propertySize,
+ &core->outputStreamBasicDescription);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get Device Stream properties\n");
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* set Samplerate */
+ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
+ propertySize = sizeof(core->outputStreamBasicDescription);
+ status = AudioDeviceSetProperty(
+ core->outputDeviceID,
+ 0,
+ 0,
+ 0,
+ kAudioDevicePropertyStreamFormat,
+ propertySize,
+ &core->outputStreamBasicDescription);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
+ as->freq);
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* set Callback */
+ status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* start Playback */
+ if (!isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not start playback\n");
+ AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void coreaudio_fini_out (HWVoiceOut *hw)
+{
+ OSStatus status;
+ int err;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ if (!conf.isAtexit) {
+ /* stop playback */
+ if (isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not stop playback\n");
+ }
+ }
+
+ /* remove callback */
+ status = AudioDeviceRemoveIOProc(core->outputDeviceID,
+ audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not remove IOProc\n");
+ }
+ }
+ core->outputDeviceID = kAudioDeviceUnknown;
+
+ /* destroy mutex */
+ err = pthread_mutex_destroy(&core->mutex);
+ if (err) {
+ dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+ }
+}
+
+static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ OSStatus status;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ /* start playback */
+ if (!isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not resume playback\n");
+ }
+ }
+ break;
+
+ case VOICE_DISABLE:
+ /* stop playback */
+ if (!conf.isAtexit) {
+ if (isPlaying(core->outputDeviceID)) {
+ status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not pause playback\n");
+ }
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static void *coreaudio_audio_init (void)
+{
+ atexit(coreaudio_atexit);
+ return &coreaudio_audio_init;
+}
+
+static void coreaudio_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option coreaudio_options[] = {
+ {
+ .name = "BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.buffer_frames,
+ .descr = "Size of the buffer in frames"
+ },
+ {
+ .name = "BUFFER_COUNT",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nbuffers,
+ .descr = "Number of buffers"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops coreaudio_pcm_ops = {
+ .init_out = coreaudio_init_out,
+ .fini_out = coreaudio_fini_out,
+ .run_out = coreaudio_run_out,
+ .write = coreaudio_write,
+ .ctl_out = coreaudio_ctl_out
+};
+
+struct audio_driver coreaudio_audio_driver = {
+ .name = "coreaudio",
+ .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
+ .options = coreaudio_options,
+ .init = coreaudio_audio_init,
+ .fini = coreaudio_audio_fini,
+ .pcm_ops = &coreaudio_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = 1,
+ .max_voices_in = 0,
+ .voice_size_out = sizeof (coreaudioVoiceOut),
+ .voice_size_in = 0
+};
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
new file mode 100644
index 0000000..8b37d16
--- /dev/null
+++ b/audio/dsound_template.h
@@ -0,0 +1,293 @@
+/*
+ * QEMU DirectSound audio driver header
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef DSBTYPE_IN
+#define NAME "capture buffer"
+#define NAME2 "DirectSoundCapture"
+#define TYPE in
+#define IFACE IDirectSoundCaptureBuffer
+#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
+#define FIELD dsound_capture_buffer
+#define FIELD2 dsound_capture
+#else
+#define NAME "playback buffer"
+#define NAME2 "DirectSound"
+#define TYPE out
+#define IFACE IDirectSoundBuffer
+#define BUFPTR LPDIRECTSOUNDBUFFER
+#define FIELD dsound_buffer
+#define FIELD2 dsound
+#endif
+
+static int glue (dsound_unlock_, TYPE) (
+ BUFPTR buf,
+ LPVOID p1,
+ LPVOID p2,
+ DWORD blen1,
+ DWORD blen2
+ )
+{
+ HRESULT hr;
+
+ hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not unlock " NAME "\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int glue (dsound_lock_, TYPE) (
+ BUFPTR buf,
+ struct audio_pcm_info *info,
+ DWORD pos,
+ DWORD len,
+ LPVOID *p1p,
+ LPVOID *p2p,
+ DWORD *blen1p,
+ DWORD *blen2p,
+ int entire
+ )
+{
+ HRESULT hr;
+ int i;
+ LPVOID p1 = NULL, p2 = NULL;
+ DWORD blen1 = 0, blen2 = 0;
+ DWORD flag;
+
+#ifdef DSBTYPE_IN
+ flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
+#else
+ flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
+#endif
+ for (i = 0; i < conf.lock_retries; ++i) {
+ hr = glue (IFACE, _Lock) (
+ buf,
+ pos,
+ len,
+ &p1,
+ &blen1,
+ &p2,
+ &blen2,
+ flag
+ );
+
+ if (FAILED (hr)) {
+#ifndef DSBTYPE_IN
+ if (hr == DSERR_BUFFERLOST) {
+ if (glue (dsound_restore_, TYPE) (buf)) {
+ dsound_logerr (hr, "Could not lock " NAME "\n");
+ goto fail;
+ }
+ continue;
+ }
+#endif
+ dsound_logerr (hr, "Could not lock " NAME "\n");
+ goto fail;
+ }
+
+ break;
+ }
+
+ if (i == conf.lock_retries) {
+ dolog ("%d attempts to lock " NAME " failed\n", i);
+ goto fail;
+ }
+
+ if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
+ dolog ("DirectSound returned misaligned buffer %ld %ld\n",
+ blen1, blen2);
+ glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
+ goto fail;
+ }
+
+ if (!p1 && blen1) {
+ dolog ("warning: !p1 && blen1=%ld\n", blen1);
+ blen1 = 0;
+ }
+
+ if (!p2 && blen2) {
+ dolog ("warning: !p2 && blen2=%ld\n", blen2);
+ blen2 = 0;
+ }
+
+ *p1p = p1;
+ *p2p = p2;
+ *blen1p = blen1;
+ *blen2p = blen2;
+ return 0;
+
+ fail:
+ *p1p = NULL - 1;
+ *p2p = NULL - 1;
+ *blen1p = -1;
+ *blen2p = -1;
+ return -1;
+}
+
+#ifdef DSBTYPE_IN
+static void dsound_fini_in (HWVoiceIn *hw)
+#else
+static void dsound_fini_out (HWVoiceOut *hw)
+#endif
+{
+ HRESULT hr;
+#ifdef DSBTYPE_IN
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+#else
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+#endif
+
+ if (ds->FIELD) {
+ hr = glue (IFACE, _Stop) (ds->FIELD);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not stop " NAME "\n");
+ }
+
+ hr = glue (IFACE, _Release) (ds->FIELD);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release " NAME "\n");
+ }
+ ds->FIELD = NULL;
+ }
+}
+
+#ifdef DSBTYPE_IN
+static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
+#else
+static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
+#endif
+{
+ int err;
+ HRESULT hr;
+ dsound *s = &glob_dsound;
+ WAVEFORMATEX wfx;
+ struct audsettings obt_as;
+#ifdef DSBTYPE_IN
+ const char *typ = "ADC";
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+ DSCBUFFERDESC bd;
+ DSCBCAPS bc;
+#else
+ const char *typ = "DAC";
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+ DSBUFFERDESC bd;
+ DSBCAPS bc;
+#endif
+
+ if (!s->FIELD2) {
+ dolog ("Attempt to initialize voice without " NAME2 " object\n");
+ return -1;
+ }
+
+ err = waveformat_from_audio_settings (&wfx, as);
+ if (err) {
+ return -1;
+ }
+
+ memset (&bd, 0, sizeof (bd));
+ bd.dwSize = sizeof (bd);
+ bd.lpwfxFormat = &wfx;
+#ifdef DSBTYPE_IN
+ bd.dwBufferBytes = conf.bufsize_in;
+ hr = IDirectSoundCapture_CreateCaptureBuffer (
+ s->dsound_capture,
+ &bd,
+ &ds->dsound_capture_buffer,
+ NULL
+ );
+#else
+ bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+ bd.dwBufferBytes = conf.bufsize_out;
+ hr = IDirectSound_CreateSoundBuffer (
+ s->dsound,
+ &bd,
+ &ds->dsound_buffer,
+ NULL
+ );
+#endif
+
+ if (FAILED (hr)) {
+ dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
+ return -1;
+ }
+
+ hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
+ if (FAILED (hr)) {
+ dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
+ goto fail0;
+ }
+
+#ifdef DEBUG_DSOUND
+ dolog (NAME "\n");
+ print_wave_format (&wfx);
+#endif
+
+ memset (&bc, 0, sizeof (bc));
+ bc.dwSize = sizeof (bc);
+
+ hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
+ if (FAILED (hr)) {
+ dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
+ goto fail0;
+ }
+
+ err = waveformat_to_audio_settings (&wfx, &obt_as);
+ if (err) {
+ goto fail0;
+ }
+
+ ds->first_time = 1;
+ obt_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ if (bc.dwBufferBytes & hw->info.align) {
+ dolog (
+ "GetCaps returned misaligned buffer size %ld, alignment %d\n",
+ bc.dwBufferBytes, hw->info.align + 1
+ );
+ }
+ hw->samples = bc.dwBufferBytes >> hw->info.shift;
+
+#ifdef DEBUG_DSOUND
+ dolog ("caps %ld, desc %ld\n",
+ bc.dwBufferBytes, bd.dwBufferBytes);
+
+ dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
+ hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
+#endif
+ return 0;
+
+ fail0:
+ glue (dsound_fini_, TYPE) (hw);
+ return -1;
+}
+
+#undef NAME
+#undef NAME2
+#undef TYPE
+#undef IFACE
+#undef BUFPTR
+#undef FIELD
+#undef FIELD2
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
new file mode 100644
index 0000000..e2d89fd
--- /dev/null
+++ b/audio/dsoundaudio.c
@@ -0,0 +1,1030 @@
+/*
+ * QEMU DirectSound audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
+ */
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "dsound"
+#include "audio_int.h"
+
+#include <windows.h>
+#include <mmsystem.h>
+#include <objbase.h>
+#include <dsound.h>
+
+#include "audio_win_int.h"
+
+/* #define DEBUG_DSOUND */
+
+static struct {
+ int lock_retries;
+ int restore_retries;
+ int getstatus_retries;
+ int set_primary;
+ int bufsize_in;
+ int bufsize_out;
+ struct audsettings settings;
+ int latency_millis;
+} conf = {
+ .lock_retries = 1,
+ .restore_retries = 1,
+ .getstatus_retries = 1,
+ .set_primary = 0,
+ .bufsize_in = 16384,
+ .bufsize_out = 16384,
+ .settings.freq = 44100,
+ .settings.nchannels = 2,
+ .settings.fmt = AUD_FMT_S16,
+ .latency_millis = 10
+};
+
+typedef struct {
+ LPDIRECTSOUND dsound;
+ LPDIRECTSOUNDCAPTURE dsound_capture;
+ LPDIRECTSOUNDBUFFER dsound_primary_buffer;
+ struct audsettings settings;
+} dsound;
+
+static dsound glob_dsound;
+
+typedef struct {
+ HWVoiceOut hw;
+ LPDIRECTSOUNDBUFFER dsound_buffer;
+ DWORD old_pos;
+ int first_time;
+#ifdef DEBUG_DSOUND
+ DWORD old_ppos;
+ DWORD played;
+ DWORD mixed;
+#endif
+} DSoundVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ int first_time;
+ LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
+} DSoundVoiceIn;
+
+static void dsound_log_hresult (HRESULT hr)
+{
+ const char *str = "BUG";
+
+ switch (hr) {
+ case DS_OK:
+ str = "The method succeeded";
+ break;
+#ifdef DS_NO_VIRTUALIZATION
+ case DS_NO_VIRTUALIZATION:
+ str = "The buffer was created, but another 3D algorithm was substituted";
+ break;
+#endif
+#ifdef DS_INCOMPLETE
+ case DS_INCOMPLETE:
+ str = "The method succeeded, but not all the optional effects were obtained";
+ break;
+#endif
+#ifdef DSERR_ACCESSDENIED
+ case DSERR_ACCESSDENIED:
+ str = "The request failed because access was denied";
+ break;
+#endif
+#ifdef DSERR_ALLOCATED
+ case DSERR_ALLOCATED:
+ str = "The request failed because resources, such as a priority level, were already in use by another caller";
+ break;
+#endif
+#ifdef DSERR_ALREADYINITIALIZED
+ case DSERR_ALREADYINITIALIZED:
+ str = "The object is already initialized";
+ break;
+#endif
+#ifdef DSERR_BADFORMAT
+ case DSERR_BADFORMAT:
+ str = "The specified wave format is not supported";
+ break;
+#endif
+#ifdef DSERR_BADSENDBUFFERGUID
+ case DSERR_BADSENDBUFFERGUID:
+ str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
+ break;
+#endif
+#ifdef DSERR_BUFFERLOST
+ case DSERR_BUFFERLOST:
+ str = "The buffer memory has been lost and must be restored";
+ break;
+#endif
+#ifdef DSERR_BUFFERTOOSMALL
+ case DSERR_BUFFERTOOSMALL:
+ str = "The buffer size is not great enough to enable effects processing";
+ break;
+#endif
+#ifdef DSERR_CONTROLUNAVAIL
+ case DSERR_CONTROLUNAVAIL:
+ str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC";
+ break;
+#endif
+#ifdef DSERR_DS8_REQUIRED
+ case DSERR_DS8_REQUIRED:
+ str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
+ break;
+#endif
+#ifdef DSERR_FXUNAVAILABLE
+ case DSERR_FXUNAVAILABLE:
+ str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software";
+ break;
+#endif
+#ifdef DSERR_GENERIC
+ case DSERR_GENERIC :
+ str = "An undetermined error occurred inside the DirectSound subsystem";
+ break;
+#endif
+#ifdef DSERR_INVALIDCALL
+ case DSERR_INVALIDCALL:
+ str = "This function is not valid for the current state of this object";
+ break;
+#endif
+#ifdef DSERR_INVALIDPARAM
+ case DSERR_INVALIDPARAM:
+ str = "An invalid parameter was passed to the returning function";
+ break;
+#endif
+#ifdef DSERR_NOAGGREGATION
+ case DSERR_NOAGGREGATION:
+ str = "The object does not support aggregation";
+ break;
+#endif
+#ifdef DSERR_NODRIVER
+ case DSERR_NODRIVER:
+ str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
+ break;
+#endif
+#ifdef DSERR_NOINTERFACE
+ case DSERR_NOINTERFACE:
+ str = "The requested COM interface is not available";
+ break;
+#endif
+#ifdef DSERR_OBJECTNOTFOUND
+ case DSERR_OBJECTNOTFOUND:
+ str = "The requested object was not found";
+ break;
+#endif
+#ifdef DSERR_OTHERAPPHASPRIO
+ case DSERR_OTHERAPPHASPRIO:
+ str = "Another application has a higher priority level, preventing this call from succeeding";
+ break;
+#endif
+#ifdef DSERR_OUTOFMEMORY
+ case DSERR_OUTOFMEMORY:
+ str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
+ break;
+#endif
+#ifdef DSERR_PRIOLEVELNEEDED
+ case DSERR_PRIOLEVELNEEDED:
+ str = "A cooperative level of DSSCL_PRIORITY or higher is required";
+ break;
+#endif
+#ifdef DSERR_SENDLOOP
+ case DSERR_SENDLOOP:
+ str = "A circular loop of send effects was detected";
+ break;
+#endif
+#ifdef DSERR_UNINITIALIZED
+ case DSERR_UNINITIALIZED:
+ str = "The Initialize method has not been called or has not been called successfully before other methods were called";
+ break;
+#endif
+#ifdef DSERR_UNSUPPORTED
+ case DSERR_UNSUPPORTED:
+ str = "The function called is not supported at this time";
+ break;
+#endif
+ default:
+ AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
+ return;
+ }
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) dsound_logerr (
+ HRESULT hr,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ dsound_log_hresult (hr);
+}
+
+static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
+ HRESULT hr,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ dsound_log_hresult (hr);
+}
+
+static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
+{
+ return (millis * info->bytes_per_second) / 1000;
+}
+
+#ifdef DEBUG_DSOUND
+static void print_wave_format (WAVEFORMATEX *wfx)
+{
+ dolog ("tag = %d\n", wfx->wFormatTag);
+ dolog ("nChannels = %d\n", wfx->nChannels);
+ dolog ("nSamplesPerSec = %ld\n", wfx->nSamplesPerSec);
+ dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
+ dolog ("nBlockAlign = %d\n", wfx->nBlockAlign);
+ dolog ("wBitsPerSample = %d\n", wfx->wBitsPerSample);
+ dolog ("cbSize = %d\n", wfx->cbSize);
+}
+#endif
+
+static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
+{
+ HRESULT hr;
+ int i;
+
+ for (i = 0; i < conf.restore_retries; ++i) {
+ hr = IDirectSoundBuffer_Restore (dsb);
+
+ switch (hr) {
+ case DS_OK:
+ return 0;
+
+ case DSERR_BUFFERLOST:
+ continue;
+
+ default:
+ dsound_logerr (hr, "Could not restore playback buffer\n");
+ return -1;
+ }
+ }
+
+ dolog ("%d attempts to restore playback buffer failed\n", i);
+ return -1;
+}
+
+#include "dsound_template.h"
+#define DSBTYPE_IN
+#include "dsound_template.h"
+#undef DSBTYPE_IN
+
+static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
+{
+ HRESULT hr;
+ int i;
+
+ for (i = 0; i < conf.getstatus_retries; ++i) {
+ hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get playback buffer status\n");
+ return -1;
+ }
+
+ if (*statusp & DSERR_BUFFERLOST) {
+ if (dsound_restore_out (dsb)) {
+ return -1;
+ }
+ continue;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
+ DWORD *statusp)
+{
+ HRESULT hr;
+
+ hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get capture buffer status\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
+{
+ int src_len1 = dst_len;
+ int src_len2 = 0;
+ int pos = hw->rpos + dst_len;
+ struct st_sample *src1 = hw->mix_buf + hw->rpos;
+ struct st_sample *src2 = NULL;
+
+ if (pos > hw->samples) {
+ src_len1 = hw->samples - hw->rpos;
+ src2 = hw->mix_buf;
+ src_len2 = dst_len - src_len1;
+ pos = src_len2;
+ }
+
+ if (src_len1) {
+ hw->clip (dst, src1, src_len1);
+ }
+
+ if (src_len2) {
+ dst = advance (dst, src_len1 << hw->info.shift);
+ hw->clip (dst, src2, src_len2);
+ }
+
+ hw->rpos = pos % hw->samples;
+}
+
+static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
+{
+ int err;
+ LPVOID p1, p2;
+ DWORD blen1, blen2, len1, len2;
+
+ err = dsound_lock_out (
+ dsb,
+ &hw->info,
+ 0,
+ hw->samples << hw->info.shift,
+ &p1, &p2,
+ &blen1, &blen2,
+ 1
+ );
+ if (err) {
+ return;
+ }
+
+ len1 = blen1 >> hw->info.shift;
+ len2 = blen2 >> hw->info.shift;
+
+#ifdef DEBUG_DSOUND
+ dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
+ p1, blen1, len1,
+ p2, blen2, len2);
+#endif
+
+ if (p1 && len1) {
+ audio_pcm_info_clear_buf (&hw->info, p1, len1);
+ }
+
+ if (p2 && len2) {
+ audio_pcm_info_clear_buf (&hw->info, p2, len2);
+ }
+
+ dsound_unlock_out (dsb, p1, p2, blen1, blen2);
+}
+
+static void dsound_close (dsound *s)
+{
+ HRESULT hr;
+
+ if (s->dsound_primary_buffer) {
+ hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release primary buffer\n");
+ }
+ s->dsound_primary_buffer = NULL;
+ }
+}
+
+static int dsound_open (dsound *s)
+{
+ int err;
+ HRESULT hr;
+ WAVEFORMATEX wfx;
+ DSBUFFERDESC dsbd;
+ HWND hwnd;
+
+ hwnd = GetForegroundWindow ();
+ hr = IDirectSound_SetCooperativeLevel (
+ s->dsound,
+ hwnd,
+ DSSCL_PRIORITY
+ );
+
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not set cooperative level for window %p\n",
+ hwnd);
+ return -1;
+ }
+
+ if (!conf.set_primary) {
+ return 0;
+ }
+
+ err = waveformat_from_audio_settings (&wfx, &conf.settings);
+ if (err) {
+ return -1;
+ }
+
+ memset (&dsbd, 0, sizeof (dsbd));
+ dsbd.dwSize = sizeof (dsbd);
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+ dsbd.dwBufferBytes = 0;
+ dsbd.lpwfxFormat = NULL;
+
+ hr = IDirectSound_CreateSoundBuffer (
+ s->dsound,
+ &dsbd,
+ &s->dsound_primary_buffer,
+ NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not create primary playback buffer\n");
+ return -1;
+ }
+
+ hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not set primary playback buffer format\n");
+ }
+
+ hr = IDirectSoundBuffer_GetFormat (
+ s->dsound_primary_buffer,
+ &wfx,
+ sizeof (wfx),
+ NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get primary playback buffer format\n");
+ goto fail0;
+ }
+
+#ifdef DEBUG_DSOUND
+ dolog ("Primary\n");
+ print_wave_format (&wfx);
+#endif
+
+ err = waveformat_to_audio_settings (&wfx, &s->settings);
+ if (err) {
+ goto fail0;
+ }
+
+ return 0;
+
+ fail0:
+ dsound_close (s);
+ return -1;
+}
+
+static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ HRESULT hr;
+ DWORD status;
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+ LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+
+ if (!dsb) {
+ dolog ("Attempt to control voice without a buffer\n");
+ return 0;
+ }
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ if (dsound_get_status_out (dsb, &status)) {
+ return -1;
+ }
+
+ if (status & DSBSTATUS_PLAYING) {
+ dolog ("warning: Voice is already playing\n");
+ return 0;
+ }
+
+ dsound_clear_sample (hw, dsb);
+
+ hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not start playing buffer\n");
+ return -1;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (dsound_get_status_out (dsb, &status)) {
+ return -1;
+ }
+
+ if (status & DSBSTATUS_PLAYING) {
+ hr = IDirectSoundBuffer_Stop (dsb);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not stop playing buffer\n");
+ return -1;
+ }
+ }
+ else {
+ dolog ("warning: Voice is not playing\n");
+ }
+ break;
+ }
+ return 0;
+}
+
+static int dsound_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int dsound_run_out (HWVoiceOut *hw, int live)
+{
+ int err;
+ HRESULT hr;
+ DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+ LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+ int len, hwshift;
+ DWORD blen1, blen2;
+ DWORD len1, len2;
+ DWORD decr;
+ DWORD wpos, ppos, old_pos;
+ LPVOID p1, p2;
+ int bufsize;
+
+ if (!dsb) {
+ dolog ("Attempt to run empty with playback buffer\n");
+ return 0;
+ }
+
+ hwshift = hw->info.shift;
+ bufsize = hw->samples << hwshift;
+
+ hr = IDirectSoundBuffer_GetCurrentPosition (
+ dsb,
+ &ppos,
+ ds->first_time ? &wpos : NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get playback buffer position\n");
+ return 0;
+ }
+
+ len = live << hwshift;
+
+ if (ds->first_time) {
+ if (conf.latency_millis) {
+ DWORD cur_blat;
+
+ cur_blat = audio_ring_dist (wpos, ppos, bufsize);
+ ds->first_time = 0;
+ old_pos = wpos;
+ old_pos +=
+ millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
+ old_pos %= bufsize;
+ old_pos &= ~hw->info.align;
+ }
+ else {
+ old_pos = wpos;
+ }
+#ifdef DEBUG_DSOUND
+ ds->played = 0;
+ ds->mixed = 0;
+#endif
+ }
+ else {
+ if (ds->old_pos == ppos) {
+#ifdef DEBUG_DSOUND
+ dolog ("old_pos == ppos\n");
+#endif
+ return 0;
+ }
+
+#ifdef DEBUG_DSOUND
+ ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
+#endif
+ old_pos = ds->old_pos;
+ }
+
+ if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
+ len = ppos - old_pos;
+ }
+ else {
+ if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
+ len = bufsize - old_pos + ppos;
+ }
+ }
+
+ if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
+ dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
+ len, bufsize, old_pos, ppos);
+ return 0;
+ }
+
+ len &= ~hw->info.align;
+ if (!len) {
+ return 0;
+ }
+
+#ifdef DEBUG_DSOUND
+ ds->old_ppos = ppos;
+#endif
+ err = dsound_lock_out (
+ dsb,
+ &hw->info,
+ old_pos,
+ len,
+ &p1, &p2,
+ &blen1, &blen2,
+ 0
+ );
+ if (err) {
+ return 0;
+ }
+
+ len1 = blen1 >> hwshift;
+ len2 = blen2 >> hwshift;
+ decr = len1 + len2;
+
+ if (p1 && len1) {
+ dsound_write_sample (hw, p1, len1);
+ }
+
+ if (p2 && len2) {
+ dsound_write_sample (hw, p2, len2);
+ }
+
+ dsound_unlock_out (dsb, p1, p2, blen1, blen2);
+ ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
+
+#ifdef DEBUG_DSOUND
+ ds->mixed += decr << hwshift;
+
+ dolog ("played %lu mixed %lu diff %ld sec %f\n",
+ ds->played,
+ ds->mixed,
+ ds->mixed - ds->played,
+ abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
+#endif
+ return decr;
+}
+
+static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ HRESULT hr;
+ DWORD status;
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+ LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
+
+ if (!dscb) {
+ dolog ("Attempt to control capture voice without a buffer\n");
+ return -1;
+ }
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ if (dsound_get_status_in (dscb, &status)) {
+ return -1;
+ }
+
+ if (status & DSCBSTATUS_CAPTURING) {
+ dolog ("warning: Voice is already capturing\n");
+ return 0;
+ }
+
+ /* clear ?? */
+
+ hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not start capturing\n");
+ return -1;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (dsound_get_status_in (dscb, &status)) {
+ return -1;
+ }
+
+ if (status & DSCBSTATUS_CAPTURING) {
+ hr = IDirectSoundCaptureBuffer_Stop (dscb);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not stop capturing\n");
+ return -1;
+ }
+ }
+ else {
+ dolog ("warning: Voice is not capturing\n");
+ }
+ break;
+ }
+ return 0;
+}
+
+static int dsound_read (SWVoiceIn *sw, void *buf, int len)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int dsound_run_in (HWVoiceIn *hw)
+{
+ int err;
+ HRESULT hr;
+ DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+ LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
+ int live, len, dead;
+ DWORD blen1, blen2;
+ DWORD len1, len2;
+ DWORD decr;
+ DWORD cpos, rpos;
+ LPVOID p1, p2;
+ int hwshift;
+
+ if (!dscb) {
+ dolog ("Attempt to run without capture buffer\n");
+ return 0;
+ }
+
+ hwshift = hw->info.shift;
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ if (!dead) {
+ return 0;
+ }
+
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
+ dscb,
+ &cpos,
+ ds->first_time ? &rpos : NULL
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not get capture buffer position\n");
+ return 0;
+ }
+
+ if (ds->first_time) {
+ ds->first_time = 0;
+ if (rpos & hw->info.align) {
+ ldebug ("warning: Misaligned capture read position %ld(%d)\n",
+ rpos, hw->info.align);
+ }
+ hw->wpos = rpos >> hwshift;
+ }
+
+ if (cpos & hw->info.align) {
+ ldebug ("warning: Misaligned capture position %ld(%d)\n",
+ cpos, hw->info.align);
+ }
+ cpos >>= hwshift;
+
+ len = audio_ring_dist (cpos, hw->wpos, hw->samples);
+ if (!len) {
+ return 0;
+ }
+ len = audio_MIN (len, dead);
+
+ err = dsound_lock_in (
+ dscb,
+ &hw->info,
+ hw->wpos << hwshift,
+ len << hwshift,
+ &p1,
+ &p2,
+ &blen1,
+ &blen2,
+ 0
+ );
+ if (err) {
+ return 0;
+ }
+
+ len1 = blen1 >> hwshift;
+ len2 = blen2 >> hwshift;
+ decr = len1 + len2;
+
+ if (p1 && len1) {
+ hw->conv (hw->conv_buf + hw->wpos, p1, len1);
+ }
+
+ if (p2 && len2) {
+ hw->conv (hw->conv_buf, p2, len2);
+ }
+
+ dsound_unlock_in (dscb, p1, p2, blen1, blen2);
+ hw->wpos = (hw->wpos + decr) % hw->samples;
+ return decr;
+}
+
+static void dsound_audio_fini (void *opaque)
+{
+ HRESULT hr;
+ dsound *s = opaque;
+
+ if (!s->dsound) {
+ return;
+ }
+
+ hr = IDirectSound_Release (s->dsound);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSound\n");
+ }
+ s->dsound = NULL;
+
+ if (!s->dsound_capture) {
+ return;
+ }
+
+ hr = IDirectSoundCapture_Release (s->dsound_capture);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+ }
+ s->dsound_capture = NULL;
+}
+
+static void *dsound_audio_init (void)
+{
+ int err;
+ HRESULT hr;
+ dsound *s = &glob_dsound;
+
+ hr = CoInitialize (NULL);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not initialize COM\n");
+ return NULL;
+ }
+
+ hr = CoCreateInstance (
+ &CLSID_DirectSound,
+ NULL,
+ CLSCTX_ALL,
+ &IID_IDirectSound,
+ (void **) &s->dsound
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not create DirectSound instance\n");
+ return NULL;
+ }
+
+ hr = IDirectSound_Initialize (s->dsound, NULL);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not initialize DirectSound\n");
+
+ hr = IDirectSound_Release (s->dsound);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSound\n");
+ }
+ s->dsound = NULL;
+ return NULL;
+ }
+
+ hr = CoCreateInstance (
+ &CLSID_DirectSoundCapture,
+ NULL,
+ CLSCTX_ALL,
+ &IID_IDirectSoundCapture,
+ (void **) &s->dsound_capture
+ );
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
+ }
+ else {
+ hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
+
+ hr = IDirectSoundCapture_Release (s->dsound_capture);
+ if (FAILED (hr)) {
+ dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+ }
+ s->dsound_capture = NULL;
+ }
+ }
+
+ err = dsound_open (s);
+ if (err) {
+ dsound_audio_fini (s);
+ return NULL;
+ }
+
+ return s;
+}
+
+static struct audio_option dsound_options[] = {
+ {
+ .name = "LOCK_RETRIES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.lock_retries,
+ .descr = "Number of times to attempt locking the buffer"
+ },
+ {
+ .name = "RESTOURE_RETRIES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.restore_retries,
+ .descr = "Number of times to attempt restoring the buffer"
+ },
+ {
+ .name = "GETSTATUS_RETRIES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.getstatus_retries,
+ .descr = "Number of times to attempt getting status of the buffer"
+ },
+ {
+ .name = "SET_PRIMARY",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.set_primary,
+ .descr = "Set the parameters of primary buffer"
+ },
+ {
+ .name = "LATENCY_MILLIS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.latency_millis,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "PRIMARY_FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.settings.freq,
+ .descr = "Primary buffer frequency"
+ },
+ {
+ .name = "PRIMARY_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.settings.nchannels,
+ .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "PRIMARY_FMT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.settings.fmt,
+ .descr = "Primary buffer format"
+ },
+ {
+ .name = "BUFSIZE_OUT",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.bufsize_out,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "BUFSIZE_IN",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.bufsize_in,
+ .descr = "(undocumented)"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops dsound_pcm_ops = {
+ .init_out = dsound_init_out,
+ .fini_out = dsound_fini_out,
+ .run_out = dsound_run_out,
+ .write = dsound_write,
+ .ctl_out = dsound_ctl_out,
+
+ .init_in = dsound_init_in,
+ .fini_in = dsound_fini_in,
+ .run_in = dsound_run_in,
+ .read = dsound_read,
+ .ctl_in = dsound_ctl_in
+};
+
+struct audio_driver dsound_audio_driver = {
+ .name = "dsound",
+ .descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
+ .options = dsound_options,
+ .init = dsound_audio_init,
+ .fini = dsound_audio_fini,
+ .pcm_ops = &dsound_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = 1,
+ .voice_size_out = sizeof (DSoundVoiceOut),
+ .voice_size_in = sizeof (DSoundVoiceIn)
+};
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
new file mode 100644
index 0000000..ff97b39
--- /dev/null
+++ b/audio/esdaudio.c
@@ -0,0 +1,557 @@
+/*
+ * QEMU ESD audio driver
+ *
+ * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <esd.h>
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "esd"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+typedef struct {
+ HWVoiceOut hw;
+ int done;
+ int live;
+ int decr;
+ int rpos;
+ void *pcm_buf;
+ int fd;
+ struct audio_pt pt;
+} ESDVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ int done;
+ int dead;
+ int incr;
+ int wpos;
+ void *pcm_buf;
+ int fd;
+ struct audio_pt pt;
+} ESDVoiceIn;
+
+static struct {
+ int samples;
+ int divisor;
+ char *dac_host;
+ char *adc_host;
+} conf = {
+ .samples = 1024,
+ .divisor = 2,
+};
+
+static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+/* playback */
+static void *qesd_thread_out (void *arg)
+{
+ ESDVoiceOut *esd = arg;
+ HWVoiceOut *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ for (;;) {
+ int decr, to_mix, rpos;
+
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->live > threshold) {
+ break;
+ }
+
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ decr = to_mix = esd->live;
+ rpos = hw->rpos;
+
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ while (to_mix) {
+ ssize_t written;
+ int chunk = audio_MIN (to_mix, hw->samples - rpos);
+ struct st_sample *src = hw->mix_buf + rpos;
+
+ hw->clip (esd->pcm_buf, src, chunk);
+
+ again:
+ written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
+ if (written == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ qesd_logerr (errno, "write failed\n");
+ return NULL;
+ }
+
+ if (written != chunk << hw->info.shift) {
+ int wsamples = written >> hw->info.shift;
+ int wbytes = wsamples << hw->info.shift;
+ if (wbytes != written) {
+ dolog ("warning: Misaligned write %d (requested %zd), "
+ "alignment %d\n",
+ wbytes, written, hw->info.align + 1);
+ }
+ to_mix -= wsamples;
+ rpos = (rpos + wsamples) % hw->samples;
+ break;
+ }
+
+ rpos = (rpos + chunk) % hw->samples;
+ to_mix -= chunk;
+ }
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ esd->rpos = rpos;
+ esd->live -= decr;
+ esd->decr += decr;
+ }
+
+ exit:
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qesd_run_out (HWVoiceOut *hw, int live)
+{
+ int decr;
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ decr = audio_MIN (live, esd->decr);
+ esd->decr -= decr;
+ esd->live = live - decr;
+ hw->rpos = esd->rpos;
+ if (esd->live > 0) {
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ }
+ return decr;
+}
+
+static int qesd_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+ struct audsettings obt_as = *as;
+ int esdfmt = ESD_STREAM | ESD_PLAY;
+
+ esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ esdfmt |= ESD_BITS8;
+ obt_as.fmt = AUD_FMT_U8;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("Will use 16 instead of 32 bit samples\n");
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ deffmt:
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
+ goto deffmt;
+
+ }
+ obt_as.endianness = AUDIO_HOST_ENDIANNESS;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ hw->samples = conf.samples;
+ esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!esd->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ return -1;
+ }
+
+ esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
+ if (esd->fd < 0) {
+ qesd_logerr (errno, "esd_play_stream failed\n");
+ goto fail1;
+ }
+
+ if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail2;
+ }
+
+ return 0;
+
+ fail2:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail1:
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+ return -1;
+}
+
+static void qesd_fini_out (HWVoiceOut *hw)
+{
+ void *ret;
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+ audio_pt_lock (&esd->pt, AUDIO_FUNC);
+ esd->done = 1;
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+ if (esd->fd >= 0) {
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "failed to close esd socket\n");
+ }
+ esd->fd = -1;
+ }
+
+ audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* capture */
+static void *qesd_thread_in (void *arg)
+{
+ ESDVoiceIn *esd = arg;
+ HWVoiceIn *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ for (;;) {
+ int incr, to_grab, wpos;
+
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->dead > threshold) {
+ break;
+ }
+
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ incr = to_grab = esd->dead;
+ wpos = hw->wpos;
+
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ while (to_grab) {
+ ssize_t nread;
+ int chunk = audio_MIN (to_grab, hw->samples - wpos);
+ void *buf = advance (esd->pcm_buf, wpos);
+
+ again:
+ nread = read (esd->fd, buf, chunk << hw->info.shift);
+ if (nread == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ qesd_logerr (errno, "read failed\n");
+ return NULL;
+ }
+
+ if (nread != chunk << hw->info.shift) {
+ int rsamples = nread >> hw->info.shift;
+ int rbytes = rsamples << hw->info.shift;
+ if (rbytes != nread) {
+ dolog ("warning: Misaligned write %d (requested %zd), "
+ "alignment %d\n",
+ rbytes, nread, hw->info.align + 1);
+ }
+ to_grab -= rsamples;
+ wpos = (wpos + rsamples) % hw->samples;
+ break;
+ }
+
+ hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift);
+ wpos = (wpos + chunk) % hw->samples;
+ to_grab -= chunk;
+ }
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ esd->wpos = wpos;
+ esd->dead -= incr;
+ esd->incr += incr;
+ }
+
+ exit:
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qesd_run_in (HWVoiceIn *hw)
+{
+ int live, incr, dead;
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ incr = audio_MIN (dead, esd->incr);
+ esd->incr -= incr;
+ esd->dead = dead - incr;
+ hw->wpos = esd->wpos;
+ if (esd->dead > 0) {
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ }
+ return incr;
+}
+
+static int qesd_read (SWVoiceIn *sw, void *buf, int len)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+ struct audsettings obt_as = *as;
+ int esdfmt = ESD_STREAM | ESD_RECORD;
+
+ esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ esdfmt |= ESD_BITS8;
+ obt_as.fmt = AUD_FMT_U8;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("Will use 16 instead of 32 bit samples\n");
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+ }
+ obt_as.endianness = AUDIO_HOST_ENDIANNESS;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ hw->samples = conf.samples;
+ esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!esd->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ return -1;
+ }
+
+ esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
+ if (esd->fd < 0) {
+ qesd_logerr (errno, "esd_record_stream failed\n");
+ goto fail1;
+ }
+
+ if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail2;
+ }
+
+ return 0;
+
+ fail2:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail1:
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+ return -1;
+}
+
+static void qesd_fini_in (HWVoiceIn *hw)
+{
+ void *ret;
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+ audio_pt_lock (&esd->pt, AUDIO_FUNC);
+ esd->done = 1;
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+ if (esd->fd >= 0) {
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "failed to close esd socket\n");
+ }
+ esd->fd = -1;
+ }
+
+ audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* common */
+static void *qesd_audio_init (void)
+{
+ return &conf;
+}
+
+static void qesd_audio_fini (void *opaque)
+{
+ (void) opaque;
+ ldebug ("esd_fini");
+}
+
+struct audio_option qesd_options[] = {
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.samples,
+ .descr = "buffer size in samples"
+ },
+ {
+ .name = "DIVISOR",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.divisor,
+ .descr = "threshold divisor"
+ },
+ {
+ .name = "DAC_HOST",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.dac_host,
+ .descr = "playback host"
+ },
+ {
+ .name = "ADC_HOST",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.adc_host,
+ .descr = "capture host"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops qesd_pcm_ops = {
+ .init_out = qesd_init_out,
+ .fini_out = qesd_fini_out,
+ .run_out = qesd_run_out,
+ .write = qesd_write,
+ .ctl_out = qesd_ctl_out,
+
+ .init_in = qesd_init_in,
+ .fini_in = qesd_fini_in,
+ .run_in = qesd_run_in,
+ .read = qesd_read,
+ .ctl_in = qesd_ctl_in,
+};
+
+struct audio_driver esd_audio_driver = {
+ .name = "esd",
+ .descr = "http://en.wikipedia.org/wiki/Esound",
+ .options = qesd_options,
+ .init = qesd_audio_init,
+ .fini = qesd_audio_fini,
+ .pcm_ops = &qesd_pcm_ops,
+ .can_be_default = 0,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (ESDVoiceOut),
+ .voice_size_in = sizeof (ESDVoiceIn)
+};
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
new file mode 100644
index 0000000..c34cf53
--- /dev/null
+++ b/audio/fmodaudio.c
@@ -0,0 +1,687 @@
+/*
+ * QEMU FMOD audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <fmod.h>
+#include <fmod_errors.h>
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "fmod"
+#include "audio_int.h"
+
+typedef struct FMODVoiceOut {
+ HWVoiceOut hw;
+ unsigned int old_pos;
+ FSOUND_SAMPLE *fmod_sample;
+ int channel;
+} FMODVoiceOut;
+
+typedef struct FMODVoiceIn {
+ HWVoiceIn hw;
+ FSOUND_SAMPLE *fmod_sample;
+} FMODVoiceIn;
+
+static struct {
+ const char *drvname;
+ int nb_samples;
+ int freq;
+ int nb_channels;
+ int bufsize;
+ int broken_adc;
+} conf = {
+ .nb_samples = 2048 * 2,
+ .freq = 44100,
+ .nb_channels = 2,
+};
+
+static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n",
+ FMOD_ErrorString (FSOUND_GetError ()));
+}
+
+static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n",
+ FMOD_ErrorString (FSOUND_GetError ()));
+}
+
+static int fmod_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static void fmod_clear_sample (FMODVoiceOut *fmd)
+{
+ HWVoiceOut *hw = &fmd->hw;
+ int status;
+ void *p1 = 0, *p2 = 0;
+ unsigned int len1 = 0, len2 = 0;
+
+ status = FSOUND_Sample_Lock (
+ fmd->fmod_sample,
+ 0,
+ hw->samples << hw->info.shift,
+ &p1,
+ &p2,
+ &len1,
+ &len2
+ );
+
+ if (!status) {
+ fmod_logerr ("Failed to lock sample\n");
+ return;
+ }
+
+ if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
+ dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
+ len1, len2, hw->info.align + 1);
+ goto fail;
+ }
+
+ if ((len1 + len2) - (hw->samples << hw->info.shift)) {
+ dolog ("Lock returned incomplete length %d, %d\n",
+ len1 + len2, hw->samples << hw->info.shift);
+ goto fail;
+ }
+
+ audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
+
+ fail:
+ status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
+ if (!status) {
+ fmod_logerr ("Failed to unlock sample\n");
+ }
+}
+
+static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
+{
+ int src_len1 = dst_len;
+ int src_len2 = 0;
+ int pos = hw->rpos + dst_len;
+ struct st_sample *src1 = hw->mix_buf + hw->rpos;
+ struct st_sample *src2 = NULL;
+
+ if (pos > hw->samples) {
+ src_len1 = hw->samples - hw->rpos;
+ src2 = hw->mix_buf;
+ src_len2 = dst_len - src_len1;
+ pos = src_len2;
+ }
+
+ if (src_len1) {
+ hw->clip (dst, src1, src_len1);
+ }
+
+ if (src_len2) {
+ dst = advance (dst, src_len1 << hw->info.shift);
+ hw->clip (dst, src2, src_len2);
+ }
+
+ hw->rpos = pos % hw->samples;
+}
+
+static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
+ unsigned int blen1, unsigned int blen2)
+{
+ int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
+ if (!status) {
+ fmod_logerr ("Failed to unlock sample\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int fmod_lock_sample (
+ FSOUND_SAMPLE *sample,
+ struct audio_pcm_info *info,
+ int pos,
+ int len,
+ void **p1,
+ void **p2,
+ unsigned int *blen1,
+ unsigned int *blen2
+ )
+{
+ int status;
+
+ status = FSOUND_Sample_Lock (
+ sample,
+ pos << info->shift,
+ len << info->shift,
+ p1,
+ p2,
+ blen1,
+ blen2
+ );
+
+ if (!status) {
+ fmod_logerr ("Failed to lock sample\n");
+ return -1;
+ }
+
+ if ((*blen1 & info->align) || (*blen2 & info->align)) {
+ dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
+ *blen1, *blen2, info->align + 1);
+
+ fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
+
+ *p1 = NULL - 1;
+ *p2 = NULL - 1;
+ *blen1 = ~0U;
+ *blen2 = ~0U;
+ return -1;
+ }
+
+ if (!*p1 && *blen1) {
+ dolog ("warning: !p1 && blen1=%d\n", *blen1);
+ *blen1 = 0;
+ }
+
+ if (!p2 && *blen2) {
+ dolog ("warning: !p2 && blen2=%d\n", *blen2);
+ *blen2 = 0;
+ }
+
+ return 0;
+}
+
+static int fmod_run_out (HWVoiceOut *hw, int live)
+{
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+ int decr;
+ void *p1 = 0, *p2 = 0;
+ unsigned int blen1 = 0, blen2 = 0;
+ unsigned int len1 = 0, len2 = 0;
+
+ if (!hw->pending_disable) {
+ return 0;
+ }
+
+ decr = live;
+
+ if (fmd->channel >= 0) {
+ int len = decr;
+ int old_pos = fmd->old_pos;
+ int ppos = FSOUND_GetCurrentPosition (fmd->channel);
+
+ if (ppos == old_pos || !ppos) {
+ return 0;
+ }
+
+ if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
+ len = ppos - old_pos;
+ }
+ else {
+ if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
+ len = hw->samples - old_pos + ppos;
+ }
+ }
+ decr = len;
+
+ if (audio_bug (AUDIO_FUNC, decr < 0)) {
+ dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
+ decr, live, ppos, old_pos, len);
+ return 0;
+ }
+ }
+
+
+ if (!decr) {
+ return 0;
+ }
+
+ if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
+ fmd->old_pos, decr,
+ &p1, &p2,
+ &blen1, &blen2)) {
+ return 0;
+ }
+
+ len1 = blen1 >> hw->info.shift;
+ len2 = blen2 >> hw->info.shift;
+ ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
+ decr = len1 + len2;
+
+ if (p1 && len1) {
+ fmod_write_sample (hw, p1, len1);
+ }
+
+ if (p2 && len2) {
+ fmod_write_sample (hw, p2, len2);
+ }
+
+ fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
+
+ fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
+ return decr;
+}
+
+static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
+{
+ int mode = FSOUND_LOOP_NORMAL;
+
+ switch (fmt) {
+ case AUD_FMT_S8:
+ mode |= FSOUND_SIGNED | FSOUND_8BITS;
+ break;
+
+ case AUD_FMT_U8:
+ mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
+ break;
+
+ case AUD_FMT_S16:
+ mode |= FSOUND_SIGNED | FSOUND_16BITS;
+ break;
+
+ case AUD_FMT_U16:
+ mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_FMOD
+ abort ();
+#endif
+ mode |= FSOUND_8BITS;
+ }
+ mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
+ return mode;
+}
+
+static void fmod_fini_out (HWVoiceOut *hw)
+{
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+
+ if (fmd->fmod_sample) {
+ FSOUND_Sample_Free (fmd->fmod_sample);
+ fmd->fmod_sample = 0;
+
+ if (fmd->channel >= 0) {
+ FSOUND_StopSound (fmd->channel);
+ }
+ }
+}
+
+static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ int bits16, mode, channel;
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+ struct audsettings obt_as = *as;
+
+ mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
+ fmd->fmod_sample = FSOUND_Sample_Alloc (
+ FSOUND_FREE, /* index */
+ conf.nb_samples, /* length */
+ mode, /* mode */
+ as->freq, /* freq */
+ 255, /* volume */
+ 128, /* pan */
+ 255 /* priority */
+ );
+
+ if (!fmd->fmod_sample) {
+ fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
+ return -1;
+ }
+
+ channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
+ if (channel < 0) {
+ fmod_logerr2 ("DAC", "Failed to start playing sound\n");
+ FSOUND_Sample_Free (fmd->fmod_sample);
+ return -1;
+ }
+ fmd->channel = channel;
+
+ /* FMOD always operates on little endian frames? */
+ obt_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &obt_as);
+ bits16 = (mode & FSOUND_16BITS) != 0;
+ hw->samples = conf.nb_samples;
+ return 0;
+}
+
+static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ int status;
+ FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ fmod_clear_sample (fmd);
+ status = FSOUND_SetPaused (fmd->channel, 0);
+ if (!status) {
+ fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
+ }
+ break;
+
+ case VOICE_DISABLE:
+ status = FSOUND_SetPaused (fmd->channel, 1);
+ if (!status) {
+ fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ int bits16, mode;
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+ struct audsettings obt_as = *as;
+
+ if (conf.broken_adc) {
+ return -1;
+ }
+
+ mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
+ fmd->fmod_sample = FSOUND_Sample_Alloc (
+ FSOUND_FREE, /* index */
+ conf.nb_samples, /* length */
+ mode, /* mode */
+ as->freq, /* freq */
+ 255, /* volume */
+ 128, /* pan */
+ 255 /* priority */
+ );
+
+ if (!fmd->fmod_sample) {
+ fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
+ return -1;
+ }
+
+ /* FMOD always operates on little endian frames? */
+ obt_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &obt_as);
+ bits16 = (mode & FSOUND_16BITS) != 0;
+ hw->samples = conf.nb_samples;
+ return 0;
+}
+
+static void fmod_fini_in (HWVoiceIn *hw)
+{
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+
+ if (fmd->fmod_sample) {
+ FSOUND_Record_Stop ();
+ FSOUND_Sample_Free (fmd->fmod_sample);
+ fmd->fmod_sample = 0;
+ }
+}
+
+static int fmod_run_in (HWVoiceIn *hw)
+{
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+ int hwshift = hw->info.shift;
+ int live, dead, new_pos, len;
+ unsigned int blen1 = 0, blen2 = 0;
+ unsigned int len1, len2;
+ unsigned int decr;
+ void *p1, *p2;
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ if (!dead) {
+ return 0;
+ }
+
+ new_pos = FSOUND_Record_GetPosition ();
+ if (new_pos < 0) {
+ fmod_logerr ("Could not get recording position\n");
+ return 0;
+ }
+
+ len = audio_ring_dist (new_pos, hw->wpos, hw->samples);
+ if (!len) {
+ return 0;
+ }
+ len = audio_MIN (len, dead);
+
+ if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
+ hw->wpos, len,
+ &p1, &p2,
+ &blen1, &blen2)) {
+ return 0;
+ }
+
+ len1 = blen1 >> hwshift;
+ len2 = blen2 >> hwshift;
+ decr = len1 + len2;
+
+ if (p1 && blen1) {
+ hw->conv (hw->conv_buf + hw->wpos, p1, len1);
+ }
+ if (p2 && len2) {
+ hw->conv (hw->conv_buf, p2, len2);
+ }
+
+ fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
+ hw->wpos = (hw->wpos + decr) % hw->samples;
+ return decr;
+}
+
+static struct {
+ const char *name;
+ int type;
+} drvtab[] = {
+ { .name = "none", .type = FSOUND_OUTPUT_NOSOUND },
+#ifdef _WIN32
+ { .name = "winmm", .type = FSOUND_OUTPUT_WINMM },
+ { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND },
+ { .name = "a3d", .type = FSOUND_OUTPUT_A3D },
+ { .name = "asio", .type = FSOUND_OUTPUT_ASIO },
+#endif
+#ifdef __linux__
+ { .name = "oss", .type = FSOUND_OUTPUT_OSS },
+ { .name = "alsa", .type = FSOUND_OUTPUT_ALSA },
+ { .name = "esd", .type = FSOUND_OUTPUT_ESD },
+#endif
+#ifdef __APPLE__
+ { .name = "mac", .type = FSOUND_OUTPUT_MAC },
+#endif
+#if 0
+ { .name = "xbox", .type = FSOUND_OUTPUT_XBOX },
+ { .name = "ps2", .type = FSOUND_OUTPUT_PS2 },
+ { .name = "gcube", .type = FSOUND_OUTPUT_GC },
+#endif
+ { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
+};
+
+static void *fmod_audio_init (void)
+{
+ size_t i;
+ double ver;
+ int status;
+ int output_type = -1;
+ const char *drv = conf.drvname;
+
+ ver = FSOUND_GetVersion ();
+ if (ver < FMOD_VERSION) {
+ dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
+ return NULL;
+ }
+
+#ifdef __linux__
+ if (ver < 3.75) {
+ dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
+ "ADC will be disabled.\n");
+ conf.broken_adc = 1;
+ }
+#endif
+
+ if (drv) {
+ int found = 0;
+ for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+ if (!strcmp (drv, drvtab[i].name)) {
+ output_type = drvtab[i].type;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ dolog ("Unknown FMOD driver `%s'\n", drv);
+ dolog ("Valid drivers:\n");
+ for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+ dolog (" %s\n", drvtab[i].name);
+ }
+ }
+ }
+
+ if (output_type != -1) {
+ status = FSOUND_SetOutput (output_type);
+ if (!status) {
+ fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
+ return NULL;
+ }
+ }
+
+ if (conf.bufsize) {
+ status = FSOUND_SetBufferSize (conf.bufsize);
+ if (!status) {
+ fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
+ }
+ }
+
+ status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
+ if (!status) {
+ fmod_logerr ("FSOUND_Init failed\n");
+ return NULL;
+ }
+
+ return &conf;
+}
+
+static int fmod_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ int status;
+ FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
+ if (!status) {
+ fmod_logerr ("Failed to start recording\n");
+ }
+ break;
+
+ case VOICE_DISABLE:
+ status = FSOUND_Record_Stop ();
+ if (!status) {
+ fmod_logerr ("Failed to stop recording\n");
+ }
+ break;
+ }
+ return 0;
+}
+
+static void fmod_audio_fini (void *opaque)
+{
+ (void) opaque;
+ FSOUND_Close ();
+}
+
+static struct audio_option fmod_options[] = {
+ {
+ .name = "DRV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.drvname,
+ .descr = "FMOD driver"
+ },
+ {
+ .name = "FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.freq,
+ .descr = "Default frequency"
+ },
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nb_samples,
+ .descr = "Buffer size in samples"
+ },
+ {
+ .name = "CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nb_channels,
+ .descr = "Number of default channels (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "BUFSIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.bufsize,
+ .descr = "(undocumented)"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops fmod_pcm_ops = {
+ .init_out = fmod_init_out,
+ .fini_out = fmod_fini_out,
+ .run_out = fmod_run_out,
+ .write = fmod_write,
+ .ctl_out = fmod_ctl_out,
+
+ .init_in = fmod_init_in,
+ .fini_in = fmod_fini_in,
+ .run_in = fmod_run_in,
+ .read = fmod_read,
+ .ctl_in = fmod_ctl_in
+};
+
+struct audio_driver fmod_audio_driver = {
+ .name = "fmod",
+ .descr = "FMOD 3.xx http://www.fmod.org",
+ .options = fmod_options,
+ .init = fmod_audio_init,
+ .fini = fmod_audio_fini,
+ .pcm_ops = &fmod_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (FMODVoiceOut),
+ .voice_size_in = sizeof (FMODVoiceIn)
+};
diff --git a/audio/mixeng.c b/audio/mixeng.c
new file mode 100644
index 0000000..4a9e8eb
--- /dev/null
+++ b/audio/mixeng.c
@@ -0,0 +1,360 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "mixeng"
+#include "audio_int.h"
+
+/* 8 bit */
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+
+/* Signed 8 bit */
+#define IN_T int8_t
+#define IN_MIN SCHAR_MIN
+#define IN_MAX SCHAR_MAX
+#define SIGNED
+#define SHIFT 8
+#include "mixeng_template.h"
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 8 bit */
+#define IN_T uint8_t
+#define IN_MIN 0
+#define IN_MAX UCHAR_MAX
+#define SHIFT 8
+#include "mixeng_template.h"
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+
+/* Signed 16 bit */
+#define IN_T int16_t
+#define IN_MIN SHRT_MIN
+#define IN_MAX SHRT_MAX
+#define SIGNED
+#define SHIFT 16
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap16 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 16 bit */
+#define IN_T uint16_t
+#define IN_MIN 0
+#define IN_MAX USHRT_MAX
+#define SHIFT 16
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap16 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Signed 32 bit */
+#define IN_T int32_t
+#define IN_MIN INT32_MIN
+#define IN_MAX INT32_MAX
+#define SIGNED
+#define SHIFT 32
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap32 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 32 bit */
+#define IN_T uint32_t
+#define IN_MIN 0
+#define IN_MAX UINT32_MAX
+#define SHIFT 32
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap32 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+t_sample *mixeng_conv[2][2][2][3] = {
+ {
+ {
+ {
+ conv_natural_uint8_t_to_mono,
+ conv_natural_uint16_t_to_mono,
+ conv_natural_uint32_t_to_mono
+ },
+ {
+ conv_natural_uint8_t_to_mono,
+ conv_swap_uint16_t_to_mono,
+ conv_swap_uint32_t_to_mono,
+ }
+ },
+ {
+ {
+ conv_natural_int8_t_to_mono,
+ conv_natural_int16_t_to_mono,
+ conv_natural_int32_t_to_mono
+ },
+ {
+ conv_natural_int8_t_to_mono,
+ conv_swap_int16_t_to_mono,
+ conv_swap_int32_t_to_mono
+ }
+ }
+ },
+ {
+ {
+ {
+ conv_natural_uint8_t_to_stereo,
+ conv_natural_uint16_t_to_stereo,
+ conv_natural_uint32_t_to_stereo
+ },
+ {
+ conv_natural_uint8_t_to_stereo,
+ conv_swap_uint16_t_to_stereo,
+ conv_swap_uint32_t_to_stereo
+ }
+ },
+ {
+ {
+ conv_natural_int8_t_to_stereo,
+ conv_natural_int16_t_to_stereo,
+ conv_natural_int32_t_to_stereo
+ },
+ {
+ conv_natural_int8_t_to_stereo,
+ conv_swap_int16_t_to_stereo,
+ conv_swap_int32_t_to_stereo,
+ }
+ }
+ }
+};
+
+f_sample *mixeng_clip[2][2][2][3] = {
+ {
+ {
+ {
+ clip_natural_uint8_t_from_mono,
+ clip_natural_uint16_t_from_mono,
+ clip_natural_uint32_t_from_mono
+ },
+ {
+ clip_natural_uint8_t_from_mono,
+ clip_swap_uint16_t_from_mono,
+ clip_swap_uint32_t_from_mono
+ }
+ },
+ {
+ {
+ clip_natural_int8_t_from_mono,
+ clip_natural_int16_t_from_mono,
+ clip_natural_int32_t_from_mono
+ },
+ {
+ clip_natural_int8_t_from_mono,
+ clip_swap_int16_t_from_mono,
+ clip_swap_int32_t_from_mono
+ }
+ }
+ },
+ {
+ {
+ {
+ clip_natural_uint8_t_from_stereo,
+ clip_natural_uint16_t_from_stereo,
+ clip_natural_uint32_t_from_stereo
+ },
+ {
+ clip_natural_uint8_t_from_stereo,
+ clip_swap_uint16_t_from_stereo,
+ clip_swap_uint32_t_from_stereo
+ }
+ },
+ {
+ {
+ clip_natural_int8_t_from_stereo,
+ clip_natural_int16_t_from_stereo,
+ clip_natural_int32_t_from_stereo
+ },
+ {
+ clip_natural_int8_t_from_stereo,
+ clip_swap_int16_t_from_stereo,
+ clip_swap_int32_t_from_stereo
+ }
+ }
+ }
+};
+
+/*
+ * August 21, 1998
+ * Copyright 1998 Fabrice Bellard.
+ *
+ * [Rewrote completly the code of Lance Norskog And Sundry
+ * Contributors with a more efficient algorithm.]
+ *
+ * This source code is freely redistributable and may be used for
+ * any purpose. This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/*
+ * Sound Tools rate change effect file.
+ */
+/*
+ * Linear Interpolation.
+ *
+ * The use of fractional increment allows us to use no buffer. It
+ * avoid the problems at the end of the buffer we had with the old
+ * method which stored a possibly big buffer of size
+ * lcm(in_rate,out_rate).
+ *
+ * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
+ * the input & output frequencies are equal, a delay of one sample is
+ * introduced. Limited to processing 32-bit count worth of samples.
+ *
+ * 1 << FRAC_BITS evaluating to zero in several places. Changed with
+ * an (unsigned long) cast to make it safe. MarkMLl 2/1/99
+ */
+
+/* Private data */
+struct rate {
+ uint64_t opos;
+ uint64_t opos_inc;
+ uint32_t ipos; /* position in the input stream (integer) */
+ struct st_sample ilast; /* last sample in the input stream */
+};
+
+/*
+ * Prepare processing.
+ */
+void *st_rate_start (int inrate, int outrate)
+{
+ struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
+
+ if (!rate) {
+ dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
+ return NULL;
+ }
+
+ rate->opos = 0;
+
+ /* increment */
+ rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
+
+ rate->ipos = 0;
+ rate->ilast.l = 0;
+ rate->ilast.r = 0;
+ return rate;
+}
+
+#define NAME st_rate_flow_mix
+#define OP(a, b) a += b
+#include "rate_template.h"
+
+#define NAME st_rate_flow
+#define OP(a, b) a = b
+#include "rate_template.h"
+
+void st_rate_stop (void *opaque)
+{
+ qemu_free (opaque);
+}
+
+void mixeng_clear (struct st_sample *buf, int len)
+{
+ memset (buf, 0, len * sizeof (struct st_sample));
+}
+
+void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol)
+{
+#ifdef CONFIG_MIXEMU
+ if (vol->mute) {
+ mixeng_clear (buf, len);
+ return;
+ }
+
+ while (len--) {
+#ifdef FLOAT_MIXENG
+ buf->l = buf->l * vol->l;
+ buf->r = buf->r * vol->r;
+#else
+ buf->l = (buf->l * vol->l) >> 32;
+ buf->r = (buf->r * vol->r) >> 32;
+#endif
+ buf += 1;
+ }
+#else
+ (void) buf;
+ (void) len;
+ (void) vol;
+#endif
+}
diff --git a/audio/mixeng.h b/audio/mixeng.h
new file mode 100644
index 0000000..9de443b
--- /dev/null
+++ b/audio/mixeng.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU Mixing engine header
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_MIXENG_H
+#define QEMU_MIXENG_H
+
+#ifdef FLOAT_MIXENG
+typedef float mixeng_real;
+struct mixeng_volume { int mute; mixeng_real r; mixeng_real l; };
+struct st_sample { mixeng_real l; mixeng_real r; };
+#else
+struct mixeng_volume { int mute; int64_t r; int64_t l; };
+struct st_sample { int64_t l; int64_t r; };
+#endif
+
+typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
+typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
+
+extern t_sample *mixeng_conv[2][2][2][3];
+extern f_sample *mixeng_clip[2][2][2][3];
+
+void *st_rate_start (int inrate, int outrate);
+void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
+ int *isamp, int *osamp);
+void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
+ int *isamp, int *osamp);
+void st_rate_stop (void *opaque);
+void mixeng_clear (struct st_sample *buf, int len);
+void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);
+
+#endif /* mixeng.h */
diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
new file mode 100644
index 0000000..a2d0ef8
--- /dev/null
+++ b/audio/mixeng_template.h
@@ -0,0 +1,152 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Tusen tack till Mike Nordell
+ * dec++'ified by Dscho
+ */
+
+#ifndef SIGNED
+#define HALF (IN_MAX >> 1)
+#endif
+
+#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
+
+#ifdef FLOAT_MIXENG
+static mixeng_real inline glue (conv_, ET) (IN_T v)
+{
+ IN_T nv = ENDIAN_CONVERT (v);
+
+#ifdef RECIPROCAL
+#ifdef SIGNED
+ return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN));
+#else
+ return (nv - HALF) * (1.f / (mixeng_real) IN_MAX);
+#endif
+#else /* !RECIPROCAL */
+#ifdef SIGNED
+ return nv / (mixeng_real) (IN_MAX - IN_MIN);
+#else
+ return (nv - HALF) / (mixeng_real) IN_MAX;
+#endif
+#endif
+}
+
+static IN_T inline glue (clip_, ET) (mixeng_real v)
+{
+ if (v >= 0.5) {
+ return IN_MAX;
+ }
+ else if (v < -0.5) {
+ return IN_MIN;
+ }
+
+#ifdef SIGNED
+ return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
+#else
+ return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
+#endif
+}
+
+#else /* !FLOAT_MIXENG */
+
+static inline int64_t glue (conv_, ET) (IN_T v)
+{
+ IN_T nv = ENDIAN_CONVERT (v);
+#ifdef SIGNED
+ return ((int64_t) nv) << (32 - SHIFT);
+#else
+ return ((int64_t) nv - HALF) << (32 - SHIFT);
+#endif
+}
+
+static inline IN_T glue (clip_, ET) (int64_t v)
+{
+ if (v >= 0x7f000000) {
+ return IN_MAX;
+ }
+ else if (v < -2147483648LL) {
+ return IN_MIN;
+ }
+
+#ifdef SIGNED
+ return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
+#else
+ return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
+#endif
+}
+#endif
+
+static void glue (glue (conv_, ET), _to_stereo)
+ (struct st_sample *dst, const void *src, int samples)
+{
+ struct st_sample *out = dst;
+ IN_T *in = (IN_T *) src;
+
+ while (samples--) {
+ out->l = glue (conv_, ET) (*in++);
+ out->r = glue (conv_, ET) (*in++);
+ out += 1;
+ }
+}
+
+static void glue (glue (conv_, ET), _to_mono)
+ (struct st_sample *dst, const void *src, int samples)
+{
+ struct st_sample *out = dst;
+ IN_T *in = (IN_T *) src;
+
+ while (samples--) {
+ out->l = glue (conv_, ET) (in[0]);
+ out->r = out->l;
+ out += 1;
+ in += 1;
+ }
+}
+
+static void glue (glue (clip_, ET), _from_stereo)
+ (void *dst, const struct st_sample *src, int samples)
+{
+ const struct st_sample *in = src;
+ IN_T *out = (IN_T *) dst;
+ while (samples--) {
+ *out++ = glue (clip_, ET) (in->l);
+ *out++ = glue (clip_, ET) (in->r);
+ in += 1;
+ }
+}
+
+static void glue (glue (clip_, ET), _from_mono)
+ (void *dst, const struct st_sample *src, int samples)
+{
+ const struct st_sample *in = src;
+ IN_T *out = (IN_T *) dst;
+ while (samples--) {
+ *out++ = glue (clip_, ET) (in->l + in->r);
+ in += 1;
+ }
+}
+
+#undef ET
+#undef HALF
diff --git a/audio/noaudio.c b/audio/noaudio.c
new file mode 100644
index 0000000..0304094
--- /dev/null
+++ b/audio/noaudio.c
@@ -0,0 +1,173 @@
+/*
+ * QEMU Timer based audio emulation
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "audio.h"
+#include "qemu-timer.h"
+
+#define AUDIO_CAP "noaudio"
+#include "audio_int.h"
+
+typedef struct NoVoiceOut {
+ HWVoiceOut hw;
+ int64_t old_ticks;
+} NoVoiceOut;
+
+typedef struct NoVoiceIn {
+ HWVoiceIn hw;
+ int64_t old_ticks;
+} NoVoiceIn;
+
+static int no_run_out (HWVoiceOut *hw, int live)
+{
+ NoVoiceOut *no = (NoVoiceOut *) hw;
+ int decr, samples;
+ int64_t now;
+ int64_t ticks;
+ int64_t bytes;
+
+ now = qemu_get_clock (vm_clock);
+ ticks = now - no->old_ticks;
+ bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
+ bytes = audio_MIN (bytes, INT_MAX);
+ samples = bytes >> hw->info.shift;
+
+ no->old_ticks = now;
+ decr = audio_MIN (live, samples);
+ hw->rpos = (hw->rpos + decr) % hw->samples;
+ return decr;
+}
+
+static int no_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int no_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = 1024;
+ return 0;
+}
+
+static void no_fini_out (HWVoiceOut *hw)
+{
+ (void) hw;
+}
+
+static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static int no_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = 1024;
+ return 0;
+}
+
+static void no_fini_in (HWVoiceIn *hw)
+{
+ (void) hw;
+}
+
+static int no_run_in (HWVoiceIn *hw)
+{
+ NoVoiceIn *no = (NoVoiceIn *) hw;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ int samples = 0;
+
+ if (dead) {
+ int64_t now = qemu_get_clock (vm_clock);
+ int64_t ticks = now - no->old_ticks;
+ int64_t bytes =
+ muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
+
+ no->old_ticks = now;
+ bytes = audio_MIN (bytes, INT_MAX);
+ samples = bytes >> hw->info.shift;
+ samples = audio_MIN (samples, dead);
+ }
+ return samples;
+}
+
+static int no_read (SWVoiceIn *sw, void *buf, int size)
+{
+ /* use custom code here instead of audio_pcm_sw_read() to avoid
+ * useless resampling/mixing */
+ int samples = size >> sw->info.shift;
+ int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
+ int to_clear = audio_MIN (samples, total);
+ sw->total_hw_samples_acquired += total;
+ audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
+ return to_clear << sw->info.shift;
+}
+
+static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static void *no_audio_init (void)
+{
+ return &no_audio_init;
+}
+
+static void no_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_pcm_ops no_pcm_ops = {
+ .init_out = no_init_out,
+ .fini_out = no_fini_out,
+ .run_out = no_run_out,
+ .write = no_write,
+ .ctl_out = no_ctl_out,
+
+ .init_in = no_init_in,
+ .fini_in = no_fini_in,
+ .run_in = no_run_in,
+ .read = no_read,
+ .ctl_in = no_ctl_in
+};
+
+struct audio_driver no_audio_driver = {
+ .name = "none",
+ .descr = "Timer based audio emulation",
+ .options = NULL,
+ .init = no_audio_init,
+ .fini = no_audio_fini,
+ .pcm_ops = &no_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (NoVoiceOut),
+ .voice_size_in = sizeof (NoVoiceIn)
+};
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
new file mode 100644
index 0000000..b49e102
--- /dev/null
+++ b/audio/ossaudio.c
@@ -0,0 +1,944 @@
+/*
+ * QEMU OSS audio driver
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef __OpenBSD__
+#include <soundcard.h>
+#else
+#include <sys/soundcard.h>
+#endif
+#include "qemu-common.h"
+#include "host-utils.h"
+#include "qemu-char.h"
+#include "audio.h"
+
+#define AUDIO_CAP "oss"
+#include "audio_int.h"
+
+#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
+#define USE_DSP_POLICY
+#endif
+
+typedef struct OSSVoiceOut {
+ HWVoiceOut hw;
+ void *pcm_buf;
+ int fd;
+ int wpos;
+ int nfrags;
+ int fragsize;
+ int mmapped;
+ int pending;
+} OSSVoiceOut;
+
+typedef struct OSSVoiceIn {
+ HWVoiceIn hw;
+ void *pcm_buf;
+ int fd;
+ int nfrags;
+ int fragsize;
+} OSSVoiceIn;
+
+static struct {
+ int try_mmap;
+ int nfrags;
+ int fragsize;
+ const char *devpath_out;
+ const char *devpath_in;
+ int debug;
+ int exclusive;
+ int policy;
+} conf = {
+ .try_mmap = 0,
+ .nfrags = 4,
+ .fragsize = 4096,
+ .devpath_out = "/dev/dsp",
+ .devpath_in = "/dev/dsp",
+ .debug = 0,
+ .exclusive = 0,
+ .policy = 5
+};
+
+struct oss_params {
+ int freq;
+ audfmt_e fmt;
+ int nchannels;
+ int nfrags;
+ int fragsize;
+};
+
+static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
+ int err,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void oss_anal_close (int *fdp)
+{
+ int err;
+
+ qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
+ err = close (*fdp);
+ if (err) {
+ oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
+ }
+ *fdp = -1;
+}
+
+static void oss_helper_poll_out (void *opaque)
+{
+ (void) opaque;
+ audio_run ("oss_poll_out");
+}
+
+static void oss_helper_poll_in (void *opaque)
+{
+ (void) opaque;
+ audio_run ("oss_poll_in");
+}
+
+static int oss_poll_out (HWVoiceOut *hw)
+{
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+ return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
+}
+
+static int oss_poll_in (HWVoiceIn *hw)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+ return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
+}
+
+static int oss_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int aud_to_ossfmt (audfmt_e fmt, int endianness)
+{
+ switch (fmt) {
+ case AUD_FMT_S8:
+ return AFMT_S8;
+
+ case AUD_FMT_U8:
+ return AFMT_U8;
+
+ case AUD_FMT_S16:
+ if (endianness) {
+ return AFMT_S16_BE;
+ }
+ else {
+ return AFMT_S16_LE;
+ }
+
+ case AUD_FMT_U16:
+ if (endianness) {
+ return AFMT_U16_BE;
+ }
+ else {
+ return AFMT_U16_LE;
+ }
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+ abort ();
+#endif
+ return AFMT_U8;
+ }
+}
+
+static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
+{
+ switch (ossfmt) {
+ case AFMT_S8:
+ *endianness = 0;
+ *fmt = AUD_FMT_S8;
+ break;
+
+ case AFMT_U8:
+ *endianness = 0;
+ *fmt = AUD_FMT_U8;
+ break;
+
+ case AFMT_S16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AFMT_U16_LE:
+ *endianness = 0;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case AFMT_S16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AFMT_U16_BE:
+ *endianness = 1;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ default:
+ dolog ("Unrecognized audio format %d\n", ossfmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined DEBUG_MISMATCHES || defined DEBUG
+static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
+{
+ dolog ("parameter | requested value | obtained value\n");
+ dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
+ dolog ("channels | %10d | %10d\n",
+ req->nchannels, obt->nchannels);
+ dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
+ dolog ("nfrags | %10d | %10d\n", req->nfrags, obt->nfrags);
+ dolog ("fragsize | %10d | %10d\n",
+ req->fragsize, obt->fragsize);
+}
+#endif
+
+#ifdef USE_DSP_POLICY
+static int oss_get_version (int fd, int *version, const char *typ)
+{
+ if (ioctl (fd, OSS_GETVERSION, &version)) {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /*
+ * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
+ * since 7.x, but currently only on the mixer device (or in
+ * the Linuxolator), and in the native version that part of
+ * the code is in fact never reached so the ioctl fails anyway.
+ * Until this is fixed, just check the errno and if its what
+ * FreeBSD's sound drivers return atm assume they are new enough.
+ */
+ if (errno == EINVAL) {
+ *version = 0x040000;
+ return 0;
+ }
+#endif
+ oss_logerr2 (errno, typ, "Failed to get OSS version\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+static int oss_open (int in, struct oss_params *req,
+ struct oss_params *obt, int *pfd)
+{
+ int fd;
+ int oflags = conf.exclusive ? O_EXCL : 0;
+ audio_buf_info abinfo;
+ int fmt, freq, nchannels;
+ int setfragment = 1;
+ const char *dspname = in ? conf.devpath_in : conf.devpath_out;
+ const char *typ = in ? "ADC" : "DAC";
+
+ /* Kludge needed to have working mmap on Linux */
+ oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+
+ fd = open (dspname, oflags | O_NONBLOCK);
+ if (-1 == fd) {
+ oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
+ return -1;
+ }
+
+ freq = req->freq;
+ nchannels = req->nchannels;
+ fmt = req->fmt;
+
+ if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
+ oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
+ goto err;
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
+ oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
+ req->nchannels);
+ goto err;
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
+ oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
+ goto err;
+ }
+
+ if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
+ oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
+ goto err;
+ }
+
+#ifdef USE_DSP_POLICY
+ if (conf.policy >= 0) {
+ int version;
+
+ if (!oss_get_version (fd, &version, typ)) {
+ if (conf.debug) {
+ dolog ("OSS version = %#x\n", version);
+ }
+
+ if (version >= 0x040000) {
+ int policy = conf.policy;
+ if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+ oss_logerr2 (errno, typ,
+ "Failed to set timing policy to %d\n",
+ conf.policy);
+ goto err;
+ }
+ setfragment = 0;
+ }
+ }
+ }
+#endif
+
+ if (setfragment) {
+ int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
+ if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
+ oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
+ req->nfrags, req->fragsize);
+ goto err;
+ }
+ }
+
+ if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
+ oss_logerr2 (errno, typ, "Failed to get buffer length\n");
+ goto err;
+ }
+
+ if (!abinfo.fragstotal || !abinfo.fragsize) {
+ AUD_log (AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
+ abinfo.fragstotal, abinfo.fragsize, typ);
+ goto err;
+ }
+
+ obt->fmt = fmt;
+ obt->nchannels = nchannels;
+ obt->freq = freq;
+ obt->nfrags = abinfo.fragstotal;
+ obt->fragsize = abinfo.fragsize;
+ *pfd = fd;
+
+#ifdef DEBUG_MISMATCHES
+ if ((req->fmt != obt->fmt) ||
+ (req->nchannels != obt->nchannels) ||
+ (req->freq != obt->freq) ||
+ (req->fragsize != obt->fragsize) ||
+ (req->nfrags != obt->nfrags)) {
+ dolog ("Audio parameters mismatch\n");
+ oss_dump_info (req, obt);
+ }
+#endif
+
+#ifdef DEBUG
+ oss_dump_info (req, obt);
+#endif
+ return 0;
+
+ err:
+ oss_anal_close (&fd);
+ return -1;
+}
+
+static void oss_write_pending (OSSVoiceOut *oss)
+{
+ HWVoiceOut *hw = &oss->hw;
+
+ if (oss->mmapped) {
+ return;
+ }
+
+ while (oss->pending) {
+ int samples_written;
+ ssize_t bytes_written;
+ int samples_till_end = hw->samples - oss->wpos;
+ int samples_to_write = audio_MIN (oss->pending, samples_till_end);
+ int bytes_to_write = samples_to_write << hw->info.shift;
+ void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
+
+ bytes_written = write (oss->fd, pcm, bytes_to_write);
+ if (bytes_written < 0) {
+ if (errno != EAGAIN) {
+ oss_logerr (errno, "failed to write %d bytes\n",
+ bytes_to_write);
+ }
+ break;
+ }
+
+ if (bytes_written & hw->info.align) {
+ dolog ("misaligned write asked for %d, but got %zd\n",
+ bytes_to_write, bytes_written);
+ return;
+ }
+
+ samples_written = bytes_written >> hw->info.shift;
+ oss->pending -= samples_written;
+ oss->wpos = (oss->wpos + samples_written) % hw->samples;
+ if (bytes_written - bytes_to_write) {
+ break;
+ }
+ }
+}
+
+static int oss_run_out (HWVoiceOut *hw, int live)
+{
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+ int err, decr;
+ struct audio_buf_info abinfo;
+ struct count_info cntinfo;
+ int bufsize;
+
+ bufsize = hw->samples << hw->info.shift;
+
+ if (oss->mmapped) {
+ int bytes, pos;
+
+ err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
+ if (err < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
+ return 0;
+ }
+
+ pos = hw->rpos << hw->info.shift;
+ bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
+ decr = audio_MIN (bytes >> hw->info.shift, live);
+ }
+ else {
+ err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
+ if (err < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
+ return 0;
+ }
+
+ if (abinfo.bytes > bufsize) {
+ if (conf.debug) {
+ dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
+ "please report your OS/audio hw to av1474@comtv.ru\n",
+ abinfo.bytes, bufsize);
+ }
+ abinfo.bytes = bufsize;
+ }
+
+ if (abinfo.bytes < 0) {
+ if (conf.debug) {
+ dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
+ abinfo.bytes, bufsize);
+ }
+ return 0;
+ }
+
+ decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
+ if (!decr) {
+ return 0;
+ }
+ }
+
+ decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
+ oss->pending += decr;
+ oss_write_pending (oss);
+
+ return decr;
+}
+
+static void oss_fini_out (HWVoiceOut *hw)
+{
+ int err;
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+ ldebug ("oss_fini\n");
+ oss_anal_close (&oss->fd);
+
+ if (oss->pcm_buf) {
+ if (oss->mmapped) {
+ err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+ if (err) {
+ oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
+ oss->pcm_buf, hw->samples << hw->info.shift);
+ }
+ }
+ else {
+ qemu_free (oss->pcm_buf);
+ }
+ oss->pcm_buf = NULL;
+ }
+}
+
+static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+ struct oss_params req, obt;
+ int endianness;
+ int err;
+ int fd;
+ audfmt_e effective_fmt;
+ struct audsettings obt_as;
+
+ oss->fd = -1;
+
+ req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.fragsize = conf.fragsize;
+ req.nfrags = conf.nfrags;
+
+ if (oss_open (0, &req, &obt, &fd)) {
+ return -1;
+ }
+
+ err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+ if (err) {
+ oss_anal_close (&fd);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ oss->nfrags = obt.nfrags;
+ oss->fragsize = obt.fragsize;
+
+ if (obt.nfrags * obt.fragsize & hw->info.align) {
+ dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
+ obt.nfrags * obt.fragsize, hw->info.align + 1);
+ }
+
+ hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+
+ oss->mmapped = 0;
+ if (conf.try_mmap) {
+ oss->pcm_buf = mmap (
+ NULL,
+ hw->samples << hw->info.shift,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fd,
+ 0
+ );
+ if (oss->pcm_buf == MAP_FAILED) {
+ oss_logerr (errno, "Failed to map %d bytes of DAC\n",
+ hw->samples << hw->info.shift);
+ }
+ else {
+ int err;
+ int trig = 0;
+ if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
+ }
+ else {
+ trig = PCM_ENABLE_OUTPUT;
+ if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (
+ errno,
+ "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+ );
+ }
+ else {
+ oss->mmapped = 1;
+ }
+ }
+
+ if (!oss->mmapped) {
+ err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+ if (err) {
+ oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
+ oss->pcm_buf, hw->samples << hw->info.shift);
+ }
+ }
+ }
+ }
+
+ if (!oss->mmapped) {
+ oss->pcm_buf = audio_calloc (
+ AUDIO_FUNC,
+ hw->samples,
+ 1 << hw->info.shift
+ );
+ if (!oss->pcm_buf) {
+ dolog (
+ "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
+ hw->samples,
+ 1 << hw->info.shift
+ );
+ oss_anal_close (&fd);
+ return -1;
+ }
+ }
+
+ oss->fd = fd;
+ return 0;
+}
+
+static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ int trig;
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ ldebug ("enabling voice\n");
+ if (poll_mode && oss_poll_out (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+
+ if (!oss->mmapped) {
+ return 0;
+ }
+
+ audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+ trig = PCM_ENABLE_OUTPUT;
+ if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (
+ errno,
+ "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+ );
+ return -1;
+ }
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (hw->poll_mode) {
+ qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
+ hw->poll_mode = 0;
+ }
+
+ if (!oss->mmapped) {
+ return 0;
+ }
+
+ ldebug ("disabling voice\n");
+ trig = 0;
+ if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
+ return -1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+ struct oss_params req, obt;
+ int endianness;
+ int err;
+ int fd;
+ audfmt_e effective_fmt;
+ struct audsettings obt_as;
+
+ oss->fd = -1;
+
+ req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
+ req.freq = as->freq;
+ req.nchannels = as->nchannels;
+ req.fragsize = conf.fragsize;
+ req.nfrags = conf.nfrags;
+ if (oss_open (1, &req, &obt, &fd)) {
+ return -1;
+ }
+
+ err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+ if (err) {
+ oss_anal_close (&fd);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.nchannels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianness;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ oss->nfrags = obt.nfrags;
+ oss->fragsize = obt.fragsize;
+
+ if (obt.nfrags * obt.fragsize & hw->info.align) {
+ dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
+ obt.nfrags * obt.fragsize, hw->info.align + 1);
+ }
+
+ hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+ oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!oss->pcm_buf) {
+ dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
+ hw->samples, 1 << hw->info.shift);
+ oss_anal_close (&fd);
+ return -1;
+ }
+
+ oss->fd = fd;
+ return 0;
+}
+
+static void oss_fini_in (HWVoiceIn *hw)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+ oss_anal_close (&oss->fd);
+
+ if (oss->pcm_buf) {
+ qemu_free (oss->pcm_buf);
+ oss->pcm_buf = NULL;
+ }
+}
+
+static int oss_run_in (HWVoiceIn *hw)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+ int hwshift = hw->info.shift;
+ int i;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ size_t read_samples = 0;
+ struct {
+ int add;
+ int len;
+ } bufs[2] = {
+ { .add = hw->wpos, .len = 0 },
+ { .add = 0, .len = 0 }
+ };
+
+ if (!dead) {
+ return 0;
+ }
+
+ if (hw->wpos + dead > hw->samples) {
+ bufs[0].len = (hw->samples - hw->wpos) << hwshift;
+ bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
+ }
+ else {
+ bufs[0].len = dead << hwshift;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ ssize_t nread;
+
+ if (bufs[i].len) {
+ void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
+ nread = read (oss->fd, p, bufs[i].len);
+
+ if (nread > 0) {
+ if (nread & hw->info.align) {
+ dolog ("warning: Misaligned read %zd (requested %d), "
+ "alignment %d\n", nread, bufs[i].add << hwshift,
+ hw->info.align + 1);
+ }
+ read_samples += nread >> hwshift;
+ hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
+ }
+
+ if (bufs[i].len - nread) {
+ if (nread == -1) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+ break;
+ default:
+ oss_logerr (
+ errno,
+ "Failed to read %d bytes of audio (to %p)\n",
+ bufs[i].len, p
+ );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ hw->wpos = (hw->wpos + read_samples) % hw->samples;
+ return read_samples;
+}
+
+static int oss_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ if (poll_mode && oss_poll_in (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (hw->poll_mode) {
+ hw->poll_mode = 0;
+ qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
+ }
+ break;
+ }
+ return 0;
+}
+
+static void *oss_audio_init (void)
+{
+ return &conf;
+}
+
+static void oss_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option oss_options[] = {
+ {
+ .name = "FRAGSIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fragsize,
+ .descr = "Fragment size in bytes"
+ },
+ {
+ .name = "NFRAGS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nfrags,
+ .descr = "Number of fragments"
+ },
+ {
+ .name = "MMAP",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.try_mmap,
+ .descr = "Try using memory mapped access"
+ },
+ {
+ .name = "DAC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.devpath_out,
+ .descr = "Path to DAC device"
+ },
+ {
+ .name = "ADC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.devpath_in,
+ .descr = "Path to ADC device"
+ },
+ {
+ .name = "EXCLUSIVE",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.exclusive,
+ .descr = "Open device in exclusive mode (vmix wont work)"
+ },
+#ifdef USE_DSP_POLICY
+ {
+ .name = "POLICY",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.policy,
+ .descr = "Set the timing policy of the device, -1 to use fragment mode",
+ },
+#endif
+ {
+ .name = "DEBUG",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.debug,
+ .descr = "Turn on some debugging messages"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops oss_pcm_ops = {
+ .init_out = oss_init_out,
+ .fini_out = oss_fini_out,
+ .run_out = oss_run_out,
+ .write = oss_write,
+ .ctl_out = oss_ctl_out,
+
+ .init_in = oss_init_in,
+ .fini_in = oss_fini_in,
+ .run_in = oss_run_in,
+ .read = oss_read,
+ .ctl_in = oss_ctl_in
+};
+
+struct audio_driver oss_audio_driver = {
+ .name = "oss",
+ .descr = "OSS http://www.opensound.com",
+ .options = oss_options,
+ .init = oss_audio_init,
+ .fini = oss_audio_fini,
+ .pcm_ops = &oss_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (OSSVoiceOut),
+ .voice_size_in = sizeof (OSSVoiceIn)
+};
diff --git a/audio/paaudio.c b/audio/paaudio.c
new file mode 100644
index 0000000..fb4510e
--- /dev/null
+++ b/audio/paaudio.c
@@ -0,0 +1,525 @@
+/* public domain */
+#include "qemu-common.h"
+#include "audio.h"
+
+#include <pulse/simple.h>
+#include <pulse/error.h>
+
+#define AUDIO_CAP "pulseaudio"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+typedef struct {
+ HWVoiceOut hw;
+ int done;
+ int live;
+ int decr;
+ int rpos;
+ pa_simple *s;
+ void *pcm_buf;
+ struct audio_pt pt;
+} PAVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ int done;
+ int dead;
+ int incr;
+ int wpos;
+ pa_simple *s;
+ void *pcm_buf;
+ struct audio_pt pt;
+} PAVoiceIn;
+
+static struct {
+ int samples;
+ char *server;
+ char *sink;
+ char *source;
+} conf = {
+ .samples = 4096,
+};
+
+static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
+}
+
+static void *qpa_thread_out (void *arg)
+{
+ PAVoiceOut *pa = arg;
+ HWVoiceOut *hw = &pa->hw;
+
+ if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ for (;;) {
+ int decr, to_mix, rpos;
+
+ for (;;) {
+ if (pa->done) {
+ goto exit;
+ }
+
+ if (pa->live > 0) {
+ break;
+ }
+
+ if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
+ rpos = pa->rpos;
+
+ if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ while (to_mix) {
+ int error;
+ int chunk = audio_MIN (to_mix, hw->samples - rpos);
+ struct st_sample *src = hw->mix_buf + rpos;
+
+ hw->clip (pa->pcm_buf, src, chunk);
+
+ if (pa_simple_write (pa->s, pa->pcm_buf,
+ chunk << hw->info.shift, &error) < 0) {
+ qpa_logerr (error, "pa_simple_write failed\n");
+ return NULL;
+ }
+
+ rpos = (rpos + chunk) % hw->samples;
+ to_mix -= chunk;
+ }
+
+ if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ pa->rpos = rpos;
+ pa->live -= decr;
+ pa->decr += decr;
+ }
+
+ exit:
+ audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qpa_run_out (HWVoiceOut *hw, int live)
+{
+ int decr;
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+
+ if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ decr = audio_MIN (live, pa->decr);
+ pa->decr -= decr;
+ pa->live = live - decr;
+ hw->rpos = pa->rpos;
+ if (pa->live > 0) {
+ audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+ }
+ return decr;
+}
+
+static int qpa_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+/* capture */
+static void *qpa_thread_in (void *arg)
+{
+ PAVoiceIn *pa = arg;
+ HWVoiceIn *hw = &pa->hw;
+
+ if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ for (;;) {
+ int incr, to_grab, wpos;
+
+ for (;;) {
+ if (pa->done) {
+ goto exit;
+ }
+
+ if (pa->dead > 0) {
+ break;
+ }
+
+ if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
+ wpos = pa->wpos;
+
+ if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ while (to_grab) {
+ int error;
+ int chunk = audio_MIN (to_grab, hw->samples - wpos);
+ void *buf = advance (pa->pcm_buf, wpos);
+
+ if (pa_simple_read (pa->s, buf,
+ chunk << hw->info.shift, &error) < 0) {
+ qpa_logerr (error, "pa_simple_read failed\n");
+ return NULL;
+ }
+
+ hw->conv (hw->conv_buf + wpos, buf, chunk);
+ wpos = (wpos + chunk) % hw->samples;
+ to_grab -= chunk;
+ }
+
+ if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ pa->wpos = wpos;
+ pa->dead -= incr;
+ pa->incr += incr;
+ }
+
+ exit:
+ audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qpa_run_in (HWVoiceIn *hw)
+{
+ int live, incr, dead;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+
+ if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ incr = audio_MIN (dead, pa->incr);
+ pa->incr -= incr;
+ pa->dead = dead - incr;
+ hw->wpos = pa->wpos;
+ if (pa->dead > 0) {
+ audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+ }
+ return incr;
+}
+
+static int qpa_read (SWVoiceIn *sw, void *buf, int len)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
+{
+ int format;
+
+ switch (afmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ format = PA_SAMPLE_U8;
+ break;
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
+ break;
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
+ break;
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", afmt);
+ format = PA_SAMPLE_U8;
+ break;
+ }
+ return format;
+}
+
+static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
+{
+ switch (fmt) {
+ case PA_SAMPLE_U8:
+ return AUD_FMT_U8;
+ case PA_SAMPLE_S16BE:
+ *endianness = 1;
+ return AUD_FMT_S16;
+ case PA_SAMPLE_S16LE:
+ *endianness = 0;
+ return AUD_FMT_S16;
+ case PA_SAMPLE_S32BE:
+ *endianness = 1;
+ return AUD_FMT_S32;
+ case PA_SAMPLE_S32LE:
+ *endianness = 0;
+ return AUD_FMT_S32;
+ default:
+ dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
+ return AUD_FMT_U8;
+ }
+}
+
+static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ int error;
+ static pa_sample_spec ss;
+ static pa_buffer_attr ba;
+ struct audsettings obt_as = *as;
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+
+ ss.format = audfmt_to_pa (as->fmt, as->endianness);
+ ss.channels = as->nchannels;
+ ss.rate = as->freq;
+
+ /*
+ * qemu audio tick runs at 250 Hz (by default), so processing
+ * data chunks worth 4 ms of sound should be a good fit.
+ */
+ ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
+ ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
+ ba.maxlength = -1;
+ ba.prebuf = -1;
+
+ obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
+
+ pa->s = pa_simple_new (
+ conf.server,
+ "qemu",
+ PA_STREAM_PLAYBACK,
+ conf.sink,
+ "pcm.playback",
+ &ss,
+ NULL, /* channel map */
+ &ba, /* buffering attributes */
+ &error
+ );
+ if (!pa->s) {
+ qpa_logerr (error, "pa_simple_new for playback failed\n");
+ goto fail1;
+ }
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = conf.samples;
+ pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ pa->rpos = hw->rpos;
+ if (!pa->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ goto fail2;
+ }
+
+ if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail3;
+ }
+
+ return 0;
+
+ fail3:
+ qemu_free (pa->pcm_buf);
+ pa->pcm_buf = NULL;
+ fail2:
+ pa_simple_free (pa->s);
+ pa->s = NULL;
+ fail1:
+ return -1;
+}
+
+static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ int error;
+ static pa_sample_spec ss;
+ struct audsettings obt_as = *as;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+
+ ss.format = audfmt_to_pa (as->fmt, as->endianness);
+ ss.channels = as->nchannels;
+ ss.rate = as->freq;
+
+ obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
+
+ pa->s = pa_simple_new (
+ conf.server,
+ "qemu",
+ PA_STREAM_RECORD,
+ conf.source,
+ "pcm.capture",
+ &ss,
+ NULL, /* channel map */
+ NULL, /* buffering attributes */
+ &error
+ );
+ if (!pa->s) {
+ qpa_logerr (error, "pa_simple_new for capture failed\n");
+ goto fail1;
+ }
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = conf.samples;
+ pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ pa->wpos = hw->wpos;
+ if (!pa->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ goto fail2;
+ }
+
+ if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail3;
+ }
+
+ return 0;
+
+ fail3:
+ qemu_free (pa->pcm_buf);
+ pa->pcm_buf = NULL;
+ fail2:
+ pa_simple_free (pa->s);
+ pa->s = NULL;
+ fail1:
+ return -1;
+}
+
+static void qpa_fini_out (HWVoiceOut *hw)
+{
+ void *ret;
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+
+ audio_pt_lock (&pa->pt, AUDIO_FUNC);
+ pa->done = 1;
+ audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+ audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
+
+ if (pa->s) {
+ pa_simple_free (pa->s);
+ pa->s = NULL;
+ }
+
+ audio_pt_fini (&pa->pt, AUDIO_FUNC);
+ qemu_free (pa->pcm_buf);
+ pa->pcm_buf = NULL;
+}
+
+static void qpa_fini_in (HWVoiceIn *hw)
+{
+ void *ret;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+
+ audio_pt_lock (&pa->pt, AUDIO_FUNC);
+ pa->done = 1;
+ audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+ audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
+
+ if (pa->s) {
+ pa_simple_free (pa->s);
+ pa->s = NULL;
+ }
+
+ audio_pt_fini (&pa->pt, AUDIO_FUNC);
+ qemu_free (pa->pcm_buf);
+ pa->pcm_buf = NULL;
+}
+
+static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* common */
+static void *qpa_audio_init (void)
+{
+ return &conf;
+}
+
+static void qpa_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+struct audio_option qpa_options[] = {
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.samples,
+ .descr = "buffer size in samples"
+ },
+ {
+ .name = "SERVER",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.server,
+ .descr = "server address"
+ },
+ {
+ .name = "SINK",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.sink,
+ .descr = "sink device name"
+ },
+ {
+ .name = "SOURCE",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.source,
+ .descr = "source device name"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops qpa_pcm_ops = {
+ .init_out = qpa_init_out,
+ .fini_out = qpa_fini_out,
+ .run_out = qpa_run_out,
+ .write = qpa_write,
+ .ctl_out = qpa_ctl_out,
+
+ .init_in = qpa_init_in,
+ .fini_in = qpa_fini_in,
+ .run_in = qpa_run_in,
+ .read = qpa_read,
+ .ctl_in = qpa_ctl_in
+};
+
+struct audio_driver pa_audio_driver = {
+ .name = "pa",
+ .descr = "http://www.pulseaudio.org/",
+ .options = qpa_options,
+ .init = qpa_audio_init,
+ .fini = qpa_audio_fini,
+ .pcm_ops = &qpa_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (PAVoiceOut),
+ .voice_size_in = sizeof (PAVoiceIn)
+};
diff --git a/audio/rate_template.h b/audio/rate_template.h
new file mode 100644
index 0000000..bd4b1c7
--- /dev/null
+++ b/audio/rate_template.h
@@ -0,0 +1,111 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
+ int *isamp, int *osamp)
+{
+ struct rate *rate = opaque;
+ struct st_sample *istart, *iend;
+ struct st_sample *ostart, *oend;
+ struct st_sample ilast, icur, out;
+#ifdef FLOAT_MIXENG
+ mixeng_real t;
+#else
+ int64_t t;
+#endif
+
+ ilast = rate->ilast;
+
+ istart = ibuf;
+ iend = ibuf + *isamp;
+
+ ostart = obuf;
+ oend = obuf + *osamp;
+
+ if (rate->opos_inc == (1ULL + UINT_MAX)) {
+ int i, n = *isamp > *osamp ? *osamp : *isamp;
+ for (i = 0; i < n; i++) {
+ OP (obuf[i].l, ibuf[i].l);
+ OP (obuf[i].r, ibuf[i].r);
+ }
+ *isamp = n;
+ *osamp = n;
+ return;
+ }
+
+ while (obuf < oend) {
+
+ /* Safety catch to make sure we have input samples. */
+ if (ibuf >= iend) {
+ break;
+ }
+
+ /* read as many input samples so that ipos > opos */
+
+ while (rate->ipos <= (rate->opos >> 32)) {
+ ilast = *ibuf++;
+ rate->ipos++;
+ /* See if we finished the input buffer yet */
+ if (ibuf >= iend) {
+ goto the_end;
+ }
+ }
+
+ icur = *ibuf;
+
+ /* interpolate */
+#ifdef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
+#else
+ t = (rate->opos & UINT_MAX) / (mixeng_real) UINT_MAX;
+#endif
+ out.l = (ilast.l * (1.0 - t)) + icur.l * t;
+ out.r = (ilast.r * (1.0 - t)) + icur.r * t;
+#else
+ t = rate->opos & 0xffffffff;
+ out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32;
+ out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32;
+#endif
+
+ /* output sample & increment position */
+ OP (obuf->l, out.l);
+ OP (obuf->r, out.r);
+ obuf += 1;
+ rate->opos += rate->opos_inc;
+ }
+
+the_end:
+ *isamp = ibuf - istart;
+ *osamp = obuf - ostart;
+ rate->ilast = ilast;
+}
+
+#undef NAME
+#undef OP
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
new file mode 100644
index 0000000..b74dcfa
--- /dev/null
+++ b/audio/sdlaudio.c
@@ -0,0 +1,459 @@
+/*
+ * QEMU SDL audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "qemu-common.h"
+#include "audio.h"
+
+#ifndef _WIN32
+#ifdef __sun__
+#define _POSIX_PTHREAD_SEMANTICS 1
+#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+#include <pthread.h>
+#endif
+#include <signal.h>
+#endif
+
+#define AUDIO_CAP "sdl"
+#include "audio_int.h"
+
+typedef struct SDLVoiceOut {
+ HWVoiceOut hw;
+ int live;
+ int rpos;
+ int decr;
+} SDLVoiceOut;
+
+static struct {
+ int nb_samples;
+} conf = {
+ .nb_samples = 1024
+};
+
+static struct SDLAudioState {
+ int exit;
+ SDL_mutex *mutex;
+ SDL_sem *sem;
+ int initialized;
+} glob_sdl;
+typedef struct SDLAudioState SDLAudioState;
+
+static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
+}
+
+static int sdl_lock (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_LockMutex (s->mutex)) {
+ sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_unlock (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_UnlockMutex (s->mutex)) {
+ sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_post (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_SemPost (s->sem)) {
+ sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_wait (SDLAudioState *s, const char *forfn)
+{
+ if (SDL_SemWait (s->sem)) {
+ sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
+ return -1;
+ }
+ return 0;
+}
+
+static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
+{
+ if (sdl_unlock (s, forfn)) {
+ return -1;
+ }
+
+ return sdl_post (s, forfn);
+}
+
+static int aud_to_sdlfmt (audfmt_e fmt)
+{
+ switch (fmt) {
+ case AUD_FMT_S8:
+ return AUDIO_S8;
+
+ case AUD_FMT_U8:
+ return AUDIO_U8;
+
+ case AUD_FMT_S16:
+ return AUDIO_S16LSB;
+
+ case AUD_FMT_U16:
+ return AUDIO_U16LSB;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+ abort ();
+#endif
+ return AUDIO_U8;
+ }
+}
+
+static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
+{
+ switch (sdlfmt) {
+ case AUDIO_S8:
+ *endianess = 0;
+ *fmt = AUD_FMT_S8;
+ break;
+
+ case AUDIO_U8:
+ *endianess = 0;
+ *fmt = AUD_FMT_U8;
+ break;
+
+ case AUDIO_S16LSB:
+ *endianess = 0;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AUDIO_U16LSB:
+ *endianess = 0;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ case AUDIO_S16MSB:
+ *endianess = 1;
+ *fmt = AUD_FMT_S16;
+ break;
+
+ case AUDIO_U16MSB:
+ *endianess = 1;
+ *fmt = AUD_FMT_U16;
+ break;
+
+ default:
+ dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
+{
+ int status;
+#ifndef _WIN32
+ int err;
+ sigset_t new, old;
+
+ /* Make sure potential threads created by SDL don't hog signals. */
+ err = sigfillset (&new);
+ if (err) {
+ dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
+ return -1;
+ }
+ err = pthread_sigmask (SIG_BLOCK, &new, &old);
+ if (err) {
+ dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
+ return -1;
+ }
+#endif
+
+ status = SDL_OpenAudio (req, obt);
+ if (status) {
+ sdl_logerr ("SDL_OpenAudio failed\n");
+ }
+
+#ifndef _WIN32
+ err = pthread_sigmask (SIG_SETMASK, &old, NULL);
+ if (err) {
+ dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
+ strerror (errno));
+ /* We have failed to restore original signal mask, all bets are off,
+ so exit the process */
+ exit (EXIT_FAILURE);
+ }
+#endif
+ return status;
+}
+
+static void sdl_close (SDLAudioState *s)
+{
+ if (s->initialized) {
+ sdl_lock (s, "sdl_close");
+ s->exit = 1;
+ sdl_unlock_and_post (s, "sdl_close");
+ SDL_PauseAudio (1);
+ SDL_CloseAudio ();
+ s->initialized = 0;
+ }
+}
+
+static void sdl_callback (void *opaque, Uint8 *buf, int len)
+{
+ SDLVoiceOut *sdl = opaque;
+ SDLAudioState *s = &glob_sdl;
+ HWVoiceOut *hw = &sdl->hw;
+ int samples = len >> hw->info.shift;
+
+ if (s->exit) {
+ return;
+ }
+
+ while (samples) {
+ int to_mix, decr;
+
+ /* dolog ("in callback samples=%d\n", samples); */
+ sdl_wait (s, "sdl_callback");
+ if (s->exit) {
+ return;
+ }
+
+ if (sdl_lock (s, "sdl_callback")) {
+ return;
+ }
+
+ if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
+ dolog ("sdl->live=%d hw->samples=%d\n",
+ sdl->live, hw->samples);
+ return;
+ }
+
+ if (!sdl->live) {
+ goto again;
+ }
+
+ /* dolog ("in callback live=%d\n", live); */
+ to_mix = audio_MIN (samples, sdl->live);
+ decr = to_mix;
+ while (to_mix) {
+ int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
+ struct st_sample *src = hw->mix_buf + hw->rpos;
+
+ /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
+ hw->clip (buf, src, chunk);
+ sdl->rpos = (sdl->rpos + chunk) % hw->samples;
+ to_mix -= chunk;
+ buf += chunk << hw->info.shift;
+ }
+ samples -= decr;
+ sdl->live -= decr;
+ sdl->decr += decr;
+
+ again:
+ if (sdl_unlock (s, "sdl_callback")) {
+ return;
+ }
+ }
+ /* dolog ("done len=%d\n", len); */
+}
+
+static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int sdl_run_out (HWVoiceOut *hw, int live)
+{
+ int decr;
+ SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+ SDLAudioState *s = &glob_sdl;
+
+ if (sdl_lock (s, "sdl_run_out")) {
+ return 0;
+ }
+
+ if (sdl->decr > live) {
+ ldebug ("sdl->decr %d live %d sdl->live %d\n",
+ sdl->decr,
+ live,
+ sdl->live);
+ }
+
+ decr = audio_MIN (sdl->decr, live);
+ sdl->decr -= decr;
+
+ sdl->live = live - decr;
+ hw->rpos = sdl->rpos;
+
+ if (sdl->live > 0) {
+ sdl_unlock_and_post (s, "sdl_run_out");
+ }
+ else {
+ sdl_unlock (s, "sdl_run_out");
+ }
+ return decr;
+}
+
+static void sdl_fini_out (HWVoiceOut *hw)
+{
+ (void) hw;
+
+ sdl_close (&glob_sdl);
+}
+
+static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+ SDLAudioState *s = &glob_sdl;
+ SDL_AudioSpec req, obt;
+ int endianess;
+ int err;
+ audfmt_e effective_fmt;
+ struct audsettings obt_as;
+
+ req.freq = as->freq;
+ req.format = aud_to_sdlfmt (as->fmt);
+ req.channels = as->nchannels;
+ req.samples = conf.nb_samples;
+ req.callback = sdl_callback;
+ req.userdata = sdl;
+
+ if (sdl_open (&req, &obt)) {
+ return -1;
+ }
+
+ err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
+ if (err) {
+ sdl_close (s);
+ return -1;
+ }
+
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.channels;
+ obt_as.fmt = effective_fmt;
+ obt_as.endianness = endianess;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = obt.samples;
+
+ s->initialized = 1;
+ s->exit = 0;
+ SDL_PauseAudio (0);
+ return 0;
+}
+
+static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ SDL_PauseAudio (0);
+ break;
+
+ case VOICE_DISABLE:
+ SDL_PauseAudio (1);
+ break;
+ }
+ return 0;
+}
+
+static void *sdl_audio_init (void)
+{
+ SDLAudioState *s = &glob_sdl;
+
+ if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
+ sdl_logerr ("SDL failed to initialize audio subsystem\n");
+ return NULL;
+ }
+
+ s->mutex = SDL_CreateMutex ();
+ if (!s->mutex) {
+ sdl_logerr ("Failed to create SDL mutex\n");
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return NULL;
+ }
+
+ s->sem = SDL_CreateSemaphore (0);
+ if (!s->sem) {
+ sdl_logerr ("Failed to create SDL semaphore\n");
+ SDL_DestroyMutex (s->mutex);
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return NULL;
+ }
+
+ return s;
+}
+
+static void sdl_audio_fini (void *opaque)
+{
+ SDLAudioState *s = opaque;
+ sdl_close (s);
+ SDL_DestroySemaphore (s->sem);
+ SDL_DestroyMutex (s->mutex);
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+}
+
+static struct audio_option sdl_options[] = {
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nb_samples,
+ .descr = "Size of SDL buffer in samples"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops sdl_pcm_ops = {
+ .init_out = sdl_init_out,
+ .fini_out = sdl_fini_out,
+ .run_out = sdl_run_out,
+ .write = sdl_write_out,
+ .ctl_out = sdl_ctl_out,
+};
+
+struct audio_driver sdl_audio_driver = {
+ .name = "sdl",
+ .descr = "SDL http://www.libsdl.org",
+ .options = sdl_options,
+ .init = sdl_audio_init,
+ .fini = sdl_audio_fini,
+ .pcm_ops = &sdl_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = 1,
+ .max_voices_in = 0,
+ .voice_size_out = sizeof (SDLVoiceOut),
+ .voice_size_in = 0
+};
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
new file mode 100644
index 0000000..a5c0d6b
--- /dev/null
+++ b/audio/spiceaudio.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * maintained by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "ui/qemu-spice.h"
+
+#define AUDIO_CAP "spice"
+#include "audio.h"
+#include "audio_int.h"
+
+#define LINE_IN_SAMPLES 1024
+#define LINE_OUT_SAMPLES 1024
+
+typedef struct SpiceRateCtl {
+ int64_t start_ticks;
+ int64_t bytes_sent;
+} SpiceRateCtl;
+
+typedef struct SpiceVoiceOut {
+ HWVoiceOut hw;
+ SpicePlaybackInstance sin;
+ SpiceRateCtl rate;
+ int active;
+ uint32_t *frame;
+ uint32_t *fpos;
+ uint32_t fsize;
+} SpiceVoiceOut;
+
+typedef struct SpiceVoiceIn {
+ HWVoiceIn hw;
+ SpiceRecordInstance sin;
+ SpiceRateCtl rate;
+ int active;
+ uint32_t samples[LINE_IN_SAMPLES];
+} SpiceVoiceIn;
+
+static const SpicePlaybackInterface playback_sif = {
+ .base.type = SPICE_INTERFACE_PLAYBACK,
+ .base.description = "playback",
+ .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
+};
+
+static const SpiceRecordInterface record_sif = {
+ .base.type = SPICE_INTERFACE_RECORD,
+ .base.description = "record",
+ .base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
+};
+
+static void *spice_audio_init (void)
+{
+ if (!using_spice) {
+ return NULL;
+ }
+ return &spice_audio_init;
+}
+
+static void spice_audio_fini (void *opaque)
+{
+ /* nothing */
+}
+
+static void rate_start (SpiceRateCtl *rate)
+{
+ memset (rate, 0, sizeof (*rate));
+ rate->start_ticks = qemu_get_clock (vm_clock);
+}
+
+static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
+{
+ int64_t now;
+ int64_t ticks;
+ int64_t bytes;
+ int64_t samples;
+
+ now = qemu_get_clock (vm_clock);
+ ticks = now - rate->start_ticks;
+ bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
+ samples = (bytes - rate->bytes_sent) >> info->shift;
+ if (samples < 0 || samples > 65536) {
+ fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
+ rate_start (rate);
+ samples = 0;
+ }
+ rate->bytes_sent += samples << info->shift;
+ return samples;
+}
+
+/* playback */
+
+static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
+{
+ SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
+ struct audsettings settings;
+
+ settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
+ settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
+ settings.fmt = AUD_FMT_S16;
+ settings.endianness = AUDIO_HOST_ENDIANNESS;
+
+ audio_pcm_init_info (&hw->info, &settings);
+ hw->samples = LINE_OUT_SAMPLES;
+ out->active = 0;
+
+ out->sin.base.sif = &playback_sif.base;
+ qemu_spice_add_interface (&out->sin.base);
+ return 0;
+}
+
+static void line_out_fini (HWVoiceOut *hw)
+{
+ SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
+
+ spice_server_remove_interface (&out->sin.base);
+}
+
+static int line_out_run (HWVoiceOut *hw, int live)
+{
+ SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
+ int rpos, decr;
+ int samples;
+
+ if (!live) {
+ return 0;
+ }
+
+ decr = rate_get_samples (&hw->info, &out->rate);
+ decr = audio_MIN (live, decr);
+
+ samples = decr;
+ rpos = hw->rpos;
+ while (samples) {
+ int left_till_end_samples = hw->samples - rpos;
+ int len = audio_MIN (samples, left_till_end_samples);
+
+ if (!out->frame) {
+ spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
+ out->fpos = out->frame;
+ }
+ if (out->frame) {
+ len = audio_MIN (len, out->fsize);
+ hw->clip (out->fpos, hw->mix_buf + rpos, len);
+ out->fsize -= len;
+ out->fpos += len;
+ if (out->fsize == 0) {
+ spice_server_playback_put_samples (&out->sin, out->frame);
+ out->frame = out->fpos = NULL;
+ }
+ }
+ rpos = (rpos + len) % hw->samples;
+ samples -= len;
+ }
+ hw->rpos = rpos;
+ return decr;
+}
+
+static int line_out_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
+{
+ SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ if (out->active) {
+ break;
+ }
+ out->active = 1;
+ rate_start (&out->rate);
+ spice_server_playback_start (&out->sin);
+ break;
+ case VOICE_DISABLE:
+ if (!out->active) {
+ break;
+ }
+ out->active = 0;
+ if (out->frame) {
+ memset (out->fpos, 0, out->fsize << 2);
+ spice_server_playback_put_samples (&out->sin, out->frame);
+ out->frame = out->fpos = NULL;
+ }
+ spice_server_playback_stop (&out->sin);
+ break;
+ }
+ return 0;
+}
+
+/* record */
+
+static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
+{
+ SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
+ struct audsettings settings;
+
+ settings.freq = SPICE_INTERFACE_RECORD_FREQ;
+ settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
+ settings.fmt = AUD_FMT_S16;
+ settings.endianness = AUDIO_HOST_ENDIANNESS;
+
+ audio_pcm_init_info (&hw->info, &settings);
+ hw->samples = LINE_IN_SAMPLES;
+ in->active = 0;
+
+ in->sin.base.sif = &record_sif.base;
+ qemu_spice_add_interface (&in->sin.base);
+ return 0;
+}
+
+static void line_in_fini (HWVoiceIn *hw)
+{
+ SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
+
+ spice_server_remove_interface (&in->sin.base);
+}
+
+static int line_in_run (HWVoiceIn *hw)
+{
+ SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
+ int num_samples;
+ int ready;
+ int len[2];
+ uint64_t delta_samp;
+ const uint32_t *samples;
+
+ if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
+ return 0;
+ }
+
+ delta_samp = rate_get_samples (&hw->info, &in->rate);
+ num_samples = audio_MIN (num_samples, delta_samp);
+
+ ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
+ samples = in->samples;
+ if (ready == 0) {
+ static const uint32_t silence[LINE_IN_SAMPLES];
+ samples = silence;
+ ready = LINE_IN_SAMPLES;
+ }
+
+ num_samples = audio_MIN (ready, num_samples);
+
+ if (hw->wpos + num_samples > hw->samples) {
+ len[0] = hw->samples - hw->wpos;
+ len[1] = num_samples - len[0];
+ } else {
+ len[0] = num_samples;
+ len[1] = 0;
+ }
+
+ hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
+
+ if (len[1]) {
+ hw->conv (hw->conv_buf, samples + len[0], len[1]);
+ }
+
+ hw->wpos = (hw->wpos + num_samples) % hw->samples;
+
+ return num_samples;
+}
+
+static int line_in_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
+{
+ SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ if (in->active) {
+ break;
+ }
+ in->active = 1;
+ rate_start (&in->rate);
+ spice_server_record_start (&in->sin);
+ break;
+ case VOICE_DISABLE:
+ if (!in->active) {
+ break;
+ }
+ in->active = 0;
+ spice_server_record_stop (&in->sin);
+ break;
+ }
+ return 0;
+}
+
+static struct audio_option audio_options[] = {
+ { /* end of list */ },
+};
+
+static struct audio_pcm_ops audio_callbacks = {
+ .init_out = line_out_init,
+ .fini_out = line_out_fini,
+ .run_out = line_out_run,
+ .write = line_out_write,
+ .ctl_out = line_out_ctl,
+
+ .init_in = line_in_init,
+ .fini_in = line_in_fini,
+ .run_in = line_in_run,
+ .read = line_in_read,
+ .ctl_in = line_in_ctl,
+};
+
+struct audio_driver spice_audio_driver = {
+ .name = "spice",
+ .descr = "spice audio driver",
+ .options = audio_options,
+ .init = spice_audio_init,
+ .fini = spice_audio_fini,
+ .pcm_ops = &audio_callbacks,
+ .max_voices_out = 1,
+ .max_voices_in = 1,
+ .voice_size_out = sizeof (SpiceVoiceOut),
+ .voice_size_in = sizeof (SpiceVoiceIn),
+};
+
+void qemu_spice_audio_init (void)
+{
+ spice_audio_driver.can_be_default = 1;
+}
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
new file mode 100644
index 0000000..c522be4
--- /dev/null
+++ b/audio/wavaudio.c
@@ -0,0 +1,262 @@
+/*
+ * QEMU WAV audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "audio.h"
+
+#define AUDIO_CAP "wav"
+#include "audio_int.h"
+
+typedef struct WAVVoiceOut {
+ HWVoiceOut hw;
+ QEMUFile *f;
+ int64_t old_ticks;
+ void *pcm_buf;
+ int total_samples;
+} WAVVoiceOut;
+
+static struct {
+ struct audsettings settings;
+ const char *wav_path;
+} conf = {
+ .settings.freq = 44100,
+ .settings.nchannels = 2,
+ .settings.fmt = AUD_FMT_S16,
+ .wav_path = "qemu.wav"
+};
+
+static int wav_run_out (HWVoiceOut *hw, int live)
+{
+ WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+ int rpos, decr, samples;
+ uint8_t *dst;
+ struct st_sample *src;
+ int64_t now = qemu_get_clock (vm_clock);
+ int64_t ticks = now - wav->old_ticks;
+ int64_t bytes =
+ muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
+
+ if (bytes > INT_MAX) {
+ samples = INT_MAX >> hw->info.shift;
+ }
+ else {
+ samples = bytes >> hw->info.shift;
+ }
+
+ wav->old_ticks = now;
+ decr = audio_MIN (live, samples);
+ samples = decr;
+ rpos = hw->rpos;
+ while (samples) {
+ int left_till_end_samples = hw->samples - rpos;
+ int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+ src = hw->mix_buf + rpos;
+ dst = advance (wav->pcm_buf, rpos << hw->info.shift);
+
+ hw->clip (dst, src, convert_samples);
+ qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
+
+ rpos = (rpos + convert_samples) % hw->samples;
+ samples -= convert_samples;
+ wav->total_samples += convert_samples;
+ }
+
+ hw->rpos = rpos;
+ return decr;
+}
+
+static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t) (val & 0xff);
+ val >>= 8;
+ }
+}
+
+static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+ int bits16 = 0, stereo = 0;
+ uint8_t hdr[] = {
+ 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+ 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+ 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+ };
+ struct audsettings wav_as = conf.settings;
+
+ (void) as;
+
+ stereo = wav_as.nchannels == 2;
+ switch (wav_as.fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ bits16 = 0;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ bits16 = 1;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("WAVE files can not handle 32bit formats\n");
+ return -1;
+ }
+
+ hdr[34] = bits16 ? 0x10 : 0x08;
+
+ wav_as.endianness = 0;
+ audio_pcm_init_info (&hw->info, &wav_as);
+
+ hw->samples = 1024;
+ wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!wav->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ return -1;
+ }
+
+ le_store (hdr + 22, hw->info.nchannels, 2);
+ le_store (hdr + 24, hw->info.freq, 4);
+ le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
+ le_store (hdr + 32, 1 << (bits16 + stereo), 2);
+
+ wav->f = qemu_fopen (conf.wav_path, "wb");
+ if (!wav->f) {
+ dolog ("Failed to open wave file `%s'\nReason: %s\n",
+ conf.wav_path, strerror (errno));
+ qemu_free (wav->pcm_buf);
+ wav->pcm_buf = NULL;
+ return -1;
+ }
+
+ qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+ return 0;
+}
+
+static void wav_fini_out (HWVoiceOut *hw)
+{
+ WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+ uint8_t rlen[4];
+ uint8_t dlen[4];
+ uint32_t datalen = wav->total_samples << hw->info.shift;
+ uint32_t rifflen = datalen + 36;
+
+ if (!wav->f) {
+ return;
+ }
+
+ le_store (rlen, rifflen, 4);
+ le_store (dlen, datalen, 4);
+
+ qemu_fseek (wav->f, 4, SEEK_SET);
+ qemu_put_buffer (wav->f, rlen, 4);
+
+ qemu_fseek (wav->f, 32, SEEK_CUR);
+ qemu_put_buffer (wav->f, dlen, 4);
+
+ qemu_fclose (wav->f);
+ wav->f = NULL;
+
+ qemu_free (wav->pcm_buf);
+ wav->pcm_buf = NULL;
+}
+
+static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+static void *wav_audio_init (void)
+{
+ return &conf;
+}
+
+static void wav_audio_fini (void *opaque)
+{
+ (void) opaque;
+ ldebug ("wav_fini");
+}
+
+static struct audio_option wav_options[] = {
+ {
+ .name = "FREQUENCY",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.settings.freq,
+ .descr = "Frequency"
+ },
+ {
+ .name = "FORMAT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.settings.fmt,
+ .descr = "Format"
+ },
+ {
+ .name = "DAC_FIXED_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.settings.nchannels,
+ .descr = "Number of channels (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "PATH",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.wav_path,
+ .descr = "Path to wave file"
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops wav_pcm_ops = {
+ .init_out = wav_init_out,
+ .fini_out = wav_fini_out,
+ .run_out = wav_run_out,
+ .write = wav_write_out,
+ .ctl_out = wav_ctl_out,
+};
+
+struct audio_driver wav_audio_driver = {
+ .name = "wav",
+ .descr = "WAV renderer http://wikipedia.org/wiki/WAV",
+ .options = wav_options,
+ .init = wav_audio_init,
+ .fini = wav_audio_fini,
+ .pcm_ops = &wav_pcm_ops,
+ .can_be_default = 0,
+ .max_voices_out = 1,
+ .max_voices_in = 0,
+ .voice_size_out = sizeof (WAVVoiceOut),
+ .voice_size_in = 0
+};
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
new file mode 100644
index 0000000..1f49cd1
--- /dev/null
+++ b/audio/wavcapture.c
@@ -0,0 +1,161 @@
+#include "hw/hw.h"
+#include "monitor.h"
+#include "audio.h"
+
+typedef struct {
+ QEMUFile *f;
+ int bytes;
+ char *path;
+ int freq;
+ int bits;
+ int nchannels;
+ CaptureVoiceOut *cap;
+} WAVState;
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t) (val & 0xff);
+ val >>= 8;
+ }
+}
+
+static void wav_notify (void *opaque, audcnotification_e cmd)
+{
+ (void) opaque;
+ (void) cmd;
+}
+
+static void wav_destroy (void *opaque)
+{
+ WAVState *wav = opaque;
+ uint8_t rlen[4];
+ uint8_t dlen[4];
+ uint32_t datalen = wav->bytes;
+ uint32_t rifflen = datalen + 36;
+
+ if (wav->f) {
+ le_store (rlen, rifflen, 4);
+ le_store (dlen, datalen, 4);
+
+ qemu_fseek (wav->f, 4, SEEK_SET);
+ qemu_put_buffer (wav->f, rlen, 4);
+
+ qemu_fseek (wav->f, 32, SEEK_CUR);
+ qemu_put_buffer (wav->f, dlen, 4);
+ qemu_fclose (wav->f);
+ }
+
+ qemu_free (wav->path);
+}
+
+static void wav_capture (void *opaque, void *buf, int size)
+{
+ WAVState *wav = opaque;
+
+ qemu_put_buffer (wav->f, buf, size);
+ wav->bytes += size;
+}
+
+static void wav_capture_destroy (void *opaque)
+{
+ WAVState *wav = opaque;
+
+ AUD_del_capture (wav->cap, wav);
+}
+
+static void wav_capture_info (void *opaque)
+{
+ WAVState *wav = opaque;
+ char *path = wav->path;
+
+ monitor_printf(cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n",
+ wav->freq, wav->bits, wav->nchannels,
+ path ? path : "<not available>", wav->bytes);
+}
+
+static struct capture_ops wav_capture_ops = {
+ .destroy = wav_capture_destroy,
+ .info = wav_capture_info
+};
+
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+ int bits, int nchannels)
+{
+ Monitor *mon = cur_mon;
+ WAVState *wav;
+ uint8_t hdr[] = {
+ 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+ 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+ 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+ };
+ struct audsettings as;
+ struct audio_capture_ops ops;
+ int stereo, bits16, shift;
+ CaptureVoiceOut *cap;
+
+ if (bits != 8 && bits != 16) {
+ monitor_printf(mon, "incorrect bit count %d, must be 8 or 16\n", bits);
+ return -1;
+ }
+
+ if (nchannels != 1 && nchannels != 2) {
+ monitor_printf(mon, "incorrect channel count %d, must be 1 or 2\n",
+ nchannels);
+ return -1;
+ }
+
+ stereo = nchannels == 2;
+ bits16 = bits == 16;
+
+ as.freq = freq;
+ as.nchannels = 1 << stereo;
+ as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+ as.endianness = 0;
+
+ ops.notify = wav_notify;
+ ops.capture = wav_capture;
+ ops.destroy = wav_destroy;
+
+ wav = qemu_mallocz (sizeof (*wav));
+
+ shift = bits16 + stereo;
+ hdr[34] = bits16 ? 0x10 : 0x08;
+
+ le_store (hdr + 22, as.nchannels, 2);
+ le_store (hdr + 24, freq, 4);
+ le_store (hdr + 28, freq << shift, 4);
+ le_store (hdr + 32, 1 << shift, 2);
+
+ wav->f = qemu_fopen (path, "wb");
+ if (!wav->f) {
+ monitor_printf(mon, "Failed to open wave file `%s'\nReason: %s\n",
+ path, strerror (errno));
+ qemu_free (wav);
+ return -1;
+ }
+
+ wav->path = qemu_strdup (path);
+ wav->bits = bits;
+ wav->nchannels = nchannels;
+ wav->freq = freq;
+
+ qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+
+ cap = AUD_add_capture (&as, &ops, wav);
+ if (!cap) {
+ monitor_printf(mon, "Failed to add audio capture\n");
+ qemu_free (wav->path);
+ qemu_fclose (wav->f);
+ qemu_free (wav);
+ return -1;
+ }
+
+ wav->cap = cap;
+ s->opaque = wav;
+ s->ops = wav_capture_ops;
+ return 0;
+}
diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c
new file mode 100644
index 0000000..e5ad3c6
--- /dev/null
+++ b/audio/winwaveaudio.c
@@ -0,0 +1,723 @@
+/* public domain */
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "audio.h"
+
+#define AUDIO_CAP "winwave"
+#include "audio_int.h"
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "audio_win_int.h"
+
+static struct {
+ int dac_headers;
+ int dac_samples;
+ int adc_headers;
+ int adc_samples;
+} conf = {
+ .dac_headers = 4,
+ .dac_samples = 1024,
+ .adc_headers = 4,
+ .adc_samples = 1024
+};
+
+typedef struct {
+ HWVoiceOut hw;
+ HWAVEOUT hwo;
+ WAVEHDR *hdrs;
+ HANDLE event;
+ void *pcm_buf;
+ int avail;
+ int pending;
+ int curhdr;
+ int paused;
+ CRITICAL_SECTION crit_sect;
+} WaveVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ HWAVEIN hwi;
+ WAVEHDR *hdrs;
+ HANDLE event;
+ void *pcm_buf;
+ int curhdr;
+ int paused;
+ int rpos;
+ int avail;
+ CRITICAL_SECTION crit_sect;
+} WaveVoiceIn;
+
+static void winwave_log_mmresult (MMRESULT mr)
+{
+ const char *str = "BUG";
+
+ switch (mr) {
+ case MMSYSERR_NOERROR:
+ str = "Success";
+ break;
+
+ case MMSYSERR_INVALHANDLE:
+ str = "Specified device handle is invalid";
+ break;
+
+ case MMSYSERR_BADDEVICEID:
+ str = "Specified device id is out of range";
+ break;
+
+ case MMSYSERR_NODRIVER:
+ str = "No device driver is present";
+ break;
+
+ case MMSYSERR_NOMEM:
+ str = "Unable to allocate or locl memory";
+ break;
+
+ case WAVERR_SYNC:
+ str = "Device is synchronous but waveOutOpen was called "
+ "without using the WINWAVE_ALLOWSYNC flag";
+ break;
+
+ case WAVERR_UNPREPARED:
+ str = "The data block pointed to by the pwh parameter "
+ "hasn't been prepared";
+ break;
+
+ case WAVERR_STILLPLAYING:
+ str = "There are still buffers in the queue";
+ break;
+
+ default:
+ dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
+ return;
+ }
+
+ dolog ("Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) winwave_logerr (
+ MMRESULT mr,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (NULL, " failed\n");
+ winwave_log_mmresult (mr);
+}
+
+static void winwave_anal_close_out (WaveVoiceOut *wave)
+{
+ MMRESULT mr;
+
+ mr = waveOutClose (wave->hwo);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutClose");
+ }
+ wave->hwo = NULL;
+}
+
+static void CALLBACK winwave_callback_out (
+ HWAVEOUT hwo,
+ UINT msg,
+ DWORD_PTR dwInstance,
+ DWORD_PTR dwParam1,
+ DWORD_PTR dwParam2
+ )
+{
+ WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
+
+ switch (msg) {
+ case WOM_DONE:
+ {
+ WAVEHDR *h = (WAVEHDR *) dwParam1;
+ if (!h->dwUser) {
+ h->dwUser = 1;
+ EnterCriticalSection (&wave->crit_sect);
+ {
+ wave->avail += conf.dac_samples;
+ }
+ LeaveCriticalSection (&wave->crit_sect);
+ if (wave->hw.poll_mode) {
+ if (!SetEvent (wave->event)) {
+ dolog ("DAC SetEvent failed %lx\n", GetLastError ());
+ }
+ }
+ }
+ }
+ break;
+
+ case WOM_CLOSE:
+ case WOM_OPEN:
+ break;
+
+ default:
+ dolog ("unknown wave out callback msg %x\n", msg);
+ }
+}
+
+static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
+{
+ int i;
+ int err;
+ MMRESULT mr;
+ WAVEFORMATEX wfx;
+ WaveVoiceOut *wave;
+
+ wave = (WaveVoiceOut *) hw;
+
+ InitializeCriticalSection (&wave->crit_sect);
+
+ err = waveformat_from_audio_settings (&wfx, as);
+ if (err) {
+ goto err0;
+ }
+
+ mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
+ (DWORD_PTR) winwave_callback_out,
+ (DWORD_PTR) wave, CALLBACK_FUNCTION);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutOpen");
+ goto err1;
+ }
+
+ wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
+ sizeof (*wave->hdrs));
+ if (!wave->hdrs) {
+ goto err2;
+ }
+
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = conf.dac_samples * conf.dac_headers;
+ wave->avail = hw->samples;
+
+ wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
+ conf.dac_headers << hw->info.shift);
+ if (!wave->pcm_buf) {
+ goto err3;
+ }
+
+ for (i = 0; i < conf.dac_headers; ++i) {
+ WAVEHDR *h = &wave->hdrs[i];
+
+ h->dwUser = 0;
+ h->dwBufferLength = conf.dac_samples << hw->info.shift;
+ h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
+ h->dwFlags = 0;
+
+ mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
+ goto err4;
+ }
+ }
+
+ return 0;
+
+ err4:
+ qemu_free (wave->pcm_buf);
+ err3:
+ qemu_free (wave->hdrs);
+ err2:
+ winwave_anal_close_out (wave);
+ err1:
+ err0:
+ return -1;
+}
+
+static int winwave_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int winwave_run_out (HWVoiceOut *hw, int live)
+{
+ WaveVoiceOut *wave = (WaveVoiceOut *) hw;
+ int decr;
+ int doreset;
+
+ EnterCriticalSection (&wave->crit_sect);
+ {
+ decr = audio_MIN (live, wave->avail);
+ decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
+ wave->pending += decr;
+ wave->avail -= decr;
+ }
+ LeaveCriticalSection (&wave->crit_sect);
+
+ doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
+ if (doreset && !ResetEvent (wave->event)) {
+ dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
+ }
+
+ while (wave->pending >= conf.dac_samples) {
+ MMRESULT mr;
+ WAVEHDR *h = &wave->hdrs[wave->curhdr];
+
+ h->dwUser = 0;
+ mr = waveOutWrite (wave->hwo, h, sizeof (*h));
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
+ break;
+ }
+
+ wave->pending -= conf.dac_samples;
+ wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
+ }
+
+ return decr;
+}
+
+static void winwave_poll (void *opaque)
+{
+ (void) opaque;
+ audio_run ("winwave_poll");
+}
+
+static void winwave_fini_out (HWVoiceOut *hw)
+{
+ int i;
+ MMRESULT mr;
+ WaveVoiceOut *wave = (WaveVoiceOut *) hw;
+
+ mr = waveOutReset (wave->hwo);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutReset");
+ }
+
+ for (i = 0; i < conf.dac_headers; ++i) {
+ mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
+ sizeof (wave->hdrs[i]));
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
+ }
+ }
+
+ winwave_anal_close_out (wave);
+
+ if (wave->event) {
+ qemu_del_wait_object (wave->event, winwave_poll, wave);
+ if (!CloseHandle (wave->event)) {
+ dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
+ }
+ wave->event = NULL;
+ }
+
+ qemu_free (wave->pcm_buf);
+ wave->pcm_buf = NULL;
+
+ qemu_free (wave->hdrs);
+ wave->hdrs = NULL;
+}
+
+static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ MMRESULT mr;
+ WaveVoiceOut *wave = (WaveVoiceOut *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ if (poll_mode && !wave->event) {
+ wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
+ if (!wave->event) {
+ dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
+ GetLastError ());
+ }
+ }
+
+ if (wave->event) {
+ int ret;
+
+ ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
+ hw->poll_mode = (ret == 0);
+ }
+ else {
+ hw->poll_mode = 0;
+ }
+ if (wave->paused) {
+ mr = waveOutRestart (wave->hwo);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutRestart");
+ }
+ wave->paused = 0;
+ }
+ }
+ return 0;
+
+ case VOICE_DISABLE:
+ if (!wave->paused) {
+ mr = waveOutPause (wave->hwo);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveOutPause");
+ }
+ else {
+ wave->paused = 1;
+ }
+ }
+ if (wave->event) {
+ qemu_del_wait_object (wave->event, winwave_poll, wave);
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static void winwave_anal_close_in (WaveVoiceIn *wave)
+{
+ MMRESULT mr;
+
+ mr = waveInClose (wave->hwi);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInClose");
+ }
+ wave->hwi = NULL;
+}
+
+static void CALLBACK winwave_callback_in (
+ HWAVEIN *hwi,
+ UINT msg,
+ DWORD_PTR dwInstance,
+ DWORD_PTR dwParam1,
+ DWORD_PTR dwParam2
+ )
+{
+ WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
+
+ switch (msg) {
+ case WIM_DATA:
+ {
+ WAVEHDR *h = (WAVEHDR *) dwParam1;
+ if (!h->dwUser) {
+ h->dwUser = 1;
+ EnterCriticalSection (&wave->crit_sect);
+ {
+ wave->avail += conf.adc_samples;
+ }
+ LeaveCriticalSection (&wave->crit_sect);
+ if (wave->hw.poll_mode) {
+ if (!SetEvent (wave->event)) {
+ dolog ("ADC SetEvent failed %lx\n", GetLastError ());
+ }
+ }
+ }
+ }
+ break;
+
+ case WIM_CLOSE:
+ case WIM_OPEN:
+ break;
+
+ default:
+ dolog ("unknown wave in callback msg %x\n", msg);
+ }
+}
+
+static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
+{
+ int doreset;
+
+ doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
+ if (doreset && !ResetEvent (wave->event)) {
+ dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
+ }
+
+ while (samples >= conf.adc_samples) {
+ MMRESULT mr;
+ WAVEHDR *h = &wave->hdrs[wave->curhdr];
+
+ h->dwUser = 0;
+ mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
+ }
+ wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
+ samples -= conf.adc_samples;
+ }
+}
+
+static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+ int i;
+ int err;
+ MMRESULT mr;
+ WAVEFORMATEX wfx;
+ WaveVoiceIn *wave;
+
+ wave = (WaveVoiceIn *) hw;
+
+ InitializeCriticalSection (&wave->crit_sect);
+
+ err = waveformat_from_audio_settings (&wfx, as);
+ if (err) {
+ goto err0;
+ }
+
+ mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
+ (DWORD_PTR) winwave_callback_in,
+ (DWORD_PTR) wave, CALLBACK_FUNCTION);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInOpen");
+ goto err1;
+ }
+
+ wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
+ sizeof (*wave->hdrs));
+ if (!wave->hdrs) {
+ goto err2;
+ }
+
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = conf.adc_samples * conf.adc_headers;
+ wave->avail = 0;
+
+ wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
+ conf.adc_headers << hw->info.shift);
+ if (!wave->pcm_buf) {
+ goto err3;
+ }
+
+ for (i = 0; i < conf.adc_headers; ++i) {
+ WAVEHDR *h = &wave->hdrs[i];
+
+ h->dwUser = 0;
+ h->dwBufferLength = conf.adc_samples << hw->info.shift;
+ h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
+ h->dwFlags = 0;
+
+ mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
+ goto err4;
+ }
+ }
+
+ wave->paused = 1;
+ winwave_add_buffers (wave, hw->samples);
+ return 0;
+
+ err4:
+ qemu_free (wave->pcm_buf);
+ err3:
+ qemu_free (wave->hdrs);
+ err2:
+ winwave_anal_close_in (wave);
+ err1:
+ err0:
+ return -1;
+}
+
+static void winwave_fini_in (HWVoiceIn *hw)
+{
+ int i;
+ MMRESULT mr;
+ WaveVoiceIn *wave = (WaveVoiceIn *) hw;
+
+ mr = waveInReset (wave->hwi);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInReset");
+ }
+
+ for (i = 0; i < conf.adc_headers; ++i) {
+ mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
+ sizeof (wave->hdrs[i]));
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
+ }
+ }
+
+ winwave_anal_close_in (wave);
+
+ if (wave->event) {
+ qemu_del_wait_object (wave->event, winwave_poll, wave);
+ if (!CloseHandle (wave->event)) {
+ dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
+ }
+ wave->event = NULL;
+ }
+
+ qemu_free (wave->pcm_buf);
+ wave->pcm_buf = NULL;
+
+ qemu_free (wave->hdrs);
+ wave->hdrs = NULL;
+}
+
+static int winwave_run_in (HWVoiceIn *hw)
+{
+ WaveVoiceIn *wave = (WaveVoiceIn *) hw;
+ int live = audio_pcm_hw_get_live_in (hw);
+ int dead = hw->samples - live;
+ int decr, ret;
+
+ if (!dead) {
+ return 0;
+ }
+
+ EnterCriticalSection (&wave->crit_sect);
+ {
+ decr = audio_MIN (dead, wave->avail);
+ wave->avail -= decr;
+ }
+ LeaveCriticalSection (&wave->crit_sect);
+
+ ret = decr;
+ while (decr) {
+ int left = hw->samples - hw->wpos;
+ int conv = audio_MIN (left, decr);
+ hw->conv (hw->conv_buf + hw->wpos,
+ advance (wave->pcm_buf, wave->rpos << hw->info.shift),
+ conv);
+
+ wave->rpos = (wave->rpos + conv) % hw->samples;
+ hw->wpos = (hw->wpos + conv) % hw->samples;
+ decr -= conv;
+ }
+
+ winwave_add_buffers (wave, ret);
+ return ret;
+}
+
+static int winwave_read (SWVoiceIn *sw, void *buf, int size)
+{
+ return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ MMRESULT mr;
+ WaveVoiceIn *wave = (WaveVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ if (poll_mode && !wave->event) {
+ wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
+ if (!wave->event) {
+ dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
+ GetLastError ());
+ }
+ }
+
+ if (wave->event) {
+ int ret;
+
+ ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
+ hw->poll_mode = (ret == 0);
+ }
+ else {
+ hw->poll_mode = 0;
+ }
+ if (wave->paused) {
+ mr = waveInStart (wave->hwi);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInStart");
+ }
+ wave->paused = 0;
+ }
+ }
+ return 0;
+
+ case VOICE_DISABLE:
+ if (!wave->paused) {
+ mr = waveInStop (wave->hwi);
+ if (mr != MMSYSERR_NOERROR) {
+ winwave_logerr (mr, "waveInStop");
+ }
+ else {
+ wave->paused = 1;
+ }
+ }
+ if (wave->event) {
+ qemu_del_wait_object (wave->event, winwave_poll, wave);
+ }
+ return 0;
+ }
+ return 0;
+}
+
+static void *winwave_audio_init (void)
+{
+ return &conf;
+}
+
+static void winwave_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option winwave_options[] = {
+ {
+ .name = "DAC_HEADERS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.dac_headers,
+ .descr = "DAC number of headers",
+ },
+ {
+ .name = "DAC_SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.dac_samples,
+ .descr = "DAC number of samples per header",
+ },
+ {
+ .name = "ADC_HEADERS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.adc_headers,
+ .descr = "ADC number of headers",
+ },
+ {
+ .name = "ADC_SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.adc_samples,
+ .descr = "ADC number of samples per header",
+ },
+ { /* End of list */ }
+};
+
+static struct audio_pcm_ops winwave_pcm_ops = {
+ .init_out = winwave_init_out,
+ .fini_out = winwave_fini_out,
+ .run_out = winwave_run_out,
+ .write = winwave_write,
+ .ctl_out = winwave_ctl_out,
+ .init_in = winwave_init_in,
+ .fini_in = winwave_fini_in,
+ .run_in = winwave_run_in,
+ .read = winwave_read,
+ .ctl_in = winwave_ctl_in
+};
+
+struct audio_driver winwave_audio_driver = {
+ .name = "winwave",
+ .descr = "Windows Waveform Audio http://msdn.microsoft.com",
+ .options = winwave_options,
+ .init = winwave_audio_init,
+ .fini = winwave_audio_fini,
+ .pcm_ops = &winwave_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (WaveVoiceOut),
+ .voice_size_in = sizeof (WaveVoiceIn)
+};
diff --git a/balloon.c b/balloon.c
new file mode 100644
index 0000000..0021fef
--- /dev/null
+++ b/balloon.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysemu.h"
+#include "monitor.h"
+#include "qjson.h"
+#include "qint.h"
+#include "cpu-common.h"
+#include "kvm.h"
+#include "balloon.h"
+#include "trace.h"
+
+
+static QEMUBalloonEvent *qemu_balloon_event;
+void *qemu_balloon_event_opaque;
+
+void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque)
+{
+ qemu_balloon_event = func;
+ qemu_balloon_event_opaque = opaque;
+}
+
+int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque)
+{
+ if (qemu_balloon_event) {
+ trace_balloon_event(qemu_balloon_event_opaque, target);
+ qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int qemu_balloon_status(MonitorCompletion cb, void *opaque)
+{
+ if (qemu_balloon_event) {
+ qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void print_balloon_stat(const char *key, QObject *obj, void *opaque)
+{
+ Monitor *mon = opaque;
+
+ if (strcmp(key, "actual"))
+ monitor_printf(mon, ",%s=%" PRId64, key,
+ qint_get_int(qobject_to_qint(obj)));
+}
+
+void monitor_print_balloon(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+
+ qdict = qobject_to_qdict(data);
+ if (!qdict_haskey(qdict, "actual"))
+ return;
+
+ monitor_printf(mon, "balloon: actual=%" PRId64,
+ qdict_get_int(qdict, "actual") >> 20);
+ qdict_iter(qdict, print_balloon_stat, mon);
+ monitor_printf(mon, "\n");
+}
+
+/**
+ * do_info_balloon(): Balloon information
+ *
+ * Make an asynchronous request for balloon info. When the request completes
+ * a QDict will be returned according to the following specification:
+ *
+ * - "actual": current balloon value in bytes
+ * The following fields may or may not be present:
+ * - "mem_swapped_in": Amount of memory swapped in (bytes)
+ * - "mem_swapped_out": Amount of memory swapped out (bytes)
+ * - "major_page_faults": Number of major faults
+ * - "minor_page_faults": Number of minor faults
+ * - "free_mem": Total amount of free and unused memory (bytes)
+ * - "total_mem": Total amount of available memory (bytes)
+ *
+ * Example:
+ *
+ * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0,
+ * "major_page_faults": 142, "minor_page_faults": 239245,
+ * "free_mem": 1014185984, "total_mem": 1044668416 }
+ */
+int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque)
+{
+ int ret;
+
+ if (kvm_enabled() && !kvm_has_sync_mmu()) {
+ qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
+ return -1;
+ }
+
+ ret = qemu_balloon_status(cb, opaque);
+ if (!ret) {
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * do_balloon(): Request VM to change its memory allocation
+ */
+int do_balloon(Monitor *mon, const QDict *params,
+ MonitorCompletion cb, void *opaque)
+{
+ int ret;
+
+ if (kvm_enabled() && !kvm_has_sync_mmu()) {
+ qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
+ return -1;
+ }
+
+ ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque);
+ if (ret == 0) {
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
+ return -1;
+ }
+
+ cb(opaque, NULL);
+ return 0;
+}
diff --git a/balloon.h b/balloon.h
new file mode 100644
index 0000000..d478e28
--- /dev/null
+++ b/balloon.h
@@ -0,0 +1,33 @@
+/*
+ * Balloon
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_BALLOON_H
+#define _QEMU_BALLOON_H
+
+#include "monitor.h"
+
+typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target,
+ MonitorCompletion cb, void *cb_data);
+
+void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque);
+
+int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque);
+
+int qemu_balloon_status(MonitorCompletion cb, void *opaque);
+
+void monitor_print_balloon(Monitor *mon, const QObject *data);
+int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque);
+int do_balloon(Monitor *mon, const QDict *params,
+ MonitorCompletion cb, void *opaque);
+
+#endif
diff --git a/bin/qemu-system-x86_64 b/bin/qemu-system-x86_64
new file mode 100755
index 0000000..1173304
--- /dev/null
+++ b/bin/qemu-system-x86_64
Binary files differ
diff --git a/block-migration.c b/block-migration.c
new file mode 100644
index 0000000..8218bac
--- /dev/null
+++ b/block-migration.c
@@ -0,0 +1,732 @@
+/*
+ * QEMU live block migration
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Liran Schour <lirans@il.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "hw/hw.h"
+#include "qemu-queue.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "block-migration.h"
+#include "migration.h"
+#include "blockdev.h"
+#include <assert.h>
+
+#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS)
+
+#define BLK_MIG_FLAG_DEVICE_BLOCK 0x01
+#define BLK_MIG_FLAG_EOS 0x02
+#define BLK_MIG_FLAG_PROGRESS 0x04
+
+#define MAX_IS_ALLOCATED_SEARCH 65536
+
+//#define DEBUG_BLK_MIGRATION
+
+#ifdef DEBUG_BLK_MIGRATION
+#define DPRINTF(fmt, ...) \
+ do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+typedef struct BlkMigDevState {
+ BlockDriverState *bs;
+ int bulk_completed;
+ int shared_base;
+ int64_t cur_sector;
+ int64_t cur_dirty;
+ int64_t completed_sectors;
+ int64_t total_sectors;
+ int64_t dirty;
+ QSIMPLEQ_ENTRY(BlkMigDevState) entry;
+ unsigned long *aio_bitmap;
+} BlkMigDevState;
+
+typedef struct BlkMigBlock {
+ uint8_t *buf;
+ BlkMigDevState *bmds;
+ int64_t sector;
+ int nr_sectors;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ BlockDriverAIOCB *aiocb;
+ int ret;
+ int64_t time;
+ QSIMPLEQ_ENTRY(BlkMigBlock) entry;
+} BlkMigBlock;
+
+typedef struct BlkMigState {
+ int blk_enable;
+ int shared_base;
+ QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list;
+ QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list;
+ int submitted;
+ int read_done;
+ int transferred;
+ int64_t total_sector_sum;
+ int prev_progress;
+ int bulk_completed;
+ long double total_time;
+ int reads;
+} BlkMigState;
+
+static BlkMigState block_mig_state;
+
+static void blk_send(QEMUFile *f, BlkMigBlock * blk)
+{
+ int len;
+
+ /* sector number and flags */
+ qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS)
+ | BLK_MIG_FLAG_DEVICE_BLOCK);
+
+ /* device name */
+ len = strlen(blk->bmds->bs->device_name);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len);
+
+ qemu_put_buffer(f, blk->buf, BLOCK_SIZE);
+}
+
+int blk_mig_active(void)
+{
+ return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list);
+}
+
+uint64_t blk_mig_bytes_transferred(void)
+{
+ BlkMigDevState *bmds;
+ uint64_t sum = 0;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ sum += bmds->completed_sectors;
+ }
+ return sum << BDRV_SECTOR_BITS;
+}
+
+uint64_t blk_mig_bytes_remaining(void)
+{
+ return blk_mig_bytes_total() - blk_mig_bytes_transferred();
+}
+
+uint64_t blk_mig_bytes_total(void)
+{
+ BlkMigDevState *bmds;
+ uint64_t sum = 0;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ sum += bmds->total_sectors;
+ }
+ return sum << BDRV_SECTOR_BITS;
+}
+
+static inline void add_avg_read_time(int64_t time)
+{
+ block_mig_state.reads++;
+ block_mig_state.total_time += time;
+}
+
+static inline long double compute_read_bwidth(void)
+{
+ assert(block_mig_state.total_time != 0);
+ return (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time;
+}
+
+static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
+{
+ int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
+
+ if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) {
+ return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] &
+ (1UL << (chunk % (sizeof(unsigned long) * 8))));
+ } else {
+ return 0;
+ }
+}
+
+static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num,
+ int nb_sectors, int set)
+{
+ int64_t start, end;
+ unsigned long val, idx, bit;
+
+ start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
+ end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
+
+ for (; start <= end; start++) {
+ idx = start / (sizeof(unsigned long) * 8);
+ bit = start % (sizeof(unsigned long) * 8);
+ val = bmds->aio_bitmap[idx];
+ if (set) {
+ val |= 1UL << bit;
+ } else {
+ val &= ~(1UL << bit);
+ }
+ bmds->aio_bitmap[idx] = val;
+ }
+}
+
+static void alloc_aio_bitmap(BlkMigDevState *bmds)
+{
+ BlockDriverState *bs = bmds->bs;
+ int64_t bitmap_size;
+
+ bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
+ BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
+ bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
+
+ bmds->aio_bitmap = qemu_mallocz(bitmap_size);
+}
+
+static void blk_mig_read_cb(void *opaque, int ret)
+{
+ BlkMigBlock *blk = opaque;
+
+ blk->ret = ret;
+
+ blk->time = qemu_get_clock_ns(rt_clock) - blk->time;
+
+ add_avg_read_time(blk->time);
+
+ QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
+ bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0);
+
+ block_mig_state.submitted--;
+ block_mig_state.read_done++;
+ assert(block_mig_state.submitted >= 0);
+}
+
+static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
+ BlkMigDevState *bmds)
+{
+ int64_t total_sectors = bmds->total_sectors;
+ int64_t cur_sector = bmds->cur_sector;
+ BlockDriverState *bs = bmds->bs;
+ BlkMigBlock *blk;
+ int nr_sectors;
+
+ if (bmds->shared_base) {
+ while (cur_sector < total_sectors &&
+ !bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH,
+ &nr_sectors)) {
+ cur_sector += nr_sectors;
+ }
+ }
+
+ if (cur_sector >= total_sectors) {
+ bmds->cur_sector = bmds->completed_sectors = total_sectors;
+ return 1;
+ }
+
+ bmds->completed_sectors = cur_sector;
+
+ cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1);
+
+ /* we are going to transfer a full block even if it is not allocated */
+ nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
+
+ if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
+ nr_sectors = total_sectors - cur_sector;
+ }
+
+ blk = qemu_malloc(sizeof(BlkMigBlock));
+ blk->buf = qemu_malloc(BLOCK_SIZE);
+ blk->bmds = bmds;
+ blk->sector = cur_sector;
+ blk->nr_sectors = nr_sectors;
+
+ blk->iov.iov_base = blk->buf;
+ blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
+ qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
+
+ blk->time = qemu_get_clock_ns(rt_clock);
+
+ blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
+ nr_sectors, blk_mig_read_cb, blk);
+ if (!blk->aiocb) {
+ goto error;
+ }
+ block_mig_state.submitted++;
+
+ bdrv_reset_dirty(bs, cur_sector, nr_sectors);
+ bmds->cur_sector = cur_sector + nr_sectors;
+
+ return (bmds->cur_sector >= total_sectors);
+
+error:
+ monitor_printf(mon, "Error reading sector %" PRId64 "\n", cur_sector);
+ qemu_file_set_error(f);
+ qemu_free(blk->buf);
+ qemu_free(blk);
+ return 0;
+}
+
+static void set_dirty_tracking(int enable)
+{
+ BlkMigDevState *bmds;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ bdrv_set_dirty_tracking(bmds->bs, enable);
+ }
+}
+
+static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
+{
+ Monitor *mon = opaque;
+ BlkMigDevState *bmds;
+ int64_t sectors;
+
+ if (!bdrv_is_read_only(bs)) {
+ sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
+ if (sectors <= 0) {
+ return;
+ }
+
+ bmds = qemu_mallocz(sizeof(BlkMigDevState));
+ bmds->bs = bs;
+ bmds->bulk_completed = 0;
+ bmds->total_sectors = sectors;
+ bmds->completed_sectors = 0;
+ bmds->shared_base = block_mig_state.shared_base;
+ alloc_aio_bitmap(bmds);
+ drive_get_ref(drive_get_by_blockdev(bs));
+ bdrv_set_in_use(bs, 1);
+
+ block_mig_state.total_sector_sum += sectors;
+
+ if (bmds->shared_base) {
+ monitor_printf(mon, "Start migration for %s with shared base "
+ "image\n",
+ bs->device_name);
+ } else {
+ monitor_printf(mon, "Start full migration for %s\n",
+ bs->device_name);
+ }
+
+ QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
+ }
+}
+
+static void init_blk_migration(Monitor *mon, QEMUFile *f)
+{
+ block_mig_state.submitted = 0;
+ block_mig_state.read_done = 0;
+ block_mig_state.transferred = 0;
+ block_mig_state.total_sector_sum = 0;
+ block_mig_state.prev_progress = -1;
+ block_mig_state.bulk_completed = 0;
+ block_mig_state.total_time = 0;
+ block_mig_state.reads = 0;
+
+ bdrv_iterate(init_blk_migration_it, mon);
+}
+
+static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
+{
+ int64_t completed_sector_sum = 0;
+ BlkMigDevState *bmds;
+ int progress;
+ int ret = 0;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ if (bmds->bulk_completed == 0) {
+ if (mig_save_device_bulk(mon, f, bmds) == 1) {
+ /* completed bulk section for this device */
+ bmds->bulk_completed = 1;
+ }
+ completed_sector_sum += bmds->completed_sectors;
+ ret = 1;
+ break;
+ } else {
+ completed_sector_sum += bmds->completed_sectors;
+ }
+ }
+
+ if (block_mig_state.total_sector_sum != 0) {
+ progress = completed_sector_sum * 100 /
+ block_mig_state.total_sector_sum;
+ } else {
+ progress = 100;
+ }
+ if (progress != block_mig_state.prev_progress) {
+ block_mig_state.prev_progress = progress;
+ qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
+ | BLK_MIG_FLAG_PROGRESS);
+ monitor_printf(mon, "Completed %d %%\r", progress);
+ monitor_flush(mon);
+ }
+
+ return ret;
+}
+
+static void blk_mig_reset_dirty_cursor(void)
+{
+ BlkMigDevState *bmds;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ bmds->cur_dirty = 0;
+ }
+}
+
+static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
+ BlkMigDevState *bmds, int is_async)
+{
+ BlkMigBlock *blk;
+ int64_t total_sectors = bmds->total_sectors;
+ int64_t sector;
+ int nr_sectors;
+
+ for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
+ if (bmds_aio_inflight(bmds, sector)) {
+ qemu_aio_flush();
+ }
+ if (bdrv_get_dirty(bmds->bs, sector)) {
+
+ if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
+ nr_sectors = total_sectors - sector;
+ } else {
+ nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
+ }
+ blk = qemu_malloc(sizeof(BlkMigBlock));
+ blk->buf = qemu_malloc(BLOCK_SIZE);
+ blk->bmds = bmds;
+ blk->sector = sector;
+ blk->nr_sectors = nr_sectors;
+
+ if (is_async) {
+ blk->iov.iov_base = blk->buf;
+ blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
+ qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
+
+ blk->time = qemu_get_clock_ns(rt_clock);
+
+ blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
+ nr_sectors, blk_mig_read_cb, blk);
+ if (!blk->aiocb) {
+ goto error;
+ }
+ block_mig_state.submitted++;
+ bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
+ } else {
+ if (bdrv_read(bmds->bs, sector, blk->buf,
+ nr_sectors) < 0) {
+ goto error;
+ }
+ blk_send(f, blk);
+
+ qemu_free(blk->buf);
+ qemu_free(blk);
+ }
+
+ bdrv_reset_dirty(bmds->bs, sector, nr_sectors);
+ break;
+ }
+ sector += BDRV_SECTORS_PER_DIRTY_CHUNK;
+ bmds->cur_dirty = sector;
+ }
+
+ return (bmds->cur_dirty >= bmds->total_sectors);
+
+error:
+ monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector);
+ qemu_file_set_error(f);
+ qemu_free(blk->buf);
+ qemu_free(blk);
+ return 0;
+}
+
+static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async)
+{
+ BlkMigDevState *bmds;
+ int ret = 0;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void flush_blks(QEMUFile* f)
+{
+ BlkMigBlock *blk;
+
+ DPRINTF("%s Enter submitted %d read_done %d transferred %d\n",
+ __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done,
+ block_mig_state.transferred);
+
+ while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
+ if (qemu_file_rate_limit(f)) {
+ break;
+ }
+ if (blk->ret < 0) {
+ qemu_file_set_error(f);
+ break;
+ }
+ blk_send(f, blk);
+
+ QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
+ qemu_free(blk->buf);
+ qemu_free(blk);
+
+ block_mig_state.read_done--;
+ block_mig_state.transferred++;
+ assert(block_mig_state.read_done >= 0);
+ }
+
+ DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__,
+ block_mig_state.submitted, block_mig_state.read_done,
+ block_mig_state.transferred);
+}
+
+static int64_t get_remaining_dirty(void)
+{
+ BlkMigDevState *bmds;
+ int64_t dirty = 0;
+
+ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
+ dirty += bdrv_get_dirty_count(bmds->bs);
+ }
+
+ return dirty * BLOCK_SIZE;
+}
+
+static int is_stage2_completed(void)
+{
+ int64_t remaining_dirty;
+ long double bwidth;
+
+ if (block_mig_state.bulk_completed == 1) {
+
+ remaining_dirty = get_remaining_dirty();
+ if (remaining_dirty == 0) {
+ return 1;
+ }
+
+ bwidth = compute_read_bwidth();
+
+ if ((remaining_dirty / bwidth) <=
+ migrate_max_downtime()) {
+ /* finish stage2 because we think that we can finish remaing work
+ below max_downtime */
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void blk_mig_cleanup(Monitor *mon)
+{
+ BlkMigDevState *bmds;
+ BlkMigBlock *blk;
+
+ set_dirty_tracking(0);
+
+ while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
+ QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
+ bdrv_set_in_use(bmds->bs, 0);
+ drive_put_ref(drive_get_by_blockdev(bmds->bs));
+ qemu_free(bmds->aio_bitmap);
+ qemu_free(bmds);
+ }
+
+ while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) {
+ QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry);
+ qemu_free(blk->buf);
+ qemu_free(blk);
+ }
+
+ monitor_printf(mon, "\n");
+}
+
+static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
+{
+ DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
+ stage, block_mig_state.submitted, block_mig_state.transferred);
+
+ if (stage < 0) {
+ blk_mig_cleanup(mon);
+ return 0;
+ }
+
+ if (block_mig_state.blk_enable != 1) {
+ /* no need to migrate storage */
+ qemu_put_be64(f, BLK_MIG_FLAG_EOS);
+ return 1;
+ }
+
+ if (stage == 1) {
+ init_blk_migration(mon, f);
+
+ /* start track dirty blocks */
+ set_dirty_tracking(1);
+ }
+
+ flush_blks(f);
+
+ if (qemu_file_has_error(f)) {
+ blk_mig_cleanup(mon);
+ return 0;
+ }
+
+ blk_mig_reset_dirty_cursor();
+
+ if (stage == 2) {
+ /* control the rate of transfer */
+ while ((block_mig_state.submitted +
+ block_mig_state.read_done) * BLOCK_SIZE <
+ qemu_file_get_rate_limit(f)) {
+ if (block_mig_state.bulk_completed == 0) {
+ /* first finish the bulk phase */
+ if (blk_mig_save_bulked_block(mon, f) == 0) {
+ /* finished saving bulk on all devices */
+ block_mig_state.bulk_completed = 1;
+ }
+ } else {
+ if (blk_mig_save_dirty_block(mon, f, 1) == 0) {
+ /* no more dirty blocks */
+ break;
+ }
+ }
+ }
+
+ flush_blks(f);
+
+ if (qemu_file_has_error(f)) {
+ blk_mig_cleanup(mon);
+ return 0;
+ }
+ }
+
+ if (stage == 3) {
+ /* we know for sure that save bulk is completed and
+ all async read completed */
+ assert(block_mig_state.submitted == 0);
+
+ while (blk_mig_save_dirty_block(mon, f, 0) != 0);
+ blk_mig_cleanup(mon);
+
+ /* report completion */
+ qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
+
+ if (qemu_file_has_error(f)) {
+ return 0;
+ }
+
+ monitor_printf(mon, "Block migration completed\n");
+ }
+
+ qemu_put_be64(f, BLK_MIG_FLAG_EOS);
+
+ return ((stage == 2) && is_stage2_completed());
+}
+
+static int block_load(QEMUFile *f, void *opaque, int version_id)
+{
+ static int banner_printed;
+ int len, flags;
+ char device_name[256];
+ int64_t addr;
+ BlockDriverState *bs, *bs_prev = NULL;
+ uint8_t *buf;
+ int64_t total_sectors = 0;
+ int nr_sectors;
+
+ do {
+ addr = qemu_get_be64(f);
+
+ flags = addr & ~BDRV_SECTOR_MASK;
+ addr >>= BDRV_SECTOR_BITS;
+
+ if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) {
+ int ret;
+ /* get device name */
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)device_name, len);
+ device_name[len] = '\0';
+
+ bs = bdrv_find(device_name);
+ if (!bs) {
+ fprintf(stderr, "Error unknown block device %s\n",
+ device_name);
+ return -EINVAL;
+ }
+
+ if (bs != bs_prev) {
+ bs_prev = bs;
+ total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
+ if (total_sectors <= 0) {
+ error_report("Error getting length of block device %s\n",
+ device_name);
+ return -EINVAL;
+ }
+ }
+
+ if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) {
+ nr_sectors = total_sectors - addr;
+ } else {
+ nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
+ }
+
+ buf = qemu_malloc(BLOCK_SIZE);
+
+ qemu_get_buffer(f, buf, BLOCK_SIZE);
+ ret = bdrv_write(bs, addr, buf, nr_sectors);
+
+ qemu_free(buf);
+ if (ret < 0) {
+ return ret;
+ }
+ } else if (flags & BLK_MIG_FLAG_PROGRESS) {
+ if (!banner_printed) {
+ printf("Receiving block device images\n");
+ banner_printed = 1;
+ }
+ printf("Completed %d %%%c", (int)addr,
+ (addr == 100) ? '\n' : '\r');
+ fflush(stdout);
+ } else if (!(flags & BLK_MIG_FLAG_EOS)) {
+ fprintf(stderr, "Unknown flags\n");
+ return -EINVAL;
+ }
+ if (qemu_file_has_error(f)) {
+ return -EIO;
+ }
+ } while (!(flags & BLK_MIG_FLAG_EOS));
+
+ return 0;
+}
+
+static void block_set_params(int blk_enable, int shared_base, void *opaque)
+{
+ block_mig_state.blk_enable = blk_enable;
+ block_mig_state.shared_base = shared_base;
+
+ /* shared base means that blk_enable = 1 */
+ block_mig_state.blk_enable |= shared_base;
+}
+
+void blk_mig_init(void)
+{
+ QSIMPLEQ_INIT(&block_mig_state.bmds_list);
+ QSIMPLEQ_INIT(&block_mig_state.blk_list);
+
+ register_savevm_live(NULL, "block", 0, 1, block_set_params,
+ block_save_live, NULL, block_load, &block_mig_state);
+}
diff --git a/block-migration.h b/block-migration.h
new file mode 100644
index 0000000..ffa8ac0
--- /dev/null
+++ b/block-migration.h
@@ -0,0 +1,23 @@
+/*
+ * QEMU live block migration
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Liran Schour <lirans@il.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef BLOCK_MIGRATION_H
+#define BLOCK_MIGRATION_H
+
+void blk_mig_init(void);
+int blk_mig_active(void);
+uint64_t blk_mig_bytes_transferred(void);
+uint64_t blk_mig_bytes_remaining(void);
+uint64_t blk_mig_bytes_total(void);
+
+#endif /* BLOCK_MIGRATION_H */
diff --git a/block.c b/block.c
new file mode 100644
index 0000000..b203875
--- /dev/null
+++ b/block.c
@@ -0,0 +1,2946 @@
+/*
+ * QEMU System Emulator block driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "config-host.h"
+#include "qemu-common.h"
+#include "trace.h"
+#include "monitor.h"
+#include "block_int.h"
+#include "module.h"
+#include "qemu-objects.h"
+
+#ifdef CONFIG_BSD
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#ifndef __DragonFly__
+#include <sys/disk.h>
+#endif
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
+static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors);
+static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+
+static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
+ QTAILQ_HEAD_INITIALIZER(bdrv_states);
+
+static QLIST_HEAD(, BlockDriver) bdrv_drivers =
+ QLIST_HEAD_INITIALIZER(bdrv_drivers);
+
+/* The device to use for VM snapshots */
+static BlockDriverState *bs_snapshots;
+
+/* If non-zero, use only whitelisted block drivers */
+static int use_bdrv_whitelist;
+
+#ifdef _WIN32
+static int is_windows_drive_prefix(const char *filename)
+{
+ return (((filename[0] >= 'a' && filename[0] <= 'z') ||
+ (filename[0] >= 'A' && filename[0] <= 'Z')) &&
+ filename[1] == ':');
+}
+
+int is_windows_drive(const char *filename)
+{
+ if (is_windows_drive_prefix(filename) &&
+ filename[2] == '\0')
+ return 1;
+ if (strstart(filename, "\\\\.\\", NULL) ||
+ strstart(filename, "//./", NULL))
+ return 1;
+ return 0;
+}
+#endif
+
+/* check if the path starts with "<protocol>:" */
+static int path_has_protocol(const char *path)
+{
+#ifdef _WIN32
+ if (is_windows_drive(path) ||
+ is_windows_drive_prefix(path)) {
+ return 0;
+ }
+#endif
+
+ return strchr(path, ':') != NULL;
+}
+
+int path_is_absolute(const char *path)
+{
+ const char *p;
+#ifdef _WIN32
+ /* specific case for names like: "\\.\d:" */
+ if (*path == '/' || *path == '\\')
+ return 1;
+#endif
+ p = strchr(path, ':');
+ if (p)
+ p++;
+ else
+ p = path;
+#ifdef _WIN32
+ return (*p == '/' || *p == '\\');
+#else
+ return (*p == '/');
+#endif
+}
+
+/* if filename is absolute, just copy it to dest. Otherwise, build a
+ path to it by considering it is relative to base_path. URL are
+ supported. */
+void path_combine(char *dest, int dest_size,
+ const char *base_path,
+ const char *filename)
+{
+ const char *p, *p1;
+ int len;
+
+ if (dest_size <= 0)
+ return;
+ if (path_is_absolute(filename)) {
+ pstrcpy(dest, dest_size, filename);
+ } else {
+ p = strchr(base_path, ':');
+ if (p)
+ p++;
+ else
+ p = base_path;
+ p1 = strrchr(base_path, '/');
+#ifdef _WIN32
+ {
+ const char *p2;
+ p2 = strrchr(base_path, '\\');
+ if (!p1 || p2 > p1)
+ p1 = p2;
+ }
+#endif
+ if (p1)
+ p1++;
+ else
+ p1 = base_path;
+ if (p1 > p)
+ p = p1;
+ len = p - base_path;
+ if (len > dest_size - 1)
+ len = dest_size - 1;
+ memcpy(dest, base_path, len);
+ dest[len] = '\0';
+ pstrcat(dest, dest_size, filename);
+ }
+}
+
+void bdrv_register(BlockDriver *bdrv)
+{
+ if (!bdrv->bdrv_aio_readv) {
+ /* add AIO emulation layer */
+ bdrv->bdrv_aio_readv = bdrv_aio_readv_em;
+ bdrv->bdrv_aio_writev = bdrv_aio_writev_em;
+ } else if (!bdrv->bdrv_read) {
+ /* add synchronous IO emulation layer */
+ bdrv->bdrv_read = bdrv_read_em;
+ bdrv->bdrv_write = bdrv_write_em;
+ }
+
+ if (!bdrv->bdrv_aio_flush)
+ bdrv->bdrv_aio_flush = bdrv_aio_flush_em;
+
+ QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list);
+}
+
+/* create a new block device (by default it is empty) */
+BlockDriverState *bdrv_new(const char *device_name)
+{
+ BlockDriverState *bs;
+
+ bs = qemu_mallocz(sizeof(BlockDriverState));
+ pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
+ if (device_name[0] != '\0') {
+ QTAILQ_INSERT_TAIL(&bdrv_states, bs, list);
+ }
+ return bs;
+}
+
+BlockDriver *bdrv_find_format(const char *format_name)
+{
+ BlockDriver *drv1;
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+ if (!strcmp(drv1->format_name, format_name)) {
+ return drv1;
+ }
+ }
+ return NULL;
+}
+
+static int bdrv_is_whitelisted(BlockDriver *drv)
+{
+ static const char *whitelist[] = {
+ CONFIG_BDRV_WHITELIST
+ };
+ const char **p;
+
+ if (!whitelist[0])
+ return 1; /* no whitelist, anything goes */
+
+ for (p = whitelist; *p; p++) {
+ if (!strcmp(drv->format_name, *p)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+BlockDriver *bdrv_find_whitelisted_format(const char *format_name)
+{
+ BlockDriver *drv = bdrv_find_format(format_name);
+ return drv && bdrv_is_whitelisted(drv) ? drv : NULL;
+}
+
+int bdrv_create(BlockDriver *drv, const char* filename,
+ QEMUOptionParameter *options)
+{
+ if (!drv->bdrv_create)
+ return -ENOTSUP;
+
+ return drv->bdrv_create(filename, options);
+}
+
+int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
+{
+ BlockDriver *drv;
+
+ drv = bdrv_find_protocol(filename);
+ if (drv == NULL) {
+ return -ENOENT;
+ }
+
+ return bdrv_create(drv, filename, options);
+}
+
+#ifdef _WIN32
+void get_tmp_filename(char *filename, int size)
+{
+ char temp_dir[MAX_PATH];
+
+ GetTempPath(MAX_PATH, temp_dir);
+ GetTempFileName(temp_dir, "qem", 0, filename);
+}
+#else
+void get_tmp_filename(char *filename, int size)
+{
+ int fd;
+ const char *tmpdir;
+ /* XXX: race condition possible */
+ tmpdir = getenv("TMPDIR");
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ snprintf(filename, size, "%s/vl.XXXXXX", tmpdir);
+ fd = mkstemp(filename);
+ close(fd);
+}
+#endif
+
+/*
+ * Detect host devices. By convention, /dev/cdrom[N] is always
+ * recognized as a host CDROM.
+ */
+static BlockDriver *find_hdev_driver(const char *filename)
+{
+ int score_max = 0, score;
+ BlockDriver *drv = NULL, *d;
+
+ QLIST_FOREACH(d, &bdrv_drivers, list) {
+ if (d->bdrv_probe_device) {
+ score = d->bdrv_probe_device(filename);
+ if (score > score_max) {
+ score_max = score;
+ drv = d;
+ }
+ }
+ }
+
+ return drv;
+}
+
+BlockDriver *bdrv_find_protocol(const char *filename)
+{
+ BlockDriver *drv1;
+ char protocol[128];
+ int len;
+ const char *p;
+
+ /* TODO Drivers without bdrv_file_open must be specified explicitly */
+
+ /*
+ * XXX(hch): we really should not let host device detection
+ * override an explicit protocol specification, but moving this
+ * later breaks access to device names with colons in them.
+ * Thanks to the brain-dead persistent naming schemes on udev-
+ * based Linux systems those actually are quite common.
+ */
+ drv1 = find_hdev_driver(filename);
+ if (drv1) {
+ return drv1;
+ }
+
+ if (!path_has_protocol(filename)) {
+ return bdrv_find_format("file");
+ }
+ p = strchr(filename, ':');
+ assert(p != NULL);
+ len = p - filename;
+ if (len > sizeof(protocol) - 1)
+ len = sizeof(protocol) - 1;
+ memcpy(protocol, filename, len);
+ protocol[len] = '\0';
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+ if (drv1->protocol_name &&
+ !strcmp(drv1->protocol_name, protocol)) {
+ return drv1;
+ }
+ }
+ return NULL;
+}
+
+static int find_image_format(const char *filename, BlockDriver **pdrv)
+{
+ int ret, score, score_max;
+ BlockDriver *drv1, *drv;
+ uint8_t buf[2048];
+ BlockDriverState *bs;
+
+ ret = bdrv_file_open(&bs, filename, 0);
+ if (ret < 0) {
+ *pdrv = NULL;
+ return ret;
+ }
+
+ /* Return the raw BlockDriver * to scsi-generic devices or empty drives */
+ if (bs->sg || !bdrv_is_inserted(bs)) {
+ bdrv_delete(bs);
+ drv = bdrv_find_format("raw");
+ if (!drv) {
+ ret = -ENOENT;
+ }
+ *pdrv = drv;
+ return ret;
+ }
+
+ ret = bdrv_pread(bs, 0, buf, sizeof(buf));
+ bdrv_delete(bs);
+ if (ret < 0) {
+ *pdrv = NULL;
+ return ret;
+ }
+
+ score_max = 0;
+ drv = NULL;
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+ if (drv1->bdrv_probe) {
+ score = drv1->bdrv_probe(buf, ret, filename);
+ if (score > score_max) {
+ score_max = score;
+ drv = drv1;
+ }
+ }
+ }
+ if (!drv) {
+ ret = -ENOENT;
+ }
+ *pdrv = drv;
+ return ret;
+}
+
+/**
+ * Set the current 'total_sectors' value
+ */
+static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
+{
+ BlockDriver *drv = bs->drv;
+
+ /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
+ if (bs->sg)
+ return 0;
+
+ /* query actual device if possible, otherwise just trust the hint */
+ if (drv->bdrv_getlength) {
+ int64_t length = drv->bdrv_getlength(bs);
+ if (length < 0) {
+ return length;
+ }
+ hint = length >> BDRV_SECTOR_BITS;
+ }
+
+ bs->total_sectors = hint;
+ return 0;
+}
+
+/*
+ * Common part for opening disk images and files
+ */
+static int bdrv_open_common(BlockDriverState *bs, const char *filename,
+ int flags, BlockDriver *drv)
+{
+ int ret, open_flags;
+
+ assert(drv != NULL);
+
+ bs->file = NULL;
+ bs->total_sectors = 0;
+ bs->encrypted = 0;
+ bs->valid_key = 0;
+ bs->open_flags = flags;
+ /* buffer_alignment defaulted to 512, drivers can change this value */
+ bs->buffer_alignment = 512;
+
+ pstrcpy(bs->filename, sizeof(bs->filename), filename);
+
+ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
+ return -ENOTSUP;
+ }
+
+ bs->drv = drv;
+ bs->opaque = qemu_mallocz(drv->instance_size);
+
+ /*
+ * Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a
+ * write cache to the guest. We do need the fdatasync to flush
+ * out transactions for block allocations, and we maybe have a
+ * volatile write cache in our backing device to deal with.
+ */
+ if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
+ bs->enable_write_cache = 1;
+
+ /*
+ * Clear flags that are internal to the block layer before opening the
+ * image.
+ */
+ open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+
+ /*
+ * Snapshots should be writeable.
+ */
+ if (bs->is_temporary) {
+ open_flags |= BDRV_O_RDWR;
+ }
+
+ /* Open the image, either directly or using a protocol */
+ if (drv->bdrv_file_open) {
+ ret = drv->bdrv_file_open(bs, filename, open_flags);
+ } else {
+ ret = bdrv_file_open(&bs->file, filename, open_flags);
+ if (ret >= 0) {
+ ret = drv->bdrv_open(bs, open_flags);
+ }
+ }
+
+ if (ret < 0) {
+ goto free_and_fail;
+ }
+
+ bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
+
+ ret = refresh_total_sectors(bs, bs->total_sectors);
+ if (ret < 0) {
+ goto free_and_fail;
+ }
+
+#ifndef _WIN32
+ if (bs->is_temporary) {
+ unlink(filename);
+ }
+#endif
+ return 0;
+
+free_and_fail:
+ if (bs->file) {
+ bdrv_delete(bs->file);
+ bs->file = NULL;
+ }
+ qemu_free(bs->opaque);
+ bs->opaque = NULL;
+ bs->drv = NULL;
+ return ret;
+}
+
+/*
+ * Opens a file using a protocol (file, host_device, nbd, ...)
+ */
+int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
+{
+ BlockDriverState *bs;
+ BlockDriver *drv;
+ int ret;
+
+ drv = bdrv_find_protocol(filename);
+ if (!drv) {
+ return -ENOENT;
+ }
+
+ bs = bdrv_new("");
+ ret = bdrv_open_common(bs, filename, flags, drv);
+ if (ret < 0) {
+ bdrv_delete(bs);
+ return ret;
+ }
+ bs->growable = 1;
+ *pbs = bs;
+ return 0;
+}
+
+/*
+ * Opens a disk image (raw, qcow2, vmdk, ...)
+ */
+int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
+ BlockDriver *drv)
+{
+ int ret;
+
+ if (flags & BDRV_O_SNAPSHOT) {
+ BlockDriverState *bs1;
+ int64_t total_size;
+ int is_protocol = 0;
+ BlockDriver *bdrv_qcow2;
+ QEMUOptionParameter *options;
+ char tmp_filename[PATH_MAX];
+ char backing_filename[PATH_MAX];
+
+ /* if snapshot, we create a temporary backing file and open it
+ instead of opening 'filename' directly */
+
+ /* if there is a backing file, use it */
+ bs1 = bdrv_new("");
+ ret = bdrv_open(bs1, filename, 0, drv);
+ if (ret < 0) {
+ bdrv_delete(bs1);
+ return ret;
+ }
+ total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
+
+ if (bs1->drv && bs1->drv->protocol_name)
+ is_protocol = 1;
+
+ bdrv_delete(bs1);
+
+ get_tmp_filename(tmp_filename, sizeof(tmp_filename));
+
+ /* Real path is meaningless for protocols */
+ if (is_protocol)
+ snprintf(backing_filename, sizeof(backing_filename),
+ "%s", filename);
+ else if (!realpath(filename, backing_filename))
+ return -errno;
+
+ bdrv_qcow2 = bdrv_find_format("qcow2");
+ options = parse_option_parameters("", bdrv_qcow2->create_options, NULL);
+
+ set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size);
+ set_option_parameter(options, BLOCK_OPT_BACKING_FILE, backing_filename);
+ if (drv) {
+ set_option_parameter(options, BLOCK_OPT_BACKING_FMT,
+ drv->format_name);
+ }
+
+ ret = bdrv_create(bdrv_qcow2, tmp_filename, options);
+ free_option_parameters(options);
+ if (ret < 0) {
+ return ret;
+ }
+
+ filename = tmp_filename;
+ drv = bdrv_qcow2;
+ bs->is_temporary = 1;
+ }
+
+ /* Find the right image format driver */
+ if (!drv) {
+ ret = find_image_format(filename, &drv);
+ }
+
+ if (!drv) {
+ goto unlink_and_fail;
+ }
+
+ /* Open the image */
+ ret = bdrv_open_common(bs, filename, flags, drv);
+ if (ret < 0) {
+ goto unlink_and_fail;
+ }
+
+ /* If there is a backing file, use it */
+ if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {
+ char backing_filename[PATH_MAX];
+ int back_flags;
+ BlockDriver *back_drv = NULL;
+
+ bs->backing_hd = bdrv_new("");
+
+ if (path_has_protocol(bs->backing_file)) {
+ pstrcpy(backing_filename, sizeof(backing_filename),
+ bs->backing_file);
+ } else {
+ path_combine(backing_filename, sizeof(backing_filename),
+ filename, bs->backing_file);
+ }
+
+ if (bs->backing_format[0] != '\0') {
+ back_drv = bdrv_find_format(bs->backing_format);
+ }
+
+ /* backing files always opened read-only */
+ back_flags =
+ flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+
+ ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);
+ if (ret < 0) {
+ bdrv_close(bs);
+ return ret;
+ }
+ if (bs->is_temporary) {
+ bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR);
+ } else {
+ /* base image inherits from "parent" */
+ bs->backing_hd->keep_read_only = bs->keep_read_only;
+ }
+ }
+
+ if (!bdrv_key_required(bs)) {
+ /* call the change callback */
+ bs->media_changed = 1;
+ if (bs->change_cb)
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
+ }
+
+ return 0;
+
+unlink_and_fail:
+ if (bs->is_temporary) {
+ unlink(filename);
+ }
+ return ret;
+}
+
+void bdrv_close(BlockDriverState *bs)
+{
+ if (bs->drv) {
+ if (bs == bs_snapshots) {
+ bs_snapshots = NULL;
+ }
+ if (bs->backing_hd) {
+ bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ }
+ bs->drv->bdrv_close(bs);
+ qemu_free(bs->opaque);
+#ifdef _WIN32
+ if (bs->is_temporary) {
+ unlink(bs->filename);
+ }
+#endif
+ bs->opaque = NULL;
+ bs->drv = NULL;
+
+ if (bs->file != NULL) {
+ bdrv_close(bs->file);
+ }
+
+ /* call the change callback */
+ bs->media_changed = 1;
+ if (bs->change_cb)
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
+ }
+}
+
+void bdrv_close_all(void)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ bdrv_close(bs);
+ }
+}
+
+/* make a BlockDriverState anonymous by removing from bdrv_state list.
+ Also, NULL terminate the device_name to prevent double remove */
+void bdrv_make_anon(BlockDriverState *bs)
+{
+ if (bs->device_name[0] != '\0') {
+ QTAILQ_REMOVE(&bdrv_states, bs, list);
+ }
+ bs->device_name[0] = '\0';
+}
+
+void bdrv_delete(BlockDriverState *bs)
+{
+ assert(!bs->peer);
+
+ /* remove from list, if necessary */
+ bdrv_make_anon(bs);
+
+ bdrv_close(bs);
+ if (bs->file != NULL) {
+ bdrv_delete(bs->file);
+ }
+
+ assert(bs != bs_snapshots);
+ qemu_free(bs);
+}
+
+int bdrv_attach(BlockDriverState *bs, DeviceState *qdev)
+{
+ if (bs->peer) {
+ return -EBUSY;
+ }
+ bs->peer = qdev;
+ return 0;
+}
+
+void bdrv_detach(BlockDriverState *bs, DeviceState *qdev)
+{
+ assert(bs->peer == qdev);
+ bs->peer = NULL;
+}
+
+DeviceState *bdrv_get_attached(BlockDriverState *bs)
+{
+ return bs->peer;
+}
+
+/*
+ * Run consistency checks on an image
+ *
+ * Returns 0 if the check could be completed (it doesn't mean that the image is
+ * free of errors) or -errno when an internal error occured. The results of the
+ * check are stored in res.
+ */
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
+{
+ if (bs->drv->bdrv_check == NULL) {
+ return -ENOTSUP;
+ }
+
+ memset(res, 0, sizeof(*res));
+ return bs->drv->bdrv_check(bs, res);
+}
+
+#define COMMIT_BUF_SECTORS 2048
+
+/* commit COW file into the raw image */
+int bdrv_commit(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ BlockDriver *backing_drv;
+ int64_t sector, total_sectors;
+ int n, ro, open_flags;
+ int ret = 0, rw_ret = 0;
+ uint8_t *buf;
+ char filename[1024];
+ BlockDriverState *bs_rw, *bs_ro;
+
+ if (!drv)
+ return -ENOMEDIUM;
+
+ if (!bs->backing_hd) {
+ return -ENOTSUP;
+ }
+
+ if (bs->backing_hd->keep_read_only) {
+ return -EACCES;
+ }
+
+ backing_drv = bs->backing_hd->drv;
+ ro = bs->backing_hd->read_only;
+ strncpy(filename, bs->backing_hd->filename, sizeof(filename));
+ open_flags = bs->backing_hd->open_flags;
+
+ if (ro) {
+ /* re-open as RW */
+ bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ bs_rw = bdrv_new("");
+ rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR,
+ backing_drv);
+ if (rw_ret < 0) {
+ bdrv_delete(bs_rw);
+ /* try to re-open read-only */
+ bs_ro = bdrv_new("");
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
+ backing_drv);
+ if (ret < 0) {
+ bdrv_delete(bs_ro);
+ /* drive not functional anymore */
+ bs->drv = NULL;
+ return ret;
+ }
+ bs->backing_hd = bs_ro;
+ return rw_ret;
+ }
+ bs->backing_hd = bs_rw;
+ }
+
+ total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
+ buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
+
+ for (sector = 0; sector < total_sectors; sector += n) {
+ if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
+
+ if (bdrv_read(bs, sector, buf, n) != 0) {
+ ret = -EIO;
+ goto ro_cleanup;
+ }
+
+ if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) {
+ ret = -EIO;
+ goto ro_cleanup;
+ }
+ }
+ }
+
+ if (drv->bdrv_make_empty) {
+ ret = drv->bdrv_make_empty(bs);
+ bdrv_flush(bs);
+ }
+
+ /*
+ * Make sure all data we wrote to the backing device is actually
+ * stable on disk.
+ */
+ if (bs->backing_hd)
+ bdrv_flush(bs->backing_hd);
+
+ro_cleanup:
+ qemu_free(buf);
+
+ if (ro) {
+ /* re-open as RO */
+ bdrv_delete(bs->backing_hd);
+ bs->backing_hd = NULL;
+ bs_ro = bdrv_new("");
+ ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR,
+ backing_drv);
+ if (ret < 0) {
+ bdrv_delete(bs_ro);
+ /* drive not functional anymore */
+ bs->drv = NULL;
+ return ret;
+ }
+ bs->backing_hd = bs_ro;
+ bs->backing_hd->keep_read_only = 0;
+ }
+
+ return ret;
+}
+
+void bdrv_commit_all(void)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ bdrv_commit(bs);
+ }
+}
+
+/*
+ * Return values:
+ * 0 - success
+ * -EINVAL - backing format specified, but no file
+ * -ENOSPC - can't update the backing file because no space is left in the
+ * image file header
+ * -ENOTSUP - format driver doesn't support changing the backing file
+ */
+int bdrv_change_backing_file(BlockDriverState *bs,
+ const char *backing_file, const char *backing_fmt)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (drv->bdrv_change_backing_file != NULL) {
+ return drv->bdrv_change_backing_file(bs, backing_file, backing_fmt);
+ } else {
+ return -ENOTSUP;
+ }
+}
+
+static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
+ size_t size)
+{
+ int64_t len;
+
+ if (!bdrv_is_inserted(bs))
+ return -ENOMEDIUM;
+
+ if (bs->growable)
+ return 0;
+
+ len = bdrv_getlength(bs);
+
+ if (offset < 0)
+ return -EIO;
+
+ if ((offset > len) || (len - offset < size))
+ return -EIO;
+
+ return 0;
+}
+
+static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors)
+{
+ return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
+ nb_sectors * BDRV_SECTOR_SIZE);
+}
+
+/* return < 0 if error. See bdrv_write() for the return codes */
+int bdrv_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv)
+ return -ENOMEDIUM;
+ if (bdrv_check_request(bs, sector_num, nb_sectors))
+ return -EIO;
+
+ return drv->bdrv_read(bs, sector_num, buf, nb_sectors);
+}
+
+static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int dirty)
+{
+ int64_t start, end;
+ unsigned long val, idx, bit;
+
+ start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
+ end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
+
+ for (; start <= end; start++) {
+ idx = start / (sizeof(unsigned long) * 8);
+ bit = start % (sizeof(unsigned long) * 8);
+ val = bs->dirty_bitmap[idx];
+ if (dirty) {
+ if (!(val & (1UL << bit))) {
+ bs->dirty_count++;
+ val |= 1UL << bit;
+ }
+ } else {
+ if (val & (1UL << bit)) {
+ bs->dirty_count--;
+ val &= ~(1UL << bit);
+ }
+ }
+ bs->dirty_bitmap[idx] = val;
+ }
+}
+
+/* Return < 0 if error. Important errors are:
+ -EIO generic I/O error (may happen for all errors)
+ -ENOMEDIUM No media inserted.
+ -EINVAL Invalid sector number or nb_sectors
+ -EACCES Trying to write a read-only device
+*/
+int bdrv_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BlockDriver *drv = bs->drv;
+ if (!bs->drv)
+ return -ENOMEDIUM;
+ if (bs->read_only)
+ return -EACCES;
+ if (bdrv_check_request(bs, sector_num, nb_sectors))
+ return -EIO;
+
+ if (bs->dirty_bitmap) {
+ set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
+ }
+
+ if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
+ bs->wr_highest_sector = sector_num + nb_sectors - 1;
+ }
+
+ return drv->bdrv_write(bs, sector_num, buf, nb_sectors);
+}
+
+int bdrv_pread(BlockDriverState *bs, int64_t offset,
+ void *buf, int count1)
+{
+ uint8_t tmp_buf[BDRV_SECTOR_SIZE];
+ int len, nb_sectors, count;
+ int64_t sector_num;
+ int ret;
+
+ count = count1;
+ /* first read to align to sector start */
+ len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
+ if (len > count)
+ len = count;
+ sector_num = offset >> BDRV_SECTOR_BITS;
+ if (len > 0) {
+ if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
+ return ret;
+ memcpy(buf, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), len);
+ count -= len;
+ if (count == 0)
+ return count1;
+ sector_num++;
+ buf += len;
+ }
+
+ /* read the sectors "in place" */
+ nb_sectors = count >> BDRV_SECTOR_BITS;
+ if (nb_sectors > 0) {
+ if ((ret = bdrv_read(bs, sector_num, buf, nb_sectors)) < 0)
+ return ret;
+ sector_num += nb_sectors;
+ len = nb_sectors << BDRV_SECTOR_BITS;
+ buf += len;
+ count -= len;
+ }
+
+ /* add data from the last sector */
+ if (count > 0) {
+ if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
+ return ret;
+ memcpy(buf, tmp_buf, count);
+ }
+ return count1;
+}
+
+int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+ const void *buf, int count1)
+{
+ uint8_t tmp_buf[BDRV_SECTOR_SIZE];
+ int len, nb_sectors, count;
+ int64_t sector_num;
+ int ret;
+
+ count = count1;
+ /* first write to align to sector start */
+ len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1);
+ if (len > count)
+ len = count;
+ sector_num = offset >> BDRV_SECTOR_BITS;
+ if (len > 0) {
+ if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
+ return ret;
+ memcpy(tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), buf, len);
+ if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
+ return ret;
+ count -= len;
+ if (count == 0)
+ return count1;
+ sector_num++;
+ buf += len;
+ }
+
+ /* write the sectors "in place" */
+ nb_sectors = count >> BDRV_SECTOR_BITS;
+ if (nb_sectors > 0) {
+ if ((ret = bdrv_write(bs, sector_num, buf, nb_sectors)) < 0)
+ return ret;
+ sector_num += nb_sectors;
+ len = nb_sectors << BDRV_SECTOR_BITS;
+ buf += len;
+ count -= len;
+ }
+
+ /* add data from the last sector */
+ if (count > 0) {
+ if ((ret = bdrv_read(bs, sector_num, tmp_buf, 1)) < 0)
+ return ret;
+ memcpy(tmp_buf, buf, count);
+ if ((ret = bdrv_write(bs, sector_num, tmp_buf, 1)) < 0)
+ return ret;
+ }
+ return count1;
+}
+
+/*
+ * Writes to the file and ensures that no writes are reordered across this
+ * request (acts as a barrier)
+ *
+ * Returns 0 on success, -errno in error cases.
+ */
+int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
+ const void *buf, int count)
+{
+ int ret;
+
+ ret = bdrv_pwrite(bs, offset, buf, count);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* No flush needed for cache=writethrough, it uses O_DSYNC */
+ if ((bs->open_flags & BDRV_O_CACHE_MASK) != 0) {
+ bdrv_flush(bs);
+ }
+
+ return 0;
+}
+
+/*
+ * Writes to the file and ensures that no writes are reordered across this
+ * request (acts as a barrier)
+ *
+ * Returns 0 on success, -errno in error cases.
+ */
+int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ return bdrv_pwrite_sync(bs, BDRV_SECTOR_SIZE * sector_num,
+ buf, BDRV_SECTOR_SIZE * nb_sectors);
+}
+
+/**
+ * Truncate file to 'offset' bytes (needed only for file protocols)
+ */
+int bdrv_truncate(BlockDriverState *bs, int64_t offset)
+{
+ BlockDriver *drv = bs->drv;
+ int ret;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (!drv->bdrv_truncate)
+ return -ENOTSUP;
+ if (bs->read_only)
+ return -EACCES;
+ if (bdrv_in_use(bs))
+ return -EBUSY;
+ ret = drv->bdrv_truncate(bs, offset);
+ if (ret == 0) {
+ ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
+ if (bs->change_cb) {
+ bs->change_cb(bs->change_opaque, CHANGE_SIZE);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Length of a file in bytes. Return < 0 if error or unknown.
+ */
+int64_t bdrv_getlength(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+
+ /* Fixed size devices use the total_sectors value for speed instead of
+ issuing a length query (like lseek) on each call. Also, legacy block
+ drivers don't provide a bdrv_getlength function and must use
+ total_sectors. */
+ if (!bs->growable || !drv->bdrv_getlength) {
+ return bs->total_sectors * BDRV_SECTOR_SIZE;
+ }
+ return drv->bdrv_getlength(bs);
+}
+
+/* return 0 as number of sectors if no device present or error */
+void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr)
+{
+ int64_t length;
+ length = bdrv_getlength(bs);
+ if (length < 0)
+ length = 0;
+ else
+ length = length >> BDRV_SECTOR_BITS;
+ *nb_sectors_ptr = length;
+}
+
+struct partition {
+ uint8_t boot_ind; /* 0x80 - active */
+ uint8_t head; /* starting head */
+ uint8_t sector; /* starting sector */
+ uint8_t cyl; /* starting cylinder */
+ uint8_t sys_ind; /* What partition type */
+ uint8_t end_head; /* end head */
+ uint8_t end_sector; /* end sector */
+ uint8_t end_cyl; /* end cylinder */
+ uint32_t start_sect; /* starting sector counting from 0 */
+ uint32_t nr_sects; /* nr of sectors in partition */
+} __attribute__((packed));
+
+/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */
+static int guess_disk_lchs(BlockDriverState *bs,
+ int *pcylinders, int *pheads, int *psectors)
+{
+ uint8_t buf[BDRV_SECTOR_SIZE];
+ int ret, i, heads, sectors, cylinders;
+ struct partition *p;
+ uint32_t nr_sects;
+ uint64_t nb_sectors;
+
+ bdrv_get_geometry(bs, &nb_sectors);
+
+ ret = bdrv_read(bs, 0, buf, 1);
+ if (ret < 0)
+ return -1;
+ /* test msdos magic */
+ if (buf[510] != 0x55 || buf[511] != 0xaa)
+ return -1;
+ for(i = 0; i < 4; i++) {
+ p = ((struct partition *)(buf + 0x1be)) + i;
+ nr_sects = le32_to_cpu(p->nr_sects);
+ if (nr_sects && p->end_head) {
+ /* We make the assumption that the partition terminates on
+ a cylinder boundary */
+ heads = p->end_head + 1;
+ sectors = p->end_sector & 63;
+ if (sectors == 0)
+ continue;
+ cylinders = nb_sectors / (heads * sectors);
+ if (cylinders < 1 || cylinders > 16383)
+ continue;
+ *pheads = heads;
+ *psectors = sectors;
+ *pcylinders = cylinders;
+#if 0
+ printf("guessed geometry: LCHS=%d %d %d\n",
+ cylinders, heads, sectors);
+#endif
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs)
+{
+ int translation, lba_detected = 0;
+ int cylinders, heads, secs;
+ uint64_t nb_sectors;
+
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry(bs, &nb_sectors);
+ bdrv_get_geometry_hint(bs, &cylinders, &heads, &secs);
+ translation = bdrv_get_translation_hint(bs);
+ if (cylinders != 0) {
+ *pcyls = cylinders;
+ *pheads = heads;
+ *psecs = secs;
+ } else {
+ if (guess_disk_lchs(bs, &cylinders, &heads, &secs) == 0) {
+ if (heads > 16) {
+ /* if heads > 16, it means that a BIOS LBA
+ translation was active, so the default
+ hardware geometry is OK */
+ lba_detected = 1;
+ goto default_geometry;
+ } else {
+ *pcyls = cylinders;
+ *pheads = heads;
+ *psecs = secs;
+ /* disable any translation to be in sync with
+ the logical geometry */
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_set_translation_hint(bs,
+ BIOS_ATA_TRANSLATION_NONE);
+ }
+ }
+ } else {
+ default_geometry:
+ /* if no geometry, use a standard physical disk geometry */
+ cylinders = nb_sectors / (16 * 63);
+
+ if (cylinders > 16383)
+ cylinders = 16383;
+ else if (cylinders < 2)
+ cylinders = 2;
+ *pcyls = cylinders;
+ *pheads = 16;
+ *psecs = 63;
+ if ((lba_detected == 1) && (translation == BIOS_ATA_TRANSLATION_AUTO)) {
+ if ((*pcyls * *pheads) <= 131072) {
+ bdrv_set_translation_hint(bs,
+ BIOS_ATA_TRANSLATION_LARGE);
+ } else {
+ bdrv_set_translation_hint(bs,
+ BIOS_ATA_TRANSLATION_LBA);
+ }
+ }
+ }
+ bdrv_set_geometry_hint(bs, *pcyls, *pheads, *psecs);
+ }
+}
+
+void bdrv_set_geometry_hint(BlockDriverState *bs,
+ int cyls, int heads, int secs)
+{
+ bs->cyls = cyls;
+ bs->heads = heads;
+ 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;
+}
+
+void bdrv_get_geometry_hint(BlockDriverState *bs,
+ int *pcyls, int *pheads, int *psecs)
+{
+ *pcyls = bs->cyls;
+ *pheads = bs->heads;
+ *psecs = bs->secs;
+}
+
+int bdrv_get_type_hint(BlockDriverState *bs)
+{
+ return bs->type;
+}
+
+int bdrv_get_translation_hint(BlockDriverState *bs)
+{
+ return bs->translation;
+}
+
+void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
+ BlockErrorAction on_write_error)
+{
+ bs->on_read_error = on_read_error;
+ bs->on_write_error = on_write_error;
+}
+
+BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read)
+{
+ return is_read ? bs->on_read_error : bs->on_write_error;
+}
+
+void bdrv_set_removable(BlockDriverState *bs, int removable)
+{
+ bs->removable = removable;
+ if (removable && bs == bs_snapshots) {
+ bs_snapshots = NULL;
+ }
+}
+
+int bdrv_is_removable(BlockDriverState *bs)
+{
+ return bs->removable;
+}
+
+int bdrv_is_read_only(BlockDriverState *bs)
+{
+ return bs->read_only;
+}
+
+int bdrv_is_sg(BlockDriverState *bs)
+{
+ return bs->sg;
+}
+
+int bdrv_enable_write_cache(BlockDriverState *bs)
+{
+ return bs->enable_write_cache;
+}
+
+/* XXX: no longer used */
+void bdrv_set_change_cb(BlockDriverState *bs,
+ void (*change_cb)(void *opaque, int reason),
+ void *opaque)
+{
+ bs->change_cb = change_cb;
+ bs->change_opaque = opaque;
+}
+
+int bdrv_is_encrypted(BlockDriverState *bs)
+{
+ if (bs->backing_hd && bs->backing_hd->encrypted)
+ return 1;
+ return bs->encrypted;
+}
+
+int bdrv_key_required(BlockDriverState *bs)
+{
+ BlockDriverState *backing_hd = bs->backing_hd;
+
+ if (backing_hd && backing_hd->encrypted && !backing_hd->valid_key)
+ return 1;
+ return (bs->encrypted && !bs->valid_key);
+}
+
+int bdrv_set_key(BlockDriverState *bs, const char *key)
+{
+ int ret;
+ if (bs->backing_hd && bs->backing_hd->encrypted) {
+ ret = bdrv_set_key(bs->backing_hd, key);
+ if (ret < 0)
+ return ret;
+ if (!bs->encrypted)
+ return 0;
+ }
+ if (!bs->encrypted) {
+ return -EINVAL;
+ } else if (!bs->drv || !bs->drv->bdrv_set_key) {
+ return -ENOMEDIUM;
+ }
+ ret = bs->drv->bdrv_set_key(bs, key);
+ if (ret < 0) {
+ bs->valid_key = 0;
+ } else if (!bs->valid_key) {
+ bs->valid_key = 1;
+ /* call the change callback now, we skipped it on open */
+ bs->media_changed = 1;
+ if (bs->change_cb)
+ bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
+ }
+ return ret;
+}
+
+void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size)
+{
+ if (!bs->drv) {
+ buf[0] = '\0';
+ } else {
+ pstrcpy(buf, buf_size, bs->drv->format_name);
+ }
+}
+
+void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
+ void *opaque)
+{
+ BlockDriver *drv;
+
+ QLIST_FOREACH(drv, &bdrv_drivers, list) {
+ it(opaque, drv->format_name);
+ }
+}
+
+BlockDriverState *bdrv_find(const char *name)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ if (!strcmp(name, bs->device_name)) {
+ return bs;
+ }
+ }
+ return NULL;
+}
+
+BlockDriverState *bdrv_next(BlockDriverState *bs)
+{
+ if (!bs) {
+ return QTAILQ_FIRST(&bdrv_states);
+ }
+ return QTAILQ_NEXT(bs, list);
+}
+
+void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ it(opaque, bs);
+ }
+}
+
+const char *bdrv_get_device_name(BlockDriverState *bs)
+{
+ return bs->device_name;
+}
+
+int bdrv_flush(BlockDriverState *bs)
+{
+ if (bs->open_flags & BDRV_O_NO_FLUSH) {
+ return 0;
+ }
+
+ if (bs->drv && bs->drv->bdrv_flush) {
+ return bs->drv->bdrv_flush(bs);
+ }
+
+ /*
+ * Some block drivers always operate in either writethrough or unsafe mode
+ * and don't support bdrv_flush therefore. Usually qemu doesn't know how
+ * the server works (because the behaviour is hardcoded or depends on
+ * server-side configuration), so we can't ensure that everything is safe
+ * on disk. Returning an error doesn't work because that would break guests
+ * even if the server operates in writethrough mode.
+ *
+ * Let's hope the user knows what he's doing.
+ */
+ return 0;
+}
+
+void bdrv_flush_all(void)
+{
+ BlockDriverState *bs;
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ if (bs->drv && !bdrv_is_read_only(bs) &&
+ (!bdrv_is_removable(bs) || bdrv_is_inserted(bs))) {
+ bdrv_flush(bs);
+ }
+ }
+}
+
+int bdrv_has_zero_init(BlockDriverState *bs)
+{
+ assert(bs->drv);
+
+ if (bs->drv->bdrv_has_zero_init) {
+ return bs->drv->bdrv_has_zero_init(bs);
+ }
+
+ return 1;
+}
+
+int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+{
+ if (!bs->drv) {
+ return -ENOMEDIUM;
+ }
+ if (!bs->drv->bdrv_discard) {
+ return 0;
+ }
+ return bs->drv->bdrv_discard(bs, sector_num, nb_sectors);
+}
+
+/*
+ * Returns true iff the specified sector is present in the disk image. Drivers
+ * not implementing the functionality are assumed to not support backing files,
+ * hence all their sectors are reported as allocated.
+ *
+ * 'pnum' is set to the number of sectors (including and immediately following
+ * the specified sector) that are known to be in the same
+ * allocated/unallocated state.
+ *
+ * 'nb_sectors' is the max value 'pnum' should be set to.
+ */
+int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
+ int *pnum)
+{
+ int64_t n;
+ if (!bs->drv->bdrv_is_allocated) {
+ if (sector_num >= bs->total_sectors) {
+ *pnum = 0;
+ return 0;
+ }
+ n = bs->total_sectors - sector_num;
+ *pnum = (n < nb_sectors) ? (n) : (nb_sectors);
+ return 1;
+ }
+ return bs->drv->bdrv_is_allocated(bs, sector_num, nb_sectors, pnum);
+}
+
+void bdrv_mon_event(const BlockDriverState *bdrv,
+ BlockMonEventAction action, int is_read)
+{
+ QObject *data;
+ const char *action_str;
+
+ switch (action) {
+ case BDRV_ACTION_REPORT:
+ action_str = "report";
+ break;
+ case BDRV_ACTION_IGNORE:
+ action_str = "ignore";
+ break;
+ case BDRV_ACTION_STOP:
+ action_str = "stop";
+ break;
+ default:
+ abort();
+ }
+
+ data = qobject_from_jsonf("{ 'device': %s, 'action': %s, 'operation': %s }",
+ bdrv->device_name,
+ action_str,
+ is_read ? "read" : "write");
+ monitor_protocol_event(QEVENT_BLOCK_IO_ERROR, data);
+
+ qobject_decref(data);
+}
+
+static void bdrv_print_dict(QObject *obj, void *opaque)
+{
+ QDict *bs_dict;
+ Monitor *mon = opaque;
+
+ bs_dict = qobject_to_qdict(obj);
+
+ monitor_printf(mon, "%s: type=%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")) {
+ monitor_printf(mon, " locked=%d", qdict_get_bool(bs_dict, "locked"));
+ }
+
+ if (qdict_haskey(bs_dict, "inserted")) {
+ QDict *qdict = qobject_to_qdict(qdict_get(bs_dict, "inserted"));
+
+ monitor_printf(mon, " file=");
+ monitor_print_filename(mon, qdict_get_str(qdict, "file"));
+ if (qdict_haskey(qdict, "backing_file")) {
+ monitor_printf(mon, " backing_file=");
+ monitor_print_filename(mon, qdict_get_str(qdict, "backing_file"));
+ }
+ monitor_printf(mon, " ro=%d drv=%s encrypted=%d",
+ qdict_get_bool(qdict, "ro"),
+ qdict_get_str(qdict, "drv"),
+ qdict_get_bool(qdict, "encrypted"));
+ } else {
+ monitor_printf(mon, " [not inserted]");
+ }
+
+ monitor_printf(mon, "\n");
+}
+
+void bdrv_info_print(Monitor *mon, const QObject *data)
+{
+ qlist_iter(qobject_to_qlist(data), bdrv_print_dict, mon);
+}
+
+void bdrv_info(Monitor *mon, QObject **ret_data)
+{
+ QList *bs_list;
+ BlockDriverState *bs;
+
+ bs_list = qlist_new();
+
+ 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, "
+ "'removable': %i, 'locked': %i }",
+ bs->device_name, type, bs->removable,
+ bs->locked);
+
+ if (bs->drv) {
+ QObject *obj;
+ QDict *bs_dict = qobject_to_qdict(bs_obj);
+
+ obj = qobject_from_jsonf("{ 'file': %s, 'ro': %i, 'drv': %s, "
+ "'encrypted': %i }",
+ bs->filename, bs->read_only,
+ bs->drv->format_name,
+ bdrv_is_encrypted(bs));
+ if (bs->backing_file[0] != '\0') {
+ QDict *qdict = qobject_to_qdict(obj);
+ qdict_put(qdict, "backing_file",
+ qstring_from_str(bs->backing_file));
+ }
+
+ qdict_put_obj(bs_dict, "inserted", obj);
+ }
+ qlist_append_obj(bs_list, bs_obj);
+ }
+
+ *ret_data = QOBJECT(bs_list);
+}
+
+static void bdrv_stats_iter(QObject *data, void *opaque)
+{
+ QDict *qdict;
+ Monitor *mon = opaque;
+
+ qdict = qobject_to_qdict(data);
+ monitor_printf(mon, "%s:", qdict_get_str(qdict, "device"));
+
+ qdict = qobject_to_qdict(qdict_get(qdict, "stats"));
+ monitor_printf(mon, " rd_bytes=%" PRId64
+ " wr_bytes=%" PRId64
+ " rd_operations=%" PRId64
+ " wr_operations=%" PRId64
+ "\n",
+ qdict_get_int(qdict, "rd_bytes"),
+ qdict_get_int(qdict, "wr_bytes"),
+ qdict_get_int(qdict, "rd_operations"),
+ qdict_get_int(qdict, "wr_operations"));
+}
+
+void bdrv_stats_print(Monitor *mon, const QObject *data)
+{
+ qlist_iter(qobject_to_qlist(data), bdrv_stats_iter, mon);
+}
+
+static QObject* bdrv_info_stats_bs(BlockDriverState *bs)
+{
+ QObject *res;
+ QDict *dict;
+
+ res = qobject_from_jsonf("{ 'stats': {"
+ "'rd_bytes': %" PRId64 ","
+ "'wr_bytes': %" PRId64 ","
+ "'rd_operations': %" PRId64 ","
+ "'wr_operations': %" PRId64 ","
+ "'wr_highest_offset': %" PRId64
+ "} }",
+ bs->rd_bytes, bs->wr_bytes,
+ bs->rd_ops, bs->wr_ops,
+ bs->wr_highest_sector *
+ (uint64_t)BDRV_SECTOR_SIZE);
+ dict = qobject_to_qdict(res);
+
+ if (*bs->device_name) {
+ qdict_put(dict, "device", qstring_from_str(bs->device_name));
+ }
+
+ if (bs->file) {
+ QObject *parent = bdrv_info_stats_bs(bs->file);
+ qdict_put_obj(dict, "parent", parent);
+ }
+
+ return res;
+}
+
+void bdrv_info_stats(Monitor *mon, QObject **ret_data)
+{
+ QObject *obj;
+ QList *devices;
+ BlockDriverState *bs;
+
+ devices = qlist_new();
+
+ QTAILQ_FOREACH(bs, &bdrv_states, list) {
+ obj = bdrv_info_stats_bs(bs);
+ qlist_append_obj(devices, obj);
+ }
+
+ *ret_data = QOBJECT(devices);
+}
+
+const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
+{
+ if (bs->backing_hd && bs->backing_hd->encrypted)
+ return bs->backing_file;
+ else if (bs->encrypted)
+ return bs->filename;
+ else
+ return NULL;
+}
+
+void bdrv_get_backing_filename(BlockDriverState *bs,
+ char *filename, int filename_size)
+{
+ if (!bs->backing_file) {
+ pstrcpy(filename, filename_size, "");
+ } else {
+ pstrcpy(filename, filename_size, bs->backing_file);
+ }
+}
+
+int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (!drv->bdrv_write_compressed)
+ return -ENOTSUP;
+ if (bdrv_check_request(bs, sector_num, nb_sectors))
+ return -EIO;
+
+ if (bs->dirty_bitmap) {
+ set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
+ }
+
+ return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
+}
+
+int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (!drv->bdrv_get_info)
+ return -ENOTSUP;
+ memset(bdi, 0, sizeof(*bdi));
+ return drv->bdrv_get_info(bs, bdi);
+}
+
+int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (drv->bdrv_save_vmstate)
+ return drv->bdrv_save_vmstate(bs, buf, pos, size);
+ if (bs->file)
+ return bdrv_save_vmstate(bs->file, buf, pos, size);
+ return -ENOTSUP;
+}
+
+int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
+ int64_t pos, int size)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (drv->bdrv_load_vmstate)
+ return drv->bdrv_load_vmstate(bs, buf, pos, size);
+ if (bs->file)
+ return bdrv_load_vmstate(bs->file, buf, pos, size);
+ return -ENOTSUP;
+}
+
+void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv || !drv->bdrv_debug_event) {
+ return;
+ }
+
+ return drv->bdrv_debug_event(bs, event);
+
+}
+
+/**************************************************************/
+/* handling of snapshots */
+
+int bdrv_can_snapshot(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv || bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ return 0;
+ }
+
+ if (!drv->bdrv_snapshot_create) {
+ if (bs->file != NULL) {
+ return bdrv_can_snapshot(bs->file);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+int bdrv_is_snapshot(BlockDriverState *bs)
+{
+ return !!(bs->open_flags & BDRV_O_SNAPSHOT);
+}
+
+BlockDriverState *bdrv_snapshots(void)
+{
+ BlockDriverState *bs;
+
+ if (bs_snapshots) {
+ return bs_snapshots;
+ }
+
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+ if (bdrv_can_snapshot(bs)) {
+ bs_snapshots = bs;
+ return bs;
+ }
+ }
+ return NULL;
+}
+
+int bdrv_snapshot_create(BlockDriverState *bs,
+ QEMUSnapshotInfo *sn_info)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (drv->bdrv_snapshot_create)
+ return drv->bdrv_snapshot_create(bs, sn_info);
+ if (bs->file)
+ return bdrv_snapshot_create(bs->file, sn_info);
+ return -ENOTSUP;
+}
+
+int bdrv_snapshot_goto(BlockDriverState *bs,
+ const char *snapshot_id)
+{
+ BlockDriver *drv = bs->drv;
+ int ret, open_ret;
+
+ if (!drv)
+ return -ENOMEDIUM;
+ if (drv->bdrv_snapshot_goto)
+ return drv->bdrv_snapshot_goto(bs, snapshot_id);
+
+ if (bs->file) {
+ drv->bdrv_close(bs);
+ ret = bdrv_snapshot_goto(bs->file, snapshot_id);
+ open_ret = drv->bdrv_open(bs, bs->open_flags);
+ if (open_ret < 0) {
+ bdrv_delete(bs->file);
+ bs->drv = NULL;
+ return open_ret;
+ }
+ return ret;
+ }
+
+ return -ENOTSUP;
+}
+
+int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (drv->bdrv_snapshot_delete)
+ return drv->bdrv_snapshot_delete(bs, snapshot_id);
+ if (bs->file)
+ return bdrv_snapshot_delete(bs->file, snapshot_id);
+ return -ENOTSUP;
+}
+
+int bdrv_snapshot_list(BlockDriverState *bs,
+ QEMUSnapshotInfo **psn_info)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv)
+ return -ENOMEDIUM;
+ if (drv->bdrv_snapshot_list)
+ return drv->bdrv_snapshot_list(bs, psn_info);
+ if (bs->file)
+ return bdrv_snapshot_list(bs->file, psn_info);
+ return -ENOTSUP;
+}
+
+int bdrv_snapshot_load_tmp(BlockDriverState *bs,
+ const char *snapshot_name)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv) {
+ return -ENOMEDIUM;
+ }
+ if (!bs->read_only) {
+ return -EINVAL;
+ }
+ if (drv->bdrv_snapshot_load_tmp) {
+ return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
+ }
+ return -ENOTSUP;
+}
+
+#define NB_SUFFIXES 4
+
+char *get_human_readable_size(char *buf, int buf_size, int64_t size)
+{
+ static const char suffixes[NB_SUFFIXES] = "KMGT";
+ int64_t base;
+ int i;
+
+ if (size <= 999) {
+ snprintf(buf, buf_size, "%" PRId64, size);
+ } else {
+ base = 1024;
+ for(i = 0; i < NB_SUFFIXES; i++) {
+ if (size < (10 * base)) {
+ snprintf(buf, buf_size, "%0.1f%c",
+ (double)size / base,
+ suffixes[i]);
+ break;
+ } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
+ snprintf(buf, buf_size, "%" PRId64 "%c",
+ ((size + (base >> 1)) / base),
+ suffixes[i]);
+ break;
+ }
+ base = base * 1024;
+ }
+ }
+ return buf;
+}
+
+char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
+{
+ char buf1[128], date_buf[128], clock_buf[128];
+#ifdef _WIN32
+ struct tm *ptm;
+#else
+ struct tm tm;
+#endif
+ time_t ti;
+ int64_t secs;
+
+ if (!sn) {
+ snprintf(buf, buf_size,
+ "%-10s%-20s%7s%20s%15s",
+ "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
+ } else {
+ ti = sn->date_sec;
+#ifdef _WIN32
+ ptm = localtime(&ti);
+ strftime(date_buf, sizeof(date_buf),
+ "%Y-%m-%d %H:%M:%S", ptm);
+#else
+ localtime_r(&ti, &tm);
+ strftime(date_buf, sizeof(date_buf),
+ "%Y-%m-%d %H:%M:%S", &tm);
+#endif
+ secs = sn->vm_clock_nsec / 1000000000;
+ snprintf(clock_buf, sizeof(clock_buf),
+ "%02d:%02d:%02d.%03d",
+ (int)(secs / 3600),
+ (int)((secs / 60) % 60),
+ (int)(secs % 60),
+ (int)((sn->vm_clock_nsec / 1000000) % 1000));
+ snprintf(buf, buf_size,
+ "%-10s%-20s%7s%20s%15s",
+ sn->id_str, sn->name,
+ get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size),
+ date_buf,
+ clock_buf);
+ }
+ return buf;
+}
+
+
+/**************************************************************/
+/* async I/Os */
+
+BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriver *drv = bs->drv;
+ BlockDriverAIOCB *ret;
+
+ trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
+
+ if (!drv)
+ return NULL;
+ if (bdrv_check_request(bs, sector_num, nb_sectors))
+ return NULL;
+
+ ret = drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors,
+ cb, opaque);
+
+ if (ret) {
+ /* Update stats even though technically transfer has not happened. */
+ bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
+ bs->rd_ops ++;
+ }
+
+ return ret;
+}
+
+typedef struct BlockCompleteData {
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+ BlockDriverState *bs;
+ int64_t sector_num;
+ int nb_sectors;
+} BlockCompleteData;
+
+static void block_complete_cb(void *opaque, int ret)
+{
+ BlockCompleteData *b = opaque;
+
+ if (b->bs->dirty_bitmap) {
+ set_dirty_bitmap(b->bs, b->sector_num, b->nb_sectors, 1);
+ }
+ b->cb(b->opaque, ret);
+ qemu_free(b);
+}
+
+static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs,
+ int64_t sector_num,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BlockCompleteData *blkdata = qemu_mallocz(sizeof(BlockCompleteData));
+
+ blkdata->bs = bs;
+ blkdata->cb = cb;
+ blkdata->opaque = opaque;
+ blkdata->sector_num = sector_num;
+ blkdata->nb_sectors = nb_sectors;
+
+ return blkdata;
+}
+
+BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriver *drv = bs->drv;
+ BlockDriverAIOCB *ret;
+ BlockCompleteData *blk_cb_data;
+
+ trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
+
+ if (!drv)
+ return NULL;
+ if (bs->read_only)
+ return NULL;
+ if (bdrv_check_request(bs, sector_num, nb_sectors))
+ return NULL;
+
+ if (bs->dirty_bitmap) {
+ blk_cb_data = blk_dirty_cb_alloc(bs, sector_num, nb_sectors, cb,
+ opaque);
+ cb = &block_complete_cb;
+ opaque = blk_cb_data;
+ }
+
+ ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors,
+ cb, opaque);
+
+ if (ret) {
+ /* Update stats even though technically transfer has not happened. */
+ bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
+ bs->wr_ops ++;
+ if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
+ bs->wr_highest_sector = sector_num + nb_sectors - 1;
+ }
+ }
+
+ return ret;
+}
+
+
+typedef struct MultiwriteCB {
+ int error;
+ int num_requests;
+ int num_callbacks;
+ struct {
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+ QEMUIOVector *free_qiov;
+ void *free_buf;
+ } callbacks[];
+} MultiwriteCB;
+
+static void multiwrite_user_cb(MultiwriteCB *mcb)
+{
+ int i;
+
+ for (i = 0; i < mcb->num_callbacks; i++) {
+ mcb->callbacks[i].cb(mcb->callbacks[i].opaque, mcb->error);
+ if (mcb->callbacks[i].free_qiov) {
+ qemu_iovec_destroy(mcb->callbacks[i].free_qiov);
+ }
+ qemu_free(mcb->callbacks[i].free_qiov);
+ qemu_vfree(mcb->callbacks[i].free_buf);
+ }
+}
+
+static void multiwrite_cb(void *opaque, int ret)
+{
+ MultiwriteCB *mcb = opaque;
+
+ trace_multiwrite_cb(mcb, ret);
+
+ if (ret < 0 && !mcb->error) {
+ mcb->error = ret;
+ }
+
+ mcb->num_requests--;
+ if (mcb->num_requests == 0) {
+ multiwrite_user_cb(mcb);
+ qemu_free(mcb);
+ }
+}
+
+static int multiwrite_req_compare(const void *a, const void *b)
+{
+ const BlockRequest *req1 = a, *req2 = b;
+
+ /*
+ * Note that we can't simply subtract req2->sector from req1->sector
+ * here as that could overflow the return value.
+ */
+ if (req1->sector > req2->sector) {
+ return 1;
+ } else if (req1->sector < req2->sector) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Takes a bunch of requests and tries to merge them. Returns the number of
+ * requests that remain after merging.
+ */
+static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
+ int num_reqs, MultiwriteCB *mcb)
+{
+ int i, outidx;
+
+ // Sort requests by start sector
+ qsort(reqs, num_reqs, sizeof(*reqs), &multiwrite_req_compare);
+
+ // Check if adjacent requests touch the same clusters. If so, combine them,
+ // filling up gaps with zero sectors.
+ outidx = 0;
+ for (i = 1; i < num_reqs; i++) {
+ int merge = 0;
+ int64_t oldreq_last = reqs[outidx].sector + reqs[outidx].nb_sectors;
+
+ // This handles the cases that are valid for all block drivers, namely
+ // exactly sequential writes and overlapping writes.
+ if (reqs[i].sector <= oldreq_last) {
+ merge = 1;
+ }
+
+ // The block driver may decide that it makes sense to combine requests
+ // even if there is a gap of some sectors between them. In this case,
+ // the gap is filled with zeros (therefore only applicable for yet
+ // unused space in format like qcow2).
+ if (!merge && bs->drv->bdrv_merge_requests) {
+ merge = bs->drv->bdrv_merge_requests(bs, &reqs[outidx], &reqs[i]);
+ }
+
+ if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) {
+ merge = 0;
+ }
+
+ if (merge) {
+ size_t size;
+ QEMUIOVector *qiov = qemu_mallocz(sizeof(*qiov));
+ qemu_iovec_init(qiov,
+ reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1);
+
+ // Add the first request to the merged one. If the requests are
+ // overlapping, drop the last sectors of the first request.
+ size = (reqs[i].sector - reqs[outidx].sector) << 9;
+ qemu_iovec_concat(qiov, reqs[outidx].qiov, size);
+
+ // We might need to add some zeros between the two requests
+ if (reqs[i].sector > oldreq_last) {
+ size_t zero_bytes = (reqs[i].sector - oldreq_last) << 9;
+ uint8_t *buf = qemu_blockalign(bs, zero_bytes);
+ memset(buf, 0, zero_bytes);
+ qemu_iovec_add(qiov, buf, zero_bytes);
+ mcb->callbacks[i].free_buf = buf;
+ }
+
+ // Add the second request
+ qemu_iovec_concat(qiov, reqs[i].qiov, reqs[i].qiov->size);
+
+ reqs[outidx].nb_sectors = qiov->size >> 9;
+ reqs[outidx].qiov = qiov;
+
+ mcb->callbacks[i].free_qiov = reqs[outidx].qiov;
+ } else {
+ outidx++;
+ reqs[outidx].sector = reqs[i].sector;
+ reqs[outidx].nb_sectors = reqs[i].nb_sectors;
+ reqs[outidx].qiov = reqs[i].qiov;
+ }
+ }
+
+ return outidx + 1;
+}
+
+/*
+ * Submit multiple AIO write requests at once.
+ *
+ * On success, the function returns 0 and all requests in the reqs array have
+ * been submitted. In error case this function returns -1, and any of the
+ * requests may or may not be submitted yet. In particular, this means that the
+ * callback will be called for some of the requests, for others it won't. The
+ * caller must check the error field of the BlockRequest to wait for the right
+ * callbacks (if error != 0, no callback will be called).
+ *
+ * The implementation may modify the contents of the reqs array, e.g. to merge
+ * requests. However, the fields opaque and error are left unmodified as they
+ * are used to signal failure for a single request to the caller.
+ */
+int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
+{
+ BlockDriverAIOCB *acb;
+ MultiwriteCB *mcb;
+ int i;
+
+ /* don't submit writes if we don't have a medium */
+ if (bs->drv == NULL) {
+ for (i = 0; i < num_reqs; i++) {
+ reqs[i].error = -ENOMEDIUM;
+ }
+ return -1;
+ }
+
+ if (num_reqs == 0) {
+ return 0;
+ }
+
+ // Create MultiwriteCB structure
+ mcb = qemu_mallocz(sizeof(*mcb) + num_reqs * sizeof(*mcb->callbacks));
+ mcb->num_requests = 0;
+ mcb->num_callbacks = num_reqs;
+
+ for (i = 0; i < num_reqs; i++) {
+ mcb->callbacks[i].cb = reqs[i].cb;
+ mcb->callbacks[i].opaque = reqs[i].opaque;
+ }
+
+ // Check for mergable requests
+ num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
+
+ trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs);
+
+ /*
+ * Run the aio requests. As soon as one request can't be submitted
+ * successfully, fail all requests that are not yet submitted (we must
+ * return failure for all requests anyway)
+ *
+ * num_requests cannot be set to the right value immediately: If
+ * bdrv_aio_writev fails for some request, num_requests would be too high
+ * and therefore multiwrite_cb() would never recognize the multiwrite
+ * request as completed. We also cannot use the loop variable i to set it
+ * when the first request fails because the callback may already have been
+ * called for previously submitted requests. Thus, num_requests must be
+ * incremented for each request that is submitted.
+ *
+ * The problem that callbacks may be called early also means that we need
+ * to take care that num_requests doesn't become 0 before all requests are
+ * submitted - multiwrite_cb() would consider the multiwrite request
+ * completed. A dummy request that is "completed" by a manual call to
+ * multiwrite_cb() takes care of this.
+ */
+ mcb->num_requests = 1;
+
+ // Run the aio requests
+ for (i = 0; i < num_reqs; i++) {
+ mcb->num_requests++;
+ acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
+ reqs[i].nb_sectors, multiwrite_cb, mcb);
+
+ if (acb == NULL) {
+ // We can only fail the whole thing if no request has been
+ // submitted yet. Otherwise we'll wait for the submitted AIOs to
+ // complete and report the error in the callback.
+ if (i == 0) {
+ trace_bdrv_aio_multiwrite_earlyfail(mcb);
+ goto fail;
+ } else {
+ trace_bdrv_aio_multiwrite_latefail(mcb, i);
+ multiwrite_cb(mcb, -EIO);
+ break;
+ }
+ }
+ }
+
+ /* Complete the dummy request */
+ multiwrite_cb(mcb, 0);
+
+ return 0;
+
+fail:
+ for (i = 0; i < mcb->num_callbacks; i++) {
+ reqs[i].error = -EIO;
+ }
+ qemu_free(mcb);
+ return -1;
+}
+
+BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (bs->open_flags & BDRV_O_NO_FLUSH) {
+ return bdrv_aio_noop_em(bs, cb, opaque);
+ }
+
+ if (!drv)
+ return NULL;
+ return drv->bdrv_aio_flush(bs, cb, opaque);
+}
+
+void bdrv_aio_cancel(BlockDriverAIOCB *acb)
+{
+ acb->pool->cancel(acb);
+}
+
+
+/**************************************************************/
+/* async block device emulation */
+
+typedef struct BlockDriverAIOCBSync {
+ BlockDriverAIOCB common;
+ QEMUBH *bh;
+ int ret;
+ /* vector translation state */
+ QEMUIOVector *qiov;
+ uint8_t *bounce;
+ int is_write;
+} BlockDriverAIOCBSync;
+
+static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb)
+{
+ BlockDriverAIOCBSync *acb =
+ container_of(blockacb, BlockDriverAIOCBSync, common);
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+ qemu_aio_release(acb);
+}
+
+static AIOPool bdrv_em_aio_pool = {
+ .aiocb_size = sizeof(BlockDriverAIOCBSync),
+ .cancel = bdrv_aio_cancel_em,
+};
+
+static void bdrv_aio_bh_cb(void *opaque)
+{
+ BlockDriverAIOCBSync *acb = opaque;
+
+ if (!acb->is_write)
+ qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
+ qemu_vfree(acb->bounce);
+ acb->common.cb(acb->common.opaque, acb->ret);
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *bdrv_aio_rw_vector(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque,
+ int is_write)
+
+{
+ BlockDriverAIOCBSync *acb;
+
+ acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
+ acb->is_write = is_write;
+ acb->qiov = qiov;
+ acb->bounce = qemu_blockalign(bs, qiov->size);
+
+ if (!acb->bh)
+ acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+
+ if (is_write) {
+ qemu_iovec_to_buffer(acb->qiov, acb->bounce);
+ acb->ret = bdrv_write(bs, sector_num, acb->bounce, nb_sectors);
+ } else {
+ acb->ret = bdrv_read(bs, sector_num, acb->bounce, nb_sectors);
+ }
+
+ qemu_bh_schedule(acb->bh);
+
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+}
+
+static BlockDriverAIOCB *bdrv_aio_writev_em(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+}
+
+static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriverAIOCBSync *acb;
+
+ acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
+ acb->is_write = 1; /* don't bounce in the completion hadler */
+ acb->qiov = NULL;
+ acb->bounce = NULL;
+ acb->ret = 0;
+
+ if (!acb->bh)
+ acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+
+ bdrv_flush(bs);
+ qemu_bh_schedule(acb->bh);
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriverAIOCBSync *acb;
+
+ acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque);
+ acb->is_write = 1; /* don't bounce in the completion handler */
+ acb->qiov = NULL;
+ acb->bounce = NULL;
+ acb->ret = 0;
+
+ if (!acb->bh) {
+ acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+ }
+
+ qemu_bh_schedule(acb->bh);
+ return &acb->common;
+}
+
+/**************************************************************/
+/* sync block device emulation */
+
+static void bdrv_rw_em_cb(void *opaque, int ret)
+{
+ *(int *)opaque = ret;
+}
+
+#define NOT_DONE 0x7fffffff
+
+static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ int async_ret;
+ BlockDriverAIOCB *acb;
+ struct iovec iov;
+ QEMUIOVector qiov;
+
+ async_context_push();
+
+ async_ret = NOT_DONE;
+ iov.iov_base = (void *)buf;
+ iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ acb = bdrv_aio_readv(bs, sector_num, &qiov, nb_sectors,
+ bdrv_rw_em_cb, &async_ret);
+ if (acb == NULL) {
+ async_ret = -1;
+ goto fail;
+ }
+
+ while (async_ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
+
+
+fail:
+ async_context_pop();
+ return async_ret;
+}
+
+static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ int async_ret;
+ BlockDriverAIOCB *acb;
+ struct iovec iov;
+ QEMUIOVector qiov;
+
+ async_context_push();
+
+ async_ret = NOT_DONE;
+ iov.iov_base = (void *)buf;
+ iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+ acb = bdrv_aio_writev(bs, sector_num, &qiov, nb_sectors,
+ bdrv_rw_em_cb, &async_ret);
+ if (acb == NULL) {
+ async_ret = -1;
+ goto fail;
+ }
+ while (async_ret == NOT_DONE) {
+ qemu_aio_wait();
+ }
+
+fail:
+ async_context_pop();
+ return async_ret;
+}
+
+void bdrv_init(void)
+{
+ module_call_init(MODULE_INIT_BLOCK);
+}
+
+void bdrv_init_with_whitelist(void)
+{
+ use_bdrv_whitelist = 1;
+ bdrv_init();
+}
+
+void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriverAIOCB *acb;
+
+ if (pool->free_aiocb) {
+ acb = pool->free_aiocb;
+ pool->free_aiocb = acb->next;
+ } else {
+ acb = qemu_mallocz(pool->aiocb_size);
+ acb->pool = pool;
+ }
+ acb->bs = bs;
+ acb->cb = cb;
+ acb->opaque = opaque;
+ return acb;
+}
+
+void qemu_aio_release(void *p)
+{
+ BlockDriverAIOCB *acb = (BlockDriverAIOCB *)p;
+ AIOPool *pool = acb->pool;
+ acb->next = pool->free_aiocb;
+ pool->free_aiocb = acb;
+}
+
+/**************************************************************/
+/* removable device support */
+
+/**
+ * Return TRUE if the media is present
+ */
+int bdrv_is_inserted(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ int ret;
+ if (!drv)
+ return 0;
+ if (!drv->bdrv_is_inserted)
+ return !bs->tray_open;
+ ret = drv->bdrv_is_inserted(bs);
+ return ret;
+}
+
+/**
+ * Return TRUE if the media changed since the last call to this
+ * function. It is currently only used for floppy disks
+ */
+int bdrv_media_changed(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ int ret;
+
+ if (!drv || !drv->bdrv_media_changed)
+ ret = -ENOTSUP;
+ else
+ ret = drv->bdrv_media_changed(bs);
+ if (ret == -ENOTSUP)
+ ret = bs->media_changed;
+ bs->media_changed = 0;
+ return ret;
+}
+
+/**
+ * If eject_flag is TRUE, eject the media. Otherwise, close the tray
+ */
+int bdrv_eject(BlockDriverState *bs, int eject_flag)
+{
+ BlockDriver *drv = bs->drv;
+ int ret;
+
+ if (bs->locked) {
+ return -EBUSY;
+ }
+
+ if (!drv || !drv->bdrv_eject) {
+ ret = -ENOTSUP;
+ } else {
+ ret = drv->bdrv_eject(bs, eject_flag);
+ }
+ if (ret == -ENOTSUP) {
+ ret = 0;
+ }
+ if (ret >= 0) {
+ bs->tray_open = eject_flag;
+ }
+
+ return ret;
+}
+
+int bdrv_is_locked(BlockDriverState *bs)
+{
+ return bs->locked;
+}
+
+/**
+ * Lock or unlock the media (if it is locked, the user won't be able
+ * to eject it manually).
+ */
+void bdrv_set_locked(BlockDriverState *bs, int locked)
+{
+ BlockDriver *drv = bs->drv;
+
+ bs->locked = locked;
+ if (drv && drv->bdrv_set_locked) {
+ drv->bdrv_set_locked(bs, locked);
+ }
+}
+
+/* needed for generic scsi interface */
+
+int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (drv && drv->bdrv_ioctl)
+ return drv->bdrv_ioctl(bs, req, buf);
+ return -ENOTSUP;
+}
+
+BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (drv && drv->bdrv_aio_ioctl)
+ return drv->bdrv_aio_ioctl(bs, req, buf, cb, opaque);
+ return NULL;
+}
+
+
+
+void *qemu_blockalign(BlockDriverState *bs, size_t size)
+{
+ return qemu_memalign((bs && bs->buffer_alignment) ? bs->buffer_alignment : 512, size);
+}
+
+void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable)
+{
+ int64_t bitmap_size;
+
+ bs->dirty_count = 0;
+ if (enable) {
+ if (!bs->dirty_bitmap) {
+ bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
+ BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
+ bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
+
+ bs->dirty_bitmap = qemu_mallocz(bitmap_size);
+ }
+ } else {
+ if (bs->dirty_bitmap) {
+ qemu_free(bs->dirty_bitmap);
+ bs->dirty_bitmap = NULL;
+ }
+ }
+}
+
+int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
+{
+ int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
+
+ if (bs->dirty_bitmap &&
+ (sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
+ return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
+ (1UL << (chunk % (sizeof(unsigned long) * 8))));
+ } else {
+ return 0;
+ }
+}
+
+void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
+ int nr_sectors)
+{
+ set_dirty_bitmap(bs, cur_sector, nr_sectors, 0);
+}
+
+int64_t bdrv_get_dirty_count(BlockDriverState *bs)
+{
+ return bs->dirty_count;
+}
+
+void bdrv_set_in_use(BlockDriverState *bs, int in_use)
+{
+ assert(bs->in_use != in_use);
+ bs->in_use = in_use;
+}
+
+int bdrv_in_use(BlockDriverState *bs)
+{
+ return bs->in_use;
+}
+
+int bdrv_img_create(const char *filename, const char *fmt,
+ const char *base_filename, const char *base_fmt,
+ char *options, uint64_t img_size, int flags)
+{
+ QEMUOptionParameter *param = NULL, *create_options = NULL;
+ QEMUOptionParameter *backing_fmt, *backing_file;
+ BlockDriverState *bs = NULL;
+ BlockDriver *drv, *proto_drv;
+ BlockDriver *backing_drv = NULL;
+ int ret = 0;
+
+ /* Find driver and parse its options */
+ drv = bdrv_find_format(fmt);
+ if (!drv) {
+ error_report("Unknown file format '%s'", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ proto_drv = bdrv_find_protocol(filename);
+ if (!proto_drv) {
+ error_report("Unknown protocol '%s'", filename);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ create_options = append_option_parameters(create_options,
+ drv->create_options);
+ create_options = append_option_parameters(create_options,
+ proto_drv->create_options);
+
+ /* Create parameter list with default values */
+ param = parse_option_parameters("", create_options, param);
+
+ set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size);
+
+ /* Parse -o options */
+ if (options) {
+ param = parse_option_parameters(options, create_options, param);
+ if (param == NULL) {
+ error_report("Invalid options for file format '%s'.", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (base_filename) {
+ if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
+ base_filename)) {
+ error_report("Backing file not supported for file format '%s'",
+ fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (base_fmt) {
+ if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
+ error_report("Backing file format not supported for file "
+ "format '%s'", fmt);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
+ if (backing_file && backing_file->value.s) {
+ if (!strcmp(filename, backing_file->value.s)) {
+ error_report("Error: Trying to create an image with the "
+ "same filename as the backing file");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
+ if (backing_fmt && backing_fmt->value.s) {
+ backing_drv = bdrv_find_format(backing_fmt->value.s);
+ if (!backing_drv) {
+ error_report("Unknown backing file format '%s'",
+ backing_fmt->value.s);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ // The size for the image must always be specified, with one exception:
+ // If we are using a backing file, we can obtain the size from there
+ if (get_option_parameter(param, BLOCK_OPT_SIZE)->value.n == -1) {
+ if (backing_file && backing_file->value.s) {
+ uint64_t size;
+ char buf[32];
+
+ bs = bdrv_new("");
+
+ ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
+ if (ret < 0) {
+ error_report("Could not open '%s'", backing_file->value.s);
+ goto out;
+ }
+ bdrv_get_geometry(bs, &size);
+ size *= 512;
+
+ snprintf(buf, sizeof(buf), "%" PRId64, size);
+ set_option_parameter(param, BLOCK_OPT_SIZE, buf);
+ } else {
+ error_report("Image creation needs a size parameter");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ printf("Formatting '%s', fmt=%s ", filename, fmt);
+ print_option_parameters(param);
+ puts("");
+
+ ret = bdrv_create(drv, filename, param);
+
+ if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ error_report("Formatting or formatting option not supported for "
+ "file format '%s'", fmt);
+ } else if (ret == -EFBIG) {
+ error_report("The image size is too large for file format '%s'",
+ fmt);
+ } else {
+ error_report("%s: error while creating %s: %s", filename, fmt,
+ strerror(-ret));
+ }
+ }
+
+out:
+ free_option_parameters(create_options);
+ free_option_parameters(param);
+
+ if (bs) {
+ bdrv_delete(bs);
+ }
+
+ return ret;
+}
diff --git a/block.h b/block.h
new file mode 100644
index 0000000..097b789
--- /dev/null
+++ b/block.h
@@ -0,0 +1,299 @@
+#ifndef BLOCK_H
+#define BLOCK_H
+
+#include "qemu-aio.h"
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qobject.h"
+
+/* block.c */
+typedef struct BlockDriver BlockDriver;
+
+typedef struct BlockDriverInfo {
+ /* in bytes, 0 if irrelevant */
+ int cluster_size;
+ /* offset at which the VM state can be saved (0 if not possible) */
+ int64_t vm_state_offset;
+} BlockDriverInfo;
+
+typedef struct QEMUSnapshotInfo {
+ char id_str[128]; /* unique snapshot id */
+ /* the following fields are informative. They are not needed for
+ the consistency of the snapshot */
+ char name[256]; /* user choosen name */
+ uint32_t vm_state_size; /* VM state info size */
+ uint32_t date_sec; /* UTC date of the snapshot */
+ uint32_t date_nsec;
+ uint64_t vm_clock_nsec; /* VM clock relative to boot */
+} QEMUSnapshotInfo;
+
+#define BDRV_O_RDWR 0x0002
+#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */
+#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */
+#define BDRV_O_CACHE_WB 0x0040 /* use write-back caching */
+#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */
+#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */
+#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */
+
+#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
+
+#define BDRV_SECTOR_BITS 9
+#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
+#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
+
+typedef enum {
+ BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC,
+ BLOCK_ERR_STOP_ANY
+} BlockErrorAction;
+
+typedef enum {
+ BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
+} BlockMonEventAction;
+
+void bdrv_mon_event(const BlockDriverState *bdrv,
+ BlockMonEventAction action, int is_read);
+void bdrv_info_print(Monitor *mon, const QObject *data);
+void bdrv_info(Monitor *mon, QObject **ret_data);
+void bdrv_stats_print(Monitor *mon, const QObject *data);
+void bdrv_info_stats(Monitor *mon, QObject **ret_data);
+
+void bdrv_init(void);
+void bdrv_init_with_whitelist(void);
+BlockDriver *bdrv_find_protocol(const char *filename);
+BlockDriver *bdrv_find_format(const char *format_name);
+BlockDriver *bdrv_find_whitelisted_format(const char *format_name);
+int bdrv_create(BlockDriver *drv, const char* filename,
+ QEMUOptionParameter *options);
+int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
+BlockDriverState *bdrv_new(const char *device_name);
+void bdrv_make_anon(BlockDriverState *bs);
+void bdrv_delete(BlockDriverState *bs);
+int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
+int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
+ BlockDriver *drv);
+void bdrv_close(BlockDriverState *bs);
+int bdrv_attach(BlockDriverState *bs, DeviceState *qdev);
+void bdrv_detach(BlockDriverState *bs, DeviceState *qdev);
+DeviceState *bdrv_get_attached(BlockDriverState *bs);
+int bdrv_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors);
+int bdrv_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+int bdrv_pread(BlockDriverState *bs, int64_t offset,
+ void *buf, int count);
+int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+ const void *buf, int count);
+int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
+ const void *buf, int count);
+int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+int bdrv_truncate(BlockDriverState *bs, int64_t offset);
+int64_t bdrv_getlength(BlockDriverState *bs);
+void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
+void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
+int bdrv_commit(BlockDriverState *bs);
+void bdrv_commit_all(void);
+int bdrv_change_backing_file(BlockDriverState *bs,
+ const char *backing_file, const char *backing_fmt);
+void bdrv_register(BlockDriver *bdrv);
+
+
+typedef struct BdrvCheckResult {
+ int corruptions;
+ int leaks;
+ int check_errors;
+} BdrvCheckResult;
+
+int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
+
+/* async block I/O */
+typedef struct BlockDriverAIOCB BlockDriverAIOCB;
+typedef void BlockDriverCompletionFunc(void *opaque, int ret);
+typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
+ int sector_num);
+BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *iov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *iov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
+void bdrv_aio_cancel(BlockDriverAIOCB *acb);
+
+typedef struct BlockRequest {
+ /* Fields to be filled by multiwrite caller */
+ int64_t sector;
+ int nb_sectors;
+ QEMUIOVector *qiov;
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+
+ /* Filled by multiwrite implementation */
+ int error;
+} BlockRequest;
+
+int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs,
+ int num_reqs);
+
+/* sg packet commands */
+int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf);
+BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque);
+
+/* Ensure contents are flushed to disk. */
+int bdrv_flush(BlockDriverState *bs);
+void bdrv_flush_all(void);
+void bdrv_close_all(void);
+
+int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
+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
+#define BIOS_ATA_TRANSLATION_LARGE 3
+#define BIOS_ATA_TRANSLATION_RECHS 4
+
+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);
+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);
+BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read);
+void bdrv_set_removable(BlockDriverState *bs, int removable);
+int bdrv_is_removable(BlockDriverState *bs);
+int bdrv_is_read_only(BlockDriverState *bs);
+int bdrv_is_sg(BlockDriverState *bs);
+int bdrv_enable_write_cache(BlockDriverState *bs);
+int bdrv_is_inserted(BlockDriverState *bs);
+int bdrv_media_changed(BlockDriverState *bs);
+int bdrv_is_locked(BlockDriverState *bs);
+void bdrv_set_locked(BlockDriverState *bs, int locked);
+int bdrv_eject(BlockDriverState *bs, int eject_flag);
+void bdrv_set_change_cb(BlockDriverState *bs,
+ void (*change_cb)(void *opaque, int reason),
+ void *opaque);
+void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
+BlockDriverState *bdrv_find(const char *name);
+BlockDriverState *bdrv_next(BlockDriverState *bs);
+void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs),
+ void *opaque);
+int bdrv_is_encrypted(BlockDriverState *bs);
+int bdrv_key_required(BlockDriverState *bs);
+int bdrv_set_key(BlockDriverState *bs, const char *key);
+int bdrv_query_missing_keys(void);
+void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
+ void *opaque);
+const char *bdrv_get_device_name(BlockDriverState *bs);
+int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
+
+const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
+void bdrv_get_backing_filename(BlockDriverState *bs,
+ char *filename, int filename_size);
+int bdrv_can_snapshot(BlockDriverState *bs);
+int bdrv_is_snapshot(BlockDriverState *bs);
+BlockDriverState *bdrv_snapshots(void);
+int bdrv_snapshot_create(BlockDriverState *bs,
+ QEMUSnapshotInfo *sn_info);
+int bdrv_snapshot_goto(BlockDriverState *bs,
+ const char *snapshot_id);
+int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
+int bdrv_snapshot_list(BlockDriverState *bs,
+ QEMUSnapshotInfo **psn_info);
+int bdrv_snapshot_load_tmp(BlockDriverState *bs,
+ const char *snapshot_name);
+char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
+
+char *get_human_readable_size(char *buf, int buf_size, int64_t size);
+int path_is_absolute(const char *path);
+void path_combine(char *dest, int dest_size,
+ const char *base_path,
+ const char *filename);
+
+int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
+ int64_t pos, int size);
+
+int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
+ int64_t pos, int size);
+
+int bdrv_img_create(const char *filename, const char *fmt,
+ const char *base_filename, const char *base_fmt,
+ char *options, uint64_t img_size, int flags);
+
+#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048
+
+void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
+int bdrv_get_dirty(BlockDriverState *bs, int64_t sector);
+void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
+ int nr_sectors);
+int64_t bdrv_get_dirty_count(BlockDriverState *bs);
+
+void bdrv_set_in_use(BlockDriverState *bs, int in_use);
+int bdrv_in_use(BlockDriverState *bs);
+
+typedef enum {
+ BLKDBG_L1_UPDATE,
+
+ BLKDBG_L1_GROW_ALLOC_TABLE,
+ BLKDBG_L1_GROW_WRITE_TABLE,
+ BLKDBG_L1_GROW_ACTIVATE_TABLE,
+
+ BLKDBG_L2_LOAD,
+ BLKDBG_L2_UPDATE,
+ BLKDBG_L2_UPDATE_COMPRESSED,
+ BLKDBG_L2_ALLOC_COW_READ,
+ BLKDBG_L2_ALLOC_WRITE,
+
+ BLKDBG_READ,
+ BLKDBG_READ_AIO,
+ BLKDBG_READ_BACKING,
+ BLKDBG_READ_BACKING_AIO,
+ BLKDBG_READ_COMPRESSED,
+
+ BLKDBG_WRITE_AIO,
+ BLKDBG_WRITE_COMPRESSED,
+
+ BLKDBG_VMSTATE_LOAD,
+ BLKDBG_VMSTATE_SAVE,
+
+ BLKDBG_COW_READ,
+ BLKDBG_COW_WRITE,
+
+ BLKDBG_REFTABLE_LOAD,
+ BLKDBG_REFTABLE_GROW,
+
+ BLKDBG_REFBLOCK_LOAD,
+ BLKDBG_REFBLOCK_UPDATE,
+ BLKDBG_REFBLOCK_UPDATE_PART,
+ BLKDBG_REFBLOCK_ALLOC,
+ BLKDBG_REFBLOCK_ALLOC_HOOKUP,
+ BLKDBG_REFBLOCK_ALLOC_WRITE,
+ BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS,
+ BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE,
+ BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE,
+
+ BLKDBG_CLUSTER_ALLOC,
+ BLKDBG_CLUSTER_ALLOC_BYTES,
+ BLKDBG_CLUSTER_FREE,
+
+ BLKDBG_EVENT_MAX,
+} BlkDebugEvent;
+
+#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt)
+void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event);
+
+#endif
diff --git a/block/blkdebug.c b/block/blkdebug.c
new file mode 100644
index 0000000..cd9eb80
--- /dev/null
+++ b/block/blkdebug.c
@@ -0,0 +1,471 @@
+/*
+ * Block protocol for I/O error injection
+ *
+ * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+typedef struct BlkdebugVars {
+ int state;
+
+ /* If inject_errno != 0, an error is injected for requests */
+ int inject_errno;
+
+ /* Decides if all future requests fail (false) or only the next one and
+ * after the next request inject_errno is reset to 0 (true) */
+ bool inject_once;
+
+ /* Decides if aio_readv/writev fails right away (true) or returns an error
+ * return value only in the callback (false) */
+ bool inject_immediately;
+} BlkdebugVars;
+
+typedef struct BDRVBlkdebugState {
+ BlkdebugVars vars;
+ QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
+} BDRVBlkdebugState;
+
+typedef struct BlkdebugAIOCB {
+ BlockDriverAIOCB common;
+ QEMUBH *bh;
+ int ret;
+} BlkdebugAIOCB;
+
+static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
+
+static AIOPool blkdebug_aio_pool = {
+ .aiocb_size = sizeof(BlkdebugAIOCB),
+ .cancel = blkdebug_aio_cancel,
+};
+
+enum {
+ ACTION_INJECT_ERROR,
+ ACTION_SET_STATE,
+};
+
+typedef struct BlkdebugRule {
+ BlkDebugEvent event;
+ int action;
+ int state;
+ union {
+ struct {
+ int error;
+ int immediately;
+ int once;
+ } inject;
+ struct {
+ int new_state;
+ } set_state;
+ } options;
+ QLIST_ENTRY(BlkdebugRule) next;
+} BlkdebugRule;
+
+static QemuOptsList inject_error_opts = {
+ .name = "inject-error",
+ .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
+ .desc = {
+ {
+ .name = "event",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "state",
+ .type = QEMU_OPT_NUMBER,
+ },
+ {
+ .name = "errno",
+ .type = QEMU_OPT_NUMBER,
+ },
+ {
+ .name = "once",
+ .type = QEMU_OPT_BOOL,
+ },
+ {
+ .name = "immediately",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList set_state_opts = {
+ .name = "set-state",
+ .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head),
+ .desc = {
+ {
+ .name = "event",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "state",
+ .type = QEMU_OPT_NUMBER,
+ },
+ {
+ .name = "new_state",
+ .type = QEMU_OPT_NUMBER,
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList *config_groups[] = {
+ &inject_error_opts,
+ &set_state_opts,
+ NULL
+};
+
+static const char *event_names[BLKDBG_EVENT_MAX] = {
+ [BLKDBG_L1_UPDATE] = "l1_update",
+ [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table",
+ [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table",
+ [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table",
+
+ [BLKDBG_L2_LOAD] = "l2_load",
+ [BLKDBG_L2_UPDATE] = "l2_update",
+ [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed",
+ [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read",
+ [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write",
+
+ [BLKDBG_READ] = "read",
+ [BLKDBG_READ_AIO] = "read_aio",
+ [BLKDBG_READ_BACKING] = "read_backing",
+ [BLKDBG_READ_BACKING_AIO] = "read_backing_aio",
+ [BLKDBG_READ_COMPRESSED] = "read_compressed",
+
+ [BLKDBG_WRITE_AIO] = "write_aio",
+ [BLKDBG_WRITE_COMPRESSED] = "write_compressed",
+
+ [BLKDBG_VMSTATE_LOAD] = "vmstate_load",
+ [BLKDBG_VMSTATE_SAVE] = "vmstate_save",
+
+ [BLKDBG_COW_READ] = "cow_read",
+ [BLKDBG_COW_WRITE] = "cow_write",
+
+ [BLKDBG_REFTABLE_LOAD] = "reftable_load",
+ [BLKDBG_REFTABLE_GROW] = "reftable_grow",
+
+ [BLKDBG_REFBLOCK_LOAD] = "refblock_load",
+ [BLKDBG_REFBLOCK_UPDATE] = "refblock_update",
+ [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part",
+ [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc",
+ [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup",
+ [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write",
+ [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks",
+ [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table",
+ [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table",
+
+ [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
+ [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
+ [BLKDBG_CLUSTER_FREE] = "cluster_free",
+};
+
+static int get_event_by_name(const char *name, BlkDebugEvent *event)
+{
+ int i;
+
+ for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
+ if (!strcmp(event_names[i], name)) {
+ *event = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+struct add_rule_data {
+ BDRVBlkdebugState *s;
+ int action;
+};
+
+static int add_rule(QemuOpts *opts, void *opaque)
+{
+ struct add_rule_data *d = opaque;
+ BDRVBlkdebugState *s = d->s;
+ const char* event_name;
+ BlkDebugEvent event;
+ struct BlkdebugRule *rule;
+
+ /* Find the right event for the rule */
+ event_name = qemu_opt_get(opts, "event");
+ if (!event_name || get_event_by_name(event_name, &event) < 0) {
+ return -1;
+ }
+
+ /* Set attributes common for all actions */
+ rule = qemu_mallocz(sizeof(*rule));
+ *rule = (struct BlkdebugRule) {
+ .event = event,
+ .action = d->action,
+ .state = qemu_opt_get_number(opts, "state", 0),
+ };
+
+ /* Parse action-specific options */
+ switch (d->action) {
+ case ACTION_INJECT_ERROR:
+ rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO);
+ rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0);
+ rule->options.inject.immediately =
+ qemu_opt_get_bool(opts, "immediately", 0);
+ break;
+
+ case ACTION_SET_STATE:
+ rule->options.set_state.new_state =
+ qemu_opt_get_number(opts, "new_state", 0);
+ break;
+ };
+
+ /* Add the rule */
+ QLIST_INSERT_HEAD(&s->rules[event], rule, next);
+
+ return 0;
+}
+
+static int read_config(BDRVBlkdebugState *s, const char *filename)
+{
+ FILE *f;
+ int ret;
+ struct add_rule_data d;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ return -errno;
+ }
+
+ ret = qemu_config_parse(f, config_groups, filename);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ d.s = s;
+ d.action = ACTION_INJECT_ERROR;
+ qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0);
+
+ d.action = ACTION_SET_STATE;
+ qemu_opts_foreach(&set_state_opts, add_rule, &d, 0);
+
+ ret = 0;
+fail:
+ qemu_opts_reset(&inject_error_opts);
+ qemu_opts_reset(&set_state_opts);
+ fclose(f);
+ return ret;
+}
+
+/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */
+static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ int ret;
+ char *config, *c;
+
+ /* Parse the blkdebug: prefix */
+ if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) {
+ return -EINVAL;
+ }
+ filename += strlen("blkdebug:");
+
+ /* Read rules from config file */
+ c = strchr(filename, ':');
+ if (c == NULL) {
+ return -EINVAL;
+ }
+
+ config = strdup(filename);
+ config[c - filename] = '\0';
+ ret = read_config(s, config);
+ free(config);
+ if (ret < 0) {
+ return ret;
+ }
+ filename = c + 1;
+
+ /* Set initial state */
+ s->vars.state = 1;
+
+ /* Open the backing file */
+ ret = bdrv_file_open(&bs->file, filename, flags);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static void error_callback_bh(void *opaque)
+{
+ struct BlkdebugAIOCB *acb = opaque;
+ qemu_bh_delete(acb->bh);
+ acb->common.cb(acb->common.opaque, acb->ret);
+ qemu_aio_release(acb);
+}
+
+static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common);
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *inject_error(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ int error = s->vars.inject_errno;
+ struct BlkdebugAIOCB *acb;
+ QEMUBH *bh;
+
+ if (s->vars.inject_once) {
+ s->vars.inject_errno = 0;
+ }
+
+ if (s->vars.inject_immediately) {
+ return NULL;
+ }
+
+ acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque);
+ acb->ret = -error;
+
+ bh = qemu_bh_new(error_callback_bh, acb);
+ acb->bh = bh;
+ qemu_bh_schedule(bh);
+
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+
+ if (s->vars.inject_errno) {
+ return inject_error(bs, cb, opaque);
+ }
+
+ BlockDriverAIOCB *acb =
+ bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
+ return acb;
+}
+
+static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+
+ if (s->vars.inject_errno) {
+ return inject_error(bs, cb, opaque);
+ }
+
+ BlockDriverAIOCB *acb =
+ bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
+ return acb;
+}
+
+static void blkdebug_close(BlockDriverState *bs)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ BlkdebugRule *rule, *next;
+ int i;
+
+ for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
+ QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
+ QLIST_REMOVE(rule, next);
+ qemu_free(rule);
+ }
+ }
+}
+
+static int blkdebug_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_flush(bs->file, cb, opaque);
+}
+
+static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
+ BlkdebugVars *old_vars)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ BlkdebugVars *vars = &s->vars;
+
+ /* Only process rules for the current state */
+ if (rule->state && rule->state != old_vars->state) {
+ return;
+ }
+
+ /* Take the action */
+ switch (rule->action) {
+ case ACTION_INJECT_ERROR:
+ vars->inject_errno = rule->options.inject.error;
+ vars->inject_once = rule->options.inject.once;
+ vars->inject_immediately = rule->options.inject.immediately;
+ break;
+
+ case ACTION_SET_STATE:
+ vars->state = rule->options.set_state.new_state;
+ break;
+ }
+}
+
+static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
+{
+ BDRVBlkdebugState *s = bs->opaque;
+ struct BlkdebugRule *rule;
+ BlkdebugVars old_vars = s->vars;
+
+ assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
+
+ QLIST_FOREACH(rule, &s->rules[event], next) {
+ process_rule(bs, rule, &old_vars);
+ }
+}
+
+static BlockDriver bdrv_blkdebug = {
+ .format_name = "blkdebug",
+ .protocol_name = "blkdebug",
+
+ .instance_size = sizeof(BDRVBlkdebugState),
+
+ .bdrv_file_open = blkdebug_open,
+ .bdrv_close = blkdebug_close,
+ .bdrv_flush = blkdebug_flush,
+
+ .bdrv_aio_readv = blkdebug_aio_readv,
+ .bdrv_aio_writev = blkdebug_aio_writev,
+ .bdrv_aio_flush = blkdebug_aio_flush,
+
+ .bdrv_debug_event = blkdebug_debug_event,
+};
+
+static void bdrv_blkdebug_init(void)
+{
+ bdrv_register(&bdrv_blkdebug);
+}
+
+block_init(bdrv_blkdebug_init);
diff --git a/block/blkverify.c b/block/blkverify.c
new file mode 100644
index 0000000..c7522b4
--- /dev/null
+++ b/block/blkverify.c
@@ -0,0 +1,383 @@
+/*
+ * Block protocol for block driver correctness testing
+ *
+ * Copyright (C) 2010 IBM, Corp.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdarg.h>
+#include "qemu_socket.h" /* for EINPROGRESS on Windows */
+#include "block_int.h"
+
+typedef struct {
+ BlockDriverState *test_file;
+} BDRVBlkverifyState;
+
+typedef struct BlkverifyAIOCB BlkverifyAIOCB;
+struct BlkverifyAIOCB {
+ BlockDriverAIOCB common;
+ QEMUBH *bh;
+
+ /* Request metadata */
+ bool is_write;
+ int64_t sector_num;
+ int nb_sectors;
+
+ int ret; /* first completed request's result */
+ unsigned int done; /* completion counter */
+ bool *finished; /* completion signal for cancel */
+
+ QEMUIOVector *qiov; /* user I/O vector */
+ QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */
+ void *buf; /* buffer for raw file I/O */
+
+ void (*verify)(BlkverifyAIOCB *acb);
+};
+
+static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb;
+ bool finished = false;
+
+ /* Wait until request completes, invokes its callback, and frees itself */
+ acb->finished = &finished;
+ while (!finished) {
+ qemu_aio_wait();
+ }
+}
+
+static AIOPool blkverify_aio_pool = {
+ .aiocb_size = sizeof(BlkverifyAIOCB),
+ .cancel = blkverify_aio_cancel,
+};
+
+static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ",
+ acb->is_write ? "write" : "read", acb->sector_num,
+ acb->nb_sectors);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
+static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ int ret;
+ char *raw, *c;
+
+ /* Parse the blkverify: prefix */
+ if (strncmp(filename, "blkverify:", strlen("blkverify:"))) {
+ return -EINVAL;
+ }
+ filename += strlen("blkverify:");
+
+ /* Parse the raw image filename */
+ c = strchr(filename, ':');
+ if (c == NULL) {
+ return -EINVAL;
+ }
+
+ raw = strdup(filename);
+ raw[c - filename] = '\0';
+ ret = bdrv_file_open(&bs->file, raw, flags);
+ free(raw);
+ if (ret < 0) {
+ return ret;
+ }
+ filename = c + 1;
+
+ /* Open the test file */
+ s->test_file = bdrv_new("");
+ ret = bdrv_open(s->test_file, filename, flags, NULL);
+ if (ret < 0) {
+ bdrv_delete(s->test_file);
+ s->test_file = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void blkverify_close(BlockDriverState *bs)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ bdrv_delete(s->test_file);
+ s->test_file = NULL;
+}
+
+static int blkverify_flush(BlockDriverState *bs)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ /* Only flush test file, the raw file is not important */
+ return bdrv_flush(s->test_file);
+}
+
+static int64_t blkverify_getlength(BlockDriverState *bs)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ return bdrv_getlength(s->test_file);
+}
+
+/**
+ * Check that I/O vector contents are identical
+ *
+ * @a: I/O vector
+ * @b: I/O vector
+ * @ret: Offset to first mismatching byte or -1 if match
+ */
+static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
+{
+ int i;
+ ssize_t offset = 0;
+
+ assert(a->niov == b->niov);
+ for (i = 0; i < a->niov; i++) {
+ size_t len = 0;
+ uint8_t *p = (uint8_t *)a->iov[i].iov_base;
+ uint8_t *q = (uint8_t *)b->iov[i].iov_base;
+
+ assert(a->iov[i].iov_len == b->iov[i].iov_len);
+ while (len < a->iov[i].iov_len && *p++ == *q++) {
+ len++;
+ }
+
+ offset += len;
+
+ if (len != a->iov[i].iov_len) {
+ return offset;
+ }
+ }
+ return -1;
+}
+
+typedef struct {
+ int src_index;
+ struct iovec *src_iov;
+ void *dest_base;
+} IOVectorSortElem;
+
+static int sortelem_cmp_src_base(const void *a, const void *b)
+{
+ const IOVectorSortElem *elem_a = a;
+ const IOVectorSortElem *elem_b = b;
+
+ /* Don't overflow */
+ if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
+ return -1;
+ } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int sortelem_cmp_src_index(const void *a, const void *b)
+{
+ const IOVectorSortElem *elem_a = a;
+ const IOVectorSortElem *elem_b = b;
+
+ return elem_a->src_index - elem_b->src_index;
+}
+
+/**
+ * Copy contents of I/O vector
+ *
+ * The relative relationships of overlapping iovecs are preserved. This is
+ * necessary to ensure identical semantics in the cloned I/O vector.
+ */
+static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
+ void *buf)
+{
+ IOVectorSortElem sortelems[src->niov];
+ void *last_end;
+ int i;
+
+ /* Sort by source iovecs by base address */
+ for (i = 0; i < src->niov; i++) {
+ sortelems[i].src_index = i;
+ sortelems[i].src_iov = &src->iov[i];
+ }
+ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
+
+ /* Allocate buffer space taking into account overlapping iovecs */
+ last_end = NULL;
+ for (i = 0; i < src->niov; i++) {
+ struct iovec *cur = sortelems[i].src_iov;
+ ptrdiff_t rewind = 0;
+
+ /* Detect overlap */
+ if (last_end && last_end > cur->iov_base) {
+ rewind = last_end - cur->iov_base;
+ }
+
+ sortelems[i].dest_base = buf - rewind;
+ buf += cur->iov_len - MIN(rewind, cur->iov_len);
+ last_end = MAX(cur->iov_base + cur->iov_len, last_end);
+ }
+
+ /* Sort by source iovec index and build destination iovec */
+ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
+ for (i = 0; i < src->niov; i++) {
+ qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
+ }
+}
+
+static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
+ int64_t sector_num, QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque);
+
+ acb->bh = NULL;
+ acb->is_write = is_write;
+ acb->sector_num = sector_num;
+ acb->nb_sectors = nb_sectors;
+ acb->ret = -EINPROGRESS;
+ acb->done = 0;
+ acb->qiov = qiov;
+ acb->buf = NULL;
+ acb->verify = NULL;
+ acb->finished = NULL;
+ return acb;
+}
+
+static void blkverify_aio_bh(void *opaque)
+{
+ BlkverifyAIOCB *acb = opaque;
+
+ qemu_bh_delete(acb->bh);
+ if (acb->buf) {
+ qemu_iovec_destroy(&acb->raw_qiov);
+ qemu_vfree(acb->buf);
+ }
+ acb->common.cb(acb->common.opaque, acb->ret);
+ if (acb->finished) {
+ *acb->finished = true;
+ }
+ qemu_aio_release(acb);
+}
+
+static void blkverify_aio_cb(void *opaque, int ret)
+{
+ BlkverifyAIOCB *acb = opaque;
+
+ switch (++acb->done) {
+ case 1:
+ acb->ret = ret;
+ break;
+
+ case 2:
+ if (acb->ret != ret) {
+ blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret);
+ }
+
+ if (acb->verify) {
+ acb->verify(acb);
+ }
+
+ acb->bh = qemu_bh_new(blkverify_aio_bh, acb);
+ qemu_bh_schedule(acb->bh);
+ break;
+ }
+}
+
+static void blkverify_verify_readv(BlkverifyAIOCB *acb)
+{
+ ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
+ if (offset != -1) {
+ blkverify_err(acb, "contents mismatch in sector %" PRId64,
+ acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
+ }
+}
+
+static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
+ nb_sectors, cb, opaque);
+
+ acb->verify = blkverify_verify_readv;
+ acb->buf = qemu_blockalign(bs->file, qiov->size);
+ qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
+ blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
+
+ if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+ BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
+ nb_sectors, cb, opaque);
+
+ if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
+ blkverify_aio_cb, acb)) {
+ blkverify_aio_cb(acb, -EIO);
+ }
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BDRVBlkverifyState *s = bs->opaque;
+
+ /* Only flush test file, the raw file is not important */
+ return bdrv_aio_flush(s->test_file, cb, opaque);
+}
+
+static BlockDriver bdrv_blkverify = {
+ .format_name = "blkverify",
+ .protocol_name = "blkverify",
+
+ .instance_size = sizeof(BDRVBlkverifyState),
+
+ .bdrv_getlength = blkverify_getlength,
+
+ .bdrv_file_open = blkverify_open,
+ .bdrv_close = blkverify_close,
+ .bdrv_flush = blkverify_flush,
+
+ .bdrv_aio_readv = blkverify_aio_readv,
+ .bdrv_aio_writev = blkverify_aio_writev,
+ .bdrv_aio_flush = blkverify_aio_flush,
+};
+
+static void bdrv_blkverify_init(void)
+{
+ bdrv_register(&bdrv_blkverify);
+}
+
+block_init(bdrv_blkverify_init);
diff --git a/block/bochs.c b/block/bochs.c
new file mode 100644
index 0000000..5fe2fa3
--- /dev/null
+++ b/block/bochs.c
@@ -0,0 +1,230 @@
+/*
+ * Block driver for the various disk image formats used by Bochs
+ * Currently only for "growing" type in read-only mode
+ *
+ * Copyright (c) 2005 Alex Beregszaszi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+/**************************************************************/
+
+#define HEADER_MAGIC "Bochs Virtual HD Image"
+#define HEADER_VERSION 0x00020000
+#define HEADER_V1 0x00010000
+#define HEADER_SIZE 512
+
+#define REDOLOG_TYPE "Redolog"
+#define GROWING_TYPE "Growing"
+
+// not allocated: 0xffffffff
+
+// always little-endian
+struct bochs_header_v1 {
+ char magic[32]; // "Bochs Virtual HD Image"
+ char type[16]; // "Redolog"
+ char subtype[16]; // "Undoable" / "Volatile" / "Growing"
+ uint32_t version;
+ uint32_t header; // size of header
+
+ union {
+ struct {
+ uint32_t catalog; // num of entries
+ uint32_t bitmap; // bitmap size
+ uint32_t extent; // extent size
+ uint64_t disk; // disk size
+ char padding[HEADER_SIZE - 64 - 8 - 20];
+ } redolog;
+ char padding[HEADER_SIZE - 64 - 8];
+ } extra;
+};
+
+// always little-endian
+struct bochs_header {
+ char magic[32]; // "Bochs Virtual HD Image"
+ char type[16]; // "Redolog"
+ char subtype[16]; // "Undoable" / "Volatile" / "Growing"
+ uint32_t version;
+ uint32_t header; // size of header
+
+ union {
+ struct {
+ uint32_t catalog; // num of entries
+ uint32_t bitmap; // bitmap size
+ uint32_t extent; // extent size
+ uint32_t reserved; // for ???
+ uint64_t disk; // disk size
+ char padding[HEADER_SIZE - 64 - 8 - 24];
+ } redolog;
+ char padding[HEADER_SIZE - 64 - 8];
+ } extra;
+};
+
+typedef struct BDRVBochsState {
+ uint32_t *catalog_bitmap;
+ int catalog_size;
+
+ int data_offset;
+
+ int bitmap_blocks;
+ int extent_blocks;
+ int extent_size;
+} BDRVBochsState;
+
+static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const struct bochs_header *bochs = (const void *)buf;
+
+ if (buf_size < HEADER_SIZE)
+ return 0;
+
+ if (!strcmp(bochs->magic, HEADER_MAGIC) &&
+ !strcmp(bochs->type, REDOLOG_TYPE) &&
+ !strcmp(bochs->subtype, GROWING_TYPE) &&
+ ((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
+ (le32_to_cpu(bochs->version) == HEADER_V1)))
+ return 100;
+
+ return 0;
+}
+
+static int bochs_open(BlockDriverState *bs, int flags)
+{
+ BDRVBochsState *s = bs->opaque;
+ int i;
+ struct bochs_header bochs;
+ struct bochs_header_v1 header_v1;
+
+ bs->read_only = 1; // no write support yet
+
+ if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) {
+ goto fail;
+ }
+
+ if (strcmp(bochs.magic, HEADER_MAGIC) ||
+ strcmp(bochs.type, REDOLOG_TYPE) ||
+ strcmp(bochs.subtype, GROWING_TYPE) ||
+ ((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
+ (le32_to_cpu(bochs.version) != HEADER_V1))) {
+ goto fail;
+ }
+
+ if (le32_to_cpu(bochs.version) == HEADER_V1) {
+ memcpy(&header_v1, &bochs, sizeof(bochs));
+ bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
+ } else {
+ bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
+ }
+
+ s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
+ s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
+ if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
+ s->catalog_size * 4) != s->catalog_size * 4)
+ goto fail;
+ for (i = 0; i < s->catalog_size; i++)
+ le32_to_cpus(&s->catalog_bitmap[i]);
+
+ s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
+
+ s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
+ s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
+
+ s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+ BDRVBochsState *s = bs->opaque;
+ int64_t offset = sector_num * 512;
+ int64_t extent_index, extent_offset, bitmap_offset;
+ char bitmap_entry;
+
+ // seek to sector
+ extent_index = offset / s->extent_size;
+ extent_offset = (offset % s->extent_size) / 512;
+
+ if (s->catalog_bitmap[extent_index] == 0xffffffff) {
+ return -1; /* not allocated */
+ }
+
+ bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
+ (s->extent_blocks + s->bitmap_blocks));
+
+ /* read in bitmap for current extent */
+ if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
+ &bitmap_entry, 1) != 1) {
+ return -1;
+ }
+
+ if (!((bitmap_entry >> (extent_offset % 8)) & 1)) {
+ return -1; /* not allocated */
+ }
+
+ return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
+}
+
+static int bochs_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ int ret;
+
+ while (nb_sectors > 0) {
+ int64_t block_offset = seek_to_sector(bs, sector_num);
+ if (block_offset >= 0) {
+ ret = bdrv_pread(bs->file, block_offset, buf, 512);
+ if (ret != 512) {
+ return -1;
+ }
+ } else
+ memset(buf, 0, 512);
+ nb_sectors--;
+ sector_num++;
+ buf += 512;
+ }
+ return 0;
+}
+
+static void bochs_close(BlockDriverState *bs)
+{
+ BDRVBochsState *s = bs->opaque;
+ qemu_free(s->catalog_bitmap);
+}
+
+static BlockDriver bdrv_bochs = {
+ .format_name = "bochs",
+ .instance_size = sizeof(BDRVBochsState),
+ .bdrv_probe = bochs_probe,
+ .bdrv_open = bochs_open,
+ .bdrv_read = bochs_read,
+ .bdrv_close = bochs_close,
+};
+
+static void bdrv_bochs_init(void)
+{
+ bdrv_register(&bdrv_bochs);
+}
+
+block_init(bdrv_bochs_init);
diff --git a/block/cloop.c b/block/cloop.c
new file mode 100644
index 0000000..fe015c4
--- /dev/null
+++ b/block/cloop.c
@@ -0,0 +1,171 @@
+/*
+ * QEMU Block driver for CLOOP images
+ *
+ * Copyright (c) 2004 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+#include <zlib.h>
+
+typedef struct BDRVCloopState {
+ uint32_t block_size;
+ uint32_t n_blocks;
+ uint64_t* offsets;
+ uint32_t sectors_per_block;
+ uint32_t current_block;
+ uint8_t *compressed_block;
+ uint8_t *uncompressed_block;
+ z_stream zstream;
+} BDRVCloopState;
+
+static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const char* magic_version_2_0="#!/bin/sh\n"
+ "#V2.0 Format\n"
+ "modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n";
+ int length=strlen(magic_version_2_0);
+ if(length>buf_size)
+ length=buf_size;
+ if(!memcmp(magic_version_2_0,buf,length))
+ return 2;
+ return 0;
+}
+
+static int cloop_open(BlockDriverState *bs, int flags)
+{
+ BDRVCloopState *s = bs->opaque;
+ uint32_t offsets_size,max_compressed_block_size=1,i;
+
+ bs->read_only = 1;
+
+ /* read header */
+ if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) {
+ goto cloop_close;
+ }
+ s->block_size = be32_to_cpu(s->block_size);
+
+ if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) {
+ goto cloop_close;
+ }
+ s->n_blocks = be32_to_cpu(s->n_blocks);
+
+ /* read offsets */
+ offsets_size = s->n_blocks * sizeof(uint64_t);
+ s->offsets = qemu_malloc(offsets_size);
+ if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) <
+ offsets_size) {
+ goto cloop_close;
+ }
+ for(i=0;i<s->n_blocks;i++) {
+ s->offsets[i]=be64_to_cpu(s->offsets[i]);
+ if(i>0) {
+ uint32_t size=s->offsets[i]-s->offsets[i-1];
+ if(size>max_compressed_block_size)
+ max_compressed_block_size=size;
+ }
+ }
+
+ /* initialize zlib engine */
+ s->compressed_block = qemu_malloc(max_compressed_block_size+1);
+ s->uncompressed_block = qemu_malloc(s->block_size);
+ if(inflateInit(&s->zstream) != Z_OK)
+ goto cloop_close;
+ s->current_block=s->n_blocks;
+
+ s->sectors_per_block = s->block_size/512;
+ bs->total_sectors = s->n_blocks*s->sectors_per_block;
+ return 0;
+
+cloop_close:
+ return -1;
+}
+
+static inline int cloop_read_block(BlockDriverState *bs, int block_num)
+{
+ BDRVCloopState *s = bs->opaque;
+
+ if(s->current_block != block_num) {
+ int ret;
+ uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
+
+ ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block,
+ bytes);
+ if (ret != bytes)
+ return -1;
+
+ s->zstream.next_in = s->compressed_block;
+ s->zstream.avail_in = bytes;
+ s->zstream.next_out = s->uncompressed_block;
+ s->zstream.avail_out = s->block_size;
+ ret = inflateReset(&s->zstream);
+ if(ret != Z_OK)
+ return -1;
+ ret = inflate(&s->zstream, Z_FINISH);
+ if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size)
+ return -1;
+
+ s->current_block = block_num;
+ }
+ return 0;
+}
+
+static int cloop_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVCloopState *s = bs->opaque;
+ int i;
+
+ for(i=0;i<nb_sectors;i++) {
+ uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block),
+ block_num=(sector_num+i)/s->sectors_per_block;
+ if(cloop_read_block(bs, block_num) != 0)
+ return -1;
+ memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512);
+ }
+ return 0;
+}
+
+static void cloop_close(BlockDriverState *bs)
+{
+ BDRVCloopState *s = bs->opaque;
+ if(s->n_blocks>0)
+ free(s->offsets);
+ free(s->compressed_block);
+ free(s->uncompressed_block);
+ inflateEnd(&s->zstream);
+}
+
+static BlockDriver bdrv_cloop = {
+ .format_name = "cloop",
+ .instance_size = sizeof(BDRVCloopState),
+ .bdrv_probe = cloop_probe,
+ .bdrv_open = cloop_open,
+ .bdrv_read = cloop_read,
+ .bdrv_close = cloop_close,
+};
+
+static void bdrv_cloop_init(void)
+{
+ bdrv_register(&bdrv_cloop);
+}
+
+block_init(bdrv_cloop_init);
diff --git a/block/cow.c b/block/cow.c
new file mode 100644
index 0000000..4cf543c
--- /dev/null
+++ b/block/cow.c
@@ -0,0 +1,324 @@
+/*
+ * Block driver for the COW format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+/**************************************************************/
+/* COW block driver using file system holes */
+
+/* user mode linux compatible COW file */
+#define COW_MAGIC 0x4f4f4f4d /* MOOO */
+#define COW_VERSION 2
+
+struct cow_header_v2 {
+ uint32_t magic;
+ uint32_t version;
+ char backing_file[1024];
+ int32_t mtime;
+ uint64_t size;
+ uint32_t sectorsize;
+};
+
+typedef struct BDRVCowState {
+ int64_t cow_sectors_offset;
+} BDRVCowState;
+
+static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const struct cow_header_v2 *cow_header = (const void *)buf;
+
+ if (buf_size >= sizeof(struct cow_header_v2) &&
+ be32_to_cpu(cow_header->magic) == COW_MAGIC &&
+ be32_to_cpu(cow_header->version) == COW_VERSION)
+ return 100;
+ else
+ return 0;
+}
+
+static int cow_open(BlockDriverState *bs, int flags)
+{
+ BDRVCowState *s = bs->opaque;
+ struct cow_header_v2 cow_header;
+ int bitmap_size;
+ int64_t size;
+
+ /* see if it is a cow image */
+ if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) !=
+ sizeof(cow_header)) {
+ goto fail;
+ }
+
+ if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
+ be32_to_cpu(cow_header.version) != COW_VERSION) {
+ goto fail;
+ }
+
+ /* cow image found */
+ size = be64_to_cpu(cow_header.size);
+ bs->total_sectors = size / 512;
+
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ cow_header.backing_file);
+
+ bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
+ s->cow_sectors_offset = (bitmap_size + 511) & ~511;
+ return 0;
+ fail:
+ return -1;
+}
+
+/*
+ * XXX(hch): right now these functions are extremly ineffcient.
+ * We should just read the whole bitmap we'll need in one go instead.
+ */
+static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum)
+{
+ uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
+ uint8_t bitmap;
+ int ret;
+
+ ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
+ if (ret < 0) {
+ return ret;
+ }
+
+ bitmap |= (1 << (bitnum % 8));
+
+ ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap));
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
+{
+ uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
+ uint8_t bitmap;
+ int ret;
+
+ ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
+ if (ret < 0) {
+ return ret;
+ }
+
+ return !!(bitmap & (1 << (bitnum % 8)));
+}
+
+/* Return true if first block has been changed (ie. current version is
+ * in COW file). Set the number of continuous blocks for which that
+ * is true. */
+static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *num_same)
+{
+ int changed;
+
+ if (nb_sectors == 0) {
+ *num_same = nb_sectors;
+ return 0;
+ }
+
+ changed = is_bit_set(bs, sector_num);
+ if (changed < 0) {
+ return 0; /* XXX: how to return I/O errors? */
+ }
+
+ for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
+ if (is_bit_set(bs, sector_num + *num_same) != changed)
+ break;
+ }
+
+ return changed;
+}
+
+static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors)
+{
+ int error = 0;
+ int i;
+
+ for (i = 0; i < nb_sectors; i++) {
+ error = cow_set_bit(bs, sector_num + i);
+ if (error) {
+ break;
+ }
+ }
+
+ return error;
+}
+
+static int cow_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVCowState *s = bs->opaque;
+ int ret, n;
+
+ while (nb_sectors > 0) {
+ if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) {
+ ret = bdrv_pread(bs->file,
+ s->cow_sectors_offset + sector_num * 512,
+ buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ } else {
+ if (bs->backing_hd) {
+ /* read from the base image */
+ ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
+ if (ret < 0)
+ return -1;
+ } else {
+ memset(buf, 0, n * 512);
+ }
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int cow_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVCowState *s = bs->opaque;
+ int ret;
+
+ ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512,
+ buf, nb_sectors * 512);
+ if (ret != nb_sectors * 512)
+ return -1;
+
+ return cow_update_bitmap(bs, sector_num, nb_sectors);
+}
+
+static void cow_close(BlockDriverState *bs)
+{
+}
+
+static int cow_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd, cow_fd;
+ struct cow_header_v2 cow_header;
+ struct stat st;
+ int64_t image_sectors = 0;
+ const char *image_filename = NULL;
+ int ret;
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ image_sectors = options->value.n / 512;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ image_filename = options->value.s;
+ }
+ options++;
+ }
+
+ cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ 0644);
+ if (cow_fd < 0)
+ return -errno;
+ memset(&cow_header, 0, sizeof(cow_header));
+ cow_header.magic = cpu_to_be32(COW_MAGIC);
+ cow_header.version = cpu_to_be32(COW_VERSION);
+ if (image_filename) {
+ /* Note: if no file, we put a dummy mtime */
+ cow_header.mtime = cpu_to_be32(0);
+
+ fd = open(image_filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ close(cow_fd);
+ goto mtime_fail;
+ }
+ if (fstat(fd, &st) != 0) {
+ close(fd);
+ goto mtime_fail;
+ }
+ close(fd);
+ cow_header.mtime = cpu_to_be32(st.st_mtime);
+ mtime_fail:
+ pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
+ image_filename);
+ }
+ cow_header.sectorsize = cpu_to_be32(512);
+ cow_header.size = cpu_to_be64(image_sectors * 512);
+ ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header));
+ if (ret != sizeof(cow_header)) {
+ ret = -errno;
+ goto exit;
+ }
+
+ /* resize to include at least all the bitmap */
+ ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
+ if (ret) {
+ ret = -errno;
+ goto exit;
+ }
+
+exit:
+ close(cow_fd);
+ return ret;
+}
+
+static int cow_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+static QEMUOptionParameter cow_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_BACKING_FILE,
+ .type = OPT_STRING,
+ .help = "File name of a base image"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_cow = {
+ .format_name = "cow",
+ .instance_size = sizeof(BDRVCowState),
+ .bdrv_probe = cow_probe,
+ .bdrv_open = cow_open,
+ .bdrv_read = cow_read,
+ .bdrv_write = cow_write,
+ .bdrv_close = cow_close,
+ .bdrv_create = cow_create,
+ .bdrv_flush = cow_flush,
+ .bdrv_is_allocated = cow_is_allocated,
+
+ .create_options = cow_create_options,
+};
+
+static void bdrv_cow_init(void)
+{
+ bdrv_register(&bdrv_cow);
+}
+
+block_init(bdrv_cow_init);
diff --git a/block/curl.c b/block/curl.c
new file mode 100644
index 0000000..407f095
--- /dev/null
+++ b/block/curl.c
@@ -0,0 +1,564 @@
+/*
+ * QEMU Block driver for CURL images
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include <curl/curl.h>
+
+// #define DEBUG
+// #define DEBUG_VERBOSE
+
+#ifdef DEBUG_CURL
+#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define CURL_NUM_STATES 8
+#define CURL_NUM_ACB 8
+#define SECTOR_SIZE 512
+#define READ_AHEAD_SIZE (256 * 1024)
+
+#define FIND_RET_NONE 0
+#define FIND_RET_OK 1
+#define FIND_RET_WAIT 2
+
+struct BDRVCURLState;
+
+typedef struct CURLAIOCB {
+ BlockDriverAIOCB common;
+ QEMUIOVector *qiov;
+ size_t start;
+ size_t end;
+} CURLAIOCB;
+
+typedef struct CURLState
+{
+ struct BDRVCURLState *s;
+ CURLAIOCB *acb[CURL_NUM_ACB];
+ CURL *curl;
+ char *orig_buf;
+ size_t buf_start;
+ size_t buf_off;
+ size_t buf_len;
+ char range[128];
+ char errmsg[CURL_ERROR_SIZE];
+ char in_use;
+} CURLState;
+
+typedef struct BDRVCURLState {
+ CURLM *multi;
+ size_t len;
+ CURLState states[CURL_NUM_STATES];
+ char *url;
+ size_t readahead_size;
+} BDRVCURLState;
+
+static void curl_clean_state(CURLState *s);
+static void curl_multi_do(void *arg);
+
+static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
+ void *s, void *sp)
+{
+ DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
+ switch (action) {
+ case CURL_POLL_IN:
+ qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s);
+ break;
+ case CURL_POLL_OUT:
+ qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, NULL, NULL, s);
+ break;
+ case CURL_POLL_INOUT:
+ qemu_aio_set_fd_handler(fd, curl_multi_do,
+ curl_multi_do, NULL, NULL, s);
+ break;
+ case CURL_POLL_REMOVE:
+ qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
+ break;
+ }
+
+ return 0;
+}
+
+static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
+{
+ CURLState *s = ((CURLState*)opaque);
+ size_t realsize = size * nmemb;
+ size_t fsize;
+
+ if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
+ s->s->len = fsize;
+ }
+
+ return realsize;
+}
+
+static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
+{
+ CURLState *s = ((CURLState*)opaque);
+ size_t realsize = size * nmemb;
+ int i;
+
+ DPRINTF("CURL: Just reading %zd bytes\n", realsize);
+
+ if (!s || !s->orig_buf)
+ goto read_end;
+
+ memcpy(s->orig_buf + s->buf_off, ptr, realsize);
+ s->buf_off += realsize;
+
+ for(i=0; i<CURL_NUM_ACB; i++) {
+ CURLAIOCB *acb = s->acb[i];
+
+ if (!acb)
+ continue;
+
+ if ((s->buf_off >= acb->end)) {
+ qemu_iovec_from_buffer(acb->qiov, s->orig_buf + acb->start,
+ acb->end - acb->start);
+ acb->common.cb(acb->common.opaque, 0);
+ qemu_aio_release(acb);
+ s->acb[i] = NULL;
+ }
+ }
+
+read_end:
+ return realsize;
+}
+
+static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
+ CURLAIOCB *acb)
+{
+ int i;
+ size_t end = start + len;
+
+ for (i=0; i<CURL_NUM_STATES; i++) {
+ CURLState *state = &s->states[i];
+ size_t buf_end = (state->buf_start + state->buf_off);
+ size_t buf_fend = (state->buf_start + state->buf_len);
+
+ if (!state->orig_buf)
+ continue;
+ if (!state->buf_off)
+ continue;
+
+ // Does the existing buffer cover our section?
+ if ((start >= state->buf_start) &&
+ (start <= buf_end) &&
+ (end >= state->buf_start) &&
+ (end <= buf_end))
+ {
+ char *buf = state->orig_buf + (start - state->buf_start);
+
+ qemu_iovec_from_buffer(acb->qiov, buf, len);
+ acb->common.cb(acb->common.opaque, 0);
+
+ return FIND_RET_OK;
+ }
+
+ // Wait for unfinished chunks
+ if ((start >= state->buf_start) &&
+ (start <= buf_fend) &&
+ (end >= state->buf_start) &&
+ (end <= buf_fend))
+ {
+ int j;
+
+ acb->start = start - state->buf_start;
+ acb->end = acb->start + len;
+
+ for (j=0; j<CURL_NUM_ACB; j++) {
+ if (!state->acb[j]) {
+ state->acb[j] = acb;
+ return FIND_RET_WAIT;
+ }
+ }
+ }
+ }
+
+ return FIND_RET_NONE;
+}
+
+static void curl_multi_do(void *arg)
+{
+ BDRVCURLState *s = (BDRVCURLState *)arg;
+ int running;
+ int r;
+ int msgs_in_queue;
+
+ if (!s->multi)
+ return;
+
+ do {
+ r = curl_multi_socket_all(s->multi, &running);
+ } while(r == CURLM_CALL_MULTI_PERFORM);
+
+ /* Try to find done transfers, so we can free the easy
+ * handle again. */
+ do {
+ CURLMsg *msg;
+ msg = curl_multi_info_read(s->multi, &msgs_in_queue);
+
+ if (!msg)
+ break;
+ if (msg->msg == CURLMSG_NONE)
+ break;
+
+ switch (msg->msg) {
+ case CURLMSG_DONE:
+ {
+ CURLState *state = NULL;
+ curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
+ curl_clean_state(state);
+ break;
+ }
+ default:
+ msgs_in_queue = 0;
+ break;
+ }
+ } while(msgs_in_queue);
+}
+
+static CURLState *curl_init_state(BDRVCURLState *s)
+{
+ CURLState *state = NULL;
+ int i, j;
+
+ do {
+ for (i=0; i<CURL_NUM_STATES; i++) {
+ for (j=0; j<CURL_NUM_ACB; j++)
+ if (s->states[i].acb[j])
+ continue;
+ if (s->states[i].in_use)
+ continue;
+
+ state = &s->states[i];
+ state->in_use = 1;
+ break;
+ }
+ if (!state) {
+ usleep(100);
+ curl_multi_do(s);
+ }
+ } while(!state);
+
+ if (state->curl)
+ goto has_curl;
+
+ state->curl = curl_easy_init();
+ if (!state->curl)
+ return NULL;
+ curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
+ curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
+ curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
+ curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
+ curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
+ curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
+ curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
+
+#ifdef DEBUG_VERBOSE
+ curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
+#endif
+
+has_curl:
+
+ state->s = s;
+
+ return state;
+}
+
+static void curl_clean_state(CURLState *s)
+{
+ if (s->s->multi)
+ curl_multi_remove_handle(s->s->multi, s->curl);
+ s->in_use = 0;
+}
+
+static int curl_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVCURLState *s = bs->opaque;
+ CURLState *state = NULL;
+ double d;
+
+ #define RA_OPTSTR ":readahead="
+ char *file;
+ char *ra;
+ const char *ra_val;
+ int parse_state = 0;
+
+ static int inited = 0;
+
+ file = qemu_strdup(filename);
+ s->readahead_size = READ_AHEAD_SIZE;
+
+ /* Parse a trailing ":readahead=#:" param, if present. */
+ ra = file + strlen(file) - 1;
+ while (ra >= file) {
+ if (parse_state == 0) {
+ if (*ra == ':')
+ parse_state++;
+ else
+ break;
+ } else if (parse_state == 1) {
+ if (*ra > '9' || *ra < '0') {
+ char *opt_start = ra - strlen(RA_OPTSTR) + 1;
+ if (opt_start > file &&
+ strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
+ ra_val = ra + 1;
+ ra -= strlen(RA_OPTSTR) - 1;
+ *ra = '\0';
+ s->readahead_size = atoi(ra_val);
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ ra--;
+ }
+
+ if ((s->readahead_size & 0x1ff) != 0) {
+ fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
+ s->readahead_size);
+ goto out_noclean;
+ }
+
+ if (!inited) {
+ curl_global_init(CURL_GLOBAL_ALL);
+ inited = 1;
+ }
+
+ DPRINTF("CURL: Opening %s\n", file);
+ s->url = file;
+ state = curl_init_state(s);
+ if (!state)
+ goto out_noclean;
+
+ // Get file size
+
+ curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
+ curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
+ if (curl_easy_perform(state->curl))
+ goto out;
+ curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
+ curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
+ curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
+ if (d)
+ s->len = (size_t)d;
+ else if(!s->len)
+ goto out;
+ DPRINTF("CURL: Size = %zd\n", s->len);
+
+ curl_clean_state(state);
+ curl_easy_cleanup(state->curl);
+ state->curl = NULL;
+
+ // Now we know the file exists and its size, so let's
+ // initialize the multi interface!
+
+ s->multi = curl_multi_init();
+ curl_multi_setopt( s->multi, CURLMOPT_SOCKETDATA, s);
+ curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb );
+ curl_multi_do(s);
+
+ return 0;
+
+out:
+ fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
+ curl_easy_cleanup(state->curl);
+ state->curl = NULL;
+out_noclean:
+ qemu_free(file);
+ return -EINVAL;
+}
+
+static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ // Do we have to implement canceling? Seems to work without...
+}
+
+static AIOPool curl_aio_pool = {
+ .aiocb_size = sizeof(CURLAIOCB),
+ .cancel = curl_aio_cancel,
+};
+
+static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVCURLState *s = bs->opaque;
+ CURLAIOCB *acb;
+ size_t start = sector_num * SECTOR_SIZE;
+ size_t end;
+ CURLState *state;
+
+ acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
+ if (!acb)
+ return NULL;
+
+ acb->qiov = qiov;
+
+ // In case we have the requested data already (e.g. read-ahead),
+ // we can just call the callback and be done.
+
+ switch (curl_find_buf(s, start, nb_sectors * SECTOR_SIZE, acb)) {
+ case FIND_RET_OK:
+ qemu_aio_release(acb);
+ // fall through
+ case FIND_RET_WAIT:
+ return &acb->common;
+ default:
+ break;
+ }
+
+ // No cache found, so let's start a new request
+
+ state = curl_init_state(s);
+ if (!state)
+ return NULL;
+
+ acb->start = 0;
+ acb->end = (nb_sectors * SECTOR_SIZE);
+
+ state->buf_off = 0;
+ if (state->orig_buf)
+ qemu_free(state->orig_buf);
+ state->buf_start = start;
+ state->buf_len = acb->end + s->readahead_size;
+ end = MIN(start + state->buf_len, s->len) - 1;
+ state->orig_buf = qemu_malloc(state->buf_len);
+ state->acb[0] = acb;
+
+ snprintf(state->range, 127, "%zd-%zd", start, end);
+ DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
+ (nb_sectors * SECTOR_SIZE), start, state->range);
+ curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
+
+ curl_multi_add_handle(s->multi, state->curl);
+ curl_multi_do(s);
+
+ return &acb->common;
+}
+
+static void curl_close(BlockDriverState *bs)
+{
+ BDRVCURLState *s = bs->opaque;
+ int i;
+
+ DPRINTF("CURL: Close\n");
+ for (i=0; i<CURL_NUM_STATES; i++) {
+ if (s->states[i].in_use)
+ curl_clean_state(&s->states[i]);
+ if (s->states[i].curl) {
+ curl_easy_cleanup(s->states[i].curl);
+ s->states[i].curl = NULL;
+ }
+ if (s->states[i].orig_buf) {
+ qemu_free(s->states[i].orig_buf);
+ s->states[i].orig_buf = NULL;
+ }
+ }
+ if (s->multi)
+ curl_multi_cleanup(s->multi);
+ if (s->url)
+ free(s->url);
+}
+
+static int64_t curl_getlength(BlockDriverState *bs)
+{
+ BDRVCURLState *s = bs->opaque;
+ return s->len;
+}
+
+static BlockDriver bdrv_http = {
+ .format_name = "http",
+ .protocol_name = "http",
+
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
+
+ .bdrv_aio_readv = curl_aio_readv,
+};
+
+static BlockDriver bdrv_https = {
+ .format_name = "https",
+ .protocol_name = "https",
+
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
+
+ .bdrv_aio_readv = curl_aio_readv,
+};
+
+static BlockDriver bdrv_ftp = {
+ .format_name = "ftp",
+ .protocol_name = "ftp",
+
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
+
+ .bdrv_aio_readv = curl_aio_readv,
+};
+
+static BlockDriver bdrv_ftps = {
+ .format_name = "ftps",
+ .protocol_name = "ftps",
+
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
+
+ .bdrv_aio_readv = curl_aio_readv,
+};
+
+static BlockDriver bdrv_tftp = {
+ .format_name = "tftp",
+ .protocol_name = "tftp",
+
+ .instance_size = sizeof(BDRVCURLState),
+ .bdrv_file_open = curl_open,
+ .bdrv_close = curl_close,
+ .bdrv_getlength = curl_getlength,
+
+ .bdrv_aio_readv = curl_aio_readv,
+};
+
+static void curl_block_init(void)
+{
+ bdrv_register(&bdrv_http);
+ bdrv_register(&bdrv_https);
+ bdrv_register(&bdrv_ftp);
+ bdrv_register(&bdrv_ftps);
+ bdrv_register(&bdrv_tftp);
+}
+
+block_init(curl_block_init);
diff --git a/block/dmg.c b/block/dmg.c
new file mode 100644
index 0000000..a3c815b
--- /dev/null
+++ b/block/dmg.c
@@ -0,0 +1,312 @@
+/*
+ * QEMU Block driver for DMG images
+ *
+ * Copyright (c) 2004 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "bswap.h"
+#include "module.h"
+#include <zlib.h>
+
+typedef struct BDRVDMGState {
+ /* each chunk contains a certain number of sectors,
+ * offsets[i] is the offset in the .dmg file,
+ * lengths[i] is the length of the compressed chunk,
+ * sectors[i] is the sector beginning at offsets[i],
+ * sectorcounts[i] is the number of sectors in that chunk,
+ * the sectors array is ordered
+ * 0<=i<n_chunks */
+
+ uint32_t n_chunks;
+ uint32_t* types;
+ uint64_t* offsets;
+ uint64_t* lengths;
+ uint64_t* sectors;
+ uint64_t* sectorcounts;
+ uint32_t current_chunk;
+ uint8_t *compressed_chunk;
+ uint8_t *uncompressed_chunk;
+ z_stream zstream;
+} BDRVDMGState;
+
+static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ int len=strlen(filename);
+ if(len>4 && !strcmp(filename+len-4,".dmg"))
+ return 2;
+ return 0;
+}
+
+static off_t read_off(BlockDriverState *bs, int64_t offset)
+{
+ uint64_t buffer;
+ if (bdrv_pread(bs->file, offset, &buffer, 8) < 8)
+ return 0;
+ return be64_to_cpu(buffer);
+}
+
+static off_t read_uint32(BlockDriverState *bs, int64_t offset)
+{
+ uint32_t buffer;
+ if (bdrv_pread(bs->file, offset, &buffer, 4) < 4)
+ return 0;
+ return be32_to_cpu(buffer);
+}
+
+static int dmg_open(BlockDriverState *bs, int flags)
+{
+ BDRVDMGState *s = bs->opaque;
+ off_t info_begin,info_end,last_in_offset,last_out_offset;
+ uint32_t count;
+ uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
+ int64_t offset;
+
+ bs->read_only = 1;
+ s->n_chunks = 0;
+ s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
+
+ /* read offset of info blocks */
+ offset = bdrv_getlength(bs->file);
+ if (offset < 0) {
+ goto fail;
+ }
+ offset -= 0x1d8;
+
+ info_begin = read_off(bs, offset);
+ if (info_begin == 0) {
+ goto fail;
+ }
+
+ if (read_uint32(bs, info_begin) != 0x100) {
+ goto fail;
+ }
+
+ count = read_uint32(bs, info_begin + 4);
+ if (count == 0) {
+ goto fail;
+ }
+ info_end = info_begin + count;
+
+ offset = info_begin + 0x100;
+
+ /* read offsets */
+ last_in_offset = last_out_offset = 0;
+ while (offset < info_end) {
+ uint32_t type;
+
+ count = read_uint32(bs, offset);
+ if(count==0)
+ goto fail;
+ offset += 4;
+
+ type = read_uint32(bs, offset);
+ if (type == 0x6d697368 && count >= 244) {
+ int new_size, chunk_count;
+
+ offset += 4;
+ offset += 200;
+
+ chunk_count = (count-204)/40;
+ new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
+ s->types = qemu_realloc(s->types, new_size/2);
+ s->offsets = qemu_realloc(s->offsets, new_size);
+ s->lengths = qemu_realloc(s->lengths, new_size);
+ s->sectors = qemu_realloc(s->sectors, new_size);
+ s->sectorcounts = qemu_realloc(s->sectorcounts, new_size);
+
+ for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
+ s->types[i] = read_uint32(bs, offset);
+ offset += 4;
+ if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
+ if(s->types[i]==0xffffffff) {
+ last_in_offset = s->offsets[i-1]+s->lengths[i-1];
+ last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
+ }
+ chunk_count--;
+ i--;
+ offset += 36;
+ continue;
+ }
+ offset += 4;
+
+ s->sectors[i] = last_out_offset+read_off(bs, offset);
+ offset += 8;
+
+ s->sectorcounts[i] = read_off(bs, offset);
+ offset += 8;
+
+ s->offsets[i] = last_in_offset+read_off(bs, offset);
+ offset += 8;
+
+ s->lengths[i] = read_off(bs, offset);
+ offset += 8;
+
+ if(s->lengths[i]>max_compressed_size)
+ max_compressed_size = s->lengths[i];
+ if(s->sectorcounts[i]>max_sectors_per_chunk)
+ max_sectors_per_chunk = s->sectorcounts[i];
+ }
+ s->n_chunks+=chunk_count;
+ }
+ }
+
+ /* initialize zlib engine */
+ s->compressed_chunk = qemu_malloc(max_compressed_size+1);
+ s->uncompressed_chunk = qemu_malloc(512*max_sectors_per_chunk);
+ if(inflateInit(&s->zstream) != Z_OK)
+ goto fail;
+
+ s->current_chunk = s->n_chunks;
+
+ return 0;
+fail:
+ return -1;
+}
+
+static inline int is_sector_in_chunk(BDRVDMGState* s,
+ uint32_t chunk_num,int sector_num)
+{
+ if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
+ s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
+ return 0;
+ else
+ return -1;
+}
+
+static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
+{
+ /* binary search */
+ uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
+ while(chunk1!=chunk2) {
+ chunk3 = (chunk1+chunk2)/2;
+ if(s->sectors[chunk3]>sector_num)
+ chunk2 = chunk3;
+ else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
+ return chunk3;
+ else
+ chunk1 = chunk3;
+ }
+ return s->n_chunks; /* error */
+}
+
+static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
+{
+ BDRVDMGState *s = bs->opaque;
+
+ if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
+ int ret;
+ uint32_t chunk = search_chunk(s,sector_num);
+
+ if(chunk>=s->n_chunks)
+ return -1;
+
+ s->current_chunk = s->n_chunks;
+ switch(s->types[chunk]) {
+ case 0x80000005: { /* zlib compressed */
+ int i;
+
+ /* we need to buffer, because only the chunk as whole can be
+ * inflated. */
+ i=0;
+ do {
+ ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
+ s->compressed_chunk+i, s->lengths[chunk]-i);
+ if(ret<0 && errno==EINTR)
+ ret=0;
+ i+=ret;
+ } while(ret>=0 && ret+i<s->lengths[chunk]);
+
+ if (ret != s->lengths[chunk])
+ return -1;
+
+ s->zstream.next_in = s->compressed_chunk;
+ s->zstream.avail_in = s->lengths[chunk];
+ s->zstream.next_out = s->uncompressed_chunk;
+ s->zstream.avail_out = 512*s->sectorcounts[chunk];
+ ret = inflateReset(&s->zstream);
+ if(ret != Z_OK)
+ return -1;
+ ret = inflate(&s->zstream, Z_FINISH);
+ if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
+ return -1;
+ break; }
+ case 1: /* copy */
+ ret = bdrv_pread(bs->file, s->offsets[chunk],
+ s->uncompressed_chunk, s->lengths[chunk]);
+ if (ret != s->lengths[chunk])
+ return -1;
+ break;
+ case 2: /* zero */
+ memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
+ break;
+ }
+ s->current_chunk = chunk;
+ }
+ return 0;
+}
+
+static int dmg_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVDMGState *s = bs->opaque;
+ int i;
+
+ for(i=0;i<nb_sectors;i++) {
+ uint32_t sector_offset_in_chunk;
+ if(dmg_read_chunk(bs, sector_num+i) != 0)
+ return -1;
+ sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
+ memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
+ }
+ return 0;
+}
+
+static void dmg_close(BlockDriverState *bs)
+{
+ BDRVDMGState *s = bs->opaque;
+ if(s->n_chunks>0) {
+ free(s->types);
+ free(s->offsets);
+ free(s->lengths);
+ free(s->sectors);
+ free(s->sectorcounts);
+ }
+ free(s->compressed_chunk);
+ free(s->uncompressed_chunk);
+ inflateEnd(&s->zstream);
+}
+
+static BlockDriver bdrv_dmg = {
+ .format_name = "dmg",
+ .instance_size = sizeof(BDRVDMGState),
+ .bdrv_probe = dmg_probe,
+ .bdrv_open = dmg_open,
+ .bdrv_read = dmg_read,
+ .bdrv_close = dmg_close,
+};
+
+static void bdrv_dmg_init(void)
+{
+ bdrv_register(&bdrv_dmg);
+}
+
+block_init(bdrv_dmg_init);
diff --git a/block/nbd.c b/block/nbd.c
new file mode 100644
index 0000000..c8dc763
--- /dev/null
+++ b/block/nbd.c
@@ -0,0 +1,221 @@
+/*
+ * QEMU Block driver for NBD
+ *
+ * Copyright (C) 2008 Bull S.A.S.
+ * Author: Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ * Some parts:
+ * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "nbd.h"
+#include "module.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#define EN_OPTSTR ":exportname="
+
+typedef struct BDRVNBDState {
+ int sock;
+ off_t size;
+ size_t blocksize;
+} BDRVNBDState;
+
+static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
+{
+ BDRVNBDState *s = bs->opaque;
+ uint32_t nbdflags;
+
+ char *file;
+ char *name;
+ const char *host;
+ const char *unixpath;
+ int sock;
+ off_t size;
+ size_t blocksize;
+ int ret;
+ int err = -EINVAL;
+
+ file = qemu_strdup(filename);
+
+ name = strstr(file, EN_OPTSTR);
+ if (name) {
+ if (name[strlen(EN_OPTSTR)] == 0) {
+ goto out;
+ }
+ name[0] = 0;
+ name += strlen(EN_OPTSTR);
+ }
+
+ if (!strstart(file, "nbd:", &host)) {
+ goto out;
+ }
+
+ if (strstart(host, "unix:", &unixpath)) {
+
+ if (unixpath[0] != '/') {
+ goto out;
+ }
+
+ sock = unix_socket_outgoing(unixpath);
+
+ } else {
+ uint16_t port = NBD_DEFAULT_PORT;
+ char *p, *r;
+ char hostname[128];
+
+ pstrcpy(hostname, 128, host);
+
+ p = strchr(hostname, ':');
+ if (p != NULL) {
+ *p = '\0';
+ p++;
+
+ port = strtol(p, &r, 0);
+ if (r == p) {
+ goto out;
+ }
+ }
+
+ sock = tcp_socket_outgoing(hostname, port);
+ }
+
+ if (sock == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize);
+ if (ret == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ s->sock = sock;
+ s->size = size;
+ s->blocksize = blocksize;
+ err = 0;
+
+out:
+ qemu_free(file);
+ return err;
+}
+
+static int nbd_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVNBDState *s = bs->opaque;
+ struct nbd_request request;
+ struct nbd_reply reply;
+
+ request.type = NBD_CMD_READ;
+ request.handle = (uint64_t)(intptr_t)bs;
+ request.from = sector_num * 512;;
+ request.len = nb_sectors * 512;
+
+ if (nbd_send_request(s->sock, &request) == -1)
+ return -errno;
+
+ if (nbd_receive_reply(s->sock, &reply) == -1)
+ return -errno;
+
+ if (reply.error !=0)
+ return -reply.error;
+
+ if (reply.handle != request.handle)
+ return -EIO;
+
+ if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
+ return -EIO;
+
+ return 0;
+}
+
+static int nbd_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVNBDState *s = bs->opaque;
+ struct nbd_request request;
+ struct nbd_reply reply;
+
+ request.type = NBD_CMD_WRITE;
+ request.handle = (uint64_t)(intptr_t)bs;
+ request.from = sector_num * 512;;
+ request.len = nb_sectors * 512;
+
+ if (nbd_send_request(s->sock, &request) == -1)
+ return -errno;
+
+ if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
+ return -EIO;
+
+ if (nbd_receive_reply(s->sock, &reply) == -1)
+ return -errno;
+
+ if (reply.error !=0)
+ return -reply.error;
+
+ if (reply.handle != request.handle)
+ return -EIO;
+
+ return 0;
+}
+
+static void nbd_close(BlockDriverState *bs)
+{
+ BDRVNBDState *s = bs->opaque;
+ struct nbd_request request;
+
+ request.type = NBD_CMD_DISC;
+ request.handle = (uint64_t)(intptr_t)bs;
+ request.from = 0;
+ request.len = 0;
+ nbd_send_request(s->sock, &request);
+
+ close(s->sock);
+}
+
+static int64_t nbd_getlength(BlockDriverState *bs)
+{
+ BDRVNBDState *s = bs->opaque;
+
+ return s->size;
+}
+
+static BlockDriver bdrv_nbd = {
+ .format_name = "nbd",
+ .instance_size = sizeof(BDRVNBDState),
+ .bdrv_file_open = nbd_open,
+ .bdrv_read = nbd_read,
+ .bdrv_write = nbd_write,
+ .bdrv_close = nbd_close,
+ .bdrv_getlength = nbd_getlength,
+ .protocol_name = "nbd",
+};
+
+static void bdrv_nbd_init(void)
+{
+ bdrv_register(&bdrv_nbd);
+}
+
+block_init(bdrv_nbd_init);
diff --git a/block/parallels.c b/block/parallels.c
new file mode 100644
index 0000000..35a14aa
--- /dev/null
+++ b/block/parallels.c
@@ -0,0 +1,157 @@
+/*
+ * Block driver for Parallels disk image format
+ *
+ * Copyright (c) 2007 Alex Beregszaszi
+ *
+ * This code is based on comparing different disk images created by Parallels.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+/**************************************************************/
+
+#define HEADER_MAGIC "WithoutFreeSpace"
+#define HEADER_VERSION 2
+#define HEADER_SIZE 64
+
+// always little-endian
+struct parallels_header {
+ char magic[16]; // "WithoutFreeSpace"
+ uint32_t version;
+ uint32_t heads;
+ uint32_t cylinders;
+ uint32_t tracks;
+ uint32_t catalog_entries;
+ uint32_t nb_sectors;
+ char padding[24];
+} __attribute__((packed));
+
+typedef struct BDRVParallelsState {
+
+ uint32_t *catalog_bitmap;
+ int catalog_size;
+
+ int tracks;
+} BDRVParallelsState;
+
+static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const struct parallels_header *ph = (const void *)buf;
+
+ if (buf_size < HEADER_SIZE)
+ return 0;
+
+ if (!memcmp(ph->magic, HEADER_MAGIC, 16) &&
+ (le32_to_cpu(ph->version) == HEADER_VERSION))
+ return 100;
+
+ return 0;
+}
+
+static int parallels_open(BlockDriverState *bs, int flags)
+{
+ BDRVParallelsState *s = bs->opaque;
+ int i;
+ struct parallels_header ph;
+
+ bs->read_only = 1; // no write support yet
+
+ if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph))
+ goto fail;
+
+ if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
+ (le32_to_cpu(ph.version) != HEADER_VERSION)) {
+ goto fail;
+ }
+
+ bs->total_sectors = le32_to_cpu(ph.nb_sectors);
+
+ s->tracks = le32_to_cpu(ph.tracks);
+
+ s->catalog_size = le32_to_cpu(ph.catalog_entries);
+ s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
+ if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) !=
+ s->catalog_size * 4)
+ goto fail;
+ for (i = 0; i < s->catalog_size; i++)
+ le32_to_cpus(&s->catalog_bitmap[i]);
+
+ return 0;
+fail:
+ if (s->catalog_bitmap)
+ qemu_free(s->catalog_bitmap);
+ return -1;
+}
+
+static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+ BDRVParallelsState *s = bs->opaque;
+ uint32_t index, offset;
+
+ index = sector_num / s->tracks;
+ offset = sector_num % s->tracks;
+
+ /* not allocated */
+ if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
+ return -1;
+ return (uint64_t)(s->catalog_bitmap[index] + offset) * 512;
+}
+
+static int parallels_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ while (nb_sectors > 0) {
+ int64_t position = seek_to_sector(bs, sector_num);
+ if (position >= 0) {
+ if (bdrv_pread(bs->file, position, buf, 512) != 512)
+ return -1;
+ } else {
+ memset(buf, 0, 512);
+ }
+ nb_sectors--;
+ sector_num++;
+ buf += 512;
+ }
+ return 0;
+}
+
+static void parallels_close(BlockDriverState *bs)
+{
+ BDRVParallelsState *s = bs->opaque;
+ qemu_free(s->catalog_bitmap);
+}
+
+static BlockDriver bdrv_parallels = {
+ .format_name = "parallels",
+ .instance_size = sizeof(BDRVParallelsState),
+ .bdrv_probe = parallels_probe,
+ .bdrv_open = parallels_open,
+ .bdrv_read = parallels_read,
+ .bdrv_close = parallels_close,
+};
+
+static void bdrv_parallels_init(void)
+{
+ bdrv_register(&bdrv_parallels);
+}
+
+block_init(bdrv_parallels_init);
diff --git a/block/qcow.c b/block/qcow.c
new file mode 100644
index 0000000..f67d3d3
--- /dev/null
+++ b/block/qcow.c
@@ -0,0 +1,975 @@
+/*
+ * Block driver for the QCOW format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+#include <zlib.h>
+#include "aes.h"
+
+/**************************************************************/
+/* QEMU COW block driver with compression and encryption support */
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW_VERSION 1
+
+#define QCOW_CRYPT_NONE 0
+#define QCOW_CRYPT_AES 1
+
+#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+
+typedef struct QCowHeader {
+ uint32_t magic;
+ uint32_t version;
+ uint64_t backing_file_offset;
+ uint32_t backing_file_size;
+ uint32_t mtime;
+ uint64_t size; /* in bytes */
+ uint8_t cluster_bits;
+ uint8_t l2_bits;
+ uint32_t crypt_method;
+ uint64_t l1_table_offset;
+} QCowHeader;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct BDRVQcowState {
+ int cluster_bits;
+ int cluster_size;
+ int cluster_sectors;
+ int l2_bits;
+ int l2_size;
+ int l1_size;
+ uint64_t cluster_offset_mask;
+ uint64_t l1_table_offset;
+ uint64_t *l1_table;
+ uint64_t *l2_cache;
+ uint64_t l2_cache_offsets[L2_CACHE_SIZE];
+ uint32_t l2_cache_counts[L2_CACHE_SIZE];
+ uint8_t *cluster_cache;
+ uint8_t *cluster_data;
+ uint64_t cluster_cache_offset;
+ uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+ uint32_t crypt_method_header;
+ AES_KEY aes_encrypt_key;
+ AES_KEY aes_decrypt_key;
+} BDRVQcowState;
+
+static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
+
+static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const QCowHeader *cow_header = (const void *)buf;
+
+ if (buf_size >= sizeof(QCowHeader) &&
+ be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
+ be32_to_cpu(cow_header->version) == QCOW_VERSION)
+ return 100;
+ else
+ return 0;
+}
+
+static int qcow_open(BlockDriverState *bs, int flags)
+{
+ BDRVQcowState *s = bs->opaque;
+ int len, i, shift;
+ QCowHeader header;
+
+ if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header))
+ goto fail;
+ be32_to_cpus(&header.magic);
+ be32_to_cpus(&header.version);
+ be64_to_cpus(&header.backing_file_offset);
+ be32_to_cpus(&header.backing_file_size);
+ be32_to_cpus(&header.mtime);
+ be64_to_cpus(&header.size);
+ be32_to_cpus(&header.crypt_method);
+ be64_to_cpus(&header.l1_table_offset);
+
+ if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
+ goto fail;
+ if (header.size <= 1 || header.cluster_bits < 9)
+ goto fail;
+ if (header.crypt_method > QCOW_CRYPT_AES)
+ goto fail;
+ s->crypt_method_header = header.crypt_method;
+ if (s->crypt_method_header)
+ bs->encrypted = 1;
+ s->cluster_bits = header.cluster_bits;
+ s->cluster_size = 1 << s->cluster_bits;
+ s->cluster_sectors = 1 << (s->cluster_bits - 9);
+ s->l2_bits = header.l2_bits;
+ s->l2_size = 1 << s->l2_bits;
+ bs->total_sectors = header.size / 512;
+ s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1;
+
+ /* read the level 1 table */
+ shift = s->cluster_bits + s->l2_bits;
+ s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
+
+ s->l1_table_offset = header.l1_table_offset;
+ s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
+ if (!s->l1_table)
+ goto fail;
+ if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
+ s->l1_size * sizeof(uint64_t))
+ goto fail;
+ for(i = 0;i < s->l1_size; i++) {
+ be64_to_cpus(&s->l1_table[i]);
+ }
+ /* alloc L2 cache */
+ s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+ if (!s->l2_cache)
+ goto fail;
+ s->cluster_cache = qemu_malloc(s->cluster_size);
+ if (!s->cluster_cache)
+ goto fail;
+ s->cluster_data = qemu_malloc(s->cluster_size);
+ if (!s->cluster_data)
+ goto fail;
+ s->cluster_cache_offset = -1;
+
+ /* read the backing file name */
+ if (header.backing_file_offset != 0) {
+ len = header.backing_file_size;
+ if (len > 1023)
+ len = 1023;
+ if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len)
+ goto fail;
+ bs->backing_file[len] = '\0';
+ }
+ return 0;
+
+ fail:
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ qemu_free(s->cluster_cache);
+ qemu_free(s->cluster_data);
+ return -1;
+}
+
+static int qcow_set_key(BlockDriverState *bs, const char *key)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint8_t keybuf[16];
+ int len, i;
+
+ memset(keybuf, 0, 16);
+ len = strlen(key);
+ if (len > 16)
+ len = 16;
+ /* XXX: we could compress the chars to 7 bits to increase
+ entropy */
+ for(i = 0;i < len;i++) {
+ keybuf[i] = key[i];
+ }
+ s->crypt_method = s->crypt_method_header;
+
+ if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+ return -1;
+ if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+ return -1;
+#if 0
+ /* test */
+ {
+ uint8_t in[16];
+ uint8_t out[16];
+ uint8_t tmp[16];
+ for(i=0;i<16;i++)
+ in[i] = i;
+ AES_encrypt(in, tmp, &s->aes_encrypt_key);
+ AES_decrypt(tmp, out, &s->aes_decrypt_key);
+ for(i = 0; i < 16; i++)
+ printf(" %02x", tmp[i]);
+ printf("\n");
+ for(i = 0; i < 16; i++)
+ printf(" %02x", out[i]);
+ printf("\n");
+ }
+#endif
+ return 0;
+}
+
+/* The crypt function is compatible with the linux cryptoloop
+ algorithm for < 4 GB images. NOTE: out_buf == in_buf is
+ supported */
+static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+ uint8_t *out_buf, const uint8_t *in_buf,
+ int nb_sectors, int enc,
+ const AES_KEY *key)
+{
+ union {
+ uint64_t ll[2];
+ uint8_t b[16];
+ } ivec;
+ int i;
+
+ for(i = 0; i < nb_sectors; i++) {
+ ivec.ll[0] = cpu_to_le64(sector_num);
+ ivec.ll[1] = 0;
+ AES_cbc_encrypt(in_buf, out_buf, 512, key,
+ ivec.b, enc);
+ sector_num++;
+ in_buf += 512;
+ out_buf += 512;
+ }
+}
+
+/* 'allocate' is:
+ *
+ * 0 to not allocate.
+ *
+ * 1 to allocate a normal cluster (for sector indexes 'n_start' to
+ * 'n_end')
+ *
+ * 2 to allocate a compressed cluster of size
+ * 'compressed_size'. 'compressed_size' must be > 0 and <
+ * cluster_size
+ *
+ * return 0 if not allocated.
+ */
+static uint64_t get_cluster_offset(BlockDriverState *bs,
+ uint64_t offset, int allocate,
+ int compressed_size,
+ int n_start, int n_end)
+{
+ BDRVQcowState *s = bs->opaque;
+ int min_index, i, j, l1_index, l2_index;
+ uint64_t l2_offset, *l2_table, cluster_offset, tmp;
+ uint32_t min_count;
+ int new_l2_table;
+
+ l1_index = offset >> (s->l2_bits + s->cluster_bits);
+ l2_offset = s->l1_table[l1_index];
+ new_l2_table = 0;
+ if (!l2_offset) {
+ if (!allocate)
+ return 0;
+ /* allocate a new l2 entry */
+ l2_offset = bdrv_getlength(bs->file);
+ /* round to cluster size */
+ l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
+ /* update the L1 entry */
+ s->l1_table[l1_index] = l2_offset;
+ tmp = cpu_to_be64(l2_offset);
+ if (bdrv_pwrite_sync(bs->file,
+ s->l1_table_offset + l1_index * sizeof(tmp),
+ &tmp, sizeof(tmp)) < 0)
+ return 0;
+ new_l2_table = 1;
+ }
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (l2_offset == s->l2_cache_offsets[i]) {
+ /* increment the hit count */
+ if (++s->l2_cache_counts[i] == 0xffffffff) {
+ for(j = 0; j < L2_CACHE_SIZE; j++) {
+ s->l2_cache_counts[j] >>= 1;
+ }
+ }
+ l2_table = s->l2_cache + (i << s->l2_bits);
+ goto found;
+ }
+ }
+ /* not found: load a new entry in the least used one */
+ min_index = 0;
+ min_count = 0xffffffff;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (s->l2_cache_counts[i] < min_count) {
+ min_count = s->l2_cache_counts[i];
+ min_index = i;
+ }
+ }
+ l2_table = s->l2_cache + (min_index << s->l2_bits);
+ if (new_l2_table) {
+ memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
+ if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
+ s->l2_size * sizeof(uint64_t)) < 0)
+ return 0;
+ } else {
+ if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
+ s->l2_size * sizeof(uint64_t))
+ return 0;
+ }
+ s->l2_cache_offsets[min_index] = l2_offset;
+ s->l2_cache_counts[min_index] = 1;
+ found:
+ l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+ cluster_offset = be64_to_cpu(l2_table[l2_index]);
+ if (!cluster_offset ||
+ ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) {
+ if (!allocate)
+ return 0;
+ /* allocate a new cluster */
+ if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
+ (n_end - n_start) < s->cluster_sectors) {
+ /* if the cluster is already compressed, we must
+ decompress it in the case it is not completely
+ overwritten */
+ if (decompress_cluster(bs, cluster_offset) < 0)
+ return 0;
+ cluster_offset = bdrv_getlength(bs->file);
+ cluster_offset = (cluster_offset + s->cluster_size - 1) &
+ ~(s->cluster_size - 1);
+ /* write the cluster content */
+ if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) !=
+ s->cluster_size)
+ return -1;
+ } else {
+ cluster_offset = bdrv_getlength(bs->file);
+ if (allocate == 1) {
+ /* round to cluster size */
+ cluster_offset = (cluster_offset + s->cluster_size - 1) &
+ ~(s->cluster_size - 1);
+ bdrv_truncate(bs->file, cluster_offset + s->cluster_size);
+ /* if encrypted, we must initialize the cluster
+ content which won't be written */
+ if (s->crypt_method &&
+ (n_end - n_start) < s->cluster_sectors) {
+ uint64_t start_sect;
+ start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
+ memset(s->cluster_data + 512, 0x00, 512);
+ for(i = 0; i < s->cluster_sectors; i++) {
+ if (i < n_start || i >= n_end) {
+ encrypt_sectors(s, start_sect + i,
+ s->cluster_data,
+ s->cluster_data + 512, 1, 1,
+ &s->aes_encrypt_key);
+ if (bdrv_pwrite(bs->file, cluster_offset + i * 512,
+ s->cluster_data, 512) != 512)
+ return -1;
+ }
+ }
+ }
+ } else if (allocate == 2) {
+ cluster_offset |= QCOW_OFLAG_COMPRESSED |
+ (uint64_t)compressed_size << (63 - s->cluster_bits);
+ }
+ }
+ /* update L2 table */
+ tmp = cpu_to_be64(cluster_offset);
+ l2_table[l2_index] = tmp;
+ if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
+ &tmp, sizeof(tmp)) < 0)
+ return 0;
+ }
+ return cluster_offset;
+}
+
+static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ BDRVQcowState *s = bs->opaque;
+ int index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ *pnum = n;
+ return (cluster_offset != 0);
+}
+
+static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
+ const uint8_t *buf, int buf_size)
+{
+ z_stream strm1, *strm = &strm1;
+ int ret, out_len;
+
+ memset(strm, 0, sizeof(*strm));
+
+ strm->next_in = (uint8_t *)buf;
+ strm->avail_in = buf_size;
+ strm->next_out = out_buf;
+ strm->avail_out = out_buf_size;
+
+ ret = inflateInit2(strm, -12);
+ if (ret != Z_OK)
+ return -1;
+ ret = inflate(strm, Z_FINISH);
+ out_len = strm->next_out - out_buf;
+ if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
+ out_len != out_buf_size) {
+ inflateEnd(strm);
+ return -1;
+ }
+ inflateEnd(strm);
+ return 0;
+}
+
+static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, csize;
+ uint64_t coffset;
+
+ coffset = cluster_offset & s->cluster_offset_mask;
+ if (s->cluster_cache_offset != coffset) {
+ csize = cluster_offset >> (63 - s->cluster_bits);
+ csize &= (s->cluster_size - 1);
+ ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize);
+ if (ret != csize)
+ return -1;
+ if (decompress_buffer(s->cluster_cache, s->cluster_size,
+ s->cluster_data, csize) < 0) {
+ return -1;
+ }
+ s->cluster_cache_offset = coffset;
+ }
+ return 0;
+}
+
+#if 0
+
+static int qcow_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ if (!cluster_offset) {
+ if (bs->backing_hd) {
+ /* read from the base image */
+ ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
+ if (ret < 0)
+ return -1;
+ } else {
+ memset(buf, 0, 512 * n);
+ }
+ } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ if (decompress_cluster(bs, cluster_offset) < 0)
+ return -1;
+ memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
+ } else {
+ ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ if (s->crypt_method) {
+ encrypt_sectors(s, sector_num, buf, buf, n, 0,
+ &s->aes_decrypt_key);
+ }
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+#endif
+
+typedef struct QCowAIOCB {
+ BlockDriverAIOCB common;
+ int64_t sector_num;
+ QEMUIOVector *qiov;
+ uint8_t *buf;
+ void *orig_buf;
+ int nb_sectors;
+ int n;
+ uint64_t cluster_offset;
+ uint8_t *cluster_data;
+ struct iovec hd_iov;
+ QEMUIOVector hd_qiov;
+ BlockDriverAIOCB *hd_aiocb;
+} QCowAIOCB;
+
+static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common);
+ if (acb->hd_aiocb)
+ bdrv_aio_cancel(acb->hd_aiocb);
+ qemu_aio_release(acb);
+}
+
+static AIOPool qcow_aio_pool = {
+ .aiocb_size = sizeof(QCowAIOCB),
+ .cancel = qcow_aio_cancel,
+};
+
+static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int is_write)
+{
+ QCowAIOCB *acb;
+
+ acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque);
+ if (!acb)
+ return NULL;
+ acb->hd_aiocb = NULL;
+ acb->sector_num = sector_num;
+ acb->qiov = qiov;
+ if (qiov->niov > 1) {
+ acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size);
+ if (is_write)
+ qemu_iovec_to_buffer(qiov, acb->buf);
+ } else {
+ acb->buf = (uint8_t *)qiov->iov->iov_base;
+ }
+ acb->nb_sectors = nb_sectors;
+ acb->n = 0;
+ acb->cluster_offset = 0;
+ return acb;
+}
+
+static void qcow_aio_read_cb(void *opaque, int ret)
+{
+ QCowAIOCB *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVQcowState *s = bs->opaque;
+ int index_in_cluster;
+
+ acb->hd_aiocb = NULL;
+ if (ret < 0)
+ goto done;
+
+ redo:
+ /* post process the read buffer */
+ if (!acb->cluster_offset) {
+ /* nothing to do */
+ } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ /* nothing to do */
+ } else {
+ if (s->crypt_method) {
+ encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf,
+ acb->n, 0,
+ &s->aes_decrypt_key);
+ }
+ }
+
+ acb->nb_sectors -= acb->n;
+ acb->sector_num += acb->n;
+ acb->buf += acb->n * 512;
+
+ if (acb->nb_sectors == 0) {
+ /* request completed */
+ ret = 0;
+ goto done;
+ }
+
+ /* prepare next AIO request */
+ acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9,
+ 0, 0, 0, 0);
+ index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+ acb->n = s->cluster_sectors - index_in_cluster;
+ if (acb->n > acb->nb_sectors)
+ acb->n = acb->nb_sectors;
+
+ if (!acb->cluster_offset) {
+ if (bs->backing_hd) {
+ /* read from the base image */
+ acb->hd_iov.iov_base = (void *)acb->buf;
+ acb->hd_iov.iov_len = acb->n * 512;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
+ &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
+ if (acb->hd_aiocb == NULL)
+ goto done;
+ } else {
+ /* Note: in this case, no need to wait */
+ memset(acb->buf, 0, 512 * acb->n);
+ goto redo;
+ }
+ } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ /* add AIO support for compressed blocks ? */
+ if (decompress_cluster(bs, acb->cluster_offset) < 0)
+ goto done;
+ memcpy(acb->buf,
+ s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
+ goto redo;
+ } else {
+ if ((acb->cluster_offset & 511) != 0) {
+ ret = -EIO;
+ goto done;
+ }
+ acb->hd_iov.iov_base = (void *)acb->buf;
+ acb->hd_iov.iov_len = acb->n * 512;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_readv(bs->file,
+ (acb->cluster_offset >> 9) + index_in_cluster,
+ &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
+ if (acb->hd_aiocb == NULL)
+ goto done;
+ }
+
+ return;
+
+done:
+ if (acb->qiov->niov > 1) {
+ qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
+ qemu_vfree(acb->orig_buf);
+ }
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ QCowAIOCB *acb;
+
+ acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+ if (!acb)
+ return NULL;
+
+ qcow_aio_read_cb(acb, 0);
+ return &acb->common;
+}
+
+static void qcow_aio_write_cb(void *opaque, int ret)
+{
+ QCowAIOCB *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVQcowState *s = bs->opaque;
+ int index_in_cluster;
+ uint64_t cluster_offset;
+ const uint8_t *src_buf;
+
+ acb->hd_aiocb = NULL;
+
+ if (ret < 0)
+ goto done;
+
+ acb->nb_sectors -= acb->n;
+ acb->sector_num += acb->n;
+ acb->buf += acb->n * 512;
+
+ if (acb->nb_sectors == 0) {
+ /* request completed */
+ ret = 0;
+ goto done;
+ }
+
+ index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+ acb->n = s->cluster_sectors - index_in_cluster;
+ if (acb->n > acb->nb_sectors)
+ acb->n = acb->nb_sectors;
+ cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, 1, 0,
+ index_in_cluster,
+ index_in_cluster + acb->n);
+ if (!cluster_offset || (cluster_offset & 511) != 0) {
+ ret = -EIO;
+ goto done;
+ }
+ if (s->crypt_method) {
+ if (!acb->cluster_data) {
+ acb->cluster_data = qemu_mallocz(s->cluster_size);
+ if (!acb->cluster_data) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ }
+ encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
+ acb->n, 1, &s->aes_encrypt_key);
+ src_buf = acb->cluster_data;
+ } else {
+ src_buf = acb->buf;
+ }
+
+ acb->hd_iov.iov_base = (void *)src_buf;
+ acb->hd_iov.iov_len = acb->n * 512;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_writev(bs->file,
+ (cluster_offset >> 9) + index_in_cluster,
+ &acb->hd_qiov, acb->n,
+ qcow_aio_write_cb, acb);
+ if (acb->hd_aiocb == NULL)
+ goto done;
+ return;
+
+done:
+ if (acb->qiov->niov > 1)
+ qemu_vfree(acb->orig_buf);
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowAIOCB *acb;
+
+ s->cluster_cache_offset = -1; /* disable compressed cache */
+
+ acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+ if (!acb)
+ return NULL;
+
+
+ qcow_aio_write_cb(acb, 0);
+ return &acb->common;
+}
+
+static void qcow_close(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ qemu_free(s->cluster_cache);
+ qemu_free(s->cluster_data);
+}
+
+static int qcow_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd, header_size, backing_filename_len, l1_size, i, shift;
+ QCowHeader header;
+ uint64_t tmp;
+ int64_t total_size = 0;
+ const char *backing_file = NULL;
+ int flags = 0;
+ int ret;
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n / 512;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
+ flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
+ }
+ options++;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ if (fd < 0)
+ return -errno;
+ memset(&header, 0, sizeof(header));
+ header.magic = cpu_to_be32(QCOW_MAGIC);
+ header.version = cpu_to_be32(QCOW_VERSION);
+ header.size = cpu_to_be64(total_size * 512);
+ header_size = sizeof(header);
+ backing_filename_len = 0;
+ if (backing_file) {
+ if (strcmp(backing_file, "fat:")) {
+ header.backing_file_offset = cpu_to_be64(header_size);
+ backing_filename_len = strlen(backing_file);
+ header.backing_file_size = cpu_to_be32(backing_filename_len);
+ header_size += backing_filename_len;
+ } else {
+ /* special backing file for vvfat */
+ backing_file = NULL;
+ }
+ header.cluster_bits = 9; /* 512 byte cluster to avoid copying
+ unmodifyed sectors */
+ header.l2_bits = 12; /* 32 KB L2 tables */
+ } else {
+ header.cluster_bits = 12; /* 4 KB clusters */
+ header.l2_bits = 9; /* 4 KB L2 tables */
+ }
+ header_size = (header_size + 7) & ~7;
+ shift = header.cluster_bits + header.l2_bits;
+ l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
+
+ header.l1_table_offset = cpu_to_be64(header_size);
+ if (flags & BLOCK_FLAG_ENCRYPT) {
+ header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+ } else {
+ header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+ }
+
+ /* write all the data */
+ ret = qemu_write_full(fd, &header, sizeof(header));
+ if (ret != sizeof(header)) {
+ ret = -errno;
+ goto exit;
+ }
+
+ if (backing_file) {
+ ret = qemu_write_full(fd, backing_file, backing_filename_len);
+ if (ret != backing_filename_len) {
+ ret = -errno;
+ goto exit;
+ }
+
+ }
+ lseek(fd, header_size, SEEK_SET);
+ tmp = 0;
+ for(i = 0;i < l1_size; i++) {
+ ret = qemu_write_full(fd, &tmp, sizeof(tmp));
+ if (ret != sizeof(tmp)) {
+ ret = -errno;
+ goto exit;
+ }
+ }
+
+ ret = 0;
+exit:
+ close(fd);
+ return ret;
+}
+
+static int qcow_make_empty(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint32_t l1_length = s->l1_size * sizeof(uint64_t);
+ int ret;
+
+ memset(s->l1_table, 0, l1_length);
+ if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table,
+ l1_length) < 0)
+ return -1;
+ ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
+ if (ret < 0)
+ return ret;
+
+ memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+ memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
+ memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
+
+ return 0;
+}
+
+/* XXX: put compressed sectors first, then all the cluster aligned
+ tables to avoid losing bytes in alignment */
+static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ z_stream strm;
+ int ret, out_len;
+ uint8_t *out_buf;
+ uint64_t cluster_offset;
+
+ if (nb_sectors != s->cluster_sectors)
+ return -EINVAL;
+
+ out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
+ if (!out_buf)
+ return -1;
+
+ /* best compression, small window, no zlib header */
+ memset(&strm, 0, sizeof(strm));
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED, -12,
+ 9, Z_DEFAULT_STRATEGY);
+ if (ret != 0) {
+ qemu_free(out_buf);
+ return -1;
+ }
+
+ strm.avail_in = s->cluster_size;
+ strm.next_in = (uint8_t *)buf;
+ strm.avail_out = s->cluster_size;
+ strm.next_out = out_buf;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ qemu_free(out_buf);
+ deflateEnd(&strm);
+ return -1;
+ }
+ out_len = strm.next_out - out_buf;
+
+ deflateEnd(&strm);
+
+ if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+ /* could not compress: write normal cluster */
+ bdrv_write(bs, sector_num, buf, s->cluster_sectors);
+ } else {
+ cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
+ out_len, 0, 0);
+ cluster_offset &= s->cluster_offset_mask;
+ if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) {
+ qemu_free(out_buf);
+ return -1;
+ }
+ }
+
+ qemu_free(out_buf);
+ return 0;
+}
+
+static int qcow_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_flush(bs->file, cb, opaque);
+}
+
+static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+ BDRVQcowState *s = bs->opaque;
+ bdi->cluster_size = s->cluster_size;
+ return 0;
+}
+
+
+static QEMUOptionParameter qcow_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_BACKING_FILE,
+ .type = OPT_STRING,
+ .help = "File name of a base image"
+ },
+ {
+ .name = BLOCK_OPT_ENCRYPT,
+ .type = OPT_FLAG,
+ .help = "Encrypt the image"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_qcow = {
+ .format_name = "qcow",
+ .instance_size = sizeof(BDRVQcowState),
+ .bdrv_probe = qcow_probe,
+ .bdrv_open = qcow_open,
+ .bdrv_close = qcow_close,
+ .bdrv_create = qcow_create,
+ .bdrv_flush = qcow_flush,
+ .bdrv_is_allocated = qcow_is_allocated,
+ .bdrv_set_key = qcow_set_key,
+ .bdrv_make_empty = qcow_make_empty,
+ .bdrv_aio_readv = qcow_aio_readv,
+ .bdrv_aio_writev = qcow_aio_writev,
+ .bdrv_aio_flush = qcow_aio_flush,
+ .bdrv_write_compressed = qcow_write_compressed,
+ .bdrv_get_info = qcow_get_info,
+
+ .create_options = qcow_create_options,
+};
+
+static void bdrv_qcow_init(void)
+{
+ bdrv_register(&bdrv_qcow);
+}
+
+block_init(bdrv_qcow_init);
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
new file mode 100644
index 0000000..3824739
--- /dev/null
+++ b/block/qcow2-cache.c
@@ -0,0 +1,314 @@
+/*
+ * L2/refcount table cache for the QCOW2 format
+ *
+ * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "block_int.h"
+#include "qemu-common.h"
+#include "qcow2.h"
+
+typedef struct Qcow2CachedTable {
+ void* table;
+ int64_t offset;
+ bool dirty;
+ int cache_hits;
+ int ref;
+} Qcow2CachedTable;
+
+struct Qcow2Cache {
+ Qcow2CachedTable* entries;
+ struct Qcow2Cache* depends;
+ int size;
+ bool depends_on_flush;
+ bool writethrough;
+};
+
+Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
+ bool writethrough)
+{
+ BDRVQcowState *s = bs->opaque;
+ Qcow2Cache *c;
+ int i;
+
+ c = qemu_mallocz(sizeof(*c));
+ c->size = num_tables;
+ c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables);
+ c->writethrough = writethrough;
+
+ for (i = 0; i < c->size; i++) {
+ c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
+ }
+
+ return c;
+}
+
+int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ assert(c->entries[i].ref == 0);
+ qemu_vfree(c->entries[i].table);
+ }
+
+ qemu_free(c->entries);
+ qemu_free(c);
+
+ return 0;
+}
+
+static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
+{
+ int ret;
+
+ ret = qcow2_cache_flush(bs, c->depends);
+ if (ret < 0) {
+ return ret;
+ }
+
+ c->depends = NULL;
+ c->depends_on_flush = false;
+
+ return 0;
+}
+
+static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret = 0;
+
+ if (!c->entries[i].dirty || !c->entries[i].offset) {
+ return 0;
+ }
+
+ if (c->depends) {
+ ret = qcow2_cache_flush_dependency(bs, c);
+ } else if (c->depends_on_flush) {
+ ret = bdrv_flush(bs->file);
+ if (ret >= 0) {
+ c->depends_on_flush = false;
+ }
+ }
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (c == s->refcount_block_cache) {
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
+ } else if (c == s->l2_table_cache) {
+ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
+ }
+
+ ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table,
+ s->cluster_size);
+ if (ret < 0) {
+ return ret;
+ }
+
+ c->entries[i].dirty = false;
+
+ return 0;
+}
+
+int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
+{
+ int result = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ ret = qcow2_cache_entry_flush(bs, c, i);
+ if (ret < 0 && result != -ENOSPC) {
+ result = ret;
+ }
+ }
+
+ if (result == 0) {
+ ret = bdrv_flush(bs->file);
+ if (ret < 0) {
+ result = ret;
+ }
+ }
+
+ return result;
+}
+
+int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
+ Qcow2Cache *dependency)
+{
+ int ret;
+
+ if (dependency->depends) {
+ ret = qcow2_cache_flush_dependency(bs, dependency);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ if (c->depends && (c->depends != dependency)) {
+ ret = qcow2_cache_flush_dependency(bs, c);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ c->depends = dependency;
+ return 0;
+}
+
+void qcow2_cache_depends_on_flush(Qcow2Cache *c)
+{
+ c->depends_on_flush = true;
+}
+
+static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
+{
+ int i;
+ int min_count = INT_MAX;
+ int min_index = -1;
+
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].ref) {
+ continue;
+ }
+
+ if (c->entries[i].cache_hits < min_count) {
+ min_index = i;
+ min_count = c->entries[i].cache_hits;
+ }
+
+ /* Give newer hits priority */
+ /* TODO Check how to optimize the replacement strategy */
+ c->entries[i].cache_hits /= 2;
+ }
+
+ if (min_index == -1) {
+ /* This can't happen in current synchronous code, but leave the check
+ * here as a reminder for whoever starts using AIO with the cache */
+ abort();
+ }
+ return min_index;
+}
+
+static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
+ uint64_t offset, void **table, bool read_from_disk)
+{
+ BDRVQcowState *s = bs->opaque;
+ int i;
+ int ret;
+
+ /* Check if the table is already cached */
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].offset == offset) {
+ goto found;
+ }
+ }
+
+ /* If not, write a table back and replace it */
+ i = qcow2_cache_find_entry_to_replace(c);
+ if (i < 0) {
+ return i;
+ }
+
+ ret = qcow2_cache_entry_flush(bs, c, i);
+ if (ret < 0) {
+ return ret;
+ }
+
+ c->entries[i].offset = 0;
+ if (read_from_disk) {
+ if (c == s->l2_table_cache) {
+ BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
+ }
+
+ ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ /* Give the table some hits for the start so that it won't be replaced
+ * immediately. The number 32 is completely arbitrary. */
+ c->entries[i].cache_hits = 32;
+ c->entries[i].offset = offset;
+
+ /* And return the right table */
+found:
+ c->entries[i].cache_hits++;
+ c->entries[i].ref++;
+ *table = c->entries[i].table;
+ return 0;
+}
+
+int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table)
+{
+ return qcow2_cache_do_get(bs, c, offset, table, true);
+}
+
+int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table)
+{
+ return qcow2_cache_do_get(bs, c, offset, table, false);
+}
+
+int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].table == *table) {
+ goto found;
+ }
+ }
+ return -ENOENT;
+
+found:
+ c->entries[i].ref--;
+ *table = NULL;
+
+ assert(c->entries[i].ref >= 0);
+
+ if (c->writethrough) {
+ return qcow2_cache_entry_flush(bs, c, i);
+ } else {
+ return 0;
+ }
+}
+
+void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
+{
+ int i;
+
+ for (i = 0; i < c->size; i++) {
+ if (c->entries[i].table == table) {
+ goto found;
+ }
+ }
+ abort();
+
+found:
+ c->entries[i].dirty = true;
+}
+
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
new file mode 100644
index 0000000..750abe3
--- /dev/null
+++ b/block/qcow2-cluster.c
@@ -0,0 +1,975 @@
+/*
+ * Block driver for the QCOW version 2 format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <zlib.h>
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "block/qcow2.h"
+
+int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int new_l1_size, new_l1_size2, ret, i;
+ uint64_t *new_l1_table;
+ int64_t new_l1_table_offset;
+ uint8_t data[12];
+
+ if (min_size <= s->l1_size)
+ return 0;
+
+ if (exact_size) {
+ new_l1_size = min_size;
+ } else {
+ /* Bump size up to reduce the number of times we have to grow */
+ new_l1_size = s->l1_size;
+ if (new_l1_size == 0) {
+ new_l1_size = 1;
+ }
+ while (min_size > new_l1_size) {
+ new_l1_size = (new_l1_size * 3 + 1) / 2;
+ }
+ }
+
+#ifdef DEBUG_ALLOC2
+ printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
+#endif
+
+ new_l1_size2 = sizeof(uint64_t) * new_l1_size;
+ new_l1_table = qemu_mallocz(align_offset(new_l1_size2, 512));
+ memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
+
+ /* write new table (align to cluster) */
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE);
+ new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2);
+ if (new_l1_table_offset < 0) {
+ qemu_free(new_l1_table);
+ return new_l1_table_offset;
+ }
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return ret;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
+ for(i = 0; i < s->l1_size; i++)
+ new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
+ ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2);
+ if (ret < 0)
+ goto fail;
+ for(i = 0; i < s->l1_size; i++)
+ new_l1_table[i] = be64_to_cpu(new_l1_table[i]);
+
+ /* set new table */
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE);
+ cpu_to_be32w((uint32_t*)data, new_l1_size);
+ cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset);
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data));
+ if (ret < 0) {
+ goto fail;
+ }
+ qemu_free(s->l1_table);
+ qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t));
+ s->l1_table_offset = new_l1_table_offset;
+ s->l1_table = new_l1_table;
+ s->l1_size = new_l1_size;
+ return 0;
+ fail:
+ qemu_free(new_l1_table);
+ qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2);
+ return ret;
+}
+
+/*
+ * l2_load
+ *
+ * Loads a L2 table into memory. If the table is in the cache, the cache
+ * is used; otherwise the L2 table is loaded from the image file.
+ *
+ * Returns a pointer to the L2 table on success, or NULL if the read from
+ * the image file failed.
+ */
+
+static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
+ uint64_t **l2_table)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table);
+
+ return ret;
+}
+
+/*
+ * Writes one sector of the L1 table to the disk (can't update single entries
+ * and we really don't want bdrv_pread to perform a read-modify-write)
+ */
+#define L1_ENTRIES_PER_SECTOR (512 / 8)
+static int write_l1_entry(BlockDriverState *bs, int l1_index)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t buf[L1_ENTRIES_PER_SECTOR];
+ int l1_start_index;
+ int i, ret;
+
+ l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1);
+ for (i = 0; i < L1_ENTRIES_PER_SECTOR; i++) {
+ buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
+ ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index,
+ buf, sizeof(buf));
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * l2_allocate
+ *
+ * Allocate a new l2 entry in the file. If l1_index points to an already
+ * used entry in the L2 table (i.e. we are doing a copy on write for the L2
+ * table) copy the contents of the old L2 table into the newly allocated one.
+ * Otherwise the new table is initialized with zeros.
+ *
+ */
+
+static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t old_l2_offset;
+ uint64_t *l2_table;
+ int64_t l2_offset;
+ int ret;
+
+ old_l2_offset = s->l1_table[l1_index];
+
+ /* allocate a new l2 entry */
+
+ l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
+ if (l2_offset < 0) {
+ return l2_offset;
+ }
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* allocate a new entry in the l2 cache */
+
+ ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table);
+ if (ret < 0) {
+ return ret;
+ }
+
+ l2_table = *table;
+
+ if (old_l2_offset == 0) {
+ /* if there was no old l2 table, clear the new table */
+ memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
+ } else {
+ uint64_t* old_table;
+
+ /* if there was an old l2 table, read it from the disk */
+ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ);
+ ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset,
+ (void**) &old_table);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ memcpy(l2_table, old_table, s->cluster_size);
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
+ /* write the l2 table to the file */
+ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
+
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+ ret = qcow2_cache_flush(bs, s->l2_table_cache);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* update the L1 entry */
+ s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
+ ret = write_l1_entry(bs, l1_index);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ *table = l2_table;
+ return 0;
+
+fail:
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) table);
+ s->l1_table[l1_index] = old_l2_offset;
+ return ret;
+}
+
+static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
+ uint64_t *l2_table, uint64_t start, uint64_t mask)
+{
+ int i;
+ uint64_t offset = be64_to_cpu(l2_table[0]) & ~mask;
+
+ if (!offset)
+ return 0;
+
+ for (i = start; i < start + nb_clusters; i++)
+ if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask))
+ break;
+
+ return (i - start);
+}
+
+static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table)
+{
+ int i = 0;
+
+ while(nb_clusters-- && l2_table[i] == 0)
+ i++;
+
+ return i;
+}
+
+/* The crypt function is compatible with the linux cryptoloop
+ algorithm for < 4 GB images. NOTE: out_buf == in_buf is
+ supported */
+void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+ uint8_t *out_buf, const uint8_t *in_buf,
+ int nb_sectors, int enc,
+ const AES_KEY *key)
+{
+ union {
+ uint64_t ll[2];
+ uint8_t b[16];
+ } ivec;
+ int i;
+
+ for(i = 0; i < nb_sectors; i++) {
+ ivec.ll[0] = cpu_to_le64(sector_num);
+ ivec.ll[1] = 0;
+ AES_cbc_encrypt(in_buf, out_buf, 512, key,
+ ivec.b, enc);
+ sector_num++;
+ in_buf += 512;
+ out_buf += 512;
+ }
+}
+
+
+static int qcow2_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, index_in_cluster, n, n1;
+ uint64_t cluster_offset;
+ struct iovec iov;
+ QEMUIOVector qiov;
+
+ while (nb_sectors > 0) {
+ n = nb_sectors;
+
+ ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n,
+ &cluster_offset);
+ if (ret < 0) {
+ return ret;
+ }
+
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ if (!cluster_offset) {
+ if (bs->backing_hd) {
+ /* read from the base image */
+ iov.iov_base = buf;
+ iov.iov_len = n * 512;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+ n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n);
+ if (n1 > 0) {
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING);
+ ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
+ if (ret < 0)
+ return -1;
+ }
+ } else {
+ memset(buf, 0, 512 * n);
+ }
+ } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ if (qcow2_decompress_cluster(bs, cluster_offset) < 0)
+ return -1;
+ memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
+ } else {
+ BLKDBG_EVENT(bs->file, BLKDBG_READ);
+ ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512);
+ if (ret != n * 512)
+ return -1;
+ if (s->crypt_method) {
+ qcow2_encrypt_sectors(s, sector_num, buf, buf, n, 0,
+ &s->aes_decrypt_key);
+ }
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
+ uint64_t cluster_offset, int n_start, int n_end)
+{
+ BDRVQcowState *s = bs->opaque;
+ int n, ret;
+
+ n = n_end - n_start;
+ if (n <= 0)
+ return 0;
+ BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
+ ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n);
+ if (ret < 0)
+ return ret;
+ if (s->crypt_method) {
+ qcow2_encrypt_sectors(s, start_sect + n_start,
+ s->cluster_data,
+ s->cluster_data, n, 1,
+ &s->aes_encrypt_key);
+ }
+ BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
+ ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start,
+ s->cluster_data, n);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+
+/*
+ * get_cluster_offset
+ *
+ * For a given offset of the disk image, find the cluster offset in
+ * qcow2 file. The offset is stored in *cluster_offset.
+ *
+ * on entry, *num is the number of contiguous clusters we'd like to
+ * access following offset.
+ *
+ * on exit, *num is the number of contiguous clusters we can read.
+ *
+ * Return 0, if the offset is found
+ * Return -errno, otherwise.
+ *
+ */
+
+int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
+ int *num, uint64_t *cluster_offset)
+{
+ BDRVQcowState *s = bs->opaque;
+ unsigned int l1_index, l2_index;
+ uint64_t l2_offset, *l2_table;
+ int l1_bits, c;
+ unsigned int index_in_cluster, nb_clusters;
+ uint64_t nb_available, nb_needed;
+ int ret;
+
+ index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1);
+ nb_needed = *num + index_in_cluster;
+
+ l1_bits = s->l2_bits + s->cluster_bits;
+
+ /* compute how many bytes there are between the offset and
+ * the end of the l1 entry
+ */
+
+ nb_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1));
+
+ /* compute the number of available sectors */
+
+ nb_available = (nb_available >> 9) + index_in_cluster;
+
+ if (nb_needed > nb_available) {
+ nb_needed = nb_available;
+ }
+
+ *cluster_offset = 0;
+
+ /* seek the the l2 offset in the l1 table */
+
+ l1_index = offset >> l1_bits;
+ if (l1_index >= s->l1_size)
+ goto out;
+
+ l2_offset = s->l1_table[l1_index];
+
+ /* seek the l2 table of the given l2 offset */
+
+ if (!l2_offset)
+ goto out;
+
+ /* load the l2 table in memory */
+
+ l2_offset &= ~QCOW_OFLAG_COPIED;
+ ret = l2_load(bs, l2_offset, &l2_table);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* find the cluster offset for the given disk offset */
+
+ l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+ *cluster_offset = be64_to_cpu(l2_table[l2_index]);
+ nb_clusters = size_to_clusters(s, nb_needed << 9);
+
+ if (!*cluster_offset) {
+ /* how many empty clusters ? */
+ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
+ } else {
+ /* how many allocated clusters ? */
+ c = count_contiguous_clusters(nb_clusters, s->cluster_size,
+ &l2_table[l2_index], 0, QCOW_OFLAG_COPIED);
+ }
+
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+
+ nb_available = (c * s->cluster_sectors);
+out:
+ if (nb_available > nb_needed)
+ nb_available = nb_needed;
+
+ *num = nb_available - index_in_cluster;
+
+ *cluster_offset &=~QCOW_OFLAG_COPIED;
+ return 0;
+}
+
+/*
+ * get_cluster_table
+ *
+ * for a given disk offset, load (and allocate if needed)
+ * the l2 table.
+ *
+ * the l2 table offset in the qcow2 file and the cluster index
+ * in the l2 table are given to the caller.
+ *
+ * Returns 0 on success, -errno in failure case
+ */
+static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
+ uint64_t **new_l2_table,
+ uint64_t *new_l2_offset,
+ int *new_l2_index)
+{
+ BDRVQcowState *s = bs->opaque;
+ unsigned int l1_index, l2_index;
+ uint64_t l2_offset;
+ uint64_t *l2_table = NULL;
+ int ret;
+
+ /* seek the the l2 offset in the l1 table */
+
+ l1_index = offset >> (s->l2_bits + s->cluster_bits);
+ if (l1_index >= s->l1_size) {
+ ret = qcow2_grow_l1_table(bs, l1_index + 1, false);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ l2_offset = s->l1_table[l1_index];
+
+ /* seek the l2 table of the given l2 offset */
+
+ if (l2_offset & QCOW_OFLAG_COPIED) {
+ /* load the l2 table in memory */
+ l2_offset &= ~QCOW_OFLAG_COPIED;
+ ret = l2_load(bs, l2_offset, &l2_table);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ /* First allocate a new L2 table (and do COW if needed) */
+ ret = l2_allocate(bs, l1_index, &l2_table);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Then decrease the refcount of the old table */
+ if (l2_offset) {
+ qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
+ }
+ l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
+ }
+
+ /* find the cluster offset for the given disk offset */
+
+ l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+
+ *new_l2_table = l2_table;
+ *new_l2_offset = l2_offset;
+ *new_l2_index = l2_index;
+
+ return 0;
+}
+
+/*
+ * alloc_compressed_cluster_offset
+ *
+ * For a given offset of the disk image, return cluster offset in
+ * qcow2 file.
+ *
+ * If the offset is not found, allocate a new compressed cluster.
+ *
+ * Return the cluster offset if successful,
+ * Return 0, otherwise.
+ *
+ */
+
+uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
+ uint64_t offset,
+ int compressed_size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int l2_index, ret;
+ uint64_t l2_offset, *l2_table;
+ int64_t cluster_offset;
+ int nb_csectors;
+
+ ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
+ if (ret < 0) {
+ return 0;
+ }
+
+ cluster_offset = be64_to_cpu(l2_table[l2_index]);
+ if (cluster_offset & QCOW_OFLAG_COPIED)
+ return cluster_offset & ~QCOW_OFLAG_COPIED;
+
+ if (cluster_offset)
+ qcow2_free_any_clusters(bs, cluster_offset, 1);
+
+ cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
+ if (cluster_offset < 0) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ return 0;
+ }
+
+ nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) -
+ (cluster_offset >> 9);
+
+ cluster_offset |= QCOW_OFLAG_COMPRESSED |
+ ((uint64_t)nb_csectors << s->csize_shift);
+
+ /* update L2 table */
+
+ /* compressed clusters never have the copied flag */
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+ l2_table[l2_index] = cpu_to_be64(cluster_offset);
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ return 0;
+ }
+
+ return cluster_offset;
+}
+
+int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
+{
+ BDRVQcowState *s = bs->opaque;
+ int i, j = 0, l2_index, ret;
+ uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
+ uint64_t cluster_offset = m->cluster_offset;
+ bool cow = false;
+
+ if (m->nb_clusters == 0)
+ return 0;
+
+ old_cluster = qemu_malloc(m->nb_clusters * sizeof(uint64_t));
+
+ /* copy content of unmodified sectors */
+ start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
+ if (m->n_start) {
+ cow = true;
+ ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (m->nb_available & (s->cluster_sectors - 1)) {
+ uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1);
+ cow = true;
+ ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
+ m->nb_available - end, s->cluster_sectors);
+ if (ret < 0)
+ goto err;
+ }
+
+ /*
+ * Update L2 table.
+ *
+ * Before we update the L2 table to actually point to the new cluster, we
+ * need to be sure that the refcounts have been increased and COW was
+ * handled.
+ */
+ if (cow) {
+ qcow2_cache_depends_on_flush(s->l2_table_cache);
+ }
+
+ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
+ ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
+ if (ret < 0) {
+ goto err;
+ }
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+
+ for (i = 0; i < m->nb_clusters; i++) {
+ /* if two concurrent writes happen to the same unallocated cluster
+ * each write allocates separate cluster and writes data concurrently.
+ * The first one to complete updates l2 table with pointer to its
+ * cluster the second one has to do RMW (which is done above by
+ * copy_sectors()), update l2 table with its cluster pointer and free
+ * old cluster. This is what this loop does */
+ if(l2_table[l2_index + i] != 0)
+ old_cluster[j++] = l2_table[l2_index + i];
+
+ l2_table[l2_index + i] = cpu_to_be64((cluster_offset +
+ (i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
+ }
+
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ goto err;
+ }
+
+ /*
+ * If this was a COW, we need to decrease the refcount of the old cluster.
+ * Also flush bs->file to get the right order for L2 and refcount update.
+ */
+ if (j != 0) {
+ for (i = 0; i < j; i++) {
+ qcow2_free_any_clusters(bs,
+ be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
+ }
+ }
+
+ ret = 0;
+err:
+ qemu_free(old_cluster);
+ return ret;
+ }
+
+/*
+ * alloc_cluster_offset
+ *
+ * For a given offset of the disk image, return cluster offset in qcow2 file.
+ * If the offset is not found, allocate a new cluster.
+ *
+ * If the cluster was already allocated, m->nb_clusters is set to 0,
+ * m->depends_on is set to NULL and the other fields in m are meaningless.
+ *
+ * If the cluster is newly allocated, m->nb_clusters is set to the number of
+ * contiguous clusters that have been allocated. This may be 0 if the request
+ * conflict with another write request in flight; in this case, m->depends_on
+ * is set and the remaining fields of m are meaningless.
+ *
+ * If m->nb_clusters is non-zero, the other fields of m are valid and contain
+ * information about the first allocated cluster.
+ *
+ * Return 0 on success and -errno in error cases
+ */
+int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
+ int n_start, int n_end, int *num, QCowL2Meta *m)
+{
+ BDRVQcowState *s = bs->opaque;
+ int l2_index, ret;
+ uint64_t l2_offset, *l2_table;
+ int64_t cluster_offset;
+ unsigned int nb_clusters, i = 0;
+ QCowL2Meta *old_alloc;
+
+ ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
+ if (ret < 0) {
+ return ret;
+ }
+
+ nb_clusters = size_to_clusters(s, n_end << 9);
+
+ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
+
+ cluster_offset = be64_to_cpu(l2_table[l2_index]);
+
+ /* We keep all QCOW_OFLAG_COPIED clusters */
+
+ if (cluster_offset & QCOW_OFLAG_COPIED) {
+ nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size,
+ &l2_table[l2_index], 0, 0);
+
+ cluster_offset &= ~QCOW_OFLAG_COPIED;
+ m->nb_clusters = 0;
+ m->depends_on = NULL;
+
+ goto out;
+ }
+
+ /* for the moment, multiple compressed clusters are not managed */
+
+ if (cluster_offset & QCOW_OFLAG_COMPRESSED)
+ nb_clusters = 1;
+
+ /* how many available clusters ? */
+
+ while (i < nb_clusters) {
+ i += count_contiguous_clusters(nb_clusters - i, s->cluster_size,
+ &l2_table[l2_index], i, 0);
+ if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) {
+ break;
+ }
+
+ i += count_contiguous_free_clusters(nb_clusters - i,
+ &l2_table[l2_index + i]);
+ if (i >= nb_clusters) {
+ break;
+ }
+
+ cluster_offset = be64_to_cpu(l2_table[l2_index + i]);
+
+ if ((cluster_offset & QCOW_OFLAG_COPIED) ||
+ (cluster_offset & QCOW_OFLAG_COMPRESSED))
+ break;
+ }
+ assert(i <= nb_clusters);
+ nb_clusters = i;
+
+ /*
+ * Check if there already is an AIO write request in flight which allocates
+ * the same cluster. In this case we need to wait until the previous
+ * request has completed and updated the L2 table accordingly.
+ */
+ QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
+
+ uint64_t end_offset = offset + nb_clusters * s->cluster_size;
+ uint64_t old_offset = old_alloc->offset;
+ uint64_t old_end_offset = old_alloc->offset +
+ old_alloc->nb_clusters * s->cluster_size;
+
+ if (end_offset < old_offset || offset > old_end_offset) {
+ /* No intersection */
+ } else {
+ if (offset < old_offset) {
+ /* Stop at the start of a running allocation */
+ nb_clusters = (old_offset - offset) >> s->cluster_bits;
+ } else {
+ nb_clusters = 0;
+ }
+
+ if (nb_clusters == 0) {
+ /* Set dependency and wait for a callback */
+ m->depends_on = old_alloc;
+ m->nb_clusters = 0;
+ *num = 0;
+ ret = 0;
+ goto fail;
+ }
+ }
+ }
+
+ if (!nb_clusters) {
+ abort();
+ }
+
+ QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
+
+ /* allocate a new cluster */
+
+ cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
+ if (cluster_offset < 0) {
+ QLIST_REMOVE(m, next_in_flight);
+ ret = cluster_offset;
+ goto fail;
+ }
+
+ /* save info needed for meta data update */
+ m->offset = offset;
+ m->n_start = n_start;
+ m->nb_clusters = nb_clusters;
+
+out:
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ return ret;
+ }
+
+ m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
+ m->cluster_offset = cluster_offset;
+
+ *num = m->nb_available - n_start;
+
+ return 0;
+
+fail:
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ return ret;
+}
+
+static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
+ const uint8_t *buf, int buf_size)
+{
+ z_stream strm1, *strm = &strm1;
+ int ret, out_len;
+
+ memset(strm, 0, sizeof(*strm));
+
+ strm->next_in = (uint8_t *)buf;
+ strm->avail_in = buf_size;
+ strm->next_out = out_buf;
+ strm->avail_out = out_buf_size;
+
+ ret = inflateInit2(strm, -12);
+ if (ret != Z_OK)
+ return -1;
+ ret = inflate(strm, Z_FINISH);
+ out_len = strm->next_out - out_buf;
+ if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
+ out_len != out_buf_size) {
+ inflateEnd(strm);
+ return -1;
+ }
+ inflateEnd(strm);
+ return 0;
+}
+
+int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, csize, nb_csectors, sector_offset;
+ uint64_t coffset;
+
+ coffset = cluster_offset & s->cluster_offset_mask;
+ if (s->cluster_cache_offset != coffset) {
+ nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1;
+ sector_offset = coffset & 511;
+ csize = nb_csectors * 512 - sector_offset;
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
+ ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors);
+ if (ret < 0) {
+ return ret;
+ }
+ if (decompress_buffer(s->cluster_cache, s->cluster_size,
+ s->cluster_data + sector_offset, csize) < 0) {
+ return -EIO;
+ }
+ s->cluster_cache_offset = coffset;
+ }
+ return 0;
+}
+
+/*
+ * This discards as many clusters of nb_clusters as possible at once (i.e.
+ * all clusters in the same L2 table) and returns the number of discarded
+ * clusters.
+ */
+static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
+ unsigned int nb_clusters)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t l2_offset, *l2_table;
+ int l2_index;
+ int ret;
+ int i;
+
+ ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Limit nb_clusters to one L2 table */
+ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
+
+ for (i = 0; i < nb_clusters; i++) {
+ uint64_t old_offset;
+
+ old_offset = be64_to_cpu(l2_table[l2_index + i]);
+ old_offset &= ~QCOW_OFLAG_COPIED;
+
+ if (old_offset == 0) {
+ continue;
+ }
+
+ /* First remove L2 entries */
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+ l2_table[l2_index + i] = cpu_to_be64(0);
+
+ /* Then decrease the refcount */
+ qcow2_free_any_clusters(bs, old_offset, 1);
+ }
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return nb_clusters;
+}
+
+int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
+ int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t end_offset;
+ unsigned int nb_clusters;
+ int ret;
+
+ end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
+
+ /* Round start up and end down */
+ offset = align_offset(offset, s->cluster_size);
+ end_offset &= ~(s->cluster_size - 1);
+
+ if (offset > end_offset) {
+ return 0;
+ }
+
+ nb_clusters = size_to_clusters(s, end_offset - offset);
+
+ /* Each L2 table is handled by its own loop iteration */
+ while (nb_clusters > 0) {
+ ret = discard_single_l2(bs, offset, nb_clusters);
+ if (ret < 0) {
+ return ret;
+ }
+
+ nb_clusters -= ret;
+ offset += (ret * s->cluster_size);
+ }
+
+ return 0;
+}
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
new file mode 100644
index 0000000..915d85a
--- /dev/null
+++ b/block/qcow2-refcount.c
@@ -0,0 +1,1166 @@
+/*
+ * Block driver for the QCOW version 2 format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "block/qcow2.h"
+
+static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
+static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
+ int64_t offset, int64_t length,
+ int addend);
+
+
+/*********************************************************/
+/* refcount handling */
+
+int qcow2_refcount_init(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, refcount_table_size2, i;
+
+ refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
+ s->refcount_table = qemu_malloc(refcount_table_size2);
+ if (s->refcount_table_size > 0) {
+ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD);
+ ret = bdrv_pread(bs->file, s->refcount_table_offset,
+ s->refcount_table, refcount_table_size2);
+ if (ret != refcount_table_size2)
+ goto fail;
+ for(i = 0; i < s->refcount_table_size; i++)
+ be64_to_cpus(&s->refcount_table[i]);
+ }
+ return 0;
+ fail:
+ return -ENOMEM;
+}
+
+void qcow2_refcount_close(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ qemu_free(s->refcount_table);
+}
+
+
+static int load_refcount_block(BlockDriverState *bs,
+ int64_t refcount_block_offset,
+ void **refcount_block)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
+ refcount_block);
+
+ return ret;
+}
+
+/*
+ * Returns the refcount of the cluster given by its index. Any non-negative
+ * return value is the refcount of the cluster, negative values are -errno
+ * and indicate an error.
+ */
+static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
+{
+ BDRVQcowState *s = bs->opaque;
+ int refcount_table_index, block_index;
+ int64_t refcount_block_offset;
+ int ret;
+ uint16_t *refcount_block;
+ uint16_t refcount;
+
+ refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+ if (refcount_table_index >= s->refcount_table_size)
+ return 0;
+ refcount_block_offset = s->refcount_table[refcount_table_index];
+ if (!refcount_block_offset)
+ return 0;
+
+ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ block_index = cluster_index &
+ ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+ refcount = be16_to_cpu(refcount_block[block_index]);
+
+ ret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return refcount;
+}
+
+/*
+ * Rounds the refcount table size up to avoid growing the table for each single
+ * refcount block that is allocated.
+ */
+static unsigned int next_refcount_table_size(BDRVQcowState *s,
+ unsigned int min_size)
+{
+ unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1;
+ unsigned int refcount_table_clusters =
+ MAX(1, s->refcount_table_size >> (s->cluster_bits - 3));
+
+ while (min_clusters > refcount_table_clusters) {
+ refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2;
+ }
+
+ return refcount_table_clusters << (s->cluster_bits - 3);
+}
+
+
+/* Checks if two offsets are described by the same refcount block */
+static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
+ uint64_t offset_b)
+{
+ uint64_t block_a = offset_a >> (2 * s->cluster_bits - REFCOUNT_SHIFT);
+ uint64_t block_b = offset_b >> (2 * s->cluster_bits - REFCOUNT_SHIFT);
+
+ return (block_a == block_b);
+}
+
+/*
+ * Loads a refcount block. If it doesn't exist yet, it is allocated first
+ * (including growing the refcount table if needed).
+ *
+ * Returns 0 on success or -errno in error case
+ */
+static int alloc_refcount_block(BlockDriverState *bs,
+ int64_t cluster_index, uint16_t **refcount_block)
+{
+ BDRVQcowState *s = bs->opaque;
+ unsigned int refcount_table_index;
+ int ret;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC);
+
+ /* Find the refcount block for the given cluster */
+ refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+
+ if (refcount_table_index < s->refcount_table_size) {
+
+ uint64_t refcount_block_offset =
+ s->refcount_table[refcount_table_index];
+
+ /* If it's already there, we're done */
+ if (refcount_block_offset) {
+ return load_refcount_block(bs, refcount_block_offset,
+ (void**) refcount_block);
+ }
+ }
+
+ /*
+ * If we came here, we need to allocate something. Something is at least
+ * a cluster for the new refcount block. It may also include a new refcount
+ * table if the old refcount table is too small.
+ *
+ * Note that allocating clusters here needs some special care:
+ *
+ * - We can't use the normal qcow2_alloc_clusters(), it would try to
+ * increase the refcount and very likely we would end up with an endless
+ * recursion. Instead we must place the refcount blocks in a way that
+ * they can describe them themselves.
+ *
+ * - We need to consider that at this point we are inside update_refcounts
+ * and doing the initial refcount increase. This means that some clusters
+ * have already been allocated by the caller, but their refcount isn't
+ * accurate yet. free_cluster_index tells us where this allocation ends
+ * as long as we don't overwrite it by freeing clusters.
+ *
+ * - alloc_clusters_noref and qcow2_free_clusters may load a different
+ * refcount block into the cache
+ */
+
+ *refcount_block = NULL;
+
+ /* We write to the refcount table, so we might depend on L2 tables */
+ qcow2_cache_flush(bs, s->l2_table_cache);
+
+ /* Allocate the refcount block itself and mark it as used */
+ int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
+ if (new_block < 0) {
+ return new_block;
+ }
+
+#ifdef DEBUG_ALLOC2
+ fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64
+ " at %" PRIx64 "\n",
+ refcount_table_index, cluster_index << s->cluster_bits, new_block);
+#endif
+
+ if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
+ /* Zero the new refcount block before updating it */
+ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
+ (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ memset(*refcount_block, 0, s->cluster_size);
+
+ /* The block describes itself, need to update the cache */
+ int block_index = (new_block >> s->cluster_bits) &
+ ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+ (*refcount_block)[block_index] = cpu_to_be16(1);
+ } else {
+ /* Described somewhere else. This can recurse at most twice before we
+ * arrive at a block that describes itself. */
+ ret = update_refcount(bs, new_block, s->cluster_size, 1);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ bdrv_flush(bs->file);
+
+ /* Initialize the new refcount block only after updating its refcount,
+ * update_refcount uses the refcount cache itself */
+ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
+ (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ memset(*refcount_block, 0, s->cluster_size);
+ }
+
+ /* Now the new refcount block needs to be written to disk */
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE);
+ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block);
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ /* If the refcount table is big enough, just hook the block up there */
+ if (refcount_table_index < s->refcount_table_size) {
+ uint64_t data64 = cpu_to_be64(new_block);
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP);
+ ret = bdrv_pwrite_sync(bs->file,
+ s->refcount_table_offset + refcount_table_index * sizeof(uint64_t),
+ &data64, sizeof(data64));
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ s->refcount_table[refcount_table_index] = new_block;
+ return 0;
+ }
+
+ ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
+ if (ret < 0) {
+ goto fail_block;
+ }
+
+ /*
+ * If we come here, we need to grow the refcount table. Again, a new
+ * refcount table needs some space and we can't simply allocate to avoid
+ * endless recursion.
+ *
+ * Therefore let's grab new refcount blocks at the end of the image, which
+ * will describe themselves and the new refcount table. This way we can
+ * reference them only in the new table and do the switch to the new
+ * refcount table at once without producing an inconsistent state in
+ * between.
+ */
+ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW);
+
+ /* Calculate the number of refcount blocks needed so far */
+ uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT);
+ uint64_t blocks_used = (s->free_cluster_index +
+ refcount_block_clusters - 1) / refcount_block_clusters;
+
+ /* And now we need at least one block more for the new metadata */
+ uint64_t table_size = next_refcount_table_size(s, blocks_used + 1);
+ uint64_t last_table_size;
+ uint64_t blocks_clusters;
+ do {
+ uint64_t table_clusters = size_to_clusters(s, table_size);
+ blocks_clusters = 1 +
+ ((table_clusters + refcount_block_clusters - 1)
+ / refcount_block_clusters);
+ uint64_t meta_clusters = table_clusters + blocks_clusters;
+
+ last_table_size = table_size;
+ table_size = next_refcount_table_size(s, blocks_used +
+ ((meta_clusters + refcount_block_clusters - 1)
+ / refcount_block_clusters));
+
+ } while (last_table_size != table_size);
+
+#ifdef DEBUG_ALLOC2
+ fprintf(stderr, "qcow2: Grow refcount table %" PRId32 " => %" PRId64 "\n",
+ s->refcount_table_size, table_size);
+#endif
+
+ /* Create the new refcount table and blocks */
+ uint64_t meta_offset = (blocks_used * refcount_block_clusters) *
+ s->cluster_size;
+ uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size;
+ uint16_t *new_blocks = qemu_mallocz(blocks_clusters * s->cluster_size);
+ uint64_t *new_table = qemu_mallocz(table_size * sizeof(uint64_t));
+
+ assert(meta_offset >= (s->free_cluster_index * s->cluster_size));
+
+ /* Fill the new refcount table */
+ memcpy(new_table, s->refcount_table,
+ s->refcount_table_size * sizeof(uint64_t));
+ new_table[refcount_table_index] = new_block;
+
+ int i;
+ for (i = 0; i < blocks_clusters; i++) {
+ new_table[blocks_used + i] = meta_offset + (i * s->cluster_size);
+ }
+
+ /* Fill the refcount blocks */
+ uint64_t table_clusters = size_to_clusters(s, table_size * sizeof(uint64_t));
+ int block = 0;
+ for (i = 0; i < table_clusters + blocks_clusters; i++) {
+ new_blocks[block++] = cpu_to_be16(1);
+ }
+
+ /* Write refcount blocks to disk */
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS);
+ ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks,
+ blocks_clusters * s->cluster_size);
+ qemu_free(new_blocks);
+ if (ret < 0) {
+ goto fail_table;
+ }
+
+ /* Write refcount table to disk */
+ for(i = 0; i < table_size; i++) {
+ cpu_to_be64s(&new_table[i]);
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE);
+ ret = bdrv_pwrite_sync(bs->file, table_offset, new_table,
+ table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ goto fail_table;
+ }
+
+ for(i = 0; i < table_size; i++) {
+ cpu_to_be64s(&new_table[i]);
+ }
+
+ /* Hook up the new refcount table in the qcow2 header */
+ uint8_t data[12];
+ cpu_to_be64w((uint64_t*)data, table_offset);
+ cpu_to_be32w((uint32_t*)(data + 8), table_clusters);
+ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE);
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset),
+ data, sizeof(data));
+ if (ret < 0) {
+ goto fail_table;
+ }
+
+ /* And switch it in memory */
+ uint64_t old_table_offset = s->refcount_table_offset;
+ uint64_t old_table_size = s->refcount_table_size;
+
+ qemu_free(s->refcount_table);
+ s->refcount_table = new_table;
+ s->refcount_table_size = table_size;
+ s->refcount_table_offset = table_offset;
+
+ /* Free old table. Remember, we must not change free_cluster_index */
+ uint64_t old_free_cluster_index = s->free_cluster_index;
+ qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
+ s->free_cluster_index = old_free_cluster_index;
+
+ ret = load_refcount_block(bs, new_block, (void**) refcount_block);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return new_block;
+
+fail_table:
+ qemu_free(new_table);
+fail_block:
+ if (*refcount_block != NULL) {
+ qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
+ }
+ return ret;
+}
+
+/* XXX: cache several refcount block clusters ? */
+static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
+ int64_t offset, int64_t length, int addend)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t start, last, cluster_offset;
+ uint16_t *refcount_block = NULL;
+ int64_t old_table_index = -1;
+ int ret;
+
+#ifdef DEBUG_ALLOC2
+ printf("update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n",
+ offset, length, addend);
+#endif
+ if (length < 0) {
+ return -EINVAL;
+ } else if (length == 0) {
+ return 0;
+ }
+
+ if (addend < 0) {
+ qcow2_cache_set_dependency(bs, s->refcount_block_cache,
+ s->l2_table_cache);
+ }
+
+ start = offset & ~(s->cluster_size - 1);
+ last = (offset + length - 1) & ~(s->cluster_size - 1);
+ for(cluster_offset = start; cluster_offset <= last;
+ cluster_offset += s->cluster_size)
+ {
+ int block_index, refcount;
+ int64_t cluster_index = cluster_offset >> s->cluster_bits;
+ int64_t table_index =
+ cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+
+ /* Load the refcount block and allocate it if needed */
+ if (table_index != old_table_index) {
+ if (refcount_block) {
+ ret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
+ ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+ old_table_index = table_index;
+
+ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block);
+
+ /* we can update the count and save it */
+ block_index = cluster_index &
+ ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+
+ refcount = be16_to_cpu(refcount_block[block_index]);
+ refcount += addend;
+ if (refcount < 0 || refcount > 0xffff) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (refcount == 0 && cluster_index < s->free_cluster_index) {
+ s->free_cluster_index = cluster_index;
+ }
+ refcount_block[block_index] = cpu_to_be16(refcount);
+ }
+
+ ret = 0;
+fail:
+ /* Write last changed block to disk */
+ if (refcount_block) {
+ int wret;
+ wret = qcow2_cache_put(bs, s->refcount_block_cache,
+ (void**) &refcount_block);
+ if (wret < 0) {
+ return ret < 0 ? ret : wret;
+ }
+ }
+
+ /*
+ * Try do undo any updates if an error is returned (This may succeed in
+ * some cases like ENOSPC for allocating a new refcount block)
+ */
+ if (ret < 0) {
+ int dummy;
+ dummy = update_refcount(bs, offset, cluster_offset - offset, -addend);
+ (void)dummy;
+ }
+
+ return ret;
+}
+
+/*
+ * Increases or decreases the refcount of a given cluster by one.
+ * addend must be 1 or -1.
+ *
+ * If the return value is non-negative, it is the new refcount of the cluster.
+ * If it is negative, it is -errno and indicates an error.
+ */
+static int update_cluster_refcount(BlockDriverState *bs,
+ int64_t cluster_index,
+ int addend)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend);
+ if (ret < 0) {
+ return ret;
+ }
+
+ bdrv_flush(bs->file);
+
+ return get_refcount(bs, cluster_index);
+}
+
+
+
+/*********************************************************/
+/* cluster allocation functions */
+
+
+
+/* return < 0 if error */
+static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int i, nb_clusters, refcount;
+
+ nb_clusters = size_to_clusters(s, size);
+retry:
+ for(i = 0; i < nb_clusters; i++) {
+ int64_t next_cluster_index = s->free_cluster_index++;
+ refcount = get_refcount(bs, next_cluster_index);
+
+ if (refcount < 0) {
+ return refcount;
+ } else if (refcount != 0) {
+ goto retry;
+ }
+ }
+#ifdef DEBUG_ALLOC2
+ printf("alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n",
+ size,
+ (s->free_cluster_index - nb_clusters) << s->cluster_bits);
+#endif
+ return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
+}
+
+int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
+{
+ int64_t offset;
+ int ret;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
+ offset = alloc_clusters_noref(bs, size);
+ if (offset < 0) {
+ return offset;
+ }
+
+ ret = update_refcount(bs, offset, size, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return offset;
+}
+
+/* only used to allocate compressed sectors. We try to allocate
+ contiguous sectors. size must be <= cluster_size */
+int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t offset, cluster_offset;
+ int free_in_cluster;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES);
+ assert(size > 0 && size <= s->cluster_size);
+ if (s->free_byte_offset == 0) {
+ s->free_byte_offset = qcow2_alloc_clusters(bs, s->cluster_size);
+ if (s->free_byte_offset < 0) {
+ return s->free_byte_offset;
+ }
+ }
+ redo:
+ free_in_cluster = s->cluster_size -
+ (s->free_byte_offset & (s->cluster_size - 1));
+ if (size <= free_in_cluster) {
+ /* enough space in current cluster */
+ offset = s->free_byte_offset;
+ s->free_byte_offset += size;
+ free_in_cluster -= size;
+ if (free_in_cluster == 0)
+ s->free_byte_offset = 0;
+ if ((offset & (s->cluster_size - 1)) != 0)
+ update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
+ } else {
+ offset = qcow2_alloc_clusters(bs, s->cluster_size);
+ if (offset < 0) {
+ return offset;
+ }
+ cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1);
+ if ((cluster_offset + s->cluster_size) == offset) {
+ /* we are lucky: contiguous data */
+ offset = s->free_byte_offset;
+ update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
+ s->free_byte_offset += size;
+ } else {
+ s->free_byte_offset = offset;
+ goto redo;
+ }
+ }
+
+ bdrv_flush(bs->file);
+ return offset;
+}
+
+void qcow2_free_clusters(BlockDriverState *bs,
+ int64_t offset, int64_t size)
+{
+ int ret;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE);
+ ret = update_refcount(bs, offset, size, -1);
+ if (ret < 0) {
+ fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret));
+ /* TODO Remember the clusters to free them later and avoid leaking */
+ }
+}
+
+/*
+ * free_any_clusters
+ *
+ * free clusters according to its type: compressed or not
+ *
+ */
+
+void qcow2_free_any_clusters(BlockDriverState *bs,
+ uint64_t cluster_offset, int nb_clusters)
+{
+ BDRVQcowState *s = bs->opaque;
+
+ /* free the cluster */
+
+ if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ int nb_csectors;
+ nb_csectors = ((cluster_offset >> s->csize_shift) &
+ s->csize_mask) + 1;
+ qcow2_free_clusters(bs,
+ (cluster_offset & s->cluster_offset_mask) & ~511,
+ nb_csectors * 512);
+ return;
+ }
+
+ qcow2_free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits);
+
+ return;
+}
+
+
+
+/*********************************************************/
+/* snapshots and image creation */
+
+
+
+void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset,
+ int64_t size)
+{
+ int refcount;
+ int64_t start, last, cluster_offset;
+ uint16_t *p;
+
+ start = offset & ~(s->cluster_size - 1);
+ last = (offset + size - 1) & ~(s->cluster_size - 1);
+ for(cluster_offset = start; cluster_offset <= last;
+ cluster_offset += s->cluster_size) {
+ p = &s->refcount_block[cluster_offset >> s->cluster_bits];
+ refcount = be16_to_cpu(*p);
+ refcount++;
+ *p = cpu_to_be16(refcount);
+ }
+}
+
+/* update the refcounts of snapshots and the copied flag */
+int qcow2_update_snapshot_refcount(BlockDriverState *bs,
+ int64_t l1_table_offset, int l1_size, int addend)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
+ int64_t old_offset, old_l2_offset;
+ int i, j, l1_modified, nb_csectors, refcount;
+ int ret;
+
+ l2_table = NULL;
+ l1_table = NULL;
+ l1_size2 = l1_size * sizeof(uint64_t);
+ if (l1_table_offset != s->l1_table_offset) {
+ if (l1_size2 != 0) {
+ l1_table = qemu_mallocz(align_offset(l1_size2, 512));
+ } else {
+ l1_table = NULL;
+ }
+ l1_allocated = 1;
+ if (bdrv_pread(bs->file, l1_table_offset,
+ l1_table, l1_size2) != l1_size2)
+ goto fail;
+ for(i = 0;i < l1_size; i++)
+ be64_to_cpus(&l1_table[i]);
+ } else {
+ assert(l1_size == s->l1_size);
+ l1_table = s->l1_table;
+ l1_allocated = 0;
+ }
+
+ l1_modified = 0;
+ for(i = 0; i < l1_size; i++) {
+ l2_offset = l1_table[i];
+ if (l2_offset) {
+ old_l2_offset = l2_offset;
+ l2_offset &= ~QCOW_OFLAG_COPIED;
+
+ ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
+ (void**) &l2_table);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ for(j = 0; j < s->l2_size; j++) {
+ offset = be64_to_cpu(l2_table[j]);
+ if (offset != 0) {
+ old_offset = offset;
+ offset &= ~QCOW_OFLAG_COPIED;
+ if (offset & QCOW_OFLAG_COMPRESSED) {
+ nb_csectors = ((offset >> s->csize_shift) &
+ s->csize_mask) + 1;
+ if (addend != 0) {
+ int ret;
+ ret = update_refcount(bs,
+ (offset & s->cluster_offset_mask) & ~511,
+ nb_csectors * 512, addend);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* TODO Flushing once for the whole function should
+ * be enough */
+ bdrv_flush(bs->file);
+ }
+ /* compressed clusters are never modified */
+ refcount = 2;
+ } else {
+ if (addend != 0) {
+ refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend);
+ } else {
+ refcount = get_refcount(bs, offset >> s->cluster_bits);
+ }
+
+ if (refcount < 0) {
+ goto fail;
+ }
+ }
+
+ if (refcount == 1) {
+ offset |= QCOW_OFLAG_COPIED;
+ }
+ if (offset != old_offset) {
+ if (addend > 0) {
+ qcow2_cache_set_dependency(bs, s->l2_table_cache,
+ s->refcount_block_cache);
+ }
+ l2_table[j] = cpu_to_be64(offset);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
+ }
+ }
+ }
+
+ ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ if (ret < 0) {
+ goto fail;
+ }
+
+
+ if (addend != 0) {
+ refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
+ } else {
+ refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
+ }
+ if (refcount < 0) {
+ goto fail;
+ } else if (refcount == 1) {
+ l2_offset |= QCOW_OFLAG_COPIED;
+ }
+ if (l2_offset != old_l2_offset) {
+ l1_table[i] = l2_offset;
+ l1_modified = 1;
+ }
+ }
+ }
+ if (l1_modified) {
+ for(i = 0; i < l1_size; i++)
+ cpu_to_be64s(&l1_table[i]);
+ if (bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table,
+ l1_size2) < 0)
+ goto fail;
+ for(i = 0; i < l1_size; i++)
+ be64_to_cpus(&l1_table[i]);
+ }
+ if (l1_allocated)
+ qemu_free(l1_table);
+ return 0;
+ fail:
+ if (l2_table) {
+ qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
+ }
+
+ if (l1_allocated)
+ qemu_free(l1_table);
+ return -EIO;
+}
+
+
+
+
+/*********************************************************/
+/* refcount checking functions */
+
+
+
+/*
+ * Increases the refcount for a range of clusters in a given refcount table.
+ * This is used to construct a temporary refcount table out of L1 and L2 tables
+ * which can be compared the the refcount table saved in the image.
+ *
+ * Modifies the number of errors in res.
+ */
+static void inc_refcounts(BlockDriverState *bs,
+ BdrvCheckResult *res,
+ uint16_t *refcount_table,
+ int refcount_table_size,
+ int64_t offset, int64_t size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t start, last, cluster_offset;
+ int k;
+
+ if (size <= 0)
+ return;
+
+ start = offset & ~(s->cluster_size - 1);
+ last = (offset + size - 1) & ~(s->cluster_size - 1);
+ for(cluster_offset = start; cluster_offset <= last;
+ cluster_offset += s->cluster_size) {
+ k = cluster_offset >> s->cluster_bits;
+ if (k < 0) {
+ fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n",
+ cluster_offset);
+ res->corruptions++;
+ } else if (k >= refcount_table_size) {
+ fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after "
+ "the end of the image file, can't properly check refcounts.\n",
+ cluster_offset);
+ res->check_errors++;
+ } else {
+ if (++refcount_table[k] == 0) {
+ fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
+ "\n", cluster_offset);
+ res->corruptions++;
+ }
+ }
+ }
+}
+
+/*
+ * Increases the refcount in the given refcount table for the all clusters
+ * referenced in the L2 table. While doing so, performs some checks on L2
+ * entries.
+ *
+ * Returns the number of errors found by the checks or -errno if an internal
+ * error occurred.
+ */
+static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
+ uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset,
+ int check_copied)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t *l2_table, offset;
+ int i, l2_size, nb_csectors, refcount;
+
+ /* Read L2 table from disk */
+ l2_size = s->l2_size * sizeof(uint64_t);
+ l2_table = qemu_malloc(l2_size);
+
+ if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size)
+ goto fail;
+
+ /* Do the actual checks */
+ for(i = 0; i < s->l2_size; i++) {
+ offset = be64_to_cpu(l2_table[i]);
+ if (offset != 0) {
+ if (offset & QCOW_OFLAG_COMPRESSED) {
+ /* Compressed clusters don't have QCOW_OFLAG_COPIED */
+ if (offset & QCOW_OFLAG_COPIED) {
+ fprintf(stderr, "ERROR: cluster %" PRId64 ": "
+ "copied flag must never be set for compressed "
+ "clusters\n", offset >> s->cluster_bits);
+ offset &= ~QCOW_OFLAG_COPIED;
+ res->corruptions++;
+ }
+
+ /* Mark cluster as used */
+ nb_csectors = ((offset >> s->csize_shift) &
+ s->csize_mask) + 1;
+ offset &= s->cluster_offset_mask;
+ inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ offset & ~511, nb_csectors * 512);
+ } else {
+ /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
+ if (check_copied) {
+ uint64_t entry = offset;
+ offset &= ~QCOW_OFLAG_COPIED;
+ refcount = get_refcount(bs, offset >> s->cluster_bits);
+ if (refcount < 0) {
+ fprintf(stderr, "Can't get refcount for offset %"
+ PRIx64 ": %s\n", entry, strerror(-refcount));
+ goto fail;
+ }
+ if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) {
+ fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
+ PRIx64 " refcount=%d\n", entry, refcount);
+ res->corruptions++;
+ }
+ }
+
+ /* Mark cluster as used */
+ offset &= ~QCOW_OFLAG_COPIED;
+ inc_refcounts(bs, res, refcount_table,refcount_table_size,
+ offset, s->cluster_size);
+
+ /* Correct offsets are cluster aligned */
+ if (offset & (s->cluster_size - 1)) {
+ fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
+ "properly aligned; L2 entry corrupted.\n", offset);
+ res->corruptions++;
+ }
+ }
+ }
+ }
+
+ qemu_free(l2_table);
+ return 0;
+
+fail:
+ fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n");
+ qemu_free(l2_table);
+ return -EIO;
+}
+
+/*
+ * Increases the refcount for the L1 table, its L2 tables and all referenced
+ * clusters in the given refcount table. While doing so, performs some checks
+ * on L1 and L2 entries.
+ *
+ * Returns the number of errors found by the checks or -errno if an internal
+ * error occurred.
+ */
+static int check_refcounts_l1(BlockDriverState *bs,
+ BdrvCheckResult *res,
+ uint16_t *refcount_table,
+ int refcount_table_size,
+ int64_t l1_table_offset, int l1_size,
+ int check_copied)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint64_t *l1_table, l2_offset, l1_size2;
+ int i, refcount, ret;
+
+ l1_size2 = l1_size * sizeof(uint64_t);
+
+ /* Mark L1 table as used */
+ inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ l1_table_offset, l1_size2);
+
+ /* Read L1 table entries from disk */
+ if (l1_size2 == 0) {
+ l1_table = NULL;
+ } else {
+ l1_table = qemu_malloc(l1_size2);
+ if (bdrv_pread(bs->file, l1_table_offset,
+ l1_table, l1_size2) != l1_size2)
+ goto fail;
+ for(i = 0;i < l1_size; i++)
+ be64_to_cpus(&l1_table[i]);
+ }
+
+ /* Do the actual checks */
+ for(i = 0; i < l1_size; i++) {
+ l2_offset = l1_table[i];
+ if (l2_offset) {
+ /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
+ if (check_copied) {
+ refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED)
+ >> s->cluster_bits);
+ if (refcount < 0) {
+ fprintf(stderr, "Can't get refcount for l2_offset %"
+ PRIx64 ": %s\n", l2_offset, strerror(-refcount));
+ goto fail;
+ }
+ if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) {
+ fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64
+ " refcount=%d\n", l2_offset, refcount);
+ res->corruptions++;
+ }
+ }
+
+ /* Mark L2 table as used */
+ l2_offset &= ~QCOW_OFLAG_COPIED;
+ inc_refcounts(bs, res, refcount_table, refcount_table_size,
+ l2_offset, s->cluster_size);
+
+ /* L2 tables are cluster aligned */
+ if (l2_offset & (s->cluster_size - 1)) {
+ fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not "
+ "cluster aligned; L1 entry corrupted\n", l2_offset);
+ res->corruptions++;
+ }
+
+ /* Process and check L2 entries */
+ ret = check_refcounts_l2(bs, res, refcount_table,
+ refcount_table_size, l2_offset, check_copied);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+ }
+ qemu_free(l1_table);
+ return 0;
+
+fail:
+ fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
+ res->check_errors++;
+ qemu_free(l1_table);
+ return -EIO;
+}
+
+/*
+ * Checks an image for refcount consistency.
+ *
+ * Returns 0 if no errors are found, the number of errors in case the image is
+ * detected as corrupted, and -errno when an internal error occured.
+ */
+int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t size;
+ int nb_clusters, refcount1, refcount2, i;
+ QCowSnapshot *sn;
+ uint16_t *refcount_table;
+ int ret;
+
+ size = bdrv_getlength(bs->file);
+ nb_clusters = size_to_clusters(s, size);
+ refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t));
+
+ /* header */
+ inc_refcounts(bs, res, refcount_table, nb_clusters,
+ 0, s->cluster_size);
+
+ /* current L1 table */
+ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
+ s->l1_table_offset, s->l1_size, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* snapshots */
+ for(i = 0; i < s->nb_snapshots; i++) {
+ sn = s->snapshots + i;
+ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
+ sn->l1_table_offset, sn->l1_size, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ inc_refcounts(bs, res, refcount_table, nb_clusters,
+ s->snapshots_offset, s->snapshots_size);
+
+ /* refcount data */
+ inc_refcounts(bs, res, refcount_table, nb_clusters,
+ s->refcount_table_offset,
+ s->refcount_table_size * sizeof(uint64_t));
+
+ for(i = 0; i < s->refcount_table_size; i++) {
+ uint64_t offset, cluster;
+ offset = s->refcount_table[i];
+ cluster = offset >> s->cluster_bits;
+
+ /* Refcount blocks are cluster aligned */
+ if (offset & (s->cluster_size - 1)) {
+ fprintf(stderr, "ERROR refcount block %d is not "
+ "cluster aligned; refcount table entry corrupted\n", i);
+ res->corruptions++;
+ continue;
+ }
+
+ if (cluster >= nb_clusters) {
+ fprintf(stderr, "ERROR refcount block %d is outside image\n", i);
+ res->corruptions++;
+ continue;
+ }
+
+ if (offset != 0) {
+ inc_refcounts(bs, res, refcount_table, nb_clusters,
+ offset, s->cluster_size);
+ if (refcount_table[cluster] != 1) {
+ fprintf(stderr, "ERROR refcount block %d refcount=%d\n",
+ i, refcount_table[cluster]);
+ res->corruptions++;
+ }
+ }
+ }
+
+ /* compare ref counts */
+ for(i = 0; i < nb_clusters; i++) {
+ refcount1 = get_refcount(bs, i);
+ if (refcount1 < 0) {
+ fprintf(stderr, "Can't get refcount for cluster %d: %s\n",
+ i, strerror(-refcount1));
+ res->check_errors++;
+ continue;
+ }
+
+ refcount2 = refcount_table[i];
+ if (refcount1 != refcount2) {
+ fprintf(stderr, "%s cluster %d refcount=%d reference=%d\n",
+ refcount1 < refcount2 ? "ERROR" : "Leaked",
+ i, refcount1, refcount2);
+ if (refcount1 < refcount2) {
+ res->corruptions++;
+ } else {
+ res->leaks++;
+ }
+ }
+ }
+
+ qemu_free(refcount_table);
+
+ return 0;
+}
+
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
new file mode 100644
index 0000000..74823a5
--- /dev/null
+++ b/block/qcow2-snapshot.c
@@ -0,0 +1,451 @@
+/*
+ * Block driver for the QCOW version 2 format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "block/qcow2.h"
+
+typedef struct __attribute__((packed)) QCowSnapshotHeader {
+ /* header is 8 byte aligned */
+ uint64_t l1_table_offset;
+
+ uint32_t l1_size;
+ uint16_t id_str_size;
+ uint16_t name_size;
+
+ uint32_t date_sec;
+ uint32_t date_nsec;
+
+ uint64_t vm_clock_nsec;
+
+ uint32_t vm_state_size;
+ uint32_t extra_data_size; /* for extension */
+ /* extra data follows */
+ /* id_str follows */
+ /* name follows */
+} QCowSnapshotHeader;
+
+void qcow2_free_snapshots(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ int i;
+
+ for(i = 0; i < s->nb_snapshots; i++) {
+ qemu_free(s->snapshots[i].name);
+ qemu_free(s->snapshots[i].id_str);
+ }
+ qemu_free(s->snapshots);
+ s->snapshots = NULL;
+ s->nb_snapshots = 0;
+}
+
+int qcow2_read_snapshots(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshotHeader h;
+ QCowSnapshot *sn;
+ int i, id_str_size, name_size;
+ int64_t offset;
+ uint32_t extra_data_size;
+
+ if (!s->nb_snapshots) {
+ s->snapshots = NULL;
+ s->snapshots_size = 0;
+ return 0;
+ }
+
+ offset = s->snapshots_offset;
+ s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot));
+ for(i = 0; i < s->nb_snapshots; i++) {
+ offset = align_offset(offset, 8);
+ if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h))
+ goto fail;
+ offset += sizeof(h);
+ sn = s->snapshots + i;
+ sn->l1_table_offset = be64_to_cpu(h.l1_table_offset);
+ sn->l1_size = be32_to_cpu(h.l1_size);
+ sn->vm_state_size = be32_to_cpu(h.vm_state_size);
+ sn->date_sec = be32_to_cpu(h.date_sec);
+ sn->date_nsec = be32_to_cpu(h.date_nsec);
+ sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec);
+ extra_data_size = be32_to_cpu(h.extra_data_size);
+
+ id_str_size = be16_to_cpu(h.id_str_size);
+ name_size = be16_to_cpu(h.name_size);
+
+ offset += extra_data_size;
+
+ sn->id_str = qemu_malloc(id_str_size + 1);
+ if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size)
+ goto fail;
+ offset += id_str_size;
+ sn->id_str[id_str_size] = '\0';
+
+ sn->name = qemu_malloc(name_size + 1);
+ if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size)
+ goto fail;
+ offset += name_size;
+ sn->name[name_size] = '\0';
+ }
+ s->snapshots_size = offset - s->snapshots_offset;
+ return 0;
+ fail:
+ qcow2_free_snapshots(bs);
+ return -1;
+}
+
+/* add at the end of the file a new list of snapshots */
+static int qcow2_write_snapshots(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshot *sn;
+ QCowSnapshotHeader h;
+ int i, name_size, id_str_size, snapshots_size;
+ uint64_t data64;
+ uint32_t data32;
+ int64_t offset, snapshots_offset;
+
+ /* compute the size of the snapshots */
+ offset = 0;
+ for(i = 0; i < s->nb_snapshots; i++) {
+ sn = s->snapshots + i;
+ offset = align_offset(offset, 8);
+ offset += sizeof(h);
+ offset += strlen(sn->id_str);
+ offset += strlen(sn->name);
+ }
+ snapshots_size = offset;
+
+ snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
+ bdrv_flush(bs->file);
+ offset = snapshots_offset;
+ if (offset < 0) {
+ return offset;
+ }
+
+ for(i = 0; i < s->nb_snapshots; i++) {
+ sn = s->snapshots + i;
+ memset(&h, 0, sizeof(h));
+ h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
+ h.l1_size = cpu_to_be32(sn->l1_size);
+ h.vm_state_size = cpu_to_be32(sn->vm_state_size);
+ h.date_sec = cpu_to_be32(sn->date_sec);
+ h.date_nsec = cpu_to_be32(sn->date_nsec);
+ h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
+
+ id_str_size = strlen(sn->id_str);
+ name_size = strlen(sn->name);
+ h.id_str_size = cpu_to_be16(id_str_size);
+ h.name_size = cpu_to_be16(name_size);
+ offset = align_offset(offset, 8);
+ if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0)
+ goto fail;
+ offset += sizeof(h);
+ if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0)
+ goto fail;
+ offset += id_str_size;
+ if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0)
+ goto fail;
+ offset += name_size;
+ }
+
+ /* update the various header fields */
+ data64 = cpu_to_be64(snapshots_offset);
+ if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset),
+ &data64, sizeof(data64)) < 0)
+ goto fail;
+ data32 = cpu_to_be32(s->nb_snapshots);
+ if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
+ &data32, sizeof(data32)) < 0)
+ goto fail;
+
+ /* free the old snapshot table */
+ qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size);
+ s->snapshots_offset = snapshots_offset;
+ s->snapshots_size = snapshots_size;
+ return 0;
+ fail:
+ return -1;
+}
+
+static void find_new_snapshot_id(BlockDriverState *bs,
+ char *id_str, int id_str_size)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshot *sn;
+ int i, id, id_max = 0;
+
+ for(i = 0; i < s->nb_snapshots; i++) {
+ sn = s->snapshots + i;
+ id = strtoul(sn->id_str, NULL, 10);
+ if (id > id_max)
+ id_max = id;
+ }
+ snprintf(id_str, id_str_size, "%d", id_max + 1);
+}
+
+static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
+{
+ BDRVQcowState *s = bs->opaque;
+ int i;
+
+ for(i = 0; i < s->nb_snapshots; i++) {
+ if (!strcmp(s->snapshots[i].id_str, id_str))
+ return i;
+ }
+ return -1;
+}
+
+static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
+{
+ BDRVQcowState *s = bs->opaque;
+ int i, ret;
+
+ ret = find_snapshot_by_id(bs, name);
+ if (ret >= 0)
+ return ret;
+ for(i = 0; i < s->nb_snapshots; i++) {
+ if (!strcmp(s->snapshots[i].name, name))
+ return i;
+ }
+ return -1;
+}
+
+/* if no id is provided, a new one is constructed */
+int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshot *snapshots1, sn1, *sn = &sn1;
+ int i, ret;
+ uint64_t *l1_table = NULL;
+ int64_t l1_table_offset;
+
+ memset(sn, 0, sizeof(*sn));
+
+ if (sn_info->id_str[0] == '\0') {
+ /* compute a new id */
+ find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
+ }
+
+ /* check that the ID is unique */
+ if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
+ return -ENOENT;
+
+ sn->id_str = qemu_strdup(sn_info->id_str);
+ if (!sn->id_str)
+ goto fail;
+ sn->name = qemu_strdup(sn_info->name);
+ if (!sn->name)
+ goto fail;
+ sn->vm_state_size = sn_info->vm_state_size;
+ sn->date_sec = sn_info->date_sec;
+ sn->date_nsec = sn_info->date_nsec;
+ sn->vm_clock_nsec = sn_info->vm_clock_nsec;
+
+ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
+ if (ret < 0)
+ goto fail;
+
+ /* create the L1 table of the snapshot */
+ l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
+ if (l1_table_offset < 0) {
+ goto fail;
+ }
+ bdrv_flush(bs->file);
+
+ sn->l1_table_offset = l1_table_offset;
+ sn->l1_size = s->l1_size;
+
+ if (s->l1_size != 0) {
+ l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
+ } else {
+ l1_table = NULL;
+ }
+
+ for(i = 0; i < s->l1_size; i++) {
+ l1_table[i] = cpu_to_be64(s->l1_table[i]);
+ }
+ if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset,
+ l1_table, s->l1_size * sizeof(uint64_t)) < 0)
+ goto fail;
+ qemu_free(l1_table);
+ l1_table = NULL;
+
+ snapshots1 = qemu_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
+ if (s->snapshots) {
+ memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
+ qemu_free(s->snapshots);
+ }
+ s->snapshots = snapshots1;
+ s->snapshots[s->nb_snapshots++] = *sn;
+
+ if (qcow2_write_snapshots(bs) < 0)
+ goto fail;
+#ifdef DEBUG_ALLOC
+ qcow2_check_refcounts(bs);
+#endif
+ return 0;
+ fail:
+ qemu_free(sn->name);
+ qemu_free(l1_table);
+ return -1;
+}
+
+/* copy the snapshot 'snapshot_name' into the current disk image */
+int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshot *sn;
+ int i, snapshot_index, l1_size2;
+
+ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
+ if (snapshot_index < 0)
+ return -ENOENT;
+ sn = &s->snapshots[snapshot_index];
+
+ if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
+ goto fail;
+
+ if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0)
+ goto fail;
+
+ s->l1_size = sn->l1_size;
+ l1_size2 = s->l1_size * sizeof(uint64_t);
+ /* copy the snapshot l1 table to the current l1 table */
+ if (bdrv_pread(bs->file, sn->l1_table_offset,
+ s->l1_table, l1_size2) != l1_size2)
+ goto fail;
+ if (bdrv_pwrite_sync(bs->file, s->l1_table_offset,
+ s->l1_table, l1_size2) < 0)
+ goto fail;
+ for(i = 0;i < s->l1_size; i++) {
+ be64_to_cpus(&s->l1_table[i]);
+ }
+
+ if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0)
+ goto fail;
+
+#ifdef DEBUG_ALLOC
+ qcow2_check_refcounts(bs);
+#endif
+ return 0;
+ fail:
+ return -EIO;
+}
+
+int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshot *sn;
+ int snapshot_index, ret;
+
+ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
+ if (snapshot_index < 0)
+ return -ENOENT;
+ sn = &s->snapshots[snapshot_index];
+
+ ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1);
+ if (ret < 0)
+ return ret;
+ /* must update the copied flag on the current cluster offsets */
+ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
+ if (ret < 0)
+ return ret;
+ qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t));
+
+ qemu_free(sn->id_str);
+ qemu_free(sn->name);
+ memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
+ s->nb_snapshots--;
+ ret = qcow2_write_snapshots(bs);
+ if (ret < 0) {
+ /* XXX: restore snapshot if error ? */
+ return ret;
+ }
+#ifdef DEBUG_ALLOC
+ qcow2_check_refcounts(bs);
+#endif
+ return 0;
+}
+
+int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
+{
+ BDRVQcowState *s = bs->opaque;
+ QEMUSnapshotInfo *sn_tab, *sn_info;
+ QCowSnapshot *sn;
+ int i;
+
+ if (!s->nb_snapshots) {
+ *psn_tab = NULL;
+ return s->nb_snapshots;
+ }
+
+ sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo));
+ for(i = 0; i < s->nb_snapshots; i++) {
+ sn_info = sn_tab + i;
+ sn = s->snapshots + i;
+ pstrcpy(sn_info->id_str, sizeof(sn_info->id_str),
+ sn->id_str);
+ pstrcpy(sn_info->name, sizeof(sn_info->name),
+ sn->name);
+ sn_info->vm_state_size = sn->vm_state_size;
+ sn_info->date_sec = sn->date_sec;
+ sn_info->date_nsec = sn->date_nsec;
+ sn_info->vm_clock_nsec = sn->vm_clock_nsec;
+ }
+ *psn_tab = sn_tab;
+ return s->nb_snapshots;
+}
+
+int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
+{
+ int i, snapshot_index, l1_size2;
+ BDRVQcowState *s = bs->opaque;
+ QCowSnapshot *sn;
+
+ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
+ if (snapshot_index < 0) {
+ return -ENOENT;
+ }
+
+ sn = &s->snapshots[snapshot_index];
+ s->l1_size = sn->l1_size;
+ l1_size2 = s->l1_size * sizeof(uint64_t);
+ if (s->l1_table != NULL) {
+ qemu_free(s->l1_table);
+ }
+
+ s->l1_table_offset = sn->l1_table_offset;
+ s->l1_table = qemu_mallocz(align_offset(l1_size2, 512));
+
+ if (bdrv_pread(bs->file, sn->l1_table_offset,
+ s->l1_table, l1_size2) != l1_size2) {
+ return -1;
+ }
+
+ for(i = 0;i < s->l1_size; i++) {
+ be64_to_cpus(&s->l1_table[i]);
+ }
+ return 0;
+}
diff --git a/block/qcow2.c b/block/qcow2.c
new file mode 100644
index 0000000..75b8bec
--- /dev/null
+++ b/block/qcow2.c
@@ -0,0 +1,1397 @@
+/*
+ * Block driver for the QCOW version 2 format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+#include <zlib.h>
+#include "aes.h"
+#include "block/qcow2.h"
+#include "qemu-error.h"
+#include "qerror.h"
+
+/*
+ Differences with QCOW:
+
+ - Support for multiple incremental snapshots.
+ - Memory management by reference counts.
+ - Clusters which have a reference count of one have the bit
+ QCOW_OFLAG_COPIED to optimize write performance.
+ - Size of compressed clusters is stored in sectors to reduce bit usage
+ in the cluster offsets.
+ - Support for storing additional data (such as the VM state) in the
+ snapshots.
+ - If a backing store is used, the cluster size is not constrained
+ (could be backported to QCOW).
+ - L2 tables have always a size of one cluster.
+*/
+
+
+typedef struct {
+ uint32_t magic;
+ uint32_t len;
+} QCowExtension;
+#define QCOW2_EXT_MAGIC_END 0
+#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
+
+static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const QCowHeader *cow_header = (const void *)buf;
+
+ if (buf_size >= sizeof(QCowHeader) &&
+ be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
+ be32_to_cpu(cow_header->version) >= QCOW_VERSION)
+ return 100;
+ else
+ return 0;
+}
+
+
+/*
+ * read qcow2 extension and fill bs
+ * start reading from start_offset
+ * finish reading upon magic of value 0 or when end_offset reached
+ * unknown magic is skipped (future extension this version knows nothing about)
+ * return 0 upon success, non-0 otherwise
+ */
+static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
+ uint64_t end_offset)
+{
+ QCowExtension ext;
+ uint64_t offset;
+
+#ifdef DEBUG_EXT
+ printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset);
+#endif
+ offset = start_offset;
+ while (offset < end_offset) {
+
+#ifdef DEBUG_EXT
+ /* Sanity check */
+ if (offset > s->cluster_size)
+ printf("qcow2_read_extension: suspicious offset %lu\n", offset);
+
+ printf("attemting to read extended header in offset %lu\n", offset);
+#endif
+
+ if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) {
+ fprintf(stderr, "qcow2_read_extension: ERROR: "
+ "pread fail from offset %" PRIu64 "\n",
+ offset);
+ return 1;
+ }
+ be32_to_cpus(&ext.magic);
+ be32_to_cpus(&ext.len);
+ offset += sizeof(ext);
+#ifdef DEBUG_EXT
+ printf("ext.magic = 0x%x\n", ext.magic);
+#endif
+ switch (ext.magic) {
+ case QCOW2_EXT_MAGIC_END:
+ return 0;
+
+ case QCOW2_EXT_MAGIC_BACKING_FORMAT:
+ if (ext.len >= sizeof(bs->backing_format)) {
+ fprintf(stderr, "ERROR: ext_backing_format: len=%u too large"
+ " (>=%zu)\n",
+ ext.len, sizeof(bs->backing_format));
+ return 2;
+ }
+ if (bdrv_pread(bs->file, offset , bs->backing_format,
+ ext.len) != ext.len)
+ return 3;
+ bs->backing_format[ext.len] = '\0';
+#ifdef DEBUG_EXT
+ printf("Qcow2: Got format extension %s\n", bs->backing_format);
+#endif
+ offset = ((offset + ext.len + 7) & ~7);
+ break;
+
+ default:
+ /* unknown magic -- just skip it */
+ offset = ((offset + ext.len + 7) & ~7);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int qcow2_open(BlockDriverState *bs, int flags)
+{
+ BDRVQcowState *s = bs->opaque;
+ int len, i, ret = 0;
+ QCowHeader header;
+ uint64_t ext_end;
+ bool writethrough;
+
+ ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
+ if (ret < 0) {
+ goto fail;
+ }
+ be32_to_cpus(&header.magic);
+ be32_to_cpus(&header.version);
+ be64_to_cpus(&header.backing_file_offset);
+ be32_to_cpus(&header.backing_file_size);
+ be64_to_cpus(&header.size);
+ be32_to_cpus(&header.cluster_bits);
+ be32_to_cpus(&header.crypt_method);
+ be64_to_cpus(&header.l1_table_offset);
+ be32_to_cpus(&header.l1_size);
+ be64_to_cpus(&header.refcount_table_offset);
+ be32_to_cpus(&header.refcount_table_clusters);
+ be64_to_cpus(&header.snapshots_offset);
+ be32_to_cpus(&header.nb_snapshots);
+
+ if (header.magic != QCOW_MAGIC) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (header.version != QCOW_VERSION) {
+ char version[64];
+ snprintf(version, sizeof(version), "QCOW version %d", header.version);
+ qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ bs->device_name, "qcow2", version);
+ ret = -ENOTSUP;
+ goto fail;
+ }
+ if (header.cluster_bits < MIN_CLUSTER_BITS ||
+ header.cluster_bits > MAX_CLUSTER_BITS) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ if (header.crypt_method > QCOW_CRYPT_AES) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ s->crypt_method_header = header.crypt_method;
+ if (s->crypt_method_header) {
+ bs->encrypted = 1;
+ }
+ s->cluster_bits = header.cluster_bits;
+ s->cluster_size = 1 << s->cluster_bits;
+ s->cluster_sectors = 1 << (s->cluster_bits - 9);
+ s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
+ s->l2_size = 1 << s->l2_bits;
+ bs->total_sectors = header.size / 512;
+ s->csize_shift = (62 - (s->cluster_bits - 8));
+ s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
+ s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
+ s->refcount_table_offset = header.refcount_table_offset;
+ s->refcount_table_size =
+ header.refcount_table_clusters << (s->cluster_bits - 3);
+
+ s->snapshots_offset = header.snapshots_offset;
+ s->nb_snapshots = header.nb_snapshots;
+
+ /* read the level 1 table */
+ s->l1_size = header.l1_size;
+ s->l1_vm_state_index = size_to_l1(s, header.size);
+ /* the L1 table must contain at least enough entries to put
+ header.size bytes */
+ if (s->l1_size < s->l1_vm_state_index) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ s->l1_table_offset = header.l1_table_offset;
+ if (s->l1_size > 0) {
+ s->l1_table = qemu_mallocz(
+ align_offset(s->l1_size * sizeof(uint64_t), 512));
+ ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
+ s->l1_size * sizeof(uint64_t));
+ if (ret < 0) {
+ goto fail;
+ }
+ for(i = 0;i < s->l1_size; i++) {
+ be64_to_cpus(&s->l1_table[i]);
+ }
+ }
+
+ /* alloc L2 table/refcount block cache */
+ writethrough = ((flags & BDRV_O_CACHE_MASK) == 0);
+ s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE, writethrough);
+ s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE,
+ writethrough);
+
+ s->cluster_cache = qemu_malloc(s->cluster_size);
+ /* one more sector for decompressed data alignment */
+ s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size
+ + 512);
+ s->cluster_cache_offset = -1;
+
+ ret = qcow2_refcount_init(bs);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ QLIST_INIT(&s->cluster_allocs);
+
+ /* read qcow2 extensions */
+ if (header.backing_file_offset) {
+ ext_end = header.backing_file_offset;
+ } else {
+ ext_end = s->cluster_size;
+ }
+ if (qcow2_read_extensions(bs, sizeof(header), ext_end)) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* read the backing file name */
+ if (header.backing_file_offset != 0) {
+ len = header.backing_file_size;
+ if (len > 1023) {
+ len = 1023;
+ }
+ ret = bdrv_pread(bs->file, header.backing_file_offset,
+ bs->backing_file, len);
+ if (ret < 0) {
+ goto fail;
+ }
+ bs->backing_file[len] = '\0';
+ }
+ if (qcow2_read_snapshots(bs) < 0) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+#ifdef DEBUG_ALLOC
+ qcow2_check_refcounts(bs);
+#endif
+ return ret;
+
+ fail:
+ qcow2_free_snapshots(bs);
+ qcow2_refcount_close(bs);
+ qemu_free(s->l1_table);
+ if (s->l2_table_cache) {
+ qcow2_cache_destroy(bs, s->l2_table_cache);
+ }
+ qemu_free(s->cluster_cache);
+ qemu_free(s->cluster_data);
+ return ret;
+}
+
+static int qcow2_set_key(BlockDriverState *bs, const char *key)
+{
+ BDRVQcowState *s = bs->opaque;
+ uint8_t keybuf[16];
+ int len, i;
+
+ memset(keybuf, 0, 16);
+ len = strlen(key);
+ if (len > 16)
+ len = 16;
+ /* XXX: we could compress the chars to 7 bits to increase
+ entropy */
+ for(i = 0;i < len;i++) {
+ keybuf[i] = key[i];
+ }
+ s->crypt_method = s->crypt_method_header;
+
+ if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+ return -1;
+ if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+ return -1;
+#if 0
+ /* test */
+ {
+ uint8_t in[16];
+ uint8_t out[16];
+ uint8_t tmp[16];
+ for(i=0;i<16;i++)
+ in[i] = i;
+ AES_encrypt(in, tmp, &s->aes_encrypt_key);
+ AES_decrypt(tmp, out, &s->aes_decrypt_key);
+ for(i = 0; i < 16; i++)
+ printf(" %02x", tmp[i]);
+ printf("\n");
+ for(i = 0; i < 16; i++)
+ printf(" %02x", out[i]);
+ printf("\n");
+ }
+#endif
+ return 0;
+}
+
+static int qcow2_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ uint64_t cluster_offset;
+ int ret;
+
+ *pnum = nb_sectors;
+ /* FIXME We can get errors here, but the bdrv_is_allocated interface can't
+ * pass them on today */
+ ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
+ if (ret < 0) {
+ *pnum = 0;
+ }
+
+ return (cluster_offset != 0);
+}
+
+/* handle reading after the end of the backing file */
+int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t sector_num, int nb_sectors)
+{
+ int n1;
+ if ((sector_num + nb_sectors) <= bs->total_sectors)
+ return nb_sectors;
+ if (sector_num >= bs->total_sectors)
+ n1 = 0;
+ else
+ n1 = bs->total_sectors - sector_num;
+
+ qemu_iovec_memset_skip(qiov, 0, 512 * (nb_sectors - n1), 512 * n1);
+
+ return n1;
+}
+
+typedef struct QCowAIOCB {
+ BlockDriverAIOCB common;
+ int64_t sector_num;
+ QEMUIOVector *qiov;
+ int remaining_sectors;
+ int cur_nr_sectors; /* number of sectors in current iteration */
+ uint64_t bytes_done;
+ uint64_t cluster_offset;
+ uint8_t *cluster_data;
+ BlockDriverAIOCB *hd_aiocb;
+ QEMUIOVector hd_qiov;
+ QEMUBH *bh;
+ QCowL2Meta l2meta;
+ QLIST_ENTRY(QCowAIOCB) next_depend;
+} QCowAIOCB;
+
+static void qcow2_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common);
+ if (acb->hd_aiocb)
+ bdrv_aio_cancel(acb->hd_aiocb);
+ qemu_aio_release(acb);
+}
+
+static AIOPool qcow2_aio_pool = {
+ .aiocb_size = sizeof(QCowAIOCB),
+ .cancel = qcow2_aio_cancel,
+};
+
+static void qcow2_aio_read_cb(void *opaque, int ret);
+static void qcow2_aio_read_bh(void *opaque)
+{
+ QCowAIOCB *acb = opaque;
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+ qcow2_aio_read_cb(opaque, 0);
+}
+
+static int qcow2_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb)
+{
+ if (acb->bh)
+ return -EIO;
+
+ acb->bh = qemu_bh_new(cb, acb);
+ if (!acb->bh)
+ return -EIO;
+
+ qemu_bh_schedule(acb->bh);
+
+ return 0;
+}
+
+static void qcow2_aio_read_cb(void *opaque, int ret)
+{
+ QCowAIOCB *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVQcowState *s = bs->opaque;
+ int index_in_cluster, n1;
+
+ acb->hd_aiocb = NULL;
+ if (ret < 0)
+ goto done;
+
+ /* post process the read buffer */
+ if (!acb->cluster_offset) {
+ /* nothing to do */
+ } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ /* nothing to do */
+ } else {
+ if (s->crypt_method) {
+ qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data,
+ acb->cluster_data, acb->cur_nr_sectors, 0, &s->aes_decrypt_key);
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done,
+ acb->cur_nr_sectors * 512);
+ qemu_iovec_from_buffer(&acb->hd_qiov, acb->cluster_data,
+ 512 * acb->cur_nr_sectors);
+ }
+ }
+
+ acb->remaining_sectors -= acb->cur_nr_sectors;
+ acb->sector_num += acb->cur_nr_sectors;
+ acb->bytes_done += acb->cur_nr_sectors * 512;
+
+ if (acb->remaining_sectors == 0) {
+ /* request completed */
+ ret = 0;
+ goto done;
+ }
+
+ /* prepare next AIO request */
+ acb->cur_nr_sectors = acb->remaining_sectors;
+ if (s->crypt_method) {
+ acb->cur_nr_sectors = MIN(acb->cur_nr_sectors,
+ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
+ }
+
+ ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9,
+ &acb->cur_nr_sectors, &acb->cluster_offset);
+ if (ret < 0) {
+ goto done;
+ }
+
+ index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done,
+ acb->cur_nr_sectors * 512);
+
+ if (!acb->cluster_offset) {
+
+ if (bs->backing_hd) {
+ /* read from the base image */
+ n1 = qcow2_backing_read1(bs->backing_hd, &acb->hd_qiov,
+ acb->sector_num, acb->cur_nr_sectors);
+ if (n1 > 0) {
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
+ acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
+ &acb->hd_qiov, n1, qcow2_aio_read_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
+ goto done;
+ }
+ } else {
+ ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb);
+ if (ret < 0)
+ goto done;
+ }
+ } else {
+ /* Note: in this case, no need to wait */
+ qemu_iovec_memset(&acb->hd_qiov, 0, 512 * acb->cur_nr_sectors);
+ ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb);
+ if (ret < 0)
+ goto done;
+ }
+ } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+ /* add AIO support for compressed blocks ? */
+ ret = qcow2_decompress_cluster(bs, acb->cluster_offset);
+ if (ret < 0) {
+ goto done;
+ }
+
+ qemu_iovec_from_buffer(&acb->hd_qiov,
+ s->cluster_cache + index_in_cluster * 512,
+ 512 * acb->cur_nr_sectors);
+
+ ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb);
+ if (ret < 0)
+ goto done;
+ } else {
+ if ((acb->cluster_offset & 511) != 0) {
+ ret = -EIO;
+ goto done;
+ }
+
+ if (s->crypt_method) {
+ /*
+ * For encrypted images, read everything into a temporary
+ * contiguous buffer on which the AES functions can work.
+ */
+ if (!acb->cluster_data) {
+ acb->cluster_data =
+ qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+ }
+
+ assert(acb->cur_nr_sectors <=
+ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_add(&acb->hd_qiov, acb->cluster_data,
+ 512 * acb->cur_nr_sectors);
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
+ acb->hd_aiocb = bdrv_aio_readv(bs->file,
+ (acb->cluster_offset >> 9) + index_in_cluster,
+ &acb->hd_qiov, acb->cur_nr_sectors,
+ qcow2_aio_read_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
+ goto done;
+ }
+ }
+
+ return;
+done:
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_iovec_destroy(&acb->hd_qiov);
+ qemu_aio_release(acb);
+}
+
+static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, int is_write)
+{
+ QCowAIOCB *acb;
+
+ acb = qemu_aio_get(&qcow2_aio_pool, bs, cb, opaque);
+ if (!acb)
+ return NULL;
+ acb->hd_aiocb = NULL;
+ acb->sector_num = sector_num;
+ acb->qiov = qiov;
+
+ qemu_iovec_init(&acb->hd_qiov, qiov->niov);
+
+ acb->bytes_done = 0;
+ acb->remaining_sectors = nb_sectors;
+ acb->cur_nr_sectors = 0;
+ acb->cluster_offset = 0;
+ acb->l2meta.nb_clusters = 0;
+ QLIST_INIT(&acb->l2meta.dependent_requests);
+ return acb;
+}
+
+static BlockDriverAIOCB *qcow2_aio_readv(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ QCowAIOCB *acb;
+
+ acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+ if (!acb)
+ return NULL;
+
+ qcow2_aio_read_cb(acb, 0);
+ return &acb->common;
+}
+
+static void qcow2_aio_write_cb(void *opaque, int ret);
+
+static void run_dependent_requests(QCowL2Meta *m)
+{
+ QCowAIOCB *req;
+ QCowAIOCB *next;
+
+ /* Take the request off the list of running requests */
+ if (m->nb_clusters != 0) {
+ QLIST_REMOVE(m, next_in_flight);
+ }
+
+ /* Restart all dependent requests */
+ QLIST_FOREACH_SAFE(req, &m->dependent_requests, next_depend, next) {
+ qcow2_aio_write_cb(req, 0);
+ }
+
+ /* Empty the list for the next part of the request */
+ QLIST_INIT(&m->dependent_requests);
+}
+
+static void qcow2_aio_write_cb(void *opaque, int ret)
+{
+ QCowAIOCB *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVQcowState *s = bs->opaque;
+ int index_in_cluster;
+ int n_end;
+
+ acb->hd_aiocb = NULL;
+
+ if (ret >= 0) {
+ ret = qcow2_alloc_cluster_link_l2(bs, &acb->l2meta);
+ }
+
+ run_dependent_requests(&acb->l2meta);
+
+ if (ret < 0)
+ goto done;
+
+ acb->remaining_sectors -= acb->cur_nr_sectors;
+ acb->sector_num += acb->cur_nr_sectors;
+ acb->bytes_done += acb->cur_nr_sectors * 512;
+
+ if (acb->remaining_sectors == 0) {
+ /* request completed */
+ ret = 0;
+ goto done;
+ }
+
+ index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+ n_end = index_in_cluster + acb->remaining_sectors;
+ if (s->crypt_method &&
+ n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors)
+ n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
+
+ ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9,
+ index_in_cluster, n_end, &acb->cur_nr_sectors, &acb->l2meta);
+ if (ret < 0) {
+ goto done;
+ }
+
+ acb->cluster_offset = acb->l2meta.cluster_offset;
+
+ /* Need to wait for another request? If so, we are done for now. */
+ if (acb->l2meta.nb_clusters == 0 && acb->l2meta.depends_on != NULL) {
+ QLIST_INSERT_HEAD(&acb->l2meta.depends_on->dependent_requests,
+ acb, next_depend);
+ return;
+ }
+
+ assert((acb->cluster_offset & 511) == 0);
+
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done,
+ acb->cur_nr_sectors * 512);
+
+ if (s->crypt_method) {
+ if (!acb->cluster_data) {
+ acb->cluster_data = qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS *
+ s->cluster_size);
+ }
+
+ assert(acb->hd_qiov.size <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
+ qemu_iovec_to_buffer(&acb->hd_qiov, acb->cluster_data);
+
+ qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data,
+ acb->cluster_data, acb->cur_nr_sectors, 1, &s->aes_encrypt_key);
+
+ qemu_iovec_reset(&acb->hd_qiov);
+ qemu_iovec_add(&acb->hd_qiov, acb->cluster_data,
+ acb->cur_nr_sectors * 512);
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
+ acb->hd_aiocb = bdrv_aio_writev(bs->file,
+ (acb->cluster_offset >> 9) + index_in_cluster,
+ &acb->hd_qiov, acb->cur_nr_sectors,
+ qcow2_aio_write_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
+ goto fail;
+ }
+
+ return;
+
+fail:
+ if (acb->l2meta.nb_clusters != 0) {
+ QLIST_REMOVE(&acb->l2meta, next_in_flight);
+ }
+done:
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_iovec_destroy(&acb->hd_qiov);
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *qcow2_aio_writev(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BDRVQcowState *s = bs->opaque;
+ QCowAIOCB *acb;
+
+ s->cluster_cache_offset = -1; /* disable compressed cache */
+
+ acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+ if (!acb)
+ return NULL;
+
+ qcow2_aio_write_cb(acb, 0);
+ return &acb->common;
+}
+
+static void qcow2_close(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ qemu_free(s->l1_table);
+
+ qcow2_cache_flush(bs, s->l2_table_cache);
+ qcow2_cache_flush(bs, s->refcount_block_cache);
+
+ qcow2_cache_destroy(bs, s->l2_table_cache);
+ qcow2_cache_destroy(bs, s->refcount_block_cache);
+
+ qemu_free(s->cluster_cache);
+ qemu_free(s->cluster_data);
+ qcow2_refcount_close(bs);
+}
+
+/*
+ * Updates the variable length parts of the qcow2 header, i.e. the backing file
+ * name and all extensions. qcow2 was not designed to allow such changes, so if
+ * we run out of space (we can only use the first cluster) this function may
+ * fail.
+ *
+ * Returns 0 on success, -errno in error cases.
+ */
+static int qcow2_update_ext_header(BlockDriverState *bs,
+ const char *backing_file, const char *backing_fmt)
+{
+ size_t backing_file_len = 0;
+ size_t backing_fmt_len = 0;
+ BDRVQcowState *s = bs->opaque;
+ QCowExtension ext_backing_fmt = {0, 0};
+ int ret;
+
+ /* Backing file format doesn't make sense without a backing file */
+ if (backing_fmt && !backing_file) {
+ return -EINVAL;
+ }
+
+ /* Prepare the backing file format extension if needed */
+ if (backing_fmt) {
+ ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt));
+ ext_backing_fmt.magic = cpu_to_be32(QCOW2_EXT_MAGIC_BACKING_FORMAT);
+ backing_fmt_len = ((sizeof(ext_backing_fmt)
+ + strlen(backing_fmt) + 7) & ~7);
+ }
+
+ /* Check if we can fit the new header into the first cluster */
+ if (backing_file) {
+ backing_file_len = strlen(backing_file);
+ }
+
+ size_t header_size = sizeof(QCowHeader) + backing_file_len
+ + backing_fmt_len;
+
+ if (header_size > s->cluster_size) {
+ return -ENOSPC;
+ }
+
+ /* Rewrite backing file name and qcow2 extensions */
+ size_t ext_size = header_size - sizeof(QCowHeader);
+ uint8_t buf[ext_size];
+ size_t offset = 0;
+ size_t backing_file_offset = 0;
+
+ if (backing_file) {
+ if (backing_fmt) {
+ int padding = backing_fmt_len -
+ (sizeof(ext_backing_fmt) + strlen(backing_fmt));
+
+ memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt));
+ offset += sizeof(ext_backing_fmt);
+
+ memcpy(buf + offset, backing_fmt, strlen(backing_fmt));
+ offset += strlen(backing_fmt);
+
+ memset(buf + offset, 0, padding);
+ offset += padding;
+ }
+
+ memcpy(buf + offset, backing_file, backing_file_len);
+ backing_file_offset = sizeof(QCowHeader) + offset;
+ }
+
+ ret = bdrv_pwrite_sync(bs->file, sizeof(QCowHeader), buf, ext_size);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ /* Update header fields */
+ uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset);
+ uint32_t be_backing_file_size = cpu_to_be32(backing_file_len);
+
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_offset),
+ &be_backing_file_offset, sizeof(uint64_t));
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_size),
+ &be_backing_file_size, sizeof(uint32_t));
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ return ret;
+}
+
+static int qcow2_change_backing_file(BlockDriverState *bs,
+ const char *backing_file, const char *backing_fmt)
+{
+ return qcow2_update_ext_header(bs, backing_file, backing_fmt);
+}
+
+static int preallocate(BlockDriverState *bs)
+{
+ uint64_t nb_sectors;
+ uint64_t offset;
+ int num;
+ int ret;
+ QCowL2Meta meta;
+
+ nb_sectors = bdrv_getlength(bs) >> 9;
+ offset = 0;
+ QLIST_INIT(&meta.dependent_requests);
+ meta.cluster_offset = 0;
+
+ while (nb_sectors) {
+ num = MIN(nb_sectors, INT_MAX >> 9);
+ ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, &meta);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = qcow2_alloc_cluster_link_l2(bs, &meta);
+ if (ret < 0) {
+ qcow2_free_any_clusters(bs, meta.cluster_offset, meta.nb_clusters);
+ return ret;
+ }
+
+ /* There are no dependent requests, but we need to remove our request
+ * from the list of in-flight requests */
+ run_dependent_requests(&meta);
+
+ /* TODO Preallocate data if requested */
+
+ nb_sectors -= num;
+ offset += num << 9;
+ }
+
+ /*
+ * It is expected that the image file is large enough to actually contain
+ * all of the allocated clusters (otherwise we get failing reads after
+ * EOF). Extend the image to the last allocated sector.
+ */
+ if (meta.cluster_offset != 0) {
+ uint8_t buf[512];
+ memset(buf, 0, 512);
+ ret = bdrv_write(bs->file, (meta.cluster_offset >> 9) + num - 1, buf, 1);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int qcow2_create2(const char *filename, int64_t total_size,
+ const char *backing_file, const char *backing_format,
+ int flags, size_t cluster_size, int prealloc,
+ QEMUOptionParameter *options)
+{
+ /* Calulate cluster_bits */
+ int cluster_bits;
+ cluster_bits = ffs(cluster_size) - 1;
+ if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
+ (1 << cluster_bits) != cluster_size)
+ {
+ error_report(
+ "Cluster size must be a power of two between %d and %dk\n",
+ 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
+ return -EINVAL;
+ }
+
+ /*
+ * Open the image file and write a minimal qcow2 header.
+ *
+ * We keep things simple and start with a zero-sized image. We also
+ * do without refcount blocks or a L1 table for now. We'll fix the
+ * inconsistency later.
+ *
+ * We do need a refcount table because growing the refcount table means
+ * allocating two new refcount blocks - the seconds of which would be at
+ * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file
+ * size for any qcow2 image.
+ */
+ BlockDriverState* bs;
+ QCowHeader header;
+ uint8_t* refcount_table;
+ int ret;
+
+ ret = bdrv_create_file(filename, options);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Write the header */
+ memset(&header, 0, sizeof(header));
+ header.magic = cpu_to_be32(QCOW_MAGIC);
+ header.version = cpu_to_be32(QCOW_VERSION);
+ header.cluster_bits = cpu_to_be32(cluster_bits);
+ header.size = cpu_to_be64(0);
+ header.l1_table_offset = cpu_to_be64(0);
+ header.l1_size = cpu_to_be32(0);
+ header.refcount_table_offset = cpu_to_be64(cluster_size);
+ header.refcount_table_clusters = cpu_to_be32(1);
+
+ if (flags & BLOCK_FLAG_ENCRYPT) {
+ header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+ } else {
+ header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+ }
+
+ ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* Write an empty refcount table */
+ refcount_table = qemu_mallocz(cluster_size);
+ ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
+ qemu_free(refcount_table);
+
+ if (ret < 0) {
+ goto out;
+ }
+
+ bdrv_close(bs);
+
+ /*
+ * And now open the image and make it consistent first (i.e. increase the
+ * refcount of the cluster that is occupied by the header and the refcount
+ * table)
+ */
+ BlockDriver* drv = bdrv_find_format("qcow2");
+ assert(drv != NULL);
+ ret = bdrv_open(bs, filename,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
+ if (ret < 0) {
+ goto out;
+
+ } else if (ret != 0) {
+ error_report("Huh, first cluster in empty image is already in use?");
+ abort();
+ }
+
+ /* Okay, now that we have a valid image, let's give it the right size */
+ ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* Want a backing file? There you go.*/
+ if (backing_file) {
+ ret = bdrv_change_backing_file(bs, backing_file, backing_format);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
+ /* And if we're supposed to preallocate metadata, do that now */
+ if (prealloc) {
+ ret = preallocate(bs);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ bdrv_delete(bs);
+ return ret;
+}
+
+static int qcow2_create(const char *filename, QEMUOptionParameter *options)
+{
+ const char *backing_file = NULL;
+ const char *backing_fmt = NULL;
+ uint64_t sectors = 0;
+ int flags = 0;
+ size_t cluster_size = 65536;
+ int prealloc = 0;
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ sectors = options->value.n / 512;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FMT)) {
+ backing_fmt = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_ENCRYPT)) {
+ flags |= options->value.n ? BLOCK_FLAG_ENCRYPT : 0;
+ } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
+ if (options->value.n) {
+ cluster_size = options->value.n;
+ }
+ } else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
+ if (!options->value.s || !strcmp(options->value.s, "off")) {
+ prealloc = 0;
+ } else if (!strcmp(options->value.s, "metadata")) {
+ prealloc = 1;
+ } else {
+ fprintf(stderr, "Invalid preallocation mode: '%s'\n",
+ options->value.s);
+ return -EINVAL;
+ }
+ }
+ options++;
+ }
+
+ if (backing_file && prealloc) {
+ fprintf(stderr, "Backing file and preallocation cannot be used at "
+ "the same time\n");
+ return -EINVAL;
+ }
+
+ return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
+ cluster_size, prealloc, options);
+}
+
+static int qcow2_make_empty(BlockDriverState *bs)
+{
+#if 0
+ /* XXX: not correct */
+ BDRVQcowState *s = bs->opaque;
+ uint32_t l1_length = s->l1_size * sizeof(uint64_t);
+ int ret;
+
+ memset(s->l1_table, 0, l1_length);
+ if (bdrv_pwrite(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0)
+ return -1;
+ ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length);
+ if (ret < 0)
+ return ret;
+
+ l2_cache_reset(bs);
+#endif
+ return 0;
+}
+
+static int qcow2_discard(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors)
+{
+ return qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
+ nb_sectors);
+}
+
+static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret, new_l1_size;
+
+ if (offset & 511) {
+ return -EINVAL;
+ }
+
+ /* cannot proceed if image has snapshots */
+ if (s->nb_snapshots) {
+ return -ENOTSUP;
+ }
+
+ /* shrinking is currently not supported */
+ if (offset < bs->total_sectors * 512) {
+ return -ENOTSUP;
+ }
+
+ new_l1_size = size_to_l1(s, offset);
+ ret = qcow2_grow_l1_table(bs, new_l1_size, true);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* write updated header.size */
+ offset = cpu_to_be64(offset);
+ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
+ &offset, sizeof(uint64_t));
+ if (ret < 0) {
+ return ret;
+ }
+
+ s->l1_vm_state_index = new_l1_size;
+ return 0;
+}
+
+/* XXX: put compressed sectors first, then all the cluster aligned
+ tables to avoid losing bytes in alignment */
+static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVQcowState *s = bs->opaque;
+ z_stream strm;
+ int ret, out_len;
+ uint8_t *out_buf;
+ uint64_t cluster_offset;
+
+ if (nb_sectors == 0) {
+ /* align end of file to a sector boundary to ease reading with
+ sector based I/Os */
+ cluster_offset = bdrv_getlength(bs->file);
+ cluster_offset = (cluster_offset + 511) & ~511;
+ bdrv_truncate(bs->file, cluster_offset);
+ return 0;
+ }
+
+ if (nb_sectors != s->cluster_sectors)
+ return -EINVAL;
+
+ out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
+
+ /* best compression, small window, no zlib header */
+ memset(&strm, 0, sizeof(strm));
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED, -12,
+ 9, Z_DEFAULT_STRATEGY);
+ if (ret != 0) {
+ qemu_free(out_buf);
+ return -1;
+ }
+
+ strm.avail_in = s->cluster_size;
+ strm.next_in = (uint8_t *)buf;
+ strm.avail_out = s->cluster_size;
+ strm.next_out = out_buf;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ qemu_free(out_buf);
+ deflateEnd(&strm);
+ return -1;
+ }
+ out_len = strm.next_out - out_buf;
+
+ deflateEnd(&strm);
+
+ if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+ /* could not compress: write normal cluster */
+ bdrv_write(bs, sector_num, buf, s->cluster_sectors);
+ } else {
+ cluster_offset = qcow2_alloc_compressed_cluster_offset(bs,
+ sector_num << 9, out_len);
+ if (!cluster_offset)
+ return -1;
+ cluster_offset &= s->cluster_offset_mask;
+ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
+ if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) {
+ qemu_free(out_buf);
+ return -1;
+ }
+ }
+
+ qemu_free(out_buf);
+ return 0;
+}
+
+static int qcow2_flush(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ ret = qcow2_cache_flush(bs, s->l2_table_cache);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return bdrv_flush(bs->file);
+}
+
+static BlockDriverAIOCB *qcow2_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BDRVQcowState *s = bs->opaque;
+ int ret;
+
+ ret = qcow2_cache_flush(bs, s->l2_table_cache);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ ret = qcow2_cache_flush(bs, s->refcount_block_cache);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ return bdrv_aio_flush(bs->file, cb, opaque);
+}
+
+static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
+{
+ return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
+}
+
+static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+ BDRVQcowState *s = bs->opaque;
+ bdi->cluster_size = s->cluster_size;
+ bdi->vm_state_offset = qcow2_vm_state_offset(s);
+ return 0;
+}
+
+
+static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result)
+{
+ return qcow2_check_refcounts(bs, result);
+}
+
+#if 0
+static void dump_refcounts(BlockDriverState *bs)
+{
+ BDRVQcowState *s = bs->opaque;
+ int64_t nb_clusters, k, k1, size;
+ int refcount;
+
+ size = bdrv_getlength(bs->file);
+ nb_clusters = size_to_clusters(s, size);
+ for(k = 0; k < nb_clusters;) {
+ k1 = k;
+ refcount = get_refcount(bs, k);
+ k++;
+ while (k < nb_clusters && get_refcount(bs, k) == refcount)
+ k++;
+ printf("%" PRId64 ": refcount=%d nb=%" PRId64 "\n", k, refcount,
+ k - k1);
+ }
+}
+#endif
+
+static int qcow2_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int growable = bs->growable;
+ int ret;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
+ bs->growable = 1;
+ ret = bdrv_pwrite(bs, qcow2_vm_state_offset(s) + pos, buf, size);
+ bs->growable = growable;
+
+ return ret;
+}
+
+static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
+ int64_t pos, int size)
+{
+ BDRVQcowState *s = bs->opaque;
+ int growable = bs->growable;
+ int ret;
+
+ BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
+ bs->growable = 1;
+ ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size);
+ bs->growable = growable;
+
+ return ret;
+}
+
+static QEMUOptionParameter qcow2_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_BACKING_FILE,
+ .type = OPT_STRING,
+ .help = "File name of a base image"
+ },
+ {
+ .name = BLOCK_OPT_BACKING_FMT,
+ .type = OPT_STRING,
+ .help = "Image format of the base image"
+ },
+ {
+ .name = BLOCK_OPT_ENCRYPT,
+ .type = OPT_FLAG,
+ .help = "Encrypt the image"
+ },
+ {
+ .name = BLOCK_OPT_CLUSTER_SIZE,
+ .type = OPT_SIZE,
+ .help = "qcow2 cluster size"
+ },
+ {
+ .name = BLOCK_OPT_PREALLOC,
+ .type = OPT_STRING,
+ .help = "Preallocation mode (allowed values: off, metadata)"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_qcow2 = {
+ .format_name = "qcow2",
+ .instance_size = sizeof(BDRVQcowState),
+ .bdrv_probe = qcow2_probe,
+ .bdrv_open = qcow2_open,
+ .bdrv_close = qcow2_close,
+ .bdrv_create = qcow2_create,
+ .bdrv_flush = qcow2_flush,
+ .bdrv_is_allocated = qcow2_is_allocated,
+ .bdrv_set_key = qcow2_set_key,
+ .bdrv_make_empty = qcow2_make_empty,
+
+ .bdrv_aio_readv = qcow2_aio_readv,
+ .bdrv_aio_writev = qcow2_aio_writev,
+ .bdrv_aio_flush = qcow2_aio_flush,
+
+ .bdrv_discard = qcow2_discard,
+ .bdrv_truncate = qcow2_truncate,
+ .bdrv_write_compressed = qcow2_write_compressed,
+
+ .bdrv_snapshot_create = qcow2_snapshot_create,
+ .bdrv_snapshot_goto = qcow2_snapshot_goto,
+ .bdrv_snapshot_delete = qcow2_snapshot_delete,
+ .bdrv_snapshot_list = qcow2_snapshot_list,
+ .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
+ .bdrv_get_info = qcow2_get_info,
+
+ .bdrv_save_vmstate = qcow2_save_vmstate,
+ .bdrv_load_vmstate = qcow2_load_vmstate,
+
+ .bdrv_change_backing_file = qcow2_change_backing_file,
+
+ .create_options = qcow2_create_options,
+ .bdrv_check = qcow2_check,
+};
+
+static void bdrv_qcow2_init(void)
+{
+ bdrv_register(&bdrv_qcow2);
+}
+
+block_init(bdrv_qcow2_init);
diff --git a/block/qcow2.h b/block/qcow2.h
new file mode 100644
index 0000000..a019831
--- /dev/null
+++ b/block/qcow2.h
@@ -0,0 +1,242 @@
+/*
+ * Block driver for the QCOW version 2 format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLOCK_QCOW2_H
+#define BLOCK_QCOW2_H
+
+#include "aes.h"
+
+//#define DEBUG_ALLOC
+//#define DEBUG_ALLOC2
+//#define DEBUG_EXT
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW_VERSION 2
+
+#define QCOW_CRYPT_NONE 0
+#define QCOW_CRYPT_AES 1
+
+#define QCOW_MAX_CRYPT_CLUSTERS 32
+
+/* indicate that the refcount of the referenced cluster is exactly one. */
+#define QCOW_OFLAG_COPIED (1LL << 63)
+/* indicate that the cluster is compressed (they never have the copied flag) */
+#define QCOW_OFLAG_COMPRESSED (1LL << 62)
+
+#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
+
+#define MIN_CLUSTER_BITS 9
+#define MAX_CLUSTER_BITS 21
+
+#define L2_CACHE_SIZE 16
+
+/* Must be at least 4 to cover all cases of refcount table growth */
+#define REFCOUNT_CACHE_SIZE 4
+
+typedef struct QCowHeader {
+ uint32_t magic;
+ uint32_t version;
+ uint64_t backing_file_offset;
+ uint32_t backing_file_size;
+ uint32_t cluster_bits;
+ uint64_t size; /* in bytes */
+ uint32_t crypt_method;
+ uint32_t l1_size; /* XXX: save number of clusters instead ? */
+ uint64_t l1_table_offset;
+ uint64_t refcount_table_offset;
+ uint32_t refcount_table_clusters;
+ uint32_t nb_snapshots;
+ uint64_t snapshots_offset;
+} QCowHeader;
+
+typedef struct QCowSnapshot {
+ uint64_t l1_table_offset;
+ uint32_t l1_size;
+ char *id_str;
+ char *name;
+ uint32_t vm_state_size;
+ uint32_t date_sec;
+ uint32_t date_nsec;
+ uint64_t vm_clock_nsec;
+} QCowSnapshot;
+
+struct Qcow2Cache;
+typedef struct Qcow2Cache Qcow2Cache;
+
+typedef struct BDRVQcowState {
+ int cluster_bits;
+ int cluster_size;
+ int cluster_sectors;
+ int l2_bits;
+ int l2_size;
+ int l1_size;
+ int l1_vm_state_index;
+ int csize_shift;
+ int csize_mask;
+ uint64_t cluster_offset_mask;
+ uint64_t l1_table_offset;
+ uint64_t *l1_table;
+
+ Qcow2Cache* l2_table_cache;
+ Qcow2Cache* refcount_block_cache;
+
+ uint8_t *cluster_cache;
+ uint8_t *cluster_data;
+ uint64_t cluster_cache_offset;
+ QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs;
+
+ uint64_t *refcount_table;
+ uint64_t refcount_table_offset;
+ uint32_t refcount_table_size;
+ int64_t free_cluster_index;
+ int64_t free_byte_offset;
+
+ uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+ uint32_t crypt_method_header;
+ AES_KEY aes_encrypt_key;
+ AES_KEY aes_decrypt_key;
+ uint64_t snapshots_offset;
+ int snapshots_size;
+ int nb_snapshots;
+ QCowSnapshot *snapshots;
+} BDRVQcowState;
+
+/* XXX: use std qcow open function ? */
+typedef struct QCowCreateState {
+ int cluster_size;
+ int cluster_bits;
+ uint16_t *refcount_block;
+ uint64_t *refcount_table;
+ int64_t l1_table_offset;
+ int64_t refcount_table_offset;
+ int64_t refcount_block_offset;
+} QCowCreateState;
+
+struct QCowAIOCB;
+
+/* XXX This could be private for qcow2-cluster.c */
+typedef struct QCowL2Meta
+{
+ uint64_t offset;
+ uint64_t cluster_offset;
+ int n_start;
+ int nb_available;
+ int nb_clusters;
+ struct QCowL2Meta *depends_on;
+ QLIST_HEAD(QCowAioDependencies, QCowAIOCB) dependent_requests;
+
+ QLIST_ENTRY(QCowL2Meta) next_in_flight;
+} QCowL2Meta;
+
+static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
+{
+ return (size + (s->cluster_size - 1)) >> s->cluster_bits;
+}
+
+static inline int size_to_l1(BDRVQcowState *s, int64_t size)
+{
+ int shift = s->cluster_bits + s->l2_bits;
+ return (size + (1ULL << shift) - 1) >> shift;
+}
+
+static inline int64_t align_offset(int64_t offset, int n)
+{
+ offset = (offset + n - 1) & ~(n - 1);
+ return offset;
+}
+
+
+// FIXME Need qcow2_ prefix to global functions
+
+/* qcow2.c functions */
+int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t sector_num, int nb_sectors);
+
+/* qcow2-refcount.c functions */
+int qcow2_refcount_init(BlockDriverState *bs);
+void qcow2_refcount_close(BlockDriverState *bs);
+
+int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
+int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
+void qcow2_free_clusters(BlockDriverState *bs,
+ int64_t offset, int64_t size);
+void qcow2_free_any_clusters(BlockDriverState *bs,
+ uint64_t cluster_offset, int nb_clusters);
+
+void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset,
+ int64_t size);
+int qcow2_update_snapshot_refcount(BlockDriverState *bs,
+ int64_t l1_table_offset, int l1_size, int addend);
+
+int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
+
+/* qcow2-cluster.c functions */
+int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);
+void qcow2_l2_cache_reset(BlockDriverState *bs);
+int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
+void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+ uint8_t *out_buf, const uint8_t *in_buf,
+ int nb_sectors, int enc,
+ const AES_KEY *key);
+
+int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
+ int *num, uint64_t *cluster_offset);
+int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
+ int n_start, int n_end, int *num, QCowL2Meta *m);
+uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
+ uint64_t offset,
+ int compressed_size);
+
+int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
+int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
+ int nb_sectors);
+
+/* qcow2-snapshot.c functions */
+int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
+int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
+int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
+int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
+int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
+
+void qcow2_free_snapshots(BlockDriverState *bs);
+int qcow2_read_snapshots(BlockDriverState *bs);
+
+/* qcow2-cache.c functions */
+Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
+ bool writethrough);
+int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
+
+void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
+int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
+int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
+ Qcow2Cache *dependency);
+void qcow2_cache_depends_on_flush(Qcow2Cache *c);
+
+int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table);
+int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
+ void **table);
+int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table);
+
+#endif
diff --git a/block/qed-check.c b/block/qed-check.c
new file mode 100644
index 0000000..474c847
--- /dev/null
+++ b/block/qed-check.c
@@ -0,0 +1,210 @@
+/*
+ * QEMU Enhanced Disk Format Consistency Check
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qed.h"
+
+typedef struct {
+ BDRVQEDState *s;
+ BdrvCheckResult *result;
+ bool fix; /* whether to fix invalid offsets */
+
+ uint64_t nclusters;
+ uint32_t *used_clusters; /* referenced cluster bitmap */
+
+ QEDRequest request;
+} QEDCheck;
+
+static bool qed_test_bit(uint32_t *bitmap, uint64_t n) {
+ return !!(bitmap[n / 32] & (1 << (n % 32)));
+}
+
+static void qed_set_bit(uint32_t *bitmap, uint64_t n) {
+ bitmap[n / 32] |= 1 << (n % 32);
+}
+
+/**
+ * Set bitmap bits for clusters
+ *
+ * @check: Check structure
+ * @offset: Starting offset in bytes
+ * @n: Number of clusters
+ */
+static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset,
+ unsigned int n)
+{
+ uint64_t cluster = qed_bytes_to_clusters(check->s, offset);
+ unsigned int corruptions = 0;
+
+ while (n-- != 0) {
+ /* Clusters should only be referenced once */
+ if (qed_test_bit(check->used_clusters, cluster)) {
+ corruptions++;
+ }
+
+ qed_set_bit(check->used_clusters, cluster);
+ cluster++;
+ }
+
+ check->result->corruptions += corruptions;
+ return corruptions == 0;
+}
+
+/**
+ * Check an L2 table
+ *
+ * @ret: Number of invalid cluster offsets
+ */
+static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
+{
+ BDRVQEDState *s = check->s;
+ unsigned int i, num_invalid = 0;
+
+ for (i = 0; i < s->table_nelems; i++) {
+ uint64_t offset = table->offsets[i];
+
+ if (!offset) {
+ continue;
+ }
+
+ /* Detect invalid cluster offset */
+ if (!qed_check_cluster_offset(s, offset)) {
+ if (check->fix) {
+ table->offsets[i] = 0;
+ } else {
+ check->result->corruptions++;
+ }
+
+ num_invalid++;
+ continue;
+ }
+
+ qed_set_used_clusters(check, offset, 1);
+ }
+
+ return num_invalid;
+}
+
+/**
+ * Descend tables and check each cluster is referenced once only
+ */
+static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
+{
+ BDRVQEDState *s = check->s;
+ unsigned int i, num_invalid_l1 = 0;
+ int ret, last_error = 0;
+
+ /* Mark L1 table clusters used */
+ qed_set_used_clusters(check, s->header.l1_table_offset,
+ s->header.table_size);
+
+ for (i = 0; i < s->table_nelems; i++) {
+ unsigned int num_invalid_l2;
+ uint64_t offset = table->offsets[i];
+
+ if (!offset) {
+ continue;
+ }
+
+ /* Detect invalid L2 offset */
+ if (!qed_check_table_offset(s, offset)) {
+ /* Clear invalid offset */
+ if (check->fix) {
+ table->offsets[i] = 0;
+ } else {
+ check->result->corruptions++;
+ }
+
+ num_invalid_l1++;
+ continue;
+ }
+
+ if (!qed_set_used_clusters(check, offset, s->header.table_size)) {
+ continue; /* skip an invalid table */
+ }
+
+ ret = qed_read_l2_table_sync(s, &check->request, offset);
+ if (ret) {
+ check->result->check_errors++;
+ last_error = ret;
+ continue;
+ }
+
+ num_invalid_l2 = qed_check_l2_table(check,
+ check->request.l2_table->table);
+
+ /* Write out fixed L2 table */
+ if (num_invalid_l2 > 0 && check->fix) {
+ ret = qed_write_l2_table_sync(s, &check->request, 0,
+ s->table_nelems, false);
+ if (ret) {
+ check->result->check_errors++;
+ last_error = ret;
+ continue;
+ }
+ }
+ }
+
+ /* Drop reference to final table */
+ qed_unref_l2_cache_entry(check->request.l2_table);
+ check->request.l2_table = NULL;
+
+ /* Write out fixed L1 table */
+ if (num_invalid_l1 > 0 && check->fix) {
+ ret = qed_write_l1_table_sync(s, 0, s->table_nelems);
+ if (ret) {
+ check->result->check_errors++;
+ last_error = ret;
+ }
+ }
+
+ return last_error;
+}
+
+/**
+ * Check for unreferenced (leaked) clusters
+ */
+static void qed_check_for_leaks(QEDCheck *check)
+{
+ BDRVQEDState *s = check->s;
+ uint64_t i;
+
+ for (i = s->header.header_size; i < check->nclusters; i++) {
+ if (!qed_test_bit(check->used_clusters, i)) {
+ check->result->leaks++;
+ }
+ }
+}
+
+int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
+{
+ QEDCheck check = {
+ .s = s,
+ .result = result,
+ .nclusters = qed_bytes_to_clusters(s, s->file_size),
+ .request = { .l2_table = NULL },
+ .fix = fix,
+ };
+ int ret;
+
+ check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) *
+ sizeof(check.used_clusters[0]));
+
+ ret = qed_check_l1_table(&check, s->l1_table);
+ if (ret == 0) {
+ /* Only check for leaks if entire image was scanned successfully */
+ qed_check_for_leaks(&check);
+ }
+
+ qemu_free(check.used_clusters);
+ return ret;
+}
diff --git a/block/qed-cluster.c b/block/qed-cluster.c
new file mode 100644
index 0000000..0ec864b
--- /dev/null
+++ b/block/qed-cluster.c
@@ -0,0 +1,154 @@
+/*
+ * QEMU Enhanced Disk Format Cluster functions
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qed.h"
+
+/**
+ * Count the number of contiguous data clusters
+ *
+ * @s: QED state
+ * @table: L2 table
+ * @index: First cluster index
+ * @n: Maximum number of clusters
+ * @offset: Set to first cluster offset
+ *
+ * This function scans tables for contiguous allocated or free clusters.
+ */
+static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
+ QEDTable *table,
+ unsigned int index,
+ unsigned int n,
+ uint64_t *offset)
+{
+ unsigned int end = MIN(index + n, s->table_nelems);
+ uint64_t last = table->offsets[index];
+ unsigned int i;
+
+ *offset = last;
+
+ for (i = index + 1; i < end; i++) {
+ if (last == 0) {
+ /* Counting free clusters */
+ if (table->offsets[i] != 0) {
+ break;
+ }
+ } else {
+ /* Counting allocated clusters */
+ if (table->offsets[i] != last + s->header.cluster_size) {
+ break;
+ }
+ last = table->offsets[i];
+ }
+ }
+ return i - index;
+}
+
+typedef struct {
+ BDRVQEDState *s;
+ uint64_t pos;
+ size_t len;
+
+ QEDRequest *request;
+
+ /* User callback */
+ QEDFindClusterFunc *cb;
+ void *opaque;
+} QEDFindClusterCB;
+
+static void qed_find_cluster_cb(void *opaque, int ret)
+{
+ QEDFindClusterCB *find_cluster_cb = opaque;
+ BDRVQEDState *s = find_cluster_cb->s;
+ QEDRequest *request = find_cluster_cb->request;
+ uint64_t offset = 0;
+ size_t len = 0;
+ unsigned int index;
+ unsigned int n;
+
+ if (ret) {
+ goto out;
+ }
+
+ index = qed_l2_index(s, find_cluster_cb->pos);
+ n = qed_bytes_to_clusters(s,
+ qed_offset_into_cluster(s, find_cluster_cb->pos) +
+ find_cluster_cb->len);
+ n = qed_count_contiguous_clusters(s, request->l2_table->table,
+ index, n, &offset);
+
+ ret = offset ? QED_CLUSTER_FOUND : QED_CLUSTER_L2;
+ len = MIN(find_cluster_cb->len, n * s->header.cluster_size -
+ qed_offset_into_cluster(s, find_cluster_cb->pos));
+
+ if (offset && !qed_check_cluster_offset(s, offset)) {
+ ret = -EINVAL;
+ }
+
+out:
+ find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len);
+ qemu_free(find_cluster_cb);
+}
+
+/**
+ * Find the offset of a data cluster
+ *
+ * @s: QED state
+ * @request: L2 cache entry
+ * @pos: Byte position in device
+ * @len: Number of bytes
+ * @cb: Completion function
+ * @opaque: User data for completion function
+ *
+ * This function translates a position in the block device to an offset in the
+ * image file. It invokes the cb completion callback to report back the
+ * translated offset or unallocated range in the image file.
+ *
+ * If the L2 table exists, request->l2_table points to the L2 table cache entry
+ * and the caller must free the reference when they are finished. The cache
+ * entry is exposed in this way to avoid callers having to read the L2 table
+ * again later during request processing. If request->l2_table is non-NULL it
+ * will be unreferenced before taking on the new cache entry.
+ */
+void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
+ size_t len, QEDFindClusterFunc *cb, void *opaque)
+{
+ QEDFindClusterCB *find_cluster_cb;
+ uint64_t l2_offset;
+
+ /* Limit length to L2 boundary. Requests are broken up at the L2 boundary
+ * so that a request acts on one L2 table at a time.
+ */
+ len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos);
+
+ l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)];
+ if (!l2_offset) {
+ cb(opaque, QED_CLUSTER_L1, 0, len);
+ return;
+ }
+ if (!qed_check_table_offset(s, l2_offset)) {
+ cb(opaque, -EINVAL, 0, 0);
+ return;
+ }
+
+ find_cluster_cb = qemu_malloc(sizeof(*find_cluster_cb));
+ find_cluster_cb->s = s;
+ find_cluster_cb->pos = pos;
+ find_cluster_cb->len = len;
+ find_cluster_cb->cb = cb;
+ find_cluster_cb->opaque = opaque;
+ find_cluster_cb->request = request;
+
+ qed_read_l2_table(s, request, l2_offset,
+ qed_find_cluster_cb, find_cluster_cb);
+}
diff --git a/block/qed-gencb.c b/block/qed-gencb.c
new file mode 100644
index 0000000..1513dc6
--- /dev/null
+++ b/block/qed-gencb.c
@@ -0,0 +1,32 @@
+/*
+ * QEMU Enhanced Disk Format
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qed.h"
+
+void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque)
+{
+ GenericCB *gencb = qemu_malloc(len);
+ gencb->cb = cb;
+ gencb->opaque = opaque;
+ return gencb;
+}
+
+void gencb_complete(void *opaque, int ret)
+{
+ GenericCB *gencb = opaque;
+ BlockDriverCompletionFunc *cb = gencb->cb;
+ void *user_opaque = gencb->opaque;
+
+ qemu_free(gencb);
+ cb(user_opaque, ret);
+}
diff --git a/block/qed-l2-cache.c b/block/qed-l2-cache.c
new file mode 100644
index 0000000..57518a4
--- /dev/null
+++ b/block/qed-l2-cache.c
@@ -0,0 +1,173 @@
+/*
+ * QEMU Enhanced Disk Format L2 Cache
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+/*
+ * L2 table cache usage is as follows:
+ *
+ * An open image has one L2 table cache that is used to avoid accessing the
+ * image file for recently referenced L2 tables.
+ *
+ * Cluster offset lookup translates the logical offset within the block device
+ * to a cluster offset within the image file. This is done by indexing into
+ * the L1 and L2 tables which store cluster offsets. It is here where the L2
+ * table cache serves up recently referenced L2 tables.
+ *
+ * If there is a cache miss, that L2 table is read from the image file and
+ * committed to the cache. Subsequent accesses to that L2 table will be served
+ * from the cache until the table is evicted from the cache.
+ *
+ * L2 tables are also committed to the cache when new L2 tables are allocated
+ * in the image file. Since the L2 table cache is write-through, the new L2
+ * table is first written out to the image file and then committed to the
+ * cache.
+ *
+ * Multiple I/O requests may be using an L2 table cache entry at any given
+ * time. That means an entry may be in use across several requests and
+ * reference counting is needed to free the entry at the correct time. In
+ * particular, an entry evicted from the cache will only be freed once all
+ * references are dropped.
+ *
+ * An in-flight I/O request will hold a reference to a L2 table cache entry for
+ * the period during which it needs to access the L2 table. This includes
+ * cluster offset lookup, L2 table allocation, and L2 table update when a new
+ * data cluster has been allocated.
+ *
+ * An interesting case occurs when two requests need to access an L2 table that
+ * is not in the cache. Since the operation to read the table from the image
+ * file takes some time to complete, both requests may see a cache miss and
+ * start reading the L2 table from the image file. The first to finish will
+ * commit its L2 table into the cache. When the second tries to commit its
+ * table will be deleted in favor of the existing cache entry.
+ */
+
+#include "trace.h"
+#include "qed.h"
+
+/* Each L2 holds 2GB so this let's us fully cache a 100GB disk */
+#define MAX_L2_CACHE_SIZE 50
+
+/**
+ * Initialize the L2 cache
+ */
+void qed_init_l2_cache(L2TableCache *l2_cache)
+{
+ QTAILQ_INIT(&l2_cache->entries);
+ l2_cache->n_entries = 0;
+}
+
+/**
+ * Free the L2 cache
+ */
+void qed_free_l2_cache(L2TableCache *l2_cache)
+{
+ CachedL2Table *entry, *next_entry;
+
+ QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) {
+ qemu_vfree(entry->table);
+ qemu_free(entry);
+ }
+}
+
+/**
+ * Allocate an uninitialized entry from the cache
+ *
+ * The returned entry has a reference count of 1 and is owned by the caller.
+ * The caller must allocate the actual table field for this entry and it must
+ * be freeable using qemu_vfree().
+ */
+CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache)
+{
+ CachedL2Table *entry;
+
+ entry = qemu_mallocz(sizeof(*entry));
+ entry->ref++;
+
+ trace_qed_alloc_l2_cache_entry(l2_cache, entry);
+
+ return entry;
+}
+
+/**
+ * Decrease an entry's reference count and free if necessary when the reference
+ * count drops to zero.
+ */
+void qed_unref_l2_cache_entry(CachedL2Table *entry)
+{
+ if (!entry) {
+ return;
+ }
+
+ entry->ref--;
+ trace_qed_unref_l2_cache_entry(entry, entry->ref);
+ if (entry->ref == 0) {
+ qemu_vfree(entry->table);
+ qemu_free(entry);
+ }
+}
+
+/**
+ * Find an entry in the L2 cache. This may return NULL and it's up to the
+ * caller to satisfy the cache miss.
+ *
+ * For a cached entry, this function increases the reference count and returns
+ * the entry.
+ */
+CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset)
+{
+ CachedL2Table *entry;
+
+ QTAILQ_FOREACH(entry, &l2_cache->entries, node) {
+ if (entry->offset == offset) {
+ trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref);
+ entry->ref++;
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Commit an L2 cache entry into the cache. This is meant to be used as part of
+ * the process to satisfy a cache miss. A caller would allocate an entry which
+ * is not actually in the L2 cache and then once the entry was valid and
+ * present on disk, the entry can be committed into the cache.
+ *
+ * Since the cache is write-through, it's important that this function is not
+ * called until the entry is present on disk and the L1 has been updated to
+ * point to the entry.
+ *
+ * N.B. This function steals a reference to the l2_table from the caller so the
+ * caller must obtain a new reference by issuing a call to
+ * qed_find_l2_cache_entry().
+ */
+void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table)
+{
+ CachedL2Table *entry;
+
+ entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset);
+ if (entry) {
+ qed_unref_l2_cache_entry(entry);
+ qed_unref_l2_cache_entry(l2_table);
+ return;
+ }
+
+ if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) {
+ entry = QTAILQ_FIRST(&l2_cache->entries);
+ QTAILQ_REMOVE(&l2_cache->entries, entry, node);
+ l2_cache->n_entries--;
+ qed_unref_l2_cache_entry(entry);
+ }
+
+ l2_cache->n_entries++;
+ QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node);
+}
diff --git a/block/qed-table.c b/block/qed-table.c
new file mode 100644
index 0000000..d38c673
--- /dev/null
+++ b/block/qed-table.c
@@ -0,0 +1,319 @@
+/*
+ * QEMU Enhanced Disk Format Table I/O
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "trace.h"
+#include "qemu_socket.h" /* for EINPROGRESS on Windows */
+#include "qed.h"
+
+typedef struct {
+ GenericCB gencb;
+ BDRVQEDState *s;
+ QEDTable *table;
+
+ struct iovec iov;
+ QEMUIOVector qiov;
+} QEDReadTableCB;
+
+static void qed_read_table_cb(void *opaque, int ret)
+{
+ QEDReadTableCB *read_table_cb = opaque;
+ QEDTable *table = read_table_cb->table;
+ int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t);
+ int i;
+
+ /* Handle I/O error */
+ if (ret) {
+ goto out;
+ }
+
+ /* Byteswap offsets */
+ for (i = 0; i < noffsets; i++) {
+ table->offsets[i] = le64_to_cpu(table->offsets[i]);
+ }
+
+out:
+ /* Completion */
+ trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret);
+ gencb_complete(&read_table_cb->gencb, ret);
+}
+
+static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb),
+ cb, opaque);
+ QEMUIOVector *qiov = &read_table_cb->qiov;
+ BlockDriverAIOCB *aiocb;
+
+ trace_qed_read_table(s, offset, table);
+
+ read_table_cb->s = s;
+ read_table_cb->table = table;
+ read_table_cb->iov.iov_base = table->offsets,
+ read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size,
+
+ qemu_iovec_init_external(qiov, &read_table_cb->iov, 1);
+ aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov,
+ read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE,
+ qed_read_table_cb, read_table_cb);
+ if (!aiocb) {
+ qed_read_table_cb(read_table_cb, -EIO);
+ }
+}
+
+typedef struct {
+ GenericCB gencb;
+ BDRVQEDState *s;
+ QEDTable *orig_table;
+ QEDTable *table;
+ bool flush; /* flush after write? */
+
+ struct iovec iov;
+ QEMUIOVector qiov;
+} QEDWriteTableCB;
+
+static void qed_write_table_cb(void *opaque, int ret)
+{
+ QEDWriteTableCB *write_table_cb = opaque;
+
+ trace_qed_write_table_cb(write_table_cb->s,
+ write_table_cb->orig_table,
+ write_table_cb->flush,
+ ret);
+
+ if (ret) {
+ goto out;
+ }
+
+ if (write_table_cb->flush) {
+ /* We still need to flush first */
+ write_table_cb->flush = false;
+ bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb,
+ write_table_cb);
+ return;
+ }
+
+out:
+ qemu_vfree(write_table_cb->table);
+ gencb_complete(&write_table_cb->gencb, ret);
+ return;
+}
+
+/**
+ * Write out an updated part or all of a table
+ *
+ * @s: QED state
+ * @offset: Offset of table in image file, in bytes
+ * @table: Table
+ * @index: Index of first element
+ * @n: Number of elements
+ * @flush: Whether or not to sync to disk
+ * @cb: Completion function
+ * @opaque: Argument for completion function
+ */
+static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
+ unsigned int index, unsigned int n, bool flush,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ QEDWriteTableCB *write_table_cb;
+ BlockDriverAIOCB *aiocb;
+ unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
+ unsigned int start, end, i;
+ size_t len_bytes;
+
+ trace_qed_write_table(s, offset, table, index, n);
+
+ /* Calculate indices of the first and one after last elements */
+ start = index & ~sector_mask;
+ end = (index + n + sector_mask) & ~sector_mask;
+
+ len_bytes = (end - start) * sizeof(uint64_t);
+
+ write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque);
+ write_table_cb->s = s;
+ write_table_cb->orig_table = table;
+ write_table_cb->flush = flush;
+ write_table_cb->table = qemu_blockalign(s->bs, len_bytes);
+ write_table_cb->iov.iov_base = write_table_cb->table->offsets;
+ write_table_cb->iov.iov_len = len_bytes;
+ qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1);
+
+ /* Byteswap table */
+ for (i = start; i < end; i++) {
+ uint64_t le_offset = cpu_to_le64(table->offsets[i]);
+ write_table_cb->table->offsets[i - start] = le_offset;
+ }
+
+ /* Adjust for offset into table */
+ offset += start * sizeof(uint64_t);
+
+ aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
+ &write_table_cb->qiov,
+ write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE,
+ qed_write_table_cb, write_table_cb);
+ if (!aiocb) {
+ qed_write_table_cb(write_table_cb, -EIO);
+ }
+}
+
+/**
+ * Propagate return value from async callback
+ */
+static void qed_sync_cb(void *opaque, int ret)
+{
+ *(int *)opaque = ret;
+}
+
+int qed_read_l1_table_sync(BDRVQEDState *s)
+{
+ int ret = -EINPROGRESS;
+
+ async_context_push();
+
+ qed_read_table(s, s->header.l1_table_offset,
+ s->l1_table, qed_sync_cb, &ret);
+ while (ret == -EINPROGRESS) {
+ qemu_aio_wait();
+ }
+
+ async_context_pop();
+
+ return ret;
+}
+
+void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
+ qed_write_table(s, s->header.l1_table_offset,
+ s->l1_table, index, n, false, cb, opaque);
+}
+
+int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
+ unsigned int n)
+{
+ int ret = -EINPROGRESS;
+
+ async_context_push();
+
+ qed_write_l1_table(s, index, n, qed_sync_cb, &ret);
+ while (ret == -EINPROGRESS) {
+ qemu_aio_wait();
+ }
+
+ async_context_pop();
+
+ return ret;
+}
+
+typedef struct {
+ GenericCB gencb;
+ BDRVQEDState *s;
+ uint64_t l2_offset;
+ QEDRequest *request;
+} QEDReadL2TableCB;
+
+static void qed_read_l2_table_cb(void *opaque, int ret)
+{
+ QEDReadL2TableCB *read_l2_table_cb = opaque;
+ QEDRequest *request = read_l2_table_cb->request;
+ BDRVQEDState *s = read_l2_table_cb->s;
+ CachedL2Table *l2_table = request->l2_table;
+
+ if (ret) {
+ /* can't trust loaded L2 table anymore */
+ qed_unref_l2_cache_entry(l2_table);
+ request->l2_table = NULL;
+ } else {
+ l2_table->offset = read_l2_table_cb->l2_offset;
+
+ qed_commit_l2_cache_entry(&s->l2_cache, l2_table);
+
+ /* This is guaranteed to succeed because we just committed the entry
+ * to the cache.
+ */
+ request->l2_table = qed_find_l2_cache_entry(&s->l2_cache,
+ l2_table->offset);
+ assert(request->l2_table != NULL);
+ }
+
+ gencb_complete(&read_l2_table_cb->gencb, ret);
+}
+
+void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ QEDReadL2TableCB *read_l2_table_cb;
+
+ qed_unref_l2_cache_entry(request->l2_table);
+
+ /* Check for cached L2 entry */
+ request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset);
+ if (request->l2_table) {
+ cb(opaque, 0);
+ return;
+ }
+
+ request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache);
+ request->l2_table->table = qed_alloc_table(s);
+
+ read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque);
+ read_l2_table_cb->s = s;
+ read_l2_table_cb->l2_offset = offset;
+ read_l2_table_cb->request = request;
+
+ BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD);
+ qed_read_table(s, offset, request->l2_table->table,
+ qed_read_l2_table_cb, read_l2_table_cb);
+}
+
+int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
+{
+ int ret = -EINPROGRESS;
+
+ async_context_push();
+
+ qed_read_l2_table(s, request, offset, qed_sync_cb, &ret);
+ while (ret == -EINPROGRESS) {
+ qemu_aio_wait();
+ }
+
+ async_context_pop();
+ return ret;
+}
+
+void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
+ unsigned int index, unsigned int n, bool flush,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
+ qed_write_table(s, request->l2_table->offset,
+ request->l2_table->table, index, n, flush, cb, opaque);
+}
+
+int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
+ unsigned int index, unsigned int n, bool flush)
+{
+ int ret = -EINPROGRESS;
+
+ async_context_push();
+
+ qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret);
+ while (ret == -EINPROGRESS) {
+ qemu_aio_wait();
+ }
+
+ async_context_pop();
+ return ret;
+}
diff --git a/block/qed.c b/block/qed.c
new file mode 100644
index 0000000..75ae244
--- /dev/null
+++ b/block/qed.c
@@ -0,0 +1,1372 @@
+/*
+ * QEMU Enhanced Disk Format
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "trace.h"
+#include "qed.h"
+#include "qerror.h"
+
+static void qed_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ QEDAIOCB *acb = (QEDAIOCB *)blockacb;
+ bool finished = false;
+
+ /* Wait for the request to finish */
+ acb->finished = &finished;
+ while (!finished) {
+ qemu_aio_wait();
+ }
+}
+
+static AIOPool qed_aio_pool = {
+ .aiocb_size = sizeof(QEDAIOCB),
+ .cancel = qed_aio_cancel,
+};
+
+static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
+ const char *filename)
+{
+ const QEDHeader *header = (const QEDHeader *)buf;
+
+ if (buf_size < sizeof(*header)) {
+ return 0;
+ }
+ if (le32_to_cpu(header->magic) != QED_MAGIC) {
+ return 0;
+ }
+ return 100;
+}
+
+/**
+ * Check whether an image format is raw
+ *
+ * @fmt: Backing file format, may be NULL
+ */
+static bool qed_fmt_is_raw(const char *fmt)
+{
+ return fmt && strcmp(fmt, "raw") == 0;
+}
+
+static void qed_header_le_to_cpu(const QEDHeader *le, QEDHeader *cpu)
+{
+ cpu->magic = le32_to_cpu(le->magic);
+ cpu->cluster_size = le32_to_cpu(le->cluster_size);
+ cpu->table_size = le32_to_cpu(le->table_size);
+ cpu->header_size = le32_to_cpu(le->header_size);
+ cpu->features = le64_to_cpu(le->features);
+ cpu->compat_features = le64_to_cpu(le->compat_features);
+ cpu->autoclear_features = le64_to_cpu(le->autoclear_features);
+ cpu->l1_table_offset = le64_to_cpu(le->l1_table_offset);
+ cpu->image_size = le64_to_cpu(le->image_size);
+ cpu->backing_filename_offset = le32_to_cpu(le->backing_filename_offset);
+ cpu->backing_filename_size = le32_to_cpu(le->backing_filename_size);
+}
+
+static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le)
+{
+ le->magic = cpu_to_le32(cpu->magic);
+ le->cluster_size = cpu_to_le32(cpu->cluster_size);
+ le->table_size = cpu_to_le32(cpu->table_size);
+ le->header_size = cpu_to_le32(cpu->header_size);
+ le->features = cpu_to_le64(cpu->features);
+ le->compat_features = cpu_to_le64(cpu->compat_features);
+ le->autoclear_features = cpu_to_le64(cpu->autoclear_features);
+ le->l1_table_offset = cpu_to_le64(cpu->l1_table_offset);
+ le->image_size = cpu_to_le64(cpu->image_size);
+ le->backing_filename_offset = cpu_to_le32(cpu->backing_filename_offset);
+ le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size);
+}
+
+static int qed_write_header_sync(BDRVQEDState *s)
+{
+ QEDHeader le;
+ int ret;
+
+ qed_header_cpu_to_le(&s->header, &le);
+ ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le));
+ if (ret != sizeof(le)) {
+ return ret;
+ }
+ return 0;
+}
+
+typedef struct {
+ GenericCB gencb;
+ BDRVQEDState *s;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ int nsectors;
+ uint8_t *buf;
+} QEDWriteHeaderCB;
+
+static void qed_write_header_cb(void *opaque, int ret)
+{
+ QEDWriteHeaderCB *write_header_cb = opaque;
+
+ qemu_vfree(write_header_cb->buf);
+ gencb_complete(write_header_cb, ret);
+}
+
+static void qed_write_header_read_cb(void *opaque, int ret)
+{
+ QEDWriteHeaderCB *write_header_cb = opaque;
+ BDRVQEDState *s = write_header_cb->s;
+ BlockDriverAIOCB *acb;
+
+ if (ret) {
+ qed_write_header_cb(write_header_cb, ret);
+ return;
+ }
+
+ /* Update header */
+ qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf);
+
+ acb = bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov,
+ write_header_cb->nsectors, qed_write_header_cb,
+ write_header_cb);
+ if (!acb) {
+ qed_write_header_cb(write_header_cb, -EIO);
+ }
+}
+
+/**
+ * Update header in-place (does not rewrite backing filename or other strings)
+ *
+ * This function only updates known header fields in-place and does not affect
+ * extra data after the QED header.
+ */
+static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
+ void *opaque)
+{
+ /* We must write full sectors for O_DIRECT but cannot necessarily generate
+ * the data following the header if an unrecognized compat feature is
+ * active. Therefore, first read the sectors containing the header, update
+ * them, and write back.
+ */
+
+ BlockDriverAIOCB *acb;
+ int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) /
+ BDRV_SECTOR_SIZE;
+ size_t len = nsectors * BDRV_SECTOR_SIZE;
+ QEDWriteHeaderCB *write_header_cb = gencb_alloc(sizeof(*write_header_cb),
+ cb, opaque);
+
+ write_header_cb->s = s;
+ write_header_cb->nsectors = nsectors;
+ write_header_cb->buf = qemu_blockalign(s->bs, len);
+ write_header_cb->iov.iov_base = write_header_cb->buf;
+ write_header_cb->iov.iov_len = len;
+ qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1);
+
+ acb = bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors,
+ qed_write_header_read_cb, write_header_cb);
+ if (!acb) {
+ qed_write_header_cb(write_header_cb, -EIO);
+ }
+}
+
+static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size)
+{
+ uint64_t table_entries;
+ uint64_t l2_size;
+
+ table_entries = (table_size * cluster_size) / sizeof(uint64_t);
+ l2_size = table_entries * cluster_size;
+
+ return l2_size * table_entries;
+}
+
+static bool qed_is_cluster_size_valid(uint32_t cluster_size)
+{
+ if (cluster_size < QED_MIN_CLUSTER_SIZE ||
+ cluster_size > QED_MAX_CLUSTER_SIZE) {
+ return false;
+ }
+ if (cluster_size & (cluster_size - 1)) {
+ return false; /* not power of 2 */
+ }
+ return true;
+}
+
+static bool qed_is_table_size_valid(uint32_t table_size)
+{
+ if (table_size < QED_MIN_TABLE_SIZE ||
+ table_size > QED_MAX_TABLE_SIZE) {
+ return false;
+ }
+ if (table_size & (table_size - 1)) {
+ return false; /* not power of 2 */
+ }
+ return true;
+}
+
+static bool qed_is_image_size_valid(uint64_t image_size, uint32_t cluster_size,
+ uint32_t table_size)
+{
+ if (image_size % BDRV_SECTOR_SIZE != 0) {
+ return false; /* not multiple of sector size */
+ }
+ if (image_size > qed_max_image_size(cluster_size, table_size)) {
+ return false; /* image is too large */
+ }
+ return true;
+}
+
+/**
+ * Read a string of known length from the image file
+ *
+ * @file: Image file
+ * @offset: File offset to start of string, in bytes
+ * @n: String length in bytes
+ * @buf: Destination buffer
+ * @buflen: Destination buffer length in bytes
+ * @ret: 0 on success, -errno on failure
+ *
+ * The string is NUL-terminated.
+ */
+static int qed_read_string(BlockDriverState *file, uint64_t offset, size_t n,
+ char *buf, size_t buflen)
+{
+ int ret;
+ if (n >= buflen) {
+ return -EINVAL;
+ }
+ ret = bdrv_pread(file, offset, buf, n);
+ if (ret < 0) {
+ return ret;
+ }
+ buf[n] = '\0';
+ return 0;
+}
+
+/**
+ * Allocate new clusters
+ *
+ * @s: QED state
+ * @n: Number of contiguous clusters to allocate
+ * @ret: Offset of first allocated cluster
+ *
+ * This function only produces the offset where the new clusters should be
+ * written. It updates BDRVQEDState but does not make any changes to the image
+ * file.
+ */
+static uint64_t qed_alloc_clusters(BDRVQEDState *s, unsigned int n)
+{
+ uint64_t offset = s->file_size;
+ s->file_size += n * s->header.cluster_size;
+ return offset;
+}
+
+QEDTable *qed_alloc_table(BDRVQEDState *s)
+{
+ /* Honor O_DIRECT memory alignment requirements */
+ return qemu_blockalign(s->bs,
+ s->header.cluster_size * s->header.table_size);
+}
+
+/**
+ * Allocate a new zeroed L2 table
+ */
+static CachedL2Table *qed_new_l2_table(BDRVQEDState *s)
+{
+ CachedL2Table *l2_table = qed_alloc_l2_cache_entry(&s->l2_cache);
+
+ l2_table->table = qed_alloc_table(s);
+ l2_table->offset = qed_alloc_clusters(s, s->header.table_size);
+
+ memset(l2_table->table->offsets, 0,
+ s->header.cluster_size * s->header.table_size);
+ return l2_table;
+}
+
+static void qed_aio_next_io(void *opaque, int ret);
+
+static int bdrv_qed_open(BlockDriverState *bs, int flags)
+{
+ BDRVQEDState *s = bs->opaque;
+ QEDHeader le_header;
+ int64_t file_size;
+ int ret;
+
+ s->bs = bs;
+ QSIMPLEQ_INIT(&s->allocating_write_reqs);
+
+ ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header));
+ if (ret < 0) {
+ return ret;
+ }
+ ret = 0; /* ret should always be 0 or -errno */
+ qed_header_le_to_cpu(&le_header, &s->header);
+
+ if (s->header.magic != QED_MAGIC) {
+ return -EINVAL;
+ }
+ if (s->header.features & ~QED_FEATURE_MASK) {
+ /* image uses unsupported feature bits */
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%" PRIx64,
+ s->header.features & ~QED_FEATURE_MASK);
+ qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ bs->device_name, "QED", buf);
+ return -ENOTSUP;
+ }
+ if (!qed_is_cluster_size_valid(s->header.cluster_size)) {
+ return -EINVAL;
+ }
+
+ /* Round down file size to the last cluster */
+ file_size = bdrv_getlength(bs->file);
+ if (file_size < 0) {
+ return file_size;
+ }
+ s->file_size = qed_start_of_cluster(s, file_size);
+
+ if (!qed_is_table_size_valid(s->header.table_size)) {
+ return -EINVAL;
+ }
+ if (!qed_is_image_size_valid(s->header.image_size,
+ s->header.cluster_size,
+ s->header.table_size)) {
+ return -EINVAL;
+ }
+ if (!qed_check_table_offset(s, s->header.l1_table_offset)) {
+ return -EINVAL;
+ }
+
+ s->table_nelems = (s->header.cluster_size * s->header.table_size) /
+ sizeof(uint64_t);
+ s->l2_shift = ffs(s->header.cluster_size) - 1;
+ s->l2_mask = s->table_nelems - 1;
+ s->l1_shift = s->l2_shift + ffs(s->table_nelems) - 1;
+
+ if ((s->header.features & QED_F_BACKING_FILE)) {
+ if ((uint64_t)s->header.backing_filename_offset +
+ s->header.backing_filename_size >
+ s->header.cluster_size * s->header.header_size) {
+ return -EINVAL;
+ }
+
+ ret = qed_read_string(bs->file, s->header.backing_filename_offset,
+ s->header.backing_filename_size, bs->backing_file,
+ sizeof(bs->backing_file));
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) {
+ pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw");
+ }
+ }
+
+ /* Reset unknown autoclear feature bits. This is a backwards
+ * compatibility mechanism that allows images to be opened by older
+ * programs, which "knock out" unknown feature bits. When an image is
+ * opened by a newer program again it can detect that the autoclear
+ * feature is no longer valid.
+ */
+ if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 &&
+ !bdrv_is_read_only(bs->file)) {
+ s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK;
+
+ ret = qed_write_header_sync(s);
+ if (ret) {
+ return ret;
+ }
+
+ /* From here on only known autoclear feature bits are valid */
+ bdrv_flush(bs->file);
+ }
+
+ s->l1_table = qed_alloc_table(s);
+ qed_init_l2_cache(&s->l2_cache);
+
+ ret = qed_read_l1_table_sync(s);
+ if (ret) {
+ goto out;
+ }
+
+ /* If image was not closed cleanly, check consistency */
+ if (s->header.features & QED_F_NEED_CHECK) {
+ /* Read-only images cannot be fixed. There is no risk of corruption
+ * since write operations are not possible. Therefore, allow
+ * potentially inconsistent images to be opened read-only. This can
+ * aid data recovery from an otherwise inconsistent image.
+ */
+ if (!bdrv_is_read_only(bs->file)) {
+ BdrvCheckResult result = {0};
+
+ ret = qed_check(s, &result, true);
+ if (!ret && !result.corruptions && !result.check_errors) {
+ /* Ensure fixes reach storage before clearing check bit */
+ bdrv_flush(s->bs);
+
+ s->header.features &= ~QED_F_NEED_CHECK;
+ qed_write_header_sync(s);
+ }
+ }
+ }
+
+out:
+ if (ret) {
+ qed_free_l2_cache(&s->l2_cache);
+ qemu_vfree(s->l1_table);
+ }
+ return ret;
+}
+
+static void bdrv_qed_close(BlockDriverState *bs)
+{
+ BDRVQEDState *s = bs->opaque;
+
+ /* Ensure writes reach stable storage */
+ bdrv_flush(bs->file);
+
+ /* Clean shutdown, no check required on next open */
+ if (s->header.features & QED_F_NEED_CHECK) {
+ s->header.features &= ~QED_F_NEED_CHECK;
+ qed_write_header_sync(s);
+ }
+
+ qed_free_l2_cache(&s->l2_cache);
+ qemu_vfree(s->l1_table);
+}
+
+static int bdrv_qed_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+static int qed_create(const char *filename, uint32_t cluster_size,
+ uint64_t image_size, uint32_t table_size,
+ const char *backing_file, const char *backing_fmt)
+{
+ QEDHeader header = {
+ .magic = QED_MAGIC,
+ .cluster_size = cluster_size,
+ .table_size = table_size,
+ .header_size = 1,
+ .features = 0,
+ .compat_features = 0,
+ .l1_table_offset = cluster_size,
+ .image_size = image_size,
+ };
+ QEDHeader le_header;
+ uint8_t *l1_table = NULL;
+ size_t l1_size = header.cluster_size * header.table_size;
+ int ret = 0;
+ BlockDriverState *bs = NULL;
+
+ ret = bdrv_create_file(filename, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR | BDRV_O_CACHE_WB);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* File must start empty and grow, check truncate is supported */
+ ret = bdrv_truncate(bs, 0);
+ if (ret < 0) {
+ goto out;
+ }
+
+ if (backing_file) {
+ header.features |= QED_F_BACKING_FILE;
+ header.backing_filename_offset = sizeof(le_header);
+ header.backing_filename_size = strlen(backing_file);
+
+ if (qed_fmt_is_raw(backing_fmt)) {
+ header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
+ }
+ }
+
+ qed_header_cpu_to_le(&header, &le_header);
+ ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header));
+ if (ret < 0) {
+ goto out;
+ }
+ ret = bdrv_pwrite(bs, sizeof(le_header), backing_file,
+ header.backing_filename_size);
+ if (ret < 0) {
+ goto out;
+ }
+
+ l1_table = qemu_mallocz(l1_size);
+ ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size);
+ if (ret < 0) {
+ goto out;
+ }
+
+ ret = 0; /* success */
+out:
+ qemu_free(l1_table);
+ bdrv_delete(bs);
+ return ret;
+}
+
+static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options)
+{
+ uint64_t image_size = 0;
+ uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
+ uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
+ const char *backing_file = NULL;
+ const char *backing_fmt = NULL;
+
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ image_size = options->value.n;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FMT)) {
+ backing_fmt = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
+ if (options->value.n) {
+ cluster_size = options->value.n;
+ }
+ } else if (!strcmp(options->name, BLOCK_OPT_TABLE_SIZE)) {
+ if (options->value.n) {
+ table_size = options->value.n;
+ }
+ }
+ options++;
+ }
+
+ if (!qed_is_cluster_size_valid(cluster_size)) {
+ fprintf(stderr, "QED cluster size must be within range [%u, %u] and power of 2\n",
+ QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
+ return -EINVAL;
+ }
+ if (!qed_is_table_size_valid(table_size)) {
+ fprintf(stderr, "QED table size must be within range [%u, %u] and power of 2\n",
+ QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
+ return -EINVAL;
+ }
+ if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
+ fprintf(stderr, "QED image size must be a non-zero multiple of "
+ "cluster size and less than %" PRIu64 " bytes\n",
+ qed_max_image_size(cluster_size, table_size));
+ return -EINVAL;
+ }
+
+ return qed_create(filename, cluster_size, image_size, table_size,
+ backing_file, backing_fmt);
+}
+
+typedef struct {
+ int is_allocated;
+ int *pnum;
+} QEDIsAllocatedCB;
+
+static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len)
+{
+ QEDIsAllocatedCB *cb = opaque;
+ *cb->pnum = len / BDRV_SECTOR_SIZE;
+ cb->is_allocated = ret == QED_CLUSTER_FOUND;
+}
+
+static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ BDRVQEDState *s = bs->opaque;
+ uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
+ size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE;
+ QEDIsAllocatedCB cb = {
+ .is_allocated = -1,
+ .pnum = pnum,
+ };
+ QEDRequest request = { .l2_table = NULL };
+
+ async_context_push();
+
+ qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb);
+
+ while (cb.is_allocated == -1) {
+ qemu_aio_wait();
+ }
+
+ async_context_pop();
+
+ qed_unref_l2_cache_entry(request.l2_table);
+
+ return cb.is_allocated;
+}
+
+static int bdrv_qed_make_empty(BlockDriverState *bs)
+{
+ return -ENOTSUP;
+}
+
+static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
+{
+ return acb->common.bs->opaque;
+}
+
+/**
+ * Read from the backing file or zero-fill if no backing file
+ *
+ * @s: QED state
+ * @pos: Byte position in device
+ * @qiov: Destination I/O vector
+ * @cb: Completion function
+ * @opaque: User data for completion function
+ *
+ * This function reads qiov->size bytes starting at pos from the backing file.
+ * If there is no backing file then zeroes are read.
+ */
+static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
+ QEMUIOVector *qiov,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BlockDriverAIOCB *aiocb;
+ uint64_t backing_length = 0;
+ size_t size;
+
+ /* If there is a backing file, get its length. Treat the absence of a
+ * backing file like a zero length backing file.
+ */
+ if (s->bs->backing_hd) {
+ int64_t l = bdrv_getlength(s->bs->backing_hd);
+ if (l < 0) {
+ cb(opaque, l);
+ return;
+ }
+ backing_length = l;
+ }
+
+ /* Zero all sectors if reading beyond the end of the backing file */
+ if (pos >= backing_length ||
+ pos + qiov->size > backing_length) {
+ qemu_iovec_memset(qiov, 0, qiov->size);
+ }
+
+ /* Complete now if there are no backing file sectors to read */
+ if (pos >= backing_length) {
+ cb(opaque, 0);
+ return;
+ }
+
+ /* If the read straddles the end of the backing file, shorten it */
+ size = MIN((uint64_t)backing_length - pos, qiov->size);
+
+ BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING);
+ aiocb = bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
+ qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
+ if (!aiocb) {
+ cb(opaque, -EIO);
+ }
+}
+
+typedef struct {
+ GenericCB gencb;
+ BDRVQEDState *s;
+ QEMUIOVector qiov;
+ struct iovec iov;
+ uint64_t offset;
+} CopyFromBackingFileCB;
+
+static void qed_copy_from_backing_file_cb(void *opaque, int ret)
+{
+ CopyFromBackingFileCB *copy_cb = opaque;
+ qemu_vfree(copy_cb->iov.iov_base);
+ gencb_complete(&copy_cb->gencb, ret);
+}
+
+static void qed_copy_from_backing_file_write(void *opaque, int ret)
+{
+ CopyFromBackingFileCB *copy_cb = opaque;
+ BDRVQEDState *s = copy_cb->s;
+ BlockDriverAIOCB *aiocb;
+
+ if (ret) {
+ qed_copy_from_backing_file_cb(copy_cb, ret);
+ return;
+ }
+
+ BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE);
+ aiocb = bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE,
+ &copy_cb->qiov,
+ copy_cb->qiov.size / BDRV_SECTOR_SIZE,
+ qed_copy_from_backing_file_cb, copy_cb);
+ if (!aiocb) {
+ qed_copy_from_backing_file_cb(copy_cb, -EIO);
+ }
+}
+
+/**
+ * Copy data from backing file into the image
+ *
+ * @s: QED state
+ * @pos: Byte position in device
+ * @len: Number of bytes
+ * @offset: Byte offset in image file
+ * @cb: Completion function
+ * @opaque: User data for completion function
+ */
+static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos,
+ uint64_t len, uint64_t offset,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ CopyFromBackingFileCB *copy_cb;
+
+ /* Skip copy entirely if there is no work to do */
+ if (len == 0) {
+ cb(opaque, 0);
+ return;
+ }
+
+ copy_cb = gencb_alloc(sizeof(*copy_cb), cb, opaque);
+ copy_cb->s = s;
+ copy_cb->offset = offset;
+ copy_cb->iov.iov_base = qemu_blockalign(s->bs, len);
+ copy_cb->iov.iov_len = len;
+ qemu_iovec_init_external(&copy_cb->qiov, &copy_cb->iov, 1);
+
+ qed_read_backing_file(s, pos, &copy_cb->qiov,
+ qed_copy_from_backing_file_write, copy_cb);
+}
+
+/**
+ * Link one or more contiguous clusters into a table
+ *
+ * @s: QED state
+ * @table: L2 table
+ * @index: First cluster index
+ * @n: Number of contiguous clusters
+ * @cluster: First cluster byte offset in image file
+ */
+static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
+ unsigned int n, uint64_t cluster)
+{
+ int i;
+ for (i = index; i < index + n; i++) {
+ table->offsets[i] = cluster;
+ cluster += s->header.cluster_size;
+ }
+}
+
+static void qed_aio_complete_bh(void *opaque)
+{
+ QEDAIOCB *acb = opaque;
+ BlockDriverCompletionFunc *cb = acb->common.cb;
+ void *user_opaque = acb->common.opaque;
+ int ret = acb->bh_ret;
+ bool *finished = acb->finished;
+
+ qemu_bh_delete(acb->bh);
+ qemu_aio_release(acb);
+
+ /* Invoke callback */
+ cb(user_opaque, ret);
+
+ /* Signal cancel completion */
+ if (finished) {
+ *finished = true;
+ }
+}
+
+static void qed_aio_complete(QEDAIOCB *acb, int ret)
+{
+ BDRVQEDState *s = acb_to_s(acb);
+
+ trace_qed_aio_complete(s, acb, ret);
+
+ /* Free resources */
+ qemu_iovec_destroy(&acb->cur_qiov);
+ qed_unref_l2_cache_entry(acb->request.l2_table);
+
+ /* Arrange for a bh to invoke the completion function */
+ acb->bh_ret = ret;
+ acb->bh = qemu_bh_new(qed_aio_complete_bh, acb);
+ qemu_bh_schedule(acb->bh);
+
+ /* Start next allocating write request waiting behind this one. Note that
+ * requests enqueue themselves when they first hit an unallocated cluster
+ * but they wait until the entire request is finished before waking up the
+ * next request in the queue. This ensures that we don't cycle through
+ * requests multiple times but rather finish one at a time completely.
+ */
+ if (acb == QSIMPLEQ_FIRST(&s->allocating_write_reqs)) {
+ QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next);
+ acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
+ if (acb) {
+ qed_aio_next_io(acb, 0);
+ }
+ }
+}
+
+/**
+ * Commit the current L2 table to the cache
+ */
+static void qed_commit_l2_update(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ CachedL2Table *l2_table = acb->request.l2_table;
+
+ qed_commit_l2_cache_entry(&s->l2_cache, l2_table);
+
+ /* This is guaranteed to succeed because we just committed the entry to the
+ * cache.
+ */
+ acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache,
+ l2_table->offset);
+ assert(acb->request.l2_table != NULL);
+
+ qed_aio_next_io(opaque, ret);
+}
+
+/**
+ * Update L1 table with new L2 table offset and write it out
+ */
+static void qed_aio_write_l1_update(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ int index;
+
+ if (ret) {
+ qed_aio_complete(acb, ret);
+ return;
+ }
+
+ index = qed_l1_index(s, acb->cur_pos);
+ s->l1_table->offsets[index] = acb->request.l2_table->offset;
+
+ qed_write_l1_table(s, index, 1, qed_commit_l2_update, acb);
+}
+
+/**
+ * Update L2 table with new cluster offsets and write them out
+ */
+static void qed_aio_write_l2_update(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1;
+ int index;
+
+ if (ret) {
+ goto err;
+ }
+
+ if (need_alloc) {
+ qed_unref_l2_cache_entry(acb->request.l2_table);
+ acb->request.l2_table = qed_new_l2_table(s);
+ }
+
+ index = qed_l2_index(s, acb->cur_pos);
+ qed_update_l2_table(s, acb->request.l2_table->table, index, acb->cur_nclusters,
+ acb->cur_cluster);
+
+ if (need_alloc) {
+ /* Write out the whole new L2 table */
+ qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true,
+ qed_aio_write_l1_update, acb);
+ } else {
+ /* Write out only the updated part of the L2 table */
+ qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false,
+ qed_aio_next_io, acb);
+ }
+ return;
+
+err:
+ qed_aio_complete(acb, ret);
+}
+
+/**
+ * Flush new data clusters before updating the L2 table
+ *
+ * This flush is necessary when a backing file is in use. A crash during an
+ * allocating write could result in empty clusters in the image. If the write
+ * only touched a subregion of the cluster, then backing image sectors have
+ * been lost in the untouched region. The solution is to flush after writing a
+ * new data cluster and before updating the L2 table.
+ */
+static void qed_aio_write_flush_before_l2_update(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+
+ if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update, opaque)) {
+ qed_aio_complete(acb, -EIO);
+ }
+}
+
+/**
+ * Write data to the image file
+ */
+static void qed_aio_write_main(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ uint64_t offset = acb->cur_cluster +
+ qed_offset_into_cluster(s, acb->cur_pos);
+ BlockDriverCompletionFunc *next_fn;
+ BlockDriverAIOCB *file_acb;
+
+ trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size);
+
+ if (ret) {
+ qed_aio_complete(acb, ret);
+ return;
+ }
+
+ if (acb->find_cluster_ret == QED_CLUSTER_FOUND) {
+ next_fn = qed_aio_next_io;
+ } else {
+ if (s->bs->backing_hd) {
+ next_fn = qed_aio_write_flush_before_l2_update;
+ } else {
+ next_fn = qed_aio_write_l2_update;
+ }
+ }
+
+ BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
+ file_acb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
+ &acb->cur_qiov,
+ acb->cur_qiov.size / BDRV_SECTOR_SIZE,
+ next_fn, acb);
+ if (!file_acb) {
+ qed_aio_complete(acb, -EIO);
+ }
+}
+
+/**
+ * Populate back untouched region of new data cluster
+ */
+static void qed_aio_write_postfill(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ uint64_t start = acb->cur_pos + acb->cur_qiov.size;
+ uint64_t len =
+ qed_start_of_cluster(s, start + s->header.cluster_size - 1) - start;
+ uint64_t offset = acb->cur_cluster +
+ qed_offset_into_cluster(s, acb->cur_pos) +
+ acb->cur_qiov.size;
+
+ if (ret) {
+ qed_aio_complete(acb, ret);
+ return;
+ }
+
+ trace_qed_aio_write_postfill(s, acb, start, len, offset);
+ qed_copy_from_backing_file(s, start, len, offset,
+ qed_aio_write_main, acb);
+}
+
+/**
+ * Populate front untouched region of new data cluster
+ */
+static void qed_aio_write_prefill(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ uint64_t start = qed_start_of_cluster(s, acb->cur_pos);
+ uint64_t len = qed_offset_into_cluster(s, acb->cur_pos);
+
+ trace_qed_aio_write_prefill(s, acb, start, len, acb->cur_cluster);
+ qed_copy_from_backing_file(s, start, len, acb->cur_cluster,
+ qed_aio_write_postfill, acb);
+}
+
+/**
+ * Check if the QED_F_NEED_CHECK bit should be set during allocating write
+ */
+static bool qed_should_set_need_check(BDRVQEDState *s)
+{
+ /* The flush before L2 update path ensures consistency */
+ if (s->bs->backing_hd) {
+ return false;
+ }
+
+ return !(s->header.features & QED_F_NEED_CHECK);
+}
+
+/**
+ * Write new data cluster
+ *
+ * @acb: Write request
+ * @len: Length in bytes
+ *
+ * This path is taken when writing to previously unallocated clusters.
+ */
+static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
+{
+ BDRVQEDState *s = acb_to_s(acb);
+
+ /* 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)) {
+ return; /* wait for existing request to finish */
+ }
+
+ acb->cur_nclusters = qed_bytes_to_clusters(s,
+ qed_offset_into_cluster(s, acb->cur_pos) + len);
+ acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
+ qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
+
+ if (qed_should_set_need_check(s)) {
+ s->header.features |= QED_F_NEED_CHECK;
+ qed_write_header(s, qed_aio_write_prefill, acb);
+ } else {
+ qed_aio_write_prefill(acb, 0);
+ }
+}
+
+/**
+ * Write data cluster in place
+ *
+ * @acb: Write request
+ * @offset: Cluster offset in bytes
+ * @len: Length in bytes
+ *
+ * This path is taken when writing to already allocated clusters.
+ */
+static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
+{
+ /* Calculate the I/O vector */
+ acb->cur_cluster = offset;
+ qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
+
+ /* Do the actual write */
+ qed_aio_write_main(acb, 0);
+}
+
+/**
+ * Write data cluster
+ *
+ * @opaque: Write request
+ * @ret: QED_CLUSTER_FOUND, QED_CLUSTER_L2, QED_CLUSTER_L1,
+ * or -errno
+ * @offset: Cluster offset in bytes
+ * @len: Length in bytes
+ *
+ * Callback from qed_find_cluster().
+ */
+static void qed_aio_write_data(void *opaque, int ret,
+ uint64_t offset, size_t len)
+{
+ QEDAIOCB *acb = opaque;
+
+ trace_qed_aio_write_data(acb_to_s(acb), acb, ret, offset, len);
+
+ acb->find_cluster_ret = ret;
+
+ switch (ret) {
+ case QED_CLUSTER_FOUND:
+ qed_aio_write_inplace(acb, offset, len);
+ break;
+
+ case QED_CLUSTER_L2:
+ case QED_CLUSTER_L1:
+ qed_aio_write_alloc(acb, len);
+ break;
+
+ default:
+ qed_aio_complete(acb, ret);
+ break;
+ }
+}
+
+/**
+ * Read data cluster
+ *
+ * @opaque: Read request
+ * @ret: QED_CLUSTER_FOUND, QED_CLUSTER_L2, QED_CLUSTER_L1,
+ * or -errno
+ * @offset: Cluster offset in bytes
+ * @len: Length in bytes
+ *
+ * Callback from qed_find_cluster().
+ */
+static void qed_aio_read_data(void *opaque, int ret,
+ uint64_t offset, size_t len)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ BlockDriverState *bs = acb->common.bs;
+ BlockDriverAIOCB *file_acb;
+
+ /* Adjust offset into cluster */
+ offset += qed_offset_into_cluster(s, acb->cur_pos);
+
+ trace_qed_aio_read_data(s, acb, ret, offset, len);
+
+ if (ret < 0) {
+ goto err;
+ }
+
+ qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
+
+ /* Handle backing file and unallocated sparse hole reads */
+ if (ret != QED_CLUSTER_FOUND) {
+ qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov,
+ qed_aio_next_io, acb);
+ return;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
+ file_acb = bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
+ &acb->cur_qiov,
+ acb->cur_qiov.size / BDRV_SECTOR_SIZE,
+ qed_aio_next_io, acb);
+ if (!file_acb) {
+ ret = -EIO;
+ goto err;
+ }
+ return;
+
+err:
+ qed_aio_complete(acb, ret);
+}
+
+/**
+ * Begin next I/O or complete the request
+ */
+static void qed_aio_next_io(void *opaque, int ret)
+{
+ QEDAIOCB *acb = opaque;
+ BDRVQEDState *s = acb_to_s(acb);
+ QEDFindClusterFunc *io_fn =
+ acb->is_write ? qed_aio_write_data : qed_aio_read_data;
+
+ trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size);
+
+ /* Handle I/O error */
+ if (ret) {
+ qed_aio_complete(acb, ret);
+ return;
+ }
+
+ acb->qiov_offset += acb->cur_qiov.size;
+ acb->cur_pos += acb->cur_qiov.size;
+ qemu_iovec_reset(&acb->cur_qiov);
+
+ /* Complete request */
+ if (acb->cur_pos >= acb->end_pos) {
+ qed_aio_complete(acb, 0);
+ return;
+ }
+
+ /* Find next cluster and start I/O */
+ qed_find_cluster(s, &acb->request,
+ acb->cur_pos, acb->end_pos - acb->cur_pos,
+ io_fn, acb);
+}
+
+static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, bool is_write)
+{
+ QEDAIOCB *acb = qemu_aio_get(&qed_aio_pool, bs, cb, opaque);
+
+ trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors,
+ opaque, is_write);
+
+ acb->is_write = is_write;
+ acb->finished = NULL;
+ acb->qiov = qiov;
+ acb->qiov_offset = 0;
+ acb->cur_pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
+ acb->end_pos = acb->cur_pos + nb_sectors * BDRV_SECTOR_SIZE;
+ acb->request.l2_table = NULL;
+ qemu_iovec_init(&acb->cur_qiov, qiov->niov);
+
+ /* Start request */
+ qed_aio_next_io(acb, 0);
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, false);
+}
+
+static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, true);
+}
+
+static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ return bdrv_aio_flush(bs->file, cb, opaque);
+}
+
+static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
+{
+ return -ENOTSUP;
+}
+
+static int64_t bdrv_qed_getlength(BlockDriverState *bs)
+{
+ BDRVQEDState *s = bs->opaque;
+ return s->header.image_size;
+}
+
+static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+ BDRVQEDState *s = bs->opaque;
+
+ memset(bdi, 0, sizeof(*bdi));
+ bdi->cluster_size = s->header.cluster_size;
+ return 0;
+}
+
+static int bdrv_qed_change_backing_file(BlockDriverState *bs,
+ const char *backing_file,
+ const char *backing_fmt)
+{
+ BDRVQEDState *s = bs->opaque;
+ QEDHeader new_header, le_header;
+ void *buffer;
+ size_t buffer_len, backing_file_len;
+ int ret;
+
+ /* Refuse to set backing filename if unknown compat feature bits are
+ * active. If the image uses an unknown compat feature then we may not
+ * know the layout of data following the header structure and cannot safely
+ * add a new string.
+ */
+ if (backing_file && (s->header.compat_features &
+ ~QED_COMPAT_FEATURE_MASK)) {
+ return -ENOTSUP;
+ }
+
+ memcpy(&new_header, &s->header, sizeof(new_header));
+
+ new_header.features &= ~(QED_F_BACKING_FILE |
+ QED_F_BACKING_FORMAT_NO_PROBE);
+
+ /* Adjust feature flags */
+ if (backing_file) {
+ new_header.features |= QED_F_BACKING_FILE;
+
+ if (qed_fmt_is_raw(backing_fmt)) {
+ new_header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
+ }
+ }
+
+ /* Calculate new header size */
+ backing_file_len = 0;
+
+ if (backing_file) {
+ backing_file_len = strlen(backing_file);
+ }
+
+ buffer_len = sizeof(new_header);
+ new_header.backing_filename_offset = buffer_len;
+ new_header.backing_filename_size = backing_file_len;
+ buffer_len += backing_file_len;
+
+ /* Make sure we can rewrite header without failing */
+ if (buffer_len > new_header.header_size * new_header.cluster_size) {
+ return -ENOSPC;
+ }
+
+ /* Prepare new header */
+ buffer = qemu_malloc(buffer_len);
+
+ qed_header_cpu_to_le(&new_header, &le_header);
+ memcpy(buffer, &le_header, sizeof(le_header));
+ buffer_len = sizeof(le_header);
+
+ memcpy(buffer + buffer_len, backing_file, backing_file_len);
+ buffer_len += backing_file_len;
+
+ /* Write new header */
+ ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len);
+ qemu_free(buffer);
+ if (ret == 0) {
+ memcpy(&s->header, &new_header, sizeof(new_header));
+ }
+ return ret;
+}
+
+static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result)
+{
+ BDRVQEDState *s = bs->opaque;
+
+ return qed_check(s, result, false);
+}
+
+static QEMUOptionParameter qed_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size (in bytes)"
+ }, {
+ .name = BLOCK_OPT_BACKING_FILE,
+ .type = OPT_STRING,
+ .help = "File name of a base image"
+ }, {
+ .name = BLOCK_OPT_BACKING_FMT,
+ .type = OPT_STRING,
+ .help = "Image format of the base image"
+ }, {
+ .name = BLOCK_OPT_CLUSTER_SIZE,
+ .type = OPT_SIZE,
+ .help = "Cluster size (in bytes)"
+ }, {
+ .name = BLOCK_OPT_TABLE_SIZE,
+ .type = OPT_SIZE,
+ .help = "L1/L2 table size (in clusters)"
+ },
+ { /* end of list */ }
+};
+
+static BlockDriver bdrv_qed = {
+ .format_name = "qed",
+ .instance_size = sizeof(BDRVQEDState),
+ .create_options = qed_create_options,
+
+ .bdrv_probe = bdrv_qed_probe,
+ .bdrv_open = bdrv_qed_open,
+ .bdrv_close = bdrv_qed_close,
+ .bdrv_create = bdrv_qed_create,
+ .bdrv_flush = bdrv_qed_flush,
+ .bdrv_is_allocated = bdrv_qed_is_allocated,
+ .bdrv_make_empty = bdrv_qed_make_empty,
+ .bdrv_aio_readv = bdrv_qed_aio_readv,
+ .bdrv_aio_writev = bdrv_qed_aio_writev,
+ .bdrv_aio_flush = bdrv_qed_aio_flush,
+ .bdrv_truncate = bdrv_qed_truncate,
+ .bdrv_getlength = bdrv_qed_getlength,
+ .bdrv_get_info = bdrv_qed_get_info,
+ .bdrv_change_backing_file = bdrv_qed_change_backing_file,
+ .bdrv_check = bdrv_qed_check,
+};
+
+static void bdrv_qed_init(void)
+{
+ bdrv_register(&bdrv_qed);
+}
+
+block_init(bdrv_qed_init);
diff --git a/block/qed.h b/block/qed.h
new file mode 100644
index 0000000..9f72ad5
--- /dev/null
+++ b/block/qed.h
@@ -0,0 +1,301 @@
+/*
+ * QEMU Enhanced Disk Format
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef BLOCK_QED_H
+#define BLOCK_QED_H
+
+#include "block_int.h"
+
+/* The layout of a QED file is as follows:
+ *
+ * +--------+----------+----------+----------+-----+
+ * | header | L1 table | cluster0 | cluster1 | ... |
+ * +--------+----------+----------+----------+-----+
+ *
+ * There is a 2-level pagetable for cluster allocation:
+ *
+ * +----------+
+ * | L1 table |
+ * +----------+
+ * ,------' | '------.
+ * +----------+ | +----------+
+ * | L2 table | ... | L2 table |
+ * +----------+ +----------+
+ * ,------' | '------.
+ * +----------+ | +----------+
+ * | Data | ... | Data |
+ * +----------+ +----------+
+ *
+ * The L1 table is fixed size and always present. L2 tables are allocated on
+ * demand. The L1 table size determines the maximum possible image size; it
+ * can be influenced using the cluster_size and table_size values.
+ *
+ * All fields are little-endian on disk.
+ */
+
+enum {
+ QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24,
+
+ /* The image supports a backing file */
+ QED_F_BACKING_FILE = 0x01,
+
+ /* The image needs a consistency check before use */
+ QED_F_NEED_CHECK = 0x02,
+
+ /* The backing file format must not be probed, treat as raw image */
+ QED_F_BACKING_FORMAT_NO_PROBE = 0x04,
+
+ /* Feature bits must be used when the on-disk format changes */
+ QED_FEATURE_MASK = QED_F_BACKING_FILE | /* supported feature bits */
+ QED_F_NEED_CHECK |
+ QED_F_BACKING_FORMAT_NO_PROBE,
+ QED_COMPAT_FEATURE_MASK = 0, /* supported compat feature bits */
+ QED_AUTOCLEAR_FEATURE_MASK = 0, /* supported autoclear feature bits */
+
+ /* Data is stored in groups of sectors called clusters. Cluster size must
+ * be large to avoid keeping too much metadata. I/O requests that have
+ * sub-cluster size will require read-modify-write.
+ */
+ QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */
+ QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024,
+ QED_DEFAULT_CLUSTER_SIZE = 64 * 1024,
+
+ /* Allocated clusters are tracked using a 2-level pagetable. Table size is
+ * a multiple of clusters so large maximum image sizes can be supported
+ * without jacking up the cluster size too much.
+ */
+ QED_MIN_TABLE_SIZE = 1, /* in clusters */
+ QED_MAX_TABLE_SIZE = 16,
+ QED_DEFAULT_TABLE_SIZE = 4,
+};
+
+typedef struct {
+ uint32_t magic; /* QED\0 */
+
+ uint32_t cluster_size; /* in bytes */
+ uint32_t table_size; /* for L1 and L2 tables, in clusters */
+ uint32_t header_size; /* in clusters */
+
+ uint64_t features; /* format feature bits */
+ uint64_t compat_features; /* compatible feature bits */
+ uint64_t autoclear_features; /* self-resetting feature bits */
+
+ uint64_t l1_table_offset; /* in bytes */
+ uint64_t image_size; /* total logical image size, in bytes */
+
+ /* if (features & QED_F_BACKING_FILE) */
+ uint32_t backing_filename_offset; /* in bytes from start of header */
+ uint32_t backing_filename_size; /* in bytes */
+} QEDHeader;
+
+typedef struct {
+ uint64_t offsets[0]; /* in bytes */
+} QEDTable;
+
+/* The L2 cache is a simple write-through cache for L2 structures */
+typedef struct CachedL2Table {
+ QEDTable *table;
+ uint64_t offset; /* offset=0 indicates an invalidate entry */
+ QTAILQ_ENTRY(CachedL2Table) node;
+ int ref;
+} CachedL2Table;
+
+typedef struct {
+ QTAILQ_HEAD(, CachedL2Table) entries;
+ unsigned int n_entries;
+} L2TableCache;
+
+typedef struct QEDRequest {
+ CachedL2Table *l2_table;
+} QEDRequest;
+
+typedef struct QEDAIOCB {
+ BlockDriverAIOCB common;
+ QEMUBH *bh;
+ int bh_ret; /* final return status for completion bh */
+ QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */
+ bool is_write; /* false - read, true - write */
+ bool *finished; /* signal for cancel completion */
+ uint64_t end_pos; /* request end on block device, in bytes */
+
+ /* User scatter-gather list */
+ QEMUIOVector *qiov;
+ size_t qiov_offset; /* byte count already processed */
+
+ /* Current cluster scatter-gather list */
+ QEMUIOVector cur_qiov;
+ uint64_t cur_pos; /* position on block device, in bytes */
+ uint64_t cur_cluster; /* cluster offset in image file */
+ unsigned int cur_nclusters; /* number of clusters being accessed */
+ int find_cluster_ret; /* used for L1/L2 update */
+
+ QEDRequest request;
+} QEDAIOCB;
+
+typedef struct {
+ BlockDriverState *bs; /* device */
+ uint64_t file_size; /* length of image file, in bytes */
+
+ QEDHeader header; /* always cpu-endian */
+ QEDTable *l1_table;
+ L2TableCache l2_cache; /* l2 table cache */
+ uint32_t table_nelems;
+ uint32_t l1_shift;
+ uint32_t l2_shift;
+ uint32_t l2_mask;
+
+ /* Allocating write request queue */
+ QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs;
+} BDRVQEDState;
+
+enum {
+ QED_CLUSTER_FOUND, /* cluster found */
+ QED_CLUSTER_L2, /* cluster missing in L2 */
+ QED_CLUSTER_L1, /* cluster missing in L1 */
+};
+
+/**
+ * qed_find_cluster() completion callback
+ *
+ * @opaque: User data for completion callback
+ * @ret: QED_CLUSTER_FOUND Success
+ * QED_CLUSTER_L2 Data cluster unallocated in L2
+ * QED_CLUSTER_L1 L2 unallocated in L1
+ * -errno POSIX error occurred
+ * @offset: Data cluster offset
+ * @len: Contiguous bytes starting from cluster offset
+ *
+ * This function is invoked when qed_find_cluster() completes.
+ *
+ * On success ret is QED_CLUSTER_FOUND and offset/len are a contiguous range
+ * in the image file.
+ *
+ * On failure ret is QED_CLUSTER_L2 or QED_CLUSTER_L1 for missing L2 or L1
+ * table offset, respectively. len is number of contiguous unallocated bytes.
+ */
+typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len);
+
+/**
+ * Generic callback for chaining async callbacks
+ */
+typedef struct {
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+} GenericCB;
+
+void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
+void gencb_complete(void *opaque, int ret);
+
+/**
+ * L2 cache functions
+ */
+void qed_init_l2_cache(L2TableCache *l2_cache);
+void qed_free_l2_cache(L2TableCache *l2_cache);
+CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache);
+void qed_unref_l2_cache_entry(CachedL2Table *entry);
+CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset);
+void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table);
+
+/**
+ * Table I/O functions
+ */
+int qed_read_l1_table_sync(BDRVQEDState *s);
+void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
+ BlockDriverCompletionFunc *cb, void *opaque);
+int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
+ unsigned int n);
+int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
+ uint64_t offset);
+void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
+ BlockDriverCompletionFunc *cb, void *opaque);
+void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
+ unsigned int index, unsigned int n, bool flush,
+ BlockDriverCompletionFunc *cb, void *opaque);
+int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
+ unsigned int index, unsigned int n, bool flush);
+
+/**
+ * Cluster functions
+ */
+void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
+ size_t len, QEDFindClusterFunc *cb, void *opaque);
+
+/**
+ * Consistency check
+ */
+int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix);
+
+QEDTable *qed_alloc_table(BDRVQEDState *s);
+
+/**
+ * Round down to the start of a cluster
+ */
+static inline uint64_t qed_start_of_cluster(BDRVQEDState *s, uint64_t offset)
+{
+ return offset & ~(uint64_t)(s->header.cluster_size - 1);
+}
+
+static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset)
+{
+ return offset & (s->header.cluster_size - 1);
+}
+
+static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes)
+{
+ return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) /
+ (s->header.cluster_size - 1);
+}
+
+static inline unsigned int qed_l1_index(BDRVQEDState *s, uint64_t pos)
+{
+ return pos >> s->l1_shift;
+}
+
+static inline unsigned int qed_l2_index(BDRVQEDState *s, uint64_t pos)
+{
+ return (pos >> s->l2_shift) & s->l2_mask;
+}
+
+/**
+ * Test if a cluster offset is valid
+ */
+static inline bool qed_check_cluster_offset(BDRVQEDState *s, uint64_t offset)
+{
+ uint64_t header_size = (uint64_t)s->header.header_size *
+ s->header.cluster_size;
+
+ if (offset & (s->header.cluster_size - 1)) {
+ return false;
+ }
+ return offset >= header_size && offset < s->file_size;
+}
+
+/**
+ * Test if a table offset is valid
+ */
+static inline bool qed_check_table_offset(BDRVQEDState *s, uint64_t offset)
+{
+ uint64_t end_offset = offset + (s->header.table_size - 1) *
+ s->header.cluster_size;
+
+ /* Overflow check */
+ if (end_offset <= offset) {
+ return false;
+ }
+
+ return qed_check_cluster_offset(s, offset) &&
+ qed_check_cluster_offset(s, end_offset);
+}
+
+#endif /* BLOCK_QED_H */
diff --git a/block/raw-posix-aio.h b/block/raw-posix-aio.h
new file mode 100644
index 0000000..dfc63b8
--- /dev/null
+++ b/block/raw-posix-aio.h
@@ -0,0 +1,43 @@
+/*
+ * QEMU Posix block I/O backend AIO support
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef QEMU_RAW_POSIX_AIO_H
+#define QEMU_RAW_POSIX_AIO_H
+
+/* AIO request types */
+#define QEMU_AIO_READ 0x0001
+#define QEMU_AIO_WRITE 0x0002
+#define QEMU_AIO_IOCTL 0x0004
+#define QEMU_AIO_FLUSH 0x0008
+#define QEMU_AIO_TYPE_MASK \
+ (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH)
+
+/* AIO flags */
+#define QEMU_AIO_MISALIGNED 0x1000
+
+
+/* posix-aio-compat.c - thread pool based implementation */
+int paio_init(void);
+BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int type);
+BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque);
+
+/* linux-aio.c - Linux native implementation */
+void *laio_init(void);
+BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int type);
+
+#endif /* QEMU_RAW_POSIX_AIO_H */
diff --git a/block/raw-posix.c b/block/raw-posix.c
new file mode 100644
index 0000000..a95c8d4
--- /dev/null
+++ b/block/raw-posix.c
@@ -0,0 +1,1446 @@
+/*
+ * Block driver for RAW files (posix)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "qemu-log.h"
+#include "block_int.h"
+#include "module.h"
+#include "compatfd.h"
+#include <assert.h>
+#include "block/raw-posix-aio.h"
+
+#ifdef CONFIG_COCOA
+#include <paths.h>
+#include <sys/param.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOMediaBSDClient.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+//#include <IOKit/storage/IOCDTypes.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#ifdef __sun__
+#define _POSIX_PTHREAD_SEMANTICS 1
+#include <signal.h>
+#include <sys/dkio.h>
+#endif
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <linux/cdrom.h>
+#include <linux/fd.h>
+#endif
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <signal.h>
+#include <sys/disk.h>
+#include <sys/cdio.h>
+#endif
+
+#ifdef __OpenBSD__
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#endif
+
+#ifdef __DragonFly__
+#include <sys/ioctl.h>
+#include <sys/diskslice.h>
+#endif
+
+#ifdef CONFIG_XFS
+#include <xfs/xfs.h>
+#endif
+
+//#define DEBUG_FLOPPY
+
+//#define DEBUG_BLOCK
+#if defined(DEBUG_BLOCK)
+#define DEBUG_BLOCK_PRINT(formatCstr, ...) do { if (qemu_log_enabled()) \
+ { qemu_log(formatCstr, ## __VA_ARGS__); qemu_log_flush(); } } while (0)
+#else
+#define DEBUG_BLOCK_PRINT(formatCstr, ...)
+#endif
+
+/* OS X does not have O_DSYNC */
+#ifndef O_DSYNC
+#ifdef O_SYNC
+#define O_DSYNC O_SYNC
+#elif defined(O_FSYNC)
+#define O_DSYNC O_FSYNC
+#endif
+#endif
+
+/* Approximate O_DIRECT with O_DSYNC if O_DIRECT isn't available */
+#ifndef O_DIRECT
+#define O_DIRECT O_DSYNC
+#endif
+
+#define FTYPE_FILE 0
+#define FTYPE_CD 1
+#define FTYPE_FD 2
+
+/* if the FD is not accessed during that time (in ns), we try to
+ reopen it to see if the disk has been changed */
+#define FD_OPEN_TIMEOUT (1000000000)
+
+#define MAX_BLOCKSIZE 4096
+
+typedef struct BDRVRawState {
+ int fd;
+ int type;
+ int open_flags;
+#if defined(__linux__)
+ /* linux floppy specific */
+ int64_t fd_open_time;
+ int64_t fd_error_time;
+ int fd_got_error;
+ int fd_media_changed;
+#endif
+#ifdef CONFIG_LINUX_AIO
+ int use_aio;
+ void *aio_ctx;
+#endif
+ uint8_t *aligned_buf;
+ unsigned aligned_buf_size;
+#ifdef CONFIG_XFS
+ bool is_xfs : 1;
+#endif
+} BDRVRawState;
+
+static int fd_open(BlockDriverState *bs);
+static int64_t raw_getlength(BlockDriverState *bs);
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+static int cdrom_reopen(BlockDriverState *bs);
+#endif
+
+static int raw_open_common(BlockDriverState *bs, const char *filename,
+ int bdrv_flags, int open_flags)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd, ret;
+
+ s->open_flags = open_flags | O_BINARY;
+ s->open_flags &= ~O_ACCMODE;
+ if (bdrv_flags & BDRV_O_RDWR) {
+ s->open_flags |= O_RDWR;
+ } else {
+ s->open_flags |= O_RDONLY;
+ }
+
+ /* Use O_DSYNC for write-through caching, no flags for write-back caching,
+ * and O_DIRECT for no caching. */
+ if ((bdrv_flags & BDRV_O_NOCACHE))
+ s->open_flags |= O_DIRECT;
+ else if (!(bdrv_flags & BDRV_O_CACHE_WB))
+ s->open_flags |= O_DSYNC;
+
+ s->fd = -1;
+ fd = qemu_open(filename, s->open_flags, 0644);
+ if (fd < 0) {
+ ret = -errno;
+ if (ret == -EROFS)
+ ret = -EACCES;
+ return ret;
+ }
+ s->fd = fd;
+ s->aligned_buf = NULL;
+
+ if ((bdrv_flags & BDRV_O_NOCACHE)) {
+ /*
+ * Allocate a buffer for read/modify/write cycles. Chose the size
+ * pessimistically as we don't know the block size yet.
+ */
+ s->aligned_buf_size = 32 * MAX_BLOCKSIZE;
+ s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size);
+ if (s->aligned_buf == NULL) {
+ goto out_close;
+ }
+ }
+
+#ifdef CONFIG_LINUX_AIO
+ if ((bdrv_flags & (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) ==
+ (BDRV_O_NOCACHE|BDRV_O_NATIVE_AIO)) {
+
+ /* We're falling back to POSIX AIO in some cases */
+ paio_init();
+
+ s->aio_ctx = laio_init();
+ if (!s->aio_ctx) {
+ goto out_free_buf;
+ }
+ s->use_aio = 1;
+ } else
+#endif
+ {
+ if (paio_init() < 0) {
+ goto out_free_buf;
+ }
+#ifdef CONFIG_LINUX_AIO
+ s->use_aio = 0;
+#endif
+ }
+
+#ifdef CONFIG_XFS
+ if (platform_test_xfs_fd(s->fd)) {
+ s->is_xfs = 1;
+ }
+#endif
+
+ return 0;
+
+out_free_buf:
+ qemu_vfree(s->aligned_buf);
+out_close:
+ close(fd);
+ return -errno;
+}
+
+static int raw_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+
+ s->type = FTYPE_FILE;
+ return raw_open_common(bs, filename, flags, 0);
+}
+
+/* XXX: use host sector size if necessary with:
+#ifdef DIOCGSECTORSIZE
+ {
+ unsigned int sectorsize = 512;
+ if (!ioctl(fd, DIOCGSECTORSIZE, &sectorsize) &&
+ sectorsize > bufsize)
+ bufsize = sectorsize;
+ }
+#endif
+#ifdef CONFIG_COCOA
+ uint32_t blockSize = 512;
+ if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
+ bufsize = blockSize;
+ }
+#endif
+*/
+
+/*
+ * offset and count are in bytes, but must be multiples of 512 for files
+ * opened with O_DIRECT. buf must be aligned to 512 bytes then.
+ *
+ * This function may be called without alignment if the caller ensures
+ * that O_DIRECT is not in effect.
+ */
+static int raw_pread_aligned(BlockDriverState *bs, int64_t offset,
+ uint8_t *buf, int count)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ ret = fd_open(bs);
+ if (ret < 0)
+ return ret;
+
+ ret = pread(s->fd, buf, count, offset);
+ if (ret == count)
+ return ret;
+
+ /* Allow reads beyond the end (needed for pwrite) */
+ if ((ret == 0) && bs->growable) {
+ int64_t size = raw_getlength(bs);
+ if (offset >= size) {
+ memset(buf, 0, count);
+ return count;
+ }
+ }
+
+ DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+ "] read failed %d : %d = %s\n",
+ s->fd, bs->filename, offset, buf, count,
+ bs->total_sectors, ret, errno, strerror(errno));
+
+ /* Try harder for CDrom. */
+ if (s->type != FTYPE_FILE) {
+ ret = pread(s->fd, buf, count, offset);
+ if (ret == count)
+ return ret;
+ ret = pread(s->fd, buf, count, offset);
+ if (ret == count)
+ return ret;
+
+ DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+ "] retry read failed %d : %d = %s\n",
+ s->fd, bs->filename, offset, buf, count,
+ bs->total_sectors, ret, errno, strerror(errno));
+ }
+
+ return (ret < 0) ? -errno : ret;
+}
+
+/*
+ * offset and count are in bytes, but must be multiples of the sector size
+ * for files opened with O_DIRECT. buf must be aligned to sector size bytes
+ * then.
+ *
+ * This function may be called without alignment if the caller ensures
+ * that O_DIRECT is not in effect.
+ */
+static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset,
+ const uint8_t *buf, int count)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ ret = fd_open(bs);
+ if (ret < 0)
+ return -errno;
+
+ ret = pwrite(s->fd, buf, count, offset);
+ if (ret == count)
+ return ret;
+
+ DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+ "] write failed %d : %d = %s\n",
+ s->fd, bs->filename, offset, buf, count,
+ bs->total_sectors, ret, errno, strerror(errno));
+
+ return (ret < 0) ? -errno : ret;
+}
+
+
+/*
+ * offset and count are in bytes and possibly not aligned. For files opened
+ * with O_DIRECT, necessary alignments are ensured before calling
+ * raw_pread_aligned to do the actual read.
+ */
+static int raw_pread(BlockDriverState *bs, int64_t offset,
+ uint8_t *buf, int count)
+{
+ BDRVRawState *s = bs->opaque;
+ unsigned sector_mask = bs->buffer_alignment - 1;
+ int size, ret, shift, sum;
+
+ sum = 0;
+
+ if (s->aligned_buf != NULL) {
+
+ if (offset & sector_mask) {
+ /* align offset on a sector size bytes boundary */
+
+ shift = offset & sector_mask;
+ size = (shift + count + sector_mask) & ~sector_mask;
+ if (size > s->aligned_buf_size)
+ size = s->aligned_buf_size;
+ ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size);
+ if (ret < 0)
+ return ret;
+
+ size = bs->buffer_alignment - shift;
+ if (size > count)
+ size = count;
+ memcpy(buf, s->aligned_buf + shift, size);
+
+ buf += size;
+ offset += size;
+ count -= size;
+ sum += size;
+
+ if (count == 0)
+ return sum;
+ }
+ if (count & sector_mask || (uintptr_t) buf & sector_mask) {
+
+ /* read on aligned buffer */
+
+ while (count) {
+
+ size = (count + sector_mask) & ~sector_mask;
+ if (size > s->aligned_buf_size)
+ size = s->aligned_buf_size;
+
+ ret = raw_pread_aligned(bs, offset, s->aligned_buf, size);
+ if (ret < 0) {
+ return ret;
+ } else if (ret == 0) {
+ fprintf(stderr, "raw_pread: read beyond end of file\n");
+ abort();
+ }
+
+ size = ret;
+ if (size > count)
+ size = count;
+
+ memcpy(buf, s->aligned_buf, size);
+
+ buf += size;
+ offset += size;
+ count -= size;
+ sum += size;
+ }
+
+ return sum;
+ }
+ }
+
+ return raw_pread_aligned(bs, offset, buf, count) + sum;
+}
+
+static int raw_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ int ret;
+
+ ret = raw_pread(bs, sector_num * BDRV_SECTOR_SIZE, buf,
+ nb_sectors * BDRV_SECTOR_SIZE);
+ if (ret == (nb_sectors * BDRV_SECTOR_SIZE))
+ ret = 0;
+ return ret;
+}
+
+/*
+ * offset and count are in bytes and possibly not aligned. For files opened
+ * with O_DIRECT, necessary alignments are ensured before calling
+ * raw_pwrite_aligned to do the actual write.
+ */
+static int raw_pwrite(BlockDriverState *bs, int64_t offset,
+ const uint8_t *buf, int count)
+{
+ BDRVRawState *s = bs->opaque;
+ unsigned sector_mask = bs->buffer_alignment - 1;
+ int size, ret, shift, sum;
+
+ sum = 0;
+
+ if (s->aligned_buf != NULL) {
+
+ if (offset & sector_mask) {
+ /* align offset on a sector size bytes boundary */
+ shift = offset & sector_mask;
+ ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf,
+ bs->buffer_alignment);
+ if (ret < 0)
+ return ret;
+
+ size = bs->buffer_alignment - shift;
+ if (size > count)
+ size = count;
+ memcpy(s->aligned_buf + shift, buf, size);
+
+ ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf,
+ bs->buffer_alignment);
+ if (ret < 0)
+ return ret;
+
+ buf += size;
+ offset += size;
+ count -= size;
+ sum += size;
+
+ if (count == 0)
+ return sum;
+ }
+ if (count & sector_mask || (uintptr_t) buf & sector_mask) {
+
+ while ((size = (count & ~sector_mask)) != 0) {
+
+ if (size > s->aligned_buf_size)
+ size = s->aligned_buf_size;
+
+ memcpy(s->aligned_buf, buf, size);
+
+ ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, size);
+ if (ret < 0)
+ return ret;
+
+ buf += ret;
+ offset += ret;
+ count -= ret;
+ sum += ret;
+ }
+ /* here, count < sector_size because (count & ~sector_mask) == 0 */
+ if (count) {
+ ret = raw_pread_aligned(bs, offset, s->aligned_buf,
+ bs->buffer_alignment);
+ if (ret < 0)
+ return ret;
+ memcpy(s->aligned_buf, buf, count);
+
+ ret = raw_pwrite_aligned(bs, offset, s->aligned_buf,
+ bs->buffer_alignment);
+ if (ret < 0)
+ return ret;
+ if (count < ret)
+ ret = count;
+
+ sum += ret;
+ }
+ return sum;
+ }
+ }
+ return raw_pwrite_aligned(bs, offset, buf, count) + sum;
+}
+
+static int raw_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ int ret;
+ ret = raw_pwrite(bs, sector_num * BDRV_SECTOR_SIZE, buf,
+ nb_sectors * BDRV_SECTOR_SIZE);
+ if (ret == (nb_sectors * BDRV_SECTOR_SIZE))
+ ret = 0;
+ return ret;
+}
+
+/*
+ * Check if all memory in this vector is sector aligned.
+ */
+static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
+{
+ int i;
+
+ for (i = 0; i < qiov->niov; i++) {
+ if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int type)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (fd_open(bs) < 0)
+ return NULL;
+
+ /*
+ * If O_DIRECT is used the buffer needs to be aligned on a sector
+ * boundary. Check if this is the case or telll the low-level
+ * driver that it needs to copy the buffer.
+ */
+ if (s->aligned_buf) {
+ if (!qiov_is_aligned(bs, qiov)) {
+ type |= QEMU_AIO_MISALIGNED;
+#ifdef CONFIG_LINUX_AIO
+ } else if (s->use_aio) {
+ return laio_submit(bs, s->aio_ctx, s->fd, sector_num, qiov,
+ nb_sectors, cb, opaque, type);
+#endif
+ }
+ }
+
+ return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors,
+ cb, opaque, type);
+}
+
+static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
+ cb, opaque, QEMU_AIO_READ);
+}
+
+static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
+ cb, opaque, QEMU_AIO_WRITE);
+}
+
+static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (fd_open(bs) < 0)
+ return NULL;
+
+ return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH);
+}
+
+static void raw_close(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ if (s->fd >= 0) {
+ close(s->fd);
+ s->fd = -1;
+ if (s->aligned_buf != NULL)
+ qemu_vfree(s->aligned_buf);
+ }
+}
+
+static int raw_truncate(BlockDriverState *bs, int64_t offset)
+{
+ BDRVRawState *s = bs->opaque;
+ if (s->type != FTYPE_FILE)
+ return -ENOTSUP;
+ if (ftruncate(s->fd, offset) < 0)
+ return -errno;
+ return 0;
+}
+
+#ifdef __OpenBSD__
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd = s->fd;
+ struct stat st;
+
+ if (fstat(fd, &st))
+ return -1;
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ struct disklabel dl;
+
+ if (ioctl(fd, DIOCGDINFO, &dl))
+ return -1;
+ return (uint64_t)dl.d_secsize *
+ dl.d_partitions[DISKPART(st.st_rdev)].p_size;
+ } else
+ return st.st_size;
+}
+#elif defined(__sun__)
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ struct dk_minfo minfo;
+ int ret;
+
+ ret = fd_open(bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * Use the DKIOCGMEDIAINFO ioctl to read the size.
+ */
+ ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo);
+ if (ret != -1) {
+ return minfo.dki_lbsize * minfo.dki_capacity;
+ }
+
+ /*
+ * There are reports that lseek on some devices fails, but
+ * irc discussion said that contingency on contingency was overkill.
+ */
+ return lseek(s->fd, 0, SEEK_END);
+}
+#elif defined(CONFIG_BSD)
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd = s->fd;
+ int64_t size;
+ struct stat sb;
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+ int reopened = 0;
+#endif
+ int ret;
+
+ ret = fd_open(bs);
+ if (ret < 0)
+ return ret;
+
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+again:
+#endif
+ if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
+#ifdef DIOCGMEDIASIZE
+ if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
+#elif defined(DIOCGPART)
+ {
+ struct partinfo pi;
+ if (ioctl(fd, DIOCGPART, &pi) == 0)
+ size = pi.media_size;
+ else
+ size = 0;
+ }
+ if (size == 0)
+#endif
+#ifdef CONFIG_COCOA
+ size = LONG_LONG_MAX;
+#else
+ size = lseek(fd, 0LL, SEEK_END);
+#endif
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ switch(s->type) {
+ case FTYPE_CD:
+ /* XXX FreeBSD acd returns UINT_MAX sectors for an empty drive */
+ if (size == 2048LL * (unsigned)-1)
+ size = 0;
+ /* XXX no disc? maybe we need to reopen... */
+ if (size <= 0 && !reopened && cdrom_reopen(bs) >= 0) {
+ reopened = 1;
+ goto again;
+ }
+ }
+#endif
+ } else {
+ size = lseek(fd, 0, SEEK_END);
+ }
+ return size;
+}
+#else
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ ret = fd_open(bs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return lseek(s->fd, 0, SEEK_END);
+}
+#endif
+
+static int raw_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd;
+ int result = 0;
+ int64_t total_size = 0;
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n / BDRV_SECTOR_SIZE;
+ }
+ options++;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ 0644);
+ if (fd < 0) {
+ result = -errno;
+ } else {
+ if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) {
+ result = -errno;
+ }
+ if (close(fd) != 0) {
+ result = -errno;
+ }
+ }
+ return result;
+}
+
+static int raw_flush(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ return qemu_fdatasync(s->fd);
+}
+
+#ifdef CONFIG_XFS
+static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors)
+{
+ struct xfs_flock64 fl;
+
+ memset(&fl, 0, sizeof(fl));
+ fl.l_whence = SEEK_SET;
+ fl.l_start = sector_num << 9;
+ fl.l_len = (int64_t)nb_sectors << 9;
+
+ if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) {
+ DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+#endif
+
+static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+{
+#ifdef CONFIG_XFS
+ BDRVRawState *s = bs->opaque;
+
+ if (s->is_xfs) {
+ return xfs_discard(s, sector_num, nb_sectors);
+ }
+#endif
+
+ return 0;
+}
+
+static QEMUOptionParameter raw_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_file = {
+ .format_name = "file",
+ .protocol_name = "file",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe = NULL, /* no probe for protocols */
+ .bdrv_file_open = raw_open,
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_close = raw_close,
+ .bdrv_create = raw_create,
+ .bdrv_flush = raw_flush,
+ .bdrv_discard = raw_discard,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+
+ .bdrv_truncate = raw_truncate,
+ .bdrv_getlength = raw_getlength,
+
+ .create_options = raw_create_options,
+};
+
+/***********************************************/
+/* host device */
+
+#ifdef CONFIG_COCOA
+static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
+static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
+
+kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
+{
+ kern_return_t kernResult;
+ mach_port_t masterPort;
+ CFMutableDictionaryRef classesToMatch;
+
+ kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
+ if ( KERN_SUCCESS != kernResult ) {
+ printf( "IOMasterPort returned %d\n", kernResult );
+ }
+
+ classesToMatch = IOServiceMatching( kIOCDMediaClass );
+ if ( classesToMatch == NULL ) {
+ printf( "IOServiceMatching returned a NULL dictionary.\n" );
+ } else {
+ CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
+ }
+ kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
+ if ( KERN_SUCCESS != kernResult )
+ {
+ printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
+ }
+
+ return kernResult;
+}
+
+kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
+{
+ io_object_t nextMedia;
+ kern_return_t kernResult = KERN_FAILURE;
+ *bsdPath = '\0';
+ nextMedia = IOIteratorNext( mediaIterator );
+ if ( nextMedia )
+ {
+ CFTypeRef bsdPathAsCFString;
+ bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
+ if ( bsdPathAsCFString ) {
+ size_t devPathLength;
+ strcpy( bsdPath, _PATH_DEV );
+ strcat( bsdPath, "r" );
+ devPathLength = strlen( bsdPath );
+ if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
+ kernResult = KERN_SUCCESS;
+ }
+ CFRelease( bsdPathAsCFString );
+ }
+ IOObjectRelease( nextMedia );
+ }
+
+ return kernResult;
+}
+
+#endif
+
+static int hdev_probe_device(const char *filename)
+{
+ struct stat st;
+
+ /* allow a dedicated CD-ROM driver to match with a higher priority */
+ if (strstart(filename, "/dev/cdrom", NULL))
+ return 50;
+
+ if (stat(filename, &st) >= 0 &&
+ (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) {
+ return 100;
+ }
+
+ return 0;
+}
+
+static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+
+#ifdef CONFIG_COCOA
+ if (strstart(filename, "/dev/cdrom", NULL)) {
+ kern_return_t kernResult;
+ io_iterator_t mediaIterator;
+ char bsdPath[ MAXPATHLEN ];
+ int fd;
+
+ kernResult = FindEjectableCDMedia( &mediaIterator );
+ kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
+
+ if ( bsdPath[ 0 ] != '\0' ) {
+ strcat(bsdPath,"s0");
+ /* some CDs don't have a partition 0 */
+ fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (fd < 0) {
+ bsdPath[strlen(bsdPath)-1] = '1';
+ } else {
+ close(fd);
+ }
+ filename = bsdPath;
+ }
+
+ if ( mediaIterator )
+ IOObjectRelease( mediaIterator );
+ }
+#endif
+
+ s->type = FTYPE_FILE;
+#if defined(__linux__)
+ {
+ char resolved_path[ MAXPATHLEN ], *temp;
+
+ temp = realpath(filename, resolved_path);
+ if (temp && strstart(temp, "/dev/sg", NULL)) {
+ bs->sg = 1;
+ }
+ }
+#endif
+
+ return raw_open_common(bs, filename, flags, 0);
+}
+
+#if defined(__linux__)
+/* Note: we do not have a reliable method to detect if the floppy is
+ present. The current method is to try to open the floppy at every
+ I/O and to keep it opened during a few hundreds of ms. */
+static int fd_open(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int last_media_present;
+
+ if (s->type != FTYPE_FD)
+ return 0;
+ last_media_present = (s->fd >= 0);
+ if (s->fd >= 0 &&
+ (get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
+ close(s->fd);
+ s->fd = -1;
+#ifdef DEBUG_FLOPPY
+ printf("Floppy closed\n");
+#endif
+ }
+ if (s->fd < 0) {
+ if (s->fd_got_error &&
+ (get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) {
+#ifdef DEBUG_FLOPPY
+ printf("No floppy (open delayed)\n");
+#endif
+ return -EIO;
+ }
+ s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK);
+ if (s->fd < 0) {
+ s->fd_error_time = get_clock();
+ s->fd_got_error = 1;
+ if (last_media_present)
+ s->fd_media_changed = 1;
+#ifdef DEBUG_FLOPPY
+ printf("No floppy\n");
+#endif
+ return -EIO;
+ }
+#ifdef DEBUG_FLOPPY
+ printf("Floppy opened\n");
+#endif
+ }
+ if (!last_media_present)
+ s->fd_media_changed = 1;
+ s->fd_open_time = get_clock();
+ s->fd_got_error = 0;
+ return 0;
+}
+
+static int hdev_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+ BDRVRawState *s = bs->opaque;
+
+ return ioctl(s->fd, req, buf);
+}
+
+static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (fd_open(bs) < 0)
+ return NULL;
+ return paio_ioctl(bs, s->fd, req, buf, cb, opaque);
+}
+
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+static int fd_open(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+
+ /* this is just to ensure s->fd is sane (its called by io ops) */
+ if (s->fd >= 0)
+ return 0;
+ return -EIO;
+}
+#else /* !linux && !FreeBSD */
+
+static int fd_open(BlockDriverState *bs)
+{
+ return 0;
+}
+
+#endif /* !linux && !FreeBSD */
+
+static int hdev_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd;
+ int ret = 0;
+ struct stat stat_buf;
+ int64_t total_size = 0;
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, "size")) {
+ total_size = options->value.n / BDRV_SECTOR_SIZE;
+ }
+ options++;
+ }
+
+ fd = open(filename, O_WRONLY | O_BINARY);
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &stat_buf) < 0)
+ ret = -errno;
+ else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode))
+ ret = -ENODEV;
+ else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE)
+ ret = -ENOSPC;
+
+ close(fd);
+ return ret;
+}
+
+static int hdev_has_zero_init(BlockDriverState *bs)
+{
+ return 0;
+}
+
+static BlockDriver bdrv_host_device = {
+ .format_name = "host_device",
+ .protocol_name = "host_device",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe_device = hdev_probe_device,
+ .bdrv_file_open = hdev_open,
+ .bdrv_close = raw_close,
+ .bdrv_create = hdev_create,
+ .create_options = raw_create_options,
+ .bdrv_has_zero_init = hdev_has_zero_init,
+ .bdrv_flush = raw_flush,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_getlength = raw_getlength,
+
+ /* generic scsi device */
+#ifdef __linux__
+ .bdrv_ioctl = hdev_ioctl,
+ .bdrv_aio_ioctl = hdev_aio_ioctl,
+#endif
+};
+
+#ifdef __linux__
+static int floppy_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ s->type = FTYPE_FD;
+
+ /* open will not fail even if no floppy is inserted, so add O_NONBLOCK */
+ ret = raw_open_common(bs, filename, flags, O_NONBLOCK);
+ if (ret)
+ return ret;
+
+ /* close fd so that we can reopen it as needed */
+ close(s->fd);
+ s->fd = -1;
+ s->fd_media_changed = 1;
+
+ return 0;
+}
+
+static int floppy_probe_device(const char *filename)
+{
+ int fd, ret;
+ int prio = 0;
+ struct floppy_struct fdparam;
+
+ if (strstart(filename, "/dev/fd", NULL))
+ prio = 50;
+
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ goto out;
+ }
+
+ /* Attempt to detect via a floppy specific ioctl */
+ ret = ioctl(fd, FDGETPRM, &fdparam);
+ if (ret >= 0)
+ prio = 100;
+
+ close(fd);
+out:
+ return prio;
+}
+
+
+static int floppy_is_inserted(BlockDriverState *bs)
+{
+ return fd_open(bs) >= 0;
+}
+
+static int floppy_media_changed(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ /*
+ * XXX: we do not have a true media changed indication.
+ * It does not work if the floppy is changed without trying to read it.
+ */
+ fd_open(bs);
+ ret = s->fd_media_changed;
+ s->fd_media_changed = 0;
+#ifdef DEBUG_FLOPPY
+ printf("Floppy changed=%d\n", ret);
+#endif
+ return ret;
+}
+
+static int floppy_eject(BlockDriverState *bs, int eject_flag)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd;
+
+ if (s->fd >= 0) {
+ close(s->fd);
+ s->fd = -1;
+ }
+ fd = open(bs->filename, s->open_flags | O_NONBLOCK);
+ if (fd >= 0) {
+ if (ioctl(fd, FDEJECT, 0) < 0)
+ perror("FDEJECT");
+ close(fd);
+ }
+
+ return 0;
+}
+
+static BlockDriver bdrv_host_floppy = {
+ .format_name = "host_floppy",
+ .protocol_name = "host_floppy",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe_device = floppy_probe_device,
+ .bdrv_file_open = floppy_open,
+ .bdrv_close = raw_close,
+ .bdrv_create = hdev_create,
+ .create_options = raw_create_options,
+ .bdrv_has_zero_init = hdev_has_zero_init,
+ .bdrv_flush = raw_flush,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_getlength = raw_getlength,
+
+ /* removable device support */
+ .bdrv_is_inserted = floppy_is_inserted,
+ .bdrv_media_changed = floppy_media_changed,
+ .bdrv_eject = floppy_eject,
+};
+
+static int cdrom_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+
+ s->type = FTYPE_CD;
+
+ /* open will not fail even if no CD is inserted, so add O_NONBLOCK */
+ return raw_open_common(bs, filename, flags, O_NONBLOCK);
+}
+
+static int cdrom_probe_device(const char *filename)
+{
+ int fd, ret;
+ int prio = 0;
+
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ goto out;
+ }
+
+ /* Attempt to detect via a CDROM specific ioctl */
+ ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ if (ret >= 0)
+ prio = 100;
+
+ close(fd);
+out:
+ return prio;
+}
+
+static int cdrom_is_inserted(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+ if (ret == CDS_DISC_OK)
+ return 1;
+ return 0;
+}
+
+static int cdrom_eject(BlockDriverState *bs, int eject_flag)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (eject_flag) {
+ if (ioctl(s->fd, CDROMEJECT, NULL) < 0)
+ perror("CDROMEJECT");
+ } else {
+ if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0)
+ perror("CDROMEJECT");
+ }
+
+ return 0;
+}
+
+static int cdrom_set_locked(BlockDriverState *bs, int locked)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (ioctl(s->fd, CDROM_LOCKDOOR, locked) < 0) {
+ /*
+ * Note: an error can happen if the distribution automatically
+ * mounts the CD-ROM
+ */
+ /* perror("CDROM_LOCKDOOR"); */
+ }
+
+ return 0;
+}
+
+static BlockDriver bdrv_host_cdrom = {
+ .format_name = "host_cdrom",
+ .protocol_name = "host_cdrom",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe_device = cdrom_probe_device,
+ .bdrv_file_open = cdrom_open,
+ .bdrv_close = raw_close,
+ .bdrv_create = hdev_create,
+ .create_options = raw_create_options,
+ .bdrv_has_zero_init = hdev_has_zero_init,
+ .bdrv_flush = raw_flush,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_getlength = raw_getlength,
+
+ /* removable device support */
+ .bdrv_is_inserted = cdrom_is_inserted,
+ .bdrv_eject = cdrom_eject,
+ .bdrv_set_locked = cdrom_set_locked,
+
+ /* generic scsi device */
+ .bdrv_ioctl = hdev_ioctl,
+ .bdrv_aio_ioctl = hdev_aio_ioctl,
+};
+#endif /* __linux__ */
+
+#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
+static int cdrom_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ s->type = FTYPE_CD;
+
+ ret = raw_open_common(bs, filename, flags, 0);
+ if (ret)
+ return ret;
+
+ /* make sure the door isnt locked at this time */
+ ioctl(s->fd, CDIOCALLOW);
+ return 0;
+}
+
+static int cdrom_probe_device(const char *filename)
+{
+ if (strstart(filename, "/dev/cd", NULL) ||
+ strstart(filename, "/dev/acd", NULL))
+ return 100;
+ return 0;
+}
+
+static int cdrom_reopen(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int fd;
+
+ /*
+ * Force reread of possibly changed/newly loaded disc,
+ * FreeBSD seems to not notice sometimes...
+ */
+ if (s->fd >= 0)
+ close(s->fd);
+ fd = open(bs->filename, s->open_flags, 0644);
+ if (fd < 0) {
+ s->fd = -1;
+ return -EIO;
+ }
+ s->fd = fd;
+
+ /* make sure the door isnt locked at this time */
+ ioctl(s->fd, CDIOCALLOW);
+ return 0;
+}
+
+static int cdrom_is_inserted(BlockDriverState *bs)
+{
+ return raw_getlength(bs) > 0;
+}
+
+static int cdrom_eject(BlockDriverState *bs, int eject_flag)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (s->fd < 0)
+ return -ENOTSUP;
+
+ (void) ioctl(s->fd, CDIOCALLOW);
+
+ if (eject_flag) {
+ if (ioctl(s->fd, CDIOCEJECT) < 0)
+ perror("CDIOCEJECT");
+ } else {
+ if (ioctl(s->fd, CDIOCCLOSE) < 0)
+ perror("CDIOCCLOSE");
+ }
+
+ if (cdrom_reopen(bs) < 0)
+ return -ENOTSUP;
+ return 0;
+}
+
+static int cdrom_set_locked(BlockDriverState *bs, int locked)
+{
+ BDRVRawState *s = bs->opaque;
+
+ if (s->fd < 0)
+ return -ENOTSUP;
+ if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) {
+ /*
+ * Note: an error can happen if the distribution automatically
+ * mounts the CD-ROM
+ */
+ /* perror("CDROM_LOCKDOOR"); */
+ }
+
+ return 0;
+}
+
+static BlockDriver bdrv_host_cdrom = {
+ .format_name = "host_cdrom",
+ .protocol_name = "host_cdrom",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe_device = cdrom_probe_device,
+ .bdrv_file_open = cdrom_open,
+ .bdrv_close = raw_close,
+ .bdrv_create = hdev_create,
+ .create_options = raw_create_options,
+ .bdrv_has_zero_init = hdev_has_zero_init,
+ .bdrv_flush = raw_flush,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_getlength = raw_getlength,
+
+ /* removable device support */
+ .bdrv_is_inserted = cdrom_is_inserted,
+ .bdrv_eject = cdrom_eject,
+ .bdrv_set_locked = cdrom_set_locked,
+};
+#endif /* __FreeBSD__ */
+
+static void bdrv_file_init(void)
+{
+ /*
+ * Register all the drivers. Note that order is important, the driver
+ * registered last will get probed first.
+ */
+ bdrv_register(&bdrv_file);
+ bdrv_register(&bdrv_host_device);
+#ifdef __linux__
+ bdrv_register(&bdrv_host_floppy);
+ bdrv_register(&bdrv_host_cdrom);
+#endif
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ bdrv_register(&bdrv_host_cdrom);
+#endif
+}
+
+block_init(bdrv_file_init);
diff --git a/block/raw-win32.c b/block/raw-win32.c
new file mode 100644
index 0000000..c204a80
--- /dev/null
+++ b/block/raw-win32.c
@@ -0,0 +1,430 @@
+/*
+ * Block driver for RAW files (win32)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "block_int.h"
+#include "module.h"
+#include <windows.h>
+#include <winioctl.h>
+
+#define FTYPE_FILE 0
+#define FTYPE_CD 1
+#define FTYPE_HARDDISK 2
+
+typedef struct BDRVRawState {
+ HANDLE hfile;
+ int type;
+ char drive_path[16]; /* format: "d:\" */
+} BDRVRawState;
+
+int qemu_ftruncate64(int fd, int64_t length)
+{
+ LARGE_INTEGER li;
+ LONG high;
+ HANDLE h;
+ BOOL res;
+
+ if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0)
+ return -1;
+
+ h = (HANDLE)_get_osfhandle(fd);
+
+ /* get current position, ftruncate do not change position */
+ li.HighPart = 0;
+ li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT);
+ if (li.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
+ return -1;
+
+ high = length >> 32;
+ if (!SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN))
+ return -1;
+ res = SetEndOfFile(h);
+
+ /* back to old position */
+ SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN);
+ return res ? 0 : -1;
+}
+
+static int set_sparse(int fd)
+{
+ DWORD returned;
+ return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE,
+ NULL, 0, NULL, 0, &returned, NULL);
+}
+
+static int raw_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+ int access_flags;
+ DWORD overlapped;
+
+ s->type = FTYPE_FILE;
+
+ if (flags & BDRV_O_RDWR) {
+ access_flags = GENERIC_READ | GENERIC_WRITE;
+ } else {
+ access_flags = GENERIC_READ;
+ }
+
+ overlapped = FILE_ATTRIBUTE_NORMAL;
+ if ((flags & BDRV_O_NOCACHE))
+ overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
+ else if (!(flags & BDRV_O_CACHE_WB))
+ overlapped |= FILE_FLAG_WRITE_THROUGH;
+ s->hfile = CreateFile(filename, access_flags,
+ FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, overlapped, NULL);
+ if (s->hfile == INVALID_HANDLE_VALUE) {
+ int err = GetLastError();
+
+ if (err == ERROR_ACCESS_DENIED)
+ return -EACCES;
+ return -1;
+ }
+ return 0;
+}
+
+static int raw_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVRawState *s = bs->opaque;
+ OVERLAPPED ov;
+ DWORD ret_count;
+ int ret;
+ int64_t offset = sector_num * 512;
+ int count = nb_sectors * 512;
+
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset = offset;
+ ov.OffsetHigh = offset >> 32;
+ ret = ReadFile(s->hfile, buf, count, &ret_count, &ov);
+ if (!ret)
+ return ret_count;
+ if (ret_count == count)
+ ret_count = 0;
+ return ret_count;
+}
+
+static int raw_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVRawState *s = bs->opaque;
+ OVERLAPPED ov;
+ DWORD ret_count;
+ int ret;
+ int64_t offset = sector_num * 512;
+ int count = nb_sectors * 512;
+
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset = offset;
+ ov.OffsetHigh = offset >> 32;
+ ret = WriteFile(s->hfile, buf, count, &ret_count, &ov);
+ if (!ret)
+ return ret_count;
+ if (ret_count == count)
+ ret_count = 0;
+ return ret_count;
+}
+
+static int raw_flush(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ int ret;
+
+ ret = FlushFileBuffers(s->hfile);
+ if (ret == 0) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void raw_close(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ CloseHandle(s->hfile);
+}
+
+static int raw_truncate(BlockDriverState *bs, int64_t offset)
+{
+ BDRVRawState *s = bs->opaque;
+ LONG low, high;
+
+ low = offset;
+ high = offset >> 32;
+ if (!SetFilePointer(s->hfile, low, &high, FILE_BEGIN))
+ return -EIO;
+ if (!SetEndOfFile(s->hfile))
+ return -EIO;
+ return 0;
+}
+
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ LARGE_INTEGER l;
+ ULARGE_INTEGER available, total, total_free;
+ DISK_GEOMETRY_EX dg;
+ DWORD count;
+ BOOL status;
+
+ switch(s->type) {
+ case FTYPE_FILE:
+ l.LowPart = GetFileSize(s->hfile, (PDWORD)&l.HighPart);
+ if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
+ return -EIO;
+ break;
+ case FTYPE_CD:
+ if (!GetDiskFreeSpaceEx(s->drive_path, &available, &total, &total_free))
+ return -EIO;
+ l.QuadPart = total.QuadPart;
+ break;
+ case FTYPE_HARDDISK:
+ status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
+ NULL, 0, &dg, sizeof(dg), &count, NULL);
+ if (status != 0) {
+ l = dg.DiskSize;
+ }
+ break;
+ default:
+ return -EIO;
+ }
+ return l.QuadPart;
+}
+
+static int raw_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd;
+ int64_t total_size = 0;
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n / 512;
+ }
+ options++;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+ 0644);
+ if (fd < 0)
+ return -EIO;
+ set_sparse(fd);
+ ftruncate(fd, total_size * 512);
+ close(fd);
+ return 0;
+}
+
+static QEMUOptionParameter raw_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_file = {
+ .format_name = "file",
+ .protocol_name = "file",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_file_open = raw_open,
+ .bdrv_close = raw_close,
+ .bdrv_create = raw_create,
+ .bdrv_flush = raw_flush,
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_truncate = raw_truncate,
+ .bdrv_getlength = raw_getlength,
+
+ .create_options = raw_create_options,
+};
+
+/***********************************************/
+/* host device */
+
+static int find_cdrom(char *cdrom_name, int cdrom_name_size)
+{
+ char drives[256], *pdrv = drives;
+ UINT type;
+
+ memset(drives, 0, sizeof(drives));
+ GetLogicalDriveStrings(sizeof(drives), drives);
+ while(pdrv[0] != '\0') {
+ type = GetDriveType(pdrv);
+ switch(type) {
+ case DRIVE_CDROM:
+ snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]);
+ return 0;
+ break;
+ }
+ pdrv += lstrlen(pdrv) + 1;
+ }
+ return -1;
+}
+
+static int find_device_type(BlockDriverState *bs, const char *filename)
+{
+ BDRVRawState *s = bs->opaque;
+ UINT type;
+ const char *p;
+
+ if (strstart(filename, "\\\\.\\", &p) ||
+ strstart(filename, "//./", &p)) {
+ if (stristart(p, "PhysicalDrive", NULL))
+ return FTYPE_HARDDISK;
+ snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", p[0]);
+ type = GetDriveType(s->drive_path);
+ switch (type) {
+ case DRIVE_REMOVABLE:
+ case DRIVE_FIXED:
+ return FTYPE_HARDDISK;
+ case DRIVE_CDROM:
+ return FTYPE_CD;
+ default:
+ return FTYPE_FILE;
+ }
+ } else {
+ return FTYPE_FILE;
+ }
+}
+
+static int hdev_probe_device(const char *filename)
+{
+ if (strstart(filename, "/dev/cdrom", NULL))
+ return 100;
+ if (is_windows_drive(filename))
+ return 100;
+ return 0;
+}
+
+static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRawState *s = bs->opaque;
+ int access_flags, create_flags;
+ DWORD overlapped;
+ char device_name[64];
+
+ if (strstart(filename, "/dev/cdrom", NULL)) {
+ if (find_cdrom(device_name, sizeof(device_name)) < 0)
+ return -ENOENT;
+ filename = device_name;
+ } else {
+ /* transform drive letters into device name */
+ if (((filename[0] >= 'a' && filename[0] <= 'z') ||
+ (filename[0] >= 'A' && filename[0] <= 'Z')) &&
+ filename[1] == ':' && filename[2] == '\0') {
+ snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]);
+ filename = device_name;
+ }
+ }
+ s->type = find_device_type(bs, filename);
+
+ if (flags & BDRV_O_RDWR) {
+ access_flags = GENERIC_READ | GENERIC_WRITE;
+ } else {
+ access_flags = GENERIC_READ;
+ }
+ create_flags = OPEN_EXISTING;
+
+ overlapped = FILE_ATTRIBUTE_NORMAL;
+ if ((flags & BDRV_O_NOCACHE))
+ overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
+ else if (!(flags & BDRV_O_CACHE_WB))
+ overlapped |= FILE_FLAG_WRITE_THROUGH;
+ s->hfile = CreateFile(filename, access_flags,
+ FILE_SHARE_READ, NULL,
+ create_flags, overlapped, NULL);
+ if (s->hfile == INVALID_HANDLE_VALUE) {
+ int err = GetLastError();
+
+ if (err == ERROR_ACCESS_DENIED)
+ return -EACCES;
+ return -1;
+ }
+ return 0;
+}
+
+#if 0
+/***********************************************/
+/* removable device additional commands */
+
+static int raw_is_inserted(BlockDriverState *bs)
+{
+ return 1;
+}
+
+static int raw_media_changed(BlockDriverState *bs)
+{
+ return -ENOTSUP;
+}
+
+static int raw_eject(BlockDriverState *bs, int eject_flag)
+{
+ DWORD ret_count;
+
+ if (s->type == FTYPE_FILE)
+ return -ENOTSUP;
+ if (eject_flag) {
+ DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA,
+ NULL, 0, NULL, 0, &lpBytesReturned, NULL);
+ } else {
+ DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA,
+ NULL, 0, NULL, 0, &lpBytesReturned, NULL);
+ }
+}
+
+static int raw_set_locked(BlockDriverState *bs, int locked)
+{
+ return -ENOTSUP;
+}
+#endif
+
+static int hdev_has_zero_init(BlockDriverState *bs)
+{
+ return 0;
+}
+
+static BlockDriver bdrv_host_device = {
+ .format_name = "host_device",
+ .protocol_name = "host_device",
+ .instance_size = sizeof(BDRVRawState),
+ .bdrv_probe_device = hdev_probe_device,
+ .bdrv_file_open = hdev_open,
+ .bdrv_close = raw_close,
+ .bdrv_flush = raw_flush,
+ .bdrv_has_zero_init = hdev_has_zero_init,
+
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_getlength = raw_getlength,
+};
+
+static void bdrv_file_init(void)
+{
+ bdrv_register(&bdrv_file);
+ bdrv_register(&bdrv_host_device);
+}
+
+block_init(bdrv_file_init);
diff --git a/block/raw.c b/block/raw.c
new file mode 100644
index 0000000..b0f72d6
--- /dev/null
+++ b/block/raw.c
@@ -0,0 +1,156 @@
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+static int raw_open(BlockDriverState *bs, int flags)
+{
+ bs->sg = bs->file->sg;
+ return 0;
+}
+
+static int raw_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ return bdrv_read(bs->file, sector_num, buf, nb_sectors);
+}
+
+static int raw_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ return bdrv_write(bs->file, sector_num, buf, nb_sectors);
+}
+
+static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
+}
+
+static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
+}
+
+static void raw_close(BlockDriverState *bs)
+{
+}
+
+static int raw_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_flush(bs->file, cb, opaque);
+}
+
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+ return bdrv_getlength(bs->file);
+}
+
+static int raw_truncate(BlockDriverState *bs, int64_t offset)
+{
+ return bdrv_truncate(bs->file, offset);
+}
+
+static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ return 1; /* everything can be opened as raw image */
+}
+
+static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
+{
+ return bdrv_discard(bs->file, sector_num, nb_sectors);
+}
+
+static int raw_is_inserted(BlockDriverState *bs)
+{
+ return bdrv_is_inserted(bs->file);
+}
+
+static int raw_eject(BlockDriverState *bs, int eject_flag)
+{
+ return bdrv_eject(bs->file, eject_flag);
+}
+
+static int raw_set_locked(BlockDriverState *bs, int locked)
+{
+ bdrv_set_locked(bs->file, locked);
+ return 0;
+}
+
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+ return bdrv_ioctl(bs->file, req, buf);
+}
+
+static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
+}
+
+static int raw_create(const char *filename, QEMUOptionParameter *options)
+{
+ return bdrv_create_file(filename, options);
+}
+
+static QEMUOptionParameter raw_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+};
+
+static int raw_has_zero_init(BlockDriverState *bs)
+{
+ return bdrv_has_zero_init(bs->file);
+}
+
+static BlockDriver bdrv_raw = {
+ .format_name = "raw",
+
+ /* It's really 0, but we need to make qemu_malloc() happy */
+ .instance_size = 1,
+
+ .bdrv_open = raw_open,
+ .bdrv_close = raw_close,
+ .bdrv_read = raw_read,
+ .bdrv_write = raw_write,
+ .bdrv_flush = raw_flush,
+ .bdrv_probe = raw_probe,
+ .bdrv_getlength = raw_getlength,
+ .bdrv_truncate = raw_truncate,
+
+ .bdrv_aio_readv = raw_aio_readv,
+ .bdrv_aio_writev = raw_aio_writev,
+ .bdrv_aio_flush = raw_aio_flush,
+ .bdrv_discard = raw_discard,
+
+ .bdrv_is_inserted = raw_is_inserted,
+ .bdrv_eject = raw_eject,
+ .bdrv_set_locked = raw_set_locked,
+ .bdrv_ioctl = raw_ioctl,
+ .bdrv_aio_ioctl = raw_aio_ioctl,
+
+ .bdrv_create = raw_create,
+ .create_options = raw_create_options,
+ .bdrv_has_zero_init = raw_has_zero_init,
+};
+
+static void bdrv_raw_init(void)
+{
+ bdrv_register(&bdrv_raw);
+}
+
+block_init(bdrv_raw_init);
diff --git a/block/rbd.c b/block/rbd.c
new file mode 100644
index 0000000..249a590
--- /dev/null
+++ b/block/rbd.c
@@ -0,0 +1,1059 @@
+/*
+ * QEMU Block driver for RADOS (Ceph)
+ *
+ * Copyright (C) 2010 Christian Brunner <chb@muc.de>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+
+#include "rbd_types.h"
+#include "block_int.h"
+
+#include <rados/librados.h>
+
+
+
+/*
+ * When specifying the image filename use:
+ *
+ * rbd:poolname/devicename
+ *
+ * poolname must be the name of an existing rados pool
+ *
+ * devicename is the basename for all objects used to
+ * emulate the raw device.
+ *
+ * Metadata information (image size, ...) is stored in an
+ * object with the name "devicename.rbd".
+ *
+ * The raw device is split into 4MB sized objects by default.
+ * The sequencenumber is encoded in a 12 byte long hex-string,
+ * and is attached to the devicename, separated by a dot.
+ * e.g. "devicename.1234567890ab"
+ *
+ */
+
+#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER)
+
+typedef struct RBDAIOCB {
+ BlockDriverAIOCB common;
+ QEMUBH *bh;
+ int ret;
+ QEMUIOVector *qiov;
+ char *bounce;
+ int write;
+ int64_t sector_num;
+ int aiocnt;
+ int error;
+ struct BDRVRBDState *s;
+ int cancelled;
+} RBDAIOCB;
+
+typedef struct RADOSCB {
+ int rcbid;
+ RBDAIOCB *acb;
+ struct BDRVRBDState *s;
+ int done;
+ int64_t segsize;
+ char *buf;
+ int ret;
+} RADOSCB;
+
+#define RBD_FD_READ 0
+#define RBD_FD_WRITE 1
+
+typedef struct BDRVRBDState {
+ int fds[2];
+ rados_pool_t pool;
+ rados_pool_t header_pool;
+ char name[RBD_MAX_OBJ_NAME_SIZE];
+ char block_name[RBD_MAX_BLOCK_NAME_SIZE];
+ uint64_t size;
+ uint64_t objsize;
+ int qemu_aio_count;
+ int event_reader_pos;
+ RADOSCB *event_rcb;
+} BDRVRBDState;
+
+typedef struct rbd_obj_header_ondisk RbdHeader1;
+
+static void rbd_aio_bh_cb(void *opaque);
+
+static int rbd_next_tok(char *dst, int dst_len,
+ char *src, char delim,
+ const char *name,
+ char **p)
+{
+ int l;
+ char *end;
+
+ *p = NULL;
+
+ if (delim != '\0') {
+ end = strchr(src, delim);
+ if (end) {
+ *p = end + 1;
+ *end = '\0';
+ }
+ }
+ l = strlen(src);
+ if (l >= dst_len) {
+ error_report("%s too long", name);
+ return -EINVAL;
+ } else if (l == 0) {
+ error_report("%s too short", name);
+ return -EINVAL;
+ }
+
+ pstrcpy(dst, dst_len, src);
+
+ return 0;
+}
+
+static int rbd_parsename(const char *filename,
+ char *pool, int pool_len,
+ char *snap, int snap_len,
+ char *name, int name_len)
+{
+ const char *start;
+ char *p, *buf;
+ int ret;
+
+ if (!strstart(filename, "rbd:", &start)) {
+ return -EINVAL;
+ }
+
+ buf = qemu_strdup(start);
+ p = buf;
+
+ ret = rbd_next_tok(pool, pool_len, p, '/', "pool name", &p);
+ if (ret < 0 || !p) {
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = rbd_next_tok(name, name_len, p, '@', "object name", &p);
+ if (ret < 0) {
+ goto done;
+ }
+ if (!p) {
+ *snap = '\0';
+ goto done;
+ }
+
+ ret = rbd_next_tok(snap, snap_len, p, '\0', "snap name", &p);
+
+done:
+ qemu_free(buf);
+ return ret;
+}
+
+static int create_tmap_op(uint8_t op, const char *name, char **tmap_desc)
+{
+ uint32_t len = strlen(name);
+ uint32_t len_le = cpu_to_le32(len);
+ /* total_len = encoding op + name + empty buffer */
+ uint32_t total_len = 1 + (sizeof(uint32_t) + len) + sizeof(uint32_t);
+ uint8_t *desc = NULL;
+
+ desc = qemu_malloc(total_len);
+
+ *tmap_desc = (char *)desc;
+
+ *desc = op;
+ desc++;
+ memcpy(desc, &len_le, sizeof(len_le));
+ desc += sizeof(len_le);
+ memcpy(desc, name, len);
+ desc += len;
+ len = 0; /* no need for endian conversion for 0 */
+ memcpy(desc, &len, sizeof(len));
+ desc += sizeof(len);
+
+ return (char *)desc - *tmap_desc;
+}
+
+static void free_tmap_op(char *tmap_desc)
+{
+ qemu_free(tmap_desc);
+}
+
+static int rbd_register_image(rados_pool_t pool, const char *name)
+{
+ char *tmap_desc;
+ const char *dir = RBD_DIRECTORY;
+ int ret;
+
+ ret = create_tmap_op(CEPH_OSD_TMAP_SET, name, &tmap_desc);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = rados_tmap_update(pool, dir, tmap_desc, ret);
+ free_tmap_op(tmap_desc);
+
+ return ret;
+}
+
+static int touch_rbd_info(rados_pool_t pool, const char *info_oid)
+{
+ int r = rados_write(pool, info_oid, 0, NULL, 0);
+ if (r < 0) {
+ return r;
+ }
+ return 0;
+}
+
+static int rbd_assign_bid(rados_pool_t pool, uint64_t *id)
+{
+ uint64_t out[1];
+ const char *info_oid = RBD_INFO;
+
+ *id = 0;
+
+ int r = touch_rbd_info(pool, info_oid);
+ if (r < 0) {
+ return r;
+ }
+
+ r = rados_exec(pool, info_oid, "rbd", "assign_bid", NULL,
+ 0, (char *)out, sizeof(out));
+ if (r < 0) {
+ return r;
+ }
+
+ le64_to_cpus(out);
+ *id = out[0];
+
+ return 0;
+}
+
+static int rbd_create(const char *filename, QEMUOptionParameter *options)
+{
+ int64_t bytes = 0;
+ int64_t objsize;
+ uint64_t size;
+ time_t mtime;
+ uint8_t obj_order = RBD_DEFAULT_OBJ_ORDER;
+ char pool[RBD_MAX_SEG_NAME_SIZE];
+ char n[RBD_MAX_SEG_NAME_SIZE];
+ char name[RBD_MAX_OBJ_NAME_SIZE];
+ char snap_buf[RBD_MAX_SEG_NAME_SIZE];
+ char *snap = NULL;
+ RbdHeader1 header;
+ rados_pool_t p;
+ uint64_t bid;
+ uint32_t hi, lo;
+ int ret;
+
+ if (rbd_parsename(filename,
+ pool, sizeof(pool),
+ snap_buf, sizeof(snap_buf),
+ name, sizeof(name)) < 0) {
+ return -EINVAL;
+ }
+ if (snap_buf[0] != '\0') {
+ snap = snap_buf;
+ }
+
+ snprintf(n, sizeof(n), "%s%s", name, RBD_SUFFIX);
+
+ /* Read out options */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ bytes = options->value.n;
+ } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
+ if (options->value.n) {
+ objsize = options->value.n;
+ if ((objsize - 1) & objsize) { /* not a power of 2? */
+ error_report("obj size needs to be power of 2");
+ return -EINVAL;
+ }
+ if (objsize < 4096) {
+ error_report("obj size too small");
+ return -EINVAL;
+ }
+ obj_order = ffs(objsize) - 1;
+ }
+ }
+ options++;
+ }
+
+ memset(&header, 0, sizeof(header));
+ pstrcpy(header.text, sizeof(header.text), RBD_HEADER_TEXT);
+ pstrcpy(header.signature, sizeof(header.signature), RBD_HEADER_SIGNATURE);
+ pstrcpy(header.version, sizeof(header.version), RBD_HEADER_VERSION);
+ header.image_size = cpu_to_le64(bytes);
+ header.options.order = obj_order;
+ header.options.crypt_type = RBD_CRYPT_NONE;
+ header.options.comp_type = RBD_COMP_NONE;
+ header.snap_seq = 0;
+ header.snap_count = 0;
+
+ if (rados_initialize(0, NULL) < 0) {
+ error_report("error initializing");
+ return -EIO;
+ }
+
+ if (rados_open_pool(pool, &p)) {
+ error_report("error opening pool %s", pool);
+ rados_deinitialize();
+ return -EIO;
+ }
+
+ /* check for existing rbd header file */
+ ret = rados_stat(p, n, &size, &mtime);
+ if (ret == 0) {
+ ret=-EEXIST;
+ goto done;
+ }
+
+ ret = rbd_assign_bid(p, &bid);
+ if (ret < 0) {
+ error_report("failed assigning block id");
+ rados_deinitialize();
+ return -EIO;
+ }
+ hi = bid >> 32;
+ lo = bid & 0xFFFFFFFF;
+ snprintf(header.block_name, sizeof(header.block_name), "rb.%x.%x", hi, lo);
+
+ /* create header file */
+ ret = rados_write(p, n, 0, (const char *)&header, sizeof(header));
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = rbd_register_image(p, name);
+done:
+ rados_close_pool(p);
+ rados_deinitialize();
+
+ return ret;
+}
+
+/*
+ * This aio completion is being called from rbd_aio_event_reader() and
+ * runs in qemu context. It schedules a bh, but just in case the aio
+ * was not cancelled before.
+ */
+static void rbd_complete_aio(RADOSCB *rcb)
+{
+ RBDAIOCB *acb = rcb->acb;
+ int64_t r;
+
+ acb->aiocnt--;
+
+ if (acb->cancelled) {
+ if (!acb->aiocnt) {
+ qemu_vfree(acb->bounce);
+ qemu_aio_release(acb);
+ }
+ goto done;
+ }
+
+ r = rcb->ret;
+
+ if (acb->write) {
+ if (r < 0) {
+ acb->ret = r;
+ acb->error = 1;
+ } else if (!acb->error) {
+ acb->ret += rcb->segsize;
+ }
+ } else {
+ if (r == -ENOENT) {
+ memset(rcb->buf, 0, rcb->segsize);
+ if (!acb->error) {
+ acb->ret += rcb->segsize;
+ }
+ } else if (r < 0) {
+ memset(rcb->buf, 0, rcb->segsize);
+ acb->ret = r;
+ acb->error = 1;
+ } else if (r < rcb->segsize) {
+ memset(rcb->buf + r, 0, rcb->segsize - r);
+ if (!acb->error) {
+ acb->ret += rcb->segsize;
+ }
+ } else if (!acb->error) {
+ acb->ret += r;
+ }
+ }
+ /* Note that acb->bh can be NULL in case where the aio was cancelled */
+ if (!acb->aiocnt) {
+ acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
+ qemu_bh_schedule(acb->bh);
+ }
+done:
+ qemu_free(rcb);
+}
+
+/*
+ * aio fd read handler. It runs in the qemu context and calls the
+ * completion handling of completed rados aio operations.
+ */
+static void rbd_aio_event_reader(void *opaque)
+{
+ BDRVRBDState *s = opaque;
+
+ ssize_t ret;
+
+ do {
+ char *p = (char *)&s->event_rcb;
+
+ /* now read the rcb pointer that was sent from a non qemu thread */
+ if ((ret = read(s->fds[RBD_FD_READ], p + s->event_reader_pos,
+ sizeof(s->event_rcb) - s->event_reader_pos)) > 0) {
+ if (ret > 0) {
+ s->event_reader_pos += ret;
+ if (s->event_reader_pos == sizeof(s->event_rcb)) {
+ s->event_reader_pos = 0;
+ rbd_complete_aio(s->event_rcb);
+ s->qemu_aio_count --;
+ }
+ }
+ }
+ } while (ret < 0 && errno == EINTR);
+}
+
+static int rbd_aio_flush_cb(void *opaque)
+{
+ BDRVRBDState *s = opaque;
+
+ return (s->qemu_aio_count > 0);
+}
+
+
+static int rbd_set_snapc(rados_pool_t pool, const char *snap, RbdHeader1 *header)
+{
+ uint32_t snap_count = le32_to_cpu(header->snap_count);
+ rados_snap_t *snaps = NULL;
+ rados_snap_t seq;
+ uint32_t i;
+ uint64_t snap_names_len = le64_to_cpu(header->snap_names_len);
+ int r;
+ rados_snap_t snapid = 0;
+
+ if (snap_count) {
+ const char *header_snap = (const char *)&header->snaps[snap_count];
+ const char *end = header_snap + snap_names_len;
+ snaps = qemu_malloc(sizeof(rados_snap_t) * header->snap_count);
+
+ for (i=0; i < snap_count; i++) {
+ snaps[i] = le64_to_cpu(header->snaps[i].id);
+
+ if (snap && strcmp(snap, header_snap) == 0) {
+ snapid = snaps[i];
+ }
+
+ header_snap += strlen(header_snap) + 1;
+ if (header_snap > end) {
+ error_report("bad header, snapshot list broken");
+ }
+ }
+ }
+
+ if (snap && !snapid) {
+ error_report("snapshot not found");
+ qemu_free(snaps);
+ return -ENOENT;
+ }
+ seq = le32_to_cpu(header->snap_seq);
+
+ r = rados_set_snap_context(pool, seq, snaps, snap_count);
+
+ rados_set_snap(pool, snapid);
+
+ qemu_free(snaps);
+
+ return r;
+}
+
+#define BUF_READ_START_LEN 4096
+
+static int rbd_read_header(BDRVRBDState *s, char **hbuf)
+{
+ char *buf = NULL;
+ char n[RBD_MAX_SEG_NAME_SIZE];
+ uint64_t len = BUF_READ_START_LEN;
+ int r;
+
+ snprintf(n, sizeof(n), "%s%s", s->name, RBD_SUFFIX);
+
+ buf = qemu_malloc(len);
+
+ r = rados_read(s->header_pool, n, 0, buf, len);
+ if (r < 0) {
+ goto failed;
+ }
+
+ if (r < len) {
+ goto done;
+ }
+
+ qemu_free(buf);
+ buf = qemu_malloc(len);
+
+ r = rados_stat(s->header_pool, n, &len, NULL);
+ if (r < 0) {
+ goto failed;
+ }
+
+ r = rados_read(s->header_pool, n, 0, buf, len);
+ if (r < 0) {
+ goto failed;
+ }
+
+done:
+ *hbuf = buf;
+ return 0;
+
+failed:
+ qemu_free(buf);
+ return r;
+}
+
+static int rbd_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ BDRVRBDState *s = bs->opaque;
+ RbdHeader1 *header;
+ char pool[RBD_MAX_SEG_NAME_SIZE];
+ char snap_buf[RBD_MAX_SEG_NAME_SIZE];
+ char *snap = NULL;
+ char *hbuf = NULL;
+ int r;
+
+ if (rbd_parsename(filename, pool, sizeof(pool),
+ snap_buf, sizeof(snap_buf),
+ s->name, sizeof(s->name)) < 0) {
+ return -EINVAL;
+ }
+ if (snap_buf[0] != '\0') {
+ snap = snap_buf;
+ }
+
+ if ((r = rados_initialize(0, NULL)) < 0) {
+ error_report("error initializing");
+ return r;
+ }
+
+ if ((r = rados_open_pool(pool, &s->pool))) {
+ error_report("error opening pool %s", pool);
+ rados_deinitialize();
+ return r;
+ }
+
+ if ((r = rados_open_pool(pool, &s->header_pool))) {
+ error_report("error opening pool %s", pool);
+ rados_deinitialize();
+ return r;
+ }
+
+ if ((r = rbd_read_header(s, &hbuf)) < 0) {
+ error_report("error reading header from %s", s->name);
+ goto failed;
+ }
+
+ if (memcmp(hbuf + 64, RBD_HEADER_SIGNATURE, 4)) {
+ error_report("Invalid header signature");
+ r = -EMEDIUMTYPE;
+ goto failed;
+ }
+
+ if (memcmp(hbuf + 68, RBD_HEADER_VERSION, 8)) {
+ error_report("Unknown image version");
+ r = -EMEDIUMTYPE;
+ goto failed;
+ }
+
+ header = (RbdHeader1 *) hbuf;
+ s->size = le64_to_cpu(header->image_size);
+ s->objsize = 1ULL << header->options.order;
+ memcpy(s->block_name, header->block_name, sizeof(header->block_name));
+
+ r = rbd_set_snapc(s->pool, snap, header);
+ if (r < 0) {
+ error_report("failed setting snap context: %s", strerror(-r));
+ goto failed;
+ }
+
+ bs->read_only = (snap != NULL);
+
+ s->event_reader_pos = 0;
+ r = qemu_pipe(s->fds);
+ if (r < 0) {
+ error_report("error opening eventfd");
+ goto failed;
+ }
+ fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
+ fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
+ qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], rbd_aio_event_reader, NULL,
+ rbd_aio_flush_cb, NULL, s);
+
+ qemu_free(hbuf);
+
+ return 0;
+
+failed:
+ qemu_free(hbuf);
+
+ rados_close_pool(s->header_pool);
+ rados_close_pool(s->pool);
+ rados_deinitialize();
+ return r;
+}
+
+static void rbd_close(BlockDriverState *bs)
+{
+ BDRVRBDState *s = bs->opaque;
+
+ close(s->fds[0]);
+ close(s->fds[1]);
+ qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL,
+ NULL);
+
+ rados_close_pool(s->header_pool);
+ rados_close_pool(s->pool);
+ rados_deinitialize();
+}
+
+/*
+ * Cancel aio. Since we don't reference acb in a non qemu threads,
+ * it is safe to access it here.
+ */
+static void rbd_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ RBDAIOCB *acb = (RBDAIOCB *) blockacb;
+ acb->cancelled = 1;
+}
+
+static AIOPool rbd_aio_pool = {
+ .aiocb_size = sizeof(RBDAIOCB),
+ .cancel = rbd_aio_cancel,
+};
+
+/*
+ * This is the callback function for rados_aio_read and _write
+ *
+ * Note: this function is being called from a non qemu thread so
+ * we need to be careful about what we do here. Generally we only
+ * write to the block notification pipe, and do the rest of the
+ * io completion handling from rbd_aio_event_reader() which
+ * runs in a qemu context.
+ */
+static void rbd_finish_aiocb(rados_completion_t c, RADOSCB *rcb)
+{
+ int ret;
+ rcb->ret = rados_aio_get_return_value(c);
+ rados_aio_release(c);
+ while (1) {
+ fd_set wfd;
+ int fd = rcb->s->fds[RBD_FD_WRITE];
+
+ /* send the rcb pointer to the qemu thread that is responsible
+ for the aio completion. Must do it in a qemu thread context */
+ ret = write(fd, (void *)&rcb, sizeof(rcb));
+ if (ret >= 0) {
+ break;
+ }
+ if (errno == EINTR) {
+ continue;
+ }
+ if (errno != EAGAIN) {
+ break;
+ }
+
+ FD_ZERO(&wfd);
+ FD_SET(fd, &wfd);
+ do {
+ ret = select(fd + 1, NULL, &wfd, NULL, NULL);
+ } while (ret < 0 && errno == EINTR);
+ }
+
+ if (ret < 0) {
+ error_report("failed writing to acb->s->fds\n");
+ qemu_free(rcb);
+ }
+}
+
+/* Callback when all queued rados_aio requests are complete */
+
+static void rbd_aio_bh_cb(void *opaque)
+{
+ RBDAIOCB *acb = opaque;
+
+ if (!acb->write) {
+ qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
+ }
+ qemu_vfree(acb->bounce);
+ acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *rbd_aio_rw_vector(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque, int write)
+{
+ RBDAIOCB *acb;
+ RADOSCB *rcb;
+ rados_completion_t c;
+ char n[RBD_MAX_SEG_NAME_SIZE];
+ int64_t segnr, segoffs, segsize, last_segnr;
+ int64_t off, size;
+ char *buf;
+
+ BDRVRBDState *s = bs->opaque;
+
+ acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque);
+ acb->write = write;
+ acb->qiov = qiov;
+ acb->bounce = qemu_blockalign(bs, qiov->size);
+ acb->aiocnt = 0;
+ acb->ret = 0;
+ acb->error = 0;
+ acb->s = s;
+ acb->cancelled = 0;
+ acb->bh = NULL;
+
+ if (write) {
+ qemu_iovec_to_buffer(acb->qiov, acb->bounce);
+ }
+
+ buf = acb->bounce;
+
+ off = sector_num * BDRV_SECTOR_SIZE;
+ size = nb_sectors * BDRV_SECTOR_SIZE;
+ segnr = off / s->objsize;
+ segoffs = off % s->objsize;
+ segsize = s->objsize - segoffs;
+
+ last_segnr = ((off + size - 1) / s->objsize);
+ acb->aiocnt = (last_segnr - segnr) + 1;
+
+ s->qemu_aio_count += acb->aiocnt; /* All the RADOSCB */
+
+ while (size > 0) {
+ if (size < segsize) {
+ segsize = size;
+ }
+
+ snprintf(n, sizeof(n), "%s.%012" PRIx64, s->block_name,
+ segnr);
+
+ rcb = qemu_malloc(sizeof(RADOSCB));
+ rcb->done = 0;
+ rcb->acb = acb;
+ rcb->segsize = segsize;
+ rcb->buf = buf;
+ rcb->s = acb->s;
+
+ if (write) {
+ rados_aio_create_completion(rcb, NULL,
+ (rados_callback_t) rbd_finish_aiocb,
+ &c);
+ rados_aio_write(s->pool, n, segoffs, buf, segsize, c);
+ } else {
+ rados_aio_create_completion(rcb,
+ (rados_callback_t) rbd_finish_aiocb,
+ NULL, &c);
+ rados_aio_read(s->pool, n, segoffs, buf, segsize, c);
+ }
+
+ buf += segsize;
+ size -= segsize;
+ segoffs = 0;
+ segsize = s->objsize;
+ segnr++;
+ }
+
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *rbd_aio_readv(BlockDriverState * bs,
+ int64_t sector_num, QEMUIOVector * qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc * cb,
+ void *opaque)
+{
+ return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+}
+
+static BlockDriverAIOCB *rbd_aio_writev(BlockDriverState * bs,
+ int64_t sector_num, QEMUIOVector * qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc * cb,
+ void *opaque)
+{
+ return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+}
+
+static int rbd_getinfo(BlockDriverState * bs, BlockDriverInfo * bdi)
+{
+ BDRVRBDState *s = bs->opaque;
+ bdi->cluster_size = s->objsize;
+ return 0;
+}
+
+static int64_t rbd_getlength(BlockDriverState * bs)
+{
+ BDRVRBDState *s = bs->opaque;
+
+ return s->size;
+}
+
+static int rbd_snap_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
+{
+ BDRVRBDState *s = bs->opaque;
+ char inbuf[512], outbuf[128];
+ uint64_t snap_id;
+ int r;
+ char *p = inbuf;
+ char *end = inbuf + sizeof(inbuf);
+ char n[RBD_MAX_SEG_NAME_SIZE];
+ char *hbuf = NULL;
+ RbdHeader1 *header;
+
+ if (sn_info->name[0] == '\0') {
+ return -EINVAL; /* we need a name for rbd snapshots */
+ }
+
+ /*
+ * rbd snapshots are using the name as the user controlled unique identifier
+ * we can't use the rbd snapid for that purpose, as it can't be set
+ */
+ if (sn_info->id_str[0] != '\0' &&
+ strcmp(sn_info->id_str, sn_info->name) != 0) {
+ return -EINVAL;
+ }
+
+ if (strlen(sn_info->name) >= sizeof(sn_info->id_str)) {
+ return -ERANGE;
+ }
+
+ r = rados_selfmanaged_snap_create(s->header_pool, &snap_id);
+ if (r < 0) {
+ error_report("failed to create snap id: %s", strerror(-r));
+ return r;
+ }
+
+ *(uint32_t *)p = strlen(sn_info->name);
+ cpu_to_le32s((uint32_t *)p);
+ p += sizeof(uint32_t);
+ strncpy(p, sn_info->name, end - p);
+ p += strlen(p);
+ if (p + sizeof(snap_id) > end) {
+ error_report("invalid input parameter");
+ return -EINVAL;
+ }
+
+ *(uint64_t *)p = snap_id;
+ cpu_to_le64s((uint64_t *)p);
+
+ snprintf(n, sizeof(n), "%s%s", s->name, RBD_SUFFIX);
+
+ r = rados_exec(s->header_pool, n, "rbd", "snap_add", inbuf,
+ sizeof(inbuf), outbuf, sizeof(outbuf));
+ if (r < 0) {
+ error_report("rbd.snap_add execution failed failed: %s", strerror(-r));
+ return r;
+ }
+
+ sprintf(sn_info->id_str, "%s", sn_info->name);
+
+ r = rbd_read_header(s, &hbuf);
+ if (r < 0) {
+ error_report("failed reading header: %s", strerror(-r));
+ return r;
+ }
+
+ header = (RbdHeader1 *) hbuf;
+ r = rbd_set_snapc(s->pool, sn_info->name, header);
+ if (r < 0) {
+ error_report("failed setting snap context: %s", strerror(-r));
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ qemu_free(header);
+ return r;
+}
+
+static int decode32(char **p, const char *end, uint32_t *v)
+{
+ if (*p + 4 > end) {
+ return -ERANGE;
+ }
+
+ *v = *(uint32_t *)(*p);
+ le32_to_cpus(v);
+ *p += 4;
+ return 0;
+}
+
+static int decode64(char **p, const char *end, uint64_t *v)
+{
+ if (*p + 8 > end) {
+ return -ERANGE;
+ }
+
+ *v = *(uint64_t *)(*p);
+ le64_to_cpus(v);
+ *p += 8;
+ return 0;
+}
+
+static int decode_str(char **p, const char *end, char **s)
+{
+ uint32_t len;
+ int r;
+
+ if ((r = decode32(p, end, &len)) < 0) {
+ return r;
+ }
+
+ *s = qemu_malloc(len + 1);
+ memcpy(*s, *p, len);
+ *p += len;
+ (*s)[len] = '\0';
+
+ return len;
+}
+
+static int rbd_snap_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
+{
+ BDRVRBDState *s = bs->opaque;
+ char n[RBD_MAX_SEG_NAME_SIZE];
+ QEMUSnapshotInfo *sn_info, *sn_tab = NULL;
+ RbdHeader1 *header;
+ char *hbuf = NULL;
+ char *outbuf = NULL, *end, *buf;
+ uint64_t len;
+ uint64_t snap_seq;
+ uint32_t snap_count;
+ int r, i;
+
+ /* read header to estimate how much space we need to read the snap
+ * list */
+ if ((r = rbd_read_header(s, &hbuf)) < 0) {
+ goto done_err;
+ }
+ header = (RbdHeader1 *)hbuf;
+ len = le64_to_cpu(header->snap_names_len);
+ len += 1024; /* should have already been enough, but new snapshots might
+ already been created since we read the header. just allocate
+ a bit more, so that in most cases it'll suffice anyway */
+ qemu_free(hbuf);
+
+ snprintf(n, sizeof(n), "%s%s", s->name, RBD_SUFFIX);
+ while (1) {
+ qemu_free(outbuf);
+ outbuf = qemu_malloc(len);
+
+ r = rados_exec(s->header_pool, n, "rbd", "snap_list", NULL, 0,
+ outbuf, len);
+ if (r < 0) {
+ error_report("rbd.snap_list execution failed failed: %s", strerror(-r));
+ goto done_err;
+ }
+ if (r != len) {
+ break;
+ }
+
+ /* if we're here, we probably raced with some snaps creation */
+ len *= 2;
+ }
+ buf = outbuf;
+ end = buf + len;
+
+ if ((r = decode64(&buf, end, &snap_seq)) < 0) {
+ goto done_err;
+ }
+ if ((r = decode32(&buf, end, &snap_count)) < 0) {
+ goto done_err;
+ }
+
+ sn_tab = qemu_mallocz(snap_count * sizeof(QEMUSnapshotInfo));
+ for (i = 0; i < snap_count; i++) {
+ uint64_t id, image_size;
+ char *snap_name;
+
+ if ((r = decode64(&buf, end, &id)) < 0) {
+ goto done_err;
+ }
+ if ((r = decode64(&buf, end, &image_size)) < 0) {
+ goto done_err;
+ }
+ if ((r = decode_str(&buf, end, &snap_name)) < 0) {
+ goto done_err;
+ }
+
+ sn_info = sn_tab + i;
+ pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), snap_name);
+ pstrcpy(sn_info->name, sizeof(sn_info->name), snap_name);
+ qemu_free(snap_name);
+
+ sn_info->vm_state_size = image_size;
+ sn_info->date_sec = 0;
+ sn_info->date_nsec = 0;
+ sn_info->vm_clock_nsec = 0;
+ }
+ *psn_tab = sn_tab;
+ qemu_free(outbuf);
+ return snap_count;
+done_err:
+ qemu_free(sn_tab);
+ qemu_free(outbuf);
+ return r;
+}
+
+static QEMUOptionParameter rbd_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_CLUSTER_SIZE,
+ .type = OPT_SIZE,
+ .help = "RBD object size"
+ },
+ {NULL}
+};
+
+static BlockDriver bdrv_rbd = {
+ .format_name = "rbd",
+ .instance_size = sizeof(BDRVRBDState),
+ .bdrv_file_open = rbd_open,
+ .bdrv_close = rbd_close,
+ .bdrv_create = rbd_create,
+ .bdrv_get_info = rbd_getinfo,
+ .create_options = rbd_create_options,
+ .bdrv_getlength = rbd_getlength,
+ .protocol_name = "rbd",
+
+ .bdrv_aio_readv = rbd_aio_readv,
+ .bdrv_aio_writev = rbd_aio_writev,
+
+ .bdrv_snapshot_create = rbd_snap_create,
+ .bdrv_snapshot_list = rbd_snap_list,
+};
+
+static void bdrv_rbd_init(void)
+{
+ bdrv_register(&bdrv_rbd);
+}
+
+block_init(bdrv_rbd_init);
diff --git a/block/rbd_types.h b/block/rbd_types.h
new file mode 100644
index 0000000..f4cca99
--- /dev/null
+++ b/block/rbd_types.h
@@ -0,0 +1,71 @@
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2010 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.LIB.
+ *
+ */
+
+#ifndef CEPH_RBD_TYPES_H
+#define CEPH_RBD_TYPES_H
+
+
+/*
+ * rbd image 'foo' consists of objects
+ * foo.rbd - image metadata
+ * foo.00000000
+ * foo.00000001
+ * ... - data
+ */
+
+#define RBD_SUFFIX ".rbd"
+#define RBD_DIRECTORY "rbd_directory"
+#define RBD_INFO "rbd_info"
+
+#define RBD_DEFAULT_OBJ_ORDER 22 /* 4MB */
+
+#define RBD_MAX_OBJ_NAME_SIZE 96
+#define RBD_MAX_BLOCK_NAME_SIZE 24
+#define RBD_MAX_SEG_NAME_SIZE 128
+
+#define RBD_COMP_NONE 0
+#define RBD_CRYPT_NONE 0
+
+#define RBD_HEADER_TEXT "<<< Rados Block Device Image >>>\n"
+#define RBD_HEADER_SIGNATURE "RBD"
+#define RBD_HEADER_VERSION "001.005"
+
+struct rbd_info {
+ uint64_t max_id;
+} __attribute__ ((packed));
+
+struct rbd_obj_snap_ondisk {
+ uint64_t id;
+ uint64_t image_size;
+} __attribute__((packed));
+
+struct rbd_obj_header_ondisk {
+ char text[40];
+ char block_name[RBD_MAX_BLOCK_NAME_SIZE];
+ char signature[4];
+ char version[8];
+ struct {
+ uint8_t order;
+ uint8_t crypt_type;
+ uint8_t comp_type;
+ uint8_t unused;
+ } __attribute__((packed)) options;
+ uint64_t image_size;
+ uint64_t snap_seq;
+ uint32_t snap_count;
+ uint32_t reserved;
+ uint64_t snap_names_len;
+ struct rbd_obj_snap_ondisk snaps[0];
+} __attribute__((packed));
+
+
+#endif
diff --git a/block/sheepdog.c b/block/sheepdog.c
new file mode 100644
index 0000000..a54e0de
--- /dev/null
+++ b/block/sheepdog.c
@@ -0,0 +1,2037 @@
+/*
+ * Copyright (C) 2009-2010 Nippon Telegraph and Telephone Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "qemu_socket.h"
+#include "block_int.h"
+
+#define SD_PROTO_VER 0x01
+
+#define SD_DEFAULT_ADDR "localhost"
+#define SD_DEFAULT_PORT "7000"
+
+#define SD_OP_CREATE_AND_WRITE_OBJ 0x01
+#define SD_OP_READ_OBJ 0x02
+#define SD_OP_WRITE_OBJ 0x03
+
+#define SD_OP_NEW_VDI 0x11
+#define SD_OP_LOCK_VDI 0x12
+#define SD_OP_RELEASE_VDI 0x13
+#define SD_OP_GET_VDI_INFO 0x14
+#define SD_OP_READ_VDIS 0x15
+
+#define SD_FLAG_CMD_WRITE 0x01
+#define SD_FLAG_CMD_COW 0x02
+
+#define SD_RES_SUCCESS 0x00 /* Success */
+#define SD_RES_UNKNOWN 0x01 /* Unknown error */
+#define SD_RES_NO_OBJ 0x02 /* No object found */
+#define SD_RES_EIO 0x03 /* I/O error */
+#define SD_RES_VDI_EXIST 0x04 /* Vdi exists already */
+#define SD_RES_INVALID_PARMS 0x05 /* Invalid parameters */
+#define SD_RES_SYSTEM_ERROR 0x06 /* System error */
+#define SD_RES_VDI_LOCKED 0x07 /* Vdi is locked */
+#define SD_RES_NO_VDI 0x08 /* No vdi found */
+#define SD_RES_NO_BASE_VDI 0x09 /* No base vdi found */
+#define SD_RES_VDI_READ 0x0A /* Cannot read requested vdi */
+#define SD_RES_VDI_WRITE 0x0B /* Cannot write requested vdi */
+#define SD_RES_BASE_VDI_READ 0x0C /* Cannot read base vdi */
+#define SD_RES_BASE_VDI_WRITE 0x0D /* Cannot write base vdi */
+#define SD_RES_NO_TAG 0x0E /* Requested tag is not found */
+#define SD_RES_STARTUP 0x0F /* Sheepdog is on starting up */
+#define SD_RES_VDI_NOT_LOCKED 0x10 /* Vdi is not locked */
+#define SD_RES_SHUTDOWN 0x11 /* Sheepdog is shutting down */
+#define SD_RES_NO_MEM 0x12 /* Cannot allocate memory */
+#define SD_RES_FULL_VDI 0x13 /* we already have the maximum vdis */
+#define SD_RES_VER_MISMATCH 0x14 /* Protocol version mismatch */
+#define SD_RES_NO_SPACE 0x15 /* Server has no room for new objects */
+#define SD_RES_WAIT_FOR_FORMAT 0x16 /* Waiting for a format operation */
+#define SD_RES_WAIT_FOR_JOIN 0x17 /* Waiting for other nodes joining */
+#define SD_RES_JOIN_FAILED 0x18 /* Target node had failed to join sheepdog */
+
+/*
+ * Object ID rules
+ *
+ * 0 - 19 (20 bits): data object space
+ * 20 - 31 (12 bits): reserved data object space
+ * 32 - 55 (24 bits): vdi object space
+ * 56 - 59 ( 4 bits): reserved vdi object space
+ * 60 - 63 ( 4 bits): object type indentifier space
+ */
+
+#define VDI_SPACE_SHIFT 32
+#define VDI_BIT (UINT64_C(1) << 63)
+#define VMSTATE_BIT (UINT64_C(1) << 62)
+#define MAX_DATA_OBJS (UINT64_C(1) << 20)
+#define MAX_CHILDREN 1024
+#define SD_MAX_VDI_LEN 256
+#define SD_MAX_VDI_TAG_LEN 256
+#define SD_NR_VDIS (1U << 24)
+#define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22)
+#define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS)
+#define SECTOR_SIZE 512
+
+#define SD_INODE_SIZE (sizeof(SheepdogInode))
+#define CURRENT_VDI_ID 0
+
+typedef struct SheepdogReq {
+ uint8_t proto_ver;
+ uint8_t opcode;
+ uint16_t flags;
+ uint32_t epoch;
+ uint32_t id;
+ uint32_t data_length;
+ uint32_t opcode_specific[8];
+} SheepdogReq;
+
+typedef struct SheepdogRsp {
+ uint8_t proto_ver;
+ uint8_t opcode;
+ uint16_t flags;
+ uint32_t epoch;
+ uint32_t id;
+ uint32_t data_length;
+ uint32_t result;
+ uint32_t opcode_specific[7];
+} SheepdogRsp;
+
+typedef struct SheepdogObjReq {
+ uint8_t proto_ver;
+ uint8_t opcode;
+ uint16_t flags;
+ uint32_t epoch;
+ uint32_t id;
+ uint32_t data_length;
+ uint64_t oid;
+ uint64_t cow_oid;
+ uint32_t copies;
+ uint32_t rsvd;
+ uint64_t offset;
+} SheepdogObjReq;
+
+typedef struct SheepdogObjRsp {
+ uint8_t proto_ver;
+ uint8_t opcode;
+ uint16_t flags;
+ uint32_t epoch;
+ uint32_t id;
+ uint32_t data_length;
+ uint32_t result;
+ uint32_t copies;
+ uint32_t pad[6];
+} SheepdogObjRsp;
+
+typedef struct SheepdogVdiReq {
+ uint8_t proto_ver;
+ uint8_t opcode;
+ uint16_t flags;
+ uint32_t epoch;
+ uint32_t id;
+ uint32_t data_length;
+ uint64_t vdi_size;
+ uint32_t base_vdi_id;
+ uint32_t copies;
+ uint32_t snapid;
+ uint32_t pad[3];
+} SheepdogVdiReq;
+
+typedef struct SheepdogVdiRsp {
+ uint8_t proto_ver;
+ uint8_t opcode;
+ uint16_t flags;
+ uint32_t epoch;
+ uint32_t id;
+ uint32_t data_length;
+ uint32_t result;
+ uint32_t rsvd;
+ uint32_t vdi_id;
+ uint32_t pad[5];
+} SheepdogVdiRsp;
+
+typedef struct SheepdogInode {
+ char name[SD_MAX_VDI_LEN];
+ char tag[SD_MAX_VDI_TAG_LEN];
+ uint64_t ctime;
+ uint64_t snap_ctime;
+ uint64_t vm_clock_nsec;
+ uint64_t vdi_size;
+ uint64_t vm_state_size;
+ uint16_t copy_policy;
+ uint8_t nr_copies;
+ uint8_t block_size_shift;
+ uint32_t snap_id;
+ uint32_t vdi_id;
+ uint32_t parent_vdi_id;
+ uint32_t child_vdi_id[MAX_CHILDREN];
+ uint32_t data_vdi_id[MAX_DATA_OBJS];
+} SheepdogInode;
+
+/*
+ * 64 bit FNV-1a non-zero initial basis
+ */
+#define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL)
+
+/*
+ * 64 bit Fowler/Noll/Vo FNV-1a hash code
+ */
+static inline uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval)
+{
+ unsigned char *bp = buf;
+ unsigned char *be = bp + len;
+ while (bp < be) {
+ hval ^= (uint64_t) *bp++;
+ hval += (hval << 1) + (hval << 4) + (hval << 5) +
+ (hval << 7) + (hval << 8) + (hval << 40);
+ }
+ return hval;
+}
+
+static inline int is_data_obj_writeable(SheepdogInode *inode, unsigned int idx)
+{
+ return inode->vdi_id == inode->data_vdi_id[idx];
+}
+
+static inline int is_data_obj(uint64_t oid)
+{
+ return !(VDI_BIT & oid);
+}
+
+static inline uint64_t data_oid_to_idx(uint64_t oid)
+{
+ return oid & (MAX_DATA_OBJS - 1);
+}
+
+static inline uint64_t vid_to_vdi_oid(uint32_t vid)
+{
+ return VDI_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT);
+}
+
+static inline uint64_t vid_to_vmstate_oid(uint32_t vid, uint32_t idx)
+{
+ return VMSTATE_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT) | idx;
+}
+
+static inline uint64_t vid_to_data_oid(uint32_t vid, uint32_t idx)
+{
+ return ((uint64_t)vid << VDI_SPACE_SHIFT) | idx;
+}
+
+static inline int is_snapshot(struct SheepdogInode *inode)
+{
+ return !!inode->snap_ctime;
+}
+
+#undef dprintf
+#ifdef DEBUG_SDOG
+#define dprintf(fmt, args...) \
+ do { \
+ fprintf(stdout, "%s %d: " fmt, __func__, __LINE__, ##args); \
+ } while (0)
+#else
+#define dprintf(fmt, args...)
+#endif
+
+typedef struct SheepdogAIOCB SheepdogAIOCB;
+
+typedef struct AIOReq {
+ SheepdogAIOCB *aiocb;
+ unsigned int iov_offset;
+
+ uint64_t oid;
+ uint64_t base_oid;
+ uint64_t offset;
+ unsigned int data_len;
+ uint8_t flags;
+ uint32_t id;
+
+ QLIST_ENTRY(AIOReq) outstanding_aio_siblings;
+ QLIST_ENTRY(AIOReq) aioreq_siblings;
+} AIOReq;
+
+enum AIOCBState {
+ AIOCB_WRITE_UDATA,
+ AIOCB_READ_UDATA,
+};
+
+struct SheepdogAIOCB {
+ BlockDriverAIOCB common;
+
+ QEMUIOVector *qiov;
+
+ int64_t sector_num;
+ int nb_sectors;
+
+ int ret;
+ enum AIOCBState aiocb_type;
+
+ QEMUBH *bh;
+ void (*aio_done_func)(SheepdogAIOCB *);
+
+ int canceled;
+
+ QLIST_HEAD(aioreq_head, AIOReq) aioreq_head;
+};
+
+typedef struct BDRVSheepdogState {
+ SheepdogInode inode;
+
+ uint32_t min_dirty_data_idx;
+ uint32_t max_dirty_data_idx;
+
+ char name[SD_MAX_VDI_LEN];
+ int is_snapshot;
+
+ char *addr;
+ char *port;
+ int fd;
+
+ uint32_t aioreq_seq_num;
+ QLIST_HEAD(outstanding_aio_head, AIOReq) outstanding_aio_head;
+} BDRVSheepdogState;
+
+static const char * sd_strerror(int err)
+{
+ int i;
+
+ static const struct {
+ int err;
+ const char *desc;
+ } errors[] = {
+ {SD_RES_SUCCESS, "Success"},
+ {SD_RES_UNKNOWN, "Unknown error"},
+ {SD_RES_NO_OBJ, "No object found"},
+ {SD_RES_EIO, "I/O error"},
+ {SD_RES_VDI_EXIST, "VDI exists already"},
+ {SD_RES_INVALID_PARMS, "Invalid parameters"},
+ {SD_RES_SYSTEM_ERROR, "System error"},
+ {SD_RES_VDI_LOCKED, "VDI is already locked"},
+ {SD_RES_NO_VDI, "No vdi found"},
+ {SD_RES_NO_BASE_VDI, "No base VDI found"},
+ {SD_RES_VDI_READ, "Failed read the requested VDI"},
+ {SD_RES_VDI_WRITE, "Failed to write the requested VDI"},
+ {SD_RES_BASE_VDI_READ, "Failed to read the base VDI"},
+ {SD_RES_BASE_VDI_WRITE, "Failed to write the base VDI"},
+ {SD_RES_NO_TAG, "Failed to find the requested tag"},
+ {SD_RES_STARTUP, "The system is still booting"},
+ {SD_RES_VDI_NOT_LOCKED, "VDI isn't locked"},
+ {SD_RES_SHUTDOWN, "The system is shutting down"},
+ {SD_RES_NO_MEM, "Out of memory on the server"},
+ {SD_RES_FULL_VDI, "We already have the maximum vdis"},
+ {SD_RES_VER_MISMATCH, "Protocol version mismatch"},
+ {SD_RES_NO_SPACE, "Server has no space for new objects"},
+ {SD_RES_WAIT_FOR_FORMAT, "Sheepdog is waiting for a format operation"},
+ {SD_RES_WAIT_FOR_JOIN, "Sheepdog is waiting for other nodes joining"},
+ {SD_RES_JOIN_FAILED, "Target node had failed to join sheepdog"},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(errors); ++i) {
+ if (errors[i].err == err) {
+ return errors[i].desc;
+ }
+ }
+
+ return "Invalid error code";
+}
+
+/*
+ * Sheepdog I/O handling:
+ *
+ * 1. In the sd_aio_readv/writev, read/write requests are added to the
+ * QEMU Bottom Halves.
+ *
+ * 2. In sd_readv_writev_bh_cb, the callbacks of BHs, we send the I/O
+ * requests to the server and link the requests to the
+ * outstanding_list in the BDRVSheepdogState. we exits the
+ * function without waiting for receiving the response.
+ *
+ * 3. We receive the response in aio_read_response, the fd handler to
+ * the sheepdog connection. If metadata update is needed, we send
+ * the write request to the vdi object in sd_write_done, the write
+ * completion function. The AIOCB callback is not called until all
+ * the requests belonging to the AIOCB are finished.
+ */
+
+static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb,
+ uint64_t oid, unsigned int data_len,
+ uint64_t offset, uint8_t flags,
+ uint64_t base_oid, unsigned int iov_offset)
+{
+ AIOReq *aio_req;
+
+ aio_req = qemu_malloc(sizeof(*aio_req));
+ aio_req->aiocb = acb;
+ aio_req->iov_offset = iov_offset;
+ aio_req->oid = oid;
+ aio_req->base_oid = base_oid;
+ aio_req->offset = offset;
+ aio_req->data_len = data_len;
+ aio_req->flags = flags;
+ aio_req->id = s->aioreq_seq_num++;
+
+ QLIST_INSERT_HEAD(&s->outstanding_aio_head, aio_req,
+ outstanding_aio_siblings);
+ QLIST_INSERT_HEAD(&acb->aioreq_head, aio_req, aioreq_siblings);
+
+ return aio_req;
+}
+
+static inline int free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
+{
+ SheepdogAIOCB *acb = aio_req->aiocb;
+ QLIST_REMOVE(aio_req, outstanding_aio_siblings);
+ QLIST_REMOVE(aio_req, aioreq_siblings);
+ qemu_free(aio_req);
+
+ return !QLIST_EMPTY(&acb->aioreq_head);
+}
+
+static void sd_finish_aiocb(SheepdogAIOCB *acb)
+{
+ if (!acb->canceled) {
+ acb->common.cb(acb->common.opaque, acb->ret);
+ }
+ qemu_aio_release(acb);
+}
+
+static void sd_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb;
+
+ /*
+ * Sheepdog cannot cancel the requests which are already sent to
+ * the servers, so we just complete the request with -EIO here.
+ */
+ acb->common.cb(acb->common.opaque, -EIO);
+ acb->canceled = 1;
+}
+
+static AIOPool sd_aio_pool = {
+ .aiocb_size = sizeof(SheepdogAIOCB),
+ .cancel = sd_aio_cancel,
+};
+
+static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
+ int64_t sector_num, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ SheepdogAIOCB *acb;
+
+ acb = qemu_aio_get(&sd_aio_pool, bs, cb, opaque);
+
+ acb->qiov = qiov;
+
+ acb->sector_num = sector_num;
+ acb->nb_sectors = nb_sectors;
+
+ acb->aio_done_func = NULL;
+ acb->canceled = 0;
+ acb->bh = NULL;
+ acb->ret = 0;
+ QLIST_INIT(&acb->aioreq_head);
+ return acb;
+}
+
+static int sd_schedule_bh(QEMUBHFunc *cb, SheepdogAIOCB *acb)
+{
+ if (acb->bh) {
+ error_report("bug: %d %d\n", acb->aiocb_type, acb->aiocb_type);
+ return -EIO;
+ }
+
+ acb->bh = qemu_bh_new(cb, acb);
+ if (!acb->bh) {
+ error_report("oom: %d %d\n", acb->aiocb_type, acb->aiocb_type);
+ return -EIO;
+ }
+
+ qemu_bh_schedule(acb->bh);
+
+ return 0;
+}
+
+#ifdef _WIN32
+
+struct msghdr {
+ struct iovec *msg_iov;
+ size_t msg_iovlen;
+};
+
+static ssize_t sendmsg(int s, const struct msghdr *msg, int flags)
+{
+ size_t size = 0;
+ char *buf, *p;
+ int i, ret;
+
+ /* count the msg size */
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ size += msg->msg_iov[i].iov_len;
+ }
+ buf = qemu_malloc(size);
+
+ p = buf;
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ memcpy(p, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
+ p += msg->msg_iov[i].iov_len;
+ }
+
+ ret = send(s, buf, size, flags);
+
+ qemu_free(buf);
+ return ret;
+}
+
+static ssize_t recvmsg(int s, struct msghdr *msg, int flags)
+{
+ size_t size = 0;
+ char *buf, *p;
+ int i, ret;
+
+ /* count the msg size */
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ size += msg->msg_iov[i].iov_len;
+ }
+ buf = qemu_malloc(size);
+
+ ret = recv(s, buf, size, flags);
+ if (ret < 0) {
+ goto out;
+ }
+
+ p = buf;
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ memcpy(msg->msg_iov[i].iov_base, p, msg->msg_iov[i].iov_len);
+ p += msg->msg_iov[i].iov_len;
+ }
+out:
+ qemu_free(buf);
+ return ret;
+}
+
+#endif
+
+/*
+ * Send/recv data with iovec buffers
+ *
+ * This function send/recv data from/to the iovec buffer directly.
+ * The first `offset' bytes in the iovec buffer are skipped and next
+ * `len' bytes are used.
+ *
+ * For example,
+ *
+ * do_send_recv(sockfd, iov, len, offset, 1);
+ *
+ * is equals to
+ *
+ * char *buf = malloc(size);
+ * iov_to_buf(iov, iovcnt, buf, offset, size);
+ * send(sockfd, buf, size, 0);
+ * free(buf);
+ */
+static int do_send_recv(int sockfd, struct iovec *iov, int len, int offset,
+ int write)
+{
+ struct msghdr msg;
+ int ret, diff;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ len += offset;
+
+ while (iov->iov_len < len) {
+ len -= iov->iov_len;
+
+ iov++;
+ msg.msg_iovlen++;
+ }
+
+ diff = iov->iov_len - len;
+ iov->iov_len -= diff;
+
+ while (msg.msg_iov->iov_len <= offset) {
+ offset -= msg.msg_iov->iov_len;
+
+ msg.msg_iov++;
+ msg.msg_iovlen--;
+ }
+
+ msg.msg_iov->iov_base = (char *) msg.msg_iov->iov_base + offset;
+ msg.msg_iov->iov_len -= offset;
+
+ if (write) {
+ ret = sendmsg(sockfd, &msg, 0);
+ } else {
+ ret = recvmsg(sockfd, &msg, 0);
+ }
+
+ msg.msg_iov->iov_base = (char *) msg.msg_iov->iov_base - offset;
+ msg.msg_iov->iov_len += offset;
+
+ iov->iov_len += diff;
+ return ret;
+}
+
+static int connect_to_sdog(const char *addr, const char *port)
+{
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ int fd, ret;
+ struct addrinfo hints, *res, *res0;
+
+ if (!addr) {
+ addr = SD_DEFAULT_ADDR;
+ port = SD_DEFAULT_PORT;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+
+ ret = getaddrinfo(addr, port, &hints, &res0);
+ if (ret) {
+ error_report("unable to get address info %s, %s\n",
+ addr, strerror(errno));
+ return -1;
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ ret = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret) {
+ continue;
+ }
+
+ fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (fd < 0) {
+ continue;
+ }
+
+ reconnect:
+ ret = connect(fd, res->ai_addr, res->ai_addrlen);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ goto reconnect;
+ }
+ break;
+ }
+
+ dprintf("connected to %s:%s\n", addr, port);
+ goto success;
+ }
+ fd = -1;
+ error_report("failed connect to %s:%s\n", addr, port);
+success:
+ freeaddrinfo(res0);
+ return fd;
+}
+
+static int do_readv_writev(int sockfd, struct iovec *iov, int len,
+ int iov_offset, int write)
+{
+ int ret;
+again:
+ ret = do_send_recv(sockfd, iov, len, iov_offset, write);
+ if (ret < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ error_report("failed to recv a rsp, %s\n", strerror(errno));
+ return 1;
+ }
+
+ iov_offset += ret;
+ len -= ret;
+ if (len) {
+ goto again;
+ }
+
+ return 0;
+}
+
+static int do_readv(int sockfd, struct iovec *iov, int len, int iov_offset)
+{
+ return do_readv_writev(sockfd, iov, len, iov_offset, 0);
+}
+
+static int do_writev(int sockfd, struct iovec *iov, int len, int iov_offset)
+{
+ return do_readv_writev(sockfd, iov, len, iov_offset, 1);
+}
+
+static int do_read_write(int sockfd, void *buf, int len, int write)
+{
+ struct iovec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = len;
+
+ return do_readv_writev(sockfd, &iov, len, 0, write);
+}
+
+static int do_read(int sockfd, void *buf, int len)
+{
+ return do_read_write(sockfd, buf, len, 0);
+}
+
+static int do_write(int sockfd, void *buf, int len)
+{
+ return do_read_write(sockfd, buf, len, 1);
+}
+
+static int send_req(int sockfd, SheepdogReq *hdr, void *data,
+ unsigned int *wlen)
+{
+ int ret;
+ struct iovec iov[2];
+
+ iov[0].iov_base = hdr;
+ iov[0].iov_len = sizeof(*hdr);
+
+ if (*wlen) {
+ iov[1].iov_base = data;
+ iov[1].iov_len = *wlen;
+ }
+
+ ret = do_writev(sockfd, iov, sizeof(*hdr) + *wlen, 0);
+ if (ret) {
+ error_report("failed to send a req, %s\n", strerror(errno));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int do_req(int sockfd, SheepdogReq *hdr, void *data,
+ unsigned int *wlen, unsigned int *rlen)
+{
+ int ret;
+
+ ret = send_req(sockfd, hdr, data, wlen);
+ if (ret) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = do_read(sockfd, hdr, sizeof(*hdr));
+ if (ret) {
+ error_report("failed to get a rsp, %s\n", strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+ if (*rlen > hdr->data_length) {
+ *rlen = hdr->data_length;
+ }
+
+ if (*rlen) {
+ ret = do_read(sockfd, data, *rlen);
+ if (ret) {
+ error_report("failed to get the data, %s\n", strerror(errno));
+ ret = -1;
+ goto out;
+ }
+ }
+ ret = 0;
+out:
+ return ret;
+}
+
+static int add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
+ struct iovec *iov, int niov, int create,
+ enum AIOCBState aiocb_type);
+
+/*
+ * This function searchs pending requests to the object `oid', and
+ * sends them.
+ */
+static void send_pending_req(BDRVSheepdogState *s, uint64_t oid, uint32_t id)
+{
+ AIOReq *aio_req, *next;
+ SheepdogAIOCB *acb;
+ int ret;
+
+ QLIST_FOREACH_SAFE(aio_req, &s->outstanding_aio_head,
+ outstanding_aio_siblings, next) {
+ if (id == aio_req->id) {
+ continue;
+ }
+ if (aio_req->oid != oid) {
+ continue;
+ }
+
+ acb = aio_req->aiocb;
+ ret = add_aio_request(s, aio_req, acb->qiov->iov,
+ acb->qiov->niov, 0, acb->aiocb_type);
+ if (ret < 0) {
+ error_report("add_aio_request is failed\n");
+ free_aio_req(s, aio_req);
+ if (QLIST_EMPTY(&acb->aioreq_head)) {
+ sd_finish_aiocb(acb);
+ }
+ }
+ }
+}
+
+/*
+ * Receive responses of the I/O requests.
+ *
+ * This function is registered as a fd handler, and called from the
+ * main loop when s->fd is ready for reading responses.
+ */
+static void aio_read_response(void *opaque)
+{
+ SheepdogObjRsp rsp;
+ BDRVSheepdogState *s = opaque;
+ int fd = s->fd;
+ int ret;
+ AIOReq *aio_req = NULL;
+ SheepdogAIOCB *acb;
+ int rest;
+ unsigned long idx;
+
+ if (QLIST_EMPTY(&s->outstanding_aio_head)) {
+ return;
+ }
+
+ /* read a header */
+ ret = do_read(fd, &rsp, sizeof(rsp));
+ if (ret) {
+ error_report("failed to get the header, %s\n", strerror(errno));
+ return;
+ }
+
+ /* find the right aio_req from the outstanding_aio list */
+ QLIST_FOREACH(aio_req, &s->outstanding_aio_head, outstanding_aio_siblings) {
+ if (aio_req->id == rsp.id) {
+ break;
+ }
+ }
+ if (!aio_req) {
+ error_report("cannot find aio_req %x\n", rsp.id);
+ return;
+ }
+
+ acb = aio_req->aiocb;
+
+ switch (acb->aiocb_type) {
+ case AIOCB_WRITE_UDATA:
+ if (!is_data_obj(aio_req->oid)) {
+ break;
+ }
+ idx = data_oid_to_idx(aio_req->oid);
+
+ if (s->inode.data_vdi_id[idx] != s->inode.vdi_id) {
+ /*
+ * If the object is newly created one, we need to update
+ * the vdi object (metadata object). min_dirty_data_idx
+ * and max_dirty_data_idx are changed to include updated
+ * index between them.
+ */
+ s->inode.data_vdi_id[idx] = s->inode.vdi_id;
+ s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx);
+ s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx);
+
+ /*
+ * Some requests may be blocked because simultaneous
+ * create requests are not allowed, so we search the
+ * pending requests here.
+ */
+ send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx), rsp.id);
+ }
+ break;
+ case AIOCB_READ_UDATA:
+ ret = do_readv(fd, acb->qiov->iov, rsp.data_length,
+ aio_req->iov_offset);
+ if (ret) {
+ error_report("failed to get the data, %s\n", strerror(errno));
+ return;
+ }
+ break;
+ }
+
+ if (rsp.result != SD_RES_SUCCESS) {
+ acb->ret = -EIO;
+ error_report("%s\n", sd_strerror(rsp.result));
+ }
+
+ rest = free_aio_req(s, aio_req);
+ if (!rest) {
+ /*
+ * We've finished all requests which belong to the AIOCB, so
+ * we can call the callback now.
+ */
+ acb->aio_done_func(acb);
+ }
+}
+
+static int aio_flush_request(void *opaque)
+{
+ BDRVSheepdogState *s = opaque;
+
+ return !QLIST_EMPTY(&s->outstanding_aio_head);
+}
+
+#if !defined(SOL_TCP) || !defined(TCP_CORK)
+
+static int set_cork(int fd, int v)
+{
+ return 0;
+}
+
+#else
+
+static int set_cork(int fd, int v)
+{
+ return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v));
+}
+
+#endif
+
+static int set_nodelay(int fd)
+{
+ int ret, opt;
+
+ opt = 1;
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt));
+ return ret;
+}
+
+/*
+ * Return a socket discriptor to read/write objects.
+ *
+ * We cannot use this discriptor for other operations because
+ * the block driver may be on waiting response from the server.
+ */
+static int get_sheep_fd(BDRVSheepdogState *s)
+{
+ int ret, fd;
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ error_report("%s\n", strerror(errno));
+ return -1;
+ }
+
+ socket_set_nonblock(fd);
+
+ ret = set_nodelay(fd);
+ if (ret) {
+ error_report("%s\n", strerror(errno));
+ closesocket(fd);
+ return -1;
+ }
+
+ qemu_aio_set_fd_handler(fd, aio_read_response, NULL, aio_flush_request,
+ NULL, s);
+ return fd;
+}
+
+/*
+ * Parse a filename
+ *
+ * filename must be one of the following formats:
+ * 1. [vdiname]
+ * 2. [vdiname]:[snapid]
+ * 3. [vdiname]:[tag]
+ * 4. [hostname]:[port]:[vdiname]
+ * 5. [hostname]:[port]:[vdiname]:[snapid]
+ * 6. [hostname]:[port]:[vdiname]:[tag]
+ *
+ * You can boot from the snapshot images by specifying `snapid` or
+ * `tag'.
+ *
+ * You can run VMs outside the Sheepdog cluster by specifying
+ * `hostname' and `port' (experimental).
+ */
+static int parse_vdiname(BDRVSheepdogState *s, const char *filename,
+ char *vdi, uint32_t *snapid, char *tag)
+{
+ char *p, *q;
+ int nr_sep;
+
+ p = q = qemu_strdup(filename);
+
+ /* count the number of separators */
+ nr_sep = 0;
+ while (*p) {
+ if (*p == ':') {
+ nr_sep++;
+ }
+ p++;
+ }
+ p = q;
+
+ /* use the first two tokens as hostname and port number. */
+ if (nr_sep >= 2) {
+ s->addr = p;
+ p = strchr(p, ':');
+ *p++ = '\0';
+
+ s->port = p;
+ p = strchr(p, ':');
+ *p++ = '\0';
+ } else {
+ s->addr = NULL;
+ s->port = 0;
+ }
+
+ strncpy(vdi, p, SD_MAX_VDI_LEN);
+
+ p = strchr(vdi, ':');
+ if (p) {
+ *p++ = '\0';
+ *snapid = strtoul(p, NULL, 10);
+ if (*snapid == 0) {
+ strncpy(tag, p, SD_MAX_VDI_TAG_LEN);
+ }
+ } else {
+ *snapid = CURRENT_VDI_ID; /* search current vdi */
+ }
+
+ if (s->addr == NULL) {
+ qemu_free(q);
+ }
+
+ return 0;
+}
+
+static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
+ char *tag, uint32_t *vid, int for_snapshot)
+{
+ int ret, fd;
+ SheepdogVdiReq hdr;
+ SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
+ unsigned int wlen, rlen = 0;
+ char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ return -1;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, filename, SD_MAX_VDI_LEN);
+ strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN);
+
+ memset(&hdr, 0, sizeof(hdr));
+ if (for_snapshot) {
+ hdr.opcode = SD_OP_GET_VDI_INFO;
+ } else {
+ hdr.opcode = SD_OP_LOCK_VDI;
+ }
+ wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN;
+ hdr.proto_ver = SD_PROTO_VER;
+ hdr.data_length = wlen;
+ hdr.snapid = snapid;
+ hdr.flags = SD_FLAG_CMD_WRITE;
+
+ ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
+ if (ret) {
+ ret = -1;
+ goto out;
+ }
+
+ if (rsp->result != SD_RES_SUCCESS) {
+ error_report("cannot get vdi info, %s, %s %d %s\n",
+ sd_strerror(rsp->result), filename, snapid, tag);
+ ret = -1;
+ goto out;
+ }
+ *vid = rsp->vdi_id;
+
+ ret = 0;
+out:
+ closesocket(fd);
+ return ret;
+}
+
+static int add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
+ struct iovec *iov, int niov, int create,
+ enum AIOCBState aiocb_type)
+{
+ int nr_copies = s->inode.nr_copies;
+ SheepdogObjReq hdr;
+ unsigned int wlen;
+ int ret;
+ uint64_t oid = aio_req->oid;
+ unsigned int datalen = aio_req->data_len;
+ uint64_t offset = aio_req->offset;
+ uint8_t flags = aio_req->flags;
+ uint64_t old_oid = aio_req->base_oid;
+
+ if (!nr_copies) {
+ error_report("bug\n");
+ }
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ if (aiocb_type == AIOCB_READ_UDATA) {
+ wlen = 0;
+ hdr.opcode = SD_OP_READ_OBJ;
+ hdr.flags = flags;
+ } else if (create) {
+ wlen = datalen;
+ hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ;
+ hdr.flags = SD_FLAG_CMD_WRITE | flags;
+ } else {
+ wlen = datalen;
+ hdr.opcode = SD_OP_WRITE_OBJ;
+ hdr.flags = SD_FLAG_CMD_WRITE | flags;
+ }
+
+ hdr.oid = oid;
+ hdr.cow_oid = old_oid;
+ hdr.copies = s->inode.nr_copies;
+
+ hdr.data_length = datalen;
+ hdr.offset = offset;
+
+ hdr.id = aio_req->id;
+
+ set_cork(s->fd, 1);
+
+ /* send a header */
+ ret = do_write(s->fd, &hdr, sizeof(hdr));
+ if (ret) {
+ error_report("failed to send a req, %s\n", strerror(errno));
+ return -EIO;
+ }
+
+ if (wlen) {
+ ret = do_writev(s->fd, iov, wlen, aio_req->iov_offset);
+ if (ret) {
+ error_report("failed to send a data, %s\n", strerror(errno));
+ return -EIO;
+ }
+ }
+
+ set_cork(s->fd, 0);
+
+ return 0;
+}
+
+static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
+ unsigned int datalen, uint64_t offset,
+ int write, int create)
+{
+ SheepdogObjReq hdr;
+ SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr;
+ unsigned int wlen, rlen;
+ int ret;
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ if (write) {
+ wlen = datalen;
+ rlen = 0;
+ hdr.flags = SD_FLAG_CMD_WRITE;
+ if (create) {
+ hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ;
+ } else {
+ hdr.opcode = SD_OP_WRITE_OBJ;
+ }
+ } else {
+ wlen = 0;
+ rlen = datalen;
+ hdr.opcode = SD_OP_READ_OBJ;
+ }
+ hdr.oid = oid;
+ hdr.data_length = datalen;
+ hdr.offset = offset;
+ hdr.copies = copies;
+
+ ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
+ if (ret) {
+ error_report("failed to send a request to the sheep\n");
+ return -1;
+ }
+
+ switch (rsp->result) {
+ case SD_RES_SUCCESS:
+ return 0;
+ default:
+ error_report("%s\n", sd_strerror(rsp->result));
+ return -1;
+ }
+}
+
+static int read_object(int fd, char *buf, uint64_t oid, int copies,
+ unsigned int datalen, uint64_t offset)
+{
+ return read_write_object(fd, buf, oid, copies, datalen, offset, 0, 0);
+}
+
+static int write_object(int fd, char *buf, uint64_t oid, int copies,
+ unsigned int datalen, uint64_t offset, int create)
+{
+ return read_write_object(fd, buf, oid, copies, datalen, offset, 1, create);
+}
+
+static int sd_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ int ret, fd;
+ uint32_t vid = 0;
+ BDRVSheepdogState *s = bs->opaque;
+ char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
+ uint32_t snapid;
+ char *buf = NULL;
+
+ strstart(filename, "sheepdog:", (const char **)&filename);
+
+ QLIST_INIT(&s->outstanding_aio_head);
+ s->fd = -1;
+
+ memset(vdi, 0, sizeof(vdi));
+ memset(tag, 0, sizeof(tag));
+ if (parse_vdiname(s, filename, vdi, &snapid, tag) < 0) {
+ goto out;
+ }
+ s->fd = get_sheep_fd(s);
+ if (s->fd < 0) {
+ goto out;
+ }
+
+ ret = find_vdi_name(s, vdi, snapid, tag, &vid, 0);
+ if (ret) {
+ goto out;
+ }
+
+ if (snapid) {
+ dprintf("%" PRIx32 " snapshot inode was open.\n", vid);
+ s->is_snapshot = 1;
+ }
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ error_report("failed to connect\n");
+ goto out;
+ }
+
+ buf = qemu_malloc(SD_INODE_SIZE);
+ ret = read_object(fd, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0);
+
+ closesocket(fd);
+
+ if (ret) {
+ goto out;
+ }
+
+ memcpy(&s->inode, buf, sizeof(s->inode));
+ s->min_dirty_data_idx = UINT32_MAX;
+ s->max_dirty_data_idx = 0;
+
+ bs->total_sectors = s->inode.vdi_size / SECTOR_SIZE;
+ strncpy(s->name, vdi, sizeof(s->name));
+ qemu_free(buf);
+ return 0;
+out:
+ qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL);
+ if (s->fd >= 0) {
+ closesocket(s->fd);
+ }
+ qemu_free(buf);
+ return -1;
+}
+
+static int do_sd_create(char *filename, int64_t vdi_size,
+ uint32_t base_vid, uint32_t *vdi_id, int snapshot,
+ const char *addr, const char *port)
+{
+ SheepdogVdiReq hdr;
+ SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
+ int fd, ret;
+ unsigned int wlen, rlen = 0;
+ char buf[SD_MAX_VDI_LEN];
+
+ fd = connect_to_sdog(addr, port);
+ if (fd < 0) {
+ return -EIO;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, filename, SD_MAX_VDI_LEN);
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.opcode = SD_OP_NEW_VDI;
+ hdr.base_vdi_id = base_vid;
+
+ wlen = SD_MAX_VDI_LEN;
+
+ hdr.flags = SD_FLAG_CMD_WRITE;
+ hdr.snapid = snapshot;
+
+ hdr.data_length = wlen;
+ hdr.vdi_size = vdi_size;
+
+ ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
+
+ closesocket(fd);
+
+ if (ret) {
+ return -EIO;
+ }
+
+ if (rsp->result != SD_RES_SUCCESS) {
+ error_report("%s, %s\n", sd_strerror(rsp->result), filename);
+ return -EIO;
+ }
+
+ if (vdi_id) {
+ *vdi_id = rsp->vdi_id;
+ }
+
+ return 0;
+}
+
+static int sd_create(const char *filename, QEMUOptionParameter *options)
+{
+ int ret;
+ uint32_t vid = 0, base_vid = 0;
+ int64_t vdi_size = 0;
+ char *backing_file = NULL;
+ BDRVSheepdogState s;
+ char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
+ uint32_t snapid;
+
+ strstart(filename, "sheepdog:", (const char **)&filename);
+
+ memset(&s, 0, sizeof(s));
+ memset(vdi, 0, sizeof(vdi));
+ memset(tag, 0, sizeof(tag));
+ if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) {
+ error_report("invalid filename\n");
+ return -EINVAL;
+ }
+
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ vdi_size = options->value.n;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ }
+ options++;
+ }
+
+ if (vdi_size > SD_MAX_VDI_SIZE) {
+ error_report("too big image size\n");
+ return -EINVAL;
+ }
+
+ if (backing_file) {
+ BlockDriverState *bs;
+ BDRVSheepdogState *s;
+ BlockDriver *drv;
+
+ /* Currently, only Sheepdog backing image is supported. */
+ drv = bdrv_find_protocol(backing_file);
+ if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
+ error_report("backing_file must be a sheepdog image\n");
+ return -EINVAL;
+ }
+
+ ret = bdrv_file_open(&bs, backing_file, 0);
+ if (ret < 0)
+ return -EIO;
+
+ s = bs->opaque;
+
+ if (!is_snapshot(&s->inode)) {
+ error_report("cannot clone from a non snapshot vdi\n");
+ bdrv_delete(bs);
+ return -EINVAL;
+ }
+
+ base_vid = s->inode.vdi_id;
+ bdrv_delete(bs);
+ }
+
+ return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
+}
+
+static void sd_close(BlockDriverState *bs)
+{
+ BDRVSheepdogState *s = bs->opaque;
+ SheepdogVdiReq hdr;
+ SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
+ unsigned int wlen, rlen = 0;
+ int fd, ret;
+
+ dprintf("%s\n", s->name);
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ return;
+ }
+
+ memset(&hdr, 0, sizeof(hdr));
+
+ hdr.opcode = SD_OP_RELEASE_VDI;
+ wlen = strlen(s->name) + 1;
+ hdr.data_length = wlen;
+ hdr.flags = SD_FLAG_CMD_WRITE;
+
+ ret = do_req(fd, (SheepdogReq *)&hdr, s->name, &wlen, &rlen);
+
+ closesocket(fd);
+
+ if (!ret && rsp->result != SD_RES_SUCCESS &&
+ rsp->result != SD_RES_VDI_NOT_LOCKED) {
+ error_report("%s, %s\n", sd_strerror(rsp->result), s->name);
+ }
+
+ qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL);
+ closesocket(s->fd);
+ qemu_free(s->addr);
+}
+
+static int64_t sd_getlength(BlockDriverState *bs)
+{
+ BDRVSheepdogState *s = bs->opaque;
+
+ return s->inode.vdi_size;
+}
+
+static int sd_truncate(BlockDriverState *bs, int64_t offset)
+{
+ BDRVSheepdogState *s = bs->opaque;
+ int ret, fd;
+ unsigned int datalen;
+
+ if (offset < s->inode.vdi_size) {
+ error_report("shrinking is not supported\n");
+ return -EINVAL;
+ } else if (offset > SD_MAX_VDI_SIZE) {
+ error_report("too big image size\n");
+ return -EINVAL;
+ }
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ return -EIO;
+ }
+
+ /* we don't need to update entire object */
+ datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
+ s->inode.vdi_size = offset;
+ ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id),
+ s->inode.nr_copies, datalen, 0, 0);
+ close(fd);
+
+ if (ret < 0) {
+ error_report("failed to update an inode.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called after writing data objects. If we need to
+ * update metadata, this sends a write request to the vdi object.
+ * Otherwise, this calls the AIOCB callback.
+ */
+static void sd_write_done(SheepdogAIOCB *acb)
+{
+ int ret;
+ BDRVSheepdogState *s = acb->common.bs->opaque;
+ struct iovec iov;
+ AIOReq *aio_req;
+ uint32_t offset, data_len, mn, mx;
+
+ mn = s->min_dirty_data_idx;
+ mx = s->max_dirty_data_idx;
+ if (mn <= mx) {
+ /* we need to update the vdi object. */
+ offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) +
+ mn * sizeof(s->inode.data_vdi_id[0]);
+ data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]);
+
+ s->min_dirty_data_idx = UINT32_MAX;
+ s->max_dirty_data_idx = 0;
+
+ iov.iov_base = &s->inode;
+ iov.iov_len = sizeof(s->inode);
+ aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id),
+ data_len, offset, 0, 0, offset);
+ ret = add_aio_request(s, aio_req, &iov, 1, 0, AIOCB_WRITE_UDATA);
+ if (ret) {
+ free_aio_req(s, aio_req);
+ acb->ret = -EIO;
+ goto out;
+ }
+
+ acb->aio_done_func = sd_finish_aiocb;
+ acb->aiocb_type = AIOCB_WRITE_UDATA;
+ return;
+ }
+out:
+ sd_finish_aiocb(acb);
+}
+
+/*
+ * Create a writable VDI from a snapshot
+ */
+static int sd_create_branch(BDRVSheepdogState *s)
+{
+ int ret, fd;
+ uint32_t vid;
+ char *buf;
+
+ dprintf("%" PRIx32 " is snapshot.\n", s->inode.vdi_id);
+
+ buf = qemu_malloc(SD_INODE_SIZE);
+
+ ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1,
+ s->addr, s->port);
+ if (ret) {
+ goto out;
+ }
+
+ dprintf("%" PRIx32 " is created.\n", vid);
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ error_report("failed to connect\n");
+ goto out;
+ }
+
+ ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
+ SD_INODE_SIZE, 0);
+
+ closesocket(fd);
+
+ if (ret < 0) {
+ goto out;
+ }
+
+ memcpy(&s->inode, buf, sizeof(s->inode));
+
+ s->is_snapshot = 0;
+ ret = 0;
+ dprintf("%" PRIx32 " was newly created.\n", s->inode.vdi_id);
+
+out:
+ qemu_free(buf);
+
+ return ret;
+}
+
+/*
+ * Send I/O requests to the server.
+ *
+ * This function sends requests to the server, links the requests to
+ * the outstanding_list in BDRVSheepdogState, and exits without
+ * waiting the response. The responses are received in the
+ * `aio_read_response' function which is called from the main loop as
+ * a fd handler.
+ */
+static void sd_readv_writev_bh_cb(void *p)
+{
+ SheepdogAIOCB *acb = p;
+ int ret = 0;
+ unsigned long len, done = 0, total = acb->nb_sectors * SECTOR_SIZE;
+ unsigned long idx = acb->sector_num * SECTOR_SIZE / SD_DATA_OBJ_SIZE;
+ uint64_t oid;
+ uint64_t offset = (acb->sector_num * SECTOR_SIZE) % SD_DATA_OBJ_SIZE;
+ BDRVSheepdogState *s = acb->common.bs->opaque;
+ SheepdogInode *inode = &s->inode;
+ AIOReq *aio_req;
+
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+
+ if (acb->aiocb_type == AIOCB_WRITE_UDATA && s->is_snapshot) {
+ /*
+ * In the case we open the snapshot VDI, Sheepdog creates the
+ * writable VDI when we do a write operation first.
+ */
+ ret = sd_create_branch(s);
+ if (ret) {
+ acb->ret = -EIO;
+ goto out;
+ }
+ }
+
+ while (done != total) {
+ uint8_t flags = 0;
+ uint64_t old_oid = 0;
+ int create = 0;
+
+ oid = vid_to_data_oid(inode->data_vdi_id[idx], idx);
+
+ len = MIN(total - done, SD_DATA_OBJ_SIZE - offset);
+
+ if (!inode->data_vdi_id[idx]) {
+ if (acb->aiocb_type == AIOCB_READ_UDATA) {
+ goto done;
+ }
+
+ create = 1;
+ } else if (acb->aiocb_type == AIOCB_WRITE_UDATA
+ && !is_data_obj_writeable(inode, idx)) {
+ /* Copy-On-Write */
+ create = 1;
+ old_oid = oid;
+ flags = SD_FLAG_CMD_COW;
+ }
+
+ if (create) {
+ dprintf("update ino (%" PRIu32") %" PRIu64 " %" PRIu64
+ " %" PRIu64 "\n", inode->vdi_id, oid,
+ vid_to_data_oid(inode->data_vdi_id[idx], idx), idx);
+ oid = vid_to_data_oid(inode->vdi_id, idx);
+ dprintf("new oid %lx\n", oid);
+ }
+
+ aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done);
+
+ if (create) {
+ AIOReq *areq;
+ QLIST_FOREACH(areq, &s->outstanding_aio_head,
+ outstanding_aio_siblings) {
+ if (areq == aio_req) {
+ continue;
+ }
+ if (areq->oid == oid) {
+ /*
+ * Sheepdog cannot handle simultaneous create
+ * requests to the same object. So we cannot send
+ * the request until the previous request
+ * finishes.
+ */
+ aio_req->flags = 0;
+ aio_req->base_oid = 0;
+ goto done;
+ }
+ }
+ }
+
+ ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
+ create, acb->aiocb_type);
+ if (ret < 0) {
+ error_report("add_aio_request is failed\n");
+ free_aio_req(s, aio_req);
+ acb->ret = -EIO;
+ goto out;
+ }
+ done:
+ offset = 0;
+ idx++;
+ done += len;
+ }
+out:
+ if (QLIST_EMPTY(&acb->aioreq_head)) {
+ sd_finish_aiocb(acb);
+ }
+}
+
+static BlockDriverAIOCB *sd_aio_writev(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ SheepdogAIOCB *acb;
+
+ if (bs->growable && sector_num + nb_sectors > bs->total_sectors) {
+ /* TODO: shouldn't block here */
+ if (sd_truncate(bs, (sector_num + nb_sectors) * SECTOR_SIZE) < 0) {
+ return NULL;
+ }
+ bs->total_sectors = sector_num + nb_sectors;
+ }
+
+ acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, cb, opaque);
+ acb->aio_done_func = sd_write_done;
+ acb->aiocb_type = AIOCB_WRITE_UDATA;
+
+ sd_schedule_bh(sd_readv_writev_bh_cb, acb);
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *sd_aio_readv(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ SheepdogAIOCB *acb;
+ int i;
+
+ acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, cb, opaque);
+ acb->aiocb_type = AIOCB_READ_UDATA;
+ acb->aio_done_func = sd_finish_aiocb;
+
+ /*
+ * TODO: we can do better; we don't need to initialize
+ * blindly.
+ */
+ for (i = 0; i < qiov->niov; i++) {
+ memset(qiov->iov[i].iov_base, 0, qiov->iov[i].iov_len);
+ }
+
+ sd_schedule_bh(sd_readv_writev_bh_cb, acb);
+ return &acb->common;
+}
+
+static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
+{
+ BDRVSheepdogState *s = bs->opaque;
+ int ret, fd;
+ uint32_t new_vid;
+ SheepdogInode *inode;
+ unsigned int datalen;
+
+ dprintf("sn_info: name %s id_str %s s: name %s vm_state_size %d "
+ "is_snapshot %d\n", sn_info->name, sn_info->id_str,
+ s->name, sn_info->vm_state_size, s->is_snapshot);
+
+ if (s->is_snapshot) {
+ error_report("You can't create a snapshot of a snapshot VDI, "
+ "%s (%" PRIu32 ").\n", s->name, s->inode.vdi_id);
+
+ return -EINVAL;
+ }
+
+ dprintf("%s %s\n", sn_info->name, sn_info->id_str);
+
+ s->inode.vm_state_size = sn_info->vm_state_size;
+ s->inode.vm_clock_nsec = sn_info->vm_clock_nsec;
+ strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
+ /* we don't need to update entire object */
+ datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
+
+ /* refresh inode. */
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id),
+ s->inode.nr_copies, datalen, 0, 0);
+ if (ret < 0) {
+ error_report("failed to write snapshot's inode.\n");
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, 1,
+ s->addr, s->port);
+ if (ret < 0) {
+ error_report("failed to create inode for snapshot. %s\n",
+ strerror(errno));
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ inode = (SheepdogInode *)qemu_malloc(datalen);
+
+ ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid),
+ s->inode.nr_copies, datalen, 0);
+
+ if (ret < 0) {
+ error_report("failed to read new inode info. %s\n", strerror(errno));
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ memcpy(&s->inode, inode, datalen);
+ dprintf("s->inode: name %s snap_id %x oid %x\n",
+ s->inode.name, s->inode.snap_id, s->inode.vdi_id);
+
+cleanup:
+ closesocket(fd);
+ return ret;
+}
+
+static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
+{
+ BDRVSheepdogState *s = bs->opaque;
+ BDRVSheepdogState *old_s;
+ char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
+ char *buf = NULL;
+ uint32_t vid;
+ uint32_t snapid = 0;
+ int ret = -ENOENT, fd;
+
+ old_s = qemu_malloc(sizeof(BDRVSheepdogState));
+
+ memcpy(old_s, s, sizeof(BDRVSheepdogState));
+
+ memset(vdi, 0, sizeof(vdi));
+ strncpy(vdi, s->name, sizeof(vdi));
+
+ memset(tag, 0, sizeof(tag));
+ snapid = strtoul(snapshot_id, NULL, 10);
+ if (!snapid) {
+ strncpy(tag, s->name, sizeof(tag));
+ }
+
+ ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1);
+ if (ret) {
+ error_report("Failed to find_vdi_name\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ error_report("failed to connect\n");
+ goto out;
+ }
+
+ buf = qemu_malloc(SD_INODE_SIZE);
+ ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
+ SD_INODE_SIZE, 0);
+
+ closesocket(fd);
+
+ if (ret) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ memcpy(&s->inode, buf, sizeof(s->inode));
+
+ if (!s->inode.vm_state_size) {
+ error_report("Invalid snapshot\n");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ s->is_snapshot = 1;
+
+ qemu_free(buf);
+ qemu_free(old_s);
+
+ return 0;
+out:
+ /* recover bdrv_sd_state */
+ memcpy(s, old_s, sizeof(BDRVSheepdogState));
+ qemu_free(buf);
+ qemu_free(old_s);
+
+ error_report("failed to open. recover old bdrv_sd_state.\n");
+
+ return ret;
+}
+
+static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+{
+ /* FIXME: Delete specified snapshot id. */
+ return 0;
+}
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define DECLARE_BITMAP(name,bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))
+
+static inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return ((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
+}
+
+static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
+{
+ BDRVSheepdogState *s = bs->opaque;
+ SheepdogReq req;
+ int fd, nr = 1024, ret, max = BITS_TO_LONGS(SD_NR_VDIS) * sizeof(long);
+ QEMUSnapshotInfo *sn_tab = NULL;
+ unsigned wlen, rlen;
+ int found = 0;
+ static SheepdogInode inode;
+ unsigned long *vdi_inuse;
+ unsigned int start_nr;
+ uint64_t hval;
+ uint32_t vid;
+
+ vdi_inuse = qemu_malloc(max);
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ goto out;
+ }
+
+ rlen = max;
+ wlen = 0;
+
+ memset(&req, 0, sizeof(req));
+
+ req.opcode = SD_OP_READ_VDIS;
+ req.data_length = max;
+
+ ret = do_req(fd, (SheepdogReq *)&req, vdi_inuse, &wlen, &rlen);
+
+ closesocket(fd);
+ if (ret) {
+ goto out;
+ }
+
+ sn_tab = qemu_mallocz(nr * sizeof(*sn_tab));
+
+ /* calculate a vdi id with hash function */
+ hval = fnv_64a_buf(s->name, strlen(s->name), FNV1A_64_INIT);
+ start_nr = hval & (SD_NR_VDIS - 1);
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ error_report("failed to connect\n");
+ goto out;
+ }
+
+ for (vid = start_nr; found < nr; vid = (vid + 1) % SD_NR_VDIS) {
+ if (!test_bit(vid, vdi_inuse)) {
+ break;
+ }
+
+ /* we don't need to read entire object */
+ ret = read_object(fd, (char *)&inode, vid_to_vdi_oid(vid),
+ 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0);
+
+ if (ret) {
+ continue;
+ }
+
+ if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) {
+ sn_tab[found].date_sec = inode.snap_ctime >> 32;
+ sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff;
+ sn_tab[found].vm_state_size = inode.vm_state_size;
+ sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec;
+
+ snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), "%u",
+ inode.snap_id);
+ strncpy(sn_tab[found].name, inode.tag,
+ MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)));
+ found++;
+ }
+ }
+
+ closesocket(fd);
+out:
+ *psn_tab = sn_tab;
+
+ qemu_free(vdi_inuse);
+
+ return found;
+}
+
+static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
+ int64_t pos, int size, int load)
+{
+ int fd, create;
+ int ret = 0;
+ unsigned int data_len;
+ uint64_t vmstate_oid;
+ uint32_t vdi_index;
+ uint64_t offset;
+
+ fd = connect_to_sdog(s->addr, s->port);
+ if (fd < 0) {
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ while (size) {
+ vdi_index = pos / SD_DATA_OBJ_SIZE;
+ offset = pos % SD_DATA_OBJ_SIZE;
+
+ data_len = MIN(size, SD_DATA_OBJ_SIZE);
+
+ vmstate_oid = vid_to_vmstate_oid(s->inode.vdi_id, vdi_index);
+
+ create = (offset == 0);
+ if (load) {
+ ret = read_object(fd, (char *)data, vmstate_oid,
+ s->inode.nr_copies, data_len, offset);
+ } else {
+ ret = write_object(fd, (char *)data, vmstate_oid,
+ s->inode.nr_copies, data_len, offset, create);
+ }
+
+ if (ret < 0) {
+ error_report("failed to save vmstate %s\n", strerror(errno));
+ ret = -EIO;
+ goto cleanup;
+ }
+
+ pos += data_len;
+ size -= data_len;
+ ret += data_len;
+ }
+cleanup:
+ closesocket(fd);
+ return ret;
+}
+
+static int sd_save_vmstate(BlockDriverState *bs, const uint8_t *data,
+ int64_t pos, int size)
+{
+ BDRVSheepdogState *s = bs->opaque;
+
+ return do_load_save_vmstate(s, (uint8_t *)data, pos, size, 0);
+}
+
+static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data,
+ int64_t pos, int size)
+{
+ BDRVSheepdogState *s = bs->opaque;
+
+ return do_load_save_vmstate(s, data, pos, size, 1);
+}
+
+
+static QEMUOptionParameter sd_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_BACKING_FILE,
+ .type = OPT_STRING,
+ .help = "File name of a base image"
+ },
+ { NULL }
+};
+
+BlockDriver bdrv_sheepdog = {
+ .format_name = "sheepdog",
+ .protocol_name = "sheepdog",
+ .instance_size = sizeof(BDRVSheepdogState),
+ .bdrv_file_open = sd_open,
+ .bdrv_close = sd_close,
+ .bdrv_create = sd_create,
+ .bdrv_getlength = sd_getlength,
+ .bdrv_truncate = sd_truncate,
+
+ .bdrv_aio_readv = sd_aio_readv,
+ .bdrv_aio_writev = sd_aio_writev,
+
+ .bdrv_snapshot_create = sd_snapshot_create,
+ .bdrv_snapshot_goto = sd_snapshot_goto,
+ .bdrv_snapshot_delete = sd_snapshot_delete,
+ .bdrv_snapshot_list = sd_snapshot_list,
+
+ .bdrv_save_vmstate = sd_save_vmstate,
+ .bdrv_load_vmstate = sd_load_vmstate,
+
+ .create_options = sd_create_options,
+};
+
+static void bdrv_sheepdog_init(void)
+{
+ bdrv_register(&bdrv_sheepdog);
+}
+block_init(bdrv_sheepdog_init);
diff --git a/block/vdi.c b/block/vdi.c
new file mode 100644
index 0000000..116b25b
--- /dev/null
+++ b/block/vdi.c
@@ -0,0 +1,961 @@
+/*
+ * Block driver for the Virtual Disk Image (VDI) format
+ *
+ * Copyright (c) 2009 Stefan Weil
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) version 3 or any later version.
+ *
+ * This program 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 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/>.
+ *
+ * Reference:
+ * http://forums.virtualbox.org/viewtopic.php?t=8046
+ *
+ * This driver supports create / read / write operations on VDI images.
+ *
+ * Todo (see also TODO in code):
+ *
+ * Some features like snapshots are still missing.
+ *
+ * Deallocation of zero-filled blocks and shrinking images are missing, too
+ * (might be added to common block layer).
+ *
+ * Allocation of blocks could be optimized (less writes to block map and
+ * header).
+ *
+ * Read and write of adjacents blocks could be done in one operation
+ * (current code uses one operation per block (1 MiB).
+ *
+ * The code is not thread safe (missing locks for changes in header and
+ * block table, no problem with current QEMU).
+ *
+ * Hints:
+ *
+ * Blocks (VDI documentation) correspond to clusters (QEMU).
+ * QEMU's backing files could be implemented using VDI snapshot files (TODO).
+ * VDI snapshot files may also contain the complete machine state.
+ * Maybe this machine state can be converted to QEMU PC machine snapshot data.
+ *
+ * The driver keeps a block cache (little endian entries) in memory.
+ * For the standard block size (1 MiB), a 1 TiB disk will use 4 MiB RAM,
+ * so this seems to be reasonable.
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+#if defined(CONFIG_UUID)
+#include <uuid/uuid.h>
+#else
+/* TODO: move uuid emulation to some central place in QEMU. */
+#include "sysemu.h" /* UUID_FMT */
+typedef unsigned char uuid_t[16];
+void uuid_generate(uuid_t out);
+int uuid_is_null(const uuid_t uu);
+void uuid_unparse(const uuid_t uu, char *out);
+#endif
+
+/* Code configuration options. */
+
+/* Enable debug messages. */
+//~ #define CONFIG_VDI_DEBUG
+
+/* Support write operations on VDI images. */
+#define CONFIG_VDI_WRITE
+
+/* Support non-standard block (cluster) size. This is untested.
+ * Maybe it will be needed for very large images.
+ */
+//~ #define CONFIG_VDI_BLOCK_SIZE
+
+/* Support static (fixed, pre-allocated) images. */
+#define CONFIG_VDI_STATIC_IMAGE
+
+/* Command line option for static images. */
+#define BLOCK_OPT_STATIC "static"
+
+#define KiB 1024
+#define MiB (KiB * KiB)
+
+#define SECTOR_SIZE 512
+
+#if defined(CONFIG_VDI_DEBUG)
+#define logout(fmt, ...) \
+ fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+/* Image signature. */
+#define VDI_SIGNATURE 0xbeda107f
+
+/* Image version. */
+#define VDI_VERSION_1_1 0x00010001
+
+/* Image type. */
+#define VDI_TYPE_DYNAMIC 1
+#define VDI_TYPE_STATIC 2
+
+/* Innotek / SUN images use these strings in header.text:
+ * "<<< innotek VirtualBox Disk Image >>>\n"
+ * "<<< Sun xVM VirtualBox Disk Image >>>\n"
+ * "<<< Sun VirtualBox Disk Image >>>\n"
+ * The value does not matter, so QEMU created images use a different text.
+ */
+#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
+
+/* Unallocated blocks use this index (no need to convert endianess). */
+#define VDI_UNALLOCATED UINT32_MAX
+
+#if !defined(CONFIG_UUID)
+void uuid_generate(uuid_t out)
+{
+ memset(out, 0, sizeof(uuid_t));
+}
+
+int uuid_is_null(const uuid_t uu)
+{
+ uuid_t null_uuid = { 0 };
+ return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
+}
+
+void uuid_unparse(const uuid_t uu, char *out)
+{
+ snprintf(out, 37, UUID_FMT,
+ uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
+ uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
+}
+#endif
+
+typedef struct {
+ BlockDriverAIOCB common;
+ int64_t sector_num;
+ QEMUIOVector *qiov;
+ uint8_t *buf;
+ /* Total number of sectors. */
+ int nb_sectors;
+ /* Number of sectors for current AIO. */
+ int n_sectors;
+ /* New allocated block map entry. */
+ uint32_t bmap_first;
+ uint32_t bmap_last;
+ /* Buffer for new allocated block. */
+ void *block_buffer;
+ void *orig_buf;
+ int header_modified;
+ BlockDriverAIOCB *hd_aiocb;
+ struct iovec hd_iov;
+ QEMUIOVector hd_qiov;
+ QEMUBH *bh;
+} VdiAIOCB;
+
+typedef struct {
+ char text[0x40];
+ uint32_t signature;
+ uint32_t version;
+ uint32_t header_size;
+ uint32_t image_type;
+ uint32_t image_flags;
+ char description[256];
+ uint32_t offset_bmap;
+ uint32_t offset_data;
+ uint32_t cylinders; /* disk geometry, unused here */
+ uint32_t heads; /* disk geometry, unused here */
+ uint32_t sectors; /* disk geometry, unused here */
+ uint32_t sector_size;
+ uint32_t unused1;
+ uint64_t disk_size;
+ uint32_t block_size;
+ uint32_t block_extra; /* unused here */
+ uint32_t blocks_in_image;
+ uint32_t blocks_allocated;
+ uuid_t uuid_image;
+ uuid_t uuid_last_snap;
+ uuid_t uuid_link;
+ uuid_t uuid_parent;
+ uint64_t unused2[7];
+} VdiHeader;
+
+typedef struct {
+ /* The block map entries are little endian (even in memory). */
+ uint32_t *bmap;
+ /* Size of block (bytes). */
+ uint32_t block_size;
+ /* Size of block (sectors). */
+ uint32_t block_sectors;
+ /* First sector of block map. */
+ uint32_t bmap_sector;
+ /* VDI header (converted to host endianess). */
+ VdiHeader header;
+} BDRVVdiState;
+
+/* Change UUID from little endian (IPRT = VirtualBox format) to big endian
+ * format (network byte order, standard, see RFC 4122) and vice versa.
+ */
+static void uuid_convert(uuid_t uuid)
+{
+ bswap32s((uint32_t *)&uuid[0]);
+ bswap16s((uint16_t *)&uuid[4]);
+ bswap16s((uint16_t *)&uuid[6]);
+}
+
+static void vdi_header_to_cpu(VdiHeader *header)
+{
+ le32_to_cpus(&header->signature);
+ le32_to_cpus(&header->version);
+ le32_to_cpus(&header->header_size);
+ le32_to_cpus(&header->image_type);
+ le32_to_cpus(&header->image_flags);
+ le32_to_cpus(&header->offset_bmap);
+ le32_to_cpus(&header->offset_data);
+ le32_to_cpus(&header->cylinders);
+ le32_to_cpus(&header->heads);
+ le32_to_cpus(&header->sectors);
+ le32_to_cpus(&header->sector_size);
+ le64_to_cpus(&header->disk_size);
+ le32_to_cpus(&header->block_size);
+ le32_to_cpus(&header->block_extra);
+ le32_to_cpus(&header->blocks_in_image);
+ le32_to_cpus(&header->blocks_allocated);
+ uuid_convert(header->uuid_image);
+ uuid_convert(header->uuid_last_snap);
+ uuid_convert(header->uuid_link);
+ uuid_convert(header->uuid_parent);
+}
+
+static void vdi_header_to_le(VdiHeader *header)
+{
+ cpu_to_le32s(&header->signature);
+ cpu_to_le32s(&header->version);
+ cpu_to_le32s(&header->header_size);
+ cpu_to_le32s(&header->image_type);
+ cpu_to_le32s(&header->image_flags);
+ cpu_to_le32s(&header->offset_bmap);
+ cpu_to_le32s(&header->offset_data);
+ cpu_to_le32s(&header->cylinders);
+ cpu_to_le32s(&header->heads);
+ cpu_to_le32s(&header->sectors);
+ cpu_to_le32s(&header->sector_size);
+ cpu_to_le64s(&header->disk_size);
+ cpu_to_le32s(&header->block_size);
+ cpu_to_le32s(&header->block_extra);
+ cpu_to_le32s(&header->blocks_in_image);
+ cpu_to_le32s(&header->blocks_allocated);
+ cpu_to_le32s(&header->blocks_allocated);
+ uuid_convert(header->uuid_image);
+ uuid_convert(header->uuid_last_snap);
+ uuid_convert(header->uuid_link);
+ uuid_convert(header->uuid_parent);
+}
+
+#if defined(CONFIG_VDI_DEBUG)
+static void vdi_header_print(VdiHeader *header)
+{
+ char uuid[37];
+ logout("text %s", header->text);
+ logout("signature 0x%04x\n", header->signature);
+ logout("header size 0x%04x\n", header->header_size);
+ logout("image type 0x%04x\n", header->image_type);
+ logout("image flags 0x%04x\n", header->image_flags);
+ logout("description %s\n", header->description);
+ logout("offset bmap 0x%04x\n", header->offset_bmap);
+ logout("offset data 0x%04x\n", header->offset_data);
+ logout("cylinders 0x%04x\n", header->cylinders);
+ logout("heads 0x%04x\n", header->heads);
+ logout("sectors 0x%04x\n", header->sectors);
+ logout("sector size 0x%04x\n", header->sector_size);
+ logout("image size 0x%" PRIx64 " B (%" PRIu64 " MiB)\n",
+ header->disk_size, header->disk_size / MiB);
+ logout("block size 0x%04x\n", header->block_size);
+ logout("block extra 0x%04x\n", header->block_extra);
+ logout("blocks tot. 0x%04x\n", header->blocks_in_image);
+ logout("blocks all. 0x%04x\n", header->blocks_allocated);
+ uuid_unparse(header->uuid_image, uuid);
+ logout("uuid image %s\n", uuid);
+ uuid_unparse(header->uuid_last_snap, uuid);
+ logout("uuid snap %s\n", uuid);
+ uuid_unparse(header->uuid_link, uuid);
+ logout("uuid link %s\n", uuid);
+ uuid_unparse(header->uuid_parent, uuid);
+ logout("uuid parent %s\n", uuid);
+}
+#endif
+
+static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
+{
+ /* TODO: additional checks possible. */
+ BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
+ uint32_t blocks_allocated = 0;
+ uint32_t block;
+ uint32_t *bmap;
+ logout("\n");
+
+ bmap = qemu_malloc(s->header.blocks_in_image * sizeof(uint32_t));
+ memset(bmap, 0xff, s->header.blocks_in_image * sizeof(uint32_t));
+
+ /* Check block map and value of blocks_allocated. */
+ for (block = 0; block < s->header.blocks_in_image; block++) {
+ uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
+ if (bmap_entry != VDI_UNALLOCATED) {
+ if (bmap_entry < s->header.blocks_in_image) {
+ blocks_allocated++;
+ if (bmap[bmap_entry] == VDI_UNALLOCATED) {
+ bmap[bmap_entry] = bmap_entry;
+ } else {
+ fprintf(stderr, "ERROR: block index %" PRIu32
+ " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry);
+ res->corruptions++;
+ }
+ } else {
+ fprintf(stderr, "ERROR: block index %" PRIu32
+ " too large, is %" PRIu32 "\n", block, bmap_entry);
+ res->corruptions++;
+ }
+ }
+ }
+ if (blocks_allocated != s->header.blocks_allocated) {
+ fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32
+ ", should be %" PRIu32 "\n",
+ blocks_allocated, s->header.blocks_allocated);
+ res->corruptions++;
+ }
+
+ qemu_free(bmap);
+
+ return 0;
+}
+
+static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+ /* TODO: vdi_get_info would be needed for machine snapshots.
+ vm_state_offset is still missing. */
+ BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
+ logout("\n");
+ bdi->cluster_size = s->block_size;
+ bdi->vm_state_offset = 0;
+ return 0;
+}
+
+static int vdi_make_empty(BlockDriverState *bs)
+{
+ /* TODO: missing code. */
+ logout("\n");
+ /* The return value for missing code must be 0, see block.c. */
+ return 0;
+}
+
+static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ const VdiHeader *header = (const VdiHeader *)buf;
+ int result = 0;
+
+ logout("\n");
+
+ if (buf_size < sizeof(*header)) {
+ /* Header too small, no VDI. */
+ } else if (le32_to_cpu(header->signature) == VDI_SIGNATURE) {
+ result = 100;
+ }
+
+ if (result == 0) {
+ logout("no vdi image\n");
+ } else {
+ logout("%s", header->text);
+ }
+
+ return result;
+}
+
+static int vdi_open(BlockDriverState *bs, int flags)
+{
+ BDRVVdiState *s = bs->opaque;
+ VdiHeader header;
+ size_t bmap_size;
+
+ logout("\n");
+
+ if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) {
+ goto fail;
+ }
+
+ vdi_header_to_cpu(&header);
+#if defined(CONFIG_VDI_DEBUG)
+ vdi_header_print(&header);
+#endif
+
+ if (header.disk_size % SECTOR_SIZE != 0) {
+ /* 'VBoxManage convertfromraw' can create images with odd disk sizes.
+ We accept them but round the disk size to the next multiple of
+ SECTOR_SIZE. */
+ logout("odd disk size %" PRIu64 " B, round up\n", header.disk_size);
+ header.disk_size += SECTOR_SIZE - 1;
+ header.disk_size &= ~(SECTOR_SIZE - 1);
+ }
+
+ if (header.version != VDI_VERSION_1_1) {
+ logout("unsupported version %u.%u\n",
+ header.version >> 16, header.version & 0xffff);
+ goto fail;
+ } else if (header.offset_bmap % SECTOR_SIZE != 0) {
+ /* We only support block maps which start on a sector boundary. */
+ logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
+ goto fail;
+ } else if (header.offset_data % SECTOR_SIZE != 0) {
+ /* We only support data blocks which start on a sector boundary. */
+ logout("unsupported data offset 0x%x B\n", header.offset_data);
+ goto fail;
+ } else if (header.sector_size != SECTOR_SIZE) {
+ logout("unsupported sector size %u B\n", header.sector_size);
+ goto fail;
+ } else if (header.block_size != 1 * MiB) {
+ logout("unsupported block size %u B\n", header.block_size);
+ goto fail;
+ } else if (header.disk_size >
+ (uint64_t)header.blocks_in_image * header.block_size) {
+ logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
+ goto fail;
+ } else if (!uuid_is_null(header.uuid_link)) {
+ logout("link uuid != 0, unsupported\n");
+ goto fail;
+ } else if (!uuid_is_null(header.uuid_parent)) {
+ logout("parent uuid != 0, unsupported\n");
+ goto fail;
+ }
+
+ bs->total_sectors = header.disk_size / SECTOR_SIZE;
+
+ s->block_size = header.block_size;
+ s->block_sectors = header.block_size / SECTOR_SIZE;
+ s->bmap_sector = header.offset_bmap / SECTOR_SIZE;
+ s->header = header;
+
+ bmap_size = header.blocks_in_image * sizeof(uint32_t);
+ bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ if (bmap_size > 0) {
+ s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE);
+ }
+ if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) {
+ goto fail_free_bmap;
+ }
+
+ return 0;
+
+ fail_free_bmap:
+ qemu_free(s->bmap);
+
+ fail:
+ return -1;
+}
+
+static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
+ BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
+ size_t bmap_index = sector_num / s->block_sectors;
+ size_t sector_in_block = sector_num % s->block_sectors;
+ int n_sectors = s->block_sectors - sector_in_block;
+ uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
+ logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum);
+ if (n_sectors > nb_sectors) {
+ n_sectors = nb_sectors;
+ }
+ *pnum = n_sectors;
+ return bmap_entry != VDI_UNALLOCATED;
+}
+
+static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ /* TODO: This code is untested. How can I get it executed? */
+ VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common);
+ logout("\n");
+ if (acb->hd_aiocb) {
+ bdrv_aio_cancel(acb->hd_aiocb);
+ }
+ qemu_aio_release(acb);
+}
+
+static AIOPool vdi_aio_pool = {
+ .aiocb_size = sizeof(VdiAIOCB),
+ .cancel = vdi_aio_cancel,
+};
+
+static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int is_write)
+{
+ VdiAIOCB *acb;
+
+ logout("%p, %" PRId64 ", %p, %d, %p, %p, %d\n",
+ bs, sector_num, qiov, nb_sectors, cb, opaque, is_write);
+
+ acb = qemu_aio_get(&vdi_aio_pool, bs, cb, opaque);
+ if (acb) {
+ acb->hd_aiocb = NULL;
+ acb->sector_num = sector_num;
+ acb->qiov = qiov;
+ if (qiov->niov > 1) {
+ acb->buf = qemu_blockalign(bs, qiov->size);
+ acb->orig_buf = acb->buf;
+ if (is_write) {
+ qemu_iovec_to_buffer(qiov, acb->buf);
+ }
+ } else {
+ acb->buf = (uint8_t *)qiov->iov->iov_base;
+ }
+ acb->nb_sectors = nb_sectors;
+ acb->n_sectors = 0;
+ acb->bmap_first = VDI_UNALLOCATED;
+ acb->bmap_last = VDI_UNALLOCATED;
+ acb->block_buffer = NULL;
+ acb->header_modified = 0;
+ }
+ return acb;
+}
+
+static int vdi_schedule_bh(QEMUBHFunc *cb, VdiAIOCB *acb)
+{
+ logout("\n");
+
+ if (acb->bh) {
+ return -EIO;
+ }
+
+ acb->bh = qemu_bh_new(cb, acb);
+ if (!acb->bh) {
+ return -EIO;
+ }
+
+ qemu_bh_schedule(acb->bh);
+
+ return 0;
+}
+
+static void vdi_aio_read_cb(void *opaque, int ret);
+
+static void vdi_aio_read_bh(void *opaque)
+{
+ VdiAIOCB *acb = opaque;
+ logout("\n");
+ qemu_bh_delete(acb->bh);
+ acb->bh = NULL;
+ vdi_aio_read_cb(opaque, 0);
+}
+
+static void vdi_aio_read_cb(void *opaque, int ret)
+{
+ VdiAIOCB *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVVdiState *s = bs->opaque;
+ uint32_t bmap_entry;
+ uint32_t block_index;
+ uint32_t sector_in_block;
+ uint32_t n_sectors;
+
+ logout("%u sectors read\n", acb->n_sectors);
+
+ acb->hd_aiocb = NULL;
+
+ if (ret < 0) {
+ goto done;
+ }
+
+ acb->nb_sectors -= acb->n_sectors;
+
+ if (acb->nb_sectors == 0) {
+ /* request completed */
+ ret = 0;
+ goto done;
+ }
+
+ acb->sector_num += acb->n_sectors;
+ acb->buf += acb->n_sectors * SECTOR_SIZE;
+
+ block_index = acb->sector_num / s->block_sectors;
+ sector_in_block = acb->sector_num % s->block_sectors;
+ n_sectors = s->block_sectors - sector_in_block;
+ if (n_sectors > acb->nb_sectors) {
+ n_sectors = acb->nb_sectors;
+ }
+
+ logout("will read %u sectors starting at sector %" PRIu64 "\n",
+ n_sectors, acb->sector_num);
+
+ /* prepare next AIO request */
+ acb->n_sectors = n_sectors;
+ bmap_entry = le32_to_cpu(s->bmap[block_index]);
+ if (bmap_entry == VDI_UNALLOCATED) {
+ /* Block not allocated, return zeros, no need to wait. */
+ memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
+ ret = vdi_schedule_bh(vdi_aio_read_bh, acb);
+ if (ret < 0) {
+ goto done;
+ }
+ } else {
+ uint64_t offset = s->header.offset_data / SECTOR_SIZE +
+ (uint64_t)bmap_entry * s->block_sectors +
+ sector_in_block;
+ acb->hd_iov.iov_base = (void *)acb->buf;
+ acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov,
+ n_sectors, vdi_aio_read_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ goto done;
+ }
+ }
+ return;
+done:
+ if (acb->qiov->niov > 1) {
+ qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
+ qemu_vfree(acb->orig_buf);
+ }
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ VdiAIOCB *acb;
+ logout("\n");
+ acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
+ if (!acb) {
+ return NULL;
+ }
+ vdi_aio_read_cb(acb, 0);
+ return &acb->common;
+}
+
+static void vdi_aio_write_cb(void *opaque, int ret)
+{
+ VdiAIOCB *acb = opaque;
+ BlockDriverState *bs = acb->common.bs;
+ BDRVVdiState *s = bs->opaque;
+ uint32_t bmap_entry;
+ uint32_t block_index;
+ uint32_t sector_in_block;
+ uint32_t n_sectors;
+
+ acb->hd_aiocb = NULL;
+
+ if (ret < 0) {
+ goto done;
+ }
+
+ acb->nb_sectors -= acb->n_sectors;
+ acb->sector_num += acb->n_sectors;
+ acb->buf += acb->n_sectors * SECTOR_SIZE;
+
+ if (acb->nb_sectors == 0) {
+ logout("finished data write\n");
+ acb->n_sectors = 0;
+ if (acb->header_modified) {
+ VdiHeader *header = acb->block_buffer;
+ logout("now writing modified header\n");
+ assert(acb->bmap_first != VDI_UNALLOCATED);
+ *header = s->header;
+ vdi_header_to_le(header);
+ acb->header_modified = 0;
+ acb->hd_iov.iov_base = acb->block_buffer;
+ acb->hd_iov.iov_len = SECTOR_SIZE;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1,
+ vdi_aio_write_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ goto done;
+ }
+ return;
+ } else if (acb->bmap_first != VDI_UNALLOCATED) {
+ /* One or more new blocks were allocated. */
+ uint64_t offset;
+ uint32_t bmap_first;
+ uint32_t bmap_last;
+ qemu_free(acb->block_buffer);
+ acb->block_buffer = NULL;
+ bmap_first = acb->bmap_first;
+ bmap_last = acb->bmap_last;
+ logout("now writing modified block map entry %u...%u\n",
+ bmap_first, bmap_last);
+ /* Write modified sectors from block map. */
+ bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
+ bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
+ n_sectors = bmap_last - bmap_first + 1;
+ offset = s->bmap_sector + bmap_first;
+ acb->bmap_first = VDI_UNALLOCATED;
+ acb->hd_iov.iov_base = (void *)((uint8_t *)&s->bmap[0] +
+ bmap_first * SECTOR_SIZE);
+ acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ logout("will write %u block map sectors starting from entry %u\n",
+ n_sectors, bmap_first);
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
+ n_sectors, vdi_aio_write_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ goto done;
+ }
+ return;
+ }
+ ret = 0;
+ goto done;
+ }
+
+ logout("%u sectors written\n", acb->n_sectors);
+
+ block_index = acb->sector_num / s->block_sectors;
+ sector_in_block = acb->sector_num % s->block_sectors;
+ n_sectors = s->block_sectors - sector_in_block;
+ if (n_sectors > acb->nb_sectors) {
+ n_sectors = acb->nb_sectors;
+ }
+
+ logout("will write %u sectors starting at sector %" PRIu64 "\n",
+ n_sectors, acb->sector_num);
+
+ /* prepare next AIO request */
+ acb->n_sectors = n_sectors;
+ bmap_entry = le32_to_cpu(s->bmap[block_index]);
+ if (bmap_entry == VDI_UNALLOCATED) {
+ /* Allocate new block and write to it. */
+ uint64_t offset;
+ uint8_t *block;
+ bmap_entry = s->header.blocks_allocated;
+ s->bmap[block_index] = cpu_to_le32(bmap_entry);
+ s->header.blocks_allocated++;
+ offset = s->header.offset_data / SECTOR_SIZE +
+ (uint64_t)bmap_entry * s->block_sectors;
+ block = acb->block_buffer;
+ if (block == NULL) {
+ block = qemu_mallocz(s->block_size);
+ acb->block_buffer = block;
+ acb->bmap_first = block_index;
+ assert(!acb->header_modified);
+ acb->header_modified = 1;
+ }
+ acb->bmap_last = block_index;
+ memcpy(block + sector_in_block * SECTOR_SIZE,
+ acb->buf, n_sectors * SECTOR_SIZE);
+ acb->hd_iov.iov_base = (void *)block;
+ acb->hd_iov.iov_len = s->block_size;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset,
+ &acb->hd_qiov, s->block_sectors,
+ vdi_aio_write_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ goto done;
+ }
+ } else {
+ uint64_t offset = s->header.offset_data / SECTOR_SIZE +
+ (uint64_t)bmap_entry * s->block_sectors +
+ sector_in_block;
+ acb->hd_iov.iov_base = (void *)acb->buf;
+ acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
+ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
+ n_sectors, vdi_aio_write_cb, acb);
+ if (acb->hd_aiocb == NULL) {
+ goto done;
+ }
+ }
+
+ return;
+
+done:
+ if (acb->qiov->niov > 1) {
+ qemu_vfree(acb->orig_buf);
+ }
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ VdiAIOCB *acb;
+ logout("\n");
+ acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
+ if (!acb) {
+ return NULL;
+ }
+ vdi_aio_write_cb(acb, 0);
+ return &acb->common;
+}
+
+static int vdi_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd;
+ int result = 0;
+ uint64_t bytes = 0;
+ uint32_t blocks;
+ size_t block_size = 1 * MiB;
+ uint32_t image_type = VDI_TYPE_DYNAMIC;
+ VdiHeader header;
+ size_t i;
+ size_t bmap_size;
+ uint32_t *bmap;
+
+ logout("\n");
+
+ /* Read out options. */
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ bytes = options->value.n;
+#if defined(CONFIG_VDI_BLOCK_SIZE)
+ } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
+ if (options->value.n) {
+ /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
+ block_size = options->value.n;
+ }
+#endif
+#if defined(CONFIG_VDI_STATIC_IMAGE)
+ } else if (!strcmp(options->name, BLOCK_OPT_STATIC)) {
+ if (options->value.n) {
+ image_type = VDI_TYPE_STATIC;
+ }
+#endif
+ }
+ options++;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0) {
+ return -errno;
+ }
+
+ /* We need enough blocks to store the given disk size,
+ so always round up. */
+ blocks = (bytes + block_size - 1) / block_size;
+
+ bmap_size = blocks * sizeof(uint32_t);
+ bmap_size = ((bmap_size + SECTOR_SIZE - 1) & ~(SECTOR_SIZE -1));
+
+ memset(&header, 0, sizeof(header));
+ pstrcpy(header.text, sizeof(header.text), VDI_TEXT);
+ header.signature = VDI_SIGNATURE;
+ header.version = VDI_VERSION_1_1;
+ header.header_size = 0x180;
+ header.image_type = image_type;
+ header.offset_bmap = 0x200;
+ header.offset_data = 0x200 + bmap_size;
+ header.sector_size = SECTOR_SIZE;
+ header.disk_size = bytes;
+ header.block_size = block_size;
+ header.blocks_in_image = blocks;
+ if (image_type == VDI_TYPE_STATIC) {
+ header.blocks_allocated = blocks;
+ }
+ uuid_generate(header.uuid_image);
+ uuid_generate(header.uuid_last_snap);
+ /* There is no need to set header.uuid_link or header.uuid_parent here. */
+#if defined(CONFIG_VDI_DEBUG)
+ vdi_header_print(&header);
+#endif
+ vdi_header_to_le(&header);
+ if (write(fd, &header, sizeof(header)) < 0) {
+ result = -errno;
+ }
+
+ bmap = NULL;
+ if (bmap_size > 0) {
+ bmap = (uint32_t *)qemu_mallocz(bmap_size);
+ }
+ for (i = 0; i < blocks; i++) {
+ if (image_type == VDI_TYPE_STATIC) {
+ bmap[i] = i;
+ } else {
+ bmap[i] = VDI_UNALLOCATED;
+ }
+ }
+ if (write(fd, bmap, bmap_size) < 0) {
+ result = -errno;
+ }
+ qemu_free(bmap);
+ if (image_type == VDI_TYPE_STATIC) {
+ if (ftruncate(fd, sizeof(header) + bmap_size + blocks * block_size)) {
+ result = -errno;
+ }
+ }
+
+ if (close(fd) < 0) {
+ result = -errno;
+ }
+
+ return result;
+}
+
+static void vdi_close(BlockDriverState *bs)
+{
+}
+
+static int vdi_flush(BlockDriverState *bs)
+{
+ logout("\n");
+ return bdrv_flush(bs->file);
+}
+
+
+static QEMUOptionParameter vdi_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+#if defined(CONFIG_VDI_BLOCK_SIZE)
+ {
+ .name = BLOCK_OPT_CLUSTER_SIZE,
+ .type = OPT_SIZE,
+ .help = "VDI cluster (block) size"
+ },
+#endif
+#if defined(CONFIG_VDI_STATIC_IMAGE)
+ {
+ .name = BLOCK_OPT_STATIC,
+ .type = OPT_FLAG,
+ .help = "VDI static (pre-allocated) image"
+ },
+#endif
+ /* TODO: An additional option to set UUID values might be useful. */
+ { NULL }
+};
+
+static BlockDriver bdrv_vdi = {
+ .format_name = "vdi",
+ .instance_size = sizeof(BDRVVdiState),
+ .bdrv_probe = vdi_probe,
+ .bdrv_open = vdi_open,
+ .bdrv_close = vdi_close,
+ .bdrv_create = vdi_create,
+ .bdrv_flush = vdi_flush,
+ .bdrv_is_allocated = vdi_is_allocated,
+ .bdrv_make_empty = vdi_make_empty,
+
+ .bdrv_aio_readv = vdi_aio_readv,
+#if defined(CONFIG_VDI_WRITE)
+ .bdrv_aio_writev = vdi_aio_writev,
+#endif
+
+ .bdrv_get_info = vdi_get_info,
+
+ .create_options = vdi_create_options,
+ .bdrv_check = vdi_check,
+};
+
+static void bdrv_vdi_init(void)
+{
+ logout("\n");
+ bdrv_register(&bdrv_vdi);
+}
+
+block_init(bdrv_vdi_init);
diff --git a/block/vmdk.c b/block/vmdk.c
new file mode 100644
index 0000000..8fc9d67
--- /dev/null
+++ b/block/vmdk.c
@@ -0,0 +1,870 @@
+/*
+ * Block driver for the VMDK format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2005 Filip Navara
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
+#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
+
+typedef struct {
+ uint32_t version;
+ uint32_t flags;
+ uint32_t disk_sectors;
+ uint32_t granularity;
+ uint32_t l1dir_offset;
+ uint32_t l1dir_size;
+ uint32_t file_sectors;
+ uint32_t cylinders;
+ uint32_t heads;
+ uint32_t sectors_per_track;
+} VMDK3Header;
+
+typedef struct {
+ uint32_t version;
+ uint32_t flags;
+ int64_t capacity;
+ int64_t granularity;
+ int64_t desc_offset;
+ int64_t desc_size;
+ int32_t num_gtes_per_gte;
+ int64_t rgd_offset;
+ int64_t gd_offset;
+ int64_t grain_offset;
+ char filler[1];
+ char check_bytes[4];
+} __attribute__((packed)) VMDK4Header;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct BDRVVmdkState {
+ int64_t l1_table_offset;
+ int64_t l1_backup_table_offset;
+ uint32_t *l1_table;
+ uint32_t *l1_backup_table;
+ unsigned int l1_size;
+ uint32_t l1_entry_sectors;
+
+ unsigned int l2_size;
+ uint32_t *l2_cache;
+ uint32_t l2_cache_offsets[L2_CACHE_SIZE];
+ uint32_t l2_cache_counts[L2_CACHE_SIZE];
+
+ unsigned int cluster_sectors;
+ uint32_t parent_cid;
+} BDRVVmdkState;
+
+typedef struct VmdkMetaData {
+ uint32_t offset;
+ unsigned int l1_index;
+ unsigned int l2_index;
+ unsigned int l2_offset;
+ int valid;
+} VmdkMetaData;
+
+static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ uint32_t magic;
+
+ if (buf_size < 4)
+ return 0;
+ magic = be32_to_cpu(*(uint32_t *)buf);
+ if (magic == VMDK3_MAGIC ||
+ magic == VMDK4_MAGIC)
+ return 100;
+ else
+ return 0;
+}
+
+#define CHECK_CID 1
+
+#define SECTOR_SIZE 512
+#define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each
+#define HEADER_SIZE 512 // first sector of 512 bytes
+
+static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
+{
+ char desc[DESC_SIZE];
+ uint32_t cid;
+ const char *p_name, *cid_str;
+ size_t cid_str_size;
+
+ /* the descriptor offset = 0x200 */
+ if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+ return 0;
+
+ if (parent) {
+ cid_str = "parentCID";
+ cid_str_size = sizeof("parentCID");
+ } else {
+ cid_str = "CID";
+ cid_str_size = sizeof("CID");
+ }
+
+ if ((p_name = strstr(desc,cid_str)) != NULL) {
+ p_name += cid_str_size;
+ sscanf(p_name,"%x",&cid);
+ }
+
+ return cid;
+}
+
+static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
+{
+ char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
+ char *p_name, *tmp_str;
+
+ /* the descriptor offset = 0x200 */
+ if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+ return -1;
+
+ tmp_str = strstr(desc,"parentCID");
+ pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
+ if ((p_name = strstr(desc,"CID")) != NULL) {
+ p_name += sizeof("CID");
+ snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
+ pstrcat(desc, sizeof(desc), tmp_desc);
+ }
+
+ if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0)
+ return -1;
+ return 0;
+}
+
+static int vmdk_is_cid_valid(BlockDriverState *bs)
+{
+#ifdef CHECK_CID
+ BDRVVmdkState *s = bs->opaque;
+ BlockDriverState *p_bs = bs->backing_hd;
+ uint32_t cur_pcid;
+
+ if (p_bs) {
+ cur_pcid = vmdk_read_cid(p_bs,0);
+ if (s->parent_cid != cur_pcid)
+ // CID not valid
+ return 0;
+ }
+#endif
+ // CID valid
+ return 1;
+}
+
+static int vmdk_snapshot_create(const char *filename, const char *backing_file)
+{
+ int snp_fd, p_fd;
+ int ret;
+ uint32_t p_cid;
+ char *p_name, *gd_buf, *rgd_buf;
+ const char *real_filename, *temp_str;
+ VMDK4Header header;
+ uint32_t gde_entries, gd_size;
+ int64_t gd_offset, rgd_offset, capacity, gt_size;
+ char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
+ static const char desc_template[] =
+ "# Disk DescriptorFile\n"
+ "version=1\n"
+ "CID=%x\n"
+ "parentCID=%x\n"
+ "createType=\"monolithicSparse\"\n"
+ "parentFileNameHint=\"%s\"\n"
+ "\n"
+ "# Extent description\n"
+ "RW %u SPARSE \"%s\"\n"
+ "\n"
+ "# The Disk Data Base \n"
+ "#DDB\n"
+ "\n";
+
+ snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
+ if (snp_fd < 0)
+ return -errno;
+ p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if (p_fd < 0) {
+ close(snp_fd);
+ return -errno;
+ }
+
+ /* read the header */
+ if (lseek(p_fd, 0x0, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail;
+ }
+ if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) {
+ ret = -errno;
+ goto fail;
+ }
+
+ /* write the header */
+ if (lseek(snp_fd, 0x0, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail;
+ }
+ if (write(snp_fd, hdr, HEADER_SIZE) == -1) {
+ ret = -errno;
+ goto fail;
+ }
+
+ memset(&header, 0, sizeof(header));
+ memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
+
+ if (ftruncate(snp_fd, header.grain_offset << 9)) {
+ ret = -errno;
+ goto fail;
+ }
+ /* the descriptor offset = 0x200 */
+ if (lseek(p_fd, 0x200, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail;
+ }
+ if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) {
+ ret = -errno;
+ goto fail;
+ }
+
+ if ((p_name = strstr(p_desc,"CID")) != NULL) {
+ p_name += sizeof("CID");
+ sscanf(p_name,"%x",&p_cid);
+ }
+
+ real_filename = filename;
+ if ((temp_str = strrchr(real_filename, '\\')) != NULL)
+ real_filename = temp_str + 1;
+ if ((temp_str = strrchr(real_filename, '/')) != NULL)
+ real_filename = temp_str + 1;
+ if ((temp_str = strrchr(real_filename, ':')) != NULL)
+ real_filename = temp_str + 1;
+
+ snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file,
+ (uint32_t)header.capacity, real_filename);
+
+ /* write the descriptor */
+ if (lseek(snp_fd, 0x200, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail;
+ }
+ if (write(snp_fd, s_desc, strlen(s_desc)) == -1) {
+ ret = -errno;
+ goto fail;
+ }
+
+ gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table
+ rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table
+ capacity = header.capacity * SECTOR_SIZE; // Extent size
+ /*
+ * Each GDE span 32M disk, means:
+ * 512 GTE per GT, each GTE points to grain
+ */
+ gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
+ if (!gt_size) {
+ ret = -EINVAL;
+ goto fail;
+ }
+ gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde
+ gd_size = gde_entries * sizeof(uint32_t);
+
+ /* write RGD */
+ rgd_buf = qemu_malloc(gd_size);
+ if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail_rgd;
+ }
+ if (read(p_fd, rgd_buf, gd_size) != gd_size) {
+ ret = -errno;
+ goto fail_rgd;
+ }
+ if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail_rgd;
+ }
+ if (write(snp_fd, rgd_buf, gd_size) == -1) {
+ ret = -errno;
+ goto fail_rgd;
+ }
+
+ /* write GD */
+ gd_buf = qemu_malloc(gd_size);
+ if (lseek(p_fd, gd_offset, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail_gd;
+ }
+ if (read(p_fd, gd_buf, gd_size) != gd_size) {
+ ret = -errno;
+ goto fail_gd;
+ }
+ if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) {
+ ret = -errno;
+ goto fail_gd;
+ }
+ if (write(snp_fd, gd_buf, gd_size) == -1) {
+ ret = -errno;
+ goto fail_gd;
+ }
+ ret = 0;
+
+fail_gd:
+ qemu_free(gd_buf);
+fail_rgd:
+ qemu_free(rgd_buf);
+fail:
+ close(p_fd);
+ close(snp_fd);
+ return ret;
+}
+
+static int vmdk_parent_open(BlockDriverState *bs)
+{
+ char *p_name;
+ char desc[DESC_SIZE];
+
+ /* the descriptor offset = 0x200 */
+ if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+ return -1;
+
+ if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) {
+ char *end_name;
+
+ p_name += sizeof("parentFileNameHint") + 1;
+ if ((end_name = strchr(p_name,'\"')) == NULL)
+ return -1;
+ if ((end_name - p_name) > sizeof (bs->backing_file) - 1)
+ return -1;
+
+ pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
+ }
+
+ return 0;
+}
+
+static int vmdk_open(BlockDriverState *bs, int flags)
+{
+ BDRVVmdkState *s = bs->opaque;
+ uint32_t magic;
+ int l1_size, i;
+
+ if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic))
+ goto fail;
+
+ magic = be32_to_cpu(magic);
+ if (magic == VMDK3_MAGIC) {
+ VMDK3Header header;
+
+ if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
+ goto fail;
+ s->cluster_sectors = le32_to_cpu(header.granularity);
+ s->l2_size = 1 << 9;
+ s->l1_size = 1 << 6;
+ bs->total_sectors = le32_to_cpu(header.disk_sectors);
+ s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
+ s->l1_backup_table_offset = 0;
+ s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+ } else if (magic == VMDK4_MAGIC) {
+ VMDK4Header header;
+
+ if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
+ goto fail;
+ bs->total_sectors = le64_to_cpu(header.capacity);
+ s->cluster_sectors = le64_to_cpu(header.granularity);
+ s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
+ s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+ if (s->l1_entry_sectors <= 0)
+ goto fail;
+ s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
+ / s->l1_entry_sectors;
+ s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
+ s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
+
+ // try to open parent images, if exist
+ if (vmdk_parent_open(bs) != 0)
+ goto fail;
+ // write the CID once after the image creation
+ s->parent_cid = vmdk_read_cid(bs,1);
+ } else {
+ goto fail;
+ }
+
+ /* read the L1 table */
+ l1_size = s->l1_size * sizeof(uint32_t);
+ s->l1_table = qemu_malloc(l1_size);
+ if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
+ goto fail;
+ for(i = 0; i < s->l1_size; i++) {
+ le32_to_cpus(&s->l1_table[i]);
+ }
+
+ if (s->l1_backup_table_offset) {
+ s->l1_backup_table = qemu_malloc(l1_size);
+ if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
+ goto fail;
+ for(i = 0; i < s->l1_size; i++) {
+ le32_to_cpus(&s->l1_backup_table[i]);
+ }
+ }
+
+ s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
+ return 0;
+ fail:
+ qemu_free(s->l1_backup_table);
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+ return -1;
+}
+
+static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
+ uint64_t offset, int allocate);
+
+static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
+ uint64_t offset, int allocate)
+{
+ BDRVVmdkState *s = bs->opaque;
+ uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB
+
+ // we will be here if it's first write on non-exist grain(cluster).
+ // try to read from parent image, if exist
+ if (bs->backing_hd) {
+ int ret;
+
+ if (!vmdk_is_cid_valid(bs))
+ return -1;
+
+ ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
+ s->cluster_sectors);
+ if (ret < 0) {
+ return -1;
+ }
+
+ //Write grain only into the active image
+ ret = bdrv_write(bs->file, cluster_offset, whole_grain,
+ s->cluster_sectors);
+ if (ret < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
+{
+ BDRVVmdkState *s = bs->opaque;
+
+ /* update L2 table */
+ if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
+ &(m_data->offset), sizeof(m_data->offset)) < 0)
+ return -1;
+ /* update backup L2 table */
+ if (s->l1_backup_table_offset != 0) {
+ m_data->l2_offset = s->l1_backup_table[m_data->l1_index];
+ if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
+ &(m_data->offset), sizeof(m_data->offset)) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
+ uint64_t offset, int allocate)
+{
+ BDRVVmdkState *s = bs->opaque;
+ unsigned int l1_index, l2_offset, l2_index;
+ int min_index, i, j;
+ uint32_t min_count, *l2_table, tmp = 0;
+ uint64_t cluster_offset;
+
+ if (m_data)
+ m_data->valid = 0;
+
+ l1_index = (offset >> 9) / s->l1_entry_sectors;
+ if (l1_index >= s->l1_size)
+ return 0;
+ l2_offset = s->l1_table[l1_index];
+ if (!l2_offset)
+ return 0;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (l2_offset == s->l2_cache_offsets[i]) {
+ /* increment the hit count */
+ if (++s->l2_cache_counts[i] == 0xffffffff) {
+ for(j = 0; j < L2_CACHE_SIZE; j++) {
+ s->l2_cache_counts[j] >>= 1;
+ }
+ }
+ l2_table = s->l2_cache + (i * s->l2_size);
+ goto found;
+ }
+ }
+ /* not found: load a new entry in the least used one */
+ min_index = 0;
+ min_count = 0xffffffff;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+ if (s->l2_cache_counts[i] < min_count) {
+ min_count = s->l2_cache_counts[i];
+ min_index = i;
+ }
+ }
+ l2_table = s->l2_cache + (min_index * s->l2_size);
+ if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
+ s->l2_size * sizeof(uint32_t))
+ return 0;
+
+ s->l2_cache_offsets[min_index] = l2_offset;
+ s->l2_cache_counts[min_index] = 1;
+ found:
+ l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
+ cluster_offset = le32_to_cpu(l2_table[l2_index]);
+
+ if (!cluster_offset) {
+ if (!allocate)
+ return 0;
+
+ // Avoid the L2 tables update for the images that have snapshots.
+ cluster_offset = bdrv_getlength(bs->file);
+ bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9));
+
+ cluster_offset >>= 9;
+ tmp = cpu_to_le32(cluster_offset);
+ l2_table[l2_index] = tmp;
+
+ /* First of all we write grain itself, to avoid race condition
+ * that may to corrupt the image.
+ * This problem may occur because of insufficient space on host disk
+ * or inappropriate VM shutdown.
+ */
+ if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
+ return 0;
+
+ if (m_data) {
+ m_data->offset = tmp;
+ m_data->l1_index = l1_index;
+ m_data->l2_index = l2_index;
+ m_data->l2_offset = l2_offset;
+ m_data->valid = 1;
+ }
+ }
+ cluster_offset <<= 9;
+ return cluster_offset;
+}
+
+static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int index_in_cluster, n;
+ uint64_t cluster_offset;
+
+ cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
+ index_in_cluster = sector_num % s->cluster_sectors;
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ *pnum = n;
+ return (cluster_offset != 0);
+}
+
+static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVVmdkState *s = bs->opaque;
+ int index_in_cluster, n, ret;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+ cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
+ index_in_cluster = sector_num % s->cluster_sectors;
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ if (!cluster_offset) {
+ // try to read from parent image, if exist
+ if (bs->backing_hd) {
+ if (!vmdk_is_cid_valid(bs))
+ return -1;
+ ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
+ if (ret < 0)
+ return -1;
+ } else {
+ memset(buf, 0, 512 * n);
+ }
+ } else {
+ if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
+ return -1;
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+ }
+ return 0;
+}
+
+static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVVmdkState *s = bs->opaque;
+ VmdkMetaData m_data;
+ int index_in_cluster, n;
+ uint64_t cluster_offset;
+ static int cid_update = 0;
+
+ if (sector_num > bs->total_sectors) {
+ fprintf(stderr,
+ "(VMDK) Wrong offset: sector_num=0x%" PRIx64
+ " total_sectors=0x%" PRIx64 "\n",
+ sector_num, bs->total_sectors);
+ return -1;
+ }
+
+ while (nb_sectors > 0) {
+ index_in_cluster = sector_num & (s->cluster_sectors - 1);
+ n = s->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1);
+ if (!cluster_offset)
+ return -1;
+
+ if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
+ return -1;
+ if (m_data.valid) {
+ /* update L2 tables */
+ if (vmdk_L2update(bs, &m_data) == -1)
+ return -1;
+ }
+ nb_sectors -= n;
+ sector_num += n;
+ buf += n * 512;
+
+ // update CID on the first write every time the virtual disk is opened
+ if (!cid_update) {
+ vmdk_write_cid(bs, time(NULL));
+ cid_update++;
+ }
+ }
+ return 0;
+}
+
+static int vmdk_create(const char *filename, QEMUOptionParameter *options)
+{
+ int fd, i;
+ VMDK4Header header;
+ uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
+ static const char desc_template[] =
+ "# Disk DescriptorFile\n"
+ "version=1\n"
+ "CID=%x\n"
+ "parentCID=ffffffff\n"
+ "createType=\"monolithicSparse\"\n"
+ "\n"
+ "# Extent description\n"
+ "RW %" PRId64 " SPARSE \"%s\"\n"
+ "\n"
+ "# The Disk Data Base \n"
+ "#DDB\n"
+ "\n"
+ "ddb.virtualHWVersion = \"%d\"\n"
+ "ddb.geometry.cylinders = \"%" PRId64 "\"\n"
+ "ddb.geometry.heads = \"16\"\n"
+ "ddb.geometry.sectors = \"63\"\n"
+ "ddb.adapterType = \"ide\"\n";
+ char desc[1024];
+ const char *real_filename, *temp_str;
+ int64_t total_size = 0;
+ const char *backing_file = NULL;
+ int flags = 0;
+ int ret;
+
+ // Read out options
+ while (options && options->name) {
+ if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+ total_size = options->value.n / 512;
+ } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
+ backing_file = options->value.s;
+ } else if (!strcmp(options->name, BLOCK_OPT_COMPAT6)) {
+ flags |= options->value.n ? BLOCK_FLAG_COMPAT6: 0;
+ }
+ options++;
+ }
+
+ /* XXX: add support for backing file */
+ if (backing_file) {
+ return vmdk_snapshot_create(filename, backing_file);
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+ 0644);
+ if (fd < 0)
+ return -errno;
+ magic = cpu_to_be32(VMDK4_MAGIC);
+ memset(&header, 0, sizeof(header));
+ header.version = cpu_to_le32(1);
+ header.flags = cpu_to_le32(3); /* ?? */
+ header.capacity = cpu_to_le64(total_size);
+ header.granularity = cpu_to_le64(128);
+ header.num_gtes_per_gte = cpu_to_le32(512);
+
+ grains = (total_size + header.granularity - 1) / header.granularity;
+ gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
+ gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
+ gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
+
+ header.desc_offset = 1;
+ header.desc_size = 20;
+ header.rgd_offset = header.desc_offset + header.desc_size;
+ header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
+ header.grain_offset =
+ ((header.gd_offset + gd_size + (gt_size * gt_count) +
+ header.granularity - 1) / header.granularity) *
+ header.granularity;
+
+ header.desc_offset = cpu_to_le64(header.desc_offset);
+ header.desc_size = cpu_to_le64(header.desc_size);
+ header.rgd_offset = cpu_to_le64(header.rgd_offset);
+ header.gd_offset = cpu_to_le64(header.gd_offset);
+ header.grain_offset = cpu_to_le64(header.grain_offset);
+
+ header.check_bytes[0] = 0xa;
+ header.check_bytes[1] = 0x20;
+ header.check_bytes[2] = 0xd;
+ header.check_bytes[3] = 0xa;
+
+ /* write all the data */
+ ret = qemu_write_full(fd, &magic, sizeof(magic));
+ if (ret != sizeof(magic)) {
+ ret = -errno;
+ goto exit;
+ }
+ ret = qemu_write_full(fd, &header, sizeof(header));
+ if (ret != sizeof(header)) {
+ ret = -errno;
+ goto exit;
+ }
+
+ ret = ftruncate(fd, header.grain_offset << 9);
+ if (ret < 0) {
+ ret = -errno;
+ goto exit;
+ }
+
+ /* write grain directory */
+ lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
+ for (i = 0, tmp = header.rgd_offset + gd_size;
+ i < gt_count; i++, tmp += gt_size) {
+ ret = qemu_write_full(fd, &tmp, sizeof(tmp));
+ if (ret != sizeof(tmp)) {
+ ret = -errno;
+ goto exit;
+ }
+ }
+
+ /* write backup grain directory */
+ lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
+ for (i = 0, tmp = header.gd_offset + gd_size;
+ i < gt_count; i++, tmp += gt_size) {
+ ret = qemu_write_full(fd, &tmp, sizeof(tmp));
+ if (ret != sizeof(tmp)) {
+ ret = -errno;
+ goto exit;
+ }
+ }
+
+ /* compose the descriptor */
+ real_filename = filename;
+ if ((temp_str = strrchr(real_filename, '\\')) != NULL)
+ real_filename = temp_str + 1;
+ if ((temp_str = strrchr(real_filename, '/')) != NULL)
+ real_filename = temp_str + 1;
+ if ((temp_str = strrchr(real_filename, ':')) != NULL)
+ real_filename = temp_str + 1;
+ snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL),
+ total_size, real_filename,
+ (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4),
+ total_size / (int64_t)(63 * 16));
+
+ /* write the descriptor */
+ lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
+ ret = qemu_write_full(fd, desc, strlen(desc));
+ if (ret != strlen(desc)) {
+ ret = -errno;
+ goto exit;
+ }
+
+ ret = 0;
+exit:
+ close(fd);
+ return ret;
+}
+
+static void vmdk_close(BlockDriverState *bs)
+{
+ BDRVVmdkState *s = bs->opaque;
+
+ qemu_free(s->l1_table);
+ qemu_free(s->l2_cache);
+}
+
+static int vmdk_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+
+static QEMUOptionParameter vmdk_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ {
+ .name = BLOCK_OPT_BACKING_FILE,
+ .type = OPT_STRING,
+ .help = "File name of a base image"
+ },
+ {
+ .name = BLOCK_OPT_COMPAT6,
+ .type = OPT_FLAG,
+ .help = "VMDK version 6 image"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_vmdk = {
+ .format_name = "vmdk",
+ .instance_size = sizeof(BDRVVmdkState),
+ .bdrv_probe = vmdk_probe,
+ .bdrv_open = vmdk_open,
+ .bdrv_read = vmdk_read,
+ .bdrv_write = vmdk_write,
+ .bdrv_close = vmdk_close,
+ .bdrv_create = vmdk_create,
+ .bdrv_flush = vmdk_flush,
+ .bdrv_is_allocated = vmdk_is_allocated,
+
+ .create_options = vmdk_create_options,
+};
+
+static void bdrv_vmdk_init(void)
+{
+ bdrv_register(&bdrv_vmdk);
+}
+
+block_init(bdrv_vmdk_init);
diff --git a/block/vpc.c b/block/vpc.c
new file mode 100644
index 0000000..7b025be
--- /dev/null
+++ b/block/vpc.c
@@ -0,0 +1,654 @@
+/*
+ * Block driver for Connectix / Microsoft Virtual PC images
+ *
+ * Copyright (c) 2005 Alex Beregszaszi
+ * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+/**************************************************************/
+
+#define HEADER_SIZE 512
+
+//#define CACHE
+
+enum vhd_type {
+ VHD_FIXED = 2,
+ VHD_DYNAMIC = 3,
+ VHD_DIFFERENCING = 4,
+};
+
+// Seconds since Jan 1, 2000 0:00:00 (UTC)
+#define VHD_TIMESTAMP_BASE 946684800
+
+// always big-endian
+struct vhd_footer {
+ char creator[8]; // "conectix"
+ uint32_t features;
+ uint32_t version;
+
+ // Offset of next header structure, 0xFFFFFFFF if none
+ uint64_t data_offset;
+
+ // Seconds since Jan 1, 2000 0:00:00 (UTC)
+ uint32_t timestamp;
+
+ char creator_app[4]; // "vpc "
+ uint16_t major;
+ uint16_t minor;
+ char creator_os[4]; // "Wi2k"
+
+ uint64_t orig_size;
+ uint64_t size;
+
+ uint16_t cyls;
+ uint8_t heads;
+ uint8_t secs_per_cyl;
+
+ uint32_t type;
+
+ // Checksum of the Hard Disk Footer ("one's complement of the sum of all
+ // the bytes in the footer without the checksum field")
+ uint32_t checksum;
+
+ // UUID used to identify a parent hard disk (backing file)
+ uint8_t uuid[16];
+
+ uint8_t in_saved_state;
+};
+
+struct vhd_dyndisk_header {
+ char magic[8]; // "cxsparse"
+
+ // Offset of next header structure, 0xFFFFFFFF if none
+ uint64_t data_offset;
+
+ // Offset of the Block Allocation Table (BAT)
+ uint64_t table_offset;
+
+ uint32_t version;
+ uint32_t max_table_entries; // 32bit/entry
+
+ // 2 MB by default, must be a power of two
+ uint32_t block_size;
+
+ uint32_t checksum;
+ uint8_t parent_uuid[16];
+ uint32_t parent_timestamp;
+ uint32_t reserved;
+
+ // Backing file name (in UTF-16)
+ uint8_t parent_name[512];
+
+ struct {
+ uint32_t platform;
+ uint32_t data_space;
+ uint32_t data_length;
+ uint32_t reserved;
+ uint64_t data_offset;
+ } parent_locator[8];
+};
+
+typedef struct BDRVVPCState {
+ uint8_t footer_buf[HEADER_SIZE];
+ uint64_t free_data_block_offset;
+ int max_table_entries;
+ uint32_t *pagetable;
+ uint64_t bat_offset;
+ uint64_t last_bitmap_offset;
+
+ uint32_t block_size;
+ uint32_t bitmap_size;
+
+#ifdef CACHE
+ uint8_t *pageentry_u8;
+ uint32_t *pageentry_u32;
+ uint16_t *pageentry_u16;
+
+ uint64_t last_bitmap;
+#endif
+} BDRVVPCState;
+
+static uint32_t vpc_checksum(uint8_t* buf, size_t size)
+{
+ uint32_t res = 0;
+ int i;
+
+ for (i = 0; i < size; i++)
+ res += buf[i];
+
+ return ~res;
+}
+
+
+static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+ if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
+ return 100;
+ return 0;
+}
+
+static int vpc_open(BlockDriverState *bs, int flags)
+{
+ BDRVVPCState *s = bs->opaque;
+ int i;
+ struct vhd_footer* footer;
+ struct vhd_dyndisk_header* dyndisk_header;
+ uint8_t buf[HEADER_SIZE];
+ uint32_t checksum;
+
+ if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
+ goto fail;
+
+ footer = (struct vhd_footer*) s->footer_buf;
+ if (strncmp(footer->creator, "conectix", 8))
+ goto fail;
+
+ checksum = be32_to_cpu(footer->checksum);
+ footer->checksum = 0;
+ if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
+ fprintf(stderr, "block-vpc: The header checksum of '%s' is "
+ "incorrect.\n", bs->filename);
+
+ // The visible size of a image in Virtual PC depends on the geometry
+ // rather than on the size stored in the footer (the size in the footer
+ // is too large usually)
+ bs->total_sectors = (int64_t)
+ be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
+
+ if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
+ != HEADER_SIZE)
+ goto fail;
+
+ dyndisk_header = (struct vhd_dyndisk_header*) buf;
+
+ if (strncmp(dyndisk_header->magic, "cxsparse", 8))
+ goto fail;
+
+
+ s->block_size = be32_to_cpu(dyndisk_header->block_size);
+ s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
+
+ s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
+ s->pagetable = qemu_malloc(s->max_table_entries * 4);
+
+ s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
+ if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
+ s->max_table_entries * 4) != s->max_table_entries * 4)
+ goto fail;
+
+ s->free_data_block_offset =
+ (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
+
+ for (i = 0; i < s->max_table_entries; i++) {
+ be32_to_cpus(&s->pagetable[i]);
+ if (s->pagetable[i] != 0xFFFFFFFF) {
+ int64_t next = (512 * (int64_t) s->pagetable[i]) +
+ s->bitmap_size + s->block_size;
+
+ if (next> s->free_data_block_offset)
+ s->free_data_block_offset = next;
+ }
+ }
+
+ s->last_bitmap_offset = (int64_t) -1;
+
+#ifdef CACHE
+ s->pageentry_u8 = qemu_malloc(512);
+ s->pageentry_u32 = s->pageentry_u8;
+ s->pageentry_u16 = s->pageentry_u8;
+ s->last_pagetable = -1;
+#endif
+
+ return 0;
+ fail:
+ return -1;
+}
+
+/*
+ * Returns the absolute byte offset of the given sector in the image file.
+ * If the sector is not allocated, -1 is returned instead.
+ *
+ * The parameter write must be 1 if the offset will be used for a write
+ * operation (the block bitmaps is updated then), 0 otherwise.
+ */
+static inline int64_t get_sector_offset(BlockDriverState *bs,
+ int64_t sector_num, int write)
+{
+ BDRVVPCState *s = bs->opaque;
+ uint64_t offset = sector_num * 512;
+ uint64_t bitmap_offset, block_offset;
+ uint32_t pagetable_index, pageentry_index;
+
+ pagetable_index = offset / s->block_size;
+ pageentry_index = (offset % s->block_size) / 512;
+
+ if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
+ return -1; // not allocated
+
+ bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index];
+ block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index);
+
+ // We must ensure that we don't write to any sectors which are marked as
+ // unused in the bitmap. We get away with setting all bits in the block
+ // bitmap each time we write to a new block. This might cause Virtual PC to
+ // miss sparse read optimization, but it's not a problem in terms of
+ // correctness.
+ if (write && (s->last_bitmap_offset != bitmap_offset)) {
+ uint8_t bitmap[s->bitmap_size];
+
+ s->last_bitmap_offset = bitmap_offset;
+ memset(bitmap, 0xff, s->bitmap_size);
+ bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
+ }
+
+// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
+// sector_num, pagetable_index, pageentry_index,
+// bitmap_offset, block_offset);
+
+// disabled by reason
+#if 0
+#ifdef CACHE
+ if (bitmap_offset != s->last_bitmap)
+ {
+ lseek(s->fd, bitmap_offset, SEEK_SET);
+
+ s->last_bitmap = bitmap_offset;
+
+ // Scary! Bitmap is stored as big endian 32bit entries,
+ // while we used to look it up byte by byte
+ read(s->fd, s->pageentry_u8, 512);
+ for (i = 0; i < 128; i++)
+ be32_to_cpus(&s->pageentry_u32[i]);
+ }
+
+ if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
+ return -1;
+#else
+ lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
+
+ read(s->fd, &bitmap_entry, 1);
+
+ if ((bitmap_entry >> (pageentry_index % 8)) & 1)
+ return -1; // not allocated
+#endif
+#endif
+
+ return block_offset;
+}
+
+/*
+ * Writes the footer to the end of the image file. This is needed when the
+ * file grows as it overwrites the old footer
+ *
+ * Returns 0 on success and < 0 on error
+ */
+static int rewrite_footer(BlockDriverState* bs)
+{
+ int ret;
+ BDRVVPCState *s = bs->opaque;
+ int64_t offset = s->free_data_block_offset;
+
+ ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Allocates a new block. This involves writing a new footer and updating
+ * the Block Allocation Table to use the space at the old end of the image
+ * file (overwriting the old footer)
+ *
+ * Returns the sectors' offset in the image file on success and < 0 on error
+ */
+static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
+{
+ BDRVVPCState *s = bs->opaque;
+ int64_t bat_offset;
+ uint32_t index, bat_value;
+ int ret;
+ uint8_t bitmap[s->bitmap_size];
+
+ // Check if sector_num is valid
+ if ((sector_num < 0) || (sector_num > bs->total_sectors))
+ return -1;
+
+ // Write entry into in-memory BAT
+ index = (sector_num * 512) / s->block_size;
+ if (s->pagetable[index] != 0xFFFFFFFF)
+ return -1;
+
+ s->pagetable[index] = s->free_data_block_offset / 512;
+
+ // Initialize the block's bitmap
+ memset(bitmap, 0xff, s->bitmap_size);
+ bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap,
+ s->bitmap_size);
+
+ // Write new footer (the old one will be overwritten)
+ s->free_data_block_offset += s->block_size + s->bitmap_size;
+ ret = rewrite_footer(bs);
+ if (ret < 0)
+ goto fail;
+
+ // Write BAT entry to disk
+ bat_offset = s->bat_offset + (4 * index);
+ bat_value = be32_to_cpu(s->pagetable[index]);
+ ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4);
+ if (ret < 0)
+ goto fail;
+
+ return get_sector_offset(bs, sector_num, 0);
+
+fail:
+ s->free_data_block_offset -= (s->block_size + s->bitmap_size);
+ return -1;
+}
+
+static int vpc_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVVPCState *s = bs->opaque;
+ int ret;
+ int64_t offset;
+ int64_t sectors, sectors_per_block;
+
+ while (nb_sectors > 0) {
+ offset = get_sector_offset(bs, sector_num, 0);
+
+ sectors_per_block = s->block_size >> BDRV_SECTOR_BITS;
+ sectors = sectors_per_block - (sector_num % sectors_per_block);
+ if (sectors > nb_sectors) {
+ sectors = nb_sectors;
+ }
+
+ if (offset == -1) {
+ memset(buf, 0, sectors * BDRV_SECTOR_SIZE);
+ } else {
+ ret = bdrv_pread(bs->file, offset, buf,
+ sectors * BDRV_SECTOR_SIZE);
+ if (ret != sectors * BDRV_SECTOR_SIZE) {
+ return -1;
+ }
+ }
+
+ nb_sectors -= sectors;
+ sector_num += sectors;
+ buf += sectors * BDRV_SECTOR_SIZE;
+ }
+ return 0;
+}
+
+static int vpc_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVVPCState *s = bs->opaque;
+ int64_t offset;
+ int64_t sectors, sectors_per_block;
+ int ret;
+
+ while (nb_sectors > 0) {
+ offset = get_sector_offset(bs, sector_num, 1);
+
+ sectors_per_block = s->block_size >> BDRV_SECTOR_BITS;
+ sectors = sectors_per_block - (sector_num % sectors_per_block);
+ if (sectors > nb_sectors) {
+ sectors = nb_sectors;
+ }
+
+ if (offset == -1) {
+ offset = alloc_block(bs, sector_num);
+ if (offset < 0)
+ return -1;
+ }
+
+ ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE);
+ if (ret != sectors * BDRV_SECTOR_SIZE) {
+ return -1;
+ }
+
+ nb_sectors -= sectors;
+ sector_num += sectors;
+ buf += sectors * BDRV_SECTOR_SIZE;
+ }
+
+ return 0;
+}
+
+static int vpc_flush(BlockDriverState *bs)
+{
+ return bdrv_flush(bs->file);
+}
+
+/*
+ * Calculates the number of cylinders, heads and sectors per cylinder
+ * based on a given number of sectors. This is the algorithm described
+ * in the VHD specification.
+ *
+ * Note that the geometry doesn't always exactly match total_sectors but
+ * may round it down.
+ *
+ * Returns 0 on success, -EFBIG if the size is larger than 127 GB
+ */
+static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
+ uint8_t* heads, uint8_t* secs_per_cyl)
+{
+ uint32_t cyls_times_heads;
+
+ if (total_sectors > 65535 * 16 * 255)
+ return -EFBIG;
+
+ if (total_sectors > 65535 * 16 * 63) {
+ *secs_per_cyl = 255;
+ *heads = 16;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ } else {
+ *secs_per_cyl = 17;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ *heads = (cyls_times_heads + 1023) / 1024;
+
+ if (*heads < 4)
+ *heads = 4;
+
+ if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
+ *secs_per_cyl = 31;
+ *heads = 16;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ }
+
+ if (cyls_times_heads >= (*heads * 1024)) {
+ *secs_per_cyl = 63;
+ *heads = 16;
+ cyls_times_heads = total_sectors / *secs_per_cyl;
+ }
+ }
+
+ *cyls = cyls_times_heads / *heads;
+
+ return 0;
+}
+
+static int vpc_create(const char *filename, QEMUOptionParameter *options)
+{
+ uint8_t buf[1024];
+ struct vhd_footer* footer = (struct vhd_footer*) buf;
+ struct vhd_dyndisk_header* dyndisk_header =
+ (struct vhd_dyndisk_header*) buf;
+ int fd, i;
+ uint16_t cyls = 0;
+ uint8_t heads = 0;
+ uint8_t secs_per_cyl = 0;
+ size_t block_size, num_bat_entries;
+ int64_t total_sectors = 0;
+ int ret = -EIO;
+
+ // Read out options
+ while (options && options->name) {
+ if (!strcmp(options->name, "size")) {
+ total_sectors = options->value.n / 512;
+ }
+ options++;
+ }
+
+ // Create the file
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ if (fd < 0)
+ return -EIO;
+
+ /* Calculate matching total_size and geometry. Increase the number of
+ sectors requested until we get enough (or fail). */
+ for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
+ if (calculate_geometry(total_sectors + i,
+ &cyls, &heads, &secs_per_cyl)) {
+ ret = -EFBIG;
+ goto fail;
+ }
+ }
+ total_sectors = (int64_t) cyls * heads * secs_per_cyl;
+
+ // Prepare the Hard Disk Footer
+ memset(buf, 0, 1024);
+
+ memcpy(footer->creator, "conectix", 8);
+ // TODO Check if "qemu" creator_app is ok for VPC
+ memcpy(footer->creator_app, "qemu", 4);
+ memcpy(footer->creator_os, "Wi2k", 4);
+
+ footer->features = be32_to_cpu(0x02);
+ footer->version = be32_to_cpu(0x00010000);
+ footer->data_offset = be64_to_cpu(HEADER_SIZE);
+ footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
+
+ // Version of Virtual PC 2007
+ footer->major = be16_to_cpu(0x0005);
+ footer->minor =be16_to_cpu(0x0003);
+
+ footer->orig_size = be64_to_cpu(total_sectors * 512);
+ footer->size = be64_to_cpu(total_sectors * 512);
+
+ footer->cyls = be16_to_cpu(cyls);
+ footer->heads = heads;
+ footer->secs_per_cyl = secs_per_cyl;
+
+ footer->type = be32_to_cpu(VHD_DYNAMIC);
+
+ // TODO uuid is missing
+
+ footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
+
+ // Write the footer (twice: at the beginning and at the end)
+ block_size = 0x200000;
+ num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
+
+ if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
+ goto fail;
+ }
+
+ if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) {
+ goto fail;
+ }
+ if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
+ goto fail;
+ }
+
+ // Write the initial BAT
+ if (lseek(fd, 3 * 512, SEEK_SET) < 0) {
+ goto fail;
+ }
+
+ memset(buf, 0xFF, 512);
+ for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
+ if (write(fd, buf, 512) != 512) {
+ goto fail;
+ }
+ }
+
+
+ // Prepare the Dynamic Disk Header
+ memset(buf, 0, 1024);
+
+ memcpy(dyndisk_header->magic, "cxsparse", 8);
+
+ dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF);
+ dyndisk_header->table_offset = be64_to_cpu(3 * 512);
+ dyndisk_header->version = be32_to_cpu(0x00010000);
+ dyndisk_header->block_size = be32_to_cpu(block_size);
+ dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries);
+
+ dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
+
+ // Write the header
+ if (lseek(fd, 512, SEEK_SET) < 0) {
+ goto fail;
+ }
+
+ if (write(fd, buf, 1024) != 1024) {
+ goto fail;
+ }
+ ret = 0;
+
+ fail:
+ close(fd);
+ return ret;
+}
+
+static void vpc_close(BlockDriverState *bs)
+{
+ BDRVVPCState *s = bs->opaque;
+ qemu_free(s->pagetable);
+#ifdef CACHE
+ qemu_free(s->pageentry_u8);
+#endif
+}
+
+static QEMUOptionParameter vpc_create_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+};
+
+static BlockDriver bdrv_vpc = {
+ .format_name = "vpc",
+ .instance_size = sizeof(BDRVVPCState),
+ .bdrv_probe = vpc_probe,
+ .bdrv_open = vpc_open,
+ .bdrv_read = vpc_read,
+ .bdrv_write = vpc_write,
+ .bdrv_flush = vpc_flush,
+ .bdrv_close = vpc_close,
+ .bdrv_create = vpc_create,
+
+ .create_options = vpc_create_options,
+};
+
+static void bdrv_vpc_init(void)
+{
+ bdrv_register(&bdrv_vpc);
+}
+
+block_init(bdrv_vpc_init);
diff --git a/block/vvfat.c b/block/vvfat.c
new file mode 100644
index 0000000..fe568fe
--- /dev/null
+++ b/block/vvfat.c
@@ -0,0 +1,2888 @@
+/* vim:set shiftwidth=4 ts=8: */
+/*
+ * QEMU Block driver for virtual VFAT (shadows a local directory)
+ *
+ * Copyright (c) 2004,2005 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <sys/stat.h>
+#include <dirent.h>
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+#ifndef S_IWGRP
+#define S_IWGRP 0
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0
+#endif
+
+/* TODO: add ":bootsector=blabla.img:" */
+/* LATER TODO: add automatic boot sector generation from
+ BOOTEASY.ASM and Ranish Partition Manager
+ Note that DOS assumes the system files to be the first files in the
+ file system (test if the boot sector still relies on that fact)! */
+/* MAYBE TODO: write block-visofs.c */
+/* TODO: call try_commit() only after a timeout */
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+
+#define DLOG(a) a
+
+#undef stderr
+#define stderr STDERR
+FILE* stderr = NULL;
+
+static void checkpoint(void);
+
+#ifdef __MINGW32__
+void nonono(const char* file, int line, const char* msg) {
+ fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg);
+ exit(-5);
+}
+#undef assert
+#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
+#endif
+
+#else
+
+#define DLOG(a)
+
+#endif
+
+/* dynamic array functions */
+typedef struct array_t {
+ char* pointer;
+ unsigned int size,next,item_size;
+} array_t;
+
+static inline void array_init(array_t* array,unsigned int item_size)
+{
+ array->pointer = NULL;
+ array->size=0;
+ array->next=0;
+ array->item_size=item_size;
+}
+
+static inline void array_free(array_t* array)
+{
+ if(array->pointer)
+ free(array->pointer);
+ array->size=array->next=0;
+}
+
+/* does not automatically grow */
+static inline void* array_get(array_t* array,unsigned int index) {
+ assert(index < array->next);
+ return array->pointer + index * array->item_size;
+}
+
+static inline int array_ensure_allocated(array_t* array, int index)
+{
+ if((index + 1) * array->item_size > array->size) {
+ int new_size = (index + 32) * array->item_size;
+ array->pointer = qemu_realloc(array->pointer, new_size);
+ if (!array->pointer)
+ return -1;
+ array->size = new_size;
+ array->next = index + 1;
+ }
+
+ return 0;
+}
+
+static inline void* array_get_next(array_t* array) {
+ unsigned int next = array->next;
+ void* result;
+
+ if (array_ensure_allocated(array, next) < 0)
+ return NULL;
+
+ array->next = next + 1;
+ result = array_get(array, next);
+
+ return result;
+}
+
+static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
+ if((array->next+count)*array->item_size>array->size) {
+ int increment=count*array->item_size;
+ array->pointer=qemu_realloc(array->pointer,array->size+increment);
+ if(!array->pointer)
+ return NULL;
+ array->size+=increment;
+ }
+ memmove(array->pointer+(index+count)*array->item_size,
+ array->pointer+index*array->item_size,
+ (array->next-index)*array->item_size);
+ array->next+=count;
+ return array->pointer+index*array->item_size;
+}
+
+/* this performs a "roll", so that the element which was at index_from becomes
+ * index_to, but the order of all other elements is preserved. */
+static inline int array_roll(array_t* array,int index_to,int index_from,int count)
+{
+ char* buf;
+ char* from;
+ char* to;
+ int is;
+
+ if(!array ||
+ index_to<0 || index_to>=array->next ||
+ index_from<0 || index_from>=array->next)
+ return -1;
+
+ if(index_to==index_from)
+ return 0;
+
+ is=array->item_size;
+ from=array->pointer+index_from*is;
+ to=array->pointer+index_to*is;
+ buf=qemu_malloc(is*count);
+ memcpy(buf,from,is*count);
+
+ if(index_to<index_from)
+ memmove(to+is*count,to,from-to);
+ else
+ memmove(from,from+is*count,to-from);
+
+ memcpy(to,buf,is*count);
+
+ free(buf);
+
+ return 0;
+}
+
+static inline int array_remove_slice(array_t* array,int index, int count)
+{
+ assert(index >=0);
+ assert(count > 0);
+ assert(index + count <= array->next);
+ if(array_roll(array,array->next-1,index,count))
+ return -1;
+ array->next -= count;
+ return 0;
+}
+
+static int array_remove(array_t* array,int index)
+{
+ return array_remove_slice(array, index, 1);
+}
+
+/* return the index for a given member */
+static int array_index(array_t* array, void* pointer)
+{
+ size_t offset = (char*)pointer - array->pointer;
+ assert((offset % array->item_size) == 0);
+ assert(offset/array->item_size < array->next);
+ return offset/array->item_size;
+}
+
+/* These structures are used to fake a disk and the VFAT filesystem.
+ * For this reason we need to use __attribute__((packed)). */
+
+typedef struct bootsector_t {
+ uint8_t jump[3];
+ uint8_t name[8];
+ uint16_t sector_size;
+ uint8_t sectors_per_cluster;
+ uint16_t reserved_sectors;
+ uint8_t number_of_fats;
+ uint16_t root_entries;
+ uint16_t total_sectors16;
+ uint8_t media_type;
+ uint16_t sectors_per_fat;
+ uint16_t sectors_per_track;
+ uint16_t number_of_heads;
+ uint32_t hidden_sectors;
+ uint32_t total_sectors;
+ union {
+ struct {
+ uint8_t drive_number;
+ uint8_t current_head;
+ uint8_t signature;
+ uint32_t id;
+ uint8_t volume_label[11];
+ } __attribute__((packed)) fat16;
+ struct {
+ uint32_t sectors_per_fat;
+ uint16_t flags;
+ uint8_t major,minor;
+ uint32_t first_cluster_of_root_directory;
+ uint16_t info_sector;
+ uint16_t backup_boot_sector;
+ uint16_t ignored;
+ } __attribute__((packed)) fat32;
+ } u;
+ uint8_t fat_type[8];
+ uint8_t ignored[0x1c0];
+ uint8_t magic[2];
+} __attribute__((packed)) bootsector_t;
+
+typedef struct {
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cylinder;
+} mbr_chs_t;
+
+typedef struct partition_t {
+ uint8_t attributes; /* 0x80 = bootable */
+ mbr_chs_t start_CHS;
+ uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
+ mbr_chs_t end_CHS;
+ uint32_t start_sector_long;
+ uint32_t length_sector_long;
+} __attribute__((packed)) partition_t;
+
+typedef struct mbr_t {
+ uint8_t ignored[0x1b8];
+ uint32_t nt_id;
+ uint8_t ignored2[2];
+ partition_t partition[4];
+ uint8_t magic[2];
+} __attribute__((packed)) mbr_t;
+
+typedef struct direntry_t {
+ uint8_t name[8];
+ uint8_t extension[3];
+ uint8_t attributes;
+ uint8_t reserved[2];
+ uint16_t ctime;
+ uint16_t cdate;
+ uint16_t adate;
+ uint16_t begin_hi;
+ uint16_t mtime;
+ uint16_t mdate;
+ uint16_t begin;
+ uint32_t size;
+} __attribute__((packed)) direntry_t;
+
+/* this structure are used to transparently access the files */
+
+typedef struct mapping_t {
+ /* begin is the first cluster, end is the last+1 */
+ uint32_t begin,end;
+ /* as s->directory is growable, no pointer may be used here */
+ unsigned int dir_index;
+ /* the clusters of a file may be in any order; this points to the first */
+ int first_mapping_index;
+ union {
+ /* offset is
+ * - the offset in the file (in clusters) for a file, or
+ * - the next cluster of the directory for a directory, and
+ * - the address of the buffer for a faked entry
+ */
+ struct {
+ uint32_t offset;
+ } file;
+ struct {
+ int parent_mapping_index;
+ int first_dir_index;
+ } dir;
+ } info;
+ /* path contains the full path, i.e. it always starts with s->path */
+ char* path;
+
+ enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
+ MODE_DIRECTORY = 4, MODE_FAKED = 8,
+ MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
+ int read_only;
+} mapping_t;
+
+#ifdef DEBUG
+static void print_direntry(const struct direntry_t*);
+static void print_mapping(const struct mapping_t* mapping);
+#endif
+
+/* here begins the real VVFAT driver */
+
+typedef struct BDRVVVFATState {
+ BlockDriverState* bs; /* pointer to parent */
+ unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
+ unsigned char first_sectors[0x40*0x200];
+
+ int fat_type; /* 16 or 32 */
+ array_t fat,directory,mapping;
+
+ unsigned int cluster_size;
+ unsigned int sectors_per_cluster;
+ unsigned int sectors_per_fat;
+ unsigned int sectors_of_root_directory;
+ uint32_t last_cluster_of_root_directory;
+ unsigned int faked_sectors; /* how many sectors are faked before file data */
+ uint32_t sector_count; /* total number of sectors of the partition */
+ uint32_t cluster_count; /* total number of clusters of this partition */
+ uint32_t max_fat_value;
+
+ int current_fd;
+ mapping_t* current_mapping;
+ unsigned char* cluster; /* points to current cluster */
+ unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
+ unsigned int current_cluster;
+
+ /* write support */
+ BlockDriverState* write_target;
+ char* qcow_filename;
+ BlockDriverState* qcow;
+ void* fat2;
+ char* used_clusters;
+ array_t commits;
+ const char* path;
+ int downcase_short_names;
+} BDRVVVFATState;
+
+/* take the sector position spos and convert it to Cylinder/Head/Sector position
+ * if the position is outside the specified geometry, fill maximum value for CHS
+ * and return 1 to signal overflow.
+ */
+static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
+ int head,sector;
+ sector = spos % (bs->secs); spos/= bs->secs;
+ head = spos % (bs->heads); spos/= bs->heads;
+ if(spos >= bs->cyls){
+ /* Overflow,
+ it happens if 32bit sector positions are used, while CHS is only 24bit.
+ Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
+ chs->head = 0xFF;
+ chs->sector = 0xFF;
+ chs->cylinder = 0xFF;
+ return 1;
+ }
+ chs->head = (uint8_t)head;
+ chs->sector = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
+ chs->cylinder = (uint8_t)spos;
+ return 0;
+}
+
+static void init_mbr(BDRVVVFATState* s)
+{
+ /* TODO: if the files mbr.img and bootsect.img exist, use them */
+ mbr_t* real_mbr=(mbr_t*)s->first_sectors;
+ partition_t* partition = &(real_mbr->partition[0]);
+ int lba;
+
+ memset(s->first_sectors,0,512);
+
+ /* Win NT Disk Signature */
+ real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
+
+ partition->attributes=0x80; /* bootable */
+
+ /* LBA is used when partition is outside the CHS geometry */
+ lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
+ lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count);
+
+ /*LBA partitions are identified only by start/length_sector_long not by CHS*/
+ partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
+ partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
+
+ /* FAT12/FAT16/FAT32 */
+ /* DOS uses different types when partition is LBA,
+ probably to prevent older versions from using CHS on them */
+ partition->fs_type= s->fat_type==12 ? 0x1:
+ s->fat_type==16 ? (lba?0xe:0x06):
+ /*fat_tyoe==32*/ (lba?0xc:0x0b);
+
+ real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
+}
+
+/* direntry functions */
+
+/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
+static inline int short2long_name(char* dest,const char* src)
+{
+ int i;
+ int len;
+ for(i=0;i<129 && src[i];i++) {
+ dest[2*i]=src[i];
+ dest[2*i+1]=0;
+ }
+ len=2*i;
+ dest[2*i]=dest[2*i+1]=0;
+ for(i=2*i+2;(i%26);i++)
+ dest[i]=0xff;
+ return len;
+}
+
+static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
+{
+ char buffer[258];
+ int length=short2long_name(buffer,filename),
+ number_of_entries=(length+25)/26,i;
+ direntry_t* entry;
+
+ for(i=0;i<number_of_entries;i++) {
+ entry=array_get_next(&(s->directory));
+ entry->attributes=0xf;
+ entry->reserved[0]=0;
+ entry->begin=0;
+ entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
+ }
+ for(i=0;i<26*number_of_entries;i++) {
+ int offset=(i%26);
+ if(offset<10) offset=1+offset;
+ else if(offset<22) offset=14+offset-10;
+ else offset=28+offset-22;
+ entry=array_get(&(s->directory),s->directory.next-1-(i/26));
+ entry->name[offset]=buffer[i];
+ }
+ return array_get(&(s->directory),s->directory.next-number_of_entries);
+}
+
+static char is_free(const direntry_t* direntry)
+{
+ return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
+}
+
+static char is_volume_label(const direntry_t* direntry)
+{
+ return direntry->attributes == 0x28;
+}
+
+static char is_long_name(const direntry_t* direntry)
+{
+ return direntry->attributes == 0xf;
+}
+
+static char is_short_name(const direntry_t* direntry)
+{
+ return !is_volume_label(direntry) && !is_long_name(direntry)
+ && !is_free(direntry);
+}
+
+static char is_directory(const direntry_t* direntry)
+{
+ return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
+}
+
+static inline char is_dot(const direntry_t* direntry)
+{
+ return is_short_name(direntry) && direntry->name[0] == '.';
+}
+
+static char is_file(const direntry_t* direntry)
+{
+ return is_short_name(direntry) && !is_directory(direntry);
+}
+
+static inline uint32_t begin_of_direntry(const direntry_t* direntry)
+{
+ return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
+}
+
+static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
+{
+ return le32_to_cpu(direntry->size);
+}
+
+static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
+{
+ direntry->begin = cpu_to_le16(begin & 0xffff);
+ direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
+}
+
+/* fat functions */
+
+static inline uint8_t fat_chksum(const direntry_t* entry)
+{
+ uint8_t chksum=0;
+ int i;
+
+ for(i=0;i<11;i++) {
+ unsigned char c;
+
+ c = (i < 8) ? entry->name[i] : entry->extension[i-8];
+ chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c;
+ }
+
+ return chksum;
+}
+
+/* if return_time==0, this returns the fat_date, else the fat_time */
+static uint16_t fat_datetime(time_t time,int return_time) {
+ struct tm* t;
+#ifdef _WIN32
+ t=localtime(&time); /* this is not thread safe */
+#else
+ struct tm t1;
+ t = &t1;
+ localtime_r(&time,t);
+#endif
+ if(return_time)
+ return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
+ return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
+}
+
+static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
+{
+ if(s->fat_type==32) {
+ uint32_t* entry=array_get(&(s->fat),cluster);
+ *entry=cpu_to_le32(value);
+ } else if(s->fat_type==16) {
+ uint16_t* entry=array_get(&(s->fat),cluster);
+ *entry=cpu_to_le16(value&0xffff);
+ } else {
+ int offset = (cluster*3/2);
+ unsigned char* p = array_get(&(s->fat), offset);
+ switch (cluster&1) {
+ case 0:
+ p[0] = value&0xff;
+ p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
+ break;
+ case 1:
+ p[0] = (p[0]&0xf) | ((value&0xf)<<4);
+ p[1] = (value>>4);
+ break;
+ }
+ }
+}
+
+static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
+{
+ if(s->fat_type==32) {
+ uint32_t* entry=array_get(&(s->fat),cluster);
+ return le32_to_cpu(*entry);
+ } else if(s->fat_type==16) {
+ uint16_t* entry=array_get(&(s->fat),cluster);
+ return le16_to_cpu(*entry);
+ } else {
+ const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
+ return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
+ }
+}
+
+static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
+{
+ if(fat_entry>s->max_fat_value-8)
+ return -1;
+ return 0;
+}
+
+static inline void init_fat(BDRVVVFATState* s)
+{
+ if (s->fat_type == 12) {
+ array_init(&(s->fat),1);
+ array_ensure_allocated(&(s->fat),
+ s->sectors_per_fat * 0x200 * 3 / 2 - 1);
+ } else {
+ array_init(&(s->fat),(s->fat_type==32?4:2));
+ array_ensure_allocated(&(s->fat),
+ s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
+ }
+ memset(s->fat.pointer,0,s->fat.size);
+
+ switch(s->fat_type) {
+ case 12: s->max_fat_value=0xfff; break;
+ case 16: s->max_fat_value=0xffff; break;
+ case 32: s->max_fat_value=0x0fffffff; break;
+ default: s->max_fat_value=0; /* error... */
+ }
+
+}
+
+/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
+/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
+static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
+ unsigned int directory_start, const char* filename, int is_dot)
+{
+ int i,j,long_index=s->directory.next;
+ direntry_t* entry = NULL;
+ direntry_t* entry_long = NULL;
+
+ if(is_dot) {
+ entry=array_get_next(&(s->directory));
+ memset(entry->name,0x20,11);
+ memcpy(entry->name,filename,strlen(filename));
+ return entry;
+ }
+
+ entry_long=create_long_filename(s,filename);
+
+ i = strlen(filename);
+ for(j = i - 1; j>0 && filename[j]!='.';j--);
+ if (j > 0)
+ i = (j > 8 ? 8 : j);
+ else if (i > 8)
+ i = 8;
+
+ entry=array_get_next(&(s->directory));
+ memset(entry->name,0x20,11);
+ memcpy(entry->name, filename, i);
+
+ if(j > 0)
+ for (i = 0; i < 3 && filename[j+1+i]; i++)
+ entry->extension[i] = filename[j+1+i];
+
+ /* upcase & remove unwanted characters */
+ for(i=10;i>=0;i--) {
+ if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
+ if(entry->name[i]<=' ' || entry->name[i]>0x7f
+ || strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
+ entry->name[i]='_';
+ else if(entry->name[i]>='a' && entry->name[i]<='z')
+ entry->name[i]+='A'-'a';
+ }
+
+ /* mangle duplicates */
+ while(1) {
+ direntry_t* entry1=array_get(&(s->directory),directory_start);
+ int j;
+
+ for(;entry1<entry;entry1++)
+ if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
+ break; /* found dupe */
+ if(entry1==entry) /* no dupe found */
+ break;
+
+ /* use all 8 characters of name */
+ if(entry->name[7]==' ') {
+ int j;
+ for(j=6;j>0 && entry->name[j]==' ';j--)
+ entry->name[j]='~';
+ }
+
+ /* increment number */
+ for(j=7;j>0 && entry->name[j]=='9';j--)
+ entry->name[j]='0';
+ if(j>0) {
+ if(entry->name[j]<'0' || entry->name[j]>'9')
+ entry->name[j]='0';
+ else
+ entry->name[j]++;
+ }
+ }
+
+ /* calculate checksum; propagate to long name */
+ if(entry_long) {
+ uint8_t chksum=fat_chksum(entry);
+
+ /* calculate anew, because realloc could have taken place */
+ entry_long=array_get(&(s->directory),long_index);
+ while(entry_long<entry && is_long_name(entry_long)) {
+ entry_long->reserved[1]=chksum;
+ entry_long++;
+ }
+ }
+
+ return entry;
+}
+
+/*
+ * Read a directory. (the index of the corresponding mapping must be passed).
+ */
+static int read_directory(BDRVVVFATState* s, int mapping_index)
+{
+ mapping_t* mapping = array_get(&(s->mapping), mapping_index);
+ direntry_t* direntry;
+ const char* dirname = mapping->path;
+ int first_cluster = mapping->begin;
+ int parent_index = mapping->info.dir.parent_mapping_index;
+ mapping_t* parent_mapping = (mapping_t*)
+ (parent_index >= 0 ? array_get(&(s->mapping), parent_index) : NULL);
+ int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
+
+ DIR* dir=opendir(dirname);
+ struct dirent* entry;
+ int i;
+
+ assert(mapping->mode & MODE_DIRECTORY);
+
+ if(!dir) {
+ mapping->end = mapping->begin;
+ return -1;
+ }
+
+ i = mapping->info.dir.first_dir_index =
+ first_cluster == 0 ? 0 : s->directory.next;
+
+ /* actually read the directory, and allocate the mappings */
+ while((entry=readdir(dir))) {
+ unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
+ char* buffer;
+ direntry_t* direntry;
+ struct stat st;
+ int is_dot=!strcmp(entry->d_name,".");
+ int is_dotdot=!strcmp(entry->d_name,"..");
+
+ if(first_cluster == 0 && (is_dotdot || is_dot))
+ continue;
+
+ buffer=(char*)qemu_malloc(length);
+ snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
+
+ if(stat(buffer,&st)<0) {
+ free(buffer);
+ continue;
+ }
+
+ /* create directory entry for this file */
+ direntry=create_short_and_long_name(s, i, entry->d_name,
+ is_dot || is_dotdot);
+ direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
+ direntry->reserved[0]=direntry->reserved[1]=0;
+ direntry->ctime=fat_datetime(st.st_ctime,1);
+ direntry->cdate=fat_datetime(st.st_ctime,0);
+ direntry->adate=fat_datetime(st.st_atime,0);
+ direntry->begin_hi=0;
+ direntry->mtime=fat_datetime(st.st_mtime,1);
+ direntry->mdate=fat_datetime(st.st_mtime,0);
+ if(is_dotdot)
+ set_begin_of_direntry(direntry, first_cluster_of_parent);
+ else if(is_dot)
+ set_begin_of_direntry(direntry, first_cluster);
+ else
+ direntry->begin=0; /* do that later */
+ if (st.st_size > 0x7fffffff) {
+ fprintf(stderr, "File %s is larger than 2GB\n", buffer);
+ free(buffer);
+ closedir(dir);
+ return -2;
+ }
+ direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
+
+ /* create mapping for this file */
+ if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
+ s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
+ s->current_mapping->begin=0;
+ s->current_mapping->end=st.st_size;
+ /*
+ * we get the direntry of the most recent direntry, which
+ * contains the short name and all the relevant information.
+ */
+ s->current_mapping->dir_index=s->directory.next-1;
+ s->current_mapping->first_mapping_index = -1;
+ if (S_ISDIR(st.st_mode)) {
+ s->current_mapping->mode = MODE_DIRECTORY;
+ s->current_mapping->info.dir.parent_mapping_index =
+ mapping_index;
+ } else {
+ s->current_mapping->mode = MODE_UNDEFINED;
+ s->current_mapping->info.file.offset = 0;
+ }
+ s->current_mapping->path=buffer;
+ s->current_mapping->read_only =
+ (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
+ }
+ }
+ closedir(dir);
+
+ /* fill with zeroes up to the end of the cluster */
+ while(s->directory.next%(0x10*s->sectors_per_cluster)) {
+ direntry_t* direntry=array_get_next(&(s->directory));
+ memset(direntry,0,sizeof(direntry_t));
+ }
+
+/* TODO: if there are more entries, bootsector has to be adjusted! */
+#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
+ if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
+ /* root directory */
+ int cur = s->directory.next;
+ array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
+ memset(array_get(&(s->directory), cur), 0,
+ (ROOT_ENTRIES - cur) * sizeof(direntry_t));
+ }
+
+ /* reget the mapping, since s->mapping was possibly realloc()ed */
+ mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
+ first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
+ * 0x20 / s->cluster_size;
+ mapping->end = first_cluster;
+
+ direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
+ set_begin_of_direntry(direntry, mapping->begin);
+
+ return 0;
+}
+
+static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
+{
+ return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+}
+
+static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
+{
+ return s->faked_sectors + s->sectors_per_cluster * cluster_num;
+}
+
+static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
+{
+ return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
+}
+
+#ifdef DBG
+static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
+{
+ if(mapping->mode==MODE_UNDEFINED)
+ return 0;
+ return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
+}
+#endif
+
+static int init_directories(BDRVVVFATState* s,
+ const char* dirname)
+{
+ bootsector_t* bootsector;
+ mapping_t* mapping;
+ unsigned int i;
+ unsigned int cluster;
+
+ memset(&(s->first_sectors[0]),0,0x40*0x200);
+
+ s->cluster_size=s->sectors_per_cluster*0x200;
+ s->cluster_buffer=qemu_malloc(s->cluster_size);
+
+ /*
+ * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
+ * where sc is sector_count,
+ * spf is sectors_per_fat,
+ * spc is sectors_per_clusters, and
+ * fat_type = 12, 16 or 32.
+ */
+ i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
+ s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
+
+ array_init(&(s->mapping),sizeof(mapping_t));
+ array_init(&(s->directory),sizeof(direntry_t));
+
+ /* add volume label */
+ {
+ direntry_t* entry=array_get_next(&(s->directory));
+ entry->attributes=0x28; /* archive | volume label */
+ memcpy(entry->name,"QEMU VVF",8);
+ memcpy(entry->extension,"AT ",3);
+ }
+
+ /* Now build FAT, and write back information into directory */
+ init_fat(s);
+
+ s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
+ s->cluster_count=sector2cluster(s, s->sector_count);
+
+ mapping = array_get_next(&(s->mapping));
+ mapping->begin = 0;
+ mapping->dir_index = 0;
+ mapping->info.dir.parent_mapping_index = -1;
+ mapping->first_mapping_index = -1;
+ mapping->path = qemu_strdup(dirname);
+ i = strlen(mapping->path);
+ if (i > 0 && mapping->path[i - 1] == '/')
+ mapping->path[i - 1] = '\0';
+ mapping->mode = MODE_DIRECTORY;
+ mapping->read_only = 0;
+ s->path = mapping->path;
+
+ for (i = 0, cluster = 0; i < s->mapping.next; i++) {
+ /* MS-DOS expects the FAT to be 0 for the root directory
+ * (except for the media byte). */
+ /* LATER TODO: still true for FAT32? */
+ int fix_fat = (i != 0);
+ mapping = array_get(&(s->mapping), i);
+
+ if (mapping->mode & MODE_DIRECTORY) {
+ mapping->begin = cluster;
+ if(read_directory(s, i)) {
+ fprintf(stderr, "Could not read directory %s\n",
+ mapping->path);
+ return -1;
+ }
+ mapping = array_get(&(s->mapping), i);
+ } else {
+ assert(mapping->mode == MODE_UNDEFINED);
+ mapping->mode=MODE_NORMAL;
+ mapping->begin = cluster;
+ if (mapping->end > 0) {
+ direntry_t* direntry = array_get(&(s->directory),
+ mapping->dir_index);
+
+ mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
+ set_begin_of_direntry(direntry, mapping->begin);
+ } else {
+ mapping->end = cluster + 1;
+ fix_fat = 0;
+ }
+ }
+
+ assert(mapping->begin < mapping->end);
+
+ /* next free cluster */
+ cluster = mapping->end;
+
+ if(cluster > s->cluster_count) {
+ fprintf(stderr,"Directory does not fit in FAT%d (capacity %s)\n",
+ s->fat_type,
+ s->fat_type == 12 ? s->sector_count == 2880 ? "1.44 MB"
+ : "2.88 MB"
+ : "504MB");
+ return -EINVAL;
+ }
+
+ /* fix fat for entry */
+ if (fix_fat) {
+ int j;
+ for(j = mapping->begin; j < mapping->end - 1; j++)
+ fat_set(s, j, j+1);
+ fat_set(s, mapping->end - 1, s->max_fat_value);
+ }
+ }
+
+ mapping = array_get(&(s->mapping), 0);
+ s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
+ s->last_cluster_of_root_directory = mapping->end;
+
+ /* the FAT signature */
+ fat_set(s,0,s->max_fat_value);
+ fat_set(s,1,s->max_fat_value);
+
+ s->current_mapping = NULL;
+
+ bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
+ bootsector->jump[0]=0xeb;
+ bootsector->jump[1]=0x3e;
+ bootsector->jump[2]=0x90;
+ memcpy(bootsector->name,"QEMU ",8);
+ bootsector->sector_size=cpu_to_le16(0x200);
+ bootsector->sectors_per_cluster=s->sectors_per_cluster;
+ bootsector->reserved_sectors=cpu_to_le16(1);
+ bootsector->number_of_fats=0x2; /* number of FATs */
+ bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
+ bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
+ bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
+ s->fat.pointer[0] = bootsector->media_type;
+ bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
+ bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
+ bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
+ bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
+ bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
+
+ /* LATER TODO: if FAT32, this is wrong */
+ bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
+ bootsector->u.fat16.current_head=0;
+ bootsector->u.fat16.signature=0x29;
+ bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
+
+ memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
+ memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8);
+ bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
+
+ return 0;
+}
+
+#ifdef DEBUG
+static BDRVVVFATState *vvv = NULL;
+#endif
+
+static int enable_write_target(BDRVVVFATState *s);
+static int is_consistent(BDRVVVFATState *s);
+
+static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
+{
+ BDRVVVFATState *s = bs->opaque;
+ int floppy = 0;
+ int i;
+
+#ifdef DEBUG
+ vvv = s;
+#endif
+
+DLOG(if (stderr == NULL) {
+ stderr = fopen("vvfat.log", "a");
+ setbuf(stderr, NULL);
+})
+
+ s->bs = bs;
+
+ s->fat_type=16;
+ /* LATER TODO: if FAT32, adjust */
+ s->sectors_per_cluster=0x10;
+ /* 504MB disk*/
+ bs->cyls=1024; bs->heads=16; bs->secs=63;
+
+ s->current_cluster=0xffffffff;
+
+ s->first_sectors_number=0x40;
+ /* read only is the default for safety */
+ bs->read_only = 1;
+ s->qcow = s->write_target = NULL;
+ s->qcow_filename = NULL;
+ s->fat2 = NULL;
+ s->downcase_short_names = 1;
+
+ if (!strstart(dirname, "fat:", NULL))
+ return -1;
+
+ if (strstr(dirname, ":floppy:")) {
+ floppy = 1;
+ s->fat_type = 12;
+ s->first_sectors_number = 1;
+ s->sectors_per_cluster=2;
+ bs->cyls = 80; bs->heads = 2; bs->secs = 36;
+ }
+
+ s->sector_count=bs->cyls*bs->heads*bs->secs;
+
+ if (strstr(dirname, ":32:")) {
+ fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
+ s->fat_type = 32;
+ } else if (strstr(dirname, ":16:")) {
+ s->fat_type = 16;
+ } else if (strstr(dirname, ":12:")) {
+ s->fat_type = 12;
+ s->sector_count=2880;
+ }
+
+ if (strstr(dirname, ":rw:")) {
+ if (enable_write_target(s))
+ return -1;
+ bs->read_only = 0;
+ }
+
+ i = strrchr(dirname, ':') - dirname;
+ assert(i >= 3);
+ if (dirname[i-2] == ':' && qemu_isalpha(dirname[i-1]))
+ /* workaround for DOS drive names */
+ dirname += i-1;
+ else
+ dirname += i+1;
+
+ bs->total_sectors=bs->cyls*bs->heads*bs->secs;
+
+ if(init_directories(s, dirname))
+ return -1;
+
+ s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
+
+ if(s->first_sectors_number==0x40)
+ init_mbr(s);
+
+ /* for some reason or other, MS-DOS does not like to know about CHS... */
+ if (floppy)
+ bs->heads = bs->cyls = bs->secs = 0;
+
+ // assert(is_consistent(s));
+ return 0;
+}
+
+static inline void vvfat_close_current_file(BDRVVVFATState *s)
+{
+ if(s->current_mapping) {
+ s->current_mapping = NULL;
+ if (s->current_fd) {
+ close(s->current_fd);
+ s->current_fd = 0;
+ }
+ }
+ s->current_cluster = -1;
+}
+
+/* mappings between index1 and index2-1 are supposed to be ordered
+ * return value is the index of the last mapping for which end>cluster_num
+ */
+static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
+{
+ while(1) {
+ int index3;
+ mapping_t* mapping;
+ index3=(index1+index2)/2;
+ mapping=array_get(&(s->mapping),index3);
+ assert(mapping->begin < mapping->end);
+ if(mapping->begin>=cluster_num) {
+ assert(index2!=index3 || index2==0);
+ if(index2==index3)
+ return index1;
+ index2=index3;
+ } else {
+ if(index1==index3)
+ return mapping->end<=cluster_num ? index2 : index1;
+ index1=index3;
+ }
+ assert(index1<=index2);
+ DLOG(mapping=array_get(&(s->mapping),index1);
+ assert(mapping->begin<=cluster_num);
+ assert(index2 >= s->mapping.next ||
+ ((mapping = array_get(&(s->mapping),index2)) &&
+ mapping->end>cluster_num)));
+ }
+}
+
+static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
+{
+ int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
+ mapping_t* mapping;
+ if(index>=s->mapping.next)
+ return NULL;
+ mapping=array_get(&(s->mapping),index);
+ if(mapping->begin>cluster_num)
+ return NULL;
+ assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
+ return mapping;
+}
+
+/*
+ * This function simply compares path == mapping->path. Since the mappings
+ * are sorted by cluster, this is expensive: O(n).
+ */
+static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
+ const char* path)
+{
+ int i;
+
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->first_mapping_index < 0 &&
+ !strcmp(path, mapping->path))
+ return mapping;
+ }
+
+ return NULL;
+}
+
+static int open_file(BDRVVVFATState* s,mapping_t* mapping)
+{
+ if(!mapping)
+ return -1;
+ if(!s->current_mapping ||
+ strcmp(s->current_mapping->path,mapping->path)) {
+ /* open file */
+ int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
+ if(fd<0)
+ return -1;
+ vvfat_close_current_file(s);
+ s->current_fd = fd;
+ s->current_mapping = mapping;
+ }
+ return 0;
+}
+
+static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
+{
+ if(s->current_cluster != cluster_num) {
+ int result=0;
+ off_t offset;
+ assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
+ if(!s->current_mapping
+ || s->current_mapping->begin>cluster_num
+ || s->current_mapping->end<=cluster_num) {
+ /* binary search of mappings for file */
+ mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
+
+ assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
+
+ if (mapping && mapping->mode & MODE_DIRECTORY) {
+ vvfat_close_current_file(s);
+ s->current_mapping = mapping;
+read_cluster_directory:
+ offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
+ s->cluster = (unsigned char*)s->directory.pointer+offset
+ + 0x20*s->current_mapping->info.dir.first_dir_index;
+ assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
+ assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
+ s->current_cluster = cluster_num;
+ return 0;
+ }
+
+ if(open_file(s,mapping))
+ return -2;
+ } else if (s->current_mapping->mode & MODE_DIRECTORY)
+ goto read_cluster_directory;
+
+ assert(s->current_fd);
+
+ offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
+ if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
+ return -3;
+ s->cluster=s->cluster_buffer;
+ result=read(s->current_fd,s->cluster,s->cluster_size);
+ if(result<0) {
+ s->current_cluster = -1;
+ return -1;
+ }
+ s->current_cluster = cluster_num;
+ }
+ return 0;
+}
+
+#ifdef DEBUG
+static void hexdump(const void* address, uint32_t len)
+{
+ const unsigned char* p = address;
+ int i, j;
+
+ for (i = 0; i < len; i += 16) {
+ for (j = 0; j < 16 && i + j < len; j++)
+ fprintf(stderr, "%02x ", p[i + j]);
+ for (; j < 16; j++)
+ fprintf(stderr, " ");
+ fprintf(stderr, " ");
+ for (j = 0; j < 16 && i + j < len; j++)
+ fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
+ fprintf(stderr, "\n");
+ }
+}
+
+static void print_direntry(const direntry_t* direntry)
+{
+ int j = 0;
+ char buffer[1024];
+
+ fprintf(stderr, "direntry %p: ", direntry);
+ if(!direntry)
+ return;
+ if(is_long_name(direntry)) {
+ unsigned char* c=(unsigned char*)direntry;
+ int i;
+ for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
+#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = 0xb0; j++;}
+ ADD_CHAR(c[i]);
+ for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
+ ADD_CHAR(c[i]);
+ for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
+ ADD_CHAR(c[i]);
+ buffer[j] = 0;
+ fprintf(stderr, "%s\n", buffer);
+ } else {
+ int i;
+ for(i=0;i<11;i++)
+ ADD_CHAR(direntry->name[i]);
+ buffer[j] = 0;
+ fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
+ buffer,
+ direntry->attributes,
+ begin_of_direntry(direntry),le32_to_cpu(direntry->size));
+ }
+}
+
+static void print_mapping(const mapping_t* mapping)
+{
+ fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, "
+ "first_mapping_index = %d, name = %s, mode = 0x%x, " ,
+ mapping, mapping->begin, mapping->end, mapping->dir_index,
+ mapping->first_mapping_index, mapping->path, mapping->mode);
+
+ if (mapping->mode & MODE_DIRECTORY)
+ fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
+ else
+ fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
+}
+#endif
+
+static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+{
+ BDRVVVFATState *s = bs->opaque;
+ int i;
+
+ for(i=0;i<nb_sectors;i++,sector_num++) {
+ if (sector_num >= s->sector_count)
+ return -1;
+ if (s->qcow) {
+ int n;
+ if (s->qcow->drv->bdrv_is_allocated(s->qcow,
+ sector_num, nb_sectors-i, &n)) {
+DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
+ if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
+ return -1;
+ i += n - 1;
+ sector_num += n - 1;
+ continue;
+ }
+DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
+ }
+ if(sector_num<s->faked_sectors) {
+ if(sector_num<s->first_sectors_number)
+ memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
+ else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
+ memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
+ else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
+ memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
+ } else {
+ uint32_t sector=sector_num-s->faked_sectors,
+ sector_offset_in_cluster=(sector%s->sectors_per_cluster),
+ cluster_num=sector/s->sectors_per_cluster;
+ if(read_cluster(s, cluster_num) != 0) {
+ /* LATER TODO: strict: return -1; */
+ memset(buf+i*0x200,0,0x200);
+ continue;
+ }
+ memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
+ }
+ }
+ return 0;
+}
+
+/* LATER TODO: statify all functions */
+
+/*
+ * Idea of the write support (use snapshot):
+ *
+ * 1. check if all data is consistent, recording renames, modifications,
+ * new files and directories (in s->commits).
+ *
+ * 2. if the data is not consistent, stop committing
+ *
+ * 3. handle renames, and create new files and directories (do not yet
+ * write their contents)
+ *
+ * 4. walk the directories, fixing the mapping and direntries, and marking
+ * the handled mappings as not deleted
+ *
+ * 5. commit the contents of the files
+ *
+ * 6. handle deleted files and directories
+ *
+ */
+
+typedef struct commit_t {
+ char* path;
+ union {
+ struct { uint32_t cluster; } rename;
+ struct { int dir_index; uint32_t modified_offset; } writeout;
+ struct { uint32_t first_cluster; } new_file;
+ struct { uint32_t cluster; } mkdir;
+ } param;
+ /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
+ enum {
+ ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
+ } action;
+} commit_t;
+
+static void clear_commits(BDRVVVFATState* s)
+{
+ int i;
+DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
+ for (i = 0; i < s->commits.next; i++) {
+ commit_t* commit = array_get(&(s->commits), i);
+ assert(commit->path || commit->action == ACTION_WRITEOUT);
+ if (commit->action != ACTION_WRITEOUT) {
+ assert(commit->path);
+ free(commit->path);
+ } else
+ assert(commit->path == NULL);
+ }
+ s->commits.next = 0;
+}
+
+static void schedule_rename(BDRVVVFATState* s,
+ uint32_t cluster, char* new_path)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = new_path;
+ commit->param.rename.cluster = cluster;
+ commit->action = ACTION_RENAME;
+}
+
+static void schedule_writeout(BDRVVVFATState* s,
+ int dir_index, uint32_t modified_offset)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = NULL;
+ commit->param.writeout.dir_index = dir_index;
+ commit->param.writeout.modified_offset = modified_offset;
+ commit->action = ACTION_WRITEOUT;
+}
+
+static void schedule_new_file(BDRVVVFATState* s,
+ char* path, uint32_t first_cluster)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = path;
+ commit->param.new_file.first_cluster = first_cluster;
+ commit->action = ACTION_NEW_FILE;
+}
+
+static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
+{
+ commit_t* commit = array_get_next(&(s->commits));
+ commit->path = path;
+ commit->param.mkdir.cluster = cluster;
+ commit->action = ACTION_MKDIR;
+}
+
+typedef struct {
+ /*
+ * Since the sequence number is at most 0x3f, and the filename
+ * length is at most 13 times the sequence number, the maximal
+ * filename length is 0x3f * 13 bytes.
+ */
+ unsigned char name[0x3f * 13 + 1];
+ int checksum, len;
+ int sequence_number;
+} long_file_name;
+
+static void lfn_init(long_file_name* lfn)
+{
+ lfn->sequence_number = lfn->len = 0;
+ lfn->checksum = 0x100;
+}
+
+/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
+static int parse_long_name(long_file_name* lfn,
+ const direntry_t* direntry)
+{
+ int i, j, offset;
+ const unsigned char* pointer = (const unsigned char*)direntry;
+
+ if (!is_long_name(direntry))
+ return 1;
+
+ if (pointer[0] & 0x40) {
+ lfn->sequence_number = pointer[0] & 0x3f;
+ lfn->checksum = pointer[13];
+ lfn->name[0] = 0;
+ lfn->name[lfn->sequence_number * 13] = 0;
+ } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
+ return -1;
+ else if (pointer[13] != lfn->checksum)
+ return -2;
+ else if (pointer[12] || pointer[26] || pointer[27])
+ return -3;
+
+ offset = 13 * (lfn->sequence_number - 1);
+ for (i = 0, j = 1; i < 13; i++, j+=2) {
+ if (j == 11)
+ j = 14;
+ else if (j == 26)
+ j = 28;
+
+ if (pointer[j+1] == 0)
+ lfn->name[offset + i] = pointer[j];
+ else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
+ return -4;
+ else
+ lfn->name[offset + i] = 0;
+ }
+
+ if (pointer[0] & 0x40)
+ lfn->len = offset + strlen((char*)lfn->name + offset);
+
+ return 0;
+}
+
+/* returns 0 if successful, >0 if no short_name, and <0 on error */
+static int parse_short_name(BDRVVVFATState* s,
+ long_file_name* lfn, direntry_t* direntry)
+{
+ int i, j;
+
+ if (!is_short_name(direntry))
+ return 1;
+
+ for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
+ for (i = 0; i <= j; i++) {
+ if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
+ return -1;
+ else if (s->downcase_short_names)
+ lfn->name[i] = qemu_tolower(direntry->name[i]);
+ else
+ lfn->name[i] = direntry->name[i];
+ }
+
+ for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
+ if (j >= 0) {
+ lfn->name[i++] = '.';
+ lfn->name[i + j + 1] = '\0';
+ for (;j >= 0; j--) {
+ if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
+ return -2;
+ else if (s->downcase_short_names)
+ lfn->name[i + j] = qemu_tolower(direntry->extension[j]);
+ else
+ lfn->name[i + j] = direntry->extension[j];
+ }
+ } else
+ lfn->name[i + j + 1] = '\0';
+
+ lfn->len = strlen((char*)lfn->name);
+
+ return 0;
+}
+
+static inline uint32_t modified_fat_get(BDRVVVFATState* s,
+ unsigned int cluster)
+{
+ if (cluster < s->last_cluster_of_root_directory) {
+ if (cluster + 1 == s->last_cluster_of_root_directory)
+ return s->max_fat_value;
+ else
+ return cluster + 1;
+ }
+
+ if (s->fat_type==32) {
+ uint32_t* entry=((uint32_t*)s->fat2)+cluster;
+ return le32_to_cpu(*entry);
+ } else if (s->fat_type==16) {
+ uint16_t* entry=((uint16_t*)s->fat2)+cluster;
+ return le16_to_cpu(*entry);
+ } else {
+ const uint8_t* x=s->fat2+cluster*3/2;
+ return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
+ }
+}
+
+static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
+{
+ int was_modified = 0;
+ int i, dummy;
+
+ if (s->qcow == NULL)
+ return 0;
+
+ for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
+ was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
+ cluster2sector(s, cluster_num) + i, 1, &dummy);
+
+ return was_modified;
+}
+
+static const char* get_basename(const char* path)
+{
+ char* basename = strrchr(path, '/');
+ if (basename == NULL)
+ return path;
+ else
+ return basename + 1; /* strip '/' */
+}
+
+/*
+ * The array s->used_clusters holds the states of the clusters. If it is
+ * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
+ * was modified, bit 3 is set.
+ * If any cluster is allocated, but not part of a file or directory, this
+ * driver refuses to commit.
+ */
+typedef enum {
+ USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
+} used_t;
+
+/*
+ * get_cluster_count_for_direntry() not only determines how many clusters
+ * are occupied by direntry, but also if it was renamed or modified.
+ *
+ * A file is thought to be renamed *only* if there already was a file with
+ * exactly the same first cluster, but a different name.
+ *
+ * Further, the files/directories handled by this function are
+ * assumed to be *not* deleted (and *only* those).
+ */
+static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
+ direntry_t* direntry, const char* path)
+{
+ /*
+ * This is a little bit tricky:
+ * IF the guest OS just inserts a cluster into the file chain,
+ * and leaves the rest alone, (i.e. the original file had clusters
+ * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
+ *
+ * - do_commit will write the cluster into the file at the given
+ * offset, but
+ *
+ * - the cluster which is overwritten should be moved to a later
+ * position in the file.
+ *
+ * I am not aware that any OS does something as braindead, but this
+ * situation could happen anyway when not committing for a long time.
+ * Just to be sure that this does not bite us, detect it, and copy the
+ * contents of the clusters to-be-overwritten into the qcow.
+ */
+ int copy_it = 0;
+ int was_modified = 0;
+ int32_t ret = 0;
+
+ uint32_t cluster_num = begin_of_direntry(direntry);
+ uint32_t offset = 0;
+ int first_mapping_index = -1;
+ mapping_t* mapping = NULL;
+ const char* basename2 = NULL;
+
+ vvfat_close_current_file(s);
+
+ /* the root directory */
+ if (cluster_num == 0)
+ return 0;
+
+ /* write support */
+ if (s->qcow) {
+ basename2 = get_basename(path);
+
+ mapping = find_mapping_for_cluster(s, cluster_num);
+
+ if (mapping) {
+ const char* basename;
+
+ assert(mapping->mode & MODE_DELETED);
+ mapping->mode &= ~MODE_DELETED;
+
+ basename = get_basename(mapping->path);
+
+ assert(mapping->mode & MODE_NORMAL);
+
+ /* rename */
+ if (strcmp(basename, basename2))
+ schedule_rename(s, cluster_num, qemu_strdup(path));
+ } else if (is_file(direntry))
+ /* new file */
+ schedule_new_file(s, qemu_strdup(path), cluster_num);
+ else {
+ abort();
+ return 0;
+ }
+ }
+
+ while(1) {
+ if (s->qcow) {
+ if (!copy_it && cluster_was_modified(s, cluster_num)) {
+ if (mapping == NULL ||
+ mapping->begin > cluster_num ||
+ mapping->end <= cluster_num)
+ mapping = find_mapping_for_cluster(s, cluster_num);
+
+
+ if (mapping &&
+ (mapping->mode & MODE_DIRECTORY) == 0) {
+
+ /* was modified in qcow */
+ if (offset != mapping->info.file.offset + s->cluster_size
+ * (cluster_num - mapping->begin)) {
+ /* offset of this cluster in file chain has changed */
+ abort();
+ copy_it = 1;
+ } else if (offset == 0) {
+ const char* basename = get_basename(mapping->path);
+
+ if (strcmp(basename, basename2))
+ copy_it = 1;
+ first_mapping_index = array_index(&(s->mapping), mapping);
+ }
+
+ if (mapping->first_mapping_index != first_mapping_index
+ && mapping->info.file.offset > 0) {
+ abort();
+ copy_it = 1;
+ }
+
+ /* need to write out? */
+ if (!was_modified && is_file(direntry)) {
+ was_modified = 1;
+ schedule_writeout(s, mapping->dir_index, offset);
+ }
+ }
+ }
+
+ if (copy_it) {
+ int i, dummy;
+ /*
+ * This is horribly inefficient, but that is okay, since
+ * it is rarely executed, if at all.
+ */
+ int64_t offset = cluster2sector(s, cluster_num);
+
+ vvfat_close_current_file(s);
+ for (i = 0; i < s->sectors_per_cluster; i++)
+ if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
+ offset + i, 1, &dummy)) {
+ if (vvfat_read(s->bs,
+ offset, s->cluster_buffer, 1))
+ return -1;
+ if (s->qcow->drv->bdrv_write(s->qcow,
+ offset, s->cluster_buffer, 1))
+ return -2;
+ }
+ }
+ }
+
+ ret++;
+ if (s->used_clusters[cluster_num] & USED_ANY)
+ return 0;
+ s->used_clusters[cluster_num] = USED_FILE;
+
+ cluster_num = modified_fat_get(s, cluster_num);
+
+ if (fat_eof(s, cluster_num))
+ return ret;
+ else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
+ return -1;
+
+ offset += s->cluster_size;
+ }
+}
+
+/*
+ * This function looks at the modified data (qcow).
+ * It returns 0 upon inconsistency or error, and the number of clusters
+ * used by the directory, its subdirectories and their files.
+ */
+static int check_directory_consistency(BDRVVVFATState *s,
+ int cluster_num, const char* path)
+{
+ int ret = 0;
+ unsigned char* cluster = qemu_malloc(s->cluster_size);
+ direntry_t* direntries = (direntry_t*)cluster;
+ mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
+
+ long_file_name lfn;
+ int path_len = strlen(path);
+ char path2[PATH_MAX];
+
+ assert(path_len < PATH_MAX); /* len was tested before! */
+ pstrcpy(path2, sizeof(path2), path);
+ path2[path_len] = '/';
+ path2[path_len + 1] = '\0';
+
+ if (mapping) {
+ const char* basename = get_basename(mapping->path);
+ const char* basename2 = get_basename(path);
+
+ assert(mapping->mode & MODE_DIRECTORY);
+
+ assert(mapping->mode & MODE_DELETED);
+ mapping->mode &= ~MODE_DELETED;
+
+ if (strcmp(basename, basename2))
+ schedule_rename(s, cluster_num, qemu_strdup(path));
+ } else
+ /* new directory */
+ schedule_mkdir(s, cluster_num, qemu_strdup(path));
+
+ lfn_init(&lfn);
+ do {
+ int i;
+ int subret = 0;
+
+ ret++;
+
+ if (s->used_clusters[cluster_num] & USED_ANY) {
+ fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
+ return 0;
+ }
+ s->used_clusters[cluster_num] = USED_DIRECTORY;
+
+DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
+ subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
+ s->sectors_per_cluster);
+ if (subret) {
+ fprintf(stderr, "Error fetching direntries\n");
+ fail:
+ free(cluster);
+ return 0;
+ }
+
+ for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
+ int cluster_count = 0;
+
+DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
+ if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
+ is_free(direntries + i))
+ continue;
+
+ subret = parse_long_name(&lfn, direntries + i);
+ if (subret < 0) {
+ fprintf(stderr, "Error in long name\n");
+ goto fail;
+ }
+ if (subret == 0 || is_free(direntries + i))
+ continue;
+
+ if (fat_chksum(direntries+i) != lfn.checksum) {
+ subret = parse_short_name(s, &lfn, direntries + i);
+ if (subret < 0) {
+ fprintf(stderr, "Error in short name (%d)\n", subret);
+ goto fail;
+ }
+ if (subret > 0 || !strcmp((char*)lfn.name, ".")
+ || !strcmp((char*)lfn.name, ".."))
+ continue;
+ }
+ lfn.checksum = 0x100; /* cannot use long name twice */
+
+ if (path_len + 1 + lfn.len >= PATH_MAX) {
+ fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
+ goto fail;
+ }
+ pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
+ (char*)lfn.name);
+
+ if (is_directory(direntries + i)) {
+ if (begin_of_direntry(direntries + i) == 0) {
+ DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
+ goto fail;
+ }
+ cluster_count = check_directory_consistency(s,
+ begin_of_direntry(direntries + i), path2);
+ if (cluster_count == 0) {
+ DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
+ goto fail;
+ }
+ } else if (is_file(direntries + i)) {
+ /* check file size with FAT */
+ cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
+ if (cluster_count !=
+ (le32_to_cpu(direntries[i].size) + s->cluster_size
+ - 1) / s->cluster_size) {
+ DLOG(fprintf(stderr, "Cluster count mismatch\n"));
+ goto fail;
+ }
+ } else
+ abort(); /* cluster_count = 0; */
+
+ ret += cluster_count;
+ }
+
+ cluster_num = modified_fat_get(s, cluster_num);
+ } while(!fat_eof(s, cluster_num));
+
+ free(cluster);
+ return ret;
+}
+
+/* returns 1 on success */
+static int is_consistent(BDRVVVFATState* s)
+{
+ int i, check;
+ int used_clusters_count = 0;
+
+DLOG(checkpoint());
+ /*
+ * - get modified FAT
+ * - compare the two FATs (TODO)
+ * - get buffer for marking used clusters
+ * - recurse direntries from root (using bs->bdrv_read to make
+ * sure to get the new data)
+ * - check that the FAT agrees with the size
+ * - count the number of clusters occupied by this directory and
+ * its files
+ * - check that the cumulative used cluster count agrees with the
+ * FAT
+ * - if all is fine, return number of used clusters
+ */
+ if (s->fat2 == NULL) {
+ int size = 0x200 * s->sectors_per_fat;
+ s->fat2 = qemu_malloc(size);
+ memcpy(s->fat2, s->fat.pointer, size);
+ }
+ check = vvfat_read(s->bs,
+ s->first_sectors_number, s->fat2, s->sectors_per_fat);
+ if (check) {
+ fprintf(stderr, "Could not copy fat\n");
+ return 0;
+ }
+ assert (s->used_clusters);
+ for (i = 0; i < sector2cluster(s, s->sector_count); i++)
+ s->used_clusters[i] &= ~USED_ANY;
+
+ clear_commits(s);
+
+ /* mark every mapped file/directory as deleted.
+ * (check_directory_consistency() will unmark those still present). */
+ if (s->qcow)
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->first_mapping_index < 0)
+ mapping->mode |= MODE_DELETED;
+ }
+
+ used_clusters_count = check_directory_consistency(s, 0, s->path);
+ if (used_clusters_count <= 0) {
+ DLOG(fprintf(stderr, "problem in directory\n"));
+ return 0;
+ }
+
+ check = s->last_cluster_of_root_directory;
+ for (i = check; i < sector2cluster(s, s->sector_count); i++) {
+ if (modified_fat_get(s, i)) {
+ if(!s->used_clusters[i]) {
+ DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
+ return 0;
+ }
+ check++;
+ }
+
+ if (s->used_clusters[i] == USED_ALLOCATED) {
+ /* allocated, but not used... */
+ DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
+ return 0;
+ }
+ }
+
+ if (check != used_clusters_count)
+ return 0;
+
+ return used_clusters_count;
+}
+
+static inline void adjust_mapping_indices(BDRVVVFATState* s,
+ int offset, int adjust)
+{
+ int i;
+
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+
+#define ADJUST_MAPPING_INDEX(name) \
+ if (mapping->name >= offset) \
+ mapping->name += adjust
+
+ ADJUST_MAPPING_INDEX(first_mapping_index);
+ if (mapping->mode & MODE_DIRECTORY)
+ ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
+ }
+}
+
+/* insert or update mapping */
+static mapping_t* insert_mapping(BDRVVVFATState* s,
+ uint32_t begin, uint32_t end)
+{
+ /*
+ * - find mapping where mapping->begin >= begin,
+ * - if mapping->begin > begin: insert
+ * - adjust all references to mappings!
+ * - else: adjust
+ * - replace name
+ */
+ int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
+ mapping_t* mapping = NULL;
+ mapping_t* first_mapping = array_get(&(s->mapping), 0);
+
+ if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
+ && mapping->begin < begin) {
+ mapping->end = begin;
+ index++;
+ mapping = array_get(&(s->mapping), index);
+ }
+ if (index >= s->mapping.next || mapping->begin > begin) {
+ mapping = array_insert(&(s->mapping), index, 1);
+ mapping->path = NULL;
+ adjust_mapping_indices(s, index, +1);
+ }
+
+ mapping->begin = begin;
+ mapping->end = end;
+
+DLOG(mapping_t* next_mapping;
+assert(index + 1 >= s->mapping.next ||
+((next_mapping = array_get(&(s->mapping), index + 1)) &&
+ next_mapping->begin >= end)));
+
+ if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
+ s->current_mapping = array_get(&(s->mapping),
+ s->current_mapping - first_mapping);
+
+ return mapping;
+}
+
+static int remove_mapping(BDRVVVFATState* s, int mapping_index)
+{
+ mapping_t* mapping = array_get(&(s->mapping), mapping_index);
+ mapping_t* first_mapping = array_get(&(s->mapping), 0);
+
+ /* free mapping */
+ if (mapping->first_mapping_index < 0)
+ free(mapping->path);
+
+ /* remove from s->mapping */
+ array_remove(&(s->mapping), mapping_index);
+
+ /* adjust all references to mappings */
+ adjust_mapping_indices(s, mapping_index, -1);
+
+ if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
+ s->current_mapping = array_get(&(s->mapping),
+ s->current_mapping - first_mapping);
+
+ return 0;
+}
+
+static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
+{
+ int i;
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->dir_index >= offset)
+ mapping->dir_index += adjust;
+ if ((mapping->mode & MODE_DIRECTORY) &&
+ mapping->info.dir.first_dir_index >= offset)
+ mapping->info.dir.first_dir_index += adjust;
+ }
+}
+
+static direntry_t* insert_direntries(BDRVVVFATState* s,
+ int dir_index, int count)
+{
+ /*
+ * make room in s->directory,
+ * adjust_dirindices
+ */
+ direntry_t* result = array_insert(&(s->directory), dir_index, count);
+ if (result == NULL)
+ return NULL;
+ adjust_dirindices(s, dir_index, count);
+ return result;
+}
+
+static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
+{
+ int ret = array_remove_slice(&(s->directory), dir_index, count);
+ if (ret)
+ return ret;
+ adjust_dirindices(s, dir_index, -count);
+ return 0;
+}
+
+/*
+ * Adapt the mappings of the cluster chain starting at first cluster
+ * (i.e. if a file starts at first_cluster, the chain is followed according
+ * to the modified fat, and the corresponding entries in s->mapping are
+ * adjusted)
+ */
+static int commit_mappings(BDRVVVFATState* s,
+ uint32_t first_cluster, int dir_index)
+{
+ mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
+ direntry_t* direntry = array_get(&(s->directory), dir_index);
+ uint32_t cluster = first_cluster;
+
+ vvfat_close_current_file(s);
+
+ assert(mapping);
+ assert(mapping->begin == first_cluster);
+ mapping->first_mapping_index = -1;
+ mapping->dir_index = dir_index;
+ mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
+ MODE_DIRECTORY : MODE_NORMAL;
+
+ while (!fat_eof(s, cluster)) {
+ uint32_t c, c1;
+
+ for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
+ c = c1, c1 = modified_fat_get(s, c1));
+
+ c++;
+ if (c > mapping->end) {
+ int index = array_index(&(s->mapping), mapping);
+ int i, max_i = s->mapping.next - index;
+ for (i = 1; i < max_i && mapping[i].begin < c; i++);
+ while (--i > 0)
+ remove_mapping(s, index + 1);
+ }
+ assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
+ || mapping[1].begin >= c);
+ mapping->end = c;
+
+ if (!fat_eof(s, c1)) {
+ int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
+ mapping_t* next_mapping = i >= s->mapping.next ? NULL :
+ array_get(&(s->mapping), i);
+
+ if (next_mapping == NULL || next_mapping->begin > c1) {
+ int i1 = array_index(&(s->mapping), mapping);
+
+ next_mapping = insert_mapping(s, c1, c1+1);
+
+ if (c1 < c)
+ i1++;
+ mapping = array_get(&(s->mapping), i1);
+ }
+
+ next_mapping->dir_index = mapping->dir_index;
+ next_mapping->first_mapping_index =
+ mapping->first_mapping_index < 0 ?
+ array_index(&(s->mapping), mapping) :
+ mapping->first_mapping_index;
+ next_mapping->path = mapping->path;
+ next_mapping->mode = mapping->mode;
+ next_mapping->read_only = mapping->read_only;
+ if (mapping->mode & MODE_DIRECTORY) {
+ next_mapping->info.dir.parent_mapping_index =
+ mapping->info.dir.parent_mapping_index;
+ next_mapping->info.dir.first_dir_index =
+ mapping->info.dir.first_dir_index +
+ 0x10 * s->sectors_per_cluster *
+ (mapping->end - mapping->begin);
+ } else
+ next_mapping->info.file.offset = mapping->info.file.offset +
+ mapping->end - mapping->begin;
+
+ mapping = next_mapping;
+ }
+
+ cluster = c1;
+ }
+
+ return 0;
+}
+
+static int commit_direntries(BDRVVVFATState* s,
+ int dir_index, int parent_mapping_index)
+{
+ direntry_t* direntry = array_get(&(s->directory), dir_index);
+ uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
+ mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
+
+ int factor = 0x10 * s->sectors_per_cluster;
+ int old_cluster_count, new_cluster_count;
+ int current_dir_index = mapping->info.dir.first_dir_index;
+ int first_dir_index = current_dir_index;
+ int ret, i;
+ uint32_t c;
+
+DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
+
+ assert(direntry);
+ assert(mapping);
+ assert(mapping->begin == first_cluster);
+ assert(mapping->info.dir.first_dir_index < s->directory.next);
+ assert(mapping->mode & MODE_DIRECTORY);
+ assert(dir_index == 0 || is_directory(direntry));
+
+ mapping->info.dir.parent_mapping_index = parent_mapping_index;
+
+ if (first_cluster == 0) {
+ old_cluster_count = new_cluster_count =
+ s->last_cluster_of_root_directory;
+ } else {
+ for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
+ c = fat_get(s, c))
+ old_cluster_count++;
+
+ for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
+ c = modified_fat_get(s, c))
+ new_cluster_count++;
+ }
+
+ if (new_cluster_count > old_cluster_count) {
+ if (insert_direntries(s,
+ current_dir_index + factor * old_cluster_count,
+ factor * (new_cluster_count - old_cluster_count)) == NULL)
+ return -1;
+ } else if (new_cluster_count < old_cluster_count)
+ remove_direntries(s,
+ current_dir_index + factor * new_cluster_count,
+ factor * (old_cluster_count - new_cluster_count));
+
+ for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
+ void* direntry = array_get(&(s->directory), current_dir_index);
+ int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
+ s->sectors_per_cluster);
+ if (ret)
+ return ret;
+ assert(!strncmp(s->directory.pointer, "QEMU", 4));
+ current_dir_index += factor;
+ }
+
+ ret = commit_mappings(s, first_cluster, dir_index);
+ if (ret)
+ return ret;
+
+ /* recurse */
+ for (i = 0; i < factor * new_cluster_count; i++) {
+ direntry = array_get(&(s->directory), first_dir_index + i);
+ if (is_directory(direntry) && !is_dot(direntry)) {
+ mapping = find_mapping_for_cluster(s, first_cluster);
+ assert(mapping->mode & MODE_DIRECTORY);
+ ret = commit_direntries(s, first_dir_index + i,
+ array_index(&(s->mapping), mapping));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* commit one file (adjust contents, adjust mapping),
+ return first_mapping_index */
+static int commit_one_file(BDRVVVFATState* s,
+ int dir_index, uint32_t offset)
+{
+ direntry_t* direntry = array_get(&(s->directory), dir_index);
+ uint32_t c = begin_of_direntry(direntry);
+ uint32_t first_cluster = c;
+ mapping_t* mapping = find_mapping_for_cluster(s, c);
+ uint32_t size = filesize_of_direntry(direntry);
+ char* cluster = qemu_malloc(s->cluster_size);
+ uint32_t i;
+ int fd = 0;
+
+ assert(offset < size);
+ assert((offset % s->cluster_size) == 0);
+
+ for (i = s->cluster_size; i < offset; i += s->cluster_size)
+ c = modified_fat_get(s, c);
+
+ fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
+ if (fd < 0) {
+ fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
+ strerror(errno), errno);
+ return fd;
+ }
+ if (offset > 0)
+ if (lseek(fd, offset, SEEK_SET) != offset)
+ return -3;
+
+ while (offset < size) {
+ uint32_t c1;
+ int rest_size = (size - offset > s->cluster_size ?
+ s->cluster_size : size - offset);
+ int ret;
+
+ c1 = modified_fat_get(s, c);
+
+ assert((size - offset == 0 && fat_eof(s, c)) ||
+ (size > offset && c >=2 && !fat_eof(s, c)));
+
+ ret = vvfat_read(s->bs, cluster2sector(s, c),
+ (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
+
+ if (ret < 0)
+ return ret;
+
+ if (write(fd, cluster, rest_size) < 0)
+ return -2;
+
+ offset += rest_size;
+ c = c1;
+ }
+
+ if (ftruncate(fd, size)) {
+ perror("ftruncate()");
+ close(fd);
+ return -4;
+ }
+ close(fd);
+
+ return commit_mappings(s, first_cluster, dir_index);
+}
+
+#ifdef DEBUG
+/* test, if all mappings point to valid direntries */
+static void check1(BDRVVVFATState* s)
+{
+ int i;
+ for (i = 0; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->mode & MODE_DELETED) {
+ fprintf(stderr, "deleted\n");
+ continue;
+ }
+ assert(mapping->dir_index < s->directory.next);
+ direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
+ assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
+ if (mapping->mode & MODE_DIRECTORY) {
+ assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
+ assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
+ }
+ }
+}
+
+/* test, if all direntries have mappings */
+static void check2(BDRVVVFATState* s)
+{
+ int i;
+ int first_mapping = -1;
+
+ for (i = 0; i < s->directory.next; i++) {
+ direntry_t* direntry = array_get(&(s->directory), i);
+
+ if (is_short_name(direntry) && begin_of_direntry(direntry)) {
+ mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
+ assert(mapping);
+ assert(mapping->dir_index == i || is_dot(direntry));
+ assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
+ }
+
+ if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
+ /* cluster start */
+ int j, count = 0;
+
+ for (j = 0; j < s->mapping.next; j++) {
+ mapping_t* mapping = array_get(&(s->mapping), j);
+ if (mapping->mode & MODE_DELETED)
+ continue;
+ if (mapping->mode & MODE_DIRECTORY) {
+ if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
+ assert(++count == 1);
+ if (mapping->first_mapping_index == -1)
+ first_mapping = array_index(&(s->mapping), mapping);
+ else
+ assert(first_mapping == mapping->first_mapping_index);
+ if (mapping->info.dir.parent_mapping_index < 0)
+ assert(j == 0);
+ else {
+ mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
+ assert(parent->mode & MODE_DIRECTORY);
+ assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
+ }
+ }
+ }
+ }
+ if (count == 0)
+ first_mapping = -1;
+ }
+ }
+}
+#endif
+
+static int handle_renames_and_mkdirs(BDRVVVFATState* s)
+{
+ int i;
+
+#ifdef DEBUG
+ fprintf(stderr, "handle_renames\n");
+ for (i = 0; i < s->commits.next; i++) {
+ commit_t* commit = array_get(&(s->commits), i);
+ fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
+ }
+#endif
+
+ for (i = 0; i < s->commits.next;) {
+ commit_t* commit = array_get(&(s->commits), i);
+ if (commit->action == ACTION_RENAME) {
+ mapping_t* mapping = find_mapping_for_cluster(s,
+ commit->param.rename.cluster);
+ char* old_path = mapping->path;
+
+ assert(commit->path);
+ mapping->path = commit->path;
+ if (rename(old_path, mapping->path))
+ return -2;
+
+ if (mapping->mode & MODE_DIRECTORY) {
+ int l1 = strlen(mapping->path);
+ int l2 = strlen(old_path);
+ int diff = l1 - l2;
+ direntry_t* direntry = array_get(&(s->directory),
+ mapping->info.dir.first_dir_index);
+ uint32_t c = mapping->begin;
+ int i = 0;
+
+ /* recurse */
+ while (!fat_eof(s, c)) {
+ do {
+ direntry_t* d = direntry + i;
+
+ if (is_file(d) || (is_directory(d) && !is_dot(d))) {
+ mapping_t* m = find_mapping_for_cluster(s,
+ begin_of_direntry(d));
+ int l = strlen(m->path);
+ char* new_path = qemu_malloc(l + diff + 1);
+
+ assert(!strncmp(m->path, mapping->path, l2));
+
+ pstrcpy(new_path, l + diff + 1, mapping->path);
+ pstrcpy(new_path + l1, l + diff + 1 - l1,
+ m->path + l2);
+
+ schedule_rename(s, m->begin, new_path);
+ }
+ i++;
+ } while((i % (0x10 * s->sectors_per_cluster)) != 0);
+ c = fat_get(s, c);
+ }
+ }
+
+ free(old_path);
+ array_remove(&(s->commits), i);
+ continue;
+ } else if (commit->action == ACTION_MKDIR) {
+ mapping_t* mapping;
+ int j, parent_path_len;
+
+#ifdef __MINGW32__
+ if (mkdir(commit->path))
+ return -5;
+#else
+ if (mkdir(commit->path, 0755))
+ return -5;
+#endif
+
+ mapping = insert_mapping(s, commit->param.mkdir.cluster,
+ commit->param.mkdir.cluster + 1);
+ if (mapping == NULL)
+ return -6;
+
+ mapping->mode = MODE_DIRECTORY;
+ mapping->read_only = 0;
+ mapping->path = commit->path;
+ j = s->directory.next;
+ assert(j);
+ insert_direntries(s, s->directory.next,
+ 0x10 * s->sectors_per_cluster);
+ mapping->info.dir.first_dir_index = j;
+
+ parent_path_len = strlen(commit->path)
+ - strlen(get_basename(commit->path)) - 1;
+ for (j = 0; j < s->mapping.next; j++) {
+ mapping_t* m = array_get(&(s->mapping), j);
+ if (m->first_mapping_index < 0 && m != mapping &&
+ !strncmp(m->path, mapping->path, parent_path_len) &&
+ strlen(m->path) == parent_path_len)
+ break;
+ }
+ assert(j < s->mapping.next);
+ mapping->info.dir.parent_mapping_index = j;
+
+ array_remove(&(s->commits), i);
+ continue;
+ }
+
+ i++;
+ }
+ return 0;
+}
+
+/*
+ * TODO: make sure that the short name is not matching *another* file
+ */
+static int handle_commits(BDRVVVFATState* s)
+{
+ int i, fail = 0;
+
+ vvfat_close_current_file(s);
+
+ for (i = 0; !fail && i < s->commits.next; i++) {
+ commit_t* commit = array_get(&(s->commits), i);
+ switch(commit->action) {
+ case ACTION_RENAME: case ACTION_MKDIR:
+ abort();
+ fail = -2;
+ break;
+ case ACTION_WRITEOUT: {
+#ifndef NDEBUG
+ /* these variables are only used by assert() below */
+ direntry_t* entry = array_get(&(s->directory),
+ commit->param.writeout.dir_index);
+ uint32_t begin = begin_of_direntry(entry);
+ mapping_t* mapping = find_mapping_for_cluster(s, begin);
+#endif
+
+ assert(mapping);
+ assert(mapping->begin == begin);
+ assert(commit->path == NULL);
+
+ if (commit_one_file(s, commit->param.writeout.dir_index,
+ commit->param.writeout.modified_offset))
+ fail = -3;
+
+ break;
+ }
+ case ACTION_NEW_FILE: {
+ int begin = commit->param.new_file.first_cluster;
+ mapping_t* mapping = find_mapping_for_cluster(s, begin);
+ direntry_t* entry;
+ int i;
+
+ /* find direntry */
+ for (i = 0; i < s->directory.next; i++) {
+ entry = array_get(&(s->directory), i);
+ if (is_file(entry) && begin_of_direntry(entry) == begin)
+ break;
+ }
+
+ if (i >= s->directory.next) {
+ fail = -6;
+ continue;
+ }
+
+ /* make sure there exists an initial mapping */
+ if (mapping && mapping->begin != begin) {
+ mapping->end = begin;
+ mapping = NULL;
+ }
+ if (mapping == NULL) {
+ mapping = insert_mapping(s, begin, begin+1);
+ }
+ /* most members will be fixed in commit_mappings() */
+ assert(commit->path);
+ mapping->path = commit->path;
+ mapping->read_only = 0;
+ mapping->mode = MODE_NORMAL;
+ mapping->info.file.offset = 0;
+
+ if (commit_one_file(s, i, 0))
+ fail = -7;
+
+ break;
+ }
+ default:
+ abort();
+ }
+ }
+ if (i > 0 && array_remove_slice(&(s->commits), 0, i))
+ return -1;
+ return fail;
+}
+
+static int handle_deletes(BDRVVVFATState* s)
+{
+ int i, deferred = 1, deleted = 1;
+
+ /* delete files corresponding to mappings marked as deleted */
+ /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
+ while (deferred && deleted) {
+ deferred = 0;
+ deleted = 0;
+
+ for (i = 1; i < s->mapping.next; i++) {
+ mapping_t* mapping = array_get(&(s->mapping), i);
+ if (mapping->mode & MODE_DELETED) {
+ direntry_t* entry = array_get(&(s->directory),
+ mapping->dir_index);
+
+ if (is_free(entry)) {
+ /* remove file/directory */
+ if (mapping->mode & MODE_DIRECTORY) {
+ int j, next_dir_index = s->directory.next,
+ first_dir_index = mapping->info.dir.first_dir_index;
+
+ if (rmdir(mapping->path) < 0) {
+ if (errno == ENOTEMPTY) {
+ deferred++;
+ continue;
+ } else
+ return -5;
+ }
+
+ for (j = 1; j < s->mapping.next; j++) {
+ mapping_t* m = array_get(&(s->mapping), j);
+ if (m->mode & MODE_DIRECTORY &&
+ m->info.dir.first_dir_index >
+ first_dir_index &&
+ m->info.dir.first_dir_index <
+ next_dir_index)
+ next_dir_index =
+ m->info.dir.first_dir_index;
+ }
+ remove_direntries(s, first_dir_index,
+ next_dir_index - first_dir_index);
+
+ deleted++;
+ }
+ } else {
+ if (unlink(mapping->path))
+ return -4;
+ deleted++;
+ }
+ DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
+ remove_mapping(s, i);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * synchronize mapping with new state:
+ *
+ * - copy FAT (with bdrv_read)
+ * - mark all filenames corresponding to mappings as deleted
+ * - recurse direntries from root (using bs->bdrv_read)
+ * - delete files corresponding to mappings marked as deleted
+ */
+static int do_commit(BDRVVVFATState* s)
+{
+ int ret = 0;
+
+ /* the real meat are the commits. Nothing to do? Move along! */
+ if (s->commits.next == 0)
+ return 0;
+
+ vvfat_close_current_file(s);
+
+ ret = handle_renames_and_mkdirs(s);
+ if (ret) {
+ fprintf(stderr, "Error handling renames (%d)\n", ret);
+ abort();
+ return ret;
+ }
+
+ /* copy FAT (with bdrv_read) */
+ memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
+
+ /* recurse direntries from root (using bs->bdrv_read) */
+ ret = commit_direntries(s, 0, -1);
+ if (ret) {
+ fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
+ abort();
+ return ret;
+ }
+
+ ret = handle_commits(s);
+ if (ret) {
+ fprintf(stderr, "Error handling commits (%d)\n", ret);
+ abort();
+ return ret;
+ }
+
+ ret = handle_deletes(s);
+ if (ret) {
+ fprintf(stderr, "Error deleting\n");
+ abort();
+ return ret;
+ }
+
+ s->qcow->drv->bdrv_make_empty(s->qcow);
+
+ memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
+
+DLOG(checkpoint());
+ return 0;
+}
+
+static int try_commit(BDRVVVFATState* s)
+{
+ vvfat_close_current_file(s);
+DLOG(checkpoint());
+ if(!is_consistent(s))
+ return -1;
+ return do_commit(s);
+}
+
+static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors)
+{
+ BDRVVVFATState *s = bs->opaque;
+ int i, ret;
+
+DLOG(checkpoint());
+
+ /* Check if we're operating in read-only mode */
+ if (s->qcow == NULL) {
+ return -EACCES;
+ }
+
+ vvfat_close_current_file(s);
+
+ /*
+ * Some sanity checks:
+ * - do not allow writing to the boot sector
+ * - do not allow to write non-ASCII filenames
+ */
+
+ if (sector_num < s->first_sectors_number)
+ return -1;
+
+ for (i = sector2cluster(s, sector_num);
+ i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
+ mapping_t* mapping = find_mapping_for_cluster(s, i);
+ if (mapping) {
+ if (mapping->read_only) {
+ fprintf(stderr, "Tried to write to write-protected file %s\n",
+ mapping->path);
+ return -1;
+ }
+
+ if (mapping->mode & MODE_DIRECTORY) {
+ int begin = cluster2sector(s, i);
+ int end = begin + s->sectors_per_cluster, k;
+ int dir_index;
+ const direntry_t* direntries;
+ long_file_name lfn;
+
+ lfn_init(&lfn);
+
+ if (begin < sector_num)
+ begin = sector_num;
+ if (end > sector_num + nb_sectors)
+ end = sector_num + nb_sectors;
+ dir_index = mapping->dir_index +
+ 0x10 * (begin - mapping->begin * s->sectors_per_cluster);
+ direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
+
+ for (k = 0; k < (end - begin) * 0x10; k++) {
+ /* do not allow non-ASCII filenames */
+ if (parse_long_name(&lfn, direntries + k) < 0) {
+ fprintf(stderr, "Warning: non-ASCII filename\n");
+ return -1;
+ }
+ /* no access to the direntry of a read-only file */
+ else if (is_short_name(direntries+k) &&
+ (direntries[k].attributes & 1)) {
+ if (memcmp(direntries + k,
+ array_get(&(s->directory), dir_index + k),
+ sizeof(direntry_t))) {
+ fprintf(stderr, "Warning: tried to write to write-protected file\n");
+ return -1;
+ }
+ }
+ }
+ }
+ i = mapping->end;
+ } else
+ i++;
+ }
+
+ /*
+ * Use qcow backend. Commit later.
+ */
+DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
+ ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
+ if (ret < 0) {
+ fprintf(stderr, "Error writing to qcow backend\n");
+ return ret;
+ }
+
+ for (i = sector2cluster(s, sector_num);
+ i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
+ if (i >= 0)
+ s->used_clusters[i] |= USED_ALLOCATED;
+
+DLOG(checkpoint());
+ /* TODO: add timeout */
+ try_commit(s);
+
+DLOG(checkpoint());
+ return 0;
+}
+
+static int vvfat_is_allocated(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors, int* n)
+{
+ BDRVVVFATState* s = bs->opaque;
+ *n = s->sector_count - sector_num;
+ if (*n > nb_sectors)
+ *n = nb_sectors;
+ else if (*n < 0)
+ return 0;
+ return 1;
+}
+
+static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t* buffer, int nb_sectors) {
+ BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
+ return try_commit(s);
+}
+
+static void write_target_close(BlockDriverState *bs) {
+ BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
+ bdrv_delete(s->qcow);
+ free(s->qcow_filename);
+}
+
+static BlockDriver vvfat_write_target = {
+ .format_name = "vvfat_write_target",
+ .bdrv_write = write_target_commit,
+ .bdrv_close = write_target_close,
+};
+
+static int enable_write_target(BDRVVVFATState *s)
+{
+ BlockDriver *bdrv_qcow;
+ QEMUOptionParameter *options;
+ int ret;
+ int size = sector2cluster(s, s->sector_count);
+ s->used_clusters = calloc(size, 1);
+
+ array_init(&(s->commits), sizeof(commit_t));
+
+ s->qcow_filename = qemu_malloc(1024);
+ get_tmp_filename(s->qcow_filename, 1024);
+
+ bdrv_qcow = bdrv_find_format("qcow");
+ options = parse_option_parameters("", bdrv_qcow->create_options, NULL);
+ set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
+ set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
+
+ if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
+ return -1;
+
+ s->qcow = bdrv_new("");
+ if (s->qcow == NULL) {
+ return -1;
+ }
+
+ ret = bdrv_open(s->qcow, s->qcow_filename,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
+ if (ret < 0) {
+ return ret;
+ }
+
+#ifndef _WIN32
+ unlink(s->qcow_filename);
+#endif
+
+ s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
+ s->bs->backing_hd->drv = &vvfat_write_target;
+ s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*));
+ *(void**)s->bs->backing_hd->opaque = s;
+
+ return 0;
+}
+
+static void vvfat_close(BlockDriverState *bs)
+{
+ BDRVVVFATState *s = bs->opaque;
+
+ vvfat_close_current_file(s);
+ array_free(&(s->fat));
+ array_free(&(s->directory));
+ array_free(&(s->mapping));
+ if(s->cluster_buffer)
+ free(s->cluster_buffer);
+}
+
+static BlockDriver bdrv_vvfat = {
+ .format_name = "vvfat",
+ .instance_size = sizeof(BDRVVVFATState),
+ .bdrv_file_open = vvfat_open,
+ .bdrv_read = vvfat_read,
+ .bdrv_write = vvfat_write,
+ .bdrv_close = vvfat_close,
+ .bdrv_is_allocated = vvfat_is_allocated,
+ .protocol_name = "fat",
+};
+
+static void bdrv_vvfat_init(void)
+{
+ bdrv_register(&bdrv_vvfat);
+}
+
+block_init(bdrv_vvfat_init);
+
+#ifdef DEBUG
+static void checkpoint(void) {
+ assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
+ check1(vvv);
+ check2(vvv);
+ assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
+#if 0
+ if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
+ fprintf(stderr, "Nonono!\n");
+ mapping_t* mapping;
+ direntry_t* direntry;
+ assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
+ assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
+ if (vvv->mapping.next<47)
+ return;
+ assert((mapping = array_get(&(vvv->mapping), 47)));
+ assert(mapping->dir_index < vvv->directory.next);
+ direntry = array_get(&(vvv->directory), mapping->dir_index);
+ assert(!memcmp(direntry->name, "USB H ", 11) || direntry->name[0]==0);
+#endif
+ return;
+ /* avoid compiler warnings: */
+ hexdump(NULL, 100);
+ remove_mapping(vvv, 0);
+ print_mapping(NULL);
+ print_direntry(NULL);
+}
+#endif
diff --git a/block_int.h b/block_int.h
new file mode 100644
index 0000000..545ad11
--- /dev/null
+++ b/block_int.h
@@ -0,0 +1,265 @@
+/*
+ * QEMU System Emulator block driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BLOCK_INT_H
+#define BLOCK_INT_H
+
+#include "block.h"
+#include "qemu-option.h"
+#include "qemu-queue.h"
+
+#define BLOCK_FLAG_ENCRYPT 1
+#define BLOCK_FLAG_COMPAT6 4
+
+#define BLOCK_OPT_SIZE "size"
+#define BLOCK_OPT_ENCRYPT "encryption"
+#define BLOCK_OPT_COMPAT6 "compat6"
+#define BLOCK_OPT_BACKING_FILE "backing_file"
+#define BLOCK_OPT_BACKING_FMT "backing_fmt"
+#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
+#define BLOCK_OPT_TABLE_SIZE "table_size"
+#define BLOCK_OPT_PREALLOC "preallocation"
+
+typedef struct AIOPool {
+ void (*cancel)(BlockDriverAIOCB *acb);
+ int aiocb_size;
+ BlockDriverAIOCB *free_aiocb;
+} AIOPool;
+
+struct BlockDriver {
+ const char *format_name;
+ int instance_size;
+ int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
+ int (*bdrv_probe_device)(const char *filename);
+ int (*bdrv_open)(BlockDriverState *bs, int flags);
+ int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags);
+ int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors);
+ int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+ void (*bdrv_close)(BlockDriverState *bs);
+ int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
+ int (*bdrv_flush)(BlockDriverState *bs);
+ int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum);
+ int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
+ int (*bdrv_make_empty)(BlockDriverState *bs);
+ /* aio */
+ BlockDriverAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+ BlockDriverAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque);
+ BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
+ int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors);
+
+ int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
+ int num_reqs);
+ int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a,
+ BlockRequest *b);
+
+
+ const char *protocol_name;
+ int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
+ int64_t (*bdrv_getlength)(BlockDriverState *bs);
+ int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
+ const uint8_t *buf, int nb_sectors);
+
+ int (*bdrv_snapshot_create)(BlockDriverState *bs,
+ QEMUSnapshotInfo *sn_info);
+ int (*bdrv_snapshot_goto)(BlockDriverState *bs,
+ const char *snapshot_id);
+ int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
+ int (*bdrv_snapshot_list)(BlockDriverState *bs,
+ QEMUSnapshotInfo **psn_info);
+ int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
+ const char *snapshot_name);
+ int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
+
+ int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
+ int64_t pos, int size);
+ int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf,
+ int64_t pos, int size);
+
+ int (*bdrv_change_backing_file)(BlockDriverState *bs,
+ const char *backing_file, const char *backing_fmt);
+
+ /* removable device specific */
+ int (*bdrv_is_inserted)(BlockDriverState *bs);
+ int (*bdrv_media_changed)(BlockDriverState *bs);
+ int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
+ int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
+
+ /* to control generic scsi devices */
+ int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf);
+ BlockDriverAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque);
+
+ /* List of options for creating images, terminated by name == NULL */
+ QEMUOptionParameter *create_options;
+
+
+ /*
+ * Returns 0 for completed check, -errno for internal errors.
+ * The check results are stored in result.
+ */
+ int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result);
+
+ void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
+
+ /*
+ * Returns 1 if newly created images are guaranteed to contain only
+ * zeros, 0 otherwise.
+ */
+ int (*bdrv_has_zero_init)(BlockDriverState *bs);
+
+ QLIST_ENTRY(BlockDriver) list;
+};
+
+struct BlockDriverState {
+ int64_t total_sectors; /* if we are reading a disk image, give its
+ size in sectors */
+ int read_only; /* if true, the media is read only */
+ int keep_read_only; /* if true, the media was requested to stay read only */
+ int open_flags; /* flags used to open the file, re-used for re-open */
+ int removable; /* if true, the media can be removed */
+ int locked; /* if true, the media cannot temporarily be ejected */
+ int tray_open; /* if true, the virtual tray is open */
+ int encrypted; /* if true, the media is encrypted */
+ int valid_key; /* if true, a valid encryption key has been set */
+ int sg; /* if true, the device is a /dev/sg* */
+ /* event callback when inserting/removing */
+ void (*change_cb)(void *opaque, int reason);
+ void *change_opaque;
+
+ BlockDriver *drv; /* NULL means no media */
+ void *opaque;
+
+ DeviceState *peer;
+
+ char filename[1024];
+ char backing_file[1024]; /* if non zero, the image is a diff of
+ this file image */
+ char backing_format[16]; /* if non-zero and backing_file exists */
+ int is_temporary;
+ int media_changed;
+
+ BlockDriverState *backing_hd;
+ BlockDriverState *file;
+
+ /* async read/write emulation */
+
+ void *sync_aiocb;
+
+ /* I/O stats (display with "info blockstats"). */
+ uint64_t rd_bytes;
+ uint64_t wr_bytes;
+ uint64_t rd_ops;
+ uint64_t wr_ops;
+ uint64_t wr_highest_sector;
+
+ /* Whether the disk can expand beyond total_sectors */
+ int growable;
+
+ /* the memory alignment required for the buffers handled by this driver */
+ int buffer_alignment;
+
+ /* do we need to tell the quest if we have a volatile write cache? */
+ int enable_write_cache;
+
+ /* 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;
+ int64_t dirty_count;
+ int in_use; /* users other than guest access, eg. block migration */
+ QTAILQ_ENTRY(BlockDriverState) list;
+ void *private;
+};
+
+#define CHANGE_MEDIA 0x01
+#define CHANGE_SIZE 0x02
+
+struct BlockDriverAIOCB {
+ AIOPool *pool;
+ BlockDriverState *bs;
+ BlockDriverCompletionFunc *cb;
+ void *opaque;
+ BlockDriverAIOCB *next;
+};
+
+void get_tmp_filename(char *filename, int size);
+
+void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque);
+void qemu_aio_release(void *p);
+
+void *qemu_blockalign(BlockDriverState *bs, size_t size);
+
+#ifdef _WIN32
+int is_windows_drive(const char *filename);
+#endif
+
+typedef struct BlockConf {
+ BlockDriverState *bs;
+ uint16_t physical_block_size;
+ uint16_t logical_block_size;
+ uint16_t min_io_size;
+ uint32_t opt_io_size;
+ int32_t bootindex;
+ uint32_t discard_granularity;
+} BlockConf;
+
+static inline unsigned int get_physical_block_exp(BlockConf *conf)
+{
+ unsigned int exp = 0, size;
+
+ for (size = conf->physical_block_size;
+ size > conf->logical_block_size;
+ size >>= 1) {
+ exp++;
+ }
+
+ return exp;
+}
+
+#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \
+ DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \
+ DEFINE_PROP_UINT16("logical_block_size", _state, \
+ _conf.logical_block_size, 512), \
+ DEFINE_PROP_UINT16("physical_block_size", _state, \
+ _conf.physical_block_size, 512), \
+ DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \
+ DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
+ DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \
+ DEFINE_PROP_UINT32("discard_granularity", _state, \
+ _conf.discard_granularity, 0)
+
+#endif /* BLOCK_INT_H */
diff --git a/blockdev.c b/blockdev.c
new file mode 100644
index 0000000..5a97c09
--- /dev/null
+++ b/blockdev.c
@@ -0,0 +1,800 @@
+/*
+ * QEMU host block devices
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+
+#include "block.h"
+#include "blockdev.h"
+#include "monitor.h"
+#include "qerror.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "sysemu.h"
+#include "hw/qdev.h"
+#include "block_int.h"
+
+DriveInfo *extboot_drive = NULL;
+
+static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
+
+static const char *const if_name[IF_COUNT] = {
+ [IF_NONE] = "none",
+ [IF_IDE] = "ide",
+ [IF_SCSI] = "scsi",
+ [IF_FLOPPY] = "floppy",
+ [IF_PFLASH] = "pflash",
+ [IF_MTD] = "mtd",
+ [IF_SD] = "sd",
+ [IF_VIRTIO] = "virtio",
+ [IF_XEN] = "xen",
+};
+
+static const int if_max_devs[IF_COUNT] = {
+ /*
+ * Do not change these numbers! They govern how drive option
+ * index maps to unit and bus. That mapping is ABI.
+ *
+ * All controllers used to imlement if=T drives need to support
+ * if_max_devs[T] units, for any T with if_max_devs[T] != 0.
+ * Otherwise, some index values map to "impossible" bus, unit
+ * values.
+ *
+ * For instance, if you change [IF_SCSI] to 255, -drive
+ * if=scsi,index=12 no longer means bus=1,unit=5, but
+ * bus=0,unit=12. With an lsi53c895a controller (7 units max),
+ * the drive can't be set up. Regression.
+ */
+ [IF_IDE] = 2,
+ [IF_SCSI] = 7,
+};
+
+/*
+ * We automatically delete the drive when a device using it gets
+ * unplugged. Questionable feature, but we can't just drop it.
+ * Device models call blockdev_mark_auto_del() to schedule the
+ * automatic deletion, and generic qdev code calls blockdev_auto_del()
+ * when deletion is actually safe.
+ */
+void blockdev_mark_auto_del(BlockDriverState *bs)
+{
+ DriveInfo *dinfo = drive_get_by_blockdev(bs);
+
+ if (dinfo) {
+ dinfo->auto_del = 1;
+ }
+}
+
+void blockdev_auto_del(BlockDriverState *bs)
+{
+ DriveInfo *dinfo = drive_get_by_blockdev(bs);
+
+ if (dinfo && dinfo->auto_del) {
+ drive_put_ref(dinfo);
+ }
+}
+
+static int drive_index_to_bus_id(BlockInterfaceType type, int index)
+{
+ int max_devs = if_max_devs[type];
+ return max_devs ? index / max_devs : 0;
+}
+
+static int drive_index_to_unit_id(BlockInterfaceType type, int index)
+{
+ int max_devs = if_max_devs[type];
+ return max_devs ? index % max_devs : index;
+}
+
+QemuOpts *drive_def(const char *optstr)
+{
+ return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0);
+}
+
+QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
+ const char *optstr)
+{
+ QemuOpts *opts;
+ char buf[32];
+
+ opts = drive_def(optstr);
+ if (!opts) {
+ return NULL;
+ }
+ if (type != IF_DEFAULT) {
+ qemu_opt_set(opts, "if", if_name[type]);
+ }
+ if (index >= 0) {
+ snprintf(buf, sizeof(buf), "%d", index);
+ qemu_opt_set(opts, "index", buf);
+ }
+ if (file)
+ qemu_opt_set(opts, "file", file);
+ return opts;
+}
+
+DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
+{
+ DriveInfo *dinfo;
+
+ /* seek interface, bus and unit */
+
+ QTAILQ_FOREACH(dinfo, &drives, next) {
+ if (dinfo->type == type &&
+ dinfo->bus == bus &&
+ dinfo->unit == unit)
+ return dinfo;
+ }
+
+ return NULL;
+}
+
+DriveInfo *drive_get_by_index(BlockInterfaceType type, int index)
+{
+ return drive_get(type,
+ drive_index_to_bus_id(type, index),
+ drive_index_to_unit_id(type, index));
+}
+
+int drive_get_max_bus(BlockInterfaceType type)
+{
+ int max_bus;
+ DriveInfo *dinfo;
+
+ max_bus = -1;
+ QTAILQ_FOREACH(dinfo, &drives, next) {
+ if(dinfo->type == type &&
+ dinfo->bus > max_bus)
+ max_bus = dinfo->bus;
+ }
+ return max_bus;
+}
+
+/* Get a block device. This should only be used for single-drive devices
+ (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
+ appropriate bus. */
+DriveInfo *drive_get_next(BlockInterfaceType type)
+{
+ static int next_block_unit[IF_COUNT];
+
+ return drive_get(type, 0, next_block_unit[type]++);
+}
+
+DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
+{
+ DriveInfo *dinfo;
+
+ QTAILQ_FOREACH(dinfo, &drives, next) {
+ if (dinfo->bdrv == bs) {
+ return dinfo;
+ }
+ }
+ return NULL;
+}
+
+static void bdrv_format_print(void *opaque, const char *name)
+{
+ error_printf(" %s", name);
+}
+
+static void drive_uninit(DriveInfo *dinfo)
+{
+ qemu_opts_del(dinfo->opts);
+ bdrv_delete(dinfo->bdrv);
+ qemu_free(dinfo->id);
+ QTAILQ_REMOVE(&drives, dinfo, next);
+ qemu_free(dinfo);
+}
+
+void drive_put_ref(DriveInfo *dinfo)
+{
+ assert(dinfo->refcount);
+ if (--dinfo->refcount == 0) {
+ drive_uninit(dinfo);
+ }
+}
+
+void drive_get_ref(DriveInfo *dinfo)
+{
+ dinfo->refcount++;
+}
+
+static int parse_block_error_action(const char *buf, int is_read)
+{
+ if (!strcmp(buf, "ignore")) {
+ return BLOCK_ERR_IGNORE;
+ } else if (!is_read && !strcmp(buf, "enospc")) {
+ return BLOCK_ERR_STOP_ENOSPC;
+ } else if (!strcmp(buf, "stop")) {
+ return BLOCK_ERR_STOP_ANY;
+ } else if (!strcmp(buf, "report")) {
+ return BLOCK_ERR_REPORT;
+ } else {
+ error_report("'%s' invalid %s error action",
+ buf, is_read ? "read" : "write");
+ return -1;
+ }
+}
+
+DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
+{
+ const char *buf;
+ const char *file = NULL;
+ char devname[128];
+ const char *serial;
+ const char *mediastr = "";
+ BlockInterfaceType type;
+ enum { MEDIA_DISK, MEDIA_CDROM } media;
+ int bus_id, unit_id;
+ int cyls, heads, secs, translation;
+ BlockDriver *drv = NULL;
+ int max_devs;
+ int index;
+ int ro = 0;
+ int bdrv_flags = 0;
+ int on_read_error, on_write_error;
+ const char *devaddr;
+ DriveInfo *dinfo;
+ int is_extboot = 0;
+ int snapshot = 0;
+ int ret;
+
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+
+ if (default_to_scsi) {
+ type = IF_SCSI;
+ pstrcpy(devname, sizeof(devname), "scsi");
+ } else {
+ type = IF_IDE;
+ pstrcpy(devname, sizeof(devname), "ide");
+ }
+ media = MEDIA_DISK;
+
+ /* extract parameters */
+ bus_id = qemu_opt_get_number(opts, "bus", 0);
+ unit_id = qemu_opt_get_number(opts, "unit", -1);
+ index = qemu_opt_get_number(opts, "index", -1);
+
+ cyls = qemu_opt_get_number(opts, "cyls", 0);
+ heads = qemu_opt_get_number(opts, "heads", 0);
+ secs = qemu_opt_get_number(opts, "secs", 0);
+
+ snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
+ ro = qemu_opt_get_bool(opts, "readonly", 0);
+
+ file = qemu_opt_get(opts, "file");
+ serial = qemu_opt_get(opts, "serial");
+
+ if ((buf = qemu_opt_get(opts, "if")) != NULL) {
+ pstrcpy(devname, sizeof(devname), buf);
+ for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
+ ;
+ if (type == IF_COUNT) {
+ error_report("unsupported bus type '%s'", buf);
+ return NULL;
+ }
+ }
+ max_devs = if_max_devs[type];
+
+ if (cyls || heads || secs) {
+ if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
+ error_report("invalid physical cyls number");
+ return NULL;
+ }
+ if (heads < 1 || (type == IF_IDE && heads > 16)) {
+ error_report("invalid physical heads number");
+ return NULL;
+ }
+ if (secs < 1 || (type == IF_IDE && secs > 63)) {
+ error_report("invalid physical secs number");
+ return NULL;
+ }
+ }
+
+ if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
+ if (!cyls) {
+ error_report("'%s' trans must be used with cyls,heads and secs",
+ buf);
+ return NULL;
+ }
+ if (!strcmp(buf, "none"))
+ translation = BIOS_ATA_TRANSLATION_NONE;
+ else if (!strcmp(buf, "lba"))
+ translation = BIOS_ATA_TRANSLATION_LBA;
+ else if (!strcmp(buf, "auto"))
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+ else {
+ error_report("'%s' invalid translation type", buf);
+ return NULL;
+ }
+ }
+
+ if ((buf = qemu_opt_get(opts, "media")) != NULL) {
+ if (!strcmp(buf, "disk")) {
+ media = MEDIA_DISK;
+ } else if (!strcmp(buf, "cdrom")) {
+ if (cyls || secs || heads) {
+ error_report("'%s' invalid physical CHS format", buf);
+ return NULL;
+ }
+ media = MEDIA_CDROM;
+ } else {
+ error_report("'%s' invalid media", buf);
+ return NULL;
+ }
+ }
+
+ if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
+ if (!strcmp(buf, "off") || !strcmp(buf, "none")) {
+ bdrv_flags |= BDRV_O_NOCACHE;
+ } else if (!strcmp(buf, "writeback")) {
+ bdrv_flags |= BDRV_O_CACHE_WB;
+ } else if (!strcmp(buf, "unsafe")) {
+ bdrv_flags |= BDRV_O_CACHE_WB;
+ bdrv_flags |= BDRV_O_NO_FLUSH;
+ } else if (!strcmp(buf, "writethrough")) {
+ /* this is the default */
+ } else {
+ error_report("invalid cache option");
+ return NULL;
+ }
+ }
+
+#ifdef CONFIG_LINUX_AIO
+ if ((buf = qemu_opt_get(opts, "aio")) != NULL) {
+ if (!strcmp(buf, "native")) {
+ bdrv_flags |= BDRV_O_NATIVE_AIO;
+ } else if (!strcmp(buf, "threads")) {
+ /* this is the default */
+ } else {
+ error_report("invalid aio option");
+ return NULL;
+ }
+ }
+#endif
+
+ if ((buf = qemu_opt_get(opts, "format")) != NULL) {
+ if (strcmp(buf, "?") == 0) {
+ error_printf("Supported formats:");
+ bdrv_iterate_format(bdrv_format_print, NULL);
+ error_printf("\n");
+ return NULL;
+ }
+ drv = bdrv_find_whitelisted_format(buf);
+ if (!drv) {
+ error_report("'%s' invalid format", buf);
+ return NULL;
+ }
+ }
+
+ is_extboot = qemu_opt_get_bool(opts, "boot", 0);
+ if (is_extboot && extboot_drive) {
+ fprintf(stderr, "qemu: two bootable drives specified\n");
+ return NULL;
+ }
+
+ on_write_error = BLOCK_ERR_STOP_ENOSPC;
+ if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
+ if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
+ error_report("werror is not supported by this bus type");
+ return NULL;
+ }
+
+ on_write_error = parse_block_error_action(buf, 0);
+ if (on_write_error < 0) {
+ return NULL;
+ }
+ }
+
+ on_read_error = BLOCK_ERR_REPORT;
+ if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
+ if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
+ error_report("rerror is not supported by this bus type");
+ return NULL;
+ }
+
+ on_read_error = parse_block_error_action(buf, 1);
+ if (on_read_error < 0) {
+ return NULL;
+ }
+ }
+
+ if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
+ if (type != IF_VIRTIO) {
+ error_report("addr is not supported by this bus type");
+ return NULL;
+ }
+ }
+
+ /* compute bus and unit according index */
+
+ if (index != -1) {
+ if (bus_id != 0 || unit_id != -1) {
+ error_report("index cannot be used with bus and unit");
+ return NULL;
+ }
+ bus_id = drive_index_to_bus_id(type, index);
+ unit_id = drive_index_to_unit_id(type, index);
+ }
+
+ /* if user doesn't specify a unit_id,
+ * try to find the first free
+ */
+
+ if (unit_id == -1) {
+ unit_id = 0;
+ while (drive_get(type, bus_id, unit_id) != NULL) {
+ unit_id++;
+ if (max_devs && unit_id >= max_devs) {
+ unit_id -= max_devs;
+ bus_id++;
+ }
+ }
+ }
+
+ /* check unit id */
+
+ if (max_devs && unit_id >= max_devs) {
+ error_report("unit %d too big (max is %d)",
+ unit_id, max_devs - 1);
+ return NULL;
+ }
+
+ /*
+ * catch multiple definitions
+ */
+
+ if (drive_get(type, bus_id, unit_id) != NULL) {
+ error_report("drive with bus=%d, unit=%d (index=%d) exists",
+ bus_id, unit_id, index);
+ return NULL;
+ }
+
+ /* init */
+
+ dinfo = qemu_mallocz(sizeof(*dinfo));
+ if ((buf = qemu_opts_id(opts)) != NULL) {
+ dinfo->id = qemu_strdup(buf);
+ } else {
+ /* no id supplied -> create one */
+ dinfo->id = qemu_mallocz(32);
+ if (type == IF_IDE || type == IF_SCSI)
+ mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
+ if (max_devs)
+ snprintf(dinfo->id, 32, "%s%i%s%i",
+ devname, bus_id, mediastr, unit_id);
+ else
+ snprintf(dinfo->id, 32, "%s%s%i",
+ devname, mediastr, unit_id);
+ }
+ dinfo->bdrv = bdrv_new(dinfo->id);
+ dinfo->devaddr = devaddr;
+ dinfo->type = type;
+ dinfo->bus = bus_id;
+ dinfo->unit = unit_id;
+ dinfo->opts = opts;
+ dinfo->refcount = 1;
+ if (serial)
+ strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
+ QTAILQ_INSERT_TAIL(&drives, dinfo, next);
+
+ if (is_extboot) {
+ extboot_drive = dinfo;
+ }
+
+ bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
+
+ switch(type) {
+ case IF_IDE:
+ case IF_SCSI:
+ case IF_XEN:
+ case IF_NONE:
+ switch(media) {
+ case MEDIA_DISK:
+ if (cyls != 0) {
+ bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs);
+ bdrv_set_translation_hint(dinfo->bdrv, translation);
+ }
+ break;
+ case MEDIA_CDROM:
+ bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM);
+ break;
+ }
+ break;
+ case IF_SD:
+ /* 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);
+ break;
+ case IF_PFLASH:
+ case IF_MTD:
+ break;
+ case IF_VIRTIO:
+ /* add virtio block device */
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ qemu_opt_set(opts, "driver", "virtio-blk-pci");
+ qemu_opt_set(opts, "drive", dinfo->id);
+ if (devaddr)
+ qemu_opt_set(opts, "addr", devaddr);
+ break;
+ default:
+ abort();
+ }
+ if (!file || !*file) {
+ return dinfo;
+ }
+ if (snapshot) {
+ /* always use cache=unsafe with snapshot */
+ bdrv_flags &= ~BDRV_O_CACHE_MASK;
+ bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
+ }
+
+ if (media == MEDIA_CDROM) {
+ /* CDROM is fine for any interface, don't check. */
+ ro = 1;
+ } else if (ro == 1) {
+ if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
+ error_report("readonly not supported by this bus type");
+ goto err;
+ }
+ }
+
+ bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
+
+ ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
+ if (ret < 0) {
+ error_report("could not open disk image %s: %s",
+ file, strerror(-ret));
+ goto err;
+ }
+
+ if (bdrv_key_required(dinfo->bdrv))
+ autostart = 0;
+ return dinfo;
+
+err:
+ bdrv_delete(dinfo->bdrv);
+ qemu_free(dinfo->id);
+ QTAILQ_REMOVE(&drives, dinfo, next);
+ qemu_free(dinfo);
+ return NULL;
+}
+
+void do_commit(Monitor *mon, const QDict *qdict)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ BlockDriverState *bs;
+
+ if (!strcmp(device, "all")) {
+ bdrv_commit_all();
+ } else {
+ bs = bdrv_find(device);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, device);
+ return;
+ }
+ bdrv_commit(bs);
+ }
+}
+
+int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ const char *filename = qdict_get_try_str(qdict, "snapshot_file");
+ const char *format = qdict_get_try_str(qdict, "format");
+ BlockDriverState *bs;
+ BlockDriver *drv, *proto_drv;
+ int ret = 0;
+ int flags;
+
+ if (!filename) {
+ qerror_report(QERR_MISSING_PARAMETER, "snapshot_file");
+ ret = -1;
+ goto out;
+ }
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, device);
+ ret = -1;
+ goto out;
+ }
+
+ if (!format) {
+ format = "qcow2";
+ }
+
+ drv = bdrv_find_format(format);
+ if (!drv) {
+ qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
+ ret = -1;
+ goto out;
+ }
+
+ proto_drv = bdrv_find_protocol(filename);
+ if (!proto_drv) {
+ qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
+ ret = -1;
+ goto out;
+ }
+
+ ret = bdrv_img_create(filename, format, bs->filename,
+ bs->drv->format_name, NULL, -1, bs->open_flags);
+ if (ret) {
+ goto out;
+ }
+
+ qemu_aio_flush();
+ bdrv_flush(bs);
+
+ flags = bs->open_flags;
+ bdrv_close(bs);
+ ret = bdrv_open(bs, filename, flags, drv);
+ /*
+ * If reopening the image file we just created fails, we really
+ * are in trouble :(
+ */
+ if (ret != 0) {
+ abort();
+ }
+out:
+ if (ret) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
+{
+ if (!force) {
+ if (!bdrv_is_removable(bs)) {
+ qerror_report(QERR_DEVICE_NOT_REMOVABLE,
+ bdrv_get_device_name(bs));
+ return -1;
+ }
+ if (bdrv_is_locked(bs)) {
+ qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
+ return -1;
+ }
+ }
+ bdrv_close(bs);
+ return 0;
+}
+
+int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ BlockDriverState *bs;
+ int force = qdict_get_try_bool(qdict, "force", 0);
+ const char *filename = qdict_get_str(qdict, "device");
+
+ bs = bdrv_find(filename);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, filename);
+ return -1;
+ }
+ return eject_device(mon, bs, force);
+}
+
+int do_block_set_passwd(Monitor *mon, const QDict *qdict,
+ QObject **ret_data)
+{
+ BlockDriverState *bs;
+ int err;
+
+ bs = bdrv_find(qdict_get_str(qdict, "device"));
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
+ return -1;
+ }
+
+ err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
+ if (err == -EINVAL) {
+ qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
+ return -1;
+ } else if (err < 0) {
+ qerror_report(QERR_INVALID_PASSWORD);
+ return -1;
+ }
+
+ return 0;
+}
+
+int do_change_block(Monitor *mon, const char *device,
+ const char *filename, const char *fmt)
+{
+ BlockDriverState *bs;
+ BlockDriver *drv = NULL;
+ int bdrv_flags;
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, device);
+ return -1;
+ }
+ if (fmt) {
+ drv = bdrv_find_whitelisted_format(fmt);
+ if (!drv) {
+ qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt);
+ return -1;
+ }
+ }
+ if (eject_device(mon, bs, 0) < 0) {
+ return -1;
+ }
+ bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
+ bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
+ if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
+ qerror_report(QERR_OPEN_FILE_FAILED, filename);
+ return -1;
+ }
+ return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
+}
+
+int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ BlockDriverState *bs;
+
+ bs = bdrv_find(id);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, id);
+ return -1;
+ }
+ if (bdrv_in_use(bs)) {
+ qerror_report(QERR_DEVICE_IN_USE, id);
+ return -1;
+ }
+
+ /* quiesce block driver; prevent further io */
+ qemu_aio_flush();
+ bdrv_flush(bs);
+ bdrv_close(bs);
+
+ /* if we have a device associated with this BlockDriverState (bs->peer)
+ * then we need to make the drive anonymous until the device
+ * can be removed. If this is a drive with no device backing
+ * then we can just get rid of the block driver state right here.
+ */
+ if (bs->peer) {
+ bdrv_make_anon(bs);
+ } else {
+ drive_uninit(drive_get_by_blockdev(bs));
+ }
+
+ return 0;
+}
+
+/*
+ * XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the
+ * existing QERR_ macro mess is cleaned up. A good example for better
+ * error reports can be found in the qemu-img resize code.
+ */
+int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ int64_t size = qdict_get_int(qdict, "size");
+ BlockDriverState *bs;
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, device);
+ return -1;
+ }
+
+ if (size < 0) {
+ qerror_report(QERR_UNDEFINED_ERROR);
+ return -1;
+ }
+
+ if (bdrv_truncate(bs, size)) {
+ qerror_report(QERR_UNDEFINED_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/blockdev.h b/blockdev.h
new file mode 100644
index 0000000..8739f46
--- /dev/null
+++ b/blockdev.h
@@ -0,0 +1,70 @@
+/*
+ * QEMU host block devices
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+
+#ifndef BLOCKDEV_H
+#define BLOCKDEV_H
+
+#include "block.h"
+#include "qemu-queue.h"
+
+void blockdev_mark_auto_del(BlockDriverState *bs);
+void blockdev_auto_del(BlockDriverState *bs);
+
+#define BLOCK_SERIAL_STRLEN 20
+
+typedef enum {
+ IF_DEFAULT = -1, /* for use with drive_add() only */
+ IF_NONE,
+ IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
+ IF_COUNT
+} BlockInterfaceType;
+
+struct DriveInfo {
+ BlockDriverState *bdrv;
+ char *id;
+ const char *devaddr;
+ BlockInterfaceType type;
+ int bus;
+ int unit;
+ int auto_del; /* see blockdev_mark_auto_del() */
+ QemuOpts *opts;
+ char serial[BLOCK_SERIAL_STRLEN + 1];
+ QTAILQ_ENTRY(DriveInfo) next;
+ int refcount;
+};
+
+DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
+DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
+int drive_get_max_bus(BlockInterfaceType type);
+DriveInfo *drive_get_next(BlockInterfaceType type);
+void drive_get_ref(DriveInfo *dinfo);
+void drive_put_ref(DriveInfo *dinfo);
+DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
+
+QemuOpts *drive_def(const char *optstr);
+QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
+ const char *optstr);
+DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi);
+
+/* device-hotplug */
+
+DriveInfo *add_init_drive(const char *opts);
+
+void do_commit(Monitor *mon, const QDict *qdict);
+int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_change_block(Monitor *mon, const char *device,
+ const char *filename, const char *fmt);
+int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+extern DriveInfo *extboot_drive;
+
+#endif
diff --git a/bsd-user/bsd-mman.h b/bsd-user/bsd-mman.h
new file mode 100644
index 0000000..910e8c1
--- /dev/null
+++ b/bsd-user/bsd-mman.h
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mman.h 8.2 (Berkeley) 1/9/95
+ * $FreeBSD: src/sys/sys/mman.h,v 1.42 2008/03/28 04:29:27 ps Exp $
+ */
+
+#define TARGET_FREEBSD_MAP_RESERVED0080 0x0080 /* previously misimplemented MAP_INHERIT */
+#define TARGET_FREEBSD_MAP_RESERVED0100 0x0100 /* previously unimplemented MAP_NOEXTEND */
+#define TARGET_FREEBSD_MAP_STACK 0x0400 /* region grows down, like a stack */
+#define TARGET_FREEBSD_MAP_NOSYNC 0x0800 /* page to but do not sync underlying file */
+
+#define TARGET_FREEBSD_MAP_FLAGMASK 0x1ff7
+
+/* $NetBSD: mman.h,v 1.42 2008/11/18 22:13:49 ad Exp $ */
+
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mman.h 8.2 (Berkeley) 1/9/95
+ */
+#define TARGET_NETBSD_MAP_INHERIT 0x0080 /* region is retained after exec */
+#define TARGET_NETBSD_MAP_TRYFIXED 0x0400 /* attempt hint address, even within break */
+#define TARGET_NETBSD_MAP_WIRED 0x0800 /* mlock() mapping when it is established */
+
+#define TARGET_NETBSD_MAP_STACK 0x2000 /* allocated from memory, swap space (stack) */
+
+#define TARGET_NETBSD_MAP_FLAGMASK 0x3ff7
+
+/* $OpenBSD: mman.h,v 1.18 2003/07/21 22:52:19 tedu Exp $ */
+/* $NetBSD: mman.h,v 1.11 1995/03/26 20:24:23 jtc Exp $ */
+
+/*-
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mman.h 8.1 (Berkeley) 6/2/93
+ */
+
+#define TARGET_OPENBSD_MAP_INHERIT 0x0080 /* region is retained after exec */
+#define TARGET_OPENBSD_MAP_NOEXTEND 0x0100 /* for MAP_FILE, don't change file size */
+#define TARGET_OPENBSD_MAP_TRYFIXED 0x0400 /* attempt hint address, even within heap */
+
+#define TARGET_OPENBSD_MAP_FLAGMASK 0x17f7
+
+// XXX
+#define TARGET_BSD_MAP_FLAGMASK 0x3ff7
diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c
new file mode 100644
index 0000000..6d9bb6f
--- /dev/null
+++ b/bsd-user/bsdload.c
@@ -0,0 +1,202 @@
+/* Code for loading BSD executables. Mostly linux kernel code. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "qemu.h"
+
+#define TARGET_NGROUPS 32
+
+/* ??? This should really be somewhere else. */
+abi_long memcpy_to_target(abi_ulong dest, const void *src,
+ unsigned long len)
+{
+ void *host_ptr;
+
+ host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
+ if (!host_ptr)
+ return -TARGET_EFAULT;
+ memcpy(host_ptr, src, len);
+ unlock_user(host_ptr, dest, 1);
+ return 0;
+}
+
+static int in_group_p(gid_t g)
+{
+ /* return TRUE if we're in the specified group, FALSE otherwise */
+ int ngroup;
+ int i;
+ gid_t grouplist[TARGET_NGROUPS];
+
+ ngroup = getgroups(TARGET_NGROUPS, grouplist);
+ for(i = 0; i < ngroup; i++) {
+ if(grouplist[i] == g) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int count(char ** vec)
+{
+ int i;
+
+ for(i = 0; *vec; i++) {
+ vec++;
+ }
+
+ return(i);
+}
+
+static int prepare_binprm(struct linux_binprm *bprm)
+{
+ struct stat st;
+ int mode;
+ int retval, id_change;
+
+ if(fstat(bprm->fd, &st) < 0) {
+ return(-errno);
+ }
+
+ mode = st.st_mode;
+ if(!S_ISREG(mode)) { /* Must be regular file */
+ return(-EACCES);
+ }
+ if(!(mode & 0111)) { /* Must have at least one execute bit set */
+ return(-EACCES);
+ }
+
+ bprm->e_uid = geteuid();
+ bprm->e_gid = getegid();
+ id_change = 0;
+
+ /* Set-uid? */
+ if(mode & S_ISUID) {
+ bprm->e_uid = st.st_uid;
+ if(bprm->e_uid != geteuid()) {
+ id_change = 1;
+ }
+ }
+
+ /* Set-gid? */
+ /*
+ * If setgid is set but no group execute bit then this
+ * is a candidate for mandatory locking, not a setgid
+ * executable.
+ */
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ bprm->e_gid = st.st_gid;
+ if (!in_group_p(bprm->e_gid)) {
+ id_change = 1;
+ }
+ }
+
+ memset(bprm->buf, 0, sizeof(bprm->buf));
+ retval = lseek(bprm->fd, 0L, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(bprm->fd, bprm->buf, 128);
+ }
+ if(retval < 0) {
+ perror("prepare_binprm");
+ exit(-1);
+ /* return(-errno); */
+ }
+ else {
+ return(retval);
+ }
+}
+
+/* Construct the envp and argv tables on the target stack. */
+abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
+ abi_ulong stringp, int push_ptr)
+{
+ int n = sizeof(abi_ulong);
+ abi_ulong envp;
+ abi_ulong argv;
+
+ sp -= (envc + 1) * n;
+ envp = sp;
+ sp -= (argc + 1) * n;
+ argv = sp;
+ if (push_ptr) {
+ /* FIXME - handle put_user() failures */
+ sp -= n;
+ put_user_ual(envp, sp);
+ sp -= n;
+ put_user_ual(argv, sp);
+ }
+ sp -= n;
+ /* FIXME - handle put_user() failures */
+ put_user_ual(argc, sp);
+
+ while (argc-- > 0) {
+ /* FIXME - handle put_user() failures */
+ put_user_ual(stringp, argv);
+ argv += n;
+ stringp += target_strlen(stringp) + 1;
+ }
+ /* FIXME - handle put_user() failures */
+ put_user_ual(0, argv);
+ while (envc-- > 0) {
+ /* FIXME - handle put_user() failures */
+ put_user_ual(stringp, envp);
+ envp += n;
+ stringp += target_strlen(stringp) + 1;
+ }
+ /* FIXME - handle put_user() failures */
+ put_user_ual(0, envp);
+
+ return sp;
+}
+
+int loader_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs, struct image_info *infop)
+{
+ struct linux_binprm bprm;
+ int retval;
+ int i;
+
+ bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
+ bprm.page[i] = NULL;
+ retval = open(filename, O_RDONLY);
+ if (retval < 0)
+ return retval;
+ bprm.fd = retval;
+ bprm.filename = (char *)filename;
+ bprm.argc = count(argv);
+ bprm.argv = argv;
+ bprm.envc = count(envp);
+ bprm.envp = envp;
+
+ retval = prepare_binprm(&bprm);
+
+ if(retval>=0) {
+ if (bprm.buf[0] == 0x7f
+ && bprm.buf[1] == 'E'
+ && bprm.buf[2] == 'L'
+ && bprm.buf[3] == 'F') {
+ retval = load_elf_binary(&bprm,regs,infop);
+ } else {
+ fprintf(stderr, "Unknown binary format\n");
+ return -1;
+ }
+ }
+
+ if(retval>=0) {
+ /* success. Initialize important registers */
+ do_init_thread(regs, infop);
+ return retval;
+ }
+
+ /* Something went wrong, return the inode and free the argument pages*/
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) {
+ free(bprm.page[i]);
+ }
+ return(retval);
+}
diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c
new file mode 100644
index 0000000..1ef1f97
--- /dev/null
+++ b/bsd-user/elfload.c
@@ -0,0 +1,1577 @@
+/* This is the Linux kernel elf-loading code, ported into user space */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu.h"
+#include "disas.h"
+
+#ifdef _ARCH_PPC64
+#undef ARCH_DLINFO
+#undef ELF_PLATFORM
+#undef ELF_HWCAP
+#undef ELF_CLASS
+#undef ELF_DATA
+#undef ELF_ARCH
+#endif
+
+/* from personality.h */
+
+/*
+ * Flags for bug emulation.
+ *
+ * These occupy the top three bytes.
+ */
+enum {
+ ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */
+ FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to descriptors
+ * (signal handling)
+ */
+ MMAP_PAGE_ZERO = 0x0100000,
+ ADDR_COMPAT_LAYOUT = 0x0200000,
+ READ_IMPLIES_EXEC = 0x0400000,
+ ADDR_LIMIT_32BIT = 0x0800000,
+ SHORT_INODE = 0x1000000,
+ WHOLE_SECONDS = 0x2000000,
+ STICKY_TIMEOUTS = 0x4000000,
+ ADDR_LIMIT_3GB = 0x8000000,
+};
+
+/*
+ * Personality types.
+ *
+ * These go in the low byte. Avoid using the top bit, it will
+ * conflict with error returns.
+ */
+enum {
+ PER_LINUX = 0x0000,
+ PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT,
+ PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS,
+ PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+ PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS |
+ WHOLE_SECONDS | SHORT_INODE,
+ PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
+ PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS,
+ PER_BSD = 0x0006,
+ PER_SUNOS = 0x0006 | STICKY_TIMEOUTS,
+ PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_LINUX32 = 0x0008,
+ PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB,
+ PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
+ PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
+ PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
+ PER_RISCOS = 0x000c,
+ PER_SOLARIS = 0x000d | STICKY_TIMEOUTS,
+ PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+ PER_OSF4 = 0x000f, /* OSF/1 v4 */
+ PER_HPUX = 0x0010,
+ PER_MASK = 0x00ff,
+};
+
+/*
+ * Return the base personality without flags.
+ */
+#define personality(pers) (pers & PER_MASK)
+
+/* this flag is uneffective under linux too, should be deleted */
+#ifndef MAP_DENYWRITE
+#define MAP_DENYWRITE 0
+#endif
+
+/* should probably go in elf.h */
+#ifndef ELIBBAD
+#define ELIBBAD 80
+#endif
+
+#ifdef TARGET_I386
+
+#define ELF_PLATFORM get_elf_platform()
+
+static const char *get_elf_platform(void)
+{
+ static char elf_platform[] = "i386";
+ int family = (thread_env->cpuid_version >> 8) & 0xff;
+ if (family > 6)
+ family = 6;
+ if (family >= 3)
+ elf_platform[1] = '0' + family;
+ return elf_platform;
+}
+
+#define ELF_HWCAP get_elf_hwcap()
+
+static uint32_t get_elf_hwcap(void)
+{
+ return thread_env->cpuid_features;
+}
+
+#ifdef TARGET_X86_64
+#define ELF_START_MMAP 0x2aaaaab000ULL
+#define elf_check_arch(x) ( ((x) == ELF_ARCH) )
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_X86_64
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->rax = 0;
+ regs->rsp = infop->start_stack;
+ regs->rip = infop->entry;
+ if (bsd_type == target_freebsd) {
+ regs->rdi = infop->start_stack;
+ }
+}
+
+#else
+
+#define ELF_START_MMAP 0x80000000
+
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) )
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_386
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->esp = infop->start_stack;
+ regs->eip = infop->entry;
+
+ /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program
+ starts %edx contains a pointer to a function which might be
+ registered using `atexit'. This provides a mean for the
+ dynamic linker to call DT_FINI functions for shared libraries
+ that have been loaded before the code runs.
+
+ A value of 0 tells we have no such handler. */
+ regs->edx = 0;
+}
+#endif
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_ARM
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_ARM )
+
+#define ELF_CLASS ELFCLASS32
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+#define ELF_ARCH EM_ARM
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ abi_long stack = infop->start_stack;
+ memset(regs, 0, sizeof(*regs));
+ regs->ARM_cpsr = 0x10;
+ if (infop->entry & 1)
+ regs->ARM_cpsr |= CPSR_T;
+ regs->ARM_pc = infop->entry & 0xfffffffe;
+ regs->ARM_sp = infop->start_stack;
+ /* FIXME - what to for failure of get_user()? */
+ get_user_ual(regs->ARM_r2, stack + 8); /* envp */
+ get_user_ual(regs->ARM_r1, stack + 4); /* envp */
+ /* XXX: it seems that r0 is zeroed after ! */
+ regs->ARM_r0 = 0;
+ /* For uClinux PIC binaries. */
+ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */
+ regs->ARM_r10 = infop->start_data;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+enum
+{
+ ARM_HWCAP_ARM_SWP = 1 << 0,
+ ARM_HWCAP_ARM_HALF = 1 << 1,
+ ARM_HWCAP_ARM_THUMB = 1 << 2,
+ ARM_HWCAP_ARM_26BIT = 1 << 3,
+ ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
+ ARM_HWCAP_ARM_FPA = 1 << 5,
+ ARM_HWCAP_ARM_VFP = 1 << 6,
+ ARM_HWCAP_ARM_EDSP = 1 << 7,
+};
+
+#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \
+ | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \
+ | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP)
+
+#endif
+
+#ifdef TARGET_SPARC
+#ifdef TARGET_SPARC64
+
+#define ELF_START_MMAP 0x80000000
+
+#ifndef TARGET_ABI32
+#define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS )
+#else
+#define elf_check_arch(x) ( (x) == EM_SPARC32PLUS || (x) == EM_SPARC )
+#endif
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_SPARCV9
+
+#define STACK_BIAS 2047
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+#ifndef TARGET_ABI32
+ regs->tstate = 0;
+#endif
+ regs->pc = infop->entry;
+ regs->npc = regs->pc + 4;
+ regs->y = 0;
+#ifdef TARGET_ABI32
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+#else
+ if (personality(infop->personality) == PER_LINUX32)
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+ else {
+ regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS;
+ if (bsd_type == target_freebsd) {
+ regs->u_regs[8] = infop->start_stack;
+ regs->u_regs[11] = infop->start_stack;
+ }
+ }
+#endif
+}
+
+#else
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SPARC )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_SPARC
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->psr = 0;
+ regs->pc = infop->entry;
+ regs->npc = regs->pc + 4;
+ regs->y = 0;
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+}
+
+#endif
+#endif
+
+#ifdef TARGET_PPC
+
+#define ELF_START_MMAP 0x80000000
+
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+
+#define elf_check_arch(x) ( (x) == EM_PPC64 )
+
+#define ELF_CLASS ELFCLASS64
+
+#else
+
+#define elf_check_arch(x) ( (x) == EM_PPC )
+
+#define ELF_CLASS ELFCLASS32
+
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+#define ELF_ARCH EM_PPC
+
+/*
+ * We need to put in some extra aux table entries to tell glibc what
+ * the cache block size is, so it can use the dcbz instruction safely.
+ */
+#define AT_DCACHEBSIZE 19
+#define AT_ICACHEBSIZE 20
+#define AT_UCACHEBSIZE 21
+/* A special ignored type value for PPC, for glibc compatibility. */
+#define AT_IGNOREPPC 22
+/*
+ * The requirements here are:
+ * - keep the final alignment of sp (sp & 0xf)
+ * - make sure the 32-bit value at the first 16 byte aligned position of
+ * AUXV is greater than 16 for glibc compatibility.
+ * AT_IGNOREPPC is used for that.
+ * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC,
+ * even if DLINFO_ARCH_ITEMS goes to zero or is undefined.
+ */
+#define DLINFO_ARCH_ITEMS 5
+#define ARCH_DLINFO \
+do { \
+ NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
+ /* \
+ * Now handle glibc compatibility. \
+ */ \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ } while (0)
+
+static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
+{
+ abi_ulong pos = infop->start_stack;
+ abi_ulong tmp;
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ abi_ulong entry, toc;
+#endif
+
+ _regs->gpr[1] = infop->start_stack;
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ entry = ldq_raw(infop->entry) + infop->load_addr;
+ toc = ldq_raw(infop->entry + 8) + infop->load_addr;
+ _regs->gpr[2] = toc;
+ infop->entry = entry;
+#endif
+ _regs->nip = infop->entry;
+ /* Note that isn't exactly what regular kernel does
+ * but this is what the ABI wants and is needed to allow
+ * execution of PPC BSD programs.
+ */
+ /* FIXME - what to for failure of get_user()? */
+ get_user_ual(_regs->gpr[3], pos);
+ pos += sizeof(abi_ulong);
+ _regs->gpr[4] = pos;
+ for (tmp = 1; tmp != 0; pos += sizeof(abi_ulong))
+ tmp = ldl(pos);
+ _regs->gpr[5] = pos;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_MIPS
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_MIPS )
+
+#ifdef TARGET_MIPS64
+#define ELF_CLASS ELFCLASS64
+#else
+#define ELF_CLASS ELFCLASS32
+#endif
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+#define ELF_ARCH EM_MIPS
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->cp0_status = 2 << CP0St_KSU;
+ regs->cp0_epc = infop->entry;
+ regs->regs[29] = infop->start_stack;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif /* TARGET_MIPS */
+
+#ifdef TARGET_SH4
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SH )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_SH
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ /* Check other registers XXXXX */
+ regs->pc = infop->entry;
+ regs->regs[15] = infop->start_stack;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_CRIS
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_CRIS )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_CRIS
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->erp = infop->entry;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 8192
+
+#endif
+
+#ifdef TARGET_M68K
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_68K )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_68K
+
+/* ??? Does this need to do anything?
+#define ELF_PLAT_INIT(_r) */
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->usp = infop->start_stack;
+ regs->sr = 0;
+ regs->pc = infop->entry;
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 8192
+
+#endif
+
+#ifdef TARGET_ALPHA
+
+#define ELF_START_MMAP (0x30000000000ULL)
+
+#define elf_check_arch(x) ( (x) == ELF_ARCH )
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_DATA ELFDATA2MSB
+#define ELF_ARCH EM_ALPHA
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->pc = infop->entry;
+ regs->ps = 8;
+ regs->usp = infop->start_stack;
+ regs->unique = infop->start_data; /* ? */
+ printf("Set unique value to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n",
+ regs->unique, infop->start_data);
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 8192
+
+#endif /* TARGET_ALPHA */
+
+#ifndef ELF_PLATFORM
+#define ELF_PLATFORM (NULL)
+#endif
+
+#ifndef ELF_HWCAP
+#define ELF_HWCAP 0
+#endif
+
+#ifdef TARGET_ABI32
+#undef ELF_CLASS
+#define ELF_CLASS ELFCLASS32
+#undef bswaptls
+#define bswaptls(ptr) bswap32s(ptr)
+#endif
+
+#include "elf.h"
+
+struct exec
+{
+ unsigned int a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned int a_text; /* length of text, in bytes */
+ unsigned int a_data; /* length of data, in bytes */
+ unsigned int a_bss; /* length of uninitialized data area, in bytes */
+ unsigned int a_syms; /* length of symbol table data in file, in bytes */
+ unsigned int a_entry; /* start address */
+ unsigned int a_trsize; /* length of relocation info for text, in bytes */
+ unsigned int a_drsize; /* length of relocation info for data, in bytes */
+};
+
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+
+/* max code+data+bss space allocated to elf interpreter */
+#define INTERP_MAP_SIZE (32 * 1024 * 1024)
+
+/* max code+data+bss+brk space allocated to ET_DYN executables */
+#define ET_DYN_MAP_SIZE (128 * 1024 * 1024)
+
+/* Necessary parameters */
+#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
+#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
+#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
+
+#define INTERPRETER_NONE 0
+#define INTERPRETER_AOUT 1
+#define INTERPRETER_ELF 2
+
+#define DLINFO_ITEMS 12
+
+static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
+{
+ memcpy(to, from, n);
+}
+
+static int load_aout_interp(void * exptr, int interp_fd);
+
+#ifdef BSWAP_NEEDED
+static void bswap_ehdr(struct elfhdr *ehdr)
+{
+ bswap16s(&ehdr->e_type); /* Object file type */
+ bswap16s(&ehdr->e_machine); /* Architecture */
+ bswap32s(&ehdr->e_version); /* Object file version */
+ bswaptls(&ehdr->e_entry); /* Entry point virtual address */
+ bswaptls(&ehdr->e_phoff); /* Program header table file offset */
+ bswaptls(&ehdr->e_shoff); /* Section header table file offset */
+ bswap32s(&ehdr->e_flags); /* Processor-specific flags */
+ bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
+ bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
+ bswap16s(&ehdr->e_phnum); /* Program header table entry count */
+ bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
+ bswap16s(&ehdr->e_shnum); /* Section header table entry count */
+ bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
+}
+
+static void bswap_phdr(struct elf_phdr *phdr)
+{
+ bswap32s(&phdr->p_type); /* Segment type */
+ bswaptls(&phdr->p_offset); /* Segment file offset */
+ bswaptls(&phdr->p_vaddr); /* Segment virtual address */
+ bswaptls(&phdr->p_paddr); /* Segment physical address */
+ bswaptls(&phdr->p_filesz); /* Segment size in file */
+ bswaptls(&phdr->p_memsz); /* Segment size in memory */
+ bswap32s(&phdr->p_flags); /* Segment flags */
+ bswaptls(&phdr->p_align); /* Segment alignment */
+}
+
+static void bswap_shdr(struct elf_shdr *shdr)
+{
+ bswap32s(&shdr->sh_name);
+ bswap32s(&shdr->sh_type);
+ bswaptls(&shdr->sh_flags);
+ bswaptls(&shdr->sh_addr);
+ bswaptls(&shdr->sh_offset);
+ bswaptls(&shdr->sh_size);
+ bswap32s(&shdr->sh_link);
+ bswap32s(&shdr->sh_info);
+ bswaptls(&shdr->sh_addralign);
+ bswaptls(&shdr->sh_entsize);
+}
+
+static void bswap_sym(struct elf_sym *sym)
+{
+ bswap32s(&sym->st_name);
+ bswaptls(&sym->st_value);
+ bswaptls(&sym->st_size);
+ bswap16s(&sym->st_shndx);
+}
+#endif
+
+/*
+ * 'copy_elf_strings()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ *
+ */
+static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
+ abi_ulong p)
+{
+ char *tmp, *tmp1, *pag = NULL;
+ int len, offset = 0;
+
+ if (!p) {
+ return 0; /* bullet-proofing */
+ }
+ while (argc-- > 0) {
+ tmp = argv[argc];
+ if (!tmp) {
+ fprintf(stderr, "VFS: argc is wrong");
+ exit(-1);
+ }
+ tmp1 = tmp;
+ while (*tmp++);
+ len = tmp - tmp1;
+ if (p < len) { /* this shouldn't happen - 128kB */
+ return 0;
+ }
+ while (len) {
+ --p; --tmp; --len;
+ if (--offset < 0) {
+ offset = p % TARGET_PAGE_SIZE;
+ pag = (char *)page[p/TARGET_PAGE_SIZE];
+ if (!pag) {
+ pag = (char *)malloc(TARGET_PAGE_SIZE);
+ memset(pag, 0, TARGET_PAGE_SIZE);
+ page[p/TARGET_PAGE_SIZE] = pag;
+ if (!pag)
+ return 0;
+ }
+ }
+ if (len == 0 || offset == 0) {
+ *(pag + offset) = *tmp;
+ }
+ else {
+ int bytes_to_copy = (len > offset) ? offset : len;
+ tmp -= bytes_to_copy;
+ p -= bytes_to_copy;
+ offset -= bytes_to_copy;
+ len -= bytes_to_copy;
+ memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
+ }
+ }
+ }
+ return p;
+}
+
+static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
+ struct image_info *info)
+{
+ abi_ulong stack_base, size, error;
+ int i;
+
+ /* Create enough stack to hold everything. If we don't use
+ * it for args, we'll use it for something else...
+ */
+ size = x86_stack_size;
+ if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE)
+ size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ error = target_mmap(0,
+ size + qemu_host_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ if (error == -1) {
+ perror("stk mmap");
+ exit(-1);
+ }
+ /* we reserve one extra page at the top of the stack as guard */
+ target_mprotect(error + size, qemu_host_page_size, PROT_NONE);
+
+ stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ p += stack_base;
+
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ if (bprm->page[i]) {
+ info->rss++;
+ /* FIXME - check return value of memcpy_to_target() for failure */
+ memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
+ free(bprm->page[i]);
+ }
+ stack_base += TARGET_PAGE_SIZE;
+ }
+ return p;
+}
+
+static void set_brk(abi_ulong start, abi_ulong end)
+{
+ /* page-align the start and end addresses... */
+ start = HOST_PAGE_ALIGN(start);
+ end = HOST_PAGE_ALIGN(end);
+ if (end <= start)
+ return;
+ if(target_mmap(start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == -1) {
+ perror("cannot mmap brk");
+ exit(-1);
+ }
+}
+
+
+/* We need to explicitly zero any fractional pages after the data
+ section (i.e. bss). This would contain the junk from the file that
+ should not be in memory. */
+static void padzero(abi_ulong elf_bss, abi_ulong last_bss)
+{
+ abi_ulong nbyte;
+
+ if (elf_bss >= last_bss)
+ return;
+
+ /* XXX: this is really a hack : if the real host page size is
+ smaller than the target page size, some pages after the end
+ of the file may not be mapped. A better fix would be to
+ patch target_mmap(), but it is more complicated as the file
+ size must be known */
+ if (qemu_real_host_page_size < qemu_host_page_size) {
+ abi_ulong end_addr, end_addr1;
+ end_addr1 = (elf_bss + qemu_real_host_page_size - 1) &
+ ~(qemu_real_host_page_size - 1);
+ end_addr = HOST_PAGE_ALIGN(elf_bss);
+ if (end_addr1 < end_addr) {
+ mmap((void *)g2h(end_addr1), end_addr - end_addr1,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
+ }
+ }
+
+ nbyte = elf_bss & (qemu_host_page_size-1);
+ if (nbyte) {
+ nbyte = qemu_host_page_size - nbyte;
+ do {
+ /* FIXME - what to do if put_user() fails? */
+ put_user_u8(0, elf_bss);
+ elf_bss++;
+ } while (--nbyte);
+ }
+}
+
+
+static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
+ struct elfhdr * exec,
+ abi_ulong load_addr,
+ abi_ulong load_bias,
+ abi_ulong interp_load_addr, int ibcs,
+ struct image_info *info)
+{
+ abi_ulong sp;
+ int size;
+ abi_ulong u_platform;
+ const char *k_platform;
+ const int n = sizeof(elf_addr_t);
+
+ sp = p;
+ u_platform = 0;
+ k_platform = ELF_PLATFORM;
+ if (k_platform) {
+ size_t len = strlen(k_platform) + 1;
+ sp -= (len + n - 1) & ~(n - 1);
+ u_platform = sp;
+ /* FIXME - check return value of memcpy_to_target() for failure */
+ memcpy_to_target(sp, k_platform, len);
+ }
+ /*
+ * Force 16 byte _final_ alignment here for generality.
+ */
+ sp = sp &~ (abi_ulong)15;
+ size = (DLINFO_ITEMS + 1) * 2;
+ if (k_platform)
+ size += 2;
+#ifdef DLINFO_ARCH_ITEMS
+ size += DLINFO_ARCH_ITEMS * 2;
+#endif
+ size += envc + argc + 2;
+ size += (!ibcs ? 3 : 1); /* argc itself */
+ size *= n;
+ if (size & 15)
+ sp -= 16 - (size & 15);
+
+ /* This is correct because Linux defines
+ * elf_addr_t as Elf32_Off / Elf64_Off
+ */
+#define NEW_AUX_ENT(id, val) do { \
+ sp -= n; put_user_ual(val, sp); \
+ sp -= n; put_user_ual(id, sp); \
+ } while(0)
+
+ NEW_AUX_ENT (AT_NULL, 0);
+
+ /* There must be exactly DLINFO_ITEMS entries here. */
+ NEW_AUX_ENT(AT_PHDR, (abi_ulong)(load_addr + exec->e_phoff));
+ NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
+ NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
+ NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
+ NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_load_addr));
+ NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
+ NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry);
+ NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
+ NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
+ NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
+ NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
+ NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
+ NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
+ if (k_platform)
+ NEW_AUX_ENT(AT_PLATFORM, u_platform);
+#ifdef ARCH_DLINFO
+ /*
+ * ARCH_DLINFO must come last so platform specific code can enforce
+ * special alignment requirements on the AUXV if necessary (eg. PPC).
+ */
+ ARCH_DLINFO;
+#endif
+#undef NEW_AUX_ENT
+
+ sp = loader_build_argptr(envc, argc, sp, p, !ibcs);
+ return sp;
+}
+
+
+static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
+ int interpreter_fd,
+ abi_ulong *interp_load_addr)
+{
+ struct elf_phdr *elf_phdata = NULL;
+ struct elf_phdr *eppnt;
+ abi_ulong load_addr = 0;
+ int load_addr_set = 0;
+ int retval;
+ abi_ulong last_bss, elf_bss;
+ abi_ulong error;
+ int i;
+
+ elf_bss = 0;
+ last_bss = 0;
+ error = 0;
+
+#ifdef BSWAP_NEEDED
+ bswap_ehdr(interp_elf_ex);
+#endif
+ /* First of all, some simple consistency checks */
+ if ((interp_elf_ex->e_type != ET_EXEC &&
+ interp_elf_ex->e_type != ET_DYN) ||
+ !elf_check_arch(interp_elf_ex->e_machine)) {
+ return ~((abi_ulong)0UL);
+ }
+
+
+ /* Now read in all of the header information */
+
+ if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE)
+ return ~(abi_ulong)0UL;
+
+ elf_phdata = (struct elf_phdr *)
+ malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+
+ if (!elf_phdata)
+ return ~((abi_ulong)0UL);
+
+ /*
+ * If the size of this structure has changed, then punt, since
+ * we will be doing the wrong thing.
+ */
+ if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) {
+ free(elf_phdata);
+ return ~((abi_ulong)0UL);
+ }
+
+ retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(interpreter_fd,
+ (char *) elf_phdata,
+ sizeof(struct elf_phdr) * interp_elf_ex->e_phnum);
+ }
+ if (retval < 0) {
+ perror("load_elf_interp");
+ exit(-1);
+ free (elf_phdata);
+ return retval;
+ }
+#ifdef BSWAP_NEEDED
+ eppnt = elf_phdata;
+ for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
+ bswap_phdr(eppnt);
+ }
+#endif
+
+ if (interp_elf_ex->e_type == ET_DYN) {
+ /* in order to avoid hardcoding the interpreter load
+ address in qemu, we allocate a big enough memory zone */
+ error = target_mmap(0, INTERP_MAP_SIZE,
+ PROT_NONE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ if (error == -1) {
+ perror("mmap");
+ exit(-1);
+ }
+ load_addr = error;
+ load_addr_set = 1;
+ }
+
+ eppnt = elf_phdata;
+ for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++)
+ if (eppnt->p_type == PT_LOAD) {
+ int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
+ int elf_prot = 0;
+ abi_ulong vaddr = 0;
+ abi_ulong k;
+
+ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
+ if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) {
+ elf_type |= MAP_FIXED;
+ vaddr = eppnt->p_vaddr;
+ }
+ error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr),
+ eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr),
+ elf_prot,
+ elf_type,
+ interpreter_fd,
+ eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr));
+
+ if (error == -1) {
+ /* Real error */
+ close(interpreter_fd);
+ free(elf_phdata);
+ return ~((abi_ulong)0UL);
+ }
+
+ if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
+ load_addr = error;
+ load_addr_set = 1;
+ }
+
+ /*
+ * Find the end of the file mapping for this phdr, and keep
+ * track of the largest address we see for this.
+ */
+ k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
+ if (k > elf_bss) elf_bss = k;
+
+ /*
+ * Do the same thing for the memory mapping - between
+ * elf_bss and last_bss is the bss section.
+ */
+ k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
+ if (k > last_bss) last_bss = k;
+ }
+
+ /* Now use mmap to map the library into memory. */
+
+ close(interpreter_fd);
+
+ /*
+ * Now fill out the bss section. First pad the last page up
+ * to the page boundary, and then perform a mmap to make sure
+ * that there are zeromapped pages up to and including the last
+ * bss page.
+ */
+ padzero(elf_bss, last_bss);
+ elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */
+
+ /* Map the last of the bss segment */
+ if (last_bss > elf_bss) {
+ target_mmap(elf_bss, last_bss-elf_bss,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
+ }
+ free(elf_phdata);
+
+ *interp_load_addr = load_addr;
+ return ((abi_ulong) interp_elf_ex->e_entry) + load_addr;
+}
+
+static int symfind(const void *s0, const void *s1)
+{
+ struct elf_sym *key = (struct elf_sym *)s0;
+ struct elf_sym *sym = (struct elf_sym *)s1;
+ int result = 0;
+ if (key->st_value < sym->st_value) {
+ result = -1;
+ } else if (key->st_value > sym->st_value + sym->st_size) {
+ result = 1;
+ }
+ return result;
+}
+
+static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr)
+{
+#if ELF_CLASS == ELFCLASS32
+ struct elf_sym *syms = s->disas_symtab.elf32;
+#else
+ struct elf_sym *syms = s->disas_symtab.elf64;
+#endif
+
+ // binary search
+ struct elf_sym key;
+ struct elf_sym *sym;
+
+ key.st_value = orig_addr;
+
+ sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind);
+ if (sym != NULL) {
+ return s->disas_strtab + sym->st_name;
+ }
+
+ return "";
+}
+
+/* FIXME: This should use elf_ops.h */
+static int symcmp(const void *s0, const void *s1)
+{
+ struct elf_sym *sym0 = (struct elf_sym *)s0;
+ struct elf_sym *sym1 = (struct elf_sym *)s1;
+ return (sym0->st_value < sym1->st_value)
+ ? -1
+ : ((sym0->st_value > sym1->st_value) ? 1 : 0);
+}
+
+/* Best attempt to load symbols from this ELF object. */
+static void load_symbols(struct elfhdr *hdr, int fd)
+{
+ unsigned int i, nsyms;
+ struct elf_shdr sechdr, symtab, strtab;
+ char *strings;
+ struct syminfo *s;
+ struct elf_sym *syms, *new_syms;
+
+ lseek(fd, hdr->e_shoff, SEEK_SET);
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr))
+ return;
+#ifdef BSWAP_NEEDED
+ bswap_shdr(&sechdr);
+#endif
+ if (sechdr.sh_type == SHT_SYMTAB) {
+ symtab = sechdr;
+ lseek(fd, hdr->e_shoff
+ + sizeof(sechdr) * sechdr.sh_link, SEEK_SET);
+ if (read(fd, &strtab, sizeof(strtab))
+ != sizeof(strtab))
+ return;
+#ifdef BSWAP_NEEDED
+ bswap_shdr(&strtab);
+#endif
+ goto found;
+ }
+ }
+ return; /* Shouldn't happen... */
+
+ found:
+ /* Now know where the strtab and symtab are. Snarf them. */
+ s = malloc(sizeof(*s));
+ syms = malloc(symtab.sh_size);
+ if (!syms) {
+ free(s);
+ return;
+ }
+ s->disas_strtab = strings = malloc(strtab.sh_size);
+ if (!s->disas_strtab) {
+ free(s);
+ free(syms);
+ return;
+ }
+
+ lseek(fd, symtab.sh_offset, SEEK_SET);
+ if (read(fd, syms, symtab.sh_size) != symtab.sh_size) {
+ free(s);
+ free(syms);
+ free(strings);
+ return;
+ }
+
+ nsyms = symtab.sh_size / sizeof(struct elf_sym);
+
+ i = 0;
+ while (i < nsyms) {
+#ifdef BSWAP_NEEDED
+ bswap_sym(syms + i);
+#endif
+ // Throw away entries which we do not need.
+ if (syms[i].st_shndx == SHN_UNDEF ||
+ syms[i].st_shndx >= SHN_LORESERVE ||
+ ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
+ nsyms--;
+ if (i < nsyms) {
+ syms[i] = syms[nsyms];
+ }
+ continue;
+ }
+#if defined(TARGET_ARM) || defined (TARGET_MIPS)
+ /* The bottom address bit marks a Thumb or MIPS16 symbol. */
+ syms[i].st_value &= ~(target_ulong)1;
+#endif
+ i++;
+ }
+
+ /* Attempt to free the storage associated with the local symbols
+ that we threw away. Whether or not this has any effect on the
+ memory allocation depends on the malloc implementation and how
+ many symbols we managed to discard. */
+ new_syms = realloc(syms, nsyms * sizeof(*syms));
+ if (new_syms == NULL) {
+ free(s);
+ free(syms);
+ free(strings);
+ return;
+ }
+ syms = new_syms;
+
+ qsort(syms, nsyms, sizeof(*syms), symcmp);
+
+ lseek(fd, strtab.sh_offset, SEEK_SET);
+ if (read(fd, strings, strtab.sh_size) != strtab.sh_size) {
+ free(s);
+ free(syms);
+ free(strings);
+ return;
+ }
+ s->disas_num_syms = nsyms;
+#if ELF_CLASS == ELFCLASS32
+ s->disas_symtab.elf32 = syms;
+ s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx;
+#else
+ s->disas_symtab.elf64 = syms;
+ s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx;
+#endif
+ s->next = syminfos;
+ syminfos = s;
+}
+
+int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info)
+{
+ struct elfhdr elf_ex;
+ struct elfhdr interp_elf_ex;
+ struct exec interp_ex;
+ int interpreter_fd = -1; /* avoid warning */
+ abi_ulong load_addr, load_bias;
+ int load_addr_set = 0;
+ unsigned int interpreter_type = INTERPRETER_NONE;
+ unsigned char ibcs2_interpreter;
+ int i;
+ abi_ulong mapped_addr;
+ struct elf_phdr * elf_ppnt;
+ struct elf_phdr *elf_phdata;
+ abi_ulong elf_bss, k, elf_brk;
+ int retval;
+ char * elf_interpreter;
+ abi_ulong elf_entry, interp_load_addr = 0;
+ int status;
+ abi_ulong start_code, end_code, start_data, end_data;
+ abi_ulong reloc_func_desc = 0;
+ abi_ulong elf_stack;
+ char passed_fileno[6];
+
+ ibcs2_interpreter = 0;
+ status = 0;
+ load_addr = 0;
+ load_bias = 0;
+ elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */
+#ifdef BSWAP_NEEDED
+ bswap_ehdr(&elf_ex);
+#endif
+
+ /* First of all, some simple consistency checks */
+ if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
+ (! elf_check_arch(elf_ex.e_machine))) {
+ return -ENOEXEC;
+ }
+
+ bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
+ bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
+ bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
+ if (!bprm->p) {
+ retval = -E2BIG;
+ }
+
+ /* Now read in all of the header information */
+ elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum);
+ if (elf_phdata == NULL) {
+ return -ENOMEM;
+ }
+
+ retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET);
+ if(retval > 0) {
+ retval = read(bprm->fd, (char *) elf_phdata,
+ elf_ex.e_phentsize * elf_ex.e_phnum);
+ }
+
+ if (retval < 0) {
+ perror("load_elf_binary");
+ exit(-1);
+ free (elf_phdata);
+ return -errno;
+ }
+
+#ifdef BSWAP_NEEDED
+ elf_ppnt = elf_phdata;
+ for (i=0; i<elf_ex.e_phnum; i++, elf_ppnt++) {
+ bswap_phdr(elf_ppnt);
+ }
+#endif
+ elf_ppnt = elf_phdata;
+
+ elf_bss = 0;
+ elf_brk = 0;
+
+
+ elf_stack = ~((abi_ulong)0UL);
+ elf_interpreter = NULL;
+ start_code = ~((abi_ulong)0UL);
+ end_code = 0;
+ start_data = 0;
+ end_data = 0;
+ interp_ex.a_info = 0;
+
+ for(i=0;i < elf_ex.e_phnum; i++) {
+ if (elf_ppnt->p_type == PT_INTERP) {
+ if ( elf_interpreter != NULL )
+ {
+ free (elf_phdata);
+ free(elf_interpreter);
+ close(bprm->fd);
+ return -EINVAL;
+ }
+
+ /* This is the program interpreter used for
+ * shared libraries - for now assume that this
+ * is an a.out format binary
+ */
+
+ elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);
+
+ if (elf_interpreter == NULL) {
+ free (elf_phdata);
+ close(bprm->fd);
+ return -ENOMEM;
+ }
+
+ retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz);
+ }
+ if(retval < 0) {
+ perror("load_elf_binary2");
+ exit(-1);
+ }
+
+ /* If the program interpreter is one of these two,
+ then assume an iBCS2 image. Otherwise assume
+ a native linux image. */
+
+ /* JRP - Need to add X86 lib dir stuff here... */
+
+ if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
+ strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) {
+ ibcs2_interpreter = 1;
+ }
+
+#if 0
+ printf("Using ELF interpreter %s\n", path(elf_interpreter));
+#endif
+ if (retval >= 0) {
+ retval = open(path(elf_interpreter), O_RDONLY);
+ if(retval >= 0) {
+ interpreter_fd = retval;
+ }
+ else {
+ perror(elf_interpreter);
+ exit(-1);
+ /* retval = -errno; */
+ }
+ }
+
+ if (retval >= 0) {
+ retval = lseek(interpreter_fd, 0, SEEK_SET);
+ if(retval >= 0) {
+ retval = read(interpreter_fd,bprm->buf,128);
+ }
+ }
+ if (retval >= 0) {
+ interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */
+ interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */
+ }
+ if (retval < 0) {
+ perror("load_elf_binary3");
+ exit(-1);
+ free (elf_phdata);
+ free(elf_interpreter);
+ close(bprm->fd);
+ return retval;
+ }
+ }
+ elf_ppnt++;
+ }
+
+ /* Some simple consistency checks for the interpreter */
+ if (elf_interpreter){
+ interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
+
+ /* Now figure out which format our binary is */
+ if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) &&
+ (N_MAGIC(interp_ex) != QMAGIC)) {
+ interpreter_type = INTERPRETER_ELF;
+ }
+
+ if (interp_elf_ex.e_ident[0] != 0x7f ||
+ strncmp((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) {
+ interpreter_type &= ~INTERPRETER_ELF;
+ }
+
+ if (!interpreter_type) {
+ free(elf_interpreter);
+ free(elf_phdata);
+ close(bprm->fd);
+ return -ELIBBAD;
+ }
+ }
+
+ /* OK, we are done with that, now set up the arg stuff,
+ and then start this sucker up */
+
+ {
+ char * passed_p;
+
+ if (interpreter_type == INTERPRETER_AOUT) {
+ snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd);
+ passed_p = passed_fileno;
+
+ if (elf_interpreter) {
+ bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p);
+ bprm->argc++;
+ }
+ }
+ if (!bprm->p) {
+ if (elf_interpreter) {
+ free(elf_interpreter);
+ }
+ free (elf_phdata);
+ close(bprm->fd);
+ return -E2BIG;
+ }
+ }
+
+ /* OK, This is the point of no return */
+ info->end_data = 0;
+ info->end_code = 0;
+ info->start_mmap = (abi_ulong)ELF_START_MMAP;
+ info->mmap = 0;
+ elf_entry = (abi_ulong) elf_ex.e_entry;
+
+#if defined(CONFIG_USE_GUEST_BASE)
+ /*
+ * In case where user has not explicitly set the guest_base, we
+ * probe here that should we set it automatically.
+ */
+ if (!have_guest_base) {
+ /*
+ * Go through ELF program header table and find out whether
+ * any of the segments drop below our current mmap_min_addr and
+ * in that case set guest_base to corresponding address.
+ */
+ for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum;
+ i++, elf_ppnt++) {
+ if (elf_ppnt->p_type != PT_LOAD)
+ continue;
+ if (HOST_PAGE_ALIGN(elf_ppnt->p_vaddr) < mmap_min_addr) {
+ guest_base = HOST_PAGE_ALIGN(mmap_min_addr);
+ break;
+ }
+ }
+ }
+#endif /* CONFIG_USE_GUEST_BASE */
+
+ /* Do this so that we can load the interpreter, if need be. We will
+ change some of these later */
+ info->rss = 0;
+ bprm->p = setup_arg_pages(bprm->p, bprm, info);
+ info->start_stack = bprm->p;
+
+ /* Now we do a little grungy work by mmaping the ELF image into
+ * the correct location in memory. At this point, we assume that
+ * the image should be loaded at fixed address, not at a variable
+ * address.
+ */
+
+ for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
+ int elf_prot = 0;
+ int elf_flags = 0;
+ abi_ulong error;
+
+ if (elf_ppnt->p_type != PT_LOAD)
+ continue;
+
+ if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
+ if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ elf_flags = MAP_PRIVATE | MAP_DENYWRITE;
+ if (elf_ex.e_type == ET_EXEC || load_addr_set) {
+ elf_flags |= MAP_FIXED;
+ } else if (elf_ex.e_type == ET_DYN) {
+ /* Try and get dynamic programs out of the way of the default mmap
+ base, as well as whatever program they might try to exec. This
+ is because the brk will follow the loader, and is not movable. */
+ /* NOTE: for qemu, we do a big mmap to get enough space
+ without hardcoding any address */
+ error = target_mmap(0, ET_DYN_MAP_SIZE,
+ PROT_NONE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ if (error == -1) {
+ perror("mmap");
+ exit(-1);
+ }
+ load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);
+ }
+
+ error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),
+ (elf_ppnt->p_filesz +
+ TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),
+ elf_prot,
+ (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),
+ bprm->fd,
+ (elf_ppnt->p_offset -
+ TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));
+ if (error == -1) {
+ perror("mmap");
+ exit(-1);
+ }
+
+#ifdef LOW_ELF_STACK
+ if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)
+ elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr);
+#endif
+
+ if (!load_addr_set) {
+ load_addr_set = 1;
+ load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;
+ if (elf_ex.e_type == ET_DYN) {
+ load_bias += error -
+ TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);
+ load_addr += load_bias;
+ reloc_func_desc = load_bias;
+ }
+ }
+ k = elf_ppnt->p_vaddr;
+ if (k < start_code)
+ start_code = k;
+ if (start_data < k)
+ start_data = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
+ if (k > elf_bss)
+ elf_bss = k;
+ if ((elf_ppnt->p_flags & PF_X) && end_code < k)
+ end_code = k;
+ if (end_data < k)
+ end_data = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
+ if (k > elf_brk) elf_brk = k;
+ }
+
+ elf_entry += load_bias;
+ elf_bss += load_bias;
+ elf_brk += load_bias;
+ start_code += load_bias;
+ end_code += load_bias;
+ start_data += load_bias;
+ end_data += load_bias;
+
+ if (elf_interpreter) {
+ if (interpreter_type & 1) {
+ elf_entry = load_aout_interp(&interp_ex, interpreter_fd);
+ }
+ else if (interpreter_type & 2) {
+ elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,
+ &interp_load_addr);
+ }
+ reloc_func_desc = interp_load_addr;
+
+ close(interpreter_fd);
+ free(elf_interpreter);
+
+ if (elf_entry == ~((abi_ulong)0UL)) {
+ printf("Unable to load interpreter\n");
+ free(elf_phdata);
+ exit(-1);
+ return 0;
+ }
+ }
+
+ free(elf_phdata);
+
+ if (qemu_log_enabled())
+ load_symbols(&elf_ex, bprm->fd);
+
+ if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);
+ info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);
+
+#ifdef LOW_ELF_STACK
+ info->start_stack = bprm->p = elf_stack - 4;
+#endif
+ bprm->p = create_elf_tables(bprm->p,
+ bprm->argc,
+ bprm->envc,
+ &elf_ex,
+ load_addr, load_bias,
+ interp_load_addr,
+ (interpreter_type == INTERPRETER_AOUT ? 0 : 1),
+ info);
+ info->load_addr = reloc_func_desc;
+ info->start_brk = info->brk = elf_brk;
+ info->end_code = end_code;
+ info->start_code = start_code;
+ info->start_data = start_data;
+ info->end_data = end_data;
+ info->start_stack = bprm->p;
+
+ /* Calling set_brk effectively mmaps the pages that we need for the bss and break
+ sections */
+ set_brk(elf_bss, elf_brk);
+
+ padzero(elf_bss, elf_brk);
+
+#if 0
+ printf("(start_brk) %x\n" , info->start_brk);
+ printf("(end_code) %x\n" , info->end_code);
+ printf("(start_code) %x\n" , info->start_code);
+ printf("(end_data) %x\n" , info->end_data);
+ printf("(start_stack) %x\n" , info->start_stack);
+ printf("(brk) %x\n" , info->brk);
+#endif
+
+ if ( info->personality == PER_SVR4 )
+ {
+ /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
+ and some applications "depend" upon this behavior.
+ Since we do not have the power to recompile these, we
+ emulate the SVr4 behavior. Sigh. */
+ mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, -1, 0);
+ }
+
+ info->entry = elf_entry;
+
+ return 0;
+}
+
+static int load_aout_interp(void * exptr, int interp_fd)
+{
+ printf("a.out interpreter not yet supported\n");
+ return(0);
+}
+
+void do_init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ init_thread(regs, infop);
+}
diff --git a/bsd-user/errno_defs.h b/bsd-user/errno_defs.h
new file mode 100644
index 0000000..1efa502
--- /dev/null
+++ b/bsd-user/errno_defs.h
@@ -0,0 +1,149 @@
+/* $OpenBSD: errno.h,v 1.20 2007/09/03 14:37:52 millert Exp $ */
+/* $NetBSD: errno.h,v 1.10 1996/01/20 01:33:53 jtc Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)errno.h 8.5 (Berkeley) 1/21/94
+ */
+
+#define TARGET_EPERM 1 /* Operation not permitted */
+#define TARGET_ENOENT 2 /* No such file or directory */
+#define TARGET_ESRCH 3 /* No such process */
+#define TARGET_EINTR 4 /* Interrupted system call */
+#define TARGET_EIO 5 /* Input/output error */
+#define TARGET_ENXIO 6 /* Device not configured */
+#define TARGET_E2BIG 7 /* Argument list too long */
+#define TARGET_ENOEXEC 8 /* Exec format error */
+#define TARGET_EBADF 9 /* Bad file descriptor */
+#define TARGET_ECHILD 10 /* No child processes */
+#define TARGET_EDEADLK 11 /* Resource deadlock avoided */
+ /* 11 was EAGAIN */
+#define TARGET_ENOMEM 12 /* Cannot allocate memory */
+#define TARGET_EACCES 13 /* Permission denied */
+#define TARGET_EFAULT 14 /* Bad address */
+#define TARGET_ENOTBLK 15 /* Block device required */
+#define TARGET_EBUSY 16 /* Device busy */
+#define TARGET_EEXIST 17 /* File exists */
+#define TARGET_EXDEV 18 /* Cross-device link */
+#define TARGET_ENODEV 19 /* Operation not supported by device */
+#define TARGET_ENOTDIR 20 /* Not a directory */
+#define TARGET_EISDIR 21 /* Is a directory */
+#define TARGET_EINVAL 22 /* Invalid argument */
+#define TARGET_ENFILE 23 /* Too many open files in system */
+#define TARGET_EMFILE 24 /* Too many open files */
+#define TARGET_ENOTTY 25 /* Inappropriate ioctl for device */
+#define TARGET_ETXTBSY 26 /* Text file busy */
+#define TARGET_EFBIG 27 /* File too large */
+#define TARGET_ENOSPC 28 /* No space left on device */
+#define TARGET_ESPIPE 29 /* Illegal seek */
+#define TARGET_EROFS 30 /* Read-only file system */
+#define TARGET_EMLINK 31 /* Too many links */
+#define TARGET_EPIPE 32 /* Broken pipe */
+
+/* math software */
+#define TARGET_EDOM 33 /* Numerical argument out of domain */
+#define TARGET_ERANGE 34 /* Result too large */
+
+/* non-blocking and interrupt i/o */
+#define TARGET_EAGAIN 35 /* Resource temporarily unavailable */
+#define TARGET_EWOULDBLOCK EAGAIN /* Operation would block */
+#define TARGET_EINPROGRESS 36 /* Operation now in progress */
+#define TARGET_EALREADY 37 /* Operation already in progress */
+
+/* ipc/network software -- argument errors */
+#define TARGET_ENOTSOCK 38 /* Socket operation on non-socket */
+#define TARGET_EDESTADDRREQ 39 /* Destination address required */
+#define TARGET_EMSGSIZE 40 /* Message too long */
+#define TARGET_EPROTOTYPE 41 /* Protocol wrong type for socket */
+#define TARGET_ENOPROTOOPT 42 /* Protocol not available */
+#define TARGET_EPROTONOSUPPORT 43 /* Protocol not supported */
+#define TARGET_ESOCKTNOSUPPORT 44 /* Socket type not supported */
+#define TARGET_EOPNOTSUPP 45 /* Operation not supported */
+#define TARGET_EPFNOSUPPORT 46 /* Protocol family not supported */
+#define TARGET_EAFNOSUPPORT 47 /* Address family not supported by protocol family */
+#define TARGET_EADDRINUSE 48 /* Address already in use */
+#define TARGET_EADDRNOTAVAIL 49 /* Can't assign requested address */
+
+/* ipc/network software -- operational errors */
+#define TARGET_ENETDOWN 50 /* Network is down */
+#define TARGET_ENETUNREACH 51 /* Network is unreachable */
+#define TARGET_ENETRESET 52 /* Network dropped connection on reset */
+#define TARGET_ECONNABORTED 53 /* Software caused connection abort */
+#define TARGET_ECONNRESET 54 /* Connection reset by peer */
+#define TARGET_ENOBUFS 55 /* No buffer space available */
+#define TARGET_EISCONN 56 /* Socket is already connected */
+#define TARGET_ENOTCONN 57 /* Socket is not connected */
+#define TARGET_ESHUTDOWN 58 /* Can't send after socket shutdown */
+#define TARGET_ETOOMANYREFS 59 /* Too many references: can't splice */
+#define TARGET_ETIMEDOUT 60 /* Operation timed out */
+#define TARGET_ECONNREFUSED 61 /* Connection refused */
+
+#define TARGET_ELOOP 62 /* Too many levels of symbolic links */
+#define TARGET_ENAMETOOLONG 63 /* File name too long */
+
+/* should be rearranged */
+#define TARGET_EHOSTDOWN 64 /* Host is down */
+#define TARGET_EHOSTUNREACH 65 /* No route to host */
+#define TARGET_ENOTEMPTY 66 /* Directory not empty */
+
+/* quotas & mush */
+#define TARGET_EPROCLIM 67 /* Too many processes */
+#define TARGET_EUSERS 68 /* Too many users */
+#define TARGET_EDQUOT 69 /* Disk quota exceeded */
+
+/* Network File System */
+#define TARGET_ESTALE 70 /* Stale NFS file handle */
+#define TARGET_EREMOTE 71 /* Too many levels of remote in path */
+#define TARGET_EBADRPC 72 /* RPC struct is bad */
+#define TARGET_ERPCMISMATCH 73 /* RPC version wrong */
+#define TARGET_EPROGUNAVAIL 74 /* RPC prog. not avail */
+#define TARGET_EPROGMISMATCH 75 /* Program version wrong */
+#define TARGET_EPROCUNAVAIL 76 /* Bad procedure for program */
+
+#define TARGET_ENOLCK 77 /* No locks available */
+#define TARGET_ENOSYS 78 /* Function not implemented */
+
+#define TARGET_EFTYPE 79 /* Inappropriate file type or format */
+#define TARGET_EAUTH 80 /* Authentication error */
+#define TARGET_ENEEDAUTH 81 /* Need authenticator */
+#define TARGET_EIPSEC 82 /* IPsec processing failure */
+#define TARGET_ENOATTR 83 /* Attribute not found */
+#define TARGET_EILSEQ 84 /* Illegal byte sequence */
+#define TARGET_ENOMEDIUM 85 /* No medium found */
+#define TARGET_EMEDIUMTYPE 86 /* Wrong Medium Type */
+#define TARGET_EOVERFLOW 87 /* Conversion overflow */
+#define TARGET_ECANCELED 88 /* Operation canceled */
+#define TARGET_EIDRM 89 /* Identifier removed */
+#define TARGET_ENOMSG 90 /* No message of desired type */
+#define TARGET_ELAST 90 /* Must be equal largest errno */
diff --git a/bsd-user/freebsd/strace.list b/bsd-user/freebsd/strace.list
new file mode 100644
index 0000000..1edf412
--- /dev/null
+++ b/bsd-user/freebsd/strace.list
@@ -0,0 +1,171 @@
+{ TARGET_FREEBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR___semctl, "__semctl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL },
+{ TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_FREEBSD_NR_acct, "acct", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_adjtime, "adjtime", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_bind, "bind", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_break, "break", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL },
+{ TARGET_FREEBSD_NR_chflags, "chflags", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_FREEBSD_NR_chown, "chown", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_chroot, "chroot", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_close, "close", "%s(%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_dup, "dup", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_dup2, "dup2", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_execve, "execve", NULL, print_execve, NULL },
+{ TARGET_FREEBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL },
+{ TARGET_FREEBSD_NR_fchdir, "fchdir", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fchflags, "fchflags", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL },
+{ TARGET_FREEBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_fcntl, "fcntl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fhopen, "fhopen", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fhstat, "fhstat", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_flock, "flock", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fork, "fork", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_fstat, "fstat", "%s(%d,%p)", NULL, NULL },
+{ TARGET_FREEBSD_NR_fstatfs, "fstatfs", "%s(%d,%p)", NULL, NULL },
+{ TARGET_FREEBSD_NR_fsync, "fsync", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_futimes, "futimes", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_freebsd6_mmap, "freebsd6_mmap", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getegid, "getegid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_getfh, "getfh", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getfsstat, "getfsstat", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getgid, "getgid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_getgroups, "getgroups", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getitimer, "getitimer", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getlogin, "getlogin", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getpeername, "getpeername", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getpgid, "getpgid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_getpid, "getpid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_getppid, "getppid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
+{ TARGET_FREEBSD_NR_getresgid, "getresgid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getresuid, "getresuid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getrusage, "getrusage", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getsid, "getsid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getsockname, "getsockname", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_getuid, "getuid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_ioctl, "ioctl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL },
+{ TARGET_FREEBSD_NR_kevent, "kevent", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_kill, "kill", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_kqueue, "kqueue", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_ktrace, "ktrace", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_lchown, "lchown", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_FREEBSD_NR_listen, "listen", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_lseek, "lseek", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL },
+{ TARGET_FREEBSD_NR_madvise, "madvise", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_mincore, "mincore", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_minherit, "minherit", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_FREEBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL },
+{ TARGET_FREEBSD_NR_mlock, "mlock", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_mlockall, "mlockall", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr },
+{ TARGET_FREEBSD_NR_mount, "mount", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_msgctl, "msgctl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_msgget, "msgget", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_msync, "msync", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_munlock, "munlock", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_munlockall, "munlockall", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL },
+{ TARGET_FREEBSD_NR_pathconf, "pathconf", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_pipe, "pipe", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_poll, "poll", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_pread, "pread", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_preadv, "preadv", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_profil, "profil", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_ptrace, "ptrace", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_pwrite, "pwrite", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_pwritev, "pwritev", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_quotactl, "quotactl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_readv, "readv", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_reboot, "reboot", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_FREEBSD_NR_revoke, "revoke", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_rfork, "rfork", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_rmdir, "rmdir", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sbrk, "sbrk", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_select, "select", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_semget, "semget", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_semop, "semop", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sendto, "sendto", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setegid, "setegid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_seteuid, "seteuid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setgid, "setgid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setgroups, "setgroups", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setitimer, "setitimer", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setlogin, "setlogin", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setpgid, "setpgid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setpriority, "setpriority", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setregid, "setregid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setresgid, "setresgid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setresuid, "setresuid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setreuid, "setreuid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setsid, "setsid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_setuid, "setuid", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_shmat, "shmat", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_shmctl, "shmctl", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_shmdt, "shmdt", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_shmget, "shmget", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_shutdown, "shutdown", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sigaction, "sigaction", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sigaltstack, "sigaltstack", "%s(%p,%p)", NULL, NULL },
+{ TARGET_FREEBSD_NR_sigpending, "sigpending", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sigprocmask, "sigprocmask", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sigreturn, "sigreturn", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sigsuspend, "sigsuspend", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_socket, "socket", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_socketpair, "socketpair", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sstk, "sstk", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL },
+{ TARGET_FREEBSD_NR_statfs, "statfs", "%s(\"%s\",%p)", NULL, NULL },
+{ TARGET_FREEBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_FREEBSD_NR_sync, "sync", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_sysarch, "sysarch", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_syscall, "syscall", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_truncate, "truncate", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL },
+{ TARGET_FREEBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL },
+{ TARGET_FREEBSD_NR_unmount, "unmount", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_utimes, "utimes", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_vfork, "vfork", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_wait4, "wait4", NULL, NULL, NULL },
+{ TARGET_FREEBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_FREEBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL },
diff --git a/bsd-user/freebsd/syscall_nr.h b/bsd-user/freebsd/syscall_nr.h
new file mode 100644
index 0000000..36336ab
--- /dev/null
+++ b/bsd-user/freebsd/syscall_nr.h
@@ -0,0 +1,373 @@
+/*
+ * System call numbers.
+ *
+ * $FreeBSD: src/sys/sys/syscall.h,v 1.224 2008/08/24 21:23:08 rwatson Exp $
+ * created from FreeBSD: head/sys/kern/syscalls.master 182123 2008-08-24 21:20:35Z rwatson
+ */
+
+#define TARGET_FREEBSD_NR_syscall 0
+#define TARGET_FREEBSD_NR_exit 1
+#define TARGET_FREEBSD_NR_fork 2
+#define TARGET_FREEBSD_NR_read 3
+#define TARGET_FREEBSD_NR_write 4
+#define TARGET_FREEBSD_NR_open 5
+#define TARGET_FREEBSD_NR_close 6
+#define TARGET_FREEBSD_NR_wait4 7
+#define TARGET_FREEBSD_NR_link 9
+#define TARGET_FREEBSD_NR_unlink 10
+#define TARGET_FREEBSD_NR_chdir 12
+#define TARGET_FREEBSD_NR_fchdir 13
+#define TARGET_FREEBSD_NR_mknod 14
+#define TARGET_FREEBSD_NR_chmod 15
+#define TARGET_FREEBSD_NR_chown 16
+#define TARGET_FREEBSD_NR_break 17
+#define TARGET_FREEBSD_NR_freebsd4_getfsstat 18
+#define TARGET_FREEBSD_NR_getpid 20
+#define TARGET_FREEBSD_NR_mount 21
+#define TARGET_FREEBSD_NR_unmount 22
+#define TARGET_FREEBSD_NR_setuid 23
+#define TARGET_FREEBSD_NR_getuid 24
+#define TARGET_FREEBSD_NR_geteuid 25
+#define TARGET_FREEBSD_NR_ptrace 26
+#define TARGET_FREEBSD_NR_recvmsg 27
+#define TARGET_FREEBSD_NR_sendmsg 28
+#define TARGET_FREEBSD_NR_recvfrom 29
+#define TARGET_FREEBSD_NR_accept 30
+#define TARGET_FREEBSD_NR_getpeername 31
+#define TARGET_FREEBSD_NR_getsockname 32
+#define TARGET_FREEBSD_NR_access 33
+#define TARGET_FREEBSD_NR_chflags 34
+#define TARGET_FREEBSD_NR_fchflags 35
+#define TARGET_FREEBSD_NR_sync 36
+#define TARGET_FREEBSD_NR_kill 37
+#define TARGET_FREEBSD_NR_getppid 39
+#define TARGET_FREEBSD_NR_dup 41
+#define TARGET_FREEBSD_NR_pipe 42
+#define TARGET_FREEBSD_NR_getegid 43
+#define TARGET_FREEBSD_NR_profil 44
+#define TARGET_FREEBSD_NR_ktrace 45
+#define TARGET_FREEBSD_NR_getgid 47
+#define TARGET_FREEBSD_NR_getlogin 49
+#define TARGET_FREEBSD_NR_setlogin 50
+#define TARGET_FREEBSD_NR_acct 51
+#define TARGET_FREEBSD_NR_sigaltstack 53
+#define TARGET_FREEBSD_NR_ioctl 54
+#define TARGET_FREEBSD_NR_reboot 55
+#define TARGET_FREEBSD_NR_revoke 56
+#define TARGET_FREEBSD_NR_symlink 57
+#define TARGET_FREEBSD_NR_readlink 58
+#define TARGET_FREEBSD_NR_execve 59
+#define TARGET_FREEBSD_NR_umask 60
+#define TARGET_FREEBSD_NR_chroot 61
+#define TARGET_FREEBSD_NR_msync 65
+#define TARGET_FREEBSD_NR_vfork 66
+#define TARGET_FREEBSD_NR_sbrk 69
+#define TARGET_FREEBSD_NR_sstk 70
+#define TARGET_FREEBSD_NR_vadvise 72
+#define TARGET_FREEBSD_NR_munmap 73
+#define TARGET_FREEBSD_NR_mprotect 74
+#define TARGET_FREEBSD_NR_madvise 75
+#define TARGET_FREEBSD_NR_mincore 78
+#define TARGET_FREEBSD_NR_getgroups 79
+#define TARGET_FREEBSD_NR_setgroups 80
+#define TARGET_FREEBSD_NR_getpgrp 81
+#define TARGET_FREEBSD_NR_setpgid 82
+#define TARGET_FREEBSD_NR_setitimer 83
+#define TARGET_FREEBSD_NR_swapon 85
+#define TARGET_FREEBSD_NR_getitimer 86
+#define TARGET_FREEBSD_NR_getdtablesize 89
+#define TARGET_FREEBSD_NR_dup2 90
+#define TARGET_FREEBSD_NR_fcntl 92
+#define TARGET_FREEBSD_NR_select 93
+#define TARGET_FREEBSD_NR_fsync 95
+#define TARGET_FREEBSD_NR_setpriority 96
+#define TARGET_FREEBSD_NR_socket 97
+#define TARGET_FREEBSD_NR_connect 98
+#define TARGET_FREEBSD_NR_getpriority 100
+#define TARGET_FREEBSD_NR_bind 104
+#define TARGET_FREEBSD_NR_setsockopt 105
+#define TARGET_FREEBSD_NR_listen 106
+#define TARGET_FREEBSD_NR_gettimeofday 116
+#define TARGET_FREEBSD_NR_getrusage 117
+#define TARGET_FREEBSD_NR_getsockopt 118
+#define TARGET_FREEBSD_NR_readv 120
+#define TARGET_FREEBSD_NR_writev 121
+#define TARGET_FREEBSD_NR_settimeofday 122
+#define TARGET_FREEBSD_NR_fchown 123
+#define TARGET_FREEBSD_NR_fchmod 124
+#define TARGET_FREEBSD_NR_setreuid 126
+#define TARGET_FREEBSD_NR_setregid 127
+#define TARGET_FREEBSD_NR_rename 128
+#define TARGET_FREEBSD_NR_flock 131
+#define TARGET_FREEBSD_NR_mkfifo 132
+#define TARGET_FREEBSD_NR_sendto 133
+#define TARGET_FREEBSD_NR_shutdown 134
+#define TARGET_FREEBSD_NR_socketpair 135
+#define TARGET_FREEBSD_NR_mkdir 136
+#define TARGET_FREEBSD_NR_rmdir 137
+#define TARGET_FREEBSD_NR_utimes 138
+#define TARGET_FREEBSD_NR_adjtime 140
+#define TARGET_FREEBSD_NR_setsid 147
+#define TARGET_FREEBSD_NR_quotactl 148
+#define TARGET_FREEBSD_NR_nlm_syscall 154
+#define TARGET_FREEBSD_NR_nfssvc 155
+#define TARGET_FREEBSD_NR_freebsd4_statfs 157
+#define TARGET_FREEBSD_NR_freebsd4_fstatfs 158
+#define TARGET_FREEBSD_NR_lgetfh 160
+#define TARGET_FREEBSD_NR_getfh 161
+#define TARGET_FREEBSD_NR_getdomainname 162
+#define TARGET_FREEBSD_NR_setdomainname 163
+#define TARGET_FREEBSD_NR_uname 164
+#define TARGET_FREEBSD_NR_sysarch 165
+#define TARGET_FREEBSD_NR_rtprio 166
+#define TARGET_FREEBSD_NR_semsys 169
+#define TARGET_FREEBSD_NR_msgsys 170
+#define TARGET_FREEBSD_NR_shmsys 171
+#define TARGET_FREEBSD_NR_freebsd6_pread 173
+#define TARGET_FREEBSD_NR_freebsd6_pwrite 174
+#define TARGET_FREEBSD_NR_setfib 175
+#define TARGET_FREEBSD_NR_ntp_adjtime 176
+#define TARGET_FREEBSD_NR_setgid 181
+#define TARGET_FREEBSD_NR_setegid 182
+#define TARGET_FREEBSD_NR_seteuid 183
+#define TARGET_FREEBSD_NR_stat 188
+#define TARGET_FREEBSD_NR_fstat 189
+#define TARGET_FREEBSD_NR_lstat 190
+#define TARGET_FREEBSD_NR_pathconf 191
+#define TARGET_FREEBSD_NR_fpathconf 192
+#define TARGET_FREEBSD_NR_getrlimit 194
+#define TARGET_FREEBSD_NR_setrlimit 195
+#define TARGET_FREEBSD_NR_getdirentries 196
+#define TARGET_FREEBSD_NR_freebsd6_mmap 197
+#define TARGET_FREEBSD_NR___syscall 198
+#define TARGET_FREEBSD_NR_freebsd6_lseek 199
+#define TARGET_FREEBSD_NR_freebsd6_truncate 200
+#define TARGET_FREEBSD_NR_freebsd6_ftruncate 201
+#define TARGET_FREEBSD_NR___sysctl 202
+#define TARGET_FREEBSD_NR_mlock 203
+#define TARGET_FREEBSD_NR_munlock 204
+#define TARGET_FREEBSD_NR_undelete 205
+#define TARGET_FREEBSD_NR_futimes 206
+#define TARGET_FREEBSD_NR_getpgid 207
+#define TARGET_FREEBSD_NR_poll 209
+#define TARGET_FREEBSD_NR___semctl 220
+#define TARGET_FREEBSD_NR_semget 221
+#define TARGET_FREEBSD_NR_semop 222
+#define TARGET_FREEBSD_NR_msgctl 224
+#define TARGET_FREEBSD_NR_msgget 225
+#define TARGET_FREEBSD_NR_msgsnd 226
+#define TARGET_FREEBSD_NR_msgrcv 227
+#define TARGET_FREEBSD_NR_shmat 228
+#define TARGET_FREEBSD_NR_shmctl 229
+#define TARGET_FREEBSD_NR_shmdt 230
+#define TARGET_FREEBSD_NR_shmget 231
+#define TARGET_FREEBSD_NR_clock_gettime 232
+#define TARGET_FREEBSD_NR_clock_settime 233
+#define TARGET_FREEBSD_NR_clock_getres 234
+#define TARGET_FREEBSD_NR_ktimer_create 235
+#define TARGET_FREEBSD_NR_ktimer_delete 236
+#define TARGET_FREEBSD_NR_ktimer_settime 237
+#define TARGET_FREEBSD_NR_ktimer_gettime 238
+#define TARGET_FREEBSD_NR_ktimer_getoverrun 239
+#define TARGET_FREEBSD_NR_nanosleep 240
+#define TARGET_FREEBSD_NR_ntp_gettime 248
+#define TARGET_FREEBSD_NR_minherit 250
+#define TARGET_FREEBSD_NR_rfork 251
+#define TARGET_FREEBSD_NR_openbsd_poll 252
+#define TARGET_FREEBSD_NR_issetugid 253
+#define TARGET_FREEBSD_NR_lchown 254
+#define TARGET_FREEBSD_NR_aio_read 255
+#define TARGET_FREEBSD_NR_aio_write 256
+#define TARGET_FREEBSD_NR_lio_listio 257
+#define TARGET_FREEBSD_NR_getdents 272
+#define TARGET_FREEBSD_NR_lchmod 274
+#define TARGET_FREEBSD_NR_netbsd_lchown 275
+#define TARGET_FREEBSD_NR_lutimes 276
+#define TARGET_FREEBSD_NR_netbsd_msync 277
+#define TARGET_FREEBSD_NR_nstat 278
+#define TARGET_FREEBSD_NR_nfstat 279
+#define TARGET_FREEBSD_NR_nlstat 280
+#define TARGET_FREEBSD_NR_preadv 289
+#define TARGET_FREEBSD_NR_pwritev 290
+#define TARGET_FREEBSD_NR_freebsd4_fhstatfs 297
+#define TARGET_FREEBSD_NR_fhopen 298
+#define TARGET_FREEBSD_NR_fhstat 299
+#define TARGET_FREEBSD_NR_modnext 300
+#define TARGET_FREEBSD_NR_modstat 301
+#define TARGET_FREEBSD_NR_modfnext 302
+#define TARGET_FREEBSD_NR_modfind 303
+#define TARGET_FREEBSD_NR_kldload 304
+#define TARGET_FREEBSD_NR_kldunload 305
+#define TARGET_FREEBSD_NR_kldfind 306
+#define TARGET_FREEBSD_NR_kldnext 307
+#define TARGET_FREEBSD_NR_kldstat 308
+#define TARGET_FREEBSD_NR_kldfirstmod 309
+#define TARGET_FREEBSD_NR_getsid 310
+#define TARGET_FREEBSD_NR_setresuid 311
+#define TARGET_FREEBSD_NR_setresgid 312
+#define TARGET_FREEBSD_NR_aio_return 314
+#define TARGET_FREEBSD_NR_aio_suspend 315
+#define TARGET_FREEBSD_NR_aio_cancel 316
+#define TARGET_FREEBSD_NR_aio_error 317
+#define TARGET_FREEBSD_NR_oaio_read 318
+#define TARGET_FREEBSD_NR_oaio_write 319
+#define TARGET_FREEBSD_NR_olio_listio 320
+#define TARGET_FREEBSD_NR_yield 321
+#define TARGET_FREEBSD_NR_mlockall 324
+#define TARGET_FREEBSD_NR_munlockall 325
+#define TARGET_FREEBSD_NR___getcwd 326
+#define TARGET_FREEBSD_NR_sched_setparam 327
+#define TARGET_FREEBSD_NR_sched_getparam 328
+#define TARGET_FREEBSD_NR_sched_setscheduler 329
+#define TARGET_FREEBSD_NR_sched_getscheduler 330
+#define TARGET_FREEBSD_NR_sched_yield 331
+#define TARGET_FREEBSD_NR_sched_get_priority_max 332
+#define TARGET_FREEBSD_NR_sched_get_priority_min 333
+#define TARGET_FREEBSD_NR_sched_rr_get_interval 334
+#define TARGET_FREEBSD_NR_utrace 335
+#define TARGET_FREEBSD_NR_freebsd4_sendfile 336
+#define TARGET_FREEBSD_NR_kldsym 337
+#define TARGET_FREEBSD_NR_jail 338
+#define TARGET_FREEBSD_NR_sigprocmask 340
+#define TARGET_FREEBSD_NR_sigsuspend 341
+#define TARGET_FREEBSD_NR_freebsd4_sigaction 342
+#define TARGET_FREEBSD_NR_sigpending 343
+#define TARGET_FREEBSD_NR_freebsd4_sigreturn 344
+#define TARGET_FREEBSD_NR_sigtimedwait 345
+#define TARGET_FREEBSD_NR_sigwaitinfo 346
+#define TARGET_FREEBSD_NR___acl_get_file 347
+#define TARGET_FREEBSD_NR___acl_set_file 348
+#define TARGET_FREEBSD_NR___acl_get_fd 349
+#define TARGET_FREEBSD_NR___acl_set_fd 350
+#define TARGET_FREEBSD_NR___acl_delete_file 351
+#define TARGET_FREEBSD_NR___acl_delete_fd 352
+#define TARGET_FREEBSD_NR___acl_aclcheck_file 353
+#define TARGET_FREEBSD_NR___acl_aclcheck_fd 354
+#define TARGET_FREEBSD_NR_extattrctl 355
+#define TARGET_FREEBSD_NR_extattr_set_file 356
+#define TARGET_FREEBSD_NR_extattr_get_file 357
+#define TARGET_FREEBSD_NR_extattr_delete_file 358
+#define TARGET_FREEBSD_NR_aio_waitcomplete 359
+#define TARGET_FREEBSD_NR_getresuid 360
+#define TARGET_FREEBSD_NR_getresgid 361
+#define TARGET_FREEBSD_NR_kqueue 362
+#define TARGET_FREEBSD_NR_kevent 363
+#define TARGET_FREEBSD_NR_extattr_set_fd 371
+#define TARGET_FREEBSD_NR_extattr_get_fd 372
+#define TARGET_FREEBSD_NR_extattr_delete_fd 373
+#define TARGET_FREEBSD_NR___setugid 374
+#define TARGET_FREEBSD_NR_nfsclnt 375
+#define TARGET_FREEBSD_NR_eaccess 376
+#define TARGET_FREEBSD_NR_nmount 378
+#define TARGET_FREEBSD_NR___mac_get_proc 384
+#define TARGET_FREEBSD_NR___mac_set_proc 385
+#define TARGET_FREEBSD_NR___mac_get_fd 386
+#define TARGET_FREEBSD_NR___mac_get_file 387
+#define TARGET_FREEBSD_NR___mac_set_fd 388
+#define TARGET_FREEBSD_NR___mac_set_file 389
+#define TARGET_FREEBSD_NR_kenv 390
+#define TARGET_FREEBSD_NR_lchflags 391
+#define TARGET_FREEBSD_NR_uuidgen 392
+#define TARGET_FREEBSD_NR_sendfile 393
+#define TARGET_FREEBSD_NR_mac_syscall 394
+#define TARGET_FREEBSD_NR_getfsstat 395
+#define TARGET_FREEBSD_NR_statfs 396
+#define TARGET_FREEBSD_NR_fstatfs 397
+#define TARGET_FREEBSD_NR_fhstatfs 398
+#define TARGET_FREEBSD_NR_ksem_close 400
+#define TARGET_FREEBSD_NR_ksem_post 401
+#define TARGET_FREEBSD_NR_ksem_wait 402
+#define TARGET_FREEBSD_NR_ksem_trywait 403
+#define TARGET_FREEBSD_NR_ksem_init 404
+#define TARGET_FREEBSD_NR_ksem_open 405
+#define TARGET_FREEBSD_NR_ksem_unlink 406
+#define TARGET_FREEBSD_NR_ksem_getvalue 407
+#define TARGET_FREEBSD_NR_ksem_destroy 408
+#define TARGET_FREEBSD_NR___mac_get_pid 409
+#define TARGET_FREEBSD_NR___mac_get_link 410
+#define TARGET_FREEBSD_NR___mac_set_link 411
+#define TARGET_FREEBSD_NR_extattr_set_link 412
+#define TARGET_FREEBSD_NR_extattr_get_link 413
+#define TARGET_FREEBSD_NR_extattr_delete_link 414
+#define TARGET_FREEBSD_NR___mac_execve 415
+#define TARGET_FREEBSD_NR_sigaction 416
+#define TARGET_FREEBSD_NR_sigreturn 417
+#define TARGET_FREEBSD_NR_getcontext 421
+#define TARGET_FREEBSD_NR_setcontext 422
+#define TARGET_FREEBSD_NR_swapcontext 423
+#define TARGET_FREEBSD_NR_swapoff 424
+#define TARGET_FREEBSD_NR___acl_get_link 425
+#define TARGET_FREEBSD_NR___acl_set_link 426
+#define TARGET_FREEBSD_NR___acl_delete_link 427
+#define TARGET_FREEBSD_NR___acl_aclcheck_link 428
+#define TARGET_FREEBSD_NR_sigwait 429
+#define TARGET_FREEBSD_NR_thr_create 430
+#define TARGET_FREEBSD_NR_thr_exit 431
+#define TARGET_FREEBSD_NR_thr_self 432
+#define TARGET_FREEBSD_NR_thr_kill 433
+#define TARGET_FREEBSD_NR__umtx_lock 434
+#define TARGET_FREEBSD_NR__umtx_unlock 435
+#define TARGET_FREEBSD_NR_jail_attach 436
+#define TARGET_FREEBSD_NR_extattr_list_fd 437
+#define TARGET_FREEBSD_NR_extattr_list_file 438
+#define TARGET_FREEBSD_NR_extattr_list_link 439
+#define TARGET_FREEBSD_NR_ksem_timedwait 441
+#define TARGET_FREEBSD_NR_thr_suspend 442
+#define TARGET_FREEBSD_NR_thr_wake 443
+#define TARGET_FREEBSD_NR_kldunloadf 444
+#define TARGET_FREEBSD_NR_audit 445
+#define TARGET_FREEBSD_NR_auditon 446
+#define TARGET_FREEBSD_NR_getauid 447
+#define TARGET_FREEBSD_NR_setauid 448
+#define TARGET_FREEBSD_NR_getaudit 449
+#define TARGET_FREEBSD_NR_setaudit 450
+#define TARGET_FREEBSD_NR_getaudit_addr 451
+#define TARGET_FREEBSD_NR_setaudit_addr 452
+#define TARGET_FREEBSD_NR_auditctl 453
+#define TARGET_FREEBSD_NR__umtx_op 454
+#define TARGET_FREEBSD_NR_thr_new 455
+#define TARGET_FREEBSD_NR_sigqueue 456
+#define TARGET_FREEBSD_NR_kmq_open 457
+#define TARGET_FREEBSD_NR_kmq_setattr 458
+#define TARGET_FREEBSD_NR_kmq_timedreceive 459
+#define TARGET_FREEBSD_NR_kmq_timedsend 460
+#define TARGET_FREEBSD_NR_kmq_notify 461
+#define TARGET_FREEBSD_NR_kmq_unlink 462
+#define TARGET_FREEBSD_NR_abort2 463
+#define TARGET_FREEBSD_NR_thr_set_name 464
+#define TARGET_FREEBSD_NR_aio_fsync 465
+#define TARGET_FREEBSD_NR_rtprio_thread 466
+#define TARGET_FREEBSD_NR_sctp_peeloff 471
+#define TARGET_FREEBSD_NR_sctp_generic_sendmsg 472
+#define TARGET_FREEBSD_NR_sctp_generic_sendmsg_iov 473
+#define TARGET_FREEBSD_NR_sctp_generic_recvmsg 474
+#define TARGET_FREEBSD_NR_pread 475
+#define TARGET_FREEBSD_NR_pwrite 476
+#define TARGET_FREEBSD_NR_mmap 477
+#define TARGET_FREEBSD_NR_lseek 478
+#define TARGET_FREEBSD_NR_truncate 479
+#define TARGET_FREEBSD_NR_ftruncate 480
+#define TARGET_FREEBSD_NR_thr_kill2 481
+#define TARGET_FREEBSD_NR_shm_open 482
+#define TARGET_FREEBSD_NR_shm_unlink 483
+#define TARGET_FREEBSD_NR_cpuset 484
+#define TARGET_FREEBSD_NR_cpuset_setid 485
+#define TARGET_FREEBSD_NR_cpuset_getid 486
+#define TARGET_FREEBSD_NR_cpuset_getaffinity 487
+#define TARGET_FREEBSD_NR_cpuset_setaffinity 488
+#define TARGET_FREEBSD_NR_faccessat 489
+#define TARGET_FREEBSD_NR_fchmodat 490
+#define TARGET_FREEBSD_NR_fchownat 491
+#define TARGET_FREEBSD_NR_fexecve 492
+#define TARGET_FREEBSD_NR_fstatat 493
+#define TARGET_FREEBSD_NR_futimesat 494
+#define TARGET_FREEBSD_NR_linkat 495
+#define TARGET_FREEBSD_NR_mkdirat 496
+#define TARGET_FREEBSD_NR_mkfifoat 497
+#define TARGET_FREEBSD_NR_mknodat 498
+#define TARGET_FREEBSD_NR_openat 499
+#define TARGET_FREEBSD_NR_readlinkat 500
+#define TARGET_FREEBSD_NR_renameat 501
+#define TARGET_FREEBSD_NR_symlinkat 502
+#define TARGET_FREEBSD_NR_unlinkat 503
+#define TARGET_FREEBSD_NR_posix_openpt 504
diff --git a/bsd-user/i386/syscall.h b/bsd-user/i386/syscall.h
new file mode 100644
index 0000000..9b34c61
--- /dev/null
+++ b/bsd-user/i386/syscall.h
@@ -0,0 +1,161 @@
+/* default linux values for the selectors */
+#define __USER_CS (0x23)
+#define __USER_DS (0x2B)
+
+struct target_pt_regs {
+ long ebx;
+ long ecx;
+ long edx;
+ long esi;
+ long edi;
+ long ebp;
+ long eax;
+ int xds;
+ int xes;
+ long orig_eax;
+ long eip;
+ int xcs;
+ long eflags;
+ long esp;
+ int xss;
+};
+
+/* ioctls */
+
+#define TARGET_LDT_ENTRIES 8192
+#define TARGET_LDT_ENTRY_SIZE 8
+
+#define TARGET_GDT_ENTRIES 9
+#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
+#define TARGET_GDT_ENTRY_TLS_MIN 6
+#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
+
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ abi_ulong base_addr;
+ unsigned int limit;
+ unsigned int flags;
+};
+
+/* vm86 defines */
+
+#define TARGET_BIOSSEG 0x0f000
+
+#define TARGET_CPU_086 0
+#define TARGET_CPU_186 1
+#define TARGET_CPU_286 2
+#define TARGET_CPU_386 3
+#define TARGET_CPU_486 4
+#define TARGET_CPU_586 5
+
+#define TARGET_VM86_SIGNAL 0 /* return due to signal */
+#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
+#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
+#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
+
+/*
+ * Additional return values when invoking new vm86()
+ */
+#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */
+#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */
+
+/*
+ * function codes when invoking new vm86()
+ */
+#define TARGET_VM86_PLUS_INSTALL_CHECK 0
+#define TARGET_VM86_ENTER 1
+#define TARGET_VM86_ENTER_NO_BYPASS 2
+#define TARGET_VM86_REQUEST_IRQ 3
+#define TARGET_VM86_FREE_IRQ 4
+#define TARGET_VM86_GET_IRQ_BITS 5
+#define TARGET_VM86_GET_AND_RESET_IRQ 6
+
+/*
+ * This is the stack-layout seen by the user space program when we have
+ * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
+ * is 'kernel_vm86_regs' (see below).
+ */
+
+struct target_vm86_regs {
+/*
+ * normal regs, with special meaning for the segment descriptors..
+ */
+ abi_long ebx;
+ abi_long ecx;
+ abi_long edx;
+ abi_long esi;
+ abi_long edi;
+ abi_long ebp;
+ abi_long eax;
+ abi_long __null_ds;
+ abi_long __null_es;
+ abi_long __null_fs;
+ abi_long __null_gs;
+ abi_long orig_eax;
+ abi_long eip;
+ unsigned short cs, __csh;
+ abi_long eflags;
+ abi_long esp;
+ unsigned short ss, __ssh;
+/*
+ * these are specific to v86 mode:
+ */
+ unsigned short es, __esh;
+ unsigned short ds, __dsh;
+ unsigned short fs, __fsh;
+ unsigned short gs, __gsh;
+};
+
+struct target_revectored_struct {
+ abi_ulong __map[8]; /* 256 bits */
+};
+
+struct target_vm86_struct {
+ struct target_vm86_regs regs;
+ abi_ulong flags;
+ abi_ulong screen_bitmap;
+ abi_ulong cpu_type;
+ struct target_revectored_struct int_revectored;
+ struct target_revectored_struct int21_revectored;
+};
+
+/*
+ * flags masks
+ */
+#define TARGET_VM86_SCREEN_BITMAP 0x0001
+
+struct target_vm86plus_info_struct {
+ abi_ulong flags;
+#define TARGET_force_return_for_pic (1 << 0)
+#define TARGET_vm86dbg_active (1 << 1) /* for debugger */
+#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */
+#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */
+ unsigned char vm86dbg_intxxtab[32]; /* for debugger */
+};
+
+struct target_vm86plus_struct {
+ struct target_vm86_regs regs;
+ abi_ulong flags;
+ abi_ulong screen_bitmap;
+ abi_ulong cpu_type;
+ struct target_revectored_struct int_revectored;
+ struct target_revectored_struct int21_revectored;
+ struct target_vm86plus_info_struct vm86plus;
+};
+
+/* FreeBSD sysarch(2) */
+#define TARGET_FREEBSD_I386_GET_LDT 0
+#define TARGET_FREEBSD_I386_SET_LDT 1
+ /* I386_IOPL */
+#define TARGET_FREEBSD_I386_GET_IOPERM 3
+#define TARGET_FREEBSD_I386_SET_IOPERM 4
+ /* xxxxx */
+#define TARGET_FREEBSD_I386_VM86 6
+#define TARGET_FREEBSD_I386_GET_FSBASE 7
+#define TARGET_FREEBSD_I386_SET_FSBASE 8
+#define TARGET_FREEBSD_I386_GET_GSBASE 9
+#define TARGET_FREEBSD_I386_SET_GSBASE 10
+
+
+#define UNAME_MACHINE "i386"
+
diff --git a/bsd-user/i386/target_signal.h b/bsd-user/i386/target_signal.h
new file mode 100644
index 0000000..2ef36d1
--- /dev/null
+++ b/bsd-user/i386/target_signal.h
@@ -0,0 +1,20 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+
+static inline abi_ulong get_sp_from_cpustate(CPUX86State *state)
+{
+ return state->regs[R_ESP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/bsd-user/main.c b/bsd-user/main.c
new file mode 100644
index 0000000..6b12f8b
--- /dev/null
+++ b/bsd-user/main.c
@@ -0,0 +1,1131 @@
+/*
+ * qemu user main
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <machine/trap.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+/* For tb_lock */
+#include "exec-all.h"
+#include "tcg.h"
+#include "qemu-timer.h"
+#include "envlist.h"
+
+#define DEBUG_LOGFILE "/tmp/qemu.log"
+
+int singlestep;
+#if defined(CONFIG_USE_GUEST_BASE)
+unsigned long mmap_min_addr;
+unsigned long guest_base;
+int have_guest_base;
+#endif
+
+static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
+const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
+extern char **environ;
+enum BSDType bsd_type;
+
+/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
+ we allocate a bigger stack. Need a better solution, for example
+ by remapping the process stack directly at the right place */
+unsigned long x86_stack_size = 512 * 1024;
+
+void gemu_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+#if defined(TARGET_I386)
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ return -1;
+}
+#endif
+
+/* These are no-ops because we are not threadsafe. */
+static inline void cpu_exec_start(CPUState *env)
+{
+}
+
+static inline void cpu_exec_end(CPUState *env)
+{
+}
+
+static inline void start_exclusive(void)
+{
+}
+
+static inline void end_exclusive(void)
+{
+}
+
+void fork_start(void)
+{
+}
+
+void fork_end(int child)
+{
+ if (child) {
+ gdbserver_fork(thread_env);
+ }
+}
+
+void cpu_list_lock(void)
+{
+}
+
+void cpu_list_unlock(void)
+{
+}
+
+#ifdef TARGET_I386
+/***********************************************************/
+/* CPUX86 core interface */
+
+void cpu_smm_update(CPUState *env)
+{
+}
+
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ return cpu_get_real_ticks();
+}
+
+static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
+ int flags)
+{
+ unsigned int e1, e2;
+ uint32_t *p;
+ e1 = (addr << 16) | (limit & 0xffff);
+ e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
+ e2 |= flags;
+ p = ptr;
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
+}
+
+static uint64_t *idt_table;
+#ifdef TARGET_X86_64
+static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
+ uint64_t addr, unsigned int sel)
+{
+ uint32_t *p, e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ p = ptr;
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
+ p[2] = tswap32(addr >> 32);
+ p[3] = 0;
+}
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
+}
+#else
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ uint32_t addr, unsigned int sel)
+{
+ uint32_t *p, e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ p = ptr;
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
+}
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
+#endif
+
+void cpu_loop(CPUX86State *env)
+{
+ int trapnr;
+ abi_ulong pc;
+ //target_siginfo_t info;
+
+ for(;;) {
+ trapnr = cpu_x86_exec(env);
+ switch(trapnr) {
+ case 0x80:
+ /* syscall from int $0x80 */
+ if (bsd_type == target_freebsd) {
+ abi_ulong params = (abi_ulong) env->regs[R_ESP] +
+ sizeof(int32_t);
+ int32_t syscall_nr = env->regs[R_EAX];
+ int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8;
+
+ if (syscall_nr == TARGET_FREEBSD_NR_syscall) {
+ get_user_s32(syscall_nr, params);
+ params += sizeof(int32_t);
+ } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) {
+ get_user_s32(syscall_nr, params);
+ params += sizeof(int64_t);
+ }
+ get_user_s32(arg1, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg2, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg3, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg4, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg5, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg6, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg7, params);
+ params += sizeof(int32_t);
+ get_user_s32(arg8, params);
+ env->regs[R_EAX] = do_freebsd_syscall(env,
+ syscall_nr,
+ arg1,
+ arg2,
+ arg3,
+ arg4,
+ arg5,
+ arg6,
+ arg7,
+ arg8);
+ } else { //if (bsd_type == target_openbsd)
+ env->regs[R_EAX] = do_openbsd_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EBX],
+ env->regs[R_ECX],
+ env->regs[R_EDX],
+ env->regs[R_ESI],
+ env->regs[R_EDI],
+ env->regs[R_EBP]);
+ }
+ if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) {
+ env->regs[R_EAX] = -env->regs[R_EAX];
+ env->eflags |= CC_C;
+ } else {
+ env->eflags &= ~CC_C;
+ }
+ break;
+#ifndef TARGET_ABI32
+ case EXCP_SYSCALL:
+ /* syscall from syscall intruction */
+ if (bsd_type == target_freebsd)
+ env->regs[R_EAX] = do_freebsd_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EDI],
+ env->regs[R_ESI],
+ env->regs[R_EDX],
+ env->regs[R_ECX],
+ env->regs[8],
+ env->regs[9], 0, 0);
+ else { //if (bsd_type == target_openbsd)
+ env->regs[R_EAX] = do_openbsd_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EDI],
+ env->regs[R_ESI],
+ env->regs[R_EDX],
+ env->regs[10],
+ env->regs[8],
+ env->regs[9]);
+ }
+ env->eip = env->exception_next_eip;
+ if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) {
+ env->regs[R_EAX] = -env->regs[R_EAX];
+ env->eflags |= CC_C;
+ } else {
+ env->eflags &= ~CC_C;
+ }
+ break;
+#endif
+#if 0
+ case EXCP0B_NOSEG:
+ case EXCP0C_STACK:
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP0D_GPF:
+ /* XXX: potential problem if ABI32 */
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_fault(env);
+ } else
+#endif
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP0E_PAGE:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ if (!(env->error_code & 1))
+ info.si_code = TARGET_SEGV_MAPERR;
+ else
+ info.si_code = TARGET_SEGV_ACCERR;
+ info._sifields._sigfault._addr = env->cr[2];
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP00_DIVZ:
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else
+#endif
+ {
+ /* division by zero */
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_INTDIV;
+ info._sifields._sigfault._addr = env->eip;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP01_DB:
+ case EXCP03_INT3:
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else
+#endif
+ {
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ if (trapnr == EXCP01_DB) {
+ info.si_code = TARGET_TRAP_BRKPT;
+ info._sifields._sigfault._addr = env->eip;
+ } else {
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ }
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP04_INTO:
+ case EXCP05_BOUND:
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else
+#endif
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP06_ILLOP:
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPN;
+ info._sifields._sigfault._addr = env->eip;
+ queue_signal(env, info.si_signo, &info);
+ break;
+#endif
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+#if 0
+ 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;
+#endif
+ default:
+ pc = env->segs[R_CS].base + env->eip;
+ fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
+ (long)pc, trapnr);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#ifdef TARGET_SPARC
+#define SPARC64_STACK_BIAS 2047
+
+//#define DEBUG_WIN
+/* WARNING: dealing with register windows _is_ complicated. More info
+ can be found at http://www.sics.se/~psm/sparcstack.html */
+static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
+{
+ index = (index + cwp * 16) % (16 * env->nwindows);
+ /* wrap handling : if cwp is on the last window, then we use the
+ registers 'after' the end */
+ if (index < 8 && env->cwp == env->nwindows - 1)
+ index += 16 * env->nwindows;
+ return index;
+}
+
+/* save the register window 'cwp1' */
+static inline void save_window_offset(CPUSPARCState *env, int cwp1)
+{
+ unsigned int i;
+ abi_ulong sp_ptr;
+
+ sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#ifdef TARGET_SPARC64
+ if (sp_ptr & 3)
+ sp_ptr += SPARC64_STACK_BIAS;
+#endif
+#if defined(DEBUG_WIN)
+ printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n",
+ sp_ptr, cwp1);
+#endif
+ for(i = 0; i < 16; i++) {
+ /* FIXME - what to do if put_user() fails? */
+ put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
+ sp_ptr += sizeof(abi_ulong);
+ }
+}
+
+static void save_window(CPUSPARCState *env)
+{
+#ifndef TARGET_SPARC64
+ unsigned int new_wim;
+ new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) &
+ ((1LL << env->nwindows) - 1);
+ save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
+ env->wim = new_wim;
+#else
+ save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
+ env->cansave++;
+ env->canrestore--;
+#endif
+}
+
+static void restore_window(CPUSPARCState *env)
+{
+#ifndef TARGET_SPARC64
+ unsigned int new_wim;
+#endif
+ unsigned int i, cwp1;
+ abi_ulong sp_ptr;
+
+#ifndef TARGET_SPARC64
+ new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
+ ((1LL << env->nwindows) - 1);
+#endif
+
+ /* restore the invalid window */
+ cwp1 = cpu_cwp_inc(env, env->cwp + 1);
+ sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#ifdef TARGET_SPARC64
+ if (sp_ptr & 3)
+ sp_ptr += SPARC64_STACK_BIAS;
+#endif
+#if defined(DEBUG_WIN)
+ printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n",
+ sp_ptr, cwp1);
+#endif
+ for(i = 0; i < 16; i++) {
+ /* FIXME - what to do if get_user() fails? */
+ get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
+ sp_ptr += sizeof(abi_ulong);
+ }
+#ifdef TARGET_SPARC64
+ env->canrestore++;
+ if (env->cleanwin < env->nwindows - 1)
+ env->cleanwin++;
+ env->cansave--;
+#else
+ env->wim = new_wim;
+#endif
+}
+
+static void flush_windows(CPUSPARCState *env)
+{
+ int offset, cwp1;
+
+ offset = 1;
+ for(;;) {
+ /* if restore would invoke restore_window(), then we can stop */
+ cwp1 = cpu_cwp_inc(env, env->cwp + offset);
+#ifndef TARGET_SPARC64
+ if (env->wim & (1 << cwp1))
+ break;
+#else
+ if (env->canrestore == 0)
+ break;
+ env->cansave++;
+ env->canrestore--;
+#endif
+ save_window_offset(env, cwp1);
+ offset++;
+ }
+ cwp1 = cpu_cwp_inc(env, env->cwp + 1);
+#ifndef TARGET_SPARC64
+ /* set wim so that restore will reload the registers */
+ env->wim = 1 << cwp1;
+#endif
+#if defined(DEBUG_WIN)
+ printf("flush_windows: nb=%d\n", offset - 1);
+#endif
+}
+
+void cpu_loop(CPUSPARCState *env)
+{
+ int trapnr, ret, syscall_nr;
+ //target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_sparc_exec (env);
+
+ switch (trapnr) {
+#ifndef TARGET_SPARC64
+ case 0x80:
+#else
+ /* FreeBSD uses 0x141 for syscalls too */
+ case 0x141:
+ if (bsd_type != target_freebsd)
+ goto badtrap;
+ case 0x100:
+#endif
+ syscall_nr = env->gregs[1];
+ if (bsd_type == target_freebsd)
+ ret = do_freebsd_syscall(env, syscall_nr,
+ env->regwptr[0], env->regwptr[1],
+ env->regwptr[2], env->regwptr[3],
+ env->regwptr[4], env->regwptr[5], 0, 0);
+ else if (bsd_type == target_netbsd)
+ ret = do_netbsd_syscall(env, syscall_nr,
+ env->regwptr[0], env->regwptr[1],
+ env->regwptr[2], env->regwptr[3],
+ env->regwptr[4], env->regwptr[5]);
+ else { //if (bsd_type == target_openbsd)
+#if defined(TARGET_SPARC64)
+ syscall_nr &= ~(TARGET_OPENBSD_SYSCALL_G7RFLAG |
+ TARGET_OPENBSD_SYSCALL_G2RFLAG);
+#endif
+ ret = do_openbsd_syscall(env, syscall_nr,
+ env->regwptr[0], env->regwptr[1],
+ env->regwptr[2], env->regwptr[3],
+ env->regwptr[4], env->regwptr[5]);
+ }
+ if ((unsigned int)ret >= (unsigned int)(-515)) {
+ ret = -ret;
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+ env->xcc |= PSR_CARRY;
+#else
+ env->psr |= PSR_CARRY;
+#endif
+ } else {
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+ env->xcc &= ~PSR_CARRY;
+#else
+ env->psr &= ~PSR_CARRY;
+#endif
+ }
+ env->regwptr[0] = ret;
+ /* next instruction */
+#if defined(TARGET_SPARC64)
+ if (bsd_type == target_openbsd &&
+ env->gregs[1] & TARGET_OPENBSD_SYSCALL_G2RFLAG) {
+ env->pc = env->gregs[2];
+ env->npc = env->pc + 4;
+ } else if (bsd_type == target_openbsd &&
+ env->gregs[1] & TARGET_OPENBSD_SYSCALL_G7RFLAG) {
+ env->pc = env->gregs[7];
+ env->npc = env->pc + 4;
+ } else {
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ }
+#else
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+#endif
+ break;
+ case 0x83: /* flush windows */
+#ifdef TARGET_ABI32
+ case 0x103:
+#endif
+ flush_windows(env);
+ /* next instruction */
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ break;
+#ifndef TARGET_SPARC64
+ case TT_WIN_OVF: /* window overflow */
+ save_window(env);
+ break;
+ case TT_WIN_UNF: /* window underflow */
+ restore_window(env);
+ break;
+ case TT_TFAULT:
+ case TT_DFAULT:
+#if 0
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->mmuregs[4];
+ queue_signal(env, info.si_signo, &info);
+ }
+#endif
+ break;
+#else
+ case TT_SPILL: /* window overflow */
+ save_window(env);
+ break;
+ case TT_FILL: /* window underflow */
+ restore_window(env);
+ break;
+ case TT_TFAULT:
+ case TT_DFAULT:
+#if 0
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ if (trapnr == TT_DFAULT)
+ info._sifields._sigfault._addr = env->dmmuregs[4];
+ else
+ info._sifields._sigfault._addr = env->tsptr->tpc;
+ //queue_signal(env, info.si_signo, &info);
+ }
+#endif
+ break;
+#endif
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+#if 0
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ //queue_signal(env, info.si_signo, &info);
+ }
+#endif
+ }
+ break;
+ default:
+#ifdef TARGET_SPARC64
+ badtrap:
+#endif
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+
+#endif
+
+static void usage(void)
+{
+ printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
+ "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
+ "BSD CPU emulator (compiled for %s emulation)\n"
+ "\n"
+ "Standard options:\n"
+ "-h print this help\n"
+ "-g port wait gdb connection to port\n"
+ "-L path set the elf interpreter prefix (default=%s)\n"
+ "-s size set the stack size in bytes (default=%ld)\n"
+ "-cpu model select CPU (-cpu ? for list)\n"
+ "-drop-ld-preload drop LD_PRELOAD for target process\n"
+ "-E var=value sets/modifies targets environment variable(s)\n"
+ "-U var unsets targets environment variable(s)\n"
+#if defined(CONFIG_USE_GUEST_BASE)
+ "-B address set guest_base address to address\n"
+#endif
+ "-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
+ "\n"
+ "Debug options:\n"
+ "-d options activate log (logfile=%s)\n"
+ "-p pagesize set the host page size to 'pagesize'\n"
+ "-singlestep always run in singlestep mode\n"
+ "-strace log system calls\n"
+ "\n"
+ "Environment variables:\n"
+ "QEMU_STRACE Print system calls and arguments similar to the\n"
+ " 'strace' program. Enable by setting to any value.\n"
+ "You can use -E and -U options to set/unset environment variables\n"
+ "for target process. It is possible to provide several variables\n"
+ "by repeating the option. For example:\n"
+ " -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
+ "Note that if you provide several changes to single variable\n"
+ "last change will stay in effect.\n"
+ ,
+ TARGET_ARCH,
+ interp_prefix,
+ x86_stack_size,
+ DEBUG_LOGFILE);
+ exit(1);
+}
+
+THREAD CPUState *thread_env;
+
+/* Assumes contents are already zeroed. */
+void init_task_state(TaskState *ts)
+{
+ int i;
+
+ ts->used = 1;
+ ts->first_free = ts->sigqueue_table;
+ for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) {
+ ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1];
+ }
+ ts->sigqueue_table[i].next = NULL;
+}
+
+int main(int argc, char **argv)
+{
+ const char *filename;
+ const char *cpu_model;
+ struct target_pt_regs regs1, *regs = &regs1;
+ struct image_info info1, *info = &info1;
+ TaskState ts1, *ts = &ts1;
+ CPUState *env;
+ int optind;
+ const char *r;
+ int gdbstub_port = 0;
+ char **target_environ, **wrk;
+ envlist_t *envlist = NULL;
+ bsd_type = target_openbsd;
+
+ if (argc <= 1)
+ usage();
+
+ /* init debug */
+ cpu_set_log_filename(DEBUG_LOGFILE);
+
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
+
+ /* add current environment into the list */
+ for (wrk = environ; *wrk != NULL; wrk++) {
+ (void) envlist_setenv(envlist, *wrk);
+ }
+
+ cpu_model = NULL;
+#if defined(cpudef_setup)
+ cpudef_setup(); /* parse cpu definitions in target config file (TBD) */
+#endif
+
+ optind = 1;
+ for(;;) {
+ if (optind >= argc)
+ break;
+ r = argv[optind];
+ if (r[0] != '-')
+ break;
+ optind++;
+ r++;
+ if (!strcmp(r, "-")) {
+ break;
+ } else if (!strcmp(r, "d")) {
+ int mask;
+ const CPULogItem *item;
+
+ if (optind >= argc)
+ break;
+
+ r = argv[optind++];
+ mask = cpu_str_to_log_mask(r);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+ } else if (!strcmp(r, "E")) {
+ r = argv[optind++];
+ if (envlist_setenv(envlist, r) != 0)
+ usage();
+ } else if (!strcmp(r, "ignore-environment")) {
+ envlist_free(envlist);
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
+ } else if (!strcmp(r, "U")) {
+ r = argv[optind++];
+ if (envlist_unsetenv(envlist, r) != 0)
+ usage();
+ } else if (!strcmp(r, "s")) {
+ r = argv[optind++];
+ x86_stack_size = strtol(r, (char **)&r, 0);
+ if (x86_stack_size <= 0)
+ usage();
+ if (*r == 'M')
+ x86_stack_size *= 1024 * 1024;
+ else if (*r == 'k' || *r == 'K')
+ x86_stack_size *= 1024;
+ } else if (!strcmp(r, "L")) {
+ interp_prefix = argv[optind++];
+ } else if (!strcmp(r, "p")) {
+ qemu_host_page_size = atoi(argv[optind++]);
+ if (qemu_host_page_size == 0 ||
+ (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
+ fprintf(stderr, "page size must be a power of two\n");
+ exit(1);
+ }
+ } else if (!strcmp(r, "g")) {
+ gdbstub_port = atoi(argv[optind++]);
+ } else if (!strcmp(r, "r")) {
+ qemu_uname_release = argv[optind++];
+ } else if (!strcmp(r, "cpu")) {
+ cpu_model = argv[optind++];
+ if (strcmp(cpu_model, "?") == 0) {
+/* XXX: implement xxx_cpu_list for targets that still miss it */
+#if defined(cpu_list)
+ cpu_list(stdout, &fprintf);
+#endif
+ exit(1);
+ }
+#if defined(CONFIG_USE_GUEST_BASE)
+ } else if (!strcmp(r, "B")) {
+ guest_base = strtol(argv[optind++], NULL, 0);
+ have_guest_base = 1;
+#endif
+ } else if (!strcmp(r, "drop-ld-preload")) {
+ (void) envlist_unsetenv(envlist, "LD_PRELOAD");
+ } else if (!strcmp(r, "bsd")) {
+ if (!strcasecmp(argv[optind], "freebsd")) {
+ bsd_type = target_freebsd;
+ } else if (!strcasecmp(argv[optind], "netbsd")) {
+ bsd_type = target_netbsd;
+ } else if (!strcasecmp(argv[optind], "openbsd")) {
+ bsd_type = target_openbsd;
+ } else {
+ usage();
+ }
+ optind++;
+ } else if (!strcmp(r, "singlestep")) {
+ singlestep = 1;
+ } else if (!strcmp(r, "strace")) {
+ do_strace = 1;
+ } else
+ {
+ usage();
+ }
+ }
+ if (optind >= argc)
+ usage();
+ filename = argv[optind];
+
+ /* Zero out regs */
+ memset(regs, 0, sizeof(struct target_pt_regs));
+
+ /* Zero out image_info */
+ memset(info, 0, sizeof(struct image_info));
+
+ /* Scan interp_prefix dir for replacement files. */
+ init_paths(interp_prefix);
+
+ if (cpu_model == NULL) {
+#if defined(TARGET_I386)
+#ifdef TARGET_X86_64
+ cpu_model = "qemu64";
+#else
+ cpu_model = "qemu32";
+#endif
+#elif defined(TARGET_SPARC)
+#ifdef TARGET_SPARC64
+ cpu_model = "TI UltraSparc II";
+#else
+ cpu_model = "Fujitsu MB86904";
+#endif
+#else
+ cpu_model = "any";
+#endif
+ }
+ cpu_exec_init_all(0);
+ /* NOTE: we need to init the CPU at this stage to get
+ qemu_host_page_size */
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
+ cpu_reset(env);
+#endif
+ thread_env = env;
+
+ if (getenv("QEMU_STRACE")) {
+ do_strace = 1;
+ }
+
+ target_environ = envlist_to_environ(envlist, NULL);
+ envlist_free(envlist);
+
+#if defined(CONFIG_USE_GUEST_BASE)
+ /*
+ * Now that page sizes are configured in cpu_init() we can do
+ * proper page alignment for guest_base.
+ */
+ guest_base = HOST_PAGE_ALIGN(guest_base);
+
+ /*
+ * Read in mmap_min_addr kernel parameter. This value is used
+ * When loading the ELF image to determine whether guest_base
+ * is needed.
+ *
+ * When user has explicitly set the quest base, we skip this
+ * test.
+ */
+ if (!have_guest_base) {
+ FILE *fp;
+
+ if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
+ unsigned long tmp;
+ if (fscanf(fp, "%lu", &tmp) == 1) {
+ mmap_min_addr = tmp;
+ qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr);
+ }
+ fclose(fp);
+ }
+ }
+#endif /* CONFIG_USE_GUEST_BASE */
+
+ if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
+ printf("Error loading %s\n", filename);
+ _exit(1);
+ }
+
+ for (wrk = target_environ; *wrk; wrk++) {
+ free(*wrk);
+ }
+
+ free(target_environ);
+
+ if (qemu_log_enabled()) {
+#if defined(CONFIG_USE_GUEST_BASE)
+ qemu_log("guest_base 0x%lx\n", guest_base);
+#endif
+ log_page_dump();
+
+ qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
+ qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code);
+ qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_code);
+ qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_data);
+ qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data);
+ qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_stack);
+ qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk);
+ qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry);
+ }
+
+ target_set_brk(info->brk);
+ syscall_init();
+ signal_init();
+
+#if defined(CONFIG_USE_GUEST_BASE)
+ /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
+ generating the prologue until now so that the prologue can take
+ the real value of GUEST_BASE into account. */
+ tcg_prologue_init(&tcg_ctx);
+#endif
+
+ /* build Task State */
+ memset(ts, 0, sizeof(TaskState));
+ init_task_state(ts);
+ ts->info = info;
+ env->opaque = ts;
+
+#if defined(TARGET_I386)
+ cpu_x86_set_cpl(env, 3);
+
+ env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
+ env->hflags |= HF_PE_MASK;
+ if (env->cpuid_features & CPUID_SSE) {
+ env->cr[4] |= CR4_OSFXSR_MASK;
+ env->hflags |= HF_OSFXSR_MASK;
+ }
+#ifndef TARGET_ABI32
+ /* enable 64 bit mode if possible */
+ if (!(env->cpuid_ext2_features & CPUID_EXT2_LM)) {
+ fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
+ exit(1);
+ }
+ env->cr[4] |= CR4_PAE_MASK;
+ env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
+ env->hflags |= HF_LMA_MASK;
+#endif
+
+ /* flags setup : we activate the IRQs by default as in user mode */
+ env->eflags |= IF_MASK;
+
+ /* linux register setup */
+#ifndef TARGET_ABI32
+ env->regs[R_EAX] = regs->rax;
+ env->regs[R_EBX] = regs->rbx;
+ env->regs[R_ECX] = regs->rcx;
+ env->regs[R_EDX] = regs->rdx;
+ env->regs[R_ESI] = regs->rsi;
+ env->regs[R_EDI] = regs->rdi;
+ env->regs[R_EBP] = regs->rbp;
+ env->regs[R_ESP] = regs->rsp;
+ env->eip = regs->rip;
+#else
+ env->regs[R_EAX] = regs->eax;
+ env->regs[R_EBX] = regs->ebx;
+ env->regs[R_ECX] = regs->ecx;
+ env->regs[R_EDX] = regs->edx;
+ env->regs[R_ESI] = regs->esi;
+ env->regs[R_EDI] = regs->edi;
+ env->regs[R_EBP] = regs->ebp;
+ env->regs[R_ESP] = regs->esp;
+ env->eip = regs->eip;
+#endif
+
+ /* linux interrupt setup */
+#ifndef TARGET_ABI32
+ env->idt.limit = 511;
+#else
+ env->idt.limit = 255;
+#endif
+ env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1),
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ idt_table = g2h(env->idt.base);
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 0);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+ set_idt(0x80, 3);
+
+ /* linux segment setup */
+ {
+ uint64_t *gdt_table;
+ env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
+ gdt_table = g2h(env->gdt.base);
+#ifdef TARGET_ABI32
+ write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+#else
+ /* 64 bit code segment */
+ write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ DESC_L_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+#endif
+ write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+ }
+
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+#ifdef TARGET_ABI32
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_FS, __USER_DS);
+ cpu_x86_load_seg(env, R_GS, __USER_DS);
+ /* This hack makes Wine work... */
+ env->segs[R_FS].selector = 0;
+#else
+ cpu_x86_load_seg(env, R_DS, 0);
+ cpu_x86_load_seg(env, R_ES, 0);
+ cpu_x86_load_seg(env, R_FS, 0);
+ cpu_x86_load_seg(env, R_GS, 0);
+#endif
+#elif defined(TARGET_SPARC)
+ {
+ int i;
+ env->pc = regs->pc;
+ env->npc = regs->npc;
+ env->y = regs->y;
+ for(i = 0; i < 8; i++)
+ env->gregs[i] = regs->u_regs[i];
+ for(i = 0; i < 8; i++)
+ env->regwptr[i] = regs->u_regs[i + 8];
+ }
+#else
+#error unsupported target CPU
+#endif
+
+ if (gdbstub_port) {
+ gdbserver_start (gdbstub_port);
+ gdb_handlesig(env, 0);
+ }
+ cpu_loop(env);
+ /* never exits */
+ return 0;
+}
diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c
new file mode 100644
index 0000000..207c774
--- /dev/null
+++ b/bsd-user/mmap.c
@@ -0,0 +1,559 @@
+/*
+ * mmap support for qemu
+ *
+ * Copyright (c) 2003 - 2008 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+#include "bsd-mman.h"
+
+//#define DEBUG_MMAP
+
+#if defined(CONFIG_USE_NPTL)
+pthread_mutex_t mmap_mutex;
+static int __thread mmap_lock_count;
+
+void mmap_lock(void)
+{
+ if (mmap_lock_count++ == 0) {
+ pthread_mutex_lock(&mmap_mutex);
+ }
+}
+
+void mmap_unlock(void)
+{
+ if (--mmap_lock_count == 0) {
+ pthread_mutex_unlock(&mmap_mutex);
+ }
+}
+
+/* Grab lock to make sure things are in a consistent state after fork(). */
+void mmap_fork_start(void)
+{
+ if (mmap_lock_count)
+ abort();
+ pthread_mutex_lock(&mmap_mutex);
+}
+
+void mmap_fork_end(int child)
+{
+ if (child)
+ pthread_mutex_init(&mmap_mutex, NULL);
+ else
+ pthread_mutex_unlock(&mmap_mutex);
+}
+#else
+/* We aren't threadsafe to start with, so no need to worry about locking. */
+void mmap_lock(void)
+{
+}
+
+void mmap_unlock(void)
+{
+}
+#endif
+
+void *qemu_vmalloc(size_t size)
+{
+ void *p;
+ mmap_lock();
+ /* Use map and mark the pages as used. */
+ p = mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (h2g_valid(p)) {
+ /* Allocated region overlaps guest address space.
+ This may recurse. */
+ abi_ulong addr = h2g(p);
+ page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size),
+ PAGE_RESERVED);
+ }
+
+ mmap_unlock();
+ return p;
+}
+
+void *qemu_malloc(size_t size)
+{
+ char * p;
+ size += 16;
+ p = qemu_vmalloc(size);
+ *(size_t *)p = size;
+ return p + 16;
+}
+
+/* We use map, which is always zero initialized. */
+void * qemu_mallocz(size_t size)
+{
+ return qemu_malloc(size);
+}
+
+void qemu_free(void *ptr)
+{
+ /* FIXME: We should unmark the reserved pages here. However this gets
+ complicated when one target page spans multiple host pages, so we
+ don't bother. */
+ size_t *p;
+ p = (size_t *)((char *)ptr - 16);
+ munmap(p, *p);
+}
+
+void *qemu_realloc(void *ptr, size_t size)
+{
+ size_t old_size, copy;
+ void *new_ptr;
+
+ if (!ptr)
+ return qemu_malloc(size);
+ old_size = *(size_t *)((char *)ptr - 16);
+ copy = old_size < size ? old_size : size;
+ new_ptr = qemu_malloc(size);
+ memcpy(new_ptr, ptr, copy);
+ qemu_free(ptr);
+ return new_ptr;
+}
+
+/* NOTE: all the constants are the HOST ones, but addresses are target. */
+int target_mprotect(abi_ulong start, abi_ulong len, int prot)
+{
+ abi_ulong end, host_start, host_end, addr;
+ int prot1, ret;
+
+#ifdef DEBUG_MMAP
+ printf("mprotect: start=0x" TARGET_FMT_lx
+ " len=0x" TARGET_FMT_lx " prot=%c%c%c\n", start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+#endif
+
+ if ((start & ~TARGET_PAGE_MASK) != 0)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
+ if (len == 0)
+ return 0;
+
+ mmap_lock();
+ host_start = start & qemu_host_page_mask;
+ host_end = HOST_PAGE_ALIGN(end);
+ if (start > host_start) {
+ /* handle host page containing start */
+ prot1 = prot;
+ for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ if (host_end == host_start + qemu_host_page_size) {
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ end = host_end;
+ }
+ ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
+ if (ret != 0)
+ goto error;
+ host_start += qemu_host_page_size;
+ }
+ if (end < host_end) {
+ prot1 = prot;
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
+ prot1 & PAGE_BITS);
+ if (ret != 0)
+ goto error;
+ host_end -= qemu_host_page_size;
+ }
+
+ /* handle the pages in the middle */
+ if (host_start < host_end) {
+ ret = mprotect(g2h(host_start), host_end - host_start, prot);
+ if (ret != 0)
+ goto error;
+ }
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ mmap_unlock();
+ return 0;
+error:
+ mmap_unlock();
+ return ret;
+}
+
+/* map an incomplete host page */
+static int mmap_frag(abi_ulong real_start,
+ abi_ulong start, abi_ulong end,
+ int prot, int flags, int fd, abi_ulong offset)
+{
+ abi_ulong real_end, addr;
+ void *host_start;
+ int prot1, prot_new;
+
+ real_end = real_start + qemu_host_page_size;
+ host_start = g2h(real_start);
+
+ /* get the protection of the target pages outside the mapping */
+ prot1 = 0;
+ for(addr = real_start; addr < real_end; addr++) {
+ if (addr < start || addr >= end)
+ prot1 |= page_get_flags(addr);
+ }
+
+ if (prot1 == 0) {
+ /* no page was there, so we allocate one */
+ void *p = mmap(host_start, qemu_host_page_size, prot,
+ flags | MAP_ANON, -1, 0);
+ if (p == MAP_FAILED)
+ return -1;
+ prot1 = prot;
+ }
+ prot1 &= PAGE_BITS;
+
+ prot_new = prot | prot1;
+ if (!(flags & MAP_ANON)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+ if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
+ (prot & PROT_WRITE))
+ return -1;
+
+ /* adjust protection to be able to read */
+ if (!(prot1 & PROT_WRITE))
+ mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
+
+ /* read the corresponding file data */
+ pread(fd, g2h(start), end - start, offset);
+
+ /* put final protection */
+ if (prot_new != (prot1 | PROT_WRITE))
+ mprotect(host_start, qemu_host_page_size, prot_new);
+ } else {
+ /* just update the protection */
+ if (prot_new != prot1) {
+ mprotect(host_start, qemu_host_page_size, prot_new);
+ }
+ }
+ return 0;
+}
+
+#if defined(__CYGWIN__)
+/* Cygwin doesn't have a whole lot of address space. */
+static abi_ulong mmap_next_start = 0x18000000;
+#else
+static abi_ulong mmap_next_start = 0x40000000;
+#endif
+
+unsigned long last_brk;
+
+/* find a free memory area of size 'size'. The search starts at
+ 'start'. If 'start' == 0, then a default start address is used.
+ Return -1 if error.
+*/
+/* page_init() marks pages used by the host as reserved to be sure not
+ to use them. */
+static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
+{
+ abi_ulong addr, addr1, addr_start;
+ int prot;
+ unsigned long new_brk;
+
+ new_brk = (unsigned long)sbrk(0);
+ if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
+ /* This is a hack to catch the host allocating memory with brk().
+ If it uses mmap then we loose.
+ FIXME: We really want to avoid the host allocating memory in
+ the first place, and maybe leave some slack to avoid switching
+ to mmap. */
+ page_set_flags(last_brk & TARGET_PAGE_MASK,
+ TARGET_PAGE_ALIGN(new_brk),
+ PAGE_RESERVED);
+ }
+ last_brk = new_brk;
+
+ size = HOST_PAGE_ALIGN(size);
+ start = start & qemu_host_page_mask;
+ addr = start;
+ if (addr == 0)
+ addr = mmap_next_start;
+ addr_start = addr;
+ for(;;) {
+ prot = 0;
+ for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr1);
+ }
+ if (prot == 0)
+ break;
+ addr += qemu_host_page_size;
+ /* we found nothing */
+ if (addr == addr_start)
+ return (abi_ulong)-1;
+ }
+ if (start == 0)
+ mmap_next_start = addr + size;
+ return addr;
+}
+
+/* NOTE: all the constants are the HOST ones */
+abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset)
+{
+ abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
+ unsigned long host_start;
+
+ mmap_lock();
+#ifdef DEBUG_MMAP
+ {
+ printf("mmap: start=0x" TARGET_FMT_lx
+ " len=0x" TARGET_FMT_lx " prot=%c%c%c flags=",
+ start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+ if (flags & MAP_FIXED)
+ printf("MAP_FIXED ");
+ if (flags & MAP_ANON)
+ printf("MAP_ANON ");
+ switch(flags & TARGET_BSD_MAP_FLAGMASK) {
+ case MAP_PRIVATE:
+ printf("MAP_PRIVATE ");
+ break;
+ case MAP_SHARED:
+ printf("MAP_SHARED ");
+ break;
+ default:
+ printf("[MAP_FLAGMASK=0x%x] ", flags & TARGET_BSD_MAP_FLAGMASK);
+ break;
+ }
+ printf("fd=%d offset=" TARGET_FMT_lx "\n", fd, offset);
+ }
+#endif
+
+ if (offset & ~TARGET_PAGE_MASK) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ goto the_end;
+ real_start = start & qemu_host_page_mask;
+
+ if (!(flags & MAP_FIXED)) {
+ abi_ulong mmap_start;
+ void *p;
+ host_offset = offset & qemu_host_page_mask;
+ host_len = len + offset - host_offset;
+ host_len = HOST_PAGE_ALIGN(host_len);
+ mmap_start = mmap_find_vma(real_start, host_len);
+ if (mmap_start == (abi_ulong)-1) {
+ errno = ENOMEM;
+ goto fail;
+ }
+ /* Note: we prefer to control the mapping address. It is
+ especially important if qemu_host_page_size >
+ qemu_real_host_page_size */
+ p = mmap(g2h(mmap_start),
+ host_len, prot, flags | MAP_FIXED, fd, host_offset);
+ if (p == MAP_FAILED)
+ goto fail;
+ /* update start so that it points to the file position at 'offset' */
+ host_start = (unsigned long)p;
+ if (!(flags & MAP_ANON))
+ host_start += offset - host_offset;
+ start = h2g(host_start);
+ } else {
+ int flg;
+ target_ulong addr;
+
+ if (start & ~TARGET_PAGE_MASK) {
+ errno = EINVAL;
+ goto fail;
+ }
+ end = start + len;
+ real_end = HOST_PAGE_ALIGN(end);
+
+ for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ flg = page_get_flags(addr);
+ if (flg & PAGE_RESERVED) {
+ errno = ENXIO;
+ goto fail;
+ }
+ }
+
+ /* worst case: we cannot map the file because the offset is not
+ aligned, so we read it */
+ if (!(flags & MAP_ANON) &&
+ (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+ if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED &&
+ (prot & PROT_WRITE)) {
+ errno = EINVAL;
+ goto fail;
+ }
+ retaddr = target_mmap(start, len, prot | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ if (retaddr == -1)
+ goto fail;
+ pread(fd, g2h(start), len, offset);
+ if (!(prot & PROT_WRITE)) {
+ ret = target_mprotect(start, len, prot);
+ if (ret != 0) {
+ start = ret;
+ goto the_end;
+ }
+ }
+ goto the_end;
+ }
+
+ /* handle the start of the mapping */
+ if (start > real_start) {
+ if (real_end == real_start + qemu_host_page_size) {
+ /* one single host page */
+ ret = mmap_frag(real_start, start, end,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ goto fail;
+ goto the_end1;
+ }
+ ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ goto fail;
+ real_start += qemu_host_page_size;
+ }
+ /* handle the end of the mapping */
+ if (end < real_end) {
+ ret = mmap_frag(real_end - qemu_host_page_size,
+ real_end - qemu_host_page_size, real_end,
+ prot, flags, fd,
+ offset + real_end - qemu_host_page_size - start);
+ if (ret == -1)
+ goto fail;
+ real_end -= qemu_host_page_size;
+ }
+
+ /* map the middle (easier) */
+ if (real_start < real_end) {
+ void *p;
+ unsigned long offset1;
+ if (flags & MAP_ANON)
+ offset1 = 0;
+ else
+ offset1 = offset + real_start - start;
+ p = mmap(g2h(real_start), real_end - real_start,
+ prot, flags, fd, offset1);
+ if (p == MAP_FAILED)
+ goto fail;
+ }
+ }
+ the_end1:
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ the_end:
+#ifdef DEBUG_MMAP
+ printf("ret=0x" TARGET_FMT_lx "\n", start);
+ page_dump(stdout);
+ printf("\n");
+#endif
+ mmap_unlock();
+ return start;
+fail:
+ mmap_unlock();
+ return -1;
+}
+
+int target_munmap(abi_ulong start, abi_ulong len)
+{
+ abi_ulong end, real_start, real_end, addr;
+ int prot, ret;
+
+#ifdef DEBUG_MMAP
+ printf("munmap: start=0x%lx len=0x%lx\n", start, len);
+#endif
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ return -EINVAL;
+ mmap_lock();
+ end = start + len;
+ real_start = start & qemu_host_page_mask;
+ real_end = HOST_PAGE_ALIGN(end);
+
+ if (start > real_start) {
+ /* handle host page containing start */
+ prot = 0;
+ for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (real_end == real_start + qemu_host_page_size) {
+ for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ end = real_end;
+ }
+ if (prot != 0)
+ real_start += qemu_host_page_size;
+ }
+ if (end < real_end) {
+ prot = 0;
+ for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (prot != 0)
+ real_end -= qemu_host_page_size;
+ }
+
+ ret = 0;
+ /* unmap what we can */
+ if (real_start < real_end) {
+ ret = munmap(g2h(real_start), real_end - real_start);
+ }
+
+ if (ret == 0)
+ page_set_flags(start, start + len, 0);
+ mmap_unlock();
+ return ret;
+}
+
+int target_msync(abi_ulong start, abi_ulong len, int flags)
+{
+ abi_ulong end;
+
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ if (end == start)
+ return 0;
+
+ start &= qemu_host_page_mask;
+ return msync(g2h(start), end - start, flags);
+}
diff --git a/bsd-user/netbsd/strace.list b/bsd-user/netbsd/strace.list
new file mode 100644
index 0000000..5609d70
--- /dev/null
+++ b/bsd-user/netbsd/strace.list
@@ -0,0 +1,145 @@
+{ TARGET_NETBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR___syscall, "__syscall", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL },
+{ TARGET_NETBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_NETBSD_NR_acct, "acct", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_adjtime, "adjtime", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_bind, "bind", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_break, "break", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL },
+{ TARGET_NETBSD_NR_chflags, "chflags", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_NETBSD_NR_chown, "chown", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_chroot, "chroot", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_close, "close", "%s(%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_dup, "dup", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_dup2, "dup2", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_execve, "execve", NULL, print_execve, NULL },
+{ TARGET_NETBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL },
+{ TARGET_NETBSD_NR_fchdir, "fchdir", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_fchflags, "fchflags", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL },
+{ TARGET_NETBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_fcntl, "fcntl", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_flock, "flock", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_fork, "fork", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_fsync, "fsync", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_futimes, "futimes", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getegid, "getegid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_getgid, "getgid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_getgroups, "getgroups", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getitimer, "getitimer", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getpeername, "getpeername", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getpgid, "getpgid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_getpid, "getpid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_getppid, "getppid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
+{ TARGET_NETBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getrusage, "getrusage", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getsid, "getsid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getsockname, "getsockname", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_getuid, "getuid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_ioctl, "ioctl", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL },
+{ TARGET_NETBSD_NR_kevent, "kevent", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_kill, "kill", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_kqueue, "kqueue", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_ktrace, "ktrace", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_lchown, "lchown", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_lfs_bmapv, "lfs_bmapv", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_lfs_markv, "lfs_markv", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_lfs_segclean, "lfs_segclean", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_lfs_segwait, "lfs_segwait", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_NETBSD_NR_listen, "listen", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_lseek, "lseek", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_madvise, "madvise", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_mincore, "mincore", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_minherit, "minherit", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_NETBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL },
+{ TARGET_NETBSD_NR_mlock, "mlock", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_mlockall, "mlockall", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr },
+{ TARGET_NETBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_msgget, "msgget", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_munlock, "munlock", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_munlockall, "munlockall", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL },
+{ TARGET_NETBSD_NR_pathconf, "pathconf", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_pipe, "pipe", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_poll, "poll", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_pread, "pread", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_preadv, "preadv", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_profil, "profil", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_ptrace, "ptrace", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_pwrite, "pwrite", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_pwritev, "pwritev", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_quotactl, "quotactl", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_readv, "readv", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_reboot, "reboot", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_NETBSD_NR_revoke, "revoke", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_rmdir, "rmdir", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_sbrk, "sbrk", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_select, "select", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_semget, "semget", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_semop, "semop", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_sendto, "sendto", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setegid, "setegid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_seteuid, "seteuid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setgid, "setgid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setgroups, "setgroups", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setitimer, "setitimer", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setpgid, "setpgid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setpriority, "setpriority", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setregid, "setregid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setreuid, "setreuid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setsid, "setsid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_setuid, "setuid", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_shmat, "shmat", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_shmdt, "shmdt", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_shmget, "shmget", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_shutdown, "shutdown", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_socketpair, "socketpair", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_sstk, "sstk", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_swapctl, "swapctl", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_NETBSD_NR_sync, "sync", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_sysarch, "sysarch", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_syscall, "syscall", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_truncate, "truncate", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL },
+{ TARGET_NETBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL },
+{ TARGET_NETBSD_NR_unmount, "unmount", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_utimes, "utimes", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_vfork, "vfork", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_wait4, "wait4", NULL, NULL, NULL },
+{ TARGET_NETBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_NETBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL },
diff --git a/bsd-user/netbsd/syscall_nr.h b/bsd-user/netbsd/syscall_nr.h
new file mode 100644
index 0000000..2e9ab53
--- /dev/null
+++ b/bsd-user/netbsd/syscall_nr.h
@@ -0,0 +1,373 @@
+/* $NetBSD: syscall.h,v 1.215 2008/06/17 16:07:57 tsutsui Exp $ */
+
+/*
+ * System call numbers.
+ *
+ * created from NetBSD: syscalls.master,v 1.204 2008/06/17 16:05:23 tsutsui Exp
+ */
+
+#define TARGET_NETBSD_NR_syscall 0
+#define TARGET_NETBSD_NR_exit 1
+#define TARGET_NETBSD_NR_fork 2
+#define TARGET_NETBSD_NR_read 3
+#define TARGET_NETBSD_NR_write 4
+#define TARGET_NETBSD_NR_open 5
+#define TARGET_NETBSD_NR_close 6
+#define TARGET_NETBSD_NR_wait4 7
+#define TARGET_NETBSD_NR_compat_43_ocreat 8
+#define TARGET_NETBSD_NR_link 9
+#define TARGET_NETBSD_NR_unlink 10
+#define TARGET_NETBSD_NR_chdir 12
+#define TARGET_NETBSD_NR_fchdir 13
+#define TARGET_NETBSD_NR_mknod 14
+#define TARGET_NETBSD_NR_chmod 15
+#define TARGET_NETBSD_NR_chown 16
+#define TARGET_NETBSD_NR_break 17
+#define TARGET_NETBSD_NR_compat_20_getfsstat 18
+#define TARGET_NETBSD_NR_compat_43_olseek 19
+#define TARGET_NETBSD_NR_getpid 20
+#define TARGET_NETBSD_NR_getpid 20
+#define TARGET_NETBSD_NR_compat_40_mount 21
+#define TARGET_NETBSD_NR_unmount 22
+#define TARGET_NETBSD_NR_setuid 23
+#define TARGET_NETBSD_NR_getuid 24
+#define TARGET_NETBSD_NR_getuid 24
+#define TARGET_NETBSD_NR_geteuid 25
+#define TARGET_NETBSD_NR_ptrace 26
+#define TARGET_NETBSD_NR_recvmsg 27
+#define TARGET_NETBSD_NR_sendmsg 28
+#define TARGET_NETBSD_NR_recvfrom 29
+#define TARGET_NETBSD_NR_accept 30
+#define TARGET_NETBSD_NR_getpeername 31
+#define TARGET_NETBSD_NR_getsockname 32
+#define TARGET_NETBSD_NR_access 33
+#define TARGET_NETBSD_NR_chflags 34
+#define TARGET_NETBSD_NR_fchflags 35
+#define TARGET_NETBSD_NR_sync 36
+#define TARGET_NETBSD_NR_kill 37
+#define TARGET_NETBSD_NR_compat_43_stat43 38
+#define TARGET_NETBSD_NR_getppid 39
+#define TARGET_NETBSD_NR_compat_43_lstat43 40
+#define TARGET_NETBSD_NR_dup 41
+#define TARGET_NETBSD_NR_pipe 42
+#define TARGET_NETBSD_NR_getegid 43
+#define TARGET_NETBSD_NR_profil 44
+#define TARGET_NETBSD_NR_ktrace 45
+#define TARGET_NETBSD_NR_compat_13_sigaction13 46
+#define TARGET_NETBSD_NR_getgid 47
+#define TARGET_NETBSD_NR_getgid 47
+#define TARGET_NETBSD_NR_compat_13_sigprocmask13 48
+#define TARGET_NETBSD_NR___getlogin 49
+#define TARGET_NETBSD_NR___setlogin 50
+#define TARGET_NETBSD_NR_acct 51
+#define TARGET_NETBSD_NR_compat_13_sigpending13 52
+#define TARGET_NETBSD_NR_compat_13_sigaltstack13 53
+#define TARGET_NETBSD_NR_ioctl 54
+#define TARGET_NETBSD_NR_compat_12_oreboot 55
+#define TARGET_NETBSD_NR_revoke 56
+#define TARGET_NETBSD_NR_symlink 57
+#define TARGET_NETBSD_NR_readlink 58
+#define TARGET_NETBSD_NR_execve 59
+#define TARGET_NETBSD_NR_umask 60
+#define TARGET_NETBSD_NR_chroot 61
+#define TARGET_NETBSD_NR_compat_43_fstat43 62
+#define TARGET_NETBSD_NR_compat_43_ogetkerninfo 63
+#define TARGET_NETBSD_NR_compat_43_ogetpagesize 64
+#define TARGET_NETBSD_NR_compat_12_msync 65
+#define TARGET_NETBSD_NR_vfork 66
+#define TARGET_NETBSD_NR_sbrk 69
+#define TARGET_NETBSD_NR_sstk 70
+#define TARGET_NETBSD_NR_compat_43_ommap 71
+#define TARGET_NETBSD_NR_vadvise 72
+#define TARGET_NETBSD_NR_munmap 73
+#define TARGET_NETBSD_NR_mprotect 74
+#define TARGET_NETBSD_NR_madvise 75
+#define TARGET_NETBSD_NR_mincore 78
+#define TARGET_NETBSD_NR_getgroups 79
+#define TARGET_NETBSD_NR_setgroups 80
+#define TARGET_NETBSD_NR_getpgrp 81
+#define TARGET_NETBSD_NR_setpgid 82
+#define TARGET_NETBSD_NR_setitimer 83
+#define TARGET_NETBSD_NR_compat_43_owait 84
+#define TARGET_NETBSD_NR_compat_12_oswapon 85
+#define TARGET_NETBSD_NR_getitimer 86
+#define TARGET_NETBSD_NR_compat_43_ogethostname 87
+#define TARGET_NETBSD_NR_compat_43_osethostname 88
+#define TARGET_NETBSD_NR_compat_43_ogetdtablesize 89
+#define TARGET_NETBSD_NR_dup2 90
+#define TARGET_NETBSD_NR_fcntl 92
+#define TARGET_NETBSD_NR_select 93
+#define TARGET_NETBSD_NR_fsync 95
+#define TARGET_NETBSD_NR_setpriority 96
+#define TARGET_NETBSD_NR_compat_30_socket 97
+#define TARGET_NETBSD_NR_connect 98
+#define TARGET_NETBSD_NR_compat_43_oaccept 99
+#define TARGET_NETBSD_NR_getpriority 100
+#define TARGET_NETBSD_NR_compat_43_osend 101
+#define TARGET_NETBSD_NR_compat_43_orecv 102
+#define TARGET_NETBSD_NR_compat_13_sigreturn13 103
+#define TARGET_NETBSD_NR_bind 104
+#define TARGET_NETBSD_NR_setsockopt 105
+#define TARGET_NETBSD_NR_listen 106
+#define TARGET_NETBSD_NR_compat_43_osigvec 108
+#define TARGET_NETBSD_NR_compat_43_osigblock 109
+#define TARGET_NETBSD_NR_compat_43_osigsetmask 110
+#define TARGET_NETBSD_NR_compat_13_sigsuspend13 111
+#define TARGET_NETBSD_NR_compat_43_osigstack 112
+#define TARGET_NETBSD_NR_compat_43_orecvmsg 113
+#define TARGET_NETBSD_NR_compat_43_osendmsg 114
+#define TARGET_NETBSD_NR_gettimeofday 116
+#define TARGET_NETBSD_NR_getrusage 117
+#define TARGET_NETBSD_NR_getsockopt 118
+#define TARGET_NETBSD_NR_readv 120
+#define TARGET_NETBSD_NR_writev 121
+#define TARGET_NETBSD_NR_settimeofday 122
+#define TARGET_NETBSD_NR_fchown 123
+#define TARGET_NETBSD_NR_fchmod 124
+#define TARGET_NETBSD_NR_compat_43_orecvfrom 125
+#define TARGET_NETBSD_NR_setreuid 126
+#define TARGET_NETBSD_NR_setregid 127
+#define TARGET_NETBSD_NR_rename 128
+#define TARGET_NETBSD_NR_compat_43_otruncate 129
+#define TARGET_NETBSD_NR_compat_43_oftruncate 130
+#define TARGET_NETBSD_NR_flock 131
+#define TARGET_NETBSD_NR_mkfifo 132
+#define TARGET_NETBSD_NR_sendto 133
+#define TARGET_NETBSD_NR_shutdown 134
+#define TARGET_NETBSD_NR_socketpair 135
+#define TARGET_NETBSD_NR_mkdir 136
+#define TARGET_NETBSD_NR_rmdir 137
+#define TARGET_NETBSD_NR_utimes 138
+#define TARGET_NETBSD_NR_adjtime 140
+#define TARGET_NETBSD_NR_compat_43_ogetpeername 141
+#define TARGET_NETBSD_NR_compat_43_ogethostid 142
+#define TARGET_NETBSD_NR_compat_43_osethostid 143
+#define TARGET_NETBSD_NR_compat_43_ogetrlimit 144
+#define TARGET_NETBSD_NR_compat_43_osetrlimit 145
+#define TARGET_NETBSD_NR_compat_43_okillpg 146
+#define TARGET_NETBSD_NR_setsid 147
+#define TARGET_NETBSD_NR_quotactl 148
+#define TARGET_NETBSD_NR_compat_43_oquota 149
+#define TARGET_NETBSD_NR_compat_43_ogetsockname 150
+#define TARGET_NETBSD_NR_nfssvc 155
+#define TARGET_NETBSD_NR_compat_43_ogetdirentries 156
+#define TARGET_NETBSD_NR_compat_20_statfs 157
+#define TARGET_NETBSD_NR_compat_20_fstatfs 158
+#define TARGET_NETBSD_NR_compat_30_getfh 161
+#define TARGET_NETBSD_NR_compat_09_ogetdomainname 162
+#define TARGET_NETBSD_NR_compat_09_osetdomainname 163
+#define TARGET_NETBSD_NR_compat_09_ouname 164
+#define TARGET_NETBSD_NR_sysarch 165
+#define TARGET_NETBSD_NR_compat_10_osemsys 169
+#define TARGET_NETBSD_NR_compat_10_omsgsys 170
+#define TARGET_NETBSD_NR_compat_10_oshmsys 171
+#define TARGET_NETBSD_NR_pread 173
+#define TARGET_NETBSD_NR_pwrite 174
+#define TARGET_NETBSD_NR_compat_30_ntp_gettime 175
+#define TARGET_NETBSD_NR_ntp_adjtime 176
+#define TARGET_NETBSD_NR_setgid 181
+#define TARGET_NETBSD_NR_setegid 182
+#define TARGET_NETBSD_NR_seteuid 183
+#define TARGET_NETBSD_NR_lfs_bmapv 184
+#define TARGET_NETBSD_NR_lfs_markv 185
+#define TARGET_NETBSD_NR_lfs_segclean 186
+#define TARGET_NETBSD_NR_lfs_segwait 187
+#define TARGET_NETBSD_NR_compat_12_stat12 188
+#define TARGET_NETBSD_NR_compat_12_fstat12 189
+#define TARGET_NETBSD_NR_compat_12_lstat12 190
+#define TARGET_NETBSD_NR_pathconf 191
+#define TARGET_NETBSD_NR_fpathconf 192
+#define TARGET_NETBSD_NR_getrlimit 194
+#define TARGET_NETBSD_NR_setrlimit 195
+#define TARGET_NETBSD_NR_compat_12_getdirentries 196
+#define TARGET_NETBSD_NR_mmap 197
+#define TARGET_NETBSD_NR___syscall 198
+#define TARGET_NETBSD_NR_lseek 199
+#define TARGET_NETBSD_NR_truncate 200
+#define TARGET_NETBSD_NR_ftruncate 201
+#define TARGET_NETBSD_NR___sysctl 202
+#define TARGET_NETBSD_NR_mlock 203
+#define TARGET_NETBSD_NR_munlock 204
+#define TARGET_NETBSD_NR_undelete 205
+#define TARGET_NETBSD_NR_futimes 206
+#define TARGET_NETBSD_NR_getpgid 207
+#define TARGET_NETBSD_NR_reboot 208
+#define TARGET_NETBSD_NR_poll 209
+#define TARGET_NETBSD_NR_compat_14___semctl 220
+#define TARGET_NETBSD_NR_semget 221
+#define TARGET_NETBSD_NR_semop 222
+#define TARGET_NETBSD_NR_semconfig 223
+#define TARGET_NETBSD_NR_compat_14_msgctl 224
+#define TARGET_NETBSD_NR_msgget 225
+#define TARGET_NETBSD_NR_msgsnd 226
+#define TARGET_NETBSD_NR_msgrcv 227
+#define TARGET_NETBSD_NR_shmat 228
+#define TARGET_NETBSD_NR_compat_14_shmctl 229
+#define TARGET_NETBSD_NR_shmdt 230
+#define TARGET_NETBSD_NR_shmget 231
+#define TARGET_NETBSD_NR_clock_gettime 232
+#define TARGET_NETBSD_NR_clock_settime 233
+#define TARGET_NETBSD_NR_clock_getres 234
+#define TARGET_NETBSD_NR_timer_create 235
+#define TARGET_NETBSD_NR_timer_delete 236
+#define TARGET_NETBSD_NR_timer_settime 237
+#define TARGET_NETBSD_NR_timer_gettime 238
+#define TARGET_NETBSD_NR_timer_getoverrun 239
+#define TARGET_NETBSD_NR_nanosleep 240
+#define TARGET_NETBSD_NR_fdatasync 241
+#define TARGET_NETBSD_NR_mlockall 242
+#define TARGET_NETBSD_NR_munlockall 243
+#define TARGET_NETBSD_NR___sigtimedwait 244
+#define TARGET_NETBSD_NR_modctl 246
+#define TARGET_NETBSD_NR__ksem_init 247
+#define TARGET_NETBSD_NR__ksem_open 248
+#define TARGET_NETBSD_NR__ksem_unlink 249
+#define TARGET_NETBSD_NR__ksem_close 250
+#define TARGET_NETBSD_NR__ksem_post 251
+#define TARGET_NETBSD_NR__ksem_wait 252
+#define TARGET_NETBSD_NR__ksem_trywait 253
+#define TARGET_NETBSD_NR__ksem_getvalue 254
+#define TARGET_NETBSD_NR__ksem_destroy 255
+#define TARGET_NETBSD_NR_mq_open 257
+#define TARGET_NETBSD_NR_mq_close 258
+#define TARGET_NETBSD_NR_mq_unlink 259
+#define TARGET_NETBSD_NR_mq_getattr 260
+#define TARGET_NETBSD_NR_mq_setattr 261
+#define TARGET_NETBSD_NR_mq_notify 262
+#define TARGET_NETBSD_NR_mq_send 263
+#define TARGET_NETBSD_NR_mq_receive 264
+#define TARGET_NETBSD_NR_mq_timedsend 265
+#define TARGET_NETBSD_NR_mq_timedreceive 266
+#define TARGET_NETBSD_NR___posix_rename 270
+#define TARGET_NETBSD_NR_swapctl 271
+#define TARGET_NETBSD_NR_compat_30_getdents 272
+#define TARGET_NETBSD_NR_minherit 273
+#define TARGET_NETBSD_NR_lchmod 274
+#define TARGET_NETBSD_NR_lchown 275
+#define TARGET_NETBSD_NR_lutimes 276
+#define TARGET_NETBSD_NR___msync13 277
+#define TARGET_NETBSD_NR_compat_30___stat13 278
+#define TARGET_NETBSD_NR_compat_30___fstat13 279
+#define TARGET_NETBSD_NR_compat_30___lstat13 280
+#define TARGET_NETBSD_NR___sigaltstack14 281
+#define TARGET_NETBSD_NR___vfork14 282
+#define TARGET_NETBSD_NR___posix_chown 283
+#define TARGET_NETBSD_NR___posix_fchown 284
+#define TARGET_NETBSD_NR___posix_lchown 285
+#define TARGET_NETBSD_NR_getsid 286
+#define TARGET_NETBSD_NR___clone 287
+#define TARGET_NETBSD_NR_fktrace 288
+#define TARGET_NETBSD_NR_preadv 289
+#define TARGET_NETBSD_NR_pwritev 290
+#define TARGET_NETBSD_NR_compat_16___sigaction14 291
+#define TARGET_NETBSD_NR___sigpending14 292
+#define TARGET_NETBSD_NR___sigprocmask14 293
+#define TARGET_NETBSD_NR___sigsuspend14 294
+#define TARGET_NETBSD_NR_compat_16___sigreturn14 295
+#define TARGET_NETBSD_NR___getcwd 296
+#define TARGET_NETBSD_NR_fchroot 297
+#define TARGET_NETBSD_NR_compat_30_fhopen 298
+#define TARGET_NETBSD_NR_compat_30_fhstat 299
+#define TARGET_NETBSD_NR_compat_20_fhstatfs 300
+#define TARGET_NETBSD_NR_____semctl13 301
+#define TARGET_NETBSD_NR___msgctl13 302
+#define TARGET_NETBSD_NR___shmctl13 303
+#define TARGET_NETBSD_NR_lchflags 304
+#define TARGET_NETBSD_NR_issetugid 305
+#define TARGET_NETBSD_NR_utrace 306
+#define TARGET_NETBSD_NR_getcontext 307
+#define TARGET_NETBSD_NR_setcontext 308
+#define TARGET_NETBSD_NR__lwp_create 309
+#define TARGET_NETBSD_NR__lwp_exit 310
+#define TARGET_NETBSD_NR__lwp_self 311
+#define TARGET_NETBSD_NR__lwp_wait 312
+#define TARGET_NETBSD_NR__lwp_suspend 313
+#define TARGET_NETBSD_NR__lwp_continue 314
+#define TARGET_NETBSD_NR__lwp_wakeup 315
+#define TARGET_NETBSD_NR__lwp_getprivate 316
+#define TARGET_NETBSD_NR__lwp_setprivate 317
+#define TARGET_NETBSD_NR__lwp_kill 318
+#define TARGET_NETBSD_NR__lwp_detach 319
+#define TARGET_NETBSD_NR__lwp_park 320
+#define TARGET_NETBSD_NR__lwp_unpark 321
+#define TARGET_NETBSD_NR__lwp_unpark_all 322
+#define TARGET_NETBSD_NR__lwp_setname 323
+#define TARGET_NETBSD_NR__lwp_getname 324
+#define TARGET_NETBSD_NR__lwp_ctl 325
+#define TARGET_NETBSD_NR_sa_register 330
+#define TARGET_NETBSD_NR_sa_stacks 331
+#define TARGET_NETBSD_NR_sa_enable 332
+#define TARGET_NETBSD_NR_sa_setconcurrency 333
+#define TARGET_NETBSD_NR_sa_yield 334
+#define TARGET_NETBSD_NR_sa_preempt 335
+#define TARGET_NETBSD_NR_sa_unblockyield 336
+#define TARGET_NETBSD_NR___sigaction_sigtramp 340
+#define TARGET_NETBSD_NR_pmc_get_info 341
+#define TARGET_NETBSD_NR_pmc_control 342
+#define TARGET_NETBSD_NR_rasctl 343
+#define TARGET_NETBSD_NR_kqueue 344
+#define TARGET_NETBSD_NR_kevent 345
+#define TARGET_NETBSD_NR__sched_setparam 346
+#define TARGET_NETBSD_NR__sched_getparam 347
+#define TARGET_NETBSD_NR__sched_setaffinity 348
+#define TARGET_NETBSD_NR__sched_getaffinity 349
+#define TARGET_NETBSD_NR_sched_yield 350
+#define TARGET_NETBSD_NR_fsync_range 354
+#define TARGET_NETBSD_NR_uuidgen 355
+#define TARGET_NETBSD_NR_getvfsstat 356
+#define TARGET_NETBSD_NR_statvfs1 357
+#define TARGET_NETBSD_NR_fstatvfs1 358
+#define TARGET_NETBSD_NR_compat_30_fhstatvfs1 359
+#define TARGET_NETBSD_NR_extattrctl 360
+#define TARGET_NETBSD_NR_extattr_set_file 361
+#define TARGET_NETBSD_NR_extattr_get_file 362
+#define TARGET_NETBSD_NR_extattr_delete_file 363
+#define TARGET_NETBSD_NR_extattr_set_fd 364
+#define TARGET_NETBSD_NR_extattr_get_fd 365
+#define TARGET_NETBSD_NR_extattr_delete_fd 366
+#define TARGET_NETBSD_NR_extattr_set_link 367
+#define TARGET_NETBSD_NR_extattr_get_link 368
+#define TARGET_NETBSD_NR_extattr_delete_link 369
+#define TARGET_NETBSD_NR_extattr_list_fd 370
+#define TARGET_NETBSD_NR_extattr_list_file 371
+#define TARGET_NETBSD_NR_extattr_list_link 372
+#define TARGET_NETBSD_NR_pselect 373
+#define TARGET_NETBSD_NR_pollts 374
+#define TARGET_NETBSD_NR_setxattr 375
+#define TARGET_NETBSD_NR_lsetxattr 376
+#define TARGET_NETBSD_NR_fsetxattr 377
+#define TARGET_NETBSD_NR_getxattr 378
+#define TARGET_NETBSD_NR_lgetxattr 379
+#define TARGET_NETBSD_NR_fgetxattr 380
+#define TARGET_NETBSD_NR_listxattr 381
+#define TARGET_NETBSD_NR_llistxattr 382
+#define TARGET_NETBSD_NR_flistxattr 383
+#define TARGET_NETBSD_NR_removexattr 384
+#define TARGET_NETBSD_NR_lremovexattr 385
+#define TARGET_NETBSD_NR_fremovexattr 386
+#define TARGET_NETBSD_NR___stat30 387
+#define TARGET_NETBSD_NR___fstat30 388
+#define TARGET_NETBSD_NR___lstat30 389
+#define TARGET_NETBSD_NR___getdents30 390
+#define TARGET_NETBSD_NR_compat_30___fhstat30 392
+#define TARGET_NETBSD_NR___ntp_gettime30 393
+#define TARGET_NETBSD_NR___socket30 394
+#define TARGET_NETBSD_NR___getfh30 395
+#define TARGET_NETBSD_NR___fhopen40 396
+#define TARGET_NETBSD_NR___fhstatvfs140 397
+#define TARGET_NETBSD_NR___fhstat40 398
+#define TARGET_NETBSD_NR_aio_cancel 399
+#define TARGET_NETBSD_NR_aio_error 400
+#define TARGET_NETBSD_NR_aio_fsync 401
+#define TARGET_NETBSD_NR_aio_read 402
+#define TARGET_NETBSD_NR_aio_return 403
+#define TARGET_NETBSD_NR_aio_suspend 404
+#define TARGET_NETBSD_NR_aio_write 405
+#define TARGET_NETBSD_NR_lio_listio 406
+#define TARGET_NETBSD_NR___mount50 410
+#define TARGET_NETBSD_NR_mremap 411
+#define TARGET_NETBSD_NR_pset_create 412
+#define TARGET_NETBSD_NR_pset_destroy 413
+#define TARGET_NETBSD_NR_pset_assign 414
+#define TARGET_NETBSD_NR__pset_bind 415
+#define TARGET_NETBSD_NR___posix_fadvise50 416
diff --git a/bsd-user/openbsd/strace.list b/bsd-user/openbsd/strace.list
new file mode 100644
index 0000000..1f0a331
--- /dev/null
+++ b/bsd-user/openbsd/strace.list
@@ -0,0 +1,187 @@
+{ TARGET_OPENBSD_NR___getcwd, "__getcwd", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR___semctl, "__semctl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR___syscall, "__syscall", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL },
+{ TARGET_OPENBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_OPENBSD_NR_acct, "acct", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_adjfreq, "adjfreq", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_adjtime, "adjtime", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_bind, "bind", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_break, "break", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_chdir, "chdir", "%s(\"%s\")", NULL, NULL },
+{ TARGET_OPENBSD_NR_chflags, "chflags", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_chmod, "chmod", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_OPENBSD_NR_chown, "chown", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_chroot, "chroot", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_clock_getres, "clock_getres", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_clock_gettime, "clock_gettime", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_clock_settime, "clock_settime", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_close, "close", "%s(%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_closefrom, "closefrom", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_connect, "connect", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_dup, "dup", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_dup2, "dup2", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_execve, "execve", NULL, print_execve, NULL },
+{ TARGET_OPENBSD_NR_exit, "exit", "%s(%d)\n", NULL, NULL },
+{ TARGET_OPENBSD_NR_fchdir, "fchdir", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fchflags, "fchflags", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fchmod, "fchmod", "%s(%d,%#o)", NULL, NULL },
+{ TARGET_OPENBSD_NR_fchown, "fchown", "%s(\"%s\",%d,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_fcntl, "fcntl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fhopen, "fhopen", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fhstat, "fhstat", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fhstatfs, "fhstatfs", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_flock, "flock", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fork, "fork", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_fpathconf, "fpathconf", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_fstat, "fstat", "%s(%d,%p)", NULL, NULL },
+{ TARGET_OPENBSD_NR_fstatfs, "fstatfs", "%s(%d,%p)", NULL, NULL },
+{ TARGET_OPENBSD_NR_fsync, "fsync", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_futimes, "futimes", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getegid, "getegid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_getfh, "getfh", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getfsstat, "getfsstat", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getgid, "getgid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_getgroups, "getgroups", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getitimer, "getitimer", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getlogin, "getlogin", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getpeereid, "getpeereid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getpeername, "getpeername", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getpgid, "getpgid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getpgrp, "getpgrp", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_getpid, "getpid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_getppid, "getppid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
+{ TARGET_OPENBSD_NR_getresgid, "getresgid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getresuid, "getresuid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getrlimit, "getrlimit", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getrusage, "getrusage", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getsid, "getsid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getsockname, "getsockname", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getsockopt, "getsockopt", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getthrid, "getthrid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_gettimeofday, "gettimeofday", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_getuid, "getuid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_ioctl, "ioctl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_issetugid, "issetugid", "%s()", NULL, NULL },
+{ TARGET_OPENBSD_NR_kevent, "kevent", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_kill, "kill", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_kqueue, "kqueue", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_ktrace, "ktrace", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lchown, "lchown", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lfs_bmapv, "lfs_bmapv", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lfs_markv, "lfs_markv", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lfs_segclean, "lfs_segclean", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lfs_segwait, "lfs_segwait", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_link, "link", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_OPENBSD_NR_listen, "listen", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lseek, "lseek", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_lstat, "lstat", "%s(\"%s\",%p)", NULL, NULL },
+{ TARGET_OPENBSD_NR_madvise, "madvise", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_mincore, "mincore", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_minherit, "minherit", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_mkdir, "mkdir", "%s(\"%s\",%#o)", NULL, NULL },
+{ TARGET_OPENBSD_NR_mkfifo, "mkfifo", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_mknod, "mknod", "%s(\"%s\",%#o,%#x)", NULL, NULL },
+{ TARGET_OPENBSD_NR_mlock, "mlock", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_mlockall, "mlockall", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_mmap, "mmap", NULL, NULL, print_syscall_ret_addr },
+{ TARGET_OPENBSD_NR_mount, "mount", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_mprotect, "mprotect", "%s(%#x,%#x,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_mquery, "mquery", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_msgctl, "msgctl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_msgget, "msgget", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_msgrcv, "msgrcv", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_msgsnd, "msgsnd", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_msync, "msync", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_munlock, "munlock", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_munlockall, "munlockall", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_munmap, "munmap", "%s(%p,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_nanosleep, "nanosleep", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_nfssvc, "nfssvc", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_open, "open", "%s(\"%s\",%#x,%#o)", NULL, NULL },
+{ TARGET_OPENBSD_NR_opipe, "opipe", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_osigaltstack, "osigaltstack", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_pathconf, "pathconf", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_pipe, "pipe", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_poll, "poll", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_pread, "pread", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_preadv, "preadv", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_profil, "profil", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_ptrace, "ptrace", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_pwrite, "pwrite", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_pwritev, "pwritev", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_quotactl, "quotactl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_read, "read", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_readlink, "readlink", "%s(\"%s\",%p,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_readv, "readv", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_reboot, "reboot", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_recvfrom, "recvfrom", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_recvmsg, "recvmsg", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_rename, "rename", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_OPENBSD_NR_revoke, "revoke", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_rfork, "rfork", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_rmdir, "rmdir", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sbrk, "sbrk", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sched_yield, "sched_yield", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_select, "select", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_semget, "semget", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_semop, "semop", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sendmsg, "sendmsg", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sendto, "sendto", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setegid, "setegid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_seteuid, "seteuid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setgid, "setgid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setgroups, "setgroups", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setitimer, "setitimer", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setlogin, "setlogin", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setpgid, "setpgid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setpriority, "setpriority", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setregid, "setregid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setresgid, "setresgid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setresuid, "setresuid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setreuid, "setreuid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setrlimit, "setrlimit", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setsid, "setsid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setsockopt, "setsockopt", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_settimeofday, "settimeofday", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_setuid, "setuid", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_shmat, "shmat", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_shmctl, "shmctl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_shmdt, "shmdt", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_shmget, "shmget", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_shutdown, "shutdown", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sigaction, "sigaction", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sigaltstack, "sigaltstack", "%s(%p,%p)", NULL, NULL },
+{ TARGET_OPENBSD_NR_sigpending, "sigpending", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sigprocmask, "sigprocmask", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sigreturn, "sigreturn", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sigsuspend, "sigsuspend", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_socket, "socket", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_socketpair, "socketpair", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sstk, "sstk", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_stat, "stat", "%s(\"%s\",%p)", NULL, NULL },
+{ TARGET_OPENBSD_NR_statfs, "statfs", "%s(\"%s\",%p)", NULL, NULL },
+{ TARGET_OPENBSD_NR_swapctl, "swapctl", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_symlink, "symlink", "%s(\"%s\",\"%s\")", NULL, NULL },
+{ TARGET_OPENBSD_NR_sync, "sync", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_sysarch, "sysarch", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_syscall, "syscall", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_threxit, "threxit", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_thrsigdivert, "thrsigdivert", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_thrsleep, "thrsleep", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_thrwakeup, "thrwakeup", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_truncate, "truncate", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_umask, "umask", "%s(%#o)", NULL, NULL },
+{ TARGET_OPENBSD_NR_unlink, "unlink", "%s(\"%s\")", NULL, NULL },
+{ TARGET_OPENBSD_NR_unmount, "unmount", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_utimes, "utimes", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_vfork, "vfork", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_wait4, "wait4", NULL, NULL, NULL },
+{ TARGET_OPENBSD_NR_write, "write", "%s(%d,%#x,%d)", NULL, NULL },
+{ TARGET_OPENBSD_NR_writev, "writev", "%s(%d,%p,%#x)", NULL, NULL },
+{ TARGET_OPENBSD_NR_xfspioctl, "xfspioctl", NULL, NULL, NULL },
diff --git a/bsd-user/openbsd/syscall_nr.h b/bsd-user/openbsd/syscall_nr.h
new file mode 100644
index 0000000..dececfd
--- /dev/null
+++ b/bsd-user/openbsd/syscall_nr.h
@@ -0,0 +1,225 @@
+/* $OpenBSD: syscall.h,v 1.101 2008/03/16 19:43:41 otto Exp $ */
+
+/*
+ * System call numbers.
+ *
+ * created from; OpenBSD: syscalls.master,v 1.90 2008/03/16 19:42:57 otto Exp
+ */
+
+#define TARGET_OPENBSD_NR_syscall 0
+#define TARGET_OPENBSD_NR_exit 1
+#define TARGET_OPENBSD_NR_fork 2
+#define TARGET_OPENBSD_NR_read 3
+#define TARGET_OPENBSD_NR_write 4
+#define TARGET_OPENBSD_NR_open 5
+#define TARGET_OPENBSD_NR_close 6
+#define TARGET_OPENBSD_NR_wait4 7
+#define TARGET_OPENBSD_NR_link 9
+#define TARGET_OPENBSD_NR_unlink 10
+#define TARGET_OPENBSD_NR_chdir 12
+#define TARGET_OPENBSD_NR_fchdir 13
+#define TARGET_OPENBSD_NR_mknod 14
+#define TARGET_OPENBSD_NR_chmod 15
+#define TARGET_OPENBSD_NR_chown 16
+#define TARGET_OPENBSD_NR_break 17
+#define TARGET_OPENBSD_NR_getpid 20
+#define TARGET_OPENBSD_NR_mount 21
+#define TARGET_OPENBSD_NR_unmount 22
+#define TARGET_OPENBSD_NR_setuid 23
+#define TARGET_OPENBSD_NR_getuid 24
+#define TARGET_OPENBSD_NR_geteuid 25
+#define TARGET_OPENBSD_NR_ptrace 26
+#define TARGET_OPENBSD_NR_recvmsg 27
+#define TARGET_OPENBSD_NR_sendmsg 28
+#define TARGET_OPENBSD_NR_recvfrom 29
+#define TARGET_OPENBSD_NR_accept 30
+#define TARGET_OPENBSD_NR_getpeername 31
+#define TARGET_OPENBSD_NR_getsockname 32
+#define TARGET_OPENBSD_NR_access 33
+#define TARGET_OPENBSD_NR_chflags 34
+#define TARGET_OPENBSD_NR_fchflags 35
+#define TARGET_OPENBSD_NR_sync 36
+#define TARGET_OPENBSD_NR_kill 37
+#define TARGET_OPENBSD_NR_getppid 39
+#define TARGET_OPENBSD_NR_dup 41
+#define TARGET_OPENBSD_NR_opipe 42
+#define TARGET_OPENBSD_NR_getegid 43
+#define TARGET_OPENBSD_NR_profil 44
+#define TARGET_OPENBSD_NR_ktrace 45
+#define TARGET_OPENBSD_NR_sigaction 46
+#define TARGET_OPENBSD_NR_getgid 47
+#define TARGET_OPENBSD_NR_sigprocmask 48
+#define TARGET_OPENBSD_NR_getlogin 49
+#define TARGET_OPENBSD_NR_setlogin 50
+#define TARGET_OPENBSD_NR_acct 51
+#define TARGET_OPENBSD_NR_sigpending 52
+#define TARGET_OPENBSD_NR_osigaltstack 53
+#define TARGET_OPENBSD_NR_ioctl 54
+#define TARGET_OPENBSD_NR_reboot 55
+#define TARGET_OPENBSD_NR_revoke 56
+#define TARGET_OPENBSD_NR_symlink 57
+#define TARGET_OPENBSD_NR_readlink 58
+#define TARGET_OPENBSD_NR_execve 59
+#define TARGET_OPENBSD_NR_umask 60
+#define TARGET_OPENBSD_NR_chroot 61
+#define TARGET_OPENBSD_NR_vfork 66
+#define TARGET_OPENBSD_NR_sbrk 69
+#define TARGET_OPENBSD_NR_sstk 70
+#define TARGET_OPENBSD_NR_munmap 73
+#define TARGET_OPENBSD_NR_mprotect 74
+#define TARGET_OPENBSD_NR_madvise 75
+#define TARGET_OPENBSD_NR_mincore 78
+#define TARGET_OPENBSD_NR_getgroups 79
+#define TARGET_OPENBSD_NR_setgroups 80
+#define TARGET_OPENBSD_NR_getpgrp 81
+#define TARGET_OPENBSD_NR_setpgid 82
+#define TARGET_OPENBSD_NR_setitimer 83
+#define TARGET_OPENBSD_NR_getitimer 86
+#define TARGET_OPENBSD_NR_dup2 90
+#define TARGET_OPENBSD_NR_fcntl 92
+#define TARGET_OPENBSD_NR_select 93
+#define TARGET_OPENBSD_NR_fsync 95
+#define TARGET_OPENBSD_NR_setpriority 96
+#define TARGET_OPENBSD_NR_socket 97
+#define TARGET_OPENBSD_NR_connect 98
+#define TARGET_OPENBSD_NR_getpriority 100
+#define TARGET_OPENBSD_NR_sigreturn 103
+#define TARGET_OPENBSD_NR_bind 104
+#define TARGET_OPENBSD_NR_setsockopt 105
+#define TARGET_OPENBSD_NR_listen 106
+#define TARGET_OPENBSD_NR_sigsuspend 111
+#define TARGET_OPENBSD_NR_gettimeofday 116
+#define TARGET_OPENBSD_NR_getrusage 117
+#define TARGET_OPENBSD_NR_getsockopt 118
+#define TARGET_OPENBSD_NR_readv 120
+#define TARGET_OPENBSD_NR_writev 121
+#define TARGET_OPENBSD_NR_settimeofday 122
+#define TARGET_OPENBSD_NR_fchown 123
+#define TARGET_OPENBSD_NR_fchmod 124
+#define TARGET_OPENBSD_NR_setreuid 126
+#define TARGET_OPENBSD_NR_setregid 127
+#define TARGET_OPENBSD_NR_rename 128
+#define TARGET_OPENBSD_NR_flock 131
+#define TARGET_OPENBSD_NR_mkfifo 132
+#define TARGET_OPENBSD_NR_sendto 133
+#define TARGET_OPENBSD_NR_shutdown 134
+#define TARGET_OPENBSD_NR_socketpair 135
+#define TARGET_OPENBSD_NR_mkdir 136
+#define TARGET_OPENBSD_NR_rmdir 137
+#define TARGET_OPENBSD_NR_utimes 138
+#define TARGET_OPENBSD_NR_adjtime 140
+#define TARGET_OPENBSD_NR_setsid 147
+#define TARGET_OPENBSD_NR_quotactl 148
+#define TARGET_OPENBSD_NR_nfssvc 155
+#define TARGET_OPENBSD_NR_getfh 161
+#define TARGET_OPENBSD_NR_sysarch 165
+#define TARGET_OPENBSD_NR_pread 173
+#define TARGET_OPENBSD_NR_pwrite 174
+#define TARGET_OPENBSD_NR_setgid 181
+#define TARGET_OPENBSD_NR_setegid 182
+#define TARGET_OPENBSD_NR_seteuid 183
+#define TARGET_OPENBSD_NR_lfs_bmapv 184
+#define TARGET_OPENBSD_NR_lfs_markv 185
+#define TARGET_OPENBSD_NR_lfs_segclean 186
+#define TARGET_OPENBSD_NR_lfs_segwait 187
+#define TARGET_OPENBSD_NR_pathconf 191
+#define TARGET_OPENBSD_NR_fpathconf 192
+#define TARGET_OPENBSD_NR_swapctl 193
+#define TARGET_OPENBSD_NR_getrlimit 194
+#define TARGET_OPENBSD_NR_setrlimit 195
+#define TARGET_OPENBSD_NR_getdirentries 196
+#define TARGET_OPENBSD_NR_mmap 197
+#define TARGET_OPENBSD_NR___syscall 198
+#define TARGET_OPENBSD_NR_lseek 199
+#define TARGET_OPENBSD_NR_truncate 200
+#define TARGET_OPENBSD_NR_ftruncate 201
+#define TARGET_OPENBSD_NR___sysctl 202
+#define TARGET_OPENBSD_NR_mlock 203
+#define TARGET_OPENBSD_NR_munlock 204
+#define TARGET_OPENBSD_NR_futimes 206
+#define TARGET_OPENBSD_NR_getpgid 207
+#define TARGET_OPENBSD_NR_xfspioctl 208
+#define TARGET_OPENBSD_NR_semget 221
+#define TARGET_OPENBSD_NR_msgget 225
+#define TARGET_OPENBSD_NR_msgsnd 226
+#define TARGET_OPENBSD_NR_msgrcv 227
+#define TARGET_OPENBSD_NR_shmat 228
+#define TARGET_OPENBSD_NR_shmdt 230
+#define TARGET_OPENBSD_NR_clock_gettime 232
+#define TARGET_OPENBSD_NR_clock_settime 233
+#define TARGET_OPENBSD_NR_clock_getres 234
+#define TARGET_OPENBSD_NR_nanosleep 240
+#define TARGET_OPENBSD_NR_minherit 250
+#define TARGET_OPENBSD_NR_rfork 251
+#define TARGET_OPENBSD_NR_poll 252
+#define TARGET_OPENBSD_NR_issetugid 253
+#define TARGET_OPENBSD_NR_lchown 254
+#define TARGET_OPENBSD_NR_getsid 255
+#define TARGET_OPENBSD_NR_msync 256
+#define TARGET_OPENBSD_NR_pipe 263
+#define TARGET_OPENBSD_NR_fhopen 264
+#define TARGET_OPENBSD_NR_preadv 267
+#define TARGET_OPENBSD_NR_pwritev 268
+#define TARGET_OPENBSD_NR_kqueue 269
+#define TARGET_OPENBSD_NR_kevent 270
+#define TARGET_OPENBSD_NR_mlockall 271
+#define TARGET_OPENBSD_NR_munlockall 272
+#define TARGET_OPENBSD_NR_getpeereid 273
+#define TARGET_OPENBSD_NR_getresuid 281
+#define TARGET_OPENBSD_NR_setresuid 282
+#define TARGET_OPENBSD_NR_getresgid 283
+#define TARGET_OPENBSD_NR_setresgid 284
+#define TARGET_OPENBSD_NR_mquery 286
+#define TARGET_OPENBSD_NR_closefrom 287
+#define TARGET_OPENBSD_NR_sigaltstack 288
+#define TARGET_OPENBSD_NR_shmget 289
+#define TARGET_OPENBSD_NR_semop 290
+#define TARGET_OPENBSD_NR_stat 291
+#define TARGET_OPENBSD_NR_fstat 292
+#define TARGET_OPENBSD_NR_lstat 293
+#define TARGET_OPENBSD_NR_fhstat 294
+#define TARGET_OPENBSD_NR___semctl 295
+#define TARGET_OPENBSD_NR_shmctl 296
+#define TARGET_OPENBSD_NR_msgctl 297
+#define TARGET_OPENBSD_NR_sched_yield 298
+#define TARGET_OPENBSD_NR_getthrid 299
+#define TARGET_OPENBSD_NR_thrsleep 300
+#define TARGET_OPENBSD_NR_thrwakeup 301
+#define TARGET_OPENBSD_NR_threxit 302
+#define TARGET_OPENBSD_NR_thrsigdivert 303
+#define TARGET_OPENBSD_NR___getcwd 304
+#define TARGET_OPENBSD_NR_adjfreq 305
+#define TARGET_OPENBSD_NR_getfsstat 306
+#define TARGET_OPENBSD_NR_statfs 307
+#define TARGET_OPENBSD_NR_fstatfs 308
+#define TARGET_OPENBSD_NR_fhstatfs 309
+
+/* syscall flags from machine/trap.h */
+
+/* $OpenBSD: trap.h,v 1.4 2008/07/04 22:04:37 kettenis Exp $ */
+/* $NetBSD: trap.h,v 1.4 1999/06/07 05:28:04 eeh Exp $ */
+
+/*
+ * Copyright (c) 1996-1999 Eduardo Horvath
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#define TARGET_OPENBSD_SYSCALL_G2RFLAG 0x400 /* on success, return to %g2 rather than npc */
+#define TARGET_OPENBSD_SYSCALL_G7RFLAG 0x800 /* use %g7 as above (deprecated) */
diff --git a/bsd-user/qemu-types.h b/bsd-user/qemu-types.h
new file mode 100644
index 0000000..1adda9f
--- /dev/null
+++ b/bsd-user/qemu-types.h
@@ -0,0 +1,24 @@
+#ifndef QEMU_TYPES_H
+#define QEMU_TYPES_H
+#include "cpu.h"
+
+#ifdef TARGET_ABI32
+typedef uint32_t abi_ulong;
+typedef int32_t abi_long;
+#define TARGET_ABI_FMT_lx "%08x"
+#define TARGET_ABI_FMT_ld "%d"
+#define TARGET_ABI_FMT_lu "%u"
+#define TARGET_ABI_BITS 32
+#else
+typedef target_ulong abi_ulong;
+typedef target_long abi_long;
+#define TARGET_ABI_FMT_lx TARGET_FMT_lx
+#define TARGET_ABI_FMT_ld TARGET_FMT_ld
+#define TARGET_ABI_FMT_lu TARGET_FMT_lu
+#define TARGET_ABI_BITS TARGET_LONG_BITS
+/* for consistency, define ABI32 too */
+#if TARGET_ABI_BITS == 32
+#define TARGET_ABI32 1
+#endif
+#endif
+#endif
diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
new file mode 100644
index 0000000..e343894
--- /dev/null
+++ b/bsd-user/qemu.h
@@ -0,0 +1,394 @@
+#ifndef QEMU_H
+#define QEMU_H
+
+#include <signal.h>
+#include <string.h>
+
+#include "cpu.h"
+
+#undef DEBUG_REMAP
+#ifdef DEBUG_REMAP
+#include <stdlib.h>
+#endif /* DEBUG_REMAP */
+
+#include "qemu-types.h"
+
+enum BSDType {
+ target_freebsd,
+ target_netbsd,
+ target_openbsd,
+};
+extern enum BSDType bsd_type;
+
+#include "syscall_defs.h"
+#include "syscall.h"
+#include "target_signal.h"
+#include "gdbstub.h"
+
+#if defined(CONFIG_USE_NPTL)
+#define THREAD __thread
+#else
+#define THREAD
+#endif
+
+/* This struct is used to hold certain information about the image.
+ * Basically, it replicates in user space what would be certain
+ * task_struct fields in the kernel
+ */
+struct image_info {
+ abi_ulong load_addr;
+ abi_ulong start_code;
+ abi_ulong end_code;
+ abi_ulong start_data;
+ abi_ulong end_data;
+ abi_ulong start_brk;
+ abi_ulong brk;
+ abi_ulong start_mmap;
+ abi_ulong mmap;
+ abi_ulong rss;
+ abi_ulong start_stack;
+ abi_ulong entry;
+ abi_ulong code_offset;
+ abi_ulong data_offset;
+ int personality;
+};
+
+#define MAX_SIGQUEUE_SIZE 1024
+
+struct sigqueue {
+ struct sigqueue *next;
+ //target_siginfo_t info;
+};
+
+struct emulated_sigtable {
+ int pending; /* true if signal is pending */
+ struct sigqueue *first;
+ struct sigqueue info; /* in order to always have memory for the
+ first signal, we put it here */
+};
+
+/* NOTE: we force a big alignment so that the stack stored after is
+ aligned too */
+typedef struct TaskState {
+ struct TaskState *next;
+ int used; /* non zero if used */
+ struct image_info *info;
+
+ struct emulated_sigtable sigtab[TARGET_NSIG];
+ struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
+ struct sigqueue *first_free; /* first free siginfo queue entry */
+ int signal_pending; /* non zero if a signal may be pending */
+
+ uint8_t stack[0];
+} __attribute__((aligned(16))) TaskState;
+
+void init_task_state(TaskState *ts);
+extern const char *qemu_uname_release;
+#if defined(CONFIG_USE_GUEST_BASE)
+extern unsigned long mmap_min_addr;
+#endif
+
+/* ??? See if we can avoid exposing so much of the loader internals. */
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB w/4KB pages!
+ */
+#define MAX_ARG_PAGES 32
+
+/*
+ * This structure is used to hold the arguments that are
+ * used when loading binaries.
+ */
+struct linux_binprm {
+ char buf[128];
+ void *page[MAX_ARG_PAGES];
+ abi_ulong p;
+ int fd;
+ int e_uid, e_gid;
+ int argc, envc;
+ char **argv;
+ char **envp;
+ char * filename; /* Name of binary */
+};
+
+void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);
+abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
+ abi_ulong stringp, int push_ptr);
+int loader_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs, struct image_info *infop);
+
+int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info);
+int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info);
+
+abi_long memcpy_to_target(abi_ulong dest, const void *src,
+ unsigned long len);
+void target_set_brk(abi_ulong new_brk);
+abi_long do_brk(abi_ulong new_brk);
+void syscall_init(void);
+abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6, abi_long arg7,
+ abi_long arg8);
+abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6);
+abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6);
+void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+extern THREAD CPUState *thread_env;
+void cpu_loop(CPUState *env);
+char *target_strerror(int err);
+int get_osversion(void);
+void fork_start(void);
+void fork_end(int child);
+
+#include "qemu-log.h"
+
+/* strace.c */
+void
+print_freebsd_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6);
+void print_freebsd_syscall_ret(int num, abi_long ret);
+void
+print_netbsd_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6);
+void print_netbsd_syscall_ret(int num, abi_long ret);
+void
+print_openbsd_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6);
+void print_openbsd_syscall_ret(int num, abi_long ret);
+extern int do_strace;
+
+/* signal.c */
+void process_pending_signals(CPUState *cpu_env);
+void signal_init(void);
+//int queue_signal(CPUState *env, int sig, target_siginfo_t *info);
+//void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
+//void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
+long do_sigreturn(CPUState *env);
+long do_rt_sigreturn(CPUState *env);
+abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
+
+/* mmap.c */
+int target_mprotect(abi_ulong start, abi_ulong len, int prot);
+abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset);
+int target_munmap(abi_ulong start, abi_ulong len);
+abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
+ abi_ulong new_size, unsigned long flags,
+ abi_ulong new_addr);
+int target_msync(abi_ulong start, abi_ulong len, int flags);
+extern unsigned long last_brk;
+void mmap_lock(void);
+void mmap_unlock(void);
+void cpu_list_lock(void);
+void cpu_list_unlock(void);
+#if defined(CONFIG_USE_NPTL)
+void mmap_fork_start(void);
+void mmap_fork_end(int child);
+#endif
+
+/* main.c */
+extern unsigned long x86_stack_size;
+
+/* user access */
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1 /* implies read access */
+
+static inline int access_ok(int type, abi_ulong addr, abi_ulong size)
+{
+ return page_check_range((target_ulong)addr, size,
+ (type == VERIFY_READ) ? PAGE_READ : (PAGE_READ | PAGE_WRITE)) == 0;
+}
+
+/* NOTE __get_user and __put_user use host pointers and don't check access. */
+/* These are usually used to access struct data members once the
+ * struct has been locked - usually with lock_user_struct().
+ */
+#define __put_user(x, hptr)\
+({\
+ int size = sizeof(*hptr);\
+ switch(size) {\
+ case 1:\
+ *(uint8_t *)(hptr) = (uint8_t)(typeof(*hptr))(x);\
+ break;\
+ case 2:\
+ *(uint16_t *)(hptr) = tswap16((typeof(*hptr))(x));\
+ break;\
+ case 4:\
+ *(uint32_t *)(hptr) = tswap32((typeof(*hptr))(x));\
+ break;\
+ case 8:\
+ *(uint64_t *)(hptr) = tswap64((typeof(*hptr))(x));\
+ break;\
+ default:\
+ abort();\
+ }\
+ 0;\
+})
+
+#define __get_user(x, hptr) \
+({\
+ int size = sizeof(*hptr);\
+ switch(size) {\
+ case 1:\
+ x = (typeof(*hptr))*(uint8_t *)(hptr);\
+ break;\
+ case 2:\
+ x = (typeof(*hptr))tswap16(*(uint16_t *)(hptr));\
+ break;\
+ case 4:\
+ x = (typeof(*hptr))tswap32(*(uint32_t *)(hptr));\
+ break;\
+ case 8:\
+ x = (typeof(*hptr))tswap64(*(uint64_t *)(hptr));\
+ break;\
+ default:\
+ /* avoid warning */\
+ x = 0;\
+ abort();\
+ }\
+ 0;\
+})
+
+/* put_user()/get_user() take a guest address and check access */
+/* These are usually used to access an atomic data type, such as an int,
+ * that has been passed by address. These internally perform locking
+ * and unlocking on the data type.
+ */
+#define put_user(x, gaddr, target_type) \
+({ \
+ abi_ulong __gaddr = (gaddr); \
+ target_type *__hptr; \
+ abi_long __ret; \
+ if ((__hptr = lock_user(VERIFY_WRITE, __gaddr, sizeof(target_type), 0))) { \
+ __ret = __put_user((x), __hptr); \
+ unlock_user(__hptr, __gaddr, sizeof(target_type)); \
+ } else \
+ __ret = -TARGET_EFAULT; \
+ __ret; \
+})
+
+#define get_user(x, gaddr, target_type) \
+({ \
+ abi_ulong __gaddr = (gaddr); \
+ target_type *__hptr; \
+ abi_long __ret; \
+ if ((__hptr = lock_user(VERIFY_READ, __gaddr, sizeof(target_type), 1))) { \
+ __ret = __get_user((x), __hptr); \
+ unlock_user(__hptr, __gaddr, 0); \
+ } else { \
+ /* avoid warning */ \
+ (x) = 0; \
+ __ret = -TARGET_EFAULT; \
+ } \
+ __ret; \
+})
+
+#define put_user_ual(x, gaddr) put_user((x), (gaddr), abi_ulong)
+#define put_user_sal(x, gaddr) put_user((x), (gaddr), abi_long)
+#define put_user_u64(x, gaddr) put_user((x), (gaddr), uint64_t)
+#define put_user_s64(x, gaddr) put_user((x), (gaddr), int64_t)
+#define put_user_u32(x, gaddr) put_user((x), (gaddr), uint32_t)
+#define put_user_s32(x, gaddr) put_user((x), (gaddr), int32_t)
+#define put_user_u16(x, gaddr) put_user((x), (gaddr), uint16_t)
+#define put_user_s16(x, gaddr) put_user((x), (gaddr), int16_t)
+#define put_user_u8(x, gaddr) put_user((x), (gaddr), uint8_t)
+#define put_user_s8(x, gaddr) put_user((x), (gaddr), int8_t)
+
+#define get_user_ual(x, gaddr) get_user((x), (gaddr), abi_ulong)
+#define get_user_sal(x, gaddr) get_user((x), (gaddr), abi_long)
+#define get_user_u64(x, gaddr) get_user((x), (gaddr), uint64_t)
+#define get_user_s64(x, gaddr) get_user((x), (gaddr), int64_t)
+#define get_user_u32(x, gaddr) get_user((x), (gaddr), uint32_t)
+#define get_user_s32(x, gaddr) get_user((x), (gaddr), int32_t)
+#define get_user_u16(x, gaddr) get_user((x), (gaddr), uint16_t)
+#define get_user_s16(x, gaddr) get_user((x), (gaddr), int16_t)
+#define get_user_u8(x, gaddr) get_user((x), (gaddr), uint8_t)
+#define get_user_s8(x, gaddr) get_user((x), (gaddr), int8_t)
+
+/* copy_from_user() and copy_to_user() are usually used to copy data
+ * buffers between the target and host. These internally perform
+ * locking/unlocking of the memory.
+ */
+abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len);
+abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len);
+
+/* Functions for accessing guest memory. The tget and tput functions
+ read/write single values, byteswapping as neccessary. The lock_user
+ gets a pointer to a contiguous area of guest memory, but does not perform
+ and byteswapping. lock_user may return either a pointer to the guest
+ memory, or a temporary buffer. */
+
+/* Lock an area of guest memory into the host. If copy is true then the
+ host area will have the same contents as the guest. */
+static inline void *lock_user(int type, abi_ulong guest_addr, long len, int copy)
+{
+ if (!access_ok(type, guest_addr, len))
+ return NULL;
+#ifdef DEBUG_REMAP
+ {
+ void *addr;
+ addr = malloc(len);
+ if (copy)
+ memcpy(addr, g2h(guest_addr), len);
+ else
+ memset(addr, 0, len);
+ return addr;
+ }
+#else
+ return g2h(guest_addr);
+#endif
+}
+
+/* Unlock an area of guest memory. The first LEN bytes must be
+ flushed back to guest memory. host_ptr = NULL is explicitly
+ allowed and does nothing. */
+static inline void unlock_user(void *host_ptr, abi_ulong guest_addr,
+ long len)
+{
+
+#ifdef DEBUG_REMAP
+ if (!host_ptr)
+ return;
+ if (host_ptr == g2h(guest_addr))
+ return;
+ if (len > 0)
+ memcpy(g2h(guest_addr), host_ptr, len);
+ free(host_ptr);
+#endif
+}
+
+/* Return the length of a string in target memory or -TARGET_EFAULT if
+ access error. */
+abi_long target_strlen(abi_ulong gaddr);
+
+/* Like lock_user but for null terminated strings. */
+static inline void *lock_user_string(abi_ulong guest_addr)
+{
+ abi_long len;
+ len = target_strlen(guest_addr);
+ if (len < 0)
+ return NULL;
+ return lock_user(VERIFY_READ, guest_addr, (long)(len + 1), 1);
+}
+
+/* Helper macros for locking/ulocking a target struct. */
+#define lock_user_struct(type, host_ptr, guest_addr, copy) \
+ (host_ptr = lock_user(type, guest_addr, sizeof(*host_ptr), copy))
+#define unlock_user_struct(host_ptr, guest_addr, copy) \
+ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0)
+
+#if defined(CONFIG_USE_NPTL)
+#include <pthread.h>
+#endif
+
+#endif /* QEMU_H */
diff --git a/bsd-user/signal.c b/bsd-user/signal.c
new file mode 100644
index 0000000..40313c8
--- /dev/null
+++ b/bsd-user/signal.c
@@ -0,0 +1,38 @@
+/*
+ * Emulation of BSD signals
+ *
+ * Copyright (c) 2003 - 2008 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "qemu.h"
+#include "target_signal.h"
+
+//#define DEBUG_SIGNAL
+
+void signal_init(void)
+{
+}
+
+void process_pending_signals(CPUState *cpu_env)
+{
+}
diff --git a/bsd-user/sparc/syscall.h b/bsd-user/sparc/syscall.h
new file mode 100644
index 0000000..5a9bb7e
--- /dev/null
+++ b/bsd-user/sparc/syscall.h
@@ -0,0 +1,9 @@
+struct target_pt_regs {
+ abi_ulong psr;
+ abi_ulong pc;
+ abi_ulong npc;
+ abi_ulong y;
+ abi_ulong u_regs[16];
+};
+
+#define UNAME_MACHINE "sun4"
diff --git a/bsd-user/sparc/target_signal.h b/bsd-user/sparc/target_signal.h
new file mode 100644
index 0000000..5b2abba
--- /dev/null
+++ b/bsd-user/sparc/target_signal.h
@@ -0,0 +1,27 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+
+#ifndef UREG_I6
+#define UREG_I6 6
+#endif
+#ifndef UREG_FP
+#define UREG_FP UREG_I6
+#endif
+
+static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state)
+{
+ return state->regwptr[UREG_FP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/bsd-user/sparc64/syscall.h b/bsd-user/sparc64/syscall.h
new file mode 100644
index 0000000..81a816d
--- /dev/null
+++ b/bsd-user/sparc64/syscall.h
@@ -0,0 +1,10 @@
+struct target_pt_regs {
+ abi_ulong u_regs[16];
+ abi_ulong tstate;
+ abi_ulong pc;
+ abi_ulong npc;
+ abi_ulong y;
+ abi_ulong fprs;
+};
+
+#define UNAME_MACHINE "sun4u"
diff --git a/bsd-user/sparc64/target_signal.h b/bsd-user/sparc64/target_signal.h
new file mode 100644
index 0000000..5b2abba
--- /dev/null
+++ b/bsd-user/sparc64/target_signal.h
@@ -0,0 +1,27 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+
+#ifndef UREG_I6
+#define UREG_I6 6
+#endif
+#ifndef UREG_FP
+#define UREG_FP UREG_I6
+#endif
+
+static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state)
+{
+ return state->regwptr[UREG_FP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/bsd-user/strace.c b/bsd-user/strace.c
new file mode 100644
index 0000000..d73bbca
--- /dev/null
+++ b/bsd-user/strace.c
@@ -0,0 +1,191 @@
+#include <stdio.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include "qemu.h"
+
+int do_strace=0;
+
+struct syscallname {
+ int nr;
+ const char *name;
+ const char *format;
+ void (*call)(const struct syscallname *,
+ abi_long, abi_long, abi_long,
+ abi_long, abi_long, abi_long);
+ void (*result)(const struct syscallname *, abi_long);
+};
+
+/*
+ * Utility functions
+ */
+
+static void
+print_execve(const struct syscallname *name,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ abi_ulong arg_ptr_addr;
+ char *s;
+
+ if (!(s = lock_user_string(arg1)))
+ return;
+ gemu_log("%s(\"%s\",{", name->name, s);
+ unlock_user(s, arg1, 0);
+
+ for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) {
+ abi_ulong *arg_ptr, arg_addr;
+
+ arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1);
+ if (!arg_ptr)
+ return;
+ arg_addr = tswapl(*arg_ptr);
+ unlock_user(arg_ptr, arg_ptr_addr, 0);
+ if (!arg_addr)
+ break;
+ if ((s = lock_user_string(arg_addr))) {
+ gemu_log("\"%s\",", s);
+ unlock_user(s, arg_addr, 0);
+ }
+ }
+
+ gemu_log("NULL})");
+}
+
+/*
+ * Variants for the return value output function
+ */
+
+static void
+print_syscall_ret_addr(const struct syscallname *name, abi_long ret)
+{
+if( ret == -1 ) {
+ gemu_log(" = -1 errno=%d (%s)\n", errno, strerror(errno));
+ } else {
+ gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret);
+ }
+}
+
+#if 0 /* currently unused */
+static void
+print_syscall_ret_raw(struct syscallname *name, abi_long ret)
+{
+ gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret);
+}
+#endif
+
+/*
+ * An array of all of the syscalls we know about
+ */
+
+static const struct syscallname freebsd_scnames[] = {
+#include "freebsd/strace.list"
+};
+static const struct syscallname netbsd_scnames[] = {
+#include "netbsd/strace.list"
+};
+static const struct syscallname openbsd_scnames[] = {
+#include "openbsd/strace.list"
+};
+
+static void
+print_syscall(int num, const struct syscallname *scnames, unsigned int nscnames,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ unsigned int i;
+ const char *format="%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ","
+ TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ","
+ TARGET_ABI_FMT_ld ")";
+
+ gemu_log("%d ", getpid() );
+
+ for (i = 0; i < nscnames; i++)
+ if (scnames[i].nr == num) {
+ if (scnames[i].call != NULL) {
+ scnames[i].call(&scnames[i], arg1, arg2, arg3, arg4, arg5,
+ arg6);
+ } else {
+ /* XXX: this format system is broken because it uses
+ host types and host pointers for strings */
+ if (scnames[i].format != NULL)
+ format = scnames[i].format;
+ gemu_log(format, scnames[i].name, arg1, arg2, arg3, arg4,
+ arg5, arg6);
+ }
+ return;
+ }
+ gemu_log("Unknown syscall %d\n", num);
+}
+
+static void
+print_syscall_ret(int num, abi_long ret, const struct syscallname *scnames,
+ unsigned int nscnames)
+{
+ unsigned int i;
+
+ for (i = 0; i < nscnames; i++)
+ if (scnames[i].nr == num) {
+ if (scnames[i].result != NULL) {
+ scnames[i].result(&scnames[i], ret);
+ } else {
+ if( ret < 0 ) {
+ gemu_log(" = -1 errno=" TARGET_ABI_FMT_ld " (%s)\n", -ret,
+ strerror(-ret));
+ } else {
+ gemu_log(" = " TARGET_ABI_FMT_ld "\n", ret);
+ }
+ }
+ break;
+ }
+}
+
+/*
+ * The public interface to this module.
+ */
+void
+print_freebsd_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ print_syscall(num, freebsd_scnames, ARRAY_SIZE(freebsd_scnames),
+ arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+void
+print_freebsd_syscall_ret(int num, abi_long ret)
+{
+ print_syscall_ret(num, ret, freebsd_scnames, ARRAY_SIZE(freebsd_scnames));
+}
+
+void
+print_netbsd_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ print_syscall(num, netbsd_scnames, ARRAY_SIZE(netbsd_scnames),
+ arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+void
+print_netbsd_syscall_ret(int num, abi_long ret)
+{
+ print_syscall_ret(num, ret, netbsd_scnames, ARRAY_SIZE(netbsd_scnames));
+}
+
+void
+print_openbsd_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ print_syscall(num, openbsd_scnames, ARRAY_SIZE(openbsd_scnames),
+ arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+void
+print_openbsd_syscall_ret(int num, abi_long ret)
+{
+ print_syscall_ret(num, ret, openbsd_scnames, ARRAY_SIZE(openbsd_scnames));
+}
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
new file mode 100644
index 0000000..eb1cdf2
--- /dev/null
+++ b/bsd-user/syscall.c
@@ -0,0 +1,560 @@
+/*
+ * BSD syscalls
+ *
+ * Copyright (c) 2003 - 2008 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <signal.h>
+#include <utime.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+
+//#define DEBUG
+
+static abi_ulong target_brk;
+static abi_ulong target_original_brk;
+
+static inline abi_long get_errno(abi_long ret)
+{
+ if (ret == -1)
+ /* XXX need to translate host -> target errnos here */
+ return -(errno);
+ else
+ return ret;
+}
+
+#define target_to_host_bitmask(x, tbl) (x)
+
+static inline int is_error(abi_long ret)
+{
+ return (abi_ulong)ret >= (abi_ulong)(-4096);
+}
+
+void target_set_brk(abi_ulong new_brk)
+{
+ target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk);
+}
+
+/* do_obreak() must return target errnos. */
+static abi_long do_obreak(abi_ulong new_brk)
+{
+ abi_ulong brk_page;
+ abi_long mapped_addr;
+ int new_alloc_size;
+
+ if (!new_brk)
+ return 0;
+ if (new_brk < target_original_brk)
+ return -TARGET_EINVAL;
+
+ brk_page = HOST_PAGE_ALIGN(target_brk);
+
+ /* If the new brk is less than this, set it and we're done... */
+ if (new_brk < brk_page) {
+ target_brk = new_brk;
+ return 0;
+ }
+
+ /* We need to allocate more memory after the brk... */
+ new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
+ mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0));
+
+ if (!is_error(mapped_addr))
+ target_brk = new_brk;
+ else
+ return mapped_addr;
+
+ return 0;
+}
+
+#if defined(TARGET_I386)
+static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms)
+{
+ abi_long ret = 0;
+ abi_ulong val;
+ int idx;
+
+ switch(op) {
+#ifdef TARGET_ABI32
+ case TARGET_FREEBSD_I386_SET_GSBASE:
+ case TARGET_FREEBSD_I386_SET_FSBASE:
+ if (op == TARGET_FREEBSD_I386_SET_GSBASE)
+#else
+ case TARGET_FREEBSD_AMD64_SET_GSBASE:
+ case TARGET_FREEBSD_AMD64_SET_FSBASE:
+ if (op == TARGET_FREEBSD_AMD64_SET_GSBASE)
+#endif
+ idx = R_GS;
+ else
+ idx = R_FS;
+ if (get_user(val, parms, abi_ulong))
+ return -TARGET_EFAULT;
+ cpu_x86_load_seg(env, idx, 0);
+ env->segs[idx].base = val;
+ break;
+#ifdef TARGET_ABI32
+ case TARGET_FREEBSD_I386_GET_GSBASE:
+ case TARGET_FREEBSD_I386_GET_FSBASE:
+ if (op == TARGET_FREEBSD_I386_GET_GSBASE)
+#else
+ case TARGET_FREEBSD_AMD64_GET_GSBASE:
+ case TARGET_FREEBSD_AMD64_GET_FSBASE:
+ if (op == TARGET_FREEBSD_AMD64_GET_GSBASE)
+#endif
+ idx = R_GS;
+ else
+ idx = R_FS;
+ val = env->segs[idx].base;
+ if (put_user(val, parms, abi_ulong))
+ return -TARGET_EFAULT;
+ break;
+ /* XXX handle the others... */
+ default:
+ ret = -TARGET_EINVAL;
+ break;
+ }
+ return ret;
+}
+#endif
+
+#ifdef TARGET_SPARC
+static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms)
+{
+ /* XXX handle
+ * TARGET_FREEBSD_SPARC_UTRAP_INSTALL,
+ * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL
+ */
+ return -TARGET_EINVAL;
+}
+#endif
+
+#ifdef __FreeBSD__
+/*
+ * XXX this uses the undocumented oidfmt interface to find the kind of
+ * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt()
+ * (this is mostly copied from src/sbin/sysctl/sysctl.c)
+ */
+static int
+oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
+{
+ int qoid[CTL_MAXNAME+2];
+ uint8_t buf[BUFSIZ];
+ int i;
+ size_t j;
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, len * sizeof(int));
+
+ j = sizeof(buf);
+ i = sysctl(qoid, len + 2, buf, &j, 0, 0);
+ if (i)
+ return i;
+
+ if (kind)
+ *kind = *(uint32_t *)buf;
+
+ if (fmt)
+ strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
+ return (0);
+}
+
+/*
+ * try and convert sysctl return data for the target.
+ * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT.
+ */
+static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
+{
+ switch (kind & CTLTYPE) {
+ case CTLTYPE_INT:
+ case CTLTYPE_UINT:
+ *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp);
+ break;
+#ifdef TARGET_ABI32
+ case CTLTYPE_LONG:
+ case CTLTYPE_ULONG:
+ *(uint32_t *)holdp = tswap32(*(long *)holdp);
+ break;
+#else
+ case CTLTYPE_LONG:
+ *(uint64_t *)holdp = tswap64(*(long *)holdp);
+ case CTLTYPE_ULONG:
+ *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
+ break;
+#endif
+ case CTLTYPE_QUAD:
+ *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
+ break;
+ case CTLTYPE_STRING:
+ break;
+ default:
+ /* XXX unhandled */
+ return -1;
+ }
+ return 0;
+}
+
+/* XXX this needs to be emulated on non-FreeBSD hosts... */
+static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
+ abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
+{
+ abi_long ret;
+ void *hnamep, *holdp, *hnewp = NULL;
+ size_t holdlen;
+ abi_ulong oldlen = 0;
+ int32_t *snamep = qemu_malloc(sizeof(int32_t) * namelen), *p, *q, i;
+ uint32_t kind = 0;
+
+ if (oldlenp)
+ get_user_ual(oldlen, oldlenp);
+ if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
+ return -TARGET_EFAULT;
+ if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
+ return -TARGET_EFAULT;
+ if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
+ return -TARGET_EFAULT;
+ holdlen = oldlen;
+ for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
+ *q++ = tswap32(*p);
+ oidfmt(snamep, namelen, NULL, &kind);
+ /* XXX swap hnewp */
+ ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
+ if (!ret)
+ sysctl_oldcvt(holdp, holdlen, kind);
+ put_user_ual(holdlen, oldlenp);
+ unlock_user(hnamep, namep, 0);
+ unlock_user(holdp, oldp, holdlen);
+ if (hnewp)
+ unlock_user(hnewp, newp, 0);
+ qemu_free(snamep);
+ return ret;
+}
+#endif
+
+/* FIXME
+ * lock_iovec()/unlock_iovec() have a return code of 0 for success where
+ * other lock functions have a return code of 0 for failure.
+ */
+static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ abi_ulong base;
+ int i;
+
+ target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
+ if (!target_vec)
+ return -TARGET_EFAULT;
+ for(i = 0;i < count; i++) {
+ base = tswapl(target_vec[i].iov_base);
+ vec[i].iov_len = tswapl(target_vec[i].iov_len);
+ if (vec[i].iov_len != 0) {
+ vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
+ /* Don't check lock_user return value. We must call writev even
+ if a element has invalid base address. */
+ } else {
+ /* zero length pointer is ignored */
+ vec[i].iov_base = NULL;
+ }
+ }
+ unlock_user (target_vec, target_addr, 0);
+ return 0;
+}
+
+static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ abi_ulong base;
+ int i;
+
+ target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
+ if (!target_vec)
+ return -TARGET_EFAULT;
+ for(i = 0;i < count; i++) {
+ if (target_vec[i].iov_base) {
+ base = tswapl(target_vec[i].iov_base);
+ unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
+ }
+ }
+ unlock_user (target_vec, target_addr, 0);
+
+ return 0;
+}
+
+/* do_syscall() should always have a single exit point at the end so
+ that actions, such as logging of syscall results, can be performed.
+ All errnos that do_syscall() returns must be -TARGET_<errcode>. */
+abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6, abi_long arg7,
+ abi_long arg8)
+{
+ abi_long ret;
+ void *p;
+
+#ifdef DEBUG
+ gemu_log("freebsd syscall %d\n", num);
+#endif
+ if(do_strace)
+ print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+
+ switch(num) {
+ case TARGET_FREEBSD_NR_exit:
+#ifdef TARGET_GPROF
+ _mcleanup();
+#endif
+ gdb_exit(cpu_env, arg1);
+ /* XXX: should free thread stack and CPU env */
+ _exit(arg1);
+ ret = 0; /* avoid warning */
+ break;
+ case TARGET_FREEBSD_NR_read:
+ if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
+ goto efault;
+ ret = get_errno(read(arg1, p, arg3));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_FREEBSD_NR_write:
+ if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
+ goto efault;
+ ret = get_errno(write(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+ case TARGET_FREEBSD_NR_writev:
+ {
+ int count = arg3;
+ struct iovec *vec;
+
+ vec = alloca(count * sizeof(struct iovec));
+ if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
+ goto efault;
+ ret = get_errno(writev(arg1, vec, count));
+ unlock_iovec(vec, arg2, count, 0);
+ }
+ break;
+ case TARGET_FREEBSD_NR_open:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(open(path(p),
+ target_to_host_bitmask(arg2, fcntl_flags_tbl),
+ arg3));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_FREEBSD_NR_mmap:
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6));
+ break;
+ case TARGET_FREEBSD_NR_mprotect:
+ ret = get_errno(target_mprotect(arg1, arg2, arg3));
+ break;
+ case TARGET_FREEBSD_NR_break:
+ ret = do_obreak(arg1);
+ break;
+#ifdef __FreeBSD__
+ case TARGET_FREEBSD_NR___sysctl:
+ ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+ case TARGET_FREEBSD_NR_sysarch:
+ ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
+ break;
+ case TARGET_FREEBSD_NR_syscall:
+ case TARGET_FREEBSD_NR___syscall:
+ ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
+ break;
+ default:
+ ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
+ break;
+ }
+ fail:
+#ifdef DEBUG
+ gemu_log(" = %ld\n", ret);
+#endif
+ if (do_strace)
+ print_freebsd_syscall_ret(num, ret);
+ return ret;
+ efault:
+ ret = -TARGET_EFAULT;
+ goto fail;
+}
+
+abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6)
+{
+ abi_long ret;
+ void *p;
+
+#ifdef DEBUG
+ gemu_log("netbsd syscall %d\n", num);
+#endif
+ if(do_strace)
+ print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+
+ switch(num) {
+ case TARGET_NETBSD_NR_exit:
+#ifdef TARGET_GPROF
+ _mcleanup();
+#endif
+ gdb_exit(cpu_env, arg1);
+ /* XXX: should free thread stack and CPU env */
+ _exit(arg1);
+ ret = 0; /* avoid warning */
+ break;
+ case TARGET_NETBSD_NR_read:
+ if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
+ goto efault;
+ ret = get_errno(read(arg1, p, arg3));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_NETBSD_NR_write:
+ if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
+ goto efault;
+ ret = get_errno(write(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+ case TARGET_NETBSD_NR_open:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(open(path(p),
+ target_to_host_bitmask(arg2, fcntl_flags_tbl),
+ arg3));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NETBSD_NR_mmap:
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6));
+ break;
+ case TARGET_NETBSD_NR_mprotect:
+ ret = get_errno(target_mprotect(arg1, arg2, arg3));
+ break;
+ case TARGET_NETBSD_NR_syscall:
+ case TARGET_NETBSD_NR___syscall:
+ ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
+ break;
+ default:
+ ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+ }
+ fail:
+#ifdef DEBUG
+ gemu_log(" = %ld\n", ret);
+#endif
+ if (do_strace)
+ print_netbsd_syscall_ret(num, ret);
+ return ret;
+ efault:
+ ret = -TARGET_EFAULT;
+ goto fail;
+}
+
+abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6)
+{
+ abi_long ret;
+ void *p;
+
+#ifdef DEBUG
+ gemu_log("openbsd syscall %d\n", num);
+#endif
+ if(do_strace)
+ print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+
+ switch(num) {
+ case TARGET_OPENBSD_NR_exit:
+#ifdef TARGET_GPROF
+ _mcleanup();
+#endif
+ gdb_exit(cpu_env, arg1);
+ /* XXX: should free thread stack and CPU env */
+ _exit(arg1);
+ ret = 0; /* avoid warning */
+ break;
+ case TARGET_OPENBSD_NR_read:
+ if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
+ goto efault;
+ ret = get_errno(read(arg1, p, arg3));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_OPENBSD_NR_write:
+ if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
+ goto efault;
+ ret = get_errno(write(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+ case TARGET_OPENBSD_NR_open:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(open(path(p),
+ target_to_host_bitmask(arg2, fcntl_flags_tbl),
+ arg3));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_OPENBSD_NR_mmap:
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6));
+ break;
+ case TARGET_OPENBSD_NR_mprotect:
+ ret = get_errno(target_mprotect(arg1, arg2, arg3));
+ break;
+ case TARGET_OPENBSD_NR_syscall:
+ case TARGET_OPENBSD_NR___syscall:
+ ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
+ break;
+ default:
+ ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+ }
+ fail:
+#ifdef DEBUG
+ gemu_log(" = %ld\n", ret);
+#endif
+ if (do_strace)
+ print_openbsd_syscall_ret(num, ret);
+ return ret;
+ efault:
+ ret = -TARGET_EFAULT;
+ goto fail;
+}
+
+void syscall_init(void)
+{
+}
diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h
new file mode 100644
index 0000000..207ddee
--- /dev/null
+++ b/bsd-user/syscall_defs.h
@@ -0,0 +1,114 @@
+/* $OpenBSD: signal.h,v 1.19 2006/01/08 14:20:16 millert Exp $ */
+/* $NetBSD: signal.h,v 1.21 1996/02/09 18:25:32 christos Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)signal.h 8.2 (Berkeley) 1/21/94
+ */
+
+#define TARGET_NSIG 32 /* counting 0; could be 33 (mask is 1-32) */
+
+#define TARGET_SIGHUP 1 /* hangup */
+#define TARGET_SIGINT 2 /* interrupt */
+#define TARGET_SIGQUIT 3 /* quit */
+#define TARGET_SIGILL 4 /* illegal instruction (not reset when caught) */
+#define TARGET_SIGTRAP 5 /* trace trap (not reset when caught) */
+#define TARGET_SIGABRT 6 /* abort() */
+#define TARGET_SIGIOT SIGABRT /* compatibility */
+#define TARGET_SIGEMT 7 /* EMT instruction */
+#define TARGET_SIGFPE 8 /* floating point exception */
+#define TARGET_SIGKILL 9 /* kill (cannot be caught or ignored) */
+#define TARGET_SIGBUS 10 /* bus error */
+#define TARGET_SIGSEGV 11 /* segmentation violation */
+#define TARGET_SIGSYS 12 /* bad argument to system call */
+#define TARGET_SIGPIPE 13 /* write on a pipe with no one to read it */
+#define TARGET_SIGALRM 14 /* alarm clock */
+#define TARGET_SIGTERM 15 /* software termination signal from kill */
+#define TARGET_SIGURG 16 /* urgent condition on IO channel */
+#define TARGET_SIGSTOP 17 /* sendable stop signal not from tty */
+#define TARGET_SIGTSTP 18 /* stop signal from tty */
+#define TARGET_SIGCONT 19 /* continue a stopped process */
+#define TARGET_SIGCHLD 20 /* to parent on child stop or exit */
+#define TARGET_SIGTTIN 21 /* to readers pgrp upon background tty read */
+#define TARGET_SIGTTOU 22 /* like TTIN for output if (tp->t_local&LTOSTOP) */
+#define TARGET_SIGIO 23 /* input/output possible signal */
+#define TARGET_SIGXCPU 24 /* exceeded CPU time limit */
+#define TARGET_SIGXFSZ 25 /* exceeded file size limit */
+#define TARGET_SIGVTALRM 26 /* virtual time alarm */
+#define TARGET_SIGPROF 27 /* profiling time alarm */
+#define TARGET_SIGWINCH 28 /* window size changes */
+#define TARGET_SIGINFO 29 /* information request */
+#define TARGET_SIGUSR1 30 /* user defined signal 1 */
+#define TARGET_SIGUSR2 31 /* user defined signal 2 */
+
+/*
+ * Language spec says we must list exactly one parameter, even though we
+ * actually supply three. Ugh!
+ */
+#define TARGET_SIG_DFL (void (*)(int))0
+#define TARGET_SIG_IGN (void (*)(int))1
+#define TARGET_SIG_ERR (void (*)(int))-1
+
+#define TARGET_SA_ONSTACK 0x0001 /* take signal on signal stack */
+#define TARGET_SA_RESTART 0x0002 /* restart system on signal return */
+#define TARGET_SA_RESETHAND 0x0004 /* reset to SIG_DFL when taking signal */
+#define TARGET_SA_NODEFER 0x0010 /* don't mask the signal we're delivering */
+#define TARGET_SA_NOCLDWAIT 0x0020 /* don't create zombies (assign to pid 1) */
+#define TARGET_SA_USERTRAMP 0x0100 /* do not bounce off kernel's sigtramp */
+#define TARGET_SA_NOCLDSTOP 0x0008 /* do not generate SIGCHLD on child stop */
+#define TARGET_SA_SIGINFO 0x0040 /* generate siginfo_t */
+
+/*
+ * Flags for sigprocmask:
+ */
+#define TARGET_SIG_BLOCK 1 /* block specified signal set */
+#define TARGET_SIG_UNBLOCK 2 /* unblock specified signal set */
+#define TARGET_SIG_SETMASK 3 /* set specified signal set */
+
+#define TARGET_BADSIG SIG_ERR
+
+#define TARGET_SS_ONSTACK 0x0001 /* take signals on alternate stack */
+#define TARGET_SS_DISABLE 0x0004 /* disable taking signals on alternate stack */
+
+#include "errno_defs.h"
+
+#include "freebsd/syscall_nr.h"
+#include "netbsd/syscall_nr.h"
+#include "openbsd/syscall_nr.h"
+
+struct target_iovec {
+ abi_long iov_base; /* Starting address */
+ abi_long iov_len; /* Number of bytes */
+};
+
diff --git a/bsd-user/uaccess.c b/bsd-user/uaccess.c
new file mode 100644
index 0000000..677f19c
--- /dev/null
+++ b/bsd-user/uaccess.c
@@ -0,0 +1,65 @@
+/* User memory access */
+#include <stdio.h>
+#include <string.h>
+
+#include "qemu.h"
+
+/* copy_from_user() and copy_to_user() are usually used to copy data
+ * buffers between the target and host. These internally perform
+ * locking/unlocking of the memory.
+ */
+abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len)
+{
+ abi_long ret = 0;
+ void *ghptr;
+
+ if ((ghptr = lock_user(VERIFY_READ, gaddr, len, 1))) {
+ memcpy(hptr, ghptr, len);
+ unlock_user(ghptr, gaddr, 0);
+ } else
+ ret = -TARGET_EFAULT;
+
+ return ret;
+}
+
+
+abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len)
+{
+ abi_long ret = 0;
+ void *ghptr;
+
+ if ((ghptr = lock_user(VERIFY_WRITE, gaddr, len, 0))) {
+ memcpy(ghptr, hptr, len);
+ unlock_user(ghptr, gaddr, len);
+ } else
+ ret = -TARGET_EFAULT;
+
+ return ret;
+}
+
+/* Return the length of a string in target memory or -TARGET_EFAULT if
+ access error */
+abi_long target_strlen(abi_ulong guest_addr1)
+{
+ uint8_t *ptr;
+ abi_ulong guest_addr;
+ int max_len, len;
+
+ guest_addr = guest_addr1;
+ for(;;) {
+ max_len = TARGET_PAGE_SIZE - (guest_addr & ~TARGET_PAGE_MASK);
+ ptr = lock_user(VERIFY_READ, guest_addr, max_len, 1);
+ if (!ptr)
+ return -TARGET_EFAULT;
+ len = qemu_strnlen((char *)ptr, max_len);
+ unlock_user(ptr, guest_addr, 0);
+ guest_addr += len;
+ /* we don't allow wrapping or integer overflow */
+ if (guest_addr == 0 ||
+ (guest_addr - guest_addr1) > 0x7fffffff)
+ return -TARGET_EFAULT;
+ if (len != max_len)
+ break;
+ }
+ return guest_addr - guest_addr1;
+}
diff --git a/bsd-user/x86_64/syscall.h b/bsd-user/x86_64/syscall.h
new file mode 100644
index 0000000..5f71b7c
--- /dev/null
+++ b/bsd-user/x86_64/syscall.h
@@ -0,0 +1,116 @@
+#define __USER_CS (0x33)
+#define __USER_DS (0x2B)
+
+struct target_pt_regs {
+ abi_ulong r15;
+ abi_ulong r14;
+ abi_ulong r13;
+ abi_ulong r12;
+ abi_ulong rbp;
+ abi_ulong rbx;
+/* arguments: non interrupts/non tracing syscalls only save upto here*/
+ abi_ulong r11;
+ abi_ulong r10;
+ abi_ulong r9;
+ abi_ulong r8;
+ abi_ulong rax;
+ abi_ulong rcx;
+ abi_ulong rdx;
+ abi_ulong rsi;
+ abi_ulong rdi;
+ abi_ulong orig_rax;
+/* end of arguments */
+/* cpu exception frame or undefined */
+ abi_ulong rip;
+ abi_ulong cs;
+ abi_ulong eflags;
+ abi_ulong rsp;
+ abi_ulong ss;
+/* top of stack page */
+};
+
+/* Maximum number of LDT entries supported. */
+#define TARGET_LDT_ENTRIES 8192
+/* The size of each LDT entry. */
+#define TARGET_LDT_ENTRY_SIZE 8
+
+#define TARGET_GDT_ENTRIES 16
+#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
+#define TARGET_GDT_ENTRY_TLS_MIN 12
+#define TARGET_GDT_ENTRY_TLS_MAX 14
+
+#if 0 // Redefine this
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ abi_ulong base_addr;
+ unsigned int limit;
+ unsigned int seg_32bit:1;
+ unsigned int contents:2;
+ unsigned int read_exec_only:1;
+ unsigned int limit_in_pages:1;
+ unsigned int seg_not_present:1;
+ unsigned int useable:1;
+ unsigned int lm:1;
+};
+#else
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ abi_ulong base_addr;
+ unsigned int limit;
+ unsigned int flags;
+};
+#endif
+
+struct target_ipc64_perm
+{
+ int key;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t cuid;
+ uint32_t cgid;
+ unsigned short mode;
+ unsigned short __pad1;
+ unsigned short seq;
+ unsigned short __pad2;
+ abi_ulong __unused1;
+ abi_ulong __unused2;
+};
+
+struct target_msqid64_ds {
+ struct target_ipc64_perm msg_perm;
+ unsigned int msg_stime; /* last msgsnd time */
+ unsigned int msg_rtime; /* last msgrcv time */
+ unsigned int msg_ctime; /* last change time */
+ abi_ulong msg_cbytes; /* current number of bytes on queue */
+ abi_ulong msg_qnum; /* number of messages in queue */
+ abi_ulong msg_qbytes; /* max number of bytes on queue */
+ unsigned int msg_lspid; /* pid of last msgsnd */
+ unsigned int msg_lrpid; /* last receive pid */
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
+/* FreeBSD sysarch(2) */
+#define TARGET_FREEBSD_I386_GET_LDT 0
+#define TARGET_FREEBSD_I386_SET_LDT 1
+ /* I386_IOPL */
+#define TARGET_FREEBSD_I386_GET_IOPERM 3
+#define TARGET_FREEBSD_I386_SET_IOPERM 4
+ /* xxxxx */
+#define TARGET_FREEBSD_I386_GET_FSBASE 7
+#define TARGET_FREEBSD_I386_SET_FSBASE 8
+#define TARGET_FREEBSD_I386_GET_GSBASE 9
+#define TARGET_FREEBSD_I386_SET_GSBASE 10
+
+#define TARGET_FREEBSD_AMD64_GET_FSBASE 128
+#define TARGET_FREEBSD_AMD64_SET_FSBASE 129
+#define TARGET_FREEBSD_AMD64_GET_GSBASE 130
+#define TARGET_FREEBSD_AMD64_SET_GSBASE 131
+
+
+#define UNAME_MACHINE "x86_64"
+
+#define TARGET_ARCH_SET_GS 0x1001
+#define TARGET_ARCH_SET_FS 0x1002
+#define TARGET_ARCH_GET_FS 0x1003
+#define TARGET_ARCH_GET_GS 0x1004
diff --git a/bsd-user/x86_64/target_signal.h b/bsd-user/x86_64/target_signal.h
new file mode 100644
index 0000000..659cd40
--- /dev/null
+++ b/bsd-user/x86_64/target_signal.h
@@ -0,0 +1,19 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+static inline abi_ulong get_sp_from_cpustate(CPUX86State *state)
+{
+ return state->regs[R_ESP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/bswap.h b/bswap.h
new file mode 100644
index 0000000..82a7951
--- /dev/null
+++ b/bswap.h
@@ -0,0 +1,240 @@
+#ifndef BSWAP_H
+#define BSWAP_H
+
+#include "config-host.h"
+
+#include <inttypes.h>
+
+#ifdef CONFIG_MACHINE_BSWAP_H
+#include <sys/endian.h>
+#include <sys/types.h>
+#include <machine/bswap.h>
+#else
+
+#ifdef CONFIG_BYTESWAP_H
+#include <byteswap.h>
+#else
+
+#define bswap_16(x) \
+({ \
+ uint16_t __x = (x); \
+ ((uint16_t)( \
+ (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
+ (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
+})
+
+#define bswap_32(x) \
+({ \
+ uint32_t __x = (x); \
+ ((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
+})
+
+#define bswap_64(x) \
+({ \
+ uint64_t __x = (x); \
+ ((uint64_t)( \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
+ (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
+})
+
+#endif /* !CONFIG_BYTESWAP_H */
+
+static inline uint16_t bswap16(uint16_t x)
+{
+ return bswap_16(x);
+}
+
+static inline uint32_t bswap32(uint32_t x)
+{
+ return bswap_32(x);
+}
+
+static inline uint64_t bswap64(uint64_t x)
+{
+ return bswap_64(x);
+}
+
+#endif /* ! CONFIG_MACHINE_BSWAP_H */
+
+static inline void bswap16s(uint16_t *s)
+{
+ *s = bswap16(*s);
+}
+
+static inline void bswap32s(uint32_t *s)
+{
+ *s = bswap32(*s);
+}
+
+static inline void bswap64s(uint64_t *s)
+{
+ *s = bswap64(*s);
+}
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define be_bswap(v, size) (v)
+#define le_bswap(v, size) bswap ## size(v)
+#define be_bswaps(v, size)
+#define le_bswaps(p, size) *p = bswap ## size(*p);
+#else
+#define le_bswap(v, size) (v)
+#define be_bswap(v, size) bswap ## size(v)
+#define le_bswaps(v, size)
+#define be_bswaps(p, size) *p = bswap ## size(*p);
+#endif
+
+#define CPU_CONVERT(endian, size, type)\
+static inline type endian ## size ## _to_cpu(type v)\
+{\
+ return endian ## _bswap(v, size);\
+}\
+\
+static inline type cpu_to_ ## endian ## size(type v)\
+{\
+ return endian ## _bswap(v, size);\
+}\
+\
+static inline void endian ## size ## _to_cpus(type *p)\
+{\
+ endian ## _bswaps(p, size)\
+}\
+\
+static inline void cpu_to_ ## endian ## size ## s(type *p)\
+{\
+ endian ## _bswaps(p, size)\
+}\
+\
+static inline type endian ## size ## _to_cpup(const type *p)\
+{\
+ return endian ## size ## _to_cpu(*p);\
+}\
+\
+static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\
+{\
+ *p = cpu_to_ ## endian ## size(v);\
+}
+
+CPU_CONVERT(be, 16, uint16_t)
+CPU_CONVERT(be, 32, uint32_t)
+CPU_CONVERT(be, 64, uint64_t)
+
+CPU_CONVERT(le, 16, uint16_t)
+CPU_CONVERT(le, 32, uint32_t)
+CPU_CONVERT(le, 64, uint64_t)
+
+/* unaligned versions (optimized for frequent unaligned accesses)*/
+
+#if defined(__i386__) || defined(_ARCH_PPC)
+
+#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v)
+#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v)
+#define le16_to_cpupu(p) le16_to_cpup(p)
+#define le32_to_cpupu(p) le32_to_cpup(p)
+#define be32_to_cpupu(p) be32_to_cpup(p)
+
+#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
+#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
+#define cpu_to_be64wu(p, v) cpu_to_be64w(p, v)
+
+#else
+
+static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v & 0xff;
+ p1[1] = v >> 8;
+}
+
+static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v & 0xff;
+ p1[1] = v >> 8;
+ p1[2] = v >> 16;
+ p1[3] = v >> 24;
+}
+
+static inline uint16_t le16_to_cpupu(const uint16_t *p)
+{
+ const uint8_t *p1 = (const uint8_t *)p;
+ return p1[0] | (p1[1] << 8);
+}
+
+static inline uint32_t le32_to_cpupu(const uint32_t *p)
+{
+ const uint8_t *p1 = (const uint8_t *)p;
+ return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
+}
+
+static inline uint32_t be32_to_cpupu(const uint32_t *p)
+{
+ const uint8_t *p1 = (const uint8_t *)p;
+ return p1[3] | (p1[2] << 8) | (p1[1] << 16) | (p1[0] << 24);
+}
+
+static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v >> 8;
+ p1[1] = v & 0xff;
+}
+
+static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v >> 24;
+ p1[1] = v >> 16;
+ p1[2] = v >> 8;
+ p1[3] = v & 0xff;
+}
+
+static inline void cpu_to_be64wu(uint64_t *p, uint64_t v)
+{
+ uint8_t *p1 = (uint8_t *)p;
+
+ p1[0] = v >> 56;
+ p1[1] = v >> 48;
+ p1[2] = v >> 40;
+ p1[3] = v >> 32;
+ p1[4] = v >> 24;
+ p1[5] = v >> 16;
+ p1[6] = v >> 8;
+ p1[7] = v & 0xff;
+}
+
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define cpu_to_32wu cpu_to_be32wu
+#define leul_to_cpu(v) glue(glue(le,HOST_LONG_BITS),_to_cpu)(v)
+#else
+#define cpu_to_32wu cpu_to_le32wu
+#define leul_to_cpu(v) (v)
+#endif
+
+#undef le_bswap
+#undef be_bswap
+#undef le_bswaps
+#undef be_bswaps
+
+/* len must be one of 1, 2, 4 */
+static inline uint32_t qemu_bswap_len(uint32_t value, int len)
+{
+ return bswap32(value) >> (32 - 8 * len);
+}
+
+#endif /* BSWAP_H */
diff --git a/bt-host.c b/bt-host.c
new file mode 100644
index 0000000..6931e7c
--- /dev/null
+++ b/bt-host.c
@@ -0,0 +1,199 @@
+/*
+ * Wrap a host Bluetooth HCI socket in a struct HCIInfo.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "net.h"
+#include "bt-host.h"
+
+#ifndef _WIN32
+# include <errno.h>
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# ifdef CONFIG_BLUEZ
+# include <bluetooth/bluetooth.h>
+# include <bluetooth/hci.h>
+# include <bluetooth/hci_lib.h>
+# else
+# include "hw/bt.h"
+# define HCI_MAX_FRAME_SIZE 1028
+# endif
+
+struct bt_host_hci_s {
+ struct HCIInfo hci;
+ int fd;
+
+ uint8_t hdr[HCI_MAX_FRAME_SIZE];
+ int len;
+};
+
+static void bt_host_send(struct HCIInfo *hci,
+ int type, const uint8_t *data, int len)
+{
+ struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci;
+ uint8_t pkt = type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = (void *)&pkt;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = (void *) data;
+ iv[1].iov_len = len;
+
+ while (writev(s->fd, iv, 2) < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ fprintf(stderr, "qemu: error %i writing bluetooth packet.\n",
+ errno);
+ return;
+ }
+ }
+}
+
+static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len)
+{
+ bt_host_send(hci, HCI_COMMAND_PKT, data, len);
+}
+
+static void bt_host_acl(struct HCIInfo *hci, const uint8_t *data, int len)
+{
+ bt_host_send(hci, HCI_ACLDATA_PKT, data, len);
+}
+
+static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len)
+{
+ bt_host_send(hci, HCI_SCODATA_PKT, data, len);
+}
+
+static void bt_host_read(void *opaque)
+{
+ struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque;
+ uint8_t *pkt;
+ int pktlen;
+
+ /* Seems that we can't read only the header first and then the amount
+ * of data indicated in the header because Linux will discard everything
+ * that's not been read in one go. */
+ s->len = read(s->fd, s->hdr, sizeof(s->hdr));
+
+ if (s->len < 0) {
+ fprintf(stderr, "qemu: error %i reading HCI frame\n", errno);
+ return;
+ }
+
+ pkt = s->hdr;
+ while (s->len --)
+ switch (*pkt ++) {
+ case HCI_EVENT_PKT:
+ if (s->len < 2)
+ goto bad_pkt;
+
+ pktlen = MIN(pkt[1] + 2, s->len);
+ s->hci.evt_recv(s->hci.opaque, pkt, pktlen);
+ s->len -= pktlen;
+ pkt += pktlen;
+
+ /* TODO: if this is an Inquiry Result event, it's also
+ * interpreted by Linux kernel before we received it, possibly
+ * we should clean the kernel Inquiry cache through
+ * ioctl(s->fd, HCI_INQUIRY, ...). */
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (s->len < 4)
+ goto bad_pkt;
+
+ pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len);
+ s->hci.acl_recv(s->hci.opaque, pkt, pktlen);
+ s->len -= pktlen;
+ pkt += pktlen;
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (s->len < 3)
+ goto bad_pkt;
+
+ pktlen = MIN(pkt[2] + 3, s->len);
+ s->len -= pktlen;
+ pkt += pktlen;
+
+ default:
+ bad_pkt:
+ fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]);
+ }
+}
+
+static int bt_host_bdaddr_set(struct HCIInfo *hci, const uint8_t *bd_addr)
+{
+ return -ENOTSUP;
+}
+
+struct HCIInfo *bt_host_hci(const char *id)
+{
+ struct bt_host_hci_s *s;
+ int fd = -1;
+# ifdef CONFIG_BLUEZ
+ int dev_id = hci_devid(id);
+ struct hci_filter flt;
+
+ if (dev_id < 0) {
+ fprintf(stderr, "qemu: `%s' not available\n", id);
+ return 0;
+ }
+
+ fd = hci_open_dev(dev_id);
+
+ /* XXX: can we ensure nobody else has the device opened? */
+# endif
+
+ if (fd < 0) {
+ fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n",
+ id, strerror(errno), errno);
+ return NULL;
+ }
+
+# ifdef CONFIG_BLUEZ
+ hci_filter_clear(&flt);
+ hci_filter_all_ptypes(&flt);
+ hci_filter_all_events(&flt);
+
+ if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ fprintf(stderr, "qemu: Can't set HCI filter on socket (%i)\n", errno);
+ return 0;
+ }
+# endif
+
+ s = qemu_mallocz(sizeof(struct bt_host_hci_s));
+ s->fd = fd;
+ s->hci.cmd_send = bt_host_cmd;
+ s->hci.sco_send = bt_host_sco;
+ s->hci.acl_send = bt_host_acl;
+ s->hci.bdaddr_set = bt_host_bdaddr_set;
+
+ qemu_set_fd_handler(s->fd, bt_host_read, NULL, s);
+
+ return &s->hci;
+}
+#else
+struct HCIInfo *bt_host_hci(const char *id)
+{
+ fprintf(stderr, "qemu: bluetooth passthrough not supported (yet)\n");
+
+ return 0;
+}
+#endif
diff --git a/bt-host.h b/bt-host.h
new file mode 100644
index 0000000..f1eff65
--- /dev/null
+++ b/bt-host.h
@@ -0,0 +1,9 @@
+#ifndef BT_HOST_H
+#define BT_HOST_H
+
+struct HCIInfo;
+
+/* bt-host.c */
+struct HCIInfo *bt_host_hci(const char *id);
+
+#endif
diff --git a/bt-vhci.c b/bt-vhci.c
new file mode 100644
index 0000000..679c5e0
--- /dev/null
+++ b/bt-vhci.c
@@ -0,0 +1,168 @@
+/*
+ * Support for host VHCIs inside qemu scatternets.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "net.h"
+#include "hw/bt.h"
+
+#define VHCI_DEV "/dev/vhci"
+#define VHCI_UDEV "/dev/hci_vhci"
+
+struct bt_vhci_s {
+ int fd;
+ struct HCIInfo *info;
+
+ uint8_t hdr[4096];
+ int len;
+};
+
+static void vhci_read(void *opaque)
+{
+ struct bt_vhci_s *s = (struct bt_vhci_s *) opaque;
+ uint8_t *pkt;
+ int pktlen;
+
+ /* Seems that we can't read only the header first and then the amount
+ * of data indicated in the header because Linux will discard everything
+ * that's not been read in one go. */
+ s->len = read(s->fd, s->hdr, sizeof(s->hdr));
+
+ if (s->len < 0) {
+ fprintf(stderr, "qemu: error %i reading the PDU\n", errno);
+ return;
+ }
+
+ pkt = s->hdr;
+ while (s->len --)
+ switch (*pkt ++) {
+ case HCI_COMMAND_PKT:
+ if (s->len < 3)
+ goto bad_pkt;
+
+ pktlen = MIN(pkt[2] + 3, s->len);
+ s->info->cmd_send(s->info, pkt, pktlen);
+ s->len -= pktlen;
+ pkt += pktlen;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (s->len < 4)
+ goto bad_pkt;
+
+ pktlen = MIN(((pkt[3] << 8) | pkt[2]) + 4, s->len);
+ s->info->acl_send(s->info, pkt, pktlen);
+ s->len -= pktlen;
+ pkt += pktlen;
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (s->len < 3)
+ goto bad_pkt;
+
+ pktlen = MIN(pkt[2] + 3, s->len);
+ s->info->sco_send(s->info, pkt, pktlen);
+ s->len -= pktlen;
+ pkt += pktlen;
+ break;
+
+ default:
+ bad_pkt:
+ fprintf(stderr, "qemu: bad HCI packet type %02x\n", pkt[-1]);
+ }
+}
+
+static void vhci_host_send(void *opaque,
+ int type, const uint8_t *data, int len)
+{
+ struct bt_vhci_s *s = (struct bt_vhci_s *) opaque;
+#if 0
+ uint8_t pkt = type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = &pkt;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = (void *) data;
+ iv[1].iov_len = len;
+
+ while (writev(s->fd, iv, 2) < 0)
+ if (errno != EAGAIN && errno != EINTR) {
+ fprintf(stderr, "qemu: error %i writing bluetooth packet.\n",
+ errno);
+ return;
+ }
+#else
+ /* Apparently VHCI wants us to write everything in one chunk :-( */
+ static uint8_t buf[4096];
+
+ buf[0] = type;
+ memcpy(buf + 1, data, len);
+
+ while (write(s->fd, buf, len + 1) < 0)
+ if (errno != EAGAIN && errno != EINTR) {
+ fprintf(stderr, "qemu: error %i writing bluetooth packet.\n",
+ errno);
+ return;
+ }
+#endif
+}
+
+static void vhci_out_hci_packet_event(void *opaque,
+ const uint8_t *data, int len)
+{
+ vhci_host_send(opaque, HCI_EVENT_PKT, data, len);
+}
+
+static void vhci_out_hci_packet_acl(void *opaque,
+ const uint8_t *data, int len)
+{
+ vhci_host_send(opaque, HCI_ACLDATA_PKT, data, len);
+}
+
+void bt_vhci_init(struct HCIInfo *info)
+{
+ struct bt_vhci_s *s;
+ int err[2];
+ int fd;
+
+ fd = open(VHCI_DEV, O_RDWR);
+ err[0] = errno;
+ if (fd < 0) {
+ fd = open(VHCI_UDEV, O_RDWR);
+ err[1] = errno;
+ }
+
+ if (fd < 0) {
+ fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n",
+ VHCI_DEV, strerror(err[0]), err[0]);
+ fprintf(stderr, "qemu: Can't open `%s': %s (%i)\n",
+ VHCI_UDEV, strerror(err[1]), err[1]);
+ exit(-1);
+ }
+
+ s = qemu_mallocz(sizeof(struct bt_vhci_s));
+ s->fd = fd;
+ s->info = info ?: qemu_next_hci();
+ s->info->opaque = s;
+ s->info->evt_recv = vhci_out_hci_packet_event;
+ s->info->acl_recv = vhci_out_hci_packet_acl;
+
+ qemu_set_fd_handler(s->fd, vhci_read, NULL, s);
+}
diff --git a/buffered_file.c b/buffered_file.c
new file mode 100644
index 0000000..8435a31
--- /dev/null
+++ b/buffered_file.c
@@ -0,0 +1,282 @@
+/*
+ * QEMU buffered QEMUFile
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "buffered_file.h"
+
+//#define DEBUG_BUFFERED_FILE
+
+typedef struct QEMUFileBuffered
+{
+ BufferedPutFunc *put_buffer;
+ BufferedPutReadyFunc *put_ready;
+ BufferedWaitForUnfreezeFunc *wait_for_unfreeze;
+ BufferedCloseFunc *close;
+ void *opaque;
+ QEMUFile *file;
+ int has_error;
+ int freeze_output;
+ size_t bytes_xfer;
+ size_t xfer_limit;
+ uint8_t *buffer;
+ size_t buffer_size;
+ size_t buffer_capacity;
+ QEMUTimer *timer;
+} QEMUFileBuffered;
+
+#ifdef DEBUG_BUFFERED_FILE
+#define DPRINTF(fmt, ...) \
+ do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static void buffered_append(QEMUFileBuffered *s,
+ const uint8_t *buf, size_t size)
+{
+ if (size > (s->buffer_capacity - s->buffer_size)) {
+ void *tmp;
+
+ DPRINTF("increasing buffer capacity from %zu by %zu\n",
+ s->buffer_capacity, size + 1024);
+
+ s->buffer_capacity += size + 1024;
+
+ tmp = qemu_realloc(s->buffer, s->buffer_capacity);
+ if (tmp == NULL) {
+ fprintf(stderr, "qemu file buffer expansion failed\n");
+ exit(1);
+ }
+
+ s->buffer = tmp;
+ }
+
+ memcpy(s->buffer + s->buffer_size, buf, size);
+ s->buffer_size += size;
+}
+
+static void buffered_flush(QEMUFileBuffered *s)
+{
+ size_t offset = 0;
+
+ if (s->has_error) {
+ DPRINTF("flush when error, bailing\n");
+ return;
+ }
+
+ DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
+
+ while (offset < s->buffer_size) {
+ ssize_t ret;
+
+ ret = s->put_buffer(s->opaque, s->buffer + offset,
+ s->buffer_size - offset);
+ if (ret == -EAGAIN) {
+ DPRINTF("backend not ready, freezing\n");
+ s->freeze_output = 1;
+ break;
+ }
+
+ if (ret <= 0) {
+ DPRINTF("error flushing data, %zd\n", ret);
+ s->has_error = 1;
+ break;
+ } else {
+ DPRINTF("flushed %zd byte(s)\n", ret);
+ offset += ret;
+ }
+ }
+
+ DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
+ memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
+ s->buffer_size -= offset;
+}
+
+static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileBuffered *s = opaque;
+ int offset = 0;
+ ssize_t ret;
+
+ DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
+
+ if (s->has_error) {
+ DPRINTF("flush when error, bailing\n");
+ return -EINVAL;
+ }
+
+ DPRINTF("unfreezing output\n");
+ s->freeze_output = 0;
+
+ buffered_flush(s);
+
+ while (!s->freeze_output && offset < size) {
+ if (s->bytes_xfer > s->xfer_limit) {
+ DPRINTF("transfer limit exceeded when putting\n");
+ break;
+ }
+
+ ret = s->put_buffer(s->opaque, buf + offset, size - offset);
+ if (ret == -EAGAIN) {
+ DPRINTF("backend not ready, freezing\n");
+ s->freeze_output = 1;
+ break;
+ }
+
+ if (ret <= 0) {
+ DPRINTF("error putting\n");
+ s->has_error = 1;
+ offset = -EINVAL;
+ break;
+ }
+
+ DPRINTF("put %zd byte(s)\n", ret);
+ offset += ret;
+ s->bytes_xfer += ret;
+ }
+
+ if (offset >= 0) {
+ DPRINTF("buffering %d bytes\n", size - offset);
+ buffered_append(s, buf + offset, size - offset);
+ offset = size;
+ }
+
+ if (pos == 0 && size == 0) {
+ DPRINTF("file is ready\n");
+ if (s->bytes_xfer <= s->xfer_limit) {
+ DPRINTF("notifying client\n");
+ s->put_ready(s->opaque);
+ }
+ }
+
+ return offset;
+}
+
+static int buffered_close(void *opaque)
+{
+ QEMUFileBuffered *s = opaque;
+ int ret;
+
+ DPRINTF("closing\n");
+
+ while (!s->has_error && s->buffer_size) {
+ buffered_flush(s);
+ if (s->freeze_output)
+ s->wait_for_unfreeze(s);
+ }
+
+ ret = s->close(s->opaque);
+
+ qemu_del_timer(s->timer);
+ qemu_free_timer(s->timer);
+ qemu_free(s->buffer);
+ qemu_free(s);
+
+ return ret;
+}
+
+static int buffered_rate_limit(void *opaque)
+{
+ QEMUFileBuffered *s = opaque;
+
+ if (s->has_error)
+ return 0;
+
+ if (s->freeze_output)
+ return 1;
+
+ if (s->bytes_xfer > s->xfer_limit)
+ return 1;
+
+ return 0;
+}
+
+static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate)
+{
+ QEMUFileBuffered *s = opaque;
+ if (s->has_error)
+ goto out;
+
+ if (new_rate > SIZE_MAX) {
+ new_rate = SIZE_MAX;
+ }
+
+ s->xfer_limit = new_rate / 10;
+
+out:
+ return s->xfer_limit;
+}
+
+static int64_t buffered_get_rate_limit(void *opaque)
+{
+ QEMUFileBuffered *s = opaque;
+
+ return s->xfer_limit;
+}
+
+static void buffered_rate_tick(void *opaque)
+{
+ QEMUFileBuffered *s = opaque;
+
+ if (s->has_error) {
+ buffered_close(s);
+ return;
+ }
+
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
+
+ if (s->freeze_output)
+ return;
+
+ s->bytes_xfer = 0;
+
+ buffered_flush(s);
+
+ /* Add some checks around this */
+ s->put_ready(s->opaque);
+}
+
+QEMUFile *qemu_fopen_ops_buffered(void *opaque,
+ size_t bytes_per_sec,
+ BufferedPutFunc *put_buffer,
+ BufferedPutReadyFunc *put_ready,
+ BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
+ BufferedCloseFunc *close)
+{
+ QEMUFileBuffered *s;
+
+ s = qemu_mallocz(sizeof(*s));
+
+ s->opaque = opaque;
+ s->xfer_limit = bytes_per_sec / 10;
+ s->put_buffer = put_buffer;
+ s->put_ready = put_ready;
+ s->wait_for_unfreeze = wait_for_unfreeze;
+ s->close = close;
+
+ s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
+ buffered_close, buffered_rate_limit,
+ buffered_set_rate_limit,
+ buffered_get_rate_limit);
+
+ s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s);
+
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
+
+ return s->file;
+}
diff --git a/buffered_file.h b/buffered_file.h
new file mode 100644
index 0000000..98d358b
--- /dev/null
+++ b/buffered_file.h
@@ -0,0 +1,30 @@
+/*
+ * QEMU buffered QEMUFile
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_BUFFERED_FILE_H
+#define QEMU_BUFFERED_FILE_H
+
+#include "hw/hw.h"
+
+typedef ssize_t (BufferedPutFunc)(void *opaque, const void *data, size_t size);
+typedef void (BufferedPutReadyFunc)(void *opaque);
+typedef void (BufferedWaitForUnfreezeFunc)(void *opaque);
+typedef int (BufferedCloseFunc)(void *opaque);
+
+QEMUFile *qemu_fopen_ops_buffered(void *opaque, size_t xfer_limit,
+ BufferedPutFunc *put_buffer,
+ BufferedPutReadyFunc *put_ready,
+ BufferedWaitForUnfreezeFunc *wait_for_unfreeze,
+ BufferedCloseFunc *close);
+
+#endif
diff --git a/cache-utils.c b/cache-utils.c
new file mode 100644
index 0000000..2db5af2
--- /dev/null
+++ b/cache-utils.c
@@ -0,0 +1,97 @@
+#include "cache-utils.h"
+
+#if defined(_ARCH_PPC)
+struct qemu_cache_conf qemu_cache_conf = {
+ .dcache_bsize = 16,
+ .icache_bsize = 16
+};
+
+#if defined _AIX
+#include <sys/systemcfg.h>
+
+static void ppc_init_cacheline_sizes(void)
+{
+ qemu_cache_conf.icache_bsize = _system_configuration.icache_line;
+ qemu_cache_conf.dcache_bsize = _system_configuration.dcache_line;
+}
+
+#elif defined __linux__
+
+#define QEMU_AT_NULL 0
+#define QEMU_AT_DCACHEBSIZE 19
+#define QEMU_AT_ICACHEBSIZE 20
+
+static void ppc_init_cacheline_sizes(char **envp)
+{
+ unsigned long *auxv;
+
+ while (*envp++);
+
+ for (auxv = (unsigned long *) envp; *auxv != QEMU_AT_NULL; auxv += 2) {
+ switch (*auxv) {
+ case QEMU_AT_DCACHEBSIZE: qemu_cache_conf.dcache_bsize = auxv[1]; break;
+ case QEMU_AT_ICACHEBSIZE: qemu_cache_conf.icache_bsize = auxv[1]; break;
+ default: break;
+ }
+ }
+}
+
+#elif defined __APPLE__
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+static void ppc_init_cacheline_sizes(void)
+{
+ size_t len;
+ unsigned cacheline;
+ int name[2] = { CTL_HW, HW_CACHELINE };
+
+ len = sizeof(cacheline);
+ if (sysctl(name, 2, &cacheline, &len, NULL, 0)) {
+ perror("sysctl CTL_HW HW_CACHELINE failed");
+ } else {
+ qemu_cache_conf.dcache_bsize = cacheline;
+ qemu_cache_conf.icache_bsize = cacheline;
+ }
+}
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+static void ppc_init_cacheline_sizes(void)
+{
+ size_t len = 4;
+ unsigned cacheline;
+
+ if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) {
+ fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ qemu_cache_conf.dcache_bsize = cacheline;
+ qemu_cache_conf.icache_bsize = cacheline;
+}
+#endif
+
+#ifdef __linux__
+void qemu_cache_utils_init(char **envp)
+{
+ ppc_init_cacheline_sizes(envp);
+}
+#else
+void qemu_cache_utils_init(char **envp)
+{
+ (void) envp;
+ ppc_init_cacheline_sizes();
+}
+#endif
+
+#endif /* _ARCH_PPC */
diff --git a/cache-utils.h b/cache-utils.h
new file mode 100644
index 0000000..ee0baef
--- /dev/null
+++ b/cache-utils.h
@@ -0,0 +1,62 @@
+#ifndef QEMU_CACHE_UTILS_H
+#define QEMU_CACHE_UTILS_H
+
+#if defined(_ARCH_PPC)
+struct qemu_cache_conf {
+ unsigned long dcache_bsize;
+ unsigned long icache_bsize;
+};
+
+extern struct qemu_cache_conf qemu_cache_conf;
+
+void qemu_cache_utils_init(char **envp);
+
+/* mildly adjusted code from tcg-dyngen.c */
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ unsigned long p, start1, stop1;
+ unsigned long dsize = qemu_cache_conf.dcache_bsize;
+ unsigned long isize = qemu_cache_conf.icache_bsize;
+
+ start1 = start & ~(dsize - 1);
+ stop1 = (stop + dsize - 1) & ~(dsize - 1);
+ for (p = start1; p < stop1; p += dsize) {
+ asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
+ }
+ asm volatile ("sync" : : : "memory");
+
+ start &= start & ~(isize - 1);
+ stop1 = (stop + isize - 1) & ~(isize - 1);
+ for (p = start1; p < stop1; p += isize) {
+ asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
+ }
+ asm volatile ("sync" : : : "memory");
+ asm volatile ("isync" : : : "memory");
+}
+
+/*
+ * Is this correct for PPC?
+ */
+static inline void dma_flush_range(unsigned long start, unsigned long stop)
+{
+}
+
+#elif defined(__ia64__)
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ while (start < stop) {
+ asm volatile ("fc %0" :: "r"(start));
+ start += 32;
+ }
+ asm volatile (";;sync.i;;srlz.i;;");
+}
+#define dma_flush_range(start, end) flush_icache_range(start, end)
+#define qemu_cache_utils_init(envp) do { (void) (envp); } while (0)
+#else
+static inline void dma_flush_range(unsigned long start, unsigned long stop)
+{
+}
+#define qemu_cache_utils_init(envp) do { (void) (envp); } while (0)
+#endif
+
+#endif /* QEMU_CACHE_UTILS_H */
diff --git a/check-qdict.c b/check-qdict.c
new file mode 100644
index 0000000..6afce5a
--- /dev/null
+++ b/check-qdict.c
@@ -0,0 +1,401 @@
+/*
+ * QDict unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <check.h>
+
+#include "qint.h"
+#include "qdict.h"
+#include "qstring.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+START_TEST(qdict_new_test)
+{
+ QDict *qdict;
+
+ qdict = qdict_new();
+ fail_unless(qdict != NULL);
+ fail_unless(qdict_size(qdict) == 0);
+ fail_unless(qdict->base.refcnt == 1);
+ fail_unless(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
+
+ // destroy doesn't exit yet
+ free(qdict);
+}
+END_TEST
+
+START_TEST(qdict_put_obj_test)
+{
+ QInt *qi;
+ QDict *qdict;
+ QDictEntry *ent;
+ const int num = 42;
+
+ qdict = qdict_new();
+
+ // key "" will have tdb hash 12345
+ qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num)));
+
+ fail_unless(qdict_size(qdict) == 1);
+ ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
+ qi = qobject_to_qint(ent->value);
+ fail_unless(qint_get_int(qi) == num);
+
+ // destroy doesn't exit yet
+ QDECREF(qi);
+ qemu_free(ent->key);
+ qemu_free(ent);
+ qemu_free(qdict);
+}
+END_TEST
+
+START_TEST(qdict_destroy_simple_test)
+{
+ QDict *qdict;
+
+ qdict = qdict_new();
+ qdict_put_obj(qdict, "num", QOBJECT(qint_from_int(0)));
+ qdict_put_obj(qdict, "str", QOBJECT(qstring_from_str("foo")));
+
+ QDECREF(qdict);
+}
+END_TEST
+
+static QDict *tests_dict = NULL;
+
+static void qdict_setup(void)
+{
+ tests_dict = qdict_new();
+ fail_unless(tests_dict != NULL);
+}
+
+static void qdict_teardown(void)
+{
+ QDECREF(tests_dict);
+ tests_dict = NULL;
+}
+
+START_TEST(qdict_get_test)
+{
+ QInt *qi;
+ QObject *obj;
+ const int value = -42;
+ const char *key = "test";
+
+ qdict_put(tests_dict, key, qint_from_int(value));
+
+ obj = qdict_get(tests_dict, key);
+ fail_unless(obj != NULL);
+
+ qi = qobject_to_qint(obj);
+ fail_unless(qint_get_int(qi) == value);
+}
+END_TEST
+
+START_TEST(qdict_get_int_test)
+{
+ int ret;
+ const int value = 100;
+ const char *key = "int";
+
+ qdict_put(tests_dict, key, qint_from_int(value));
+
+ ret = qdict_get_int(tests_dict, key);
+ fail_unless(ret == value);
+}
+END_TEST
+
+START_TEST(qdict_get_try_int_test)
+{
+ int ret;
+ const int value = 100;
+ const char *key = "int";
+
+ qdict_put(tests_dict, key, qint_from_int(value));
+
+ ret = qdict_get_try_int(tests_dict, key, 0);
+ fail_unless(ret == value);
+}
+END_TEST
+
+START_TEST(qdict_get_str_test)
+{
+ const char *p;
+ const char *key = "key";
+ const char *str = "string";
+
+ qdict_put(tests_dict, key, qstring_from_str(str));
+
+ p = qdict_get_str(tests_dict, key);
+ fail_unless(p != NULL);
+ fail_unless(strcmp(p, str) == 0);
+}
+END_TEST
+
+START_TEST(qdict_get_try_str_test)
+{
+ const char *p;
+ const char *key = "key";
+ const char *str = "string";
+
+ qdict_put(tests_dict, key, qstring_from_str(str));
+
+ p = qdict_get_try_str(tests_dict, key);
+ fail_unless(p != NULL);
+ fail_unless(strcmp(p, str) == 0);
+}
+END_TEST
+
+START_TEST(qdict_haskey_not_test)
+{
+ fail_unless(qdict_haskey(tests_dict, "test") == 0);
+}
+END_TEST
+
+START_TEST(qdict_haskey_test)
+{
+ const char *key = "test";
+
+ qdict_put(tests_dict, key, qint_from_int(0));
+ fail_unless(qdict_haskey(tests_dict, key) == 1);
+}
+END_TEST
+
+START_TEST(qdict_del_test)
+{
+ const char *key = "key test";
+
+ qdict_put(tests_dict, key, qstring_from_str("foo"));
+ fail_unless(qdict_size(tests_dict) == 1);
+
+ qdict_del(tests_dict, key);
+
+ fail_unless(qdict_size(tests_dict) == 0);
+ fail_unless(qdict_haskey(tests_dict, key) == 0);
+}
+END_TEST
+
+START_TEST(qobject_to_qdict_test)
+{
+ fail_unless(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
+}
+END_TEST
+
+START_TEST(qdict_iterapi_test)
+{
+ int count;
+ const QDictEntry *ent;
+
+ fail_unless(qdict_first(tests_dict) == NULL);
+
+ qdict_put(tests_dict, "key1", qint_from_int(1));
+ qdict_put(tests_dict, "key2", qint_from_int(2));
+ qdict_put(tests_dict, "key3", qint_from_int(3));
+
+ count = 0;
+ for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
+ fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
+ count++;
+ }
+
+ fail_unless(count == qdict_size(tests_dict));
+
+ /* Do it again to test restarting */
+ count = 0;
+ for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
+ fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
+ count++;
+ }
+
+ fail_unless(count == qdict_size(tests_dict));
+}
+END_TEST
+
+/*
+ * Errors test-cases
+ */
+
+START_TEST(qdict_put_exists_test)
+{
+ int value;
+ const char *key = "exists";
+
+ qdict_put(tests_dict, key, qint_from_int(1));
+ qdict_put(tests_dict, key, qint_from_int(2));
+
+ value = qdict_get_int(tests_dict, key);
+ fail_unless(value == 2);
+
+ fail_unless(qdict_size(tests_dict) == 1);
+}
+END_TEST
+
+START_TEST(qdict_get_not_exists_test)
+{
+ fail_unless(qdict_get(tests_dict, "foo") == NULL);
+}
+END_TEST
+
+/*
+ * Stress test-case
+ *
+ * This is a lot big for a unit-test, but there is no other place
+ * to have it.
+ */
+
+static void remove_dots(char *string)
+{
+ char *p = strchr(string, ':');
+ if (p)
+ *p = '\0';
+}
+
+static QString *read_line(FILE *file, char *key)
+{
+ char value[128];
+
+ if (fscanf(file, "%s%s", key, value) == EOF)
+ return NULL;
+ remove_dots(key);
+ return qstring_from_str(value);
+}
+
+#define reset_file(file) fseek(file, 0L, SEEK_SET)
+
+START_TEST(qdict_stress_test)
+{
+ size_t lines;
+ char key[128];
+ FILE *test_file;
+ QDict *qdict;
+ QString *value;
+ const char *test_file_path = "qdict-test-data.txt";
+
+ test_file = fopen(test_file_path, "r");
+ fail_unless(test_file != NULL);
+
+ // Create the dict
+ qdict = qdict_new();
+ fail_unless(qdict != NULL);
+
+ // Add everything from the test file
+ for (lines = 0;; lines++) {
+ value = read_line(test_file, key);
+ if (!value)
+ break;
+
+ qdict_put(qdict, key, value);
+ }
+ fail_unless(qdict_size(qdict) == lines);
+
+ // Check if everything is really in there
+ reset_file(test_file);
+ for (;;) {
+ const char *str1, *str2;
+
+ value = read_line(test_file, key);
+ if (!value)
+ break;
+
+ str1 = qstring_get_str(value);
+
+ str2 = qdict_get_str(qdict, key);
+ fail_unless(str2 != NULL);
+
+ fail_unless(strcmp(str1, str2) == 0);
+
+ QDECREF(value);
+ }
+
+ // Delete everything
+ reset_file(test_file);
+ for (;;) {
+ value = read_line(test_file, key);
+ if (!value)
+ break;
+
+ qdict_del(qdict, key);
+ QDECREF(value);
+
+ fail_unless(qdict_haskey(qdict, key) == 0);
+ }
+ fclose(test_file);
+
+ fail_unless(qdict_size(qdict) == 0);
+ QDECREF(qdict);
+}
+END_TEST
+
+static Suite *qdict_suite(void)
+{
+ Suite *s;
+ TCase *qdict_public_tcase;
+ TCase *qdict_public2_tcase;
+ TCase *qdict_stress_tcase;
+ TCase *qdict_errors_tcase;
+
+ s = suite_create("QDict test-suite");
+
+ qdict_public_tcase = tcase_create("Public Interface");
+ suite_add_tcase(s, qdict_public_tcase);
+ tcase_add_test(qdict_public_tcase, qdict_new_test);
+ tcase_add_test(qdict_public_tcase, qdict_put_obj_test);
+ tcase_add_test(qdict_public_tcase, qdict_destroy_simple_test);
+
+ /* Continue, but now with fixtures */
+ qdict_public2_tcase = tcase_create("Public Interface (2)");
+ suite_add_tcase(s, qdict_public2_tcase);
+ tcase_add_checked_fixture(qdict_public2_tcase, qdict_setup, qdict_teardown);
+ tcase_add_test(qdict_public2_tcase, qdict_get_test);
+ tcase_add_test(qdict_public2_tcase, qdict_get_int_test);
+ tcase_add_test(qdict_public2_tcase, qdict_get_try_int_test);
+ tcase_add_test(qdict_public2_tcase, qdict_get_str_test);
+ tcase_add_test(qdict_public2_tcase, qdict_get_try_str_test);
+ tcase_add_test(qdict_public2_tcase, qdict_haskey_not_test);
+ tcase_add_test(qdict_public2_tcase, qdict_haskey_test);
+ tcase_add_test(qdict_public2_tcase, qdict_del_test);
+ tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test);
+ tcase_add_test(qdict_public2_tcase, qdict_iterapi_test);
+
+ qdict_errors_tcase = tcase_create("Errors");
+ suite_add_tcase(s, qdict_errors_tcase);
+ tcase_add_checked_fixture(qdict_errors_tcase, qdict_setup, qdict_teardown);
+ tcase_add_test(qdict_errors_tcase, qdict_put_exists_test);
+ tcase_add_test(qdict_errors_tcase, qdict_get_not_exists_test);
+
+ /* The Big one */
+ qdict_stress_tcase = tcase_create("Stress Test");
+ suite_add_tcase(s, qdict_stress_tcase);
+ tcase_add_test(qdict_stress_tcase, qdict_stress_test);
+
+ return s;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = qdict_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/check-qfloat.c b/check-qfloat.c
new file mode 100644
index 0000000..b71d983
--- /dev/null
+++ b/check-qfloat.c
@@ -0,0 +1,76 @@
+/*
+ * QFloat unit-tests.
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#include <check.h>
+
+#include "qfloat.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+START_TEST(qfloat_from_double_test)
+{
+ QFloat *qf;
+ const double value = -42.23423;
+
+ qf = qfloat_from_double(value);
+ fail_unless(qf != NULL);
+ fail_unless(qf->value == value);
+ fail_unless(qf->base.refcnt == 1);
+ fail_unless(qobject_type(QOBJECT(qf)) == QTYPE_QFLOAT);
+
+ // destroy doesn't exit yet
+ qemu_free(qf);
+}
+END_TEST
+
+START_TEST(qfloat_destroy_test)
+{
+ QFloat *qf = qfloat_from_double(0.0);
+ QDECREF(qf);
+}
+END_TEST
+
+static Suite *qfloat_suite(void)
+{
+ Suite *s;
+ TCase *qfloat_public_tcase;
+
+ s = suite_create("QFloat test-suite");
+
+ qfloat_public_tcase = tcase_create("Public Interface");
+ suite_add_tcase(s, qfloat_public_tcase);
+ tcase_add_test(qfloat_public_tcase, qfloat_from_double_test);
+ tcase_add_test(qfloat_public_tcase, qfloat_destroy_test);
+
+ return s;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = qfloat_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/check-qint.c b/check-qint.c
new file mode 100644
index 0000000..f3b0316
--- /dev/null
+++ b/check-qint.c
@@ -0,0 +1,113 @@
+/*
+ * QInt unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <check.h>
+
+#include "qint.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+START_TEST(qint_from_int_test)
+{
+ QInt *qi;
+ const int value = -42;
+
+ qi = qint_from_int(value);
+ fail_unless(qi != NULL);
+ fail_unless(qi->value == value);
+ fail_unless(qi->base.refcnt == 1);
+ fail_unless(qobject_type(QOBJECT(qi)) == QTYPE_QINT);
+
+ // destroy doesn't exit yet
+ qemu_free(qi);
+}
+END_TEST
+
+START_TEST(qint_destroy_test)
+{
+ QInt *qi = qint_from_int(0);
+ QDECREF(qi);
+}
+END_TEST
+
+START_TEST(qint_from_int64_test)
+{
+ QInt *qi;
+ const int64_t value = 0x1234567890abcdefLL;
+
+ qi = qint_from_int(value);
+ fail_unless((int64_t) qi->value == value);
+
+ QDECREF(qi);
+}
+END_TEST
+
+START_TEST(qint_get_int_test)
+{
+ QInt *qi;
+ const int value = 123456;
+
+ qi = qint_from_int(value);
+ fail_unless(qint_get_int(qi) == value);
+
+ QDECREF(qi);
+}
+END_TEST
+
+START_TEST(qobject_to_qint_test)
+{
+ QInt *qi;
+
+ qi = qint_from_int(0);
+ fail_unless(qobject_to_qint(QOBJECT(qi)) == qi);
+
+ QDECREF(qi);
+}
+END_TEST
+
+static Suite *qint_suite(void)
+{
+ Suite *s;
+ TCase *qint_public_tcase;
+
+ s = suite_create("QInt test-suite");
+
+ qint_public_tcase = tcase_create("Public Interface");
+ suite_add_tcase(s, qint_public_tcase);
+ tcase_add_test(qint_public_tcase, qint_from_int_test);
+ tcase_add_test(qint_public_tcase, qint_destroy_test);
+ tcase_add_test(qint_public_tcase, qint_from_int64_test);
+ tcase_add_test(qint_public_tcase, qint_get_int_test);
+ tcase_add_test(qint_public_tcase, qobject_to_qint_test);
+
+ return s;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = qint_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/check-qjson.c b/check-qjson.c
new file mode 100644
index 0000000..64fcdcb
--- /dev/null
+++ b/check-qjson.c
@@ -0,0 +1,794 @@
+/*
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#include <check.h>
+
+#include "qstring.h"
+#include "qint.h"
+#include "qdict.h"
+#include "qlist.h"
+#include "qfloat.h"
+#include "qbool.h"
+#include "qjson.h"
+
+#include "qemu-common.h"
+
+START_TEST(escaped_string)
+{
+ int i;
+ struct {
+ const char *encoded;
+ const char *decoded;
+ int skip;
+ } test_cases[] = {
+ { "\"\\b\"", "\b" },
+ { "\"\\f\"", "\f" },
+ { "\"\\n\"", "\n" },
+ { "\"\\r\"", "\r" },
+ { "\"\\t\"", "\t" },
+ { "\"\\/\"", "\\/" },
+ { "\"\\\\\"", "\\" },
+ { "\"\\\"\"", "\"" },
+ { "\"hello world \\\"embedded string\\\"\"",
+ "hello world \"embedded string\"" },
+ { "\"hello world\\nwith new line\"", "hello world\nwith new line" },
+ { "\"single byte utf-8 \\u0020\"", "single byte utf-8 ", .skip = 1 },
+ { "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" },
+ { "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" },
+ {}
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QSTRING);
+
+ str = qobject_to_qstring(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0,
+ "%s != %s\n", qstring_get_str(str), test_cases[i].decoded);
+
+ if (test_cases[i].skip == 0) {
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str),test_cases[i].encoded) == 0,
+ "%s != %s\n", qstring_get_str(str),
+ test_cases[i].encoded);
+
+ qobject_decref(obj);
+ }
+
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(simple_string)
+{
+ int i;
+ struct {
+ const char *encoded;
+ const char *decoded;
+ } test_cases[] = {
+ { "\"hello world\"", "hello world" },
+ { "\"the quick brown fox jumped over the fence\"",
+ "the quick brown fox jumped over the fence" },
+ {}
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QSTRING);
+
+ str = qobject_to_qstring(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+
+ qobject_decref(obj);
+
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(single_quote_string)
+{
+ int i;
+ struct {
+ const char *encoded;
+ const char *decoded;
+ } test_cases[] = {
+ { "'hello world'", "hello world" },
+ { "'the quick brown fox \\' jumped over the fence'",
+ "the quick brown fox ' jumped over the fence" },
+ {}
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QSTRING);
+
+ str = qobject_to_qstring(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(vararg_string)
+{
+ int i;
+ struct {
+ const char *decoded;
+ } test_cases[] = {
+ { "hello world" },
+ { "the quick brown fox jumped over the fence" },
+ {}
+ };
+
+ for (i = 0; test_cases[i].decoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_jsonf("%s", test_cases[i].decoded);
+
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QSTRING);
+
+ str = qobject_to_qstring(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
+
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(simple_number)
+{
+ int i;
+ struct {
+ const char *encoded;
+ int64_t decoded;
+ int skip;
+ } test_cases[] = {
+ { "0", 0 },
+ { "1234", 1234 },
+ { "1", 1 },
+ { "-32", -32 },
+ { "-0", 0, .skip = 1 },
+ { },
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QInt *qint;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QINT);
+
+ qint = qobject_to_qint(obj);
+ fail_unless(qint_get_int(qint) == test_cases[i].decoded);
+ if (test_cases[i].skip == 0) {
+ QString *str;
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+ QDECREF(str);
+ }
+
+ QDECREF(qint);
+ }
+}
+END_TEST
+
+START_TEST(float_number)
+{
+ int i;
+ struct {
+ const char *encoded;
+ double decoded;
+ int skip;
+ } test_cases[] = {
+ { "32.43", 32.43 },
+ { "0.222", 0.222 },
+ { "-32.12313", -32.12313 },
+ { "-32.20e-10", -32.20e-10, .skip = 1 },
+ { },
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QFloat *qfloat;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
+
+ qfloat = qobject_to_qfloat(obj);
+ fail_unless(qfloat_get_double(qfloat) == test_cases[i].decoded);
+
+ if (test_cases[i].skip == 0) {
+ QString *str;
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
+ QDECREF(str);
+ }
+
+ QDECREF(qfloat);
+ }
+}
+END_TEST
+
+START_TEST(vararg_number)
+{
+ QObject *obj;
+ QInt *qint;
+ QFloat *qfloat;
+ int value = 0x2342;
+ int64_t value64 = 0x2342342343LL;
+ double valuef = 2.323423423;
+
+ obj = qobject_from_jsonf("%d", value);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QINT);
+
+ qint = qobject_to_qint(obj);
+ fail_unless(qint_get_int(qint) == value);
+
+ QDECREF(qint);
+
+ obj = qobject_from_jsonf("%" PRId64, value64);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QINT);
+
+ qint = qobject_to_qint(obj);
+ fail_unless(qint_get_int(qint) == value64);
+
+ QDECREF(qint);
+
+ obj = qobject_from_jsonf("%f", valuef);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
+
+ qfloat = qobject_to_qfloat(obj);
+ fail_unless(qfloat_get_double(qfloat) == valuef);
+
+ QDECREF(qfloat);
+}
+END_TEST
+
+START_TEST(keyword_literal)
+{
+ QObject *obj;
+ QBool *qbool;
+ QString *str;
+
+ obj = qobject_from_json("true");
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QBOOL);
+
+ qbool = qobject_to_qbool(obj);
+ fail_unless(qbool_get_int(qbool) != 0);
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), "true") == 0);
+ QDECREF(str);
+
+ QDECREF(qbool);
+
+ obj = qobject_from_json("false");
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QBOOL);
+
+ qbool = qobject_to_qbool(obj);
+ fail_unless(qbool_get_int(qbool) == 0);
+
+ str = qobject_to_json(obj);
+ fail_unless(strcmp(qstring_get_str(str), "false") == 0);
+ QDECREF(str);
+
+ QDECREF(qbool);
+
+ obj = qobject_from_jsonf("%i", false);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QBOOL);
+
+ qbool = qobject_to_qbool(obj);
+ fail_unless(qbool_get_int(qbool) == 0);
+
+ QDECREF(qbool);
+
+ obj = qobject_from_jsonf("%i", true);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QBOOL);
+
+ qbool = qobject_to_qbool(obj);
+ fail_unless(qbool_get_int(qbool) != 0);
+
+ QDECREF(qbool);
+}
+END_TEST
+
+typedef struct LiteralQDictEntry LiteralQDictEntry;
+typedef struct LiteralQObject LiteralQObject;
+
+struct LiteralQObject
+{
+ int type;
+ union {
+ int64_t qint;
+ const char *qstr;
+ LiteralQDictEntry *qdict;
+ LiteralQObject *qlist;
+ } value;
+};
+
+struct LiteralQDictEntry
+{
+ const char *key;
+ LiteralQObject value;
+};
+
+#define QLIT_QINT(val) (LiteralQObject){.type = QTYPE_QINT, .value.qint = (val)}
+#define QLIT_QSTR(val) (LiteralQObject){.type = QTYPE_QSTRING, .value.qstr = (val)}
+#define QLIT_QDICT(val) (LiteralQObject){.type = QTYPE_QDICT, .value.qdict = (val)}
+#define QLIT_QLIST(val) (LiteralQObject){.type = QTYPE_QLIST, .value.qlist = (val)}
+
+typedef struct QListCompareHelper
+{
+ int index;
+ LiteralQObject *objs;
+ int result;
+} QListCompareHelper;
+
+static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs);
+
+static void compare_helper(QObject *obj, void *opaque)
+{
+ QListCompareHelper *helper = opaque;
+
+ if (helper->result == 0) {
+ return;
+ }
+
+ if (helper->objs[helper->index].type == QTYPE_NONE) {
+ helper->result = 0;
+ return;
+ }
+
+ helper->result = compare_litqobj_to_qobj(&helper->objs[helper->index++], obj);
+}
+
+static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
+{
+ if (lhs->type != qobject_type(rhs)) {
+ return 0;
+ }
+
+ switch (lhs->type) {
+ case QTYPE_QINT:
+ return lhs->value.qint == qint_get_int(qobject_to_qint(rhs));
+ case QTYPE_QSTRING:
+ return (strcmp(lhs->value.qstr, qstring_get_str(qobject_to_qstring(rhs))) == 0);
+ case QTYPE_QDICT: {
+ int i;
+
+ for (i = 0; lhs->value.qdict[i].key; i++) {
+ QObject *obj = qdict_get(qobject_to_qdict(rhs), lhs->value.qdict[i].key);
+
+ if (!compare_litqobj_to_qobj(&lhs->value.qdict[i].value, obj)) {
+ return 0;
+ }
+ }
+
+ return 1;
+ }
+ case QTYPE_QLIST: {
+ QListCompareHelper helper;
+
+ helper.index = 0;
+ helper.objs = lhs->value.qlist;
+ helper.result = 1;
+
+ qlist_iter(qobject_to_qlist(rhs), compare_helper, &helper);
+
+ return helper.result;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+START_TEST(simple_dict)
+{
+ int i;
+ struct {
+ const char *encoded;
+ LiteralQObject decoded;
+ } test_cases[] = {
+ {
+ .encoded = "{\"foo\": 42, \"bar\": \"hello world\"}",
+ .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+ { "foo", QLIT_QINT(42) },
+ { "bar", QLIT_QSTR("hello world") },
+ { }
+ })),
+ }, {
+ .encoded = "{}",
+ .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+ { }
+ })),
+ }, {
+ .encoded = "{\"foo\": 43}",
+ .decoded = QLIT_QDICT(((LiteralQDictEntry[]){
+ { "foo", QLIT_QINT(43) },
+ { }
+ })),
+ },
+ { }
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QDICT);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+ str = qobject_to_json(obj);
+ qobject_decref(obj);
+
+ obj = qobject_from_json(qstring_get_str(str));
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QDICT);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+ qobject_decref(obj);
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(simple_list)
+{
+ int i;
+ struct {
+ const char *encoded;
+ LiteralQObject decoded;
+ } test_cases[] = {
+ {
+ .encoded = "[43,42]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(43),
+ QLIT_QINT(42),
+ { }
+ })),
+ },
+ {
+ .encoded = "[43]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(43),
+ { }
+ })),
+ },
+ {
+ .encoded = "[]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ { }
+ })),
+ },
+ {
+ .encoded = "[{}]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QDICT(((LiteralQDictEntry[]){
+ {},
+ })),
+ {},
+ })),
+ },
+ { }
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QLIST);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+ str = qobject_to_json(obj);
+ qobject_decref(obj);
+
+ obj = qobject_from_json(qstring_get_str(str));
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QLIST);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+ qobject_decref(obj);
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(simple_whitespace)
+{
+ int i;
+ struct {
+ const char *encoded;
+ LiteralQObject decoded;
+ } test_cases[] = {
+ {
+ .encoded = " [ 43 , 42 ]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(43),
+ QLIT_QINT(42),
+ { }
+ })),
+ },
+ {
+ .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(43),
+ QLIT_QDICT(((LiteralQDictEntry[]){
+ { "h", QLIT_QSTR("b") },
+ { }})),
+ QLIT_QLIST(((LiteralQObject[]){
+ { }})),
+ QLIT_QINT(42),
+ { }
+ })),
+ },
+ {
+ .encoded = " [ 43 , { 'h' : 'b' , 'a' : 32 }, [ ], 42 ]",
+ .decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(43),
+ QLIT_QDICT(((LiteralQDictEntry[]){
+ { "h", QLIT_QSTR("b") },
+ { "a", QLIT_QINT(32) },
+ { }})),
+ QLIT_QLIST(((LiteralQObject[]){
+ { }})),
+ QLIT_QINT(42),
+ { }
+ })),
+ },
+ { }
+ };
+
+ for (i = 0; test_cases[i].encoded; i++) {
+ QObject *obj;
+ QString *str;
+
+ obj = qobject_from_json(test_cases[i].encoded);
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QLIST);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+ str = qobject_to_json(obj);
+ qobject_decref(obj);
+
+ obj = qobject_from_json(qstring_get_str(str));
+ fail_unless(obj != NULL);
+ fail_unless(qobject_type(obj) == QTYPE_QLIST);
+
+ fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
+
+ qobject_decref(obj);
+ QDECREF(str);
+ }
+}
+END_TEST
+
+START_TEST(simple_varargs)
+{
+ QObject *embedded_obj;
+ QObject *obj;
+ LiteralQObject decoded = QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(1),
+ QLIT_QINT(2),
+ QLIT_QLIST(((LiteralQObject[]){
+ QLIT_QINT(32),
+ QLIT_QINT(42),
+ {}})),
+ {}}));
+
+ embedded_obj = qobject_from_json("[32, 42]");
+ fail_unless(embedded_obj != NULL);
+
+ obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj);
+ fail_unless(obj != NULL);
+
+ fail_unless(compare_litqobj_to_qobj(&decoded, obj) == 1);
+
+ qobject_decref(obj);
+}
+END_TEST
+
+START_TEST(empty_input)
+{
+ const char *empty = "";
+
+ QObject *obj = qobject_from_json(empty);
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_string)
+{
+ QObject *obj = qobject_from_json("\"abc");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_sq_string)
+{
+ QObject *obj = qobject_from_json("'abc");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_escape)
+{
+ QObject *obj = qobject_from_json("\"abc\\\"");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_array)
+{
+ QObject *obj = qobject_from_json("[32");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_array_comma)
+{
+ QObject *obj = qobject_from_json("[32,");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(invalid_array_comma)
+{
+ QObject *obj = qobject_from_json("[32,}");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_dict)
+{
+ QObject *obj = qobject_from_json("{'abc':32");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_dict_comma)
+{
+ QObject *obj = qobject_from_json("{'abc':32,");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+#if 0
+START_TEST(invalid_dict_comma)
+{
+ QObject *obj = qobject_from_json("{'abc':32,}");
+ fail_unless(obj == NULL);
+}
+END_TEST
+
+START_TEST(unterminated_literal)
+{
+ QObject *obj = qobject_from_json("nul");
+ fail_unless(obj == NULL);
+}
+END_TEST
+#endif
+
+static Suite *qjson_suite(void)
+{
+ Suite *suite;
+ TCase *string_literals, *number_literals, *keyword_literals;
+ TCase *dicts, *lists, *whitespace, *varargs, *errors;
+
+ string_literals = tcase_create("String Literals");
+ tcase_add_test(string_literals, simple_string);
+ tcase_add_test(string_literals, escaped_string);
+ tcase_add_test(string_literals, single_quote_string);
+ tcase_add_test(string_literals, vararg_string);
+
+ number_literals = tcase_create("Number Literals");
+ tcase_add_test(number_literals, simple_number);
+ tcase_add_test(number_literals, float_number);
+ tcase_add_test(number_literals, vararg_number);
+
+ keyword_literals = tcase_create("Keywords");
+ tcase_add_test(keyword_literals, keyword_literal);
+ dicts = tcase_create("Objects");
+ tcase_add_test(dicts, simple_dict);
+ lists = tcase_create("Lists");
+ tcase_add_test(lists, simple_list);
+
+ whitespace = tcase_create("Whitespace");
+ tcase_add_test(whitespace, simple_whitespace);
+
+ varargs = tcase_create("Varargs");
+ tcase_add_test(varargs, simple_varargs);
+
+ errors = tcase_create("Invalid JSON");
+ tcase_add_test(errors, empty_input);
+ tcase_add_test(errors, unterminated_string);
+ tcase_add_test(errors, unterminated_escape);
+ tcase_add_test(errors, unterminated_sq_string);
+ tcase_add_test(errors, unterminated_array);
+ tcase_add_test(errors, unterminated_array_comma);
+ tcase_add_test(errors, invalid_array_comma);
+ tcase_add_test(errors, unterminated_dict);
+ tcase_add_test(errors, unterminated_dict_comma);
+#if 0
+ /* FIXME: this print parse error messages on stderr. */
+ tcase_add_test(errors, invalid_dict_comma);
+ tcase_add_test(errors, unterminated_literal);
+#endif
+
+ suite = suite_create("QJSON test-suite");
+ suite_add_tcase(suite, string_literals);
+ suite_add_tcase(suite, number_literals);
+ suite_add_tcase(suite, keyword_literals);
+ suite_add_tcase(suite, dicts);
+ suite_add_tcase(suite, lists);
+ suite_add_tcase(suite, whitespace);
+ suite_add_tcase(suite, varargs);
+ suite_add_tcase(suite, errors);
+
+ return suite;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = qjson_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/check-qlist.c b/check-qlist.c
new file mode 100644
index 0000000..58984cb
--- /dev/null
+++ b/check-qlist.c
@@ -0,0 +1,153 @@
+/*
+ * QList unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <check.h>
+
+#include "qint.h"
+#include "qlist.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+START_TEST(qlist_new_test)
+{
+ QList *qlist;
+
+ qlist = qlist_new();
+ fail_unless(qlist != NULL);
+ fail_unless(qlist->base.refcnt == 1);
+ fail_unless(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
+
+ // destroy doesn't exist yet
+ qemu_free(qlist);
+}
+END_TEST
+
+START_TEST(qlist_append_test)
+{
+ QInt *qi;
+ QList *qlist;
+ QListEntry *entry;
+
+ qi = qint_from_int(42);
+
+ qlist = qlist_new();
+ qlist_append(qlist, qi);
+
+ entry = QTAILQ_FIRST(&qlist->head);
+ fail_unless(entry != NULL);
+ fail_unless(entry->value == QOBJECT(qi));
+
+ // destroy doesn't exist yet
+ QDECREF(qi);
+ qemu_free(entry);
+ qemu_free(qlist);
+}
+END_TEST
+
+START_TEST(qobject_to_qlist_test)
+{
+ QList *qlist;
+
+ qlist = qlist_new();
+
+ fail_unless(qobject_to_qlist(QOBJECT(qlist)) == qlist);
+
+ // destroy doesn't exist yet
+ qemu_free(qlist);
+}
+END_TEST
+
+START_TEST(qlist_destroy_test)
+{
+ int i;
+ QList *qlist;
+
+ qlist = qlist_new();
+
+ for (i = 0; i < 42; i++)
+ qlist_append(qlist, qint_from_int(i));
+
+ QDECREF(qlist);
+}
+END_TEST
+
+static int iter_called;
+static const int iter_max = 42;
+
+static void iter_func(QObject *obj, void *opaque)
+{
+ QInt *qi;
+
+ fail_unless(opaque == NULL);
+
+ qi = qobject_to_qint(obj);
+ fail_unless(qi != NULL);
+ fail_unless((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
+
+ iter_called++;
+}
+
+START_TEST(qlist_iter_test)
+{
+ int i;
+ QList *qlist;
+
+ qlist = qlist_new();
+
+ for (i = 0; i < iter_max; i++)
+ qlist_append(qlist, qint_from_int(i));
+
+ iter_called = 0;
+ qlist_iter(qlist, iter_func, NULL);
+
+ fail_unless(iter_called == iter_max);
+
+ QDECREF(qlist);
+}
+END_TEST
+
+static Suite *QList_suite(void)
+{
+ Suite *s;
+ TCase *qlist_public_tcase;
+
+ s = suite_create("QList suite");
+
+ qlist_public_tcase = tcase_create("Public Interface");
+ suite_add_tcase(s, qlist_public_tcase);
+ tcase_add_test(qlist_public_tcase, qlist_new_test);
+ tcase_add_test(qlist_public_tcase, qlist_append_test);
+ tcase_add_test(qlist_public_tcase, qobject_to_qlist_test);
+ tcase_add_test(qlist_public_tcase, qlist_destroy_test);
+ tcase_add_test(qlist_public_tcase, qlist_iter_test);
+
+ return s;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = QList_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/check-qstring.c b/check-qstring.c
new file mode 100644
index 0000000..c9bafc2
--- /dev/null
+++ b/check-qstring.c
@@ -0,0 +1,134 @@
+/*
+ * QString unit-tests.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#include <check.h>
+
+#include "qstring.h"
+#include "qemu-common.h"
+
+/*
+ * Public Interface test-cases
+ *
+ * (with some violations to access 'private' data)
+ */
+
+START_TEST(qstring_from_str_test)
+{
+ QString *qstring;
+ const char *str = "QEMU";
+
+ qstring = qstring_from_str(str);
+ fail_unless(qstring != NULL);
+ fail_unless(qstring->base.refcnt == 1);
+ fail_unless(strcmp(str, qstring->string) == 0);
+ fail_unless(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
+
+ // destroy doesn't exit yet
+ qemu_free(qstring->string);
+ qemu_free(qstring);
+}
+END_TEST
+
+START_TEST(qstring_destroy_test)
+{
+ QString *qstring = qstring_from_str("destroy test");
+ QDECREF(qstring);
+}
+END_TEST
+
+START_TEST(qstring_get_str_test)
+{
+ QString *qstring;
+ const char *ret_str;
+ const char *str = "QEMU/KVM";
+
+ qstring = qstring_from_str(str);
+ ret_str = qstring_get_str(qstring);
+ fail_unless(strcmp(ret_str, str) == 0);
+
+ QDECREF(qstring);
+}
+END_TEST
+
+START_TEST(qstring_append_chr_test)
+{
+ int i;
+ QString *qstring;
+ const char *str = "qstring append char unit-test";
+
+ qstring = qstring_new();
+
+ for (i = 0; str[i]; i++)
+ qstring_append_chr(qstring, str[i]);
+
+ fail_unless(strcmp(str, qstring_get_str(qstring)) == 0);
+ QDECREF(qstring);
+}
+END_TEST
+
+START_TEST(qstring_from_substr_test)
+{
+ QString *qs;
+
+ qs = qstring_from_substr("virtualization", 3, 9);
+ fail_unless(qs != NULL);
+ fail_unless(strcmp(qstring_get_str(qs), "tualiza") == 0);
+
+ QDECREF(qs);
+}
+END_TEST
+
+
+START_TEST(qobject_to_qstring_test)
+{
+ QString *qstring;
+
+ qstring = qstring_from_str("foo");
+ fail_unless(qobject_to_qstring(QOBJECT(qstring)) == qstring);
+
+ QDECREF(qstring);
+}
+END_TEST
+
+static Suite *qstring_suite(void)
+{
+ Suite *s;
+ TCase *qstring_public_tcase;
+
+ s = suite_create("QString test-suite");
+
+ qstring_public_tcase = tcase_create("Public Interface");
+ suite_add_tcase(s, qstring_public_tcase);
+ tcase_add_test(qstring_public_tcase, qstring_from_str_test);
+ tcase_add_test(qstring_public_tcase, qstring_destroy_test);
+ tcase_add_test(qstring_public_tcase, qstring_get_str_test);
+ tcase_add_test(qstring_public_tcase, qstring_append_chr_test);
+ tcase_add_test(qstring_public_tcase, qstring_from_substr_test);
+ tcase_add_test(qstring_public_tcase, qobject_to_qstring_test);
+
+ return s;
+}
+
+int main(void)
+{
+ int nf;
+ Suite *s;
+ SRunner *sr;
+
+ s = qstring_suite();
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+ nf = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/cmd.c b/cmd.c
new file mode 100644
index 0000000..db2c9c4
--- /dev/null
+++ b/cmd.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#include "cmd.h"
+#include "qemu-aio.h"
+
+#define _(x) x /* not gettext support yet */
+
+/* from libxcmd/command.c */
+
+cmdinfo_t *cmdtab;
+int ncmds;
+
+static argsfunc_t args_func;
+static checkfunc_t check_func;
+static int ncmdline;
+static char **cmdline;
+
+static int
+compare(const void *a, const void *b)
+{
+ return strcmp(((const cmdinfo_t *)a)->name,
+ ((const cmdinfo_t *)b)->name);
+}
+
+void
+add_command(
+ const cmdinfo_t *ci)
+{
+ cmdtab = realloc((void *)cmdtab, ++ncmds * sizeof(*cmdtab));
+ cmdtab[ncmds - 1] = *ci;
+ qsort(cmdtab, ncmds, sizeof(*cmdtab), compare);
+}
+
+static int
+check_command(
+ const cmdinfo_t *ci)
+{
+ if (check_func)
+ return check_func(ci);
+ return 1;
+}
+
+void
+add_check_command(
+ checkfunc_t cf)
+{
+ check_func = cf;
+}
+
+int
+command_usage(
+ const cmdinfo_t *ci)
+{
+ printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline);
+ return 0;
+}
+
+int
+command(
+ const cmdinfo_t *ct,
+ int argc,
+ char **argv)
+{
+ char *cmd = argv[0];
+
+ if (!check_command(ct))
+ return 0;
+
+ if (argc-1 < ct->argmin || (ct->argmax != -1 && argc-1 > ct->argmax)) {
+ if (ct->argmax == -1)
+ fprintf(stderr,
+ _("bad argument count %d to %s, expected at least %d arguments\n"),
+ argc-1, cmd, ct->argmin);
+ else if (ct->argmin == ct->argmax)
+ fprintf(stderr,
+ _("bad argument count %d to %s, expected %d arguments\n"),
+ argc-1, cmd, ct->argmin);
+ else
+ fprintf(stderr,
+ _("bad argument count %d to %s, expected between %d and %d arguments\n"),
+ argc-1, cmd, ct->argmin, ct->argmax);
+ return 0;
+ }
+ optind = 0;
+ return ct->cfunc(argc, argv);
+}
+
+const cmdinfo_t *
+find_command(
+ const char *cmd)
+{
+ cmdinfo_t *ct;
+
+ for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
+ if (strcmp(ct->name, cmd) == 0 ||
+ (ct->altname && strcmp(ct->altname, cmd) == 0))
+ return (const cmdinfo_t *)ct;
+ }
+ return NULL;
+}
+
+void
+add_user_command(char *optarg)
+{
+ ncmdline++;
+ cmdline = realloc(cmdline, sizeof(char*) * (ncmdline));
+ if (!cmdline) {
+ perror("realloc");
+ exit(1);
+ }
+ cmdline[ncmdline-1] = optarg;
+}
+
+static int
+args_command(
+ int index)
+{
+ if (args_func)
+ return args_func(index);
+ return 0;
+}
+
+void
+add_args_command(
+ argsfunc_t af)
+{
+ args_func = af;
+}
+
+static void prep_fetchline(void *opaque)
+{
+ int *fetchable = opaque;
+
+ qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
+ *fetchable= 1;
+}
+
+static char *get_prompt(void);
+
+void
+command_loop(void)
+{
+ int c, i, j = 0, done = 0, fetchable = 0, prompted = 0;
+ char *input;
+ char **v;
+ const cmdinfo_t *ct;
+
+ for (i = 0; !done && i < ncmdline; i++) {
+ input = strdup(cmdline[i]);
+ if (!input) {
+ fprintf(stderr,
+ _("cannot strdup command '%s': %s\n"),
+ cmdline[i], strerror(errno));
+ exit(1);
+ }
+ v = breakline(input, &c);
+ if (c) {
+ ct = find_command(v[0]);
+ if (ct) {
+ if (ct->flags & CMD_FLAG_GLOBAL)
+ done = command(ct, c, v);
+ else {
+ j = 0;
+ while (!done && (j = args_command(j)))
+ done = command(ct, c, v);
+ }
+ } else
+ fprintf(stderr, _("command \"%s\" not found\n"),
+ v[0]);
+ }
+ doneline(input, v);
+ }
+ if (cmdline) {
+ free(cmdline);
+ return;
+ }
+
+ while (!done) {
+ if (!prompted) {
+ printf("%s", get_prompt());
+ fflush(stdout);
+ qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL,
+ NULL, &fetchable);
+ prompted = 1;
+ }
+
+ qemu_aio_wait();
+
+ if (!fetchable) {
+ continue;
+ }
+ if ((input = fetchline()) == NULL)
+ break;
+ v = breakline(input, &c);
+ if (c) {
+ ct = find_command(v[0]);
+ if (ct)
+ done = command(ct, c, v);
+ else
+ fprintf(stderr, _("command \"%s\" not found\n"),
+ v[0]);
+ }
+ doneline(input, v);
+
+ prompted = 0;
+ fetchable = 0;
+ }
+ qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
+}
+
+/* from libxcmd/input.c */
+
+#if defined(ENABLE_READLINE)
+# include <readline/history.h>
+# include <readline/readline.h>
+#elif defined(ENABLE_EDITLINE)
+# include <histedit.h>
+#endif
+
+static char *
+get_prompt(void)
+{
+ static char prompt[FILENAME_MAX + 2 /*"> "*/ + 1 /*"\0"*/ ];
+
+ if (!prompt[0])
+ snprintf(prompt, sizeof(prompt), "%s> ", progname);
+ return prompt;
+}
+
+#if defined(ENABLE_READLINE)
+char *
+fetchline(void)
+{
+ char *line;
+
+ line = readline(get_prompt());
+ if (line && *line)
+ add_history(line);
+ return line;
+}
+#elif defined(ENABLE_EDITLINE)
+static char *el_get_prompt(EditLine *e) { return get_prompt(); }
+char *
+fetchline(void)
+{
+ static EditLine *el;
+ static History *hist;
+ HistEvent hevent;
+ char *line;
+ int count;
+
+ if (!el) {
+ hist = history_init();
+ history(hist, &hevent, H_SETSIZE, 100);
+ el = el_init(progname, stdin, stdout, stderr);
+ el_source(el, NULL);
+ el_set(el, EL_SIGNAL, 1);
+ el_set(el, EL_PROMPT, el_get_prompt);
+ el_set(el, EL_HIST, history, (const char *)hist);
+ }
+ line = strdup(el_gets(el, &count));
+ if (line) {
+ if (count > 0)
+ line[count-1] = '\0';
+ if (*line)
+ history(hist, &hevent, H_ENTER, line);
+ }
+ return line;
+}
+#else
+# define MAXREADLINESZ 1024
+char *
+fetchline(void)
+{
+ char *p, *line = malloc(MAXREADLINESZ);
+
+ if (!line)
+ return NULL;
+ if (!fgets(line, MAXREADLINESZ, stdin)) {
+ free(line);
+ return NULL;
+ }
+ p = line + strlen(line);
+ if (p != line && p[-1] == '\n')
+ p[-1] = '\0';
+ return line;
+}
+#endif
+
+static char *qemu_strsep(char **input, const char *delim)
+{
+ char *result = *input;
+ if (result != NULL) {
+ char *p;
+
+ for (p = result; *p != '\0'; p++) {
+ if (strchr(delim, *p)) {
+ break;
+ }
+ }
+ if (*p == '\0') {
+ *input = NULL;
+ } else {
+ *p = '\0';
+ *input = p + 1;
+ }
+ }
+ return result;
+}
+
+char **
+breakline(
+ char *input,
+ int *count)
+{
+ int c = 0;
+ char *p;
+ char **rval = calloc(sizeof(char *), 1);
+
+ while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
+ if (!*p)
+ continue;
+ c++;
+ rval = realloc(rval, sizeof(*rval) * (c + 1));
+ if (!rval) {
+ c = 0;
+ break;
+ }
+ rval[c - 1] = p;
+ rval[c] = NULL;
+ }
+ *count = c;
+ return rval;
+}
+
+void
+doneline(
+ char *input,
+ char **vec)
+{
+ free(input);
+ free(vec);
+}
+
+#define EXABYTES(x) ((long long)(x) << 60)
+#define PETABYTES(x) ((long long)(x) << 50)
+#define TERABYTES(x) ((long long)(x) << 40)
+#define GIGABYTES(x) ((long long)(x) << 30)
+#define MEGABYTES(x) ((long long)(x) << 20)
+#define KILOBYTES(x) ((long long)(x) << 10)
+
+long long
+cvtnum(
+ char *s)
+{
+ long long i;
+ char *sp;
+ int c;
+
+ i = strtoll(s, &sp, 0);
+ if (i == 0 && sp == s)
+ return -1LL;
+ if (*sp == '\0')
+ return i;
+
+ if (sp[1] != '\0')
+ return -1LL;
+
+ c = tolower(*sp);
+ switch (c) {
+ default:
+ return i;
+ case 'k':
+ return KILOBYTES(i);
+ case 'm':
+ return MEGABYTES(i);
+ case 'g':
+ return GIGABYTES(i);
+ case 't':
+ return TERABYTES(i);
+ case 'p':
+ return PETABYTES(i);
+ case 'e':
+ return EXABYTES(i);
+ }
+ return -1LL;
+}
+
+#define TO_EXABYTES(x) ((x) / EXABYTES(1))
+#define TO_PETABYTES(x) ((x) / PETABYTES(1))
+#define TO_TERABYTES(x) ((x) / TERABYTES(1))
+#define TO_GIGABYTES(x) ((x) / GIGABYTES(1))
+#define TO_MEGABYTES(x) ((x) / MEGABYTES(1))
+#define TO_KILOBYTES(x) ((x) / KILOBYTES(1))
+
+void
+cvtstr(
+ double value,
+ char *str,
+ size_t size)
+{
+ const char *fmt;
+ int precise;
+
+ precise = ((double)value * 1000 == (double)(int)value * 1000);
+
+ if (value >= EXABYTES(1)) {
+ fmt = precise ? "%.f EiB" : "%.3f EiB";
+ snprintf(str, size, fmt, TO_EXABYTES(value));
+ } else if (value >= PETABYTES(1)) {
+ fmt = precise ? "%.f PiB" : "%.3f PiB";
+ snprintf(str, size, fmt, TO_PETABYTES(value));
+ } else if (value >= TERABYTES(1)) {
+ fmt = precise ? "%.f TiB" : "%.3f TiB";
+ snprintf(str, size, fmt, TO_TERABYTES(value));
+ } else if (value >= GIGABYTES(1)) {
+ fmt = precise ? "%.f GiB" : "%.3f GiB";
+ snprintf(str, size, fmt, TO_GIGABYTES(value));
+ } else if (value >= MEGABYTES(1)) {
+ fmt = precise ? "%.f MiB" : "%.3f MiB";
+ snprintf(str, size, fmt, TO_MEGABYTES(value));
+ } else if (value >= KILOBYTES(1)) {
+ fmt = precise ? "%.f KiB" : "%.3f KiB";
+ snprintf(str, size, fmt, TO_KILOBYTES(value));
+ } else {
+ snprintf(str, size, "%f bytes", value);
+ }
+}
+
+struct timeval
+tsub(struct timeval t1, struct timeval t2)
+{
+ t1.tv_usec -= t2.tv_usec;
+ if (t1.tv_usec < 0) {
+ t1.tv_usec += 1000000;
+ t1.tv_sec--;
+ }
+ t1.tv_sec -= t2.tv_sec;
+ return t1;
+}
+
+double
+tdiv(double value, struct timeval tv)
+{
+ return value / ((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
+}
+
+#define HOURS(sec) ((sec) / (60 * 60))
+#define MINUTES(sec) (((sec) % (60 * 60)) / 60)
+#define SECONDS(sec) ((sec) % 60)
+
+void
+timestr(
+ struct timeval *tv,
+ char *ts,
+ size_t size,
+ int format)
+{
+ double usec = (double)tv->tv_usec / 1000000.0;
+
+ if (format & TERSE_FIXED_TIME) {
+ if (!HOURS(tv->tv_sec)) {
+ snprintf(ts, size, "%u:%02u.%02u",
+ (unsigned int) MINUTES(tv->tv_sec),
+ (unsigned int) SECONDS(tv->tv_sec),
+ (unsigned int) usec * 100);
+ return;
+ }
+ format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
+ }
+
+ if ((format & VERBOSE_FIXED_TIME) || tv->tv_sec) {
+ snprintf(ts, size, "%u:%02u:%02u.%02u",
+ (unsigned int) HOURS(tv->tv_sec),
+ (unsigned int) MINUTES(tv->tv_sec),
+ (unsigned int) SECONDS(tv->tv_sec),
+ (unsigned int) usec * 100);
+ } else {
+ snprintf(ts, size, "0.%04u sec", (unsigned int) usec * 10000);
+ }
+}
+
+
+/* from libxcmd/quit.c */
+
+static cmdinfo_t quit_cmd;
+
+/* ARGSUSED */
+static int
+quit_f(
+ int argc,
+ char **argv)
+{
+ return 1;
+}
+
+void
+quit_init(void)
+{
+ quit_cmd.name = _("quit");
+ quit_cmd.altname = _("q");
+ quit_cmd.cfunc = quit_f;
+ quit_cmd.argmin = -1;
+ quit_cmd.argmax = -1;
+ quit_cmd.flags = CMD_FLAG_GLOBAL;
+ quit_cmd.oneline = _("exit the program");
+
+ add_command(&quit_cmd);
+}
+
+/* from libxcmd/help.c */
+
+static cmdinfo_t help_cmd;
+static void help_onecmd(const char *cmd, const cmdinfo_t *ct);
+static void help_oneline(const char *cmd, const cmdinfo_t *ct);
+
+static void
+help_all(void)
+{
+ const cmdinfo_t *ct;
+
+ for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++)
+ help_oneline(ct->name, ct);
+ printf(_("\nUse 'help commandname' for extended help.\n"));
+}
+
+static int
+help_f(
+ int argc,
+ char **argv)
+{
+ const cmdinfo_t *ct;
+
+ if (argc == 1) {
+ help_all();
+ return 0;
+ }
+ ct = find_command(argv[1]);
+ if (ct == NULL) {
+ printf(_("command %s not found\n"), argv[1]);
+ return 0;
+ }
+ help_onecmd(argv[1], ct);
+ return 0;
+}
+
+static void
+help_onecmd(
+ const char *cmd,
+ const cmdinfo_t *ct)
+{
+ help_oneline(cmd, ct);
+ if (ct->help)
+ ct->help();
+}
+
+static void
+help_oneline(
+ const char *cmd,
+ const cmdinfo_t *ct)
+{
+ if (cmd)
+ printf("%s ", cmd);
+ else {
+ printf("%s ", ct->name);
+ if (ct->altname)
+ printf("(or %s) ", ct->altname);
+ }
+ if (ct->args)
+ printf("%s ", ct->args);
+ printf("-- %s\n", ct->oneline);
+}
+
+void
+help_init(void)
+{
+ help_cmd.name = _("help");
+ help_cmd.altname = _("?");
+ help_cmd.cfunc = help_f;
+ help_cmd.argmin = 0;
+ help_cmd.argmax = 1;
+ help_cmd.flags = CMD_FLAG_GLOBAL;
+ help_cmd.args = _("[command]");
+ help_cmd.oneline = _("help for one or all commands");
+
+ add_command(&help_cmd);
+}
diff --git a/cmd.h b/cmd.h
new file mode 100644
index 0000000..b763b19
--- /dev/null
+++ b/cmd.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU 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/>.
+ */
+#ifndef __COMMAND_H__
+#define __COMMAND_H__
+
+#define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */
+
+typedef int (*cfunc_t)(int argc, char **argv);
+typedef void (*helpfunc_t)(void);
+
+typedef struct cmdinfo {
+ const char *name;
+ const char *altname;
+ cfunc_t cfunc;
+ int argmin;
+ int argmax;
+ int canpush;
+ int flags;
+ const char *args;
+ const char *oneline;
+ helpfunc_t help;
+} cmdinfo_t;
+
+extern cmdinfo_t *cmdtab;
+extern int ncmds;
+
+void help_init(void);
+void quit_init(void);
+
+typedef int (*argsfunc_t)(int index);
+typedef int (*checkfunc_t)(const cmdinfo_t *ci);
+
+void add_command(const cmdinfo_t *ci);
+void add_user_command(char *optarg);
+void add_args_command(argsfunc_t af);
+void add_check_command(checkfunc_t cf);
+
+const cmdinfo_t *find_command(const char *cmd);
+
+void command_loop(void);
+int command_usage(const cmdinfo_t *ci);
+int command(const cmdinfo_t *ci, int argc, char **argv);
+
+/* from input.h */
+char **breakline(char *input, int *count);
+void doneline(char *input, char **vec);
+char *fetchline(void);
+
+long long cvtnum(char *s);
+void cvtstr(double value, char *str, size_t sz);
+
+struct timeval tsub(struct timeval t1, struct timeval t2);
+double tdiv(double value, struct timeval tv);
+
+enum {
+ DEFAULT_TIME = 0x0,
+ TERSE_FIXED_TIME = 0x1,
+ VERBOSE_FIXED_TIME = 0x2
+};
+
+void timestr(struct timeval *tv, char *str, size_t sz, int flags);
+
+extern char *progname;
+
+#endif /* __COMMAND_H__ */
diff --git a/compat/sys/eventfd.h b/compat/sys/eventfd.h
new file mode 100644
index 0000000..f55d96a
--- /dev/null
+++ b/compat/sys/eventfd.h
@@ -0,0 +1,13 @@
+#ifndef _COMPAT_SYS_EVENTFD
+#define _COMPAT_SYS_EVENTFD
+
+#include <unistd.h>
+#include <syscall.h>
+
+
+static inline int eventfd (int count, int flags)
+{
+ return syscall(SYS_eventfd, count, flags);
+}
+
+#endif
diff --git a/compatfd.c b/compatfd.c
new file mode 100644
index 0000000..a7cebc4
--- /dev/null
+++ b/compatfd.c
@@ -0,0 +1,117 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "compatfd.h"
+
+#include <sys/syscall.h>
+#include <pthread.h>
+
+struct sigfd_compat_info
+{
+ sigset_t mask;
+ int fd;
+};
+
+static void *sigwait_compat(void *opaque)
+{
+ struct sigfd_compat_info *info = opaque;
+ int err;
+ sigset_t all;
+
+ sigfillset(&all);
+ sigprocmask(SIG_BLOCK, &all, NULL);
+
+ do {
+ siginfo_t siginfo;
+
+ err = sigwaitinfo(&info->mask, &siginfo);
+ if (err == -1 && errno == EINTR) {
+ err = 0;
+ continue;
+ }
+
+ if (err > 0) {
+ char buffer[128];
+ size_t offset = 0;
+
+ memcpy(buffer, &err, sizeof(err));
+ while (offset < sizeof(buffer)) {
+ ssize_t len;
+
+ len = write(info->fd, buffer + offset,
+ sizeof(buffer) - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+
+ if (len <= 0) {
+ err = -1;
+ break;
+ }
+
+ offset += len;
+ }
+ }
+ } while (err >= 0);
+
+ return NULL;
+}
+
+static int qemu_signalfd_compat(const sigset_t *mask)
+{
+ pthread_attr_t attr;
+ pthread_t tid;
+ struct sigfd_compat_info *info;
+ int fds[2];
+
+ info = malloc(sizeof(*info));
+ if (info == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (pipe(fds) == -1) {
+ free(info);
+ return -1;
+ }
+
+ qemu_set_cloexec(fds[0]);
+ qemu_set_cloexec(fds[1]);
+
+ memcpy(&info->mask, mask, sizeof(*mask));
+ info->fd = fds[1];
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ pthread_create(&tid, &attr, sigwait_compat, info);
+
+ pthread_attr_destroy(&attr);
+
+ return fds[0];
+}
+
+int qemu_signalfd(const sigset_t *mask)
+{
+#if defined(CONFIG_SIGNALFD)
+ int ret;
+
+ ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
+ if (ret != -1) {
+ qemu_set_cloexec(ret);
+ return ret;
+ }
+#endif
+
+ return qemu_signalfd_compat(mask);
+}
diff --git a/compatfd.h b/compatfd.h
new file mode 100644
index 0000000..fc37915
--- /dev/null
+++ b/compatfd.h
@@ -0,0 +1,43 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COMPATFD_H
+#define QEMU_COMPATFD_H
+
+#include <signal.h>
+
+struct qemu_signalfd_siginfo {
+ uint32_t ssi_signo; /* Signal number */
+ int32_t ssi_errno; /* Error number (unused) */
+ int32_t ssi_code; /* Signal code */
+ uint32_t ssi_pid; /* PID of sender */
+ uint32_t ssi_uid; /* Real UID of sender */
+ int32_t ssi_fd; /* File descriptor (SIGIO) */
+ uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */
+ uint32_t ssi_band; /* Band event (SIGIO) */
+ uint32_t ssi_overrun; /* POSIX timer overrun count */
+ uint32_t ssi_trapno; /* Trap number that caused signal */
+ int32_t ssi_status; /* Exit status or signal (SIGCHLD) */
+ int32_t ssi_int; /* Integer sent by sigqueue(2) */
+ uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */
+ uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */
+ uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */
+ uint64_t ssi_addr; /* Address that generated signal
+ (for hardware-generated signals) */
+ uint8_t pad[48]; /* Pad size to 128 bytes (allow for
+ additional fields in the future) */
+};
+
+int qemu_signalfd(const sigset_t *mask);
+
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..e20f786
--- /dev/null
+++ b/config.h
@@ -0,0 +1,2 @@
+#include "config-host.h"
+#include "config-target.h"
diff --git a/configure b/configure
new file mode 100755
index 0000000..50f5de9
--- /dev/null
+++ b/configure
@@ -0,0 +1,3432 @@
+#!/bin/sh
+#
+# qemu configure script (c) 2003 Fabrice Bellard
+#
+# set temporary file name
+if test ! -z "$TMPDIR" ; then
+ TMPDIR1="${TMPDIR}"
+elif test ! -z "$TEMPDIR" ; then
+ TMPDIR1="${TEMPDIR}"
+else
+ TMPDIR1="/tmp"
+fi
+
+TMPC="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.c"
+TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o"
+TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe"
+
+# NB: do not call "exit" in the trap handler; this is buggy with some shells;
+# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org>
+trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM
+rm -f config.log
+
+compile_object() {
+ echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log
+ $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1
+}
+
+compile_prog() {
+ local_cflags="$1"
+ local_ldflags="$2"
+ echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log
+ $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1
+}
+
+# symbolically link $1 to $2. Portable version of "ln -sf".
+symlink() {
+ rm -f $2
+ ln -s $1 $2
+}
+
+# check whether a command is available to this shell (may be either an
+# executable or a builtin)
+has() {
+ type "$1" >/dev/null 2>&1
+}
+
+# search for an executable in PATH
+path_of() {
+ local_command="$1"
+ local_ifs="$IFS"
+ local_dir=""
+
+ # pathname has a dir component?
+ if [ "${local_command#*/}" != "$local_command" ]; then
+ if [ -x "$local_command" ] && [ ! -d "$local_command" ]; then
+ echo "$local_command"
+ return 0
+ fi
+ fi
+ if [ -z "$local_command" ]; then
+ return 1
+ fi
+
+ IFS=:
+ for local_dir in $PATH; do
+ if [ -x "$local_dir/$local_command" ] && [ ! -d "$local_dir/$local_command" ]; then
+ echo "$local_dir/$local_command"
+ IFS="${local_ifs:-$(printf ' \t\n')}"
+ return 0
+ fi
+ done
+ # not found
+ IFS="${local_ifs:-$(printf ' \t\n')}"
+ return 1
+}
+
+# default parameters
+source_path=`dirname "$0"`
+cpu=""
+interp_prefix="/usr/gnemul/qemu-%M"
+static="no"
+sparc_cpu=""
+cross_prefix=""
+audio_drv_list=""
+audio_card_list="ac97 es1370 sb16 hda"
+audio_possible_cards="ac97 es1370 sb16 cs4231a adlib gus hda"
+block_drv_whitelist=""
+host_cc="gcc"
+helper_cflags=""
+libs_softmmu=""
+libs_tools=""
+audio_pt_int=""
+audio_win_int=""
+cc_i386=i386-pc-linux-gnu-gcc
+
+target_list="x86_64-softmmu"
+
+kvm_version() {
+ local fname="$(dirname "$0")/KVM_VERSION"
+
+ if test -f "$fname"; then
+ cat "$fname"
+ else
+ echo "qemu-kvm-devel"
+ fi
+}
+
+# Default value for a variable defining feature "foo".
+# * foo="no" feature will only be used if --enable-foo arg is given
+# * foo="" feature will be searched for, and if found, will be used
+# unless --disable-foo is given
+# * foo="yes" this value will only be set by --enable-foo flag.
+# feature will searched for,
+# if not found, configure exits with error
+#
+# Always add --enable-foo and --disable-foo command line args.
+# Distributions want to ensure that several features are compiled in, and it
+# is impossible without a --enable-foo that exits if a feature is not found.
+
+bluez=""
+brlapi=""
+curl=""
+curses=""
+docs=""
+fdt=""
+kvm=""
+kvm_para=""
+nptl=""
+sdl=""
+sparse="no"
+uuid=""
+vde=""
+vnc_tls=""
+vnc_sasl=""
+vnc_jpeg=""
+vnc_png=""
+vnc_thread="no"
+xen=""
+linux_aio=""
+attr=""
+vhost_net=""
+xfs=""
+
+gprof="no"
+debug_tcg="no"
+debug_mon="no"
+debug="no"
+strip_opt="yes"
+bigendian="no"
+mingw32="no"
+EXESUF=""
+prefix="/usr/local"
+mandir="\${prefix}/share/man"
+datadir="\${prefix}/share/qemu"
+docdir="\${prefix}/share/doc/qemu"
+bindir="\${prefix}/bin"
+sysconfdir="\${prefix}/etc"
+confsuffix="/qemu"
+slirp="yes"
+fmod_lib=""
+fmod_inc=""
+oss_lib=""
+bsd="no"
+linux="no"
+solaris="no"
+profiler="no"
+cocoa="no"
+softmmu="yes"
+linux_user="no"
+darwin_user="no"
+bsd_user="no"
+guest_base=""
+uname_release=""
+io_thread="no"
+mixemu="no"
+kvm_cap_pit=""
+kvm_cap_device_assignment=""
+kerneldir=""
+aix="no"
+blobs="yes"
+pkgversion=" ($(kvm_version))"
+cpu_emulation="yes"
+check_utests="no"
+user_pie="no"
+zero_malloc=""
+trace_backend="nop"
+trace_file="trace"
+spice=""
+rbd=""
+
+# parse CC options first
+for opt do
+ optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
+ case "$opt" in
+ --cross-prefix=*) cross_prefix="$optarg"
+ ;;
+ --cc=*) CC="$optarg"
+ ;;
+ --source-path=*) source_path="$optarg"
+ ;;
+ --cpu=*) cpu="$optarg"
+ ;;
+ --extra-cflags=*) QEMU_CFLAGS="$optarg $QEMU_CFLAGS"
+ ;;
+ --extra-ldflags=*) LDFLAGS="$optarg $LDFLAGS"
+ ;;
+ --sparc_cpu=*)
+ sparc_cpu="$optarg"
+ case $sparc_cpu in
+ v7|v8|v8plus|v8plusa)
+ cpu="sparc"
+ ;;
+ v9)
+ cpu="sparc64"
+ ;;
+ *)
+ echo "undefined SPARC architecture. Exiting";
+ exit 1
+ ;;
+ esac
+ ;;
+ esac
+done
+# OS specific
+# Using uname is really, really broken. Once we have the right set of checks
+# we can eliminate it's usage altogether
+
+cc="${cross_prefix}${CC-gcc}"
+ar="${cross_prefix}${AR-ar}"
+objcopy="${cross_prefix}${OBJCOPY-objcopy}"
+ld="${cross_prefix}${LD-ld}"
+strip="${cross_prefix}${STRIP-strip}"
+windres="${cross_prefix}${WINDRES-windres}"
+pkg_config="${cross_prefix}${PKG_CONFIG-pkg-config}"
+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="-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"
+QEMU_INCLUDES="-I. -I\$(SRC_PATH)"
+LDFLAGS="-g $LDFLAGS"
+
+# make source path absolute
+source_path=`cd "$source_path"; pwd`
+
+check_define() {
+cat > $TMPC <<EOF
+#if !defined($1)
+#error Not defined
+#endif
+int main(void) { return 0; }
+EOF
+ compile_object
+}
+
+if test ! -z "$cpu" ; then
+ # command line argument
+ :
+elif check_define __i386__ ; then
+ cpu="i386"
+elif check_define __x86_64__ ; then
+ cpu="x86_64"
+elif check_define __sparc__ ; then
+ # We can't check for 64 bit (when gcc is biarch) or V8PLUSA
+ # They must be specified using --sparc_cpu
+ if check_define __arch64__ ; then
+ cpu="sparc64"
+ else
+ cpu="sparc"
+ fi
+elif check_define _ARCH_PPC ; then
+ if check_define _ARCH_PPC64 ; then
+ cpu="ppc64"
+ else
+ cpu="ppc"
+ fi
+elif check_define __mips__ ; then
+ cpu="mips"
+elif check_define __ia64__ ; then
+ cpu="ia64"
+elif check_define __s390__ ; then
+ if check_define __s390x__ ; then
+ cpu="s390x"
+ else
+ cpu="s390"
+ fi
+else
+ cpu=`uname -m`
+fi
+
+case "$cpu" in
+ alpha|cris|ia64|m68k|microblaze|ppc|ppc64|sparc64)
+ cpu="$cpu"
+ ;;
+ i386|i486|i586|i686|i86pc|BePC)
+ cpu="i386"
+ ;;
+ x86_64|amd64)
+ cpu="x86_64"
+ ;;
+ armv*b)
+ cpu="armv4b"
+ ;;
+ armv*l)
+ cpu="armv4l"
+ ;;
+ parisc|parisc64)
+ cpu="hppa"
+ ;;
+ mips*)
+ cpu="mips"
+ ;;
+ s390)
+ cpu="s390"
+ ;;
+ s390x)
+ cpu="s390x"
+ ;;
+ sparc|sun4[cdmuv])
+ cpu="sparc"
+ ;;
+ *)
+ echo "Unsupported CPU = $cpu"
+ exit 1
+ ;;
+esac
+
+# OS specific
+if check_define __linux__ ; then
+ targetos="Linux"
+elif check_define _WIN32 ; then
+ targetos='MINGW32'
+elif check_define __OpenBSD__ ; then
+ targetos='OpenBSD'
+elif check_define __sun__ ; then
+ targetos='SunOS'
+elif check_define __HAIKU__ ; then
+ targetos='Haiku'
+else
+ targetos=`uname -s`
+fi
+
+case $targetos in
+CYGWIN*)
+ mingw32="yes"
+ QEMU_CFLAGS="-mno-cygwin $QEMU_CFLAGS"
+ audio_possible_drivers="winwave sdl"
+ audio_drv_list="winwave"
+;;
+MINGW32*)
+ mingw32="yes"
+ audio_possible_drivers="winwave dsound sdl fmod"
+ audio_drv_list="winwave"
+;;
+GNU/kFreeBSD)
+ bsd="yes"
+ audio_drv_list="oss"
+ audio_possible_drivers="oss sdl esd pa"
+;;
+FreeBSD)
+ bsd="yes"
+ make="${MAKE-gmake}"
+ audio_drv_list="oss"
+ audio_possible_drivers="oss sdl esd pa"
+ # needed for kinfo_getvmmap(3) in libutil.h
+ LIBS="-lutil $LIBS"
+;;
+DragonFly)
+ bsd="yes"
+ make="${MAKE-gmake}"
+ audio_drv_list="oss"
+ audio_possible_drivers="oss sdl esd pa"
+;;
+NetBSD)
+ bsd="yes"
+ make="${MAKE-gmake}"
+ audio_drv_list="oss"
+ audio_possible_drivers="oss sdl esd"
+ oss_lib="-lossaudio"
+;;
+OpenBSD)
+ bsd="yes"
+ make="${MAKE-gmake}"
+ audio_drv_list="oss"
+ audio_possible_drivers="oss sdl esd"
+ oss_lib="-lossaudio"
+;;
+Darwin)
+ bsd="yes"
+ darwin="yes"
+ # on Leopard most of the system is 32-bit, so we have to ask the kernel it if we can
+ # run 64-bit userspace code
+ if [ "$cpu" = "i386" ] ; then
+ is_x86_64=`sysctl -n hw.optional.x86_64`
+ [ "$is_x86_64" = "1" ] && cpu=x86_64
+ fi
+ if [ "$cpu" = "x86_64" ] ; then
+ QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
+ LDFLAGS="-arch x86_64 $LDFLAGS"
+ else
+ QEMU_CFLAGS="-mdynamic-no-pic $QEMU_CFLAGS"
+ fi
+ darwin_user="yes"
+ cocoa="yes"
+ audio_drv_list="coreaudio"
+ audio_possible_drivers="coreaudio sdl fmod"
+ LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS"
+ libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu"
+;;
+SunOS)
+ solaris="yes"
+ make="${MAKE-gmake}"
+ install="${INSTALL-ginstall}"
+ ld="gld"
+ needs_libsunmath="no"
+ solarisrev=`uname -r | cut -f2 -d.`
+ # have to select again, because `uname -m` returns i86pc
+ # even on an x86_64 box.
+ solariscpu=`isainfo -k`
+ if test "${solariscpu}" = "amd64" ; then
+ cpu="x86_64"
+ fi
+ if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
+ if test "$solarisrev" -le 9 ; then
+ if test -f /opt/SUNWspro/prod/lib/libsunmath.so.1; then
+ needs_libsunmath="yes"
+ QEMU_CFLAGS="-I/opt/SUNWspro/prod/include/cc $QEMU_CFLAGS"
+ LDFLAGS="-L/opt/SUNWspro/prod/lib -R/opt/SUNWspro/prod/lib $LDFLAGS"
+ LIBS="-lsunmath $LIBS"
+ else
+ echo "QEMU will not link correctly on Solaris 8/X86 or 9/x86 without"
+ echo "libsunmath from the Sun Studio compilers tools, due to a lack of"
+ echo "C99 math features in libm.so in Solaris 8/x86 and Solaris 9/x86"
+ echo "Studio 11 can be downloaded from www.sun.com."
+ exit 1
+ fi
+ fi
+ fi
+ if test -f /usr/include/sys/soundcard.h ; then
+ audio_drv_list="oss"
+ fi
+ audio_possible_drivers="oss sdl"
+# needed for CMSG_ macros in sys/socket.h
+ QEMU_CFLAGS="-D_XOPEN_SOURCE=600 $QEMU_CFLAGS"
+# needed for TIOCWIN* defines in termios.h
+ QEMU_CFLAGS="-D__EXTENSIONS__ $QEMU_CFLAGS"
+ QEMU_CFLAGS="-std=gnu99 $QEMU_CFLAGS"
+ LIBS="-lsocket -lnsl -lresolv $LIBS"
+;;
+AIX)
+ aix="yes"
+ make="${MAKE-gmake}"
+;;
+Haiku)
+ haiku="yes"
+ QEMU_CFLAGS="-DB_USE_POSITIVE_POSIX_ERRORS $QEMU_CFLAGS"
+ LIBS="-lposix_error_mapper -lnetwork $LIBS"
+;;
+*)
+ audio_drv_list="oss"
+ audio_possible_drivers="oss alsa sdl esd pa"
+ linux="yes"
+ linux_user="yes"
+ usb="linux"
+ if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
+ audio_possible_drivers="$audio_possible_drivers fmod"
+ fi
+ if [ "$cpu" = "ia64" ] ; then
+ xen="no"
+ target_list="ia64-softmmu"
+ cpu_emulation="no"
+ gdbstub="no"
+ slirp="no"
+ fi
+;;
+esac
+
+if [ "$bsd" = "yes" ] ; then
+ if [ "$darwin" != "yes" ] ; then
+ usb="bsd"
+ fi
+ bsd_user="yes"
+fi
+
+: ${make=${MAKE-make}}
+: ${install=${INSTALL-install}}
+
+if test "$mingw32" = "yes" ; then
+ EXESUF=".exe"
+ QEMU_CFLAGS="-DWIN32_LEAN_AND_MEAN -DWINVER=0x501 $QEMU_CFLAGS"
+ # enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later)
+ QEMU_CFLAGS="-D__USE_MINGW_ANSI_STDIO=1 $QEMU_CFLAGS"
+ LIBS="-lwinmm -lws2_32 -liphlpapi $LIBS"
+ prefix="c:/Program Files/Qemu"
+ mandir="\${prefix}"
+ datadir="\${prefix}"
+ docdir="\${prefix}"
+ bindir="\${prefix}"
+ sysconfdir="\${prefix}"
+ confsuffix=""
+fi
+
+werror=""
+
+for opt do
+ optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
+ case "$opt" in
+ --help|-h) show_help=yes
+ ;;
+ --prefix=*) prefix="$optarg"
+ ;;
+ --interp-prefix=*) interp_prefix="$optarg"
+ ;;
+ --source-path=*)
+ ;;
+ --cross-prefix=*)
+ ;;
+ --cc=*)
+ ;;
+ --host-cc=*) host_cc="$optarg"
+ ;;
+ --make=*) make="$optarg"
+ ;;
+ --install=*) install="$optarg"
+ ;;
+ --extra-cflags=*)
+ ;;
+ --extra-ldflags=*)
+ ;;
+ --cpu=*)
+ ;;
+ --target-list=*) target_list="$optarg"
+ ;;
+ --enable-trace-backend=*) trace_backend="$optarg"
+ ;;
+ --with-trace-file=*) trace_file="$optarg"
+ ;;
+ --enable-gprof) gprof="yes"
+ ;;
+ --static)
+ static="yes"
+ LDFLAGS="-static $LDFLAGS"
+ ;;
+ --mandir=*) mandir="$optarg"
+ ;;
+ --bindir=*) bindir="$optarg"
+ ;;
+ --datadir=*) datadir="$optarg"
+ ;;
+ --docdir=*) docdir="$optarg"
+ ;;
+ --sysconfdir=*) sysconfdir="$optarg"
+ ;;
+ --disable-sdl) sdl="no"
+ ;;
+ --enable-sdl) sdl="yes"
+ ;;
+ --fmod-lib=*) fmod_lib="$optarg"
+ ;;
+ --fmod-inc=*) fmod_inc="$optarg"
+ ;;
+ --oss-lib=*) oss_lib="$optarg"
+ ;;
+ --audio-card-list=*) audio_card_list=`echo "$optarg" | sed -e 's/,/ /g'`
+ ;;
+ --audio-drv-list=*) audio_drv_list="$optarg"
+ ;;
+ --block-drv-whitelist=*) block_drv_whitelist=`echo "$optarg" | sed -e 's/,/ /g'`
+ ;;
+ --enable-debug-tcg) debug_tcg="yes"
+ ;;
+ --disable-debug-tcg) debug_tcg="no"
+ ;;
+ --enable-debug-mon) debug_mon="yes"
+ ;;
+ --disable-debug-mon) debug_mon="no"
+ ;;
+ --enable-debug)
+ # Enable debugging options that aren't excessively noisy
+ debug_tcg="yes"
+ debug_mon="yes"
+ debug="yes"
+ strip_opt="no"
+ ;;
+ --enable-sparse) sparse="yes"
+ ;;
+ --disable-sparse) sparse="no"
+ ;;
+ --disable-strip) strip_opt="no"
+ ;;
+ --disable-vnc-tls) vnc_tls="no"
+ ;;
+ --enable-vnc-tls) vnc_tls="yes"
+ ;;
+ --disable-vnc-sasl) vnc_sasl="no"
+ ;;
+ --enable-vnc-sasl) vnc_sasl="yes"
+ ;;
+ --disable-vnc-jpeg) vnc_jpeg="no"
+ ;;
+ --enable-vnc-jpeg) vnc_jpeg="yes"
+ ;;
+ --disable-vnc-png) vnc_png="no"
+ ;;
+ --enable-vnc-png) vnc_png="yes"
+ ;;
+ --disable-vnc-thread) vnc_thread="no"
+ ;;
+ --enable-vnc-thread) vnc_thread="yes"
+ ;;
+ --disable-slirp) slirp="no"
+ ;;
+ --disable-uuid) uuid="no"
+ ;;
+ --enable-uuid) uuid="yes"
+ ;;
+ --disable-vde) vde="no"
+ ;;
+ --enable-vde) vde="yes"
+ ;;
+ --disable-xen) xen="no"
+ ;;
+ --enable-xen) xen="yes"
+ ;;
+ --disable-brlapi) brlapi="no"
+ ;;
+ --enable-brlapi) brlapi="yes"
+ ;;
+ --disable-bluez) bluez="no"
+ ;;
+ --enable-bluez) bluez="yes"
+ ;;
+ --disable-kvm) kvm="no"
+ ;;
+ --enable-kvm) kvm="yes"
+ ;;
+ --disable-kvm-pit) kvm_cap_pit="no"
+ ;;
+ --enable-kvm-pit) kvm_cap_pit="yes"
+ ;;
+ --disable-kvm-device-assignment) kvm_cap_device_assignment="no"
+ ;;
+ --enable-kvm-device-assignment) kvm_cap_device_assignment="yes"
+ ;;
+ --disable-spice) spice="no"
+ ;;
+ --enable-spice) spice="yes"
+ ;;
+ --enable-profiler) profiler="yes"
+ ;;
+ --enable-cocoa)
+ cocoa="yes" ;
+ sdl="no" ;
+ audio_drv_list="coreaudio `echo $audio_drv_list | sed s,coreaudio,,g`"
+ ;;
+ --disable-system) softmmu="no"
+ ;;
+ --enable-system) softmmu="yes"
+ ;;
+ --disable-user)
+ linux_user="no" ;
+ bsd_user="no" ;
+ darwin_user="no"
+ ;;
+ --enable-user) ;;
+ --disable-linux-user) linux_user="no"
+ ;;
+ --enable-linux-user) linux_user="yes"
+ ;;
+ --disable-darwin-user) darwin_user="no"
+ ;;
+ --enable-darwin-user) darwin_user="yes"
+ ;;
+ --disable-bsd-user) bsd_user="no"
+ ;;
+ --enable-bsd-user) bsd_user="yes"
+ ;;
+ --enable-guest-base) guest_base="yes"
+ ;;
+ --disable-guest-base) guest_base="no"
+ ;;
+ --enable-user-pie) user_pie="yes"
+ ;;
+ --disable-user-pie) user_pie="no"
+ ;;
+ --enable-uname-release=*) uname_release="$optarg"
+ ;;
+ --sparc_cpu=*)
+ ;;
+ --enable-werror) werror="yes"
+ ;;
+ --disable-werror) werror="no"
+ ;;
+ --disable-curses) curses="no"
+ ;;
+ --enable-curses) curses="yes"
+ ;;
+ --disable-curl) curl="no"
+ ;;
+ --enable-curl) curl="yes"
+ ;;
+ --disable-fdt) fdt="no"
+ ;;
+ --enable-fdt) fdt="yes"
+ ;;
+ --disable-check-utests) check_utests="no"
+ ;;
+ --enable-check-utests) check_utests="yes"
+ ;;
+ --disable-nptl) nptl="no"
+ ;;
+ --enable-nptl) nptl="yes"
+ ;;
+ --enable-mixemu) mixemu="yes"
+ ;;
+ --disable-linux-aio) linux_aio="no"
+ ;;
+ --enable-linux-aio) linux_aio="yes"
+ ;;
+ --disable-attr) attr="no"
+ ;;
+ --enable-attr) attr="yes"
+ ;;
+ --enable-io-thread) io_thread="yes"
+ ;;
+ --disable-blobs) blobs="no"
+ ;;
+ --kerneldir=*) kerneldir="$optarg"
+ ;;
+ --with-pkgversion=*) pkgversion=" ($optarg)"
+ ;;
+ --disable-docs) docs="no"
+ ;;
+ --enable-docs) docs="yes"
+ ;;
+ --disable-cpu-emulation) cpu_emulation="no"
+ ;;
+ --disable-vhost-net) vhost_net="no"
+ ;;
+ --enable-vhost-net) vhost_net="yes"
+ ;;
+ --*dir)
+ ;;
+ --disable-rbd) rbd="no"
+ ;;
+ --enable-rbd) rbd="yes"
+ ;;
+ *) echo "ERROR: unknown option $opt"; show_help="yes"
+ ;;
+ esac
+done
+
+#
+# If cpu ~= sparc and sparc_cpu hasn't been defined, plug in the right
+# QEMU_CFLAGS/LDFLAGS (assume sparc_v8plus for 32-bit and sparc_v9 for 64-bit)
+#
+host_guest_base="no"
+case "$cpu" in
+ sparc) case $sparc_cpu in
+ v7|v8)
+ QEMU_CFLAGS="-mcpu=${sparc_cpu} -D__sparc_${sparc_cpu}__ $QEMU_CFLAGS"
+ ;;
+ v8plus|v8plusa)
+ QEMU_CFLAGS="-mcpu=ultrasparc -D__sparc_${sparc_cpu}__ $QEMU_CFLAGS"
+ ;;
+ *) # sparc_cpu not defined in the command line
+ QEMU_CFLAGS="-mcpu=ultrasparc -D__sparc_v8plus__ $QEMU_CFLAGS"
+ esac
+ LDFLAGS="-m32 $LDFLAGS"
+ QEMU_CFLAGS="-m32 -ffixed-g2 -ffixed-g3 $QEMU_CFLAGS"
+ if test "$solaris" = "no" ; then
+ QEMU_CFLAGS="-ffixed-g1 -ffixed-g6 $QEMU_CFLAGS"
+ helper_cflags="-ffixed-i0"
+ fi
+ ;;
+ sparc64)
+ QEMU_CFLAGS="-m64 -mcpu=ultrasparc -D__sparc_v9__ $QEMU_CFLAGS"
+ LDFLAGS="-m64 $LDFLAGS"
+ QEMU_CFLAGS="-ffixed-g5 -ffixed-g6 -ffixed-g7 $QEMU_CFLAGS"
+ if test "$solaris" != "no" ; then
+ QEMU_CFLAGS="-ffixed-g1 $QEMU_CFLAGS"
+ fi
+ ;;
+ s390)
+ QEMU_CFLAGS="-m31 -march=z990 $QEMU_CFLAGS"
+ LDFLAGS="-m31 $LDFLAGS"
+ host_guest_base="yes"
+ ;;
+ s390x)
+ QEMU_CFLAGS="-m64 -march=z990 $QEMU_CFLAGS"
+ LDFLAGS="-m64 $LDFLAGS"
+ host_guest_base="yes"
+ ;;
+ i386)
+ QEMU_CFLAGS="-m32 $QEMU_CFLAGS"
+ LDFLAGS="-m32 $LDFLAGS"
+ cc_i386='$(CC) -m32'
+ helper_cflags="-fomit-frame-pointer"
+ host_guest_base="yes"
+ ;;
+ x86_64)
+ QEMU_CFLAGS="-m64 $QEMU_CFLAGS"
+ LDFLAGS="-m64 $LDFLAGS"
+ cc_i386='$(CC) -m32'
+ host_guest_base="yes"
+ ;;
+ arm*)
+ host_guest_base="yes"
+ ;;
+ ppc*)
+ host_guest_base="yes"
+ ;;
+ mips*)
+ host_guest_base="yes"
+ ;;
+ ia64*)
+ host_guest_base="yes"
+ ;;
+ hppa*)
+ host_guest_base="yes"
+ ;;
+esac
+
+[ -z "$guest_base" ] && guest_base="$host_guest_base"
+
+if test x"$show_help" = x"yes" ; then
+cat << EOF
+
+Usage: configure [options]
+Options: [defaults in brackets after descriptions]
+
+EOF
+echo "Standard options:"
+echo " --help print this message"
+echo " --prefix=PREFIX install in PREFIX [$prefix]"
+echo " --interp-prefix=PREFIX where to find shared libraries, etc."
+echo " use %M for cpu name [$interp_prefix]"
+echo " --target-list=LIST set target list [$target_list]"
+echo ""
+echo "Advanced options (experts only):"
+echo " --source-path=PATH path of source code [$source_path]"
+echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
+echo " --cc=CC use C compiler CC [$cc]"
+echo " --host-cc=CC use C compiler CC [$host_cc] for code run at"
+echo " build time"
+echo " --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS"
+echo " --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS"
+echo " --make=MAKE use specified make [$make]"
+echo " --install=INSTALL use specified install [$install]"
+echo " --static enable static build [$static]"
+echo " --mandir=PATH install man pages in PATH"
+echo " --datadir=PATH install firmware in PATH"
+echo " --docdir=PATH install documentation in PATH"
+echo " --bindir=PATH install binaries in PATH"
+echo " --sysconfdir=PATH install config in PATH/qemu"
+echo " --enable-debug-tcg enable TCG debugging"
+echo " --disable-debug-tcg disable TCG debugging (default)"
+echo " --enable-debug enable common debug build options"
+echo " --enable-sparse enable sparse checker"
+echo " --disable-sparse disable sparse checker (default)"
+echo " --disable-strip disable stripping binaries"
+echo " --disable-werror disable compilation abort on warning"
+echo " --disable-sdl disable SDL"
+echo " --enable-sdl enable SDL"
+echo " --enable-cocoa enable COCOA (Mac OS X only)"
+echo " --audio-drv-list=LIST set audio drivers list:"
+echo " Available drivers: $audio_possible_drivers"
+echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_list]"
+echo " Available cards: $audio_possible_cards"
+echo " --block-drv-whitelist=L set block driver whitelist"
+echo " (affects only QEMU, not qemu-img)"
+echo " --enable-mixemu enable mixer emulation"
+echo " --disable-xen disable xen backend driver support"
+echo " --enable-xen enable xen backend driver support"
+echo " --disable-brlapi disable BrlAPI"
+echo " --enable-brlapi enable BrlAPI"
+echo " --disable-vnc-tls disable TLS encryption for VNC server"
+echo " --enable-vnc-tls enable TLS encryption for VNC server"
+echo " --disable-vnc-sasl disable SASL encryption for VNC server"
+echo " --enable-vnc-sasl enable SASL encryption for VNC server"
+echo " --disable-vnc-jpeg disable JPEG lossy compression for VNC server"
+echo " --enable-vnc-jpeg enable JPEG lossy compression for VNC server"
+echo " --disable-vnc-png disable PNG compression for VNC server (default)"
+echo " --enable-vnc-png enable PNG compression for VNC server"
+echo " --disable-vnc-thread disable threaded VNC server"
+echo " --enable-vnc-thread enable threaded VNC server"
+echo " --disable-curses disable curses output"
+echo " --enable-curses enable curses output"
+echo " --disable-curl disable curl connectivity"
+echo " --enable-curl enable curl connectivity"
+echo " --disable-fdt disable fdt device tree"
+echo " --enable-fdt enable fdt device tree"
+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-kvm disable KVM acceleration support"
+echo " --enable-kvm enable KVM acceleration support"
+echo " --disable-kvm-pit disable KVM pit support"
+echo " --enable-kvm-pit enable KVM pit support"
+echo " --disable-kvm-device-assignment disable KVM device assignment support"
+echo " --enable-kvm-device-assignment enable KVM device assignment support"
+echo " --disable-nptl disable usermode NPTL support"
+echo " --enable-nptl enable usermode NPTL support"
+echo " --enable-system enable all system emulation targets"
+echo " --disable-system disable all system emulation targets"
+echo " --enable-user enable supported user emulation targets"
+echo " --disable-user disable all user emulation targets"
+echo " --enable-linux-user enable all linux usermode emulation targets"
+echo " --disable-linux-user disable all linux usermode emulation targets"
+echo " --enable-darwin-user enable all darwin usermode emulation targets"
+echo " --disable-darwin-user disable all darwin usermode emulation targets"
+echo " --enable-bsd-user enable all BSD usermode emulation targets"
+echo " --disable-bsd-user disable all BSD usermode emulation targets"
+echo " --enable-guest-base enable GUEST_BASE support for usermode"
+echo " emulation targets"
+echo " --disable-guest-base disable GUEST_BASE support"
+echo " --enable-user-pie build usermode emulation targets as PIE"
+echo " --disable-user-pie do not build usermode emulation targets as PIE"
+echo " --fmod-lib path to FMOD library"
+echo " --fmod-inc path to FMOD includes"
+echo " --oss-lib path to OSS library"
+echo " --enable-uname-release=R Return R for uname -r in usermode emulation"
+echo " --sparc_cpu=V Build qemu for Sparc architecture v7, v8, v8plus, v8plusa, v9"
+echo " --disable-uuid disable uuid support"
+echo " --enable-uuid enable uuid support"
+echo " --disable-vde disable support for vde network"
+echo " --enable-vde enable support for vde network"
+echo " --disable-linux-aio disable Linux AIO support"
+echo " --enable-linux-aio enable Linux AIO support"
+echo " --disable-attr disables attr and xattr support"
+echo " --enable-attr enable attr and xattr support"
+echo " --enable-io-thread enable IO thread"
+echo " --disable-blobs disable installing provided firmware blobs"
+echo " --kerneldir=PATH look for kernel includes in PATH"
+echo " --disable-cpu-emulation disables use of qemu cpu emulation code"
+echo " --enable-docs enable documentation build"
+echo " --disable-docs disable documentation build"
+echo " --disable-vhost-net disable vhost-net acceleration support"
+echo " --enable-vhost-net enable vhost-net acceleration support"
+echo " --enable-trace-backend=B Set trace backend"
+echo " Available backends:" $("$source_path"/scripts/tracetool --list-backends)
+echo " --with-trace-file=NAME Full PATH,NAME of file to store traces"
+echo " Default:trace-<pid>"
+echo " --disable-spice disable spice"
+echo " --enable-spice enable spice"
+echo " --enable-rbd enable building the rados block device (rbd)"
+echo ""
+echo "NOTE: The object files are built at the place where configure is launched"
+exit 1
+fi
+
+# check that the C compiler works.
+cat > $TMPC <<EOF
+int main(void) {}
+EOF
+
+if compile_object ; then
+ : C compiler works ok
+else
+ echo "ERROR: \"$cc\" either does not exist or does not work"
+ exit 1
+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"
+cat > $TMPC << EOF
+int main(void) { return 0; }
+EOF
+for flag in $gcc_flags; do
+ if compile_prog "-Werror $QEMU_CFLAGS" "-Werror $flag" ; then
+ QEMU_CFLAGS="$QEMU_CFLAGS $flag"
+ fi
+done
+
+#
+# Solaris specific configure tool chain decisions
+#
+if test "$solaris" = "yes" ; then
+ if has $install; then
+ :
+ else
+ echo "Solaris install program not found. Use --install=/usr/ucb/install or"
+ echo "install fileutils from www.blastwave.org using pkg-get -i fileutils"
+ echo "to get ginstall which is used by default (which lives in /opt/csw/bin)"
+ exit 1
+ fi
+ if test "`path_of $install`" = "/usr/sbin/install" ; then
+ echo "Error: Solaris /usr/sbin/install is not an appropriate install program."
+ echo "try ginstall from the GNU fileutils available from www.blastwave.org"
+ echo "using pkg-get -i fileutils, or use --install=/usr/ucb/install"
+ exit 1
+ fi
+ if has ar; then
+ :
+ else
+ echo "Error: No path includes ar"
+ if test -f /usr/ccs/bin/ar ; then
+ echo "Add /usr/ccs/bin to your path and rerun configure"
+ fi
+ exit 1
+ fi
+fi
+
+
+if test -z "$target_list" ; then
+# these targets are portable
+ if [ "$softmmu" = "yes" ] ; then
+ target_list="\
+i386-softmmu \
+x86_64-softmmu \
+arm-softmmu \
+cris-softmmu \
+m68k-softmmu \
+microblaze-softmmu \
+mips-softmmu \
+mipsel-softmmu \
+mips64-softmmu \
+mips64el-softmmu \
+ppc-softmmu \
+ppcemb-softmmu \
+ppc64-softmmu \
+sh4-softmmu \
+sh4eb-softmmu \
+sparc-softmmu \
+sparc64-softmmu \
+"
+ fi
+# the following are Linux specific
+ if [ "$linux_user" = "yes" ] ; then
+ target_list="${target_list}\
+i386-linux-user \
+x86_64-linux-user \
+alpha-linux-user \
+arm-linux-user \
+armeb-linux-user \
+cris-linux-user \
+m68k-linux-user \
+microblaze-linux-user \
+mips-linux-user \
+mipsel-linux-user \
+ppc-linux-user \
+ppc64-linux-user \
+ppc64abi32-linux-user \
+sh4-linux-user \
+sh4eb-linux-user \
+sparc-linux-user \
+sparc64-linux-user \
+sparc32plus-linux-user \
+"
+ fi
+# the following are Darwin specific
+ if [ "$darwin_user" = "yes" ] ; then
+ target_list="$target_list i386-darwin-user ppc-darwin-user "
+ fi
+# the following are BSD specific
+ if [ "$bsd_user" = "yes" ] ; then
+ target_list="${target_list}\
+i386-bsd-user \
+x86_64-bsd-user \
+sparc-bsd-user \
+sparc64-bsd-user \
+"
+ fi
+else
+ target_list=`echo "$target_list" | sed -e 's/,/ /g'`
+fi
+if test -z "$target_list" ; then
+ echo "No targets enabled"
+ exit 1
+fi
+# see if system emulation was really requested
+case " $target_list " in
+ *"-softmmu "*) softmmu=yes
+ ;;
+ *) softmmu=no
+ ;;
+esac
+
+feature_not_found() {
+ feature=$1
+
+ echo "ERROR"
+ echo "ERROR: User requested feature $feature"
+ echo "ERROR: configure was not able to find it"
+ echo "ERROR"
+ exit 1;
+}
+
+if test -z "$cross_prefix" ; then
+
+# ---
+# big/little endian test
+cat > $TMPC << EOF
+#include <inttypes.h>
+int main(int argc, char ** argv){
+ volatile uint32_t i=0x01234567;
+ return (*((uint8_t*)(&i))) == 0x67;
+}
+EOF
+
+if compile_prog "" "" ; then
+$TMPE && bigendian="yes"
+else
+echo big/little test failed
+fi
+
+else
+
+# if cross compiling, cannot launch a program, so make a static guess
+case "$cpu" in
+ armv4b|hppa|m68k|mips|mips64|ppc|ppc64|s390|s390x|sparc|sparc64)
+ bigendian=yes
+ ;;
+esac
+
+fi
+
+# host long bits test, actually a pointer size test
+cat > $TMPC << EOF
+int sizeof_pointer_is_8[sizeof(void *) == 8 ? 1 : -1];
+EOF
+if compile_object; then
+hostlongbits=64
+else
+hostlongbits=32
+fi
+
+
+##########################################
+# NPTL probe
+
+if test "$nptl" != "no" ; then
+ cat > $TMPC <<EOF
+#include <sched.h>
+#include <linux/futex.h>
+void foo()
+{
+#if !defined(CLONE_SETTLS) || !defined(FUTEX_WAIT)
+#error bork
+#endif
+}
+EOF
+
+ if compile_object ; then
+ nptl=yes
+ else
+ if test "$nptl" = "yes" ; then
+ feature_not_found "nptl"
+ fi
+ nptl=no
+ fi
+fi
+
+##########################################
+# zlib check
+
+cat > $TMPC << EOF
+#include <zlib.h>
+int main(void) { zlibVersion(); return 0; }
+EOF
+if compile_prog "" "-lz" ; then
+ :
+else
+ echo
+ echo "Error: zlib check failed"
+ echo "Make sure to have the zlib libs and headers installed."
+ echo
+ exit 1
+fi
+
+##########################################
+# xen probe
+
+if test "$xen" != "no" ; then
+ xen_libs="-lxenstore -lxenctrl -lxenguest"
+ cat > $TMPC <<EOF
+#include <xenctrl.h>
+#include <xs.h>
+int main(void) { xs_daemon_open(); xc_interface_open(); return 0; }
+EOF
+ if compile_prog "" "$xen_libs" ; then
+ xen=yes
+ libs_softmmu="$xen_libs $libs_softmmu"
+ else
+ if test "$xen" = "yes" ; then
+ feature_not_found "xen"
+ fi
+ xen=no
+ fi
+fi
+
+##########################################
+# pkg-config probe
+
+if ! has $pkg_config; then
+ echo warning: proceeding without "$pkg_config" >&2
+ pkg_config=/bin/false
+fi
+
+##########################################
+# Sparse probe
+if test "$sparse" != "no" ; then
+ if has cgcc; then
+ sparse=yes
+ else
+ if test "$sparse" = "yes" ; then
+ feature_not_found "sparse"
+ fi
+ sparse=no
+ fi
+fi
+
+##########################################
+# SDL probe
+
+# Look for sdl configuration program (pkg-config or sdl-config). Try
+# sdl-config even without cross prefix, and favour pkg-config over sdl-config.
+if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then
+ sdl_config=sdl-config
+fi
+
+if $pkg_config sdl --modversion >/dev/null 2>&1; then
+ sdlconfig="$pkg_config sdl"
+ _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'`
+elif has ${sdl_config}; then
+ sdlconfig="$sdl_config"
+ _sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'`
+else
+ if test "$sdl" = "yes" ; then
+ feature_not_found "sdl"
+ fi
+ sdl=no
+fi
+if test -n "$cross_prefix" && test "`basename $sdlconfig`" = sdl-config; then
+ echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2
+fi
+
+sdl_too_old=no
+if test "$sdl" != "no" ; then
+ cat > $TMPC << EOF
+#include <SDL.h>
+#undef main /* We don't want SDL to override our main() */
+int main( void ) { return SDL_Init (SDL_INIT_VIDEO); }
+EOF
+ sdl_cflags=`$sdlconfig --cflags 2> /dev/null`
+ if test "$static" = "yes" ; then
+ sdl_libs=`$sdlconfig --static-libs 2>/dev/null`
+ else
+ sdl_libs=`$sdlconfig --libs 2> /dev/null`
+ fi
+ if compile_prog "$sdl_cflags" "$sdl_libs" ; then
+ if test "$_sdlversion" -lt 121 ; then
+ sdl_too_old=yes
+ else
+ if test "$cocoa" = "no" ; then
+ sdl=yes
+ fi
+ fi
+
+ # static link with sdl ? (note: sdl.pc's --static --libs is broken)
+ if test "$sdl" = "yes" -a "$static" = "yes" ; then
+ if test $? = 0 && echo $sdl_libs | grep -- -laa > /dev/null; then
+ sdl_libs="$sdl_libs `aalib-config --static-libs 2>/dev/null`"
+ sdl_cflags="$sdl_cflags `aalib-config --cflags 2>/dev/null`"
+ fi
+ if compile_prog "$sdl_cflags" "$sdl_libs" ; then
+ :
+ else
+ sdl=no
+ fi
+ fi # static link
+ else # sdl not found
+ if test "$sdl" = "yes" ; then
+ feature_not_found "sdl"
+ fi
+ sdl=no
+ fi # sdl compile test
+fi
+
+if test "$sdl" = "yes" ; then
+ cat > $TMPC <<EOF
+#include <SDL.h>
+#if defined(SDL_VIDEO_DRIVER_X11)
+#include <X11/XKBlib.h>
+#else
+#error No x11 support
+#endif
+int main(void) { return 0; }
+EOF
+ if compile_prog "$sdl_cflags" "$sdl_libs" ; then
+ sdl_libs="$sdl_libs -lX11"
+ fi
+ if test "$mingw32" = "yes" ; then
+ sdl_libs="`echo $sdl_libs | sed s/-mwindows//g` -mconsole"
+ fi
+ libs_softmmu="$sdl_libs $libs_softmmu"
+fi
+
+##########################################
+# VNC TLS detection
+if test "$vnc_tls" != "no" ; then
+ cat > $TMPC <<EOF
+#include <gnutls/gnutls.h>
+int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; }
+EOF
+ vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null`
+ vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null`
+ if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then
+ vnc_tls=yes
+ libs_softmmu="$vnc_tls_libs $libs_softmmu"
+ else
+ if test "$vnc_tls" = "yes" ; then
+ feature_not_found "vnc-tls"
+ fi
+ vnc_tls=no
+ fi
+fi
+
+##########################################
+# VNC SASL detection
+if test "$vnc_sasl" != "no" ; then
+ cat > $TMPC <<EOF
+#include <sasl/sasl.h>
+#include <stdio.h>
+int main(void) { sasl_server_init(NULL, "qemu"); return 0; }
+EOF
+ # Assuming Cyrus-SASL installed in /usr prefix
+ vnc_sasl_cflags=""
+ vnc_sasl_libs="-lsasl2"
+ if compile_prog "$vnc_sasl_cflags" "$vnc_sasl_libs" ; then
+ vnc_sasl=yes
+ libs_softmmu="$vnc_sasl_libs $libs_softmmu"
+ else
+ if test "$vnc_sasl" = "yes" ; then
+ feature_not_found "vnc-sasl"
+ fi
+ vnc_sasl=no
+ fi
+fi
+
+##########################################
+# VNC JPEG detection
+if test "$vnc_jpeg" != "no" ; then
+cat > $TMPC <<EOF
+#include <stdio.h>
+#include <jpeglib.h>
+int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; }
+EOF
+ vnc_jpeg_cflags=""
+ vnc_jpeg_libs="-ljpeg"
+ if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then
+ vnc_jpeg=yes
+ libs_softmmu="$vnc_jpeg_libs $libs_softmmu"
+ else
+ if test "$vnc_jpeg" = "yes" ; then
+ feature_not_found "vnc-jpeg"
+ fi
+ vnc_jpeg=no
+ fi
+fi
+
+##########################################
+# VNC PNG detection
+if test "$vnc_png" != "no" ; then
+cat > $TMPC <<EOF
+//#include <stdio.h>
+#include <png.h>
+#include <stddef.h>
+int main(void) {
+ png_structp png_ptr;
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ return 0;
+}
+EOF
+ vnc_png_cflags=""
+ vnc_png_libs="-lpng"
+ if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then
+ vnc_png=yes
+ libs_softmmu="$vnc_png_libs $libs_softmmu"
+ else
+ if test "$vnc_png" = "yes" ; then
+ feature_not_found "vnc-png"
+ fi
+ vnc_png=no
+ fi
+fi
+
+##########################################
+# fnmatch() probe, used for ACL routines
+fnmatch="no"
+cat > $TMPC << EOF
+#include <fnmatch.h>
+int main(void)
+{
+ fnmatch("foo", "foo", 0);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ fnmatch="yes"
+fi
+
+##########################################
+# uuid_generate() probe, used for vdi block driver
+if test "$uuid" != "no" ; then
+ uuid_libs="-luuid"
+ cat > $TMPC << EOF
+#include <uuid/uuid.h>
+int main(void)
+{
+ uuid_t my_uuid;
+ uuid_generate(my_uuid);
+ return 0;
+}
+EOF
+ if compile_prog "" "$uuid_libs" ; then
+ uuid="yes"
+ libs_softmmu="$uuid_libs $libs_softmmu"
+ libs_tools="$uuid_libs $libs_tools"
+ else
+ if test "$uuid" = "yes" ; then
+ feature_not_found "uuid"
+ fi
+ uuid=no
+ fi
+fi
+
+##########################################
+# xfsctl() probe, used for raw-posix
+if test "$xfs" != "no" ; then
+ cat > $TMPC << EOF
+#include <xfs/xfs.h>
+int main(void)
+{
+ xfsctl(NULL, 0, 0, NULL);
+ return 0;
+}
+EOF
+ if compile_prog "" "" ; then
+ xfs="yes"
+ else
+ if test "$xfs" = "yes" ; then
+ feature_not_found "xfs"
+ fi
+ xfs=no
+ fi
+fi
+
+##########################################
+# vde libraries probe
+if test "$vde" != "no" ; then
+ vde_libs="-lvdeplug"
+ cat > $TMPC << EOF
+#include <libvdeplug.h>
+int main(void)
+{
+ struct vde_open_args a = {0, 0, 0};
+ vde_open("", "", &a);
+ return 0;
+}
+EOF
+ if compile_prog "" "$vde_libs" ; then
+ vde=yes
+ libs_softmmu="$vde_libs $libs_softmmu"
+ libs_tools="$vde_libs $libs_tools"
+ else
+ if test "$vde" = "yes" ; then
+ feature_not_found "vde"
+ fi
+ vde=no
+ fi
+fi
+
+##########################################
+# Sound support libraries probe
+
+audio_drv_probe()
+{
+ drv=$1
+ hdr=$2
+ lib=$3
+ exp=$4
+ cfl=$5
+ cat > $TMPC << EOF
+#include <$hdr>
+int main(void) { $exp }
+EOF
+ if compile_prog "$cfl" "$lib" ; then
+ :
+ else
+ echo
+ echo "Error: $drv check failed"
+ echo "Make sure to have the $drv libs and headers installed."
+ echo
+ exit 1
+ fi
+}
+
+audio_drv_list=`echo "$audio_drv_list" | sed -e 's/,/ /g'`
+for drv in $audio_drv_list; do
+ case $drv in
+ alsa)
+ audio_drv_probe $drv alsa/asoundlib.h -lasound \
+ "snd_pcm_t **handle; return snd_pcm_close(*handle);"
+ libs_softmmu="-lasound $libs_softmmu"
+ ;;
+
+ fmod)
+ if test -z $fmod_lib || test -z $fmod_inc; then
+ echo
+ echo "Error: You must specify path to FMOD library and headers"
+ echo "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so"
+ echo
+ exit 1
+ fi
+ audio_drv_probe $drv fmod.h $fmod_lib "return FSOUND_GetVersion();" "-I $fmod_inc"
+ libs_softmmu="$fmod_lib $libs_softmmu"
+ ;;
+
+ esd)
+ audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);'
+ libs_softmmu="-lesd $libs_softmmu"
+ audio_pt_int="yes"
+ ;;
+
+ pa)
+ audio_drv_probe $drv pulse/simple.h "-lpulse-simple -lpulse" \
+ "pa_simple *s = NULL; pa_simple_free(s); return 0;"
+ libs_softmmu="-lpulse -lpulse-simple $libs_softmmu"
+ audio_pt_int="yes"
+ ;;
+
+ coreaudio)
+ libs_softmmu="-framework CoreAudio $libs_softmmu"
+ ;;
+
+ dsound)
+ libs_softmmu="-lole32 -ldxguid $libs_softmmu"
+ audio_win_int="yes"
+ ;;
+
+ oss)
+ libs_softmmu="$oss_lib $libs_softmmu"
+ ;;
+
+ sdl|wav)
+ # XXX: Probes for CoreAudio, DirectSound, SDL(?)
+ ;;
+
+ winwave)
+ libs_softmmu="-lwinmm $libs_softmmu"
+ audio_win_int="yes"
+ ;;
+
+ *)
+ echo "$audio_possible_drivers" | grep -q "\<$drv\>" || {
+ echo
+ echo "Error: Unknown driver '$drv' selected"
+ echo "Possible drivers are: $audio_possible_drivers"
+ echo
+ exit 1
+ }
+ ;;
+ esac
+done
+
+##########################################
+# BrlAPI probe
+
+if test "$brlapi" != "no" ; then
+ brlapi_libs="-lbrlapi"
+ cat > $TMPC << EOF
+#include <brlapi.h>
+#include <stddef.h>
+int main( void ) { return brlapi__openConnection (NULL, NULL, NULL); }
+EOF
+ if compile_prog "" "$brlapi_libs" ; then
+ brlapi=yes
+ libs_softmmu="$brlapi_libs $libs_softmmu"
+ else
+ if test "$brlapi" = "yes" ; then
+ feature_not_found "brlapi"
+ fi
+ brlapi=no
+ fi
+fi
+
+##########################################
+# curses probe
+curses_list="-lncurses -lcurses"
+
+if test "$curses" != "no" ; then
+ curses_found=no
+ cat > $TMPC << EOF
+#include <curses.h>
+#ifdef __OpenBSD__
+#define resize_term resizeterm
+#endif
+int main(void) { resize_term(0, 0); return curses_version(); }
+EOF
+ for curses_lib in $curses_list; do
+ if compile_prog "" "$curses_lib" ; then
+ curses_found=yes
+ libs_softmmu="$curses_lib $libs_softmmu"
+ break
+ fi
+ done
+ if test "$curses_found" = "yes" ; then
+ curses=yes
+ else
+ if test "$curses" = "yes" ; then
+ feature_not_found "curses"
+ fi
+ curses=no
+ fi
+fi
+
+##########################################
+# curl probe
+
+if $pkg_config libcurl --modversion >/dev/null 2>&1; then
+ curlconfig="$pkg_config libcurl"
+else
+ curlconfig=curl-config
+fi
+
+if test "$curl" != "no" ; then
+ cat > $TMPC << EOF
+#include <curl/curl.h>
+int main(void) { return curl_easy_init(); }
+EOF
+ curl_cflags=`$curlconfig --cflags 2>/dev/null`
+ curl_libs=`$curlconfig --libs 2>/dev/null`
+ if compile_prog "$curl_cflags" "$curl_libs" ; then
+ curl=yes
+ libs_tools="$curl_libs $libs_tools"
+ libs_softmmu="$curl_libs $libs_softmmu"
+ else
+ if test "$curl" = "yes" ; then
+ feature_not_found "curl"
+ fi
+ curl=no
+ fi
+fi # test "$curl"
+
+##########################################
+# check framework probe
+
+if test "$check_utests" != "no" ; then
+ cat > $TMPC << EOF
+#include <check.h>
+int main(void) { suite_create("qemu test"); return 0; }
+EOF
+ check_libs=`$pkg_config --libs check`
+ if compile_prog "" $check_libs ; then
+ check_utests=yes
+ libs_tools="$check_libs $libs_tools"
+ else
+ if test "$check_utests" = "yes" ; then
+ feature_not_found "check"
+ fi
+ check_utests=no
+ fi
+fi # test "$check_utests"
+
+##########################################
+# bluez support probe
+if test "$bluez" != "no" ; then
+ cat > $TMPC << EOF
+#include <bluetooth/bluetooth.h>
+int main(void) { return bt_error(0); }
+EOF
+ bluez_cflags=`$pkg_config --cflags bluez 2> /dev/null`
+ bluez_libs=`$pkg_config --libs bluez 2> /dev/null`
+ if compile_prog "$bluez_cflags" "$bluez_libs" ; then
+ bluez=yes
+ libs_softmmu="$bluez_libs $libs_softmmu"
+ else
+ if test "$bluez" = "yes" ; then
+ feature_not_found "bluez"
+ fi
+ bluez="no"
+ fi
+fi
+
+##########################################
+# kvm probe
+if test "$kvm" != "no" ; then
+ cat > $TMPC <<EOF
+#include <linux/kvm.h>
+#if !defined(KVM_API_VERSION) || KVM_API_VERSION < 12 || KVM_API_VERSION > 12
+#error Invalid KVM version
+#endif
+EOF
+ must_have_caps="KVM_CAP_USER_MEMORY \
+ KVM_CAP_DESTROY_MEMORY_REGION_WORKS \
+ KVM_CAP_COALESCED_MMIO \
+ KVM_CAP_SYNC_MMU \
+ "
+ if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) ; then
+ must_have_caps="$caps \
+ KVM_CAP_SET_TSS_ADDR \
+ KVM_CAP_EXT_CPUID \
+ KVM_CAP_CLOCKSOURCE \
+ KVM_CAP_NOP_IO_DELAY \
+ KVM_CAP_PV_MMU \
+ KVM_CAP_MP_STATE \
+ KVM_CAP_USER_NMI \
+ "
+ fi
+ for c in $must_have_caps ; do
+ cat >> $TMPC <<EOF
+#if !defined($c)
+#error Missing KVM capability $c
+#endif
+EOF
+ done
+ cat >> $TMPC <<EOF
+int main(void) { return 0; }
+EOF
+ if test "$kerneldir" != "" ; then
+ kvm_cflags=-I"$kerneldir"/include
+ if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) \
+ -a -d "$kerneldir/arch/x86/include" ; then
+ kvm_cflags="$kvm_cflags -I$kerneldir/arch/x86/include"
+ elif test "$cpu" = "ppc" -a -d "$kerneldir/arch/powerpc/include" ; then
+ kvm_cflags="$kvm_cflags -I$kerneldir/arch/powerpc/include"
+ elif test "$cpu" = "s390x" -a -d "$kerneldir/arch/s390/include" ; then
+ kvm_cflags="$kvm_cflags -I$kerneldir/arch/s390/include"
+ elif test -d "$kerneldir/arch/$cpu/include" ; then
+ kvm_cflags="$kvm_cflags -I$kerneldir/arch/$cpu/include"
+ fi
+ else
+ kvm_cflags=`$pkg_config --cflags kvm-kmod 2>/dev/null`
+ if test "$kvm_cflags" = ""; then
+ case "$cpu" in
+ i386 | x86_64)
+ kvm_arch="x86"
+ ;;
+ ppc)
+ kvm_arch="powerpc"
+ ;;
+ *)
+ kvm_arch="$cpu"
+ ;;
+ esac
+ kvm_cflags="-I$source_path/kvm/include"
+ kvm_cflags="$kvm_cflags -include $source_path/kvm/include/linux/config.h"
+ kvm_cflags="$kvm_cflags -I$source_path/kvm/include/$kvm_arch"
+ fi
+ fi
+ kvm_cflags="$kvm_cflags -idirafter $source_path/compat"
+ if compile_prog "$kvm_cflags" "" ; then
+ kvm=yes
+ cat > $TMPC <<EOF
+#include <linux/kvm_para.h>
+int main(void) { return 0; }
+EOF
+ if compile_prog "$kvm_cflags" "" ; then
+ kvm_para=yes
+ fi
+ else
+ if test "$kvm" = "yes" ; then
+ if has awk && has grep; then
+ kvmerr=`LANG=C $cc $QEMU_CFLAGS -o $TMPE $kvm_cflags $TMPC 2>&1 \
+ | grep "error: " \
+ | awk -F "error: " '{if (NR>1) printf(", "); printf("%s",$2);}'`
+ if test "$kvmerr" != "" ; then
+ echo -e "${kvmerr}\n\
+NOTE: To enable KVM support, update your kernel to 2.6.29+ or install \
+recent kvm-kmod from http://sourceforge.net/projects/kvm."
+ fi
+ fi
+ feature_not_found "kvm"
+ fi
+ kvm=no
+ fi
+fi
+
+##########################################
+# test for KVM_CAP_PIT
+
+if test "$kvm_cap_pit" != "no" ; then
+ if test "$kvm" = "no" -a "$kvm_cap_pit" = "yes" ; then
+ feature_not_found "kvm_cap_pit (kvm is not enabled)"
+ fi
+ cat > $TMPC <<EOF
+#include <linux/kvm.h>
+#ifndef KVM_CAP_PIT
+#error "kvm no pit capability"
+#endif
+int main(void) { return 0; }
+EOF
+ if compile_prog "$kvm_cflags" ""; then
+ kvm_cap_pit=yes
+ else
+ if test "$kvm_cap_pit" = "yes" ; then
+ feature_not_found "kvm_cap_pit"
+ fi
+ kvm_cap_pit=no
+ fi
+fi
+
+##########################################
+# test for KVM_CAP_DEVICE_ASSIGNMENT
+
+if test "$kvm_cap_device_assignment" != "no" ; then
+ if test "$kvm" = "no" -a "$kvm_cap_device_assignment" = "yes" ; then
+ feature_not_found "kvm_cap_device_assignment (kvm is not enabled)"
+ fi
+ cat > $TMPC <<EOF
+#include <linux/kvm.h>
+#ifndef KVM_CAP_DEVICE_ASSIGNMENT
+#error "kvm no device assignment capability"
+#endif
+int main(void) { return 0; }
+EOF
+ if compile_prog "$kvm_cflags" "" ; then
+ kvm_cap_device_assignment=yes
+ else
+ if test "$kvm_cap_device_assignment" = "yes" ; then
+ feature_not_found "kvm_cap_device_assigment"
+ fi
+ kvm_cap_device_assignment=no
+ fi
+fi
+
+##########################################
+# libpci header probe for kvm_cap_device_assignment
+if test $kvm_cap_device_assignment = "yes" ; then
+ cat > $TMPC << EOF
+#include <pci/header.h>
+#ifndef PCI_VENDOR_ID
+#error NO LIBPCI HEADER
+#endif
+int main(void) { return 0; }
+EOF
+ if compile_prog "" "" ; then
+ kvm_cap_device_assignment=yes
+ else
+ echo
+ echo "Error: libpci header check failed"
+ echo "Disable KVM Device Assignment capability."
+ echo
+ kvm_cap_device_assignment=no
+ fi
+fi
+
+##########################################
+# test for vhost net
+
+if test "$vhost_net" != "no"; then
+ if test "$kvm" != "no"; then
+ cat > $TMPC <<EOF
+ #include <linux/vhost.h>
+ int main(void) { return 0; }
+EOF
+ if compile_prog "$kvm_cflags" "" ; then
+ vhost_net=yes
+ else
+ if test "$vhost_net" = "yes" ; then
+ feature_not_found "vhost-net"
+ fi
+ vhost_net=no
+ fi
+ else
+ if test "$vhost_net" = "yes" ; then
+ echo "NOTE: vhost-net feature requires KVM (--enable-kvm)."
+ feature_not_found "vhost-net"
+ fi
+ vhost_net=no
+ fi
+fi
+
+##########################################
+# pthread probe
+PTHREADLIBS_LIST="-lpthread -lpthreadGC2"
+
+pthread=no
+cat > $TMPC << EOF
+#include <pthread.h>
+int main(void) { pthread_create(0,0,0,0); return 0; }
+EOF
+if compile_prog "" "" ; then
+ pthread=yes
+else
+ for pthread_lib in $PTHREADLIBS_LIST; do
+ if compile_prog "" "$pthread_lib" ; then
+ pthread=yes
+ LIBS="$pthread_lib $LIBS"
+ break
+ fi
+ done
+fi
+
+if test "$mingw32" != yes -a "$pthread" = no; then
+ echo
+ echo "Error: pthread check failed"
+ echo "Make sure to have the pthread libs and headers installed."
+ echo
+ exit 1
+fi
+
+##########################################
+# rbd probe
+if test "$rbd" != "no" ; then
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <rados/librados.h>
+int main(void) { rados_initialize(0, NULL); return 0; }
+EOF
+ rbd_libs="-lrados"
+ if compile_prog "" "$rbd_libs" ; then
+ librados_too_old=no
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <rados/librados.h>
+#ifndef CEPH_OSD_TMAP_SET
+#error missing CEPH_OSD_TMAP_SET
+#endif
+int main(void) {
+ int (*func)(const rados_pool_t pool, uint64_t *snapid) = rados_selfmanaged_snap_create;
+ rados_initialize(0, NULL);
+ return 0;
+}
+EOF
+ if compile_prog "" "$rbd_libs" ; then
+ rbd=yes
+ libs_tools="$rbd_libs $libs_tools"
+ libs_softmmu="$rbd_libs $libs_softmmu"
+ else
+ rbd=no
+ librados_too_old=yes
+ fi
+ else
+ if test "$rbd" = "yes" ; then
+ feature_not_found "rados block device"
+ fi
+ rbd=no
+ fi
+ if test "$librados_too_old" = "yes" ; then
+ echo "-> Your librados version is too old - upgrade needed to have rbd support"
+ fi
+fi
+
+##########################################
+# linux-aio probe
+
+if test "$linux_aio" != "no" ; then
+ cat > $TMPC <<EOF
+#include <libaio.h>
+#include <sys/eventfd.h>
+#include <stddef.h>
+int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); return 0; }
+EOF
+ if compile_prog "" "-laio" ; then
+ linux_aio=yes
+ libs_softmmu="$libs_softmmu -laio"
+ libs_tools="$libs_tools -laio"
+ else
+ if test "$linux_aio" = "yes" ; then
+ feature_not_found "linux AIO"
+ fi
+ linux_aio=no
+ fi
+fi
+
+##########################################
+# attr probe
+
+if test "$attr" != "no" ; then
+ cat > $TMPC <<EOF
+#include <stdio.h>
+#include <sys/types.h>
+#include <attr/xattr.h>
+int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; }
+EOF
+ if compile_prog "" "-lattr" ; then
+ attr=yes
+ LIBS="-lattr $LIBS"
+ else
+ if test "$attr" = "yes" ; then
+ feature_not_found "ATTR"
+ fi
+ attr=no
+ fi
+fi
+
+##########################################
+# iovec probe
+cat > $TMPC <<EOF
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+int main(void) { struct iovec iov; return 0; }
+EOF
+iovec=no
+if compile_prog "" "" ; then
+ iovec=yes
+fi
+
+##########################################
+# preadv probe
+cat > $TMPC <<EOF
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+int main(void) { preadv; }
+EOF
+preadv=no
+if compile_prog "" "" ; then
+ preadv=yes
+fi
+
+##########################################
+# fdt probe
+if test "$fdt" != "no" ; then
+ fdt_libs="-lfdt"
+ cat > $TMPC << EOF
+int main(void) { return 0; }
+EOF
+ if compile_prog "" "$fdt_libs" ; then
+ fdt=yes
+ libs_softmmu="$fdt_libs $libs_softmmu"
+ else
+ if test "$fdt" = "yes" ; then
+ feature_not_found "fdt"
+ fi
+ fdt=no
+ fi
+fi
+
+#
+# Check for xxxat() functions when we are building linux-user
+# emulator. This is done because older glibc versions don't
+# have syscall stubs for these implemented.
+#
+atfile=no
+cat > $TMPC << EOF
+#define _ATFILE_SOURCE
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+ /* try to unlink nonexisting file */
+ return (unlinkat(AT_FDCWD, "nonexistent_file", 0));
+}
+EOF
+if compile_prog "" "" ; then
+ atfile=yes
+fi
+
+# Check for inotify functions when we are building linux-user
+# emulator. This is done because older glibc versions don't
+# have syscall stubs for these implemented. In that case we
+# don't provide them even if kernel supports them.
+#
+inotify=no
+cat > $TMPC << EOF
+#include <sys/inotify.h>
+
+int
+main(void)
+{
+ /* try to start inotify */
+ return inotify_init();
+}
+EOF
+if compile_prog "" "" ; then
+ inotify=yes
+fi
+
+inotify1=no
+cat > $TMPC << EOF
+#include <sys/inotify.h>
+
+int
+main(void)
+{
+ /* try to start inotify */
+ return inotify_init1(0);
+}
+EOF
+if compile_prog "" "" ; then
+ inotify1=yes
+fi
+
+# check if utimensat and futimens are supported
+utimens=no
+cat > $TMPC << EOF
+#define _ATFILE_SOURCE
+#include <stddef.h>
+#include <fcntl.h>
+
+int main(void)
+{
+ utimensat(AT_FDCWD, "foo", NULL, 0);
+ futimens(0, NULL);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ utimens=yes
+fi
+
+# check if pipe2 is there
+pipe2=no
+cat > $TMPC << EOF
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(void)
+{
+ int pipefd[2];
+ pipe2(pipefd, O_CLOEXEC);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ pipe2=yes
+fi
+
+# check if accept4 is there
+accept4=no
+cat > $TMPC << EOF
+#include <sys/socket.h>
+#include <stddef.h>
+
+int main(void)
+{
+ accept4(0, NULL, NULL, SOCK_CLOEXEC);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ accept4=yes
+fi
+
+# check if tee/splice is there. vmsplice was added same time.
+splice=no
+cat > $TMPC << EOF
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+
+int main(void)
+{
+ int len, fd;
+ len = tee(STDIN_FILENO, STDOUT_FILENO, INT_MAX, SPLICE_F_NONBLOCK);
+ splice(STDIN_FILENO, NULL, fd, NULL, len, SPLICE_F_MOVE);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ splice=yes
+fi
+
+##########################################
+# signalfd probe
+signalfd="no"
+cat > $TMPC << EOF
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <signal.h>
+int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); }
+EOF
+
+if compile_prog "" "" ; then
+ signalfd=yes
+fi
+
+# check if eventfd is supported
+eventfd=no
+cat > $TMPC << EOF
+#include <sys/eventfd.h>
+
+int main(void)
+{
+ int efd = eventfd(0, 0);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ eventfd=yes
+fi
+
+# check for fallocate
+fallocate=no
+cat > $TMPC << EOF
+#include <fcntl.h>
+
+int main(void)
+{
+ fallocate(0, 0, 0, 0);
+ return 0;
+}
+EOF
+if compile_prog "$ARCH_CFLAGS" "" ; then
+ fallocate=yes
+fi
+
+# check for sync_file_range
+sync_file_range=no
+cat > $TMPC << EOF
+#include <fcntl.h>
+
+int main(void)
+{
+ sync_file_range(0, 0, 0, 0);
+ return 0;
+}
+EOF
+if compile_prog "$ARCH_CFLAGS" "" ; then
+ sync_file_range=yes
+fi
+
+# check for linux/fiemap.h and FS_IOC_FIEMAP
+fiemap=no
+cat > $TMPC << EOF
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
+
+int main(void)
+{
+ ioctl(0, FS_IOC_FIEMAP, 0);
+ return 0;
+}
+EOF
+if compile_prog "$ARCH_CFLAGS" "" ; then
+ fiemap=yes
+fi
+
+# check for dup3
+dup3=no
+cat > $TMPC << EOF
+#include <unistd.h>
+
+int main(void)
+{
+ dup3(0, 0, 0);
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ dup3=yes
+fi
+
+# Check if tools are available to build documentation.
+if test "$docs" != "no" ; then
+ if has makeinfo && has pod2man; then
+ docs=yes
+ else
+ if test "$docs" = "yes" ; then
+ feature_not_found "docs"
+ fi
+ docs=no
+ fi
+fi
+
+# Search for bswap_32 function
+byteswap_h=no
+cat > $TMPC << EOF
+#include <byteswap.h>
+int main(void) { return bswap_32(0); }
+EOF
+if compile_prog "" "" ; then
+ byteswap_h=yes
+fi
+
+# Search for bswap_32 function
+bswap_h=no
+cat > $TMPC << EOF
+#include <sys/endian.h>
+#include <sys/types.h>
+#include <machine/bswap.h>
+int main(void) { return bswap32(0); }
+EOF
+if compile_prog "" "" ; then
+ bswap_h=yes
+fi
+
+##########################################
+# Do we need librt
+cat > $TMPC <<EOF
+#include <signal.h>
+#include <time.h>
+int main(void) { clockid_t id; return clock_gettime(id, NULL); }
+EOF
+
+if compile_prog "" "" ; then
+ :
+elif compile_prog "" "-lrt" ; then
+ LIBS="-lrt $LIBS"
+fi
+
+if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \
+ "$aix" != "yes" -a "$haiku" != "yes" ; then
+ libs_softmmu="-lutil $libs_softmmu"
+fi
+
+##########################################
+# check if the compiler defines offsetof
+
+need_offsetof=yes
+cat > $TMPC << EOF
+#include <stddef.h>
+int main(void) { struct s { int f; }; return offsetof(struct s, f); }
+EOF
+if compile_prog "" "" ; then
+ need_offsetof=no
+fi
+
+##########################################
+# check if the compiler understands attribute warn_unused_result
+#
+# This could be smarter, but gcc -Werror does not error out even when warning
+# about attribute warn_unused_result
+
+gcc_attribute_warn_unused_result=no
+cat > $TMPC << EOF
+#if defined(__GNUC__) && (__GNUC__ < 4) && defined(__GNUC_MINOR__) && (__GNUC__ < 4)
+#error gcc 3.3 or older
+#endif
+int main(void) { return 0;}
+EOF
+if compile_prog "" ""; then
+ gcc_attribute_warn_unused_result=yes
+fi
+
+# spice probe
+if test "$spice" != "no" ; then
+ cat > $TMPC << EOF
+#include <spice.h>
+int main(void) { spice_server_new(); return 0; }
+EOF
+ spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null)
+ spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null)
+ if $pkg_config --atleast-version=0.5.3 spice-server >/dev/null 2>&1 && \
+ compile_prog "$spice_cflags" "$spice_libs" ; then
+ spice="yes"
+ libs_softmmu="$libs_softmmu $spice_libs"
+ QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
+ else
+ if test "$spice" = "yes" ; then
+ feature_not_found "spice"
+ fi
+ spice="no"
+ fi
+fi
+
+##########################################
+
+##########################################
+# check if we have fdatasync
+
+fdatasync=no
+cat > $TMPC << EOF
+#include <unistd.h>
+int main(void) { return fdatasync(0); }
+EOF
+if compile_prog "" "" ; then
+ fdatasync=yes
+fi
+
+##########################################
+# check if we have madvise
+
+madvise=no
+cat > $TMPC << EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <stddef.h>
+int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }
+EOF
+if compile_prog "" "" ; then
+ madvise=yes
+fi
+
+##########################################
+# check if we have posix_madvise
+
+posix_madvise=no
+cat > $TMPC << EOF
+#include <sys/mman.h>
+#include <stddef.h>
+int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }
+EOF
+if compile_prog "" "" ; then
+ posix_madvise=yes
+fi
+
+##########################################
+# check if trace backend exists
+
+sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null
+if test "$?" -ne 0 ; then
+ echo
+ echo "Error: invalid trace backend"
+ echo "Please choose a supported trace backend."
+ echo
+ exit 1
+fi
+
+##########################################
+# For 'ust' backend, test if ust headers are present
+if test "$trace_backend" = "ust"; then
+ cat > $TMPC << EOF
+#include <ust/tracepoint.h>
+#include <ust/marker.h>
+int main(void) { return 0; }
+EOF
+ if compile_prog "" "" ; then
+ LIBS="-lust $LIBS"
+ else
+ echo
+ echo "Error: Trace backend 'ust' missing libust header files"
+ echo
+ exit 1
+ fi
+fi
+
+##########################################
+# For 'dtrace' backend, test if 'dtrace' command is present
+if test "$trace_backend" = "dtrace"; then
+ if ! has 'dtrace' ; then
+ echo
+ echo "Error: dtrace command is not found in PATH $PATH"
+ echo
+ exit 1
+ fi
+ trace_backend_stap="no"
+ if has 'stap' ; then
+ trace_backend_stap="yes"
+ fi
+fi
+
+##########################################
+# End of CC checks
+# After here, no more $cc or $ld runs
+
+if test "$debug" = "no" ; then
+ CFLAGS="-O2 $CFLAGS"
+fi
+
+# Consult white-list to determine whether to enable werror
+# by default. Only enable by default for git builds
+z_version=`cut -f3 -d. $source_path/VERSION`
+
+if test -z "$werror" ; then
+ if test "$z_version" = "50" -a \
+ "$linux" = "yes" ; then
+ werror="yes"
+ else
+ werror="no"
+ fi
+fi
+
+# Disable zero malloc errors for official releases unless explicitly told to
+# enable/disable
+if test -z "$zero_malloc" ; then
+ if test "$z_version" = "50" ; then
+ zero_malloc="no"
+ else
+ zero_malloc="yes"
+ fi
+fi
+
+if test "$werror" = "yes" ; then
+ QEMU_CFLAGS="-Werror $QEMU_CFLAGS"
+fi
+
+if test "$solaris" = "no" ; then
+ if $ld --version 2>/dev/null | grep "GNU ld" >/dev/null 2>/dev/null ; then
+ LDFLAGS="-Wl,--warn-common $LDFLAGS"
+ fi
+fi
+
+# Use ASLR, no-SEH and DEP if available
+if test "$mingw32" = "yes" ; then
+ for flag in --dynamicbase --no-seh --nxcompat; do
+ if $ld --help 2>/dev/null | grep ".$flag" >/dev/null 2>/dev/null ; then
+ LDFLAGS="-Wl,$flag $LDFLAGS"
+ fi
+ done
+fi
+
+confdir=$sysconfdir$confsuffix
+
+tools=
+if test "$softmmu" = yes ; then
+ tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools"
+ if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then
+ tools="qemu-nbd\$(EXESUF) $tools"
+ if [ "$check_utests" = "yes" ]; then
+ tools="check-qint check-qstring check-qdict check-qlist $tools"
+ tools="check-qfloat check-qjson $tools"
+ fi
+ fi
+fi
+
+# Mac OS X ships with a broken assembler
+roms=
+if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \
+ "$targetos" != "Darwin" -a "$targetos" != "SunOS" -a \
+ "$softmmu" = yes ; then
+ roms="optionrom"
+fi
+
+
+echo "Install prefix $prefix"
+echo "BIOS directory `eval echo $datadir`"
+echo "binary directory `eval echo $bindir`"
+echo "config directory `eval echo $sysconfdir`"
+if test "$mingw32" = "no" ; then
+echo "Manual directory `eval echo $mandir`"
+echo "ELF interp prefix $interp_prefix"
+fi
+echo "Source path $source_path"
+echo "C compiler $cc"
+echo "Host C compiler $host_cc"
+echo "CFLAGS $CFLAGS"
+echo "QEMU_CFLAGS $QEMU_CFLAGS"
+echo "LDFLAGS $LDFLAGS"
+echo "make $make"
+echo "install $install"
+echo "host CPU $cpu"
+echo "host big endian $bigendian"
+echo "target list $target_list"
+echo "tcg debug enabled $debug_tcg"
+echo "Mon debug enabled $debug_mon"
+echo "gprof enabled $gprof"
+echo "sparse enabled $sparse"
+echo "strip binaries $strip_opt"
+echo "profiler $profiler"
+echo "static build $static"
+echo "-Werror enabled $werror"
+if test "$darwin" = "yes" ; then
+ echo "Cocoa support $cocoa"
+fi
+echo "SDL support $sdl"
+echo "curses support $curses"
+echo "curl support $curl"
+echo "check support $check_utests"
+echo "mingw32 support $mingw32"
+echo "Audio drivers $audio_drv_list"
+echo "Extra audio cards $audio_card_list"
+echo "Block whitelist $block_drv_whitelist"
+echo "Mixer emulation $mixemu"
+echo "VNC TLS support $vnc_tls"
+echo "VNC SASL support $vnc_sasl"
+echo "VNC JPEG support $vnc_jpeg"
+echo "VNC PNG support $vnc_png"
+echo "VNC thread $vnc_thread"
+if test -n "$sparc_cpu"; then
+ echo "Target Sparc Arch $sparc_cpu"
+fi
+echo "xen support $xen"
+echo "CPU emulation $cpu_emulation"
+echo "brlapi support $brlapi"
+echo "bluez support $bluez"
+echo "Documentation $docs"
+[ ! -z "$uname_release" ] && \
+echo "uname -r $uname_release"
+echo "NPTL support $nptl"
+echo "GUEST_BASE $guest_base"
+echo "PIE user targets $user_pie"
+echo "vde support $vde"
+echo "IO thread $io_thread"
+echo "Linux AIO support $linux_aio"
+echo "ATTR/XATTR support $attr"
+echo "Install blobs $blobs"
+echo "KVM support $kvm"
+echo "KVM PIT support $kvm_cap_pit"
+echo "KVM device assig. $kvm_cap_device_assignment"
+echo "fdt support $fdt"
+echo "preadv support $preadv"
+echo "fdatasync $fdatasync"
+echo "madvise $madvise"
+echo "posix_madvise $posix_madvise"
+echo "uuid support $uuid"
+echo "vhost-net support $vhost_net"
+echo "Trace backend $trace_backend"
+echo "Trace output file $trace_file-<pid>"
+echo "spice support $spice"
+echo "rbd support $rbd"
+echo "xfsctl support $xfs"
+
+if test $sdl_too_old = "yes"; then
+echo "-> Your SDL version is too old - please upgrade to have SDL support"
+fi
+
+config_host_mak="config-host.mak"
+config_host_ld="config-host.ld"
+
+echo "# Automatically generated by configure - do not modify" > $config_host_mak
+printf "# Configured with:" >> $config_host_mak
+printf " '%s'" "$0" "$@" >> $config_host_mak
+echo >> $config_host_mak
+
+echo all: >> $config_host_mak
+echo "prefix=$prefix" >> $config_host_mak
+echo "bindir=$bindir" >> $config_host_mak
+echo "mandir=$mandir" >> $config_host_mak
+echo "datadir=$datadir" >> $config_host_mak
+echo "sysconfdir=$sysconfdir" >> $config_host_mak
+echo "docdir=$docdir" >> $config_host_mak
+echo "confdir=$confdir" >> $config_host_mak
+
+case "$cpu" in
+ i386|x86_64|alpha|cris|hppa|ia64|m68k|microblaze|mips|mips64|ppc|ppc64|s390|s390x|sparc|sparc64)
+ ARCH=$cpu
+ ;;
+ armv4b|armv4l)
+ ARCH=arm
+ ;;
+esac
+echo "ARCH=$ARCH" >> $config_host_mak
+if test "$debug_tcg" = "yes" ; then
+ echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak
+fi
+if test "$debug_mon" = "yes" ; then
+ echo "CONFIG_DEBUG_MONITOR=y" >> $config_host_mak
+fi
+if test "$debug" = "yes" ; then
+ echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak
+fi
+if test "$strip_opt" = "yes" ; then
+ echo "STRIP=${strip}" >> $config_host_mak
+fi
+if test "$bigendian" = "yes" ; then
+ echo "HOST_WORDS_BIGENDIAN=y" >> $config_host_mak
+fi
+echo "HOST_LONG_BITS=$hostlongbits" >> $config_host_mak
+if test "$mingw32" = "yes" ; then
+ echo "CONFIG_WIN32=y" >> $config_host_mak
+ rc_version=`cat $source_path/VERSION`
+ version_major=${rc_version%%.*}
+ rc_version=${rc_version#*.}
+ version_minor=${rc_version%%.*}
+ rc_version=${rc_version#*.}
+ version_subminor=${rc_version%%.*}
+ version_micro=0
+ echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak
+ echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak
+else
+ echo "CONFIG_POSIX=y" >> $config_host_mak
+fi
+
+if test "$linux" = "yes" ; then
+ echo "CONFIG_LINUX=y" >> $config_host_mak
+fi
+
+if test "$darwin" = "yes" ; then
+ echo "CONFIG_DARWIN=y" >> $config_host_mak
+fi
+
+if test "$aix" = "yes" ; then
+ echo "CONFIG_AIX=y" >> $config_host_mak
+fi
+
+if test "$solaris" = "yes" ; then
+ echo "CONFIG_SOLARIS=y" >> $config_host_mak
+ echo "CONFIG_SOLARIS_VERSION=$solarisrev" >> $config_host_mak
+ if test "$needs_libsunmath" = "yes" ; then
+ echo "CONFIG_NEEDS_LIBSUNMATH=y" >> $config_host_mak
+ fi
+fi
+if test "$haiku" = "yes" ; then
+ echo "CONFIG_HAIKU=y" >> $config_host_mak
+fi
+if test "$static" = "yes" ; then
+ echo "CONFIG_STATIC=y" >> $config_host_mak
+fi
+if test $profiler = "yes" ; then
+ echo "CONFIG_PROFILER=y" >> $config_host_mak
+fi
+if test "$slirp" = "yes" ; then
+ echo "CONFIG_SLIRP=y" >> $config_host_mak
+ QEMU_INCLUDES="-I\$(SRC_PATH)/slirp $QEMU_INCLUDES"
+fi
+if test "$vde" = "yes" ; then
+ echo "CONFIG_VDE=y" >> $config_host_mak
+fi
+for card in $audio_card_list; do
+ def=CONFIG_`echo $card | tr '[:lower:]' '[:upper:]'`
+ echo "$def=y" >> $config_host_mak
+done
+echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak
+for drv in $audio_drv_list; do
+ def=CONFIG_`echo $drv | tr '[:lower:]' '[:upper:]'`
+ echo "$def=y" >> $config_host_mak
+ if test "$drv" = "fmod"; then
+ echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak
+ fi
+done
+if test "$audio_pt_int" = "yes" ; then
+ echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
+fi
+if test "$audio_win_int" = "yes" ; then
+ echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
+fi
+echo "CONFIG_BDRV_WHITELIST=$block_drv_whitelist" >> $config_host_mak
+if test "$mixemu" = "yes" ; then
+ echo "CONFIG_MIXEMU=y" >> $config_host_mak
+fi
+if test "$vnc_tls" = "yes" ; then
+ echo "CONFIG_VNC_TLS=y" >> $config_host_mak
+ echo "VNC_TLS_CFLAGS=$vnc_tls_cflags" >> $config_host_mak
+fi
+if test "$vnc_sasl" = "yes" ; then
+ echo "CONFIG_VNC_SASL=y" >> $config_host_mak
+ echo "VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_host_mak
+fi
+if test "$vnc_jpeg" != "no" ; then
+ echo "CONFIG_VNC_JPEG=y" >> $config_host_mak
+ echo "VNC_JPEG_CFLAGS=$vnc_jpeg_cflags" >> $config_host_mak
+fi
+if test "$vnc_png" != "no" ; then
+ echo "CONFIG_VNC_PNG=y" >> $config_host_mak
+ echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak
+fi
+if test "$vnc_thread" != "no" ; then
+ echo "CONFIG_VNC_THREAD=y" >> $config_host_mak
+ echo "CONFIG_THREAD=y" >> $config_host_mak
+fi
+if test "$fnmatch" = "yes" ; then
+ echo "CONFIG_FNMATCH=y" >> $config_host_mak
+fi
+if test "$uuid" = "yes" ; then
+ echo "CONFIG_UUID=y" >> $config_host_mak
+fi
+if test "$xfs" = "yes" ; then
+ echo "CONFIG_XFS=y" >> $config_host_mak
+fi
+qemu_version=`head $source_path/VERSION`
+echo "VERSION=$qemu_version" >>$config_host_mak
+echo "PKGVERSION=$pkgversion" >>$config_host_mak
+echo "SRC_PATH=$source_path" >> $config_host_mak
+echo "TARGET_DIRS=$target_list" >> $config_host_mak
+if [ "$docs" = "yes" ] ; then
+ echo "BUILD_DOCS=yes" >> $config_host_mak
+fi
+if test "$sdl" = "yes" ; then
+ echo "CONFIG_SDL=y" >> $config_host_mak
+ echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak
+fi
+if test "$cocoa" = "yes" ; then
+ echo "CONFIG_COCOA=y" >> $config_host_mak
+fi
+if test "$curses" = "yes" ; then
+ echo "CONFIG_CURSES=y" >> $config_host_mak
+fi
+if test "$atfile" = "yes" ; then
+ echo "CONFIG_ATFILE=y" >> $config_host_mak
+fi
+if test "$utimens" = "yes" ; then
+ echo "CONFIG_UTIMENSAT=y" >> $config_host_mak
+fi
+if test "$pipe2" = "yes" ; then
+ echo "CONFIG_PIPE2=y" >> $config_host_mak
+fi
+if test "$accept4" = "yes" ; then
+ echo "CONFIG_ACCEPT4=y" >> $config_host_mak
+fi
+if test "$splice" = "yes" ; then
+ echo "CONFIG_SPLICE=y" >> $config_host_mak
+fi
+if test "$eventfd" = "yes" ; then
+ echo "CONFIG_EVENTFD=y" >> $config_host_mak
+fi
+if test "$fallocate" = "yes" ; then
+ echo "CONFIG_FALLOCATE=y" >> $config_host_mak
+fi
+if test "$sync_file_range" = "yes" ; then
+ echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak
+fi
+if test "$fiemap" = "yes" ; then
+ echo "CONFIG_FIEMAP=y" >> $config_host_mak
+fi
+if test "$dup3" = "yes" ; then
+ echo "CONFIG_DUP3=y" >> $config_host_mak
+fi
+if test "$inotify" = "yes" ; then
+ echo "CONFIG_INOTIFY=y" >> $config_host_mak
+fi
+if test "$inotify1" = "yes" ; then
+ echo "CONFIG_INOTIFY1=y" >> $config_host_mak
+fi
+if test "$byteswap_h" = "yes" ; then
+ echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak
+fi
+if test "$bswap_h" = "yes" ; then
+ echo "CONFIG_MACHINE_BSWAP_H=y" >> $config_host_mak
+fi
+if test "$curl" = "yes" ; then
+ echo "CONFIG_CURL=y" >> $config_host_mak
+ echo "CURL_CFLAGS=$curl_cflags" >> $config_host_mak
+fi
+if test "$brlapi" = "yes" ; then
+ echo "CONFIG_BRLAPI=y" >> $config_host_mak
+fi
+if test "$bluez" = "yes" ; then
+ echo "CONFIG_BLUEZ=y" >> $config_host_mak
+ echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
+fi
+if test "$xen" = "yes" ; then
+ echo "CONFIG_XEN=y" >> $config_host_mak
+fi
+if test "$io_thread" = "yes" ; then
+ echo "CONFIG_IOTHREAD=y" >> $config_host_mak
+ echo "CONFIG_THREAD=y" >> $config_host_mak
+fi
+if test "$linux_aio" = "yes" ; then
+ echo "CONFIG_LINUX_AIO=y" >> $config_host_mak
+fi
+if test "$attr" = "yes" ; then
+ echo "CONFIG_ATTR=y" >> $config_host_mak
+fi
+if test "$linux" = "yes" ; then
+ if test "$attr" = "yes" ; then
+ echo "CONFIG_VIRTFS=y" >> $config_host_mak
+ fi
+fi
+if test "$blobs" = "yes" ; then
+ echo "INSTALL_BLOBS=yes" >> $config_host_mak
+fi
+if test "$iovec" = "yes" ; then
+ echo "CONFIG_IOVEC=y" >> $config_host_mak
+fi
+if test "$preadv" = "yes" ; then
+ echo "CONFIG_PREADV=y" >> $config_host_mak
+fi
+if test "$fdt" = "yes" ; then
+ echo "CONFIG_FDT=y" >> $config_host_mak
+fi
+if test "$signalfd" = "yes" ; then
+ echo "CONFIG_SIGNALFD=y" >> $config_host_mak
+fi
+if test "$need_offsetof" = "yes" ; then
+ echo "CONFIG_NEED_OFFSETOF=y" >> $config_host_mak
+fi
+if test "$gcc_attribute_warn_unused_result" = "yes" ; then
+ echo "CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT=y" >> $config_host_mak
+fi
+if test "$fdatasync" = "yes" ; then
+ echo "CONFIG_FDATASYNC=y" >> $config_host_mak
+fi
+if test $cpu_emulation = "yes"; then
+ echo "CONFIG_CPU_EMULATION=y" >> $config_host_mak
+else
+ echo "CONFIG_NO_CPU_EMULATION=y" >> $config_host_mak
+fi
+if test "$madvise" = "yes" ; then
+ echo "CONFIG_MADVISE=y" >> $config_host_mak
+fi
+if test "$posix_madvise" = "yes" ; then
+ echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
+fi
+
+if test "$spice" = "yes" ; then
+ echo "CONFIG_SPICE=y" >> $config_host_mak
+fi
+
+# XXX: suppress that
+if [ "$bsd" = "yes" ] ; then
+ echo "CONFIG_BSD=y" >> $config_host_mak
+fi
+
+echo "CONFIG_UNAME_RELEASE=\"$uname_release\"" >> $config_host_mak
+
+if test "$zero_malloc" = "yes" ; then
+ echo "CONFIG_ZERO_MALLOC=y" >> $config_host_mak
+fi
+if test "$rbd" = "yes" ; then
+ echo "CONFIG_RBD=y" >> $config_host_mak
+fi
+
+# USB host support
+case "$usb" in
+linux)
+ echo "HOST_USB=linux" >> $config_host_mak
+;;
+bsd)
+ echo "HOST_USB=bsd" >> $config_host_mak
+;;
+*)
+ echo "HOST_USB=stub" >> $config_host_mak
+;;
+esac
+
+echo "TRACE_BACKEND=$trace_backend" >> $config_host_mak
+if test "$trace_backend" = "simple"; then
+ echo "CONFIG_SIMPLE_TRACE=y" >> $config_host_mak
+fi
+# Set the appropriate trace file.
+if test "$trace_backend" = "simple"; then
+ trace_file="\"$trace_file-%u\""
+fi
+if test "$trace_backend" = "dtrace" -a "$trace_backend_stap" = "yes" ; then
+ echo "CONFIG_SYSTEMTAP_TRACE=y" >> $config_host_mak
+fi
+echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak
+
+echo "TOOLS=$tools" >> $config_host_mak
+echo "ROMS=$roms" >> $config_host_mak
+echo "MAKE=$make" >> $config_host_mak
+echo "INSTALL=$install" >> $config_host_mak
+echo "INSTALL_DIR=$install -d -m0755 -p" >> $config_host_mak
+echo "INSTALL_DATA=$install -m0644 -p" >> $config_host_mak
+echo "INSTALL_PROG=$install -m0755 -p" >> $config_host_mak
+echo "CC=$cc" >> $config_host_mak
+echo "CC_I386=$cc_i386" >> $config_host_mak
+echo "HOST_CC=$host_cc" >> $config_host_mak
+echo "AR=$ar" >> $config_host_mak
+echo "OBJCOPY=$objcopy" >> $config_host_mak
+echo "LD=$ld" >> $config_host_mak
+echo "WINDRES=$windres" >> $config_host_mak
+echo "CFLAGS=$CFLAGS" >> $config_host_mak
+echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak
+echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak
+if test "$sparse" = "yes" ; then
+ echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak
+ echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak
+ echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak
+fi
+echo "HELPER_CFLAGS=$helper_cflags" >> $config_host_mak
+echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
+echo "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak
+echo "ARLIBS_END=$arlibs_end" >> $config_host_mak
+echo "LIBS+=$LIBS" >> $config_host_mak
+echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
+echo "EXESUF=$EXESUF" >> $config_host_mak
+
+# generate list of library paths for linker script
+
+$ld --verbose -v 2> /dev/null | grep SEARCH_DIR > ${config_host_ld}
+
+if test -f ${config_host_ld}~ ; then
+ if cmp -s $config_host_ld ${config_host_ld}~ ; then
+ mv ${config_host_ld}~ $config_host_ld
+ else
+ rm ${config_host_ld}~
+ fi
+fi
+
+for d in libdis libdis-user; do
+ mkdir -p $d
+ symlink $source_path/Makefile.dis $d/Makefile
+ echo > $d/config.mak
+done
+if test "$static" = "no" -a "$user_pie" = "yes" ; then
+ echo "QEMU_CFLAGS+=-fpie" > libdis-user/config.mak
+fi
+
+for target in $target_list; do
+target_dir="$target"
+config_target_mak=$target_dir/config-target.mak
+target_arch2=`echo $target | cut -d '-' -f 1`
+target_bigendian="no"
+
+case "$target_arch2" in
+ armeb|m68k|microblaze|mips|mipsn32|mips64|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus)
+ target_bigendian=yes
+ ;;
+esac
+target_softmmu="no"
+target_user_only="no"
+target_linux_user="no"
+target_darwin_user="no"
+target_bsd_user="no"
+case "$target" in
+ ${target_arch2}-softmmu)
+ target_softmmu="yes"
+ ;;
+ ${target_arch2}-linux-user)
+ if test "$linux" != "yes" ; then
+ echo "ERROR: Target '$target' is only available on a Linux host"
+ exit 1
+ fi
+ target_user_only="yes"
+ target_linux_user="yes"
+ ;;
+ ${target_arch2}-darwin-user)
+ if test "$darwin" != "yes" ; then
+ echo "ERROR: Target '$target' is only available on a Darwin host"
+ exit 1
+ fi
+ target_user_only="yes"
+ target_darwin_user="yes"
+ ;;
+ ${target_arch2}-bsd-user)
+ if test "$bsd" != "yes" ; then
+ echo "ERROR: Target '$target' is only available on a BSD host"
+ exit 1
+ fi
+ target_user_only="yes"
+ target_bsd_user="yes"
+ ;;
+ *)
+ echo "ERROR: Target '$target' not recognised"
+ exit 1
+ ;;
+esac
+
+mkdir -p $target_dir
+mkdir -p $target_dir/fpu
+mkdir -p $target_dir/tcg
+mkdir -p $target_dir/ide
+if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" -o "$target" = "arm-bsd-user" -o "$target" = "armeb-bsd-user" ; then
+ mkdir -p $target_dir/nwfpe
+fi
+symlink $source_path/Makefile.target $target_dir/Makefile
+
+
+echo "# Automatically generated by configure - do not modify" > $config_target_mak
+
+bflt="no"
+target_nptl="no"
+interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_arch2/g"`
+echo "CONFIG_QEMU_INTERP_PREFIX=\"$interp_prefix1\"" >> $config_target_mak
+gdb_xml_files=""
+
+TARGET_ARCH="$target_arch2"
+TARGET_BASE_ARCH=""
+TARGET_ABI_DIR=""
+
+case "$target_arch2" in
+ i386)
+ target_phys_bits=32
+ ;;
+ x86_64)
+ TARGET_BASE_ARCH=i386
+ target_phys_bits=64
+ ;;
+ ia64)
+ target_phys_bits=64
+ ;;
+ alpha)
+ target_phys_bits=64
+ target_nptl="yes"
+ ;;
+ arm|armeb)
+ TARGET_ARCH=arm
+ bflt="yes"
+ target_nptl="yes"
+ gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
+ target_phys_bits=32
+ ;;
+ cris)
+ target_nptl="yes"
+ target_phys_bits=32
+ ;;
+ m68k)
+ bflt="yes"
+ gdb_xml_files="cf-core.xml cf-fp.xml"
+ target_phys_bits=32
+ ;;
+ microblaze)
+ bflt="yes"
+ target_nptl="yes"
+ target_phys_bits=32
+ ;;
+ mips|mipsel)
+ TARGET_ARCH=mips
+ echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak
+ target_nptl="yes"
+ target_phys_bits=64
+ ;;
+ mipsn32|mipsn32el)
+ TARGET_ARCH=mipsn32
+ TARGET_BASE_ARCH=mips
+ echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak
+ target_phys_bits=64
+ ;;
+ mips64|mips64el)
+ TARGET_ARCH=mips64
+ TARGET_BASE_ARCH=mips
+ echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak
+ target_phys_bits=64
+ ;;
+ ppc)
+ gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
+ target_phys_bits=32
+ target_nptl="yes"
+ ;;
+ ppcemb)
+ TARGET_BASE_ARCH=ppc
+ TARGET_ABI_DIR=ppc
+ gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
+ target_phys_bits=64
+ target_nptl="yes"
+ ;;
+ ppc64)
+ TARGET_BASE_ARCH=ppc
+ TARGET_ABI_DIR=ppc
+ gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
+ target_phys_bits=64
+ ;;
+ ppc64abi32)
+ TARGET_ARCH=ppc64
+ TARGET_BASE_ARCH=ppc
+ TARGET_ABI_DIR=ppc
+ echo "TARGET_ABI32=y" >> $config_target_mak
+ gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
+ target_phys_bits=64
+ ;;
+ sh4|sh4eb)
+ TARGET_ARCH=sh4
+ bflt="yes"
+ target_nptl="yes"
+ target_phys_bits=32
+ ;;
+ sparc)
+ target_phys_bits=64
+ ;;
+ sparc64)
+ TARGET_BASE_ARCH=sparc
+ target_phys_bits=64
+ ;;
+ sparc32plus)
+ TARGET_ARCH=sparc64
+ TARGET_BASE_ARCH=sparc
+ TARGET_ABI_DIR=sparc
+ echo "TARGET_ABI32=y" >> $config_target_mak
+ target_phys_bits=64
+ ;;
+ s390x)
+ target_phys_bits=64
+ ;;
+ *)
+ echo "Unsupported target CPU"
+ exit 1
+ ;;
+esac
+echo "TARGET_ARCH=$TARGET_ARCH" >> $config_target_mak
+target_arch_name="`echo $TARGET_ARCH | tr '[:lower:]' '[:upper:]'`"
+echo "TARGET_$target_arch_name=y" >> $config_target_mak
+echo "TARGET_ARCH2=$target_arch2" >> $config_target_mak
+# TARGET_BASE_ARCH needs to be defined after TARGET_ARCH
+if [ "$TARGET_BASE_ARCH" = "" ]; then
+ TARGET_BASE_ARCH=$TARGET_ARCH
+fi
+echo "TARGET_BASE_ARCH=$TARGET_BASE_ARCH" >> $config_target_mak
+if [ "$TARGET_ABI_DIR" = "" ]; then
+ TARGET_ABI_DIR=$TARGET_ARCH
+fi
+echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak
+case "$target_arch2" in
+ i386|x86_64)
+ if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then
+ echo "CONFIG_XEN=y" >> $config_target_mak
+ fi
+esac
+case "$target_arch2" in
+ i386|x86_64|ppcemb|ppc|ppc64|s390x)
+ # Make sure the target and host cpus are compatible
+ if test "$kvm" = "yes" -a "$target_softmmu" = "yes" -a \
+ \( "$target_arch2" = "$cpu" -o \
+ \( "$target_arch2" = "ppcemb" -a "$cpu" = "ppc" \) -o \
+ \( "$target_arch2" = "ppc64" -a "$cpu" = "ppc" \) -o \
+ \( "$target_arch2" = "x86_64" -a "$cpu" = "i386" \) -o \
+ \( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then
+ echo "CONFIG_KVM=y" >> $config_target_mak
+ echo "KVM_CFLAGS=$kvm_cflags" >> $config_target_mak
+ if test "$kvm_para" = "yes"; then
+ echo "CONFIG_KVM_PARA=y" >> $config_target_mak
+ fi
+ if test $kvm_cap_pit = "yes" ; then
+ echo "CONFIG_KVM_PIT=y" >> $config_target_mak
+ fi
+ if test $kvm_cap_device_assignment = "yes" ; then
+ echo "CONFIG_KVM_DEVICE_ASSIGNMENT=y" >> $config_target_mak
+ fi
+ if test $vhost_net = "yes" ; then
+ echo "CONFIG_VHOST_NET=y" >> $config_target_mak
+ fi
+ fi
+esac
+if test "$target_bigendian" = "yes" ; then
+ echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
+fi
+if test "$target_softmmu" = "yes" ; then
+ echo "TARGET_PHYS_ADDR_BITS=$target_phys_bits" >> $config_target_mak
+ echo "CONFIG_SOFTMMU=y" >> $config_target_mak
+ echo "LIBS+=$libs_softmmu" >> $config_target_mak
+ echo "HWDIR=../libhw$target_phys_bits" >> $config_target_mak
+ echo "subdir-$target: subdir-libhw$target_phys_bits" >> $config_host_mak
+fi
+if test "$target_user_only" = "yes" ; then
+ echo "CONFIG_USER_ONLY=y" >> $config_target_mak
+fi
+if test "$target_linux_user" = "yes" ; then
+ echo "CONFIG_LINUX_USER=y" >> $config_target_mak
+fi
+if test "$target_darwin_user" = "yes" ; then
+ echo "CONFIG_DARWIN_USER=y" >> $config_target_mak
+fi
+list=""
+if test ! -z "$gdb_xml_files" ; then
+ for x in $gdb_xml_files; do
+ list="$list $source_path/gdb-xml/$x"
+ done
+ echo "TARGET_XML_FILES=$list" >> $config_target_mak
+fi
+
+case "$target_arch2" in
+ i386|x86_64)
+ echo "CONFIG_NOSOFTFLOAT=y" >> $config_target_mak
+ ;;
+ *)
+ echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak
+ ;;
+esac
+
+if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then
+ echo "TARGET_HAS_BFLT=y" >> $config_target_mak
+fi
+if test "$target_user_only" = "yes" \
+ -a "$nptl" = "yes" -a "$target_nptl" = "yes"; then
+ echo "CONFIG_USE_NPTL=y" >> $config_target_mak
+fi
+if test "$target_user_only" = "yes" -a "$guest_base" = "yes"; then
+ echo "CONFIG_USE_GUEST_BASE=y" >> $config_target_mak
+fi
+if test "$target_bsd_user" = "yes" ; then
+ echo "CONFIG_BSD_USER=y" >> $config_target_mak
+fi
+
+# generate QEMU_CFLAGS/LDFLAGS for targets
+
+cflags=""
+includes=""
+ldflags=""
+
+if test "$ARCH" = "sparc64" ; then
+ includes="-I\$(SRC_PATH)/tcg/sparc $includes"
+elif test "$ARCH" = "s390x" ; then
+ includes="-I\$(SRC_PATH)/tcg/s390 $includes"
+elif test "$ARCH" = "x86_64" ; then
+ includes="-I\$(SRC_PATH)/tcg/i386 $includes"
+else
+ includes="-I\$(SRC_PATH)/tcg/\$(ARCH) $includes"
+fi
+includes="-I\$(SRC_PATH)/tcg $includes"
+includes="-I\$(SRC_PATH)/fpu $includes"
+
+if test "$target_user_only" = "yes" ; then
+ libdis_config_mak=libdis-user/config.mak
+else
+ libdis_config_mak=libdis/config.mak
+fi
+
+for i in $ARCH $TARGET_BASE_ARCH ; do
+ case "$i" in
+ alpha)
+ echo "CONFIG_ALPHA_DIS=y" >> $config_target_mak
+ echo "CONFIG_ALPHA_DIS=y" >> $libdis_config_mak
+ ;;
+ arm)
+ echo "CONFIG_ARM_DIS=y" >> $config_target_mak
+ echo "CONFIG_ARM_DIS=y" >> $libdis_config_mak
+ ;;
+ cris)
+ echo "CONFIG_CRIS_DIS=y" >> $config_target_mak
+ echo "CONFIG_CRIS_DIS=y" >> $libdis_config_mak
+ ;;
+ hppa)
+ echo "CONFIG_HPPA_DIS=y" >> $config_target_mak
+ echo "CONFIG_HPPA_DIS=y" >> $libdis_config_mak
+ ;;
+ i386|x86_64)
+ echo "CONFIG_I386_DIS=y" >> $config_target_mak
+ echo "CONFIG_I386_DIS=y" >> $libdis_config_mak
+ ;;
+ ia64*)
+ echo "CONFIG_IA64_DIS=y" >> $config_target_mak
+ echo "CONFIG_IA64_DIS=y" >> $libdis_config_mak
+ ;;
+ m68k)
+ echo "CONFIG_M68K_DIS=y" >> $config_target_mak
+ echo "CONFIG_M68K_DIS=y" >> $libdis_config_mak
+ ;;
+ microblaze)
+ echo "CONFIG_MICROBLAZE_DIS=y" >> $config_target_mak
+ echo "CONFIG_MICROBLAZE_DIS=y" >> $libdis_config_mak
+ ;;
+ mips*)
+ echo "CONFIG_MIPS_DIS=y" >> $config_target_mak
+ echo "CONFIG_MIPS_DIS=y" >> $libdis_config_mak
+ ;;
+ ppc*)
+ echo "CONFIG_PPC_DIS=y" >> $config_target_mak
+ echo "CONFIG_PPC_DIS=y" >> $libdis_config_mak
+ ;;
+ s390*)
+ echo "CONFIG_S390_DIS=y" >> $config_target_mak
+ echo "CONFIG_S390_DIS=y" >> $libdis_config_mak
+ ;;
+ sh4)
+ echo "CONFIG_SH4_DIS=y" >> $config_target_mak
+ echo "CONFIG_SH4_DIS=y" >> $libdis_config_mak
+ ;;
+ sparc*)
+ echo "CONFIG_SPARC_DIS=y" >> $config_target_mak
+ echo "CONFIG_SPARC_DIS=y" >> $libdis_config_mak
+ ;;
+ esac
+done
+
+case "$ARCH" in
+alpha)
+ # Ensure there's only a single GP
+ cflags="-msmall-data $cflags"
+;;
+esac
+
+if test "$target_softmmu" = "yes" ; then
+ case "$TARGET_BASE_ARCH" in
+ arm)
+ cflags="-DHAS_AUDIO $cflags"
+ ;;
+ i386|mips|ppc)
+ cflags="-DHAS_AUDIO -DHAS_AUDIO_CHOICE $cflags"
+ ;;
+ esac
+fi
+
+if test "$target_user_only" = "yes" -a "$static" = "no" -a \
+ "$user_pie" = "yes" ; then
+ cflags="-fpie $cflags"
+ ldflags="-pie $ldflags"
+fi
+
+if test "$target_softmmu" = "yes" -a \( \
+ "$TARGET_ARCH" = "microblaze" -o \
+ "$TARGET_ARCH" = "cris" \) ; then
+ echo "CONFIG_NEED_MMU=y" >> $config_target_mak
+fi
+
+if test "$gprof" = "yes" ; then
+ echo "TARGET_GPROF=yes" >> $config_target_mak
+ if test "$target_linux_user" = "yes" ; then
+ cflags="-p $cflags"
+ ldflags="-p $ldflags"
+ fi
+ if test "$target_softmmu" = "yes" ; then
+ ldflags="-p $ldflags"
+ echo "GPROF_CFLAGS=-p" >> $config_target_mak
+ fi
+fi
+
+linker_script="-Wl,-T../config-host.ld -Wl,-T,\$(SRC_PATH)/\$(ARCH).ld"
+if test "$target_linux_user" = "yes" -o "$target_bsd_user" = "yes" ; then
+ case "$ARCH" in
+ sparc)
+ # -static is used to avoid g1/g3 usage by the dynamic linker
+ ldflags="$linker_script -static $ldflags"
+ ;;
+ alpha | s390x)
+ # The default placement of the application is fine.
+ ;;
+ *)
+ ldflags="$linker_script $ldflags"
+ ;;
+ esac
+fi
+
+echo "LDFLAGS+=$ldflags" >> $config_target_mak
+echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak
+echo "QEMU_INCLUDES+=$includes" >> $config_target_mak
+
+done # for target in $targets
+
+# build tree in object directory in case the source is not in the current directory
+DIRS="tests tests/cris slirp audio block net pc-bios/optionrom"
+DIRS="$DIRS roms/seabios roms/vgabios"
+DIRS="$DIRS fsdev ui"
+FILES="Makefile tests/Makefile"
+FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit"
+FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
+FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
+for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do
+ FILES="$FILES pc-bios/`basename $bios_file`"
+done
+mkdir -p $DIRS
+for f in $FILES ; do
+ test -e $f || symlink $source_path/$f $f
+done
+
+# temporary config to build submodules
+for rom in seabios vgabios; do
+ config_mak=roms/$rom/config.mak
+ echo "# Automatically generated by configure - do not modify" > $config_mak
+ echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak
+ echo "CC=$cc" >> $config_mak
+ echo "BCC=bcc" >> $config_mak
+ echo "CPP=${cross_prefix}cpp" >> $config_mak
+ echo "OBJCOPY=objcopy" >> $config_mak
+ echo "IASL=iasl" >> $config_mak
+ echo "LD=$ld" >> $config_mak
+done
+
+for hwlib in 32 64; do
+ d=libhw$hwlib
+ mkdir -p $d
+ mkdir -p $d/ide
+ symlink $source_path/Makefile.hw $d/Makefile
+ echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak
+done
+
+d=libuser
+mkdir -p $d
+symlink $source_path/Makefile.user $d/Makefile
+if test "$static" = "no" -a "$user_pie" = "yes" ; then
+ echo "QEMU_CFLAGS+=-fpie" > $d/config.mak
+fi
+
+if test "$docs" = "yes" ; then
+ mkdir -p QMP
+fi
diff --git a/console.c b/console.c
new file mode 100644
index 0000000..57d6eb5
--- /dev/null
+++ b/console.c
@@ -0,0 +1,1707 @@
+/*
+ * QEMU graphical console
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "console.h"
+#include "qemu-timer.h"
+
+//#define DEBUG_CONSOLE
+#define DEFAULT_BACKSCROLL 512
+#define MAX_CONSOLES 12
+
+#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
+
+typedef struct TextAttributes {
+ uint8_t fgcol:4;
+ uint8_t bgcol:4;
+ uint8_t bold:1;
+ uint8_t uline:1;
+ uint8_t blink:1;
+ uint8_t invers:1;
+ uint8_t unvisible:1;
+} TextAttributes;
+
+typedef struct TextCell {
+ uint8_t ch;
+ TextAttributes t_attrib;
+} TextCell;
+
+#define MAX_ESC_PARAMS 3
+
+enum TTYState {
+ TTY_STATE_NORM,
+ TTY_STATE_ESC,
+ TTY_STATE_CSI,
+};
+
+typedef struct QEMUFIFO {
+ uint8_t *buf;
+ int buf_size;
+ int count, wptr, rptr;
+} QEMUFIFO;
+
+static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
+{
+ int l, len;
+
+ l = f->buf_size - f->count;
+ if (len1 > l)
+ len1 = l;
+ len = len1;
+ while (len > 0) {
+ l = f->buf_size - f->wptr;
+ if (l > len)
+ l = len;
+ memcpy(f->buf + f->wptr, buf, l);
+ f->wptr += l;
+ if (f->wptr >= f->buf_size)
+ f->wptr = 0;
+ buf += l;
+ len -= l;
+ }
+ f->count += len1;
+ return len1;
+}
+
+static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
+{
+ int l, len;
+
+ if (len1 > f->count)
+ len1 = f->count;
+ len = len1;
+ while (len > 0) {
+ l = f->buf_size - f->rptr;
+ if (l > len)
+ l = len;
+ memcpy(buf, f->buf + f->rptr, l);
+ f->rptr += l;
+ if (f->rptr >= f->buf_size)
+ f->rptr = 0;
+ buf += l;
+ len -= l;
+ }
+ f->count -= len1;
+ return len1;
+}
+
+typedef enum {
+ GRAPHIC_CONSOLE,
+ TEXT_CONSOLE,
+ TEXT_CONSOLE_FIXED_SIZE
+} console_type_t;
+
+/* ??? This is mis-named.
+ It is used for both text and graphical consoles. */
+struct TextConsole {
+ console_type_t console_type;
+ DisplayState *ds;
+ /* Graphic console state. */
+ vga_hw_update_ptr hw_update;
+ vga_hw_invalidate_ptr hw_invalidate;
+ vga_hw_screen_dump_ptr hw_screen_dump;
+ vga_hw_text_update_ptr hw_text_update;
+ void *hw;
+
+ int g_width, g_height;
+ int width;
+ int height;
+ int total_height;
+ int backscroll_height;
+ int x, y;
+ int x_saved, y_saved;
+ int y_displayed;
+ int y_base;
+ TextAttributes t_attrib_default; /* default text attributes */
+ TextAttributes t_attrib; /* currently active text attributes */
+ TextCell *cells;
+ int text_x[2], text_y[2], cursor_invalidate;
+ int echo;
+
+ int update_x0;
+ int update_y0;
+ int update_x1;
+ int update_y1;
+
+ enum TTYState state;
+ int esc_params[MAX_ESC_PARAMS];
+ int nb_esc_params;
+
+ CharDriverState *chr;
+ /* fifo for key pressed */
+ QEMUFIFO out_fifo;
+ uint8_t out_fifo_buf[16];
+ QEMUTimer *kbd_timer;
+};
+
+static DisplayState *display_state;
+static TextConsole *active_console;
+static TextConsole *consoles[MAX_CONSOLES];
+static int nb_consoles = 0;
+
+void vga_hw_update(void)
+{
+ if (active_console && active_console->hw_update)
+ active_console->hw_update(active_console->hw);
+}
+
+void vga_hw_invalidate(void)
+{
+ if (active_console && active_console->hw_invalidate)
+ active_console->hw_invalidate(active_console->hw);
+}
+
+void vga_hw_screen_dump(const char *filename)
+{
+ TextConsole *previous_active_console;
+
+ previous_active_console = active_console;
+ 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)
+ consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
+ active_console = previous_active_console;
+}
+
+void vga_hw_text_update(console_ch_t *chardata)
+{
+ if (active_console && active_console->hw_text_update)
+ active_console->hw_text_update(active_console->hw, chardata);
+}
+
+/* convert a RGBA color to a color index usable in graphic primitives */
+static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
+{
+ unsigned int r, g, b, color;
+
+ switch(ds_get_bits_per_pixel(ds)) {
+#if 0
+ case 8:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = (rgb_to_index[r] * 6 * 6) +
+ (rgb_to_index[g] * 6) +
+ (rgb_to_index[b]);
+ break;
+#endif
+ case 15:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+ break;
+ case 16:
+ r = (rgba >> 16) & 0xff;
+ g = (rgba >> 8) & 0xff;
+ b = (rgba) & 0xff;
+ color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+ break;
+ case 32:
+ default:
+ color = rgba;
+ break;
+ }
+ return color;
+}
+
+static void vga_fill_rect (DisplayState *ds,
+ int posx, int posy, int width, int height, uint32_t color)
+{
+ uint8_t *d, *d1;
+ int x, y, bpp;
+
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ d1 = ds_get_data(ds) +
+ ds_get_linesize(ds) * posy + bpp * posx;
+ for (y = 0; y < height; y++) {
+ d = d1;
+ switch(bpp) {
+ case 1:
+ for (x = 0; x < width; x++) {
+ *((uint8_t *)d) = color;
+ d++;
+ }
+ break;
+ case 2:
+ for (x = 0; x < width; x++) {
+ *((uint16_t *)d) = color;
+ d += 2;
+ }
+ break;
+ case 4:
+ for (x = 0; x < width; x++) {
+ *((uint32_t *)d) = color;
+ d += 4;
+ }
+ break;
+ }
+ d1 += ds_get_linesize(ds);
+ }
+}
+
+/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
+static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
+{
+ const uint8_t *s;
+ uint8_t *d;
+ int wb, y, bpp;
+
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ wb = w * bpp;
+ if (yd <= ys) {
+ s = ds_get_data(ds) +
+ ds_get_linesize(ds) * ys + bpp * xs;
+ d = ds_get_data(ds) +
+ ds_get_linesize(ds) * yd + bpp * xd;
+ for (y = 0; y < h; y++) {
+ memmove(d, s, wb);
+ d += ds_get_linesize(ds);
+ s += ds_get_linesize(ds);
+ }
+ } else {
+ s = ds_get_data(ds) +
+ ds_get_linesize(ds) * (ys + h - 1) + bpp * xs;
+ d = ds_get_data(ds) +
+ ds_get_linesize(ds) * (yd + h - 1) + bpp * xd;
+ for (y = 0; y < h; y++) {
+ memmove(d, s, wb);
+ d -= ds_get_linesize(ds);
+ s -= ds_get_linesize(ds);
+ }
+ }
+}
+
+/***********************************************************/
+/* basic char display */
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+#include "vgafont.h"
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define PAT(x) x
+#else
+#define PAT(x) cbswap_32(x)
+#endif
+
+static const uint32_t dmask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+ PAT(0x00000000),
+ PAT(0x0000ffff),
+ PAT(0xffff0000),
+ PAT(0xffffffff),
+};
+
+static uint32_t color_table[2][8];
+
+enum color_names {
+ COLOR_BLACK = 0,
+ COLOR_RED = 1,
+ COLOR_GREEN = 2,
+ COLOR_YELLOW = 3,
+ COLOR_BLUE = 4,
+ COLOR_MAGENTA = 5,
+ COLOR_CYAN = 6,
+ COLOR_WHITE = 7
+};
+
+static const uint32_t color_table_rgb[2][8] = {
+ { /* dark */
+ QEMU_RGB(0x00, 0x00, 0x00), /* black */
+ QEMU_RGB(0xaa, 0x00, 0x00), /* red */
+ QEMU_RGB(0x00, 0xaa, 0x00), /* green */
+ QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
+ QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
+ QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
+ QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
+ QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
+ },
+ { /* bright */
+ QEMU_RGB(0x00, 0x00, 0x00), /* black */
+ QEMU_RGB(0xff, 0x00, 0x00), /* red */
+ QEMU_RGB(0x00, 0xff, 0x00), /* green */
+ QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
+ QEMU_RGB(0x00, 0x00, 0xff), /* blue */
+ QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
+ QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
+ QEMU_RGB(0xff, 0xff, 0xff), /* white */
+ }
+};
+
+static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
+{
+ switch(ds_get_bits_per_pixel(ds)) {
+ case 8:
+ col |= col << 8;
+ col |= col << 16;
+ break;
+ case 15:
+ case 16:
+ col |= col << 16;
+ break;
+ default:
+ break;
+ }
+
+ return col;
+}
+#ifdef DEBUG_CONSOLE
+static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
+{
+ if (t_attrib->bold) {
+ printf("b");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->uline) {
+ printf("u");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->blink) {
+ printf("l");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->invers) {
+ printf("i");
+ } else {
+ printf(" ");
+ }
+ if (t_attrib->unvisible) {
+ printf("n");
+ } else {
+ printf(" ");
+ }
+
+ printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
+}
+#endif
+
+static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
+ TextAttributes *t_attrib)
+{
+ uint8_t *d;
+ const uint8_t *font_ptr;
+ unsigned int font_data, linesize, xorcol, bpp;
+ int i;
+ unsigned int fgcol, bgcol;
+
+#ifdef DEBUG_CONSOLE
+ printf("x: %2i y: %2i", x, y);
+ console_print_text_attributes(t_attrib, ch);
+#endif
+
+ if (t_attrib->invers) {
+ bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
+ fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
+ } else {
+ fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
+ bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
+ }
+
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ d = ds_get_data(ds) +
+ ds_get_linesize(ds) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
+ linesize = ds_get_linesize(ds);
+ font_ptr = vgafont16 + FONT_HEIGHT * ch;
+ xorcol = bgcol ^ fgcol;
+ switch(ds_get_bits_per_pixel(ds)) {
+ case 8:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ if (t_attrib->uline
+ && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+ font_data = 0xFFFF;
+ }
+ ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ case 16:
+ case 15:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ if (t_attrib->uline
+ && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+ font_data = 0xFFFF;
+ }
+ ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ case 32:
+ for(i = 0; i < FONT_HEIGHT; i++) {
+ font_data = *font_ptr++;
+ if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+ font_data = 0xFFFF;
+ }
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ d += linesize;
+ }
+ break;
+ }
+}
+
+static void text_console_resize(TextConsole *s)
+{
+ TextCell *cells, *c, *c1;
+ int w1, x, y, last_width;
+
+ last_width = s->width;
+ s->width = s->g_width / FONT_WIDTH;
+ s->height = s->g_height / FONT_HEIGHT;
+
+ w1 = last_width;
+ if (s->width < w1)
+ w1 = s->width;
+
+ cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
+ for(y = 0; y < s->total_height; y++) {
+ c = &cells[y * s->width];
+ if (w1 > 0) {
+ c1 = &s->cells[y * last_width];
+ for(x = 0; x < w1; x++) {
+ *c++ = *c1++;
+ }
+ }
+ for(x = w1; x < s->width; x++) {
+ c->ch = ' ';
+ c->t_attrib = s->t_attrib_default;
+ c++;
+ }
+ }
+ qemu_free(s->cells);
+ s->cells = cells;
+}
+
+static inline void text_update_xy(TextConsole *s, int x, int y)
+{
+ s->text_x[0] = MIN(s->text_x[0], x);
+ s->text_x[1] = MAX(s->text_x[1], x);
+ s->text_y[0] = MIN(s->text_y[0], y);
+ s->text_y[1] = MAX(s->text_y[1], y);
+}
+
+static void invalidate_xy(TextConsole *s, int x, int y)
+{
+ if (s->update_x0 > x * FONT_WIDTH)
+ s->update_x0 = x * FONT_WIDTH;
+ if (s->update_y0 > y * FONT_HEIGHT)
+ s->update_y0 = y * FONT_HEIGHT;
+ if (s->update_x1 < (x + 1) * FONT_WIDTH)
+ s->update_x1 = (x + 1) * FONT_WIDTH;
+ if (s->update_y1 < (y + 1) * FONT_HEIGHT)
+ s->update_y1 = (y + 1) * FONT_HEIGHT;
+}
+
+static void update_xy(TextConsole *s, int x, int y)
+{
+ TextCell *c;
+ int y1, y2;
+
+ if (s == active_console) {
+ if (!ds_get_bits_per_pixel(s->ds)) {
+ text_update_xy(s, x, y);
+ return;
+ }
+
+ y1 = (s->y_base + y) % s->total_height;
+ y2 = y1 - s->y_displayed;
+ if (y2 < 0)
+ y2 += s->total_height;
+ if (y2 < s->height) {
+ c = &s->cells[y1 * s->width + x];
+ vga_putcharxy(s->ds, x, y2, c->ch,
+ &(c->t_attrib));
+ invalidate_xy(s, x, y2);
+ }
+ }
+}
+
+static void console_show_cursor(TextConsole *s, int show)
+{
+ TextCell *c;
+ int y, y1;
+
+ if (s == active_console) {
+ int x = s->x;
+
+ if (!ds_get_bits_per_pixel(s->ds)) {
+ s->cursor_invalidate = 1;
+ return;
+ }
+
+ if (x >= s->width) {
+ x = s->width - 1;
+ }
+ y1 = (s->y_base + s->y) % s->total_height;
+ y = y1 - s->y_displayed;
+ if (y < 0)
+ y += s->total_height;
+ if (y < s->height) {
+ c = &s->cells[y1 * s->width + x];
+ if (show) {
+ TextAttributes t_attrib = s->t_attrib_default;
+ t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
+ vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
+ } else {
+ vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
+ }
+ invalidate_xy(s, x, y);
+ }
+ }
+}
+
+static void console_refresh(TextConsole *s)
+{
+ TextCell *c;
+ int x, y, y1;
+
+ if (s != active_console)
+ return;
+ if (!ds_get_bits_per_pixel(s->ds)) {
+ s->text_x[0] = 0;
+ s->text_y[0] = 0;
+ s->text_x[1] = s->width - 1;
+ s->text_y[1] = s->height - 1;
+ s->cursor_invalidate = 1;
+ return;
+ }
+
+ vga_fill_rect(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds),
+ color_table[0][COLOR_BLACK]);
+ y1 = s->y_displayed;
+ for(y = 0; y < s->height; y++) {
+ c = s->cells + y1 * s->width;
+ for(x = 0; x < s->width; x++) {
+ vga_putcharxy(s->ds, x, y, c->ch,
+ &(c->t_attrib));
+ c++;
+ }
+ if (++y1 == s->total_height)
+ y1 = 0;
+ }
+ console_show_cursor(s, 1);
+ dpy_update(s->ds, 0, 0, ds_get_width(s->ds), ds_get_height(s->ds));
+}
+
+static void console_scroll(int ydelta)
+{
+ TextConsole *s;
+ int i, y1;
+
+ s = active_console;
+ if (!s || (s->console_type == GRAPHIC_CONSOLE))
+ return;
+
+ if (ydelta > 0) {
+ for(i = 0; i < ydelta; i++) {
+ if (s->y_displayed == s->y_base)
+ break;
+ if (++s->y_displayed == s->total_height)
+ s->y_displayed = 0;
+ }
+ } else {
+ ydelta = -ydelta;
+ i = s->backscroll_height;
+ if (i > s->total_height - s->height)
+ i = s->total_height - s->height;
+ y1 = s->y_base - i;
+ if (y1 < 0)
+ y1 += s->total_height;
+ for(i = 0; i < ydelta; i++) {
+ if (s->y_displayed == y1)
+ break;
+ if (--s->y_displayed < 0)
+ s->y_displayed = s->total_height - 1;
+ }
+ }
+ console_refresh(s);
+}
+
+static void console_put_lf(TextConsole *s)
+{
+ TextCell *c;
+ int x, y1;
+
+ s->y++;
+ if (s->y >= s->height) {
+ s->y = s->height - 1;
+
+ if (s->y_displayed == s->y_base) {
+ if (++s->y_displayed == s->total_height)
+ s->y_displayed = 0;
+ }
+ if (++s->y_base == s->total_height)
+ s->y_base = 0;
+ if (s->backscroll_height < s->total_height)
+ s->backscroll_height++;
+ y1 = (s->y_base + s->height - 1) % s->total_height;
+ c = &s->cells[y1 * s->width];
+ for(x = 0; x < s->width; x++) {
+ c->ch = ' ';
+ c->t_attrib = s->t_attrib_default;
+ c++;
+ }
+ if (s == active_console && s->y_displayed == s->y_base) {
+ if (!ds_get_bits_per_pixel(s->ds)) {
+ s->text_x[0] = 0;
+ s->text_y[0] = 0;
+ s->text_x[1] = s->width - 1;
+ s->text_y[1] = s->height - 1;
+ return;
+ }
+
+ vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
+ s->width * FONT_WIDTH,
+ (s->height - 1) * FONT_HEIGHT);
+ vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
+ s->width * FONT_WIDTH, FONT_HEIGHT,
+ color_table[0][s->t_attrib_default.bgcol]);
+ s->update_x0 = 0;
+ s->update_y0 = 0;
+ s->update_x1 = s->width * FONT_WIDTH;
+ s->update_y1 = s->height * FONT_HEIGHT;
+ }
+ }
+}
+
+/* Set console attributes depending on the current escape codes.
+ * NOTE: I know this code is not very efficient (checking every color for it
+ * self) but it is more readable and better maintainable.
+ */
+static void console_handle_escape(TextConsole *s)
+{
+ int i;
+
+ for (i=0; i<s->nb_esc_params; i++) {
+ switch (s->esc_params[i]) {
+ case 0: /* reset all console attributes to default */
+ s->t_attrib = s->t_attrib_default;
+ break;
+ case 1:
+ s->t_attrib.bold = 1;
+ break;
+ case 4:
+ s->t_attrib.uline = 1;
+ break;
+ case 5:
+ s->t_attrib.blink = 1;
+ break;
+ case 7:
+ s->t_attrib.invers = 1;
+ break;
+ case 8:
+ s->t_attrib.unvisible = 1;
+ break;
+ case 22:
+ s->t_attrib.bold = 0;
+ break;
+ case 24:
+ s->t_attrib.uline = 0;
+ break;
+ case 25:
+ s->t_attrib.blink = 0;
+ break;
+ case 27:
+ s->t_attrib.invers = 0;
+ break;
+ case 28:
+ s->t_attrib.unvisible = 0;
+ break;
+ /* set foreground color */
+ case 30:
+ s->t_attrib.fgcol=COLOR_BLACK;
+ break;
+ case 31:
+ s->t_attrib.fgcol=COLOR_RED;
+ break;
+ case 32:
+ s->t_attrib.fgcol=COLOR_GREEN;
+ break;
+ case 33:
+ s->t_attrib.fgcol=COLOR_YELLOW;
+ break;
+ case 34:
+ s->t_attrib.fgcol=COLOR_BLUE;
+ break;
+ case 35:
+ s->t_attrib.fgcol=COLOR_MAGENTA;
+ break;
+ case 36:
+ s->t_attrib.fgcol=COLOR_CYAN;
+ break;
+ case 37:
+ s->t_attrib.fgcol=COLOR_WHITE;
+ break;
+ /* set background color */
+ case 40:
+ s->t_attrib.bgcol=COLOR_BLACK;
+ break;
+ case 41:
+ s->t_attrib.bgcol=COLOR_RED;
+ break;
+ case 42:
+ s->t_attrib.bgcol=COLOR_GREEN;
+ break;
+ case 43:
+ s->t_attrib.bgcol=COLOR_YELLOW;
+ break;
+ case 44:
+ s->t_attrib.bgcol=COLOR_BLUE;
+ break;
+ case 45:
+ s->t_attrib.bgcol=COLOR_MAGENTA;
+ break;
+ case 46:
+ s->t_attrib.bgcol=COLOR_CYAN;
+ break;
+ case 47:
+ s->t_attrib.bgcol=COLOR_WHITE;
+ break;
+ }
+ }
+}
+
+static void console_clear_xy(TextConsole *s, int x, int y)
+{
+ int y1 = (s->y_base + y) % s->total_height;
+ TextCell *c = &s->cells[y1 * s->width + x];
+ c->ch = ' ';
+ c->t_attrib = s->t_attrib_default;
+ update_xy(s, x, y);
+}
+
+static void console_putchar(TextConsole *s, int ch)
+{
+ TextCell *c;
+ int y1, i;
+ int x, y;
+
+ switch(s->state) {
+ case TTY_STATE_NORM:
+ switch(ch) {
+ case '\r': /* carriage return */
+ s->x = 0;
+ break;
+ case '\n': /* newline */
+ console_put_lf(s);
+ break;
+ case '\b': /* backspace */
+ if (s->x > 0)
+ s->x--;
+ break;
+ case '\t': /* tabspace */
+ if (s->x + (8 - (s->x % 8)) > s->width) {
+ s->x = 0;
+ console_put_lf(s);
+ } else {
+ s->x = s->x + (8 - (s->x % 8));
+ }
+ break;
+ case '\a': /* alert aka. bell */
+ /* TODO: has to be implemented */
+ break;
+ case 14:
+ /* SI (shift in), character set 0 (ignored) */
+ break;
+ case 15:
+ /* SO (shift out), character set 1 (ignored) */
+ break;
+ case 27: /* esc (introducing an escape sequence) */
+ s->state = TTY_STATE_ESC;
+ break;
+ default:
+ if (s->x >= s->width) {
+ /* line wrap */
+ s->x = 0;
+ console_put_lf(s);
+ }
+ y1 = (s->y_base + s->y) % s->total_height;
+ c = &s->cells[y1 * s->width + s->x];
+ c->ch = ch;
+ c->t_attrib = s->t_attrib;
+ update_xy(s, s->x, s->y);
+ s->x++;
+ break;
+ }
+ break;
+ case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
+ if (ch == '[') {
+ for(i=0;i<MAX_ESC_PARAMS;i++)
+ s->esc_params[i] = 0;
+ s->nb_esc_params = 0;
+ s->state = TTY_STATE_CSI;
+ } else {
+ s->state = TTY_STATE_NORM;
+ }
+ break;
+ case TTY_STATE_CSI: /* handle escape sequence parameters */
+ if (ch >= '0' && ch <= '9') {
+ if (s->nb_esc_params < MAX_ESC_PARAMS) {
+ s->esc_params[s->nb_esc_params] =
+ s->esc_params[s->nb_esc_params] * 10 + ch - '0';
+ }
+ } else {
+ s->nb_esc_params++;
+ if (ch == ';')
+ break;
+#ifdef DEBUG_CONSOLE
+ fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
+ s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
+#endif
+ s->state = TTY_STATE_NORM;
+ switch(ch) {
+ case 'A':
+ /* move cursor up */
+ if (s->esc_params[0] == 0) {
+ s->esc_params[0] = 1;
+ }
+ s->y -= s->esc_params[0];
+ if (s->y < 0) {
+ s->y = 0;
+ }
+ break;
+ case 'B':
+ /* move cursor down */
+ if (s->esc_params[0] == 0) {
+ s->esc_params[0] = 1;
+ }
+ s->y += s->esc_params[0];
+ if (s->y >= s->height) {
+ s->y = s->height - 1;
+ }
+ break;
+ case 'C':
+ /* move cursor right */
+ if (s->esc_params[0] == 0) {
+ s->esc_params[0] = 1;
+ }
+ s->x += s->esc_params[0];
+ if (s->x >= s->width) {
+ s->x = s->width - 1;
+ }
+ break;
+ case 'D':
+ /* move cursor left */
+ if (s->esc_params[0] == 0) {
+ s->esc_params[0] = 1;
+ }
+ s->x -= s->esc_params[0];
+ if (s->x < 0) {
+ s->x = 0;
+ }
+ break;
+ case 'G':
+ /* move cursor to column */
+ s->x = s->esc_params[0] - 1;
+ if (s->x < 0) {
+ s->x = 0;
+ }
+ break;
+ case 'f':
+ case 'H':
+ /* move cursor to row, column */
+ s->x = s->esc_params[1] - 1;
+ if (s->x < 0) {
+ s->x = 0;
+ }
+ s->y = s->esc_params[0] - 1;
+ if (s->y < 0) {
+ s->y = 0;
+ }
+ break;
+ case 'J':
+ switch (s->esc_params[0]) {
+ case 0:
+ /* clear to end of screen */
+ for (y = s->y; y < s->height; y++) {
+ for (x = 0; x < s->width; x++) {
+ if (y == s->y && x < s->x) {
+ continue;
+ }
+ console_clear_xy(s, x, y);
+ }
+ }
+ break;
+ case 1:
+ /* clear from beginning of screen */
+ for (y = 0; y <= s->y; y++) {
+ for (x = 0; x < s->width; x++) {
+ if (y == s->y && x > s->x) {
+ break;
+ }
+ console_clear_xy(s, x, y);
+ }
+ }
+ break;
+ case 2:
+ /* clear entire screen */
+ for (y = 0; y <= s->height; y++) {
+ for (x = 0; x < s->width; x++) {
+ console_clear_xy(s, x, y);
+ }
+ }
+ break;
+ }
+ case 'K':
+ switch (s->esc_params[0]) {
+ case 0:
+ /* clear to eol */
+ for(x = s->x; x < s->width; x++) {
+ console_clear_xy(s, x, s->y);
+ }
+ break;
+ case 1:
+ /* clear from beginning of line */
+ for (x = 0; x <= s->x; x++) {
+ console_clear_xy(s, x, s->y);
+ }
+ break;
+ case 2:
+ /* clear entire line */
+ for(x = 0; x < s->width; x++) {
+ console_clear_xy(s, x, s->y);
+ }
+ break;
+ }
+ break;
+ case 'm':
+ console_handle_escape(s);
+ break;
+ case 'n':
+ /* report cursor position */
+ /* TODO: send ESC[row;colR */
+ break;
+ case 's':
+ /* save cursor position */
+ s->x_saved = s->x;
+ s->y_saved = s->y;
+ break;
+ case 'u':
+ /* restore cursor position */
+ s->x = s->x_saved;
+ s->y = s->y_saved;
+ break;
+ default:
+#ifdef DEBUG_CONSOLE
+ fprintf(stderr, "unhandled escape character '%c'\n", ch);
+#endif
+ break;
+ }
+ break;
+ }
+ }
+}
+
+void console_select(unsigned int index)
+{
+ TextConsole *s;
+
+ if (index >= MAX_CONSOLES)
+ return;
+ if (active_console) {
+ active_console->g_width = ds_get_width(active_console->ds);
+ active_console->g_height = ds_get_height(active_console->ds);
+ }
+ s = consoles[index];
+ if (s) {
+ DisplayState *ds = s->ds;
+ active_console = s;
+ if (ds_get_bits_per_pixel(s->ds)) {
+ ds->surface = qemu_resize_displaysurface(ds, s->g_width, s->g_height);
+ } else {
+ s->ds->surface->width = s->width;
+ s->ds->surface->height = s->height;
+ }
+ dpy_resize(s->ds);
+ vga_hw_invalidate();
+ }
+}
+
+static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ TextConsole *s = chr->opaque;
+ int i;
+
+ s->update_x0 = s->width * FONT_WIDTH;
+ s->update_y0 = s->height * FONT_HEIGHT;
+ s->update_x1 = 0;
+ s->update_y1 = 0;
+ console_show_cursor(s, 0);
+ for(i = 0; i < len; i++) {
+ console_putchar(s, buf[i]);
+ }
+ console_show_cursor(s, 1);
+ if (ds_get_bits_per_pixel(s->ds) && s->update_x0 < s->update_x1) {
+ dpy_update(s->ds, s->update_x0, s->update_y0,
+ s->update_x1 - s->update_x0,
+ s->update_y1 - s->update_y0);
+ }
+ return len;
+}
+
+static void console_send_event(CharDriverState *chr, int event)
+{
+ TextConsole *s = chr->opaque;
+ int i;
+
+ if (event == CHR_EVENT_FOCUS) {
+ for(i = 0; i < nb_consoles; i++) {
+ if (consoles[i] == s) {
+ console_select(i);
+ break;
+ }
+ }
+ }
+}
+
+static void kbd_send_chars(void *opaque)
+{
+ TextConsole *s = opaque;
+ int len;
+ uint8_t buf[16];
+
+ len = qemu_chr_can_read(s->chr);
+ if (len > s->out_fifo.count)
+ len = s->out_fifo.count;
+ if (len > 0) {
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+ qemu_fifo_read(&s->out_fifo, buf, len);
+ qemu_chr_read(s->chr, buf, len);
+ }
+ /* characters are pending: we send them a bit later (XXX:
+ horrible, should change char device API) */
+ if (s->out_fifo.count > 0) {
+ qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
+ }
+}
+
+/* called when an ascii key is pressed */
+void kbd_put_keysym(int keysym)
+{
+ TextConsole *s;
+ uint8_t buf[16], *q;
+ int c;
+
+ s = active_console;
+ if (!s || (s->console_type == GRAPHIC_CONSOLE))
+ return;
+
+ switch(keysym) {
+ case QEMU_KEY_CTRL_UP:
+ console_scroll(-1);
+ break;
+ case QEMU_KEY_CTRL_DOWN:
+ console_scroll(1);
+ break;
+ case QEMU_KEY_CTRL_PAGEUP:
+ console_scroll(-10);
+ break;
+ case QEMU_KEY_CTRL_PAGEDOWN:
+ console_scroll(10);
+ break;
+ default:
+ /* convert the QEMU keysym to VT100 key string */
+ q = buf;
+ if (keysym >= 0xe100 && keysym <= 0xe11f) {
+ *q++ = '\033';
+ *q++ = '[';
+ c = keysym - 0xe100;
+ if (c >= 10)
+ *q++ = '0' + (c / 10);
+ *q++ = '0' + (c % 10);
+ *q++ = '~';
+ } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
+ *q++ = '\033';
+ *q++ = '[';
+ *q++ = keysym & 0xff;
+ } else if (s->echo && (keysym == '\r' || keysym == '\n')) {
+ console_puts(s->chr, (const uint8_t *) "\r", 1);
+ *q++ = '\n';
+ } else {
+ *q++ = keysym;
+ }
+ if (s->echo) {
+ console_puts(s->chr, buf, q - buf);
+ }
+ if (s->chr->chr_read) {
+ qemu_fifo_write(&s->out_fifo, buf, q - buf);
+ kbd_send_chars(s);
+ }
+ break;
+ }
+}
+
+static void text_console_invalidate(void *opaque)
+{
+ TextConsole *s = (TextConsole *) opaque;
+ if (!ds_get_bits_per_pixel(s->ds) && s->console_type == TEXT_CONSOLE) {
+ s->g_width = ds_get_width(s->ds);
+ s->g_height = ds_get_height(s->ds);
+ text_console_resize(s);
+ }
+ console_refresh(s);
+}
+
+static void text_console_update(void *opaque, console_ch_t *chardata)
+{
+ TextConsole *s = (TextConsole *) opaque;
+ int i, j, src;
+
+ if (s->text_x[0] <= s->text_x[1]) {
+ src = (s->y_base + s->text_y[0]) * s->width;
+ chardata += s->text_y[0] * s->width;
+ for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
+ for (j = 0; j < s->width; j ++, src ++)
+ console_write_ch(chardata ++, s->cells[src].ch |
+ (s->cells[src].t_attrib.fgcol << 12) |
+ (s->cells[src].t_attrib.bgcol << 8) |
+ (s->cells[src].t_attrib.bold << 21));
+ dpy_update(s->ds, s->text_x[0], s->text_y[0],
+ s->text_x[1] - s->text_x[0], i - s->text_y[0]);
+ s->text_x[0] = s->width;
+ s->text_y[0] = s->height;
+ s->text_x[1] = 0;
+ s->text_y[1] = 0;
+ }
+ if (s->cursor_invalidate) {
+ dpy_cursor(s->ds, s->x, s->y);
+ s->cursor_invalidate = 0;
+ }
+}
+
+static TextConsole *get_graphic_console(DisplayState *ds)
+{
+ int i;
+ TextConsole *s;
+ for (i = 0; i < nb_consoles; i++) {
+ s = consoles[i];
+ if (s->console_type == GRAPHIC_CONSOLE && s->ds == ds)
+ return s;
+ }
+ return NULL;
+}
+
+static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
+{
+ TextConsole *s;
+ int i;
+
+ if (nb_consoles >= MAX_CONSOLES)
+ return NULL;
+ s = qemu_mallocz(sizeof(TextConsole));
+ if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
+ (console_type == GRAPHIC_CONSOLE))) {
+ active_console = s;
+ }
+ s->ds = ds;
+ s->console_type = console_type;
+ if (console_type != GRAPHIC_CONSOLE) {
+ consoles[nb_consoles++] = s;
+ } else {
+ /* HACK: Put graphical consoles before text consoles. */
+ for (i = nb_consoles; i > 0; i--) {
+ if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
+ break;
+ consoles[i] = consoles[i - 1];
+ }
+ consoles[i] = s;
+ nb_consoles++;
+ }
+ return s;
+}
+
+static DisplaySurface* defaultallocator_create_displaysurface(int width, int height)
+{
+ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
+
+ surface->width = width;
+ surface->height = height;
+ surface->linesize = width * 4;
+ surface->pf = qemu_default_pixelformat(32);
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_ALLOCATED_FLAG;
+#endif
+ surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
+
+ return surface;
+}
+
+static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface,
+ int width, int height)
+{
+ surface->width = width;
+ surface->height = height;
+ surface->linesize = width * 4;
+ surface->pf = qemu_default_pixelformat(32);
+ if (surface->flags & QEMU_ALLOCATED_FLAG)
+ surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height);
+ else
+ surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height);
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_ALLOCATED_FLAG;
+#endif
+
+ return surface;
+}
+
+DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
+ int linesize, uint8_t *data)
+{
+ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
+
+ surface->width = width;
+ surface->height = height;
+ surface->linesize = linesize;
+ surface->pf = qemu_default_pixelformat(bpp);
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_BIG_ENDIAN_FLAG;
+#endif
+ surface->data = data;
+
+ return surface;
+}
+
+static void defaultallocator_free_displaysurface(DisplaySurface *surface)
+{
+ if (surface == NULL)
+ return;
+ if (surface->flags & QEMU_ALLOCATED_FLAG)
+ qemu_free(surface->data);
+ qemu_free(surface);
+}
+
+static struct DisplayAllocator default_allocator = {
+ defaultallocator_create_displaysurface,
+ defaultallocator_resize_displaysurface,
+ defaultallocator_free_displaysurface
+};
+
+static void dumb_display_init(void)
+{
+ DisplayState *ds = qemu_mallocz(sizeof(DisplayState));
+ ds->allocator = &default_allocator;
+ ds->surface = qemu_create_displaysurface(ds, 640, 480);
+ register_displaystate(ds);
+}
+
+/***********************************************************/
+/* register display */
+
+void register_displaystate(DisplayState *ds)
+{
+ DisplayState **s;
+ s = &display_state;
+ while (*s != NULL)
+ s = &(*s)->next;
+ ds->next = NULL;
+ *s = ds;
+}
+
+DisplayState *get_displaystate(void)
+{
+ if (!display_state) {
+ dumb_display_init ();
+ }
+ return display_state;
+}
+
+DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da)
+{
+ if(ds->allocator == &default_allocator) {
+ DisplaySurface *surf;
+ surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds));
+ defaultallocator_free_displaysurface(ds->surface);
+ ds->surface = surf;
+ ds->allocator = da;
+ }
+ return ds->allocator;
+}
+
+DisplayState *graphic_console_init(vga_hw_update_ptr update,
+ vga_hw_invalidate_ptr invalidate,
+ vga_hw_screen_dump_ptr screen_dump,
+ vga_hw_text_update_ptr text_update,
+ void *opaque)
+{
+ TextConsole *s;
+ DisplayState *ds;
+
+ ds = (DisplayState *) qemu_mallocz(sizeof(DisplayState));
+ ds->allocator = &default_allocator;
+ ds->surface = qemu_create_displaysurface(ds, 640, 480);
+
+ s = new_console(ds, GRAPHIC_CONSOLE);
+ if (s == NULL) {
+ qemu_free_displaysurface(ds);
+ qemu_free(ds);
+ return NULL;
+ }
+ s->hw_update = update;
+ s->hw_invalidate = invalidate;
+ s->hw_screen_dump = screen_dump;
+ s->hw_text_update = text_update;
+ s->hw = opaque;
+
+ register_displaystate(ds);
+ return ds;
+}
+
+int is_graphic_console(void)
+{
+ return active_console && active_console->console_type == GRAPHIC_CONSOLE;
+}
+
+int is_fixedsize_console(void)
+{
+ return active_console && active_console->console_type != TEXT_CONSOLE;
+}
+
+void console_color_init(DisplayState *ds)
+{
+ int i, j;
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 8; i++) {
+ color_table[j][i] = col_expand(ds,
+ vga_get_color(ds, color_table_rgb[j][i]));
+ }
+ }
+}
+
+static int n_text_consoles;
+static CharDriverState *text_consoles[128];
+
+static void text_console_set_echo(CharDriverState *chr, bool echo)
+{
+ TextConsole *s = chr->opaque;
+
+ s->echo = echo;
+}
+
+static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
+{
+ TextConsole *s;
+ static int color_inited;
+
+ s = chr->opaque;
+
+ chr->chr_write = console_puts;
+ chr->chr_send_event = console_send_event;
+
+ s->out_fifo.buf = s->out_fifo_buf;
+ s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
+ s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
+ s->ds = ds;
+
+ if (!color_inited) {
+ color_inited = 1;
+ console_color_init(s->ds);
+ }
+ s->y_displayed = 0;
+ s->y_base = 0;
+ s->total_height = DEFAULT_BACKSCROLL;
+ s->x = 0;
+ s->y = 0;
+ if (s->console_type == TEXT_CONSOLE) {
+ s->g_width = ds_get_width(s->ds);
+ s->g_height = ds_get_height(s->ds);
+ }
+
+ s->hw_invalidate = text_console_invalidate;
+ s->hw_text_update = text_console_update;
+ s->hw = s;
+
+ /* Set text attribute defaults */
+ s->t_attrib_default.bold = 0;
+ s->t_attrib_default.uline = 0;
+ s->t_attrib_default.blink = 0;
+ s->t_attrib_default.invers = 0;
+ s->t_attrib_default.unvisible = 0;
+ s->t_attrib_default.fgcol = COLOR_WHITE;
+ s->t_attrib_default.bgcol = COLOR_BLACK;
+ /* set current text attributes to default */
+ s->t_attrib = s->t_attrib_default;
+ text_console_resize(s);
+
+ if (chr->label) {
+ char msg[128];
+ int len;
+
+ s->t_attrib.bgcol = COLOR_BLUE;
+ len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
+ console_puts(chr, (uint8_t*)msg, len);
+ s->t_attrib = s->t_attrib_default;
+ }
+
+ qemu_chr_generic_open(chr);
+ if (chr->init)
+ chr->init(chr);
+}
+
+CharDriverState *text_console_init(QemuOpts *opts)
+{
+ CharDriverState *chr;
+ TextConsole *s;
+ unsigned width;
+ unsigned height;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+
+ if (n_text_consoles == 128) {
+ fprintf(stderr, "Too many text consoles\n");
+ exit(1);
+ }
+ text_consoles[n_text_consoles] = chr;
+ n_text_consoles++;
+
+ width = qemu_opt_get_number(opts, "width", 0);
+ if (width == 0)
+ width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH;
+
+ height = qemu_opt_get_number(opts, "height", 0);
+ if (height == 0)
+ height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT;
+
+ if (width == 0 || height == 0) {
+ s = new_console(NULL, TEXT_CONSOLE);
+ } else {
+ s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
+ }
+
+ if (!s) {
+ free(chr);
+ return NULL;
+ }
+
+ s->chr = chr;
+ s->g_width = width;
+ s->g_height = height;
+ chr->opaque = s;
+ chr->chr_set_echo = text_console_set_echo;
+ return chr;
+}
+
+void text_consoles_set_display(DisplayState *ds)
+{
+ int i;
+
+ for (i = 0; i < n_text_consoles; i++) {
+ text_console_do_init(text_consoles[i], ds);
+ }
+
+ n_text_consoles = 0;
+}
+
+void qemu_console_resize(DisplayState *ds, int width, int height)
+{
+ TextConsole *s = get_graphic_console(ds);
+ if (!s) return;
+
+ s->g_width = width;
+ s->g_height = height;
+ if (is_graphic_console()) {
+ ds->surface = qemu_resize_displaysurface(ds, width, height);
+ dpy_resize(ds);
+ }
+}
+
+void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
+ int dst_x, int dst_y, int w, int h)
+{
+ if (is_graphic_console()) {
+ dpy_copy(ds, src_x, src_y, dst_x, dst_y, w, h);
+ }
+}
+
+PixelFormat qemu_different_endianness_pixelformat(int bpp)
+{
+ PixelFormat pf;
+
+ memset(&pf, 0x00, sizeof(PixelFormat));
+
+ pf.bits_per_pixel = bpp;
+ pf.bytes_per_pixel = bpp / 8;
+ pf.depth = bpp == 32 ? 24 : bpp;
+
+ switch (bpp) {
+ case 24:
+ pf.rmask = 0x000000FF;
+ pf.gmask = 0x0000FF00;
+ pf.bmask = 0x00FF0000;
+ pf.rmax = 255;
+ pf.gmax = 255;
+ pf.bmax = 255;
+ pf.rshift = 0;
+ pf.gshift = 8;
+ pf.bshift = 16;
+ pf.rbits = 8;
+ pf.gbits = 8;
+ pf.bbits = 8;
+ break;
+ case 32:
+ pf.rmask = 0x0000FF00;
+ pf.gmask = 0x00FF0000;
+ pf.bmask = 0xFF000000;
+ pf.amask = 0x00000000;
+ pf.amax = 255;
+ pf.rmax = 255;
+ pf.gmax = 255;
+ pf.bmax = 255;
+ pf.ashift = 0;
+ pf.rshift = 8;
+ pf.gshift = 16;
+ pf.bshift = 24;
+ pf.rbits = 8;
+ pf.gbits = 8;
+ pf.bbits = 8;
+ pf.abits = 8;
+ break;
+ default:
+ break;
+ }
+ return pf;
+}
+
+PixelFormat qemu_default_pixelformat(int bpp)
+{
+ PixelFormat pf;
+
+ memset(&pf, 0x00, sizeof(PixelFormat));
+
+ pf.bits_per_pixel = bpp;
+ pf.bytes_per_pixel = bpp / 8;
+ pf.depth = bpp == 32 ? 24 : bpp;
+
+ switch (bpp) {
+ case 15:
+ pf.bits_per_pixel = 16;
+ pf.bytes_per_pixel = 2;
+ pf.rmask = 0x00007c00;
+ pf.gmask = 0x000003E0;
+ pf.bmask = 0x0000001F;
+ pf.rmax = 31;
+ pf.gmax = 31;
+ pf.bmax = 31;
+ pf.rshift = 10;
+ pf.gshift = 5;
+ pf.bshift = 0;
+ pf.rbits = 5;
+ pf.gbits = 5;
+ pf.bbits = 5;
+ break;
+ case 16:
+ pf.rmask = 0x0000F800;
+ pf.gmask = 0x000007E0;
+ pf.bmask = 0x0000001F;
+ pf.rmax = 31;
+ pf.gmax = 63;
+ pf.bmax = 31;
+ pf.rshift = 11;
+ pf.gshift = 5;
+ pf.bshift = 0;
+ pf.rbits = 5;
+ pf.gbits = 6;
+ pf.bbits = 5;
+ break;
+ case 24:
+ pf.rmask = 0x00FF0000;
+ pf.gmask = 0x0000FF00;
+ pf.bmask = 0x000000FF;
+ pf.rmax = 255;
+ pf.gmax = 255;
+ pf.bmax = 255;
+ pf.rshift = 16;
+ pf.gshift = 8;
+ pf.bshift = 0;
+ pf.rbits = 8;
+ pf.gbits = 8;
+ pf.bbits = 8;
+ case 32:
+ pf.rmask = 0x00FF0000;
+ pf.gmask = 0x0000FF00;
+ pf.bmask = 0x000000FF;
+ pf.amax = 255;
+ pf.rmax = 255;
+ pf.gmax = 255;
+ pf.bmax = 255;
+ pf.ashift = 24;
+ pf.rshift = 16;
+ pf.gshift = 8;
+ pf.bshift = 0;
+ pf.rbits = 8;
+ pf.gbits = 8;
+ pf.bbits = 8;
+ pf.abits = 8;
+ break;
+ default:
+ break;
+ }
+ return pf;
+}
diff --git a/console.h b/console.h
new file mode 100644
index 0000000..f4e4741
--- /dev/null
+++ b/console.h
@@ -0,0 +1,381 @@
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include "qemu-char.h"
+#include "qdict.h"
+#include "notify.h"
+
+/* keyboard/mouse support */
+
+#define MOUSE_EVENT_LBUTTON 0x01
+#define MOUSE_EVENT_RBUTTON 0x02
+#define MOUSE_EVENT_MBUTTON 0x04
+
+/* identical to the ps/2 keyboard bits */
+#define QEMU_SCROLL_LOCK_LED (1 << 0)
+#define QEMU_NUM_LOCK_LED (1 << 1)
+#define QEMU_CAPS_LOCK_LED (1 << 2)
+
+/* in ms */
+#define GUI_REFRESH_INTERVAL 30
+
+typedef void QEMUPutKBDEvent(void *opaque, int keycode);
+typedef void QEMUPutLEDEvent(void *opaque, int ledstate);
+typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state);
+
+typedef struct QEMUPutMouseEntry {
+ QEMUPutMouseEvent *qemu_put_mouse_event;
+ void *qemu_put_mouse_event_opaque;
+ int qemu_put_mouse_event_absolute;
+ char *qemu_put_mouse_event_name;
+
+ int index;
+
+ /* used internally by qemu for handling mice */
+ QTAILQ_ENTRY(QEMUPutMouseEntry) node;
+} QEMUPutMouseEntry;
+
+typedef struct QEMUPutLEDEntry {
+ QEMUPutLEDEvent *put_led;
+ void *opaque;
+ QTAILQ_ENTRY(QEMUPutLEDEntry) next;
+} QEMUPutLEDEntry;
+
+void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque);
+void qemu_remove_kbd_event_handler(void);
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+ void *opaque, int absolute,
+ const char *name);
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry);
+void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry);
+
+QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, void *opaque);
+void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry);
+
+void kbd_put_keycode(int keycode);
+void kbd_put_ledstate(int ledstate);
+void kbd_mouse_event(int dx, int dy, int dz, int buttons_state);
+
+/* Does the current mouse generate absolute events */
+int kbd_mouse_is_absolute(void);
+void qemu_add_mouse_mode_change_notifier(Notifier *notify);
+void qemu_remove_mouse_mode_change_notifier(Notifier *notify);
+
+/* Of all the mice, is there one that generates absolute events */
+int kbd_mouse_has_absolute(void);
+
+struct MouseTransformInfo {
+ /* Touchscreen resolution */
+ int x;
+ int y;
+ /* Calibration values as used/generated by tslib */
+ int a[7];
+};
+
+void do_info_mice_print(Monitor *mon, const QObject *data);
+void do_info_mice(Monitor *mon, QObject **ret_data);
+void do_mouse_set(Monitor *mon, const QDict *qdict);
+
+/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
+ constants) */
+#define QEMU_KEY_ESC1(c) ((c) | 0xe100)
+#define QEMU_KEY_BACKSPACE 0x007f
+#define QEMU_KEY_UP QEMU_KEY_ESC1('A')
+#define QEMU_KEY_DOWN QEMU_KEY_ESC1('B')
+#define QEMU_KEY_RIGHT QEMU_KEY_ESC1('C')
+#define QEMU_KEY_LEFT QEMU_KEY_ESC1('D')
+#define QEMU_KEY_HOME QEMU_KEY_ESC1(1)
+#define QEMU_KEY_END QEMU_KEY_ESC1(4)
+#define QEMU_KEY_PAGEUP QEMU_KEY_ESC1(5)
+#define QEMU_KEY_PAGEDOWN QEMU_KEY_ESC1(6)
+#define QEMU_KEY_DELETE QEMU_KEY_ESC1(3)
+
+#define QEMU_KEY_CTRL_UP 0xe400
+#define QEMU_KEY_CTRL_DOWN 0xe401
+#define QEMU_KEY_CTRL_LEFT 0xe402
+#define QEMU_KEY_CTRL_RIGHT 0xe403
+#define QEMU_KEY_CTRL_HOME 0xe404
+#define QEMU_KEY_CTRL_END 0xe405
+#define QEMU_KEY_CTRL_PAGEUP 0xe406
+#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
+
+void kbd_put_keysym(int keysym);
+
+/* consoles */
+
+#define QEMU_BIG_ENDIAN_FLAG 0x01
+#define QEMU_ALLOCATED_FLAG 0x02
+#define QEMU_REALPIXELS_FLAG 0x04
+
+struct PixelFormat {
+ uint8_t bits_per_pixel;
+ uint8_t bytes_per_pixel;
+ uint8_t depth; /* color depth in bits */
+ uint32_t rmask, gmask, bmask, amask;
+ uint8_t rshift, gshift, bshift, ashift;
+ uint8_t rmax, gmax, bmax, amax;
+ uint8_t rbits, gbits, bbits, abits;
+};
+
+struct DisplaySurface {
+ uint8_t flags;
+ int width;
+ int height;
+ int linesize; /* bytes per line */
+ uint8_t *data;
+
+ struct PixelFormat pf;
+};
+
+/* cursor data format is 32bit RGBA */
+typedef struct QEMUCursor {
+ int width, height;
+ int hot_x, hot_y;
+ int refcount;
+ uint32_t data[];
+} QEMUCursor;
+
+QEMUCursor *cursor_alloc(int width, int height);
+void cursor_get(QEMUCursor *c);
+void cursor_put(QEMUCursor *c);
+QEMUCursor *cursor_builtin_hidden(void);
+QEMUCursor *cursor_builtin_left_ptr(void);
+void cursor_print_ascii_art(QEMUCursor *c, const char *prefix);
+int cursor_get_mono_bpl(QEMUCursor *c);
+void cursor_set_mono(QEMUCursor *c,
+ uint32_t foreground, uint32_t background, uint8_t *image,
+ int transparent, uint8_t *mask);
+void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *mask);
+void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask);
+
+struct DisplayChangeListener {
+ int idle;
+ uint64_t gui_timer_interval;
+
+ void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h);
+ void (*dpy_resize)(struct DisplayState *s);
+ void (*dpy_setdata)(struct DisplayState *s);
+ void (*dpy_refresh)(struct DisplayState *s);
+ void (*dpy_copy)(struct DisplayState *s, int src_x, int src_y,
+ int dst_x, int dst_y, int w, int h);
+ void (*dpy_fill)(struct DisplayState *s, int x, int y,
+ int w, int h, uint32_t c);
+ void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
+
+ struct DisplayChangeListener *next;
+};
+
+struct DisplayAllocator {
+ DisplaySurface* (*create_displaysurface)(int width, int height);
+ DisplaySurface* (*resize_displaysurface)(DisplaySurface *surface, int width, int height);
+ void (*free_displaysurface)(DisplaySurface *surface);
+};
+
+struct DisplayState {
+ struct DisplaySurface *surface;
+ void *opaque;
+ struct QEMUTimer *gui_timer;
+
+ struct DisplayAllocator* allocator;
+ struct DisplayChangeListener* listeners;
+
+ void (*mouse_set)(int x, int y, int on);
+ void (*cursor_define)(QEMUCursor *cursor);
+
+ struct DisplayState *next;
+};
+
+void register_displaystate(DisplayState *ds);
+DisplayState *get_displaystate(void);
+DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
+ int linesize, uint8_t *data);
+PixelFormat qemu_different_endianness_pixelformat(int bpp);
+PixelFormat qemu_default_pixelformat(int bpp);
+
+DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da);
+
+static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int width, int height)
+{
+ return ds->allocator->create_displaysurface(width, height);
+}
+
+static inline DisplaySurface* qemu_resize_displaysurface(DisplayState *ds, int width, int height)
+{
+ return ds->allocator->resize_displaysurface(ds->surface, width, height);
+}
+
+static inline void qemu_free_displaysurface(DisplayState *ds)
+{
+ ds->allocator->free_displaysurface(ds->surface);
+}
+
+static inline int is_surface_bgr(DisplaySurface *surface)
+{
+ if (surface->pf.bits_per_pixel == 32 && surface->pf.rshift == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static inline int is_buffer_shared(DisplaySurface *surface)
+{
+ return (!(surface->flags & QEMU_ALLOCATED_FLAG) &&
+ !(surface->flags & QEMU_REALPIXELS_FLAG));
+}
+
+static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
+{
+ dcl->next = ds->listeners;
+ ds->listeners = dcl;
+}
+
+static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
+{
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ dcl->dpy_update(s, x, y, w, h);
+ dcl = dcl->next;
+ }
+}
+
+static inline void dpy_resize(DisplayState *s)
+{
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ dcl->dpy_resize(s);
+ dcl = dcl->next;
+ }
+}
+
+static inline void dpy_setdata(DisplayState *s)
+{
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ if (dcl->dpy_setdata) dcl->dpy_setdata(s);
+ dcl = dcl->next;
+ }
+}
+
+static inline void dpy_refresh(DisplayState *s)
+{
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ if (dcl->dpy_refresh) dcl->dpy_refresh(s);
+ dcl = dcl->next;
+ }
+}
+
+static inline void dpy_copy(struct DisplayState *s, int src_x, int src_y,
+ int dst_x, int dst_y, int w, int h) {
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ if (dcl->dpy_copy)
+ dcl->dpy_copy(s, src_x, src_y, dst_x, dst_y, w, h);
+ else /* TODO */
+ dcl->dpy_update(s, dst_x, dst_y, w, h);
+ dcl = dcl->next;
+ }
+}
+
+static inline void dpy_fill(struct DisplayState *s, int x, int y,
+ int w, int h, uint32_t c) {
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ if (dcl->dpy_fill) dcl->dpy_fill(s, x, y, w, h, c);
+ dcl = dcl->next;
+ }
+}
+
+static inline void dpy_cursor(struct DisplayState *s, int x, int y) {
+ struct DisplayChangeListener *dcl = s->listeners;
+ while (dcl != NULL) {
+ if (dcl->dpy_text_cursor) dcl->dpy_text_cursor(s, x, y);
+ dcl = dcl->next;
+ }
+}
+
+static inline int ds_get_linesize(DisplayState *ds)
+{
+ return ds->surface->linesize;
+}
+
+static inline uint8_t* ds_get_data(DisplayState *ds)
+{
+ return ds->surface->data;
+}
+
+static inline int ds_get_width(DisplayState *ds)
+{
+ return ds->surface->width;
+}
+
+static inline int ds_get_height(DisplayState *ds)
+{
+ return ds->surface->height;
+}
+
+static inline int ds_get_bits_per_pixel(DisplayState *ds)
+{
+ return ds->surface->pf.bits_per_pixel;
+}
+
+static inline int ds_get_bytes_per_pixel(DisplayState *ds)
+{
+ return ds->surface->pf.bytes_per_pixel;
+}
+
+typedef unsigned long console_ch_t;
+static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
+{
+ if (!(ch & 0xff))
+ ch |= ' ';
+ *dest = ch;
+}
+
+typedef void (*vga_hw_update_ptr)(void *);
+typedef void (*vga_hw_invalidate_ptr)(void *);
+typedef void (*vga_hw_screen_dump_ptr)(void *, const char *);
+typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *);
+
+DisplayState *graphic_console_init(vga_hw_update_ptr update,
+ vga_hw_invalidate_ptr invalidate,
+ vga_hw_screen_dump_ptr screen_dump,
+ vga_hw_text_update_ptr text_update,
+ void *opaque);
+
+void vga_hw_update(void);
+void vga_hw_invalidate(void);
+void vga_hw_screen_dump(const char *filename);
+void vga_hw_text_update(console_ch_t *chardata);
+
+int is_graphic_console(void);
+int is_fixedsize_console(void);
+CharDriverState *text_console_init(QemuOpts *opts);
+void text_consoles_set_display(DisplayState *ds);
+void console_select(unsigned int index);
+void console_color_init(DisplayState *ds);
+void qemu_console_resize(DisplayState *ds, int width, int height);
+void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
+ int dst_x, int dst_y, int w, int h);
+
+/* sdl.c */
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
+
+/* cocoa.m */
+void cocoa_display_init(DisplayState *ds, int full_screen);
+
+/* vnc.c */
+void vnc_display_init(DisplayState *ds);
+void vnc_display_close(DisplayState *ds);
+int vnc_display_open(DisplayState *ds, const char *display);
+int vnc_display_password(DisplayState *ds, const char *password);
+int vnc_display_disable_login(DisplayState *ds);
+int vnc_display_pw_expire(DisplayState *ds, time_t expires);
+void do_info_vnc_print(Monitor *mon, const QObject *data);
+void do_info_vnc(Monitor *mon, QObject **ret_data);
+char *vnc_display_local_addr(DisplayState *ds);
+
+/* curses.c */
+void curses_display_init(DisplayState *ds, int full_screen);
+
+#endif
diff --git a/cpu-all.h b/cpu-all.h
new file mode 100644
index 0000000..ffbd6a4
--- /dev/null
+++ b/cpu-all.h
@@ -0,0 +1,972 @@
+/*
+ * defines common to all virtual CPUs
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#ifndef CPU_ALL_H
+#define CPU_ALL_H
+
+#include "qemu-common.h"
+#include "cpu-common.h"
+
+/* some important defines:
+ *
+ * WORDS_ALIGNED : if defined, the host cpu can only make word aligned
+ * memory accesses.
+ *
+ * HOST_WORDS_BIGENDIAN : if defined, the host cpu is big endian and
+ * otherwise little endian.
+ *
+ * (TARGET_WORDS_ALIGNED : same for target cpu (not supported yet))
+ *
+ * TARGET_WORDS_BIGENDIAN : same for target cpu
+ */
+
+#include "softfloat.h"
+
+#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+#define BSWAP_NEEDED
+#endif
+
+#ifdef BSWAP_NEEDED
+
+static inline uint16_t tswap16(uint16_t s)
+{
+ return bswap16(s);
+}
+
+static inline uint32_t tswap32(uint32_t s)
+{
+ return bswap32(s);
+}
+
+static inline uint64_t tswap64(uint64_t s)
+{
+ return bswap64(s);
+}
+
+static inline void tswap16s(uint16_t *s)
+{
+ *s = bswap16(*s);
+}
+
+static inline void tswap32s(uint32_t *s)
+{
+ *s = bswap32(*s);
+}
+
+static inline void tswap64s(uint64_t *s)
+{
+ *s = bswap64(*s);
+}
+
+#else
+
+static inline uint16_t tswap16(uint16_t s)
+{
+ return s;
+}
+
+static inline uint32_t tswap32(uint32_t s)
+{
+ return s;
+}
+
+static inline uint64_t tswap64(uint64_t s)
+{
+ return s;
+}
+
+static inline void tswap16s(uint16_t *s)
+{
+}
+
+static inline void tswap32s(uint32_t *s)
+{
+}
+
+static inline void tswap64s(uint64_t *s)
+{
+}
+
+#endif
+
+#if TARGET_LONG_SIZE == 4
+#define tswapl(s) tswap32(s)
+#define tswapls(s) tswap32s((uint32_t *)(s))
+#define bswaptls(s) bswap32s(s)
+#else
+#define tswapl(s) tswap64(s)
+#define tswapls(s) tswap64s((uint64_t *)(s))
+#define bswaptls(s) bswap64s(s)
+#endif
+
+typedef union {
+ float32 f;
+ uint32_t l;
+} CPU_FloatU;
+
+/* NOTE: arm FPA is horrible as double 32 bit words are stored in big
+ endian ! */
+typedef union {
+ float64 d;
+#if defined(HOST_WORDS_BIGENDIAN) \
+ || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
+ struct {
+ uint32_t upper;
+ uint32_t lower;
+ } l;
+#else
+ struct {
+ uint32_t lower;
+ uint32_t upper;
+ } l;
+#endif
+ uint64_t ll;
+} CPU_DoubleU;
+
+#ifdef TARGET_SPARC
+typedef union {
+ float128 q;
+#if defined(HOST_WORDS_BIGENDIAN) \
+ || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
+ struct {
+ uint32_t upmost;
+ uint32_t upper;
+ uint32_t lower;
+ uint32_t lowest;
+ } l;
+ struct {
+ uint64_t upper;
+ uint64_t lower;
+ } ll;
+#else
+ struct {
+ uint32_t lowest;
+ uint32_t lower;
+ uint32_t upper;
+ uint32_t upmost;
+ } l;
+ struct {
+ uint64_t lower;
+ uint64_t upper;
+ } ll;
+#endif
+} CPU_QuadU;
+#endif
+
+/* CPU memory access without any memory or io remapping */
+
+/*
+ * the generic syntax for the memory accesses is:
+ *
+ * load: ld{type}{sign}{size}{endian}_{access_type}(ptr)
+ *
+ * store: st{type}{size}{endian}_{access_type}(ptr, val)
+ *
+ * type is:
+ * (empty): integer access
+ * f : float access
+ *
+ * sign is:
+ * (empty): for floats or 32 bit size
+ * u : unsigned
+ * s : signed
+ *
+ * size is:
+ * b: 8 bits
+ * w: 16 bits
+ * l: 32 bits
+ * q: 64 bits
+ *
+ * endian is:
+ * (empty): target cpu endianness or 8 bit access
+ * r : reversed target cpu endianness (not implemented yet)
+ * be : big endian (not implemented yet)
+ * le : little endian (not implemented yet)
+ *
+ * access_type is:
+ * raw : host memory access
+ * user : user mode access using soft MMU
+ * kernel : kernel mode access using soft MMU
+ */
+static inline int ldub_p(const void *ptr)
+{
+ return *(uint8_t *)ptr;
+}
+
+static inline int ldsb_p(const void *ptr)
+{
+ return *(int8_t *)ptr;
+}
+
+static inline void stb_p(void *ptr, int v)
+{
+ *(uint8_t *)ptr = v;
+}
+
+/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
+ kernel handles unaligned load/stores may give better results, but
+ it is a system wide setting : bad */
+#if defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+/* conservative code for little endian unaligned accesses */
+static inline int lduw_le_p(const void *ptr)
+{
+#ifdef _ARCH_PPC
+ int val;
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return val;
+#else
+ const uint8_t *p = ptr;
+ return p[0] | (p[1] << 8);
+#endif
+}
+
+static inline int ldsw_le_p(const void *ptr)
+{
+#ifdef _ARCH_PPC
+ int val;
+ __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return (int16_t)val;
+#else
+ const uint8_t *p = ptr;
+ return (int16_t)(p[0] | (p[1] << 8));
+#endif
+}
+
+static inline int ldl_le_p(const void *ptr)
+{
+#ifdef _ARCH_PPC
+ int val;
+ __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+ return val;
+#else
+ const uint8_t *p = ptr;
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+#endif
+}
+
+static inline uint64_t ldq_le_p(const void *ptr)
+{
+ const uint8_t *p = ptr;
+ uint32_t v1, v2;
+ v1 = ldl_le_p(p);
+ v2 = ldl_le_p(p + 4);
+ return v1 | ((uint64_t)v2 << 32);
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+#ifdef _ARCH_PPC
+ __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
+#else
+ uint8_t *p = ptr;
+ p[0] = v;
+ p[1] = v >> 8;
+#endif
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+#ifdef _ARCH_PPC
+ __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
+#else
+ uint8_t *p = ptr;
+ p[0] = v;
+ p[1] = v >> 8;
+ p[2] = v >> 16;
+ p[3] = v >> 24;
+#endif
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+ uint8_t *p = ptr;
+ stl_le_p(p, (uint32_t)v);
+ stl_le_p(p + 4, v >> 32);
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(const void *ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = ldl_le_p(ptr);
+ return u.f;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl_le_p(ptr, u.i);
+}
+
+static inline float64 ldfq_le_p(const void *ptr)
+{
+ CPU_DoubleU u;
+ u.l.lower = ldl_le_p(ptr);
+ u.l.upper = ldl_le_p(ptr + 4);
+ return u.d;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+ CPU_DoubleU u;
+ u.d = v;
+ stl_le_p(ptr, u.l.lower);
+ stl_le_p(ptr + 4, u.l.upper);
+}
+
+#else
+
+static inline int lduw_le_p(const void *ptr)
+{
+ return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_le_p(const void *ptr)
+{
+ return *(int16_t *)ptr;
+}
+
+static inline int ldl_le_p(const void *ptr)
+{
+ return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_le_p(const void *ptr)
+{
+ return *(uint64_t *)ptr;
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+ *(uint16_t *)ptr = v;
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+ *(uint32_t *)ptr = v;
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+ *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(const void *ptr)
+{
+ return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_le_p(const void *ptr)
+{
+ return *(float64 *)ptr;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+ *(float32 *)ptr = v;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+ *(float64 *)ptr = v;
+}
+#endif
+
+#if !defined(HOST_WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+static inline int lduw_be_p(const void *ptr)
+{
+#if defined(__i386__)
+ int val;
+ asm volatile ("movzwl %1, %0\n"
+ "xchgb %b0, %h0\n"
+ : "=q" (val)
+ : "m" (*(uint16_t *)ptr));
+ return val;
+#else
+ const uint8_t *b = ptr;
+ return ((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldsw_be_p(const void *ptr)
+{
+#if defined(__i386__)
+ int val;
+ asm volatile ("movzwl %1, %0\n"
+ "xchgb %b0, %h0\n"
+ : "=q" (val)
+ : "m" (*(uint16_t *)ptr));
+ return (int16_t)val;
+#else
+ const uint8_t *b = ptr;
+ return (int16_t)((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldl_be_p(const void *ptr)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ int val;
+ asm volatile ("movl %1, %0\n"
+ "bswap %0\n"
+ : "=r" (val)
+ : "m" (*(uint32_t *)ptr));
+ return val;
+#else
+ const uint8_t *b = ptr;
+ return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+#endif
+}
+
+static inline uint64_t ldq_be_p(const void *ptr)
+{
+ uint32_t a,b;
+ a = ldl_be_p(ptr);
+ b = ldl_be_p((uint8_t *)ptr + 4);
+ return (((uint64_t)a<<32)|b);
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+#if defined(__i386__)
+ asm volatile ("xchgb %b0, %h0\n"
+ "movw %w0, %1\n"
+ : "=q" (v)
+ : "m" (*(uint16_t *)ptr), "0" (v));
+#else
+ uint8_t *d = (uint8_t *) ptr;
+ d[0] = v >> 8;
+ d[1] = v;
+#endif
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+#if defined(__i386__) || defined(__x86_64__)
+ asm volatile ("bswap %0\n"
+ "movl %0, %1\n"
+ : "=r" (v)
+ : "m" (*(uint32_t *)ptr), "0" (v));
+#else
+ uint8_t *d = (uint8_t *) ptr;
+ d[0] = v >> 24;
+ d[1] = v >> 16;
+ d[2] = v >> 8;
+ d[3] = v;
+#endif
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+ stl_be_p(ptr, v >> 32);
+ stl_be_p((uint8_t *)ptr + 4, v);
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(const void *ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = ldl_be_p(ptr);
+ return u.f;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ stl_be_p(ptr, u.i);
+}
+
+static inline float64 ldfq_be_p(const void *ptr)
+{
+ CPU_DoubleU u;
+ u.l.upper = ldl_be_p(ptr);
+ u.l.lower = ldl_be_p((uint8_t *)ptr + 4);
+ return u.d;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+ CPU_DoubleU u;
+ u.d = v;
+ stl_be_p(ptr, u.l.upper);
+ stl_be_p((uint8_t *)ptr + 4, u.l.lower);
+}
+
+#else
+
+static inline int lduw_be_p(const void *ptr)
+{
+ return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_be_p(const void *ptr)
+{
+ return *(int16_t *)ptr;
+}
+
+static inline int ldl_be_p(const void *ptr)
+{
+ return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_be_p(const void *ptr)
+{
+ return *(uint64_t *)ptr;
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+ *(uint16_t *)ptr = v;
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+ *(uint32_t *)ptr = v;
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+ *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(const void *ptr)
+{
+ return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_be_p(const void *ptr)
+{
+ return *(float64 *)ptr;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+ *(float32 *)ptr = v;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+ *(float64 *)ptr = v;
+}
+
+#endif
+
+/* target CPU memory access functions */
+#if defined(TARGET_WORDS_BIGENDIAN)
+#define lduw_p(p) lduw_be_p(p)
+#define ldsw_p(p) ldsw_be_p(p)
+#define ldl_p(p) ldl_be_p(p)
+#define ldq_p(p) ldq_be_p(p)
+#define ldfl_p(p) ldfl_be_p(p)
+#define ldfq_p(p) ldfq_be_p(p)
+#define stw_p(p, v) stw_be_p(p, v)
+#define stl_p(p, v) stl_be_p(p, v)
+#define stq_p(p, v) stq_be_p(p, v)
+#define stfl_p(p, v) stfl_be_p(p, v)
+#define stfq_p(p, v) stfq_be_p(p, v)
+#else
+#define lduw_p(p) lduw_le_p(p)
+#define ldsw_p(p) ldsw_le_p(p)
+#define ldl_p(p) ldl_le_p(p)
+#define ldq_p(p) ldq_le_p(p)
+#define ldfl_p(p) ldfl_le_p(p)
+#define ldfq_p(p) ldfq_le_p(p)
+#define stw_p(p, v) stw_le_p(p, v)
+#define stl_p(p, v) stl_le_p(p, v)
+#define stq_p(p, v) stq_le_p(p, v)
+#define stfl_p(p, v) stfl_le_p(p, v)
+#define stfq_p(p, v) stfq_le_p(p, v)
+#endif
+
+/* MMU memory access macros */
+
+#if defined(CONFIG_USER_ONLY)
+#include <assert.h>
+#include "qemu-types.h"
+
+/* On some host systems the guest address space is reserved on the host.
+ * This allows the guest address space to be offset to a convenient location.
+ */
+#if defined(CONFIG_USE_GUEST_BASE)
+extern unsigned long guest_base;
+extern int have_guest_base;
+extern unsigned long reserved_va;
+#define GUEST_BASE guest_base
+#define RESERVED_VA reserved_va
+#else
+#define GUEST_BASE 0ul
+#define RESERVED_VA 0ul
+#endif
+
+/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
+#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
+
+#if HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS
+#define h2g_valid(x) 1
+#else
+#define h2g_valid(x) ({ \
+ unsigned long __guest = (unsigned long)(x) - GUEST_BASE; \
+ __guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS); \
+})
+#endif
+
+#define h2g(x) ({ \
+ unsigned long __ret = (unsigned long)(x) - GUEST_BASE; \
+ /* Check if given address fits target address space */ \
+ assert(h2g_valid(x)); \
+ (abi_ulong)__ret; \
+})
+
+#define saddr(x) g2h(x)
+#define laddr(x) g2h(x)
+
+#else /* !CONFIG_USER_ONLY */
+/* NOTE: we use double casts if pointers and target_ulong have
+ different sizes */
+#define saddr(x) (uint8_t *)(long)(x)
+#define laddr(x) (uint8_t *)(long)(x)
+#endif
+
+#define ldub_raw(p) ldub_p(laddr((p)))
+#define ldsb_raw(p) ldsb_p(laddr((p)))
+#define lduw_raw(p) lduw_p(laddr((p)))
+#define ldsw_raw(p) ldsw_p(laddr((p)))
+#define ldl_raw(p) ldl_p(laddr((p)))
+#define ldq_raw(p) ldq_p(laddr((p)))
+#define ldfl_raw(p) ldfl_p(laddr((p)))
+#define ldfq_raw(p) ldfq_p(laddr((p)))
+#define stb_raw(p, v) stb_p(saddr((p)), v)
+#define stw_raw(p, v) stw_p(saddr((p)), v)
+#define stl_raw(p, v) stl_p(saddr((p)), v)
+#define stq_raw(p, v) stq_p(saddr((p)), v)
+#define stfl_raw(p, v) stfl_p(saddr((p)), v)
+#define stfq_raw(p, v) stfq_p(saddr((p)), v)
+
+
+#if defined(CONFIG_USER_ONLY)
+
+/* if user mode, no other memory access functions */
+#define ldub(p) ldub_raw(p)
+#define ldsb(p) ldsb_raw(p)
+#define lduw(p) lduw_raw(p)
+#define ldsw(p) ldsw_raw(p)
+#define ldl(p) ldl_raw(p)
+#define ldq(p) ldq_raw(p)
+#define ldfl(p) ldfl_raw(p)
+#define ldfq(p) ldfq_raw(p)
+#define stb(p, v) stb_raw(p, v)
+#define stw(p, v) stw_raw(p, v)
+#define stl(p, v) stl_raw(p, v)
+#define stq(p, v) stq_raw(p, v)
+#define stfl(p, v) stfl_raw(p, v)
+#define stfq(p, v) stfq_raw(p, v)
+
+#define ldub_code(p) ldub_raw(p)
+#define ldsb_code(p) ldsb_raw(p)
+#define lduw_code(p) lduw_raw(p)
+#define ldsw_code(p) ldsw_raw(p)
+#define ldl_code(p) ldl_raw(p)
+#define ldq_code(p) ldq_raw(p)
+
+#define ldub_kernel(p) ldub_raw(p)
+#define ldsb_kernel(p) ldsb_raw(p)
+#define lduw_kernel(p) lduw_raw(p)
+#define ldsw_kernel(p) ldsw_raw(p)
+#define ldl_kernel(p) ldl_raw(p)
+#define ldq_kernel(p) ldq_raw(p)
+#define ldfl_kernel(p) ldfl_raw(p)
+#define ldfq_kernel(p) ldfq_raw(p)
+#define stb_kernel(p, v) stb_raw(p, v)
+#define stw_kernel(p, v) stw_raw(p, v)
+#define stl_kernel(p, v) stl_raw(p, v)
+#define stq_kernel(p, v) stq_raw(p, v)
+#define stfl_kernel(p, v) stfl_raw(p, v)
+#define stfq_kernel(p, vt) stfq_raw(p, v)
+
+#endif /* defined(CONFIG_USER_ONLY) */
+
+/* page related stuff */
+
+#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
+#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
+#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
+
+/* ??? These should be the larger of unsigned long and target_ulong. */
+extern unsigned long qemu_real_host_page_size;
+extern unsigned long qemu_host_page_bits;
+extern unsigned long qemu_host_page_size;
+extern unsigned long qemu_host_page_mask;
+
+#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask)
+
+/* same as PROT_xxx */
+#define PAGE_READ 0x0001
+#define PAGE_WRITE 0x0002
+#define PAGE_EXEC 0x0004
+#define PAGE_BITS (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
+#define PAGE_VALID 0x0008
+/* original state of the write flag (used when tracking self-modifying
+ code */
+#define PAGE_WRITE_ORG 0x0010
+#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
+/* FIXME: Code that sets/uses this is broken and needs to go away. */
+#define PAGE_RESERVED 0x0020
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+void page_dump(FILE *f);
+
+typedef int (*walk_memory_regions_fn)(void *, abi_ulong,
+ abi_ulong, unsigned long);
+int walk_memory_regions(void *, walk_memory_regions_fn);
+
+int page_get_flags(target_ulong address);
+void page_set_flags(target_ulong start, target_ulong end, int flags);
+int page_check_range(target_ulong start, target_ulong len, int flags);
+#endif
+
+CPUState *cpu_copy(CPUState *env);
+CPUState *qemu_get_cpu(int cpu);
+
+#define CPU_DUMP_CODE 0x00010000
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags);
+void cpu_dump_statistics(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags);
+
+void QEMU_NORETURN cpu_abort(CPUState *env, const char *fmt, ...)
+ GCC_FMT_ATTR(2, 3);
+extern CPUState *first_cpu;
+extern CPUState *cpu_single_env;
+
+#define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */
+#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */
+#define CPU_INTERRUPT_TIMER 0x08 /* internal timer exception pending */
+#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */
+#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */
+#define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */
+#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */
+#define CPU_INTERRUPT_VIRQ 0x100 /* virtual interrupt pending. */
+#define CPU_INTERRUPT_NMI 0x200 /* NMI pending. */
+#define CPU_INTERRUPT_INIT 0x400 /* INIT pending. */
+#define CPU_INTERRUPT_SIPI 0x800 /* SIPI pending. */
+#define CPU_INTERRUPT_MCE 0x1000 /* (x86 only) MCE pending. */
+
+void cpu_interrupt(CPUState *s, int mask);
+void cpu_reset_interrupt(CPUState *env, int mask);
+
+void cpu_exit(CPUState *s);
+
+int qemu_cpu_has_work(CPUState *env);
+
+/* Breakpoint/watchpoint flags */
+#define BP_MEM_READ 0x01
+#define BP_MEM_WRITE 0x02
+#define BP_MEM_ACCESS (BP_MEM_READ | BP_MEM_WRITE)
+#define BP_STOP_BEFORE_ACCESS 0x04
+#define BP_WATCHPOINT_HIT 0x08
+#define BP_GDB 0x10
+#define BP_CPU 0x20
+
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags,
+ CPUBreakpoint **breakpoint);
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags);
+void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint);
+void cpu_breakpoint_remove_all(CPUState *env, int mask);
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
+ int flags, CPUWatchpoint **watchpoint);
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr,
+ target_ulong len, int flags);
+void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint);
+void cpu_watchpoint_remove_all(CPUState *env, int mask);
+
+#define SSTEP_ENABLE 0x1 /* Enable simulated HW single stepping */
+#define SSTEP_NOIRQ 0x2 /* Do not use IRQ while single stepping */
+#define SSTEP_NOTIMER 0x4 /* Do not Timers while single stepping */
+
+void cpu_single_step(CPUState *env, int enabled);
+void cpu_reset(CPUState *s);
+int cpu_is_stopped(CPUState *env);
+void run_on_cpu(CPUState *env, void (*func)(void *data), void *data);
+
+#define CPU_LOG_TB_OUT_ASM (1 << 0)
+#define CPU_LOG_TB_IN_ASM (1 << 1)
+#define CPU_LOG_TB_OP (1 << 2)
+#define CPU_LOG_TB_OP_OPT (1 << 3)
+#define CPU_LOG_INT (1 << 4)
+#define CPU_LOG_EXEC (1 << 5)
+#define CPU_LOG_PCALL (1 << 6)
+#define CPU_LOG_IOPORT (1 << 7)
+#define CPU_LOG_TB_CPU (1 << 8)
+#define CPU_LOG_RESET (1 << 9)
+
+/* define log items */
+typedef struct CPULogItem {
+ int mask;
+ const char *name;
+ const char *help;
+} CPULogItem;
+
+extern const CPULogItem cpu_log_items[];
+
+void cpu_set_log(int log_flags);
+void cpu_set_log_filename(const char *filename);
+int cpu_str_to_log_mask(const char *str);
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* Return the physical page corresponding to a virtual one. Use it
+ only for debugging because no protection checks are done. Return -1
+ if no page found. */
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
+
+/* memory API */
+
+extern int phys_ram_fd;
+extern ram_addr_t ram_size;
+
+typedef struct RAMBlock {
+ uint8_t *host;
+ ram_addr_t offset;
+ ram_addr_t length;
+ char idstr[256];
+ QLIST_ENTRY(RAMBlock) next;
+#if defined(__linux__) && !defined(TARGET_S390X)
+ int fd;
+#endif
+} RAMBlock;
+
+typedef struct RAMList {
+ uint8_t *phys_dirty;
+ QLIST_HEAD(ram, RAMBlock) blocks;
+} RAMList;
+extern RAMList ram_list;
+
+extern const char *mem_path;
+extern int mem_prealloc;
+
+/* physical memory access */
+
+/* MMIO pages are identified by a combination of an IO device index and
+ 3 flags. The ROMD code stores the page ram offset in iotlb entry,
+ so only a limited number of ids are avaiable. */
+
+#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT))
+
+/* Flags stored in the low bits of the TLB virtual address. These are
+ defined so that fast path ram access is all zeros. */
+/* Zero if TLB entry is valid. */
+#define TLB_INVALID_MASK (1 << 3)
+/* Set if TLB entry references a clean RAM page. The iotlb entry will
+ contain the page physical address. */
+#define TLB_NOTDIRTY (1 << 4)
+/* Set if TLB entry is an IO callback. */
+#define TLB_MMIO (1 << 5)
+
+#define VGA_DIRTY_FLAG 0x01
+#define CODE_DIRTY_FLAG 0x02
+#define MIGRATION_DIRTY_FLAG 0x08
+
+/* read dirty bit (return 0 or 1) */
+static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
+{
+ return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
+}
+
+static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
+{
+ return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
+}
+
+static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
+ int dirty_flags)
+{
+ return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
+}
+
+static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
+{
+ ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
+}
+
+static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
+ int dirty_flags)
+{
+ return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
+}
+
+static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
+ int length,
+ int dirty_flags)
+{
+ int i, mask, len;
+ uint8_t *p;
+
+ len = length >> TARGET_PAGE_BITS;
+ mask = ~dirty_flags;
+ p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS);
+ for (i = 0; i < len; i++) {
+ p[i] &= mask;
+ }
+}
+
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
+ int dirty_flags);
+void cpu_tlb_update_dirty(CPUState *env);
+
+int cpu_physical_memory_set_dirty_tracking(int enable);
+
+int cpu_physical_memory_get_dirty_tracking(void);
+
+int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr);
+
+void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
+#endif /* !CONFIG_USER_ONLY */
+
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+ uint8_t *buf, int len, int is_write);
+
+void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc,
+ int broadcast);
+
+#endif /* CPU_ALL_H */
diff --git a/cpu-common.h b/cpu-common.h
new file mode 100644
index 0000000..aef2979
--- /dev/null
+++ b/cpu-common.h
@@ -0,0 +1,144 @@
+#ifndef CPU_COMMON_H
+#define CPU_COMMON_H 1
+
+/* CPU interfaces that are target indpendent. */
+
+#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__)
+#define WORDS_ALIGNED
+#endif
+
+#ifdef TARGET_PHYS_ADDR_BITS
+#include "targphys.h"
+#endif
+
+#ifndef NEED_CPU_H
+#include "poison.h"
+#endif
+
+#include "bswap.h"
+#include "qemu-queue.h"
+
+#if !defined(CONFIG_USER_ONLY)
+
+enum device_endian {
+ DEVICE_NATIVE_ENDIAN,
+ DEVICE_BIG_ENDIAN,
+ DEVICE_LITTLE_ENDIAN,
+};
+
+/* address in the RAM (different from a physical address) */
+typedef unsigned long ram_addr_t;
+
+/* memory API */
+
+typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
+typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
+
+void cpu_register_physical_memory_offset(target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset,
+ ram_addr_t region_offset);
+static inline void cpu_register_physical_memory(target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset)
+{
+ cpu_register_physical_memory_offset(start_addr, size, phys_offset, 0);
+}
+
+ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
+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_unmap(ram_addr_t addr);
+void qemu_ram_free(ram_addr_t addr);
+/* This should only be used for ram local to a device. */
+void *qemu_get_ram_ptr(ram_addr_t addr);
+/* Same but slower, to use for migration, where the order of
+ * RAMBlocks must not change. */
+void *qemu_safe_ram_ptr(ram_addr_t addr);
+/* This should not be used by devices. */
+int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr);
+ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr);
+
+int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write,
+ void *opaque, enum device_endian endian);
+void cpu_unregister_io_memory(int table_address);
+
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+ int len, int is_write);
+static inline void cpu_physical_memory_read(target_phys_addr_t addr,
+ uint8_t *buf, int len)
+{
+ cpu_physical_memory_rw(addr, buf, len, 0);
+}
+static inline void cpu_physical_memory_write(target_phys_addr_t addr,
+ const uint8_t *buf, int len)
+{
+ cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
+}
+void *cpu_physical_memory_map(target_phys_addr_t addr,
+ target_phys_addr_t *plen,
+ int is_write);
+void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
+ int is_write, target_phys_addr_t access_len);
+void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
+void cpu_unregister_map_client(void *cookie);
+
+struct CPUPhysMemoryClient;
+typedef struct CPUPhysMemoryClient CPUPhysMemoryClient;
+struct CPUPhysMemoryClient {
+ void (*set_memory)(struct CPUPhysMemoryClient *client,
+ target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset);
+ int (*sync_dirty_bitmap)(struct CPUPhysMemoryClient *client,
+ target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr);
+ int (*migration_log)(struct CPUPhysMemoryClient *client,
+ int enable);
+ QLIST_ENTRY(CPUPhysMemoryClient) list;
+};
+
+void cpu_register_phys_memory_client(CPUPhysMemoryClient *);
+void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *);
+
+/* Coalesced MMIO regions are areas where write operations can be reordered.
+ * This usually implies that write operations are side-effect free. This allows
+ * batching which can make a major impact on performance when using
+ * virtualization.
+ */
+void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
+
+void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
+
+void qemu_flush_coalesced_mmio_buffer(void);
+
+uint32_t ldub_phys(target_phys_addr_t addr);
+uint32_t lduw_phys(target_phys_addr_t addr);
+uint32_t ldl_phys(target_phys_addr_t addr);
+uint64_t ldq_phys(target_phys_addr_t addr);
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
+void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
+void stb_phys(target_phys_addr_t addr, uint32_t val);
+void stw_phys(target_phys_addr_t addr, uint32_t val);
+void stl_phys(target_phys_addr_t addr, uint32_t val);
+void stq_phys(target_phys_addr_t addr, uint64_t val);
+
+void cpu_physical_memory_write_rom(target_phys_addr_t addr,
+ const uint8_t *buf, int len);
+
+#define IO_MEM_SHIFT 3
+
+#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */
+#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */
+#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT)
+#define IO_MEM_NOTDIRTY (3 << IO_MEM_SHIFT)
+
+/* Acts like a ROM when read and like a device when written. */
+#define IO_MEM_ROMD (1)
+#define IO_MEM_SUBPAGE (2)
+
+#endif
+
+#endif /* !CPU_COMMON_H */
diff --git a/cpu-defs.h b/cpu-defs.h
new file mode 100644
index 0000000..51533c6
--- /dev/null
+++ b/cpu-defs.h
@@ -0,0 +1,227 @@
+/*
+ * common defines for all CPUs
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#ifndef CPU_DEFS_H
+#define CPU_DEFS_H
+
+#ifndef NEED_CPU_H
+#error cpu.h included from common code
+#endif
+
+#include "config.h"
+#include <setjmp.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <pthread.h>
+#include "osdep.h"
+#include "qemu-queue.h"
+#include "targphys.h"
+
+#ifndef TARGET_LONG_BITS
+#error TARGET_LONG_BITS must be defined before including this header
+#endif
+
+#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
+
+/* target_ulong is the type of a virtual address */
+#if TARGET_LONG_SIZE == 4
+typedef int32_t target_long;
+typedef uint32_t target_ulong;
+#define TARGET_FMT_lx "%08x"
+#define TARGET_FMT_ld "%d"
+#define TARGET_FMT_lu "%u"
+#elif TARGET_LONG_SIZE == 8
+typedef int64_t target_long;
+typedef uint64_t target_ulong;
+#define TARGET_FMT_lx "%016" PRIx64
+#define TARGET_FMT_ld "%" PRId64
+#define TARGET_FMT_lu "%" PRIu64
+#else
+#error TARGET_LONG_SIZE undefined
+#endif
+
+#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
+
+#define EXCP_INTERRUPT 0x10000 /* async interruption */
+#define EXCP_HLT 0x10001 /* hlt instruction reached */
+#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
+#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
+
+#define TB_JMP_CACHE_BITS 12
+#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
+
+/* Only the bottom TB_JMP_PAGE_BITS of the jump cache hash bits vary for
+ addresses on the same page. The top bits are the same. This allows
+ TLB invalidation to quickly clear a subset of the hash table. */
+#define TB_JMP_PAGE_BITS (TB_JMP_CACHE_BITS / 2)
+#define TB_JMP_PAGE_SIZE (1 << TB_JMP_PAGE_BITS)
+#define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1)
+#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
+
+#if !defined(CONFIG_USER_ONLY)
+#define CPU_TLB_BITS 8
+#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
+
+#if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32
+#define CPU_TLB_ENTRY_BITS 4
+#else
+#define CPU_TLB_ENTRY_BITS 5
+#endif
+
+typedef struct CPUTLBEntry {
+ /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address
+ bit TARGET_PAGE_BITS-1..4 : Nonzero for accesses that should not
+ go directly to ram.
+ bit 3 : indicates that the entry is invalid
+ bit 2..0 : zero
+ */
+ target_ulong addr_read;
+ target_ulong addr_write;
+ target_ulong addr_code;
+ /* Addend to virtual address to get host address. IO accesses
+ use the corresponding iotlb value. */
+ unsigned long addend;
+ /* padding to get a power of two size */
+ uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) -
+ (sizeof(target_ulong) * 3 +
+ ((-sizeof(target_ulong) * 3) & (sizeof(unsigned long) - 1)) +
+ sizeof(unsigned long))];
+} CPUTLBEntry;
+
+extern int CPUTLBEntry_wrong_size[sizeof(CPUTLBEntry) == (1 << CPU_TLB_ENTRY_BITS) ? 1 : -1];
+
+#define CPU_COMMON_TLB \
+ /* The meaning of the MMU modes is defined in the target code. */ \
+ CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \
+ target_phys_addr_t iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \
+ target_ulong tlb_flush_addr; \
+ target_ulong tlb_flush_mask;
+
+#else
+
+#define CPU_COMMON_TLB
+
+#endif
+
+
+#ifdef HOST_WORDS_BIGENDIAN
+typedef struct icount_decr_u16 {
+ uint16_t high;
+ uint16_t low;
+} icount_decr_u16;
+#else
+typedef struct icount_decr_u16 {
+ uint16_t low;
+ uint16_t high;
+} icount_decr_u16;
+#endif
+
+struct kvm_run;
+struct KVMState;
+struct qemu_work_item;
+
+typedef struct CPUBreakpoint {
+ target_ulong pc;
+ int flags; /* BP_* */
+ QTAILQ_ENTRY(CPUBreakpoint) entry;
+} CPUBreakpoint;
+
+typedef struct CPUWatchpoint {
+ target_ulong vaddr;
+ target_ulong len_mask;
+ int flags; /* BP_* */
+ QTAILQ_ENTRY(CPUWatchpoint) entry;
+} CPUWatchpoint;
+
+/* forward decleration */
+struct qemu_work_item;
+
+struct KVMCPUState {
+ pthread_t thread;
+ int signalled;
+ struct qemu_work_item *queued_work_first, *queued_work_last;
+};
+
+#define CPU_TEMP_BUF_NLONGS 128
+#define CPU_COMMON \
+ struct TranslationBlock *current_tb; /* currently executing TB */ \
+ /* soft mmu support */ \
+ /* in order to avoid passing too many arguments to the MMIO \
+ helpers, we store some rarely used information in the CPU \
+ context) */ \
+ unsigned long mem_io_pc; /* host pc at which the memory was \
+ accessed */ \
+ target_ulong mem_io_vaddr; /* target virtual addr at which the \
+ memory was accessed */ \
+ uint32_t halted; /* Nonzero if the CPU is in suspend state */ \
+ uint32_t interrupt_request; \
+ volatile sig_atomic_t exit_request; \
+ CPU_COMMON_TLB \
+ struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \
+ /* buffer for temporaries in the code generator */ \
+ long temp_buf[CPU_TEMP_BUF_NLONGS]; \
+ \
+ int64_t icount_extra; /* Instructions until next timer event. */ \
+ /* Number of cycles left, with interrupt flag in high bit. \
+ This allows a single read-compare-cbranch-write sequence to test \
+ for both decrementer underflow and exceptions. */ \
+ union { \
+ uint32_t u32; \
+ icount_decr_u16 u16; \
+ } icount_decr; \
+ uint32_t can_do_io; /* nonzero if memory mapped IO is safe. */ \
+ \
+ /* from this point: preserved by CPU reset */ \
+ /* ice debug support */ \
+ QTAILQ_HEAD(breakpoints_head, CPUBreakpoint) breakpoints; \
+ int singlestep_enabled; \
+ \
+ QTAILQ_HEAD(watchpoints_head, CPUWatchpoint) watchpoints; \
+ CPUWatchpoint *watchpoint_hit; \
+ \
+ struct GDBRegisterState *gdb_regs; \
+ \
+ /* Core interrupt code */ \
+ jmp_buf jmp_env; \
+ int exception_index; \
+ \
+ CPUState *next_cpu; /* next CPU sharing TB cache */ \
+ int cpu_index; /* CPU index (informative) */ \
+ uint32_t host_tid; /* host thread ID */ \
+ int numa_node; /* NUMA node this cpu is belonging to */ \
+ int nr_cores; /* number of cores within this CPU package */ \
+ int nr_threads;/* number of threads within this CPU */ \
+ int running; /* Nonzero if cpu is currently running(usermode). */ \
+ int thread_id; \
+ /* user data */ \
+ void *opaque; \
+ \
+ uint32_t created; \
+ uint32_t stop; /* Stop request */ \
+ uint32_t stopped; /* Artificially stopped */ \
+ struct QemuThread *thread; \
+ struct QemuCond *halt_cond; \
+ struct qemu_work_item *queued_work_first, *queued_work_last; \
+ const char *cpu_model_str; \
+ struct KVMState *kvm_state; \
+ struct kvm_run *kvm_run; \
+ int kvm_fd; \
+ int kvm_vcpu_dirty; \
+ struct KVMCPUState kvm_cpu_state;
+
+#endif
diff --git a/cpu-exec.c b/cpu-exec.c
new file mode 100644
index 0000000..27b6a7c
--- /dev/null
+++ b/cpu-exec.c
@@ -0,0 +1,1279 @@
+/*
+ * i386 emulator main execution loop
+ *
+ * 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"
+#if !defined(TARGET_IA64)
+#include "tcg.h"
+#endif
+#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
+
+#include "qemu-kvm.h"
+
+#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
+// Work around ugly bugs in glibc that mangle global register contents
+#undef env
+#define env cpu_single_env
+#endif
+
+int tb_invalidated_flag;
+
+//#define CONFIG_DEBUG_EXEC
+//#define DEBUG_SIGNAL
+
+int qemu_cpu_has_work(CPUState *env)
+{
+ return cpu_has_work(env);
+}
+
+void cpu_loop_exit(void)
+{
+ env->current_tb = NULL;
+ longjmp(env->jmp_env, 1);
+}
+
+/* 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)
+{
+#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__
+#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
+ }
+#endif
+ env->exception_index = -1;
+ longjmp(env->jmp_env, 1);
+}
+
+/* Execute the code without caching the generated code. An interpreter
+ could be used if available. */
+static void cpu_exec_nocache(int max_cycles, TranslationBlock *orig_tb)
+{
+ unsigned long next_tb;
+ TranslationBlock *tb;
+
+ /* Should never happen.
+ We only end up here when an existing TB is too long. */
+ if (max_cycles > CF_COUNT_MASK)
+ max_cycles = CF_COUNT_MASK;
+
+ tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
+ max_cycles);
+ env->current_tb = tb;
+ /* execute the generated code */
+ next_tb = tcg_qemu_tb_exec(tb->tc_ptr);
+ env->current_tb = NULL;
+
+ if ((next_tb & 3) == 2) {
+ /* Restore PC. This may happen if async event occurs before
+ the TB starts executing. */
+ cpu_pc_from_tb(env, tb);
+ }
+ tb_phys_invalidate(tb, -1);
+ tb_free(tb);
+}
+
+static TranslationBlock *tb_find_slow(target_ulong pc,
+ target_ulong cs_base,
+ uint64_t flags)
+{
+ TranslationBlock *tb, **ptb1;
+ unsigned int h;
+ tb_page_addr_t phys_pc, phys_page1, phys_page2;
+ target_ulong virt_page2;
+
+ tb_invalidated_flag = 0;
+
+ /* find translated block using physical mappings */
+ phys_pc = get_page_addr_code(env, pc);
+ phys_page1 = phys_pc & TARGET_PAGE_MASK;
+ phys_page2 = -1;
+ h = tb_phys_hash_func(phys_pc);
+ ptb1 = &tb_phys_hash[h];
+ for(;;) {
+ tb = *ptb1;
+ if (!tb)
+ goto not_found;
+ if (tb->pc == pc &&
+ tb->page_addr[0] == phys_page1 &&
+ tb->cs_base == cs_base &&
+ tb->flags == flags) {
+ /* check next page if needed */
+ if (tb->page_addr[1] != -1) {
+ virt_page2 = (pc & TARGET_PAGE_MASK) +
+ TARGET_PAGE_SIZE;
+ phys_page2 = get_page_addr_code(env, virt_page2);
+ if (tb->page_addr[1] == phys_page2)
+ goto found;
+ } else {
+ goto found;
+ }
+ }
+ ptb1 = &tb->phys_hash_next;
+ }
+ not_found:
+ /* if no translated code available, then translate it now */
+ tb = tb_gen_code(env, pc, cs_base, flags, 0);
+
+ found:
+ /* Move the last found TB to the head of the list */
+ if (likely(*ptb1)) {
+ *ptb1 = tb->phys_hash_next;
+ tb->phys_hash_next = tb_phys_hash[h];
+ tb_phys_hash[h] = tb;
+ }
+ /* we add the TB in the virtual pc hash table */
+ env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
+ return tb;
+}
+
+static inline TranslationBlock *tb_find_fast(void)
+{
+ TranslationBlock *tb;
+ target_ulong cs_base, pc;
+ int flags;
+
+ /* we record a subset of the CPU state. It will
+ always be the same before a given translated block
+ is executed. */
+ cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
+ tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
+ if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base ||
+ tb->flags != flags)) {
+ tb = tb_find_slow(pc, cs_base, flags);
+ }
+ return tb;
+}
+
+static CPUDebugExcpHandler *debug_excp_handler;
+
+CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
+{
+ CPUDebugExcpHandler *old_handler = debug_excp_handler;
+
+ debug_excp_handler = handler;
+ return old_handler;
+}
+
+static void cpu_handle_debug_exception(CPUState *env)
+{
+ CPUWatchpoint *wp;
+
+ if (!env->watchpoint_hit)
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry)
+ wp->flags &= ~BP_WATCHPOINT_HIT;
+
+ if (debug_excp_handler)
+ debug_excp_handler(env);
+}
+
+/* main execution loop */
+
+volatile sig_atomic_t exit_request;
+
+int cpu_exec(CPUState *env1)
+{
+ volatile host_reg_t saved_env_reg;
+ int ret, interrupt_request;
+ TranslationBlock *tb;
+ uint8_t *tc_ptr;
+ unsigned long next_tb;
+
+ if (cpu_halted(env1) == EXCP_HALTED)
+ return EXCP_HALTED;
+
+ cpu_single_env = env1;
+
+ /* the access to env below is actually saving the global register's
+ value, so that files not including target-xyz/exec.h are free to
+ use it. */
+ QEMU_BUILD_BUG_ON (sizeof (saved_env_reg) != sizeof (env));
+ saved_env_reg = (host_reg_t) env;
+ barrier();
+ env = env1;
+
+ if (unlikely(exit_request)) {
+ env->exit_request = 1;
+ }
+
+#if defined(TARGET_I386)
+ if (!kvm_enabled()) {
+ /* put eflags in CPU temporary format */
+ CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((env->eflags >> 10) & 1));
+ CC_OP = CC_OP_EFLAGS;
+ env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ }
+#elif defined(TARGET_SPARC)
+#elif defined(TARGET_M68K)
+ env->cc_op = CC_OP_FLAGS;
+ env->cc_dest = env->sr & 0xf;
+ env->cc_x = (env->sr >> 4) & 1;
+#elif defined(TARGET_ALPHA)
+#elif defined(TARGET_ARM)
+#elif defined(TARGET_PPC)
+#elif defined(TARGET_MICROBLAZE)
+#elif defined(TARGET_MIPS)
+#elif defined(TARGET_SH4)
+#elif defined(TARGET_CRIS)
+#elif defined(TARGET_S390X)
+#elif defined(TARGET_IA64)
+ /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+ env->exception_index = -1;
+
+ /* prepare setjmp context for exception handling */
+ for(;;) {
+ if (setjmp(env->jmp_env) == 0) {
+#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
+#undef env
+ env = cpu_single_env;
+#define env cpu_single_env
+#endif
+ /* if an exception is pending, we execute it here */
+ if (env->exception_index >= 0) {
+ if (env->exception_index >= EXCP_INTERRUPT) {
+ /* exit request from the cpu execution loop */
+ ret = env->exception_index;
+ if (ret == EXCP_DEBUG)
+ cpu_handle_debug_exception(env);
+ break;
+ } else {
+#if defined(CONFIG_USER_ONLY)
+ /* if user mode only, we simulate a fake exception
+ which will be handled outside the cpu execution
+ loop */
+#if defined(TARGET_I386)
+ do_interrupt_user(env->exception_index,
+ env->exception_is_int,
+ env->error_code,
+ env->exception_next_eip);
+ /* successfully delivered */
+ env->old_exception = -1;
+#endif
+ ret = env->exception_index;
+ break;
+#else
+#if defined(TARGET_I386)
+ /* simulate a real cpu exception. On i386, it can
+ trigger new exceptions, but we do not handle
+ double or triple faults yet. */
+ do_interrupt(env->exception_index,
+ env->exception_is_int,
+ env->error_code,
+ env->exception_next_eip, 0);
+ /* successfully delivered */
+ env->old_exception = -1;
+#elif defined(TARGET_PPC)
+ do_interrupt(env);
+#elif defined(TARGET_MICROBLAZE)
+ do_interrupt(env);
+#elif defined(TARGET_MIPS)
+ do_interrupt(env);
+#elif defined(TARGET_SPARC)
+ do_interrupt(env);
+#elif defined(TARGET_ARM)
+ do_interrupt(env);
+#elif defined(TARGET_SH4)
+ do_interrupt(env);
+#elif defined(TARGET_ALPHA)
+ do_interrupt(env);
+#elif defined(TARGET_CRIS)
+ do_interrupt(env);
+#elif defined(TARGET_M68K)
+ do_interrupt(0);
+#elif defined(TARGET_IA64)
+ do_interrupt(env);
+#endif
+ env->exception_index = -1;
+#endif
+ }
+ }
+
+ if (kvm_enabled()) {
+ kvm_cpu_exec(env);
+ longjmp(env->jmp_env, 1);
+ }
+
+ next_tb = 0; /* force lookup of first TB */
+ for(;;) {
+ interrupt_request = env->interrupt_request;
+ if (unlikely(interrupt_request)) {
+ if (unlikely(env->singlestep_enabled & SSTEP_NOIRQ)) {
+ /* Mask out external interrupts for this step. */
+ interrupt_request &= ~(CPU_INTERRUPT_HARD |
+ CPU_INTERRUPT_FIQ |
+ CPU_INTERRUPT_SMI |
+ CPU_INTERRUPT_NMI);
+ }
+ if (interrupt_request & CPU_INTERRUPT_DEBUG) {
+ env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+ }
+#if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \
+ defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \
+ defined(TARGET_MICROBLAZE)
+ if (interrupt_request & CPU_INTERRUPT_HALT) {
+ env->interrupt_request &= ~CPU_INTERRUPT_HALT;
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ cpu_loop_exit();
+ }
+#endif
+#if defined(TARGET_I386)
+ if (interrupt_request & CPU_INTERRUPT_INIT) {
+ svm_check_intercept(SVM_EXIT_INIT);
+ do_cpu_init(env);
+ env->exception_index = EXCP_HALTED;
+ cpu_loop_exit();
+ } else if (interrupt_request & CPU_INTERRUPT_SIPI) {
+ do_cpu_sipi(env);
+ } else if (env->hflags2 & HF2_GIF_MASK) {
+ if ((interrupt_request & CPU_INTERRUPT_SMI) &&
+ !(env->hflags & HF_SMM_MASK)) {
+ svm_check_intercept(SVM_EXIT_SMI);
+ env->interrupt_request &= ~CPU_INTERRUPT_SMI;
+ do_smm_enter();
+ next_tb = 0;
+ } else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
+ !(env->hflags2 & HF2_NMI_MASK)) {
+ env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ env->hflags2 |= HF2_NMI_MASK;
+ do_interrupt(EXCP02_NMI, 0, 0, 0, 1);
+ next_tb = 0;
+ } else if (interrupt_request & CPU_INTERRUPT_MCE) {
+ env->interrupt_request &= ~CPU_INTERRUPT_MCE;
+ do_interrupt(EXCP12_MCHK, 0, 0, 0, 0);
+ next_tb = 0;
+ } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+ (((env->hflags2 & HF2_VINTR_MASK) &&
+ (env->hflags2 & HF2_HIF_MASK)) ||
+ (!(env->hflags2 & HF2_VINTR_MASK) &&
+ (env->eflags & IF_MASK &&
+ !(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
+ int intno;
+ svm_check_intercept(SVM_EXIT_INTR);
+ env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ);
+ intno = cpu_get_pic_interrupt(env);
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing hardware INT=0x%02x\n", intno);
+#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
+#undef env
+ env = cpu_single_env;
+#define env cpu_single_env
+#endif
+ do_interrupt(intno, 0, 0, 0, 1);
+ /* ensure that no TB jump will be modified as
+ the program flow was changed */
+ next_tb = 0;
+#if !defined(CONFIG_USER_ONLY)
+ } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+ (env->eflags & IF_MASK) &&
+ !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+ int intno;
+ /* FIXME: this should respect TPR */
+ svm_check_intercept(SVM_EXIT_VINTR);
+ intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "Servicing virtual hardware INT=0x%02x\n", intno);
+ do_interrupt(intno, 0, 0, 0, 1);
+ env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+ next_tb = 0;
+#endif
+ }
+ }
+#elif defined(TARGET_PPC)
+#if 0
+ if ((interrupt_request & CPU_INTERRUPT_RESET)) {
+ cpu_reset(env);
+ }
+#endif
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ ppc_hw_interrupt(env);
+ if (env->pending_interrupts == 0)
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ next_tb = 0;
+ }
+#elif defined(TARGET_MICROBLAZE)
+ if ((interrupt_request & CPU_INTERRUPT_HARD)
+ && (env->sregs[SR_MSR] & MSR_IE)
+ && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))
+ && !(env->iflags & (D_FLAG | IMM_FLAG))) {
+ env->exception_index = EXCP_IRQ;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+#elif defined(TARGET_MIPS)
+ if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+ cpu_mips_hw_interrupts_pending(env)) {
+ /* Raise it */
+ env->exception_index = EXCP_EXT_INTERRUPT;
+ env->error_code = 0;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+#elif defined(TARGET_SPARC)
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ if (cpu_interrupts_enabled(env) &&
+ env->interrupt_index > 0) {
+ int pil = env->interrupt_index & 0xf;
+ int type = env->interrupt_index & 0xf0;
+
+ if (((type == TT_EXTINT) &&
+ cpu_pil_allowed(env, pil)) ||
+ type != TT_EXTINT) {
+ env->exception_index = env->interrupt_index;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+ }
+ } else if (interrupt_request & CPU_INTERRUPT_TIMER) {
+ //do_interrupt(0, 0, 0, 0, 0);
+ env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
+ }
+#elif defined(TARGET_ARM)
+ if (interrupt_request & CPU_INTERRUPT_FIQ
+ && !(env->uncached_cpsr & CPSR_F)) {
+ env->exception_index = EXCP_FIQ;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+ /* ARMv7-M interrupt return works by loading a magic value
+ into the PC. On real hardware the load causes the
+ return to occur. The qemu implementation performs the
+ jump normally, then does the exception return when the
+ CPU tries to execute code at the magic address.
+ This will cause the magic PC value to be pushed to
+ the stack if an interrupt occured at the wrong time.
+ We avoid this by disabling interrupts when
+ pc contains a magic address. */
+ if (interrupt_request & CPU_INTERRUPT_HARD
+ && ((IS_M(env) && env->regs[15] < 0xfffffff0)
+ || !(env->uncached_cpsr & CPSR_I))) {
+ env->exception_index = EXCP_IRQ;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+#elif defined(TARGET_SH4)
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ do_interrupt(env);
+ next_tb = 0;
+ }
+#elif defined(TARGET_ALPHA)
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ do_interrupt(env);
+ next_tb = 0;
+ }
+#elif defined(TARGET_CRIS)
+ if (interrupt_request & CPU_INTERRUPT_HARD
+ && (env->pregs[PR_CCS] & I_FLAG)
+ && !env->locked_irq) {
+ env->exception_index = EXCP_IRQ;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+ if (interrupt_request & CPU_INTERRUPT_NMI
+ && (env->pregs[PR_CCS] & M_FLAG)) {
+ env->exception_index = EXCP_NMI;
+ do_interrupt(env);
+ next_tb = 0;
+ }
+#elif defined(TARGET_M68K)
+ if (interrupt_request & CPU_INTERRUPT_HARD
+ && ((env->sr & SR_I) >> SR_I_SHIFT)
+ < env->pending_level) {
+ /* Real hardware gets the interrupt vector via an
+ IACK cycle at this point. Current emulated
+ hardware doesn't rely on this, so we
+ provide/save the vector when the interrupt is
+ first signalled. */
+ env->exception_index = env->pending_vector;
+ do_interrupt(1);
+ next_tb = 0;
+ }
+#endif
+ /* Don't use the cached interupt_request value,
+ do_interrupt may have updated the EXITTB flag. */
+ if (env->interrupt_request & CPU_INTERRUPT_EXITTB) {
+ env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
+ /* ensure that no TB jump will be modified as
+ the program flow was changed */
+ next_tb = 0;
+ }
+ }
+ if (unlikely(env->exit_request)) {
+ env->exit_request = 0;
+ env->exception_index = EXCP_INTERRUPT;
+ cpu_loop_exit();
+ }
+#if defined(DEBUG_DISAS) || defined(CONFIG_DEBUG_EXEC)
+ if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
+ /* restore flags in standard format */
+#if defined(TARGET_I386)
+ env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
+ log_cpu_state(env, X86_DUMP_CCOP);
+ env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+#elif defined(TARGET_M68K)
+ cpu_m68k_flush_flags(env, env->cc_op);
+ env->cc_op = CC_OP_FLAGS;
+ env->sr = (env->sr & 0xffe0)
+ | env->cc_dest | (env->cc_x << 4);
+ log_cpu_state(env, 0);
+#else
+ log_cpu_state(env, 0);
+#endif
+ }
+#endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */
+ spin_lock(&tb_lock);
+ tb = tb_find_fast();
+ /* Note: we do it here to avoid a gcc bug on Mac OS X when
+ doing it in tb_find_slow */
+ if (tb_invalidated_flag) {
+ /* as some TB could have been invalidated because
+ of memory exceptions while generating the code, we
+ must recompute the hash index here */
+ next_tb = 0;
+ tb_invalidated_flag = 0;
+ }
+#ifdef CONFIG_DEBUG_EXEC
+ qemu_log_mask(CPU_LOG_EXEC, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n",
+ (long)tb->tc_ptr, tb->pc,
+ lookup_symbol(tb->pc));
+#endif
+ /* see if we can patch the calling TB. When the TB
+ spans two pages, we cannot safely do a direct
+ jump. */
+ if (next_tb != 0 && tb->page_addr[1] == -1) {
+ tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb);
+ }
+ spin_unlock(&tb_lock);
+
+ /* cpu_interrupt might be called while translating the
+ TB, but before it is linked into a potentially
+ infinite loop and becomes env->current_tb. Avoid
+ starting execution if there is a pending interrupt. */
+ env->current_tb = tb;
+ barrier();
+ if (likely(!env->exit_request)) {
+ tc_ptr = tb->tc_ptr;
+ /* execute the generated code */
+#if defined(__sparc__) && !defined(CONFIG_SOLARIS)
+#undef env
+ env = cpu_single_env;
+#define env cpu_single_env
+#endif
+ next_tb = tcg_qemu_tb_exec(tc_ptr);
+ if ((next_tb & 3) == 2) {
+ /* Instruction counter expired. */
+ int insns_left;
+ tb = (TranslationBlock *)(long)(next_tb & ~3);
+ /* Restore PC. */
+ cpu_pc_from_tb(env, tb);
+ insns_left = env->icount_decr.u32;
+ if (env->icount_extra && insns_left >= 0) {
+ /* Refill decrementer and continue execution. */
+ env->icount_extra += insns_left;
+ if (env->icount_extra > 0xffff) {
+ insns_left = 0xffff;
+ } else {
+ insns_left = env->icount_extra;
+ }
+ env->icount_extra -= insns_left;
+ env->icount_decr.u16.low = insns_left;
+ } else {
+ if (insns_left > 0) {
+ /* Execute remaining instructions. */
+ cpu_exec_nocache(insns_left, tb);
+ }
+ env->exception_index = EXCP_INTERRUPT;
+ next_tb = 0;
+ cpu_loop_exit();
+ }
+ }
+ }
+ env->current_tb = NULL;
+ /* reset soft MMU for next block (it can currently
+ only be set by a memory fault) */
+ } /* for(;;) */
+ }
+ } /* for(;;) */
+
+
+#if defined(TARGET_I386)
+ /* restore flags in standard format */
+ env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
+#elif defined(TARGET_ARM)
+ /* XXX: Save/restore host fpu exception state?. */
+#elif defined(TARGET_SPARC)
+#elif defined(TARGET_PPC)
+#elif defined(TARGET_M68K)
+ cpu_m68k_flush_flags(env, env->cc_op);
+ env->cc_op = CC_OP_FLAGS;
+ env->sr = (env->sr & 0xffe0)
+ | env->cc_dest | (env->cc_x << 4);
+#elif defined(TARGET_MICROBLAZE)
+#elif defined(TARGET_MIPS)
+#elif defined(TARGET_SH4)
+#elif defined(TARGET_IA64)
+#elif defined(TARGET_ALPHA)
+#elif defined(TARGET_CRIS)
+#elif defined(TARGET_S390X)
+ /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+
+ /* restore global registers */
+ barrier();
+ env = (void *) saved_env_reg;
+
+ /* fail safe : never use cpu_single_env outside cpu_exec() */
+ cpu_single_env = NULL;
+ 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)
+{
+ 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, puc);
+ }
+
+ /* 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)
+# 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 */
+/* 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)
+# 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 */
+/* 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 */
+# 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 */
+#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/cpus.c b/cpus.c
new file mode 100644
index 0000000..c0f9210
--- /dev/null
+++ b/cpus.c
@@ -0,0 +1,991 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Needed early for CONFIG_BSD etc. */
+#include "config-host.h"
+
+#include "monitor.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#include "dma.h"
+#include "kvm.h"
+#include "exec-all.h"
+
+#include "cpus.h"
+#include "compatfd.h"
+#ifdef CONFIG_LINUX
+#include <sys/prctl.h>
+#endif
+
+#ifdef SIGRTMIN
+#define SIG_IPI (SIGRTMIN+4)
+#else
+#define SIG_IPI SIGUSR1
+#endif
+
+#ifndef PR_MCE_KILL
+#define PR_MCE_KILL 33
+#endif
+
+static CPUState *next_cpu;
+
+/***********************************************************/
+void hw_error(const char *fmt, ...)
+{
+ va_list ap;
+ CPUState *env;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "qemu: hardware error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ fprintf(stderr, "CPU #%d:\n", env->cpu_index);
+#ifdef TARGET_I386
+ cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU);
+#else
+ cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+ }
+ va_end(ap);
+ abort();
+}
+
+void cpu_synchronize_all_states(void)
+{
+ CPUState *cpu;
+
+ for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
+ cpu_synchronize_state(cpu);
+ }
+}
+
+void cpu_synchronize_all_post_reset(void)
+{
+ CPUState *cpu;
+
+ for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
+ cpu_synchronize_post_reset(cpu);
+ }
+}
+
+void cpu_synchronize_all_post_init(void)
+{
+ CPUState *cpu;
+
+ for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
+ cpu_synchronize_post_init(cpu);
+ }
+}
+
+int cpu_is_stopped(CPUState *env)
+{
+ return !vm_running || env->stopped;
+}
+
+static void do_vm_stop(int reason)
+{
+ if (vm_running) {
+ cpu_disable_ticks();
+ vm_running = 0;
+ pause_all_vcpus();
+ vm_state_notify(0, reason);
+ qemu_aio_flush();
+ bdrv_flush_all();
+ monitor_protocol_event(QEVENT_STOP, NULL);
+ }
+}
+
+static int cpu_can_run(CPUState *env)
+{
+ if (env->stop)
+ return 0;
+ if (env->stopped || !vm_running)
+ return 0;
+ return 1;
+}
+
+static int cpu_has_work(CPUState *env)
+{
+ if (env->stop)
+ return 1;
+ if (env->queued_work_first)
+ return 1;
+ if (env->stopped || !vm_running)
+ return 0;
+ if (!env->halted)
+ return 1;
+ if (qemu_cpu_has_work(env))
+ return 1;
+ return 0;
+}
+
+static int any_cpu_has_work(void)
+{
+ CPUState *env;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ if (cpu_has_work(env))
+ return 1;
+ return 0;
+}
+
+static void cpu_debug_handler(CPUState *env)
+{
+ gdb_set_stop_cpu(env);
+ debug_requested = EXCP_DEBUG;
+ vm_stop(EXCP_DEBUG);
+}
+
+#ifndef _WIN32
+static int io_thread_fd = -1;
+
+static void qemu_event_increment(void)
+{
+ /* Write 8 bytes to be compatible with eventfd. */
+ static const uint64_t val = 1;
+ ssize_t ret;
+
+ if (io_thread_fd == -1)
+ return;
+
+ do {
+ ret = write(io_thread_fd, &val, sizeof(val));
+ } while (ret < 0 && errno == EINTR);
+
+ /* EAGAIN is fine, a read must be pending. */
+ if (ret < 0 && errno != EAGAIN) {
+ fprintf(stderr, "qemu_event_increment: write() filed: %s\n",
+ strerror(errno));
+ exit (1);
+ }
+}
+
+static void qemu_event_read(void *opaque)
+{
+ int fd = (unsigned long)opaque;
+ ssize_t len;
+ char buffer[512];
+
+ /* Drain the notify pipe. For eventfd, only 8 bytes will be read. */
+ do {
+ len = read(fd, buffer, sizeof(buffer));
+ } while ((len == -1 && errno == EINTR) || len == sizeof(buffer));
+}
+
+static int qemu_event_init(void)
+{
+ int err;
+ int fds[2];
+
+ err = qemu_eventfd(fds);
+ if (err == -1)
+ return -errno;
+
+ err = fcntl_setfl(fds[0], O_NONBLOCK);
+ if (err < 0)
+ goto fail;
+
+ err = fcntl_setfl(fds[1], O_NONBLOCK);
+ if (err < 0)
+ goto fail;
+
+ qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL,
+ (void *)(unsigned long)fds[0]);
+
+ io_thread_fd = fds[1];
+ return 0;
+
+fail:
+ close(fds[0]);
+ close(fds[1]);
+ return err;
+}
+#else
+HANDLE qemu_event_handle;
+
+static void dummy_event_handler(void *opaque)
+{
+}
+
+static int qemu_event_init(void)
+{
+ qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!qemu_event_handle) {
+ fprintf(stderr, "Failed CreateEvent: %ld\n", GetLastError());
+ return -1;
+ }
+ qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL);
+ return 0;
+}
+
+static void qemu_event_increment(void)
+{
+ if (!SetEvent(qemu_event_handle)) {
+ fprintf(stderr, "qemu_event_increment: SetEvent failed: %ld\n",
+ GetLastError());
+ exit (1);
+ }
+}
+#endif
+
+#ifndef CONFIG_IOTHREAD
+int qemu_init_main_loop(void)
+{
+ cpu_set_debug_excp_handler(cpu_debug_handler);
+
+ return qemu_event_init();
+}
+
+void qemu_main_loop_start(void)
+{
+}
+
+void qemu_init_vcpu(void *_env)
+{
+ CPUState *env = _env;
+
+ env->nr_cores = smp_cores;
+ env->nr_threads = smp_threads;
+ if (kvm_enabled())
+ kvm_init_vcpu(env);
+ return;
+}
+
+int qemu_cpu_self(void *env)
+{
+ return 1;
+}
+
+void run_on_cpu(CPUState *env, void (*func)(void *data), void *data)
+{
+ func(data);
+}
+
+void resume_all_vcpus(void)
+{
+}
+
+void pause_all_vcpus(void)
+{
+}
+
+void qemu_cpu_kick(void *env)
+{
+ return;
+}
+
+void qemu_notify_event(void)
+{
+ CPUState *env = cpu_single_env;
+
+ qemu_event_increment ();
+ if (env) {
+ cpu_exit(env);
+ }
+ if (next_cpu && env != next_cpu) {
+ cpu_exit(next_cpu);
+ }
+}
+
+#if defined(OBSOLETE_KVM_IMPL) || !defined(CONFIG_KVM)
+void qemu_mutex_lock_iothread(void) {}
+void qemu_mutex_unlock_iothread(void) {}
+#endif
+
+void vm_stop(int reason)
+{
+ do_vm_stop(reason);
+}
+
+#else /* CONFIG_IOTHREAD */
+
+#include "qemu-thread.h"
+
+QemuMutex qemu_global_mutex;
+static QemuMutex qemu_fair_mutex;
+
+static QemuThread io_thread;
+
+static QemuThread *tcg_cpu_thread;
+static QemuCond *tcg_halt_cond;
+
+static int qemu_system_ready;
+/* cpu creation */
+static QemuCond qemu_cpu_cond;
+/* system init */
+static QemuCond qemu_system_cond;
+static QemuCond qemu_pause_cond;
+static QemuCond qemu_work_cond;
+
+static void tcg_init_ipi(void);
+static void kvm_init_ipi(CPUState *env);
+static sigset_t block_io_signals(void);
+
+/* If we have signalfd, we mask out the signals we want to handle and then
+ * use signalfd to listen for them. We rely on whatever the current signal
+ * handler is to dispatch the signals when we receive them.
+ */
+static void sigfd_handler(void *opaque)
+{
+ int fd = (unsigned long) opaque;
+ struct qemu_signalfd_siginfo info;
+ struct sigaction action;
+ ssize_t len;
+
+ while (1) {
+ do {
+ len = read(fd, &info, sizeof(info));
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN) {
+ break;
+ }
+
+ if (len != sizeof(info)) {
+ printf("read from sigfd returned %zd: %m\n", len);
+ return;
+ }
+
+ sigaction(info.ssi_signo, NULL, &action);
+ if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) {
+ action.sa_sigaction(info.ssi_signo,
+ (siginfo_t *)&info, NULL);
+ } else if (action.sa_handler) {
+ action.sa_handler(info.ssi_signo);
+ }
+ }
+}
+
+static int qemu_signalfd_init(sigset_t mask)
+{
+ int sigfd;
+
+ sigfd = qemu_signalfd(&mask);
+ if (sigfd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
+ return -errno;
+ }
+
+ fcntl_setfl(sigfd, O_NONBLOCK);
+
+ qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
+ (void *)(unsigned long) sigfd);
+
+ return 0;
+}
+
+int qemu_init_main_loop(void)
+{
+ int ret;
+ sigset_t blocked_signals;
+
+ cpu_set_debug_excp_handler(cpu_debug_handler);
+
+ blocked_signals = block_io_signals();
+
+ ret = qemu_signalfd_init(blocked_signals);
+ if (ret)
+ return ret;
+
+ /* Note eventfd must be drained before signalfd handlers run */
+ ret = qemu_event_init();
+ if (ret)
+ return ret;
+
+ qemu_cond_init(&qemu_pause_cond);
+ qemu_cond_init(&qemu_system_cond);
+ qemu_mutex_init(&qemu_fair_mutex);
+ qemu_mutex_init(&qemu_global_mutex);
+ qemu_mutex_lock(&qemu_global_mutex);
+
+ qemu_thread_self(&io_thread);
+
+ return 0;
+}
+
+void qemu_main_loop_start(void)
+{
+ qemu_system_ready = 1;
+ qemu_cond_broadcast(&qemu_system_cond);
+}
+
+void run_on_cpu(CPUState *env, void (*func)(void *data), void *data)
+{
+ struct qemu_work_item wi;
+
+ if (qemu_cpu_self(env)) {
+ func(data);
+ return;
+ }
+
+ wi.func = func;
+ wi.data = data;
+ if (!env->queued_work_first)
+ env->queued_work_first = &wi;
+ else
+ env->queued_work_last->next = &wi;
+ env->queued_work_last = &wi;
+ wi.next = NULL;
+ wi.done = false;
+
+ qemu_cpu_kick(env);
+ while (!wi.done) {
+ CPUState *self_env = cpu_single_env;
+
+ qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex);
+ cpu_single_env = self_env;
+ }
+}
+
+static void flush_queued_work(CPUState *env)
+{
+ struct qemu_work_item *wi;
+
+ if (!env->queued_work_first)
+ return;
+
+ while ((wi = env->queued_work_first)) {
+ env->queued_work_first = wi->next;
+ wi->func(wi->data);
+ wi->done = true;
+ }
+ env->queued_work_last = NULL;
+ qemu_cond_broadcast(&qemu_work_cond);
+}
+
+static void qemu_wait_io_event_common(CPUState *env)
+{
+ if (env->stop) {
+ env->stop = 0;
+ env->stopped = 1;
+ qemu_cond_signal(&qemu_pause_cond);
+ }
+ flush_queued_work(env);
+}
+
+static void qemu_tcg_wait_io_event(void)
+{
+ CPUState *env;
+
+ while (!any_cpu_has_work())
+ qemu_cond_timedwait(tcg_halt_cond, &qemu_global_mutex, 1000);
+
+ qemu_mutex_unlock(&qemu_global_mutex);
+
+ /*
+ * Users of qemu_global_mutex can be starved, having no chance
+ * to acquire it since this path will get to it first.
+ * So use another lock to provide fairness.
+ */
+ qemu_mutex_lock(&qemu_fair_mutex);
+ qemu_mutex_unlock(&qemu_fair_mutex);
+
+ qemu_mutex_lock(&qemu_global_mutex);
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ qemu_wait_io_event_common(env);
+ }
+}
+
+static void sigbus_reraise(void)
+{
+ sigset_t set;
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_DFL;
+ if (!sigaction(SIGBUS, &action, NULL)) {
+ raise(SIGBUS);
+ sigemptyset(&set);
+ sigaddset(&set, SIGBUS);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ }
+ perror("Failed to re-raise SIGBUS!\n");
+ abort();
+}
+
+static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
+ void *ctx)
+{
+#if defined(TARGET_I386)
+ if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr))
+#endif
+ sigbus_reraise();
+}
+
+static void qemu_kvm_eat_signal(CPUState *env, int timeout)
+{
+ struct timespec ts;
+ int r, e;
+ siginfo_t siginfo;
+ sigset_t waitset;
+ sigset_t chkset;
+
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+
+ sigemptyset(&waitset);
+ sigaddset(&waitset, SIG_IPI);
+ sigaddset(&waitset, SIGBUS);
+
+ do {
+ qemu_mutex_unlock(&qemu_global_mutex);
+
+ r = sigtimedwait(&waitset, &siginfo, &ts);
+ e = errno;
+
+ qemu_mutex_lock(&qemu_global_mutex);
+
+ if (r == -1 && !(e == EAGAIN || e == EINTR)) {
+ fprintf(stderr, "sigtimedwait: %s\n", strerror(e));
+ exit(1);
+ }
+
+ switch (r) {
+ case SIGBUS:
+#ifdef TARGET_I386
+ if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr))
+#endif
+ sigbus_reraise();
+ break;
+ default:
+ break;
+ }
+
+ r = sigpending(&chkset);
+ if (r == -1) {
+ fprintf(stderr, "sigpending: %s\n", strerror(e));
+ exit(1);
+ }
+ } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
+}
+
+static void qemu_kvm_wait_io_event(CPUState *env)
+{
+ while (!cpu_has_work(env))
+ qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000);
+
+ qemu_kvm_eat_signal(env, 0);
+ qemu_wait_io_event_common(env);
+}
+
+static int qemu_cpu_exec(CPUState *env);
+
+static void *kvm_cpu_thread_fn(void *arg)
+{
+ CPUState *env = arg;
+
+ qemu_mutex_lock(&qemu_global_mutex);
+ qemu_thread_self(env->thread);
+ if (kvm_enabled())
+ kvm_init_vcpu(env);
+
+ kvm_init_ipi(env);
+
+ /* signal CPU creation */
+ env->created = 1;
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ /* and wait for machine initialization */
+ while (!qemu_system_ready)
+ qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
+
+ while (1) {
+ if (cpu_can_run(env))
+ qemu_cpu_exec(env);
+ qemu_kvm_wait_io_event(env);
+ }
+
+ return NULL;
+}
+
+static void *tcg_cpu_thread_fn(void *arg)
+{
+ CPUState *env = arg;
+
+ tcg_init_ipi();
+ qemu_thread_self(env->thread);
+
+ /* signal CPU creation */
+ qemu_mutex_lock(&qemu_global_mutex);
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ env->created = 1;
+ qemu_cond_signal(&qemu_cpu_cond);
+
+ /* and wait for machine initialization */
+ while (!qemu_system_ready)
+ qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100);
+
+ while (1) {
+ cpu_exec_all();
+ qemu_tcg_wait_io_event();
+ }
+
+ return NULL;
+}
+
+void qemu_cpu_kick(void *_env)
+{
+ CPUState *env = _env;
+ qemu_cond_broadcast(env->halt_cond);
+ qemu_thread_signal(env->thread, SIG_IPI);
+}
+
+int qemu_cpu_self(void *_env)
+{
+ CPUState *env = _env;
+ QemuThread this;
+
+ qemu_thread_self(&this);
+
+ return qemu_thread_equal(&this, env->thread);
+}
+
+static void cpu_signal(int sig)
+{
+ if (cpu_single_env)
+ cpu_exit(cpu_single_env);
+ exit_request = 1;
+}
+
+static void tcg_init_ipi(void)
+{
+ sigset_t set;
+ struct sigaction sigact;
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = cpu_signal;
+ sigaction(SIG_IPI, &sigact, NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIG_IPI);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+}
+
+static void dummy_signal(int sig)
+{
+}
+
+static void kvm_init_ipi(CPUState *env)
+{
+ int r;
+ sigset_t set;
+ struct sigaction sigact;
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = dummy_signal;
+ sigaction(SIG_IPI, &sigact, NULL);
+
+ pthread_sigmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+ sigdelset(&set, SIGBUS);
+ r = kvm_set_signal_mask(env, &set);
+ if (r) {
+ fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(r));
+ exit(1);
+ }
+}
+
+static sigset_t block_io_signals(void)
+{
+ sigset_t set;
+ struct sigaction action;
+
+ /* SIGUSR2 used by posix-aio-compat.c */
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ sigaddset(&set, SIG_IPI);
+ sigaddset(&set, SIGBUS);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ memset(&action, 0, sizeof(action));
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
+ sigaction(SIGBUS, &action, NULL);
+ prctl(PR_MCE_KILL, 1, 1, 0, 0);
+
+ return set;
+}
+
+void qemu_mutex_lock_iothread(void)
+{
+ if (kvm_enabled()) {
+ qemu_mutex_lock(&qemu_global_mutex);
+ } else {
+ qemu_mutex_lock(&qemu_fair_mutex);
+ if (qemu_mutex_trylock(&qemu_global_mutex)) {
+ qemu_thread_signal(tcg_cpu_thread, SIG_IPI);
+ qemu_mutex_lock(&qemu_global_mutex);
+ }
+ qemu_mutex_unlock(&qemu_fair_mutex);
+ }
+}
+
+void qemu_mutex_unlock_iothread(void)
+{
+ qemu_mutex_unlock(&qemu_global_mutex);
+}
+
+static int all_vcpus_paused(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ if (!penv->stopped)
+ return 0;
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ return 1;
+}
+
+void pause_all_vcpus(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ penv->stop = 1;
+ qemu_cpu_kick(penv);
+ penv = (CPUState *)penv->next_cpu;
+ }
+
+ while (!all_vcpus_paused()) {
+ qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100);
+ penv = first_cpu;
+ while (penv) {
+ qemu_cpu_kick(penv);
+ penv = (CPUState *)penv->next_cpu;
+ }
+ }
+}
+
+void resume_all_vcpus(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ penv->stop = 0;
+ penv->stopped = 0;
+ qemu_cpu_kick(penv);
+ penv = (CPUState *)penv->next_cpu;
+ }
+}
+
+static void tcg_init_vcpu(void *_env)
+{
+ CPUState *env = _env;
+ /* share a single thread for all cpus with TCG */
+ if (!tcg_cpu_thread) {
+ env->thread = qemu_mallocz(sizeof(QemuThread));
+ env->halt_cond = qemu_mallocz(sizeof(QemuCond));
+ qemu_cond_init(env->halt_cond);
+ qemu_thread_create(env->thread, tcg_cpu_thread_fn, env);
+ while (env->created == 0)
+ qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100);
+ tcg_cpu_thread = env->thread;
+ tcg_halt_cond = env->halt_cond;
+ } else {
+ env->thread = tcg_cpu_thread;
+ env->halt_cond = tcg_halt_cond;
+ }
+}
+
+static void kvm_start_vcpu(CPUState *env)
+{
+ env->thread = qemu_mallocz(sizeof(QemuThread));
+ env->halt_cond = qemu_mallocz(sizeof(QemuCond));
+ qemu_cond_init(env->halt_cond);
+ qemu_thread_create(env->thread, kvm_cpu_thread_fn, env);
+ while (env->created == 0)
+ qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100);
+}
+
+void qemu_init_vcpu(void *_env)
+{
+ CPUState *env = _env;
+
+ env->nr_cores = smp_cores;
+ env->nr_threads = smp_threads;
+ if (kvm_enabled())
+ kvm_start_vcpu(env);
+ else
+ tcg_init_vcpu(env);
+}
+
+void qemu_notify_event(void)
+{
+ qemu_event_increment();
+}
+
+static void qemu_system_vmstop_request(int reason)
+{
+ vmstop_requested = reason;
+ qemu_notify_event();
+}
+
+void vm_stop(int reason)
+{
+ QemuThread me;
+ qemu_thread_self(&me);
+
+ if (!qemu_thread_equal(&me, &io_thread)) {
+ qemu_system_vmstop_request(reason);
+ /*
+ * FIXME: should not return to device code in case
+ * vm_stop() has been requested.
+ */
+ if (cpu_single_env) {
+ cpu_exit(cpu_single_env);
+ cpu_single_env->stop = 1;
+ }
+ return;
+ }
+ do_vm_stop(reason);
+}
+
+#endif
+
+static int qemu_cpu_exec(CPUState *env)
+{
+ int ret;
+#ifdef CONFIG_PROFILER
+ int64_t ti;
+#endif
+
+#ifdef CONFIG_PROFILER
+ ti = profile_getclock();
+#endif
+ if (use_icount) {
+ int64_t count;
+ int decr;
+ qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
+ env->icount_decr.u16.low = 0;
+ env->icount_extra = 0;
+ count = qemu_icount_round (qemu_next_deadline());
+ qemu_icount += count;
+ decr = (count > 0xffff) ? 0xffff : count;
+ count -= decr;
+ env->icount_decr.u16.low = decr;
+ env->icount_extra = count;
+ }
+ ret = cpu_exec(env);
+#ifdef CONFIG_PROFILER
+ qemu_time += profile_getclock() - ti;
+#endif
+ if (use_icount) {
+ /* Fold pending instructions back into the
+ instruction counter, and clear the interrupt flag. */
+ qemu_icount -= (env->icount_decr.u16.low
+ + env->icount_extra);
+ env->icount_decr.u32 = 0;
+ env->icount_extra = 0;
+ }
+ return ret;
+}
+
+bool cpu_exec_all(void)
+{
+ if (next_cpu == NULL)
+ next_cpu = first_cpu;
+ for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
+ CPUState *env = next_cpu;
+
+ qemu_clock_enable(vm_clock,
+ (env->singlestep_enabled & SSTEP_NOTIMER) == 0);
+
+ if (qemu_alarm_pending())
+ break;
+ if (cpu_can_run(env)) {
+ if (qemu_cpu_exec(env) == EXCP_DEBUG) {
+ break;
+ }
+ } else if (env->stop) {
+ break;
+ }
+ }
+ exit_request = 0;
+ return any_cpu_has_work();
+}
+
+void set_numa_modes(void)
+{
+ CPUState *env;
+ int i;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ for (i = 0; i < nb_numa_nodes; i++) {
+ if (node_cpumask[i] & (1 << env->cpu_index)) {
+ env->numa_node = i;
+ }
+ }
+ }
+}
+
+void set_cpu_log(const char *optarg)
+{
+ int mask;
+ const CPULogItem *item;
+
+ mask = cpu_str_to_log_mask(optarg);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for (item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+}
+
+/* Return the virtual CPU time, based on the instruction counter. */
+int64_t cpu_get_icount(void)
+{
+ int64_t icount;
+ CPUState *env = cpu_single_env;;
+
+ icount = qemu_icount;
+ if (env) {
+ if (!can_do_io(env)) {
+ fprintf(stderr, "Bad clock read\n");
+ }
+ icount -= (env->icount_decr.u16.low + env->icount_extra);
+ }
+ return qemu_icount_bias + (icount << icount_time_shift);
+}
+
+void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
+{
+ /* XXX: implement xxx_cpu_list for targets that still miss it */
+#if defined(cpu_list_id)
+ cpu_list_id(f, cpu_fprintf, optarg);
+#elif defined(cpu_list)
+ cpu_list(f, cpu_fprintf); /* deprecated */
+#endif
+}
diff --git a/cpus.h b/cpus.h
new file mode 100644
index 0000000..bf4d9bb
--- /dev/null
+++ b/cpus.h
@@ -0,0 +1,21 @@
+#ifndef QEMU_CPUS_H
+#define QEMU_CPUS_H
+
+/* cpus.c */
+int qemu_init_main_loop(void);
+void qemu_main_loop_start(void);
+void resume_all_vcpus(void);
+void pause_all_vcpus(void);
+
+/* vl.c */
+extern int smp_cores;
+extern int smp_threads;
+extern int debug_requested;
+extern int vmstop_requested;
+void vm_state_notify(int running, int reason);
+bool cpu_exec_all(void);
+void set_numa_modes(void);
+void set_cpu_log(const char *optarg);
+void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg);
+
+#endif
diff --git a/cris-dis.c b/cris-dis.c
new file mode 100644
index 0000000..5fa67d9
--- /dev/null
+++ b/cris-dis.c
@@ -0,0 +1,2893 @@
+/* Disassembler code for CRIS.
+ Copyright 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+ Contributed by Axis Communications AB, Lund, Sweden.
+ Written by Hans-Peter Nilsson.
+
+ This file is part of the GNU binutils and GDB, the GNU debugger.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ This program 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 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/>. */
+
+#include "qemu-common.h"
+#include "dis-asm.h"
+//#include "sysdep.h"
+#include "target-cris/opcode-cris.h"
+//#include "libiberty.h"
+
+#define CONST_STRNEQ(STR1,STR2) (strncmp ((STR1), (STR2), sizeof (STR2) - 1) == 0)
+
+/* cris-opc.c -- Table of opcodes for the CRIS processor.
+ Copyright 2000, 2001, 2004 Free Software Foundation, Inc.
+ Contributed by Axis Communications AB, Lund, Sweden.
+ Originally written for GAS 1.38.1 by Mikael Asker.
+ Reorganized by Hans-Peter Nilsson.
+
+This file is part of GAS, GDB and the GNU binutils.
+
+GAS, GDB, and GNU binutils is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2, or (at your
+option) any later version.
+
+GAS, GDB, and GNU binutils are distributed in the hope that they will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU 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/>. */
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+/* This table isn't used for CRISv32 and the size of immediate operands. */
+const struct cris_spec_reg
+cris_spec_regs[] =
+{
+ {"bz", 0, 1, cris_ver_v32p, NULL},
+ {"p0", 0, 1, 0, NULL},
+ {"vr", 1, 1, 0, NULL},
+ {"p1", 1, 1, 0, NULL},
+ {"pid", 2, 1, cris_ver_v32p, NULL},
+ {"p2", 2, 1, cris_ver_v32p, NULL},
+ {"p2", 2, 1, cris_ver_warning, NULL},
+ {"srs", 3, 1, cris_ver_v32p, NULL},
+ {"p3", 3, 1, cris_ver_v32p, NULL},
+ {"p3", 3, 1, cris_ver_warning, NULL},
+ {"wz", 4, 2, cris_ver_v32p, NULL},
+ {"p4", 4, 2, 0, NULL},
+ {"ccr", 5, 2, cris_ver_v0_10, NULL},
+ {"exs", 5, 4, cris_ver_v32p, NULL},
+ {"p5", 5, 2, cris_ver_v0_10, NULL},
+ {"p5", 5, 4, cris_ver_v32p, NULL},
+ {"dcr0",6, 2, cris_ver_v0_3, NULL},
+ {"eda", 6, 4, cris_ver_v32p, NULL},
+ {"p6", 6, 2, cris_ver_v0_3, NULL},
+ {"p6", 6, 4, cris_ver_v32p, NULL},
+ {"dcr1/mof", 7, 4, cris_ver_v10p,
+ "Register `dcr1/mof' with ambiguous size specified. Guessing 4 bytes"},
+ {"dcr1/mof", 7, 2, cris_ver_v0_3,
+ "Register `dcr1/mof' with ambiguous size specified. Guessing 2 bytes"},
+ {"mof", 7, 4, cris_ver_v10p, NULL},
+ {"dcr1",7, 2, cris_ver_v0_3, NULL},
+ {"p7", 7, 4, cris_ver_v10p, NULL},
+ {"p7", 7, 2, cris_ver_v0_3, NULL},
+ {"dz", 8, 4, cris_ver_v32p, NULL},
+ {"p8", 8, 4, 0, NULL},
+ {"ibr", 9, 4, cris_ver_v0_10, NULL},
+ {"ebp", 9, 4, cris_ver_v32p, NULL},
+ {"p9", 9, 4, 0, NULL},
+ {"irp", 10, 4, cris_ver_v0_10, NULL},
+ {"erp", 10, 4, cris_ver_v32p, NULL},
+ {"p10", 10, 4, 0, NULL},
+ {"srp", 11, 4, 0, NULL},
+ {"p11", 11, 4, 0, NULL},
+ /* For disassembly use only. Accept at assembly with a warning. */
+ {"bar/dtp0", 12, 4, cris_ver_warning,
+ "Ambiguous register `bar/dtp0' specified"},
+ {"nrp", 12, 4, cris_ver_v32p, NULL},
+ {"bar", 12, 4, cris_ver_v8_10, NULL},
+ {"dtp0",12, 4, cris_ver_v0_3, NULL},
+ {"p12", 12, 4, 0, NULL},
+ /* For disassembly use only. Accept at assembly with a warning. */
+ {"dccr/dtp1",13, 4, cris_ver_warning,
+ "Ambiguous register `dccr/dtp1' specified"},
+ {"ccs", 13, 4, cris_ver_v32p, NULL},
+ {"dccr",13, 4, cris_ver_v8_10, NULL},
+ {"dtp1",13, 4, cris_ver_v0_3, NULL},
+ {"p13", 13, 4, 0, NULL},
+ {"brp", 14, 4, cris_ver_v3_10, NULL},
+ {"usp", 14, 4, cris_ver_v32p, NULL},
+ {"p14", 14, 4, cris_ver_v3p, NULL},
+ {"usp", 15, 4, cris_ver_v10, NULL},
+ {"spc", 15, 4, cris_ver_v32p, NULL},
+ {"p15", 15, 4, cris_ver_v10p, NULL},
+ {NULL, 0, 0, cris_ver_version_all, NULL}
+};
+
+/* Add version specifiers to this table when necessary.
+ The (now) regular coding of register names suggests a simpler
+ implementation. */
+const struct cris_support_reg cris_support_regs[] =
+{
+ {"s0", 0},
+ {"s1", 1},
+ {"s2", 2},
+ {"s3", 3},
+ {"s4", 4},
+ {"s5", 5},
+ {"s6", 6},
+ {"s7", 7},
+ {"s8", 8},
+ {"s9", 9},
+ {"s10", 10},
+ {"s11", 11},
+ {"s12", 12},
+ {"s13", 13},
+ {"s14", 14},
+ {"s15", 15},
+ {NULL, 0}
+};
+
+/* All CRIS opcodes are 16 bits.
+
+ - The match component is a mask saying which bits must match a
+ particular opcode in order for an instruction to be an instance
+ of that opcode.
+
+ - The args component is a string containing characters symbolically
+ matching the operands of an instruction. Used for both assembly
+ and disassembly.
+
+ Operand-matching characters:
+ [ ] , space
+ Verbatim.
+ A The string "ACR" (case-insensitive).
+ B Not really an operand. It causes a "BDAP -size,SP" prefix to be
+ output for the PUSH alias-instructions and recognizes a push-
+ prefix at disassembly. This letter isn't recognized for v32.
+ Must be followed by a R or P letter.
+ ! Non-match pattern, will not match if there's a prefix insn.
+ b Non-matching operand, used for branches with 16-bit
+ displacement. Only recognized by the disassembler.
+ c 5-bit unsigned immediate in bits <4:0>.
+ C 4-bit unsigned immediate in bits <3:0>.
+ d At assembly, optionally (as in put other cases before this one)
+ ".d" or ".D" at the start of the operands, followed by one space
+ character. At disassembly, nothing.
+ D General register in bits <15:12> and <3:0>.
+ f List of flags in bits <15:12> and <3:0>.
+ i 6-bit signed immediate in bits <5:0>.
+ I 6-bit unsigned immediate in bits <5:0>.
+ M Size modifier (B, W or D) for CLEAR instructions.
+ m Size modifier (B, W or D) in bits <5:4>
+ N A 32-bit dword, like in the difference between s and y.
+ This has no effect on bits in the opcode. Can also be expressed
+ as "[pc+]" in input.
+ n As N, but PC-relative (to the start of the instruction).
+ o [-128..127] word offset in bits <7:1> and <0>. Used by 8-bit
+ branch instructions.
+ O [-128..127] offset in bits <7:0>. Also matches a comma and a
+ general register after the expression, in bits <15:12>. Used
+ only for the BDAP prefix insn (in v32 the ADDOQ insn; same opcode).
+ P Special register in bits <15:12>.
+ p Indicates that the insn is a prefix insn. Must be first
+ character.
+ Q As O, but don't relax; force an 8-bit offset.
+ R General register in bits <15:12>.
+ r General register in bits <3:0>.
+ S Source operand in bit <10> and a prefix; a 3-operand prefix
+ without side-effect.
+ s Source operand in bits <10> and <3:0>, optionally with a
+ side-effect prefix, except [pc] (the name, not R15 as in ACR)
+ isn't allowed for v32 and higher.
+ T Support register in bits <15:12>.
+ u 4-bit (PC-relative) unsigned immediate word offset in bits <3:0>.
+ U Relaxes to either u or n, instruction is assumed LAPCQ or LAPC.
+ Not recognized at disassembly.
+ x Register-dot-modifier, for example "r5.w" in bits <15:12> and <5:4>.
+ y Like 's' but do not allow an integer at assembly.
+ Y The difference s-y; only an integer is allowed.
+ z Size modifier (B or W) in bit <4>. */
+
+
+/* Please note the order of the opcodes in this table is significant.
+ The assembler requires that all instances of the same mnemonic must
+ be consecutive. If they aren't, the assembler might not recognize
+ them, or may indicate an internal error.
+
+ The disassembler should not normally care about the order of the
+ opcodes, but will prefer an earlier alternative if the "match-score"
+ (see cris-dis.c) is computed as equal.
+
+ It should not be significant for proper execution that this table is
+ in alphabetical order, but please follow that convention for an easy
+ overview. */
+
+const struct cris_opcode
+cris_opcodes[] =
+{
+ {"abs", 0x06B0, 0x0940, "r,R", 0, SIZE_NONE, 0,
+ cris_abs_op},
+
+ {"add", 0x0600, 0x09c0, "m r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"add", 0x0A00, 0x01c0, "m s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"add", 0x0A00, 0x01c0, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"add", 0x0a00, 0x05c0, "m S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"add", 0x0A00, 0x01c0, "m s,R", 0, SIZE_FIELD,
+ cris_ver_v32p,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"addc", 0x0570, 0x0A80, "r,R", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"addc", 0x09A0, 0x0250, "s,R", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"addi", 0x0540, 0x0A80, "x,r,A", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_addi_op},
+
+ {"addi", 0x0500, 0x0Ac0, "x,r", 0, SIZE_NONE, 0,
+ cris_addi_op},
+
+ /* This collates after "addo", but we want to disassemble as "addoq",
+ not "addo". */
+ {"addoq", 0x0100, 0x0E00, "Q,A", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"addo", 0x0940, 0x0280, "m s,R,A", 0, SIZE_FIELD_SIGNED,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ /* This must be located after the insn above, lest we misinterpret
+ "addo.b -1,r0,acr" as "addo .b-1,r0,acr". FIXME: Sounds like a
+ parser bug. */
+ {"addo", 0x0100, 0x0E00, "O,A", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"addq", 0x0200, 0x0Dc0, "I,R", 0, SIZE_NONE, 0,
+ cris_quick_mode_add_sub_op},
+
+ {"adds", 0x0420, 0x0Bc0, "z r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */
+ {"adds", 0x0820, 0x03c0, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"adds", 0x0820, 0x03c0, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"adds", 0x0820, 0x07c0, "z S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"addu", 0x0400, 0x0be0, "z r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */
+ {"addu", 0x0800, 0x03e0, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"addu", 0x0800, 0x03e0, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"addu", 0x0800, 0x07e0, "z S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"and", 0x0700, 0x08C0, "m r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"and", 0x0B00, 0x00C0, "m s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"and", 0x0B00, 0x00C0, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"and", 0x0B00, 0x04C0, "m S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"andq", 0x0300, 0x0CC0, "i,R", 0, SIZE_NONE, 0,
+ cris_quick_mode_and_cmp_move_or_op},
+
+ {"asr", 0x0780, 0x0840, "m r,R", 0, SIZE_NONE, 0,
+ cris_asr_op},
+
+ {"asrq", 0x03a0, 0x0c40, "c,R", 0, SIZE_NONE, 0,
+ cris_asrq_op},
+
+ {"ax", 0x15B0, 0xEA4F, "", 0, SIZE_NONE, 0,
+ cris_ax_ei_setf_op},
+
+ /* FIXME: Should use branch #defines. */
+ {"b", 0x0dff, 0x0200, "b", 1, SIZE_NONE, 0,
+ cris_sixteen_bit_offset_branch_op},
+
+ {"ba",
+ BA_QUICK_OPCODE,
+ 0x0F00+(0xF-CC_A)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ /* Needs to come after the usual "ba o", which might be relaxed to
+ this one. */
+ {"ba", BA_DWORD_OPCODE,
+ 0xffff & (~BA_DWORD_OPCODE), "n", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"bas", 0x0EBF, 0x0140, "n,P", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"basc", 0x0EFF, 0x0100, "n,P", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"bcc",
+ BRANCH_QUICK_OPCODE+CC_CC*0x1000,
+ 0x0f00+(0xF-CC_CC)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bcs",
+ BRANCH_QUICK_OPCODE+CC_CS*0x1000,
+ 0x0f00+(0xF-CC_CS)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bdap",
+ BDAP_INDIR_OPCODE, BDAP_INDIR_Z_BITS, "pm s,R", 0, SIZE_FIELD_SIGNED,
+ cris_ver_v0_10,
+ cris_bdap_prefix},
+
+ {"bdap",
+ BDAP_QUICK_OPCODE, BDAP_QUICK_Z_BITS, "pO", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_quick_mode_bdap_prefix},
+
+ {"beq",
+ BRANCH_QUICK_OPCODE+CC_EQ*0x1000,
+ 0x0f00+(0xF-CC_EQ)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ /* This is deliberately put before "bext" to trump it, even though not
+ in alphabetical order, since we don't do excluding version checks
+ for v0..v10. */
+ {"bwf",
+ BRANCH_QUICK_OPCODE+CC_EXT*0x1000,
+ 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE,
+ cris_ver_v10,
+ cris_eight_bit_offset_branch_op},
+
+ {"bext",
+ BRANCH_QUICK_OPCODE+CC_EXT*0x1000,
+ 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE,
+ cris_ver_v0_3,
+ cris_eight_bit_offset_branch_op},
+
+ {"bge",
+ BRANCH_QUICK_OPCODE+CC_GE*0x1000,
+ 0x0f00+(0xF-CC_GE)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bgt",
+ BRANCH_QUICK_OPCODE+CC_GT*0x1000,
+ 0x0f00+(0xF-CC_GT)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bhi",
+ BRANCH_QUICK_OPCODE+CC_HI*0x1000,
+ 0x0f00+(0xF-CC_HI)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bhs",
+ BRANCH_QUICK_OPCODE+CC_HS*0x1000,
+ 0x0f00+(0xF-CC_HS)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"biap", BIAP_OPCODE, BIAP_Z_BITS, "pm r,R", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_biap_prefix},
+
+ {"ble",
+ BRANCH_QUICK_OPCODE+CC_LE*0x1000,
+ 0x0f00+(0xF-CC_LE)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"blo",
+ BRANCH_QUICK_OPCODE+CC_LO*0x1000,
+ 0x0f00+(0xF-CC_LO)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bls",
+ BRANCH_QUICK_OPCODE+CC_LS*0x1000,
+ 0x0f00+(0xF-CC_LS)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"blt",
+ BRANCH_QUICK_OPCODE+CC_LT*0x1000,
+ 0x0f00+(0xF-CC_LT)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bmi",
+ BRANCH_QUICK_OPCODE+CC_MI*0x1000,
+ 0x0f00+(0xF-CC_MI)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bmod", 0x0ab0, 0x0140, "s,R", 0, SIZE_FIX_32,
+ cris_ver_sim_v0_10,
+ cris_not_implemented_op},
+
+ {"bmod", 0x0ab0, 0x0140, "S,D", 0, SIZE_NONE,
+ cris_ver_sim_v0_10,
+ cris_not_implemented_op},
+
+ {"bmod", 0x0ab0, 0x0540, "S,R,r", 0, SIZE_NONE,
+ cris_ver_sim_v0_10,
+ cris_not_implemented_op},
+
+ {"bne",
+ BRANCH_QUICK_OPCODE+CC_NE*0x1000,
+ 0x0f00+(0xF-CC_NE)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bound", 0x05c0, 0x0A00, "m r,R", 0, SIZE_NONE, 0,
+ cris_two_operand_bound_op},
+ /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */
+ {"bound", 0x09c0, 0x0200, "m s,R", 0, SIZE_FIELD,
+ cris_ver_v0_10,
+ cris_two_operand_bound_op},
+ /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */
+ {"bound", 0x0dcf, 0x0200, "m Y,R", 0, SIZE_FIELD, 0,
+ cris_two_operand_bound_op},
+ {"bound", 0x09c0, 0x0200, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_two_operand_bound_op},
+ {"bound", 0x09c0, 0x0600, "m S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_bound_op},
+
+ {"bpl",
+ BRANCH_QUICK_OPCODE+CC_PL*0x1000,
+ 0x0f00+(0xF-CC_PL)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"break", 0xe930, 0x16c0, "C", 0, SIZE_NONE,
+ cris_ver_v3p,
+ cris_break_op},
+
+ {"bsb",
+ BRANCH_QUICK_OPCODE+CC_EXT*0x1000,
+ 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE,
+ cris_ver_v32p,
+ cris_eight_bit_offset_branch_op},
+
+ {"bsr", 0xBEBF, 0x4140, "n", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"bsrc", 0xBEFF, 0x4100, "n", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"bstore", 0x0af0, 0x0100, "s,R", 0, SIZE_FIX_32,
+ cris_ver_warning,
+ cris_not_implemented_op},
+
+ {"bstore", 0x0af0, 0x0100, "S,D", 0, SIZE_NONE,
+ cris_ver_warning,
+ cris_not_implemented_op},
+
+ {"bstore", 0x0af0, 0x0500, "S,R,r", 0, SIZE_NONE,
+ cris_ver_warning,
+ cris_not_implemented_op},
+
+ {"btst", 0x04F0, 0x0B00, "r,R", 0, SIZE_NONE, 0,
+ cris_btst_nop_op},
+ {"btstq", 0x0380, 0x0C60, "c,R", 0, SIZE_NONE, 0,
+ cris_btst_nop_op},
+
+ {"bvc",
+ BRANCH_QUICK_OPCODE+CC_VC*0x1000,
+ 0x0f00+(0xF-CC_VC)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"bvs",
+ BRANCH_QUICK_OPCODE+CC_VS*0x1000,
+ 0x0f00+(0xF-CC_VS)*0x1000, "o", 1, SIZE_NONE, 0,
+ cris_eight_bit_offset_branch_op},
+
+ {"clear", 0x0670, 0x3980, "M r", 0, SIZE_NONE, 0,
+ cris_reg_mode_clear_op},
+
+ {"clear", 0x0A70, 0x3180, "M y", 0, SIZE_NONE, 0,
+ cris_none_reg_mode_clear_test_op},
+
+ {"clear", 0x0A70, 0x3180, "M S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_clear_test_op},
+
+ {"clearf", 0x05F0, 0x0A00, "f", 0, SIZE_NONE, 0,
+ cris_clearf_di_op},
+
+ {"cmp", 0x06C0, 0x0900, "m r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"cmp", 0x0Ac0, 0x0100, "m s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"cmp", 0x0Ac0, 0x0100, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"cmpq", 0x02C0, 0x0D00, "i,R", 0, SIZE_NONE, 0,
+ cris_quick_mode_and_cmp_move_or_op},
+
+ /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */
+ {"cmps", 0x08e0, 0x0300, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"cmps", 0x08e0, 0x0300, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */
+ {"cmpu", 0x08c0, 0x0320, "z s,R" , 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"cmpu", 0x08c0, 0x0320, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"di", 0x25F0, 0xDA0F, "", 0, SIZE_NONE, 0,
+ cris_clearf_di_op},
+
+ {"dip", DIP_OPCODE, DIP_Z_BITS, "ps", 0, SIZE_FIX_32,
+ cris_ver_v0_10,
+ cris_dip_prefix},
+
+ {"div", 0x0980, 0x0640, "m R,r", 0, SIZE_FIELD, 0,
+ cris_not_implemented_op},
+
+ {"dstep", 0x06f0, 0x0900, "r,R", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"ei", 0x25B0, 0xDA4F, "", 0, SIZE_NONE, 0,
+ cris_ax_ei_setf_op},
+
+ {"fidxd", 0x0ab0, 0xf540, "[r]", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"fidxi", 0x0d30, 0xF2C0, "[r]", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"ftagd", 0x1AB0, 0xE540, "[r]", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"ftagi", 0x1D30, 0xE2C0, "[r]", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"halt", 0xF930, 0x06CF, "", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"jas", 0x09B0, 0x0640, "r,P", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_reg_mode_jump_op},
+
+ {"jas", 0x0DBF, 0x0240, "N,P", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_reg_mode_jump_op},
+
+ {"jasc", 0x0B30, 0x04C0, "r,P", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_reg_mode_jump_op},
+
+ {"jasc", 0x0F3F, 0x00C0, "N,P", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_reg_mode_jump_op},
+
+ {"jbrc", 0x69b0, 0x9640, "r", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_reg_mode_jump_op},
+
+ {"jbrc", 0x6930, 0x92c0, "s", 0, SIZE_FIX_32,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jbrc", 0x6930, 0x92c0, "S", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jir", 0xA9b0, 0x5640, "r", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_reg_mode_jump_op},
+
+ {"jir", 0xA930, 0x52c0, "s", 0, SIZE_FIX_32,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jir", 0xA930, 0x52c0, "S", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jirc", 0x29b0, 0xd640, "r", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_reg_mode_jump_op},
+
+ {"jirc", 0x2930, 0xd2c0, "s", 0, SIZE_FIX_32,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jirc", 0x2930, 0xd2c0, "S", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jsr", 0xB9b0, 0x4640, "r", 0, SIZE_NONE, 0,
+ cris_reg_mode_jump_op},
+
+ {"jsr", 0xB930, 0x42c0, "s", 0, SIZE_FIX_32,
+ cris_ver_v0_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jsr", 0xBDBF, 0x4240, "N", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"jsr", 0xB930, 0x42c0, "S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jsrc", 0x39b0, 0xc640, "r", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_reg_mode_jump_op},
+
+ {"jsrc", 0x3930, 0xc2c0, "s", 0, SIZE_FIX_32,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jsrc", 0x3930, 0xc2c0, "S", 0, SIZE_NONE,
+ cris_ver_v8_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jsrc", 0xBB30, 0x44C0, "r", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_reg_mode_jump_op},
+
+ {"jsrc", 0xBF3F, 0x40C0, "N", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_reg_mode_jump_op},
+
+ {"jump", 0x09b0, 0xF640, "r", 0, SIZE_NONE, 0,
+ cris_reg_mode_jump_op},
+
+ {"jump",
+ JUMP_INDIR_OPCODE, JUMP_INDIR_Z_BITS, "s", 0, SIZE_FIX_32,
+ cris_ver_v0_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jump",
+ JUMP_INDIR_OPCODE, JUMP_INDIR_Z_BITS, "S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_jump_op},
+
+ {"jump", 0x09F0, 0x060F, "P", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"jump",
+ JUMP_PC_INCR_OPCODE_V32,
+ (0xffff & ~JUMP_PC_INCR_OPCODE_V32), "N", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_none_reg_mode_jump_op},
+
+ {"jmpu", 0x8930, 0x72c0, "s", 0, SIZE_FIX_32,
+ cris_ver_v10,
+ cris_none_reg_mode_jump_op},
+
+ {"jmpu", 0x8930, 0x72c0, "S", 0, SIZE_NONE,
+ cris_ver_v10,
+ cris_none_reg_mode_jump_op},
+
+ {"lapc", 0x0970, 0x0680, "U,R", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"lapc", 0x0D7F, 0x0280, "dn,R", 0, SIZE_FIX_32,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"lapcq", 0x0970, 0x0680, "u,R", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_addi_op},
+
+ {"lsl", 0x04C0, 0x0B00, "m r,R", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"lslq", 0x03c0, 0x0C20, "c,R", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"lsr", 0x07C0, 0x0800, "m r,R", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"lsrq", 0x03e0, 0x0C00, "c,R", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"lz", 0x0730, 0x08C0, "r,R", 0, SIZE_NONE,
+ cris_ver_v3p,
+ cris_not_implemented_op},
+
+ {"mcp", 0x07f0, 0x0800, "P,r", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"move", 0x0640, 0x0980, "m r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"move", 0x0A40, 0x0180, "m s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"move", 0x0A40, 0x0180, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"move", 0x0630, 0x09c0, "r,P", 0, SIZE_NONE, 0,
+ cris_move_to_preg_op},
+
+ {"move", 0x0670, 0x0980, "P,r", 0, SIZE_NONE, 0,
+ cris_reg_mode_move_from_preg_op},
+
+ {"move", 0x0BC0, 0x0000, "m R,y", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"move", 0x0BC0, 0x0000, "m D,S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"move",
+ MOVE_M_TO_PREG_OPCODE, MOVE_M_TO_PREG_ZBITS,
+ "s,P", 0, SIZE_SPEC_REG, 0,
+ cris_move_to_preg_op},
+
+ {"move", 0x0A30, 0x01c0, "S,P", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_move_to_preg_op},
+
+ {"move", 0x0A70, 0x0180, "P,y", 0, SIZE_SPEC_REG, 0,
+ cris_none_reg_mode_move_from_preg_op},
+
+ {"move", 0x0A70, 0x0180, "P,S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_move_from_preg_op},
+
+ {"move", 0x0B70, 0x0480, "r,T", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"move", 0x0F70, 0x0080, "T,r", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"movem", 0x0BF0, 0x0000, "R,y", 0, SIZE_FIX_32, 0,
+ cris_move_reg_to_mem_movem_op},
+
+ {"movem", 0x0BF0, 0x0000, "D,S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_move_reg_to_mem_movem_op},
+
+ {"movem", 0x0BB0, 0x0040, "s,R", 0, SIZE_FIX_32, 0,
+ cris_move_mem_to_reg_movem_op},
+
+ {"movem", 0x0BB0, 0x0040, "S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_move_mem_to_reg_movem_op},
+
+ {"moveq", 0x0240, 0x0D80, "i,R", 0, SIZE_NONE, 0,
+ cris_quick_mode_and_cmp_move_or_op},
+
+ {"movs", 0x0460, 0x0B80, "z r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */
+ {"movs", 0x0860, 0x0380, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"movs", 0x0860, 0x0380, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"movu", 0x0440, 0x0Ba0, "z r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */
+ {"movu", 0x0840, 0x03a0, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"movu", 0x0840, 0x03a0, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"mstep", 0x07f0, 0x0800, "r,R", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"muls", 0x0d00, 0x02c0, "m r,R", 0, SIZE_NONE,
+ cris_ver_v10p,
+ cris_muls_op},
+
+ {"mulu", 0x0900, 0x06c0, "m r,R", 0, SIZE_NONE,
+ cris_ver_v10p,
+ cris_mulu_op},
+
+ {"neg", 0x0580, 0x0A40, "m r,R", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"nop", NOP_OPCODE, NOP_Z_BITS, "", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_btst_nop_op},
+
+ {"nop", NOP_OPCODE_V32, NOP_Z_BITS_V32, "", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_btst_nop_op},
+
+ {"not", 0x8770, 0x7880, "r", 0, SIZE_NONE, 0,
+ cris_dstep_logshift_mstep_neg_not_op},
+
+ {"or", 0x0740, 0x0880, "m r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"or", 0x0B40, 0x0080, "m s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"or", 0x0B40, 0x0080, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"or", 0x0B40, 0x0480, "m S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"orq", 0x0340, 0x0C80, "i,R", 0, SIZE_NONE, 0,
+ cris_quick_mode_and_cmp_move_or_op},
+
+ {"pop", 0x0E6E, 0x0191, "!R", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"pop", 0x0e3e, 0x01c1, "!P", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_move_from_preg_op},
+
+ {"push", 0x0FEE, 0x0011, "BR", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"push", 0x0E7E, 0x0181, "BP", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_move_to_preg_op},
+
+ {"rbf", 0x3b30, 0xc0c0, "y", 0, SIZE_NONE,
+ cris_ver_v10,
+ cris_not_implemented_op},
+
+ {"rbf", 0x3b30, 0xc0c0, "S", 0, SIZE_NONE,
+ cris_ver_v10,
+ cris_not_implemented_op},
+
+ {"rfe", 0x2930, 0xD6CF, "", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"rfg", 0x4930, 0xB6CF, "", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"rfn", 0x5930, 0xA6CF, "", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ {"ret", 0xB67F, 0x4980, "", 1, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_reg_mode_move_from_preg_op},
+
+ {"ret", 0xB9F0, 0x460F, "", 1, SIZE_NONE,
+ cris_ver_v32p,
+ cris_reg_mode_move_from_preg_op},
+
+ {"retb", 0xe67f, 0x1980, "", 1, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_reg_mode_move_from_preg_op},
+
+ {"rete", 0xA9F0, 0x560F, "", 1, SIZE_NONE,
+ cris_ver_v32p,
+ cris_reg_mode_move_from_preg_op},
+
+ {"reti", 0xA67F, 0x5980, "", 1, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_reg_mode_move_from_preg_op},
+
+ {"retn", 0xC9F0, 0x360F, "", 1, SIZE_NONE,
+ cris_ver_v32p,
+ cris_reg_mode_move_from_preg_op},
+
+ {"sbfs", 0x3b70, 0xc080, "y", 0, SIZE_NONE,
+ cris_ver_v10,
+ cris_not_implemented_op},
+
+ {"sbfs", 0x3b70, 0xc080, "S", 0, SIZE_NONE,
+ cris_ver_v10,
+ cris_not_implemented_op},
+
+ {"sa",
+ 0x0530+CC_A*0x1000,
+ 0x0AC0+(0xf-CC_A)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"ssb",
+ 0x0530+CC_EXT*0x1000,
+ 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_scc_op},
+
+ {"scc",
+ 0x0530+CC_CC*0x1000,
+ 0x0AC0+(0xf-CC_CC)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"scs",
+ 0x0530+CC_CS*0x1000,
+ 0x0AC0+(0xf-CC_CS)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"seq",
+ 0x0530+CC_EQ*0x1000,
+ 0x0AC0+(0xf-CC_EQ)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"setf", 0x05b0, 0x0A40, "f", 0, SIZE_NONE, 0,
+ cris_ax_ei_setf_op},
+
+ {"sfe", 0x3930, 0xC6CF, "", 0, SIZE_NONE,
+ cris_ver_v32p,
+ cris_not_implemented_op},
+
+ /* Need to have "swf" in front of "sext" so it is the one displayed in
+ disassembly. */
+ {"swf",
+ 0x0530+CC_EXT*0x1000,
+ 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE,
+ cris_ver_v10,
+ cris_scc_op},
+
+ {"sext",
+ 0x0530+CC_EXT*0x1000,
+ 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE,
+ cris_ver_v0_3,
+ cris_scc_op},
+
+ {"sge",
+ 0x0530+CC_GE*0x1000,
+ 0x0AC0+(0xf-CC_GE)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"sgt",
+ 0x0530+CC_GT*0x1000,
+ 0x0AC0+(0xf-CC_GT)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"shi",
+ 0x0530+CC_HI*0x1000,
+ 0x0AC0+(0xf-CC_HI)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"shs",
+ 0x0530+CC_HS*0x1000,
+ 0x0AC0+(0xf-CC_HS)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"sle",
+ 0x0530+CC_LE*0x1000,
+ 0x0AC0+(0xf-CC_LE)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"slo",
+ 0x0530+CC_LO*0x1000,
+ 0x0AC0+(0xf-CC_LO)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"sls",
+ 0x0530+CC_LS*0x1000,
+ 0x0AC0+(0xf-CC_LS)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"slt",
+ 0x0530+CC_LT*0x1000,
+ 0x0AC0+(0xf-CC_LT)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"smi",
+ 0x0530+CC_MI*0x1000,
+ 0x0AC0+(0xf-CC_MI)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"sne",
+ 0x0530+CC_NE*0x1000,
+ 0x0AC0+(0xf-CC_NE)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"spl",
+ 0x0530+CC_PL*0x1000,
+ 0x0AC0+(0xf-CC_PL)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"sub", 0x0680, 0x0940, "m r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"sub", 0x0a80, 0x0140, "m s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"sub", 0x0a80, 0x0140, "m S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"sub", 0x0a80, 0x0540, "m S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"subq", 0x0280, 0x0d40, "I,R", 0, SIZE_NONE, 0,
+ cris_quick_mode_add_sub_op},
+
+ {"subs", 0x04a0, 0x0b40, "z r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */
+ {"subs", 0x08a0, 0x0340, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"subs", 0x08a0, 0x0340, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"subs", 0x08a0, 0x0740, "z S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"subu", 0x0480, 0x0b60, "z r,R", 0, SIZE_NONE, 0,
+ cris_reg_mode_add_sub_cmp_and_or_move_op},
+
+ /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */
+ {"subu", 0x0880, 0x0360, "z s,R", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"subu", 0x0880, 0x0360, "z S,D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op},
+
+ {"subu", 0x0880, 0x0760, "z S,R,r", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_three_operand_add_sub_cmp_and_or_op},
+
+ {"svc",
+ 0x0530+CC_VC*0x1000,
+ 0x0AC0+(0xf-CC_VC)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ {"svs",
+ 0x0530+CC_VS*0x1000,
+ 0x0AC0+(0xf-CC_VS)*0x1000, "r", 0, SIZE_NONE, 0,
+ cris_scc_op},
+
+ /* The insn "swapn" is the same as "not" and will be disassembled as
+ such, but the swap* family of mnmonics are generally v8-and-higher
+ only, so count it in. */
+ {"swapn", 0x8770, 0x7880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapw", 0x4770, 0xb880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnw", 0xc770, 0x3880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapb", 0x2770, 0xd880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnb", 0xA770, 0x5880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapwb", 0x6770, 0x9880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnwb", 0xE770, 0x1880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapr", 0x1770, 0xe880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnr", 0x9770, 0x6880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapwr", 0x5770, 0xa880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnwr", 0xd770, 0x2880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapbr", 0x3770, 0xc880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnbr", 0xb770, 0x4880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapwbr", 0x7770, 0x8880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"swapnwbr", 0xf770, 0x0880, "r", 0, SIZE_NONE,
+ cris_ver_v8p,
+ cris_not_implemented_op},
+
+ {"test", 0x0640, 0x0980, "m D", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_reg_mode_test_op},
+
+ {"test", 0x0b80, 0xf040, "m y", 0, SIZE_FIELD, 0,
+ cris_none_reg_mode_clear_test_op},
+
+ {"test", 0x0b80, 0xf040, "m S", 0, SIZE_NONE,
+ cris_ver_v0_10,
+ cris_none_reg_mode_clear_test_op},
+
+ {"xor", 0x07B0, 0x0840, "r,R", 0, SIZE_NONE, 0,
+ cris_xor_op},
+
+ {NULL, 0, 0, NULL, 0, 0, 0, cris_not_implemented_op}
+};
+
+/* Condition-names, indexed by the CC_* numbers as found in cris.h. */
+const char * const
+cris_cc_strings[] =
+{
+ "hs",
+ "lo",
+ "ne",
+ "eq",
+ "vc",
+ "vs",
+ "pl",
+ "mi",
+ "ls",
+ "hi",
+ "ge",
+ "lt",
+ "gt",
+ "le",
+ "a",
+ /* This is a placeholder. In v0, this would be "ext". In v32, this
+ is "sb". See cris_conds15. */
+ "wf"
+};
+
+/* Different names and semantics for condition 1111 (0xf). */
+const struct cris_cond15 cris_cond15s[] =
+{
+ /* FIXME: In what version did condition "ext" disappear? */
+ {"ext", cris_ver_v0_3},
+ {"wf", cris_ver_v10},
+ {"sb", cris_ver_v32p},
+ {NULL, 0}
+};
+
+
+/*
+ * Local variables:
+ * eval: (c-set-style "gnu")
+ * indent-tabs-mode: t
+ * End:
+ */
+
+
+/* No instruction will be disassembled longer than this. In theory, and
+ in silicon, address prefixes can be cascaded. In practice, cascading
+ is not used by GCC, and not supported by the assembler. */
+#ifndef MAX_BYTES_PER_CRIS_INSN
+#define MAX_BYTES_PER_CRIS_INSN 8
+#endif
+
+/* Whether or not to decode prefixes, folding it into the following
+ instruction. FIXME: Make this optional later. */
+#ifndef PARSE_PREFIX
+#define PARSE_PREFIX 1
+#endif
+
+/* Sometimes we prefix all registers with this character. */
+#define REGISTER_PREFIX_CHAR '$'
+
+/* Whether or not to trace the following sequence:
+ sub* X,r%d
+ bound* Y,r%d
+ adds.w [pc+r%d.w],pc
+
+ This is the assembly form of a switch-statement in C.
+ The "sub is optional. If there is none, then X will be zero.
+ X is the value of the first case,
+ Y is the number of cases (including default).
+
+ This results in case offsets printed on the form:
+ case N: -> case_address
+ where N is an estimation on the corresponding 'case' operand in C,
+ and case_address is where execution of that case continues after the
+ sequence presented above.
+
+ The old style of output was to print the offsets as instructions,
+ which made it hard to follow "case"-constructs in the disassembly,
+ and caused a lot of annoying warnings about undefined instructions.
+
+ FIXME: Make this optional later. */
+#ifndef TRACE_CASE
+#define TRACE_CASE (disdata->trace_case)
+#endif
+
+enum cris_disass_family
+ { cris_dis_v0_v10, cris_dis_common_v10_v32, cris_dis_v32 };
+
+/* Stored in the disasm_info->private_data member. */
+struct cris_disasm_data
+{
+ /* Whether to print something less confusing if we find something
+ matching a switch-construct. */
+ bfd_boolean trace_case;
+
+ /* Whether this code is flagged as crisv32. FIXME: Should be an enum
+ that includes "compatible". */
+ enum cris_disass_family distype;
+};
+
+/* Value of first element in switch. */
+static long case_offset = 0;
+
+/* How many more case-offsets to print. */
+static long case_offset_counter = 0;
+
+/* Number of case offsets. */
+static long no_of_case_offsets = 0;
+
+/* Candidate for next case_offset. */
+static long last_immediate = 0;
+
+static int cris_constraint
+ (const char *, unsigned, unsigned, struct cris_disasm_data *);
+
+/* Parse disassembler options and store state in info. FIXME: For the
+ time being, we abuse static variables. */
+
+static bfd_boolean
+cris_parse_disassembler_options (disassemble_info *info,
+ enum cris_disass_family distype)
+{
+ struct cris_disasm_data *disdata;
+
+ info->private_data = calloc (1, sizeof (struct cris_disasm_data));
+ disdata = (struct cris_disasm_data *) info->private_data;
+ if (disdata == NULL)
+ return false;
+
+ /* Default true. */
+ disdata->trace_case
+ = (info->disassembler_options == NULL
+ || (strcmp (info->disassembler_options, "nocase") != 0));
+
+ disdata->distype = distype;
+ return true;
+}
+
+static const struct cris_spec_reg *
+spec_reg_info (unsigned int sreg, enum cris_disass_family distype)
+{
+ int i;
+
+ for (i = 0; cris_spec_regs[i].name != NULL; i++)
+ {
+ if (cris_spec_regs[i].number == sreg)
+ {
+ if (distype == cris_dis_v32)
+ switch (cris_spec_regs[i].applicable_version)
+ {
+ case cris_ver_warning:
+ case cris_ver_version_all:
+ case cris_ver_v3p:
+ case cris_ver_v8p:
+ case cris_ver_v10p:
+ case cris_ver_v32p:
+ /* No ambiguous sizes or register names with CRISv32. */
+ if (cris_spec_regs[i].warning == NULL)
+ return &cris_spec_regs[i];
+ default:
+ ;
+ }
+ else if (cris_spec_regs[i].applicable_version != cris_ver_v32p)
+ return &cris_spec_regs[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Return the number of bits in the argument. */
+
+static int
+number_of_bits (unsigned int val)
+{
+ int bits;
+
+ for (bits = 0; val != 0; val &= val - 1)
+ bits++;
+
+ return bits;
+}
+
+/* Get an entry in the opcode-table. */
+
+static const struct cris_opcode *
+get_opcode_entry (unsigned int insn,
+ unsigned int prefix_insn,
+ struct cris_disasm_data *disdata)
+{
+ /* For non-prefixed insns, we keep a table of pointers, indexed by the
+ insn code. Each entry is initialized when found to be NULL. */
+ static const struct cris_opcode **opc_table = NULL;
+
+ const struct cris_opcode *max_matchedp = NULL;
+ const struct cris_opcode **prefix_opc_table = NULL;
+
+ /* We hold a table for each prefix that need to be handled differently. */
+ static const struct cris_opcode **dip_prefixes = NULL;
+ static const struct cris_opcode **bdapq_m1_prefixes = NULL;
+ static const struct cris_opcode **bdapq_m2_prefixes = NULL;
+ static const struct cris_opcode **bdapq_m4_prefixes = NULL;
+ static const struct cris_opcode **rest_prefixes = NULL;
+
+ /* Allocate and clear the opcode-table. */
+ if (opc_table == NULL)
+ {
+ opc_table = qemu_malloc (65536 * sizeof (opc_table[0]));
+
+ memset (opc_table, 0, 65536 * sizeof (const struct cris_opcode *));
+
+ dip_prefixes
+ = qemu_malloc (65536 * sizeof (const struct cris_opcode **));
+
+ memset (dip_prefixes, 0, 65536 * sizeof (dip_prefixes[0]));
+
+ bdapq_m1_prefixes
+ = qemu_malloc (65536 * sizeof (const struct cris_opcode **));
+
+ memset (bdapq_m1_prefixes, 0, 65536 * sizeof (bdapq_m1_prefixes[0]));
+
+ bdapq_m2_prefixes
+ = qemu_malloc (65536 * sizeof (const struct cris_opcode **));
+
+ memset (bdapq_m2_prefixes, 0, 65536 * sizeof (bdapq_m2_prefixes[0]));
+
+ bdapq_m4_prefixes
+ = qemu_malloc (65536 * sizeof (const struct cris_opcode **));
+
+ memset (bdapq_m4_prefixes, 0, 65536 * sizeof (bdapq_m4_prefixes[0]));
+
+ rest_prefixes
+ = qemu_malloc (65536 * sizeof (const struct cris_opcode **));
+
+ memset (rest_prefixes, 0, 65536 * sizeof (rest_prefixes[0]));
+ }
+
+ /* Get the right table if this is a prefix.
+ This code is connected to cris_constraints in that it knows what
+ prefixes play a role in recognition of patterns; the necessary
+ state is reflected by which table is used. If constraints
+ involving match or non-match of prefix insns are changed, then this
+ probably needs changing too. */
+ if (prefix_insn != NO_CRIS_PREFIX)
+ {
+ const struct cris_opcode *popcodep
+ = (opc_table[prefix_insn] != NULL
+ ? opc_table[prefix_insn]
+ : get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata));
+
+ if (popcodep == NULL)
+ return NULL;
+
+ if (popcodep->match == BDAP_QUICK_OPCODE)
+ {
+ /* Since some offsets are recognized with "push" macros, we
+ have to have different tables for them. */
+ int offset = (prefix_insn & 255);
+
+ if (offset > 127)
+ offset -= 256;
+
+ switch (offset)
+ {
+ case -4:
+ prefix_opc_table = bdapq_m4_prefixes;
+ break;
+
+ case -2:
+ prefix_opc_table = bdapq_m2_prefixes;
+ break;
+
+ case -1:
+ prefix_opc_table = bdapq_m1_prefixes;
+ break;
+
+ default:
+ prefix_opc_table = rest_prefixes;
+ break;
+ }
+ }
+ else if (popcodep->match == DIP_OPCODE)
+ /* We don't allow postincrement when the prefix is DIP, so use a
+ different table for DIP. */
+ prefix_opc_table = dip_prefixes;
+ else
+ prefix_opc_table = rest_prefixes;
+ }
+
+ if (prefix_insn != NO_CRIS_PREFIX
+ && prefix_opc_table[insn] != NULL)
+ max_matchedp = prefix_opc_table[insn];
+ else if (prefix_insn == NO_CRIS_PREFIX && opc_table[insn] != NULL)
+ max_matchedp = opc_table[insn];
+ else
+ {
+ const struct cris_opcode *opcodep;
+ int max_level_of_match = -1;
+
+ for (opcodep = cris_opcodes;
+ opcodep->name != NULL;
+ opcodep++)
+ {
+ int level_of_match;
+
+ if (disdata->distype == cris_dis_v32)
+ {
+ switch (opcodep->applicable_version)
+ {
+ case cris_ver_version_all:
+ break;
+
+ case cris_ver_v0_3:
+ case cris_ver_v0_10:
+ case cris_ver_v3_10:
+ case cris_ver_sim_v0_10:
+ case cris_ver_v8_10:
+ case cris_ver_v10:
+ case cris_ver_warning:
+ continue;
+
+ case cris_ver_v3p:
+ case cris_ver_v8p:
+ case cris_ver_v10p:
+ case cris_ver_v32p:
+ break;
+
+ case cris_ver_v8:
+ abort ();
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ switch (opcodep->applicable_version)
+ {
+ case cris_ver_version_all:
+ case cris_ver_v0_3:
+ case cris_ver_v3p:
+ case cris_ver_v0_10:
+ case cris_ver_v8p:
+ case cris_ver_v8_10:
+ case cris_ver_v10:
+ case cris_ver_sim_v0_10:
+ case cris_ver_v10p:
+ case cris_ver_warning:
+ break;
+
+ case cris_ver_v32p:
+ continue;
+
+ case cris_ver_v8:
+ abort ();
+ default:
+ abort ();
+ }
+ }
+
+ /* We give a double lead for bits matching the template in
+ cris_opcodes. Not even, because then "move p8,r10" would
+ be given 2 bits lead over "clear.d r10". When there's a
+ tie, the first entry in the table wins. This is
+ deliberate, to avoid a more complicated recognition
+ formula. */
+ if ((opcodep->match & insn) == opcodep->match
+ && (opcodep->lose & insn) == 0
+ && ((level_of_match
+ = cris_constraint (opcodep->args,
+ insn,
+ prefix_insn,
+ disdata))
+ >= 0)
+ && ((level_of_match
+ += 2 * number_of_bits (opcodep->match
+ | opcodep->lose))
+ > max_level_of_match))
+ {
+ max_matchedp = opcodep;
+ max_level_of_match = level_of_match;
+
+ /* If there was a full match, never mind looking
+ further. */
+ if (level_of_match >= 2 * 16)
+ break;
+ }
+ }
+ /* Fill in the new entry.
+
+ If there are changes to the opcode-table involving prefixes, and
+ disassembly then does not work correctly, try removing the
+ else-clause below that fills in the prefix-table. If that
+ helps, you need to change the prefix_opc_table setting above, or
+ something related. */
+ if (prefix_insn == NO_CRIS_PREFIX)
+ opc_table[insn] = max_matchedp;
+ else
+ prefix_opc_table[insn] = max_matchedp;
+ }
+
+ return max_matchedp;
+}
+
+/* Return -1 if the constraints of a bitwise-matched instruction say
+ that there is no match. Otherwise return a nonnegative number
+ indicating the confidence in the match (higher is better). */
+
+static int
+cris_constraint (const char *cs,
+ unsigned int insn,
+ unsigned int prefix_insn,
+ struct cris_disasm_data *disdata)
+{
+ int retval = 0;
+ int tmp;
+ int prefix_ok = 0;
+ const char *s;
+
+ for (s = cs; *s; s++)
+ switch (*s)
+ {
+ case '!':
+ /* Do not recognize "pop" if there's a prefix and then only for
+ v0..v10. */
+ if (prefix_insn != NO_CRIS_PREFIX
+ || disdata->distype != cris_dis_v0_v10)
+ return -1;
+ break;
+
+ case 'U':
+ /* Not recognized at disassembly. */
+ return -1;
+
+ case 'M':
+ /* Size modifier for "clear", i.e. special register 0, 4 or 8.
+ Check that it is one of them. Only special register 12 could
+ be mismatched, but checking for matches is more logical than
+ checking for mismatches when there are only a few cases. */
+ tmp = ((insn >> 12) & 0xf);
+ if (tmp != 0 && tmp != 4 && tmp != 8)
+ return -1;
+ break;
+
+ case 'm':
+ if ((insn & 0x30) == 0x30)
+ return -1;
+ break;
+
+ case 'S':
+ /* A prefix operand without side-effect. */
+ if (prefix_insn != NO_CRIS_PREFIX && (insn & 0x400) == 0)
+ {
+ prefix_ok = 1;
+ break;
+ }
+ else
+ return -1;
+
+ case 's':
+ case 'y':
+ case 'Y':
+ /* If this is a prefixed insn with postincrement (side-effect),
+ the prefix must not be DIP. */
+ if (prefix_insn != NO_CRIS_PREFIX)
+ {
+ if (insn & 0x400)
+ {
+ const struct cris_opcode *prefix_opcodep
+ = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata);
+
+ if (prefix_opcodep->match == DIP_OPCODE)
+ return -1;
+ }
+
+ prefix_ok = 1;
+ }
+ break;
+
+ case 'B':
+ /* If we don't fall through, then the prefix is ok. */
+ prefix_ok = 1;
+
+ /* A "push" prefix. Check for valid "push" size.
+ In case of special register, it may be != 4. */
+ if (prefix_insn != NO_CRIS_PREFIX)
+ {
+ /* Match the prefix insn to BDAPQ. */
+ const struct cris_opcode *prefix_opcodep
+ = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata);
+
+ if (prefix_opcodep->match == BDAP_QUICK_OPCODE)
+ {
+ int pushsize = (prefix_insn & 255);
+
+ if (pushsize > 127)
+ pushsize -= 256;
+
+ if (s[1] == 'P')
+ {
+ unsigned int spec_reg = (insn >> 12) & 15;
+ const struct cris_spec_reg *sregp
+ = spec_reg_info (spec_reg, disdata->distype);
+
+ /* For a special-register, the "prefix size" must
+ match the size of the register. */
+ if (sregp && sregp->reg_size == (unsigned int) -pushsize)
+ break;
+ }
+ else if (s[1] == 'R')
+ {
+ if ((insn & 0x30) == 0x20 && pushsize == -4)
+ break;
+ }
+ /* FIXME: Should abort here; next constraint letter
+ *must* be 'P' or 'R'. */
+ }
+ }
+ return -1;
+
+ case 'D':
+ retval = (((insn >> 12) & 15) == (insn & 15));
+ if (!retval)
+ return -1;
+ else
+ retval += 4;
+ break;
+
+ case 'P':
+ {
+ const struct cris_spec_reg *sregp
+ = spec_reg_info ((insn >> 12) & 15, disdata->distype);
+
+ /* Since we match four bits, we will give a value of 4-1 = 3
+ in a match. If there is a corresponding exact match of a
+ special register in another pattern, it will get a value of
+ 4, which will be higher. This should be correct in that an
+ exact pattern would match better than a general pattern.
+
+ Note that there is a reason for not returning zero; the
+ pattern for "clear" is partly matched in the bit-pattern
+ (the two lower bits must be zero), while the bit-pattern
+ for a move from a special register is matched in the
+ register constraint. */
+
+ if (sregp != NULL)
+ {
+ retval += 3;
+ break;
+ }
+ else
+ return -1;
+ }
+ }
+
+ if (prefix_insn != NO_CRIS_PREFIX && ! prefix_ok)
+ return -1;
+
+ return retval;
+}
+
+/* Format number as hex with a leading "0x" into outbuffer. */
+
+static char *
+format_hex (unsigned long number,
+ char *outbuffer,
+ struct cris_disasm_data *disdata)
+{
+ /* Truncate negative numbers on >32-bit hosts. */
+ number &= 0xffffffff;
+
+ sprintf (outbuffer, "0x%lx", number);
+
+ /* Save this value for the "case" support. */
+ if (TRACE_CASE)
+ last_immediate = number;
+
+ return outbuffer + strlen (outbuffer);
+}
+
+/* Format number as decimal into outbuffer. Parameter signedp says
+ whether the number should be formatted as signed (!= 0) or
+ unsigned (== 0). */
+
+static char *
+format_dec (long number, char *outbuffer, int signedp)
+{
+ last_immediate = number;
+ sprintf (outbuffer, signedp ? "%ld" : "%lu", number);
+
+ return outbuffer + strlen (outbuffer);
+}
+
+/* Format the name of the general register regno into outbuffer. */
+
+static char *
+format_reg (struct cris_disasm_data *disdata,
+ int regno,
+ char *outbuffer_start,
+ bfd_boolean with_reg_prefix)
+{
+ char *outbuffer = outbuffer_start;
+
+ if (with_reg_prefix)
+ *outbuffer++ = REGISTER_PREFIX_CHAR;
+
+ switch (regno)
+ {
+ case 15:
+ /* For v32, there is no context in which we output PC. */
+ if (disdata->distype == cris_dis_v32)
+ strcpy (outbuffer, "acr");
+ else
+ strcpy (outbuffer, "pc");
+ break;
+
+ case 14:
+ strcpy (outbuffer, "sp");
+ break;
+
+ default:
+ sprintf (outbuffer, "r%d", regno);
+ break;
+ }
+
+ return outbuffer_start + strlen (outbuffer_start);
+}
+
+/* Format the name of a support register into outbuffer. */
+
+static char *
+format_sup_reg (unsigned int regno,
+ char *outbuffer_start,
+ bfd_boolean with_reg_prefix)
+{
+ char *outbuffer = outbuffer_start;
+ int i;
+
+ if (with_reg_prefix)
+ *outbuffer++ = REGISTER_PREFIX_CHAR;
+
+ for (i = 0; cris_support_regs[i].name != NULL; i++)
+ if (cris_support_regs[i].number == regno)
+ {
+ sprintf (outbuffer, "%s", cris_support_regs[i].name);
+ return outbuffer_start + strlen (outbuffer_start);
+ }
+
+ /* There's supposed to be register names covering all numbers, though
+ some may be generic names. */
+ sprintf (outbuffer, "format_sup_reg-BUG");
+ return outbuffer_start + strlen (outbuffer_start);
+}
+
+/* Return the length of an instruction. */
+
+static unsigned
+bytes_to_skip (unsigned int insn,
+ const struct cris_opcode *matchedp,
+ enum cris_disass_family distype,
+ const struct cris_opcode *prefix_matchedp)
+{
+ /* Each insn is a word plus "immediate" operands. */
+ unsigned to_skip = 2;
+ const char *template = matchedp->args;
+ const char *s;
+
+ for (s = template; *s; s++)
+ if ((*s == 's' || *s == 'N' || *s == 'Y')
+ && (insn & 0x400) && (insn & 15) == 15
+ && prefix_matchedp == NULL)
+ {
+ /* Immediate via [pc+], so we have to check the size of the
+ operand. */
+ int mode_size = 1 << ((insn >> 4) & (*template == 'z' ? 1 : 3));
+
+ if (matchedp->imm_oprnd_size == SIZE_FIX_32)
+ to_skip += 4;
+ else if (matchedp->imm_oprnd_size == SIZE_SPEC_REG)
+ {
+ const struct cris_spec_reg *sregp
+ = spec_reg_info ((insn >> 12) & 15, distype);
+
+ /* FIXME: Improve error handling; should have been caught
+ earlier. */
+ if (sregp == NULL)
+ return 2;
+
+ /* PC is incremented by two, not one, for a byte. Except on
+ CRISv32, where constants are always DWORD-size for
+ special registers. */
+ to_skip +=
+ distype == cris_dis_v32 ? 4 : (sregp->reg_size + 1) & ~1;
+ }
+ else
+ to_skip += (mode_size + 1) & ~1;
+ }
+ else if (*s == 'n')
+ to_skip += 4;
+ else if (*s == 'b')
+ to_skip += 2;
+
+ return to_skip;
+}
+
+/* Print condition code flags. */
+
+static char *
+print_flags (struct cris_disasm_data *disdata, unsigned int insn, char *cp)
+{
+ /* Use the v8 (Etrax 100) flag definitions for disassembly.
+ The differences with v0 (Etrax 1..4) vs. Svinto are:
+ v0 'd' <=> v8 'm'
+ v0 'e' <=> v8 'b'.
+ FIXME: Emit v0..v3 flag names somehow. */
+ static const char v8_fnames[] = "cvznxibm";
+ static const char v32_fnames[] = "cvznxiup";
+ const char *fnames
+ = disdata->distype == cris_dis_v32 ? v32_fnames : v8_fnames;
+
+ unsigned char flagbits = (((insn >> 8) & 0xf0) | (insn & 15));
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (flagbits & (1 << i))
+ *cp++ = fnames[i];
+
+ return cp;
+}
+
+/* Print out an insn with its operands, and update the info->insn_type
+ fields. The prefix_opcodep and the rest hold a prefix insn that is
+ supposed to be output as an address mode. */
+
+static void
+print_with_operands (const struct cris_opcode *opcodep,
+ unsigned int insn,
+ unsigned char *buffer,
+ bfd_vma addr,
+ disassemble_info *info,
+ /* If a prefix insn was before this insn (and is supposed
+ to be output as an address), here is a description of
+ it. */
+ const struct cris_opcode *prefix_opcodep,
+ unsigned int prefix_insn,
+ unsigned char *prefix_buffer,
+ bfd_boolean with_reg_prefix)
+{
+ /* Get a buffer of somewhat reasonable size where we store
+ intermediate parts of the insn. */
+ char temp[sizeof (".d [$r13=$r12-2147483648],$r10") * 2];
+ char *tp = temp;
+ static const char mode_char[] = "bwd?";
+ const char *s;
+ const char *cs;
+ struct cris_disasm_data *disdata
+ = (struct cris_disasm_data *) info->private_data;
+
+ /* Print out the name first thing we do. */
+ (*info->fprintf_func) (info->stream, "%s", opcodep->name);
+
+ cs = opcodep->args;
+ s = cs;
+
+ /* Ignore any prefix indicator. */
+ if (*s == 'p')
+ s++;
+
+ if (*s == 'm' || *s == 'M' || *s == 'z')
+ {
+ *tp++ = '.';
+
+ /* Get the size-letter. */
+ *tp++ = *s == 'M'
+ ? (insn & 0x8000 ? 'd'
+ : insn & 0x4000 ? 'w' : 'b')
+ : mode_char[(insn >> 4) & (*s == 'z' ? 1 : 3)];
+
+ /* Ignore the size and the space character that follows. */
+ s += 2;
+ }
+
+ /* Add a space if this isn't a long-branch, because for those will add
+ the condition part of the name later. */
+ if (opcodep->match != (BRANCH_PC_LOW + BRANCH_INCR_HIGH * 256))
+ *tp++ = ' ';
+
+ /* Fill in the insn-type if deducible from the name (and there's no
+ better way). */
+ if (opcodep->name[0] == 'j')
+ {
+ if (CONST_STRNEQ (opcodep->name, "jsr"))
+ /* It's "jsr" or "jsrc". */
+ info->insn_type = dis_jsr;
+ else
+ /* Any other jump-type insn is considered a branch. */
+ info->insn_type = dis_branch;
+ }
+
+ /* We might know some more fields right now. */
+ info->branch_delay_insns = opcodep->delayed;
+
+ /* Handle operands. */
+ for (; *s; s++)
+ {
+ switch (*s)
+ {
+ case 'T':
+ tp = format_sup_reg ((insn >> 12) & 15, tp, with_reg_prefix);
+ break;
+
+ case 'A':
+ if (with_reg_prefix)
+ *tp++ = REGISTER_PREFIX_CHAR;
+ *tp++ = 'a';
+ *tp++ = 'c';
+ *tp++ = 'r';
+ break;
+
+ case '[':
+ case ']':
+ case ',':
+ *tp++ = *s;
+ break;
+
+ case '!':
+ /* Ignore at this point; used at earlier stages to avoid
+ recognition if there's a prefix at something that in other
+ ways looks like a "pop". */
+ break;
+
+ case 'd':
+ /* Ignore. This is an optional ".d " on the large one of
+ relaxable insns. */
+ break;
+
+ case 'B':
+ /* This was the prefix that made this a "push". We've already
+ handled it by recognizing it, so signal that the prefix is
+ handled by setting it to NULL. */
+ prefix_opcodep = NULL;
+ break;
+
+ case 'D':
+ case 'r':
+ tp = format_reg (disdata, insn & 15, tp, with_reg_prefix);
+ break;
+
+ case 'R':
+ tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix);
+ break;
+
+ case 'n':
+ {
+ /* Like N but pc-relative to the start of the insn. */
+ unsigned long number
+ = (buffer[2] + buffer[3] * 256 + buffer[4] * 65536
+ + buffer[5] * 0x1000000 + addr);
+
+ /* Finish off and output previous formatted bytes. */
+ *tp = 0;
+ if (temp[0])
+ (*info->fprintf_func) (info->stream, "%s", temp);
+ tp = temp;
+
+ (*info->print_address_func) ((bfd_vma) number, info);
+ }
+ break;
+
+ case 'u':
+ {
+ /* Like n but the offset is bits <3:0> in the instruction. */
+ unsigned long number = (buffer[0] & 0xf) * 2 + addr;
+
+ /* Finish off and output previous formatted bytes. */
+ *tp = 0;
+ if (temp[0])
+ (*info->fprintf_func) (info->stream, "%s", temp);
+ tp = temp;
+
+ (*info->print_address_func) ((bfd_vma) number, info);
+ }
+ break;
+
+ case 'N':
+ case 'y':
+ case 'Y':
+ case 'S':
+ case 's':
+ /* Any "normal" memory operand. */
+ if ((insn & 0x400) && (insn & 15) == 15 && prefix_opcodep == NULL)
+ {
+ /* We're looking at [pc+], i.e. we need to output an immediate
+ number, where the size can depend on different things. */
+ long number;
+ int signedp
+ = ((*cs == 'z' && (insn & 0x20))
+ || opcodep->match == BDAP_QUICK_OPCODE);
+ int nbytes;
+
+ if (opcodep->imm_oprnd_size == SIZE_FIX_32)
+ nbytes = 4;
+ else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG)
+ {
+ const struct cris_spec_reg *sregp
+ = spec_reg_info ((insn >> 12) & 15, disdata->distype);
+
+ /* A NULL return should have been as a non-match earlier,
+ so catch it as an internal error in the error-case
+ below. */
+ if (sregp == NULL)
+ /* Whatever non-valid size. */
+ nbytes = 42;
+ else
+ /* PC is always incremented by a multiple of two.
+ For CRISv32, immediates are always 4 bytes for
+ special registers. */
+ nbytes = disdata->distype == cris_dis_v32
+ ? 4 : (sregp->reg_size + 1) & ~1;
+ }
+ else
+ {
+ int mode_size = 1 << ((insn >> 4) & (*cs == 'z' ? 1 : 3));
+
+ if (mode_size == 1)
+ nbytes = 2;
+ else
+ nbytes = mode_size;
+ }
+
+ switch (nbytes)
+ {
+ case 1:
+ number = buffer[2];
+ if (signedp && number > 127)
+ number -= 256;
+ break;
+
+ case 2:
+ number = buffer[2] + buffer[3] * 256;
+ if (signedp && number > 32767)
+ number -= 65536;
+ break;
+
+ case 4:
+ number
+ = buffer[2] + buffer[3] * 256 + buffer[4] * 65536
+ + buffer[5] * 0x1000000;
+ break;
+
+ default:
+ strcpy (tp, "bug");
+ tp += 3;
+ number = 42;
+ }
+
+ if ((*cs == 'z' && (insn & 0x20))
+ || (opcodep->match == BDAP_QUICK_OPCODE
+ && (nbytes <= 2 || buffer[1 + nbytes] == 0)))
+ tp = format_dec (number, tp, signedp);
+ else
+ {
+ unsigned int highbyte = (number >> 24) & 0xff;
+
+ /* Either output this as an address or as a number. If it's
+ a dword with the same high-byte as the address of the
+ insn, assume it's an address, and also if it's a non-zero
+ non-0xff high-byte. If this is a jsr or a jump, then
+ it's definitely an address. */
+ if (nbytes == 4
+ && (highbyte == ((addr >> 24) & 0xff)
+ || (highbyte != 0 && highbyte != 0xff)
+ || info->insn_type == dis_branch
+ || info->insn_type == dis_jsr))
+ {
+ /* Finish off and output previous formatted bytes. */
+ *tp = 0;
+ tp = temp;
+ if (temp[0])
+ (*info->fprintf_func) (info->stream, "%s", temp);
+
+ (*info->print_address_func) ((bfd_vma) number, info);
+
+ info->target = number;
+ }
+ else
+ tp = format_hex (number, tp, disdata);
+ }
+ }
+ else
+ {
+ /* Not an immediate number. Then this is a (possibly
+ prefixed) memory operand. */
+ if (info->insn_type != dis_nonbranch)
+ {
+ int mode_size
+ = 1 << ((insn >> 4)
+ & (opcodep->args[0] == 'z' ? 1 : 3));
+ int size;
+ info->insn_type = dis_dref;
+ info->flags |= CRIS_DIS_FLAG_MEMREF;
+
+ if (opcodep->imm_oprnd_size == SIZE_FIX_32)
+ size = 4;
+ else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG)
+ {
+ const struct cris_spec_reg *sregp
+ = spec_reg_info ((insn >> 12) & 15, disdata->distype);
+
+ /* FIXME: Improve error handling; should have been caught
+ earlier. */
+ if (sregp == NULL)
+ size = 4;
+ else
+ size = sregp->reg_size;
+ }
+ else
+ size = mode_size;
+
+ info->data_size = size;
+ }
+
+ *tp++ = '[';
+
+ if (prefix_opcodep
+ /* We don't match dip with a postincremented field
+ as a side-effect address mode. */
+ && ((insn & 0x400) == 0
+ || prefix_opcodep->match != DIP_OPCODE))
+ {
+ if (insn & 0x400)
+ {
+ tp = format_reg (disdata, insn & 15, tp, with_reg_prefix);
+ *tp++ = '=';
+ }
+
+
+ /* We mainly ignore the prefix format string when the
+ address-mode syntax is output. */
+ switch (prefix_opcodep->match)
+ {
+ case DIP_OPCODE:
+ /* It's [r], [r+] or [pc+]. */
+ if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15)
+ {
+ /* It's [pc+]. This cannot possibly be anything
+ but an address. */
+ unsigned long number
+ = prefix_buffer[2] + prefix_buffer[3] * 256
+ + prefix_buffer[4] * 65536
+ + prefix_buffer[5] * 0x1000000;
+
+ info->target = (bfd_vma) number;
+
+ /* Finish off and output previous formatted
+ data. */
+ *tp = 0;
+ tp = temp;
+ if (temp[0])
+ (*info->fprintf_func) (info->stream, "%s", temp);
+
+ (*info->print_address_func) ((bfd_vma) number, info);
+ }
+ else
+ {
+ /* For a memref in an address, we use target2.
+ In this case, target is zero. */
+ info->flags
+ |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG
+ | CRIS_DIS_FLAG_MEM_TARGET2_MEM);
+
+ info->target2 = prefix_insn & 15;
+
+ *tp++ = '[';
+ tp = format_reg (disdata, prefix_insn & 15, tp,
+ with_reg_prefix);
+ if (prefix_insn & 0x400)
+ *tp++ = '+';
+ *tp++ = ']';
+ }
+ break;
+
+ case BDAP_QUICK_OPCODE:
+ {
+ int number;
+
+ number = prefix_buffer[0];
+ if (number > 127)
+ number -= 256;
+
+ /* Output "reg+num" or, if num < 0, "reg-num". */
+ tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp,
+ with_reg_prefix);
+ if (number >= 0)
+ *tp++ = '+';
+ tp = format_dec (number, tp, 1);
+
+ info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG;
+ info->target = (prefix_insn >> 12) & 15;
+ info->target2 = (bfd_vma) number;
+ break;
+ }
+
+ case BIAP_OPCODE:
+ /* Output "r+R.m". */
+ tp = format_reg (disdata, prefix_insn & 15, tp,
+ with_reg_prefix);
+ *tp++ = '+';
+ tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp,
+ with_reg_prefix);
+ *tp++ = '.';
+ *tp++ = mode_char[(prefix_insn >> 4) & 3];
+
+ info->flags
+ |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG
+ | CRIS_DIS_FLAG_MEM_TARGET_IS_REG
+
+ | ((prefix_insn & 0x8000)
+ ? CRIS_DIS_FLAG_MEM_TARGET2_MULT4
+ : ((prefix_insn & 0x8000)
+ ? CRIS_DIS_FLAG_MEM_TARGET2_MULT2 : 0)));
+
+ /* Is it the casejump? It's a "adds.w [pc+r%d.w],pc". */
+ if (insn == 0xf83f && (prefix_insn & ~0xf000) == 0x55f)
+ /* Then start interpreting data as offsets. */
+ case_offset_counter = no_of_case_offsets;
+ break;
+
+ case BDAP_INDIR_OPCODE:
+ /* Output "r+s.m", or, if "s" is [pc+], "r+s" or
+ "r-s". */
+ tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp,
+ with_reg_prefix);
+
+ if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15)
+ {
+ long number;
+ unsigned int nbytes;
+
+ /* It's a value. Get its size. */
+ int mode_size = 1 << ((prefix_insn >> 4) & 3);
+
+ if (mode_size == 1)
+ nbytes = 2;
+ else
+ nbytes = mode_size;
+
+ switch (nbytes)
+ {
+ case 1:
+ number = prefix_buffer[2];
+ if (number > 127)
+ number -= 256;
+ break;
+
+ case 2:
+ number = prefix_buffer[2] + prefix_buffer[3] * 256;
+ if (number > 32767)
+ number -= 65536;
+ break;
+
+ case 4:
+ number
+ = prefix_buffer[2] + prefix_buffer[3] * 256
+ + prefix_buffer[4] * 65536
+ + prefix_buffer[5] * 0x1000000;
+ break;
+
+ default:
+ strcpy (tp, "bug");
+ tp += 3;
+ number = 42;
+ }
+
+ info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG;
+ info->target2 = (bfd_vma) number;
+
+ /* If the size is dword, then assume it's an
+ address. */
+ if (nbytes == 4)
+ {
+ /* Finish off and output previous formatted
+ bytes. */
+ *tp++ = '+';
+ *tp = 0;
+ tp = temp;
+ (*info->fprintf_func) (info->stream, "%s", temp);
+
+ (*info->print_address_func) ((bfd_vma) number, info);
+ }
+ else
+ {
+ if (number >= 0)
+ *tp++ = '+';
+ tp = format_dec (number, tp, 1);
+ }
+ }
+ else
+ {
+ /* Output "r+[R].m" or "r+[R+].m". */
+ *tp++ = '+';
+ *tp++ = '[';
+ tp = format_reg (disdata, prefix_insn & 15, tp,
+ with_reg_prefix);
+ if (prefix_insn & 0x400)
+ *tp++ = '+';
+ *tp++ = ']';
+ *tp++ = '.';
+ *tp++ = mode_char[(prefix_insn >> 4) & 3];
+
+ info->flags
+ |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG
+ | CRIS_DIS_FLAG_MEM_TARGET2_MEM
+ | CRIS_DIS_FLAG_MEM_TARGET_IS_REG
+
+ | (((prefix_insn >> 4) == 2)
+ ? 0
+ : (((prefix_insn >> 4) & 3) == 1
+ ? CRIS_DIS_FLAG_MEM_TARGET2_MEM_WORD
+ : CRIS_DIS_FLAG_MEM_TARGET2_MEM_BYTE)));
+ }
+ break;
+
+ default:
+ (*info->fprintf_func) (info->stream, "?prefix-bug");
+ }
+
+ /* To mark that the prefix is used, reset it. */
+ prefix_opcodep = NULL;
+ }
+ else
+ {
+ tp = format_reg (disdata, insn & 15, tp, with_reg_prefix);
+
+ info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG;
+ info->target = insn & 15;
+
+ if (insn & 0x400)
+ *tp++ = '+';
+ }
+ *tp++ = ']';
+ }
+ break;
+
+ case 'x':
+ tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix);
+ *tp++ = '.';
+ *tp++ = mode_char[(insn >> 4) & 3];
+ break;
+
+ case 'I':
+ tp = format_dec (insn & 63, tp, 0);
+ break;
+
+ case 'b':
+ {
+ int where = buffer[2] + buffer[3] * 256;
+
+ if (where > 32767)
+ where -= 65536;
+
+ where += addr + ((disdata->distype == cris_dis_v32) ? 0 : 4);
+
+ if (insn == BA_PC_INCR_OPCODE)
+ info->insn_type = dis_branch;
+ else
+ info->insn_type = dis_condbranch;
+
+ info->target = (bfd_vma) where;
+
+ *tp = 0;
+ tp = temp;
+ (*info->fprintf_func) (info->stream, "%s%s ",
+ temp, cris_cc_strings[insn >> 12]);
+
+ (*info->print_address_func) ((bfd_vma) where, info);
+ }
+ break;
+
+ case 'c':
+ tp = format_dec (insn & 31, tp, 0);
+ break;
+
+ case 'C':
+ tp = format_dec (insn & 15, tp, 0);
+ break;
+
+ case 'o':
+ {
+ long offset = insn & 0xfe;
+ bfd_vma target;
+
+ if (insn & 1)
+ offset |= ~0xff;
+
+ if (opcodep->match == BA_QUICK_OPCODE)
+ info->insn_type = dis_branch;
+ else
+ info->insn_type = dis_condbranch;
+
+ target = addr + ((disdata->distype == cris_dis_v32) ? 0 : 2) + offset;
+ info->target = target;
+ *tp = 0;
+ tp = temp;
+ (*info->fprintf_func) (info->stream, "%s", temp);
+ (*info->print_address_func) (target, info);
+ }
+ break;
+
+ case 'Q':
+ case 'O':
+ {
+ long number = buffer[0];
+
+ if (number > 127)
+ number = number - 256;
+
+ tp = format_dec (number, tp, 1);
+ *tp++ = ',';
+ tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix);
+ }
+ break;
+
+ case 'f':
+ tp = print_flags (disdata, insn, tp);
+ break;
+
+ case 'i':
+ tp = format_dec ((insn & 32) ? (insn & 31) | ~31L : insn & 31, tp, 1);
+ break;
+
+ case 'P':
+ {
+ const struct cris_spec_reg *sregp
+ = spec_reg_info ((insn >> 12) & 15, disdata->distype);
+
+ if (sregp->name == NULL)
+ /* Should have been caught as a non-match eariler. */
+ *tp++ = '?';
+ else
+ {
+ if (with_reg_prefix)
+ *tp++ = REGISTER_PREFIX_CHAR;
+ strcpy (tp, sregp->name);
+ tp += strlen (tp);
+ }
+ }
+ break;
+
+ default:
+ strcpy (tp, "???");
+ tp += 3;
+ }
+ }
+
+ *tp = 0;
+
+ if (prefix_opcodep)
+ (*info->fprintf_func) (info->stream, " (OOPS unused prefix \"%s: %s\")",
+ prefix_opcodep->name, prefix_opcodep->args);
+
+ (*info->fprintf_func) (info->stream, "%s", temp);
+
+ /* Get info for matching case-tables, if we don't have any active.
+ We assume that the last constant seen is used; either in the insn
+ itself or in a "move.d const,rN, sub.d rN,rM"-like sequence. */
+ if (TRACE_CASE && case_offset_counter == 0)
+ {
+ if (CONST_STRNEQ (opcodep->name, "sub"))
+ case_offset = last_immediate;
+
+ /* It could also be an "add", if there are negative case-values. */
+ else if (CONST_STRNEQ (opcodep->name, "add"))
+ /* The first case is the negated operand to the add. */
+ case_offset = -last_immediate;
+
+ /* A bound insn will tell us the number of cases. */
+ else if (CONST_STRNEQ (opcodep->name, "bound"))
+ no_of_case_offsets = last_immediate + 1;
+
+ /* A jump or jsr or branch breaks the chain of insns for a
+ case-table, so assume default first-case again. */
+ else if (info->insn_type == dis_jsr
+ || info->insn_type == dis_branch
+ || info->insn_type == dis_condbranch)
+ case_offset = 0;
+ }
+}
+
+
+/* Print the CRIS instruction at address memaddr on stream. Returns
+ length of the instruction, in bytes. Prefix register names with `$' if
+ WITH_REG_PREFIX. */
+
+static int
+print_insn_cris_generic (bfd_vma memaddr,
+ disassemble_info *info,
+ bfd_boolean with_reg_prefix)
+{
+ int nbytes;
+ unsigned int insn;
+ const struct cris_opcode *matchedp;
+ int advance = 0;
+ struct cris_disasm_data *disdata
+ = (struct cris_disasm_data *) info->private_data;
+
+ /* No instruction will be disassembled as longer than this number of
+ bytes; stacked prefixes will not be expanded. */
+ unsigned char buffer[MAX_BYTES_PER_CRIS_INSN];
+ unsigned char *bufp;
+ int status = 0;
+ bfd_vma addr;
+
+ /* There will be an "out of range" error after the last instruction.
+ Reading pairs of bytes in decreasing number, we hope that we will get
+ at least the amount that we will consume.
+
+ If we can't get any data, or we do not get enough data, we print
+ the error message. */
+
+ nbytes = info->buffer_length;
+ if (nbytes > MAX_BYTES_PER_CRIS_INSN)
+ nbytes = MAX_BYTES_PER_CRIS_INSN;
+ status = (*info->read_memory_func) (memaddr, buffer, nbytes, info);
+
+ /* If we did not get all we asked for, then clear the rest.
+ Hopefully this makes a reproducible result in case of errors. */
+ if (nbytes != MAX_BYTES_PER_CRIS_INSN)
+ memset (buffer + nbytes, 0, MAX_BYTES_PER_CRIS_INSN - nbytes);
+
+ addr = memaddr;
+ bufp = buffer;
+
+ /* Set some defaults for the insn info. */
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->flags = 0;
+ info->target = 0;
+ info->target2 = 0;
+
+ /* If we got any data, disassemble it. */
+ if (nbytes != 0)
+ {
+ matchedp = NULL;
+
+ insn = bufp[0] + bufp[1] * 256;
+
+ /* If we're in a case-table, don't disassemble the offsets. */
+ if (TRACE_CASE && case_offset_counter != 0)
+ {
+ info->insn_type = dis_noninsn;
+ advance += 2;
+
+ /* If to print data as offsets, then shortcut here. */
+ (*info->fprintf_func) (info->stream, "case %ld%s: -> ",
+ case_offset + no_of_case_offsets
+ - case_offset_counter,
+ case_offset_counter == 1 ? "/default" :
+ "");
+
+ (*info->print_address_func) ((bfd_vma)
+ ((short) (insn)
+ + (long) (addr
+ - (no_of_case_offsets
+ - case_offset_counter)
+ * 2)), info);
+ case_offset_counter--;
+
+ /* The default case start (without a "sub" or "add") must be
+ zero. */
+ if (case_offset_counter == 0)
+ case_offset = 0;
+ }
+ else if (insn == 0)
+ {
+ /* We're often called to disassemble zeroes. While this is a
+ valid "bcc .+2" insn, it is also useless enough and enough
+ of a nuiscance that we will just output "bcc .+2" for it
+ and signal it as a noninsn. */
+ (*info->fprintf_func) (info->stream,
+ disdata->distype == cris_dis_v32
+ ? "bcc ." : "bcc .+2");
+ info->insn_type = dis_noninsn;
+ advance += 2;
+ }
+ else
+ {
+ const struct cris_opcode *prefix_opcodep = NULL;
+ unsigned char *prefix_buffer = bufp;
+ unsigned int prefix_insn = insn;
+ int prefix_size = 0;
+
+ matchedp = get_opcode_entry (insn, NO_CRIS_PREFIX, disdata);
+
+ /* Check if we're supposed to write out prefixes as address
+ modes and if this was a prefix. */
+ if (matchedp != NULL && PARSE_PREFIX && matchedp->args[0] == 'p')
+ {
+ /* If it's a prefix, put it into the prefix vars and get the
+ main insn. */
+ prefix_size = bytes_to_skip (prefix_insn, matchedp,
+ disdata->distype, NULL);
+ prefix_opcodep = matchedp;
+
+ insn = bufp[prefix_size] + bufp[prefix_size + 1] * 256;
+ matchedp = get_opcode_entry (insn, prefix_insn, disdata);
+
+ if (matchedp != NULL)
+ {
+ addr += prefix_size;
+ bufp += prefix_size;
+ advance += prefix_size;
+ }
+ else
+ {
+ /* The "main" insn wasn't valid, at least not when
+ prefixed. Put back things enough to output the
+ prefix insn only, as a normal insn. */
+ matchedp = prefix_opcodep;
+ insn = prefix_insn;
+ prefix_opcodep = NULL;
+ }
+ }
+
+ if (matchedp == NULL)
+ {
+ (*info->fprintf_func) (info->stream, "??0x%x", insn);
+ advance += 2;
+
+ info->insn_type = dis_noninsn;
+ }
+ else
+ {
+ advance
+ += bytes_to_skip (insn, matchedp, disdata->distype,
+ prefix_opcodep);
+
+ /* The info_type and assorted fields will be set according
+ to the operands. */
+ print_with_operands (matchedp, insn, bufp, addr, info,
+ prefix_opcodep, prefix_insn,
+ prefix_buffer, with_reg_prefix);
+ }
+ }
+ }
+ else
+ info->insn_type = dis_noninsn;
+
+ /* If we read less than MAX_BYTES_PER_CRIS_INSN, i.e. we got an error
+ status when reading that much, and the insn decoding indicated a
+ length exceeding what we read, there is an error. */
+ if (status != 0 && (nbytes == 0 || advance > nbytes))
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ /* Max supported insn size with one folded prefix insn. */
+ info->bytes_per_line = MAX_BYTES_PER_CRIS_INSN;
+
+ /* I would like to set this to a fixed value larger than the actual
+ number of bytes to print in order to avoid spaces between bytes,
+ but objdump.c (2.9.1) does not like that, so we print 16-bit
+ chunks, which is the next choice. */
+ info->bytes_per_chunk = 2;
+
+ /* Printing bytes in order of increasing addresses makes sense,
+ especially on a little-endian target.
+ This is completely the opposite of what you think; setting this to
+ BFD_ENDIAN_LITTLE will print bytes in order N..0 rather than the 0..N
+ we want. */
+ info->display_endian = BFD_ENDIAN_BIG;
+
+ return advance;
+}
+
+/* Disassemble, prefixing register names with `$'. CRIS v0..v10. */
+static int
+print_insn_cris_with_register_prefix (bfd_vma vma,
+ disassemble_info *info)
+{
+ if (info->private_data == NULL
+ && !cris_parse_disassembler_options (info, cris_dis_v0_v10))
+ return -1;
+ return print_insn_cris_generic (vma, info, true);
+}
+/* Disassemble, prefixing register names with `$'. CRIS v32. */
+
+static int
+print_insn_crisv32_with_register_prefix (bfd_vma vma,
+ disassemble_info *info)
+{
+ if (info->private_data == NULL
+ && !cris_parse_disassembler_options (info, cris_dis_v32))
+ return -1;
+ return print_insn_cris_generic (vma, info, true);
+}
+
+#if 0
+/* Disassemble, prefixing register names with `$'.
+ Common v10 and v32 subset. */
+
+static int
+print_insn_crisv10_v32_with_register_prefix (bfd_vma vma,
+ disassemble_info *info)
+{
+ if (info->private_data == NULL
+ && !cris_parse_disassembler_options (info, cris_dis_common_v10_v32))
+ return -1;
+ return print_insn_cris_generic (vma, info, true);
+}
+
+/* Disassemble, no prefixes on register names. CRIS v0..v10. */
+
+static int
+print_insn_cris_without_register_prefix (bfd_vma vma,
+ disassemble_info *info)
+{
+ if (info->private_data == NULL
+ && !cris_parse_disassembler_options (info, cris_dis_v0_v10))
+ return -1;
+ return print_insn_cris_generic (vma, info, false);
+}
+
+/* Disassemble, no prefixes on register names. CRIS v32. */
+
+static int
+print_insn_crisv32_without_register_prefix (bfd_vma vma,
+ disassemble_info *info)
+{
+ if (info->private_data == NULL
+ && !cris_parse_disassembler_options (info, cris_dis_v32))
+ return -1;
+ return print_insn_cris_generic (vma, info, false);
+}
+
+/* Disassemble, no prefixes on register names.
+ Common v10 and v32 subset. */
+
+static int
+print_insn_crisv10_v32_without_register_prefix (bfd_vma vma,
+ disassemble_info *info)
+{
+ if (info->private_data == NULL
+ && !cris_parse_disassembler_options (info, cris_dis_common_v10_v32))
+ return -1;
+ return print_insn_cris_generic (vma, info, false);
+}
+#endif
+
+int
+print_insn_crisv10 (bfd_vma vma,
+ disassemble_info *info)
+{
+ return print_insn_cris_with_register_prefix(vma, info);
+}
+
+int
+print_insn_crisv32 (bfd_vma vma,
+ disassemble_info *info)
+{
+ return print_insn_crisv32_with_register_prefix(vma, info);
+}
+
+/* Return a disassembler-function that prints registers with a `$' prefix,
+ or one that prints registers without a prefix.
+ FIXME: We should improve the solution to avoid the multitude of
+ functions seen above. */
+#if 0
+disassembler_ftype
+cris_get_disassembler (bfd *abfd)
+{
+ /* If there's no bfd in sight, we return what is valid as input in all
+ contexts if fed back to the assembler: disassembly *with* register
+ prefix. Unfortunately this will be totally wrong for v32. */
+ if (abfd == NULL)
+ return print_insn_cris_with_register_prefix;
+
+ if (bfd_get_symbol_leading_char (abfd) == 0)
+ {
+ if (bfd_get_mach (abfd) == bfd_mach_cris_v32)
+ return print_insn_crisv32_with_register_prefix;
+ if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32)
+ return print_insn_crisv10_v32_with_register_prefix;
+
+ /* We default to v10. This may be specifically specified in the
+ bfd mach, but is also the default setting. */
+ return print_insn_cris_with_register_prefix;
+ }
+
+ if (bfd_get_mach (abfd) == bfd_mach_cris_v32)
+ return print_insn_crisv32_without_register_prefix;
+ if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32)
+ return print_insn_crisv10_v32_without_register_prefix;
+ return print_insn_cris_without_register_prefix;
+}
+#endif
+/* Local variables:
+ eval: (c-set-style "gnu")
+ indent-tabs-mode: t
+ End: */
diff --git a/cursor.c b/cursor.c
new file mode 100644
index 0000000..dfb9eef
--- /dev/null
+++ b/cursor.c
@@ -0,0 +1,210 @@
+#include "qemu-common.h"
+#include "console.h"
+
+#include "cursor_hidden.xpm"
+#include "cursor_left_ptr.xpm"
+
+/* for creating built-in cursors */
+static QEMUCursor *cursor_parse_xpm(const char *xpm[])
+{
+ QEMUCursor *c;
+ uint32_t ctab[128];
+ unsigned int width, height, colors, chars;
+ unsigned int line = 0, i, r, g, b, x, y, pixel;
+ char name[16];
+ uint8_t idx;
+
+ /* parse header line: width, height, #colors, #chars */
+ if (sscanf(xpm[line], "%d %d %d %d", &width, &height, &colors, &chars) != 4) {
+ fprintf(stderr, "%s: header parse error: \"%s\"\n",
+ __FUNCTION__, xpm[line]);
+ return NULL;
+ }
+ if (chars != 1) {
+ fprintf(stderr, "%s: chars != 1 not supported\n", __FUNCTION__);
+ return NULL;
+ }
+ line++;
+
+ /* parse color table */
+ for (i = 0; i < colors; i++, line++) {
+ if (sscanf(xpm[line], "%c c %15s", &idx, name) == 2) {
+ if (sscanf(name, "#%02x%02x%02x", &r, &g, &b) == 3) {
+ ctab[idx] = (0xff << 24) | (b << 16) | (g << 8) | r;
+ continue;
+ }
+ if (strcmp(name, "None") == 0) {
+ ctab[idx] = 0x00000000;
+ continue;
+ }
+ }
+ fprintf(stderr, "%s: color parse error: \"%s\"\n",
+ __FUNCTION__, xpm[line]);
+ return NULL;
+ }
+
+ /* parse pixel data */
+ c = cursor_alloc(width, height);
+ for (pixel = 0, y = 0; y < height; y++, line++) {
+ for (x = 0; x < height; x++, pixel++) {
+ idx = xpm[line][x];
+ c->data[pixel] = ctab[idx];
+ }
+ }
+ return c;
+}
+
+/* nice for debugging */
+void cursor_print_ascii_art(QEMUCursor *c, const char *prefix)
+{
+ uint32_t *data = c->data;
+ int x,y;
+
+ for (y = 0; y < c->height; y++) {
+ fprintf(stderr, "%s: %2d: |", prefix, y);
+ for (x = 0; x < c->width; x++, data++) {
+ if ((*data & 0xff000000) != 0xff000000) {
+ fprintf(stderr, " "); /* transparent */
+ } else if ((*data & 0x00ffffff) == 0x00ffffff) {
+ fprintf(stderr, "."); /* white */
+ } else if ((*data & 0x00ffffff) == 0x00000000) {
+ fprintf(stderr, "X"); /* black */
+ } else {
+ fprintf(stderr, "o"); /* other */
+ }
+ }
+ fprintf(stderr, "|\n");
+ }
+}
+
+QEMUCursor *cursor_builtin_hidden(void)
+{
+ QEMUCursor *c;
+
+ c = cursor_parse_xpm(cursor_hidden_xpm);
+ return c;
+}
+
+QEMUCursor *cursor_builtin_left_ptr(void)
+{
+ QEMUCursor *c;
+
+ c = cursor_parse_xpm(cursor_left_ptr_xpm);
+ return c;
+}
+
+QEMUCursor *cursor_alloc(int width, int height)
+{
+ QEMUCursor *c;
+ int datasize = width * height * sizeof(uint32_t);
+
+ c = qemu_mallocz(sizeof(QEMUCursor) + datasize);
+ c->width = width;
+ c->height = height;
+ c->refcount = 1;
+ return c;
+}
+
+void cursor_get(QEMUCursor *c)
+{
+ c->refcount++;
+}
+
+void cursor_put(QEMUCursor *c)
+{
+ if (c == NULL)
+ return;
+ c->refcount--;
+ if (c->refcount)
+ return;
+ qemu_free(c);
+}
+
+int cursor_get_mono_bpl(QEMUCursor *c)
+{
+ return (c->width + 7) / 8;
+}
+
+void cursor_set_mono(QEMUCursor *c,
+ uint32_t foreground, uint32_t background, uint8_t *image,
+ int transparent, uint8_t *mask)
+{
+ uint32_t *data = c->data;
+ uint8_t bit;
+ int x,y,bpl;
+
+ bpl = cursor_get_mono_bpl(c);
+ for (y = 0; y < c->height; y++) {
+ bit = 0x80;
+ for (x = 0; x < c->width; x++, data++) {
+ if (transparent && mask[x/8] & bit) {
+ *data = 0x00000000;
+ } else if (!transparent && !(mask[x/8] & bit)) {
+ *data = 0x00000000;
+ } else if (image[x/8] & bit) {
+ *data = 0xff000000 | foreground;
+ } else {
+ *data = 0xff000000 | background;
+ }
+ bit >>= 1;
+ if (bit == 0) {
+ bit = 0x80;
+ }
+ }
+ mask += bpl;
+ image += bpl;
+ }
+}
+
+void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image)
+{
+ uint32_t *data = c->data;
+ uint8_t bit;
+ int x,y,bpl;
+
+ bpl = cursor_get_mono_bpl(c);
+ memset(image, 0, bpl * c->height);
+ for (y = 0; y < c->height; y++) {
+ bit = 0x80;
+ for (x = 0; x < c->width; x++, data++) {
+ if (((*data & 0xff000000) == 0xff000000) &&
+ ((*data & 0x00ffffff) == foreground)) {
+ image[x/8] |= bit;
+ }
+ bit >>= 1;
+ if (bit == 0) {
+ bit = 0x80;
+ }
+ }
+ image += bpl;
+ }
+}
+
+void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask)
+{
+ uint32_t *data = c->data;
+ uint8_t bit;
+ int x,y,bpl;
+
+ bpl = cursor_get_mono_bpl(c);
+ memset(mask, 0, bpl * c->height);
+ for (y = 0; y < c->height; y++) {
+ bit = 0x80;
+ for (x = 0; x < c->width; x++, data++) {
+ if ((*data & 0xff000000) != 0xff000000) {
+ if (transparent != 0) {
+ mask[x/8] |= bit;
+ }
+ } else {
+ if (transparent == 0) {
+ mask[x/8] |= bit;
+ }
+ }
+ bit >>= 1;
+ if (bit == 0) {
+ bit = 0x80;
+ }
+ }
+ mask += bpl;
+ }
+}
diff --git a/cursor_hidden.xpm b/cursor_hidden.xpm
new file mode 100644
index 0000000..354e7a9
--- /dev/null
+++ b/cursor_hidden.xpm
@@ -0,0 +1,37 @@
+/* XPM */
+static const char *cursor_hidden_xpm[] = {
+ "32 32 1 1",
+ " c None",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+};
diff --git a/cursor_left_ptr.xpm b/cursor_left_ptr.xpm
new file mode 100644
index 0000000..6c9ada9
--- /dev/null
+++ b/cursor_left_ptr.xpm
@@ -0,0 +1,39 @@
+/* XPM */
+static const char *cursor_left_ptr_xpm[] = {
+ "32 32 3 1",
+ "X c #000000",
+ ". c #ffffff",
+ " c None",
+ "X ",
+ "XX ",
+ "X.X ",
+ "X..X ",
+ "X...X ",
+ "X....X ",
+ "X.....X ",
+ "X......X ",
+ "X.......X ",
+ "X........X ",
+ "X.....XXXXX ",
+ "X..X..X ",
+ "X.X X..X ",
+ "XX X..X ",
+ "X X..X ",
+ " X..X ",
+ " X..X ",
+ " X..X ",
+ " XX ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+ " ",
+};
diff --git a/cutils.c b/cutils.c
new file mode 100644
index 0000000..9de57d9
--- /dev/null
+++ b/cutils.c
@@ -0,0 +1,416 @@
+/*
+ * Simple C functions to supplement the C library
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "host-utils.h"
+#include <math.h>
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+int stristart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (qemu_toupper(*p) != qemu_toupper(*q))
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+/* XXX: use host strnlen if available ? */
+int qemu_strnlen(const char *s, int max_len)
+{
+ int i;
+
+ for(i = 0; i < max_len; i++) {
+ if (s[i] == '\0') {
+ break;
+ }
+ }
+ return i;
+}
+
+time_t mktimegm(struct tm *tm)
+{
+ time_t t;
+ int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
+ if (m < 3) {
+ m += 12;
+ y--;
+ }
+ t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 +
+ y / 400 - 719469);
+ t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
+ return t;
+}
+
+int qemu_fls(int i)
+{
+ return 32 - clz32(i);
+}
+
+/*
+ * Make sure data goes on disk, but if possible do not bother to
+ * write out the inode just for timestamp updates.
+ *
+ * Unfortunately even in 2009 many operating systems do not support
+ * fdatasync and have to fall back to fsync.
+ */
+int qemu_fdatasync(int fd)
+{
+#ifdef CONFIG_FDATASYNC
+ return fdatasync(fd);
+#else
+ return fsync(fd);
+#endif
+}
+
+/* io vectors */
+
+void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
+{
+ qiov->iov = qemu_malloc(alloc_hint * sizeof(struct iovec));
+ qiov->niov = 0;
+ qiov->nalloc = alloc_hint;
+ qiov->size = 0;
+}
+
+void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov)
+{
+ int i;
+
+ qiov->iov = iov;
+ qiov->niov = niov;
+ qiov->nalloc = -1;
+ qiov->size = 0;
+ for (i = 0; i < niov; i++)
+ qiov->size += iov[i].iov_len;
+}
+
+void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
+{
+ assert(qiov->nalloc != -1);
+
+ if (qiov->niov == qiov->nalloc) {
+ qiov->nalloc = 2 * qiov->nalloc + 1;
+ qiov->iov = qemu_realloc(qiov->iov, qiov->nalloc * sizeof(struct iovec));
+ }
+ qiov->iov[qiov->niov].iov_base = base;
+ qiov->iov[qiov->niov].iov_len = len;
+ qiov->size += len;
+ ++qiov->niov;
+}
+
+/*
+ * Copies iovecs from src to the end of dst. It starts copying after skipping
+ * the given number of bytes in src and copies until src is completely copied
+ * or the total size of the copied iovec reaches size.The size of the last
+ * copied iovec is changed in order to fit the specified total size if it isn't
+ * a perfect fit already.
+ */
+void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip,
+ size_t size)
+{
+ int i;
+ size_t done;
+ void *iov_base;
+ uint64_t iov_len;
+
+ assert(dst->nalloc != -1);
+
+ done = 0;
+ for (i = 0; (i < src->niov) && (done != size); i++) {
+ if (skip >= src->iov[i].iov_len) {
+ /* Skip the whole iov */
+ skip -= src->iov[i].iov_len;
+ continue;
+ } else {
+ /* Skip only part (or nothing) of the iov */
+ iov_base = (uint8_t*) src->iov[i].iov_base + skip;
+ iov_len = src->iov[i].iov_len - skip;
+ skip = 0;
+ }
+
+ if (done + iov_len > size) {
+ qemu_iovec_add(dst, iov_base, size - done);
+ break;
+ } else {
+ qemu_iovec_add(dst, iov_base, iov_len);
+ }
+ done += iov_len;
+ }
+}
+
+void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size)
+{
+ qemu_iovec_copy(dst, src, 0, size);
+}
+
+void qemu_iovec_destroy(QEMUIOVector *qiov)
+{
+ assert(qiov->nalloc != -1);
+
+ qemu_free(qiov->iov);
+}
+
+void qemu_iovec_reset(QEMUIOVector *qiov)
+{
+ assert(qiov->nalloc != -1);
+
+ qiov->niov = 0;
+ qiov->size = 0;
+}
+
+void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf)
+{
+ uint8_t *p = (uint8_t *)buf;
+ int i;
+
+ for (i = 0; i < qiov->niov; ++i) {
+ memcpy(p, qiov->iov[i].iov_base, qiov->iov[i].iov_len);
+ p += qiov->iov[i].iov_len;
+ }
+}
+
+/*
+ * No dma flushing needed here, as the aio code will call dma_bdrv_cb()
+ * on completion as well, which will result in a call to
+ * dma_bdrv_unmap() which will do the flushing ....
+ */
+void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+ size_t copy;
+ int i;
+
+ for (i = 0; i < qiov->niov && count; ++i) {
+ copy = count;
+ if (copy > qiov->iov[i].iov_len)
+ copy = qiov->iov[i].iov_len;
+ memcpy(qiov->iov[i].iov_base, p, copy);
+ p += copy;
+ count -= copy;
+ }
+}
+
+void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count)
+{
+ size_t n;
+ int i;
+
+ for (i = 0; i < qiov->niov && count; ++i) {
+ n = MIN(count, qiov->iov[i].iov_len);
+ memset(qiov->iov[i].iov_base, c, n);
+ count -= n;
+ }
+}
+
+void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
+ size_t skip)
+{
+ int i;
+ size_t done;
+ void *iov_base;
+ uint64_t iov_len;
+
+ done = 0;
+ for (i = 0; (i < qiov->niov) && (done != count); i++) {
+ if (skip >= qiov->iov[i].iov_len) {
+ /* Skip the whole iov */
+ skip -= qiov->iov[i].iov_len;
+ continue;
+ } else {
+ /* Skip only part (or nothing) of the iov */
+ iov_base = (uint8_t*) qiov->iov[i].iov_base + skip;
+ iov_len = qiov->iov[i].iov_len - skip;
+ skip = 0;
+ }
+
+ if (done + iov_len > count) {
+ memset(iov_base, c, count - done);
+ break;
+ } else {
+ memset(iov_base, c, iov_len);
+ }
+ done += iov_len;
+ }
+}
+
+#ifndef _WIN32
+/* Sets a specific flag */
+int fcntl_setfl(int fd, int flag)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ return -errno;
+
+ if (fcntl(fd, F_SETFL, flags | flag) == -1)
+ return -errno;
+
+ return 0;
+}
+#endif
+
+/*
+ * Convert string to bytes, allowing either B/b for bytes, K/k for KB,
+ * M/m for MB, G/g for GB or T/t for TB. Default without any postfix
+ * is MB. End pointer will be returned in *end, if not NULL. A valid
+ * value must be terminated by whitespace, ',' or '\0'. Return -1 on
+ * error.
+ */
+int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix)
+{
+ int64_t retval = -1;
+ char *endptr;
+ unsigned char c, d;
+ int mul_required = 0;
+ double val, mul, integral, fraction;
+
+ errno = 0;
+ val = strtod(nptr, &endptr);
+ if (isnan(val) || endptr == nptr || errno != 0) {
+ goto fail;
+ }
+ fraction = modf(val, &integral);
+ if (fraction != 0) {
+ mul_required = 1;
+ }
+ /*
+ * Any whitespace character is fine for terminating the number,
+ * in addition we accept ',' to handle strings where the size is
+ * part of a multi token argument.
+ */
+ c = *endptr;
+ d = c;
+ if (qemu_isspace(c) || c == '\0' || c == ',') {
+ c = 0;
+ if (default_suffix) {
+ d = default_suffix;
+ } else {
+ d = c;
+ }
+ }
+ switch (qemu_toupper(d)) {
+ case STRTOSZ_DEFSUFFIX_B:
+ mul = 1;
+ if (mul_required) {
+ goto fail;
+ }
+ break;
+ case STRTOSZ_DEFSUFFIX_KB:
+ mul = 1 << 10;
+ break;
+ case 0:
+ if (mul_required) {
+ goto fail;
+ }
+ case STRTOSZ_DEFSUFFIX_MB:
+ mul = 1ULL << 20;
+ break;
+ case STRTOSZ_DEFSUFFIX_GB:
+ mul = 1ULL << 30;
+ break;
+ case STRTOSZ_DEFSUFFIX_TB:
+ mul = 1ULL << 40;
+ break;
+ default:
+ goto fail;
+ }
+ /*
+ * If not terminated by whitespace, ',', or \0, increment endptr
+ * to point to next character, then check that we are terminated
+ * by an appropriate separating character, ie. whitespace, ',', or
+ * \0. If not, we are seeing trailing garbage, thus fail.
+ */
+ if (c != 0) {
+ endptr++;
+ if (!qemu_isspace(*endptr) && *endptr != ',' && *endptr != 0) {
+ goto fail;
+ }
+ }
+ if ((val * mul >= INT64_MAX) || val < 0) {
+ goto fail;
+ }
+ retval = val * mul;
+
+fail:
+ if (end) {
+ *end = endptr;
+ }
+
+ return retval;
+}
+
+int64_t strtosz(const char *nptr, char **end)
+{
+ return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB);
+}
diff --git a/darwin-user/commpage.c b/darwin-user/commpage.c
new file mode 100644
index 0000000..f6aa71e
--- /dev/null
+++ b/darwin-user/commpage.c
@@ -0,0 +1,357 @@
+ /*
+ * Commpage syscalls
+ *
+ * Copyright (c) 2006 Pierre d'Herbemont
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <mach/message.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <libkern/OSAtomic.h>
+
+#include "qemu.h"
+
+//#define DEBUG_COMMPAGE
+
+#ifdef DEBUG_COMMPAGE
+# define DPRINTF(...) do { qemu_log(__VA_ARGS__); printf(__VA_ARGS__); } while(0)
+#else
+# define DPRINTF(...) do { qemu_log(__VA_ARGS__); } while(0)
+#endif
+
+/********************************************************************
+ * Commpage definitions
+ */
+#ifdef TARGET_I386
+/* Reserve space for the commpage see xnu/osfmk/i386/cpu_capabilities.h */
+# define COMMPAGE_START (-16 * 4096) /* base address is -20 * 4096 */
+# define COMMPAGE_SIZE (0x1240) /* _COMM_PAGE_AREA_LENGTH is 19 * 4096 */
+#elif defined(TARGET_PPC)
+/* Reserve space for the commpage see xnu/osfmk/ppc/cpu_capabilities.h */
+# define COMMPAGE_START (-8*4096)
+# define COMMPAGE_SIZE (2*4096) /* its _COMM_PAGE_AREA_USED but _COMM_PAGE_AREA_LENGTH is 7*4096 */
+#endif
+
+void do_compare_and_swap32(void *cpu_env, int num);
+void do_compare_and_swap64(void *cpu_env, int num);
+void do_add_atomic_word32(void *cpu_env, int num);
+void do_cgettimeofday(void *cpu_env, int num, uint32_t arg1);
+void do_nanotime(void *cpu_env, int num);
+
+void unimpl_commpage(void *cpu_env, int num);
+
+typedef void (*commpage_8args_function_t)(uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7,
+ uint32_t arg8);
+typedef void (*commpage_indirect_function_t)(void *cpu_env, int num, uint32_t arg1,
+ uint32_t arg2, uint32_t arg3, uint32_t arg4, uint32_t arg5,
+ uint32_t arg6, uint32_t arg7, uint32_t arg8);
+
+#define HAS_PTR 0x10
+#define NO_PTR 0x20
+#define CALL_DIRECT 0x1
+#define CALL_INDIRECT 0x2
+
+#define COMMPAGE_ENTRY(name, nargs, offset, func, options) \
+ { #name, offset, nargs, options, (commpage_8args_function_t)func }
+
+struct commpage_entry {
+ char * name;
+ int offset;
+ int nargs;
+ char options;
+ commpage_8args_function_t function;
+};
+
+static inline int commpage_code_num(struct commpage_entry *entry)
+{
+ if((entry->options & HAS_PTR))
+ return entry->offset + 4;
+ else
+ return entry->offset;
+}
+
+static inline int commpage_is_indirect(struct commpage_entry *entry)
+{
+ return !(entry->options & CALL_DIRECT);
+}
+
+/********************************************************************
+ * Commpage entry
+ */
+static struct commpage_entry commpage_entries[] =
+{
+ COMMPAGE_ENTRY(compare_and_swap32, 0, 0x080, do_compare_and_swap32, CALL_INDIRECT | HAS_PTR),
+ COMMPAGE_ENTRY(compare_and_swap64, 0, 0x0c0, do_compare_and_swap64, CALL_INDIRECT | HAS_PTR),
+ COMMPAGE_ENTRY(enqueue, 0, 0x100, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(dequeue, 0, 0x140, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(memory_barrier, 0, 0x180, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(add_atomic_word32, 0, 0x1a0, do_add_atomic_word32, CALL_INDIRECT | HAS_PTR),
+ COMMPAGE_ENTRY(add_atomic_word64, 0, 0x1c0, unimpl_commpage, CALL_INDIRECT | HAS_PTR),
+
+ COMMPAGE_ENTRY(mach_absolute_time, 0, 0x200, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(spinlock_try, 1, 0x220, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(spinlock_lock, 1, 0x260, OSSpinLockLock, CALL_DIRECT),
+ COMMPAGE_ENTRY(spinlock_unlock, 1, 0x2a0, OSSpinLockUnlock, CALL_DIRECT),
+ COMMPAGE_ENTRY(pthread_getspecific, 0, 0x2c0, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(gettimeofday, 1, 0x2e0, do_cgettimeofday, CALL_INDIRECT),
+ COMMPAGE_ENTRY(sys_dcache_flush, 0, 0x4e0, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(sys_icache_invalidate, 0, 0x520, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(pthread_self, 0, 0x580, unimpl_commpage, CALL_INDIRECT),
+
+ COMMPAGE_ENTRY(relinquish, 0, 0x5c0, unimpl_commpage, CALL_INDIRECT),
+
+#ifdef TARGET_I386
+ COMMPAGE_ENTRY(bts, 0, 0x5e0, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(btc, 0, 0x5f0, unimpl_commpage, CALL_INDIRECT),
+#endif
+
+ COMMPAGE_ENTRY(bzero, 2, 0x600, bzero, CALL_DIRECT),
+ COMMPAGE_ENTRY(bcopy, 3, 0x780, bcopy, CALL_DIRECT),
+ COMMPAGE_ENTRY(memcpy, 3, 0x7a0, memcpy, CALL_DIRECT),
+
+#ifdef TARGET_I386
+ COMMPAGE_ENTRY(old_nanotime, 0, 0xf80, do_nanotime, CALL_INDIRECT),
+ COMMPAGE_ENTRY(memset_pattern, 0, 0xf80, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(long_copy, 0, 0x1200, unimpl_commpage, CALL_INDIRECT),
+
+ COMMPAGE_ENTRY(sysintegrity, 0, 0x1600, unimpl_commpage, CALL_INDIRECT),
+
+ COMMPAGE_ENTRY(nanotime, 0, 0x1700, do_nanotime, CALL_INDIRECT),
+#elif TARGET_PPC
+ COMMPAGE_ENTRY(compare_and_swap32b, 0, 0xf80, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(compare_and_swap64b, 0, 0xfc0, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(memset_pattern, 0, 0x1000, unimpl_commpage, CALL_INDIRECT),
+ COMMPAGE_ENTRY(bigcopy, 0, 0x1140, unimpl_commpage, CALL_INDIRECT),
+#endif
+};
+
+
+/********************************************************************
+ * Commpage backdoor
+ */
+static inline void print_commpage_entry(struct commpage_entry entry)
+{
+ printf("@0x%x %s\n", entry.offset, entry.name);
+}
+
+static inline void install_commpage_backdoor_for_entry(struct commpage_entry entry)
+{
+#ifdef TARGET_I386
+ char * commpage = (char*)(COMMPAGE_START+entry.offset);
+ int c = 0;
+ if(entry.options & HAS_PTR)
+ {
+ commpage[c++] = (COMMPAGE_START+entry.offset+4) & 0xff;
+ commpage[c++] = ((COMMPAGE_START+entry.offset+4) >> 8) & 0xff;
+ commpage[c++] = ((COMMPAGE_START+entry.offset+4) >> 16) & 0xff;
+ commpage[c++] = ((COMMPAGE_START+entry.offset+4) >> 24) & 0xff;
+ }
+ commpage[c++] = 0xcd;
+ commpage[c++] = 0x79; /* int 0x79 */
+ commpage[c++] = 0xc3; /* ret */
+#else
+ qerror("can't install the commpage on this arch\n");
+#endif
+}
+
+/********************************************************************
+ * Commpage initialization
+ */
+void commpage_init(void)
+{
+#if (defined(__i386__) ^ defined(TARGET_I386)) || (defined(_ARCH_PPC) ^ defined(TARGET_PPC))
+ int i;
+ void * commpage = (void *)target_mmap( COMMPAGE_START, COMMPAGE_SIZE,
+ PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if((int)commpage != COMMPAGE_START)
+ qerror("can't allocate the commpage\n");
+
+ bzero(commpage, COMMPAGE_SIZE);
+
+ /* XXX: commpage data not handled */
+
+ for(i = 0; i < ARRAY_SIZE(commpage_entries); i++)
+ install_commpage_backdoor_for_entry(commpage_entries[i]);
+#else
+ /* simply map our pages so they can be executed
+ XXX: we don't really want to do that since in the ppc on ppc situation we may
+ not able to run commpages host optimized instructions (like G5's on a G5),
+ hence this is sometimes a broken fix. */
+ page_set_flags(COMMPAGE_START, COMMPAGE_START+COMMPAGE_SIZE, PROT_EXEC | PROT_READ | PAGE_VALID);
+#endif
+}
+
+/********************************************************************
+ * Commpage implementation
+ */
+void do_compare_and_swap32(void *cpu_env, int num)
+{
+#ifdef TARGET_I386
+ uint32_t old = ((CPUX86State*)cpu_env)->regs[R_EAX];
+ uint32_t *value = (uint32_t*)((CPUX86State*)cpu_env)->regs[R_ECX];
+ DPRINTF("commpage: compare_and_swap32(%x,new,%p)\n", old, value);
+
+ if(value && old == tswap32(*value))
+ {
+ uint32_t new = ((CPUX86State*)cpu_env)->regs[R_EDX];
+ *value = tswap32(new);
+ /* set zf flag */
+ ((CPUX86State*)cpu_env)->eflags |= 0x40;
+ }
+ else
+ {
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = tswap32(*value);
+ /* unset zf flag */
+ ((CPUX86State*)cpu_env)->eflags &= ~0x40;
+ }
+#else
+ qerror("do_compare_and_swap32 unimplemented");
+#endif
+}
+
+void do_compare_and_swap64(void *cpu_env, int num)
+{
+#ifdef TARGET_I386
+ /* OSAtomicCompareAndSwap64 is not available on non 64 bits ppc, here is a raw implementation */
+ uint64_t old, new, swapped_val;
+ uint64_t *value = (uint64_t*)((CPUX86State*)cpu_env)->regs[R_ESI];
+ old = (uint64_t)((uint64_t)((CPUX86State*)cpu_env)->regs[R_EDX]) << 32 | (uint64_t)((CPUX86State*)cpu_env)->regs[R_EAX];
+
+ DPRINTF("commpage: compare_and_swap64(%" PRIx64 ",new,%p)\n", old, value);
+ swapped_val = tswap64(*value);
+
+ if(old == swapped_val)
+ {
+ new = (uint64_t)((uint64_t)((CPUX86State*)cpu_env)->regs[R_ECX]) << 32 | (uint64_t)((CPUX86State*)cpu_env)->regs[R_EBX];
+ *value = tswap64(new);
+ /* set zf flag */
+ ((CPUX86State*)cpu_env)->eflags |= 0x40;
+ }
+ else
+ {
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = (uint32_t)(swapped_val);
+ ((CPUX86State*)cpu_env)->regs[R_EDX] = (uint32_t)(swapped_val >> 32);
+ /* unset zf flag */
+ ((CPUX86State*)cpu_env)->eflags &= ~0x40;
+ }
+#else
+ qerror("do_compare_and_swap64 unimplemented");
+#endif
+}
+
+void do_add_atomic_word32(void *cpu_env, int num)
+{
+#ifdef TARGET_I386
+ uint32_t amt = ((CPUX86State*)cpu_env)->regs[R_EAX];
+ uint32_t *value = (uint32_t*)((CPUX86State*)cpu_env)->regs[R_EDX];
+ uint32_t swapped_value = tswap32(*value);
+
+ DPRINTF("commpage: add_atomic_word32(%x,%p)\n", amt, value);
+
+ /* old value in EAX */
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = swapped_value;
+ *value = tswap32(swapped_value + amt);
+#else
+ qerror("do_add_atomic_word32 unimplemented");
+#endif
+}
+
+void do_cgettimeofday(void *cpu_env, int num, uint32_t arg1)
+{
+#ifdef TARGET_I386
+ extern int __commpage_gettimeofday(struct timeval *);
+ DPRINTF("commpage: gettimeofday(0x%x)\n", arg1);
+ struct timeval *time = (struct timeval *)arg1;
+ int ret = __commpage_gettimeofday(time);
+ tswap32s((uint32_t*)&time->tv_sec);
+ tswap32s((uint32_t*)&time->tv_usec);
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = ret; /* Success */
+#else
+ qerror("do_gettimeofday unimplemented");
+#endif
+}
+
+void do_nanotime(void *cpu_env, int num)
+{
+#ifdef TARGET_I386
+ uint64_t t = mach_absolute_time();
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = (int)(t & 0xffffffff);
+ ((CPUX86State*)cpu_env)->regs[R_EDX] = (int)((t >> 32) & 0xffffffff);
+#else
+ qerror("do_nanotime unimplemented");
+#endif
+}
+
+void unimpl_commpage(void *cpu_env, int num)
+{
+ qerror("qemu: commpage function 0x%x not implemented\n", num);
+}
+
+/********************************************************************
+ * do_commpage - called by the main cpu loop
+ */
+void
+do_commpage(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7,
+ uint32_t arg8)
+{
+ int i, found = 0;
+
+ arg1 = tswap32(arg1);
+ arg2 = tswap32(arg2);
+ arg3 = tswap32(arg3);
+ arg4 = tswap32(arg4);
+ arg5 = tswap32(arg5);
+ arg6 = tswap32(arg6);
+ arg7 = tswap32(arg7);
+ arg8 = tswap32(arg8);
+
+ num = num-COMMPAGE_START-2;
+
+ for(i = 0; i < ARRAY_SIZE(commpage_entries); i++) {
+ if( num == commpage_code_num(&commpage_entries[i]) )
+ {
+ DPRINTF("commpage: %s %s\n", commpage_entries[i].name, commpage_is_indirect(&commpage_entries[i]) ? "[indirect]" : "[direct]");
+ found = 1;
+ if(commpage_is_indirect(&commpage_entries[i]))
+ {
+ commpage_indirect_function_t function = (commpage_indirect_function_t)commpage_entries[i].function;
+ function(cpu_env, num, arg1, arg2, arg3,
+ arg4, arg5, arg6, arg7, arg8);
+ }
+ else
+ {
+ commpage_entries[i].function(arg1, arg2, arg3,
+ arg4, arg5, arg6, arg7, arg8);
+ }
+ break;
+ }
+ }
+
+ if(!found)
+ {
+ gemu_log("qemu: commpage function 0x%x not defined\n", num);
+ gdb_handlesig (cpu_env, SIGTRAP);
+ exit(-1);
+ }
+}
diff --git a/darwin-user/ioctls.h b/darwin-user/ioctls.h
new file mode 100644
index 0000000..dc73af2
--- /dev/null
+++ b/darwin-user/ioctls.h
@@ -0,0 +1,4 @@
+ /* emulated ioctl list */
+
+ IOCTL(TIOCGETA, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCSETA, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
diff --git a/darwin-user/ioctls_types.h b/darwin-user/ioctls_types.h
new file mode 100644
index 0000000..014561a
--- /dev/null
+++ b/darwin-user/ioctls_types.h
@@ -0,0 +1 @@
+STRUCT(termios, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, MK_ARRAY(TYPE_CHAR, 20), TYPE_INT, TYPE_INT)
diff --git a/darwin-user/machload.c b/darwin-user/machload.c
new file mode 100644
index 0000000..3bc3b65
--- /dev/null
+++ b/darwin-user/machload.c
@@ -0,0 +1,902 @@
+/*
+ * Mach-O object file loading
+ *
+ * Copyright (c) 2006 Pierre d'Herbemont
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu.h"
+#include "disas.h"
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <mach-o/nlist.h>
+#include <mach-o/reloc.h>
+#include <mach-o/ppc/reloc.h>
+
+//#define DEBUG_MACHLOAD
+
+#ifdef DEBUG_MACHLOAD
+# define DPRINTF(...) do { qemu_log(__VA_ARGS__); printf(__VA_ARGS__); } while(0)
+#else
+# define DPRINTF(...) do { qemu_log(__VA_ARGS__); } while(0)
+#endif
+
+# define check_mach_header(x) (x.magic == MH_CIGAM)
+
+extern const char *interp_prefix;
+
+/* we don't have a good implementation for this */
+#define DONT_USE_DYLD_SHARED_MAP
+
+/* Pass extra arg to DYLD for debug */
+//#define ACTIVATE_DYLD_TRACE
+
+//#define OVERRIDE_DYLINKER
+
+#ifdef OVERRIDE_DYLINKER
+# ifdef TARGET_I386
+# define DYLINKER_NAME "/Users/steg/qemu/tests/i386-darwin-env/usr/lib/dyld"
+# else
+# define DYLINKER_NAME "/usr/lib/dyld"
+# endif
+#endif
+
+/* XXX: in an include */
+struct nlist_extended
+{
+ union {
+ char *n_name;
+ long n_strx;
+ } n_un;
+ unsigned char n_type;
+ unsigned char n_sect;
+ short st_desc;
+ unsigned long st_value;
+ unsigned long st_size;
+};
+
+/* Print symbols in gdb */
+void *macho_text_sect = 0;
+int macho_offset = 0;
+
+int load_object(const char *filename, struct target_pt_regs * regs, void ** mh);
+
+#ifdef TARGET_I386
+typedef struct mach_i386_thread_state {
+ unsigned int eax;
+ unsigned int ebx;
+ unsigned int ecx;
+ unsigned int edx;
+ unsigned int edi;
+ unsigned int esi;
+ unsigned int ebp;
+ unsigned int esp;
+ unsigned int ss;
+ unsigned int eflags;
+ unsigned int eip;
+ unsigned int cs;
+ unsigned int ds;
+ unsigned int es;
+ unsigned int fs;
+ unsigned int gs;
+} mach_i386_thread_state_t;
+
+void bswap_i386_thread_state(struct mach_i386_thread_state *ts)
+{
+ bswap32s((uint32_t*)&ts->eax);
+ bswap32s((uint32_t*)&ts->ebx);
+ bswap32s((uint32_t*)&ts->ecx);
+ bswap32s((uint32_t*)&ts->edx);
+ bswap32s((uint32_t*)&ts->edi);
+ bswap32s((uint32_t*)&ts->esi);
+ bswap32s((uint32_t*)&ts->ebp);
+ bswap32s((uint32_t*)&ts->esp);
+ bswap32s((uint32_t*)&ts->ss);
+ bswap32s((uint32_t*)&ts->eflags);
+ bswap32s((uint32_t*)&ts->eip);
+ bswap32s((uint32_t*)&ts->cs);
+ bswap32s((uint32_t*)&ts->ds);
+ bswap32s((uint32_t*)&ts->es);
+ bswap32s((uint32_t*)&ts->fs);
+ bswap32s((uint32_t*)&ts->gs);
+}
+#define target_thread_state mach_i386_thread_state
+#define TARGET_CPU_TYPE CPU_TYPE_I386
+#define TARGET_CPU_NAME "i386"
+#endif
+
+#ifdef TARGET_PPC
+struct mach_ppc_thread_state {
+ unsigned int srr0; /* Instruction address register (PC) */
+ unsigned int srr1; /* Machine state register (supervisor) */
+ unsigned int r0;
+ unsigned int r1;
+ unsigned int r2;
+ unsigned int r3;
+ unsigned int r4;
+ unsigned int r5;
+ unsigned int r6;
+ unsigned int r7;
+ unsigned int r8;
+ unsigned int r9;
+ unsigned int r10;
+ unsigned int r11;
+ unsigned int r12;
+ unsigned int r13;
+ unsigned int r14;
+ unsigned int r15;
+ unsigned int r16;
+ unsigned int r17;
+ unsigned int r18;
+ unsigned int r19;
+ unsigned int r20;
+ unsigned int r21;
+ unsigned int r22;
+ unsigned int r23;
+ unsigned int r24;
+ unsigned int r25;
+ unsigned int r26;
+ unsigned int r27;
+ unsigned int r28;
+ unsigned int r29;
+ unsigned int r30;
+ unsigned int r31;
+
+ unsigned int cr; /* Condition register */
+ unsigned int xer; /* User's integer exception register */
+ unsigned int lr; /* Link register */
+ unsigned int ctr; /* Count register */
+ unsigned int mq; /* MQ register (601 only) */
+
+ unsigned int vrsave; /* Vector Save Register */
+};
+
+void bswap_ppc_thread_state(struct mach_ppc_thread_state *ts)
+{
+ bswap32s((uint32_t*)&ts->srr0);
+ bswap32s((uint32_t*)&ts->srr1);
+ bswap32s((uint32_t*)&ts->r0);
+ bswap32s((uint32_t*)&ts->r1);
+ bswap32s((uint32_t*)&ts->r2);
+ bswap32s((uint32_t*)&ts->r3);
+ bswap32s((uint32_t*)&ts->r4);
+ bswap32s((uint32_t*)&ts->r5);
+ bswap32s((uint32_t*)&ts->r6);
+ bswap32s((uint32_t*)&ts->r7);
+ bswap32s((uint32_t*)&ts->r8);
+ bswap32s((uint32_t*)&ts->r9);
+ bswap32s((uint32_t*)&ts->r10);
+ bswap32s((uint32_t*)&ts->r11);
+ bswap32s((uint32_t*)&ts->r12);
+ bswap32s((uint32_t*)&ts->r13);
+ bswap32s((uint32_t*)&ts->r14);
+ bswap32s((uint32_t*)&ts->r15);
+ bswap32s((uint32_t*)&ts->r16);
+ bswap32s((uint32_t*)&ts->r17);
+ bswap32s((uint32_t*)&ts->r18);
+ bswap32s((uint32_t*)&ts->r19);
+ bswap32s((uint32_t*)&ts->r20);
+ bswap32s((uint32_t*)&ts->r21);
+ bswap32s((uint32_t*)&ts->r22);
+ bswap32s((uint32_t*)&ts->r23);
+ bswap32s((uint32_t*)&ts->r24);
+ bswap32s((uint32_t*)&ts->r25);
+ bswap32s((uint32_t*)&ts->r26);
+ bswap32s((uint32_t*)&ts->r27);
+ bswap32s((uint32_t*)&ts->r28);
+ bswap32s((uint32_t*)&ts->r29);
+ bswap32s((uint32_t*)&ts->r30);
+ bswap32s((uint32_t*)&ts->r31);
+
+ bswap32s((uint32_t*)&ts->cr);
+ bswap32s((uint32_t*)&ts->xer);
+ bswap32s((uint32_t*)&ts->lr);
+ bswap32s((uint32_t*)&ts->ctr);
+ bswap32s((uint32_t*)&ts->mq);
+
+ bswap32s((uint32_t*)&ts->vrsave);
+}
+
+#define target_thread_state mach_ppc_thread_state
+#define TARGET_CPU_TYPE CPU_TYPE_POWERPC
+#define TARGET_CPU_NAME "PowerPC"
+#endif
+
+struct target_thread_command {
+ unsigned long cmd; /* LC_THREAD or LC_UNIXTHREAD */
+ unsigned long cmdsize; /* total size of this command */
+ unsigned long flavor; /* flavor of thread state */
+ unsigned long count; /* count of longs in thread state */
+ struct target_thread_state state; /* thread state for this flavor */
+};
+
+void bswap_tc(struct target_thread_command *tc)
+{
+ bswap32s((uint32_t*)(&tc->flavor));
+ bswap32s((uint32_t*)&tc->count);
+#if defined(TARGET_I386)
+ bswap_i386_thread_state(&tc->state);
+#elif defined(TARGET_PPC)
+ bswap_ppc_thread_state(&tc->state);
+#else
+# error unknown TARGET_CPU_TYPE
+#endif
+}
+
+void bswap_mh(struct mach_header *mh)
+{
+ bswap32s((uint32_t*)(&mh->magic));
+ bswap32s((uint32_t*)&mh->cputype);
+ bswap32s((uint32_t*)&mh->cpusubtype);
+ bswap32s((uint32_t*)&mh->filetype);
+ bswap32s((uint32_t*)&mh->ncmds);
+ bswap32s((uint32_t*)&mh->sizeofcmds);
+ bswap32s((uint32_t*)&mh->flags);
+}
+
+void bswap_lc(struct load_command *lc)
+{
+ bswap32s((uint32_t*)&lc->cmd);
+ bswap32s((uint32_t*)&lc->cmdsize);
+}
+
+
+void bswap_fh(struct fat_header *fh)
+{
+ bswap32s((uint32_t*)&fh->magic);
+ bswap32s((uint32_t*)&fh->nfat_arch);
+}
+
+void bswap_fa(struct fat_arch *fa)
+{
+ bswap32s((uint32_t*)&fa->cputype);
+ bswap32s((uint32_t*)&fa->cpusubtype);
+ bswap32s((uint32_t*)&fa->offset);
+ bswap32s((uint32_t*)&fa->size);
+ bswap32s((uint32_t*)&fa->align);
+}
+
+void bswap_segcmd(struct segment_command *sc)
+{
+ bswap32s((uint32_t*)&sc->vmaddr);
+ bswap32s((uint32_t*)&sc->vmsize);
+ bswap32s((uint32_t*)&sc->fileoff);
+ bswap32s((uint32_t*)&sc->filesize);
+ bswap32s((uint32_t*)&sc->maxprot);
+ bswap32s((uint32_t*)&sc->initprot);
+ bswap32s((uint32_t*)&sc->nsects);
+ bswap32s((uint32_t*)&sc->flags);
+}
+
+void bswap_symtabcmd(struct symtab_command *stc)
+{
+ bswap32s((uint32_t*)&stc->cmd);
+ bswap32s((uint32_t*)&stc->cmdsize);
+ bswap32s((uint32_t*)&stc->symoff);
+ bswap32s((uint32_t*)&stc->nsyms);
+ bswap32s((uint32_t*)&stc->stroff);
+ bswap32s((uint32_t*)&stc->strsize);
+}
+
+void bswap_sym(struct nlist *n)
+{
+ bswap32s((uint32_t*)&n->n_un.n_strx);
+ bswap16s((uint16_t*)&n->n_desc);
+ bswap32s((uint32_t*)&n->n_value);
+}
+
+int load_thread(struct mach_header *mh, struct target_thread_command *tc, struct target_pt_regs * regs, int fd, int mh_pos, int need_bswap)
+{
+ int entry;
+ if(need_bswap)
+ bswap_tc(tc);
+#if defined(TARGET_I386)
+ entry = tc->state.eip;
+ DPRINTF(" eax 0x%.8x\n ebx 0x%.8x\n ecx 0x%.8x\n edx 0x%.8x\n edi 0x%.8x\n esi 0x%.8x\n ebp 0x%.8x\n esp 0x%.8x\n ss 0x%.8x\n eflags 0x%.8x\n eip 0x%.8x\n cs 0x%.8x\n ds 0x%.8x\n es 0x%.8x\n fs 0x%.8x\n gs 0x%.8x\n",
+ tc->state.eax, tc->state.ebx, tc->state.ecx, tc->state.edx, tc->state.edi, tc->state.esi, tc->state.ebp,
+ tc->state.esp, tc->state.ss, tc->state.eflags, tc->state.eip, tc->state.cs, tc->state.ds, tc->state.es,
+ tc->state.fs, tc->state.gs );
+#define reg_copy(reg) regs->reg = tc->state.reg
+ if(regs)
+ {
+ reg_copy(eax);
+ reg_copy(ebx);
+ reg_copy(ecx);
+ reg_copy(edx);
+
+ reg_copy(edi);
+ reg_copy(esi);
+
+ reg_copy(ebp);
+ reg_copy(esp);
+
+ reg_copy(eflags);
+ reg_copy(eip);
+ /*
+ reg_copy(ss);
+ reg_copy(cs);
+ reg_copy(ds);
+ reg_copy(es);
+ reg_copy(fs);
+ reg_copy(gs);*/
+ }
+#undef reg_copy
+#elif defined(TARGET_PPC)
+ entry = tc->state.srr0;
+#endif
+ DPRINTF("load_thread: entry 0x%x\n", entry);
+ return entry;
+}
+
+int load_dylinker(struct mach_header *mh, struct dylinker_command *dc, int fd, int mh_pos, int need_bswap)
+{
+ int size;
+ char * dylinker_name;
+ size = dc->cmdsize - sizeof(struct dylinker_command);
+
+ if(need_bswap)
+ dylinker_name = (char*)(bswap_32(dc->name.offset)+(int)dc);
+ else
+ dylinker_name = (char*)((dc->name.offset)+(int)dc);
+
+#ifdef OVERRIDE_DYLINKER
+ dylinker_name = DYLINKER_NAME;
+#else
+ if(asprintf(&dylinker_name, "%s%s", interp_prefix, dylinker_name) == -1)
+ qerror("can't allocate the new dylinker name\n");
+#endif
+
+ DPRINTF("dylinker_name %s\n", dylinker_name);
+ return load_object(dylinker_name, NULL, NULL);
+}
+
+int load_segment(struct mach_header *mh, struct segment_command *sc, int fd, int mh_pos, int need_bswap, int fixed, int slide)
+{
+ unsigned long addr = sc->vmaddr;
+ unsigned long size = sc->filesize;
+ unsigned long error = 0;
+
+ if(need_bswap)
+ bswap_segcmd(sc);
+
+ if(sc->vmaddr == 0)
+ {
+ DPRINTF("load_segment: sc->vmaddr == 0 returning\n");
+ return -1;
+ }
+
+ if (strcmp(sc->segname, "__PAGEZERO") == 0)
+ {
+ DPRINTF("load_segment: __PAGEZERO returning\n");
+ return -1;
+ }
+
+ /* Right now mmap memory */
+ /* XXX: should check to see that the space is free, because MAP_FIXED is dangerous */
+ DPRINTF("load_segment: mmaping %s to 0x%x-(0x%x|0x%x) + 0x%x\n", sc->segname, sc->vmaddr, sc->filesize, sc->vmsize, slide);
+
+ if(sc->filesize > 0)
+ {
+ int opt = 0;
+
+ if(fixed)
+ opt |= MAP_FIXED;
+
+ DPRINTF("sc->vmaddr 0x%x slide 0x%x add 0x%x\n", slide, sc->vmaddr, sc->vmaddr+slide);
+
+ addr = target_mmap(sc->vmaddr+slide, sc->filesize, sc->initprot, opt, fd, mh_pos + sc->fileoff);
+
+ if(addr==-1)
+ qerror("load_segment: can't mmap at 0x%x\n", sc->vmaddr+slide);
+
+ error = addr-sc->vmaddr;
+ }
+ else
+ {
+ addr = sc->vmaddr+slide;
+ error = slide;
+ }
+
+ if(sc->vmsize > sc->filesize)
+ {
+ addr += sc->filesize;
+ size = sc->vmsize-sc->filesize;
+ addr = target_mmap(addr, size, sc->initprot, MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if(addr==-1)
+ qerror("load_segment: can't mmap at 0x%x\n", sc->vmaddr+slide);
+ }
+
+ return error;
+}
+
+void *load_data(int fd, long offset, unsigned int size)
+{
+ char *data;
+
+ data = malloc(size);
+ if (!data)
+ return NULL;
+ lseek(fd, offset, SEEK_SET);
+ if (read(fd, data, size) != size) {
+ free(data);
+ return NULL;
+ }
+ return data;
+}
+
+/* load a mach-o object file */
+int load_object(const char *filename, struct target_pt_regs * regs, void ** mh)
+{
+ int need_bswap = 0;
+ int entry_point = 0;
+ int dyld_entry_point = 0;
+ int slide, mmapfixed;
+ int fd;
+ struct load_command *lcmds, *lc;
+ int is_fat = 0;
+ unsigned int i, magic;
+ int mach_hdr_pos = 0;
+ struct mach_header mach_hdr;
+
+ /* for symbol lookup whith -d flag. */
+ struct symtab_command * symtabcmd = 0;
+ struct nlist_extended *symtab, *sym;
+ struct nlist *symtab_std, *syment;
+ char *strtab;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ qerror("can't open file '%s'", filename);
+
+ /* Read magic header. */
+ if (read(fd, &magic, sizeof (magic)) != sizeof (magic))
+ qerror("unable to read Magic of '%s'", filename);
+
+ /* Check Mach identification. */
+ if(magic == MH_MAGIC)
+ {
+ is_fat = 0;
+ need_bswap = 0;
+ } else if (magic == MH_CIGAM)
+ {
+ is_fat = 0;
+ need_bswap = 1;
+ } else if (magic == FAT_MAGIC)
+ {
+ is_fat = 1;
+ need_bswap = 0;
+ } else if (magic == FAT_CIGAM)
+ {
+ is_fat = 1;
+ need_bswap = 1;
+ }
+ else
+ qerror("Not a Mach-O file.", filename);
+
+ DPRINTF("loading %s %s...\n", filename, is_fat ? "[FAT]": "[REGULAR]");
+ if(is_fat)
+ {
+ int found = 0;
+ struct fat_header fh;
+ struct fat_arch *fa;
+
+ lseek(fd, 0, SEEK_SET);
+
+ /* Read Fat header. */
+ if (read(fd, &fh, sizeof (fh)) != sizeof (fh))
+ qerror("unable to read file header");
+
+ if(need_bswap)
+ bswap_fh(&fh);
+
+ /* Read Fat Arch. */
+ fa = malloc(sizeof(struct fat_arch)*fh.nfat_arch);
+
+ if (read(fd, fa, sizeof(struct fat_arch)*fh.nfat_arch) != sizeof(struct fat_arch)*fh.nfat_arch)
+ qerror("unable to read file header");
+
+ for( i = 0; i < fh.nfat_arch; i++, fa++)
+ {
+ if(need_bswap)
+ bswap_fa(fa);
+ if(fa->cputype == TARGET_CPU_TYPE)
+ {
+ mach_hdr_pos = fa->offset;
+ lseek(fd, mach_hdr_pos, SEEK_SET);
+
+ /* Read Mach header. */
+
+ if (read(fd, &mach_hdr, sizeof(struct mach_header)) != sizeof (struct mach_header))
+ qerror("unable to read file header");
+
+ if(mach_hdr.magic == MH_MAGIC)
+ need_bswap = 0;
+ else if (mach_hdr.magic == MH_CIGAM)
+ need_bswap = 1;
+ else
+ qerror("Invalid mach header in Fat Mach-O File");
+ found = 1;
+ break;
+ }
+ }
+ if(!found)
+ qerror("%s: No %s CPU found in FAT Header", filename, TARGET_CPU_NAME);
+ }
+ else
+ {
+ lseek(fd, 0, SEEK_SET);
+ /* Read Mach header */
+ if (read(fd, &mach_hdr, sizeof (mach_hdr)) != sizeof (mach_hdr))
+ qerror("%s: unable to read file header", filename);
+ }
+
+ if(need_bswap)
+ bswap_mh(&mach_hdr);
+
+ if ((mach_hdr.cputype) != TARGET_CPU_TYPE)
+ qerror("%s: Unsupported CPU 0x%x (only 0x%x(%s) supported)", filename, mach_hdr.cputype, TARGET_CPU_TYPE, TARGET_CPU_NAME);
+
+
+ switch(mach_hdr.filetype)
+ {
+ case MH_EXECUTE: break;
+ case MH_FVMLIB:
+ case MH_DYLIB:
+ case MH_DYLINKER: break;
+ default:
+ qerror("%s: Unsupported Mach type (0x%x)", filename, mach_hdr.filetype);
+ }
+
+ /* read segment headers */
+ lcmds = malloc(mach_hdr.sizeofcmds);
+
+ if(read(fd, lcmds, mach_hdr.sizeofcmds) != mach_hdr.sizeofcmds)
+ qerror("%s: unable to read load_command", filename);
+ slide = 0;
+ mmapfixed = 0;
+ for(i=0, lc = lcmds; i < (mach_hdr.ncmds) ; i++)
+ {
+
+ if(need_bswap)
+ bswap_lc(lc);
+ switch(lc->cmd)
+ {
+ case LC_SEGMENT:
+ /* The main_exe can't be relocated */
+ if(mach_hdr.filetype == MH_EXECUTE)
+ mmapfixed = 1;
+
+ slide = load_segment(&mach_hdr, (struct segment_command*)lc, fd, mach_hdr_pos, need_bswap, mmapfixed, slide);
+
+ /* other segment must be mapped according to slide exactly, if load_segment did something */
+ if(slide != -1)
+ mmapfixed = 1;
+ else
+ slide = 0; /* load_segment didn't map the segment */
+
+ if(mach_hdr.filetype == MH_EXECUTE && slide != 0)
+ qerror("%s: Warning executable can't be mapped at the right address (offset: 0x%x)\n", filename, slide);
+
+ if(strcmp(((struct segment_command*)(lc))->segname, "__TEXT") == 0)
+ {
+ /* Text section */
+ if(mach_hdr.filetype == MH_EXECUTE)
+ {
+ /* return the mach_header */
+ *mh = (void*)(((struct segment_command*)(lc))->vmaddr + slide);
+ }
+ else
+ {
+ /* it is dyld save the section for gdb, we will be interested in dyld symbol
+ while debuging */
+ macho_text_sect = (void*)(((struct segment_command*)(lc))->vmaddr + slide);
+ macho_offset = slide;
+ }
+ }
+ break;
+ case LC_LOAD_DYLINKER:
+ dyld_entry_point = load_dylinker( &mach_hdr, (struct dylinker_command*)lc, fd, mach_hdr_pos, need_bswap );
+ break;
+ case LC_LOAD_DYLIB:
+ /* dyld will do that for us */
+ break;
+ case LC_THREAD:
+ case LC_UNIXTHREAD:
+ {
+ struct target_pt_regs * _regs;
+ if(mach_hdr.filetype == MH_DYLINKER)
+ _regs = regs;
+ else
+ _regs = 0;
+ entry_point = load_thread( &mach_hdr, (struct target_thread_command*)lc, _regs, fd, mach_hdr_pos, need_bswap );
+ }
+ break;
+ case LC_SYMTAB:
+ /* Save the symtab and strtab */
+ symtabcmd = (struct symtab_command *)lc;
+ break;
+ case LC_ID_DYLINKER:
+ case LC_ID_DYLIB:
+ case LC_UUID:
+ case LC_DYSYMTAB:
+ case LC_TWOLEVEL_HINTS:
+ case LC_PREBIND_CKSUM:
+ case LC_SUB_LIBRARY:
+ break;
+ default: fprintf(stderr, "warning: unkown command 0x%x in '%s'\n", lc->cmd, filename);
+ }
+ lc = (struct load_command*)((int)(lc)+(lc->cmdsize));
+ }
+
+ if(symtabcmd)
+ {
+ if(need_bswap)
+ bswap_symtabcmd(symtabcmd);
+
+ symtab_std = load_data(fd, symtabcmd->symoff+mach_hdr_pos, symtabcmd->nsyms * sizeof(struct nlist));
+ strtab = load_data(fd, symtabcmd->stroff+mach_hdr_pos, symtabcmd->strsize);
+
+ symtab = malloc(sizeof(struct nlist_extended) * symtabcmd->nsyms);
+
+ if(need_bswap)
+ {
+ for(i = 0, syment = symtab_std; i < symtabcmd->nsyms; i++, syment++)
+ bswap_sym(syment);
+ }
+
+ for(i = 0, sym = symtab, syment = symtab_std; i < symtabcmd->nsyms; i++, sym++, syment++)
+ {
+ struct nlist *sym_follow, *sym_next = 0;
+ unsigned int j;
+ memset(sym, 0, sizeof(*sym));
+
+ sym->n_type = syment->n_type;
+ if ( syment->n_type & N_STAB ) /* Debug symbols are skipped */
+ continue;
+
+ memcpy(sym, syment, sizeof(*syment));
+
+ /* Find the following symbol in order to get the current symbol size */
+ for(j = 0, sym_follow = symtab_std; j < symtabcmd->nsyms; j++, sym_follow++) {
+ if ( sym_follow->n_type & N_STAB || !(sym_follow->n_value > sym->st_value))
+ continue;
+ if(!sym_next) {
+ sym_next = sym_follow;
+ continue;
+ }
+ if(!(sym_next->n_value > sym_follow->n_value))
+ continue;
+ sym_next = sym_follow;
+ }
+ if(sym_next)
+ sym->st_size = sym_next->n_value - sym->st_value;
+ else
+ sym->st_size = 10; /* XXX: text_sec_hdr->size + text_sec_hdr->offset - sym->st_value; */
+
+ sym->st_value += slide;
+ }
+
+ free((void*)symtab_std);
+
+ {
+ DPRINTF("saving symtab of %s (%d symbol(s))\n", filename, symtabcmd->nsyms);
+ struct syminfo *s;
+ s = malloc(sizeof(*s));
+ s->disas_symtab = symtab;
+ s->disas_strtab = strtab;
+ s->disas_num_syms = symtabcmd->nsyms;
+ s->next = syminfos;
+ syminfos = s;
+ }
+ }
+ close(fd);
+ if(mach_hdr.filetype == MH_EXECUTE && dyld_entry_point)
+ return dyld_entry_point;
+ else
+ return entry_point+slide;
+}
+
+extern unsigned long stack_size;
+
+unsigned long setup_arg_pages(void * mh, char ** argv, char ** env)
+{
+ unsigned long stack_base, error, size;
+ int i;
+ int * stack;
+ int argc, envc;
+
+ /* Create enough stack to hold everything. If we don't use
+ * it for args, we'll use it for something else...
+ */
+ size = stack_size;
+
+ error = target_mmap(0,
+ size + qemu_host_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (error == -1)
+ qerror("stk mmap");
+
+ /* we reserve one extra page at the top of the stack as guard */
+ target_mprotect(error + size, qemu_host_page_size, PROT_NONE);
+
+ stack_base = error + size;
+ stack = (void*)stack_base;
+/*
+ * | STRING AREA |
+ * +-------------+
+ * | 0 |
+* +-------------+
+ * | apple[n] |
+ * +-------------+
+ * :
+ * +-------------+
+ * | apple[0] |
+ * +-------------+
+ * | 0 |
+ * +-------------+
+ * | env[n] |
+ * +-------------+
+ * :
+ * :
+ * +-------------+
+ * | env[0] |
+ * +-------------+
+ * | 0 |
+ * +-------------+
+ * | arg[argc-1] |
+ * +-------------+
+ * :
+ * :
+ * +-------------+
+ * | arg[0] |
+ * +-------------+
+ * | argc |
+ * +-------------+
+ * sp-> | mh | address of where the a.out's file offset 0 is in memory
+ * +-------------+
+*/
+ /* Construct the stack Stack grows down */
+ stack--;
+
+ /* XXX: string should go up there */
+
+ *stack = 0;
+ stack--;
+
+ /* Push the absolute path of our executable */
+ DPRINTF("pushing apple %s (0x%x)\n", (char*)argv[0], (int)argv[0]);
+ stl(stack, (int) argv[0]);
+
+ stack--;
+
+ stl(stack, 0);
+ stack--;
+
+ /* Get envc */
+ for(envc = 0; env[envc]; envc++);
+
+ for(i = envc-1; i >= 0; i--)
+ {
+ DPRINTF("pushing env %s (0x%x)\n", (char*)env[i], (int)env[i]);
+ stl(stack, (int)env[i]);
+ stack--;
+
+ /* XXX: remove that when string will be on top of the stack */
+ page_set_flags((int)env[i], (int)(env[i]+strlen(env[i])), PROT_READ | PAGE_VALID);
+ }
+
+ /* Add on the stack the interp_prefix choosen if so */
+ if(interp_prefix[0])
+ {
+ char *dyld_root;
+ asprintf(&dyld_root, "DYLD_ROOT_PATH=%s", interp_prefix);
+ page_set_flags((int)dyld_root, (int)(dyld_root+strlen(interp_prefix)+1), PROT_READ | PAGE_VALID);
+
+ stl(stack, (int)dyld_root);
+ stack--;
+ }
+
+#ifdef DONT_USE_DYLD_SHARED_MAP
+ {
+ char *shared_map_mode;
+ asprintf(&shared_map_mode, "DYLD_SHARED_REGION=avoid");
+ page_set_flags((int)shared_map_mode, (int)(shared_map_mode+strlen(shared_map_mode)+1), PROT_READ | PAGE_VALID);
+
+ stl(stack, (int)shared_map_mode);
+ stack--;
+ }
+#endif
+
+#ifdef ACTIVATE_DYLD_TRACE
+ char * extra_env_static[] = {"DYLD_DEBUG_TRACE=yes",
+ "DYLD_PREBIND_DEBUG=3", "DYLD_UNKNOW_TRACE=yes",
+ "DYLD_PRINT_INITIALIZERS=yes",
+ "DYLD_PRINT_SEGMENTS=yes", "DYLD_PRINT_REBASINGS=yes", "DYLD_PRINT_BINDINGS=yes", "DYLD_PRINT_INITIALIZERS=yes", "DYLD_PRINT_WARNINGS=yes" };
+
+ char ** extra_env = malloc(sizeof(extra_env_static));
+ bcopy(extra_env_static, extra_env, sizeof(extra_env_static));
+ page_set_flags((int)extra_env, (int)((void*)extra_env+sizeof(extra_env_static)), PROT_READ | PAGE_VALID);
+
+ for(i = 0; i<9; i++)
+ {
+ DPRINTF("pushing (extra) env %s (0x%x)\n", (char*)extra_env[i], (int)extra_env[i]);
+ stl(stack, (int) extra_env[i]);
+ stack--;
+ }
+#endif
+
+ stl(stack, 0);
+ stack--;
+
+ /* Get argc */
+ for(argc = 0; argv[argc]; argc++);
+
+ for(i = argc-1; i >= 0; i--)
+ {
+ DPRINTF("pushing arg %s (0x%x)\n", (char*)argv[i], (int)argv[i]);
+ stl(stack, (int) argv[i]);
+ stack--;
+
+ /* XXX: remove that when string will be on top of the stack */
+ page_set_flags((int)argv[i], (int)(argv[i]+strlen(argv[i])), PROT_READ | PAGE_VALID);
+ }
+
+ DPRINTF("pushing argc %d \n", argc);
+ stl(stack, argc);
+ stack--;
+
+ DPRINTF("pushing mh 0x%x \n", (int)mh);
+ stl(stack, (int) mh);
+
+ /* Stack points on the mh */
+ return (unsigned long)stack;
+}
+
+int mach_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs)
+{
+ int entrypoint, stack;
+ void * mh; /* the Mach Header that will be used by dyld */
+
+ DPRINTF("mach_exec at 0x%x\n", (int)mach_exec);
+
+ entrypoint = load_object(filename, regs, &mh);
+ stack = setup_arg_pages(mh, argv, envp);
+#if defined(TARGET_I386)
+ regs->eip = entrypoint;
+ regs->esp = stack;
+#elif defined(TARGET_PPC)
+ regs->nip = entrypoint;
+ regs->gpr[1] = stack;
+#endif
+ DPRINTF("mach_exec returns eip set to 0x%x esp 0x%x mh 0x%x\n", entrypoint, stack, (int)mh);
+
+ if(!entrypoint)
+ qerror("%s: no entry point!\n", filename);
+
+ return 0;
+}
diff --git a/darwin-user/main.c b/darwin-user/main.c
new file mode 100644
index 0000000..175e12f
--- /dev/null
+++ b/darwin-user/main.c
@@ -0,0 +1,1015 @@
+/*
+ * qemu user main
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Pierre d'Herbemont
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/syscall.h>
+#include <sys/mman.h>
+
+#include "qemu.h"
+
+#define DEBUG_LOGFILE "/tmp/qemu.log"
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+# define environ (*_NSGetEnviron())
+#endif
+
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+
+int singlestep;
+
+const char *interp_prefix = "";
+
+asm(".zerofill __STD_PROG_ZONE, __STD_PROG_ZONE, __std_prog_zone, 0x0dfff000");
+
+/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
+ we allocate a bigger stack. Need a better solution, for example
+ by remapping the process stack directly at the right place */
+unsigned long stack_size = 512 * 1024;
+
+void qerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+void gemu_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ return -1;
+}
+#ifdef TARGET_PPC
+
+static inline uint64_t cpu_ppc_get_tb (CPUState *env)
+{
+ /* TO FIX */
+ return 0;
+}
+
+uint64_t cpu_ppc_load_tbl (CPUState *env)
+{
+ return cpu_ppc_get_tb(env);
+}
+
+uint32_t cpu_ppc_load_tbu (CPUState *env)
+{
+ return cpu_ppc_get_tb(env) >> 32;
+}
+
+uint64_t cpu_ppc_load_atbl (CPUState *env)
+{
+ return cpu_ppc_get_tb(env);
+}
+
+uint32_t cpu_ppc_load_atbu (CPUState *env)
+{
+ return cpu_ppc_get_tb(env) >> 32;
+}
+
+uint32_t cpu_ppc601_load_rtcu (CPUState *env)
+{
+ cpu_ppc_load_tbu(env);
+}
+
+uint32_t cpu_ppc601_load_rtcl (CPUState *env)
+{
+ return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
+}
+
+/* XXX: to be fixed */
+int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
+{
+ return -1;
+}
+
+int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
+{
+ return -1;
+}
+
+#define EXCP_DUMP(env, fmt, ...) \
+do { \
+ fprintf(stderr, fmt , ## __VA_ARGS__); \
+ cpu_dump_state(env, stderr, fprintf, 0); \
+ qemu_log(fmt, ## __VA_ARGS__); \
+ log_cpu_state(env, 0); \
+} while (0)
+
+void cpu_loop(CPUPPCState *env)
+{
+ int trapnr;
+ uint32_t ret;
+ target_siginfo_t info;
+
+ for(;;) {
+ trapnr = cpu_ppc_exec(env);
+ switch(trapnr) {
+ case POWERPC_EXCP_NONE:
+ /* Just go on */
+ break;
+ case POWERPC_EXCP_CRITICAL: /* Critical input */
+ cpu_abort(env, "Critical interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ cpu_abort(env, "Machine check exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+#ifndef DAR
+/* To deal with multiple qemu header version as host for the darwin-user code */
+# define DAR SPR_DAR
+#endif
+ EXCP_DUMP(env, "Invalid data memory access: 0x" TARGET_FMT_lx "\n",
+ env->spr[SPR_DAR]);
+ /* Handle this via the gdb */
+ gdb_handlesig (env, SIGSEGV);
+
+ info.si_addr = (void*)env->nip;
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ EXCP_DUMP(env, "Invalid instruction fetch: 0x\n" TARGET_FMT_lx "\n",
+ env->spr[SPR_DAR]);
+ /* Handle this via the gdb */
+ gdb_handlesig (env, SIGSEGV);
+
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ cpu_abort(env, "External interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ EXCP_DUMP(env, "Unaligned memory access\n");
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ /* XXX: check this */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ EXCP_DUMP(env, "Floating point program exception\n");
+ /* Set FX */
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case POWERPC_EXCP_FP_OX:
+ info.si_code = FPE_FLTOVF;
+ break;
+ case POWERPC_EXCP_FP_UX:
+ info.si_code = FPE_FLTUND;
+ break;
+ case POWERPC_EXCP_FP_ZX:
+ case POWERPC_EXCP_FP_VXZDZ:
+ info.si_code = FPE_FLTDIV;
+ break;
+ case POWERPC_EXCP_FP_XX:
+ info.si_code = FPE_FLTRES;
+ break;
+ case POWERPC_EXCP_FP_VXSOFT:
+ info.si_code = FPE_FLTINV;
+ break;
+ case POWERPC_EXCP_FP_VXSNAN:
+ case POWERPC_EXCP_FP_VXISI:
+ case POWERPC_EXCP_FP_VXIDI:
+ case POWERPC_EXCP_FP_VXIMZ:
+ case POWERPC_EXCP_FP_VXVC:
+ case POWERPC_EXCP_FP_VXSQRT:
+ case POWERPC_EXCP_FP_VXCVI:
+ info.si_code = FPE_FLTSUB;
+ break;
+ default:
+ EXCP_DUMP(env, "Unknown floating point exception (%02x)\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_INVAL:
+ EXCP_DUMP(env, "Invalid instruction\n");
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case POWERPC_EXCP_INVAL_INVAL:
+ info.si_code = ILL_ILLOPC;
+ break;
+ case POWERPC_EXCP_INVAL_LSWX:
+ info.si_code = ILL_ILLOPN;
+ break;
+ case POWERPC_EXCP_INVAL_SPR:
+ info.si_code = ILL_PRVREG;
+ break;
+ case POWERPC_EXCP_INVAL_FP:
+ info.si_code = ILL_COPROC;
+ break;
+ default:
+ EXCP_DUMP(env, "Unknown invalid operation (%02x)\n",
+ env->error_code & 0xF);
+ info.si_code = ILL_ILLADR;
+ break;
+ }
+ /* Handle this via the gdb */
+ gdb_handlesig (env, SIGSEGV);
+ break;
+ case POWERPC_EXCP_PRIV:
+ EXCP_DUMP(env, "Privilege violation\n");
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case POWERPC_EXCP_PRIV_OPC:
+ info.si_code = ILL_PRVOPC;
+ break;
+ case POWERPC_EXCP_PRIV_REG:
+ info.si_code = ILL_PRVREG;
+ break;
+ default:
+ EXCP_DUMP(env, "Unknown privilege violation (%02x)\n",
+ env->error_code & 0xF);
+ info.si_code = ILL_PRVOPC;
+ break;
+ }
+ break;
+ case POWERPC_EXCP_TRAP:
+ cpu_abort(env, "Tried to call a TRAP\n");
+ break;
+ default:
+ /* Should not happen ! */
+ cpu_abort(env, "Unknown program exception (%02x)\n",
+ env->error_code);
+ break;
+ }
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
+ EXCP_DUMP(env, "No floating point allowed\n");
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_COPROC;
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ cpu_abort(env, "Syscall exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
+ EXCP_DUMP(env, "No APU instruction allowed\n");
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_COPROC;
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ cpu_abort(env, "Decrementer interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
+ cpu_abort(env, "Fix interval timer interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
+ cpu_abort(env, "Watchdog timer interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DTLB: /* Data TLB error */
+ cpu_abort(env, "Data TLB exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_ITLB: /* Instruction TLB error */
+ cpu_abort(env, "Instruction TLB exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DEBUG: /* Debug interrupt */
+ gdb_handlesig (env, SIGTRAP);
+ break;
+ case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */
+ EXCP_DUMP(env, "No SPE/floating-point instruction allowed\n");
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_COPROC;
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */
+ cpu_abort(env, "Embedded floating-point data IRQ not handled\n");
+ break;
+ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */
+ cpu_abort(env, "Embedded floating-point round IRQ not handled\n");
+ break;
+ case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */
+ cpu_abort(env, "Performance monitor exception not handled\n");
+ break;
+ case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
+ cpu_abort(env, "Doorbell interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
+ cpu_abort(env, "Doorbell critical interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ cpu_abort(env, "Reset interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DSEG: /* Data segment exception */
+ cpu_abort(env, "Data segment exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_ISEG: /* Instruction segment exception */
+ cpu_abort(env, "Instruction segment exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
+ cpu_abort(env, "Hypervisor decrementer interrupt "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ /* Nothing to do:
+ * we use this exception to emulate step-by-step execution mode.
+ */
+ break;
+ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
+ cpu_abort(env, "Hypervisor data storage exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */
+ cpu_abort(env, "Hypervisor instruction storage exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */
+ cpu_abort(env, "Hypervisor data segment exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */
+ cpu_abort(env, "Hypervisor instruction segment exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_VPU: /* Vector unavailable exception */
+ EXCP_DUMP(env, "No Altivec instructions allowed\n");
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_COPROC;
+ info.si_addr = (void*)(env->nip - 4);
+ queue_signal(info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */
+ cpu_abort(env, "Programable interval timer interrupt "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_IO: /* IO error exception */
+ cpu_abort(env, "IO error exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_RUNM: /* Run mode exception */
+ cpu_abort(env, "Run mode exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_EMUL: /* Emulation trap exception */
+ cpu_abort(env, "Emulation trap exception not handled\n");
+ break;
+ case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
+ cpu_abort(env, "Instruction fetch TLB exception "
+ "while in user-mode. Aborting");
+ break;
+ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
+ cpu_abort(env, "Data load TLB exception while in user-mode. "
+ "Aborting");
+ break;
+ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
+ cpu_abort(env, "Data store TLB exception while in user-mode. "
+ "Aborting");
+ break;
+ case POWERPC_EXCP_FPA: /* Floating-point assist exception */
+ cpu_abort(env, "Floating-point assist exception not handled\n");
+ break;
+ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
+ cpu_abort(env, "Instruction address breakpoint exception "
+ "not handled\n");
+ break;
+ case POWERPC_EXCP_SMI: /* System management interrupt */
+ cpu_abort(env, "System management interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_THERM: /* Thermal interrupt */
+ cpu_abort(env, "Thermal interrupt interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */
+ cpu_abort(env, "Performance monitor exception not handled\n");
+ break;
+ case POWERPC_EXCP_VPUA: /* Vector assist exception */
+ cpu_abort(env, "Vector assist exception not handled\n");
+ break;
+ case POWERPC_EXCP_SOFTP: /* Soft patch exception */
+ cpu_abort(env, "Soft patch exception not handled\n");
+ break;
+ case POWERPC_EXCP_MAINT: /* Maintenance exception */
+ cpu_abort(env, "Maintenance exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_STOP: /* stop translation */
+ /* We did invalidate the instruction cache. Go on */
+ break;
+ case POWERPC_EXCP_BRANCH: /* branch instruction: */
+ /* We just stopped because of a branch. Go on */
+ break;
+ case POWERPC_EXCP_SYSCALL_USER:
+ /* system call in user-mode emulation */
+ /* system call */
+ if(((int)env->gpr[0]) <= SYS_MAXSYSCALL && ((int)env->gpr[0])>0)
+ ret = do_unix_syscall(env, env->gpr[0]/*, env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->gpr[7],
+ env->gpr[8], env->gpr[9], env->gpr[10]*/);
+ else if(((int)env->gpr[0])<0)
+ ret = do_mach_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->gpr[7],
+ env->gpr[8], env->gpr[9], env->gpr[10]);
+ else
+ ret = do_thread_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->gpr[7],
+ env->gpr[8], env->gpr[9], env->gpr[10]);
+
+ /* Unix syscall error signaling */
+ if(((int)env->gpr[0]) <= SYS_MAXSYSCALL && ((int)env->gpr[0])>0)
+ {
+ if( (int)ret < 0 )
+ env->nip += 0;
+ else
+ env->nip += 4;
+ }
+
+ /* Return value */
+ env->gpr[3] = ret;
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ default:
+ cpu_abort(env, "Unknown exception 0x%d. Aborting\n", trapnr);
+ break;
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+
+#ifdef TARGET_I386
+
+/***********************************************************/
+/* CPUX86 core interface */
+
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ return cpu_get_real_ticks();
+}
+
+void
+write_dt(void *ptr, unsigned long addr, unsigned long limit,
+ int flags)
+{
+ unsigned int e1, e2;
+ e1 = (addr << 16) | (limit & 0xffff);
+ e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
+ e2 |= flags;
+ stl((uint8_t *)ptr, e1);
+ stl((uint8_t *)ptr + 4, e2);
+}
+
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ unsigned long addr, unsigned int sel)
+{
+ unsigned int e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ stl((uint8_t *)ptr, e1);
+ stl((uint8_t *)ptr + 4, e2);
+}
+
+#define GDT_TABLE_SIZE 14
+#define LDT_TABLE_SIZE 15
+#define IDT_TABLE_SIZE 256
+#define TSS_SIZE 104
+uint64_t gdt_table[GDT_TABLE_SIZE];
+uint64_t ldt_table[LDT_TABLE_SIZE];
+uint64_t idt_table[IDT_TABLE_SIZE];
+uint32_t tss[TSS_SIZE];
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
+
+/* ABI convention: after a syscall if there was an error the CF flag is set */
+static inline void set_error(CPUX86State *env, int ret)
+{
+ if(ret<0)
+ env->eflags = env->eflags | 0x1;
+ else
+ env->eflags &= ~0x1;
+ env->regs[R_EAX] = ret;
+}
+
+void cpu_loop(CPUX86State *env)
+{
+ int trapnr;
+ int ret;
+ uint8_t *pc;
+ target_siginfo_t info;
+
+ for(;;) {
+ trapnr = cpu_x86_exec(env);
+ uint32_t *params = (uint32_t *)env->regs[R_ESP];
+ switch(trapnr) {
+ case 0x79: /* Our commpage hack back door exit is here */
+ do_commpage(env, env->eip, *(params + 1), *(params + 2),
+ *(params + 3), *(params + 4),
+ *(params + 5), *(params + 6),
+ *(params + 7), *(params + 8));
+ break;
+ case 0x81: /* mach syscall */
+ {
+ ret = do_mach_syscall(env, env->regs[R_EAX],
+ *(params + 1), *(params + 2),
+ *(params + 3), *(params + 4),
+ *(params + 5), *(params + 6),
+ *(params + 7), *(params + 8));
+ set_error(env, ret);
+ break;
+ }
+ case 0x90: /* unix backdoor */
+ {
+ /* after sysenter, stack is in R_ECX, new eip in R_EDX (sysexit will flip them back)*/
+ int saved_stack = env->regs[R_ESP];
+ env->regs[R_ESP] = env->regs[R_ECX];
+
+ ret = do_unix_syscall(env, env->regs[R_EAX]);
+
+ env->regs[R_ECX] = env->regs[R_ESP];
+ env->regs[R_ESP] = saved_stack;
+
+ set_error(env, ret);
+ break;
+ }
+ case 0x80: /* unix syscall */
+ {
+ ret = do_unix_syscall(env, env->regs[R_EAX]/*,
+ *(params + 1), *(params + 2),
+ *(params + 3), *(params + 4),
+ *(params + 5), *(params + 6),
+ *(params + 7), *(params + 8)*/);
+ set_error(env, ret);
+ break;
+ }
+ case 0x82: /* thread syscall */
+ {
+ ret = do_thread_syscall(env, env->regs[R_EAX],
+ *(params + 1), *(params + 2),
+ *(params + 3), *(params + 4),
+ *(params + 5), *(params + 6),
+ *(params + 7), *(params + 8));
+ set_error(env, ret);
+ break;
+ }
+ case EXCP0B_NOSEG:
+ case EXCP0C_STACK:
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_NOOP;
+ info.si_addr = 0;
+ gdb_handlesig (env, SIGBUS);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP0D_GPF:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_NOOP;
+ info.si_addr = 0;
+ gdb_handlesig (env, SIGSEGV);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP0E_PAGE:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ if (!(env->error_code & 1))
+ info.si_code = SEGV_MAPERR;
+ else
+ info.si_code = SEGV_ACCERR;
+ info.si_addr = (void*)env->cr[2];
+ gdb_handlesig (env, SIGSEGV);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP00_DIVZ:
+ /* division by zero */
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = FPE_INTDIV;
+ info.si_addr = (void*)env->eip;
+ gdb_handlesig (env, SIGFPE);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP01_SSTP:
+ case EXCP03_INT3:
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_BRKPT;
+ info.si_addr = (void*)env->eip;
+ gdb_handlesig (env, SIGTRAP);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP04_INTO:
+ case EXCP05_BOUND:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = SEGV_NOOP;
+ info.si_addr = 0;
+ gdb_handlesig (env, SIGSEGV);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP06_ILLOP:
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLOPN;
+ info.si_addr = (void*)env->eip;
+ gdb_handlesig (env, SIGILL);
+ queue_signal(info.si_signo, &info);
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TRAP_BRKPT;
+ queue_signal(info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ pc = (void*)(env->segs[R_CS].base + env->eip);
+ fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
+ (long)pc, trapnr);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+static void usage(void)
+{
+ printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n"
+ "usage: qemu-" TARGET_ARCH " [-h] [-d opts] [-L path] [-s size] program [arguments...]\n"
+ "Darwin CPU emulator (compiled for %s emulation)\n"
+ "\n"
+ "-h print this help\n"
+ "-L path set the %s library path (default='%s')\n"
+ "-s size set the stack size in bytes (default=%ld)\n"
+ "\n"
+ "debug options:\n"
+ "-d options activate log (logfile='%s')\n"
+ "-g wait for gdb on port 1234\n"
+ "-p pagesize set the host page size to 'pagesize'\n",
+ "-singlestep always run in singlestep mode\n"
+ TARGET_ARCH,
+ TARGET_ARCH,
+ interp_prefix,
+ stack_size,
+ DEBUG_LOGFILE);
+ exit(1);
+}
+
+/* XXX: currently only used for async signals (see signal.c) */
+CPUState *global_env;
+/* used only if single thread */
+CPUState *cpu_single_env = NULL;
+
+/* used to free thread contexts */
+TaskState *first_task_state;
+
+int main(int argc, char **argv)
+{
+ const char *filename;
+ struct target_pt_regs regs1, *regs = &regs1;
+ TaskState ts1, *ts = &ts1;
+ CPUState *env;
+ int optind;
+ short use_gdbstub = 0;
+ const char *r;
+ const char *cpu_model;
+
+ if (argc <= 1)
+ usage();
+
+ /* init debug */
+ cpu_set_log_filename(DEBUG_LOGFILE);
+
+ optind = 1;
+ for(;;) {
+ if (optind >= argc)
+ break;
+ r = argv[optind];
+ if (r[0] != '-')
+ break;
+ optind++;
+ r++;
+ if (!strcmp(r, "-")) {
+ break;
+ } else if (!strcmp(r, "d")) {
+ int mask;
+ CPULogItem *item;
+
+ if (optind >= argc)
+ break;
+
+ r = argv[optind++];
+ mask = cpu_str_to_log_mask(r);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+ } else if (!strcmp(r, "s")) {
+ r = argv[optind++];
+ stack_size = strtol(r, (char **)&r, 0);
+ if (stack_size <= 0)
+ usage();
+ if (*r == 'M')
+ stack_size *= 1024 * 1024;
+ else if (*r == 'k' || *r == 'K')
+ stack_size *= 1024;
+ } else if (!strcmp(r, "L")) {
+ interp_prefix = argv[optind++];
+ } else if (!strcmp(r, "p")) {
+ qemu_host_page_size = atoi(argv[optind++]);
+ if (qemu_host_page_size == 0 ||
+ (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
+ fprintf(stderr, "page size must be a power of two\n");
+ exit(1);
+ }
+ } else
+ if (!strcmp(r, "g")) {
+ use_gdbstub = 1;
+ } else if (!strcmp(r, "cpu")) {
+ cpu_model = argv[optind++];
+ if (strcmp(cpu_model, "?") == 0) {
+/* XXX: implement xxx_cpu_list for targets that still miss it */
+#if defined(cpu_list)
+ cpu_list(stdout, &fprintf);
+#endif
+ exit(1);
+ }
+ } else if (!strcmp(r, "singlestep")) {
+ singlestep = 1;
+ } else
+ {
+ usage();
+ }
+ }
+ if (optind >= argc)
+ usage();
+ filename = argv[optind];
+
+ /* Zero out regs */
+ memset(regs, 0, sizeof(struct target_pt_regs));
+
+ if (cpu_model == NULL) {
+#if defined(TARGET_I386)
+#ifdef TARGET_X86_64
+ cpu_model = "qemu64";
+#else
+ cpu_model = "qemu32";
+#endif
+#elif defined(TARGET_PPC)
+#ifdef TARGET_PPC64
+ cpu_model = "970";
+#else
+ cpu_model = "750";
+#endif
+#else
+#error unsupported CPU
+#endif
+ }
+
+ cpu_exec_init_all(0);
+ /* NOTE: we need to init the CPU at this stage to get
+ qemu_host_page_size */
+ env = cpu_init(cpu_model);
+ cpu_reset(env);
+
+ printf("Starting %s with qemu\n----------------\n", filename);
+
+ commpage_init();
+
+ if (mach_exec(filename, argv+optind, environ, regs) != 0) {
+ printf("Error loading %s\n", filename);
+ _exit(1);
+ }
+
+ syscall_init();
+ signal_init();
+ global_env = env;
+
+ /* build Task State */
+ memset(ts, 0, sizeof(TaskState));
+ env->opaque = ts;
+ ts->used = 1;
+
+#if defined(TARGET_I386)
+ cpu_x86_set_cpl(env, 3);
+
+ env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
+ env->hflags |= HF_PE_MASK;
+
+ if (env->cpuid_features & CPUID_SSE) {
+ env->cr[4] |= CR4_OSFXSR_MASK;
+ env->hflags |= HF_OSFXSR_MASK;
+ }
+
+ /* flags setup : we activate the IRQs by default as in user mode */
+ env->eflags |= IF_MASK;
+
+ /* darwin register setup */
+ env->regs[R_EAX] = regs->eax;
+ env->regs[R_EBX] = regs->ebx;
+ env->regs[R_ECX] = regs->ecx;
+ env->regs[R_EDX] = regs->edx;
+ env->regs[R_ESI] = regs->esi;
+ env->regs[R_EDI] = regs->edi;
+ env->regs[R_EBP] = regs->ebp;
+ env->regs[R_ESP] = regs->esp;
+ env->eip = regs->eip;
+
+ /* Darwin LDT setup */
+ /* 2 - User code segment
+ 3 - User data segment
+ 4 - User cthread */
+ bzero(ldt_table, LDT_TABLE_SIZE * sizeof(ldt_table[0]));
+ env->ldt.base = (uint32_t) ldt_table;
+ env->ldt.limit = sizeof(ldt_table) - 1;
+
+ write_dt(ldt_table + 2, 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+ write_dt(ldt_table + 3, 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+ write_dt(ldt_table + 4, 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+
+ /* Darwin GDT setup.
+ * has changed a lot between old Darwin/x86 (pre-Mac Intel) and Mac OS X/x86,
+ now everything is done via int 0x81(mach) int 0x82 (thread) and sysenter/sysexit(unix) */
+ bzero(gdt_table, sizeof(gdt_table));
+ env->gdt.base = (uint32_t)gdt_table;
+ env->gdt.limit = sizeof(gdt_table) - 1;
+
+ /* Set up a back door to handle sysenter syscalls (unix) */
+ char * syscallbackdoor = malloc(64);
+ page_set_flags((int)syscallbackdoor, (int)syscallbackdoor + 64, PROT_EXEC | PROT_READ | PAGE_VALID);
+
+ int i = 0;
+ syscallbackdoor[i++] = 0xcd;
+ syscallbackdoor[i++] = 0x90; /* int 0x90 */
+ syscallbackdoor[i++] = 0x0F;
+ syscallbackdoor[i++] = 0x35; /* sysexit */
+
+ /* Darwin sysenter/sysexit setup */
+ env->sysenter_cs = 0x1; //XXX
+ env->sysenter_eip = (int)syscallbackdoor;
+ env->sysenter_esp = (int)malloc(64);
+
+ /* Darwin TSS setup
+ This must match up with GDT[4] */
+ env->tr.base = (uint32_t) tss;
+ env->tr.limit = sizeof(tss) - 1;
+ env->tr.flags = DESC_P_MASK | (0x9 << DESC_TYPE_SHIFT);
+ stw(tss + 2, 0x10); // ss0 = 0x10 = GDT[2] = Kernel Data Segment
+
+ /* Darwin interrupt setup */
+ bzero(idt_table, sizeof(idt_table));
+ env->idt.base = (uint32_t) idt_table;
+ env->idt.limit = sizeof(idt_table) - 1;
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 3);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+ /* Syscalls are done via
+ int 0x80 (unix) (rarely used)
+ int 0x81 (mach)
+ int 0x82 (thread)
+ int 0x83 (diag) (not handled here)
+ sysenter/sysexit (unix) -> we redirect that to int 0x90 */
+ set_idt(0x79, 3); /* Commpage hack, here is our backdoor interrupt */
+ set_idt(0x80, 3); /* Unix Syscall */
+ set_idt(0x81, 3); /* Mach Syscalls */
+ set_idt(0x82, 3); /* thread Syscalls */
+
+ set_idt(0x90, 3); /* qemu-darwin-user's Unix syscalls backdoor */
+
+
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+ cpu_x86_load_seg(env, R_FS, __USER_DS);
+ cpu_x86_load_seg(env, R_GS, __USER_DS);
+
+#elif defined(TARGET_PPC)
+ {
+ int i;
+
+#if defined(TARGET_PPC64)
+#if defined(TARGET_ABI32)
+ env->msr &= ~((target_ulong)1 << MSR_SF);
+#else
+ env->msr |= (target_ulong)1 << MSR_SF;
+#endif
+#endif
+ env->nip = regs->nip;
+ for(i = 0; i < 32; i++) {
+ env->gpr[i] = regs->gpr[i];
+ }
+ }
+#else
+#error unsupported target CPU
+#endif
+
+ if (use_gdbstub) {
+ printf("Waiting for gdb Connection on port 1234...\n");
+ gdbserver_start (1234);
+ gdb_handlesig(env, 0);
+ }
+
+ cpu_loop(env);
+ /* never exits */
+ return 0;
+}
diff --git a/darwin-user/mmap.c b/darwin-user/mmap.c
new file mode 100644
index 0000000..d840b28
--- /dev/null
+++ b/darwin-user/mmap.c
@@ -0,0 +1,409 @@
+/*
+ * mmap support for qemu
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "qemu.h"
+
+//#define DEBUG_MMAP
+
+/* NOTE: all the constants are the HOST ones */
+int target_mprotect(unsigned long start, unsigned long len, int prot)
+{
+ unsigned long end, host_start, host_end, addr;
+ int prot1, ret;
+
+#ifdef DEBUG_MMAP
+ printf("mprotect: start=0x%lx len=0x%lx prot=%c%c%c\n", start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+#endif
+
+ if ((start & ~TARGET_PAGE_MASK) != 0)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
+ return -EINVAL;
+ if (len == 0)
+ return 0;
+
+ host_start = start & qemu_host_page_mask;
+ host_end = HOST_PAGE_ALIGN(end);
+ if (start > host_start) {
+ /* handle host page containing start */
+ prot1 = prot;
+ for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ if (host_end == host_start + qemu_host_page_size) {
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ end = host_end;
+ }
+ ret = mprotect((void *)host_start, qemu_host_page_size, prot1 & PAGE_BITS);
+ if (ret != 0)
+ return ret;
+ host_start += qemu_host_page_size;
+ }
+ if (end < host_end) {
+ prot1 = prot;
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ ret = mprotect((void *)(host_end - qemu_host_page_size), qemu_host_page_size,
+ prot1 & PAGE_BITS);
+ if (ret != 0)
+ return ret;
+ host_end -= qemu_host_page_size;
+ }
+
+ /* handle the pages in the middle */
+ if (host_start < host_end) {
+ ret = mprotect((void *)host_start, host_end - host_start, prot);
+ if (ret != 0)
+ return ret;
+ }
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ return 0;
+}
+
+/* map an incomplete host page */
+int mmap_frag(unsigned long host_start,
+ unsigned long start, unsigned long end,
+ int prot, int flags, int fd, unsigned long offset)
+{
+ unsigned long host_end, ret, addr;
+ int prot1, prot_new;
+
+ host_end = host_start + qemu_host_page_size;
+
+ /* get the protection of the target pages outside the mapping */
+ prot1 = 0;
+ for(addr = host_start; addr < host_end; addr++) {
+ if (addr < start || addr >= end)
+ prot1 |= page_get_flags(addr);
+ }
+
+ if (prot1 == 0) {
+ /* no page was there, so we allocate one */
+ ret = (long)mmap((void *)host_start, qemu_host_page_size, prot,
+ flags | MAP_ANONYMOUS, -1, 0);
+ if (ret == -1)
+ return ret;
+ }
+ prot1 &= PAGE_BITS;
+
+ prot_new = prot | prot1;
+ if (!(flags & MAP_ANONYMOUS)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+#ifndef __APPLE__
+ if ((flags & MAP_TYPE) == MAP_SHARED &&
+#else
+ if ((flags & MAP_SHARED) &&
+#endif
+ (prot & PROT_WRITE))
+ return -1;
+
+ /* adjust protection to be able to read */
+ if (!(prot1 & PROT_WRITE))
+ mprotect((void *)host_start, qemu_host_page_size, prot1 | PROT_WRITE);
+
+ /* read the corresponding file data */
+ pread(fd, (void *)start, end - start, offset);
+
+ /* put final protection */
+ if (prot_new != (prot1 | PROT_WRITE))
+ mprotect((void *)host_start, qemu_host_page_size, prot_new);
+ } else {
+ /* just update the protection */
+ if (prot_new != prot1) {
+ mprotect((void *)host_start, qemu_host_page_size, prot_new);
+ }
+ }
+ return 0;
+}
+
+/* NOTE: all the constants are the HOST ones */
+long target_mmap(unsigned long start, unsigned long len, int prot,
+ int flags, int fd, unsigned long offset)
+{
+ unsigned long ret, end, host_start, host_end, retaddr, host_offset, host_len;
+#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
+ static unsigned long last_start = 0x40000000;
+#endif
+
+#ifdef DEBUG_MMAP
+ {
+ printf("mmap: start=0x%lx len=0x%lx prot=%c%c%c flags=",
+ start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+ if (flags & MAP_FIXED)
+ printf("MAP_FIXED ");
+ if (flags & MAP_ANONYMOUS)
+ printf("MAP_ANON ");
+#ifndef MAP_TYPE
+# define MAP_TYPE 0x3
+#endif
+ switch(flags & MAP_TYPE) {
+ case MAP_PRIVATE:
+ printf("MAP_PRIVATE ");
+ break;
+ case MAP_SHARED:
+ printf("MAP_SHARED ");
+ break;
+ default:
+ printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
+ break;
+ }
+ printf("fd=%d offset=%lx\n", fd, offset);
+ }
+#endif
+
+ if (offset & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ return start;
+ host_start = start & qemu_host_page_mask;
+
+ if (!(flags & MAP_FIXED)) {
+#if defined(__alpha__) || defined(__sparc__) || defined(__x86_64__)
+ /* tell the kernel to search at the same place as i386 */
+ if (host_start == 0) {
+ host_start = last_start;
+ last_start += HOST_PAGE_ALIGN(len);
+ }
+#endif
+ if (qemu_host_page_size != qemu_real_host_page_size) {
+ /* NOTE: this code is only for debugging with '-p' option */
+ /* reserve a memory area */
+ host_len = HOST_PAGE_ALIGN(len) + qemu_host_page_size - TARGET_PAGE_SIZE;
+ host_start = (long)mmap((void *)host_start, host_len, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (host_start == -1)
+ return host_start;
+ host_end = host_start + host_len;
+ start = HOST_PAGE_ALIGN(host_start);
+ end = start + HOST_PAGE_ALIGN(len);
+ if (start > host_start)
+ munmap((void *)host_start, start - host_start);
+ if (end < host_end)
+ munmap((void *)end, host_end - end);
+ /* use it as a fixed mapping */
+ flags |= MAP_FIXED;
+ } else {
+ /* if not fixed, no need to do anything */
+ host_offset = offset & qemu_host_page_mask;
+ host_len = len + offset - host_offset;
+ start = (long)mmap((void *)host_start, host_len,
+ prot, flags, fd, host_offset);
+ if (start == -1)
+ return start;
+ /* update start so that it points to the file position at 'offset' */
+ if (!(flags & MAP_ANONYMOUS))
+ start += offset - host_offset;
+ goto the_end1;
+ }
+ }
+
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ end = start + len;
+ host_end = HOST_PAGE_ALIGN(end);
+
+ /* worst case: we cannot map the file because the offset is not
+ aligned, so we read it */
+ if (!(flags & MAP_ANONYMOUS) &&
+ (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+#ifndef __APPLE__
+ if ((flags & MAP_TYPE) == MAP_SHARED &&
+#else
+ if ((flags & MAP_SHARED) &&
+#endif
+ (prot & PROT_WRITE))
+ return -EINVAL;
+ retaddr = target_mmap(start, len, prot | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (retaddr == -1)
+ return retaddr;
+ pread(fd, (void *)start, len, offset);
+ if (!(prot & PROT_WRITE)) {
+ ret = target_mprotect(start, len, prot);
+ if (ret != 0)
+ return ret;
+ }
+ goto the_end;
+ }
+
+ /* handle the start of the mapping */
+ if (start > host_start) {
+ if (host_end == host_start + qemu_host_page_size) {
+ /* one single host page */
+ ret = mmap_frag(host_start, start, end,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ return ret;
+ goto the_end1;
+ }
+ ret = mmap_frag(host_start, start, host_start + qemu_host_page_size,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ return ret;
+ host_start += qemu_host_page_size;
+ }
+ /* handle the end of the mapping */
+ if (end < host_end) {
+ ret = mmap_frag(host_end - qemu_host_page_size,
+ host_end - qemu_host_page_size, host_end,
+ prot, flags, fd,
+ offset + host_end - qemu_host_page_size - start);
+ if (ret == -1)
+ return ret;
+ host_end -= qemu_host_page_size;
+ }
+
+ /* map the middle (easier) */
+ if (host_start < host_end) {
+ unsigned long offset1;
+ if (flags & MAP_ANONYMOUS)
+ offset1 = 0;
+ else
+ offset1 = offset + host_start - start;
+ ret = (long)mmap((void *)host_start, host_end - host_start,
+ prot, flags, fd, offset1);
+ if (ret == -1)
+ return ret;
+ }
+ the_end1:
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ the_end:
+#ifdef DEBUG_MMAP
+ printf("target_mmap: ret=0x%lx\n", (long)start);
+ page_dump(stdout);
+ printf("\n");
+#endif
+ return start;
+}
+
+int target_munmap(unsigned long start, unsigned long len)
+{
+ unsigned long end, host_start, host_end, addr;
+ int prot, ret;
+
+#ifdef DEBUG_MMAP
+ printf("munmap: start=0x%lx len=0x%lx\n", start, len);
+#endif
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ return -EINVAL;
+ end = start + len;
+ host_start = start & qemu_host_page_mask;
+ host_end = HOST_PAGE_ALIGN(end);
+
+ if (start > host_start) {
+ /* handle host page containing start */
+ prot = 0;
+ for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (host_end == host_start + qemu_host_page_size) {
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ end = host_end;
+ }
+ if (prot != 0)
+ host_start += qemu_host_page_size;
+ }
+ if (end < host_end) {
+ prot = 0;
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (prot != 0)
+ host_end -= qemu_host_page_size;
+ }
+
+ /* unmap what we can */
+ if (host_start < host_end) {
+ ret = munmap((void *)host_start, host_end - host_start);
+ if (ret != 0)
+ return ret;
+ }
+
+ page_set_flags(start, start + len, 0);
+ return 0;
+}
+
+/* XXX: currently, we only handle MAP_ANONYMOUS and not MAP_FIXED
+ blocks which have been allocated starting on a host page */
+long target_mremap(unsigned long old_addr, unsigned long old_size,
+ unsigned long new_size, unsigned long flags,
+ unsigned long new_addr)
+{
+#ifndef __APPLE__
+ /* XXX: use 5 args syscall */
+ new_addr = (long)mremap((void *)old_addr, old_size, new_size, flags);
+ if (new_addr == -1)
+ return new_addr;
+ prot = page_get_flags(old_addr);
+ page_set_flags(old_addr, old_addr + old_size, 0);
+ page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
+ return new_addr;
+#else
+ qerror("target_mremap: unsupported\n");
+#endif
+
+}
+
+int target_msync(unsigned long start, unsigned long len, int flags)
+{
+ unsigned long end;
+
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ if (end == start)
+ return 0;
+
+ start &= qemu_host_page_mask;
+ return msync((void *)start, end - start, flags);
+}
diff --git a/darwin-user/qemu.h b/darwin-user/qemu.h
new file mode 100644
index 0000000..b6d3e6c
--- /dev/null
+++ b/darwin-user/qemu.h
@@ -0,0 +1,178 @@
+#ifndef GEMU_H
+#define GEMU_H
+
+#include <signal.h>
+#include <string.h>
+
+#include "cpu.h"
+
+#include "thunk.h"
+
+#include "gdbstub.h"
+
+typedef siginfo_t target_siginfo_t;
+#define target_sigaction sigaction
+#ifdef TARGET_I386
+struct target_pt_regs {
+ long ebx;
+ long ecx;
+ long edx;
+ long esi;
+ long edi;
+ long ebp;
+ long eax;
+ int xds;
+ int xes;
+ long orig_eax;
+ long eip;
+ int xcs;
+ long eflags;
+ long esp;
+ int xss;
+};
+struct target_sigcontext {
+ int sc_onstack;
+ int sc_mask;
+ int sc_eax;
+ int sc_ebx;
+ int sc_ecx;
+ int sc_edx;
+ int sc_edi;
+ int sc_esi;
+ int sc_ebp;
+ int sc_esp;
+ int sc_ss;
+ int sc_eflags;
+ int sc_eip;
+ int sc_cs;
+ int sc_ds;
+ int sc_es;
+ int sc_fs;
+ int sc_gs;
+};
+
+#define __USER_CS (0x17)
+#define __USER_DS (0x1F)
+
+#elif defined(TARGET_PPC)
+struct target_pt_regs {
+ unsigned long gpr[32];
+ unsigned long nip;
+ unsigned long msr;
+ unsigned long orig_gpr3; /* Used for restarting system calls */
+ unsigned long ctr;
+ unsigned long link;
+ unsigned long xer;
+ unsigned long ccr;
+ unsigned long mq; /* 601 only (not used at present) */
+ /* Used on APUS to hold IPL value. */
+ unsigned long trap; /* Reason for being here */
+ unsigned long dar; /* Fault registers */
+ unsigned long dsisr;
+ unsigned long result; /* Result of a system call */
+};
+
+struct target_sigcontext {
+ int sc_onstack; /* sigstack state to restore */
+ int sc_mask; /* signal mask to restore */
+ int sc_ir; /* pc */
+ int sc_psw; /* processor status word */
+ int sc_sp; /* stack pointer if sc_regs == NULL */
+ void *sc_regs; /* (kernel private) saved state */
+};
+
+#endif
+
+typedef struct TaskState {
+ struct TaskState *next;
+ int used; /* non zero if used */
+ uint8_t stack[0];
+} __attribute__((aligned(16))) TaskState;
+
+void syscall_init(void);
+long do_mach_syscall(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8);
+long do_thread_syscall(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8);
+long do_unix_syscall(void *cpu_env, int num);
+int do_sigaction(int sig, const struct sigaction *act,
+ struct sigaction *oact);
+int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
+
+void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+void qerror(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+
+void write_dt(void *ptr, unsigned long addr, unsigned long limit, int flags);
+
+extern CPUState *global_env;
+void cpu_loop(CPUState *env);
+void init_paths(const char *prefix);
+const char *path(const char *pathname);
+
+#include "qemu-log.h"
+
+/* commpage.c */
+void commpage_init(void);
+void do_commpage(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7, uint32_t arg8);
+
+/* signal.c */
+void process_pending_signals(void *cpu_env);
+void signal_init(void);
+int queue_signal(int sig, target_siginfo_t *info);
+void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
+void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
+long do_sigreturn(CPUState *env, int num);
+
+/* machload.c */
+int mach_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs);
+
+/* mmap.c */
+int target_mprotect(unsigned long start, unsigned long len, int prot);
+long target_mmap(unsigned long start, unsigned long len, int prot,
+ int flags, int fd, unsigned long offset);
+int target_munmap(unsigned long start, unsigned long len);
+long target_mremap(unsigned long old_addr, unsigned long old_size,
+ unsigned long new_size, unsigned long flags,
+ unsigned long new_addr);
+int target_msync(unsigned long start, unsigned long len, int flags);
+
+/* user access */
+
+/* XXX: todo protect every memory access */
+#define lock_user(x,y,z) (void*)(x)
+#define unlock_user(x,y,z)
+
+/* Mac OS X ABI arguments processing */
+#ifdef TARGET_I386
+static inline uint32_t get_int_arg(int *i, CPUX86State *cpu_env)
+{
+ uint32_t *args = (uint32_t*)(cpu_env->regs[R_ESP] + 4 + *i);
+ *i+=4;
+ return tswap32(*args);
+}
+static inline uint64_t get_int64_arg(int *i, CPUX86State *cpu_env)
+{
+ uint64_t *args = (uint64_t*)(cpu_env->regs[R_ESP] + 4 + *i);
+ *i+=8;
+ return tswap64(*args);
+}
+#elif defined(TARGET_PPC)
+static inline uint32_t get_int_arg(int *i, CPUPPCState *cpu_env)
+{
+ /* XXX: won't work when args goes on stack after gpr10 */
+ uint32_t args = (uint32_t)(cpu_env->gpr[3+(*i & 0xff)/4]);
+ *i+=4;
+ return tswap32(args);
+}
+static inline uint64_t get_int64_arg(int *i, CPUPPCState *cpu_env)
+{
+ /* XXX: won't work when args goes on stack after gpr10 */
+ uint64_t args = (uint64_t)(cpu_env->fpr[1+(*i >> 8)/8]);
+ *i+=(8 << 8) + 8;
+ return tswap64(args);
+}
+#endif
+
+#endif
diff --git a/darwin-user/signal.c b/darwin-user/signal.c
new file mode 100644
index 0000000..4862018
--- /dev/null
+++ b/darwin-user/signal.c
@@ -0,0 +1,457 @@
+/*
+ * Emulation of Linux signals
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+
+#ifdef __ia64__
+#undef uc_mcontext
+#undef uc_sigmask
+#undef uc_stack
+#undef uc_link
+#endif
+
+#include <signal.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+
+#define DEBUG_SIGNAL
+
+#define MAX_SIGQUEUE_SIZE 1024
+
+struct sigqueue {
+ struct sigqueue *next;
+ target_siginfo_t info;
+};
+
+struct emulated_sigaction {
+ struct target_sigaction sa;
+ int pending; /* true if signal is pending */
+ struct sigqueue *first;
+ struct sigqueue info; /* in order to always have memory for the
+ first signal, we put it here */
+};
+
+static struct sigaltstack target_sigaltstack_used = {
+ 0, 0, SA_DISABLE
+};
+
+static struct emulated_sigaction sigact_table[NSIG];
+static struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
+static struct sigqueue *first_free; /* first free siginfo queue entry */
+static int signal_pending; /* non zero if a signal may be pending */
+
+static void host_signal_handler(int host_signum, siginfo_t *info,
+ void *puc);
+
+
+static inline int host_to_target_signal(int sig)
+{
+ return sig;
+}
+
+static inline int target_to_host_signal(int sig)
+{
+ return sig;
+}
+
+/* siginfo conversion */
+
+
+
+void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
+{
+
+}
+
+void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
+{
+
+}
+
+void signal_init(void)
+{
+ struct sigaction act;
+ int i;
+
+ /* set all host signal handlers. ALL signals are blocked during
+ the handlers to serialize them. */
+ sigfillset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = host_signal_handler;
+ for(i = 1; i < NSIG; i++) {
+ sigaction(i, &act, NULL);
+ }
+
+ memset(sigact_table, 0, sizeof(sigact_table));
+
+ first_free = &sigqueue_table[0];
+ for(i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++)
+ sigqueue_table[i].next = &sigqueue_table[i + 1];
+ sigqueue_table[MAX_SIGQUEUE_SIZE - 1].next = NULL;
+}
+
+/* signal queue handling */
+
+static inline struct sigqueue *alloc_sigqueue(void)
+{
+ struct sigqueue *q = first_free;
+ if (!q)
+ return NULL;
+ first_free = q->next;
+ return q;
+}
+
+static inline void free_sigqueue(struct sigqueue *q)
+{
+ q->next = first_free;
+ first_free = q;
+}
+
+/* abort execution with signal */
+void QEMU_NORETURN force_sig(int sig)
+{
+ int host_sig;
+ host_sig = target_to_host_signal(sig);
+ fprintf(stderr, "qemu: uncaught target signal %d (%s) - exiting\n",
+ sig, strsignal(host_sig));
+ _exit(-host_sig);
+}
+
+/* queue a signal so that it will be send to the virtual CPU as soon
+ as possible */
+int queue_signal(int sig, target_siginfo_t *info)
+{
+ struct emulated_sigaction *k;
+ struct sigqueue *q, **pq;
+ target_ulong handler;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "queue_signal: sig=%d\n",
+ sig);
+#endif
+ k = &sigact_table[sig - 1];
+ handler = (target_ulong)k->sa.sa_handler;
+ if (handler == SIG_DFL) {
+ /* default handler : ignore some signal. The other are fatal */
+ if (sig != SIGCHLD &&
+ sig != SIGURG &&
+ sig != SIGWINCH) {
+ force_sig(sig);
+ } else {
+ return 0; /* indicate ignored */
+ }
+ } else if (handler == host_to_target_signal(SIG_IGN)) {
+ /* ignore signal */
+ return 0;
+ } else if (handler == host_to_target_signal(SIG_ERR)) {
+ force_sig(sig);
+ } else {
+ pq = &k->first;
+ if (!k->pending) {
+ /* first signal */
+ q = &k->info;
+ } else {
+ q = alloc_sigqueue();
+ if (!q)
+ return -EAGAIN;
+ while (*pq != NULL)
+ pq = &(*pq)->next;
+ }
+ *pq = q;
+ q->info = *info;
+ q->next = NULL;
+ k->pending = 1;
+ /* signal that a new signal is pending */
+ signal_pending = 1;
+ return 1; /* indicates that the signal was queued */
+ }
+}
+
+static void host_signal_handler(int host_signum, siginfo_t *info,
+ void *puc)
+{
+ int sig;
+ target_siginfo_t tinfo;
+
+ /* the CPU emulator uses some host signals to detect exceptions,
+ we we forward to it some signals */
+ if (host_signum == SIGSEGV || host_signum == SIGBUS) {
+ if (cpu_signal_handler(host_signum, (void*)info, puc))
+ return;
+ }
+
+ /* get target signal number */
+ sig = host_to_target_signal(host_signum);
+ if (sig < 1 || sig > NSIG)
+ return;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "qemu: got signal %d\n", sig);
+#endif
+ if (queue_signal(sig, &tinfo) == 1) {
+ /* interrupt the virtual CPU as soon as possible */
+ cpu_exit(global_env);
+ }
+}
+
+int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss)
+{
+ /* XXX: test errors */
+ if(oss)
+ {
+ oss->ss_sp = tswap32(target_sigaltstack_used.ss_sp);
+ oss->ss_size = tswap32(target_sigaltstack_used.ss_size);
+ oss->ss_flags = tswap32(target_sigaltstack_used.ss_flags);
+ }
+ if(ss)
+ {
+ target_sigaltstack_used.ss_sp = tswap32(ss->ss_sp);
+ target_sigaltstack_used.ss_size = tswap32(ss->ss_size);
+ target_sigaltstack_used.ss_flags = tswap32(ss->ss_flags);
+ }
+ return 0;
+}
+
+int do_sigaction(int sig, const struct sigaction *act,
+ struct sigaction *oact)
+{
+ struct emulated_sigaction *k;
+ struct sigaction act1;
+ int host_sig;
+
+ if (sig < 1 || sig > NSIG)
+ return -EINVAL;
+
+ k = &sigact_table[sig - 1];
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "sigaction 1 sig=%d act=0x%08x, oact=0x%08x\n",
+ sig, (int)act, (int)oact);
+#endif
+ if (oact) {
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "sigaction 1 sig=%d act=0x%08x, oact=0x%08x\n",
+ sig, (int)act, (int)oact);
+#endif
+
+ oact->sa_handler = tswapl(k->sa.sa_handler);
+ oact->sa_flags = tswapl(k->sa.sa_flags);
+ oact->sa_mask = tswapl(k->sa.sa_mask);
+ }
+ if (act) {
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "sigaction handler 0x%x flag 0x%x mask 0x%x\n",
+ act->sa_handler, act->sa_flags, act->sa_mask);
+#endif
+
+ k->sa.sa_handler = tswapl(act->sa_handler);
+ k->sa.sa_flags = tswapl(act->sa_flags);
+ k->sa.sa_mask = tswapl(act->sa_mask);
+ /* we update the host signal state */
+ host_sig = target_to_host_signal(sig);
+ if (host_sig != SIGSEGV && host_sig != SIGBUS) {
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "sigaction handler going to call sigaction\n",
+ act->sa_handler, act->sa_flags, act->sa_mask);
+#endif
+
+ sigfillset(&act1.sa_mask);
+ act1.sa_flags = SA_SIGINFO;
+ if (k->sa.sa_flags & SA_RESTART)
+ act1.sa_flags |= SA_RESTART;
+ /* NOTE: it is important to update the host kernel signal
+ ignore state to avoid getting unexpected interrupted
+ syscalls */
+ if (k->sa.sa_handler == SIG_IGN) {
+ act1.sa_sigaction = (void *)SIG_IGN;
+ } else if (k->sa.sa_handler == SIG_DFL) {
+ act1.sa_sigaction = (void *)SIG_DFL;
+ } else {
+ act1.sa_sigaction = host_signal_handler;
+ }
+ sigaction(host_sig, &act1, NULL);
+ }
+ }
+ return 0;
+}
+
+
+#ifdef TARGET_I386
+
+static inline void *
+get_sigframe(struct emulated_sigaction *ka, CPUX86State *env, size_t frame_size)
+{
+ /* XXX Fix that */
+ if(target_sigaltstack_used.ss_flags & SA_DISABLE)
+ {
+ int esp;
+ /* Default to using normal stack */
+ esp = env->regs[R_ESP];
+
+ return (void *)((esp - frame_size) & -8ul);
+ }
+ else
+ {
+ return target_sigaltstack_used.ss_sp;
+ }
+}
+
+static void setup_frame(int sig, struct emulated_sigaction *ka,
+ void *set, CPUState *env)
+{
+ void *frame;
+ int i, err = 0;
+
+ fprintf(stderr, "setup_frame %d\n", sig);
+ frame = get_sigframe(ka, env, sizeof(*frame));
+
+ /* Set up registers for signal handler */
+ env->regs[R_ESP] = (unsigned long) frame;
+ env->eip = (unsigned long) ka->sa.sa_handler;
+
+ env->eflags &= ~TF_MASK;
+
+ return;
+
+give_sigsegv:
+ if (sig == SIGSEGV)
+ ka->sa.sa_handler = SIG_DFL;
+ force_sig(SIGSEGV /* , current */);
+}
+
+long do_sigreturn(CPUState *env, int num)
+{
+ int i = 0;
+ struct target_sigcontext *scp = get_int_arg(&i, env);
+ /* XXX Get current signal number */
+ /* XXX Adjust accordin to sc_onstack, sc_mask */
+ if(tswapl(scp->sc_onstack) & 0x1)
+ target_sigaltstack_used.ss_flags |= ~SA_DISABLE;
+ else
+ target_sigaltstack_used.ss_flags &= SA_DISABLE;
+ int set = tswapl(scp->sc_eax);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ fprintf(stderr, "do_sigreturn: partially implemented %x EAX:%x EBX:%x\n", scp->sc_mask, tswapl(scp->sc_eax), tswapl(scp->sc_ebx));
+ fprintf(stderr, "ECX:%x EDX:%x EDI:%x\n", scp->sc_ecx, tswapl(scp->sc_edx), tswapl(scp->sc_edi));
+ fprintf(stderr, "EIP:%x\n", tswapl(scp->sc_eip));
+
+ env->regs[R_EAX] = tswapl(scp->sc_eax);
+ env->regs[R_EBX] = tswapl(scp->sc_ebx);
+ env->regs[R_ECX] = tswapl(scp->sc_ecx);
+ env->regs[R_EDX] = tswapl(scp->sc_edx);
+ env->regs[R_EDI] = tswapl(scp->sc_edi);
+ env->regs[R_ESI] = tswapl(scp->sc_esi);
+ env->regs[R_EBP] = tswapl(scp->sc_ebp);
+ env->regs[R_ESP] = tswapl(scp->sc_esp);
+ env->segs[R_SS].selector = (void*)tswapl(scp->sc_ss);
+ env->eflags = tswapl(scp->sc_eflags);
+ env->eip = tswapl(scp->sc_eip);
+ env->segs[R_CS].selector = (void*)tswapl(scp->sc_cs);
+ env->segs[R_DS].selector = (void*)tswapl(scp->sc_ds);
+ env->segs[R_ES].selector = (void*)tswapl(scp->sc_es);
+ env->segs[R_FS].selector = (void*)tswapl(scp->sc_fs);
+ env->segs[R_GS].selector = (void*)tswapl(scp->sc_gs);
+
+ /* Again, because our caller's caller will reset EAX */
+ return env->regs[R_EAX];
+}
+
+#else
+
+static void setup_frame(int sig, struct emulated_sigaction *ka,
+ void *set, CPUState *env)
+{
+ fprintf(stderr, "setup_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env, int num)
+{
+ int i = 0;
+ struct target_sigcontext *scp = get_int_arg(&i, env);
+ fprintf(stderr, "do_sigreturn: not implemented\n");
+ return -ENOSYS;
+}
+
+#endif
+
+void process_pending_signals(void *cpu_env)
+{
+ struct emulated_sigaction *k;
+ struct sigqueue *q;
+ target_ulong handler;
+ int sig;
+
+ if (!signal_pending)
+ return;
+
+ k = sigact_table;
+
+ for(sig = 1; sig <= NSIG; sig++) {
+ if (k->pending)
+ goto handle_signal;
+ k++;
+ }
+
+ /* if no signal is pending, just return */
+ signal_pending = 0;
+ return;
+handle_signal:
+ #ifdef DEBUG_SIGNAL
+ fprintf(stderr, "qemu: process signal %d\n", sig);
+ #endif
+ /* dequeue signal */
+ q = k->first;
+ k->first = q->next;
+ if (!k->first)
+ k->pending = 0;
+
+ sig = gdb_handlesig (cpu_env, sig);
+ if (!sig) {
+ fprintf (stderr, "Lost signal\n");
+ abort();
+ }
+
+ handler = k->sa.sa_handler;
+ if (handler == SIG_DFL) {
+ /* default handler : ignore some signal. The other are fatal */
+ if (sig != SIGCHLD &&
+ sig != SIGURG &&
+ sig != SIGWINCH) {
+ force_sig(sig);
+ }
+ } else if (handler == SIG_IGN) {
+ /* ignore sig */
+ } else if (handler == SIG_ERR) {
+ force_sig(sig);
+ } else {
+
+ setup_frame(sig, k, 0, cpu_env);
+ if (k->sa.sa_flags & SA_RESETHAND)
+ k->sa.sa_handler = SIG_DFL;
+ }
+ if (q != &k->info)
+ free_sigqueue(q);
+}
diff --git a/darwin-user/syscall.c b/darwin-user/syscall.c
new file mode 100644
index 0000000..060acc8
--- /dev/null
+++ b/darwin-user/syscall.c
@@ -0,0 +1,1566 @@
+/*
+ * Darwin syscalls
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Pierre d'Herbemont
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <mach/host_info.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach/message.h>
+
+#include <pthread.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/dirent.h>
+#include <sys/uio.h>
+#include <sys/termios.h>
+#include <sys/ptrace.h>
+#include <net/if.h>
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <sys/attr.h>
+
+#include <mach/ndr.h>
+#include <mach/mig_errors.h>
+
+#include <sys/xattr.h>
+
+#include "qemu.h"
+
+//#define DEBUG_SYSCALL
+
+#ifdef DEBUG_SYSCALL
+# define DEBUG_FORCE_ENABLE_LOCAL() int __DEBUG_qemu_user_force_enable = 1
+# define DEBUG_BEGIN_ENABLE __DEBUG_qemu_user_force_enable = 1;
+# define DEBUG_END_ENABLE __DEBUG_qemu_user_force_enable = 0;
+
+# define DEBUG_DISABLE_ALL() static int __DEBUG_qemu_user_force_enable = 0
+# define DEBUG_ENABLE_ALL() static int __DEBUG_qemu_user_force_enable = 1
+ DEBUG_ENABLE_ALL();
+
+# define DPRINTF(...) do { qemu_log(__VA_ARGS__); \
+ if(__DEBUG_qemu_user_force_enable) fprintf(stderr, __VA_ARGS__); \
+ } while(0)
+#else
+# define DEBUG_FORCE_ENABLE_LOCAL()
+# define DEBUG_BEGIN_ENABLE
+# define DEBUG_END_ENABLE
+
+# define DPRINTF(...) do { qemu_log(__VA_ARGS__); } while(0)
+#endif
+
+enum {
+ bswap_out = 0,
+ bswap_in = 1
+};
+
+extern const char *interp_prefix;
+
+static inline long get_errno(long ret)
+{
+ if (ret == -1)
+ return -errno;
+ else
+ return ret;
+}
+
+static inline int is_error(long ret)
+{
+ return (unsigned long)ret >= (unsigned long)(-4096);
+}
+
+/* ------------------------------------------------------------
+ Mach syscall handling
+*/
+
+void static inline print_description_msg_header(mach_msg_header_t *hdr)
+{
+ char *name = NULL;
+ int i;
+ struct { int number; char *name; } msg_name[] =
+ {
+ /* see http://fxr.watson.org/fxr/source/compat/mach/mach_namemap.c?v=NETBSD */
+ { 200, "host_info" },
+ { 202, "host_page_size" },
+ { 206, "host_get_clock_service" },
+ { 206, "host_get_clock_service" },
+ { 206, "host_get_clock_service" },
+ { 306, "host_get_clock_service" },
+ { 3204, "mach_port_allocate" },
+ { 3206, "mach_port_deallocate" },
+ { 3404, "mach_ports_lookup" },
+ { 3409, "mach_task_get_special_port" },
+ { 3414, "mach_task_get_exception_ports" },
+ { 3418, "mach_semaphore_create" },
+ { 3504, "mach_semaphore_create" },
+ { 3509, "mach_semaphore_create" },
+ { 3518, "semaphore_create" },
+ { 3616, "thread_policy" },
+ { 3801, "vm_allocate" },
+ { 3802, "vm_deallocate" },
+ { 3802, "vm_deallocate" },
+ { 3803, "vm_protect" },
+ { 3812, "vm_map" },
+ { 4241776, "lu_message_send_id" }, /* lookupd */
+ { 4241876, "lu_message_reply_id" }, /* lookupd */
+ };
+
+ for(i = 0; i < ARRAY_SIZE(msg_name); i++) {
+ if(msg_name[i].number == hdr->msgh_id)
+ {
+ name = msg_name[i].name;
+ break;
+ }
+ }
+ if(!name)
+ DPRINTF("unknown mach msg %d 0x%x\n", hdr->msgh_id, hdr->msgh_id);
+ else
+ DPRINTF("%s\n", name);
+#if 0
+ DPRINTF("Bits: %8x\n", hdr->msgh_bits);
+ DPRINTF("Size: %8x\n", hdr->msgh_size);
+ DPRINTF("Rmte: %8x\n", hdr->msgh_remote_port);
+ DPRINTF("Locl: %8x\n", hdr->msgh_local_port);
+ DPRINTF("Rsrv: %8x\n", hdr->msgh_reserved);
+
+ DPRINTF("Id : %8x\n", hdr->msgh_id);
+
+ NDR_record_t *ndr = (NDR_record_t *)(hdr + 1);
+ DPRINTF("hdr = %p, sizeof(hdr) = %x, NDR = %p\n", hdr, (unsigned int)sizeof(mach_msg_header_t), ndr);
+ DPRINTF("%d %d %d %d %d %d %d %d\n",
+ ndr->mig_vers, ndr->if_vers, ndr->reserved1, ndr->mig_encoding,
+ ndr->int_rep, ndr->char_rep, ndr->float_rep, ndr->reserved2);
+#endif
+}
+
+static inline void print_mach_msg_return(mach_msg_return_t ret)
+{
+ int i, found = 0;
+#define MACH_MSG_RET(msg) { msg, #msg }
+ struct { int code; char *name; } msg_name[] =
+ {
+ /* ref: http://darwinsource.opendarwin.org/10.4.2/xnu-792.2.4/osfmk/man/mach_msg.html */
+ /* send message */
+ MACH_MSG_RET(MACH_SEND_MSG_TOO_SMALL),
+ MACH_MSG_RET(MACH_SEND_NO_BUFFER),
+ MACH_MSG_RET(MACH_SEND_INVALID_DATA),
+ MACH_MSG_RET(MACH_SEND_INVALID_HEADER),
+ MACH_MSG_RET(MACH_SEND_INVALID_DEST),
+ MACH_MSG_RET(MACH_SEND_INVALID_NOTIFY),
+ MACH_MSG_RET(MACH_SEND_INVALID_REPLY),
+ MACH_MSG_RET(MACH_SEND_INVALID_TRAILER),
+ MACH_MSG_RET(MACH_SEND_INVALID_MEMORY),
+ MACH_MSG_RET(MACH_SEND_INVALID_RIGHT),
+ MACH_MSG_RET(MACH_SEND_INVALID_TYPE),
+ MACH_MSG_RET(MACH_SEND_INTERRUPTED),
+ MACH_MSG_RET(MACH_SEND_TIMED_OUT),
+
+ MACH_MSG_RET(MACH_RCV_BODY_ERROR),
+ MACH_MSG_RET(MACH_RCV_HEADER_ERROR),
+
+ MACH_MSG_RET(MACH_RCV_IN_SET),
+ MACH_MSG_RET(MACH_RCV_INTERRUPTED),
+
+ MACH_MSG_RET(MACH_RCV_INVALID_DATA),
+ MACH_MSG_RET(MACH_RCV_INVALID_NAME),
+ MACH_MSG_RET(MACH_RCV_INVALID_NOTIFY),
+ MACH_MSG_RET(MACH_RCV_INVALID_TRAILER),
+ MACH_MSG_RET(MACH_RCV_INVALID_TYPE),
+
+ MACH_MSG_RET(MACH_RCV_PORT_CHANGED),
+ MACH_MSG_RET(MACH_RCV_PORT_DIED),
+
+ MACH_MSG_RET(MACH_RCV_SCATTER_SMALL),
+ MACH_MSG_RET(MACH_RCV_TIMED_OUT),
+ MACH_MSG_RET(MACH_RCV_TOO_LARGE)
+ };
+#undef MACH_MSG_RET
+
+ if( ret == MACH_MSG_SUCCESS)
+ DPRINTF("MACH_MSG_SUCCESS\n");
+ else
+ {
+ for( i = 0; i < ARRAY_SIZE(msg_name); i++) {
+ if(msg_name[i].code == ret) {
+ DPRINTF("%s\n", msg_name[i].name);
+ found = 1;
+ break;
+ }
+ }
+ if(!found)
+ qerror("unknow mach message ret code %d\n", ret);
+ }
+}
+
+static inline void swap_mach_msg_header(mach_msg_header_t *hdr)
+{
+ hdr->msgh_bits = tswap32(hdr->msgh_bits);
+ hdr->msgh_size = tswap32(hdr->msgh_size);
+ hdr->msgh_remote_port = tswap32(hdr->msgh_remote_port);
+ hdr->msgh_local_port = tswap32(hdr->msgh_local_port);
+ hdr->msgh_reserved = tswap32(hdr->msgh_reserved);
+ hdr->msgh_id = tswap32(hdr->msgh_id);
+}
+
+struct complex_msg {
+ mach_msg_header_t hdr;
+ mach_msg_body_t body;
+};
+
+static inline void swap_mach_msg_body(struct complex_msg *complex_msg, int bswap)
+{
+ mach_msg_port_descriptor_t *descr = (mach_msg_port_descriptor_t *)(complex_msg+1);
+ int i,j;
+
+ if(bswap == bswap_in)
+ tswap32s(&complex_msg->body.msgh_descriptor_count);
+
+ DPRINTF("body.msgh_descriptor_count %d\n", complex_msg->body.msgh_descriptor_count);
+
+ for(i = 0; i < complex_msg->body.msgh_descriptor_count; i++) {
+ switch(descr->type)
+ {
+ case MACH_MSG_PORT_DESCRIPTOR:
+ tswap32s(&descr->name);
+ descr++;
+ break;
+ case MACH_MSG_OOL_DESCRIPTOR:
+ {
+ mach_msg_ool_descriptor_t *ool = (void *)descr;
+ tswap32s((uint32_t *)&ool->address);
+ tswap32s(&ool->size);
+
+ descr = (mach_msg_port_descriptor_t *)(ool+1);
+ break;
+ }
+ case MACH_MSG_OOL_PORTS_DESCRIPTOR:
+ {
+ mach_msg_ool_ports_descriptor_t *ool_ports = (void *)descr;
+ mach_port_name_t * port_names;
+
+ if(bswap == bswap_in)
+ {
+ tswap32s((uint32_t *)&ool_ports->address);
+ tswap32s(&ool_ports->count);
+ }
+
+ port_names = ool_ports->address;
+
+ for(j = 0; j < ool_ports->count; j++)
+ tswap32s(&port_names[j]);
+
+ if(bswap == bswap_out)
+ {
+ tswap32s((uint32_t *)&ool_ports->address);
+ tswap32s(&ool_ports->count);
+ }
+
+ descr = (mach_msg_port_descriptor_t *)(ool_ports+1);
+ break;
+ }
+ default: qerror("unknow mach msg descriptor type %x\n", descr->type);
+ }
+ }
+ if(bswap == bswap_out)
+ tswap32s(&complex_msg->body.msgh_descriptor_count);
+}
+
+static inline void swap_mach_msg(mach_msg_header_t *hdr, int bswap)
+{
+ if (bswap == bswap_out && hdr->msgh_bits & MACH_MSGH_BITS_COMPLEX)
+ swap_mach_msg_body((struct complex_msg *)hdr, bswap);
+
+ swap_mach_msg_header(hdr);
+
+ if (bswap == bswap_in && hdr->msgh_bits & MACH_MSGH_BITS_COMPLEX)
+ swap_mach_msg_body((struct complex_msg *)hdr, bswap);
+}
+
+static inline uint32_t target_mach_msg_trap(
+ mach_msg_header_t *hdr, uint32_t options, uint32_t send_size,
+ uint32_t rcv_size, uint32_t rcv_name, uint32_t time_out, uint32_t notify)
+{
+ extern int mach_msg_trap(mach_msg_header_t *, mach_msg_option_t,
+ mach_msg_size_t, mach_msg_size_t, mach_port_t,
+ mach_msg_timeout_t, mach_port_t);
+ mach_msg_audit_trailer_t *trailer;
+ mach_msg_id_t msg_id;
+ uint32_t ret = 0;
+ int i;
+
+ swap_mach_msg(hdr, bswap_in);
+
+ msg_id = hdr->msgh_id;
+
+ print_description_msg_header(hdr);
+
+ ret = mach_msg_trap(hdr, options, send_size, rcv_size, rcv_name, time_out, notify);
+
+ print_mach_msg_return(ret);
+
+ if( (options & MACH_RCV_MSG) && (REQUESTED_TRAILER_SIZE(options) > 0) )
+ {
+ /* XXX: the kernel always return the full trailer with MACH_SEND_MSG, so we should
+ probably always bswap it */
+ /* warning: according to Mac OS X Internals (the book) msg_size might be expressed in
+ natural_t units but according to xnu/osfmk/mach/message.h: "The size of
+ the message must be specified in bytes" */
+ trailer = (mach_msg_audit_trailer_t *)((uint8_t *)hdr + hdr->msgh_size);
+ /* XXX: Should probably do that based on the option asked by the sender, but dealing
+ with kernel answer seems more sound */
+ switch(trailer->msgh_trailer_size)
+ {
+ case sizeof(mach_msg_audit_trailer_t):
+ for(i = 0; i < 8; i++)
+ tswap32s(&trailer->msgh_audit.val[i]);
+ /* Fall in mach_msg_security_trailer_t case */
+ case sizeof(mach_msg_security_trailer_t):
+ tswap32s(&trailer->msgh_sender.val[0]);
+ tswap32s(&trailer->msgh_sender.val[1]);
+ /* Fall in mach_msg_seqno_trailer_t case */
+ case sizeof(mach_msg_seqno_trailer_t):
+ tswap32s(&trailer->msgh_seqno);
+ /* Fall in mach_msg_trailer_t case */
+ case sizeof(mach_msg_trailer_t):
+ tswap32s(&trailer->msgh_trailer_type);
+ tswap32s(&trailer->msgh_trailer_size);
+ break;
+ case 0:
+ /* Safer not to byteswap, but probably wrong */
+ break;
+ default:
+ qerror("unknow trailer type given its size %d\n", trailer->msgh_trailer_size);
+ break;
+ }
+ }
+
+ /* Special message handling */
+ switch (msg_id) {
+ case 200: /* host_info */
+ {
+ mig_reply_error_t *err = (mig_reply_error_t *)hdr;
+ struct {
+ uint32_t unknow1;
+ uint32_t max_cpus;
+ uint32_t avail_cpus;
+ uint32_t memory_size;
+ uint32_t cpu_type;
+ uint32_t cpu_subtype;
+ } *data = (void *)(err+1);
+
+ DPRINTF("maxcpu = 0x%x\n", data->max_cpus);
+ DPRINTF("numcpu = 0x%x\n", data->avail_cpus);
+ DPRINTF("memsize = 0x%x\n", data->memory_size);
+
+#if defined(TARGET_I386)
+ data->cpu_type = CPU_TYPE_I386;
+ DPRINTF("cpu_type changed to 0x%x(i386)\n", data->cpu_type);
+ data->cpu_subtype = CPU_SUBTYPE_PENT;
+ DPRINTF("cpu_subtype changed to 0x%x(i386_pent)\n", data->cpu_subtype);
+#elif defined(TARGET_PPC)
+ data->cpu_type = CPU_TYPE_POWERPC;
+ DPRINTF("cpu_type changed to 0x%x(ppc)\n", data->cpu_type);
+ data->cpu_subtype = CPU_SUBTYPE_POWERPC_750;
+ DPRINTF("cpu_subtype changed to 0x%x(ppc_all)\n", data->cpu_subtype);
+#else
+# error target not supported
+#endif
+ break;
+ }
+ case 202: /* host_page_size */
+ {
+ mig_reply_error_t *err = (mig_reply_error_t *)hdr;
+ uint32_t *pagesize = (uint32_t *)(err+1);
+
+ DPRINTF("pagesize = %d\n", *pagesize);
+ break;
+ }
+ default: break;
+ }
+
+ swap_mach_msg(hdr, bswap_out);
+
+ return ret;
+}
+
+long do_mach_syscall(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7,
+ uint32_t arg8)
+{
+ extern uint32_t mach_reply_port(void);
+
+ long ret = 0;
+
+ arg1 = tswap32(arg1);
+ arg2 = tswap32(arg2);
+ arg3 = tswap32(arg3);
+ arg4 = tswap32(arg4);
+ arg5 = tswap32(arg5);
+ arg6 = tswap32(arg6);
+ arg7 = tswap32(arg7);
+ arg8 = tswap32(arg8);
+
+ DPRINTF("mach syscall %d : " , num);
+
+ switch(num) {
+ /* see xnu/osfmk/mach/syscall_sw.h */
+ case -26:
+ DPRINTF("mach_reply_port()\n");
+ ret = mach_reply_port();
+ break;
+ case -27:
+ DPRINTF("mach_thread_self()\n");
+ ret = mach_thread_self();
+ break;
+ case -28:
+ DPRINTF("mach_task_self()\n");
+ ret = mach_task_self();
+ break;
+ case -29:
+ DPRINTF("mach_host_self()\n");
+ ret = mach_host_self();
+ break;
+ case -31:
+ DPRINTF("mach_msg_trap(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
+ arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ ret = target_mach_msg_trap((mach_msg_header_t *)arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+ break;
+/* may need more translation if target arch is different from host */
+#if (defined(TARGET_I386) && defined(__i386__)) || (defined(TARGET_PPC) && defined(__ppc__))
+ case -33:
+ DPRINTF("semaphore_signal_trap(0x%x)\n", arg1);
+ ret = semaphore_signal_trap(arg1);
+ break;
+ case -34:
+ DPRINTF("semaphore_signal_all_trap(0x%x)\n", arg1);
+ ret = semaphore_signal_all_trap(arg1);
+ break;
+ case -35:
+ DPRINTF("semaphore_signal_thread_trap(0x%x)\n", arg1, arg2);
+ ret = semaphore_signal_thread_trap(arg1,arg2);
+ break;
+#endif
+ case -36:
+ DPRINTF("semaphore_wait_trap(0x%x)\n", arg1);
+ extern int semaphore_wait_trap(int); // XXX: is there any header for that?
+ ret = semaphore_wait_trap(arg1);
+ break;
+/* may need more translation if target arch is different from host */
+#if (defined(TARGET_I386) && defined(__i386__)) || (defined(TARGET_PPC) && defined(__ppc__))
+ case -37:
+ DPRINTF("semaphore_wait_signal_trap(0x%x, 0x%x)\n", arg1, arg2);
+ ret = semaphore_wait_signal_trap(arg1,arg2);
+ break;
+#endif
+ case -43:
+ DPRINTF("map_fd(0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
+ arg1, arg2, arg3, arg4, arg5);
+ ret = map_fd(arg1, arg2, (void*)arg3, arg4, arg5);
+ tswap32s((uint32_t*)arg3);
+ break;
+/* may need more translation if target arch is different from host */
+#if (defined(TARGET_I386) && defined(__i386__)) || (defined(TARGET_PPC) && defined(__ppc__))
+ case -61:
+ DPRINTF("syscall_thread_switch(0x%x, 0x%x, 0x%x)\n",
+ arg1, arg2, arg3);
+ ret = syscall_thread_switch(arg1, arg2, arg3); // just a hint to the scheduler; can drop?
+ break;
+#endif
+ case -89:
+ DPRINTF("mach_timebase_info(0x%x)\n", arg1);
+ struct mach_timebase_info info;
+ ret = mach_timebase_info(&info);
+ if(!is_error(ret))
+ {
+ struct mach_timebase_info *outInfo = (void*)arg1;
+ outInfo->numer = tswap32(info.numer);
+ outInfo->denom = tswap32(info.denom);
+ }
+ break;
+ case -90:
+ DPRINTF("mach_wait_until()\n");
+ extern int mach_wait_until(uint64_t); // XXX: is there any header for that?
+ ret = mach_wait_until(((uint64_t)arg2<<32) | (uint64_t)arg1);
+ break;
+ case -91:
+ DPRINTF("mk_timer_create()\n");
+ extern int mk_timer_create(); // XXX: is there any header for that?
+ ret = mk_timer_create();
+ break;
+ case -92:
+ DPRINTF("mk_timer_destroy()\n");
+ extern int mk_timer_destroy(int); // XXX: is there any header for that?
+ ret = mk_timer_destroy(arg1);
+ break;
+ case -93:
+ DPRINTF("mk_timer_create()\n");
+ extern int mk_timer_arm(int, uint64_t); // XXX: is there any header for that?
+ ret = mk_timer_arm(arg1, ((uint64_t)arg3<<32) | (uint64_t)arg2);
+ break;
+ case -94:
+ DPRINTF("mk_timer_cancel()\n");
+ extern int mk_timer_cancel(int, uint64_t *); // XXX: is there any header for that?
+ ret = mk_timer_cancel(arg1, (uint64_t *)arg2);
+ if((!is_error(ret)) && arg2)
+ tswap64s((uint64_t *)arg2);
+ break;
+ default:
+ gemu_log("qemu: Unsupported mach syscall: %d(0x%x)\n", num, num);
+ gdb_handlesig (cpu_env, SIGTRAP);
+ exit(0);
+ break;
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------
+ thread type syscall handling
+*/
+long do_thread_syscall(void *cpu_env, int num, uint32_t arg1, uint32_t arg2, uint32_t arg3,
+ uint32_t arg4, uint32_t arg5, uint32_t arg6, uint32_t arg7,
+ uint32_t arg8)
+{
+ extern uint32_t cthread_set_self(uint32_t);
+ extern uint32_t processor_facilities_used(void);
+ long ret = 0;
+
+ arg1 = tswap32(arg1);
+ arg2 = tswap32(arg2);
+ arg3 = tswap32(arg3);
+ arg4 = tswap32(arg4);
+ arg5 = tswap32(arg5);
+ arg6 = tswap32(arg6);
+ arg7 = tswap32(arg7);
+ arg8 = tswap32(arg8);
+
+ DPRINTF("thread syscall %d : " , num);
+
+ switch(num) {
+#ifdef TARGET_I386
+ case 0x3:
+#endif
+ case 0x7FF1: /* cthread_set_self */
+ DPRINTF("cthread_set_self(0x%x)\n", (unsigned int)arg1);
+ ret = cthread_set_self(arg1);
+#ifdef TARGET_I386
+ /* we need to update the LDT with the address of the thread */
+ write_dt((void *)(((CPUX86State *) cpu_env)->ldt.base + (4 * sizeof(uint64_t))), arg1, 1,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+ /* New i386 convention, %gs should be set to our this LDT entry */
+ cpu_x86_load_seg(cpu_env, R_GS, 0x27);
+ /* Old i386 convention, the kernel returns the selector for the cthread (pre-10.4.8?)*/
+ ret = 0x27;
+#endif
+ break;
+ case 0x7FF2: /* Called the super-fast pthread_self handler by the apple guys */
+ DPRINTF("pthread_self()\n");
+ ret = (uint32_t)pthread_self();
+ break;
+ case 0x7FF3:
+ DPRINTF("processor_facilities_used()\n");
+#ifdef __i386__
+ qerror("processor_facilities_used: not implemented!\n");
+#else
+ ret = (uint32_t)processor_facilities_used();
+#endif
+ break;
+ default:
+ gemu_log("qemu: Unsupported thread syscall: %d(0x%x)\n", num, num);
+ gdb_handlesig (cpu_env, SIGTRAP);
+ exit(0);
+ break;
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------
+ ioctl handling
+*/
+static inline void byteswap_termios(struct termios *t)
+{
+ tswap32s((uint32_t*)&t->c_iflag);
+ tswap32s((uint32_t*)&t->c_oflag);
+ tswap32s((uint32_t*)&t->c_cflag);
+ tswap32s((uint32_t*)&t->c_lflag);
+ /* 20 (char) bytes then */
+ tswap32s((uint32_t*)&t->c_ispeed);
+ tswap32s((uint32_t*)&t->c_ospeed);
+}
+
+static inline void byteswap_winsize(struct winsize *w)
+{
+ tswap16s(&w->ws_row);
+ tswap16s(&w->ws_col);
+ tswap16s(&w->ws_xpixel);
+ tswap16s(&w->ws_ypixel);
+}
+
+#define STRUCT(name, ...) STRUCT_ ## name,
+#define STRUCT_SPECIAL(name) STRUCT_ ## name,
+enum {
+#include "ioctls_types.h"
+};
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+#define STRUCT(name, ...) const argtype struct_ ## name ## _def[] = { __VA_ARGS__, TYPE_NULL };
+#define STRUCT_SPECIAL(name)
+#include "ioctls_types.h"
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+typedef struct IOCTLEntry {
+ unsigned int target_cmd;
+ unsigned int host_cmd;
+ const char *name;
+ int access;
+ const argtype arg_type[5];
+} IOCTLEntry;
+
+#define IOC_R 0x0001
+#define IOC_W 0x0002
+#define IOC_RW (IOC_R | IOC_W)
+
+#define MAX_STRUCT_SIZE 4096
+
+static IOCTLEntry ioctl_entries[] = {
+#define IOCTL(cmd, access, ...) \
+ { cmd, cmd, #cmd, access, { __VA_ARGS__ } },
+#include "ioctls.h"
+ { 0, 0, },
+};
+
+/* ??? Implement proper locking for ioctls. */
+static long do_ioctl(long fd, long cmd, long arg)
+{
+ const IOCTLEntry *ie;
+ const argtype *arg_type;
+ int ret;
+ uint8_t buf_temp[MAX_STRUCT_SIZE];
+ int target_size;
+ void *argptr;
+
+ ie = ioctl_entries;
+ for(;;) {
+ if (ie->target_cmd == 0) {
+ gemu_log("Unsupported ioctl: cmd=0x%04lx\n", cmd);
+ return -ENOSYS;
+ }
+ if (ie->target_cmd == cmd)
+ break;
+ ie++;
+ }
+ arg_type = ie->arg_type;
+#if defined(DEBUG)
+ gemu_log("ioctl: cmd=0x%04lx (%s)\n", cmd, ie->name);
+#endif
+ switch(arg_type[0]) {
+ case TYPE_NULL:
+ /* no argument */
+ ret = get_errno(ioctl(fd, ie->host_cmd));
+ break;
+ case TYPE_PTRVOID:
+ case TYPE_INT:
+ /* int argment */
+ ret = get_errno(ioctl(fd, ie->host_cmd, arg));
+ break;
+ case TYPE_PTR:
+ arg_type++;
+ target_size = thunk_type_size(arg_type, 0);
+ switch(ie->access) {
+ case IOC_R:
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ if (!is_error(ret)) {
+ argptr = lock_user(arg, target_size, 0);
+ thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+ }
+ break;
+ case IOC_W:
+ argptr = lock_user(arg, target_size, 1);
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ break;
+ default:
+ case IOC_RW:
+ argptr = lock_user(arg, target_size, 1);
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ if (!is_error(ret)) {
+ argptr = lock_user(arg, target_size, 0);
+ thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+ }
+ break;
+ }
+ break;
+ default:
+ gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n", cmd, arg_type[0]);
+ ret = -ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------
+ Unix syscall handling
+*/
+
+static inline void byteswap_attrlist(struct attrlist *a)
+{
+ tswap16s(&a->bitmapcount);
+ tswap16s(&a->reserved);
+ tswap32s(&a->commonattr);
+ tswap32s(&a->volattr);
+ tswap32s(&a->dirattr);
+ tswap32s(&a->fileattr);
+ tswap32s(&a->forkattr);
+}
+
+struct attrbuf_header {
+ unsigned long length;
+};
+
+static inline void byteswap_attrbuf(struct attrbuf_header *attrbuf, struct attrlist *attrlist)
+{
+ DPRINTF("attrBuf.lenght %lx\n", attrbuf->length);
+}
+
+static inline void byteswap_statfs(struct statfs *s)
+{
+ tswap16s((uint16_t*)&s->f_otype);
+ tswap16s((uint16_t*)&s->f_oflags);
+ tswap32s((uint32_t*)&s->f_bsize);
+ tswap32s((uint32_t*)&s->f_iosize);
+ tswap32s((uint32_t*)&s->f_blocks);
+ tswap32s((uint32_t*)&s->f_bfree);
+ tswap32s((uint32_t*)&s->f_bavail);
+ tswap32s((uint32_t*)&s->f_files);
+ tswap32s((uint32_t*)&s->f_ffree);
+ tswap32s((uint32_t*)&s->f_fsid.val[0]);
+ tswap32s((uint32_t*)&s->f_fsid.val[1]);
+ tswap16s((uint16_t*)&s->f_reserved1);
+ tswap16s((uint16_t*)&s->f_type);
+ tswap32s((uint32_t*)&s->f_flags);
+}
+
+static inline void byteswap_stat(struct stat *s)
+{
+ tswap32s((uint32_t*)&s->st_dev);
+ tswap32s(&s->st_ino);
+ tswap16s(&s->st_mode);
+ tswap16s(&s->st_nlink);
+ tswap32s(&s->st_uid);
+ tswap32s(&s->st_gid);
+ tswap32s((uint32_t*)&s->st_rdev);
+ tswap32s((uint32_t*)&s->st_atimespec.tv_sec);
+ tswap32s((uint32_t*)&s->st_atimespec.tv_nsec);
+ tswap32s((uint32_t*)&s->st_mtimespec.tv_sec);
+ tswap32s((uint32_t*)&s->st_mtimespec.tv_nsec);
+ tswap32s((uint32_t*)&s->st_ctimespec.tv_sec);
+ tswap32s((uint32_t*)&s->st_ctimespec.tv_nsec);
+ tswap64s((uint64_t*)&s->st_size);
+ tswap64s((uint64_t*)&s->st_blocks);
+ tswap32s((uint32_t*)&s->st_blksize);
+ tswap32s(&s->st_flags);
+ tswap32s(&s->st_gen);
+}
+
+static inline void byteswap_dirents(struct dirent *d, int bytes)
+{
+ char *b;
+ for( b = (char*)d; (int)b < (int)d+bytes; )
+ {
+ unsigned short s = ((struct dirent *)b)->d_reclen;
+ tswap32s(&((struct dirent *)b)->d_ino);
+ tswap16s(&((struct dirent *)b)->d_reclen);
+ if(s<=0)
+ break;
+ b += s;
+ }
+}
+
+static inline void byteswap_iovec(struct iovec *v, int n)
+{
+ int i;
+ for(i = 0; i < n; i++)
+ {
+ tswap32s((uint32_t*)&v[i].iov_base);
+ tswap32s((uint32_t*)&v[i].iov_len);
+ }
+}
+
+static inline void byteswap_timeval(struct timeval *t)
+{
+ tswap32s((uint32_t*)&t->tv_sec);
+ tswap32s((uint32_t*)&t->tv_usec);
+}
+
+long do_unix_syscall_indirect(void *cpu_env, int num);
+long do_sync(void);
+long do_exit(uint32_t arg1);
+long do_getlogin(char *out, uint32_t size);
+long do_open(char * arg1, uint32_t arg2, uint32_t arg3);
+long do_getfsstat(struct statfs * arg1, uint32_t arg2, uint32_t arg3);
+long do_sigprocmask(uint32_t arg1, uint32_t * arg2, uint32_t * arg3);
+long do_execve(char* arg1, char ** arg2, char ** arg3);
+long do_getgroups(uint32_t arg1, gid_t * arg2);
+long do_gettimeofday(struct timeval * arg1, void * arg2);
+long do_readv(uint32_t arg1, struct iovec * arg2, uint32_t arg3);
+long do_writev(uint32_t arg1, struct iovec * arg2, uint32_t arg3);
+long do_utimes(char * arg1, struct timeval * arg2);
+long do_futimes(uint32_t arg1, struct timeval * arg2);
+long do_statfs(char * arg1, struct statfs * arg2);
+long do_fstatfs(uint32_t arg1, struct statfs * arg2);
+long do_stat(char * arg1, struct stat * arg2);
+long do_fstat(uint32_t arg1, struct stat * arg2);
+long do_lstat(char * arg1, struct stat * arg2);
+long do_getdirentries(uint32_t arg1, void* arg2, uint32_t arg3, void* arg4);
+long do_lseek(void *cpu_env, int num);
+long do___sysctl(int * name, uint32_t namelen, void * oldp, size_t * oldlenp, void * newp, size_t newlen /* ignored */);
+long do_getattrlist(void * arg1, void * arg2, void * arg3, uint32_t arg4, uint32_t arg5);
+long do_getdirentriesattr(uint32_t arg1, void * arg2, void * arg3, size_t arg4, void * arg5, void * arg6, void* arg7, uint32_t arg8);
+long do_fcntl(int fd, int cmd, int arg);
+
+long no_syscall(void *cpu_env, int num);
+
+long do_pread(uint32_t arg1, void * arg2, size_t arg3, off_t arg4)
+{
+ DPRINTF("0x%x, %p, 0x%lx, 0x%" PRIx64 "\n", arg1, arg2, arg3, arg4);
+ long ret = pread(arg1, arg2, arg3, arg4);
+ return ret;
+}
+
+long do_read(int d, void *buf, size_t nbytes)
+{
+ DPRINTF("0x%x, %p, 0x%lx\n", d, buf, nbytes);
+ long ret = get_errno(read(d, buf, nbytes));
+ if(!is_error(ret))
+ DPRINTF("%x\n", *(uint32_t*)buf);
+ return ret;
+}
+
+long unimpl_unix_syscall(void *cpu_env, int num);
+
+typedef long (*syscall_function_t)(void *cpu_env, int num);
+
+
+/* define a table that will handle the syscall number->function association */
+#define VOID void
+#define INT (uint32_t)get_int_arg(&i, cpu_env)
+#define INT64 (uint64_t)get_int64_arg(&i, cpu_env)
+#define UINT (unsigned int)INT
+#define PTR (void*)INT
+
+#define SIZE INT
+#define OFFSET INT64
+
+#define WRAPPER_CALL_DIRECT_0(function, args) long __qemu_##function(void *cpu_env) { return (long)function(); }
+#define WRAPPER_CALL_DIRECT_1(function, _arg1) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; return (long)function(arg1); }
+#define WRAPPER_CALL_DIRECT_2(function, _arg1, _arg2) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; return (long)function(arg1, arg2); }
+#define WRAPPER_CALL_DIRECT_3(function, _arg1, _arg2, _arg3) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; typeof(_arg3) arg3 = _arg3; return (long)function(arg1, arg2, arg3); }
+#define WRAPPER_CALL_DIRECT_4(function, _arg1, _arg2, _arg3, _arg4) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; typeof(_arg3) arg3 = _arg3; typeof(_arg4) arg4 = _arg4; return (long)function(arg1, arg2, arg3, arg4); }
+#define WRAPPER_CALL_DIRECT_5(function, _arg1, _arg2, _arg3, _arg4, _arg5) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; typeof(_arg3) arg3 = _arg3; typeof(_arg4) arg4 = _arg4; typeof(_arg5) arg5 = _arg5; return (long)function(arg1, arg2, arg3, arg4, arg5); }
+#define WRAPPER_CALL_DIRECT_6(function, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; typeof(_arg3) arg3 = _arg3; typeof(_arg4) arg4 = _arg4; typeof(_arg5) arg5 = _arg5; typeof(_arg6) arg6 = _arg6; return (long)function(arg1, arg2, arg3, arg4, arg5, arg6); }
+#define WRAPPER_CALL_DIRECT_7(function, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; typeof(_arg3) arg3 = _arg3; typeof(_arg4) arg4 = _arg4; typeof(_arg5) arg5 = _arg5; typeof(_arg6) arg6 = _arg6; typeof(_arg7) arg7 = _arg7; return (long)function(arg1, arg2, arg3, arg4, arg5, arg6, arg7); }
+#define WRAPPER_CALL_DIRECT_8(function, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8) long __qemu_##function(void *cpu_env) { int i = 0; typeof(_arg1) arg1 = _arg1; typeof(_arg2) arg2 = _arg2; typeof(_arg3) arg3 = _arg3; typeof(_arg4) arg4 = _arg4; typeof(_arg5) arg5 = _arg5; typeof(_arg6) arg6 = _arg6; typeof(_arg7) arg7 = _arg7; typeof(_arg8) arg8 = _arg8; return (long)function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); }
+#define WRAPPER_CALL_DIRECT(function, nargs, ...) WRAPPER_CALL_DIRECT_##nargs(function, __VA_ARGS__)
+#define WRAPPER_CALL_NOERRNO(function, nargs, ...) WRAPPER_CALL_DIRECT(function, nargs, __VA_ARGS__)
+#define WRAPPER_CALL_INDIRECT(function, nargs, ...)
+#define ENTRY(name, number, function, nargs, call_type, ...) WRAPPER_##call_type(function, nargs, __VA_ARGS__)
+
+#include "syscalls.h"
+
+#undef ENTRY
+#undef WRAPPER_CALL_DIRECT
+#undef WRAPPER_CALL_NOERRNO
+#undef WRAPPER_CALL_INDIRECT
+#undef OFFSET
+#undef SIZE
+#undef INT
+#undef PTR
+#undef INT64
+
+#define _ENTRY(name, number, function, nargs, call_type) [number] = {\
+ name, \
+ number, \
+ (syscall_function_t)function, \
+ nargs, \
+ call_type \
+ },
+
+#define ENTRY_CALL_DIRECT(name, number, function, nargs, call_type) _ENTRY(name, number, __qemu_##function, nargs, call_type)
+#define ENTRY_CALL_NOERRNO(name, number, function, nargs, call_type) ENTRY_CALL_DIRECT(name, number, function, nargs, call_type)
+#define ENTRY_CALL_INDIRECT(name, number, function, nargs, call_type) _ENTRY(name, number, function, nargs, call_type)
+#define ENTRY(name, number, function, nargs, call_type, ...) ENTRY_##call_type(name, number, function, nargs, call_type)
+
+#define CALL_DIRECT 1
+#define CALL_INDIRECT 2
+#define CALL_NOERRNO (CALL_DIRECT | 4 /* = 5 */)
+
+struct unix_syscall {
+ char * name;
+ int number;
+ syscall_function_t function;
+ int nargs;
+ int call_type;
+} unix_syscall_table[SYS_MAXSYSCALL] = {
+#include "syscalls.h"
+};
+
+#undef ENTRY
+#undef _ENTRY
+#undef ENTRY_CALL_DIRECT
+#undef ENTRY_CALL_INDIRECT
+#undef ENTRY_CALL_NOERRNO
+
+/* Actual syscalls implementation */
+
+long do_unix_syscall_indirect(void *cpu_env, int num)
+{
+ long ret;
+ int new_num;
+ int i = 0;
+
+ new_num = get_int_arg(&i, cpu_env);
+#ifdef TARGET_I386
+ ((CPUX86State*)cpu_env)->regs[R_ESP] += 4;
+ /* XXX: not necessary */
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = new_num;
+#elif TARGET_PPC
+ {
+ int i;
+ uint32_t **regs = ((CPUPPCState*)cpu_env)->gpr;
+ for(i = 3; i < 11; i++)
+ *regs[i] = *regs[i+1];
+ /* XXX: not necessary */
+ *regs[0] = new_num;
+ }
+#endif
+ ret = do_unix_syscall(cpu_env, new_num);
+#ifdef TARGET_I386
+ ((CPUX86State*)cpu_env)->regs[R_ESP] -= 4;
+ /* XXX: not necessary */
+ ((CPUX86State*)cpu_env)->regs[R_EAX] = num;
+#elif TARGET_PPC
+ {
+ int i;
+ /* XXX: not really needed those regs are volatile accross calls */
+ uint32_t **regs = ((CPUPPCState*)cpu_env)->gpr;
+ for(i = 11; i > 3; i--)
+ *regs[i] = *regs[i-1];
+ regs[3] = new_num;
+ *regs[0] = num;
+ }
+#endif
+ return ret;
+}
+
+long do_exit(uint32_t arg1)
+{
+ exit(arg1);
+ /* not reached */
+ return -1;
+}
+
+long do_sync(void)
+{
+ sync();
+ return 0;
+}
+
+long do_getlogin(char *out, uint32_t size)
+{
+ char *login = getlogin();
+ if(!login)
+ return -1;
+ memcpy(out, login, size);
+ return 0;
+}
+long do_open(char * arg1, uint32_t arg2, uint32_t arg3)
+{
+ /* XXX: don't let the %s stay in there */
+ DPRINTF("open(%s, 0x%x, 0x%x)\n", arg1, arg2, arg3);
+ return get_errno(open(arg1, arg2, arg3));
+}
+
+long do_getfsstat(struct statfs * arg1, uint32_t arg2, uint32_t arg3)
+{
+ long ret;
+ DPRINTF("getfsstat(%p, 0x%x, 0x%x)\n", arg1, arg2, arg3);
+ ret = get_errno(getfsstat(arg1, arg2, arg3));
+ if((!is_error(ret)) && arg1)
+ byteswap_statfs(arg1);
+ return ret;
+}
+
+long do_sigprocmask(uint32_t arg1, uint32_t * arg2, uint32_t * arg3)
+{
+ long ret;
+ DPRINTF("sigprocmask(%d, %p, %p)\n", arg1, arg2, arg3);
+ gemu_log("XXX: sigprocmask not tested (%d, %p, %p)\n", arg1, arg2, arg3);
+ if(arg2)
+ tswap32s(arg2);
+ ret = get_errno(sigprocmask(arg1, (void *)arg2, (void *)arg3));
+ if((!is_error(ret)) && arg3)
+ tswap32s(arg3);
+ if(arg2)
+ tswap32s(arg2);
+ return ret;
+}
+
+long do_execve(char* arg1, char ** arg2, char ** arg3)
+{
+ long ret;
+ char **argv = arg2;
+ char **envp = arg3;
+ int argc;
+ int envc;
+
+ /* XXX: don't let the %s stay in here */
+ DPRINTF("execve(%s, %p, %p)\n", arg1, arg2, arg3);
+
+ for(argc = 0; argv[argc]; argc++);
+ for(envc = 0; envp[envc]; envc++);
+
+ argv = (char**)malloc(sizeof(char*)*argc);
+ envp = (char**)malloc(sizeof(char*)*envc);
+
+ for(; argc >= 0; argc--)
+ argv[argc] = (char*)tswap32((uint32_t)(arg2)[argc]);
+
+ for(; envc >= 0; envc--)
+ envp[envc] = (char*)tswap32((uint32_t)(arg3)[envc]);
+
+ ret = get_errno(execve(arg1, argv, envp));
+ free(argv);
+ free(envp);
+ return ret;
+}
+
+long do_getgroups(uint32_t arg1, gid_t * arg2)
+{
+ long ret;
+ int i;
+ DPRINTF("getgroups(0x%x, %p)\n", arg1, arg2);
+ ret = get_errno(getgroups(arg1, arg2));
+ if(ret > 0)
+ for(i = 0; i < arg1; i++)
+ tswap32s(&arg2[i]);
+ return ret;
+}
+
+long do_gettimeofday(struct timeval * arg1, void * arg2)
+{
+ long ret;
+ DPRINTF("gettimeofday(%p, %p)\n",
+ arg1, arg2);
+ ret = get_errno(gettimeofday(arg1, arg2));
+ if(!is_error(ret))
+ {
+ /* timezone no longer used according to the manpage, so don't bother with it */
+ byteswap_timeval(arg1);
+ }
+ return ret;
+}
+
+long do_readv(uint32_t arg1, struct iovec * arg2, uint32_t arg3)
+{
+ long ret;
+ DPRINTF("readv(0x%x, %p, 0x%x)\n", arg1, arg2, arg3);
+ if(arg2)
+ byteswap_iovec(arg2, arg3);
+ ret = get_errno(readv(arg1, arg2, arg3));
+ if((!is_error(ret)) && arg2)
+ byteswap_iovec(arg2, arg3);
+ return ret;
+}
+
+long do_writev(uint32_t arg1, struct iovec * arg2, uint32_t arg3)
+{
+ long ret;
+ DPRINTF("writev(0x%x, %p, 0x%x)\n", arg1, arg2, arg3);
+ if(arg2)
+ byteswap_iovec(arg2, arg3);
+ ret = get_errno(writev(arg1, arg2, arg3));
+ if((!is_error(ret)) && arg2)
+ byteswap_iovec(arg2, arg3);
+ return ret;
+}
+
+long do_utimes(char * arg1, struct timeval * arg2)
+{
+ DPRINTF("utimes(%p, %p)\n", arg1, arg2);
+ if(arg2)
+ {
+ byteswap_timeval(arg2);
+ byteswap_timeval(arg2+1);
+ }
+ return get_errno(utimes(arg1, arg2));
+}
+
+long do_futimes(uint32_t arg1, struct timeval * arg2)
+{
+ DPRINTF("futimes(0x%x, %p)\n", arg1, arg2);
+ if(arg2)
+ {
+ byteswap_timeval(arg2);
+ byteswap_timeval(arg2+1);
+ }
+ return get_errno(futimes(arg1, arg2));
+}
+
+long do_statfs(char * arg1, struct statfs * arg2)
+{
+ long ret;
+ DPRINTF("statfs(%p, %p)\n", arg1, arg2);
+ ret = get_errno(statfs(arg1, arg2));
+ if(!is_error(ret))
+ byteswap_statfs(arg2);
+ return ret;
+}
+
+long do_fstatfs(uint32_t arg1, struct statfs* arg2)
+{
+ long ret;
+ DPRINTF("fstatfs(0x%x, %p)\n",
+ arg1, arg2);
+ ret = get_errno(fstatfs(arg1, arg2));
+ if(!is_error(ret))
+ byteswap_statfs(arg2);
+
+ return ret;
+}
+
+long do_stat(char * arg1, struct stat * arg2)
+{
+ long ret;
+ /* XXX: don't let the %s stay in there */
+ DPRINTF("stat(%s, %p)\n", arg1, arg2);
+ ret = get_errno(stat(arg1, arg2));
+ if(!is_error(ret))
+ byteswap_stat(arg2);
+ return ret;
+}
+
+long do_fstat(uint32_t arg1, struct stat * arg2)
+{
+ long ret;
+ DPRINTF("fstat(0x%x, %p)\n", arg1, arg2);
+ ret = get_errno(fstat(arg1, arg2));
+ if(!is_error(ret))
+ byteswap_stat(arg2);
+ return ret;
+}
+
+long do_lstat(char * arg1, struct stat * arg2)
+{
+ long ret;
+ /* XXX: don't let the %s stay in there */
+ DPRINTF("lstat(%s, %p)\n", (const char *)arg1, arg2);
+ ret = get_errno(lstat(arg1, arg2));
+ if(!is_error(ret))
+ byteswap_stat(arg2);
+ return ret;
+}
+
+long do_getdirentries(uint32_t arg1, void* arg2, uint32_t arg3, void* arg4)
+{
+ long ret;
+ DPRINTF("getdirentries(0x%x, %p, 0x%x, %p)\n", arg1, arg2, arg3, arg4);
+ if(arg4)
+ tswap32s((uint32_t *)arg4);
+ ret = get_errno(getdirentries(arg1, arg2, arg3, arg4));
+ if(arg4)
+ tswap32s((uint32_t *)arg4);
+ if(!is_error(ret))
+ byteswap_dirents(arg2, ret);
+ return ret;
+}
+
+long do_lseek(void *cpu_env, int num)
+{
+ long ret;
+ int i = 0;
+ uint32_t arg1 = get_int_arg(&i, cpu_env);
+ uint64_t offset = get_int64_arg(&i, cpu_env);
+ uint32_t arg3 = get_int_arg(&i, cpu_env);
+ uint64_t r = lseek(arg1, offset, arg3);
+#ifdef TARGET_I386
+ /* lowest word in eax, highest in edx */
+ ret = r & 0xffffffff; /* will be set to eax after do_unix_syscall exit */
+ ((CPUX86State *)cpu_env)->regs[R_EDX] = (uint32_t)((r >> 32) & 0xffffffff) ;
+#elif defined TARGET_PPC
+ ret = r & 0xffffffff; /* will be set to r3 after do_unix_syscall exit */
+ ((CPUPPCState *)cpu_env)->gpr[4] = (uint32_t)((r >> 32) & 0xffffffff) ;
+#else
+ qerror("64 bit ret value on your arch?");
+#endif
+ return get_errno(ret);
+}
+
+void no_swap(void * oldp, int size)
+{
+}
+
+void sysctl_tswap32s(void * oldp, int size)
+{
+ tswap32s(oldp);
+}
+
+void bswap_oid(uint32_t * oldp, int size)
+{
+ int count = size / sizeof(int);
+ int i = 0;
+ do { tswap32s(oldp + i); } while (++i < count);
+}
+
+void sysctl_usrstack(uint32_t * oldp, int size)
+{
+ DPRINTF("sysctl_usrstack: 0x%x\n", *oldp);
+ tswap32s(oldp);
+}
+
+void sysctl_ncpu(uint32_t * ncpu, int size)
+{
+ *ncpu = 0x1;
+ DPRINTF("sysctl_ncpu: 0x%x\n", *ncpu);
+ tswap32s(ncpu);
+}
+
+void sysctl_exec(char * exec, int size)
+{
+ DPRINTF("sysctl_exec: %s\n", exec);
+}
+
+void sysctl_translate(char * exec, int size)
+{
+ DPRINTF("sysctl_translate: %s\n", exec);
+}
+
+struct sysctl_dir {
+ int num;
+ const char * name;
+ void (*swap_func)(void *, int);
+ struct sysctl_dir *childs;
+};
+
+#define ENTRYD(num, name, childs) { num, name, NULL, childs }
+#define ENTRYE(num, name, func) { num, name, (void (*)(void *, int))func, NULL }
+struct sysctl_dir sysctls_unspec[] = {
+ ENTRYE(3, "oip", bswap_oid),
+ { 0, NULL, NULL, NULL }
+};
+
+struct sysctl_dir sysctls_kern[] = {
+ ENTRYE(KERN_TRANSLATE, "translate", sysctl_translate), /* 44 */
+ ENTRYE(KERN_EXEC, "exec", sysctl_exec), /* 45 */
+ ENTRYE(KERN_USRSTACK32, "KERN_USRSTACK32", sysctl_usrstack), /* 35 */
+ ENTRYE(KERN_SHREG_PRIVATIZABLE, "KERN_SHREG_PRIVATIZABLE", sysctl_tswap32s), /* 54 */
+ { 0, NULL, NULL, NULL }
+};
+
+struct sysctl_dir sysctls_hw[] = {
+ ENTRYE(HW_NCPU, "ncpud", sysctl_tswap32s),
+ ENTRYE(104, "104", no_swap),
+ ENTRYE(105, "105", no_swap),
+ { 0, NULL, NULL, NULL }
+};
+
+struct sysctl_dir sysctls[] = {
+ ENTRYD(CTL_UNSPEC, "unspec", sysctls_unspec),
+ ENTRYD(CTL_KERN, "kern", sysctls_kern),
+ ENTRYD(CTL_HW, "hw", sysctls_hw ),
+ { 0, NULL, NULL, NULL }
+};
+
+#undef ENTRYE
+#undef ENTRYD
+
+static inline struct sysctl_dir * get_sysctl_entry_for_mib(int mib, struct sysctl_dir * sysctl_elmt)
+{
+ if(!sysctl_elmt)
+ return NULL;
+ for(; sysctl_elmt->name != NULL ; sysctl_elmt++) {
+ if(sysctl_elmt->num == mib)
+ return sysctl_elmt;
+ }
+ return NULL;
+}
+
+static inline long bswap_syctl(int * mib, int count, void *buf, int size)
+{
+ int i;
+ struct sysctl_dir * sysctl = sysctls;
+ struct sysctl_dir * ret = NULL;
+
+ for(i = 0; i < count; i++) {
+
+ if(!(ret = sysctl = get_sysctl_entry_for_mib(mib[i], sysctl))) {
+ gemu_log("bswap_syctl: can't find mib %d\n", mib[i]);
+ return -ENOTDIR;
+ }
+ if(!(sysctl = sysctl->childs))
+ break;
+ }
+
+ if(ret->childs)
+ qerror("we shouldn't have a directory element\n");
+
+ ret->swap_func(buf, size);
+ return 0;
+}
+
+static inline void print_syctl(int * mib, int count)
+{
+ int i;
+ struct sysctl_dir * sysctl = sysctls;
+ struct sysctl_dir * ret = NULL;
+
+ for(i = 0; i < count; i++) {
+ if(!(ret = sysctl = get_sysctl_entry_for_mib(mib[i], sysctl))){
+ gemu_log("print_syctl: can't find mib %d\n", mib[i]);
+ return;
+ }
+ DPRINTF("%s.", sysctl->name);
+ if(!(sysctl = sysctl->childs))
+ break;
+ }
+ DPRINTF("\n");
+}
+
+long do___sysctl(int * name, uint32_t namelen, void * oldp, size_t * oldlenp, void * newp, size_t newlen /* ignored */)
+{
+ long ret = 0;
+ int i;
+ DPRINTF("sysctl(%p, 0x%x, %p, %p, %p, 0x%lx)\n",
+ name, namelen, oldp, oldlenp, newp, newlen);
+ if(name) {
+ i = 0;
+ do { tswap32s( name + i); } while (++i < namelen);
+ print_syctl(name, namelen);
+ //bswap_syctl(name, namelen, newp, newlen);
+ tswap32s((uint32_t*)oldlenp);
+ }
+
+ if(name) /* Sometimes sysctl is called with no arg1, ignore */
+ ret = get_errno(sysctl(name, namelen, oldp, oldlenp, newp, newlen));
+
+#if defined(TARGET_I386) ^ defined(__i386__) || defined(TARGET_PPC) ^ defined(__ppc__)
+ if (!is_error(ret) && bswap_syctl(name, namelen, oldp, *oldlenp) != 0) {
+ return -ENOTDIR;
+ }
+#endif
+
+ if(name) {
+ //bswap_syctl(name, namelen, newp, newlen);
+ tswap32s((uint32_t*)oldlenp);
+
+ i = 0;
+ do { tswap32s( name + i); } while (++i < namelen);
+ }
+ return ret;
+}
+
+long do_getattrlist(void * arg1, void * arg2, void * arg3, uint32_t arg4, uint32_t arg5)
+{
+ struct attrlist * attrlist = (void *)arg2;
+ long ret;
+
+#if defined(TARGET_I386) ^ defined(__i386__) || defined(TARGET_PPC) ^ defined(__ppc__)
+ gemu_log("SYS_getdirentriesattr unimplemented\n");
+ return -ENOTSUP;
+#endif
+ /* XXX: don't let the %s stay in there */
+ DPRINTF("getattrlist(%s, %p, %p, 0x%x, 0x%x)\n",
+ (char *)arg1, arg2, arg3, arg4, arg5);
+
+ if(arg2) /* XXX: We should handle that in a copy especially
+ if the structure is not writable */
+ byteswap_attrlist(attrlist);
+
+ ret = get_errno(getattrlist((const char* )arg1, attrlist, (void *)arg3, arg4, arg5));
+
+ if(!is_error(ret))
+ {
+ byteswap_attrbuf((void *)arg3, attrlist);
+ byteswap_attrlist(attrlist);
+ }
+ return ret;
+}
+
+long do_getdirentriesattr(uint32_t arg1, void * arg2, void * arg3, size_t arg4, void * arg5, void * arg6, void* arg7, uint32_t arg8)
+{
+ DPRINTF("getdirentriesattr(0x%x, %p, %p, 0x%lx, %p, %p, %p, 0x%x)\n",
+ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+#if defined(TARGET_I386) ^ defined(__i386__) || defined(TARGET_PPC) ^ defined(__ppc__)
+ qerror("SYS_getdirentriesattr unimplemented\n");
+#endif
+
+ return get_errno(getdirentriesattr( arg1, (struct attrlist * )arg2, (void *)arg3, arg4,
+ (unsigned long *)arg5, (unsigned long *)arg6,
+ (unsigned long *)arg7, arg8));
+}
+
+static inline void bswap_flock(struct flock *f)
+{
+ tswap64s(&f->l_start);
+ tswap64s(&f->l_len);
+ tswap32s(&f->l_pid);
+ tswap16s(&f->l_type);
+ tswap16s(&f->l_whence);
+}
+
+static inline void bswap_fstore(struct fstore *f)
+{
+ tswap32s(&f->fst_flags);
+ tswap32s(&f->fst_posmode);
+ tswap64s(&f->fst_offset);
+ tswap64s(&f->fst_length);
+ tswap64s(&f->fst_bytesalloc);
+}
+
+static inline void bswap_radvisory(struct radvisory *f)
+{
+ tswap64s(&f->ra_offset);
+ tswap32s(&f->ra_count);
+}
+
+static inline void bswap_fbootstraptransfer(struct fbootstraptransfer *f)
+{
+ tswap64s(&f->fbt_offset);
+ tswap32s((uint32_t*)&f->fbt_length);
+ tswap32s((uint32_t*)&f->fbt_buffer); /* XXX: this is a ptr */
+}
+
+static inline void bswap_log2phys(struct log2phys *f)
+{
+ tswap32s(&f->l2p_flags);
+ tswap64s(&f->l2p_contigbytes);
+ tswap64s(&f->l2p_devoffset);
+}
+
+static inline void bswap_fcntl_arg(int cmd, void * arg)
+{
+ switch(cmd)
+ {
+ case F_DUPFD:
+ case F_GETFD:
+ case F_SETFD:
+ case F_GETFL:
+ case F_SETFL:
+ case F_GETOWN:
+ case F_SETOWN:
+ case F_SETSIZE:
+ case F_RDAHEAD:
+ case F_FULLFSYNC:
+ break;
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ bswap_flock(arg);
+ break;
+ case F_PREALLOCATE:
+ bswap_fstore(arg);
+ break;
+ case F_RDADVISE:
+ bswap_radvisory(arg);
+ break;
+ case F_READBOOTSTRAP:
+ case F_WRITEBOOTSTRAP:
+ bswap_fbootstraptransfer(arg);
+ break;
+ case F_LOG2PHYS:
+ bswap_log2phys(arg);
+ break;
+ default:
+ gemu_log("unknow cmd in fcntl\n");
+ }
+}
+
+long do_fcntl(int fd, int cmd, int arg)
+{
+ long ret;
+ bswap_fcntl_arg(cmd, (void *)arg);
+ ret = get_errno(fcntl(fd, cmd, arg));
+ if(!is_error(ret))
+ bswap_fcntl_arg(cmd, (void *)arg);
+ return ret;
+}
+
+long no_syscall(void *cpu_env, int num)
+{
+ /* XXX: We should probably fordward it to the host kernel */
+ qerror("no unix syscall %d\n", num);
+ /* not reached */
+ return -1;
+}
+
+long unimpl_unix_syscall(void *cpu_env, int num)
+{
+ if( (num < 0) || (num > SYS_MAXSYSCALL-1) )
+ qerror("unix syscall %d is out of unix syscall bounds (0-%d) " , num, SYS_MAXSYSCALL-1);
+
+ gemu_log("qemu: Unsupported unix syscall %s %d\n", unix_syscall_table[num].name , num);
+ gdb_handlesig (cpu_env, SIGTRAP);
+ exit(-1);
+}
+
+long do_unix_syscall(void *cpu_env, int num)
+{
+ long ret = 0;
+
+ DPRINTF("unix syscall %d: " , num);
+
+ if( (num < 0) || (num > SYS_MAXSYSCALL-1) )
+ qerror("unix syscall %d is out of unix syscall bounds (0-%d) " , num, SYS_MAXSYSCALL-1);
+
+ DPRINTF("%s [%s]", unix_syscall_table[num].name, unix_syscall_table[num].call_type & CALL_DIRECT ? "direct" : "indirect" );
+ ret = unix_syscall_table[num].function(cpu_env, num);
+
+ if(!(unix_syscall_table[num].call_type & CALL_NOERRNO))
+ ret = get_errno(ret);
+
+ DPRINTF("[returned 0x%x(%d)]\n", (int)ret, (int)ret);
+ return ret;
+}
+
+/* ------------------------------------------------------------
+ syscall_init
+*/
+void syscall_init(void)
+{
+ /* Nothing yet */
+}
diff --git a/darwin-user/syscalls.h b/darwin-user/syscalls.h
new file mode 100644
index 0000000..34d95da
--- /dev/null
+++ b/darwin-user/syscalls.h
@@ -0,0 +1,384 @@
+/* generated from xnu/bsd/kern/syscalls.master */
+
+ ENTRY("syscall", SYS_syscall, do_unix_syscall_indirect, 0, CALL_INDIRECT, VOID) /* 0 indirect syscall */
+ ENTRY("exit", SYS_exit, do_exit, 1, CALL_DIRECT, INT) /* 1 */
+ ENTRY("fork", SYS_fork, fork, 0, CALL_NOERRNO, VOID) /* 2 */
+ ENTRY("read", SYS_read, do_read, 3, CALL_DIRECT, INT, PTR, SIZE) /* 3 */
+ ENTRY("write", SYS_write, write, 3, CALL_DIRECT, INT, PTR, SIZE) /* 4 */
+ ENTRY("open", SYS_open, do_open, 3, CALL_DIRECT, PTR, INT, INT) /* 5 */
+ ENTRY("close", SYS_close, close, 1, CALL_DIRECT, INT) /* 6 */
+ ENTRY("wait4", SYS_wait4, wait4, 4, CALL_DIRECT, INT, PTR, INT, PTR) /* 7 */
+ ENTRY("", 8, no_syscall, 0, CALL_INDIRECT, VOID) /* 8 old creat */
+ ENTRY("link", SYS_link, link, 2, CALL_DIRECT, PTR, PTR) /* 9 */
+ ENTRY("unlink", SYS_unlink, unlink, 1, CALL_DIRECT, PTR) /* 10 */
+ ENTRY("", 11, no_syscall, 0, CALL_INDIRECT, VOID) /* 11 old execv */
+ ENTRY("chdir", SYS_chdir, chdir, 1, CALL_DIRECT, PTR) /* 12 */
+ ENTRY("fchdir", SYS_fchdir, fchdir, 1, CALL_DIRECT, INT) /* 13 */
+ ENTRY("mknod", SYS_mknod, mknod, 3, CALL_DIRECT, PTR, INT, INT) /* 14 */
+ ENTRY("chmod", SYS_chmod, chmod, 2, CALL_DIRECT, PTR, INT) /* 15 */
+ ENTRY("chown", SYS_chown, chown, 3, CALL_DIRECT, PTR, INT, INT) /* 16 */
+ ENTRY("obreak", SYS_obreak, no_syscall, 1, CALL_INDIRECT, VOID) /* 17 old break */
+ ENTRY("ogetfsstat", 18, unimpl_unix_syscall, 3, CALL_INDIRECT, PTR, INT, INT) /* 18 */
+ ENTRY("", 19, no_syscall, 0, CALL_INDIRECT, VOID) /* 19 old lseek */
+ ENTRY("getpid", SYS_getpid, getpid, 0, CALL_NOERRNO, VOID) /* 20 */
+ ENTRY("", 21, no_syscall, 0, CALL_INDIRECT, VOID) /* 21 old mount */
+ ENTRY("", 22, no_syscall, 0, CALL_INDIRECT, VOID) /* 22 old umount */
+ ENTRY("setuid", SYS_setuid, setuid, 1, CALL_DIRECT, INT) /* 23 */
+ ENTRY("getuid", SYS_getuid, getuid, 0, CALL_NOERRNO, VOID) /* 24 */
+ ENTRY("geteuid", SYS_geteuid, geteuid, 0, CALL_NOERRNO, VOID) /* 25 */
+ ENTRY("ptrace", SYS_ptrace, ptrace, 4, CALL_DIRECT, INT, INT, PTR, INT) /* 26 */
+ ENTRY("recvmsg", SYS_recvmsg, recvmsg, 3, CALL_DIRECT, INT, PTR, INT) /* 27 */
+ ENTRY("sendmsg", SYS_sendmsg, sendmsg, 3, CALL_DIRECT, INT, PTR, INT) /* 28 */
+ ENTRY("recvfrom", SYS_recvfrom, recvfrom, 6, CALL_DIRECT, INT, PTR, INT, INT, PTR, PTR) /* 29 */
+ ENTRY("accept", SYS_accept, accept, 3, CALL_DIRECT, INT, PTR, PTR) /* 30 */
+ ENTRY("getpeername", SYS_getpeername, getpeername, 3, CALL_DIRECT, INT, PTR, PTR) /* 31 */
+ ENTRY("getsockname", SYS_getsockname, getsockname, 3, CALL_DIRECT, INT, PTR, PTR) /* 32 */
+ ENTRY("access", SYS_access, access, 2, CALL_DIRECT, PTR, INT) /* 33 */
+ ENTRY("chflags", SYS_chflags, chflags, 2, CALL_DIRECT, PTR, INT) /* 34 */
+ ENTRY("fchflags", SYS_fchflags, fchflags, 2, CALL_DIRECT, INT, INT) /* 35 */
+ ENTRY("sync", SYS_sync, do_sync, 0, CALL_INDIRECT, VOID) /* 36 */
+ ENTRY("kill", SYS_kill, kill, 2, CALL_DIRECT, INT, INT) /* 37 */
+ ENTRY("", 38, no_syscall, 0, CALL_INDIRECT, VOID) /* 38 old stat */
+ ENTRY("getppid", SYS_getppid, getppid, 0, CALL_DIRECT, VOID) /* 39 */
+ ENTRY("", 40, no_syscall, 0, CALL_INDIRECT, VOID) /* 40 old lstat */
+ ENTRY("dup", SYS_dup, dup, 1, CALL_DIRECT, INT) /* 41 */
+ ENTRY("pipe", SYS_pipe, pipe, 0, CALL_INDIRECT, PTR) /* 42 */
+ ENTRY("getegid", SYS_getegid, getegid, 0, CALL_NOERRNO, VOID) /* 43 */
+ ENTRY("profil", SYS_profil, profil, 4, CALL_DIRECT, PTR, SIZE, INT, INT) /* 44 */
+ ENTRY("ktrace", SYS_ktrace, no_syscall, 4, CALL_INDIRECT, VOID) /* 45 */
+ ENTRY("sigaction", SYS_sigaction, do_sigaction, 3, CALL_DIRECT, INT, PTR, PTR) /* 46 */
+ ENTRY("getgid", SYS_getgid, getgid, 0, CALL_NOERRNO, VOID) /* 47 */
+ ENTRY("sigprocmask", SYS_sigprocmask, do_sigprocmask, 3, CALL_DIRECT, INT, PTR, PTR) /* 48 */
+ ENTRY("getlogin", SYS_getlogin, do_getlogin, 2, CALL_DIRECT, PTR, UINT) /* 49 XXX */
+ ENTRY("setlogin", SYS_setlogin, setlogin, 1, CALL_DIRECT, PTR) /* 50 */
+ ENTRY("acct", SYS_acct, acct, 1, CALL_DIRECT, PTR) /* 51 */
+ ENTRY("sigpending", SYS_sigpending, sigpending, 1, CALL_DIRECT, PTR) /* 52 */
+ ENTRY("sigaltstack", SYS_sigaltstack, do_sigaltstack, 2, CALL_DIRECT, PTR, PTR) /* 53 */
+ ENTRY("ioctl", SYS_ioctl, do_ioctl, 3, CALL_DIRECT, INT, INT, INT) /* 54 */
+ ENTRY("reboot", SYS_reboot, unimpl_unix_syscall, 2, CALL_INDIRECT, INT, PTR) /* 55 */
+ ENTRY("revoke", SYS_revoke, revoke, 1, CALL_DIRECT, PTR) /* 56 */
+ ENTRY("symlink", SYS_symlink, symlink, 2, CALL_DIRECT, PTR, PTR) /* 57 */
+ ENTRY("readlink", SYS_readlink, readlink, 3, CALL_DIRECT, PTR, PTR, INT) /* 58 */
+ ENTRY("execve", SYS_execve, do_execve, 3, CALL_DIRECT, PTR, PTR, PTR) /* 59 */
+ ENTRY("umask", SYS_umask, umask, 1, CALL_DIRECT, INT) /* 60 */
+ ENTRY("chroot", SYS_chroot, chroot, 1, CALL_DIRECT, PTR) /* 61 */
+ ENTRY("", 62, no_syscall, 0, CALL_INDIRECT, VOID) /* 62 old fstat */
+ ENTRY("", 63, no_syscall, 0, CALL_INDIRECT, VOID) /* 63 used internally , reserved */
+ ENTRY("", 64, no_syscall, 0, CALL_INDIRECT, VOID) /* 64 old getpagesize */
+ ENTRY("msync", SYS_msync, target_msync, 3, CALL_DIRECT, UINT /*PTR*/, SIZE, INT) /* 65 */
+ ENTRY("vfork", SYS_vfork, vfork, 0, CALL_DIRECT, VOID) /* 66 */
+ ENTRY("", 67, no_syscall, 0, CALL_INDIRECT, VOID) /* 67 old vread */
+ ENTRY("", 68, no_syscall, 0, CALL_INDIRECT, VOID) /* 68 old vwrite */
+ ENTRY("sbrk", SYS_sbrk, sbrk, 1, CALL_DIRECT, INT) /* 69 */
+ ENTRY("sstk", SYS_sstk, no_syscall, 1, CALL_INDIRECT, VOID) /* 70 */
+ ENTRY("", 71, no_syscall, 0, CALL_INDIRECT, VOID) /* 71 old mmap */
+ ENTRY("ovadvise", SYS_ovadvise, no_syscall, 0, CALL_INDIRECT, VOID) /* 72 old vadvise */
+ ENTRY("munmap", SYS_munmap, target_munmap, 2, CALL_DIRECT, UINT /* PTR */, SIZE) /* 73 */
+ ENTRY("mprotect", SYS_mprotect, mprotect, 3, CALL_DIRECT, PTR, SIZE, INT) /* 74 */
+ ENTRY("madvise", SYS_madvise, madvise, 3, CALL_DIRECT, PTR, SIZE, INT) /* 75 */
+ ENTRY("", 76, no_syscall, 0, CALL_INDIRECT, VOID) /* 76 old vhangup */
+ ENTRY("", 77, no_syscall, 0, CALL_INDIRECT, VOID) /* 77 old vlimit */
+ ENTRY("mincore", SYS_mincore, mincore, 3, CALL_DIRECT, PTR, SIZE, PTR) /* 78 */
+ ENTRY("getgroups", SYS_getgroups, do_getgroups, 2, CALL_DIRECT, UINT, PTR) /* 79 */
+ ENTRY("setgroups", SYS_setgroups, setgroups, 2, CALL_DIRECT, UINT, PTR) /* 80 */
+ ENTRY("getpgrp", SYS_getpgrp, getpgrp, 0, CALL_DIRECT, VOID) /* 81 */
+ ENTRY("setpgid", SYS_setpgid, setpgid, 2, CALL_DIRECT, INT, INT) /* 82 */
+ ENTRY("setitimer", SYS_setitimer, setitimer, 3, CALL_DIRECT, INT, PTR, PTR) /* 83 */
+ ENTRY("", 84, no_syscall, 0, CALL_INDIRECT, VOID) /* 84 old wait */
+ ENTRY("swapon", SYS_swapon, unimpl_unix_syscall, 0, CALL_INDIRECT, VOID) /* 85 */
+ ENTRY("getitimer", SYS_getitimer, getitimer, 2, CALL_DIRECT, INT, PTR) /* 86 */
+ ENTRY("", 87, no_syscall, 0, CALL_INDIRECT, VOID) /* 87 old gethostname */
+ ENTRY("", 88, no_syscall, 0, CALL_INDIRECT, VOID) /* 88 old sethostname */
+ ENTRY("getdtablesize", SYS_getdtablesize, getdtablesize, 0, CALL_DIRECT, VOID) /* 89 */
+ ENTRY("dup2", SYS_dup2, dup2, 2, CALL_DIRECT, INT, INT) /* 90 */
+ ENTRY("", 91, no_syscall, 0, CALL_INDIRECT, VOID) /* 91 old getdopt */
+ ENTRY("fcntl", SYS_fcntl, do_fcntl, 3, CALL_DIRECT, INT, INT, INT) /* 92 */
+ ENTRY("select", SYS_select, select, 5, CALL_DIRECT, INT, PTR, PTR, PTR, PTR) /* 93 */
+ ENTRY("", 94, no_syscall, 0, CALL_INDIRECT, VOID) /* 94 old setdopt */
+ ENTRY("fsync", SYS_fsync, fsync, 1, CALL_DIRECT, INT) /* 95 */
+ ENTRY("setpriority", SYS_setpriority, setpriority, 3, CALL_DIRECT, INT, INT, INT) /* 96 */
+ ENTRY("socket", SYS_socket, socket, 3, CALL_DIRECT, INT, INT, INT) /* 97 */
+ ENTRY("connect", SYS_connect, connect, 3, CALL_DIRECT, INT, PTR, INT) /* 98 */
+ ENTRY("", 99, no_syscall, 0, CALL_INDIRECT, VOID) /* 99 old accept */
+ ENTRY("getpriority", SYS_getpriority, getpriority, 2, CALL_DIRECT, INT, INT) /* 100 */
+ ENTRY("", 101, no_syscall, 0, CALL_INDIRECT, VOID) /* 101 old send */
+ ENTRY("", 102, no_syscall, 0, CALL_INDIRECT, VOID) /* 102 old recv */
+ ENTRY("", 103, no_syscall, 0, CALL_INDIRECT, VOID) /* 103 old sigreturn */
+ ENTRY("bind", SYS_bind, bind, 3, CALL_DIRECT, INT, PTR, INT) /* 104 */
+ ENTRY("setsockopt", SYS_setsockopt, setsockopt, 5, CALL_DIRECT, INT, INT, INT, PTR, INT) /* 105 */
+ ENTRY("listen", SYS_listen, listen, 2, CALL_DIRECT, INT, INT) /* 106 */
+ ENTRY("", 107, no_syscall, 0, CALL_INDIRECT, VOID) /* 107 old vtimes */
+ ENTRY("", 108, no_syscall, 0, CALL_INDIRECT, VOID) /* 108 old sigvec */
+ ENTRY("", 109, no_syscall, 0, CALL_INDIRECT, VOID) /* 109 old sigblock */
+ ENTRY("", 110, no_syscall, 0, CALL_INDIRECT, VOID) /* 110 old sigsetmask */
+ ENTRY("sigsuspend", SYS_sigsuspend, unimpl_unix_syscall, 1, CALL_INDIRECT, INT) /* 111 */
+ ENTRY("", 112, no_syscall, 0, CALL_INDIRECT, VOID) /* 112 old sigstack */
+ ENTRY("", 113, no_syscall, 0, CALL_INDIRECT, VOID) /* 113 old recvmsg */
+ ENTRY("", 114, no_syscall, 0, CALL_INDIRECT, VOID) /* 114 old sendmsg */
+ ENTRY("", 115, no_syscall, 0, CALL_INDIRECT, VOID) /* 115 old vtrace */
+ ENTRY("gettimeofday", SYS_gettimeofday, do_gettimeofday, 2, CALL_DIRECT, PTR, PTR) /* 116 */
+ ENTRY("getrusage", SYS_getrusage, getrusage, 2, CALL_DIRECT, INT, PTR) /* 117 */
+ ENTRY("getsockopt", SYS_getsockopt, getsockopt, 5, CALL_DIRECT, INT, INT, INT, PTR, PTR) /* 118 */
+ ENTRY("", 119, no_syscall, 0, CALL_INDIRECT, VOID) /* 119 old resuba */
+ ENTRY("readv", SYS_readv, do_readv, 3, CALL_DIRECT, INT, PTR, UINT) /* 120 */
+ ENTRY("writev", SYS_writev, do_writev, 3, CALL_DIRECT, INT, PTR, UINT) /* 121 */
+ ENTRY("settimeofday", SYS_settimeofday, settimeofday, 2, CALL_DIRECT, PTR, PTR) /* 122 */
+ ENTRY("fchown", SYS_fchown, fchown, 3, CALL_DIRECT, INT, INT, INT) /* 123 */
+ ENTRY("fchmod", SYS_fchmod, fchmod, 2, CALL_DIRECT, INT, INT) /* 124 */
+ ENTRY("", 125, no_syscall, 0, CALL_INDIRECT, VOID) /* 125 old recvfrom */
+ ENTRY("", 126, no_syscall, 0, CALL_INDIRECT, VOID) /* 126 old setreuid */
+ ENTRY("", 127, no_syscall, 0, CALL_INDIRECT, VOID) /* 127 old setregid */
+ ENTRY("rename", SYS_rename, rename, 2, CALL_DIRECT, PTR, PTR) /* 128 */
+ ENTRY("", 129, no_syscall, 0, CALL_INDIRECT, VOID) /* 129 old truncate */
+ ENTRY("", 130, no_syscall, 0, CALL_INDIRECT, VOID) /* 130 old ftruncate */
+ ENTRY("flock", SYS_flock, flock, 2, CALL_DIRECT, INT, INT) /* 131 */
+ ENTRY("mkfifo", SYS_mkfifo, mkfifo, 2, CALL_DIRECT, PTR, INT) /* 132 */
+ ENTRY("sendto", SYS_sendto, sendto, 6, CALL_DIRECT, INT, PTR, SIZE, INT, PTR, INT) /* 133 */
+ ENTRY("shutdown", SYS_shutdown, shutdown, 2, CALL_DIRECT, INT, INT) /* 134 */
+ ENTRY("socketpair", SYS_socketpair, socketpair, 4, CALL_DIRECT, INT, INT, INT, PTR) /* 135 */
+ ENTRY("mkdir", SYS_mkdir, mkdir, 2, CALL_DIRECT, PTR, INT) /* 136 */
+ ENTRY("rmdir", SYS_rmdir, rmdir, 1, CALL_DIRECT, PTR) /* 137 */
+ ENTRY("utimes", SYS_utimes, do_utimes, 2, CALL_DIRECT, PTR, PTR) /* 138 */
+ ENTRY("futimes", SYS_futimes, do_futimes, 2, CALL_DIRECT, INT, PTR) /* 139 */
+ ENTRY("adjtime", SYS_adjtime, adjtime, 2, CALL_DIRECT, PTR, PTR) /* 140 */
+ ENTRY("", 141, no_syscall, 0, CALL_INDIRECT, VOID) /* 141 old getpeername */
+ ENTRY("", 142, no_syscall, 0, CALL_INDIRECT, VOID) /* 142 old gethostid */
+ ENTRY("", 143, no_syscall, 0, CALL_INDIRECT, VOID) /* 143 old sethostid */
+ ENTRY("", 144, no_syscall, 0, CALL_INDIRECT, VOID) /* 144 old getrlimit */
+ ENTRY("", 145, no_syscall, 0, CALL_INDIRECT, VOID) /* 145 old setrlimit */
+ ENTRY("", 146, no_syscall, 0, CALL_INDIRECT, VOID) /* 146 old killpg */
+ ENTRY("setsid", SYS_setsid, setsid, 0, CALL_DIRECT, VOID) /* 147 */
+ ENTRY("", 148, no_syscall, 0, CALL_INDIRECT, VOID) /* 148 old setquota */
+ ENTRY("", 149, no_syscall, 0, CALL_INDIRECT, VOID) /* 149 old qquota */
+ ENTRY("", 150, no_syscall, 0, CALL_INDIRECT, VOID) /* 150 old getsockname */
+ ENTRY("getpgid", SYS_getpgid, getpgid, 1, CALL_DIRECT, INT) /* 151 */
+ ENTRY("setprivexec", SYS_setprivexec, no_syscall, 1, CALL_INDIRECT, VOID) /* 152 */
+ ENTRY("pread", SYS_pread, do_pread, 4, CALL_DIRECT, INT, PTR, SIZE, OFFSET) /* 153 */
+ ENTRY("pwrite", SYS_pwrite, pwrite, 4, CALL_DIRECT, INT, PTR, SIZE, OFFSET) /* 154 */
+#ifdef SYS_nfssvc
+ ENTRY("nfssvc", SYS_nfssvc, nfssvc, 2, CALL_DIRECT, INT, PTR) /* 155 */
+#else
+ ENTRY("nfssvc", 155, no_syscall, 2, CALL_INDIRECT, VOID) /* 155 */
+#endif
+ ENTRY("", 155, no_syscall, 0, CALL_INDIRECT, VOID) /* 155 */
+ ENTRY("", 156, no_syscall, 0, CALL_INDIRECT, VOID) /* 156 old getdirentries */
+ ENTRY("statfs", SYS_statfs, do_statfs, 2, CALL_DIRECT, PTR, PTR) /* 157 */
+ ENTRY("fstatfs", SYS_fstatfs, do_fstatfs, 2, CALL_DIRECT, INT, PTR) /* 158 */
+ ENTRY("unmount", SYS_unmount, unmount, 2, CALL_DIRECT, PTR, INT) /* 159 */
+ ENTRY("", 160, no_syscall, 0, CALL_INDIRECT, VOID) /* 160 old async_daemon */
+ ENTRY("", 161, no_syscall, 0, CALL_INDIRECT, VOID) /* 161 */
+ ENTRY("", 162, no_syscall, 0, CALL_INDIRECT, VOID) /* 162 old getdomainname */
+ ENTRY("", 163, no_syscall, 0, CALL_INDIRECT, VOID) /* 163 old setdomainname */
+ ENTRY("", 164, no_syscall, 0, CALL_INDIRECT, VOID) /* 164 */
+ ENTRY("quotactl", SYS_quotactl, no_syscall, 4, CALL_INDIRECT, VOID) /* 165 */
+ ENTRY("", 166, no_syscall, 0, CALL_INDIRECT, VOID) /* 166 old exportfs */
+ ENTRY("mount", SYS_mount, mount, 4, CALL_DIRECT, PTR, PTR, INT, PTR) /* 167 */
+ ENTRY("", 168, no_syscall, 0, CALL_INDIRECT, VOID) /* 168 old ustat */
+ ENTRY("", 169, no_syscall, 0, CALL_INDIRECT, VOID) /* 169 */
+ ENTRY("table", SYS_table, no_syscall, 0, CALL_INDIRECT, VOID) /* 170 old table */
+ ENTRY("", 171, no_syscall, 0, CALL_INDIRECT, VOID) /* 171 old wait3 */
+ ENTRY("", 172, no_syscall, 0, CALL_INDIRECT, VOID) /* 172 old rpause */
+ ENTRY("waitid", SYS_waitid, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 173 */
+ ENTRY("", 174, no_syscall, 0, CALL_INDIRECT, VOID) /* 174 old getdents */
+ ENTRY("", 175, no_syscall, 0, CALL_INDIRECT, VOID) /* 175 old gc_control */
+ ENTRY("add_profil", SYS_add_profil, add_profil, 4, CALL_DIRECT, PTR, SIZE, UINT, UINT) /* 176 */
+ ENTRY("", 177, no_syscall, 0, CALL_INDIRECT, VOID) /* 177 */
+ ENTRY("", 178, no_syscall, 0, CALL_INDIRECT, VOID) /* 178 */
+ ENTRY("", 179, no_syscall, 0, CALL_INDIRECT, VOID) /* 179 */
+ ENTRY("kdebug_trace", SYS_kdebug_trace, no_syscall, 6, CALL_INDIRECT, VOID) /* 180 */
+ ENTRY("setgid", SYS_setgid, setgid, 1, CALL_DIRECT, INT) /* 181 */
+ ENTRY("setegid", SYS_setegid, setegid, 1, CALL_DIRECT, INT) /* 182 */
+ ENTRY("seteuid", SYS_seteuid, seteuid, 1, CALL_DIRECT, INT) /* 183 */
+ ENTRY("sigreturn", SYS_sigreturn, do_sigreturn, 2, CALL_INDIRECT, PTR, INT) /* 184 */
+ ENTRY("chud", SYS_chud, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 185 */
+ ENTRY("", 186, no_syscall, 0, CALL_INDIRECT, VOID) /* 186 */
+ ENTRY("", 187, no_syscall, 0, CALL_INDIRECT, VOID) /* 187 */
+ ENTRY("stat", SYS_stat, do_stat, 2, CALL_DIRECT, PTR, PTR) /* 188 */
+ ENTRY("fstat", SYS_fstat, do_fstat, 2, CALL_DIRECT, INT, PTR) /* 189 */
+ ENTRY("lstat", SYS_lstat, do_lstat, 2, CALL_DIRECT, PTR, PTR) /* 190 */
+ ENTRY("pathconf", SYS_pathconf, pathconf, 2, CALL_DIRECT, PTR, INT) /* 191 */
+ ENTRY("fpathconf", SYS_fpathconf, fpathconf, 2, CALL_DIRECT, INT, INT) /* 192 */
+ ENTRY("getfsstat", SYS_getfsstat, do_getfsstat, 3, CALL_DIRECT, PTR, INT, INT) /* 193 */
+ ENTRY("", 193, no_syscall, 0, CALL_INDIRECT, VOID) /* 193 */
+ ENTRY("getrlimit", SYS_getrlimit, getrlimit, 2, CALL_DIRECT, UINT, PTR) /* 194 */
+ ENTRY("setrlimit", SYS_setrlimit, setrlimit, 2, CALL_DIRECT, UINT, PTR) /* 195 */
+ ENTRY("getdirentries", SYS_getdirentries, do_getdirentries, 4, CALL_DIRECT, INT, PTR, UINT, PTR) /* 196 */
+ ENTRY("mmap", SYS_mmap, target_mmap, 6, CALL_DIRECT, UINT /*PTR*/, SIZE, INT, INT, INT, OFFSET) /* 197 */
+ ENTRY("", 198, no_syscall, 0, CALL_INDIRECT, VOID) /* 198 __syscall */
+ ENTRY("lseek", SYS_lseek, do_lseek, 3, CALL_INDIRECT, INT, OFFSET, INT) /* 199 */
+ ENTRY("truncate", SYS_truncate, truncate, 2, CALL_DIRECT, PTR, OFFSET) /* 200 */
+ ENTRY("ftruncate", SYS_ftruncate, ftruncate, 2, CALL_DIRECT, INT, OFFSET) /* 201 */
+ ENTRY("__sysctl", SYS___sysctl, do___sysctl, 6, CALL_DIRECT, PTR, INT, PTR, PTR, PTR, SIZE) /* 202 */
+ ENTRY("mlock", SYS_mlock, mlock, 2, CALL_DIRECT, PTR, SIZE) /* 203 */
+ ENTRY("munlock", SYS_munlock, munlock, 2, CALL_DIRECT, PTR, SIZE) /* 204 */
+ ENTRY("undelete", SYS_undelete, undelete, 1, CALL_DIRECT, PTR) /* 205 */
+ ENTRY("ATsocket", SYS_ATsocket, no_syscall, 1, CALL_INDIRECT, VOID) /* 206 */
+ ENTRY("ATgetmsg", SYS_ATgetmsg, no_syscall, 4, CALL_INDIRECT, VOID) /* 207 */
+ ENTRY("ATputmsg", SYS_ATputmsg, no_syscall, 4, CALL_INDIRECT, VOID) /* 208 */
+ ENTRY("ATPsndreq", SYS_ATPsndreq, no_syscall, 4, CALL_INDIRECT, VOID) /* 209 */
+ ENTRY("ATPsndrsp", SYS_ATPsndrsp, no_syscall, 4, CALL_INDIRECT, VOID) /* 210 */
+ ENTRY("ATPgetreq", SYS_ATPgetreq, no_syscall, 3, CALL_INDIRECT, VOID) /* 211 */
+ ENTRY("ATPgetrsp", SYS_ATPgetrsp, no_syscall, 2, CALL_INDIRECT, VOID) /* 212 */
+ ENTRY("", 213, no_syscall, 0, CALL_INDIRECT, VOID) /* 213 Reserved for AppleTalk */
+ ENTRY("kqueue_from_portset_np", SYS_kqueue_from_portset_np, no_syscall, 1, CALL_INDIRECT, VOID) /* 214 */
+ ENTRY("kqueue_portset_np", SYS_kqueue_portset_np, no_syscall, 1, CALL_INDIRECT, VOID) /* 215 */
+ ENTRY("mkcomplex", SYS_mkcomplex, no_syscall, 3, CALL_INDIRECT, VOID) /* 216 soon to be obsolete */
+ ENTRY("statv", SYS_statv, no_syscall, 2, CALL_INDIRECT, VOID) /* 217 soon to be obsolete */
+ ENTRY("lstatv", SYS_lstatv, no_syscall, 2, CALL_INDIRECT, VOID) /* 218 soon to be obsolete */
+ ENTRY("fstatv", SYS_fstatv, no_syscall, 2, CALL_INDIRECT, VOID) /* 219 soon to be obsolete */
+ ENTRY("getattrlist", SYS_getattrlist, do_getattrlist, 5, CALL_DIRECT, PTR, PTR, PTR, SIZE, UINT) /* 220 */
+ ENTRY("setattrlist", SYS_setattrlist, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 221 */
+ ENTRY("getdirentriesattr", SYS_getdirentriesattr, do_getdirentriesattr, 8, CALL_DIRECT, INT, PTR, PTR, SIZE, PTR, PTR, PTR, UINT) /* 222 */
+ ENTRY("exchangedata", SYS_exchangedata, exchangedata, 3, CALL_DIRECT, PTR, PTR, UINT) /* 223 */
+ ENTRY("checkuseraccess", SYS_checkuseraccess, checkuseraccess, 6, CALL_DIRECT, PTR, INT, PTR, INT, INT, UINT) /* 224 */
+ ENTRY("", 224, no_syscall, 0, CALL_INDIRECT, VOID) /* 224 HFS checkuseraccess check access to a file */
+ ENTRY("searchfs", SYS_searchfs, searchfs, 6, CALL_DIRECT, PTR, PTR, PTR, UINT, UINT, PTR) /* 225 */
+ ENTRY("delete", SYS_delete, no_syscall, 1, CALL_INDIRECT, VOID) /* 226 private delete ( Carbon semantics ) */
+ ENTRY("copyfile", SYS_copyfile, no_syscall, 4, CALL_INDIRECT, VOID) /* 227 */
+ ENTRY("", 228, no_syscall, 0, CALL_INDIRECT, VOID) /* 228 */
+ ENTRY("", 229, no_syscall, 0, CALL_INDIRECT, VOID) /* 229 */
+ ENTRY("poll", SYS_poll, no_syscall, 3, CALL_INDIRECT, VOID) /* 230 */
+ ENTRY("watchevent", SYS_watchevent, no_syscall, 2, CALL_INDIRECT, VOID) /* 231 */
+ ENTRY("waitevent", SYS_waitevent, no_syscall, 2, CALL_INDIRECT, VOID) /* 232 */
+ ENTRY("modwatch", SYS_modwatch, no_syscall, 2, CALL_INDIRECT, VOID) /* 233 */
+ ENTRY("getxattr", SYS_getxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 234 */
+ ENTRY("fgetxattr", SYS_fgetxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 235 */
+ ENTRY("setxattr", SYS_setxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 236 */
+ ENTRY("fsetxattr", SYS_fsetxattr, no_syscall, 6, CALL_INDIRECT, VOID) /* 237 */
+ ENTRY("removexattr", SYS_removexattr, no_syscall, 3, CALL_INDIRECT, VOID) /* 238 */
+ ENTRY("fremovexattr", SYS_fremovexattr, no_syscall, 3, CALL_INDIRECT, VOID) /* 239 */
+ ENTRY("listxattr", SYS_listxattr, listxattr, 4, CALL_INDIRECT, VOID) /* 240 */
+ ENTRY("flistxattr", SYS_flistxattr, no_syscall, 4, CALL_INDIRECT, VOID) /* 241 */
+ ENTRY("fsctl", SYS_fsctl, fsctl, 4, CALL_DIRECT, PTR, UINT, PTR, UINT) /* 242 */
+ ENTRY("initgroups", SYS_initgroups, unimpl_unix_syscall, 3, CALL_INDIRECT, UINT, PTR, INT) /* 243 */
+ ENTRY("", 244, no_syscall, 0, CALL_INDIRECT, VOID) /* 244 */
+ ENTRY("", 245, no_syscall, 0, CALL_INDIRECT, VOID) /* 245 */
+ ENTRY("", 246, no_syscall, 0, CALL_INDIRECT, VOID) /* 246 */
+#ifdef SYS_nfsclnt
+ ENTRY("nfsclnt", SYS_nfsclnt, nfsclnt, 2, CALL_DIRECT, INT, PTR) /* 247 */
+#else
+ ENTRY("nfsclnt", 247, no_syscall, 2, CALL_INDIRECT, VOID) /* 247 */
+#endif
+ ENTRY("", 247, no_syscall, 0, CALL_INDIRECT, VOID) /* 247 */
+ ENTRY("", 248, no_syscall, 0, CALL_INDIRECT, VOID) /* 248 */
+ ENTRY("", 249, no_syscall, 0, CALL_INDIRECT, VOID) /* 249 */
+ ENTRY("minherit", SYS_minherit, minherit, 3, CALL_DIRECT, PTR, INT, INT) /* 250 */
+ ENTRY("semsys", SYS_semsys, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 251 */
+ ENTRY("msgsys", SYS_msgsys, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 252 */
+ ENTRY("shmsys", SYS_shmsys, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 253 */
+ ENTRY("semctl", SYS_semctl, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 254 */
+ ENTRY("semget", SYS_semget, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 255 */
+ ENTRY("semop", SYS_semop, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 256 */
+ ENTRY("", 257, no_syscall, 0, CALL_INDIRECT, VOID) /* 257 */
+ ENTRY("msgctl", SYS_msgctl, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 258 */
+ ENTRY("msgget", SYS_msgget, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 259 */
+ ENTRY("msgsnd", SYS_msgsnd, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 260 */
+ ENTRY("msgrcv", SYS_msgrcv, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 261 */
+ ENTRY("shmat", SYS_shmat, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 262 */
+ ENTRY("shmctl", SYS_shmctl, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 263 */
+ ENTRY("shmdt", SYS_shmdt, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 264 */
+ ENTRY("shmget", SYS_shmget, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 265 */
+ ENTRY("shm_open", SYS_shm_open, shm_open, 3, CALL_DIRECT, PTR, INT, INT) /* 266 */
+ ENTRY("shm_unlink", SYS_shm_unlink, shm_unlink, 1, CALL_DIRECT, PTR) /* 267 */
+ ENTRY("sem_open", SYS_sem_open, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 268 */
+ ENTRY("sem_close", SYS_sem_close, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 269 */
+ ENTRY("sem_unlink", SYS_sem_unlink, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 270 */
+ ENTRY("sem_wait", SYS_sem_wait, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 271 */
+ ENTRY("sem_trywait", SYS_sem_trywait, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 272 */
+ ENTRY("sem_post", SYS_sem_post, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 273 */
+ ENTRY("sem_getvalue", SYS_sem_getvalue, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 274 */
+ ENTRY("sem_init", SYS_sem_init, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 275 */
+ ENTRY("sem_destroy", SYS_sem_destroy, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 276 */
+ ENTRY("open_extended", SYS_open_extended, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 277 */
+ ENTRY("umask_extended", SYS_umask_extended, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 278 */
+ ENTRY("stat_extended", SYS_stat_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 279 */
+ ENTRY("lstat_extended", SYS_lstat_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 280 */
+ ENTRY("fstat_extended", SYS_fstat_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 281 */
+ ENTRY("chmod_extended", SYS_chmod_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 282 */
+ ENTRY("fchmod_extended", SYS_fchmod_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 283 */
+ ENTRY("access_extended", SYS_access_extended, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 284 */
+ ENTRY("settid", SYS_settid, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 285 */
+ ENTRY("gettid", SYS_gettid, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 286 */
+ ENTRY("setsgroups", SYS_setsgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 287 */
+ ENTRY("getsgroups", SYS_getsgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 288 */
+ ENTRY("setwgroups", SYS_setwgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 289 */
+ ENTRY("getwgroups", SYS_getwgroups, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 290 */
+ ENTRY("mkfifo_extended", SYS_mkfifo_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 291 */
+ ENTRY("mkdir_extended", SYS_mkdir_extended, unimpl_unix_syscall, 5, CALL_INDIRECT, VOID) /* 292 */
+ ENTRY("identitysvc", SYS_identitysvc, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 293 */
+ ENTRY("", 294, no_syscall, 0, CALL_INDIRECT, VOID) /* 294 */
+ ENTRY("", 295, no_syscall, 0, CALL_INDIRECT, VOID) /* 295 */
+ ENTRY("load_shared_file", SYS_load_shared_file, unimpl_unix_syscall, 7, CALL_INDIRECT, VOID) /* 296 */
+ ENTRY("reset_shared_file", SYS_reset_shared_file, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 297 */
+ ENTRY("new_system_shared_regions", SYS_new_system_shared_regions, unimpl_unix_syscall, 0, CALL_INDIRECT, VOID) /* 298 */
+ ENTRY("shared_region_map_file_np", SYS_shared_region_map_file_np, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 299 */
+ ENTRY("shared_region_make_private_np", SYS_shared_region_make_private_np, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 300 */
+ ENTRY("", 301, no_syscall, 0, CALL_INDIRECT, VOID) /* 301 */
+ ENTRY("", 302, no_syscall, 0, CALL_INDIRECT, VOID) /* 302 */
+ ENTRY("", 303, no_syscall, 0, CALL_INDIRECT, VOID) /* 303 */
+ ENTRY("", 304, no_syscall, 0, CALL_INDIRECT, VOID) /* 304 */
+ ENTRY("", 305, no_syscall, 0, CALL_INDIRECT, VOID) /* 305 */
+ ENTRY("", 306, no_syscall, 0, CALL_INDIRECT, VOID) /* 306 */
+ ENTRY("", 307, no_syscall, 0, CALL_INDIRECT, VOID) /* 307 */
+ ENTRY("", 308, no_syscall, 0, CALL_INDIRECT, VOID) /* 308 */
+ ENTRY("", 309, no_syscall, 0, CALL_INDIRECT, VOID) /* 309 */
+ ENTRY("getsid", SYS_getsid, getsid, 1, CALL_DIRECT, INT) /* 310 */
+ ENTRY("settid_with_pid", SYS_settid_with_pid, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 311 */
+ ENTRY("", 312, no_syscall, 0, CALL_INDIRECT, VOID) /* 312 */
+ ENTRY("aio_fsync", SYS_aio_fsync, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 313 */
+ ENTRY("aio_return", SYS_aio_return, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 314 */
+ ENTRY("aio_suspend", SYS_aio_suspend, unimpl_unix_syscall, 3, CALL_INDIRECT, VOID) /* 315 */
+ ENTRY("aio_cancel", SYS_aio_cancel, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 316 */
+ ENTRY("aio_error", SYS_aio_error, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 317 */
+ ENTRY("aio_read", SYS_aio_read, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 318 */
+ ENTRY("aio_write", SYS_aio_write, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 319 */
+ ENTRY("lio_listio", SYS_lio_listio, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 320 */
+ ENTRY("", 321, no_syscall, 0, CALL_INDIRECT, VOID) /* 321 */
+ ENTRY("", 322, no_syscall, 0, CALL_INDIRECT, VOID) /* 322 */
+ ENTRY("", 323, no_syscall, 0, CALL_INDIRECT, VOID) /* 323 */
+ ENTRY("mlockall", SYS_mlockall, mlockall, 1, CALL_DIRECT, INT) /* 324 */
+ ENTRY("munlockall", SYS_munlockall, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 325 */
+ ENTRY("", 326, no_syscall, 0, CALL_INDIRECT, VOID) /* 326 */
+ ENTRY("issetugid", SYS_issetugid, issetugid, 0, CALL_DIRECT, VOID) /* 327 */
+ ENTRY("__pthread_kill", SYS___pthread_kill, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 328 */
+ ENTRY("pthread_sigmask", SYS_pthread_sigmask, pthread_sigmask, 3, CALL_DIRECT, INT, PTR, PTR) /* 329 */
+ ENTRY("sigwait", SYS_sigwait, sigwait, 2, CALL_DIRECT, PTR, PTR) /* 330 */
+ ENTRY("__disable_threadsignal", SYS___disable_threadsignal, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 331 */
+ ENTRY("__pthread_markcancel", SYS___pthread_markcancel, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 332 */
+ ENTRY("__pthread_canceled", SYS___pthread_canceled, unimpl_unix_syscall, 1, CALL_INDIRECT, VOID) /* 333 */
+ ENTRY("__semwait_signal", SYS___semwait_signal, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 334 */
+ ENTRY("utrace", SYS_utrace, unimpl_unix_syscall, 2, CALL_INDIRECT, VOID) /* 335 */
+ ENTRY("proc_info", SYS_proc_info, unimpl_unix_syscall, 6, CALL_INDIRECT, VOID) /* 336 */
+ ENTRY("", 337, no_syscall, 0, CALL_INDIRECT, VOID) /* 337 */
+ ENTRY("", 338, no_syscall, 0, CALL_INDIRECT, VOID) /* 338 */
+ ENTRY("", 339, no_syscall, 0, CALL_INDIRECT, VOID) /* 339 */
+ ENTRY("", 340, no_syscall, 0, CALL_INDIRECT, VOID) /* 340 */
+ ENTRY("", 341, no_syscall, 0, CALL_INDIRECT, VOID) /* 341 */
+ ENTRY("", 342, no_syscall, 0, CALL_INDIRECT, VOID) /* 342 */
+ ENTRY("", 343, no_syscall, 0, CALL_INDIRECT, VOID) /* 343 */
+ ENTRY("", 344, no_syscall, 0, CALL_INDIRECT, VOID) /* 344 */
+ ENTRY("", 345, no_syscall, 0, CALL_INDIRECT, VOID) /* 345 */
+ ENTRY("", 346, no_syscall, 0, CALL_INDIRECT, VOID) /* 346 */
+ ENTRY("", 347, no_syscall, 0, CALL_INDIRECT, VOID) /* 347 */
+ ENTRY("", 348, no_syscall, 0, CALL_INDIRECT, VOID) /* 348 */
+ ENTRY("", 349, no_syscall, 0, CALL_INDIRECT, VOID) /* 349 */
+ ENTRY("audit", SYS_audit, audit, 2, CALL_DIRECT, PTR, INT) /* 350 */
+ ENTRY("auditon", SYS_auditon, auditon, 3, CALL_DIRECT, INT, PTR, INT) /* 351 */
+ ENTRY("", 352, no_syscall, 0, CALL_INDIRECT, VOID) /* 352 */
+ ENTRY("getauid", SYS_getauid, getauid, 1, CALL_DIRECT, PTR) /* 353 */
+ ENTRY("setauid", SYS_setauid, setauid, 1, CALL_DIRECT, PTR) /* 354 */
+ ENTRY("getaudit", SYS_getaudit, getaudit, 1, CALL_DIRECT, PTR) /* 355 */
+ ENTRY("setaudit", SYS_setaudit, setaudit, 1, CALL_DIRECT, PTR) /* 356 */
+ ENTRY("getaudit_addr", SYS_getaudit_addr, getaudit_addr, 2, CALL_DIRECT, PTR, INT) /* 357 */
+ ENTRY("setaudit_addr", SYS_setaudit_addr, setaudit_addr, 2, CALL_DIRECT, PTR, INT) /* 358 */
+ ENTRY("auditctl", SYS_auditctl, auditctl, 1, CALL_DIRECT, PTR) /* 359 */
+ ENTRY("", 360, no_syscall, 0, CALL_INDIRECT, VOID) /* 360 */
+ ENTRY("", 361, no_syscall, 0, CALL_INDIRECT, VOID) /* 361 */
+ ENTRY("kqueue", SYS_kqueue, kqueue, 0, CALL_DIRECT, VOID) /* 362 */
+ ENTRY("kevent", SYS_kevent, kevent, 6, CALL_DIRECT, INT, PTR, INT, PTR, INT, PTR) /* 363 */
+ ENTRY("lchown", SYS_lchown, lchown, 3, CALL_DIRECT, PTR, INT , INT) /* 364 */
+ ENTRY("stack_snapshot", SYS_stack_snapshot, unimpl_unix_syscall, 4, CALL_INDIRECT, VOID) /* 365 */
+ ENTRY("", 366, no_syscall, 0, CALL_INDIRECT, VOID) /* 366 */
+ ENTRY("", 367, no_syscall, 0, CALL_INDIRECT, VOID) /* 367 */
+ ENTRY("", 368, no_syscall, 0, CALL_INDIRECT, VOID) /* 368 */
+ ENTRY("", 369, no_syscall, 0, CALL_INDIRECT, VOID) /* 369 */
diff --git a/def-helper.h b/def-helper.h
new file mode 100644
index 0000000..8a822c7
--- /dev/null
+++ b/def-helper.h
@@ -0,0 +1,240 @@
+/* Helper file for declaring TCG helper functions.
+ Should be included at the start and end of target-foo/helper.h.
+
+ Targets should use DEF_HELPER_N and DEF_HELPER_FLAGS_N to declare helper
+ functions. Names should be specified without the helper_ prefix, and
+ the return and argument types specified. 3 basic types are understood
+ (i32, i64 and ptr). Additional aliases are provided for convenience and
+ to match the types used by the C helper implementation.
+
+ The target helper.h should be included in all files that use/define
+ helper functions. THis will ensure that function prototypes are
+ consistent. In addition it should be included an extra two times for
+ helper.c, defining:
+ GEN_HELPER 1 to produce op generation functions (gen_helper_*)
+ GEN_HELPER 2 to do runtime registration helper functions.
+ */
+
+#ifndef DEF_HELPER_H
+#define DEF_HELPER_H 1
+
+#define HELPER(name) glue(helper_, name)
+
+#define GET_TCGV_i32 GET_TCGV_I32
+#define GET_TCGV_i64 GET_TCGV_I64
+#define GET_TCGV_ptr GET_TCGV_PTR
+
+/* Some types that make sense in C, but not for TCG. */
+#define dh_alias_i32 i32
+#define dh_alias_s32 i32
+#define dh_alias_int i32
+#define dh_alias_i64 i64
+#define dh_alias_s64 i64
+#define dh_alias_f32 i32
+#define dh_alias_f64 i64
+#if TARGET_LONG_BITS == 32
+#define dh_alias_tl i32
+#else
+#define dh_alias_tl i64
+#endif
+#define dh_alias_ptr ptr
+#define dh_alias_void void
+#define dh_alias_env ptr
+#define dh_alias(t) glue(dh_alias_, t)
+
+#define dh_ctype_i32 uint32_t
+#define dh_ctype_s32 int32_t
+#define dh_ctype_int int
+#define dh_ctype_i64 uint64_t
+#define dh_ctype_s64 int64_t
+#define dh_ctype_f32 float32
+#define dh_ctype_f64 float64
+#define dh_ctype_tl target_ulong
+#define dh_ctype_ptr void *
+#define dh_ctype_void void
+#define dh_ctype_env CPUState *
+#define dh_ctype(t) dh_ctype_##t
+
+/* We can't use glue() here because it falls foul of C preprocessor
+ recursive expansion rules. */
+#define dh_retvar_decl0_void void
+#define dh_retvar_decl0_i32 TCGv_i32 retval
+#define dh_retvar_decl0_i64 TCGv_i64 retval
+#define dh_retvar_decl0_ptr TCGv_ptr retval
+#define dh_retvar_decl0(t) glue(dh_retvar_decl0_, dh_alias(t))
+
+#define dh_retvar_decl_void
+#define dh_retvar_decl_i32 TCGv_i32 retval,
+#define dh_retvar_decl_i64 TCGv_i64 retval,
+#define dh_retvar_decl_ptr TCGv_ptr retval,
+#define dh_retvar_decl(t) glue(dh_retvar_decl_, dh_alias(t))
+
+#define dh_retvar_void TCG_CALL_DUMMY_ARG
+#define dh_retvar_i32 GET_TCGV_i32(retval)
+#define dh_retvar_i64 GET_TCGV_i64(retval)
+#define dh_retvar_ptr GET_TCGV_ptr(retval)
+#define dh_retvar(t) glue(dh_retvar_, dh_alias(t))
+
+#define dh_is_64bit_void 0
+#define dh_is_64bit_i32 0
+#define dh_is_64bit_i64 1
+#define dh_is_64bit_ptr (TCG_TARGET_REG_BITS == 64)
+#define dh_is_64bit(t) glue(dh_is_64bit_, dh_alias(t))
+
+#define dh_is_signed_void 0
+#define dh_is_signed_i32 0
+#define dh_is_signed_s32 1
+#define dh_is_signed_i64 0
+#define dh_is_signed_s64 1
+#define dh_is_signed_f32 0
+#define dh_is_signed_f64 0
+#define dh_is_signed_tl 0
+#define dh_is_signed_int 1
+/* ??? This is highly specific to the host cpu. There are even special
+ extension instructions that may be required, e.g. ia64's addp4. But
+ for now we don't support any 64-bit targets with 32-bit pointers. */
+#define dh_is_signed_ptr 0
+#define dh_is_signed_env dh_is_signed_ptr
+#define dh_is_signed(t) dh_is_signed_##t
+
+#define dh_sizemask(t, n) \
+ sizemask |= dh_is_64bit(t) << (n*2); \
+ sizemask |= dh_is_signed(t) << (n*2+1)
+
+#define dh_arg(t, n) \
+ args[n - 1] = glue(GET_TCGV_, dh_alias(t))(glue(arg, n)); \
+ dh_sizemask(t, n)
+
+#define dh_arg_decl(t, n) glue(TCGv_, dh_alias(t)) glue(arg, n)
+
+
+#define DEF_HELPER_0(name, ret) \
+ DEF_HELPER_FLAGS_0(name, 0, ret)
+#define DEF_HELPER_1(name, ret, t1) \
+ DEF_HELPER_FLAGS_1(name, 0, ret, t1)
+#define DEF_HELPER_2(name, ret, t1, t2) \
+ DEF_HELPER_FLAGS_2(name, 0, ret, t1, t2)
+#define DEF_HELPER_3(name, ret, t1, t2, t3) \
+ DEF_HELPER_FLAGS_3(name, 0, ret, t1, t2, t3)
+#define DEF_HELPER_4(name, ret, t1, t2, t3, t4) \
+ DEF_HELPER_FLAGS_4(name, 0, ret, t1, t2, t3, t4)
+
+#endif /* DEF_HELPER_H */
+
+#ifndef GEN_HELPER
+/* Function prototypes. */
+
+#define DEF_HELPER_FLAGS_0(name, flags, ret) \
+dh_ctype(ret) HELPER(name) (void);
+
+#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \
+dh_ctype(ret) HELPER(name) (dh_ctype(t1));
+
+#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \
+dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2));
+
+#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \
+dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3));
+
+#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \
+dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \
+ dh_ctype(t4));
+
+#undef GEN_HELPER
+#define GEN_HELPER -1
+
+#elif GEN_HELPER == 1
+/* Gen functions. */
+
+#define DEF_HELPER_FLAGS_0(name, flags, ret) \
+static inline void glue(gen_helper_, name)(dh_retvar_decl0(ret)) \
+{ \
+ int sizemask; \
+ sizemask = dh_is_64bit(ret); \
+ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 0, NULL); \
+}
+
+#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \
+static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1)) \
+{ \
+ TCGArg args[1]; \
+ int sizemask = 0; \
+ dh_sizemask(ret, 0); \
+ dh_arg(t1, 1); \
+ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 1, args); \
+}
+
+#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \
+static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1), \
+ dh_arg_decl(t2, 2)) \
+{ \
+ TCGArg args[2]; \
+ int sizemask = 0; \
+ dh_sizemask(ret, 0); \
+ dh_arg(t1, 1); \
+ dh_arg(t2, 2); \
+ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 2, args); \
+}
+
+#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \
+static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1), \
+ dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \
+{ \
+ TCGArg args[3]; \
+ int sizemask = 0; \
+ dh_sizemask(ret, 0); \
+ dh_arg(t1, 1); \
+ dh_arg(t2, 2); \
+ dh_arg(t3, 3); \
+ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 3, args); \
+}
+
+#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \
+static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1), \
+ dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \
+{ \
+ TCGArg args[4]; \
+ int sizemask = 0; \
+ dh_sizemask(ret, 0); \
+ dh_arg(t1, 1); \
+ dh_arg(t2, 2); \
+ dh_arg(t3, 3); \
+ dh_arg(t4, 4); \
+ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 4, args); \
+}
+
+#undef GEN_HELPER
+#define GEN_HELPER -1
+
+#elif GEN_HELPER == 2
+/* Register helpers. */
+
+#define DEF_HELPER_FLAGS_0(name, flags, ret) \
+tcg_register_helper(HELPER(name), #name);
+
+#define DEF_HELPER_FLAGS_1(name, flags, ret, t1) \
+DEF_HELPER_FLAGS_0(name, flags, ret)
+
+#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \
+DEF_HELPER_FLAGS_0(name, flags, ret)
+
+#define DEF_HELPER_FLAGS_3(name, flags, ret, t1, t2, t3) \
+DEF_HELPER_FLAGS_0(name, flags, ret)
+
+#define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \
+DEF_HELPER_FLAGS_0(name, flags, ret)
+
+#undef GEN_HELPER
+#define GEN_HELPER -1
+
+#elif GEN_HELPER == -1
+/* Undefine macros. */
+
+#undef DEF_HELPER_FLAGS_0
+#undef DEF_HELPER_FLAGS_1
+#undef DEF_HELPER_FLAGS_2
+#undef DEF_HELPER_FLAGS_3
+#undef DEF_HELPER_FLAGS_4
+#undef GEN_HELPER
+
+#endif
diff --git a/default-configs/alpha-linux-user.mak b/default-configs/alpha-linux-user.mak
new file mode 100644
index 0000000..7956e29
--- /dev/null
+++ b/default-configs/alpha-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for alpha-linux-user
diff --git a/default-configs/arm-linux-user.mak b/default-configs/arm-linux-user.mak
new file mode 100644
index 0000000..46d4aa2
--- /dev/null
+++ b/default-configs/arm-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for arm-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
new file mode 100644
index 0000000..8d1174f
--- /dev/null
+++ b/default-configs/arm-softmmu.mak
@@ -0,0 +1,28 @@
+# Default configuration for arm-softmmu
+
+include pci.mak
+CONFIG_GDBSTUB_XML=y
+CONFIG_ISA_MMIO=y
+CONFIG_NAND=y
+CONFIG_ECC=y
+CONFIG_SERIAL=y
+CONFIG_PTIMER=y
+CONFIG_SD=y
+CONFIG_MAX7310=y
+CONFIG_WM8750=y
+CONFIG_TWL92230=y
+CONFIG_TSC2005=y
+CONFIG_LM832X=y
+CONFIG_TMP105=y
+CONFIG_STELLARIS_INPUT=y
+CONFIG_SSD0303=y
+CONFIG_SSD0323=y
+CONFIG_ADS7846=y
+CONFIG_MAX111X=y
+CONFIG_SSI=y
+CONFIG_SSI_SD=y
+CONFIG_LAN9118=y
+CONFIG_SMC91C111=y
+CONFIG_DS1338=y
+CONFIG_PFLASH_CFI01=y
+CONFIG_PFLASH_CFI02=y
diff --git a/default-configs/armeb-linux-user.mak b/default-configs/armeb-linux-user.mak
new file mode 100644
index 0000000..41d0cc4
--- /dev/null
+++ b/default-configs/armeb-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for armeb-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/cris-linux-user.mak b/default-configs/cris-linux-user.mak
new file mode 100644
index 0000000..e3aec7b
--- /dev/null
+++ b/default-configs/cris-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for cris-linux-user
diff --git a/default-configs/cris-softmmu.mak b/default-configs/cris-softmmu.mak
new file mode 100644
index 0000000..1a479cd
--- /dev/null
+++ b/default-configs/cris-softmmu.mak
@@ -0,0 +1,5 @@
+# Default configuration for cris-softmmu
+
+CONFIG_NAND=y
+CONFIG_PTIMER=y
+CONFIG_PFLASH_CFI02=y
diff --git a/default-configs/i386-bsd-user.mak b/default-configs/i386-bsd-user.mak
new file mode 100644
index 0000000..af1b31a
--- /dev/null
+++ b/default-configs/i386-bsd-user.mak
@@ -0,0 +1 @@
+# Default configuration for i386-bsd-user
diff --git a/default-configs/i386-darwin-user.mak b/default-configs/i386-darwin-user.mak
new file mode 100644
index 0000000..19afd3d
--- /dev/null
+++ b/default-configs/i386-darwin-user.mak
@@ -0,0 +1 @@
+# Default configuration for i386-darwin-user
diff --git a/default-configs/i386-linux-user.mak b/default-configs/i386-linux-user.mak
new file mode 100644
index 0000000..8657e68
--- /dev/null
+++ b/default-configs/i386-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for i386-linux-user
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
new file mode 100644
index 0000000..323fafb
--- /dev/null
+++ b/default-configs/i386-softmmu.mak
@@ -0,0 +1,20 @@
+# Default configuration for i386-softmmu
+
+include pci.mak
+CONFIG_VGA_PCI=y
+CONFIG_VGA_ISA=y
+CONFIG_VMWARE_VGA=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_I8254=y
+CONFIG_PCSPK=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_ACPI=y
+CONFIG_APM=y
+CONFIG_DMA=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_PIIX=y
+CONFIG_NE2000_ISA=y
+CONFIG_PIIX_PCI=y
+CONFIG_SOUND=y
diff --git a/default-configs/m68k-linux-user.mak b/default-configs/m68k-linux-user.mak
new file mode 100644
index 0000000..f3487aa
--- /dev/null
+++ b/default-configs/m68k-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for m68k-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak
new file mode 100644
index 0000000..3e2ec37
--- /dev/null
+++ b/default-configs/m68k-softmmu.mak
@@ -0,0 +1,5 @@
+# Default configuration for m68k-softmmu
+
+include pci.mak
+CONFIG_GDBSTUB_XML=y
+CONFIG_PTIMER=y
diff --git a/default-configs/microblaze-linux-user.mak b/default-configs/microblaze-linux-user.mak
new file mode 100644
index 0000000..566fdc0
--- /dev/null
+++ b/default-configs/microblaze-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for microblaze-linux-user
diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
new file mode 100644
index 0000000..4399b8b
--- /dev/null
+++ b/default-configs/microblaze-softmmu.mak
@@ -0,0 +1,4 @@
+# Default configuration for microblaze-softmmu
+
+CONFIG_PTIMER=y
+CONFIG_PFLASH_CFI01=y
diff --git a/default-configs/mips-linux-user.mak b/default-configs/mips-linux-user.mak
new file mode 100644
index 0000000..31df570
--- /dev/null
+++ b/default-configs/mips-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for mips-linux-user
diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak
new file mode 100644
index 0000000..f524971
--- /dev/null
+++ b/default-configs/mips-softmmu.mak
@@ -0,0 +1,28 @@
+# Default configuration for mips-softmmu
+
+include pci.mak
+CONFIG_ISA_MMIO=y
+CONFIG_ESP=y
+CONFIG_VGA_PCI=y
+CONFIG_VGA_ISA=y
+CONFIG_VGA_ISA_MM=y
+CONFIG_VMWARE_VGA=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_I8254=y
+CONFIG_PCSPK=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_ACPI=y
+CONFIG_APM=y
+CONFIG_DMA=y
+CONFIG_PIIX4=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_PIIX=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_RC4030=y
+CONFIG_DP8393X=y
+CONFIG_DS1225Y=y
+CONFIG_MIPSNET=y
+CONFIG_PFLASH_CFI01=y
diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak
new file mode 100644
index 0000000..aeab6b2
--- /dev/null
+++ b/default-configs/mips64-softmmu.mak
@@ -0,0 +1,28 @@
+# Default configuration for mips64-softmmu
+
+include pci.mak
+CONFIG_ISA_MMIO=y
+CONFIG_ESP=y
+CONFIG_VGA_PCI=y
+CONFIG_VGA_ISA=y
+CONFIG_VGA_ISA_MM=y
+CONFIG_VMWARE_VGA=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_I8254=y
+CONFIG_PCSPK=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_ACPI=y
+CONFIG_APM=y
+CONFIG_DMA=y
+CONFIG_PIIX4=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_PIIX=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_RC4030=y
+CONFIG_DP8393X=y
+CONFIG_DS1225Y=y
+CONFIG_MIPSNET=y
+CONFIG_PFLASH_CFI01=y
diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak
new file mode 100644
index 0000000..8e6511c
--- /dev/null
+++ b/default-configs/mips64el-softmmu.mak
@@ -0,0 +1,30 @@
+# Default configuration for mips64el-softmmu
+
+include pci.mak
+CONFIG_ISA_MMIO=y
+CONFIG_ESP=y
+CONFIG_VGA_PCI=y
+CONFIG_VGA_ISA=y
+CONFIG_VGA_ISA_MM=y
+CONFIG_VMWARE_VGA=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_I8254=y
+CONFIG_PCSPK=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_ACPI=y
+CONFIG_APM=y
+CONFIG_DMA=y
+CONFIG_PIIX4=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_PIIX=y
+CONFIG_IDE_VIA=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_RC4030=y
+CONFIG_DP8393X=y
+CONFIG_DS1225Y=y
+CONFIG_MIPSNET=y
+CONFIG_PFLASH_CFI01=y
+CONFIG_FULONG=y
diff --git a/default-configs/mipsel-linux-user.mak b/default-configs/mipsel-linux-user.mak
new file mode 100644
index 0000000..4d0e4af
--- /dev/null
+++ b/default-configs/mipsel-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for mipsel-linux-user
diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak
new file mode 100644
index 0000000..a05ac25
--- /dev/null
+++ b/default-configs/mipsel-softmmu.mak
@@ -0,0 +1,28 @@
+# Default configuration for mipsel-softmmu
+
+include pci.mak
+CONFIG_ISA_MMIO=y
+CONFIG_ESP=y
+CONFIG_VGA_PCI=y
+CONFIG_VGA_ISA=y
+CONFIG_VGA_ISA_MM=y
+CONFIG_VMWARE_VGA=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_I8254=y
+CONFIG_PCSPK=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_ACPI=y
+CONFIG_APM=y
+CONFIG_DMA=y
+CONFIG_PIIX4=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_PIIX=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_RC4030=y
+CONFIG_DP8393X=y
+CONFIG_DS1225Y=y
+CONFIG_MIPSNET=y
+CONFIG_PFLASH_CFI01=y
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
new file mode 100644
index 0000000..0471efb
--- /dev/null
+++ b/default-configs/pci.mak
@@ -0,0 +1,16 @@
+CONFIG_PCI=y
+CONFIG_VIRTIO_PCI=y
+CONFIG_VIRTIO=y
+CONFIG_USB_UHCI=y
+CONFIG_USB_OHCI=y
+CONFIG_NE2000_PCI=y
+CONFIG_EEPRO100_PCI=y
+CONFIG_PCNET_PCI=y
+CONFIG_PCNET_COMMON=y
+CONFIG_LSI_SCSI_PCI=y
+CONFIG_RTL8139_PCI=y
+CONFIG_E1000_PCI=y
+CONFIG_IDE_CORE=y
+CONFIG_IDE_QDEV=y
+CONFIG_IDE_PCI=y
+CONFIG_AHCI=y
diff --git a/default-configs/ppc-darwin-user.mak b/default-configs/ppc-darwin-user.mak
new file mode 100644
index 0000000..153ed12
--- /dev/null
+++ b/default-configs/ppc-darwin-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for ppc-darwin-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/ppc-linux-user.mak b/default-configs/ppc-linux-user.mak
new file mode 100644
index 0000000..681a945
--- /dev/null
+++ b/default-configs/ppc-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for ppc-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
new file mode 100644
index 0000000..4563742
--- /dev/null
+++ b/default-configs/ppc-softmmu.mak
@@ -0,0 +1,33 @@
+# Default configuration for ppc-softmmu
+
+include pci.mak
+CONFIG_GDBSTUB_XML=y
+CONFIG_ISA_MMIO=y
+CONFIG_ESCC=y
+CONFIG_M48T59=y
+CONFIG_VGA_PCI=y
+CONFIG_SERIAL=y
+CONFIG_I8254=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_DMA=y
+CONFIG_OPENPIC=y
+CONFIG_PREP_PCI=y
+CONFIG_MACIO=y
+CONFIG_CUDA=y
+CONFIG_ADB=y
+CONFIG_MAC_NVRAM=y
+CONFIG_MAC_DBDMA=y
+CONFIG_HEATHROW_PIC=y
+CONFIG_GRACKLE_PCI=y
+CONFIG_UNIN_PCI=y
+CONFIG_DEC_PCI=y
+CONFIG_PPCE500_PCI=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_CMD646=y
+CONFIG_IDE_MACIO=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_PFLASH_CFI01=y
+CONFIG_PFLASH_CFI02=y
+CONFIG_PTIMER=y
diff --git a/default-configs/ppc64-linux-user.mak b/default-configs/ppc64-linux-user.mak
new file mode 100644
index 0000000..089c08f
--- /dev/null
+++ b/default-configs/ppc64-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for ppc64-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
new file mode 100644
index 0000000..d5073b3
--- /dev/null
+++ b/default-configs/ppc64-softmmu.mak
@@ -0,0 +1,33 @@
+# Default configuration for ppc64-softmmu
+
+include pci.mak
+CONFIG_GDBSTUB_XML=y
+CONFIG_ISA_MMIO=y
+CONFIG_ESCC=y
+CONFIG_M48T59=y
+CONFIG_VGA_PCI=y
+CONFIG_SERIAL=y
+CONFIG_I8254=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_DMA=y
+CONFIG_OPENPIC=y
+CONFIG_PREP_PCI=y
+CONFIG_MACIO=y
+CONFIG_CUDA=y
+CONFIG_ADB=y
+CONFIG_MAC_NVRAM=y
+CONFIG_MAC_DBDMA=y
+CONFIG_HEATHROW_PIC=y
+CONFIG_GRACKLE_PCI=y
+CONFIG_UNIN_PCI=y
+CONFIG_DEC_PCI=y
+CONFIG_PPCE500_PCI=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_CMD646=y
+CONFIG_IDE_MACIO=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_PFLASH_CFI01=y
+CONFIG_PFLASH_CFI02=y
+CONFIG_PTIMER=y
diff --git a/default-configs/ppc64abi32-linux-user.mak b/default-configs/ppc64abi32-linux-user.mak
new file mode 100644
index 0000000..f038ffd
--- /dev/null
+++ b/default-configs/ppc64abi32-linux-user.mak
@@ -0,0 +1,3 @@
+# Default configuration for ppc64abi32-linux-user
+
+CONFIG_GDBSTUB_XML=y
diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak
new file mode 100644
index 0000000..9f0730c
--- /dev/null
+++ b/default-configs/ppcemb-softmmu.mak
@@ -0,0 +1,33 @@
+# Default configuration for ppcemb-softmmu
+
+include pci.mak
+CONFIG_GDBSTUB_XML=y
+CONFIG_ISA_MMIO=y
+CONFIG_ESCC=y
+CONFIG_M48T59=y
+CONFIG_VGA_PCI=y
+CONFIG_SERIAL=y
+CONFIG_I8254=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_DMA=y
+CONFIG_OPENPIC=y
+CONFIG_PREP_PCI=y
+CONFIG_MACIO=y
+CONFIG_CUDA=y
+CONFIG_ADB=y
+CONFIG_MAC_NVRAM=y
+CONFIG_MAC_DBDMA=y
+CONFIG_HEATHROW_PIC=y
+CONFIG_GRACKLE_PCI=y
+CONFIG_UNIN_PCI=y
+CONFIG_DEC_PCI=y
+CONFIG_PPCE500_PCI=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_CMD646=y
+CONFIG_IDE_MACIO=y
+CONFIG_NE2000_ISA=y
+CONFIG_SOUND=y
+CONFIG_PFLASH_CFI01=y
+CONFIG_PFLASH_CFI02=y
+CONFIG_PTIMER=y
diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak
new file mode 100644
index 0000000..3005729
--- /dev/null
+++ b/default-configs/s390x-softmmu.mak
@@ -0,0 +1 @@
+CONFIG_VIRTIO=y
diff --git a/default-configs/sh4-linux-user.mak b/default-configs/sh4-linux-user.mak
new file mode 100644
index 0000000..a469e19
--- /dev/null
+++ b/default-configs/sh4-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for sh4-linux-user
diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak
new file mode 100644
index 0000000..5c69acc
--- /dev/null
+++ b/default-configs/sh4-softmmu.mak
@@ -0,0 +1,7 @@
+# Default configuration for sh4-softmmu
+
+include pci.mak
+CONFIG_SERIAL=y
+CONFIG_PTIMER=y
+CONFIG_PFLASH_CFI02=y
+CONFIG_ISA_MMIO=y
diff --git a/default-configs/sh4eb-linux-user.mak b/default-configs/sh4eb-linux-user.mak
new file mode 100644
index 0000000..be08ca1
--- /dev/null
+++ b/default-configs/sh4eb-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for sh4eb-linux-user
diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak
new file mode 100644
index 0000000..7cdc122
--- /dev/null
+++ b/default-configs/sh4eb-softmmu.mak
@@ -0,0 +1,7 @@
+# Default configuration for sh4eb-softmmu
+
+include pci.mak
+CONFIG_SERIAL=y
+CONFIG_PTIMER=y
+CONFIG_PFLASH_CFI02=y
+CONFIG_ISA_MMIO=y
diff --git a/default-configs/sparc-bsd-user.mak b/default-configs/sparc-bsd-user.mak
new file mode 100644
index 0000000..21e0950
--- /dev/null
+++ b/default-configs/sparc-bsd-user.mak
@@ -0,0 +1 @@
+# Default configuration for sparc-bsd-user
diff --git a/default-configs/sparc-linux-user.mak b/default-configs/sparc-linux-user.mak
new file mode 100644
index 0000000..9c716d1
--- /dev/null
+++ b/default-configs/sparc-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for sparc-linux-user
diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak
new file mode 100644
index 0000000..b0310c5
--- /dev/null
+++ b/default-configs/sparc-softmmu.mak
@@ -0,0 +1,10 @@
+# Default configuration for sparc-softmmu
+
+CONFIG_ECC=y
+CONFIG_ESP=y
+CONFIG_ESCC=y
+CONFIG_M48T59=y
+CONFIG_PTIMER=y
+CONFIG_FDC=y
+CONFIG_EMPTY_SLOT=y
+CONFIG_PCNET_COMMON=y
diff --git a/default-configs/sparc32plus-linux-user.mak b/default-configs/sparc32plus-linux-user.mak
new file mode 100644
index 0000000..432e880
--- /dev/null
+++ b/default-configs/sparc32plus-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for sparc32plus-linux-user
diff --git a/default-configs/sparc64-bsd-user.mak b/default-configs/sparc64-bsd-user.mak
new file mode 100644
index 0000000..b8b9eea
--- /dev/null
+++ b/default-configs/sparc64-bsd-user.mak
@@ -0,0 +1 @@
+# Default configuration for sparc64-bsd-user
diff --git a/default-configs/sparc64-linux-user.mak b/default-configs/sparc64-linux-user.mak
new file mode 100644
index 0000000..bf1bdd6
--- /dev/null
+++ b/default-configs/sparc64-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for sparc64-linux-user
diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak
new file mode 100644
index 0000000..d8f17e7
--- /dev/null
+++ b/default-configs/sparc64-softmmu.mak
@@ -0,0 +1,13 @@
+# Default configuration for sparc64-softmmu
+
+include pci.mak
+CONFIG_ISA_MMIO=y
+CONFIG_M48T59=y
+CONFIG_PTIMER=y
+CONFIG_VGA_PCI=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_CMD646=y
diff --git a/default-configs/x86_64-bsd-user.mak b/default-configs/x86_64-bsd-user.mak
new file mode 100644
index 0000000..73e5d34
--- /dev/null
+++ b/default-configs/x86_64-bsd-user.mak
@@ -0,0 +1 @@
+# Default configuration for x86_64-bsd-user
diff --git a/default-configs/x86_64-linux-user.mak b/default-configs/x86_64-linux-user.mak
new file mode 100644
index 0000000..bec1d9e
--- /dev/null
+++ b/default-configs/x86_64-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for x86_64-linux-user
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
new file mode 100644
index 0000000..eff26d2
--- /dev/null
+++ b/default-configs/x86_64-softmmu.mak
@@ -0,0 +1,20 @@
+# Default configuration for x86_64-softmmu
+
+include pci.mak
+CONFIG_VGA_PCI=y
+CONFIG_VGA_ISA=y
+CONFIG_VMWARE_VGA=y
+CONFIG_SERIAL=y
+CONFIG_PARALLEL=y
+CONFIG_I8254=y
+CONFIG_PCSPK=y
+CONFIG_PCKBD=y
+CONFIG_FDC=y
+CONFIG_ACPI=y
+CONFIG_APM=y
+CONFIG_DMA=y
+CONFIG_IDE_ISA=y
+CONFIG_IDE_PIIX=y
+CONFIG_NE2000_ISA=y
+CONFIG_PIIX_PCI=y
+CONFIG_SOUND=y
diff --git a/device_tree.c b/device_tree.c
new file mode 100644
index 0000000..426a631
--- /dev/null
+++ b/device_tree.c
@@ -0,0 +1,110 @@
+/*
+ * Functions to help device tree manipulation using libfdt.
+ * It also provides functions to read entries from device tree proc
+ * interface.
+ *
+ * Copyright 2008 IBM Corporation.
+ * Authors: Jerone Young <jyoung5@us.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "device_tree.h"
+#include "hw/loader.h"
+
+#include <libfdt.h>
+
+void *load_device_tree(const char *filename_path, int *sizep)
+{
+ int dt_size;
+ int dt_file_load_size;
+ int ret;
+ void *fdt = NULL;
+
+ *sizep = 0;
+ dt_size = get_image_size(filename_path);
+ if (dt_size < 0) {
+ printf("Unable to get size of device tree file '%s'\n",
+ filename_path);
+ goto fail;
+ }
+
+ /* Expand to 2x size to give enough room for manipulation. */
+ dt_size *= 2;
+ /* First allocate space in qemu for device tree */
+ fdt = qemu_mallocz(dt_size);
+
+ dt_file_load_size = load_image(filename_path, fdt);
+ if (dt_file_load_size < 0) {
+ printf("Unable to open device tree file '%s'\n",
+ filename_path);
+ goto fail;
+ }
+
+ ret = fdt_open_into(fdt, fdt, dt_size);
+ if (ret) {
+ printf("Unable to copy device tree in memory\n");
+ goto fail;
+ }
+
+ /* Check sanity of device tree */
+ if (fdt_check_header(fdt)) {
+ printf ("Device tree file loaded into memory is invalid: %s\n",
+ filename_path);
+ goto fail;
+ }
+ *sizep = dt_size;
+ return fdt;
+
+fail:
+ qemu_free(fdt);
+ return NULL;
+}
+
+int qemu_devtree_setprop(void *fdt, const char *node_path,
+ const char *property, uint32_t *val_array, int size)
+{
+ int offset;
+
+ offset = fdt_path_offset(fdt, node_path);
+ if (offset < 0)
+ return offset;
+
+ return fdt_setprop(fdt, offset, property, val_array, size);
+}
+
+int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
+ const char *property, uint32_t val)
+{
+ int offset;
+
+ offset = fdt_path_offset(fdt, node_path);
+ if (offset < 0)
+ return offset;
+
+ return fdt_setprop_cell(fdt, offset, property, val);
+}
+
+int qemu_devtree_setprop_string(void *fdt, const char *node_path,
+ const char *property, const char *string)
+{
+ int offset;
+
+ offset = fdt_path_offset(fdt, node_path);
+ if (offset < 0)
+ return offset;
+
+ return fdt_setprop_string(fdt, offset, property, string);
+}
diff --git a/device_tree.h b/device_tree.h
new file mode 100644
index 0000000..f05c4e7
--- /dev/null
+++ b/device_tree.h
@@ -0,0 +1,26 @@
+/*
+ * Header with function prototypes to help device tree manipulation using
+ * libfdt. It also provides functions to read entries from device tree proc
+ * interface.
+ *
+ * Copyright 2008 IBM Corporation.
+ * Authors: Jerone Young <jyoung5@us.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#ifndef __DEVICE_TREE_H__
+#define __DEVICE_TREE_H__
+
+void *load_device_tree(const char *filename_path, int *sizep);
+
+int qemu_devtree_setprop(void *fdt, const char *node_path,
+ const char *property, uint32_t *val_array, int size);
+int qemu_devtree_setprop_cell(void *fdt, const char *node_path,
+ const char *property, uint32_t val);
+int qemu_devtree_setprop_string(void *fdt, const char *node_path,
+ const char *property, const char *string);
+
+#endif /* __DEVICE_TREE_H__ */
diff --git a/dis-asm.h b/dis-asm.h
new file mode 100644
index 0000000..296537a
--- /dev/null
+++ b/dis-asm.h
@@ -0,0 +1,476 @@
+/* Interface between the opcode library and its callers.
+ Written by Cygnus Support, 1993.
+
+ The opcode library (libopcodes.a) provides instruction decoders for
+ a large variety of instruction sets, callable with an identical
+ interface, for making instruction-processing programs more independent
+ of the instruction set being processed. */
+
+#ifndef DIS_ASM_H
+#define DIS_ASM_H
+
+#include "qemu-common.h"
+
+typedef void *PTR;
+typedef uint64_t bfd_vma;
+typedef int64_t bfd_signed_vma;
+typedef uint8_t bfd_byte;
+#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x)
+#define snprintf_vma(s,ss,x) snprintf (s, ss, "%0" PRIx64, x)
+
+#define BFD64
+
+enum bfd_flavour {
+ bfd_target_unknown_flavour,
+ bfd_target_aout_flavour,
+ bfd_target_coff_flavour,
+ bfd_target_ecoff_flavour,
+ bfd_target_elf_flavour,
+ bfd_target_ieee_flavour,
+ bfd_target_nlm_flavour,
+ bfd_target_oasys_flavour,
+ bfd_target_tekhex_flavour,
+ bfd_target_srec_flavour,
+ bfd_target_ihex_flavour,
+ bfd_target_som_flavour,
+ bfd_target_os9k_flavour,
+ bfd_target_versados_flavour,
+ bfd_target_msdos_flavour,
+ bfd_target_evax_flavour
+};
+
+enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
+
+enum bfd_architecture
+{
+ bfd_arch_unknown, /* File arch not known */
+ bfd_arch_obscure, /* Arch known, not one of these */
+ bfd_arch_m68k, /* Motorola 68xxx */
+#define bfd_mach_m68000 1
+#define bfd_mach_m68008 2
+#define bfd_mach_m68010 3
+#define bfd_mach_m68020 4
+#define bfd_mach_m68030 5
+#define bfd_mach_m68040 6
+#define bfd_mach_m68060 7
+#define bfd_mach_cpu32 8
+#define bfd_mach_mcf5200 9
+#define bfd_mach_mcf5206e 10
+#define bfd_mach_mcf5307 11
+#define bfd_mach_mcf5407 12
+#define bfd_mach_mcf528x 13
+#define bfd_mach_mcfv4e 14
+#define bfd_mach_mcf521x 15
+#define bfd_mach_mcf5249 16
+#define bfd_mach_mcf547x 17
+#define bfd_mach_mcf548x 18
+ bfd_arch_vax, /* DEC Vax */
+ bfd_arch_i960, /* Intel 960 */
+ /* The order of the following is important.
+ lower number indicates a machine type that
+ only accepts a subset of the instructions
+ available to machines with higher numbers.
+ The exception is the "ca", which is
+ incompatible with all other machines except
+ "core". */
+
+#define bfd_mach_i960_core 1
+#define bfd_mach_i960_ka_sa 2
+#define bfd_mach_i960_kb_sb 3
+#define bfd_mach_i960_mc 4
+#define bfd_mach_i960_xa 5
+#define bfd_mach_i960_ca 6
+#define bfd_mach_i960_jx 7
+#define bfd_mach_i960_hx 8
+
+ bfd_arch_a29k, /* AMD 29000 */
+ bfd_arch_sparc, /* SPARC */
+#define bfd_mach_sparc 1
+/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */
+#define bfd_mach_sparc_sparclet 2
+#define bfd_mach_sparc_sparclite 3
+#define bfd_mach_sparc_v8plus 4
+#define bfd_mach_sparc_v8plusa 5 /* with ultrasparc add'ns. */
+#define bfd_mach_sparc_sparclite_le 6
+#define bfd_mach_sparc_v9 7
+#define bfd_mach_sparc_v9a 8 /* with ultrasparc add'ns. */
+#define bfd_mach_sparc_v8plusb 9 /* with cheetah add'ns. */
+#define bfd_mach_sparc_v9b 10 /* with cheetah add'ns. */
+/* Nonzero if MACH has the v9 instruction set. */
+#define bfd_mach_sparc_v9_p(mach) \
+ ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \
+ && (mach) != bfd_mach_sparc_sparclite_le)
+ bfd_arch_mips, /* MIPS Rxxxx */
+#define bfd_mach_mips3000 3000
+#define bfd_mach_mips3900 3900
+#define bfd_mach_mips4000 4000
+#define bfd_mach_mips4010 4010
+#define bfd_mach_mips4100 4100
+#define bfd_mach_mips4300 4300
+#define bfd_mach_mips4400 4400
+#define bfd_mach_mips4600 4600
+#define bfd_mach_mips4650 4650
+#define bfd_mach_mips5000 5000
+#define bfd_mach_mips6000 6000
+#define bfd_mach_mips8000 8000
+#define bfd_mach_mips10000 10000
+#define bfd_mach_mips16 16
+ bfd_arch_i386, /* Intel 386 */
+#define bfd_mach_i386_i386 0
+#define bfd_mach_i386_i8086 1
+#define bfd_mach_i386_i386_intel_syntax 2
+#define bfd_mach_x86_64 3
+#define bfd_mach_x86_64_intel_syntax 4
+ bfd_arch_we32k, /* AT&T WE32xxx */
+ bfd_arch_tahoe, /* CCI/Harris Tahoe */
+ bfd_arch_i860, /* Intel 860 */
+ bfd_arch_romp, /* IBM ROMP PC/RT */
+ bfd_arch_alliant, /* Alliant */
+ bfd_arch_convex, /* Convex */
+ bfd_arch_m88k, /* Motorola 88xxx */
+ bfd_arch_pyramid, /* Pyramid Technology */
+ bfd_arch_h8300, /* Hitachi H8/300 */
+#define bfd_mach_h8300 1
+#define bfd_mach_h8300h 2
+#define bfd_mach_h8300s 3
+ bfd_arch_powerpc, /* PowerPC */
+#define bfd_mach_ppc 0
+#define bfd_mach_ppc64 1
+#define bfd_mach_ppc_403 403
+#define bfd_mach_ppc_403gc 4030
+#define bfd_mach_ppc_e500 500
+#define bfd_mach_ppc_505 505
+#define bfd_mach_ppc_601 601
+#define bfd_mach_ppc_602 602
+#define bfd_mach_ppc_603 603
+#define bfd_mach_ppc_ec603e 6031
+#define bfd_mach_ppc_604 604
+#define bfd_mach_ppc_620 620
+#define bfd_mach_ppc_630 630
+#define bfd_mach_ppc_750 750
+#define bfd_mach_ppc_860 860
+#define bfd_mach_ppc_a35 35
+#define bfd_mach_ppc_rs64ii 642
+#define bfd_mach_ppc_rs64iii 643
+#define bfd_mach_ppc_7400 7400
+ bfd_arch_rs6000, /* IBM RS/6000 */
+ bfd_arch_hppa, /* HP PA RISC */
+#define bfd_mach_hppa10 10
+#define bfd_mach_hppa11 11
+#define bfd_mach_hppa20 20
+#define bfd_mach_hppa20w 25
+ bfd_arch_d10v, /* Mitsubishi D10V */
+ bfd_arch_z8k, /* Zilog Z8000 */
+#define bfd_mach_z8001 1
+#define bfd_mach_z8002 2
+ bfd_arch_h8500, /* Hitachi H8/500 */
+ bfd_arch_sh, /* Hitachi SH */
+#define bfd_mach_sh 1
+#define bfd_mach_sh2 0x20
+#define bfd_mach_sh_dsp 0x2d
+#define bfd_mach_sh2a 0x2a
+#define bfd_mach_sh2a_nofpu 0x2b
+#define bfd_mach_sh2e 0x2e
+#define bfd_mach_sh3 0x30
+#define bfd_mach_sh3_nommu 0x31
+#define bfd_mach_sh3_dsp 0x3d
+#define bfd_mach_sh3e 0x3e
+#define bfd_mach_sh4 0x40
+#define bfd_mach_sh4_nofpu 0x41
+#define bfd_mach_sh4_nommu_nofpu 0x42
+#define bfd_mach_sh4a 0x4a
+#define bfd_mach_sh4a_nofpu 0x4b
+#define bfd_mach_sh4al_dsp 0x4d
+#define bfd_mach_sh5 0x50
+ bfd_arch_alpha, /* Dec Alpha */
+#define bfd_mach_alpha 1
+ bfd_arch_arm, /* Advanced Risc Machines ARM */
+#define bfd_mach_arm_unknown 0
+#define bfd_mach_arm_2 1
+#define bfd_mach_arm_2a 2
+#define bfd_mach_arm_3 3
+#define bfd_mach_arm_3M 4
+#define bfd_mach_arm_4 5
+#define bfd_mach_arm_4T 6
+#define bfd_mach_arm_5 7
+#define bfd_mach_arm_5T 8
+#define bfd_mach_arm_5TE 9
+#define bfd_mach_arm_XScale 10
+#define bfd_mach_arm_ep9312 11
+#define bfd_mach_arm_iWMMXt 12
+#define bfd_mach_arm_iWMMXt2 13
+ bfd_arch_ns32k, /* National Semiconductors ns32000 */
+ bfd_arch_w65, /* WDC 65816 */
+ bfd_arch_tic30, /* Texas Instruments TMS320C30 */
+ bfd_arch_v850, /* NEC V850 */
+#define bfd_mach_v850 0
+ bfd_arch_arc, /* Argonaut RISC Core */
+#define bfd_mach_arc_base 0
+ bfd_arch_m32r, /* Mitsubishi M32R/D */
+#define bfd_mach_m32r 0 /* backwards compatibility */
+ bfd_arch_mn10200, /* Matsushita MN10200 */
+ bfd_arch_mn10300, /* Matsushita MN10300 */
+ bfd_arch_cris, /* Axis CRIS */
+#define bfd_mach_cris_v0_v10 255
+#define bfd_mach_cris_v32 32
+#define bfd_mach_cris_v10_v32 1032
+ bfd_arch_microblaze, /* Xilinx MicroBlaze. */
+ bfd_arch_ia64, /* HP/Intel ia64 */
+#define bfd_mach_ia64_elf64 64
+#define bfd_mach_ia64_elf32 32
+ bfd_arch_last
+ };
+#define bfd_mach_s390_31 31
+#define bfd_mach_s390_64 64
+
+typedef struct symbol_cache_entry
+{
+ const char *name;
+ union
+ {
+ PTR p;
+ bfd_vma i;
+ } udata;
+} asymbol;
+
+enum dis_insn_type {
+ dis_noninsn, /* Not a valid instruction */
+ dis_nonbranch, /* Not a branch instruction */
+ dis_branch, /* Unconditional branch */
+ dis_condbranch, /* Conditional branch */
+ dis_jsr, /* Jump to subroutine */
+ dis_condjsr, /* Conditional jump to subroutine */
+ dis_dref, /* Data reference instruction */
+ dis_dref2 /* Two data references in instruction */
+};
+
+/* This struct is passed into the instruction decoding routine,
+ and is passed back out into each callback. The various fields are used
+ for conveying information from your main routine into your callbacks,
+ for passing information into the instruction decoders (such as the
+ addresses of the callback functions), or for passing information
+ back from the instruction decoders to their callers.
+
+ It must be initialized before it is first passed; this can be done
+ by hand, or using one of the initialization macros below. */
+
+typedef struct disassemble_info {
+ fprintf_function fprintf_func;
+ FILE *stream;
+ PTR application_data;
+
+ /* Target description. We could replace this with a pointer to the bfd,
+ but that would require one. There currently isn't any such requirement
+ so to avoid introducing one we record these explicitly. */
+ /* The bfd_flavour. This can be bfd_target_unknown_flavour. */
+ enum bfd_flavour flavour;
+ /* The bfd_arch value. */
+ enum bfd_architecture arch;
+ /* The bfd_mach value. */
+ unsigned long mach;
+ /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */
+ enum bfd_endian endian;
+
+ /* An array of pointers to symbols either at the location being disassembled
+ or at the start of the function being disassembled. The array is sorted
+ so that the first symbol is intended to be the one used. The others are
+ present for any misc. purposes. This is not set reliably, but if it is
+ not NULL, it is correct. */
+ asymbol **symbols;
+ /* Number of symbols in array. */
+ int num_symbols;
+
+ /* For use by the disassembler.
+ The top 16 bits are reserved for public use (and are documented here).
+ The bottom 16 bits are for the internal use of the disassembler. */
+ unsigned long flags;
+#define INSN_HAS_RELOC 0x80000000
+ PTR private_data;
+
+ /* Function used to get bytes to disassemble. MEMADDR is the
+ address of the stuff to be disassembled, MYADDR is the address to
+ put the bytes in, and LENGTH is the number of bytes to read.
+ INFO is a pointer to this struct.
+ Returns an errno value or 0 for success. */
+ int (*read_memory_func)
+ (bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info);
+
+ /* Function which should be called if we get an error that we can't
+ recover from. STATUS is the errno value from read_memory_func and
+ MEMADDR is the address that we were trying to read. INFO is a
+ pointer to this struct. */
+ void (*memory_error_func)
+ (int status, bfd_vma memaddr, struct disassemble_info *info);
+
+ /* Function called to print ADDR. */
+ void (*print_address_func)
+ (bfd_vma addr, struct disassemble_info *info);
+
+ /* Function called to determine if there is a symbol at the given ADDR.
+ If there is, the function returns 1, otherwise it returns 0.
+ This is used by ports which support an overlay manager where
+ the overlay number is held in the top part of an address. In
+ some circumstances we want to include the overlay number in the
+ address, (normally because there is a symbol associated with
+ that address), but sometimes we want to mask out the overlay bits. */
+ int (* symbol_at_address_func)
+ (bfd_vma addr, struct disassemble_info * info);
+
+ /* These are for buffer_read_memory. */
+ bfd_byte *buffer;
+ bfd_vma buffer_vma;
+ int buffer_length;
+
+ /* This variable may be set by the instruction decoder. It suggests
+ the number of bytes objdump should display on a single line. If
+ the instruction decoder sets this, it should always set it to
+ the same value in order to get reasonable looking output. */
+ int bytes_per_line;
+
+ /* the next two variables control the way objdump displays the raw data */
+ /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */
+ /* output will look like this:
+ 00: 00000000 00000000
+ with the chunks displayed according to "display_endian". */
+ int bytes_per_chunk;
+ enum bfd_endian display_endian;
+
+ /* Results from instruction decoders. Not all decoders yet support
+ this information. This info is set each time an instruction is
+ decoded, and is only valid for the last such instruction.
+
+ To determine whether this decoder supports this information, set
+ insn_info_valid to 0, decode an instruction, then check it. */
+
+ char insn_info_valid; /* Branch info has been set. */
+ char branch_delay_insns; /* How many sequential insn's will run before
+ a branch takes effect. (0 = normal) */
+ char data_size; /* Size of data reference in insn, in bytes */
+ enum dis_insn_type insn_type; /* Type of instruction */
+ bfd_vma target; /* Target address of branch or dref, if known;
+ zero if unknown. */
+ bfd_vma target2; /* Second target address for dref2 */
+
+ /* Command line options specific to the target disassembler. */
+ char * disassembler_options;
+
+} disassemble_info;
+
+
+/* Standard disassemblers. Disassemble one instruction at the given
+ target address. Return number of bytes processed. */
+typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *);
+
+int print_insn_big_mips (bfd_vma, disassemble_info*);
+int print_insn_little_mips (bfd_vma, disassemble_info*);
+int print_insn_i386 (bfd_vma, disassemble_info*);
+int print_insn_m68k (bfd_vma, disassemble_info*);
+int print_insn_z8001 (bfd_vma, disassemble_info*);
+int print_insn_z8002 (bfd_vma, disassemble_info*);
+int print_insn_h8300 (bfd_vma, disassemble_info*);
+int print_insn_h8300h (bfd_vma, disassemble_info*);
+int print_insn_h8300s (bfd_vma, disassemble_info*);
+int print_insn_h8500 (bfd_vma, disassemble_info*);
+int print_insn_alpha (bfd_vma, disassemble_info*);
+disassembler_ftype arc_get_disassembler (int, int);
+int print_insn_arm (bfd_vma, disassemble_info*);
+int print_insn_sparc (bfd_vma, disassemble_info*);
+int print_insn_big_a29k (bfd_vma, disassemble_info*);
+int print_insn_little_a29k (bfd_vma, disassemble_info*);
+int print_insn_i960 (bfd_vma, disassemble_info*);
+int print_insn_sh (bfd_vma, disassemble_info*);
+int print_insn_shl (bfd_vma, disassemble_info*);
+int print_insn_hppa (bfd_vma, disassemble_info*);
+int print_insn_m32r (bfd_vma, disassemble_info*);
+int print_insn_m88k (bfd_vma, disassemble_info*);
+int print_insn_mn10200 (bfd_vma, disassemble_info*);
+int print_insn_mn10300 (bfd_vma, disassemble_info*);
+int print_insn_ns32k (bfd_vma, disassemble_info*);
+int print_insn_big_powerpc (bfd_vma, disassemble_info*);
+int print_insn_little_powerpc (bfd_vma, disassemble_info*);
+int print_insn_rs6000 (bfd_vma, disassemble_info*);
+int print_insn_w65 (bfd_vma, disassemble_info*);
+int print_insn_d10v (bfd_vma, disassemble_info*);
+int print_insn_v850 (bfd_vma, disassemble_info*);
+int print_insn_tic30 (bfd_vma, disassemble_info*);
+int print_insn_ppc (bfd_vma, disassemble_info*);
+int print_insn_s390 (bfd_vma, disassemble_info*);
+int print_insn_crisv32 (bfd_vma, disassemble_info*);
+int print_insn_crisv10 (bfd_vma, disassemble_info*);
+int print_insn_microblaze (bfd_vma, disassemble_info*);
+int print_insn_ia64 (bfd_vma, disassemble_info*);
+
+#if 0
+/* Fetch the disassembler for a given BFD, if that support is available. */
+disassembler_ftype disassembler(bfd *);
+#endif
+
+
+/* This block of definitions is for particular callers who read instructions
+ into a buffer before calling the instruction decoder. */
+
+/* Here is a function which callers may wish to use for read_memory_func.
+ It gets bytes from a buffer. */
+int buffer_read_memory(bfd_vma, bfd_byte *, int, struct disassemble_info *);
+
+/* This function goes with buffer_read_memory.
+ It prints a message using info->fprintf_func and info->stream. */
+void perror_memory(int, bfd_vma, struct disassemble_info *);
+
+
+/* Just print the address in hex. This is included for completeness even
+ though both GDB and objdump provide their own (to print symbolic
+ addresses). */
+void generic_print_address(bfd_vma, struct disassemble_info *);
+
+/* Always true. */
+int generic_symbol_at_address(bfd_vma, struct disassemble_info *);
+
+/* Macro to initialize a disassemble_info struct. This should be called
+ by all applications creating such a struct. */
+#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \
+ (INFO).flavour = bfd_target_unknown_flavour, \
+ (INFO).arch = bfd_arch_unknown, \
+ (INFO).mach = 0, \
+ (INFO).endian = BFD_ENDIAN_UNKNOWN, \
+ INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC)
+
+/* Call this macro to initialize only the internal variables for the
+ disassembler. Architecture dependent things such as byte order, or machine
+ variant are not touched by this macro. This makes things much easier for
+ GDB which must initialize these things separately. */
+
+#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \
+ (INFO).fprintf_func = (FPRINTF_FUNC), \
+ (INFO).stream = (STREAM), \
+ (INFO).symbols = NULL, \
+ (INFO).num_symbols = 0, \
+ (INFO).private_data = NULL, \
+ (INFO).buffer = NULL, \
+ (INFO).buffer_vma = 0, \
+ (INFO).buffer_length = 0, \
+ (INFO).read_memory_func = buffer_read_memory, \
+ (INFO).memory_error_func = perror_memory, \
+ (INFO).print_address_func = generic_print_address, \
+ (INFO).symbol_at_address_func = generic_symbol_at_address, \
+ (INFO).flags = 0, \
+ (INFO).bytes_per_line = 0, \
+ (INFO).bytes_per_chunk = 0, \
+ (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \
+ (INFO).disassembler_options = NULL, \
+ (INFO).insn_info_valid = 0
+
+#define _(x) x
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+
+/* from libbfd */
+
+bfd_vma bfd_getl64 (const bfd_byte *addr);
+bfd_vma bfd_getl32 (const bfd_byte *addr);
+bfd_vma bfd_getb32 (const bfd_byte *addr);
+bfd_vma bfd_getl16 (const bfd_byte *addr);
+bfd_vma bfd_getb16 (const bfd_byte *addr);
+typedef bool bfd_boolean;
+
+#endif /* ! defined (DIS_ASM_H) */
diff --git a/disas.c b/disas.c
new file mode 100644
index 0000000..c76f36f
--- /dev/null
+++ b/disas.c
@@ -0,0 +1,432 @@
+/* General "disassemble this chunk" code. Used for debugging. */
+#include "config.h"
+#include "dis-asm.h"
+#include "elf.h"
+#include <errno.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+/* Filled in by elfload.c. Simplistic, but will do for now. */
+struct syminfo *syminfos = NULL;
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+ Transfer them to myaddr. */
+int
+buffer_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info)
+{
+ if (memaddr < info->buffer_vma
+ || memaddr + length > info->buffer_vma + info->buffer_length)
+ /* Out of bounds. Use EIO because GDB uses it. */
+ return EIO;
+ memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
+ return 0;
+}
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+ Transfer them to myaddr. */
+static int
+target_read_memory (bfd_vma memaddr,
+ bfd_byte *myaddr,
+ int length,
+ struct disassemble_info *info)
+{
+ cpu_memory_rw_debug(cpu_single_env, memaddr, myaddr, length, 0);
+ return 0;
+}
+
+/* Print an error message. We can assume that this is in response to
+ an error return from buffer_read_memory. */
+void
+perror_memory (int status, bfd_vma memaddr, struct disassemble_info *info)
+{
+ if (status != EIO)
+ /* Can't happen. */
+ (*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
+ else
+ /* Actually, address between memaddr and memaddr + len was
+ out of bounds. */
+ (*info->fprintf_func) (info->stream,
+ "Address 0x%" PRIx64 " is out of bounds.\n", memaddr);
+}
+
+/* This could be in a separate file, to save miniscule amounts of space
+ in statically linked executables. */
+
+/* Just print the address is hex. This is included for completeness even
+ though both GDB and objdump provide their own (to print symbolic
+ addresses). */
+
+void
+generic_print_address (bfd_vma addr, struct disassemble_info *info)
+{
+ (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
+}
+
+/* Just return the given address. */
+
+int
+generic_symbol_at_address (bfd_vma addr, struct disassemble_info *info)
+{
+ return 1;
+}
+
+bfd_vma bfd_getl64 (const bfd_byte *addr)
+{
+ unsigned long long v;
+
+ v = (unsigned long long) addr[0];
+ v |= (unsigned long long) addr[1] << 8;
+ v |= (unsigned long long) addr[2] << 16;
+ v |= (unsigned long long) addr[3] << 24;
+ v |= (unsigned long long) addr[4] << 32;
+ v |= (unsigned long long) addr[5] << 40;
+ v |= (unsigned long long) addr[6] << 48;
+ v |= (unsigned long long) addr[7] << 56;
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getl32 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0];
+ v |= (unsigned long) addr[1] << 8;
+ v |= (unsigned long) addr[2] << 16;
+ v |= (unsigned long) addr[3] << 24;
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getb32 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0] << 24;
+ v |= (unsigned long) addr[1] << 16;
+ v |= (unsigned long) addr[2] << 8;
+ v |= (unsigned long) addr[3];
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getl16 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0];
+ v |= (unsigned long) addr[1] << 8;
+ return (bfd_vma) v;
+}
+
+bfd_vma bfd_getb16 (const bfd_byte *addr)
+{
+ unsigned long v;
+
+ v = (unsigned long) addr[0] << 24;
+ v |= (unsigned long) addr[1] << 16;
+ return (bfd_vma) v;
+}
+
+#ifdef TARGET_ARM
+static int
+print_insn_thumb1(bfd_vma pc, disassemble_info *info)
+{
+ return print_insn_arm(pc | 1, info);
+}
+#endif
+
+/* Disassemble this for me please... (debugging). 'flags' has the following
+ values:
+ i386 - nonzero means 16 bit code
+ arm - nonzero means thumb code
+ ppc - nonzero means little endian
+ other targets - unused
+ */
+void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
+{
+ target_ulong pc;
+ int count;
+ struct disassemble_info disasm_info;
+ int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+ INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
+
+ disasm_info.read_memory_func = target_read_memory;
+ disasm_info.buffer_vma = code;
+ disasm_info.buffer_length = size;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(TARGET_I386)
+ if (flags == 2)
+ disasm_info.mach = bfd_mach_x86_64;
+ else if (flags == 1)
+ disasm_info.mach = bfd_mach_i386_i8086;
+ else
+ disasm_info.mach = bfd_mach_i386_i386;
+ print_insn = print_insn_i386;
+#elif defined(TARGET_ARM)
+ if (flags)
+ print_insn = print_insn_thumb1;
+ else
+ print_insn = print_insn_arm;
+#elif defined(TARGET_SPARC)
+ print_insn = print_insn_sparc;
+#ifdef TARGET_SPARC64
+ disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(TARGET_PPC)
+ if (flags >> 16)
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+ if (flags & 0xFFFF) {
+ /* If we have a precise definitions of the instructions set, use it */
+ disasm_info.mach = flags & 0xFFFF;
+ } else {
+#ifdef TARGET_PPC64
+ disasm_info.mach = bfd_mach_ppc64;
+#else
+ disasm_info.mach = bfd_mach_ppc;
+#endif
+ }
+ print_insn = print_insn_ppc;
+#elif defined(TARGET_M68K)
+ print_insn = print_insn_m68k;
+#elif defined(TARGET_MIPS)
+#ifdef TARGET_WORDS_BIGENDIAN
+ print_insn = print_insn_big_mips;
+#else
+ print_insn = print_insn_little_mips;
+#endif
+#elif defined(TARGET_SH4)
+ disasm_info.mach = bfd_mach_sh4;
+ print_insn = print_insn_sh;
+#elif defined(TARGET_ALPHA)
+ disasm_info.mach = bfd_mach_alpha;
+ print_insn = print_insn_alpha;
+#elif defined(TARGET_CRIS)
+ if (flags != 32) {
+ disasm_info.mach = bfd_mach_cris_v0_v10;
+ print_insn = print_insn_crisv10;
+ } else {
+ disasm_info.mach = bfd_mach_cris_v32;
+ print_insn = print_insn_crisv32;
+ }
+#elif defined(TARGET_MICROBLAZE)
+ disasm_info.mach = bfd_arch_microblaze;
+ print_insn = print_insn_microblaze;
+#else
+ fprintf(out, "0x" TARGET_FMT_lx
+ ": Asm output not supported on this arch\n", code);
+ return;
+#endif
+
+ for (pc = code; size > 0; pc += count, size -= count) {
+ fprintf(out, "0x" TARGET_FMT_lx ": ", pc);
+ count = print_insn(pc, &disasm_info);
+#if 0
+ {
+ int i;
+ uint8_t b;
+ fprintf(out, " {");
+ for(i = 0; i < count; i++) {
+ target_read_memory(pc + i, &b, 1, &disasm_info);
+ fprintf(out, " %02x", b);
+ }
+ fprintf(out, " }");
+ }
+#endif
+ fprintf(out, "\n");
+ if (count < 0)
+ break;
+ if (size < count) {
+ fprintf(out,
+ "Disassembler disagrees with translator over instruction "
+ "decoding\n"
+ "Please report this to qemu-devel@nongnu.org\n");
+ break;
+ }
+ }
+}
+
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, void *code, unsigned long size)
+{
+ unsigned long pc;
+ int count;
+ struct disassemble_info disasm_info;
+ int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+ INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
+
+ disasm_info.buffer = code;
+ disasm_info.buffer_vma = (unsigned long)code;
+ disasm_info.buffer_length = size;
+
+#ifdef HOST_WORDS_BIGENDIAN
+ disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(__i386__)
+ disasm_info.mach = bfd_mach_i386_i386;
+ print_insn = print_insn_i386;
+#elif defined(__x86_64__)
+ disasm_info.mach = bfd_mach_x86_64;
+ print_insn = print_insn_i386;
+#elif defined(_ARCH_PPC)
+ print_insn = print_insn_ppc;
+#elif defined(__alpha__)
+ print_insn = print_insn_alpha;
+#elif defined(__sparc__)
+ print_insn = print_insn_sparc;
+#if defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || defined(__sparc_v9__)
+ disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(__arm__)
+ print_insn = print_insn_arm;
+#elif defined(__MIPSEB__)
+ print_insn = print_insn_big_mips;
+#elif defined(__MIPSEL__)
+ print_insn = print_insn_little_mips;
+#elif defined(__m68k__)
+ print_insn = print_insn_m68k;
+#elif defined(__s390__)
+ print_insn = print_insn_s390;
+#elif defined(__hppa__)
+ print_insn = print_insn_hppa;
+#elif defined(__ia64__)
+ print_insn = print_insn_ia64;
+#else
+ fprintf(out, "0x%lx: Asm output not supported on this arch\n",
+ (long) code);
+ return;
+#endif
+ for (pc = (unsigned long)code; size > 0; pc += count, size -= count) {
+ fprintf(out, "0x%08lx: ", pc);
+ count = print_insn(pc, &disasm_info);
+ fprintf(out, "\n");
+ if (count < 0)
+ break;
+ }
+}
+
+/* Look up symbol for debugging purpose. Returns "" if unknown. */
+const char *lookup_symbol(target_ulong orig_addr)
+{
+ const char *symbol = "";
+ struct syminfo *s;
+
+ for (s = syminfos; s; s = s->next) {
+ symbol = s->lookup_symbol(s, orig_addr);
+ if (symbol[0] != '\0') {
+ break;
+ }
+ }
+
+ return symbol;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#include "monitor.h"
+
+static int monitor_disas_is_physical;
+static CPUState *monitor_disas_env;
+
+static int
+monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length,
+ struct disassemble_info *info)
+{
+ if (monitor_disas_is_physical) {
+ cpu_physical_memory_rw(memaddr, myaddr, length, 0);
+ } else {
+ cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0);
+ }
+ return 0;
+}
+
+static int GCC_FMT_ATTR(2, 3)
+monitor_fprintf(FILE *stream, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ monitor_vprintf((Monitor *)stream, fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+void monitor_disas(Monitor *mon, CPUState *env,
+ target_ulong pc, int nb_insn, int is_physical, int flags)
+{
+ int count, i;
+ struct disassemble_info disasm_info;
+ int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+ INIT_DISASSEMBLE_INFO(disasm_info, (FILE *)mon, monitor_fprintf);
+
+ monitor_disas_env = env;
+ monitor_disas_is_physical = is_physical;
+ disasm_info.read_memory_func = monitor_read_memory;
+
+ disasm_info.buffer_vma = pc;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+ disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(TARGET_I386)
+ if (flags == 2)
+ disasm_info.mach = bfd_mach_x86_64;
+ else if (flags == 1)
+ disasm_info.mach = bfd_mach_i386_i8086;
+ else
+ disasm_info.mach = bfd_mach_i386_i386;
+ print_insn = print_insn_i386;
+#elif defined(TARGET_ARM)
+ print_insn = print_insn_arm;
+#elif defined(TARGET_ALPHA)
+ print_insn = print_insn_alpha;
+#elif defined(TARGET_SPARC)
+ print_insn = print_insn_sparc;
+#ifdef TARGET_SPARC64
+ disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(TARGET_PPC)
+#ifdef TARGET_PPC64
+ disasm_info.mach = bfd_mach_ppc64;
+#else
+ disasm_info.mach = bfd_mach_ppc;
+#endif
+ print_insn = print_insn_ppc;
+#elif defined(TARGET_M68K)
+ print_insn = print_insn_m68k;
+#elif defined(TARGET_MIPS)
+#ifdef TARGET_WORDS_BIGENDIAN
+ print_insn = print_insn_big_mips;
+#else
+ print_insn = print_insn_little_mips;
+#endif
+#elif defined(TARGET_SH4)
+ disasm_info.mach = bfd_mach_sh4;
+ print_insn = print_insn_sh;
+#else
+ monitor_printf(mon, "0x" TARGET_FMT_lx
+ ": Asm output not supported on this arch\n", pc);
+ return;
+#endif
+
+ for(i = 0; i < nb_insn; i++) {
+ monitor_printf(mon, "0x" TARGET_FMT_lx ": ", pc);
+ count = print_insn(pc, &disasm_info);
+ monitor_printf(mon, "\n");
+ if (count < 0)
+ break;
+ pc += count;
+ }
+}
+#endif
diff --git a/disas.h b/disas.h
new file mode 100644
index 0000000..f9287f7
--- /dev/null
+++ b/disas.h
@@ -0,0 +1,42 @@
+#ifndef _QEMU_DISAS_H
+#define _QEMU_DISAS_H
+
+#include "qemu-common.h"
+
+#ifdef NEED_CPU_H
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, void *code, unsigned long size);
+void target_disas(FILE *out, target_ulong code, target_ulong size, int flags);
+
+void monitor_disas(Monitor *mon, CPUState *env,
+ target_ulong pc, int nb_insn, int is_physical, int flags);
+
+/* Look up symbol for debugging purpose. Returns "" if unknown. */
+const char *lookup_symbol(target_ulong orig_addr);
+#endif
+
+struct syminfo;
+struct elf32_sym;
+struct elf64_sym;
+
+#if defined(CONFIG_USER_ONLY)
+typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_ulong orig_addr);
+#else
+typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_phys_addr_t orig_addr);
+#endif
+
+struct syminfo {
+ lookup_symbol_t lookup_symbol;
+ unsigned int disas_num_syms;
+ union {
+ struct elf32_sym *elf32;
+ struct elf64_sym *elf64;
+ } disas_symtab;
+ const char *disas_strtab;
+ struct syminfo *next;
+};
+
+/* Filled in by elfload.c. Simplistic, but will do for now. */
+extern struct syminfo *syminfos;
+
+#endif /* _QEMU_DISAS_H */
diff --git a/dma-helpers.c b/dma-helpers.c
new file mode 100644
index 0000000..d4fc077
--- /dev/null
+++ b/dma-helpers.c
@@ -0,0 +1,188 @@
+/*
+ * DMA helper functions
+ *
+ * Copyright (c) 2009 Red Hat
+ *
+ * This work is licensed under the terms of the GNU General Public License
+ * (GNU GPL), version 2 or later.
+ */
+
+#include "dma.h"
+#include "block_int.h"
+
+void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint)
+{
+ qsg->sg = qemu_malloc(alloc_hint * sizeof(ScatterGatherEntry));
+ qsg->nsg = 0;
+ qsg->nalloc = alloc_hint;
+ qsg->size = 0;
+}
+
+void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base,
+ target_phys_addr_t len)
+{
+ if (qsg->nsg == qsg->nalloc) {
+ qsg->nalloc = 2 * qsg->nalloc + 1;
+ qsg->sg = qemu_realloc(qsg->sg, qsg->nalloc * sizeof(ScatterGatherEntry));
+ }
+ qsg->sg[qsg->nsg].base = base;
+ qsg->sg[qsg->nsg].len = len;
+ qsg->size += len;
+ ++qsg->nsg;
+}
+
+void qemu_sglist_destroy(QEMUSGList *qsg)
+{
+ qemu_free(qsg->sg);
+}
+
+typedef struct {
+ BlockDriverAIOCB common;
+ BlockDriverState *bs;
+ BlockDriverAIOCB *acb;
+ QEMUSGList *sg;
+ uint64_t sector_num;
+ int is_write;
+ int sg_cur_index;
+ target_phys_addr_t sg_cur_byte;
+ QEMUIOVector iov;
+ QEMUBH *bh;
+} DMAAIOCB;
+
+static void dma_bdrv_cb(void *opaque, int ret);
+
+static void reschedule_dma(void *opaque)
+{
+ DMAAIOCB *dbs = (DMAAIOCB *)opaque;
+
+ qemu_bh_delete(dbs->bh);
+ dbs->bh = NULL;
+ dma_bdrv_cb(opaque, 0);
+}
+
+static void continue_after_map_failure(void *opaque)
+{
+ DMAAIOCB *dbs = (DMAAIOCB *)opaque;
+
+ dbs->bh = qemu_bh_new(reschedule_dma, dbs);
+ qemu_bh_schedule(dbs->bh);
+}
+
+static void dma_bdrv_unmap(DMAAIOCB *dbs)
+{
+ int i;
+
+ for (i = 0; i < dbs->iov.niov; ++i) {
+ cpu_physical_memory_unmap(dbs->iov.iov[i].iov_base,
+ dbs->iov.iov[i].iov_len, !dbs->is_write,
+ dbs->iov.iov[i].iov_len);
+ }
+}
+
+static void dma_bdrv_cb(void *opaque, int ret)
+{
+ DMAAIOCB *dbs = (DMAAIOCB *)opaque;
+ target_phys_addr_t cur_addr, cur_len;
+ void *mem;
+
+ dbs->acb = NULL;
+ dbs->sector_num += dbs->iov.size / 512;
+ dma_bdrv_unmap(dbs);
+ qemu_iovec_reset(&dbs->iov);
+
+ if (dbs->sg_cur_index == dbs->sg->nsg || ret < 0) {
+ dbs->common.cb(dbs->common.opaque, ret);
+ qemu_iovec_destroy(&dbs->iov);
+ qemu_aio_release(dbs);
+ return;
+ }
+
+ while (dbs->sg_cur_index < dbs->sg->nsg) {
+ cur_addr = dbs->sg->sg[dbs->sg_cur_index].base + dbs->sg_cur_byte;
+ cur_len = dbs->sg->sg[dbs->sg_cur_index].len - dbs->sg_cur_byte;
+ mem = cpu_physical_memory_map(cur_addr, &cur_len, !dbs->is_write);
+ if (!mem)
+ break;
+ qemu_iovec_add(&dbs->iov, mem, cur_len);
+ dbs->sg_cur_byte += cur_len;
+ if (dbs->sg_cur_byte == dbs->sg->sg[dbs->sg_cur_index].len) {
+ dbs->sg_cur_byte = 0;
+ ++dbs->sg_cur_index;
+ }
+ }
+
+ if (dbs->iov.size == 0) {
+ cpu_register_map_client(dbs, continue_after_map_failure);
+ return;
+ }
+
+ if (dbs->is_write) {
+ dbs->acb = bdrv_aio_writev(dbs->bs, dbs->sector_num, &dbs->iov,
+ dbs->iov.size / 512, dma_bdrv_cb, dbs);
+ } else {
+ dbs->acb = bdrv_aio_readv(dbs->bs, dbs->sector_num, &dbs->iov,
+ dbs->iov.size / 512, dma_bdrv_cb, dbs);
+ }
+ if (!dbs->acb) {
+ dma_bdrv_unmap(dbs);
+ qemu_iovec_destroy(&dbs->iov);
+ return;
+ }
+}
+
+static void dma_aio_cancel(BlockDriverAIOCB *acb)
+{
+ DMAAIOCB *dbs = container_of(acb, DMAAIOCB, common);
+
+ if (dbs->acb) {
+ bdrv_aio_cancel(dbs->acb);
+ }
+}
+
+static AIOPool dma_aio_pool = {
+ .aiocb_size = sizeof(DMAAIOCB),
+ .cancel = dma_aio_cancel,
+};
+
+static BlockDriverAIOCB *dma_bdrv_io(
+ BlockDriverState *bs, QEMUSGList *sg, uint64_t sector_num,
+ BlockDriverCompletionFunc *cb, void *opaque,
+ int is_write)
+{
+ DMAAIOCB *dbs = qemu_aio_get(&dma_aio_pool, bs, cb, opaque);
+
+ dbs->acb = NULL;
+ dbs->bs = bs;
+ dbs->sg = sg;
+ dbs->sector_num = sector_num;
+ dbs->sg_cur_index = 0;
+ dbs->sg_cur_byte = 0;
+ dbs->is_write = is_write;
+ dbs->bh = NULL;
+ qemu_iovec_init(&dbs->iov, sg->nsg);
+ /*
+ * DMA flushing is handled in dma_bdrv_cb() calling dma_bdrv_unmap()
+ * so we don't need to do that here.
+ */
+ dma_bdrv_cb(dbs, 0);
+ if (!dbs->acb) {
+ qemu_aio_release(dbs);
+ return NULL;
+ }
+ return &dbs->common;
+}
+
+
+BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs,
+ QEMUSGList *sg, uint64_t sector,
+ void (*cb)(void *opaque, int ret), void *opaque)
+{
+ return dma_bdrv_io(bs, sg, sector, cb, opaque, 0);
+}
+
+BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
+ QEMUSGList *sg, uint64_t sector,
+ void (*cb)(void *opaque, int ret), void *opaque)
+{
+ return dma_bdrv_io(bs, sg, sector, cb, opaque, 1);
+}
diff --git a/dma.h b/dma.h
new file mode 100644
index 0000000..f3bb275
--- /dev/null
+++ b/dma.h
@@ -0,0 +1,41 @@
+/*
+ * DMA helper functions
+ *
+ * Copyright (c) 2009 Red Hat
+ *
+ * This work is licensed under the terms of the GNU General Public License
+ * (GNU GPL), version 2 or later.
+ */
+
+#ifndef DMA_H
+#define DMA_H
+
+#include <stdio.h>
+//#include "cpu.h"
+#include "hw/hw.h"
+#include "block.h"
+
+typedef struct {
+ target_phys_addr_t base;
+ target_phys_addr_t len;
+} ScatterGatherEntry;
+
+typedef struct {
+ ScatterGatherEntry *sg;
+ int nsg;
+ int nalloc;
+ target_phys_addr_t size;
+} QEMUSGList;
+
+void qemu_sglist_init(QEMUSGList *qsg, int alloc_hint);
+void qemu_sglist_add(QEMUSGList *qsg, target_phys_addr_t base,
+ target_phys_addr_t len);
+void qemu_sglist_destroy(QEMUSGList *qsg);
+
+BlockDriverAIOCB *dma_bdrv_read(BlockDriverState *bs,
+ QEMUSGList *sg, uint64_t sector,
+ BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *dma_bdrv_write(BlockDriverState *bs,
+ QEMUSGList *sg, uint64_t sector,
+ BlockDriverCompletionFunc *cb, void *opaque);
+#endif
diff --git a/docs/blkverify.txt b/docs/blkverify.txt
new file mode 100644
index 0000000..d556dc4
--- /dev/null
+++ b/docs/blkverify.txt
@@ -0,0 +1,69 @@
+= Block driver correctness testing with blkverify =
+
+== Introduction ==
+
+This document describes how to use the blkverify protocol to test that a block
+driver is operating correctly.
+
+It is difficult to test and debug block drivers against real guests. Often
+processes inside the guest will crash because corrupt sectors were read as part
+of the executable. Other times obscure errors are raised by a program inside
+the guest. These issues are extremely hard to trace back to bugs in the block
+driver.
+
+Blkverify solves this problem by catching data corruption inside QEMU the first
+time bad data is read and reporting the disk sector that is corrupted.
+
+== How it works ==
+
+The blkverify protocol has two child block devices, the "test" device and the
+"raw" device. Read/write operations are mirrored to both devices so their
+state should always be in sync.
+
+The "raw" device is a raw image, a flat file, that has identical starting
+contents to the "test" image. The idea is that the "raw" device will handle
+read/write operations correctly and not corrupt data. It can be used as a
+reference for comparison against the "test" device.
+
+After a mirrored read operation completes, blkverify will compare the data and
+raise an error if it is not identical. This makes it possible to catch the
+first instance where corrupt data is read.
+
+== Example ==
+
+Imagine raw.img has 0xcd repeated throughout its first sector:
+
+ $ ./qemu-io -c 'read -v 0 512' raw.img
+ 00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ 00000010: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ [...]
+ 000001e0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ 000001f0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
+ read 512/512 bytes at offset 0
+ 512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec)
+
+And test.img is corrupt, its first sector is zeroed when it shouldn't be:
+
+ $ ./qemu-io -c 'read -v 0 512' test.img
+ 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ [...]
+ 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+ read 512/512 bytes at offset 0
+ 512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec)
+
+This error is caught by blkverify:
+
+ $ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img
+ blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
+
+A more realistic scenario is verifying the installation of a guest OS:
+
+ $ ./qemu-img create raw.img 16G
+ $ ./qemu-img create -f qcow2 test.qcow2 16G
+ $ x86_64-softmmu/qemu-system-x86_64 -cdrom debian.iso \
+ -drive file=blkverify:raw.img:test.qcow2
+
+If the installation is aborted when blkverify detects corruption, use qemu-io
+to explore the contents of the disk image at the sector in question.
diff --git a/docs/bootindex.txt b/docs/bootindex.txt
new file mode 100644
index 0000000..16083b3
--- /dev/null
+++ b/docs/bootindex.txt
@@ -0,0 +1,43 @@
+= Bootindex propery =
+
+Block and net devices have bootindex property. This property is used to
+determine the order in which firmware will consider devices for booting
+the guest OS. If the bootindex property is not set for a device, it gets
+lowest boot priority. There is no particular order in which devices with
+unset bootindex property will be considered for booting, but they will
+still be bootable.
+
+== Example ==
+
+Lets assume we have QEMU machine with two NICs (virtio, e1000) and two
+disks (IDE, virtio):
+
+qemu -drive file=disk1.img,if=none,id=disk1
+ -device ide-drive,drive=disk1,bootindex=4
+ -drive file=disk2.img,if=none,id=disk2
+ -device virtio-blk-pci,drive=disk2,bootindex=3
+ -netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=2
+ -netdev type=user,id=net1 -device e1000,netdev=net1,bootindex=1
+
+Given the command above, firmware should try to boot from the e1000 NIC
+first. If this fails, it should try the virtio NIC next, if this fails
+too, it should try the virtio disk, and then the IDE disk.
+
+== Limitations ==
+
+1. Some firmware has limitations on which devices can be considered for
+booting. For instance, the PC BIOS boot specification allows only one
+disk to be bootable. If boot from disk fails for some reason, the BIOS
+won't retry booting from other disk. It still can try to boot from
+floppy or net, though.
+
+2. Sometimes, firmware cannot map the device path QEMU wants firmware to
+boot from to a boot method. It doesn't happen for devices the firmware
+can natively boot from, but if firmware relies on an option ROM for
+booting, and the same option ROM is used for booting from more then one
+device, the firmware may not be able to ask the option ROM to boot from
+a particular device reliably. For instance with PC BIOS, if a SCSI HBA
+has three bootable devices target1, target3, target5 connected to it,
+the option ROM will have a boot method for each of them, but it is not
+possible to map from boot method back to a specific target. This is a
+shortcoming of PC BIOS boot specification.
diff --git a/docs/migration.txt b/docs/migration.txt
new file mode 100644
index 0000000..4848c1e
--- /dev/null
+++ b/docs/migration.txt
@@ -0,0 +1,303 @@
+= Migration =
+
+QEMU has code to load/save the state of the guest that it is running.
+These are two complementary operations. Saving the state just does
+that, saves the state for each device that the guest is running.
+Restoring a guest is just the opposite operation: we need to load the
+state of each device.
+
+For this to work, QEMU has to be launched with the same arguments the
+two times. I.e. it can only restore the state in one guest that has
+the same devices that the one it was saved (this last requirement can
+be relaxed a bit, but for now we can consider that configuration has
+to be exactly the same).
+
+Once that we are able to save/restore a guest, a new functionality is
+requested: migration. This means that QEMU is able to start in one
+machine and being "migrated" to another machine. I.e. being moved to
+another machine.
+
+Next was the "live migration" functionality. This is important
+because some guests run with a lot of state (specially RAM), and it
+can take a while to move all state from one machine to another. Live
+migration allows the guest to continue running while the state is
+transferred. Only while the last part of the state is transferred has
+the guest to be stopped. Typically the time that the guest is
+unresponsive during live migration is the low hundred of milliseconds
+(notice that this depends on a lot of things).
+
+=== Types of migration ===
+
+Now that we have talked about live migration, there are several ways
+to do migration:
+
+- tcp migration: do the migration using tcp sockets
+- unix migration: do the migration using unix sockets
+- exec migration: do the migration using the stdin/stdout through a process.
+- fd migration: do the migration using an file descriptor that is
+ passed to QEMU. QEMU doesn't care how this file descriptor is opened.
+
+All these four migration protocols use the same infrastructure to
+save/restore state devices. This infrastructure is shared with the
+savevm/loadvm functionality.
+
+=== State Live Migration ==
+
+This is used for RAM and block devices. It is not yet ported to vmstate.
+<Fill more information here>
+
+=== What is the common infrastructure ===
+
+QEMU uses a QEMUFile abstraction to be able to do migration. Any type
+of migration that wants to use QEMU infrastructure has to create a
+QEMUFile with:
+
+QEMUFile *qemu_fopen_ops(void *opaque,
+ QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer,
+ QEMUFileCloseFunc *close,
+ QEMUFileRateLimit *rate_limit,
+ QEMUFileSetRateLimit *set_rate_limit,
+ QEMUFileGetRateLimit *get_rate_limit);
+
+The functions have the following functionality:
+
+This function writes a chunk of data to a file at the given position.
+The pos argument can be ignored if the file is only used for
+streaming. The handler should try to write all of the data it can.
+
+typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
+ int64_t pos, int size);
+
+Read a chunk of data from a file at the given position. The pos argument
+can be ignored if the file is only be used for streaming. The number of
+bytes actually read should be returned.
+
+typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
+ int64_t pos, int size);
+
+Close a file and return an error code.
+
+typedef int (QEMUFileCloseFunc)(void *opaque);
+
+Called to determine if the file has exceeded its bandwidth allocation. The
+bandwidth capping is a soft limit, not a hard limit.
+
+typedef int (QEMUFileRateLimit)(void *opaque);
+
+Called to change the current bandwidth allocation. This function must return
+the new actual bandwidth. It should be new_rate if everything goes OK, and
+the old rate otherwise.
+
+typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate);
+typedef size_t (QEMUFileGetRateLimit)(void *opaque);
+
+You can use any internal state that you need using the opaque void *
+pointer that is passed to all functions.
+
+The rate limiting functions are used to limit the bandwidth used by
+QEMU migration.
+
+The important functions for us are put_buffer()/get_buffer() that
+allow to write/read a buffer into the QEMUFile.
+
+=== How to save the state of one device ==
+
+The state of a device is saved using intermediate buffers. There are
+some helper functions to assist this saving.
+
+There is a new concept that we have to explain here: device state
+version. When we migrate a device, we save/load the state as a series
+of fields. Some times, due to bugs or new functionality, we need to
+change the state to store more/different information. We use the
+version to identify each time that we do a change. Each version is
+associated with a series of fields saved. The save_state always saves
+the state as the newer version. But load_state sometimes is able to
+load state from an older version.
+
+ === Legacy way ===
+
+This way is going to disappear as soon as all current users are ported to VMSTATE.
+
+Each device has to register two functions, one to save the state and
+another to load the state back.
+
+int register_savevm(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+The important functions for the device state format are the save_state
+and load_state. Notice that load_state receives a version_id
+parameter to know what state format is receiving. save_state doesn't
+have a version_id parameter because it always uses the latest version.
+
+=== VMState ===
+
+The legacy way of saving/loading state of the device had the problem
+that we have to maintain two functions in sync. If we did one change
+in one of them and not in the other, we would get a failed migration.
+
+VMState changed the way that state is saved/loaded. Instead of using
+a function to save the state and another to load it, it was changed to
+a declarative way of what the state consisted of. Now VMState is able
+to interpret that definition to be able to load/save the state. As
+the state is declared only once, it can't go out of sync in the
+save/load functions.
+
+An example (from hw/pckbd.c)
+
+static const VMStateDescription vmstate_kbd = {
+ .name = "pckbd",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(write_cmd, KBDState),
+ VMSTATE_UINT8(status, KBDState),
+ VMSTATE_UINT8(mode, KBDState),
+ VMSTATE_UINT8(pending, KBDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+We are declaring the state with name "pckbd".
+The version_id is 3, and the fields are 4 uint8_t in a KBDState structure.
+We registered this with:
+
+ vmstate_register(NULL, 0, &vmstate_kbd, s);
+
+Note: talk about how vmstate <-> qdev interact, and what the instance ids mean.
+
+You can search for VMSTATE_* macros for lots of types used in QEMU in
+hw/hw.h.
+
+=== More about versions ==
+
+You can see that there are several version fields:
+
+- version_id: the maximum version_id supported by VMState for that device.
+- minimum_version_id: the minimum version_id that VMState is able to understand
+ for that device.
+- minimum_version_id_old: For devices that were not able to port to vmstate, we can
+ assign a function that knows how to read this old state.
+
+So, VMState is able to read versions from minimum_version_id to
+version_id. And the function load_state_old() is able to load state
+from minimum_version_id_old to minimum_version_id. This function is
+deprecated and will be removed when no more users are left.
+
+=== Massaging functions ===
+
+Sometimes, it is not enough to be able to save the state directly
+from one structure, we need to fill the correct values there. One
+example is when we are using kvm. Before saving the cpu state, we
+need to ask kvm to copy to QEMU the state that it is using. And the
+opposite when we are loading the state, we need a way to tell kvm to
+load the state for the cpu that we have just loaded from the QEMUFile.
+
+The functions to do that are inside a vmstate definition, and are called:
+
+- int (*pre_load)(void *opaque);
+
+ This function is called before we load the state of one device.
+
+- int (*post_load)(void *opaque, int version_id);
+
+ This function is called after we load the state of one device.
+
+- void (*pre_save)(void *opaque);
+
+ This function is called before we save the state of one device.
+
+Example: You can look at hpet.c, that uses the three function to
+ massage the state that is transferred.
+
+=== Subsections ===
+
+The use of version_id allows to be able to migrate from older versions
+to newer versions of a device. But not the other way around. This
+makes very complicated to fix bugs in stable branches. If we need to
+add anything to the state to fix a bug, we have to disable migration
+to older versions that don't have that bug-fix (i.e. a new field).
+
+But sometimes, that bug-fix is only needed sometimes, not always. For
+instance, if the device is in the middle of a DMA operation, it is
+using a specific functionality, ....
+
+It is impossible to create a way to make migration from any version to
+any other version to work. But we can do better than only allowing
+migration from older versions no newer ones. For that fields that are
+only needed sometimes, we add the idea of subsections. A subsection
+is "like" a device vmstate, but with a particularity, it has a Boolean
+function that tells if that values are needed to be sent or not. If
+this functions returns false, the subsection is not sent.
+
+On the receiving side, if we found a subsection for a device that we
+don't understand, we just fail the migration. If we understand all
+the subsections, then we load the state with success.
+
+One important note is that the post_load() function is called "after"
+loading all subsections, because a newer subsection could change same
+value that it uses.
+
+Example:
+
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+ IDEState *s = opaque;
+
+ return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+ .name = "ide_drive/pio_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = ide_drive_pio_pre_save,
+ .post_load = ide_drive_pio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(req_nb_sectors, IDEState),
+ VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+ VMSTATE_INT32(cur_io_buffer_len, IDEState),
+ VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+ VMSTATE_INT32(elementary_transfer_size, IDEState),
+ VMSTATE_INT32(packet_transfer_size, IDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+const VMStateDescription vmstate_ide_drive = {
+ .name = "ide_drive",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = ide_drive_post_load,
+ .fields = (VMStateField []) {
+ .... several fields ....
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_ide_drive_pio_state,
+ .needed = ide_drive_pio_state_needed,
+ }, {
+ /* empty */
+ }
+ }
+};
+
+Here we have a subsection for the pio state. We only need to
+save/send this state when we are in the middle of a pio operation
+(that is what ide_drive_pio_state_needed() checks). If DRQ_STAT is
+not enabled, the values on that fields are garbage and don't need to
+be sent.
diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt
new file mode 100644
index 0000000..4bb2be8
--- /dev/null
+++ b/docs/qdev-device-use.txt
@@ -0,0 +1,368 @@
+= How to convert to -device & friends =
+
+=== Specifying Bus and Address on Bus ===
+
+In qdev, each device has a parent bus. Some devices provide one or
+more buses for children. You can specify a device's parent bus with
+-device parameter bus.
+
+A device typically has a device address on its parent bus. For buses
+where this address can be configured, devices provide a bus-specific
+property. These are
+
+ bus property name value format
+ PCI addr %x.%x (dev.fn, .fn optional)
+ I2C address %u
+ SCSI scsi-id %u
+
+Example: device i440FX-pcihost is on the root bus, and provides a PCI
+bus named pci.0. To put a FOO device into its slot 4, use -device
+FOO,bus=/i440FX-pcihost/pci.0,addr=4. The abbreviated form bus=pci.0
+also works as long as the bus name is unique.
+
+Note: the USB device address can't be controlled at this time.
+
+=== Block Devices ===
+
+A QEMU block device (drive) has a host and a guest part.
+
+In the general case, the guest device is connected to a controller
+device. For instance, the IDE controller provides two IDE buses, each
+of which can have up to two ide-drive devices, and each ide-drive
+device is a guest part, and is connected to a host part.
+
+Except we sometimes lump controller, bus(es) and drive device(s) all
+together into a single device. For instance, the ISA floppy
+controller is connected to up to two host drives.
+
+The old ways to define block devices define host and guest part
+together. Sometimes, they can even define a controller device in
+addition to the block device.
+
+The new way keeps the parts separate: you create the host part with
+-drive, and guest device(s) with -device.
+
+The various old ways to define drives all boil down to the common form
+
+ -drive if=TYPE,index=IDX,bus=BUS,unit=UNIT,HOST-OPTS...
+
+TYPE, BUS and UNIT identify the controller device, which of its buses
+to use, and the drive's address on that bus. Details depend on TYPE.
+IDX is an alternative way to specify BUS and UNIT.
+
+In the new way, this becomes something like
+
+ -drive if=none,id=DRIVE-ID,HOST-OPTS...
+ -device DEVNAME,drive=DRIVE-ID,DEV-OPTS...
+
+The -device argument differs in detail for each kind of drive:
+
+* if=ide
+
+ -device ide-drive,drive=DRIVE-ID,bus=IDE-BUS,unit=UNIT
+
+ where IDE-BUS identifies an IDE bus, normally either ide.0 or ide.1,
+ and UNIT is either 0 or 1.
+
+ Bug: new way does not work for ide.1 unit 0 (in old terms: index=2)
+ unless you disable the default CD-ROM with -nodefaults.
+
+* if=scsi
+
+ The old way implicitly creates SCSI controllers as needed. The new
+ way makes that explicit:
+
+ -device lsi53c895a,id=ID
+
+ As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to
+ control the PCI device address.
+
+ This SCSI controller a single SCSI bus, named ID.0. Put a disk on
+ it:
+
+ -device scsi-disk,drive=DRIVE-ID,bus=ID.0,scsi-id=SCSI-ID,removable=RMB
+
+ The (optional) removable parameter lets you override the SCSI INQUIRY
+ removable (RMB) bit for non CD-ROM devices. It is ignored for CD-ROM devices
+ which are always removable. RMB is "on" or "off".
+
+* if=floppy
+
+ -global isa-fdc,driveA=DRIVE-ID,driveB=DRIVE-ID
+
+ This is -global instead of -device, because the floppy controller is
+ created automatically, and we want to configure that one, not create
+ a second one (which isn't possible anyway).
+
+ Omitting a drive parameter makes that drive empty.
+
+ Bug: driveA works only if you disable the default floppy drive with
+ -nodefaults.
+
+* if=virtio
+
+ -device virtio-blk-pci,drive=DRIVE-ID,class=C,vectors=V,ioeventfd=IOEVENTFD
+
+ This lets you control PCI device class and MSI-X vectors.
+
+ IOEVENTFD controls whether or not ioeventfd is used for virtqueue notify. It
+ can be set to on (default) or off.
+
+ As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to
+ control the PCI device address.
+
+* if=pflash, if=mtd, if=sd, if=xen are not yet available with -device
+
+For USB devices, the old way is actually different:
+
+ -usbdevice disk:format=FMT:FILENAME
+
+Provides much less control than -drive's HOST-OPTS... The new way
+fixes that:
+
+ -device usb-storage,drive=DRIVE-ID,removable=RMB
+
+The removable parameter gives control over the SCSI INQUIRY removable (RMB)
+bit. USB thumbdrives usually set removable=on, while USB hard disks set
+removable=off. See the if=scsi description above for details on the removable
+parameter, which applies only to scsi-disk devices and not to scsi-generic.
+
+=== Character Devices ===
+
+A QEMU character device has a host and a guest part.
+
+The old ways to define character devices define host and guest part
+together.
+
+The new way keeps the parts separate: you create the host part with
+-chardev, and the guest device with -device.
+
+The various old ways to define a character device are all of the
+general form
+
+ -FOO FOO-OPTS...,LEGACY-CHARDEV
+
+where FOO-OPTS... is specific to -FOO, and the host part
+LEGACY-CHARDEV is the same everywhere.
+
+In the new way, this becomes
+
+ -chardev HOST-OPTS...,id=CHR-ID
+ -device DEVNAME,chardev=CHR-ID,DEV-OPTS...
+
+The appropriate DEVNAME depends on the machine type. For type "pc":
+
+* -serial becomes -device isa-serial,iobase=IOADDR,irq=IRQ,index=IDX
+
+ This lets you control I/O ports and IRQs.
+
+* -parallel becomes -device isa-parallel,iobase=IOADDR,irq=IRQ,index=IDX
+
+ This lets you control I/O ports and IRQs.
+
+* -usbdevice serial:vendorid=VID,productid=PRID becomes
+ -device usb-serial,vendorid=VID,productid=PRID
+
+* -usbdevice braille doesn't support LEGACY-CHARDEV syntax. It always
+ uses "braille". With -device, this useful default is gone, so you
+ have to use something like
+
+ -device usb-braille,chardev=braille,vendorid=VID,productid=PRID
+ -chardev braille,id=braille
+
+* -virtioconsole is still being worked on
+
+LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows:
+
+* null becomes -chardev null
+
+* pty, msmouse, braille, stdio likewise
+
+* vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT
+
+* vc:<COLS>Cx<ROWS>C becomes -chardev vc,cols=<COLS>,rows=<ROWS>
+
+* con: becomes -chardev console
+
+* COM<NUM> becomes -chardev serial,path=<NUM>
+
+* file:FNAME becomes -chardev file,path=FNAME
+
+* pipe:FNAME becomes -chardev pipe,path=FNAME
+
+* tcp:HOST:PORT,OPTS... becomes -chardev socket,host=HOST,port=PORT,OPTS...
+
+* telnet:HOST:PORT,OPTS... becomes
+ -chardev socket,host=HOST,port=PORT,OPTS...,telnet=on
+
+* udp:HOST:PORT@LOCALADDR:LOCALPORT becomes
+ -chardev udp,host=HOST,port=PORT,localaddr=LOCALADDR,localport=LOCALPORT
+
+* unix:FNAME becomes -chardev socket,path=FNAME
+
+* /dev/parportN becomes -chardev parport,file=/dev/parportN
+
+* /dev/ppiN likewise
+
+* Any other /dev/FNAME becomes -chardev tty,path=/dev/FNAME
+
+* mon:LEGACY-CHARDEV is special: it multiplexes the monitor onto the
+ character device defined by LEGACY-CHARDEV. -chardev provides more
+ general multiplexing instead: you can connect up to four users to a
+ single host part. You need to pass mux=on to -chardev to enable
+ switching the input focus.
+
+QEMU uses LEGACY-CHARDEV syntax not just to set up guest devices, but
+also in various other places such as -monitor or -net
+user,guestfwd=... You can use chardev:CHR-ID in place of
+LEGACY-CHARDEV to refer to a host part defined with -chardev.
+
+=== Network Devices ===
+
+A QEMU network device (NIC) has a host and a guest part.
+
+The old ways to define NICs define host and guest part together. It
+looks like this:
+
+ -net nic,vlan=VLAN,macaddr=MACADDR,model=MODEL,name=ID,addr=STR,vectors=V
+
+Except for USB it looks like this:
+
+ -usbdevice net:vlan=VLAN,macaddr=MACADDR,name=ID,addr=STR,vectors=V
+
+The new way keeps the parts separate: you create the host part with
+-netdev, and the guest device with -device, like this:
+
+ -netdev type=TYPE,id=NET-ID
+ -device DEVNAME,netdev=NET-ID,mac=MACADDR,DEV-OPTS...
+
+Unlike the old way, this creates just a network device, not a VLAN.
+If you really want a VLAN, create it the usual way, then create the
+guest device like this:
+
+ -device DEVNAME,vlan=VLAN,mac=MACADDR,DEV-OPTS...
+
+DEVNAME equals MODEL, except for virtio you have to name the virtio
+device appropriate for the bus (virtio-net-pci for PCI), and for USB
+NIC you have to use usb-net.
+
+The old name=ID parameter becomes the usual id=ID with -device.
+
+For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI
+device address, as usual. The old -net nic provides parameter addr
+for that, it is silently ignored when the NIC is not a PCI device.
+
+For virtio-net-pci, you can control whether or not ioeventfd is used for
+virtqueue notify by setting ioeventfd= to on or off (default).
+
+-net nic accepts vectors=V for all models, but it's silently ignored
+except for virtio-net-pci (model=virtio). With -device, only devices
+that support it accept it.
+
+Not all devices are available with -device at this time. All PCI
+devices and ne2k_isa are.
+
+Some PCI devices aren't available with -net nic, e.g. i82558a.
+
+Bug: usb-net does not work, yet. Patch posted.
+
+=== Graphics Devices ===
+
+Host and guest part of graphics devices have always been separate.
+
+The old way to define the guest graphics device is -vga VGA.
+
+The new way is -device. Map from -vga argument to -device:
+
+ std -device VGA
+ cirrus -device cirrus-vga
+ vmware -device vmware-svga
+ xenfb not yet available with -device
+
+As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control
+the PCI device address.
+
+-device VGA supports properties bios-offset and bios-size, but they
+aren't used with machine type "pc".
+
+Bug: -device cirrus-vga and -device vmware-svga require -nodefaults.
+
+Bug: the new way requires PCI; ISA VGA is not yet available with
+-device.
+
+Bug: the new way doesn't work for machine type "pc", because it
+violates obscure device initialization ordering constraints.
+
+=== Audio Devices ===
+
+Host and guest part of audio devices have always been separate.
+
+The old way to define guest audio devices is -soundhw C1,...
+
+The new way is to define each guest audio device separately with
+-device.
+
+Map from -soundhw sound card name to -device:
+
+ ac97 -device AC97
+ cs4231a -device cs4231a,iobase=IOADDR,irq=IRQ,dma=DMA
+ es1370 -device ES1370
+ gus -device gus,iobase=IOADDR,irq=IRQ,dma=DMA,freq=F
+ sb16 -device sb16,iobase=IOADDR,irq=IRQ,dma=DMA,dma16=DMA16,version=V
+ adlib not yet available with -device
+ pcspk not yet available with -device
+
+For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI
+device address, as usual.
+
+=== USB Devices ===
+
+The old way to define a virtual USB device is -usbdevice DRIVER:OPTS...
+
+The new way is -device DEVNAME,DEV-OPTS... Details depend on DRIVER:
+
+* mouse -device usb-mouse
+* tablet -device usb-tablet
+* keyboard -device usb-kdb
+* wacom-tablet -device usb-wacom-tablet
+* host:... See "Host Device Assignment"
+* disk:... See "Block Devices"
+* serial:... See "Character Devices"
+* braille See "Character Devices"
+* net:... See "Network Devices"
+* bt:... not yet available with -device
+
+=== Watchdog Devices ===
+
+Host and guest part of watchdog devices have always been separate.
+
+The old way to define a guest watchdog device is -watchdog DEVNAME.
+The new way is -device DEVNAME. For PCI devices, you can add
+bus=PCI-BUS,addr=DEVFN to control the PCI device address, as usual.
+
+=== Host Device Assignment ===
+
+QEMU supports assigning host PCI devices (qemu-kvm only at this time)
+and host USB devices.
+
+The old way to assign a host PCI device is
+
+ -pcidevice host=ADDR,dma=none,id=ID
+
+The new way is
+
+ -device pci-assign,host=ADDR,iommu=IOMMU,id=ID
+
+The old dma=none becomes iommu=0 with -device.
+
+The old way to assign a host USB device is
+
+ -usbdevice host:auto:BUS.ADDR:VID:PRID
+
+where any of BUS, ADDR, VID, PRID can be the wildcard *.
+
+The new way is
+
+ -device usb-host,hostbus=BUS,hostaddr=ADDR,vendorid=VID,productid=PRID
+
+where left out or zero BUS, ADDR, VID, PRID serve as wildcard.
diff --git a/docs/specs/acpi_pci_hotplug.txt b/docs/specs/acpi_pci_hotplug.txt
new file mode 100644
index 0000000..f0f74a7
--- /dev/null
+++ b/docs/specs/acpi_pci_hotplug.txt
@@ -0,0 +1,37 @@
+QEMU<->ACPI BIOS PCI hotplug interface
+--------------------------------------
+
+QEMU supports PCI hotplug via ACPI, for PCI bus 0. This document
+describes the interface between QEMU and the ACPI BIOS.
+
+ACPI GPE block (IO ports 0xafe0-0xafe3, byte access):
+-----------------------------------------
+
+Generic ACPI GPE block. Bit 1 (GPE.1) used to notify PCI hotplug/eject
+event to ACPI BIOS, via SCI interrupt.
+
+PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access):
+---------------------------------------------------------------
+Slot injection notification pending. One bit per slot.
+
+Read by ACPI BIOS GPE.1 handler to notify OS of injection
+events.
+
+PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access):
+-----------------------------------------------------
+Slot removal notification pending. One bit per slot.
+
+Read by ACPI BIOS GPE.1 handler to notify OS of removal
+events.
+
+PCI device eject (IO port 0xae08-0xae0b, 4-byte access):
+----------------------------------------
+
+Used by ACPI BIOS _EJ0 method to request device removal. One bit per slot.
+Reads return 0.
+
+PCI removability status (IO port 0xae0c-0xae0f, 4-byte access):
+-----------------------------------------------
+
+Used by ACPI BIOS _RMV method to indicate removability status to OS. One
+bit per slot.
diff --git a/docs/specs/ivshmem_device_spec.txt b/docs/specs/ivshmem_device_spec.txt
new file mode 100644
index 0000000..23dd2ba
--- /dev/null
+++ b/docs/specs/ivshmem_device_spec.txt
@@ -0,0 +1,96 @@
+
+Device Specification for Inter-VM shared memory device
+------------------------------------------------------
+
+The Inter-VM shared memory device is designed to share a region of memory to
+userspace in multiple virtual guests. The memory region does not belong to any
+guest, but is a POSIX memory object on the host. Optionally, the device may
+support sending interrupts to other guests sharing the same memory region.
+
+
+The Inter-VM PCI device
+-----------------------
+
+*BARs*
+
+The device supports three BARs. BAR0 is a 1 Kbyte MMIO region to support
+registers. BAR1 is used for MSI-X when it is enabled in the device. BAR2 is
+used to map the shared memory object from the host. The size of BAR2 is
+specified when the guest is started and must be a power of 2 in size.
+
+*Registers*
+
+The device currently supports 4 registers of 32-bits each. Registers
+are used for synchronization between guests sharing the same memory object when
+interrupts are supported (this requires using the shared memory server).
+
+The server assigns each VM an ID number and sends this ID number to the Qemu
+process when the guest starts.
+
+enum ivshmem_registers {
+ IntrMask = 0,
+ IntrStatus = 4,
+ IVPosition = 8,
+ Doorbell = 12
+};
+
+The first two registers are the interrupt mask and status registers. Mask and
+status are only used with pin-based interrupts. They are unused with MSI
+interrupts.
+
+Status Register: The status register is set to 1 when an interrupt occurs.
+
+Mask Register: The mask register is bitwise ANDed with the interrupt status
+and the result will raise an interrupt if it is non-zero. However, since 1 is
+the only value the status will be set to, it is only the first bit of the mask
+that has any effect. Therefore interrupts can be masked by setting the first
+bit to 0 and unmasked by setting the first bit to 1.
+
+IVPosition Register: The IVPosition register is read-only and reports the
+guest's ID number. The guest IDs are non-negative integers. When using the
+server, since the server is a separate process, the VM ID will only be set when
+the device is ready (shared memory is received from the server and accessible via
+the device). If the device is not ready, the IVPosition will return -1.
+Applications should ensure that they have a valid VM ID before accessing the
+shared memory.
+
+Doorbell Register: To interrupt another guest, a guest must write to the
+Doorbell register. The doorbell register is 32-bits, logically divided into
+two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low
+16-bits are the interrupt vector to trigger. The semantics of the value
+written to the doorbell depends on whether the device is using MSI or a regular
+pin-based interrupt. In short, MSI uses vectors while regular interrupts set the
+status register.
+
+Regular Interrupts
+
+If regular interrupts are used (due to either a guest not supporting MSI or the
+user specifying not to use them on startup) then the value written to the lower
+16-bits of the Doorbell register results is arbitrary and will trigger an
+interrupt in the destination guest.
+
+Message Signalled Interrupts
+
+A ivshmem device may support multiple MSI vectors. If so, the lower 16-bits
+written to the Doorbell register must be between 0 and the maximum number of
+vectors the guest supports. The lower 16 bits written to the doorbell is the
+MSI vector that will be raised in the destination guest. The number of MSI
+vectors is configurable but it is set when the VM is started.
+
+The important thing to remember with MSI is that it is only a signal, no status
+is set (since MSI interrupts are not shared). All information other than the
+interrupt itself should be communicated via the shared memory region. Devices
+supporting multiple MSI vectors can use different vectors to indicate different
+events have occurred. The semantics of interrupt vectors are left to the
+user's discretion.
+
+
+Usage in the Guest
+------------------
+
+The shared memory device is intended to be used with the provided UIO driver.
+Very little configuration is needed. The guest should map BAR0 to access the
+registers (an array of 32-bit ints allows simple writing) and map BAR2 to
+access the shared memory region itself. The size of the shared memory region
+is specified when the guest (or shared memory server) is started. A guest may
+map the whole shared memory region or only part of it.
diff --git a/docs/specs/qed_spec.txt b/docs/specs/qed_spec.txt
new file mode 100644
index 0000000..1d5fa87
--- /dev/null
+++ b/docs/specs/qed_spec.txt
@@ -0,0 +1,130 @@
+=Specification=
+
+The file format looks like this:
+
+ +----------+----------+----------+-----+
+ | cluster0 | cluster1 | cluster2 | ... |
+ +----------+----------+----------+-----+
+
+The first cluster begins with the '''header'''. The header contains information about where regular clusters start; this allows the header to be extensible and store extra information about the image file. A regular cluster may be a '''data cluster''', an '''L2''', or an '''L1 table'''. L1 and L2 tables are composed of one or more contiguous clusters.
+
+Normally the file size will be a multiple of the cluster size. If the file size is not a multiple, extra information after the last cluster may not be preserved if data is written. Legitimate extra information should use space between the header and the first regular cluster.
+
+All fields are little-endian.
+
+==Header==
+ Header {
+ uint32_t magic; /* QED\0 */
+
+ uint32_t cluster_size; /* in bytes */
+ uint32_t table_size; /* for L1 and L2 tables, in clusters */
+ uint32_t header_size; /* in clusters */
+
+ uint64_t features; /* format feature bits */
+ uint64_t compat_features; /* compat feature bits */
+ uint64_t autoclear_features; /* self-resetting feature bits */
+
+ uint64_t l1_table_offset; /* in bytes */
+ uint64_t image_size; /* total logical image size, in bytes */
+
+ /* if (features & QED_F_BACKING_FILE) */
+ uint32_t backing_filename_offset; /* in bytes from start of header */
+ uint32_t backing_filename_size; /* in bytes */
+ }
+
+Field descriptions:
+* ''cluster_size'' must be a power of 2 in range [2^12, 2^26].
+* ''table_size'' must be a power of 2 in range [1, 16].
+* ''header_size'' is the number of clusters used by the header and any additional information stored before regular clusters.
+* ''features'', ''compat_features'', and ''autoclear_features'' are file format extension bitmaps. They work as follows:
+** An image with unknown ''features'' bits enabled must not be opened. File format changes that are not backwards-compatible must use ''features'' bits.
+** An image with unknown ''compat_features'' bits enabled can be opened safely. The unknown features are simply ignored and represent backwards-compatible changes to the file format.
+** An image with unknown ''autoclear_features'' bits enable can be opened safely after clearing the unknown bits. This allows for backwards-compatible changes to the file format which degrade gracefully and can be re-enabled again by a new program later.
+* ''l1_table_offset'' is the offset of the first byte of the L1 table in the image file and must be a multiple of ''cluster_size''.
+* ''image_size'' is the block device size seen by the guest and must be a multiple of 512 bytes.
+* ''backing_filename_offset'' and ''backing_filename_size'' describe a string in (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. The string must be stored within the first ''header_size'' clusters. The backing filename may be an absolute path or relative to the image file.
+
+Feature bits:
+* QED_F_BACKING_FILE = 0x01. The image uses a backing file.
+* QED_F_NEED_CHECK = 0x02. The image needs a consistency check before use.
+* QED_F_BACKING_FORMAT_NO_PROBE = 0x04. The backing file is a raw disk image and no file format autodetection should be attempted. This should be used to ensure that raw backing files are never detected as an image format if they happen to contain magic constants.
+
+There are currently no defined ''compat_features'' or ''autoclear_features'' bits.
+
+Fields predicated on a feature bit are only used when that feature is set. The fields always take up header space, regardless of whether or not the feature bit is set.
+
+==Tables==
+
+Tables provide the translation from logical offsets in the block device to cluster offsets in the file.
+
+ #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t))
+
+ Table {
+ uint64_t offsets[TABLE_NOFFSETS];
+ }
+
+The tables are organized as follows:
+
+ +----------+
+ | L1 table |
+ +----------+
+ ,------' | '------.
+ +----------+ | +----------+
+ | L2 table | ... | L2 table |
+ +----------+ +----------+
+ ,------' | '------.
+ +----------+ | +----------+
+ | Data | ... | Data |
+ +----------+ +----------+
+
+A table is made up of one or more contiguous clusters. The table_size header field determines table size for an image file. For example, cluster_size=64 KB and table_size=4 results in 256 KB tables.
+
+The logical image size must be less than or equal to the maximum possible size of clusters rooted by the L1 table:
+ header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size
+
+L1, L2, and data cluster offsets must be aligned to header.cluster_size. The following offsets have special meanings:
+
+===L2 table offsets===
+* 0 - unallocated. The L2 table is not yet allocated.
+
+===Data cluster offsets===
+* 0 - unallocated. The data cluster is not yet allocated.
+
+Future format extensions may wish to store per-offset information. The least significant 12 bits of an offset are reserved for this purpose and must be set to zero. Image files with cluster_size > 2^12 will have more unused bits which should also be zeroed.
+
+===Unallocated L2 tables and data clusters===
+Reads to an unallocated area of the image file access the backing file. If there is no backing file, then zeroes are produced. The backing file may be smaller than the image file and reads of unallocated areas beyond the end of the backing file produce zeroes.
+
+Writes to an unallocated area cause a new data clusters to be allocated, and a new L2 table if that is also unallocated. The new data cluster is populated with data from the backing file (or zeroes if no backing file) and the data being written.
+
+===Logical offset translation===
+Logical offsets are translated into cluster offsets as follows:
+
+ table_bits table_bits cluster_bits
+ <--------> <--------> <--------------->
+ +----------+----------+-----------------+
+ | L1 index | L2 index | byte offset |
+ +----------+----------+-----------------+
+
+ Structure of a logical offset
+
+ offset_mask = ~(cluster_size - 1) # mask for the image file byte offset
+
+ def logical_to_cluster_offset(l1_index, l2_index, byte_offset):
+ l2_offset = l1_table[l1_index]
+ l2_table = load_table(l2_offset)
+ cluster_offset = l2_table[l2_index] & offset_mask
+ return cluster_offset + byte_offset
+
+==Consistency checking==
+
+This section is informational and included to provide background on the use of the QED_F_NEED_CHECK ''features'' bit.
+
+The QED_F_NEED_CHECK bit is used to mark an image as dirty before starting an operation that could leave the image in an inconsistent state if interrupted by a crash or power failure. A dirty image must be checked on open because its metadata may not be consistent.
+
+Consistency check includes the following invariants:
+# Each cluster is referenced once and only once. It is an inconsistency to have a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked if it has no references.
+# Offsets must be within the image file size and must be ''cluster_size'' aligned.
+# Table offsets must at least ''table_size'' * ''cluster_size'' bytes from the end of the image file so that there is space for the entire table.
+
+The consistency check process starts by from ''l1_table_offset'' and scans all L2 tables. After the check completes with no other errors besides leaks, the QED_F_NEED_CHECK bit can be cleared and the image can be accessed.
diff --git a/docs/tracing.txt b/docs/tracing.txt
new file mode 100644
index 0000000..21183f9
--- /dev/null
+++ b/docs/tracing.txt
@@ -0,0 +1,189 @@
+= Tracing =
+
+== Introduction ==
+
+This document describes the tracing infrastructure in QEMU and how to use it
+for debugging, profiling, and observing execution.
+
+== Quickstart ==
+
+1. Build with the 'simple' trace backend:
+
+ ./configure --trace-backend=simple
+ make
+
+2. Enable trace events you are interested in:
+
+ $EDITOR trace-events # remove "disable" from events you want
+
+3. Run the virtual machine to produce a trace file:
+
+ qemu ... # your normal QEMU invocation
+
+4. Pretty-print the binary trace file:
+
+ ./simpletrace.py trace-events trace-*
+
+== Trace events ==
+
+There is a set of static trace events declared in the trace-events source
+file. Each trace event declaration names the event, its arguments, and the
+format string which can be used for pretty-printing:
+
+ qemu_malloc(size_t size, void *ptr) "size %zu ptr %p"
+ qemu_free(void *ptr) "ptr %p"
+
+The trace-events file is processed by the tracetool script during build to
+generate code for the trace events. Trace events are invoked directly from
+source code like this:
+
+ #include "trace.h" /* needed for trace event prototype */
+
+ void *qemu_malloc(size_t size)
+ {
+ void *ptr;
+ if (!size && !allow_zero_malloc()) {
+ abort();
+ }
+ ptr = oom_check(malloc(size ? size : 1));
+ trace_qemu_malloc(size, ptr); /* <-- trace event */
+ return ptr;
+ }
+
+=== Declaring trace events ===
+
+The tracetool script produces the trace.h header file which is included by
+every source file that uses trace events. Since many source files include
+trace.h, it uses a minimum of types and other header files included to keep
+the namespace clean and compile times and dependencies down.
+
+Trace events should use types as follows:
+
+ * Use stdint.h types for fixed-size types. Most offsets and guest memory
+ addresses are best represented with uint32_t or uint64_t. Use fixed-size
+ types over primitive types whose size may change depending on the host
+ (32-bit versus 64-bit) so trace events don't truncate values or break
+ the build.
+
+ * Use void * for pointers to structs or for arrays. The trace.h header
+ cannot include all user-defined struct declarations and it is therefore
+ necessary to use void * for pointers to structs.
+
+ * For everything else, use primitive scalar types (char, int, long) with the
+ appropriate signedness.
+
+Format strings should reflect the types defined in the trace event. Take
+special care to use PRId64 and PRIu64 for int64_t and uint64_t types,
+respectively. This ensures portability between 32- and 64-bit platforms. Note
+that format strings must begin and end with double quotes. When using
+portability macros, ensure they are preceded and followed by double quotes:
+"value %"PRIx64"".
+
+=== Hints for adding new trace events ===
+
+1. Trace state changes in the code. Interesting points in the code usually
+ involve a state change like starting, stopping, allocating, freeing. State
+ changes are good trace events because they can be used to understand the
+ execution of the system.
+
+2. Trace guest operations. Guest I/O accesses like reading device registers
+ are good trace events because they can be used to understand guest
+ interactions.
+
+3. Use correlator fields so the context of an individual line of trace output
+ can be understood. For example, trace the pointer returned by malloc and
+ used as an argument to free. This way mallocs and frees can be matched up.
+ Trace events with no context are not very useful.
+
+4. Name trace events after their function. If there are multiple trace events
+ in one function, append a unique distinguisher at the end of the name.
+
+5. Declare trace events with the "disable" keyword. Some trace events can
+ produce a lot of output and users are typically only interested in a subset
+ of trace events. Marking trace events disabled by default saves the user
+ from having to manually disable noisy trace events.
+
+== Trace backends ==
+
+The tracetool script automates tedious trace event code generation and also
+keeps the trace event declarations independent of the trace backend. The trace
+events are not tightly coupled to a specific trace backend, such as LTTng or
+SystemTap. Support for trace backends can be added by extending the tracetool
+script.
+
+The trace backend is chosen at configure time and only one trace backend can
+be built into the binary:
+
+ ./configure --trace-backend=simple
+
+For a list of supported trace backends, try ./configure --help or see below.
+
+The following subsections describe the supported trace backends.
+
+=== Nop ===
+
+The "nop" backend generates empty trace event functions so that the compiler
+can optimize out trace events completely. This is the default and imposes no
+performance penalty.
+
+=== Simpletrace ===
+
+The "simple" backend supports common use cases and comes as part of the QEMU
+source tree. It may not be as powerful as platform-specific or third-party
+trace backends but it is portable. This is the recommended trace backend
+unless you have specific needs for more advanced backends.
+
+=== Stderr ===
+
+The "stderr" backend sends trace events directly to standard error output
+during emulation.
+
+==== Monitor commands ====
+
+* info trace
+ Display the contents of trace buffer. This command dumps the trace buffer
+ with simple formatting. For full pretty-printing, use the simpletrace.py
+ script on a binary trace file.
+
+ The trace buffer is written into until full. The full trace buffer is
+ flushed and emptied. This means the 'info trace' will display few or no
+ entries if the buffer has just been flushed.
+
+* info trace-events
+ View available trace events and their state. State 1 means enabled, state 0
+ means disabled.
+
+* trace-event NAME on|off
+ Enable/disable a given trace event.
+
+* trace-file on|off|flush|set <path>
+ Enable/disable/flush the trace file or set the trace file name.
+
+==== Enabling/disabling trace events programmatically ====
+
+The st_change_trace_event_state() function can be used to enable or disable trace
+events at runtime inside QEMU:
+
+ #include "trace.h"
+
+ st_change_trace_event_state("virtio_irq", true); /* enable */
+ [...]
+ st_change_trace_event_state("virtio_irq", false); /* disable */
+
+==== Analyzing trace files ====
+
+The "simple" backend produces binary trace files that can be formatted with the
+simpletrace.py script. The script takes the trace-events file and the binary
+trace:
+
+ ./simpletrace.py trace-events trace-12345
+
+You must ensure that the same trace-events file was used to build QEMU,
+otherwise trace event declarations may have changed and output will not be
+consistent.
+
+=== LTTng Userspace Tracer ===
+
+The "ust" backend uses the LTTng Userspace Tracer library. There are no
+monitor commands built into QEMU, instead UST utilities should be used to list,
+enable/disable, and dump traces.
diff --git a/dyngen-exec.h b/dyngen-exec.h
new file mode 100644
index 0000000..db00fba
--- /dev/null
+++ b/dyngen-exec.h
@@ -0,0 +1,84 @@
+/*
+ * dyngen defines for micro operation code
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#if !defined(__DYNGEN_EXEC_H__)
+#define __DYNGEN_EXEC_H__
+
+#include "qemu-common.h"
+
+#ifdef __OpenBSD__
+#include <sys/types.h>
+#endif
+
+/* XXX: This may be wrong for 64-bit ILP32 hosts. */
+typedef void * host_reg_t;
+
+#if defined(__i386__)
+#define AREG0 "ebp"
+#elif defined(__x86_64__)
+#define AREG0 "r14"
+#elif defined(_ARCH_PPC)
+#define AREG0 "r27"
+#elif defined(__arm__)
+#define AREG0 "r7"
+#elif defined(__hppa__)
+#define AREG0 "r17"
+#elif defined(__mips__)
+#define AREG0 "s0"
+#elif defined(__sparc__)
+#ifdef CONFIG_SOLARIS
+#define AREG0 "g2"
+#else
+#ifdef __sparc_v9__
+#define AREG0 "g5"
+#else
+#define AREG0 "g6"
+#endif
+#endif
+#elif defined(__s390__)
+#define AREG0 "r10"
+#elif defined(__alpha__)
+/* Note $15 is the frame pointer, so anything in op-i386.c that would
+ require a frame pointer, like alloca, would probably loose. */
+#define AREG0 "$15"
+#elif defined(__mc68000)
+#define AREG0 "%a5"
+#elif defined(__ia64__)
+#define AREG0 "r7"
+#else
+#error unsupported CPU
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+/* The return address may point to the start of the next instruction.
+ Subtracting one gets us the call instruction itself. */
+#if defined(__s390__) && !defined(__s390x__)
+# define GETPC() ((void*)(((unsigned long)__builtin_return_address(0) & 0x7fffffffUL) - 1))
+#elif defined(__arm__)
+/* Thumb return addresses have the low bit set, so we need to subtract two.
+ This is still safe in ARM mode because instructions are 4 bytes. */
+# define GETPC() ((void *)((unsigned long)__builtin_return_address(0) - 2))
+#else
+# define GETPC() ((void *)((unsigned long)__builtin_return_address(0) - 1))
+#endif
+
+#endif /* !defined(__DYNGEN_EXEC_H__) */
diff --git a/elf.h b/elf.h
new file mode 100644
index 0000000..7067c90
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,1240 @@
+#ifndef _QEMU_ELF_H
+#define _QEMU_ELF_H
+
+#include <inttypes.h>
+
+/* 32-bit ELF base types. */
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+/* 64-bit ELF base types. */
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef int16_t Elf64_SHalf;
+typedef uint64_t Elf64_Off;
+typedef int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Xword;
+typedef int64_t Elf64_Sxword;
+
+/* These constants are for the segment types stored in the image headers */
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_LOPROC 0x70000000
+#define PT_HIPROC 0x7fffffff
+#define PT_MIPS_REGINFO 0x70000000
+#define PT_MIPS_OPTIONS 0x70000001
+
+/* Flags in the e_flags field of the header */
+/* MIPS architecture level. */
+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */
+#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */
+
+/* The ABI of a file. */
+#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */
+#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */
+
+#define EF_MIPS_NOREORDER 0x00000001
+#define EF_MIPS_PIC 0x00000002
+#define EF_MIPS_CPIC 0x00000004
+#define EF_MIPS_ABI2 0x00000020
+#define EF_MIPS_OPTIONS_FIRST 0x00000080
+#define EF_MIPS_32BITMODE 0x00000100
+#define EF_MIPS_ABI 0x0000f000
+#define EF_MIPS_ARCH 0xf0000000
+
+/* These constants define the different elf file types */
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+
+/* These constants define the various ELF target machines */
+#define EM_NONE 0
+#define EM_M32 1
+#define EM_SPARC 2
+#define EM_386 3
+#define EM_68K 4
+#define EM_88K 5
+#define EM_486 6 /* Perhaps disused */
+#define EM_860 7
+
+#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */
+
+#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
+
+#define EM_PARISC 15 /* HPPA */
+
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+
+#define EM_PPC 20 /* PowerPC */
+#define EM_PPC64 21 /* PowerPC64 */
+
+#define EM_ARM 40 /* ARM */
+
+#define EM_SH 42 /* SuperH */
+
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+
+#define EM_IA_64 50 /* HP/Intel IA-64 */
+
+#define EM_X86_64 62 /* AMD x86-64 */
+
+#define EM_S390 22 /* IBM S/390 */
+
+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+
+#define EM_V850 87 /* NEC v850 */
+
+#define EM_H8_300H 47 /* Hitachi H8/300H */
+#define EM_H8S 48 /* Hitachi H8S */
+
+/*
+ * This is an interim value that we will use until the committee comes
+ * up with a final number.
+ */
+#define EM_ALPHA 0x9026
+
+/* Bogus old v850 magic number, used by old tools. */
+#define EM_CYGNUS_V850 0x9080
+
+/*
+ * This is the old interim value for S/390 architecture
+ */
+#define EM_S390_OLD 0xA390
+
+#define EM_MICROBLAZE 189
+#define EM_MICROBLAZE_OLD 0xBAAB
+
+/* This is the info that is needed to parse the dynamic section of the file */
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_INIT 12
+#define DT_FINI 13
+#define DT_SONAME 14
+#define DT_RPATH 15
+#define DT_SYMBOLIC 16
+#define DT_REL 17
+#define DT_RELSZ 18
+#define DT_RELENT 19
+#define DT_PLTREL 20
+#define DT_DEBUG 21
+#define DT_TEXTREL 22
+#define DT_JMPREL 23
+#define DT_BINDNOW 24
+#define DT_INIT_ARRAY 25
+#define DT_FINI_ARRAY 26
+#define DT_INIT_ARRAYSZ 27
+#define DT_FINI_ARRAYSZ 28
+#define DT_RUNPATH 29
+#define DT_FLAGS 30
+#define DT_LOOS 0x6000000d
+#define DT_HIOS 0x6ffff000
+#define DT_LOPROC 0x70000000
+#define DT_HIPROC 0x7fffffff
+
+/* DT_ entries which fall between DT_VALRNGLO and DT_VALRNDHI use
+ the d_val field of the Elf*_Dyn structure. I.e. they contain scalars. */
+#define DT_VALRNGLO 0x6ffffd00
+#define DT_VALRNGHI 0x6ffffdff
+
+/* DT_ entries which fall between DT_ADDRRNGLO and DT_ADDRRNGHI use
+ the d_ptr field of the Elf*_Dyn structure. I.e. they contain pointers. */
+#define DT_ADDRRNGLO 0x6ffffe00
+#define DT_ADDRRNGHI 0x6ffffeff
+
+#define DT_VERSYM 0x6ffffff0
+#define DT_RELACOUNT 0x6ffffff9
+#define DT_RELCOUNT 0x6ffffffa
+#define DT_FLAGS_1 0x6ffffffb
+#define DT_VERDEF 0x6ffffffc
+#define DT_VERDEFNUM 0x6ffffffd
+#define DT_VERNEED 0x6ffffffe
+#define DT_VERNEEDNUM 0x6fffffff
+
+#define DT_MIPS_RLD_VERSION 0x70000001
+#define DT_MIPS_TIME_STAMP 0x70000002
+#define DT_MIPS_ICHECKSUM 0x70000003
+#define DT_MIPS_IVERSION 0x70000004
+#define DT_MIPS_FLAGS 0x70000005
+ #define RHF_NONE 0
+ #define RHF_HARDWAY 1
+ #define RHF_NOTPOT 2
+#define DT_MIPS_BASE_ADDRESS 0x70000006
+#define DT_MIPS_CONFLICT 0x70000008
+#define DT_MIPS_LIBLIST 0x70000009
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a
+#define DT_MIPS_CONFLICTNO 0x7000000b
+#define DT_MIPS_LIBLISTNO 0x70000010
+#define DT_MIPS_SYMTABNO 0x70000011
+#define DT_MIPS_UNREFEXTNO 0x70000012
+#define DT_MIPS_GOTSYM 0x70000013
+#define DT_MIPS_HIPAGENO 0x70000014
+#define DT_MIPS_RLD_MAP 0x70000016
+
+/* This info is needed when parsing the symbol table */
+#define STB_LOCAL 0
+#define STB_GLOBAL 1
+#define STB_WEAK 2
+
+#define STT_NOTYPE 0
+#define STT_OBJECT 1
+#define STT_FUNC 2
+#define STT_SECTION 3
+#define STT_FILE 4
+
+#define ELF_ST_BIND(x) ((x) >> 4)
+#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf)
+#define ELF32_ST_BIND(x) ELF_ST_BIND(x)
+#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x)
+#define ELF64_ST_BIND(x) ELF_ST_BIND(x)
+#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x)
+
+/* Symbolic values for the entries in the auxiliary table
+ put on the initial stack */
+#define AT_NULL 0 /* end of vector */
+#define AT_IGNORE 1 /* entry should be ignored */
+#define AT_EXECFD 2 /* file descriptor of program */
+#define AT_PHDR 3 /* program headers for program */
+#define AT_PHENT 4 /* size of program header entry */
+#define AT_PHNUM 5 /* number of program headers */
+#define AT_PAGESZ 6 /* system page size */
+#define AT_BASE 7 /* base address of interpreter */
+#define AT_FLAGS 8 /* flags */
+#define AT_ENTRY 9 /* entry point of program */
+#define AT_NOTELF 10 /* program is not ELF */
+#define AT_UID 11 /* real uid */
+#define AT_EUID 12 /* effective uid */
+#define AT_GID 13 /* real gid */
+#define AT_EGID 14 /* effective gid */
+#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
+#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
+#define AT_CLKTCK 17 /* frequency at which times() increments */
+#define AT_FPUCW 18 /* info about fpu initialization by kernel */
+#define AT_DCACHEBSIZE 19 /* data cache block size */
+#define AT_ICACHEBSIZE 20 /* instruction cache block size */
+#define AT_UCACHEBSIZE 21 /* unified cache block size */
+#define AT_IGNOREPPC 22 /* ppc only; entry should be ignored */
+#define AT_SECURE 23 /* boolean, was exec suid-like? */
+#define AT_BASE_PLATFORM 24 /* string identifying real platforms */
+#define AT_RANDOM 25 /* address of 16 random bytes */
+#define AT_EXECFN 31 /* filename of the executable */
+#define AT_SYSINFO 32 /* address of kernel entry point */
+#define AT_SYSINFO_EHDR 33 /* address of kernel vdso */
+#define AT_L1I_CACHESHAPE 34 /* shapes of the caches: */
+#define AT_L1D_CACHESHAPE 35 /* bits 0-3: cache associativity. */
+#define AT_L2_CACHESHAPE 36 /* bits 4-7: log2 of line size. */
+#define AT_L3_CACHESHAPE 37 /* val&~255: cache size. */
+
+typedef struct dynamic{
+ Elf32_Sword d_tag;
+ union{
+ Elf32_Sword d_val;
+ Elf32_Addr d_ptr;
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+ Elf64_Sxword d_tag; /* entry tag value */
+ union {
+ Elf64_Xword d_val;
+ Elf64_Addr d_ptr;
+ } d_un;
+} Elf64_Dyn;
+
+/* The following are used with relocations */
+#define ELF32_R_SYM(x) ((x) >> 8)
+#define ELF32_R_TYPE(x) ((x) & 0xff)
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
+#define ELF64_R_TYPE_DATA(i) (((ELF64_R_TYPE(i) >> 8) ^ 0x00800000) - 0x00800000)
+
+#define R_386_NONE 0
+#define R_386_32 1
+#define R_386_PC32 2
+#define R_386_GOT32 3
+#define R_386_PLT32 4
+#define R_386_COPY 5
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+#define R_386_RELATIVE 8
+#define R_386_GOTOFF 9
+#define R_386_GOTPC 10
+#define R_386_NUM 11
+/* Not a dynamic reloc, so not included in R_386_NUM. Used in TCG. */
+#define R_386_PC8 23
+
+#define R_MIPS_NONE 0
+#define R_MIPS_16 1
+#define R_MIPS_32 2
+#define R_MIPS_REL32 3
+#define R_MIPS_26 4
+#define R_MIPS_HI16 5
+#define R_MIPS_LO16 6
+#define R_MIPS_GPREL16 7
+#define R_MIPS_LITERAL 8
+#define R_MIPS_GOT16 9
+#define R_MIPS_PC16 10
+#define R_MIPS_CALL16 11
+#define R_MIPS_GPREL32 12
+/* The remaining relocs are defined on Irix, although they are not
+ in the MIPS ELF ABI. */
+#define R_MIPS_UNUSED1 13
+#define R_MIPS_UNUSED2 14
+#define R_MIPS_UNUSED3 15
+#define R_MIPS_SHIFT5 16
+#define R_MIPS_SHIFT6 17
+#define R_MIPS_64 18
+#define R_MIPS_GOT_DISP 19
+#define R_MIPS_GOT_PAGE 20
+#define R_MIPS_GOT_OFST 21
+/*
+ * The following two relocation types are specified in the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_GOTHI16 22
+#define R_MIPS_GOTLO16 23
+#define R_MIPS_SUB 24
+#define R_MIPS_INSERT_A 25
+#define R_MIPS_INSERT_B 26
+#define R_MIPS_DELETE 27
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+/*
+ * The following two relocation types are specified in the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_CALLHI16 30
+#define R_MIPS_CALLLO16 31
+/*
+ * This range is reserved for vendor specific relocations.
+ */
+#define R_MIPS_LOVENDOR 100
+#define R_MIPS_HIVENDOR 127
+
+
+/*
+ * Sparc ELF relocation types
+ */
+#define R_SPARC_NONE 0
+#define R_SPARC_8 1
+#define R_SPARC_16 2
+#define R_SPARC_32 3
+#define R_SPARC_DISP8 4
+#define R_SPARC_DISP16 5
+#define R_SPARC_DISP32 6
+#define R_SPARC_WDISP30 7
+#define R_SPARC_WDISP22 8
+#define R_SPARC_HI22 9
+#define R_SPARC_22 10
+#define R_SPARC_13 11
+#define R_SPARC_LO10 12
+#define R_SPARC_GOT10 13
+#define R_SPARC_GOT13 14
+#define R_SPARC_GOT22 15
+#define R_SPARC_PC10 16
+#define R_SPARC_PC22 17
+#define R_SPARC_WPLT30 18
+#define R_SPARC_COPY 19
+#define R_SPARC_GLOB_DAT 20
+#define R_SPARC_JMP_SLOT 21
+#define R_SPARC_RELATIVE 22
+#define R_SPARC_UA32 23
+#define R_SPARC_PLT32 24
+#define R_SPARC_HIPLT22 25
+#define R_SPARC_LOPLT10 26
+#define R_SPARC_PCPLT32 27
+#define R_SPARC_PCPLT22 28
+#define R_SPARC_PCPLT10 29
+#define R_SPARC_10 30
+#define R_SPARC_11 31
+#define R_SPARC_64 32
+#define R_SPARC_OLO10 33
+#define R_SPARC_HH22 34
+#define R_SPARC_HM10 35
+#define R_SPARC_LM22 36
+#define R_SPARC_WDISP16 40
+#define R_SPARC_WDISP19 41
+#define R_SPARC_7 43
+#define R_SPARC_5 44
+#define R_SPARC_6 45
+
+/* Bits present in AT_HWCAP, primarily for Sparc32. */
+
+#define HWCAP_SPARC_FLUSH 1 /* CPU supports flush instruction. */
+#define HWCAP_SPARC_STBAR 2
+#define HWCAP_SPARC_SWAP 4
+#define HWCAP_SPARC_MULDIV 8
+#define HWCAP_SPARC_V9 16
+#define HWCAP_SPARC_ULTRA3 32
+
+/*
+ * 68k ELF relocation types
+ */
+#define R_68K_NONE 0
+#define R_68K_32 1
+#define R_68K_16 2
+#define R_68K_8 3
+#define R_68K_PC32 4
+#define R_68K_PC16 5
+#define R_68K_PC8 6
+#define R_68K_GOT32 7
+#define R_68K_GOT16 8
+#define R_68K_GOT8 9
+#define R_68K_GOT32O 10
+#define R_68K_GOT16O 11
+#define R_68K_GOT8O 12
+#define R_68K_PLT32 13
+#define R_68K_PLT16 14
+#define R_68K_PLT8 15
+#define R_68K_PLT32O 16
+#define R_68K_PLT16O 17
+#define R_68K_PLT8O 18
+#define R_68K_COPY 19
+#define R_68K_GLOB_DAT 20
+#define R_68K_JMP_SLOT 21
+#define R_68K_RELATIVE 22
+
+/*
+ * Alpha ELF relocation types
+ */
+#define R_ALPHA_NONE 0 /* No reloc */
+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
+#define R_ALPHA_BRSGP 28
+#define R_ALPHA_TLSGD 29
+#define R_ALPHA_TLS_LDM 30
+#define R_ALPHA_DTPMOD64 31
+#define R_ALPHA_GOTDTPREL 32
+#define R_ALPHA_DTPREL64 33
+#define R_ALPHA_DTPRELHI 34
+#define R_ALPHA_DTPRELLO 35
+#define R_ALPHA_DTPREL16 36
+#define R_ALPHA_GOTTPREL 37
+#define R_ALPHA_TPREL64 38
+#define R_ALPHA_TPRELHI 39
+#define R_ALPHA_TPRELLO 40
+#define R_ALPHA_TPREL16 41
+
+#define SHF_ALPHA_GPREL 0x10000000
+
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE 0
+#define R_PPC_ADDR32 1 /* 32bit absolute address */
+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
+#define R_PPC_ADDR16 3 /* 16bit absolute address */
+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN 8
+#define R_PPC_ADDR14_BRNTAKEN 9
+#define R_PPC_REL24 10 /* PC relative 26 bit */
+#define R_PPC_REL14 11 /* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN 12
+#define R_PPC_REL14_BRNTAKEN 13
+#define R_PPC_GOT16 14
+#define R_PPC_GOT16_LO 15
+#define R_PPC_GOT16_HI 16
+#define R_PPC_GOT16_HA 17
+#define R_PPC_PLTREL24 18
+#define R_PPC_COPY 19
+#define R_PPC_GLOB_DAT 20
+#define R_PPC_JMP_SLOT 21
+#define R_PPC_RELATIVE 22
+#define R_PPC_LOCAL24PC 23
+#define R_PPC_UADDR32 24
+#define R_PPC_UADDR16 25
+#define R_PPC_REL32 26
+#define R_PPC_PLT32 27
+#define R_PPC_PLTREL32 28
+#define R_PPC_PLT16_LO 29
+#define R_PPC_PLT16_HI 30
+#define R_PPC_PLT16_HA 31
+#define R_PPC_SDAREL16 32
+#define R_PPC_SECTOFF 33
+#define R_PPC_SECTOFF_LO 34
+#define R_PPC_SECTOFF_HI 35
+#define R_PPC_SECTOFF_HA 36
+/* Keep this the last entry. */
+#ifndef R_PPC_NUM
+#define R_PPC_NUM 37
+#endif
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_ARM_RELEXEC 0x01
+#define EF_ARM_HASENTRY 0x02
+#define EF_ARM_INTERWORK 0x04
+#define EF_ARM_APCS_26 0x08
+#define EF_ARM_APCS_FLOAT 0x10
+#define EF_ARM_PIC 0x20
+#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */
+#define EF_NEW_ABI 0x80
+#define EF_OLD_ABI 0x100
+
+/* Additional symbol types for Thumb */
+#define STT_ARM_TFUNC 0xd
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
+ in the input to a link step */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB 0x10000000 /* Segment contains the location
+ addressed by the static base */
+
+/* ARM relocs. */
+#define R_ARM_NONE 0 /* No reloc */
+#define R_ARM_PC24 1 /* PC relative 26 bit branch */
+#define R_ARM_ABS32 2 /* Direct 32 bit */
+#define R_ARM_REL32 3 /* PC relative 32 bit */
+#define R_ARM_PC13 4
+#define R_ARM_ABS16 5 /* Direct 16 bit */
+#define R_ARM_ABS12 6 /* Direct 12 bit */
+#define R_ARM_THM_ABS5 7
+#define R_ARM_ABS8 8 /* Direct 8 bit */
+#define R_ARM_SBREL32 9
+#define R_ARM_THM_PC22 10
+#define R_ARM_THM_PC8 11
+#define R_ARM_AMP_VCALL9 12
+#define R_ARM_SWI24 13
+#define R_ARM_THM_SWI8 14
+#define R_ARM_XPC25 15
+#define R_ARM_THM_XPC22 16
+#define R_ARM_COPY 20 /* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
+#define R_ARM_RELATIVE 23 /* Adjust by program base */
+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32 26 /* 32 bit GOT entry */
+#define R_ARM_PLT32 27 /* 32 bit PLT address */
+#define R_ARM_CALL 28
+#define R_ARM_JUMP24 29
+#define R_ARM_GNU_VTENTRY 100
+#define R_ARM_GNU_VTINHERIT 101
+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
+#define R_ARM_THM_PC9 103 /* thumb conditional branch */
+#define R_ARM_RXPC25 249
+#define R_ARM_RSBREL32 250
+#define R_ARM_THM_RPC22 251
+#define R_ARM_RREL32 252
+#define R_ARM_RABS22 253
+#define R_ARM_RPC24 254
+#define R_ARM_RBASE 255
+/* Keep this the last entry. */
+#define R_ARM_NUM 256
+
+/* s390 relocations defined by the ABIs */
+#define R_390_NONE 0 /* No reloc. */
+#define R_390_8 1 /* Direct 8 bit. */
+#define R_390_12 2 /* Direct 12 bit. */
+#define R_390_16 3 /* Direct 16 bit. */
+#define R_390_32 4 /* Direct 32 bit. */
+#define R_390_PC32 5 /* PC relative 32 bit. */
+#define R_390_GOT12 6 /* 12 bit GOT offset. */
+#define R_390_GOT32 7 /* 32 bit GOT offset. */
+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
+#define R_390_COPY 9 /* Copy symbol at runtime. */
+#define R_390_GLOB_DAT 10 /* Create GOT entry. */
+#define R_390_JMP_SLOT 11 /* Create PLT entry. */
+#define R_390_RELATIVE 12 /* Adjust by program base. */
+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
+#define R_390_GOTPC 14 /* 32 bit PC rel. offset to GOT. */
+#define R_390_GOT16 15 /* 16 bit GOT offset. */
+#define R_390_PC16 16 /* PC relative 16 bit. */
+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
+#define R_390_64 22 /* Direct 64 bit. */
+#define R_390_PC64 23 /* PC relative 64 bit. */
+#define R_390_GOT64 24 /* 64 bit GOT offset. */
+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
+#define R_390_TLS_GDCALL 38 /* Tag for function call in general
+ dynamic TLS code. */
+#define R_390_TLS_LDCALL 39 /* Tag for function call in local
+ dynamic TLS code. */
+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
+ thread local data in LD code. */
+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
+ thread local data in LD code. */
+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
+ block. */
+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
+ block. */
+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
+#define R_390_TLS_TPOFF 56 /* Negate offset in static TLS
+ block. */
+/* Keep this the last entry. */
+#define R_390_NUM 57
+
+/* x86-64 relocation types */
+#define R_X86_64_NONE 0 /* No reloc */
+#define R_X86_64_64 1 /* Direct 64 bit */
+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
+#define R_X86_64_PLT32 4 /* 32 bit PLT address */
+#define R_X86_64_COPY 5 /* Copy symbol at runtime */
+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
+#define R_X86_64_RELATIVE 8 /* Adjust by program base */
+#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative
+ offset to GOT */
+#define R_X86_64_32 10 /* Direct 32 bit zero extended */
+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
+#define R_X86_64_16 12 /* Direct 16 bit zero extended */
+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
+#define R_X86_64_8 14 /* Direct 8 bit sign extended */
+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
+
+#define R_X86_64_NUM 16
+
+/* Legal values for e_flags field of Elf64_Ehdr. */
+
+#define EF_ALPHA_32BIT 1 /* All addresses are below 2GB */
+
+/* HPPA specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
+ prediction. */
+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
+
+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
+
+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
+
+/* Additional section indeces. */
+
+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
+ symbols in ANSI C. */
+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
+
+#define STT_HP_OPAQUE (STT_LOOS + 0x1)
+#define STT_HP_STUB (STT_LOOS + 0x2)
+
+/* HPPA relocs. */
+
+#define R_PARISC_NONE 0 /* No reloc. */
+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
+#define R_PARISC_FPTR64 64 /* 64 bits function address. */
+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LORESERVE 128
+#define R_PARISC_COPY 128 /* Copy relocation. */
+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_HIRESERVE 255
+
+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PT_HP_TLS (PT_LOOS + 0x0)
+#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
+#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
+#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
+#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
+#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
+#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
+#define PT_HP_PARALLEL (PT_LOOS + 0x10)
+#define PT_HP_FASTBIND (PT_LOOS + 0x11)
+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
+#define PT_HP_STACK (PT_LOOS + 0x14)
+
+#define PT_PARISC_ARCHEXT 0x70000000
+#define PT_PARISC_UNWIND 0x70000001
+
+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PF_PARISC_SBP 0x08000000
+
+#define PF_HP_PAGE_SIZE 0x00100000
+#define PF_HP_FAR_SHARED 0x00200000
+#define PF_HP_NEAR_SHARED 0x00400000
+#define PF_HP_CODE 0x01000000
+#define PF_HP_MODIFY 0x02000000
+#define PF_HP_LAZYSWAP 0x04000000
+#define PF_HP_SBP 0x08000000
+
+/* IA-64 specific declarations. */
+
+/* Processor specific flags for the Ehdr e_flags field. */
+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
+
+/* Processor specific flags for the Phdr p_flags field. */
+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
+
+/* Processor specific flags for the Shdr sh_flags field. */
+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Dyn d_tag field. */
+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
+#define DT_IA_64_NUM 1
+
+/* IA-64 relocations. */
+#define R_IA64_NONE 0x00 /* none */
+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
+#define R_IA64_COPY 0x84 /* copy relocation */
+#define R_IA64_SUB 0x85 /* Addend and symbol difference */
+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
+
+typedef struct elf32_rel {
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+} Elf32_Rel;
+
+typedef struct elf64_rel {
+ Elf64_Addr r_offset; /* Location at which to apply the action */
+ Elf64_Xword r_info; /* index and type of relocation */
+} Elf64_Rel;
+
+typedef struct elf32_rela{
+ Elf32_Addr r_offset;
+ Elf32_Word r_info;
+ Elf32_Sword r_addend;
+} Elf32_Rela;
+
+typedef struct elf64_rela {
+ Elf64_Addr r_offset; /* Location at which to apply the action */
+ Elf64_Xword r_info; /* index and type of relocation */
+ Elf64_Sxword r_addend; /* Constant addend used to compute value */
+} Elf64_Rela;
+
+typedef struct elf32_sym{
+ Elf32_Word st_name;
+ Elf32_Addr st_value;
+ Elf32_Word st_size;
+ unsigned char st_info;
+ unsigned char st_other;
+ Elf32_Half st_shndx;
+} Elf32_Sym;
+
+typedef struct elf64_sym {
+ Elf64_Word st_name; /* Symbol name, index in string tbl */
+ unsigned char st_info; /* Type and binding attributes */
+ unsigned char st_other; /* No defined meaning, 0 */
+ Elf64_Half st_shndx; /* Associated section index */
+ Elf64_Addr st_value; /* Value of the symbol */
+ Elf64_Xword st_size; /* Associated symbol size */
+} Elf64_Sym;
+
+
+#define EI_NIDENT 16
+
+typedef struct elf32_hdr{
+ unsigned char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry; /* Entry point */
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct elf64_hdr {
+ unsigned char e_ident[16]; /* ELF "magic number" */
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+/* These constants define the permissions on sections in the program
+ header, p_flags. */
+#define PF_R 0x4
+#define PF_W 0x2
+#define PF_X 0x1
+
+typedef struct elf32_phdr{
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+typedef struct elf64_phdr {
+ Elf64_Word p_type;
+ Elf64_Word p_flags;
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment, file & memory */
+} Elf64_Phdr;
+
+/* sh_type */
+#define SHT_NULL 0
+#define SHT_PROGBITS 1
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_RELA 4
+#define SHT_HASH 5
+#define SHT_DYNAMIC 6
+#define SHT_NOTE 7
+#define SHT_NOBITS 8
+#define SHT_REL 9
+#define SHT_SHLIB 10
+#define SHT_DYNSYM 11
+#define SHT_NUM 12
+#define SHT_LOPROC 0x70000000
+#define SHT_HIPROC 0x7fffffff
+#define SHT_LOUSER 0x80000000
+#define SHT_HIUSER 0xffffffff
+#define SHT_MIPS_LIST 0x70000000
+#define SHT_MIPS_CONFLICT 0x70000002
+#define SHT_MIPS_GPTAB 0x70000003
+#define SHT_MIPS_UCODE 0x70000004
+
+/* sh_flags */
+#define SHF_WRITE 0x1
+#define SHF_ALLOC 0x2
+#define SHF_EXECINSTR 0x4
+#define SHF_MASKPROC 0xf0000000
+#define SHF_MIPS_GPREL 0x10000000
+
+/* special section indexes */
+#define SHN_UNDEF 0
+#define SHN_LORESERVE 0xff00
+#define SHN_LOPROC 0xff00
+#define SHN_HIPROC 0xff1f
+#define SHN_ABS 0xfff1
+#define SHN_COMMON 0xfff2
+#define SHN_HIRESERVE 0xffff
+#define SHN_MIPS_ACCOMON 0xff00
+
+typedef struct elf32_shdr {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct elf64_shdr {
+ Elf64_Word sh_name; /* Section name, index in string tbl */
+ Elf64_Word sh_type; /* Type of section */
+ Elf64_Xword sh_flags; /* Miscellaneous section attributes */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Size of section in bytes */
+ Elf64_Word sh_link; /* Index of another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_PAD 8
+
+#define ELFOSABI_NONE 0 /* UNIX System V ABI */
+#define ELFOSABI_SYSV 0 /* Alias. */
+#define ELFOSABI_HPUX 1 /* HP-UX */
+#define ELFOSABI_NETBSD 2 /* NetBSD. */
+#define ELFOSABI_LINUX 3 /* Linux. */
+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
+#define ELFOSABI_AIX 7 /* IBM AIX. */
+#define ELFOSABI_IRIX 8 /* SGI Irix. */
+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
+#define ELFOSABI_ARM 97 /* ARM */
+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define ELFCLASSNONE 0 /* EI_CLASS */
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+#define ELFCLASSNUM 3
+
+#define ELFDATANONE 0 /* e_ident[EI_DATA] */
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EV_NONE 0 /* e_version, EI_VERSION */
+#define EV_CURRENT 1
+#define EV_NUM 2
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_TASKSTRUCT 4
+#define NT_AUXV 6
+#define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */
+
+
+/* Note header in a PT_NOTE section */
+typedef struct elf32_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf32_Nhdr;
+
+/* Note header in a PT_NOTE section */
+typedef struct elf64_note {
+ Elf64_Word n_namesz; /* Name size */
+ Elf64_Word n_descsz; /* Content size */
+ Elf64_Word n_type; /* Content type */
+} Elf64_Nhdr;
+
+#ifdef ELF_CLASS
+#if ELF_CLASS == ELFCLASS32
+
+#define elfhdr elf32_hdr
+#define elf_phdr elf32_phdr
+#define elf_note elf32_note
+#define elf_shdr elf32_shdr
+#define elf_sym elf32_sym
+#define elf_addr_t Elf32_Off
+
+#ifdef ELF_USES_RELOCA
+# define ELF_RELOC Elf32_Rela
+#else
+# define ELF_RELOC Elf32_Rel
+#endif
+
+#else
+
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+#define elf_shdr elf64_shdr
+#define elf_sym elf64_sym
+#define elf_addr_t Elf64_Off
+
+#ifdef ELF_USES_RELOCA
+# define ELF_RELOC Elf64_Rela
+#else
+# define ELF_RELOC Elf64_Rel
+#endif
+
+#endif /* ELF_CLASS */
+
+#ifndef ElfW
+# if ELF_CLASS == ELFCLASS32
+# define ElfW(x) Elf32_ ## x
+# define ELFW(x) ELF32_ ## x
+# else
+# define ElfW(x) Elf64_ ## x
+# define ELFW(x) ELF64_ ## x
+# endif
+#endif
+
+#endif /* ELF_CLASS */
+
+
+#endif /* _QEMU_ELF_H */
diff --git a/envlist.c b/envlist.c
new file mode 100644
index 0000000..f2303cd
--- /dev/null
+++ b/envlist.c
@@ -0,0 +1,246 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "qemu-queue.h"
+#include "envlist.h"
+
+struct envlist_entry {
+ const char *ev_var; /* actual env value */
+ QLIST_ENTRY(envlist_entry) ev_link;
+};
+
+struct envlist {
+ QLIST_HEAD(, envlist_entry) el_entries; /* actual entries */
+ size_t el_count; /* number of entries */
+};
+
+static int envlist_parse(envlist_t *envlist,
+ const char *env, int (*)(envlist_t *, const char *));
+
+/*
+ * Allocates new envlist and returns pointer to that or
+ * NULL in case of error.
+ */
+envlist_t *
+envlist_create(void)
+{
+ envlist_t *envlist;
+
+ if ((envlist = malloc(sizeof (*envlist))) == NULL)
+ return (NULL);
+
+ QLIST_INIT(&envlist->el_entries);
+ envlist->el_count = 0;
+
+ return (envlist);
+}
+
+/*
+ * Releases given envlist and its entries.
+ */
+void
+envlist_free(envlist_t *envlist)
+{
+ struct envlist_entry *entry;
+
+ assert(envlist != NULL);
+
+ while (envlist->el_entries.lh_first != NULL) {
+ entry = envlist->el_entries.lh_first;
+ QLIST_REMOVE(entry, ev_link);
+
+ free((char *)entry->ev_var);
+ free(entry);
+ }
+ free(envlist);
+}
+
+/*
+ * Parses comma separated list of set/modify environment
+ * variable entries and updates given enlist accordingly.
+ *
+ * For example:
+ * envlist_parse(el, "HOME=foo,SHELL=/bin/sh");
+ *
+ * inserts/sets environment variables HOME and SHELL.
+ *
+ * Returns 0 on success, errno otherwise.
+ */
+int
+envlist_parse_set(envlist_t *envlist, const char *env)
+{
+ return (envlist_parse(envlist, env, &envlist_setenv));
+}
+
+/*
+ * Parses comma separated list of unset environment variable
+ * entries and removes given variables from given envlist.
+ *
+ * Returns 0 on success, errno otherwise.
+ */
+int
+envlist_parse_unset(envlist_t *envlist, const char *env)
+{
+ return (envlist_parse(envlist, env, &envlist_unsetenv));
+}
+
+/*
+ * Parses comma separated list of set, modify or unset entries
+ * and calls given callback for each entry.
+ *
+ * Returns 0 in case of success, errno otherwise.
+ */
+static int
+envlist_parse(envlist_t *envlist, const char *env,
+ int (*callback)(envlist_t *, const char *))
+{
+ char *tmpenv, *envvar;
+ char *envsave = NULL;
+
+ assert(callback != NULL);
+
+ if ((envlist == NULL) || (env == NULL))
+ return (EINVAL);
+
+ /*
+ * We need to make temporary copy of the env string
+ * as strtok_r(3) modifies it while it tokenizes.
+ */
+ if ((tmpenv = strdup(env)) == NULL)
+ return (errno);
+
+ envvar = strtok_r(tmpenv, ",", &envsave);
+ while (envvar != NULL) {
+ if ((*callback)(envlist, envvar) != 0) {
+ free(tmpenv);
+ return (errno);
+ }
+ envvar = strtok_r(NULL, ",", &envsave);
+ }
+
+ free(tmpenv);
+ return (0);
+}
+
+/*
+ * Sets environment value to envlist in similar manner
+ * than putenv(3).
+ *
+ * Returns 0 in success, errno otherwise.
+ */
+int
+envlist_setenv(envlist_t *envlist, const char *env)
+{
+ struct envlist_entry *entry = NULL;
+ const char *eq_sign;
+ size_t envname_len;
+
+ if ((envlist == NULL) || (env == NULL))
+ return (EINVAL);
+
+ /* find out first equals sign in given env */
+ if ((eq_sign = strchr(env, '=')) == NULL)
+ return (EINVAL);
+ envname_len = eq_sign - env + 1;
+
+ /*
+ * If there already exists variable with given name
+ * we remove and release it before allocating a whole
+ * new entry.
+ */
+ for (entry = envlist->el_entries.lh_first; entry != NULL;
+ entry = entry->ev_link.le_next) {
+ if (strncmp(entry->ev_var, env, envname_len) == 0)
+ break;
+ }
+
+ if (entry != NULL) {
+ QLIST_REMOVE(entry, ev_link);
+ free((char *)entry->ev_var);
+ free(entry);
+ } else {
+ envlist->el_count++;
+ }
+
+ if ((entry = malloc(sizeof (*entry))) == NULL)
+ return (errno);
+ if ((entry->ev_var = strdup(env)) == NULL) {
+ free(entry);
+ return (errno);
+ }
+ QLIST_INSERT_HEAD(&envlist->el_entries, entry, ev_link);
+
+ return (0);
+}
+
+/*
+ * Removes given env value from envlist in similar manner
+ * than unsetenv(3). Returns 0 in success, errno otherwise.
+ */
+int
+envlist_unsetenv(envlist_t *envlist, const char *env)
+{
+ struct envlist_entry *entry;
+ size_t envname_len;
+
+ if ((envlist == NULL) || (env == NULL))
+ return (EINVAL);
+
+ /* env is not allowed to contain '=' */
+ if (strchr(env, '=') != NULL)
+ return (EINVAL);
+
+ /*
+ * Find out the requested entry and remove
+ * it from the list.
+ */
+ envname_len = strlen(env);
+ for (entry = envlist->el_entries.lh_first; entry != NULL;
+ entry = entry->ev_link.le_next) {
+ if (strncmp(entry->ev_var, env, envname_len) == 0)
+ break;
+ }
+ if (entry != NULL) {
+ QLIST_REMOVE(entry, ev_link);
+ free((char *)entry->ev_var);
+ free(entry);
+
+ envlist->el_count--;
+ }
+ return (0);
+}
+
+/*
+ * Returns given envlist as array of strings (in same form that
+ * global variable environ is). Caller must free returned memory
+ * by calling free(3) for each element and for the array. Returned
+ * array and given envlist are not related (no common references).
+ *
+ * If caller provides count pointer, number of items in array is
+ * stored there. In case of error, NULL is returned and no memory
+ * is allocated.
+ */
+char **
+envlist_to_environ(const envlist_t *envlist, size_t *count)
+{
+ struct envlist_entry *entry;
+ char **env, **penv;
+
+ penv = env = malloc((envlist->el_count + 1) * sizeof (char *));
+ if (env == NULL)
+ return (NULL);
+
+ for (entry = envlist->el_entries.lh_first; entry != NULL;
+ entry = entry->ev_link.le_next) {
+ *(penv++) = strdup(entry->ev_var);
+ }
+ *penv = NULL; /* NULL terminate the list */
+
+ if (count != NULL)
+ *count = envlist->el_count;
+
+ return (env);
+}
diff --git a/envlist.h b/envlist.h
new file mode 100644
index 0000000..b9addcc
--- /dev/null
+++ b/envlist.h
@@ -0,0 +1,22 @@
+#ifndef ENVLIST_H
+#define ENVLIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct envlist envlist_t;
+
+envlist_t *envlist_create(void);
+void envlist_free(envlist_t *);
+int envlist_setenv(envlist_t *, const char *);
+int envlist_unsetenv(envlist_t *, const char *);
+int envlist_parse_set(envlist_t *, const char *);
+int envlist_parse_unset(envlist_t *, const char *);
+char **envlist_to_environ(const envlist_t *, size_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ENVLIST_H */
diff --git a/etc/qemu/target-x86_64.conf b/etc/qemu/target-x86_64.conf
new file mode 100644
index 0000000..43ad282
--- /dev/null
+++ b/etc/qemu/target-x86_64.conf
@@ -0,0 +1,86 @@
+# x86 CPU MODELS
+
+[cpudef]
+ name = "Conroe"
+ level = "2"
+ vendor = "GenuineIntel"
+ family = "6"
+ model = "2"
+ stepping = "3"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 ssse3"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+ extfeature_ecx = "lahf_lm"
+ xlevel = "0x8000000A"
+ model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)"
+
+[cpudef]
+ name = "Penryn"
+ level = "2"
+ vendor = "GenuineIntel"
+ family = "6"
+ model = "2"
+ stepping = "3"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16 ssse3 sse4.1"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+ extfeature_ecx = "lahf_lm"
+ xlevel = "0x8000000A"
+ model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)"
+
+[cpudef]
+ name = "Nehalem"
+ level = "2"
+ vendor = "GenuineIntel"
+ family = "6"
+ model = "2"
+ stepping = "3"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16 ssse3 sse4.1 sse4.2 popcnt"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+ extfeature_ecx = "lahf_lm"
+ xlevel = "0x8000000A"
+ model_id = "Intel Core i7 9xx (Nehalem Class Core i7)"
+
+[cpudef]
+ name = "Opteron_G1"
+ level = "5"
+ vendor = "AuthenticAMD"
+ family = "15"
+ model = "6"
+ stepping = "1"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+# extfeature_ecx = ""
+ xlevel = "0x80000008"
+ model_id = "AMD Opteron 240 (Gen 1 Class Opteron)"
+
+[cpudef]
+ name = "Opteron_G2"
+ level = "5"
+ vendor = "AuthenticAMD"
+ family = "15"
+ model = "6"
+ stepping = "1"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp"
+ extfeature_ecx = "svm lahf_lm"
+ xlevel = "0x80000008"
+ model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)"
+
+[cpudef]
+ name = "Opteron_G3"
+ level = "5"
+ vendor = "AuthenticAMD"
+ family = "15"
+ model = "6"
+ stepping = "1"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16 monitor popcnt"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp"
+ extfeature_ecx = "svm sse4a abm misalignsse lahf_lm"
+ xlevel = "0x80000008"
+ model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)"
+
diff --git a/exec-all.h b/exec-all.h
new file mode 100644
index 0000000..e3a82bc
--- /dev/null
+++ b/exec-all.h
@@ -0,0 +1,349 @@
+/*
+ * internal execution defines for qemu
+ *
+ * Copyright (c) 2003 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/>.
+ */
+
+#ifndef _EXEC_ALL_H_
+#define _EXEC_ALL_H_
+
+#include "qemu-common.h"
+
+/* allow to see translation results - the slowdown should be negligible, so we leave it */
+#define DEBUG_DISAS
+
+/* Page tracking code uses ram addresses in system mode, and virtual
+ addresses in userspace mode. Define tb_page_addr_t to be an appropriate
+ type. */
+#if defined(CONFIG_USER_ONLY)
+typedef abi_ulong tb_page_addr_t;
+#else
+typedef ram_addr_t tb_page_addr_t;
+#endif
+
+/* is_jmp field values */
+#define DISAS_NEXT 0 /* next instruction can be analyzed */
+#define DISAS_JUMP 1 /* only pc was modified dynamically */
+#define DISAS_UPDATE 2 /* cpu state was modified dynamically */
+#define DISAS_TB_JUMP 3 /* only pc was modified statically */
+
+typedef struct TranslationBlock TranslationBlock;
+
+/* XXX: make safe guess about sizes */
+#define MAX_OP_PER_INSTR 96
+
+#if HOST_LONG_BITS == 32
+#define MAX_OPC_PARAM_PER_ARG 2
+#else
+#define MAX_OPC_PARAM_PER_ARG 1
+#endif
+#define MAX_OPC_PARAM_IARGS 4
+#define MAX_OPC_PARAM_OARGS 1
+#define MAX_OPC_PARAM_ARGS (MAX_OPC_PARAM_IARGS + MAX_OPC_PARAM_OARGS)
+
+/* A Call op needs up to 4 + 2N parameters on 32-bit archs,
+ * and up to 4 + N parameters on 64-bit archs
+ * (N = number of input arguments + output arguments). */
+#define MAX_OPC_PARAM (4 + (MAX_OPC_PARAM_PER_ARG * MAX_OPC_PARAM_ARGS))
+#define OPC_BUF_SIZE 640
+#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR)
+
+/* Maximum size a TCG op can expand to. This is complicated because a
+ single op may require several host instructions and register reloads.
+ For now take a wild guess at 192 bytes, which should allow at least
+ a couple of fixup instructions per argument. */
+#define TCG_MAX_OP_SIZE 192
+
+#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * MAX_OPC_PARAM)
+
+extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
+extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+extern uint16_t gen_opc_icount[OPC_BUF_SIZE];
+
+#include "qemu-log.h"
+
+void gen_intermediate_code(CPUState *env, struct TranslationBlock *tb);
+void gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb);
+void gen_pc_load(CPUState *env, struct TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc);
+
+void cpu_gen_init(void);
+int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
+ int *gen_code_size_ptr);
+int cpu_restore_state(struct TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc);
+void cpu_resume_from_signal(CPUState *env1, void *puc);
+void cpu_io_recompile(CPUState *env, void *retaddr);
+TranslationBlock *tb_gen_code(CPUState *env,
+ target_ulong pc, target_ulong cs_base, int flags,
+ int cflags);
+void cpu_exec_init(CPUState *env);
+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)
+void tlb_set_page(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int mmu_idx, target_ulong size);
+#endif
+
+#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
+
+#define CODE_GEN_PHYS_HASH_BITS 15
+#define CODE_GEN_PHYS_HASH_SIZE (1 << CODE_GEN_PHYS_HASH_BITS)
+
+#define MIN_CODE_GEN_BUFFER_SIZE (1024 * 1024)
+
+/* estimated block size for TB allocation */
+/* XXX: use a per code average code fragment size and modulate it
+ according to the host CPU */
+#if defined(CONFIG_SOFTMMU)
+#define CODE_GEN_AVG_BLOCK_SIZE 128
+#else
+#define CODE_GEN_AVG_BLOCK_SIZE 64
+#endif
+
+#if defined(_ARCH_PPC) || defined(__x86_64__) || defined(__arm__) || defined(__i386__)
+#define USE_DIRECT_JUMP
+#endif
+
+struct TranslationBlock {
+ target_ulong pc; /* simulated PC corresponding to this block (EIP + CS base) */
+ target_ulong cs_base; /* CS base for this block */
+ uint64_t flags; /* flags defining in which context the code was generated */
+ uint16_t size; /* size of target code for this block (1 <=
+ size <= TARGET_PAGE_SIZE) */
+ uint16_t cflags; /* compile flags */
+#define CF_COUNT_MASK 0x7fff
+#define CF_LAST_IO 0x8000 /* Last insn may be an IO access. */
+
+ uint8_t *tc_ptr; /* pointer to the translated code */
+ /* next matching tb for physical address. */
+ struct TranslationBlock *phys_hash_next;
+ /* first and second physical page containing code. The lower bit
+ of the pointer tells the index in page_next[] */
+ struct TranslationBlock *page_next[2];
+ tb_page_addr_t page_addr[2];
+
+ /* the following data are used to directly call another TB from
+ the code of this one. */
+ uint16_t tb_next_offset[2]; /* offset of original jump target */
+#ifdef USE_DIRECT_JUMP
+ uint16_t tb_jmp_offset[2]; /* offset of jump instruction */
+#else
+ unsigned long tb_next[2]; /* address of jump generated code */
+#endif
+ /* list of TBs jumping to this one. This is a circular list using
+ the two least significant bits of the pointers to tell what is
+ the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
+ jmp_first */
+ struct TranslationBlock *jmp_next[2];
+ struct TranslationBlock *jmp_first;
+ uint32_t icount;
+};
+
+static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
+{
+ target_ulong tmp;
+ tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
+ return (tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK;
+}
+
+static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
+{
+ target_ulong tmp;
+ tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
+ return (((tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK)
+ | (tmp & TB_JMP_ADDR_MASK));
+}
+
+static inline unsigned int tb_phys_hash_func(tb_page_addr_t pc)
+{
+ return (pc >> 2) & (CODE_GEN_PHYS_HASH_SIZE - 1);
+}
+
+TranslationBlock *tb_alloc(target_ulong pc);
+void tb_free(TranslationBlock *tb);
+void tb_flush(CPUState *env);
+void tb_link_page(TranslationBlock *tb,
+ tb_page_addr_t phys_pc, tb_page_addr_t phys_page2);
+void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
+
+extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
+
+#if defined(USE_DIRECT_JUMP)
+
+#if defined(_ARCH_PPC)
+void ppc_tb_set_jmp_target(unsigned long jmp_addr, unsigned long addr);
+#define tb_set_jmp_target1 ppc_tb_set_jmp_target
+#elif defined(__i386__) || defined(__x86_64__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+ /* patch the branch destination */
+ *(uint32_t *)jmp_addr = addr - (jmp_addr + 4);
+ /* no need to flush icache explicitly */
+}
+#elif defined(__arm__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+#if !QEMU_GNUC_PREREQ(4, 1)
+ register unsigned long _beg __asm ("a1");
+ register unsigned long _end __asm ("a2");
+ register unsigned long _flg __asm ("a3");
+#endif
+
+ /* we could use a ldr pc, [pc, #-4] kind of branch and avoid the flush */
+ *(uint32_t *)jmp_addr =
+ (*(uint32_t *)jmp_addr & ~0xffffff)
+ | (((addr - (jmp_addr + 8)) >> 2) & 0xffffff);
+
+#if QEMU_GNUC_PREREQ(4, 1)
+ __builtin___clear_cache((char *) jmp_addr, (char *) jmp_addr + 4);
+#else
+ /* flush icache */
+ _beg = jmp_addr;
+ _end = jmp_addr + 4;
+ _flg = 0;
+ __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
+#endif
+}
+#endif
+
+static inline void tb_set_jmp_target(TranslationBlock *tb,
+ int n, unsigned long addr)
+{
+ unsigned long offset;
+
+ offset = tb->tb_jmp_offset[n];
+ tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
+}
+
+#else
+
+/* set the jump target */
+static inline void tb_set_jmp_target(TranslationBlock *tb,
+ int n, unsigned long addr)
+{
+ tb->tb_next[n] = addr;
+}
+
+#endif
+
+static inline void tb_add_jump(TranslationBlock *tb, int n,
+ TranslationBlock *tb_next)
+{
+ /* NOTE: this test is only needed for thread safety */
+ if (!tb->jmp_next[n]) {
+ /* patch the native jump address */
+ tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);
+
+ /* add in TB jmp circular list */
+ tb->jmp_next[n] = tb_next->jmp_first;
+ tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
+ }
+}
+
+TranslationBlock *tb_find_pc(unsigned long pc_ptr);
+
+#include "qemu-lock.h"
+
+extern spinlock_t tb_lock;
+
+extern int tb_invalidated_flag;
+
+#if !defined(CONFIG_USER_ONLY)
+
+extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
+extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+
+void tlb_fill(target_ulong addr, int is_write, int mmu_idx,
+ void *retaddr);
+
+#include "softmmu_defs.h"
+
+#define ACCESS_TYPE (NB_MMU_MODES + 1)
+#define MEMSUFFIX _code
+#define env cpu_single_env
+
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#undef env
+
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong addr)
+{
+ return addr;
+}
+#else
+/* NOTE: this function can trigger an exception */
+/* NOTE2: the returned address is not exactly the physical address: it
+ is the offset relative to phys_ram_base */
+static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong addr)
+{
+ int mmu_idx, page_index, pd;
+ void *p;
+
+ page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ mmu_idx = cpu_mmu_index(env1);
+ if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code !=
+ (addr & TARGET_PAGE_MASK))) {
+ ldub_code(addr);
+ }
+ pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK;
+ if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+#if defined(TARGET_SPARC) || defined(TARGET_MIPS)
+ do_unassigned_access(addr, 0, 1, 0, 4);
+#else
+ cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);
+#endif
+ }
+ p = (void *)(unsigned long)addr
+ + env1->tlb_table[mmu_idx][page_index].addend;
+ return qemu_ram_addr_from_host_nofail(p);
+}
+#endif
+
+typedef void (CPUDebugExcpHandler)(CPUState *env);
+
+CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler);
+
+/* vl.c */
+extern int singlestep;
+
+/* cpu-exec.c */
+extern volatile sig_atomic_t exit_request;
+
+#endif
diff --git a/exec.c b/exec.c
new file mode 100644
index 0000000..9f748bc
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,4356 @@
+/*
+ * virtual page mapping and translated block handling
+ *
+ * Copyright (c) 2003 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"
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "cache-utils.h"
+
+#if !defined(TARGET_IA64)
+#include "tcg.h"
+#endif
+#include "qemu-kvm.h"
+
+#include "hw/hw.h"
+#include "hw/qdev.h"
+#include "osdep.h"
+#include "kvm.h"
+#include "qemu-timer.h"
+#if defined(CONFIG_USER_ONLY)
+#include <qemu.h>
+#include <signal.h>
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/param.h>
+#if __FreeBSD_version >= 700104
+#define HAVE_KINFO_GETVMMAP
+#define sigqueue sigqueue_freebsd /* avoid redefinition */
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <machine/profile.h>
+#define _KERNEL
+#include <sys/user.h>
+#undef _KERNEL
+#undef sigqueue
+#include <libutil.h>
+#endif
+#endif
+#endif
+
+//#define DEBUG_TB_INVALIDATE
+//#define DEBUG_FLUSH
+//#define DEBUG_TLB
+//#define DEBUG_UNASSIGNED
+
+/* make various TB consistency checks */
+//#define DEBUG_TB_CHECK
+//#define DEBUG_TLB_CHECK
+
+//#define DEBUG_IOPORT
+//#define DEBUG_SUBPAGE
+
+#if !defined(CONFIG_USER_ONLY)
+/* TB consistency checks only implemented for usermode emulation. */
+#undef DEBUG_TB_CHECK
+#endif
+
+#define SMC_BITMAP_USE_THRESHOLD 10
+
+static TranslationBlock *tbs;
+static int code_gen_max_blocks;
+TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
+static int nb_tbs;
+/* any access to the tbs or the page table must use this lock */
+spinlock_t tb_lock = SPIN_LOCK_UNLOCKED;
+
+#if defined(__arm__) || defined(__sparc_v9__)
+/* The prologue must be reachable with a direct jump. ARM and Sparc64
+ have limited branch ranges (possibly also PPC) so place it in a
+ section close to code segment. */
+#define code_gen_section \
+ __attribute__((__section__(".gen_code"))) \
+ __attribute__((aligned (32)))
+#elif defined(_WIN32)
+/* Maximum alignment for Win32 is 16. */
+#define code_gen_section \
+ __attribute__((aligned (16)))
+#else
+#define code_gen_section \
+ __attribute__((aligned (32)))
+#endif
+
+uint8_t code_gen_prologue[1024] code_gen_section;
+static uint8_t *code_gen_buffer;
+static unsigned long code_gen_buffer_size;
+/* threshold to flush the translated code buffer */
+static unsigned long code_gen_buffer_max_size;
+static uint8_t *code_gen_ptr;
+
+#if !defined(CONFIG_USER_ONLY)
+int phys_ram_fd;
+static int in_migration;
+
+RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list) };
+#endif
+
+CPUState *first_cpu;
+/* current CPU in the current thread. It is only valid inside
+ cpu_exec() */
+CPUState *cpu_single_env;
+/* 0 = Do not count executed instructions.
+ 1 = Precise instruction counting.
+ 2 = Adaptive rate instruction counting. */
+int use_icount = 0;
+/* Current instruction counter. While executing translated code this may
+ include some instructions that have not yet been executed. */
+int64_t qemu_icount;
+
+typedef struct PageDesc {
+ /* list of TBs intersecting this ram page */
+ TranslationBlock *first_tb;
+ /* in order to optimize self modifying code, we count the number
+ of lookups we do to a given page to use a bitmap */
+ unsigned int code_write_count;
+ uint8_t *code_bitmap;
+#if defined(CONFIG_USER_ONLY)
+ unsigned long flags;
+#endif
+} PageDesc;
+
+/* In system mode we want L1_MAP to be based on ram offsets,
+ while in user mode we want it to be based on virtual addresses. */
+#if !defined(CONFIG_USER_ONLY)
+#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS
+# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS
+#else
+# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS
+#endif
+#else
+# define L1_MAP_ADDR_SPACE_BITS TARGET_VIRT_ADDR_SPACE_BITS
+#endif
+
+/* Size of the L2 (and L3, etc) page tables. */
+#define L2_BITS 10
+#define L2_SIZE (1 << L2_BITS)
+
+/* The bits remaining after N lower levels of page tables. */
+#define P_L1_BITS_REM \
+ ((TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS)
+#define V_L1_BITS_REM \
+ ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS)
+
+/* Size of the L1 page table. Avoid silly small sizes. */
+#if P_L1_BITS_REM < 4
+#define P_L1_BITS (P_L1_BITS_REM + L2_BITS)
+#else
+#define P_L1_BITS P_L1_BITS_REM
+#endif
+
+#if V_L1_BITS_REM < 4
+#define V_L1_BITS (V_L1_BITS_REM + L2_BITS)
+#else
+#define V_L1_BITS V_L1_BITS_REM
+#endif
+
+#define P_L1_SIZE ((target_phys_addr_t)1 << P_L1_BITS)
+#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS)
+
+#define P_L1_SHIFT (TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS - P_L1_BITS)
+#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS)
+
+unsigned long qemu_real_host_page_size;
+unsigned long qemu_host_page_bits;
+unsigned long qemu_host_page_size;
+unsigned long qemu_host_page_mask;
+
+/* This is a multi-level map on the virtual address space.
+ The bottom level has pointers to PageDesc. */
+static void *l1_map[V_L1_SIZE];
+
+#if !defined(CONFIG_USER_ONLY)
+typedef struct PhysPageDesc {
+ /* offset in host memory of the page + io_index in the low bits */
+ ram_addr_t phys_offset;
+ ram_addr_t region_offset;
+} PhysPageDesc;
+
+/* This is a multi-level map on the physical address space.
+ The bottom level has pointers to PhysPageDesc. */
+static void *l1_phys_map[P_L1_SIZE];
+
+static void io_mem_init(void);
+
+/* io memory support */
+CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
+CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+static char io_mem_used[IO_MEM_NB_ENTRIES];
+static int io_mem_watch;
+#endif
+
+/* log support */
+#ifdef WIN32
+static const char *logfilename = "qemu.log";
+#else
+static const char *logfilename = "/tmp/qemu.log";
+#endif
+FILE *logfile;
+int loglevel;
+static int log_append = 0;
+
+/* statistics */
+#if !defined(CONFIG_USER_ONLY)
+static int tlb_flush_count;
+#endif
+static int tb_flush_count;
+static int tb_phys_invalidate_count;
+
+#ifdef _WIN32
+static void map_exec(void *addr, long size)
+{
+ DWORD old_protect;
+ VirtualProtect(addr, size,
+ PAGE_EXECUTE_READWRITE, &old_protect);
+
+}
+#else
+static void map_exec(void *addr, long size)
+{
+ unsigned long start, end, page_size;
+
+ page_size = getpagesize();
+ start = (unsigned long)addr;
+ start &= ~(page_size - 1);
+
+ end = (unsigned long)addr + size;
+ end += page_size - 1;
+ end &= ~(page_size - 1);
+
+ mprotect((void *)start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC);
+}
+#endif
+
+static void page_init(void)
+{
+ /* NOTE: we can always suppose that qemu_host_page_size >=
+ TARGET_PAGE_SIZE */
+#ifdef _WIN32
+ {
+ SYSTEM_INFO system_info;
+
+ GetSystemInfo(&system_info);
+ qemu_real_host_page_size = system_info.dwPageSize;
+ }
+#else
+ qemu_real_host_page_size = getpagesize();
+#endif
+ if (qemu_host_page_size == 0)
+ qemu_host_page_size = qemu_real_host_page_size;
+ if (qemu_host_page_size < TARGET_PAGE_SIZE)
+ qemu_host_page_size = TARGET_PAGE_SIZE;
+ qemu_host_page_bits = 0;
+ while ((1 << qemu_host_page_bits) < qemu_host_page_size)
+ qemu_host_page_bits++;
+ qemu_host_page_mask = ~(qemu_host_page_size - 1);
+
+#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY)
+ {
+#ifdef HAVE_KINFO_GETVMMAP
+ struct kinfo_vmentry *freep;
+ int i, cnt;
+
+ freep = kinfo_getvmmap(getpid(), &cnt);
+ if (freep) {
+ mmap_lock();
+ for (i = 0; i < cnt; i++) {
+ unsigned long startaddr, endaddr;
+
+ startaddr = freep[i].kve_start;
+ endaddr = freep[i].kve_end;
+ if (h2g_valid(startaddr)) {
+ startaddr = h2g(startaddr) & TARGET_PAGE_MASK;
+
+ if (h2g_valid(endaddr)) {
+ endaddr = h2g(endaddr);
+ page_set_flags(startaddr, endaddr, PAGE_RESERVED);
+ } else {
+#if TARGET_ABI_BITS <= L1_MAP_ADDR_SPACE_BITS
+ endaddr = ~0ul;
+ page_set_flags(startaddr, endaddr, PAGE_RESERVED);
+#endif
+ }
+ }
+ }
+ free(freep);
+ mmap_unlock();
+ }
+#else
+ FILE *f;
+
+ last_brk = (unsigned long)sbrk(0);
+
+ f = fopen("/compat/linux/proc/self/maps", "r");
+ if (f) {
+ mmap_lock();
+
+ do {
+ unsigned long startaddr, endaddr;
+ int n;
+
+ n = fscanf (f, "%lx-%lx %*[^\n]\n", &startaddr, &endaddr);
+
+ if (n == 2 && h2g_valid(startaddr)) {
+ startaddr = h2g(startaddr) & TARGET_PAGE_MASK;
+
+ if (h2g_valid(endaddr)) {
+ endaddr = h2g(endaddr);
+ } else {
+ endaddr = ~0ul;
+ }
+ page_set_flags(startaddr, endaddr, PAGE_RESERVED);
+ }
+ } while (!feof(f));
+
+ fclose(f);
+ mmap_unlock();
+ }
+#endif
+ }
+#endif
+}
+
+static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc)
+{
+ PageDesc *pd;
+ void **lp;
+ int i;
+
+#if defined(CONFIG_USER_ONLY)
+ /* We can't use qemu_malloc because it may recurse into a locked mutex. */
+# define ALLOC(P, SIZE) \
+ do { \
+ P = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, \
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \
+ } while (0)
+#else
+# define ALLOC(P, SIZE) \
+ do { P = qemu_mallocz(SIZE); } while (0)
+#endif
+
+ /* Level 1. Always allocated. */
+ lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1));
+
+ /* Level 2..N-1. */
+ for (i = V_L1_SHIFT / L2_BITS - 1; i > 0; i--) {
+ void **p = *lp;
+
+ if (p == NULL) {
+ if (!alloc) {
+ return NULL;
+ }
+ ALLOC(p, sizeof(void *) * L2_SIZE);
+ *lp = p;
+ }
+
+ lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1));
+ }
+
+ pd = *lp;
+ if (pd == NULL) {
+ if (!alloc) {
+ return NULL;
+ }
+ ALLOC(pd, sizeof(PageDesc) * L2_SIZE);
+ *lp = pd;
+ }
+
+#undef ALLOC
+
+ return pd + (index & (L2_SIZE - 1));
+}
+
+static inline PageDesc *page_find(tb_page_addr_t index)
+{
+ return page_find_alloc(index, 0);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc)
+{
+ PhysPageDesc *pd;
+ void **lp;
+ int i;
+
+ /* Level 1. Always allocated. */
+ lp = l1_phys_map + ((index >> P_L1_SHIFT) & (P_L1_SIZE - 1));
+
+ /* Level 2..N-1. */
+ for (i = P_L1_SHIFT / L2_BITS - 1; i > 0; i--) {
+ void **p = *lp;
+ if (p == NULL) {
+ if (!alloc) {
+ return NULL;
+ }
+ *lp = p = qemu_mallocz(sizeof(void *) * L2_SIZE);
+ }
+ lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1));
+ }
+
+ pd = *lp;
+ if (pd == NULL) {
+ int i;
+
+ if (!alloc) {
+ return NULL;
+ }
+
+ *lp = pd = qemu_malloc(sizeof(PhysPageDesc) * L2_SIZE);
+
+ for (i = 0; i < L2_SIZE; i++) {
+ pd[i].phys_offset = IO_MEM_UNASSIGNED;
+ pd[i].region_offset = (index + i) << TARGET_PAGE_BITS;
+ }
+ }
+
+ return pd + (index & (L2_SIZE - 1));
+}
+
+static inline PhysPageDesc *phys_page_find(target_phys_addr_t index)
+{
+ return phys_page_find_alloc(index, 0);
+}
+
+static void tlb_protect_code(ram_addr_t ram_addr);
+static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr,
+ target_ulong vaddr);
+#define mmap_lock() do { } while(0)
+#define mmap_unlock() do { } while(0)
+#endif
+
+#define DEFAULT_CODE_GEN_BUFFER_SIZE (32 * 1024 * 1024)
+
+#if defined(CONFIG_USER_ONLY)
+/* Currently it is not recommended to allocate big chunks of data in
+ user mode. It will change when a dedicated libc will be used */
+#define USE_STATIC_CODE_GEN_BUFFER
+#endif
+
+#ifdef USE_STATIC_CODE_GEN_BUFFER
+static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]
+ __attribute__((aligned (CODE_GEN_ALIGN)));
+#endif
+
+static void code_gen_alloc(unsigned long tb_size)
+{
+ if (kvm_enabled())
+ return;
+
+#ifdef USE_STATIC_CODE_GEN_BUFFER
+ code_gen_buffer = static_code_gen_buffer;
+ code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
+ map_exec(code_gen_buffer, code_gen_buffer_size);
+#else
+ code_gen_buffer_size = tb_size;
+ if (code_gen_buffer_size == 0) {
+#if defined(CONFIG_USER_ONLY)
+ /* in user mode, phys_ram_size is not meaningful */
+ code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
+#else
+ /* XXX: needs adjustments */
+ code_gen_buffer_size = (unsigned long)(ram_size / 4);
+#endif
+ }
+ if (code_gen_buffer_size < MIN_CODE_GEN_BUFFER_SIZE)
+ code_gen_buffer_size = MIN_CODE_GEN_BUFFER_SIZE;
+ /* The code gen buffer location may have constraints depending on
+ the host cpu and OS */
+#if defined(__linux__)
+ {
+ int flags;
+ void *start = NULL;
+
+ flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#if defined(__x86_64__)
+ flags |= MAP_32BIT;
+ /* Cannot map more than that */
+ if (code_gen_buffer_size > (800 * 1024 * 1024))
+ code_gen_buffer_size = (800 * 1024 * 1024);
+#elif defined(__sparc_v9__)
+ // Map the buffer below 2G, so we can use direct calls and branches
+ flags |= MAP_FIXED;
+ start = (void *) 0x60000000UL;
+ if (code_gen_buffer_size > (512 * 1024 * 1024))
+ code_gen_buffer_size = (512 * 1024 * 1024);
+#elif defined(__arm__)
+ /* Map the buffer below 32M, so we can use direct calls and branches */
+ flags |= MAP_FIXED;
+ start = (void *) 0x01000000UL;
+ if (code_gen_buffer_size > 16 * 1024 * 1024)
+ code_gen_buffer_size = 16 * 1024 * 1024;
+#elif defined(__s390x__)
+ /* Map the buffer so that we can use direct calls and branches. */
+ /* We have a +- 4GB range on the branches; leave some slop. */
+ if (code_gen_buffer_size > (3ul * 1024 * 1024 * 1024)) {
+ code_gen_buffer_size = 3ul * 1024 * 1024 * 1024;
+ }
+ start = (void *)0x90000000UL;
+#endif
+ code_gen_buffer = mmap(start, code_gen_buffer_size,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ flags, -1, 0);
+ if (code_gen_buffer == MAP_FAILED) {
+ fprintf(stderr, "Could not allocate dynamic translator buffer\n");
+ exit(1);
+ }
+ }
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
+ || defined(__DragonFly__) || defined(__OpenBSD__)
+ {
+ int flags;
+ void *addr = NULL;
+ flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#if defined(__x86_64__)
+ /* FreeBSD doesn't have MAP_32BIT, use MAP_FIXED and assume
+ * 0x40000000 is free */
+ flags |= MAP_FIXED;
+ addr = (void *)0x40000000;
+ /* Cannot map more than that */
+ if (code_gen_buffer_size > (800 * 1024 * 1024))
+ code_gen_buffer_size = (800 * 1024 * 1024);
+#elif defined(__sparc_v9__)
+ // Map the buffer below 2G, so we can use direct calls and branches
+ flags |= MAP_FIXED;
+ addr = (void *) 0x60000000UL;
+ if (code_gen_buffer_size > (512 * 1024 * 1024)) {
+ code_gen_buffer_size = (512 * 1024 * 1024);
+ }
+#endif
+ code_gen_buffer = mmap(addr, code_gen_buffer_size,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ flags, -1, 0);
+ if (code_gen_buffer == MAP_FAILED) {
+ fprintf(stderr, "Could not allocate dynamic translator buffer\n");
+ exit(1);
+ }
+ }
+#else
+ code_gen_buffer = qemu_malloc(code_gen_buffer_size);
+ map_exec(code_gen_buffer, code_gen_buffer_size);
+#endif
+#endif /* !USE_STATIC_CODE_GEN_BUFFER */
+ map_exec(code_gen_prologue, sizeof(code_gen_prologue));
+ code_gen_buffer_max_size = code_gen_buffer_size -
+ (TCG_MAX_OP_SIZE * OPC_MAX_SIZE);
+ code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE;
+ tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock));
+}
+
+/* Must be called before using the QEMU cpus. 'tb_size' is the size
+ (in bytes) allocated to the translation buffer. Zero means default
+ size. */
+void cpu_exec_init_all(unsigned long tb_size)
+{
+ cpu_gen_init();
+ code_gen_alloc(tb_size);
+ code_gen_ptr = code_gen_buffer;
+ page_init();
+#if !defined(CONFIG_USER_ONLY)
+ io_mem_init();
+#endif
+#if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE)
+ /* There's no guest base to take into account, so go ahead and
+ initialize the prologue now. */
+ tcg_prologue_init(&tcg_ctx);
+#endif
+}
+
+#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
+
+static int cpu_common_post_load(void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+
+ /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the
+ version_id is increased. */
+ env->interrupt_request &= ~0x01;
+ tlb_flush(env, 1);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_cpu_common = {
+ .name = "cpu_common",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = cpu_common_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(halted, CPUState),
+ VMSTATE_UINT32(interrupt_request, CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+#endif
+
+CPUState *qemu_get_cpu(int cpu)
+{
+ CPUState *env = first_cpu;
+
+ while (env) {
+ if (env->cpu_index == cpu)
+ break;
+ env = env->next_cpu;
+ }
+
+ return env;
+}
+
+void cpu_exec_init(CPUState *env)
+{
+ CPUState **penv;
+ int cpu_index;
+
+#if defined(CONFIG_USER_ONLY)
+ cpu_list_lock();
+#endif
+ env->next_cpu = NULL;
+ penv = &first_cpu;
+ cpu_index = 0;
+ while (*penv != NULL) {
+ penv = &(*penv)->next_cpu;
+ cpu_index++;
+ }
+ env->cpu_index = cpu_index;
+ env->numa_node = 0;
+ QTAILQ_INIT(&env->breakpoints);
+ QTAILQ_INIT(&env->watchpoints);
+#ifdef __WIN32
+ env->thread_id = GetCurrentProcessId();
+#else
+ env->thread_id = getpid();
+#endif
+ *penv = env;
+#if defined(CONFIG_USER_ONLY)
+ cpu_list_unlock();
+#endif
+#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
+ vmstate_register(NULL, cpu_index, &vmstate_cpu_common, env);
+ register_savevm(NULL, "cpu", cpu_index, CPU_SAVE_VERSION,
+ cpu_save, cpu_load, env);
+#endif
+}
+
+static inline void invalidate_page_bitmap(PageDesc *p)
+{
+ if (p->code_bitmap) {
+ qemu_free(p->code_bitmap);
+ p->code_bitmap = NULL;
+ }
+ p->code_write_count = 0;
+}
+
+/* Set to NULL all the 'first_tb' fields in all PageDescs. */
+
+static void page_flush_tb_1 (int level, void **lp)
+{
+ int i;
+
+ if (*lp == NULL) {
+ return;
+ }
+ if (level == 0) {
+ PageDesc *pd = *lp;
+ for (i = 0; i < L2_SIZE; ++i) {
+ pd[i].first_tb = NULL;
+ invalidate_page_bitmap(pd + i);
+ }
+ } else {
+ void **pp = *lp;
+ for (i = 0; i < L2_SIZE; ++i) {
+ page_flush_tb_1 (level - 1, pp + i);
+ }
+ }
+}
+
+static void page_flush_tb(void)
+{
+ int i;
+ for (i = 0; i < V_L1_SIZE; i++) {
+ page_flush_tb_1(V_L1_SHIFT / L2_BITS - 1, l1_map + i);
+ }
+}
+
+/* flush all the translation blocks */
+/* XXX: tb_flush is currently not thread safe */
+void tb_flush(CPUState *env1)
+{
+ CPUState *env;
+#if defined(DEBUG_FLUSH)
+ printf("qemu: flush code_size=%ld nb_tbs=%d avg_tb_size=%ld\n",
+ (unsigned long)(code_gen_ptr - code_gen_buffer),
+ nb_tbs, nb_tbs > 0 ?
+ ((unsigned long)(code_gen_ptr - code_gen_buffer)) / nb_tbs : 0);
+#endif
+ if ((unsigned long)(code_gen_ptr - code_gen_buffer) > code_gen_buffer_size)
+ cpu_abort(env1, "Internal error: code buffer overflow\n");
+
+ nb_tbs = 0;
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+ }
+
+ memset (tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *));
+ page_flush_tb();
+
+ code_gen_ptr = code_gen_buffer;
+ /* XXX: flush processor icache at this point if cache flush is
+ expensive */
+ tb_flush_count++;
+}
+
+#ifdef DEBUG_TB_CHECK
+
+static void tb_invalidate_check(target_ulong address)
+{
+ TranslationBlock *tb;
+ int i;
+ address &= TARGET_PAGE_MASK;
+ for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) {
+ for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) {
+ if (!(address + TARGET_PAGE_SIZE <= tb->pc ||
+ address >= tb->pc + tb->size)) {
+ printf("ERROR invalidate: address=" TARGET_FMT_lx
+ " PC=%08lx size=%04x\n",
+ address, (long)tb->pc, tb->size);
+ }
+ }
+ }
+}
+
+/* verify that all the pages have correct rights for code */
+static void tb_page_check(void)
+{
+ TranslationBlock *tb;
+ int i, flags1, flags2;
+
+ for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) {
+ for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) {
+ flags1 = page_get_flags(tb->pc);
+ flags2 = page_get_flags(tb->pc + tb->size - 1);
+ if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) {
+ printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n",
+ (long)tb->pc, tb->size, flags1, flags2);
+ }
+ }
+ }
+}
+
+#endif
+
+/* invalidate one TB */
+static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb,
+ int next_offset)
+{
+ TranslationBlock *tb1;
+ for(;;) {
+ tb1 = *ptb;
+ if (tb1 == tb) {
+ *ptb = *(TranslationBlock **)((char *)tb1 + next_offset);
+ break;
+ }
+ ptb = (TranslationBlock **)((char *)tb1 + next_offset);
+ }
+}
+
+static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb)
+{
+ TranslationBlock *tb1;
+ unsigned int n1;
+
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (tb1 == tb) {
+ *ptb = tb1->page_next[n1];
+ break;
+ }
+ ptb = &tb1->page_next[n1];
+ }
+}
+
+static inline void tb_jmp_remove(TranslationBlock *tb, int n)
+{
+ TranslationBlock *tb1, **ptb;
+ unsigned int n1;
+
+ ptb = &tb->jmp_next[n];
+ tb1 = *ptb;
+ if (tb1) {
+ /* find tb(n) in circular list */
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == n && tb1 == tb)
+ break;
+ if (n1 == 2) {
+ ptb = &tb1->jmp_first;
+ } else {
+ ptb = &tb1->jmp_next[n1];
+ }
+ }
+ /* now we can suppress tb(n) from the list */
+ *ptb = tb->jmp_next[n];
+
+ tb->jmp_next[n] = NULL;
+ }
+}
+
+/* reset the jump entry 'n' of a TB so that it is not chained to
+ another TB */
+static inline void tb_reset_jump(TranslationBlock *tb, int n)
+{
+ tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n]));
+}
+
+void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
+{
+ CPUState *env;
+ PageDesc *p;
+ unsigned int h, n1;
+ tb_page_addr_t phys_pc;
+ TranslationBlock *tb1, *tb2;
+
+ /* remove the TB from the hash list */
+ phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
+ h = tb_phys_hash_func(phys_pc);
+ tb_remove(&tb_phys_hash[h], tb,
+ offsetof(TranslationBlock, phys_hash_next));
+
+ /* remove the TB from the page list */
+ if (tb->page_addr[0] != page_addr) {
+ p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS);
+ tb_page_remove(&p->first_tb, tb);
+ invalidate_page_bitmap(p);
+ }
+ if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) {
+ p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS);
+ tb_page_remove(&p->first_tb, tb);
+ invalidate_page_bitmap(p);
+ }
+
+ tb_invalidated_flag = 1;
+
+ /* remove the TB from the hash list */
+ h = tb_jmp_cache_hash_func(tb->pc);
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->tb_jmp_cache[h] == tb)
+ env->tb_jmp_cache[h] = NULL;
+ }
+
+ /* suppress this TB from the two jump lists */
+ tb_jmp_remove(tb, 0);
+ tb_jmp_remove(tb, 1);
+
+ /* suppress any remaining jumps to this TB */
+ tb1 = tb->jmp_first;
+ for(;;) {
+ n1 = (long)tb1 & 3;
+ if (n1 == 2)
+ break;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ tb2 = tb1->jmp_next[n1];
+ tb_reset_jump(tb1, n1);
+ tb1->jmp_next[n1] = NULL;
+ tb1 = tb2;
+ }
+ tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */
+
+ tb_phys_invalidate_count++;
+}
+
+static inline void set_bits(uint8_t *tab, int start, int len)
+{
+ int end, mask, end1;
+
+ end = start + len;
+ tab += start >> 3;
+ mask = 0xff << (start & 7);
+ if ((start & ~7) == (end & ~7)) {
+ if (start < end) {
+ mask &= ~(0xff << (end & 7));
+ *tab |= mask;
+ }
+ } else {
+ *tab++ |= mask;
+ start = (start + 8) & ~7;
+ end1 = end & ~7;
+ while (start < end1) {
+ *tab++ = 0xff;
+ start += 8;
+ }
+ if (start < end) {
+ mask = ~(0xff << (end & 7));
+ *tab |= mask;
+ }
+ }
+}
+
+static void build_page_bitmap(PageDesc *p)
+{
+ int n, tb_start, tb_end;
+ TranslationBlock *tb;
+
+ p->code_bitmap = qemu_mallocz(TARGET_PAGE_SIZE / 8);
+
+ tb = p->first_tb;
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+ /* NOTE: this is subtle as a TB may span two physical pages */
+ if (n == 0) {
+ /* NOTE: tb_end may be after the end of the page, but
+ it is not a problem */
+ tb_start = tb->pc & ~TARGET_PAGE_MASK;
+ tb_end = tb_start + tb->size;
+ if (tb_end > TARGET_PAGE_SIZE)
+ tb_end = TARGET_PAGE_SIZE;
+ } else {
+ tb_start = 0;
+ tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
+ }
+ set_bits(p->code_bitmap, tb_start, tb_end - tb_start);
+ tb = tb->page_next[n];
+ }
+}
+
+TranslationBlock *tb_gen_code(CPUState *env,
+ target_ulong pc, target_ulong cs_base,
+ int flags, int cflags)
+{
+ TranslationBlock *tb;
+ uint8_t *tc_ptr;
+ tb_page_addr_t phys_pc, phys_page2;
+ target_ulong virt_page2;
+ int code_gen_size;
+
+ phys_pc = get_page_addr_code(env, pc);
+ tb = tb_alloc(pc);
+ if (!tb) {
+ /* flush must be done */
+ tb_flush(env);
+ /* cannot fail at this point */
+ tb = tb_alloc(pc);
+ /* Don't forget to invalidate previous TB info. */
+ tb_invalidated_flag = 1;
+ }
+ tc_ptr = code_gen_ptr;
+ tb->tc_ptr = tc_ptr;
+ tb->cs_base = cs_base;
+ tb->flags = flags;
+ tb->cflags = cflags;
+ cpu_gen_code(env, tb, &code_gen_size);
+ code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
+
+ /* check next page if needed */
+ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
+ phys_page2 = -1;
+ if ((pc & TARGET_PAGE_MASK) != virt_page2) {
+ phys_page2 = get_page_addr_code(env, virt_page2);
+ }
+ tb_link_page(tb, phys_pc, phys_page2);
+ return tb;
+}
+
+/* invalidate all TBs which intersect with the target physical page
+ starting in range [start;end[. NOTE: start and end must refer to
+ the same physical page. 'is_cpu_write_access' should be true if called
+ from a real cpu write access: the virtual CPU will exit the current
+ TB if code is modified inside this TB. */
+void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
+ int is_cpu_write_access)
+{
+ TranslationBlock *tb, *tb_next, *saved_tb;
+ CPUState *env = cpu_single_env;
+ tb_page_addr_t tb_start, tb_end;
+ PageDesc *p;
+ int n;
+#ifdef TARGET_HAS_PRECISE_SMC
+ int current_tb_not_found = is_cpu_write_access;
+ TranslationBlock *current_tb = NULL;
+ int current_tb_modified = 0;
+ target_ulong current_pc = 0;
+ target_ulong current_cs_base = 0;
+ int current_flags = 0;
+#endif /* TARGET_HAS_PRECISE_SMC */
+
+ p = page_find(start >> TARGET_PAGE_BITS);
+ if (!p)
+ return;
+ if (!p->code_bitmap &&
+ ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD &&
+ is_cpu_write_access) {
+ /* build code bitmap */
+ build_page_bitmap(p);
+ }
+
+ /* we remove all the TBs in the range [start, end[ */
+ /* XXX: see if in some cases it could be faster to invalidate all the code */
+ tb = p->first_tb;
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+ tb_next = tb->page_next[n];
+ /* NOTE: this is subtle as a TB may span two physical pages */
+ if (n == 0) {
+ /* NOTE: tb_end may be after the end of the page, but
+ it is not a problem */
+ tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
+ tb_end = tb_start + tb->size;
+ } else {
+ tb_start = tb->page_addr[1];
+ tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
+ }
+ if (!(tb_end <= start || tb_start >= end)) {
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb_not_found) {
+ current_tb_not_found = 0;
+ current_tb = NULL;
+ if (env->mem_io_pc) {
+ /* now we have a real cpu fault */
+ current_tb = tb_find_pc(env->mem_io_pc);
+ }
+ }
+ if (current_tb == tb &&
+ (current_tb->cflags & CF_COUNT_MASK) != 1) {
+ /* If we are modifying the current TB, we must stop
+ its execution. We could be more precise by checking
+ that the modification is after the current PC, but it
+ would require a specialized function to partially
+ restore the CPU state */
+
+ current_tb_modified = 1;
+ cpu_restore_state(current_tb, env,
+ env->mem_io_pc, NULL);
+ cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
+ &current_flags);
+ }
+#endif /* TARGET_HAS_PRECISE_SMC */
+ /* we need to do that to handle the case where a signal
+ occurs while doing tb_phys_invalidate() */
+ saved_tb = NULL;
+ if (env) {
+ saved_tb = env->current_tb;
+ env->current_tb = NULL;
+ }
+ tb_phys_invalidate(tb, -1);
+ if (env) {
+ env->current_tb = saved_tb;
+ if (env->interrupt_request && env->current_tb)
+ cpu_interrupt(env, env->interrupt_request);
+ }
+ }
+ tb = tb_next;
+ }
+#if !defined(CONFIG_USER_ONLY)
+ /* if no code remaining, no need to continue to use slow writes */
+ if (!p->first_tb) {
+ invalidate_page_bitmap(p);
+ if (is_cpu_write_access) {
+ tlb_unprotect_code_phys(env, start, env->mem_io_vaddr);
+ }
+ }
+#endif
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb_modified) {
+ /* we generate a block containing just the instruction
+ modifying the memory. It will ensure that it cannot modify
+ itself */
+ env->current_tb = NULL;
+ tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
+ cpu_resume_from_signal(env, NULL);
+ }
+#endif
+}
+
+/* len must be <= 8 and start must be a multiple of len */
+static inline void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
+{
+ PageDesc *p;
+ int offset, b;
+#if 0
+ if (1) {
+ qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
+ cpu_single_env->mem_io_vaddr, len,
+ cpu_single_env->eip,
+ cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base);
+ }
+#endif
+ p = page_find(start >> TARGET_PAGE_BITS);
+ if (!p)
+ return;
+ if (p->code_bitmap) {
+ offset = start & ~TARGET_PAGE_MASK;
+ b = p->code_bitmap[offset >> 3] >> (offset & 7);
+ if (b & ((1 << len) - 1))
+ goto do_invalidate;
+ } else {
+ do_invalidate:
+ tb_invalidate_phys_page_range(start, start + len, 1);
+ }
+}
+
+#if !defined(CONFIG_SOFTMMU)
+static void tb_invalidate_phys_page(tb_page_addr_t addr,
+ unsigned long pc, void *puc)
+{
+ TranslationBlock *tb;
+ PageDesc *p;
+ int n;
+#ifdef TARGET_HAS_PRECISE_SMC
+ TranslationBlock *current_tb = NULL;
+ CPUState *env = cpu_single_env;
+ int current_tb_modified = 0;
+ target_ulong current_pc = 0;
+ target_ulong current_cs_base = 0;
+ int current_flags = 0;
+#endif
+
+ addr &= TARGET_PAGE_MASK;
+ p = page_find(addr >> TARGET_PAGE_BITS);
+ if (!p)
+ return;
+ tb = p->first_tb;
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (tb && pc != 0) {
+ current_tb = tb_find_pc(pc);
+ }
+#endif
+ while (tb != NULL) {
+ n = (long)tb & 3;
+ tb = (TranslationBlock *)((long)tb & ~3);
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb == tb &&
+ (current_tb->cflags & CF_COUNT_MASK) != 1) {
+ /* If we are modifying the current TB, we must stop
+ its execution. We could be more precise by checking
+ that the modification is after the current PC, but it
+ would require a specialized function to partially
+ restore the CPU state */
+
+ current_tb_modified = 1;
+ cpu_restore_state(current_tb, env, pc, puc);
+ cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
+ &current_flags);
+ }
+#endif /* TARGET_HAS_PRECISE_SMC */
+ tb_phys_invalidate(tb, addr);
+ tb = tb->page_next[n];
+ }
+ p->first_tb = NULL;
+#ifdef TARGET_HAS_PRECISE_SMC
+ if (current_tb_modified) {
+ /* we generate a block containing just the instruction
+ modifying the memory. It will ensure that it cannot modify
+ itself */
+ env->current_tb = NULL;
+ tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
+ cpu_resume_from_signal(env, puc);
+ }
+#endif
+}
+#endif
+
+/* add the tb in the target page and protect it if necessary */
+static inline void tb_alloc_page(TranslationBlock *tb,
+ unsigned int n, tb_page_addr_t page_addr)
+{
+ PageDesc *p;
+ TranslationBlock *last_first_tb;
+
+ tb->page_addr[n] = page_addr;
+ p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1);
+ tb->page_next[n] = p->first_tb;
+ last_first_tb = p->first_tb;
+ p->first_tb = (TranslationBlock *)((long)tb | n);
+ invalidate_page_bitmap(p);
+
+#if defined(TARGET_HAS_SMC) || 1
+
+#if defined(CONFIG_USER_ONLY)
+ if (p->flags & PAGE_WRITE) {
+ target_ulong addr;
+ PageDesc *p2;
+ int prot;
+
+ /* force the host page as non writable (writes will have a
+ page fault + mprotect overhead) */
+ page_addr &= qemu_host_page_mask;
+ prot = 0;
+ for(addr = page_addr; addr < page_addr + qemu_host_page_size;
+ addr += TARGET_PAGE_SIZE) {
+
+ p2 = page_find (addr >> TARGET_PAGE_BITS);
+ if (!p2)
+ continue;
+ prot |= p2->flags;
+ p2->flags &= ~PAGE_WRITE;
+ }
+ mprotect(g2h(page_addr), qemu_host_page_size,
+ (prot & PAGE_BITS) & ~PAGE_WRITE);
+#ifdef DEBUG_TB_INVALIDATE
+ printf("protecting code page: 0x" TARGET_FMT_lx "\n",
+ page_addr);
+#endif
+ }
+#else
+ /* if some code is already present, then the pages are already
+ protected. So we handle the case where only the first TB is
+ allocated in a physical page */
+ if (!last_first_tb) {
+ tlb_protect_code(page_addr);
+ }
+#endif
+
+#endif /* TARGET_HAS_SMC */
+}
+
+/* Allocate a new translation block. Flush the translation buffer if
+ too many translation blocks or too much generated code. */
+TranslationBlock *tb_alloc(target_ulong pc)
+{
+ TranslationBlock *tb;
+
+ if (nb_tbs >= code_gen_max_blocks ||
+ (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size)
+ return NULL;
+ tb = &tbs[nb_tbs++];
+ tb->pc = pc;
+ tb->cflags = 0;
+ return tb;
+}
+
+void tb_free(TranslationBlock *tb)
+{
+ /* In practice this is mostly used for single use temporary TB
+ Ignore the hard cases and just back up if this TB happens to
+ be the last one generated. */
+ if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) {
+ code_gen_ptr = tb->tc_ptr;
+ nb_tbs--;
+ }
+}
+
+/* add a new TB and link it to the physical page tables. phys_page2 is
+ (-1) to indicate that only one page contains the TB. */
+void tb_link_page(TranslationBlock *tb,
+ tb_page_addr_t phys_pc, tb_page_addr_t phys_page2)
+{
+ unsigned int h;
+ TranslationBlock **ptb;
+
+ /* Grab the mmap lock to stop another thread invalidating this TB
+ before we are done. */
+ mmap_lock();
+ /* add in the physical hash table */
+ h = tb_phys_hash_func(phys_pc);
+ ptb = &tb_phys_hash[h];
+ tb->phys_hash_next = *ptb;
+ *ptb = tb;
+
+ /* add in the page list */
+ tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK);
+ if (phys_page2 != -1)
+ tb_alloc_page(tb, 1, phys_page2);
+ else
+ tb->page_addr[1] = -1;
+
+ tb->jmp_first = (TranslationBlock *)((long)tb | 2);
+ tb->jmp_next[0] = NULL;
+ tb->jmp_next[1] = NULL;
+
+ /* init original jump addresses */
+ if (tb->tb_next_offset[0] != 0xffff)
+ tb_reset_jump(tb, 0);
+ if (tb->tb_next_offset[1] != 0xffff)
+ tb_reset_jump(tb, 1);
+
+#ifdef DEBUG_TB_CHECK
+ tb_page_check();
+#endif
+ mmap_unlock();
+}
+
+/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
+ tb[1].tc_ptr. Return NULL if not found */
+TranslationBlock *tb_find_pc(unsigned long tc_ptr)
+{
+ int m_min, m_max, m;
+ unsigned long v;
+ TranslationBlock *tb;
+
+ if (nb_tbs <= 0)
+ return NULL;
+ if (tc_ptr < (unsigned long)code_gen_buffer ||
+ tc_ptr >= (unsigned long)code_gen_ptr)
+ return NULL;
+ /* binary search (cf Knuth) */
+ m_min = 0;
+ m_max = nb_tbs - 1;
+ while (m_min <= m_max) {
+ m = (m_min + m_max) >> 1;
+ tb = &tbs[m];
+ v = (unsigned long)tb->tc_ptr;
+ if (v == tc_ptr)
+ return tb;
+ else if (tc_ptr < v) {
+ m_max = m - 1;
+ } else {
+ m_min = m + 1;
+ }
+ }
+ return &tbs[m_max];
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb);
+
+static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n)
+{
+ TranslationBlock *tb1, *tb_next, **ptb;
+ unsigned int n1;
+
+ tb1 = tb->jmp_next[n];
+ if (tb1 != NULL) {
+ /* find head of list */
+ for(;;) {
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == 2)
+ break;
+ tb1 = tb1->jmp_next[n1];
+ }
+ /* we are now sure now that tb jumps to tb1 */
+ tb_next = tb1;
+
+ /* remove tb from the jmp_first list */
+ ptb = &tb_next->jmp_first;
+ for(;;) {
+ tb1 = *ptb;
+ n1 = (long)tb1 & 3;
+ tb1 = (TranslationBlock *)((long)tb1 & ~3);
+ if (n1 == n && tb1 == tb)
+ break;
+ ptb = &tb1->jmp_next[n1];
+ }
+ *ptb = tb->jmp_next[n];
+ tb->jmp_next[n] = NULL;
+
+ /* suppress the jump to next tb in generated code */
+ tb_reset_jump(tb, n);
+
+ /* suppress jumps in the tb on which we could have jumped */
+ tb_reset_jump_recursive(tb_next);
+ }
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb)
+{
+ tb_reset_jump_recursive2(tb, 0);
+ tb_reset_jump_recursive2(tb, 1);
+}
+
+#if defined(TARGET_HAS_ICE)
+#if defined(CONFIG_USER_ONLY)
+static void breakpoint_invalidate(CPUState *env, target_ulong pc)
+{
+ tb_invalidate_phys_page_range(pc, pc + 1, 0);
+}
+#else
+static void breakpoint_invalidate(CPUState *env, target_ulong pc)
+{
+ target_phys_addr_t addr;
+ target_ulong pd;
+ ram_addr_t ram_addr;
+ PhysPageDesc *p;
+
+ addr = cpu_get_phys_page_debug(env, pc);
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+ ram_addr = (pd & TARGET_PAGE_MASK) | (pc & ~TARGET_PAGE_MASK);
+ tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0);
+}
+#endif
+#endif /* TARGET_HAS_ICE */
+
+#if defined(CONFIG_USER_ONLY)
+void cpu_watchpoint_remove_all(CPUState *env, int mask)
+
+{
+}
+
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
+ int flags, CPUWatchpoint **watchpoint)
+{
+ return -ENOSYS;
+}
+#else
+/* Add a watchpoint. */
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
+ int flags, CPUWatchpoint **watchpoint)
+{
+ target_ulong len_mask = ~(len - 1);
+ CPUWatchpoint *wp;
+
+ /* sanity checks: allow power-of-2 lengths, deny unaligned watchpoints */
+ if ((len != 1 && len != 2 && len != 4 && len != 8) || (addr & ~len_mask)) {
+ fprintf(stderr, "qemu: tried to set invalid watchpoint at "
+ TARGET_FMT_lx ", len=" TARGET_FMT_lu "\n", addr, len);
+ return -EINVAL;
+ }
+ wp = qemu_malloc(sizeof(*wp));
+
+ wp->vaddr = addr;
+ wp->len_mask = len_mask;
+ wp->flags = flags;
+
+ /* keep all GDB-injected watchpoints in front */
+ if (flags & BP_GDB)
+ QTAILQ_INSERT_HEAD(&env->watchpoints, wp, entry);
+ else
+ QTAILQ_INSERT_TAIL(&env->watchpoints, wp, entry);
+
+ tlb_flush_page(env, addr);
+
+ if (watchpoint)
+ *watchpoint = wp;
+ return 0;
+}
+
+/* Remove a specific watchpoint. */
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len,
+ int flags)
+{
+ target_ulong len_mask = ~(len - 1);
+ CPUWatchpoint *wp;
+
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
+ if (addr == wp->vaddr && len_mask == wp->len_mask
+ && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) {
+ cpu_watchpoint_remove_by_ref(env, wp);
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+/* Remove a specific watchpoint by reference. */
+void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint)
+{
+ QTAILQ_REMOVE(&env->watchpoints, watchpoint, entry);
+
+ tlb_flush_page(env, watchpoint->vaddr);
+
+ qemu_free(watchpoint);
+}
+
+/* Remove all matching watchpoints. */
+void cpu_watchpoint_remove_all(CPUState *env, int mask)
+{
+ CPUWatchpoint *wp, *next;
+
+ QTAILQ_FOREACH_SAFE(wp, &env->watchpoints, entry, next) {
+ if (wp->flags & mask)
+ cpu_watchpoint_remove_by_ref(env, wp);
+ }
+}
+#endif
+
+/* Add a breakpoint. */
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags,
+ CPUBreakpoint **breakpoint)
+{
+#if defined(TARGET_HAS_ICE)
+ CPUBreakpoint *bp;
+
+ bp = qemu_malloc(sizeof(*bp));
+
+ bp->pc = pc;
+ bp->flags = flags;
+
+ /* keep all GDB-injected breakpoints in front */
+ if (flags & BP_GDB)
+ QTAILQ_INSERT_HEAD(&env->breakpoints, bp, entry);
+ else
+ QTAILQ_INSERT_TAIL(&env->breakpoints, bp, entry);
+
+ breakpoint_invalidate(env, pc);
+
+ if (breakpoint)
+ *breakpoint = bp;
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+/* Remove a specific breakpoint. */
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags)
+{
+#if defined(TARGET_HAS_ICE)
+ CPUBreakpoint *bp;
+
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == pc && bp->flags == flags) {
+ cpu_breakpoint_remove_by_ref(env, bp);
+ return 0;
+ }
+ }
+ return -ENOENT;
+#else
+ return -ENOSYS;
+#endif
+}
+
+/* Remove a specific breakpoint by reference. */
+void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint)
+{
+#if defined(TARGET_HAS_ICE)
+ QTAILQ_REMOVE(&env->breakpoints, breakpoint, entry);
+
+ breakpoint_invalidate(env, breakpoint->pc);
+
+ qemu_free(breakpoint);
+#endif
+}
+
+/* Remove all matching breakpoints. */
+void cpu_breakpoint_remove_all(CPUState *env, int mask)
+{
+#if defined(TARGET_HAS_ICE)
+ CPUBreakpoint *bp, *next;
+
+ QTAILQ_FOREACH_SAFE(bp, &env->breakpoints, entry, next) {
+ if (bp->flags & mask)
+ cpu_breakpoint_remove_by_ref(env, bp);
+ }
+#endif
+}
+
+/* enable or disable single step mode. EXCP_DEBUG is returned by the
+ CPU loop after each instruction */
+void cpu_single_step(CPUState *env, int enabled)
+{
+#if defined(TARGET_HAS_ICE)
+ if (env->singlestep_enabled != enabled) {
+ env->singlestep_enabled = enabled;
+ if (kvm_enabled())
+ kvm_update_guest_debug(env, 0);
+ else {
+ /* must flush all the translated code to avoid inconsistencies */
+ /* XXX: only flush what is necessary */
+ tb_flush(env);
+ }
+ }
+#endif
+}
+
+/* enable or disable low levels log */
+void cpu_set_log(int log_flags)
+{
+ loglevel = log_flags;
+ if (loglevel && !logfile) {
+ logfile = fopen(logfilename, log_append ? "a" : "w");
+ if (!logfile) {
+ perror(logfilename);
+ _exit(1);
+ }
+#if !defined(CONFIG_SOFTMMU)
+ /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
+ {
+ static char logfile_buf[4096];
+ setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
+ }
+#elif !defined(_WIN32)
+ /* Win32 doesn't support line-buffering and requires size >= 2 */
+ setvbuf(logfile, NULL, _IOLBF, 0);
+#endif
+ log_append = 1;
+ }
+ if (!loglevel && logfile) {
+ fclose(logfile);
+ logfile = NULL;
+ }
+}
+
+void cpu_set_log_filename(const char *filename)
+{
+ logfilename = strdup(filename);
+ if (logfile) {
+ fclose(logfile);
+ logfile = NULL;
+ }
+ cpu_set_log(loglevel);
+}
+
+static void cpu_unlink_tb(CPUState *env)
+{
+ /* FIXME: TB unchaining isn't SMP safe. For now just ignore the
+ problem and hope the cpu will stop of its own accord. For userspace
+ emulation this often isn't actually as bad as it sounds. Often
+ signals are used primarily to interrupt blocking syscalls. */
+ TranslationBlock *tb;
+ static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED;
+
+ spin_lock(&interrupt_lock);
+ tb = env->current_tb;
+ /* if the cpu is currently executing code, we must unlink it and
+ all the potentially executing TB */
+ if (tb) {
+ env->current_tb = NULL;
+ tb_reset_jump_recursive(tb);
+ }
+ spin_unlock(&interrupt_lock);
+}
+
+/* mask must never be zero, except for A20 change call */
+void cpu_interrupt(CPUState *env, int mask)
+{
+ int old_mask;
+
+ old_mask = env->interrupt_request;
+ env->interrupt_request |= mask;
+ if (kvm_enabled() && !kvm_irqchip_in_kernel())
+ kvm_update_interrupt_request(env);
+
+#ifndef CONFIG_USER_ONLY
+ /*
+ * If called from iothread context, wake the target cpu in
+ * case its halted.
+ */
+ if (!qemu_cpu_self(env)) {
+ qemu_cpu_kick(env);
+ return;
+ }
+#endif
+
+ if (use_icount) {
+ env->icount_decr.u16.high = 0xffff;
+#ifndef CONFIG_USER_ONLY
+ if (!can_do_io(env)
+ && (mask & ~old_mask) != 0) {
+ cpu_abort(env, "Raised interrupt while not in I/O function");
+ }
+#endif
+ } else {
+ cpu_unlink_tb(env);
+ }
+}
+
+void cpu_reset_interrupt(CPUState *env, int mask)
+{
+ env->interrupt_request &= ~mask;
+}
+
+void cpu_exit(CPUState *env)
+{
+ env->exit_request = 1;
+ cpu_unlink_tb(env);
+}
+
+const CPULogItem cpu_log_items[] = {
+ { CPU_LOG_TB_OUT_ASM, "out_asm",
+ "show generated host assembly code for each compiled TB" },
+ { CPU_LOG_TB_IN_ASM, "in_asm",
+ "show target assembly code for each compiled TB" },
+ { CPU_LOG_TB_OP, "op",
+ "show micro ops for each compiled TB" },
+ { CPU_LOG_TB_OP_OPT, "op_opt",
+ "show micro ops "
+#ifdef TARGET_I386
+ "before eflags optimization and "
+#endif
+ "after liveness analysis" },
+ { CPU_LOG_INT, "int",
+ "show interrupts/exceptions in short format" },
+ { CPU_LOG_EXEC, "exec",
+ "show trace before each executed TB (lots of logs)" },
+ { CPU_LOG_TB_CPU, "cpu",
+ "show CPU state before block translation" },
+#ifdef TARGET_I386
+ { CPU_LOG_PCALL, "pcall",
+ "show protected mode far calls/returns/exceptions" },
+ { CPU_LOG_RESET, "cpu_reset",
+ "show CPU state before CPU resets" },
+#endif
+#ifdef DEBUG_IOPORT
+ { CPU_LOG_IOPORT, "ioport",
+ "show all i/o ports accesses" },
+#endif
+ { 0, NULL, NULL },
+};
+
+#ifndef CONFIG_USER_ONLY
+static QLIST_HEAD(memory_client_list, CPUPhysMemoryClient) memory_client_list
+ = QLIST_HEAD_INITIALIZER(memory_client_list);
+
+static void cpu_notify_set_memory(target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset)
+{
+ CPUPhysMemoryClient *client;
+ QLIST_FOREACH(client, &memory_client_list, list) {
+ client->set_memory(client, start_addr, size, phys_offset);
+ }
+}
+
+static int cpu_notify_sync_dirty_bitmap(target_phys_addr_t start,
+ target_phys_addr_t end)
+{
+ CPUPhysMemoryClient *client;
+ QLIST_FOREACH(client, &memory_client_list, list) {
+ int r = client->sync_dirty_bitmap(client, start, end);
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
+
+static int cpu_notify_migration_log(int enable)
+{
+ CPUPhysMemoryClient *client;
+ QLIST_FOREACH(client, &memory_client_list, list) {
+ int r = client->migration_log(client, enable);
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
+
+static void phys_page_for_each_1(CPUPhysMemoryClient *client,
+ int level, void **lp)
+{
+ int i;
+
+ if (*lp == NULL) {
+ return;
+ }
+ if (level == 0) {
+ PhysPageDesc *pd = *lp;
+ for (i = 0; i < L2_SIZE; ++i) {
+ if (pd[i].phys_offset != IO_MEM_UNASSIGNED) {
+ client->set_memory(client, pd[i].region_offset,
+ TARGET_PAGE_SIZE, pd[i].phys_offset);
+ }
+ }
+ } else {
+ void **pp = *lp;
+ for (i = 0; i < L2_SIZE; ++i) {
+ phys_page_for_each_1(client, level - 1, pp + i);
+ }
+ }
+}
+
+static void phys_page_for_each(CPUPhysMemoryClient *client)
+{
+ int i;
+ for (i = 0; i < P_L1_SIZE; ++i) {
+ phys_page_for_each_1(client, P_L1_SHIFT / L2_BITS - 1,
+ l1_phys_map + 1);
+ }
+}
+
+void cpu_register_phys_memory_client(CPUPhysMemoryClient *client)
+{
+ QLIST_INSERT_HEAD(&memory_client_list, client, list);
+ phys_page_for_each(client);
+}
+
+void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *client)
+{
+ QLIST_REMOVE(client, list);
+}
+#endif
+
+static int cmp1(const char *s1, int n, const char *s2)
+{
+ if (strlen(s2) != n)
+ return 0;
+ return memcmp(s1, s2, n) == 0;
+}
+
+/* takes a comma separated list of log masks. Return 0 if error. */
+int cpu_str_to_log_mask(const char *str)
+{
+ const CPULogItem *item;
+ int mask;
+ const char *p, *p1;
+
+ p = str;
+ mask = 0;
+ for(;;) {
+ p1 = strchr(p, ',');
+ if (!p1)
+ p1 = p + strlen(p);
+ if(cmp1(p,p1-p,"all")) {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ mask |= item->mask;
+ }
+ } else {
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ if (cmp1(p, p1 - p, item->name))
+ goto found;
+ }
+ return 0;
+ }
+ found:
+ mask |= item->mask;
+ if (*p1 != ',')
+ break;
+ p = p1 + 1;
+ }
+ return mask;
+}
+
+void cpu_abort(CPUState *env, const char *fmt, ...)
+{
+ va_list ap;
+ va_list ap2;
+
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
+ fprintf(stderr, "qemu: fatal: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+#ifdef TARGET_I386
+ cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
+#else
+ cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+ if (qemu_log_enabled()) {
+ qemu_log("qemu: fatal: ");
+ qemu_log_vprintf(fmt, ap2);
+ qemu_log("\n");
+#ifdef TARGET_I386
+ log_cpu_state(env, X86_DUMP_FPU | X86_DUMP_CCOP);
+#else
+ log_cpu_state(env, 0);
+#endif
+ qemu_log_flush();
+ qemu_log_close();
+ }
+ va_end(ap2);
+ va_end(ap);
+#if defined(CONFIG_USER_ONLY)
+ {
+ struct sigaction act;
+ sigfillset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGABRT, &act, NULL);
+ }
+#endif
+ abort();
+}
+
+CPUState *cpu_copy(CPUState *env)
+{
+ CPUState *new_env = cpu_init(env->cpu_model_str);
+ CPUState *next_cpu = new_env->next_cpu;
+ int cpu_index = new_env->cpu_index;
+#if defined(TARGET_HAS_ICE)
+ CPUBreakpoint *bp;
+ CPUWatchpoint *wp;
+#endif
+
+ memcpy(new_env, env, sizeof(CPUState));
+
+ /* Preserve chaining and index. */
+ new_env->next_cpu = next_cpu;
+ new_env->cpu_index = cpu_index;
+
+ /* Clone all break/watchpoints.
+ Note: Once we support ptrace with hw-debug register access, make sure
+ BP_CPU break/watchpoints are handled correctly on clone. */
+ QTAILQ_INIT(&env->breakpoints);
+ QTAILQ_INIT(&env->watchpoints);
+#if defined(TARGET_HAS_ICE)
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ cpu_breakpoint_insert(new_env, bp->pc, bp->flags, NULL);
+ }
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
+ cpu_watchpoint_insert(new_env, wp->vaddr, (~wp->len_mask) + 1,
+ wp->flags, NULL);
+ }
+#endif
+
+ return new_env;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr)
+{
+ unsigned int i;
+
+ /* Discard jump cache entries for any tb which might potentially
+ overlap the flushed page. */
+ i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE);
+ memset (&env->tb_jmp_cache[i], 0,
+ TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
+
+ i = tb_jmp_cache_hash_page(addr);
+ memset (&env->tb_jmp_cache[i], 0,
+ TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
+}
+
+static CPUTLBEntry s_cputlb_empty_entry = {
+ .addr_read = -1,
+ .addr_write = -1,
+ .addr_code = -1,
+ .addend = -1,
+};
+
+/* NOTE: if flush_global is true, also flush global entries (not
+ implemented yet) */
+void tlb_flush(CPUState *env, int flush_global)
+{
+ int i;
+
+#if defined(DEBUG_TLB)
+ printf("tlb_flush:\n");
+#endif
+ /* must reset current TB so that interrupts cannot modify the
+ links while we are modifying them */
+ env->current_tb = NULL;
+
+ for(i = 0; i < CPU_TLB_SIZE; i++) {
+ int mmu_idx;
+ for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
+ env->tlb_table[mmu_idx][i] = s_cputlb_empty_entry;
+ }
+ }
+
+ memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+
+ env->tlb_flush_addr = -1;
+ env->tlb_flush_mask = 0;
+ tlb_flush_count++;
+}
+
+static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
+{
+ if (addr == (tlb_entry->addr_read &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
+ addr == (tlb_entry->addr_write &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
+ addr == (tlb_entry->addr_code &
+ (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ *tlb_entry = s_cputlb_empty_entry;
+ }
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+ int i;
+ int mmu_idx;
+
+#if defined(DEBUG_TLB)
+ printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr);
+#endif
+ /* Check if we need to flush due to large pages. */
+ if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) {
+#if defined(DEBUG_TLB)
+ printf("tlb_flush_page: forced full flush ("
+ TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
+ env->tlb_flush_addr, env->tlb_flush_mask);
+#endif
+ tlb_flush(env, 1);
+ return;
+ }
+ /* must reset current TB so that interrupts cannot modify the
+ links while we are modifying them */
+ env->current_tb = NULL;
+
+ addr &= TARGET_PAGE_MASK;
+ i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++)
+ tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
+
+ tlb_flush_jmp_cache(env, addr);
+}
+
+/* update the TLBs so that writes to code in the virtual page 'addr'
+ can be detected */
+static void tlb_protect_code(ram_addr_t ram_addr)
+{
+ cpu_physical_memory_reset_dirty(ram_addr,
+ ram_addr + TARGET_PAGE_SIZE,
+ CODE_DIRTY_FLAG);
+}
+
+/* update the TLB so that writes in physical page 'phys_addr' are no longer
+ tested for self modifying code */
+static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr,
+ target_ulong vaddr)
+{
+ cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG);
+}
+
+static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry,
+ unsigned long start, unsigned long length)
+{
+ unsigned long addr;
+ if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+ addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend;
+ if ((addr - start) < length) {
+ tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | TLB_NOTDIRTY;
+ }
+ }
+}
+
+/* Note: start and end must be within the same ram block. */
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
+ int dirty_flags)
+{
+ CPUState *env;
+ unsigned long length, start1;
+ int i;
+
+ start &= TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+
+ length = end - start;
+ if (length == 0)
+ return;
+ cpu_physical_memory_mask_dirty_range(start, length, dirty_flags);
+
+ /* 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
+ address comparisons below. */
+ if ((unsigned long)qemu_safe_ram_ptr(end - 1) - start1
+ != (end - 1) - start) {
+ abort();
+ }
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ int mmu_idx;
+ for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i],
+ start1, length);
+ }
+ }
+}
+
+int cpu_physical_memory_set_dirty_tracking(int enable)
+{
+ int ret = 0;
+ in_migration = enable;
+ ret = cpu_notify_migration_log(!!enable);
+ return ret;
+}
+
+int cpu_physical_memory_get_dirty_tracking(void)
+{
+ return in_migration;
+}
+
+int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
+{
+ int ret;
+
+ ret = cpu_notify_sync_dirty_bitmap(start_addr, end_addr);
+ return ret;
+}
+
+static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
+{
+ ram_addr_t ram_addr;
+ void *p;
+
+ if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+ p = (void *)(unsigned long)((tlb_entry->addr_write & TARGET_PAGE_MASK)
+ + tlb_entry->addend);
+ ram_addr = qemu_ram_addr_from_host_nofail(p);
+ if (!cpu_physical_memory_is_dirty(ram_addr)) {
+ tlb_entry->addr_write |= TLB_NOTDIRTY;
+ }
+ }
+}
+
+/* update the TLB according to the current state of the dirty bits */
+void cpu_tlb_update_dirty(CPUState *env)
+{
+ int i;
+ int mmu_idx;
+ for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
+ for(i = 0; i < CPU_TLB_SIZE; i++)
+ tlb_update_dirty(&env->tlb_table[mmu_idx][i]);
+ }
+}
+
+static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
+{
+ if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY))
+ tlb_entry->addr_write = vaddr;
+}
+
+/* update the TLB corresponding to virtual page vaddr
+ so that it is no longer dirty */
+static inline void tlb_set_dirty(CPUState *env, target_ulong vaddr)
+{
+ int i;
+ int mmu_idx;
+
+ vaddr &= TARGET_PAGE_MASK;
+ i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++)
+ tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr);
+}
+
+/* Our TLB does not support large pages, so remember the area covered by
+ large pages and trigger a full TLB flush if these are invalidated. */
+static void tlb_add_large_page(CPUState *env, target_ulong vaddr,
+ target_ulong size)
+{
+ target_ulong mask = ~(size - 1);
+
+ if (env->tlb_flush_addr == (target_ulong)-1) {
+ env->tlb_flush_addr = vaddr & mask;
+ env->tlb_flush_mask = mask;
+ return;
+ }
+ /* Extend the existing region to include the new page.
+ This is a compromise between unnecessary flushes and the cost
+ of maintaining a full variable size TLB. */
+ mask &= env->tlb_flush_mask;
+ while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) {
+ mask <<= 1;
+ }
+ env->tlb_flush_addr &= mask;
+ env->tlb_flush_mask = mask;
+}
+
+/* Add a new TLB entry. At most one entry for a given virtual address
+ is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the
+ supplied size is only used by tlb_flush_page. */
+void tlb_set_page(CPUState *env, target_ulong vaddr,
+ target_phys_addr_t paddr, int prot,
+ int mmu_idx, target_ulong size)
+{
+ PhysPageDesc *p;
+ unsigned long pd;
+ unsigned int index;
+ target_ulong address;
+ target_ulong code_address;
+ unsigned long addend;
+ CPUTLBEntry *te;
+ CPUWatchpoint *wp;
+ target_phys_addr_t iotlb;
+
+ assert(size >= TARGET_PAGE_SIZE);
+ if (size != TARGET_PAGE_SIZE) {
+ tlb_add_large_page(env, vaddr, size);
+ }
+ p = phys_page_find(paddr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+#if defined(DEBUG_TLB)
+ printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
+ " prot=%x idx=%d pd=0x%08lx\n",
+ vaddr, paddr, prot, mmu_idx, pd);
+#endif
+
+ address = vaddr;
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+ /* IO memory case (romd handled later) */
+ address |= TLB_MMIO;
+ }
+ addend = (unsigned long)qemu_get_ram_ptr(pd & TARGET_PAGE_MASK);
+ if ((pd & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) {
+ /* Normal RAM. */
+ iotlb = pd & TARGET_PAGE_MASK;
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM)
+ iotlb |= IO_MEM_NOTDIRTY;
+ else
+ iotlb |= IO_MEM_ROM;
+ } else {
+ /* IO handlers are currently passed a physical address.
+ It would be nice to pass an offset from the base address
+ of that region. This would avoid having to special case RAM,
+ and avoid full address decoding in every device.
+ We can't use the high bits of pd for this because
+ IO_MEM_ROMD uses these as a ram address. */
+ iotlb = (pd & ~TARGET_PAGE_MASK);
+ if (p) {
+ iotlb += p->region_offset;
+ } else {
+ iotlb += paddr;
+ }
+ }
+
+ code_address = address;
+ /* Make accesses to pages with watchpoints go via the
+ watchpoint trap routines. */
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
+ if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) {
+ /* Avoid trapping reads of pages with a write breakpoint. */
+ if ((prot & PAGE_WRITE) || (wp->flags & BP_MEM_READ)) {
+ iotlb = io_mem_watch + paddr;
+ address |= TLB_MMIO;
+ break;
+ }
+ }
+ }
+
+ index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ env->iotlb[mmu_idx][index] = iotlb - vaddr;
+ te = &env->tlb_table[mmu_idx][index];
+ te->addend = addend - vaddr;
+ if (prot & PAGE_READ) {
+ te->addr_read = address;
+ } else {
+ te->addr_read = -1;
+ }
+
+ if (prot & PAGE_EXEC) {
+ te->addr_code = code_address;
+ } else {
+ te->addr_code = -1;
+ }
+ if (prot & PAGE_WRITE) {
+ if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM ||
+ (pd & IO_MEM_ROMD)) {
+ /* Write access calls the I/O callback. */
+ te->addr_write = address | TLB_MMIO;
+ } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM &&
+ !cpu_physical_memory_is_dirty(pd)) {
+ te->addr_write = address | TLB_NOTDIRTY;
+ } else {
+ te->addr_write = address;
+ }
+ } else {
+ te->addr_write = -1;
+ }
+}
+
+#else
+
+void tlb_flush(CPUState *env, int flush_global)
+{
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+}
+
+/*
+ * Walks guest process memory "regions" one by one
+ * and calls callback function 'fn' for each region.
+ */
+
+struct walk_memory_regions_data
+{
+ walk_memory_regions_fn fn;
+ void *priv;
+ unsigned long start;
+ int prot;
+};
+
+static int walk_memory_regions_end(struct walk_memory_regions_data *data,
+ abi_ulong end, int new_prot)
+{
+ if (data->start != -1ul) {
+ int rc = data->fn(data->priv, data->start, end, data->prot);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ data->start = (new_prot ? end : -1ul);
+ data->prot = new_prot;
+
+ return 0;
+}
+
+static int walk_memory_regions_1(struct walk_memory_regions_data *data,
+ abi_ulong base, int level, void **lp)
+{
+ abi_ulong pa;
+ int i, rc;
+
+ if (*lp == NULL) {
+ return walk_memory_regions_end(data, base, 0);
+ }
+
+ if (level == 0) {
+ PageDesc *pd = *lp;
+ for (i = 0; i < L2_SIZE; ++i) {
+ int prot = pd[i].flags;
+
+ pa = base | (i << TARGET_PAGE_BITS);
+ if (prot != data->prot) {
+ rc = walk_memory_regions_end(data, pa, prot);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+ }
+ } else {
+ void **pp = *lp;
+ for (i = 0; i < L2_SIZE; ++i) {
+ pa = base | ((abi_ulong)i <<
+ (TARGET_PAGE_BITS + L2_BITS * level));
+ rc = walk_memory_regions_1(data, pa, level - 1, pp + i);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int walk_memory_regions(void *priv, walk_memory_regions_fn fn)
+{
+ struct walk_memory_regions_data data;
+ unsigned long i;
+
+ data.fn = fn;
+ data.priv = priv;
+ data.start = -1ul;
+ data.prot = 0;
+
+ for (i = 0; i < V_L1_SIZE; i++) {
+ int rc = walk_memory_regions_1(&data, (abi_ulong)i << V_L1_SHIFT,
+ V_L1_SHIFT / L2_BITS - 1, l1_map + i);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ return walk_memory_regions_end(&data, 0, 0);
+}
+
+static int dump_region(void *priv, abi_ulong start,
+ abi_ulong end, unsigned long prot)
+{
+ FILE *f = (FILE *)priv;
+
+ (void) fprintf(f, TARGET_ABI_FMT_lx"-"TARGET_ABI_FMT_lx
+ " "TARGET_ABI_FMT_lx" %c%c%c\n",
+ start, end, end - start,
+ ((prot & PAGE_READ) ? 'r' : '-'),
+ ((prot & PAGE_WRITE) ? 'w' : '-'),
+ ((prot & PAGE_EXEC) ? 'x' : '-'));
+
+ return (0);
+}
+
+/* dump memory mappings */
+void page_dump(FILE *f)
+{
+ (void) fprintf(f, "%-8s %-8s %-8s %s\n",
+ "start", "end", "size", "prot");
+ walk_memory_regions(f, dump_region);
+}
+
+int page_get_flags(target_ulong address)
+{
+ PageDesc *p;
+
+ p = page_find(address >> TARGET_PAGE_BITS);
+ if (!p)
+ return 0;
+ return p->flags;
+}
+
+/* Modify the flags of a page and invalidate the code if necessary.
+ The flag PAGE_WRITE_ORG is positioned automatically depending
+ on PAGE_WRITE. The mmap_lock should already be held. */
+void page_set_flags(target_ulong start, target_ulong end, int flags)
+{
+ target_ulong addr, len;
+
+ /* This function should never be called with addresses outside the
+ guest address space. If this assert fires, it probably indicates
+ a missing call to h2g_valid. */
+#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
+ assert(end < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
+#endif
+ assert(start < end);
+
+ start = start & TARGET_PAGE_MASK;
+ end = TARGET_PAGE_ALIGN(end);
+
+ if (flags & PAGE_WRITE) {
+ flags |= PAGE_WRITE_ORG;
+ }
+
+ for (addr = start, len = end - start;
+ len != 0;
+ len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) {
+ PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
+
+ /* If the write protection bit is set, then we invalidate
+ the code inside. */
+ if (!(p->flags & PAGE_WRITE) &&
+ (flags & PAGE_WRITE) &&
+ p->first_tb) {
+ tb_invalidate_phys_page(addr, 0, NULL);
+ }
+ p->flags = flags;
+ }
+}
+
+int page_check_range(target_ulong start, target_ulong len, int flags)
+{
+ PageDesc *p;
+ target_ulong end;
+ target_ulong addr;
+
+ /* This function should never be called with addresses outside the
+ guest address space. If this assert fires, it probably indicates
+ a missing call to h2g_valid. */
+#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS
+ assert(start < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS));
+#endif
+
+ if (len == 0) {
+ return 0;
+ }
+ if (start + len - 1 < start) {
+ /* We've wrapped around. */
+ return -1;
+ }
+
+ end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */
+ start = start & TARGET_PAGE_MASK;
+
+ for (addr = start, len = end - start;
+ len != 0;
+ len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) {
+ p = page_find(addr >> TARGET_PAGE_BITS);
+ if( !p )
+ return -1;
+ if( !(p->flags & PAGE_VALID) )
+ return -1;
+
+ if ((flags & PAGE_READ) && !(p->flags & PAGE_READ))
+ return -1;
+ if (flags & PAGE_WRITE) {
+ if (!(p->flags & PAGE_WRITE_ORG))
+ return -1;
+ /* unprotect the page if it was put read-only because it
+ contains translated code */
+ if (!(p->flags & PAGE_WRITE)) {
+ if (!page_unprotect(addr, 0, NULL))
+ return -1;
+ }
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/* called from signal handler: invalidate the code and unprotect the
+ page. Return TRUE if the fault was successfully handled. */
+int page_unprotect(target_ulong address, unsigned long pc, void *puc)
+{
+ unsigned int prot;
+ PageDesc *p;
+ target_ulong host_start, host_end, addr;
+
+ /* Technically this isn't safe inside a signal handler. However we
+ know this only ever happens in a synchronous SEGV handler, so in
+ practice it seems to be ok. */
+ mmap_lock();
+
+ p = page_find(address >> TARGET_PAGE_BITS);
+ if (!p) {
+ mmap_unlock();
+ return 0;
+ }
+
+ /* if the page was really writable, then we change its
+ protection back to writable */
+ if ((p->flags & PAGE_WRITE_ORG) && !(p->flags & PAGE_WRITE)) {
+ host_start = address & qemu_host_page_mask;
+ host_end = host_start + qemu_host_page_size;
+
+ prot = 0;
+ for (addr = host_start ; addr < host_end ; addr += TARGET_PAGE_SIZE) {
+ p = page_find(addr >> TARGET_PAGE_BITS);
+ p->flags |= PAGE_WRITE;
+ prot |= p->flags;
+
+ /* and since the content will be modified, we must invalidate
+ the corresponding translated code. */
+ tb_invalidate_phys_page(addr, pc, puc);
+#ifdef DEBUG_TB_CHECK
+ tb_invalidate_check(addr);
+#endif
+ }
+ mprotect((void *)g2h(host_start), qemu_host_page_size,
+ prot & PAGE_BITS);
+
+ mmap_unlock();
+ return 1;
+ }
+ mmap_unlock();
+ return 0;
+}
+
+static inline void tlb_set_dirty(CPUState *env,
+ unsigned long addr, target_ulong vaddr)
+{
+}
+#endif /* defined(CONFIG_USER_ONLY) */
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK)
+typedef struct subpage_t {
+ target_phys_addr_t base;
+ ram_addr_t sub_io_index[TARGET_PAGE_SIZE];
+ ram_addr_t region_offset[TARGET_PAGE_SIZE];
+} subpage_t;
+
+static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
+ ram_addr_t memory, ram_addr_t region_offset);
+static subpage_t *subpage_init (target_phys_addr_t base, ram_addr_t *phys,
+ ram_addr_t orig_memory,
+ ram_addr_t region_offset);
+#define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \
+ need_subpage) \
+ do { \
+ if (addr > start_addr) \
+ start_addr2 = 0; \
+ else { \
+ start_addr2 = start_addr & ~TARGET_PAGE_MASK; \
+ if (start_addr2 > 0) \
+ need_subpage = 1; \
+ } \
+ \
+ if ((start_addr + orig_size) - addr >= TARGET_PAGE_SIZE) \
+ end_addr2 = TARGET_PAGE_SIZE - 1; \
+ else { \
+ end_addr2 = (start_addr + orig_size - 1) & ~TARGET_PAGE_MASK; \
+ if (end_addr2 < TARGET_PAGE_SIZE - 1) \
+ need_subpage = 1; \
+ } \
+ } while (0)
+
+/* register physical memory.
+ For RAM, 'size' must be a multiple of the target page size.
+ If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
+ io memory page. The address used when calling the IO function is
+ the offset from the start of the region, plus region_offset. Both
+ start_addr and region_offset are rounded down to a page boundary
+ before calculating this offset. This should not be a problem unless
+ the low bits of start_addr and region_offset differ. */
+void cpu_register_physical_memory_offset(target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset,
+ ram_addr_t region_offset)
+{
+ target_phys_addr_t addr, end_addr;
+ PhysPageDesc *p;
+ CPUState *env;
+ ram_addr_t orig_size = size;
+ subpage_t *subpage;
+
+ cpu_notify_set_memory(start_addr, size, phys_offset);
+
+ if (phys_offset == IO_MEM_UNASSIGNED) {
+ region_offset = start_addr;
+ }
+ region_offset &= TARGET_PAGE_MASK;
+ size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+ end_addr = start_addr + (target_phys_addr_t)size;
+ for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (p && p->phys_offset != IO_MEM_UNASSIGNED) {
+ ram_addr_t orig_memory = p->phys_offset;
+ target_phys_addr_t start_addr2, end_addr2;
+ int need_subpage = 0;
+
+ CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2,
+ need_subpage);
+ if (need_subpage) {
+ if (!(orig_memory & IO_MEM_SUBPAGE)) {
+ subpage = subpage_init((addr & TARGET_PAGE_MASK),
+ &p->phys_offset, orig_memory,
+ p->region_offset);
+ } else {
+ subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK)
+ >> IO_MEM_SHIFT];
+ }
+ subpage_register(subpage, start_addr2, end_addr2, phys_offset,
+ region_offset);
+ p->region_offset = 0;
+ } else {
+ p->phys_offset = phys_offset;
+ if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
+ (phys_offset & IO_MEM_ROMD))
+ phys_offset += TARGET_PAGE_SIZE;
+ }
+ } else {
+ p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
+ p->phys_offset = phys_offset;
+ p->region_offset = region_offset;
+ if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
+ (phys_offset & IO_MEM_ROMD)) {
+ phys_offset += TARGET_PAGE_SIZE;
+ } else {
+ target_phys_addr_t start_addr2, end_addr2;
+ int need_subpage = 0;
+
+ CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr,
+ end_addr2, need_subpage);
+
+ if (need_subpage) {
+ subpage = subpage_init((addr & TARGET_PAGE_MASK),
+ &p->phys_offset, IO_MEM_UNASSIGNED,
+ addr & TARGET_PAGE_MASK);
+ subpage_register(subpage, start_addr2, end_addr2,
+ phys_offset, region_offset);
+ p->region_offset = 0;
+ }
+ }
+ }
+ region_offset += TARGET_PAGE_SIZE;
+ }
+
+ /* since each CPU stores ram addresses in its TLB cache, we must
+ reset the modified entries */
+ /* XXX: slow ! */
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ tlb_flush(env, 1);
+ }
+}
+
+/* XXX: temporary until new memory mapping API */
+ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr)
+{
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p)
+ return IO_MEM_UNASSIGNED;
+ return p->phys_offset;
+}
+
+void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size)
+{
+ if (kvm_enabled())
+ kvm_coalesce_mmio_region(addr, size);
+}
+
+void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size)
+{
+ if (kvm_enabled())
+ kvm_uncoalesce_mmio_region(addr, size);
+}
+
+void qemu_flush_coalesced_mmio_buffer(void)
+{
+ if (kvm_enabled())
+ kvm_flush_coalesced_mmio_buffer();
+}
+
+#if defined(__linux__) && !defined(TARGET_S390X)
+
+#include <sys/vfs.h>
+
+#define HUGETLBFS_MAGIC 0x958458f6
+
+static long gethugepagesize(const char *path)
+{
+ struct statfs fs;
+ int ret;
+
+ do {
+ ret = statfs(path, &fs);
+ } while (ret != 0 && errno == EINTR);
+
+ if (ret != 0) {
+ perror(path);
+ return 0;
+ }
+
+ if (fs.f_type != HUGETLBFS_MAGIC)
+ fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path);
+
+ return fs.f_bsize;
+}
+
+static void *file_ram_alloc(RAMBlock *block,
+ ram_addr_t memory,
+ const char *path)
+{
+ char *filename;
+ void *area;
+ int fd;
+#ifdef MAP_POPULATE
+ int flags;
+#endif
+ unsigned long hpagesize;
+
+ hpagesize = gethugepagesize(path);
+ if (!hpagesize) {
+ return NULL;
+ }
+
+ if (memory < hpagesize) {
+ return NULL;
+ }
+
+ if (kvm_enabled() && !kvm_has_sync_mmu()) {
+ fprintf(stderr, "host lacks kvm mmu notifiers, -mem-path unsupported\n");
+ return NULL;
+ }
+
+ if (asprintf(&filename, "%s/qemu_back_mem.XXXXXX", path) == -1) {
+ return NULL;
+ }
+
+ fd = mkstemp(filename);
+ if (fd < 0) {
+ perror("unable to create backing store for hugepages");
+ free(filename);
+ return NULL;
+ }
+ unlink(filename);
+ free(filename);
+
+ memory = (memory+hpagesize-1) & ~(hpagesize-1);
+
+ /*
+ * ftruncate is not supported by hugetlbfs in older
+ * hosts, so don't bother bailing out on errors.
+ * If anything goes wrong with it under other filesystems,
+ * mmap will fail.
+ */
+ if (ftruncate(fd, memory))
+ perror("ftruncate");
+
+#ifdef MAP_POPULATE
+ /* NB: MAP_POPULATE won't exhaustively alloc all phys pages in the case
+ * MAP_PRIVATE is requested. For mem_prealloc we mmap as MAP_SHARED
+ * to sidestep this quirk.
+ */
+ flags = mem_prealloc ? MAP_POPULATE | MAP_SHARED : MAP_PRIVATE;
+ area = mmap(0, memory, PROT_READ | PROT_WRITE, flags, fd, 0);
+#else
+ area = mmap(0, memory, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+#endif
+ if (area == MAP_FAILED) {
+ perror("file_ram_alloc: can't mmap RAM pages");
+ close(fd);
+ return (NULL);
+ }
+ block->fd = fd;
+ return area;
+}
+#endif
+
+static ram_addr_t find_ram_offset(ram_addr_t size)
+{
+ RAMBlock *block, *next_block;
+ ram_addr_t offset = 0, mingap = ULONG_MAX;
+
+ if (QLIST_EMPTY(&ram_list.blocks))
+ return 0;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ ram_addr_t end, next = ULONG_MAX;
+
+ end = block->offset + block->length;
+
+ QLIST_FOREACH(next_block, &ram_list.blocks, next) {
+ if (next_block->offset >= end) {
+ next = MIN(next, next_block->offset);
+ }
+ }
+ if (next - end >= size && next - end < mingap) {
+ offset = end;
+ mingap = next - end;
+ }
+ }
+ return offset;
+}
+
+static ram_addr_t last_ram_offset(void)
+{
+ RAMBlock *block;
+ ram_addr_t last = 0;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next)
+ last = MAX(last, block->offset + block->length);
+
+ return last;
+}
+
+ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
+ ram_addr_t size, void *host)
+{
+ RAMBlock *new_block, *block;
+
+ size = TARGET_PAGE_ALIGN(size);
+ new_block = qemu_mallocz(sizeof(*new_block));
+
+ if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
+ char *id = dev->parent_bus->info->get_dev_path(dev);
+ if (id) {
+ snprintf(new_block->idstr, sizeof(new_block->idstr), "%s/", id);
+ qemu_free(id);
+ }
+ }
+ pstrcat(new_block->idstr, sizeof(new_block->idstr), name);
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (!strcmp(block->idstr, new_block->idstr)) {
+ fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n",
+ new_block->idstr);
+ abort();
+ }
+ }
+
+ if (host) {
+ new_block->host = host;
+ } else {
+ if (mem_path) {
+#if defined (__linux__) && !defined(TARGET_S390X)
+ new_block->host = file_ram_alloc(new_block, size, mem_path);
+ if (!new_block->host) {
+ new_block->host = qemu_vmalloc(size);
+ qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE);
+ }
+#else
+ fprintf(stderr, "-mem-path option unsupported\n");
+ exit(1);
+#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,
+ PROT_EXEC|PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+#else
+ new_block->host = qemu_vmalloc(size);
+#endif
+ qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE);
+ }
+ }
+
+ new_block->offset = find_ram_offset(size);
+ new_block->length = size;
+
+ QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next);
+
+ ram_list.phys_dirty = qemu_realloc(ram_list.phys_dirty,
+ last_ram_offset() >> TARGET_PAGE_BITS);
+ memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS),
+ 0xff, size >> TARGET_PAGE_BITS);
+
+ if (kvm_enabled())
+ kvm_setup_guest_memory(new_block->host, size);
+
+ return new_block->offset;
+}
+
+void qemu_ram_unmap(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;
+ }
+ }
+}
+
+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(ram_addr_t addr)
+{
+ RAMBlock *block;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (addr == block->offset) {
+ QLIST_REMOVE(block, next);
+ if (mem_path) {
+#if defined (__linux__) && !defined(TARGET_S390X)
+ if (block->fd) {
+ munmap(block->host, block->length);
+ close(block->fd);
+ } else {
+ qemu_vfree(block->host);
+ }
+#endif
+ } else {
+#if defined(TARGET_S390X) && defined(CONFIG_KVM)
+ munmap(block->host, block->length);
+#else
+ qemu_vfree(block->host);
+#endif
+ }
+ qemu_free(block);
+ return;
+ }
+ }
+
+}
+
+/* Return a host pointer to ram allocated with qemu_ram_alloc.
+ With the exception of the softmmu code in this file, this should
+ only be used for local memory (e.g. video ram) that the device owns,
+ and knows it isn't going to access beyond the end of the block.
+
+ It should not be used for general purpose DMA.
+ Use cpu_physical_memory_map/cpu_physical_memory_rw instead.
+ */
+void *qemu_get_ram_ptr(ram_addr_t addr)
+{
+ RAMBlock *block;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (addr - block->offset < block->length) {
+ QLIST_REMOVE(block, next);
+ QLIST_INSERT_HEAD(&ram_list.blocks, block, next);
+ return block->host + (addr - block->offset);
+ }
+ }
+
+ fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
+ abort();
+
+ return NULL;
+}
+
+/* Return a host pointer to ram allocated with qemu_ram_alloc.
+ * Same as qemu_get_ram_ptr but avoid reordering ramblocks.
+ */
+void *qemu_safe_ram_ptr(ram_addr_t addr)
+{
+ RAMBlock *block;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (addr - block->offset < block->length) {
+ return block->host + (addr - block->offset);
+ }
+ }
+
+ fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
+ abort();
+
+ return NULL;
+}
+
+int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
+{
+ RAMBlock *block;
+ uint8_t *host = ptr;
+
+ QLIST_FOREACH(block, &ram_list.blocks, next) {
+ if (host - block->host < block->length) {
+ *ram_addr = block->offset + (host - block->host);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/* Some of the softmmu routines need to translate from a host pointer
+ (typically a TLB entry) back to a ram offset. */
+ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
+{
+ ram_addr_t ram_addr;
+
+ if (qemu_ram_addr_from_host(ptr, &ram_addr)) {
+ fprintf(stderr, "Bad ram pointer %p\n", ptr);
+ abort();
+ }
+ return ram_addr;
+}
+
+static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+ do_unassigned_access(addr, 0, 0, 0, 1);
+#endif
+ return 0;
+}
+
+static uint32_t unassigned_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+ do_unassigned_access(addr, 0, 0, 0, 2);
+#endif
+ return 0;
+}
+
+static uint32_t unassigned_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+ do_unassigned_access(addr, 0, 0, 0, 4);
+#endif
+ return 0;
+}
+
+static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+ do_unassigned_access(addr, 1, 0, 0, 1);
+#endif
+}
+
+static void unassigned_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+ do_unassigned_access(addr, 1, 0, 0, 2);
+#endif
+}
+
+static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
+#endif
+#if defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+ do_unassigned_access(addr, 1, 0, 0, 4);
+#endif
+}
+
+static CPUReadMemoryFunc * const unassigned_mem_read[3] = {
+ unassigned_mem_readb,
+ unassigned_mem_readw,
+ unassigned_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const unassigned_mem_write[3] = {
+ unassigned_mem_writeb,
+ unassigned_mem_writew,
+ unassigned_mem_writel,
+};
+
+static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr,
+ uint32_t val)
+{
+ int dirty_flags;
+ dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
+ if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+ tb_invalidate_phys_page_fast(ram_addr, 1);
+ dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
+#endif
+ }
+ stb_p(qemu_get_ram_ptr(ram_addr), val);
+ dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+ cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags);
+ /* we remove the notdirty callback only if the code has been
+ flushed */
+ if (dirty_flags == 0xff)
+ tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
+}
+
+static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr,
+ uint32_t val)
+{
+ int dirty_flags;
+ dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
+ if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+ tb_invalidate_phys_page_fast(ram_addr, 2);
+ dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
+#endif
+ }
+ stw_p(qemu_get_ram_ptr(ram_addr), val);
+ dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+ cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags);
+ /* we remove the notdirty callback only if the code has been
+ flushed */
+ if (dirty_flags == 0xff)
+ tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
+}
+
+static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr,
+ uint32_t val)
+{
+ int dirty_flags;
+ dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
+ if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+ tb_invalidate_phys_page_fast(ram_addr, 4);
+ dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr);
+#endif
+ }
+ stl_p(qemu_get_ram_ptr(ram_addr), val);
+ dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+ cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags);
+ /* we remove the notdirty callback only if the code has been
+ flushed */
+ if (dirty_flags == 0xff)
+ tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
+}
+
+static CPUReadMemoryFunc * const error_mem_read[3] = {
+ NULL, /* never used */
+ NULL, /* never used */
+ NULL, /* never used */
+};
+
+static CPUWriteMemoryFunc * const notdirty_mem_write[3] = {
+ notdirty_mem_writeb,
+ notdirty_mem_writew,
+ notdirty_mem_writel,
+};
+
+/* Generate a debug exception if a watchpoint has been hit. */
+static void check_watchpoint(int offset, int len_mask, int flags)
+{
+ CPUState *env = cpu_single_env;
+ target_ulong pc, cs_base;
+ TranslationBlock *tb;
+ target_ulong vaddr;
+ CPUWatchpoint *wp;
+ int cpu_flags;
+
+ if (env->watchpoint_hit) {
+ /* We re-entered the check after replacing the TB. Now raise
+ * the debug interrupt so that is will trigger after the
+ * current instruction. */
+ cpu_interrupt(env, CPU_INTERRUPT_DEBUG);
+ return;
+ }
+ vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset;
+ QTAILQ_FOREACH(wp, &env->watchpoints, entry) {
+ if ((vaddr == (wp->vaddr & len_mask) ||
+ (vaddr & wp->len_mask) == wp->vaddr) && (wp->flags & flags)) {
+ wp->flags |= BP_WATCHPOINT_HIT;
+ if (!env->watchpoint_hit) {
+ env->watchpoint_hit = wp;
+ tb = tb_find_pc(env->mem_io_pc);
+ if (!tb) {
+ cpu_abort(env, "check_watchpoint: could not find TB for "
+ "pc=%p", (void *)env->mem_io_pc);
+ }
+ cpu_restore_state(tb, env, env->mem_io_pc, NULL);
+ tb_phys_invalidate(tb, -1);
+ if (wp->flags & BP_STOP_BEFORE_ACCESS) {
+ env->exception_index = EXCP_DEBUG;
+ } else {
+ cpu_get_tb_cpu_state(env, &pc, &cs_base, &cpu_flags);
+ tb_gen_code(env, pc, cs_base, cpu_flags, 1);
+ }
+ cpu_resume_from_signal(env, NULL);
+ }
+ } else {
+ wp->flags &= ~BP_WATCHPOINT_HIT;
+ }
+ }
+}
+
+/* Watchpoint access routines. Watchpoints are inserted using TLB tricks,
+ so these check for a hit then pass through to the normal out-of-line
+ phys routines. */
+static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x0, BP_MEM_READ);
+ return ldub_phys(addr);
+}
+
+static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x1, BP_MEM_READ);
+ return lduw_phys(addr);
+}
+
+static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x3, BP_MEM_READ);
+ return ldl_phys(addr);
+}
+
+static void watch_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x0, BP_MEM_WRITE);
+ stb_phys(addr, val);
+}
+
+static void watch_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x1, BP_MEM_WRITE);
+ stw_phys(addr, val);
+}
+
+static void watch_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ check_watchpoint(addr & ~TARGET_PAGE_MASK, ~0x3, BP_MEM_WRITE);
+ stl_phys(addr, val);
+}
+
+static CPUReadMemoryFunc * const watch_mem_read[3] = {
+ watch_mem_readb,
+ watch_mem_readw,
+ watch_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const watch_mem_write[3] = {
+ watch_mem_writeb,
+ watch_mem_writew,
+ watch_mem_writel,
+};
+
+static inline uint32_t subpage_readlen (subpage_t *mmio,
+ target_phys_addr_t addr,
+ unsigned int len)
+{
+ unsigned int idx = SUBPAGE_IDX(addr);
+#if defined(DEBUG_SUBPAGE)
+ printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__,
+ mmio, len, addr, idx);
+#endif
+
+ addr += mmio->region_offset[idx];
+ idx = mmio->sub_io_index[idx];
+ return io_mem_read[idx][len](io_mem_opaque[idx], addr);
+}
+
+static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr,
+ uint32_t value, unsigned int len)
+{
+ unsigned int idx = SUBPAGE_IDX(addr);
+#if defined(DEBUG_SUBPAGE)
+ printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n",
+ __func__, mmio, len, addr, idx, value);
+#endif
+
+ addr += mmio->region_offset[idx];
+ idx = mmio->sub_io_index[idx];
+ io_mem_write[idx][len](io_mem_opaque[idx], addr, value);
+}
+
+static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr)
+{
+ return subpage_readlen(opaque, addr, 0);
+}
+
+static void subpage_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ subpage_writelen(opaque, addr, value, 0);
+}
+
+static uint32_t subpage_readw (void *opaque, target_phys_addr_t addr)
+{
+ return subpage_readlen(opaque, addr, 1);
+}
+
+static void subpage_writew (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ subpage_writelen(opaque, addr, value, 1);
+}
+
+static uint32_t subpage_readl (void *opaque, target_phys_addr_t addr)
+{
+ return subpage_readlen(opaque, addr, 2);
+}
+
+static void subpage_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ subpage_writelen(opaque, addr, value, 2);
+}
+
+static CPUReadMemoryFunc * const subpage_read[] = {
+ &subpage_readb,
+ &subpage_readw,
+ &subpage_readl,
+};
+
+static CPUWriteMemoryFunc * const subpage_write[] = {
+ &subpage_writeb,
+ &subpage_writew,
+ &subpage_writel,
+};
+
+static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
+ ram_addr_t memory, ram_addr_t region_offset)
+{
+ int idx, eidx;
+
+ if (start >= TARGET_PAGE_SIZE || end >= TARGET_PAGE_SIZE)
+ return -1;
+ idx = SUBPAGE_IDX(start);
+ eidx = SUBPAGE_IDX(end);
+#if defined(DEBUG_SUBPAGE)
+ printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__,
+ mmio, start, end, idx, eidx, memory);
+#endif
+ if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM)
+ memory = IO_MEM_UNASSIGNED;
+ memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ for (; idx <= eidx; idx++) {
+ mmio->sub_io_index[idx] = memory;
+ mmio->region_offset[idx] = region_offset;
+ }
+
+ return 0;
+}
+
+static subpage_t *subpage_init (target_phys_addr_t base, ram_addr_t *phys,
+ ram_addr_t orig_memory,
+ ram_addr_t region_offset)
+{
+ subpage_t *mmio;
+ int subpage_memory;
+
+ mmio = qemu_mallocz(sizeof(subpage_t));
+
+ mmio->base = base;
+ subpage_memory = cpu_register_io_memory(subpage_read, subpage_write, mmio,
+ DEVICE_NATIVE_ENDIAN);
+#if defined(DEBUG_SUBPAGE)
+ printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__,
+ mmio, base, TARGET_PAGE_SIZE, subpage_memory);
+#endif
+ *phys = subpage_memory | IO_MEM_SUBPAGE;
+ subpage_register(mmio, 0, TARGET_PAGE_SIZE-1, orig_memory, region_offset);
+
+ return mmio;
+}
+
+static int get_free_io_mem_idx(void)
+{
+ int i;
+
+ for (i = 0; i<IO_MEM_NB_ENTRIES; i++)
+ if (!io_mem_used[i]) {
+ io_mem_used[i] = 1;
+ return i;
+ }
+ fprintf(stderr, "RAN out out io_mem_idx, max %d !\n", IO_MEM_NB_ENTRIES);
+ return -1;
+}
+
+/*
+ * Usually, devices operate in little endian mode. There are devices out
+ * there that operate in big endian too. Each device gets byte swapped
+ * mmio if plugged onto a CPU that does the other endianness.
+ *
+ * CPU Device swap?
+ *
+ * little little no
+ * little big yes
+ * big little yes
+ * big big no
+ */
+
+typedef struct SwapEndianContainer {
+ CPUReadMemoryFunc *read[3];
+ CPUWriteMemoryFunc *write[3];
+ void *opaque;
+} SwapEndianContainer;
+
+static uint32_t swapendian_mem_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ SwapEndianContainer *c = opaque;
+ val = c->read[0](c->opaque, addr);
+ return val;
+}
+
+static uint32_t swapendian_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ SwapEndianContainer *c = opaque;
+ val = bswap16(c->read[1](c->opaque, addr));
+ return val;
+}
+
+static uint32_t swapendian_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ SwapEndianContainer *c = opaque;
+ val = bswap32(c->read[2](c->opaque, addr));
+ return val;
+}
+
+static CPUReadMemoryFunc * const swapendian_readfn[3]={
+ swapendian_mem_readb,
+ swapendian_mem_readw,
+ swapendian_mem_readl
+};
+
+static void swapendian_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SwapEndianContainer *c = opaque;
+ c->write[0](c->opaque, addr, val);
+}
+
+static void swapendian_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SwapEndianContainer *c = opaque;
+ c->write[1](c->opaque, addr, bswap16(val));
+}
+
+static void swapendian_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SwapEndianContainer *c = opaque;
+ c->write[2](c->opaque, addr, bswap32(val));
+}
+
+static CPUWriteMemoryFunc * const swapendian_writefn[3]={
+ swapendian_mem_writeb,
+ swapendian_mem_writew,
+ swapendian_mem_writel
+};
+
+static void swapendian_init(int io_index)
+{
+ SwapEndianContainer *c = qemu_malloc(sizeof(SwapEndianContainer));
+ int i;
+
+ /* Swap mmio for big endian targets */
+ c->opaque = io_mem_opaque[io_index];
+ for (i = 0; i < 3; i++) {
+ c->read[i] = io_mem_read[io_index][i];
+ c->write[i] = io_mem_write[io_index][i];
+
+ io_mem_read[io_index][i] = swapendian_readfn[i];
+ io_mem_write[io_index][i] = swapendian_writefn[i];
+ }
+ io_mem_opaque[io_index] = c;
+}
+
+static void swapendian_del(int io_index)
+{
+ if (io_mem_read[io_index][0] == swapendian_readfn[0]) {
+ qemu_free(io_mem_opaque[io_index]);
+ }
+}
+
+/* mem_read and mem_write are arrays of functions containing the
+ function to access byte (index 0), word (index 1) and dword (index
+ 2). Functions can be omitted with a NULL function pointer.
+ If io_index is non zero, the corresponding io zone is
+ modified. If it is zero, a new io zone is allocated. The return
+ value can be used with cpu_register_physical_memory(). (-1) is
+ returned if error. */
+static int cpu_register_io_memory_fixed(int io_index,
+ CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write,
+ void *opaque, enum device_endian endian)
+{
+ int i;
+
+ if (io_index <= 0) {
+ io_index = get_free_io_mem_idx();
+ if (io_index == -1)
+ return io_index;
+ } else {
+ io_index >>= IO_MEM_SHIFT;
+ if (io_index >= IO_MEM_NB_ENTRIES)
+ return -1;
+ }
+
+ for (i = 0; i < 3; ++i) {
+ io_mem_read[io_index][i]
+ = (mem_read[i] ? mem_read[i] : unassigned_mem_read[i]);
+ }
+ for (i = 0; i < 3; ++i) {
+ io_mem_write[io_index][i]
+ = (mem_write[i] ? mem_write[i] : unassigned_mem_write[i]);
+ }
+ io_mem_opaque[io_index] = opaque;
+
+ switch (endian) {
+ case DEVICE_BIG_ENDIAN:
+#ifndef TARGET_WORDS_BIGENDIAN
+ swapendian_init(io_index);
+#endif
+ break;
+ case DEVICE_LITTLE_ENDIAN:
+#ifdef TARGET_WORDS_BIGENDIAN
+ swapendian_init(io_index);
+#endif
+ break;
+ case DEVICE_NATIVE_ENDIAN:
+ default:
+ break;
+ }
+
+ return (io_index << IO_MEM_SHIFT);
+}
+
+int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write,
+ void *opaque, enum device_endian endian)
+{
+ return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque, endian);
+}
+
+void cpu_unregister_io_memory(int io_table_address)
+{
+ int i;
+ int io_index = io_table_address >> IO_MEM_SHIFT;
+
+ swapendian_del(io_index);
+
+ for (i=0;i < 3; i++) {
+ io_mem_read[io_index][i] = unassigned_mem_read[i];
+ io_mem_write[io_index][i] = unassigned_mem_write[i];
+ }
+ io_mem_opaque[io_index] = NULL;
+ io_mem_used[io_index] = 0;
+}
+
+static void io_mem_init(void)
+{
+ int i;
+
+ cpu_register_io_memory_fixed(IO_MEM_ROM, error_mem_read,
+ unassigned_mem_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_io_memory_fixed(IO_MEM_UNASSIGNED, unassigned_mem_read,
+ unassigned_mem_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read,
+ notdirty_mem_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ for (i=0; i<5; i++)
+ io_mem_used[i] = 1;
+
+ io_mem_watch = cpu_register_io_memory(watch_mem_read,
+ watch_mem_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+}
+
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+/* physical memory access (slow version, mainly for debug) */
+#if defined(CONFIG_USER_ONLY)
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+ uint8_t *buf, int len, int is_write)
+{
+ int l, flags;
+ target_ulong page;
+ void * p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ flags = page_get_flags(page);
+ if (!(flags & PAGE_VALID))
+ return -1;
+ if (is_write) {
+ if (!(flags & PAGE_WRITE))
+ return -1;
+ /* XXX: this code should not depend on lock_user */
+ if (!(p = lock_user(VERIFY_WRITE, addr, l, 0)))
+ return -1;
+ memcpy(p, buf, l);
+ unlock_user(p, addr, l);
+ } else {
+ if (!(flags & PAGE_READ))
+ return -1;
+ /* XXX: this code should not depend on lock_user */
+ if (!(p = lock_user(VERIFY_READ, addr, l, 1)))
+ return -1;
+ memcpy(buf, p, l);
+ unlock_user(p, addr, 0);
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+ return 0;
+}
+
+#else
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+ int len, int is_write)
+{
+ int l, io_index;
+ uint8_t *ptr;
+ uint32_t val;
+ target_phys_addr_t page;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ p = phys_page_find(page >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if (is_write) {
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ target_phys_addr_t addr1 = addr;
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr1 = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ /* XXX: could force cpu_single_env to NULL to avoid
+ potential bugs */
+ if (l >= 4 && ((addr1 & 3) == 0)) {
+ /* 32 bit write access */
+ val = ldl_p(buf);
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr1, val);
+ l = 4;
+ } else if (l >= 2 && ((addr1 & 1) == 0)) {
+ /* 16 bit write access */
+ val = lduw_p(buf);
+ io_mem_write[io_index][1](io_mem_opaque[io_index], addr1, val);
+ l = 2;
+ } else {
+ /* 8 bit write access */
+ val = ldub_p(buf);
+ io_mem_write[io_index][0](io_mem_opaque[io_index], addr1, val);
+ l = 1;
+ }
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(addr1);
+ memcpy(ptr, buf, l);
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + l, 0);
+ /* set dirty bit */
+ cpu_physical_memory_set_dirty_flags(
+ addr1, (0xff & ~CODE_DIRTY_FLAG));
+ }
+ /* qemu doesn't execute guest code directly, but kvm does
+ therefore flush instruction caches */
+ if (kvm_enabled())
+ flush_icache_range((unsigned long)ptr,
+ ((unsigned long)ptr)+l);
+ }
+ } else {
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ target_phys_addr_t addr1 = addr;
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr1 = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ if (l >= 4 && ((addr1 & 3) == 0)) {
+ /* 32 bit read access */
+ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr1);
+ stl_p(buf, val);
+ l = 4;
+ } else if (l >= 2 && ((addr1 & 1) == 0)) {
+ /* 16 bit read access */
+ val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr1);
+ stw_p(buf, val);
+ l = 2;
+ } else {
+ /* 8 bit read access */
+ val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr1);
+ stb_p(buf, val);
+ l = 1;
+ }
+ } else {
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ memcpy(buf, ptr, l);
+ }
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+}
+
+/* used for ROM loading : can write in RAM and ROM */
+void cpu_physical_memory_write_rom(target_phys_addr_t addr,
+ const uint8_t *buf, int len)
+{
+ int l;
+ uint8_t *ptr;
+ target_phys_addr_t page;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ p = phys_page_find(page >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM &&
+ (pd & ~TARGET_PAGE_MASK) != IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* do nothing */
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* ROM/RAM case */
+ ptr = qemu_get_ram_ptr(addr1);
+ memcpy(ptr, buf, l);
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+}
+
+typedef struct {
+ void *buffer;
+ target_phys_addr_t addr;
+ target_phys_addr_t len;
+} BounceBuffer;
+
+static BounceBuffer bounce;
+
+typedef struct MapClient {
+ void *opaque;
+ void (*callback)(void *opaque);
+ QLIST_ENTRY(MapClient) link;
+} MapClient;
+
+static QLIST_HEAD(map_client_list, MapClient) map_client_list
+ = QLIST_HEAD_INITIALIZER(map_client_list);
+
+void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque))
+{
+ MapClient *client = qemu_malloc(sizeof(*client));
+
+ client->opaque = opaque;
+ client->callback = callback;
+ QLIST_INSERT_HEAD(&map_client_list, client, link);
+ return client;
+}
+
+void cpu_unregister_map_client(void *_client)
+{
+ MapClient *client = (MapClient *)_client;
+
+ QLIST_REMOVE(client, link);
+ qemu_free(client);
+}
+
+static void cpu_notify_map_clients(void)
+{
+ MapClient *client;
+
+ while (!QLIST_EMPTY(&map_client_list)) {
+ client = QLIST_FIRST(&map_client_list);
+ client->callback(client->opaque);
+ cpu_unregister_map_client(client);
+ }
+}
+
+/* Map a physical memory region into a host virtual address.
+ * May map a subset of the requested range, given by and returned in *plen.
+ * May return NULL if resources needed to perform the mapping are exhausted.
+ * Use only for reads OR writes - not for read-modify-write operations.
+ * Use cpu_register_map_client() to know when retrying the map operation is
+ * likely to succeed.
+ */
+void *cpu_physical_memory_map(target_phys_addr_t addr,
+ target_phys_addr_t *plen,
+ int is_write)
+{
+ target_phys_addr_t len = *plen;
+ target_phys_addr_t done = 0;
+ int l;
+ uint8_t *ret = NULL;
+ uint8_t *ptr;
+ target_phys_addr_t page;
+ unsigned long pd;
+ PhysPageDesc *p;
+ unsigned long addr1;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ p = phys_page_find(page >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ if (done || bounce.buffer) {
+ break;
+ }
+ bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE);
+ bounce.addr = addr;
+ bounce.len = l;
+ if (!is_write) {
+ cpu_physical_memory_rw(addr, bounce.buffer, l, 0);
+ }
+ ptr = bounce.buffer;
+ } else {
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ ptr = qemu_get_ram_ptr(addr1);
+ }
+ if (!done) {
+ ret = ptr;
+ } else if (ret + done != ptr) {
+ break;
+ }
+
+ len -= l;
+ addr += l;
+ done += l;
+ }
+ *plen = done;
+ return ret;
+}
+
+/* Unmaps a memory region previously mapped by cpu_physical_memory_map().
+ * Will also mark the memory as dirty if is_write == 1. access_len gives
+ * the amount of memory that was actually read or written by the caller.
+ */
+void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
+ int is_write, target_phys_addr_t access_len)
+{
+ unsigned long flush_len = (unsigned long)access_len;
+
+ if (buffer != bounce.buffer) {
+ if (is_write) {
+ ram_addr_t addr1 = qemu_ram_addr_from_host_nofail(buffer);
+ while (access_len) {
+ unsigned l;
+ l = TARGET_PAGE_SIZE;
+ if (l > access_len)
+ l = access_len;
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + l, 0);
+ /* set dirty bit */
+ cpu_physical_memory_set_dirty_flags(
+ addr1, (0xff & ~CODE_DIRTY_FLAG));
+ }
+ addr1 += l;
+ access_len -= l;
+ }
+ dma_flush_range((unsigned long)buffer,
+ (unsigned long)buffer + flush_len);
+ }
+ return;
+ }
+ if (is_write) {
+ cpu_physical_memory_write(bounce.addr, bounce.buffer, access_len);
+ }
+ qemu_vfree(bounce.buffer);
+ bounce.buffer = NULL;
+ cpu_notify_map_clients();
+}
+
+/* warning: addr must be aligned */
+uint32_t ldl_phys(target_phys_addr_t addr)
+{
+ int io_index;
+ uint8_t *ptr;
+ uint32_t val;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+ } else {
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ val = ldl_p(ptr);
+ }
+ return val;
+}
+
+/* warning: addr must be aligned */
+uint64_t ldq_phys(target_phys_addr_t addr)
+{
+ int io_index;
+ uint8_t *ptr;
+ uint64_t val;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32;
+ val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4);
+#else
+ val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+ val |= (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4) << 32;
+#endif
+ } else {
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ val = ldq_p(ptr);
+ }
+ return val;
+}
+
+/* XXX: optimize */
+uint32_t ldub_phys(target_phys_addr_t addr)
+{
+ uint8_t val;
+ cpu_physical_memory_read(addr, &val, 1);
+ return val;
+}
+
+/* warning: addr must be aligned */
+uint32_t lduw_phys(target_phys_addr_t addr)
+{
+ int io_index;
+ uint8_t *ptr;
+ uint64_t val;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+ !(pd & IO_MEM_ROMD)) {
+ /* I/O case */
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr);
+ } else {
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ val = lduw_p(ptr);
+ }
+ return val;
+}
+
+/* warning: addr must be aligned. The ram page is not masked as dirty
+ and the code inside is not invalidated. It is useful if the dirty
+ bits are used to track modified PTEs */
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val)
+{
+ int io_index;
+ uint8_t *ptr;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+ } else {
+ unsigned long addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ ptr = qemu_get_ram_ptr(addr1);
+ stl_p(ptr, val);
+
+ if (unlikely(in_migration)) {
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
+ /* set dirty bit */
+ cpu_physical_memory_set_dirty_flags(
+ addr1, (0xff & ~CODE_DIRTY_FLAG));
+ }
+ }
+ }
+}
+
+void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val)
+{
+ int io_index;
+ uint8_t *ptr;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+#ifdef TARGET_WORDS_BIGENDIAN
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val >> 32);
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr + 4, val);
+#else
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr + 4, val >> 32);
+#endif
+ } else {
+ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
+ (addr & ~TARGET_PAGE_MASK);
+ stq_p(ptr, val);
+ }
+}
+
+/* warning: addr must be aligned */
+void stl_phys(target_phys_addr_t addr, uint32_t val)
+{
+ int io_index;
+ uint8_t *ptr;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(addr1);
+ stl_p(ptr, val);
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
+ /* set dirty bit */
+ cpu_physical_memory_set_dirty_flags(addr1,
+ (0xff & ~CODE_DIRTY_FLAG));
+ }
+ }
+}
+
+/* XXX: optimize */
+void stb_phys(target_phys_addr_t addr, uint32_t val)
+{
+ uint8_t v = val;
+ cpu_physical_memory_write(addr, &v, 1);
+}
+
+/* warning: addr must be aligned */
+void stw_phys(target_phys_addr_t addr, uint32_t val)
+{
+ int io_index;
+ uint8_t *ptr;
+ unsigned long pd;
+ PhysPageDesc *p;
+
+ p = phys_page_find(addr >> TARGET_PAGE_BITS);
+ if (!p) {
+ pd = IO_MEM_UNASSIGNED;
+ } else {
+ pd = p->phys_offset;
+ }
+
+ if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+ io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ if (p)
+ addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset;
+ io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val);
+ } else {
+ unsigned long addr1;
+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+ /* RAM case */
+ ptr = qemu_get_ram_ptr(addr1);
+ stw_p(ptr, val);
+ if (!cpu_physical_memory_is_dirty(addr1)) {
+ /* invalidate code */
+ tb_invalidate_phys_page_range(addr1, addr1 + 2, 0);
+ /* set dirty bit */
+ cpu_physical_memory_set_dirty_flags(addr1,
+ (0xff & ~CODE_DIRTY_FLAG));
+ }
+ }
+}
+
+/* XXX: optimize */
+void stq_phys(target_phys_addr_t addr, uint64_t val)
+{
+ val = tswap64(val);
+ cpu_physical_memory_write(addr, (const uint8_t *)&val, 8);
+}
+
+/* virtual memory access for debug (includes writing to ROM) */
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+ uint8_t *buf, int len, int is_write)
+{
+ int l;
+ target_phys_addr_t phys_addr;
+ target_ulong page;
+
+ while (len > 0) {
+ page = addr & TARGET_PAGE_MASK;
+ phys_addr = cpu_get_phys_page_debug(env, page);
+ /* if no physical page mapped, return an error */
+ if (phys_addr == -1)
+ return -1;
+ l = (page + TARGET_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ phys_addr += (addr & ~TARGET_PAGE_MASK);
+ if (is_write)
+ cpu_physical_memory_write_rom(phys_addr, buf, l);
+ else
+ cpu_physical_memory_rw(phys_addr, buf, l, is_write);
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+ return 0;
+}
+#endif
+
+/* in deterministic execution mode, instructions doing device I/Os
+ must be at the end of the TB */
+void cpu_io_recompile(CPUState *env, void *retaddr)
+{
+ TranslationBlock *tb;
+ uint32_t n, cflags;
+ target_ulong pc, cs_base;
+ uint64_t flags;
+
+ tb = tb_find_pc((unsigned long)retaddr);
+ if (!tb) {
+ cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p",
+ retaddr);
+ }
+ n = env->icount_decr.u16.low + tb->icount;
+ cpu_restore_state(tb, env, (unsigned long)retaddr, NULL);
+ /* Calculate how many instructions had been executed before the fault
+ occurred. */
+ n = n - env->icount_decr.u16.low;
+ /* Generate a new TB ending on the I/O insn. */
+ n++;
+ /* On MIPS and SH, delay slot instructions can only be restarted if
+ they were already the first instruction in the TB. If this is not
+ the first instruction in a TB then re-execute the preceding
+ branch. */
+#if defined(TARGET_MIPS)
+ if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) {
+ env->active_tc.PC -= 4;
+ env->icount_decr.u16.low++;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ }
+#elif defined(TARGET_SH4)
+ if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
+ && n > 1) {
+ env->pc -= 2;
+ env->icount_decr.u16.low++;
+ env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
+ }
+#endif
+ /* This should never happen. */
+ if (n > CF_COUNT_MASK)
+ cpu_abort(env, "TB too big during recompile");
+
+ cflags = n | CF_LAST_IO;
+ pc = tb->pc;
+ cs_base = tb->cs_base;
+ flags = tb->flags;
+ tb_phys_invalidate(tb, -1);
+ /* FIXME: In theory this could raise an exception. In practice
+ we have already translated the block once so it's probably ok. */
+ tb_gen_code(env, pc, cs_base, flags, cflags);
+ /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
+ the first in the TB) then we end up generating a whole new TB and
+ repeating the fault, which is horribly inefficient.
+ Better would be to execute just this insn uncached, or generate a
+ second new TB. */
+ cpu_resume_from_signal(env, NULL);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
+{
+ int i, target_code_size, max_target_code_size;
+ int direct_jmp_count, direct_jmp2_count, cross_page;
+ TranslationBlock *tb;
+
+ target_code_size = 0;
+ max_target_code_size = 0;
+ cross_page = 0;
+ direct_jmp_count = 0;
+ direct_jmp2_count = 0;
+ for(i = 0; i < nb_tbs; i++) {
+ tb = &tbs[i];
+ target_code_size += tb->size;
+ if (tb->size > max_target_code_size)
+ max_target_code_size = tb->size;
+ if (tb->page_addr[1] != -1)
+ cross_page++;
+ if (tb->tb_next_offset[0] != 0xffff) {
+ direct_jmp_count++;
+ if (tb->tb_next_offset[1] != 0xffff) {
+ direct_jmp2_count++;
+ }
+ }
+ }
+ /* XXX: avoid using doubles ? */
+ cpu_fprintf(f, "Translation buffer state:\n");
+ cpu_fprintf(f, "gen code size %td/%ld\n",
+ code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size);
+ cpu_fprintf(f, "TB count %d/%d\n",
+ nb_tbs, code_gen_max_blocks);
+ cpu_fprintf(f, "TB avg target size %d max=%d bytes\n",
+ nb_tbs ? target_code_size / nb_tbs : 0,
+ max_target_code_size);
+ cpu_fprintf(f, "TB avg host size %td bytes (expansion ratio: %0.1f)\n",
+ nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0,
+ target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0);
+ cpu_fprintf(f, "cross page TB count %d (%d%%)\n",
+ cross_page,
+ nb_tbs ? (cross_page * 100) / nb_tbs : 0);
+ cpu_fprintf(f, "direct jump count %d (%d%%) (2 jumps=%d %d%%)\n",
+ direct_jmp_count,
+ nb_tbs ? (direct_jmp_count * 100) / nb_tbs : 0,
+ direct_jmp2_count,
+ nb_tbs ? (direct_jmp2_count * 100) / nb_tbs : 0);
+ cpu_fprintf(f, "\nStatistics:\n");
+ cpu_fprintf(f, "TB flush count %d\n", tb_flush_count);
+ cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count);
+ cpu_fprintf(f, "TLB flush count %d\n", tlb_flush_count);
+#ifdef CONFIG_PROFILER
+ tcg_dump_info(f, cpu_fprintf);
+#endif
+}
+
+#define MMUSUFFIX _cmmu
+#define GETPC() NULL
+#define env cpu_single_env
+#define SOFTMMU_CODE_ACCESS
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#undef env
+
+#endif
diff --git a/fpu/softfloat-macros.h b/fpu/softfloat-macros.h
new file mode 100644
index 0000000..7838228
--- /dev/null
+++ b/fpu/softfloat-macros.h
@@ -0,0 +1,719 @@
+
+/*============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+/*----------------------------------------------------------------------------
+| Shifts `a' right by the number of bits given in `count'. If any nonzero
+| bits are shifted off, they are ``jammed'' into the least significant bit of
+| the result by setting the least significant bit to 1. The value of `count'
+| can be arbitrarily large; in particular, if `count' is greater than 32, the
+| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+| The result is stored in the location pointed to by `zPtr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
+{
+ bits32 z;
+
+ if ( count == 0 ) {
+ z = a;
+ }
+ else if ( count < 32 ) {
+ z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
+ }
+ else {
+ z = ( a != 0 );
+ }
+ *zPtr = z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts `a' right by the number of bits given in `count'. If any nonzero
+| bits are shifted off, they are ``jammed'' into the least significant bit of
+| the result by setting the least significant bit to 1. The value of `count'
+| can be arbitrarily large; in particular, if `count' is greater than 64, the
+| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+| The result is stored in the location pointed to by `zPtr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
+{
+ bits64 z;
+
+ if ( count == 0 ) {
+ z = a;
+ }
+ else if ( count < 64 ) {
+ z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
+ }
+ else {
+ z = ( a != 0 );
+ }
+ *zPtr = z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
+| _plus_ the number of bits given in `count'. The shifted result is at most
+| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
+| bits shifted off form a second 64-bit result as follows: The _last_ bit
+| shifted off is the most-significant bit of the extra result, and the other
+| 63 bits of the extra result are all zero if and only if _all_but_the_last_
+| bits shifted off were all zero. This extra result is stored in the location
+| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
+| (This routine makes more sense if `a0' and `a1' are considered to form
+| a fixed-point value with binary point between `a0' and `a1'. This fixed-
+| point value is shifted right by the number of bits given in `count', and
+| the integer part of the result is returned at the location pointed to by
+| `z0Ptr'. The fractional part of the result may be slightly corrupted as
+| described above, and is returned at the location pointed to by `z1Ptr'.)
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift64ExtraRightJamming(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1 != 0 );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z1 = a0 | ( a1 != 0 );
+ }
+ else {
+ z1 = ( ( a0 | a1 ) != 0 );
+ }
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+| number of bits given in `count'. Any bits shifted off are lost. The value
+| of `count' can be arbitrarily large; in particular, if `count' is greater
+| than 128, the result will be 0. The result is broken into two 64-bit pieces
+| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128Right(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1>>count );
+ z0 = a0>>count;
+ }
+ else {
+ z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+| number of bits given in `count'. If any nonzero bits are shifted off, they
+| are ``jammed'' into the least significant bit of the result by setting the
+| least significant bit to 1. The value of `count' can be arbitrarily large;
+| in particular, if `count' is greater than 128, the result will be either
+| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or
+| nonzero. The result is broken into two 64-bit pieces which are stored at
+| the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128RightJamming(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z0, z1;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z1 = a1;
+ z0 = a0;
+ }
+ else if ( count < 64 ) {
+ z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z1 = a0 | ( a1 != 0 );
+ }
+ else if ( count < 128 ) {
+ z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
+ }
+ else {
+ z1 = ( ( a0 | a1 ) != 0 );
+ }
+ z0 = 0;
+ }
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
+| by 64 _plus_ the number of bits given in `count'. The shifted result is
+| at most 128 nonzero bits; these are broken into two 64-bit pieces which are
+| stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
+| off form a third 64-bit result as follows: The _last_ bit shifted off is
+| the most-significant bit of the extra result, and the other 63 bits of the
+| extra result are all zero if and only if _all_but_the_last_ bits shifted off
+| were all zero. This extra result is stored in the location pointed to by
+| `z2Ptr'. The value of `count' can be arbitrarily large.
+| (This routine makes more sense if `a0', `a1', and `a2' are considered
+| to form a fixed-point value with binary point between `a1' and `a2'. This
+| fixed-point value is shifted right by the number of bits given in `count',
+| and the integer part of the result is returned at the locations pointed to
+| by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
+| corrupted as described above, and is returned at the location pointed to by
+| `z2Ptr'.)
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128ExtraRightJamming(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ int16 count,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 negCount = ( - count ) & 63;
+
+ if ( count == 0 ) {
+ z2 = a2;
+ z1 = a1;
+ z0 = a0;
+ }
+ else {
+ if ( count < 64 ) {
+ z2 = a1<<negCount;
+ z1 = ( a0<<negCount ) | ( a1>>count );
+ z0 = a0>>count;
+ }
+ else {
+ if ( count == 64 ) {
+ z2 = a1;
+ z1 = a0;
+ }
+ else {
+ a2 |= a1;
+ if ( count < 128 ) {
+ z2 = a0<<negCount;
+ z1 = a0>>( count & 63 );
+ }
+ else {
+ z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
+ z1 = 0;
+ }
+ }
+ z0 = 0;
+ }
+ z2 |= ( a2 != 0 );
+ }
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
+| number of bits given in `count'. Any bits shifted off are lost. The value
+| of `count' must be less than 64. The result is broken into two 64-bit
+| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shortShift128Left(
+ bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+ *z1Ptr = a1<<count;
+ *z0Ptr =
+ ( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
+| by the number of bits given in `count'. Any bits shifted off are lost.
+| The value of `count' must be less than 64. The result is broken into three
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+| `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shortShift192Left(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ int16 count,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 negCount;
+
+ z2 = a2<<count;
+ z1 = a1<<count;
+ z0 = a0<<count;
+ if ( 0 < count ) {
+ negCount = ( ( - count ) & 63 );
+ z1 |= a2>>negCount;
+ z0 |= a1>>negCount;
+ }
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
+| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
+| any carry out is lost. The result is broken into two 64-bit pieces which
+| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ add128(
+ bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits64 z1;
+
+ z1 = a1 + b1;
+ *z1Ptr = z1;
+ *z0Ptr = a0 + b0 + ( z1 < a1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
+| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
+| modulo 2^192, so any carry out is lost. The result is broken into three
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+| `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ add192(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ bits64 b0,
+ bits64 b1,
+ bits64 b2,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 carry0, carry1;
+
+ z2 = a2 + b2;
+ carry1 = ( z2 < a2 );
+ z1 = a1 + b1;
+ carry0 = ( z1 < a1 );
+ z0 = a0 + b0;
+ z1 += carry1;
+ z0 += ( z1 < carry1 );
+ z0 += carry0;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
+| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
+| 2^128, so any borrow out (carry out) is lost. The result is broken into two
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
+| `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ sub128(
+ bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+ *z1Ptr = a1 - b1;
+ *z0Ptr = a0 - b0 - ( a1 < b1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
+| from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
+| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
+| result is broken into three 64-bit pieces which are stored at the locations
+| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ sub192(
+ bits64 a0,
+ bits64 a1,
+ bits64 a2,
+ bits64 b0,
+ bits64 b1,
+ bits64 b2,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2;
+ int8 borrow0, borrow1;
+
+ z2 = a2 - b2;
+ borrow1 = ( a2 < b2 );
+ z1 = a1 - b1;
+ borrow0 = ( a1 < b1 );
+ z0 = a0 - b0;
+ z0 -= ( z1 < borrow1 );
+ z1 -= borrow1;
+ z0 -= borrow0;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
+| into two 64-bit pieces which are stored at the locations pointed to by
+| `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+ bits32 aHigh, aLow, bHigh, bLow;
+ bits64 z0, zMiddleA, zMiddleB, z1;
+
+ aLow = a;
+ aHigh = a>>32;
+ bLow = b;
+ bHigh = b>>32;
+ z1 = ( (bits64) aLow ) * bLow;
+ zMiddleA = ( (bits64) aLow ) * bHigh;
+ zMiddleB = ( (bits64) aHigh ) * bLow;
+ z0 = ( (bits64) aHigh ) * bHigh;
+ zMiddleA += zMiddleB;
+ z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
+ zMiddleA <<= 32;
+ z1 += zMiddleA;
+ z0 += ( z1 < zMiddleA );
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by
+| `b' to obtain a 192-bit product. The product is broken into three 64-bit
+| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
+| `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ mul128By64To192(
+ bits64 a0,
+ bits64 a1,
+ bits64 b,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr
+ )
+{
+ bits64 z0, z1, z2, more1;
+
+ mul64To128( a1, b, &z1, &z2 );
+ mul64To128( a0, b, &z0, &more1 );
+ add128( z0, more1, 0, z1, &z0, &z1 );
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
+| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
+| product. The product is broken into four 64-bit pieces which are stored at
+| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ mul128To256(
+ bits64 a0,
+ bits64 a1,
+ bits64 b0,
+ bits64 b1,
+ bits64 *z0Ptr,
+ bits64 *z1Ptr,
+ bits64 *z2Ptr,
+ bits64 *z3Ptr
+ )
+{
+ bits64 z0, z1, z2, z3;
+ bits64 more1, more2;
+
+ mul64To128( a1, b1, &z2, &z3 );
+ mul64To128( a1, b0, &z1, &more2 );
+ add128( z1, more2, 0, z2, &z1, &z2 );
+ mul64To128( a0, b0, &z0, &more1 );
+ add128( z0, more1, 0, z1, &z0, &z1 );
+ mul64To128( a0, b1, &more1, &more2 );
+ add128( more1, more2, 0, z2, &more1, &z2 );
+ add128( z0, z1, 0, more1, &z0, &z1 );
+ *z3Ptr = z3;
+ *z2Ptr = z2;
+ *z1Ptr = z1;
+ *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns an approximation to the 64-bit integer quotient obtained by dividing
+| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The
+| divisor `b' must be at least 2^63. If q is the exact quotient truncated
+| toward zero, the approximation returned lies between q and q + 2 inclusive.
+| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
+| unsigned integer is returned.
+*----------------------------------------------------------------------------*/
+
+static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
+{
+ bits64 b0, b1;
+ bits64 rem0, rem1, term0, term1;
+ bits64 z;
+
+ if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
+ b0 = b>>32;
+ z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
+ mul64To128( b, z, &term0, &term1 );
+ sub128( a0, a1, term0, term1, &rem0, &rem1 );
+ while ( ( (sbits64) rem0 ) < 0 ) {
+ z -= LIT64( 0x100000000 );
+ b1 = b<<32;
+ add128( rem0, rem1, b0, b1, &rem0, &rem1 );
+ }
+ rem0 = ( rem0<<32 ) | ( rem1>>32 );
+ z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns an approximation to the square root of the 32-bit significand given
+| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
+| `aExp' (the least significant bit) is 1, the integer returned approximates
+| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
+| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
+| case, the approximation returned lies strictly within +/-2 of the exact
+| value.
+*----------------------------------------------------------------------------*/
+
+static bits32 estimateSqrt32( int16 aExp, bits32 a )
+{
+ static const bits16 sqrtOddAdjustments[] = {
+ 0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
+ 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
+ };
+ static const bits16 sqrtEvenAdjustments[] = {
+ 0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
+ 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
+ };
+ int8 index;
+ bits32 z;
+
+ index = ( a>>27 ) & 15;
+ if ( aExp & 1 ) {
+ z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ (int)index ];
+ z = ( ( a / z )<<14 ) + ( z<<15 );
+ a >>= 1;
+ }
+ else {
+ z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ (int)index ];
+ z = a / z + z;
+ z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
+ if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
+ }
+ return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the number of leading 0 bits before the most-significant 1 bit of
+| `a'. If `a' is zero, 32 is returned.
+*----------------------------------------------------------------------------*/
+
+static int8 countLeadingZeros32( bits32 a )
+{
+ static const int8 countLeadingZerosHigh[] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ int8 shiftCount;
+
+ shiftCount = 0;
+ if ( a < 0x10000 ) {
+ shiftCount += 16;
+ a <<= 16;
+ }
+ if ( a < 0x1000000 ) {
+ shiftCount += 8;
+ a <<= 8;
+ }
+ shiftCount += countLeadingZerosHigh[ a>>24 ];
+ return shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the number of leading 0 bits before the most-significant 1 bit of
+| `a'. If `a' is zero, 64 is returned.
+*----------------------------------------------------------------------------*/
+
+static int8 countLeadingZeros64( bits64 a )
+{
+ int8 shiftCount;
+
+ shiftCount = 0;
+ if ( a < ( (bits64) 1 )<<32 ) {
+ shiftCount += 32;
+ }
+ else {
+ a >>= 32;
+ }
+ shiftCount += countLeadingZeros32( a );
+ return shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
+| is equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 == b0 ) && ( a1 == b1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+| than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
+| returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
+| not equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+ return ( a0 != b0 ) || ( a1 != b1 );
+
+}
diff --git a/fpu/softfloat-native.c b/fpu/softfloat-native.c
new file mode 100644
index 0000000..3c7afc0
--- /dev/null
+++ b/fpu/softfloat-native.c
@@ -0,0 +1,515 @@
+/* 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
+#include "config-host.h"
+
+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 ) );
+}
+
+/*----------------------------------------------------------------------------
+| 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 ) < (bits64) ( a<<1 ) );
+
+}
+
+#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 )
+ && (bits64) ( aLow<<1 )
+ && ( u.i.low == aLow );
+}
+
+int floatx80_is_quiet_nan( floatx80 a1 )
+{
+ floatx80u u;
+ u.f = a1;
+ return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 );
+}
+
+#endif
diff --git a/fpu/softfloat-native.h b/fpu/softfloat-native.h
new file mode 100644
index 0000000..80b5f28
--- /dev/null
+++ b/fpu/softfloat-native.h
@@ -0,0 +1,492 @@
+/* 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 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( 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_signaling( 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);
+
+}
+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 );
+
+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)
+{
+ return scalbnf(a, n);
+}
+
+/*----------------------------------------------------------------------------
+| 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( 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_signaling( 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);
+
+}
+int float64_compare( float64, float64 STATUS_PARAM );
+int float64_compare_quiet( float64, float64 STATUS_PARAM );
+int float64_is_signaling_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)
+{
+ return scalbn(a, n);
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| 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( 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_signaling( 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);
+
+}
+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 );
+
+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)
+{
+ return scalbnl(a, n);
+}
+
+#endif
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
new file mode 100644
index 0000000..eb644b2
--- /dev/null
+++ b/fpu/softfloat-specialize.h
@@ -0,0 +1,755 @@
+
+/*============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#if defined(TARGET_MIPS) || defined(TARGET_SH4)
+#define SNAN_BIT_IS_ONE 1
+#else
+#define SNAN_BIT_IS_ONE 0
+#endif
+
+/*----------------------------------------------------------------------------
+| Raises the exceptions specified by `flags'. Floating-point traps can be
+| defined here if desired. It is currently not possible for such a trap
+| to substitute a result value. If traps are not implemented, this routine
+| should be simply `float_exception_flags |= flags;'.
+*----------------------------------------------------------------------------*/
+
+void float_raise( int8 flags STATUS_PARAM )
+{
+ STATUS(float_exception_flags) |= flags;
+}
+
+/*----------------------------------------------------------------------------
+| Internal canonical NaN format.
+*----------------------------------------------------------------------------*/
+typedef struct {
+ flag sign;
+ bits64 high, low;
+} commonNaNT;
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated single-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_SPARC)
+#define float32_default_nan make_float32(0x7FFFFFFF)
+#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
+#define float32_default_nan make_float32(0x7FC00000)
+#elif SNAN_BIT_IS_ONE
+#define float32_default_nan make_float32(0x7FBFFFFF)
+#else
+#define float32_default_nan make_float32(0xFFC00000)
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is a quiet
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float32_is_quiet_nan( float32 a_ )
+{
+ uint32_t a = float32_val(a_);
+#if SNAN_BIT_IS_ONE
+ return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+#else
+ return ( 0xFF800000 <= (bits32) ( a<<1 ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is a signaling
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float32_is_signaling_nan( float32 a_ )
+{
+ uint32_t a = float32_val(a_);
+#if SNAN_BIT_IS_ONE
+ return ( 0xFF800000 <= (bits32) ( a<<1 ) );
+#else
+ return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns a quiet NaN if the single-precision floating point value `a' is a
+| signaling NaN; otherwise returns `a'.
+*----------------------------------------------------------------------------*/
+
+float32 float32_maybe_silence_nan( float32 a_ )
+{
+ if (float32_is_signaling_nan(a_)) {
+#if SNAN_BIT_IS_ONE
+# if defined(TARGET_MIPS) || defined(TARGET_SH4)
+ return float32_default_nan;
+# else
+# error Rules for silencing a signaling NaN are target-specific
+# endif
+#else
+ bits32 a = float32_val(a_);
+ a |= (1 << 22);
+ return make_float32(a);
+#endif
+ }
+ return a_;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point NaN
+| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM )
+{
+ commonNaNT z;
+
+ if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR );
+ z.sign = float32_val(a)>>31;
+ z.low = 0;
+ z.high = ( (bits64) float32_val(a) )<<41;
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the single-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float32 commonNaNToFloat32( commonNaNT a )
+{
+ bits32 mantissa = a.high>>41;
+ if ( mantissa )
+ return make_float32(
+ ( ( (bits32) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) );
+ else
+ return float32_default_nan;
+}
+
+/*----------------------------------------------------------------------------
+| Select which NaN to propagate for a two-input operation.
+| IEEE754 doesn't specify all the details of this, so the
+| algorithm is target-specific.
+| The routine is passed various bits of information about the
+| two NaNs and should return 0 to select NaN a and 1 for NaN b.
+| Note that signalling NaNs are always squashed to quiet NaNs
+| by the caller, by calling floatXX_maybe_silence_nan() before
+| returning them.
+|
+| aIsLargerSignificand is only valid if both a and b are NaNs
+| of some kind, and is true if a has the larger significand,
+| or if both a and b have the same significand but a is
+| positive but b is negative. It is only needed for the x87
+| tie-break rule.
+*----------------------------------------------------------------------------*/
+
+#if defined(TARGET_ARM)
+static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
+ flag aIsLargerSignificand)
+{
+ /* ARM mandated NaN propagation rules: take the first of:
+ * 1. A if it is signaling
+ * 2. B if it is signaling
+ * 3. A (quiet)
+ * 4. B (quiet)
+ * A signaling NaN is always quietened before returning it.
+ */
+ if (aIsSNaN) {
+ return 0;
+ } else if (bIsSNaN) {
+ return 1;
+ } else if (aIsQNaN) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+#elif defined(TARGET_MIPS)
+static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
+ flag aIsLargerSignificand)
+{
+ /* According to MIPS specifications, if one of the two operands is
+ * a sNaN, a new qNaN has to be generated. This is done in
+ * floatXX_maybe_silence_nan(). For qNaN inputs the specifications
+ * says: "When possible, this QNaN result is one of the operand QNaN
+ * values." In practice it seems that most implementations choose
+ * the first operand if both operands are qNaN. In short this gives
+ * the following rules:
+ * 1. A if it is signaling
+ * 2. B if it is signaling
+ * 3. A (quiet)
+ * 4. B (quiet)
+ * A signaling NaN is always silenced before returning it.
+ */
+ if (aIsSNaN) {
+ return 0;
+ } else if (bIsSNaN) {
+ return 1;
+ } else if (aIsQNaN) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+#elif defined(TARGET_PPC)
+static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
+ flag aIsLargerSignificand)
+{
+ /* PowerPC propagation rules:
+ * 1. A if it sNaN or qNaN
+ * 2. B if it sNaN or qNaN
+ * A signaling NaN is always silenced before returning it.
+ */
+ if (aIsSNaN || aIsQNaN) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+#else
+static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
+ flag aIsLargerSignificand)
+{
+ /* This implements x87 NaN propagation rules:
+ * SNaN + QNaN => return the QNaN
+ * two SNaNs => return the one with the larger significand, silenced
+ * two QNaNs => return the one with the larger significand
+ * SNaN and a non-NaN => return the SNaN, silenced
+ * QNaN and a non-NaN => return the QNaN
+ *
+ * If we get down to comparing significands and they are the same,
+ * return the NaN with the positive sign bit (if any).
+ */
+ if (aIsSNaN) {
+ if (bIsSNaN) {
+ return aIsLargerSignificand ? 0 : 1;
+ }
+ return bIsQNaN ? 1 : 0;
+ }
+ else if (aIsQNaN) {
+ if (bIsSNaN || !bIsQNaN)
+ return 0;
+ else {
+ return aIsLargerSignificand ? 0 : 1;
+ }
+ } else {
+ return 1;
+ }
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Takes two single-precision floating-point values `a' and `b', one of which
+| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
+| signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM)
+{
+ flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN;
+ flag aIsLargerSignificand;
+ bits32 av, bv;
+
+ aIsQuietNaN = float32_is_quiet_nan( a );
+ aIsSignalingNaN = float32_is_signaling_nan( a );
+ bIsQuietNaN = float32_is_quiet_nan( b );
+ bIsSignalingNaN = float32_is_signaling_nan( b );
+ av = float32_val(a);
+ bv = float32_val(b);
+
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+
+ if ( STATUS(default_nan_mode) )
+ return float32_default_nan;
+
+ if ((bits32)(av<<1) < (bits32)(bv<<1)) {
+ aIsLargerSignificand = 0;
+ } else if ((bits32)(bv<<1) < (bits32)(av<<1)) {
+ aIsLargerSignificand = 1;
+ } else {
+ aIsLargerSignificand = (av < bv) ? 1 : 0;
+ }
+
+ if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN,
+ aIsLargerSignificand)) {
+ return float32_maybe_silence_nan(b);
+ } else {
+ return float32_maybe_silence_nan(a);
+ }
+}
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated double-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_SPARC)
+#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF ))
+#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA)
+#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 ))
+#elif SNAN_BIT_IS_ONE
+#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF ))
+#else
+#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 ))
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is a quiet
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float64_is_quiet_nan( float64 a_ )
+{
+ bits64 a = float64_val(a_);
+#if SNAN_BIT_IS_ONE
+ return
+ ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+ && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+#else
+ return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is a signaling
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float64_is_signaling_nan( float64 a_ )
+{
+ bits64 a = float64_val(a_);
+#if SNAN_BIT_IS_ONE
+ return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) );
+#else
+ return
+ ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+ && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns a quiet NaN if the double-precision floating point value `a' is a
+| signaling NaN; otherwise returns `a'.
+*----------------------------------------------------------------------------*/
+
+float64 float64_maybe_silence_nan( float64 a_ )
+{
+ if (float64_is_signaling_nan(a_)) {
+#if SNAN_BIT_IS_ONE
+# if defined(TARGET_MIPS) || defined(TARGET_SH4)
+ return float64_default_nan;
+# else
+# error Rules for silencing a signaling NaN are target-specific
+# endif
+#else
+ bits64 a = float64_val(a_);
+ a |= LIT64( 0x0008000000000000 );
+ return make_float64(a);
+#endif
+ }
+ return a_;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point NaN
+| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM)
+{
+ commonNaNT z;
+
+ if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+ z.sign = float64_val(a)>>63;
+ z.low = 0;
+ z.high = float64_val(a)<<12;
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the double-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float64 commonNaNToFloat64( commonNaNT a )
+{
+ bits64 mantissa = a.high>>12;
+
+ if ( mantissa )
+ return make_float64(
+ ( ( (bits64) a.sign )<<63 )
+ | LIT64( 0x7FF0000000000000 )
+ | ( a.high>>12 ));
+ else
+ return float64_default_nan;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two double-precision floating-point values `a' and `b', one of which
+| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
+| signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
+{
+ flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN;
+ flag aIsLargerSignificand;
+ bits64 av, bv;
+
+ aIsQuietNaN = float64_is_quiet_nan( a );
+ aIsSignalingNaN = float64_is_signaling_nan( a );
+ bIsQuietNaN = float64_is_quiet_nan( b );
+ bIsSignalingNaN = float64_is_signaling_nan( b );
+ av = float64_val(a);
+ bv = float64_val(b);
+
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+
+ if ( STATUS(default_nan_mode) )
+ return float64_default_nan;
+
+ if ((bits64)(av<<1) < (bits64)(bv<<1)) {
+ aIsLargerSignificand = 0;
+ } else if ((bits64)(bv<<1) < (bits64)(av<<1)) {
+ aIsLargerSignificand = 1;
+ } else {
+ aIsLargerSignificand = (av < bv) ? 1 : 0;
+ }
+
+ if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN,
+ aIsLargerSignificand)) {
+ return float64_maybe_silence_nan(b);
+ } else {
+ return float64_maybe_silence_nan(a);
+ }
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated extended double-precision NaN. The
+| `high' and `low' values hold the most- and least-significant bits,
+| respectively.
+*----------------------------------------------------------------------------*/
+#if SNAN_BIT_IS_ONE
+#define floatx80_default_nan_high 0x7FFF
+#define floatx80_default_nan_low LIT64( 0xBFFFFFFFFFFFFFFF )
+#else
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| quiet NaN; otherwise returns 0. This slightly differs from the same
+| function for other types as floatx80 has an explicit bit.
+*----------------------------------------------------------------------------*/
+
+int floatx80_is_quiet_nan( floatx80 a )
+{
+#if SNAN_BIT_IS_ONE
+ bits64 aLow;
+
+ aLow = a.low & ~ LIT64( 0x4000000000000000 );
+ return
+ ( ( a.high & 0x7FFF ) == 0x7FFF )
+ && (bits64) ( aLow<<1 )
+ && ( a.low == aLow );
+#else
+ return ( ( a.high & 0x7FFF ) == 0x7FFF )
+ && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 )));
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0. This slightly differs from the same
+| function for other types as floatx80 has an explicit bit.
+*----------------------------------------------------------------------------*/
+
+int floatx80_is_signaling_nan( floatx80 a )
+{
+#if SNAN_BIT_IS_ONE
+ return ( ( a.high & 0x7FFF ) == 0x7FFF )
+ && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 )));
+#else
+ bits64 aLow;
+
+ aLow = a.low & ~ LIT64( 0x4000000000000000 );
+ return
+ ( ( a.high & 0x7FFF ) == 0x7FFF )
+ && (bits64) ( aLow<<1 )
+ && ( a.low == aLow );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns a quiet NaN if the extended double-precision floating point value
+| `a' is a signaling NaN; otherwise returns `a'.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_maybe_silence_nan( floatx80 a )
+{
+ if (floatx80_is_signaling_nan(a)) {
+#if SNAN_BIT_IS_ONE
+# if defined(TARGET_MIPS) || defined(TARGET_SH4)
+ a.low = floatx80_default_nan_low;
+ a.high = floatx80_default_nan_high;
+# else
+# error Rules for silencing a signaling NaN are target-specific
+# endif
+#else
+ a.low |= LIT64( 0xC000000000000000 );
+ return a;
+#endif
+ }
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
+| invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM)
+{
+ commonNaNT z;
+
+ if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+ z.sign = a.high>>15;
+ z.low = 0;
+ z.high = a.low;
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the extended
+| double-precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static floatx80 commonNaNToFloatx80( commonNaNT a )
+{
+ floatx80 z;
+
+ if (a.high)
+ z.low = a.high;
+ else
+ z.low = floatx80_default_nan_low;
+ z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two extended double-precision floating-point values `a' and `b', one
+| of which is a NaN, and returns the appropriate NaN result. If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
+{
+ flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN;
+ flag aIsLargerSignificand;
+
+ aIsQuietNaN = floatx80_is_quiet_nan( a );
+ aIsSignalingNaN = floatx80_is_signaling_nan( a );
+ bIsQuietNaN = floatx80_is_quiet_nan( b );
+ bIsSignalingNaN = floatx80_is_signaling_nan( b );
+
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+
+ if ( STATUS(default_nan_mode) ) {
+ a.low = floatx80_default_nan_low;
+ a.high = floatx80_default_nan_high;
+ return a;
+ }
+
+ if (a.low < b.low) {
+ aIsLargerSignificand = 0;
+ } else if (b.low < a.low) {
+ aIsLargerSignificand = 1;
+ } else {
+ aIsLargerSignificand = (a.high < b.high) ? 1 : 0;
+ }
+
+ if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN,
+ aIsLargerSignificand)) {
+ return floatx80_maybe_silence_nan(b);
+ } else {
+ return floatx80_maybe_silence_nan(a);
+ }
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated quadruple-precision NaN. The `high' and
+| `low' values hold the most- and least-significant bits, respectively.
+*----------------------------------------------------------------------------*/
+#if SNAN_BIT_IS_ONE
+#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF )
+#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
+#else
+#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
+#define float128_default_nan_low LIT64( 0x0000000000000000 )
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is a quiet
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float128_is_quiet_nan( float128 a )
+{
+#if SNAN_BIT_IS_ONE
+ return
+ ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+ && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+#else
+ return
+ ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+ && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float128_is_signaling_nan( float128 a )
+{
+#if SNAN_BIT_IS_ONE
+ return
+ ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+ && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+#else
+ return
+ ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+ && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns a quiet NaN if the quadruple-precision floating point value `a' is
+| a signaling NaN; otherwise returns `a'.
+*----------------------------------------------------------------------------*/
+
+float128 float128_maybe_silence_nan( float128 a )
+{
+ if (float128_is_signaling_nan(a)) {
+#if SNAN_BIT_IS_ONE
+# if defined(TARGET_MIPS) || defined(TARGET_SH4)
+ a.low = float128_default_nan_low;
+ a.high = float128_default_nan_high;
+# else
+# error Rules for silencing a signaling NaN are target-specific
+# endif
+#else
+ a.high |= LIT64( 0x0000800000000000 );
+ return a;
+#endif
+ }
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point NaN
+| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM)
+{
+ commonNaNT z;
+
+ if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+ z.sign = a.high>>63;
+ shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the quadruple-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float128 commonNaNToFloat128( commonNaNT a )
+{
+ float128 z;
+
+ shift128Right( a.high, a.low, 16, &z.high, &z.low );
+ z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 );
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two quadruple-precision floating-point values `a' and `b', one of
+| which is a NaN, and returns the appropriate NaN result. If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM)
+{
+ flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN;
+ flag aIsLargerSignificand;
+
+ aIsQuietNaN = float128_is_quiet_nan( a );
+ aIsSignalingNaN = float128_is_signaling_nan( a );
+ bIsQuietNaN = float128_is_quiet_nan( b );
+ bIsSignalingNaN = float128_is_signaling_nan( b );
+
+ if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+
+ if ( STATUS(default_nan_mode) ) {
+ a.low = float128_default_nan_low;
+ a.high = float128_default_nan_high;
+ return a;
+ }
+
+ if (lt128(a.high<<1, a.low, b.high<<1, b.low)) {
+ aIsLargerSignificand = 0;
+ } else if (lt128(b.high<<1, b.low, a.high<<1, a.low)) {
+ aIsLargerSignificand = 1;
+ } else {
+ aIsLargerSignificand = (a.high < b.high) ? 1 : 0;
+ }
+
+ if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN,
+ aIsLargerSignificand)) {
+ return float128_maybe_silence_nan(b);
+ } else {
+ return float128_maybe_silence_nan(a);
+ }
+}
+
+#endif
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
new file mode 100644
index 0000000..17842f4
--- /dev/null
+++ b/fpu/softfloat.c
@@ -0,0 +1,6119 @@
+
+/*============================================================================
+
+This C source file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
+Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#include "softfloat.h"
+
+/*----------------------------------------------------------------------------
+| Primitive arithmetic functions, including multi-word arithmetic, and
+| division and square root approximations. (Can be specialized to target if
+| desired.)
+*----------------------------------------------------------------------------*/
+#include "softfloat-macros.h"
+
+/*----------------------------------------------------------------------------
+| Functions and definitions to determine: (1) whether tininess for underflow
+| is detected before or after rounding by default, (2) what (if anything)
+| happens when exceptions are raised, (3) how signaling NaNs are distinguished
+| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
+| are propagated from function inputs to output. These details are target-
+| specific.
+*----------------------------------------------------------------------------*/
+#include "softfloat-specialize.h"
+
+void set_float_rounding_mode(int val STATUS_PARAM)
+{
+ STATUS(float_rounding_mode) = val;
+}
+
+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
+
+/*----------------------------------------------------------------------------
+| Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
+| and 7, and returns the properly rounded 32-bit integer corresponding to the
+| input. If `zSign' is 1, the input is negated before being converted to an
+| integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point input
+| is simply rounded to an integer, with the inexact exception raised if the
+| input cannot be represented exactly as an integer. However, if the fixed-
+| point input is too large, the invalid exception is raised and the largest
+| positive or negative integer is returned.
+*----------------------------------------------------------------------------*/
+
+static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int8 roundIncrement, roundBits;
+ int32 z;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x40;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x7F;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = absZ & 0x7F;
+ absZ = ( absZ + roundIncrement )>>7;
+ absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+ z = absZ;
+ if ( zSign ) z = - z;
+ if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return zSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
+| `absZ1', with binary point between bits 63 and 64 (between the input words),
+| and returns the properly rounded 64-bit integer corresponding to the input.
+| If `zSign' is 1, the input is negated before being converted to an integer.
+| Ordinarily, the fixed-point input is simply rounded to an integer, with
+| the inexact exception raised if the input cannot be represented exactly as
+| an integer. However, if the fixed-point input is too large, the invalid
+| exception is raised and the largest positive or negative integer is
+| returned.
+*----------------------------------------------------------------------------*/
+
+static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment;
+ int64 z;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ increment = ( (sbits64) absZ1 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && absZ1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && absZ1;
+ }
+ }
+ }
+ if ( increment ) {
+ ++absZ0;
+ if ( absZ0 == 0 ) goto overflow;
+ absZ0 &= ~ ( ( (bits64) ( absZ1<<1 ) == 0 ) & roundNearestEven );
+ }
+ z = absZ0;
+ if ( zSign ) z = - z;
+ if ( z && ( ( z < 0 ) ^ zSign ) ) {
+ overflow:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return
+ zSign ? (sbits64) LIT64( 0x8000000000000000 )
+ : LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits32 extractFloat32Frac( float32 a )
+{
+
+ return float32_val(a) & 0x007FFFFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int16 extractFloat32Exp( float32 a )
+{
+
+ return ( float32_val(a)>>23 ) & 0xFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat32Sign( float32 a )
+{
+
+ return float32_val(a)>>31;
+
+}
+
+/*----------------------------------------------------------------------------
+| If `a' is denormal and we are in flush-to-zero mode then set the
+| input-denormal exception and return zero. Otherwise just return the value.
+*----------------------------------------------------------------------------*/
+static float32 float32_squash_input_denormal(float32 a STATUS_PARAM)
+{
+ if (STATUS(flush_inputs_to_zero)) {
+ if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) {
+ float_raise(float_flag_input_denormal STATUS_VAR);
+ return make_float32(float32_val(a) & 0x80000000);
+ }
+ }
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal single-precision floating-point value represented
+| by the denormalized significand `aSig'. The normalized exponent and
+| significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros32( aSig ) - 8;
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| single-precision floating-point value, returning the result. After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result. This means that any integer portion of `zSig'
+| will be added into the exponent. Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+
+ return make_float32(
+ ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig);
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper single-precision floating-
+| point value corresponding to the abstract input. Ordinarily, the abstract
+| value is simply rounded and packed into the single-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal single-
+| precision floating-point number.
+| The input significand `zSig' has its binary point between bits 30
+| and 29, which is 7 bits to the left of the usual location. This shifted
+| significand must be normalized or smaller. If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding. In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int8 roundIncrement, roundBits;
+ flag isTiny;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x40;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x7F;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig & 0x7F;
+ if ( 0xFD <= (bits16) zExp ) {
+ if ( ( 0xFD < zExp )
+ || ( ( zExp == 0xFD )
+ && ( (sbits32) ( zSig + roundIncrement ) < 0 ) )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 ));
+ }
+ if ( zExp < 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloat32( zSign, 0, 0 );
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ( zSig + roundIncrement < 0x80000000 );
+ shift32RightJamming( zSig, - zExp, &zSig );
+ zExp = 0;
+ roundBits = zSig & 0x7F;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+ }
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig = ( zSig + roundIncrement )>>7;
+ zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+ if ( zSig == 0 ) zExp = 0;
+ return packFloat32( zSign, zExp, zSig );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper single-precision floating-
+| point value corresponding to the abstract input. This routine is just like
+| `roundAndPackFloat32' except that `zSig' does not have to be normalized.
+| Bit 31 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
+| floating-point exponent.
+*----------------------------------------------------------------------------*/
+
+static float32
+ normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM)
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros32( zSig ) - 1;
+ return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<<shiftCount STATUS_VAR);
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat64Frac( float64 a )
+{
+
+ return float64_val(a) & LIT64( 0x000FFFFFFFFFFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int16 extractFloat64Exp( float64 a )
+{
+
+ return ( float64_val(a)>>52 ) & 0x7FF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat64Sign( float64 a )
+{
+
+ return float64_val(a)>>63;
+
+}
+
+/*----------------------------------------------------------------------------
+| If `a' is denormal and we are in flush-to-zero mode then set the
+| input-denormal exception and return zero. Otherwise just return the value.
+*----------------------------------------------------------------------------*/
+static float64 float64_squash_input_denormal(float64 a STATUS_PARAM)
+{
+ if (STATUS(flush_inputs_to_zero)) {
+ if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) {
+ float_raise(float_flag_input_denormal STATUS_VAR);
+ return make_float64(float64_val(a) & (1ULL << 63));
+ }
+ }
+ return a;
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal double-precision floating-point value represented
+| by the denormalized significand `aSig'. The normalized exponent and
+| significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( aSig ) - 11;
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| double-precision floating-point value, returning the result. After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result. This means that any integer portion of `zSig'
+| will be added into the exponent. Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+
+ return make_float64(
+ ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig);
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper double-precision floating-
+| point value corresponding to the abstract input. Ordinarily, the abstract
+| value is simply rounded and packed into the double-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded
+| to a subnormal number, and the underflow and inexact exceptions are raised
+| if the abstract input cannot be represented exactly as a subnormal double-
+| precision floating-point number.
+| The input significand `zSig' has its binary point between bits 62
+| and 61, which is 10 bits to the left of the usual location. This shifted
+| significand must be normalized or smaller. If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding. In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven;
+ int16 roundIncrement, roundBits;
+ flag isTiny;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ roundIncrement = 0x200;
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = 0x3FF;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig & 0x3FF;
+ if ( 0x7FD <= (bits16) zExp ) {
+ if ( ( 0x7FD < zExp )
+ || ( ( zExp == 0x7FD )
+ && ( (sbits64) ( zSig + roundIncrement ) < 0 ) )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 ));
+ }
+ if ( zExp < 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloat64( zSign, 0, 0 );
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
+ shift64RightJamming( zSig, - zExp, &zSig );
+ zExp = 0;
+ roundBits = zSig & 0x3FF;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+ }
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig = ( zSig + roundIncrement )>>10;
+ zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
+ if ( zSig == 0 ) zExp = 0;
+ return packFloat64( zSign, zExp, zSig );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper double-precision floating-
+| point value corresponding to the abstract input. This routine is just like
+| `roundAndPackFloat64' except that `zSig' does not have to be normalized.
+| Bit 63 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
+| floating-point exponent.
+*----------------------------------------------------------------------------*/
+
+static float64
+ normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM)
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( zSig ) - 1;
+ return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<<shiftCount STATUS_VAR);
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the extended double-precision floating-point
+| value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloatx80Frac( floatx80 a )
+{
+
+ return a.low;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the extended double-precision floating-point
+| value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int32 extractFloatx80Exp( floatx80 a )
+{
+
+ return a.high & 0x7FFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the extended double-precision floating-point value
+| `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloatx80Sign( floatx80 a )
+{
+
+ return a.high>>15;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal extended double-precision floating-point value
+| represented by the denormalized significand `aSig'. The normalized exponent
+| and significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr )
+{
+ int8 shiftCount;
+
+ shiftCount = countLeadingZeros64( aSig );
+ *zSigPtr = aSig<<shiftCount;
+ *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into an
+| extended double-precision floating-point value, returning the result.
+*----------------------------------------------------------------------------*/
+
+INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig )
+{
+ floatx80 z;
+
+ z.low = zSig;
+ z.high = ( ( (bits16) zSign )<<15 ) + zExp;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and extended significand formed by the concatenation of `zSig0' and `zSig1',
+| and returns the proper extended double-precision floating-point value
+| corresponding to the abstract input. Ordinarily, the abstract value is
+| rounded and packed into the extended double-precision format, with the
+| inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal extended
+| double-precision floating-point number.
+| If `roundingPrecision' is 32 or 64, the result is rounded to the same
+| number of bits as single or double precision, respectively. Otherwise, the
+| result is rounded to the full precision of the extended double-precision
+| format.
+| The input significand must be normalized or smaller. If the input
+| significand is not normalized, `zExp' must be 0; in that case, the result
+| returned is a subnormal number, and it must not require rounding. The
+| handling of underflow and overflow follows the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80
+ roundAndPackFloatx80(
+ int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment, isTiny;
+ int64 roundIncrement, roundMask, roundBits;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ if ( roundingPrecision == 80 ) goto precision80;
+ if ( roundingPrecision == 64 ) {
+ roundIncrement = LIT64( 0x0000000000000400 );
+ roundMask = LIT64( 0x00000000000007FF );
+ }
+ else if ( roundingPrecision == 32 ) {
+ roundIncrement = LIT64( 0x0000008000000000 );
+ roundMask = LIT64( 0x000000FFFFFFFFFF );
+ }
+ else {
+ goto precision80;
+ }
+ zSig0 |= ( zSig1 != 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ roundIncrement = 0;
+ }
+ else {
+ roundIncrement = roundMask;
+ if ( zSign ) {
+ if ( roundingMode == float_round_up ) roundIncrement = 0;
+ }
+ else {
+ if ( roundingMode == float_round_down ) roundIncrement = 0;
+ }
+ }
+ }
+ roundBits = zSig0 & roundMask;
+ if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+ if ( ( 0x7FFE < zExp )
+ || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
+ ) {
+ goto overflow;
+ }
+ if ( zExp <= 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloatx80( zSign, 0, 0 );
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < 0 )
+ || ( zSig0 <= zSig0 + roundIncrement );
+ shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
+ zExp = 0;
+ roundBits = zSig0 & roundMask;
+ if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig0 += roundIncrement;
+ if ( (sbits64) zSig0 < 0 ) zExp = 1;
+ roundIncrement = roundMask + 1;
+ if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+ roundMask |= roundIncrement;
+ }
+ zSig0 &= ~ roundMask;
+ return packFloatx80( zSign, zExp, zSig0 );
+ }
+ }
+ if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+ zSig0 += roundIncrement;
+ if ( zSig0 < roundIncrement ) {
+ ++zExp;
+ zSig0 = LIT64( 0x8000000000000000 );
+ }
+ roundIncrement = roundMask + 1;
+ if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+ roundMask |= roundIncrement;
+ }
+ zSig0 &= ~ roundMask;
+ if ( zSig0 == 0 ) zExp = 0;
+ return packFloatx80( zSign, zExp, zSig0 );
+ precision80:
+ increment = ( (sbits64) zSig1 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig1;
+ }
+ }
+ }
+ if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+ if ( ( 0x7FFE < zExp )
+ || ( ( zExp == 0x7FFE )
+ && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
+ && increment
+ )
+ ) {
+ roundMask = 0;
+ overflow:
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ if ( ( roundingMode == float_round_to_zero )
+ || ( zSign && ( roundingMode == float_round_up ) )
+ || ( ! zSign && ( roundingMode == float_round_down ) )
+ ) {
+ return packFloatx80( zSign, 0x7FFE, ~ roundMask );
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( zExp <= 0 ) {
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < 0 )
+ || ! increment
+ || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
+ shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
+ zExp = 0;
+ if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR);
+ if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ if ( roundNearestEven ) {
+ increment = ( (sbits64) zSig1 < 0 );
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig1;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig1;
+ }
+ }
+ if ( increment ) {
+ ++zSig0;
+ zSig0 &=
+ ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven );
+ if ( (sbits64) zSig0 < 0 ) zExp = 1;
+ }
+ return packFloatx80( zSign, zExp, zSig0 );
+ }
+ }
+ if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ if ( increment ) {
+ ++zSig0;
+ if ( zSig0 == 0 ) {
+ ++zExp;
+ zSig0 = LIT64( 0x8000000000000000 );
+ }
+ else {
+ zSig0 &= ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven );
+ }
+ }
+ else {
+ if ( zSig0 == 0 ) zExp = 0;
+ }
+ return packFloatx80( zSign, zExp, zSig0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent
+| `zExp', and significand formed by the concatenation of `zSig0' and `zSig1',
+| and returns the proper extended double-precision floating-point value
+| corresponding to the abstract input. This routine is just like
+| `roundAndPackFloatx80' except that the input significand does not have to be
+| normalized.
+*----------------------------------------------------------------------------*/
+
+static floatx80
+ normalizeRoundAndPackFloatx80(
+ int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ STATUS_PARAM)
+{
+ int8 shiftCount;
+
+ if ( zSig0 == 0 ) {
+ zSig0 = zSig1;
+ zSig1 = 0;
+ zExp -= 64;
+ }
+ shiftCount = countLeadingZeros64( zSig0 );
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ zExp -= shiftCount;
+ return
+ roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 STATUS_VAR);
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the least-significant 64 fraction bits of the quadruple-precision
+| floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat128Frac1( float128 a )
+{
+
+ return a.low;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the most-significant 48 fraction bits of the quadruple-precision
+| floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat128Frac0( float128 a )
+{
+
+ return a.high & LIT64( 0x0000FFFFFFFFFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the quadruple-precision floating-point value
+| `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int32 extractFloat128Exp( float128 a )
+{
+
+ return ( a.high>>48 ) & 0x7FFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the quadruple-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat128Sign( float128 a )
+{
+
+ return a.high>>63;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal quadruple-precision floating-point value
+| represented by the denormalized significand formed by the concatenation of
+| `aSig0' and `aSig1'. The normalized exponent is stored at the location
+| pointed to by `zExpPtr'. The most significant 49 bits of the normalized
+| significand are stored at the location pointed to by `zSig0Ptr', and the
+| least significant 64 bits of the normalized significand are stored at the
+| location pointed to by `zSig1Ptr'.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat128Subnormal(
+ bits64 aSig0,
+ bits64 aSig1,
+ int32 *zExpPtr,
+ bits64 *zSig0Ptr,
+ bits64 *zSig1Ptr
+ )
+{
+ int8 shiftCount;
+
+ if ( aSig0 == 0 ) {
+ shiftCount = countLeadingZeros64( aSig1 ) - 15;
+ if ( shiftCount < 0 ) {
+ *zSig0Ptr = aSig1>>( - shiftCount );
+ *zSig1Ptr = aSig1<<( shiftCount & 63 );
+ }
+ else {
+ *zSig0Ptr = aSig1<<shiftCount;
+ *zSig1Ptr = 0;
+ }
+ *zExpPtr = - shiftCount - 63;
+ }
+ else {
+ shiftCount = countLeadingZeros64( aSig0 ) - 15;
+ shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr );
+ *zExpPtr = 1 - shiftCount;
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', the exponent `zExp', and the significand formed
+| by the concatenation of `zSig0' and `zSig1' into a quadruple-precision
+| floating-point value, returning the result. After being shifted into the
+| proper positions, the three fields `zSign', `zExp', and `zSig0' are simply
+| added together to form the most significant 32 bits of the result. This
+| means that any integer portion of `zSig0' will be added into the exponent.
+| Since a properly normalized significand will have an integer portion equal
+| to 1, the `zExp' input should be 1 less than the desired result exponent
+| whenever `zSig0' and `zSig1' concatenated form a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float128
+ packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+ float128 z;
+
+ z.low = zSig1;
+ z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and extended significand formed by the concatenation of `zSig0', `zSig1',
+| and `zSig2', and returns the proper quadruple-precision floating-point value
+| corresponding to the abstract input. Ordinarily, the abstract value is
+| simply rounded and packed into the quadruple-precision format, with the
+| inexact exception raised if the abstract input cannot be represented
+| exactly. However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned. If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal quadruple-
+| precision floating-point number.
+| The input significand must be normalized or smaller. If the input
+| significand is not normalized, `zExp' must be 0; in that case, the result
+| returned is a subnormal number, and it must not require rounding. In the
+| usual case that the input significand is normalized, `zExp' must be 1 less
+| than the ``true'' floating-point exponent. The handling of underflow and
+| overflow follows the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128
+ roundAndPackFloat128(
+ flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 STATUS_PARAM)
+{
+ int8 roundingMode;
+ flag roundNearestEven, increment, isTiny;
+
+ roundingMode = STATUS(float_rounding_mode);
+ roundNearestEven = ( roundingMode == float_round_nearest_even );
+ increment = ( (sbits64) zSig2 < 0 );
+ if ( ! roundNearestEven ) {
+ if ( roundingMode == float_round_to_zero ) {
+ increment = 0;
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig2;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig2;
+ }
+ }
+ }
+ if ( 0x7FFD <= (bits32) zExp ) {
+ if ( ( 0x7FFD < zExp )
+ || ( ( zExp == 0x7FFD )
+ && eq128(
+ LIT64( 0x0001FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF ),
+ zSig0,
+ zSig1
+ )
+ && increment
+ )
+ ) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ if ( ( roundingMode == float_round_to_zero )
+ || ( zSign && ( roundingMode == float_round_up ) )
+ || ( ! zSign && ( roundingMode == float_round_down ) )
+ ) {
+ return
+ packFloat128(
+ zSign,
+ 0x7FFE,
+ LIT64( 0x0000FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF )
+ );
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( zExp < 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloat128( zSign, 0, 0, 0 );
+ isTiny =
+ ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+ || ( zExp < -1 )
+ || ! increment
+ || lt128(
+ zSig0,
+ zSig1,
+ LIT64( 0x0001FFFFFFFFFFFF ),
+ LIT64( 0xFFFFFFFFFFFFFFFF )
+ );
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
+ zExp = 0;
+ if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR);
+ if ( roundNearestEven ) {
+ increment = ( (sbits64) zSig2 < 0 );
+ }
+ else {
+ if ( zSign ) {
+ increment = ( roundingMode == float_round_down ) && zSig2;
+ }
+ else {
+ increment = ( roundingMode == float_round_up ) && zSig2;
+ }
+ }
+ }
+ }
+ if ( zSig2 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ if ( increment ) {
+ add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 );
+ zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven );
+ }
+ else {
+ if ( ( zSig0 | zSig1 ) == 0 ) zExp = 0;
+ }
+ return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand formed by the concatenation of `zSig0' and `zSig1', and
+| returns the proper quadruple-precision floating-point value corresponding
+| to the abstract input. This routine is just like `roundAndPackFloat128'
+| except that the input significand has fewer bits and does not have to be
+| normalized. In all cases, `zExp' must be 1 less than the ``true'' floating-
+| point exponent.
+*----------------------------------------------------------------------------*/
+
+static float128
+ normalizeRoundAndPackFloat128(
+ flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 STATUS_PARAM)
+{
+ int8 shiftCount;
+ bits64 zSig2;
+
+ if ( zSig0 == 0 ) {
+ zSig0 = zSig1;
+ zSig1 = 0;
+ zExp -= 64;
+ }
+ shiftCount = countLeadingZeros64( zSig0 ) - 15;
+ if ( 0 <= shiftCount ) {
+ zSig2 = 0;
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ }
+ else {
+ shift128ExtraRightJamming(
+ zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 );
+ }
+ zExp -= shiftCount;
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR);
+
+}
+
+#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
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 int32_to_float32( int32 a STATUS_PARAM )
+{
+ flag zSign;
+
+ if ( a == 0 ) return float32_zero;
+ if ( a == (sbits32) 0x80000000 ) return packFloat32( 1, 0x9E, 0 );
+ zSign = ( a < 0 );
+ return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the double-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 int32_to_float64( int32 a STATUS_PARAM )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig;
+
+ if ( a == 0 ) return float64_zero;
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 21;
+ zSig = absA;
+ return packFloat64( zSign, 0x432 - shiftCount, zSig<<shiftCount );
+
+}
+
+#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
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 int32_to_floatx80( int32 a STATUS_PARAM )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig;
+
+ if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 32;
+ zSig = absA;
+ return packFloatx80( zSign, 0x403E - shiftCount, zSig<<shiftCount );
+
+}
+
+#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
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 int32_to_float128( int32 a STATUS_PARAM )
+{
+ flag zSign;
+ uint32 absA;
+ int8 shiftCount;
+ bits64 zSig0;
+
+ if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros32( absA ) + 17;
+ zSig0 = absA;
+ return packFloat128( zSign, 0x402E - shiftCount, zSig0<<shiftCount, 0 );
+
+}
+
+#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
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 int64_to_float32( int64 a STATUS_PARAM )
+{
+ flag zSign;
+ uint64 absA;
+ int8 shiftCount;
+
+ if ( a == 0 ) return float32_zero;
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros64( absA ) - 40;
+ if ( 0 <= shiftCount ) {
+ return packFloat32( zSign, 0x95 - shiftCount, absA<<shiftCount );
+ }
+ else {
+ shiftCount += 7;
+ if ( shiftCount < 0 ) {
+ shift64RightJamming( absA, - shiftCount, &absA );
+ }
+ else {
+ absA <<= shiftCount;
+ }
+ return roundAndPackFloat32( zSign, 0x9C - shiftCount, absA STATUS_VAR );
+ }
+
+}
+
+float32 uint64_to_float32( uint64 a STATUS_PARAM )
+{
+ int8 shiftCount;
+
+ if ( a == 0 ) return float32_zero;
+ shiftCount = countLeadingZeros64( a ) - 40;
+ if ( 0 <= shiftCount ) {
+ return packFloat32( 1 > 0, 0x95 - shiftCount, a<<shiftCount );
+ }
+ else {
+ shiftCount += 7;
+ if ( shiftCount < 0 ) {
+ shift64RightJamming( a, - shiftCount, &a );
+ }
+ else {
+ a <<= shiftCount;
+ }
+ return roundAndPackFloat32( 1 > 0, 0x9C - shiftCount, a STATUS_VAR );
+ }
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the double-precision floating-point format. The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 int64_to_float64( int64 a STATUS_PARAM )
+{
+ flag zSign;
+
+ if ( a == 0 ) return float64_zero;
+ if ( a == (sbits64) LIT64( 0x8000000000000000 ) ) {
+ return packFloat64( 1, 0x43E, 0 );
+ }
+ zSign = ( a < 0 );
+ return normalizeRoundAndPackFloat64( zSign, 0x43C, zSign ? - a : a STATUS_VAR );
+
+}
+
+float64 uint64_to_float64( uint64 a STATUS_PARAM )
+{
+ if ( a == 0 ) return float64_zero;
+ return normalizeRoundAndPackFloat64( 0, 0x43C, a STATUS_VAR );
+
+}
+
+#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
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 int64_to_floatx80( int64 a STATUS_PARAM )
+{
+ flag zSign;
+ uint64 absA;
+ int8 shiftCount;
+
+ if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros64( absA );
+ return packFloatx80( zSign, 0x403E - shiftCount, absA<<shiftCount );
+
+}
+
+#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
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 int64_to_float128( int64 a STATUS_PARAM )
+{
+ flag zSign;
+ uint64 absA;
+ int8 shiftCount;
+ int32 zExp;
+ bits64 zSig0, zSig1;
+
+ if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+ zSign = ( a < 0 );
+ absA = zSign ? - a : a;
+ shiftCount = countLeadingZeros64( absA ) + 49;
+ zExp = 0x406E - shiftCount;
+ if ( 64 <= shiftCount ) {
+ zSig1 = 0;
+ zSig0 = absA;
+ shiftCount -= 64;
+ }
+ else {
+ zSig1 = absA;
+ zSig0 = 0;
+ }
+ shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+ return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+#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
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float32_to_int32( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 aSig64;
+
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( ( aExp == 0xFF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= 0x00800000;
+ shiftCount = 0xAF - aExp;
+ aSig64 = aSig;
+ aSig64 <<= 32;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig64, shiftCount, &aSig64 );
+ return roundAndPackInt32( aSign, aSig64 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ int32 z;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = aExp - 0x9E;
+ if ( 0 <= shiftCount ) {
+ if ( float32_val(a) != 0xCF000000 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF;
+ }
+ return (sbits32) 0x80000000;
+ }
+ else if ( aExp <= 0x7E ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig = ( aSig | 0x00800000 )<<8;
+ z = aSig>>( - shiftCount );
+ if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 16-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int16 float32_to_int16_round_to_zero( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ int32 z;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = aExp - 0x8E;
+ if ( 0 <= shiftCount ) {
+ if ( float32_val(a) != 0xC7000000 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+ return 0x7FFF;
+ }
+ }
+ return (sbits32) 0xffff8000;
+ }
+ else if ( aExp <= 0x7E ) {
+ if ( aExp | aSig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return 0;
+ }
+ shiftCount -= 0x10;
+ aSig = ( aSig | 0x00800000 )<<8;
+ z = aSig>>( - shiftCount );
+ if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) {
+ z = - z;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float32_to_int64( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 aSig64, aSigExtra;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = 0xBE - aExp;
+ if ( shiftCount < 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ if ( aExp ) aSig |= 0x00800000;
+ aSig64 = aSig;
+ aSig64 <<= 40;
+ shift64ExtraRightJamming( aSig64, 0, shiftCount, &aSig64, &aSigExtra );
+ return roundAndPackInt64( aSign, aSig64, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero. If
+| `a' is a NaN, the largest positive integer is returned. Otherwise, if the
+| conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits32 aSig;
+ bits64 aSig64;
+ int64 z;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ shiftCount = aExp - 0xBE;
+ if ( 0 <= shiftCount ) {
+ if ( float32_val(a) != 0xDF000000 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ else if ( aExp <= 0x7E ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig64 = aSig | 0x00800000;
+ aSig64 <<= 40;
+ z = aSig64>>( - shiftCount );
+ if ( (bits64) ( aSig64<<( shiftCount & 63 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the double-precision floating-point format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float32_to_float64( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ));
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( aSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the extended double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float32_to_floatx80( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) );
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ aSig |= 0x00800000;
+ return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 );
+
+}
+
+#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
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float32_to_float128( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) );
+ return packFloat128( aSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the single-precision floating-point value `a' to an integer, and
+| returns the result as a single-precision floating-point value. The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_round_to_int( float32 a STATUS_PARAM)
+{
+ flag aSign;
+ int16 aExp;
+ bits32 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ bits32 z;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aExp = extractFloat32Exp( a );
+ if ( 0x96 <= aExp ) {
+ if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) {
+ return propagateFloat32NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ if ( aExp <= 0x7E ) {
+ if ( (bits32) ( float32_val(a)<<1 ) == 0 ) return a;
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloat32Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) {
+ return packFloat32( aSign, 0x7F, 0 );
+ }
+ break;
+ case float_round_down:
+ return make_float32(aSign ? 0xBF800000 : 0);
+ case float_round_up:
+ return make_float32(aSign ? 0x80000000 : 0x3F800000);
+ }
+ return packFloat32( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x96 - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = float32_val(a);
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z += lastBitMask>>1;
+ if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat32Sign( make_float32(z) ) ^ ( roundingMode == float_round_up ) ) {
+ z += roundBitsMask;
+ }
+ }
+ z &= ~ roundBitsMask;
+ if ( z != float32_val(a) ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return make_float32(z);
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the single-precision
+| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
+| before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
+{
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 6;
+ bSig <<= 6;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= 0x20000000;
+ }
+ shift32RightJamming( bSig, expDiff, &bSig );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= 0x20000000;
+ }
+ shift32RightJamming( aSig, - expDiff, &aSig );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0xFF ) {
+ if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( aExp == 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloat32( zSign, 0, 0 );
+ return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
+ }
+ zSig = 0x40000000 + aSig + bSig;
+ zExp = aExp;
+ goto roundAndPack;
+ }
+ aSig |= 0x20000000;
+ zSig = ( aSig + bSig )<<1;
+ --zExp;
+ if ( (sbits32) zSig < 0 ) {
+ zSig = aSig + bSig;
+ ++zExp;
+ }
+ roundAndPack:
+ return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the single-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
+{
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 7;
+ bSig <<= 7;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0xFF ) {
+ if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloat32( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return packFloat32( zSign ^ 1, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= 0x40000000;
+ }
+ shift32RightJamming( aSig, - expDiff, &aSig );
+ bSig |= 0x40000000;
+ bBigger:
+ zSig = bSig - aSig;
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= 0x40000000;
+ }
+ shift32RightJamming( bSig, expDiff, &bSig );
+ aSig |= 0x40000000;
+ aBigger:
+ zSig = aSig - bSig;
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the single-precision floating-point values `a'
+| and `b'. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_add( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat32Sigs( a, b, aSign STATUS_VAR);
+ }
+ else {
+ return subFloat32Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the single-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_sub( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat32Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloat32Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the single-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_mul( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig;
+ bits64 zSig64;
+ bits32 zSig;
+
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0xFF ) {
+ if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+ return propagateFloat32NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x7F;
+ aSig = ( aSig | 0x00800000 )<<7;
+ bSig = ( bSig | 0x00800000 )<<8;
+ shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 );
+ zSig = zSig64;
+ if ( 0 <= (sbits32) ( zSig<<1 ) ) {
+ zSig <<= 1;
+ --zExp;
+ }
+ return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the single-precision floating-point value `a'
+| by the corresponding value `b'. The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_div( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits32 aSig, bSig, zSig;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ bSign = extractFloat32Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return packFloat32( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloat32( zSign, 0xFF, 0 );
+ }
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x7D;
+ aSig = ( aSig | 0x00800000 )<<7;
+ bSig = ( bSig | 0x00800000 )<<8;
+ if ( bSig <= ( aSig + aSig ) ) {
+ aSig >>= 1;
+ ++zExp;
+ }
+ zSig = ( ( (bits64) aSig )<<32 ) / bSig;
+ if ( ( zSig & 0x3F ) == 0 ) {
+ zSig |= ( (bits64) bSig * zSig != ( (bits64) aSig )<<32 );
+ }
+ return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the single-precision floating-point value `a'
+| with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_rem( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, zSign;
+ int16 aExp, bExp, expDiff;
+ bits32 aSig, bSig;
+ bits32 q;
+ bits64 aSig64, bSig64, q64;
+ bits32 alternateASig;
+ sbits32 sigMean;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ bSig = extractFloat32Frac( b );
+ bExp = extractFloat32Exp( b );
+ if ( aExp == 0xFF ) {
+ if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+ return propagateFloat32NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( bExp == 0xFF ) {
+ if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return a;
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ expDiff = aExp - bExp;
+ aSig |= 0x00800000;
+ bSig |= 0x00800000;
+ if ( expDiff < 32 ) {
+ aSig <<= 8;
+ bSig <<= 8;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ aSig >>= 1;
+ }
+ q = ( bSig <= aSig );
+ if ( q ) aSig -= bSig;
+ if ( 0 < expDiff ) {
+ q = ( ( (bits64) aSig )<<32 ) / bSig;
+ q >>= 32 - expDiff;
+ bSig >>= 2;
+ aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ else {
+ aSig >>= 2;
+ bSig >>= 2;
+ }
+ }
+ else {
+ if ( bSig <= aSig ) aSig -= bSig;
+ aSig64 = ( (bits64) aSig )<<40;
+ bSig64 = ( (bits64) bSig )<<40;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+ q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+ aSig64 = - ( ( bSig * q64 )<<38 );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+ q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+ q = q64>>( 64 - expDiff );
+ bSig <<= 6;
+ aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ do {
+ alternateASig = aSig;
+ ++q;
+ aSig -= bSig;
+ } while ( 0 <= (sbits32) aSig );
+ sigMean = aSig + alternateASig;
+ if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+ aSig = alternateASig;
+ }
+ zSign = ( (sbits32) aSig < 0 );
+ if ( zSign ) aSig = - aSig;
+ return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the single-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_sqrt( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, zExp;
+ bits32 aSig, zSig;
+ bits64 rem, term;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR );
+ if ( ! aSign ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig ) == 0 ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return float32_zero;
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E;
+ aSig = ( aSig | 0x00800000 )<<8;
+ zSig = estimateSqrt32( aExp, aSig ) + 2;
+ if ( ( zSig & 0x7F ) <= 5 ) {
+ if ( zSig < 2 ) {
+ zSig = 0x7FFFFFFF;
+ goto roundAndPack;
+ }
+ aSig >>= aExp & 1;
+ term = ( (bits64) zSig ) * zSig;
+ rem = ( ( (bits64) aSig )<<32 ) - term;
+ while ( (sbits64) rem < 0 ) {
+ --zSig;
+ rem += ( ( (bits64) zSig )<<1 ) | 1;
+ }
+ zSig |= ( rem != 0 );
+ }
+ shift32RightJamming( zSig, 1, &zSig );
+ roundAndPack:
+ return roundAndPackFloat32( 0, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the binary exponential of the single-precision floating-point value
+| `a'. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+|
+| Uses the following identities:
+|
+| 1. -------------------------------------------------------------------------
+| x x*ln(2)
+| 2 = e
+|
+| 2. -------------------------------------------------------------------------
+| 2 3 4 5 n
+| x x x x x x x
+| e = 1 + --- + --- + --- + --- + --- + ... + --- + ...
+| 1! 2! 3! 4! 5! n!
+*----------------------------------------------------------------------------*/
+
+static const float64 float32_exp2_coefficients[15] =
+{
+ make_float64( 0x3ff0000000000000ll ), /* 1 */
+ make_float64( 0x3fe0000000000000ll ), /* 2 */
+ make_float64( 0x3fc5555555555555ll ), /* 3 */
+ make_float64( 0x3fa5555555555555ll ), /* 4 */
+ make_float64( 0x3f81111111111111ll ), /* 5 */
+ make_float64( 0x3f56c16c16c16c17ll ), /* 6 */
+ make_float64( 0x3f2a01a01a01a01all ), /* 7 */
+ make_float64( 0x3efa01a01a01a01all ), /* 8 */
+ make_float64( 0x3ec71de3a556c734ll ), /* 9 */
+ make_float64( 0x3e927e4fb7789f5cll ), /* 10 */
+ make_float64( 0x3e5ae64567f544e4ll ), /* 11 */
+ make_float64( 0x3e21eed8eff8d898ll ), /* 12 */
+ make_float64( 0x3de6124613a86d09ll ), /* 13 */
+ make_float64( 0x3da93974a8c07c9dll ), /* 14 */
+ make_float64( 0x3d6ae7f3e733b81fll ), /* 15 */
+};
+
+float32 float32_exp2( float32 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+ float64 r, x, xn;
+ int i;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+
+ if ( aExp == 0xFF) {
+ if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR );
+ return (aSign) ? float32_zero : a;
+ }
+ if (aExp == 0) {
+ if (aSig == 0) return float32_one;
+ }
+
+ float_raise( float_flag_inexact STATUS_VAR);
+
+ /* ******************************* */
+ /* using float64 for approximation */
+ /* ******************************* */
+ x = float32_to_float64(a STATUS_VAR);
+ x = float64_mul(x, float64_ln2 STATUS_VAR);
+
+ xn = x;
+ r = float64_one;
+ for (i = 0 ; i < 15 ; i++) {
+ float64 f;
+
+ f = float64_mul(xn, float32_exp2_coefficients[i] STATUS_VAR);
+ r = float64_add(r, f STATUS_VAR);
+
+ xn = float64_mul(xn, x STATUS_VAR);
+ }
+
+ return float64_to_float32(r, status);
+}
+
+/*----------------------------------------------------------------------------
+| Returns the binary log of the single-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+float32 float32_log2( float32 a STATUS_PARAM )
+{
+ flag aSign, zSign;
+ int16 aExp;
+ bits32 aSig, zSig, i;
+
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat32( 1, 0xFF, 0 );
+ normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( aSign ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float32_default_nan;
+ }
+ if ( aExp == 0xFF ) {
+ if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR );
+ return a;
+ }
+
+ aExp -= 0x7F;
+ aSig |= 0x00800000;
+ zSign = aExp < 0;
+ zSig = aExp << 23;
+
+ for (i = 1 << 22; i > 0; i >>= 1) {
+ aSig = ( (bits64)aSig * aSig ) >> 23;
+ if ( aSig & 0x01000000 ) {
+ aSig >>= 1;
+ zSig |= i;
+ }
+ }
+
+ if ( zSign )
+ zSig = -zSig;
+
+ return normalizeRoundAndPackFloat32( zSign, 0x85, zSig STATUS_VAR );
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_eq( float32 a, float32 b STATUS_PARAM )
+{
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return ( float32_val(a) == float32_val(b) ) ||
+ ( (bits32) ( ( float32_val(a) | float32_val(b) )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise. The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_le( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits32 av, bv;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ av = float32_val(a);
+ bv = float32_val(b);
+ if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 );
+ return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_lt( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits32 av, bv;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ av = float32_val(a);
+ bv = float32_val(b);
+ if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 );
+ return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The invalid exception is
+| raised if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
+{
+ bits32 av, bv;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ av = float32_val(a);
+ bv = float32_val(b);
+ return ( av == bv ) || ( (bits32) ( ( av | bv )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+| cause an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_le_quiet( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits32 av, bv;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ av = float32_val(a);
+ bv = float32_val(b);
+ if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 );
+ return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+| exception. Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits32 av, bv;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ b = float32_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+ || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+ ) {
+ if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat32Sign( a );
+ bSign = extractFloat32Sign( b );
+ av = float32_val(a);
+ bv = float32_val(b);
+ if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 );
+ return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float64_to_int32( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x42C - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 32-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( 0x41E < aExp ) {
+ if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+ goto invalid;
+ }
+ else if ( aExp < 0x3FF ) {
+ if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x433 - aExp;
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 16-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int16 float64_to_int16_round_to_zero( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( 0x40E < aExp ) {
+ if ( ( aExp == 0x7FF ) && aSig ) {
+ aSign = 0;
+ }
+ goto invalid;
+ }
+ else if ( aExp < 0x3FF ) {
+ if ( aExp || aSig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return 0;
+ }
+ aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x433 - aExp;
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) {
+ z = - z;
+ }
+ if ( ( (int16_t)z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0xffff8000 : 0x7FFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float64_to_int64( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig, aSigExtra;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = 0x433 - aExp;
+ if ( shiftCount <= 0 ) {
+ if ( 0x43E < aExp ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FF )
+ && ( aSig != LIT64( 0x0010000000000000 ) ) )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ aSigExtra = 0;
+ aSig <<= - shiftCount;
+ }
+ else {
+ shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra );
+ }
+ return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit two's complement integer format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, shiftCount;
+ bits64 aSig;
+ int64 z;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+ shiftCount = aExp - 0x433;
+ if ( 0 <= shiftCount ) {
+ if ( 0x43E <= aExp ) {
+ if ( float64_val(a) != LIT64( 0xC3E0000000000000 ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FF )
+ && ( aSig != LIT64( 0x0010000000000000 ) ) )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ z = aSig<<shiftCount;
+ }
+ else {
+ if ( aExp < 0x3FE ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ z = aSig>>( - shiftCount );
+ if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the single-precision floating-point format. The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float64_to_float32( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+ bits32 zSig;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) );
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ shift64RightJamming( aSig, 22, &aSig );
+ zSig = aSig;
+ if ( aExp || zSig ) {
+ zSig |= 0x40000000;
+ aExp -= 0x381;
+ }
+ return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| half-precision floating-point value, returning the result. After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result. This means that any integer portion of `zSig'
+| will be added into the exponent. Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+static bits16 packFloat16(flag zSign, int16 zExp, bits16 zSig)
+{
+ return (((bits32)zSign) << 15) + (((bits32)zExp) << 10) + zSig;
+}
+
+/* Half precision floats come in two formats: standard IEEE and "ARM" format.
+ The latter gains extra exponent range by omitting the NaN/Inf encodings. */
+
+float32 float16_to_float32( bits16 a, flag ieee STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ aSign = a >> 15;
+ aExp = (a >> 10) & 0x1f;
+ aSig = a & 0x3ff;
+
+ if (aExp == 0x1f && ieee) {
+ if (aSig) {
+ /* Make sure correct exceptions are raised. */
+ float32ToCommonNaN(a STATUS_VAR);
+ aSig |= 0x200;
+ }
+ return packFloat32(aSign, 0xff, aSig << 13);
+ }
+ if (aExp == 0) {
+ int8 shiftCount;
+
+ if (aSig == 0) {
+ return packFloat32(aSign, 0, 0);
+ }
+
+ shiftCount = countLeadingZeros32( aSig ) - 21;
+ aSig = aSig << shiftCount;
+ aExp = -shiftCount;
+ }
+ return packFloat32( aSign, aExp + 0x70, aSig << 13);
+}
+
+bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM)
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+ bits32 mask;
+ bits32 increment;
+ int8 roundingMode;
+ a = float32_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+ if ( aExp == 0xFF ) {
+ if (aSig) {
+ /* Make sure correct exceptions are raised. */
+ float32ToCommonNaN(a STATUS_VAR);
+ aSig |= 0x00400000;
+ }
+ return packFloat16(aSign, 0x1f, aSig >> 13);
+ }
+ if (aExp == 0 && aSign == 0) {
+ return packFloat16(aSign, 0, 0);
+ }
+ /* Decimal point between bits 22 and 23. */
+ aSig |= 0x00800000;
+ aExp -= 0x7f;
+ if (aExp < -14) {
+ mask = 0x007fffff;
+ if (aExp < -24) {
+ aExp = -25;
+ } else {
+ mask >>= 24 + aExp;
+ }
+ } else {
+ mask = 0x00001fff;
+ }
+ if (aSig & mask) {
+ float_raise( float_flag_underflow STATUS_VAR );
+ roundingMode = STATUS(float_rounding_mode);
+ switch (roundingMode) {
+ case float_round_nearest_even:
+ increment = (mask + 1) >> 1;
+ if ((aSig & mask) == increment) {
+ increment = aSig & (increment << 1);
+ }
+ break;
+ case float_round_up:
+ increment = aSign ? 0 : mask;
+ break;
+ case float_round_down:
+ increment = aSign ? mask : 0;
+ break;
+ default: /* round_to_zero */
+ increment = 0;
+ break;
+ }
+ aSig += increment;
+ if (aSig >= 0x01000000) {
+ aSig >>= 1;
+ aExp++;
+ }
+ } else if (aExp < -14
+ && STATUS(float_detect_tininess) == float_tininess_before_rounding) {
+ float_raise( float_flag_underflow STATUS_VAR);
+ }
+
+ if (ieee) {
+ if (aExp > 15) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat16(aSign, 0x1f, 0);
+ }
+ } else {
+ if (aExp > 16) {
+ float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+ return packFloat16(aSign, 0x1f, 0x3ff);
+ }
+ }
+ if (aExp < -24) {
+ return packFloat16(aSign, 0, 0);
+ }
+ if (aExp < -14) {
+ aSig >>= -14 - aExp;
+ aExp = -14;
+ }
+ 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
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float64_to_floatx80( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) );
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ return
+ packFloatx80(
+ aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 );
+
+}
+
+#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
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float64_to_float128( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig, zSig0, zSig1;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) );
+ return packFloat128( aSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ --aExp;
+ }
+ shift128Right( aSig, 0, 4, &zSig0, &zSig1 );
+ return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the double-precision floating-point value `a' to an integer, and
+| returns the result as a double-precision floating-point value. The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_round_to_int( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ bits64 z;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aExp = extractFloat64Exp( a );
+ if ( 0x433 <= aExp ) {
+ if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) {
+ return propagateFloat64NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ if ( aExp < 0x3FF ) {
+ if ( (bits64) ( float64_val(a)<<1 ) == 0 ) return a;
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloat64Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) {
+ return packFloat64( aSign, 0x3FF, 0 );
+ }
+ break;
+ case float_round_down:
+ return make_float64(aSign ? LIT64( 0xBFF0000000000000 ) : 0);
+ case float_round_up:
+ return make_float64(
+ aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 ));
+ }
+ return packFloat64( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x433 - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = float64_val(a);
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z += lastBitMask>>1;
+ if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat64Sign( make_float64(z) ) ^ ( roundingMode == float_round_up ) ) {
+ z += roundBitsMask;
+ }
+ }
+ z &= ~ roundBitsMask;
+ if ( z != float64_val(a) )
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ return make_float64(z);
+
+}
+
+float64 float64_trunc_to_int( float64 a STATUS_PARAM)
+{
+ int oldmode;
+ float64 res;
+ oldmode = STATUS(float_rounding_mode);
+ STATUS(float_rounding_mode) = float_round_to_zero;
+ res = float64_round_to_int(a STATUS_VAR);
+ STATUS(float_rounding_mode) = oldmode;
+ return res;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the double-precision
+| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
+| before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
+{
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 9;
+ bSig <<= 9;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= LIT64( 0x2000000000000000 );
+ }
+ shift64RightJamming( bSig, expDiff, &bSig );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= LIT64( 0x2000000000000000 );
+ }
+ shift64RightJamming( aSig, - expDiff, &aSig );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FF ) {
+ if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( aExp == 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloat64( zSign, 0, 0 );
+ return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
+ }
+ zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
+ zExp = aExp;
+ goto roundAndPack;
+ }
+ aSig |= LIT64( 0x2000000000000000 );
+ zSig = ( aSig + bSig )<<1;
+ --zExp;
+ if ( (sbits64) zSig < 0 ) {
+ zSig = aSig + bSig;
+ ++zExp;
+ }
+ roundAndPack:
+ return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the double-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
+{
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ int16 expDiff;
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ expDiff = aExp - bExp;
+ aSig <<= 10;
+ bSig <<= 10;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FF ) {
+ if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloat64( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return packFloat64( zSign ^ 1, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig |= LIT64( 0x4000000000000000 );
+ }
+ shift64RightJamming( aSig, - expDiff, &aSig );
+ bSig |= LIT64( 0x4000000000000000 );
+ bBigger:
+ zSig = bSig - aSig;
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig |= LIT64( 0x4000000000000000 );
+ }
+ shift64RightJamming( bSig, expDiff, &bSig );
+ aSig |= LIT64( 0x4000000000000000 );
+ aBigger:
+ zSig = aSig - bSig;
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the double-precision floating-point values `a'
+| and `b'. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_add( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return subFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the double-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_sub( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloat64Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the double-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_mul( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FF ) {
+ if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+ return propagateFloat64NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x3FF;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ mul64To128( aSig, bSig, &zSig0, &zSig1 );
+ zSig0 |= ( zSig1 != 0 );
+ if ( 0 <= (sbits64) ( zSig0<<1 ) ) {
+ zSig0 <<= 1;
+ --zExp;
+ }
+ return roundAndPackFloat64( zSign, zExp, zSig0 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the double-precision floating-point value `a'
+| by the corresponding value `b'. The operation is performed according to
+| the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_div( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int16 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig;
+ bits64 rem0, rem1;
+ bits64 term0, term1;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ bSign = extractFloat64Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return packFloat64( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloat64( zSign, 0x7FF, 0 );
+ }
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x3FD;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ if ( bSig <= ( aSig + aSig ) ) {
+ aSig >>= 1;
+ ++zExp;
+ }
+ zSig = estimateDiv128To64( aSig, 0, bSig );
+ if ( ( zSig & 0x1FF ) <= 2 ) {
+ mul64To128( bSig, zSig, &term0, &term1 );
+ sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig;
+ add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+ }
+ zSig |= ( rem1 != 0 );
+ }
+ return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the double-precision floating-point value `a'
+| with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_rem( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, zSign;
+ int16 aExp, bExp, expDiff;
+ bits64 aSig, bSig;
+ bits64 q, alternateASig;
+ sbits64 sigMean;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ bSig = extractFloat64Frac( b );
+ bExp = extractFloat64Exp( b );
+ if ( aExp == 0x7FF ) {
+ if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+ return propagateFloat64NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( bExp == 0x7FF ) {
+ if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return a;
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ expDiff = aExp - bExp;
+ aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11;
+ bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ aSig >>= 1;
+ }
+ q = ( bSig <= aSig );
+ if ( q ) aSig -= bSig;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig, 0, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ aSig = - ( ( bSig>>2 ) * q );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ if ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig, 0, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ q >>= 64 - expDiff;
+ bSig >>= 2;
+ aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+ }
+ else {
+ aSig >>= 2;
+ bSig >>= 2;
+ }
+ do {
+ alternateASig = aSig;
+ ++q;
+ aSig -= bSig;
+ } while ( 0 <= (sbits64) aSig );
+ sigMean = aSig + alternateASig;
+ if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+ aSig = alternateASig;
+ }
+ zSign = ( (sbits64) aSig < 0 );
+ if ( zSign ) aSig = - aSig;
+ return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the double-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_sqrt( float64 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp, zExp;
+ bits64 aSig, zSig, doubleZSig;
+ bits64 rem0, rem1, term0, term1;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, a STATUS_VAR );
+ if ( ! aSign ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig ) == 0 ) return a;
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return float64_zero;
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE;
+ aSig |= LIT64( 0x0010000000000000 );
+ zSig = estimateSqrt32( aExp, aSig>>21 );
+ aSig <<= 9 - ( aExp & 1 );
+ zSig = estimateDiv128To64( aSig, 0, zSig<<32 ) + ( zSig<<30 );
+ if ( ( zSig & 0x1FF ) <= 5 ) {
+ doubleZSig = zSig<<1;
+ mul64To128( zSig, zSig, &term0, &term1 );
+ sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig;
+ doubleZSig -= 2;
+ add128( rem0, rem1, zSig>>63, doubleZSig | 1, &rem0, &rem1 );
+ }
+ zSig |= ( ( rem0 | rem1 ) != 0 );
+ }
+ return roundAndPackFloat64( 0, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the binary log of the double-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+float64 float64_log2( float64 a STATUS_PARAM )
+{
+ flag aSign, zSign;
+ int16 aExp;
+ bits64 aSig, aSig0, aSig1, zSig, i;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloat64( 1, 0x7FF, 0 );
+ normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( aSign ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return float64_default_nan;
+ }
+ if ( aExp == 0x7FF ) {
+ if ( aSig ) return propagateFloat64NaN( a, float64_zero STATUS_VAR );
+ return a;
+ }
+
+ aExp -= 0x3FF;
+ aSig |= LIT64( 0x0010000000000000 );
+ zSign = aExp < 0;
+ zSig = (bits64)aExp << 52;
+ for (i = 1LL << 51; i > 0; i >>= 1) {
+ mul64To128( aSig, aSig, &aSig0, &aSig1 );
+ aSig = ( aSig0 << 12 ) | ( aSig1 >> 52 );
+ if ( aSig & LIT64( 0x0020000000000000 ) ) {
+ aSig >>= 1;
+ zSig |= i;
+ }
+ }
+
+ if ( zSign )
+ zSig = -zSig;
+ return normalizeRoundAndPackFloat64( zSign, 0x408, zSig STATUS_VAR );
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is equal to the
+| corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_eq( float64 a, float64 b STATUS_PARAM )
+{
+ bits64 av, bv;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ av = float64_val(a);
+ bv = float64_val(b);
+ return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise. The comparison is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_le( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits64 av, bv;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ av = float64_val(a);
+ bv = float64_val(b);
+ if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+ return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_lt( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits64 av, bv;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ av = float64_val(a);
+ bv = float64_val(b);
+ if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 );
+ return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is equal to the
+| corresponding value `b', and 0 otherwise. The invalid exception is raised
+| if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
+{
+ bits64 av, bv;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ av = float64_val(a);
+ bv = float64_val(b);
+ return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+| cause an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_le_quiet( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits64 av, bv;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ av = float64_val(a);
+ bv = float64_val(b);
+ if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+ return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+| exception. Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+ bits64 av, bv;
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ b = float64_squash_input_denormal(b STATUS_VAR);
+
+ if ( ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+ || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+ ) {
+ if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat64Sign( a );
+ bSign = extractFloat64Sign( b );
+ av = float64_val(a);
+ bv = float64_val(b);
+ if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 );
+ return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+#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
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic---which means in particular that the conversion
+| is rounded according to the current rounding mode. If `a' is a NaN, the
+| largest positive integer is returned. Otherwise, if the conversion
+| overflows, the largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 floatx80_to_int32( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+ shiftCount = 0x4037 - aExp;
+ if ( shiftCount <= 0 ) shiftCount = 1;
+ shift64RightJamming( aSig, shiftCount, &aSig );
+ return roundAndPackInt32( aSign, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 32-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic, except that the conversion is always rounded
+| toward zero. If `a' is a NaN, the largest positive integer is returned.
+| Otherwise, if the conversion overflows, the largest integer with the same
+| sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig, savedASig;
+ int32 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( 0x401E < aExp ) {
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+ goto invalid;
+ }
+ else if ( aExp < 0x3FFF ) {
+ if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ shiftCount = 0x403E - aExp;
+ savedASig = aSig;
+ aSig >>= shiftCount;
+ z = aSig;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 64-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic---which means in particular that the conversion
+| is rounded according to the current rounding mode. If `a' is a NaN,
+| the largest positive integer is returned. Otherwise, if the conversion
+| overflows, the largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 floatx80_to_int64( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig, aSigExtra;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ shiftCount = 0x403E - aExp;
+ if ( shiftCount <= 0 ) {
+ if ( shiftCount ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FFF )
+ && ( aSig != LIT64( 0x8000000000000000 ) ) )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ aSigExtra = 0;
+ }
+ else {
+ shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra );
+ }
+ return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 64-bit two's complement integer format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic, except that the conversion is always rounded
+| toward zero. If `a' is a NaN, the largest positive integer is returned.
+| Otherwise, if the conversion overflows, the largest integer with the same
+| sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig;
+ int64 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ shiftCount = aExp - 0x403E;
+ if ( 0 <= shiftCount ) {
+ aSig &= LIT64( 0x7FFFFFFFFFFFFFFF );
+ if ( ( a.high != 0xC03E ) || aSig ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0x7FFF ) && aSig ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ else if ( aExp < 0x3FFF ) {
+ if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ z = aSig>>( - shiftCount );
+ if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the single-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 floatx80_to_float32( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ shift64RightJamming( aSig, 33, &aSig );
+ if ( aExp || aSig ) aExp -= 0x3F81;
+ return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the double-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 floatx80_to_float64( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig, zSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ shift64RightJamming( aSig, 1, &zSig );
+ if ( aExp || aSig ) aExp -= 0x3C01;
+ return roundAndPackFloat64( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the quadruple-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 floatx80_to_float128( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig, zSig0, zSig1;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) {
+ return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) );
+ }
+ shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
+ return packFloat128( aSign, aExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the extended double-precision floating-point value `a' to an integer,
+| and returns the result as an extended quadruple-precision floating-point
+| value. The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ floatx80 z;
+
+ aExp = extractFloatx80Exp( a );
+ if ( 0x403E <= aExp ) {
+ if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) {
+ return propagateFloatx80NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ if ( aExp < 0x3FFF ) {
+ if ( ( aExp == 0 )
+ && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+ return a;
+ }
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloatx80Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 )
+ ) {
+ return
+ packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
+ }
+ break;
+ case float_round_down:
+ return
+ aSign ?
+ packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) )
+ : packFloatx80( 0, 0, 0 );
+ case float_round_up:
+ return
+ aSign ? packFloatx80( 1, 0, 0 )
+ : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) );
+ }
+ return packFloatx80( aSign, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x403E - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z.low += lastBitMask>>1;
+ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+ z.low += roundBitsMask;
+ }
+ }
+ z.low &= ~ roundBitsMask;
+ if ( z.low == 0 ) {
+ ++z.high;
+ z.low = LIT64( 0x8000000000000000 );
+ }
+ if ( z.low != a.low ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the extended double-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the sum is
+| negated before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM)
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ int32 expDiff;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) --expDiff;
+ shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) ++expDiff;
+ shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ return a;
+ }
+ zSig1 = 0;
+ zSig0 = aSig + bSig;
+ if ( aExp == 0 ) {
+ normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 );
+ goto roundAndPack;
+ }
+ zExp = aExp;
+ goto shiftRight1;
+ }
+ zSig0 = aSig + bSig;
+ if ( (sbits64) zSig0 < 0 ) goto roundAndPack;
+ shiftRight1:
+ shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 );
+ zSig0 |= LIT64( 0x8000000000000000 );
+ ++zExp;
+ roundAndPack:
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the extended
+| double-precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM )
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ int32 expDiff;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ zSig1 = 0;
+ if ( bSig < aSig ) goto aBigger;
+ if ( aSig < bSig ) goto bBigger;
+ return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) ++expDiff;
+ shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ bBigger:
+ sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 );
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) --expDiff;
+ shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ aBigger:
+ sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 );
+ zExp = aExp;
+ normalizeRoundAndPack:
+ return
+ normalizeRoundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the extended double-precision floating-point
+| values `a' and `b'. The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign == bSign ) {
+ return addFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return subFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the extended double-precision floating-
+| point values `a' and `b'. The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign == bSign ) {
+ return subFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloatx80Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the extended double-precision floating-
+| point values `a' and `b'. The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 )
+ || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig ) == 0 ) goto invalid;
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ zExp = aExp + bExp - 0x3FFE;
+ mul64To128( aSig, bSig, &zSig0, &zSig1 );
+ if ( 0 < (sbits64) zSig0 ) {
+ shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 );
+ --zExp;
+ }
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the extended double-precision floating-point
+| value `a' by the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig, bSig, zSig0, zSig1;
+ bits64 rem0, rem1, rem2, term0, term1, term2;
+ floatx80 z;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ bSign = extractFloatx80Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ goto invalid;
+ }
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return packFloatx80( zSign, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+ normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+ }
+ zExp = aExp - bExp + 0x3FFE;
+ rem1 = 0;
+ if ( bSig <= aSig ) {
+ shift128Right( aSig, 0, 1, &aSig, &rem1 );
+ ++zExp;
+ }
+ zSig0 = estimateDiv128To64( aSig, rem1, bSig );
+ mul64To128( bSig, zSig0, &term0, &term1 );
+ sub128( aSig, rem1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, bSig );
+ if ( (bits64) ( zSig1<<1 ) <= 8 ) {
+ mul64To128( bSig, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ add128( rem1, rem2, 0, bSig, &rem1, &rem2 );
+ }
+ zSig1 |= ( ( rem1 | rem2 ) != 0 );
+ }
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the extended double-precision floating-point value
+| `a' with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, zSign;
+ int32 aExp, bExp, expDiff;
+ bits64 aSig0, aSig1, bSig;
+ bits64 q, term0, term1, alternateASig0, alternateASig1;
+ floatx80 z;
+
+ aSig0 = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ bSig = extractFloatx80Frac( b );
+ bExp = extractFloatx80Exp( b );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig0<<1 )
+ || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+ return propagateFloatx80NaN( a, b STATUS_VAR );
+ }
+ goto invalid;
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( bSig == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+ }
+ if ( aExp == 0 ) {
+ if ( (bits64) ( aSig0<<1 ) == 0 ) return a;
+ normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+ }
+ bSig |= LIT64( 0x8000000000000000 );
+ zSign = aSign;
+ expDiff = aExp - bExp;
+ aSig1 = 0;
+ if ( expDiff < 0 ) {
+ if ( expDiff < -1 ) return a;
+ shift128Right( aSig0, 0, 1, &aSig0, &aSig1 );
+ expDiff = 0;
+ }
+ q = ( bSig <= aSig0 );
+ if ( q ) aSig0 -= bSig;
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ mul64To128( bSig, q, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 );
+ expDiff -= 62;
+ }
+ expDiff += 64;
+ if ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig );
+ q = ( 2 < q ) ? q - 2 : 0;
+ q >>= 64 - expDiff;
+ mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 );
+ while ( le128( term0, term1, aSig0, aSig1 ) ) {
+ ++q;
+ sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+ }
+ }
+ else {
+ term1 = 0;
+ term0 = bSig;
+ }
+ sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 );
+ if ( lt128( alternateASig0, alternateASig1, aSig0, aSig1 )
+ || ( eq128( alternateASig0, alternateASig1, aSig0, aSig1 )
+ && ( q & 1 ) )
+ ) {
+ aSig0 = alternateASig0;
+ aSig1 = alternateASig1;
+ zSign = ! zSign;
+ }
+ return
+ normalizeRoundAndPackFloatx80(
+ 80, zSign, bExp + expDiff, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the extended double-precision floating-point
+| value `a'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, zExp;
+ bits64 aSig0, aSig1, zSig0, zSig1, doubleZSig0;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ floatx80 z;
+
+ aSig0 = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR );
+ if ( ! aSign ) return a;
+ goto invalid;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig0 ) == 0 ) return a;
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = floatx80_default_nan_low;
+ z.high = floatx80_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 );
+ normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+ }
+ zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF;
+ zSig0 = estimateSqrt32( aExp, aSig0>>32 );
+ shift128Right( aSig0, 0, 2 + ( aExp & 1 ), &aSig0, &aSig1 );
+ zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 );
+ doubleZSig0 = zSig0<<1;
+ mul64To128( zSig0, zSig0, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ doubleZSig0 -= 2;
+ add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 );
+ if ( ( zSig1 & LIT64( 0x3FFFFFFFFFFFFFFF ) ) <= 5 ) {
+ if ( zSig1 == 0 ) zSig1 = 1;
+ mul64To128( doubleZSig0, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ mul64To128( zSig1, zSig1, &term2, &term3 );
+ sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ shortShift128Left( 0, zSig1, 1, &term2, &term3 );
+ term3 |= 1;
+ term2 |= doubleZSig0;
+ add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shortShift128Left( 0, zSig1, 1, &zSig0, &zSig1 );
+ zSig0 |= doubleZSig0;
+ return
+ roundAndPackFloatx80(
+ STATUS(floatx80_rounding_precision), 0, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| equal to the corresponding value `b', and 0 otherwise. The comparison is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| less than or equal to the corresponding value `b', and 0 otherwise. The
+| comparison is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| less than the corresponding value `b', and 0 otherwise. The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is equal
+| to the corresponding value `b', and 0 otherwise. The invalid exception is
+| raised if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is less
+| than or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs
+| do not cause an exception. Otherwise, the comparison is performed according
+| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is less
+| than the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause
+| an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloatx80Exp( a ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+ || ( ( extractFloatx80Exp( b ) == 0x7FFF )
+ && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+ ) {
+ if ( floatx80_is_signaling_nan( a )
+ || floatx80_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloatx80Sign( a );
+ bSign = extractFloatx80Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#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
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float128_to_int32( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0;
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ aSig0 |= ( aSig1 != 0 );
+ shiftCount = 0x4028 - aExp;
+ if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 );
+ return roundAndPackInt32( aSign, aSig0 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 32-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero. If
+| `a' is a NaN, the largest positive integer is returned. Otherwise, if the
+| conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1, savedASig;
+ int32 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ aSig0 |= ( aSig1 != 0 );
+ if ( 0x401E < aExp ) {
+ if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0;
+ goto invalid;
+ }
+ else if ( aExp < 0x3FFF ) {
+ if ( aExp || aSig0 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ return 0;
+ }
+ aSig0 |= LIT64( 0x0001000000000000 );
+ shiftCount = 0x402F - aExp;
+ savedASig = aSig0;
+ aSig0 >>= shiftCount;
+ z = aSig0;
+ if ( aSign ) z = - z;
+ if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+ }
+ if ( ( aSig0<<shiftCount ) != savedASig ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 64-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode. If `a' is a NaN, the largest
+| positive integer is returned. Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float128_to_int64( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ shiftCount = 0x402F - aExp;
+ if ( shiftCount <= 0 ) {
+ if ( 0x403E < aExp ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign
+ || ( ( aExp == 0x7FFF )
+ && ( aSig1 || ( aSig0 != LIT64( 0x0001000000000000 ) ) )
+ )
+ ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ shortShift128Left( aSig0, aSig1, - shiftCount, &aSig0, &aSig1 );
+ }
+ else {
+ shift64ExtraRightJamming( aSig0, aSig1, shiftCount, &aSig0, &aSig1 );
+ }
+ return roundAndPackInt64( aSign, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 64-bit two's complement integer format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned. Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, shiftCount;
+ bits64 aSig0, aSig1;
+ int64 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+ shiftCount = aExp - 0x402F;
+ if ( 0 < shiftCount ) {
+ if ( 0x403E <= aExp ) {
+ aSig0 &= LIT64( 0x0000FFFFFFFFFFFF );
+ if ( ( a.high == LIT64( 0xC03E000000000000 ) )
+ && ( aSig1 < LIT64( 0x0002000000000000 ) ) ) {
+ if ( aSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ else {
+ float_raise( float_flag_invalid STATUS_VAR);
+ if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) ) {
+ return LIT64( 0x7FFFFFFFFFFFFFFF );
+ }
+ }
+ return (sbits64) LIT64( 0x8000000000000000 );
+ }
+ z = ( aSig0<<shiftCount ) | ( aSig1>>( ( - shiftCount ) & 63 ) );
+ if ( (bits64) ( aSig1<<shiftCount ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ }
+ else {
+ if ( aExp < 0x3FFF ) {
+ if ( aExp | aSig0 | aSig1 ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return 0;
+ }
+ z = aSig0>>( - shiftCount );
+ if ( aSig1
+ || ( shiftCount && (bits64) ( aSig0<<( shiftCount & 63 ) ) ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ }
+ if ( aSign ) z = - z;
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the single-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float128_to_float32( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+ bits32 zSig;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat32( aSign, 0xFF, 0 );
+ }
+ aSig0 |= ( aSig1 != 0 );
+ shift64RightJamming( aSig0, 18, &aSig0 );
+ zSig = aSig0;
+ if ( aExp || zSig ) {
+ zSig |= 0x40000000;
+ aExp -= 0x3F81;
+ }
+ return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the double-precision floating-point format. The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float128_to_float64( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloat64( aSign, 0x7FF, 0 );
+ }
+ shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+ aSig0 |= ( aSig1 != 0 );
+ if ( aExp || aSig0 ) {
+ aSig0 |= LIT64( 0x4000000000000000 );
+ aExp -= 0x3C01;
+ }
+ return roundAndPackFloat64( aSign, aExp, aSig0 STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the extended double-precision floating-point format. The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float128_to_floatx80( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) {
+ return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) );
+ }
+ return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ else {
+ aSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 );
+ return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 STATUS_VAR );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the quadruple-precision floating-point value `a' to an integer, and
+| returns the result as a quadruple-precision floating-point value. The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_round_to_int( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp;
+ bits64 lastBitMask, roundBitsMask;
+ int8 roundingMode;
+ float128 z;
+
+ aExp = extractFloat128Exp( a );
+ if ( 0x402F <= aExp ) {
+ if ( 0x406F <= aExp ) {
+ if ( ( aExp == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) )
+ ) {
+ return propagateFloat128NaN( a, a STATUS_VAR );
+ }
+ return a;
+ }
+ lastBitMask = 1;
+ lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
+ roundBitsMask = lastBitMask - 1;
+ z = a;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ if ( lastBitMask ) {
+ add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
+ if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+ }
+ else {
+ if ( (sbits64) z.low < 0 ) {
+ ++z.high;
+ if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1;
+ }
+ }
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat128Sign( z )
+ ^ ( roundingMode == float_round_up ) ) {
+ add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+ }
+ }
+ z.low &= ~ roundBitsMask;
+ }
+ else {
+ if ( aExp < 0x3FFF ) {
+ if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a;
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ aSign = extractFloat128Sign( a );
+ switch ( STATUS(float_rounding_mode) ) {
+ case float_round_nearest_even:
+ if ( ( aExp == 0x3FFE )
+ && ( extractFloat128Frac0( a )
+ | extractFloat128Frac1( a ) )
+ ) {
+ return packFloat128( aSign, 0x3FFF, 0, 0 );
+ }
+ break;
+ case float_round_down:
+ return
+ aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
+ : packFloat128( 0, 0, 0, 0 );
+ case float_round_up:
+ return
+ aSign ? packFloat128( 1, 0, 0, 0 )
+ : packFloat128( 0, 0x3FFF, 0, 0 );
+ }
+ return packFloat128( aSign, 0, 0, 0 );
+ }
+ lastBitMask = 1;
+ lastBitMask <<= 0x402F - aExp;
+ roundBitsMask = lastBitMask - 1;
+ z.low = 0;
+ z.high = a.high;
+ roundingMode = STATUS(float_rounding_mode);
+ if ( roundingMode == float_round_nearest_even ) {
+ z.high += lastBitMask>>1;
+ if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
+ z.high &= ~ lastBitMask;
+ }
+ }
+ else if ( roundingMode != float_round_to_zero ) {
+ if ( extractFloat128Sign( z )
+ ^ ( roundingMode == float_round_up ) ) {
+ z.high |= ( a.low != 0 );
+ z.high += roundBitsMask;
+ }
+ }
+ z.high &= ~ roundBitsMask;
+ }
+ if ( ( z.low != a.low ) || ( z.high != a.high ) ) {
+ STATUS(float_exception_flags) |= float_flag_inexact;
+ }
+ return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the quadruple-precision
+| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated
+| before being returned. `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM)
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+ int32 expDiff;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ expDiff = aExp - bExp;
+ if ( 0 < expDiff ) {
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shift128ExtraRightJamming(
+ bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 );
+ zExp = aExp;
+ }
+ else if ( expDiff < 0 ) {
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig0 |= LIT64( 0x0001000000000000 );
+ }
+ shift128ExtraRightJamming(
+ aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 );
+ zExp = bExp;
+ }
+ else {
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ return a;
+ }
+ add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ if ( aExp == 0 ) {
+ if ( STATUS(flush_to_zero) ) return packFloat128( zSign, 0, 0, 0 );
+ return packFloat128( zSign, 0, zSig0, zSig1 );
+ }
+ zSig2 = 0;
+ zSig0 |= LIT64( 0x0002000000000000 );
+ zExp = aExp;
+ goto shiftRight1;
+ }
+ aSig0 |= LIT64( 0x0001000000000000 );
+ add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ --zExp;
+ if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack;
+ ++zExp;
+ shiftRight1:
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ roundAndPack:
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the quadruple-
+| precision floating-point values `a' and `b'. If `zSign' is 1, the
+| difference is negated before being returned. `zSign' is ignored if the
+| result is a NaN. The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM)
+{
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1;
+ int32 expDiff;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ expDiff = aExp - bExp;
+ shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+ shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 );
+ if ( 0 < expDiff ) goto aExpBigger;
+ if ( expDiff < 0 ) goto bExpBigger;
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ aExp = 1;
+ bExp = 1;
+ }
+ if ( bSig0 < aSig0 ) goto aBigger;
+ if ( aSig0 < bSig0 ) goto bBigger;
+ if ( bSig1 < aSig1 ) goto aBigger;
+ if ( aSig1 < bSig1 ) goto bBigger;
+ return packFloat128( STATUS(float_rounding_mode) == float_round_down, 0, 0, 0 );
+ bExpBigger:
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ ++expDiff;
+ }
+ else {
+ aSig0 |= LIT64( 0x4000000000000000 );
+ }
+ shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+ bSig0 |= LIT64( 0x4000000000000000 );
+ bBigger:
+ sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 );
+ zExp = bExp;
+ zSign ^= 1;
+ goto normalizeRoundAndPack;
+ aExpBigger:
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ --expDiff;
+ }
+ else {
+ bSig0 |= LIT64( 0x4000000000000000 );
+ }
+ shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 );
+ aSig0 |= LIT64( 0x4000000000000000 );
+ aBigger:
+ sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+ zExp = aExp;
+ normalizeRoundAndPack:
+ --zExp;
+ return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the quadruple-precision floating-point values
+| `a' and `b'. The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_add( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign == bSign ) {
+ return addFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return subFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the quadruple-precision floating-point
+| values `a' and `b'. The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_sub( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign == bSign ) {
+ return subFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+ else {
+ return addFloat128Sigs( a, b, aSign STATUS_VAR );
+ }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the quadruple-precision floating-point
+| values `a' and `b'. The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_mul( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( ( aSig0 | aSig1 )
+ || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid;
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ zExp = aExp + bExp - 0x4000;
+ aSig0 |= LIT64( 0x0001000000000000 );
+ shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 );
+ mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 );
+ add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 );
+ zSig2 |= ( zSig3 != 0 );
+ if ( LIT64( 0x0002000000000000 ) <= zSig0 ) {
+ shift128ExtraRightJamming(
+ zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ ++zExp;
+ }
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the quadruple-precision floating-point value
+| `a' by the corresponding value `b'. The operation is performed according to
+| the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_div( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign, zSign;
+ int32 aExp, bExp, zExp;
+ bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ bSign = extractFloat128Sign( b );
+ zSign = aSign ^ bSign;
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ goto invalid;
+ }
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return packFloat128( zSign, 0, 0, 0 );
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) {
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ float_raise( float_flag_divbyzero STATUS_VAR);
+ return packFloat128( zSign, 0x7FFF, 0, 0 );
+ }
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ zExp = aExp - bExp + 0x3FFD;
+ shortShift128Left(
+ aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 );
+ shortShift128Left(
+ bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+ if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) {
+ shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 );
+ ++zExp;
+ }
+ zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 );
+ sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 );
+ }
+ zSig1 = estimateDiv128To64( rem1, rem2, bSig0 );
+ if ( ( zSig1 & 0x3FFF ) <= 4 ) {
+ mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 );
+ sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+ return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the quadruple-precision floating-point value `a'
+| with respect to the corresponding value `b'. The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_rem( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, zSign;
+ int32 aExp, bExp, expDiff;
+ bits64 aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2;
+ bits64 allZero, alternateASig0, alternateASig1, sigMean1;
+ sbits64 sigMean0;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ bSig1 = extractFloat128Frac1( b );
+ bSig0 = extractFloat128Frac0( b );
+ bExp = extractFloat128Exp( b );
+ if ( aExp == 0x7FFF ) {
+ if ( ( aSig0 | aSig1 )
+ || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+ return propagateFloat128NaN( a, b STATUS_VAR );
+ }
+ goto invalid;
+ }
+ if ( bExp == 0x7FFF ) {
+ if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+ return a;
+ }
+ if ( bExp == 0 ) {
+ if ( ( bSig0 | bSig1 ) == 0 ) {
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return a;
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ expDiff = aExp - bExp;
+ if ( expDiff < -1 ) return a;
+ shortShift128Left(
+ aSig0 | LIT64( 0x0001000000000000 ),
+ aSig1,
+ 15 - ( expDiff < 0 ),
+ &aSig0,
+ &aSig1
+ );
+ shortShift128Left(
+ bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+ q = le128( bSig0, bSig1, aSig0, aSig1 );
+ if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+ expDiff -= 64;
+ while ( 0 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ q = ( 4 < q ) ? q - 4 : 0;
+ mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+ shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero );
+ shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero );
+ sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 );
+ expDiff -= 61;
+ }
+ if ( -64 < expDiff ) {
+ q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+ q = ( 4 < q ) ? q - 4 : 0;
+ q >>= - expDiff;
+ shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+ expDiff += 52;
+ if ( expDiff < 0 ) {
+ shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+ }
+ else {
+ shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 );
+ }
+ mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+ sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 );
+ }
+ else {
+ shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 );
+ shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+ }
+ do {
+ alternateASig0 = aSig0;
+ alternateASig1 = aSig1;
+ ++q;
+ sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+ } while ( 0 <= (sbits64) aSig0 );
+ add128(
+ aSig0, aSig1, alternateASig0, alternateASig1, (bits64 *)&sigMean0, &sigMean1 );
+ if ( ( sigMean0 < 0 )
+ || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) {
+ aSig0 = alternateASig0;
+ aSig1 = alternateASig1;
+ }
+ zSign = ( (sbits64) aSig0 < 0 );
+ if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 );
+ return
+ normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the quadruple-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_sqrt( float128 a STATUS_PARAM )
+{
+ flag aSign;
+ int32 aExp, zExp;
+ bits64 aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0;
+ bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+ float128 z;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a STATUS_VAR );
+ if ( ! aSign ) return a;
+ goto invalid;
+ }
+ if ( aSign ) {
+ if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a;
+ invalid:
+ float_raise( float_flag_invalid STATUS_VAR);
+ z.low = float128_default_nan_low;
+ z.high = float128_default_nan_high;
+ return z;
+ }
+ if ( aExp == 0 ) {
+ if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 );
+ normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+ }
+ zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE;
+ aSig0 |= LIT64( 0x0001000000000000 );
+ zSig0 = estimateSqrt32( aExp, aSig0>>17 );
+ shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 );
+ zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 );
+ doubleZSig0 = zSig0<<1;
+ mul64To128( zSig0, zSig0, &term0, &term1 );
+ sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+ while ( (sbits64) rem0 < 0 ) {
+ --zSig0;
+ doubleZSig0 -= 2;
+ add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 );
+ }
+ zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 );
+ if ( ( zSig1 & 0x1FFF ) <= 5 ) {
+ if ( zSig1 == 0 ) zSig1 = 1;
+ mul64To128( doubleZSig0, zSig1, &term1, &term2 );
+ sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+ mul64To128( zSig1, zSig1, &term2, &term3 );
+ sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+ while ( (sbits64) rem1 < 0 ) {
+ --zSig1;
+ shortShift128Left( 0, zSig1, 1, &term2, &term3 );
+ term3 |= 1;
+ term2 |= doubleZSig0;
+ add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 );
+ }
+ zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+ }
+ shift128ExtraRightJamming( zSig0, zSig1, 0, 14, &zSig0, &zSig1, &zSig2 );
+ return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_eq( float128 a, float128 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise. The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_le( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_lt( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise. The invalid exception is
+| raised if either operand is a NaN. Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_eq_signaling( float128 a, float128 b STATUS_PARAM )
+{
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ return 0;
+ }
+ return
+ ( a.low == b.low )
+ && ( ( a.high == b.high )
+ || ( ( a.low == 0 )
+ && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+ );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not
+| cause an exception. Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_le_quiet( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ || ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ == 0 );
+ }
+ return
+ aSign ? le128( b.high, b.low, a.high, a.low )
+ : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an
+| exception. Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_lt_quiet( float128 a, float128 b STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if ( ( ( extractFloat128Exp( a ) == 0x7FFF )
+ && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+ || ( ( extractFloat128Exp( b ) == 0x7FFF )
+ && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+ ) {
+ if ( float128_is_signaling_nan( a )
+ || float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return 0;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ return
+ aSign
+ && ( ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+ != 0 );
+ }
+ return
+ aSign ? lt128( b.high, b.low, a.high, a.low )
+ : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+/* misc functions */
+float32 uint32_to_float32( unsigned int a STATUS_PARAM )
+{
+ return int64_to_float32(a STATUS_VAR);
+}
+
+float64 uint32_to_float64( unsigned int a STATUS_PARAM )
+{
+ return int64_to_float64(a STATUS_VAR);
+}
+
+unsigned int float32_to_uint32( float32 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float32_to_int64(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float32_to_int64_round_to_zero(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float32_to_uint16_round_to_zero( float32 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float32_to_int64_round_to_zero(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffff) {
+ res = 0xffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float64_to_uint32( float64 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float64_to_int64(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float64_to_int64_round_to_zero(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffffffff) {
+ res = 0xffffffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+unsigned int float64_to_uint16_round_to_zero( float64 a STATUS_PARAM )
+{
+ int64_t v;
+ unsigned int res;
+
+ v = float64_to_int64_round_to_zero(a STATUS_VAR);
+ if (v < 0) {
+ res = 0;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else if (v > 0xffff) {
+ res = 0xffff;
+ float_raise( float_flag_invalid STATUS_VAR);
+ } else {
+ res = v;
+ }
+ return res;
+}
+
+/* FIXME: This looks broken. */
+uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
+{
+ int64_t v;
+
+ v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
+ v += float64_val(a);
+ v = float64_to_int64(make_float64(v) STATUS_VAR);
+
+ return v - INT64_MIN;
+}
+
+uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
+{
+ int64_t v;
+
+ v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
+ v += float64_val(a);
+ v = float64_to_int64_round_to_zero(make_float64(v) STATUS_VAR);
+
+ return v - INT64_MIN;
+}
+
+#define COMPARE(s, nan_exp) \
+INLINE int float ## s ## _compare_internal( float ## s a, float ## s b, \
+ int is_quiet STATUS_PARAM ) \
+{ \
+ flag aSign, bSign; \
+ bits ## s av, bv; \
+ a = float ## s ## _squash_input_denormal(a STATUS_VAR); \
+ b = float ## s ## _squash_input_denormal(b STATUS_VAR); \
+ \
+ if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) && \
+ extractFloat ## s ## Frac( a ) ) || \
+ ( ( extractFloat ## s ## Exp( b ) == nan_exp ) && \
+ extractFloat ## s ## Frac( b ) )) { \
+ if (!is_quiet || \
+ float ## s ## _is_signaling_nan( a ) || \
+ float ## s ## _is_signaling_nan( b ) ) { \
+ float_raise( float_flag_invalid STATUS_VAR); \
+ } \
+ return float_relation_unordered; \
+ } \
+ aSign = extractFloat ## s ## Sign( a ); \
+ bSign = extractFloat ## s ## Sign( b ); \
+ av = float ## s ## _val(a); \
+ bv = float ## s ## _val(b); \
+ if ( aSign != bSign ) { \
+ if ( (bits ## s) ( ( av | bv )<<1 ) == 0 ) { \
+ /* zero case */ \
+ return float_relation_equal; \
+ } else { \
+ return 1 - (2 * aSign); \
+ } \
+ } else { \
+ if (av == bv) { \
+ return float_relation_equal; \
+ } else { \
+ return 1 - 2 * (aSign ^ ( av < bv )); \
+ } \
+ } \
+} \
+ \
+int float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM ) \
+{ \
+ return float ## s ## _compare_internal(a, b, 0 STATUS_VAR); \
+} \
+ \
+int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM ) \
+{ \
+ return float ## s ## _compare_internal(a, b, 1 STATUS_VAR); \
+}
+
+COMPARE(32, 0xff)
+COMPARE(64, 0x7ff)
+
+INLINE int float128_compare_internal( float128 a, float128 b,
+ int is_quiet STATUS_PARAM )
+{
+ flag aSign, bSign;
+
+ if (( ( extractFloat128Exp( a ) == 0x7fff ) &&
+ ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) ||
+ ( ( extractFloat128Exp( b ) == 0x7fff ) &&
+ ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )) {
+ if (!is_quiet ||
+ float128_is_signaling_nan( a ) ||
+ float128_is_signaling_nan( b ) ) {
+ float_raise( float_flag_invalid STATUS_VAR);
+ }
+ return float_relation_unordered;
+ }
+ aSign = extractFloat128Sign( a );
+ bSign = extractFloat128Sign( b );
+ if ( aSign != bSign ) {
+ if ( ( ( ( a.high | b.high )<<1 ) | a.low | b.low ) == 0 ) {
+ /* zero case */
+ return float_relation_equal;
+ } else {
+ return 1 - (2 * aSign);
+ }
+ } else {
+ if (a.low == b.low && a.high == b.high) {
+ return float_relation_equal;
+ } else {
+ return 1 - 2 * (aSign ^ ( lt128( a.high, a.low, b.high, b.low ) ));
+ }
+ }
+}
+
+int float128_compare( float128 a, float128 b STATUS_PARAM )
+{
+ return float128_compare_internal(a, b, 0 STATUS_VAR);
+}
+
+int float128_compare_quiet( float128 a, float128 b STATUS_PARAM )
+{
+ return float128_compare_internal(a, b, 1 STATUS_VAR);
+}
+
+/* Multiply A by 2 raised to the power N. */
+float32 float32_scalbn( float32 a, int n STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits32 aSig;
+
+ a = float32_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat32Frac( a );
+ aExp = extractFloat32Exp( a );
+ aSign = extractFloat32Sign( a );
+
+ if ( aExp == 0xFF ) {
+ return a;
+ }
+ if ( aExp != 0 )
+ aSig |= 0x00800000;
+ else if ( aSig == 0 )
+ return a;
+
+ aExp += n - 1;
+ aSig <<= 7;
+ return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
+}
+
+float64 float64_scalbn( float64 a, int n STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+
+ a = float64_squash_input_denormal(a STATUS_VAR);
+ aSig = extractFloat64Frac( a );
+ aExp = extractFloat64Exp( a );
+ aSign = extractFloat64Sign( a );
+
+ if ( aExp == 0x7FF ) {
+ return a;
+ }
+ if ( aExp != 0 )
+ aSig |= LIT64( 0x0010000000000000 );
+ else if ( aSig == 0 )
+ return a;
+
+ aExp += n - 1;
+ aSig <<= 10;
+ return normalizeRoundAndPackFloat64( aSign, aExp, aSig STATUS_VAR );
+}
+
+#ifdef FLOATX80
+floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
+{
+ flag aSign;
+ int16 aExp;
+ bits64 aSig;
+
+ aSig = extractFloatx80Frac( a );
+ aExp = extractFloatx80Exp( a );
+ aSign = extractFloatx80Sign( a );
+
+ if ( aExp == 0x7FF ) {
+ return a;
+ }
+ if (aExp == 0 && aSig == 0)
+ return a;
+
+ aExp += n;
+ 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;
+ int32 aExp;
+ bits64 aSig0, aSig1;
+
+ aSig1 = extractFloat128Frac1( a );
+ aSig0 = extractFloat128Frac0( a );
+ aExp = extractFloat128Exp( a );
+ aSign = extractFloat128Sign( a );
+ if ( aExp == 0x7FFF ) {
+ return a;
+ }
+ if ( aExp != 0 )
+ aSig0 |= LIT64( 0x0001000000000000 );
+ else if ( aSig0 == 0 && aSig1 == 0 )
+ return a;
+
+ aExp += n - 1;
+ return normalizeRoundAndPackFloat128( aSign, aExp, aSig0, aSig1
+ STATUS_VAR );
+
+}
+#endif
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
new file mode 100644
index 0000000..4a5345c
--- /dev/null
+++ b/fpu/softfloat.h
@@ -0,0 +1,583 @@
+/*============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
+Package, Release 2b.
+
+Written by John R. Hauser. This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704. Funding was partially provided by the
+National Science Foundation under grant MIP-9311980. The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek. More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#ifndef SOFTFLOAT_H
+#define SOFTFLOAT_H
+
+#if defined(CONFIG_SOLARIS) && defined(CONFIG_NEEDS_LIBSUNMATH)
+#include <sunmath.h>
+#endif
+
+#include <inttypes.h>
+#include "config.h"
+
+/*----------------------------------------------------------------------------
+| Each of the following `typedef's defines the most convenient type that holds
+| integers of at least as many bits as specified. For example, `uint8' should
+| be the most convenient type that can hold unsigned integers of as many as
+| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most
+| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
+| to the same as `int'.
+*----------------------------------------------------------------------------*/
+typedef uint8_t flag;
+typedef uint8_t uint8;
+typedef int8_t int8;
+#ifndef _AIX
+typedef int uint16;
+typedef int int16;
+#endif
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef uint64_t uint64;
+typedef int64_t int64;
+
+/*----------------------------------------------------------------------------
+| Each of the following `typedef's defines a type that holds integers
+| of _exactly_ the number of bits specified. For instance, for most
+| implementation of C, `bits16' and `sbits16' should be `typedef'ed to
+| `unsigned short int' and `signed short int' (or `short int'), respectively.
+*----------------------------------------------------------------------------*/
+typedef uint8_t bits8;
+typedef int8_t sbits8;
+typedef uint16_t bits16;
+typedef int16_t sbits16;
+typedef uint32_t bits32;
+typedef int32_t sbits32;
+typedef uint64_t bits64;
+typedef int64_t sbits64;
+
+#define LIT64( a ) a##LL
+#define INLINE static inline
+
+/*----------------------------------------------------------------------------
+| 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'.
+*----------------------------------------------------------------------------*/
+#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
+#define STATUS_VAR , status
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point ordering relations
+*----------------------------------------------------------------------------*/
+enum {
+ float_relation_less = -1,
+ float_relation_equal = 0,
+ float_relation_greater = 1,
+ float_relation_unordered = 2
+};
+
+#ifdef CONFIG_SOFTFLOAT
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point types.
+*----------------------------------------------------------------------------*/
+/* Use structures for soft-float types. This prevents accidentally mixing
+ them with native int/float types. A sufficiently clever compiler and
+ sane ABI should be able to see though these structs. However
+ x86/gcc 3.x seems to struggle a bit, so leave them disabled by default. */
+//#define USE_SOFTFLOAT_STRUCT_TYPES
+#ifdef USE_SOFTFLOAT_STRUCT_TYPES
+typedef struct {
+ uint32_t v;
+} float32;
+/* The cast ensures an error if the wrong type is passed. */
+#define float32_val(x) (((float32)(x)).v)
+#define make_float32(x) __extension__ ({ float32 f32_val = {x}; f32_val; })
+typedef struct {
+ uint64_t v;
+} float64;
+#define float64_val(x) (((float64)(x)).v)
+#define make_float64(x) __extension__ ({ float64 f64_val = {x}; f64_val; })
+#else
+typedef uint32_t float32;
+typedef uint64_t float64;
+#define float32_val(x) (x)
+#define float64_val(x) (x)
+#define make_float32(x) (x)
+#define make_float64(x) (x)
+#endif
+#ifdef FLOATX80
+typedef struct {
+ uint64_t low;
+ uint16_t high;
+} floatx80;
+#endif
+#ifdef FLOAT128
+typedef struct {
+#ifdef HOST_WORDS_BIGENDIAN
+ uint64_t high, low;
+#else
+ uint64_t low, high;
+#endif
+} float128;
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point underflow tininess-detection mode.
+*----------------------------------------------------------------------------*/
+enum {
+ float_tininess_after_rounding = 0,
+ float_tininess_before_rounding = 1
+};
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point rounding mode.
+*----------------------------------------------------------------------------*/
+enum {
+ float_round_nearest_even = 0,
+ float_round_down = 1,
+ float_round_up = 2,
+ float_round_to_zero = 3
+};
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point exception flags.
+*----------------------------------------------------------------------------*/
+enum {
+ float_flag_invalid = 1,
+ float_flag_divbyzero = 4,
+ float_flag_overflow = 8,
+ float_flag_underflow = 16,
+ float_flag_inexact = 32,
+ float_flag_input_denormal = 64
+};
+
+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? */
+ flag flush_inputs_to_zero;
+ flag default_nan_mode;
+} float_status;
+
+void set_float_rounding_mode(int val STATUS_PARAM);
+void set_float_exception_flags(int val STATUS_PARAM);
+INLINE void set_flush_to_zero(flag val STATUS_PARAM)
+{
+ STATUS(flush_to_zero) = val;
+}
+INLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM)
+{
+ STATUS(flush_inputs_to_zero) = val;
+}
+INLINE void set_default_nan_mode(flag val STATUS_PARAM)
+{
+ STATUS(default_nan_mode) = val;
+}
+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
+| exception flags.
+*----------------------------------------------------------------------------*/
+void float_raise( int8 flags STATUS_PARAM);
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32( int STATUS_PARAM );
+float64 int32_to_float64( int STATUS_PARAM );
+float32 uint32_to_float32( unsigned 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 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 half-precision conversion routines.
+*----------------------------------------------------------------------------*/
+bits16 float32_to_float16( float32, flag STATUS_PARAM );
+float32 float16_to_float32( bits16, flag STATUS_PARAM );
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int16_round_to_zero( float32 STATUS_PARAM );
+unsigned int float32_to_uint16_round_to_zero( float32 STATUS_PARAM );
+int float32_to_int32( float32 STATUS_PARAM );
+int float32_to_int32_round_to_zero( float32 STATUS_PARAM );
+unsigned int float32_to_uint32( float32 STATUS_PARAM );
+unsigned int float32_to_uint32_round_to_zero( float32 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 );
+float32 float32_add( float32, float32 STATUS_PARAM );
+float32 float32_sub( float32, float32 STATUS_PARAM );
+float32 float32_mul( float32, float32 STATUS_PARAM );
+float32 float32_div( float32, float32 STATUS_PARAM );
+float32 float32_rem( float32, float32 STATUS_PARAM );
+float32 float32_sqrt( float32 STATUS_PARAM );
+float32 float32_exp2( float32 STATUS_PARAM );
+float32 float32_log2( float32 STATUS_PARAM );
+int float32_eq( float32, float32 STATUS_PARAM );
+int float32_le( float32, float32 STATUS_PARAM );
+int float32_lt( float32, float32 STATUS_PARAM );
+int float32_eq_signaling( float32, float32 STATUS_PARAM );
+int float32_le_quiet( float32, float32 STATUS_PARAM );
+int float32_lt_quiet( float32, float32 STATUS_PARAM );
+int float32_compare( float32, float32 STATUS_PARAM );
+int float32_compare_quiet( float32, float32 STATUS_PARAM );
+int float32_is_quiet_nan( float32 );
+int float32_is_signaling_nan( float32 );
+float32 float32_maybe_silence_nan( float32 );
+float32 float32_scalbn( float32, int STATUS_PARAM );
+
+INLINE float32 float32_abs(float32 a)
+{
+ /* Note that abs does *not* handle NaN specially, nor does
+ * it flush denormal inputs to zero.
+ */
+ return make_float32(float32_val(a) & 0x7fffffff);
+}
+
+INLINE float32 float32_chs(float32 a)
+{
+ /* Note that chs does *not* handle NaN specially, nor does
+ * it flush denormal inputs to zero.
+ */
+ return make_float32(float32_val(a) ^ 0x80000000);
+}
+
+INLINE int float32_is_infinity(float32 a)
+{
+ return (float32_val(a) & 0x7fffffff) == 0x7f800000;
+}
+
+INLINE int float32_is_neg(float32 a)
+{
+ return float32_val(a) >> 31;
+}
+
+INLINE int float32_is_zero(float32 a)
+{
+ return (float32_val(a) & 0x7fffffff) == 0;
+}
+
+INLINE int float32_is_any_nan(float32 a)
+{
+ return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL);
+}
+
+INLINE int float32_is_zero_or_denormal(float32 a)
+{
+ return (float32_val(a) & 0x7f800000) == 0;
+}
+
+#define float32_zero make_float32(0)
+#define float32_one make_float32(0x3f800000)
+#define float32_ln2 make_float32(0x3f317218)
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int16_round_to_zero( float64 STATUS_PARAM );
+unsigned int float64_to_uint16_round_to_zero( float64 STATUS_PARAM );
+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 a STATUS_PARAM);
+uint64_t 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.
+*----------------------------------------------------------------------------*/
+float64 float64_round_to_int( float64 STATUS_PARAM );
+float64 float64_trunc_to_int( float64 STATUS_PARAM );
+float64 float64_add( float64, float64 STATUS_PARAM );
+float64 float64_sub( float64, float64 STATUS_PARAM );
+float64 float64_mul( float64, float64 STATUS_PARAM );
+float64 float64_div( float64, float64 STATUS_PARAM );
+float64 float64_rem( float64, float64 STATUS_PARAM );
+float64 float64_sqrt( float64 STATUS_PARAM );
+float64 float64_log2( float64 STATUS_PARAM );
+int float64_eq( float64, float64 STATUS_PARAM );
+int float64_le( float64, float64 STATUS_PARAM );
+int float64_lt( float64, float64 STATUS_PARAM );
+int float64_eq_signaling( float64, float64 STATUS_PARAM );
+int float64_le_quiet( float64, float64 STATUS_PARAM );
+int float64_lt_quiet( float64, float64 STATUS_PARAM );
+int float64_compare( float64, float64 STATUS_PARAM );
+int float64_compare_quiet( float64, float64 STATUS_PARAM );
+int float64_is_quiet_nan( float64 a );
+int float64_is_signaling_nan( float64 );
+float64 float64_maybe_silence_nan( float64 );
+float64 float64_scalbn( float64, int STATUS_PARAM );
+
+INLINE float64 float64_abs(float64 a)
+{
+ /* Note that abs does *not* handle NaN specially, nor does
+ * it flush denormal inputs to zero.
+ */
+ return make_float64(float64_val(a) & 0x7fffffffffffffffLL);
+}
+
+INLINE float64 float64_chs(float64 a)
+{
+ /* Note that chs does *not* handle NaN specially, nor does
+ * it flush denormal inputs to zero.
+ */
+ return make_float64(float64_val(a) ^ 0x8000000000000000LL);
+}
+
+INLINE int float64_is_infinity(float64 a)
+{
+ return (float64_val(a) & 0x7fffffffffffffffLL ) == 0x7ff0000000000000LL;
+}
+
+INLINE int float64_is_neg(float64 a)
+{
+ return float64_val(a) >> 63;
+}
+
+INLINE int float64_is_zero(float64 a)
+{
+ return (float64_val(a) & 0x7fffffffffffffffLL) == 0;
+}
+
+INLINE int float64_is_any_nan(float64 a)
+{
+ return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL);
+}
+
+#define float64_zero make_float64(0)
+#define float64_one make_float64(0x3ff0000000000000LL)
+#define float64_ln2 make_float64(0x3fe62e42fefa39efLL)
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| 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 );
+floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
+int floatx80_eq( floatx80, floatx80 STATUS_PARAM );
+int floatx80_le( floatx80, floatx80 STATUS_PARAM );
+int floatx80_lt( floatx80, floatx80 STATUS_PARAM );
+int floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM );
+int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
+int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
+int floatx80_is_quiet_nan( floatx80 );
+int floatx80_is_signaling_nan( floatx80 );
+floatx80 floatx80_maybe_silence_nan( floatx80 );
+floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM );
+
+INLINE floatx80 floatx80_abs(floatx80 a)
+{
+ a.high &= 0x7fff;
+ return a;
+}
+
+INLINE floatx80 floatx80_chs(floatx80 a)
+{
+ a.high ^= 0x8000;
+ return a;
+}
+
+INLINE int floatx80_is_infinity(floatx80 a)
+{
+ return (a.high & 0x7fff) == 0x7fff && a.low == 0;
+}
+
+INLINE int floatx80_is_neg(floatx80 a)
+{
+ return a.high >> 15;
+}
+
+INLINE int floatx80_is_zero(floatx80 a)
+{
+ return (a.high & 0x7fff) == 0 && a.low == 0;
+}
+
+INLINE int floatx80_is_any_nan(floatx80 a)
+{
+ return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1);
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE quadruple-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float128_to_int32( float128 STATUS_PARAM );
+int float128_to_int32_round_to_zero( float128 STATUS_PARAM );
+int64_t float128_to_int64( float128 STATUS_PARAM );
+int64_t 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.
+*----------------------------------------------------------------------------*/
+float128 float128_round_to_int( float128 STATUS_PARAM );
+float128 float128_add( float128, float128 STATUS_PARAM );
+float128 float128_sub( float128, float128 STATUS_PARAM );
+float128 float128_mul( float128, float128 STATUS_PARAM );
+float128 float128_div( float128, float128 STATUS_PARAM );
+float128 float128_rem( float128, float128 STATUS_PARAM );
+float128 float128_sqrt( float128 STATUS_PARAM );
+int float128_eq( float128, float128 STATUS_PARAM );
+int float128_le( float128, float128 STATUS_PARAM );
+int float128_lt( float128, float128 STATUS_PARAM );
+int float128_eq_signaling( float128, float128 STATUS_PARAM );
+int float128_le_quiet( float128, float128 STATUS_PARAM );
+int float128_lt_quiet( float128, float128 STATUS_PARAM );
+int float128_compare( float128, float128 STATUS_PARAM );
+int float128_compare_quiet( float128, float128 STATUS_PARAM );
+int float128_is_quiet_nan( float128 );
+int float128_is_signaling_nan( float128 );
+float128 float128_maybe_silence_nan( float128 );
+float128 float128_scalbn( float128, int STATUS_PARAM );
+
+INLINE float128 float128_abs(float128 a)
+{
+ a.high &= 0x7fffffffffffffffLL;
+ return a;
+}
+
+INLINE float128 float128_chs(float128 a)
+{
+ a.high ^= 0x8000000000000000LL;
+ return a;
+}
+
+INLINE int float128_is_infinity(float128 a)
+{
+ return (a.high & 0x7fffffffffffffffLL) == 0x7fff000000000000LL && a.low == 0;
+}
+
+INLINE int float128_is_neg(float128 a)
+{
+ return a.high >> 63;
+}
+
+INLINE int float128_is_zero(float128 a)
+{
+ return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0;
+}
+
+INLINE int float128_is_any_nan(float128 a)
+{
+ return ((a.high >> 48) & 0x7fff) == 0x7fff &&
+ ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0));
+}
+
+#endif
+
+#else /* CONFIG_SOFTFLOAT */
+
+#include "softfloat-native.h"
+
+#endif /* !CONFIG_SOFTFLOAT */
+
+#endif /* !SOFTFLOAT_H */
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
new file mode 100644
index 0000000..0b33290
--- /dev/null
+++ b/fsdev/qemu-fsdev.c
@@ -0,0 +1,100 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Gautham R Shenoy <ego@in.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include "qemu-fsdev.h"
+#include "qemu-queue.h"
+#include "osdep.h"
+#include "qemu-common.h"
+#include "qemu-config.h"
+
+static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries =
+ QTAILQ_HEAD_INITIALIZER(fstype_entries);
+
+static FsTypeTable FsTypes[] = {
+ { .name = "local", .ops = &local_ops},
+};
+
+int qemu_fsdev_add(QemuOpts *opts)
+{
+ struct FsTypeListEntry *fsle;
+ int i;
+ const char *fsdev_id = qemu_opts_id(opts);
+ const char *fstype = qemu_opt_get(opts, "fstype");
+ const char *path = qemu_opt_get(opts, "path");
+ const char *sec_model = qemu_opt_get(opts, "security_model");
+
+ if (!fsdev_id) {
+ fprintf(stderr, "fsdev: No id specified\n");
+ return -1;
+ }
+
+ if (fstype) {
+ for (i = 0; i < ARRAY_SIZE(FsTypes); i++) {
+ if (strcmp(FsTypes[i].name, fstype) == 0) {
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(FsTypes)) {
+ fprintf(stderr, "fsdev: fstype %s not found\n", fstype);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "fsdev: No fstype specified\n");
+ return -1;
+ }
+
+ if (!sec_model) {
+ fprintf(stderr, "fsdev: No security_model specified.\n");
+ return -1;
+ }
+
+ if (!path) {
+ fprintf(stderr, "fsdev: No path specified.\n");
+ return -1;
+ }
+
+ fsle = qemu_malloc(sizeof(*fsle));
+
+ fsle->fse.fsdev_id = qemu_strdup(fsdev_id);
+ fsle->fse.path = qemu_strdup(path);
+ fsle->fse.security_model = qemu_strdup(sec_model);
+ fsle->fse.ops = FsTypes[i].ops;
+
+ QTAILQ_INSERT_TAIL(&fstype_entries, fsle, next);
+ return 0;
+
+}
+
+FsTypeEntry *get_fsdev_fsentry(char *id)
+{
+ if (id) {
+ struct FsTypeListEntry *fsle;
+
+ QTAILQ_FOREACH(fsle, &fstype_entries, next) {
+ if (strcmp(fsle->fse.fsdev_id, id) == 0) {
+ return &fsle->fse;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void fsdev_register_config(void)
+{
+ qemu_add_opts(&qemu_fsdev_opts);
+ qemu_add_opts(&qemu_virtfs_opts);
+}
+machine_init(fsdev_register_config);
+
diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h
new file mode 100644
index 0000000..a704043
--- /dev/null
+++ b/fsdev/qemu-fsdev.h
@@ -0,0 +1,55 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Gautham R Shenoy <ego@in.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef QEMU_FSDEV_H
+#define QEMU_FSDEV_H
+#include "qemu-option.h"
+#include "hw/file-op-9p.h"
+
+
+/*
+ * A table to store the various file systems and their callback operations.
+ * -----------------
+ * fstype | ops
+ * -----------------
+ * local | local_ops
+ * . |
+ * . |
+ * . |
+ * . |
+ * -----------------
+ * etc
+ */
+typedef struct FsTypeTable {
+ const char *name;
+ FileOperations *ops;
+} FsTypeTable;
+
+/*
+ * Structure to store the various fsdev's passed through command line.
+ */
+typedef struct FsTypeEntry {
+ char *fsdev_id;
+ char *path;
+ char *security_model;
+ FileOperations *ops;
+} FsTypeEntry;
+
+typedef struct FsTypeListEntry {
+ FsTypeEntry fse;
+ QTAILQ_ENTRY(FsTypeListEntry) next;
+} FsTypeListEntry;
+
+int qemu_fsdev_add(QemuOpts *opts);
+FsTypeEntry *get_fsdev_fsentry(char *id);
+extern FileOperations local_ops;
+#endif
diff --git a/gdb-xml/arm-core.xml b/gdb-xml/arm-core.xml
new file mode 100644
index 0000000..6012f34
--- /dev/null
+++ b/gdb-xml/arm-core.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.core">
+ <reg name="r0" bitsize="32"/>
+ <reg name="r1" bitsize="32"/>
+ <reg name="r2" bitsize="32"/>
+ <reg name="r3" bitsize="32"/>
+ <reg name="r4" bitsize="32"/>
+ <reg name="r5" bitsize="32"/>
+ <reg name="r6" bitsize="32"/>
+ <reg name="r7" bitsize="32"/>
+ <reg name="r8" bitsize="32"/>
+ <reg name="r9" bitsize="32"/>
+ <reg name="r10" bitsize="32"/>
+ <reg name="r11" bitsize="32"/>
+ <reg name="r12" bitsize="32"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="lr" bitsize="32"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+
+ <!-- The CPSR is register 25, rather than register 16, because
+ the FPA registers historically were placed between the PC
+ and the CPSR in the "g" packet. -->
+ <reg name="cpsr" bitsize="32" regnum="25"/>
+</feature>
diff --git a/gdb-xml/arm-neon.xml b/gdb-xml/arm-neon.xml
new file mode 100644
index 0000000..ce3ee03
--- /dev/null
+++ b/gdb-xml/arm-neon.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.vfp">
+ <vector id="neon_uint8x8" type="uint8" count="8"/>
+ <vector id="neon_uint16x4" type="uint16" count="4"/>
+ <vector id="neon_uint32x2" type="uint32" count="2"/>
+ <vector id="neon_float32x2" type="ieee_single" count="2"/>
+ <union id="neon_d">
+ <field name="u8" type="neon_uint8x8"/>
+ <field name="u16" type="neon_uint16x4"/>
+ <field name="u32" type="neon_uint32x2"/>
+ <field name="u64" type="uint64"/>
+ <field name="f32" type="neon_float32x2"/>
+ <field name="f64" type="ieee_double"/>
+ </union>
+ <vector id="neon_uint8x16" type="uint8" count="16"/>
+ <vector id="neon_uint16x8" type="uint16" count="8"/>
+ <vector id="neon_uint32x4" type="uint32" count="4"/>
+ <vector id="neon_uint64x2" type="uint64" count="2"/>
+ <vector id="neon_float32x4" type="ieee_single" count="4"/>
+ <vector id="neon_float64x2" type="ieee_double" count="2"/>
+ <union id="neon_q">
+ <field name="u8" type="neon_uint8x16"/>
+ <field name="u16" type="neon_uint16x8"/>
+ <field name="u32" type="neon_uint32x4"/>
+ <field name="u64" type="neon_uint64x2"/>
+ <field name="f32" type="neon_float32x4"/>
+ <field name="f64" type="neon_float64x2"/>
+ </union>
+ <reg name="d0" bitsize="64" type="neon_d"/>
+ <reg name="d1" bitsize="64" type="neon_d"/>
+ <reg name="d2" bitsize="64" type="neon_d"/>
+ <reg name="d3" bitsize="64" type="neon_d"/>
+ <reg name="d4" bitsize="64" type="neon_d"/>
+ <reg name="d5" bitsize="64" type="neon_d"/>
+ <reg name="d6" bitsize="64" type="neon_d"/>
+ <reg name="d7" bitsize="64" type="neon_d"/>
+ <reg name="d8" bitsize="64" type="neon_d"/>
+ <reg name="d9" bitsize="64" type="neon_d"/>
+ <reg name="d10" bitsize="64" type="neon_d"/>
+ <reg name="d11" bitsize="64" type="neon_d"/>
+ <reg name="d12" bitsize="64" type="neon_d"/>
+ <reg name="d13" bitsize="64" type="neon_d"/>
+ <reg name="d14" bitsize="64" type="neon_d"/>
+ <reg name="d15" bitsize="64" type="neon_d"/>
+ <reg name="d16" bitsize="64" type="neon_d"/>
+ <reg name="d17" bitsize="64" type="neon_d"/>
+ <reg name="d18" bitsize="64" type="neon_d"/>
+ <reg name="d19" bitsize="64" type="neon_d"/>
+ <reg name="d20" bitsize="64" type="neon_d"/>
+ <reg name="d21" bitsize="64" type="neon_d"/>
+ <reg name="d22" bitsize="64" type="neon_d"/>
+ <reg name="d23" bitsize="64" type="neon_d"/>
+ <reg name="d24" bitsize="64" type="neon_d"/>
+ <reg name="d25" bitsize="64" type="neon_d"/>
+ <reg name="d26" bitsize="64" type="neon_d"/>
+ <reg name="d27" bitsize="64" type="neon_d"/>
+ <reg name="d28" bitsize="64" type="neon_d"/>
+ <reg name="d29" bitsize="64" type="neon_d"/>
+ <reg name="d30" bitsize="64" type="neon_d"/>
+ <reg name="d31" bitsize="64" type="neon_d"/>
+
+ <reg name="q0" bitsize="128" type="neon_q"/>
+ <reg name="q1" bitsize="128" type="neon_q"/>
+ <reg name="q2" bitsize="128" type="neon_q"/>
+ <reg name="q3" bitsize="128" type="neon_q"/>
+ <reg name="q4" bitsize="128" type="neon_q"/>
+ <reg name="q5" bitsize="128" type="neon_q"/>
+ <reg name="q6" bitsize="128" type="neon_q"/>
+ <reg name="q7" bitsize="128" type="neon_q"/>
+ <reg name="q8" bitsize="128" type="neon_q"/>
+ <reg name="q9" bitsize="128" type="neon_q"/>
+ <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q12" bitsize="128" type="neon_q"/>
+ <reg name="q13" bitsize="128" type="neon_q"/>
+ <reg name="q14" bitsize="128" type="neon_q"/>
+ <reg name="q15" bitsize="128" type="neon_q"/>
+
+ <reg name="fpsid" bitsize="32" type="int" group="float"/>
+ <reg name="fpscr" bitsize="32" type="int" group="float"/>
+ <reg name="fpexc" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/gdb-xml/arm-vfp.xml b/gdb-xml/arm-vfp.xml
new file mode 100644
index 0000000..b20881e
--- /dev/null
+++ b/gdb-xml/arm-vfp.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.vfp">
+ <reg name="d0" bitsize="64" type="float"/>
+ <reg name="d1" bitsize="64" type="float"/>
+ <reg name="d2" bitsize="64" type="float"/>
+ <reg name="d3" bitsize="64" type="float"/>
+ <reg name="d4" bitsize="64" type="float"/>
+ <reg name="d5" bitsize="64" type="float"/>
+ <reg name="d6" bitsize="64" type="float"/>
+ <reg name="d7" bitsize="64" type="float"/>
+ <reg name="d8" bitsize="64" type="float"/>
+ <reg name="d9" bitsize="64" type="float"/>
+ <reg name="d10" bitsize="64" type="float"/>
+ <reg name="d11" bitsize="64" type="float"/>
+ <reg name="d12" bitsize="64" type="float"/>
+ <reg name="d13" bitsize="64" type="float"/>
+ <reg name="d14" bitsize="64" type="float"/>
+ <reg name="d15" bitsize="64" type="float"/>
+
+ <reg name="fpsid" bitsize="32" type="int" group="float"/>
+ <reg name="fpscr" bitsize="32" type="int" group="float"/>
+ <reg name="fpexc" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/gdb-xml/arm-vfp3.xml b/gdb-xml/arm-vfp3.xml
new file mode 100644
index 0000000..227afd8
--- /dev/null
+++ b/gdb-xml/arm-vfp3.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.arm.vfp">
+ <reg name="d0" bitsize="64" type="float"/>
+ <reg name="d1" bitsize="64" type="float"/>
+ <reg name="d2" bitsize="64" type="float"/>
+ <reg name="d3" bitsize="64" type="float"/>
+ <reg name="d4" bitsize="64" type="float"/>
+ <reg name="d5" bitsize="64" type="float"/>
+ <reg name="d6" bitsize="64" type="float"/>
+ <reg name="d7" bitsize="64" type="float"/>
+ <reg name="d8" bitsize="64" type="float"/>
+ <reg name="d9" bitsize="64" type="float"/>
+ <reg name="d10" bitsize="64" type="float"/>
+ <reg name="d11" bitsize="64" type="float"/>
+ <reg name="d12" bitsize="64" type="float"/>
+ <reg name="d13" bitsize="64" type="float"/>
+ <reg name="d14" bitsize="64" type="float"/>
+ <reg name="d15" bitsize="64" type="float"/>
+ <reg name="d16" bitsize="64" type="float"/>
+ <reg name="d17" bitsize="64" type="float"/>
+ <reg name="d18" bitsize="64" type="float"/>
+ <reg name="d19" bitsize="64" type="float"/>
+ <reg name="d20" bitsize="64" type="float"/>
+ <reg name="d21" bitsize="64" type="float"/>
+ <reg name="d22" bitsize="64" type="float"/>
+ <reg name="d23" bitsize="64" type="float"/>
+ <reg name="d24" bitsize="64" type="float"/>
+ <reg name="d25" bitsize="64" type="float"/>
+ <reg name="d26" bitsize="64" type="float"/>
+ <reg name="d27" bitsize="64" type="float"/>
+ <reg name="d28" bitsize="64" type="float"/>
+ <reg name="d29" bitsize="64" type="float"/>
+ <reg name="d30" bitsize="64" type="float"/>
+ <reg name="d31" bitsize="64" type="float"/>
+
+ <reg name="fpsid" bitsize="32" type="int" group="float"/>
+ <reg name="fpscr" bitsize="32" type="int" group="float"/>
+ <reg name="fpexc" bitsize="32" type="int" group="float"/>
+</feature>
diff --git a/gdb-xml/cf-core.xml b/gdb-xml/cf-core.xml
new file mode 100644
index 0000000..b90af30
--- /dev/null
+++ b/gdb-xml/cf-core.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.coldfire.core">
+ <reg name="d0" bitsize="32"/>
+ <reg name="d1" bitsize="32"/>
+ <reg name="d2" bitsize="32"/>
+ <reg name="d3" bitsize="32"/>
+ <reg name="d4" bitsize="32"/>
+ <reg name="d5" bitsize="32"/>
+ <reg name="d6" bitsize="32"/>
+ <reg name="d7" bitsize="32"/>
+ <reg name="a0" bitsize="32" type="data_ptr"/>
+ <reg name="a1" bitsize="32" type="data_ptr"/>
+ <reg name="a2" bitsize="32" type="data_ptr"/>
+ <reg name="a3" bitsize="32" type="data_ptr"/>
+ <reg name="a4" bitsize="32" type="data_ptr"/>
+ <reg name="a5" bitsize="32" type="data_ptr"/>
+ <reg name="fp" bitsize="32" type="data_ptr"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+
+ <reg name="ps" bitsize="32"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+
+</feature>
diff --git a/gdb-xml/cf-fp.xml b/gdb-xml/cf-fp.xml
new file mode 100644
index 0000000..bf71c32
--- /dev/null
+++ b/gdb-xml/cf-fp.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.coldfire.fp">
+ <reg name="fp0" bitsize="64" type="float" group="float"/>
+ <reg name="fp1" bitsize="64" type="float" group="float"/>
+ <reg name="fp2" bitsize="64" type="float" group="float"/>
+ <reg name="fp3" bitsize="64" type="float" group="float"/>
+ <reg name="fp4" bitsize="64" type="float" group="float"/>
+ <reg name="fp5" bitsize="64" type="float" group="float"/>
+ <reg name="fp6" bitsize="64" type="float" group="float"/>
+ <reg name="fp7" bitsize="64" type="float" group="float"/>
+
+
+ <reg name="fpcontrol" bitsize="32" group="float"/>
+ <reg name="fpstatus" bitsize="32" group="float"/>,
+ <reg name="fpiaddr" bitsize="32" type="code_ptr" group="float"/>
+</feature>
diff --git a/gdb-xml/power-altivec.xml b/gdb-xml/power-altivec.xml
new file mode 100644
index 0000000..84f4d27
--- /dev/null
+++ b/gdb-xml/power-altivec.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.altivec">
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4i32" type="int32" count="4"/>
+ <vector id="v8i16" type="int16" count="8"/>
+ <vector id="v16i8" type="int8" count="16"/>
+ <union id="vec128">
+ <field name="uint128" type="uint128"/>
+ <field name="v4_float" type="v4f"/>
+ <field name="v4_int32" type="v4i32"/>
+ <field name="v8_int16" type="v8i16"/>
+ <field name="v16_int8" type="v16i8"/>
+ </union>
+
+ <reg name="vr0" bitsize="128" type="vec128"/>
+ <reg name="vr1" bitsize="128" type="vec128"/>
+ <reg name="vr2" bitsize="128" type="vec128"/>
+ <reg name="vr3" bitsize="128" type="vec128"/>
+ <reg name="vr4" bitsize="128" type="vec128"/>
+ <reg name="vr5" bitsize="128" type="vec128"/>
+ <reg name="vr6" bitsize="128" type="vec128"/>
+ <reg name="vr7" bitsize="128" type="vec128"/>
+ <reg name="vr8" bitsize="128" type="vec128"/>
+ <reg name="vr9" bitsize="128" type="vec128"/>
+ <reg name="vr10" bitsize="128" type="vec128"/>
+ <reg name="vr11" bitsize="128" type="vec128"/>
+ <reg name="vr12" bitsize="128" type="vec128"/>
+ <reg name="vr13" bitsize="128" type="vec128"/>
+ <reg name="vr14" bitsize="128" type="vec128"/>
+ <reg name="vr15" bitsize="128" type="vec128"/>
+ <reg name="vr16" bitsize="128" type="vec128"/>
+ <reg name="vr17" bitsize="128" type="vec128"/>
+ <reg name="vr18" bitsize="128" type="vec128"/>
+ <reg name="vr19" bitsize="128" type="vec128"/>
+ <reg name="vr20" bitsize="128" type="vec128"/>
+ <reg name="vr21" bitsize="128" type="vec128"/>
+ <reg name="vr22" bitsize="128" type="vec128"/>
+ <reg name="vr23" bitsize="128" type="vec128"/>
+ <reg name="vr24" bitsize="128" type="vec128"/>
+ <reg name="vr25" bitsize="128" type="vec128"/>
+ <reg name="vr26" bitsize="128" type="vec128"/>
+ <reg name="vr27" bitsize="128" type="vec128"/>
+ <reg name="vr28" bitsize="128" type="vec128"/>
+ <reg name="vr29" bitsize="128" type="vec128"/>
+ <reg name="vr30" bitsize="128" type="vec128"/>
+ <reg name="vr31" bitsize="128" type="vec128"/>
+
+ <reg name="vscr" bitsize="32" group="vector"/>
+ <reg name="vrsave" bitsize="32" group="vector"/>
+</feature>
diff --git a/gdb-xml/power-core.xml b/gdb-xml/power-core.xml
new file mode 100644
index 0000000..0c69e8c
--- /dev/null
+++ b/gdb-xml/power-core.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.core">
+ <reg name="r0" bitsize="32" type="uint32"/>
+ <reg name="r1" bitsize="32" type="uint32"/>
+ <reg name="r2" bitsize="32" type="uint32"/>
+ <reg name="r3" bitsize="32" type="uint32"/>
+ <reg name="r4" bitsize="32" type="uint32"/>
+ <reg name="r5" bitsize="32" type="uint32"/>
+ <reg name="r6" bitsize="32" type="uint32"/>
+ <reg name="r7" bitsize="32" type="uint32"/>
+ <reg name="r8" bitsize="32" type="uint32"/>
+ <reg name="r9" bitsize="32" type="uint32"/>
+ <reg name="r10" bitsize="32" type="uint32"/>
+ <reg name="r11" bitsize="32" type="uint32"/>
+ <reg name="r12" bitsize="32" type="uint32"/>
+ <reg name="r13" bitsize="32" type="uint32"/>
+ <reg name="r14" bitsize="32" type="uint32"/>
+ <reg name="r15" bitsize="32" type="uint32"/>
+ <reg name="r16" bitsize="32" type="uint32"/>
+ <reg name="r17" bitsize="32" type="uint32"/>
+ <reg name="r18" bitsize="32" type="uint32"/>
+ <reg name="r19" bitsize="32" type="uint32"/>
+ <reg name="r20" bitsize="32" type="uint32"/>
+ <reg name="r21" bitsize="32" type="uint32"/>
+ <reg name="r22" bitsize="32" type="uint32"/>
+ <reg name="r23" bitsize="32" type="uint32"/>
+ <reg name="r24" bitsize="32" type="uint32"/>
+ <reg name="r25" bitsize="32" type="uint32"/>
+ <reg name="r26" bitsize="32" type="uint32"/>
+ <reg name="r27" bitsize="32" type="uint32"/>
+ <reg name="r28" bitsize="32" type="uint32"/>
+ <reg name="r29" bitsize="32" type="uint32"/>
+ <reg name="r30" bitsize="32" type="uint32"/>
+ <reg name="r31" bitsize="32" type="uint32"/>
+
+ <reg name="pc" bitsize="32" type="code_ptr" regnum="64"/>
+ <reg name="msr" bitsize="32" type="uint32"/>
+ <reg name="cr" bitsize="32" type="uint32"/>
+ <reg name="lr" bitsize="32" type="code_ptr"/>
+ <reg name="ctr" bitsize="32" type="uint32"/>
+ <reg name="xer" bitsize="32" type="uint32"/>
+</feature>
diff --git a/gdb-xml/power-fpu.xml b/gdb-xml/power-fpu.xml
new file mode 100644
index 0000000..3870551
--- /dev/null
+++ b/gdb-xml/power-fpu.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.fpu">
+ <reg name="f0" bitsize="64" type="ieee_double" regnum="71"/>
+ <reg name="f1" bitsize="64" type="ieee_double"/>
+ <reg name="f2" bitsize="64" type="ieee_double"/>
+ <reg name="f3" bitsize="64" type="ieee_double"/>
+ <reg name="f4" bitsize="64" type="ieee_double"/>
+ <reg name="f5" bitsize="64" type="ieee_double"/>
+ <reg name="f6" bitsize="64" type="ieee_double"/>
+ <reg name="f7" bitsize="64" type="ieee_double"/>
+ <reg name="f8" bitsize="64" type="ieee_double"/>
+ <reg name="f9" bitsize="64" type="ieee_double"/>
+ <reg name="f10" bitsize="64" type="ieee_double"/>
+ <reg name="f11" bitsize="64" type="ieee_double"/>
+ <reg name="f12" bitsize="64" type="ieee_double"/>
+ <reg name="f13" bitsize="64" type="ieee_double"/>
+ <reg name="f14" bitsize="64" type="ieee_double"/>
+ <reg name="f15" bitsize="64" type="ieee_double"/>
+ <reg name="f16" bitsize="64" type="ieee_double"/>
+ <reg name="f17" bitsize="64" type="ieee_double"/>
+ <reg name="f18" bitsize="64" type="ieee_double"/>
+ <reg name="f19" bitsize="64" type="ieee_double"/>
+ <reg name="f20" bitsize="64" type="ieee_double"/>
+ <reg name="f21" bitsize="64" type="ieee_double"/>
+ <reg name="f22" bitsize="64" type="ieee_double"/>
+ <reg name="f23" bitsize="64" type="ieee_double"/>
+ <reg name="f24" bitsize="64" type="ieee_double"/>
+ <reg name="f25" bitsize="64" type="ieee_double"/>
+ <reg name="f26" bitsize="64" type="ieee_double"/>
+ <reg name="f27" bitsize="64" type="ieee_double"/>
+ <reg name="f28" bitsize="64" type="ieee_double"/>
+ <reg name="f29" bitsize="64" type="ieee_double"/>
+ <reg name="f30" bitsize="64" type="ieee_double"/>
+ <reg name="f31" bitsize="64" type="ieee_double"/>
+
+ <reg name="fpscr" bitsize="32" group="float"/>
+</feature>
diff --git a/gdb-xml/power-spe.xml b/gdb-xml/power-spe.xml
new file mode 100644
index 0000000..57740cc
--- /dev/null
+++ b/gdb-xml/power-spe.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.spe">
+ <reg name="ev0h" bitsize="32" regnum="71"/>
+ <reg name="ev1h" bitsize="32"/>
+ <reg name="ev2h" bitsize="32"/>
+ <reg name="ev3h" bitsize="32"/>
+ <reg name="ev4h" bitsize="32"/>
+ <reg name="ev5h" bitsize="32"/>
+ <reg name="ev6h" bitsize="32"/>
+ <reg name="ev7h" bitsize="32"/>
+ <reg name="ev8h" bitsize="32"/>
+ <reg name="ev9h" bitsize="32"/>
+ <reg name="ev10h" bitsize="32"/>
+ <reg name="ev11h" bitsize="32"/>
+ <reg name="ev12h" bitsize="32"/>
+ <reg name="ev13h" bitsize="32"/>
+ <reg name="ev14h" bitsize="32"/>
+ <reg name="ev15h" bitsize="32"/>
+ <reg name="ev16h" bitsize="32"/>
+ <reg name="ev17h" bitsize="32"/>
+ <reg name="ev18h" bitsize="32"/>
+ <reg name="ev19h" bitsize="32"/>
+ <reg name="ev20h" bitsize="32"/>
+ <reg name="ev21h" bitsize="32"/>
+ <reg name="ev22h" bitsize="32"/>
+ <reg name="ev23h" bitsize="32"/>
+ <reg name="ev24h" bitsize="32"/>
+ <reg name="ev25h" bitsize="32"/>
+ <reg name="ev26h" bitsize="32"/>
+ <reg name="ev27h" bitsize="32"/>
+ <reg name="ev28h" bitsize="32"/>
+ <reg name="ev29h" bitsize="32"/>
+ <reg name="ev30h" bitsize="32"/>
+ <reg name="ev31h" bitsize="32"/>
+
+ <reg name="acc" bitsize="64"/>
+ <reg name="spefscr" bitsize="32"/>
+</feature>
diff --git a/gdb-xml/power64-core.xml b/gdb-xml/power64-core.xml
new file mode 100644
index 0000000..6cc1531
--- /dev/null
+++ b/gdb-xml/power64-core.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.core">
+ <reg name="r0" bitsize="64" type="uint64"/>
+ <reg name="r1" bitsize="64" type="uint64"/>
+ <reg name="r2" bitsize="64" type="uint64"/>
+ <reg name="r3" bitsize="64" type="uint64"/>
+ <reg name="r4" bitsize="64" type="uint64"/>
+ <reg name="r5" bitsize="64" type="uint64"/>
+ <reg name="r6" bitsize="64" type="uint64"/>
+ <reg name="r7" bitsize="64" type="uint64"/>
+ <reg name="r8" bitsize="64" type="uint64"/>
+ <reg name="r9" bitsize="64" type="uint64"/>
+ <reg name="r10" bitsize="64" type="uint64"/>
+ <reg name="r11" bitsize="64" type="uint64"/>
+ <reg name="r12" bitsize="64" type="uint64"/>
+ <reg name="r13" bitsize="64" type="uint64"/>
+ <reg name="r14" bitsize="64" type="uint64"/>
+ <reg name="r15" bitsize="64" type="uint64"/>
+ <reg name="r16" bitsize="64" type="uint64"/>
+ <reg name="r17" bitsize="64" type="uint64"/>
+ <reg name="r18" bitsize="64" type="uint64"/>
+ <reg name="r19" bitsize="64" type="uint64"/>
+ <reg name="r20" bitsize="64" type="uint64"/>
+ <reg name="r21" bitsize="64" type="uint64"/>
+ <reg name="r22" bitsize="64" type="uint64"/>
+ <reg name="r23" bitsize="64" type="uint64"/>
+ <reg name="r24" bitsize="64" type="uint64"/>
+ <reg name="r25" bitsize="64" type="uint64"/>
+ <reg name="r26" bitsize="64" type="uint64"/>
+ <reg name="r27" bitsize="64" type="uint64"/>
+ <reg name="r28" bitsize="64" type="uint64"/>
+ <reg name="r29" bitsize="64" type="uint64"/>
+ <reg name="r30" bitsize="64" type="uint64"/>
+ <reg name="r31" bitsize="64" type="uint64"/>
+
+ <reg name="pc" bitsize="64" type="code_ptr" regnum="64"/>
+ <reg name="msr" bitsize="64" type="uint64"/>
+ <reg name="cr" bitsize="32" type="uint32"/>
+ <reg name="lr" bitsize="64" type="code_ptr"/>
+ <reg name="ctr" bitsize="64" type="uint64"/>
+ <reg name="xer" bitsize="32" type="uint32"/>
+</feature>
diff --git a/gdbstub.c b/gdbstub.c
new file mode 100644
index 0000000..14e8b9b
--- /dev/null
+++ b/gdbstub.c
@@ -0,0 +1,2695 @@
+/*
+ * gdb server stub
+ *
+ * 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 "qemu-common.h"
+#ifdef CONFIG_USER_ONLY
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "qemu.h"
+#else
+#include "monitor.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#endif
+#include "qemu-kvm.h"
+
+#define MAX_PACKET_LENGTH 4096
+
+#include "exec-all.h"
+#include "qemu_socket.h"
+#include "kvm.h"
+
+
+enum {
+ GDB_SIGNAL_0 = 0,
+ GDB_SIGNAL_INT = 2,
+ GDB_SIGNAL_TRAP = 5,
+ GDB_SIGNAL_UNKNOWN = 143
+};
+
+#ifdef CONFIG_USER_ONLY
+
+/* Map target signal numbers to GDB protocol signal numbers and vice
+ * versa. For user emulation's currently supported systems, we can
+ * assume most signals are defined.
+ */
+
+static int gdb_signal_table[] = {
+ 0,
+ TARGET_SIGHUP,
+ TARGET_SIGINT,
+ TARGET_SIGQUIT,
+ TARGET_SIGILL,
+ TARGET_SIGTRAP,
+ TARGET_SIGABRT,
+ -1, /* SIGEMT */
+ TARGET_SIGFPE,
+ TARGET_SIGKILL,
+ TARGET_SIGBUS,
+ TARGET_SIGSEGV,
+ TARGET_SIGSYS,
+ TARGET_SIGPIPE,
+ TARGET_SIGALRM,
+ TARGET_SIGTERM,
+ TARGET_SIGURG,
+ TARGET_SIGSTOP,
+ TARGET_SIGTSTP,
+ TARGET_SIGCONT,
+ TARGET_SIGCHLD,
+ TARGET_SIGTTIN,
+ TARGET_SIGTTOU,
+ TARGET_SIGIO,
+ TARGET_SIGXCPU,
+ TARGET_SIGXFSZ,
+ TARGET_SIGVTALRM,
+ TARGET_SIGPROF,
+ TARGET_SIGWINCH,
+ -1, /* SIGLOST */
+ TARGET_SIGUSR1,
+ TARGET_SIGUSR2,
+#ifdef TARGET_SIGPWR
+ TARGET_SIGPWR,
+#else
+ -1,
+#endif
+ -1, /* SIGPOLL */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+#ifdef __SIGRTMIN
+ __SIGRTMIN + 1,
+ __SIGRTMIN + 2,
+ __SIGRTMIN + 3,
+ __SIGRTMIN + 4,
+ __SIGRTMIN + 5,
+ __SIGRTMIN + 6,
+ __SIGRTMIN + 7,
+ __SIGRTMIN + 8,
+ __SIGRTMIN + 9,
+ __SIGRTMIN + 10,
+ __SIGRTMIN + 11,
+ __SIGRTMIN + 12,
+ __SIGRTMIN + 13,
+ __SIGRTMIN + 14,
+ __SIGRTMIN + 15,
+ __SIGRTMIN + 16,
+ __SIGRTMIN + 17,
+ __SIGRTMIN + 18,
+ __SIGRTMIN + 19,
+ __SIGRTMIN + 20,
+ __SIGRTMIN + 21,
+ __SIGRTMIN + 22,
+ __SIGRTMIN + 23,
+ __SIGRTMIN + 24,
+ __SIGRTMIN + 25,
+ __SIGRTMIN + 26,
+ __SIGRTMIN + 27,
+ __SIGRTMIN + 28,
+ __SIGRTMIN + 29,
+ __SIGRTMIN + 30,
+ __SIGRTMIN + 31,
+ -1, /* SIGCANCEL */
+ __SIGRTMIN,
+ __SIGRTMIN + 32,
+ __SIGRTMIN + 33,
+ __SIGRTMIN + 34,
+ __SIGRTMIN + 35,
+ __SIGRTMIN + 36,
+ __SIGRTMIN + 37,
+ __SIGRTMIN + 38,
+ __SIGRTMIN + 39,
+ __SIGRTMIN + 40,
+ __SIGRTMIN + 41,
+ __SIGRTMIN + 42,
+ __SIGRTMIN + 43,
+ __SIGRTMIN + 44,
+ __SIGRTMIN + 45,
+ __SIGRTMIN + 46,
+ __SIGRTMIN + 47,
+ __SIGRTMIN + 48,
+ __SIGRTMIN + 49,
+ __SIGRTMIN + 50,
+ __SIGRTMIN + 51,
+ __SIGRTMIN + 52,
+ __SIGRTMIN + 53,
+ __SIGRTMIN + 54,
+ __SIGRTMIN + 55,
+ __SIGRTMIN + 56,
+ __SIGRTMIN + 57,
+ __SIGRTMIN + 58,
+ __SIGRTMIN + 59,
+ __SIGRTMIN + 60,
+ __SIGRTMIN + 61,
+ __SIGRTMIN + 62,
+ __SIGRTMIN + 63,
+ __SIGRTMIN + 64,
+ __SIGRTMIN + 65,
+ __SIGRTMIN + 66,
+ __SIGRTMIN + 67,
+ __SIGRTMIN + 68,
+ __SIGRTMIN + 69,
+ __SIGRTMIN + 70,
+ __SIGRTMIN + 71,
+ __SIGRTMIN + 72,
+ __SIGRTMIN + 73,
+ __SIGRTMIN + 74,
+ __SIGRTMIN + 75,
+ __SIGRTMIN + 76,
+ __SIGRTMIN + 77,
+ __SIGRTMIN + 78,
+ __SIGRTMIN + 79,
+ __SIGRTMIN + 80,
+ __SIGRTMIN + 81,
+ __SIGRTMIN + 82,
+ __SIGRTMIN + 83,
+ __SIGRTMIN + 84,
+ __SIGRTMIN + 85,
+ __SIGRTMIN + 86,
+ __SIGRTMIN + 87,
+ __SIGRTMIN + 88,
+ __SIGRTMIN + 89,
+ __SIGRTMIN + 90,
+ __SIGRTMIN + 91,
+ __SIGRTMIN + 92,
+ __SIGRTMIN + 93,
+ __SIGRTMIN + 94,
+ __SIGRTMIN + 95,
+ -1, /* SIGINFO */
+ -1, /* UNKNOWN */
+ -1, /* DEFAULT */
+ -1,
+ -1,
+ -1,
+ -1,
+ -1,
+ -1
+#endif
+};
+#else
+/* In system mode we only need SIGINT and SIGTRAP; other signals
+ are not yet supported. */
+
+enum {
+ TARGET_SIGINT = 2,
+ TARGET_SIGTRAP = 5
+};
+
+static int gdb_signal_table[] = {
+ -1,
+ -1,
+ TARGET_SIGINT,
+ -1,
+ -1,
+ TARGET_SIGTRAP
+};
+#endif
+
+#ifdef CONFIG_USER_ONLY
+static int target_signal_to_gdb (int sig)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE (gdb_signal_table); i++)
+ if (gdb_signal_table[i] == sig)
+ return i;
+ return GDB_SIGNAL_UNKNOWN;
+}
+#endif
+
+static int gdb_signal_to_target (int sig)
+{
+ if (sig < ARRAY_SIZE (gdb_signal_table))
+ return gdb_signal_table[sig];
+ else
+ return -1;
+}
+
+//#define DEBUG_GDB
+
+typedef struct GDBRegisterState {
+ int base_reg;
+ int num_regs;
+ gdb_reg_cb get_reg;
+ gdb_reg_cb set_reg;
+ const char *xml;
+ struct GDBRegisterState *next;
+} GDBRegisterState;
+
+enum RSState {
+ RS_INACTIVE,
+ RS_IDLE,
+ RS_GETLINE,
+ RS_CHKSUM1,
+ RS_CHKSUM2,
+ RS_SYSCALL,
+};
+typedef struct GDBState {
+ CPUState *c_cpu; /* current CPU for step/continue ops */
+ CPUState *g_cpu; /* current CPU for other ops */
+ CPUState *query_cpu; /* for q{f|s}ThreadInfo */
+ enum RSState state; /* parsing state */
+ char line_buf[MAX_PACKET_LENGTH];
+ int line_buf_index;
+ int line_csum;
+ uint8_t last_packet[MAX_PACKET_LENGTH + 4];
+ int last_packet_len;
+ int signal;
+#ifdef CONFIG_USER_ONLY
+ int fd;
+ int running_state;
+#else
+ CharDriverState *chr;
+ CharDriverState *mon_chr;
+#endif
+} GDBState;
+
+/* By default use no IRQs and no timers while single stepping so as to
+ * make single stepping like an ICE HW step.
+ */
+static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
+
+static GDBState *gdbserver_state;
+
+/* This is an ugly hack to cope with both new and old gdb.
+ If gdb sends qXfer:features:read then assume we're talking to a newish
+ gdb that understands target descriptions. */
+static int gdb_has_xml;
+
+#ifdef CONFIG_USER_ONLY
+/* XXX: This is not thread safe. Do we care? */
+static int gdbserver_fd = -1;
+
+static int get_char(GDBState *s)
+{
+ uint8_t ch;
+ int ret;
+
+ for(;;) {
+ ret = recv(s->fd, &ch, 1, 0);
+ if (ret < 0) {
+ if (errno == ECONNRESET)
+ s->fd = -1;
+ if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ } else if (ret == 0) {
+ close(s->fd);
+ s->fd = -1;
+ return -1;
+ } else {
+ break;
+ }
+ }
+ return ch;
+}
+#endif
+
+static gdb_syscall_complete_cb gdb_current_syscall_cb;
+
+static enum {
+ GDB_SYS_UNKNOWN,
+ GDB_SYS_ENABLED,
+ GDB_SYS_DISABLED,
+} gdb_syscall_mode;
+
+/* If gdb is connected when the first semihosting syscall occurs then use
+ remote gdb syscalls. Otherwise use native file IO. */
+int use_gdb_syscalls(void)
+{
+ if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
+ gdb_syscall_mode = (gdbserver_state ? GDB_SYS_ENABLED
+ : GDB_SYS_DISABLED);
+ }
+ return gdb_syscall_mode == GDB_SYS_ENABLED;
+}
+
+/* Resume execution. */
+static inline void gdb_continue(GDBState *s)
+{
+#ifdef CONFIG_USER_ONLY
+ s->running_state = 1;
+#else
+ vm_start();
+#endif
+}
+
+static void put_buffer(GDBState *s, const uint8_t *buf, int len)
+{
+#ifdef CONFIG_USER_ONLY
+ int ret;
+
+ while (len > 0) {
+ ret = send(s->fd, buf, len, 0);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+#else
+ qemu_chr_write(s->chr, buf, len);
+#endif
+}
+
+static inline int fromhex(int v)
+{
+ if (v >= '0' && v <= '9')
+ return v - '0';
+ else if (v >= 'A' && v <= 'F')
+ return v - 'A' + 10;
+ else if (v >= 'a' && v <= 'f')
+ return v - 'a' + 10;
+ else
+ return 0;
+}
+
+static inline int tohex(int v)
+{
+ if (v < 10)
+ return v + '0';
+ else
+ return v - 10 + 'a';
+}
+
+static void memtohex(char *buf, const uint8_t *mem, int len)
+{
+ int i, c;
+ char *q;
+ q = buf;
+ for(i = 0; i < len; i++) {
+ c = mem[i];
+ *q++ = tohex(c >> 4);
+ *q++ = tohex(c & 0xf);
+ }
+ *q = '\0';
+}
+
+static void hextomem(uint8_t *mem, const char *buf, int len)
+{
+ int i;
+
+ for(i = 0; i < len; i++) {
+ mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]);
+ buf += 2;
+ }
+}
+
+/* return -1 if error, 0 if OK */
+static int put_packet_binary(GDBState *s, const char *buf, int len)
+{
+ int csum, i;
+ uint8_t *p;
+
+ for(;;) {
+ p = s->last_packet;
+ *(p++) = '$';
+ memcpy(p, buf, len);
+ p += len;
+ csum = 0;
+ for(i = 0; i < len; i++) {
+ csum += buf[i];
+ }
+ *(p++) = '#';
+ *(p++) = tohex((csum >> 4) & 0xf);
+ *(p++) = tohex((csum) & 0xf);
+
+ s->last_packet_len = p - s->last_packet;
+ put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len);
+
+#ifdef CONFIG_USER_ONLY
+ i = get_char(s);
+ if (i < 0)
+ return -1;
+ if (i == '+')
+ break;
+#else
+ break;
+#endif
+ }
+ return 0;
+}
+
+/* return -1 if error, 0 if OK */
+static int put_packet(GDBState *s, const char *buf)
+{
+#ifdef DEBUG_GDB
+ printf("reply='%s'\n", buf);
+#endif
+
+ return put_packet_binary(s, buf, strlen(buf));
+}
+
+/* The GDB remote protocol transfers values in target byte order. This means
+ we can use the raw memory access routines to access the value buffer.
+ Conveniently, these also handle the case where the buffer is mis-aligned.
+ */
+#define GET_REG8(val) do { \
+ stb_p(mem_buf, val); \
+ return 1; \
+ } while(0)
+#define GET_REG16(val) do { \
+ stw_p(mem_buf, val); \
+ return 2; \
+ } while(0)
+#define GET_REG32(val) do { \
+ stl_p(mem_buf, val); \
+ return 4; \
+ } while(0)
+#define GET_REG64(val) do { \
+ stq_p(mem_buf, val); \
+ return 8; \
+ } while(0)
+
+#if TARGET_LONG_BITS == 64
+#define GET_REGL(val) GET_REG64(val)
+#define ldtul_p(addr) ldq_p(addr)
+#else
+#define GET_REGL(val) GET_REG32(val)
+#define ldtul_p(addr) ldl_p(addr)
+#endif
+
+#if defined(TARGET_I386)
+
+#ifdef TARGET_X86_64
+static const int gpr_map[16] = {
+ R_EAX, R_EBX, R_ECX, R_EDX, R_ESI, R_EDI, R_EBP, R_ESP,
+ 8, 9, 10, 11, 12, 13, 14, 15
+};
+#else
+#define gpr_map gpr_map32
+#endif
+static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+
+#define NUM_CORE_REGS (CPU_NB_REGS * 2 + 25)
+
+#define IDX_IP_REG CPU_NB_REGS
+#define IDX_FLAGS_REG (IDX_IP_REG + 1)
+#define IDX_SEG_REGS (IDX_FLAGS_REG + 1)
+#define IDX_FP_REGS (IDX_SEG_REGS + 6)
+#define IDX_XMM_REGS (IDX_FP_REGS + 16)
+#define IDX_MXCSR_REG (IDX_XMM_REGS + CPU_NB_REGS)
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < CPU_NB_REGS) {
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ GET_REG64(env->regs[gpr_map[n]]);
+ } else if (n < CPU_NB_REGS32) {
+ GET_REG32(env->regs[gpr_map32[n]]);
+ }
+ } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) {
+#ifdef USE_X86LDOUBLE
+ /* FIXME: byteswap float values - after fixing fpregs layout. */
+ memcpy(mem_buf, &env->fpregs[n - IDX_FP_REGS], 10);
+#else
+ memset(mem_buf, 0, 10);
+#endif
+ return 10;
+ } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) {
+ n -= IDX_XMM_REGS;
+ if (n < CPU_NB_REGS32 ||
+ (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) {
+ stq_p(mem_buf, env->xmm_regs[n].XMM_Q(0));
+ stq_p(mem_buf + 8, env->xmm_regs[n].XMM_Q(1));
+ return 16;
+ }
+ } else {
+ switch (n) {
+ case IDX_IP_REG:
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ GET_REG64(env->eip);
+ } else {
+ GET_REG32(env->eip);
+ }
+ case IDX_FLAGS_REG: GET_REG32(env->eflags);
+
+ case IDX_SEG_REGS: GET_REG32(env->segs[R_CS].selector);
+ case IDX_SEG_REGS + 1: GET_REG32(env->segs[R_SS].selector);
+ case IDX_SEG_REGS + 2: GET_REG32(env->segs[R_DS].selector);
+ case IDX_SEG_REGS + 3: GET_REG32(env->segs[R_ES].selector);
+ case IDX_SEG_REGS + 4: GET_REG32(env->segs[R_FS].selector);
+ case IDX_SEG_REGS + 5: GET_REG32(env->segs[R_GS].selector);
+
+ case IDX_FP_REGS + 8: GET_REG32(env->fpuc);
+ case IDX_FP_REGS + 9: GET_REG32((env->fpus & ~0x3800) |
+ (env->fpstt & 0x7) << 11);
+ case IDX_FP_REGS + 10: GET_REG32(0); /* ftag */
+ case IDX_FP_REGS + 11: GET_REG32(0); /* fiseg */
+ case IDX_FP_REGS + 12: GET_REG32(0); /* fioff */
+ case IDX_FP_REGS + 13: GET_REG32(0); /* foseg */
+ case IDX_FP_REGS + 14: GET_REG32(0); /* fooff */
+ case IDX_FP_REGS + 15: GET_REG32(0); /* fop */
+
+ case IDX_MXCSR_REG: GET_REG32(env->mxcsr);
+ }
+ }
+ return 0;
+}
+
+static int cpu_x86_gdb_load_seg(CPUState *env, int sreg, uint8_t *mem_buf)
+{
+ uint16_t selector = ldl_p(mem_buf);
+
+ if (selector != env->segs[sreg].selector) {
+#if defined(CONFIG_USER_ONLY)
+ cpu_x86_load_seg(env, sreg, selector);
+#else
+ unsigned int limit, flags;
+ target_ulong base;
+
+ if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
+ base = selector << 4;
+ limit = 0xffff;
+ flags = 0;
+ } else {
+ if (!cpu_x86_get_descr_debug(env, selector, &base, &limit, &flags))
+ return 4;
+ }
+ cpu_x86_load_seg_cache(env, sreg, selector, base, limit, flags);
+#endif
+ }
+ return 4;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ if (n < CPU_NB_REGS) {
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ env->regs[gpr_map[n]] = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ } else if (n < CPU_NB_REGS32) {
+ n = gpr_map32[n];
+ env->regs[n] &= ~0xffffffffUL;
+ env->regs[n] |= (uint32_t)ldl_p(mem_buf);
+ return 4;
+ }
+ } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) {
+#ifdef USE_X86LDOUBLE
+ /* FIXME: byteswap float values - after fixing fpregs layout. */
+ memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10);
+#endif
+ return 10;
+ } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) {
+ n -= IDX_XMM_REGS;
+ if (n < CPU_NB_REGS32 ||
+ (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) {
+ env->xmm_regs[n].XMM_Q(0) = ldq_p(mem_buf);
+ env->xmm_regs[n].XMM_Q(1) = ldq_p(mem_buf + 8);
+ return 16;
+ }
+ } else {
+ switch (n) {
+ case IDX_IP_REG:
+ if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) {
+ env->eip = ldq_p(mem_buf);
+ return 8;
+ } else {
+ env->eip &= ~0xffffffffUL;
+ env->eip |= (uint32_t)ldl_p(mem_buf);
+ return 4;
+ }
+ case IDX_FLAGS_REG:
+ env->eflags = ldl_p(mem_buf);
+ return 4;
+
+ case IDX_SEG_REGS: return cpu_x86_gdb_load_seg(env, R_CS, mem_buf);
+ case IDX_SEG_REGS + 1: return cpu_x86_gdb_load_seg(env, R_SS, mem_buf);
+ case IDX_SEG_REGS + 2: return cpu_x86_gdb_load_seg(env, R_DS, mem_buf);
+ case IDX_SEG_REGS + 3: return cpu_x86_gdb_load_seg(env, R_ES, mem_buf);
+ case IDX_SEG_REGS + 4: return cpu_x86_gdb_load_seg(env, R_FS, mem_buf);
+ case IDX_SEG_REGS + 5: return cpu_x86_gdb_load_seg(env, R_GS, mem_buf);
+
+ case IDX_FP_REGS + 8:
+ env->fpuc = ldl_p(mem_buf);
+ return 4;
+ case IDX_FP_REGS + 9:
+ tmp = ldl_p(mem_buf);
+ env->fpstt = (tmp >> 11) & 7;
+ env->fpus = tmp & ~0x3800;
+ return 4;
+ case IDX_FP_REGS + 10: /* ftag */ return 4;
+ case IDX_FP_REGS + 11: /* fiseg */ return 4;
+ case IDX_FP_REGS + 12: /* fioff */ return 4;
+ case IDX_FP_REGS + 13: /* foseg */ return 4;
+ case IDX_FP_REGS + 14: /* fooff */ return 4;
+ case IDX_FP_REGS + 15: /* fop */ return 4;
+
+ case IDX_MXCSR_REG:
+ env->mxcsr = ldl_p(mem_buf);
+ return 4;
+ }
+ }
+ /* Unrecognised register. */
+ return 0;
+}
+
+#elif defined (TARGET_PPC)
+
+/* Old gdb always expects FP registers. Newer (xml-aware) gdb only
+ expects whatever the target description contains. Due to a
+ historical mishap the FP registers appear in between core integer
+ regs and PC, MSR, CR, and so forth. We hack round this by giving the
+ FP regs zero size when talking to a newer gdb. */
+#define NUM_CORE_REGS 71
+#if defined (TARGET_PPC64)
+#define GDB_CORE_XML "power64-core.xml"
+#else
+#define GDB_CORE_XML "power-core.xml"
+#endif
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ /* gprs */
+ GET_REGL(env->gpr[n]);
+ } else if (n < 64) {
+ /* fprs */
+ if (gdb_has_xml)
+ return 0;
+ stfq_p(mem_buf, env->fpr[n-32]);
+ return 8;
+ } else {
+ switch (n) {
+ case 64: GET_REGL(env->nip);
+ case 65: GET_REGL(env->msr);
+ case 66:
+ {
+ uint32_t cr = 0;
+ int i;
+ for (i = 0; i < 8; i++)
+ cr |= env->crf[i] << (32 - ((i + 1) * 4));
+ GET_REG32(cr);
+ }
+ case 67: GET_REGL(env->lr);
+ case 68: GET_REGL(env->ctr);
+ case 69: GET_REGL(env->xer);
+ case 70:
+ {
+ if (gdb_has_xml)
+ return 0;
+ GET_REG32(0); /* fpscr */
+ }
+ }
+ }
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ /* gprs */
+ env->gpr[n] = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ } else if (n < 64) {
+ /* fprs */
+ if (gdb_has_xml)
+ return 0;
+ env->fpr[n-32] = ldfq_p(mem_buf);
+ return 8;
+ } else {
+ switch (n) {
+ case 64:
+ env->nip = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 65:
+ ppc_store_msr(env, ldtul_p(mem_buf));
+ return sizeof(target_ulong);
+ case 66:
+ {
+ uint32_t cr = ldl_p(mem_buf);
+ int i;
+ for (i = 0; i < 8; i++)
+ env->crf[i] = (cr >> (32 - ((i + 1) * 4))) & 0xF;
+ return 4;
+ }
+ case 67:
+ env->lr = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 68:
+ env->ctr = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 69:
+ env->xer = ldtul_p(mem_buf);
+ return sizeof(target_ulong);
+ case 70:
+ /* fpscr */
+ if (gdb_has_xml)
+ return 0;
+ return 4;
+ }
+ }
+ return 0;
+}
+
+#elif defined (TARGET_SPARC)
+
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+#define NUM_CORE_REGS 86
+#else
+#define NUM_CORE_REGS 72
+#endif
+
+#ifdef TARGET_ABI32
+#define GET_REGA(val) GET_REG32(val)
+#else
+#define GET_REGA(val) GET_REGL(val)
+#endif
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ /* g0..g7 */
+ GET_REGA(env->gregs[n]);
+ }
+ if (n < 32) {
+ /* register window */
+ GET_REGA(env->regwptr[n - 8]);
+ }
+#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
+ if (n < 64) {
+ /* fprs */
+ GET_REG32(*((uint32_t *)&env->fpr[n - 32]));
+ }
+ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+ switch (n) {
+ case 64: GET_REGA(env->y);
+ case 65: GET_REGA(cpu_get_psr(env));
+ case 66: GET_REGA(env->wim);
+ case 67: GET_REGA(env->tbr);
+ case 68: GET_REGA(env->pc);
+ case 69: GET_REGA(env->npc);
+ case 70: GET_REGA(env->fsr);
+ case 71: GET_REGA(0); /* csr */
+ default: GET_REGA(0);
+ }
+#else
+ if (n < 64) {
+ /* f0-f31 */
+ GET_REG32(*((uint32_t *)&env->fpr[n - 32]));
+ }
+ if (n < 80) {
+ /* f32-f62 (double width, even numbers only) */
+ uint64_t val;
+
+ val = (uint64_t)*((uint32_t *)&env->fpr[(n - 64) * 2 + 32]) << 32;
+ val |= *((uint32_t *)&env->fpr[(n - 64) * 2 + 33]);
+ GET_REG64(val);
+ }
+ switch (n) {
+ case 80: GET_REGL(env->pc);
+ case 81: GET_REGL(env->npc);
+ case 82: GET_REGL((cpu_get_ccr(env) << 32) |
+ ((env->asi & 0xff) << 24) |
+ ((env->pstate & 0xfff) << 8) |
+ cpu_get_cwp64(env));
+ case 83: GET_REGL(env->fsr);
+ case 84: GET_REGL(env->fprs);
+ case 85: GET_REGL(env->y);
+ }
+#endif
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+#if defined(TARGET_ABI32)
+ abi_ulong tmp;
+
+ tmp = ldl_p(mem_buf);
+#else
+ target_ulong tmp;
+
+ tmp = ldtul_p(mem_buf);
+#endif
+
+ if (n < 8) {
+ /* g0..g7 */
+ env->gregs[n] = tmp;
+ } else if (n < 32) {
+ /* register window */
+ env->regwptr[n - 8] = tmp;
+ }
+#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
+ else if (n < 64) {
+ /* fprs */
+ *((uint32_t *)&env->fpr[n - 32]) = tmp;
+ } else {
+ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+ switch (n) {
+ case 64: env->y = tmp; break;
+ case 65: cpu_put_psr(env, tmp); break;
+ case 66: env->wim = tmp; break;
+ case 67: env->tbr = tmp; break;
+ case 68: env->pc = tmp; break;
+ case 69: env->npc = tmp; break;
+ case 70: env->fsr = tmp; break;
+ default: return 0;
+ }
+ }
+ return 4;
+#else
+ else if (n < 64) {
+ /* f0-f31 */
+ env->fpr[n] = ldfl_p(mem_buf);
+ return 4;
+ } else if (n < 80) {
+ /* f32-f62 (double width, even numbers only) */
+ *((uint32_t *)&env->fpr[(n - 64) * 2 + 32]) = tmp >> 32;
+ *((uint32_t *)&env->fpr[(n - 64) * 2 + 33]) = tmp;
+ } else {
+ switch (n) {
+ case 80: env->pc = tmp; break;
+ case 81: env->npc = tmp; break;
+ case 82:
+ cpu_put_ccr(env, tmp >> 32);
+ env->asi = (tmp >> 24) & 0xff;
+ env->pstate = (tmp >> 8) & 0xfff;
+ cpu_put_cwp64(env, tmp & 0xff);
+ break;
+ case 83: env->fsr = tmp; break;
+ case 84: env->fprs = tmp; break;
+ case 85: env->y = tmp; break;
+ default: return 0;
+ }
+ }
+ return 8;
+#endif
+}
+#elif defined (TARGET_ARM)
+
+/* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect
+ whatever the target description contains. Due to a historical mishap
+ the FPA registers appear in between core integer regs and the CPSR.
+ We hack round this by giving the FPA regs zero size when talking to a
+ newer gdb. */
+#define NUM_CORE_REGS 26
+#define GDB_CORE_XML "arm-core.xml"
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 16) {
+ /* Core integer register. */
+ GET_REG32(env->regs[n]);
+ }
+ if (n < 24) {
+ /* FPA registers. */
+ if (gdb_has_xml)
+ return 0;
+ memset(mem_buf, 0, 12);
+ return 12;
+ }
+ switch (n) {
+ case 24:
+ /* FPA status register. */
+ if (gdb_has_xml)
+ return 0;
+ GET_REG32(0);
+ case 25:
+ /* CPSR */
+ GET_REG32(cpsr_read(env));
+ }
+ /* Unknown register. */
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ tmp = ldl_p(mem_buf);
+
+ /* Mask out low bit of PC to workaround gdb bugs. This will probably
+ cause problems if we ever implement the Jazelle DBX extensions. */
+ if (n == 15)
+ tmp &= ~1;
+
+ if (n < 16) {
+ /* Core integer register. */
+ env->regs[n] = tmp;
+ return 4;
+ }
+ if (n < 24) { /* 16-23 */
+ /* FPA registers (ignored). */
+ if (gdb_has_xml)
+ return 0;
+ return 12;
+ }
+ switch (n) {
+ case 24:
+ /* FPA status register (ignored). */
+ if (gdb_has_xml)
+ return 0;
+ return 4;
+ case 25:
+ /* CPSR */
+ cpsr_write (env, tmp, 0xffffffff);
+ return 4;
+ }
+ /* Unknown register. */
+ return 0;
+}
+
+#elif defined (TARGET_M68K)
+
+#define NUM_CORE_REGS 18
+
+#define GDB_CORE_XML "cf-core.xml"
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ /* D0-D7 */
+ GET_REG32(env->dregs[n]);
+ } else if (n < 16) {
+ /* A0-A7 */
+ GET_REG32(env->aregs[n - 8]);
+ } else {
+ switch (n) {
+ case 16: GET_REG32(env->sr);
+ case 17: GET_REG32(env->pc);
+ }
+ }
+ /* FP registers not included here because they vary between
+ ColdFire and m68k. Use XML bits for these. */
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ tmp = ldl_p(mem_buf);
+
+ if (n < 8) {
+ /* D0-D7 */
+ env->dregs[n] = tmp;
+ } else if (n < 16) {
+ /* A0-A7 */
+ env->aregs[n - 8] = tmp;
+ } else {
+ switch (n) {
+ case 16: env->sr = tmp; break;
+ case 17: env->pc = tmp; break;
+ default: return 0;
+ }
+ }
+ return 4;
+}
+#elif defined (TARGET_MIPS)
+
+#define NUM_CORE_REGS 73
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ GET_REGL(env->active_tc.gpr[n]);
+ }
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ if (n >= 38 && n < 70) {
+ if (env->CP0_Status & (1 << CP0St_FR))
+ GET_REGL(env->active_fpu.fpr[n - 38].d);
+ else
+ GET_REGL(env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX]);
+ }
+ switch (n) {
+ case 70: GET_REGL((int32_t)env->active_fpu.fcr31);
+ case 71: GET_REGL((int32_t)env->active_fpu.fcr0);
+ }
+ }
+ switch (n) {
+ case 32: GET_REGL((int32_t)env->CP0_Status);
+ case 33: GET_REGL(env->active_tc.LO[0]);
+ case 34: GET_REGL(env->active_tc.HI[0]);
+ case 35: GET_REGL(env->CP0_BadVAddr);
+ case 36: GET_REGL((int32_t)env->CP0_Cause);
+ case 37: GET_REGL(env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16));
+ case 72: GET_REGL(0); /* fp */
+ case 89: GET_REGL((int32_t)env->CP0_PRid);
+ }
+ if (n >= 73 && n <= 88) {
+ /* 16 embedded regs. */
+ GET_REGL(0);
+ }
+
+ return 0;
+}
+
+/* convert MIPS rounding mode in FCR31 to IEEE library */
+static unsigned int ieee_rm[] =
+ {
+ float_round_nearest_even,
+ float_round_to_zero,
+ float_round_up,
+ float_round_down
+ };
+#define RESTORE_ROUNDING_MODE \
+ set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], &env->active_fpu.fp_status)
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ target_ulong tmp;
+
+ tmp = ldtul_p(mem_buf);
+
+ if (n < 32) {
+ env->active_tc.gpr[n] = tmp;
+ return sizeof(target_ulong);
+ }
+ if (env->CP0_Config1 & (1 << CP0C1_FP)
+ && n >= 38 && n < 73) {
+ if (n < 70) {
+ if (env->CP0_Status & (1 << CP0St_FR))
+ env->active_fpu.fpr[n - 38].d = tmp;
+ else
+ env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp;
+ }
+ switch (n) {
+ case 70:
+ 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;
+ }
+ return sizeof(target_ulong);
+ }
+ switch (n) {
+ case 32: env->CP0_Status = tmp; break;
+ case 33: env->active_tc.LO[0] = tmp; break;
+ case 34: env->active_tc.HI[0] = tmp; break;
+ case 35: env->CP0_BadVAddr = tmp; break;
+ case 36: env->CP0_Cause = tmp; break;
+ case 37:
+ env->active_tc.PC = tmp & ~(target_ulong)1;
+ if (tmp & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ } else {
+ env->hflags &= ~(MIPS_HFLAG_M16);
+ }
+ break;
+ case 72: /* fp, ignored */ break;
+ default:
+ if (n > 89)
+ return 0;
+ /* Other registers are readonly. Ignore writes. */
+ break;
+ }
+
+ return sizeof(target_ulong);
+}
+#elif defined (TARGET_SH4)
+
+/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
+/* FIXME: We should use XML for this. */
+
+#define NUM_CORE_REGS 59
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ GET_REGL(env->gregs[n + 16]);
+ } else {
+ GET_REGL(env->gregs[n]);
+ }
+ } else if (n < 16) {
+ GET_REGL(env->gregs[n]);
+ } else if (n >= 25 && n < 41) {
+ GET_REGL(env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
+ } else if (n >= 43 && n < 51) {
+ GET_REGL(env->gregs[n - 43]);
+ } else if (n >= 51 && n < 59) {
+ GET_REGL(env->gregs[n - (51 - 16)]);
+ }
+ switch (n) {
+ case 16: GET_REGL(env->pc);
+ case 17: GET_REGL(env->pr);
+ case 18: GET_REGL(env->gbr);
+ case 19: GET_REGL(env->vbr);
+ case 20: GET_REGL(env->mach);
+ case 21: GET_REGL(env->macl);
+ case 22: GET_REGL(env->sr);
+ case 23: GET_REGL(env->fpul);
+ case 24: GET_REGL(env->fpscr);
+ case 41: GET_REGL(env->ssr);
+ case 42: GET_REGL(env->spc);
+ }
+
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ tmp = ldl_p(mem_buf);
+
+ if (n < 8) {
+ if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+ env->gregs[n + 16] = tmp;
+ } else {
+ env->gregs[n] = tmp;
+ }
+ return 4;
+ } else if (n < 16) {
+ env->gregs[n] = tmp;
+ return 4;
+ } else if (n >= 25 && n < 41) {
+ env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)] = tmp;
+ return 4;
+ } else if (n >= 43 && n < 51) {
+ env->gregs[n - 43] = tmp;
+ return 4;
+ } else if (n >= 51 && n < 59) {
+ env->gregs[n - (51 - 16)] = tmp;
+ return 4;
+ }
+ switch (n) {
+ case 16: env->pc = tmp; break;
+ case 17: env->pr = tmp; break;
+ case 18: env->gbr = tmp; break;
+ case 19: env->vbr = tmp; break;
+ case 20: env->mach = tmp; break;
+ case 21: env->macl = tmp; break;
+ case 22: env->sr = tmp; break;
+ case 23: env->fpul = tmp; break;
+ case 24: env->fpscr = tmp; break;
+ case 41: env->ssr = tmp; break;
+ case 42: env->spc = tmp; break;
+ default: return 0;
+ }
+
+ return 4;
+}
+#elif defined (TARGET_MICROBLAZE)
+
+#define NUM_CORE_REGS (32 + 5)
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ GET_REG32(env->regs[n]);
+ } else {
+ GET_REG32(env->sregs[n - 32]);
+ }
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ if (n > NUM_CORE_REGS)
+ return 0;
+
+ tmp = ldl_p(mem_buf);
+
+ if (n < 32) {
+ env->regs[n] = tmp;
+ } else {
+ env->sregs[n - 32] = tmp;
+ }
+ return 4;
+}
+#elif defined (TARGET_CRIS)
+
+#define NUM_CORE_REGS 49
+
+static int
+read_register_crisv10(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 15) {
+ GET_REG32(env->regs[n]);
+ }
+
+ if (n == 15) {
+ GET_REG32(env->pc);
+ }
+
+ if (n < 32) {
+ switch (n) {
+ case 16:
+ GET_REG8(env->pregs[n - 16]);
+ break;
+ case 17:
+ GET_REG8(env->pregs[n - 16]);
+ break;
+ case 20:
+ case 21:
+ GET_REG16(env->pregs[n - 16]);
+ break;
+ default:
+ if (n >= 23) {
+ GET_REG32(env->pregs[n - 16]);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint8_t srs;
+
+ if (env->pregs[PR_VR] < 32)
+ return read_register_crisv10(env, mem_buf, n);
+
+ srs = env->pregs[PR_SRS];
+ if (n < 16) {
+ GET_REG32(env->regs[n]);
+ }
+
+ if (n >= 21 && n < 32) {
+ GET_REG32(env->pregs[n - 16]);
+ }
+ if (n >= 33 && n < 49) {
+ GET_REG32(env->sregs[srs][n - 33]);
+ }
+ switch (n) {
+ case 16: GET_REG8(env->pregs[0]);
+ case 17: GET_REG8(env->pregs[1]);
+ case 18: GET_REG32(env->pregs[2]);
+ case 19: GET_REG8(srs);
+ case 20: GET_REG16(env->pregs[4]);
+ case 32: GET_REG32(env->pc);
+ }
+
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ if (n > 49)
+ return 0;
+
+ tmp = ldl_p(mem_buf);
+
+ if (n < 16) {
+ env->regs[n] = tmp;
+ }
+
+ if (n >= 21 && n < 32) {
+ env->pregs[n - 16] = tmp;
+ }
+
+ /* FIXME: Should support function regs be writable? */
+ switch (n) {
+ case 16: return 1;
+ case 17: return 1;
+ case 18: env->pregs[PR_PID] = tmp; break;
+ case 19: return 1;
+ case 20: return 2;
+ case 32: env->pc = tmp; break;
+ }
+
+ return 4;
+}
+#elif defined (TARGET_ALPHA)
+
+#define NUM_CORE_REGS 67
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ uint64_t val;
+ CPU_DoubleU d;
+
+ switch (n) {
+ case 0 ... 30:
+ val = env->ir[n];
+ break;
+ case 32 ... 62:
+ d.d = env->fir[n - 32];
+ val = d.ll;
+ break;
+ case 63:
+ val = cpu_alpha_load_fpcr(env);
+ break;
+ case 64:
+ val = env->pc;
+ break;
+ case 66:
+ val = env->unique;
+ break;
+ case 31:
+ case 65:
+ /* 31 really is the zero register; 65 is unassigned in the
+ gdb protocol, but is still required to occupy 8 bytes. */
+ val = 0;
+ break;
+ default:
+ return 0;
+ }
+ GET_REGL(val);
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ target_ulong tmp = ldtul_p(mem_buf);
+ CPU_DoubleU d;
+
+ switch (n) {
+ case 0 ... 30:
+ env->ir[n] = tmp;
+ break;
+ case 32 ... 62:
+ d.ll = tmp;
+ env->fir[n - 32] = d.d;
+ break;
+ case 63:
+ cpu_alpha_store_fpcr(env, tmp);
+ break;
+ case 64:
+ env->pc = tmp;
+ break;
+ case 66:
+ env->unique = tmp;
+ break;
+ case 31:
+ case 65:
+ /* 31 really is the zero register; 65 is unassigned in the
+ gdb protocol, but is still required to occupy 8 bytes. */
+ break;
+ default:
+ return 0;
+ }
+ return 8;
+}
+#elif defined (TARGET_S390X)
+
+#define NUM_CORE_REGS S390_NUM_TOTAL_REGS
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ switch (n) {
+ case S390_PSWM_REGNUM: GET_REGL(env->psw.mask); break;
+ case S390_PSWA_REGNUM: GET_REGL(env->psw.addr); break;
+ case S390_R0_REGNUM ... S390_R15_REGNUM:
+ GET_REGL(env->regs[n-S390_R0_REGNUM]); break;
+ case S390_A0_REGNUM ... S390_A15_REGNUM:
+ GET_REG32(env->aregs[n-S390_A0_REGNUM]); break;
+ case S390_FPC_REGNUM: GET_REG32(env->fpc); break;
+ case S390_F0_REGNUM ... S390_F15_REGNUM:
+ /* XXX */
+ break;
+ case S390_PC_REGNUM: GET_REGL(env->psw.addr); break;
+ case S390_CC_REGNUM: GET_REG32(env->cc); break;
+ }
+
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ target_ulong tmpl;
+ uint32_t tmp32;
+ int r = 8;
+ tmpl = ldtul_p(mem_buf);
+ tmp32 = ldl_p(mem_buf);
+
+ switch (n) {
+ case S390_PSWM_REGNUM: env->psw.mask = tmpl; break;
+ case S390_PSWA_REGNUM: env->psw.addr = tmpl; break;
+ case S390_R0_REGNUM ... S390_R15_REGNUM:
+ env->regs[n-S390_R0_REGNUM] = tmpl; break;
+ case S390_A0_REGNUM ... S390_A15_REGNUM:
+ env->aregs[n-S390_A0_REGNUM] = tmp32; r=4; break;
+ case S390_FPC_REGNUM: env->fpc = tmp32; r=4; break;
+ case S390_F0_REGNUM ... S390_F15_REGNUM:
+ /* XXX */
+ break;
+ case S390_PC_REGNUM: env->psw.addr = tmpl; break;
+ case S390_CC_REGNUM: env->cc = tmp32; r=4; break;
+ }
+
+ return r;
+}
+#else
+
+#define NUM_CORE_REGS 0
+
+static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
+{
+ return 0;
+}
+
+#endif
+
+static int num_g_regs = NUM_CORE_REGS;
+
+#ifdef GDB_CORE_XML
+/* Encode data using the encoding for 'x' packets. */
+static int memtox(char *buf, const char *mem, int len)
+{
+ char *p = buf;
+ char c;
+
+ while (len--) {
+ c = *(mem++);
+ switch (c) {
+ case '#': case '$': case '*': case '}':
+ *(p++) = '}';
+ *(p++) = c ^ 0x20;
+ break;
+ default:
+ *(p++) = c;
+ break;
+ }
+ }
+ return p - buf;
+}
+
+static const char *get_feature_xml(const char *p, const char **newp)
+{
+ size_t len;
+ int i;
+ const char *name;
+ static char target_xml[1024];
+
+ len = 0;
+ while (p[len] && p[len] != ':')
+ len++;
+ *newp = p + len;
+
+ name = NULL;
+ if (strncmp(p, "target.xml", len) == 0) {
+ /* Generate the XML description for this CPU. */
+ if (!target_xml[0]) {
+ GDBRegisterState *r;
+
+ snprintf(target_xml, sizeof(target_xml),
+ "<?xml version=\"1.0\"?>"
+ "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
+ "<target>"
+ "<xi:include href=\"%s\"/>",
+ GDB_CORE_XML);
+
+ for (r = first_cpu->gdb_regs; r; r = r->next) {
+ pstrcat(target_xml, sizeof(target_xml), "<xi:include href=\"");
+ pstrcat(target_xml, sizeof(target_xml), r->xml);
+ pstrcat(target_xml, sizeof(target_xml), "\"/>");
+ }
+ pstrcat(target_xml, sizeof(target_xml), "</target>");
+ }
+ return target_xml;
+ }
+ for (i = 0; ; i++) {
+ name = xml_builtin[i][0];
+ if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len))
+ break;
+ }
+ return name ? xml_builtin[i][1] : NULL;
+}
+#endif
+
+static int gdb_read_register(CPUState *env, uint8_t *mem_buf, int reg)
+{
+ GDBRegisterState *r;
+
+ if (reg < NUM_CORE_REGS)
+ return cpu_gdb_read_register(env, mem_buf, reg);
+
+ for (r = env->gdb_regs; r; r = r->next) {
+ if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+ return r->get_reg(env, mem_buf, reg - r->base_reg);
+ }
+ }
+ return 0;
+}
+
+static int gdb_write_register(CPUState *env, uint8_t *mem_buf, int reg)
+{
+ GDBRegisterState *r;
+
+ if (reg < NUM_CORE_REGS)
+ return cpu_gdb_write_register(env, mem_buf, reg);
+
+ for (r = env->gdb_regs; r; r = r->next) {
+ if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+ return r->set_reg(env, mem_buf, reg - r->base_reg);
+ }
+ }
+ return 0;
+}
+
+/* Register a supplemental set of CPU registers. If g_pos is nonzero it
+ specifies the first register number and these registers are included in
+ a standard "g" packet. Direction is relative to gdb, i.e. get_reg is
+ gdb reading a CPU register, and set_reg is gdb modifying a CPU register.
+ */
+
+void gdb_register_coprocessor(CPUState * env,
+ gdb_reg_cb get_reg, gdb_reg_cb set_reg,
+ int num_regs, const char *xml, int g_pos)
+{
+ GDBRegisterState *s;
+ GDBRegisterState **p;
+ static int last_reg = NUM_CORE_REGS;
+
+ s = (GDBRegisterState *)qemu_mallocz(sizeof(GDBRegisterState));
+ s->base_reg = last_reg;
+ s->num_regs = num_regs;
+ s->get_reg = get_reg;
+ s->set_reg = set_reg;
+ s->xml = xml;
+ p = &env->gdb_regs;
+ while (*p) {
+ /* Check for duplicates. */
+ if (strcmp((*p)->xml, xml) == 0)
+ return;
+ p = &(*p)->next;
+ }
+ /* Add to end of list. */
+ last_reg += num_regs;
+ *p = s;
+ if (g_pos) {
+ if (g_pos != s->base_reg) {
+ fprintf(stderr, "Error: Bad gdb register numbering for '%s'\n"
+ "Expected %d got %d\n", xml, g_pos, s->base_reg);
+ } else {
+ num_g_regs = last_reg;
+ }
+ }
+}
+
+#ifndef CONFIG_USER_ONLY
+static const int xlat_gdb_type[] = {
+ [GDB_WATCHPOINT_WRITE] = BP_GDB | BP_MEM_WRITE,
+ [GDB_WATCHPOINT_READ] = BP_GDB | BP_MEM_READ,
+ [GDB_WATCHPOINT_ACCESS] = BP_GDB | BP_MEM_ACCESS,
+};
+#endif
+
+static int gdb_breakpoint_insert(target_ulong addr, target_ulong len, int type)
+{
+ CPUState *env;
+ int err = 0;
+
+ if (kvm_enabled())
+ return kvm_insert_breakpoint(gdbserver_state->c_cpu, addr, len, type);
+
+ switch (type) {
+ case GDB_BREAKPOINT_SW:
+ case GDB_BREAKPOINT_HW:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_breakpoint_insert(env, addr, BP_GDB, NULL);
+ if (err)
+ break;
+ }
+ return err;
+#ifndef CONFIG_USER_ONLY
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_READ:
+ case GDB_WATCHPOINT_ACCESS:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_watchpoint_insert(env, addr, len, xlat_gdb_type[type],
+ NULL);
+ if (err)
+ break;
+ }
+ return err;
+#endif
+ default:
+ return -ENOSYS;
+ }
+}
+
+static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type)
+{
+ CPUState *env;
+ int err = 0;
+
+ if (kvm_enabled())
+ return kvm_remove_breakpoint(gdbserver_state->c_cpu, addr, len, type);
+
+ switch (type) {
+ case GDB_BREAKPOINT_SW:
+ case GDB_BREAKPOINT_HW:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_breakpoint_remove(env, addr, BP_GDB);
+ if (err)
+ break;
+ }
+ return err;
+#ifndef CONFIG_USER_ONLY
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_READ:
+ case GDB_WATCHPOINT_ACCESS:
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = cpu_watchpoint_remove(env, addr, len, xlat_gdb_type[type]);
+ if (err)
+ break;
+ }
+ return err;
+#endif
+ default:
+ return -ENOSYS;
+ }
+}
+
+static void gdb_breakpoint_remove_all(void)
+{
+ CPUState *env;
+
+ if (kvm_enabled()) {
+ kvm_remove_all_breakpoints(gdbserver_state->c_cpu);
+ return;
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ cpu_breakpoint_remove_all(env, BP_GDB);
+#ifndef CONFIG_USER_ONLY
+ cpu_watchpoint_remove_all(env, BP_GDB);
+#endif
+ }
+}
+
+static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
+{
+#if defined(TARGET_I386)
+ cpu_synchronize_state(s->c_cpu);
+ s->c_cpu->eip = pc;
+#elif defined (TARGET_PPC)
+ s->c_cpu->nip = pc;
+#elif defined (TARGET_SPARC)
+ s->c_cpu->pc = pc;
+ s->c_cpu->npc = pc + 4;
+#elif defined (TARGET_ARM)
+ s->c_cpu->regs[15] = pc;
+#elif defined (TARGET_SH4)
+ s->c_cpu->pc = pc;
+#elif defined (TARGET_MIPS)
+ s->c_cpu->active_tc.PC = pc & ~(target_ulong)1;
+ if (pc & 1) {
+ s->c_cpu->hflags |= MIPS_HFLAG_M16;
+ } else {
+ s->c_cpu->hflags &= ~(MIPS_HFLAG_M16);
+ }
+#elif defined (TARGET_MICROBLAZE)
+ s->c_cpu->sregs[SR_PC] = pc;
+#elif defined (TARGET_CRIS)
+ s->c_cpu->pc = pc;
+#elif defined (TARGET_ALPHA)
+ s->c_cpu->pc = pc;
+#elif defined (TARGET_S390X)
+ cpu_synchronize_state(s->c_cpu);
+ s->c_cpu->psw.addr = pc;
+#endif
+}
+
+static inline int gdb_id(CPUState *env)
+{
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_USE_NPTL)
+ return env->host_tid;
+#else
+ return env->cpu_index + 1;
+#endif
+}
+
+static CPUState *find_cpu(uint32_t thread_id)
+{
+ CPUState *env;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (gdb_id(env) == thread_id) {
+ return env;
+ }
+ }
+
+ return NULL;
+}
+
+static int gdb_handle_packet(GDBState *s, const char *line_buf)
+{
+ CPUState *env;
+ const char *p;
+ uint32_t thread;
+ int ch, reg_size, type, res;
+ char buf[MAX_PACKET_LENGTH];
+ uint8_t mem_buf[MAX_PACKET_LENGTH];
+ uint8_t *registers;
+ target_ulong addr, len;
+
+#ifdef DEBUG_GDB
+ printf("command='%s'\n", line_buf);
+#endif
+ p = line_buf;
+ ch = *p++;
+ switch(ch) {
+ case '?':
+ /* TODO: Make this return the correct value for user-mode. */
+ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP,
+ gdb_id(s->c_cpu));
+ put_packet(s, buf);
+ /* Remove all the breakpoints when this query is issued,
+ * because gdb is doing and initial connect and the state
+ * should be cleaned up.
+ */
+ gdb_breakpoint_remove_all();
+ break;
+ case 'c':
+ if (*p != '\0') {
+ addr = strtoull(p, (char **)&p, 16);
+ gdb_set_cpu_pc(s, addr);
+ }
+ s->signal = 0;
+ gdb_continue(s);
+ return RS_IDLE;
+ case 'C':
+ s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16));
+ if (s->signal == -1)
+ s->signal = 0;
+ gdb_continue(s);
+ return RS_IDLE;
+ case 'v':
+ if (strncmp(p, "Cont", 4) == 0) {
+ int res_signal, res_thread;
+
+ p += 4;
+ if (*p == '?') {
+ put_packet(s, "vCont;c;C;s;S");
+ break;
+ }
+ res = 0;
+ res_signal = 0;
+ res_thread = 0;
+ while (*p) {
+ int action, signal;
+
+ if (*p++ != ';') {
+ res = 0;
+ break;
+ }
+ action = *p++;
+ signal = 0;
+ if (action == 'C' || action == 'S') {
+ signal = strtoul(p, (char **)&p, 16);
+ } else if (action != 'c' && action != 's') {
+ res = 0;
+ break;
+ }
+ thread = 0;
+ if (*p == ':') {
+ thread = strtoull(p+1, (char **)&p, 16);
+ }
+ action = tolower(action);
+ if (res == 0 || (res == 'c' && action == 's')) {
+ res = action;
+ res_signal = signal;
+ res_thread = thread;
+ }
+ }
+ if (res) {
+ if (res_thread != -1 && res_thread != 0) {
+ env = find_cpu(res_thread);
+ if (env == NULL) {
+ put_packet(s, "E22");
+ break;
+ }
+ s->c_cpu = env;
+ }
+ if (res == 's') {
+ cpu_single_step(s->c_cpu, sstep_flags);
+ }
+ s->signal = res_signal;
+ gdb_continue(s);
+ return RS_IDLE;
+ }
+ break;
+ } else {
+ goto unknown_command;
+ }
+ case 'k':
+ /* Kill the target */
+ fprintf(stderr, "\nQEMU: Terminated via GDBstub\n");
+ exit(0);
+ case 'D':
+ /* Detach packet */
+ gdb_breakpoint_remove_all();
+ gdb_syscall_mode = GDB_SYS_DISABLED;
+ gdb_continue(s);
+ put_packet(s, "OK");
+ break;
+ case 's':
+ if (*p != '\0') {
+ addr = strtoull(p, (char **)&p, 16);
+ gdb_set_cpu_pc(s, addr);
+ }
+ cpu_single_step(s->c_cpu, sstep_flags);
+ gdb_continue(s);
+ return RS_IDLE;
+ case 'F':
+ {
+ target_ulong ret;
+ target_ulong err;
+
+ ret = strtoull(p, (char **)&p, 16);
+ if (*p == ',') {
+ p++;
+ err = strtoull(p, (char **)&p, 16);
+ } else {
+ err = 0;
+ }
+ if (*p == ',')
+ p++;
+ type = *p;
+ if (gdb_current_syscall_cb)
+ gdb_current_syscall_cb(s->c_cpu, ret, err);
+ if (type == 'C') {
+ put_packet(s, "T02");
+ } else {
+ gdb_continue(s);
+ }
+ }
+ break;
+ case 'g':
+ cpu_synchronize_state(s->g_cpu);
+ len = 0;
+ for (addr = 0; addr < num_g_regs; addr++) {
+ reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr);
+ len += reg_size;
+ }
+ memtohex(buf, mem_buf, len);
+ put_packet(s, buf);
+ break;
+ case 'G':
+ cpu_synchronize_state(s->g_cpu);
+ registers = mem_buf;
+ len = strlen(p) / 2;
+ hextomem((uint8_t *)registers, p, len);
+ for (addr = 0; addr < num_g_regs && len > 0; addr++) {
+ reg_size = gdb_write_register(s->g_cpu, registers, addr);
+ len -= reg_size;
+ registers += reg_size;
+ }
+ put_packet(s, "OK");
+ break;
+ case 'm':
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, NULL, 16);
+ if (cpu_memory_rw_debug(s->g_cpu, addr, mem_buf, len, 0) != 0) {
+ put_packet (s, "E14");
+ } else {
+ memtohex(buf, mem_buf, len);
+ put_packet(s, buf);
+ }
+ break;
+ case 'M':
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, (char **)&p, 16);
+ if (*p == ':')
+ p++;
+ hextomem(mem_buf, p, len);
+ if (cpu_memory_rw_debug(s->g_cpu, addr, mem_buf, len, 1) != 0)
+ put_packet(s, "E14");
+ else
+ put_packet(s, "OK");
+ break;
+ case 'p':
+ /* Older gdb are really dumb, and don't use 'g' if 'p' is avaialable.
+ This works, but can be very slow. Anything new enough to
+ understand XML also knows how to use this properly. */
+ if (!gdb_has_xml)
+ goto unknown_command;
+ addr = strtoull(p, (char **)&p, 16);
+ reg_size = gdb_read_register(s->g_cpu, mem_buf, addr);
+ if (reg_size) {
+ memtohex(buf, mem_buf, reg_size);
+ put_packet(s, buf);
+ } else {
+ put_packet(s, "E14");
+ }
+ break;
+ case 'P':
+ if (!gdb_has_xml)
+ goto unknown_command;
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == '=')
+ p++;
+ reg_size = strlen(p) / 2;
+ hextomem(mem_buf, p, reg_size);
+ gdb_write_register(s->g_cpu, mem_buf, addr);
+ put_packet(s, "OK");
+ break;
+ case 'Z':
+ case 'z':
+ type = strtoul(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ addr = strtoull(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoull(p, (char **)&p, 16);
+ if (ch == 'Z')
+ res = gdb_breakpoint_insert(addr, len, type);
+ else
+ res = gdb_breakpoint_remove(addr, len, type);
+ if (res >= 0)
+ put_packet(s, "OK");
+ else if (res == -ENOSYS)
+ put_packet(s, "");
+ else
+ put_packet(s, "E22");
+ break;
+ case 'H':
+ type = *p++;
+ thread = strtoull(p, (char **)&p, 16);
+ if (thread == -1 || thread == 0) {
+ put_packet(s, "OK");
+ break;
+ }
+ env = find_cpu(thread);
+ if (env == NULL) {
+ put_packet(s, "E22");
+ break;
+ }
+ switch (type) {
+ case 'c':
+ s->c_cpu = env;
+ put_packet(s, "OK");
+ break;
+ case 'g':
+ s->g_cpu = env;
+ put_packet(s, "OK");
+ break;
+ default:
+ put_packet(s, "E22");
+ break;
+ }
+ break;
+ case 'T':
+ thread = strtoull(p, (char **)&p, 16);
+ env = find_cpu(thread);
+
+ if (env != NULL) {
+ put_packet(s, "OK");
+ } else {
+ put_packet(s, "E22");
+ }
+ break;
+ case 'q':
+ case 'Q':
+ /* parse any 'q' packets here */
+ if (!strcmp(p,"qemu.sstepbits")) {
+ /* Query Breakpoint bit definitions */
+ snprintf(buf, sizeof(buf), "ENABLE=%x,NOIRQ=%x,NOTIMER=%x",
+ SSTEP_ENABLE,
+ SSTEP_NOIRQ,
+ SSTEP_NOTIMER);
+ put_packet(s, buf);
+ break;
+ } else if (strncmp(p,"qemu.sstep",10) == 0) {
+ /* Display or change the sstep_flags */
+ p += 10;
+ if (*p != '=') {
+ /* Display current setting */
+ snprintf(buf, sizeof(buf), "0x%x", sstep_flags);
+ put_packet(s, buf);
+ break;
+ }
+ p++;
+ type = strtoul(p, (char **)&p, 16);
+ sstep_flags = type;
+ put_packet(s, "OK");
+ break;
+ } else if (strcmp(p,"C") == 0) {
+ /* "Current thread" remains vague in the spec, so always return
+ * the first CPU (gdb returns the first thread). */
+ put_packet(s, "QC1");
+ break;
+ } else if (strcmp(p,"fThreadInfo") == 0) {
+ s->query_cpu = first_cpu;
+ goto report_cpuinfo;
+ } else if (strcmp(p,"sThreadInfo") == 0) {
+ report_cpuinfo:
+ if (s->query_cpu) {
+ snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu));
+ put_packet(s, buf);
+ s->query_cpu = s->query_cpu->next_cpu;
+ } else
+ put_packet(s, "l");
+ break;
+ } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) {
+ thread = strtoull(p+16, (char **)&p, 16);
+ env = find_cpu(thread);
+ if (env != NULL) {
+ cpu_synchronize_state(env);
+ len = snprintf((char *)mem_buf, sizeof(mem_buf),
+ "CPU#%d [%s]", env->cpu_index,
+ env->halted ? "halted " : "running");
+ memtohex(buf, mem_buf, len);
+ put_packet(s, buf);
+ }
+ break;
+ }
+#ifdef CONFIG_USER_ONLY
+ else if (strncmp(p, "Offsets", 7) == 0) {
+ TaskState *ts = s->c_cpu->opaque;
+
+ snprintf(buf, sizeof(buf),
+ "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx
+ ";Bss=" TARGET_ABI_FMT_lx,
+ ts->info->code_offset,
+ ts->info->data_offset,
+ ts->info->data_offset);
+ put_packet(s, buf);
+ break;
+ }
+#else /* !CONFIG_USER_ONLY */
+ else if (strncmp(p, "Rcmd,", 5) == 0) {
+ int len = strlen(p + 5);
+
+ if ((len % 2) != 0) {
+ put_packet(s, "E01");
+ break;
+ }
+ hextomem(mem_buf, p + 5, len);
+ len = len / 2;
+ mem_buf[len++] = 0;
+ qemu_chr_read(s->mon_chr, mem_buf, len);
+ put_packet(s, "OK");
+ break;
+ }
+#endif /* !CONFIG_USER_ONLY */
+ if (strncmp(p, "Supported", 9) == 0) {
+ snprintf(buf, sizeof(buf), "PacketSize=%x", MAX_PACKET_LENGTH);
+#ifdef GDB_CORE_XML
+ pstrcat(buf, sizeof(buf), ";qXfer:features:read+");
+#endif
+ put_packet(s, buf);
+ break;
+ }
+#ifdef GDB_CORE_XML
+ if (strncmp(p, "Xfer:features:read:", 19) == 0) {
+ const char *xml;
+ target_ulong total_len;
+
+ gdb_has_xml = 1;
+ p += 19;
+ xml = get_feature_xml(p, &p);
+ if (!xml) {
+ snprintf(buf, sizeof(buf), "E00");
+ put_packet(s, buf);
+ break;
+ }
+
+ if (*p == ':')
+ p++;
+ addr = strtoul(p, (char **)&p, 16);
+ if (*p == ',')
+ p++;
+ len = strtoul(p, (char **)&p, 16);
+
+ total_len = strlen(xml);
+ if (addr > total_len) {
+ snprintf(buf, sizeof(buf), "E00");
+ put_packet(s, buf);
+ break;
+ }
+ if (len > (MAX_PACKET_LENGTH - 5) / 2)
+ len = (MAX_PACKET_LENGTH - 5) / 2;
+ if (len < total_len - addr) {
+ buf[0] = 'm';
+ len = memtox(buf + 1, xml + addr, len);
+ } else {
+ buf[0] = 'l';
+ len = memtox(buf + 1, xml + addr, total_len - addr);
+ }
+ put_packet_binary(s, buf, len + 1);
+ break;
+ }
+#endif
+ /* Unrecognised 'q' command. */
+ goto unknown_command;
+
+ default:
+ unknown_command:
+ /* put empty packet */
+ buf[0] = '\0';
+ put_packet(s, buf);
+ break;
+ }
+ return RS_IDLE;
+}
+
+void gdb_set_stop_cpu(CPUState *env)
+{
+ gdbserver_state->c_cpu = env;
+ gdbserver_state->g_cpu = env;
+}
+
+#ifndef CONFIG_USER_ONLY
+static void gdb_vm_state_change(void *opaque, int running, int reason)
+{
+ GDBState *s = gdbserver_state;
+ CPUState *env = s->c_cpu;
+ char buf[256];
+ const char *type;
+ int ret;
+
+ if (running || (reason != EXCP_DEBUG && reason != EXCP_INTERRUPT) ||
+ s->state == RS_INACTIVE || s->state == RS_SYSCALL)
+ return;
+
+ /* disable single step if it was enable */
+ cpu_single_step(env, 0);
+
+ if (reason == EXCP_DEBUG) {
+ if (env->watchpoint_hit) {
+ switch (env->watchpoint_hit->flags & BP_MEM_ACCESS) {
+ case BP_MEM_READ:
+ type = "r";
+ break;
+ case BP_MEM_ACCESS:
+ type = "a";
+ break;
+ default:
+ type = "";
+ break;
+ }
+ snprintf(buf, sizeof(buf),
+ "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
+ GDB_SIGNAL_TRAP, gdb_id(env), type,
+ env->watchpoint_hit->vaddr);
+ put_packet(s, buf);
+ env->watchpoint_hit = NULL;
+ return;
+ }
+ tb_flush(env);
+ ret = GDB_SIGNAL_TRAP;
+ } else {
+ ret = GDB_SIGNAL_INT;
+ }
+ snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env));
+ put_packet(s, buf);
+}
+#endif
+
+/* Send a gdb syscall request.
+ This accepts limited printf-style format specifiers, specifically:
+ %x - target_ulong argument printed in hex.
+ %lx - 64-bit argument printed in hex.
+ %s - string pointer (target_ulong) and length (int) pair. */
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+{
+ va_list va;
+ char buf[256];
+ char *p;
+ target_ulong addr;
+ uint64_t i64;
+ GDBState *s;
+
+ s = gdbserver_state;
+ if (!s)
+ return;
+ gdb_current_syscall_cb = cb;
+ s->state = RS_SYSCALL;
+#ifndef CONFIG_USER_ONLY
+ vm_stop(EXCP_DEBUG);
+#endif
+ s->state = RS_IDLE;
+ va_start(va, fmt);
+ p = buf;
+ *(p++) = 'F';
+ while (*fmt) {
+ if (*fmt == '%') {
+ fmt++;
+ switch (*fmt++) {
+ case 'x':
+ addr = va_arg(va, target_ulong);
+ p += snprintf(p, &buf[sizeof(buf)] - p, TARGET_FMT_lx, addr);
+ break;
+ case 'l':
+ if (*(fmt++) != 'x')
+ goto bad_format;
+ i64 = va_arg(va, uint64_t);
+ p += snprintf(p, &buf[sizeof(buf)] - p, "%" PRIx64, i64);
+ break;
+ case 's':
+ addr = va_arg(va, target_ulong);
+ p += snprintf(p, &buf[sizeof(buf)] - p, TARGET_FMT_lx "/%x",
+ addr, va_arg(va, int));
+ break;
+ default:
+ bad_format:
+ fprintf(stderr, "gdbstub: Bad syscall format string '%s'\n",
+ fmt - 1);
+ break;
+ }
+ } else {
+ *(p++) = *(fmt++);
+ }
+ }
+ *p = 0;
+ va_end(va);
+ put_packet(s, buf);
+#ifdef CONFIG_USER_ONLY
+ gdb_handlesig(s->c_cpu, 0);
+#else
+ cpu_exit(s->c_cpu);
+#endif
+}
+
+static void gdb_read_byte(GDBState *s, int ch)
+{
+ int i, csum;
+ uint8_t reply;
+
+#ifndef CONFIG_USER_ONLY
+ if (s->last_packet_len) {
+ /* Waiting for a response to the last packet. If we see the start
+ of a new command then abandon the previous response. */
+ if (ch == '-') {
+#ifdef DEBUG_GDB
+ printf("Got NACK, retransmitting\n");
+#endif
+ put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len);
+ }
+#ifdef DEBUG_GDB
+ else if (ch == '+')
+ printf("Got ACK\n");
+ else
+ printf("Got '%c' when expecting ACK/NACK\n", ch);
+#endif
+ if (ch == '+' || ch == '$')
+ s->last_packet_len = 0;
+ if (ch != '$')
+ return;
+ }
+ if (vm_running) {
+ /* when the CPU is running, we cannot do anything except stop
+ it when receiving a char */
+ vm_stop(EXCP_INTERRUPT);
+ } else
+#endif
+ {
+ switch(s->state) {
+ case RS_IDLE:
+ if (ch == '$') {
+ s->line_buf_index = 0;
+ s->state = RS_GETLINE;
+ }
+ break;
+ case RS_GETLINE:
+ if (ch == '#') {
+ s->state = RS_CHKSUM1;
+ } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
+ s->state = RS_IDLE;
+ } else {
+ s->line_buf[s->line_buf_index++] = ch;
+ }
+ break;
+ case RS_CHKSUM1:
+ s->line_buf[s->line_buf_index] = '\0';
+ s->line_csum = fromhex(ch) << 4;
+ s->state = RS_CHKSUM2;
+ break;
+ case RS_CHKSUM2:
+ s->line_csum |= fromhex(ch);
+ csum = 0;
+ for(i = 0; i < s->line_buf_index; i++) {
+ csum += s->line_buf[i];
+ }
+ if (s->line_csum != (csum & 0xff)) {
+ reply = '-';
+ put_buffer(s, &reply, 1);
+ s->state = RS_IDLE;
+ } else {
+ reply = '+';
+ put_buffer(s, &reply, 1);
+ s->state = gdb_handle_packet(s, s->line_buf);
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
+/* Tell the remote gdb that the process has exited. */
+void gdb_exit(CPUState *env, int code)
+{
+ GDBState *s;
+ char buf[4];
+
+ s = gdbserver_state;
+ if (!s) {
+ return;
+ }
+#ifdef CONFIG_USER_ONLY
+ if (gdbserver_fd < 0 || s->fd < 0) {
+ return;
+ }
+#endif
+
+ snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
+ put_packet(s, buf);
+
+#ifndef CONFIG_USER_ONLY
+ if (s->chr) {
+ qemu_chr_close(s->chr);
+ }
+#endif
+}
+
+#ifdef CONFIG_USER_ONLY
+int
+gdb_queuesig (void)
+{
+ GDBState *s;
+
+ s = gdbserver_state;
+
+ if (gdbserver_fd < 0 || s->fd < 0)
+ return 0;
+ else
+ return 1;
+}
+
+int
+gdb_handlesig (CPUState *env, int sig)
+{
+ GDBState *s;
+ char buf[256];
+ int n;
+
+ s = gdbserver_state;
+ if (gdbserver_fd < 0 || s->fd < 0)
+ return sig;
+
+ /* disable single step if it was enabled */
+ cpu_single_step(env, 0);
+ tb_flush(env);
+
+ if (sig != 0)
+ {
+ snprintf(buf, sizeof(buf), "S%02x", target_signal_to_gdb (sig));
+ put_packet(s, buf);
+ }
+ /* put_packet() might have detected that the peer terminated the
+ connection. */
+ if (s->fd < 0)
+ return sig;
+
+ sig = 0;
+ s->state = RS_IDLE;
+ s->running_state = 0;
+ while (s->running_state == 0) {
+ n = read (s->fd, buf, 256);
+ if (n > 0)
+ {
+ int i;
+
+ for (i = 0; i < n; i++)
+ gdb_read_byte (s, buf[i]);
+ }
+ else if (n == 0 || errno != EAGAIN)
+ {
+ /* XXX: Connection closed. Should probably wait for annother
+ connection before continuing. */
+ return sig;
+ }
+ }
+ sig = s->signal;
+ s->signal = 0;
+ return sig;
+}
+
+/* Tell the remote gdb that the process has exited due to SIG. */
+void gdb_signalled(CPUState *env, int sig)
+{
+ GDBState *s;
+ char buf[4];
+
+ s = gdbserver_state;
+ if (gdbserver_fd < 0 || s->fd < 0)
+ return;
+
+ snprintf(buf, sizeof(buf), "X%02x", target_signal_to_gdb (sig));
+ put_packet(s, buf);
+}
+
+static void gdb_accept(void)
+{
+ GDBState *s;
+ struct sockaddr_in sockaddr;
+ socklen_t len;
+ int val, fd;
+
+ for(;;) {
+ len = sizeof(sockaddr);
+ fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept");
+ return;
+ } else if (fd >= 0) {
+#ifndef _WIN32
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+ break;
+ }
+ }
+
+ /* set short latency */
+ val = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+
+ s = qemu_mallocz(sizeof(GDBState));
+ s->c_cpu = first_cpu;
+ s->g_cpu = first_cpu;
+ s->fd = fd;
+ gdb_has_xml = 0;
+
+ gdbserver_state = s;
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+}
+
+static int gdbserver_open(int port)
+{
+ struct sockaddr_in sockaddr;
+ int fd, val, ret;
+
+ fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+#ifndef _WIN32
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(port);
+ sockaddr.sin_addr.s_addr = 0;
+ ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ if (ret < 0) {
+ perror("bind");
+ return -1;
+ }
+ ret = listen(fd, 0);
+ if (ret < 0) {
+ perror("listen");
+ return -1;
+ }
+ return fd;
+}
+
+int gdbserver_start(int port)
+{
+ gdbserver_fd = gdbserver_open(port);
+ if (gdbserver_fd < 0)
+ return -1;
+ /* accept connections */
+ gdb_accept();
+ return 0;
+}
+
+/* Disable gdb stub for child processes. */
+void gdbserver_fork(CPUState *env)
+{
+ GDBState *s = gdbserver_state;
+ if (gdbserver_fd < 0 || s->fd < 0)
+ return;
+ close(s->fd);
+ s->fd = -1;
+ cpu_breakpoint_remove_all(env, BP_GDB);
+ cpu_watchpoint_remove_all(env, BP_GDB);
+}
+#else
+static int gdb_chr_can_receive(void *opaque)
+{
+ /* We can handle an arbitrarily large amount of data.
+ Pick the maximum packet size, which is as good as anything. */
+ return MAX_PACKET_LENGTH;
+}
+
+static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ gdb_read_byte(gdbserver_state, buf[i]);
+ }
+}
+
+static void gdb_chr_event(void *opaque, int event)
+{
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ vm_stop(EXCP_INTERRUPT);
+ gdb_has_xml = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static void gdb_monitor_output(GDBState *s, const char *msg, int len)
+{
+ char buf[MAX_PACKET_LENGTH];
+
+ buf[0] = 'O';
+ if (len > (MAX_PACKET_LENGTH/2) - 1)
+ len = (MAX_PACKET_LENGTH/2) - 1;
+ memtohex(buf + 1, (uint8_t *)msg, len);
+ put_packet(s, buf);
+}
+
+static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ const char *p = (const char *)buf;
+ int max_sz;
+
+ max_sz = (sizeof(gdbserver_state->last_packet) - 2) / 2;
+ for (;;) {
+ if (len <= max_sz) {
+ gdb_monitor_output(gdbserver_state, p, len);
+ break;
+ }
+ gdb_monitor_output(gdbserver_state, p, max_sz);
+ p += max_sz;
+ len -= max_sz;
+ }
+ return len;
+}
+
+#ifndef _WIN32
+static void gdb_sigterm_handler(int signal)
+{
+ if (vm_running)
+ vm_stop(EXCP_INTERRUPT);
+}
+#endif
+
+int gdbserver_start(const char *device)
+{
+ GDBState *s;
+ char gdbstub_device_name[128];
+ CharDriverState *chr = NULL;
+ CharDriverState *mon_chr;
+
+ if (!device)
+ return -1;
+ if (strcmp(device, "none") != 0) {
+ if (strstart(device, "tcp:", NULL)) {
+ /* enforce required TCP attributes */
+ snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
+ "%s,nowait,nodelay,server", device);
+ device = gdbstub_device_name;
+ }
+#ifndef _WIN32
+ else if (strcmp(device, "stdio") == 0) {
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = gdb_sigterm_handler;
+ sigaction(SIGINT, &act, NULL);
+ }
+#endif
+ chr = qemu_chr_open("gdb", device, NULL);
+ if (!chr)
+ return -1;
+
+ qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
+ gdb_chr_event, NULL);
+ }
+
+ s = gdbserver_state;
+ if (!s) {
+ s = qemu_mallocz(sizeof(GDBState));
+ gdbserver_state = s;
+
+ qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL);
+
+ /* Initialize a monitor terminal for gdb */
+ mon_chr = qemu_mallocz(sizeof(*mon_chr));
+ mon_chr->chr_write = gdb_monitor_write;
+ monitor_init(mon_chr, 0);
+ } else {
+ if (s->chr)
+ qemu_chr_close(s->chr);
+ mon_chr = s->mon_chr;
+ memset(s, 0, sizeof(GDBState));
+ }
+ s->c_cpu = first_cpu;
+ s->g_cpu = first_cpu;
+ s->chr = chr;
+ s->state = chr ? RS_IDLE : RS_INACTIVE;
+ s->mon_chr = mon_chr;
+
+ return 0;
+}
+#endif
diff --git a/gdbstub.h b/gdbstub.h
new file mode 100644
index 0000000..d82334f
--- /dev/null
+++ b/gdbstub.h
@@ -0,0 +1,44 @@
+#ifndef GDBSTUB_H
+#define GDBSTUB_H
+
+#define DEFAULT_GDBSTUB_PORT "1234"
+
+/* GDB breakpoint/watchpoint types */
+#define GDB_BREAKPOINT_SW 0
+#define GDB_BREAKPOINT_HW 1
+#define GDB_WATCHPOINT_WRITE 2
+#define GDB_WATCHPOINT_READ 3
+#define GDB_WATCHPOINT_ACCESS 4
+
+#ifdef NEED_CPU_H
+typedef void (*gdb_syscall_complete_cb)(CPUState *env,
+ target_ulong ret, target_ulong err);
+
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);
+int use_gdb_syscalls(void);
+void gdb_set_stop_cpu(CPUState *env);
+void gdb_exit(CPUState *, int);
+#ifdef CONFIG_USER_ONLY
+int gdb_queuesig (void);
+int gdb_handlesig (CPUState *, int);
+void gdb_signalled(CPUState *, int);
+void gdbserver_fork(CPUState *);
+#endif
+/* Get or set a register. Returns the size of the register. */
+typedef int (*gdb_reg_cb)(CPUState *env, uint8_t *buf, int reg);
+void gdb_register_coprocessor(CPUState *env,
+ gdb_reg_cb get_reg, gdb_reg_cb set_reg,
+ int num_regs, const char *xml, int g_pos);
+
+#endif
+
+#ifdef CONFIG_USER_ONLY
+int gdbserver_start(int);
+#else
+int gdbserver_start(const char *port);
+#endif
+
+/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */
+extern const char *const xml_builtin[][2];
+
+#endif
diff --git a/gen-icount.h b/gen-icount.h
new file mode 100644
index 0000000..8879da6
--- /dev/null
+++ b/gen-icount.h
@@ -0,0 +1,48 @@
+#include "qemu-timer.h"
+
+/* Helpers for instruction counting code generation. */
+
+static TCGArg *icount_arg;
+static int icount_label;
+
+static inline void gen_icount_start(void)
+{
+ TCGv_i32 count;
+
+ if (!use_icount)
+ return;
+
+ icount_label = gen_new_label();
+ count = tcg_temp_local_new_i32();
+ tcg_gen_ld_i32(count, cpu_env, offsetof(CPUState, icount_decr.u32));
+ /* This is a horrid hack to allow fixing up the value later. */
+ icount_arg = gen_opparam_ptr + 1;
+ tcg_gen_subi_i32(count, count, 0xdeadbeef);
+
+ tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, icount_label);
+ tcg_gen_st16_i32(count, cpu_env, offsetof(CPUState, icount_decr.u16.low));
+ tcg_temp_free_i32(count);
+}
+
+static void gen_icount_end(TranslationBlock *tb, int num_insns)
+{
+ if (use_icount) {
+ *icount_arg = num_insns;
+ gen_set_label(icount_label);
+ tcg_gen_exit_tb((long)tb + 2);
+ }
+}
+
+static inline void gen_io_start(void)
+{
+ TCGv_i32 tmp = tcg_const_i32(1);
+ tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, can_do_io));
+ tcg_temp_free_i32(tmp);
+}
+
+static inline void gen_io_end(void)
+{
+ TCGv_i32 tmp = tcg_const_i32(0);
+ tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, can_do_io));
+ tcg_temp_free_i32(tmp);
+}
diff --git a/hmp-commands.hx b/hmp-commands.hx
new file mode 100644
index 0000000..247fdf0
--- /dev/null
+++ b/hmp-commands.hx
@@ -0,0 +1,1382 @@
+HXCOMM Use DEFHEADING() to define headings in both help text and texi
+HXCOMM Text between STEXI and ETEXI are copied to texi version and
+HXCOMM discarded from C version
+HXCOMM DEF(command, args, callback, arg_string, help) is used to construct
+HXCOMM monitor commands
+HXCOMM HXCOMM can be used for comments, discarded from both texi and C
+
+STEXI
+@table @option
+ETEXI
+
+ {
+ .name = "help|?",
+ .args_type = "name:s?",
+ .params = "[cmd]",
+ .help = "show the help",
+ .mhandler.cmd = do_help_cmd,
+ },
+
+STEXI
+@item help or ? [@var{cmd}]
+@findex help
+Show the help for all commands or just for command @var{cmd}.
+ETEXI
+
+ {
+ .name = "commit",
+ .args_type = "device:B",
+ .params = "device|all",
+ .help = "commit changes to the disk images (if -snapshot is used) or backing files",
+ .mhandler.cmd = do_commit,
+ },
+
+STEXI
+@item commit
+@findex commit
+Commit changes to the disk images (if -snapshot is used) or backing files.
+ETEXI
+
+ {
+ .name = "q|quit",
+ .args_type = "",
+ .params = "",
+ .help = "quit the emulator",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_quit,
+ },
+
+STEXI
+@item q or quit
+@findex quit
+Quit the emulator.
+ETEXI
+
+ {
+ .name = "block_resize",
+ .args_type = "device:B,size:o",
+ .params = "device size",
+ .help = "resize a block image",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_block_resize,
+ },
+
+STEXI
+@item block_resize
+@findex block_resize
+Resize a block image while a guest is running. Usually requires guest
+action to see the updated size. Resize to a lower size is supported,
+but should be used with extreme caution. Note that this command only
+resizes image files, it can not resize block devices like LVM volumes.
+ETEXI
+
+
+ {
+ .name = "eject",
+ .args_type = "force:-f,device:B",
+ .params = "[-f] device",
+ .help = "eject a removable medium (use -f to force it)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_eject,
+ },
+
+STEXI
+@item eject [-f] @var{device}
+@findex eject
+Eject a removable medium (use -f to force it).
+ETEXI
+
+ {
+ .name = "drive_del",
+ .args_type = "id:s",
+ .params = "device",
+ .help = "remove host block device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_drive_del,
+ },
+
+STEXI
+@item drive_del @var{device}
+@findex drive_del
+Remove host block device. The result is that guest generated IO is no longer
+submitted against the host device underlying the disk. Once a drive has
+been deleted, the QEMU Block layer returns -EIO which results in IO
+errors in the guest for applications that are reading/writing to the device.
+ETEXI
+
+ {
+ .name = "change",
+ .args_type = "device:B,target:F,arg:s?",
+ .params = "device filename [format]",
+ .help = "change a removable medium, optional format",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_change,
+ },
+
+STEXI
+@item change @var{device} @var{setting}
+@findex change
+
+Change the configuration of a device.
+
+@table @option
+@item change @var{diskdevice} @var{filename} [@var{format}]
+Change the medium for a removable disk device to point to @var{filename}. eg
+
+@example
+(qemu) change ide1-cd0 /path/to/some.iso
+@end example
+
+@var{format} is optional.
+
+@item change vnc @var{display},@var{options}
+Change the configuration of the VNC server. The valid syntax for @var{display}
+and @var{options} are described at @ref{sec_invocation}. eg
+
+@example
+(qemu) change vnc localhost:1
+@end example
+
+@item change vnc password [@var{password}]
+
+Change the password associated with the VNC server. If the new password is not
+supplied, the monitor will prompt for it to be entered. VNC passwords are only
+significant up to 8 letters. eg
+
+@example
+(qemu) change vnc password
+Password: ********
+@end example
+
+@end table
+ETEXI
+
+ {
+ .name = "screendump",
+ .args_type = "filename:F",
+ .params = "filename",
+ .help = "save screen into PPM image 'filename'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_screen_dump,
+ },
+
+STEXI
+@item screendump @var{filename}
+@findex screendump
+Save screen into PPM image @var{filename}.
+ETEXI
+
+ {
+ .name = "logfile",
+ .args_type = "filename:F",
+ .params = "filename",
+ .help = "output logs to 'filename'",
+ .mhandler.cmd = do_logfile,
+ },
+
+STEXI
+@item logfile @var{filename}
+@findex logfile
+Output logs to @var{filename}.
+ETEXI
+
+#ifdef CONFIG_SIMPLE_TRACE
+ {
+ .name = "trace-event",
+ .args_type = "name:s,option:b",
+ .params = "name on|off",
+ .help = "changes status of a specific trace event",
+ .mhandler.cmd = do_change_trace_event_state,
+ },
+
+STEXI
+@item trace-event
+@findex trace-event
+changes status of a trace event
+ETEXI
+
+ {
+ .name = "trace-file",
+ .args_type = "op:s?,arg:F?",
+ .params = "on|off|flush|set [arg]",
+ .help = "open, close, or flush trace file, or set a new file name",
+ .mhandler.cmd = do_trace_file,
+ },
+
+STEXI
+@item trace-file on|off|flush
+@findex trace-file
+Open, close, or flush the trace file. If no argument is given, the status of the trace file is displayed.
+ETEXI
+#endif
+
+ {
+ .name = "log",
+ .args_type = "items:s",
+ .params = "item1[,...]",
+ .help = "activate logging of the specified items to '/tmp/qemu.log'",
+ .mhandler.cmd = do_log,
+ },
+
+STEXI
+@item log @var{item1}[,...]
+@findex log
+Activate logging of the specified items to @file{/tmp/qemu.log}.
+ETEXI
+
+ {
+ .name = "savevm",
+ .args_type = "name:s?",
+ .params = "[tag|id]",
+ .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+ .mhandler.cmd = do_savevm,
+ },
+
+STEXI
+@item savevm [@var{tag}|@var{id}]
+@findex savevm
+Create a snapshot of the whole virtual machine. If @var{tag} is
+provided, it is used as human readable identifier. If there is already
+a snapshot with the same tag or ID, it is replaced. More info at
+@ref{vm_snapshots}.
+ETEXI
+
+ {
+ .name = "loadvm",
+ .args_type = "name:s",
+ .params = "tag|id",
+ .help = "restore a VM snapshot from its tag or id",
+ .mhandler.cmd = do_loadvm,
+ },
+
+STEXI
+@item loadvm @var{tag}|@var{id}
+@findex loadvm
+Set the whole virtual machine to the snapshot identified by the tag
+@var{tag} or the unique snapshot ID @var{id}.
+ETEXI
+
+ {
+ .name = "delvm",
+ .args_type = "name:s",
+ .params = "tag|id",
+ .help = "delete a VM snapshot from its tag or id",
+ .mhandler.cmd = do_delvm,
+ },
+
+STEXI
+@item delvm @var{tag}|@var{id}
+@findex delvm
+Delete the snapshot identified by @var{tag} or @var{id}.
+ETEXI
+
+ {
+ .name = "singlestep",
+ .args_type = "option:s?",
+ .params = "[on|off]",
+ .help = "run emulation in singlestep mode or switch to normal mode",
+ .mhandler.cmd = do_singlestep,
+ },
+
+STEXI
+@item singlestep [off]
+@findex singlestep
+Run the emulation in single step mode.
+If called with option off, the emulation returns to normal mode.
+ETEXI
+
+ {
+ .name = "stop",
+ .args_type = "",
+ .params = "",
+ .help = "stop emulation",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_stop,
+ },
+
+STEXI
+@item stop
+@findex stop
+Stop emulation.
+ETEXI
+
+ {
+ .name = "c|cont",
+ .args_type = "",
+ .params = "",
+ .help = "resume emulation",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_cont,
+ },
+
+STEXI
+@item c or cont
+@findex cont
+Resume emulation.
+ETEXI
+
+ {
+ .name = "gdbserver",
+ .args_type = "device:s?",
+ .params = "[device]",
+ .help = "start gdbserver on given device (default 'tcp::1234'), stop with 'none'",
+ .mhandler.cmd = do_gdbserver,
+ },
+
+STEXI
+@item gdbserver [@var{port}]
+@findex gdbserver
+Start gdbserver session (default @var{port}=1234)
+ETEXI
+
+ {
+ .name = "x",
+ .args_type = "fmt:/,addr:l",
+ .params = "/fmt addr",
+ .help = "virtual memory dump starting at 'addr'",
+ .mhandler.cmd = do_memory_dump,
+ },
+
+STEXI
+@item x/fmt @var{addr}
+@findex x
+Virtual memory dump starting at @var{addr}.
+ETEXI
+
+ {
+ .name = "xp",
+ .args_type = "fmt:/,addr:l",
+ .params = "/fmt addr",
+ .help = "physical memory dump starting at 'addr'",
+ .mhandler.cmd = do_physical_memory_dump,
+ },
+
+STEXI
+@item xp /@var{fmt} @var{addr}
+@findex xp
+Physical memory dump starting at @var{addr}.
+
+@var{fmt} is a format which tells the command how to format the
+data. Its syntax is: @option{/@{count@}@{format@}@{size@}}
+
+@table @var
+@item count
+is the number of items to be dumped.
+
+@item format
+can be x (hex), d (signed decimal), u (unsigned decimal), o (octal),
+c (char) or i (asm instruction).
+
+@item size
+can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86,
+@code{h} or @code{w} can be specified with the @code{i} format to
+respectively select 16 or 32 bit code instruction size.
+
+@end table
+
+Examples:
+@itemize
+@item
+Dump 10 instructions at the current instruction pointer:
+@example
+(qemu) x/10i $eip
+0x90107063: ret
+0x90107064: sti
+0x90107065: lea 0x0(%esi,1),%esi
+0x90107069: lea 0x0(%edi,1),%edi
+0x90107070: ret
+0x90107071: jmp 0x90107080
+0x90107073: nop
+0x90107074: nop
+0x90107075: nop
+0x90107076: nop
+@end example
+
+@item
+Dump 80 16 bit values at the start of the video memory.
+@smallexample
+(qemu) xp/80hx 0xb8000
+0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42
+0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41
+0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72
+0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73
+0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20
+0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720
+0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720
+@end smallexample
+@end itemize
+ETEXI
+
+ {
+ .name = "p|print",
+ .args_type = "fmt:/,val:l",
+ .params = "/fmt expr",
+ .help = "print expression value (use $reg for CPU register access)",
+ .mhandler.cmd = do_print,
+ },
+
+STEXI
+@item p or print/@var{fmt} @var{expr}
+@findex print
+
+Print expression value. Only the @var{format} part of @var{fmt} is
+used.
+ETEXI
+
+ {
+ .name = "i",
+ .args_type = "fmt:/,addr:i,index:i.",
+ .params = "/fmt addr",
+ .help = "I/O port read",
+ .mhandler.cmd = do_ioport_read,
+ },
+
+STEXI
+Read I/O port.
+ETEXI
+
+ {
+ .name = "o",
+ .args_type = "fmt:/,addr:i,val:i",
+ .params = "/fmt addr value",
+ .help = "I/O port write",
+ .mhandler.cmd = do_ioport_write,
+ },
+
+STEXI
+Write to I/O port.
+ETEXI
+
+ {
+ .name = "sendkey",
+ .args_type = "string:s,hold_time:i?",
+ .params = "keys [hold_ms]",
+ .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
+ .mhandler.cmd = do_sendkey,
+ },
+
+STEXI
+@item sendkey @var{keys}
+@findex sendkey
+
+Send @var{keys} to the emulator. @var{keys} could be the name of the
+key or @code{#} followed by the raw value in either decimal or hexadecimal
+format. Use @code{-} to press several keys simultaneously. Example:
+@example
+sendkey ctrl-alt-f1
+@end example
+
+This command is useful to send keys that your graphical user interface
+intercepts at low level, such as @code{ctrl-alt-f1} in X Window.
+ETEXI
+
+ {
+ .name = "system_reset",
+ .args_type = "",
+ .params = "",
+ .help = "reset the system",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_system_reset,
+ },
+
+STEXI
+@item system_reset
+@findex system_reset
+
+Reset the system.
+ETEXI
+
+ {
+ .name = "system_powerdown",
+ .args_type = "",
+ .params = "",
+ .help = "send system power down event",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_system_powerdown,
+ },
+
+STEXI
+@item system_powerdown
+@findex system_powerdown
+
+Power down the system (if supported).
+ETEXI
+
+ {
+ .name = "sum",
+ .args_type = "start:i,size:i",
+ .params = "addr size",
+ .help = "compute the checksum of a memory region",
+ .mhandler.cmd = do_sum,
+ },
+
+STEXI
+@item sum @var{addr} @var{size}
+@findex sum
+
+Compute the checksum of a memory region.
+ETEXI
+
+ {
+ .name = "usb_add",
+ .args_type = "devname:s",
+ .params = "device",
+ .help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')",
+ .mhandler.cmd = do_usb_add,
+ },
+
+STEXI
+@item usb_add @var{devname}
+@findex usb_add
+
+Add the USB device @var{devname}. For details of available devices see
+@ref{usb_devices}
+ETEXI
+
+ {
+ .name = "usb_del",
+ .args_type = "devname:s",
+ .params = "device",
+ .help = "remove USB device 'bus.addr'",
+ .mhandler.cmd = do_usb_del,
+ },
+
+STEXI
+@item usb_del @var{devname}
+@findex usb_del
+
+Remove the USB device @var{devname} from the QEMU virtual USB
+hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor
+command @code{info usb} to see the devices you can remove.
+ETEXI
+
+ {
+ .name = "device_add",
+ .args_type = "device:O",
+ .params = "driver[,prop=value][,...]",
+ .help = "add device, like -device on the command line",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_device_add,
+ },
+
+STEXI
+@item device_add @var{config}
+@findex device_add
+
+Add device.
+ETEXI
+
+ {
+ .name = "device_del",
+ .args_type = "id:s",
+ .params = "device",
+ .help = "remove device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_device_del,
+ },
+
+STEXI
+@item device_del @var{id}
+@findex device_del
+
+Remove device @var{id}.
+ETEXI
+
+ {
+ .name = "cpu",
+ .args_type = "index:i",
+ .params = "index",
+ .help = "set the default CPU",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_cpu_set,
+ },
+
+STEXI
+@item cpu @var{index}
+@findex cpu
+Set the default CPU.
+ETEXI
+
+ {
+ .name = "mouse_move",
+ .args_type = "dx_str:s,dy_str:s,dz_str:s?",
+ .params = "dx dy [dz]",
+ .help = "send mouse move events",
+ .mhandler.cmd = do_mouse_move,
+ },
+
+STEXI
+@item mouse_move @var{dx} @var{dy} [@var{dz}]
+@findex mouse_move
+Move the active mouse to the specified coordinates @var{dx} @var{dy}
+with optional scroll axis @var{dz}.
+ETEXI
+
+ {
+ .name = "mouse_button",
+ .args_type = "button_state:i",
+ .params = "state",
+ .help = "change mouse button state (1=L, 2=M, 4=R)",
+ .mhandler.cmd = do_mouse_button,
+ },
+
+STEXI
+@item mouse_button @var{val}
+@findex mouse_button
+Change the active mouse button state @var{val} (1=L, 2=M, 4=R).
+ETEXI
+
+ {
+ .name = "mouse_set",
+ .args_type = "index:i",
+ .params = "index",
+ .help = "set which mouse device receives events",
+ .mhandler.cmd = do_mouse_set,
+ },
+
+STEXI
+@item mouse_set @var{index}
+@findex mouse_set
+Set which mouse device receives events at given @var{index}, index
+can be obtained with
+@example
+info mice
+@end example
+ETEXI
+
+#ifdef HAS_AUDIO
+ {
+ .name = "wavcapture",
+ .args_type = "path:F,freq:i?,bits:i?,nchannels:i?",
+ .params = "path [frequency [bits [channels]]]",
+ .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
+ .mhandler.cmd = do_wav_capture,
+ },
+#endif
+STEXI
+@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]]
+@findex wavcapture
+Capture audio into @var{filename}. Using sample rate @var{frequency}
+bits per sample @var{bits} and number of channels @var{channels}.
+
+Defaults:
+@itemize @minus
+@item Sample rate = 44100 Hz - CD quality
+@item Bits = 16
+@item Number of channels = 2 - Stereo
+@end itemize
+ETEXI
+
+#ifdef HAS_AUDIO
+ {
+ .name = "stopcapture",
+ .args_type = "n:i",
+ .params = "capture index",
+ .help = "stop capture",
+ .mhandler.cmd = do_stop_capture,
+ },
+#endif
+STEXI
+@item stopcapture @var{index}
+@findex stopcapture
+Stop capture with a given @var{index}, index can be obtained with
+@example
+info capture
+@end example
+ETEXI
+
+ {
+ .name = "memsave",
+ .args_type = "val:l,size:i,filename:s",
+ .params = "addr size file",
+ .help = "save to disk virtual memory dump starting at 'addr' of size 'size'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_memory_save,
+ },
+
+STEXI
+@item memsave @var{addr} @var{size} @var{file}
+@findex memsave
+save to disk virtual memory dump starting at @var{addr} of size @var{size}.
+ETEXI
+
+ {
+ .name = "pmemsave",
+ .args_type = "val:l,size:i,filename:s",
+ .params = "addr size file",
+ .help = "save to disk physical memory dump starting at 'addr' of size 'size'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_physical_memory_save,
+ },
+
+STEXI
+@item pmemsave @var{addr} @var{size} @var{file}
+@findex pmemsave
+save to disk physical memory dump starting at @var{addr} of size @var{size}.
+ETEXI
+
+ {
+ .name = "boot_set",
+ .args_type = "bootdevice:s",
+ .params = "bootdevice",
+ .help = "define new values for the boot device list",
+ .mhandler.cmd = do_boot_set,
+ },
+
+STEXI
+@item boot_set @var{bootdevicelist}
+@findex boot_set
+
+Define new values for the boot device list. Those values will override
+the values specified on the command line through the @code{-boot} option.
+
+The values that can be specified here depend on the machine type, but are
+the same that can be specified in the @code{-boot} command line option.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "nmi",
+ .args_type = "cpu_index:i",
+ .params = "cpu",
+ .help = "inject an NMI on the given CPU",
+ .mhandler.cmd = do_inject_nmi,
+ },
+#endif
+STEXI
+@item nmi @var{cpu}
+@findex nmi
+Inject an NMI on the given CPU (x86 only).
+ETEXI
+
+ {
+ .name = "migrate",
+ .args_type = "detach:-d,blk:-b,inc:-i,uri:s",
+ .params = "[-d] [-b] [-i] uri",
+ .help = "migrate to URI (using -d to not wait for completion)"
+ "\n\t\t\t -b for migration without shared storage with"
+ " full copy of disk\n\t\t\t -i for migration without "
+ "shared storage with incremental copy of disk "
+ "(base image shared between src and destination)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate,
+ },
+
+
+STEXI
+@item migrate [-d] [-b] [-i] @var{uri}
+@findex migrate
+Migrate to @var{uri} (using -d to not wait for completion).
+ -b for migration with full copy of disk
+ -i for migration with incremental copy of disk (base image is shared)
+ETEXI
+
+ {
+ .name = "migrate_cancel",
+ .args_type = "",
+ .params = "",
+ .help = "cancel the current VM migration",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_cancel,
+ },
+
+STEXI
+@item migrate_cancel
+@findex migrate_cancel
+Cancel the current VM migration.
+ETEXI
+
+ {
+ .name = "migrate_set_speed",
+ .args_type = "value:o",
+ .params = "value",
+ .help = "set maximum speed (in bytes) for migrations. "
+ "Defaults to MB if no size suffix is specified, ie. B/K/M/G/T",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_set_speed,
+ },
+
+STEXI
+@item migrate_set_speed @var{value}
+@findex migrate_set_speed
+Set maximum speed to @var{value} (in bytes) for migrations.
+ETEXI
+
+ {
+ .name = "migrate_set_downtime",
+ .args_type = "value:T",
+ .params = "value",
+ .help = "set maximum tolerated downtime (in seconds) for migrations",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_set_downtime,
+ },
+
+STEXI
+@item migrate_set_downtime @var{second}
+@findex migrate_set_downtime
+Set maximum tolerated downtime (in seconds) for migration.
+ETEXI
+
+ {
+ .name = "snapshot_blkdev",
+ .args_type = "device:B,snapshot_file:s?,format:s?",
+ .params = "device [new-image-file] [format]",
+ .help = "initiates a live snapshot\n\t\t\t"
+ "of device. If a new image file is specified, the\n\t\t\t"
+ "new image file will become the new root image.\n\t\t\t"
+ "If format is specified, the snapshot file will\n\t\t\t"
+ "be created in that format. Otherwise the\n\t\t\t"
+ "snapshot will be internal! (currently unsupported)",
+ .mhandler.cmd_new = do_snapshot_blkdev,
+ },
+
+STEXI
+@item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject}
+@findex client_migrate_info
+Set the spice/vnc connection info for the migration target. The spice/vnc
+server will ask the spice/vnc client to automatically reconnect using the
+new parameters (if specified) once the vm migration finished successfully.
+ETEXI
+
+ {
+ .name = "client_migrate_info",
+ .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
+ .params = "protocol hostname port tls-port cert-subject",
+ .help = "send migration info to spice/vnc client",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = client_migrate_info,
+ },
+
+STEXI
+@item snapshot_blkdev
+@findex snapshot_blkdev
+Snapshot device, using snapshot file as target if provided
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "drive_add",
+ .args_type = "pci_addr:s,opts:s",
+ .params = "[[<domain>:]<bus>:]<slot>\n"
+ "[file=file][,if=type][,bus=n]\n"
+ "[,unit=m][,media=d][index=i]\n"
+ "[,cyls=c,heads=h,secs=s[,trans=t]]\n"
+ "[snapshot=on|off][,cache=on|off]",
+ .help = "add drive to PCI storage controller",
+ .mhandler.cmd = drive_hot_add,
+ },
+#endif
+
+STEXI
+@item drive_add
+@findex drive_add
+Add drive to PCI storage controller.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "pci_add",
+ .args_type = "pci_addr:s,type:s,opts:s?",
+ .params = "auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...",
+ .help = "hot-add PCI device",
+ .mhandler.cmd = pci_device_hot_add,
+ },
+#endif
+
+STEXI
+@item pci_add
+@findex pci_add
+Hot-add PCI device.
+ETEXI
+
+#if defined(TARGET_I386)
+ {
+ .name = "pci_del",
+ .args_type = "pci_addr:s",
+ .params = "[[<domain>:]<bus>:]<slot>",
+ .help = "hot remove PCI device",
+ .mhandler.cmd = do_pci_device_hot_remove,
+ },
+#endif
+
+STEXI
+@item pci_del
+@findex pci_del
+Hot remove PCI device.
+ETEXI
+
+ {
+ .name = "pcie_aer_inject_error",
+ .args_type = "advisory_non_fatal:-a,correctable:-c,"
+ "id:s,error_status:s,"
+ "header0:i?,header1:i?,header2:i?,header3:i?,"
+ "prefix0:i?,prefix1:i?,prefix2:i?,prefix3:i?",
+ .params = "[-a] [-c] id "
+ "<error_status> [<tlp header> [<tlp header prefix>]]",
+ .help = "inject pcie aer error\n\t\t\t"
+ " -a for advisory non fatal error\n\t\t\t"
+ " -c for correctable error\n\t\t\t"
+ "<id> = qdev device id\n\t\t\t"
+ "<error_status> = error string or 32bit\n\t\t\t"
+ "<tlb header> = 32bit x 4\n\t\t\t"
+ "<tlb header prefix> = 32bit x 4",
+ .user_print = pcie_aer_inject_error_print,
+ .mhandler.cmd_new = do_pcie_aer_inejct_error,
+ },
+
+STEXI
+@item pcie_aer_inject_error
+@findex pcie_aer_inject_error
+Inject PCIe AER error
+ETEXI
+
+ {
+ .name = "host_net_add",
+ .args_type = "device:s,opts:s?",
+ .params = "tap|user|socket|vde|dump [options]",
+ .help = "add host VLAN client",
+ .mhandler.cmd = net_host_device_add,
+ },
+
+STEXI
+@item host_net_add
+@findex host_net_add
+Add host VLAN client.
+ETEXI
+
+ {
+ .name = "host_net_remove",
+ .args_type = "vlan_id:i,device:s",
+ .params = "vlan_id name",
+ .help = "remove host VLAN client",
+ .mhandler.cmd = net_host_device_remove,
+ },
+
+STEXI
+@item host_net_remove
+@findex host_net_remove
+Remove host VLAN client.
+ETEXI
+
+ {
+ .name = "netdev_add",
+ .args_type = "netdev:O",
+ .params = "[user|tap|socket],id=str[,prop=value][,...]",
+ .help = "add host network device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_netdev_add,
+ },
+
+STEXI
+@item netdev_add
+@findex netdev_add
+Add host network device.
+ETEXI
+
+ {
+ .name = "netdev_del",
+ .args_type = "id:s",
+ .params = "id",
+ .help = "remove host network device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_netdev_del,
+ },
+
+STEXI
+@item netdev_del
+@findex netdev_del
+Remove host network device.
+ETEXI
+
+#ifdef CONFIG_SLIRP
+ {
+ .name = "hostfwd_add",
+ .args_type = "arg1:s,arg2:s?,arg3:s?",
+ .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport",
+ .help = "redirect TCP or UDP connections from host to guest (requires -net user)",
+ .mhandler.cmd = net_slirp_hostfwd_add,
+ },
+#endif
+STEXI
+@item hostfwd_add
+@findex hostfwd_add
+Redirect TCP or UDP connections from host to guest (requires -net user).
+ETEXI
+
+#ifdef CONFIG_SLIRP
+ {
+ .name = "hostfwd_remove",
+ .args_type = "arg1:s,arg2:s?,arg3:s?",
+ .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport",
+ .help = "remove host-to-guest TCP or UDP redirection",
+ .mhandler.cmd = net_slirp_hostfwd_remove,
+ },
+
+#endif
+STEXI
+@item hostfwd_remove
+@findex hostfwd_remove
+Remove host-to-guest TCP or UDP redirection.
+ETEXI
+
+ {
+ .name = "balloon",
+ .args_type = "value:M",
+ .params = "target",
+ .help = "request VM to change its memory allocation (in MB)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_async = do_balloon,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+STEXI
+@item balloon @var{value}
+@findex balloon
+Request VM to change its memory allocation to @var{value} (in MB).
+ETEXI
+
+ {
+ .name = "set_link",
+ .args_type = "name:s,up:b",
+ .params = "name on|off",
+ .help = "change the link status of a network adapter",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_set_link,
+ },
+
+STEXI
+@item set_link @var{name} [on|off]
+@findex set_link
+Switch link @var{name} on (i.e. up) or off (i.e. down).
+ETEXI
+
+ {
+ .name = "watchdog_action",
+ .args_type = "action:s",
+ .params = "[reset|shutdown|poweroff|pause|debug|none]",
+ .help = "change watchdog action",
+ .mhandler.cmd = do_watchdog_action,
+ },
+
+STEXI
+@item watchdog_action
+@findex watchdog_action
+Change watchdog action.
+ETEXI
+
+ {
+ .name = "acl_show",
+ .args_type = "aclname:s",
+ .params = "aclname",
+ .help = "list rules in the access control list",
+ .mhandler.cmd = do_acl_show,
+ },
+
+STEXI
+@item acl_show @var{aclname}
+@findex acl_show
+List all the matching rules in the access control list, and the default
+policy. There are currently two named access control lists,
+@var{vnc.x509dname} and @var{vnc.username} matching on the x509 client
+certificate distinguished name, and SASL username respectively.
+ETEXI
+
+ {
+ .name = "acl_policy",
+ .args_type = "aclname:s,policy:s",
+ .params = "aclname allow|deny",
+ .help = "set default access control list policy",
+ .mhandler.cmd = do_acl_policy,
+ },
+
+STEXI
+@item acl_policy @var{aclname} @code{allow|deny}
+@findex acl_policy
+Set the default access control list policy, used in the event that
+none of the explicit rules match. The default policy at startup is
+always @code{deny}.
+ETEXI
+
+ {
+ .name = "acl_add",
+ .args_type = "aclname:s,match:s,policy:s,index:i?",
+ .params = "aclname match allow|deny [index]",
+ .help = "add a match rule to the access control list",
+ .mhandler.cmd = do_acl_add,
+ },
+
+STEXI
+@item acl_add @var{aclname} @var{match} @code{allow|deny} [@var{index}]
+@findex acl_add
+Add a match rule to the access control list, allowing or denying access.
+The match will normally be an exact username or x509 distinguished name,
+but can optionally include wildcard globs. eg @code{*@@EXAMPLE.COM} to
+allow all users in the @code{EXAMPLE.COM} kerberos realm. The match will
+normally be appended to the end of the ACL, but can be inserted
+earlier in the list if the optional @var{index} parameter is supplied.
+ETEXI
+
+ {
+ .name = "acl_remove",
+ .args_type = "aclname:s,match:s",
+ .params = "aclname match",
+ .help = "remove a match rule from the access control list",
+ .mhandler.cmd = do_acl_remove,
+ },
+
+STEXI
+@item acl_remove @var{aclname} @var{match}
+@findex acl_remove
+Remove the specified match rule from the access control list.
+ETEXI
+
+ {
+ .name = "acl_reset",
+ .args_type = "aclname:s",
+ .params = "aclname",
+ .help = "reset the access control list",
+ .mhandler.cmd = do_acl_reset,
+ },
+
+STEXI
+@item acl_reset @var{aclname}
+@findex acl_reset
+Remove all matches from the access control list, and set the default
+policy back to @code{deny}.
+ETEXI
+
+#if defined(TARGET_I386)
+
+ {
+ .name = "mce",
+ .args_type = "broadcast:-b,cpu_index:i,bank:i,status:l,mcg_status:l,addr:l,misc:l",
+ .params = "[-b] cpu bank status mcgstatus addr misc",
+ .help = "inject a MCE on the given CPU [and broadcast to other CPUs with -b option]",
+ .mhandler.cmd = do_inject_mce,
+ },
+
+#endif
+STEXI
+@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc}
+@findex mce (x86)
+Inject an MCE on the given CPU (x86 only).
+ETEXI
+
+ {
+ .name = "getfd",
+ .args_type = "fdname:s",
+ .params = "getfd name",
+ .help = "receive a file descriptor via SCM rights and assign it a name",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_getfd,
+ },
+
+STEXI
+@item getfd @var{fdname}
+@findex getfd
+If a file descriptor is passed alongside this command using the SCM_RIGHTS
+mechanism on unix sockets, it is stored using the name @var{fdname} for
+later use by other monitor commands.
+ETEXI
+
+ {
+ .name = "closefd",
+ .args_type = "fdname:s",
+ .params = "closefd name",
+ .help = "close a file descriptor previously passed via SCM rights",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_closefd,
+ },
+
+STEXI
+@item closefd @var{fdname}
+@findex closefd
+Close the file descriptor previously assigned to @var{fdname} using the
+@code{getfd} command. This is only needed if the file descriptor was never
+used by another monitor command.
+ETEXI
+
+ {
+ .name = "block_passwd",
+ .args_type = "device:B,password:s",
+ .params = "block_passwd device password",
+ .help = "set the password of encrypted block devices",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_block_set_passwd,
+ },
+
+STEXI
+@item block_passwd @var{device} @var{password}
+@findex block_passwd
+Set the encrypted device @var{device} password to @var{password}
+ETEXI
+
+ {
+ .name = "cpu_set",
+ .args_type = "cpu:i,state:s",
+ .params = "cpu [online|offline]",
+ .help = "change cpu state",
+ .mhandler.cmd = do_cpu_set_nr,
+ },
+
+STEXI
+@item cpu_set @var{cpu} [online|offline]
+Set CPU @var{cpu} online or offline.
+ETEXI
+
+ {
+ .name = "set_password",
+ .args_type = "protocol:s,password:s,connected:s?",
+ .params = "protocol password action-if-connected",
+ .help = "set spice/vnc password",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = set_password,
+ },
+
+STEXI
+@item set_password [ vnc | spice ] password [ action-if-connected ]
+@findex set_password
+
+Change spice/vnc password. Use zero to make the password stay valid
+forever. @var{action-if-connected} specifies what should happen in
+case a connection is established: @var{fail} makes the password change
+fail. @var{disconnect} changes the password and disconnects the
+client. @var{keep} changes the password and keeps the connection up.
+@var{keep} is the default.
+ETEXI
+
+ {
+ .name = "expire_password",
+ .args_type = "protocol:s,time:s",
+ .params = "protocol time",
+ .help = "set spice/vnc password expire-time",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = expire_password,
+ },
+
+STEXI
+@item expire_password [ vnc | spice ] expire-time
+@findex expire_password
+
+Specify when a password for spice/vnc becomes
+invalid. @var{expire-time} accepts:
+
+@table @var
+@item now
+Invalidate password instantly.
+
+@item never
+Password stays valid forever.
+
+@item +nsec
+Password stays valid for @var{nsec} seconds starting now.
+
+@item nsec
+Password is invalidated at the given time. @var{nsec} are the seconds
+passed since 1970, i.e. unix epoch.
+
+@end table
+ETEXI
+
+ {
+ .name = "info",
+ .args_type = "item:s?",
+ .params = "[subcommand]",
+ .help = "show various information about the system state",
+ .mhandler.cmd = do_info,
+ },
+
+STEXI
+@item info @var{subcommand}
+@findex info
+Show various information about the system state.
+
+@table @option
+@item info version
+show the version of QEMU
+@item info network
+show the various VLANs and the associated devices
+@item info chardev
+show the character devices
+@item info block
+show the block devices
+@item info blockstats
+show block device statistics
+@item info registers
+show the cpu registers
+@item info cpus
+show infos for each CPU
+@item info history
+show the command line history
+@item info irq
+show the interrupts statistics (if available)
+@item info pic
+show i8259 (PIC) state
+@item info pci
+show emulated PCI device info
+@item info tlb
+show virtual to physical memory mappings (i386, SH4 and SPARC only)
+@item info mem
+show the active virtual memory mappings (i386 only)
+@item info jit
+show dynamic compiler info
+@item info kvm
+show KVM information
+@item info numa
+show NUMA information
+@item info kvm
+show KVM information
+@item info usb
+show USB devices plugged on the virtual USB hub
+@item info usbhost
+show all USB host devices
+@item info profile
+show profiling information
+@item info capture
+show information about active capturing
+@item info snapshots
+show list of VM snapshots
+@item info status
+show the current VM status (running|paused)
+@item info pcmcia
+show guest PCMCIA status
+@item info mice
+show which guest mouse is receiving events
+@item info vnc
+show the vnc server status
+@item info name
+show the current VM name
+@item info uuid
+show the current VM UUID
+@item info cpustats
+show CPU statistics
+@item info usernet
+show user network stack connection states
+@item info migrate
+show migration status
+@item info balloon
+show balloon information
+@item info qtree
+show device tree
+@item info qdm
+show qdev device model list
+@item info roms
+show roms
+@end table
+ETEXI
+
+#ifdef CONFIG_SIMPLE_TRACE
+STEXI
+@item info trace
+show contents of trace buffer
+@item info trace-events
+show available trace events and their state
+ETEXI
+#endif
+
+STEXI
+@end table
+ETEXI
diff --git a/host-utils.c b/host-utils.c
new file mode 100644
index 0000000..dc96123
--- /dev/null
+++ b/host-utils.c
@@ -0,0 +1,105 @@
+/*
+ * Utility compute operations used by translated code.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2007 Aurelien Jarno
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "host-utils.h"
+
+//#define DEBUG_MULDIV
+
+/* Long integer helpers */
+#if !defined(__x86_64__)
+static void add128 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ *plow += a;
+ /* carry test */
+ if (*plow < a)
+ (*phigh)++;
+ *phigh += b;
+}
+
+static void neg128 (uint64_t *plow, uint64_t *phigh)
+{
+ *plow = ~*plow;
+ *phigh = ~*phigh;
+ add128(plow, phigh, 1, 0);
+}
+
+static void mul64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ uint32_t a0, a1, b0, b1;
+ uint64_t v;
+
+ a0 = a;
+ a1 = a >> 32;
+
+ b0 = b;
+ b1 = b >> 32;
+
+ v = (uint64_t)a0 * (uint64_t)b0;
+ *plow = v;
+ *phigh = 0;
+
+ v = (uint64_t)a0 * (uint64_t)b1;
+ add128(plow, phigh, v << 32, v >> 32);
+
+ v = (uint64_t)a1 * (uint64_t)b0;
+ add128(plow, phigh, v << 32, v >> 32);
+
+ v = (uint64_t)a1 * (uint64_t)b1;
+ *phigh += v;
+}
+
+/* Unsigned 64x64 -> 128 multiplication */
+void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ mul64(plow, phigh, a, b);
+#if defined(DEBUG_MULDIV)
+ printf("mulu64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
+ a, b, *phigh, *plow);
+#endif
+}
+
+/* Signed 64x64 -> 128 multiplication */
+void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
+{
+ int sa, sb;
+
+ sa = (a < 0);
+ if (sa)
+ a = -a;
+ sb = (b < 0);
+ if (sb)
+ b = -b;
+ mul64(plow, phigh, a, b);
+ if (sa ^ sb) {
+ neg128(plow, phigh);
+ }
+#if defined(DEBUG_MULDIV)
+ printf("muls64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
+ a, b, *phigh, *plow);
+#endif
+}
+#endif /* !defined(__x86_64__) */
diff --git a/host-utils.h b/host-utils.h
new file mode 100644
index 0000000..0ddc176
--- /dev/null
+++ b/host-utils.h
@@ -0,0 +1,236 @@
+/*
+ * Utility compute operations used by translated code.
+ *
+ * Copyright (c) 2007 Thiemo Seufer
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "osdep.h"
+
+#if defined(__x86_64__)
+#define __HAVE_FAST_MULU64__
+static inline void mulu64(uint64_t *plow, uint64_t *phigh,
+ uint64_t a, uint64_t b)
+{
+ __asm__ ("mul %0\n\t"
+ : "=d" (*phigh), "=a" (*plow)
+ : "a" (a), "0" (b));
+}
+#define __HAVE_FAST_MULS64__
+static inline void muls64(uint64_t *plow, uint64_t *phigh,
+ int64_t a, int64_t b)
+{
+ __asm__ ("imul %0\n\t"
+ : "=d" (*phigh), "=a" (*plow)
+ : "a" (a), "0" (b));
+}
+#else
+void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b);
+void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b);
+#endif
+
+/* Binary search for leading zeros. */
+
+static inline int clz32(uint32_t val)
+{
+#if QEMU_GNUC_PREREQ(3, 4)
+ if (val)
+ return __builtin_clz(val);
+ else
+ return 32;
+#else
+ int cnt = 0;
+
+ if (!(val & 0xFFFF0000U)) {
+ cnt += 16;
+ val <<= 16;
+ }
+ if (!(val & 0xFF000000U)) {
+ cnt += 8;
+ val <<= 8;
+ }
+ if (!(val & 0xF0000000U)) {
+ cnt += 4;
+ val <<= 4;
+ }
+ if (!(val & 0xC0000000U)) {
+ cnt += 2;
+ val <<= 2;
+ }
+ if (!(val & 0x80000000U)) {
+ cnt++;
+ val <<= 1;
+ }
+ if (!(val & 0x80000000U)) {
+ cnt++;
+ }
+ return cnt;
+#endif
+}
+
+static inline int clo32(uint32_t val)
+{
+ return clz32(~val);
+}
+
+static inline int clz64(uint64_t val)
+{
+#if QEMU_GNUC_PREREQ(3, 4)
+ if (val)
+ return __builtin_clzll(val);
+ else
+ return 64;
+#else
+ int cnt = 0;
+
+ if (!(val >> 32)) {
+ cnt += 32;
+ } else {
+ val >>= 32;
+ }
+
+ return cnt + clz32(val);
+#endif
+}
+
+static inline int clo64(uint64_t val)
+{
+ return clz64(~val);
+}
+
+static inline int ctz32(uint32_t val)
+{
+#if QEMU_GNUC_PREREQ(3, 4)
+ if (val)
+ return __builtin_ctz(val);
+ else
+ return 32;
+#else
+ int cnt;
+
+ cnt = 0;
+ if (!(val & 0x0000FFFFUL)) {
+ cnt += 16;
+ val >>= 16;
+ }
+ if (!(val & 0x000000FFUL)) {
+ cnt += 8;
+ val >>= 8;
+ }
+ if (!(val & 0x0000000FUL)) {
+ cnt += 4;
+ val >>= 4;
+ }
+ if (!(val & 0x00000003UL)) {
+ cnt += 2;
+ val >>= 2;
+ }
+ if (!(val & 0x00000001UL)) {
+ cnt++;
+ val >>= 1;
+ }
+ if (!(val & 0x00000001UL)) {
+ cnt++;
+ }
+
+ return cnt;
+#endif
+}
+
+static inline int cto32(uint32_t val)
+{
+ return ctz32(~val);
+}
+
+static inline int ctz64(uint64_t val)
+{
+#if QEMU_GNUC_PREREQ(3, 4)
+ if (val)
+ return __builtin_ctzll(val);
+ else
+ return 64;
+#else
+ int cnt;
+
+ cnt = 0;
+ if (!((uint32_t)val)) {
+ cnt += 32;
+ val >>= 32;
+ }
+
+ return cnt + ctz32(val);
+#endif
+}
+
+static inline int cto64(uint64_t val)
+{
+ return ctz64(~val);
+}
+
+static inline int ctpop8(uint8_t val)
+{
+ val = (val & 0x55) + ((val >> 1) & 0x55);
+ val = (val & 0x33) + ((val >> 2) & 0x33);
+ val = (val & 0x0f) + ((val >> 4) & 0x0f);
+
+ return val;
+}
+
+static inline int ctpop16(uint16_t val)
+{
+ val = (val & 0x5555) + ((val >> 1) & 0x5555);
+ val = (val & 0x3333) + ((val >> 2) & 0x3333);
+ val = (val & 0x0f0f) + ((val >> 4) & 0x0f0f);
+ val = (val & 0x00ff) + ((val >> 8) & 0x00ff);
+
+ return val;
+}
+
+static inline int ctpop32(uint32_t val)
+{
+#if QEMU_GNUC_PREREQ(3, 4)
+ return __builtin_popcount(val);
+#else
+ val = (val & 0x55555555) + ((val >> 1) & 0x55555555);
+ val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
+ val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f);
+ val = (val & 0x00ff00ff) + ((val >> 8) & 0x00ff00ff);
+ val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff);
+
+ return val;
+#endif
+}
+
+static inline int ctpop64(uint64_t val)
+{
+#if QEMU_GNUC_PREREQ(3, 4)
+ return __builtin_popcountll(val);
+#else
+ val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL);
+ val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL);
+ val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & 0x0f0f0f0f0f0f0f0fULL);
+ val = (val & 0x00ff00ff00ff00ffULL) + ((val >> 8) & 0x00ff00ff00ff00ffULL);
+ val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) & 0x0000ffff0000ffffULL);
+ val = (val & 0x00000000ffffffffULL) + ((val >> 32) & 0x00000000ffffffffULL);
+
+ return val;
+#endif
+}
diff --git a/hpet.h b/hpet.h
new file mode 100644
index 0000000..754051a
--- /dev/null
+++ b/hpet.h
@@ -0,0 +1,22 @@
+#ifndef __HPET__
+#define __HPET__ 1
+
+
+
+struct hpet_info {
+ unsigned long hi_ireqfreq; /* Hz */
+ unsigned long hi_flags; /* information */
+ unsigned short hi_hpet;
+ unsigned short hi_timer;
+};
+
+#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */
+
+#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */
+#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */
+#define HPET_INFO _IOR('h', 0x03, struct hpet_info)
+#define HPET_EPI _IO('h', 0x04) /* enable periodic */
+#define HPET_DPI _IO('h', 0x05) /* disable periodic */
+#define HPET_IRQFREQ _IOW('h', 0x6, unsigned long) /* IRQFREQ usec */
+
+#endif /* !__HPET__ */
diff --git a/hppa-dis.c b/hppa-dis.c
new file mode 100644
index 0000000..49f99c8
--- /dev/null
+++ b/hppa-dis.c
@@ -0,0 +1,2831 @@
+/* Disassembler for the PA-RISC. Somewhat derived from sparc-pinsn.c.
+ Copyright 1989, 1990, 1992, 1993, 1994, 1995, 1998, 1999, 2000, 2001, 2003,
+ 2005 Free Software Foundation, Inc.
+
+ Contributed by the Center for Software Science at the
+ University of Utah (pa-gdb-bugs@cs.utah.edu).
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+#include "dis-asm.h"
+
+/* HP PA-RISC SOM object file format: definitions internal to BFD.
+ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000,
+ 2003 Free Software Foundation, Inc.
+
+ Contributed by the Center for Software Science at the
+ University of Utah (pa-gdb-bugs@cs.utah.edu).
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+#ifndef _LIBHPPA_H
+#define _LIBHPPA_H
+
+#define BYTES_IN_WORD 4
+#define PA_PAGESIZE 0x1000
+
+/* The PA instruction set variants. */
+enum pa_arch {pa10 = 10, pa11 = 11, pa20 = 20, pa20w = 25};
+
+/* HP PA-RISC relocation types */
+
+enum hppa_reloc_field_selector_type
+ {
+ R_HPPA_FSEL = 0x0,
+ R_HPPA_LSSEL = 0x1,
+ R_HPPA_RSSEL = 0x2,
+ R_HPPA_LSEL = 0x3,
+ R_HPPA_RSEL = 0x4,
+ R_HPPA_LDSEL = 0x5,
+ R_HPPA_RDSEL = 0x6,
+ R_HPPA_LRSEL = 0x7,
+ R_HPPA_RRSEL = 0x8,
+ R_HPPA_NSEL = 0x9,
+ R_HPPA_NLSEL = 0xa,
+ R_HPPA_NLRSEL = 0xb,
+ R_HPPA_PSEL = 0xc,
+ R_HPPA_LPSEL = 0xd,
+ R_HPPA_RPSEL = 0xe,
+ R_HPPA_TSEL = 0xf,
+ R_HPPA_LTSEL = 0x10,
+ R_HPPA_RTSEL = 0x11,
+ R_HPPA_LTPSEL = 0x12,
+ R_HPPA_RTPSEL = 0x13
+ };
+
+/* /usr/include/reloc.h defines these to constants. We want to use
+ them in enums, so #undef them before we start using them. We might
+ be able to fix this another way by simply managing not to include
+ /usr/include/reloc.h, but currently GDB picks up these defines
+ somewhere. */
+#undef e_fsel
+#undef e_lssel
+#undef e_rssel
+#undef e_lsel
+#undef e_rsel
+#undef e_ldsel
+#undef e_rdsel
+#undef e_lrsel
+#undef e_rrsel
+#undef e_nsel
+#undef e_nlsel
+#undef e_nlrsel
+#undef e_psel
+#undef e_lpsel
+#undef e_rpsel
+#undef e_tsel
+#undef e_ltsel
+#undef e_rtsel
+#undef e_one
+#undef e_two
+#undef e_pcrel
+#undef e_con
+#undef e_plabel
+#undef e_abs
+
+/* for compatibility */
+enum hppa_reloc_field_selector_type_alt
+ {
+ e_fsel = R_HPPA_FSEL,
+ e_lssel = R_HPPA_LSSEL,
+ e_rssel = R_HPPA_RSSEL,
+ e_lsel = R_HPPA_LSEL,
+ e_rsel = R_HPPA_RSEL,
+ e_ldsel = R_HPPA_LDSEL,
+ e_rdsel = R_HPPA_RDSEL,
+ e_lrsel = R_HPPA_LRSEL,
+ e_rrsel = R_HPPA_RRSEL,
+ e_nsel = R_HPPA_NSEL,
+ e_nlsel = R_HPPA_NLSEL,
+ e_nlrsel = R_HPPA_NLRSEL,
+ e_psel = R_HPPA_PSEL,
+ e_lpsel = R_HPPA_LPSEL,
+ e_rpsel = R_HPPA_RPSEL,
+ e_tsel = R_HPPA_TSEL,
+ e_ltsel = R_HPPA_LTSEL,
+ e_rtsel = R_HPPA_RTSEL,
+ e_ltpsel = R_HPPA_LTPSEL,
+ e_rtpsel = R_HPPA_RTPSEL
+ };
+
+enum hppa_reloc_expr_type
+ {
+ R_HPPA_E_ONE = 0,
+ R_HPPA_E_TWO = 1,
+ R_HPPA_E_PCREL = 2,
+ R_HPPA_E_CON = 3,
+ R_HPPA_E_PLABEL = 7,
+ R_HPPA_E_ABS = 18
+ };
+
+/* for compatibility */
+enum hppa_reloc_expr_type_alt
+ {
+ e_one = R_HPPA_E_ONE,
+ e_two = R_HPPA_E_TWO,
+ e_pcrel = R_HPPA_E_PCREL,
+ e_con = R_HPPA_E_CON,
+ e_plabel = R_HPPA_E_PLABEL,
+ e_abs = R_HPPA_E_ABS
+ };
+
+
+/* Relocations for function calls must be accompanied by parameter
+ relocation bits. These bits describe exactly where the caller has
+ placed the function's arguments and where it expects to find a return
+ value.
+
+ Both ELF and SOM encode this information within the addend field
+ of the call relocation. (Note this could break very badly if one
+ was to make a call like bl foo + 0x12345678).
+
+ The high order 10 bits contain parameter relocation information,
+ the low order 22 bits contain the constant offset. */
+
+#define HPPA_R_ARG_RELOC(a) \
+ (((a) >> 22) & 0x3ff)
+#define HPPA_R_CONSTANT(a) \
+ ((((bfd_signed_vma)(a)) << (BFD_ARCH_SIZE-22)) >> (BFD_ARCH_SIZE-22))
+#define HPPA_R_ADDEND(r, c) \
+ (((r) << 22) + ((c) & 0x3fffff))
+
+
+/* Some functions to manipulate PA instructions. */
+
+/* Declare the functions with the unused attribute to avoid warnings. */
+static inline int sign_extend (int, int) ATTRIBUTE_UNUSED;
+static inline int low_sign_extend (int, int) ATTRIBUTE_UNUSED;
+static inline int sign_unext (int, int) ATTRIBUTE_UNUSED;
+static inline int low_sign_unext (int, int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_3 (int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_12 (int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_14 (int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_16 (int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_17 (int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_21 (int) ATTRIBUTE_UNUSED;
+static inline int re_assemble_22 (int) ATTRIBUTE_UNUSED;
+static inline bfd_signed_vma hppa_field_adjust
+ (bfd_vma, bfd_signed_vma, enum hppa_reloc_field_selector_type_alt)
+ ATTRIBUTE_UNUSED;
+static inline int hppa_rebuild_insn (int, int, int) ATTRIBUTE_UNUSED;
+
+
+/* The *sign_extend functions are used to assemble various bitfields
+ taken from an instruction and return the resulting immediate
+ value. */
+
+static inline int
+sign_extend (int x, int len)
+{
+ int signbit = (1 << (len - 1));
+ int mask = (signbit << 1) - 1;
+ return ((x & mask) ^ signbit) - signbit;
+}
+
+static inline int
+low_sign_extend (int x, int len)
+{
+ return (x >> 1) - ((x & 1) << (len - 1));
+}
+
+
+/* The re_assemble_* functions prepare an immediate value for
+ insertion into an opcode. pa-risc uses all sorts of weird bitfields
+ in the instruction to hold the value. */
+
+static inline int
+sign_unext (int x, int len)
+{
+ int len_ones;
+
+ len_ones = (1 << len) - 1;
+
+ return x & len_ones;
+}
+
+static inline int
+low_sign_unext (int x, int len)
+{
+ int temp;
+ int sign;
+
+ sign = (x >> (len-1)) & 1;
+
+ temp = sign_unext (x, len-1);
+
+ return (temp << 1) | sign;
+}
+
+static inline int
+re_assemble_3 (int as3)
+{
+ return (( (as3 & 4) << (13-2))
+ | ((as3 & 3) << (13+1)));
+}
+
+static inline int
+re_assemble_12 (int as12)
+{
+ return (( (as12 & 0x800) >> 11)
+ | ((as12 & 0x400) >> (10 - 2))
+ | ((as12 & 0x3ff) << (1 + 2)));
+}
+
+static inline int
+re_assemble_14 (int as14)
+{
+ return (( (as14 & 0x1fff) << 1)
+ | ((as14 & 0x2000) >> 13));
+}
+
+static inline int
+re_assemble_16 (int as16)
+{
+ int s, t;
+
+ /* Unusual 16-bit encoding, for wide mode only. */
+ t = (as16 << 1) & 0xffff;
+ s = (as16 & 0x8000);
+ return (t ^ s ^ (s >> 1)) | (s >> 15);
+}
+
+static inline int
+re_assemble_17 (int as17)
+{
+ return (( (as17 & 0x10000) >> 16)
+ | ((as17 & 0x0f800) << (16 - 11))
+ | ((as17 & 0x00400) >> (10 - 2))
+ | ((as17 & 0x003ff) << (1 + 2)));
+}
+
+static inline int
+re_assemble_21 (int as21)
+{
+ return (( (as21 & 0x100000) >> 20)
+ | ((as21 & 0x0ffe00) >> 8)
+ | ((as21 & 0x000180) << 7)
+ | ((as21 & 0x00007c) << 14)
+ | ((as21 & 0x000003) << 12));
+}
+
+static inline int
+re_assemble_22 (int as22)
+{
+ return (( (as22 & 0x200000) >> 21)
+ | ((as22 & 0x1f0000) << (21 - 16))
+ | ((as22 & 0x00f800) << (16 - 11))
+ | ((as22 & 0x000400) >> (10 - 2))
+ | ((as22 & 0x0003ff) << (1 + 2)));
+}
+
+
+/* Handle field selectors for PA instructions.
+ The L and R (and LS, RS etc.) selectors are used in pairs to form a
+ full 32 bit address. eg.
+
+ LDIL L'start,%r1 ; put left part into r1
+ LDW R'start(%r1),%r2 ; add r1 and right part to form address
+
+ This function returns sign extended values in all cases.
+*/
+
+static inline bfd_signed_vma
+hppa_field_adjust (bfd_vma sym_val,
+ bfd_signed_vma addend,
+ enum hppa_reloc_field_selector_type_alt r_field)
+{
+ bfd_signed_vma value;
+
+ value = sym_val + addend;
+ switch (r_field)
+ {
+ case e_fsel:
+ /* F: No change. */
+ break;
+
+ case e_nsel:
+ /* N: null selector. I don't really understand what this is all
+ about, but HP's documentation says "this indicates that zero
+ bits are to be used for the displacement on the instruction.
+ This fixup is used to identify three-instruction sequences to
+ access data (for importing shared library data)." */
+ value = 0;
+ break;
+
+ case e_lsel:
+ case e_nlsel:
+ /* L: Select top 21 bits. */
+ value = value >> 11;
+ break;
+
+ case e_rsel:
+ /* R: Select bottom 11 bits. */
+ value = value & 0x7ff;
+ break;
+
+ case e_lssel:
+ /* LS: Round to nearest multiple of 2048 then select top 21 bits. */
+ value = value + 0x400;
+ value = value >> 11;
+ break;
+
+ case e_rssel:
+ /* RS: Select bottom 11 bits for LS.
+ We need to return a value such that 2048 * LS'x + RS'x == x.
+ ie. RS'x = x - ((x + 0x400) & -0x800)
+ this is just a sign extension from bit 21. */
+ value = ((value & 0x7ff) ^ 0x400) - 0x400;
+ break;
+
+ case e_ldsel:
+ /* LD: Round to next multiple of 2048 then select top 21 bits.
+ Yes, if we are already on a multiple of 2048, we go up to the
+ next one. RD in this case will be -2048. */
+ value = value + 0x800;
+ value = value >> 11;
+ break;
+
+ case e_rdsel:
+ /* RD: Set bits 0-20 to one. */
+ value = value | -0x800;
+ break;
+
+ case e_lrsel:
+ case e_nlrsel:
+ /* LR: L with rounding of the addend to nearest 8k. */
+ value = sym_val + ((addend + 0x1000) & -0x2000);
+ value = value >> 11;
+ break;
+
+ case e_rrsel:
+ /* RR: R with rounding of the addend to nearest 8k.
+ We need to return a value such that 2048 * LR'x + RR'x == x
+ ie. RR'x = s+a - (s + (((a + 0x1000) & -0x2000) & -0x800))
+ . = s+a - ((s & -0x800) + ((a + 0x1000) & -0x2000))
+ . = (s & 0x7ff) + a - ((a + 0x1000) & -0x2000) */
+ value = (sym_val & 0x7ff) + (((addend & 0x1fff) ^ 0x1000) - 0x1000);
+ break;
+
+ default:
+ abort ();
+ }
+ return value;
+}
+
+/* PA-RISC OPCODES */
+#define get_opcode(insn) (((insn) >> 26) & 0x3f)
+
+enum hppa_opcode_type
+{
+ /* None of the opcodes in the first group generate relocs, so we
+ aren't too concerned about them. */
+ OP_SYSOP = 0x00,
+ OP_MEMMNG = 0x01,
+ OP_ALU = 0x02,
+ OP_NDXMEM = 0x03,
+ OP_SPOP = 0x04,
+ OP_DIAG = 0x05,
+ OP_FMPYADD = 0x06,
+ OP_UNDEF07 = 0x07,
+ OP_COPRW = 0x09,
+ OP_COPRDW = 0x0b,
+ OP_COPR = 0x0c,
+ OP_FLOAT = 0x0e,
+ OP_PRDSPEC = 0x0f,
+ OP_UNDEF15 = 0x15,
+ OP_UNDEF1d = 0x1d,
+ OP_FMPYSUB = 0x26,
+ OP_FPFUSED = 0x2e,
+ OP_SHEXDP0 = 0x34,
+ OP_SHEXDP1 = 0x35,
+ OP_SHEXDP2 = 0x36,
+ OP_UNDEF37 = 0x37,
+ OP_SHEXDP3 = 0x3c,
+ OP_SHEXDP4 = 0x3d,
+ OP_MULTMED = 0x3e,
+ OP_UNDEF3f = 0x3f,
+
+ OP_LDIL = 0x08,
+ OP_ADDIL = 0x0a,
+
+ OP_LDO = 0x0d,
+ OP_LDB = 0x10,
+ OP_LDH = 0x11,
+ OP_LDW = 0x12,
+ OP_LDWM = 0x13,
+ OP_STB = 0x18,
+ OP_STH = 0x19,
+ OP_STW = 0x1a,
+ OP_STWM = 0x1b,
+
+ OP_LDD = 0x14,
+ OP_STD = 0x1c,
+
+ OP_FLDW = 0x16,
+ OP_LDWL = 0x17,
+ OP_FSTW = 0x1e,
+ OP_STWL = 0x1f,
+
+ OP_COMBT = 0x20,
+ OP_COMIBT = 0x21,
+ OP_COMBF = 0x22,
+ OP_COMIBF = 0x23,
+ OP_CMPBDT = 0x27,
+ OP_ADDBT = 0x28,
+ OP_ADDIBT = 0x29,
+ OP_ADDBF = 0x2a,
+ OP_ADDIBF = 0x2b,
+ OP_CMPBDF = 0x2f,
+ OP_BVB = 0x30,
+ OP_BB = 0x31,
+ OP_MOVB = 0x32,
+ OP_MOVIB = 0x33,
+ OP_CMPIBD = 0x3b,
+
+ OP_COMICLR = 0x24,
+ OP_SUBI = 0x25,
+ OP_ADDIT = 0x2c,
+ OP_ADDI = 0x2d,
+
+ OP_BE = 0x38,
+ OP_BLE = 0x39,
+ OP_BL = 0x3a
+};
+
+
+/* Insert VALUE into INSN using R_FORMAT to determine exactly what
+ bits to change. */
+
+static inline int
+hppa_rebuild_insn (int insn, int value, int r_format)
+{
+ switch (r_format)
+ {
+ case 11:
+ return (insn & ~ 0x7ff) | low_sign_unext (value, 11);
+
+ case 12:
+ return (insn & ~ 0x1ffd) | re_assemble_12 (value);
+
+
+ case 10:
+ return (insn & ~ 0x3ff1) | re_assemble_14 (value & -8);
+
+ case -11:
+ return (insn & ~ 0x3ff9) | re_assemble_14 (value & -4);
+
+ case 14:
+ return (insn & ~ 0x3fff) | re_assemble_14 (value);
+
+
+ case -10:
+ return (insn & ~ 0xfff1) | re_assemble_16 (value & -8);
+
+ case -16:
+ return (insn & ~ 0xfff9) | re_assemble_16 (value & -4);
+
+ case 16:
+ return (insn & ~ 0xffff) | re_assemble_16 (value);
+
+
+ case 17:
+ return (insn & ~ 0x1f1ffd) | re_assemble_17 (value);
+
+ case 21:
+ return (insn & ~ 0x1fffff) | re_assemble_21 (value);
+
+ case 22:
+ return (insn & ~ 0x3ff1ffd) | re_assemble_22 (value);
+
+ case 32:
+ return value;
+
+ default:
+ abort ();
+ }
+ return insn;
+}
+
+#endif /* _LIBHPPA_H */
+/* Table of opcodes for the PA-RISC.
+ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000,
+ 2001, 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+ Contributed by the Center for Software Science at the
+ University of Utah (pa-gdb-bugs@cs.utah.edu).
+
+This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler.
+
+GAS/GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS/GDB 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS or GDB; see the file COPYING.
+If not, see <http://www.gnu.org/licenses/>. */
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+/*
+ * Structure of an opcode table entry.
+ */
+
+/* There are two kinds of delay slot nullification: normal which is
+ * controled by the nullification bit, and conditional, which depends
+ * on the direction of the branch and its success or failure.
+ *
+ * NONE is unfortunately #defined in the hiux system include files.
+ * #undef it away.
+ */
+#undef NONE
+struct pa_opcode
+{
+ const char *name;
+ unsigned long int match; /* Bits that must be set... */
+ unsigned long int mask; /* ... in these bits. */
+ const char *args;
+ enum pa_arch arch;
+ char flags;
+};
+
+/* Enables strict matching. Opcodes with match errors are skipped
+ when this bit is set. */
+#define FLAG_STRICT 0x1
+
+/*
+ All hppa opcodes are 32 bits.
+
+ The match component is a mask saying which bits must match a
+ particular opcode in order for an instruction to be an instance
+ of that opcode.
+
+ The args component is a string containing one character for each operand of
+ the instruction. Characters used as a prefix allow any second character to
+ be used without conflicting with the main operand characters.
+
+ Bit positions in this description follow HP usage of lsb = 31,
+ "at" is lsb of field.
+
+ In the args field, the following characters must match exactly:
+
+ '+,() '
+
+ In the args field, the following characters are unused:
+
+ ' " - / 34 6789:; '
+ '@ C M [\] '
+ '` e g } '
+
+ Here are all the characters:
+
+ ' !"#$%&'()*+-,./0123456789:;<=>?'
+ '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_'
+ '`abcdefghijklmnopqrstuvwxyz{|}~ '
+
+Kinds of operands:
+ x integer register field at 15.
+ b integer register field at 10.
+ t integer register field at 31.
+ a integer register field at 10 and 15 (for PERMH)
+ 5 5 bit immediate at 15.
+ s 2 bit space specifier at 17.
+ S 3 bit space specifier at 18.
+ V 5 bit immediate value at 31
+ i 11 bit immediate value at 31
+ j 14 bit immediate value at 31
+ k 21 bit immediate value at 31
+ l 16 bit immediate value at 31 (wide mode only, unusual encoding).
+ n nullification for branch instructions
+ N nullification for spop and copr instructions
+ w 12 bit branch displacement
+ W 17 bit branch displacement (PC relative)
+ X 22 bit branch displacement (PC relative)
+ z 17 bit branch displacement (just a number, not an address)
+
+Also these:
+
+ . 2 bit shift amount at 25
+ * 4 bit shift amount at 25
+ p 5 bit shift count at 26 (to support the SHD instruction) encoded as
+ 31-p
+ ~ 6 bit shift count at 20,22:26 encoded as 63-~.
+ P 5 bit bit position at 26
+ q 6 bit bit position at 20,22:26
+ T 5 bit field length at 31 (encoded as 32-T)
+ % 6 bit field length at 23,27:31 (variable extract/deposit)
+ | 6 bit field length at 19,27:31 (fixed extract/deposit)
+ A 13 bit immediate at 18 (to support the BREAK instruction)
+ ^ like b, but describes a control register
+ ! sar (cr11) register
+ D 26 bit immediate at 31 (to support the DIAG instruction)
+ $ 9 bit immediate at 28 (to support POPBTS)
+
+ v 3 bit Special Function Unit identifier at 25
+ O 20 bit Special Function Unit operation split between 15 bits at 20
+ and 5 bits at 31
+ o 15 bit Special Function Unit operation at 20
+ 2 22 bit Special Function Unit operation split between 17 bits at 20
+ and 5 bits at 31
+ 1 15 bit Special Function Unit operation split between 10 bits at 20
+ and 5 bits at 31
+ 0 10 bit Special Function Unit operation split between 5 bits at 20
+ and 5 bits at 31
+ u 3 bit coprocessor unit identifier at 25
+ F Source Floating Point Operand Format Completer encoded 2 bits at 20
+ I Source Floating Point Operand Format Completer encoded 1 bits at 20
+ (for 0xe format FP instructions)
+ G Destination Floating Point Operand Format Completer encoded 2 bits at 18
+ H Floating Point Operand Format at 26 for 'fmpyadd' and 'fmpysub'
+ (very similar to 'F')
+
+ r 5 bit immediate value at 31 (for the break instruction)
+ (very similar to V above, except the value is unsigned instead of
+ low_sign_ext)
+ R 5 bit immediate value at 15 (for the ssm, rsm, probei instructions)
+ (same as r above, except the value is in a different location)
+ U 10 bit immediate value at 15 (for SSM, RSM on pa2.0)
+ Q 5 bit immediate value at 10 (a bit position specified in
+ the bb instruction. It's the same as r above, except the
+ value is in a different location)
+ B 5 bit immediate value at 10 (a bit position specified in
+ the bb instruction. Similar to Q, but 64 bit handling is
+ different.
+ Z %r1 -- implicit target of addil instruction.
+ L ,%r2 completer for new syntax branch
+ { Source format completer for fcnv
+ _ Destination format completer for fcnv
+ h cbit for fcmp
+ = gfx tests for ftest
+ d 14 bit offset for single precision FP long load/store.
+ # 14 bit offset for double precision FP load long/store.
+ J Yet another 14 bit offset for load/store with ma,mb completers.
+ K Yet another 14 bit offset for load/store with ma,mb completers.
+ y 16 bit offset for word aligned load/store (PA2.0 wide).
+ & 16 bit offset for dword aligned load/store (PA2.0 wide).
+ < 16 bit offset for load/store with ma,mb completers (PA2.0 wide).
+ > 16 bit offset for load/store with ma,mb completers (PA2.0 wide).
+ Y %sr0,%r31 -- implicit target of be,l instruction.
+ @ implicit immediate value of 0
+
+Completer operands all have 'c' as the prefix:
+
+ cx indexed load and store completer.
+ cX indexed load and store completer. Like cx, but emits a space
+ after in disassembler.
+ cm short load and store completer.
+ cM short load and store completer. Like cm, but emits a space
+ after in disassembler.
+ cq long load and store completer (like cm, but inserted into a
+ different location in the target instruction).
+ cs store bytes short completer.
+ cA store bytes short completer. Like cs, but emits a space
+ after in disassembler.
+ ce long load/store completer for LDW/STW with a different encoding
+ than the others
+ cc load cache control hint
+ cd load and clear cache control hint
+ cC store cache control hint
+ co ordered access
+
+ cp branch link and push completer
+ cP branch pop completer
+ cl branch link completer
+ cg branch gate completer
+
+ cw read/write completer for PROBE
+ cW wide completer for MFCTL
+ cL local processor completer for cache control
+ cZ System Control Completer (to support LPA, LHA, etc.)
+
+ ci correction completer for DCOR
+ ca add completer
+ cy 32 bit add carry completer
+ cY 64 bit add carry completer
+ cv signed overflow trap completer
+ ct trap on condition completer for ADDI, SUB
+ cT trap on condition completer for UADDCM
+ cb 32 bit borrow completer for SUB
+ cB 64 bit borrow completer for SUB
+
+ ch left/right half completer
+ cH signed/unsigned saturation completer
+ cS signed/unsigned completer at 21
+ cz zero/sign extension completer.
+ c* permutation completer
+
+Condition operands all have '?' as the prefix:
+
+ ?f Floating point compare conditions (encoded as 5 bits at 31)
+
+ ?a add conditions
+ ?A 64 bit add conditions
+ ?@ add branch conditions followed by nullify
+ ?d non-negated add branch conditions
+ ?D negated add branch conditions
+ ?w wide mode non-negated add branch conditions
+ ?W wide mode negated add branch conditions
+
+ ?s compare/subtract conditions
+ ?S 64 bit compare/subtract conditions
+ ?t non-negated compare and branch conditions
+ ?n 32 bit compare and branch conditions followed by nullify
+ ?N 64 bit compare and branch conditions followed by nullify
+ ?Q 64 bit compare and branch conditions for CMPIB instruction
+
+ ?l logical conditions
+ ?L 64 bit logical conditions
+
+ ?b branch on bit conditions
+ ?B 64 bit branch on bit conditions
+
+ ?x shift/extract/deposit conditions
+ ?X 64 bit shift/extract/deposit conditions
+ ?y shift/extract/deposit conditions followed by nullify for conditional
+ branches
+
+ ?u unit conditions
+ ?U 64 bit unit conditions
+
+Floating point registers all have 'f' as a prefix:
+
+ ft target register at 31
+ fT target register with L/R halves at 31
+ fa operand 1 register at 10
+ fA operand 1 register with L/R halves at 10
+ fX Same as fA, except prints a space before register during disasm
+ fb operand 2 register at 15
+ fB operand 2 register with L/R halves at 15
+ fC operand 3 register with L/R halves at 16:18,21:23
+ fe Like fT, but encoding is different.
+ fE Same as fe, except prints a space before register during disasm.
+ fx target register at 15 (only for PA 2.0 long format FLDD/FSTD).
+
+Float registers for fmpyadd and fmpysub:
+
+ fi mult operand 1 register at 10
+ fj mult operand 2 register at 15
+ fk mult target register at 20
+ fl add/sub operand register at 25
+ fm add/sub target register at 31
+
+*/
+
+
+#if 0
+/* List of characters not to put a space after. Note that
+ "," is included, as the "spopN" operations use literal
+ commas in their completer sections. */
+static const char *const completer_chars = ",CcY<>?!@+&U~FfGHINnOoZMadu|/=0123%e$m}";
+#endif
+
+/* The order of the opcodes in this table is significant:
+
+ * The assembler requires that all instances of the same mnemonic be
+ consecutive. If they aren't, the assembler will bomb at runtime.
+
+ * Immediate fields use pa_get_absolute_expression to parse the
+ string. It will generate a "bad expression" error if passed
+ a register name. Thus, register index variants of an opcode
+ need to precede immediate variants.
+
+ * The disassembler does not care about the order of the opcodes
+ except in cases where implicit addressing is used.
+
+ Here are the rules for ordering the opcodes of a mnemonic:
+
+ 1) Opcodes with FLAG_STRICT should precede opcodes without
+ FLAG_STRICT.
+
+ 2) Opcodes with FLAG_STRICT should be ordered as follows:
+ register index opcodes, short immediate opcodes, and finally
+ long immediate opcodes. When both pa10 and pa11 variants
+ of the same opcode are available, the pa10 opcode should
+ come first for correct architectural promotion.
+
+ 3) When implicit addressing is available for an opcode, the
+ implicit opcode should precede the explicit opcode.
+
+ 4) Opcodes without FLAG_STRICT should be ordered as follows:
+ register index opcodes, long immediate opcodes, and finally
+ short immediate opcodes. */
+
+static const struct pa_opcode pa_opcodes[] =
+{
+
+/* Pseudo-instructions. */
+
+{ "ldi", 0x34000000, 0xffe00000, "l,x", pa20w, 0},/* ldo val(r0),r */
+{ "ldi", 0x34000000, 0xffe0c000, "j,x", pa10, 0},/* ldo val(r0),r */
+
+{ "cmpib", 0xec000000, 0xfc000000, "?Qn5,b,w", pa20, FLAG_STRICT},
+{ "cmpib", 0x84000000, 0xf4000000, "?nn5,b,w", pa10, FLAG_STRICT},
+{ "comib", 0x84000000, 0xfc000000, "?nn5,b,w", pa10, 0}, /* comib{tf}*/
+/* This entry is for the disassembler only. It will never be used by
+ assembler. */
+{ "comib", 0x8c000000, 0xfc000000, "?nn5,b,w", pa10, 0}, /* comib{tf}*/
+{ "cmpb", 0x9c000000, 0xdc000000, "?Nnx,b,w", pa20, FLAG_STRICT},
+{ "cmpb", 0x80000000, 0xf4000000, "?nnx,b,w", pa10, FLAG_STRICT},
+{ "comb", 0x80000000, 0xfc000000, "?nnx,b,w", pa10, 0}, /* comb{tf} */
+/* This entry is for the disassembler only. It will never be used by
+ assembler. */
+{ "comb", 0x88000000, 0xfc000000, "?nnx,b,w", pa10, 0}, /* comb{tf} */
+{ "addb", 0xa0000000, 0xf4000000, "?Wnx,b,w", pa20w, FLAG_STRICT},
+{ "addb", 0xa0000000, 0xfc000000, "?@nx,b,w", pa10, 0}, /* addb{tf} */
+/* This entry is for the disassembler only. It will never be used by
+ assembler. */
+{ "addb", 0xa8000000, 0xfc000000, "?@nx,b,w", pa10, 0},
+{ "addib", 0xa4000000, 0xf4000000, "?Wn5,b,w", pa20w, FLAG_STRICT},
+{ "addib", 0xa4000000, 0xfc000000, "?@n5,b,w", pa10, 0}, /* addib{tf}*/
+/* This entry is for the disassembler only. It will never be used by
+ assembler. */
+{ "addib", 0xac000000, 0xfc000000, "?@n5,b,w", pa10, 0}, /* addib{tf}*/
+{ "nop", 0x08000240, 0xffffffff, "", pa10, 0}, /* or 0,0,0 */
+{ "copy", 0x08000240, 0xffe0ffe0, "x,t", pa10, 0}, /* or r,0,t */
+{ "mtsar", 0x01601840, 0xffe0ffff, "x", pa10, 0}, /* mtctl r,cr11 */
+
+/* Loads and Stores for integer registers. */
+
+{ "ldd", 0x0c0000c0, 0xfc00d3c0, "cxccx(b),t", pa20, FLAG_STRICT},
+{ "ldd", 0x0c0000c0, 0xfc0013c0, "cxccx(s,b),t", pa20, FLAG_STRICT},
+{ "ldd", 0x0c0010e0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT},
+{ "ldd", 0x0c0010e0, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT},
+{ "ldd", 0x0c0010c0, 0xfc00d3c0, "cmcc5(b),t", pa20, FLAG_STRICT},
+{ "ldd", 0x0c0010c0, 0xfc0013c0, "cmcc5(s,b),t", pa20, FLAG_STRICT},
+{ "ldd", 0x50000000, 0xfc000002, "cq&(b),x", pa20w, FLAG_STRICT},
+{ "ldd", 0x50000000, 0xfc00c002, "cq#(b),x", pa20, FLAG_STRICT},
+{ "ldd", 0x50000000, 0xfc000002, "cq#(s,b),x", pa20, FLAG_STRICT},
+{ "ldw", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldw", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldw", 0x0c000080, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldw", 0x0c000080, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT},
+{ "ldw", 0x0c0010a0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT},
+{ "ldw", 0x0c0010a0, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT},
+{ "ldw", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldw", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldw", 0x0c001080, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldw", 0x0c001080, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "ldw", 0x4c000000, 0xfc000000, "ce<(b),x", pa20w, FLAG_STRICT},
+{ "ldw", 0x5c000004, 0xfc000006, "ce>(b),x", pa20w, FLAG_STRICT},
+{ "ldw", 0x48000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT},
+{ "ldw", 0x5c000004, 0xfc00c006, "ceK(b),x", pa20, FLAG_STRICT},
+{ "ldw", 0x5c000004, 0xfc000006, "ceK(s,b),x", pa20, FLAG_STRICT},
+{ "ldw", 0x4c000000, 0xfc00c000, "ceJ(b),x", pa10, FLAG_STRICT},
+{ "ldw", 0x4c000000, 0xfc000000, "ceJ(s,b),x", pa10, FLAG_STRICT},
+{ "ldw", 0x48000000, 0xfc00c000, "j(b),x", pa10, 0},
+{ "ldw", 0x48000000, 0xfc000000, "j(s,b),x", pa10, 0},
+{ "ldh", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldh", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldh", 0x0c000040, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldh", 0x0c000040, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT},
+{ "ldh", 0x0c001060, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT},
+{ "ldh", 0x0c001060, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT},
+{ "ldh", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldh", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldh", 0x0c001040, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldh", 0x0c001040, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "ldh", 0x44000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT},
+{ "ldh", 0x44000000, 0xfc00c000, "j(b),x", pa10, 0},
+{ "ldh", 0x44000000, 0xfc000000, "j(s,b),x", pa10, 0},
+{ "ldb", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldb", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldb", 0x0c000000, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldb", 0x0c000000, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT},
+{ "ldb", 0x0c001020, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT},
+{ "ldb", 0x0c001020, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT},
+{ "ldb", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldb", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldb", 0x0c001000, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldb", 0x0c001000, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "ldb", 0x40000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT},
+{ "ldb", 0x40000000, 0xfc00c000, "j(b),x", pa10, 0},
+{ "ldb", 0x40000000, 0xfc000000, "j(s,b),x", pa10, 0},
+{ "std", 0x0c0012e0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT},
+{ "std", 0x0c0012e0, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT},
+{ "std", 0x0c0012c0, 0xfc00d3c0, "cmcCx,V(b)", pa20, FLAG_STRICT},
+{ "std", 0x0c0012c0, 0xfc0013c0, "cmcCx,V(s,b)", pa20, FLAG_STRICT},
+{ "std", 0x70000000, 0xfc000002, "cqx,&(b)", pa20w, FLAG_STRICT},
+{ "std", 0x70000000, 0xfc00c002, "cqx,#(b)", pa20, FLAG_STRICT},
+{ "std", 0x70000000, 0xfc000002, "cqx,#(s,b)", pa20, FLAG_STRICT},
+{ "stw", 0x0c0012a0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT},
+{ "stw", 0x0c0012a0, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT},
+{ "stw", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "stw", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT},
+{ "stw", 0x0c001280, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "stw", 0x0c001280, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT},
+{ "stw", 0x6c000000, 0xfc000000, "cex,<(b)", pa20w, FLAG_STRICT},
+{ "stw", 0x7c000004, 0xfc000006, "cex,>(b)", pa20w, FLAG_STRICT},
+{ "stw", 0x68000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT},
+{ "stw", 0x7c000004, 0xfc00c006, "cex,K(b)", pa20, FLAG_STRICT},
+{ "stw", 0x7c000004, 0xfc000006, "cex,K(s,b)", pa20, FLAG_STRICT},
+{ "stw", 0x6c000000, 0xfc00c000, "cex,J(b)", pa10, FLAG_STRICT},
+{ "stw", 0x6c000000, 0xfc000000, "cex,J(s,b)", pa10, FLAG_STRICT},
+{ "stw", 0x68000000, 0xfc00c000, "x,j(b)", pa10, 0},
+{ "stw", 0x68000000, 0xfc000000, "x,j(s,b)", pa10, 0},
+{ "sth", 0x0c001260, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT},
+{ "sth", 0x0c001260, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT},
+{ "sth", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "sth", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT},
+{ "sth", 0x0c001240, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "sth", 0x0c001240, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT},
+{ "sth", 0x64000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT},
+{ "sth", 0x64000000, 0xfc00c000, "x,j(b)", pa10, 0},
+{ "sth", 0x64000000, 0xfc000000, "x,j(s,b)", pa10, 0},
+{ "stb", 0x0c001220, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT},
+{ "stb", 0x0c001220, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT},
+{ "stb", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "stb", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT},
+{ "stb", 0x0c001200, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "stb", 0x0c001200, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT},
+{ "stb", 0x60000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT},
+{ "stb", 0x60000000, 0xfc00c000, "x,j(b)", pa10, 0},
+{ "stb", 0x60000000, 0xfc000000, "x,j(s,b)", pa10, 0},
+{ "ldwm", 0x4c000000, 0xfc00c000, "j(b),x", pa10, 0},
+{ "ldwm", 0x4c000000, 0xfc000000, "j(s,b),x", pa10, 0},
+{ "stwm", 0x6c000000, 0xfc00c000, "x,j(b)", pa10, 0},
+{ "stwm", 0x6c000000, 0xfc000000, "x,j(s,b)", pa10, 0},
+{ "ldwx", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldwx", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldwx", 0x0c000080, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldwx", 0x0c000080, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT},
+{ "ldwx", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, 0},
+{ "ldwx", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, 0},
+{ "ldhx", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldhx", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldhx", 0x0c000040, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldhx", 0x0c000040, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT},
+{ "ldhx", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, 0},
+{ "ldhx", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, 0},
+{ "ldbx", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldbx", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldbx", 0x0c000000, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldbx", 0x0c000000, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT},
+{ "ldbx", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, 0},
+{ "ldbx", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, 0},
+{ "ldwa", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldwa", 0x0c000180, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldwa", 0x0c0011a0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT},
+{ "ldwa", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldwa", 0x0c001180, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldcw", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldcw", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldcw", 0x0c0001c0, 0xfc00d3c0, "cxcdx(b),t", pa11, FLAG_STRICT},
+{ "ldcw", 0x0c0001c0, 0xfc0013c0, "cxcdx(s,b),t", pa11, FLAG_STRICT},
+{ "ldcw", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldcw", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldcw", 0x0c0011c0, 0xfc00d3c0, "cmcd5(b),t", pa11, FLAG_STRICT},
+{ "ldcw", 0x0c0011c0, 0xfc0013c0, "cmcd5(s,b),t", pa11, FLAG_STRICT},
+{ "stwa", 0x0c0013a0, 0xfc00d3ff, "cocCx,@(b)", pa20, FLAG_STRICT},
+{ "stwa", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "stwa", 0x0c001380, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "stby", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, FLAG_STRICT},
+{ "stby", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, FLAG_STRICT},
+{ "stby", 0x0c001300, 0xfc00d3c0, "cscCx,V(b)", pa11, FLAG_STRICT},
+{ "stby", 0x0c001300, 0xfc0013c0, "cscCx,V(s,b)", pa11, FLAG_STRICT},
+{ "ldda", 0x0c000100, 0xfc00d3c0, "cxccx(b),t", pa20, FLAG_STRICT},
+{ "ldda", 0x0c001120, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT},
+{ "ldda", 0x0c001100, 0xfc00d3c0, "cmcc5(b),t", pa20, FLAG_STRICT},
+{ "ldcd", 0x0c000140, 0xfc00d3c0, "cxcdx(b),t", pa20, FLAG_STRICT},
+{ "ldcd", 0x0c000140, 0xfc0013c0, "cxcdx(s,b),t", pa20, FLAG_STRICT},
+{ "ldcd", 0x0c001140, 0xfc00d3c0, "cmcd5(b),t", pa20, FLAG_STRICT},
+{ "ldcd", 0x0c001140, 0xfc0013c0, "cmcd5(s,b),t", pa20, FLAG_STRICT},
+{ "stda", 0x0c0013e0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT},
+{ "stda", 0x0c0013c0, 0xfc00d3c0, "cmcCx,V(b)", pa20, FLAG_STRICT},
+{ "ldwax", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldwax", 0x0c000180, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT},
+{ "ldwax", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, 0},
+{ "ldcwx", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT},
+{ "ldcwx", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT},
+{ "ldcwx", 0x0c0001c0, 0xfc00d3c0, "cxcdx(b),t", pa11, FLAG_STRICT},
+{ "ldcwx", 0x0c0001c0, 0xfc0013c0, "cxcdx(s,b),t", pa11, FLAG_STRICT},
+{ "ldcwx", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, 0},
+{ "ldcwx", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, 0},
+{ "ldws", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldws", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldws", 0x0c001080, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldws", 0x0c001080, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "ldws", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, 0},
+{ "ldws", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, 0},
+{ "ldhs", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldhs", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldhs", 0x0c001040, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldhs", 0x0c001040, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "ldhs", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, 0},
+{ "ldhs", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, 0},
+{ "ldbs", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldbs", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldbs", 0x0c001000, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldbs", 0x0c001000, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "ldbs", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, 0},
+{ "ldbs", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, 0},
+{ "ldwas", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldwas", 0x0c001180, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT},
+{ "ldwas", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, 0},
+{ "ldcws", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT},
+{ "ldcws", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT},
+{ "ldcws", 0x0c0011c0, 0xfc00d3c0, "cmcd5(b),t", pa11, FLAG_STRICT},
+{ "ldcws", 0x0c0011c0, 0xfc0013c0, "cmcd5(s,b),t", pa11, FLAG_STRICT},
+{ "ldcws", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, 0},
+{ "ldcws", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, 0},
+{ "stws", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "stws", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT},
+{ "stws", 0x0c001280, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "stws", 0x0c001280, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT},
+{ "stws", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, 0},
+{ "stws", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, 0},
+{ "sths", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "sths", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT},
+{ "sths", 0x0c001240, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "sths", 0x0c001240, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT},
+{ "sths", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, 0},
+{ "sths", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, 0},
+{ "stbs", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "stbs", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT},
+{ "stbs", 0x0c001200, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "stbs", 0x0c001200, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT},
+{ "stbs", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, 0},
+{ "stbs", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, 0},
+{ "stwas", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT},
+{ "stwas", 0x0c001380, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT},
+{ "stwas", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, 0},
+{ "stdby", 0x0c001340, 0xfc00d3c0, "cscCx,V(b)", pa20, FLAG_STRICT},
+{ "stdby", 0x0c001340, 0xfc0013c0, "cscCx,V(s,b)", pa20, FLAG_STRICT},
+{ "stbys", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, FLAG_STRICT},
+{ "stbys", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, FLAG_STRICT},
+{ "stbys", 0x0c001300, 0xfc00d3c0, "cscCx,V(b)", pa11, FLAG_STRICT},
+{ "stbys", 0x0c001300, 0xfc0013c0, "cscCx,V(s,b)", pa11, FLAG_STRICT},
+{ "stbys", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, 0},
+{ "stbys", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, 0},
+
+/* Immediate instructions. */
+{ "ldo", 0x34000000, 0xfc000000, "l(b),x", pa20w, 0},
+{ "ldo", 0x34000000, 0xfc00c000, "j(b),x", pa10, 0},
+{ "ldil", 0x20000000, 0xfc000000, "k,b", pa10, 0},
+{ "addil", 0x28000000, 0xfc000000, "k,b,Z", pa10, 0},
+{ "addil", 0x28000000, 0xfc000000, "k,b", pa10, 0},
+
+/* Branching instructions. */
+{ "b", 0xe8008000, 0xfc00e000, "cpnXL", pa20, FLAG_STRICT},
+{ "b", 0xe800a000, 0xfc00e000, "clnXL", pa20, FLAG_STRICT},
+{ "b", 0xe8000000, 0xfc00e000, "clnW,b", pa10, FLAG_STRICT},
+{ "b", 0xe8002000, 0xfc00e000, "cgnW,b", pa10, FLAG_STRICT},
+{ "b", 0xe8000000, 0xffe0e000, "nW", pa10, 0}, /* b,l foo,r0 */
+{ "bl", 0xe8000000, 0xfc00e000, "nW,b", pa10, 0},
+{ "gate", 0xe8002000, 0xfc00e000, "nW,b", pa10, 0},
+{ "blr", 0xe8004000, 0xfc00e001, "nx,b", pa10, 0},
+{ "bv", 0xe800c000, 0xfc00fffd, "nx(b)", pa10, 0},
+{ "bv", 0xe800c000, 0xfc00fffd, "n(b)", pa10, 0},
+{ "bve", 0xe800f001, 0xfc1ffffd, "cpn(b)L", pa20, FLAG_STRICT},
+{ "bve", 0xe800f000, 0xfc1ffffd, "cln(b)L", pa20, FLAG_STRICT},
+{ "bve", 0xe800d001, 0xfc1ffffd, "cPn(b)", pa20, FLAG_STRICT},
+{ "bve", 0xe800d000, 0xfc1ffffd, "n(b)", pa20, FLAG_STRICT},
+{ "be", 0xe4000000, 0xfc000000, "clnz(S,b),Y", pa10, FLAG_STRICT},
+{ "be", 0xe4000000, 0xfc000000, "clnz(b),Y", pa10, FLAG_STRICT},
+{ "be", 0xe0000000, 0xfc000000, "nz(S,b)", pa10, 0},
+{ "be", 0xe0000000, 0xfc000000, "nz(b)", pa10, 0},
+{ "ble", 0xe4000000, 0xfc000000, "nz(S,b)", pa10, 0},
+{ "movb", 0xc8000000, 0xfc000000, "?ynx,b,w", pa10, 0},
+{ "movib", 0xcc000000, 0xfc000000, "?yn5,b,w", pa10, 0},
+{ "combt", 0x80000000, 0xfc000000, "?tnx,b,w", pa10, 0},
+{ "combf", 0x88000000, 0xfc000000, "?tnx,b,w", pa10, 0},
+{ "comibt", 0x84000000, 0xfc000000, "?tn5,b,w", pa10, 0},
+{ "comibf", 0x8c000000, 0xfc000000, "?tn5,b,w", pa10, 0},
+{ "addbt", 0xa0000000, 0xfc000000, "?dnx,b,w", pa10, 0},
+{ "addbf", 0xa8000000, 0xfc000000, "?dnx,b,w", pa10, 0},
+{ "addibt", 0xa4000000, 0xfc000000, "?dn5,b,w", pa10, 0},
+{ "addibf", 0xac000000, 0xfc000000, "?dn5,b,w", pa10, 0},
+{ "bb", 0xc0004000, 0xffe06000, "?bnx,!,w", pa10, FLAG_STRICT},
+{ "bb", 0xc0006000, 0xffe06000, "?Bnx,!,w", pa20, FLAG_STRICT},
+{ "bb", 0xc4004000, 0xfc006000, "?bnx,Q,w", pa10, FLAG_STRICT},
+{ "bb", 0xc4004000, 0xfc004000, "?Bnx,B,w", pa20, FLAG_STRICT},
+{ "bvb", 0xc0004000, 0xffe04000, "?bnx,w", pa10, 0},
+{ "clrbts", 0xe8004005, 0xffffffff, "", pa20, FLAG_STRICT},
+{ "popbts", 0xe8004005, 0xfffff007, "$", pa20, FLAG_STRICT},
+{ "pushnom", 0xe8004001, 0xffffffff, "", pa20, FLAG_STRICT},
+{ "pushbts", 0xe8004001, 0xffe0ffff, "x", pa20, FLAG_STRICT},
+
+/* Computation Instructions. */
+
+{ "cmpclr", 0x080008a0, 0xfc000fe0, "?Sx,b,t", pa20, FLAG_STRICT},
+{ "cmpclr", 0x08000880, 0xfc000fe0, "?sx,b,t", pa10, FLAG_STRICT},
+{ "comclr", 0x08000880, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "or", 0x08000260, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT},
+{ "or", 0x08000240, 0xfc000fe0, "?lx,b,t", pa10, 0},
+{ "xor", 0x080002a0, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT},
+{ "xor", 0x08000280, 0xfc000fe0, "?lx,b,t", pa10, 0},
+{ "and", 0x08000220, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT},
+{ "and", 0x08000200, 0xfc000fe0, "?lx,b,t", pa10, 0},
+{ "andcm", 0x08000020, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT},
+{ "andcm", 0x08000000, 0xfc000fe0, "?lx,b,t", pa10, 0},
+{ "uxor", 0x080003a0, 0xfc000fe0, "?Ux,b,t", pa20, FLAG_STRICT},
+{ "uxor", 0x08000380, 0xfc000fe0, "?ux,b,t", pa10, 0},
+{ "uaddcm", 0x080009a0, 0xfc000fa0, "cT?Ux,b,t", pa20, FLAG_STRICT},
+{ "uaddcm", 0x08000980, 0xfc000fa0, "cT?ux,b,t", pa10, FLAG_STRICT},
+{ "uaddcm", 0x08000980, 0xfc000fe0, "?ux,b,t", pa10, 0},
+{ "uaddcmt", 0x080009c0, 0xfc000fe0, "?ux,b,t", pa10, 0},
+{ "dcor", 0x08000ba0, 0xfc1f0fa0, "ci?Ub,t", pa20, FLAG_STRICT},
+{ "dcor", 0x08000b80, 0xfc1f0fa0, "ci?ub,t", pa10, FLAG_STRICT},
+{ "dcor", 0x08000b80, 0xfc1f0fe0, "?ub,t", pa10, 0},
+{ "idcor", 0x08000bc0, 0xfc1f0fe0, "?ub,t", pa10, 0},
+{ "addi", 0xb0000000, 0xfc000000, "ct?ai,b,x", pa10, FLAG_STRICT},
+{ "addi", 0xb4000000, 0xfc000000, "cv?ai,b,x", pa10, FLAG_STRICT},
+{ "addi", 0xb4000000, 0xfc000800, "?ai,b,x", pa10, 0},
+{ "addio", 0xb4000800, 0xfc000800, "?ai,b,x", pa10, 0},
+{ "addit", 0xb0000000, 0xfc000800, "?ai,b,x", pa10, 0},
+{ "addito", 0xb0000800, 0xfc000800, "?ai,b,x", pa10, 0},
+{ "add", 0x08000720, 0xfc0007e0, "cY?Ax,b,t", pa20, FLAG_STRICT},
+{ "add", 0x08000700, 0xfc0007e0, "cy?ax,b,t", pa10, FLAG_STRICT},
+{ "add", 0x08000220, 0xfc0003e0, "ca?Ax,b,t", pa20, FLAG_STRICT},
+{ "add", 0x08000200, 0xfc0003e0, "ca?ax,b,t", pa10, FLAG_STRICT},
+{ "add", 0x08000600, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "addl", 0x08000a00, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "addo", 0x08000e00, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "addc", 0x08000700, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "addco", 0x08000f00, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sub", 0x080004e0, 0xfc0007e0, "ct?Sx,b,t", pa20, FLAG_STRICT},
+{ "sub", 0x080004c0, 0xfc0007e0, "ct?sx,b,t", pa10, FLAG_STRICT},
+{ "sub", 0x08000520, 0xfc0007e0, "cB?Sx,b,t", pa20, FLAG_STRICT},
+{ "sub", 0x08000500, 0xfc0007e0, "cb?sx,b,t", pa10, FLAG_STRICT},
+{ "sub", 0x08000420, 0xfc0007e0, "cv?Sx,b,t", pa20, FLAG_STRICT},
+{ "sub", 0x08000400, 0xfc0007e0, "cv?sx,b,t", pa10, FLAG_STRICT},
+{ "sub", 0x08000400, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "subo", 0x08000c00, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "subb", 0x08000500, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "subbo", 0x08000d00, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "subt", 0x080004c0, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "subto", 0x08000cc0, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "ds", 0x08000440, 0xfc000fe0, "?sx,b,t", pa10, 0},
+{ "subi", 0x94000000, 0xfc000000, "cv?si,b,x", pa10, FLAG_STRICT},
+{ "subi", 0x94000000, 0xfc000800, "?si,b,x", pa10, 0},
+{ "subio", 0x94000800, 0xfc000800, "?si,b,x", pa10, 0},
+{ "cmpiclr", 0x90000800, 0xfc000800, "?Si,b,x", pa20, FLAG_STRICT},
+{ "cmpiclr", 0x90000000, 0xfc000800, "?si,b,x", pa10, FLAG_STRICT},
+{ "comiclr", 0x90000000, 0xfc000800, "?si,b,x", pa10, 0},
+{ "shladd", 0x08000220, 0xfc000320, "ca?Ax,.,b,t", pa20, FLAG_STRICT},
+{ "shladd", 0x08000200, 0xfc000320, "ca?ax,.,b,t", pa10, FLAG_STRICT},
+{ "sh1add", 0x08000640, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh1addl", 0x08000a40, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh1addo", 0x08000e40, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh2add", 0x08000680, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh2addl", 0x08000a80, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh2addo", 0x08000e80, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh3add", 0x080006c0, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh3addl", 0x08000ac0, 0xfc000fe0, "?ax,b,t", pa10, 0},
+{ "sh3addo", 0x08000ec0, 0xfc000fe0, "?ax,b,t", pa10, 0},
+
+/* Subword Operation Instructions. */
+
+{ "hadd", 0x08000300, 0xfc00ff20, "cHx,b,t", pa20, FLAG_STRICT},
+{ "havg", 0x080002c0, 0xfc00ffe0, "x,b,t", pa20, FLAG_STRICT},
+{ "hshl", 0xf8008800, 0xffe0fc20, "x,*,t", pa20, FLAG_STRICT},
+{ "hshladd", 0x08000700, 0xfc00ff20, "x,.,b,t", pa20, FLAG_STRICT},
+{ "hshr", 0xf800c800, 0xfc1ff820, "cSb,*,t", pa20, FLAG_STRICT},
+{ "hshradd", 0x08000500, 0xfc00ff20, "x,.,b,t", pa20, FLAG_STRICT},
+{ "hsub", 0x08000100, 0xfc00ff20, "cHx,b,t", pa20, FLAG_STRICT},
+{ "mixh", 0xf8008400, 0xfc009fe0, "chx,b,t", pa20, FLAG_STRICT},
+{ "mixw", 0xf8008000, 0xfc009fe0, "chx,b,t", pa20, FLAG_STRICT},
+{ "permh", 0xf8000000, 0xfc009020, "c*a,t", pa20, FLAG_STRICT},
+
+
+/* Extract and Deposit Instructions. */
+
+{ "shrpd", 0xd0000200, 0xfc001fe0, "?Xx,b,!,t", pa20, FLAG_STRICT},
+{ "shrpd", 0xd0000400, 0xfc001400, "?Xx,b,~,t", pa20, FLAG_STRICT},
+{ "shrpw", 0xd0000000, 0xfc001fe0, "?xx,b,!,t", pa10, FLAG_STRICT},
+{ "shrpw", 0xd0000800, 0xfc001c00, "?xx,b,p,t", pa10, FLAG_STRICT},
+{ "vshd", 0xd0000000, 0xfc001fe0, "?xx,b,t", pa10, 0},
+{ "shd", 0xd0000800, 0xfc001c00, "?xx,b,p,t", pa10, 0},
+{ "extrd", 0xd0001200, 0xfc001ae0, "cS?Xb,!,%,x", pa20, FLAG_STRICT},
+{ "extrd", 0xd8000000, 0xfc000000, "cS?Xb,q,|,x", pa20, FLAG_STRICT},
+{ "extrw", 0xd0001000, 0xfc001be0, "cS?xb,!,T,x", pa10, FLAG_STRICT},
+{ "extrw", 0xd0001800, 0xfc001800, "cS?xb,P,T,x", pa10, FLAG_STRICT},
+{ "vextru", 0xd0001000, 0xfc001fe0, "?xb,T,x", pa10, 0},
+{ "vextrs", 0xd0001400, 0xfc001fe0, "?xb,T,x", pa10, 0},
+{ "extru", 0xd0001800, 0xfc001c00, "?xb,P,T,x", pa10, 0},
+{ "extrs", 0xd0001c00, 0xfc001c00, "?xb,P,T,x", pa10, 0},
+{ "depd", 0xd4000200, 0xfc001ae0, "cz?Xx,!,%,b", pa20, FLAG_STRICT},
+{ "depd", 0xf0000000, 0xfc000000, "cz?Xx,~,|,b", pa20, FLAG_STRICT},
+{ "depdi", 0xd4001200, 0xfc001ae0, "cz?X5,!,%,b", pa20, FLAG_STRICT},
+{ "depdi", 0xf4000000, 0xfc000000, "cz?X5,~,|,b", pa20, FLAG_STRICT},
+{ "depw", 0xd4000000, 0xfc001be0, "cz?xx,!,T,b", pa10, FLAG_STRICT},
+{ "depw", 0xd4000800, 0xfc001800, "cz?xx,p,T,b", pa10, FLAG_STRICT},
+{ "depwi", 0xd4001000, 0xfc001be0, "cz?x5,!,T,b", pa10, FLAG_STRICT},
+{ "depwi", 0xd4001800, 0xfc001800, "cz?x5,p,T,b", pa10, FLAG_STRICT},
+{ "zvdep", 0xd4000000, 0xfc001fe0, "?xx,T,b", pa10, 0},
+{ "vdep", 0xd4000400, 0xfc001fe0, "?xx,T,b", pa10, 0},
+{ "zdep", 0xd4000800, 0xfc001c00, "?xx,p,T,b", pa10, 0},
+{ "dep", 0xd4000c00, 0xfc001c00, "?xx,p,T,b", pa10, 0},
+{ "zvdepi", 0xd4001000, 0xfc001fe0, "?x5,T,b", pa10, 0},
+{ "vdepi", 0xd4001400, 0xfc001fe0, "?x5,T,b", pa10, 0},
+{ "zdepi", 0xd4001800, 0xfc001c00, "?x5,p,T,b", pa10, 0},
+{ "depi", 0xd4001c00, 0xfc001c00, "?x5,p,T,b", pa10, 0},
+
+/* System Control Instructions. */
+
+{ "break", 0x00000000, 0xfc001fe0, "r,A", pa10, 0},
+{ "rfi", 0x00000c00, 0xffffff1f, "cr", pa10, FLAG_STRICT},
+{ "rfi", 0x00000c00, 0xffffffff, "", pa10, 0},
+{ "rfir", 0x00000ca0, 0xffffffff, "", pa11, 0},
+{ "ssm", 0x00000d60, 0xfc00ffe0, "U,t", pa20, FLAG_STRICT},
+{ "ssm", 0x00000d60, 0xffe0ffe0, "R,t", pa10, 0},
+{ "rsm", 0x00000e60, 0xfc00ffe0, "U,t", pa20, FLAG_STRICT},
+{ "rsm", 0x00000e60, 0xffe0ffe0, "R,t", pa10, 0},
+{ "mtsm", 0x00001860, 0xffe0ffff, "x", pa10, 0},
+{ "ldsid", 0x000010a0, 0xfc1fffe0, "(b),t", pa10, 0},
+{ "ldsid", 0x000010a0, 0xfc1f3fe0, "(s,b),t", pa10, 0},
+{ "mtsp", 0x00001820, 0xffe01fff, "x,S", pa10, 0},
+{ "mtctl", 0x00001840, 0xfc00ffff, "x,^", pa10, 0},
+{ "mtsarcm", 0x016018C0, 0xffe0ffff, "x", pa20, FLAG_STRICT},
+{ "mfia", 0x000014A0, 0xffffffe0, "t", pa20, FLAG_STRICT},
+{ "mfsp", 0x000004a0, 0xffff1fe0, "S,t", pa10, 0},
+{ "mfctl", 0x016048a0, 0xffffffe0, "cW!,t", pa20, FLAG_STRICT},
+{ "mfctl", 0x000008a0, 0xfc1fffe0, "^,t", pa10, 0},
+{ "sync", 0x00000400, 0xffffffff, "", pa10, 0},
+{ "syncdma", 0x00100400, 0xffffffff, "", pa10, 0},
+{ "probe", 0x04001180, 0xfc00ffa0, "cw(b),x,t", pa10, FLAG_STRICT},
+{ "probe", 0x04001180, 0xfc003fa0, "cw(s,b),x,t", pa10, FLAG_STRICT},
+{ "probei", 0x04003180, 0xfc00ffa0, "cw(b),R,t", pa10, FLAG_STRICT},
+{ "probei", 0x04003180, 0xfc003fa0, "cw(s,b),R,t", pa10, FLAG_STRICT},
+{ "prober", 0x04001180, 0xfc00ffe0, "(b),x,t", pa10, 0},
+{ "prober", 0x04001180, 0xfc003fe0, "(s,b),x,t", pa10, 0},
+{ "proberi", 0x04003180, 0xfc00ffe0, "(b),R,t", pa10, 0},
+{ "proberi", 0x04003180, 0xfc003fe0, "(s,b),R,t", pa10, 0},
+{ "probew", 0x040011c0, 0xfc00ffe0, "(b),x,t", pa10, 0},
+{ "probew", 0x040011c0, 0xfc003fe0, "(s,b),x,t", pa10, 0},
+{ "probewi", 0x040031c0, 0xfc00ffe0, "(b),R,t", pa10, 0},
+{ "probewi", 0x040031c0, 0xfc003fe0, "(s,b),R,t", pa10, 0},
+{ "lpa", 0x04001340, 0xfc00ffc0, "cZx(b),t", pa10, 0},
+{ "lpa", 0x04001340, 0xfc003fc0, "cZx(s,b),t", pa10, 0},
+{ "lci", 0x04001300, 0xfc00ffe0, "x(b),t", pa11, 0},
+{ "lci", 0x04001300, 0xfc003fe0, "x(s,b),t", pa11, 0},
+{ "pdtlb", 0x04001600, 0xfc00ffdf, "cLcZx(b)", pa20, FLAG_STRICT},
+{ "pdtlb", 0x04001600, 0xfc003fdf, "cLcZx(s,b)", pa20, FLAG_STRICT},
+{ "pdtlb", 0x04001600, 0xfc1fffdf, "cLcZ@(b)", pa20, FLAG_STRICT},
+{ "pdtlb", 0x04001600, 0xfc1f3fdf, "cLcZ@(s,b)", pa20, FLAG_STRICT},
+{ "pdtlb", 0x04001200, 0xfc00ffdf, "cZx(b)", pa10, 0},
+{ "pdtlb", 0x04001200, 0xfc003fdf, "cZx(s,b)", pa10, 0},
+{ "pitlb", 0x04000600, 0xfc001fdf, "cLcZx(S,b)", pa20, FLAG_STRICT},
+{ "pitlb", 0x04000600, 0xfc1f1fdf, "cLcZ@(S,b)", pa20, FLAG_STRICT},
+{ "pitlb", 0x04000200, 0xfc001fdf, "cZx(S,b)", pa10, 0},
+{ "pdtlbe", 0x04001240, 0xfc00ffdf, "cZx(b)", pa10, 0},
+{ "pdtlbe", 0x04001240, 0xfc003fdf, "cZx(s,b)", pa10, 0},
+{ "pitlbe", 0x04000240, 0xfc001fdf, "cZx(S,b)", pa10, 0},
+{ "idtlba", 0x04001040, 0xfc00ffff, "x,(b)", pa10, 0},
+{ "idtlba", 0x04001040, 0xfc003fff, "x,(s,b)", pa10, 0},
+{ "iitlba", 0x04000040, 0xfc001fff, "x,(S,b)", pa10, 0},
+{ "idtlbp", 0x04001000, 0xfc00ffff, "x,(b)", pa10, 0},
+{ "idtlbp", 0x04001000, 0xfc003fff, "x,(s,b)", pa10, 0},
+{ "iitlbp", 0x04000000, 0xfc001fff, "x,(S,b)", pa10, 0},
+{ "pdc", 0x04001380, 0xfc00ffdf, "cZx(b)", pa10, 0},
+{ "pdc", 0x04001380, 0xfc003fdf, "cZx(s,b)", pa10, 0},
+{ "fdc", 0x04001280, 0xfc00ffdf, "cZx(b)", pa10, FLAG_STRICT},
+{ "fdc", 0x04001280, 0xfc003fdf, "cZx(s,b)", pa10, FLAG_STRICT},
+{ "fdc", 0x04003280, 0xfc00ffff, "5(b)", pa20, FLAG_STRICT},
+{ "fdc", 0x04003280, 0xfc003fff, "5(s,b)", pa20, FLAG_STRICT},
+{ "fdc", 0x04001280, 0xfc00ffdf, "cZx(b)", pa10, 0},
+{ "fdc", 0x04001280, 0xfc003fdf, "cZx(s,b)", pa10, 0},
+{ "fic", 0x040013c0, 0xfc00dfdf, "cZx(b)", pa20, FLAG_STRICT},
+{ "fic", 0x04000280, 0xfc001fdf, "cZx(S,b)", pa10, 0},
+{ "fdce", 0x040012c0, 0xfc00ffdf, "cZx(b)", pa10, 0},
+{ "fdce", 0x040012c0, 0xfc003fdf, "cZx(s,b)", pa10, 0},
+{ "fice", 0x040002c0, 0xfc001fdf, "cZx(S,b)", pa10, 0},
+{ "diag", 0x14000000, 0xfc000000, "D", pa10, 0},
+{ "idtlbt", 0x04001800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT},
+{ "iitlbt", 0x04000800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT},
+
+/* These may be specific to certain versions of the PA. Joel claimed
+ they were 72000 (7200?) specific. However, I'm almost certain the
+ mtcpu/mfcpu were undocumented, but available in the older 700 machines. */
+{ "mtcpu", 0x14001600, 0xfc00ffff, "x,^", pa10, 0},
+{ "mfcpu", 0x14001A00, 0xfc00ffff, "^,x", pa10, 0},
+{ "tocen", 0x14403600, 0xffffffff, "", pa10, 0},
+{ "tocdis", 0x14401620, 0xffffffff, "", pa10, 0},
+{ "shdwgr", 0x14402600, 0xffffffff, "", pa10, 0},
+{ "grshdw", 0x14400620, 0xffffffff, "", pa10, 0},
+
+/* gfw and gfr are not in the HP PA 1.1 manual, but they are in either
+ the Timex FPU or the Mustang ERS (not sure which) manual. */
+{ "gfw", 0x04001680, 0xfc00ffdf, "cZx(b)", pa11, 0},
+{ "gfw", 0x04001680, 0xfc003fdf, "cZx(s,b)", pa11, 0},
+{ "gfr", 0x04001a80, 0xfc00ffdf, "cZx(b)", pa11, 0},
+{ "gfr", 0x04001a80, 0xfc003fdf, "cZx(s,b)", pa11, 0},
+
+/* Floating Point Coprocessor Instructions. */
+
+{ "fldw", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, FLAG_STRICT},
+{ "fldw", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, FLAG_STRICT},
+{ "fldw", 0x24000000, 0xfc00d380, "cxccx(b),fT", pa11, FLAG_STRICT},
+{ "fldw", 0x24000000, 0xfc001380, "cxccx(s,b),fT", pa11, FLAG_STRICT},
+{ "fldw", 0x24001020, 0xfc1ff3a0, "cocc@(b),fT", pa20, FLAG_STRICT},
+{ "fldw", 0x24001020, 0xfc1f33a0, "cocc@(s,b),fT", pa20, FLAG_STRICT},
+{ "fldw", 0x24001000, 0xfc00df80, "cM5(b),fT", pa10, FLAG_STRICT},
+{ "fldw", 0x24001000, 0xfc001f80, "cM5(s,b),fT", pa10, FLAG_STRICT},
+{ "fldw", 0x24001000, 0xfc00d380, "cmcc5(b),fT", pa11, FLAG_STRICT},
+{ "fldw", 0x24001000, 0xfc001380, "cmcc5(s,b),fT", pa11, FLAG_STRICT},
+{ "fldw", 0x5c000000, 0xfc000004, "y(b),fe", pa20w, FLAG_STRICT},
+{ "fldw", 0x58000000, 0xfc000000, "cJy(b),fe", pa20w, FLAG_STRICT},
+{ "fldw", 0x5c000000, 0xfc00c004, "d(b),fe", pa20, FLAG_STRICT},
+{ "fldw", 0x5c000000, 0xfc000004, "d(s,b),fe", pa20, FLAG_STRICT},
+{ "fldw", 0x58000000, 0xfc00c000, "cJd(b),fe", pa20, FLAG_STRICT},
+{ "fldw", 0x58000000, 0xfc000000, "cJd(s,b),fe", pa20, FLAG_STRICT},
+{ "fldd", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, FLAG_STRICT},
+{ "fldd", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, FLAG_STRICT},
+{ "fldd", 0x2c000000, 0xfc00d3c0, "cxccx(b),ft", pa11, FLAG_STRICT},
+{ "fldd", 0x2c000000, 0xfc0013c0, "cxccx(s,b),ft", pa11, FLAG_STRICT},
+{ "fldd", 0x2c001020, 0xfc1ff3e0, "cocc@(b),ft", pa20, FLAG_STRICT},
+{ "fldd", 0x2c001020, 0xfc1f33e0, "cocc@(s,b),ft", pa20, FLAG_STRICT},
+{ "fldd", 0x2c001000, 0xfc00dfc0, "cM5(b),ft", pa10, FLAG_STRICT},
+{ "fldd", 0x2c001000, 0xfc001fc0, "cM5(s,b),ft", pa10, FLAG_STRICT},
+{ "fldd", 0x2c001000, 0xfc00d3c0, "cmcc5(b),ft", pa11, FLAG_STRICT},
+{ "fldd", 0x2c001000, 0xfc0013c0, "cmcc5(s,b),ft", pa11, FLAG_STRICT},
+{ "fldd", 0x50000002, 0xfc000002, "cq&(b),fx", pa20w, FLAG_STRICT},
+{ "fldd", 0x50000002, 0xfc00c002, "cq#(b),fx", pa20, FLAG_STRICT},
+{ "fldd", 0x50000002, 0xfc000002, "cq#(s,b),fx", pa20, FLAG_STRICT},
+{ "fstw", 0x24000200, 0xfc00df80, "cXfT,x(b)", pa10, FLAG_STRICT},
+{ "fstw", 0x24000200, 0xfc001f80, "cXfT,x(s,b)", pa10, FLAG_STRICT},
+{ "fstw", 0x24000200, 0xfc00d380, "cxcCfT,x(b)", pa11, FLAG_STRICT},
+{ "fstw", 0x24000200, 0xfc001380, "cxcCfT,x(s,b)", pa11, FLAG_STRICT},
+{ "fstw", 0x24001220, 0xfc1ff3a0, "cocCfT,@(b)", pa20, FLAG_STRICT},
+{ "fstw", 0x24001220, 0xfc1f33a0, "cocCfT,@(s,b)", pa20, FLAG_STRICT},
+{ "fstw", 0x24001200, 0xfc00df80, "cMfT,5(b)", pa10, FLAG_STRICT},
+{ "fstw", 0x24001200, 0xfc001f80, "cMfT,5(s,b)", pa10, FLAG_STRICT},
+{ "fstw", 0x24001200, 0xfc00df80, "cMfT,5(b)", pa10, FLAG_STRICT},
+{ "fstw", 0x24001200, 0xfc001f80, "cMfT,5(s,b)", pa10, FLAG_STRICT},
+{ "fstw", 0x7c000000, 0xfc000004, "fE,y(b)", pa20w, FLAG_STRICT},
+{ "fstw", 0x78000000, 0xfc000000, "cJfE,y(b)", pa20w, FLAG_STRICT},
+{ "fstw", 0x7c000000, 0xfc00c004, "fE,d(b)", pa20, FLAG_STRICT},
+{ "fstw", 0x7c000000, 0xfc000004, "fE,d(s,b)", pa20, FLAG_STRICT},
+{ "fstw", 0x78000000, 0xfc00c000, "cJfE,d(b)", pa20, FLAG_STRICT},
+{ "fstw", 0x78000000, 0xfc000000, "cJfE,d(s,b)", pa20, FLAG_STRICT},
+{ "fstd", 0x2c000200, 0xfc00dfc0, "cXft,x(b)", pa10, FLAG_STRICT},
+{ "fstd", 0x2c000200, 0xfc001fc0, "cXft,x(s,b)", pa10, FLAG_STRICT},
+{ "fstd", 0x2c000200, 0xfc00d3c0, "cxcCft,x(b)", pa11, FLAG_STRICT},
+{ "fstd", 0x2c000200, 0xfc0013c0, "cxcCft,x(s,b)", pa11, FLAG_STRICT},
+{ "fstd", 0x2c001220, 0xfc1ff3e0, "cocCft,@(b)", pa20, FLAG_STRICT},
+{ "fstd", 0x2c001220, 0xfc1f33e0, "cocCft,@(s,b)", pa20, FLAG_STRICT},
+{ "fstd", 0x2c001200, 0xfc00dfc0, "cMft,5(b)", pa10, FLAG_STRICT},
+{ "fstd", 0x2c001200, 0xfc001fc0, "cMft,5(s,b)", pa10, FLAG_STRICT},
+{ "fstd", 0x2c001200, 0xfc00d3c0, "cmcCft,5(b)", pa11, FLAG_STRICT},
+{ "fstd", 0x2c001200, 0xfc0013c0, "cmcCft,5(s,b)", pa11, FLAG_STRICT},
+{ "fstd", 0x70000002, 0xfc000002, "cqfx,&(b)", pa20w, FLAG_STRICT},
+{ "fstd", 0x70000002, 0xfc00c002, "cqfx,#(b)", pa20, FLAG_STRICT},
+{ "fstd", 0x70000002, 0xfc000002, "cqfx,#(s,b)", pa20, FLAG_STRICT},
+{ "fldwx", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, FLAG_STRICT},
+{ "fldwx", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, FLAG_STRICT},
+{ "fldwx", 0x24000000, 0xfc00d380, "cxccx(b),fT", pa11, FLAG_STRICT},
+{ "fldwx", 0x24000000, 0xfc001380, "cxccx(s,b),fT", pa11, FLAG_STRICT},
+{ "fldwx", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, 0},
+{ "fldwx", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, 0},
+{ "flddx", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, FLAG_STRICT},
+{ "flddx", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, FLAG_STRICT},
+{ "flddx", 0x2c000000, 0xfc00d3c0, "cxccx(b),ft", pa11, FLAG_STRICT},
+{ "flddx", 0x2c000000, 0xfc0013c0, "cxccx(s,b),ft", pa11, FLAG_STRICT},
+{ "flddx", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, 0},
+{ "flddx", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, 0},
+{ "fstwx", 0x24000200, 0xfc00df80, "cxfT,x(b)", pa10, FLAG_STRICT},
+{ "fstwx", 0x24000200, 0xfc001f80, "cxfT,x(s,b)", pa10, FLAG_STRICT},
+{ "fstwx", 0x24000200, 0xfc00d380, "cxcCfT,x(b)", pa11, FLAG_STRICT},
+{ "fstwx", 0x24000200, 0xfc001380, "cxcCfT,x(s,b)", pa11, FLAG_STRICT},
+{ "fstwx", 0x24000200, 0xfc00df80, "cxfT,x(b)", pa10, 0},
+{ "fstwx", 0x24000200, 0xfc001f80, "cxfT,x(s,b)", pa10, 0},
+{ "fstdx", 0x2c000200, 0xfc00dfc0, "cxft,x(b)", pa10, FLAG_STRICT},
+{ "fstdx", 0x2c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, FLAG_STRICT},
+{ "fstdx", 0x2c000200, 0xfc00d3c0, "cxcCft,x(b)", pa11, FLAG_STRICT},
+{ "fstdx", 0x2c000200, 0xfc0013c0, "cxcCft,x(s,b)", pa11, FLAG_STRICT},
+{ "fstdx", 0x2c000200, 0xfc00dfc0, "cxft,x(b)", pa10, 0},
+{ "fstdx", 0x2c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, 0},
+{ "fstqx", 0x3c000200, 0xfc00dfc0, "cxft,x(b)", pa10, 0},
+{ "fstqx", 0x3c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, 0},
+{ "fldws", 0x24001000, 0xfc00df80, "cm5(b),fT", pa10, FLAG_STRICT},
+{ "fldws", 0x24001000, 0xfc001f80, "cm5(s,b),fT", pa10, FLAG_STRICT},
+{ "fldws", 0x24001000, 0xfc00d380, "cmcc5(b),fT", pa11, FLAG_STRICT},
+{ "fldws", 0x24001000, 0xfc001380, "cmcc5(s,b),fT", pa11, FLAG_STRICT},
+{ "fldws", 0x24001000, 0xfc00df80, "cm5(b),fT", pa10, 0},
+{ "fldws", 0x24001000, 0xfc001f80, "cm5(s,b),fT", pa10, 0},
+{ "fldds", 0x2c001000, 0xfc00dfc0, "cm5(b),ft", pa10, FLAG_STRICT},
+{ "fldds", 0x2c001000, 0xfc001fc0, "cm5(s,b),ft", pa10, FLAG_STRICT},
+{ "fldds", 0x2c001000, 0xfc00d3c0, "cmcc5(b),ft", pa11, FLAG_STRICT},
+{ "fldds", 0x2c001000, 0xfc0013c0, "cmcc5(s,b),ft", pa11, FLAG_STRICT},
+{ "fldds", 0x2c001000, 0xfc00dfc0, "cm5(b),ft", pa10, 0},
+{ "fldds", 0x2c001000, 0xfc001fc0, "cm5(s,b),ft", pa10, 0},
+{ "fstws", 0x24001200, 0xfc00df80, "cmfT,5(b)", pa10, FLAG_STRICT},
+{ "fstws", 0x24001200, 0xfc001f80, "cmfT,5(s,b)", pa10, FLAG_STRICT},
+{ "fstws", 0x24001200, 0xfc00d380, "cmcCfT,5(b)", pa11, FLAG_STRICT},
+{ "fstws", 0x24001200, 0xfc001380, "cmcCfT,5(s,b)", pa11, FLAG_STRICT},
+{ "fstws", 0x24001200, 0xfc00df80, "cmfT,5(b)", pa10, 0},
+{ "fstws", 0x24001200, 0xfc001f80, "cmfT,5(s,b)", pa10, 0},
+{ "fstds", 0x2c001200, 0xfc00dfc0, "cmft,5(b)", pa10, FLAG_STRICT},
+{ "fstds", 0x2c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, FLAG_STRICT},
+{ "fstds", 0x2c001200, 0xfc00d3c0, "cmcCft,5(b)", pa11, FLAG_STRICT},
+{ "fstds", 0x2c001200, 0xfc0013c0, "cmcCft,5(s,b)", pa11, FLAG_STRICT},
+{ "fstds", 0x2c001200, 0xfc00dfc0, "cmft,5(b)", pa10, 0},
+{ "fstds", 0x2c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, 0},
+{ "fstqs", 0x3c001200, 0xfc00dfc0, "cmft,5(b)", pa10, 0},
+{ "fstqs", 0x3c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, 0},
+{ "fadd", 0x30000600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0},
+{ "fadd", 0x38000600, 0xfc00e720, "IfA,fB,fT", pa10, 0},
+{ "fsub", 0x30002600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0},
+{ "fsub", 0x38002600, 0xfc00e720, "IfA,fB,fT", pa10, 0},
+{ "fmpy", 0x30004600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0},
+{ "fmpy", 0x38004600, 0xfc00e720, "IfA,fB,fT", pa10, 0},
+{ "fdiv", 0x30006600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0},
+{ "fdiv", 0x38006600, 0xfc00e720, "IfA,fB,fT", pa10, 0},
+{ "fsqrt", 0x30008000, 0xfc1fe7e0, "Ffa,fT", pa10, 0},
+{ "fsqrt", 0x38008000, 0xfc1fe720, "FfA,fT", pa10, 0},
+{ "fabs", 0x30006000, 0xfc1fe7e0, "Ffa,fT", pa10, 0},
+{ "fabs", 0x38006000, 0xfc1fe720, "FfA,fT", pa10, 0},
+{ "frem", 0x30008600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0},
+{ "frem", 0x38008600, 0xfc00e720, "FfA,fB,fT", pa10, 0},
+{ "frnd", 0x3000a000, 0xfc1fe7e0, "Ffa,fT", pa10, 0},
+{ "frnd", 0x3800a000, 0xfc1fe720, "FfA,fT", pa10, 0},
+{ "fcpy", 0x30004000, 0xfc1fe7e0, "Ffa,fT", pa10, 0},
+{ "fcpy", 0x38004000, 0xfc1fe720, "FfA,fT", pa10, 0},
+{ "fcnvff", 0x30000200, 0xfc1f87e0, "FGfa,fT", pa10, 0},
+{ "fcnvff", 0x38000200, 0xfc1f8720, "FGfA,fT", pa10, 0},
+{ "fcnvxf", 0x30008200, 0xfc1f87e0, "FGfa,fT", pa10, 0},
+{ "fcnvxf", 0x38008200, 0xfc1f8720, "FGfA,fT", pa10, 0},
+{ "fcnvfx", 0x30010200, 0xfc1f87e0, "FGfa,fT", pa10, 0},
+{ "fcnvfx", 0x38010200, 0xfc1f8720, "FGfA,fT", pa10, 0},
+{ "fcnvfxt", 0x30018200, 0xfc1f87e0, "FGfa,fT", pa10, 0},
+{ "fcnvfxt", 0x38018200, 0xfc1f8720, "FGfA,fT", pa10, 0},
+{ "fmpyfadd", 0xb8000000, 0xfc000020, "IfA,fB,fC,fT", pa20, FLAG_STRICT},
+{ "fmpynfadd", 0xb8000020, 0xfc000020, "IfA,fB,fC,fT", pa20, FLAG_STRICT},
+{ "fneg", 0x3000c000, 0xfc1fe7e0, "Ffa,fT", pa20, FLAG_STRICT},
+{ "fneg", 0x3800c000, 0xfc1fe720, "IfA,fT", pa20, FLAG_STRICT},
+{ "fnegabs", 0x3000e000, 0xfc1fe7e0, "Ffa,fT", pa20, FLAG_STRICT},
+{ "fnegabs", 0x3800e000, 0xfc1fe720, "IfA,fT", pa20, FLAG_STRICT},
+{ "fcnv", 0x30000200, 0xfc1c0720, "{_fa,fT", pa20, FLAG_STRICT},
+{ "fcnv", 0x38000200, 0xfc1c0720, "FGfA,fT", pa20, FLAG_STRICT},
+{ "fcmp", 0x30000400, 0xfc00e7e0, "F?ffa,fb", pa10, FLAG_STRICT},
+{ "fcmp", 0x38000400, 0xfc00e720, "I?ffA,fB", pa10, FLAG_STRICT},
+{ "fcmp", 0x30000400, 0xfc0007e0, "F?ffa,fb,h", pa20, FLAG_STRICT},
+{ "fcmp", 0x38000400, 0xfc000720, "I?ffA,fB,h", pa20, FLAG_STRICT},
+{ "fcmp", 0x30000400, 0xfc00e7e0, "F?ffa,fb", pa10, 0},
+{ "fcmp", 0x38000400, 0xfc00e720, "I?ffA,fB", pa10, 0},
+{ "xmpyu", 0x38004700, 0xfc00e720, "fX,fB,fT", pa11, 0},
+{ "fmpyadd", 0x18000000, 0xfc000000, "Hfi,fj,fk,fl,fm", pa11, 0},
+{ "fmpysub", 0x98000000, 0xfc000000, "Hfi,fj,fk,fl,fm", pa11, 0},
+{ "ftest", 0x30002420, 0xffffffff, "", pa10, FLAG_STRICT},
+{ "ftest", 0x30002420, 0xffffffe0, ",=", pa20, FLAG_STRICT},
+{ "ftest", 0x30000420, 0xffff1fff, "m", pa20, FLAG_STRICT},
+{ "fid", 0x30000000, 0xffffffff, "", pa11, 0},
+
+/* Performance Monitor Instructions. */
+
+{ "pmdis", 0x30000280, 0xffffffdf, "N", pa20, FLAG_STRICT},
+{ "pmenb", 0x30000680, 0xffffffff, "", pa20, FLAG_STRICT},
+
+/* Assist Instructions. */
+
+{ "spop0", 0x10000000, 0xfc000600, "v,ON", pa10, 0},
+{ "spop1", 0x10000200, 0xfc000600, "v,oNt", pa10, 0},
+{ "spop2", 0x10000400, 0xfc000600, "v,1Nb", pa10, 0},
+{ "spop3", 0x10000600, 0xfc000600, "v,0Nx,b", pa10, 0},
+{ "copr", 0x30000000, 0xfc000000, "u,2N", pa10, 0},
+{ "cldw", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT},
+{ "cldw", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT},
+{ "cldw", 0x24000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT},
+{ "cldw", 0x24000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT},
+{ "cldw", 0x24001000, 0xfc00d200, "ucocc@(b),t", pa20, FLAG_STRICT},
+{ "cldw", 0x24001000, 0xfc001200, "ucocc@(s,b),t", pa20, FLAG_STRICT},
+{ "cldw", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT},
+{ "cldw", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT},
+{ "cldw", 0x24001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT},
+{ "cldw", 0x24001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "cldd", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT},
+{ "cldd", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT},
+{ "cldd", 0x2c000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT},
+{ "cldd", 0x2c000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT},
+{ "cldd", 0x2c001000, 0xfc00d200, "ucocc@(b),t", pa20, FLAG_STRICT},
+{ "cldd", 0x2c001000, 0xfc001200, "ucocc@(s,b),t", pa20, FLAG_STRICT},
+{ "cldd", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT},
+{ "cldd", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT},
+{ "cldd", 0x2c001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT},
+{ "cldd", 0x2c001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "cstw", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT},
+{ "cstw", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT},
+{ "cstw", 0x24000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT},
+{ "cstw", 0x24000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT},
+{ "cstw", 0x24001200, 0xfc00d200, "ucocCt,@(b)", pa20, FLAG_STRICT},
+{ "cstw", 0x24001200, 0xfc001200, "ucocCt,@(s,b)", pa20, FLAG_STRICT},
+{ "cstw", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT},
+{ "cstw", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT},
+{ "cstw", 0x24001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT},
+{ "cstw", 0x24001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT},
+{ "cstd", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT},
+{ "cstd", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT},
+{ "cstd", 0x2c000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT},
+{ "cstd", 0x2c000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT},
+{ "cstd", 0x2c001200, 0xfc00d200, "ucocCt,@(b)", pa20, FLAG_STRICT},
+{ "cstd", 0x2c001200, 0xfc001200, "ucocCt,@(s,b)", pa20, FLAG_STRICT},
+{ "cstd", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT},
+{ "cstd", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT},
+{ "cstd", 0x2c001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT},
+{ "cstd", 0x2c001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT},
+{ "cldwx", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT},
+{ "cldwx", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT},
+{ "cldwx", 0x24000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT},
+{ "cldwx", 0x24000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT},
+{ "cldwx", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, 0},
+{ "cldwx", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, 0},
+{ "clddx", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT},
+{ "clddx", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT},
+{ "clddx", 0x2c000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT},
+{ "clddx", 0x2c000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT},
+{ "clddx", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, 0},
+{ "clddx", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, 0},
+{ "cstwx", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT},
+{ "cstwx", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT},
+{ "cstwx", 0x24000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT},
+{ "cstwx", 0x24000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT},
+{ "cstwx", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, 0},
+{ "cstwx", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, 0},
+{ "cstdx", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT},
+{ "cstdx", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT},
+{ "cstdx", 0x2c000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT},
+{ "cstdx", 0x2c000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT},
+{ "cstdx", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, 0},
+{ "cstdx", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, 0},
+{ "cldws", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT},
+{ "cldws", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT},
+{ "cldws", 0x24001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT},
+{ "cldws", 0x24001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "cldws", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, 0},
+{ "cldws", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, 0},
+{ "cldds", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT},
+{ "cldds", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT},
+{ "cldds", 0x2c001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT},
+{ "cldds", 0x2c001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT},
+{ "cldds", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, 0},
+{ "cldds", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, 0},
+{ "cstws", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT},
+{ "cstws", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT},
+{ "cstws", 0x24001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT},
+{ "cstws", 0x24001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT},
+{ "cstws", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, 0},
+{ "cstws", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, 0},
+{ "cstds", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT},
+{ "cstds", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT},
+{ "cstds", 0x2c001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT},
+{ "cstds", 0x2c001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT},
+{ "cstds", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, 0},
+{ "cstds", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, 0},
+
+/* More pseudo instructions which must follow the main table. */
+{ "call", 0xe800f000, 0xfc1ffffd, "n(b)", pa20, FLAG_STRICT},
+{ "call", 0xe800a000, 0xffe0e000, "nW", pa10, FLAG_STRICT},
+{ "ret", 0xe840d000, 0xfffffffd, "n", pa20, FLAG_STRICT},
+
+};
+
+#define NUMOPCODES ((sizeof pa_opcodes)/(sizeof pa_opcodes[0]))
+
+/* SKV 12/18/92. Added some denotations for various operands. */
+
+#define PA_IMM11_AT_31 'i'
+#define PA_IMM14_AT_31 'j'
+#define PA_IMM21_AT_31 'k'
+#define PA_DISP12 'w'
+#define PA_DISP17 'W'
+
+#define N_HPPA_OPERAND_FORMATS 5
+
+/* Integer register names, indexed by the numbers which appear in the
+ opcodes. */
+static const char *const reg_names[] =
+{
+ "flags", "r1", "rp", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
+ "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23", "r24", "r25", "r26", "dp", "ret0", "ret1",
+ "sp", "r31"
+};
+
+/* Floating point register names, indexed by the numbers which appear in the
+ opcodes. */
+static const char *const fp_reg_names[] =
+{
+ "fpsr", "fpe2", "fpe4", "fpe6",
+ "fr4", "fr5", "fr6", "fr7", "fr8",
+ "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",
+ "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23",
+ "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31"
+};
+
+typedef unsigned int CORE_ADDR;
+
+/* Get at various relevent fields of an instruction word. */
+
+#define MASK_5 0x1f
+#define MASK_10 0x3ff
+#define MASK_11 0x7ff
+#define MASK_14 0x3fff
+#define MASK_16 0xffff
+#define MASK_21 0x1fffff
+
+/* These macros get bit fields using HP's numbering (MSB = 0). */
+
+#define GET_FIELD(X, FROM, TO) \
+ ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
+
+#define GET_BIT(X, WHICH) \
+ GET_FIELD (X, WHICH, WHICH)
+
+/* Some of these have been converted to 2-d arrays because they
+ consume less storage this way. If the maintenance becomes a
+ problem, convert them back to const 1-d pointer arrays. */
+static const char *const control_reg[] =
+{
+ "rctr", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+ "pidr1", "pidr2", "ccr", "sar", "pidr3", "pidr4",
+ "iva", "eiem", "itmr", "pcsq", "pcoq", "iir", "isr",
+ "ior", "ipsw", "eirr", "tr0", "tr1", "tr2", "tr3",
+ "tr4", "tr5", "tr6", "tr7"
+};
+
+static const char *const compare_cond_names[] =
+{
+ "", ",=", ",<", ",<=", ",<<", ",<<=", ",sv", ",od",
+ ",tr", ",<>", ",>=", ",>", ",>>=", ",>>", ",nsv", ",ev"
+};
+static const char *const compare_cond_64_names[] =
+{
+ "", ",*=", ",*<", ",*<=", ",*<<", ",*<<=", ",*sv", ",*od",
+ ",*tr", ",*<>", ",*>=", ",*>", ",*>>=", ",*>>", ",*nsv", ",*ev"
+};
+static const char *const cmpib_cond_64_names[] =
+{
+ ",*<<", ",*=", ",*<", ",*<=", ",*>>=", ",*<>", ",*>=", ",*>"
+};
+static const char *const add_cond_names[] =
+{
+ "", ",=", ",<", ",<=", ",nuv", ",znv", ",sv", ",od",
+ ",tr", ",<>", ",>=", ",>", ",uv", ",vnz", ",nsv", ",ev"
+};
+static const char *const add_cond_64_names[] =
+{
+ "", ",*=", ",*<", ",*<=", ",*nuv", ",*znv", ",*sv", ",*od",
+ ",*tr", ",*<>", ",*>=", ",*>", ",*uv", ",*vnz", ",*nsv", ",*ev"
+};
+static const char *const wide_add_cond_names[] =
+{
+ "", ",=", ",<", ",<=", ",nuv", ",*=", ",*<", ",*<=",
+ ",tr", ",<>", ",>=", ",>", ",uv", ",*<>", ",*>=", ",*>"
+};
+static const char *const logical_cond_names[] =
+{
+ "", ",=", ",<", ",<=", 0, 0, 0, ",od",
+ ",tr", ",<>", ",>=", ",>", 0, 0, 0, ",ev"};
+static const char *const logical_cond_64_names[] =
+{
+ "", ",*=", ",*<", ",*<=", 0, 0, 0, ",*od",
+ ",*tr", ",*<>", ",*>=", ",*>", 0, 0, 0, ",*ev"};
+static const char *const unit_cond_names[] =
+{
+ "", ",swz", ",sbz", ",shz", ",sdc", ",swc", ",sbc", ",shc",
+ ",tr", ",nwz", ",nbz", ",nhz", ",ndc", ",nwc", ",nbc", ",nhc"
+};
+static const char *const unit_cond_64_names[] =
+{
+ "", ",*swz", ",*sbz", ",*shz", ",*sdc", ",*swc", ",*sbc", ",*shc",
+ ",*tr", ",*nwz", ",*nbz", ",*nhz", ",*ndc", ",*nwc", ",*nbc", ",*nhc"
+};
+static const char *const shift_cond_names[] =
+{
+ "", ",=", ",<", ",od", ",tr", ",<>", ",>=", ",ev"
+};
+static const char *const shift_cond_64_names[] =
+{
+ "", ",*=", ",*<", ",*od", ",*tr", ",*<>", ",*>=", ",*ev"
+};
+static const char *const bb_cond_64_names[] =
+{
+ ",*<", ",*>="
+};
+static const char *const index_compl_names[] = {"", ",m", ",s", ",sm"};
+static const char *const short_ldst_compl_names[] = {"", ",ma", "", ",mb"};
+static const char *const short_bytes_compl_names[] =
+{
+ "", ",b,m", ",e", ",e,m"
+};
+static const char *const float_format_names[] = {",sgl", ",dbl", "", ",quad"};
+static const char *const fcnv_fixed_names[] = {",w", ",dw", "", ",qw"};
+static const char *const fcnv_ufixed_names[] = {",uw", ",udw", "", ",uqw"};
+static const char *const float_comp_names[] =
+{
+ ",false?", ",false", ",?", ",!<=>", ",=", ",=t", ",?=", ",!<>",
+ ",!?>=", ",<", ",?<", ",!>=", ",!?>", ",<=", ",?<=", ",!>",
+ ",!?<=", ",>", ",?>", ",!<=", ",!?<", ",>=", ",?>=", ",!<",
+ ",!?=", ",<>", ",!=", ",!=t", ",!?", ",<=>", ",true?", ",true"
+};
+static const char *const signed_unsigned_names[] = {",u", ",s"};
+static const char *const mix_half_names[] = {",l", ",r"};
+static const char *const saturation_names[] = {",us", ",ss", 0, ""};
+static const char *const read_write_names[] = {",r", ",w"};
+static const char *const add_compl_names[] = { 0, "", ",l", ",tsv" };
+
+/* For a bunch of different instructions form an index into a
+ completer name table. */
+#define GET_COMPL(insn) (GET_FIELD (insn, 26, 26) | \
+ GET_FIELD (insn, 18, 18) << 1)
+
+#define GET_COND(insn) (GET_FIELD ((insn), 16, 18) + \
+ (GET_FIELD ((insn), 19, 19) ? 8 : 0))
+
+/* Utility function to print registers. Put these first, so gcc's function
+ inlining can do its stuff. */
+
+#define fputs_filtered(STR,F) (*info->fprintf_func) (info->stream, "%s", STR)
+
+static void
+fput_reg (unsigned reg, disassemble_info *info)
+{
+ (*info->fprintf_func) (info->stream, reg ? reg_names[reg] : "r0");
+}
+
+static void
+fput_fp_reg (unsigned reg, disassemble_info *info)
+{
+ (*info->fprintf_func) (info->stream, reg ? fp_reg_names[reg] : "fr0");
+}
+
+static void
+fput_fp_reg_r (unsigned reg, disassemble_info *info)
+{
+ /* Special case floating point exception registers. */
+ if (reg < 4)
+ (*info->fprintf_func) (info->stream, "fpe%d", reg * 2 + 1);
+ else
+ (*info->fprintf_func) (info->stream, "%sR",
+ reg ? fp_reg_names[reg] : "fr0");
+}
+
+static void
+fput_creg (unsigned reg, disassemble_info *info)
+{
+ (*info->fprintf_func) (info->stream, control_reg[reg]);
+}
+
+/* Print constants with sign. */
+
+static void
+fput_const (unsigned num, disassemble_info *info)
+{
+ if ((int) num < 0)
+ (*info->fprintf_func) (info->stream, "-%x", - (int) num);
+ else
+ (*info->fprintf_func) (info->stream, "%x", num);
+}
+
+/* Routines to extract various sized constants out of hppa
+ instructions. */
+
+/* Extract a 3-bit space register number from a be, ble, mtsp or mfsp. */
+static int
+extract_3 (unsigned word)
+{
+ return GET_FIELD (word, 18, 18) << 2 | GET_FIELD (word, 16, 17);
+}
+
+static int
+extract_5_load (unsigned word)
+{
+ return low_sign_extend (word >> 16 & MASK_5, 5);
+}
+
+/* Extract the immediate field from a st{bhw}s instruction. */
+
+static int
+extract_5_store (unsigned word)
+{
+ return low_sign_extend (word & MASK_5, 5);
+}
+
+/* Extract the immediate field from a break instruction. */
+
+static unsigned
+extract_5r_store (unsigned word)
+{
+ return (word & MASK_5);
+}
+
+/* Extract the immediate field from a {sr}sm instruction. */
+
+static unsigned
+extract_5R_store (unsigned word)
+{
+ return (word >> 16 & MASK_5);
+}
+
+/* Extract the 10 bit immediate field from a {sr}sm instruction. */
+
+static unsigned
+extract_10U_store (unsigned word)
+{
+ return (word >> 16 & MASK_10);
+}
+
+/* Extract the immediate field from a bb instruction. */
+
+static unsigned
+extract_5Q_store (unsigned word)
+{
+ return (word >> 21 & MASK_5);
+}
+
+/* Extract an 11 bit immediate field. */
+
+static int
+extract_11 (unsigned word)
+{
+ return low_sign_extend (word & MASK_11, 11);
+}
+
+/* Extract a 14 bit immediate field. */
+
+static int
+extract_14 (unsigned word)
+{
+ return low_sign_extend (word & MASK_14, 14);
+}
+
+/* Extract a 16 bit immediate field (PA2.0 wide only). */
+
+static int
+extract_16 (unsigned word)
+{
+ int m15, m0, m1;
+
+ m0 = GET_BIT (word, 16);
+ m1 = GET_BIT (word, 17);
+ m15 = GET_BIT (word, 31);
+ word = (word >> 1) & 0x1fff;
+ word = word | (m15 << 15) | ((m15 ^ m0) << 14) | ((m15 ^ m1) << 13);
+ return sign_extend (word, 16);
+}
+
+/* Extract a 21 bit constant. */
+
+static int
+extract_21 (unsigned word)
+{
+ int val;
+
+ word &= MASK_21;
+ word <<= 11;
+ val = GET_FIELD (word, 20, 20);
+ val <<= 11;
+ val |= GET_FIELD (word, 9, 19);
+ val <<= 2;
+ val |= GET_FIELD (word, 5, 6);
+ val <<= 5;
+ val |= GET_FIELD (word, 0, 4);
+ val <<= 2;
+ val |= GET_FIELD (word, 7, 8);
+ return sign_extend (val, 21) << 11;
+}
+
+/* Extract a 12 bit constant from branch instructions. */
+
+static int
+extract_12 (unsigned word)
+{
+ return sign_extend (GET_FIELD (word, 19, 28)
+ | GET_FIELD (word, 29, 29) << 10
+ | (word & 0x1) << 11, 12) << 2;
+}
+
+/* Extract a 17 bit constant from branch instructions, returning the
+ 19 bit signed value. */
+
+static int
+extract_17 (unsigned word)
+{
+ return sign_extend (GET_FIELD (word, 19, 28)
+ | GET_FIELD (word, 29, 29) << 10
+ | GET_FIELD (word, 11, 15) << 11
+ | (word & 0x1) << 16, 17) << 2;
+}
+
+static int
+extract_22 (unsigned word)
+{
+ return sign_extend (GET_FIELD (word, 19, 28)
+ | GET_FIELD (word, 29, 29) << 10
+ | GET_FIELD (word, 11, 15) << 11
+ | GET_FIELD (word, 6, 10) << 16
+ | (word & 0x1) << 21, 22) << 2;
+}
+
+/* Print one instruction. */
+
+int
+print_insn_hppa (bfd_vma memaddr, disassemble_info *info)
+{
+ bfd_byte buffer[4];
+ unsigned int insn, i;
+
+ {
+ int status =
+ (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ }
+
+ insn = bfd_getb32 (buffer);
+
+ for (i = 0; i < NUMOPCODES; ++i)
+ {
+ const struct pa_opcode *opcode = &pa_opcodes[i];
+
+ if ((insn & opcode->mask) == opcode->match)
+ {
+ const char *s;
+#ifndef BFD64
+ if (opcode->arch == pa20w)
+ continue;
+#endif
+ (*info->fprintf_func) (info->stream, "%s", opcode->name);
+
+ if (!strchr ("cfCY?-+nHNZFIuv{", opcode->args[0]))
+ (*info->fprintf_func) (info->stream, " ");
+ for (s = opcode->args; *s != '\0'; ++s)
+ {
+ switch (*s)
+ {
+ case 'x':
+ fput_reg (GET_FIELD (insn, 11, 15), info);
+ break;
+ case 'a':
+ case 'b':
+ fput_reg (GET_FIELD (insn, 6, 10), info);
+ break;
+ case '^':
+ fput_creg (GET_FIELD (insn, 6, 10), info);
+ break;
+ case 't':
+ fput_reg (GET_FIELD (insn, 27, 31), info);
+ break;
+
+ /* Handle floating point registers. */
+ case 'f':
+ switch (*++s)
+ {
+ case 't':
+ fput_fp_reg (GET_FIELD (insn, 27, 31), info);
+ break;
+ case 'T':
+ if (GET_FIELD (insn, 25, 25))
+ fput_fp_reg_r (GET_FIELD (insn, 27, 31), info);
+ else
+ fput_fp_reg (GET_FIELD (insn, 27, 31), info);
+ break;
+ case 'a':
+ if (GET_FIELD (insn, 25, 25))
+ fput_fp_reg_r (GET_FIELD (insn, 6, 10), info);
+ else
+ fput_fp_reg (GET_FIELD (insn, 6, 10), info);
+ break;
+
+ /* 'fA' will not generate a space before the regsiter
+ name. Normally that is fine. Except that it
+ causes problems with xmpyu which has no FP format
+ completer. */
+ case 'X':
+ fputs_filtered (" ", info);
+ /* FALLTHRU */
+
+ case 'A':
+ if (GET_FIELD (insn, 24, 24))
+ fput_fp_reg_r (GET_FIELD (insn, 6, 10), info);
+ else
+ fput_fp_reg (GET_FIELD (insn, 6, 10), info);
+ break;
+ case 'b':
+ if (GET_FIELD (insn, 25, 25))
+ fput_fp_reg_r (GET_FIELD (insn, 11, 15), info);
+ else
+ fput_fp_reg (GET_FIELD (insn, 11, 15), info);
+ break;
+ case 'B':
+ if (GET_FIELD (insn, 19, 19))
+ fput_fp_reg_r (GET_FIELD (insn, 11, 15), info);
+ else
+ fput_fp_reg (GET_FIELD (insn, 11, 15), info);
+ break;
+ case 'C':
+ {
+ int reg = GET_FIELD (insn, 21, 22);
+ reg |= GET_FIELD (insn, 16, 18) << 2;
+ if (GET_FIELD (insn, 23, 23) != 0)
+ fput_fp_reg_r (reg, info);
+ else
+ fput_fp_reg (reg, info);
+ break;
+ }
+ case 'i':
+ {
+ int reg = GET_FIELD (insn, 6, 10);
+
+ reg |= (GET_FIELD (insn, 26, 26) << 4);
+ fput_fp_reg (reg, info);
+ break;
+ }
+ case 'j':
+ {
+ int reg = GET_FIELD (insn, 11, 15);
+
+ reg |= (GET_FIELD (insn, 26, 26) << 4);
+ fput_fp_reg (reg, info);
+ break;
+ }
+ case 'k':
+ {
+ int reg = GET_FIELD (insn, 27, 31);
+
+ reg |= (GET_FIELD (insn, 26, 26) << 4);
+ fput_fp_reg (reg, info);
+ break;
+ }
+ case 'l':
+ {
+ int reg = GET_FIELD (insn, 21, 25);
+
+ reg |= (GET_FIELD (insn, 26, 26) << 4);
+ fput_fp_reg (reg, info);
+ break;
+ }
+ case 'm':
+ {
+ int reg = GET_FIELD (insn, 16, 20);
+
+ reg |= (GET_FIELD (insn, 26, 26) << 4);
+ fput_fp_reg (reg, info);
+ break;
+ }
+
+ /* 'fe' will not generate a space before the register
+ name. Normally that is fine. Except that it
+ causes problems with fstw fe,y(b) which has no FP
+ format completer. */
+ case 'E':
+ fputs_filtered (" ", info);
+ /* FALLTHRU */
+
+ case 'e':
+ if (GET_FIELD (insn, 30, 30))
+ fput_fp_reg_r (GET_FIELD (insn, 11, 15), info);
+ else
+ fput_fp_reg (GET_FIELD (insn, 11, 15), info);
+ break;
+ case 'x':
+ fput_fp_reg (GET_FIELD (insn, 11, 15), info);
+ break;
+ }
+ break;
+
+ case '5':
+ fput_const (extract_5_load (insn), info);
+ break;
+ case 's':
+ {
+ int space = GET_FIELD (insn, 16, 17);
+ /* Zero means implicit addressing, not use of sr0. */
+ if (space != 0)
+ (*info->fprintf_func) (info->stream, "sr%d", space);
+ }
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "sr%d",
+ extract_3 (insn));
+ break;
+
+ /* Handle completers. */
+ case 'c':
+ switch (*++s)
+ {
+ case 'x':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ index_compl_names[GET_COMPL (insn)]);
+ break;
+ case 'X':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ index_compl_names[GET_COMPL (insn)]);
+ break;
+ case 'm':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ short_ldst_compl_names[GET_COMPL (insn)]);
+ break;
+ case 'M':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ short_ldst_compl_names[GET_COMPL (insn)]);
+ break;
+ case 'A':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ short_bytes_compl_names[GET_COMPL (insn)]);
+ break;
+ case 's':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ short_bytes_compl_names[GET_COMPL (insn)]);
+ break;
+ case 'c':
+ case 'C':
+ switch (GET_FIELD (insn, 20, 21))
+ {
+ case 1:
+ (*info->fprintf_func) (info->stream, ",bc ");
+ break;
+ case 2:
+ (*info->fprintf_func) (info->stream, ",sl ");
+ break;
+ default:
+ (*info->fprintf_func) (info->stream, " ");
+ }
+ break;
+ case 'd':
+ switch (GET_FIELD (insn, 20, 21))
+ {
+ case 1:
+ (*info->fprintf_func) (info->stream, ",co ");
+ break;
+ default:
+ (*info->fprintf_func) (info->stream, " ");
+ }
+ break;
+ case 'o':
+ (*info->fprintf_func) (info->stream, ",o");
+ break;
+ case 'g':
+ (*info->fprintf_func) (info->stream, ",gate");
+ break;
+ case 'p':
+ (*info->fprintf_func) (info->stream, ",l,push");
+ break;
+ case 'P':
+ (*info->fprintf_func) (info->stream, ",pop");
+ break;
+ case 'l':
+ case 'L':
+ (*info->fprintf_func) (info->stream, ",l");
+ break;
+ case 'w':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ read_write_names[GET_FIELD (insn, 25, 25)]);
+ break;
+ case 'W':
+ (*info->fprintf_func) (info->stream, ",w ");
+ break;
+ case 'r':
+ if (GET_FIELD (insn, 23, 26) == 5)
+ (*info->fprintf_func) (info->stream, ",r");
+ break;
+ case 'Z':
+ if (GET_FIELD (insn, 26, 26))
+ (*info->fprintf_func) (info->stream, ",m ");
+ else
+ (*info->fprintf_func) (info->stream, " ");
+ break;
+ case 'i':
+ if (GET_FIELD (insn, 25, 25))
+ (*info->fprintf_func) (info->stream, ",i");
+ break;
+ case 'z':
+ if (!GET_FIELD (insn, 21, 21))
+ (*info->fprintf_func) (info->stream, ",z");
+ break;
+ case 'a':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ add_compl_names[GET_FIELD (insn, 20, 21)]);
+ break;
+ case 'Y':
+ (*info->fprintf_func)
+ (info->stream, ",dc%s",
+ add_compl_names[GET_FIELD (insn, 20, 21)]);
+ break;
+ case 'y':
+ (*info->fprintf_func)
+ (info->stream, ",c%s",
+ add_compl_names[GET_FIELD (insn, 20, 21)]);
+ break;
+ case 'v':
+ if (GET_FIELD (insn, 20, 20))
+ (*info->fprintf_func) (info->stream, ",tsv");
+ break;
+ case 't':
+ (*info->fprintf_func) (info->stream, ",tc");
+ if (GET_FIELD (insn, 20, 20))
+ (*info->fprintf_func) (info->stream, ",tsv");
+ break;
+ case 'B':
+ (*info->fprintf_func) (info->stream, ",db");
+ if (GET_FIELD (insn, 20, 20))
+ (*info->fprintf_func) (info->stream, ",tsv");
+ break;
+ case 'b':
+ (*info->fprintf_func) (info->stream, ",b");
+ if (GET_FIELD (insn, 20, 20))
+ (*info->fprintf_func) (info->stream, ",tsv");
+ break;
+ case 'T':
+ if (GET_FIELD (insn, 25, 25))
+ (*info->fprintf_func) (info->stream, ",tc");
+ break;
+ case 'S':
+ /* EXTRD/W has a following condition. */
+ if (*(s + 1) == '?')
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ signed_unsigned_names[GET_FIELD (insn, 21, 21)]);
+ else
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ signed_unsigned_names[GET_FIELD (insn, 21, 21)]);
+ break;
+ case 'h':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ mix_half_names[GET_FIELD (insn, 17, 17)]);
+ break;
+ case 'H':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ saturation_names[GET_FIELD (insn, 24, 25)]);
+ break;
+ case '*':
+ (*info->fprintf_func)
+ (info->stream, ",%d%d%d%d ",
+ GET_FIELD (insn, 17, 18), GET_FIELD (insn, 20, 21),
+ GET_FIELD (insn, 22, 23), GET_FIELD (insn, 24, 25));
+ break;
+
+ case 'q':
+ {
+ int m, a;
+
+ m = GET_FIELD (insn, 28, 28);
+ a = GET_FIELD (insn, 29, 29);
+
+ if (m && !a)
+ fputs_filtered (",ma ", info);
+ else if (m && a)
+ fputs_filtered (",mb ", info);
+ else
+ fputs_filtered (" ", info);
+ break;
+ }
+
+ case 'J':
+ {
+ int opc = GET_FIELD (insn, 0, 5);
+
+ if (opc == 0x16 || opc == 0x1e)
+ {
+ if (GET_FIELD (insn, 29, 29) == 0)
+ fputs_filtered (",ma ", info);
+ else
+ fputs_filtered (",mb ", info);
+ }
+ else
+ fputs_filtered (" ", info);
+ break;
+ }
+
+ case 'e':
+ {
+ int opc = GET_FIELD (insn, 0, 5);
+
+ if (opc == 0x13 || opc == 0x1b)
+ {
+ if (GET_FIELD (insn, 18, 18) == 1)
+ fputs_filtered (",mb ", info);
+ else
+ fputs_filtered (",ma ", info);
+ }
+ else if (opc == 0x17 || opc == 0x1f)
+ {
+ if (GET_FIELD (insn, 31, 31) == 1)
+ fputs_filtered (",ma ", info);
+ else
+ fputs_filtered (",mb ", info);
+ }
+ else
+ fputs_filtered (" ", info);
+
+ break;
+ }
+ }
+ break;
+
+ /* Handle conditions. */
+ case '?':
+ {
+ s++;
+ switch (*s)
+ {
+ case 'f':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ float_comp_names[GET_FIELD (insn, 27, 31)]);
+ break;
+
+ /* These four conditions are for the set of instructions
+ which distinguish true/false conditions by opcode
+ rather than by the 'f' bit (sigh): comb, comib,
+ addb, addib. */
+ case 't':
+ fputs_filtered
+ (compare_cond_names[GET_FIELD (insn, 16, 18)], info);
+ break;
+ case 'n':
+ fputs_filtered
+ (compare_cond_names[GET_FIELD (insn, 16, 18)
+ + GET_FIELD (insn, 4, 4) * 8],
+ info);
+ break;
+ case 'N':
+ fputs_filtered
+ (compare_cond_64_names[GET_FIELD (insn, 16, 18)
+ + GET_FIELD (insn, 2, 2) * 8],
+ info);
+ break;
+ case 'Q':
+ fputs_filtered
+ (cmpib_cond_64_names[GET_FIELD (insn, 16, 18)],
+ info);
+ break;
+ case '@':
+ fputs_filtered
+ (add_cond_names[GET_FIELD (insn, 16, 18)
+ + GET_FIELD (insn, 4, 4) * 8],
+ info);
+ break;
+ case 's':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ compare_cond_names[GET_COND (insn)]);
+ break;
+ case 'S':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ compare_cond_64_names[GET_COND (insn)]);
+ break;
+ case 'a':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ add_cond_names[GET_COND (insn)]);
+ break;
+ case 'A':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ add_cond_64_names[GET_COND (insn)]);
+ break;
+ case 'd':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ add_cond_names[GET_FIELD (insn, 16, 18)]);
+ break;
+
+ case 'W':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ wide_add_cond_names[GET_FIELD (insn, 16, 18) +
+ GET_FIELD (insn, 4, 4) * 8]);
+ break;
+
+ case 'l':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ logical_cond_names[GET_COND (insn)]);
+ break;
+ case 'L':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ logical_cond_64_names[GET_COND (insn)]);
+ break;
+ case 'u':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ unit_cond_names[GET_COND (insn)]);
+ break;
+ case 'U':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ unit_cond_64_names[GET_COND (insn)]);
+ break;
+ case 'y':
+ case 'x':
+ case 'b':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ shift_cond_names[GET_FIELD (insn, 16, 18)]);
+
+ /* If the next character in args is 'n', it will handle
+ putting out the space. */
+ if (s[1] != 'n')
+ (*info->fprintf_func) (info->stream, " ");
+ break;
+ case 'X':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ shift_cond_64_names[GET_FIELD (insn, 16, 18)]);
+ break;
+ case 'B':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ bb_cond_64_names[GET_FIELD (insn, 16, 16)]);
+
+ /* If the next character in args is 'n', it will handle
+ putting out the space. */
+ if (s[1] != 'n')
+ (*info->fprintf_func) (info->stream, " ");
+ break;
+ }
+ break;
+ }
+
+ case 'V':
+ fput_const (extract_5_store (insn), info);
+ break;
+ case 'r':
+ fput_const (extract_5r_store (insn), info);
+ break;
+ case 'R':
+ fput_const (extract_5R_store (insn), info);
+ break;
+ case 'U':
+ fput_const (extract_10U_store (insn), info);
+ break;
+ case 'B':
+ case 'Q':
+ fput_const (extract_5Q_store (insn), info);
+ break;
+ case 'i':
+ fput_const (extract_11 (insn), info);
+ break;
+ case 'j':
+ fput_const (extract_14 (insn), info);
+ break;
+ case 'k':
+ fputs_filtered ("L%", info);
+ fput_const (extract_21 (insn), info);
+ break;
+ case '<':
+ case 'l':
+ /* 16-bit long disp., PA2.0 wide only. */
+ fput_const (extract_16 (insn), info);
+ break;
+ case 'n':
+ if (insn & 0x2)
+ (*info->fprintf_func) (info->stream, ",n ");
+ else
+ (*info->fprintf_func) (info->stream, " ");
+ break;
+ case 'N':
+ if ((insn & 0x20) && s[1])
+ (*info->fprintf_func) (info->stream, ",n ");
+ else if (insn & 0x20)
+ (*info->fprintf_func) (info->stream, ",n");
+ else if (s[1])
+ (*info->fprintf_func) (info->stream, " ");
+ break;
+ case 'w':
+ (*info->print_address_func)
+ (memaddr + 8 + extract_12 (insn), info);
+ break;
+ case 'W':
+ /* 17 bit PC-relative branch. */
+ (*info->print_address_func)
+ ((memaddr + 8 + extract_17 (insn)), info);
+ break;
+ case 'z':
+ /* 17 bit displacement. This is an offset from a register
+ so it gets disasssembled as just a number, not any sort
+ of address. */
+ fput_const (extract_17 (insn), info);
+ break;
+
+ case 'Z':
+ /* addil %r1 implicit output. */
+ fputs_filtered ("r1", info);
+ break;
+
+ case 'Y':
+ /* be,l %sr0,%r31 implicit output. */
+ fputs_filtered ("sr0,r31", info);
+ break;
+
+ case '@':
+ (*info->fprintf_func) (info->stream, "0");
+ break;
+
+ case '.':
+ (*info->fprintf_func) (info->stream, "%d",
+ GET_FIELD (insn, 24, 25));
+ break;
+ case '*':
+ (*info->fprintf_func) (info->stream, "%d",
+ GET_FIELD (insn, 22, 25));
+ break;
+ case '!':
+ fputs_filtered ("sar", info);
+ break;
+ case 'p':
+ (*info->fprintf_func) (info->stream, "%d",
+ 31 - GET_FIELD (insn, 22, 26));
+ break;
+ case '~':
+ {
+ int num;
+ num = GET_FIELD (insn, 20, 20) << 5;
+ num |= GET_FIELD (insn, 22, 26);
+ (*info->fprintf_func) (info->stream, "%d", 63 - num);
+ break;
+ }
+ case 'P':
+ (*info->fprintf_func) (info->stream, "%d",
+ GET_FIELD (insn, 22, 26));
+ break;
+ case 'q':
+ {
+ int num;
+ num = GET_FIELD (insn, 20, 20) << 5;
+ num |= GET_FIELD (insn, 22, 26);
+ (*info->fprintf_func) (info->stream, "%d", num);
+ break;
+ }
+ case 'T':
+ (*info->fprintf_func) (info->stream, "%d",
+ 32 - GET_FIELD (insn, 27, 31));
+ break;
+ case '%':
+ {
+ int num;
+ num = (GET_FIELD (insn, 23, 23) + 1) * 32;
+ num -= GET_FIELD (insn, 27, 31);
+ (*info->fprintf_func) (info->stream, "%d", num);
+ break;
+ }
+ case '|':
+ {
+ int num;
+ num = (GET_FIELD (insn, 19, 19) + 1) * 32;
+ num -= GET_FIELD (insn, 27, 31);
+ (*info->fprintf_func) (info->stream, "%d", num);
+ break;
+ }
+ case '$':
+ fput_const (GET_FIELD (insn, 20, 28), info);
+ break;
+ case 'A':
+ fput_const (GET_FIELD (insn, 6, 18), info);
+ break;
+ case 'D':
+ fput_const (GET_FIELD (insn, 6, 31), info);
+ break;
+ case 'v':
+ (*info->fprintf_func) (info->stream, ",%d",
+ GET_FIELD (insn, 23, 25));
+ break;
+ case 'O':
+ fput_const ((GET_FIELD (insn, 6,20) << 5 |
+ GET_FIELD (insn, 27, 31)), info);
+ break;
+ case 'o':
+ fput_const (GET_FIELD (insn, 6, 20), info);
+ break;
+ case '2':
+ fput_const ((GET_FIELD (insn, 6, 22) << 5 |
+ GET_FIELD (insn, 27, 31)), info);
+ break;
+ case '1':
+ fput_const ((GET_FIELD (insn, 11, 20) << 5 |
+ GET_FIELD (insn, 27, 31)), info);
+ break;
+ case '0':
+ fput_const ((GET_FIELD (insn, 16, 20) << 5 |
+ GET_FIELD (insn, 27, 31)), info);
+ break;
+ case 'u':
+ (*info->fprintf_func) (info->stream, ",%d",
+ GET_FIELD (insn, 23, 25));
+ break;
+ case 'F':
+ /* If no destination completer and not before a completer
+ for fcmp, need a space here. */
+ if (s[1] == 'G' || s[1] == '?')
+ fputs_filtered
+ (float_format_names[GET_FIELD (insn, 19, 20)], info);
+ else
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ float_format_names[GET_FIELD (insn, 19, 20)]);
+ break;
+ case 'G':
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ float_format_names[GET_FIELD (insn, 17, 18)]);
+ break;
+ case 'H':
+ if (GET_FIELD (insn, 26, 26) == 1)
+ (*info->fprintf_func) (info->stream, "%s ",
+ float_format_names[0]);
+ else
+ (*info->fprintf_func) (info->stream, "%s ",
+ float_format_names[1]);
+ break;
+ case 'I':
+ /* If no destination completer and not before a completer
+ for fcmp, need a space here. */
+ if (s[1] == '?')
+ fputs_filtered
+ (float_format_names[GET_FIELD (insn, 20, 20)], info);
+ else
+ (*info->fprintf_func)
+ (info->stream, "%s ",
+ float_format_names[GET_FIELD (insn, 20, 20)]);
+ break;
+
+ case 'J':
+ fput_const (extract_14 (insn), info);
+ break;
+
+ case '#':
+ {
+ int sign = GET_FIELD (insn, 31, 31);
+ int imm10 = GET_FIELD (insn, 18, 27);
+ int disp;
+
+ if (sign)
+ disp = (-1 << 10) | imm10;
+ else
+ disp = imm10;
+
+ disp <<= 3;
+ fput_const (disp, info);
+ break;
+ }
+ case 'K':
+ case 'd':
+ {
+ int sign = GET_FIELD (insn, 31, 31);
+ int imm11 = GET_FIELD (insn, 18, 28);
+ int disp;
+
+ if (sign)
+ disp = (-1 << 11) | imm11;
+ else
+ disp = imm11;
+
+ disp <<= 2;
+ fput_const (disp, info);
+ break;
+ }
+
+ case '>':
+ case 'y':
+ {
+ /* 16-bit long disp., PA2.0 wide only. */
+ int disp = extract_16 (insn);
+ disp &= ~3;
+ fput_const (disp, info);
+ break;
+ }
+
+ case '&':
+ {
+ /* 16-bit long disp., PA2.0 wide only. */
+ int disp = extract_16 (insn);
+ disp &= ~7;
+ fput_const (disp, info);
+ break;
+ }
+
+ case '_':
+ break; /* Dealt with by '{' */
+
+ case '{':
+ {
+ int sub = GET_FIELD (insn, 14, 16);
+ int df = GET_FIELD (insn, 17, 18);
+ int sf = GET_FIELD (insn, 19, 20);
+ const char * const * source = float_format_names;
+ const char * const * dest = float_format_names;
+ const char *t = "";
+
+ if (sub == 4)
+ {
+ fputs_filtered (",UND ", info);
+ break;
+ }
+ if ((sub & 3) == 3)
+ t = ",t";
+ if ((sub & 3) == 1)
+ source = sub & 4 ? fcnv_ufixed_names : fcnv_fixed_names;
+ if (sub & 2)
+ dest = sub & 4 ? fcnv_ufixed_names : fcnv_fixed_names;
+
+ (*info->fprintf_func) (info->stream, "%s%s%s ",
+ t, source[sf], dest[df]);
+ break;
+ }
+
+ case 'm':
+ {
+ int y = GET_FIELD (insn, 16, 18);
+
+ if (y != 1)
+ fput_const ((y ^ 1) - 1, info);
+ }
+ break;
+
+ case 'h':
+ {
+ int cbit;
+
+ cbit = GET_FIELD (insn, 16, 18);
+
+ if (cbit > 0)
+ (*info->fprintf_func) (info->stream, ",%d", cbit - 1);
+ break;
+ }
+
+ case '=':
+ {
+ int cond = GET_FIELD (insn, 27, 31);
+
+ switch (cond)
+ {
+ case 0: fputs_filtered (" ", info); break;
+ case 1: fputs_filtered ("acc ", info); break;
+ case 2: fputs_filtered ("rej ", info); break;
+ case 5: fputs_filtered ("acc8 ", info); break;
+ case 6: fputs_filtered ("rej8 ", info); break;
+ case 9: fputs_filtered ("acc6 ", info); break;
+ case 13: fputs_filtered ("acc4 ", info); break;
+ case 17: fputs_filtered ("acc2 ", info); break;
+ default: break;
+ }
+ break;
+ }
+
+ case 'X':
+ (*info->print_address_func)
+ (memaddr + 8 + extract_22 (insn), info);
+ break;
+ case 'L':
+ fputs_filtered (",rp", info);
+ break;
+ default:
+ (*info->fprintf_func) (info->stream, "%c", *s);
+ break;
+ }
+ }
+ return sizeof (insn);
+ }
+ }
+ (*info->fprintf_func) (info->stream, "#%8x", insn);
+ return sizeof (insn);
+}
diff --git a/hppa.ld b/hppa.ld
new file mode 100644
index 0000000..9a4b22c
--- /dev/null
+++ b/hppa.ld
@@ -0,0 +1,213 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf32-hppa-linux", "elf32-hppa-linux",
+ "elf32-hppa-linux")
+OUTPUT_ARCH(hppa:hppa1.1)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = 0x60000000); . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data.rel.ro : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) }
+ .rela.data.rel.ro : { *(.rela.data.rel.ro* .rela.gnu.linkonce.d.rel.ro.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+ .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+ .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+ .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+ .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+ .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+ .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+ .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x08000240
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x08000240
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x08000240
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 :
+ {
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .PARISC.unwind : { *(.PARISC.unwind) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x10000) + (. & (0x10000 - 1));
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .preinit_array :
+ {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ }
+ .init_array :
+ {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+ .dynamic : { *(.dynamic) }
+ .data :
+ {
+ PROVIDE ($global$ = .);
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .; PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss :
+ {
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections.
+ FIXME: Why do we need it? When there is no .bss section, we don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ . = ALIGN(32 / 8);
+ . = ALIGN(32 / 8);
+ _end = .; PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/hw/9p.h b/hw/9p.h
new file mode 100644
index 0000000..d9951d6
--- /dev/null
+++ b/hw/9p.h
@@ -0,0 +1,24 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_9P_H
+#define QEMU_9P_H
+
+typedef struct V9fsConf
+{
+ /* tag name for the device */
+ char *tag;
+ char *fsdev_id;
+} V9fsConf;
+
+#endif
diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c
new file mode 100644
index 0000000..b5e5328
--- /dev/null
+++ b/hw/a9mpcore.c
@@ -0,0 +1,29 @@
+/*
+ * Cortex-A9MPCore internal peripheral emulation.
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* 64 external IRQ lines. */
+#define GIC_NIRQ 96
+#include "mpcore.c"
+
+static SysBusDeviceInfo mpcore_priv_info = {
+ .init = mpcore_priv_init,
+ .qdev.name = "a9mpcore_priv",
+ .qdev.size = sizeof(mpcore_priv_state),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void a9mpcore_register_devices(void)
+{
+ sysbus_register_withprop(&mpcore_priv_info);
+}
+
+device_init(a9mpcore_register_devices)
diff --git a/hw/ac97.c b/hw/ac97.c
new file mode 100644
index 0000000..d71072d
--- /dev/null
+++ b/hw/ac97.c
@@ -0,0 +1,1351 @@
+/*
+ * Copyright (C) 2006 InnoTek Systemberatung GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "pci.h"
+
+enum {
+ AC97_Reset = 0x00,
+ AC97_Master_Volume_Mute = 0x02,
+ AC97_Headphone_Volume_Mute = 0x04,
+ AC97_Master_Volume_Mono_Mute = 0x06,
+ AC97_Master_Tone_RL = 0x08,
+ AC97_PC_BEEP_Volume_Mute = 0x0A,
+ AC97_Phone_Volume_Mute = 0x0C,
+ AC97_Mic_Volume_Mute = 0x0E,
+ AC97_Line_In_Volume_Mute = 0x10,
+ AC97_CD_Volume_Mute = 0x12,
+ AC97_Video_Volume_Mute = 0x14,
+ AC97_Aux_Volume_Mute = 0x16,
+ AC97_PCM_Out_Volume_Mute = 0x18,
+ AC97_Record_Select = 0x1A,
+ AC97_Record_Gain_Mute = 0x1C,
+ AC97_Record_Gain_Mic_Mute = 0x1E,
+ AC97_General_Purpose = 0x20,
+ AC97_3D_Control = 0x22,
+ AC97_AC_97_RESERVED = 0x24,
+ AC97_Powerdown_Ctrl_Stat = 0x26,
+ AC97_Extended_Audio_ID = 0x28,
+ AC97_Extended_Audio_Ctrl_Stat = 0x2A,
+ AC97_PCM_Front_DAC_Rate = 0x2C,
+ AC97_PCM_Surround_DAC_Rate = 0x2E,
+ AC97_PCM_LFE_DAC_Rate = 0x30,
+ AC97_PCM_LR_ADC_Rate = 0x32,
+ AC97_MIC_ADC_Rate = 0x34,
+ AC97_6Ch_Vol_C_LFE_Mute = 0x36,
+ AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
+ AC97_Vendor_Reserved = 0x58,
+ AC97_Vendor_ID1 = 0x7c,
+ AC97_Vendor_ID2 = 0x7e
+};
+
+#define SOFT_VOLUME
+#define SR_FIFOE 16 /* rwc */
+#define SR_BCIS 8 /* rwc */
+#define SR_LVBCI 4 /* rwc */
+#define SR_CELV 2 /* ro */
+#define SR_DCH 1 /* ro */
+#define SR_VALID_MASK ((1 << 5) - 1)
+#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+#define SR_RO_MASK (SR_DCH | SR_CELV)
+#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+
+#define CR_IOCE 16 /* rw */
+#define CR_FEIE 8 /* rw */
+#define CR_LVBIE 4 /* rw */
+#define CR_RR 2 /* rw */
+#define CR_RPBM 1 /* rw */
+#define CR_VALID_MASK ((1 << 5) - 1)
+#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
+
+#define GC_WR 4 /* rw */
+#define GC_CR 2 /* rw */
+#define GC_VALID_MASK ((1 << 6) - 1)
+
+#define GS_MD3 (1<<17) /* rw */
+#define GS_AD3 (1<<16) /* rw */
+#define GS_RCS (1<<15) /* rwc */
+#define GS_B3S12 (1<<14) /* ro */
+#define GS_B2S12 (1<<13) /* ro */
+#define GS_B1S12 (1<<12) /* ro */
+#define GS_S1R1 (1<<11) /* rwc */
+#define GS_S0R1 (1<<10) /* rwc */
+#define GS_S1CR (1<<9) /* ro */
+#define GS_S0CR (1<<8) /* ro */
+#define GS_MINT (1<<7) /* ro */
+#define GS_POINT (1<<6) /* ro */
+#define GS_PIINT (1<<5) /* ro */
+#define GS_RSRVD ((1<<4)|(1<<3))
+#define GS_MOINT (1<<2) /* ro */
+#define GS_MIINT (1<<1) /* ro */
+#define GS_GSCI 1 /* rwc */
+#define GS_RO_MASK (GS_B3S12| \
+ GS_B2S12| \
+ GS_B1S12| \
+ GS_S1CR| \
+ GS_S0CR| \
+ GS_MINT| \
+ GS_POINT| \
+ GS_PIINT| \
+ GS_RSRVD| \
+ GS_MOINT| \
+ GS_MIINT)
+#define GS_VALID_MASK ((1 << 18) - 1)
+#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
+
+#define BD_IOC (1<<31)
+#define BD_BUP (1<<30)
+
+#define EACS_VRA 1
+#define EACS_VRM 8
+
+#define VOL_MASK 0x1f
+#define MUTE_SHIFT 15
+
+#define REC_MASK 7
+enum {
+ REC_MIC = 0,
+ REC_CD,
+ REC_VIDEO,
+ REC_AUX,
+ REC_LINE_IN,
+ REC_STEREO_MIX,
+ REC_MONO_MIX,
+ REC_PHONE
+};
+
+typedef struct BD {
+ uint32_t addr;
+ uint32_t ctl_len;
+} BD;
+
+typedef struct AC97BusMasterRegs {
+ uint32_t bdbar; /* rw 0 */
+ uint8_t civ; /* ro 0 */
+ uint8_t lvi; /* rw 0 */
+ uint16_t sr; /* rw 1 */
+ uint16_t picb; /* ro 0 */
+ uint8_t piv; /* ro 0 */
+ uint8_t cr; /* rw 0 */
+ unsigned int bd_valid;
+ BD bd;
+} AC97BusMasterRegs;
+
+typedef struct AC97LinkState {
+ PCIDevice dev;
+ QEMUSoundCard card;
+ uint32_t glob_cnt;
+ uint32_t glob_sta;
+ uint32_t cas;
+ uint32_t last_samp;
+ AC97BusMasterRegs bm_regs[3];
+ uint8_t mixer_data[256];
+ SWVoiceIn *voice_pi;
+ SWVoiceOut *voice_po;
+ SWVoiceIn *voice_mc;
+ int invalid_freq[3];
+ uint8_t silence[128];
+ uint32_t base[2];
+ int bup_flag;
+} AC97LinkState;
+
+enum {
+ BUP_SET = 1,
+ BUP_LAST = 2
+};
+
+#ifdef DEBUG_AC97
+#define dolog(...) AUD_log ("ac97", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#define MKREGS(prefix, start) \
+enum { \
+ prefix ## _BDBAR = start, \
+ prefix ## _CIV = start + 4, \
+ prefix ## _LVI = start + 5, \
+ prefix ## _SR = start + 6, \
+ prefix ## _PICB = start + 8, \
+ prefix ## _PIV = start + 10, \
+ prefix ## _CR = start + 11 \
+}
+
+enum {
+ PI_INDEX = 0,
+ PO_INDEX,
+ MC_INDEX,
+ LAST_INDEX
+};
+
+MKREGS (PI, PI_INDEX * 16);
+MKREGS (PO, PO_INDEX * 16);
+MKREGS (MC, MC_INDEX * 16);
+
+enum {
+ GLOB_CNT = 0x2c,
+ GLOB_STA = 0x30,
+ CAS = 0x34
+};
+
+#define GET_BM(index) (((index) >> 4) & 3)
+
+static void po_callback (void *opaque, int free);
+static void pi_callback (void *opaque, int avail);
+static void mc_callback (void *opaque, int avail);
+
+static void warm_reset (AC97LinkState *s)
+{
+ (void) s;
+}
+
+static void cold_reset (AC97LinkState * s)
+{
+ (void) s;
+}
+
+static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r)
+{
+ uint8_t b[8];
+
+ cpu_physical_memory_read (r->bdbar + r->civ * 8, b, 8);
+ r->bd_valid = 1;
+ r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3;
+ r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]);
+ r->picb = r->bd.ctl_len & 0xffff;
+ dolog ("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
+ r->civ, r->bd.addr, r->bd.ctl_len >> 16,
+ r->bd.ctl_len & 0xffff,
+ (r->bd.ctl_len & 0xffff) << 1);
+}
+
+static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr)
+{
+ int event = 0;
+ int level = 0;
+ uint32_t new_mask = new_sr & SR_INT_MASK;
+ uint32_t old_mask = r->sr & SR_INT_MASK;
+ uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT};
+
+ if (new_mask ^ old_mask) {
+ /** @todo is IRQ deasserted when only one of status bits is cleared? */
+ if (!new_mask) {
+ event = 1;
+ level = 0;
+ }
+ else {
+ if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) {
+ event = 1;
+ level = 1;
+ }
+ if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) {
+ event = 1;
+ level = 1;
+ }
+ }
+ }
+
+ r->sr = new_sr;
+
+ dolog ("IOC%d LVB%d sr=%#x event=%d level=%d\n",
+ r->sr & SR_BCIS, r->sr & SR_LVBCI,
+ r->sr,
+ event, level);
+
+ if (!event)
+ return;
+
+ if (level) {
+ s->glob_sta |= masks[r - s->bm_regs];
+ dolog ("set irq level=1\n");
+ qemu_set_irq (s->dev.irq[0], 1);
+ }
+ else {
+ s->glob_sta &= ~masks[r - s->bm_regs];
+ dolog ("set irq level=0\n");
+ qemu_set_irq (s->dev.irq[0], 0);
+ }
+}
+
+static void voice_set_active (AC97LinkState *s, int bm_index, int on)
+{
+ switch (bm_index) {
+ case PI_INDEX:
+ AUD_set_active_in (s->voice_pi, on);
+ break;
+
+ case PO_INDEX:
+ AUD_set_active_out (s->voice_po, on);
+ break;
+
+ case MC_INDEX:
+ AUD_set_active_in (s->voice_mc, on);
+ break;
+
+ default:
+ AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active", bm_index);
+ break;
+ }
+}
+
+static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r)
+{
+ dolog ("reset_bm_regs\n");
+ r->bdbar = 0;
+ r->civ = 0;
+ r->lvi = 0;
+ /** todo do we need to do that? */
+ update_sr (s, r, SR_DCH);
+ r->picb = 0;
+ r->piv = 0;
+ r->cr = r->cr & CR_DONT_CLEAR_MASK;
+ r->bd_valid = 0;
+
+ voice_set_active (s, r - s->bm_regs, 0);
+ memset (s->silence, 0, sizeof (s->silence));
+}
+
+static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v)
+{
+ if (i + 2 > sizeof (s->mixer_data)) {
+ dolog ("mixer_store: index %d out of bounds %zd\n",
+ i, sizeof (s->mixer_data));
+ return;
+ }
+
+ s->mixer_data[i + 0] = v & 0xff;
+ s->mixer_data[i + 1] = v >> 8;
+}
+
+static uint16_t mixer_load (AC97LinkState *s, uint32_t i)
+{
+ uint16_t val = 0xffff;
+
+ if (i + 2 > sizeof (s->mixer_data)) {
+ dolog ("mixer_store: index %d out of bounds %zd\n",
+ i, sizeof (s->mixer_data));
+ }
+ else {
+ val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8);
+ }
+
+ return val;
+}
+
+static void open_voice (AC97LinkState *s, int index, int freq)
+{
+ struct audsettings as;
+
+ as.freq = freq;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = 0;
+
+ if (freq > 0) {
+ s->invalid_freq[index] = 0;
+ switch (index) {
+ case PI_INDEX:
+ s->voice_pi = AUD_open_in (
+ &s->card,
+ s->voice_pi,
+ "ac97.pi",
+ s,
+ pi_callback,
+ &as
+ );
+ break;
+
+ case PO_INDEX:
+ s->voice_po = AUD_open_out (
+ &s->card,
+ s->voice_po,
+ "ac97.po",
+ s,
+ po_callback,
+ &as
+ );
+ break;
+
+ case MC_INDEX:
+ s->voice_mc = AUD_open_in (
+ &s->card,
+ s->voice_mc,
+ "ac97.mc",
+ s,
+ mc_callback,
+ &as
+ );
+ break;
+ }
+ }
+ else {
+ s->invalid_freq[index] = freq;
+ switch (index) {
+ case PI_INDEX:
+ AUD_close_in (&s->card, s->voice_pi);
+ s->voice_pi = NULL;
+ break;
+
+ case PO_INDEX:
+ AUD_close_out (&s->card, s->voice_po);
+ s->voice_po = NULL;
+ break;
+
+ case MC_INDEX:
+ AUD_close_in (&s->card, s->voice_mc);
+ s->voice_mc = NULL;
+ break;
+ }
+ }
+}
+
+static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX])
+{
+ uint16_t freq;
+
+ freq = mixer_load (s, AC97_PCM_LR_ADC_Rate);
+ open_voice (s, PI_INDEX, freq);
+ AUD_set_active_in (s->voice_pi, active[PI_INDEX]);
+
+ freq = mixer_load (s, AC97_PCM_Front_DAC_Rate);
+ open_voice (s, PO_INDEX, freq);
+ AUD_set_active_out (s->voice_po, active[PO_INDEX]);
+
+ freq = mixer_load (s, AC97_MIC_ADC_Rate);
+ open_voice (s, MC_INDEX, freq);
+ AUD_set_active_in (s->voice_mc, active[MC_INDEX]);
+}
+
+#ifdef USE_MIXER
+static void set_volume (AC97LinkState *s, int index,
+ audmixerctl_t mt, uint32_t val)
+{
+ int mute = (val >> MUTE_SHIFT) & 1;
+ uint8_t rvol = VOL_MASK - (val & VOL_MASK);
+ uint8_t lvol = VOL_MASK - ((val >> 8) & VOL_MASK);
+ rvol = 255 * rvol / VOL_MASK;
+ lvol = 255 * lvol / VOL_MASK;
+
+#ifdef SOFT_VOLUME
+ if (index == AC97_Master_Volume_Mute) {
+ AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
+ }
+ else {
+ AUD_set_volume (mt, &mute, &lvol, &rvol);
+ }
+#else
+ AUD_set_volume (mt, &mute, &lvol, &rvol);
+#endif
+
+ rvol = VOL_MASK - ((VOL_MASK * rvol) / 255);
+ lvol = VOL_MASK - ((VOL_MASK * lvol) / 255);
+ mixer_store (s, index, val);
+}
+
+static audrecsource_t ac97_to_aud_record_source (uint8_t i)
+{
+ switch (i) {
+ case REC_MIC:
+ return AUD_REC_MIC;
+
+ case REC_CD:
+ return AUD_REC_CD;
+
+ case REC_VIDEO:
+ return AUD_REC_VIDEO;
+
+ case REC_AUX:
+ return AUD_REC_AUX;
+
+ case REC_LINE_IN:
+ return AUD_REC_LINE_IN;
+
+ case REC_PHONE:
+ return AUD_REC_PHONE;
+
+ default:
+ dolog ("Unknown record source %d, using MIC\n", i);
+ return AUD_REC_MIC;
+ }
+}
+
+static uint8_t aud_to_ac97_record_source (audrecsource_t rs)
+{
+ switch (rs) {
+ case AUD_REC_MIC:
+ return REC_MIC;
+
+ case AUD_REC_CD:
+ return REC_CD;
+
+ case AUD_REC_VIDEO:
+ return REC_VIDEO;
+
+ case AUD_REC_AUX:
+ return REC_AUX;
+
+ case AUD_REC_LINE_IN:
+ return REC_LINE_IN;
+
+ case AUD_REC_PHONE:
+ return REC_PHONE;
+
+ default:
+ dolog ("Unknown audio recording source %d using MIC\n", rs);
+ return REC_MIC;
+ }
+}
+
+static void record_select (AC97LinkState *s, uint32_t val)
+{
+ uint8_t rs = val & REC_MASK;
+ uint8_t ls = (val >> 8) & REC_MASK;
+ audrecsource_t ars = ac97_to_aud_record_source (rs);
+ audrecsource_t als = ac97_to_aud_record_source (ls);
+ AUD_set_record_source (&als, &ars);
+ rs = aud_to_ac97_record_source (ars);
+ ls = aud_to_ac97_record_source (als);
+ mixer_store (s, AC97_Record_Select, rs | (ls << 8));
+}
+#endif
+
+static void mixer_reset (AC97LinkState *s)
+{
+ uint8_t active[LAST_INDEX];
+
+ dolog ("mixer_reset\n");
+ memset (s->mixer_data, 0, sizeof (s->mixer_data));
+ memset (active, 0, sizeof (active));
+ mixer_store (s, AC97_Reset , 0x0000); /* 6940 */
+ mixer_store (s, AC97_Master_Volume_Mono_Mute , 0x8000);
+ mixer_store (s, AC97_PC_BEEP_Volume_Mute , 0x0000);
+
+ mixer_store (s, AC97_Phone_Volume_Mute , 0x8008);
+ mixer_store (s, AC97_Mic_Volume_Mute , 0x8008);
+ mixer_store (s, AC97_CD_Volume_Mute , 0x8808);
+ mixer_store (s, AC97_Aux_Volume_Mute , 0x8808);
+ mixer_store (s, AC97_Record_Gain_Mic_Mute , 0x8000);
+ mixer_store (s, AC97_General_Purpose , 0x0000);
+ mixer_store (s, AC97_3D_Control , 0x0000);
+ mixer_store (s, AC97_Powerdown_Ctrl_Stat , 0x000f);
+
+ /*
+ * Sigmatel 9700 (STAC9700)
+ */
+ mixer_store (s, AC97_Vendor_ID1 , 0x8384);
+ mixer_store (s, AC97_Vendor_ID2 , 0x7600); /* 7608 */
+
+ mixer_store (s, AC97_Extended_Audio_ID , 0x0809);
+ mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
+ mixer_store (s, AC97_PCM_Front_DAC_Rate , 0xbb80);
+ mixer_store (s, AC97_PCM_Surround_DAC_Rate , 0xbb80);
+ mixer_store (s, AC97_PCM_LFE_DAC_Rate , 0xbb80);
+ mixer_store (s, AC97_PCM_LR_ADC_Rate , 0xbb80);
+ mixer_store (s, AC97_MIC_ADC_Rate , 0xbb80);
+
+#ifdef USE_MIXER
+ record_select (s, 0);
+ set_volume (s, AC97_Master_Volume_Mute, AUD_MIXER_VOLUME , 0x8000);
+ set_volume (s, AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM , 0x8808);
+ set_volume (s, AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN, 0x8808);
+#endif
+ reset_voices (s, active);
+}
+
+/**
+ * Native audio mixer
+ * I/O Reads
+ */
+static uint32_t nam_readb (void *opaque, uint32_t addr)
+{
+ AC97LinkState *s = opaque;
+ dolog ("U nam readb %#x\n", addr);
+ s->cas = 0;
+ return ~0U;
+}
+
+static uint32_t nam_readw (void *opaque, uint32_t addr)
+{
+ AC97LinkState *s = opaque;
+ uint32_t val = ~0U;
+ uint32_t index = addr - s->base[0];
+ s->cas = 0;
+ val = mixer_load (s, index);
+ return val;
+}
+
+static uint32_t nam_readl (void *opaque, uint32_t addr)
+{
+ AC97LinkState *s = opaque;
+ dolog ("U nam readl %#x\n", addr);
+ s->cas = 0;
+ return ~0U;
+}
+
+/**
+ * Native audio mixer
+ * I/O Writes
+ */
+static void nam_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+ AC97LinkState *s = opaque;
+ dolog ("U nam writeb %#x <- %#x\n", addr, val);
+ s->cas = 0;
+}
+
+static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
+{
+ AC97LinkState *s = opaque;
+ uint32_t index = addr - s->base[0];
+ s->cas = 0;
+ switch (index) {
+ case AC97_Reset:
+ mixer_reset (s);
+ break;
+ case AC97_Powerdown_Ctrl_Stat:
+ val &= ~0xf;
+ val |= mixer_load (s, index) & 0xf;
+ mixer_store (s, index, val);
+ break;
+#ifdef USE_MIXER
+ case AC97_Master_Volume_Mute:
+ set_volume (s, index, AUD_MIXER_VOLUME, val);
+ break;
+ case AC97_PCM_Out_Volume_Mute:
+ set_volume (s, index, AUD_MIXER_PCM, val);
+ break;
+ case AC97_Line_In_Volume_Mute:
+ set_volume (s, index, AUD_MIXER_LINE_IN, val);
+ break;
+ case AC97_Record_Select:
+ record_select (s, val);
+ break;
+#endif
+ case AC97_Vendor_ID1:
+ case AC97_Vendor_ID2:
+ dolog ("Attempt to write vendor ID to %#x\n", val);
+ break;
+ case AC97_Extended_Audio_ID:
+ dolog ("Attempt to write extended audio ID to %#x\n", val);
+ break;
+ case AC97_Extended_Audio_Ctrl_Stat:
+ if (!(val & EACS_VRA)) {
+ mixer_store (s, AC97_PCM_Front_DAC_Rate, 0xbb80);
+ mixer_store (s, AC97_PCM_LR_ADC_Rate, 0xbb80);
+ open_voice (s, PI_INDEX, 48000);
+ open_voice (s, PO_INDEX, 48000);
+ }
+ if (!(val & EACS_VRM)) {
+ mixer_store (s, AC97_MIC_ADC_Rate, 0xbb80);
+ open_voice (s, MC_INDEX, 48000);
+ }
+ dolog ("Setting extended audio control to %#x\n", val);
+ mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, val);
+ break;
+ case AC97_PCM_Front_DAC_Rate:
+ if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+ mixer_store (s, index, val);
+ dolog ("Set front DAC rate to %d\n", val);
+ open_voice (s, PO_INDEX, val);
+ }
+ else {
+ dolog ("Attempt to set front DAC rate to %d, "
+ "but VRA is not set\n",
+ val);
+ }
+ break;
+ case AC97_MIC_ADC_Rate:
+ if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) {
+ mixer_store (s, index, val);
+ dolog ("Set MIC ADC rate to %d\n", val);
+ open_voice (s, MC_INDEX, val);
+ }
+ else {
+ dolog ("Attempt to set MIC ADC rate to %d, "
+ "but VRM is not set\n",
+ val);
+ }
+ break;
+ case AC97_PCM_LR_ADC_Rate:
+ if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+ mixer_store (s, index, val);
+ dolog ("Set front LR ADC rate to %d\n", val);
+ open_voice (s, PI_INDEX, val);
+ }
+ else {
+ dolog ("Attempt to set LR ADC rate to %d, but VRA is not set\n",
+ val);
+ }
+ break;
+ default:
+ dolog ("U nam writew %#x <- %#x\n", addr, val);
+ mixer_store (s, index, val);
+ break;
+ }
+}
+
+static void nam_writel (void *opaque, uint32_t addr, uint32_t val)
+{
+ AC97LinkState *s = opaque;
+ dolog ("U nam writel %#x <- %#x\n", addr, val);
+ s->cas = 0;
+}
+
+/**
+ * Native audio bus master
+ * I/O Reads
+ */
+static uint32_t nabm_readb (void *opaque, uint32_t addr)
+{
+ AC97LinkState *s = opaque;
+ AC97BusMasterRegs *r = NULL;
+ uint32_t index = addr - s->base[1];
+ uint32_t val = ~0U;
+
+ switch (index) {
+ case CAS:
+ dolog ("CAS %d\n", s->cas);
+ val = s->cas;
+ s->cas = 1;
+ break;
+ case PI_CIV:
+ case PO_CIV:
+ case MC_CIV:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->civ;
+ dolog ("CIV[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ case PI_LVI:
+ case PO_LVI:
+ case MC_LVI:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->lvi;
+ dolog ("LVI[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ case PI_PIV:
+ case PO_PIV:
+ case MC_PIV:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->piv;
+ dolog ("PIV[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ case PI_CR:
+ case PO_CR:
+ case MC_CR:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->cr;
+ dolog ("CR[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ case PI_SR:
+ case PO_SR:
+ case MC_SR:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->sr & 0xff;
+ dolog ("SRb[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ default:
+ dolog ("U nabm readb %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+static uint32_t nabm_readw (void *opaque, uint32_t addr)
+{
+ AC97LinkState *s = opaque;
+ AC97BusMasterRegs *r = NULL;
+ uint32_t index = addr - s->base[1];
+ uint32_t val = ~0U;
+
+ switch (index) {
+ case PI_SR:
+ case PO_SR:
+ case MC_SR:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->sr;
+ dolog ("SR[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ case PI_PICB:
+ case PO_PICB:
+ case MC_PICB:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->picb;
+ dolog ("PICB[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ default:
+ dolog ("U nabm readw %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+static uint32_t nabm_readl (void *opaque, uint32_t addr)
+{
+ AC97LinkState *s = opaque;
+ AC97BusMasterRegs *r = NULL;
+ uint32_t index = addr - s->base[1];
+ uint32_t val = ~0U;
+
+ switch (index) {
+ case PI_BDBAR:
+ case PO_BDBAR:
+ case MC_BDBAR:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->bdbar;
+ dolog ("BMADDR[%d] -> %#x\n", GET_BM (index), val);
+ break;
+ case PI_CIV:
+ case PO_CIV:
+ case MC_CIV:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->civ | (r->lvi << 8) | (r->sr << 16);
+ dolog ("CIV LVI SR[%d] -> %#x, %#x, %#x\n", GET_BM (index),
+ r->civ, r->lvi, r->sr);
+ break;
+ case PI_PICB:
+ case PO_PICB:
+ case MC_PICB:
+ r = &s->bm_regs[GET_BM (index)];
+ val = r->picb | (r->piv << 16) | (r->cr << 24);
+ dolog ("PICB PIV CR[%d] -> %#x %#x %#x %#x\n", GET_BM (index),
+ val, r->picb, r->piv, r->cr);
+ break;
+ case GLOB_CNT:
+ val = s->glob_cnt;
+ dolog ("glob_cnt -> %#x\n", val);
+ break;
+ case GLOB_STA:
+ val = s->glob_sta | GS_S0CR;
+ dolog ("glob_sta -> %#x\n", val);
+ break;
+ default:
+ dolog ("U nabm readl %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+/**
+ * Native audio bus master
+ * I/O Writes
+ */
+static void nabm_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+ AC97LinkState *s = opaque;
+ AC97BusMasterRegs *r = NULL;
+ uint32_t index = addr - s->base[1];
+ switch (index) {
+ case PI_LVI:
+ case PO_LVI:
+ case MC_LVI:
+ r = &s->bm_regs[GET_BM (index)];
+ if ((r->cr & CR_RPBM) && (r->sr & SR_DCH)) {
+ r->sr &= ~(SR_DCH | SR_CELV);
+ r->civ = r->piv;
+ r->piv = (r->piv + 1) % 32;
+ fetch_bd (s, r);
+ }
+ r->lvi = val % 32;
+ dolog ("LVI[%d] <- %#x\n", GET_BM (index), val);
+ break;
+ case PI_CR:
+ case PO_CR:
+ case MC_CR:
+ r = &s->bm_regs[GET_BM (index)];
+ if (val & CR_RR) {
+ reset_bm_regs (s, r);
+ }
+ else {
+ r->cr = val & CR_VALID_MASK;
+ if (!(r->cr & CR_RPBM)) {
+ voice_set_active (s, r - s->bm_regs, 0);
+ r->sr |= SR_DCH;
+ }
+ else {
+ r->civ = r->piv;
+ r->piv = (r->piv + 1) % 32;
+ fetch_bd (s, r);
+ r->sr &= ~SR_DCH;
+ voice_set_active (s, r - s->bm_regs, 1);
+ }
+ }
+ dolog ("CR[%d] <- %#x (cr %#x)\n", GET_BM (index), val, r->cr);
+ break;
+ case PI_SR:
+ case PO_SR:
+ case MC_SR:
+ r = &s->bm_regs[GET_BM (index)];
+ r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+ update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
+ dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
+ break;
+ default:
+ dolog ("U nabm writeb %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+static void nabm_writew (void *opaque, uint32_t addr, uint32_t val)
+{
+ AC97LinkState *s = opaque;
+ AC97BusMasterRegs *r = NULL;
+ uint32_t index = addr - s->base[1];
+ switch (index) {
+ case PI_SR:
+ case PO_SR:
+ case MC_SR:
+ r = &s->bm_regs[GET_BM (index)];
+ r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+ update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
+ dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
+ break;
+ default:
+ dolog ("U nabm writew %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+static void nabm_writel (void *opaque, uint32_t addr, uint32_t val)
+{
+ AC97LinkState *s = opaque;
+ AC97BusMasterRegs *r = NULL;
+ uint32_t index = addr - s->base[1];
+ switch (index) {
+ case PI_BDBAR:
+ case PO_BDBAR:
+ case MC_BDBAR:
+ r = &s->bm_regs[GET_BM (index)];
+ r->bdbar = val & ~3;
+ dolog ("BDBAR[%d] <- %#x (bdbar %#x)\n",
+ GET_BM (index), val, r->bdbar);
+ break;
+ case GLOB_CNT:
+ if (val & GC_WR)
+ warm_reset (s);
+ if (val & GC_CR)
+ cold_reset (s);
+ if (!(val & (GC_WR | GC_CR)))
+ s->glob_cnt = val & GC_VALID_MASK;
+ dolog ("glob_cnt <- %#x (glob_cnt %#x)\n", val, s->glob_cnt);
+ break;
+ case GLOB_STA:
+ s->glob_sta &= ~(val & GS_WCLEAR_MASK);
+ s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
+ dolog ("glob_sta <- %#x (glob_sta %#x)\n", val, s->glob_sta);
+ break;
+ default:
+ dolog ("U nabm writel %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
+ int max, int *stop)
+{
+ uint8_t tmpbuf[4096];
+ uint32_t addr = r->bd.addr;
+ uint32_t temp = r->picb << 1;
+ uint32_t written = 0;
+ int to_copy = 0;
+ temp = audio_MIN (temp, max);
+
+ if (!temp) {
+ *stop = 1;
+ return 0;
+ }
+
+ while (temp) {
+ int copied;
+ to_copy = audio_MIN (temp, sizeof (tmpbuf));
+ cpu_physical_memory_read (addr, tmpbuf, to_copy);
+ copied = AUD_write (s->voice_po, tmpbuf, to_copy);
+ dolog ("write_audio max=%x to_copy=%x copied=%x\n",
+ max, to_copy, copied);
+ if (!copied) {
+ *stop = 1;
+ break;
+ }
+ temp -= copied;
+ addr += copied;
+ written += copied;
+ }
+
+ if (!temp) {
+ if (to_copy < 4) {
+ dolog ("whoops\n");
+ s->last_samp = 0;
+ }
+ else {
+ s->last_samp = *(uint32_t *) &tmpbuf[to_copy - 4];
+ }
+ }
+
+ r->bd.addr = addr;
+ return written;
+}
+
+static void write_bup (AC97LinkState *s, int elapsed)
+{
+ int written = 0;
+
+ dolog ("write_bup\n");
+ if (!(s->bup_flag & BUP_SET)) {
+ if (s->bup_flag & BUP_LAST) {
+ int i;
+ uint8_t *p = s->silence;
+ for (i = 0; i < sizeof (s->silence) / 4; i++, p += 4) {
+ *(uint32_t *) p = s->last_samp;
+ }
+ }
+ else {
+ memset (s->silence, 0, sizeof (s->silence));
+ }
+ s->bup_flag |= BUP_SET;
+ }
+
+ while (elapsed) {
+ int temp = audio_MIN (elapsed, sizeof (s->silence));
+ while (temp) {
+ int copied = AUD_write (s->voice_po, s->silence, temp);
+ if (!copied)
+ return;
+ temp -= copied;
+ elapsed -= copied;
+ written += copied;
+ }
+ }
+}
+
+static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
+ int max, int *stop)
+{
+ uint8_t tmpbuf[4096];
+ uint32_t addr = r->bd.addr;
+ uint32_t temp = r->picb << 1;
+ uint32_t nread = 0;
+ int to_copy = 0;
+ SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
+
+ temp = audio_MIN (temp, max);
+
+ if (!temp) {
+ *stop = 1;
+ return 0;
+ }
+
+ while (temp) {
+ int acquired;
+ to_copy = audio_MIN (temp, sizeof (tmpbuf));
+ acquired = AUD_read (voice, tmpbuf, to_copy);
+ if (!acquired) {
+ *stop = 1;
+ break;
+ }
+ cpu_physical_memory_write (addr, tmpbuf, acquired);
+ temp -= acquired;
+ addr += acquired;
+ nread += acquired;
+ }
+
+ r->bd.addr = addr;
+ return nread;
+}
+
+static void transfer_audio (AC97LinkState *s, int index, int elapsed)
+{
+ AC97BusMasterRegs *r = &s->bm_regs[index];
+ int written = 0, stop = 0;
+
+ if (s->invalid_freq[index]) {
+ AUD_log ("ac97", "attempt to use voice %d with invalid frequency %d\n",
+ index, s->invalid_freq[index]);
+ return;
+ }
+
+ if (r->sr & SR_DCH) {
+ if (r->cr & CR_RPBM) {
+ switch (index) {
+ case PO_INDEX:
+ write_bup (s, elapsed);
+ break;
+ }
+ }
+ return;
+ }
+
+ while ((elapsed >> 1) && !stop) {
+ int temp;
+
+ if (!r->bd_valid) {
+ dolog ("invalid bd\n");
+ fetch_bd (s, r);
+ }
+
+ if (!r->picb) {
+ dolog ("fresh bd %d is empty %#x %#x\n",
+ r->civ, r->bd.addr, r->bd.ctl_len);
+ if (r->civ == r->lvi) {
+ r->sr |= SR_DCH; /* CELV? */
+ s->bup_flag = 0;
+ break;
+ }
+ r->sr &= ~SR_CELV;
+ r->civ = r->piv;
+ r->piv = (r->piv + 1) % 32;
+ fetch_bd (s, r);
+ return;
+ }
+
+ switch (index) {
+ case PO_INDEX:
+ temp = write_audio (s, r, elapsed, &stop);
+ written += temp;
+ elapsed -= temp;
+ r->picb -= (temp >> 1);
+ break;
+
+ case PI_INDEX:
+ case MC_INDEX:
+ temp = read_audio (s, r, elapsed, &stop);
+ elapsed -= temp;
+ r->picb -= (temp >> 1);
+ break;
+ }
+
+ if (!r->picb) {
+ uint32_t new_sr = r->sr & ~SR_CELV;
+
+ if (r->bd.ctl_len & BD_IOC) {
+ new_sr |= SR_BCIS;
+ }
+
+ if (r->civ == r->lvi) {
+ dolog ("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi);
+
+ new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
+ stop = 1;
+ s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
+ }
+ else {
+ r->civ = r->piv;
+ r->piv = (r->piv + 1) % 32;
+ fetch_bd (s, r);
+ }
+
+ update_sr (s, r, new_sr);
+ }
+ }
+}
+
+static void pi_callback (void *opaque, int avail)
+{
+ transfer_audio (opaque, PI_INDEX, avail);
+}
+
+static void mc_callback (void *opaque, int avail)
+{
+ transfer_audio (opaque, MC_INDEX, avail);
+}
+
+static void po_callback (void *opaque, int free)
+{
+ transfer_audio (opaque, PO_INDEX, free);
+}
+
+static const VMStateDescription vmstate_ac97_bm_regs = {
+ .name = "ac97_bm_regs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(bdbar, AC97BusMasterRegs),
+ VMSTATE_UINT8(civ, AC97BusMasterRegs),
+ VMSTATE_UINT8(lvi, AC97BusMasterRegs),
+ VMSTATE_UINT16(sr, AC97BusMasterRegs),
+ VMSTATE_UINT16(picb, AC97BusMasterRegs),
+ VMSTATE_UINT8(piv, AC97BusMasterRegs),
+ VMSTATE_UINT8(cr, AC97BusMasterRegs),
+ VMSTATE_UINT32(bd_valid, AC97BusMasterRegs),
+ VMSTATE_UINT32(bd.addr, AC97BusMasterRegs),
+ VMSTATE_UINT32(bd.ctl_len, AC97BusMasterRegs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ac97_post_load (void *opaque, int version_id)
+{
+ uint8_t active[LAST_INDEX];
+ AC97LinkState *s = opaque;
+
+#ifdef USE_MIXER
+ record_select (s, mixer_load (s, AC97_Record_Select));
+#define V_(a, b) set_volume (s, a, b, mixer_load (s, a))
+ V_ (AC97_Master_Volume_Mute, AUD_MIXER_VOLUME);
+ V_ (AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM);
+ V_ (AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN);
+#undef V_
+#endif
+ active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM);
+ active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM);
+ active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM);
+ reset_voices (s, active);
+
+ s->bup_flag = 0;
+ s->last_samp = 0;
+ return 0;
+}
+
+static bool is_version_2 (void *opaque, int version_id)
+{
+ return version_id == 2;
+}
+
+static const VMStateDescription vmstate_ac97 = {
+ .name = "ac97",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .post_load = ac97_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, AC97LinkState),
+ VMSTATE_UINT32(glob_cnt, AC97LinkState),
+ VMSTATE_UINT32(glob_sta, AC97LinkState),
+ VMSTATE_UINT32(cas, AC97LinkState),
+ VMSTATE_STRUCT_ARRAY(bm_regs, AC97LinkState, 3, 1,
+ vmstate_ac97_bm_regs, AC97BusMasterRegs),
+ VMSTATE_BUFFER(mixer_data, AC97LinkState),
+ VMSTATE_UNUSED_TEST(is_version_2, 3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ac97_map (PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, pci_dev);
+ PCIDevice *d = &s->dev;
+
+ if (!region_num) {
+ s->base[0] = addr;
+ register_ioport_read (addr, 256 * 1, 1, nam_readb, d);
+ register_ioport_read (addr, 256 * 2, 2, nam_readw, d);
+ register_ioport_read (addr, 256 * 4, 4, nam_readl, d);
+ register_ioport_write (addr, 256 * 1, 1, nam_writeb, d);
+ register_ioport_write (addr, 256 * 2, 2, nam_writew, d);
+ register_ioport_write (addr, 256 * 4, 4, nam_writel, d);
+ }
+ else {
+ s->base[1] = addr;
+ register_ioport_read (addr, 64 * 1, 1, nabm_readb, d);
+ register_ioport_read (addr, 64 * 2, 2, nabm_readw, d);
+ register_ioport_read (addr, 64 * 4, 4, nabm_readl, d);
+ register_ioport_write (addr, 64 * 1, 1, nabm_writeb, d);
+ register_ioport_write (addr, 64 * 2, 2, nabm_writew, d);
+ register_ioport_write (addr, 64 * 4, 4, nabm_writel, d);
+ }
+}
+
+static void ac97_on_reset (void *opaque)
+{
+ AC97LinkState *s = opaque;
+
+ reset_bm_regs (s, &s->bm_regs[0]);
+ reset_bm_regs (s, &s->bm_regs[1]);
+ reset_bm_regs (s, &s->bm_regs[2]);
+
+ /*
+ * Reset the mixer too. The Windows XP driver seems to rely on
+ * this. At least it wants to read the vendor id before it resets
+ * the codec manually.
+ */
+ mixer_reset (s);
+}
+
+static int ac97_initfn (PCIDevice *dev)
+{
+ AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev);
+ uint8_t *c = s->dev.config;
+
+ pci_config_set_vendor_id (c, PCI_VENDOR_ID_INTEL); /* ro */
+ pci_config_set_device_id (c, PCI_DEVICE_ID_INTEL_82801AA_5); /* ro */
+
+ /* TODO: no need to override */
+ c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */
+ c[PCI_COMMAND + 1] = 0x00;
+
+ /* TODO: */
+ c[PCI_STATUS] = PCI_STATUS_FAST_BACK; /* pcists pci status rwc, ro */
+ c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8;
+
+ c[PCI_REVISION_ID] = 0x01; /* rid revision ro */
+ c[PCI_CLASS_PROG] = 0x00; /* pi programming interface ro */
+ pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO); /* ro */
+
+ /* TODO set when bar is registered. no need to override. */
+ /* nabmar native audio mixer base address rw */
+ c[PCI_BASE_ADDRESS_0] = PCI_BASE_ADDRESS_SPACE_IO;
+ c[PCI_BASE_ADDRESS_0 + 1] = 0x00;
+ c[PCI_BASE_ADDRESS_0 + 2] = 0x00;
+ c[PCI_BASE_ADDRESS_0 + 3] = 0x00;
+
+ /* TODO set when bar is registered. no need to override. */
+ /* nabmbar native audio bus mastering base address rw */
+ c[PCI_BASE_ADDRESS_0 + 4] = PCI_BASE_ADDRESS_SPACE_IO;
+ c[PCI_BASE_ADDRESS_0 + 5] = 0x00;
+ c[PCI_BASE_ADDRESS_0 + 6] = 0x00;
+ c[PCI_BASE_ADDRESS_0 + 7] = 0x00;
+
+ c[PCI_SUBSYSTEM_VENDOR_ID] = 0x86; /* svid subsystem vendor id rwo */
+ c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x80;
+
+ c[PCI_SUBSYSTEM_ID] = 0x00; /* sid subsystem id rwo */
+ c[PCI_SUBSYSTEM_ID + 1] = 0x00;
+
+ c[PCI_INTERRUPT_LINE] = 0x00; /* intr_ln interrupt line rw */
+ /* TODO: RST# value should be 0. */
+ c[PCI_INTERRUPT_PIN] = 0x01; /* intr_pn interrupt pin ro */
+
+ pci_register_bar (&s->dev, 0, 256 * 4, PCI_BASE_ADDRESS_SPACE_IO,
+ ac97_map);
+ pci_register_bar (&s->dev, 1, 64 * 4, PCI_BASE_ADDRESS_SPACE_IO, ac97_map);
+ qemu_register_reset (ac97_on_reset, s);
+ AUD_register_card ("ac97", &s->card);
+ ac97_on_reset (s);
+ return 0;
+}
+
+int ac97_init (PCIBus *bus)
+{
+ pci_create_simple (bus, -1, "AC97");
+ return 0;
+}
+
+static PCIDeviceInfo ac97_info = {
+ .qdev.name = "AC97",
+ .qdev.desc = "Intel 82801AA AC97 Audio",
+ .qdev.size = sizeof (AC97LinkState),
+ .qdev.vmsd = &vmstate_ac97,
+ .init = ac97_initfn,
+};
+
+static void ac97_register (void)
+{
+ pci_qdev_register (&ac97_info);
+}
+device_init (ac97_register);
+
diff --git a/hw/acpi.c b/hw/acpi.c
new file mode 100644
index 0000000..237526d
--- /dev/null
+++ b/hw/acpi.c
@@ -0,0 +1,202 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 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 version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+#include "hw.h"
+#include "pc.h"
+#include "acpi.h"
+#include "kvm.h"
+#include "qemu-kvm.h"
+#include "string.h"
+
+struct acpi_table_header
+{
+ char signature [4]; /* ACPI signature (4 ASCII characters) */
+ uint32_t length; /* Length of table, in bytes, including header */
+ uint8_t revision; /* ACPI Specification minor version # */
+ uint8_t checksum; /* To make sum of entire table == 0 */
+ char oem_id [6]; /* OEM identification */
+ char oem_table_id [8]; /* OEM table identification */
+ uint32_t oem_revision; /* OEM revision number */
+ char asl_compiler_id [4]; /* ASL compiler vendor ID */
+ uint32_t asl_compiler_revision; /* ASL compiler revision number */
+} __attribute__((packed));
+
+char *acpi_tables;
+size_t acpi_tables_len;
+
+static int acpi_checksum(const uint8_t *data, int len)
+{
+ int sum, i;
+ sum = 0;
+ for(i = 0; i < len; i++)
+ sum += data[i];
+ return (-sum) & 0xff;
+}
+
+int acpi_table_add(const char *t)
+{
+ static const char *dfl_id = "QEMUQEMU";
+ char buf[1024], *p, *f;
+ struct acpi_table_header acpi_hdr;
+ unsigned long val;
+ uint32_t length;
+ struct acpi_table_header *acpi_hdr_p;
+ size_t off;
+
+ memset(&acpi_hdr, 0, sizeof(acpi_hdr));
+
+ if (get_param_value(buf, sizeof(buf), "sig", t)) {
+ strncpy(acpi_hdr.signature, buf, 4);
+ } else {
+ strncpy(acpi_hdr.signature, dfl_id, 4);
+ }
+ if (get_param_value(buf, sizeof(buf), "rev", t)) {
+ val = strtoul(buf, &p, 10);
+ if (val > 255 || *p != '\0')
+ goto out;
+ } else {
+ val = 1;
+ }
+ acpi_hdr.revision = (int8_t)val;
+
+ if (get_param_value(buf, sizeof(buf), "oem_id", t)) {
+ strncpy(acpi_hdr.oem_id, buf, 6);
+ } else {
+ strncpy(acpi_hdr.oem_id, dfl_id, 6);
+ }
+
+ if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) {
+ strncpy(acpi_hdr.oem_table_id, buf, 8);
+ } else {
+ strncpy(acpi_hdr.oem_table_id, dfl_id, 8);
+ }
+
+ if (get_param_value(buf, sizeof(buf), "oem_rev", t)) {
+ val = strtol(buf, &p, 10);
+ if(*p != '\0')
+ goto out;
+ } else {
+ val = 1;
+ }
+ acpi_hdr.oem_revision = cpu_to_le32(val);
+
+ if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) {
+ strncpy(acpi_hdr.asl_compiler_id, buf, 4);
+ } else {
+ strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4);
+ }
+
+ if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) {
+ val = strtol(buf, &p, 10);
+ if(*p != '\0')
+ goto out;
+ } else {
+ val = 1;
+ }
+ acpi_hdr.asl_compiler_revision = cpu_to_le32(val);
+
+ if (!get_param_value(buf, sizeof(buf), "data", t)) {
+ buf[0] = '\0';
+ }
+
+ length = sizeof(acpi_hdr);
+
+ f = buf;
+ while (buf[0]) {
+ struct stat s;
+ char *n = strchr(f, ':');
+ if (n)
+ *n = '\0';
+ if(stat(f, &s) < 0) {
+ fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno));
+ goto out;
+ }
+ length += s.st_size;
+ if (!n)
+ break;
+ *n = ':';
+ f = n + 1;
+ }
+
+ if (!acpi_tables) {
+ acpi_tables_len = sizeof(uint16_t);
+ acpi_tables = qemu_mallocz(acpi_tables_len);
+ }
+ acpi_tables = qemu_realloc(acpi_tables,
+ acpi_tables_len + sizeof(uint16_t) + length);
+ p = acpi_tables + acpi_tables_len;
+ acpi_tables_len += sizeof(uint16_t) + length;
+
+ *(uint16_t*)p = cpu_to_le32(length);
+ p += sizeof(uint16_t);
+ memcpy(p, &acpi_hdr, sizeof(acpi_hdr));
+ off = sizeof(acpi_hdr);
+
+ f = buf;
+ while (buf[0]) {
+ struct stat s;
+ int fd;
+ char *n = strchr(f, ':');
+ if (n)
+ *n = '\0';
+ fd = open(f, O_RDONLY);
+
+ if(fd < 0)
+ goto out;
+ if(fstat(fd, &s) < 0) {
+ close(fd);
+ goto out;
+ }
+
+ /* off < length is necessary because file size can be changed
+ under our foot */
+ while(s.st_size && off < length) {
+ int r;
+ r = read(fd, p + off, s.st_size);
+ if (r > 0) {
+ off += r;
+ s.st_size -= r;
+ } else if ((r < 0 && errno != EINTR) || r == 0) {
+ close(fd);
+ goto out;
+ }
+ }
+
+ close(fd);
+ if (!n)
+ break;
+ f = n + 1;
+ }
+ if (off < length) {
+ /* don't pass random value in process to guest */
+ memset(p + off, 0, length - off);
+ }
+
+ acpi_hdr_p = (struct acpi_table_header*)p;
+ acpi_hdr_p->length = cpu_to_le32(length);
+ acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length);
+ /* increase number of tables */
+ (*(uint16_t*)acpi_tables) =
+ cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1);
+ return 0;
+out:
+ if (acpi_tables) {
+ qemu_free(acpi_tables);
+ acpi_tables = NULL;
+ }
+ return -1;
+}
diff --git a/hw/acpi.h b/hw/acpi.h
new file mode 100644
index 0000000..5949958
--- /dev/null
+++ b/hw/acpi.h
@@ -0,0 +1,78 @@
+#ifndef QEMU_HW_ACPI_H
+#define QEMU_HW_ACPI_H
+/*
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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/>.
+ */
+
+/* from linux include/acpi/actype.h */
+/* Default ACPI register widths */
+
+#define ACPI_GPE_REGISTER_WIDTH 8
+#define ACPI_PM1_REGISTER_WIDTH 16
+#define ACPI_PM2_REGISTER_WIDTH 8
+#define ACPI_PM_TIMER_WIDTH 32
+
+/* PM Timer ticks per second (HZ) */
+#define PM_TIMER_FREQUENCY 3579545
+
+
+/* ACPI fixed hardware registers */
+
+/* from linux/drivers/acpi/acpica/aclocal.h */
+/* Masks used to access the bit_registers */
+
+/* PM1x_STS */
+#define ACPI_BITMASK_TIMER_STATUS 0x0001
+#define ACPI_BITMASK_BUS_MASTER_STATUS 0x0010
+#define ACPI_BITMASK_GLOBAL_LOCK_STATUS 0x0020
+#define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100
+#define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200
+#define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400
+#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */
+#define ACPI_BITMASK_WAKE_STATUS 0x8000
+
+#define ACPI_BITMASK_ALL_FIXED_STATUS (\
+ ACPI_BITMASK_TIMER_STATUS | \
+ ACPI_BITMASK_BUS_MASTER_STATUS | \
+ ACPI_BITMASK_GLOBAL_LOCK_STATUS | \
+ ACPI_BITMASK_POWER_BUTTON_STATUS | \
+ ACPI_BITMASK_SLEEP_BUTTON_STATUS | \
+ ACPI_BITMASK_RT_CLOCK_STATUS | \
+ ACPI_BITMASK_WAKE_STATUS)
+
+/* PM1x_EN */
+#define ACPI_BITMASK_TIMER_ENABLE 0x0001
+#define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020
+#define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100
+#define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200
+#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400
+#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */
+
+/* PM1x_CNT */
+#define ACPI_BITMASK_SCI_ENABLE 0x0001
+#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002
+#define ACPI_BITMASK_GLOBAL_LOCK_RELEASE 0x0004
+#define ACPI_BITMASK_SLEEP_TYPE 0x1C00
+#define ACPI_BITMASK_SLEEP_ENABLE 0x2000
+
+/* PM2_CNT */
+#define ACPI_BITMASK_ARB_DISABLE 0x0001
+
+/* PM_TMR */
+
+#endif /* !QEMU_HW_ACPI_H */
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
new file mode 100644
index 0000000..dde8646
--- /dev/null
+++ b/hw/acpi_piix4.c
@@ -0,0 +1,739 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 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 version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+#include "hw.h"
+#include "pc.h"
+#include "apm.h"
+#include "pm_smbus.h"
+#include "pci.h"
+#include "acpi.h"
+#include "sysemu.h"
+#include "range.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+# define PIIX4_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
+#else
+# define PIIX4_DPRINTF(format, ...) do { } while (0)
+#endif
+
+#define ACPI_DBG_IO_ADDR 0xb044
+
+#define GPE_BASE 0xafe0
+#define PROC_BASE 0xaf00
+#define PCI_BASE 0xae00
+#define PCI_EJ_BASE 0xae08
+#define PCI_RMV_BASE 0xae0c
+
+#define PIIX4_CPU_HOTPLUG_STATUS 4
+#define PIIX4_PCI_HOTPLUG_STATUS 2
+
+struct gpe_regs {
+ uint16_t sts; /* status */
+ uint16_t en; /* enabled */
+ uint8_t cpus_sts[32];
+};
+
+struct pci_status {
+ uint32_t up;
+ uint32_t down;
+};
+
+typedef struct PIIX4PMState {
+ PCIDevice dev;
+ IORange ioport;
+ uint16_t pmsts;
+ uint16_t pmen;
+ uint16_t pmcntrl;
+
+ APMState apm;
+
+ QEMUTimer *tmr_timer;
+ int64_t tmr_overflow_time;
+
+ PMSMBus smb;
+ uint32_t smb_io_base;
+
+ qemu_irq irq;
+ qemu_irq cmos_s3;
+ qemu_irq smi_irq;
+ int kvm_enabled;
+
+ /* for pci hotplug */
+ struct gpe_regs gpe;
+ struct pci_status pci0_status;
+ uint32_t pci0_hotplug_enable;
+} PIIX4PMState;
+
+static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s);
+
+#define ACPI_ENABLE 0xf1
+#define ACPI_DISABLE 0xf0
+
+static uint32_t get_pmtmr(PIIX4PMState *s)
+{
+ uint32_t d;
+ d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec());
+ return d & 0xffffff;
+}
+
+static int get_pmsts(PIIX4PMState *s)
+{
+ int64_t d;
+
+ d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY,
+ get_ticks_per_sec());
+ if (d >= s->tmr_overflow_time)
+ s->pmsts |= ACPI_BITMASK_TIMER_STATUS;
+ return s->pmsts;
+}
+
+static void pm_update_sci(PIIX4PMState *s)
+{
+ int sci_level, pmsts;
+ int64_t expire_time;
+
+ pmsts = get_pmsts(s);
+ sci_level = (((pmsts & s->pmen) &
+ (ACPI_BITMASK_RT_CLOCK_ENABLE |
+ ACPI_BITMASK_POWER_BUTTON_ENABLE |
+ ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
+ ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
+ (((s->gpe.sts & s->gpe.en) &
+ (PIIX4_CPU_HOTPLUG_STATUS | PIIX4_PCI_HOTPLUG_STATUS)) != 0);
+
+ qemu_set_irq(s->irq, sci_level);
+ /* schedule a timer interruption if needed */
+ if ((s->pmen & ACPI_BITMASK_TIMER_ENABLE) &&
+ !(pmsts & ACPI_BITMASK_TIMER_STATUS)) {
+ expire_time = muldiv64(s->tmr_overflow_time, get_ticks_per_sec(),
+ PM_TIMER_FREQUENCY);
+ qemu_mod_timer(s->tmr_timer, expire_time);
+ } else {
+ qemu_del_timer(s->tmr_timer);
+ }
+}
+
+static void pm_tmr_timer(void *opaque)
+{
+ PIIX4PMState *s = opaque;
+ pm_update_sci(s);
+}
+
+static void pm_ioport_write(IORange *ioport, uint64_t addr, unsigned width,
+ uint64_t val)
+{
+ PIIX4PMState *s = container_of(ioport, PIIX4PMState, ioport);
+
+ if (width != 2) {
+ PIIX4_DPRINTF("PM write port=0x%04x width=%d val=0x%08x\n",
+ (unsigned)addr, width, (unsigned)val);
+ }
+
+ switch(addr) {
+ case 0x00:
+ {
+ int64_t d;
+ int pmsts;
+ pmsts = get_pmsts(s);
+ if (pmsts & val & ACPI_BITMASK_TIMER_STATUS) {
+ /* if TMRSTS is reset, then compute the new overflow time */
+ d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY,
+ get_ticks_per_sec());
+ s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+ }
+ s->pmsts &= ~val;
+ pm_update_sci(s);
+ }
+ break;
+ case 0x02:
+ s->pmen = val;
+ pm_update_sci(s);
+ break;
+ case 0x04:
+ {
+ int sus_typ;
+ s->pmcntrl = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+ if (val & ACPI_BITMASK_SLEEP_ENABLE) {
+ /* change suspend type */
+ sus_typ = (val >> 10) & 7;
+ switch(sus_typ) {
+ case 0: /* soft power off */
+ qemu_system_shutdown_request();
+ break;
+ case 1:
+ /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
+ Pretend that resume was caused by power button */
+ s->pmsts |= (ACPI_BITMASK_WAKE_STATUS |
+ ACPI_BITMASK_POWER_BUTTON_STATUS);
+ qemu_system_reset_request();
+ if (s->cmos_s3) {
+ qemu_irq_raise(s->cmos_s3);
+ }
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ PIIX4_DPRINTF("PM writew port=0x%04x val=0x%04x\n", addr, val);
+}
+
+static void pm_ioport_read(IORange *ioport, uint64_t addr, unsigned width,
+ uint64_t *data)
+{
+ PIIX4PMState *s = container_of(ioport, PIIX4PMState, ioport);
+ uint32_t val;
+
+ switch(addr) {
+ case 0x00:
+ val = get_pmsts(s);
+ break;
+ case 0x02:
+ val = s->pmen;
+ break;
+ case 0x04:
+ val = s->pmcntrl;
+ break;
+ case 0x08:
+ val = get_pmtmr(s);
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ PIIX4_DPRINTF("PM readw port=0x%04x val=0x%04x\n", addr, val);
+ *data = val;
+}
+
+static const IORangeOps pm_iorange_ops = {
+ .read = pm_ioport_read,
+ .write = pm_ioport_write,
+};
+
+static void apm_ctrl_changed(uint32_t val, void *arg)
+{
+ PIIX4PMState *s = arg;
+
+ /* ACPI specs 3.0, 4.7.2.5 */
+ if (val == ACPI_ENABLE) {
+ s->pmcntrl |= ACPI_BITMASK_SCI_ENABLE;
+ } else if (val == ACPI_DISABLE) {
+ s->pmcntrl &= ~ACPI_BITMASK_SCI_ENABLE;
+ }
+
+ if (s->dev.config[0x5b] & (1 << 1)) {
+ if (s->smi_irq) {
+ qemu_irq_raise(s->smi_irq);
+ }
+ }
+}
+
+static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ PIIX4_DPRINTF("ACPI: DBG: 0x%08x\n", val);
+}
+
+static void pm_io_space_update(PIIX4PMState *s)
+{
+ uint32_t pm_io_base;
+
+ if (s->dev.config[0x80] & 1) {
+ pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40));
+ pm_io_base &= 0xffc0;
+
+ /* XXX: need to improve memory and ioport allocation */
+ PIIX4_DPRINTF("PM: mapping to 0x%x\n", pm_io_base);
+ iorange_init(&s->ioport, &pm_iorange_ops, pm_io_base, 64);
+ ioport_register(&s->ioport);
+ }
+}
+
+static void pm_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ pci_default_write_config(d, address, val, len);
+ if (range_covers_byte(address, len, 0x80))
+ pm_io_space_update((PIIX4PMState *)d);
+}
+
+static int vmstate_acpi_post_load(void *opaque, int version_id)
+{
+ PIIX4PMState *s = opaque;
+
+ pm_io_space_update(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_gpe = {
+ .name = "gpe",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(sts, struct gpe_regs),
+ VMSTATE_UINT16(en, struct gpe_regs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pci_status = {
+ .name = "pci_status",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(up, struct pci_status),
+ VMSTATE_UINT32(down, struct pci_status),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_acpi = {
+ .name = "piix4_pm",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = vmstate_acpi_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PIIX4PMState),
+ VMSTATE_UINT16(pmsts, PIIX4PMState),
+ VMSTATE_UINT16(pmen, PIIX4PMState),
+ VMSTATE_UINT16(pmcntrl, PIIX4PMState),
+ VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
+ VMSTATE_TIMER(tmr_timer, PIIX4PMState),
+ VMSTATE_INT64(tmr_overflow_time, PIIX4PMState),
+ VMSTATE_STRUCT(gpe, PIIX4PMState, 2, vmstate_gpe, struct gpe_regs),
+ VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status,
+ struct pci_status),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void piix4_update_hotplug(PIIX4PMState *s)
+{
+ PCIDevice *dev = &s->dev;
+ BusState *bus = qdev_get_parent_bus(&dev->qdev);
+ DeviceState *qdev, *next;
+
+ s->pci0_hotplug_enable = ~0;
+
+ QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
+ PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
+ PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
+ int slot = PCI_SLOT(pdev->devfn);
+
+ if (info->no_hotplug) {
+ s->pci0_hotplug_enable &= ~(1 << slot);
+ }
+ }
+}
+
+static void piix4_reset(void *opaque)
+{
+ PIIX4PMState *s = opaque;
+ uint8_t *pci_conf = s->dev.config;
+
+ pci_conf[0x58] = 0;
+ pci_conf[0x59] = 0;
+ pci_conf[0x5a] = 0;
+ pci_conf[0x5b] = 0;
+
+ if (s->kvm_enabled) {
+ /* Mark SMM as already inited (until KVM supports SMM). */
+ pci_conf[0x5B] = 0x02;
+ }
+ piix4_update_hotplug(s);
+}
+
+static void piix4_powerdown(void *opaque, int irq, int power_failing)
+{
+ PIIX4PMState *s = opaque;
+
+ if (!s) {
+ qemu_system_shutdown_request();
+ } else if (s->pmen & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
+ s->pmsts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
+ pm_update_sci(s);
+ }
+}
+
+static PIIX4PMState *global_piix4_pm_state; /* cpu hotadd */
+
+static int piix4_pm_initfn(PCIDevice *dev)
+{
+ PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev);
+ uint8_t *pci_conf;
+
+ /* for cpu hotadd */
+ global_piix4_pm_state = s;
+
+ pci_conf = s->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_3);
+ pci_conf[0x06] = 0x80;
+ pci_conf[0x07] = 0x02;
+ pci_conf[0x08] = 0x03; // revision number
+ pci_conf[0x09] = 0x00;
+ pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER);
+ pci_conf[0x3d] = 0x01; // interrupt pin 1
+
+ pci_conf[0x40] = 0x01; /* PM io base read only bit */
+
+#if defined(TARGET_IA64)
+ pci_conf[0x40] = 0x41; /* PM io base read only bit */
+ pci_conf[0x41] = 0x1f;
+ pm_write_config(s, 0x80, 0x01, 1); /*Set default pm_io_base 0x1f40*/
+ s->pmcntrl = SCI_EN;
+#endif
+
+ /* APM */
+ apm_init(&s->apm, apm_ctrl_changed, s);
+
+ register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s);
+
+ if (s->kvm_enabled) {
+ /* Mark SMM as already inited to prevent SMM from running. KVM does not
+ * support SMM mode. */
+ pci_conf[0x5B] = 0x02;
+ }
+
+ /* XXX: which specification is used ? The i82731AB has different
+ mappings */
+ pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10;
+ pci_conf[0x63] = 0x60;
+ pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) |
+ (serial_hds[1] != NULL ? 0x90 : 0);
+
+ pci_conf[0x90] = s->smb_io_base | 1;
+ pci_conf[0x91] = s->smb_io_base >> 8;
+ pci_conf[0xd2] = 0x09;
+ register_ioport_write(s->smb_io_base, 64, 1, smb_ioport_writeb, &s->smb);
+ register_ioport_read(s->smb_io_base, 64, 1, smb_ioport_readb, &s->smb);
+
+ s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s);
+
+ qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1);
+
+ pm_smbus_init(&s->dev.qdev, &s->smb);
+ qemu_register_reset(piix4_reset, s);
+ piix4_acpi_system_hot_add_init(dev->bus, s);
+
+ return 0;
+}
+
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+ qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq,
+ int kvm_enabled)
+{
+ PCIDevice *dev;
+ PIIX4PMState *s;
+
+ dev = pci_create(bus, devfn, "PIIX4_PM");
+ qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
+
+ s = DO_UPCAST(PIIX4PMState, dev, dev);
+ s->irq = sci_irq;
+ s->cmos_s3 = cmos_s3;
+ s->smi_irq = smi_irq;
+ s->kvm_enabled = kvm_enabled;
+
+ qdev_init_nofail(&dev->qdev);
+
+ return s->smb.smbus;
+}
+
+static PCIDeviceInfo piix4_pm_info = {
+ .qdev.name = "PIIX4_PM",
+ .qdev.desc = "PM",
+ .qdev.size = sizeof(PIIX4PMState),
+ .qdev.vmsd = &vmstate_acpi,
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
+ .init = piix4_pm_initfn,
+ .config_write = pm_write_config,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void piix4_pm_register(void)
+{
+ pci_qdev_register(&piix4_pm_info);
+}
+
+device_init(piix4_pm_register);
+
+static uint32_t gpe_read_val(uint16_t val, uint32_t addr)
+{
+ if (addr & 1)
+ return (val >> 8) & 0xff;
+ return val & 0xff;
+}
+
+static uint32_t gpe_readb(void *opaque, uint32_t addr)
+{
+ uint32_t val = 0;
+ PIIX4PMState *s = opaque;
+ struct gpe_regs *g = &s->gpe;
+
+ switch (addr) {
+ case PROC_BASE ... PROC_BASE+31:
+ val = g->cpus_sts[addr - PROC_BASE];
+ break;
+
+ case GPE_BASE:
+ case GPE_BASE + 1:
+ val = gpe_read_val(g->sts, addr);
+ break;
+ case GPE_BASE + 2:
+ case GPE_BASE + 3:
+ val = gpe_read_val(g->en, addr);
+ break;
+ default:
+ break;
+ }
+
+ PIIX4_DPRINTF("gpe read %x == %x\n", addr, val);
+ return val;
+}
+
+static void gpe_write_val(uint16_t *cur, int addr, uint32_t val)
+{
+ if (addr & 1)
+ *cur = (*cur & 0xff) | (val << 8);
+ else
+ *cur = (*cur & 0xff00) | (val & 0xff);
+}
+
+static void gpe_reset_val(uint16_t *cur, int addr, uint32_t val)
+{
+ uint16_t x1, x0 = val & 0xff;
+ int shift = (addr & 1) ? 8 : 0;
+
+ x1 = (*cur >> shift) & 0xff;
+
+ x1 = x1 & ~x0;
+
+ *cur = (*cur & (0xff << (8 - shift))) | (x1 << shift);
+}
+
+static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ PIIX4PMState *s = opaque;
+ struct gpe_regs *g = &s->gpe;
+
+ switch (addr) {
+ case GPE_BASE:
+ case GPE_BASE + 1:
+ gpe_reset_val(&g->sts, addr, val);
+ break;
+ case GPE_BASE + 2:
+ case GPE_BASE + 3:
+ gpe_write_val(&g->en, addr, val);
+ break;
+ default:
+ break;
+ }
+
+ pm_update_sci(s);
+
+ PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val);
+}
+
+static uint32_t pcihotplug_read(void *opaque, uint32_t addr)
+{
+ uint32_t val = 0;
+ struct pci_status *g = opaque;
+ switch (addr) {
+ case PCI_BASE:
+ val = g->up;
+ break;
+ case PCI_BASE + 4:
+ val = g->down;
+ break;
+ default:
+ break;
+ }
+
+ PIIX4_DPRINTF("pcihotplug read %x == %x\n", addr, val);
+ return val;
+}
+
+static void pcihotplug_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ struct pci_status *g = opaque;
+ switch (addr) {
+ case PCI_BASE:
+ g->up = val;
+ break;
+ case PCI_BASE + 4:
+ g->down = val;
+ break;
+ }
+
+ PIIX4_DPRINTF("pcihotplug write %x <== %d\n", addr, val);
+}
+
+static uint32_t pciej_read(void *opaque, uint32_t addr)
+{
+ PIIX4_DPRINTF("pciej read %x\n", addr);
+ return 0;
+}
+
+static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ BusState *bus = opaque;
+ DeviceState *qdev, *next;
+ PCIDevice *dev;
+ 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) {
+ qdev_free(qdev);
+ }
+ }
+
+
+ PIIX4_DPRINTF("pciej write %x <== %d\n", addr, val);
+}
+
+static uint32_t pcirmv_read(void *opaque, uint32_t addr)
+{
+ PIIX4PMState *s = opaque;
+
+ return s->pci0_hotplug_enable;
+}
+
+static void pcirmv_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ return;
+}
+
+extern const char *global_cpu_model;
+
+static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
+ PCIHotplugState state);
+
+static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s)
+{
+ struct pci_status *pci0_status = &s->pci0_status;
+ int i = 0, cpus = smp_cpus;
+
+ while (cpus > 0) {
+ s->gpe.cpus_sts[i++] = (cpus < 8) ? (1 << cpus) - 1 : 0xff;
+ cpus -= 8;
+ }
+
+ register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, s);
+ register_ioport_read(GPE_BASE, 4, 1, gpe_readb, s);
+
+ register_ioport_write(PROC_BASE, 32, 1, gpe_writeb, s);
+ register_ioport_read(PROC_BASE, 32, 1, gpe_readb, s);
+
+ register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, pci0_status);
+ register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, pci0_status);
+
+ register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus);
+ register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus);
+
+ register_ioport_write(PCI_RMV_BASE, 4, 4, pcirmv_write, s);
+ register_ioport_read(PCI_RMV_BASE, 4, 4, pcirmv_read, s);
+
+ pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev);
+}
+
+#if defined(TARGET_I386)
+static void enable_processor(struct gpe_regs *g, int cpu)
+{
+ g->sts |= PIIX4_CPU_HOTPLUG_STATUS;
+ g->cpus_sts[cpu/8] |= (1 << (cpu%8));
+}
+
+static void disable_processor(struct gpe_regs *g, int cpu)
+{
+ g->sts |= PIIX4_CPU_HOTPLUG_STATUS;
+ g->cpus_sts[cpu/8] &= ~(1 << (cpu%8));
+}
+
+void qemu_system_cpu_hot_add(int cpu, int state)
+{
+ CPUState *env;
+ PIIX4PMState *s = global_piix4_pm_state;
+
+ if (state && !qemu_get_cpu(cpu)) {
+ env = pc_new_cpu(global_cpu_model);
+ if (!env) {
+ fprintf(stderr, "cpu %d creation failed\n", cpu);
+ return;
+ }
+ env->cpuid_apic_id = cpu;
+ }
+
+ if (state)
+ enable_processor(&s->gpe, cpu);
+ else
+ disable_processor(&s->gpe, cpu);
+
+ pm_update_sci(s);
+}
+#endif
+
+static void enable_device(PIIX4PMState *s, int slot)
+{
+ s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS;
+ s->pci0_status.up |= (1 << slot);
+}
+
+static void disable_device(PIIX4PMState *s, int slot)
+{
+ s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS;
+ s->pci0_status.down |= (1 << slot);
+}
+
+static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
+ PCIHotplugState state)
+{
+ int slot = PCI_SLOT(dev->devfn);
+ PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev,
+ DO_UPCAST(PCIDevice, qdev, qdev));
+
+ /* Don't send event when device is enabled during qemu machine creation:
+ * it is present on boot, no hotplug event is necessary. We do send an
+ * event when the device is disabled later. */
+ if (state == PCI_COLDPLUG_ENABLED) {
+ return 0;
+ }
+
+ s->pci0_status.up = 0;
+ s->pci0_status.down = 0;
+ if (state == PCI_HOTPLUG_ENABLED) {
+ enable_device(s, slot);
+ } else {
+ disable_device(s, slot);
+ }
+
+ pm_update_sci(s);
+
+ return 0;
+}
diff --git a/hw/adb.c b/hw/adb.c
new file mode 100644
index 0000000..99b30f6
--- /dev/null
+++ b/hw/adb.c
@@ -0,0 +1,480 @@
+/*
+ * QEMU ADB support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc_mac.h"
+#include "console.h"
+
+/* debug ADB */
+//#define DEBUG_ADB
+
+#ifdef DEBUG_ADB
+#define ADB_DPRINTF(fmt, ...) \
+do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define ADB_DPRINTF(fmt, ...)
+#endif
+
+/* ADB commands */
+#define ADB_BUSRESET 0x00
+#define ADB_FLUSH 0x01
+#define ADB_WRITEREG 0x08
+#define ADB_READREG 0x0c
+
+/* ADB device commands */
+#define ADB_CMD_SELF_TEST 0xff
+#define ADB_CMD_CHANGE_ID 0xfe
+#define ADB_CMD_CHANGE_ID_AND_ACT 0xfd
+#define ADB_CMD_CHANGE_ID_AND_ENABLE 0x00
+
+/* ADB default device IDs (upper 4 bits of ADB command byte) */
+#define ADB_DONGLE 1
+#define ADB_KEYBOARD 2
+#define ADB_MOUSE 3
+#define ADB_TABLET 4
+#define ADB_MODEM 5
+#define ADB_MISC 7
+
+/* error codes */
+#define ADB_RET_NOTPRESENT (-2)
+
+int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
+{
+ ADBDevice *d;
+ int devaddr, cmd, i;
+
+ cmd = buf[0] & 0xf;
+ if (cmd == ADB_BUSRESET) {
+ for(i = 0; i < s->nb_devices; i++) {
+ d = &s->devices[i];
+ if (d->devreset) {
+ d->devreset(d);
+ }
+ }
+ return 0;
+ }
+ devaddr = buf[0] >> 4;
+ for(i = 0; i < s->nb_devices; i++) {
+ d = &s->devices[i];
+ if (d->devaddr == devaddr) {
+ return d->devreq(d, obuf, buf, len);
+ }
+ }
+ return ADB_RET_NOTPRESENT;
+}
+
+/* XXX: move that to cuda ? */
+int adb_poll(ADBBusState *s, uint8_t *obuf)
+{
+ ADBDevice *d;
+ int olen, i;
+ uint8_t buf[1];
+
+ olen = 0;
+ for(i = 0; i < s->nb_devices; i++) {
+ if (s->poll_index >= s->nb_devices)
+ s->poll_index = 0;
+ d = &s->devices[s->poll_index];
+ buf[0] = ADB_READREG | (d->devaddr << 4);
+ olen = adb_request(s, obuf + 1, buf, 1);
+ /* if there is data, we poll again the same device */
+ if (olen > 0) {
+ obuf[0] = buf[0];
+ olen++;
+ break;
+ }
+ s->poll_index++;
+ }
+ return olen;
+}
+
+ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
+ ADBDeviceRequest *devreq,
+ ADBDeviceReset *devreset,
+ void *opaque)
+{
+ ADBDevice *d;
+ if (s->nb_devices >= MAX_ADB_DEVICES)
+ return NULL;
+ d = &s->devices[s->nb_devices++];
+ d->bus = s;
+ d->devaddr = devaddr;
+ d->devreq = devreq;
+ d->devreset = devreset;
+ d->opaque = opaque;
+ qemu_register_reset((QEMUResetHandler *)devreset, d);
+ return d;
+}
+
+/***************************************************************/
+/* Keyboard ADB device */
+
+typedef struct KBDState {
+ uint8_t data[128];
+ int rptr, wptr, count;
+} KBDState;
+
+static const uint8_t pc_to_adb_keycode[256] = {
+ 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
+ 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54, 0, 1,
+ 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9,
+ 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
+ 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
+ 84, 85, 82, 65, 0, 0, 10,103,111, 0, 0,110, 81, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 94, 0, 93, 0, 0, 0, 0, 0, 0,104,102, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76,125, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 75, 0, 0,124, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,115, 62,116, 0, 59, 0, 60, 0,119,
+ 61,121,114,117, 0, 0, 0, 0, 0, 0, 0, 55,126, 0,127, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static void adb_kbd_put_keycode(void *opaque, int keycode)
+{
+ ADBDevice *d = opaque;
+ KBDState *s = d->opaque;
+
+ if (s->count < sizeof(s->data)) {
+ s->data[s->wptr] = keycode;
+ if (++s->wptr == sizeof(s->data))
+ s->wptr = 0;
+ s->count++;
+ }
+}
+
+static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
+{
+ static int ext_keycode;
+ KBDState *s = d->opaque;
+ int adb_keycode, keycode;
+ int olen;
+
+ olen = 0;
+ for(;;) {
+ if (s->count == 0)
+ break;
+ keycode = s->data[s->rptr];
+ if (++s->rptr == sizeof(s->data))
+ s->rptr = 0;
+ s->count--;
+
+ if (keycode == 0xe0) {
+ ext_keycode = 1;
+ } else {
+ if (ext_keycode)
+ adb_keycode = pc_to_adb_keycode[keycode | 0x80];
+ else
+ adb_keycode = pc_to_adb_keycode[keycode & 0x7f];
+ obuf[0] = adb_keycode | (keycode & 0x80);
+ /* NOTE: could put a second keycode if needed */
+ obuf[1] = 0xff;
+ olen = 2;
+ ext_keycode = 0;
+ break;
+ }
+ }
+ return olen;
+}
+
+static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
+ const uint8_t *buf, int len)
+{
+ KBDState *s = d->opaque;
+ int cmd, reg, olen;
+
+ if ((buf[0] & 0x0f) == ADB_FLUSH) {
+ /* flush keyboard fifo */
+ s->wptr = s->rptr = s->count = 0;
+ return 0;
+ }
+
+ cmd = buf[0] & 0xc;
+ reg = buf[0] & 0x3;
+ olen = 0;
+ switch(cmd) {
+ case ADB_WRITEREG:
+ switch(reg) {
+ case 2:
+ /* LED status */
+ break;
+ case 3:
+ switch(buf[2]) {
+ case ADB_CMD_SELF_TEST:
+ break;
+ case ADB_CMD_CHANGE_ID:
+ case ADB_CMD_CHANGE_ID_AND_ACT:
+ case ADB_CMD_CHANGE_ID_AND_ENABLE:
+ d->devaddr = buf[1] & 0xf;
+ break;
+ default:
+ /* XXX: check this */
+ d->devaddr = buf[1] & 0xf;
+ d->handler = buf[2];
+ break;
+ }
+ }
+ break;
+ case ADB_READREG:
+ switch(reg) {
+ case 0:
+ olen = adb_kbd_poll(d, obuf);
+ break;
+ case 1:
+ break;
+ case 2:
+ obuf[0] = 0x00; /* XXX: check this */
+ obuf[1] = 0x07; /* led status */
+ olen = 2;
+ break;
+ case 3:
+ obuf[0] = d->handler;
+ obuf[1] = d->devaddr;
+ olen = 2;
+ break;
+ }
+ break;
+ }
+ return olen;
+}
+
+static void adb_kbd_save(QEMUFile *f, void *opaque)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ qemu_put_buffer(f, s->data, sizeof(s->data));
+ qemu_put_sbe32s(f, &s->rptr);
+ qemu_put_sbe32s(f, &s->wptr);
+ qemu_put_sbe32s(f, &s->count);
+}
+
+static int adb_kbd_load(QEMUFile *f, void *opaque, int version_id)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_buffer(f, s->data, sizeof(s->data));
+ qemu_get_sbe32s(f, &s->rptr);
+ qemu_get_sbe32s(f, &s->wptr);
+ qemu_get_sbe32s(f, &s->count);
+
+ return 0;
+}
+
+static int adb_kbd_reset(ADBDevice *d)
+{
+ KBDState *s = d->opaque;
+
+ d->handler = 1;
+ d->devaddr = ADB_KEYBOARD;
+ memset(s, 0, sizeof(KBDState));
+
+ return 0;
+}
+
+void adb_kbd_init(ADBBusState *bus)
+{
+ ADBDevice *d;
+ KBDState *s;
+ s = qemu_mallocz(sizeof(KBDState));
+ d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request,
+ adb_kbd_reset, s);
+ qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
+ register_savevm(NULL, "adb_kbd", -1, 1, adb_kbd_save,
+ adb_kbd_load, s);
+}
+
+/***************************************************************/
+/* Mouse ADB device */
+
+typedef struct MouseState {
+ int buttons_state, last_buttons_state;
+ int dx, dy, dz;
+} MouseState;
+
+static void adb_mouse_event(void *opaque,
+ int dx1, int dy1, int dz1, int buttons_state)
+{
+ ADBDevice *d = opaque;
+ MouseState *s = d->opaque;
+
+ s->dx += dx1;
+ s->dy += dy1;
+ s->dz += dz1;
+ s->buttons_state = buttons_state;
+}
+
+
+static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
+{
+ MouseState *s = d->opaque;
+ int dx, dy;
+
+ if (s->last_buttons_state == s->buttons_state &&
+ s->dx == 0 && s->dy == 0)
+ return 0;
+
+ dx = s->dx;
+ if (dx < -63)
+ dx = -63;
+ else if (dx > 63)
+ dx = 63;
+
+ dy = s->dy;
+ if (dy < -63)
+ dy = -63;
+ else if (dy > 63)
+ dy = 63;
+
+ s->dx -= dx;
+ s->dy -= dy;
+ s->last_buttons_state = s->buttons_state;
+
+ dx &= 0x7f;
+ dy &= 0x7f;
+
+ if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
+ dy |= 0x80;
+ if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
+ dx |= 0x80;
+
+ obuf[0] = dy;
+ obuf[1] = dx;
+ return 2;
+}
+
+static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
+ const uint8_t *buf, int len)
+{
+ MouseState *s = d->opaque;
+ int cmd, reg, olen;
+
+ if ((buf[0] & 0x0f) == ADB_FLUSH) {
+ /* flush mouse fifo */
+ s->buttons_state = s->last_buttons_state;
+ s->dx = 0;
+ s->dy = 0;
+ s->dz = 0;
+ return 0;
+ }
+
+ cmd = buf[0] & 0xc;
+ reg = buf[0] & 0x3;
+ olen = 0;
+ switch(cmd) {
+ case ADB_WRITEREG:
+ ADB_DPRINTF("write reg %d val 0x%2.2x\n", reg, buf[1]);
+ switch(reg) {
+ case 2:
+ break;
+ case 3:
+ switch(buf[2]) {
+ case ADB_CMD_SELF_TEST:
+ break;
+ case ADB_CMD_CHANGE_ID:
+ case ADB_CMD_CHANGE_ID_AND_ACT:
+ case ADB_CMD_CHANGE_ID_AND_ENABLE:
+ d->devaddr = buf[1] & 0xf;
+ break;
+ default:
+ /* XXX: check this */
+ d->devaddr = buf[1] & 0xf;
+ break;
+ }
+ }
+ break;
+ case ADB_READREG:
+ switch(reg) {
+ case 0:
+ olen = adb_mouse_poll(d, obuf);
+ break;
+ case 1:
+ break;
+ case 3:
+ obuf[0] = d->handler;
+ obuf[1] = d->devaddr;
+ olen = 2;
+ break;
+ }
+ ADB_DPRINTF("read reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x\n", reg,
+ obuf[0], obuf[1]);
+ break;
+ }
+ return olen;
+}
+
+static int adb_mouse_reset(ADBDevice *d)
+{
+ MouseState *s = d->opaque;
+
+ d->handler = 2;
+ d->devaddr = ADB_MOUSE;
+ memset(s, 0, sizeof(MouseState));
+
+ return 0;
+}
+
+static void adb_mouse_save(QEMUFile *f, void *opaque)
+{
+ MouseState *s = (MouseState *)opaque;
+
+ qemu_put_sbe32s(f, &s->buttons_state);
+ qemu_put_sbe32s(f, &s->last_buttons_state);
+ qemu_put_sbe32s(f, &s->dx);
+ qemu_put_sbe32s(f, &s->dy);
+ qemu_put_sbe32s(f, &s->dz);
+}
+
+static int adb_mouse_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MouseState *s = (MouseState *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_sbe32s(f, &s->buttons_state);
+ qemu_get_sbe32s(f, &s->last_buttons_state);
+ qemu_get_sbe32s(f, &s->dx);
+ qemu_get_sbe32s(f, &s->dy);
+ qemu_get_sbe32s(f, &s->dz);
+
+ return 0;
+}
+
+void adb_mouse_init(ADBBusState *bus)
+{
+ ADBDevice *d;
+ MouseState *s;
+
+ s = qemu_mallocz(sizeof(MouseState));
+ d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request,
+ adb_mouse_reset, s);
+ qemu_add_mouse_event_handler(adb_mouse_event, d, 0, "QEMU ADB Mouse");
+ register_savevm(NULL, "adb_mouse", -1, 1, adb_mouse_save,
+ adb_mouse_load, s);
+}
diff --git a/hw/adlib.c b/hw/adlib.c
new file mode 100644
index 0000000..1d8092b
--- /dev/null
+++ b/hw/adlib.c
@@ -0,0 +1,338 @@
+/*
+ * QEMU Proxy for OPL2/3 emulation by MAME team
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "isa.h"
+
+//#define DEBUG
+
+#define ADLIB_KILL_TIMERS 1
+
+#ifdef DEBUG
+#include "qemu-timer.h"
+#endif
+
+#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#ifdef HAS_YMF262
+#include "ymf262.h"
+void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
+#define SHIFT 2
+#else
+#include "fmopl.h"
+#define SHIFT 1
+#endif
+
+#define IO_READ_PROTO(name) \
+ uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+ void name (void *opaque, uint32_t nport, uint32_t val)
+
+static struct {
+ int port;
+ int freq;
+} conf = {0x220, 44100};
+
+typedef struct {
+ QEMUSoundCard card;
+ int ticking[2];
+ int enabled;
+ int active;
+ int bufpos;
+#ifdef DEBUG
+ int64_t exp[2];
+#endif
+ int16_t *mixbuf;
+ uint64_t dexp[2];
+ SWVoiceOut *voice;
+ int left, pos, samples;
+ QEMUAudioTimeStamp ats;
+#ifndef HAS_YMF262
+ FM_OPL *opl;
+#endif
+} AdlibState;
+
+static AdlibState glob_adlib;
+
+static void adlib_stop_opl_timer (AdlibState *s, size_t n)
+{
+#ifdef HAS_YMF262
+ YMF262TimerOver (0, n);
+#else
+ OPLTimerOver (s->opl, n);
+#endif
+ s->ticking[n] = 0;
+}
+
+static void adlib_kill_timers (AdlibState *s)
+{
+ size_t i;
+
+ for (i = 0; i < 2; ++i) {
+ if (s->ticking[i]) {
+ uint64_t delta;
+
+ delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
+ ldebug (
+ "delta = %f dexp = %f expired => %d\n",
+ delta / 1000000.0,
+ s->dexp[i] / 1000000.0,
+ delta >= s->dexp[i]
+ );
+ if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
+ adlib_stop_opl_timer (s, i);
+ AUD_init_time_stamp_out (s->voice, &s->ats);
+ }
+ }
+ }
+}
+
+static IO_WRITE_PROTO (adlib_write)
+{
+ AdlibState *s = opaque;
+ int a = nport & 3;
+ int status;
+
+ s->active = 1;
+ AUD_set_active_out (s->voice, 1);
+
+ adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
+ status = YMF262Write (0, a, val);
+#else
+ status = OPLWrite (s->opl, a, val);
+#endif
+}
+
+static IO_READ_PROTO (adlib_read)
+{
+ AdlibState *s = opaque;
+ uint8_t data;
+ int a = nport & 3;
+
+ adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
+ data = YMF262Read (0, a);
+#else
+ data = OPLRead (s->opl, a);
+#endif
+ return data;
+}
+
+static void timer_handler (int c, double interval_Sec)
+{
+ AdlibState *s = &glob_adlib;
+ unsigned n = c & 1;
+#ifdef DEBUG
+ double interval;
+ int64_t exp;
+#endif
+
+ if (interval_Sec == 0.0) {
+ s->ticking[n] = 0;
+ return;
+ }
+
+ s->ticking[n] = 1;
+#ifdef DEBUG
+ interval = get_ticks_per_sec() * interval_Sec;
+ exp = qemu_get_clock (vm_clock) + interval;
+ s->exp[n] = exp;
+#endif
+
+ s->dexp[n] = interval_Sec * 1000000.0;
+ AUD_init_time_stamp_out (s->voice, &s->ats);
+}
+
+static int write_audio (AdlibState *s, int samples)
+{
+ int net = 0;
+ int pos = s->pos;
+
+ while (samples) {
+ int nbytes, wbytes, wsampl;
+
+ nbytes = samples << SHIFT;
+ wbytes = AUD_write (
+ s->voice,
+ s->mixbuf + (pos << (SHIFT - 1)),
+ nbytes
+ );
+
+ if (wbytes) {
+ wsampl = wbytes >> SHIFT;
+
+ samples -= wsampl;
+ pos = (pos + wsampl) % s->samples;
+
+ net += wsampl;
+ }
+ else {
+ break;
+ }
+ }
+
+ return net;
+}
+
+static void adlib_callback (void *opaque, int free)
+{
+ AdlibState *s = opaque;
+ int samples, net = 0, to_play, written;
+
+ samples = free >> SHIFT;
+ if (!(s->active && s->enabled) || !samples) {
+ return;
+ }
+
+ to_play = audio_MIN (s->left, samples);
+ while (to_play) {
+ written = write_audio (s, to_play);
+
+ if (written) {
+ s->left -= written;
+ samples -= written;
+ to_play -= written;
+ s->pos = (s->pos + written) % s->samples;
+ }
+ else {
+ return;
+ }
+ }
+
+ samples = audio_MIN (samples, s->samples - s->pos);
+ if (!samples) {
+ return;
+ }
+
+#ifdef HAS_YMF262
+ YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
+#else
+ YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
+#endif
+
+ while (samples) {
+ written = write_audio (s, samples);
+
+ if (written) {
+ net += written;
+ samples -= written;
+ s->pos = (s->pos + written) % s->samples;
+ }
+ else {
+ s->left = samples;
+ return;
+ }
+ }
+}
+
+static void Adlib_fini (AdlibState *s)
+{
+#ifdef HAS_YMF262
+ YMF262Shutdown ();
+#else
+ if (s->opl) {
+ OPLDestroy (s->opl);
+ s->opl = NULL;
+ }
+#endif
+
+ if (s->mixbuf) {
+ qemu_free (s->mixbuf);
+ }
+
+ s->active = 0;
+ s->enabled = 0;
+ AUD_remove_card (&s->card);
+}
+
+int Adlib_init (qemu_irq *pic)
+{
+ AdlibState *s = &glob_adlib;
+ struct audsettings as;
+
+#ifdef HAS_YMF262
+ if (YMF262Init (1, 14318180, conf.freq)) {
+ dolog ("YMF262Init %d failed\n", conf.freq);
+ return -1;
+ }
+ else {
+ YMF262SetTimerHandler (0, timer_handler, 0);
+ s->enabled = 1;
+ }
+#else
+ s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
+ if (!s->opl) {
+ dolog ("OPLCreate %d failed\n", conf.freq);
+ return -1;
+ }
+ else {
+ OPLSetTimerHandler (s->opl, timer_handler, 0);
+ s->enabled = 1;
+ }
+#endif
+
+ as.freq = conf.freq;
+ as.nchannels = SHIFT;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = AUDIO_HOST_ENDIANNESS;
+
+ AUD_register_card ("adlib", &s->card);
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "adlib",
+ s,
+ adlib_callback,
+ &as
+ );
+ if (!s->voice) {
+ Adlib_fini (s);
+ return -1;
+ }
+
+ s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
+ s->mixbuf = qemu_mallocz (s->samples << SHIFT);
+
+ register_ioport_read (0x388, 4, 1, adlib_read, s);
+ register_ioport_write (0x388, 4, 1, adlib_write, s);
+
+ register_ioport_read (conf.port, 4, 1, adlib_read, s);
+ register_ioport_write (conf.port, 4, 1, adlib_write, s);
+
+ register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
+ register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
+
+ return 0;
+}
diff --git a/hw/ads7846.c b/hw/ads7846.c
new file mode 100644
index 0000000..b3bbeaf
--- /dev/null
+++ b/hw/ads7846.c
@@ -0,0 +1,170 @@
+/*
+ * TI ADS7846 / TSC2046 chip emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "ssi.h"
+#include "console.h"
+
+typedef struct {
+ SSISlave ssidev;
+ qemu_irq interrupt;
+
+ int input[8];
+ int pressure;
+ int noise;
+
+ int cycle;
+ int output;
+} ADS7846State;
+
+/* Control-byte bitfields */
+#define CB_PD0 (1 << 0)
+#define CB_PD1 (1 << 1)
+#define CB_SER (1 << 2)
+#define CB_MODE (1 << 3)
+#define CB_A0 (1 << 4)
+#define CB_A1 (1 << 5)
+#define CB_A2 (1 << 6)
+#define CB_START (1 << 7)
+
+#define X_AXIS_DMAX 3470
+#define X_AXIS_MIN 290
+#define Y_AXIS_DMAX 3450
+#define Y_AXIS_MIN 200
+
+#define ADS_VBAT 2000
+#define ADS_VAUX 2000
+#define ADS_TEMP0 2000
+#define ADS_TEMP1 3000
+#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
+#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
+#define ADS_Z1POS(x, y) 600
+#define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y))
+
+static void ads7846_int_update(ADS7846State *s)
+{
+ if (s->interrupt)
+ qemu_set_irq(s->interrupt, s->pressure == 0);
+}
+
+static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value)
+{
+ ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
+
+ switch (s->cycle ++) {
+ case 0:
+ if (!(value & CB_START)) {
+ s->cycle = 0;
+ break;
+ }
+
+ s->output = s->input[(value >> 4) & 7];
+
+ /* Imitate the ADC noise, some drivers expect this. */
+ s->noise = (s->noise + 3) & 7;
+ switch ((value >> 4) & 7) {
+ case 1: s->output += s->noise ^ 2; break;
+ case 3: s->output += s->noise ^ 0; break;
+ case 4: s->output += s->noise ^ 7; break;
+ case 5: s->output += s->noise ^ 5; break;
+ }
+
+ if (value & CB_MODE)
+ s->output >>= 4; /* 8 bits instead of 12 */
+
+ break;
+ case 1:
+ s->cycle = 0;
+ break;
+ }
+ return s->output;
+}
+
+static void ads7846_ts_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ ADS7846State *s = opaque;
+
+ if (buttons_state) {
+ x = 0x7fff - x;
+ s->input[1] = ADS_XPOS(x, y);
+ s->input[3] = ADS_Z1POS(x, y);
+ s->input[4] = ADS_Z2POS(x, y);
+ s->input[5] = ADS_YPOS(x, y);
+ }
+
+ if (s->pressure == !buttons_state) {
+ s->pressure = !!buttons_state;
+
+ ads7846_int_update(s);
+ }
+}
+
+static void ads7846_save(QEMUFile *f, void *opaque)
+{
+ ADS7846State *s = (ADS7846State *) opaque;
+ int i;
+
+ for (i = 0; i < 8; i ++)
+ qemu_put_be32(f, s->input[i]);
+ qemu_put_be32(f, s->noise);
+ qemu_put_be32(f, s->cycle);
+ qemu_put_be32(f, s->output);
+}
+
+static int ads7846_load(QEMUFile *f, void *opaque, int version_id)
+{
+ ADS7846State *s = (ADS7846State *) opaque;
+ int i;
+
+ for (i = 0; i < 8; i ++)
+ s->input[i] = qemu_get_be32(f);
+ s->noise = qemu_get_be32(f);
+ s->cycle = qemu_get_be32(f);
+ s->output = qemu_get_be32(f);
+
+ s->pressure = 0;
+ ads7846_int_update(s);
+
+ return 0;
+}
+
+static int ads7846_init(SSISlave *dev)
+{
+ ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
+
+ qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
+
+ s->input[0] = ADS_TEMP0; /* TEMP0 */
+ s->input[2] = ADS_VBAT; /* VBAT */
+ s->input[6] = ADS_VAUX; /* VAUX */
+ s->input[7] = ADS_TEMP1; /* TEMP1 */
+
+ /* We want absolute coordinates */
+ qemu_add_mouse_event_handler(ads7846_ts_event, s, 1,
+ "QEMU ADS7846-driven Touchscreen");
+
+ ads7846_int_update(s);
+
+ register_savevm(NULL, "ads7846", -1, 0, ads7846_save, ads7846_load, s);
+ return 0;
+}
+
+static SSISlaveInfo ads7846_info = {
+ .qdev.name ="ads7846",
+ .qdev.size = sizeof(ADS7846State),
+ .init = ads7846_init,
+ .transfer = ads7846_transfer
+};
+
+static void ads7846_register_devices(void)
+{
+ ssi_register_slave(&ads7846_info);
+}
+
+device_init(ads7846_register_devices)
diff --git a/hw/alpha_palcode.c b/hw/alpha_palcode.c
new file mode 100644
index 0000000..033b542
--- /dev/null
+++ b/hw/alpha_palcode.c
@@ -0,0 +1,1048 @@
+/*
+ * Alpha emulation - PALcode emulation for qemu.
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+/* Shared handlers */
+static void pal_reset (CPUState *env);
+/* Console handlers */
+static void pal_console_call (CPUState *env, uint32_t palcode);
+/* OpenVMS handlers */
+static void pal_openvms_call (CPUState *env, uint32_t palcode);
+/* UNIX / Linux handlers */
+static void pal_unix_call (CPUState *env, uint32_t palcode);
+
+pal_handler_t pal_handlers[] = {
+ /* Console handler */
+ {
+ .reset = &pal_reset,
+ .call_pal = &pal_console_call,
+ },
+ /* OpenVMS handler */
+ {
+ .reset = &pal_reset,
+ .call_pal = &pal_openvms_call,
+ },
+ /* UNIX / Linux handler */
+ {
+ .reset = &pal_reset,
+ .call_pal = &pal_unix_call,
+ },
+};
+
+#if 0
+/* One must explicitly check that the TB is valid and the FOE bit is reset */
+static void update_itb (void)
+{
+ /* This writes into a temp register, not the actual one */
+ mtpr(TB_TAG);
+ mtpr(TB_CTL);
+ /* This commits the TB update */
+ mtpr(ITB_PTE);
+}
+
+static void update_dtb (void);
+{
+ mtpr(TB_CTL);
+ /* This write into a temp register, not the actual one */
+ mtpr(TB_TAG);
+ /* This commits the TB update */
+ mtpr(DTB_PTE);
+}
+#endif
+
+static void pal_reset (CPUState *env)
+{
+}
+
+static void do_swappal (CPUState *env, uint64_t palid)
+{
+ pal_handler_t *pal_handler;
+
+ switch (palid) {
+ case 0 ... 2:
+ pal_handler = &pal_handlers[palid];
+ env->pal_handler = pal_handler;
+ env->ipr[IPR_PAL_BASE] = -1ULL;
+ (*pal_handler->reset)(env);
+ break;
+ case 3 ... 255:
+ /* Unknown identifier */
+ env->ir[0] = 1;
+ return;
+ default:
+ /* We were given the entry point address */
+ env->pal_handler = NULL;
+ env->ipr[IPR_PAL_BASE] = palid;
+ env->pc = env->ipr[IPR_PAL_BASE];
+ cpu_loop_exit();
+ }
+}
+
+static void pal_console_call (CPUState *env, uint32_t palcode)
+{
+ uint64_t palid;
+
+ if (palcode < 0x00000080) {
+ /* Privileged palcodes */
+ if (!(env->ps >> 3)) {
+ /* TODO: generate privilege exception */
+ }
+ }
+ switch (palcode) {
+ case 0x00000000:
+ /* HALT */
+ /* REQUIRED */
+ break;
+ case 0x00000001:
+ /* CFLUSH */
+ break;
+ case 0x00000002:
+ /* DRAINA */
+ /* REQUIRED */
+ /* Implemented as no-op */
+ break;
+ case 0x00000009:
+ /* CSERVE */
+ /* REQUIRED */
+ break;
+ case 0x0000000A:
+ /* SWPPAL */
+ /* REQUIRED */
+ palid = env->ir[16];
+ do_swappal(env, palid);
+ break;
+ case 0x00000080:
+ /* BPT */
+ /* REQUIRED */
+ break;
+ case 0x00000081:
+ /* BUGCHK */
+ /* REQUIRED */
+ break;
+ case 0x00000086:
+ /* IMB */
+ /* REQUIRED */
+ /* Implemented as no-op */
+ break;
+ case 0x0000009E:
+ /* RDUNIQUE */
+ /* REQUIRED */
+ break;
+ case 0x0000009F:
+ /* WRUNIQUE */
+ /* REQUIRED */
+ break;
+ case 0x000000AA:
+ /* GENTRAP */
+ /* REQUIRED */
+ break;
+ default:
+ break;
+ }
+}
+
+static void pal_openvms_call (CPUState *env, uint32_t palcode)
+{
+ uint64_t palid, val, oldval;
+
+ if (palcode < 0x00000080) {
+ /* Privileged palcodes */
+ if (!(env->ps >> 3)) {
+ /* TODO: generate privilege exception */
+ }
+ }
+ switch (palcode) {
+ case 0x00000000:
+ /* HALT */
+ /* REQUIRED */
+ break;
+ case 0x00000001:
+ /* CFLUSH */
+ break;
+ case 0x00000002:
+ /* DRAINA */
+ /* REQUIRED */
+ /* Implemented as no-op */
+ break;
+ case 0x00000003:
+ /* LDQP */
+ break;
+ case 0x00000004:
+ /* STQP */
+ break;
+ case 0x00000005:
+ /* SWPCTX */
+ break;
+ case 0x00000006:
+ /* MFPR_ASN */
+ if (cpu_alpha_mfpr(env, IPR_ASN, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000007:
+ /* MTPR_ASTEN */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_ASTEN, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000008:
+ /* MTPR_ASTSR */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_ASTSR, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000009:
+ /* CSERVE */
+ /* REQUIRED */
+ break;
+ case 0x0000000A:
+ /* SWPPAL */
+ /* REQUIRED */
+ palid = env->ir[16];
+ do_swappal(env, palid);
+ break;
+ case 0x0000000B:
+ /* MFPR_FEN */
+ if (cpu_alpha_mfpr(env, IPR_FEN, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000000C:
+ /* MTPR_FEN */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_FEN, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000000D:
+ /* MTPR_IPIR */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000000E:
+ /* MFPR_IPL */
+ if (cpu_alpha_mfpr(env, IPR_IPL, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000000F:
+ /* MTPR_IPL */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_IPL, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000010:
+ /* MFPR_MCES */
+ if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000011:
+ /* MTPR_MCES */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000012:
+ /* MFPR_PCBB */
+ if (cpu_alpha_mfpr(env, IPR_PCBB, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000013:
+ /* MFPR_PRBR */
+ if (cpu_alpha_mfpr(env, IPR_PRBR, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000014:
+ /* MTPR_PRBR */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_PRBR, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000015:
+ /* MFPR_PTBR */
+ if (cpu_alpha_mfpr(env, IPR_PTBR, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000016:
+ /* MFPR_SCBB */
+ if (cpu_alpha_mfpr(env, IPR_SCBB, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000017:
+ /* MTPR_SCBB */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_SCBB, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000018:
+ /* MTPR_SIRR */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_SIRR, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000019:
+ /* MFPR_SISR */
+ if (cpu_alpha_mfpr(env, IPR_SISR, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000001A:
+ /* MFPR_TBCHK */
+ if (cpu_alpha_mfpr(env, IPR_TBCHK, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000001B:
+ /* MTPR_TBIA */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_TBIA, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000001C:
+ /* MTPR_TBIAP */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_TBIAP, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000001D:
+ /* MTPR_TBIS */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000001E:
+ /* MFPR_ESP */
+ if (cpu_alpha_mfpr(env, IPR_ESP, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000001F:
+ /* MTPR_ESP */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_ESP, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000020:
+ /* MFPR_SSP */
+ if (cpu_alpha_mfpr(env, IPR_SSP, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000021:
+ /* MTPR_SSP */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_SSP, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000022:
+ /* MFPR_USP */
+ if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000023:
+ /* MTPR_USP */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000024:
+ /* MTPR_TBISD */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_TBISD, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000025:
+ /* MTPR_TBISI */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_TBISI, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000026:
+ /* MFPR_ASTEN */
+ if (cpu_alpha_mfpr(env, IPR_ASTEN, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000027:
+ /* MFPR_ASTSR */
+ if (cpu_alpha_mfpr(env, IPR_ASTSR, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000029:
+ /* MFPR_VPTB */
+ if (cpu_alpha_mfpr(env, IPR_VPTB, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000002A:
+ /* MTPR_VPTB */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_VPTB, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000002B:
+ /* MTPR_PERFMON */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000002E:
+ /* MTPR_DATFX */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_DATFX, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000003E:
+ /* WTINT */
+ break;
+ case 0x0000003F:
+ /* MFPR_WHAMI */
+ if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000080:
+ /* BPT */
+ /* REQUIRED */
+ break;
+ case 0x00000081:
+ /* BUGCHK */
+ /* REQUIRED */
+ break;
+ case 0x00000082:
+ /* CHME */
+ break;
+ case 0x00000083:
+ /* CHMK */
+ break;
+ case 0x00000084:
+ /* CHMS */
+ break;
+ case 0x00000085:
+ /* CHMU */
+ break;
+ case 0x00000086:
+ /* IMB */
+ /* REQUIRED */
+ /* Implemented as no-op */
+ break;
+ case 0x00000087:
+ /* INSQHIL */
+ break;
+ case 0x00000088:
+ /* INSQTIL */
+ break;
+ case 0x00000089:
+ /* INSQHIQ */
+ break;
+ case 0x0000008A:
+ /* INSQTIQ */
+ break;
+ case 0x0000008B:
+ /* INSQUEL */
+ break;
+ case 0x0000008C:
+ /* INSQUEQ */
+ break;
+ case 0x0000008D:
+ /* INSQUEL/D */
+ break;
+ case 0x0000008E:
+ /* INSQUEQ/D */
+ break;
+ case 0x0000008F:
+ /* PROBER */
+ break;
+ case 0x00000090:
+ /* PROBEW */
+ break;
+ case 0x00000091:
+ /* RD_PS */
+ break;
+ case 0x00000092:
+ /* REI */
+ break;
+ case 0x00000093:
+ /* REMQHIL */
+ break;
+ case 0x00000094:
+ /* REMQTIL */
+ break;
+ case 0x00000095:
+ /* REMQHIQ */
+ break;
+ case 0x00000096:
+ /* REMQTIQ */
+ break;
+ case 0x00000097:
+ /* REMQUEL */
+ break;
+ case 0x00000098:
+ /* REMQUEQ */
+ break;
+ case 0x00000099:
+ /* REMQUEL/D */
+ break;
+ case 0x0000009A:
+ /* REMQUEQ/D */
+ break;
+ case 0x0000009B:
+ /* SWASTEN */
+ break;
+ case 0x0000009C:
+ /* WR_PS_SW */
+ break;
+ case 0x0000009D:
+ /* RSCC */
+ break;
+ case 0x0000009E:
+ /* READ_UNQ */
+ /* REQUIRED */
+ break;
+ case 0x0000009F:
+ /* WRITE_UNQ */
+ /* REQUIRED */
+ break;
+ case 0x000000A0:
+ /* AMOVRR */
+ break;
+ case 0x000000A1:
+ /* AMOVRM */
+ break;
+ case 0x000000A2:
+ /* INSQHILR */
+ break;
+ case 0x000000A3:
+ /* INSQTILR */
+ break;
+ case 0x000000A4:
+ /* INSQHIQR */
+ break;
+ case 0x000000A5:
+ /* INSQTIQR */
+ break;
+ case 0x000000A6:
+ /* REMQHILR */
+ break;
+ case 0x000000A7:
+ /* REMQTILR */
+ break;
+ case 0x000000A8:
+ /* REMQHIQR */
+ break;
+ case 0x000000A9:
+ /* REMQTIQR */
+ break;
+ case 0x000000AA:
+ /* GENTRAP */
+ /* REQUIRED */
+ break;
+ case 0x000000AE:
+ /* CLRFEN */
+ break;
+ default:
+ break;
+ }
+}
+
+static void pal_unix_call (CPUState *env, uint32_t palcode)
+{
+ uint64_t palid, val, oldval;
+
+ if (palcode < 0x00000080) {
+ /* Privileged palcodes */
+ if (!(env->ps >> 3)) {
+ /* TODO: generate privilege exception */
+ }
+ }
+ switch (palcode) {
+ case 0x00000000:
+ /* HALT */
+ /* REQUIRED */
+ break;
+ case 0x00000001:
+ /* CFLUSH */
+ break;
+ case 0x00000002:
+ /* DRAINA */
+ /* REQUIRED */
+ /* Implemented as no-op */
+ break;
+ case 0x00000009:
+ /* CSERVE */
+ /* REQUIRED */
+ break;
+ case 0x0000000A:
+ /* SWPPAL */
+ /* REQUIRED */
+ palid = env->ir[16];
+ do_swappal(env, palid);
+ break;
+ case 0x0000000D:
+ /* WRIPIR */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000010:
+ /* RDMCES */
+ if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000011:
+ /* WRMCES */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000002B:
+ /* WRFEN */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000002D:
+ /* WRVPTPTR */
+ break;
+ case 0x00000030:
+ /* SWPCTX */
+ break;
+ case 0x00000031:
+ /* WRVAL */
+ break;
+ case 0x00000032:
+ /* RDVAL */
+ break;
+ case 0x00000033:
+ /* TBI */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000034:
+ /* WRENT */
+ break;
+ case 0x00000035:
+ /* SWPIPL */
+ break;
+ case 0x00000036:
+ /* RDPS */
+ break;
+ case 0x00000037:
+ /* WRKGP */
+ break;
+ case 0x00000038:
+ /* WRUSP */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x00000039:
+ /* WRPERFMON */
+ val = env->ir[16];
+ if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1)
+ env->ir[0] = val;
+ break;
+ case 0x0000003A:
+ /* RDUSP */
+ if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000003C:
+ /* WHAMI */
+ if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x0000003D:
+ /* RETSYS */
+ break;
+ case 0x0000003E:
+ /* WTINT */
+ break;
+ case 0x0000003F:
+ /* RTI */
+ if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0)
+ env->ir[0] = val;
+ break;
+ case 0x00000080:
+ /* BPT */
+ /* REQUIRED */
+ break;
+ case 0x00000081:
+ /* BUGCHK */
+ /* REQUIRED */
+ break;
+ case 0x00000083:
+ /* CALLSYS */
+ break;
+ case 0x00000086:
+ /* IMB */
+ /* REQUIRED */
+ /* Implemented as no-op */
+ break;
+ case 0x00000092:
+ /* URTI */
+ break;
+ case 0x0000009E:
+ /* RDUNIQUE */
+ /* REQUIRED */
+ break;
+ case 0x0000009F:
+ /* WRUNIQUE */
+ /* REQUIRED */
+ break;
+ case 0x000000AA:
+ /* GENTRAP */
+ /* REQUIRED */
+ break;
+ case 0x000000AE:
+ /* CLRFEN */
+ break;
+ default:
+ break;
+ }
+}
+
+void call_pal (CPUState *env)
+{
+ pal_handler_t *pal_handler = env->pal_handler;
+
+ switch (env->exception_index) {
+ case EXCP_RESET:
+ (*pal_handler->reset)(env);
+ break;
+ case EXCP_MCHK:
+ (*pal_handler->machine_check)(env);
+ break;
+ case EXCP_ARITH:
+ (*pal_handler->arithmetic)(env);
+ break;
+ case EXCP_INTERRUPT:
+ (*pal_handler->interrupt)(env);
+ break;
+ case EXCP_DFAULT:
+ (*pal_handler->dfault)(env);
+ break;
+ case EXCP_DTB_MISS_PAL:
+ (*pal_handler->dtb_miss_pal)(env);
+ break;
+ case EXCP_DTB_MISS_NATIVE:
+ (*pal_handler->dtb_miss_native)(env);
+ break;
+ case EXCP_UNALIGN:
+ (*pal_handler->unalign)(env);
+ break;
+ case EXCP_ITB_MISS:
+ (*pal_handler->itb_miss)(env);
+ break;
+ case EXCP_ITB_ACV:
+ (*pal_handler->itb_acv)(env);
+ break;
+ case EXCP_OPCDEC:
+ (*pal_handler->opcdec)(env);
+ break;
+ case EXCP_FEN:
+ (*pal_handler->fen)(env);
+ break;
+ default:
+ if (env->exception_index >= EXCP_CALL_PAL &&
+ env->exception_index < EXCP_CALL_PALP) {
+ /* Unprivileged PAL call */
+ (*pal_handler->call_pal)
+ (env, (env->exception_index - EXCP_CALL_PAL) >> 6);
+ } else if (env->exception_index >= EXCP_CALL_PALP &&
+ env->exception_index < EXCP_CALL_PALE) {
+ /* Privileged PAL call */
+ (*pal_handler->call_pal)
+ (env, ((env->exception_index - EXCP_CALL_PALP) >> 6) + 0x80);
+ } else {
+ /* Should never happen */
+ }
+ break;
+ }
+ env->ipr[IPR_EXC_ADDR] &= ~1;
+}
+
+void pal_init (CPUState *env)
+{
+ do_swappal(env, 0);
+}
+
+#if 0
+static uint64_t get_ptebase (CPUState *env, uint64_t vaddr)
+{
+ uint64_t virbnd, ptbr;
+
+ if ((env->features & FEATURE_VIRBND)) {
+ cpu_alpha_mfpr(env, IPR_VIRBND, &virbnd);
+ if (vaddr >= virbnd)
+ cpu_alpha_mfpr(env, IPR_SYSPTBR, &ptbr);
+ else
+ cpu_alpha_mfpr(env, IPR_PTBR, &ptbr);
+ } else {
+ cpu_alpha_mfpr(env, IPR_PTBR, &ptbr);
+ }
+
+ return ptbr;
+}
+
+static int get_page_bits (CPUState *env)
+{
+ /* XXX */
+ return 13;
+}
+
+static int get_pte (uint64_t *pfnp, int *zbitsp, int *protp,
+ uint64_t ptebase, int page_bits, uint64_t level,
+ int mmu_idx, int rw)
+{
+ uint64_t pteaddr, pte, pfn;
+ uint8_t gh;
+ int ure, uwe, kre, kwe, foE, foR, foW, v, ret, ar, is_user;
+
+ /* XXX: TOFIX */
+ is_user = mmu_idx == MMU_USER_IDX;
+ pteaddr = (ptebase << page_bits) + (8 * level);
+ pte = ldq_raw(pteaddr);
+ /* Decode all interresting PTE fields */
+ pfn = pte >> 32;
+ uwe = (pte >> 13) & 1;
+ kwe = (pte >> 12) & 1;
+ ure = (pte >> 9) & 1;
+ kre = (pte >> 8) & 1;
+ gh = (pte >> 5) & 3;
+ foE = (pte >> 3) & 1;
+ foW = (pte >> 2) & 1;
+ foR = (pte >> 1) & 1;
+ v = pte & 1;
+ ret = 0;
+ if (!v)
+ ret = 0x1;
+ /* Check access rights */
+ ar = 0;
+ if (is_user) {
+ if (ure)
+ ar |= PAGE_READ;
+ if (uwe)
+ ar |= PAGE_WRITE;
+ if (rw == 1 && !uwe)
+ ret |= 0x2;
+ if (rw != 1 && !ure)
+ ret |= 0x2;
+ } else {
+ if (kre)
+ ar |= PAGE_READ;
+ if (kwe)
+ ar |= PAGE_WRITE;
+ if (rw == 1 && !kwe)
+ ret |= 0x2;
+ if (rw != 1 && !kre)
+ ret |= 0x2;
+ }
+ if (rw == 0 && foR)
+ ret |= 0x4;
+ if (rw == 2 && foE)
+ ret |= 0x8;
+ if (rw == 1 && foW)
+ ret |= 0xC;
+ *pfnp = pfn;
+ if (zbitsp != NULL)
+ *zbitsp = page_bits + (3 * gh);
+ if (protp != NULL)
+ *protp = ar;
+
+ return ret;
+}
+
+static int paddr_from_pte (uint64_t *paddr, int *zbitsp, int *prot,
+ uint64_t ptebase, int page_bits,
+ uint64_t vaddr, int mmu_idx, int rw)
+{
+ uint64_t pfn, page_mask, lvl_mask, level1, level2, level3;
+ int lvl_bits, ret;
+
+ page_mask = (1ULL << page_bits) - 1ULL;
+ lvl_bits = page_bits - 3;
+ lvl_mask = (1ULL << lvl_bits) - 1ULL;
+ level3 = (vaddr >> page_bits) & lvl_mask;
+ level2 = (vaddr >> (page_bits + lvl_bits)) & lvl_mask;
+ level1 = (vaddr >> (page_bits + (2 * lvl_bits))) & lvl_mask;
+ /* Level 1 PTE */
+ ret = get_pte(&pfn, NULL, NULL, ptebase, page_bits, level1, 0, 0);
+ switch (ret) {
+ case 3:
+ /* Access violation */
+ return 2;
+ case 2:
+ /* translation not valid */
+ return 1;
+ default:
+ /* OK */
+ break;
+ }
+ /* Level 2 PTE */
+ ret = get_pte(&pfn, NULL, NULL, pfn, page_bits, level2, 0, 0);
+ switch (ret) {
+ case 3:
+ /* Access violation */
+ return 2;
+ case 2:
+ /* translation not valid */
+ return 1;
+ default:
+ /* OK */
+ break;
+ }
+ /* Level 3 PTE */
+ ret = get_pte(&pfn, zbitsp, prot, pfn, page_bits, level3, mmu_idx, rw);
+ if (ret & 0x1) {
+ /* Translation not valid */
+ ret = 1;
+ } else if (ret & 2) {
+ /* Access violation */
+ ret = 2;
+ } else {
+ switch (ret & 0xC) {
+ case 0:
+ /* OK */
+ ret = 0;
+ break;
+ case 0x4:
+ /* Fault on read */
+ ret = 3;
+ break;
+ case 0x8:
+ /* Fault on execute */
+ ret = 4;
+ break;
+ case 0xC:
+ /* Fault on write */
+ ret = 5;
+ break;
+ }
+ }
+ *paddr = (pfn << page_bits) | (vaddr & page_mask);
+
+ return 0;
+}
+
+static int virtual_to_physical (CPUState *env, uint64_t *physp,
+ int *zbitsp, int *protp,
+ uint64_t virtual, int mmu_idx, int rw)
+{
+ uint64_t sva, ptebase;
+ int seg, page_bits, ret;
+
+ sva = ((int64_t)(virtual << (64 - VA_BITS))) >> (64 - VA_BITS);
+ if (sva != virtual)
+ seg = -1;
+ else
+ seg = sva >> (VA_BITS - 2);
+ virtual &= ~(0xFFFFFC0000000000ULL << (VA_BITS - 43));
+ ptebase = get_ptebase(env, virtual);
+ page_bits = get_page_bits(env);
+ ret = 0;
+ switch (seg) {
+ case 0:
+ /* seg1: 3 levels of PTE */
+ ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits,
+ virtual, mmu_idx, rw);
+ break;
+ case 1:
+ /* seg1: 2 levels of PTE */
+ ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits,
+ virtual, mmu_idx, rw);
+ break;
+ case 2:
+ /* kernel segment */
+ if (mmu_idx != 0) {
+ ret = 2;
+ } else {
+ *physp = virtual;
+ }
+ break;
+ case 3:
+ /* seg1: TB mapped */
+ ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits,
+ virtual, mmu_idx, rw);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+
+/* XXX: code provision */
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ uint64_t physical, page_size, end;
+ int prot, zbits, ret;
+
+ ret = virtual_to_physical(env, &physical, &zbits, &prot,
+ address, mmu_idx, rw);
+
+ switch (ret) {
+ case 0:
+ /* No fault */
+ page_size = 1ULL << zbits;
+ address &= ~(page_size - 1);
+ /* FIXME: page_size should probably be passed to tlb_set_page,
+ and this loop removed. */
+ for (end = physical + page_size; physical < end; physical += 0x1000) {
+ tlb_set_page(env, address, physical, prot, mmu_idx,
+ TARGET_PAGE_SIZE);
+ address += 0x1000;
+ }
+ ret = 0;
+ break;
+#if 0
+ case 1:
+ env->exception_index = EXCP_DFAULT;
+ env->ipr[IPR_EXC_ADDR] = address;
+ ret = 1;
+ break;
+ case 2:
+ env->exception_index = EXCP_ACCESS_VIOLATION;
+ env->ipr[IPR_EXC_ADDR] = address;
+ ret = 1;
+ break;
+ case 3:
+ env->exception_index = EXCP_FAULT_ON_READ;
+ env->ipr[IPR_EXC_ADDR] = address;
+ ret = 1;
+ break;
+ case 4:
+ env->exception_index = EXCP_FAULT_ON_EXECUTE;
+ env->ipr[IPR_EXC_ADDR] = address;
+ ret = 1;
+ case 5:
+ env->exception_index = EXCP_FAULT_ON_WRITE;
+ env->ipr[IPR_EXC_ADDR] = address;
+ ret = 1;
+#endif
+ default:
+ /* Should never happen */
+ env->exception_index = EXCP_MCHK;
+ env->ipr[IPR_EXC_ADDR] = address;
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+#endif
diff --git a/hw/an5206.c b/hw/an5206.c
new file mode 100644
index 0000000..b9f19a9
--- /dev/null
+++ b/hw/an5206.c
@@ -0,0 +1,101 @@
+/*
+ * Arnewsh 5206 ColdFire system emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "mcf.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "loader.h"
+#include "elf.h"
+
+#define KERNEL_LOAD_ADDR 0x10000
+#define AN5206_MBAR_ADDR 0x10000000
+#define AN5206_RAMBAR_ADDR 0x20000000
+
+/* Stub functions for hardware that doesn't exist. */
+void pic_info(Monitor *mon)
+{
+}
+
+void irq_info(Monitor *mon)
+{
+}
+
+/* Board init. */
+
+static void an5206_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ int kernel_size;
+ uint64_t elf_entry;
+ target_phys_addr_t entry;
+
+ if (!cpu_model)
+ cpu_model = "m5206";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ hw_error("Unable to find m68k CPU definition\n");
+ }
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+ /* TODO: allow changing MBAR and RAMBAR. */
+ env->mbar = AN5206_MBAR_ADDR | 1;
+ env->rambar0 = AN5206_RAMBAR_ADDR | 1;
+
+ /* DRAM at address zero */
+ cpu_register_physical_memory(0, ram_size,
+ qemu_ram_alloc(NULL, "an5206.ram", ram_size) | IO_MEM_RAM);
+
+ /* Internal SRAM. */
+ cpu_register_physical_memory(AN5206_RAMBAR_ADDR, 512,
+ qemu_ram_alloc(NULL, "an5206.sram", 512) | IO_MEM_RAM);
+
+ mcf5206_init(AN5206_MBAR_ADDR, env);
+
+ /* Load kernel. */
+ if (!kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ NULL, NULL, 1, ELF_MACHINE, 0);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR,
+ ram_size - KERNEL_LOAD_ADDR);
+ entry = KERNEL_LOAD_ADDR;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+
+ env->pc = entry;
+}
+
+static QEMUMachine an5206_machine = {
+ .name = "an5206",
+ .desc = "Arnewsh 5206",
+ .init = an5206_init,
+};
+
+static void an5206_machine_init(void)
+{
+ qemu_register_machine(&an5206_machine);
+}
+
+machine_init(an5206_machine_init);
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
new file mode 100644
index 0000000..84e9af7
--- /dev/null
+++ b/hw/apb_pci.c
@@ -0,0 +1,482 @@
+/*
+ * QEMU Ultrasparc APB PCI host
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* XXX This file and most of its contents are somewhat misnamed. The
+ Ultrasparc PCI host is called the PCI Bus Module (PBM). The APB is
+ the secondary PCI bridge. */
+
+#include "sysbus.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "pci_bridge.h"
+#include "pci_internals.h"
+#include "rwhandler.h"
+#include "apb_pci.h"
+#include "sysemu.h"
+
+/* debug APB */
+//#define DEBUG_APB
+
+#ifdef DEBUG_APB
+#define APB_DPRINTF(fmt, ...) \
+do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define APB_DPRINTF(fmt, ...)
+#endif
+
+/*
+ * Chipset docs:
+ * PBM: "UltraSPARC IIi User's Manual",
+ * http://www.sun.com/processors/manuals/805-0087.pdf
+ *
+ * APB: "Advanced PCI Bridge (APB) User's Manual",
+ * http://www.sun.com/processors/manuals/805-1251.pdf
+ */
+
+#define PBM_PCI_IMR_MASK 0x7fffffff
+#define PBM_PCI_IMR_ENABLED 0x80000000
+
+#define POR (1 << 31)
+#define SOFT_POR (1 << 30)
+#define SOFT_XIR (1 << 29)
+#define BTN_POR (1 << 28)
+#define BTN_XIR (1 << 27)
+#define RESET_MASK 0xf8000000
+#define RESET_WCMASK 0x98000000
+#define RESET_WMASK 0x60000000
+
+typedef struct APBState {
+ SysBusDevice busdev;
+ PCIBus *bus;
+ ReadWriteHandler pci_config_handler;
+ uint32_t iommu[4];
+ uint32_t pci_control[16];
+ uint32_t pci_irq_map[8];
+ uint32_t obio_irq_map[32];
+ qemu_irq pci_irqs[32];
+ uint32_t reset_control;
+ unsigned int nr_resets;
+} APBState;
+
+static void apb_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ APBState *s = opaque;
+
+ APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
+
+ switch (addr & 0xffff) {
+ case 0x30 ... 0x4f: /* DMA error registers */
+ /* XXX: not implemented yet */
+ break;
+ case 0x200 ... 0x20b: /* IOMMU */
+ s->iommu[(addr & 0xf) >> 2] = val;
+ break;
+ case 0x20c ... 0x3ff: /* IOMMU flush */
+ break;
+ case 0xc00 ... 0xc3f: /* PCI interrupt control */
+ if (addr & 4) {
+ s->pci_irq_map[(addr & 0x3f) >> 3] &= PBM_PCI_IMR_MASK;
+ s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK;
+ }
+ break;
+ case 0x2000 ... 0x202f: /* PCI control */
+ s->pci_control[(addr & 0x3f) >> 2] = val;
+ break;
+ case 0xf020 ... 0xf027: /* Reset control */
+ if (addr & 4) {
+ val &= RESET_MASK;
+ s->reset_control &= ~(val & RESET_WCMASK);
+ s->reset_control |= val & RESET_WMASK;
+ if (val & SOFT_POR) {
+ s->nr_resets = 0;
+ qemu_system_reset_request();
+ } else if (val & SOFT_XIR) {
+ qemu_system_reset_request();
+ }
+ }
+ break;
+ case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */
+ case 0xa400 ... 0xa67f: /* IOMMU diagnostics */
+ case 0xa800 ... 0xa80f: /* Interrupt diagnostics */
+ case 0xf000 ... 0xf01f: /* FFB config, memory control */
+ /* we don't care */
+ default:
+ break;
+ }
+}
+
+static uint32_t apb_config_readl (void *opaque,
+ target_phys_addr_t addr)
+{
+ APBState *s = opaque;
+ uint32_t val;
+
+ switch (addr & 0xffff) {
+ case 0x30 ... 0x4f: /* DMA error registers */
+ val = 0;
+ /* XXX: not implemented yet */
+ break;
+ case 0x200 ... 0x20b: /* IOMMU */
+ val = s->iommu[(addr & 0xf) >> 2];
+ break;
+ case 0x20c ... 0x3ff: /* IOMMU flush */
+ val = 0;
+ break;
+ case 0xc00 ... 0xc3f: /* PCI interrupt control */
+ if (addr & 4) {
+ val = s->pci_irq_map[(addr & 0x3f) >> 3];
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x2000 ... 0x202f: /* PCI control */
+ val = s->pci_control[(addr & 0x3f) >> 2];
+ break;
+ case 0xf020 ... 0xf027: /* Reset control */
+ if (addr & 4) {
+ val = s->reset_control;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */
+ case 0xa400 ... 0xa67f: /* IOMMU diagnostics */
+ case 0xa800 ... 0xa80f: /* Interrupt diagnostics */
+ case 0xf000 ... 0xf01f: /* FFB config, memory control */
+ /* we don't care */
+ default:
+ val = 0;
+ break;
+ }
+ APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, val);
+
+ return val;
+}
+
+static CPUWriteMemoryFunc * const apb_config_write[] = {
+ &apb_config_writel,
+ &apb_config_writel,
+ &apb_config_writel,
+};
+
+static CPUReadMemoryFunc * const apb_config_read[] = {
+ &apb_config_readl,
+ &apb_config_readl,
+ &apb_config_readl,
+};
+
+static void apb_pci_config_write(ReadWriteHandler *h, pcibus_t addr,
+ uint32_t val, int size)
+{
+ APBState *s = container_of(h, APBState, pci_config_handler);
+
+ val = qemu_bswap_len(val, size);
+ APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
+ pci_data_write(s->bus, addr, val, size);
+}
+
+static uint32_t apb_pci_config_read(ReadWriteHandler *h, pcibus_t addr,
+ int size)
+{
+ uint32_t ret;
+ APBState *s = container_of(h, APBState, pci_config_handler);
+
+ ret = pci_data_read(s->bus, addr, size);
+ ret = qemu_bswap_len(ret, size);
+ APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, ret);
+ return ret;
+}
+
+static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outb(addr & IOPORTS_MASK, val);
+}
+
+static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outw(addr & IOPORTS_MASK, bswap16(val));
+}
+
+static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outl(addr & IOPORTS_MASK, bswap32(val));
+}
+
+static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+
+ val = cpu_inb(addr & IOPORTS_MASK);
+ return val;
+}
+
+static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+
+ val = bswap16(cpu_inw(addr & IOPORTS_MASK));
+ return val;
+}
+
+static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+
+ val = bswap32(cpu_inl(addr & IOPORTS_MASK));
+ return val;
+}
+
+static CPUWriteMemoryFunc * const pci_apb_iowrite[] = {
+ &pci_apb_iowriteb,
+ &pci_apb_iowritew,
+ &pci_apb_iowritel,
+};
+
+static CPUReadMemoryFunc * const pci_apb_ioread[] = {
+ &pci_apb_ioreadb,
+ &pci_apb_ioreadw,
+ &pci_apb_ioreadl,
+};
+
+/* The APB host has an IRQ line for each IRQ line of each slot. */
+static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return ((pci_dev->devfn & 0x18) >> 1) + irq_num;
+}
+
+static int pci_pbm_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int bus_offset;
+ if (pci_dev->devfn & 1)
+ bus_offset = 16;
+ else
+ bus_offset = 0;
+ return bus_offset + irq_num;
+}
+
+static void pci_apb_set_irq(void *opaque, int irq_num, int level)
+{
+ APBState *s = opaque;
+
+ /* PCI IRQ map onto the first 32 INO. */
+ if (irq_num < 32) {
+ if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
+ APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
+ qemu_set_irq(s->pci_irqs[irq_num], level);
+ } else {
+ APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
+ qemu_irq_lower(s->pci_irqs[irq_num]);
+ }
+ }
+}
+
+static int apb_pci_bridge_initfn(PCIDevice *dev)
+{
+ int rc;
+
+ rc = pci_bridge_initfn(dev);
+ if (rc < 0) {
+ return rc;
+ }
+
+ pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_SUN);
+ pci_config_set_device_id(dev->config, PCI_DEVICE_ID_SUN_SIMBA);
+
+ /*
+ * command register:
+ * According to PCI bridge spec, after reset
+ * bus master bit is off
+ * memory space enable bit is off
+ * According to manual (805-1251.pdf).
+ * the reset value should be zero unless the boot pin is tied high
+ * (which is true) and thus it should be PCI_COMMAND_MEMORY.
+ */
+ pci_set_word(dev->config + PCI_COMMAND,
+ PCI_COMMAND_MEMORY);
+ pci_set_word(dev->config + PCI_STATUS,
+ PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
+ PCI_STATUS_DEVSEL_MEDIUM);
+ pci_set_byte(dev->config + PCI_REVISION_ID, 0x11);
+ return 0;
+}
+
+PCIBus *pci_apb_init(target_phys_addr_t special_base,
+ target_phys_addr_t mem_base,
+ qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ APBState *d;
+ unsigned int i;
+ PCIDevice *pci_dev;
+ PCIBridge *br;
+
+ /* Ultrasparc PBM main bus */
+ dev = qdev_create(NULL, "pbm");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ /* apb_config */
+ sysbus_mmio_map(s, 0, special_base);
+ /* PCI configuration space */
+ sysbus_mmio_map(s, 1, special_base + 0x1000000ULL);
+ /* pci_ioport */
+ sysbus_mmio_map(s, 2, special_base + 0x2000000ULL);
+ d = FROM_SYSBUS(APBState, s);
+
+ d->bus = pci_register_bus(&d->busdev.qdev, "pci",
+ pci_apb_set_irq, pci_pbm_map_irq, d,
+ 0, 32);
+ pci_bus_set_mem_base(d->bus, mem_base);
+
+ for (i = 0; i < 32; i++) {
+ sysbus_connect_irq(s, i, pic[i]);
+ }
+
+ pci_create_simple(d->bus, 0, "pbm");
+
+ /* APB secondary busses */
+ pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
+ "pbm-bridge");
+ br = DO_UPCAST(PCIBridge, dev, pci_dev);
+ pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
+ pci_apb_map_irq);
+ qdev_init_nofail(&pci_dev->qdev);
+ *bus2 = pci_bridge_get_sec_bus(br);
+
+ pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
+ "pbm-bridge");
+ br = DO_UPCAST(PCIBridge, dev, pci_dev);
+ pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
+ pci_apb_map_irq);
+ qdev_init_nofail(&pci_dev->qdev);
+ *bus3 = pci_bridge_get_sec_bus(br);
+
+ return d->bus;
+}
+
+static void pci_pbm_reset(DeviceState *d)
+{
+ unsigned int i;
+ APBState *s = container_of(d, APBState, busdev.qdev);
+
+ for (i = 0; i < 8; i++) {
+ s->pci_irq_map[i] &= PBM_PCI_IMR_MASK;
+ }
+
+ if (s->nr_resets++ == 0) {
+ /* Power on reset */
+ s->reset_control = POR;
+ }
+}
+
+static int pci_pbm_init_device(SysBusDevice *dev)
+{
+ APBState *s;
+ int pci_config, apb_config, pci_ioport;
+ unsigned int i;
+
+ s = FROM_SYSBUS(APBState, dev);
+ for (i = 0; i < 8; i++) {
+ s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
+ }
+ for (i = 0; i < 32; i++) {
+ sysbus_init_irq(dev, &s->pci_irqs[i]);
+ }
+
+ /* apb_config */
+ apb_config = cpu_register_io_memory(apb_config_read,
+ apb_config_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ /* at region 0 */
+ sysbus_init_mmio(dev, 0x10000ULL, apb_config);
+
+ /* PCI configuration space */
+ s->pci_config_handler.read = apb_pci_config_read;
+ s->pci_config_handler.write = apb_pci_config_write;
+ pci_config = cpu_register_io_memory_simple(&s->pci_config_handler,
+ DEVICE_NATIVE_ENDIAN);
+ assert(pci_config >= 0);
+ /* at region 1 */
+ sysbus_init_mmio(dev, 0x1000000ULL, pci_config);
+
+ /* pci_ioport */
+ pci_ioport = cpu_register_io_memory(pci_apb_ioread,
+ pci_apb_iowrite, s,
+ DEVICE_NATIVE_ENDIAN);
+ /* at region 2 */
+ sysbus_init_mmio(dev, 0x10000ULL, pci_ioport);
+
+ return 0;
+}
+
+static int pbm_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_SUN);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_SUN_SABRE);
+ pci_set_word(d->config + PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ pci_set_word(d->config + PCI_STATUS,
+ PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
+ PCI_STATUS_DEVSEL_MEDIUM);
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ return 0;
+}
+
+static PCIDeviceInfo pbm_pci_host_info = {
+ .qdev.name = "pbm",
+ .qdev.size = sizeof(PCIDevice),
+ .init = pbm_pci_host_init,
+ .is_bridge = 1,
+};
+
+static SysBusDeviceInfo pbm_host_info = {
+ .qdev.name = "pbm",
+ .qdev.size = sizeof(APBState),
+ .qdev.reset = pci_pbm_reset,
+ .init = pci_pbm_init_device,
+};
+
+static PCIDeviceInfo pbm_pci_bridge_info = {
+ .qdev.name = "pbm-bridge",
+ .qdev.size = sizeof(PCIBridge),
+ .qdev.vmsd = &vmstate_pci_device,
+ .qdev.reset = pci_bridge_reset,
+ .init = apb_pci_bridge_initfn,
+ .exit = pci_bridge_exitfn,
+ .config_write = pci_bridge_write_config,
+ .is_bridge = 1,
+};
+
+static void pbm_register_devices(void)
+{
+ sysbus_register_withprop(&pbm_host_info);
+ pci_qdev_register(&pbm_pci_host_info);
+ pci_qdev_register(&pbm_pci_bridge_info);
+}
+
+device_init(pbm_register_devices)
diff --git a/hw/apb_pci.h b/hw/apb_pci.h
new file mode 100644
index 0000000..8869f9d
--- /dev/null
+++ b/hw/apb_pci.h
@@ -0,0 +1,9 @@
+#ifndef APB_PCI_H
+#define APB_PCI_H
+
+#include "qemu-common.h"
+
+PCIBus *pci_apb_init(target_phys_addr_t special_base,
+ target_phys_addr_t mem_base,
+ qemu_irq *pic, PCIBus **bus2, PCIBus **bus3);
+#endif
diff --git a/hw/apic.c b/hw/apic.c
new file mode 100644
index 0000000..00907e0
--- /dev/null
+++ b/hw/apic.c
@@ -0,0 +1,1149 @@
+/*
+ * APIC support
+ *
+ * Copyright (c) 2004-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 "hw.h"
+#include "apic.h"
+#include "ioapic.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+#include "sysbus.h"
+#include "trace.h"
+#include "kvm.h"
+
+/* APIC Local Vector Table */
+#define APIC_LVT_TIMER 0
+#define APIC_LVT_THERMAL 1
+#define APIC_LVT_PERFORM 2
+#define APIC_LVT_LINT0 3
+#define APIC_LVT_LINT1 4
+#define APIC_LVT_ERROR 5
+#define APIC_LVT_NB 6
+
+/* APIC delivery modes */
+#define APIC_DM_FIXED 0
+#define APIC_DM_LOWPRI 1
+#define APIC_DM_SMI 2
+#define APIC_DM_NMI 4
+#define APIC_DM_INIT 5
+#define APIC_DM_SIPI 6
+#define APIC_DM_EXTINT 7
+
+/* APIC destination mode */
+#define APIC_DESTMODE_FLAT 0xf
+#define APIC_DESTMODE_CLUSTER 1
+
+#define APIC_TRIGGER_EDGE 0
+#define APIC_TRIGGER_LEVEL 1
+
+#define APIC_LVT_TIMER_PERIODIC (1<<17)
+#define APIC_LVT_MASKED (1<<16)
+#define APIC_LVT_LEVEL_TRIGGER (1<<15)
+#define APIC_LVT_REMOTE_IRR (1<<14)
+#define APIC_INPUT_POLARITY (1<<13)
+#define APIC_SEND_PENDING (1<<12)
+
+#define ESR_ILLEGAL_ADDRESS (1 << 7)
+
+#define APIC_SV_DIRECTED_IO (1<<12)
+#define APIC_SV_ENABLE (1<<8)
+
+#define MAX_APICS 255
+#define MAX_APIC_WORDS 8
+
+/* Intel APIC constants: from include/asm/msidef.h */
+#define MSI_DATA_VECTOR_SHIFT 0
+#define MSI_DATA_VECTOR_MASK 0x000000ff
+#define MSI_DATA_DELIVERY_MODE_SHIFT 8
+#define MSI_DATA_TRIGGER_SHIFT 15
+#define MSI_DATA_LEVEL_SHIFT 14
+#define MSI_ADDR_DEST_MODE_SHIFT 2
+#define MSI_ADDR_DEST_ID_SHIFT 12
+#define MSI_ADDR_DEST_ID_MASK 0x00ffff0
+
+#define MSI_ADDR_SIZE 0x100000
+
+typedef struct APICState APICState;
+
+struct APICState {
+ SysBusDevice busdev;
+ void *cpu_env;
+ uint32_t apicbase;
+ uint8_t id;
+ uint8_t arb_id;
+ uint8_t tpr;
+ uint32_t spurious_vec;
+ uint8_t log_dest;
+ uint8_t dest_mode;
+ uint32_t isr[8]; /* in service register */
+ uint32_t tmr[8]; /* trigger mode register */
+ uint32_t irr[8]; /* interrupt request register */
+ uint32_t lvt[APIC_LVT_NB];
+ uint32_t esr; /* error register */
+ uint32_t icr[2];
+
+ uint32_t divide_conf;
+ int count_shift;
+ uint32_t initial_count;
+ int64_t initial_count_load_time, next_time;
+ uint32_t idx;
+ QEMUTimer *timer;
+ int sipi_vector;
+ int wait_for_sipi;
+};
+
+static APICState *local_apics[MAX_APICS + 1];
+static int apic_irq_delivered;
+
+static void apic_set_irq(APICState *s, int vector_num, int trigger_mode);
+static void apic_update_irq(APICState *s);
+static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
+ uint8_t dest, uint8_t dest_mode);
+
+/* Find first bit starting from msb */
+static int fls_bit(uint32_t value)
+{
+ return 31 - clz32(value);
+}
+
+/* Find first bit starting from lsb */
+static int ffs_bit(uint32_t value)
+{
+ return ctz32(value);
+}
+
+static inline void set_bit(uint32_t *tab, int index)
+{
+ int i, mask;
+ i = index >> 5;
+ mask = 1 << (index & 0x1f);
+ tab[i] |= mask;
+}
+
+static inline void reset_bit(uint32_t *tab, int index)
+{
+ int i, mask;
+ i = index >> 5;
+ mask = 1 << (index & 0x1f);
+ tab[i] &= ~mask;
+}
+
+static inline int get_bit(uint32_t *tab, int index)
+{
+ int i, mask;
+ i = index >> 5;
+ mask = 1 << (index & 0x1f);
+ return !!(tab[i] & mask);
+}
+
+static void apic_local_deliver(APICState *s, int vector)
+{
+ uint32_t lvt = s->lvt[vector];
+ int trigger_mode;
+
+ trace_apic_local_deliver(vector, (lvt >> 8) & 7);
+
+ if (lvt & APIC_LVT_MASKED)
+ return;
+
+ switch ((lvt >> 8) & 7) {
+ case APIC_DM_SMI:
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_SMI);
+ break;
+
+ case APIC_DM_NMI:
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_NMI);
+ break;
+
+ case APIC_DM_EXTINT:
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ break;
+
+ case APIC_DM_FIXED:
+ trigger_mode = APIC_TRIGGER_EDGE;
+ if ((vector == APIC_LVT_LINT0 || vector == APIC_LVT_LINT1) &&
+ (lvt & APIC_LVT_LEVEL_TRIGGER))
+ trigger_mode = APIC_TRIGGER_LEVEL;
+ apic_set_irq(s, lvt & 0xff, trigger_mode);
+ }
+}
+
+void apic_deliver_pic_intr(DeviceState *d, int level)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ if (level) {
+ apic_local_deliver(s, APIC_LVT_LINT0);
+ } else {
+ uint32_t lvt = s->lvt[APIC_LVT_LINT0];
+
+ switch ((lvt >> 8) & 7) {
+ case APIC_DM_FIXED:
+ if (!(lvt & APIC_LVT_LEVEL_TRIGGER))
+ break;
+ reset_bit(s->irr, lvt & 0xff);
+ /* fall through */
+ case APIC_DM_EXTINT:
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ break;
+ }
+ }
+}
+
+#define foreach_apic(apic, deliver_bitmask, code) \
+{\
+ int __i, __j, __mask;\
+ for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
+ __mask = deliver_bitmask[__i];\
+ if (__mask) {\
+ for(__j = 0; __j < 32; __j++) {\
+ if (__mask & (1 << __j)) {\
+ apic = local_apics[__i * 32 + __j];\
+ if (apic) {\
+ code;\
+ }\
+ }\
+ }\
+ }\
+ }\
+}
+
+static void apic_bus_deliver(const uint32_t *deliver_bitmask,
+ uint8_t delivery_mode,
+ uint8_t vector_num, uint8_t polarity,
+ uint8_t trigger_mode)
+{
+ APICState *apic_iter;
+
+ switch (delivery_mode) {
+ case APIC_DM_LOWPRI:
+ /* XXX: search for focus processor, arbitration */
+ {
+ int i, d;
+ d = -1;
+ for(i = 0; i < MAX_APIC_WORDS; i++) {
+ if (deliver_bitmask[i]) {
+ d = i * 32 + ffs_bit(deliver_bitmask[i]);
+ break;
+ }
+ }
+ if (d >= 0) {
+ apic_iter = local_apics[d];
+ if (apic_iter) {
+ apic_set_irq(apic_iter, vector_num, trigger_mode);
+ }
+ }
+ }
+ return;
+
+ case APIC_DM_FIXED:
+ break;
+
+ case APIC_DM_SMI:
+ foreach_apic(apic_iter, deliver_bitmask,
+ cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_SMI) );
+ return;
+
+ case APIC_DM_NMI:
+ foreach_apic(apic_iter, deliver_bitmask,
+ cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_NMI) );
+ return;
+
+ case APIC_DM_INIT:
+ /* normal INIT IPI sent to processors */
+ foreach_apic(apic_iter, deliver_bitmask,
+ cpu_interrupt(apic_iter->cpu_env, CPU_INTERRUPT_INIT) );
+ return;
+
+ case APIC_DM_EXTINT:
+ /* handled in I/O APIC code */
+ break;
+
+ default:
+ return;
+ }
+
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_set_irq(apic_iter, vector_num, trigger_mode) );
+}
+
+void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
+ uint8_t delivery_mode, uint8_t vector_num,
+ uint8_t polarity, uint8_t trigger_mode)
+{
+ uint32_t deliver_bitmask[MAX_APIC_WORDS];
+
+ trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num,
+ polarity, trigger_mode);
+
+ apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
+ apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
+ trigger_mode);
+}
+
+void cpu_set_apic_base(DeviceState *d, uint64_t val)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ trace_cpu_set_apic_base(val);
+
+ if (!s)
+ return;
+ if (kvm_enabled() && kvm_irqchip_in_kernel())
+ s->apicbase = val;
+ else
+ s->apicbase = (val & 0xfffff000) |
+ (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
+ /* if disabled, cannot be enabled again */
+ if (!(val & MSR_IA32_APICBASE_ENABLE)) {
+ s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
+ cpu_clear_apic_feature(s->cpu_env);
+ s->spurious_vec &= ~APIC_SV_ENABLE;
+ }
+}
+
+uint64_t cpu_get_apic_base(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ trace_cpu_get_apic_base(s ? (uint64_t)s->apicbase: 0);
+
+ return s ? s->apicbase : 0;
+}
+
+void cpu_set_apic_tpr(DeviceState *d, uint8_t val)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ if (!s)
+ return;
+ s->tpr = (val & 0x0f) << 4;
+ apic_update_irq(s);
+}
+
+uint8_t cpu_get_apic_tpr(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ return s ? s->tpr >> 4 : 0;
+}
+
+/* return -1 if no bit is set */
+static int get_highest_priority_int(uint32_t *tab)
+{
+ int i;
+ for(i = 7; i >= 0; i--) {
+ if (tab[i] != 0) {
+ return i * 32 + fls_bit(tab[i]);
+ }
+ }
+ return -1;
+}
+
+static int apic_get_ppr(APICState *s)
+{
+ int tpr, isrv, ppr;
+
+ tpr = (s->tpr >> 4);
+ isrv = get_highest_priority_int(s->isr);
+ if (isrv < 0)
+ isrv = 0;
+ isrv >>= 4;
+ if (tpr >= isrv)
+ ppr = s->tpr;
+ else
+ ppr = isrv << 4;
+ return ppr;
+}
+
+static int apic_get_arb_pri(APICState *s)
+{
+ /* XXX: arbitration */
+ return 0;
+}
+
+
+/*
+ * <0 - low prio interrupt,
+ * 0 - no interrupt,
+ * >0 - interrupt number
+ */
+static int apic_irq_pending(APICState *s)
+{
+ int irrv, ppr;
+ irrv = get_highest_priority_int(s->irr);
+ if (irrv < 0) {
+ return 0;
+ }
+ ppr = apic_get_ppr(s);
+ if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) {
+ return -1;
+ }
+
+ return irrv;
+}
+
+/* signal the CPU if an irq is pending */
+static void apic_update_irq(APICState *s)
+{
+ if (!(s->spurious_vec & APIC_SV_ENABLE)) {
+ return;
+ }
+ if (apic_irq_pending(s) > 0) {
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ }
+}
+
+void apic_reset_irq_delivered(void)
+{
+ trace_apic_reset_irq_delivered(apic_irq_delivered);
+
+ apic_irq_delivered = 0;
+}
+
+int apic_get_irq_delivered(void)
+{
+ trace_apic_get_irq_delivered(apic_irq_delivered);
+
+ return apic_irq_delivered;
+}
+
+void apic_set_irq_delivered(void)
+{
+ apic_irq_delivered = 1;
+}
+
+static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
+{
+ apic_irq_delivered += !get_bit(s->irr, vector_num);
+
+ trace_apic_set_irq(apic_irq_delivered);
+
+ set_bit(s->irr, vector_num);
+ if (trigger_mode)
+ set_bit(s->tmr, vector_num);
+ else
+ reset_bit(s->tmr, vector_num);
+ apic_update_irq(s);
+}
+
+static void apic_eoi(APICState *s)
+{
+ int isrv;
+ isrv = get_highest_priority_int(s->isr);
+ if (isrv < 0)
+ return;
+ reset_bit(s->isr, isrv);
+ if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) {
+ ioapic_eoi_broadcast(isrv);
+ }
+ apic_update_irq(s);
+}
+
+static int apic_find_dest(uint8_t dest)
+{
+ APICState *apic = local_apics[dest];
+ int i;
+
+ if (apic && apic->id == dest)
+ return dest; /* shortcut in case apic->id == apic->idx */
+
+ for (i = 0; i < MAX_APICS; i++) {
+ apic = local_apics[i];
+ if (apic && apic->id == dest)
+ return i;
+ if (!apic)
+ break;
+ }
+
+ return -1;
+}
+
+static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
+ uint8_t dest, uint8_t dest_mode)
+{
+ APICState *apic_iter;
+ int i;
+
+ if (dest_mode == 0) {
+ if (dest == 0xff) {
+ memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t));
+ } else {
+ int idx = apic_find_dest(dest);
+ memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
+ if (idx >= 0)
+ set_bit(deliver_bitmask, idx);
+ }
+ } else {
+ /* XXX: cluster mode */
+ memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
+ for(i = 0; i < MAX_APICS; i++) {
+ apic_iter = local_apics[i];
+ if (apic_iter) {
+ if (apic_iter->dest_mode == 0xf) {
+ if (dest & apic_iter->log_dest)
+ set_bit(deliver_bitmask, i);
+ } else if (apic_iter->dest_mode == 0x0) {
+ if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
+ (dest & apic_iter->log_dest & 0x0f)) {
+ set_bit(deliver_bitmask, i);
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+void apic_init_reset(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+ int i;
+
+ if (!s)
+ return;
+
+ s->tpr = 0;
+ s->spurious_vec = 0xff;
+ s->log_dest = 0;
+ s->dest_mode = 0xf;
+ memset(s->isr, 0, sizeof(s->isr));
+ memset(s->tmr, 0, sizeof(s->tmr));
+ memset(s->irr, 0, sizeof(s->irr));
+ for(i = 0; i < APIC_LVT_NB; i++)
+ s->lvt[i] = 1 << 16; /* mask LVT */
+ s->esr = 0;
+ memset(s->icr, 0, sizeof(s->icr));
+ s->divide_conf = 0;
+ s->count_shift = 0;
+ s->initial_count = 0;
+ s->initial_count_load_time = 0;
+ s->next_time = 0;
+ s->wait_for_sipi = 1;
+}
+
+static void apic_startup(APICState *s, int vector_num)
+{
+ s->sipi_vector = vector_num;
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI);
+}
+
+void apic_sipi(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI);
+
+ if (!s->wait_for_sipi)
+ return;
+ cpu_x86_load_seg_cache_sipi(s->cpu_env, s->sipi_vector);
+ s->wait_for_sipi = 0;
+}
+
+static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode,
+ uint8_t delivery_mode, uint8_t vector_num,
+ uint8_t polarity, uint8_t trigger_mode)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+ uint32_t deliver_bitmask[MAX_APIC_WORDS];
+ int dest_shorthand = (s->icr[0] >> 18) & 3;
+ APICState *apic_iter;
+
+ switch (dest_shorthand) {
+ case 0:
+ apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
+ break;
+ case 1:
+ memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
+ set_bit(deliver_bitmask, s->idx);
+ break;
+ case 2:
+ memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
+ break;
+ case 3:
+ memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
+ reset_bit(deliver_bitmask, s->idx);
+ break;
+ }
+
+ switch (delivery_mode) {
+ case APIC_DM_INIT:
+ {
+ int trig_mode = (s->icr[0] >> 15) & 1;
+ int level = (s->icr[0] >> 14) & 1;
+ if (level == 0 && trig_mode == 1) {
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_iter->arb_id = apic_iter->id );
+ return;
+ }
+ }
+ break;
+
+ case APIC_DM_SIPI:
+ foreach_apic(apic_iter, deliver_bitmask,
+ apic_startup(apic_iter, vector_num) );
+ return;
+ }
+
+ apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity,
+ trigger_mode);
+}
+
+int apic_get_interrupt(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+ int intno;
+
+ /* if the APIC is installed or enabled, we let the 8259 handle the
+ IRQs */
+ if (!s)
+ return -1;
+ if (!(s->spurious_vec & APIC_SV_ENABLE))
+ return -1;
+
+ intno = apic_irq_pending(s);
+
+ if (intno == 0) {
+ return -1;
+ } else if (intno < 0) {
+ return s->spurious_vec & 0xff;
+ }
+ reset_bit(s->irr, intno);
+ set_bit(s->isr, intno);
+ apic_update_irq(s);
+ return intno;
+}
+
+int apic_accept_pic_intr(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+ uint32_t lvt0;
+
+ if (!s)
+ return -1;
+
+ lvt0 = s->lvt[APIC_LVT_LINT0];
+
+ if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 ||
+ (lvt0 & APIC_LVT_MASKED) == 0)
+ return 1;
+
+ return 0;
+}
+
+static uint32_t apic_get_current_count(APICState *s)
+{
+ int64_t d;
+ uint32_t val;
+ d = (qemu_get_clock(vm_clock) - s->initial_count_load_time) >>
+ s->count_shift;
+ if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+ /* periodic */
+ val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
+ } else {
+ if (d >= s->initial_count)
+ val = 0;
+ else
+ val = s->initial_count - d;
+ }
+ return val;
+}
+
+static void apic_timer_update(APICState *s, int64_t current_time)
+{
+ int64_t next_time, d;
+
+ if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
+ d = (current_time - s->initial_count_load_time) >>
+ s->count_shift;
+ if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+ if (!s->initial_count)
+ goto no_timer;
+ d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1);
+ } else {
+ if (d >= s->initial_count)
+ goto no_timer;
+ d = (uint64_t)s->initial_count + 1;
+ }
+ next_time = s->initial_count_load_time + (d << s->count_shift);
+ qemu_mod_timer(s->timer, next_time);
+ s->next_time = next_time;
+ } else {
+ no_timer:
+ qemu_del_timer(s->timer);
+ }
+}
+
+static void apic_timer(void *opaque)
+{
+ APICState *s = opaque;
+
+ apic_local_deliver(s, APIC_LVT_TIMER);
+ apic_timer_update(s, s->next_time);
+}
+
+static uint32_t apic_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static uint32_t apic_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void apic_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static void apic_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ DeviceState *d;
+ APICState *s;
+ uint32_t val;
+ int index;
+
+ d = cpu_get_current_apic();
+ if (!d) {
+ return 0;
+ }
+ s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ index = (addr >> 4) & 0xff;
+ switch(index) {
+ case 0x02: /* id */
+ val = s->id << 24;
+ break;
+ case 0x03: /* version */
+ val = 0x11 | ((APIC_LVT_NB - 1) << 16); /* version 0x11 */
+ break;
+ case 0x08:
+ val = s->tpr;
+ break;
+ case 0x09:
+ val = apic_get_arb_pri(s);
+ break;
+ case 0x0a:
+ /* ppr */
+ val = apic_get_ppr(s);
+ break;
+ case 0x0b:
+ val = 0;
+ break;
+ case 0x0d:
+ val = s->log_dest << 24;
+ break;
+ case 0x0e:
+ val = s->dest_mode << 28;
+ break;
+ case 0x0f:
+ val = s->spurious_vec;
+ break;
+ case 0x10 ... 0x17:
+ val = s->isr[index & 7];
+ break;
+ case 0x18 ... 0x1f:
+ val = s->tmr[index & 7];
+ break;
+ case 0x20 ... 0x27:
+ val = s->irr[index & 7];
+ break;
+ case 0x28:
+ val = s->esr;
+ break;
+ case 0x30:
+ case 0x31:
+ val = s->icr[index & 1];
+ break;
+ case 0x32 ... 0x37:
+ val = s->lvt[index - 0x32];
+ break;
+ case 0x38:
+ val = s->initial_count;
+ break;
+ case 0x39:
+ val = apic_get_current_count(s);
+ break;
+ case 0x3e:
+ val = s->divide_conf;
+ break;
+ default:
+ s->esr |= ESR_ILLEGAL_ADDRESS;
+ val = 0;
+ break;
+ }
+ trace_apic_mem_readl(addr, val);
+ return val;
+}
+
+static void apic_send_msi(target_phys_addr_t addr, uint32_t data)
+{
+ uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
+ uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
+ uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
+ uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
+ uint8_t delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
+ /* XXX: Ignore redirection hint. */
+ apic_deliver_irq(dest, dest_mode, delivery, vector, 0, trigger_mode);
+}
+
+static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ DeviceState *d;
+ APICState *s;
+ int index = (addr >> 4) & 0xff;
+ if (addr > 0xfff || !index) {
+ /* MSI and MMIO APIC are at the same memory location,
+ * but actually not on the global bus: MSI is on PCI bus
+ * APIC is connected directly to the CPU.
+ * Mapping them on the global bus happens to work because
+ * MSI registers are reserved in APIC MMIO and vice versa. */
+ apic_send_msi(addr, val);
+ return;
+ }
+
+ d = cpu_get_current_apic();
+ if (!d) {
+ return;
+ }
+ s = DO_UPCAST(APICState, busdev.qdev, d);
+
+ trace_apic_mem_writel(addr, val);
+
+ switch(index) {
+ case 0x02:
+ s->id = (val >> 24);
+ break;
+ case 0x03:
+ break;
+ case 0x08:
+ s->tpr = val;
+ apic_update_irq(s);
+ break;
+ case 0x09:
+ case 0x0a:
+ break;
+ case 0x0b: /* EOI */
+ apic_eoi(s);
+ break;
+ case 0x0d:
+ s->log_dest = val >> 24;
+ break;
+ case 0x0e:
+ s->dest_mode = val >> 28;
+ break;
+ case 0x0f:
+ s->spurious_vec = val & 0x1ff;
+ apic_update_irq(s);
+ break;
+ case 0x10 ... 0x17:
+ case 0x18 ... 0x1f:
+ case 0x20 ... 0x27:
+ case 0x28:
+ break;
+ case 0x30:
+ s->icr[0] = val;
+ apic_deliver(d, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
+ (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff),
+ (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1);
+ break;
+ case 0x31:
+ s->icr[1] = val;
+ break;
+ case 0x32 ... 0x37:
+ {
+ int n = index - 0x32;
+ s->lvt[n] = val;
+ if (n == APIC_LVT_TIMER)
+ apic_timer_update(s, qemu_get_clock(vm_clock));
+ }
+ break;
+ case 0x38:
+ s->initial_count = val;
+ s->initial_count_load_time = qemu_get_clock(vm_clock);
+ apic_timer_update(s, s->initial_count_load_time);
+ break;
+ case 0x39:
+ break;
+ case 0x3e:
+ {
+ int v;
+ s->divide_conf = val & 0xb;
+ v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
+ s->count_shift = (v + 1) & 7;
+ }
+ break;
+ default:
+ s->esr |= ESR_ILLEGAL_ADDRESS;
+ break;
+ }
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+static inline uint32_t kapic_reg(struct kvm_lapic_state *kapic, int reg_id)
+{
+ return *((uint32_t *) (kapic->regs + (reg_id << 4)));
+}
+
+static inline void kapic_set_reg(struct kvm_lapic_state *kapic,
+ int reg_id, uint32_t val)
+{
+ *((uint32_t *) (kapic->regs + (reg_id << 4))) = val;
+}
+
+static void kvm_kernel_lapic_save_to_user(APICState *s)
+{
+ struct kvm_lapic_state apic;
+ struct kvm_lapic_state *kapic = &apic;
+ int i, v;
+
+ kvm_get_lapic(s->cpu_env, kapic);
+
+ s->id = kapic_reg(kapic, 0x2) >> 24;
+ s->tpr = kapic_reg(kapic, 0x8);
+ s->arb_id = kapic_reg(kapic, 0x9);
+ s->log_dest = kapic_reg(kapic, 0xd) >> 24;
+ s->dest_mode = kapic_reg(kapic, 0xe) >> 28;
+ s->spurious_vec = kapic_reg(kapic, 0xf);
+ for (i = 0; i < 8; i++) {
+ s->isr[i] = kapic_reg(kapic, 0x10 + i);
+ s->tmr[i] = kapic_reg(kapic, 0x18 + i);
+ s->irr[i] = kapic_reg(kapic, 0x20 + i);
+ }
+ s->esr = kapic_reg(kapic, 0x28);
+ s->icr[0] = kapic_reg(kapic, 0x30);
+ s->icr[1] = kapic_reg(kapic, 0x31);
+ for (i = 0; i < APIC_LVT_NB; i++)
+ s->lvt[i] = kapic_reg(kapic, 0x32 + i);
+ s->initial_count = kapic_reg(kapic, 0x38);
+ s->divide_conf = kapic_reg(kapic, 0x3e);
+
+ v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
+ s->count_shift = (v + 1) & 7;
+
+ s->initial_count_load_time = qemu_get_clock(vm_clock);
+ apic_timer_update(s, s->initial_count_load_time);
+}
+
+static void kvm_kernel_lapic_load_from_user(APICState *s)
+{
+ struct kvm_lapic_state apic;
+ struct kvm_lapic_state *klapic = &apic;
+ int i;
+
+ memset(klapic, 0, sizeof apic);
+ kapic_set_reg(klapic, 0x2, s->id << 24);
+ kapic_set_reg(klapic, 0x8, s->tpr);
+ kapic_set_reg(klapic, 0xd, s->log_dest << 24);
+ kapic_set_reg(klapic, 0xe, s->dest_mode << 28 | 0x0fffffff);
+ kapic_set_reg(klapic, 0xf, s->spurious_vec);
+ for (i = 0; i < 8; i++) {
+ kapic_set_reg(klapic, 0x10 + i, s->isr[i]);
+ kapic_set_reg(klapic, 0x18 + i, s->tmr[i]);
+ kapic_set_reg(klapic, 0x20 + i, s->irr[i]);
+ }
+ kapic_set_reg(klapic, 0x28, s->esr);
+ kapic_set_reg(klapic, 0x30, s->icr[0]);
+ kapic_set_reg(klapic, 0x31, s->icr[1]);
+ for (i = 0; i < APIC_LVT_NB; i++)
+ kapic_set_reg(klapic, 0x32 + i, s->lvt[i]);
+ kapic_set_reg(klapic, 0x38, s->initial_count);
+ kapic_set_reg(klapic, 0x3e, s->divide_conf);
+
+ kvm_set_lapic(s->cpu_env, klapic);
+}
+
+#endif
+
+void kvm_load_lapic(CPUState *env)
+{
+#ifdef KVM_CAP_IRQCHIP
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, env->apic_state);
+
+ if (!s) {
+ return;
+ }
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_lapic_load_from_user(s);
+ }
+#endif
+}
+
+void kvm_save_lapic(CPUState *env)
+{
+#ifdef KVM_CAP_IRQCHIP
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, env->apic_state);
+
+ if (!s) {
+ return;
+ }
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_lapic_save_to_user(s);
+ }
+#endif
+}
+
+/* This function is only used for old state version 1 and 2 */
+static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
+{
+ APICState *s = opaque;
+ int i;
+
+ if (version_id > 2)
+ return -EINVAL;
+
+ /* XXX: what if the base changes? (registered memory regions) */
+ qemu_get_be32s(f, &s->apicbase);
+ qemu_get_8s(f, &s->id);
+ qemu_get_8s(f, &s->arb_id);
+ qemu_get_8s(f, &s->tpr);
+ qemu_get_be32s(f, &s->spurious_vec);
+ qemu_get_8s(f, &s->log_dest);
+ qemu_get_8s(f, &s->dest_mode);
+ for (i = 0; i < 8; i++) {
+ qemu_get_be32s(f, &s->isr[i]);
+ qemu_get_be32s(f, &s->tmr[i]);
+ qemu_get_be32s(f, &s->irr[i]);
+ }
+ for (i = 0; i < APIC_LVT_NB; i++) {
+ qemu_get_be32s(f, &s->lvt[i]);
+ }
+ qemu_get_be32s(f, &s->esr);
+ qemu_get_be32s(f, &s->icr[0]);
+ qemu_get_be32s(f, &s->icr[1]);
+ qemu_get_be32s(f, &s->divide_conf);
+ s->count_shift=qemu_get_be32(f);
+ qemu_get_be32s(f, &s->initial_count);
+ s->initial_count_load_time=qemu_get_be64(f);
+ s->next_time=qemu_get_be64(f);
+
+ if (version_id >= 2)
+ qemu_get_timer(f, s->timer);
+ return 0;
+}
+
+static const VMStateDescription vmstate_apic = {
+ .name = "apic",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 1,
+ .load_state_old = apic_load_old,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(apicbase, APICState),
+ VMSTATE_UINT8(id, APICState),
+ VMSTATE_UINT8(arb_id, APICState),
+ VMSTATE_UINT8(tpr, APICState),
+ VMSTATE_UINT32(spurious_vec, APICState),
+ VMSTATE_UINT8(log_dest, APICState),
+ VMSTATE_UINT8(dest_mode, APICState),
+ VMSTATE_UINT32_ARRAY(isr, APICState, 8),
+ VMSTATE_UINT32_ARRAY(tmr, APICState, 8),
+ VMSTATE_UINT32_ARRAY(irr, APICState, 8),
+ VMSTATE_UINT32_ARRAY(lvt, APICState, APIC_LVT_NB),
+ VMSTATE_UINT32(esr, APICState),
+ VMSTATE_UINT32_ARRAY(icr, APICState, 2),
+ VMSTATE_UINT32(divide_conf, APICState),
+ VMSTATE_INT32(count_shift, APICState),
+ VMSTATE_UINT32(initial_count, APICState),
+ VMSTATE_INT64(initial_count_load_time, APICState),
+ VMSTATE_INT64(next_time, APICState),
+ VMSTATE_TIMER(timer, APICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void apic_reset(DeviceState *d)
+{
+ APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
+ int bsp;
+
+ bsp = cpu_is_bsp(s->cpu_env);
+ s->apicbase = 0xfee00000 |
+ (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
+
+ apic_init_reset(d);
+
+ if (bsp) {
+ /*
+ * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization
+ * time typically by BIOS, so PIC interrupt can be delivered to the
+ * processor when local APIC is enabled.
+ */
+ s->lvt[APIC_LVT_LINT0] = 0x700;
+ }
+}
+
+static CPUReadMemoryFunc * const apic_mem_read[3] = {
+ apic_mem_readb,
+ apic_mem_readw,
+ apic_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const apic_mem_write[3] = {
+ apic_mem_writeb,
+ apic_mem_writew,
+ apic_mem_writel,
+};
+
+static int apic_init1(SysBusDevice *dev)
+{
+ APICState *s = FROM_SYSBUS(APICState, dev);
+ int apic_io_memory;
+ static int last_apic_idx;
+
+ if (last_apic_idx >= MAX_APICS) {
+ return -1;
+ }
+ apic_io_memory = cpu_register_io_memory(apic_mem_read,
+ apic_mem_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MSI_ADDR_SIZE, apic_io_memory);
+
+ s->timer = qemu_new_timer(vm_clock, apic_timer, s);
+ s->idx = last_apic_idx++;
+ local_apics[s->idx] = s;
+ return 0;
+}
+
+static SysBusDeviceInfo apic_info = {
+ .init = apic_init1,
+ .qdev.name = "apic",
+ .qdev.size = sizeof(APICState),
+ .qdev.vmsd = &vmstate_apic,
+ .qdev.reset = apic_reset,
+ .qdev.no_user = 1,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("id", APICState, id, -1),
+ DEFINE_PROP_PTR("cpu_env", APICState, cpu_env),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void apic_register_devices(void)
+{
+ sysbus_register_withprop(&apic_info);
+}
+
+device_init(apic_register_devices)
diff --git a/hw/apic.h b/hw/apic.h
new file mode 100644
index 0000000..c857d52
--- /dev/null
+++ b/hw/apic.h
@@ -0,0 +1,28 @@
+#ifndef APIC_H
+#define APIC_H
+
+#include "qemu-common.h"
+
+/* apic.c */
+void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
+ uint8_t delivery_mode,
+ uint8_t vector_num, uint8_t polarity,
+ uint8_t trigger_mode);
+int apic_accept_pic_intr(DeviceState *s);
+void apic_deliver_pic_intr(DeviceState *s, int level);
+int apic_get_interrupt(DeviceState *s);
+void apic_reset_irq_delivered(void);
+int apic_get_irq_delivered(void);
+void apic_set_irq_delivered(void);
+void cpu_set_apic_base(DeviceState *s, uint64_t val);
+uint64_t cpu_get_apic_base(DeviceState *s);
+void cpu_set_apic_tpr(DeviceState *s, uint8_t val);
+uint8_t cpu_get_apic_tpr(DeviceState *s);
+void apic_init_reset(DeviceState *s);
+void apic_sipi(DeviceState *s);
+
+/* pc.c */
+int cpu_is_bsp(CPUState *env);
+DeviceState *cpu_get_current_apic(void);
+
+#endif
diff --git a/hw/apm.c b/hw/apm.c
new file mode 100644
index 0000000..cdda72f
--- /dev/null
+++ b/hw/apm.c
@@ -0,0 +1,86 @@
+/*
+ * QEMU PC APM controller Emulation
+ * This is split out from acpi.c
+ *
+ * Copyright (c) 2006 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 version 2 as published by the Free Software Foundation.
+ *
+ * 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 "apm.h"
+#include "hw.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+# define APM_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
+#else
+# define APM_DPRINTF(format, ...) do { } while (0)
+#endif
+
+/* fixed I/O location */
+#define APM_CNT_IOPORT 0xb2
+#define APM_STS_IOPORT 0xb3
+
+static void apm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ APMState *apm = opaque;
+ addr &= 1;
+ APM_DPRINTF("apm_ioport_writeb addr=0x%x val=0x%02x\n", addr, val);
+ if (addr == 0) {
+ apm->apmc = val;
+
+ if (apm->callback) {
+ (apm->callback)(val, apm->arg);
+ }
+ } else {
+ apm->apms = val;
+ }
+}
+
+static uint32_t apm_ioport_readb(void *opaque, uint32_t addr)
+{
+ APMState *apm = opaque;
+ uint32_t val;
+
+ addr &= 1;
+ if (addr == 0) {
+ val = apm->apmc;
+ } else {
+ val = apm->apms;
+ }
+ APM_DPRINTF("apm_ioport_readb addr=0x%x val=0x%02x\n", addr, val);
+ return val;
+}
+
+const VMStateDescription vmstate_apm = {
+ .name = "APM State",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(apmc, APMState),
+ VMSTATE_UINT8(apms, APMState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void apm_init(APMState *apm, apm_ctrl_changed_t callback, void *arg)
+{
+ apm->callback = callback;
+ apm->arg = arg;
+
+ /* ioport 0xb2, 0xb3 */
+ register_ioport_write(APM_CNT_IOPORT, 2, 1, apm_ioport_writeb, apm);
+ register_ioport_read(APM_CNT_IOPORT, 2, 1, apm_ioport_readb, apm);
+}
diff --git a/hw/apm.h b/hw/apm.h
new file mode 100644
index 0000000..f7c741e
--- /dev/null
+++ b/hw/apm.h
@@ -0,0 +1,22 @@
+#ifndef APM_H
+#define APM_H
+
+#include <stdint.h>
+#include "qemu-common.h"
+#include "hw.h"
+
+typedef void (*apm_ctrl_changed_t)(uint32_t val, void *arg);
+
+typedef struct APMState {
+ uint8_t apmc;
+ uint8_t apms;
+
+ apm_ctrl_changed_t callback;
+ void *arg;
+} APMState;
+
+void apm_init(APMState *s, apm_ctrl_changed_t callback, void *arg);
+
+extern const VMStateDescription vmstate_apm;
+
+#endif /* APM_H */
diff --git a/hw/applesmc.c b/hw/applesmc.c
new file mode 100644
index 0000000..23ed328
--- /dev/null
+++ b/hw/applesmc.c
@@ -0,0 +1,241 @@
+/*
+ * Apple SMC controller
+ *
+ * Copyright (c) 2007 Alexander Graf
+ *
+ * Authors: Alexander Graf <agraf@suse.de>
+ * Susanne Graf <suse@csgraf.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * *****************************************************************
+ *
+ * In all Intel-based Apple hardware there is an SMC chip to control the
+ * backlight, fans and several other generic device parameters. It also
+ * contains the magic keys used to dongle Mac OS X to the device.
+ *
+ * This driver was mostly created by looking at the Linux AppleSMC driver
+ * implementation and does not support IRQ.
+ *
+ */
+
+#include "hw.h"
+#include "isa.h"
+#include "console.h"
+#include "qemu-timer.h"
+
+/* #define DEBUG_SMC */
+
+#define APPLESMC_DEFAULT_IOBASE 0x300
+/* data port used by Apple SMC */
+#define APPLESMC_DATA_PORT 0x0
+/* command/status port used by Apple SMC */
+#define APPLESMC_CMD_PORT 0x4
+#define APPLESMC_NR_PORTS 32
+#define APPLESMC_MAX_DATA_LENGTH 32
+
+#define APPLESMC_READ_CMD 0x10
+#define APPLESMC_WRITE_CMD 0x11
+#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12
+#define APPLESMC_GET_KEY_TYPE_CMD 0x13
+
+#ifdef DEBUG_SMC
+#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__)
+#else
+#define smc_debug(...) do { } while(0)
+#endif
+
+static char default_osk[64] = "This is a dummy key. Enter the real key "
+ "using the -osk parameter";
+
+struct AppleSMCData {
+ uint8_t len;
+ const char *key;
+ const char *data;
+ QLIST_ENTRY(AppleSMCData) node;
+};
+
+struct AppleSMCStatus {
+ ISADevice dev;
+ uint32_t iobase;
+ uint8_t cmd;
+ uint8_t status;
+ uint8_t key[4];
+ uint8_t read_pos;
+ uint8_t data_len;
+ uint8_t data_pos;
+ uint8_t data[255];
+ uint8_t charactic[4];
+ char *osk;
+ QLIST_HEAD(, AppleSMCData) data_def;
+};
+
+static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ struct AppleSMCStatus *s = opaque;
+
+ smc_debug("CMD Write B: %#x = %#x\n", addr, val);
+ switch(val) {
+ case APPLESMC_READ_CMD:
+ s->status = 0x0c;
+ break;
+ }
+ s->cmd = val;
+ s->read_pos = 0;
+ s->data_pos = 0;
+}
+
+static void applesmc_fill_data(struct AppleSMCStatus *s)
+{
+ struct AppleSMCData *d;
+
+ QLIST_FOREACH(d, &s->data_def, node) {
+ if (!memcmp(d->key, s->key, 4)) {
+ smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key,
+ d->len, d->data);
+ memcpy(s->data, d->data, d->len);
+ return;
+ }
+ }
+}
+
+static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ struct AppleSMCStatus *s = opaque;
+
+ smc_debug("DATA Write B: %#x = %#x\n", addr, val);
+ switch(s->cmd) {
+ case APPLESMC_READ_CMD:
+ if(s->read_pos < 4) {
+ s->key[s->read_pos] = val;
+ s->status = 0x04;
+ } else if(s->read_pos == 4) {
+ s->data_len = val;
+ s->status = 0x05;
+ s->data_pos = 0;
+ smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0],
+ s->key[1], s->key[2], s->key[3], val);
+ applesmc_fill_data(s);
+ }
+ s->read_pos++;
+ break;
+ }
+}
+
+static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1)
+{
+ struct AppleSMCStatus *s = opaque;
+ uint8_t retval = 0;
+
+ switch(s->cmd) {
+ case APPLESMC_READ_CMD:
+ if(s->data_pos < s->data_len) {
+ retval = s->data[s->data_pos];
+ smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos,
+ retval);
+ s->data_pos++;
+ if(s->data_pos == s->data_len) {
+ s->status = 0x00;
+ smc_debug("EOF\n");
+ } else
+ s->status = 0x05;
+ }
+ }
+ smc_debug("DATA Read b: %#x = %#x\n", addr1, retval);
+
+ return retval;
+}
+
+static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1)
+{
+ struct AppleSMCStatus *s = opaque;
+
+ smc_debug("CMD Read B: %#x\n", addr1);
+ return s->status;
+}
+
+static void applesmc_add_key(struct AppleSMCStatus *s, const char *key,
+ int len, const char *data)
+{
+ struct AppleSMCData *def;
+
+ def = qemu_mallocz(sizeof(struct AppleSMCData));
+ def->key = key;
+ def->len = len;
+ def->data = data;
+
+ QLIST_INSERT_HEAD(&s->data_def, def, node);
+}
+
+static void qdev_applesmc_isa_reset(DeviceState *dev)
+{
+ struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev);
+ struct AppleSMCData *d, *next;
+
+ /* Remove existing entries */
+ QLIST_FOREACH_SAFE(d, &s->data_def, node, next) {
+ QLIST_REMOVE(d, node);
+ }
+
+ applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03");
+ applesmc_add_key(s, "OSK0", 32, s->osk);
+ applesmc_add_key(s, "OSK1", 32, s->osk + 32);
+ applesmc_add_key(s, "NATJ", 1, "\0");
+ applesmc_add_key(s, "MSSP", 1, "\0");
+ applesmc_add_key(s, "MSSD", 1, "\0x3");
+}
+
+static int applesmc_isa_init(ISADevice *dev)
+{
+ struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev);
+
+ register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1,
+ applesmc_io_data_readb, s);
+ register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1,
+ applesmc_io_cmd_readb, s);
+ register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1,
+ applesmc_io_data_writeb, s);
+ register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1,
+ applesmc_io_cmd_writeb, s);
+
+ if (!s->osk || (strlen(s->osk) != 64)) {
+ fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n");
+ s->osk = default_osk;
+ }
+
+ QLIST_INIT(&s->data_def);
+ qdev_applesmc_isa_reset(&dev->qdev);
+
+ return 0;
+}
+
+static ISADeviceInfo applesmc_isa_info = {
+ .qdev.name = "isa-applesmc",
+ .qdev.size = sizeof(struct AppleSMCStatus),
+ .qdev.reset = qdev_applesmc_isa_reset,
+ .init = applesmc_isa_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase,
+ APPLESMC_DEFAULT_IOBASE),
+ DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void applesmc_register_devices(void)
+{
+ isa_qdev_register(&applesmc_isa_info);
+}
+
+device_init(applesmc_register_devices)
diff --git a/hw/arm-misc.h b/hw/arm-misc.h
new file mode 100644
index 0000000..010acb4
--- /dev/null
+++ b/hw/arm-misc.h
@@ -0,0 +1,46 @@
+/*
+ * Misc ARM declarations
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ */
+
+#ifndef ARM_MISC_H
+#define ARM_MISC_H 1
+
+/* The CPU is also modeled as an interrupt controller. */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+qemu_irq *arm_pic_init_cpu(CPUState *env);
+
+/* armv7m.c */
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+ const char *kernel_filename, const char *cpu_model);
+
+/* arm_boot.c */
+struct arm_boot_info {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+ target_phys_addr_t loader_start;
+ target_phys_addr_t smp_loader_start;
+ target_phys_addr_t smp_priv_base;
+ int nb_cpus;
+ int board_id;
+ int (*atag_board)(struct arm_boot_info *info, void *p);
+ /* Used internally by arm_boot.c */
+ int is_linux;
+ target_phys_addr_t initrd_size;
+ target_phys_addr_t entry;
+};
+void arm_load_kernel(CPUState *env, struct arm_boot_info *info);
+
+/* Multiplication factor to convert from system clock ticks to qemu timer
+ ticks. */
+extern int system_clock_scale;
+
+#endif /* !ARM_MISC_H */
diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c
new file mode 100644
index 0000000..3bbd885
--- /dev/null
+++ b/hw/arm11mpcore.c
@@ -0,0 +1,112 @@
+/*
+ * ARM11MPCore internal peripheral emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
+ (+ 32 internal). However my test chip only exposes/reports 32.
+ More importantly Linux falls over if more than 32 are present! */
+#define GIC_NIRQ 64
+#include "mpcore.c"
+
+/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ
+ controllers. The output of these, plus some of the raw input lines
+ are fed into a single SMP-aware interrupt controller on the CPU. */
+typedef struct {
+ SysBusDevice busdev;
+ SysBusDevice *priv;
+ qemu_irq cpuic[32];
+ qemu_irq rvic[4][64];
+ uint32_t num_cpu;
+} mpcore_rirq_state;
+
+/* Map baseboard IRQs onto CPU IRQ lines. */
+static const int mpcore_irq_map[32] = {
+ -1, -1, -1, -1, 1, 2, -1, -1,
+ -1, -1, 6, -1, 4, 5, -1, -1,
+ -1, 14, 15, 0, 7, 8, -1, -1,
+ -1, -1, -1, -1, 9, 3, -1, -1,
+};
+
+static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
+{
+ mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ qemu_set_irq(s->rvic[i][irq], level);
+ }
+ if (irq < 32) {
+ irq = mpcore_irq_map[irq];
+ if (irq >= 0) {
+ qemu_set_irq(s->cpuic[irq], level);
+ }
+ }
+}
+
+static void mpcore_rirq_map(SysBusDevice *dev, target_phys_addr_t base)
+{
+ mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev);
+ sysbus_mmio_map(s->priv, 0, base);
+}
+
+static int realview_mpcore_init(SysBusDevice *dev)
+{
+ mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev);
+ DeviceState *gic;
+ DeviceState *priv;
+ int n;
+ int i;
+
+ priv = qdev_create(NULL, "arm11mpcore_priv");
+ qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu);
+ qdev_init_nofail(priv);
+ s->priv = sysbus_from_qdev(priv);
+ sysbus_pass_irq(dev, s->priv);
+ for (i = 0; i < 32; i++) {
+ s->cpuic[i] = qdev_get_gpio_in(priv, i);
+ }
+ /* ??? IRQ routing is hardcoded to "normal" mode. */
+ for (n = 0; n < 4; n++) {
+ gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000,
+ s->cpuic[10 + n]);
+ for (i = 0; i < 64; i++) {
+ s->rvic[n][i] = qdev_get_gpio_in(gic, i);
+ }
+ }
+ qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64);
+ sysbus_init_mmio_cb(dev, 0x2000, mpcore_rirq_map);
+ return 0;
+}
+
+static SysBusDeviceInfo mpcore_rirq_info = {
+ .init = realview_mpcore_init,
+ .qdev.name = "realview_mpcore",
+ .qdev.size = sizeof(mpcore_rirq_state),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static SysBusDeviceInfo mpcore_priv_info = {
+ .init = mpcore_priv_init,
+ .qdev.name = "arm11mpcore_priv",
+ .qdev.size = sizeof(mpcore_priv_state),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void arm11mpcore_register_devices(void)
+{
+ sysbus_register_withprop(&mpcore_rirq_info);
+ sysbus_register_withprop(&mpcore_priv_info);
+}
+
+device_init(arm11mpcore_register_devices)
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
new file mode 100644
index 0000000..620550b
--- /dev/null
+++ b/hw/arm_boot.c
@@ -0,0 +1,283 @@
+/*
+ * ARM kernel loader.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+#include "loader.h"
+#include "elf.h"
+
+#define KERNEL_ARGS_ADDR 0x100
+#define KERNEL_LOAD_ADDR 0x00010000
+#define INITRD_LOAD_ADDR 0x00800000
+
+/* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */
+static uint32_t bootloader[] = {
+ 0xe3a00000, /* mov r0, #0 */
+ 0xe3a01000, /* mov r1, #0x?? */
+ 0xe3811c00, /* orr r1, r1, #0x??00 */
+ 0xe59f2000, /* ldr r2, [pc, #0] */
+ 0xe59ff000, /* ldr pc, [pc, #0] */
+ 0, /* Address of kernel args. Set by integratorcp_init. */
+ 0 /* Kernel entry point. Set by integratorcp_init. */
+};
+
+/* Entry point for secondary CPUs. Enable interrupt controller and
+ Issue WFI until start address is written to system controller. */
+static uint32_t smpboot[] = {
+ 0xe59f0020, /* ldr r0, privbase */
+ 0xe3a01001, /* mov r1, #1 */
+ 0xe5801100, /* str r1, [r0, #0x100] */
+ 0xe3a00201, /* mov r0, #0x10000000 */
+ 0xe3800030, /* orr r0, #0x30 */
+ 0xe320f003, /* wfi */
+ 0xe5901000, /* ldr r1, [r0] */
+ 0xe1110001, /* tst r1, r1 */
+ 0x0afffffb, /* beq <wfi> */
+ 0xe12fff11, /* bx r1 */
+ 0 /* privbase: Private memory region base address. */
+};
+
+#define WRITE_WORD(p, value) do { \
+ stl_phys_notdirty(p, value); \
+ p += 4; \
+} while (0)
+
+static void set_kernel_args(struct arm_boot_info *info,
+ int initrd_size, target_phys_addr_t base)
+{
+ target_phys_addr_t p;
+
+ p = base + KERNEL_ARGS_ADDR;
+ /* ATAG_CORE */
+ WRITE_WORD(p, 5);
+ WRITE_WORD(p, 0x54410001);
+ WRITE_WORD(p, 1);
+ WRITE_WORD(p, 0x1000);
+ WRITE_WORD(p, 0);
+ /* ATAG_MEM */
+ /* TODO: handle multiple chips on one ATAG list */
+ WRITE_WORD(p, 4);
+ WRITE_WORD(p, 0x54410002);
+ WRITE_WORD(p, info->ram_size);
+ WRITE_WORD(p, info->loader_start);
+ if (initrd_size) {
+ /* ATAG_INITRD2 */
+ WRITE_WORD(p, 4);
+ WRITE_WORD(p, 0x54420005);
+ WRITE_WORD(p, info->loader_start + INITRD_LOAD_ADDR);
+ WRITE_WORD(p, initrd_size);
+ }
+ if (info->kernel_cmdline && *info->kernel_cmdline) {
+ /* ATAG_CMDLINE */
+ int cmdline_size;
+
+ cmdline_size = strlen(info->kernel_cmdline);
+ cpu_physical_memory_write(p + 8, (void *)info->kernel_cmdline,
+ cmdline_size + 1);
+ cmdline_size = (cmdline_size >> 2) + 1;
+ WRITE_WORD(p, cmdline_size + 2);
+ WRITE_WORD(p, 0x54410009);
+ p += cmdline_size * 4;
+ }
+ if (info->atag_board) {
+ /* ATAG_BOARD */
+ int atag_board_len;
+ uint8_t atag_board_buf[0x1000];
+
+ atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
+ WRITE_WORD(p, (atag_board_len + 8) >> 2);
+ WRITE_WORD(p, 0x414f4d50);
+ cpu_physical_memory_write(p, atag_board_buf, atag_board_len);
+ p += atag_board_len;
+ }
+ /* ATAG_END */
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+}
+
+static void set_kernel_args_old(struct arm_boot_info *info,
+ int initrd_size, target_phys_addr_t base)
+{
+ target_phys_addr_t p;
+ const char *s;
+
+
+ /* see linux/include/asm-arm/setup.h */
+ p = base + KERNEL_ARGS_ADDR;
+ /* page_size */
+ WRITE_WORD(p, 4096);
+ /* nr_pages */
+ WRITE_WORD(p, info->ram_size / 4096);
+ /* ramdisk_size */
+ WRITE_WORD(p, 0);
+#define FLAG_READONLY 1
+#define FLAG_RDLOAD 4
+#define FLAG_RDPROMPT 8
+ /* flags */
+ WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
+ /* rootdev */
+ WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
+ /* video_num_cols */
+ WRITE_WORD(p, 0);
+ /* video_num_rows */
+ WRITE_WORD(p, 0);
+ /* video_x */
+ WRITE_WORD(p, 0);
+ /* video_y */
+ WRITE_WORD(p, 0);
+ /* memc_control_reg */
+ WRITE_WORD(p, 0);
+ /* unsigned char sounddefault */
+ /* unsigned char adfsdrives */
+ /* unsigned char bytes_per_char_h */
+ /* unsigned char bytes_per_char_v */
+ WRITE_WORD(p, 0);
+ /* pages_in_bank[4] */
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+ WRITE_WORD(p, 0);
+ /* pages_in_vram */
+ WRITE_WORD(p, 0);
+ /* initrd_start */
+ if (initrd_size)
+ WRITE_WORD(p, info->loader_start + INITRD_LOAD_ADDR);
+ else
+ WRITE_WORD(p, 0);
+ /* initrd_size */
+ WRITE_WORD(p, initrd_size);
+ /* rd_start */
+ WRITE_WORD(p, 0);
+ /* system_rev */
+ WRITE_WORD(p, 0);
+ /* system_serial_low */
+ WRITE_WORD(p, 0);
+ /* system_serial_high */
+ WRITE_WORD(p, 0);
+ /* mem_fclk_21285 */
+ WRITE_WORD(p, 0);
+ /* zero unused fields */
+ while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) {
+ WRITE_WORD(p, 0);
+ }
+ s = info->kernel_cmdline;
+ if (s) {
+ cpu_physical_memory_write(p, (void *)s, strlen(s) + 1);
+ } else {
+ WRITE_WORD(p, 0);
+ }
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ struct arm_boot_info *info = env->boot_info;
+
+ cpu_reset(env);
+ if (info) {
+ if (!info->is_linux) {
+ /* Jump to the entry point. */
+ env->regs[15] = info->entry & 0xfffffffe;
+ env->thumb = info->entry & 1;
+ } else {
+ env->regs[15] = info->loader_start;
+ if (old_param) {
+ set_kernel_args_old(info, info->initrd_size,
+ info->loader_start);
+ } else {
+ set_kernel_args(info, info->initrd_size, info->loader_start);
+ }
+ }
+ }
+ /* TODO: Reset secondary CPUs. */
+}
+
+void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
+{
+ int kernel_size;
+ int initrd_size;
+ int n;
+ int is_linux = 0;
+ uint64_t elf_entry;
+ target_phys_addr_t entry;
+ int big_endian;
+
+ /* Load the kernel. */
+ if (!info->kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ if (info->nb_cpus == 0)
+ info->nb_cpus = 1;
+ env->boot_info = info;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ /* Assume that raw images are linux kernels, and ELF images are not. */
+ kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
+ NULL, NULL, big_endian, ELF_MACHINE, 1);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
+ &is_linux);
+ }
+ if (kernel_size < 0) {
+ entry = info->loader_start + KERNEL_LOAD_ADDR;
+ kernel_size = load_image_targphys(info->kernel_filename, entry,
+ ram_size - KERNEL_LOAD_ADDR);
+ is_linux = 1;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ info->kernel_filename);
+ exit(1);
+ }
+ info->entry = entry;
+ if (is_linux) {
+ if (info->initrd_filename) {
+ initrd_size = load_image_targphys(info->initrd_filename,
+ info->loader_start
+ + INITRD_LOAD_ADDR,
+ ram_size - INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initrd '%s'\n",
+ info->initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_size = 0;
+ }
+ bootloader[1] |= info->board_id & 0xff;
+ bootloader[2] |= (info->board_id >> 8) & 0xff;
+ bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+ bootloader[6] = entry;
+ for (n = 0; n < sizeof(bootloader) / 4; n++) {
+ bootloader[n] = tswap32(bootloader[n]);
+ }
+ rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
+ info->loader_start);
+ if (info->nb_cpus > 1) {
+ smpboot[10] = info->smp_priv_base;
+ for (n = 0; n < sizeof(smpboot) / 4; n++) {
+ smpboot[n] = tswap32(smpboot[n]);
+ }
+ rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
+ info->smp_loader_start);
+ }
+ info->initrd_size = initrd_size;
+ }
+ info->is_linux = is_linux;
+ qemu_register_reset(main_cpu_reset, env);
+}
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
new file mode 100644
index 0000000..e6b1953
--- /dev/null
+++ b/hw/arm_gic.c
@@ -0,0 +1,749 @@
+/*
+ * ARM Generic/Distributed Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* This file contains implementation code for the RealView EB interrupt
+ controller, MPCore distributed interrupt controller and ARMv7-M
+ Nested Vectored Interrupt Controller. */
+
+//#define DEBUG_GIC
+
+#ifdef DEBUG_GIC
+#define DPRINTF(fmt, ...) \
+do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#ifdef NVIC
+static const uint8_t gic_id[] =
+{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
+/* The NVIC has 16 internal vectors. However these are not exposed
+ through the normal GIC interface. */
+#define GIC_BASE_IRQ 32
+#else
+static const uint8_t gic_id[] =
+{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#define GIC_BASE_IRQ 0
+#endif
+
+#define FROM_SYSBUSGIC(type, dev) \
+ DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev))
+
+typedef struct gic_irq_state
+{
+ /* ??? The documentation seems to imply the enable bits are global, even
+ for per-cpu interrupts. This seems strange. */
+ unsigned enabled:1;
+ unsigned pending:NCPU;
+ unsigned active:NCPU;
+ unsigned level:NCPU;
+ unsigned model:1; /* 0 = N:N, 1 = 1:N */
+ unsigned trigger:1; /* nonzero = edge triggered. */
+} gic_irq_state;
+
+#define ALL_CPU_MASK ((1 << NCPU) - 1)
+#if NCPU > 1
+#define NUM_CPU(s) ((s)->num_cpu)
+#else
+#define NUM_CPU(s) 1
+#endif
+
+#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
+#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
+#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
+#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
+#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
+#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
+#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
+#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
+#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
+#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
+#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
+#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
+#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
+#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
+#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
+#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
+#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
+#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_GET_PRIORITY(irq, cpu) \
+ (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
+#ifdef NVIC
+#define GIC_TARGET(irq) 1
+#else
+#define GIC_TARGET(irq) s->irq_target[irq]
+#endif
+
+typedef struct gic_state
+{
+ SysBusDevice busdev;
+ qemu_irq parent_irq[NCPU];
+ int enabled;
+ int cpu_enabled[NCPU];
+
+ gic_irq_state irq_state[GIC_NIRQ];
+#ifndef NVIC
+ int irq_target[GIC_NIRQ];
+#endif
+ int priority1[32][NCPU];
+ int priority2[GIC_NIRQ - 32];
+ int last_active[GIC_NIRQ][NCPU];
+
+ int priority_mask[NCPU];
+ int running_irq[NCPU];
+ int running_priority[NCPU];
+ int current_pending[NCPU];
+
+#if NCPU > 1
+ int num_cpu;
+#endif
+
+ int iomemtype;
+} gic_state;
+
+/* TODO: Many places that call this routine could be optimized. */
+/* Update interrupt status after enabled or pending bits have been changed. */
+static void gic_update(gic_state *s)
+{
+ int best_irq;
+ int best_prio;
+ int irq;
+ int level;
+ int cpu;
+ int cm;
+
+ for (cpu = 0; cpu < NUM_CPU(s); cpu++) {
+ cm = 1 << cpu;
+ s->current_pending[cpu] = 1023;
+ if (!s->enabled || !s->cpu_enabled[cpu]) {
+ qemu_irq_lower(s->parent_irq[cpu]);
+ return;
+ }
+ best_prio = 0x100;
+ best_irq = 1023;
+ for (irq = 0; irq < GIC_NIRQ; irq++) {
+ if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
+ if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
+ best_prio = GIC_GET_PRIORITY(irq, cpu);
+ best_irq = irq;
+ }
+ }
+ }
+ level = 0;
+ if (best_prio <= s->priority_mask[cpu]) {
+ s->current_pending[cpu] = best_irq;
+ if (best_prio < s->running_priority[cpu]) {
+ DPRINTF("Raised pending IRQ %d\n", best_irq);
+ level = 1;
+ }
+ }
+ qemu_set_irq(s->parent_irq[cpu], level);
+ }
+}
+
+static void __attribute__((unused))
+gic_set_pending_private(gic_state *s, int cpu, int irq)
+{
+ int cm = 1 << cpu;
+
+ if (GIC_TEST_PENDING(irq, cm))
+ return;
+
+ DPRINTF("Set %d pending cpu %d\n", irq, cpu);
+ GIC_SET_PENDING(irq, cm);
+ gic_update(s);
+}
+
+/* Process a change in an external IRQ input. */
+static void gic_set_irq(void *opaque, int irq, int level)
+{
+ gic_state *s = (gic_state *)opaque;
+ /* The first external input line is internal interrupt 32. */
+ irq += 32;
+ if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
+ return;
+
+ if (level) {
+ GIC_SET_LEVEL(irq, ALL_CPU_MASK);
+ if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
+ DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
+ GIC_SET_PENDING(irq, GIC_TARGET(irq));
+ }
+ } else {
+ GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
+ }
+ gic_update(s);
+}
+
+static void gic_set_running_irq(gic_state *s, int cpu, int irq)
+{
+ s->running_irq[cpu] = irq;
+ if (irq == 1023) {
+ s->running_priority[cpu] = 0x100;
+ } else {
+ s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+ }
+ gic_update(s);
+}
+
+static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
+{
+ int new_irq;
+ int cm = 1 << cpu;
+ new_irq = s->current_pending[cpu];
+ if (new_irq == 1023
+ || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
+ DPRINTF("ACK no pending IRQ\n");
+ return 1023;
+ }
+ s->last_active[new_irq][cpu] = s->running_irq[cpu];
+ /* Clear pending flags for both level and edge triggered interrupts.
+ Level triggered IRQs will be reasserted once they become inactive. */
+ GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
+ gic_set_running_irq(s, cpu, new_irq);
+ DPRINTF("ACK %d\n", new_irq);
+ return new_irq;
+}
+
+static void gic_complete_irq(gic_state * s, int cpu, int irq)
+{
+ int update = 0;
+ int cm = 1 << cpu;
+ DPRINTF("EOI %d\n", irq);
+ if (s->running_irq[cpu] == 1023)
+ return; /* No active IRQ. */
+ if (irq != 1023) {
+ /* Mark level triggered interrupts as pending if they are still
+ raised. */
+ if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
+ GIC_SET_PENDING(irq, cm);
+ update = 1;
+ }
+ }
+ if (irq != s->running_irq[cpu]) {
+ /* Complete an IRQ that is not currently running. */
+ int tmp = s->running_irq[cpu];
+ while (s->last_active[tmp][cpu] != 1023) {
+ if (s->last_active[tmp][cpu] == irq) {
+ s->last_active[tmp][cpu] = s->last_active[irq][cpu];
+ break;
+ }
+ tmp = s->last_active[tmp][cpu];
+ }
+ if (update) {
+ gic_update(s);
+ }
+ } else {
+ /* Complete the current running IRQ. */
+ gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
+ }
+}
+
+static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
+{
+ gic_state *s = (gic_state *)opaque;
+ uint32_t res;
+ int irq;
+ int i;
+ int cpu;
+ int cm;
+ int mask;
+
+ cpu = gic_get_current_cpu();
+ cm = 1 << cpu;
+ if (offset < 0x100) {
+#ifndef NVIC
+ if (offset == 0)
+ return s->enabled;
+ if (offset == 4)
+ return ((GIC_NIRQ / 32) - 1) | ((NUM_CPU(s) - 1) << 5);
+ if (offset < 0x08)
+ return 0;
+#endif
+ goto bad_reg;
+ } else if (offset < 0x200) {
+ /* Interrupt Set/Clear Enable. */
+ if (offset < 0x180)
+ irq = (offset - 0x100) * 8;
+ else
+ irq = (offset - 0x180) * 8;
+ irq += GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_ENABLED(irq + i)) {
+ res |= (1 << i);
+ }
+ }
+ } else if (offset < 0x300) {
+ /* Interrupt Set/Clear Pending. */
+ if (offset < 0x280)
+ irq = (offset - 0x200) * 8;
+ else
+ irq = (offset - 0x280) * 8;
+ irq += GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ mask = (irq < 32) ? cm : ALL_CPU_MASK;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_PENDING(irq + i, mask)) {
+ res |= (1 << i);
+ }
+ }
+ } else if (offset < 0x400) {
+ /* Interrupt Active. */
+ irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ mask = (irq < 32) ? cm : ALL_CPU_MASK;
+ for (i = 0; i < 8; i++) {
+ if (GIC_TEST_ACTIVE(irq + i, mask)) {
+ res |= (1 << i);
+ }
+ }
+ } else if (offset < 0x800) {
+ /* Interrupt Priority. */
+ irq = (offset - 0x400) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = GIC_GET_PRIORITY(irq, cpu);
+#ifndef NVIC
+ } else if (offset < 0xc00) {
+ /* Interrupt CPU Target. */
+ irq = (offset - 0x800) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq >= 29 && irq <= 31) {
+ res = cm;
+ } else {
+ res = GIC_TARGET(irq);
+ }
+ } else if (offset < 0xf00) {
+ /* Interrupt Configuration. */
+ irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ res = 0;
+ for (i = 0; i < 4; i++) {
+ if (GIC_TEST_MODEL(irq + i))
+ res |= (1 << (i * 2));
+ if (GIC_TEST_TRIGGER(irq + i))
+ res |= (2 << (i * 2));
+ }
+#endif
+ } else if (offset < 0xfe0) {
+ goto bad_reg;
+ } else /* offset >= 0xfe0 */ {
+ if (offset & 3) {
+ res = 0;
+ } else {
+ res = gic_id[(offset - 0xfe0) >> 2];
+ }
+ }
+ return res;
+bad_reg:
+ hw_error("gic_dist_readb: Bad offset %x\n", (int)offset);
+ return 0;
+}
+
+static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = gic_dist_readb(opaque, offset);
+ val |= gic_dist_readb(opaque, offset + 1) << 8;
+ return val;
+}
+
+static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+#ifdef NVIC
+ gic_state *s = (gic_state *)opaque;
+ uint32_t addr;
+ addr = offset;
+ if (addr < 0x100 || addr > 0xd00)
+ return nvic_readl(s, addr);
+#endif
+ val = gic_dist_readw(opaque, offset);
+ val |= gic_dist_readw(opaque, offset + 2) << 16;
+ return val;
+}
+
+static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_state *s = (gic_state *)opaque;
+ int irq;
+ int i;
+ int cpu;
+
+ cpu = gic_get_current_cpu();
+ if (offset < 0x100) {
+#ifdef NVIC
+ goto bad_reg;
+#else
+ if (offset == 0) {
+ s->enabled = (value & 1);
+ DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
+ } else if (offset < 4) {
+ /* ignored. */
+ } else {
+ goto bad_reg;
+ }
+#endif
+ } else if (offset < 0x180) {
+ /* Interrupt Set Enable. */
+ irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 16)
+ value = 0xff;
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
+ if (!GIC_TEST_ENABLED(irq + i))
+ DPRINTF("Enabled IRQ %d\n", irq + i);
+ GIC_SET_ENABLED(irq + i);
+ /* If a raised level triggered IRQ enabled then mark
+ is as pending. */
+ if (GIC_TEST_LEVEL(irq + i, mask)
+ && !GIC_TEST_TRIGGER(irq + i)) {
+ DPRINTF("Set %d pending mask %x\n", irq + i, mask);
+ GIC_SET_PENDING(irq + i, mask);
+ }
+ }
+ }
+ } else if (offset < 0x200) {
+ /* Interrupt Clear Enable. */
+ irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 16)
+ value = 0;
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ if (GIC_TEST_ENABLED(irq + i))
+ DPRINTF("Disabled IRQ %d\n", irq + i);
+ GIC_CLEAR_ENABLED(irq + i);
+ }
+ }
+ } else if (offset < 0x280) {
+ /* Interrupt Set Pending. */
+ irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 16)
+ irq = 0;
+
+ for (i = 0; i < 8; i++) {
+ if (value & (1 << i)) {
+ GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
+ }
+ }
+ } else if (offset < 0x300) {
+ /* Interrupt Clear Pending. */
+ irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ for (i = 0; i < 8; i++) {
+ /* ??? This currently clears the pending bit for all CPUs, even
+ for per-CPU interrupts. It's unclear whether this is the
+ corect behavior. */
+ if (value & (1 << i)) {
+ GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
+ }
+ }
+ } else if (offset < 0x400) {
+ /* Interrupt Active. */
+ goto bad_reg;
+ } else if (offset < 0x800) {
+ /* Interrupt Priority. */
+ irq = (offset - 0x400) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 32) {
+ s->priority1[irq][cpu] = value;
+ } else {
+ s->priority2[irq - 32] = value;
+ }
+#ifndef NVIC
+ } else if (offset < 0xc00) {
+ /* Interrupt CPU Target. */
+ irq = (offset - 0x800) + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 29)
+ value = 0;
+ else if (irq < 32)
+ value = ALL_CPU_MASK;
+ s->irq_target[irq] = value & ALL_CPU_MASK;
+ } else if (offset < 0xf00) {
+ /* Interrupt Configuration. */
+ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
+ if (irq >= GIC_NIRQ)
+ goto bad_reg;
+ if (irq < 32)
+ value |= 0xaa;
+ for (i = 0; i < 4; i++) {
+ if (value & (1 << (i * 2))) {
+ GIC_SET_MODEL(irq + i);
+ } else {
+ GIC_CLEAR_MODEL(irq + i);
+ }
+ if (value & (2 << (i * 2))) {
+ GIC_SET_TRIGGER(irq + i);
+ } else {
+ GIC_CLEAR_TRIGGER(irq + i);
+ }
+ }
+#endif
+ } else {
+ /* 0xf00 is only handled for 32-bit writes. */
+ goto bad_reg;
+ }
+ gic_update(s);
+ return;
+bad_reg:
+ hw_error("gic_dist_writeb: Bad offset %x\n", (int)offset);
+}
+
+static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_dist_writeb(opaque, offset, value & 0xff);
+ gic_dist_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_state *s = (gic_state *)opaque;
+#ifdef NVIC
+ uint32_t addr;
+ addr = offset;
+ if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
+ nvic_writel(s, addr, value);
+ return;
+ }
+#endif
+ if (offset == 0xf00) {
+ int cpu;
+ int irq;
+ int mask;
+
+ cpu = gic_get_current_cpu();
+ irq = value & 0x3ff;
+ switch ((value >> 24) & 3) {
+ case 0:
+ mask = (value >> 16) & ALL_CPU_MASK;
+ break;
+ case 1:
+ mask = 1 << cpu;
+ break;
+ case 2:
+ mask = ALL_CPU_MASK ^ (1 << cpu);
+ break;
+ default:
+ DPRINTF("Bad Soft Int target filter\n");
+ mask = ALL_CPU_MASK;
+ break;
+ }
+ GIC_SET_PENDING(irq, mask);
+ gic_update(s);
+ return;
+ }
+ gic_dist_writew(opaque, offset, value & 0xffff);
+ gic_dist_writew(opaque, offset + 2, value >> 16);
+}
+
+static CPUReadMemoryFunc * const gic_dist_readfn[] = {
+ gic_dist_readb,
+ gic_dist_readw,
+ gic_dist_readl
+};
+
+static CPUWriteMemoryFunc * const gic_dist_writefn[] = {
+ gic_dist_writeb,
+ gic_dist_writew,
+ gic_dist_writel
+};
+
+#ifndef NVIC
+static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
+{
+ switch (offset) {
+ case 0x00: /* Control */
+ return s->cpu_enabled[cpu];
+ case 0x04: /* Priority mask */
+ return s->priority_mask[cpu];
+ case 0x08: /* Binary Point */
+ /* ??? Not implemented. */
+ return 0;
+ case 0x0c: /* Acknowledge */
+ return gic_acknowledge_irq(s, cpu);
+ case 0x14: /* Runing Priority */
+ return s->running_priority[cpu];
+ case 0x18: /* Highest Pending Interrupt */
+ return s->current_pending[cpu];
+ default:
+ hw_error("gic_cpu_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
+{
+ switch (offset) {
+ case 0x00: /* Control */
+ s->cpu_enabled[cpu] = (value & 1);
+ DPRINTF("CPU %d %sabled\n", cpu, s->cpu_enabled ? "En" : "Dis");
+ break;
+ case 0x04: /* Priority mask */
+ s->priority_mask[cpu] = (value & 0xff);
+ break;
+ case 0x08: /* Binary Point */
+ /* ??? Not implemented. */
+ break;
+ case 0x10: /* End Of Interrupt */
+ return gic_complete_irq(s, cpu, value & 0x3ff);
+ default:
+ hw_error("gic_cpu_write: Bad offset %x\n", (int)offset);
+ return;
+ }
+ gic_update(s);
+}
+#endif
+
+static void gic_reset(gic_state *s)
+{
+ int i;
+ memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
+ for (i = 0 ; i < NUM_CPU(s); i++) {
+ s->priority_mask[i] = 0xf0;
+ s->current_pending[i] = 1023;
+ s->running_irq[i] = 1023;
+ s->running_priority[i] = 0x100;
+#ifdef NVIC
+ /* The NVIC doesn't have per-cpu interfaces, so enable by default. */
+ s->cpu_enabled[i] = 1;
+#else
+ s->cpu_enabled[i] = 0;
+#endif
+ }
+ for (i = 0; i < 16; i++) {
+ GIC_SET_ENABLED(i);
+ GIC_SET_TRIGGER(i);
+ }
+#ifdef NVIC
+ /* The NVIC is always enabled. */
+ s->enabled = 1;
+#else
+ s->enabled = 0;
+#endif
+}
+
+static void gic_save(QEMUFile *f, void *opaque)
+{
+ gic_state *s = (gic_state *)opaque;
+ int i;
+ int j;
+
+ qemu_put_be32(f, s->enabled);
+ for (i = 0; i < NUM_CPU(s); i++) {
+ qemu_put_be32(f, s->cpu_enabled[i]);
+#ifndef NVIC
+ qemu_put_be32(f, s->irq_target[i]);
+#endif
+ for (j = 0; j < 32; j++)
+ qemu_put_be32(f, s->priority1[j][i]);
+ for (j = 0; j < GIC_NIRQ; j++)
+ qemu_put_be32(f, s->last_active[j][i]);
+ qemu_put_be32(f, s->priority_mask[i]);
+ qemu_put_be32(f, s->running_irq[i]);
+ qemu_put_be32(f, s->running_priority[i]);
+ qemu_put_be32(f, s->current_pending[i]);
+ }
+ for (i = 0; i < GIC_NIRQ - 32; i++) {
+ qemu_put_be32(f, s->priority2[i]);
+ }
+ for (i = 0; i < GIC_NIRQ; i++) {
+ qemu_put_byte(f, s->irq_state[i].enabled);
+ qemu_put_byte(f, s->irq_state[i].pending);
+ qemu_put_byte(f, s->irq_state[i].active);
+ qemu_put_byte(f, s->irq_state[i].level);
+ qemu_put_byte(f, s->irq_state[i].model);
+ qemu_put_byte(f, s->irq_state[i].trigger);
+ }
+}
+
+static int gic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ gic_state *s = (gic_state *)opaque;
+ int i;
+ int j;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->enabled = qemu_get_be32(f);
+ for (i = 0; i < NUM_CPU(s); i++) {
+ s->cpu_enabled[i] = qemu_get_be32(f);
+#ifndef NVIC
+ s->irq_target[i] = qemu_get_be32(f);
+#endif
+ for (j = 0; j < 32; j++)
+ s->priority1[j][i] = qemu_get_be32(f);
+ for (j = 0; j < GIC_NIRQ; j++)
+ s->last_active[j][i] = qemu_get_be32(f);
+ s->priority_mask[i] = qemu_get_be32(f);
+ s->running_irq[i] = qemu_get_be32(f);
+ s->running_priority[i] = qemu_get_be32(f);
+ s->current_pending[i] = qemu_get_be32(f);
+ }
+ for (i = 0; i < GIC_NIRQ - 32; i++) {
+ s->priority2[i] = qemu_get_be32(f);
+ }
+ for (i = 0; i < GIC_NIRQ; i++) {
+ s->irq_state[i].enabled = qemu_get_byte(f);
+ s->irq_state[i].pending = qemu_get_byte(f);
+ s->irq_state[i].active = qemu_get_byte(f);
+ s->irq_state[i].level = qemu_get_byte(f);
+ s->irq_state[i].model = qemu_get_byte(f);
+ s->irq_state[i].trigger = qemu_get_byte(f);
+ }
+
+ return 0;
+}
+
+#if NCPU > 1
+static void gic_init(gic_state *s, int num_cpu)
+#else
+static void gic_init(gic_state *s)
+#endif
+{
+ int i;
+
+#if NCPU > 1
+ s->num_cpu = num_cpu;
+#endif
+ qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, GIC_NIRQ - 32);
+ for (i = 0; i < NUM_CPU(s); i++) {
+ sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
+ }
+ s->iomemtype = cpu_register_io_memory(gic_dist_readfn,
+ gic_dist_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ gic_reset(s);
+ register_savevm(NULL, "arm_gic", -1, 1, gic_save, gic_load, s);
+}
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
new file mode 100644
index 0000000..f44568c
--- /dev/null
+++ b/hw/arm_pic.c
@@ -0,0 +1,49 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "arm-misc.h"
+
+/* Stub functions for hardware that doesn't exist. */
+void pic_info(Monitor *mon)
+{
+}
+
+void irq_info(Monitor *mon)
+{
+}
+
+
+/* Input 0 is IRQ and input 1 is FIQ. */
+static void arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ CPUState *env = (CPUState *)opaque;
+ switch (irq) {
+ case ARM_PIC_CPU_IRQ:
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ break;
+ case ARM_PIC_CPU_FIQ:
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_FIQ);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_FIQ);
+ break;
+ default:
+ hw_error("arm_pic_cpu_handler: Bad interrput line %d\n", irq);
+ }
+}
+
+qemu_irq *arm_pic_init_cpu(CPUState *env)
+{
+ return qemu_allocate_irqs(arm_pic_cpu_handler, env, 2);
+}
diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c
new file mode 100644
index 0000000..d8b062c
--- /dev/null
+++ b/hw/arm_sysctl.c
@@ -0,0 +1,264 @@
+/*
+ * Status and system control registers for ARM RealView/Versatile boards.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "primecell.h"
+#include "sysemu.h"
+
+#define LOCK_VALUE 0xa05f
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t sys_id;
+ uint32_t leds;
+ uint16_t lockval;
+ uint32_t cfgdata1;
+ uint32_t cfgdata2;
+ uint32_t flags;
+ uint32_t nvflags;
+ uint32_t resetlevel;
+ uint32_t proc_id;
+} arm_sysctl_state;
+
+static const VMStateDescription vmstate_arm_sysctl = {
+ .name = "realview_sysctl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(leds, arm_sysctl_state),
+ VMSTATE_UINT16(lockval, arm_sysctl_state),
+ VMSTATE_UINT32(cfgdata1, arm_sysctl_state),
+ VMSTATE_UINT32(cfgdata2, arm_sysctl_state),
+ VMSTATE_UINT32(flags, arm_sysctl_state),
+ VMSTATE_UINT32(nvflags, arm_sysctl_state),
+ VMSTATE_UINT32(resetlevel, arm_sysctl_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void arm_sysctl_reset(DeviceState *d)
+{
+ arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sysbus_from_qdev(d));
+
+ s->leds = 0;
+ s->lockval = 0;
+ s->cfgdata1 = 0;
+ s->cfgdata2 = 0;
+ s->flags = 0;
+ s->resetlevel = 0;
+}
+
+static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset)
+{
+ arm_sysctl_state *s = (arm_sysctl_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* ID */
+ return s->sys_id;
+ case 0x04: /* SW */
+ /* General purpose hardware switches.
+ We don't have a useful way of exposing these to the user. */
+ return 0;
+ case 0x08: /* LED */
+ return s->leds;
+ case 0x20: /* LOCK */
+ return s->lockval;
+ case 0x0c: /* OSC0 */
+ case 0x10: /* OSC1 */
+ case 0x14: /* OSC2 */
+ case 0x18: /* OSC3 */
+ case 0x1c: /* OSC4 */
+ case 0x24: /* 100HZ */
+ /* ??? Implement these. */
+ return 0;
+ case 0x28: /* CFGDATA1 */
+ return s->cfgdata1;
+ case 0x2c: /* CFGDATA2 */
+ return s->cfgdata2;
+ case 0x30: /* FLAGS */
+ return s->flags;
+ case 0x38: /* NVFLAGS */
+ return s->nvflags;
+ case 0x40: /* RESETCTL */
+ return s->resetlevel;
+ case 0x44: /* PCICTL */
+ return 1;
+ case 0x48: /* MCI */
+ return 0;
+ case 0x4c: /* FLASH */
+ return 0;
+ case 0x50: /* CLCD */
+ return 0x1000;
+ case 0x54: /* CLCDSER */
+ return 0;
+ case 0x58: /* BOOTCS */
+ return 0;
+ case 0x5c: /* 24MHz */
+ return muldiv64(qemu_get_clock(vm_clock), 24000000, get_ticks_per_sec());
+ case 0x60: /* MISC */
+ return 0;
+ case 0x84: /* PROCID0 */
+ return s->proc_id;
+ case 0x88: /* PROCID1 */
+ return 0xff000000;
+ case 0x64: /* DMAPSR0 */
+ case 0x68: /* DMAPSR1 */
+ case 0x6c: /* DMAPSR2 */
+ case 0x70: /* IOSEL */
+ case 0x74: /* PLDCTL */
+ case 0x80: /* BUSID */
+ case 0x8c: /* OSCRESET0 */
+ case 0x90: /* OSCRESET1 */
+ case 0x94: /* OSCRESET2 */
+ case 0x98: /* OSCRESET3 */
+ case 0x9c: /* OSCRESET4 */
+ case 0xc0: /* SYS_TEST_OSC0 */
+ case 0xc4: /* SYS_TEST_OSC1 */
+ case 0xc8: /* SYS_TEST_OSC2 */
+ case 0xcc: /* SYS_TEST_OSC3 */
+ case 0xd0: /* SYS_TEST_OSC4 */
+ return 0;
+ default:
+ printf ("arm_sysctl_read: Bad register offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void arm_sysctl_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ arm_sysctl_state *s = (arm_sysctl_state *)opaque;
+
+ switch (offset) {
+ case 0x08: /* LED */
+ s->leds = val;
+ case 0x0c: /* OSC0 */
+ case 0x10: /* OSC1 */
+ case 0x14: /* OSC2 */
+ case 0x18: /* OSC3 */
+ case 0x1c: /* OSC4 */
+ /* ??? */
+ break;
+ case 0x20: /* LOCK */
+ if (val == LOCK_VALUE)
+ s->lockval = val;
+ else
+ s->lockval = val & 0x7fff;
+ break;
+ case 0x28: /* CFGDATA1 */
+ /* ??? Need to implement this. */
+ s->cfgdata1 = val;
+ break;
+ case 0x2c: /* CFGDATA2 */
+ /* ??? Need to implement this. */
+ s->cfgdata2 = val;
+ break;
+ case 0x30: /* FLAGSSET */
+ s->flags |= val;
+ break;
+ case 0x34: /* FLAGSCLR */
+ s->flags &= ~val;
+ break;
+ case 0x38: /* NVFLAGSSET */
+ s->nvflags |= val;
+ break;
+ case 0x3c: /* NVFLAGSCLR */
+ s->nvflags &= ~val;
+ break;
+ case 0x40: /* RESETCTL */
+ if (s->lockval == LOCK_VALUE) {
+ s->resetlevel = val;
+ if (val & 0x100)
+ qemu_system_reset_request ();
+ }
+ break;
+ case 0x44: /* PCICTL */
+ /* nothing to do. */
+ break;
+ case 0x4c: /* FLASH */
+ case 0x50: /* CLCD */
+ case 0x54: /* CLCDSER */
+ case 0x64: /* DMAPSR0 */
+ case 0x68: /* DMAPSR1 */
+ case 0x6c: /* DMAPSR2 */
+ case 0x70: /* IOSEL */
+ case 0x74: /* PLDCTL */
+ case 0x80: /* BUSID */
+ case 0x84: /* PROCID0 */
+ case 0x88: /* PROCID1 */
+ case 0x8c: /* OSCRESET0 */
+ case 0x90: /* OSCRESET1 */
+ case 0x94: /* OSCRESET2 */
+ case 0x98: /* OSCRESET3 */
+ case 0x9c: /* OSCRESET4 */
+ break;
+ default:
+ printf ("arm_sysctl_write: Bad register offset 0x%x\n", (int)offset);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const arm_sysctl_readfn[] = {
+ arm_sysctl_read,
+ arm_sysctl_read,
+ arm_sysctl_read
+};
+
+static CPUWriteMemoryFunc * const arm_sysctl_writefn[] = {
+ arm_sysctl_write,
+ arm_sysctl_write,
+ arm_sysctl_write
+};
+
+static int arm_sysctl_init1(SysBusDevice *dev)
+{
+ arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(arm_sysctl_readfn,
+ arm_sysctl_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ /* ??? Save/restore. */
+ return 0;
+}
+
+/* Legacy helper function. */
+void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "realview_sysctl");
+ qdev_prop_set_uint32(dev, "sys_id", sys_id);
+ qdev_init_nofail(dev);
+ qdev_prop_set_uint32(dev, "proc_id", proc_id);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+}
+
+static SysBusDeviceInfo arm_sysctl_info = {
+ .init = arm_sysctl_init1,
+ .qdev.name = "realview_sysctl",
+ .qdev.size = sizeof(arm_sysctl_state),
+ .qdev.vmsd = &vmstate_arm_sysctl,
+ .qdev.reset = arm_sysctl_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0),
+ DEFINE_PROP_UINT32("proc_id", arm_sysctl_state, proc_id, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void arm_sysctl_register_devices(void)
+{
+ sysbus_register_withprop(&arm_sysctl_info);
+}
+
+device_init(arm_sysctl_register_devices)
diff --git a/hw/arm_timer.c b/hw/arm_timer.c
new file mode 100644
index 0000000..82f05de
--- /dev/null
+++ b/hw/arm_timer.c
@@ -0,0 +1,357 @@
+/*
+ * ARM PrimeCell Timer modules.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+/* Common timer implementation. */
+
+#define TIMER_CTRL_ONESHOT (1 << 0)
+#define TIMER_CTRL_32BIT (1 << 1)
+#define TIMER_CTRL_DIV1 (0 << 2)
+#define TIMER_CTRL_DIV16 (1 << 2)
+#define TIMER_CTRL_DIV256 (2 << 2)
+#define TIMER_CTRL_IE (1 << 5)
+#define TIMER_CTRL_PERIODIC (1 << 6)
+#define TIMER_CTRL_ENABLE (1 << 7)
+
+typedef struct {
+ ptimer_state *timer;
+ uint32_t control;
+ uint32_t limit;
+ int freq;
+ int int_level;
+ qemu_irq irq;
+} arm_timer_state;
+
+/* Check all active timers, and schedule the next timer interrupt. */
+
+static void arm_timer_update(arm_timer_state *s)
+{
+ /* Update interrupts. */
+ if (s->int_level && (s->control & TIMER_CTRL_IE)) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* TimerLoad */
+ case 6: /* TimerBGLoad */
+ return s->limit;
+ case 1: /* TimerValue */
+ return ptimer_get_count(s->timer);
+ case 2: /* TimerControl */
+ return s->control;
+ case 4: /* TimerRIS */
+ return s->int_level;
+ case 5: /* TimerMIS */
+ if ((s->control & TIMER_CTRL_IE) == 0)
+ return 0;
+ return s->int_level;
+ default:
+ hw_error("arm_timer_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+/* Reset the timer limit after settings have changed. */
+static void arm_timer_recalibrate(arm_timer_state *s, int reload)
+{
+ uint32_t limit;
+
+ if ((s->control & (TIMER_CTRL_PERIODIC | TIMER_CTRL_ONESHOT)) == 0) {
+ /* Free running. */
+ if (s->control & TIMER_CTRL_32BIT)
+ limit = 0xffffffff;
+ else
+ limit = 0xffff;
+ } else {
+ /* Periodic. */
+ limit = s->limit;
+ }
+ ptimer_set_limit(s->timer, limit, reload);
+}
+
+static void arm_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+ int freq;
+
+ switch (offset >> 2) {
+ case 0: /* TimerLoad */
+ s->limit = value;
+ arm_timer_recalibrate(s, 1);
+ break;
+ case 1: /* TimerValue */
+ /* ??? Linux seems to want to write to this readonly register.
+ Ignore it. */
+ break;
+ case 2: /* TimerControl */
+ if (s->control & TIMER_CTRL_ENABLE) {
+ /* Pause the timer if it is running. This may cause some
+ inaccuracy dure to rounding, but avoids a whole lot of other
+ messyness. */
+ ptimer_stop(s->timer);
+ }
+ s->control = value;
+ freq = s->freq;
+ /* ??? Need to recalculate expiry time after changing divisor. */
+ switch ((value >> 2) & 3) {
+ case 1: freq >>= 4; break;
+ case 2: freq >>= 8; break;
+ }
+ arm_timer_recalibrate(s, s->control & TIMER_CTRL_ENABLE);
+ ptimer_set_freq(s->timer, freq);
+ if (s->control & TIMER_CTRL_ENABLE) {
+ /* Restart the timer if still enabled. */
+ ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
+ }
+ break;
+ case 3: /* TimerIntClr */
+ s->int_level = 0;
+ break;
+ case 6: /* TimerBGLoad */
+ s->limit = value;
+ arm_timer_recalibrate(s, 0);
+ break;
+ default:
+ hw_error("arm_timer_write: Bad offset %x\n", (int)offset);
+ }
+ arm_timer_update(s);
+}
+
+static void arm_timer_tick(void *opaque)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+ s->int_level = 1;
+ arm_timer_update(s);
+}
+
+static void arm_timer_save(QEMUFile *f, void *opaque)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+ qemu_put_be32(f, s->control);
+ qemu_put_be32(f, s->limit);
+ qemu_put_be32(f, s->int_level);
+ qemu_put_ptimer(f, s->timer);
+}
+
+static int arm_timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+ arm_timer_state *s = (arm_timer_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->control = qemu_get_be32(f);
+ s->limit = qemu_get_be32(f);
+ s->int_level = qemu_get_be32(f);
+ qemu_get_ptimer(f, s->timer);
+ return 0;
+}
+
+static arm_timer_state *arm_timer_init(uint32_t freq)
+{
+ arm_timer_state *s;
+ QEMUBH *bh;
+
+ s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
+ s->freq = freq;
+ s->control = TIMER_CTRL_IE;
+
+ bh = qemu_bh_new(arm_timer_tick, s);
+ s->timer = ptimer_init(bh);
+ register_savevm(NULL, "arm_timer", -1, 1, arm_timer_save, arm_timer_load, s);
+ return s;
+}
+
+/* ARM PrimeCell SP804 dual timer module.
+ Docs for this device don't seem to be publicly available. This
+ implementation is based on guesswork, the linux kernel sources and the
+ Integrator/CP timer modules. */
+
+typedef struct {
+ SysBusDevice busdev;
+ arm_timer_state *timer[2];
+ int level[2];
+ qemu_irq irq;
+} sp804_state;
+
+/* Merge the IRQs from the two component devices. */
+static void sp804_set_irq(void *opaque, int irq, int level)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ s->level[irq] = level;
+ qemu_set_irq(s->irq, s->level[0] || s->level[1]);
+}
+
+static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ /* ??? Don't know the PrimeCell ID for this device. */
+ if (offset < 0x20) {
+ return arm_timer_read(s->timer[0], offset);
+ } else {
+ return arm_timer_read(s->timer[1], offset - 0x20);
+ }
+}
+
+static void sp804_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ if (offset < 0x20) {
+ arm_timer_write(s->timer[0], offset, value);
+ } else {
+ arm_timer_write(s->timer[1], offset - 0x20, value);
+ }
+}
+
+static CPUReadMemoryFunc * const sp804_readfn[] = {
+ sp804_read,
+ sp804_read,
+ sp804_read
+};
+
+static CPUWriteMemoryFunc * const sp804_writefn[] = {
+ sp804_write,
+ sp804_write,
+ sp804_write
+};
+
+static void sp804_save(QEMUFile *f, void *opaque)
+{
+ sp804_state *s = (sp804_state *)opaque;
+ qemu_put_be32(f, s->level[0]);
+ qemu_put_be32(f, s->level[1]);
+}
+
+static int sp804_load(QEMUFile *f, void *opaque, int version_id)
+{
+ sp804_state *s = (sp804_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->level[0] = qemu_get_be32(f);
+ s->level[1] = qemu_get_be32(f);
+ return 0;
+}
+
+static int sp804_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ sp804_state *s = FROM_SYSBUS(sp804_state, dev);
+ qemu_irq *qi;
+
+ qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
+ sysbus_init_irq(dev, &s->irq);
+ /* ??? The timers are actually configurable between 32kHz and 1MHz, but
+ we don't implement that. */
+ s->timer[0] = arm_timer_init(1000000);
+ s->timer[1] = arm_timer_init(1000000);
+ s->timer[0]->irq = qi[0];
+ s->timer[1]->irq = qi[1];
+ iomemtype = cpu_register_io_memory(sp804_readfn,
+ sp804_writefn, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ register_savevm(&dev->qdev, "sp804", -1, 1, sp804_save, sp804_load, s);
+ return 0;
+}
+
+
+/* Integrator/CP timer module. */
+
+typedef struct {
+ SysBusDevice busdev;
+ arm_timer_state *timer[3];
+} icp_pit_state;
+
+static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset)
+{
+ icp_pit_state *s = (icp_pit_state *)opaque;
+ int n;
+
+ /* ??? Don't know the PrimeCell ID for this device. */
+ n = offset >> 8;
+ if (n > 3) {
+ hw_error("sp804_read: Bad timer %d\n", n);
+ }
+
+ return arm_timer_read(s->timer[n], offset & 0xff);
+}
+
+static void icp_pit_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ icp_pit_state *s = (icp_pit_state *)opaque;
+ int n;
+
+ n = offset >> 8;
+ if (n > 3) {
+ hw_error("sp804_write: Bad timer %d\n", n);
+ }
+
+ arm_timer_write(s->timer[n], offset & 0xff, value);
+}
+
+
+static CPUReadMemoryFunc * const icp_pit_readfn[] = {
+ icp_pit_read,
+ icp_pit_read,
+ icp_pit_read
+};
+
+static CPUWriteMemoryFunc * const icp_pit_writefn[] = {
+ icp_pit_write,
+ icp_pit_write,
+ icp_pit_write
+};
+
+static int icp_pit_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ icp_pit_state *s = FROM_SYSBUS(icp_pit_state, dev);
+
+ /* Timer 0 runs at the system clock speed (40MHz). */
+ s->timer[0] = arm_timer_init(40000000);
+ /* The other two timers run at 1MHz. */
+ s->timer[1] = arm_timer_init(1000000);
+ s->timer[2] = arm_timer_init(1000000);
+
+ sysbus_init_irq(dev, &s->timer[0]->irq);
+ sysbus_init_irq(dev, &s->timer[1]->irq);
+ sysbus_init_irq(dev, &s->timer[2]->irq);
+
+ iomemtype = cpu_register_io_memory(icp_pit_readfn,
+ icp_pit_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ /* This device has no state to save/restore. The component timers will
+ save themselves. */
+ return 0;
+}
+
+static void arm_timer_register_devices(void)
+{
+ sysbus_register_dev("integrator_pit", sizeof(icp_pit_state), icp_pit_init);
+ sysbus_register_dev("sp804", sizeof(sp804_state), sp804_init);
+}
+
+device_init(arm_timer_register_devices)
diff --git a/hw/armv7m.c b/hw/armv7m.c
new file mode 100644
index 0000000..304cd34
--- /dev/null
+++ b/hw/armv7m.c
@@ -0,0 +1,263 @@
+/*
+ * ARMV7M System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+#include "loader.h"
+#include "elf.h"
+
+/* Bitbanded IO. Each word corresponds to a single bit. */
+
+/* Get the byte address of the real memory for a bitband acess. */
+static inline uint32_t bitband_addr(void * opaque, uint32_t addr)
+{
+ uint32_t res;
+
+ res = *(uint32_t *)opaque;
+ res |= (addr & 0x1ffffff) >> 5;
+ return res;
+
+}
+
+static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
+{
+ uint8_t v;
+ cpu_physical_memory_read(bitband_addr(opaque, offset), &v, 1);
+ return (v & (1 << ((offset >> 2) & 7))) != 0;
+}
+
+static void bitband_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint8_t mask;
+ uint8_t v;
+ addr = bitband_addr(opaque, offset);
+ mask = (1 << ((offset >> 2) & 7));
+ cpu_physical_memory_read(addr, &v, 1);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, &v, 1);
+}
+
+static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(opaque, offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ return (v & mask) != 0;
+}
+
+static void bitband_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint16_t mask;
+ uint16_t v;
+ addr = bitband_addr(opaque, offset) & ~1;
+ mask = (1 << ((offset >> 2) & 15));
+ mask = tswap16(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
+}
+
+static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(opaque, offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ return (v & mask) != 0;
+}
+
+static void bitband_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ uint32_t addr;
+ uint32_t mask;
+ uint32_t v;
+ addr = bitband_addr(opaque, offset) & ~3;
+ mask = (1 << ((offset >> 2) & 31));
+ mask = tswap32(mask);
+ cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+ if (value & 1)
+ v |= mask;
+ else
+ v &= ~mask;
+ cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
+}
+
+static CPUReadMemoryFunc * const bitband_readfn[] = {
+ bitband_readb,
+ bitband_readw,
+ bitband_readl
+};
+
+static CPUWriteMemoryFunc * const bitband_writefn[] = {
+ bitband_writeb,
+ bitband_writew,
+ bitband_writel
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t base;
+} BitBandState;
+
+static int bitband_init(SysBusDevice *dev)
+{
+ BitBandState *s = FROM_SYSBUS(BitBandState, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(bitband_readfn, bitband_writefn,
+ &s->base, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x02000000, iomemtype);
+ return 0;
+}
+
+static void armv7m_bitband_init(void)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "ARM,bitband-memory");
+ qdev_prop_set_uint32(dev, "base", 0x20000000);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x22000000);
+
+ dev = qdev_create(NULL, "ARM,bitband-memory");
+ qdev_prop_set_uint32(dev, "base", 0x40000000);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0x42000000);
+}
+
+/* Board init. */
+
+static void armv7m_reset(void *opaque)
+{
+ cpu_reset((CPUState *)opaque);
+}
+
+/* Init CPU and memory for a v7-M based board.
+ flash_size and sram_size are in kb.
+ Returns the NVIC array. */
+
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+ const char *kernel_filename, const char *cpu_model)
+{
+ CPUState *env;
+ DeviceState *nvic;
+ /* FIXME: make this local state. */
+ static qemu_irq pic[64];
+ qemu_irq *cpu_pic;
+ int image_size;
+ uint64_t entry;
+ uint64_t lowaddr;
+ int i;
+ int big_endian;
+
+ flash_size *= 1024;
+ sram_size *= 1024;
+
+ if (!cpu_model)
+ cpu_model = "cortex-m3";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+#if 0
+ /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
+ We don't have proper commandline options, so allocate half of memory
+ as SRAM, up to a maximum of 32Mb, and the rest as code. */
+ if (ram_size > (512 + 32) * 1024 * 1024)
+ ram_size = (512 + 32) * 1024 * 1024;
+ sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
+ if (sram_size > 32 * 1024 * 1024)
+ sram_size = 32 * 1024 * 1024;
+ code_size = ram_size - sram_size;
+#endif
+
+ /* Flash programming is done via the SCU, so pretend it is ROM. */
+ cpu_register_physical_memory(0, flash_size,
+ qemu_ram_alloc(NULL, "armv7m.flash",
+ flash_size) | IO_MEM_ROM);
+ cpu_register_physical_memory(0x20000000, sram_size,
+ qemu_ram_alloc(NULL, "armv7m.sram",
+ sram_size) | IO_MEM_RAM);
+ armv7m_bitband_init();
+
+ nvic = qdev_create(NULL, "armv7m_nvic");
+ env->nvic = nvic;
+ qdev_init_nofail(nvic);
+ cpu_pic = arm_pic_init_cpu(env);
+ sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]);
+ for (i = 0; i < 64; i++) {
+ pic[i] = qdev_get_gpio_in(nvic, i);
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
+ NULL, big_endian, ELF_MACHINE, 1);
+ if (image_size < 0) {
+ image_size = load_image_targphys(kernel_filename, 0, flash_size);
+ lowaddr = 0;
+ }
+ if (image_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* Hack to map an additional page of ram at the top of the address
+ space. This stops qemu complaining about executing code outside RAM
+ when returning from an exception. */
+ cpu_register_physical_memory(0xfffff000, 0x1000,
+ qemu_ram_alloc(NULL, "armv7m.hack",
+ 0x1000) | IO_MEM_RAM);
+
+ qemu_register_reset(armv7m_reset, env);
+ return pic;
+}
+
+static SysBusDeviceInfo bitband_info = {
+ .init = bitband_init,
+ .qdev.name = "ARM,bitband-memory",
+ .qdev.size = sizeof(BitBandState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("base", BitBandState, base, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void armv7m_register_devices(void)
+{
+ sysbus_register_withprop(&bitband_info);
+}
+
+device_init(armv7m_register_devices)
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
new file mode 100644
index 0000000..6c7ce01
--- /dev/null
+++ b/hw/armv7m_nvic.c
@@ -0,0 +1,409 @@
+/*
+ * ARM Nested Vectored Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ *
+ * The ARMv7M System controller is fairly tightly tied in with the
+ * NVIC. Much of that is also implemented here.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "arm-misc.h"
+
+/* 32 internal lines (16 used for system exceptions) plus 64 external
+ interrupt lines. */
+#define GIC_NIRQ 96
+#define NCPU 1
+#define NVIC 1
+
+/* Only a single "CPU" interface is present. */
+static inline int
+gic_get_current_cpu(void)
+{
+ return 0;
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset);
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
+
+#include "arm_gic.c"
+
+typedef struct {
+ gic_state gic;
+ struct {
+ uint32_t control;
+ uint32_t reload;
+ int64_t tick;
+ QEMUTimer *timer;
+ } systick;
+} nvic_state;
+
+/* qemu timers run at 1GHz. We want something closer to 1MHz. */
+#define SYSTICK_SCALE 1000ULL
+
+#define SYSTICK_ENABLE (1 << 0)
+#define SYSTICK_TICKINT (1 << 1)
+#define SYSTICK_CLKSOURCE (1 << 2)
+#define SYSTICK_COUNTFLAG (1 << 16)
+
+int system_clock_scale;
+
+/* Conversion factor from qemu timer to SysTick frequencies. */
+static inline int64_t systick_scale(nvic_state *s)
+{
+ if (s->systick.control & SYSTICK_CLKSOURCE)
+ return system_clock_scale;
+ else
+ return 1000;
+}
+
+static void systick_reload(nvic_state *s, int reset)
+{
+ if (reset)
+ s->systick.tick = qemu_get_clock(vm_clock);
+ s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
+ qemu_mod_timer(s->systick.timer, s->systick.tick);
+}
+
+static void systick_timer_tick(void * opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ s->systick.control |= SYSTICK_COUNTFLAG;
+ if (s->systick.control & SYSTICK_TICKINT) {
+ /* Trigger the interrupt. */
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ }
+ if (s->systick.reload == 0) {
+ s->systick.control &= ~SYSTICK_ENABLE;
+ } else {
+ systick_reload(s, 0);
+ }
+}
+
+/* The external routines use the hardware vector numbering, ie. the first
+ IRQ is #16. The internal GIC routines use #32 as the first IRQ. */
+void armv7m_nvic_set_pending(void *opaque, int irq)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ if (irq >= 16)
+ irq += 16;
+ gic_set_pending_private(&s->gic, 0, irq);
+}
+
+/* Make pending IRQ active. */
+int armv7m_nvic_acknowledge_irq(void *opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t irq;
+
+ irq = gic_acknowledge_irq(&s->gic, 0);
+ if (irq == 1023)
+ hw_error("Interrupt but no vector\n");
+ if (irq >= 32)
+ irq -= 16;
+ return irq;
+}
+
+void armv7m_nvic_complete_irq(void *opaque, int irq)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ if (irq >= 16)
+ irq += 16;
+ gic_complete_irq(&s->gic, 0, irq);
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t val;
+ int irq;
+
+ switch (offset) {
+ case 4: /* Interrupt Control Type. */
+ return (GIC_NIRQ / 32) - 1;
+ case 0x10: /* SysTick Control and Status. */
+ val = s->systick.control;
+ s->systick.control &= ~SYSTICK_COUNTFLAG;
+ return val;
+ case 0x14: /* SysTick Reload Value. */
+ return s->systick.reload;
+ case 0x18: /* SysTick Current Value. */
+ {
+ int64_t t;
+ if ((s->systick.control & SYSTICK_ENABLE) == 0)
+ return 0;
+ t = qemu_get_clock(vm_clock);
+ if (t >= s->systick.tick)
+ return 0;
+ val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
+ /* The interrupt in triggered when the timer reaches zero.
+ However the counter is not reloaded until the next clock
+ tick. This is a hack to return zero during the first tick. */
+ if (val > s->systick.reload)
+ val = 0;
+ return val;
+ }
+ case 0x1c: /* SysTick Calibration Value. */
+ return 10000;
+ case 0xd00: /* CPUID Base. */
+ return cpu_single_env->cp15.c0_cpuid;
+ case 0xd04: /* Interrypt Control State. */
+ /* VECTACTIVE */
+ val = s->gic.running_irq[0];
+ if (val == 1023) {
+ val = 0;
+ } else if (val >= 32) {
+ val -= 16;
+ }
+ /* RETTOBASE */
+ if (s->gic.running_irq[0] == 1023
+ || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) {
+ val |= (1 << 11);
+ }
+ /* VECTPENDING */
+ if (s->gic.current_pending[0] != 1023)
+ val |= (s->gic.current_pending[0] << 12);
+ /* ISRPENDING */
+ for (irq = 32; irq < GIC_NIRQ; irq++) {
+ if (s->gic.irq_state[irq].pending) {
+ val |= (1 << 22);
+ break;
+ }
+ }
+ /* PENDSTSET */
+ if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending)
+ val |= (1 << 26);
+ /* PENDSVSET */
+ if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending)
+ val |= (1 << 28);
+ /* NMIPENDSET */
+ if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending)
+ val |= (1 << 31);
+ return val;
+ case 0xd08: /* Vector Table Offset. */
+ return cpu_single_env->v7m.vecbase;
+ case 0xd0c: /* Application Interrupt/Reset Control. */
+ return 0xfa05000;
+ case 0xd10: /* System Control. */
+ /* TODO: Implement SLEEPONEXIT. */
+ return 0;
+ case 0xd14: /* Configuration Control. */
+ /* TODO: Implement Configuration Control bits. */
+ return 0;
+ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
+ irq = offset - 0xd14;
+ val = 0;
+ val |= s->gic.priority1[irq++][0];
+ val |= s->gic.priority1[irq++][0] << 8;
+ val |= s->gic.priority1[irq++][0] << 16;
+ val |= s->gic.priority1[irq][0] << 24;
+ return val;
+ case 0xd24: /* System Handler Status. */
+ val = 0;
+ if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
+ if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
+ if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
+ if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
+ if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
+ if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
+ if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
+ if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
+ if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
+ if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
+ if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
+ if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
+ if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
+ if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
+ return val;
+ case 0xd28: /* Configurable Fault Status. */
+ /* TODO: Implement Fault Status. */
+ hw_error("Not implemented: Configurable Fault Status.");
+ return 0;
+ case 0xd2c: /* Hard Fault Status. */
+ case 0xd30: /* Debug Fault Status. */
+ case 0xd34: /* Mem Manage Address. */
+ case 0xd38: /* Bus Fault Address. */
+ case 0xd3c: /* Aux Fault Status. */
+ /* TODO: Implement fault status registers. */
+ goto bad_reg;
+ case 0xd40: /* PFR0. */
+ return 0x00000030;
+ case 0xd44: /* PRF1. */
+ return 0x00000200;
+ case 0xd48: /* DFR0. */
+ return 0x00100000;
+ case 0xd4c: /* AFR0. */
+ return 0x00000000;
+ case 0xd50: /* MMFR0. */
+ return 0x00000030;
+ case 0xd54: /* MMFR1. */
+ return 0x00000000;
+ case 0xd58: /* MMFR2. */
+ return 0x00000000;
+ case 0xd5c: /* MMFR3. */
+ return 0x00000000;
+ case 0xd60: /* ISAR0. */
+ return 0x01141110;
+ case 0xd64: /* ISAR1. */
+ return 0x02111000;
+ case 0xd68: /* ISAR2. */
+ return 0x21112231;
+ case 0xd6c: /* ISAR3. */
+ return 0x01111110;
+ case 0xd70: /* ISAR4. */
+ return 0x01310102;
+ /* TODO: Implement debug registers. */
+ default:
+ bad_reg:
+ hw_error("NVIC: Bad read offset 0x%x\n", offset);
+ }
+}
+
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
+{
+ nvic_state *s = (nvic_state *)opaque;
+ uint32_t oldval;
+ switch (offset) {
+ case 0x10: /* SysTick Control and Status. */
+ oldval = s->systick.control;
+ s->systick.control &= 0xfffffff8;
+ s->systick.control |= value & 7;
+ if ((oldval ^ value) & SYSTICK_ENABLE) {
+ int64_t now = qemu_get_clock(vm_clock);
+ if (value & SYSTICK_ENABLE) {
+ if (s->systick.tick) {
+ s->systick.tick += now;
+ qemu_mod_timer(s->systick.timer, s->systick.tick);
+ } else {
+ systick_reload(s, 1);
+ }
+ } else {
+ qemu_del_timer(s->systick.timer);
+ s->systick.tick -= now;
+ if (s->systick.tick < 0)
+ s->systick.tick = 0;
+ }
+ } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
+ /* This is a hack. Force the timer to be reloaded
+ when the reference clock is changed. */
+ systick_reload(s, 1);
+ }
+ break;
+ case 0x14: /* SysTick Reload Value. */
+ s->systick.reload = value;
+ break;
+ case 0x18: /* SysTick Current Value. Writes reload the timer. */
+ systick_reload(s, 1);
+ s->systick.control &= ~SYSTICK_COUNTFLAG;
+ break;
+ case 0xd04: /* Interrupt Control State. */
+ if (value & (1 << 31)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
+ }
+ if (value & (1 << 28)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
+ } else if (value & (1 << 27)) {
+ s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
+ gic_update(&s->gic);
+ }
+ if (value & (1 << 26)) {
+ armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+ } else if (value & (1 << 25)) {
+ s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
+ gic_update(&s->gic);
+ }
+ break;
+ case 0xd08: /* Vector Table Offset. */
+ cpu_single_env->v7m.vecbase = value & 0xffffff80;
+ break;
+ case 0xd0c: /* Application Interrupt/Reset Control. */
+ if ((value >> 16) == 0x05fa) {
+ if (value & 2) {
+ hw_error("VECTCLRACTIVE not implemented");
+ }
+ if (value & 5) {
+ hw_error("System reset");
+ }
+ }
+ break;
+ case 0xd10: /* System Control. */
+ case 0xd14: /* Configuration Control. */
+ /* TODO: Implement control registers. */
+ goto bad_reg;
+ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */
+ {
+ int irq;
+ irq = offset - 0xd14;
+ s->gic.priority1[irq++][0] = value & 0xff;
+ s->gic.priority1[irq++][0] = (value >> 8) & 0xff;
+ s->gic.priority1[irq++][0] = (value >> 16) & 0xff;
+ s->gic.priority1[irq][0] = (value >> 24) & 0xff;
+ gic_update(&s->gic);
+ }
+ break;
+ case 0xd24: /* System Handler Control. */
+ /* TODO: Real hardware allows you to set/clear the active bits
+ under some circumstances. We don't implement this. */
+ s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+ s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+ s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+ break;
+ case 0xd28: /* Configurable Fault Status. */
+ case 0xd2c: /* Hard Fault Status. */
+ case 0xd30: /* Debug Fault Status. */
+ case 0xd34: /* Mem Manage Address. */
+ case 0xd38: /* Bus Fault Address. */
+ case 0xd3c: /* Aux Fault Status. */
+ goto bad_reg;
+ default:
+ bad_reg:
+ hw_error("NVIC: Bad write offset 0x%x\n", offset);
+ }
+}
+
+static void nvic_save(QEMUFile *f, void *opaque)
+{
+ nvic_state *s = (nvic_state *)opaque;
+
+ qemu_put_be32(f, s->systick.control);
+ qemu_put_be32(f, s->systick.reload);
+ qemu_put_be64(f, s->systick.tick);
+ qemu_put_timer(f, s->systick.timer);
+}
+
+static int nvic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ nvic_state *s = (nvic_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->systick.control = qemu_get_be32(f);
+ s->systick.reload = qemu_get_be32(f);
+ s->systick.tick = qemu_get_be64(f);
+ qemu_get_timer(f, s->systick.timer);
+
+ return 0;
+}
+
+static int armv7m_nvic_init(SysBusDevice *dev)
+{
+ nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev);
+
+ gic_init(&s->gic);
+ cpu_register_physical_memory(0xe000e000, 0x1000, s->gic.iomemtype);
+ s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
+ register_savevm(&dev->qdev, "armv7m_nvic", -1, 1, nvic_save, nvic_load, s);
+ return 0;
+}
+
+static void armv7m_nvic_register_devices(void)
+{
+ sysbus_register_dev("armv7m_nvic", sizeof(nvic_state), armv7m_nvic_init);
+}
+
+device_init(armv7m_nvic_register_devices)
diff --git a/hw/audiodev.h b/hw/audiodev.h
new file mode 100644
index 0000000..8e930b2
--- /dev/null
+++ b/hw/audiodev.h
@@ -0,0 +1,20 @@
+/* es1370.c */
+int es1370_init(PCIBus *bus);
+
+/* sb16.c */
+int SB16_init(qemu_irq *pic);
+
+/* adlib.c */
+int Adlib_init(qemu_irq *pic);
+
+/* gus.c */
+int GUS_init(qemu_irq *pic);
+
+/* ac97.c */
+int ac97_init(PCIBus *buf);
+
+/* cs4231a.c */
+int cs4231a_init(qemu_irq *pic);
+
+/* intel-hda.c + hda-audio.c */
+int intel_hda_and_codec_init(PCIBus *bus);
diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c
new file mode 100644
index 0000000..57b5e2f
--- /dev/null
+++ b/hw/axis_dev88.c
@@ -0,0 +1,357 @@
+/*
+ * QEMU model for the AXIS devboard 88.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "net.h"
+#include "flash.h"
+#include "boards.h"
+#include "sysemu.h"
+#include "etraxfs.h"
+#include "loader.h"
+#include "elf.h"
+#include "cris-boot.h"
+
+#define D(x)
+#define DNAND(x)
+
+struct nand_state_t
+{
+ NANDFlashState *nand;
+ unsigned int rdy:1;
+ unsigned int ale:1;
+ unsigned int cle:1;
+ unsigned int ce:1;
+};
+
+static struct nand_state_t nand_state;
+static uint32_t nand_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct nand_state_t *s = opaque;
+ uint32_t r;
+ int rdy;
+
+ r = nand_getio(s->nand);
+ nand_getpins(s->nand, &rdy);
+ s->rdy = rdy;
+
+ DNAND(printf("%s addr=%x r=%x\n", __func__, addr, r));
+ return r;
+}
+
+static void
+nand_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct nand_state_t *s = opaque;
+ int rdy;
+
+ DNAND(printf("%s addr=%x v=%x\n", __func__, addr, value));
+ nand_setpins(s->nand, s->cle, s->ale, s->ce, 1, 0);
+ nand_setio(s->nand, value);
+ nand_getpins(s->nand, &rdy);
+ s->rdy = rdy;
+}
+
+static CPUReadMemoryFunc * const nand_read[] = {
+ &nand_readl,
+ &nand_readl,
+ &nand_readl,
+};
+
+static CPUWriteMemoryFunc * const nand_write[] = {
+ &nand_writel,
+ &nand_writel,
+ &nand_writel,
+};
+
+
+struct tempsensor_t
+{
+ unsigned int shiftreg;
+ unsigned int count;
+ enum {
+ ST_OUT, ST_IN, ST_Z
+ } state;
+
+ uint16_t regs[3];
+};
+
+static void tempsensor_clkedge(struct tempsensor_t *s,
+ unsigned int clk, unsigned int data_in)
+{
+ D(printf("%s clk=%d state=%d sr=%x\n", __func__,
+ clk, s->state, s->shiftreg));
+ if (s->count == 0) {
+ s->count = 16;
+ s->state = ST_OUT;
+ }
+ switch (s->state) {
+ case ST_OUT:
+ /* Output reg is clocked at negedge. */
+ if (!clk) {
+ s->count--;
+ s->shiftreg <<= 1;
+ if (s->count == 0) {
+ s->shiftreg = 0;
+ s->state = ST_IN;
+ s->count = 16;
+ }
+ }
+ break;
+ case ST_Z:
+ if (clk) {
+ s->count--;
+ if (s->count == 0) {
+ s->shiftreg = 0;
+ s->state = ST_OUT;
+ s->count = 16;
+ }
+ }
+ break;
+ case ST_IN:
+ /* Indata is sampled at posedge. */
+ if (clk) {
+ s->count--;
+ s->shiftreg <<= 1;
+ s->shiftreg |= data_in & 1;
+ if (s->count == 0) {
+ D(printf("%s cfgreg=%x\n", __func__, s->shiftreg));
+ s->regs[0] = s->shiftreg;
+ s->state = ST_OUT;
+ s->count = 16;
+
+ if ((s->regs[0] & 0xff) == 0) {
+ /* 25 degrees celcius. */
+ s->shiftreg = 0x0b9f;
+ } else if ((s->regs[0] & 0xff) == 0xff) {
+ /* Sensor ID, 0x8100 LM70. */
+ s->shiftreg = 0x8100;
+ } else
+ printf("Invalid tempsens state %x\n", s->regs[0]);
+ }
+ }
+ break;
+ }
+}
+
+
+#define RW_PA_DOUT 0x00
+#define R_PA_DIN 0x01
+#define RW_PA_OE 0x02
+#define RW_PD_DOUT 0x10
+#define R_PD_DIN 0x11
+#define RW_PD_OE 0x12
+
+static struct gpio_state_t
+{
+ struct nand_state_t *nand;
+ struct tempsensor_t tempsensor;
+ uint32_t regs[0x5c / 4];
+} gpio_state;
+
+static uint32_t gpio_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct gpio_state_t *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_PA_DIN:
+ r = s->regs[RW_PA_DOUT] & s->regs[RW_PA_OE];
+
+ /* Encode pins from the nand. */
+ r |= s->nand->rdy << 7;
+ break;
+ case R_PD_DIN:
+ r = s->regs[RW_PD_DOUT] & s->regs[RW_PD_OE];
+
+ /* Encode temp sensor pins. */
+ r |= (!!(s->tempsensor.shiftreg & 0x10000)) << 4;
+ break;
+
+ default:
+ r = s->regs[addr];
+ break;
+ }
+ return r;
+ D(printf("%s %x=%x\n", __func__, addr, r));
+}
+
+static void gpio_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct gpio_state_t *s = opaque;
+ D(printf("%s %x=%x\n", __func__, addr, value));
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case RW_PA_DOUT:
+ /* Decode nand pins. */
+ s->nand->ale = !!(value & (1 << 6));
+ s->nand->cle = !!(value & (1 << 5));
+ s->nand->ce = !!(value & (1 << 4));
+
+ s->regs[addr] = value;
+ break;
+
+ case RW_PD_DOUT:
+ /* Temp sensor clk. */
+ if ((s->regs[addr] ^ value) & 2)
+ tempsensor_clkedge(&s->tempsensor, !!(value & 2),
+ !!(value & 16));
+ s->regs[addr] = value;
+ break;
+
+ default:
+ s->regs[addr] = value;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const gpio_read[] = {
+ NULL, NULL,
+ &gpio_readl,
+};
+
+static CPUWriteMemoryFunc * const gpio_write[] = {
+ NULL, NULL,
+ &gpio_writel,
+};
+
+#define INTMEM_SIZE (128 * 1024)
+
+static struct cris_load_info li;
+
+static
+void axisdev88_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ DeviceState *dev;
+ SysBusDevice *s;
+ qemu_irq irq[30], nmi[2], *cpu_irq;
+ void *etraxfs_dmac;
+ struct etraxfs_dma_client *eth[2] = {NULL, NULL};
+ int i;
+ int nand_regs;
+ int gpio_regs;
+ ram_addr_t phys_ram;
+ ram_addr_t phys_intmem;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "crisv32";
+ }
+ env = cpu_init(cpu_model);
+
+ /* allocate RAM */
+ phys_ram = qemu_ram_alloc(NULL, "axisdev88.ram", ram_size);
+ cpu_register_physical_memory(0x40000000, ram_size, phys_ram | IO_MEM_RAM);
+
+ /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the
+ internal memory. */
+ phys_intmem = qemu_ram_alloc(NULL, "axisdev88.chipram", INTMEM_SIZE);
+ cpu_register_physical_memory(0x38000000, INTMEM_SIZE,
+ phys_intmem | IO_MEM_RAM);
+
+
+ /* Attach a NAND flash to CS1. */
+ nand_state.nand = nand_init(NAND_MFR_STMICRO, 0x39);
+ nand_regs = cpu_register_io_memory(nand_read, nand_write, &nand_state,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0x10000000, 0x05000000, nand_regs);
+
+ gpio_state.nand = &nand_state;
+ gpio_regs = cpu_register_io_memory(gpio_read, gpio_write, &gpio_state,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0x3001a000, 0x5c, gpio_regs);
+
+
+ cpu_irq = cris_pic_init_cpu(env);
+ dev = qdev_create(NULL, "etraxfs,pic");
+ /* FIXME: Is there a proper way to signal vectors to the CPU core? */
+ qdev_prop_set_ptr(dev, "interrupt_vector", &env->interrupt_vector);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, 0x3001c000);
+ sysbus_connect_irq(s, 0, cpu_irq[0]);
+ sysbus_connect_irq(s, 1, cpu_irq[1]);
+ for (i = 0; i < 30; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+ nmi[0] = qdev_get_gpio_in(dev, 30);
+ nmi[1] = qdev_get_gpio_in(dev, 31);
+
+ etraxfs_dmac = etraxfs_dmac_init(0x30000000, 10);
+ for (i = 0; i < 10; i++) {
+ /* On ETRAX, odd numbered channels are inputs. */
+ etraxfs_dmac_connect(etraxfs_dmac, i, irq + 7 + i, i & 1);
+ }
+
+ /* Add the two ethernet blocks. */
+ eth[0] = etraxfs_eth_init(&nd_table[0], 0x30034000, 1);
+ if (nb_nics > 1)
+ eth[1] = etraxfs_eth_init(&nd_table[1], 0x30036000, 2);
+
+ /* The DMA Connector block is missing, hardwire things for now. */
+ etraxfs_dmac_connect_client(etraxfs_dmac, 0, eth[0]);
+ etraxfs_dmac_connect_client(etraxfs_dmac, 1, eth[0] + 1);
+ if (eth[1]) {
+ etraxfs_dmac_connect_client(etraxfs_dmac, 6, eth[1]);
+ etraxfs_dmac_connect_client(etraxfs_dmac, 7, eth[1] + 1);
+ }
+
+ /* 2 timers. */
+ sysbus_create_varargs("etraxfs,timer", 0x3001e000, irq[0x1b], nmi[1], NULL);
+ sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL);
+
+ for (i = 0; i < 4; i++) {
+ sysbus_create_simple("etraxfs,serial", 0x30026000 + i * 0x2000,
+ irq[0x14 + i]);
+ }
+
+ if (!kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ li.image_filename = kernel_filename;
+ li.cmdline = kernel_cmdline;
+ cris_load_image(env, &li);
+}
+
+static QEMUMachine axisdev88_machine = {
+ .name = "axis-dev88",
+ .desc = "AXIS devboard 88",
+ .init = axisdev88_init,
+};
+
+static void axisdev88_machine_init(void)
+{
+ qemu_register_machine(&axisdev88_machine);
+}
+
+machine_init(axisdev88_machine_init);
diff --git a/hw/baum.c b/hw/baum.c
new file mode 100644
index 0000000..21326ae
--- /dev/null
+++ b/hw/baum.c
@@ -0,0 +1,642 @@
+/*
+ * QEMU Baum Braille Device
+ *
+ * Copyright (c) 2008 Samuel Thibault
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "baum.h"
+#include <brlapi.h>
+#include <brlapi_constants.h>
+#include <brlapi_keycodes.h>
+#ifdef CONFIG_SDL
+#include <SDL_syswm.h>
+#endif
+
+#if 0
+#define DPRINTF(fmt, ...) \
+ printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define ESC 0x1B
+
+#define BAUM_REQ_DisplayData 0x01
+#define BAUM_REQ_GetVersionNumber 0x05
+#define BAUM_REQ_GetKeys 0x08
+#define BAUM_REQ_SetMode 0x12
+#define BAUM_REQ_SetProtocol 0x15
+#define BAUM_REQ_GetDeviceIdentity 0x84
+#define BAUM_REQ_GetSerialNumber 0x8A
+
+#define BAUM_RSP_CellCount 0x01
+#define BAUM_RSP_VersionNumber 0x05
+#define BAUM_RSP_ModeSetting 0x11
+#define BAUM_RSP_CommunicationChannel 0x16
+#define BAUM_RSP_PowerdownSignal 0x17
+#define BAUM_RSP_HorizontalSensors 0x20
+#define BAUM_RSP_VerticalSensors 0x21
+#define BAUM_RSP_RoutingKeys 0x22
+#define BAUM_RSP_Switches 0x23
+#define BAUM_RSP_TopKeys 0x24
+#define BAUM_RSP_HorizontalSensor 0x25
+#define BAUM_RSP_VerticalSensor 0x26
+#define BAUM_RSP_RoutingKey 0x27
+#define BAUM_RSP_FrontKeys6 0x28
+#define BAUM_RSP_BackKeys6 0x29
+#define BAUM_RSP_CommandKeys 0x2B
+#define BAUM_RSP_FrontKeys10 0x2C
+#define BAUM_RSP_BackKeys10 0x2D
+#define BAUM_RSP_EntryKeys 0x33
+#define BAUM_RSP_JoyStick 0x34
+#define BAUM_RSP_ErrorCode 0x40
+#define BAUM_RSP_InfoBlock 0x42
+#define BAUM_RSP_DeviceIdentity 0x84
+#define BAUM_RSP_SerialNumber 0x8A
+#define BAUM_RSP_BluetoothName 0x8C
+
+#define BAUM_TL1 0x01
+#define BAUM_TL2 0x02
+#define BAUM_TL3 0x04
+#define BAUM_TR1 0x08
+#define BAUM_TR2 0x10
+#define BAUM_TR3 0x20
+
+#define BUF_SIZE 256
+
+typedef struct {
+ CharDriverState *chr;
+
+ brlapi_handle_t *brlapi;
+ int brlapi_fd;
+ unsigned int x, y;
+
+ uint8_t in_buf[BUF_SIZE];
+ uint8_t in_buf_used;
+ uint8_t out_buf[BUF_SIZE];
+ uint8_t out_buf_used, out_buf_ptr;
+
+ QEMUTimer *cellCount_timer;
+} BaumDriverState;
+
+/* Let's assume NABCC by default */
+static const uint8_t nabcc_translation[256] = {
+ [0] = ' ',
+#ifndef BRLAPI_DOTS
+#define BRLAPI_DOTS(d1,d2,d3,d4,d5,d6,d7,d8) \
+ ((d1?BRLAPI_DOT1:0)|\
+ (d2?BRLAPI_DOT2:0)|\
+ (d3?BRLAPI_DOT3:0)|\
+ (d4?BRLAPI_DOT4:0)|\
+ (d5?BRLAPI_DOT5:0)|\
+ (d6?BRLAPI_DOT6:0)|\
+ (d7?BRLAPI_DOT7:0)|\
+ (d8?BRLAPI_DOT8:0))
+#endif
+ [BRLAPI_DOTS(1,0,0,0,0,0,0,0)] = 'a',
+ [BRLAPI_DOTS(1,1,0,0,0,0,0,0)] = 'b',
+ [BRLAPI_DOTS(1,0,0,1,0,0,0,0)] = 'c',
+ [BRLAPI_DOTS(1,0,0,1,1,0,0,0)] = 'd',
+ [BRLAPI_DOTS(1,0,0,0,1,0,0,0)] = 'e',
+ [BRLAPI_DOTS(1,1,0,1,0,0,0,0)] = 'f',
+ [BRLAPI_DOTS(1,1,0,1,1,0,0,0)] = 'g',
+ [BRLAPI_DOTS(1,1,0,0,1,0,0,0)] = 'h',
+ [BRLAPI_DOTS(0,1,0,1,0,0,0,0)] = 'i',
+ [BRLAPI_DOTS(0,1,0,1,1,0,0,0)] = 'j',
+ [BRLAPI_DOTS(1,0,1,0,0,0,0,0)] = 'k',
+ [BRLAPI_DOTS(1,1,1,0,0,0,0,0)] = 'l',
+ [BRLAPI_DOTS(1,0,1,1,0,0,0,0)] = 'm',
+ [BRLAPI_DOTS(1,0,1,1,1,0,0,0)] = 'n',
+ [BRLAPI_DOTS(1,0,1,0,1,0,0,0)] = 'o',
+ [BRLAPI_DOTS(1,1,1,1,0,0,0,0)] = 'p',
+ [BRLAPI_DOTS(1,1,1,1,1,0,0,0)] = 'q',
+ [BRLAPI_DOTS(1,1,1,0,1,0,0,0)] = 'r',
+ [BRLAPI_DOTS(0,1,1,1,0,0,0,0)] = 's',
+ [BRLAPI_DOTS(0,1,1,1,1,0,0,0)] = 't',
+ [BRLAPI_DOTS(1,0,1,0,0,1,0,0)] = 'u',
+ [BRLAPI_DOTS(1,1,1,0,0,1,0,0)] = 'v',
+ [BRLAPI_DOTS(0,1,0,1,1,1,0,0)] = 'w',
+ [BRLAPI_DOTS(1,0,1,1,0,1,0,0)] = 'x',
+ [BRLAPI_DOTS(1,0,1,1,1,1,0,0)] = 'y',
+ [BRLAPI_DOTS(1,0,1,0,1,1,0,0)] = 'z',
+
+ [BRLAPI_DOTS(1,0,0,0,0,0,1,0)] = 'A',
+ [BRLAPI_DOTS(1,1,0,0,0,0,1,0)] = 'B',
+ [BRLAPI_DOTS(1,0,0,1,0,0,1,0)] = 'C',
+ [BRLAPI_DOTS(1,0,0,1,1,0,1,0)] = 'D',
+ [BRLAPI_DOTS(1,0,0,0,1,0,1,0)] = 'E',
+ [BRLAPI_DOTS(1,1,0,1,0,0,1,0)] = 'F',
+ [BRLAPI_DOTS(1,1,0,1,1,0,1,0)] = 'G',
+ [BRLAPI_DOTS(1,1,0,0,1,0,1,0)] = 'H',
+ [BRLAPI_DOTS(0,1,0,1,0,0,1,0)] = 'I',
+ [BRLAPI_DOTS(0,1,0,1,1,0,1,0)] = 'J',
+ [BRLAPI_DOTS(1,0,1,0,0,0,1,0)] = 'K',
+ [BRLAPI_DOTS(1,1,1,0,0,0,1,0)] = 'L',
+ [BRLAPI_DOTS(1,0,1,1,0,0,1,0)] = 'M',
+ [BRLAPI_DOTS(1,0,1,1,1,0,1,0)] = 'N',
+ [BRLAPI_DOTS(1,0,1,0,1,0,1,0)] = 'O',
+ [BRLAPI_DOTS(1,1,1,1,0,0,1,0)] = 'P',
+ [BRLAPI_DOTS(1,1,1,1,1,0,1,0)] = 'Q',
+ [BRLAPI_DOTS(1,1,1,0,1,0,1,0)] = 'R',
+ [BRLAPI_DOTS(0,1,1,1,0,0,1,0)] = 'S',
+ [BRLAPI_DOTS(0,1,1,1,1,0,1,0)] = 'T',
+ [BRLAPI_DOTS(1,0,1,0,0,1,1,0)] = 'U',
+ [BRLAPI_DOTS(1,1,1,0,0,1,1,0)] = 'V',
+ [BRLAPI_DOTS(0,1,0,1,1,1,1,0)] = 'W',
+ [BRLAPI_DOTS(1,0,1,1,0,1,1,0)] = 'X',
+ [BRLAPI_DOTS(1,0,1,1,1,1,1,0)] = 'Y',
+ [BRLAPI_DOTS(1,0,1,0,1,1,1,0)] = 'Z',
+
+ [BRLAPI_DOTS(0,0,1,0,1,1,0,0)] = '0',
+ [BRLAPI_DOTS(0,1,0,0,0,0,0,0)] = '1',
+ [BRLAPI_DOTS(0,1,1,0,0,0,0,0)] = '2',
+ [BRLAPI_DOTS(0,1,0,0,1,0,0,0)] = '3',
+ [BRLAPI_DOTS(0,1,0,0,1,1,0,0)] = '4',
+ [BRLAPI_DOTS(0,1,0,0,0,1,0,0)] = '5',
+ [BRLAPI_DOTS(0,1,1,0,1,0,0,0)] = '6',
+ [BRLAPI_DOTS(0,1,1,0,1,1,0,0)] = '7',
+ [BRLAPI_DOTS(0,1,1,0,0,1,0,0)] = '8',
+ [BRLAPI_DOTS(0,0,1,0,1,0,0,0)] = '9',
+
+ [BRLAPI_DOTS(0,0,0,1,0,1,0,0)] = '.',
+ [BRLAPI_DOTS(0,0,1,1,0,1,0,0)] = '+',
+ [BRLAPI_DOTS(0,0,1,0,0,1,0,0)] = '-',
+ [BRLAPI_DOTS(1,0,0,0,0,1,0,0)] = '*',
+ [BRLAPI_DOTS(0,0,1,1,0,0,0,0)] = '/',
+ [BRLAPI_DOTS(1,1,1,0,1,1,0,0)] = '(',
+ [BRLAPI_DOTS(0,1,1,1,1,1,0,0)] = ')',
+
+ [BRLAPI_DOTS(1,1,1,1,0,1,0,0)] = '&',
+ [BRLAPI_DOTS(0,0,1,1,1,1,0,0)] = '#',
+
+ [BRLAPI_DOTS(0,0,0,0,0,1,0,0)] = ',',
+ [BRLAPI_DOTS(0,0,0,0,1,1,0,0)] = ';',
+ [BRLAPI_DOTS(1,0,0,0,1,1,0,0)] = ':',
+ [BRLAPI_DOTS(0,1,1,1,0,1,0,0)] = '!',
+ [BRLAPI_DOTS(1,0,0,1,1,1,0,0)] = '?',
+ [BRLAPI_DOTS(0,0,0,0,1,0,0,0)] = '"',
+ [BRLAPI_DOTS(0,0,1,0,0,0,0,0)] ='\'',
+ [BRLAPI_DOTS(0,0,0,1,0,0,0,0)] = '`',
+ [BRLAPI_DOTS(0,0,0,1,1,0,1,0)] = '^',
+ [BRLAPI_DOTS(0,0,0,1,1,0,0,0)] = '~',
+ [BRLAPI_DOTS(0,1,0,1,0,1,1,0)] = '[',
+ [BRLAPI_DOTS(1,1,0,1,1,1,1,0)] = ']',
+ [BRLAPI_DOTS(0,1,0,1,0,1,0,0)] = '{',
+ [BRLAPI_DOTS(1,1,0,1,1,1,0,0)] = '}',
+ [BRLAPI_DOTS(1,1,1,1,1,1,0,0)] = '=',
+ [BRLAPI_DOTS(1,1,0,0,0,1,0,0)] = '<',
+ [BRLAPI_DOTS(0,0,1,1,1,0,0,0)] = '>',
+ [BRLAPI_DOTS(1,1,0,1,0,1,0,0)] = '$',
+ [BRLAPI_DOTS(1,0,0,1,0,1,0,0)] = '%',
+ [BRLAPI_DOTS(0,0,0,1,0,0,1,0)] = '@',
+ [BRLAPI_DOTS(1,1,0,0,1,1,0,0)] = '|',
+ [BRLAPI_DOTS(1,1,0,0,1,1,1,0)] ='\\',
+ [BRLAPI_DOTS(0,0,0,1,1,1,0,0)] = '_',
+};
+
+/* The serial port can receive more of our data */
+static void baum_accept_input(struct CharDriverState *chr)
+{
+ BaumDriverState *baum = chr->opaque;
+ int room, first;
+
+ if (!baum->out_buf_used)
+ return;
+ room = qemu_chr_can_read(chr);
+ if (!room)
+ return;
+ if (room > baum->out_buf_used)
+ room = baum->out_buf_used;
+
+ first = BUF_SIZE - baum->out_buf_ptr;
+ if (room > first) {
+ qemu_chr_read(chr, baum->out_buf + baum->out_buf_ptr, first);
+ baum->out_buf_ptr = 0;
+ baum->out_buf_used -= first;
+ room -= first;
+ }
+ qemu_chr_read(chr, baum->out_buf + baum->out_buf_ptr, room);
+ baum->out_buf_ptr += room;
+ baum->out_buf_used -= room;
+}
+
+/* We want to send a packet */
+static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len)
+{
+ uint8_t io_buf[1 + 2 * len], *cur = io_buf;
+ int room;
+ *cur++ = ESC;
+ while (len--)
+ if ((*cur++ = *buf++) == ESC)
+ *cur++ = ESC;
+ room = qemu_chr_can_read(baum->chr);
+ len = cur - io_buf;
+ if (len <= room) {
+ /* Fits */
+ qemu_chr_read(baum->chr, io_buf, len);
+ } else {
+ int first;
+ uint8_t out;
+ /* Can't fit all, send what can be, and store the rest. */
+ qemu_chr_read(baum->chr, io_buf, room);
+ len -= room;
+ cur = io_buf + room;
+ if (len > BUF_SIZE - baum->out_buf_used) {
+ /* Can't even store it, drop the previous data... */
+ assert(len <= BUF_SIZE);
+ baum->out_buf_used = 0;
+ baum->out_buf_ptr = 0;
+ }
+ out = baum->out_buf_ptr;
+ baum->out_buf_used += len;
+ first = BUF_SIZE - baum->out_buf_ptr;
+ if (len > first) {
+ memcpy(baum->out_buf + out, cur, first);
+ out = 0;
+ len -= first;
+ cur += first;
+ }
+ memcpy(baum->out_buf + out, cur, len);
+ }
+}
+
+/* Called when the other end seems to have a wrong idea of our display size */
+static void baum_cellCount_timer_cb(void *opaque)
+{
+ BaumDriverState *baum = opaque;
+ uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y };
+ DPRINTF("Timeout waiting for DisplayData, sending cell count\n");
+ baum_write_packet(baum, cell_count, sizeof(cell_count));
+}
+
+/* Try to interpret a whole incoming packet */
+static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len)
+{
+ const uint8_t *cur = buf;
+ uint8_t req = 0;
+
+ if (!len--)
+ return 0;
+ if (*cur++ != ESC) {
+ while (*cur != ESC) {
+ if (!len--)
+ return 0;
+ cur++;
+ }
+ DPRINTF("Dropped %d bytes!\n", cur - buf);
+ }
+
+#define EAT(c) do {\
+ if (!len--) \
+ return 0; \
+ if ((c = *cur++) == ESC) { \
+ if (!len--) \
+ return 0; \
+ if (*cur++ != ESC) { \
+ DPRINTF("Broken packet %#2x, tossing\n", req); \
+ if (qemu_timer_pending(baum->cellCount_timer)) { \
+ qemu_del_timer(baum->cellCount_timer); \
+ baum_cellCount_timer_cb(baum); \
+ } \
+ return (cur - 2 - buf); \
+ } \
+ } \
+} while (0)
+
+ EAT(req);
+ switch (req) {
+ case BAUM_REQ_DisplayData:
+ {
+ uint8_t cells[baum->x * baum->y], c;
+ uint8_t text[baum->x * baum->y];
+ uint8_t zero[baum->x * baum->y];
+ int cursor = BRLAPI_CURSOR_OFF;
+ int i;
+
+ /* Allow 100ms to complete the DisplayData packet */
+ qemu_mod_timer(baum->cellCount_timer, qemu_get_clock(vm_clock) +
+ get_ticks_per_sec() / 10);
+ for (i = 0; i < baum->x * baum->y ; i++) {
+ EAT(c);
+ cells[i] = c;
+ if ((c & (BRLAPI_DOT7|BRLAPI_DOT8))
+ == (BRLAPI_DOT7|BRLAPI_DOT8)) {
+ cursor = i + 1;
+ c &= ~(BRLAPI_DOT7|BRLAPI_DOT8);
+ }
+ if (!(c = nabcc_translation[c]))
+ c = '?';
+ text[i] = c;
+ }
+ qemu_del_timer(baum->cellCount_timer);
+
+ memset(zero, 0, sizeof(zero));
+
+ brlapi_writeArguments_t wa = {
+ .displayNumber = BRLAPI_DISPLAY_DEFAULT,
+ .regionBegin = 1,
+ .regionSize = baum->x * baum->y,
+ .text = (char *)text,
+ .textSize = baum->x * baum->y,
+ .andMask = zero,
+ .orMask = cells,
+ .cursor = cursor,
+ .charset = (char *)"ISO-8859-1",
+ };
+
+ if (brlapi__write(baum->brlapi, &wa) == -1)
+ brlapi_perror("baum brlapi_write");
+ break;
+ }
+ case BAUM_REQ_SetMode:
+ {
+ uint8_t mode, setting;
+ DPRINTF("SetMode\n");
+ EAT(mode);
+ EAT(setting);
+ /* ignore */
+ break;
+ }
+ case BAUM_REQ_SetProtocol:
+ {
+ uint8_t protocol;
+ DPRINTF("SetProtocol\n");
+ EAT(protocol);
+ /* ignore */
+ break;
+ }
+ case BAUM_REQ_GetDeviceIdentity:
+ {
+ uint8_t identity[17] = { BAUM_RSP_DeviceIdentity,
+ 'B','a','u','m',' ','V','a','r','i','o' };
+ DPRINTF("GetDeviceIdentity\n");
+ identity[11] = '0' + baum->x / 10;
+ identity[12] = '0' + baum->x % 10;
+ baum_write_packet(baum, identity, sizeof(identity));
+ break;
+ }
+ case BAUM_REQ_GetVersionNumber:
+ {
+ uint8_t version[] = { BAUM_RSP_VersionNumber, 1 }; /* ? */
+ DPRINTF("GetVersionNumber\n");
+ baum_write_packet(baum, version, sizeof(version));
+ break;
+ }
+ case BAUM_REQ_GetSerialNumber:
+ {
+ uint8_t serial[] = { BAUM_RSP_SerialNumber,
+ '0','0','0','0','0','0','0','0' };
+ DPRINTF("GetSerialNumber\n");
+ baum_write_packet(baum, serial, sizeof(serial));
+ break;
+ }
+ case BAUM_REQ_GetKeys:
+ {
+ DPRINTF("Get%0#2x\n", req);
+ /* ignore */
+ break;
+ }
+ default:
+ DPRINTF("unrecognized request %0#2x\n", req);
+ do
+ if (!len--)
+ return 0;
+ while (*cur++ != ESC);
+ cur--;
+ break;
+ }
+ return cur - buf;
+}
+
+/* The other end is writing some data. Store it and try to interpret */
+static int baum_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ BaumDriverState *baum = chr->opaque;
+ int tocopy, cur, eaten, orig_len = len;
+
+ if (!len)
+ return 0;
+ if (!baum->brlapi)
+ return len;
+
+ while (len) {
+ /* Complete our buffer as much as possible */
+ tocopy = len;
+ if (tocopy > BUF_SIZE - baum->in_buf_used)
+ tocopy = BUF_SIZE - baum->in_buf_used;
+
+ memcpy(baum->in_buf + baum->in_buf_used, buf, tocopy);
+ baum->in_buf_used += tocopy;
+ buf += tocopy;
+ len -= tocopy;
+
+ /* Interpret it as much as possible */
+ cur = 0;
+ while (cur < baum->in_buf_used &&
+ (eaten = baum_eat_packet(baum, baum->in_buf + cur, baum->in_buf_used - cur)))
+ cur += eaten;
+
+ /* Shift the remainder */
+ if (cur) {
+ memmove(baum->in_buf, baum->in_buf + cur, baum->in_buf_used - cur);
+ baum->in_buf_used -= cur;
+ }
+
+ /* And continue if any data left */
+ }
+ return orig_len;
+}
+
+/* The other end sent us some event */
+static void baum_send_event(CharDriverState *chr, int event)
+{
+ BaumDriverState *baum = chr->opaque;
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ break;
+ case CHR_EVENT_OPENED:
+ /* Reset state */
+ baum->in_buf_used = 0;
+ break;
+ }
+}
+
+/* Send the key code to the other end */
+static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) {
+ uint8_t packet[] = { type, value };
+ DPRINTF("writing key %x %x\n", type, value);
+ baum_write_packet(baum, packet, sizeof(packet));
+}
+
+/* We got some data on the BrlAPI socket */
+static void baum_chr_read(void *opaque)
+{
+ BaumDriverState *baum = opaque;
+ brlapi_keyCode_t code;
+ int ret;
+ if (!baum->brlapi)
+ return;
+ while ((ret = brlapi__readKey(baum->brlapi, 0, &code)) == 1) {
+ DPRINTF("got key %"BRLAPI_PRIxKEYCODE"\n", code);
+ /* Emulate */
+ switch (code & BRLAPI_KEY_TYPE_MASK) {
+ case BRLAPI_KEY_TYPE_CMD:
+ switch (code & BRLAPI_KEY_CMD_BLK_MASK) {
+ case BRLAPI_KEY_CMD_ROUTE:
+ baum_send_key(baum, BAUM_RSP_RoutingKey, (code & BRLAPI_KEY_CMD_ARG_MASK)+1);
+ baum_send_key(baum, BAUM_RSP_RoutingKey, 0);
+ break;
+ case 0:
+ switch (code & BRLAPI_KEY_CMD_ARG_MASK) {
+ case BRLAPI_KEY_CMD_FWINLT:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_FWINRT:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR2);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_LNUP:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR1);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_LNDN:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TR3);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_TOP:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TR1);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_BOT:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL3|BAUM_TR3);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_TOP_LEFT:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_BOT_LEFT:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR3);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_HOME:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL2|BAUM_TR1|BAUM_TR3);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ case BRLAPI_KEY_CMD_PREFMENU:
+ baum_send_key(baum, BAUM_RSP_TopKeys, BAUM_TL1|BAUM_TL3|BAUM_TR1);
+ baum_send_key(baum, BAUM_RSP_TopKeys, 0);
+ break;
+ }
+ }
+ break;
+ case BRLAPI_KEY_TYPE_SYM:
+ break;
+ }
+ }
+ if (ret == -1 && (brlapi_errno != BRLAPI_ERROR_LIBCERR || errno != EINTR)) {
+ brlapi_perror("baum: brlapi_readKey");
+ brlapi__closeConnection(baum->brlapi);
+ qemu_free(baum->brlapi);
+ baum->brlapi = NULL;
+ }
+}
+
+static void baum_close(struct CharDriverState *chr)
+{
+ BaumDriverState *baum = chr->opaque;
+
+ qemu_free_timer(baum->cellCount_timer);
+ if (baum->brlapi) {
+ brlapi__closeConnection(baum->brlapi);
+ qemu_free(baum->brlapi);
+ }
+ qemu_free(baum);
+}
+
+CharDriverState *chr_baum_init(QemuOpts *opts)
+{
+ BaumDriverState *baum;
+ CharDriverState *chr;
+ brlapi_handle_t *handle;
+#ifdef CONFIG_SDL
+ SDL_SysWMinfo info;
+#endif
+ int tty;
+
+ baum = qemu_mallocz(sizeof(BaumDriverState));
+ baum->chr = chr = qemu_mallocz(sizeof(CharDriverState));
+
+ chr->opaque = baum;
+ chr->chr_write = baum_write;
+ chr->chr_send_event = baum_send_event;
+ chr->chr_accept_input = baum_accept_input;
+ chr->chr_close = baum_close;
+
+ handle = qemu_mallocz(brlapi_getHandleSize());
+ baum->brlapi = handle;
+
+ baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL);
+ if (baum->brlapi_fd == -1) {
+ brlapi_perror("baum_init: brlapi_openConnection");
+ goto fail_handle;
+ }
+
+ baum->cellCount_timer = qemu_new_timer(vm_clock, baum_cellCount_timer_cb, baum);
+
+ if (brlapi__getDisplaySize(handle, &baum->x, &baum->y) == -1) {
+ brlapi_perror("baum_init: brlapi_getDisplaySize");
+ goto fail;
+ }
+
+#ifdef CONFIG_SDL
+ memset(&info, 0, sizeof(info));
+ SDL_VERSION(&info.version);
+ if (SDL_GetWMInfo(&info))
+ tty = info.info.x11.wmwindow;
+ else
+#endif
+ tty = BRLAPI_TTY_DEFAULT;
+
+ if (brlapi__enterTtyMode(handle, tty, NULL) == -1) {
+ brlapi_perror("baum_init: brlapi_enterTtyMode");
+ goto fail;
+ }
+
+ qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum);
+
+ qemu_chr_generic_open(chr);
+
+ return chr;
+
+fail:
+ qemu_free_timer(baum->cellCount_timer);
+ brlapi__closeConnection(handle);
+fail_handle:
+ qemu_free(handle);
+ qemu_free(chr);
+ qemu_free(baum);
+ return NULL;
+}
diff --git a/hw/baum.h b/hw/baum.h
new file mode 100644
index 0000000..8af710f
--- /dev/null
+++ b/hw/baum.h
@@ -0,0 +1,26 @@
+/*
+ * QEMU Baum
+ *
+ * Copyright (c) 2008 Samuel Thibault
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* char device */
+CharDriverState *chr_baum_init(QemuOpts *opts);
diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c
new file mode 100644
index 0000000..4ee99a1
--- /dev/null
+++ b/hw/bitbang_i2c.c
@@ -0,0 +1,228 @@
+/*
+ * Bit-Bang i2c emulation extracted from
+ * Marvell MV88W8618 / Freecom MusicPal emulation.
+ *
+ * Copyright (c) 2008 Jan Kiszka
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "bitbang_i2c.h"
+#include "sysbus.h"
+
+//#define DEBUG_BITBANG_I2C
+
+#ifdef DEBUG_BITBANG_I2C
+#define DPRINTF(fmt, ...) \
+do { printf("bitbang_i2c: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+typedef enum bitbang_i2c_state {
+ STOPPED = 0,
+ SENDING_BIT7,
+ SENDING_BIT6,
+ SENDING_BIT5,
+ SENDING_BIT4,
+ SENDING_BIT3,
+ SENDING_BIT2,
+ SENDING_BIT1,
+ SENDING_BIT0,
+ WAITING_FOR_ACK,
+ RECEIVING_BIT7,
+ RECEIVING_BIT6,
+ RECEIVING_BIT5,
+ RECEIVING_BIT4,
+ RECEIVING_BIT3,
+ RECEIVING_BIT2,
+ RECEIVING_BIT1,
+ RECEIVING_BIT0,
+ SENDING_ACK
+} bitbang_i2c_state;
+
+struct bitbang_i2c_interface {
+ i2c_bus *bus;
+ bitbang_i2c_state state;
+ int last_data;
+ int last_clock;
+ int device_out;
+ uint8_t buffer;
+ int current_addr;
+};
+
+static void bitbang_i2c_enter_stop(bitbang_i2c_interface *i2c)
+{
+ DPRINTF("STOP\n");
+ if (i2c->current_addr >= 0)
+ i2c_end_transfer(i2c->bus);
+ i2c->current_addr = -1;
+ i2c->state = STOPPED;
+}
+
+/* Set device data pin. */
+static int bitbang_i2c_ret(bitbang_i2c_interface *i2c, int level)
+{
+ i2c->device_out = level;
+ //DPRINTF("%d %d %d\n", i2c->last_clock, i2c->last_data, i2c->device_out);
+ return level & i2c->last_data;
+}
+
+/* Leave device data pin unodified. */
+static int bitbang_i2c_nop(bitbang_i2c_interface *i2c)
+{
+ return bitbang_i2c_ret(i2c, i2c->device_out);
+}
+
+/* Returns data line level. */
+int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level)
+{
+ int data;
+
+ if (level != 0 && level != 1) {
+ abort();
+ }
+
+ if (line == BITBANG_I2C_SDA) {
+ if (level == i2c->last_data) {
+ return bitbang_i2c_nop(i2c);
+ }
+ i2c->last_data = level;
+ if (i2c->last_clock == 0) {
+ return bitbang_i2c_nop(i2c);
+ }
+ if (level == 0) {
+ DPRINTF("START\n");
+ /* START condition. */
+ i2c->state = SENDING_BIT7;
+ i2c->current_addr = -1;
+ } else {
+ /* STOP condition. */
+ bitbang_i2c_enter_stop(i2c);
+ }
+ return bitbang_i2c_ret(i2c, 1);
+ }
+
+ data = i2c->last_data;
+ if (i2c->last_clock == level) {
+ return bitbang_i2c_nop(i2c);
+ }
+ i2c->last_clock = level;
+ if (level == 0) {
+ /* State is set/read at the start of the clock pulse.
+ release the data line at the end. */
+ return bitbang_i2c_ret(i2c, 1);
+ }
+ switch (i2c->state) {
+ case STOPPED:
+ return bitbang_i2c_ret(i2c, 1);
+
+ case SENDING_BIT7 ... SENDING_BIT0:
+ i2c->buffer = (i2c->buffer << 1) | data;
+ /* will end up in WAITING_FOR_ACK */
+ i2c->state++;
+ return bitbang_i2c_ret(i2c, 1);
+
+ case WAITING_FOR_ACK:
+ if (i2c->current_addr < 0) {
+ i2c->current_addr = i2c->buffer;
+ DPRINTF("Address 0x%02x\n", i2c->current_addr);
+ i2c_start_transfer(i2c->bus, i2c->current_addr >> 1,
+ i2c->current_addr & 1);
+ } else {
+ DPRINTF("Sent 0x%02x\n", i2c->buffer);
+ i2c_send(i2c->bus, i2c->buffer);
+ }
+ if (i2c->current_addr & 1) {
+ i2c->state = RECEIVING_BIT7;
+ } else {
+ i2c->state = SENDING_BIT7;
+ }
+ return bitbang_i2c_ret(i2c, 0);
+
+ case RECEIVING_BIT7:
+ i2c->buffer = i2c_recv(i2c->bus);
+ DPRINTF("RX byte 0x%02x\n", i2c->buffer);
+ /* Fall through... */
+ case RECEIVING_BIT6 ... RECEIVING_BIT0:
+ data = i2c->buffer >> 7;
+ /* will end up in SENDING_ACK */
+ i2c->state++;
+ i2c->buffer <<= 1;
+ return bitbang_i2c_ret(i2c, data);
+
+ case SENDING_ACK:
+ i2c->state = RECEIVING_BIT7;
+ if (data != 0) {
+ DPRINTF("NACKED\n");
+ i2c_nack(i2c->bus);
+ } else {
+ DPRINTF("ACKED\n");
+ }
+ return bitbang_i2c_ret(i2c, 1);
+ }
+ abort();
+}
+
+bitbang_i2c_interface *bitbang_i2c_init(i2c_bus *bus)
+{
+ bitbang_i2c_interface *s;
+
+ s = qemu_mallocz(sizeof(bitbang_i2c_interface));
+
+ s->bus = bus;
+ s->last_data = 1;
+ s->last_clock = 1;
+ s->device_out = 1;
+
+ return s;
+}
+
+/* GPIO interface. */
+typedef struct {
+ SysBusDevice busdev;
+ bitbang_i2c_interface *bitbang;
+ int last_level;
+ qemu_irq out;
+} GPIOI2CState;
+
+static void bitbang_i2c_gpio_set(void *opaque, int irq, int level)
+{
+ GPIOI2CState *s = opaque;
+
+ level = bitbang_i2c_set(s->bitbang, irq, level);
+ if (level != s->last_level) {
+ s->last_level = level;
+ qemu_set_irq(s->out, level);
+ }
+}
+
+static int gpio_i2c_init(SysBusDevice *dev)
+{
+ GPIOI2CState *s = FROM_SYSBUS(GPIOI2CState, dev);
+ i2c_bus *bus;
+
+ sysbus_init_mmio(dev, 0x0, 0);
+
+ bus = i2c_init_bus(&dev->qdev, "i2c");
+ s->bitbang = bitbang_i2c_init(bus);
+
+ qdev_init_gpio_in(&dev->qdev, bitbang_i2c_gpio_set, 2);
+ qdev_init_gpio_out(&dev->qdev, &s->out, 1);
+
+ return 0;
+}
+
+static SysBusDeviceInfo gpio_i2c_info = {
+ .init = gpio_i2c_init,
+ .qdev.name = "gpio_i2c",
+ .qdev.desc = "Virtual GPIO to I2C bridge",
+ .qdev.size = sizeof(GPIOI2CState),
+};
+
+static void bitbang_i2c_register(void)
+{
+ sysbus_register_withprop(&gpio_i2c_info);
+}
+
+device_init(bitbang_i2c_register)
diff --git a/hw/bitbang_i2c.h b/hw/bitbang_i2c.h
new file mode 100644
index 0000000..519d2dc
--- /dev/null
+++ b/hw/bitbang_i2c.h
@@ -0,0 +1,14 @@
+#ifndef BITBANG_I2C_H
+#define BITBANG_I2C_H
+
+#include "i2c.h"
+
+typedef struct bitbang_i2c_interface bitbang_i2c_interface;
+
+#define BITBANG_I2C_SDA 0
+#define BITBANG_I2C_SCL 1
+
+bitbang_i2c_interface *bitbang_i2c_init(i2c_bus *bus);
+int bitbang_i2c_set(bitbang_i2c_interface *i2c, int line, int level);
+
+#endif
diff --git a/hw/blizzard.c b/hw/blizzard.c
new file mode 100644
index 0000000..5f329ad
--- /dev/null
+++ b/hw/blizzard.c
@@ -0,0 +1,998 @@
+/*
+ * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "console.h"
+#include "devices.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+
+typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int);
+
+typedef struct {
+ uint8_t reg;
+ uint32_t addr;
+ int swallow;
+
+ int pll;
+ int pll_range;
+ int pll_ctrl;
+ uint8_t pll_mode;
+ uint8_t clksel;
+ int memenable;
+ int memrefresh;
+ uint8_t timing[3];
+ int priority;
+
+ uint8_t lcd_config;
+ int x;
+ int y;
+ int skipx;
+ int skipy;
+ uint8_t hndp;
+ uint8_t vndp;
+ uint8_t hsync;
+ uint8_t vsync;
+ uint8_t pclk;
+ uint8_t u;
+ uint8_t v;
+ uint8_t yrc[2];
+ int ix[2];
+ int iy[2];
+ int ox[2];
+ int oy[2];
+
+ int enable;
+ int blank;
+ int bpp;
+ int invalidate;
+ int mx[2];
+ int my[2];
+ uint8_t mode;
+ uint8_t effect;
+ uint8_t iformat;
+ uint8_t source;
+ DisplayState *state;
+ blizzard_fn_t *line_fn_tab[2];
+ void *fb;
+
+ uint8_t hssi_config[3];
+ uint8_t tv_config;
+ uint8_t tv_timing[4];
+ uint8_t vbi;
+ uint8_t tv_x;
+ uint8_t tv_y;
+ uint8_t tv_test;
+ uint8_t tv_filter_config;
+ uint8_t tv_filter_idx;
+ uint8_t tv_filter_coeff[0x20];
+ uint8_t border_r;
+ uint8_t border_g;
+ uint8_t border_b;
+ uint8_t gamma_config;
+ uint8_t gamma_idx;
+ uint8_t gamma_lut[0x100];
+ uint8_t matrix_ena;
+ uint8_t matrix_coeff[0x12];
+ uint8_t matrix_r;
+ uint8_t matrix_g;
+ uint8_t matrix_b;
+ uint8_t pm;
+ uint8_t status;
+ uint8_t rgbgpio_dir;
+ uint8_t rgbgpio;
+ uint8_t gpio_dir;
+ uint8_t gpio;
+ uint8_t gpio_edge[2];
+ uint8_t gpio_irq;
+ uint8_t gpio_pdown;
+
+ struct {
+ int x;
+ int y;
+ int dx;
+ int dy;
+ int len;
+ int buflen;
+ void *buf;
+ void *data;
+ uint16_t *ptr;
+ int angle;
+ int pitch;
+ blizzard_fn_t line_fn;
+ } data;
+} BlizzardState;
+
+/* Bytes(!) per pixel */
+static const int blizzard_iformat_bpp[0x10] = {
+ 0,
+ 2, /* RGB 5:6:5*/
+ 3, /* RGB 6:6:6 mode 1 */
+ 3, /* RGB 8:8:8 mode 1 */
+ 0, 0,
+ 4, /* RGB 6:6:6 mode 2 */
+ 4, /* RGB 8:8:8 mode 2 */
+ 0, /* YUV 4:2:2 */
+ 0, /* YUV 4:2:0 */
+ 0, 0, 0, 0, 0, 0,
+};
+
+static inline void blizzard_rgb2yuv(int r, int g, int b,
+ int *y, int *u, int *v)
+{
+ *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13);
+ *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13);
+ *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13);
+}
+
+static void blizzard_window(BlizzardState *s)
+{
+ uint8_t *src, *dst;
+ int bypp[2];
+ int bypl[3];
+ int y;
+ blizzard_fn_t fn = s->data.line_fn;
+
+ if (!fn)
+ return;
+ if (s->mx[0] > s->data.x)
+ s->mx[0] = s->data.x;
+ if (s->my[0] > s->data.y)
+ s->my[0] = s->data.y;
+ if (s->mx[1] < s->data.x + s->data.dx)
+ s->mx[1] = s->data.x + s->data.dx;
+ if (s->my[1] < s->data.y + s->data.dy)
+ s->my[1] = s->data.y + s->data.dy;
+
+ bypp[0] = s->bpp;
+ bypp[1] = (ds_get_bits_per_pixel(s->state) + 7) >> 3;
+ bypl[0] = bypp[0] * s->data.pitch;
+ bypl[1] = bypp[1] * s->x;
+ bypl[2] = bypp[0] * s->data.dx;
+
+ src = s->data.data;
+ dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x;
+ for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1])
+ fn(dst, src, bypl[2]);
+}
+
+static int blizzard_transfer_setup(BlizzardState *s)
+{
+ if (s->source > 3 || !s->bpp ||
+ s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0])
+ return 0;
+
+ s->data.angle = s->effect & 3;
+ s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat];
+ s->data.x = s->ix[0];
+ s->data.y = s->iy[0];
+ s->data.dx = s->ix[1] - s->ix[0] + 1;
+ s->data.dy = s->iy[1] - s->iy[0] + 1;
+ s->data.len = s->bpp * s->data.dx * s->data.dy;
+ s->data.pitch = s->data.dx;
+ if (s->data.len > s->data.buflen) {
+ s->data.buf = qemu_realloc(s->data.buf, s->data.len);
+ s->data.buflen = s->data.len;
+ }
+ s->data.ptr = s->data.buf;
+ s->data.data = s->data.buf;
+ s->data.len /= 2;
+ return 1;
+}
+
+static void blizzard_reset(BlizzardState *s)
+{
+ s->reg = 0;
+ s->swallow = 0;
+
+ s->pll = 9;
+ s->pll_range = 1;
+ s->pll_ctrl = 0x14;
+ s->pll_mode = 0x32;
+ s->clksel = 0x00;
+ s->memenable = 0;
+ s->memrefresh = 0x25c;
+ s->timing[0] = 0x3f;
+ s->timing[1] = 0x13;
+ s->timing[2] = 0x21;
+ s->priority = 0;
+
+ s->lcd_config = 0x74;
+ s->x = 8;
+ s->y = 1;
+ s->skipx = 0;
+ s->skipy = 0;
+ s->hndp = 3;
+ s->vndp = 2;
+ s->hsync = 1;
+ s->vsync = 1;
+ s->pclk = 0x80;
+
+ s->ix[0] = 0;
+ s->ix[1] = 0;
+ s->iy[0] = 0;
+ s->iy[1] = 0;
+ s->ox[0] = 0;
+ s->ox[1] = 0;
+ s->oy[0] = 0;
+ s->oy[1] = 0;
+
+ s->yrc[0] = 0x00;
+ s->yrc[1] = 0x30;
+ s->u = 0;
+ s->v = 0;
+
+ s->iformat = 3;
+ s->source = 0;
+ s->bpp = blizzard_iformat_bpp[s->iformat];
+
+ s->hssi_config[0] = 0x00;
+ s->hssi_config[1] = 0x00;
+ s->hssi_config[2] = 0x01;
+ s->tv_config = 0x00;
+ s->tv_timing[0] = 0x00;
+ s->tv_timing[1] = 0x00;
+ s->tv_timing[2] = 0x00;
+ s->tv_timing[3] = 0x00;
+ s->vbi = 0x10;
+ s->tv_x = 0x14;
+ s->tv_y = 0x03;
+ s->tv_test = 0x00;
+ s->tv_filter_config = 0x80;
+ s->tv_filter_idx = 0x00;
+ s->border_r = 0x10;
+ s->border_g = 0x80;
+ s->border_b = 0x80;
+ s->gamma_config = 0x00;
+ s->gamma_idx = 0x00;
+ s->matrix_ena = 0x00;
+ memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff));
+ s->matrix_r = 0x00;
+ s->matrix_g = 0x00;
+ s->matrix_b = 0x00;
+ s->pm = 0x02;
+ s->status = 0x00;
+ s->rgbgpio_dir = 0x00;
+ s->gpio_dir = 0x00;
+ s->gpio_edge[0] = 0x00;
+ s->gpio_edge[1] = 0x00;
+ s->gpio_irq = 0x00;
+ s->gpio_pdown = 0xff;
+}
+
+static inline void blizzard_invalidate_display(void *opaque) {
+ BlizzardState *s = (BlizzardState *) opaque;
+
+ s->invalidate = 1;
+}
+
+static uint16_t blizzard_reg_read(void *opaque, uint8_t reg)
+{
+ BlizzardState *s = (BlizzardState *) opaque;
+
+ switch (reg) {
+ case 0x00: /* Revision Code */
+ return 0xa5;
+
+ case 0x02: /* Configuration Readback */
+ return 0x83; /* Macrovision OK, CNF[2:0] = 3 */
+
+ case 0x04: /* PLL M-Divider */
+ return (s->pll - 1) | (1 << 7);
+ case 0x06: /* PLL Lock Range Control */
+ return s->pll_range;
+ case 0x08: /* PLL Lock Synthesis Control 0 */
+ return s->pll_ctrl & 0xff;
+ case 0x0a: /* PLL Lock Synthesis Control 1 */
+ return s->pll_ctrl >> 8;
+ case 0x0c: /* PLL Mode Control 0 */
+ return s->pll_mode;
+
+ case 0x0e: /* Clock-Source Select */
+ return s->clksel;
+
+ case 0x10: /* Memory Controller Activate */
+ case 0x14: /* Memory Controller Bank 0 Status Flag */
+ return s->memenable;
+
+ case 0x18: /* Auto-Refresh Interval Setting 0 */
+ return s->memrefresh & 0xff;
+ case 0x1a: /* Auto-Refresh Interval Setting 1 */
+ return s->memrefresh >> 8;
+
+ case 0x1c: /* Power-On Sequence Timing Control */
+ return s->timing[0];
+ case 0x1e: /* Timing Control 0 */
+ return s->timing[1];
+ case 0x20: /* Timing Control 1 */
+ return s->timing[2];
+
+ case 0x24: /* Arbitration Priority Control */
+ return s->priority;
+
+ case 0x28: /* LCD Panel Configuration */
+ return s->lcd_config;
+
+ case 0x2a: /* LCD Horizontal Display Width */
+ return s->x >> 3;
+ case 0x2c: /* LCD Horizontal Non-display Period */
+ return s->hndp;
+ case 0x2e: /* LCD Vertical Display Height 0 */
+ return s->y & 0xff;
+ case 0x30: /* LCD Vertical Display Height 1 */
+ return s->y >> 8;
+ case 0x32: /* LCD Vertical Non-display Period */
+ return s->vndp;
+ case 0x34: /* LCD HS Pulse-width */
+ return s->hsync;
+ case 0x36: /* LCd HS Pulse Start Position */
+ return s->skipx >> 3;
+ case 0x38: /* LCD VS Pulse-width */
+ return s->vsync;
+ case 0x3a: /* LCD VS Pulse Start Position */
+ return s->skipy;
+
+ case 0x3c: /* PCLK Polarity */
+ return s->pclk;
+
+ case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */
+ return s->hssi_config[0];
+ case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */
+ return s->hssi_config[1];
+ case 0x42: /* High-speed Serial Interface Tx Mode */
+ return s->hssi_config[2];
+ case 0x44: /* TV Display Configuration */
+ return s->tv_config;
+ case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */
+ return s->tv_timing[(reg - 0x46) >> 1];
+ case 0x4e: /* VBI: Closed Caption / XDS Control / Status */
+ return s->vbi;
+ case 0x50: /* TV Horizontal Start Position */
+ return s->tv_x;
+ case 0x52: /* TV Vertical Start Position */
+ return s->tv_y;
+ case 0x54: /* TV Test Pattern Setting */
+ return s->tv_test;
+ case 0x56: /* TV Filter Setting */
+ return s->tv_filter_config;
+ case 0x58: /* TV Filter Coefficient Index */
+ return s->tv_filter_idx;
+ case 0x5a: /* TV Filter Coefficient Data */
+ if (s->tv_filter_idx < 0x20)
+ return s->tv_filter_coeff[s->tv_filter_idx ++];
+ return 0;
+
+ case 0x60: /* Input YUV/RGB Translate Mode 0 */
+ return s->yrc[0];
+ case 0x62: /* Input YUV/RGB Translate Mode 1 */
+ return s->yrc[1];
+ case 0x64: /* U Data Fix */
+ return s->u;
+ case 0x66: /* V Data Fix */
+ return s->v;
+
+ case 0x68: /* Display Mode */
+ return s->mode;
+
+ case 0x6a: /* Special Effects */
+ return s->effect;
+
+ case 0x6c: /* Input Window X Start Position 0 */
+ return s->ix[0] & 0xff;
+ case 0x6e: /* Input Window X Start Position 1 */
+ return s->ix[0] >> 3;
+ case 0x70: /* Input Window Y Start Position 0 */
+ return s->ix[0] & 0xff;
+ case 0x72: /* Input Window Y Start Position 1 */
+ return s->ix[0] >> 3;
+ case 0x74: /* Input Window X End Position 0 */
+ return s->ix[1] & 0xff;
+ case 0x76: /* Input Window X End Position 1 */
+ return s->ix[1] >> 3;
+ case 0x78: /* Input Window Y End Position 0 */
+ return s->ix[1] & 0xff;
+ case 0x7a: /* Input Window Y End Position 1 */
+ return s->ix[1] >> 3;
+ case 0x7c: /* Output Window X Start Position 0 */
+ return s->ox[0] & 0xff;
+ case 0x7e: /* Output Window X Start Position 1 */
+ return s->ox[0] >> 3;
+ case 0x80: /* Output Window Y Start Position 0 */
+ return s->oy[0] & 0xff;
+ case 0x82: /* Output Window Y Start Position 1 */
+ return s->oy[0] >> 3;
+ case 0x84: /* Output Window X End Position 0 */
+ return s->ox[1] & 0xff;
+ case 0x86: /* Output Window X End Position 1 */
+ return s->ox[1] >> 3;
+ case 0x88: /* Output Window Y End Position 0 */
+ return s->oy[1] & 0xff;
+ case 0x8a: /* Output Window Y End Position 1 */
+ return s->oy[1] >> 3;
+
+ case 0x8c: /* Input Data Format */
+ return s->iformat;
+ case 0x8e: /* Data Source Select */
+ return s->source;
+ case 0x90: /* Display Memory Data Port */
+ return 0;
+
+ case 0xa8: /* Border Color 0 */
+ return s->border_r;
+ case 0xaa: /* Border Color 1 */
+ return s->border_g;
+ case 0xac: /* Border Color 2 */
+ return s->border_b;
+
+ case 0xb4: /* Gamma Correction Enable */
+ return s->gamma_config;
+ case 0xb6: /* Gamma Correction Table Index */
+ return s->gamma_idx;
+ case 0xb8: /* Gamma Correction Table Data */
+ return s->gamma_lut[s->gamma_idx ++];
+
+ case 0xba: /* 3x3 Matrix Enable */
+ return s->matrix_ena;
+ case 0xbc ... 0xde: /* Coefficient Registers */
+ return s->matrix_coeff[(reg - 0xbc) >> 1];
+ case 0xe0: /* 3x3 Matrix Red Offset */
+ return s->matrix_r;
+ case 0xe2: /* 3x3 Matrix Green Offset */
+ return s->matrix_g;
+ case 0xe4: /* 3x3 Matrix Blue Offset */
+ return s->matrix_b;
+
+ case 0xe6: /* Power-save */
+ return s->pm;
+ case 0xe8: /* Non-display Period Control / Status */
+ return s->status | (1 << 5);
+ case 0xea: /* RGB Interface Control */
+ return s->rgbgpio_dir;
+ case 0xec: /* RGB Interface Status */
+ return s->rgbgpio;
+ case 0xee: /* General-purpose IO Pins Configuration */
+ return s->gpio_dir;
+ case 0xf0: /* General-purpose IO Pins Status / Control */
+ return s->gpio;
+ case 0xf2: /* GPIO Positive Edge Interrupt Trigger */
+ return s->gpio_edge[0];
+ case 0xf4: /* GPIO Negative Edge Interrupt Trigger */
+ return s->gpio_edge[1];
+ case 0xf6: /* GPIO Interrupt Status */
+ return s->gpio_irq;
+ case 0xf8: /* GPIO Pull-down Control */
+ return s->gpio_pdown;
+
+ default:
+ fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg);
+ return 0;
+ }
+}
+
+static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value)
+{
+ BlizzardState *s = (BlizzardState *) opaque;
+
+ switch (reg) {
+ case 0x04: /* PLL M-Divider */
+ s->pll = (value & 0x3f) + 1;
+ break;
+ case 0x06: /* PLL Lock Range Control */
+ s->pll_range = value & 3;
+ break;
+ case 0x08: /* PLL Lock Synthesis Control 0 */
+ s->pll_ctrl &= 0xf00;
+ s->pll_ctrl |= (value << 0) & 0x0ff;
+ break;
+ case 0x0a: /* PLL Lock Synthesis Control 1 */
+ s->pll_ctrl &= 0x0ff;
+ s->pll_ctrl |= (value << 8) & 0xf00;
+ break;
+ case 0x0c: /* PLL Mode Control 0 */
+ s->pll_mode = value & 0x77;
+ if ((value & 3) == 0 || (value & 3) == 3)
+ fprintf(stderr, "%s: wrong PLL Control bits (%i)\n",
+ __FUNCTION__, value & 3);
+ break;
+
+ case 0x0e: /* Clock-Source Select */
+ s->clksel = value & 0xff;
+ break;
+
+ case 0x10: /* Memory Controller Activate */
+ s->memenable = value & 1;
+ break;
+ case 0x14: /* Memory Controller Bank 0 Status Flag */
+ break;
+
+ case 0x18: /* Auto-Refresh Interval Setting 0 */
+ s->memrefresh &= 0xf00;
+ s->memrefresh |= (value << 0) & 0x0ff;
+ break;
+ case 0x1a: /* Auto-Refresh Interval Setting 1 */
+ s->memrefresh &= 0x0ff;
+ s->memrefresh |= (value << 8) & 0xf00;
+ break;
+
+ case 0x1c: /* Power-On Sequence Timing Control */
+ s->timing[0] = value & 0x7f;
+ break;
+ case 0x1e: /* Timing Control 0 */
+ s->timing[1] = value & 0x17;
+ break;
+ case 0x20: /* Timing Control 1 */
+ s->timing[2] = value & 0x35;
+ break;
+
+ case 0x24: /* Arbitration Priority Control */
+ s->priority = value & 1;
+ break;
+
+ case 0x28: /* LCD Panel Configuration */
+ s->lcd_config = value & 0xff;
+ if (value & (1 << 7))
+ fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__);
+ break;
+
+ case 0x2a: /* LCD Horizontal Display Width */
+ s->x = value << 3;
+ break;
+ case 0x2c: /* LCD Horizontal Non-display Period */
+ s->hndp = value & 0xff;
+ break;
+ case 0x2e: /* LCD Vertical Display Height 0 */
+ s->y &= 0x300;
+ s->y |= (value << 0) & 0x0ff;
+ break;
+ case 0x30: /* LCD Vertical Display Height 1 */
+ s->y &= 0x0ff;
+ s->y |= (value << 8) & 0x300;
+ break;
+ case 0x32: /* LCD Vertical Non-display Period */
+ s->vndp = value & 0xff;
+ break;
+ case 0x34: /* LCD HS Pulse-width */
+ s->hsync = value & 0xff;
+ break;
+ case 0x36: /* LCD HS Pulse Start Position */
+ s->skipx = value & 0xff;
+ break;
+ case 0x38: /* LCD VS Pulse-width */
+ s->vsync = value & 0xbf;
+ break;
+ case 0x3a: /* LCD VS Pulse Start Position */
+ s->skipy = value & 0xff;
+ break;
+
+ case 0x3c: /* PCLK Polarity */
+ s->pclk = value & 0x82;
+ /* Affects calculation of s->hndp, s->hsync and s->skipx. */
+ break;
+
+ case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */
+ s->hssi_config[0] = value;
+ break;
+ case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */
+ s->hssi_config[1] = value;
+ if (((value >> 4) & 3) == 3)
+ fprintf(stderr, "%s: Illegal active-data-links value\n",
+ __FUNCTION__);
+ break;
+ case 0x42: /* High-speed Serial Interface Tx Mode */
+ s->hssi_config[2] = value & 0xbd;
+ break;
+
+ case 0x44: /* TV Display Configuration */
+ s->tv_config = value & 0xfe;
+ break;
+ case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */
+ s->tv_timing[(reg - 0x46) >> 1] = value;
+ break;
+ case 0x4e: /* VBI: Closed Caption / XDS Control / Status */
+ s->vbi = value;
+ break;
+ case 0x50: /* TV Horizontal Start Position */
+ s->tv_x = value;
+ break;
+ case 0x52: /* TV Vertical Start Position */
+ s->tv_y = value & 0x7f;
+ break;
+ case 0x54: /* TV Test Pattern Setting */
+ s->tv_test = value;
+ break;
+ case 0x56: /* TV Filter Setting */
+ s->tv_filter_config = value & 0xbf;
+ break;
+ case 0x58: /* TV Filter Coefficient Index */
+ s->tv_filter_idx = value & 0x1f;
+ break;
+ case 0x5a: /* TV Filter Coefficient Data */
+ if (s->tv_filter_idx < 0x20)
+ s->tv_filter_coeff[s->tv_filter_idx ++] = value;
+ break;
+
+ case 0x60: /* Input YUV/RGB Translate Mode 0 */
+ s->yrc[0] = value & 0xb0;
+ break;
+ case 0x62: /* Input YUV/RGB Translate Mode 1 */
+ s->yrc[1] = value & 0x30;
+ break;
+ case 0x64: /* U Data Fix */
+ s->u = value & 0xff;
+ break;
+ case 0x66: /* V Data Fix */
+ s->v = value & 0xff;
+ break;
+
+ case 0x68: /* Display Mode */
+ if ((s->mode ^ value) & 3)
+ s->invalidate = 1;
+ s->mode = value & 0xb7;
+ s->enable = value & 1;
+ s->blank = (value >> 1) & 1;
+ if (value & (1 << 4))
+ fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__);
+ break;
+
+ case 0x6a: /* Special Effects */
+ s->effect = value & 0xfb;
+ break;
+
+ case 0x6c: /* Input Window X Start Position 0 */
+ s->ix[0] &= 0x300;
+ s->ix[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x6e: /* Input Window X Start Position 1 */
+ s->ix[0] &= 0x0ff;
+ s->ix[0] |= (value << 8) & 0x300;
+ break;
+ case 0x70: /* Input Window Y Start Position 0 */
+ s->iy[0] &= 0x300;
+ s->iy[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x72: /* Input Window Y Start Position 1 */
+ s->iy[0] &= 0x0ff;
+ s->iy[0] |= (value << 8) & 0x300;
+ break;
+ case 0x74: /* Input Window X End Position 0 */
+ s->ix[1] &= 0x300;
+ s->ix[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x76: /* Input Window X End Position 1 */
+ s->ix[1] &= 0x0ff;
+ s->ix[1] |= (value << 8) & 0x300;
+ break;
+ case 0x78: /* Input Window Y End Position 0 */
+ s->iy[1] &= 0x300;
+ s->iy[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x7a: /* Input Window Y End Position 1 */
+ s->iy[1] &= 0x0ff;
+ s->iy[1] |= (value << 8) & 0x300;
+ break;
+ case 0x7c: /* Output Window X Start Position 0 */
+ s->ox[0] &= 0x300;
+ s->ox[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x7e: /* Output Window X Start Position 1 */
+ s->ox[0] &= 0x0ff;
+ s->ox[0] |= (value << 8) & 0x300;
+ break;
+ case 0x80: /* Output Window Y Start Position 0 */
+ s->oy[0] &= 0x300;
+ s->oy[0] |= (value << 0) & 0x0ff;
+ break;
+ case 0x82: /* Output Window Y Start Position 1 */
+ s->oy[0] &= 0x0ff;
+ s->oy[0] |= (value << 8) & 0x300;
+ break;
+ case 0x84: /* Output Window X End Position 0 */
+ s->ox[1] &= 0x300;
+ s->ox[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x86: /* Output Window X End Position 1 */
+ s->ox[1] &= 0x0ff;
+ s->ox[1] |= (value << 8) & 0x300;
+ break;
+ case 0x88: /* Output Window Y End Position 0 */
+ s->oy[1] &= 0x300;
+ s->oy[1] |= (value << 0) & 0x0ff;
+ break;
+ case 0x8a: /* Output Window Y End Position 1 */
+ s->oy[1] &= 0x0ff;
+ s->oy[1] |= (value << 8) & 0x300;
+ break;
+
+ case 0x8c: /* Input Data Format */
+ s->iformat = value & 0xf;
+ s->bpp = blizzard_iformat_bpp[s->iformat];
+ if (!s->bpp)
+ fprintf(stderr, "%s: Illegal or unsupported input format %x\n",
+ __FUNCTION__, s->iformat);
+ break;
+ case 0x8e: /* Data Source Select */
+ s->source = value & 7;
+ /* Currently all windows will be "destructive overlays". */
+ if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] ||
+ s->iy[0] != s->oy[0] ||
+ s->ix[1] != s->ox[1] ||
+ s->iy[1] != s->oy[1])) ||
+ !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) &
+ (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1))
+ fprintf(stderr, "%s: Illegal input/output window positions\n",
+ __FUNCTION__);
+
+ blizzard_transfer_setup(s);
+ break;
+
+ case 0x90: /* Display Memory Data Port */
+ if (!s->data.len && !blizzard_transfer_setup(s))
+ break;
+
+ *s->data.ptr ++ = value;
+ if (-- s->data.len == 0)
+ blizzard_window(s);
+ break;
+
+ case 0xa8: /* Border Color 0 */
+ s->border_r = value;
+ break;
+ case 0xaa: /* Border Color 1 */
+ s->border_g = value;
+ break;
+ case 0xac: /* Border Color 2 */
+ s->border_b = value;
+ break;
+
+ case 0xb4: /* Gamma Correction Enable */
+ s->gamma_config = value & 0x87;
+ break;
+ case 0xb6: /* Gamma Correction Table Index */
+ s->gamma_idx = value;
+ break;
+ case 0xb8: /* Gamma Correction Table Data */
+ s->gamma_lut[s->gamma_idx ++] = value;
+ break;
+
+ case 0xba: /* 3x3 Matrix Enable */
+ s->matrix_ena = value & 1;
+ break;
+ case 0xbc ... 0xde: /* Coefficient Registers */
+ s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff);
+ break;
+ case 0xe0: /* 3x3 Matrix Red Offset */
+ s->matrix_r = value;
+ break;
+ case 0xe2: /* 3x3 Matrix Green Offset */
+ s->matrix_g = value;
+ break;
+ case 0xe4: /* 3x3 Matrix Blue Offset */
+ s->matrix_b = value;
+ break;
+
+ case 0xe6: /* Power-save */
+ s->pm = value & 0x83;
+ if (value & s->mode & 1)
+ fprintf(stderr, "%s: The display must be disabled before entering "
+ "Standby Mode\n", __FUNCTION__);
+ break;
+ case 0xe8: /* Non-display Period Control / Status */
+ s->status = value & 0x1b;
+ break;
+ case 0xea: /* RGB Interface Control */
+ s->rgbgpio_dir = value & 0x8f;
+ break;
+ case 0xec: /* RGB Interface Status */
+ s->rgbgpio = value & 0xcf;
+ break;
+ case 0xee: /* General-purpose IO Pins Configuration */
+ s->gpio_dir = value;
+ break;
+ case 0xf0: /* General-purpose IO Pins Status / Control */
+ s->gpio = value;
+ break;
+ case 0xf2: /* GPIO Positive Edge Interrupt Trigger */
+ s->gpio_edge[0] = value;
+ break;
+ case 0xf4: /* GPIO Negative Edge Interrupt Trigger */
+ s->gpio_edge[1] = value;
+ break;
+ case 0xf6: /* GPIO Interrupt Status */
+ s->gpio_irq &= value;
+ break;
+ case 0xf8: /* GPIO Pull-down Control */
+ s->gpio_pdown = value;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg);
+ break;
+ }
+}
+
+uint16_t s1d13745_read(void *opaque, int dc)
+{
+ BlizzardState *s = (BlizzardState *) opaque;
+ uint16_t value = blizzard_reg_read(s, s->reg);
+
+ if (s->swallow -- > 0)
+ return 0;
+ if (dc)
+ s->reg ++;
+
+ return value;
+}
+
+void s1d13745_write(void *opaque, int dc, uint16_t value)
+{
+ BlizzardState *s = (BlizzardState *) opaque;
+
+ if (s->swallow -- > 0)
+ return;
+ if (dc) {
+ blizzard_reg_write(s, s->reg, value);
+
+ if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8)
+ s->reg += 2;
+ } else
+ s->reg = value & 0xff;
+}
+
+void s1d13745_write_block(void *opaque, int dc,
+ void *buf, size_t len, int pitch)
+{
+ BlizzardState *s = (BlizzardState *) opaque;
+
+ while (len > 0) {
+ if (s->reg == 0x90 && dc &&
+ (s->data.len || blizzard_transfer_setup(s)) &&
+ len >= (s->data.len << 1)) {
+ len -= s->data.len << 1;
+ s->data.len = 0;
+ s->data.data = buf;
+ if (pitch)
+ s->data.pitch = pitch;
+ blizzard_window(s);
+ s->data.data = s->data.buf;
+ continue;
+ }
+
+ s1d13745_write(opaque, dc, *(uint16_t *) buf);
+ len -= 2;
+ buf += 2;
+ }
+
+ return;
+}
+
+static void blizzard_update_display(void *opaque)
+{
+ BlizzardState *s = (BlizzardState *) opaque;
+ int y, bypp, bypl, bwidth;
+ uint8_t *src, *dst;
+
+ if (!s->enable)
+ return;
+
+ if (s->x != ds_get_width(s->state) || s->y != ds_get_height(s->state)) {
+ s->invalidate = 1;
+ qemu_console_resize(s->state, s->x, s->y);
+ }
+
+ if (s->invalidate) {
+ s->invalidate = 0;
+
+ if (s->blank) {
+ bypp = (ds_get_bits_per_pixel(s->state) + 7) >> 3;
+ memset(ds_get_data(s->state), 0, bypp * s->x * s->y);
+ return;
+ }
+
+ s->mx[0] = 0;
+ s->mx[1] = s->x;
+ s->my[0] = 0;
+ s->my[1] = s->y;
+ }
+
+ if (s->mx[1] <= s->mx[0])
+ return;
+
+ bypp = (ds_get_bits_per_pixel(s->state) + 7) >> 3;
+ bypl = bypp * s->x;
+ bwidth = bypp * (s->mx[1] - s->mx[0]);
+ y = s->my[0];
+ src = s->fb + bypl * y + bypp * s->mx[0];
+ dst = ds_get_data(s->state) + bypl * y + bypp * s->mx[0];
+ for (; y < s->my[1]; y ++, src += bypl, dst += bypl)
+ memcpy(dst, src, bwidth);
+
+ dpy_update(s->state, s->mx[0], s->my[0],
+ s->mx[1] - s->mx[0], y - s->my[0]);
+
+ s->mx[0] = s->x;
+ s->mx[1] = 0;
+ s->my[0] = s->y;
+ s->my[1] = 0;
+}
+
+static void blizzard_screen_dump(void *opaque, const char *filename) {
+ BlizzardState *s = (BlizzardState *) opaque;
+
+ blizzard_update_display(opaque);
+ if (s && ds_get_data(s->state))
+ ppm_save(filename, s->state->surface);
+}
+
+#define DEPTH 8
+#include "blizzard_template.h"
+#define DEPTH 15
+#include "blizzard_template.h"
+#define DEPTH 16
+#include "blizzard_template.h"
+#define DEPTH 24
+#include "blizzard_template.h"
+#define DEPTH 32
+#include "blizzard_template.h"
+
+void *s1d13745_init(qemu_irq gpio_int)
+{
+ BlizzardState *s = (BlizzardState *) qemu_mallocz(sizeof(*s));
+
+ s->fb = qemu_malloc(0x180000);
+
+ s->state = graphic_console_init(blizzard_update_display,
+ blizzard_invalidate_display,
+ blizzard_screen_dump, NULL, s);
+
+ switch (ds_get_bits_per_pixel(s->state)) {
+ case 0:
+ s->line_fn_tab[0] = s->line_fn_tab[1] =
+ qemu_mallocz(sizeof(blizzard_fn_t) * 0x10);
+ break;
+ case 8:
+ s->line_fn_tab[0] = blizzard_draw_fn_8;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_8;
+ break;
+ case 15:
+ s->line_fn_tab[0] = blizzard_draw_fn_15;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_15;
+ break;
+ case 16:
+ s->line_fn_tab[0] = blizzard_draw_fn_16;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_16;
+ break;
+ case 24:
+ s->line_fn_tab[0] = blizzard_draw_fn_24;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_24;
+ break;
+ case 32:
+ s->line_fn_tab[0] = blizzard_draw_fn_32;
+ s->line_fn_tab[1] = blizzard_draw_fn_r_32;
+ break;
+ default:
+ fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
+ exit(1);
+ }
+
+ blizzard_reset(s);
+
+ return s;
+}
diff --git a/hw/blizzard_template.h b/hw/blizzard_template.h
new file mode 100644
index 0000000..42f4e90
--- /dev/null
+++ b/hw/blizzard_template.h
@@ -0,0 +1,136 @@
+/*
+ * QEMU Epson S1D13744/S1D13745 templates
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#define SKIP_PIXEL(to) to += deststep
+#if DEPTH == 8
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#elif DEPTH == 15 || DEPTH == 16
+# define PIXEL_TYPE uint16_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#elif DEPTH == 24
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) \
+ to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) \
+ *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16
+#elif DEPTH == 32
+# define PIXEL_TYPE uint32_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#else
+# error unknown bit depth
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+# define SWAP_WORDS 1
+#endif
+
+static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest,
+ const uint16_t *src, unsigned int width)
+{
+#if !defined(SWAP_WORDS) && DEPTH == 16
+ memcpy(dest, src, width);
+#else
+ uint16_t data;
+ unsigned int r, g, b;
+ const uint16_t *end = (const void *) src + width;
+ while (src < end) {
+ data = lduw_raw(src ++);
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
+ }
+#endif
+}
+
+static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest,
+ const uint8_t *src, unsigned int width)
+{
+ /* TODO: check if SDL 24-bit planes are not in the same format and
+ * if so, use memcpy */
+ unsigned int r[2], g[2], b[2];
+ const uint8_t *end = src + width;
+ while (src < end) {
+ g[0] = *src ++;
+ r[0] = *src ++;
+ r[1] = *src ++;
+ b[0] = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0]));
+ b[1] = *src ++;
+ g[1] = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1]));
+ }
+}
+
+static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest,
+ const uint8_t *src, unsigned int width)
+{
+ unsigned int r, g, b;
+ const uint8_t *end = src + width;
+ while (src < end) {
+ r = *src ++;
+ src ++;
+ b = *src ++;
+ g = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
+ }
+}
+
+/* No rotation */
+static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = {
+ NULL,
+ /* RGB 5:6:5*/
+ (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH),
+ /* RGB 6:6:6 mode 1 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
+ /* RGB 8:8:8 mode 1 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
+ NULL, NULL,
+ /* RGB 6:6:6 mode 2 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
+ /* RGB 8:8:8 mode 2 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
+ /* YUV 4:2:2 */
+ NULL,
+ /* YUV 4:2:0 */
+ NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+/* 90deg, 180deg and 270deg rotation */
+static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = {
+ /* TODO */
+ [0 ... 0xf] = NULL,
+};
+
+#undef DEPTH
+#undef SKIP_PIXEL
+#undef COPY_PIXEL
+#undef COPY_PIXEL1
+#undef PIXEL_TYPE
+
+#undef SWAP_WORDS
diff --git a/hw/boards.h b/hw/boards.h
new file mode 100644
index 0000000..6f0f0d7
--- /dev/null
+++ b/hw/boards.h
@@ -0,0 +1,38 @@
+/* Declarations for use by board files for creating devices. */
+
+#ifndef HW_BOARDS_H
+#define HW_BOARDS_H
+
+#include "qdev.h"
+
+typedef void QEMUMachineInitFunc(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model);
+
+typedef struct QEMUMachine {
+ const char *name;
+ const char *alias;
+ const char *desc;
+ QEMUMachineInitFunc *init;
+ int use_scsi;
+ int max_cpus;
+ unsigned int no_serial:1,
+ no_parallel:1,
+ use_virtcon:1,
+ no_vga:1,
+ no_floppy:1,
+ no_cdrom:1,
+ no_sdcard:1;
+ int is_default;
+ GlobalProperty *compat_props;
+ struct QEMUMachine *next;
+} QEMUMachine;
+
+int qemu_register_machine(QEMUMachine *m);
+
+extern QEMUMachine *current_machine;
+
+#endif
diff --git a/hw/bonito.c b/hw/bonito.c
new file mode 100644
index 0000000..65a4a63
--- /dev/null
+++ b/hw/bonito.c
@@ -0,0 +1,813 @@
+/*
+ * bonito north bridge support
+ *
+ * Copyright (c) 2008 yajin (yajin@vm-kernel.org)
+ * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com)
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+/*
+ * fulong 2e mini pc has a bonito north bridge.
+ */
+
+/* what is the meaning of devfn in qemu and IDSEL in bonito northbridge?
+ *
+ * devfn pci_slot<<3 + funno
+ * one pci bus can have 32 devices and each device can have 8 functions.
+ *
+ * In bonito north bridge, pci slot = IDSEL bit - 12.
+ * For example, PCI_IDSEL_VIA686B = 17,
+ * pci slot = 17-12=5
+ *
+ * so
+ * VT686B_FUN0's devfn = (5<<3)+0
+ * VT686B_FUN1's devfn = (5<<3)+1
+ *
+ * qemu also uses pci address for north bridge to access pci config register.
+ * bus_no [23:16]
+ * dev_no [15:11]
+ * fun_no [10:8]
+ * reg_no [7:2]
+ *
+ * so function bonito_sbridge_pciaddr for the translation from
+ * north bridge address to pci address.
+ */
+
+#include <assert.h>
+
+#include "hw.h"
+#include "pci.h"
+#include "pc.h"
+#include "mips.h"
+#include "pci_host.h"
+#include "sysemu.h"
+
+//#define DEBUG_BONITO
+
+#ifdef DEBUG_BONITO
+#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/
+#define BONITO_BOOT_BASE 0x1fc00000
+#define BONITO_BOOT_SIZE 0x00100000
+#define BONITO_BOOT_TOP (BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1)
+#define BONITO_FLASH_BASE 0x1c000000
+#define BONITO_FLASH_SIZE 0x03000000
+#define BONITO_FLASH_TOP (BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1)
+#define BONITO_SOCKET_BASE 0x1f800000
+#define BONITO_SOCKET_SIZE 0x00400000
+#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1)
+#define BONITO_REG_BASE 0x1fe00000
+#define BONITO_REG_SIZE 0x00040000
+#define BONITO_REG_TOP (BONITO_REG_BASE+BONITO_REG_SIZE-1)
+#define BONITO_DEV_BASE 0x1ff00000
+#define BONITO_DEV_SIZE 0x00100000
+#define BONITO_DEV_TOP (BONITO_DEV_BASE+BONITO_DEV_SIZE-1)
+#define BONITO_PCILO_BASE 0x10000000
+#define BONITO_PCILO_BASE_VA 0xb0000000
+#define BONITO_PCILO_SIZE 0x0c000000
+#define BONITO_PCILO_TOP (BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1)
+#define BONITO_PCILO0_BASE 0x10000000
+#define BONITO_PCILO1_BASE 0x14000000
+#define BONITO_PCILO2_BASE 0x18000000
+#define BONITO_PCIHI_BASE 0x20000000
+#define BONITO_PCIHI_SIZE 0x20000000
+#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1)
+#define BONITO_PCIIO_BASE 0x1fd00000
+#define BONITO_PCIIO_BASE_VA 0xbfd00000
+#define BONITO_PCIIO_SIZE 0x00010000
+#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1)
+#define BONITO_PCICFG_BASE 0x1fe80000
+#define BONITO_PCICFG_SIZE 0x00080000
+#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1)
+
+
+#define BONITO_PCICONFIGBASE 0x00
+#define BONITO_REGBASE 0x100
+
+#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE+BONITO_REG_BASE)
+#define BONITO_PCICONFIG_SIZE (0x100)
+
+#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE+BONITO_REG_BASE)
+#define BONITO_INTERNAL_REG_SIZE (0x70)
+
+#define BONITO_SPCICONFIG_BASE (BONITO_PCICFG_BASE)
+#define BONITO_SPCICONFIG_SIZE (BONITO_PCICFG_SIZE)
+
+
+
+/* 1. Bonito h/w Configuration */
+/* Power on register */
+
+#define BONITO_BONPONCFG (0x00 >> 2) /* 0x100 */
+#define BONITO_BONGENCFG_OFFSET 0x4
+#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET>>2) /*0x104 */
+
+/* 2. IO & IDE configuration */
+#define BONITO_IODEVCFG (0x08 >> 2) /* 0x108 */
+
+/* 3. IO & IDE configuration */
+#define BONITO_SDCFG (0x0c >> 2) /* 0x10c */
+
+/* 4. PCI address map control */
+#define BONITO_PCIMAP (0x10 >> 2) /* 0x110 */
+#define BONITO_PCIMEMBASECFG (0x14 >> 2) /* 0x114 */
+#define BONITO_PCIMAP_CFG (0x18 >> 2) /* 0x118 */
+
+/* 5. ICU & GPIO regs */
+/* GPIO Regs - r/w */
+#define BONITO_GPIODATA_OFFSET 0x1c
+#define BONITO_GPIODATA (BONITO_GPIODATA_OFFSET >> 2) /* 0x11c */
+#define BONITO_GPIOIE (0x20 >> 2) /* 0x120 */
+
+/* ICU Configuration Regs - r/w */
+#define BONITO_INTEDGE (0x24 >> 2) /* 0x124 */
+#define BONITO_INTSTEER (0x28 >> 2) /* 0x128 */
+#define BONITO_INTPOL (0x2c >> 2) /* 0x12c */
+
+/* ICU Enable Regs - IntEn & IntISR are r/o. */
+#define BONITO_INTENSET (0x30 >> 2) /* 0x130 */
+#define BONITO_INTENCLR (0x34 >> 2) /* 0x134 */
+#define BONITO_INTEN (0x38 >> 2) /* 0x138 */
+#define BONITO_INTISR (0x3c >> 2) /* 0x13c */
+
+/* PCI mail boxes */
+#define BONITO_PCIMAIL0_OFFSET 0x40
+#define BONITO_PCIMAIL1_OFFSET 0x44
+#define BONITO_PCIMAIL2_OFFSET 0x48
+#define BONITO_PCIMAIL3_OFFSET 0x4c
+#define BONITO_PCIMAIL0 (0x40 >> 2) /* 0x140 */
+#define BONITO_PCIMAIL1 (0x44 >> 2) /* 0x144 */
+#define BONITO_PCIMAIL2 (0x48 >> 2) /* 0x148 */
+#define BONITO_PCIMAIL3 (0x4c >> 2) /* 0x14c */
+
+/* 6. PCI cache */
+#define BONITO_PCICACHECTRL (0x50 >> 2) /* 0x150 */
+#define BONITO_PCICACHETAG (0x54 >> 2) /* 0x154 */
+#define BONITO_PCIBADADDR (0x58 >> 2) /* 0x158 */
+#define BONITO_PCIMSTAT (0x5c >> 2) /* 0x15c */
+
+/* 7. other*/
+#define BONITO_TIMECFG (0x60 >> 2) /* 0x160 */
+#define BONITO_CPUCFG (0x64 >> 2) /* 0x164 */
+#define BONITO_DQCFG (0x68 >> 2) /* 0x168 */
+#define BONITO_MEMSIZE (0x6C >> 2) /* 0x16c */
+
+#define BONITO_REGS (0x70 >> 2)
+
+/* PCI config for south bridge. type 0 */
+#define BONITO_PCICONF_IDSEL_MASK 0xfffff800 /* [31:11] */
+#define BONITO_PCICONF_IDSEL_OFFSET 11
+#define BONITO_PCICONF_FUN_MASK 0x700 /* [10:8] */
+#define BONITO_PCICONF_FUN_OFFSET 8
+#define BONITO_PCICONF_REG_MASK 0xFC
+#define BONITO_PCICONF_REG_OFFSET 0
+
+
+/* idsel BIT = pci slot number +12 */
+#define PCI_SLOT_BASE 12
+#define PCI_IDSEL_VIA686B_BIT (17)
+#define PCI_IDSEL_VIA686B (1<<PCI_IDSEL_VIA686B_BIT)
+
+#define PCI_ADDR(busno,devno,funno,regno) \
+ ((((busno)<<16)&0xff0000) + (((devno)<<11)&0xf800) + (((funno)<<8)&0x700) + (regno))
+
+typedef PCIHostState BonitoState;
+
+typedef struct PCIBonitoState
+{
+ PCIDevice dev;
+ BonitoState *pcihost;
+ uint32_t regs[BONITO_REGS];
+
+ struct bonldma {
+ uint32_t ldmactrl;
+ uint32_t ldmastat;
+ uint32_t ldmaaddr;
+ uint32_t ldmago;
+ } bonldma;
+
+ /* Based at 1fe00300, bonito Copier */
+ struct boncop {
+ uint32_t copctrl;
+ uint32_t copstat;
+ uint32_t coppaddr;
+ uint32_t copgo;
+ } boncop;
+
+ /* Bonito registers */
+ target_phys_addr_t bonito_reg_start;
+ target_phys_addr_t bonito_reg_length;
+ int bonito_reg_handle;
+
+ target_phys_addr_t bonito_pciconf_start;
+ target_phys_addr_t bonito_pciconf_length;
+ int bonito_pciconf_handle;
+
+ target_phys_addr_t bonito_spciconf_start;
+ target_phys_addr_t bonito_spciconf_length;
+ int bonito_spciconf_handle;
+
+ target_phys_addr_t bonito_pciio_start;
+ target_phys_addr_t bonito_pciio_length;
+ int bonito_pciio_handle;
+
+ target_phys_addr_t bonito_localio_start;
+ target_phys_addr_t bonito_localio_length;
+ int bonito_localio_handle;
+
+ target_phys_addr_t bonito_ldma_start;
+ target_phys_addr_t bonito_ldma_length;
+ int bonito_ldma_handle;
+
+ target_phys_addr_t bonito_cop_start;
+ target_phys_addr_t bonito_cop_length;
+ int bonito_cop_handle;
+
+} PCIBonitoState;
+
+PCIBonitoState * bonito_state;
+
+static void bonito_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t saddr;
+ int reset = 0;
+
+ saddr = (addr - BONITO_REGBASE) >> 2;
+
+ DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x \n", addr, val, saddr);
+ switch (saddr) {
+ case BONITO_BONPONCFG:
+ case BONITO_IODEVCFG:
+ case BONITO_SDCFG:
+ case BONITO_PCIMAP:
+ case BONITO_PCIMEMBASECFG:
+ case BONITO_PCIMAP_CFG:
+ case BONITO_GPIODATA:
+ case BONITO_GPIOIE:
+ case BONITO_INTEDGE:
+ case BONITO_INTSTEER:
+ case BONITO_INTPOL:
+ case BONITO_PCIMAIL0:
+ case BONITO_PCIMAIL1:
+ case BONITO_PCIMAIL2:
+ case BONITO_PCIMAIL3:
+ case BONITO_PCICACHECTRL:
+ case BONITO_PCICACHETAG:
+ case BONITO_PCIBADADDR:
+ case BONITO_PCIMSTAT:
+ case BONITO_TIMECFG:
+ case BONITO_CPUCFG:
+ case BONITO_DQCFG:
+ case BONITO_MEMSIZE:
+ s->regs[saddr] = val;
+ break;
+ case BONITO_BONGENCFG:
+ if (!(s->regs[saddr] & 0x04) && (val & 0x04)) {
+ reset = 1; /* bit 2 jump from 0 to 1 cause reset */
+ }
+ s->regs[saddr] = val;
+ if (reset) {
+ qemu_system_reset_request();
+ }
+ break;
+ case BONITO_INTENSET:
+ s->regs[BONITO_INTENSET] = val;
+ s->regs[BONITO_INTEN] |= val;
+ break;
+ case BONITO_INTENCLR:
+ s->regs[BONITO_INTENCLR] = val;
+ s->regs[BONITO_INTEN] &= ~val;
+ break;
+ case BONITO_INTEN:
+ case BONITO_INTISR:
+ DPRINTF("write to readonly bonito register %x \n", saddr);
+ break;
+ default:
+ DPRINTF("write to unknown bonito register %x \n", saddr);
+ break;
+ }
+}
+
+static uint32_t bonito_readl(void *opaque, target_phys_addr_t addr)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr - BONITO_REGBASE) >> 2;
+
+ DPRINTF("bonito_readl "TARGET_FMT_plx" \n", addr);
+ switch (saddr) {
+ case BONITO_INTISR:
+ return s->regs[saddr];
+ default:
+ return s->regs[saddr];
+ }
+}
+
+static CPUWriteMemoryFunc * const bonito_write[] = {
+ NULL,
+ NULL,
+ bonito_writel,
+};
+
+static CPUReadMemoryFunc * const bonito_read[] = {
+ NULL,
+ NULL,
+ bonito_readl,
+};
+
+static void bonito_pciconf_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+
+ DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %x \n", addr, val);
+ s->dev.config_write(&s->dev, addr, val, 4);
+}
+
+static uint32_t bonito_pciconf_readl(void *opaque, target_phys_addr_t addr)
+{
+
+ PCIBonitoState *s = opaque;
+
+ DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr);
+ return s->dev.config_read(&s->dev, addr, 4);
+}
+
+/* north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */
+static CPUWriteMemoryFunc * const bonito_pciconf_write[] = {
+ NULL,
+ NULL,
+ bonito_pciconf_writel,
+};
+
+static CPUReadMemoryFunc * const bonito_pciconf_read[] = {
+ NULL,
+ NULL,
+ bonito_pciconf_readl,
+};
+
+static uint32_t bonito_ldma_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ PCIBonitoState *s = opaque;
+
+ val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)];
+
+ return val;
+}
+
+static void bonito_ldma_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+
+ ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff;
+}
+
+static CPUWriteMemoryFunc * const bonito_ldma_write[] = {
+ NULL,
+ NULL,
+ bonito_ldma_writel,
+};
+
+static CPUReadMemoryFunc * const bonito_ldma_read[] = {
+ NULL,
+ NULL,
+ bonito_ldma_readl,
+};
+
+static uint32_t bonito_cop_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ PCIBonitoState *s = opaque;
+
+ val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)];
+
+ return val;
+}
+
+static void bonito_cop_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+
+ ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff;
+}
+
+static CPUWriteMemoryFunc * const bonito_cop_write[] = {
+ NULL,
+ NULL,
+ bonito_cop_writel,
+};
+
+static CPUReadMemoryFunc * const bonito_cop_read[] = {
+ NULL,
+ NULL,
+ bonito_cop_readl,
+};
+
+static uint32_t bonito_sbridge_pciaddr(void *opaque, target_phys_addr_t addr)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t cfgaddr;
+ uint32_t idsel;
+ uint32_t devno;
+ uint32_t funno;
+ uint32_t regno;
+ uint32_t pciaddr;
+
+ /* support type0 pci config */
+ if ((s->regs[BONITO_PCIMAP_CFG] & 0x10000) != 0x0) {
+ return 0xffffffff;
+ }
+
+ cfgaddr = addr & 0xffff;
+ cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16;
+
+ idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET;
+ devno = ffs(idsel) - 1;
+ funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET;
+ regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET;
+
+ if (idsel == 0) {
+ fprintf(stderr, "error in bonito pci config address" TARGET_FMT_plx
+ ",pcimap_cfg=%x\n", addr, s->regs[BONITO_PCIMAP_CFG]);
+ exit(1);
+ }
+ pciaddr = PCI_ADDR(pci_bus_num(s->pcihost->bus), devno, funno, regno);
+ DPRINTF("cfgaddr %x pciaddr %x busno %x devno %d funno %d regno %d \n",
+ cfgaddr, pciaddr, pci_bus_num(s->pcihost->bus), devno, funno, regno);
+
+ return pciaddr;
+}
+
+static void bonito_spciconf_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t pciaddr;
+ uint16_t status;
+
+ DPRINTF("bonito_spciconf_writeb "TARGET_FMT_plx" val %x \n", addr, val);
+ pciaddr = bonito_sbridge_pciaddr(s, addr);
+
+ if (pciaddr == 0xffffffff) {
+ return;
+ }
+
+ /* set the pci address in s->config_reg */
+ s->pcihost->config_reg = (pciaddr) | (1u << 31);
+ pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val & 0xff, 1);
+
+ /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */
+ status = pci_get_word(s->dev.config + PCI_STATUS);
+ status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT);
+ pci_set_word(s->dev.config + PCI_STATUS, status);
+}
+
+static void bonito_spciconf_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t pciaddr;
+ uint16_t status;
+
+ DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x \n", addr, val);
+ assert((addr&0x1)==0);
+
+ pciaddr = bonito_sbridge_pciaddr(s, addr);
+
+ if (pciaddr == 0xffffffff) {
+ return;
+ }
+
+ /* set the pci address in s->config_reg */
+ s->pcihost->config_reg = (pciaddr) | (1u << 31);
+ pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val, 2);
+
+ /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */
+ status = pci_get_word(s->dev.config + PCI_STATUS);
+ status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT);
+ pci_set_word(s->dev.config + PCI_STATUS, status);
+}
+
+static void bonito_spciconf_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t pciaddr;
+ uint16_t status;
+
+ DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x \n", addr, val);
+ assert((addr&0x3)==0);
+
+ pciaddr = bonito_sbridge_pciaddr(s, addr);
+
+ if (pciaddr == 0xffffffff) {
+ return;
+ }
+
+ /* set the pci address in s->config_reg */
+ s->pcihost->config_reg = (pciaddr) | (1u << 31);
+ pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val, 4);
+
+ /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */
+ status = pci_get_word(s->dev.config + PCI_STATUS);
+ status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT);
+ pci_set_word(s->dev.config + PCI_STATUS, status);
+}
+
+static uint32_t bonito_spciconf_readb(void *opaque, target_phys_addr_t addr)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t pciaddr;
+ uint16_t status;
+
+ DPRINTF("bonito_spciconf_readb "TARGET_FMT_plx" \n", addr);
+ pciaddr = bonito_sbridge_pciaddr(s, addr);
+
+ if (pciaddr == 0xffffffff) {
+ return 0xff;
+ }
+
+ /* set the pci address in s->config_reg */
+ s->pcihost->config_reg = (pciaddr) | (1u << 31);
+
+ /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */
+ status = pci_get_word(s->dev.config + PCI_STATUS);
+ status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT);
+ pci_set_word(s->dev.config + PCI_STATUS, status);
+
+ return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 1);
+}
+
+static uint32_t bonito_spciconf_readw(void *opaque, target_phys_addr_t addr)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t pciaddr;
+ uint16_t status;
+
+ DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx" \n", addr);
+ assert((addr&0x1)==0);
+
+ pciaddr = bonito_sbridge_pciaddr(s, addr);
+
+ if (pciaddr == 0xffffffff) {
+ return 0xffff;
+ }
+
+ /* set the pci address in s->config_reg */
+ s->pcihost->config_reg = (pciaddr) | (1u << 31);
+
+ /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */
+ status = pci_get_word(s->dev.config + PCI_STATUS);
+ status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT);
+ pci_set_word(s->dev.config + PCI_STATUS, status);
+
+ return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 2);
+}
+
+static uint32_t bonito_spciconf_readl(void *opaque, target_phys_addr_t addr)
+{
+ PCIBonitoState *s = opaque;
+ uint32_t pciaddr;
+ uint16_t status;
+
+ DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx" \n", addr);
+ assert((addr&0x3) == 0);
+
+ pciaddr = bonito_sbridge_pciaddr(s, addr);
+
+ if (pciaddr == 0xffffffff) {
+ return 0xffffffff;
+ }
+
+ /* set the pci address in s->config_reg */
+ s->pcihost->config_reg = (pciaddr) | (1u << 31);
+
+ /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */
+ status = pci_get_word(s->dev.config + PCI_STATUS);
+ status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT);
+ pci_set_word(s->dev.config + PCI_STATUS, status);
+
+ return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 4);
+}
+
+/* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */
+static CPUWriteMemoryFunc * const bonito_spciconf_write[] = {
+ bonito_spciconf_writeb,
+ bonito_spciconf_writew,
+ bonito_spciconf_writel,
+};
+
+static CPUReadMemoryFunc * const bonito_spciconf_read[] = {
+ bonito_spciconf_readb,
+ bonito_spciconf_readw,
+ bonito_spciconf_readl,
+};
+
+#define BONITO_IRQ_BASE 32
+
+static void pci_bonito_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+ int internal_irq = irq_num - BONITO_IRQ_BASE;
+
+ if (bonito_state->regs[BONITO_INTEDGE] & (1<<internal_irq)) {
+ qemu_irq_pulse(*pic);
+ } else { /* level triggered */
+ if (bonito_state->regs[BONITO_INTPOL] & (1<<internal_irq)) {
+ qemu_irq_raise(*pic);
+ } else {
+ qemu_irq_lower(*pic);
+ }
+ }
+}
+
+/* map the original irq (0~3) to bonito irq (16~47, but 16~31 are unused) */
+static int pci_bonito_map_irq(PCIDevice * pci_dev, int irq_num)
+{
+ int slot;
+
+ slot = (pci_dev->devfn >> 3);
+
+ switch (slot) {
+ case 5: /* FULONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */
+ return irq_num % 4 + BONITO_IRQ_BASE;
+ case 6: /* FULONG2E_ATI_SLOT, VGA */
+ return 4 + BONITO_IRQ_BASE;
+ case 7: /* FULONG2E_RTL_SLOT, RTL8139 */
+ return 5 + BONITO_IRQ_BASE;
+ case 8 ... 12: /* PCI slot 1 to 4 */
+ return (slot - 8 + irq_num) + 6 + BONITO_IRQ_BASE;
+ default: /* Unknown device, don't do any translation */
+ return irq_num;
+ }
+}
+
+static void bonito_reset(void *opaque)
+{
+ PCIBonitoState *s = opaque;
+
+ /* set the default value of north bridge registers */
+
+ s->regs[BONITO_BONPONCFG] = 0xc40;
+ s->regs[BONITO_BONGENCFG] = 0x1384;
+ s->regs[BONITO_IODEVCFG] = 0x2bff8010;
+ s->regs[BONITO_SDCFG] = 0x255e0091;
+
+ s->regs[BONITO_GPIODATA] = 0x1ff;
+ s->regs[BONITO_GPIOIE] = 0x1ff;
+ s->regs[BONITO_DQCFG] = 0x8;
+ s->regs[BONITO_MEMSIZE] = 0x10000000;
+ s->regs[BONITO_PCIMAP] = 0x6140;
+}
+
+static const VMStateDescription vmstate_bonito = {
+ .name = "Bonito",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCIBonitoState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int bonito_pcihost_initfn(SysBusDevice *dev)
+{
+ return 0;
+}
+
+static int bonito_initfn(PCIDevice *dev)
+{
+ PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev);
+
+ /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */
+ pci_config_set_vendor_id(dev->config, 0xdf53);
+ pci_config_set_device_id(dev->config, 0x00d5);
+ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST);
+ pci_config_set_prog_interface(dev->config, 0x00);
+ pci_config_set_revision(dev->config, 0x01);
+
+ /* set the north bridge register mapping */
+ s->bonito_reg_handle = cpu_register_io_memory(bonito_read, bonito_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ s->bonito_reg_start = BONITO_INTERNAL_REG_BASE;
+ s->bonito_reg_length = BONITO_INTERNAL_REG_SIZE;
+ cpu_register_physical_memory(s->bonito_reg_start, s->bonito_reg_length,
+ s->bonito_reg_handle);
+
+ /* set the north bridge pci configure mapping */
+ s->bonito_pciconf_handle = cpu_register_io_memory(bonito_pciconf_read,
+ bonito_pciconf_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ s->bonito_pciconf_start = BONITO_PCICONFIG_BASE;
+ s->bonito_pciconf_length = BONITO_PCICONFIG_SIZE;
+ cpu_register_physical_memory(s->bonito_pciconf_start, s->bonito_pciconf_length,
+ s->bonito_pciconf_handle);
+
+ /* set the south bridge pci configure mapping */
+ s->bonito_spciconf_handle = cpu_register_io_memory(bonito_spciconf_read,
+ bonito_spciconf_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ s->bonito_spciconf_start = BONITO_SPCICONFIG_BASE;
+ s->bonito_spciconf_length = BONITO_SPCICONFIG_SIZE;
+ cpu_register_physical_memory(s->bonito_spciconf_start, s->bonito_spciconf_length,
+ s->bonito_spciconf_handle);
+
+ s->bonito_ldma_handle = cpu_register_io_memory(bonito_ldma_read,
+ bonito_ldma_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ s->bonito_ldma_start = 0xbfe00200;
+ s->bonito_ldma_length = 0x100;
+ cpu_register_physical_memory(s->bonito_ldma_start, s->bonito_ldma_length,
+ s->bonito_ldma_handle);
+
+ s->bonito_cop_handle = cpu_register_io_memory(bonito_cop_read,
+ bonito_cop_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ s->bonito_cop_start = 0xbfe00300;
+ s->bonito_cop_length = 0x100;
+ cpu_register_physical_memory(s->bonito_cop_start, s->bonito_cop_length,
+ s->bonito_cop_handle);
+
+ /* Map PCI IO Space 0x1fd0 0000 - 0x1fd1 0000 */
+ s->bonito_pciio_start = BONITO_PCIIO_BASE;
+ s->bonito_pciio_length = BONITO_PCIIO_SIZE;
+ isa_mem_base = s->bonito_pciio_start;
+ isa_mmio_init(s->bonito_pciio_start, s->bonito_pciio_length);
+
+ /* add pci local io mapping */
+ s->bonito_localio_start = BONITO_DEV_BASE;
+ s->bonito_localio_length = BONITO_DEV_SIZE;
+ isa_mmio_init(s->bonito_localio_start, s->bonito_localio_length);
+
+ /* set the default value of north bridge pci config */
+ pci_set_word(dev->config + PCI_COMMAND, 0x0000);
+ pci_set_word(dev->config + PCI_STATUS, 0x0000);
+ pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0000);
+ pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000);
+
+ pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00);
+ pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01);
+ pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c);
+ pci_set_byte(dev->config + PCI_MAX_LAT, 0x00);
+
+ qemu_register_reset(bonito_reset, s);
+
+ return 0;
+}
+
+PCIBus *bonito_init(qemu_irq *pic)
+{
+ DeviceState *dev;
+ PCIBus *b;
+ BonitoState *pcihost;
+ PCIBonitoState *s;
+ PCIDevice *d;
+
+ dev = qdev_create(NULL, "Bonito-pcihost");
+ pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev));
+ b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq,
+ pci_bonito_map_irq, pic, 0x28, 32);
+ pcihost->bus = b;
+ qdev_init_nofail(dev);
+
+ d = pci_create_simple(b, PCI_DEVFN(0, 0), "Bonito");
+ s = DO_UPCAST(PCIBonitoState, dev, d);
+ s->pcihost = pcihost;
+ bonito_state = s;
+
+ return b;
+}
+
+static PCIDeviceInfo bonito_info = {
+ .qdev.name = "Bonito",
+ .qdev.desc = "Host bridge",
+ .qdev.size = sizeof(PCIBonitoState),
+ .qdev.vmsd = &vmstate_bonito,
+ .qdev.no_user = 1,
+ .init = bonito_initfn,
+};
+
+static SysBusDeviceInfo bonito_pcihost_info = {
+ .init = bonito_pcihost_initfn,
+ .qdev.name = "Bonito-pcihost",
+ .qdev.size = sizeof(BonitoState),
+ .qdev.no_user = 1,
+};
+
+static void bonito_register(void)
+{
+ sysbus_register_withprop(&bonito_pcihost_info);
+ pci_qdev_register(&bonito_info);
+}
+device_init(bonito_register);
diff --git a/hw/bt-hci-csr.c b/hw/bt-hci-csr.c
new file mode 100644
index 0000000..982577d
--- /dev/null
+++ b/hw/bt-hci-csr.c
@@ -0,0 +1,454 @@
+/*
+ * Bluetooth serial HCI transport.
+ * CSR41814 HCI with H4p vendor extensions.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "qemu-timer.h"
+#include "irq.h"
+#include "sysemu.h"
+#include "net.h"
+#include "bt.h"
+
+struct csrhci_s {
+ int enable;
+ qemu_irq *pins;
+ int pin_state;
+ int modem_state;
+ CharDriverState chr;
+#define FIFO_LEN 4096
+ int out_start;
+ int out_len;
+ int out_size;
+ uint8_t outfifo[FIFO_LEN * 2];
+ uint8_t inpkt[FIFO_LEN];
+ int in_len;
+ int in_hdr;
+ int in_data;
+ QEMUTimer *out_tm;
+ int64_t baud_delay;
+
+ bdaddr_t bd_addr;
+ struct HCIInfo *hci;
+};
+
+/* H4+ packet types */
+enum {
+ H4_CMD_PKT = 1,
+ H4_ACL_PKT = 2,
+ H4_SCO_PKT = 3,
+ H4_EVT_PKT = 4,
+ H4_NEG_PKT = 6,
+ H4_ALIVE_PKT = 7,
+};
+
+/* CSR41814 negotiation start magic packet */
+static const uint8_t csrhci_neg_packet[] = {
+ H4_NEG_PKT, 10,
+ 0x00, 0xa0, 0x01, 0x00, 0x00,
+ 0x4c, 0x00, 0x96, 0x00, 0x00,
+};
+
+/* CSR41814 vendor-specific command OCFs */
+enum {
+ OCF_CSR_SEND_FIRMWARE = 0x000,
+};
+
+static inline void csrhci_fifo_wake(struct csrhci_s *s)
+{
+ if (!s->enable || !s->out_len)
+ return;
+
+ /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */
+ if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) &&
+ s->chr.chr_read) {
+ s->chr.chr_read(s->chr.handler_opaque,
+ s->outfifo + s->out_start ++, 1);
+ s->out_len --;
+ if (s->out_start >= s->out_size) {
+ s->out_start = 0;
+ s->out_size = FIFO_LEN;
+ }
+ }
+
+ if (s->out_len)
+ qemu_mod_timer(s->out_tm, qemu_get_clock(vm_clock) + s->baud_delay);
+}
+
+#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len)
+static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len)
+{
+ int off = s->out_start + s->out_len;
+
+ /* TODO: do the padding here, i.e. align len */
+ s->out_len += len;
+
+ if (off < FIFO_LEN) {
+ if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) {
+ fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+ exit(-1);
+ }
+ return s->outfifo + off;
+ }
+
+ if (s->out_len > s->out_size) {
+ fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+ exit(-1);
+ }
+
+ return s->outfifo + off - s->out_size;
+}
+
+static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s,
+ int type, int len)
+{
+ uint8_t *ret = csrhci_out_packetz(s, len + 2);
+
+ *ret ++ = type;
+ *ret ++ = len;
+
+ return ret;
+}
+
+static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s,
+ int evt, int len)
+{
+ uint8_t *ret = csrhci_out_packetz(s,
+ len + 1 + sizeof(struct hci_event_hdr));
+
+ *ret ++ = H4_EVT_PKT;
+ ((struct hci_event_hdr *) ret)->evt = evt;
+ ((struct hci_event_hdr *) ret)->plen = len;
+
+ return ret + sizeof(struct hci_event_hdr);
+}
+
+static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf,
+ uint8_t *data, int len)
+{
+ int offset;
+ uint8_t *rpkt;
+
+ switch (ocf) {
+ case OCF_CSR_SEND_FIRMWARE:
+ /* Check if this is the bd_address packet */
+ if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) {
+ offset = 18;
+ s->bd_addr.b[0] = data[offset + 7]; /* Beyond cmd packet end(!?) */
+ s->bd_addr.b[1] = data[offset + 6];
+ s->bd_addr.b[2] = data[offset + 4];
+ s->bd_addr.b[3] = data[offset + 0];
+ s->bd_addr.b[4] = data[offset + 3];
+ s->bd_addr.b[5] = data[offset + 2];
+
+ s->hci->bdaddr_set(s->hci, s->bd_addr.b);
+ fprintf(stderr, "%s: bd_address loaded from firmware: "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__,
+ s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2],
+ s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]);
+ }
+
+ rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11);
+ /* Status bytes: no error */
+ rpkt[9] = 0x00;
+ rpkt[10] = 0x00;
+ break;
+
+ default:
+ fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__);
+ return;
+ }
+
+ csrhci_fifo_wake(s);
+}
+
+static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt)
+{
+ uint8_t *rpkt;
+ int opc;
+
+ switch (*pkt ++) {
+ case H4_CMD_PKT:
+ opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode);
+ if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) {
+ csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc),
+ pkt + sizeof(struct hci_command_hdr),
+ s->in_len - sizeof(struct hci_command_hdr) - 1);
+ return;
+ }
+
+ /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes,
+ * we need to send it to the HCI layer and then add our supported
+ * commands to the returned mask (such as OGF_VENDOR_CMD). With
+ * bt-hci.c we could just have hooks for this kind of commands but
+ * we can't with bt-host.c. */
+
+ s->hci->cmd_send(s->hci, pkt, s->in_len - 1);
+ break;
+
+ case H4_EVT_PKT:
+ goto bad_pkt;
+
+ case H4_ACL_PKT:
+ s->hci->acl_send(s->hci, pkt, s->in_len - 1);
+ break;
+
+ case H4_SCO_PKT:
+ s->hci->sco_send(s->hci, pkt, s->in_len - 1);
+ break;
+
+ case H4_NEG_PKT:
+ if (s->in_hdr != sizeof(csrhci_neg_packet) ||
+ memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) {
+ fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__);
+ return;
+ }
+ pkt += 2;
+
+ rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10);
+
+ *rpkt ++ = 0x20; /* Operational settings negotation Ok */
+ memcpy(rpkt, pkt, 7); rpkt += 7;
+ *rpkt ++ = 0xff;
+ *rpkt = 0xff;
+ break;
+
+ case H4_ALIVE_PKT:
+ if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) {
+ fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__);
+ return;
+ }
+
+ rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2);
+
+ *rpkt ++ = 0xcc;
+ *rpkt = 0x00;
+ break;
+
+ default:
+ bad_pkt:
+ /* TODO: error out */
+ fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__);
+ break;
+ }
+
+ csrhci_fifo_wake(s);
+}
+
+static int csrhci_header_len(const uint8_t *pkt)
+{
+ switch (pkt[0]) {
+ case H4_CMD_PKT:
+ return HCI_COMMAND_HDR_SIZE;
+ case H4_EVT_PKT:
+ return HCI_EVENT_HDR_SIZE;
+ case H4_ACL_PKT:
+ return HCI_ACL_HDR_SIZE;
+ case H4_SCO_PKT:
+ return HCI_SCO_HDR_SIZE;
+ case H4_NEG_PKT:
+ return pkt[1] + 1;
+ case H4_ALIVE_PKT:
+ return 3;
+ }
+
+ exit(-1);
+}
+
+static int csrhci_data_len(const uint8_t *pkt)
+{
+ switch (*pkt ++) {
+ case H4_CMD_PKT:
+ /* It seems that vendor-specific command packets for H4+ are all
+ * one byte longer than indicated in the standard header. */
+ if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00)
+ return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1;
+
+ return ((struct hci_command_hdr *) pkt)->plen;
+ case H4_EVT_PKT:
+ return ((struct hci_event_hdr *) pkt)->plen;
+ case H4_ACL_PKT:
+ return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen);
+ case H4_SCO_PKT:
+ return ((struct hci_sco_hdr *) pkt)->dlen;
+ case H4_NEG_PKT:
+ case H4_ALIVE_PKT:
+ return 0;
+ }
+
+ exit(-1);
+}
+
+static int csrhci_write(struct CharDriverState *chr,
+ const uint8_t *buf, int len)
+{
+ struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+ int plen = s->in_len;
+
+ if (!s->enable)
+ return 0;
+
+ s->in_len += len;
+ memcpy(s->inpkt + plen, buf, len);
+
+ while (1) {
+ if (s->in_len >= 2 && plen < 2)
+ s->in_hdr = csrhci_header_len(s->inpkt) + 1;
+
+ if (s->in_len >= s->in_hdr && plen < s->in_hdr)
+ s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
+
+ if (s->in_len >= s->in_data) {
+ csrhci_in_packet(s, s->inpkt);
+
+ memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
+ s->in_len -= s->in_data;
+ s->in_hdr = INT_MAX;
+ s->in_data = INT_MAX;
+ plen = 0;
+ } else
+ break;
+ }
+
+ return len;
+}
+
+static void csrhci_out_hci_packet_event(void *opaque,
+ const uint8_t *data, int len)
+{
+ struct csrhci_s *s = (struct csrhci_s *) opaque;
+ uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */
+
+ *pkt ++ = H4_EVT_PKT;
+ memcpy(pkt, data, len);
+
+ csrhci_fifo_wake(s);
+}
+
+static void csrhci_out_hci_packet_acl(void *opaque,
+ const uint8_t *data, int len)
+{
+ struct csrhci_s *s = (struct csrhci_s *) opaque;
+ uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1); /* Align */
+
+ *pkt ++ = H4_ACL_PKT;
+ pkt[len & ~1] = 0;
+ memcpy(pkt, data, len);
+
+ csrhci_fifo_wake(s);
+}
+
+static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
+{
+ QEMUSerialSetParams *ssp;
+ struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+ int prev_state = s->modem_state;
+
+ switch (cmd) {
+ case CHR_IOCTL_SERIAL_SET_PARAMS:
+ ssp = (QEMUSerialSetParams *) arg;
+ s->baud_delay = get_ticks_per_sec() / ssp->speed;
+ /* Moments later... (but shorter than 100ms) */
+ s->modem_state |= CHR_TIOCM_CTS;
+ break;
+
+ case CHR_IOCTL_SERIAL_GET_TIOCM:
+ *(int *) arg = s->modem_state;
+ break;
+
+ case CHR_IOCTL_SERIAL_SET_TIOCM:
+ s->modem_state = *(int *) arg;
+ if (~s->modem_state & prev_state & CHR_TIOCM_RTS)
+ s->modem_state &= ~CHR_TIOCM_CTS;
+ break;
+
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+static void csrhci_reset(struct csrhci_s *s)
+{
+ s->out_len = 0;
+ s->out_size = FIFO_LEN;
+ s->in_len = 0;
+ s->baud_delay = get_ticks_per_sec();
+ s->enable = 0;
+ s->in_hdr = INT_MAX;
+ s->in_data = INT_MAX;
+
+ s->modem_state = 0;
+ /* After a while... (but sooner than 10ms) */
+ s->modem_state |= CHR_TIOCM_CTS;
+
+ memset(&s->bd_addr, 0, sizeof(bdaddr_t));
+}
+
+static void csrhci_out_tick(void *opaque)
+{
+ csrhci_fifo_wake((struct csrhci_s *) opaque);
+}
+
+static void csrhci_pins(void *opaque, int line, int level)
+{
+ struct csrhci_s *s = (struct csrhci_s *) opaque;
+ int state = s->pin_state;
+
+ s->pin_state &= ~(1 << line);
+ s->pin_state |= (!!level) << line;
+
+ if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) {
+ /* TODO: Disappear from lower layers */
+ csrhci_reset(s);
+ }
+
+ if (s->pin_state == 3 && state != 3) {
+ s->enable = 1;
+ /* TODO: Wake lower layers up */
+ }
+}
+
+qemu_irq *csrhci_pins_get(CharDriverState *chr)
+{
+ struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+
+ return s->pins;
+}
+
+CharDriverState *uart_hci_init(qemu_irq wakeup)
+{
+ struct csrhci_s *s = (struct csrhci_s *)
+ qemu_mallocz(sizeof(struct csrhci_s));
+
+ s->chr.opaque = s;
+ s->chr.chr_write = csrhci_write;
+ s->chr.chr_ioctl = csrhci_ioctl;
+
+ s->hci = qemu_next_hci();
+ s->hci->opaque = s;
+ s->hci->evt_recv = csrhci_out_hci_packet_event;
+ s->hci->acl_recv = csrhci_out_hci_packet_acl;
+
+ s->out_tm = qemu_new_timer(vm_clock, csrhci_out_tick, s);
+ s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
+ csrhci_reset(s);
+
+ return &s->chr;
+}
diff --git a/hw/bt-hci.c b/hw/bt-hci.c
new file mode 100644
index 0000000..f1ee92c
--- /dev/null
+++ b/hw/bt-hci.c
@@ -0,0 +1,2221 @@
+/*
+ * QEMU Bluetooth HCI logic.
+ *
+ * Copyright (C) 2007 OpenMoko, Inc.
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "net.h"
+#include "bt.h"
+
+struct bt_hci_s {
+ uint8_t *(*evt_packet)(void *opaque);
+ void (*evt_submit)(void *opaque, int len);
+ void *opaque;
+ uint8_t evt_buf[256];
+
+ uint8_t acl_buf[4096];
+ int acl_len;
+
+ uint16_t asb_handle;
+ uint16_t psb_handle;
+
+ int last_cmd; /* Note: Always little-endian */
+
+ struct bt_device_s *conn_req_host;
+
+ struct {
+ int inquire;
+ int periodic;
+ int responses_left;
+ int responses;
+ QEMUTimer *inquiry_done;
+ QEMUTimer *inquiry_next;
+ int inquiry_length;
+ int inquiry_period;
+ int inquiry_mode;
+
+#define HCI_HANDLE_OFFSET 0x20
+#define HCI_HANDLES_MAX 0x10
+ struct bt_hci_master_link_s {
+ struct bt_link_s *link;
+ void (*lmp_acl_data)(struct bt_link_s *link,
+ const uint8_t *data, int start, int len);
+ QEMUTimer *acl_mode_timer;
+ } handle[HCI_HANDLES_MAX];
+ uint32_t role_bmp;
+ int last_handle;
+ int connecting;
+ bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX];
+ } lm;
+
+ uint8_t event_mask[8];
+ uint16_t voice_setting; /* Notw: Always little-endian */
+ uint16_t conn_accept_tout;
+ QEMUTimer *conn_accept_timer;
+
+ struct HCIInfo info;
+ struct bt_device_s device;
+};
+
+#define DEFAULT_RSSI_DBM 20
+
+#define hci_from_info(ptr) container_of((ptr), struct bt_hci_s, info)
+#define hci_from_device(ptr) container_of((ptr), struct bt_hci_s, device)
+
+struct bt_hci_link_s {
+ struct bt_link_s btlink;
+ uint16_t handle; /* Local */
+};
+
+/* LMP layer emulation */
+#if 0
+static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data)
+{
+ int resp, resplen, error, op, tr;
+ uint8_t respdata[17];
+
+ if (length < 1)
+ return;
+
+ tr = *data & 1;
+ op = *(data ++) >> 1;
+ resp = LMP_ACCEPTED;
+ resplen = 2;
+ respdata[1] = op;
+ error = 0;
+ length --;
+
+ if (op >= 0x7c) { /* Extended opcode */
+ op |= *(data ++) << 8;
+ resp = LMP_ACCEPTED_EXT;
+ resplen = 4;
+ respdata[0] = op >> 8;
+ respdata[1] = op & 0xff;
+ length --;
+ }
+
+ switch (op) {
+ case LMP_ACCEPTED:
+ /* data[0] Op code
+ */
+ if (length < 1) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = 0;
+ break;
+
+ case LMP_ACCEPTED_EXT:
+ /* data[0] Escape op code
+ * data[1] Extended op code
+ */
+ if (length < 2) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = 0;
+ break;
+
+ case LMP_NOT_ACCEPTED:
+ /* data[0] Op code
+ * data[1] Error code
+ */
+ if (length < 2) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = 0;
+ break;
+
+ case LMP_NOT_ACCEPTED_EXT:
+ /* data[0] Op code
+ * data[1] Extended op code
+ * data[2] Error code
+ */
+ if (length < 3) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = 0;
+ break;
+
+ case LMP_HOST_CONNECTION_REQ:
+ break;
+
+ case LMP_SETUP_COMPLETE:
+ resp = LMP_SETUP_COMPLETE;
+ resplen = 1;
+ bt->setup = 1;
+ break;
+
+ case LMP_DETACH:
+ /* data[0] Error code
+ */
+ if (length < 1) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ bt->setup = 0;
+ resp = 0;
+ break;
+
+ case LMP_SUPERVISION_TIMEOUT:
+ /* data[0,1] Supervision timeout
+ */
+ if (length < 2) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = 0;
+ break;
+
+ case LMP_QUALITY_OF_SERVICE:
+ resp = 0;
+ /* Fall through */
+ case LMP_QOS_REQ:
+ /* data[0,1] Poll interval
+ * data[2] N(BC)
+ */
+ if (length < 3) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ break;
+
+ case LMP_MAX_SLOT:
+ resp = 0;
+ /* Fall through */
+ case LMP_MAX_SLOT_REQ:
+ /* data[0] Max slots
+ */
+ if (length < 1) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ break;
+
+ case LMP_AU_RAND:
+ case LMP_IN_RAND:
+ case LMP_COMB_KEY:
+ /* data[0-15] Random number
+ */
+ if (length < 16) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ if (op == LMP_AU_RAND) {
+ if (bt->key_present) {
+ resp = LMP_SRES;
+ resplen = 5;
+ /* XXX: [Part H] Section 6.1 on page 801 */
+ } else {
+ error = HCI_PIN_OR_KEY_MISSING;
+ goto not_accepted;
+ }
+ } else if (op == LMP_IN_RAND) {
+ error = HCI_PAIRING_NOT_ALLOWED;
+ goto not_accepted;
+ } else {
+ /* XXX: [Part H] Section 3.2 on page 779 */
+ resp = LMP_UNIT_KEY;
+ resplen = 17;
+ memcpy(respdata + 1, bt->key, 16);
+
+ error = HCI_UNIT_LINK_KEY_USED;
+ goto not_accepted;
+ }
+ break;
+
+ case LMP_UNIT_KEY:
+ /* data[0-15] Key
+ */
+ if (length < 16) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ memcpy(bt->key, data, 16);
+ bt->key_present = 1;
+ break;
+
+ case LMP_SRES:
+ /* data[0-3] Authentication response
+ */
+ if (length < 4) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ break;
+
+ case LMP_CLKOFFSET_REQ:
+ resp = LMP_CLKOFFSET_RES;
+ resplen = 3;
+ respdata[1] = 0x33;
+ respdata[2] = 0x33;
+ break;
+
+ case LMP_CLKOFFSET_RES:
+ /* data[0,1] Clock offset
+ * (Slave to master only)
+ */
+ if (length < 2) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ break;
+
+ case LMP_VERSION_REQ:
+ case LMP_VERSION_RES:
+ /* data[0] VersNr
+ * data[1,2] CompId
+ * data[3,4] SubVersNr
+ */
+ if (length < 5) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ if (op == LMP_VERSION_REQ) {
+ resp = LMP_VERSION_RES;
+ resplen = 6;
+ respdata[1] = 0x20;
+ respdata[2] = 0xff;
+ respdata[3] = 0xff;
+ respdata[4] = 0xff;
+ respdata[5] = 0xff;
+ } else
+ resp = 0;
+ break;
+
+ case LMP_FEATURES_REQ:
+ case LMP_FEATURES_RES:
+ /* data[0-7] Features
+ */
+ if (length < 8) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ if (op == LMP_FEATURES_REQ) {
+ resp = LMP_FEATURES_RES;
+ resplen = 9;
+ respdata[1] = (bt->lmp_caps >> 0) & 0xff;
+ respdata[2] = (bt->lmp_caps >> 8) & 0xff;
+ respdata[3] = (bt->lmp_caps >> 16) & 0xff;
+ respdata[4] = (bt->lmp_caps >> 24) & 0xff;
+ respdata[5] = (bt->lmp_caps >> 32) & 0xff;
+ respdata[6] = (bt->lmp_caps >> 40) & 0xff;
+ respdata[7] = (bt->lmp_caps >> 48) & 0xff;
+ respdata[8] = (bt->lmp_caps >> 56) & 0xff;
+ } else
+ resp = 0;
+ break;
+
+ case LMP_NAME_REQ:
+ /* data[0] Name offset
+ */
+ if (length < 1) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = LMP_NAME_RES;
+ resplen = 17;
+ respdata[1] = data[0];
+ respdata[2] = strlen(bt->lmp_name);
+ memset(respdata + 3, 0x00, 14);
+ if (respdata[2] > respdata[1])
+ memcpy(respdata + 3, bt->lmp_name + respdata[1],
+ respdata[2] - respdata[1]);
+ break;
+
+ case LMP_NAME_RES:
+ /* data[0] Name offset
+ * data[1] Name length
+ * data[2-15] Name fragment
+ */
+ if (length < 16) {
+ error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+ goto not_accepted;
+ }
+ resp = 0;
+ break;
+
+ default:
+ error = HCI_UNKNOWN_LMP_PDU;
+ /* Fall through */
+ not_accepted:
+ if (op >> 8) {
+ resp = LMP_NOT_ACCEPTED_EXT;
+ resplen = 5;
+ respdata[0] = op >> 8;
+ respdata[1] = op & 0xff;
+ respdata[2] = error;
+ } else {
+ resp = LMP_NOT_ACCEPTED;
+ resplen = 3;
+ respdata[0] = op & 0xff;
+ respdata[1] = error;
+ }
+ }
+
+ if (resp == 0)
+ return;
+
+ if (resp >> 8) {
+ respdata[0] = resp >> 8;
+ respdata[1] = resp & 0xff;
+ } else
+ respdata[0] = resp & 0xff;
+
+ respdata[0] <<= 1;
+ respdata[0] |= tr;
+}
+
+static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data)
+{
+ struct bt_device_s *slave;
+ if (length < 1)
+ return;
+
+ slave = 0;
+#if 0
+ slave = net->slave;
+#endif
+
+ switch (data[0] & 3) {
+ case LLID_ACLC:
+ bt_submit_lmp(slave, length - 1, data + 1);
+ break;
+ case LLID_ACLU_START:
+#if 0
+ bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1);
+ breka;
+#endif
+ default:
+ case LLID_ACLU_CONT:
+ break;
+ }
+}
+#endif
+
+/* HCI layer emulation */
+
+/* Note: we could ignore endiannes because unswapped handles will still
+ * be valid as connection identifiers for the guest - they don't have to
+ * be continuously allocated. We do it though, to preserve similar
+ * behaviour between hosts. Some things, like the BD_ADDR cannot be
+ * preserved though (for example if a real hci is used). */
+#ifdef HOST_WORDS_BIGENDIAN
+# define HNDL(raw) bswap16(raw)
+#else
+# define HNDL(raw) (raw)
+#endif
+
+static const uint8_t bt_event_reserved_mask[8] = {
+ 0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00,
+};
+
+static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci,
+ int evt, int len)
+{
+ uint8_t *packet, mask;
+ int mask_byte;
+
+ if (len > 255) {
+ fprintf(stderr, "%s: HCI event params too long (%ib)\n",
+ __FUNCTION__, len);
+ exit(-1);
+ }
+
+ mask_byte = (evt - 1) >> 3;
+ mask = 1 << ((evt - 1) & 3);
+ if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte])
+ return NULL;
+
+ packet = hci->evt_packet(hci->opaque);
+ packet[0] = evt;
+ packet[1] = len;
+
+ return &packet[2];
+}
+
+static inline void bt_hci_event(struct bt_hci_s *hci, int evt,
+ void *params, int len)
+{
+ uint8_t *packet = bt_hci_event_start(hci, evt, len);
+
+ if (!packet)
+ return;
+
+ if (len)
+ memcpy(packet, params, len);
+
+ hci->evt_submit(hci->opaque, len + 2);
+}
+
+static inline void bt_hci_event_status(struct bt_hci_s *hci, int status)
+{
+ evt_cmd_status params = {
+ .status = status,
+ .ncmd = 1,
+ .opcode = hci->last_cmd,
+ };
+
+ bt_hci_event(hci, EVT_CMD_STATUS, &params, EVT_CMD_STATUS_SIZE);
+}
+
+static inline void bt_hci_event_complete(struct bt_hci_s *hci,
+ void *ret, int len)
+{
+ uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE,
+ len + EVT_CMD_COMPLETE_SIZE);
+ evt_cmd_complete *params = (evt_cmd_complete *) packet;
+
+ if (!packet)
+ return;
+
+ params->ncmd = 1;
+ params->opcode = hci->last_cmd;
+ if (len)
+ memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len);
+
+ hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2);
+}
+
+static void bt_hci_inquiry_done(void *opaque)
+{
+ struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
+ uint8_t status = HCI_SUCCESS;
+
+ if (!hci->lm.periodic)
+ hci->lm.inquire = 0;
+
+ /* The specification is inconsistent about this one. Page 565 reads
+ * "The event parameters of Inquiry Complete event will have a summary
+ * of the result from the Inquiry process, which reports the number of
+ * nearby Bluetooth devices that responded [so hci->responses].", but
+ * Event Parameters (see page 729) has only Status. */
+ bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1);
+}
+
+static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci,
+ struct bt_device_s *slave)
+{
+ inquiry_info params = {
+ .num_responses = 1,
+ .bdaddr = BAINIT(&slave->bd_addr),
+ .pscan_rep_mode = 0x00, /* R0 */
+ .pscan_period_mode = 0x00, /* P0 - deprecated */
+ .pscan_mode = 0x00, /* Standard scan - deprecated */
+ .dev_class[0] = slave->class[0],
+ .dev_class[1] = slave->class[1],
+ .dev_class[2] = slave->class[2],
+ /* TODO: return the clkoff *differenece* */
+ .clock_offset = slave->clkoff, /* Note: no swapping */
+ };
+
+ bt_hci_event(hci, EVT_INQUIRY_RESULT, &params, INQUIRY_INFO_SIZE);
+}
+
+static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci,
+ struct bt_device_s *slave)
+{
+ inquiry_info_with_rssi params = {
+ .num_responses = 1,
+ .bdaddr = BAINIT(&slave->bd_addr),
+ .pscan_rep_mode = 0x00, /* R0 */
+ .pscan_period_mode = 0x00, /* P0 - deprecated */
+ .dev_class[0] = slave->class[0],
+ .dev_class[1] = slave->class[1],
+ .dev_class[2] = slave->class[2],
+ /* TODO: return the clkoff *differenece* */
+ .clock_offset = slave->clkoff, /* Note: no swapping */
+ .rssi = DEFAULT_RSSI_DBM,
+ };
+
+ bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI,
+ &params, INQUIRY_INFO_WITH_RSSI_SIZE);
+}
+
+static void bt_hci_inquiry_result(struct bt_hci_s *hci,
+ struct bt_device_s *slave)
+{
+ if (!slave->inquiry_scan || !hci->lm.responses_left)
+ return;
+
+ hci->lm.responses_left --;
+ hci->lm.responses ++;
+
+ switch (hci->lm.inquiry_mode) {
+ case 0x00:
+ bt_hci_inquiry_result_standard(hci, slave);
+ return;
+ case 0x01:
+ bt_hci_inquiry_result_with_rssi(hci, slave);
+ return;
+ default:
+ fprintf(stderr, "%s: bad inquiry mode %02x\n", __FUNCTION__,
+ hci->lm.inquiry_mode);
+ exit(-1);
+ }
+}
+
+static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period)
+{
+ qemu_mod_timer(timer, qemu_get_clock(vm_clock) +
+ muldiv64(period << 7, get_ticks_per_sec(), 100));
+}
+
+static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length)
+{
+ struct bt_device_s *slave;
+
+ hci->lm.inquiry_length = length;
+ for (slave = hci->device.net->slave; slave; slave = slave->next)
+ /* Don't uncover ourselves. */
+ if (slave != &hci->device)
+ bt_hci_inquiry_result(hci, slave);
+
+ /* TODO: register for a callback on a new device's addition to the
+ * scatternet so that if it's added before inquiry_length expires,
+ * an Inquiry Result is generated immediately. Alternatively re-loop
+ * through the devices on the inquiry_length expiration and report
+ * devices not seen before. */
+ if (hci->lm.responses_left)
+ bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length);
+ else
+ bt_hci_inquiry_done(hci);
+
+ if (hci->lm.periodic)
+ bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period);
+}
+
+static void bt_hci_inquiry_next(void *opaque)
+{
+ struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
+
+ hci->lm.responses_left += hci->lm.responses;
+ hci->lm.responses = 0;
+ bt_hci_inquiry_start(hci, hci->lm.inquiry_length);
+}
+
+static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle)
+{
+ return !(handle & HCI_HANDLE_OFFSET) ||
+ handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) ||
+ !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
+}
+
+static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle)
+{
+ return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET)));
+}
+
+static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci,
+ uint16_t handle)
+{
+ struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
+
+ return bt_hci_role_master(hci, handle) ? link->slave : link->host;
+}
+
+static void bt_hci_mode_tick(void *opaque);
+static void bt_hci_lmp_link_establish(struct bt_hci_s *hci,
+ struct bt_link_s *link, int master)
+{
+ hci->lm.handle[hci->lm.last_handle].link = link;
+
+ if (master) {
+ /* We are the master side of an ACL link */
+ hci->lm.role_bmp |= 1 << hci->lm.last_handle;
+
+ hci->lm.handle[hci->lm.last_handle].lmp_acl_data =
+ link->slave->lmp_acl_data;
+ } else {
+ /* We are the slave side of an ACL link */
+ hci->lm.role_bmp &= ~(1 << hci->lm.last_handle);
+
+ hci->lm.handle[hci->lm.last_handle].lmp_acl_data =
+ link->host->lmp_acl_resp;
+ }
+
+ /* Mode */
+ if (master) {
+ link->acl_mode = acl_active;
+ hci->lm.handle[hci->lm.last_handle].acl_mode_timer =
+ qemu_new_timer(vm_clock, bt_hci_mode_tick, link);
+ }
+}
+
+static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle)
+{
+ handle &= ~HCI_HANDLE_OFFSET;
+ hci->lm.handle[handle].link = NULL;
+
+ if (bt_hci_role_master(hci, handle)) {
+ qemu_del_timer(hci->lm.handle[handle].acl_mode_timer);
+ qemu_free_timer(hci->lm.handle[handle].acl_mode_timer);
+ }
+}
+
+static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr)
+{
+ struct bt_device_s *slave;
+ struct bt_link_s link;
+
+ for (slave = hci->device.net->slave; slave; slave = slave->next)
+ if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
+ break;
+ if (!slave || slave == &hci->device)
+ return -ENODEV;
+
+ bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr);
+
+ link.slave = slave;
+ link.host = &hci->device;
+ link.slave->lmp_connection_request(&link); /* Always last */
+
+ return 0;
+}
+
+static void bt_hci_connection_reject(struct bt_hci_s *hci,
+ struct bt_device_s *host, uint8_t because)
+{
+ struct bt_link_s link = {
+ .slave = &hci->device,
+ .host = host,
+ /* Rest uninitialised */
+ };
+
+ host->reject_reason = because;
+ host->lmp_connection_complete(&link);
+}
+
+static void bt_hci_connection_reject_event(struct bt_hci_s *hci,
+ bdaddr_t *bdaddr)
+{
+ evt_conn_complete params;
+
+ params.status = HCI_NO_CONNECTION;
+ params.handle = 0;
+ bacpy(&params.bdaddr, bdaddr);
+ params.link_type = ACL_LINK;
+ params.encr_mode = 0x00; /* Encryption not required */
+ bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
+}
+
+static void bt_hci_connection_accept(struct bt_hci_s *hci,
+ struct bt_device_s *host)
+{
+ struct bt_hci_link_s *link = qemu_mallocz(sizeof(struct bt_hci_link_s));
+ evt_conn_complete params;
+ uint16_t handle;
+ uint8_t status = HCI_SUCCESS;
+ int tries = HCI_HANDLES_MAX;
+
+ /* Make a connection handle */
+ do {
+ while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries)
+ hci->lm.last_handle &= HCI_HANDLES_MAX - 1;
+ handle = hci->lm.last_handle | HCI_HANDLE_OFFSET;
+ } while ((handle == hci->asb_handle || handle == hci->psb_handle) &&
+ tries);
+
+ if (!tries) {
+ qemu_free(link);
+ bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES);
+ status = HCI_NO_CONNECTION;
+ goto complete;
+ }
+
+ link->btlink.slave = &hci->device;
+ link->btlink.host = host;
+ link->handle = handle;
+
+ /* Link established */
+ bt_hci_lmp_link_establish(hci, &link->btlink, 0);
+
+complete:
+ params.status = status;
+ params.handle = HNDL(handle);
+ bacpy(&params.bdaddr, &host->bd_addr);
+ params.link_type = ACL_LINK;
+ params.encr_mode = 0x00; /* Encryption not required */
+ bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
+
+ /* Neets to be done at the very end because it can trigger a (nested)
+ * disconnected, in case the other and had cancelled the request
+ * locally. */
+ if (status == HCI_SUCCESS) {
+ host->reject_reason = 0;
+ host->lmp_connection_complete(&link->btlink);
+ }
+}
+
+static void bt_hci_lmp_connection_request(struct bt_link_s *link)
+{
+ struct bt_hci_s *hci = hci_from_device(link->slave);
+ evt_conn_request params;
+
+ if (hci->conn_req_host) {
+ bt_hci_connection_reject(hci, link->host,
+ HCI_REJECTED_LIMITED_RESOURCES);
+ return;
+ }
+ hci->conn_req_host = link->host;
+ /* TODO: if masked and auto-accept, then auto-accept,
+ * if masked and not auto-accept, then auto-reject */
+ /* TODO: kick the hci->conn_accept_timer, timeout after
+ * hci->conn_accept_tout * 0.625 msec */
+
+ bacpy(&params.bdaddr, &link->host->bd_addr);
+ memcpy(&params.dev_class, &link->host->class, sizeof(params.dev_class));
+ params.link_type = ACL_LINK;
+ bt_hci_event(hci, EVT_CONN_REQUEST, &params, EVT_CONN_REQUEST_SIZE);
+ return;
+}
+
+static void bt_hci_conn_accept_timeout(void *opaque)
+{
+ struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
+
+ if (!hci->conn_req_host)
+ /* Already accepted or rejected. If the other end cancelled the
+ * connection request then we still have to reject or accept it
+ * and then we'll get a disconnect. */
+ return;
+
+ /* TODO */
+}
+
+/* Remove from the list of devices which we wanted to connect to and
+ * are awaiting a response from. If the callback sees a response from
+ * a device which is not on the list it will assume it's a connection
+ * that's been cancelled by the host in the meantime and immediately
+ * try to detach the link and send a Connection Complete. */
+static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci,
+ bdaddr_t *bdaddr)
+{
+ int i;
+
+ for (i = 0; i < hci->lm.connecting; i ++)
+ if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) {
+ if (i < -- hci->lm.connecting)
+ bacpy(&hci->lm.awaiting_bdaddr[i],
+ &hci->lm.awaiting_bdaddr[hci->lm.connecting]);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void bt_hci_lmp_connection_complete(struct bt_link_s *link)
+{
+ struct bt_hci_s *hci = hci_from_device(link->host);
+ evt_conn_complete params;
+ uint16_t handle;
+ uint8_t status = HCI_SUCCESS;
+ int tries = HCI_HANDLES_MAX;
+
+ if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) {
+ if (!hci->device.reject_reason)
+ link->slave->lmp_disconnect_slave(link);
+ handle = 0;
+ status = HCI_NO_CONNECTION;
+ goto complete;
+ }
+
+ if (hci->device.reject_reason) {
+ handle = 0;
+ status = hci->device.reject_reason;
+ goto complete;
+ }
+
+ /* Make a connection handle */
+ do {
+ while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries)
+ hci->lm.last_handle &= HCI_HANDLES_MAX - 1;
+ handle = hci->lm.last_handle | HCI_HANDLE_OFFSET;
+ } while ((handle == hci->asb_handle || handle == hci->psb_handle) &&
+ tries);
+
+ if (!tries) {
+ link->slave->lmp_disconnect_slave(link);
+ status = HCI_NO_CONNECTION;
+ goto complete;
+ }
+
+ /* Link established */
+ link->handle = handle;
+ bt_hci_lmp_link_establish(hci, link, 1);
+
+complete:
+ params.status = status;
+ params.handle = HNDL(handle);
+ params.link_type = ACL_LINK;
+ bacpy(&params.bdaddr, &link->slave->bd_addr);
+ params.encr_mode = 0x00; /* Encryption not required */
+ bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
+}
+
+static void bt_hci_disconnect(struct bt_hci_s *hci,
+ uint16_t handle, int reason)
+{
+ struct bt_link_s *btlink =
+ hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
+ struct bt_hci_link_s *link;
+ evt_disconn_complete params;
+
+ if (bt_hci_role_master(hci, handle)) {
+ btlink->slave->reject_reason = reason;
+ btlink->slave->lmp_disconnect_slave(btlink);
+ /* The link pointer is invalid from now on */
+
+ goto complete;
+ }
+
+ btlink->host->reject_reason = reason;
+ btlink->host->lmp_disconnect_master(btlink);
+
+ /* We are the slave, we get to clean this burden */
+ link = (struct bt_hci_link_s *) btlink;
+ qemu_free(link);
+
+complete:
+ bt_hci_lmp_link_teardown(hci, handle);
+
+ params.status = HCI_SUCCESS;
+ params.handle = HNDL(handle);
+ params.reason = HCI_CONNECTION_TERMINATED;
+ bt_hci_event(hci, EVT_DISCONN_COMPLETE,
+ &params, EVT_DISCONN_COMPLETE_SIZE);
+}
+
+/* TODO: use only one function */
+static void bt_hci_lmp_disconnect_host(struct bt_link_s *link)
+{
+ struct bt_hci_s *hci = hci_from_device(link->host);
+ uint16_t handle = link->handle;
+ evt_disconn_complete params;
+
+ bt_hci_lmp_link_teardown(hci, handle);
+
+ params.status = HCI_SUCCESS;
+ params.handle = HNDL(handle);
+ params.reason = hci->device.reject_reason;
+ bt_hci_event(hci, EVT_DISCONN_COMPLETE,
+ &params, EVT_DISCONN_COMPLETE_SIZE);
+}
+
+static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink)
+{
+ struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
+ struct bt_hci_s *hci = hci_from_device(btlink->slave);
+ uint16_t handle = link->handle;
+ evt_disconn_complete params;
+
+ qemu_free(link);
+
+ bt_hci_lmp_link_teardown(hci, handle);
+
+ params.status = HCI_SUCCESS;
+ params.handle = HNDL(handle);
+ params.reason = hci->device.reject_reason;
+ bt_hci_event(hci, EVT_DISCONN_COMPLETE,
+ &params, EVT_DISCONN_COMPLETE_SIZE);
+}
+
+static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr)
+{
+ struct bt_device_s *slave;
+ evt_remote_name_req_complete params;
+ int len;
+
+ for (slave = hci->device.net->slave; slave; slave = slave->next)
+ if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
+ break;
+ if (!slave)
+ return -ENODEV;
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ params.status = HCI_SUCCESS;
+ bacpy(&params.bdaddr, &slave->bd_addr);
+ len = snprintf(params.name, sizeof(params.name),
+ "%s", slave->lmp_name ?: "");
+ memset(params.name + len, 0, sizeof(params.name) - len);
+ bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE,
+ &params, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE);
+
+ return 0;
+}
+
+static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle)
+{
+ struct bt_device_s *slave;
+ evt_read_remote_features_complete params;
+
+ if (bt_hci_handle_bad(hci, handle))
+ return -ENODEV;
+
+ slave = bt_hci_remote_dev(hci, handle);
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ params.status = HCI_SUCCESS;
+ params.handle = HNDL(handle);
+ params.features[0] = (slave->lmp_caps >> 0) & 0xff;
+ params.features[1] = (slave->lmp_caps >> 8) & 0xff;
+ params.features[2] = (slave->lmp_caps >> 16) & 0xff;
+ params.features[3] = (slave->lmp_caps >> 24) & 0xff;
+ params.features[4] = (slave->lmp_caps >> 32) & 0xff;
+ params.features[5] = (slave->lmp_caps >> 40) & 0xff;
+ params.features[6] = (slave->lmp_caps >> 48) & 0xff;
+ params.features[7] = (slave->lmp_caps >> 56) & 0xff;
+ bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE,
+ &params, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE);
+
+ return 0;
+}
+
+static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle)
+{
+ evt_read_remote_version_complete params;
+
+ if (bt_hci_handle_bad(hci, handle))
+ return -ENODEV;
+
+ bt_hci_remote_dev(hci, handle);
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ params.status = HCI_SUCCESS;
+ params.handle = HNDL(handle);
+ params.lmp_ver = 0x03;
+ params.manufacturer = cpu_to_le16(0xa000);
+ params.lmp_subver = cpu_to_le16(0xa607);
+ bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE,
+ &params, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE);
+
+ return 0;
+}
+
+static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle)
+{
+ struct bt_device_s *slave;
+ evt_read_clock_offset_complete params;
+
+ if (bt_hci_handle_bad(hci, handle))
+ return -ENODEV;
+
+ slave = bt_hci_remote_dev(hci, handle);
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ params.status = HCI_SUCCESS;
+ params.handle = HNDL(handle);
+ /* TODO: return the clkoff *differenece* */
+ params.clock_offset = slave->clkoff; /* Note: no swapping */
+ bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE,
+ &params, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE);
+
+ return 0;
+}
+
+static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link,
+ uint16_t handle)
+{
+ evt_mode_change params = {
+ .status = HCI_SUCCESS,
+ .handle = HNDL(handle),
+ .mode = link->acl_mode,
+ .interval = cpu_to_le16(link->acl_interval),
+ };
+
+ bt_hci_event(hci, EVT_MODE_CHANGE, &params, EVT_MODE_CHANGE_SIZE);
+}
+
+static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci,
+ struct bt_link_s *link, int mode, uint16_t interval)
+{
+ link->acl_mode = mode;
+ link->acl_interval = interval;
+
+ bt_hci_event_mode(hci, link, link->handle);
+
+ link->slave->lmp_mode_change(link);
+}
+
+static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink)
+{
+ struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
+ struct bt_hci_s *hci = hci_from_device(btlink->slave);
+
+ bt_hci_event_mode(hci, btlink, link->handle);
+}
+
+static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle,
+ int interval, int mode)
+{
+ struct bt_hci_master_link_s *link;
+
+ if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle))
+ return -ENODEV;
+
+ link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET];
+ if (link->link->acl_mode != acl_active) {
+ bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED);
+ return 0;
+ }
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ qemu_mod_timer(link->acl_mode_timer, qemu_get_clock(vm_clock) +
+ muldiv64(interval * 625, get_ticks_per_sec(), 1000000));
+ bt_hci_lmp_mode_change_master(hci, link->link, mode, interval);
+
+ return 0;
+}
+
+static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode)
+{
+ struct bt_hci_master_link_s *link;
+
+ if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle))
+ return -ENODEV;
+
+ link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET];
+ if (link->link->acl_mode != mode) {
+ bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED);
+
+ return 0;
+ }
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ qemu_del_timer(link->acl_mode_timer);
+ bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0);
+
+ return 0;
+}
+
+static void bt_hci_mode_tick(void *opaque)
+{
+ struct bt_link_s *link = opaque;
+ struct bt_hci_s *hci = hci_from_device(link->host);
+
+ bt_hci_lmp_mode_change_master(hci, link, acl_active, 0);
+}
+
+static void bt_hci_reset(struct bt_hci_s *hci)
+{
+ hci->acl_len = 0;
+ hci->last_cmd = 0;
+ hci->lm.connecting = 0;
+
+ hci->event_mask[0] = 0xff;
+ hci->event_mask[1] = 0xff;
+ hci->event_mask[2] = 0xff;
+ hci->event_mask[3] = 0xff;
+ hci->event_mask[4] = 0xff;
+ hci->event_mask[5] = 0x1f;
+ hci->event_mask[6] = 0x00;
+ hci->event_mask[7] = 0x00;
+ hci->device.inquiry_scan = 0;
+ hci->device.page_scan = 0;
+ if (hci->device.lmp_name)
+ qemu_free((void *) hci->device.lmp_name);
+ hci->device.lmp_name = NULL;
+ hci->device.class[0] = 0x00;
+ hci->device.class[1] = 0x00;
+ hci->device.class[2] = 0x00;
+ hci->voice_setting = 0x0000;
+ hci->conn_accept_tout = 0x1f40;
+ hci->lm.inquiry_mode = 0x00;
+
+ hci->psb_handle = 0x000;
+ hci->asb_handle = 0x000;
+
+ /* XXX: qemu_del_timer(sl->acl_mode_timer); for all links */
+ qemu_del_timer(hci->lm.inquiry_done);
+ qemu_del_timer(hci->lm.inquiry_next);
+ qemu_del_timer(hci->conn_accept_timer);
+}
+
+static void bt_hci_read_local_version_rp(struct bt_hci_s *hci)
+{
+ read_local_version_rp lv = {
+ .status = HCI_SUCCESS,
+ .hci_ver = 0x03,
+ .hci_rev = cpu_to_le16(0xa607),
+ .lmp_ver = 0x03,
+ .manufacturer = cpu_to_le16(0xa000),
+ .lmp_subver = cpu_to_le16(0xa607),
+ };
+
+ bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE);
+}
+
+static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci)
+{
+ read_local_commands_rp lc = {
+ .status = HCI_SUCCESS,
+ .commands = {
+ /* Keep updated! */
+ /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */
+ 0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3,
+ 0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ };
+
+ bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE);
+}
+
+static void bt_hci_read_local_features_rp(struct bt_hci_s *hci)
+{
+ read_local_features_rp lf = {
+ .status = HCI_SUCCESS,
+ .features = {
+ (hci->device.lmp_caps >> 0) & 0xff,
+ (hci->device.lmp_caps >> 8) & 0xff,
+ (hci->device.lmp_caps >> 16) & 0xff,
+ (hci->device.lmp_caps >> 24) & 0xff,
+ (hci->device.lmp_caps >> 32) & 0xff,
+ (hci->device.lmp_caps >> 40) & 0xff,
+ (hci->device.lmp_caps >> 48) & 0xff,
+ (hci->device.lmp_caps >> 56) & 0xff,
+ },
+ };
+
+ bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE);
+}
+
+static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page)
+{
+ read_local_ext_features_rp lef = {
+ .status = HCI_SUCCESS,
+ .page_num = page,
+ .max_page_num = 0x00,
+ .features = {
+ /* Keep updated! */
+ 0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80,
+ },
+ };
+ if (page)
+ memset(lef.features, 0, sizeof(lef.features));
+
+ bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE);
+}
+
+static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci)
+{
+ read_buffer_size_rp bs = {
+ /* This can be made configurable, for one standard USB dongle HCI
+ * the four values are cpu_to_le16(0x0180), 0x40,
+ * cpu_to_le16(0x0008), cpu_to_le16(0x0008). */
+ .status = HCI_SUCCESS,
+ .acl_mtu = cpu_to_le16(0x0200),
+ .sco_mtu = 0,
+ .acl_max_pkt = cpu_to_le16(0x0001),
+ .sco_max_pkt = cpu_to_le16(0x0000),
+ };
+
+ bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE);
+}
+
+/* Deprecated in V2.0 (page 661) */
+static void bt_hci_read_country_code_rp(struct bt_hci_s *hci)
+{
+ read_country_code_rp cc ={
+ .status = HCI_SUCCESS,
+ .country_code = 0x00, /* North America & Europe^1 and Japan */
+ };
+
+ bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE);
+
+ /* ^1. Except France, sorry */
+}
+
+static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci)
+{
+ read_bd_addr_rp ba = {
+ .status = HCI_SUCCESS,
+ .bdaddr = BAINIT(&hci->device.bd_addr),
+ };
+
+ bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE);
+}
+
+static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle)
+{
+ read_link_quality_rp lq = {
+ .status = HCI_SUCCESS,
+ .handle = HNDL(handle),
+ .link_quality = 0xff,
+ };
+
+ if (bt_hci_handle_bad(hci, handle))
+ lq.status = HCI_NO_CONNECTION;
+
+ bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE);
+ return 0;
+}
+
+/* Generate a Command Complete event with only the Status parameter */
+static inline void bt_hci_event_complete_status(struct bt_hci_s *hci,
+ uint8_t status)
+{
+ bt_hci_event_complete(hci, &status, 1);
+}
+
+static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci,
+ uint8_t status, bdaddr_t *bd_addr)
+{
+ create_conn_cancel_rp params = {
+ .status = status,
+ .bdaddr = BAINIT(bd_addr),
+ };
+
+ bt_hci_event_complete(hci, &params, CREATE_CONN_CANCEL_RP_SIZE);
+}
+
+static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci,
+ uint16_t handle)
+{
+ evt_auth_complete params = {
+ .status = HCI_SUCCESS,
+ .handle = HNDL(handle),
+ };
+
+ bt_hci_event(hci, EVT_AUTH_COMPLETE, &params, EVT_AUTH_COMPLETE_SIZE);
+}
+
+static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci,
+ uint16_t handle, uint8_t mode)
+{
+ evt_encrypt_change params = {
+ .status = HCI_SUCCESS,
+ .handle = HNDL(handle),
+ .encrypt = mode,
+ };
+
+ bt_hci_event(hci, EVT_ENCRYPT_CHANGE, &params, EVT_ENCRYPT_CHANGE_SIZE);
+}
+
+static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci,
+ bdaddr_t *bd_addr)
+{
+ remote_name_req_cancel_rp params = {
+ .status = HCI_INVALID_PARAMETERS,
+ .bdaddr = BAINIT(bd_addr),
+ };
+
+ bt_hci_event_complete(hci, &params, REMOTE_NAME_REQ_CANCEL_RP_SIZE);
+}
+
+static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci,
+ uint16_t handle)
+{
+ evt_read_remote_ext_features_complete params = {
+ .status = HCI_UNSUPPORTED_FEATURE,
+ .handle = HNDL(handle),
+ /* Rest uninitialised */
+ };
+
+ bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE,
+ &params, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE);
+}
+
+static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci,
+ uint16_t handle)
+{
+ read_lmp_handle_rp params = {
+ .status = HCI_NO_CONNECTION,
+ .handle = HNDL(handle),
+ .reserved = 0,
+ /* Rest uninitialised */
+ };
+
+ bt_hci_event_complete(hci, &params, READ_LMP_HANDLE_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci,
+ int status, uint16_t handle, int master)
+{
+ role_discovery_rp params = {
+ .status = status,
+ .handle = HNDL(handle),
+ .role = master ? 0x00 : 0x01,
+ };
+
+ bt_hci_event_complete(hci, &params, ROLE_DISCOVERY_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci,
+ int status, uint16_t handle)
+{
+ flush_rp params = {
+ .status = status,
+ .handle = HNDL(handle),
+ };
+
+ bt_hci_event_complete(hci, &params, FLUSH_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci)
+{
+ read_local_name_rp params;
+ params.status = HCI_SUCCESS;
+ memset(params.name, 0, sizeof(params.name));
+ if (hci->device.lmp_name)
+ strncpy(params.name, hci->device.lmp_name, sizeof(params.name));
+
+ bt_hci_event_complete(hci, &params, READ_LOCAL_NAME_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_conn_accept_timeout(
+ struct bt_hci_s *hci)
+{
+ read_conn_accept_timeout_rp params = {
+ .status = HCI_SUCCESS,
+ .timeout = cpu_to_le16(hci->conn_accept_tout),
+ };
+
+ bt_hci_event_complete(hci, &params, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci)
+{
+ read_scan_enable_rp params = {
+ .status = HCI_SUCCESS,
+ .enable =
+ (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) |
+ (hci->device.page_scan ? SCAN_PAGE : 0),
+ };
+
+ bt_hci_event_complete(hci, &params, READ_SCAN_ENABLE_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci)
+{
+ read_class_of_dev_rp params;
+
+ params.status = HCI_SUCCESS;
+ memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class));
+
+ bt_hci_event_complete(hci, &params, READ_CLASS_OF_DEV_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci)
+{
+ read_voice_setting_rp params = {
+ .status = HCI_SUCCESS,
+ .voice_setting = hci->voice_setting, /* Note: no swapping */
+ };
+
+ bt_hci_event_complete(hci, &params, READ_VOICE_SETTING_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_inquiry_mode(
+ struct bt_hci_s *hci)
+{
+ read_inquiry_mode_rp params = {
+ .status = HCI_SUCCESS,
+ .mode = hci->lm.inquiry_mode,
+ };
+
+ bt_hci_event_complete(hci, &params, READ_INQUIRY_MODE_RP_SIZE);
+}
+
+static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci,
+ uint16_t handle, int packets)
+{
+ uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1];
+ evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1);
+
+ params->num_hndl = 1;
+ params->connection->handle = HNDL(handle);
+ params->connection->num_packets = cpu_to_le16(packets);
+
+ bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1));
+}
+
+static void bt_submit_hci(struct HCIInfo *info,
+ const uint8_t *data, int length)
+{
+ struct bt_hci_s *hci = hci_from_info(info);
+ uint16_t cmd;
+ int paramlen, i;
+
+ if (length < HCI_COMMAND_HDR_SIZE)
+ goto short_hci;
+
+ memcpy(&hci->last_cmd, data, 2);
+
+ cmd = (data[1] << 8) | data[0];
+ paramlen = data[2];
+ if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0) /* NOP */
+ return;
+
+ data += HCI_COMMAND_HDR_SIZE;
+ length -= HCI_COMMAND_HDR_SIZE;
+
+ if (paramlen > length)
+ return;
+
+#define PARAM(cmd, param) (((cmd##_cp *) data)->param)
+#define PARAM16(cmd, param) le16_to_cpup(&PARAM(cmd, param))
+#define PARAMHANDLE(cmd) HNDL(PARAM(cmd, handle))
+#define LENGTH_CHECK(cmd) if (length < sizeof(cmd##_cp)) goto short_hci
+ /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp
+ * needs to be updated every time a command is implemented here! */
+ switch (cmd) {
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY):
+ LENGTH_CHECK(inquiry);
+
+ if (PARAM(inquiry, length) < 1) {
+ bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ hci->lm.inquire = 1;
+ hci->lm.periodic = 0;
+ hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX;
+ hci->lm.responses = 0;
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_inquiry_start(hci, PARAM(inquiry, length));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
+ if (!hci->lm.inquire || hci->lm.periodic) {
+ fprintf(stderr, "%s: Inquiry Cancel should only be issued after "
+ "the Inquiry command has been issued, a Command "
+ "Status event has been received for the Inquiry "
+ "command, and before the Inquiry Complete event "
+ "occurs", __FUNCTION__);
+ bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED);
+ break;
+ }
+
+ hci->lm.inquire = 0;
+ qemu_del_timer(hci->lm.inquiry_done);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY):
+ LENGTH_CHECK(periodic_inquiry);
+
+ if (!(PARAM(periodic_inquiry, length) <
+ PARAM16(periodic_inquiry, min_period) &&
+ PARAM16(periodic_inquiry, min_period) <
+ PARAM16(periodic_inquiry, max_period)) ||
+ PARAM(periodic_inquiry, length) < 1 ||
+ PARAM16(periodic_inquiry, min_period) < 2 ||
+ PARAM16(periodic_inquiry, max_period) < 3) {
+ bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ hci->lm.inquire = 1;
+ hci->lm.periodic = 1;
+ hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp);
+ hci->lm.responses = 0;
+ hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY):
+ if (!hci->lm.inquire || !hci->lm.periodic) {
+ fprintf(stderr, "%s: Inquiry Cancel should only be issued after "
+ "the Inquiry command has been issued, a Command "
+ "Status event has been received for the Inquiry "
+ "command, and before the Inquiry Complete event "
+ "occurs", __FUNCTION__);
+ bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED);
+ break;
+ }
+ hci->lm.inquire = 0;
+ qemu_del_timer(hci->lm.inquiry_done);
+ qemu_del_timer(hci->lm.inquiry_next);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN):
+ LENGTH_CHECK(create_conn);
+
+ if (hci->lm.connecting >= HCI_HANDLES_MAX) {
+ bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES);
+ break;
+ }
+ bt_hci_event_status(hci, HCI_SUCCESS);
+
+ if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr)))
+ bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT):
+ LENGTH_CHECK(disconnect);
+
+ if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) {
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+ }
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_disconnect(hci, PARAMHANDLE(disconnect),
+ PARAM(disconnect, reason));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL):
+ LENGTH_CHECK(create_conn_cancel);
+
+ if (bt_hci_lmp_connection_ready(hci,
+ &PARAM(create_conn_cancel, bdaddr))) {
+ for (i = 0; i < HCI_HANDLES_MAX; i ++)
+ if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link &&
+ !bacmp(&hci->lm.handle[i].link->slave->bd_addr,
+ &PARAM(create_conn_cancel, bdaddr)))
+ break;
+
+ bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ?
+ HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION,
+ &PARAM(create_conn_cancel, bdaddr));
+ } else
+ bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS,
+ &PARAM(create_conn_cancel, bdaddr));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ):
+ LENGTH_CHECK(accept_conn_req);
+
+ if (!hci->conn_req_host ||
+ bacmp(&PARAM(accept_conn_req, bdaddr),
+ &hci->conn_req_host->bd_addr)) {
+ bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_connection_accept(hci, hci->conn_req_host);
+ hci->conn_req_host = NULL;
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ):
+ LENGTH_CHECK(reject_conn_req);
+
+ if (!hci->conn_req_host ||
+ bacmp(&PARAM(reject_conn_req, bdaddr),
+ &hci->conn_req_host->bd_addr)) {
+ bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_connection_reject(hci, hci->conn_req_host,
+ PARAM(reject_conn_req, reason));
+ bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr);
+ hci->conn_req_host = NULL;
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED):
+ LENGTH_CHECK(auth_requested);
+
+ if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ else {
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested));
+ }
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT):
+ LENGTH_CHECK(set_conn_encrypt);
+
+ if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ else {
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_event_encrypt_change(hci,
+ PARAMHANDLE(set_conn_encrypt),
+ PARAM(set_conn_encrypt, encrypt));
+ }
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ):
+ LENGTH_CHECK(remote_name_req);
+
+ if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL):
+ LENGTH_CHECK(remote_name_req_cancel);
+
+ bt_hci_event_complete_name_cancel(hci,
+ &PARAM(remote_name_req_cancel, bdaddr));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES):
+ LENGTH_CHECK(read_remote_features);
+
+ if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES):
+ LENGTH_CHECK(read_remote_ext_features);
+
+ if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ else {
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ bt_hci_event_read_remote_ext_features(hci,
+ PARAMHANDLE(read_remote_ext_features));
+ }
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION):
+ LENGTH_CHECK(read_remote_version);
+
+ if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET):
+ LENGTH_CHECK(read_clock_offset);
+
+ if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset)))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE):
+ LENGTH_CHECK(read_lmp_handle);
+
+ /* TODO: */
+ bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle));
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE):
+ LENGTH_CHECK(hold_mode);
+
+ if (PARAM16(hold_mode, min_interval) >
+ PARAM16(hold_mode, max_interval) ||
+ PARAM16(hold_mode, min_interval) < 0x0002 ||
+ PARAM16(hold_mode, max_interval) > 0xff00 ||
+ (PARAM16(hold_mode, min_interval) & 1) ||
+ (PARAM16(hold_mode, max_interval) & 1)) {
+ bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode),
+ PARAM16(hold_mode, max_interval),
+ acl_hold))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE):
+ LENGTH_CHECK(park_mode);
+
+ if (PARAM16(park_mode, min_interval) >
+ PARAM16(park_mode, max_interval) ||
+ PARAM16(park_mode, min_interval) < 0x000e ||
+ (PARAM16(park_mode, min_interval) & 1) ||
+ (PARAM16(park_mode, max_interval) & 1)) {
+ bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode),
+ PARAM16(park_mode, max_interval),
+ acl_parked))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE):
+ LENGTH_CHECK(exit_park_mode);
+
+ if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode),
+ acl_parked))
+ bt_hci_event_status(hci, HCI_NO_CONNECTION);
+ break;
+
+ case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY):
+ LENGTH_CHECK(role_discovery);
+
+ if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery)))
+ bt_hci_event_complete_role_discovery(hci,
+ HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0);
+ else
+ bt_hci_event_complete_role_discovery(hci,
+ HCI_SUCCESS, PARAMHANDLE(role_discovery),
+ bt_hci_role_master(hci,
+ PARAMHANDLE(role_discovery)));
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK):
+ LENGTH_CHECK(set_event_mask);
+
+ memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET):
+ bt_hci_reset(hci);
+ bt_hci_event_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT):
+ if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL)
+ /* No length check */;
+ else
+ LENGTH_CHECK(set_event_flt);
+
+ /* Filters are not implemented */
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH):
+ LENGTH_CHECK(flush);
+
+ if (bt_hci_handle_bad(hci, PARAMHANDLE(flush)))
+ bt_hci_event_complete_flush(hci,
+ HCI_NO_CONNECTION, PARAMHANDLE(flush));
+ else {
+ /* TODO: ordering? */
+ bt_hci_event(hci, EVT_FLUSH_OCCURRED,
+ &PARAM(flush, handle),
+ EVT_FLUSH_OCCURRED_SIZE);
+ bt_hci_event_complete_flush(hci,
+ HCI_SUCCESS, PARAMHANDLE(flush));
+ }
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
+ LENGTH_CHECK(change_local_name);
+
+ if (hci->device.lmp_name)
+ qemu_free((void *) hci->device.lmp_name);
+ hci->device.lmp_name = qemu_strndup(PARAM(change_local_name, name),
+ sizeof(PARAM(change_local_name, name)));
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME):
+ bt_hci_event_complete_read_local_name(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT):
+ bt_hci_event_complete_read_conn_accept_timeout(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT):
+ /* TODO */
+ LENGTH_CHECK(write_conn_accept_timeout);
+
+ if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 ||
+ PARAM16(write_conn_accept_timeout, timeout) > 0xb540) {
+ bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE):
+ bt_hci_event_complete_read_scan_enable(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE):
+ LENGTH_CHECK(write_scan_enable);
+
+ /* TODO: check that the remaining bits are all 0 */
+ hci->device.inquiry_scan =
+ !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY);
+ hci->device.page_scan =
+ !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV):
+ bt_hci_event_complete_read_local_class(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV):
+ LENGTH_CHECK(write_class_of_dev);
+
+ memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class),
+ sizeof(PARAM(write_class_of_dev, dev_class)));
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING):
+ bt_hci_event_complete_voice_setting(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING):
+ LENGTH_CHECK(write_voice_setting);
+
+ hci->voice_setting = PARAM(write_voice_setting, voice_setting);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS):
+ if (length < data[0] * 2 + 1)
+ goto short_hci;
+
+ for (i = 0; i < data[0]; i ++)
+ if (bt_hci_handle_bad(hci,
+ data[i * 2 + 1] | (data[i * 2 + 2] << 8)))
+ bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE):
+ /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40)
+ * else
+ * goto unknown_command */
+ bt_hci_event_complete_read_inquiry_mode(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE):
+ /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80)
+ * else
+ * goto unknown_command */
+ LENGTH_CHECK(write_inquiry_mode);
+
+ if (PARAM(write_inquiry_mode, mode) > 0x01) {
+ bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+
+ hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode);
+ bt_hci_event_complete_status(hci, HCI_SUCCESS);
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION):
+ bt_hci_read_local_version_rp(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS):
+ bt_hci_read_local_commands_rp(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES):
+ bt_hci_read_local_features_rp(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES):
+ LENGTH_CHECK(read_local_ext_features);
+
+ bt_hci_read_local_ext_features_rp(hci,
+ PARAM(read_local_ext_features, page_num));
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE):
+ bt_hci_read_buffer_size_rp(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE):
+ bt_hci_read_country_code_rp(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR):
+ bt_hci_read_bd_addr_rp(hci);
+ break;
+
+ case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY):
+ LENGTH_CHECK(read_link_quality);
+
+ bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality));
+ break;
+
+ default:
+ bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND);
+ break;
+
+ short_hci:
+ fprintf(stderr, "%s: HCI packet too short (%iB)\n",
+ __FUNCTION__, length);
+ bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+ break;
+ }
+}
+
+/* We could perform fragmentation here, we can't do "recombination" because
+ * at this layer the length of the payload is not know ahead, so we only
+ * know that a packet contained the last fragment of the SDU when the next
+ * SDU starts. */
+static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle,
+ const uint8_t *data, int start, int len)
+{
+ struct hci_acl_hdr *pkt = (void *) hci->acl_buf;
+
+ /* TODO: packet flags */
+ /* TODO: avoid memcpy'ing */
+
+ if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) {
+ fprintf(stderr, "%s: can't take ACL packets %i bytes long\n",
+ __FUNCTION__, len);
+ return;
+ }
+ memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len);
+
+ pkt->handle = cpu_to_le16(
+ acl_handle_pack(handle, start ? ACL_START : ACL_CONT));
+ pkt->dlen = cpu_to_le16(len);
+ hci->info.acl_recv(hci->info.opaque,
+ hci->acl_buf, len + HCI_ACL_HDR_SIZE);
+}
+
+static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink,
+ const uint8_t *data, int start, int len)
+{
+ struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
+
+ bt_hci_lmp_acl_data(hci_from_device(btlink->slave),
+ link->handle, data, start, len);
+}
+
+static void bt_hci_lmp_acl_data_host(struct bt_link_s *link,
+ const uint8_t *data, int start, int len)
+{
+ bt_hci_lmp_acl_data(hci_from_device(link->host),
+ link->handle, data, start, len);
+}
+
+static void bt_submit_acl(struct HCIInfo *info,
+ const uint8_t *data, int length)
+{
+ struct bt_hci_s *hci = hci_from_info(info);
+ uint16_t handle;
+ int datalen, flags;
+ struct bt_link_s *link;
+
+ if (length < HCI_ACL_HDR_SIZE) {
+ fprintf(stderr, "%s: ACL packet too short (%iB)\n",
+ __FUNCTION__, length);
+ return;
+ }
+
+ handle = acl_handle((data[1] << 8) | data[0]);
+ flags = acl_flags((data[1] << 8) | data[0]);
+ datalen = (data[3] << 8) | data[2];
+ data += HCI_ACL_HDR_SIZE;
+ length -= HCI_ACL_HDR_SIZE;
+
+ if (bt_hci_handle_bad(hci, handle)) {
+ fprintf(stderr, "%s: invalid ACL handle %03x\n",
+ __FUNCTION__, handle);
+ /* TODO: signal an error */
+ return;
+ }
+ handle &= ~HCI_HANDLE_OFFSET;
+
+ if (datalen > length) {
+ fprintf(stderr, "%s: ACL packet too short (%iB < %iB)\n",
+ __FUNCTION__, length, datalen);
+ return;
+ }
+
+ link = hci->lm.handle[handle].link;
+
+ if ((flags & ~3) == ACL_ACTIVE_BCAST) {
+ if (!hci->asb_handle)
+ hci->asb_handle = handle;
+ else if (handle != hci->asb_handle) {
+ fprintf(stderr, "%s: Bad handle %03x in Active Slave Broadcast\n",
+ __FUNCTION__, handle);
+ /* TODO: signal an error */
+ return;
+ }
+
+ /* TODO */
+ }
+
+ if ((flags & ~3) == ACL_PICO_BCAST) {
+ if (!hci->psb_handle)
+ hci->psb_handle = handle;
+ else if (handle != hci->psb_handle) {
+ fprintf(stderr, "%s: Bad handle %03x in Parked Slave Broadcast\n",
+ __FUNCTION__, handle);
+ /* TODO: signal an error */
+ return;
+ }
+
+ /* TODO */
+ }
+
+ /* TODO: increase counter and send EVT_NUM_COMP_PKTS */
+ bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1);
+
+ /* Do this last as it can trigger further events even in this HCI */
+ hci->lm.handle[handle].lmp_acl_data(link, data,
+ (flags & 3) == ACL_START, length);
+}
+
+static void bt_submit_sco(struct HCIInfo *info,
+ const uint8_t *data, int length)
+{
+ struct bt_hci_s *hci = hci_from_info(info);
+ uint16_t handle;
+ int datalen;
+
+ if (length < 3)
+ return;
+
+ handle = acl_handle((data[1] << 8) | data[0]);
+ datalen = data[2];
+ length -= 3;
+
+ if (bt_hci_handle_bad(hci, handle)) {
+ fprintf(stderr, "%s: invalid SCO handle %03x\n",
+ __FUNCTION__, handle);
+ return;
+ }
+
+ if (datalen > length) {
+ fprintf(stderr, "%s: SCO packet too short (%iB < %iB)\n",
+ __FUNCTION__, length, datalen);
+ return;
+ }
+
+ /* TODO */
+
+ /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous
+ * Flow Control is enabled.
+ * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and
+ * page 514.) */
+}
+
+static uint8_t *bt_hci_evt_packet(void *opaque)
+{
+ /* TODO: allocate a packet from upper layer */
+ struct bt_hci_s *s = opaque;
+
+ return s->evt_buf;
+}
+
+static void bt_hci_evt_submit(void *opaque, int len)
+{
+ /* TODO: notify upper layer */
+ struct bt_hci_s *s = opaque;
+
+ s->info.evt_recv(s->info.opaque, s->evt_buf, len);
+}
+
+static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr)
+{
+ struct bt_hci_s *hci = hci_from_info(info);
+
+ bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr);
+ return 0;
+}
+
+static void bt_hci_done(struct HCIInfo *info);
+static void bt_hci_destroy(struct bt_device_s *dev)
+{
+ struct bt_hci_s *hci = hci_from_device(dev);
+
+ bt_hci_done(&hci->info);
+}
+
+struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net)
+{
+ struct bt_hci_s *s = qemu_mallocz(sizeof(struct bt_hci_s));
+
+ s->lm.inquiry_done = qemu_new_timer(vm_clock, bt_hci_inquiry_done, s);
+ s->lm.inquiry_next = qemu_new_timer(vm_clock, bt_hci_inquiry_next, s);
+ s->conn_accept_timer =
+ qemu_new_timer(vm_clock, bt_hci_conn_accept_timeout, s);
+
+ s->evt_packet = bt_hci_evt_packet;
+ s->evt_submit = bt_hci_evt_submit;
+ s->opaque = s;
+
+ bt_device_init(&s->device, net);
+ s->device.lmp_connection_request = bt_hci_lmp_connection_request;
+ s->device.lmp_connection_complete = bt_hci_lmp_connection_complete;
+ s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host;
+ s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave;
+ s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave;
+ s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host;
+ s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave;
+
+ /* Keep updated! */
+ /* Also keep in sync with supported commands bitmask in
+ * bt_hci_read_local_commands_rp */
+ s->device.lmp_caps = 0x8000199b7e85355fll;
+
+ bt_hci_reset(s);
+
+ s->info.cmd_send = bt_submit_hci;
+ s->info.sco_send = bt_submit_sco;
+ s->info.acl_send = bt_submit_acl;
+ s->info.bdaddr_set = bt_hci_bdaddr_set;
+
+ s->device.handle_destroy = bt_hci_destroy;
+
+ return &s->info;
+}
+
+static void bt_hci_done(struct HCIInfo *info)
+{
+ struct bt_hci_s *hci = hci_from_info(info);
+ int handle;
+
+ bt_device_done(&hci->device);
+
+ if (hci->device.lmp_name)
+ qemu_free((void *) hci->device.lmp_name);
+
+ /* Be gentle and send DISCONNECT to all connected peers and those
+ * currently waiting for us to accept or reject a connection request.
+ * This frees the links. */
+ if (hci->conn_req_host) {
+ bt_hci_connection_reject(hci,
+ hci->conn_req_host, HCI_OE_POWER_OFF);
+ return;
+ }
+
+ for (handle = HCI_HANDLE_OFFSET;
+ handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++)
+ if (!bt_hci_handle_bad(hci, handle))
+ bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF);
+
+ /* TODO: this is not enough actually, there may be slaves from whom
+ * we have requested a connection who will soon (or not) respond with
+ * an accept or a reject, so we should also check if hci->lm.connecting
+ * is non-zero and if so, avoid freeing the hci but otherwise disappear
+ * from all qemu social life (e.g. stop scanning and request to be
+ * removed from s->device.net) and arrange for
+ * s->device.lmp_connection_complete to free the remaining bits once
+ * hci->lm.awaiting_bdaddr[] is empty. */
+
+ qemu_free_timer(hci->lm.inquiry_done);
+ qemu_free_timer(hci->lm.inquiry_next);
+ qemu_free_timer(hci->conn_accept_timer);
+
+ qemu_free(hci);
+}
diff --git a/hw/bt-hid.c b/hw/bt-hid.c
new file mode 100644
index 0000000..abdfd35
--- /dev/null
+++ b/hw/bt-hid.c
@@ -0,0 +1,571 @@
+/*
+ * QEMU Bluetooth HID Profile wrapper for USB HID.
+ *
+ * Copyright (C) 2007-2008 OpenMoko, Inc.
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "bt.h"
+
+enum hid_transaction_req {
+ BT_HANDSHAKE = 0x0,
+ BT_HID_CONTROL = 0x1,
+ BT_GET_REPORT = 0x4,
+ BT_SET_REPORT = 0x5,
+ BT_GET_PROTOCOL = 0x6,
+ BT_SET_PROTOCOL = 0x7,
+ BT_GET_IDLE = 0x8,
+ BT_SET_IDLE = 0x9,
+ BT_DATA = 0xa,
+ BT_DATC = 0xb,
+};
+
+enum hid_transaction_handshake {
+ BT_HS_SUCCESSFUL = 0x0,
+ BT_HS_NOT_READY = 0x1,
+ BT_HS_ERR_INVALID_REPORT_ID = 0x2,
+ BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3,
+ BT_HS_ERR_INVALID_PARAMETER = 0x4,
+ BT_HS_ERR_UNKNOWN = 0xe,
+ BT_HS_ERR_FATAL = 0xf,
+};
+
+enum hid_transaction_control {
+ BT_HC_NOP = 0x0,
+ BT_HC_HARD_RESET = 0x1,
+ BT_HC_SOFT_RESET = 0x2,
+ BT_HC_SUSPEND = 0x3,
+ BT_HC_EXIT_SUSPEND = 0x4,
+ BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5,
+};
+
+enum hid_protocol {
+ BT_HID_PROTO_BOOT = 0,
+ BT_HID_PROTO_REPORT = 1,
+};
+
+enum hid_boot_reportid {
+ BT_HID_BOOT_INVALID = 0,
+ BT_HID_BOOT_KEYBOARD,
+ BT_HID_BOOT_MOUSE,
+};
+
+enum hid_data_pkt {
+ BT_DATA_OTHER = 0,
+ BT_DATA_INPUT,
+ BT_DATA_OUTPUT,
+ BT_DATA_FEATURE,
+};
+
+#define BT_HID_MTU 48
+
+/* HID interface requests */
+#define GET_REPORT 0xa101
+#define GET_IDLE 0xa102
+#define GET_PROTOCOL 0xa103
+#define SET_REPORT 0x2109
+#define SET_IDLE 0x210a
+#define SET_PROTOCOL 0x210b
+
+struct bt_hid_device_s {
+ struct bt_l2cap_device_s btdev;
+ struct bt_l2cap_conn_params_s *control;
+ struct bt_l2cap_conn_params_s *interrupt;
+ USBDevice *usbdev;
+
+ int proto;
+ int connected;
+ int data_type;
+ int intr_state;
+ struct {
+ int len;
+ uint8_t buffer[1024];
+ } dataother, datain, dataout, feature, intrdataout;
+ enum {
+ bt_state_ready,
+ bt_state_transaction,
+ bt_state_suspend,
+ } state;
+};
+
+static void bt_hid_reset(struct bt_hid_device_s *s)
+{
+ struct bt_scatternet_s *net = s->btdev.device.net;
+
+ /* Go as far as... */
+ bt_l2cap_device_done(&s->btdev);
+ bt_l2cap_device_init(&s->btdev, net);
+
+ s->usbdev->info->handle_reset(s->usbdev);
+ s->proto = BT_HID_PROTO_REPORT;
+ s->state = bt_state_ready;
+ s->dataother.len = 0;
+ s->datain.len = 0;
+ s->dataout.len = 0;
+ s->feature.len = 0;
+ s->intrdataout.len = 0;
+ s->intr_state = 0;
+}
+
+static int bt_hid_out(struct bt_hid_device_s *s)
+{
+ USBPacket p;
+
+ if (s->data_type == BT_DATA_OUTPUT) {
+ p.pid = USB_TOKEN_OUT;
+ p.devep = 1;
+ p.data = s->dataout.buffer;
+ p.len = s->dataout.len;
+ s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p);
+
+ return s->dataout.len;
+ }
+
+ if (s->data_type == BT_DATA_FEATURE) {
+ /* XXX:
+ * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
+ * or a SET_REPORT? */
+ p.devep = 0;
+ }
+
+ return -1;
+}
+
+static int bt_hid_in(struct bt_hid_device_s *s)
+{
+ USBPacket p;
+
+ p.pid = USB_TOKEN_IN;
+ p.devep = 1;
+ p.data = s->datain.buffer;
+ p.len = sizeof(s->datain.buffer);
+ s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p);
+
+ return s->datain.len;
+}
+
+static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
+{
+ *s->control->sdu_out(s->control, 1) =
+ (BT_HANDSHAKE << 4) | result;
+ s->control->sdu_submit(s->control);
+}
+
+static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
+{
+ *s->control->sdu_out(s->control, 1) =
+ (BT_HID_CONTROL << 4) | operation;
+ s->control->sdu_submit(s->control);
+}
+
+static void bt_hid_disconnect(struct bt_hid_device_s *s)
+{
+ /* Disconnect s->control and s->interrupt */
+}
+
+static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
+ const uint8_t *data, int len)
+{
+ uint8_t *pkt, hdr = (BT_DATA << 4) | type;
+ int plen;
+
+ do {
+ plen = MIN(len, ch->remote_mtu - 1);
+ pkt = ch->sdu_out(ch, plen + 1);
+
+ pkt[0] = hdr;
+ if (plen)
+ memcpy(pkt + 1, data, plen);
+ ch->sdu_submit(ch);
+
+ len -= plen;
+ data += plen;
+ hdr = (BT_DATC << 4) | type;
+ } while (plen == ch->remote_mtu - 1);
+}
+
+static void bt_hid_control_transaction(struct bt_hid_device_s *s,
+ const uint8_t *data, int len)
+{
+ uint8_t type, parameter;
+ int rlen, ret = -1;
+ if (len < 1)
+ return;
+
+ type = data[0] >> 4;
+ parameter = data[0] & 0xf;
+
+ switch (type) {
+ case BT_HANDSHAKE:
+ case BT_DATA:
+ switch (parameter) {
+ default:
+ /* These are not expected to be sent this direction. */
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case BT_HID_CONTROL:
+ if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
+ s->state == bt_state_transaction)) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ switch (parameter) {
+ case BT_HC_NOP:
+ break;
+ case BT_HC_HARD_RESET:
+ case BT_HC_SOFT_RESET:
+ bt_hid_reset(s);
+ break;
+ case BT_HC_SUSPEND:
+ if (s->state == bt_state_ready)
+ s->state = bt_state_suspend;
+ else
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ case BT_HC_EXIT_SUSPEND:
+ if (s->state == bt_state_suspend)
+ s->state = bt_state_ready;
+ else
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ case BT_HC_VIRTUAL_CABLE_UNPLUG:
+ bt_hid_disconnect(s);
+ break;
+ default:
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ }
+ break;
+
+ case BT_GET_REPORT:
+ /* No ReportIDs declared. */
+ if (((parameter & 8) && len != 3) ||
+ (!(parameter & 8) && len != 1) ||
+ s->state != bt_state_ready) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (parameter & 8)
+ rlen = data[2] | (data[3] << 8);
+ else
+ rlen = INT_MAX;
+ switch (parameter & 3) {
+ case BT_DATA_OTHER:
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ case BT_DATA_INPUT:
+ /* Here we can as well poll s->usbdev */
+ bt_hid_send_data(s->control, BT_DATA_INPUT,
+ s->datain.buffer, MIN(rlen, s->datain.len));
+ break;
+ case BT_DATA_OUTPUT:
+ bt_hid_send_data(s->control, BT_DATA_OUTPUT,
+ s->dataout.buffer, MIN(rlen, s->dataout.len));
+ break;
+ case BT_DATA_FEATURE:
+ bt_hid_send_data(s->control, BT_DATA_FEATURE,
+ s->feature.buffer, MIN(rlen, s->feature.len));
+ break;
+ }
+ break;
+
+ case BT_SET_REPORT:
+ if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
+ (parameter & 3) == BT_DATA_OTHER ||
+ (parameter & 3) == BT_DATA_INPUT) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ s->data_type = parameter & 3;
+ if (s->data_type == BT_DATA_OUTPUT) {
+ s->dataout.len = len - 1;
+ memcpy(s->dataout.buffer, data + 1, s->dataout.len);
+ } else {
+ s->feature.len = len - 1;
+ memcpy(s->feature.buffer, data + 1, s->feature.len);
+ }
+ if (len == BT_HID_MTU)
+ s->state = bt_state_transaction;
+ else
+ bt_hid_out(s);
+ break;
+
+ case BT_GET_PROTOCOL:
+ if (len != 1 || s->state == bt_state_transaction) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ *s->control->sdu_out(s->control, 1) = s->proto;
+ s->control->sdu_submit(s->control);
+ break;
+
+ case BT_SET_PROTOCOL:
+ if (len != 1 || s->state == bt_state_transaction ||
+ (parameter != BT_HID_PROTO_BOOT &&
+ parameter != BT_HID_PROTO_REPORT)) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ s->proto = parameter;
+ s->usbdev->info->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0,
+ NULL);
+ ret = BT_HS_SUCCESSFUL;
+ break;
+
+ case BT_GET_IDLE:
+ if (len != 1 || s->state == bt_state_transaction) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ s->usbdev->info->handle_control(s->usbdev, GET_IDLE, 0, 0, 1,
+ s->control->sdu_out(s->control, 1));
+ s->control->sdu_submit(s->control);
+ break;
+
+ case BT_SET_IDLE:
+ if (len != 2 || s->state == bt_state_transaction) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+
+ /* 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,
+ SET_IDLE, data[1], 0, 0, NULL) ?
+ BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER;
+ /* XXX: Does this generate a handshake? */
+ break;
+
+ case BT_DATC:
+ if (len > BT_HID_MTU || s->state != bt_state_transaction) {
+ ret = BT_HS_ERR_INVALID_PARAMETER;
+ break;
+ }
+ if (s->data_type == BT_DATA_OUTPUT) {
+ memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
+ s->dataout.len += len - 1;
+ } else {
+ memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
+ s->feature.len += len - 1;
+ }
+ if (len < BT_HID_MTU) {
+ bt_hid_out(s);
+ s->state = bt_state_ready;
+ }
+ break;
+
+ default:
+ ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
+ }
+
+ if (ret != -1)
+ bt_hid_send_handshake(s, ret);
+}
+
+static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
+{
+ struct bt_hid_device_s *hid = opaque;
+
+ bt_hid_control_transaction(hid, data, len);
+}
+
+static void bt_hid_datain(void *opaque)
+{
+ struct bt_hid_device_s *hid = opaque;
+
+ /* If suspended, wake-up and send a wake-up event first. We might
+ * want to also inspect the input report and ignore event like
+ * mouse movements until a button event occurs. */
+ if (hid->state == bt_state_suspend) {
+ hid->state = bt_state_ready;
+ }
+
+ if (bt_hid_in(hid) > 0)
+ /* TODO: when in boot-mode precede any Input reports with the ReportID
+ * byte, here and in GetReport/SetReport on the Control channel. */
+ bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
+ hid->datain.buffer, hid->datain.len);
+}
+
+static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
+{
+ struct bt_hid_device_s *hid = opaque;
+
+ if (len > BT_HID_MTU || len < 1)
+ goto bad;
+ if ((data[0] & 3) != BT_DATA_OUTPUT)
+ goto bad;
+ if ((data[0] >> 4) == BT_DATA) {
+ if (hid->intr_state)
+ goto bad;
+
+ hid->data_type = BT_DATA_OUTPUT;
+ hid->intrdataout.len = 0;
+ } else if ((data[0] >> 4) == BT_DATC) {
+ if (!hid->intr_state)
+ goto bad;
+ } else
+ goto bad;
+
+ memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
+ hid->intrdataout.len += len - 1;
+ hid->intr_state = (len == BT_HID_MTU);
+ if (!hid->intr_state) {
+ memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
+ hid->dataout.len = hid->intrdataout.len);
+ bt_hid_out(hid);
+ }
+
+ return;
+bad:
+ fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
+ __FUNCTION__);
+}
+
+/* "Virtual cable" plug/unplug event. */
+static void bt_hid_connected_update(struct bt_hid_device_s *hid)
+{
+ int prev = hid->connected;
+
+ hid->connected = hid->control && hid->interrupt;
+
+ /* Stop page-/inquiry-scanning when a host is connected. */
+ hid->btdev.device.page_scan = !hid->connected;
+ hid->btdev.device.inquiry_scan = !hid->connected;
+
+ if (hid->connected && !prev) {
+ hid->usbdev->info->handle_reset(hid->usbdev);
+ hid->proto = BT_HID_PROTO_REPORT;
+ }
+
+ /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
+ * isn't destroyed yet, in case we're being called from handle_destroy) */
+}
+
+static void bt_hid_close_control(void *opaque)
+{
+ struct bt_hid_device_s *hid = opaque;
+
+ hid->control = NULL;
+ bt_hid_connected_update(hid);
+}
+
+static void bt_hid_close_interrupt(void *opaque)
+{
+ struct bt_hid_device_s *hid = opaque;
+
+ hid->interrupt = NULL;
+ bt_hid_connected_update(hid);
+}
+
+static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
+ struct bt_l2cap_conn_params_s *params)
+{
+ struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
+
+ if (hid->control)
+ return 1;
+
+ hid->control = params;
+ hid->control->opaque = hid;
+ hid->control->close = bt_hid_close_control;
+ hid->control->sdu_in = bt_hid_control_sdu;
+
+ bt_hid_connected_update(hid);
+
+ return 0;
+}
+
+static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
+ struct bt_l2cap_conn_params_s *params)
+{
+ struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
+
+ if (hid->interrupt)
+ return 1;
+
+ hid->interrupt = params;
+ hid->interrupt->opaque = hid;
+ hid->interrupt->close = bt_hid_close_interrupt;
+ hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
+
+ bt_hid_connected_update(hid);
+
+ return 0;
+}
+
+static void bt_hid_destroy(struct bt_device_s *dev)
+{
+ struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
+
+ if (hid->connected)
+ bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
+ bt_l2cap_device_done(&hid->btdev);
+
+ hid->usbdev->info->handle_destroy(hid->usbdev);
+
+ qemu_free(hid);
+}
+
+enum peripheral_minor_class {
+ class_other = 0 << 4,
+ class_keyboard = 1 << 4,
+ class_pointing = 2 << 4,
+ class_combo = 3 << 4,
+};
+
+static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
+ USBDevice *dev, enum peripheral_minor_class minor)
+{
+ struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s));
+ uint32_t class =
+ /* Format type */
+ (0 << 0) |
+ /* Device class */
+ (minor << 2) |
+ (5 << 8) | /* "Peripheral" */
+ /* Service classes */
+ (1 << 13) | /* Limited discoverable mode */
+ (1 << 19); /* Capturing device (?) */
+
+ bt_l2cap_device_init(&s->btdev, net);
+ bt_l2cap_sdp_init(&s->btdev);
+ bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
+ BT_HID_MTU, bt_hid_new_control_ch);
+ bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
+ BT_HID_MTU, bt_hid_new_interrupt_ch);
+
+ s->usbdev = dev;
+ s->btdev.device.lmp_name = s->usbdev->product_desc;
+ usb_hid_datain_cb(s->usbdev, s, bt_hid_datain);
+
+ s->btdev.device.handle_destroy = bt_hid_destroy;
+
+ s->btdev.device.class[0] = (class >> 0) & 0xff;
+ s->btdev.device.class[1] = (class >> 8) & 0xff;
+ s->btdev.device.class[2] = (class >> 16) & 0xff;
+
+ return &s->btdev.device;
+}
+
+struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
+{
+ USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd");
+ return bt_hid_init(net, dev, class_keyboard);
+}
diff --git a/hw/bt-l2cap.c b/hw/bt-l2cap.c
new file mode 100644
index 0000000..7e2f668
--- /dev/null
+++ b/hw/bt-l2cap.c
@@ -0,0 +1,1362 @@
+/*
+ * QEMU Bluetooth L2CAP logic.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "bt.h"
+
+#define L2CAP_CID_MAX 0x100 /* Between 0x40 and 0x10000 */
+
+struct l2cap_instance_s {
+ struct bt_link_s *link;
+ struct bt_l2cap_device_s *dev;
+ int role;
+
+ uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4)));
+ int frame_in_len;
+
+ uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4)));
+ int frame_out_len;
+
+ /* Signalling channel timers. They exist per-request but we can make
+ * sure we have no more than one outstanding request at any time. */
+ QEMUTimer *rtx;
+ QEMUTimer *ertx;
+
+ int last_id;
+ int next_id;
+
+ struct l2cap_chan_s {
+ struct bt_l2cap_conn_params_s params;
+
+ void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid,
+ const l2cap_hdr *hdr, int len);
+ int mps;
+ int min_mtu;
+
+ struct l2cap_instance_s *l2cap;
+
+ /* Only allocated channels */
+ uint16_t remote_cid;
+#define L2CAP_CFG_INIT 2
+#define L2CAP_CFG_ACC 1
+ int config_req_id; /* TODO: handle outgoing requests generically */
+ int config;
+
+ /* Only connection-oriented channels. Note: if we allow the tx and
+ * rx traffic to be in different modes at any time, we need two. */
+ int mode;
+
+ /* Only flow-controlled, connection-oriented channels */
+ uint8_t sdu[65536]; /* TODO: dynamically allocate */
+ int len_cur, len_total;
+ int rexmit;
+ int monitor_timeout;
+ QEMUTimer *monitor_timer;
+ QEMUTimer *retransmission_timer;
+ } *cid[L2CAP_CID_MAX];
+ /* The channel state machine states map as following:
+ * CLOSED -> !cid[N]
+ * WAIT_CONNECT -> never occurs
+ * WAIT_CONNECT_RSP -> never occurs
+ * CONFIG -> cid[N] && config < 3
+ * WAIT_CONFIG -> never occurs, cid[N] && config == 0 && !config_r
+ * WAIT_SEND_CONFIG -> never occurs, cid[N] && config == 1 && !config_r
+ * WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id
+ * WAIT_CONFIG_RSP -> cid[N] && config == 1 && config_req_id
+ * WAIT_CONFIG_REQ -> cid[N] && config == 2
+ * OPEN -> cid[N] && config == 3
+ * WAIT_DISCONNECT -> never occurs
+ */
+
+ struct l2cap_chan_s signalling_ch;
+ struct l2cap_chan_s group_ch;
+};
+
+struct slave_l2cap_instance_s {
+ struct bt_link_s link; /* Underlying logical link (ACL) */
+ struct l2cap_instance_s l2cap;
+};
+
+struct bt_l2cap_psm_s {
+ int psm;
+ int min_mtu;
+ int (*new_channel)(struct bt_l2cap_device_s *device,
+ struct bt_l2cap_conn_params_s *params);
+ struct bt_l2cap_psm_s *next;
+};
+
+static const uint16_t l2cap_fcs16_table[256] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
+};
+
+static uint16_t l2cap_fcs16(const uint8_t *message, int len)
+{
+ uint16_t fcs = 0x0000;
+
+ while (len --)
+#if 0
+ {
+ int i;
+
+ fcs ^= *message ++;
+ for (i = 8; i; -- i)
+ if (fcs & 1)
+ fcs = (fcs >> 1) ^ 0xa001;
+ else
+ fcs = (fcs >> 1);
+ }
+#else
+ fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff];
+#endif
+
+ return fcs;
+}
+
+/* L2CAP layer logic (protocol) */
+
+static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch)
+{
+#if 0
+ if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit)
+ qemu_mod_timer(ch->retransmission_timer);
+ else
+ qemu_del_timer(ch->retransmission_timer);
+#endif
+}
+
+static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch)
+{
+#if 0
+ if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit)
+ qemu_mod_timer(ch->monitor_timer);
+ else
+ qemu_del_timer(ch->monitor_timer);
+#endif
+}
+
+static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id,
+ uint16_t reason, const void *data, int plen)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ l2cap_cmd_rej *params;
+ uint16_t len;
+
+ reason = cpu_to_le16(reason);
+ len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen);
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen);
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ hdr->code = L2CAP_COMMAND_REJ;
+ hdr->ident = id;
+ memcpy(&hdr->len, &len, sizeof(hdr->len));
+ memcpy(&params->reason, &reason, sizeof(reason));
+ if (plen)
+ memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id,
+ uint16_t reason, uint16_t dcid, uint16_t scid)
+{
+ l2cap_cmd_rej_cid params = {
+ .dcid = dcid,
+ .scid = scid,
+ };
+
+ l2cap_command_reject(l2cap, id, reason, &params, L2CAP_CMD_REJ_CID_SIZE);
+}
+
+static void l2cap_connection_response(struct l2cap_instance_s *l2cap,
+ int dcid, int scid, int result, int status)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ l2cap_conn_rsp *params;
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE);
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ hdr->code = L2CAP_CONN_RSP;
+ hdr->ident = l2cap->last_id;
+ hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE);
+
+ params->dcid = cpu_to_le16(dcid);
+ params->scid = cpu_to_le16(scid);
+ params->result = cpu_to_le16(result);
+ params->status = cpu_to_le16(status);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_configuration_request(struct l2cap_instance_s *l2cap,
+ int dcid, int flag, const uint8_t *data, int len)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ l2cap_conf_req *params;
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len));
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ /* TODO: unify the id sequencing */
+ l2cap->last_id = l2cap->next_id;
+ l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1;
+
+ hdr->code = L2CAP_CONF_REQ;
+ hdr->ident = l2cap->last_id;
+ hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len));
+
+ params->dcid = cpu_to_le16(dcid);
+ params->flags = cpu_to_le16(flag);
+ if (len)
+ memcpy(params->data, data, len);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_configuration_response(struct l2cap_instance_s *l2cap,
+ int scid, int flag, int result, const uint8_t *data, int len)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ l2cap_conf_rsp *params;
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len));
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ hdr->code = L2CAP_CONF_RSP;
+ hdr->ident = l2cap->last_id;
+ hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len));
+
+ params->scid = cpu_to_le16(scid);
+ params->flags = cpu_to_le16(flag);
+ params->result = cpu_to_le16(result);
+ if (len)
+ memcpy(params->data, data, len);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap,
+ int dcid, int scid)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ l2cap_disconn_rsp *params;
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE);
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ hdr->code = L2CAP_DISCONN_RSP;
+ hdr->ident = l2cap->last_id;
+ hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE);
+
+ params->dcid = cpu_to_le16(dcid);
+ params->scid = cpu_to_le16(scid);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_echo_response(struct l2cap_instance_s *l2cap,
+ const uint8_t *data, int len)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ uint8_t *params;
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + len);
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ hdr->code = L2CAP_ECHO_RSP;
+ hdr->ident = l2cap->last_id;
+ hdr->len = cpu_to_le16(len);
+
+ memcpy(params, data, len);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type,
+ int result, const uint8_t *data, int len)
+{
+ uint8_t *pkt;
+ l2cap_cmd_hdr *hdr;
+ l2cap_info_rsp *params;
+
+ pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+ L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len);
+ hdr = (void *) (pkt + 0);
+ params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+ hdr->code = L2CAP_INFO_RSP;
+ hdr->ident = l2cap->last_id;
+ hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len);
+
+ params->type = cpu_to_le16(type);
+ params->result = cpu_to_le16(result);
+ if (len)
+ memcpy(params->data, data, len);
+
+ l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len);
+static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms);
+#if 0
+static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len);
+static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm);
+#endif
+static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+ const l2cap_hdr *hdr, int len);
+static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+ const l2cap_hdr *hdr, int len);
+
+static int l2cap_cid_new(struct l2cap_instance_s *l2cap)
+{
+ int i;
+
+ for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++)
+ if (!l2cap->cid[i])
+ return i;
+
+ return L2CAP_CID_INVALID;
+}
+
+static inline struct bt_l2cap_psm_s *l2cap_psm(
+ struct bt_l2cap_device_s *device, int psm)
+{
+ struct bt_l2cap_psm_s *ret = device->first_psm;
+
+ while (ret && ret->psm != psm)
+ ret = ret->next;
+
+ return ret;
+}
+
+static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap,
+ int psm, int source_cid)
+{
+ struct l2cap_chan_s *ch = NULL;
+ struct bt_l2cap_psm_s *psm_info;
+ int result, status;
+ int cid = l2cap_cid_new(l2cap);
+
+ if (cid) {
+ /* See what the channel is to be used for.. */
+ psm_info = l2cap_psm(l2cap->dev, psm);
+
+ if (psm_info) {
+ /* Device supports this use-case. */
+ ch = qemu_mallocz(sizeof(*ch));
+ ch->params.sdu_out = l2cap_bframe_out;
+ ch->params.sdu_submit = l2cap_bframe_submit;
+ ch->frame_in = l2cap_bframe_in;
+ ch->mps = 65536;
+ ch->min_mtu = MAX(48, psm_info->min_mtu);
+ ch->params.remote_mtu = MAX(672, ch->min_mtu);
+ ch->remote_cid = source_cid;
+ ch->mode = L2CAP_MODE_BASIC;
+ ch->l2cap = l2cap;
+
+ /* Does it feel like opening yet another channel though? */
+ if (!psm_info->new_channel(l2cap->dev, &ch->params)) {
+ l2cap->cid[cid] = ch;
+
+ result = L2CAP_CR_SUCCESS;
+ status = L2CAP_CS_NO_INFO;
+ } else {
+ qemu_free(ch);
+
+ result = L2CAP_CR_NO_MEM;
+ status = L2CAP_CS_NO_INFO;
+ }
+ } else {
+ result = L2CAP_CR_BAD_PSM;
+ status = L2CAP_CS_NO_INFO;
+ }
+ } else {
+ result = L2CAP_CR_NO_MEM;
+ status = L2CAP_CS_NO_INFO;
+ }
+
+ l2cap_connection_response(l2cap, cid, source_cid, result, status);
+
+ return ch;
+}
+
+static void l2cap_channel_close(struct l2cap_instance_s *l2cap,
+ int cid, int source_cid)
+{
+ struct l2cap_chan_s *ch = NULL;
+
+ /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a
+ * connection in CLOSED state still responds with a L2CAP_DisconnectRsp
+ * message on an L2CAP_DisconnectReq event. */
+ if (unlikely(cid < L2CAP_CID_ALLOC)) {
+ l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
+ cid, source_cid);
+ return;
+ }
+ if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX))
+ ch = l2cap->cid[cid];
+
+ if (likely(ch)) {
+ if (ch->remote_cid != source_cid) {
+ fprintf(stderr, "%s: Ignoring a Disconnection Request with the "
+ "invalid SCID %04x.\n", __FUNCTION__, source_cid);
+ return;
+ }
+
+ l2cap->cid[cid] = NULL;
+
+ ch->params.close(ch->params.opaque);
+ qemu_free(ch);
+ }
+
+ l2cap_disconnection_response(l2cap, cid, source_cid);
+}
+
+static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap,
+ struct l2cap_chan_s *ch)
+{
+ l2cap_configuration_request(l2cap, ch->remote_cid, 0, NULL, 0);
+ ch->config_req_id = l2cap->last_id;
+ ch->config &= ~L2CAP_CFG_INIT;
+}
+
+static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap,
+ struct l2cap_chan_s *ch)
+{
+ /* Use all default channel options and terminate negotiation. */
+ l2cap_channel_config_null(l2cap, ch);
+}
+
+static int l2cap_channel_config(struct l2cap_instance_s *l2cap,
+ struct l2cap_chan_s *ch, int flag,
+ const uint8_t *data, int len)
+{
+ l2cap_conf_opt *opt;
+ l2cap_conf_opt_qos *qos;
+ uint32_t val;
+ uint8_t rsp[len];
+ int result = L2CAP_CONF_SUCCESS;
+
+ data = memcpy(rsp, data, len);
+ while (len) {
+ opt = (void *) data;
+
+ if (len < L2CAP_CONF_OPT_SIZE ||
+ len < L2CAP_CONF_OPT_SIZE + opt->len) {
+ result = L2CAP_CONF_REJECT;
+ break;
+ }
+ data += L2CAP_CONF_OPT_SIZE + opt->len;
+ len -= L2CAP_CONF_OPT_SIZE + opt->len;
+
+ switch (opt->type & 0x7f) {
+ case L2CAP_CONF_MTU:
+ if (opt->len != 2) {
+ result = L2CAP_CONF_REJECT;
+ break;
+ }
+
+ /* MTU */
+ val = le16_to_cpup((void *) opt->val);
+ if (val < ch->min_mtu) {
+ cpu_to_le16w((void *) opt->val, ch->min_mtu);
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+
+ ch->params.remote_mtu = val;
+ break;
+
+ case L2CAP_CONF_FLUSH_TO:
+ if (opt->len != 2) {
+ result = L2CAP_CONF_REJECT;
+ break;
+ }
+
+ /* Flush Timeout */
+ val = le16_to_cpup((void *) opt->val);
+ if (val < 0x0001) {
+ opt->val[0] = 0xff;
+ opt->val[1] = 0xff;
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+ break;
+
+ case L2CAP_CONF_QOS:
+ if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) {
+ result = L2CAP_CONF_REJECT;
+ break;
+ }
+ qos = (void *) opt->val;
+
+ /* Flags */
+ val = qos->flags;
+ if (val) {
+ qos->flags = 0;
+ result = L2CAP_CONF_UNACCEPT;
+ }
+
+ /* Service type */
+ val = qos->service_type;
+ if (val != L2CAP_CONF_QOS_BEST_EFFORT &&
+ val != L2CAP_CONF_QOS_NO_TRAFFIC) {
+ qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT;
+ result = L2CAP_CONF_UNACCEPT;
+ }
+
+ if (val != L2CAP_CONF_QOS_NO_TRAFFIC) {
+ /* XXX: These values should possibly be calculated
+ * based on LM / baseband properties also. */
+
+ /* Token rate */
+ val = le32_to_cpu(qos->token_rate);
+ if (val == L2CAP_CONF_QOS_WILDCARD)
+ qos->token_rate = cpu_to_le32(0x100000);
+
+ /* Token bucket size */
+ val = le32_to_cpu(qos->token_bucket_size);
+ if (val == L2CAP_CONF_QOS_WILDCARD)
+ qos->token_bucket_size = cpu_to_le32(65500);
+
+ /* Any Peak bandwidth value is correct to return as-is */
+ /* Any Access latency value is correct to return as-is */
+ /* Any Delay variation value is correct to return as-is */
+ }
+ break;
+
+ case L2CAP_CONF_RFC:
+ if (opt->len != 9) {
+ result = L2CAP_CONF_REJECT;
+ break;
+ }
+
+ /* Mode */
+ val = opt->val[0];
+ switch (val) {
+ case L2CAP_MODE_BASIC:
+ ch->mode = val;
+ ch->frame_in = l2cap_bframe_in;
+
+ /* All other parameters shall be ignored */
+ break;
+
+ case L2CAP_MODE_RETRANS:
+ case L2CAP_MODE_FLOWCTL:
+ ch->mode = val;
+ ch->frame_in = l2cap_iframe_in;
+ /* Note: most of these parameters refer to incoming traffic
+ * so we don't need to save them as long as we can accept
+ * incoming PDUs at any values of the parameters. */
+
+ /* TxWindow size */
+ val = opt->val[1];
+ if (val < 1 || val > 32) {
+ opt->val[1] = 32;
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+
+ /* MaxTransmit */
+ val = opt->val[2];
+ if (val < 1) {
+ opt->val[2] = 1;
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+
+ /* Remote Retransmission time-out shouldn't affect local
+ * operation (?) */
+
+ /* The Monitor time-out drives the local Monitor timer (?),
+ * so save the value. */
+ val = (opt->val[6] << 8) | opt->val[5];
+ if (val < 30) {
+ opt->val[5] = 100 & 0xff;
+ opt->val[6] = 100 >> 8;
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+ ch->monitor_timeout = val;
+ l2cap_monitor_timer_update(ch);
+
+ /* MPS */
+ val = (opt->val[8] << 8) | opt->val[7];
+ if (val < ch->min_mtu) {
+ opt->val[7] = ch->min_mtu & 0xff;
+ opt->val[8] = ch->min_mtu >> 8;
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+ ch->mps = val;
+ break;
+
+ default:
+ result = L2CAP_CONF_UNACCEPT;
+ break;
+ }
+ break;
+
+ default:
+ if (!(opt->type >> 7))
+ result = L2CAP_CONF_UNKNOWN;
+ break;
+ }
+
+ if (result != L2CAP_CONF_SUCCESS)
+ break; /* XXX: should continue? */
+ }
+
+ l2cap_configuration_response(l2cap, ch->remote_cid,
+ flag, result, rsp, len);
+
+ return result == L2CAP_CONF_SUCCESS && !flag;
+}
+
+static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap,
+ int flag, int cid, const uint8_t *data, int len)
+{
+ struct l2cap_chan_s *ch;
+
+ if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
+ l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
+ cid, 0x0000);
+ return;
+ }
+ ch = l2cap->cid[cid];
+
+ /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to
+ * WAIT_CONFIG_REQ_RSP. This is assuming the transition chart for OPEN
+ * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake
+ * and on options-acceptable we go back to OPEN and otherwise to
+ * WAIT_CONFIG_REQ and not the other way. */
+ ch->config &= ~L2CAP_CFG_ACC;
+
+ if (l2cap_channel_config(l2cap, ch, flag, data, len))
+ /* Go to OPEN or WAIT_CONFIG_RSP */
+ ch->config |= L2CAP_CFG_ACC;
+
+ /* TODO: if the incoming traffic flow control or retransmission mode
+ * changed then we probably need to also generate the
+ * ConfigureChannel_Req event and set the outgoing traffic to the same
+ * mode. */
+ if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) &&
+ !ch->config_req_id)
+ l2cap_channel_config_req_event(l2cap, ch);
+}
+
+static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap,
+ int result, int flag, int cid, const uint8_t *data, int len)
+{
+ struct l2cap_chan_s *ch;
+
+ if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
+ l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
+ cid, 0x0000);
+ return 0;
+ }
+ ch = l2cap->cid[cid];
+
+ if (ch->config_req_id != l2cap->last_id)
+ return 1;
+ ch->config_req_id = 0;
+
+ if (result == L2CAP_CONF_SUCCESS) {
+ if (!flag)
+ ch->config |= L2CAP_CFG_INIT;
+ else
+ l2cap_channel_config_null(l2cap, ch);
+ } else
+ /* Retry until we succeed */
+ l2cap_channel_config_req_event(l2cap, ch);
+
+ return 0;
+}
+
+static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap,
+ int psm, int source_cid)
+{
+ struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid);
+
+ if (!ch)
+ return;
+
+ /* Optional */
+ if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id)
+ l2cap_channel_config_req_event(l2cap, ch);
+}
+
+static void l2cap_info(struct l2cap_instance_s *l2cap, int type)
+{
+ uint8_t data[4];
+ int len = 0;
+ int result = L2CAP_IR_SUCCESS;
+
+ switch (type) {
+ case L2CAP_IT_CL_MTU:
+ data[len ++] = l2cap->group_ch.mps & 0xff;
+ data[len ++] = l2cap->group_ch.mps >> 8;
+ break;
+
+ case L2CAP_IT_FEAT_MASK:
+ /* (Prematurely) report Flow control and Retransmission modes. */
+ data[len ++] = 0x03;
+ data[len ++] = 0x00;
+ data[len ++] = 0x00;
+ data[len ++] = 0x00;
+ break;
+
+ default:
+ result = L2CAP_IR_NOTSUPP;
+ }
+
+ l2cap_info_response(l2cap, type, result, data, len);
+}
+
+static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id,
+ const uint8_t *params, int len)
+{
+ int err;
+
+#if 0
+ /* TODO: do the IDs really have to be in sequence? */
+ if (!id || (id != l2cap->last_id && id != l2cap->next_id)) {
+ fprintf(stderr, "%s: out of sequence command packet ignored.\n",
+ __FUNCTION__);
+ return;
+ }
+#else
+ l2cap->next_id = id;
+#endif
+ if (id == l2cap->next_id) {
+ l2cap->last_id = l2cap->next_id;
+ l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1;
+ } else {
+ /* TODO: Need to re-send the same response, without re-executing
+ * the corresponding command! */
+ }
+
+ switch (code) {
+ case L2CAP_COMMAND_REJ:
+ if (unlikely(len != 2 && len != 4 && len != 6)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ /* We never issue commands other than Command Reject currently. */
+ fprintf(stderr, "%s: stray Command Reject (%02x, %04x) "
+ "packet, ignoring.\n", __FUNCTION__, id,
+ le16_to_cpu(((l2cap_cmd_rej *) params)->reason));
+ break;
+
+ case L2CAP_CONN_REQ:
+ if (unlikely(len != L2CAP_CONN_REQ_SIZE)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ l2cap_channel_open_req_msg(l2cap,
+ le16_to_cpu(((l2cap_conn_req *) params)->psm),
+ le16_to_cpu(((l2cap_conn_req *) params)->scid));
+ break;
+
+ case L2CAP_CONN_RSP:
+ if (unlikely(len != L2CAP_CONN_RSP_SIZE)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ /* We never issue Connection Requests currently. TODO */
+ fprintf(stderr, "%s: unexpected Connection Response (%02x) "
+ "packet, ignoring.\n", __FUNCTION__, id);
+ break;
+
+ case L2CAP_CONF_REQ:
+ if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ l2cap_channel_config_req_msg(l2cap,
+ le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1,
+ le16_to_cpu(((l2cap_conf_req *) params)->dcid),
+ ((l2cap_conf_req *) params)->data,
+ len - L2CAP_CONF_REQ_SIZE(0));
+ break;
+
+ case L2CAP_CONF_RSP:
+ if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ if (l2cap_channel_config_rsp_msg(l2cap,
+ le16_to_cpu(((l2cap_conf_rsp *) params)->result),
+ le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1,
+ le16_to_cpu(((l2cap_conf_rsp *) params)->scid),
+ ((l2cap_conf_rsp *) params)->data,
+ len - L2CAP_CONF_RSP_SIZE(0)))
+ fprintf(stderr, "%s: unexpected Configure Response (%02x) "
+ "packet, ignoring.\n", __FUNCTION__, id);
+ break;
+
+ case L2CAP_DISCONN_REQ:
+ if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ l2cap_channel_close(l2cap,
+ le16_to_cpu(((l2cap_disconn_req *) params)->dcid),
+ le16_to_cpu(((l2cap_disconn_req *) params)->scid));
+ break;
+
+ case L2CAP_DISCONN_RSP:
+ if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ /* We never issue Disconnection Requests currently. TODO */
+ fprintf(stderr, "%s: unexpected Disconnection Response (%02x) "
+ "packet, ignoring.\n", __FUNCTION__, id);
+ break;
+
+ case L2CAP_ECHO_REQ:
+ l2cap_echo_response(l2cap, params, len);
+ break;
+
+ case L2CAP_ECHO_RSP:
+ /* We never issue Echo Requests currently. TODO */
+ fprintf(stderr, "%s: unexpected Echo Response (%02x) "
+ "packet, ignoring.\n", __FUNCTION__, id);
+ break;
+
+ case L2CAP_INFO_REQ:
+ if (unlikely(len != L2CAP_INFO_REQ_SIZE)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type));
+ break;
+
+ case L2CAP_INFO_RSP:
+ if (unlikely(len != L2CAP_INFO_RSP_SIZE)) {
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ goto reject;
+ }
+
+ /* We never issue Information Requests currently. TODO */
+ fprintf(stderr, "%s: unexpected Information Response (%02x) "
+ "packet, ignoring.\n", __FUNCTION__, id);
+ break;
+
+ default:
+ err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+ reject:
+ l2cap_command_reject(l2cap, id, err, 0, 0);
+ break;
+ }
+}
+
+static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable)
+{
+ ch->rexmit = enable;
+
+ l2cap_retransmission_timer_update(ch);
+ l2cap_monitor_timer_update(ch);
+}
+
+/* Command frame SDU */
+static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len)
+{
+ struct l2cap_instance_s *l2cap = opaque;
+ const l2cap_cmd_hdr *hdr;
+ int clen;
+
+ while (len) {
+ hdr = (void *) data;
+ if (len < L2CAP_CMD_HDR_SIZE)
+ /* TODO: signal an error */
+ return;
+ len -= L2CAP_CMD_HDR_SIZE;
+ data += L2CAP_CMD_HDR_SIZE;
+
+ clen = le16_to_cpu(hdr->len);
+ if (len < clen) {
+ l2cap_command_reject(l2cap, hdr->ident,
+ L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0);
+ break;
+ }
+
+ l2cap_command(l2cap, hdr->code, hdr->ident, data, clen);
+ len -= clen;
+ data += clen;
+ }
+}
+
+/* Group frame SDU */
+static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len)
+{
+}
+
+/* Supervisory frame */
+static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl)
+{
+}
+
+/* Basic L2CAP mode Information frame */
+static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+ const l2cap_hdr *hdr, int len)
+{
+ /* We have a full SDU, no further processing */
+ ch->params.sdu_in(ch->params.opaque, hdr->data, len);
+}
+
+/* Flow Control and Retransmission mode frame */
+static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+ const l2cap_hdr *hdr, int len)
+{
+ uint16_t fcs = le16_to_cpup((void *) (hdr->data + len - 2));
+
+ if (len < 4)
+ goto len_error;
+ if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs)
+ goto fcs_error;
+
+ if ((hdr->data[0] >> 7) == ch->rexmit)
+ l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7));
+
+ if (hdr->data[0] & 1) {
+ if (len != 4) {
+ /* TODO: Signal an error? */
+ return;
+ }
+ return l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data));
+ }
+
+ switch (hdr->data[1] >> 6) { /* SAR */
+ case L2CAP_SAR_NO_SEG:
+ if (ch->len_total)
+ goto seg_error;
+ if (len - 4 > ch->mps)
+ goto len_error;
+
+ return ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4);
+
+ case L2CAP_SAR_START:
+ if (ch->len_total || len < 6)
+ goto seg_error;
+ if (len - 6 > ch->mps)
+ goto len_error;
+
+ ch->len_total = le16_to_cpup((void *) (hdr->data + 2));
+ if (len >= 6 + ch->len_total)
+ goto seg_error;
+
+ ch->len_cur = len - 6;
+ memcpy(ch->sdu, hdr->data + 4, ch->len_cur);
+ break;
+
+ case L2CAP_SAR_END:
+ if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total)
+ goto seg_error;
+ if (len - 4 > ch->mps)
+ goto len_error;
+
+ memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4);
+ return ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total);
+
+ case L2CAP_SAR_CONT:
+ if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total)
+ goto seg_error;
+ if (len - 4 > ch->mps)
+ goto len_error;
+
+ memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4);
+ ch->len_cur += len - 4;
+ break;
+
+ seg_error:
+ len_error: /* TODO */
+ fcs_error: /* TODO */
+ ch->len_cur = 0;
+ ch->len_total = 0;
+ break;
+ }
+}
+
+static void l2cap_frame_in(struct l2cap_instance_s *l2cap,
+ const l2cap_hdr *frame)
+{
+ uint16_t cid = le16_to_cpu(frame->cid);
+ uint16_t len = le16_to_cpu(frame->len);
+
+ if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
+ fprintf(stderr, "%s: frame addressed to a non-existent L2CAP "
+ "channel %04x received.\n", __FUNCTION__, cid);
+ return;
+ }
+
+ l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len);
+}
+
+/* "Recombination" */
+static void l2cap_pdu_in(struct l2cap_instance_s *l2cap,
+ const uint8_t *data, int len)
+{
+ const l2cap_hdr *hdr = (void *) l2cap->frame_in;
+
+ if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) {
+ if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) {
+ memcpy(l2cap->frame_in + l2cap->frame_in_len, data,
+ sizeof(l2cap->frame_in) - l2cap->frame_in_len);
+ l2cap->frame_in_len = sizeof(l2cap->frame_in);
+ /* TODO: truncate */
+ l2cap_frame_in(l2cap, hdr);
+ }
+
+ return;
+ }
+
+ memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len);
+ l2cap->frame_in_len += len;
+
+ if (len >= L2CAP_HDR_SIZE)
+ if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len))
+ l2cap_frame_in(l2cap, hdr);
+ /* There is never a start of a new PDU in the same ACL packet, so
+ * no need to memmove the remaining payload and loop. */
+}
+
+static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap,
+ uint16_t cid, uint16_t len)
+{
+ l2cap_hdr *hdr = (void *) l2cap->frame_out;
+
+ l2cap->frame_out_len = len + L2CAP_HDR_SIZE;
+
+ hdr->cid = cpu_to_le16(cid);
+ hdr->len = cpu_to_le16(len);
+
+ return l2cap->frame_out + L2CAP_HDR_SIZE;
+}
+
+static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap)
+{
+ /* TODO: Fragmentation */
+ (l2cap->role ?
+ l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp)
+ (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len);
+}
+
+static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len)
+{
+ struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm;
+
+ if (len > chan->params.remote_mtu) {
+ fprintf(stderr, "%s: B-Frame for CID %04x longer than %i octets.\n",
+ __FUNCTION__,
+ chan->remote_cid, chan->params.remote_mtu);
+ exit(-1);
+ }
+
+ return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len);
+}
+
+static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms)
+{
+ struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms;
+
+ return l2cap_pdu_submit(chan->l2cap);
+}
+
+#if 0
+/* Stub: Only used if an emulated device requests outgoing flow control */
+static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len)
+{
+ struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm;
+
+ if (len > chan->params.remote_mtu) {
+ /* TODO: slice into segments and queue each segment as a separate
+ * I-Frame in a FIFO of I-Frames, local to the CID. */
+ } else {
+ /* TODO: add to the FIFO of I-Frames, local to the CID. */
+ /* Possibly we need to return a pointer to a contiguous buffer
+ * for now and then memcpy from it into FIFOs in l2cap_iframe_submit
+ * while segmenting at the same time. */
+ }
+ return 0;
+}
+
+static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm)
+{
+ /* TODO: If flow control indicates clear to send, start submitting the
+ * invidual I-Frames from the FIFO, but don't remove them from there.
+ * Kick the appropriate timer until we get an S-Frame, and only then
+ * remove from FIFO or resubmit and re-kick the timer if the timer
+ * expired. */
+}
+#endif
+
+static void l2cap_init(struct l2cap_instance_s *l2cap,
+ struct bt_link_s *link, int role)
+{
+ l2cap->link = link;
+ l2cap->role = role;
+ l2cap->dev = (struct bt_l2cap_device_s *)
+ (role ? link->host : link->slave);
+
+ l2cap->next_id = 1;
+
+ /* Establish the signalling channel */
+ l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in;
+ l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out;
+ l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit;
+ l2cap->signalling_ch.params.opaque = l2cap;
+ l2cap->signalling_ch.params.remote_mtu = 48;
+ l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING;
+ l2cap->signalling_ch.frame_in = l2cap_bframe_in;
+ l2cap->signalling_ch.mps = 65536;
+ l2cap->signalling_ch.min_mtu = 48;
+ l2cap->signalling_ch.mode = L2CAP_MODE_BASIC;
+ l2cap->signalling_ch.l2cap = l2cap;
+ l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch;
+
+ /* Establish the connection-less data channel */
+ l2cap->group_ch.params.sdu_in = l2cap_gframe_in;
+ l2cap->group_ch.params.opaque = l2cap;
+ l2cap->group_ch.frame_in = l2cap_bframe_in;
+ l2cap->group_ch.mps = 65533;
+ l2cap->group_ch.l2cap = l2cap;
+ l2cap->group_ch.remote_cid = L2CAP_CID_INVALID;
+ l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch;
+}
+
+static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect)
+{
+ int cid;
+
+ /* Don't send DISCONNECT if we are currently handling a DISCONNECT
+ * sent from the other side. */
+ if (send_disconnect) {
+ if (l2cap->role)
+ l2cap->dev->device.lmp_disconnect_slave(l2cap->link);
+ /* l2cap->link is invalid from now on. */
+ else
+ l2cap->dev->device.lmp_disconnect_master(l2cap->link);
+ }
+
+ for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++)
+ if (l2cap->cid[cid]) {
+ l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque);
+ qemu_free(l2cap->cid[cid]);
+ }
+
+ if (l2cap->role)
+ qemu_free(l2cap);
+ else
+ qemu_free(l2cap->link);
+}
+
+/* L2CAP glue to lower layers in bluetooth stack (LMP) */
+
+static void l2cap_lmp_connection_request(struct bt_link_s *link)
+{
+ struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave;
+ struct slave_l2cap_instance_s *l2cap;
+
+ /* Always accept - we only get called if (dev->device->page_scan). */
+
+ l2cap = qemu_mallocz(sizeof(struct slave_l2cap_instance_s));
+ l2cap->link.slave = &dev->device;
+ l2cap->link.host = link->host;
+ l2cap_init(&l2cap->l2cap, &l2cap->link, 0);
+
+ /* Always at the end */
+ link->host->reject_reason = 0;
+ link->host->lmp_connection_complete(&l2cap->link);
+}
+
+/* Stub */
+static void l2cap_lmp_connection_complete(struct bt_link_s *link)
+{
+ struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
+ struct l2cap_instance_s *l2cap;
+
+ if (dev->device.reject_reason) {
+ /* Signal to upper layer */
+ return;
+ }
+
+ l2cap = qemu_mallocz(sizeof(struct l2cap_instance_s));
+ l2cap_init(l2cap, link, 1);
+
+ link->acl_mode = acl_active;
+
+ /* Signal to upper layer */
+}
+
+/* Stub */
+static void l2cap_lmp_disconnect_host(struct bt_link_s *link)
+{
+ struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
+ struct l2cap_instance_s *l2cap =
+ /* TODO: Retrieve from upper layer */ (void *) dev;
+
+ /* Signal to upper layer */
+
+ l2cap_teardown(l2cap, 0);
+}
+
+static void l2cap_lmp_disconnect_slave(struct bt_link_s *link)
+{
+ struct slave_l2cap_instance_s *l2cap =
+ (struct slave_l2cap_instance_s *) link;
+
+ l2cap_teardown(&l2cap->l2cap, 0);
+}
+
+static void l2cap_lmp_acl_data_slave(struct bt_link_s *link,
+ const uint8_t *data, int start, int len)
+{
+ struct slave_l2cap_instance_s *l2cap =
+ (struct slave_l2cap_instance_s *) link;
+
+ if (start)
+ l2cap->l2cap.frame_in_len = 0;
+
+ l2cap_pdu_in(&l2cap->l2cap, data, len);
+}
+
+/* Stub */
+static void l2cap_lmp_acl_data_host(struct bt_link_s *link,
+ const uint8_t *data, int start, int len)
+{
+ struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
+ struct l2cap_instance_s *l2cap =
+ /* TODO: Retrieve from upper layer */ (void *) dev;
+
+ if (start)
+ l2cap->frame_in_len = 0;
+
+ l2cap_pdu_in(l2cap, data, len);
+}
+
+static void l2cap_dummy_destroy(struct bt_device_s *dev)
+{
+ struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev;
+
+ bt_l2cap_device_done(l2cap_dev);
+}
+
+void bt_l2cap_device_init(struct bt_l2cap_device_s *dev,
+ struct bt_scatternet_s *net)
+{
+ bt_device_init(&dev->device, net);
+
+ dev->device.lmp_connection_request = l2cap_lmp_connection_request;
+ dev->device.lmp_connection_complete = l2cap_lmp_connection_complete;
+ dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host;
+ dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave;
+ dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave;
+ dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host;
+
+ dev->device.handle_destroy = l2cap_dummy_destroy;
+}
+
+void bt_l2cap_device_done(struct bt_l2cap_device_s *dev)
+{
+ bt_device_done(&dev->device);
+
+ /* Should keep a list of all instances and go through it and
+ * invoke l2cap_teardown() for each. */
+}
+
+void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu,
+ int (*new_channel)(struct bt_l2cap_device_s *dev,
+ struct bt_l2cap_conn_params_s *params))
+{
+ struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm);
+
+ if (new_psm) {
+ fprintf(stderr, "%s: PSM %04x already registered for device `%s'.\n",
+ __FUNCTION__, psm, dev->device.lmp_name);
+ exit(-1);
+ }
+
+ new_psm = qemu_mallocz(sizeof(*new_psm));
+ new_psm->psm = psm;
+ new_psm->min_mtu = min_mtu;
+ new_psm->new_channel = new_channel;
+ new_psm->next = dev->first_psm;
+ dev->first_psm = new_psm;
+}
diff --git a/hw/bt-sdp.c b/hw/bt-sdp.c
new file mode 100644
index 0000000..cdf2d95
--- /dev/null
+++ b/hw/bt-sdp.c
@@ -0,0 +1,967 @@
+/*
+ * Service Discover Protocol server for QEMU L2CAP devices
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "bt.h"
+
+struct bt_l2cap_sdp_state_s {
+ struct bt_l2cap_conn_params_s *channel;
+
+ struct sdp_service_record_s {
+ int match;
+
+ int *uuid;
+ int uuids;
+ struct sdp_service_attribute_s {
+ int match;
+
+ int attribute_id;
+ int len;
+ void *pair;
+ } *attribute_list;
+ int attributes;
+ } *service_list;
+ int services;
+};
+
+static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left)
+{
+ size_t len = *(*element) ++ & SDP_DSIZE_MASK;
+
+ if (!*left)
+ return -1;
+ (*left) --;
+
+ if (len < SDP_DSIZE_NEXT1)
+ return 1 << len;
+ else if (len == SDP_DSIZE_NEXT1) {
+ if (*left < 1)
+ return -1;
+ (*left) --;
+
+ return *(*element) ++;
+ } else if (len == SDP_DSIZE_NEXT2) {
+ if (*left < 2)
+ return -1;
+ (*left) -= 2;
+
+ len = (*(*element) ++) << 8;
+ return len | (*(*element) ++);
+ } else {
+ if (*left < 4)
+ return -1;
+ (*left) -= 4;
+
+ len = (*(*element) ++) << 24;
+ len |= (*(*element) ++) << 16;
+ len |= (*(*element) ++) << 8;
+ return len | (*(*element) ++);
+ }
+}
+
+static const uint8_t bt_base_uuid[12] = {
+ 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+};
+
+static int sdp_uuid_match(struct sdp_service_record_s *record,
+ const uint8_t *uuid, ssize_t datalen)
+{
+ int *lo, hi, val;
+
+ if (datalen == 16 || datalen == 4) {
+ if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12))
+ return 0;
+
+ if (uuid[0] | uuid[1])
+ return 0;
+ uuid += 2;
+ }
+
+ val = (uuid[0] << 8) | uuid[1];
+ lo = record->uuid;
+ hi = record->uuids;
+ while (hi >>= 1)
+ if (lo[hi] <= val)
+ lo += hi;
+
+ return *lo == val;
+}
+
+#define CONTINUATION_PARAM_SIZE (1 + sizeof(int))
+#define MAX_PDU_OUT_SIZE 96 /* Arbitrary */
+#define PDU_HEADER_SIZE 5
+#define MAX_RSP_PARAM_SIZE (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \
+ CONTINUATION_PARAM_SIZE)
+
+static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp,
+ const uint8_t **req, ssize_t *len)
+{
+ size_t datalen;
+ int i;
+
+ if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID)
+ return 1;
+
+ datalen = sdp_datalen(req, len);
+ if (datalen != 2 && datalen != 4 && datalen != 16)
+ return 1;
+
+ for (i = 0; i < sdp->services; i ++)
+ if (sdp_uuid_match(&sdp->service_list[i], *req, datalen))
+ sdp->service_list[i].match = 1;
+
+ (*req) += datalen;
+ (*len) -= datalen;
+
+ return 0;
+}
+
+static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp,
+ uint8_t *rsp, const uint8_t *req, ssize_t len)
+{
+ ssize_t seqlen;
+ int i, count, start, end, max;
+ int32_t handle;
+
+ /* Perform the search */
+ for (i = 0; i < sdp->services; i ++)
+ sdp->service_list[i].match = 0;
+
+ if (len < 1)
+ return -SDP_INVALID_SYNTAX;
+ if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+ seqlen = sdp_datalen(&req, &len);
+ if (seqlen < 3 || len < seqlen)
+ return -SDP_INVALID_SYNTAX;
+ len -= seqlen;
+
+ while (seqlen)
+ if (sdp_svc_match(sdp, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+ } else if (sdp_svc_match(sdp, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+
+ if (len < 3)
+ return -SDP_INVALID_SYNTAX;
+ max = (req[0] << 8) | req[1];
+ req += 2;
+ len -= 2;
+
+ if (*req) {
+ if (len <= sizeof(int))
+ return -SDP_INVALID_SYNTAX;
+ len -= sizeof(int);
+ memcpy(&start, req + 1, sizeof(int));
+ } else
+ start = 0;
+
+ if (len > 1)
+ return -SDP_INVALID_SYNTAX;
+
+ /* Output the results */
+ len = 4;
+ count = 0;
+ end = start;
+ for (i = 0; i < sdp->services; i ++)
+ if (sdp->service_list[i].match) {
+ if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) {
+ handle = i;
+ memcpy(rsp + len, &handle, 4);
+ len += 4;
+ end = count + 1;
+ }
+
+ count ++;
+ }
+
+ rsp[0] = count >> 8;
+ rsp[1] = count & 0xff;
+ rsp[2] = (end - start) >> 8;
+ rsp[3] = (end - start) & 0xff;
+
+ if (end < count) {
+ rsp[len ++] = sizeof(int);
+ memcpy(rsp + len, &end, sizeof(int));
+ len += 4;
+ } else
+ rsp[len ++] = 0;
+
+ return len;
+}
+
+static int sdp_attr_match(struct sdp_service_record_s *record,
+ const uint8_t **req, ssize_t *len)
+{
+ int i, start, end;
+
+ if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
+ (*req) ++;
+ if (*len < 3)
+ return 1;
+
+ start = (*(*req) ++) << 8;
+ start |= *(*req) ++;
+ end = start;
+ *len -= 3;
+ } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
+ (*req) ++;
+ if (*len < 5)
+ return 1;
+
+ start = (*(*req) ++) << 8;
+ start |= *(*req) ++;
+ end = (*(*req) ++) << 8;
+ end |= *(*req) ++;
+ *len -= 5;
+ } else
+ return 1;
+
+ for (i = 0; i < record->attributes; i ++)
+ if (record->attribute_list[i].attribute_id >= start &&
+ record->attribute_list[i].attribute_id <= end)
+ record->attribute_list[i].match = 1;
+
+ return 0;
+}
+
+static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp,
+ uint8_t *rsp, const uint8_t *req, ssize_t len)
+{
+ ssize_t seqlen;
+ int i, start, end, max;
+ int32_t handle;
+ struct sdp_service_record_s *record;
+ uint8_t *lst;
+
+ /* Perform the search */
+ if (len < 7)
+ return -SDP_INVALID_SYNTAX;
+ memcpy(&handle, req, 4);
+ req += 4;
+ len -= 4;
+
+ if (handle < 0 || handle > sdp->services)
+ return -SDP_INVALID_RECORD_HANDLE;
+ record = &sdp->service_list[handle];
+
+ for (i = 0; i < record->attributes; i ++)
+ record->attribute_list[i].match = 0;
+
+ max = (req[0] << 8) | req[1];
+ req += 2;
+ len -= 2;
+ if (max < 0x0007)
+ return -SDP_INVALID_SYNTAX;
+
+ if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+ seqlen = sdp_datalen(&req, &len);
+ if (seqlen < 3 || len < seqlen)
+ return -SDP_INVALID_SYNTAX;
+ len -= seqlen;
+
+ while (seqlen)
+ if (sdp_attr_match(record, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+ } else if (sdp_attr_match(record, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+
+ if (len < 1)
+ return -SDP_INVALID_SYNTAX;
+
+ if (*req) {
+ if (len <= sizeof(int))
+ return -SDP_INVALID_SYNTAX;
+ len -= sizeof(int);
+ memcpy(&start, req + 1, sizeof(int));
+ } else
+ start = 0;
+
+ if (len > 1)
+ return -SDP_INVALID_SYNTAX;
+
+ /* Output the results */
+ lst = rsp + 2;
+ max = MIN(max, MAX_RSP_PARAM_SIZE);
+ len = 3 - start;
+ end = 0;
+ for (i = 0; i < record->attributes; i ++)
+ if (record->attribute_list[i].match) {
+ if (len >= 0 && len + record->attribute_list[i].len < max) {
+ memcpy(lst + len, record->attribute_list[i].pair,
+ record->attribute_list[i].len);
+ end = len + record->attribute_list[i].len;
+ }
+ len += record->attribute_list[i].len;
+ }
+ if (0 >= start) {
+ lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
+ lst[1] = (len + start - 3) >> 8;
+ lst[2] = (len + start - 3) & 0xff;
+ }
+
+ rsp[0] = end >> 8;
+ rsp[1] = end & 0xff;
+
+ if (end < len) {
+ len = end + start;
+ lst[end ++] = sizeof(int);
+ memcpy(lst + end, &len, sizeof(int));
+ end += sizeof(int);
+ } else
+ lst[end ++] = 0;
+
+ return end + 2;
+}
+
+static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp,
+ const uint8_t **req, ssize_t *len)
+{
+ int i, j, start, end;
+ struct sdp_service_record_s *record;
+
+ if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
+ (*req) ++;
+ if (*len < 3)
+ return 1;
+
+ start = (*(*req) ++) << 8;
+ start |= *(*req) ++;
+ end = start;
+ *len -= 3;
+ } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
+ (*req) ++;
+ if (*len < 5)
+ return 1;
+
+ start = (*(*req) ++) << 8;
+ start |= *(*req) ++;
+ end = (*(*req) ++) << 8;
+ end |= *(*req) ++;
+ *len -= 5;
+ } else
+ return 1;
+
+ for (i = 0; i < sdp->services; i ++)
+ if ((record = &sdp->service_list[i])->match)
+ for (j = 0; j < record->attributes; j ++)
+ if (record->attribute_list[j].attribute_id >= start &&
+ record->attribute_list[j].attribute_id <= end)
+ record->attribute_list[j].match = 1;
+
+ return 0;
+}
+
+static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
+ uint8_t *rsp, const uint8_t *req, ssize_t len)
+{
+ ssize_t seqlen;
+ int i, j, start, end, max;
+ struct sdp_service_record_s *record;
+ uint8_t *lst;
+
+ /* Perform the search */
+ for (i = 0; i < sdp->services; i ++) {
+ sdp->service_list[i].match = 0;
+ for (j = 0; j < sdp->service_list[i].attributes; j ++)
+ sdp->service_list[i].attribute_list[j].match = 0;
+ }
+
+ if (len < 1)
+ return -SDP_INVALID_SYNTAX;
+ if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+ seqlen = sdp_datalen(&req, &len);
+ if (seqlen < 3 || len < seqlen)
+ return -SDP_INVALID_SYNTAX;
+ len -= seqlen;
+
+ while (seqlen)
+ if (sdp_svc_match(sdp, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+ } else if (sdp_svc_match(sdp, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+
+ if (len < 3)
+ return -SDP_INVALID_SYNTAX;
+ max = (req[0] << 8) | req[1];
+ req += 2;
+ len -= 2;
+ if (max < 0x0007)
+ return -SDP_INVALID_SYNTAX;
+
+ if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+ seqlen = sdp_datalen(&req, &len);
+ if (seqlen < 3 || len < seqlen)
+ return -SDP_INVALID_SYNTAX;
+ len -= seqlen;
+
+ while (seqlen)
+ if (sdp_svc_attr_match(sdp, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+ } else if (sdp_svc_attr_match(sdp, &req, &seqlen))
+ return -SDP_INVALID_SYNTAX;
+
+ if (len < 1)
+ return -SDP_INVALID_SYNTAX;
+
+ if (*req) {
+ if (len <= sizeof(int))
+ return -SDP_INVALID_SYNTAX;
+ len -= sizeof(int);
+ memcpy(&start, req + 1, sizeof(int));
+ } else
+ start = 0;
+
+ if (len > 1)
+ return -SDP_INVALID_SYNTAX;
+
+ /* Output the results */
+ /* This assumes empty attribute lists are never to be returned even
+ * for matching Service Records. In practice this shouldn't happen
+ * as the requestor will usually include the always present
+ * ServiceRecordHandle AttributeID in AttributeIDList. */
+ lst = rsp + 2;
+ max = MIN(max, MAX_RSP_PARAM_SIZE);
+ len = 3 - start;
+ end = 0;
+ for (i = 0; i < sdp->services; i ++)
+ if ((record = &sdp->service_list[i])->match) {
+ len += 3;
+ seqlen = len;
+ for (j = 0; j < record->attributes; j ++)
+ if (record->attribute_list[j].match) {
+ if (len >= 0)
+ if (len + record->attribute_list[j].len < max) {
+ memcpy(lst + len, record->attribute_list[j].pair,
+ record->attribute_list[j].len);
+ end = len + record->attribute_list[j].len;
+ }
+ len += record->attribute_list[j].len;
+ }
+ if (seqlen == len)
+ len -= 3;
+ else if (seqlen >= 3 && seqlen < max) {
+ lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
+ lst[seqlen - 2] = (len - seqlen) >> 8;
+ lst[seqlen - 1] = (len - seqlen) & 0xff;
+ }
+ }
+ if (len == 3 - start)
+ len -= 3;
+ else if (0 >= start) {
+ lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
+ lst[1] = (len + start - 3) >> 8;
+ lst[2] = (len + start - 3) & 0xff;
+ }
+
+ rsp[0] = end >> 8;
+ rsp[1] = end & 0xff;
+
+ if (end < len) {
+ len = end + start;
+ lst[end ++] = sizeof(int);
+ memcpy(lst + end, &len, sizeof(int));
+ end += sizeof(int);
+ } else
+ lst[end ++] = 0;
+
+ return end + 2;
+}
+
+static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len)
+{
+ struct bt_l2cap_sdp_state_s *sdp = opaque;
+ enum bt_sdp_cmd pdu_id;
+ uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out;
+ int transaction_id, plen;
+ int err = 0;
+ int rsp_len = 0;
+
+ if (len < 5) {
+ fprintf(stderr, "%s: short SDP PDU (%iB).\n", __FUNCTION__, len);
+ return;
+ }
+
+ pdu_id = *data ++;
+ transaction_id = (data[0] << 8) | data[1];
+ plen = (data[2] << 8) | data[3];
+ data += 4;
+ len -= 5;
+
+ if (len != plen) {
+ fprintf(stderr, "%s: wrong SDP PDU length (%iB != %iB).\n",
+ __FUNCTION__, plen, len);
+ err = SDP_INVALID_PDU_SIZE;
+ goto respond;
+ }
+
+ switch (pdu_id) {
+ case SDP_SVC_SEARCH_REQ:
+ rsp_len = sdp_svc_search(sdp, rsp, data, len);
+ pdu_id = SDP_SVC_SEARCH_RSP;
+ break;
+
+ case SDP_SVC_ATTR_REQ:
+ rsp_len = sdp_attr_get(sdp, rsp, data, len);
+ pdu_id = SDP_SVC_ATTR_RSP;
+ break;
+
+ case SDP_SVC_SEARCH_ATTR_REQ:
+ rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len);
+ pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
+ break;
+
+ case SDP_ERROR_RSP:
+ case SDP_SVC_ATTR_RSP:
+ case SDP_SVC_SEARCH_RSP:
+ case SDP_SVC_SEARCH_ATTR_RSP:
+ default:
+ fprintf(stderr, "%s: unexpected SDP PDU ID %02x.\n",
+ __FUNCTION__, pdu_id);
+ err = SDP_INVALID_SYNTAX;
+ break;
+ }
+
+ if (rsp_len < 0) {
+ err = -rsp_len;
+ rsp_len = 0;
+ }
+
+respond:
+ if (err) {
+ pdu_id = SDP_ERROR_RSP;
+ rsp[rsp_len ++] = err >> 8;
+ rsp[rsp_len ++] = err & 0xff;
+ }
+
+ sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE);
+
+ sdu_out[0] = pdu_id;
+ sdu_out[1] = transaction_id >> 8;
+ sdu_out[2] = transaction_id & 0xff;
+ sdu_out[3] = rsp_len >> 8;
+ sdu_out[4] = rsp_len & 0xff;
+ memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len);
+
+ sdp->channel->sdu_submit(sdp->channel);
+}
+
+static void bt_l2cap_sdp_close_ch(void *opaque)
+{
+ struct bt_l2cap_sdp_state_s *sdp = opaque;
+ int i;
+
+ for (i = 0; i < sdp->services; i ++) {
+ qemu_free(sdp->service_list[i].attribute_list->pair);
+ qemu_free(sdp->service_list[i].attribute_list);
+ qemu_free(sdp->service_list[i].uuid);
+ }
+ qemu_free(sdp->service_list);
+ qemu_free(sdp);
+}
+
+struct sdp_def_service_s {
+ uint16_t class_uuid;
+ struct sdp_def_attribute_s {
+ uint16_t id;
+ struct sdp_def_data_element_s {
+ uint8_t type;
+ union {
+ uint32_t uint;
+ const char *str;
+ struct sdp_def_data_element_s *list;
+ } value;
+ } data;
+ } attributes[];
+};
+
+/* Calculate a safe byte count to allocate that will store the given
+ * element, at the same time count elements of a UUID type. */
+static int sdp_attr_max_size(struct sdp_def_data_element_s *element,
+ int *uuids)
+{
+ int type = element->type & ~SDP_DSIZE_MASK;
+ int len;
+
+ if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID ||
+ type == SDP_DTYPE_BOOL) {
+ if (type == SDP_DTYPE_UUID)
+ (*uuids) ++;
+ return 1 + (1 << (element->type & SDP_DSIZE_MASK));
+ }
+
+ if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
+ if (element->type & SDP_DSIZE_MASK) {
+ for (len = 0; element->value.str[len] |
+ element->value.str[len + 1]; len ++);
+ return len;
+ } else
+ return 2 + strlen(element->value.str);
+ }
+
+ if (type != SDP_DTYPE_SEQ)
+ exit(-1);
+ len = 2;
+ element = element->value.list;
+ while (element->type)
+ len += sdp_attr_max_size(element ++, uuids);
+ if (len > 255)
+ exit (-1);
+
+ return len;
+}
+
+static int sdp_attr_write(uint8_t *data,
+ struct sdp_def_data_element_s *element, int **uuid)
+{
+ int type = element->type & ~SDP_DSIZE_MASK;
+ int len = 0;
+
+ if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) {
+ data[len ++] = element->type;
+ if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1)
+ data[len ++] = (element->value.uint >> 0) & 0xff;
+ else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) {
+ data[len ++] = (element->value.uint >> 8) & 0xff;
+ data[len ++] = (element->value.uint >> 0) & 0xff;
+ } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) {
+ data[len ++] = (element->value.uint >> 24) & 0xff;
+ data[len ++] = (element->value.uint >> 16) & 0xff;
+ data[len ++] = (element->value.uint >> 8) & 0xff;
+ data[len ++] = (element->value.uint >> 0) & 0xff;
+ }
+
+ return len;
+ }
+
+ if (type == SDP_DTYPE_UUID) {
+ *(*uuid) ++ = element->value.uint;
+
+ data[len ++] = element->type;
+ data[len ++] = (element->value.uint >> 24) & 0xff;
+ data[len ++] = (element->value.uint >> 16) & 0xff;
+ data[len ++] = (element->value.uint >> 8) & 0xff;
+ data[len ++] = (element->value.uint >> 0) & 0xff;
+ memcpy(data + len, bt_base_uuid, 12);
+
+ return len + 12;
+ }
+
+ data[0] = type | SDP_DSIZE_NEXT1;
+ if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
+ if (element->type & SDP_DSIZE_MASK)
+ for (len = 0; element->value.str[len] |
+ element->value.str[len + 1]; len ++);
+ else
+ len = strlen(element->value.str);
+ memcpy(data + 2, element->value.str, data[1] = len);
+
+ return len + 2;
+ }
+
+ len = 2;
+ element = element->value.list;
+ while (element->type)
+ len += sdp_attr_write(data + len, element ++, uuid);
+ data[1] = len - 2;
+
+ return len;
+}
+
+static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a,
+ const struct sdp_service_attribute_s *b)
+{
+ return (int) b->attribute_id - a->attribute_id;
+}
+
+static int sdp_uuid_compare(const int *a, const int *b)
+{
+ return *a - *b;
+}
+
+static void sdp_service_record_build(struct sdp_service_record_s *record,
+ struct sdp_def_service_s *def, int handle)
+{
+ int len = 0;
+ uint8_t *data;
+ int *uuid;
+
+ record->uuids = 0;
+ while (def->attributes[record->attributes].data.type) {
+ len += 3;
+ len += sdp_attr_max_size(&def->attributes[record->attributes ++].data,
+ &record->uuids);
+ }
+ record->uuids = 1 << ffs(record->uuids - 1);
+ record->attribute_list =
+ qemu_mallocz(record->attributes * sizeof(*record->attribute_list));
+ record->uuid =
+ qemu_mallocz(record->uuids * sizeof(*record->uuid));
+ data = qemu_malloc(len);
+
+ record->attributes = 0;
+ uuid = record->uuid;
+ while (def->attributes[record->attributes].data.type) {
+ record->attribute_list[record->attributes].pair = data;
+
+ len = 0;
+ data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2;
+ data[len ++] = def->attributes[record->attributes].id >> 8;
+ data[len ++] = def->attributes[record->attributes].id & 0xff;
+ len += sdp_attr_write(data + len,
+ &def->attributes[record->attributes].data, &uuid);
+
+ /* Special case: assign a ServiceRecordHandle in sequence */
+ if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE)
+ def->attributes[record->attributes].data.value.uint = handle;
+ /* Note: we could also assign a ServiceDescription based on
+ * sdp->device.device->lmp_name. */
+
+ record->attribute_list[record->attributes ++].len = len;
+ data += len;
+ }
+
+ /* Sort the attribute list by the AttributeID */
+ qsort(record->attribute_list, record->attributes,
+ sizeof(*record->attribute_list),
+ (void *) sdp_attributeid_compare);
+ /* Sort the searchable UUIDs list for bisection */
+ qsort(record->uuid, record->uuids,
+ sizeof(*record->uuid),
+ (void *) sdp_uuid_compare);
+}
+
+static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp,
+ struct sdp_def_service_s **service)
+{
+ sdp->services = 0;
+ while (service[sdp->services])
+ sdp->services ++;
+ sdp->service_list =
+ qemu_mallocz(sdp->services * sizeof(*sdp->service_list));
+
+ sdp->services = 0;
+ while (*service) {
+ sdp_service_record_build(&sdp->service_list[sdp->services],
+ *service, sdp->services);
+ service ++;
+ sdp->services ++;
+ }
+}
+
+#define LAST { .type = 0 }
+#define SERVICE(name, attrs) \
+ static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \
+ .attributes = { attrs { .data = LAST } }, \
+ };
+#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val },
+#define UINT8(val) { \
+ .type = SDP_DTYPE_UINT | SDP_DSIZE_1, \
+ .value.uint = val, \
+ },
+#define UINT16(val) { \
+ .type = SDP_DTYPE_UINT | SDP_DSIZE_2, \
+ .value.uint = val, \
+ },
+#define UINT32(val) { \
+ .type = SDP_DTYPE_UINT | SDP_DSIZE_4, \
+ .value.uint = val, \
+ },
+#define UUID128(val) { \
+ .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \
+ .value.uint = val, \
+ },
+#define SDP_TRUE { \
+ .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \
+ .value.uint = 1, \
+ },
+#define SDP_FALSE { \
+ .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \
+ .value.uint = 0, \
+ },
+#define STRING(val) { \
+ .type = SDP_DTYPE_STRING, \
+ .value.str = val, \
+ },
+#define ARRAY(...) { \
+ .type = SDP_DTYPE_STRING | SDP_DSIZE_2, \
+ .value.str = (char []) { __VA_ARGS__, 0, 0 }, \
+ },
+#define URL(val) { \
+ .type = SDP_DTYPE_URL, \
+ .value.str = val, \
+ },
+#if 1
+#define LIST(val) { \
+ .type = SDP_DTYPE_SEQ, \
+ .value.list = (struct sdp_def_data_element_s []) { val LAST }, \
+ },
+#endif
+
+/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes
+ * in resulting SDP data representation size. */
+
+SERVICE(hid,
+ ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */
+ ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID)))
+ ATTRIBUTE(RECORD_STATE, UINT32(1))
+ ATTRIBUTE(PROTO_DESC_LIST, LIST(
+ LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL))
+ LIST(UUID128(HIDP_UUID))
+ ))
+ ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
+ ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
+ UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
+ ))
+ ATTRIBUTE(PFILE_DESC_LIST, LIST(
+ LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100))
+ ))
+ ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html"))
+ ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID"))
+ ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse"))
+ ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION))
+
+ /* Profile specific */
+ ATTRIBUTE(DEVICE_RELEASE_NUMBER, UINT16(0x0091)) /* Deprecated, remove */
+ ATTRIBUTE(PARSER_VERSION, UINT16(0x0111))
+ /* TODO: extract from l2cap_device->device.class[0] */
+ ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40))
+ ATTRIBUTE(COUNTRY_CODE, UINT8(0x15))
+ ATTRIBUTE(VIRTUAL_CABLE, SDP_TRUE)
+ ATTRIBUTE(RECONNECT_INITIATE, SDP_FALSE)
+ /* TODO: extract from hid->usbdev->report_desc */
+ ATTRIBUTE(DESCRIPTOR_LIST, LIST(
+ LIST(UINT8(0x22) ARRAY(
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x06, /* Usage (Keyboard) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0xe0, /* Usage Minimum (224) */
+ 0x29, 0xe7, /* Usage Maximum (231) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x05, 0x08, /* Usage Page (LEDs) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x05, /* Usage Maximum (5) */
+ 0x91, 0x02, /* Output (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x03, /* Report Size (3) */
+ 0x91, 0x01, /* Output (Constant) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0xff, /* Logical Maximum (255) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x29, 0xff, /* Usage Maximum (255) */
+ 0x81, 0x00, /* Input (Data, Array) */
+ 0xc0 /* End Collection */
+ ))))
+ ATTRIBUTE(LANG_ID_BASE_LIST, LIST(
+ LIST(UINT16(0x0409) UINT16(0x0100))
+ ))
+ ATTRIBUTE(SDP_DISABLE, SDP_FALSE)
+ ATTRIBUTE(BATTERY_POWER, SDP_TRUE)
+ ATTRIBUTE(REMOTE_WAKEUP, SDP_TRUE)
+ ATTRIBUTE(BOOT_DEVICE, SDP_TRUE) /* XXX: untested */
+ ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80))
+ ATTRIBUTE(NORMALLY_CONNECTABLE, SDP_TRUE)
+ ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100))
+)
+
+SERVICE(sdp,
+ ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */
+ ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID)))
+ ATTRIBUTE(RECORD_STATE, UINT32(1))
+ ATTRIBUTE(PROTO_DESC_LIST, LIST(
+ LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
+ LIST(UUID128(SDP_UUID))
+ ))
+ ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
+ ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
+ UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
+ ))
+ ATTRIBUTE(PFILE_DESC_LIST, LIST(
+ LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100))
+ ))
+ ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html"))
+ ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION))
+
+ /* Profile specific */
+ ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100)))
+ ATTRIBUTE(SVCDB_STATE , UINT32(1))
+)
+
+SERVICE(pnp,
+ ATTRIBUTE(RECORD_HANDLE, UINT32(0)) /* Filled in later */
+ ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID)))
+ ATTRIBUTE(RECORD_STATE, UINT32(1))
+ ATTRIBUTE(PROTO_DESC_LIST, LIST(
+ LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
+ LIST(UUID128(SDP_UUID))
+ ))
+ ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
+ ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
+ UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
+ ))
+ ATTRIBUTE(PFILE_DESC_LIST, LIST(
+ LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100))
+ ))
+ ATTRIBUTE(DOC_URL, URL("http://bellard.org/qemu/user-doc.html"))
+ ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU " QEMU_VERSION))
+
+ /* Profile specific */
+ ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100))
+ ATTRIBUTE(VERSION, UINT16(0x0100))
+ ATTRIBUTE(PRIMARY_RECORD, SDP_TRUE)
+)
+
+static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev,
+ struct bt_l2cap_conn_params_s *params)
+{
+ struct bt_l2cap_sdp_state_s *sdp = qemu_mallocz(sizeof(*sdp));
+ struct sdp_def_service_s *services[] = {
+ &sdp_service_sdp_s,
+ &sdp_service_hid_s,
+ &sdp_service_pnp_s,
+ NULL,
+ };
+
+ sdp->channel = params;
+ sdp->channel->opaque = sdp;
+ sdp->channel->close = bt_l2cap_sdp_close_ch;
+ sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in;
+
+ sdp_service_db_build(sdp, services);
+
+ return 0;
+}
+
+void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev)
+{
+ bt_l2cap_psm_register(dev, BT_PSM_SDP,
+ MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch);
+}
diff --git a/hw/bt.c b/hw/bt.c
new file mode 100644
index 0000000..34bf004
--- /dev/null
+++ b/hw/bt.c
@@ -0,0 +1,121 @@
+/*
+ * Convenience functions for bluetooth.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "net.h"
+#include "bt.h"
+
+/* Slave implementations can ignore this */
+static void bt_dummy_lmp_mode_change(struct bt_link_s *link)
+{
+}
+
+/* Slaves should never receive these PDUs */
+static void bt_dummy_lmp_connection_complete(struct bt_link_s *link)
+{
+ if (link->slave->reject_reason)
+ fprintf(stderr, "%s: stray LMP_not_accepted received, fixme\n",
+ __FUNCTION__);
+ else
+ fprintf(stderr, "%s: stray LMP_accepted received, fixme\n",
+ __FUNCTION__);
+ exit(-1);
+}
+
+static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link)
+{
+ fprintf(stderr, "%s: stray LMP_detach received, fixme\n", __FUNCTION__);
+ exit(-1);
+}
+
+static void bt_dummy_lmp_acl_resp(struct bt_link_s *link,
+ const uint8_t *data, int start, int len)
+{
+ fprintf(stderr, "%s: stray ACL response PDU, fixme\n", __FUNCTION__);
+ exit(-1);
+}
+
+/* Slaves that don't hold any additional per link state can use these */
+static void bt_dummy_lmp_connection_request(struct bt_link_s *req)
+{
+ struct bt_link_s *link = qemu_mallocz(sizeof(struct bt_link_s));
+
+ link->slave = req->slave;
+ link->host = req->host;
+
+ req->host->reject_reason = 0;
+ req->host->lmp_connection_complete(link);
+}
+
+static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link)
+{
+ qemu_free(link);
+}
+
+static void bt_dummy_destroy(struct bt_device_s *device)
+{
+ bt_device_done(device);
+ qemu_free(device);
+}
+
+static int bt_dev_idx = 0;
+
+void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net)
+{
+ memset(dev, 0, sizeof(*dev));
+ dev->inquiry_scan = 1;
+ dev->page_scan = 1;
+
+ dev->bd_addr.b[0] = bt_dev_idx & 0xff;
+ dev->bd_addr.b[1] = bt_dev_idx >> 8;
+ dev->bd_addr.b[2] = 0xd0;
+ dev->bd_addr.b[3] = 0xba;
+ dev->bd_addr.b[4] = 0xbe;
+ dev->bd_addr.b[5] = 0xba;
+ bt_dev_idx ++;
+
+ /* Simple slave-only devices need to implement only .lmp_acl_data */
+ dev->lmp_connection_complete = bt_dummy_lmp_connection_complete;
+ dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master;
+ dev->lmp_acl_resp = bt_dummy_lmp_acl_resp;
+ dev->lmp_mode_change = bt_dummy_lmp_mode_change;
+ dev->lmp_connection_request = bt_dummy_lmp_connection_request;
+ dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave;
+
+ dev->handle_destroy = bt_dummy_destroy;
+
+ dev->net = net;
+ dev->next = net->slave;
+ net->slave = dev;
+}
+
+void bt_device_done(struct bt_device_s *dev)
+{
+ struct bt_device_s **p = &dev->net->slave;
+
+ while (*p && *p != dev)
+ p = &(*p)->next;
+ if (*p != dev) {
+ fprintf(stderr, "%s: bad bt device \"%s\"\n", __FUNCTION__,
+ dev->lmp_name ?: "(null)");
+ exit(-1);
+ }
+
+ *p = dev->next;
+}
diff --git a/hw/bt.h b/hw/bt.h
new file mode 100644
index 0000000..4a702ad
--- /dev/null
+++ b/hw/bt.h
@@ -0,0 +1,2183 @@
+/*
+ * QEMU Bluetooth HCI helpers.
+ *
+ * Copyright (C) 2007 OpenMoko, Inc.
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * Useful definitions taken from BlueZ project's headers.
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2006 Marcel Holtmann <marcel@holtmann.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+/* BD Address */
+typedef struct {
+ uint8_t b[6];
+} __attribute__((packed)) bdaddr_t;
+
+#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
+
+/* Copy, swap, convert BD Address */
+static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
+{
+ return memcmp(ba1, ba2, sizeof(bdaddr_t));
+}
+static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src)
+{
+ memcpy(dst, src, sizeof(bdaddr_t));
+}
+
+#define BAINIT(orig) { .b = { \
+ (orig)->b[0], (orig)->b[1], (orig)->b[2], \
+ (orig)->b[3], (orig)->b[4], (orig)->b[5], \
+}, }
+
+/* The twisted structures of a bluetooth environment */
+struct bt_device_s;
+struct bt_scatternet_s;
+struct bt_piconet_s;
+struct bt_link_s;
+
+struct bt_scatternet_s {
+ struct bt_device_s *slave;
+};
+
+struct bt_link_s {
+ struct bt_device_s *slave, *host;
+ uint16_t handle; /* Master (host) side handle */
+ uint16_t acl_interval;
+ enum {
+ acl_active,
+ acl_hold,
+ acl_sniff,
+ acl_parked,
+ } acl_mode;
+};
+
+struct bt_device_s {
+ int lt_addr;
+ bdaddr_t bd_addr;
+ int mtu;
+ int setup;
+ struct bt_scatternet_s *net;
+
+ uint8_t key[16];
+ int key_present;
+ uint8_t class[3];
+
+ uint8_t reject_reason;
+
+ uint64_t lmp_caps;
+ const char *lmp_name;
+ void (*lmp_connection_request)(struct bt_link_s *link);
+ void (*lmp_connection_complete)(struct bt_link_s *link);
+ void (*lmp_disconnect_master)(struct bt_link_s *link);
+ void (*lmp_disconnect_slave)(struct bt_link_s *link);
+ void (*lmp_acl_data)(struct bt_link_s *link, const uint8_t *data,
+ int start, int len);
+ void (*lmp_acl_resp)(struct bt_link_s *link, const uint8_t *data,
+ int start, int len);
+ void (*lmp_mode_change)(struct bt_link_s *link);
+
+ void (*handle_destroy)(struct bt_device_s *device);
+ struct bt_device_s *next; /* Next in the piconet/scatternet */
+
+ int inquiry_scan;
+ int page_scan;
+
+ uint16_t clkoff; /* Note: Always little-endian */
+};
+
+/* bt.c */
+void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net);
+void bt_device_done(struct bt_device_s *dev);
+
+/* bt-hci.c */
+struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net);
+
+/* bt-vhci.c */
+void bt_vhci_init(struct HCIInfo *info);
+
+/* bt-hci-csr.c */
+enum {
+ csrhci_pin_reset,
+ csrhci_pin_wakeup,
+ __csrhci_pins,
+};
+qemu_irq *csrhci_pins_get(CharDriverState *chr);
+CharDriverState *uart_hci_init(qemu_irq wakeup);
+
+/* bt-l2cap.c */
+struct bt_l2cap_device_s;
+struct bt_l2cap_conn_params_s;
+struct bt_l2cap_psm_s;
+void bt_l2cap_device_init(struct bt_l2cap_device_s *dev,
+ struct bt_scatternet_s *net);
+void bt_l2cap_device_done(struct bt_l2cap_device_s *dev);
+void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm,
+ int min_mtu, int (*new_channel)(struct bt_l2cap_device_s *dev,
+ struct bt_l2cap_conn_params_s *params));
+
+struct bt_l2cap_device_s {
+ struct bt_device_s device;
+ struct bt_l2cap_psm_s *first_psm;
+};
+
+struct bt_l2cap_conn_params_s {
+ /* Input */
+ uint8_t *(*sdu_out)(struct bt_l2cap_conn_params_s *chan, int len);
+ void (*sdu_submit)(struct bt_l2cap_conn_params_s *chan);
+ int remote_mtu;
+ /* Output */
+ void *opaque;
+ void (*sdu_in)(void *opaque, const uint8_t *data, int len);
+ void (*close)(void *opaque);
+};
+
+enum bt_l2cap_psm_predef {
+ BT_PSM_SDP = 0x0001,
+ BT_PSM_RFCOMM = 0x0003,
+ BT_PSM_TELEPHONY = 0x0005,
+ BT_PSM_TCS = 0x0007,
+ BT_PSM_BNEP = 0x000f,
+ BT_PSM_HID_CTRL = 0x0011,
+ BT_PSM_HID_INTR = 0x0013,
+ BT_PSM_UPNP = 0x0015,
+ BT_PSM_AVCTP = 0x0017,
+ BT_PSM_AVDTP = 0x0019,
+};
+
+/* bt-sdp.c */
+void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev);
+
+/* bt-hid.c */
+struct bt_device_s *bt_mouse_init(struct bt_scatternet_s *net);
+struct bt_device_s *bt_tablet_init(struct bt_scatternet_s *net);
+struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net);
+
+/* Link Management Protocol layer defines */
+
+#define LLID_ACLU_CONT 0x1
+#define LLID_ACLU_START 0x2
+#define LLID_ACLC 0x3
+
+enum lmp_pdu_type {
+ LMP_NAME_REQ = 0x0001,
+ LMP_NAME_RES = 0x0002,
+ LMP_ACCEPTED = 0x0003,
+ LMP_NOT_ACCEPTED = 0x0004,
+ LMP_CLKOFFSET_REQ = 0x0005,
+ LMP_CLKOFFSET_RES = 0x0006,
+ LMP_DETACH = 0x0007,
+ LMP_IN_RAND = 0x0008,
+ LMP_COMB_KEY = 0x0009,
+ LMP_UNIT_KEY = 0x000a,
+ LMP_AU_RAND = 0x000b,
+ LMP_SRES = 0x000c,
+ LMP_TEMP_RAND = 0x000d,
+ LMP_TEMP_KEY = 0x000e,
+ LMP_CRYPT_MODE_REQ = 0x000f,
+ LMP_CRYPT_KEY_SIZE_REQ = 0x0010,
+ LMP_START_ENCRYPT_REQ = 0x0011,
+ LMP_STOP_ENCRYPT_REQ = 0x0012,
+ LMP_SWITCH_REQ = 0x0013,
+ LMP_HOLD = 0x0014,
+ LMP_HOLD_REQ = 0x0015,
+ LMP_SNIFF_REQ = 0x0017,
+ LMP_UNSNIFF_REQ = 0x0018,
+ LMP_LMP_PARK_REQ = 0x0019,
+ LMP_SET_BCAST_SCAN_WND = 0x001b,
+ LMP_MODIFY_BEACON = 0x001c,
+ LMP_UNPARK_BD_ADDR_REQ = 0x001d,
+ LMP_UNPARK_PM_ADDR_REQ = 0x001e,
+ LMP_INCR_POWER_REQ = 0x001f,
+ LMP_DECR_POWER_REQ = 0x0020,
+ LMP_MAX_POWER = 0x0021,
+ LMP_MIN_POWER = 0x0022,
+ LMP_AUTO_RATE = 0x0023,
+ LMP_PREFERRED_RATE = 0x0024,
+ LMP_VERSION_REQ = 0x0025,
+ LMP_VERSION_RES = 0x0026,
+ LMP_FEATURES_REQ = 0x0027,
+ LMP_FEATURES_RES = 0x0028,
+ LMP_QUALITY_OF_SERVICE = 0x0029,
+ LMP_QOS_REQ = 0x002a,
+ LMP_RM_SCO_LINK_REQ = 0x002b,
+ LMP_SCO_LINK_REQ = 0x002c,
+ LMP_MAX_SLOT = 0x002d,
+ LMP_MAX_SLOT_REQ = 0x002e,
+ LMP_TIMING_ACCURACY_REQ = 0x002f,
+ LMP_TIMING_ACCURACY_RES = 0x0030,
+ LMP_SETUP_COMPLETE = 0x0031,
+ LMP_USE_SEMIPERM_KEY = 0x0032,
+ LMP_HOST_CONNECTION_REQ = 0x0033,
+ LMP_SLOT_OFFSET = 0x0034,
+ LMP_PAGE_MODE_REQ = 0x0035,
+ LMP_PAGE_SCAN_MODE_REQ = 0x0036,
+ LMP_SUPERVISION_TIMEOUT = 0x0037,
+ LMP_TEST_ACTIVATE = 0x0038,
+ LMP_TEST_CONTROL = 0x0039,
+ LMP_CRYPT_KEY_MASK_REQ = 0x003a,
+ LMP_CRYPT_KEY_MASK_RES = 0x003b,
+ LMP_SET_AFH = 0x003c,
+ LMP_ACCEPTED_EXT = 0x7f01,
+ LMP_NOT_ACCEPTED_EXT = 0x7f02,
+ LMP_FEATURES_REQ_EXT = 0x7f03,
+ LMP_FEATURES_RES_EXT = 0x7f04,
+ LMP_PACKET_TYPE_TBL_REQ = 0x7f0b,
+ LMP_ESCO_LINK_REQ = 0x7f0c,
+ LMP_RM_ESCO_LINK_REQ = 0x7f0d,
+ LMP_CHANNEL_CLASS_REQ = 0x7f10,
+ LMP_CHANNEL_CLASS = 0x7f11,
+};
+
+/* Host Controller Interface layer defines */
+
+enum hci_packet_type {
+ HCI_COMMAND_PKT = 0x01,
+ HCI_ACLDATA_PKT = 0x02,
+ HCI_SCODATA_PKT = 0x03,
+ HCI_EVENT_PKT = 0x04,
+ HCI_VENDOR_PKT = 0xff,
+};
+
+enum bt_packet_type {
+ HCI_2DH1 = 1 << 1,
+ HCI_3DH1 = 1 << 2,
+ HCI_DM1 = 1 << 3,
+ HCI_DH1 = 1 << 4,
+ HCI_2DH3 = 1 << 8,
+ HCI_3DH3 = 1 << 9,
+ HCI_DM3 = 1 << 10,
+ HCI_DH3 = 1 << 11,
+ HCI_2DH5 = 1 << 12,
+ HCI_3DH5 = 1 << 13,
+ HCI_DM5 = 1 << 14,
+ HCI_DH5 = 1 << 15,
+};
+
+enum sco_packet_type {
+ HCI_HV1 = 1 << 5,
+ HCI_HV2 = 1 << 6,
+ HCI_HV3 = 1 << 7,
+};
+
+enum ev_packet_type {
+ HCI_EV3 = 1 << 3,
+ HCI_EV4 = 1 << 4,
+ HCI_EV5 = 1 << 5,
+ HCI_2EV3 = 1 << 6,
+ HCI_3EV3 = 1 << 7,
+ HCI_2EV5 = 1 << 8,
+ HCI_3EV5 = 1 << 9,
+};
+
+enum hci_error_code {
+ HCI_SUCCESS = 0x00,
+ HCI_UNKNOWN_COMMAND = 0x01,
+ HCI_NO_CONNECTION = 0x02,
+ HCI_HARDWARE_FAILURE = 0x03,
+ HCI_PAGE_TIMEOUT = 0x04,
+ HCI_AUTHENTICATION_FAILURE = 0x05,
+ HCI_PIN_OR_KEY_MISSING = 0x06,
+ HCI_MEMORY_FULL = 0x07,
+ HCI_CONNECTION_TIMEOUT = 0x08,
+ HCI_MAX_NUMBER_OF_CONNECTIONS = 0x09,
+ HCI_MAX_NUMBER_OF_SCO_CONNECTIONS = 0x0a,
+ HCI_ACL_CONNECTION_EXISTS = 0x0b,
+ HCI_COMMAND_DISALLOWED = 0x0c,
+ HCI_REJECTED_LIMITED_RESOURCES = 0x0d,
+ HCI_REJECTED_SECURITY = 0x0e,
+ HCI_REJECTED_PERSONAL = 0x0f,
+ HCI_HOST_TIMEOUT = 0x10,
+ HCI_UNSUPPORTED_FEATURE = 0x11,
+ HCI_INVALID_PARAMETERS = 0x12,
+ HCI_OE_USER_ENDED_CONNECTION = 0x13,
+ HCI_OE_LOW_RESOURCES = 0x14,
+ HCI_OE_POWER_OFF = 0x15,
+ HCI_CONNECTION_TERMINATED = 0x16,
+ HCI_REPEATED_ATTEMPTS = 0x17,
+ HCI_PAIRING_NOT_ALLOWED = 0x18,
+ HCI_UNKNOWN_LMP_PDU = 0x19,
+ HCI_UNSUPPORTED_REMOTE_FEATURE = 0x1a,
+ HCI_SCO_OFFSET_REJECTED = 0x1b,
+ HCI_SCO_INTERVAL_REJECTED = 0x1c,
+ HCI_AIR_MODE_REJECTED = 0x1d,
+ HCI_INVALID_LMP_PARAMETERS = 0x1e,
+ HCI_UNSPECIFIED_ERROR = 0x1f,
+ HCI_UNSUPPORTED_LMP_PARAMETER_VALUE = 0x20,
+ HCI_ROLE_CHANGE_NOT_ALLOWED = 0x21,
+ HCI_LMP_RESPONSE_TIMEOUT = 0x22,
+ HCI_LMP_ERROR_TRANSACTION_COLLISION = 0x23,
+ HCI_LMP_PDU_NOT_ALLOWED = 0x24,
+ HCI_ENCRYPTION_MODE_NOT_ACCEPTED = 0x25,
+ HCI_UNIT_LINK_KEY_USED = 0x26,
+ HCI_QOS_NOT_SUPPORTED = 0x27,
+ HCI_INSTANT_PASSED = 0x28,
+ HCI_PAIRING_NOT_SUPPORTED = 0x29,
+ HCI_TRANSACTION_COLLISION = 0x2a,
+ HCI_QOS_UNACCEPTABLE_PARAMETER = 0x2c,
+ HCI_QOS_REJECTED = 0x2d,
+ HCI_CLASSIFICATION_NOT_SUPPORTED = 0x2e,
+ HCI_INSUFFICIENT_SECURITY = 0x2f,
+ HCI_PARAMETER_OUT_OF_RANGE = 0x30,
+ HCI_ROLE_SWITCH_PENDING = 0x32,
+ HCI_SLOT_VIOLATION = 0x34,
+ HCI_ROLE_SWITCH_FAILED = 0x35,
+};
+
+enum acl_flag_bits {
+ ACL_CONT = 1 << 0,
+ ACL_START = 1 << 1,
+ ACL_ACTIVE_BCAST = 1 << 2,
+ ACL_PICO_BCAST = 1 << 3,
+};
+
+enum baseband_link_type {
+ SCO_LINK = 0x00,
+ ACL_LINK = 0x01,
+};
+
+enum lmp_feature_bits0 {
+ LMP_3SLOT = 1 << 0,
+ LMP_5SLOT = 1 << 1,
+ LMP_ENCRYPT = 1 << 2,
+ LMP_SOFFSET = 1 << 3,
+ LMP_TACCURACY = 1 << 4,
+ LMP_RSWITCH = 1 << 5,
+ LMP_HOLD_MODE = 1 << 6,
+ LMP_SNIFF_MODE = 1 << 7,
+};
+
+enum lmp_feature_bits1 {
+ LMP_PARK = 1 << 0,
+ LMP_RSSI = 1 << 1,
+ LMP_QUALITY = 1 << 2,
+ LMP_SCO = 1 << 3,
+ LMP_HV2 = 1 << 4,
+ LMP_HV3 = 1 << 5,
+ LMP_ULAW = 1 << 6,
+ LMP_ALAW = 1 << 7,
+};
+
+enum lmp_feature_bits2 {
+ LMP_CVSD = 1 << 0,
+ LMP_PSCHEME = 1 << 1,
+ LMP_PCONTROL = 1 << 2,
+ LMP_TRSP_SCO = 1 << 3,
+ LMP_BCAST_ENC = 1 << 7,
+};
+
+enum lmp_feature_bits3 {
+ LMP_EDR_ACL_2M = 1 << 1,
+ LMP_EDR_ACL_3M = 1 << 2,
+ LMP_ENH_ISCAN = 1 << 3,
+ LMP_ILACE_ISCAN = 1 << 4,
+ LMP_ILACE_PSCAN = 1 << 5,
+ LMP_RSSI_INQ = 1 << 6,
+ LMP_ESCO = 1 << 7,
+};
+
+enum lmp_feature_bits4 {
+ LMP_EV4 = 1 << 0,
+ LMP_EV5 = 1 << 1,
+ LMP_AFH_CAP_SLV = 1 << 3,
+ LMP_AFH_CLS_SLV = 1 << 4,
+ LMP_EDR_3SLOT = 1 << 7,
+};
+
+enum lmp_feature_bits5 {
+ LMP_EDR_5SLOT = 1 << 0,
+ LMP_SNIFF_SUBR = 1 << 1,
+ LMP_AFH_CAP_MST = 1 << 3,
+ LMP_AFH_CLS_MST = 1 << 4,
+ LMP_EDR_ESCO_2M = 1 << 5,
+ LMP_EDR_ESCO_3M = 1 << 6,
+ LMP_EDR_3S_ESCO = 1 << 7,
+};
+
+enum lmp_feature_bits6 {
+ LMP_EXT_INQ = 1 << 0,
+};
+
+enum lmp_feature_bits7 {
+ LMP_EXT_FEAT = 1 << 7,
+};
+
+enum hci_link_policy {
+ HCI_LP_RSWITCH = 1 << 0,
+ HCI_LP_HOLD = 1 << 1,
+ HCI_LP_SNIFF = 1 << 2,
+ HCI_LP_PARK = 1 << 3,
+};
+
+enum hci_link_mode {
+ HCI_LM_ACCEPT = 1 << 15,
+ HCI_LM_MASTER = 1 << 0,
+ HCI_LM_AUTH = 1 << 1,
+ HCI_LM_ENCRYPT = 1 << 2,
+ HCI_LM_TRUSTED = 1 << 3,
+ HCI_LM_RELIABLE = 1 << 4,
+ HCI_LM_SECURE = 1 << 5,
+};
+
+/* HCI Commands */
+
+/* Link Control */
+#define OGF_LINK_CTL 0x01
+
+#define OCF_INQUIRY 0x0001
+typedef struct {
+ uint8_t lap[3];
+ uint8_t length; /* 1.28s units */
+ uint8_t num_rsp;
+} __attribute__ ((packed)) inquiry_cp;
+#define INQUIRY_CP_SIZE 5
+
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) status_bdaddr_rp;
+#define STATUS_BDADDR_RP_SIZE 7
+
+#define OCF_INQUIRY_CANCEL 0x0002
+
+#define OCF_PERIODIC_INQUIRY 0x0003
+typedef struct {
+ uint16_t max_period; /* 1.28s units */
+ uint16_t min_period; /* 1.28s units */
+ uint8_t lap[3];
+ uint8_t length; /* 1.28s units */
+ uint8_t num_rsp;
+} __attribute__ ((packed)) periodic_inquiry_cp;
+#define PERIODIC_INQUIRY_CP_SIZE 9
+
+#define OCF_EXIT_PERIODIC_INQUIRY 0x0004
+
+#define OCF_CREATE_CONN 0x0005
+typedef struct {
+ bdaddr_t bdaddr;
+ uint16_t pkt_type;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_mode;
+ uint16_t clock_offset;
+ uint8_t role_switch;
+} __attribute__ ((packed)) create_conn_cp;
+#define CREATE_CONN_CP_SIZE 13
+
+#define OCF_DISCONNECT 0x0006
+typedef struct {
+ uint16_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) disconnect_cp;
+#define DISCONNECT_CP_SIZE 3
+
+#define OCF_ADD_SCO 0x0007
+typedef struct {
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) add_sco_cp;
+#define ADD_SCO_CP_SIZE 4
+
+#define OCF_CREATE_CONN_CANCEL 0x0008
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) create_conn_cancel_cp;
+#define CREATE_CONN_CANCEL_CP_SIZE 6
+
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) create_conn_cancel_rp;
+#define CREATE_CONN_CANCEL_RP_SIZE 7
+
+#define OCF_ACCEPT_CONN_REQ 0x0009
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t role;
+} __attribute__ ((packed)) accept_conn_req_cp;
+#define ACCEPT_CONN_REQ_CP_SIZE 7
+
+#define OCF_REJECT_CONN_REQ 0x000A
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t reason;
+} __attribute__ ((packed)) reject_conn_req_cp;
+#define REJECT_CONN_REQ_CP_SIZE 7
+
+#define OCF_LINK_KEY_REPLY 0x000B
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t link_key[16];
+} __attribute__ ((packed)) link_key_reply_cp;
+#define LINK_KEY_REPLY_CP_SIZE 22
+
+#define OCF_LINK_KEY_NEG_REPLY 0x000C
+
+#define OCF_PIN_CODE_REPLY 0x000D
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pin_len;
+ uint8_t pin_code[16];
+} __attribute__ ((packed)) pin_code_reply_cp;
+#define PIN_CODE_REPLY_CP_SIZE 23
+
+#define OCF_PIN_CODE_NEG_REPLY 0x000E
+
+#define OCF_SET_CONN_PTYPE 0x000F
+typedef struct {
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) set_conn_ptype_cp;
+#define SET_CONN_PTYPE_CP_SIZE 4
+
+#define OCF_AUTH_REQUESTED 0x0011
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) auth_requested_cp;
+#define AUTH_REQUESTED_CP_SIZE 2
+
+#define OCF_SET_CONN_ENCRYPT 0x0013
+typedef struct {
+ uint16_t handle;
+ uint8_t encrypt;
+} __attribute__ ((packed)) set_conn_encrypt_cp;
+#define SET_CONN_ENCRYPT_CP_SIZE 3
+
+#define OCF_CHANGE_CONN_LINK_KEY 0x0015
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) change_conn_link_key_cp;
+#define CHANGE_CONN_LINK_KEY_CP_SIZE 2
+
+#define OCF_MASTER_LINK_KEY 0x0017
+typedef struct {
+ uint8_t key_flag;
+} __attribute__ ((packed)) master_link_key_cp;
+#define MASTER_LINK_KEY_CP_SIZE 1
+
+#define OCF_REMOTE_NAME_REQ 0x0019
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_mode;
+ uint16_t clock_offset;
+} __attribute__ ((packed)) remote_name_req_cp;
+#define REMOTE_NAME_REQ_CP_SIZE 10
+
+#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) remote_name_req_cancel_cp;
+#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6
+
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) remote_name_req_cancel_rp;
+#define REMOTE_NAME_REQ_CANCEL_RP_SIZE 7
+
+#define OCF_READ_REMOTE_FEATURES 0x001B
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_remote_features_cp;
+#define READ_REMOTE_FEATURES_CP_SIZE 2
+
+#define OCF_READ_REMOTE_EXT_FEATURES 0x001C
+typedef struct {
+ uint16_t handle;
+ uint8_t page_num;
+} __attribute__ ((packed)) read_remote_ext_features_cp;
+#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3
+
+#define OCF_READ_REMOTE_VERSION 0x001D
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_remote_version_cp;
+#define READ_REMOTE_VERSION_CP_SIZE 2
+
+#define OCF_READ_CLOCK_OFFSET 0x001F
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_clock_offset_cp;
+#define READ_CLOCK_OFFSET_CP_SIZE 2
+
+#define OCF_READ_LMP_HANDLE 0x0020
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_lmp_handle_cp;
+#define READ_LMP_HANDLE_CP_SIZE 2
+
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t lmp_handle;
+ uint32_t reserved;
+} __attribute__ ((packed)) read_lmp_handle_rp;
+#define READ_LMP_HANDLE_RP_SIZE 8
+
+#define OCF_SETUP_SYNC_CONN 0x0028
+typedef struct {
+ uint16_t handle;
+ uint32_t tx_bandwith;
+ uint32_t rx_bandwith;
+ uint16_t max_latency;
+ uint16_t voice_setting;
+ uint8_t retrans_effort;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) setup_sync_conn_cp;
+#define SETUP_SYNC_CONN_CP_SIZE 17
+
+#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029
+typedef struct {
+ bdaddr_t bdaddr;
+ uint32_t tx_bandwith;
+ uint32_t rx_bandwith;
+ uint16_t max_latency;
+ uint16_t voice_setting;
+ uint8_t retrans_effort;
+ uint16_t pkt_type;
+} __attribute__ ((packed)) accept_sync_conn_req_cp;
+#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21
+
+#define OCF_REJECT_SYNC_CONN_REQ 0x002A
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t reason;
+} __attribute__ ((packed)) reject_sync_conn_req_cp;
+#define REJECT_SYNC_CONN_REQ_CP_SIZE 7
+
+/* Link Policy */
+#define OGF_LINK_POLICY 0x02
+
+#define OCF_HOLD_MODE 0x0001
+typedef struct {
+ uint16_t handle;
+ uint16_t max_interval;
+ uint16_t min_interval;
+} __attribute__ ((packed)) hold_mode_cp;
+#define HOLD_MODE_CP_SIZE 6
+
+#define OCF_SNIFF_MODE 0x0003
+typedef struct {
+ uint16_t handle;
+ uint16_t max_interval;
+ uint16_t min_interval;
+ uint16_t attempt;
+ uint16_t timeout;
+} __attribute__ ((packed)) sniff_mode_cp;
+#define SNIFF_MODE_CP_SIZE 10
+
+#define OCF_EXIT_SNIFF_MODE 0x0004
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) exit_sniff_mode_cp;
+#define EXIT_SNIFF_MODE_CP_SIZE 2
+
+#define OCF_PARK_MODE 0x0005
+typedef struct {
+ uint16_t handle;
+ uint16_t max_interval;
+ uint16_t min_interval;
+} __attribute__ ((packed)) park_mode_cp;
+#define PARK_MODE_CP_SIZE 6
+
+#define OCF_EXIT_PARK_MODE 0x0006
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) exit_park_mode_cp;
+#define EXIT_PARK_MODE_CP_SIZE 2
+
+#define OCF_QOS_SETUP 0x0007
+typedef struct {
+ uint8_t service_type; /* 1 = best effort */
+ uint32_t token_rate; /* Byte per seconds */
+ uint32_t peak_bandwidth; /* Byte per seconds */
+ uint32_t latency; /* Microseconds */
+ uint32_t delay_variation; /* Microseconds */
+} __attribute__ ((packed)) hci_qos;
+#define HCI_QOS_CP_SIZE 17
+typedef struct {
+ uint16_t handle;
+ uint8_t flags; /* Reserved */
+ hci_qos qos;
+} __attribute__ ((packed)) qos_setup_cp;
+#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE)
+
+#define OCF_ROLE_DISCOVERY 0x0009
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) role_discovery_cp;
+#define ROLE_DISCOVERY_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t role;
+} __attribute__ ((packed)) role_discovery_rp;
+#define ROLE_DISCOVERY_RP_SIZE 4
+
+#define OCF_SWITCH_ROLE 0x000B
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t role;
+} __attribute__ ((packed)) switch_role_cp;
+#define SWITCH_ROLE_CP_SIZE 7
+
+#define OCF_READ_LINK_POLICY 0x000C
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_link_policy_cp;
+#define READ_LINK_POLICY_CP_SIZE 2
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t policy;
+} __attribute__ ((packed)) read_link_policy_rp;
+#define READ_LINK_POLICY_RP_SIZE 5
+
+#define OCF_WRITE_LINK_POLICY 0x000D
+typedef struct {
+ uint16_t handle;
+ uint16_t policy;
+} __attribute__ ((packed)) write_link_policy_cp;
+#define WRITE_LINK_POLICY_CP_SIZE 4
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) write_link_policy_rp;
+#define WRITE_LINK_POLICY_RP_SIZE 3
+
+#define OCF_READ_DEFAULT_LINK_POLICY 0x000E
+
+#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F
+
+#define OCF_FLOW_SPECIFICATION 0x0010
+
+#define OCF_SNIFF_SUBRATE 0x0011
+typedef struct {
+ uint16_t handle;
+ uint16_t max_remote_latency;
+ uint16_t max_local_latency;
+ uint16_t min_remote_timeout;
+ uint16_t min_local_timeout;
+} __attribute__ ((packed)) sniff_subrate_cp;
+#define SNIFF_SUBRATE_CP_SIZE 10
+
+/* Host Controller and Baseband */
+#define OGF_HOST_CTL 0x03
+
+#define OCF_SET_EVENT_MASK 0x0001
+typedef struct {
+ uint8_t mask[8];
+} __attribute__ ((packed)) set_event_mask_cp;
+#define SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_RESET 0x0003
+
+#define OCF_SET_EVENT_FLT 0x0005
+typedef struct {
+ uint8_t flt_type;
+ uint8_t cond_type;
+ uint8_t condition[0];
+} __attribute__ ((packed)) set_event_flt_cp;
+#define SET_EVENT_FLT_CP_SIZE 2
+
+enum bt_filter_type {
+ FLT_CLEAR_ALL = 0x00,
+ FLT_INQ_RESULT = 0x01,
+ FLT_CONN_SETUP = 0x02,
+};
+enum inq_result_cond_type {
+ INQ_RESULT_RETURN_ALL = 0x00,
+ INQ_RESULT_RETURN_CLASS = 0x01,
+ INQ_RESULT_RETURN_BDADDR = 0x02,
+};
+enum conn_setup_cond_type {
+ CONN_SETUP_ALLOW_ALL = 0x00,
+ CONN_SETUP_ALLOW_CLASS = 0x01,
+ CONN_SETUP_ALLOW_BDADDR = 0x02,
+};
+enum conn_setup_cond {
+ CONN_SETUP_AUTO_OFF = 0x01,
+ CONN_SETUP_AUTO_ON = 0x02,
+};
+
+#define OCF_FLUSH 0x0008
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) flush_cp;
+#define FLUSH_CP_SIZE 2
+
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) flush_rp;
+#define FLUSH_RP_SIZE 3
+
+#define OCF_READ_PIN_TYPE 0x0009
+typedef struct {
+ uint8_t status;
+ uint8_t pin_type;
+} __attribute__ ((packed)) read_pin_type_rp;
+#define READ_PIN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_PIN_TYPE 0x000A
+typedef struct {
+ uint8_t pin_type;
+} __attribute__ ((packed)) write_pin_type_cp;
+#define WRITE_PIN_TYPE_CP_SIZE 1
+
+#define OCF_CREATE_NEW_UNIT_KEY 0x000B
+
+#define OCF_READ_STORED_LINK_KEY 0x000D
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t read_all;
+} __attribute__ ((packed)) read_stored_link_key_cp;
+#define READ_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+ uint8_t status;
+ uint16_t max_keys;
+ uint16_t num_keys;
+} __attribute__ ((packed)) read_stored_link_key_rp;
+#define READ_STORED_LINK_KEY_RP_SIZE 5
+
+#define OCF_WRITE_STORED_LINK_KEY 0x0011
+typedef struct {
+ uint8_t num_keys;
+ /* variable length part */
+} __attribute__ ((packed)) write_stored_link_key_cp;
+#define WRITE_STORED_LINK_KEY_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+ uint8_t num_keys;
+} __attribute__ ((packed)) write_stored_link_key_rp;
+#define READ_WRITE_LINK_KEY_RP_SIZE 2
+
+#define OCF_DELETE_STORED_LINK_KEY 0x0012
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t delete_all;
+} __attribute__ ((packed)) delete_stored_link_key_cp;
+#define DELETE_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+ uint8_t status;
+ uint16_t num_keys;
+} __attribute__ ((packed)) delete_stored_link_key_rp;
+#define DELETE_STORED_LINK_KEY_RP_SIZE 3
+
+#define OCF_CHANGE_LOCAL_NAME 0x0013
+typedef struct {
+ char name[248];
+} __attribute__ ((packed)) change_local_name_cp;
+#define CHANGE_LOCAL_NAME_CP_SIZE 248
+
+#define OCF_READ_LOCAL_NAME 0x0014
+typedef struct {
+ uint8_t status;
+ char name[248];
+} __attribute__ ((packed)) read_local_name_rp;
+#define READ_LOCAL_NAME_RP_SIZE 249
+
+#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015
+typedef struct {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed)) read_conn_accept_timeout_rp;
+#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016
+typedef struct {
+ uint16_t timeout;
+} __attribute__ ((packed)) write_conn_accept_timeout_cp;
+#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_PAGE_TIMEOUT 0x0017
+typedef struct {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed)) read_page_timeout_rp;
+#define READ_PAGE_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_PAGE_TIMEOUT 0x0018
+typedef struct {
+ uint16_t timeout;
+} __attribute__ ((packed)) write_page_timeout_cp;
+#define WRITE_PAGE_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_SCAN_ENABLE 0x0019
+typedef struct {
+ uint8_t status;
+ uint8_t enable;
+} __attribute__ ((packed)) read_scan_enable_rp;
+#define READ_SCAN_ENABLE_RP_SIZE 2
+
+#define OCF_WRITE_SCAN_ENABLE 0x001A
+typedef struct {
+ uint8_t scan_enable;
+} __attribute__ ((packed)) write_scan_enable_cp;
+#define WRITE_SCAN_ENABLE_CP_SIZE 1
+
+enum scan_enable_bits {
+ SCAN_DISABLED = 0,
+ SCAN_INQUIRY = 1 << 0,
+ SCAN_PAGE = 1 << 1,
+};
+
+#define OCF_READ_PAGE_ACTIVITY 0x001B
+typedef struct {
+ uint8_t status;
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) read_page_activity_rp;
+#define READ_PAGE_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_PAGE_ACTIVITY 0x001C
+typedef struct {
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) write_page_activity_cp;
+#define WRITE_PAGE_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_INQ_ACTIVITY 0x001D
+typedef struct {
+ uint8_t status;
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) read_inq_activity_rp;
+#define READ_INQ_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_INQ_ACTIVITY 0x001E
+typedef struct {
+ uint16_t interval;
+ uint16_t window;
+} __attribute__ ((packed)) write_inq_activity_cp;
+#define WRITE_INQ_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_AUTH_ENABLE 0x001F
+
+#define OCF_WRITE_AUTH_ENABLE 0x0020
+
+#define AUTH_DISABLED 0x00
+#define AUTH_ENABLED 0x01
+
+#define OCF_READ_ENCRYPT_MODE 0x0021
+
+#define OCF_WRITE_ENCRYPT_MODE 0x0022
+
+#define ENCRYPT_DISABLED 0x00
+#define ENCRYPT_P2P 0x01
+#define ENCRYPT_BOTH 0x02
+
+#define OCF_READ_CLASS_OF_DEV 0x0023
+typedef struct {
+ uint8_t status;
+ uint8_t dev_class[3];
+} __attribute__ ((packed)) read_class_of_dev_rp;
+#define READ_CLASS_OF_DEV_RP_SIZE 4
+
+#define OCF_WRITE_CLASS_OF_DEV 0x0024
+typedef struct {
+ uint8_t dev_class[3];
+} __attribute__ ((packed)) write_class_of_dev_cp;
+#define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
+#define OCF_READ_VOICE_SETTING 0x0025
+typedef struct {
+ uint8_t status;
+ uint16_t voice_setting;
+} __attribute__ ((packed)) read_voice_setting_rp;
+#define READ_VOICE_SETTING_RP_SIZE 3
+
+#define OCF_WRITE_VOICE_SETTING 0x0026
+typedef struct {
+ uint16_t voice_setting;
+} __attribute__ ((packed)) write_voice_setting_cp;
+#define WRITE_VOICE_SETTING_CP_SIZE 2
+
+#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027
+
+#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028
+
+#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029
+
+#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A
+
+#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B
+
+#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C
+
+#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D
+typedef struct {
+ uint16_t handle;
+ uint8_t type;
+} __attribute__ ((packed)) read_transmit_power_level_cp;
+#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ int8_t level;
+} __attribute__ ((packed)) read_transmit_power_level_rp;
+#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4
+
+#define OCF_HOST_BUFFER_SIZE 0x0033
+typedef struct {
+ uint16_t acl_mtu;
+ uint8_t sco_mtu;
+ uint16_t acl_max_pkt;
+ uint16_t sco_max_pkt;
+} __attribute__ ((packed)) host_buffer_size_cp;
+#define HOST_BUFFER_SIZE_CP_SIZE 7
+
+#define OCF_HOST_NUMBER_OF_COMPLETED_PACKETS 0x0035
+
+#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t link_sup_to;
+} __attribute__ ((packed)) read_link_supervision_timeout_rp;
+#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037
+typedef struct {
+ uint16_t handle;
+ uint16_t link_sup_to;
+} __attribute__ ((packed)) write_link_supervision_timeout_cp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) write_link_supervision_timeout_rp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3
+
+#define OCF_READ_NUM_SUPPORTED_IAC 0x0038
+
+#define MAX_IAC_LAP 0x40
+#define OCF_READ_CURRENT_IAC_LAP 0x0039
+typedef struct {
+ uint8_t status;
+ uint8_t num_current_iac;
+ uint8_t lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) read_current_iac_lap_rp;
+#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP
+
+#define OCF_WRITE_CURRENT_IAC_LAP 0x003A
+typedef struct {
+ uint8_t num_current_iac;
+ uint8_t lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) write_current_iac_lap_cp;
+#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP
+
+#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B
+
+#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C
+
+#define OCF_READ_PAGE_SCAN_MODE 0x003D
+
+#define OCF_WRITE_PAGE_SCAN_MODE 0x003E
+
+#define OCF_SET_AFH_CLASSIFICATION 0x003F
+typedef struct {
+ uint8_t map[10];
+} __attribute__ ((packed)) set_afh_classification_cp;
+#define SET_AFH_CLASSIFICATION_CP_SIZE 10
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) set_afh_classification_rp;
+#define SET_AFH_CLASSIFICATION_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042
+typedef struct {
+ uint8_t status;
+ uint8_t type;
+} __attribute__ ((packed)) read_inquiry_scan_type_rp;
+#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043
+typedef struct {
+ uint8_t type;
+} __attribute__ ((packed)) write_inquiry_scan_type_cp;
+#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_inquiry_scan_type_rp;
+#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_MODE 0x0044
+typedef struct {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed)) read_inquiry_mode_rp;
+#define READ_INQUIRY_MODE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_MODE 0x0045
+typedef struct {
+ uint8_t mode;
+} __attribute__ ((packed)) write_inquiry_mode_cp;
+#define WRITE_INQUIRY_MODE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_inquiry_mode_rp;
+#define WRITE_INQUIRY_MODE_RP_SIZE 1
+
+#define OCF_READ_PAGE_SCAN_TYPE 0x0046
+
+#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047
+
+#define OCF_READ_AFH_MODE 0x0048
+typedef struct {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed)) read_afh_mode_rp;
+#define READ_AFH_MODE_RP_SIZE 2
+
+#define OCF_WRITE_AFH_MODE 0x0049
+typedef struct {
+ uint8_t mode;
+} __attribute__ ((packed)) write_afh_mode_cp;
+#define WRITE_AFH_MODE_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_afh_mode_rp;
+#define WRITE_AFH_MODE_RP_SIZE 1
+
+#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051
+typedef struct {
+ uint8_t status;
+ uint8_t fec;
+ uint8_t data[240];
+} __attribute__ ((packed)) read_ext_inquiry_response_rp;
+#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242
+
+#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052
+typedef struct {
+ uint8_t fec;
+ uint8_t data[240];
+} __attribute__ ((packed)) write_ext_inquiry_response_cp;
+#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241
+typedef struct {
+ uint8_t status;
+} __attribute__ ((packed)) write_ext_inquiry_response_rp;
+#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1
+
+/* Informational Parameters */
+#define OGF_INFO_PARAM 0x04
+
+#define OCF_READ_LOCAL_VERSION 0x0001
+typedef struct {
+ uint8_t status;
+ uint8_t hci_ver;
+ uint16_t hci_rev;
+ uint8_t lmp_ver;
+ uint16_t manufacturer;
+ uint16_t lmp_subver;
+} __attribute__ ((packed)) read_local_version_rp;
+#define READ_LOCAL_VERSION_RP_SIZE 9
+
+#define OCF_READ_LOCAL_COMMANDS 0x0002
+typedef struct {
+ uint8_t status;
+ uint8_t commands[64];
+} __attribute__ ((packed)) read_local_commands_rp;
+#define READ_LOCAL_COMMANDS_RP_SIZE 65
+
+#define OCF_READ_LOCAL_FEATURES 0x0003
+typedef struct {
+ uint8_t status;
+ uint8_t features[8];
+} __attribute__ ((packed)) read_local_features_rp;
+#define READ_LOCAL_FEATURES_RP_SIZE 9
+
+#define OCF_READ_LOCAL_EXT_FEATURES 0x0004
+typedef struct {
+ uint8_t page_num;
+} __attribute__ ((packed)) read_local_ext_features_cp;
+#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1
+typedef struct {
+ uint8_t status;
+ uint8_t page_num;
+ uint8_t max_page_num;
+ uint8_t features[8];
+} __attribute__ ((packed)) read_local_ext_features_rp;
+#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11
+
+#define OCF_READ_BUFFER_SIZE 0x0005
+typedef struct {
+ uint8_t status;
+ uint16_t acl_mtu;
+ uint8_t sco_mtu;
+ uint16_t acl_max_pkt;
+ uint16_t sco_max_pkt;
+} __attribute__ ((packed)) read_buffer_size_rp;
+#define READ_BUFFER_SIZE_RP_SIZE 8
+
+#define OCF_READ_COUNTRY_CODE 0x0007
+typedef struct {
+ uint8_t status;
+ uint8_t country_code;
+} __attribute__ ((packed)) read_country_code_rp;
+#define READ_COUNTRY_CODE_RP_SIZE 2
+
+#define OCF_READ_BD_ADDR 0x0009
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) read_bd_addr_rp;
+#define READ_BD_ADDR_RP_SIZE 7
+
+/* Status params */
+#define OGF_STATUS_PARAM 0x05
+
+#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t counter;
+} __attribute__ ((packed)) read_failed_contact_counter_rp;
+#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) reset_failed_contact_counter_rp;
+#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_READ_LINK_QUALITY 0x0003
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) read_link_quality_cp;
+#define READ_LINK_QUALITY_CP_SIZE 4
+
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t link_quality;
+} __attribute__ ((packed)) read_link_quality_rp;
+#define READ_LINK_QUALITY_RP_SIZE 4
+
+#define OCF_READ_RSSI 0x0005
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ int8_t rssi;
+} __attribute__ ((packed)) read_rssi_rp;
+#define READ_RSSI_RP_SIZE 4
+
+#define OCF_READ_AFH_MAP 0x0006
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t mode;
+ uint8_t map[10];
+} __attribute__ ((packed)) read_afh_map_rp;
+#define READ_AFH_MAP_RP_SIZE 14
+
+#define OCF_READ_CLOCK 0x0007
+typedef struct {
+ uint16_t handle;
+ uint8_t which_clock;
+} __attribute__ ((packed)) read_clock_cp;
+#define READ_CLOCK_CP_SIZE 3
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint32_t clock;
+ uint16_t accuracy;
+} __attribute__ ((packed)) read_clock_rp;
+#define READ_CLOCK_RP_SIZE 9
+
+/* Testing commands */
+#define OGF_TESTING_CMD 0x3e
+
+/* Vendor specific commands */
+#define OGF_VENDOR_CMD 0x3f
+
+/* HCI Events */
+
+#define EVT_INQUIRY_COMPLETE 0x01
+
+#define EVT_INQUIRY_RESULT 0x02
+typedef struct {
+ uint8_t num_responses;
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t pscan_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+} __attribute__ ((packed)) inquiry_info;
+#define INQUIRY_INFO_SIZE 14
+
+#define EVT_CONN_COMPLETE 0x03
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ bdaddr_t bdaddr;
+ uint8_t link_type;
+ uint8_t encr_mode;
+} __attribute__ ((packed)) evt_conn_complete;
+#define EVT_CONN_COMPLETE_SIZE 11
+
+#define EVT_CONN_REQUEST 0x04
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t dev_class[3];
+ uint8_t link_type;
+} __attribute__ ((packed)) evt_conn_request;
+#define EVT_CONN_REQUEST_SIZE 10
+
+#define EVT_DISCONN_COMPLETE 0x05
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t reason;
+} __attribute__ ((packed)) evt_disconn_complete;
+#define EVT_DISCONN_COMPLETE_SIZE 4
+
+#define EVT_AUTH_COMPLETE 0x06
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) evt_auth_complete;
+#define EVT_AUTH_COMPLETE_SIZE 3
+
+#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+ char name[248];
+} __attribute__ ((packed)) evt_remote_name_req_complete;
+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
+
+#define EVT_ENCRYPT_CHANGE 0x08
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t encrypt;
+} __attribute__ ((packed)) evt_encrypt_change;
+#define EVT_ENCRYPT_CHANGE_SIZE 5
+
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+} __attribute__ ((packed)) evt_change_conn_link_key_complete;
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3
+
+#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t key_flag;
+} __attribute__ ((packed)) evt_master_link_key_complete;
+#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4
+
+#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t features[8];
+} __attribute__ ((packed)) evt_read_remote_features_complete;
+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t lmp_ver;
+ uint16_t manufacturer;
+ uint16_t lmp_subver;
+} __attribute__ ((packed)) evt_read_remote_version_complete;
+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
+
+#define EVT_QOS_SETUP_COMPLETE 0x0D
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t flags; /* Reserved */
+ hci_qos qos;
+} __attribute__ ((packed)) evt_qos_setup_complete;
+#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE)
+
+#define EVT_CMD_COMPLETE 0x0E
+typedef struct {
+ uint8_t ncmd;
+ uint16_t opcode;
+} __attribute__ ((packed)) evt_cmd_complete;
+#define EVT_CMD_COMPLETE_SIZE 3
+
+#define EVT_CMD_STATUS 0x0F
+typedef struct {
+ uint8_t status;
+ uint8_t ncmd;
+ uint16_t opcode;
+} __attribute__ ((packed)) evt_cmd_status;
+#define EVT_CMD_STATUS_SIZE 4
+
+#define EVT_HARDWARE_ERROR 0x10
+typedef struct {
+ uint8_t code;
+} __attribute__ ((packed)) evt_hardware_error;
+#define EVT_HARDWARE_ERROR_SIZE 1
+
+#define EVT_FLUSH_OCCURRED 0x11
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) evt_flush_occured;
+#define EVT_FLUSH_OCCURRED_SIZE 2
+
+#define EVT_ROLE_CHANGE 0x12
+typedef struct {
+ uint8_t status;
+ bdaddr_t bdaddr;
+ uint8_t role;
+} __attribute__ ((packed)) evt_role_change;
+#define EVT_ROLE_CHANGE_SIZE 8
+
+#define EVT_NUM_COMP_PKTS 0x13
+typedef struct {
+ uint8_t num_hndl;
+ struct {
+ uint16_t handle;
+ uint16_t num_packets;
+ } connection[0];
+} __attribute__ ((packed)) evt_num_comp_pkts;
+#define EVT_NUM_COMP_PKTS_SIZE(num_hndl) (1 + 4 * (num_hndl))
+
+#define EVT_MODE_CHANGE 0x14
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t mode;
+ uint16_t interval;
+} __attribute__ ((packed)) evt_mode_change;
+#define EVT_MODE_CHANGE_SIZE 6
+
+#define EVT_RETURN_LINK_KEYS 0x15
+typedef struct {
+ uint8_t num_keys;
+ /* variable length part */
+} __attribute__ ((packed)) evt_return_link_keys;
+#define EVT_RETURN_LINK_KEYS_SIZE 1
+
+#define EVT_PIN_CODE_REQ 0x16
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_pin_code_req;
+#define EVT_PIN_CODE_REQ_SIZE 6
+
+#define EVT_LINK_KEY_REQ 0x17
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_link_key_req;
+#define EVT_LINK_KEY_REQ_SIZE 6
+
+#define EVT_LINK_KEY_NOTIFY 0x18
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t link_key[16];
+ uint8_t key_type;
+} __attribute__ ((packed)) evt_link_key_notify;
+#define EVT_LINK_KEY_NOTIFY_SIZE 23
+
+#define EVT_LOOPBACK_COMMAND 0x19
+
+#define EVT_DATA_BUFFER_OVERFLOW 0x1A
+typedef struct {
+ uint8_t link_type;
+} __attribute__ ((packed)) evt_data_buffer_overflow;
+#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1
+
+#define EVT_MAX_SLOTS_CHANGE 0x1B
+typedef struct {
+ uint16_t handle;
+ uint8_t max_slots;
+} __attribute__ ((packed)) evt_max_slots_change;
+#define EVT_MAX_SLOTS_CHANGE_SIZE 3
+
+#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t clock_offset;
+} __attribute__ ((packed)) evt_read_clock_offset_complete;
+#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5
+
+#define EVT_CONN_PTYPE_CHANGED 0x1D
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t ptype;
+} __attribute__ ((packed)) evt_conn_ptype_changed;
+#define EVT_CONN_PTYPE_CHANGED_SIZE 5
+
+#define EVT_QOS_VIOLATION 0x1E
+typedef struct {
+ uint16_t handle;
+} __attribute__ ((packed)) evt_qos_violation;
+#define EVT_QOS_VIOLATION_SIZE 2
+
+#define EVT_PSCAN_REP_MODE_CHANGE 0x20
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+} __attribute__ ((packed)) evt_pscan_rep_mode_change;
+#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7
+
+#define EVT_FLOW_SPEC_COMPLETE 0x21
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t flags;
+ uint8_t direction;
+ hci_qos qos;
+} __attribute__ ((packed)) evt_flow_spec_complete;
+#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE)
+
+#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22
+typedef struct {
+ uint8_t num_responses;
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi;
+#define INQUIRY_INFO_WITH_RSSI_SIZE 15
+typedef struct {
+ uint8_t num_responses;
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t pscan_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode;
+#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 16
+
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t page_num;
+ uint8_t max_page_num;
+ uint8_t features[8];
+} __attribute__ ((packed)) evt_read_remote_ext_features_complete;
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13
+
+#define EVT_SYNC_CONN_COMPLETE 0x2C
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ bdaddr_t bdaddr;
+ uint8_t link_type;
+ uint8_t trans_interval;
+ uint8_t retrans_window;
+ uint16_t rx_pkt_len;
+ uint16_t tx_pkt_len;
+ uint8_t air_mode;
+} __attribute__ ((packed)) evt_sync_conn_complete;
+#define EVT_SYNC_CONN_COMPLETE_SIZE 17
+
+#define EVT_SYNC_CONN_CHANGED 0x2D
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t trans_interval;
+ uint8_t retrans_window;
+ uint16_t rx_pkt_len;
+ uint16_t tx_pkt_len;
+} __attribute__ ((packed)) evt_sync_conn_changed;
+#define EVT_SYNC_CONN_CHANGED_SIZE 9
+
+#define EVT_SNIFF_SUBRATE 0x2E
+typedef struct {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t max_remote_latency;
+ uint16_t max_local_latency;
+ uint16_t min_remote_timeout;
+ uint16_t min_local_timeout;
+} __attribute__ ((packed)) evt_sniff_subrate;
+#define EVT_SNIFF_SUBRATE_SIZE 11
+
+#define EVT_EXTENDED_INQUIRY_RESULT 0x2F
+typedef struct {
+ bdaddr_t bdaddr;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+ uint8_t data[240];
+} __attribute__ ((packed)) extended_inquiry_info;
+#define EXTENDED_INQUIRY_INFO_SIZE 254
+
+#define EVT_TESTING 0xFE
+
+#define EVT_VENDOR 0xFF
+
+/* Command opcode pack/unpack */
+#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10))
+#define cmd_opcode_ogf(op) (op >> 10)
+#define cmd_opcode_ocf(op) (op & 0x03ff)
+
+/* ACL handle and flags pack/unpack */
+#define acl_handle_pack(h, f) (uint16_t)(((h) & 0x0fff)|((f) << 12))
+#define acl_handle(h) ((h) & 0x0fff)
+#define acl_flags(h) ((h) >> 12)
+
+/* HCI Packet structures */
+#define HCI_COMMAND_HDR_SIZE 3
+#define HCI_EVENT_HDR_SIZE 2
+#define HCI_ACL_HDR_SIZE 4
+#define HCI_SCO_HDR_SIZE 3
+
+struct hci_command_hdr {
+ uint16_t opcode; /* OCF & OGF */
+ uint8_t plen;
+} __attribute__ ((packed));
+
+struct hci_event_hdr {
+ uint8_t evt;
+ uint8_t plen;
+} __attribute__ ((packed));
+
+struct hci_acl_hdr {
+ uint16_t handle; /* Handle & Flags(PB, BC) */
+ uint16_t dlen;
+} __attribute__ ((packed));
+
+struct hci_sco_hdr {
+ uint16_t handle;
+ uint8_t dlen;
+} __attribute__ ((packed));
+
+/* L2CAP layer defines */
+
+enum bt_l2cap_lm_bits {
+ L2CAP_LM_MASTER = 1 << 0,
+ L2CAP_LM_AUTH = 1 << 1,
+ L2CAP_LM_ENCRYPT = 1 << 2,
+ L2CAP_LM_TRUSTED = 1 << 3,
+ L2CAP_LM_RELIABLE = 1 << 4,
+ L2CAP_LM_SECURE = 1 << 5,
+};
+
+enum bt_l2cap_cid_predef {
+ L2CAP_CID_INVALID = 0x0000,
+ L2CAP_CID_SIGNALLING= 0x0001,
+ L2CAP_CID_GROUP = 0x0002,
+ L2CAP_CID_ALLOC = 0x0040,
+};
+
+/* L2CAP command codes */
+enum bt_l2cap_cmd {
+ L2CAP_COMMAND_REJ = 1,
+ L2CAP_CONN_REQ,
+ L2CAP_CONN_RSP,
+ L2CAP_CONF_REQ,
+ L2CAP_CONF_RSP,
+ L2CAP_DISCONN_REQ,
+ L2CAP_DISCONN_RSP,
+ L2CAP_ECHO_REQ,
+ L2CAP_ECHO_RSP,
+ L2CAP_INFO_REQ,
+ L2CAP_INFO_RSP,
+};
+
+enum bt_l2cap_sar_bits {
+ L2CAP_SAR_NO_SEG = 0,
+ L2CAP_SAR_START,
+ L2CAP_SAR_END,
+ L2CAP_SAR_CONT,
+};
+
+/* L2CAP structures */
+typedef struct {
+ uint16_t len;
+ uint16_t cid;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_hdr;
+#define L2CAP_HDR_SIZE 4
+
+typedef struct {
+ uint8_t code;
+ uint8_t ident;
+ uint16_t len;
+} __attribute__ ((packed)) l2cap_cmd_hdr;
+#define L2CAP_CMD_HDR_SIZE 4
+
+typedef struct {
+ uint16_t reason;
+} __attribute__ ((packed)) l2cap_cmd_rej;
+#define L2CAP_CMD_REJ_SIZE 2
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_cmd_rej_cid;
+#define L2CAP_CMD_REJ_CID_SIZE 4
+
+/* reject reason */
+enum bt_l2cap_rej_reason {
+ L2CAP_REJ_CMD_NOT_UNDERSTOOD = 0,
+ L2CAP_REJ_SIG_TOOBIG,
+ L2CAP_REJ_CID_INVAL,
+};
+
+typedef struct {
+ uint16_t psm;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_conn_req;
+#define L2CAP_CONN_REQ_SIZE 4
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+ uint16_t result;
+ uint16_t status;
+} __attribute__ ((packed)) l2cap_conn_rsp;
+#define L2CAP_CONN_RSP_SIZE 8
+
+/* connect result */
+enum bt_l2cap_conn_res {
+ L2CAP_CR_SUCCESS = 0,
+ L2CAP_CR_PEND,
+ L2CAP_CR_BAD_PSM,
+ L2CAP_CR_SEC_BLOCK,
+ L2CAP_CR_NO_MEM,
+};
+
+/* connect status */
+enum bt_l2cap_conn_stat {
+ L2CAP_CS_NO_INFO = 0,
+ L2CAP_CS_AUTHEN_PEND,
+ L2CAP_CS_AUTHOR_PEND,
+};
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t flags;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_conf_req;
+#define L2CAP_CONF_REQ_SIZE(datalen) (4 + (datalen))
+
+typedef struct {
+ uint16_t scid;
+ uint16_t flags;
+ uint16_t result;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_conf_rsp;
+#define L2CAP_CONF_RSP_SIZE(datalen) (6 + datalen)
+
+enum bt_l2cap_conf_res {
+ L2CAP_CONF_SUCCESS = 0,
+ L2CAP_CONF_UNACCEPT,
+ L2CAP_CONF_REJECT,
+ L2CAP_CONF_UNKNOWN,
+};
+
+typedef struct {
+ uint8_t type;
+ uint8_t len;
+ uint8_t val[0];
+} __attribute__ ((packed)) l2cap_conf_opt;
+#define L2CAP_CONF_OPT_SIZE 2
+
+enum bt_l2cap_conf_val {
+ L2CAP_CONF_MTU = 1,
+ L2CAP_CONF_FLUSH_TO,
+ L2CAP_CONF_QOS,
+ L2CAP_CONF_RFC,
+ L2CAP_CONF_RFC_MODE = L2CAP_CONF_RFC,
+};
+
+typedef struct {
+ uint8_t flags;
+ uint8_t service_type;
+ uint32_t token_rate;
+ uint32_t token_bucket_size;
+ uint32_t peak_bandwidth;
+ uint32_t latency;
+ uint32_t delay_variation;
+} __attribute__ ((packed)) l2cap_conf_opt_qos;
+#define L2CAP_CONF_OPT_QOS_SIZE 22
+
+enum bt_l2cap_conf_opt_qos_st {
+ L2CAP_CONF_QOS_NO_TRAFFIC = 0x00,
+ L2CAP_CONF_QOS_BEST_EFFORT,
+ L2CAP_CONF_QOS_GUARANTEED,
+};
+
+#define L2CAP_CONF_QOS_WILDCARD 0xffffffff
+
+enum bt_l2cap_mode {
+ L2CAP_MODE_BASIC = 0,
+ L2CAP_MODE_RETRANS = 1,
+ L2CAP_MODE_FLOWCTL = 2,
+};
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_disconn_req;
+#define L2CAP_DISCONN_REQ_SIZE 4
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+} __attribute__ ((packed)) l2cap_disconn_rsp;
+#define L2CAP_DISCONN_RSP_SIZE 4
+
+typedef struct {
+ uint16_t type;
+} __attribute__ ((packed)) l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE 2
+
+typedef struct {
+ uint16_t type;
+ uint16_t result;
+ uint8_t data[0];
+} __attribute__ ((packed)) l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE 4
+
+/* info type */
+enum bt_l2cap_info_type {
+ L2CAP_IT_CL_MTU = 1,
+ L2CAP_IT_FEAT_MASK,
+};
+
+/* info result */
+enum bt_l2cap_info_result {
+ L2CAP_IR_SUCCESS = 0,
+ L2CAP_IR_NOTSUPP,
+};
+
+/* Service Discovery Protocol defines */
+/* Note that all multibyte values in lower layer protocols (above in this file)
+ * are little-endian while SDP is big-endian. */
+
+/* Protocol UUIDs */
+enum sdp_proto_uuid {
+ SDP_UUID = 0x0001,
+ UDP_UUID = 0x0002,
+ RFCOMM_UUID = 0x0003,
+ TCP_UUID = 0x0004,
+ TCS_BIN_UUID = 0x0005,
+ TCS_AT_UUID = 0x0006,
+ OBEX_UUID = 0x0008,
+ IP_UUID = 0x0009,
+ FTP_UUID = 0x000a,
+ HTTP_UUID = 0x000c,
+ WSP_UUID = 0x000e,
+ BNEP_UUID = 0x000f,
+ UPNP_UUID = 0x0010,
+ HIDP_UUID = 0x0011,
+ HCRP_CTRL_UUID = 0x0012,
+ HCRP_DATA_UUID = 0x0014,
+ HCRP_NOTE_UUID = 0x0016,
+ AVCTP_UUID = 0x0017,
+ AVDTP_UUID = 0x0019,
+ CMTP_UUID = 0x001b,
+ UDI_UUID = 0x001d,
+ MCAP_CTRL_UUID = 0x001e,
+ MCAP_DATA_UUID = 0x001f,
+ L2CAP_UUID = 0x0100,
+};
+
+/*
+ * Service class identifiers of standard services and service groups
+ */
+enum service_class_id {
+ SDP_SERVER_SVCLASS_ID = 0x1000,
+ BROWSE_GRP_DESC_SVCLASS_ID = 0x1001,
+ PUBLIC_BROWSE_GROUP = 0x1002,
+ SERIAL_PORT_SVCLASS_ID = 0x1101,
+ LAN_ACCESS_SVCLASS_ID = 0x1102,
+ DIALUP_NET_SVCLASS_ID = 0x1103,
+ IRMC_SYNC_SVCLASS_ID = 0x1104,
+ OBEX_OBJPUSH_SVCLASS_ID = 0x1105,
+ OBEX_FILETRANS_SVCLASS_ID = 0x1106,
+ IRMC_SYNC_CMD_SVCLASS_ID = 0x1107,
+ HEADSET_SVCLASS_ID = 0x1108,
+ CORDLESS_TELEPHONY_SVCLASS_ID = 0x1109,
+ AUDIO_SOURCE_SVCLASS_ID = 0x110a,
+ AUDIO_SINK_SVCLASS_ID = 0x110b,
+ AV_REMOTE_TARGET_SVCLASS_ID = 0x110c,
+ ADVANCED_AUDIO_SVCLASS_ID = 0x110d,
+ AV_REMOTE_SVCLASS_ID = 0x110e,
+ VIDEO_CONF_SVCLASS_ID = 0x110f,
+ INTERCOM_SVCLASS_ID = 0x1110,
+ FAX_SVCLASS_ID = 0x1111,
+ HEADSET_AGW_SVCLASS_ID = 0x1112,
+ WAP_SVCLASS_ID = 0x1113,
+ WAP_CLIENT_SVCLASS_ID = 0x1114,
+ PANU_SVCLASS_ID = 0x1115,
+ NAP_SVCLASS_ID = 0x1116,
+ GN_SVCLASS_ID = 0x1117,
+ DIRECT_PRINTING_SVCLASS_ID = 0x1118,
+ REFERENCE_PRINTING_SVCLASS_ID = 0x1119,
+ IMAGING_SVCLASS_ID = 0x111a,
+ IMAGING_RESPONDER_SVCLASS_ID = 0x111b,
+ IMAGING_ARCHIVE_SVCLASS_ID = 0x111c,
+ IMAGING_REFOBJS_SVCLASS_ID = 0x111d,
+ HANDSFREE_SVCLASS_ID = 0x111e,
+ HANDSFREE_AGW_SVCLASS_ID = 0x111f,
+ DIRECT_PRT_REFOBJS_SVCLASS_ID = 0x1120,
+ REFLECTED_UI_SVCLASS_ID = 0x1121,
+ BASIC_PRINTING_SVCLASS_ID = 0x1122,
+ PRINTING_STATUS_SVCLASS_ID = 0x1123,
+ HID_SVCLASS_ID = 0x1124,
+ HCR_SVCLASS_ID = 0x1125,
+ HCR_PRINT_SVCLASS_ID = 0x1126,
+ HCR_SCAN_SVCLASS_ID = 0x1127,
+ CIP_SVCLASS_ID = 0x1128,
+ VIDEO_CONF_GW_SVCLASS_ID = 0x1129,
+ UDI_MT_SVCLASS_ID = 0x112a,
+ UDI_TA_SVCLASS_ID = 0x112b,
+ AV_SVCLASS_ID = 0x112c,
+ SAP_SVCLASS_ID = 0x112d,
+ PBAP_PCE_SVCLASS_ID = 0x112e,
+ PBAP_PSE_SVCLASS_ID = 0x112f,
+ PBAP_SVCLASS_ID = 0x1130,
+ PNP_INFO_SVCLASS_ID = 0x1200,
+ GENERIC_NETWORKING_SVCLASS_ID = 0x1201,
+ GENERIC_FILETRANS_SVCLASS_ID = 0x1202,
+ GENERIC_AUDIO_SVCLASS_ID = 0x1203,
+ GENERIC_TELEPHONY_SVCLASS_ID = 0x1204,
+ UPNP_SVCLASS_ID = 0x1205,
+ UPNP_IP_SVCLASS_ID = 0x1206,
+ UPNP_PAN_SVCLASS_ID = 0x1300,
+ UPNP_LAP_SVCLASS_ID = 0x1301,
+ UPNP_L2CAP_SVCLASS_ID = 0x1302,
+ VIDEO_SOURCE_SVCLASS_ID = 0x1303,
+ VIDEO_SINK_SVCLASS_ID = 0x1304,
+ VIDEO_DISTRIBUTION_SVCLASS_ID = 0x1305,
+ MDP_SVCLASS_ID = 0x1400,
+ MDP_SOURCE_SVCLASS_ID = 0x1401,
+ MDP_SINK_SVCLASS_ID = 0x1402,
+ APPLE_AGENT_SVCLASS_ID = 0x2112,
+};
+
+/*
+ * Standard profile descriptor identifiers; note these
+ * may be identical to some of the service classes defined above
+ */
+#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID
+#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID
+#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID
+#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID
+#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID
+#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID
+#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID
+#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID
+#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID
+#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID
+#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID
+#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID
+#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID
+#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID
+#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID
+#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID
+#define VIDEO_CONF_PROFILE_ID VIDEO_CONF_SVCLASS_ID
+#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID
+#define FAX_PROFILE_ID FAX_SVCLASS_ID
+#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID
+#define WAP_PROFILE_ID WAP_SVCLASS_ID
+#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID
+#define PANU_PROFILE_ID PANU_SVCLASS_ID
+#define NAP_PROFILE_ID NAP_SVCLASS_ID
+#define GN_PROFILE_ID GN_SVCLASS_ID
+#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID
+#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID
+#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID
+#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID
+#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID
+#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID
+#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID
+#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID
+#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID
+#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID
+#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID
+#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID
+#define HID_PROFILE_ID HID_SVCLASS_ID
+#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID
+#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID
+#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID
+#define CIP_PROFILE_ID CIP_SVCLASS_ID
+#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID
+#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID
+#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID
+#define AV_PROFILE_ID AV_SVCLASS_ID
+#define SAP_PROFILE_ID SAP_SVCLASS_ID
+#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID
+#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID
+#define PBAP_PROFILE_ID PBAP_SVCLASS_ID
+#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID
+#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID
+#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID
+#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID
+#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID
+#define UPNP_PROFILE_ID UPNP_SVCLASS_ID
+#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID
+#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID
+#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID
+#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID
+#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID
+#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID
+#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID
+#define MDP_PROFILE_ID MDP_SVCLASS_ID
+#define MDP_SOURCE_PROFILE_ID MDP_SROUCE_SVCLASS_ID
+#define MDP_SINK_PROFILE_ID MDP_SINK_SVCLASS_ID
+#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID
+
+/* Data Representation */
+enum bt_sdp_data_type {
+ SDP_DTYPE_NIL = 0 << 3,
+ SDP_DTYPE_UINT = 1 << 3,
+ SDP_DTYPE_SINT = 2 << 3,
+ SDP_DTYPE_UUID = 3 << 3,
+ SDP_DTYPE_STRING = 4 << 3,
+ SDP_DTYPE_BOOL = 5 << 3,
+ SDP_DTYPE_SEQ = 6 << 3,
+ SDP_DTYPE_ALT = 7 << 3,
+ SDP_DTYPE_URL = 8 << 3,
+};
+
+enum bt_sdp_data_size {
+ SDP_DSIZE_1 = 0,
+ SDP_DSIZE_2,
+ SDP_DSIZE_4,
+ SDP_DSIZE_8,
+ SDP_DSIZE_16,
+ SDP_DSIZE_NEXT1,
+ SDP_DSIZE_NEXT2,
+ SDP_DSIZE_NEXT4,
+ SDP_DSIZE_MASK = SDP_DSIZE_NEXT4,
+};
+
+enum bt_sdp_cmd {
+ SDP_ERROR_RSP = 0x01,
+ SDP_SVC_SEARCH_REQ = 0x02,
+ SDP_SVC_SEARCH_RSP = 0x03,
+ SDP_SVC_ATTR_REQ = 0x04,
+ SDP_SVC_ATTR_RSP = 0x05,
+ SDP_SVC_SEARCH_ATTR_REQ = 0x06,
+ SDP_SVC_SEARCH_ATTR_RSP = 0x07,
+};
+
+enum bt_sdp_errorcode {
+ SDP_INVALID_VERSION = 0x0001,
+ SDP_INVALID_RECORD_HANDLE = 0x0002,
+ SDP_INVALID_SYNTAX = 0x0003,
+ SDP_INVALID_PDU_SIZE = 0x0004,
+ SDP_INVALID_CSTATE = 0x0005,
+};
+
+/*
+ * String identifiers are based on the SDP spec stating that
+ * "base attribute id of the primary (universal) language must be 0x0100"
+ *
+ * Other languages should have their own offset; e.g.:
+ * #define XXXLangBase yyyy
+ * #define AttrServiceName_XXX 0x0000+XXXLangBase
+ */
+#define SDP_PRIMARY_LANG_BASE 0x0100
+
+enum bt_sdp_attribute_id {
+ SDP_ATTR_RECORD_HANDLE = 0x0000,
+ SDP_ATTR_SVCLASS_ID_LIST = 0x0001,
+ SDP_ATTR_RECORD_STATE = 0x0002,
+ SDP_ATTR_SERVICE_ID = 0x0003,
+ SDP_ATTR_PROTO_DESC_LIST = 0x0004,
+ SDP_ATTR_BROWSE_GRP_LIST = 0x0005,
+ SDP_ATTR_LANG_BASE_ATTR_ID_LIST = 0x0006,
+ SDP_ATTR_SVCINFO_TTL = 0x0007,
+ SDP_ATTR_SERVICE_AVAILABILITY = 0x0008,
+ SDP_ATTR_PFILE_DESC_LIST = 0x0009,
+ SDP_ATTR_DOC_URL = 0x000a,
+ SDP_ATTR_CLNT_EXEC_URL = 0x000b,
+ SDP_ATTR_ICON_URL = 0x000c,
+ SDP_ATTR_ADD_PROTO_DESC_LIST = 0x000d,
+
+ SDP_ATTR_SVCNAME_PRIMARY = SDP_PRIMARY_LANG_BASE + 0,
+ SDP_ATTR_SVCDESC_PRIMARY = SDP_PRIMARY_LANG_BASE + 1,
+ SDP_ATTR_SVCPROV_PRIMARY = SDP_PRIMARY_LANG_BASE + 2,
+
+ SDP_ATTR_GROUP_ID = 0x0200,
+ SDP_ATTR_IP_SUBNET = 0x0200,
+
+ /* SDP */
+ SDP_ATTR_VERSION_NUM_LIST = 0x0200,
+ SDP_ATTR_SVCDB_STATE = 0x0201,
+
+ SDP_ATTR_SERVICE_VERSION = 0x0300,
+ SDP_ATTR_EXTERNAL_NETWORK = 0x0301,
+ SDP_ATTR_SUPPORTED_DATA_STORES_LIST = 0x0301,
+ SDP_ATTR_FAX_CLASS1_SUPPORT = 0x0302,
+ SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL = 0x0302,
+ SDP_ATTR_FAX_CLASS20_SUPPORT = 0x0303,
+ SDP_ATTR_SUPPORTED_FORMATS_LIST = 0x0303,
+ SDP_ATTR_FAX_CLASS2_SUPPORT = 0x0304,
+ SDP_ATTR_AUDIO_FEEDBACK_SUPPORT = 0x0305,
+ SDP_ATTR_NETWORK_ADDRESS = 0x0306,
+ SDP_ATTR_WAP_GATEWAY = 0x0307,
+ SDP_ATTR_HOMEPAGE_URL = 0x0308,
+ SDP_ATTR_WAP_STACK_TYPE = 0x0309,
+ SDP_ATTR_SECURITY_DESC = 0x030a,
+ SDP_ATTR_NET_ACCESS_TYPE = 0x030b,
+ SDP_ATTR_MAX_NET_ACCESSRATE = 0x030c,
+ SDP_ATTR_IP4_SUBNET = 0x030d,
+ SDP_ATTR_IP6_SUBNET = 0x030e,
+ SDP_ATTR_SUPPORTED_CAPABILITIES = 0x0310,
+ SDP_ATTR_SUPPORTED_FEATURES = 0x0311,
+ SDP_ATTR_SUPPORTED_FUNCTIONS = 0x0312,
+ SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY = 0x0313,
+ SDP_ATTR_SUPPORTED_REPOSITORIES = 0x0314,
+
+ /* PnP Information */
+ SDP_ATTR_SPECIFICATION_ID = 0x0200,
+ SDP_ATTR_VENDOR_ID = 0x0201,
+ SDP_ATTR_PRODUCT_ID = 0x0202,
+ SDP_ATTR_VERSION = 0x0203,
+ SDP_ATTR_PRIMARY_RECORD = 0x0204,
+ SDP_ATTR_VENDOR_ID_SOURCE = 0x0205,
+
+ /* BT HID */
+ SDP_ATTR_DEVICE_RELEASE_NUMBER = 0x0200,
+ SDP_ATTR_PARSER_VERSION = 0x0201,
+ SDP_ATTR_DEVICE_SUBCLASS = 0x0202,
+ SDP_ATTR_COUNTRY_CODE = 0x0203,
+ SDP_ATTR_VIRTUAL_CABLE = 0x0204,
+ SDP_ATTR_RECONNECT_INITIATE = 0x0205,
+ SDP_ATTR_DESCRIPTOR_LIST = 0x0206,
+ SDP_ATTR_LANG_ID_BASE_LIST = 0x0207,
+ SDP_ATTR_SDP_DISABLE = 0x0208,
+ SDP_ATTR_BATTERY_POWER = 0x0209,
+ SDP_ATTR_REMOTE_WAKEUP = 0x020a,
+ SDP_ATTR_PROFILE_VERSION = 0x020b,
+ SDP_ATTR_SUPERVISION_TIMEOUT = 0x020c,
+ SDP_ATTR_NORMALLY_CONNECTABLE = 0x020d,
+ SDP_ATTR_BOOT_DEVICE = 0x020e,
+};
diff --git a/hw/cbus.c b/hw/cbus.c
new file mode 100644
index 0000000..8ae24e0
--- /dev/null
+++ b/hw/cbus.c
@@ -0,0 +1,618 @@
+/*
+ * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma /
+ * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms.
+ * Based on reverse-engineering of a linux driver.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "irq.h"
+#include "devices.h"
+#include "sysemu.h"
+
+//#define DEBUG
+
+typedef struct {
+ void *opaque;
+ void (*io)(void *opaque, int rw, int reg, uint16_t *val);
+ int addr;
+} CBusSlave;
+
+typedef struct {
+ CBus cbus;
+
+ int sel;
+ int dat;
+ int clk;
+ int bit;
+ int dir;
+ uint16_t val;
+ qemu_irq dat_out;
+
+ int addr;
+ int reg;
+ int rw;
+ enum {
+ cbus_address,
+ cbus_value,
+ } cycle;
+
+ CBusSlave *slave[8];
+} CBusPriv;
+
+static void cbus_io(CBusPriv *s)
+{
+ if (s->slave[s->addr])
+ s->slave[s->addr]->io(s->slave[s->addr]->opaque,
+ s->rw, s->reg, &s->val);
+ else
+ hw_error("%s: bad slave address %i\n", __FUNCTION__, s->addr);
+}
+
+static void cbus_cycle(CBusPriv *s)
+{
+ switch (s->cycle) {
+ case cbus_address:
+ s->addr = (s->val >> 6) & 7;
+ s->rw = (s->val >> 5) & 1;
+ s->reg = (s->val >> 0) & 0x1f;
+
+ s->cycle = cbus_value;
+ s->bit = 15;
+ s->dir = !s->rw;
+ s->val = 0;
+
+ if (s->rw)
+ cbus_io(s);
+ break;
+
+ case cbus_value:
+ if (!s->rw)
+ cbus_io(s);
+
+ s->cycle = cbus_address;
+ s->bit = 8;
+ s->dir = 1;
+ s->val = 0;
+ break;
+ }
+}
+
+static void cbus_clk(void *opaque, int line, int level)
+{
+ CBusPriv *s = (CBusPriv *) opaque;
+
+ if (!s->sel && level && !s->clk) {
+ if (s->dir)
+ s->val |= s->dat << (s->bit --);
+ else
+ qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1);
+
+ if (s->bit < 0)
+ cbus_cycle(s);
+ }
+
+ s->clk = level;
+}
+
+static void cbus_dat(void *opaque, int line, int level)
+{
+ CBusPriv *s = (CBusPriv *) opaque;
+
+ s->dat = level;
+}
+
+static void cbus_sel(void *opaque, int line, int level)
+{
+ CBusPriv *s = (CBusPriv *) opaque;
+
+ if (!level) {
+ s->dir = 1;
+ s->bit = 8;
+ s->val = 0;
+ }
+
+ s->sel = level;
+}
+
+CBus *cbus_init(qemu_irq dat)
+{
+ CBusPriv *s = (CBusPriv *) qemu_mallocz(sizeof(*s));
+
+ s->dat_out = dat;
+ s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0];
+ s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0];
+ s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0];
+
+ s->sel = 1;
+ s->clk = 0;
+ s->dat = 0;
+
+ return &s->cbus;
+}
+
+void cbus_attach(CBus *bus, void *slave_opaque)
+{
+ CBusSlave *slave = (CBusSlave *) slave_opaque;
+ CBusPriv *s = (CBusPriv *) bus;
+
+ s->slave[slave->addr] = slave;
+}
+
+/* Retu/Vilma */
+typedef struct {
+ uint16_t irqst;
+ uint16_t irqen;
+ uint16_t cc[2];
+ int channel;
+ uint16_t result[16];
+ uint16_t sample;
+ uint16_t status;
+
+ struct {
+ uint16_t cal;
+ } rtc;
+
+ int is_vilma;
+ qemu_irq irq;
+ CBusSlave cbus;
+} CBusRetu;
+
+static void retu_interrupt_update(CBusRetu *s)
+{
+ qemu_set_irq(s->irq, s->irqst & ~s->irqen);
+}
+
+#define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */
+#define RETU_REG_IDR 0x01 /* (T) Interrupt ID */
+#define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */
+#define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */
+#define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */
+#define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */
+#define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */
+#define RETU_REG_ADCR 0x08 /* (RW) ADC result register */
+#define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */
+#define RETU_REG_AFCR 0x0a /* (RW) AFC register */
+#define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */
+#define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/
+#define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */
+#define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */
+#define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */
+#define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */
+#define RETU_REG_TXCR 0x11 /* (RW) TxC register */
+#define RETU_REG_STATUS 0x16 /* (RO) Status register */
+#define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */
+#define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */
+#define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */
+#define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */
+#define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */
+#define RETU_REG_SGR1 0x1c /* (RW) */
+#define RETU_REG_SCR1 0x1d /* (RW) */
+#define RETU_REG_SGR2 0x1e /* (RW) */
+#define RETU_REG_SCR2 0x1f /* (RW) */
+
+/* Retu Interrupt sources */
+enum {
+ retu_int_pwr = 0, /* Power button */
+ retu_int_char = 1, /* Charger */
+ retu_int_rtcs = 2, /* Seconds */
+ retu_int_rtcm = 3, /* Minutes */
+ retu_int_rtcd = 4, /* Days */
+ retu_int_rtca = 5, /* Alarm */
+ retu_int_hook = 6, /* Hook */
+ retu_int_head = 7, /* Headset */
+ retu_int_adcs = 8, /* ADC sample */
+};
+
+/* Retu ADC channel wiring */
+enum {
+ retu_adc_bsi = 1, /* BSI */
+ retu_adc_batt_temp = 2, /* Battery temperature */
+ retu_adc_chg_volt = 3, /* Charger voltage */
+ retu_adc_head_det = 4, /* Headset detection */
+ retu_adc_hook_det = 5, /* Hook detection */
+ retu_adc_rf_gp = 6, /* RF GP */
+ retu_adc_tx_det = 7, /* Wideband Tx detection */
+ retu_adc_batt_volt = 8, /* Battery voltage */
+ retu_adc_sens = 10, /* Light sensor */
+ retu_adc_sens_temp = 11, /* Light sensor temperature */
+ retu_adc_bbatt_volt = 12, /* Backup battery voltage */
+ retu_adc_self_temp = 13, /* RETU temperature */
+};
+
+static inline uint16_t retu_read(CBusRetu *s, int reg)
+{
+#ifdef DEBUG
+ printf("RETU read at %02x\n", reg);
+#endif
+
+ switch (reg) {
+ case RETU_REG_ASICR:
+ return 0x0215 | (s->is_vilma << 7);
+
+ case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */
+ return s->irqst;
+
+ case RETU_REG_IMR:
+ return s->irqen;
+
+ case RETU_REG_RTCDSR:
+ case RETU_REG_RTCHMR:
+ case RETU_REG_RTCHMAR:
+ /* TODO */
+ return 0x0000;
+
+ case RETU_REG_RTCCALR:
+ return s->rtc.cal;
+
+ case RETU_REG_ADCR:
+ return (s->channel << 10) | s->result[s->channel];
+ case RETU_REG_ADCSCR:
+ return s->sample;
+
+ case RETU_REG_AFCR:
+ case RETU_REG_ANTIFR:
+ case RETU_REG_CALIBR:
+ /* TODO */
+ return 0x0000;
+
+ case RETU_REG_CCR1:
+ return s->cc[0];
+ case RETU_REG_CCR2:
+ return s->cc[1];
+
+ case RETU_REG_RCTRL_CLR:
+ case RETU_REG_RCTRL_SET:
+ case RETU_REG_TXCR:
+ /* TODO */
+ return 0x0000;
+
+ case RETU_REG_STATUS:
+ return s->status;
+
+ case RETU_REG_WATCHDOG:
+ case RETU_REG_AUDTXR:
+ case RETU_REG_AUDPAR:
+ case RETU_REG_AUDRXR1:
+ case RETU_REG_AUDRXR2:
+ case RETU_REG_SGR1:
+ case RETU_REG_SCR1:
+ case RETU_REG_SGR2:
+ case RETU_REG_SCR2:
+ /* TODO */
+ return 0x0000;
+
+ default:
+ hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
+ }
+}
+
+static inline void retu_write(CBusRetu *s, int reg, uint16_t val)
+{
+#ifdef DEBUG
+ printf("RETU write of %04x at %02x\n", val, reg);
+#endif
+
+ switch (reg) {
+ case RETU_REG_IDR:
+ s->irqst ^= val;
+ retu_interrupt_update(s);
+ break;
+
+ case RETU_REG_IMR:
+ s->irqen = val;
+ retu_interrupt_update(s);
+ break;
+
+ case RETU_REG_RTCDSR:
+ case RETU_REG_RTCHMAR:
+ /* TODO */
+ break;
+
+ case RETU_REG_RTCCALR:
+ s->rtc.cal = val;
+ break;
+
+ case RETU_REG_ADCR:
+ s->channel = (val >> 10) & 0xf;
+ s->irqst |= 1 << retu_int_adcs;
+ retu_interrupt_update(s);
+ break;
+ case RETU_REG_ADCSCR:
+ s->sample &= ~val;
+ break;
+
+ case RETU_REG_AFCR:
+ case RETU_REG_ANTIFR:
+ case RETU_REG_CALIBR:
+
+ case RETU_REG_CCR1:
+ s->cc[0] = val;
+ break;
+ case RETU_REG_CCR2:
+ s->cc[1] = val;
+ break;
+
+ case RETU_REG_RCTRL_CLR:
+ case RETU_REG_RCTRL_SET:
+ /* TODO */
+ break;
+
+ case RETU_REG_WATCHDOG:
+ if (val == 0 && (s->cc[0] & 2))
+ qemu_system_shutdown_request();
+ break;
+
+ case RETU_REG_TXCR:
+ case RETU_REG_AUDTXR:
+ case RETU_REG_AUDPAR:
+ case RETU_REG_AUDRXR1:
+ case RETU_REG_AUDRXR2:
+ case RETU_REG_SGR1:
+ case RETU_REG_SCR1:
+ case RETU_REG_SGR2:
+ case RETU_REG_SCR2:
+ /* TODO */
+ break;
+
+ default:
+ hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
+ }
+}
+
+static void retu_io(void *opaque, int rw, int reg, uint16_t *val)
+{
+ CBusRetu *s = (CBusRetu *) opaque;
+
+ if (rw)
+ *val = retu_read(s, reg);
+ else
+ retu_write(s, reg, *val);
+}
+
+void *retu_init(qemu_irq irq, int vilma)
+{
+ CBusRetu *s = (CBusRetu *) qemu_mallocz(sizeof(*s));
+
+ s->irq = irq;
+ s->irqen = 0xffff;
+ s->irqst = 0x0000;
+ s->status = 0x0020;
+ s->is_vilma = !!vilma;
+ s->rtc.cal = 0x01;
+ s->result[retu_adc_bsi] = 0x3c2;
+ s->result[retu_adc_batt_temp] = 0x0fc;
+ s->result[retu_adc_chg_volt] = 0x165;
+ s->result[retu_adc_head_det] = 123;
+ s->result[retu_adc_hook_det] = 1023;
+ s->result[retu_adc_rf_gp] = 0x11;
+ s->result[retu_adc_tx_det] = 0x11;
+ s->result[retu_adc_batt_volt] = 0x250;
+ s->result[retu_adc_sens] = 2;
+ s->result[retu_adc_sens_temp] = 0x11;
+ s->result[retu_adc_bbatt_volt] = 0x3d0;
+ s->result[retu_adc_self_temp] = 0x330;
+
+ s->cbus.opaque = s;
+ s->cbus.io = retu_io;
+ s->cbus.addr = 1;
+
+ return &s->cbus;
+}
+
+void retu_key_event(void *retu, int state)
+{
+ CBusSlave *slave = (CBusSlave *) retu;
+ CBusRetu *s = (CBusRetu *) slave->opaque;
+
+ s->irqst |= 1 << retu_int_pwr;
+ retu_interrupt_update(s);
+
+ if (state)
+ s->status &= ~(1 << 5);
+ else
+ s->status |= 1 << 5;
+}
+
+#if 0
+static void retu_head_event(void *retu, int state)
+{
+ CBusSlave *slave = (CBusSlave *) retu;
+ CBusRetu *s = (CBusRetu *) slave->opaque;
+
+ if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */
+ /* TODO: reissue the interrupt every 100ms or so. */
+ s->irqst |= 1 << retu_int_head;
+ retu_interrupt_update(s);
+ }
+
+ if (state)
+ s->result[retu_adc_head_det] = 50;
+ else
+ s->result[retu_adc_head_det] = 123;
+}
+
+static void retu_hook_event(void *retu, int state)
+{
+ CBusSlave *slave = (CBusSlave *) retu;
+ CBusRetu *s = (CBusRetu *) slave->opaque;
+
+ if ((s->cc[0] & 0x500) == 0x500) {
+ /* TODO: reissue the interrupt every 100ms or so. */
+ s->irqst |= 1 << retu_int_hook;
+ retu_interrupt_update(s);
+ }
+
+ if (state)
+ s->result[retu_adc_hook_det] = 50;
+ else
+ s->result[retu_adc_hook_det] = 123;
+}
+#endif
+
+/* Tahvo/Betty */
+typedef struct {
+ uint16_t irqst;
+ uint16_t irqen;
+ uint8_t charger;
+ uint8_t backlight;
+ uint16_t usbr;
+ uint16_t power;
+
+ int is_betty;
+ qemu_irq irq;
+ CBusSlave cbus;
+} CBusTahvo;
+
+static void tahvo_interrupt_update(CBusTahvo *s)
+{
+ qemu_set_irq(s->irq, s->irqst & ~s->irqen);
+}
+
+#define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */
+#define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */
+#define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */
+#define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */
+#define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */
+#define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */
+#define TAHVO_REG_USBR 0x06 /* (RW) USB control */
+#define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */
+#define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */
+#define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */
+#define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */
+#define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */
+#define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */
+#define TAHVO_REG_FRR 0x0d /* (RO) FR */
+
+static inline uint16_t tahvo_read(CBusTahvo *s, int reg)
+{
+#ifdef DEBUG
+ printf("TAHVO read at %02x\n", reg);
+#endif
+
+ switch (reg) {
+ case TAHVO_REG_ASICR:
+ return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */
+
+ case TAHVO_REG_IDR:
+ case TAHVO_REG_IDSR: /* XXX: what does this do? */
+ return s->irqst;
+
+ case TAHVO_REG_IMR:
+ return s->irqen;
+
+ case TAHVO_REG_CHAPWMR:
+ return s->charger;
+
+ case TAHVO_REG_LEDPWMR:
+ return s->backlight;
+
+ case TAHVO_REG_USBR:
+ return s->usbr;
+
+ case TAHVO_REG_RCR:
+ return s->power;
+
+ case TAHVO_REG_CCR1:
+ case TAHVO_REG_CCR2:
+ case TAHVO_REG_TESTR1:
+ case TAHVO_REG_TESTR2:
+ case TAHVO_REG_NOPR:
+ case TAHVO_REG_FRR:
+ return 0x0000;
+
+ default:
+ hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
+ }
+}
+
+static inline void tahvo_write(CBusTahvo *s, int reg, uint16_t val)
+{
+#ifdef DEBUG
+ printf("TAHVO write of %04x at %02x\n", val, reg);
+#endif
+
+ switch (reg) {
+ case TAHVO_REG_IDR:
+ s->irqst ^= val;
+ tahvo_interrupt_update(s);
+ break;
+
+ case TAHVO_REG_IMR:
+ s->irqen = val;
+ tahvo_interrupt_update(s);
+ break;
+
+ case TAHVO_REG_CHAPWMR:
+ s->charger = val;
+ break;
+
+ case TAHVO_REG_LEDPWMR:
+ if (s->backlight != (val & 0x7f)) {
+ s->backlight = val & 0x7f;
+ printf("%s: LCD backlight now at %i / 127\n",
+ __FUNCTION__, s->backlight);
+ }
+ break;
+
+ case TAHVO_REG_USBR:
+ s->usbr = val;
+ break;
+
+ case TAHVO_REG_RCR:
+ s->power = val;
+ break;
+
+ case TAHVO_REG_CCR1:
+ case TAHVO_REG_CCR2:
+ case TAHVO_REG_TESTR1:
+ case TAHVO_REG_TESTR2:
+ case TAHVO_REG_NOPR:
+ case TAHVO_REG_FRR:
+ break;
+
+ default:
+ hw_error("%s: bad register %02x\n", __FUNCTION__, reg);
+ }
+}
+
+static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val)
+{
+ CBusTahvo *s = (CBusTahvo *) opaque;
+
+ if (rw)
+ *val = tahvo_read(s, reg);
+ else
+ tahvo_write(s, reg, *val);
+}
+
+void *tahvo_init(qemu_irq irq, int betty)
+{
+ CBusTahvo *s = (CBusTahvo *) qemu_mallocz(sizeof(*s));
+
+ s->irq = irq;
+ s->irqen = 0xffff;
+ s->irqst = 0x0000;
+ s->is_betty = !!betty;
+
+ s->cbus.opaque = s;
+ s->cbus.io = tahvo_io;
+ s->cbus.addr = 2;
+
+ return &s->cbus;
+}
diff --git a/hw/cdrom.c b/hw/cdrom.c
new file mode 100644
index 0000000..3b99535
--- /dev/null
+++ b/hw/cdrom.c
@@ -0,0 +1,155 @@
+/*
+ * QEMU ATAPI CD-ROM Emulator
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
+ here. */
+
+#include "qemu-common.h"
+#include "scsi.h"
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+}
+
+/* same toc as bochs. Return -1 if error or the toc length */
+/* XXX: check this */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
+{
+ uint8_t *q;
+ int len;
+
+ if (start_track > 1 && start_track != 0xaa)
+ return -1;
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+ if (start_track <= 1) {
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 1; /* track number */
+ *q++ = 0; /* reserved */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ /* sector 0 */
+ cpu_to_be32wu((uint32_t *)q, 0);
+ q += 4;
+ }
+ }
+ /* lead out track */
+ *q++ = 0; /* reserved */
+ *q++ = 0x16; /* ADR, control */
+ *q++ = 0xaa; /* track number */
+ *q++ = 0; /* reserved */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, nb_sectors);
+ q += 3;
+ } else {
+ cpu_to_be32wu((uint32_t *)q, nb_sectors);
+ q += 4;
+ }
+ len = q - buf;
+ cpu_to_be16wu((uint16_t *)buf, len - 2);
+ return len;
+}
+
+/* mostly same info as PearPc */
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
+{
+ uint8_t *q;
+ int len;
+
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa0; /* lead-in */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* first track */
+ *q++ = 0x00; /* disk type */
+ *q++ = 0x00;
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa1;
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* last track */
+ *q++ = 0x00;
+ *q++ = 0x00;
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa2; /* lead-out */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ lba_to_msf(q, nb_sectors);
+ q += 3;
+ } else {
+ cpu_to_be32wu((uint32_t *)q, nb_sectors);
+ q += 4;
+ }
+
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 0; /* track number */
+ *q++ = 1; /* point */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0;
+ lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
+
+ len = q - buf;
+ cpu_to_be16wu((uint16_t *)buf, len - 2);
+ return len;
+}
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
new file mode 100644
index 0000000..b58dd7e
--- /dev/null
+++ b/hw/cirrus_vga.c
@@ -0,0 +1,3171 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2004 Makoto Suzuki (suzu)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * Reference: Finn Thogersons' VGADOC4b
+ * available at http://home.worldonline.dk/~finth/
+ */
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "console.h"
+#include "vga_int.h"
+#include "kvm.h"
+#include "qemu-kvm.h"
+#include "loader.h"
+
+/*
+ * TODO:
+ * - destination write mask support not complete (bits 5..7)
+ * - optimize linear mappings
+ * - optimize bitblt functions
+ */
+
+//#define DEBUG_CIRRUS
+//#define DEBUG_BITBLT
+
+/***************************************
+ *
+ * definitions
+ *
+ ***************************************/
+
+// ID
+#define CIRRUS_ID_CLGD5422 (0x23<<2)
+#define CIRRUS_ID_CLGD5426 (0x24<<2)
+#define CIRRUS_ID_CLGD5424 (0x25<<2)
+#define CIRRUS_ID_CLGD5428 (0x26<<2)
+#define CIRRUS_ID_CLGD5430 (0x28<<2)
+#define CIRRUS_ID_CLGD5434 (0x2A<<2)
+#define CIRRUS_ID_CLGD5436 (0x2B<<2)
+#define CIRRUS_ID_CLGD5446 (0x2E<<2)
+
+// sequencer 0x07
+#define CIRRUS_SR7_BPP_VGA 0x00
+#define CIRRUS_SR7_BPP_SVGA 0x01
+#define CIRRUS_SR7_BPP_MASK 0x0e
+#define CIRRUS_SR7_BPP_8 0x00
+#define CIRRUS_SR7_BPP_16_DOUBLEVCLK 0x02
+#define CIRRUS_SR7_BPP_24 0x04
+#define CIRRUS_SR7_BPP_16 0x06
+#define CIRRUS_SR7_BPP_32 0x08
+#define CIRRUS_SR7_ISAADDR_MASK 0xe0
+
+// sequencer 0x0f
+#define CIRRUS_MEMSIZE_512k 0x08
+#define CIRRUS_MEMSIZE_1M 0x10
+#define CIRRUS_MEMSIZE_2M 0x18
+#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled.
+
+// sequencer 0x12
+#define CIRRUS_CURSOR_SHOW 0x01
+#define CIRRUS_CURSOR_HIDDENPEL 0x02
+#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear
+
+// sequencer 0x17
+#define CIRRUS_BUSTYPE_VLBFAST 0x10
+#define CIRRUS_BUSTYPE_PCI 0x20
+#define CIRRUS_BUSTYPE_VLBSLOW 0x30
+#define CIRRUS_BUSTYPE_ISA 0x38
+#define CIRRUS_MMIO_ENABLE 0x04
+#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared.
+#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80
+
+// control 0x0b
+#define CIRRUS_BANKING_DUAL 0x01
+#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k
+
+// control 0x30
+#define CIRRUS_BLTMODE_BACKWARDS 0x01
+#define CIRRUS_BLTMODE_MEMSYSDEST 0x02
+#define CIRRUS_BLTMODE_MEMSYSSRC 0x04
+#define CIRRUS_BLTMODE_TRANSPARENTCOMP 0x08
+#define CIRRUS_BLTMODE_PATTERNCOPY 0x40
+#define CIRRUS_BLTMODE_COLOREXPAND 0x80
+#define CIRRUS_BLTMODE_PIXELWIDTHMASK 0x30
+#define CIRRUS_BLTMODE_PIXELWIDTH8 0x00
+#define CIRRUS_BLTMODE_PIXELWIDTH16 0x10
+#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20
+#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30
+
+// control 0x31
+#define CIRRUS_BLT_BUSY 0x01
+#define CIRRUS_BLT_START 0x02
+#define CIRRUS_BLT_RESET 0x04
+#define CIRRUS_BLT_FIFOUSED 0x10
+#define CIRRUS_BLT_AUTOSTART 0x80
+
+// control 0x32
+#define CIRRUS_ROP_0 0x00
+#define CIRRUS_ROP_SRC_AND_DST 0x05
+#define CIRRUS_ROP_NOP 0x06
+#define CIRRUS_ROP_SRC_AND_NOTDST 0x09
+#define CIRRUS_ROP_NOTDST 0x0b
+#define CIRRUS_ROP_SRC 0x0d
+#define CIRRUS_ROP_1 0x0e
+#define CIRRUS_ROP_NOTSRC_AND_DST 0x50
+#define CIRRUS_ROP_SRC_XOR_DST 0x59
+#define CIRRUS_ROP_SRC_OR_DST 0x6d
+#define CIRRUS_ROP_NOTSRC_OR_NOTDST 0x90
+#define CIRRUS_ROP_SRC_NOTXOR_DST 0x95
+#define CIRRUS_ROP_SRC_OR_NOTDST 0xad
+#define CIRRUS_ROP_NOTSRC 0xd0
+#define CIRRUS_ROP_NOTSRC_OR_DST 0xd6
+#define CIRRUS_ROP_NOTSRC_AND_NOTDST 0xda
+
+#define CIRRUS_ROP_NOP_INDEX 2
+#define CIRRUS_ROP_SRC_INDEX 5
+
+// control 0x33
+#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04
+#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02
+#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01
+
+// memory-mapped IO
+#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword
+#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword
+#define CIRRUS_MMIO_BLTWIDTH 0x08 // word
+#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word
+#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word
+#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word
+#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword
+#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword
+#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte
+#define CIRRUS_MMIO_BLTMODE 0x18 // byte
+#define CIRRUS_MMIO_BLTROP 0x1a // byte
+#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word?
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word?
+#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word
+#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word
+#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word
+#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte
+#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word
+#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word
+#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word
+#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word
+#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte
+#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte
+#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte
+
+#define CIRRUS_PNPMMIO_SIZE 0x1000
+
+#define ABS(a) ((signed)(a) > 0 ? a : -a)
+
+#define BLTUNSAFE(s) \
+ ( \
+ ( /* check dst is within bounds */ \
+ (s)->cirrus_blt_height * ABS((s)->cirrus_blt_dstpitch) \
+ + ((s)->cirrus_blt_dstaddr & (s)->cirrus_addr_mask) > \
+ (s)->vga.vram_size \
+ ) || \
+ ( /* check src is within bounds */ \
+ (s)->cirrus_blt_height * ABS((s)->cirrus_blt_srcpitch) \
+ + ((s)->cirrus_blt_srcaddr & (s)->cirrus_addr_mask) > \
+ (s)->vga.vram_size \
+ ) \
+ )
+
+struct CirrusVGAState;
+typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
+ uint8_t * dst, const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight);
+typedef void (*cirrus_fill_t)(struct CirrusVGAState *s,
+ uint8_t *dst, int dst_pitch, int width, int height);
+
+typedef struct CirrusVGAState {
+ VGACommonState vga;
+
+ int cirrus_linear_io_addr;
+ int cirrus_linear_bitblt_io_addr;
+ int cirrus_mmio_io_addr;
+ uint32_t cirrus_addr_mask;
+ uint32_t linear_mmio_mask;
+ uint8_t cirrus_shadow_gr0;
+ uint8_t cirrus_shadow_gr1;
+ uint8_t cirrus_hidden_dac_lockindex;
+ uint8_t cirrus_hidden_dac_data;
+ uint32_t cirrus_bank_base[2];
+ uint32_t cirrus_bank_limit[2];
+ uint8_t cirrus_hidden_palette[48];
+ uint32_t hw_cursor_x;
+ uint32_t hw_cursor_y;
+ int cirrus_blt_pixelwidth;
+ int cirrus_blt_width;
+ int cirrus_blt_height;
+ int cirrus_blt_dstpitch;
+ int cirrus_blt_srcpitch;
+ uint32_t cirrus_blt_fgcol;
+ uint32_t cirrus_blt_bgcol;
+ uint32_t cirrus_blt_dstaddr;
+ uint32_t cirrus_blt_srcaddr;
+ uint8_t cirrus_blt_mode;
+ uint8_t cirrus_blt_modeext;
+ cirrus_bitblt_rop_t cirrus_rop;
+#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */
+ uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE];
+ uint8_t *cirrus_srcptr;
+ uint8_t *cirrus_srcptr_end;
+ uint32_t cirrus_srccounter;
+ /* hwcursor display state */
+ int last_hw_cursor_size;
+ int last_hw_cursor_x;
+ int last_hw_cursor_y;
+ int last_hw_cursor_y_start;
+ int last_hw_cursor_y_end;
+ int real_vram_size; /* XXX: suppress that */
+ int device_id;
+ int bustype;
+} CirrusVGAState;
+
+typedef struct PCICirrusVGAState {
+ PCIDevice dev;
+ CirrusVGAState cirrus_vga;
+} PCICirrusVGAState;
+
+static uint8_t rop_to_index[256];
+
+/***************************************
+ *
+ * prototypes.
+ *
+ ***************************************/
+
+
+static void cirrus_bitblt_reset(CirrusVGAState *s);
+static void cirrus_update_memory_access(CirrusVGAState *s);
+
+/***************************************
+ *
+ * raster operations
+ *
+ ***************************************/
+
+static void cirrus_bitblt_rop_nop(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+}
+
+static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
+ uint8_t *dst,
+ int dstpitch, int bltwidth,int bltheight)
+{
+}
+
+#define ROP_NAME 0
+#define ROP_FN(d, s) 0
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_dst
+#define ROP_FN(d, s) (s) & (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_notdst
+#define ROP_FN(d, s) (s) & (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notdst
+#define ROP_FN(d, s) ~(d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src
+#define ROP_FN(d, s) s
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME 1
+#define ROP_FN(d, s) ~0
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_dst
+#define ROP_FN(d, s) (~(s)) & (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_xor_dst
+#define ROP_FN(d, s) (s) ^ (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_dst
+#define ROP_FN(d, s) (s) | (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_notdst
+#define ROP_FN(d, s) (~(s)) | (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_notxor_dst
+#define ROP_FN(d, s) ~((s) ^ (d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_notdst
+#define ROP_FN(d, s) (s) | (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc
+#define ROP_FN(d, s) (~(s))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_dst
+#define ROP_FN(d, s) (~(s)) | (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_notdst
+#define ROP_FN(d, s) (~(s)) & (~(d))
+#include "cirrus_vga_rop.h"
+
+static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = {
+ cirrus_bitblt_rop_fwd_0,
+ cirrus_bitblt_rop_fwd_src_and_dst,
+ cirrus_bitblt_rop_nop,
+ cirrus_bitblt_rop_fwd_src_and_notdst,
+ cirrus_bitblt_rop_fwd_notdst,
+ cirrus_bitblt_rop_fwd_src,
+ cirrus_bitblt_rop_fwd_1,
+ cirrus_bitblt_rop_fwd_notsrc_and_dst,
+ cirrus_bitblt_rop_fwd_src_xor_dst,
+ cirrus_bitblt_rop_fwd_src_or_dst,
+ cirrus_bitblt_rop_fwd_notsrc_or_notdst,
+ cirrus_bitblt_rop_fwd_src_notxor_dst,
+ cirrus_bitblt_rop_fwd_src_or_notdst,
+ cirrus_bitblt_rop_fwd_notsrc,
+ cirrus_bitblt_rop_fwd_notsrc_or_dst,
+ cirrus_bitblt_rop_fwd_notsrc_and_notdst,
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
+ cirrus_bitblt_rop_bkwd_0,
+ cirrus_bitblt_rop_bkwd_src_and_dst,
+ cirrus_bitblt_rop_nop,
+ cirrus_bitblt_rop_bkwd_src_and_notdst,
+ cirrus_bitblt_rop_bkwd_notdst,
+ cirrus_bitblt_rop_bkwd_src,
+ cirrus_bitblt_rop_bkwd_1,
+ cirrus_bitblt_rop_bkwd_notsrc_and_dst,
+ cirrus_bitblt_rop_bkwd_src_xor_dst,
+ cirrus_bitblt_rop_bkwd_src_or_dst,
+ cirrus_bitblt_rop_bkwd_notsrc_or_notdst,
+ cirrus_bitblt_rop_bkwd_src_notxor_dst,
+ cirrus_bitblt_rop_bkwd_src_or_notdst,
+ cirrus_bitblt_rop_bkwd_notsrc,
+ cirrus_bitblt_rop_bkwd_notsrc_or_dst,
+ cirrus_bitblt_rop_bkwd_notsrc_and_notdst,
+};
+
+#define TRANSP_ROP(name) {\
+ name ## _8,\
+ name ## _16,\
+ }
+#define TRANSP_NOP(func) {\
+ func,\
+ func,\
+ }
+
+static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = {
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst),
+ TRANSP_NOP(cirrus_bitblt_rop_nop),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = {
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst),
+ TRANSP_NOP(cirrus_bitblt_rop_nop),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst),
+};
+
+#define ROP2(name) {\
+ name ## _8,\
+ name ## _16,\
+ name ## _24,\
+ name ## _32,\
+ }
+
+#define ROP_NOP2(func) {\
+ func,\
+ func,\
+ func,\
+ func,\
+ }
+
+static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = {
+ ROP2(cirrus_patternfill_0),
+ ROP2(cirrus_patternfill_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_patternfill_src_and_notdst),
+ ROP2(cirrus_patternfill_notdst),
+ ROP2(cirrus_patternfill_src),
+ ROP2(cirrus_patternfill_1),
+ ROP2(cirrus_patternfill_notsrc_and_dst),
+ ROP2(cirrus_patternfill_src_xor_dst),
+ ROP2(cirrus_patternfill_src_or_dst),
+ ROP2(cirrus_patternfill_notsrc_or_notdst),
+ ROP2(cirrus_patternfill_src_notxor_dst),
+ ROP2(cirrus_patternfill_src_or_notdst),
+ ROP2(cirrus_patternfill_notsrc),
+ ROP2(cirrus_patternfill_notsrc_or_dst),
+ ROP2(cirrus_patternfill_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
+ ROP2(cirrus_colorexpand_transp_0),
+ ROP2(cirrus_colorexpand_transp_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_transp_src_and_notdst),
+ ROP2(cirrus_colorexpand_transp_notdst),
+ ROP2(cirrus_colorexpand_transp_src),
+ ROP2(cirrus_colorexpand_transp_1),
+ ROP2(cirrus_colorexpand_transp_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_transp_src_xor_dst),
+ ROP2(cirrus_colorexpand_transp_src_or_dst),
+ ROP2(cirrus_colorexpand_transp_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_transp_src_notxor_dst),
+ ROP2(cirrus_colorexpand_transp_src_or_notdst),
+ ROP2(cirrus_colorexpand_transp_notsrc),
+ ROP2(cirrus_colorexpand_transp_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
+ ROP2(cirrus_colorexpand_0),
+ ROP2(cirrus_colorexpand_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_src_and_notdst),
+ ROP2(cirrus_colorexpand_notdst),
+ ROP2(cirrus_colorexpand_src),
+ ROP2(cirrus_colorexpand_1),
+ ROP2(cirrus_colorexpand_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_src_xor_dst),
+ ROP2(cirrus_colorexpand_src_or_dst),
+ ROP2(cirrus_colorexpand_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_src_notxor_dst),
+ ROP2(cirrus_colorexpand_src_or_notdst),
+ ROP2(cirrus_colorexpand_notsrc),
+ ROP2(cirrus_colorexpand_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = {
+ ROP2(cirrus_colorexpand_pattern_transp_0),
+ ROP2(cirrus_colorexpand_pattern_transp_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_src),
+ ROP2(cirrus_colorexpand_pattern_transp_1),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_or_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = {
+ ROP2(cirrus_colorexpand_pattern_0),
+ ROP2(cirrus_colorexpand_pattern_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_pattern_src_and_notdst),
+ ROP2(cirrus_colorexpand_pattern_notdst),
+ ROP2(cirrus_colorexpand_pattern_src),
+ ROP2(cirrus_colorexpand_pattern_1),
+ ROP2(cirrus_colorexpand_pattern_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_pattern_src_xor_dst),
+ ROP2(cirrus_colorexpand_pattern_src_or_dst),
+ ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_src_notxor_dst),
+ ROP2(cirrus_colorexpand_pattern_src_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_notsrc),
+ ROP2(cirrus_colorexpand_pattern_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst),
+};
+
+static const cirrus_fill_t cirrus_fill[16][4] = {
+ ROP2(cirrus_fill_0),
+ ROP2(cirrus_fill_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_fill_nop),
+ ROP2(cirrus_fill_src_and_notdst),
+ ROP2(cirrus_fill_notdst),
+ ROP2(cirrus_fill_src),
+ ROP2(cirrus_fill_1),
+ ROP2(cirrus_fill_notsrc_and_dst),
+ ROP2(cirrus_fill_src_xor_dst),
+ ROP2(cirrus_fill_src_or_dst),
+ ROP2(cirrus_fill_notsrc_or_notdst),
+ ROP2(cirrus_fill_src_notxor_dst),
+ ROP2(cirrus_fill_src_or_notdst),
+ ROP2(cirrus_fill_notsrc),
+ ROP2(cirrus_fill_notsrc_or_dst),
+ ROP2(cirrus_fill_notsrc_and_notdst),
+};
+
+static inline void cirrus_bitblt_fgcol(CirrusVGAState *s)
+{
+ unsigned int color;
+ switch (s->cirrus_blt_pixelwidth) {
+ case 1:
+ s->cirrus_blt_fgcol = s->cirrus_shadow_gr1;
+ break;
+ case 2:
+ color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8);
+ s->cirrus_blt_fgcol = le16_to_cpu(color);
+ break;
+ case 3:
+ s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 |
+ (s->vga.gr[0x11] << 8) | (s->vga.gr[0x13] << 16);
+ break;
+ default:
+ case 4:
+ color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8) |
+ (s->vga.gr[0x13] << 16) | (s->vga.gr[0x15] << 24);
+ s->cirrus_blt_fgcol = le32_to_cpu(color);
+ break;
+ }
+}
+
+static inline void cirrus_bitblt_bgcol(CirrusVGAState *s)
+{
+ unsigned int color;
+ switch (s->cirrus_blt_pixelwidth) {
+ case 1:
+ s->cirrus_blt_bgcol = s->cirrus_shadow_gr0;
+ break;
+ case 2:
+ color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8);
+ s->cirrus_blt_bgcol = le16_to_cpu(color);
+ break;
+ case 3:
+ s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 |
+ (s->vga.gr[0x10] << 8) | (s->vga.gr[0x12] << 16);
+ break;
+ default:
+ case 4:
+ color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8) |
+ (s->vga.gr[0x12] << 16) | (s->vga.gr[0x14] << 24);
+ s->cirrus_blt_bgcol = le32_to_cpu(color);
+ break;
+ }
+}
+
+static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
+ int off_pitch, int bytesperline,
+ int lines)
+{
+ int y;
+ int off_cur;
+ int off_cur_end;
+
+ for (y = 0; y < lines; y++) {
+ off_cur = off_begin;
+ off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask;
+ off_cur &= TARGET_PAGE_MASK;
+ while (off_cur < off_cur_end) {
+ cpu_physical_memory_set_dirty(s->vga.vram_offset + off_cur);
+ off_cur += TARGET_PAGE_SIZE;
+ }
+ off_begin += off_pitch;
+ }
+}
+
+static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
+ const uint8_t * src)
+{
+ uint8_t *dst;
+
+ dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
+
+ if (BLTUNSAFE(s))
+ return 0;
+
+ (*s->cirrus_rop) (s, dst, src,
+ s->cirrus_blt_dstpitch, 0,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ return 1;
+}
+
+/* fill */
+
+static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
+{
+ cirrus_fill_t rop_func;
+
+ if (BLTUNSAFE(s))
+ return 0;
+ rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+ s->cirrus_blt_dstpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ cirrus_bitblt_reset(s);
+ return 1;
+}
+
+/***************************************
+ *
+ * bitblt (video-to-video)
+ *
+ ***************************************/
+
+static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
+{
+ return cirrus_bitblt_common_patterncopy(s,
+ s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) &
+ s->cirrus_addr_mask));
+}
+
+static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
+{
+ int sx = 0, sy = 0;
+ int dx = 0, dy = 0;
+ int depth = 0;
+ int notify = 0;
+
+ /* make sure to only copy if it's a plain copy ROP */
+ if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src ||
+ *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) {
+
+ int width, height;
+
+ depth = s->vga.get_bpp(&s->vga) / 8;
+ s->vga.get_resolution(&s->vga, &width, &height);
+
+ /* extra x, y */
+ sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth;
+ sy = (src / ABS(s->cirrus_blt_srcpitch));
+ dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth;
+ dy = (dst / ABS(s->cirrus_blt_dstpitch));
+
+ /* normalize width */
+ w /= depth;
+
+ /* if we're doing a backward copy, we have to adjust
+ our x/y to be the upper left corner (instead of the lower
+ right corner) */
+ if (s->cirrus_blt_dstpitch < 0) {
+ sx -= (s->cirrus_blt_width / depth) - 1;
+ dx -= (s->cirrus_blt_width / depth) - 1;
+ sy -= s->cirrus_blt_height - 1;
+ dy -= s->cirrus_blt_height - 1;
+ }
+
+ /* are we in the visible portion of memory? */
+ if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
+ (sx + w) <= width && (sy + h) <= height &&
+ (dx + w) <= width && (dy + h) <= height) {
+ notify = 1;
+ }
+ }
+
+ /* we have to flush all pending changes so that the copy
+ is generated at the appropriate moment in time */
+ if (notify)
+ vga_hw_update();
+
+ (*s->cirrus_rop) (s, s->vga.vram_ptr +
+ (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+ s->vga.vram_ptr +
+ (s->cirrus_blt_srcaddr & s->cirrus_addr_mask),
+ s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+
+ if (notify)
+ qemu_console_copy(s->vga.ds,
+ sx, sy, dx, dy,
+ s->cirrus_blt_width / depth,
+ s->cirrus_blt_height);
+
+ /* we don't have to notify the display that this portion has
+ changed since qemu_console_copy implies this */
+
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+}
+
+static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+{
+ if (BLTUNSAFE(s))
+ return 0;
+
+ cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
+ s->cirrus_blt_srcaddr - s->vga.start_addr,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+
+ return 1;
+}
+
+/***************************************
+ *
+ * bitblt (cpu-to-video)
+ *
+ ***************************************/
+
+static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
+{
+ int copy_count;
+ uint8_t *end_ptr;
+
+ if (s->cirrus_srccounter > 0) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf);
+ the_end:
+ s->cirrus_srccounter = 0;
+ cirrus_bitblt_reset(s);
+ } else {
+ /* at least one scan line */
+ do {
+ (*s->cirrus_rop)(s, s->vga.vram_ptr +
+ (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+ s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0,
+ s->cirrus_blt_width, 1);
+ s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch;
+ s->cirrus_srccounter -= s->cirrus_blt_srcpitch;
+ if (s->cirrus_srccounter <= 0)
+ goto the_end;
+ /* more bytes than needed can be transfered because of
+ word alignment, so we keep them for the next line */
+ /* XXX: keep alignment to speed up transfer */
+ end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ copy_count = s->cirrus_srcptr_end - end_ptr;
+ memmove(s->cirrus_bltbuf, end_ptr, copy_count);
+ s->cirrus_srcptr = s->cirrus_bltbuf + copy_count;
+ s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ } while (s->cirrus_srcptr >= s->cirrus_srcptr_end);
+ }
+ }
+}
+
+/***************************************
+ *
+ * bitblt wrapper
+ *
+ ***************************************/
+
+static void cirrus_bitblt_reset(CirrusVGAState * s)
+{
+ int need_update;
+
+ s->vga.gr[0x31] &=
+ ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED);
+ need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0]
+ || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+ s->cirrus_srccounter = 0;
+ if (!need_update)
+ return;
+ cirrus_update_memory_access(s);
+}
+
+static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
+{
+ int w;
+
+ s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC;
+ s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ s->cirrus_blt_srcpitch = 8;
+ } else {
+ /* XXX: check for 24 bpp */
+ s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth;
+ }
+ s->cirrus_srccounter = s->cirrus_blt_srcpitch;
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth;
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY)
+ s->cirrus_blt_srcpitch = ((w + 31) >> 5);
+ else
+ s->cirrus_blt_srcpitch = ((w + 7) >> 3);
+ } else {
+ /* always align input size to 32 bits */
+ s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3;
+ }
+ s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
+ }
+ s->cirrus_srcptr = s->cirrus_bltbuf;
+ s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ cirrus_update_memory_access(s);
+ return 1;
+}
+
+static int cirrus_bitblt_videotocpu(CirrusVGAState * s)
+{
+ /* XXX */
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt (video to cpu) is not implemented yet\n");
+#endif
+ return 0;
+}
+
+static int cirrus_bitblt_videotovideo(CirrusVGAState * s)
+{
+ int ret;
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ ret = cirrus_bitblt_videotovideo_patterncopy(s);
+ } else {
+ ret = cirrus_bitblt_videotovideo_copy(s);
+ }
+ if (ret)
+ cirrus_bitblt_reset(s);
+ return ret;
+}
+
+static void cirrus_bitblt_start(CirrusVGAState * s)
+{
+ uint8_t blt_rop;
+
+ s->vga.gr[0x31] |= CIRRUS_BLT_BUSY;
+
+ s->cirrus_blt_width = (s->vga.gr[0x20] | (s->vga.gr[0x21] << 8)) + 1;
+ s->cirrus_blt_height = (s->vga.gr[0x22] | (s->vga.gr[0x23] << 8)) + 1;
+ s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8));
+ s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8));
+ s->cirrus_blt_dstaddr =
+ (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16));
+ s->cirrus_blt_srcaddr =
+ (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16));
+ s->cirrus_blt_mode = s->vga.gr[0x30];
+ s->cirrus_blt_modeext = s->vga.gr[0x33];
+ blt_rop = s->vga.gr[0x32];
+
+#ifdef DEBUG_BITBLT
+ printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
+ blt_rop,
+ s->cirrus_blt_mode,
+ s->cirrus_blt_modeext,
+ s->cirrus_blt_width,
+ s->cirrus_blt_height,
+ s->cirrus_blt_dstpitch,
+ s->cirrus_blt_srcpitch,
+ s->cirrus_blt_dstaddr,
+ s->cirrus_blt_srcaddr,
+ s->vga.gr[0x2f]);
+#endif
+
+ switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) {
+ case CIRRUS_BLTMODE_PIXELWIDTH8:
+ s->cirrus_blt_pixelwidth = 1;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH16:
+ s->cirrus_blt_pixelwidth = 2;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH24:
+ s->cirrus_blt_pixelwidth = 3;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH32:
+ s->cirrus_blt_pixelwidth = 4;
+ break;
+ default:
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt - pixel width is unknown\n");
+#endif
+ goto bitblt_ignore;
+ }
+ s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK;
+
+ if ((s->
+ cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC |
+ CIRRUS_BLTMODE_MEMSYSDEST))
+ == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) {
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt - memory-to-memory copy is requested\n");
+#endif
+ goto bitblt_ignore;
+ }
+
+ if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) &&
+ (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST |
+ CIRRUS_BLTMODE_TRANSPARENTCOMP |
+ CIRRUS_BLTMODE_PATTERNCOPY |
+ CIRRUS_BLTMODE_COLOREXPAND)) ==
+ (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_solidfill(s, blt_rop);
+ } else {
+ if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND |
+ CIRRUS_BLTMODE_PATTERNCOPY)) ==
+ CIRRUS_BLTMODE_COLOREXPAND) {
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+ cirrus_bitblt_bgcol(s);
+ else
+ cirrus_bitblt_fgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_bgcol(s);
+ s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+ cirrus_bitblt_bgcol(s);
+ else
+ cirrus_bitblt_fgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_bgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_pixelwidth > 2) {
+ printf("src transparent without colorexpand must be 8bpp or 16bpp\n");
+ goto bitblt_ignore;
+ }
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+ s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+ s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+ s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+ s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+ s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+ s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
+ } else {
+ s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
+ }
+ }
+ }
+ // setup bitblt engine.
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) {
+ if (!cirrus_bitblt_cputovideo(s))
+ goto bitblt_ignore;
+ } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) {
+ if (!cirrus_bitblt_videotocpu(s))
+ goto bitblt_ignore;
+ } else {
+ if (!cirrus_bitblt_videotovideo(s))
+ goto bitblt_ignore;
+ }
+ }
+ return;
+ bitblt_ignore:;
+ cirrus_bitblt_reset(s);
+}
+
+static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
+{
+ unsigned old_value;
+
+ old_value = s->vga.gr[0x31];
+ s->vga.gr[0x31] = reg_value;
+
+ if (((old_value & CIRRUS_BLT_RESET) != 0) &&
+ ((reg_value & CIRRUS_BLT_RESET) == 0)) {
+ cirrus_bitblt_reset(s);
+ } else if (((old_value & CIRRUS_BLT_START) == 0) &&
+ ((reg_value & CIRRUS_BLT_START) != 0)) {
+ cirrus_bitblt_start(s);
+ }
+}
+
+
+/***************************************
+ *
+ * basic parameters
+ *
+ ***************************************/
+
+static void cirrus_get_offsets(VGACommonState *s1,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr,
+ uint32_t *pline_compare)
+{
+ CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
+ uint32_t start_addr, line_offset, line_compare;
+
+ line_offset = s->vga.cr[0x13]
+ | ((s->vga.cr[0x1b] & 0x10) << 4);
+ line_offset <<= 3;
+ *pline_offset = line_offset;
+
+ start_addr = (s->vga.cr[0x0c] << 8)
+ | s->vga.cr[0x0d]
+ | ((s->vga.cr[0x1b] & 0x01) << 16)
+ | ((s->vga.cr[0x1b] & 0x0c) << 15)
+ | ((s->vga.cr[0x1d] & 0x80) << 12);
+ *pstart_addr = start_addr;
+
+ line_compare = s->vga.cr[0x18] |
+ ((s->vga.cr[0x07] & 0x10) << 4) |
+ ((s->vga.cr[0x09] & 0x40) << 3);
+ *pline_compare = line_compare;
+}
+
+static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
+{
+ uint32_t ret = 16;
+
+ switch (s->cirrus_hidden_dac_data & 0xf) {
+ case 0:
+ ret = 15;
+ break; /* Sierra HiColor */
+ case 1:
+ ret = 16;
+ break; /* XGA HiColor */
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: invalid DAC value %x in 16bpp\n",
+ (s->cirrus_hidden_dac_data & 0xf));
+#endif
+ ret = 15; /* XXX */
+ break;
+ }
+ return ret;
+}
+
+static int cirrus_get_bpp(VGACommonState *s1)
+{
+ CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
+ uint32_t ret = 8;
+
+ if ((s->vga.sr[0x07] & 0x01) != 0) {
+ /* Cirrus SVGA */
+ switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) {
+ case CIRRUS_SR7_BPP_8:
+ ret = 8;
+ break;
+ case CIRRUS_SR7_BPP_16_DOUBLEVCLK:
+ ret = cirrus_get_bpp16_depth(s);
+ break;
+ case CIRRUS_SR7_BPP_24:
+ ret = 24;
+ break;
+ case CIRRUS_SR7_BPP_16:
+ ret = cirrus_get_bpp16_depth(s);
+ break;
+ case CIRRUS_SR7_BPP_32:
+ ret = 32;
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]);
+#endif
+ ret = 8;
+ break;
+ }
+ } else {
+ /* VGA */
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void cirrus_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
+{
+ int width, height;
+
+ width = (s->cr[0x01] + 1) * 8;
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1);
+ /* interlace support */
+ if (s->cr[0x1a] & 0x01)
+ height = height * 2;
+ *pwidth = width;
+ *pheight = height;
+}
+
+/***************************************
+ *
+ * bank memory
+ *
+ ***************************************/
+
+static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
+{
+ unsigned offset;
+ unsigned limit;
+
+ if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */
+ offset = s->vga.gr[0x09 + bank_index];
+ else /* single bank */
+ offset = s->vga.gr[0x09];
+
+ if ((s->vga.gr[0x0b] & 0x20) != 0)
+ offset <<= 14;
+ else
+ offset <<= 12;
+
+ if (s->real_vram_size <= offset)
+ limit = 0;
+ else
+ limit = s->real_vram_size - offset;
+
+ if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
+ if (limit > 0x8000) {
+ offset += 0x8000;
+ limit -= 0x8000;
+ } else {
+ limit = 0;
+ }
+ }
+
+ if (limit > 0) {
+ /* Thinking about changing bank base? First, drop the dirty bitmap information
+ * on the current location, otherwise we lose this pointer forever */
+ if (s->vga.lfb_vram_mapped) {
+ target_phys_addr_t base_addr = isa_mem_base + 0xa0000 + bank_index * 0x8000;
+ cpu_physical_sync_dirty_bitmap(base_addr, base_addr + 0x8000);
+ }
+ s->cirrus_bank_base[bank_index] = offset;
+ s->cirrus_bank_limit[bank_index] = limit;
+ } else {
+ s->cirrus_bank_base[bank_index] = 0;
+ s->cirrus_bank_limit[bank_index] = 0;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3c4-0x3c5
+ *
+ ***************************************/
+
+static int cirrus_vga_read_sr(CirrusVGAState * s)
+{
+ switch (s->vga.sr_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ return s->vga.sr[s->vga.sr_index];
+ case 0x06: // Unlock Cirrus extensions
+ return s->vga.sr[s->vga.sr_index];
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70: // Graphics Cursor X
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // Graphics Cursor X
+ return s->vga.sr[0x10];
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71: // Graphics Cursor Y
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // Graphics Cursor Y
+ return s->vga.sr[0x11];
+ case 0x05: // ???
+ case 0x07: // Extended Sequencer Mode
+ case 0x08: // EEPROM Control
+ case 0x09: // Scratch Register 0
+ case 0x0a: // Scratch Register 1
+ case 0x0b: // VCLK 0
+ case 0x0c: // VCLK 1
+ case 0x0d: // VCLK 2
+ case 0x0e: // VCLK 3
+ case 0x0f: // DRAM Control
+ case 0x12: // Graphics Cursor Attribute
+ case 0x13: // Graphics Cursor Pattern Address
+ case 0x14: // Scratch Register 2
+ case 0x15: // Scratch Register 3
+ case 0x16: // Performance Tuning Register
+ case 0x17: // Configuration Readback and Extended Control
+ case 0x18: // Signature Generator Control
+ case 0x19: // Signal Generator Result
+ case 0x1a: // Signal Generator Result
+ case 0x1b: // VCLK 0 Denominator & Post
+ case 0x1c: // VCLK 1 Denominator & Post
+ case 0x1d: // VCLK 2 Denominator & Post
+ case 0x1e: // VCLK 3 Denominator & Post
+ case 0x1f: // BIOS Write Enable and MCLK select
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index);
+#endif
+ return s->vga.sr[s->vga.sr_index];
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport sr_index %02x\n", s->vga.sr_index);
+#endif
+ return 0xff;
+ break;
+ }
+}
+
+static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val)
+{
+ switch (s->vga.sr_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index];
+ if (s->vga.sr_index == 1)
+ s->vga.update_retrace_info(&s->vga);
+ break;
+ case 0x06: // Unlock Cirrus extensions
+ val &= 0x17;
+ if (val == 0x12) {
+ s->vga.sr[s->vga.sr_index] = 0x12;
+ } else {
+ s->vga.sr[s->vga.sr_index] = 0x0f;
+ }
+ break;
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70: // Graphics Cursor X
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // Graphics Cursor X
+ s->vga.sr[0x10] = val;
+ s->hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5);
+ break;
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71: // Graphics Cursor Y
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // Graphics Cursor Y
+ s->vga.sr[0x11] = val;
+ s->hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5);
+ break;
+ case 0x07: // Extended Sequencer Mode
+ cirrus_update_memory_access(s);
+ case 0x08: // EEPROM Control
+ case 0x09: // Scratch Register 0
+ case 0x0a: // Scratch Register 1
+ case 0x0b: // VCLK 0
+ case 0x0c: // VCLK 1
+ case 0x0d: // VCLK 2
+ case 0x0e: // VCLK 3
+ case 0x0f: // DRAM Control
+ case 0x12: // Graphics Cursor Attribute
+ case 0x13: // Graphics Cursor Pattern Address
+ case 0x14: // Scratch Register 2
+ case 0x15: // Scratch Register 3
+ case 0x16: // Performance Tuning Register
+ case 0x18: // Signature Generator Control
+ case 0x19: // Signature Generator Result
+ case 0x1a: // Signature Generator Result
+ case 0x1b: // VCLK 0 Denominator & Post
+ case 0x1c: // VCLK 1 Denominator & Post
+ case 0x1d: // VCLK 2 Denominator & Post
+ case 0x1e: // VCLK 3 Denominator & Post
+ case 0x1f: // BIOS Write Enable and MCLK select
+ s->vga.sr[s->vga.sr_index] = val;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled outport sr_index %02x, sr_value %02x\n",
+ s->vga.sr_index, val);
+#endif
+ break;
+ case 0x17: // Configuration Readback and Extended Control
+ s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38)
+ | (val & 0xc7);
+ cirrus_update_memory_access(s);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport sr_index %02x, sr_value %02x\n",
+ s->vga.sr_index, val);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * I/O access at 0x3c6
+ *
+ ***************************************/
+
+static int cirrus_read_hidden_dac(CirrusVGAState * s)
+{
+ if (++s->cirrus_hidden_dac_lockindex == 5) {
+ s->cirrus_hidden_dac_lockindex = 0;
+ return s->cirrus_hidden_dac_data;
+ }
+ return 0xff;
+}
+
+static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value)
+{
+ if (s->cirrus_hidden_dac_lockindex == 4) {
+ s->cirrus_hidden_dac_data = reg_value;
+#if defined(DEBUG_CIRRUS)
+ printf("cirrus: outport hidden DAC, value %02x\n", reg_value);
+#endif
+ }
+ s->cirrus_hidden_dac_lockindex = 0;
+}
+
+/***************************************
+ *
+ * I/O access at 0x3c9
+ *
+ ***************************************/
+
+static int cirrus_vga_read_palette(CirrusVGAState * s)
+{
+ int val;
+
+ if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
+ val = s->cirrus_hidden_palette[(s->vga.dac_read_index & 0x0f) * 3 +
+ s->vga.dac_sub_index];
+ } else {
+ val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index];
+ }
+ if (++s->vga.dac_sub_index == 3) {
+ s->vga.dac_sub_index = 0;
+ s->vga.dac_read_index++;
+ }
+ return val;
+}
+
+static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value)
+{
+ s->vga.dac_cache[s->vga.dac_sub_index] = reg_value;
+ if (++s->vga.dac_sub_index == 3) {
+ if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
+ memcpy(&s->cirrus_hidden_palette[(s->vga.dac_write_index & 0x0f) * 3],
+ s->vga.dac_cache, 3);
+ } else {
+ memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3);
+ }
+ /* XXX update cursor */
+ s->vga.dac_sub_index = 0;
+ s->vga.dac_write_index++;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3ce-0x3cf
+ *
+ ***************************************/
+
+static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+ return s->cirrus_shadow_gr0;
+ case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+ return s->cirrus_shadow_gr1;
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ return s->vga.gr[s->vga.gr_index];
+ case 0x05: // Standard VGA, Cirrus extended mode
+ default:
+ break;
+ }
+
+ if (reg_index < 0x3a) {
+ return s->vga.gr[reg_index];
+ } else {
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport gr_index %02x\n", reg_index);
+#endif
+ return 0xff;
+ }
+}
+
+static void
+cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
+{
+#if defined(DEBUG_BITBLT) && 0
+ printf("gr%02x: %02x\n", reg_index, reg_value);
+#endif
+ switch (reg_index) {
+ case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+ s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+ s->cirrus_shadow_gr0 = reg_value;
+ break;
+ case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+ s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+ s->cirrus_shadow_gr1 = reg_value;
+ break;
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+ break;
+ case 0x05: // Standard VGA, Cirrus extended mode
+ s->vga.gr[reg_index] = reg_value & 0x7f;
+ cirrus_update_memory_access(s);
+ break;
+ case 0x09: // bank offset #0
+ case 0x0A: // bank offset #1
+ s->vga.gr[reg_index] = reg_value;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ cirrus_update_memory_access(s);
+ break;
+ case 0x0B:
+ s->vga.gr[reg_index] = reg_value;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ cirrus_update_memory_access(s);
+ break;
+ case 0x10: // BGCOLOR 0x0000ff00
+ case 0x11: // FGCOLOR 0x0000ff00
+ case 0x12: // BGCOLOR 0x00ff0000
+ case 0x13: // FGCOLOR 0x00ff0000
+ case 0x14: // BGCOLOR 0xff000000
+ case 0x15: // FGCOLOR 0xff000000
+ case 0x20: // BLT WIDTH 0x0000ff
+ case 0x22: // BLT HEIGHT 0x0000ff
+ case 0x24: // BLT DEST PITCH 0x0000ff
+ case 0x26: // BLT SRC PITCH 0x0000ff
+ case 0x28: // BLT DEST ADDR 0x0000ff
+ case 0x29: // BLT DEST ADDR 0x00ff00
+ case 0x2c: // BLT SRC ADDR 0x0000ff
+ case 0x2d: // BLT SRC ADDR 0x00ff00
+ case 0x2f: // BLT WRITEMASK
+ case 0x30: // BLT MODE
+ case 0x32: // RASTER OP
+ case 0x33: // BLT MODEEXT
+ case 0x34: // BLT TRANSPARENT COLOR 0x00ff
+ case 0x35: // BLT TRANSPARENT COLOR 0xff00
+ case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff
+ case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00
+ s->vga.gr[reg_index] = reg_value;
+ break;
+ case 0x21: // BLT WIDTH 0x001f00
+ case 0x23: // BLT HEIGHT 0x001f00
+ case 0x25: // BLT DEST PITCH 0x001f00
+ case 0x27: // BLT SRC PITCH 0x001f00
+ s->vga.gr[reg_index] = reg_value & 0x1f;
+ break;
+ case 0x2a: // BLT DEST ADDR 0x3f0000
+ s->vga.gr[reg_index] = reg_value & 0x3f;
+ /* if auto start mode, starts bit blt now */
+ if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) {
+ cirrus_bitblt_start(s);
+ }
+ break;
+ case 0x2e: // BLT SRC ADDR 0x3f0000
+ s->vga.gr[reg_index] = reg_value & 0x3f;
+ break;
+ case 0x31: // BLT STATUS/START
+ cirrus_write_bitblt(s, reg_value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index,
+ reg_value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3d4-0x3d5
+ *
+ ***************************************/
+
+static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x05: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ case 0x09: // Standard VGA
+ case 0x0a: // Standard VGA
+ case 0x0b: // Standard VGA
+ case 0x0c: // Standard VGA
+ case 0x0d: // Standard VGA
+ case 0x0e: // Standard VGA
+ case 0x0f: // Standard VGA
+ case 0x10: // Standard VGA
+ case 0x11: // Standard VGA
+ case 0x12: // Standard VGA
+ case 0x13: // Standard VGA
+ case 0x14: // Standard VGA
+ case 0x15: // Standard VGA
+ case 0x16: // Standard VGA
+ case 0x17: // Standard VGA
+ case 0x18: // Standard VGA
+ return s->vga.cr[s->vga.cr_index];
+ case 0x24: // Attribute Controller Toggle Readback (R)
+ return (s->vga.ar_flip_flop << 7);
+ case 0x19: // Interlace End
+ case 0x1a: // Miscellaneous Control
+ case 0x1b: // Extended Display Control
+ case 0x1c: // Sync Adjust and Genlock
+ case 0x1d: // Overlay Extended Control
+ case 0x22: // Graphics Data Latches Readback (R)
+ case 0x25: // Part Status
+ case 0x27: // Part ID (R)
+ return s->vga.cr[s->vga.cr_index];
+ case 0x26: // Attribute Controller Index Readback (R)
+ return s->vga.ar_index & 0x3f;
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport cr_index %02x\n", reg_index);
+#endif
+ return 0xff;
+ }
+}
+
+static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value)
+{
+ switch (s->vga.cr_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x05: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ case 0x09: // Standard VGA
+ case 0x0a: // Standard VGA
+ case 0x0b: // Standard VGA
+ case 0x0c: // Standard VGA
+ case 0x0d: // Standard VGA
+ case 0x0e: // Standard VGA
+ case 0x0f: // Standard VGA
+ case 0x10: // Standard VGA
+ case 0x11: // Standard VGA
+ case 0x12: // Standard VGA
+ case 0x13: // Standard VGA
+ case 0x14: // Standard VGA
+ case 0x15: // Standard VGA
+ case 0x16: // Standard VGA
+ case 0x17: // Standard VGA
+ case 0x18: // Standard VGA
+ /* handle CR0-7 protection */
+ if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) {
+ /* can always write bit 4 of CR7 */
+ if (s->vga.cr_index == 7)
+ s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10);
+ return;
+ }
+ s->vga.cr[s->vga.cr_index] = reg_value;
+ switch(s->vga.cr_index) {
+ case 0x00:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x11:
+ case 0x17:
+ s->vga.update_retrace_info(&s->vga);
+ break;
+ }
+ break;
+ case 0x19: // Interlace End
+ case 0x1a: // Miscellaneous Control
+ case 0x1b: // Extended Display Control
+ case 0x1c: // Sync Adjust and Genlock
+ case 0x1d: // Overlay Extended Control
+ s->vga.cr[s->vga.cr_index] = reg_value;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled outport cr_index %02x, cr_value %02x\n",
+ s->vga.cr_index, reg_value);
+#endif
+ break;
+ case 0x22: // Graphics Data Latches Readback (R)
+ case 0x24: // Attribute Controller Toggle Readback (R)
+ case 0x26: // Attribute Controller Index Readback (R)
+ case 0x27: // Part ID (R)
+ break;
+ case 0x25: // Part Status
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport cr_index %02x, cr_value %02x\n",
+ s->vga.cr_index, reg_value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * memory-mapped I/O (bitblt)
+ *
+ ***************************************/
+
+static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address)
+{
+ int value = 0xff;
+
+ switch (address) {
+ case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+ value = cirrus_vga_read_gr(s, 0x00);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+ value = cirrus_vga_read_gr(s, 0x10);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+ value = cirrus_vga_read_gr(s, 0x12);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+ value = cirrus_vga_read_gr(s, 0x14);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+ value = cirrus_vga_read_gr(s, 0x01);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+ value = cirrus_vga_read_gr(s, 0x11);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+ value = cirrus_vga_read_gr(s, 0x13);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+ value = cirrus_vga_read_gr(s, 0x15);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 0):
+ value = cirrus_vga_read_gr(s, 0x20);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 1):
+ value = cirrus_vga_read_gr(s, 0x21);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 0):
+ value = cirrus_vga_read_gr(s, 0x22);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 1):
+ value = cirrus_vga_read_gr(s, 0x23);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+ value = cirrus_vga_read_gr(s, 0x24);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+ value = cirrus_vga_read_gr(s, 0x25);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+ value = cirrus_vga_read_gr(s, 0x26);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+ value = cirrus_vga_read_gr(s, 0x27);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 0):
+ value = cirrus_vga_read_gr(s, 0x28);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 1):
+ value = cirrus_vga_read_gr(s, 0x29);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 2):
+ value = cirrus_vga_read_gr(s, 0x2a);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 0):
+ value = cirrus_vga_read_gr(s, 0x2c);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 1):
+ value = cirrus_vga_read_gr(s, 0x2d);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 2):
+ value = cirrus_vga_read_gr(s, 0x2e);
+ break;
+ case CIRRUS_MMIO_BLTWRITEMASK:
+ value = cirrus_vga_read_gr(s, 0x2f);
+ break;
+ case CIRRUS_MMIO_BLTMODE:
+ value = cirrus_vga_read_gr(s, 0x30);
+ break;
+ case CIRRUS_MMIO_BLTROP:
+ value = cirrus_vga_read_gr(s, 0x32);
+ break;
+ case CIRRUS_MMIO_BLTMODEEXT:
+ value = cirrus_vga_read_gr(s, 0x33);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+ value = cirrus_vga_read_gr(s, 0x34);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+ value = cirrus_vga_read_gr(s, 0x35);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+ value = cirrus_vga_read_gr(s, 0x38);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+ value = cirrus_vga_read_gr(s, 0x39);
+ break;
+ case CIRRUS_MMIO_BLTSTATUS:
+ value = cirrus_vga_read_gr(s, 0x31);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mmio read - address 0x%04x\n", address);
+#endif
+ break;
+ }
+
+ return (uint8_t) value;
+}
+
+static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address,
+ uint8_t value)
+{
+ switch (address) {
+ case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+ cirrus_vga_write_gr(s, 0x00, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+ cirrus_vga_write_gr(s, 0x10, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+ cirrus_vga_write_gr(s, 0x12, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+ cirrus_vga_write_gr(s, 0x14, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+ cirrus_vga_write_gr(s, 0x01, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+ cirrus_vga_write_gr(s, 0x11, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+ cirrus_vga_write_gr(s, 0x13, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+ cirrus_vga_write_gr(s, 0x15, value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 0):
+ cirrus_vga_write_gr(s, 0x20, value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 1):
+ cirrus_vga_write_gr(s, 0x21, value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 0):
+ cirrus_vga_write_gr(s, 0x22, value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 1):
+ cirrus_vga_write_gr(s, 0x23, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+ cirrus_vga_write_gr(s, 0x24, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+ cirrus_vga_write_gr(s, 0x25, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+ cirrus_vga_write_gr(s, 0x26, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+ cirrus_vga_write_gr(s, 0x27, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 0):
+ cirrus_vga_write_gr(s, 0x28, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 1):
+ cirrus_vga_write_gr(s, 0x29, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 2):
+ cirrus_vga_write_gr(s, 0x2a, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 3):
+ /* ignored */
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 0):
+ cirrus_vga_write_gr(s, 0x2c, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 1):
+ cirrus_vga_write_gr(s, 0x2d, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 2):
+ cirrus_vga_write_gr(s, 0x2e, value);
+ break;
+ case CIRRUS_MMIO_BLTWRITEMASK:
+ cirrus_vga_write_gr(s, 0x2f, value);
+ break;
+ case CIRRUS_MMIO_BLTMODE:
+ cirrus_vga_write_gr(s, 0x30, value);
+ break;
+ case CIRRUS_MMIO_BLTROP:
+ cirrus_vga_write_gr(s, 0x32, value);
+ break;
+ case CIRRUS_MMIO_BLTMODEEXT:
+ cirrus_vga_write_gr(s, 0x33, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+ cirrus_vga_write_gr(s, 0x34, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+ cirrus_vga_write_gr(s, 0x35, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+ cirrus_vga_write_gr(s, 0x38, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+ cirrus_vga_write_gr(s, 0x39, value);
+ break;
+ case CIRRUS_MMIO_BLTSTATUS:
+ cirrus_vga_write_gr(s, 0x31, value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n",
+ address, value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * write mode 4/5
+ *
+ * assume TARGET_PAGE_SIZE >= 16
+ *
+ ***************************************/
+
+static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
+ unsigned mode,
+ unsigned offset,
+ uint32_t mem_value)
+{
+ int x;
+ unsigned val = mem_value;
+ uint8_t *dst;
+
+ dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
+ for (x = 0; x < 8; x++) {
+ if (val & 0x80) {
+ *dst = s->cirrus_shadow_gr1;
+ } else if (mode == 5) {
+ *dst = s->cirrus_shadow_gr0;
+ }
+ val <<= 1;
+ dst++;
+ }
+ cpu_physical_memory_set_dirty(s->vga.vram_offset + offset);
+ cpu_physical_memory_set_dirty(s->vga.vram_offset + offset + 7);
+}
+
+static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
+ unsigned mode,
+ unsigned offset,
+ uint32_t mem_value)
+{
+ int x;
+ unsigned val = mem_value;
+ uint8_t *dst;
+
+ dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
+ for (x = 0; x < 8; x++) {
+ if (val & 0x80) {
+ *dst = s->cirrus_shadow_gr1;
+ *(dst + 1) = s->vga.gr[0x11];
+ } else if (mode == 5) {
+ *dst = s->cirrus_shadow_gr0;
+ *(dst + 1) = s->vga.gr[0x10];
+ }
+ val <<= 1;
+ dst += 2;
+ }
+ cpu_physical_memory_set_dirty(s->vga.vram_offset + offset);
+ cpu_physical_memory_set_dirty(s->vga.vram_offset + offset + 15);
+}
+
+/***************************************
+ *
+ * memory access between 0xa0000-0xbffff
+ *
+ ***************************************/
+
+static uint32_t cirrus_vga_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ CirrusVGAState *s = opaque;
+ unsigned bank_index;
+ unsigned bank_offset;
+ uint32_t val;
+
+ if ((s->vga.sr[0x07] & 0x01) == 0) {
+ return vga_mem_readb(s, addr);
+ }
+
+ addr &= 0x1ffff;
+
+ if (addr < 0x10000) {
+ /* XXX handle bitblt */
+ /* video memory */
+ bank_index = addr >> 15;
+ bank_offset = addr & 0x7fff;
+ if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+ bank_offset += s->cirrus_bank_base[bank_index];
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ bank_offset <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ bank_offset <<= 3;
+ }
+ bank_offset &= s->cirrus_addr_mask;
+ val = *(s->vga.vram_ptr + bank_offset);
+ } else
+ val = 0xff;
+ } else if (addr >= 0x18000 && addr < 0x18100) {
+ /* memory-mapped I/O */
+ val = 0xff;
+ if ((s->vga.sr[0x17] & 0x44) == 0x04) {
+ val = cirrus_mmio_blt_read(s, addr & 0xff);
+ }
+ } else {
+ val = 0xff;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mem_readb " TARGET_FMT_plx "\n", addr);
+#endif
+ }
+ return val;
+}
+
+static uint32_t cirrus_vga_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_vga_mem_readb(opaque, addr);
+ v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8;
+ return v;
+}
+
+static uint32_t cirrus_vga_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_vga_mem_readb(opaque, addr);
+ v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8;
+ v |= cirrus_vga_mem_readb(opaque, addr + 2) << 16;
+ v |= cirrus_vga_mem_readb(opaque, addr + 3) << 24;
+ return v;
+}
+
+static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ CirrusVGAState *s = opaque;
+ unsigned bank_index;
+ unsigned bank_offset;
+ unsigned mode;
+
+ if ((s->vga.sr[0x07] & 0x01) == 0) {
+ vga_mem_writeb(s, addr, mem_value);
+ return;
+ }
+
+ addr &= 0x1ffff;
+
+ if (addr < 0x10000) {
+ if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) mem_value;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ } else {
+ /* video memory */
+ bank_index = addr >> 15;
+ bank_offset = addr & 0x7fff;
+ if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+ bank_offset += s->cirrus_bank_base[bank_index];
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ bank_offset <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ bank_offset <<= 3;
+ }
+ bank_offset &= s->cirrus_addr_mask;
+ mode = s->vga.gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+ *(s->vga.vram_ptr + bank_offset) = mem_value;
+ cpu_physical_memory_set_dirty(s->vga.vram_offset +
+ bank_offset);
+ } else {
+ if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
+ cirrus_mem_writeb_mode4and5_8bpp(s, mode,
+ bank_offset,
+ mem_value);
+ } else {
+ cirrus_mem_writeb_mode4and5_16bpp(s, mode,
+ bank_offset,
+ mem_value);
+ }
+ }
+ }
+ }
+ } else if (addr >= 0x18000 && addr < 0x18100) {
+ /* memory-mapped I/O */
+ if ((s->vga.sr[0x17] & 0x44) == 0x04) {
+ cirrus_mmio_blt_write(s, addr & 0xff, mem_value);
+ }
+ } else {
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mem_writeb " TARGET_FMT_plx " value %02x\n", addr,
+ mem_value);
+#endif
+ }
+}
+
+static void cirrus_vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ cirrus_vga_mem_writeb(opaque, addr, val & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void cirrus_vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ cirrus_vga_mem_writeb(opaque, addr, val & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+static CPUReadMemoryFunc * const cirrus_vga_mem_read[3] = {
+ cirrus_vga_mem_readb,
+ cirrus_vga_mem_readw,
+ cirrus_vga_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const cirrus_vga_mem_write[3] = {
+ cirrus_vga_mem_writeb,
+ cirrus_vga_mem_writew,
+ cirrus_vga_mem_writel,
+};
+
+/***************************************
+ *
+ * hardware cursor
+ *
+ ***************************************/
+
+static inline void invalidate_cursor1(CirrusVGAState *s)
+{
+ if (s->last_hw_cursor_size) {
+ vga_invalidate_scanlines(&s->vga,
+ s->last_hw_cursor_y + s->last_hw_cursor_y_start,
+ s->last_hw_cursor_y + s->last_hw_cursor_y_end);
+ }
+}
+
+static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s)
+{
+ const uint8_t *src;
+ uint32_t content;
+ int y, y_min, y_max;
+
+ src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ src += (s->vga.sr[0x13] & 0x3c) * 256;
+ y_min = 64;
+ y_max = -1;
+ for(y = 0; y < 64; y++) {
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)src)[1] |
+ ((uint32_t *)src)[2] |
+ ((uint32_t *)src)[3];
+ if (content) {
+ if (y < y_min)
+ y_min = y;
+ if (y > y_max)
+ y_max = y;
+ }
+ src += 16;
+ }
+ } else {
+ src += (s->vga.sr[0x13] & 0x3f) * 256;
+ y_min = 32;
+ y_max = -1;
+ for(y = 0; y < 32; y++) {
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)(src + 128))[0];
+ if (content) {
+ if (y < y_min)
+ y_min = y;
+ if (y > y_max)
+ y_max = y;
+ }
+ src += 4;
+ }
+ }
+ if (y_min > y_max) {
+ s->last_hw_cursor_y_start = 0;
+ s->last_hw_cursor_y_end = 0;
+ } else {
+ s->last_hw_cursor_y_start = y_min;
+ s->last_hw_cursor_y_end = y_max + 1;
+ }
+}
+
+/* NOTE: we do not currently handle the cursor bitmap change, so we
+ update the cursor only if it moves. */
+static void cirrus_cursor_invalidate(VGACommonState *s1)
+{
+ CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
+ int size;
+
+ if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) {
+ size = 0;
+ } else {
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE)
+ size = 64;
+ else
+ size = 32;
+ }
+ /* invalidate last cursor and new cursor if any change */
+ if (s->last_hw_cursor_size != size ||
+ s->last_hw_cursor_x != s->hw_cursor_x ||
+ s->last_hw_cursor_y != s->hw_cursor_y) {
+
+ invalidate_cursor1(s);
+
+ s->last_hw_cursor_size = size;
+ s->last_hw_cursor_x = s->hw_cursor_x;
+ s->last_hw_cursor_y = s->hw_cursor_y;
+ /* compute the real cursor min and max y */
+ cirrus_cursor_compute_yrange(s);
+ invalidate_cursor1(s);
+ }
+}
+
+static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y)
+{
+ CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
+ int w, h, bpp, x1, x2, poffset;
+ unsigned int color0, color1;
+ const uint8_t *palette, *src;
+ uint32_t content;
+
+ if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW))
+ return;
+ /* fast test to see if the cursor intersects with the scan line */
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ h = 64;
+ } else {
+ h = 32;
+ }
+ if (scr_y < s->hw_cursor_y ||
+ scr_y >= (s->hw_cursor_y + h))
+ return;
+
+ src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ src += (s->vga.sr[0x13] & 0x3c) * 256;
+ src += (scr_y - s->hw_cursor_y) * 16;
+ poffset = 8;
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)src)[1] |
+ ((uint32_t *)src)[2] |
+ ((uint32_t *)src)[3];
+ } else {
+ src += (s->vga.sr[0x13] & 0x3f) * 256;
+ src += (scr_y - s->hw_cursor_y) * 4;
+ poffset = 128;
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)(src + 128))[0];
+ }
+ /* if nothing to draw, no need to continue */
+ if (!content)
+ return;
+ w = h;
+
+ x1 = s->hw_cursor_x;
+ if (x1 >= s->vga.last_scr_width)
+ return;
+ x2 = s->hw_cursor_x + w;
+ if (x2 > s->vga.last_scr_width)
+ x2 = s->vga.last_scr_width;
+ w = x2 - x1;
+ palette = s->cirrus_hidden_palette;
+ color0 = s->vga.rgb_to_pixel(c6_to_8(palette[0x0 * 3]),
+ c6_to_8(palette[0x0 * 3 + 1]),
+ c6_to_8(palette[0x0 * 3 + 2]));
+ color1 = s->vga.rgb_to_pixel(c6_to_8(palette[0xf * 3]),
+ c6_to_8(palette[0xf * 3 + 1]),
+ c6_to_8(palette[0xf * 3 + 2]));
+ bpp = ((ds_get_bits_per_pixel(s->vga.ds) + 7) >> 3);
+ d1 += x1 * bpp;
+ switch(ds_get_bits_per_pixel(s->vga.ds)) {
+ default:
+ break;
+ case 8:
+ vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff);
+ break;
+ case 15:
+ vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff);
+ break;
+ case 16:
+ vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff);
+ break;
+ case 32:
+ vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff);
+ break;
+ }
+}
+
+/***************************************
+ *
+ * LFB memory access
+ *
+ ***************************************/
+
+static uint32_t cirrus_linear_readb(void *opaque, target_phys_addr_t addr)
+{
+ CirrusVGAState *s = opaque;
+ uint32_t ret;
+
+ addr &= s->cirrus_addr_mask;
+
+ if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
+ ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+ /* memory-mapped I/O */
+ ret = cirrus_mmio_blt_read(s, addr & 0xff);
+ } else if (0) {
+ /* XXX handle bitblt */
+ ret = 0xff;
+ } else {
+ /* video memory */
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ addr <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ addr <<= 3;
+ }
+ addr &= s->cirrus_addr_mask;
+ ret = *(s->vga.vram_ptr + addr);
+ }
+
+ return ret;
+}
+
+static uint32_t cirrus_linear_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_linear_readb(opaque, addr);
+ v |= cirrus_linear_readb(opaque, addr + 1) << 8;
+ return v;
+}
+
+static uint32_t cirrus_linear_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_linear_readb(opaque, addr);
+ v |= cirrus_linear_readb(opaque, addr + 1) << 8;
+ v |= cirrus_linear_readb(opaque, addr + 2) << 16;
+ v |= cirrus_linear_readb(opaque, addr + 3) << 24;
+ return v;
+}
+
+static void cirrus_linear_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = opaque;
+ unsigned mode;
+
+ addr &= s->cirrus_addr_mask;
+
+ if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
+ ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+ /* memory-mapped I/O */
+ cirrus_mmio_blt_write(s, addr & 0xff, val);
+ } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) val;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ } else {
+ /* video memory */
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ addr <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ addr <<= 3;
+ }
+ addr &= s->cirrus_addr_mask;
+
+ mode = s->vga.gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+ *(s->vga.vram_ptr + addr) = (uint8_t) val;
+ cpu_physical_memory_set_dirty(s->vga.vram_offset + addr);
+ } else {
+ if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
+ cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val);
+ } else {
+ cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val);
+ }
+ }
+ }
+}
+
+static void cirrus_linear_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cirrus_linear_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void cirrus_linear_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cirrus_linear_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_linear_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+
+static CPUReadMemoryFunc * const cirrus_linear_read[3] = {
+ cirrus_linear_readb,
+ cirrus_linear_readw,
+ cirrus_linear_readl,
+};
+
+static CPUWriteMemoryFunc * const cirrus_linear_write[3] = {
+ cirrus_linear_writeb,
+ cirrus_linear_writew,
+ cirrus_linear_writel,
+};
+
+/***************************************
+ *
+ * system to screen memory access
+ *
+ ***************************************/
+
+
+static uint32_t cirrus_linear_bitblt_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ /* XXX handle bitblt */
+ ret = 0xff;
+ return ret;
+}
+
+static uint32_t cirrus_linear_bitblt_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_linear_bitblt_readb(opaque, addr);
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8;
+ return v;
+}
+
+static uint32_t cirrus_linear_bitblt_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_linear_bitblt_readb(opaque, addr);
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 16;
+ v |= cirrus_linear_bitblt_readb(opaque, addr + 3) << 24;
+ return v;
+}
+
+static void cirrus_linear_bitblt_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = opaque;
+
+ if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) val;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ }
+}
+
+static void cirrus_linear_bitblt_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void cirrus_linear_bitblt_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_linear_bitblt_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+
+static CPUReadMemoryFunc * const cirrus_linear_bitblt_read[3] = {
+ cirrus_linear_bitblt_readb,
+ cirrus_linear_bitblt_readw,
+ cirrus_linear_bitblt_readl,
+};
+
+static CPUWriteMemoryFunc * const cirrus_linear_bitblt_write[3] = {
+ cirrus_linear_bitblt_writeb,
+ cirrus_linear_bitblt_writew,
+ cirrus_linear_bitblt_writel,
+};
+
+static void map_linear_vram(CirrusVGAState *s)
+{
+ vga_dirty_log_stop(&s->vga);
+ if (!s->vga.map_addr && s->vga.lfb_addr && s->vga.lfb_end) {
+ s->vga.map_addr = s->vga.lfb_addr;
+ s->vga.map_end = s->vga.lfb_end;
+ cpu_register_physical_memory(s->vga.map_addr, s->vga.map_end - s->vga.map_addr, s->vga.vram_offset);
+ }
+
+ if (!s->vga.map_addr)
+ return;
+
+#ifndef TARGET_IA64
+ s->vga.lfb_vram_mapped = 0;
+
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000,
+ (s->vga.vram_offset + s->cirrus_bank_base[0]) | IO_MEM_UNASSIGNED);
+ cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000,
+ (s->vga.vram_offset + s->cirrus_bank_base[1]) | IO_MEM_UNASSIGNED);
+ if (!(s->cirrus_srcptr != s->cirrus_srcptr_end)
+ && !((s->vga.sr[0x07] & 0x01) == 0)
+ && !((s->vga.gr[0x0B] & 0x14) == 0x14)
+ && !(s->vga.gr[0x0B] & 0x02)) {
+
+ vga_dirty_log_stop(&s->vga);
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x8000,
+ (s->vga.vram_offset + s->cirrus_bank_base[0]) | IO_MEM_RAM);
+ cpu_register_physical_memory(isa_mem_base + 0xa8000, 0x8000,
+ (s->vga.vram_offset + s->cirrus_bank_base[1]) | IO_MEM_RAM);
+
+ s->vga.lfb_vram_mapped = 1;
+ }
+ else {
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000,
+ s->vga.vga_io_memory);
+ }
+#endif
+
+ vga_dirty_log_start(&s->vga);
+}
+
+static void unmap_linear_vram(CirrusVGAState *s)
+{
+ vga_dirty_log_stop(&s->vga);
+ if (s->vga.map_addr && s->vga.lfb_addr && s->vga.lfb_end) {
+ s->vga.map_addr = s->vga.map_end = 0;
+ cpu_register_physical_memory(s->vga.lfb_addr, s->vga.vram_size,
+ s->cirrus_linear_io_addr);
+ }
+ cpu_register_physical_memory(isa_mem_base + 0xa0000, 0x20000,
+ s->vga.vga_io_memory);
+
+ vga_dirty_log_start(&s->vga);
+}
+
+/* Compute the memory access functions */
+static void cirrus_update_memory_access(CirrusVGAState *s)
+{
+ unsigned mode;
+
+ if ((s->vga.sr[0x17] & 0x44) == 0x44) {
+ goto generic_io;
+ } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ goto generic_io;
+ } else {
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ goto generic_io;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ goto generic_io;
+ }
+
+ mode = s->vga.gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+ map_linear_vram(s);
+ } else {
+ generic_io:
+ unmap_linear_vram(s);
+ }
+ }
+}
+
+
+/* I/O ports */
+
+static uint32_t cirrus_vga_ioport_read(void *opaque, uint32_t addr)
+{
+ CirrusVGAState *c = opaque;
+ VGACommonState *s = &c->vga;
+ int val, index;
+
+ if (vga_ioport_invalid(s, addr)) {
+ val = 0xff;
+ } else {
+ switch (addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val = s->ar_index;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x3c1:
+ index = s->ar_index & 0x1f;
+ if (index < 21)
+ val = s->ar[index];
+ else
+ val = 0;
+ break;
+ case 0x3c2:
+ val = s->st00;
+ break;
+ case 0x3c4:
+ val = s->sr_index;
+ break;
+ case 0x3c5:
+ val = cirrus_vga_read_sr(c);
+ break;
+#ifdef DEBUG_VGA_REG
+ printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ break;
+ case 0x3c6:
+ val = cirrus_read_hidden_dac(c);
+ break;
+ case 0x3c7:
+ val = s->dac_state;
+ break;
+ case 0x3c8:
+ val = s->dac_write_index;
+ c->cirrus_hidden_dac_lockindex = 0;
+ break;
+ case 0x3c9:
+ val = cirrus_vga_read_palette(c);
+ break;
+ case 0x3ca:
+ val = s->fcr;
+ break;
+ case 0x3cc:
+ val = s->msr;
+ break;
+ case 0x3ce:
+ val = s->gr_index;
+ break;
+ case 0x3cf:
+ val = cirrus_vga_read_gr(c, s->gr_index);
+#ifdef DEBUG_VGA_REG
+ printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ val = s->cr_index;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ val = cirrus_vga_read_cr(c, s->cr_index);
+#ifdef DEBUG_VGA_REG
+ printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ break;
+ case 0x3ba:
+ case 0x3da:
+ /* just toggle to fool polling */
+ val = s->st01 = s->retrace(s);
+ s->ar_flip_flop = 0;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ }
+#if defined(DEBUG_VGA)
+ printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void cirrus_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ CirrusVGAState *c = opaque;
+ VGACommonState *s = &c->vga;
+ int index;
+
+ /* check port range access depending on color/monochrome mode */
+ if (vga_ioport_invalid(s, addr)) {
+ return;
+ }
+#ifdef DEBUG_VGA
+ printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+
+ switch (addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val &= 0x3f;
+ s->ar_index = val;
+ } else {
+ index = s->ar_index & 0x1f;
+ switch (index) {
+ case 0x00 ... 0x0f:
+ s->ar[index] = val & 0x3f;
+ break;
+ case 0x10:
+ s->ar[index] = val & ~0x10;
+ break;
+ case 0x11:
+ s->ar[index] = val;
+ break;
+ case 0x12:
+ s->ar[index] = val & ~0xc0;
+ break;
+ case 0x13:
+ s->ar[index] = val & ~0xf0;
+ break;
+ case 0x14:
+ s->ar[index] = val & ~0xf0;
+ break;
+ default:
+ break;
+ }
+ }
+ s->ar_flip_flop ^= 1;
+ break;
+ case 0x3c2:
+ s->msr = val & ~0x10;
+ s->update_retrace_info(s);
+ break;
+ case 0x3c4:
+ s->sr_index = val;
+ break;
+ case 0x3c5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ cirrus_vga_write_sr(c, val);
+ break;
+ break;
+ case 0x3c6:
+ cirrus_write_hidden_dac(c, val);
+ break;
+ case 0x3c7:
+ s->dac_read_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 3;
+ break;
+ case 0x3c8:
+ s->dac_write_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 0;
+ break;
+ case 0x3c9:
+ cirrus_vga_write_palette(c, val);
+ break;
+ case 0x3ce:
+ s->gr_index = val;
+ break;
+ case 0x3cf:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ cirrus_vga_write_gr(c, s->gr_index, val);
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ s->cr_index = val;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ cirrus_vga_write_cr(c, val);
+ break;
+ case 0x3ba:
+ case 0x3da:
+ s->fcr = val & 0x10;
+ break;
+ }
+}
+
+/***************************************
+ *
+ * memory-mapped I/O access
+ *
+ ***************************************/
+
+static uint32_t cirrus_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ CirrusVGAState *s = opaque;
+
+ addr &= CIRRUS_PNPMMIO_SIZE - 1;
+
+ if (addr >= 0x100) {
+ return cirrus_mmio_blt_read(s, addr - 0x100);
+ } else {
+ return cirrus_vga_ioport_read(s, addr + 0x3c0);
+ }
+}
+
+static uint32_t cirrus_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_mmio_readb(opaque, addr);
+ v |= cirrus_mmio_readb(opaque, addr + 1) << 8;
+ return v;
+}
+
+static uint32_t cirrus_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+
+ v = cirrus_mmio_readb(opaque, addr);
+ v |= cirrus_mmio_readb(opaque, addr + 1) << 8;
+ v |= cirrus_mmio_readb(opaque, addr + 2) << 16;
+ v |= cirrus_mmio_readb(opaque, addr + 3) << 24;
+ return v;
+}
+
+static void cirrus_mmio_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ CirrusVGAState *s = opaque;
+
+ addr &= CIRRUS_PNPMMIO_SIZE - 1;
+
+ if (addr >= 0x100) {
+ cirrus_mmio_blt_write(s, addr - 0x100, val);
+ } else {
+ cirrus_vga_ioport_write(s, addr + 0x3c0, val);
+ }
+}
+
+static void cirrus_mmio_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cirrus_mmio_writeb(opaque, addr, val & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void cirrus_mmio_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cirrus_mmio_writeb(opaque, addr, val & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ cirrus_mmio_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+
+static CPUReadMemoryFunc * const cirrus_mmio_read[3] = {
+ cirrus_mmio_readb,
+ cirrus_mmio_readw,
+ cirrus_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const cirrus_mmio_write[3] = {
+ cirrus_mmio_writeb,
+ cirrus_mmio_writew,
+ cirrus_mmio_writel,
+};
+
+/* load/save state */
+
+static int cirrus_post_load(void *opaque, int version_id)
+{
+ CirrusVGAState *s = opaque;
+
+ s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f;
+ s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f;
+
+ cirrus_update_memory_access(s);
+ /* force refresh */
+ s->vga.graphic_mode = -1;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cirrus_vga = {
+ .name = "cirrus_vga",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = cirrus_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(vga.latch, CirrusVGAState),
+ VMSTATE_UINT8(vga.sr_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.sr, CirrusVGAState),
+ VMSTATE_UINT8(vga.gr_index, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_shadow_gr0, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_shadow_gr1, CirrusVGAState),
+ VMSTATE_BUFFER_START_MIDDLE(vga.gr, CirrusVGAState, 2),
+ VMSTATE_UINT8(vga.ar_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.ar, CirrusVGAState),
+ VMSTATE_INT32(vga.ar_flip_flop, CirrusVGAState),
+ VMSTATE_UINT8(vga.cr_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.cr, CirrusVGAState),
+ VMSTATE_UINT8(vga.msr, CirrusVGAState),
+ VMSTATE_UINT8(vga.fcr, CirrusVGAState),
+ VMSTATE_UINT8(vga.st00, CirrusVGAState),
+ VMSTATE_UINT8(vga.st01, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_state, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_sub_index, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_read_index, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_write_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.dac_cache, CirrusVGAState),
+ VMSTATE_BUFFER(vga.palette, CirrusVGAState),
+ VMSTATE_INT32(vga.bank_offset, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState),
+ VMSTATE_UINT32(hw_cursor_x, CirrusVGAState),
+ VMSTATE_UINT32(hw_cursor_y, CirrusVGAState),
+ /* XXX: we do not save the bitblt state - we assume we do not save
+ the state when the blitter is active */
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pci_cirrus_vga = {
+ .name = "cirrus_vga",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState),
+ VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0,
+ vmstate_cirrus_vga, CirrusVGAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/***************************************
+ *
+ * initialize
+ *
+ ***************************************/
+
+static void cirrus_reset(void *opaque)
+{
+ CirrusVGAState *s = opaque;
+
+ vga_common_reset(&s->vga);
+ unmap_linear_vram(s);
+ s->vga.sr[0x06] = 0x0f;
+ if (s->device_id == CIRRUS_ID_CLGD5446) {
+ /* 4MB 64 bit memory config, always PCI */
+ s->vga.sr[0x1F] = 0x2d; // MemClock
+ s->vga.gr[0x18] = 0x0f; // fastest memory configuration
+ s->vga.sr[0x0f] = 0x98;
+ s->vga.sr[0x17] = 0x20;
+ s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */
+ } else {
+ s->vga.sr[0x1F] = 0x22; // MemClock
+ s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M;
+ s->vga.sr[0x17] = s->bustype;
+ s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+ }
+ s->vga.cr[0x27] = s->device_id;
+
+ /* Win2K seems to assume that the pattern buffer is at 0xff
+ initially ! */
+ memset(s->vga.vram_ptr, 0xff, s->real_vram_size);
+
+ s->cirrus_hidden_dac_lockindex = 5;
+ s->cirrus_hidden_dac_data = 0;
+}
+
+static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
+{
+ int i;
+ static int inited;
+
+ if (!inited) {
+ inited = 1;
+ for(i = 0;i < 256; i++)
+ rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */
+ rop_to_index[CIRRUS_ROP_0] = 0;
+ rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1;
+ rop_to_index[CIRRUS_ROP_NOP] = 2;
+ rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3;
+ rop_to_index[CIRRUS_ROP_NOTDST] = 4;
+ rop_to_index[CIRRUS_ROP_SRC] = 5;
+ rop_to_index[CIRRUS_ROP_1] = 6;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7;
+ rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8;
+ rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10;
+ rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11;
+ rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12;
+ rop_to_index[CIRRUS_ROP_NOTSRC] = 13;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15;
+ s->device_id = device_id;
+ if (is_pci)
+ s->bustype = CIRRUS_BUSTYPE_PCI;
+ else
+ s->bustype = CIRRUS_BUSTYPE_ISA;
+ }
+
+ register_ioport_write(0x3c0, 16, 1, cirrus_vga_ioport_write, s);
+
+ register_ioport_write(0x3b4, 2, 1, cirrus_vga_ioport_write, s);
+ register_ioport_write(0x3d4, 2, 1, cirrus_vga_ioport_write, s);
+ register_ioport_write(0x3ba, 1, 1, cirrus_vga_ioport_write, s);
+ register_ioport_write(0x3da, 1, 1, cirrus_vga_ioport_write, s);
+
+ register_ioport_read(0x3c0, 16, 1, cirrus_vga_ioport_read, s);
+
+ register_ioport_read(0x3b4, 2, 1, cirrus_vga_ioport_read, s);
+ register_ioport_read(0x3d4, 2, 1, cirrus_vga_ioport_read, s);
+ register_ioport_read(0x3ba, 1, 1, cirrus_vga_ioport_read, s);
+ register_ioport_read(0x3da, 1, 1, cirrus_vga_ioport_read, s);
+
+ s->vga.vga_io_memory = cpu_register_io_memory(cirrus_vga_mem_read,
+ cirrus_vga_mem_write, s,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
+ s->vga.vga_io_memory);
+ qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000);
+
+ /* I/O handler for LFB */
+ s->cirrus_linear_io_addr =
+ cpu_register_io_memory(cirrus_linear_read, cirrus_linear_write, s,
+ DEVICE_LITTLE_ENDIAN);
+
+ /* I/O handler for LFB */
+ s->cirrus_linear_bitblt_io_addr =
+ cpu_register_io_memory(cirrus_linear_bitblt_read,
+ cirrus_linear_bitblt_write, s,
+ DEVICE_LITTLE_ENDIAN);
+
+ /* I/O handler for memory-mapped I/O */
+ s->cirrus_mmio_io_addr =
+ cpu_register_io_memory(cirrus_mmio_read, cirrus_mmio_write, s,
+ DEVICE_LITTLE_ENDIAN);
+
+ s->real_vram_size =
+ (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024;
+
+ /* XXX: s->vga.vram_size must be a power of two */
+ s->cirrus_addr_mask = s->real_vram_size - 1;
+ s->linear_mmio_mask = s->real_vram_size - 256;
+
+ s->vga.get_bpp = cirrus_get_bpp;
+ s->vga.get_offsets = cirrus_get_offsets;
+ s->vga.get_resolution = cirrus_get_resolution;
+ s->vga.cursor_invalidate = cirrus_cursor_invalidate;
+ s->vga.cursor_draw_line = cirrus_cursor_draw_line;
+
+ qemu_register_reset(cirrus_reset, s);
+ cirrus_reset(s);
+}
+
+/***************************************
+ *
+ * ISA bus support
+ *
+ ***************************************/
+
+void isa_cirrus_vga_init(void)
+{
+ CirrusVGAState *s;
+
+ s = qemu_mallocz(sizeof(CirrusVGAState));
+
+ vga_common_init(&s->vga, VGA_RAM_SIZE);
+ cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0);
+ s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate,
+ s->vga.screen_dump, s->vga.text_update,
+ &s->vga);
+ vmstate_register(NULL, 0, &vmstate_cirrus_vga, s);
+ rom_add_vga(VGABIOS_CIRRUS_FILENAME);
+ /* XXX ISA-LFB support */
+}
+
+/***************************************
+ *
+ * PCI bus support
+ *
+ ***************************************/
+
+static void cirrus_pci_lfb_map(PCIDevice *d, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ CirrusVGAState *s = &DO_UPCAST(PCICirrusVGAState, dev, d)->cirrus_vga;
+
+ vga_dirty_log_stop(&s->vga);
+
+ /* XXX: add byte swapping apertures */
+ cpu_register_physical_memory(addr, s->vga.vram_size,
+ s->cirrus_linear_io_addr);
+ cpu_register_physical_memory(addr + 0x1000000, 0x400000,
+ s->cirrus_linear_bitblt_io_addr);
+
+ s->vga.map_addr = s->vga.map_end = 0;
+ s->vga.lfb_addr = addr & TARGET_PAGE_MASK;
+ s->vga.lfb_end = ((addr + VGA_RAM_SIZE) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+ /* account for overflow */
+ if (s->vga.lfb_end < addr + VGA_RAM_SIZE)
+ s->vga.lfb_end = addr + VGA_RAM_SIZE;
+
+ vga_dirty_log_start(&s->vga);
+}
+
+static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ CirrusVGAState *s = &DO_UPCAST(PCICirrusVGAState, dev, d)->cirrus_vga;
+
+ cpu_register_physical_memory(addr, CIRRUS_PNPMMIO_SIZE,
+ s->cirrus_mmio_io_addr);
+}
+
+static void pci_cirrus_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ PCICirrusVGAState *pvs = DO_UPCAST(PCICirrusVGAState, dev, d);
+ CirrusVGAState *s = &pvs->cirrus_vga;
+
+ vga_dirty_log_stop(&s->vga);
+
+ pci_default_write_config(d, address, val, len);
+ if (s->vga.map_addr && d->io_regions[0].addr == PCI_BAR_UNMAPPED)
+ s->vga.map_addr = 0;
+ cirrus_update_memory_access(s);
+
+ vga_dirty_log_start(&s->vga);
+}
+
+static int pci_cirrus_vga_initfn(PCIDevice *dev)
+{
+ PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
+ CirrusVGAState *s = &d->cirrus_vga;
+ uint8_t *pci_conf = d->dev.config;
+ int device_id = CIRRUS_ID_CLGD5446;
+
+ /* setup VGA */
+ vga_common_init(&s->vga, VGA_RAM_SIZE);
+ cirrus_init_common(s, device_id, 1);
+ s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate,
+ s->vga.screen_dump, s->vga.text_update,
+ &s->vga);
+
+ /* setup PCI */
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_CIRRUS);
+ pci_config_set_device_id(pci_conf, device_id);
+ pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA);
+
+ /* setup memory space */
+ /* memory #0 LFB */
+ /* memory #1 memory-mapped I/O */
+ /* XXX: s->vga.vram_size must be a power of two */
+ pci_register_bar(&d->dev, 0, 0x2000000,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, cirrus_pci_lfb_map);
+ if (device_id == CIRRUS_ID_CLGD5446) {
+ pci_register_bar(&d->dev, 1, CIRRUS_PNPMMIO_SIZE,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, cirrus_pci_mmio_map);
+ }
+ return 0;
+}
+
+void pci_cirrus_vga_init(PCIBus *bus)
+{
+ pci_create_simple(bus, -1, "cirrus-vga");
+}
+
+static PCIDeviceInfo cirrus_vga_info = {
+ .qdev.name = "cirrus-vga",
+ .qdev.desc = "Cirrus CLGD 54xx VGA",
+ .qdev.size = sizeof(PCICirrusVGAState),
+ .qdev.vmsd = &vmstate_pci_cirrus_vga,
+ .no_hotplug = 1,
+ .init = pci_cirrus_vga_initfn,
+ .romfile = VGABIOS_CIRRUS_FILENAME,
+ .config_write = pci_cirrus_write_config,
+};
+
+static void cirrus_vga_register(void)
+{
+ pci_qdev_register(&cirrus_vga_info);
+}
+device_init(cirrus_vga_register);
diff --git a/hw/cirrus_vga_rop.h b/hw/cirrus_vga_rop.h
new file mode 100644
index 0000000..9c7bb09
--- /dev/null
+++ b/hw/cirrus_vga_rop.h
@@ -0,0 +1,208 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static inline void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+static inline void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+static inline void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s)
+#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s)
+#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s)
+#undef ROP_FN
+
+static void
+glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+
+ if (dstpitch < 0 || srcpitch < 0) {
+ /* is 0 valid? srcpitch == 0 could be useful */
+ return;
+ }
+
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ ROP_OP(dst, *src);
+ dst++;
+ src++;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ ROP_OP(dst, *src);
+ dst--;
+ src--;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ p = *dst;
+ ROP_OP(&p, *src);
+ if (p != s->vga.gr[0x34]) *dst = p;
+ dst++;
+ src++;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ p = *dst;
+ ROP_OP(&p, *src);
+ if (p != s->vga.gr[0x34]) *dst = p;
+ dst--;
+ src--;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p1, p2;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x+=2) {
+ p1 = *dst;
+ p2 = *(dst+1);
+ ROP_OP(&p1, *src);
+ ROP_OP(&p2, *(src + 1));
+ if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
+ *dst = p1;
+ *(dst+1) = p2;
+ }
+ dst+=2;
+ src+=2;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p1, p2;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x+=2) {
+ p1 = *(dst-1);
+ p2 = *dst;
+ ROP_OP(&p1, *(src - 1));
+ ROP_OP(&p2, *src);
+ if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
+ *(dst-1) = p1;
+ *dst = p2;
+ }
+ dst-=2;
+ src-=2;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+#define DEPTH 8
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 16
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 24
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 32
+#include "cirrus_vga_rop2.h"
+
+#undef ROP_NAME
+#undef ROP_OP
+#undef ROP_OP_16
+#undef ROP_OP_32
diff --git a/hw/cirrus_vga_rop2.h b/hw/cirrus_vga_rop2.h
new file mode 100644
index 0000000..d28bcc6
--- /dev/null
+++ b/hw/cirrus_vga_rop2.h
@@ -0,0 +1,281 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if DEPTH == 8
+#define PUTPIXEL() ROP_OP(&d[0], col)
+#elif DEPTH == 16
+#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col)
+#elif DEPTH == 24
+#define PUTPIXEL() ROP_OP(&d[0], col); \
+ ROP_OP(&d[1], (col >> 8)); \
+ ROP_OP(&d[2], (col >> 16))
+#elif DEPTH == 32
+#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col)
+#else
+#error unsupported DEPTH
+#endif
+
+static void
+glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y, pattern_y, pattern_pitch, pattern_x;
+ unsigned int col;
+ const uint8_t *src1;
+#if DEPTH == 24
+ int skipleft = s->vga.gr[0x2f] & 0x1f;
+#else
+ int skipleft = (s->vga.gr[0x2f] & 0x07) * (DEPTH / 8);
+#endif
+
+#if DEPTH == 8
+ pattern_pitch = 8;
+#elif DEPTH == 16
+ pattern_pitch = 16;
+#else
+ pattern_pitch = 32;
+#endif
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+ for(y = 0; y < bltheight; y++) {
+ pattern_x = skipleft;
+ d = dst + skipleft;
+ src1 = src + pattern_y * pattern_pitch;
+ for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) {
+#if DEPTH == 8
+ col = src1[pattern_x];
+ pattern_x = (pattern_x + 1) & 7;
+#elif DEPTH == 16
+ col = ((uint16_t *)(src1 + pattern_x))[0];
+ pattern_x = (pattern_x + 2) & 15;
+#elif DEPTH == 24
+ {
+ const uint8_t *src2 = src1 + pattern_x * 3;
+ col = src2[0] | (src2[1] << 8) | (src2[2] << 16);
+ pattern_x = (pattern_x + 1) & 7;
+ }
+#else
+ col = ((uint32_t *)(src1 + pattern_x))[0];
+ pattern_x = (pattern_x + 4) & 31;
+#endif
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+/* NOTE: srcpitch is ignored */
+static void
+glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y;
+ unsigned bits, bits_xor;
+ unsigned int col;
+ unsigned bitmask;
+ unsigned index;
+#if DEPTH == 24
+ int dstskipleft = s->vga.gr[0x2f] & 0x1f;
+ int srcskipleft = dstskipleft / 3;
+#else
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+#endif
+
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+ bits_xor = 0xff;
+ col = s->cirrus_blt_bgcol;
+ } else {
+ bits_xor = 0x00;
+ col = s->cirrus_blt_fgcol;
+ }
+
+ for(y = 0; y < bltheight; y++) {
+ bitmask = 0x80 >> srcskipleft;
+ bits = *src++ ^ bits_xor;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bitmask & 0xff) == 0) {
+ bitmask = 0x80;
+ bits = *src++ ^ bits_xor;
+ }
+ index = (bits & bitmask);
+ if (index) {
+ PUTPIXEL();
+ }
+ d += (DEPTH / 8);
+ bitmask >>= 1;
+ }
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint32_t colors[2];
+ uint8_t *d;
+ int x, y;
+ unsigned bits;
+ unsigned int col;
+ unsigned bitmask;
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+
+ colors[0] = s->cirrus_blt_bgcol;
+ colors[1] = s->cirrus_blt_fgcol;
+ for(y = 0; y < bltheight; y++) {
+ bitmask = 0x80 >> srcskipleft;
+ bits = *src++;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bitmask & 0xff) == 0) {
+ bitmask = 0x80;
+ bits = *src++;
+ }
+ col = colors[!!(bits & bitmask)];
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ bitmask >>= 1;
+ }
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y, bitpos, pattern_y;
+ unsigned int bits, bits_xor;
+ unsigned int col;
+#if DEPTH == 24
+ int dstskipleft = s->vga.gr[0x2f] & 0x1f;
+ int srcskipleft = dstskipleft / 3;
+#else
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+#endif
+
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+ bits_xor = 0xff;
+ col = s->cirrus_blt_bgcol;
+ } else {
+ bits_xor = 0x00;
+ col = s->cirrus_blt_fgcol;
+ }
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+
+ for(y = 0; y < bltheight; y++) {
+ bits = src[pattern_y] ^ bits_xor;
+ bitpos = 7 - srcskipleft;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bits >> bitpos) & 1) {
+ PUTPIXEL();
+ }
+ d += (DEPTH / 8);
+ bitpos = (bitpos - 1) & 7;
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint32_t colors[2];
+ uint8_t *d;
+ int x, y, bitpos, pattern_y;
+ unsigned int bits;
+ unsigned int col;
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+
+ colors[0] = s->cirrus_blt_bgcol;
+ colors[1] = s->cirrus_blt_fgcol;
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+
+ for(y = 0; y < bltheight; y++) {
+ bits = src[pattern_y];
+ bitpos = 7 - srcskipleft;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ col = colors[(bits >> bitpos) & 1];
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ bitpos = (bitpos - 1) & 7;
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState *s,
+ uint8_t *dst, int dst_pitch,
+ int width, int height)
+{
+ uint8_t *d, *d1;
+ uint32_t col;
+ int x, y;
+
+ col = s->cirrus_blt_fgcol;
+
+ d1 = dst;
+ for(y = 0; y < height; y++) {
+ d = d1;
+ for(x = 0; x < width; x += (DEPTH / 8)) {
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ }
+ d1 += dst_pitch;
+ }
+}
+
+#undef DEPTH
+#undef PUTPIXEL
diff --git a/hw/cris-boot.c b/hw/cris-boot.c
new file mode 100644
index 0000000..2ef17f6
--- /dev/null
+++ b/hw/cris-boot.c
@@ -0,0 +1,97 @@
+/*
+ * CRIS image loading.
+ *
+ * Copyright (c) 2010 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "sysemu.h"
+#include "loader.h"
+#include "elf.h"
+#include "cris-boot.h"
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ struct cris_load_info *li;
+
+ li = env->load_info;
+
+ cpu_reset(env);
+
+ if (!li) {
+ /* nothing more to do. */
+ return;
+ }
+
+ env->pc = li->entry;
+
+ if (li->image_filename) {
+ env->regs[8] = 0x56902387; /* RAM boot magic. */
+ env->regs[9] = 0x40004000 + li->image_size;
+ }
+
+ if (li->cmdline) {
+ /* Let the kernel know we are modifying the cmdline. */
+ env->regs[10] = 0x87109563;
+ env->regs[11] = 0x40000000;
+ }
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return addr - 0x80000000LL;
+}
+
+void cris_load_image(CPUState *env, struct cris_load_info *li)
+{
+ uint64_t entry, high;
+ int kcmdline_len;
+ int image_size;
+
+ env->load_info = li;
+ /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
+ devboard SDK. */
+ image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
+ &entry, NULL, &high, 0, ELF_MACHINE, 0);
+ li->entry = entry;
+ if (image_size < 0) {
+ /* Takes a kimage from the axis devboard SDK. */
+ image_size = load_image_targphys(li->image_filename, 0x40004000,
+ ram_size);
+ li->entry = 0x40004000;
+ }
+
+ if (image_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ li->image_filename);
+ exit(1);
+ }
+
+ if (li->cmdline && (kcmdline_len = strlen(li->cmdline))) {
+ if (kcmdline_len > 256) {
+ fprintf(stderr, "Too long CRIS kernel cmdline (max 256)\n");
+ exit(1);
+ }
+ pstrcpy_targphys("cmdline", 0x40000000, 256, li->cmdline);
+ }
+ qemu_register_reset(main_cpu_reset, env);
+}
diff --git a/hw/cris-boot.h b/hw/cris-boot.h
new file mode 100644
index 0000000..e9caf8d
--- /dev/null
+++ b/hw/cris-boot.h
@@ -0,0 +1,11 @@
+
+struct cris_load_info
+{
+ const char *image_filename;
+ const char *cmdline;
+ int image_size;
+
+ target_phys_addr_t entry;
+};
+
+void cris_load_image(CPUState *env, struct cris_load_info *li);
diff --git a/hw/cris_pic_cpu.c b/hw/cris_pic_cpu.c
new file mode 100644
index 0000000..a92d445
--- /dev/null
+++ b/hw/cris_pic_cpu.c
@@ -0,0 +1,50 @@
+/*
+ * QEMU CRIS CPU interrupt wrapper logic.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "etraxfs.h"
+
+#define D(x)
+
+void pic_info(Monitor *mon)
+{}
+void irq_info(Monitor *mon)
+{}
+
+static void cris_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ CPUState *env = (CPUState *)opaque;
+ int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+ if (level)
+ cpu_interrupt(env, type);
+ else
+ cpu_reset_interrupt(env, type);
+}
+
+qemu_irq *cris_pic_init_cpu(CPUState *env)
+{
+ return qemu_allocate_irqs(cris_pic_cpu_handler, env, 2);
+}
diff --git a/hw/cs4231.c b/hw/cs4231.c
new file mode 100644
index 0000000..a65b697
--- /dev/null
+++ b/hw/cs4231.c
@@ -0,0 +1,175 @@
+/*
+ * QEMU Crystal CS4231 audio chip emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "trace.h"
+
+/*
+ * In addition to Crystal CS4231 there is a DMA controller on Sparc.
+ */
+#define CS_SIZE 0x40
+#define CS_REGS 16
+#define CS_DREGS 32
+#define CS_MAXDREG (CS_DREGS - 1)
+
+typedef struct CSState {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t regs[CS_REGS];
+ uint8_t dregs[CS_DREGS];
+} CSState;
+
+#define CS_RAP(s) ((s)->regs[0] & CS_MAXDREG)
+#define CS_VER 0xa0
+#define CS_CDC_VER 0x8a
+
+static void cs_reset(DeviceState *d)
+{
+ CSState *s = container_of(d, CSState, busdev.qdev);
+
+ memset(s->regs, 0, CS_REGS * 4);
+ memset(s->dregs, 0, CS_DREGS);
+ s->dregs[12] = CS_CDC_VER;
+ s->dregs[25] = CS_VER;
+}
+
+static uint32_t cs_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ CSState *s = opaque;
+ uint32_t saddr, ret;
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ case 1:
+ switch (CS_RAP(s)) {
+ case 3: // Write only
+ ret = 0;
+ break;
+ default:
+ ret = s->dregs[CS_RAP(s)];
+ break;
+ }
+ trace_cs4231_mem_readl_dreg(CS_RAP(s), ret);
+ break;
+ default:
+ ret = s->regs[saddr];
+ trace_cs4231_mem_readl_reg(saddr, ret);
+ break;
+ }
+ return ret;
+}
+
+static void cs_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ CSState *s = opaque;
+ uint32_t saddr;
+
+ saddr = addr >> 2;
+ trace_cs4231_mem_writel_reg(saddr, s->regs[saddr], val);
+ switch (saddr) {
+ case 1:
+ trace_cs4231_mem_writel_dreg(CS_RAP(s), s->dregs[CS_RAP(s)], val);
+ switch(CS_RAP(s)) {
+ case 11:
+ case 25: // Read only
+ break;
+ case 12:
+ val &= 0x40;
+ val |= CS_CDC_VER; // Codec version
+ s->dregs[CS_RAP(s)] = val;
+ break;
+ default:
+ s->dregs[CS_RAP(s)] = val;
+ break;
+ }
+ break;
+ case 2: // Read only
+ break;
+ case 4:
+ if (val & 1) {
+ cs_reset(&s->busdev.qdev);
+ }
+ val &= 0x7f;
+ s->regs[saddr] = val;
+ break;
+ default:
+ s->regs[saddr] = val;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const cs_mem_read[3] = {
+ cs_mem_readl,
+ cs_mem_readl,
+ cs_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const cs_mem_write[3] = {
+ cs_mem_writel,
+ cs_mem_writel,
+ cs_mem_writel,
+};
+
+static const VMStateDescription vmstate_cs4231 = {
+ .name ="cs4231",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS),
+ VMSTATE_UINT8_ARRAY(dregs, CSState, CS_DREGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int cs4231_init1(SysBusDevice *dev)
+{
+ int io;
+ CSState *s = FROM_SYSBUS(CSState, dev);
+
+ io = cpu_register_io_memory(cs_mem_read, cs_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, CS_SIZE, io);
+ sysbus_init_irq(dev, &s->irq);
+
+ return 0;
+}
+
+static SysBusDeviceInfo cs4231_info = {
+ .init = cs4231_init1,
+ .qdev.name = "SUNW,CS4231",
+ .qdev.size = sizeof(CSState),
+ .qdev.vmsd = &vmstate_cs4231,
+ .qdev.reset = cs_reset,
+ .qdev.props = (Property[]) {
+ {.name = NULL}
+ }
+};
+
+static void cs4231_register_devices(void)
+{
+ sysbus_register_withprop(&cs4231_info);
+}
+
+device_init(cs4231_register_devices)
diff --git a/hw/cs4231a.c b/hw/cs4231a.c
new file mode 100644
index 0000000..598f032
--- /dev/null
+++ b/hw/cs4231a.c
@@ -0,0 +1,686 @@
+/*
+ * QEMU Crystal CS4231 audio chip emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "isa.h"
+#include "qdev.h"
+#include "qemu-timer.h"
+
+/*
+ Missing features:
+ ADC
+ Loopback
+ Timer
+ ADPCM
+ More...
+*/
+
+/* #define DEBUG */
+/* #define DEBUG_XLAW */
+
+static struct {
+ int aci_counter;
+} conf = {1};
+
+#ifdef DEBUG
+#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
+#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
+
+#define CS_REGS 16
+#define CS_DREGS 32
+
+typedef struct CSState {
+ ISADevice dev;
+ QEMUSoundCard card;
+ qemu_irq pic;
+ uint32_t regs[CS_REGS];
+ uint8_t dregs[CS_DREGS];
+ uint32_t irq;
+ uint32_t dma;
+ uint32_t port;
+ int shift;
+ int dma_running;
+ int audio_free;
+ int transferred;
+ int aci_counter;
+ SWVoiceOut *voice;
+ int16_t *tab;
+} CSState;
+
+#define IO_READ_PROTO(name) \
+ static uint32_t name (void *opaque, uint32_t addr)
+
+#define IO_WRITE_PROTO(name) \
+ static void name (void *opaque, uint32_t addr, uint32_t val)
+
+#define GET_SADDR(addr) (addr & 3)
+
+#define MODE2 (1 << 6)
+#define MCE (1 << 6)
+#define PMCE (1 << 4)
+#define CMCE (1 << 5)
+#define TE (1 << 6)
+#define PEN (1 << 0)
+#define INT (1 << 0)
+#define IEN (1 << 1)
+#define PPIO (1 << 6)
+#define PI (1 << 4)
+#define CI (1 << 5)
+#define TI (1 << 6)
+
+enum {
+ Index_Address,
+ Index_Data,
+ Status,
+ PIO_Data
+};
+
+enum {
+ Left_ADC_Input_Control,
+ Right_ADC_Input_Control,
+ Left_AUX1_Input_Control,
+ Right_AUX1_Input_Control,
+ Left_AUX2_Input_Control,
+ Right_AUX2_Input_Control,
+ Left_DAC_Output_Control,
+ Right_DAC_Output_Control,
+ FS_And_Playback_Data_Format,
+ Interface_Configuration,
+ Pin_Control,
+ Error_Status_And_Initialization,
+ MODE_And_ID,
+ Loopback_Control,
+ Playback_Upper_Base_Count,
+ Playback_Lower_Base_Count,
+ Alternate_Feature_Enable_I,
+ Alternate_Feature_Enable_II,
+ Left_Line_Input_Control,
+ Right_Line_Input_Control,
+ Timer_Low_Base,
+ Timer_High_Base,
+ RESERVED,
+ Alternate_Feature_Enable_III,
+ Alternate_Feature_Status,
+ Version_Chip_ID,
+ Mono_Input_And_Output_Control,
+ RESERVED_2,
+ Capture_Data_Format,
+ RESERVED_3,
+ Capture_Upper_Base_Count,
+ Capture_Lower_Base_Count
+};
+
+static int freqs[2][8] = {
+ { 8000, 16000, 27420, 32000, -1, -1, 48000, 9000 },
+ { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
+};
+
+/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
+static int16_t MuLawDecompressTable[256] =
+{
+ -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+ -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+ -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+ -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, 0,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+ 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+ 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+ 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
+ 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
+ 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
+ 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
+ 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
+ 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
+ 876, 844, 812, 780, 748, 716, 684, 652,
+ 620, 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276, 260,
+ 244, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0
+};
+
+static int16_t ALawDecompressTable[256] =
+{
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+ -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+ -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+ -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
+ -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
+ -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
+ -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
+ -344, -328, -376, -360, -280, -264, -312, -296,
+ -472, -456, -504, -488, -408, -392, -440, -424,
+ -88, -72, -120, -104, -24, -8, -56, -40,
+ -216, -200, -248, -232, -152, -136, -184, -168,
+ -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624, -592,
+ -944, -912, -1008, -976, -816, -784, -880, -848,
+ 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
+ 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
+ 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
+ 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
+ 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+ 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
+ 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+ 344, 328, 376, 360, 280, 264, 312, 296,
+ 472, 456, 504, 488, 408, 392, 440, 424,
+ 88, 72, 120, 104, 24, 8, 56, 40,
+ 216, 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
+ 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
+ 688, 656, 752, 720, 560, 528, 624, 592,
+ 944, 912, 1008, 976, 816, 784, 880, 848
+};
+
+static void cs_reset (void *opaque)
+{
+ CSState *s = opaque;
+
+ s->regs[Index_Address] = 0x40;
+ s->regs[Index_Data] = 0x00;
+ s->regs[Status] = 0x00;
+ s->regs[PIO_Data] = 0x00;
+
+ s->dregs[Left_ADC_Input_Control] = 0x00;
+ s->dregs[Right_ADC_Input_Control] = 0x00;
+ s->dregs[Left_AUX1_Input_Control] = 0x88;
+ s->dregs[Right_AUX1_Input_Control] = 0x88;
+ s->dregs[Left_AUX2_Input_Control] = 0x88;
+ s->dregs[Right_AUX2_Input_Control] = 0x88;
+ s->dregs[Left_DAC_Output_Control] = 0x80;
+ s->dregs[Right_DAC_Output_Control] = 0x80;
+ s->dregs[FS_And_Playback_Data_Format] = 0x00;
+ s->dregs[Interface_Configuration] = 0x08;
+ s->dregs[Pin_Control] = 0x00;
+ s->dregs[Error_Status_And_Initialization] = 0x00;
+ s->dregs[MODE_And_ID] = 0x8a;
+ s->dregs[Loopback_Control] = 0x00;
+ s->dregs[Playback_Upper_Base_Count] = 0x00;
+ s->dregs[Playback_Lower_Base_Count] = 0x00;
+ s->dregs[Alternate_Feature_Enable_I] = 0x00;
+ s->dregs[Alternate_Feature_Enable_II] = 0x00;
+ s->dregs[Left_Line_Input_Control] = 0x88;
+ s->dregs[Right_Line_Input_Control] = 0x88;
+ s->dregs[Timer_Low_Base] = 0x00;
+ s->dregs[Timer_High_Base] = 0x00;
+ s->dregs[RESERVED] = 0x00;
+ s->dregs[Alternate_Feature_Enable_III] = 0x00;
+ s->dregs[Alternate_Feature_Status] = 0x00;
+ s->dregs[Version_Chip_ID] = 0xa0;
+ s->dregs[Mono_Input_And_Output_Control] = 0xa0;
+ s->dregs[RESERVED_2] = 0x00;
+ s->dregs[Capture_Data_Format] = 0x00;
+ s->dregs[RESERVED_3] = 0x00;
+ s->dregs[Capture_Upper_Base_Count] = 0x00;
+ s->dregs[Capture_Lower_Base_Count] = 0x00;
+}
+
+static void cs_audio_callback (void *opaque, int free)
+{
+ CSState *s = opaque;
+ s->audio_free = free;
+}
+
+static void cs_reset_voices (CSState *s, uint32_t val)
+{
+ int xtal;
+ struct audsettings as;
+
+#ifdef DEBUG_XLAW
+ if (val == 0 || val == 32)
+ val = (1 << 4) | (1 << 5);
+#endif
+
+ xtal = val & 1;
+ as.freq = freqs[xtal][(val >> 1) & 7];
+
+ if (as.freq == -1) {
+ lerr ("unsupported frequency (val=%#x)\n", val);
+ goto error;
+ }
+
+ as.nchannels = (val & (1 << 4)) ? 2 : 1;
+ as.endianness = 0;
+ s->tab = NULL;
+
+ switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
+ case 0:
+ as.fmt = AUD_FMT_U8;
+ s->shift = as.nchannels == 2;
+ break;
+
+ case 1:
+ s->tab = MuLawDecompressTable;
+ goto x_law;
+ case 3:
+ s->tab = ALawDecompressTable;
+ x_law:
+ as.fmt = AUD_FMT_S16;
+ as.endianness = AUDIO_HOST_ENDIANNESS;
+ s->shift = as.nchannels == 2;
+ break;
+
+ case 6:
+ as.endianness = 1;
+ case 2:
+ as.fmt = AUD_FMT_S16;
+ s->shift = as.nchannels;
+ break;
+
+ case 7:
+ case 4:
+ lerr ("attempt to use reserved format value (%#x)\n", val);
+ goto error;
+
+ case 5:
+ lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
+ goto error;
+ }
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "cs4231a",
+ s,
+ cs_audio_callback,
+ &as
+ );
+
+ if (s->dregs[Interface_Configuration] & PEN) {
+ if (!s->dma_running) {
+ DMA_hold_DREQ (s->dma);
+ AUD_set_active_out (s->voice, 1);
+ s->transferred = 0;
+ }
+ s->dma_running = 1;
+ }
+ else {
+ if (s->dma_running) {
+ DMA_release_DREQ (s->dma);
+ AUD_set_active_out (s->voice, 0);
+ }
+ s->dma_running = 0;
+ }
+ return;
+
+ error:
+ if (s->dma_running) {
+ DMA_release_DREQ (s->dma);
+ AUD_set_active_out (s->voice, 0);
+ }
+}
+
+IO_READ_PROTO (cs_read)
+{
+ CSState *s = opaque;
+ uint32_t saddr, iaddr, ret;
+
+ saddr = GET_SADDR (addr);
+ iaddr = ~0U;
+
+ switch (saddr) {
+ case Index_Address:
+ ret = s->regs[saddr] & ~0x80;
+ break;
+
+ case Index_Data:
+ if (!(s->dregs[MODE_And_ID] & MODE2))
+ iaddr = s->regs[Index_Address] & 0x0f;
+ else
+ iaddr = s->regs[Index_Address] & 0x1f;
+
+ ret = s->dregs[iaddr];
+ if (iaddr == Error_Status_And_Initialization) {
+ /* keep SEAL happy */
+ if (s->aci_counter) {
+ ret |= 1 << 5;
+ s->aci_counter -= 1;
+ }
+ }
+ break;
+
+ default:
+ ret = s->regs[saddr];
+ break;
+ }
+ dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
+ return ret;
+}
+
+IO_WRITE_PROTO (cs_write)
+{
+ CSState *s = opaque;
+ uint32_t saddr, iaddr;
+
+ saddr = GET_SADDR (addr);
+
+ switch (saddr) {
+ case Index_Address:
+ if (!(s->regs[Index_Address] & MCE) && (val & MCE)
+ && (s->dregs[Interface_Configuration] & (3 << 3)))
+ s->aci_counter = conf.aci_counter;
+
+ s->regs[Index_Address] = val & ~(1 << 7);
+ break;
+
+ case Index_Data:
+ if (!(s->dregs[MODE_And_ID] & MODE2))
+ iaddr = s->regs[Index_Address] & 0x0f;
+ else
+ iaddr = s->regs[Index_Address] & 0x1f;
+
+ switch (iaddr) {
+ case RESERVED:
+ case RESERVED_2:
+ case RESERVED_3:
+ lwarn ("attempt to write %#x to reserved indirect register %d\n",
+ val, iaddr);
+ break;
+
+ case FS_And_Playback_Data_Format:
+ if (s->regs[Index_Address] & MCE) {
+ cs_reset_voices (s, val);
+ }
+ else {
+ if (s->dregs[Alternate_Feature_Status] & PMCE) {
+ val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
+ cs_reset_voices (s, val);
+ }
+ else {
+ lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
+ s->regs[Index_Address],
+ s->dregs[Alternate_Feature_Status],
+ val);
+ break;
+ }
+ }
+ s->dregs[iaddr] = val;
+ break;
+
+ case Interface_Configuration:
+ val &= ~(1 << 5); /* D5 is reserved */
+ s->dregs[iaddr] = val;
+ if (val & PPIO) {
+ lwarn ("PIO is not supported (%#x)\n", val);
+ break;
+ }
+ if (val & PEN) {
+ if (!s->dma_running) {
+ cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
+ }
+ }
+ else {
+ if (s->dma_running) {
+ DMA_release_DREQ (s->dma);
+ AUD_set_active_out (s->voice, 0);
+ s->dma_running = 0;
+ }
+ }
+ break;
+
+ case Error_Status_And_Initialization:
+ lwarn ("attempt to write to read only register %d\n", iaddr);
+ break;
+
+ case MODE_And_ID:
+ dolog ("val=%#x\n", val);
+ if (val & MODE2)
+ s->dregs[iaddr] |= MODE2;
+ else
+ s->dregs[iaddr] &= ~MODE2;
+ break;
+
+ case Alternate_Feature_Enable_I:
+ if (val & TE)
+ lerr ("timer is not yet supported\n");
+ s->dregs[iaddr] = val;
+ break;
+
+ case Alternate_Feature_Status:
+ if ((s->dregs[iaddr] & PI) && !(val & PI)) {
+ /* XXX: TI CI */
+ qemu_irq_lower (s->pic);
+ s->regs[Status] &= ~INT;
+ }
+ s->dregs[iaddr] = val;
+ break;
+
+ case Version_Chip_ID:
+ lwarn ("write to Version_Chip_ID register %#x\n", val);
+ s->dregs[iaddr] = val;
+ break;
+
+ default:
+ s->dregs[iaddr] = val;
+ break;
+ }
+ dolog ("written value %#x to indirect register %d\n", val, iaddr);
+ break;
+
+ case Status:
+ if (s->regs[Status] & INT) {
+ qemu_irq_lower (s->pic);
+ }
+ s->regs[Status] &= ~INT;
+ s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
+ break;
+
+ case PIO_Data:
+ lwarn ("attempt to write value %#x to PIO register\n", val);
+ break;
+ }
+}
+
+static int cs_write_audio (CSState *s, int nchan, int dma_pos,
+ int dma_len, int len)
+{
+ int temp, net;
+ uint8_t tmpbuf[4096];
+
+ temp = len;
+ net = 0;
+
+ while (temp) {
+ int left = dma_len - dma_pos;
+ int copied;
+ size_t to_copy;
+
+ to_copy = audio_MIN (temp, left);
+ if (to_copy > sizeof (tmpbuf)) {
+ to_copy = sizeof (tmpbuf);
+ }
+
+ copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+ if (s->tab) {
+ int i;
+ int16_t linbuf[4096];
+
+ for (i = 0; i < copied; ++i)
+ linbuf[i] = s->tab[tmpbuf[i]];
+ copied = AUD_write (s->voice, linbuf, copied << 1);
+ copied >>= 1;
+ }
+ else {
+ copied = AUD_write (s->voice, tmpbuf, copied);
+ }
+
+ temp -= copied;
+ dma_pos = (dma_pos + copied) % dma_len;
+ net += copied;
+
+ if (!copied) {
+ break;
+ }
+ }
+
+ return net;
+}
+
+static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ CSState *s = opaque;
+ int copy, written;
+ int till = -1;
+
+ copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
+
+ if (s->dregs[Pin_Control] & IEN) {
+ till = (s->dregs[Playback_Lower_Base_Count]
+ | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
+ till -= s->transferred;
+ copy = audio_MIN (till, copy);
+ }
+
+ if ((copy <= 0) || (dma_len <= 0)) {
+ return dma_pos;
+ }
+
+ written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
+
+ dma_pos = (dma_pos + written) % dma_len;
+ s->audio_free -= (written << (s->tab != NULL));
+
+ if (written == till) {
+ s->regs[Status] |= INT;
+ s->dregs[Alternate_Feature_Status] |= PI;
+ s->transferred = 0;
+ qemu_irq_raise (s->pic);
+ }
+ else {
+ s->transferred += written;
+ }
+
+ return dma_pos;
+}
+
+static int cs4231a_pre_load (void *opaque)
+{
+ CSState *s = opaque;
+
+ if (s->dma_running) {
+ DMA_release_DREQ (s->dma);
+ AUD_set_active_out (s->voice, 0);
+ }
+ s->dma_running = 0;
+ return 0;
+}
+
+static int cs4231a_post_load (void *opaque, int version_id)
+{
+ CSState *s = opaque;
+
+ if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
+ s->dma_running = 0;
+ cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_cs4231a = {
+ .name = "cs4231a",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_load = cs4231a_pre_load,
+ .post_load = cs4231a_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS),
+ VMSTATE_BUFFER(dregs, CSState),
+ VMSTATE_INT32(dma_running, CSState),
+ VMSTATE_INT32(audio_free, CSState),
+ VMSTATE_INT32(transferred, CSState),
+ VMSTATE_INT32(aci_counter, CSState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int cs4231a_initfn (ISADevice *dev)
+{
+ CSState *s = DO_UPCAST (CSState, dev, dev);
+ int i;
+
+ isa_init_irq (dev, &s->pic, s->irq);
+
+ for (i = 0; i < 4; i++) {
+ isa_init_ioport(dev, i);
+ register_ioport_write (s->port + i, 1, 1, cs_write, s);
+ register_ioport_read (s->port + i, 1, 1, cs_read, s);
+ }
+
+ DMA_register_channel (s->dma, cs_dma_read, s);
+
+ qemu_register_reset (cs_reset, s);
+ cs_reset (s);
+
+ AUD_register_card ("cs4231a", &s->card);
+ return 0;
+}
+
+int cs4231a_init (qemu_irq *pic)
+{
+ isa_create_simple ("cs4231a");
+ return 0;
+}
+
+static ISADeviceInfo cs4231a_info = {
+ .qdev.name = "cs4231a",
+ .qdev.desc = "Crystal Semiconductor CS4231A",
+ .qdev.size = sizeof (CSState),
+ .qdev.vmsd = &vmstate_cs4231a,
+ .init = cs4231a_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32 ("iobase", CSState, port, 0x534),
+ DEFINE_PROP_UINT32 ("irq", CSState, irq, 9),
+ DEFINE_PROP_UINT32 ("dma", CSState, dma, 3),
+ DEFINE_PROP_END_OF_LIST (),
+ },
+};
+
+static void cs4231a_register (void)
+{
+ isa_qdev_register (&cs4231a_info);
+}
+device_init (cs4231a_register)
diff --git a/hw/cuda.c b/hw/cuda.c
new file mode 100644
index 0000000..e4c178d
--- /dev/null
+++ b/hw/cuda.c
@@ -0,0 +1,769 @@
+/*
+ * QEMU PowerMac CUDA device support
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc_mac.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+/* XXX: implement all timer modes */
+
+/* debug CUDA */
+//#define DEBUG_CUDA
+
+/* debug CUDA packets */
+//#define DEBUG_CUDA_PACKET
+
+#ifdef DEBUG_CUDA
+#define CUDA_DPRINTF(fmt, ...) \
+ do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define CUDA_DPRINTF(fmt, ...)
+#endif
+
+/* Bits in B data register: all active low */
+#define TREQ 0x08 /* Transfer request (input) */
+#define TACK 0x10 /* Transfer acknowledge (output) */
+#define TIP 0x20 /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL 0x1c /* Shift register control bits */
+#define SR_EXT 0x0c /* Shift on external clock */
+#define SR_OUT 0x10 /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET 0x80 /* set bits in IER */
+#define IER_CLR 0 /* clear bits in IER */
+#define SR_INT 0x04 /* Shift register full/empty */
+#define T1_INT 0x40 /* Timer 1 interrupt */
+#define T2_INT 0x20 /* Timer 2 interrupt */
+
+/* Bits in ACR */
+#define T1MODE 0xc0 /* Timer 1 mode */
+#define T1MODE_CONT 0x40 /* continuous interrupts */
+
+/* commands (1st byte) */
+#define ADB_PACKET 0
+#define CUDA_PACKET 1
+#define ERROR_PACKET 2
+#define TIMER_PACKET 3
+#define POWER_PACKET 4
+#define MACIIC_PACKET 5
+#define PMU_PACKET 6
+
+
+/* CUDA commands (2nd byte) */
+#define CUDA_WARM_START 0x0
+#define CUDA_AUTOPOLL 0x1
+#define CUDA_GET_6805_ADDR 0x2
+#define CUDA_GET_TIME 0x3
+#define CUDA_GET_PRAM 0x7
+#define CUDA_SET_6805_ADDR 0x8
+#define CUDA_SET_TIME 0x9
+#define CUDA_POWERDOWN 0xa
+#define CUDA_POWERUP_TIME 0xb
+#define CUDA_SET_PRAM 0xc
+#define CUDA_MS_RESET 0xd
+#define CUDA_SEND_DFAC 0xe
+#define CUDA_BATTERY_SWAP_SENSE 0x10
+#define CUDA_RESET_SYSTEM 0x11
+#define CUDA_SET_IPL 0x12
+#define CUDA_FILE_SERVER_FLAG 0x13
+#define CUDA_SET_AUTO_RATE 0x14
+#define CUDA_GET_AUTO_RATE 0x16
+#define CUDA_SET_DEVICE_LIST 0x19
+#define CUDA_GET_DEVICE_LIST 0x1a
+#define CUDA_SET_ONE_SECOND_MODE 0x1b
+#define CUDA_SET_POWER_MESSAGES 0x21
+#define CUDA_GET_SET_IIC 0x22
+#define CUDA_WAKEUP 0x23
+#define CUDA_TIMER_TICKLE 0x24
+#define CUDA_COMBINED_FORMAT_IIC 0x25
+
+#define CUDA_TIMER_FREQ (4700000 / 6)
+#define CUDA_ADB_POLL_FREQ 50
+
+/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
+#define RTC_OFFSET 2082844800
+
+typedef struct CUDATimer {
+ int index;
+ uint16_t latch;
+ uint16_t counter_value; /* counter value at load time */
+ int64_t load_time;
+ int64_t next_irq_time;
+ QEMUTimer *timer;
+} CUDATimer;
+
+typedef struct CUDAState {
+ /* cuda registers */
+ uint8_t b; /* B-side data */
+ uint8_t a; /* A-side data */
+ uint8_t dirb; /* B-side direction (1=output) */
+ uint8_t dira; /* A-side direction (1=output) */
+ uint8_t sr; /* Shift register */
+ uint8_t acr; /* Auxiliary control register */
+ uint8_t pcr; /* Peripheral control register */
+ uint8_t ifr; /* Interrupt flag register */
+ uint8_t ier; /* Interrupt enable register */
+ uint8_t anh; /* A-side data, no handshake */
+
+ CUDATimer timers[2];
+
+ uint32_t tick_offset;
+
+ uint8_t last_b; /* last value of B register */
+ uint8_t last_acr; /* last value of B register */
+
+ int data_in_size;
+ int data_in_index;
+ int data_out_index;
+
+ qemu_irq irq;
+ uint8_t autopoll;
+ uint8_t data_in[128];
+ uint8_t data_out[16];
+ QEMUTimer *adb_poll_timer;
+} CUDAState;
+
+static CUDAState cuda_state;
+ADBBusState adb_bus;
+
+static void cuda_update(CUDAState *s);
+static void cuda_receive_packet_from_host(CUDAState *s,
+ const uint8_t *data, int len);
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+ int64_t current_time);
+
+static void cuda_update_irq(CUDAState *s)
+{
+ if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static unsigned int get_counter(CUDATimer *s)
+{
+ int64_t d;
+ unsigned int counter;
+
+ d = muldiv64(qemu_get_clock(vm_clock) - s->load_time,
+ CUDA_TIMER_FREQ, get_ticks_per_sec());
+ if (s->index == 0) {
+ /* the timer goes down from latch to -1 (period of latch + 2) */
+ if (d <= (s->counter_value + 1)) {
+ counter = (s->counter_value - d) & 0xffff;
+ } else {
+ counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+ counter = (s->latch - counter) & 0xffff;
+ }
+ } else {
+ counter = (s->counter_value - d) & 0xffff;
+ }
+ return counter;
+}
+
+static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
+{
+ CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val);
+ ti->load_time = qemu_get_clock(vm_clock);
+ ti->counter_value = val;
+ cuda_timer_update(s, ti, ti->load_time);
+}
+
+static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
+{
+ int64_t d, next_time;
+ unsigned int counter;
+
+ /* current counter value */
+ d = muldiv64(current_time - s->load_time,
+ CUDA_TIMER_FREQ, get_ticks_per_sec());
+ /* the timer goes down from latch to -1 (period of latch + 2) */
+ if (d <= (s->counter_value + 1)) {
+ counter = (s->counter_value - d) & 0xffff;
+ } else {
+ counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+ counter = (s->latch - counter) & 0xffff;
+ }
+
+ /* Note: we consider the irq is raised on 0 */
+ if (counter == 0xffff) {
+ next_time = d + s->latch + 1;
+ } else if (counter == 0) {
+ next_time = d + s->latch + 2;
+ } else {
+ next_time = d + counter;
+ }
+ CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
+ s->latch, d, next_time - d);
+ next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) +
+ s->load_time;
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+ int64_t current_time)
+{
+ if (!ti->timer)
+ return;
+ if ((s->acr & T1MODE) != T1MODE_CONT) {
+ qemu_del_timer(ti->timer);
+ } else {
+ ti->next_irq_time = get_next_irq_time(ti, current_time);
+ qemu_mod_timer(ti->timer, ti->next_irq_time);
+ }
+}
+
+static void cuda_timer1(void *opaque)
+{
+ CUDAState *s = opaque;
+ CUDATimer *ti = &s->timers[0];
+
+ cuda_timer_update(s, ti, ti->next_irq_time);
+ s->ifr |= T1_INT;
+ cuda_update_irq(s);
+}
+
+static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr)
+{
+ CUDAState *s = opaque;
+ uint32_t val;
+
+ addr = (addr >> 9) & 0xf;
+ switch(addr) {
+ case 0:
+ val = s->b;
+ break;
+ case 1:
+ val = s->a;
+ break;
+ case 2:
+ val = s->dirb;
+ break;
+ case 3:
+ val = s->dira;
+ break;
+ case 4:
+ val = get_counter(&s->timers[0]) & 0xff;
+ s->ifr &= ~T1_INT;
+ cuda_update_irq(s);
+ break;
+ case 5:
+ val = get_counter(&s->timers[0]) >> 8;
+ cuda_update_irq(s);
+ break;
+ case 6:
+ val = s->timers[0].latch & 0xff;
+ break;
+ case 7:
+ /* XXX: check this */
+ val = (s->timers[0].latch >> 8) & 0xff;
+ break;
+ case 8:
+ val = get_counter(&s->timers[1]) & 0xff;
+ s->ifr &= ~T2_INT;
+ break;
+ case 9:
+ val = get_counter(&s->timers[1]) >> 8;
+ break;
+ case 10:
+ val = s->sr;
+ s->ifr &= ~SR_INT;
+ cuda_update_irq(s);
+ break;
+ case 11:
+ val = s->acr;
+ break;
+ case 12:
+ val = s->pcr;
+ break;
+ case 13:
+ val = s->ifr;
+ if (s->ifr & s->ier)
+ val |= 0x80;
+ break;
+ case 14:
+ val = s->ier | 0x80;
+ break;
+ default:
+ case 15:
+ val = s->anh;
+ break;
+ }
+ if (addr != 13 || val != 0) {
+ CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val);
+ }
+
+ return val;
+}
+
+static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ CUDAState *s = opaque;
+
+ addr = (addr >> 9) & 0xf;
+ CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val);
+
+ switch(addr) {
+ case 0:
+ s->b = val;
+ cuda_update(s);
+ break;
+ case 1:
+ s->a = val;
+ break;
+ case 2:
+ s->dirb = val;
+ break;
+ case 3:
+ s->dira = val;
+ break;
+ case 4:
+ s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ break;
+ case 5:
+ s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+ s->ifr &= ~T1_INT;
+ set_counter(s, &s->timers[0], s->timers[0].latch);
+ break;
+ case 6:
+ s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ break;
+ case 7:
+ s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+ s->ifr &= ~T1_INT;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ break;
+ case 8:
+ s->timers[1].latch = val;
+ set_counter(s, &s->timers[1], val);
+ break;
+ case 9:
+ set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
+ break;
+ case 10:
+ s->sr = val;
+ break;
+ case 11:
+ s->acr = val;
+ cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
+ cuda_update(s);
+ break;
+ case 12:
+ s->pcr = val;
+ break;
+ case 13:
+ /* reset bits */
+ s->ifr &= ~val;
+ cuda_update_irq(s);
+ break;
+ case 14:
+ if (val & IER_SET) {
+ /* set bits */
+ s->ier |= val & 0x7f;
+ } else {
+ /* reset bits */
+ s->ier &= ~val;
+ }
+ cuda_update_irq(s);
+ break;
+ default:
+ case 15:
+ s->anh = val;
+ break;
+ }
+}
+
+/* NOTE: TIP and TREQ are negated */
+static void cuda_update(CUDAState *s)
+{
+ int packet_received, len;
+
+ packet_received = 0;
+ if (!(s->b & TIP)) {
+ /* transfer requested from host */
+
+ if (s->acr & SR_OUT) {
+ /* data output */
+ if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+ if (s->data_out_index < sizeof(s->data_out)) {
+ CUDA_DPRINTF("send: %02x\n", s->sr);
+ s->data_out[s->data_out_index++] = s->sr;
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ }
+ } else {
+ if (s->data_in_index < s->data_in_size) {
+ /* data input */
+ if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+ s->sr = s->data_in[s->data_in_index++];
+ CUDA_DPRINTF("recv: %02x\n", s->sr);
+ /* indicate end of transfer */
+ if (s->data_in_index >= s->data_in_size) {
+ s->b = (s->b | TREQ);
+ }
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ }
+ }
+ } else {
+ /* no transfer requested: handle sync case */
+ if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) {
+ /* update TREQ state each time TACK change state */
+ if (s->b & TACK)
+ s->b = (s->b | TREQ);
+ else
+ s->b = (s->b & ~TREQ);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ } else {
+ if (!(s->last_b & TIP)) {
+ /* handle end of host to cuda transfer */
+ packet_received = (s->data_out_index > 0);
+ /* always an IRQ at the end of transfer */
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+ }
+ /* signal if there is data to read */
+ if (s->data_in_index < s->data_in_size) {
+ s->b = (s->b & ~TREQ);
+ }
+ }
+ }
+
+ s->last_acr = s->acr;
+ s->last_b = s->b;
+
+ /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
+ recursively */
+ if (packet_received) {
+ len = s->data_out_index;
+ s->data_out_index = 0;
+ cuda_receive_packet_from_host(s, s->data_out, len);
+ }
+}
+
+static void cuda_send_packet_to_host(CUDAState *s,
+ const uint8_t *data, int len)
+{
+#ifdef DEBUG_CUDA_PACKET
+ {
+ int i;
+ printf("cuda_send_packet_to_host:\n");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+#endif
+ memcpy(s->data_in, data, len);
+ s->data_in_size = len;
+ s->data_in_index = 0;
+ cuda_update(s);
+ s->ifr |= SR_INT;
+ cuda_update_irq(s);
+}
+
+static void cuda_adb_poll(void *opaque)
+{
+ CUDAState *s = opaque;
+ uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ int olen;
+
+ olen = adb_poll(&adb_bus, obuf + 2);
+ if (olen > 0) {
+ obuf[0] = ADB_PACKET;
+ obuf[1] = 0x40; /* polled data */
+ cuda_send_packet_to_host(s, obuf, olen + 2);
+ }
+ qemu_mod_timer(s->adb_poll_timer,
+ qemu_get_clock(vm_clock) +
+ (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+}
+
+static void cuda_receive_packet(CUDAState *s,
+ const uint8_t *data, int len)
+{
+ uint8_t obuf[16];
+ int autopoll;
+ uint32_t ti;
+
+ switch(data[0]) {
+ case CUDA_AUTOPOLL:
+ autopoll = (data[1] != 0);
+ if (autopoll != s->autopoll) {
+ s->autopoll = autopoll;
+ if (autopoll) {
+ qemu_mod_timer(s->adb_poll_timer,
+ qemu_get_clock(vm_clock) +
+ (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+ } else {
+ qemu_del_timer(s->adb_poll_timer);
+ }
+ }
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = data[1];
+ cuda_send_packet_to_host(s, obuf, 2);
+ break;
+ case CUDA_SET_TIME:
+ ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4];
+ s->tick_offset = ti - (qemu_get_clock(vm_clock) / get_ticks_per_sec());
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ obuf[2] = 0;
+ cuda_send_packet_to_host(s, obuf, 3);
+ break;
+ case CUDA_GET_TIME:
+ ti = s->tick_offset + (qemu_get_clock(vm_clock) / get_ticks_per_sec());
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ obuf[2] = 0;
+ obuf[3] = ti >> 24;
+ obuf[4] = ti >> 16;
+ obuf[5] = ti >> 8;
+ obuf[6] = ti;
+ cuda_send_packet_to_host(s, obuf, 7);
+ break;
+ case CUDA_FILE_SERVER_FLAG:
+ case CUDA_SET_DEVICE_LIST:
+ case CUDA_SET_AUTO_RATE:
+ case CUDA_SET_POWER_MESSAGES:
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ cuda_send_packet_to_host(s, obuf, 2);
+ break;
+ case CUDA_POWERDOWN:
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ cuda_send_packet_to_host(s, obuf, 2);
+ qemu_system_shutdown_request();
+ break;
+ case CUDA_RESET_SYSTEM:
+ obuf[0] = CUDA_PACKET;
+ obuf[1] = 0;
+ cuda_send_packet_to_host(s, obuf, 2);
+ qemu_system_reset_request();
+ break;
+ default:
+ break;
+ }
+}
+
+static void cuda_receive_packet_from_host(CUDAState *s,
+ const uint8_t *data, int len)
+{
+#ifdef DEBUG_CUDA_PACKET
+ {
+ int i;
+ printf("cuda_receive_packet_from_host:\n");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+ }
+#endif
+ switch(data[0]) {
+ case ADB_PACKET:
+ {
+ uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+ int olen;
+ olen = adb_request(&adb_bus, obuf + 2, data + 1, len - 1);
+ if (olen > 0) {
+ obuf[0] = ADB_PACKET;
+ obuf[1] = 0x00;
+ } else {
+ /* error */
+ obuf[0] = ADB_PACKET;
+ obuf[1] = -olen;
+ olen = 0;
+ }
+ cuda_send_packet_to_host(s, obuf, olen + 2);
+ }
+ break;
+ case CUDA_PACKET:
+ cuda_receive_packet(s, data + 1, len - 1);
+ break;
+ }
+}
+
+static void cuda_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static void cuda_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+}
+
+static uint32_t cuda_readw (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static uint32_t cuda_readl (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static CPUWriteMemoryFunc * const cuda_write[] = {
+ &cuda_writeb,
+ &cuda_writew,
+ &cuda_writel,
+};
+
+static CPUReadMemoryFunc * const cuda_read[] = {
+ &cuda_readb,
+ &cuda_readw,
+ &cuda_readl,
+};
+
+static void cuda_save_timer(QEMUFile *f, CUDATimer *s)
+{
+ qemu_put_be16s(f, &s->latch);
+ qemu_put_be16s(f, &s->counter_value);
+ qemu_put_sbe64s(f, &s->load_time);
+ qemu_put_sbe64s(f, &s->next_irq_time);
+ if (s->timer)
+ qemu_put_timer(f, s->timer);
+}
+
+static void cuda_save(QEMUFile *f, void *opaque)
+{
+ CUDAState *s = (CUDAState *)opaque;
+
+ qemu_put_ubyte(f, s->b);
+ qemu_put_ubyte(f, s->a);
+ qemu_put_ubyte(f, s->dirb);
+ qemu_put_ubyte(f, s->dira);
+ qemu_put_ubyte(f, s->sr);
+ qemu_put_ubyte(f, s->acr);
+ qemu_put_ubyte(f, s->pcr);
+ qemu_put_ubyte(f, s->ifr);
+ qemu_put_ubyte(f, s->ier);
+ qemu_put_ubyte(f, s->anh);
+ qemu_put_sbe32s(f, &s->data_in_size);
+ qemu_put_sbe32s(f, &s->data_in_index);
+ qemu_put_sbe32s(f, &s->data_out_index);
+ qemu_put_ubyte(f, s->autopoll);
+ qemu_put_buffer(f, s->data_in, sizeof(s->data_in));
+ qemu_put_buffer(f, s->data_out, sizeof(s->data_out));
+ qemu_put_be32s(f, &s->tick_offset);
+ cuda_save_timer(f, &s->timers[0]);
+ cuda_save_timer(f, &s->timers[1]);
+}
+
+static void cuda_load_timer(QEMUFile *f, CUDATimer *s)
+{
+ qemu_get_be16s(f, &s->latch);
+ qemu_get_be16s(f, &s->counter_value);
+ qemu_get_sbe64s(f, &s->load_time);
+ qemu_get_sbe64s(f, &s->next_irq_time);
+ if (s->timer)
+ qemu_get_timer(f, s->timer);
+}
+
+static int cuda_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CUDAState *s = (CUDAState *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->b = qemu_get_ubyte(f);
+ s->a = qemu_get_ubyte(f);
+ s->dirb = qemu_get_ubyte(f);
+ s->dira = qemu_get_ubyte(f);
+ s->sr = qemu_get_ubyte(f);
+ s->acr = qemu_get_ubyte(f);
+ s->pcr = qemu_get_ubyte(f);
+ s->ifr = qemu_get_ubyte(f);
+ s->ier = qemu_get_ubyte(f);
+ s->anh = qemu_get_ubyte(f);
+ qemu_get_sbe32s(f, &s->data_in_size);
+ qemu_get_sbe32s(f, &s->data_in_index);
+ qemu_get_sbe32s(f, &s->data_out_index);
+ s->autopoll = qemu_get_ubyte(f);
+ qemu_get_buffer(f, s->data_in, sizeof(s->data_in));
+ qemu_get_buffer(f, s->data_out, sizeof(s->data_out));
+ qemu_get_be32s(f, &s->tick_offset);
+ cuda_load_timer(f, &s->timers[0]);
+ cuda_load_timer(f, &s->timers[1]);
+
+ return 0;
+}
+
+static void cuda_reset(void *opaque)
+{
+ CUDAState *s = opaque;
+
+ s->b = 0;
+ s->a = 0;
+ s->dirb = 0;
+ s->dira = 0;
+ s->sr = 0;
+ s->acr = 0;
+ s->pcr = 0;
+ s->ifr = 0;
+ s->ier = 0;
+ // s->ier = T1_INT | SR_INT;
+ s->anh = 0;
+ s->data_in_size = 0;
+ s->data_in_index = 0;
+ s->data_out_index = 0;
+ s->autopoll = 0;
+
+ s->timers[0].latch = 0xffff;
+ set_counter(s, &s->timers[0], 0xffff);
+
+ s->timers[1].latch = 0;
+ set_counter(s, &s->timers[1], 0xffff);
+}
+
+void cuda_init (int *cuda_mem_index, qemu_irq irq)
+{
+ struct tm tm;
+ CUDAState *s = &cuda_state;
+
+ s->irq = irq;
+
+ s->timers[0].index = 0;
+ s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s);
+
+ s->timers[1].index = 1;
+
+ qemu_get_timedate(&tm, 0);
+ s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
+
+ s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s);
+ *cuda_mem_index = cpu_register_io_memory(cuda_read, cuda_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ register_savevm(NULL, "cuda", -1, 1, cuda_save, cuda_load, s);
+ qemu_register_reset(cuda_reset, s);
+}
diff --git a/hw/debugcon.c b/hw/debugcon.c
new file mode 100644
index 0000000..5ee6821
--- /dev/null
+++ b/hw/debugcon.c
@@ -0,0 +1,107 @@
+/*
+ * QEMU Bochs-style debug console ("port E9") emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ * Copyright (c) Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "qemu-char.h"
+#include "isa.h"
+#include "pc.h"
+
+//#define DEBUG_DEBUGCON
+
+typedef struct DebugconState {
+ CharDriverState *chr;
+ uint32_t readback;
+} DebugconState;
+
+typedef struct ISADebugconState {
+ ISADevice dev;
+ uint32_t iobase;
+ DebugconState state;
+} ISADebugconState;
+
+static void debugcon_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ DebugconState *s = opaque;
+ unsigned char ch = val;
+
+#ifdef DEBUG_DEBUGCON
+ printf("debugcon: write addr=0x%04x val=0x%02x\n", addr, val);
+#endif
+
+ qemu_chr_write(s->chr, &ch, 1);
+}
+
+
+static uint32_t debugcon_ioport_read(void *opaque, uint32_t addr)
+{
+ DebugconState *s = opaque;
+
+#ifdef DEBUG_DEBUGCON
+ printf("debugcon: read addr=0x%04x\n", addr);
+#endif
+
+ return s->readback;
+}
+
+static void debugcon_init_core(DebugconState *s)
+{
+ if (!s->chr) {
+ fprintf(stderr, "Can't create debugcon device, empty char device\n");
+ exit(1);
+ }
+
+ qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s);
+}
+
+static int debugcon_isa_initfn(ISADevice *dev)
+{
+ ISADebugconState *isa = DO_UPCAST(ISADebugconState, dev, dev);
+ DebugconState *s = &isa->state;
+
+ debugcon_init_core(s);
+ register_ioport_write(isa->iobase, 1, 1, debugcon_ioport_write, s);
+ register_ioport_read(isa->iobase, 1, 1, debugcon_ioport_read, s);
+ return 0;
+}
+
+static ISADeviceInfo debugcon_isa_info = {
+ .qdev.name = "isa-debugcon",
+ .qdev.size = sizeof(ISADebugconState),
+ .init = debugcon_isa_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("iobase", ISADebugconState, iobase, 0xe9),
+ DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr),
+ DEFINE_PROP_HEX32("readback", ISADebugconState, state.readback, 0xe9),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void debugcon_register_devices(void)
+{
+ isa_qdev_register(&debugcon_isa_info);
+}
+
+device_init(debugcon_register_devices)
diff --git a/hw/dec_pci.c b/hw/dec_pci.c
new file mode 100644
index 0000000..bf88f2a
--- /dev/null
+++ b/hw/dec_pci.c
@@ -0,0 +1,133 @@
+/*
+ * QEMU DEC 21154 PCI bridge
+ *
+ * Copyright (c) 2006-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "dec_pci.h"
+#include "sysbus.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "pci_bridge.h"
+#include "pci_internals.h"
+
+/* debug DEC */
+//#define DEBUG_DEC
+
+#ifdef DEBUG_DEC
+#define DEC_DPRINTF(fmt, ...) \
+ do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DEC_DPRINTF(fmt, ...)
+#endif
+
+typedef struct DECState {
+ SysBusDevice busdev;
+ PCIHostState host_state;
+} DECState;
+
+static int dec_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return irq_num;
+}
+
+static int dec_21154_initfn(PCIDevice *dev)
+{
+ int rc;
+
+ rc = pci_bridge_initfn(dev);
+ if (rc < 0) {
+ return rc;
+ }
+
+ pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_DEC);
+ pci_config_set_device_id(dev->config, PCI_DEVICE_ID_DEC_21154);
+ return 0;
+}
+
+static PCIDeviceInfo dec_21154_pci_bridge_info = {
+ .qdev.name = "dec-21154-p2p-bridge",
+ .qdev.desc = "DEC 21154 PCI-PCI bridge",
+ .qdev.size = sizeof(PCIBridge),
+ .qdev.vmsd = &vmstate_pci_device,
+ .qdev.reset = pci_bridge_reset,
+ .init = dec_21154_initfn,
+ .exit = pci_bridge_exitfn,
+ .config_write = pci_bridge_write_config,
+ .is_bridge = 1,
+};
+
+PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn)
+{
+ PCIDevice *dev;
+ PCIBridge *br;
+
+ dev = pci_create_multifunction(parent_bus, devfn, false,
+ "dec-21154-p2p-bridge");
+ br = DO_UPCAST(PCIBridge, dev, dev);
+ pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq);
+ qdev_init_nofail(&dev->qdev);
+ return pci_bridge_get_sec_bus(br);
+}
+
+static int pci_dec_21154_init_device(SysBusDevice *dev)
+{
+ DECState *s;
+ int pci_mem_config, pci_mem_data;
+
+ s = FROM_SYSBUS(DECState, dev);
+
+ pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ pci_mem_data = pci_host_data_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+ return 0;
+}
+
+static int dec_21154_pci_host_init(PCIDevice *d)
+{
+ /* PCI2PCI bridge same values as PearPC - check this */
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_DEC);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_DEC_21154);
+ pci_set_byte(d->config + PCI_REVISION_ID, 0x02);
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
+ return 0;
+}
+
+static PCIDeviceInfo dec_21154_pci_host_info = {
+ .qdev.name = "dec-21154",
+ .qdev.size = sizeof(PCIDevice),
+ .init = dec_21154_pci_host_init,
+ .is_bridge = 1,
+};
+
+static void dec_register_devices(void)
+{
+ sysbus_register_dev("dec-21154", sizeof(DECState),
+ pci_dec_21154_init_device);
+ pci_qdev_register(&dec_21154_pci_host_info);
+ pci_qdev_register(&dec_21154_pci_bridge_info);
+}
+
+device_init(dec_register_devices)
diff --git a/hw/dec_pci.h b/hw/dec_pci.h
new file mode 100644
index 0000000..79264ba
--- /dev/null
+++ b/hw/dec_pci.h
@@ -0,0 +1,8 @@
+#ifndef DEC_PCI_H
+#define DEC_PCI_H
+
+#include "qemu-common.h"
+
+PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn);
+
+#endif
diff --git a/hw/device-assignment.c b/hw/device-assignment.c
new file mode 100644
index 0000000..e5205cf
--- /dev/null
+++ b/hw/device-assignment.c
@@ -0,0 +1,1926 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * Assign a PCI device from the host to a guest VM.
+ *
+ * Adapted for KVM by Qumranet.
+ *
+ * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com)
+ * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com)
+ * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com)
+ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
+ * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "qemu-kvm.h"
+#include "hw.h"
+#include "pc.h"
+#include "qemu-error.h"
+#include "console.h"
+#include "device-assignment.h"
+#include "loader.h"
+#include "monitor.h"
+#include "range.h"
+#include <pci/header.h>
+#include "sysemu.h"
+
+/* From linux/ioport.h */
+#define IORESOURCE_IO 0x00000100 /* Resource type */
+#define IORESOURCE_MEM 0x00000200
+#define IORESOURCE_IRQ 0x00000400
+#define IORESOURCE_DMA 0x00000800
+#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */
+
+/* #define DEVICE_ASSIGNMENT_DEBUG 1 */
+
+#ifdef DEVICE_ASSIGNMENT_DEBUG
+#define DEBUG(fmt, ...) \
+ do { \
+ fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
+ } while (0)
+#else
+#define DEBUG(fmt, ...) do { } while(0)
+#endif
+
+static void assigned_dev_load_option_rom(AssignedDevice *dev);
+
+static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev);
+
+static void assigned_device_pci_cap_write_config(PCIDevice *pci_dev,
+ uint32_t address,
+ uint32_t val, int len);
+
+static uint32_t assigned_device_pci_cap_read_config(PCIDevice *pci_dev,
+ uint32_t address, int len);
+
+static uint32_t assigned_dev_ioport_rw(AssignedDevRegion *dev_region,
+ uint32_t addr, int len, uint32_t *val)
+{
+ uint32_t ret = 0;
+ uint32_t offset = addr - dev_region->e_physbase;
+ int fd = dev_region->region->resource_fd;
+
+ if (fd >= 0) {
+ if (val) {
+ DEBUG("pwrite val=%x, len=%d, e_phys=%x, offset=%x\n",
+ *val, len, addr, offset);
+ if (pwrite(fd, val, len, offset) != len) {
+ fprintf(stderr, "%s - pwrite failed %s\n",
+ __func__, strerror(errno));
+ }
+ } else {
+ if (pread(fd, &ret, len, offset) != len) {
+ fprintf(stderr, "%s - pread failed %s\n",
+ __func__, strerror(errno));
+ ret = (1UL << (len * 8)) - 1;
+ }
+ DEBUG("pread ret=%x, len=%d, e_phys=%x, offset=%x\n",
+ ret, len, addr, offset);
+ }
+ } else {
+ uint32_t port = offset + dev_region->u.r_baseport;
+
+ if (val) {
+ DEBUG("out val=%x, len=%d, e_phys=%x, host=%x\n",
+ *val, len, addr, port);
+ switch (len) {
+ case 1:
+ outb(*val, port);
+ break;
+ case 2:
+ outw(*val, port);
+ break;
+ case 4:
+ outl(*val, port);
+ break;
+ }
+ } else {
+ switch (len) {
+ case 1:
+ ret = inb(port);
+ break;
+ case 2:
+ ret = inw(port);
+ break;
+ case 4:
+ ret = inl(port);
+ break;
+ }
+ DEBUG("in val=%x, len=%d, e_phys=%x, host=%x\n",
+ ret, len, addr, port);
+ }
+ }
+ return ret;
+}
+
+static void assigned_dev_ioport_writeb(void *opaque, uint32_t addr,
+ uint32_t value)
+{
+ assigned_dev_ioport_rw(opaque, addr, 1, &value);
+ return;
+}
+
+static void assigned_dev_ioport_writew(void *opaque, uint32_t addr,
+ uint32_t value)
+{
+ assigned_dev_ioport_rw(opaque, addr, 2, &value);
+ return;
+}
+
+static void assigned_dev_ioport_writel(void *opaque, uint32_t addr,
+ uint32_t value)
+{
+ assigned_dev_ioport_rw(opaque, addr, 4, &value);
+ return;
+}
+
+static uint32_t assigned_dev_ioport_readb(void *opaque, uint32_t addr)
+{
+ return assigned_dev_ioport_rw(opaque, addr, 1, NULL);
+}
+
+static uint32_t assigned_dev_ioport_readw(void *opaque, uint32_t addr)
+{
+ return assigned_dev_ioport_rw(opaque, addr, 2, NULL);
+}
+
+static uint32_t assigned_dev_ioport_readl(void *opaque, uint32_t addr)
+{
+ return assigned_dev_ioport_rw(opaque, addr, 4, NULL);
+}
+
+static uint32_t slow_bar_readb(void *opaque, target_phys_addr_t addr)
+{
+ AssignedDevRegion *d = opaque;
+ uint8_t *in = d->u.r_virtbase + addr;
+ uint32_t r;
+
+ r = *in;
+ DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r);
+
+ return r;
+}
+
+static uint32_t slow_bar_readw(void *opaque, target_phys_addr_t addr)
+{
+ AssignedDevRegion *d = opaque;
+ uint16_t *in = d->u.r_virtbase + addr;
+ uint32_t r;
+
+ r = *in;
+ DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r);
+
+ return r;
+}
+
+static uint32_t slow_bar_readl(void *opaque, target_phys_addr_t addr)
+{
+ AssignedDevRegion *d = opaque;
+ uint32_t *in = d->u.r_virtbase + addr;
+ uint32_t r;
+
+ r = *in;
+ DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r);
+
+ return r;
+}
+
+static void slow_bar_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ AssignedDevRegion *d = opaque;
+ uint8_t *out = d->u.r_virtbase + addr;
+
+ DEBUG("slow_bar_writeb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+ *out = val;
+}
+
+static void slow_bar_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ AssignedDevRegion *d = opaque;
+ uint16_t *out = d->u.r_virtbase + addr;
+
+ DEBUG("slow_bar_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, val);
+ *out = val;
+}
+
+static void slow_bar_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ AssignedDevRegion *d = opaque;
+ uint32_t *out = d->u.r_virtbase + addr;
+
+ DEBUG("slow_bar_writel addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, val);
+ *out = val;
+}
+
+static CPUWriteMemoryFunc * const slow_bar_write[] = {
+ &slow_bar_writeb,
+ &slow_bar_writew,
+ &slow_bar_writel
+};
+
+static CPUReadMemoryFunc * const slow_bar_read[] = {
+ &slow_bar_readb,
+ &slow_bar_readw,
+ &slow_bar_readl
+};
+
+static void assigned_dev_iomem_map_slow(PCIDevice *pci_dev, int region_num,
+ pcibus_t e_phys, pcibus_t e_size,
+ int type)
+{
+ AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev);
+ AssignedDevRegion *region = &r_dev->v_addrs[region_num];
+ PCIRegion *real_region = &r_dev->real_device.regions[region_num];
+ int m;
+
+ DEBUG("%s", "slow map\n");
+ m = cpu_register_io_memory(slow_bar_read, slow_bar_write, region,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(e_phys, e_size, m);
+
+ /* MSI-X MMIO page */
+ if ((e_size > 0) &&
+ real_region->base_addr <= r_dev->msix_table_addr &&
+ real_region->base_addr + real_region->size >= r_dev->msix_table_addr) {
+ int offset = r_dev->msix_table_addr - real_region->base_addr;
+
+ cpu_register_physical_memory(e_phys + offset,
+ TARGET_PAGE_SIZE, r_dev->mmio_index);
+ }
+}
+
+static void assigned_dev_iomem_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t e_phys, pcibus_t e_size, int type)
+{
+ AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev);
+ AssignedDevRegion *region = &r_dev->v_addrs[region_num];
+ PCIRegion *real_region = &r_dev->real_device.regions[region_num];
+ int ret = 0;
+
+ DEBUG("e_phys=%08" FMT_PCIBUS " r_virt=%p type=%d len=%08" FMT_PCIBUS " region_num=%d \n",
+ e_phys, region->u.r_virtbase, type, e_size, region_num);
+
+ region->e_physbase = e_phys;
+ region->e_size = e_size;
+
+ if (e_size > 0) {
+ cpu_register_physical_memory(e_phys, e_size, region->memory_index);
+
+ /* deal with MSI-X MMIO page */
+ if (real_region->base_addr <= r_dev->msix_table_addr &&
+ real_region->base_addr + real_region->size >=
+ r_dev->msix_table_addr) {
+ int offset = r_dev->msix_table_addr - real_region->base_addr;
+
+ cpu_register_physical_memory(e_phys + offset,
+ TARGET_PAGE_SIZE, r_dev->mmio_index);
+ }
+ }
+
+ if (ret != 0) {
+ fprintf(stderr, "%s: Error: create new mapping failed\n", __func__);
+ exit(1);
+ }
+}
+
+static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev);
+ AssignedDevRegion *region = &r_dev->v_addrs[region_num];
+ int first_map = (region->e_size == 0);
+ CPUState *env;
+
+ region->e_physbase = addr;
+ region->e_size = size;
+
+ DEBUG("e_phys=0x%" FMT_PCIBUS " r_baseport=%x type=0x%x len=%" FMT_PCIBUS " region_num=%d \n",
+ addr, region->u.r_baseport, type, size, region_num);
+
+ if (first_map && region->region->resource_fd < 0) {
+ struct ioperm_data *data;
+
+ data = qemu_mallocz(sizeof(struct ioperm_data));
+ if (data == NULL) {
+ fprintf(stderr, "%s: Out of memory\n", __func__);
+ exit(1);
+ }
+
+ data->start_port = region->u.r_baseport;
+ data->num = region->r_size;
+ data->turn_on = 1;
+
+ kvm_add_ioperm_data(data);
+
+ for (env = first_cpu; env; env = env->next_cpu)
+ kvm_ioperm(env, data);
+ }
+
+ register_ioport_read(addr, size, 1, assigned_dev_ioport_readb,
+ (r_dev->v_addrs + region_num));
+ register_ioport_read(addr, size, 2, assigned_dev_ioport_readw,
+ (r_dev->v_addrs + region_num));
+ register_ioport_read(addr, size, 4, assigned_dev_ioport_readl,
+ (r_dev->v_addrs + region_num));
+ register_ioport_write(addr, size, 1, assigned_dev_ioport_writeb,
+ (r_dev->v_addrs + region_num));
+ register_ioport_write(addr, size, 2, assigned_dev_ioport_writew,
+ (r_dev->v_addrs + region_num));
+ register_ioport_write(addr, size, 4, assigned_dev_ioport_writel,
+ (r_dev->v_addrs + region_num));
+}
+
+static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len)
+{
+ AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev);
+ uint32_t val;
+ ssize_t ret;
+ int fd = pci_dev->real_device.config_fd;
+
+again:
+ ret = pread(fd, &val, len, pos);
+ if (ret != len) {
+ if ((ret < 0) && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ fprintf(stderr, "%s: pread failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+
+ return val;
+}
+
+static uint8_t assigned_dev_pci_read_byte(PCIDevice *d, int pos)
+{
+ return (uint8_t)assigned_dev_pci_read(d, pos, 1);
+}
+
+static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len)
+{
+ AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev);
+ ssize_t ret;
+ int fd = pci_dev->real_device.config_fd;
+
+again:
+ ret = pwrite(fd, &val, len, pos);
+ if (ret != len) {
+ if ((ret < 0) && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ fprintf(stderr, "%s: pwrite failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+
+ return;
+}
+
+static uint8_t pci_find_cap_offset(PCIDevice *d, uint8_t cap, uint8_t start)
+{
+ int id;
+ int max_cap = 48;
+ int pos = start ? start : PCI_CAPABILITY_LIST;
+ int status;
+
+ status = assigned_dev_pci_read_byte(d, PCI_STATUS);
+ if ((status & PCI_STATUS_CAP_LIST) == 0)
+ return 0;
+
+ while (max_cap--) {
+ pos = assigned_dev_pci_read_byte(d, pos);
+ if (pos < 0x40)
+ break;
+
+ pos &= ~3;
+ id = assigned_dev_pci_read_byte(d, pos + PCI_CAP_LIST_ID);
+
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+static void assigned_dev_pci_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ int fd;
+ ssize_t ret;
+ AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev);
+
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7),
+ (uint16_t) address, val, len);
+
+ if (address >= PCI_CONFIG_HEADER_SIZE && d->config_map[address]) {
+ return assigned_device_pci_cap_write_config(d, address, val, len);
+ }
+
+ if (address == 0x4) {
+ pci_default_write_config(d, address, val, len);
+ /* Continue to program the card */
+ }
+
+ if ((address >= 0x10 && address <= 0x24) || address == 0x30 ||
+ address == 0x34 || address == 0x3c || address == 0x3d) {
+ /* used for update-mappings (BAR emulation) */
+ pci_default_write_config(d, address, val, len);
+ return;
+ }
+
+ DEBUG("NON BAR (%x.%x): address=%04x val=0x%08x len=%d\n",
+ ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7),
+ (uint16_t) address, val, len);
+
+ fd = pci_dev->real_device.config_fd;
+
+again:
+ ret = pwrite(fd, &val, len, address);
+ if (ret != len) {
+ if ((ret < 0) && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ fprintf(stderr, "%s: pwrite failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+}
+
+static uint32_t assigned_dev_pci_read_config(PCIDevice *d, uint32_t address,
+ int len)
+{
+ uint32_t val = 0;
+ int fd;
+ ssize_t ret;
+ AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev);
+
+ if (address >= PCI_CONFIG_HEADER_SIZE && d->config_map[address]) {
+ val = assigned_device_pci_cap_read_config(d, address, len);
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+ return val;
+ }
+
+ if (address < 0x4 || (pci_dev->need_emulate_cmd && address == 0x4) ||
+ (address >= 0x10 && address <= 0x24) || address == 0x30 ||
+ address == 0x34 || address == 0x3c || address == 0x3d) {
+ val = pci_default_read_config(d, address, len);
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+ return val;
+ }
+
+ /* vga specific, remove later */
+ if (address == 0xFC)
+ goto do_log;
+
+ fd = pci_dev->real_device.config_fd;
+
+again:
+ ret = pread(fd, &val, len, address);
+ if (ret != len) {
+ if ((ret < 0) && (errno == EINTR || errno == EAGAIN))
+ goto again;
+
+ fprintf(stderr, "%s: pread failed, ret = %zd errno = %d\n",
+ __func__, ret, errno);
+
+ exit(1);
+ }
+
+do_log:
+ DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n",
+ (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len);
+
+ if (!pci_dev->cap.available) {
+ /* kill the special capabilities */
+ if (address == 4 && len == 4)
+ val &= ~0x100000;
+ else if (address == 6)
+ val &= ~0x10;
+ }
+
+ return val;
+}
+
+static int assigned_dev_register_regions(PCIRegion *io_regions,
+ unsigned long regions_num,
+ AssignedDevice *pci_dev)
+{
+ uint32_t i;
+ PCIRegion *cur_region = io_regions;
+
+ for (i = 0; i < regions_num; i++, cur_region++) {
+ if (!cur_region->valid)
+ continue;
+ pci_dev->v_addrs[i].num = i;
+
+ /* handle memory io regions */
+ if (cur_region->type & IORESOURCE_MEM) {
+ int slow_map = 0;
+ int t = cur_region->type & IORESOURCE_PREFETCH
+ ? PCI_BASE_ADDRESS_MEM_PREFETCH
+ : PCI_BASE_ADDRESS_SPACE_MEMORY;
+
+ if (cur_region->size & 0xFFF) {
+ fprintf(stderr, "PCI region %d at address 0x%llx "
+ "has size 0x%x, which is not a multiple of 4K. "
+ "You might experience some performance hit "
+ "due to that.\n",
+ i, (unsigned long long)cur_region->base_addr,
+ cur_region->size);
+ slow_map = 1;
+ }
+
+ /* map physical memory */
+ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
+ pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size,
+ PROT_WRITE | PROT_READ,
+ MAP_SHARED,
+ cur_region->resource_fd,
+ (off_t)0);
+
+ if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) {
+ pci_dev->v_addrs[i].u.r_virtbase = NULL;
+ fprintf(stderr, "%s: Error: Couldn't mmap 0x%x!"
+ "\n", __func__,
+ (uint32_t) (cur_region->base_addr));
+ return -1;
+ }
+
+ pci_dev->v_addrs[i].r_size = cur_region->size;
+ pci_dev->v_addrs[i].e_size = 0;
+
+ /* add offset */
+ pci_dev->v_addrs[i].u.r_virtbase +=
+ (cur_region->base_addr & 0xFFF);
+
+
+ if (!slow_map) {
+ void *virtbase = pci_dev->v_addrs[i].u.r_virtbase;
+ char name[32];
+ snprintf(name, sizeof(name), "%s.bar%d",
+ pci_dev->dev.qdev.info->name, i);
+ pci_dev->v_addrs[i].memory_index =
+ qemu_ram_alloc_from_ptr(
+ &pci_dev->dev.qdev,
+ name, cur_region->size,
+ virtbase);
+ } else
+ pci_dev->v_addrs[i].memory_index = 0;
+
+ pci_register_bar((PCIDevice *) pci_dev, i,
+ cur_region->size, t,
+ slow_map ? assigned_dev_iomem_map_slow
+ : assigned_dev_iomem_map);
+ continue;
+ } else {
+ /* handle port io regions */
+ uint32_t val;
+ int ret;
+
+ /* Test kernel support for ioport resource read/write. Old
+ * kernels return EIO. New kernels only allow 1/2/4 byte reads
+ * so should return EINVAL for a 3 byte read */
+ ret = pread(pci_dev->v_addrs[i].region->resource_fd, &val, 3, 0);
+ if (ret == 3) {
+ fprintf(stderr, "I/O port resource supports 3 byte read?!\n");
+ abort();
+ } else if (errno != EINVAL) {
+ fprintf(stderr, "Using raw in/out ioport access (sysfs - %s)\n",
+ strerror(errno));
+ close(pci_dev->v_addrs[i].region->resource_fd);
+ pci_dev->v_addrs[i].region->resource_fd = -1;
+ }
+
+ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr;
+ pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr;
+ pci_dev->v_addrs[i].r_size = cur_region->size;
+ pci_dev->v_addrs[i].e_size = 0;
+
+ pci_register_bar((PCIDevice *) pci_dev, i,
+ cur_region->size, PCI_BASE_ADDRESS_SPACE_IO,
+ assigned_dev_ioport_map);
+
+ /* not relevant for port io */
+ pci_dev->v_addrs[i].memory_index = 0;
+ }
+ }
+
+ /* success */
+ return 0;
+}
+
+static int get_real_id(const char *devpath, const char *idname, uint16_t *val)
+{
+ FILE *f;
+ char name[128];
+ long id;
+
+ snprintf(name, sizeof(name), "%s%s", devpath, idname);
+ f = fopen(name, "r");
+ if (f == NULL) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return -1;
+ }
+ if (fscanf(f, "%li\n", &id) == 1) {
+ *val = id;
+ } else {
+ return -1;
+ }
+ fclose(f);
+
+ return 0;
+}
+
+static int get_real_vendor_id(const char *devpath, uint16_t *val)
+{
+ return get_real_id(devpath, "vendor", val);
+}
+
+static int get_real_device_id(const char *devpath, uint16_t *val)
+{
+ return get_real_id(devpath, "device", val);
+}
+
+static int get_real_device(AssignedDevice *pci_dev, uint16_t r_seg,
+ uint8_t r_bus, uint8_t r_dev, uint8_t r_func)
+{
+ char dir[128], name[128];
+ int fd, r = 0, v;
+ FILE *f;
+ unsigned long long start, end, size, flags;
+ uint16_t id;
+ struct stat statbuf;
+ PCIRegion *rp;
+ PCIDevRegions *dev = &pci_dev->real_device;
+
+ dev->region_number = 0;
+
+ snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%x/",
+ r_seg, r_bus, r_dev, r_func);
+
+ snprintf(name, sizeof(name), "%sconfig", dir);
+
+ if (pci_dev->configfd_name && *pci_dev->configfd_name) {
+ if (qemu_isdigit(pci_dev->configfd_name[0])) {
+ dev->config_fd = strtol(pci_dev->configfd_name, NULL, 0);
+ } else {
+ dev->config_fd = monitor_get_fd(cur_mon, pci_dev->configfd_name);
+ if (dev->config_fd < 0) {
+ fprintf(stderr, "%s: (%s) unkown\n", __func__,
+ pci_dev->configfd_name);
+ return 1;
+ }
+ }
+ } else {
+ dev->config_fd = open(name, O_RDWR);
+
+ if (dev->config_fd == -1) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
+ }
+again:
+ r = read(dev->config_fd, pci_dev->dev.config,
+ pci_config_size(&pci_dev->dev));
+ if (r < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ goto again;
+ fprintf(stderr, "%s: read failed, errno = %d\n", __func__, errno);
+ }
+
+ /* Clear host resource mapping info. If we choose not to register a
+ * BAR, such as might be the case with the option ROM, we can get
+ * confusing, unwritable, residual addresses from the host here. */
+ memset(&pci_dev->dev.config[PCI_BASE_ADDRESS_0], 0, 24);
+ memset(&pci_dev->dev.config[PCI_ROM_ADDRESS], 0, 4);
+
+ snprintf(name, sizeof(name), "%sresource", dir);
+
+ f = fopen(name, "r");
+ if (f == NULL) {
+ fprintf(stderr, "%s: %s: %m\n", __func__, name);
+ return 1;
+ }
+
+ for (r = 0; r < PCI_ROM_SLOT; r++) {
+ if (fscanf(f, "%lli %lli %lli\n", &start, &end, &flags) != 3)
+ break;
+
+ rp = dev->regions + r;
+ rp->valid = 0;
+ rp->resource_fd = -1;
+ size = end - start + 1;
+ flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH;
+ if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0)
+ continue;
+ if (flags & IORESOURCE_MEM) {
+ flags &= ~IORESOURCE_IO;
+ } else {
+ flags &= ~IORESOURCE_PREFETCH;
+ }
+ snprintf(name, sizeof(name), "%sresource%d", dir, r);
+ fd = open(name, O_RDWR);
+ if (fd == -1)
+ continue;
+ rp->resource_fd = fd;
+
+ rp->type = flags;
+ rp->valid = 1;
+ rp->base_addr = start;
+ rp->size = size;
+ pci_dev->v_addrs[r].region = rp;
+ DEBUG("region %d size %d start 0x%llx type %d resource_fd %d\n",
+ r, rp->size, start, rp->type, rp->resource_fd);
+ }
+
+ fclose(f);
+
+ /* read and fill vendor ID */
+ v = get_real_vendor_id(dir, &id);
+ if (v) {
+ return 1;
+ }
+ pci_dev->dev.config[0] = id & 0xff;
+ pci_dev->dev.config[1] = (id & 0xff00) >> 8;
+
+ /* read and fill device ID */
+ v = get_real_device_id(dir, &id);
+ if (v) {
+ return 1;
+ }
+ pci_dev->dev.config[2] = id & 0xff;
+ pci_dev->dev.config[3] = (id & 0xff00) >> 8;
+
+ /* dealing with virtual function device */
+ snprintf(name, sizeof(name), "%sphysfn/", dir);
+ if (!stat(name, &statbuf))
+ pci_dev->need_emulate_cmd = 1;
+ else
+ pci_dev->need_emulate_cmd = 0;
+
+ dev->region_number = r;
+ return 0;
+}
+
+static QLIST_HEAD(, AssignedDevice) devs = QLIST_HEAD_INITIALIZER(devs);
+
+#ifdef KVM_CAP_IRQ_ROUTING
+static void free_dev_irq_entries(AssignedDevice *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->irq_entries_nr; i++)
+ kvm_del_routing_entry(&dev->entry[i]);
+ free(dev->entry);
+ dev->entry = NULL;
+ dev->irq_entries_nr = 0;
+}
+#endif
+
+static void free_assigned_device(AssignedDevice *dev)
+{
+ if (dev) {
+ int i;
+
+ for (i = 0; i < dev->real_device.region_number; i++) {
+ PCIRegion *pci_region = &dev->real_device.regions[i];
+ AssignedDevRegion *region = &dev->v_addrs[i];
+
+ if (!pci_region->valid)
+ continue;
+
+ if (pci_region->type & IORESOURCE_IO) {
+ if (pci_region->resource_fd < 0) {
+ kvm_remove_ioperm_data(region->u.r_baseport,
+ region->r_size);
+ }
+ } else if (pci_region->type & IORESOURCE_MEM) {
+ if (region->u.r_virtbase) {
+ if (region->memory_index) {
+ cpu_register_physical_memory(region->e_physbase,
+ region->e_size,
+ IO_MEM_UNASSIGNED);
+ qemu_ram_unmap(region->memory_index);
+ }
+ if (munmap(region->u.r_virtbase,
+ (pci_region->size + 0xFFF) & 0xFFFFF000))
+ fprintf(stderr,
+ "Failed to unmap assigned device region: %s\n",
+ strerror(errno));
+ }
+ }
+ if (pci_region->resource_fd >= 0) {
+ close(pci_region->resource_fd);
+ }
+ }
+
+ if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX)
+ assigned_dev_unregister_msix_mmio(dev);
+
+ if (dev->real_device.config_fd >= 0) {
+ close(dev->real_device.config_fd);
+ }
+
+#ifdef KVM_CAP_IRQ_ROUTING
+ free_dev_irq_entries(dev);
+#endif
+ }
+}
+
+static uint32_t calc_assigned_dev_id(uint16_t seg, uint8_t bus, uint8_t devfn)
+{
+ return (uint32_t)seg << 16 | (uint32_t)bus << 8 | (uint32_t)devfn;
+}
+
+static void assign_failed_examine(AssignedDevice *dev)
+{
+ char name[PATH_MAX], dir[PATH_MAX], driver[PATH_MAX] = {}, *ns;
+ uint16_t vendor_id, device_id;
+ int r;
+
+ sprintf(dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+
+ sprintf(name, "%sdriver", dir);
+
+ r = readlink(name, driver, sizeof(driver));
+ if ((r <= 0) || r >= sizeof(driver) || !(ns = strrchr(driver, '/'))) {
+ goto fail;
+ }
+
+ ns++;
+
+ if (get_real_vendor_id(dir, &vendor_id) ||
+ get_real_device_id(dir, &device_id)) {
+ goto fail;
+ }
+
+ fprintf(stderr, "*** The driver '%s' is occupying your device "
+ "%04x:%02x:%02x.%x.\n",
+ ns, dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+ fprintf(stderr, "***\n");
+ fprintf(stderr, "*** You can try the following commands to free it:\n");
+ fprintf(stderr, "***\n");
+ fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/"
+ "new_id\n", vendor_id, device_id);
+ fprintf(stderr, "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/"
+ "%s/unbind\n",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func, ns);
+ fprintf(stderr, "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/"
+ "pci-stub/bind\n",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+ fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub"
+ "/remove_id\n", vendor_id, device_id);
+ fprintf(stderr, "***\n");
+
+ return;
+
+fail:
+ fprintf(stderr, "Couldn't find out why.\n");
+}
+
+static int assign_device(AssignedDevice *dev)
+{
+ struct kvm_assigned_pci_dev assigned_dev_data;
+ int r;
+
+#ifdef KVM_CAP_PCI_SEGMENT
+ /* Only pass non-zero PCI segment to capable module */
+ if (!kvm_check_extension(kvm_state, KVM_CAP_PCI_SEGMENT) &&
+ dev->h_segnr) {
+ fprintf(stderr, "Can't assign device inside non-zero PCI segment "
+ "as this KVM module doesn't support it.\n");
+ return -ENODEV;
+ }
+#endif
+
+ memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+ assigned_dev_data.assigned_dev_id =
+ calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn);
+#ifdef KVM_CAP_PCI_SEGMENT
+ assigned_dev_data.segnr = dev->h_segnr;
+#endif
+ assigned_dev_data.busnr = dev->h_busnr;
+ assigned_dev_data.devfn = dev->h_devfn;
+
+#ifdef KVM_CAP_IOMMU
+ /* We always enable the IOMMU unless disabled on the command line */
+ if (dev->features & ASSIGNED_DEVICE_USE_IOMMU_MASK) {
+ if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) {
+ fprintf(stderr, "No IOMMU found. Unable to assign device \"%s\"\n",
+ dev->dev.qdev.id);
+ return -ENODEV;
+ }
+ assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU;
+ }
+#else
+ dev->features &= ~ASSIGNED_DEVICE_USE_IOMMU_MASK;
+#endif
+ if (!(dev->features & ASSIGNED_DEVICE_USE_IOMMU_MASK)) {
+ fprintf(stderr,
+ "WARNING: Assigning a device without IOMMU protection can "
+ "cause host memory corruption if the device issues DMA write "
+ "requests!\n");
+ }
+
+ r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
+ if (r < 0) {
+ fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
+ dev->dev.qdev.id, strerror(-r));
+
+ switch (r) {
+ case -EBUSY:
+ assign_failed_examine(dev);
+ break;
+ default:
+ break;
+ }
+ }
+ return r;
+}
+
+static int assign_irq(AssignedDevice *dev)
+{
+ struct kvm_assigned_irq assigned_irq_data;
+ int irq, r = 0;
+
+ /* Interrupt PIN 0 means don't use INTx */
+ if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0)
+ return 0;
+
+ irq = pci_map_irq(&dev->dev, dev->intpin);
+ irq = piix_get_irq(irq);
+
+#ifdef TARGET_IA64
+ irq = ipf_map_irq(&dev->dev, irq);
+#endif
+
+ if (dev->girq == irq)
+ return r;
+
+ memset(&assigned_irq_data, 0, sizeof(assigned_irq_data));
+ assigned_irq_data.assigned_dev_id =
+ calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn);
+ assigned_irq_data.guest_irq = irq;
+ assigned_irq_data.host_irq = dev->real_device.irq;
+#ifdef KVM_CAP_ASSIGN_DEV_IRQ
+ if (dev->irq_requested_type) {
+ assigned_irq_data.flags = dev->irq_requested_type;
+ r = kvm_deassign_irq(kvm_context, &assigned_irq_data);
+ /* -ENXIO means no assigned irq */
+ if (r && r != -ENXIO)
+ perror("assign_irq: deassign");
+ }
+
+ assigned_irq_data.flags = KVM_DEV_IRQ_GUEST_INTX;
+ if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK &&
+ dev->cap.available & ASSIGNED_DEVICE_CAP_MSI)
+ assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_MSI;
+ else
+ assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_INTX;
+#endif
+
+ r = kvm_assign_irq(kvm_context, &assigned_irq_data);
+ if (r < 0) {
+ fprintf(stderr, "Failed to assign irq for \"%s\": %s\n",
+ dev->dev.qdev.id, strerror(-r));
+ fprintf(stderr, "Perhaps you are assigning a device "
+ "that shares an IRQ with another device?\n");
+ return r;
+ }
+
+ dev->girq = irq;
+ dev->irq_requested_type = assigned_irq_data.flags;
+ return r;
+}
+
+static void deassign_device(AssignedDevice *dev)
+{
+#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
+ struct kvm_assigned_pci_dev assigned_dev_data;
+ int r;
+
+ memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
+ assigned_dev_data.assigned_dev_id =
+ calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn);
+
+ r = kvm_deassign_pci_device(kvm_context, &assigned_dev_data);
+ if (r < 0)
+ fprintf(stderr, "Failed to deassign device \"%s\" : %s\n",
+ dev->dev.qdev.id, strerror(-r));
+#endif
+}
+
+#if 0
+AssignedDevInfo *get_assigned_device(int pcibus, int slot)
+{
+ AssignedDevice *assigned_dev = NULL;
+ AssignedDevInfo *adev = NULL;
+
+ QLIST_FOREACH(adev, &adev_head, next) {
+ assigned_dev = adev->assigned_dev;
+ if (pci_bus_num(assigned_dev->dev.bus) == pcibus &&
+ PCI_SLOT(assigned_dev->dev.devfn) == slot)
+ return adev;
+ }
+
+ return NULL;
+}
+#endif
+
+/* The pci config space got updated. Check if irq numbers have changed
+ * for our devices
+ */
+void assigned_dev_update_irqs(void)
+{
+ AssignedDevice *dev, *next;
+ int r;
+
+ dev = QLIST_FIRST(&devs);
+ while (dev) {
+ next = QLIST_NEXT(dev, next);
+ r = assign_irq(dev);
+ if (r < 0)
+ qdev_unplug(&dev->dev.qdev);
+ dev = next;
+ }
+}
+
+#ifdef KVM_CAP_IRQ_ROUTING
+
+#ifdef KVM_CAP_DEVICE_MSI
+static void assigned_dev_update_msi(PCIDevice *pci_dev, unsigned int ctrl_pos)
+{
+ struct kvm_assigned_irq assigned_irq_data;
+ AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev);
+ uint8_t ctrl_byte = pci_dev->config[ctrl_pos];
+ int r;
+
+ memset(&assigned_irq_data, 0, sizeof assigned_irq_data);
+ assigned_irq_data.assigned_dev_id =
+ calc_assigned_dev_id(assigned_dev->h_segnr, assigned_dev->h_busnr,
+ (uint8_t)assigned_dev->h_devfn);
+
+ /* Some guests gratuitously disable MSI even if they're not using it,
+ * try to catch this by only deassigning irqs if the guest is using
+ * MSI or intends to start. */
+ if ((assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_MSI) ||
+ (ctrl_byte & PCI_MSI_FLAGS_ENABLE)) {
+
+ assigned_irq_data.flags = assigned_dev->irq_requested_type;
+ free_dev_irq_entries(assigned_dev);
+ r = kvm_deassign_irq(kvm_context, &assigned_irq_data);
+ /* -ENXIO means no assigned irq */
+ if (r && r != -ENXIO)
+ perror("assigned_dev_update_msi: deassign irq");
+
+ assigned_dev->irq_requested_type = 0;
+ }
+
+ if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) {
+ int pos = ctrl_pos - PCI_MSI_FLAGS;
+ assigned_dev->entry = calloc(1, sizeof(struct kvm_irq_routing_entry));
+ if (!assigned_dev->entry) {
+ perror("assigned_dev_update_msi: ");
+ return;
+ }
+ assigned_dev->entry->u.msi.address_lo =
+ pci_get_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO);
+ assigned_dev->entry->u.msi.address_hi = 0;
+ assigned_dev->entry->u.msi.data =
+ pci_get_word(pci_dev->config + pos + PCI_MSI_DATA_32);
+ assigned_dev->entry->type = KVM_IRQ_ROUTING_MSI;
+ r = kvm_get_irq_route_gsi();
+ if (r < 0) {
+ perror("assigned_dev_update_msi: kvm_get_irq_route_gsi");
+ return;
+ }
+ assigned_dev->entry->gsi = r;
+
+ kvm_add_routing_entry(assigned_dev->entry);
+ if (kvm_commit_irq_routes() < 0) {
+ perror("assigned_dev_update_msi: kvm_commit_irq_routes");
+ assigned_dev->cap.state &= ~ASSIGNED_DEVICE_MSI_ENABLED;
+ return;
+ }
+ assigned_dev->irq_entries_nr = 1;
+
+ assigned_irq_data.guest_irq = assigned_dev->entry->gsi;
+ assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSI | KVM_DEV_IRQ_GUEST_MSI;
+ if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0)
+ perror("assigned_dev_enable_msi: assign irq");
+
+ assigned_dev->girq = -1;
+ assigned_dev->irq_requested_type = assigned_irq_data.flags;
+ } else {
+ assign_irq(assigned_dev);
+ }
+}
+#endif
+
+#ifdef KVM_CAP_DEVICE_MSIX
+static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev)
+{
+ AssignedDevice *adev = container_of(pci_dev, AssignedDevice, dev);
+ uint16_t entries_nr = 0, entries_max_nr;
+ int pos = 0, i, r = 0;
+ uint32_t msg_addr, msg_upper_addr, msg_data, msg_ctrl;
+ struct kvm_assigned_msix_nr msix_nr;
+ struct kvm_assigned_msix_entry msix_entry;
+ void *va = adev->msix_table_page;
+
+ pos = pci_find_capability(pci_dev, PCI_CAP_ID_MSIX);
+
+ entries_max_nr = *(uint16_t *)(pci_dev->config + pos + 2);
+ entries_max_nr &= PCI_MSIX_TABSIZE;
+ entries_max_nr += 1;
+
+ /* Get the usable entry number for allocating */
+ for (i = 0; i < entries_max_nr; i++) {
+ memcpy(&msg_ctrl, va + i * 16 + 12, 4);
+ memcpy(&msg_data, va + i * 16 + 8, 4);
+ /* Ignore unused entry even it's unmasked */
+ if (msg_data == 0)
+ continue;
+ entries_nr ++;
+ }
+
+ if (entries_nr == 0) {
+ fprintf(stderr, "MSI-X entry number is zero!\n");
+ return -EINVAL;
+ }
+ msix_nr.assigned_dev_id = calc_assigned_dev_id(adev->h_segnr, adev->h_busnr,
+ (uint8_t)adev->h_devfn);
+ msix_nr.entry_nr = entries_nr;
+ r = kvm_assign_set_msix_nr(kvm_context, &msix_nr);
+ if (r != 0) {
+ fprintf(stderr, "fail to set MSI-X entry number for MSIX! %s\n",
+ strerror(-r));
+ return r;
+ }
+
+ free_dev_irq_entries(adev);
+ adev->irq_entries_nr = entries_nr;
+ adev->entry = calloc(entries_nr, sizeof(struct kvm_irq_routing_entry));
+ if (!adev->entry) {
+ perror("assigned_dev_update_msix_mmio: ");
+ return -errno;
+ }
+
+ msix_entry.assigned_dev_id = msix_nr.assigned_dev_id;
+ entries_nr = 0;
+ for (i = 0; i < entries_max_nr; i++) {
+ if (entries_nr >= msix_nr.entry_nr)
+ break;
+ memcpy(&msg_ctrl, va + i * 16 + 12, 4);
+ memcpy(&msg_data, va + i * 16 + 8, 4);
+ if (msg_data == 0)
+ continue;
+
+ memcpy(&msg_addr, va + i * 16, 4);
+ memcpy(&msg_upper_addr, va + i * 16 + 4, 4);
+
+ r = kvm_get_irq_route_gsi();
+ if (r < 0)
+ return r;
+
+ adev->entry[entries_nr].gsi = r;
+ adev->entry[entries_nr].type = KVM_IRQ_ROUTING_MSI;
+ adev->entry[entries_nr].flags = 0;
+ adev->entry[entries_nr].u.msi.address_lo = msg_addr;
+ adev->entry[entries_nr].u.msi.address_hi = msg_upper_addr;
+ adev->entry[entries_nr].u.msi.data = msg_data;
+ DEBUG("MSI-X data 0x%x, MSI-X addr_lo 0x%x\n!", msg_data, msg_addr);
+ kvm_add_routing_entry(&adev->entry[entries_nr]);
+
+ msix_entry.gsi = adev->entry[entries_nr].gsi;
+ msix_entry.entry = i;
+ r = kvm_assign_set_msix_entry(kvm_context, &msix_entry);
+ if (r) {
+ fprintf(stderr, "fail to set MSI-X entry! %s\n", strerror(-r));
+ break;
+ }
+ DEBUG("MSI-X entry gsi 0x%x, entry %d\n!",
+ msix_entry.gsi, msix_entry.entry);
+ entries_nr ++;
+ }
+
+ if (r == 0 && kvm_commit_irq_routes() < 0) {
+ perror("assigned_dev_update_msix_mmio: kvm_commit_irq_routes");
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+static void assigned_dev_update_msix(PCIDevice *pci_dev, unsigned int ctrl_pos)
+{
+ struct kvm_assigned_irq assigned_irq_data;
+ AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev);
+ uint16_t *ctrl_word = (uint16_t *)(pci_dev->config + ctrl_pos);
+ int r;
+
+ memset(&assigned_irq_data, 0, sizeof assigned_irq_data);
+ assigned_irq_data.assigned_dev_id =
+ calc_assigned_dev_id(assigned_dev->h_segnr, assigned_dev->h_busnr,
+ (uint8_t)assigned_dev->h_devfn);
+
+ /* Some guests gratuitously disable MSIX even if they're not using it,
+ * try to catch this by only deassigning irqs if the guest is using
+ * MSIX or intends to start. */
+ if ((assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_MSIX) ||
+ (*ctrl_word & PCI_MSIX_ENABLE)) {
+
+ assigned_irq_data.flags = assigned_dev->irq_requested_type;
+ free_dev_irq_entries(assigned_dev);
+ r = kvm_deassign_irq(kvm_context, &assigned_irq_data);
+ /* -ENXIO means no assigned irq */
+ if (r && r != -ENXIO)
+ perror("assigned_dev_update_msix: deassign irq");
+
+ assigned_dev->irq_requested_type = 0;
+ }
+
+ if (*ctrl_word & PCI_MSIX_ENABLE) {
+ assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSIX |
+ KVM_DEV_IRQ_GUEST_MSIX;
+
+ if (assigned_dev_update_msix_mmio(pci_dev) < 0) {
+ perror("assigned_dev_update_msix_mmio");
+ return;
+ }
+ if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0) {
+ perror("assigned_dev_enable_msix: assign irq");
+ return;
+ }
+ assigned_dev->girq = -1;
+ assigned_dev->irq_requested_type = assigned_irq_data.flags;
+ } else {
+ assign_irq(assigned_dev);
+ }
+}
+#endif
+#endif
+
+/* There can be multiple VNDR capabilities per device, we need to find the
+ * one that starts closet to the given address without going over. */
+static uint8_t find_vndr_start(PCIDevice *pci_dev, uint32_t address)
+{
+ uint8_t cap, pos;
+
+ for (cap = pos = 0;
+ (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos));
+ pos += PCI_CAP_LIST_NEXT) {
+ if (pos <= address) {
+ cap = MAX(pos, cap);
+ }
+ }
+ return cap;
+}
+
+/* Merge the bits set in mask from mval into val. Both val and mval are
+ * at the same addr offset, pos is the starting offset of the mask. */
+static uint32_t merge_bits(uint32_t val, uint32_t mval, uint8_t addr,
+ int len, uint8_t pos, uint32_t mask)
+{
+ if (!ranges_overlap(addr, len, pos, 4)) {
+ return val;
+ }
+
+ if (addr >= pos) {
+ mask >>= (addr - pos) * 8;
+ } else {
+ mask <<= (pos - addr) * 8;
+ }
+ mask &= 0xffffffffU >> (4 - len) * 8;
+
+ val &= ~mask;
+ val |= (mval & mask);
+
+ return val;
+}
+
+static uint32_t assigned_device_pci_cap_read_config(PCIDevice *pci_dev,
+ uint32_t address, int len)
+{
+ uint8_t cap, cap_id = pci_dev->config_map[address];
+ uint32_t val;
+
+ switch (cap_id) {
+
+ case PCI_CAP_ID_VPD:
+ cap = pci_find_capability(pci_dev, cap_id);
+ val = assigned_dev_pci_read(pci_dev, address, len);
+ return merge_bits(val, pci_get_long(pci_dev->config + address),
+ address, len, cap + PCI_CAP_LIST_NEXT, 0xff);
+
+ case PCI_CAP_ID_VNDR:
+ cap = find_vndr_start(pci_dev, address);
+ val = assigned_dev_pci_read(pci_dev, address, len);
+ return merge_bits(val, pci_get_long(pci_dev->config + address),
+ address, len, cap + PCI_CAP_LIST_NEXT, 0xff);
+ }
+
+ return pci_default_read_config(pci_dev, address, len);
+}
+
+static void assigned_device_pci_cap_write_config(PCIDevice *pci_dev,
+ uint32_t address,
+ uint32_t val, int len)
+{
+ uint8_t cap_id = pci_dev->config_map[address];
+
+ pci_default_write_config(pci_dev, address, val, len);
+ switch (cap_id) {
+#ifdef KVM_CAP_IRQ_ROUTING
+ case PCI_CAP_ID_MSI:
+#ifdef KVM_CAP_DEVICE_MSI
+ {
+ uint8_t cap = pci_find_capability(pci_dev, cap_id);
+ if (ranges_overlap(address - cap, len, PCI_MSI_FLAGS, 1)) {
+ assigned_dev_update_msi(pci_dev, cap + PCI_MSI_FLAGS);
+ }
+ }
+#endif
+ break;
+
+ case PCI_CAP_ID_MSIX:
+#ifdef KVM_CAP_DEVICE_MSIX
+ {
+ uint8_t cap = pci_find_capability(pci_dev, cap_id);
+ if (ranges_overlap(address - cap, len, PCI_MSIX_FLAGS + 1, 1)) {
+ assigned_dev_update_msix(pci_dev, cap + PCI_MSIX_FLAGS);
+ }
+ }
+#endif
+ break;
+#endif
+
+ case PCI_CAP_ID_VPD:
+ case PCI_CAP_ID_VNDR:
+ assigned_dev_pci_write(pci_dev, address, val, len);
+ break;
+ }
+}
+
+static int assigned_device_pci_cap_init(PCIDevice *pci_dev)
+{
+ AssignedDevice *dev = container_of(pci_dev, AssignedDevice, dev);
+ PCIRegion *pci_region = dev->real_device.regions;
+ int ret, pos;
+
+ /* Clear initial capabilities pointer and status copied from hw */
+ pci_set_byte(pci_dev->config + PCI_CAPABILITY_LIST, 0);
+ pci_set_word(pci_dev->config + PCI_STATUS,
+ pci_get_word(pci_dev->config + PCI_STATUS) &
+ ~PCI_STATUS_CAP_LIST);
+
+#ifdef KVM_CAP_IRQ_ROUTING
+#ifdef KVM_CAP_DEVICE_MSI
+ /* Expose MSI capability
+ * MSI capability is the 1st capability in capability config */
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0))) {
+ dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI;
+ /* Only 32-bit/no-mask currently supported */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10)) < 0) {
+ return ret;
+ }
+
+ pci_set_word(pci_dev->config + pos + PCI_MSI_FLAGS,
+ pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS) &
+ PCI_MSI_FLAGS_QMASK);
+ pci_set_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO, 0);
+ pci_set_word(pci_dev->config + pos + PCI_MSI_DATA_32, 0);
+
+ /* Set writable fields */
+ pci_set_word(pci_dev->wmask + pos + PCI_MSI_FLAGS,
+ PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+ pci_set_long(pci_dev->wmask + pos + PCI_MSI_ADDRESS_LO, 0xfffffffc);
+ pci_set_word(pci_dev->wmask + pos + PCI_MSI_DATA_32, 0xffff);
+ }
+#endif
+#ifdef KVM_CAP_DEVICE_MSIX
+ /* Expose MSI-X capability */
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0))) {
+ int bar_nr;
+ uint32_t msix_table_entry;
+
+ dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX;
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSIX, pos, 12)) < 0) {
+ return ret;
+ }
+
+ pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS,
+ pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) &
+ PCI_MSIX_TABSIZE);
+
+ /* Only enable and function mask bits are writable */
+ pci_set_word(pci_dev->wmask + pos + PCI_MSIX_FLAGS,
+ PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
+
+ msix_table_entry = pci_get_long(pci_dev->config + pos + PCI_MSIX_TABLE);
+ bar_nr = msix_table_entry & PCI_MSIX_BIR;
+ msix_table_entry &= ~PCI_MSIX_BIR;
+ dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry;
+ }
+#endif
+#endif
+
+ /* Minimal PM support, nothing writable, device appears to NAK changes */
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PM, 0))) {
+ uint16_t pmc;
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, pos,
+ PCI_PM_SIZEOF)) < 0) {
+ return ret;
+ }
+
+ pmc = pci_get_word(pci_dev->config + pos + PCI_CAP_FLAGS);
+ pmc &= (PCI_PM_CAP_VER_MASK | PCI_PM_CAP_DSI);
+ pci_set_word(pci_dev->config + pos + PCI_CAP_FLAGS, pmc);
+
+ /* assign_device will bring the device up to D0, so we don't need
+ * to worry about doing that ourselves here. */
+ pci_set_word(pci_dev->config + pos + PCI_PM_CTRL,
+ PCI_PM_CTRL_NO_SOFT_RESET);
+
+ pci_set_byte(pci_dev->config + pos + PCI_PM_PPB_EXTENSIONS, 0);
+ pci_set_byte(pci_dev->config + pos + PCI_PM_DATA_REGISTER, 0);
+ }
+
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0))) {
+ uint8_t version;
+ uint16_t type, devctl, lnkcap, lnksta;
+ uint32_t devcap;
+ int size = 0x3c; /* version 2 size */
+
+ version = pci_get_byte(pci_dev->config + pos + PCI_EXP_FLAGS);
+ version &= PCI_EXP_FLAGS_VERS;
+ if (version == 1) {
+ size = 0x14;
+ } else if (version > 2) {
+ fprintf(stderr, "Unsupported PCI express capability version %d\n",
+ version);
+ return -EINVAL;
+ }
+
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_EXP,
+ pos, size)) < 0) {
+ return ret;
+ }
+
+ type = pci_get_word(pci_dev->config + pos + PCI_EXP_FLAGS);
+ type = (type & PCI_EXP_FLAGS_TYPE) >> 8;
+ if (type != PCI_EXP_TYPE_ENDPOINT &&
+ type != PCI_EXP_TYPE_LEG_END && type != PCI_EXP_TYPE_RC_END) {
+ fprintf(stderr,
+ "Device assignment only supports endpoint assignment, "
+ "device type %d\n", type);
+ return -EINVAL;
+ }
+
+ /* capabilities, pass existing read-only copy
+ * PCI_EXP_FLAGS_IRQ: updated by hardware, should be direct read */
+
+ /* device capabilities: hide FLR */
+ devcap = pci_get_long(pci_dev->config + pos + PCI_EXP_DEVCAP);
+ devcap &= ~PCI_EXP_DEVCAP_FLR;
+ pci_set_long(pci_dev->config + pos + PCI_EXP_DEVCAP, devcap);
+
+ /* device control: clear all error reporting enable bits, leaving
+ * leaving only a few host values. Note, these are
+ * all writable, but not passed to hw.
+ */
+ devctl = pci_get_word(pci_dev->config + pos + PCI_EXP_DEVCTL);
+ devctl = (devctl & (PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_PAYLOAD)) |
+ PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN;
+ pci_set_word(pci_dev->config + pos + PCI_EXP_DEVCTL, devctl);
+ devctl = PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_AUX_PME;
+ pci_set_word(pci_dev->wmask + pos + PCI_EXP_DEVCTL, ~devctl);
+
+ /* Clear device status */
+ pci_set_word(pci_dev->config + pos + PCI_EXP_DEVSTA, 0);
+
+ /* Link capabilities, expose links and latencues, clear reporting */
+ lnkcap = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKCAP);
+ lnkcap &= (PCI_EXP_LNKCAP_SLS | PCI_EXP_LNKCAP_MLW |
+ PCI_EXP_LNKCAP_ASPMS | PCI_EXP_LNKCAP_L0SEL |
+ PCI_EXP_LNKCAP_L1EL);
+ pci_set_word(pci_dev->config + pos + PCI_EXP_LNKCAP, lnkcap);
+ pci_set_word(pci_dev->wmask + pos + PCI_EXP_LNKCAP,
+ PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_RCB |
+ PCI_EXP_LNKCTL_CCC | PCI_EXP_LNKCTL_ES |
+ PCI_EXP_LNKCTL_CLKREQ_EN | PCI_EXP_LNKCTL_HAWD);
+
+ /* Link control, pass existing read-only copy. Should be writable? */
+
+ /* Link status, only expose current speed and width */
+ lnksta = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKSTA);
+ lnksta &= (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW);
+ pci_set_word(pci_dev->config + pos + PCI_EXP_LNKSTA, lnksta);
+
+ if (version >= 2) {
+ /* Slot capabilities, control, status - not needed for endpoints */
+ pci_set_long(pci_dev->config + pos + PCI_EXP_SLTCAP, 0);
+ pci_set_word(pci_dev->config + pos + PCI_EXP_SLTCTL, 0);
+ pci_set_word(pci_dev->config + pos + PCI_EXP_SLTSTA, 0);
+
+ /* Root control, capabilities, status - not needed for endpoints */
+ pci_set_word(pci_dev->config + pos + PCI_EXP_RTCTL, 0);
+ pci_set_word(pci_dev->config + pos + PCI_EXP_RTCAP, 0);
+ pci_set_long(pci_dev->config + pos + PCI_EXP_RTSTA, 0);
+
+ /* Device capabilities/control 2, pass existing read-only copy */
+ /* Link control 2, pass existing read-only copy */
+ }
+ }
+
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PCIX, 0))) {
+ uint16_t cmd;
+ uint32_t status;
+
+ /* Only expose the minimum, 8 byte capability */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_PCIX, pos, 8)) < 0) {
+ return ret;
+ }
+
+ /* Command register, clear upper bits, including extended modes */
+ cmd = pci_get_word(pci_dev->config + pos + PCI_X_CMD);
+ cmd &= (PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO | PCI_X_CMD_MAX_READ |
+ PCI_X_CMD_MAX_SPLIT);
+ pci_set_word(pci_dev->config + pos + PCI_X_CMD, cmd);
+
+ /* Status register, update with emulated PCI bus location, clear
+ * error bits, leave the rest. */
+ status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS);
+ status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN);
+ status |= (pci_bus_num(pci_dev->bus) << 8) | pci_dev->devfn;
+ status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL |
+ PCI_X_STATUS_SPL_ERR);
+ pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status);
+ }
+
+ if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0))) {
+ /* Direct R/W passthrough */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_VPD, pos, 8)) < 0) {
+ return ret;
+ }
+ }
+
+ /* Devices can have multiple vendor capabilities, get them all */
+ for (pos = 0; (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos));
+ pos += PCI_CAP_LIST_NEXT) {
+ uint8_t len = pci_get_byte(pci_dev->config + pos + PCI_CAP_FLAGS);
+ /* Direct R/W passthrough */
+ if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_VNDR,
+ pos, len)) < 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t msix_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ AssignedDevice *adev = opaque;
+ unsigned int offset = addr & 0xfff;
+ void *page = adev->msix_table_page;
+ uint32_t val = 0;
+
+ memcpy(&val, (void *)((char *)page + offset), 4);
+
+ return val;
+}
+
+static uint32_t msix_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return ((msix_mmio_readl(opaque, addr & ~3)) >>
+ (8 * (addr & 3))) & 0xff;
+}
+
+static uint32_t msix_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ return ((msix_mmio_readl(opaque, addr & ~3)) >>
+ (8 * (addr & 3))) & 0xffff;
+}
+
+static void msix_mmio_writel(void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ AssignedDevice *adev = opaque;
+ unsigned int offset = addr & 0xfff;
+ void *page = adev->msix_table_page;
+
+ DEBUG("write to MSI-X entry table mmio offset 0x%lx, val 0x%x\n",
+ addr, val);
+ memcpy((void *)((char *)page + offset), &val, 4);
+}
+
+static void msix_mmio_writew(void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ msix_mmio_writel(opaque, addr & ~3,
+ (val & 0xffff) << (8*(addr & 3)));
+}
+
+static void msix_mmio_writeb(void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ msix_mmio_writel(opaque, addr & ~3,
+ (val & 0xff) << (8*(addr & 3)));
+}
+
+static CPUWriteMemoryFunc *msix_mmio_write[] = {
+ msix_mmio_writeb, msix_mmio_writew, msix_mmio_writel
+};
+
+static CPUReadMemoryFunc *msix_mmio_read[] = {
+ msix_mmio_readb, msix_mmio_readw, msix_mmio_readl
+};
+
+static int assigned_dev_register_msix_mmio(AssignedDevice *dev)
+{
+ dev->msix_table_page = mmap(NULL, 0x1000,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
+ if (dev->msix_table_page == MAP_FAILED) {
+ fprintf(stderr, "fail allocate msix_table_page! %s\n",
+ strerror(errno));
+ return -EFAULT;
+ }
+ memset(dev->msix_table_page, 0, 0x1000);
+ dev->mmio_index = cpu_register_io_memory(
+ msix_mmio_read, msix_mmio_write, dev,
+ DEVICE_NATIVE_ENDIAN);
+ return 0;
+}
+
+static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev)
+{
+ if (!dev->msix_table_page)
+ return;
+
+ cpu_unregister_io_memory(dev->mmio_index);
+ dev->mmio_index = 0;
+
+ if (munmap(dev->msix_table_page, 0x1000) == -1) {
+ fprintf(stderr, "error unmapping msix_table_page! %s\n",
+ strerror(errno));
+ }
+ dev->msix_table_page = NULL;
+}
+
+static const VMStateDescription vmstate_assigned_device = {
+ .name = "pci-assign",
+ .fields = (VMStateField []) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void reset_assigned_device(DeviceState *dev)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, dev);
+
+ /*
+ * When a 0 is written to the command register, the device is logically
+ * disconnected from the PCI bus. This avoids further DMA transfers.
+ */
+ assigned_dev_pci_write_config(d, PCI_COMMAND, 0, 2);
+}
+
+static int assigned_initfn(struct PCIDevice *pci_dev)
+{
+ AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+ uint8_t e_device, e_intx;
+ int r;
+
+ if (!kvm_enabled()) {
+ error_report("pci-assign: error: requires KVM support");
+ return -1;
+ }
+
+ if (!dev->host.seg && !dev->host.bus && !dev->host.dev && !dev->host.func) {
+ error_report("pci-assign: error: no host device specified");
+ return -1;
+ }
+
+ if (get_real_device(dev, dev->host.seg, dev->host.bus,
+ dev->host.dev, dev->host.func)) {
+ error_report("pci-assign: Error: Couldn't get real device (%s)!",
+ dev->dev.qdev.id);
+ goto out;
+ }
+
+ /* handle real device's MMIO/PIO BARs */
+ if (assigned_dev_register_regions(dev->real_device.regions,
+ dev->real_device.region_number,
+ dev))
+ goto out;
+
+ /* handle interrupt routing */
+ e_device = (dev->dev.devfn >> 3) & 0x1f;
+ e_intx = dev->dev.config[0x3d] - 1;
+ dev->intpin = e_intx;
+ dev->run = 0;
+ dev->girq = -1;
+ dev->h_segnr = dev->host.seg;
+ dev->h_busnr = dev->host.bus;
+ dev->h_devfn = PCI_DEVFN(dev->host.dev, dev->host.func);
+
+ if (assigned_device_pci_cap_init(pci_dev) < 0)
+ goto out;
+
+ /* assign device to guest */
+ r = assign_device(dev);
+ if (r < 0)
+ goto out;
+
+ /* assign irq for the device */
+ r = assign_irq(dev);
+ if (r < 0)
+ goto assigned_out;
+
+ /* intercept MSI-X entry page in the MMIO */
+ if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX)
+ if (assigned_dev_register_msix_mmio(dev))
+ goto assigned_out;
+
+ assigned_dev_load_option_rom(dev);
+ QLIST_INSERT_HEAD(&devs, dev, next);
+
+ add_boot_device_path(dev->bootindex, &pci_dev->qdev, NULL);
+
+ /* Register a vmsd so that we can mark it unmigratable. */
+ vmstate_register(&dev->dev.qdev, 0, &vmstate_assigned_device, dev);
+ register_device_unmigratable(&dev->dev.qdev,
+ vmstate_assigned_device.name, dev);
+
+ return 0;
+
+assigned_out:
+ deassign_device(dev);
+out:
+ free_assigned_device(dev);
+ return -1;
+}
+
+static int assigned_exitfn(struct PCIDevice *pci_dev)
+{
+ AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+
+ vmstate_unregister(&dev->dev.qdev, &vmstate_assigned_device, dev);
+ QLIST_REMOVE(dev, next);
+ deassign_device(dev);
+ free_assigned_device(dev);
+ return 0;
+}
+
+static int parse_hostaddr(DeviceState *dev, Property *prop, const char *str)
+{
+ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop);
+ int rc;
+
+ rc = pci_parse_host_devaddr(str, &ptr->seg, &ptr->bus, &ptr->dev, &ptr->func);
+ if (rc != 0)
+ return -1;
+ return 0;
+}
+
+static int print_hostaddr(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop);
+
+ return snprintf(dest, len, "%02x:%02x.%x", ptr->bus, ptr->dev, ptr->func);
+}
+
+PropertyInfo qdev_prop_hostaddr = {
+ .name = "pci-hostaddr",
+ .type = -1,
+ .size = sizeof(PCIHostDevice),
+ .parse = parse_hostaddr,
+ .print = print_hostaddr,
+};
+
+static PCIDeviceInfo assign_info = {
+ .qdev.name = "pci-assign",
+ .qdev.desc = "pass through host pci devices to the guest",
+ .qdev.size = sizeof(AssignedDevice),
+ .qdev.reset = reset_assigned_device,
+ .init = assigned_initfn,
+ .exit = assigned_exitfn,
+ .config_read = assigned_dev_pci_read_config,
+ .config_write = assigned_dev_pci_write_config,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP("host", AssignedDevice, host, qdev_prop_hostaddr, PCIHostDevice),
+ DEFINE_PROP_BIT("iommu", AssignedDevice, features,
+ ASSIGNED_DEVICE_USE_IOMMU_BIT, true),
+ DEFINE_PROP_BIT("prefer_msi", AssignedDevice, features,
+ ASSIGNED_DEVICE_PREFER_MSI_BIT, true),
+ DEFINE_PROP_INT32("bootindex", AssignedDevice, bootindex, -1),
+ DEFINE_PROP_STRING("configfd", AssignedDevice, configfd_name),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void assign_register_devices(void)
+{
+ pci_qdev_register(&assign_info);
+}
+
+device_init(assign_register_devices)
+
+/*
+ * Scan the assigned devices for the devices that have an option ROM, and then
+ * load the corresponding ROM data to RAM. If an error occurs while loading an
+ * option ROM, we just ignore that option ROM and continue with the next one.
+ */
+static void assigned_dev_load_option_rom(AssignedDevice *dev)
+{
+ char name[32], rom_file[64];
+ FILE *fp;
+ uint8_t val;
+ struct stat st;
+ void *ptr;
+
+ /* If loading ROM from file, pci handles it */
+ if (dev->dev.romfile || !dev->dev.rom_bar)
+ return;
+
+ snprintf(rom_file, sizeof(rom_file),
+ "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
+ dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func);
+
+ if (stat(rom_file, &st)) {
+ return;
+ }
+
+ if (access(rom_file, F_OK)) {
+ fprintf(stderr, "pci-assign: Insufficient privileges for %s\n",
+ rom_file);
+ return;
+ }
+
+ /* Write "1" to the ROM file to enable it */
+ fp = fopen(rom_file, "r+");
+ if (fp == NULL) {
+ return;
+ }
+ val = 1;
+ if (fwrite(&val, 1, 1, fp) != 1) {
+ goto close_rom;
+ }
+ fseek(fp, 0, SEEK_SET);
+
+ snprintf(name, sizeof(name), "%s.rom", dev->dev.qdev.info->name);
+ dev->dev.rom_offset = qemu_ram_alloc(&dev->dev.qdev, name, st.st_size);
+ ptr = qemu_get_ram_ptr(dev->dev.rom_offset);
+ memset(ptr, 0xff, st.st_size);
+
+ if (!fread(ptr, 1, st.st_size, fp)) {
+ fprintf(stderr, "pci-assign: Cannot read from host %s\n"
+ "\tDevice option ROM contents are probably invalid "
+ "(check dmesg).\n\tSkip option ROM probe with rombar=0, "
+ "or load from file with romfile=\n", rom_file);
+ qemu_ram_free(dev->dev.rom_offset);
+ dev->dev.rom_offset = 0;
+ goto close_rom;
+ }
+
+ pci_register_bar(&dev->dev, PCI_ROM_SLOT,
+ st.st_size, 0, pci_map_option_rom);
+close_rom:
+ /* Write "0" to disable ROM */
+ fseek(fp, 0, SEEK_SET);
+ val = 0;
+ if (!fwrite(&val, 1, 1, fp)) {
+ DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
+ }
+ fclose(fp);
+}
diff --git a/hw/device-assignment.h b/hw/device-assignment.h
new file mode 100644
index 0000000..86af0a9
--- /dev/null
+++ b/hw/device-assignment.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2007, Neocleus Corporation.
+ * Copyright (c) 2007, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Data structures for storing PCI state
+ *
+ * Adapted to kvm by Qumranet
+ *
+ * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com)
+ * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com)
+ * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com)
+ * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
+ */
+
+#ifndef __DEVICE_ASSIGNMENT_H__
+#define __DEVICE_ASSIGNMENT_H__
+
+#include <sys/mman.h>
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "pci.h"
+
+/* From include/linux/pci.h in the kernel sources */
+#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+
+typedef struct PCIHostDevice {
+ int seg;
+ int bus;
+ int dev;
+ int func;
+} PCIHostDevice;
+
+typedef struct {
+ int type; /* Memory or port I/O */
+ int valid;
+ uint32_t base_addr;
+ uint32_t size; /* size of the region */
+ int resource_fd;
+} PCIRegion;
+
+typedef struct {
+ uint8_t bus, dev, func; /* Bus inside domain, device and function */
+ int irq; /* IRQ number */
+ uint16_t region_number; /* number of active regions */
+
+ /* Port I/O or MMIO Regions */
+ PCIRegion regions[PCI_NUM_REGIONS - 1];
+ int config_fd;
+} PCIDevRegions;
+
+typedef struct {
+ pcibus_t e_physbase;
+ ram_addr_t memory_index;
+ union {
+ void *r_virtbase; /* mmapped access address for memory regions */
+ uint32_t r_baseport; /* the base guest port for I/O regions */
+ } u;
+ int num; /* our index within v_addrs[] */
+ pcibus_t e_size; /* emulated size of region in bytes */
+ pcibus_t r_size; /* real size of region in bytes */
+ PCIRegion *region;
+} AssignedDevRegion;
+
+#define ASSIGNED_DEVICE_USE_IOMMU_BIT 0
+#define ASSIGNED_DEVICE_PREFER_MSI_BIT 1
+
+#define ASSIGNED_DEVICE_USE_IOMMU_MASK (1 << ASSIGNED_DEVICE_USE_IOMMU_BIT)
+#define ASSIGNED_DEVICE_PREFER_MSI_MASK (1 << ASSIGNED_DEVICE_PREFER_MSI_BIT)
+
+typedef struct AssignedDevice {
+ PCIDevice dev;
+ PCIHostDevice host;
+ uint32_t features;
+ int intpin;
+ uint8_t debug_flags;
+ AssignedDevRegion v_addrs[PCI_NUM_REGIONS - 1];
+ PCIDevRegions real_device;
+ int run;
+ int girq;
+ unsigned int h_segnr;
+ unsigned char h_busnr;
+ unsigned int h_devfn;
+ int irq_requested_type;
+ int bound;
+ struct {
+#define ASSIGNED_DEVICE_CAP_MSI (1 << 0)
+#define ASSIGNED_DEVICE_CAP_MSIX (1 << 1)
+ uint32_t available;
+#define ASSIGNED_DEVICE_MSI_ENABLED (1 << 0)
+#define ASSIGNED_DEVICE_MSIX_ENABLED (1 << 1)
+#define ASSIGNED_DEVICE_MSIX_MASKED (1 << 2)
+ uint32_t state;
+ } cap;
+ int irq_entries_nr;
+ struct kvm_irq_routing_entry *entry;
+ void *msix_table_page;
+ target_phys_addr_t msix_table_addr;
+ int mmio_index;
+ int need_emulate_cmd;
+ char *configfd_name;
+ int32_t bootindex;
+ QLIST_ENTRY(AssignedDevice) next;
+} AssignedDevice;
+
+void assigned_dev_update_irqs(void);
+
+#endif /* __DEVICE_ASSIGNMENT_H__ */
diff --git a/hw/device-hotplug.c b/hw/device-hotplug.c
new file mode 100644
index 0000000..8b2ed7a
--- /dev/null
+++ b/hw/device-hotplug.c
@@ -0,0 +1,46 @@
+/*
+ * QEMU device hotplug helpers
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "boards.h"
+#include "net.h"
+#include "blockdev.h"
+
+DriveInfo *add_init_drive(const char *optstr)
+{
+ DriveInfo *dinfo;
+ QemuOpts *opts;
+
+ opts = drive_def(optstr);
+ if (!opts)
+ return NULL;
+
+ dinfo = drive_init(opts, current_machine->use_scsi);
+ if (!dinfo) {
+ qemu_opts_del(opts);
+ return NULL;
+ }
+
+ return dinfo;
+}
diff --git a/hw/devices.h b/hw/devices.h
new file mode 100644
index 0000000..c788373
--- /dev/null
+++ b/hw/devices.h
@@ -0,0 +1,70 @@
+#ifndef QEMU_DEVICES_H
+#define QEMU_DEVICES_H
+
+/* Devices that have nowhere better to go. */
+
+/* smc91c111.c */
+void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
+
+/* lan9118.c */
+void lan9118_init(NICInfo *, uint32_t, qemu_irq);
+
+/* tsc210x.c */
+uWireSlave *tsc2102_init(qemu_irq pint);
+uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav);
+I2SCodec *tsc210x_codec(uWireSlave *chip);
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len);
+void tsc210x_set_transform(uWireSlave *chip,
+ MouseTransformInfo *info);
+void tsc210x_key_event(uWireSlave *chip, int key, int down);
+
+/* tsc2005.c */
+void *tsc2005_init(qemu_irq pintdav);
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len);
+void tsc2005_set_transform(void *opaque, MouseTransformInfo *info);
+
+/* stellaris_input.c */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
+
+/* blizzard.c */
+void *s1d13745_init(qemu_irq gpio_int);
+void s1d13745_write(void *opaque, int dc, uint16_t value);
+void s1d13745_write_block(void *opaque, int dc,
+ void *buf, size_t len, int pitch);
+uint16_t s1d13745_read(void *opaque, int dc);
+
+/* cbus.c */
+typedef struct {
+ qemu_irq clk;
+ qemu_irq dat;
+ qemu_irq sel;
+} CBus;
+CBus *cbus_init(qemu_irq dat_out);
+void cbus_attach(CBus *bus, void *slave_opaque);
+
+void *retu_init(qemu_irq irq, int vilma);
+void *tahvo_init(qemu_irq irq, int betty);
+
+void retu_key_event(void *retu, int state);
+
+/* tusb6010.c */
+typedef struct TUSBState TUSBState;
+TUSBState *tusb6010_init(qemu_irq intr);
+int tusb6010_sync_io(TUSBState *s);
+int tusb6010_async_io(TUSBState *s);
+void tusb6010_power(TUSBState *s, int on);
+
+/* tc6393xb.c */
+typedef struct TC6393xbState TC6393xbState;
+#define TC6393XB_RAM 0x110000 /* amount of ram for Video and USB */
+TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq);
+void tc6393xb_gpio_out_set(TC6393xbState *s, int line,
+ qemu_irq handler);
+qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s);
+qemu_irq tc6393xb_l3v_get(TC6393xbState *s);
+
+/* sm501.c */
+void sm501_init(uint32_t base, uint32_t local_mem_bytes, qemu_irq irq,
+ CharDriverState *chr);
+
+#endif
diff --git a/hw/dma.c b/hw/dma.c
new file mode 100644
index 0000000..8a7302a
--- /dev/null
+++ b/hw/dma.c
@@ -0,0 +1,555 @@
+/*
+ * QEMU DMA emulation
+ *
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+
+/* #define DEBUG_DMA */
+
+#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#ifdef DEBUG_DMA
+#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#else
+#define linfo(...)
+#define ldebug(...)
+#endif
+
+struct dma_regs {
+ int now[2];
+ uint16_t base[2];
+ uint8_t mode;
+ uint8_t page;
+ uint8_t pageh;
+ uint8_t dack;
+ uint8_t eop;
+ DMA_transfer_handler transfer_handler;
+ void *opaque;
+};
+
+#define ADDR 0
+#define COUNT 1
+
+static struct dma_cont {
+ uint8_t status;
+ uint8_t command;
+ uint8_t mask;
+ uint8_t flip_flop;
+ int dshift;
+ struct dma_regs regs[4];
+ qemu_irq *cpu_request_exit;
+} dma_controllers[2];
+
+enum {
+ CMD_MEMORY_TO_MEMORY = 0x01,
+ CMD_FIXED_ADDRESS = 0x02,
+ CMD_BLOCK_CONTROLLER = 0x04,
+ CMD_COMPRESSED_TIME = 0x08,
+ CMD_CYCLIC_PRIORITY = 0x10,
+ CMD_EXTENDED_WRITE = 0x20,
+ CMD_LOW_DREQ = 0x40,
+ CMD_LOW_DACK = 0x80,
+ CMD_NOT_SUPPORTED = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
+ | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
+ | CMD_LOW_DREQ | CMD_LOW_DACK
+
+};
+
+static void DMA_run (void);
+
+static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
+
+static void write_page (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel %#x %#x\n", nport, data);
+ return;
+ }
+ d->regs[ichan].page = data;
+}
+
+static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel %#x %#x\n", nport, data);
+ return;
+ }
+ d->regs[ichan].pageh = data;
+}
+
+static uint32_t read_page (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel read %#x\n", nport);
+ return 0;
+ }
+ return d->regs[ichan].page;
+}
+
+static uint32_t read_pageh (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan;
+
+ ichan = channels[nport & 7];
+ if (-1 == ichan) {
+ dolog ("invalid channel read %#x\n", nport);
+ return 0;
+ }
+ return d->regs[ichan].pageh;
+}
+
+static inline void init_chan (struct dma_cont *d, int ichan)
+{
+ struct dma_regs *r;
+
+ r = d->regs + ichan;
+ r->now[ADDR] = r->base[ADDR] << d->dshift;
+ r->now[COUNT] = 0;
+}
+
+static inline int getff (struct dma_cont *d)
+{
+ int ff;
+
+ ff = d->flip_flop;
+ d->flip_flop = !ff;
+ return ff;
+}
+
+static uint32_t read_chan (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int ichan, nreg, iport, ff, val, dir;
+ struct dma_regs *r;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ ichan = iport >> 1;
+ nreg = iport & 1;
+ r = d->regs + ichan;
+
+ dir = ((r->mode >> 5) & 1) ? -1 : 1;
+ ff = getff (d);
+ if (nreg)
+ val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
+ else
+ val = r->now[ADDR] + r->now[COUNT] * dir;
+
+ ldebug ("read_chan %#x -> %d\n", iport, val);
+ return (val >> (d->dshift + (ff << 3))) & 0xff;
+}
+
+static void write_chan (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int iport, ichan, nreg;
+ struct dma_regs *r;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ ichan = iport >> 1;
+ nreg = iport & 1;
+ r = d->regs + ichan;
+ if (getff (d)) {
+ r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
+ init_chan (d, ichan);
+ } else {
+ r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
+ }
+}
+
+static void write_cont (void *opaque, uint32_t nport, uint32_t data)
+{
+ struct dma_cont *d = opaque;
+ int iport, ichan = 0;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ switch (iport) {
+ case 0x08: /* command */
+ if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
+ dolog ("command %#x not supported\n", data);
+ return;
+ }
+ d->command = data;
+ break;
+
+ case 0x09:
+ ichan = data & 3;
+ if (data & 4) {
+ d->status |= 1 << (ichan + 4);
+ }
+ else {
+ d->status &= ~(1 << (ichan + 4));
+ }
+ d->status &= ~(1 << ichan);
+ DMA_run();
+ break;
+
+ case 0x0a: /* single mask */
+ if (data & 4)
+ d->mask |= 1 << (data & 3);
+ else
+ d->mask &= ~(1 << (data & 3));
+ DMA_run();
+ break;
+
+ case 0x0b: /* mode */
+ {
+ ichan = data & 3;
+#ifdef DEBUG_DMA
+ {
+ int op, ai, dir, opmode;
+ op = (data >> 2) & 3;
+ ai = (data >> 4) & 1;
+ dir = (data >> 5) & 1;
+ opmode = (data >> 6) & 3;
+
+ linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
+ ichan, op, ai, dir, opmode);
+ }
+#endif
+ d->regs[ichan].mode = data;
+ break;
+ }
+
+ case 0x0c: /* clear flip flop */
+ d->flip_flop = 0;
+ break;
+
+ case 0x0d: /* reset */
+ d->flip_flop = 0;
+ d->mask = ~0;
+ d->status = 0;
+ d->command = 0;
+ break;
+
+ case 0x0e: /* clear mask for all channels */
+ d->mask = 0;
+ DMA_run();
+ break;
+
+ case 0x0f: /* write mask for all channels */
+ d->mask = data;
+ DMA_run();
+ break;
+
+ default:
+ dolog ("unknown iport %#x\n", iport);
+ break;
+ }
+
+#ifdef DEBUG_DMA
+ if (0xc != iport) {
+ linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
+ nport, ichan, data);
+ }
+#endif
+}
+
+static uint32_t read_cont (void *opaque, uint32_t nport)
+{
+ struct dma_cont *d = opaque;
+ int iport, val;
+
+ iport = (nport >> d->dshift) & 0x0f;
+ switch (iport) {
+ case 0x08: /* status */
+ val = d->status;
+ d->status &= 0xf0;
+ break;
+ case 0x0f: /* mask */
+ val = d->mask;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
+ return val;
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+ return dma_controllers[nchan > 3].regs[nchan & 3].mode;
+}
+
+void DMA_hold_DREQ (int nchan)
+{
+ int ncont, ichan;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+ linfo ("held cont=%d chan=%d\n", ncont, ichan);
+ dma_controllers[ncont].status |= 1 << (ichan + 4);
+ DMA_run();
+}
+
+void DMA_release_DREQ (int nchan)
+{
+ int ncont, ichan;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+ linfo ("released cont=%d chan=%d\n", ncont, ichan);
+ dma_controllers[ncont].status &= ~(1 << (ichan + 4));
+ DMA_run();
+}
+
+static void channel_run (int ncont, int ichan)
+{
+ int n;
+ struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+#ifdef DEBUG_DMA
+ int dir, opmode;
+
+ dir = (r->mode >> 5) & 1;
+ opmode = (r->mode >> 6) & 3;
+
+ if (dir) {
+ dolog ("DMA in address decrement mode\n");
+ }
+ if (opmode != 1) {
+ dolog ("DMA not in single mode select %#x\n", opmode);
+ }
+#endif
+
+ n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
+ r->now[COUNT], (r->base[COUNT] + 1) << ncont);
+ r->now[COUNT] = n;
+ ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+}
+
+static QEMUBH *dma_bh;
+
+static void DMA_run (void)
+{
+ struct dma_cont *d;
+ int icont, ichan;
+ int rearm = 0;
+
+ d = dma_controllers;
+
+ for (icont = 0; icont < 2; icont++, d++) {
+ for (ichan = 0; ichan < 4; ichan++) {
+ int mask;
+
+ mask = 1 << ichan;
+
+ if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
+ channel_run (icont, ichan);
+ rearm = 1;
+ }
+ }
+ }
+
+ if (rearm)
+ qemu_bh_schedule_idle(dma_bh);
+}
+
+static void DMA_run_bh(void *unused)
+{
+ DMA_run();
+}
+
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+ struct dma_regs *r;
+ int ichan, ncont;
+
+ ncont = nchan > 3;
+ ichan = nchan & 3;
+
+ r = dma_controllers[ncont].regs + ichan;
+ r->transfer_handler = transfer_handler;
+ r->opaque = opaque;
+}
+
+int DMA_read_memory (int nchan, void *buf, int pos, int len)
+{
+ struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+ if (r->mode & 0x20) {
+ int i;
+ uint8_t *p = buf;
+
+ cpu_physical_memory_read (addr - pos - len, buf, len);
+ /* What about 16bit transfers? */
+ for (i = 0; i < len >> 1; i++) {
+ uint8_t b = p[len - i - 1];
+ p[i] = b;
+ }
+ }
+ else
+ cpu_physical_memory_read (addr + pos, buf, len);
+
+ return len;
+}
+
+int DMA_write_memory (int nchan, void *buf, int pos, int len)
+{
+ struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+ target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+ if (r->mode & 0x20) {
+ int i;
+ uint8_t *p = buf;
+
+ cpu_physical_memory_write (addr - pos - len, buf, len);
+ /* What about 16bit transfers? */
+ for (i = 0; i < len; i++) {
+ uint8_t b = p[len - i - 1];
+ p[i] = b;
+ }
+ }
+ else
+ cpu_physical_memory_write (addr + pos, buf, len);
+
+ return len;
+}
+
+/* request the emulator to transfer a new DMA memory block ASAP */
+void DMA_schedule(int nchan)
+{
+ struct dma_cont *d = &dma_controllers[nchan > 3];
+
+ qemu_irq_pulse(*d->cpu_request_exit);
+}
+
+static void dma_reset(void *opaque)
+{
+ struct dma_cont *d = opaque;
+ write_cont (d, (0x0d << d->dshift), 0);
+}
+
+static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
+ nchan, dma_pos, dma_len);
+ return dma_pos;
+}
+
+/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
+static void dma_init2(struct dma_cont *d, int base, int dshift,
+ int page_base, int pageh_base,
+ qemu_irq *cpu_request_exit)
+{
+ static const int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
+ int i;
+
+ d->dshift = dshift;
+ d->cpu_request_exit = cpu_request_exit;
+ for (i = 0; i < 8; i++) {
+ register_ioport_write (base + (i << dshift), 1, 1, write_chan, d);
+ register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
+ }
+ for (i = 0; i < ARRAY_SIZE (page_port_list); i++) {
+ register_ioport_write (page_base + page_port_list[i], 1, 1,
+ write_page, d);
+ register_ioport_read (page_base + page_port_list[i], 1, 1,
+ read_page, d);
+ if (pageh_base >= 0) {
+ register_ioport_write (pageh_base + page_port_list[i], 1, 1,
+ write_pageh, d);
+ register_ioport_read (pageh_base + page_port_list[i], 1, 1,
+ read_pageh, d);
+ }
+ }
+ for (i = 0; i < 8; i++) {
+ register_ioport_write (base + ((i + 8) << dshift), 1, 1,
+ write_cont, d);
+ register_ioport_read (base + ((i + 8) << dshift), 1, 1,
+ read_cont, d);
+ }
+ qemu_register_reset(dma_reset, d);
+ dma_reset(d);
+ for (i = 0; i < ARRAY_SIZE (d->regs); ++i) {
+ d->regs[i].transfer_handler = dma_phony_handler;
+ }
+}
+
+static const VMStateDescription vmstate_dma_regs = {
+ .name = "dma_regs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_ARRAY(now, struct dma_regs, 2),
+ VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2),
+ VMSTATE_UINT8(mode, struct dma_regs),
+ VMSTATE_UINT8(page, struct dma_regs),
+ VMSTATE_UINT8(pageh, struct dma_regs),
+ VMSTATE_UINT8(dack, struct dma_regs),
+ VMSTATE_UINT8(eop, struct dma_regs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int dma_post_load(void *opaque, int version_id)
+{
+ DMA_run();
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_dma = {
+ .name = "dma",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = dma_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(command, struct dma_cont),
+ VMSTATE_UINT8(mask, struct dma_cont),
+ VMSTATE_UINT8(flip_flop, struct dma_cont),
+ VMSTATE_INT32(dshift, struct dma_cont),
+ VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_dma_regs, struct dma_regs),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+{
+ dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
+ high_page_enable ? 0x480 : -1, cpu_request_exit);
+ dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
+ high_page_enable ? 0x488 : -1, cpu_request_exit);
+ vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]);
+ vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]);
+
+ dma_bh = qemu_bh_new(DMA_run_bh, NULL);
+}
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
new file mode 100644
index 0000000..0ef8abe
--- /dev/null
+++ b/hw/dp8393x.c
@@ -0,0 +1,914 @@
+/*
+ * QEMU NS SONIC DP8393x netcard
+ *
+ * Copyright (c) 2008-2009 Herve Poussineau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "net.h"
+#include "mips.h"
+
+//#define DEBUG_SONIC
+
+/* Calculate CRCs properly on Rx packets */
+#define SONIC_CALCULATE_RXCRC
+
+#if defined(SONIC_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#ifdef DEBUG_SONIC
+#define DPRINTF(fmt, ...) \
+do { printf("sonic: " fmt , ## __VA_ARGS__); } while (0)
+static const char* reg_names[] = {
+ "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
+ "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
+ "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
+ "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
+ "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
+ "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
+ "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
+ "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define SONIC_ERROR(fmt, ...) \
+do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+
+#define SONIC_CR 0x00
+#define SONIC_DCR 0x01
+#define SONIC_RCR 0x02
+#define SONIC_TCR 0x03
+#define SONIC_IMR 0x04
+#define SONIC_ISR 0x05
+#define SONIC_UTDA 0x06
+#define SONIC_CTDA 0x07
+#define SONIC_TPS 0x08
+#define SONIC_TFC 0x09
+#define SONIC_TSA0 0x0a
+#define SONIC_TSA1 0x0b
+#define SONIC_TFS 0x0c
+#define SONIC_URDA 0x0d
+#define SONIC_CRDA 0x0e
+#define SONIC_CRBA0 0x0f
+#define SONIC_CRBA1 0x10
+#define SONIC_RBWC0 0x11
+#define SONIC_RBWC1 0x12
+#define SONIC_EOBC 0x13
+#define SONIC_URRA 0x14
+#define SONIC_RSA 0x15
+#define SONIC_REA 0x16
+#define SONIC_RRP 0x17
+#define SONIC_RWP 0x18
+#define SONIC_TRBA0 0x19
+#define SONIC_TRBA1 0x1a
+#define SONIC_LLFA 0x1f
+#define SONIC_TTDA 0x20
+#define SONIC_CEP 0x21
+#define SONIC_CAP2 0x22
+#define SONIC_CAP1 0x23
+#define SONIC_CAP0 0x24
+#define SONIC_CE 0x25
+#define SONIC_CDP 0x26
+#define SONIC_CDC 0x27
+#define SONIC_SR 0x28
+#define SONIC_WT0 0x29
+#define SONIC_WT1 0x2a
+#define SONIC_RSC 0x2b
+#define SONIC_CRCT 0x2c
+#define SONIC_FAET 0x2d
+#define SONIC_MPT 0x2e
+#define SONIC_MDT 0x2f
+#define SONIC_DCR2 0x3f
+
+#define SONIC_CR_HTX 0x0001
+#define SONIC_CR_TXP 0x0002
+#define SONIC_CR_RXDIS 0x0004
+#define SONIC_CR_RXEN 0x0008
+#define SONIC_CR_STP 0x0010
+#define SONIC_CR_ST 0x0020
+#define SONIC_CR_RST 0x0080
+#define SONIC_CR_RRRA 0x0100
+#define SONIC_CR_LCAM 0x0200
+#define SONIC_CR_MASK 0x03bf
+
+#define SONIC_DCR_DW 0x0020
+#define SONIC_DCR_LBR 0x2000
+#define SONIC_DCR_EXBUS 0x8000
+
+#define SONIC_RCR_PRX 0x0001
+#define SONIC_RCR_LBK 0x0002
+#define SONIC_RCR_FAER 0x0004
+#define SONIC_RCR_CRCR 0x0008
+#define SONIC_RCR_CRS 0x0020
+#define SONIC_RCR_LPKT 0x0040
+#define SONIC_RCR_BC 0x0080
+#define SONIC_RCR_MC 0x0100
+#define SONIC_RCR_LB0 0x0200
+#define SONIC_RCR_LB1 0x0400
+#define SONIC_RCR_AMC 0x0800
+#define SONIC_RCR_PRO 0x1000
+#define SONIC_RCR_BRD 0x2000
+#define SONIC_RCR_RNT 0x4000
+
+#define SONIC_TCR_PTX 0x0001
+#define SONIC_TCR_BCM 0x0002
+#define SONIC_TCR_FU 0x0004
+#define SONIC_TCR_EXC 0x0040
+#define SONIC_TCR_CRSL 0x0080
+#define SONIC_TCR_NCRS 0x0100
+#define SONIC_TCR_EXD 0x0400
+#define SONIC_TCR_CRCI 0x2000
+#define SONIC_TCR_PINT 0x8000
+
+#define SONIC_ISR_RBE 0x0020
+#define SONIC_ISR_RDE 0x0040
+#define SONIC_ISR_TC 0x0080
+#define SONIC_ISR_TXDN 0x0200
+#define SONIC_ISR_PKTRX 0x0400
+#define SONIC_ISR_PINT 0x0800
+#define SONIC_ISR_LCD 0x1000
+
+typedef struct dp8393xState {
+ /* Hardware */
+ int it_shift;
+ qemu_irq irq;
+#ifdef DEBUG_SONIC
+ int irq_level;
+#endif
+ QEMUTimer *watchdog;
+ int64_t wt_last_update;
+ NICConf conf;
+ NICState *nic;
+ int mmio_index;
+
+ /* Registers */
+ uint8_t cam[16][6];
+ uint16_t regs[0x40];
+
+ /* Temporaries */
+ uint8_t tx_buffer[0x10000];
+ int loopback_packet;
+
+ /* Memory access */
+ void (*memory_rw)(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write);
+ void* mem_opaque;
+} dp8393xState;
+
+static void dp8393x_update_irq(dp8393xState *s)
+{
+ int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
+
+#ifdef DEBUG_SONIC
+ if (level != s->irq_level) {
+ s->irq_level = level;
+ if (level) {
+ DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
+ } else {
+ DPRINTF("lower irq\n");
+ }
+ }
+#endif
+
+ qemu_set_irq(s->irq, level);
+}
+
+static void do_load_cam(dp8393xState *s)
+{
+ uint16_t data[8];
+ int width, size;
+ uint16_t index = 0;
+
+ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+ size = sizeof(uint16_t) * 4 * width;
+
+ while (s->regs[SONIC_CDC] & 0x1f) {
+ /* Fill current entry */
+ s->memory_rw(s->mem_opaque,
+ (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
+ (uint8_t *)data, size, 0);
+ s->cam[index][0] = data[1 * width] & 0xff;
+ s->cam[index][1] = data[1 * width] >> 8;
+ s->cam[index][2] = data[2 * width] & 0xff;
+ s->cam[index][3] = data[2 * width] >> 8;
+ s->cam[index][4] = data[3 * width] & 0xff;
+ s->cam[index][5] = data[3 * width] >> 8;
+ DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
+ s->cam[index][0], s->cam[index][1], s->cam[index][2],
+ s->cam[index][3], s->cam[index][4], s->cam[index][5]);
+ /* Move to next entry */
+ s->regs[SONIC_CDC]--;
+ s->regs[SONIC_CDP] += size;
+ index++;
+ }
+
+ /* Read CAM enable */
+ s->memory_rw(s->mem_opaque,
+ (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
+ (uint8_t *)data, size, 0);
+ s->regs[SONIC_CE] = data[0 * width];
+ DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
+
+ /* Done */
+ s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
+ s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
+ dp8393x_update_irq(s);
+}
+
+static void do_read_rra(dp8393xState *s)
+{
+ uint16_t data[8];
+ int width, size;
+
+ /* Read memory */
+ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+ size = sizeof(uint16_t) * 4 * width;
+ s->memory_rw(s->mem_opaque,
+ (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
+ (uint8_t *)data, size, 0);
+
+ /* Update SONIC registers */
+ s->regs[SONIC_CRBA0] = data[0 * width];
+ s->regs[SONIC_CRBA1] = data[1 * width];
+ s->regs[SONIC_RBWC0] = data[2 * width];
+ s->regs[SONIC_RBWC1] = data[3 * width];
+ DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
+ s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
+ s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
+
+ /* Go to next entry */
+ s->regs[SONIC_RRP] += size;
+
+ /* Handle wrap */
+ if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
+ s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
+ }
+
+ /* Check resource exhaustion */
+ if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
+ {
+ s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
+ dp8393x_update_irq(s);
+ }
+
+ /* Done */
+ s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
+}
+
+static void do_software_reset(dp8393xState *s)
+{
+ qemu_del_timer(s->watchdog);
+
+ s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
+ s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
+}
+
+static void set_next_tick(dp8393xState *s)
+{
+ uint32_t ticks;
+ int64_t delay;
+
+ if (s->regs[SONIC_CR] & SONIC_CR_STP) {
+ qemu_del_timer(s->watchdog);
+ return;
+ }
+
+ ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
+ s->wt_last_update = qemu_get_clock(vm_clock);
+ delay = get_ticks_per_sec() * ticks / 5000000;
+ qemu_mod_timer(s->watchdog, s->wt_last_update + delay);
+}
+
+static void update_wt_regs(dp8393xState *s)
+{
+ int64_t elapsed;
+ uint32_t val;
+
+ if (s->regs[SONIC_CR] & SONIC_CR_STP) {
+ qemu_del_timer(s->watchdog);
+ return;
+ }
+
+ elapsed = s->wt_last_update - qemu_get_clock(vm_clock);
+ val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
+ val -= elapsed / 5000000;
+ s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
+ s->regs[SONIC_WT0] = (val >> 0) & 0xffff;
+ set_next_tick(s);
+
+}
+
+static void do_start_timer(dp8393xState *s)
+{
+ s->regs[SONIC_CR] &= ~SONIC_CR_STP;
+ set_next_tick(s);
+}
+
+static void do_stop_timer(dp8393xState *s)
+{
+ s->regs[SONIC_CR] &= ~SONIC_CR_ST;
+ update_wt_regs(s);
+}
+
+static void do_receiver_enable(dp8393xState *s)
+{
+ s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
+}
+
+static void do_receiver_disable(dp8393xState *s)
+{
+ s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
+}
+
+static void do_transmit_packets(dp8393xState *s)
+{
+ uint16_t data[12];
+ int width, size;
+ int tx_len, len;
+ uint16_t i;
+
+ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+
+ while (1) {
+ /* Read memory */
+ DPRINTF("Transmit packet at %08x\n",
+ (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
+ size = sizeof(uint16_t) * 6 * width;
+ s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
+ s->memory_rw(s->mem_opaque,
+ ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
+ (uint8_t *)data, size, 0);
+ tx_len = 0;
+
+ /* Update registers */
+ s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
+ s->regs[SONIC_TPS] = data[1 * width];
+ s->regs[SONIC_TFC] = data[2 * width];
+ s->regs[SONIC_TSA0] = data[3 * width];
+ s->regs[SONIC_TSA1] = data[4 * width];
+ s->regs[SONIC_TFS] = data[5 * width];
+
+ /* Handle programmable interrupt */
+ if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
+ s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
+ } else {
+ s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
+ }
+
+ for (i = 0; i < s->regs[SONIC_TFC]; ) {
+ /* Append fragment */
+ len = s->regs[SONIC_TFS];
+ if (tx_len + len > sizeof(s->tx_buffer)) {
+ len = sizeof(s->tx_buffer) - tx_len;
+ }
+ s->memory_rw(s->mem_opaque,
+ (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
+ &s->tx_buffer[tx_len], len, 0);
+ tx_len += len;
+
+ i++;
+ if (i != s->regs[SONIC_TFC]) {
+ /* Read next fragment details */
+ size = sizeof(uint16_t) * 3 * width;
+ s->memory_rw(s->mem_opaque,
+ ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
+ (uint8_t *)data, size, 0);
+ s->regs[SONIC_TSA0] = data[0 * width];
+ s->regs[SONIC_TSA1] = data[1 * width];
+ s->regs[SONIC_TFS] = data[2 * width];
+ }
+ }
+
+ /* Handle Ethernet checksum */
+ if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
+ /* Don't append FCS there, to look like slirp packets
+ * which don't have one */
+ } else {
+ /* Remove existing FCS */
+ tx_len -= 4;
+ }
+
+ if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
+ /* Loopback */
+ s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
+ if (s->nic->nc.info->can_receive(&s->nic->nc)) {
+ s->loopback_packet = 1;
+ s->nic->nc.info->receive(&s->nic->nc, s->tx_buffer, tx_len);
+ }
+ } else {
+ /* Transmit packet */
+ qemu_send_packet(&s->nic->nc, s->tx_buffer, tx_len);
+ }
+ s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
+
+ /* Write status */
+ data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
+ size = sizeof(uint16_t) * width;
+ s->memory_rw(s->mem_opaque,
+ (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
+ (uint8_t *)data, size, 1);
+
+ if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
+ /* Read footer of packet */
+ size = sizeof(uint16_t) * width;
+ s->memory_rw(s->mem_opaque,
+ ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
+ (uint8_t *)data, size, 0);
+ s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
+ if (data[0 * width] & 0x1) {
+ /* EOL detected */
+ break;
+ }
+ }
+ }
+
+ /* Done */
+ s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
+ s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
+ dp8393x_update_irq(s);
+}
+
+static void do_halt_transmission(dp8393xState *s)
+{
+ /* Nothing to do */
+}
+
+static void do_command(dp8393xState *s, uint16_t command)
+{
+ if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
+ s->regs[SONIC_CR] &= ~SONIC_CR_RST;
+ return;
+ }
+
+ s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
+
+ if (command & SONIC_CR_HTX)
+ do_halt_transmission(s);
+ if (command & SONIC_CR_TXP)
+ do_transmit_packets(s);
+ if (command & SONIC_CR_RXDIS)
+ do_receiver_disable(s);
+ if (command & SONIC_CR_RXEN)
+ do_receiver_enable(s);
+ if (command & SONIC_CR_STP)
+ do_stop_timer(s);
+ if (command & SONIC_CR_ST)
+ do_start_timer(s);
+ if (command & SONIC_CR_RST)
+ do_software_reset(s);
+ if (command & SONIC_CR_RRRA)
+ do_read_rra(s);
+ if (command & SONIC_CR_LCAM)
+ do_load_cam(s);
+}
+
+static uint16_t read_register(dp8393xState *s, int reg)
+{
+ uint16_t val = 0;
+
+ switch (reg) {
+ /* Update data before reading it */
+ case SONIC_WT0:
+ case SONIC_WT1:
+ update_wt_regs(s);
+ val = s->regs[reg];
+ break;
+ /* Accept read to some registers only when in reset mode */
+ case SONIC_CAP2:
+ case SONIC_CAP1:
+ case SONIC_CAP0:
+ if (s->regs[SONIC_CR] & SONIC_CR_RST) {
+ val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
+ val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
+ }
+ break;
+ /* All other registers have no special contrainst */
+ default:
+ val = s->regs[reg];
+ }
+
+ DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
+
+ return val;
+}
+
+static void write_register(dp8393xState *s, int reg, uint16_t val)
+{
+ DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
+
+ switch (reg) {
+ /* Command register */
+ case SONIC_CR:
+ do_command(s, val);;
+ break;
+ /* Prevent write to read-only registers */
+ case SONIC_CAP2:
+ case SONIC_CAP1:
+ case SONIC_CAP0:
+ case SONIC_SR:
+ case SONIC_MDT:
+ DPRINTF("writing to reg %d invalid\n", reg);
+ break;
+ /* Accept write to some registers only when in reset mode */
+ case SONIC_DCR:
+ if (s->regs[SONIC_CR] & SONIC_CR_RST) {
+ s->regs[reg] = val & 0xbfff;
+ } else {
+ DPRINTF("writing to DCR invalid\n");
+ }
+ break;
+ case SONIC_DCR2:
+ if (s->regs[SONIC_CR] & SONIC_CR_RST) {
+ s->regs[reg] = val & 0xf017;
+ } else {
+ DPRINTF("writing to DCR2 invalid\n");
+ }
+ break;
+ /* 12 lower bytes are Read Only */
+ case SONIC_TCR:
+ s->regs[reg] = val & 0xf000;
+ break;
+ /* 9 lower bytes are Read Only */
+ case SONIC_RCR:
+ s->regs[reg] = val & 0xffe0;
+ break;
+ /* Ignore most significant bit */
+ case SONIC_IMR:
+ s->regs[reg] = val & 0x7fff;
+ dp8393x_update_irq(s);
+ break;
+ /* Clear bits by writing 1 to them */
+ case SONIC_ISR:
+ val &= s->regs[reg];
+ s->regs[reg] &= ~val;
+ if (val & SONIC_ISR_RBE) {
+ do_read_rra(s);
+ }
+ dp8393x_update_irq(s);
+ break;
+ /* Ignore least significant bit */
+ case SONIC_RSA:
+ case SONIC_REA:
+ case SONIC_RRP:
+ case SONIC_RWP:
+ s->regs[reg] = val & 0xfffe;
+ break;
+ /* Invert written value for some registers */
+ case SONIC_CRCT:
+ case SONIC_FAET:
+ case SONIC_MPT:
+ s->regs[reg] = val ^ 0xffff;
+ break;
+ /* All other registers have no special contrainst */
+ default:
+ s->regs[reg] = val;
+ }
+
+ if (reg == SONIC_WT0 || reg == SONIC_WT1) {
+ set_next_tick(s);
+ }
+}
+
+static void dp8393x_watchdog(void *opaque)
+{
+ dp8393xState *s = opaque;
+
+ if (s->regs[SONIC_CR] & SONIC_CR_STP) {
+ return;
+ }
+
+ s->regs[SONIC_WT1] = 0xffff;
+ s->regs[SONIC_WT0] = 0xffff;
+ set_next_tick(s);
+
+ /* Signal underflow */
+ s->regs[SONIC_ISR] |= SONIC_ISR_TC;
+ dp8393x_update_irq(s);
+}
+
+static uint32_t dp8393x_readw(void *opaque, target_phys_addr_t addr)
+{
+ dp8393xState *s = opaque;
+ int reg;
+
+ if ((addr & ((1 << s->it_shift) - 1)) != 0) {
+ return 0;
+ }
+
+ reg = addr >> s->it_shift;
+ return read_register(s, reg);
+}
+
+static uint32_t dp8393x_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
+ return (v >> (8 * (addr & 0x1))) & 0xff;
+}
+
+static uint32_t dp8393x_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = dp8393x_readw(opaque, addr);
+ v |= dp8393x_readw(opaque, addr + 2) << 16;
+ return v;
+}
+
+static void dp8393x_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ dp8393xState *s = opaque;
+ int reg;
+
+ if ((addr & ((1 << s->it_shift) - 1)) != 0) {
+ return;
+ }
+
+ reg = addr >> s->it_shift;
+
+ write_register(s, reg, (uint16_t)val);
+}
+
+static void dp8393x_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
+
+ switch (addr & 3) {
+ case 0:
+ val = val | (old_val & 0xff00);
+ break;
+ case 1:
+ val = (val << 8) | (old_val & 0x00ff);
+ break;
+ }
+ dp8393x_writew(opaque, addr & ~0x1, val);
+}
+
+static void dp8393x_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ dp8393x_writew(opaque, addr, val & 0xffff);
+ dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
+}
+
+static CPUReadMemoryFunc * const dp8393x_read[3] = {
+ dp8393x_readb,
+ dp8393x_readw,
+ dp8393x_readl,
+};
+
+static CPUWriteMemoryFunc * const dp8393x_write[3] = {
+ dp8393x_writeb,
+ dp8393x_writew,
+ dp8393x_writel,
+};
+
+static int nic_can_receive(VLANClientState *nc)
+{
+ dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
+ return 0;
+ if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
+ return 0;
+ return 1;
+}
+
+static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
+{
+ static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ int i;
+
+ /* Check for runt packet (remember that checksum is not there) */
+ if (size < 64 - 4) {
+ return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
+ }
+
+ /* Check promiscuous mode */
+ if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
+ return 0;
+ }
+
+ /* Check multicast packets */
+ if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
+ return SONIC_RCR_MC;
+ }
+
+ /* Check broadcast */
+ if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
+ return SONIC_RCR_BC;
+ }
+
+ /* Check CAM */
+ for (i = 0; i < 16; i++) {
+ if (s->regs[SONIC_CE] & (1 << i)) {
+ /* Entry enabled */
+ if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size)
+{
+ dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ uint16_t data[10];
+ int packet_type;
+ uint32_t available, address;
+ int width, rx_len = size;
+ uint32_t checksum;
+
+ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+
+ s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
+ SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
+
+ packet_type = receive_filter(s, buf, size);
+ if (packet_type < 0) {
+ DPRINTF("packet not for netcard\n");
+ return -1;
+ }
+
+ /* XXX: Check byte ordering */
+
+ /* Check for EOL */
+ if (s->regs[SONIC_LLFA] & 0x1) {
+ /* Are we still in resource exhaustion? */
+ size = sizeof(uint16_t) * 1 * width;
+ address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
+ s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
+ if (data[0 * width] & 0x1) {
+ /* Still EOL ; stop reception */
+ return -1;
+ } else {
+ s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
+ }
+ }
+
+ /* Save current position */
+ s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
+ s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
+
+ /* Calculate the ethernet checksum */
+#ifdef SONIC_CALCULATE_RXCRC
+ checksum = cpu_to_le32(crc32(0, buf, rx_len));
+#else
+ checksum = 0;
+#endif
+
+ /* Put packet into RBA */
+ DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
+ address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
+ s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
+ address += rx_len;
+ s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
+ rx_len += 4;
+ s->regs[SONIC_CRBA1] = address >> 16;
+ s->regs[SONIC_CRBA0] = address & 0xffff;
+ available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
+ available -= rx_len / 2;
+ s->regs[SONIC_RBWC1] = available >> 16;
+ s->regs[SONIC_RBWC0] = available & 0xffff;
+
+ /* Update status */
+ if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
+ s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
+ }
+ s->regs[SONIC_RCR] |= packet_type;
+ s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
+ if (s->loopback_packet) {
+ s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
+ s->loopback_packet = 0;
+ }
+
+ /* Write status to memory */
+ DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
+ data[0 * width] = s->regs[SONIC_RCR]; /* status */
+ data[1 * width] = rx_len; /* byte count */
+ data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
+ data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
+ data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
+ size = sizeof(uint16_t) * 5 * width;
+ s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
+
+ /* Move to next descriptor */
+ size = sizeof(uint16_t) * width;
+ s->memory_rw(s->mem_opaque,
+ ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
+ (uint8_t *)data, size, 0);
+ s->regs[SONIC_LLFA] = data[0 * width];
+ if (s->regs[SONIC_LLFA] & 0x1) {
+ /* EOL detected */
+ s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
+ } else {
+ data[0 * width] = 0; /* in_use */
+ s->memory_rw(s->mem_opaque,
+ ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
+ (uint8_t *)data, size, 1);
+ s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
+ s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
+ s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
+
+ if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
+ /* Read next RRA */
+ do_read_rra(s);
+ }
+ }
+
+ /* Done */
+ dp8393x_update_irq(s);
+
+ return size;
+}
+
+static void nic_reset(void *opaque)
+{
+ dp8393xState *s = opaque;
+ qemu_del_timer(s->watchdog);
+
+ s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
+ s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
+ s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
+ s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
+ s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
+ s->regs[SONIC_IMR] = 0;
+ s->regs[SONIC_ISR] = 0;
+ s->regs[SONIC_DCR2] = 0;
+ s->regs[SONIC_EOBC] = 0x02F8;
+ s->regs[SONIC_RSC] = 0;
+ s->regs[SONIC_CE] = 0;
+ s->regs[SONIC_RSC] = 0;
+
+ /* Network cable is connected */
+ s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
+
+ dp8393x_update_irq(s);
+}
+
+static void nic_cleanup(VLANClientState *nc)
+{
+ dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ cpu_unregister_io_memory(s->mmio_index);
+
+ qemu_del_timer(s->watchdog);
+ qemu_free_timer(s->watchdog);
+
+ qemu_free(s);
+}
+
+static NetClientInfo net_dp83932_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = nic_can_receive,
+ .receive = nic_receive,
+ .cleanup = nic_cleanup,
+};
+
+void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
+ qemu_irq irq, void* mem_opaque,
+ void (*memory_rw)(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write))
+{
+ dp8393xState *s;
+
+ qemu_check_nic_model(nd, "dp83932");
+
+ s = qemu_mallocz(sizeof(dp8393xState));
+
+ s->mem_opaque = mem_opaque;
+ s->memory_rw = memory_rw;
+ s->it_shift = it_shift;
+ s->irq = irq;
+ s->watchdog = qemu_new_timer(vm_clock, dp8393x_watchdog, s);
+ s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
+
+ memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr));
+ s->conf.vlan = nd->vlan;
+ s->conf.peer = nd->netdev;
+
+ s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
+
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ qemu_register_reset(nic_reset, s);
+ nic_reset(s);
+
+ s->mmio_index = cpu_register_io_memory(dp8393x_read, dp8393x_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x40 << it_shift, s->mmio_index);
+}
diff --git a/hw/ds1225y.c b/hw/ds1225y.c
new file mode 100644
index 0000000..b1c5232
--- /dev/null
+++ b/hw/ds1225y.c
@@ -0,0 +1,182 @@
+/*
+ * QEMU NVRAM emulation for DS1225Y chip
+ *
+ * Copyright (c) 2007-2008 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips.h"
+#include "nvram.h"
+
+//#define DEBUG_NVRAM
+
+typedef struct ds1225y_t
+{
+ uint32_t chip_size;
+ QEMUFile *file;
+ uint8_t *contents;
+ uint8_t protection;
+} ds1225y_t;
+
+
+static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
+{
+ ds1225y_t *s = opaque;
+ uint32_t val;
+
+ val = s->contents[addr];
+
+#ifdef DEBUG_NVRAM
+ printf("nvram: read 0x%x at " TARGET_FMT_lx "\n", val, addr);
+#endif
+ return val;
+}
+
+static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = nvram_readb(opaque, addr);
+ v |= nvram_readb(opaque, addr + 1) << 8;
+ return v;
+}
+
+static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = nvram_readb(opaque, addr);
+ v |= nvram_readb(opaque, addr + 1) << 8;
+ v |= nvram_readb(opaque, addr + 2) << 16;
+ v |= nvram_readb(opaque, addr + 3) << 24;
+ return v;
+}
+
+static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ ds1225y_t *s = opaque;
+
+#ifdef DEBUG_NVRAM
+ printf("nvram: write 0x%x at " TARGET_FMT_lx "\n", val, addr);
+#endif
+
+ s->contents[addr] = val & 0xff;
+ if (s->file) {
+ qemu_fseek(s->file, addr, SEEK_SET);
+ qemu_put_byte(s->file, (int)val);
+ qemu_fflush(s->file);
+ }
+}
+
+static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ nvram_writeb(opaque, addr, val & 0xff);
+ nvram_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ nvram_writeb(opaque, addr, val & 0xff);
+ nvram_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ nvram_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ nvram_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+static void nvram_writeb_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ ds1225y_t *s = opaque;
+
+ if (s->protection != 7) {
+#ifdef DEBUG_NVRAM
+ printf("nvram: prevent write of 0x%x at " TARGET_FMT_lx "\n", val, addr);
+#endif
+ return;
+ }
+
+ nvram_writeb(opaque, addr, val);
+}
+
+static void nvram_writew_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ nvram_writeb_protected(opaque, addr, val & 0xff);
+ nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void nvram_writel_protected (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ nvram_writeb_protected(opaque, addr, val & 0xff);
+ nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff);
+ nvram_writeb_protected(opaque, addr + 2, (val >> 16) & 0xff);
+ nvram_writeb_protected(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+static CPUReadMemoryFunc * const nvram_read[] = {
+ &nvram_readb,
+ &nvram_readw,
+ &nvram_readl,
+};
+
+static CPUWriteMemoryFunc * const nvram_write[] = {
+ &nvram_writeb,
+ &nvram_writew,
+ &nvram_writel,
+};
+
+static CPUWriteMemoryFunc * const nvram_write_protected[] = {
+ &nvram_writeb_protected,
+ &nvram_writew_protected,
+ &nvram_writel_protected,
+};
+
+/* Initialisation routine */
+void *ds1225y_init(target_phys_addr_t mem_base, const char *filename)
+{
+ ds1225y_t *s;
+ int mem_indexRW, mem_indexRP;
+ QEMUFile *file;
+
+ s = qemu_mallocz(sizeof(ds1225y_t));
+ s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */
+ s->contents = qemu_mallocz(s->chip_size);
+ s->protection = 7;
+
+ /* Read current file */
+ file = qemu_fopen(filename, "rb");
+ if (file) {
+ /* Read nvram contents */
+ qemu_get_buffer(file, s->contents, s->chip_size);
+ qemu_fclose(file);
+ }
+ s->file = qemu_fopen(filename, "wb");
+ if (s->file) {
+ /* Write back contents, as 'wb' mode cleaned the file */
+ qemu_put_buffer(s->file, s->contents, s->chip_size);
+ qemu_fflush(s->file);
+ }
+
+ /* Read/write memory */
+ mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW);
+ /* Read/write protected memory */
+ mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP);
+ return s;
+}
diff --git a/hw/ds1338.c b/hw/ds1338.c
new file mode 100644
index 0000000..6f5ae5e
--- /dev/null
+++ b/hw/ds1338.c
@@ -0,0 +1,132 @@
+/*
+ * MAXIM DS1338 I2C RTC+NVRAM
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+
+#include "i2c.h"
+
+typedef struct {
+ i2c_slave i2c;
+ time_t offset;
+ struct tm now;
+ uint8_t nvram[56];
+ int ptr;
+ int addr_byte;
+} DS1338State;
+
+static void ds1338_event(i2c_slave *i2c, enum i2c_event event)
+{
+ DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
+
+ switch (event) {
+ case I2C_START_RECV:
+ qemu_get_timedate(&s->now, s->offset);
+ s->nvram[0] = to_bcd(s->now.tm_sec);
+ s->nvram[1] = to_bcd(s->now.tm_min);
+ if (s->nvram[2] & 0x40) {
+ s->nvram[2] = (to_bcd((s->now.tm_hour % 12)) + 1) | 0x40;
+ if (s->now.tm_hour >= 12) {
+ s->nvram[2] |= 0x20;
+ }
+ } else {
+ s->nvram[2] = to_bcd(s->now.tm_hour);
+ }
+ s->nvram[3] = to_bcd(s->now.tm_wday) + 1;
+ s->nvram[4] = to_bcd(s->now.tm_mday);
+ s->nvram[5] = to_bcd(s->now.tm_mon) + 1;
+ s->nvram[6] = to_bcd(s->now.tm_year - 100);
+ break;
+ case I2C_START_SEND:
+ s->addr_byte = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+static int ds1338_recv(i2c_slave *i2c)
+{
+ DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
+ uint8_t res;
+
+ res = s->nvram[s->ptr];
+ s->ptr = (s->ptr + 1) & 0xff;
+ return res;
+}
+
+static int ds1338_send(i2c_slave *i2c, uint8_t data)
+{
+ DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
+ if (s->addr_byte) {
+ s->ptr = data;
+ s->addr_byte = 0;
+ return 0;
+ }
+ s->nvram[s->ptr - 8] = data;
+ if (data < 8) {
+ qemu_get_timedate(&s->now, s->offset);
+ switch(data) {
+ case 0:
+ /* TODO: Implement CH (stop) bit. */
+ s->now.tm_sec = from_bcd(data & 0x7f);
+ break;
+ case 1:
+ s->now.tm_min = from_bcd(data & 0x7f);
+ break;
+ case 2:
+ if (data & 0x40) {
+ if (data & 0x20) {
+ data = from_bcd(data & 0x4f) + 11;
+ } else {
+ data = from_bcd(data & 0x1f) - 1;
+ }
+ } else {
+ data = from_bcd(data);
+ }
+ s->now.tm_hour = data;
+ break;
+ case 3:
+ s->now.tm_wday = from_bcd(data & 7) - 1;
+ break;
+ case 4:
+ s->now.tm_mday = from_bcd(data & 0x3f);
+ break;
+ case 5:
+ s->now.tm_mon = from_bcd(data & 0x1f) - 1;
+ case 6:
+ s->now.tm_year = from_bcd(data) + 100;
+ break;
+ case 7:
+ /* Control register. Currently ignored. */
+ break;
+ }
+ s->offset = qemu_timedate_diff(&s->now);
+ }
+ s->ptr = (s->ptr + 1) & 0xff;
+ return 0;
+}
+
+static int ds1338_init(i2c_slave *i2c)
+{
+ return 0;
+}
+
+static I2CSlaveInfo ds1338_info = {
+ .qdev.name = "ds1338",
+ .qdev.size = sizeof(DS1338State),
+ .init = ds1338_init,
+ .event = ds1338_event,
+ .recv = ds1338_recv,
+ .send = ds1338_send,
+};
+
+static void ds1338_register_devices(void)
+{
+ i2c_register_slave(&ds1338_info);
+}
+
+device_init(ds1338_register_devices)
diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c
new file mode 100644
index 0000000..61efb39
--- /dev/null
+++ b/hw/dummy_m68k.c
@@ -0,0 +1,80 @@
+/*
+ * Dummy board with just RAM and CPU for use as an ISS.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "hw.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "loader.h"
+#include "elf.h"
+
+#define KERNEL_LOAD_ADDR 0x10000
+
+/* Board init. */
+
+static void dummy_m68k_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ int kernel_size;
+ uint64_t elf_entry;
+ target_phys_addr_t entry;
+
+ if (!cpu_model)
+ cpu_model = "cfv4e";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find m68k CPU definition\n");
+ exit(1);
+ }
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+
+ /* RAM at address zero */
+ cpu_register_physical_memory(0, ram_size,
+ qemu_ram_alloc(NULL, "dummy_m68k.ram", ram_size) | IO_MEM_RAM);
+
+ /* Load kernel. */
+ if (kernel_filename) {
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ NULL, NULL, 1, ELF_MACHINE, 0);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename,
+ KERNEL_LOAD_ADDR,
+ ram_size - KERNEL_LOAD_ADDR);
+ entry = KERNEL_LOAD_ADDR;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ } else {
+ entry = 0;
+ }
+ env->pc = entry;
+}
+
+static QEMUMachine dummy_m68k_machine = {
+ .name = "dummy",
+ .desc = "Dummy board",
+ .init = dummy_m68k_init,
+};
+
+static void dummy_m68k_machine_init(void)
+{
+ qemu_register_machine(&dummy_m68k_machine);
+}
+
+machine_init(dummy_m68k_machine_init);
diff --git a/hw/e1000.c b/hw/e1000.c
new file mode 100644
index 0000000..af101bd
--- /dev/null
+++ b/hw/e1000.c
@@ -0,0 +1,1183 @@
+/*
+ * QEMU e1000 emulation
+ *
+ * Software developer's manual:
+ * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
+ *
+ * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc.
+ * Copyright (c) 2008 Qumranet
+ * Based on work done by:
+ * Copyright (c) 2007 Dan Aloni
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "hw.h"
+#include "pci.h"
+#include "net.h"
+#include "net/checksum.h"
+#include "loader.h"
+#include "sysemu.h"
+
+#include "e1000_hw.h"
+
+#define E1000_DEBUG
+
+#ifdef E1000_DEBUG
+enum {
+ DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT,
+ DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM,
+ DEBUG_UNKNOWN, DEBUG_TXSUM, DEBUG_TXERR, DEBUG_RXERR,
+ DEBUG_RXFILTER, DEBUG_NOTYET,
+};
+#define DBGBIT(x) (1<<DEBUG_##x)
+static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
+
+#define DBGOUT(what, fmt, ...) do { \
+ if (debugflags & DBGBIT(what)) \
+ fprintf(stderr, "e1000: " fmt, ## __VA_ARGS__); \
+ } while (0)
+#else
+#define DBGOUT(what, fmt, ...) do {} while (0)
+#endif
+
+#define IOPORT_SIZE 0x40
+#define PNPMMIO_SIZE 0x20000
+#define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */
+
+/*
+ * HW models:
+ * E1000_DEV_ID_82540EM works with Windows and Linux
+ * E1000_DEV_ID_82573L OK with windoze and Linux 2.6.22,
+ * appears to perform better than 82540EM, but breaks with Linux 2.6.18
+ * E1000_DEV_ID_82544GC_COPPER appears to work; not well tested
+ * Others never tested
+ */
+enum { E1000_DEVID = E1000_DEV_ID_82540EM };
+
+/*
+ * May need to specify additional MAC-to-PHY entries --
+ * Intel's Windows driver refuses to initialize unless they match
+ */
+enum {
+ PHY_ID2_INIT = E1000_DEVID == E1000_DEV_ID_82573L ? 0xcc2 :
+ E1000_DEVID == E1000_DEV_ID_82544GC_COPPER ? 0xc30 :
+ /* default to E1000_DEV_ID_82540EM */ 0xc20
+};
+
+typedef struct E1000State_st {
+ PCIDevice dev;
+ NICState *nic;
+ NICConf conf;
+ int mmio_index;
+
+ uint32_t mac_reg[0x8000];
+ uint16_t phy_reg[0x20];
+ uint16_t eeprom_data[64];
+
+ uint32_t rxbuf_size;
+ uint32_t rxbuf_min_shift;
+ int check_rxov;
+ struct e1000_tx {
+ unsigned char header[256];
+ unsigned char vlan_header[4];
+ /* Fields vlan and data must not be reordered or separated. */
+ unsigned char vlan[4];
+ unsigned char data[0x10000];
+ uint16_t size;
+ unsigned char sum_needed;
+ unsigned char vlan_needed;
+ uint8_t ipcss;
+ uint8_t ipcso;
+ uint16_t ipcse;
+ uint8_t tucss;
+ uint8_t tucso;
+ uint16_t tucse;
+ uint8_t hdr_len;
+ uint16_t mss;
+ uint32_t paylen;
+ uint16_t tso_frames;
+ char tse;
+ int8_t ip;
+ int8_t tcp;
+ char cptse; // current packet tse bit
+ } tx;
+
+ struct {
+ uint32_t val_in; // shifted in from guest driver
+ uint16_t bitnum_in;
+ uint16_t bitnum_out;
+ uint16_t reading;
+ uint32_t old_eecd;
+ } eecd_state;
+} E1000State;
+
+#define defreg(x) x = (E1000_##x>>2)
+enum {
+ defreg(CTRL), defreg(EECD), defreg(EERD), defreg(GPRC),
+ defreg(GPTC), defreg(ICR), defreg(ICS), defreg(IMC),
+ defreg(IMS), defreg(LEDCTL), defreg(MANC), defreg(MDIC),
+ defreg(MPC), defreg(PBA), defreg(RCTL), defreg(RDBAH),
+ defreg(RDBAL), defreg(RDH), defreg(RDLEN), defreg(RDT),
+ defreg(STATUS), defreg(SWSM), defreg(TCTL), defreg(TDBAH),
+ defreg(TDBAL), defreg(TDH), defreg(TDLEN), defreg(TDT),
+ defreg(TORH), defreg(TORL), defreg(TOTH), defreg(TOTL),
+ defreg(TPR), defreg(TPT), defreg(TXDCTL), defreg(WUFC),
+ defreg(RA), defreg(MTA), defreg(CRCERRS),defreg(VFTA),
+ defreg(VET),
+};
+
+enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
+static const char phy_regcap[0x20] = {
+ [PHY_STATUS] = PHY_R, [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
+ [PHY_ID1] = PHY_R, [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
+ [PHY_CTRL] = PHY_RW, [PHY_1000T_CTRL] = PHY_RW,
+ [PHY_LP_ABILITY] = PHY_R, [PHY_1000T_STATUS] = PHY_R,
+ [PHY_AUTONEG_ADV] = PHY_RW, [M88E1000_RX_ERR_CNTR] = PHY_R,
+ [PHY_ID2] = PHY_R, [M88E1000_PHY_SPEC_STATUS] = PHY_R
+};
+
+static void
+ioport_map(PCIDevice *pci_dev, int region_num, pcibus_t addr,
+ pcibus_t size, int type)
+{
+ DBGOUT(IO, "e1000_ioport_map addr=0x%04"FMT_PCIBUS
+ " size=0x%08"FMT_PCIBUS"\n", addr, size);
+}
+
+static void
+set_interrupt_cause(E1000State *s, int index, uint32_t val)
+{
+ if (val)
+ val |= E1000_ICR_INT_ASSERTED;
+ s->mac_reg[ICR] = val;
+ s->mac_reg[ICS] = val;
+ qemu_set_irq(s->dev.irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0);
+}
+
+static void
+set_ics(E1000State *s, int index, uint32_t val)
+{
+ DBGOUT(INTERRUPT, "set_ics %x, ICR %x, IMR %x\n", val, s->mac_reg[ICR],
+ s->mac_reg[IMS]);
+ set_interrupt_cause(s, 0, val | s->mac_reg[ICR]);
+}
+
+static int
+rxbufsize(uint32_t v)
+{
+ v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 |
+ E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 |
+ E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256;
+ switch (v) {
+ case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384:
+ return 16384;
+ case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192:
+ return 8192;
+ case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096:
+ return 4096;
+ case E1000_RCTL_SZ_1024:
+ return 1024;
+ case E1000_RCTL_SZ_512:
+ return 512;
+ case E1000_RCTL_SZ_256:
+ return 256;
+ }
+ return 2048;
+}
+
+static void
+set_ctrl(E1000State *s, int index, uint32_t val)
+{
+ /* RST is self clearing */
+ s->mac_reg[CTRL] = val & ~E1000_CTRL_RST;
+}
+
+static void
+set_rx_control(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[RCTL] = val;
+ s->rxbuf_size = rxbufsize(val);
+ s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
+ DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT],
+ s->mac_reg[RCTL]);
+}
+
+static void
+set_mdic(E1000State *s, int index, uint32_t val)
+{
+ uint32_t data = val & E1000_MDIC_DATA_MASK;
+ uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
+
+ if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) // phy #
+ val = s->mac_reg[MDIC] | E1000_MDIC_ERROR;
+ else if (val & E1000_MDIC_OP_READ) {
+ DBGOUT(MDIC, "MDIC read reg 0x%x\n", addr);
+ if (!(phy_regcap[addr] & PHY_R)) {
+ DBGOUT(MDIC, "MDIC read reg %x unhandled\n", addr);
+ val |= E1000_MDIC_ERROR;
+ } else
+ val = (val ^ data) | s->phy_reg[addr];
+ } else if (val & E1000_MDIC_OP_WRITE) {
+ DBGOUT(MDIC, "MDIC write reg 0x%x, value 0x%x\n", addr, data);
+ if (!(phy_regcap[addr] & PHY_W)) {
+ DBGOUT(MDIC, "MDIC write reg %x unhandled\n", addr);
+ val |= E1000_MDIC_ERROR;
+ } else
+ s->phy_reg[addr] = data;
+ }
+ s->mac_reg[MDIC] = val | E1000_MDIC_READY;
+ set_ics(s, 0, E1000_ICR_MDAC);
+}
+
+static uint32_t
+get_eecd(E1000State *s, int index)
+{
+ uint32_t ret = E1000_EECD_PRES|E1000_EECD_GNT | s->eecd_state.old_eecd;
+
+ DBGOUT(EEPROM, "reading eeprom bit %d (reading %d)\n",
+ s->eecd_state.bitnum_out, s->eecd_state.reading);
+ if (!s->eecd_state.reading ||
+ ((s->eeprom_data[(s->eecd_state.bitnum_out >> 4) & 0x3f] >>
+ ((s->eecd_state.bitnum_out & 0xf) ^ 0xf))) & 1)
+ ret |= E1000_EECD_DO;
+ return ret;
+}
+
+static void
+set_eecd(E1000State *s, int index, uint32_t val)
+{
+ uint32_t oldval = s->eecd_state.old_eecd;
+
+ s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
+ E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
+ if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do
+ return;
+ if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state
+ s->eecd_state.val_in = 0;
+ s->eecd_state.bitnum_in = 0;
+ s->eecd_state.bitnum_out = 0;
+ s->eecd_state.reading = 0;
+ }
+ if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge
+ return;
+ if (!(E1000_EECD_SK & val)) { // falling edge
+ s->eecd_state.bitnum_out++;
+ return;
+ }
+ s->eecd_state.val_in <<= 1;
+ if (val & E1000_EECD_DI)
+ s->eecd_state.val_in |= 1;
+ if (++s->eecd_state.bitnum_in == 9 && !s->eecd_state.reading) {
+ s->eecd_state.bitnum_out = ((s->eecd_state.val_in & 0x3f)<<4)-1;
+ s->eecd_state.reading = (((s->eecd_state.val_in >> 6) & 7) ==
+ EEPROM_READ_OPCODE_MICROWIRE);
+ }
+ DBGOUT(EEPROM, "eeprom bitnum in %d out %d, reading %d\n",
+ s->eecd_state.bitnum_in, s->eecd_state.bitnum_out,
+ s->eecd_state.reading);
+}
+
+static uint32_t
+flash_eerd_read(E1000State *s, int x)
+{
+ unsigned int index, r = s->mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START;
+
+ if ((s->mac_reg[EERD] & E1000_EEPROM_RW_REG_START) == 0)
+ return (s->mac_reg[EERD]);
+
+ if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG)
+ return (E1000_EEPROM_RW_REG_DONE | r);
+
+ return ((s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) |
+ E1000_EEPROM_RW_REG_DONE | r);
+}
+
+static void
+putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse)
+{
+ uint32_t sum;
+
+ if (cse && cse < n)
+ n = cse + 1;
+ if (sloc < n-1) {
+ sum = net_checksum_add(n-css, data+css);
+ cpu_to_be16wu((uint16_t *)(data + sloc),
+ net_checksum_finish(sum));
+ }
+}
+
+static inline int
+vlan_enabled(E1000State *s)
+{
+ return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0);
+}
+
+static inline int
+vlan_rx_filter_enabled(E1000State *s)
+{
+ return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0);
+}
+
+static inline int
+is_vlan_packet(E1000State *s, const uint8_t *buf)
+{
+ return (be16_to_cpup((uint16_t *)(buf + 12)) ==
+ le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
+}
+
+static inline int
+is_vlan_txd(uint32_t txd_lower)
+{
+ return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
+}
+
+/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
+ * fill it in, just pad descriptor length by 4 bytes unless guest
+ * told us to strip it off the packet. */
+static inline int
+fcs_len(E1000State *s)
+{
+ return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
+}
+
+static void
+xmit_seg(E1000State *s)
+{
+ uint16_t len, *sp;
+ unsigned int frames = s->tx.tso_frames, css, sofar, n;
+ struct e1000_tx *tp = &s->tx;
+
+ if (tp->tse && tp->cptse) {
+ css = tp->ipcss;
+ DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
+ frames, tp->size, css);
+ if (tp->ip) { // IPv4
+ cpu_to_be16wu((uint16_t *)(tp->data+css+2),
+ tp->size - css);
+ cpu_to_be16wu((uint16_t *)(tp->data+css+4),
+ be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
+ } else // IPv6
+ cpu_to_be16wu((uint16_t *)(tp->data+css+4),
+ tp->size - css);
+ css = tp->tucss;
+ len = tp->size - css;
+ DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len);
+ if (tp->tcp) {
+ sofar = frames * tp->mss;
+ cpu_to_be32wu((uint32_t *)(tp->data+css+4), // seq
+ be32_to_cpupu((uint32_t *)(tp->data+css+4))+sofar);
+ if (tp->paylen - sofar > tp->mss)
+ tp->data[css + 13] &= ~9; // PSH, FIN
+ } else // UDP
+ cpu_to_be16wu((uint16_t *)(tp->data+css+4), len);
+ if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
+ unsigned int phsum;
+ // add pseudo-header length before checksum calculation
+ sp = (uint16_t *)(tp->data + tp->tucso);
+ phsum = be16_to_cpup(sp) + len;
+ phsum = (phsum >> 16) + (phsum & 0xffff);
+ cpu_to_be16wu(sp, phsum);
+ }
+ tp->tso_frames++;
+ }
+
+ if (tp->sum_needed & E1000_TXD_POPTS_TXSM)
+ putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse);
+ if (tp->sum_needed & E1000_TXD_POPTS_IXSM)
+ putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse);
+ if (tp->vlan_needed) {
+ memmove(tp->vlan, tp->data, 4);
+ memmove(tp->data, tp->data + 4, 8);
+ memcpy(tp->data + 8, tp->vlan_header, 4);
+ qemu_send_packet(&s->nic->nc, tp->vlan, tp->size + 4);
+ } else
+ qemu_send_packet(&s->nic->nc, tp->data, tp->size);
+ s->mac_reg[TPT]++;
+ s->mac_reg[GPTC]++;
+ n = s->mac_reg[TOTL];
+ if ((s->mac_reg[TOTL] += s->tx.size) < n)
+ s->mac_reg[TOTH]++;
+}
+
+static void
+process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
+{
+ uint32_t txd_lower = le32_to_cpu(dp->lower.data);
+ uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D);
+ unsigned int split_size = txd_lower & 0xffff, bytes, sz, op;
+ unsigned int msh = 0xfffff, hdr = 0;
+ uint64_t addr;
+ struct e1000_context_desc *xp = (struct e1000_context_desc *)dp;
+ struct e1000_tx *tp = &s->tx;
+
+ if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor
+ op = le32_to_cpu(xp->cmd_and_length);
+ tp->ipcss = xp->lower_setup.ip_fields.ipcss;
+ tp->ipcso = xp->lower_setup.ip_fields.ipcso;
+ tp->ipcse = le16_to_cpu(xp->lower_setup.ip_fields.ipcse);
+ tp->tucss = xp->upper_setup.tcp_fields.tucss;
+ tp->tucso = xp->upper_setup.tcp_fields.tucso;
+ tp->tucse = le16_to_cpu(xp->upper_setup.tcp_fields.tucse);
+ tp->paylen = op & 0xfffff;
+ tp->hdr_len = xp->tcp_seg_setup.fields.hdr_len;
+ tp->mss = le16_to_cpu(xp->tcp_seg_setup.fields.mss);
+ tp->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0;
+ tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0;
+ tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0;
+ tp->tso_frames = 0;
+ if (tp->tucso == 0) { // this is probably wrong
+ DBGOUT(TXSUM, "TCP/UDP: cso 0!\n");
+ tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
+ }
+ return;
+ } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
+ // data descriptor
+ tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
+ tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0;
+ } else {
+ // legacy descriptor
+ tp->cptse = 0;
+ }
+
+ if (vlan_enabled(s) && is_vlan_txd(txd_lower) &&
+ (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
+ tp->vlan_needed = 1;
+ cpu_to_be16wu((uint16_t *)(tp->vlan_header),
+ le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
+ cpu_to_be16wu((uint16_t *)(tp->vlan_header + 2),
+ le16_to_cpu(dp->upper.fields.special));
+ }
+
+ addr = le64_to_cpu(dp->buffer_addr);
+ if (tp->tse && tp->cptse) {
+ hdr = tp->hdr_len;
+ msh = hdr + tp->mss;
+ do {
+ bytes = split_size;
+ if (tp->size + bytes > msh)
+ bytes = msh - tp->size;
+ cpu_physical_memory_read(addr, tp->data + tp->size, bytes);
+ if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
+ memmove(tp->header, tp->data, hdr);
+ tp->size = sz;
+ addr += bytes;
+ if (sz == msh) {
+ xmit_seg(s);
+ memmove(tp->data, tp->header, hdr);
+ tp->size = hdr;
+ }
+ } while (split_size -= bytes);
+ } else if (!tp->tse && tp->cptse) {
+ // context descriptor TSE is not set, while data descriptor TSE is set
+ DBGOUT(TXERR, "TCP segmentaion Error\n");
+ } else {
+ cpu_physical_memory_read(addr, tp->data + tp->size, split_size);
+ tp->size += split_size;
+ }
+
+ if (!(txd_lower & E1000_TXD_CMD_EOP))
+ return;
+ if (!(tp->tse && tp->cptse && tp->size < hdr))
+ xmit_seg(s);
+ tp->tso_frames = 0;
+ tp->sum_needed = 0;
+ tp->vlan_needed = 0;
+ tp->size = 0;
+ tp->cptse = 0;
+}
+
+static uint32_t
+txdesc_writeback(target_phys_addr_t base, struct e1000_tx_desc *dp)
+{
+ uint32_t txd_upper, txd_lower = le32_to_cpu(dp->lower.data);
+
+ if (!(txd_lower & (E1000_TXD_CMD_RS|E1000_TXD_CMD_RPS)))
+ return 0;
+ txd_upper = (le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD) &
+ ~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU);
+ dp->upper.data = cpu_to_le32(txd_upper);
+ cpu_physical_memory_write(base + ((char *)&dp->upper - (char *)dp),
+ (void *)&dp->upper, sizeof(dp->upper));
+ return E1000_ICR_TXDW;
+}
+
+static void
+start_xmit(E1000State *s)
+{
+ target_phys_addr_t base;
+ struct e1000_tx_desc desc;
+ uint32_t tdh_start = s->mac_reg[TDH], cause = E1000_ICS_TXQE;
+
+ if (!(s->mac_reg[TCTL] & E1000_TCTL_EN)) {
+ DBGOUT(TX, "tx disabled\n");
+ return;
+ }
+
+ while (s->mac_reg[TDH] != s->mac_reg[TDT]) {
+ base = ((uint64_t)s->mac_reg[TDBAH] << 32) + s->mac_reg[TDBAL] +
+ sizeof(struct e1000_tx_desc) * s->mac_reg[TDH];
+ cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
+
+ DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH],
+ (void *)(intptr_t)desc.buffer_addr, desc.lower.data,
+ desc.upper.data);
+
+ process_tx_desc(s, &desc);
+ cause |= txdesc_writeback(base, &desc);
+
+ if (++s->mac_reg[TDH] * sizeof(desc) >= s->mac_reg[TDLEN])
+ s->mac_reg[TDH] = 0;
+ /*
+ * the following could happen only if guest sw assigns
+ * bogus values to TDT/TDLEN.
+ * there's nothing too intelligent we could do about this.
+ */
+ if (s->mac_reg[TDH] == tdh_start) {
+ DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
+ tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
+ break;
+ }
+ }
+ set_ics(s, 0, cause);
+}
+
+static int
+receive_filter(E1000State *s, const uint8_t *buf, int size)
+{
+ static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const int mta_shift[] = {4, 3, 2, 0};
+ uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp;
+
+ if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) {
+ uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
+ uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) +
+ ((vid >> 5) & 0x7f));
+ if ((vfta & (1 << (vid & 0x1f))) == 0)
+ return 0;
+ }
+
+ if (rctl & E1000_RCTL_UPE) // promiscuous
+ return 1;
+
+ if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE)) // promiscuous mcast
+ return 1;
+
+ if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast))
+ return 1;
+
+ for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) {
+ if (!(rp[1] & E1000_RAH_AV))
+ continue;
+ ra[0] = cpu_to_le32(rp[0]);
+ ra[1] = cpu_to_le32(rp[1]);
+ if (!memcmp(buf, (uint8_t *)ra, 6)) {
+ DBGOUT(RXFILTER,
+ "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ (int)(rp - s->mac_reg - RA)/2,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ return 1;
+ }
+ }
+ DBGOUT(RXFILTER, "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+ f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
+ f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff;
+ if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f)))
+ return 1;
+ DBGOUT(RXFILTER,
+ "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
+ (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5,
+ s->mac_reg[MTA + (f >> 5)]);
+
+ return 0;
+}
+
+static void
+e1000_set_link_status(VLANClientState *nc)
+{
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ uint32_t old_status = s->mac_reg[STATUS];
+
+ if (nc->link_down)
+ s->mac_reg[STATUS] &= ~E1000_STATUS_LU;
+ else
+ s->mac_reg[STATUS] |= E1000_STATUS_LU;
+
+ if (s->mac_reg[STATUS] != old_status)
+ set_ics(s, 0, E1000_ICR_LSC);
+}
+
+static int
+e1000_can_receive(VLANClientState *nc)
+{
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ return (s->mac_reg[RCTL] & E1000_RCTL_EN);
+}
+
+static ssize_t
+e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct e1000_rx_desc desc;
+ target_phys_addr_t base;
+ unsigned int n, rdt;
+ uint32_t rdh_start;
+ uint16_t vlan_special = 0;
+ uint8_t vlan_status = 0, vlan_offset = 0;
+ uint8_t min_buf[MIN_BUF_SIZE];
+
+ if (!(s->mac_reg[RCTL] & E1000_RCTL_EN))
+ return -1;
+
+ /* Pad to minimum Ethernet frame length */
+ if (size < sizeof(min_buf)) {
+ memcpy(min_buf, buf, size);
+ memset(&min_buf[size], 0, sizeof(min_buf) - size);
+ buf = min_buf;
+ size = sizeof(min_buf);
+ }
+
+ if (size > s->rxbuf_size) {
+ DBGOUT(RX, "packet too large for buffers (%lu > %d)\n",
+ (unsigned long)size, s->rxbuf_size);
+ return -1;
+ }
+
+ if (!receive_filter(s, buf, size))
+ return size;
+
+ if (vlan_enabled(s) && is_vlan_packet(s, buf)) {
+ vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14)));
+ memmove((uint8_t *)buf + 4, buf, 12);
+ vlan_status = E1000_RXD_STAT_VP;
+ vlan_offset = 4;
+ size -= 4;
+ }
+
+ rdh_start = s->mac_reg[RDH];
+ do {
+ if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) {
+ set_ics(s, 0, E1000_ICS_RXO);
+ return -1;
+ }
+ base = ((uint64_t)s->mac_reg[RDBAH] << 32) + s->mac_reg[RDBAL] +
+ sizeof(desc) * s->mac_reg[RDH];
+ cpu_physical_memory_read(base, (void *)&desc, sizeof(desc));
+ desc.special = vlan_special;
+ desc.status |= (vlan_status | E1000_RXD_STAT_DD);
+ if (desc.buffer_addr) {
+ cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr),
+ (void *)(buf + vlan_offset), size);
+ desc.length = cpu_to_le16(size + fcs_len(s));
+ desc.status |= E1000_RXD_STAT_EOP|E1000_RXD_STAT_IXSM;
+ } else { // as per intel docs; skip descriptors with null buf addr
+ DBGOUT(RX, "Null RX descriptor!!\n");
+ }
+ cpu_physical_memory_write(base, (void *)&desc, sizeof(desc));
+
+ if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
+ s->mac_reg[RDH] = 0;
+ s->check_rxov = 1;
+ /* see comment in start_xmit; same here */
+ if (s->mac_reg[RDH] == rdh_start) {
+ DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
+ rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
+ set_ics(s, 0, E1000_ICS_RXO);
+ return -1;
+ }
+ } while (desc.buffer_addr == 0);
+
+ s->mac_reg[GPRC]++;
+ s->mac_reg[TPR]++;
+ /* TOR - Total Octets Received:
+ * This register includes bytes received in a packet from the <Destination
+ * Address> field through the <CRC> field, inclusively.
+ */
+ n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
+ if (n < s->mac_reg[TORL])
+ s->mac_reg[TORH]++;
+ s->mac_reg[TORL] = n;
+
+ n = E1000_ICS_RXT0;
+ if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
+ rdt += s->mac_reg[RDLEN] / sizeof(desc);
+ if (((rdt - s->mac_reg[RDH]) * sizeof(desc)) <= s->mac_reg[RDLEN] >>
+ s->rxbuf_min_shift)
+ n |= E1000_ICS_RXDMT0;
+
+ set_ics(s, 0, n);
+
+ return size;
+}
+
+static uint32_t
+mac_readreg(E1000State *s, int index)
+{
+ return s->mac_reg[index];
+}
+
+static uint32_t
+mac_icr_read(E1000State *s, int index)
+{
+ uint32_t ret = s->mac_reg[ICR];
+
+ DBGOUT(INTERRUPT, "ICR read: %x\n", ret);
+ set_interrupt_cause(s, 0, 0);
+ return ret;
+}
+
+static uint32_t
+mac_read_clr4(E1000State *s, int index)
+{
+ uint32_t ret = s->mac_reg[index];
+
+ s->mac_reg[index] = 0;
+ return ret;
+}
+
+static uint32_t
+mac_read_clr8(E1000State *s, int index)
+{
+ uint32_t ret = s->mac_reg[index];
+
+ s->mac_reg[index] = 0;
+ s->mac_reg[index-1] = 0;
+ return ret;
+}
+
+static void
+mac_writereg(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[index] = val;
+}
+
+static void
+set_rdt(E1000State *s, int index, uint32_t val)
+{
+ s->check_rxov = 0;
+ s->mac_reg[index] = val & 0xffff;
+}
+
+static void
+set_16bit(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[index] = val & 0xffff;
+}
+
+static void
+set_dlen(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[index] = val & 0xfff80;
+}
+
+static void
+set_tctl(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[index] = val;
+ s->mac_reg[TDT] &= 0xffff;
+ start_xmit(s);
+}
+
+static void
+set_icr(E1000State *s, int index, uint32_t val)
+{
+ DBGOUT(INTERRUPT, "set_icr %x\n", val);
+ set_interrupt_cause(s, 0, s->mac_reg[ICR] & ~val);
+}
+
+static void
+set_imc(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[IMS] &= ~val;
+ set_ics(s, 0, 0);
+}
+
+static void
+set_ims(E1000State *s, int index, uint32_t val)
+{
+ s->mac_reg[IMS] |= val;
+ set_ics(s, 0, 0);
+}
+
+#define getreg(x) [x] = mac_readreg
+static uint32_t (*macreg_readops[])(E1000State *, int) = {
+ getreg(PBA), getreg(RCTL), getreg(TDH), getreg(TXDCTL),
+ getreg(WUFC), getreg(TDT), getreg(CTRL), getreg(LEDCTL),
+ getreg(MANC), getreg(MDIC), getreg(SWSM), getreg(STATUS),
+ getreg(TORL), getreg(TOTL), getreg(IMS), getreg(TCTL),
+ getreg(RDH), getreg(RDT), getreg(VET), getreg(ICS),
+ getreg(TDBAL), getreg(TDBAH), getreg(RDBAH), getreg(RDBAL),
+ getreg(TDLEN), getreg(RDLEN),
+
+ [TOTH] = mac_read_clr8, [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
+ [GPTC] = mac_read_clr4, [TPR] = mac_read_clr4, [TPT] = mac_read_clr4,
+ [ICR] = mac_icr_read, [EECD] = get_eecd, [EERD] = flash_eerd_read,
+ [CRCERRS ... MPC] = &mac_readreg,
+ [RA ... RA+31] = &mac_readreg,
+ [MTA ... MTA+127] = &mac_readreg,
+ [VFTA ... VFTA+127] = &mac_readreg,
+};
+enum { NREADOPS = ARRAY_SIZE(macreg_readops) };
+
+#define putreg(x) [x] = mac_writereg
+static void (*macreg_writeops[])(E1000State *, int, uint32_t) = {
+ putreg(PBA), putreg(EERD), putreg(SWSM), putreg(WUFC),
+ putreg(TDBAL), putreg(TDBAH), putreg(TXDCTL), putreg(RDBAH),
+ putreg(RDBAL), putreg(LEDCTL), putreg(VET),
+ [TDLEN] = set_dlen, [RDLEN] = set_dlen, [TCTL] = set_tctl,
+ [TDT] = set_tctl, [MDIC] = set_mdic, [ICS] = set_ics,
+ [TDH] = set_16bit, [RDH] = set_16bit, [RDT] = set_rdt,
+ [IMC] = set_imc, [IMS] = set_ims, [ICR] = set_icr,
+ [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
+ [RA ... RA+31] = &mac_writereg,
+ [MTA ... MTA+127] = &mac_writereg,
+ [VFTA ... VFTA+127] = &mac_writereg,
+};
+enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
+
+static void
+e1000_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ E1000State *s = opaque;
+ unsigned int index = (addr & 0x1ffff) >> 2;
+
+ if (index < NWRITEOPS && macreg_writeops[index]) {
+ macreg_writeops[index](s, index, val);
+ } else if (index < NREADOPS && macreg_readops[index]) {
+ DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04x\n", index<<2, val);
+ } else {
+ DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08x\n",
+ index<<2, val);
+ }
+}
+
+static void
+e1000_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ // emulate hw without byte enables: no RMW
+ e1000_mmio_writel(opaque, addr & ~3,
+ (val & 0xffff) << (8*(addr & 3)));
+}
+
+static void
+e1000_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ // emulate hw without byte enables: no RMW
+ e1000_mmio_writel(opaque, addr & ~3,
+ (val & 0xff) << (8*(addr & 3)));
+}
+
+static uint32_t
+e1000_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ E1000State *s = opaque;
+ unsigned int index = (addr & 0x1ffff) >> 2;
+
+ if (index < NREADOPS && macreg_readops[index])
+ {
+ return macreg_readops[index](s, index);
+ }
+ DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
+ return 0;
+}
+
+static uint32_t
+e1000_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return ((e1000_mmio_readl(opaque, addr & ~3)) >>
+ (8 * (addr & 3))) & 0xff;
+}
+
+static uint32_t
+e1000_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ return ((e1000_mmio_readl(opaque, addr & ~3)) >>
+ (8 * (addr & 3))) & 0xffff;
+}
+
+static bool is_version_1(void *opaque, int version_id)
+{
+ return version_id == 1;
+}
+
+static const VMStateDescription vmstate_e1000 = {
+ .name = "e1000",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, E1000State),
+ VMSTATE_UNUSED_TEST(is_version_1, 4), /* was instance id */
+ VMSTATE_UNUSED(4), /* Was mmio_base. */
+ VMSTATE_UINT32(rxbuf_size, E1000State),
+ VMSTATE_UINT32(rxbuf_min_shift, E1000State),
+ VMSTATE_UINT32(eecd_state.val_in, E1000State),
+ VMSTATE_UINT16(eecd_state.bitnum_in, E1000State),
+ VMSTATE_UINT16(eecd_state.bitnum_out, E1000State),
+ VMSTATE_UINT16(eecd_state.reading, E1000State),
+ VMSTATE_UINT32(eecd_state.old_eecd, E1000State),
+ VMSTATE_UINT8(tx.ipcss, E1000State),
+ VMSTATE_UINT8(tx.ipcso, E1000State),
+ VMSTATE_UINT16(tx.ipcse, E1000State),
+ VMSTATE_UINT8(tx.tucss, E1000State),
+ VMSTATE_UINT8(tx.tucso, E1000State),
+ VMSTATE_UINT16(tx.tucse, E1000State),
+ VMSTATE_UINT32(tx.paylen, E1000State),
+ VMSTATE_UINT8(tx.hdr_len, E1000State),
+ VMSTATE_UINT16(tx.mss, E1000State),
+ VMSTATE_UINT16(tx.size, E1000State),
+ VMSTATE_UINT16(tx.tso_frames, E1000State),
+ VMSTATE_UINT8(tx.sum_needed, E1000State),
+ VMSTATE_INT8(tx.ip, E1000State),
+ VMSTATE_INT8(tx.tcp, E1000State),
+ VMSTATE_BUFFER(tx.header, E1000State),
+ VMSTATE_BUFFER(tx.data, E1000State),
+ VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64),
+ VMSTATE_UINT16_ARRAY(phy_reg, E1000State, 0x20),
+ VMSTATE_UINT32(mac_reg[CTRL], E1000State),
+ VMSTATE_UINT32(mac_reg[EECD], E1000State),
+ VMSTATE_UINT32(mac_reg[EERD], E1000State),
+ VMSTATE_UINT32(mac_reg[GPRC], E1000State),
+ VMSTATE_UINT32(mac_reg[GPTC], E1000State),
+ VMSTATE_UINT32(mac_reg[ICR], E1000State),
+ VMSTATE_UINT32(mac_reg[ICS], E1000State),
+ VMSTATE_UINT32(mac_reg[IMC], E1000State),
+ VMSTATE_UINT32(mac_reg[IMS], E1000State),
+ VMSTATE_UINT32(mac_reg[LEDCTL], E1000State),
+ VMSTATE_UINT32(mac_reg[MANC], E1000State),
+ VMSTATE_UINT32(mac_reg[MDIC], E1000State),
+ VMSTATE_UINT32(mac_reg[MPC], E1000State),
+ VMSTATE_UINT32(mac_reg[PBA], E1000State),
+ VMSTATE_UINT32(mac_reg[RCTL], E1000State),
+ VMSTATE_UINT32(mac_reg[RDBAH], E1000State),
+ VMSTATE_UINT32(mac_reg[RDBAL], E1000State),
+ VMSTATE_UINT32(mac_reg[RDH], E1000State),
+ VMSTATE_UINT32(mac_reg[RDLEN], E1000State),
+ VMSTATE_UINT32(mac_reg[RDT], E1000State),
+ VMSTATE_UINT32(mac_reg[STATUS], E1000State),
+ VMSTATE_UINT32(mac_reg[SWSM], E1000State),
+ VMSTATE_UINT32(mac_reg[TCTL], E1000State),
+ VMSTATE_UINT32(mac_reg[TDBAH], E1000State),
+ VMSTATE_UINT32(mac_reg[TDBAL], E1000State),
+ VMSTATE_UINT32(mac_reg[TDH], E1000State),
+ VMSTATE_UINT32(mac_reg[TDLEN], E1000State),
+ VMSTATE_UINT32(mac_reg[TDT], E1000State),
+ VMSTATE_UINT32(mac_reg[TORH], E1000State),
+ VMSTATE_UINT32(mac_reg[TORL], E1000State),
+ VMSTATE_UINT32(mac_reg[TOTH], E1000State),
+ VMSTATE_UINT32(mac_reg[TOTL], E1000State),
+ VMSTATE_UINT32(mac_reg[TPR], E1000State),
+ VMSTATE_UINT32(mac_reg[TPT], E1000State),
+ VMSTATE_UINT32(mac_reg[TXDCTL], E1000State),
+ VMSTATE_UINT32(mac_reg[WUFC], E1000State),
+ VMSTATE_UINT32(mac_reg[VET], E1000State),
+ VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32),
+ VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128),
+ VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const uint16_t e1000_eeprom_template[64] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000,
+ 0x3000, 0x1000, 0x6403, E1000_DEVID, 0x8086, E1000_DEVID, 0x8086, 0x3040,
+ 0x0008, 0x2000, 0x7e14, 0x0048, 0x1000, 0x00d8, 0x0000, 0x2700,
+ 0x6cc9, 0x3150, 0x0722, 0x040b, 0x0984, 0x0000, 0xc000, 0x0706,
+ 0x1008, 0x0000, 0x0f04, 0x7fff, 0x4d01, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0100, 0x4000, 0x121c, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000,
+};
+
+static const uint16_t phy_reg_init[] = {
+ [PHY_CTRL] = 0x1140, [PHY_STATUS] = 0x796d, // link initially up
+ [PHY_ID1] = 0x141, [PHY_ID2] = PHY_ID2_INIT,
+ [PHY_1000T_CTRL] = 0x0e00, [M88E1000_PHY_SPEC_CTRL] = 0x360,
+ [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60, [PHY_AUTONEG_ADV] = 0xde1,
+ [PHY_LP_ABILITY] = 0x1e0, [PHY_1000T_STATUS] = 0x3c00,
+ [M88E1000_PHY_SPEC_STATUS] = 0xac00,
+};
+
+static const uint32_t mac_reg_init[] = {
+ [PBA] = 0x00100030,
+ [LEDCTL] = 0x602,
+ [CTRL] = E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
+ E1000_CTRL_SPD_1000 | E1000_CTRL_SLU,
+ [STATUS] = 0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
+ E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
+ E1000_STATUS_SPEED_1000 | E1000_STATUS_FD |
+ E1000_STATUS_LU,
+ [MANC] = E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
+ E1000_MANC_ARP_EN | E1000_MANC_0298_EN |
+ E1000_MANC_RMCP_EN,
+};
+
+/* PCI interface */
+
+static CPUWriteMemoryFunc * const e1000_mmio_write[] = {
+ e1000_mmio_writeb, e1000_mmio_writew, e1000_mmio_writel
+};
+
+static CPUReadMemoryFunc * const e1000_mmio_read[] = {
+ e1000_mmio_readb, e1000_mmio_readw, e1000_mmio_readl
+};
+
+static void
+e1000_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
+ int i;
+ const uint32_t excluded_regs[] = {
+ E1000_MDIC, E1000_ICR, E1000_ICS, E1000_IMS,
+ E1000_IMC, E1000_TCTL, E1000_TDT, PNPMMIO_SIZE
+ };
+
+
+ DBGOUT(MMIO, "e1000_mmio_map addr=0x%08"FMT_PCIBUS" 0x%08"FMT_PCIBUS"\n",
+ addr, size);
+
+ cpu_register_physical_memory(addr, PNPMMIO_SIZE, d->mmio_index);
+ qemu_register_coalesced_mmio(addr, excluded_regs[0]);
+
+ for (i = 0; excluded_regs[i] != PNPMMIO_SIZE; i++)
+ qemu_register_coalesced_mmio(addr + excluded_regs[i] + 4,
+ excluded_regs[i + 1] -
+ excluded_regs[i] - 4);
+}
+
+static void
+e1000_cleanup(VLANClientState *nc)
+{
+ E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static int
+pci_e1000_uninit(PCIDevice *dev)
+{
+ E1000State *d = DO_UPCAST(E1000State, dev, dev);
+
+ cpu_unregister_io_memory(d->mmio_index);
+ qemu_del_vlan_client(&d->nic->nc);
+ return 0;
+}
+
+static void e1000_reset(void *opaque)
+{
+ E1000State *d = opaque;
+
+ memset(d->phy_reg, 0, sizeof d->phy_reg);
+ memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init);
+ memset(d->mac_reg, 0, sizeof d->mac_reg);
+ memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init);
+ d->rxbuf_min_shift = 1;
+ memset(&d->tx, 0, sizeof d->tx);
+}
+
+static NetClientInfo net_e1000_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = e1000_can_receive,
+ .receive = e1000_receive,
+ .cleanup = e1000_cleanup,
+ .link_status_changed = e1000_set_link_status,
+};
+
+static int pci_e1000_init(PCIDevice *pci_dev)
+{
+ E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
+ uint8_t *pci_conf;
+ uint16_t checksum = 0;
+ int i;
+ uint8_t *macaddr;
+
+ pci_conf = d->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, E1000_DEVID);
+ /* TODO: we have no capabilities, so why is this bit set? */
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST);
+ pci_conf[PCI_REVISION_ID] = 0x03;
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ /* TODO: RST# value should be 0, PCI spec 6.2.4 */
+ pci_conf[PCI_CACHE_LINE_SIZE] = 0x10;
+
+ /* TODO: RST# value should be 0 if programmable, PCI spec 6.2.4 */
+ pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0
+
+ d->mmio_index = cpu_register_io_memory(e1000_mmio_read,
+ e1000_mmio_write, d, DEVICE_LITTLE_ENDIAN);
+
+ pci_register_bar(&d->dev, 0, PNPMMIO_SIZE,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, e1000_mmio_map);
+
+ pci_register_bar(&d->dev, 1, IOPORT_SIZE,
+ PCI_BASE_ADDRESS_SPACE_IO, ioport_map);
+
+ memmove(d->eeprom_data, e1000_eeprom_template,
+ sizeof e1000_eeprom_template);
+ qemu_macaddr_default_if_unset(&d->conf.macaddr);
+ macaddr = d->conf.macaddr.a;
+ for (i = 0; i < 3; i++)
+ d->eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i];
+ for (i = 0; i < EEPROM_CHECKSUM_REG; i++)
+ checksum += d->eeprom_data[i];
+ checksum = (uint16_t) EEPROM_SUM - checksum;
+ d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
+
+ d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
+ d->dev.qdev.info->name, d->dev.qdev.id, d);
+
+ qemu_format_nic_info_str(&d->nic->nc, macaddr);
+
+ add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
+ return 0;
+}
+
+static void qdev_e1000_reset(DeviceState *dev)
+{
+ E1000State *d = DO_UPCAST(E1000State, dev.qdev, dev);
+ e1000_reset(d);
+}
+
+static PCIDeviceInfo e1000_info = {
+ .qdev.name = "e1000",
+ .qdev.desc = "Intel Gigabit Ethernet",
+ .qdev.size = sizeof(E1000State),
+ .qdev.reset = qdev_e1000_reset,
+ .qdev.vmsd = &vmstate_e1000,
+ .init = pci_e1000_init,
+ .exit = pci_e1000_uninit,
+ .romfile = "pxe-e1000.bin",
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(E1000State, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void e1000_register_devices(void)
+{
+ pci_qdev_register(&e1000_info);
+}
+
+device_init(e1000_register_devices)
diff --git a/hw/e1000_hw.h b/hw/e1000_hw.h
new file mode 100644
index 0000000..9bd8a4b
--- /dev/null
+++ b/hw/e1000_hw.h
@@ -0,0 +1,864 @@
+/*******************************************************************************
+
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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/>.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* e1000_hw.h
+ * Structures, enums, and macros for the MAC
+ */
+
+#ifndef _E1000_HW_H_
+#define _E1000_HW_H_
+
+
+/* PCI Device IDs */
+#define E1000_DEV_ID_82542 0x1000
+#define E1000_DEV_ID_82543GC_FIBER 0x1001
+#define E1000_DEV_ID_82543GC_COPPER 0x1004
+#define E1000_DEV_ID_82544EI_COPPER 0x1008
+#define E1000_DEV_ID_82544EI_FIBER 0x1009
+#define E1000_DEV_ID_82544GC_COPPER 0x100C
+#define E1000_DEV_ID_82544GC_LOM 0x100D
+#define E1000_DEV_ID_82540EM 0x100E
+#define E1000_DEV_ID_82540EM_LOM 0x1015
+#define E1000_DEV_ID_82540EP_LOM 0x1016
+#define E1000_DEV_ID_82540EP 0x1017
+#define E1000_DEV_ID_82540EP_LP 0x101E
+#define E1000_DEV_ID_82545EM_COPPER 0x100F
+#define E1000_DEV_ID_82545EM_FIBER 0x1011
+#define E1000_DEV_ID_82545GM_COPPER 0x1026
+#define E1000_DEV_ID_82545GM_FIBER 0x1027
+#define E1000_DEV_ID_82545GM_SERDES 0x1028
+#define E1000_DEV_ID_82546EB_COPPER 0x1010
+#define E1000_DEV_ID_82546EB_FIBER 0x1012
+#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D
+#define E1000_DEV_ID_82541EI 0x1013
+#define E1000_DEV_ID_82541EI_MOBILE 0x1018
+#define E1000_DEV_ID_82541ER_LOM 0x1014
+#define E1000_DEV_ID_82541ER 0x1078
+#define E1000_DEV_ID_82547GI 0x1075
+#define E1000_DEV_ID_82541GI 0x1076
+#define E1000_DEV_ID_82541GI_MOBILE 0x1077
+#define E1000_DEV_ID_82541GI_LF 0x107C
+#define E1000_DEV_ID_82546GB_COPPER 0x1079
+#define E1000_DEV_ID_82546GB_FIBER 0x107A
+#define E1000_DEV_ID_82546GB_SERDES 0x107B
+#define E1000_DEV_ID_82546GB_PCIE 0x108A
+#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099
+#define E1000_DEV_ID_82547EI 0x1019
+#define E1000_DEV_ID_82547EI_MOBILE 0x101A
+#define E1000_DEV_ID_82571EB_COPPER 0x105E
+#define E1000_DEV_ID_82571EB_FIBER 0x105F
+#define E1000_DEV_ID_82571EB_SERDES 0x1060
+#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4
+#define E1000_DEV_ID_82571PT_QUAD_COPPER 0x10D5
+#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5
+#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC
+#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9
+#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA
+#define E1000_DEV_ID_82572EI_COPPER 0x107D
+#define E1000_DEV_ID_82572EI_FIBER 0x107E
+#define E1000_DEV_ID_82572EI_SERDES 0x107F
+#define E1000_DEV_ID_82572EI 0x10B9
+#define E1000_DEV_ID_82573E 0x108B
+#define E1000_DEV_ID_82573E_IAMT 0x108C
+#define E1000_DEV_ID_82573L 0x109A
+#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5
+#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096
+#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098
+#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA
+#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB
+
+#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049
+#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A
+#define E1000_DEV_ID_ICH8_IGP_C 0x104B
+#define E1000_DEV_ID_ICH8_IFE 0x104C
+#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4
+#define E1000_DEV_ID_ICH8_IFE_G 0x10C5
+#define E1000_DEV_ID_ICH8_IGP_M 0x104D
+
+/* Register Set. (82543, 82544)
+ *
+ * Registers are defined to be 32 bits and should be accessed as 32 bit values.
+ * These registers are physically located on the NIC, but are mapped into the
+ * host memory address space.
+ *
+ * RW - register is both readable and writable
+ * RO - register is read only
+ * WO - register is write only
+ * R/clr - register is read only and is cleared when read
+ * A - register array
+ */
+#define E1000_CTRL 0x00000 /* Device Control - RW */
+#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */
+#define E1000_STATUS 0x00008 /* Device Status - RO */
+#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */
+#define E1000_EERD 0x00014 /* EEPROM Read - RW */
+#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */
+#define E1000_FLA 0x0001C /* Flash Access - RW */
+#define E1000_MDIC 0x00020 /* MDI Control - RW */
+#define E1000_SCTL 0x00024 /* SerDes Control - RW */
+#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */
+#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
+#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
+#define E1000_FCT 0x00030 /* Flow Control Type - RW */
+#define E1000_VET 0x00038 /* VLAN Ether Type - RW */
+#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */
+#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */
+#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */
+#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */
+#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */
+#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */
+#define E1000_RCTL 0x00100 /* RX Control - RW */
+#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */
+#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */
+#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */
+#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */
+#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */
+#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */
+#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */
+#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */
+#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */
+#define E1000_TCTL 0x00400 /* TX Control - RW */
+#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */
+#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */
+#define E1000_TBT 0x00448 /* TX Burst Timer - RW */
+#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */
+#define E1000_LEDCTL 0x00E00 /* LED Control - RW */
+#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */
+#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */
+#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */
+#define FEXTNVM_SW_CONFIG 0x0001
+#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */
+#define E1000_PBS 0x01008 /* Packet Buffer Size */
+#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */
+#define E1000_FLASH_UPDATES 1000
+#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */
+#define E1000_FLASHT 0x01028 /* FLASH Timer Register */
+#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */
+#define E1000_FLSWCTL 0x01030 /* FLASH control register */
+#define E1000_FLSWDATA 0x01034 /* FLASH data register */
+#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */
+#define E1000_FLOP 0x0103C /* FLASH Opcode Register */
+#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */
+#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */
+#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */
+#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */
+#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */
+#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */
+#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */
+#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */
+#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */
+#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */
+#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */
+#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */
+#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */
+#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */
+#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */
+#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */
+#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */
+#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */
+#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */
+#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */
+#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */
+#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */
+#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */
+#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */
+#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */
+#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */
+#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */
+#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */
+#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */
+#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */
+#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */
+#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */
+#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */
+#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */
+#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */
+#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */
+#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */
+#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */
+#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */
+#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */
+#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */
+#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */
+#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */
+#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */
+#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */
+#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */
+#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */
+#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */
+#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */
+#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */
+#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */
+#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */
+#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */
+#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */
+#define E1000_COLC 0x04028 /* Collision Count - R/clr */
+#define E1000_DC 0x04030 /* Defer Count - R/clr */
+#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */
+#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */
+#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */
+#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */
+#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */
+#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */
+#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */
+#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */
+#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */
+#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */
+#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */
+#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */
+#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */
+#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */
+#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */
+#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */
+#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */
+#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */
+#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */
+#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */
+#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */
+#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */
+#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */
+#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */
+#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */
+#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */
+#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */
+#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */
+#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */
+#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */
+#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */
+#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */
+#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */
+#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */
+#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */
+#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */
+#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */
+#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */
+#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */
+#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */
+#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */
+#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */
+#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */
+#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */
+#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */
+#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */
+#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */
+#define E1000_IAC 0x04100 /* Interrupt Assertion Count */
+#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */
+#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */
+#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */
+#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */
+#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */
+#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */
+#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */
+#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */
+#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */
+#define E1000_RFCTL 0x05008 /* Receive Filter Control*/
+#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */
+#define E1000_RA 0x05400 /* Receive Address - RW Array */
+#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */
+#define E1000_WUC 0x05800 /* Wakeup Control - RW */
+#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */
+#define E1000_WUS 0x05810 /* Wakeup Status - RO */
+#define E1000_MANC 0x05820 /* Management Control - RW */
+#define E1000_IPAV 0x05838 /* IP Address Valid - RW */
+#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */
+#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */
+#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */
+#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */
+#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */
+#define E1000_HOST_IF 0x08800 /* Host Interface */
+#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */
+#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */
+
+#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */
+#define E1000_MDPHYA 0x0003C /* PHY address - RW */
+#define E1000_MANC2H 0x05860 /* Managment Control To Host - RW */
+#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */
+
+#define E1000_GCR 0x05B00 /* PCI-Ex Control */
+#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */
+#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */
+#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */
+#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */
+#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */
+#define E1000_SWSM 0x05B50 /* SW Semaphore */
+#define E1000_FWSM 0x05B54 /* FW Semaphore */
+#define E1000_FFLT_DBG 0x05F04 /* Debug Register */
+#define E1000_HICR 0x08F00 /* Host Inteface Control */
+
+/* RSS registers */
+#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */
+#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */
+#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */
+#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */
+#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */
+#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */
+
+/* PHY 1000 MII Register/Bit Definitions */
+/* PHY Registers defined by IEEE */
+#define PHY_CTRL 0x00 /* Control Register */
+#define PHY_STATUS 0x01 /* Status Regiser */
+#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */
+#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */
+#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */
+#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */
+#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */
+#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
+#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */
+
+#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */
+#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */
+
+/* M88E1000 Specific Registers */
+#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */
+#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */
+#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */
+#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */
+#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */
+#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */
+
+#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */
+#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */
+#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */
+#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */
+#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */
+
+/* Interrupt Cause Read */
+#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */
+#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */
+#define E1000_ICR_LSC 0x00000004 /* Link Status Change */
+#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */
+#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */
+#define E1000_ICR_RXO 0x00000040 /* rx overrun */
+#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */
+#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */
+#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */
+#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */
+#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */
+#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */
+#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */
+#define E1000_ICR_TXD_LOW 0x00008000
+#define E1000_ICR_SRPD 0x00010000
+#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */
+#define E1000_ICR_MNG 0x00040000 /* Manageability event */
+#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */
+#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */
+#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */
+#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */
+#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */
+#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */
+#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */
+#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */
+
+/* Interrupt Cause Set */
+#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_ICS_SRPD E1000_ICR_SRPD
+#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */
+#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */
+#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */
+#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */
+#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_ICS_DSW E1000_ICR_DSW
+#define E1000_ICS_PHYINT E1000_ICR_PHYINT
+#define E1000_ICS_EPRST E1000_ICR_EPRST
+
+/* Interrupt Mask Set */
+#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_IMS_SRPD E1000_ICR_SRPD
+#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */
+#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */
+#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */
+#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */
+#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_IMS_DSW E1000_ICR_DSW
+#define E1000_IMS_PHYINT E1000_ICR_PHYINT
+#define E1000_IMS_EPRST E1000_ICR_EPRST
+
+/* Interrupt Mask Clear */
+#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */
+#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */
+#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */
+#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */
+#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */
+#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */
+#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */
+#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */
+#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */
+#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */
+#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */
+#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */
+#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */
+#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW
+#define E1000_IMC_SRPD E1000_ICR_SRPD
+#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */
+#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */
+#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */
+#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */
+#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */
+#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */
+#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */
+#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */
+#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */
+#define E1000_IMC_DSW E1000_ICR_DSW
+#define E1000_IMC_PHYINT E1000_ICR_PHYINT
+#define E1000_IMC_EPRST E1000_ICR_EPRST
+
+/* Receive Control */
+#define E1000_RCTL_RST 0x00000001 /* Software reset */
+#define E1000_RCTL_EN 0x00000002 /* enable */
+#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
+#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
+#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
+#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
+#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
+#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
+#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */
+#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */
+#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
+#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
+#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
+#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
+#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
+#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
+#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
+#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
+#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
+#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */
+#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */
+
+
+#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */
+#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */
+#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */
+#define E1000_EEPROM_RW_REG_DONE 0x10 /* Offset to READ/WRITE done bit */
+#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */
+#define E1000_EEPROM_RW_ADDR_SHIFT 8 /* Shift to the address bits */
+#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */
+#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */
+/* Register Bit Masks */
+/* Device Control */
+#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */
+#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */
+#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */
+#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */
+#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */
+#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */
+#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */
+#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */
+#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */
+#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */
+#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */
+#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */
+#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */
+#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */
+#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */
+#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */
+#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */
+#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */
+#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */
+#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */
+#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
+#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
+#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
+#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */
+#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */
+#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */
+#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */
+#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */
+#define E1000_CTRL_RST 0x04000000 /* Global reset */
+#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
+#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
+#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */
+#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
+#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */
+#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */
+
+/* Device Status */
+#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
+#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */
+#define E1000_STATUS_FUNC_SHIFT 2
+#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */
+#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */
+#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
+#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */
+#define E1000_STATUS_SPEED_MASK 0x000000C0
+#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */
+#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
+#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion
+ by EEPROM/Flash */
+#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */
+#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */
+#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */
+#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */
+#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */
+#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */
+#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */
+#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */
+#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */
+#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */
+#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */
+#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */
+#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */
+#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */
+#define E1000_STATUS_FUSE_8 0x04000000
+#define E1000_STATUS_FUSE_9 0x08000000
+#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */
+#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */
+
+/* EEPROM/Flash Control */
+#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */
+#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */
+#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */
+#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */
+#define E1000_EECD_FWE_MASK 0x00000030
+#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */
+#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */
+#define E1000_EECD_FWE_SHIFT 4
+#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */
+#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */
+#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */
+#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */
+#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type
+ * (0-small, 1-large) */
+#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */
+#ifndef E1000_EEPROM_GRANT_ATTEMPTS
+#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
+#endif
+#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */
+#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */
+#define E1000_EECD_SIZE_EX_SHIFT 11
+#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */
+#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */
+#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */
+#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */
+#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */
+#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */
+#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */
+#define E1000_EECD_SECVAL_SHIFT 22
+#define E1000_STM_OPCODE 0xDB00
+#define E1000_HICR_FW_RESET 0xC0
+
+#define E1000_SHADOW_RAM_WORDS 2048
+#define E1000_ICH_NVM_SIG_WORD 0x13
+#define E1000_ICH_NVM_SIG_MASK 0xC0
+
+/* MDI Control */
+#define E1000_MDIC_DATA_MASK 0x0000FFFF
+#define E1000_MDIC_REG_MASK 0x001F0000
+#define E1000_MDIC_REG_SHIFT 16
+#define E1000_MDIC_PHY_MASK 0x03E00000
+#define E1000_MDIC_PHY_SHIFT 21
+#define E1000_MDIC_OP_WRITE 0x04000000
+#define E1000_MDIC_OP_READ 0x08000000
+#define E1000_MDIC_READY 0x10000000
+#define E1000_MDIC_INT_EN 0x20000000
+#define E1000_MDIC_ERROR 0x40000000
+
+/* EEPROM Commands - Microwire */
+#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */
+#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */
+#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */
+#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */
+#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */
+
+/* EEPROM Word Offsets */
+#define EEPROM_COMPAT 0x0003
+#define EEPROM_ID_LED_SETTINGS 0x0004
+#define EEPROM_VERSION 0x0005
+#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */
+#define EEPROM_PHY_CLASS_WORD 0x0007
+#define EEPROM_INIT_CONTROL1_REG 0x000A
+#define EEPROM_INIT_CONTROL2_REG 0x000F
+#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010
+#define EEPROM_INIT_CONTROL3_PORT_B 0x0014
+#define EEPROM_INIT_3GIO_3 0x001A
+#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020
+#define EEPROM_INIT_CONTROL3_PORT_A 0x0024
+#define EEPROM_CFG 0x0012
+#define EEPROM_FLASH_VERSION 0x0032
+#define EEPROM_CHECKSUM_REG 0x003F
+
+#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */
+#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */
+
+/* Transmit Descriptor */
+struct e1000_tx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t cso; /* Checksum offset */
+ uint8_t cmd; /* Descriptor control */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t css; /* Checksum start */
+ uint16_t special;
+ } fields;
+ } upper;
+};
+
+/* Transmit Descriptor bit definitions */
+#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */
+#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */
+#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
+#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
+#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */
+#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */
+#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */
+#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */
+#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */
+#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
+#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */
+#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */
+#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */
+#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */
+#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */
+#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */
+#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */
+#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */
+#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */
+
+/* Transmit Control */
+#define E1000_TCTL_RST 0x00000001 /* software reset */
+#define E1000_TCTL_EN 0x00000002 /* enable tx */
+#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
+#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
+#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
+#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
+#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
+#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
+#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
+#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */
+
+/* Receive Descriptor */
+struct e1000_rx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ uint16_t length; /* Length of data DMAed into data buffer */
+ uint16_t csum; /* Packet checksum */
+ uint8_t status; /* Descriptor status */
+ uint8_t errors; /* Descriptor Errors */
+ uint16_t special;
+};
+
+/* Receive Descriptor bit definitions */
+#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
+#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */
+#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */
+#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */
+#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */
+#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */
+#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */
+#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */
+#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */
+#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */
+#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */
+#define E1000_RXD_ERR_CE 0x01 /* CRC Error */
+#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */
+#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */
+#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */
+#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */
+#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */
+#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */
+#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */
+#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */
+#define E1000_RXD_SPC_PRI_SHIFT 13
+#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */
+#define E1000_RXD_SPC_CFI_SHIFT 12
+
+#define E1000_RXDEXT_STATERR_CE 0x01000000
+#define E1000_RXDEXT_STATERR_SE 0x02000000
+#define E1000_RXDEXT_STATERR_SEQ 0x04000000
+#define E1000_RXDEXT_STATERR_CXE 0x10000000
+#define E1000_RXDEXT_STATERR_TCPE 0x20000000
+#define E1000_RXDEXT_STATERR_IPE 0x40000000
+#define E1000_RXDEXT_STATERR_RXE 0x80000000
+
+#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000
+#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF
+
+/* Receive Address */
+#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */
+
+/* Offload Context Descriptor */
+struct e1000_context_desc {
+ union {
+ uint32_t ip_config;
+ struct {
+ uint8_t ipcss; /* IP checksum start */
+ uint8_t ipcso; /* IP checksum offset */
+ uint16_t ipcse; /* IP checksum end */
+ } ip_fields;
+ } lower_setup;
+ union {
+ uint32_t tcp_config;
+ struct {
+ uint8_t tucss; /* TCP checksum start */
+ uint8_t tucso; /* TCP checksum offset */
+ uint16_t tucse; /* TCP checksum end */
+ } tcp_fields;
+ } upper_setup;
+ uint32_t cmd_and_length; /* */
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t hdr_len; /* Header length */
+ uint16_t mss; /* Maximum segment size */
+ } fields;
+ } tcp_seg_setup;
+};
+
+/* Offload data descriptor */
+struct e1000_data_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's buffer address */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t typ_len_ext; /* */
+ uint8_t cmd; /* */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t popts; /* Packet Options */
+ uint16_t special; /* */
+ } fields;
+ } upper;
+};
+
+/* Management Control */
+#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */
+#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */
+#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */
+#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */
+#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */
+#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */
+#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */
+#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */
+#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */
+#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery
+ * Filtering */
+#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */
+#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */
+#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */
+#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */
+#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */
+#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */
+#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address
+ * filtering */
+#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host
+ * memory */
+#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address
+ * filtering */
+#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */
+#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */
+#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */
+#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */
+#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */
+#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */
+#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */
+#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */
+
+#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */
+#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */
+
+/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */
+#define EEPROM_SUM 0xBABA
+
+#endif /* _E1000_HW_H_ */
diff --git a/hw/ecc.c b/hw/ecc.c
new file mode 100644
index 0000000..a75408b
--- /dev/null
+++ b/hw/ecc.c
@@ -0,0 +1,88 @@
+/*
+ * Calculate Error-correcting Codes. Used by NAND Flash controllers
+ * (not by NAND chips).
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "flash.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux.
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+ 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+ 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+ 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+ 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+ 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+ 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+ 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+ 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+ 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+ 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+ 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+ 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+ 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+ 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+ 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+};
+
+/* Update ECC parity count. */
+uint8_t ecc_digest(ECCState *s, uint8_t sample)
+{
+ uint8_t idx = nand_ecc_precalc_table[sample];
+
+ s->cp ^= idx & 0x3f;
+ if (idx & 0x40) {
+ s->lp[0] ^= ~s->count;
+ s->lp[1] ^= s->count;
+ }
+ s->count ++;
+
+ return sample;
+}
+
+/* Reinitialise the counters. */
+void ecc_reset(ECCState *s)
+{
+ s->lp[0] = 0x0000;
+ s->lp[1] = 0x0000;
+ s->cp = 0x00;
+ s->count = 0;
+}
+
+/* Save/restore */
+VMStateDescription vmstate_ecc_state = {
+ .name = "ecc-state",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(cp, ECCState),
+ VMSTATE_UINT16_ARRAY(lp, ECCState, 2),
+ VMSTATE_UINT16(count, ECCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
diff --git a/hw/eccmemctl.c b/hw/eccmemctl.c
new file mode 100644
index 0000000..2bda87b
--- /dev/null
+++ b/hw/eccmemctl.c
@@ -0,0 +1,332 @@
+/*
+ * QEMU Sparc Sun4m ECC memory controller emulation
+ *
+ * Copyright (c) 2007 Robert Reif
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "trace.h"
+
+/* There are 3 versions of this chip used in SMP sun4m systems:
+ * MCC (version 0, implementation 0) SS-600MP
+ * EMC (version 0, implementation 1) SS-10
+ * SMC (version 0, implementation 2) SS-10SX and SS-20
+ *
+ * Chipset docs:
+ * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01,
+ * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf
+ */
+
+#define ECC_MCC 0x00000000
+#define ECC_EMC 0x10000000
+#define ECC_SMC 0x20000000
+
+/* Register indexes */
+#define ECC_MER 0 /* Memory Enable Register */
+#define ECC_MDR 1 /* Memory Delay Register */
+#define ECC_MFSR 2 /* Memory Fault Status Register */
+#define ECC_VCR 3 /* Video Configuration Register */
+#define ECC_MFAR0 4 /* Memory Fault Address Register 0 */
+#define ECC_MFAR1 5 /* Memory Fault Address Register 1 */
+#define ECC_DR 6 /* Diagnostic Register */
+#define ECC_ECR0 7 /* Event Count Register 0 */
+#define ECC_ECR1 8 /* Event Count Register 1 */
+
+/* ECC fault control register */
+#define ECC_MER_EE 0x00000001 /* Enable ECC checking */
+#define ECC_MER_EI 0x00000002 /* Enable Interrupts on
+ correctable errors */
+#define ECC_MER_MRR0 0x00000004 /* SIMM 0 */
+#define ECC_MER_MRR1 0x00000008 /* SIMM 1 */
+#define ECC_MER_MRR2 0x00000010 /* SIMM 2 */
+#define ECC_MER_MRR3 0x00000020 /* SIMM 3 */
+#define ECC_MER_MRR4 0x00000040 /* SIMM 4 */
+#define ECC_MER_MRR5 0x00000080 /* SIMM 5 */
+#define ECC_MER_MRR6 0x00000100 /* SIMM 6 */
+#define ECC_MER_MRR7 0x00000200 /* SIMM 7 */
+#define ECC_MER_REU 0x00000100 /* Memory Refresh Enable (600MP) */
+#define ECC_MER_MRR 0x000003fc /* MRR mask */
+#define ECC_MER_A 0x00000400 /* Memory controller addr map select */
+#define ECC_MER_DCI 0x00000800 /* Disables Coherent Invalidate ACK */
+#define ECC_MER_VER 0x0f000000 /* Version */
+#define ECC_MER_IMPL 0xf0000000 /* Implementation */
+#define ECC_MER_MASK_0 0x00000103 /* Version 0 (MCC) mask */
+#define ECC_MER_MASK_1 0x00000bff /* Version 1 (EMC) mask */
+#define ECC_MER_MASK_2 0x00000bff /* Version 2 (SMC) mask */
+
+/* ECC memory delay register */
+#define ECC_MDR_RRI 0x000003ff /* Refresh Request Interval */
+#define ECC_MDR_MI 0x00001c00 /* MIH Delay */
+#define ECC_MDR_CI 0x0000e000 /* Coherent Invalidate Delay */
+#define ECC_MDR_MDL 0x001f0000 /* MBus Master arbitration delay */
+#define ECC_MDR_MDH 0x03e00000 /* MBus Master arbitration delay */
+#define ECC_MDR_GAD 0x7c000000 /* Graphics Arbitration Delay */
+#define ECC_MDR_RSC 0x80000000 /* Refresh load control */
+#define ECC_MDR_MASK 0x7fffffff
+
+/* ECC fault status register */
+#define ECC_MFSR_CE 0x00000001 /* Correctable error */
+#define ECC_MFSR_BS 0x00000002 /* C2 graphics bad slot access */
+#define ECC_MFSR_TO 0x00000004 /* Timeout on write */
+#define ECC_MFSR_UE 0x00000008 /* Uncorrectable error */
+#define ECC_MFSR_DW 0x000000f0 /* Index of double word in block */
+#define ECC_MFSR_SYND 0x0000ff00 /* Syndrome for correctable error */
+#define ECC_MFSR_ME 0x00010000 /* Multiple errors */
+#define ECC_MFSR_C2ERR 0x00020000 /* C2 graphics error */
+
+/* ECC fault address register 0 */
+#define ECC_MFAR0_PADDR 0x0000000f /* PA[32-35] */
+#define ECC_MFAR0_TYPE 0x000000f0 /* Transaction type */
+#define ECC_MFAR0_SIZE 0x00000700 /* Transaction size */
+#define ECC_MFAR0_CACHE 0x00000800 /* Mapped cacheable */
+#define ECC_MFAR0_LOCK 0x00001000 /* Error occurred in atomic cycle */
+#define ECC_MFAR0_BMODE 0x00002000 /* Boot mode */
+#define ECC_MFAR0_VADDR 0x003fc000 /* VA[12-19] (superset bits) */
+#define ECC_MFAR0_S 0x08000000 /* Supervisor mode */
+#define ECC_MFARO_MID 0xf0000000 /* Module ID */
+
+/* ECC diagnostic register */
+#define ECC_DR_CBX 0x00000001
+#define ECC_DR_CB0 0x00000002
+#define ECC_DR_CB1 0x00000004
+#define ECC_DR_CB2 0x00000008
+#define ECC_DR_CB4 0x00000010
+#define ECC_DR_CB8 0x00000020
+#define ECC_DR_CB16 0x00000040
+#define ECC_DR_CB32 0x00000080
+#define ECC_DR_DMODE 0x00000c00
+
+#define ECC_NREGS 9
+#define ECC_SIZE (ECC_NREGS * sizeof(uint32_t))
+
+#define ECC_DIAG_SIZE 4
+#define ECC_DIAG_MASK (ECC_DIAG_SIZE - 1)
+
+typedef struct ECCState {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t regs[ECC_NREGS];
+ uint8_t diag[ECC_DIAG_SIZE];
+ uint32_t version;
+} ECCState;
+
+static void ecc_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ ECCState *s = opaque;
+
+ switch (addr >> 2) {
+ case ECC_MER:
+ if (s->version == ECC_MCC)
+ s->regs[ECC_MER] = (val & ECC_MER_MASK_0);
+ else if (s->version == ECC_EMC)
+ s->regs[ECC_MER] = s->version | (val & ECC_MER_MASK_1);
+ else if (s->version == ECC_SMC)
+ s->regs[ECC_MER] = s->version | (val & ECC_MER_MASK_2);
+ trace_ecc_mem_writel_mer(val);
+ break;
+ case ECC_MDR:
+ s->regs[ECC_MDR] = val & ECC_MDR_MASK;
+ trace_ecc_mem_writel_mdr(val);
+ break;
+ case ECC_MFSR:
+ s->regs[ECC_MFSR] = val;
+ qemu_irq_lower(s->irq);
+ trace_ecc_mem_writel_mfsr(val);
+ break;
+ case ECC_VCR:
+ s->regs[ECC_VCR] = val;
+ trace_ecc_mem_writel_vcr(val);
+ break;
+ case ECC_DR:
+ s->regs[ECC_DR] = val;
+ trace_ecc_mem_writel_dr(val);
+ break;
+ case ECC_ECR0:
+ s->regs[ECC_ECR0] = val;
+ trace_ecc_mem_writel_ecr0(val);
+ break;
+ case ECC_ECR1:
+ s->regs[ECC_ECR0] = val;
+ trace_ecc_mem_writel_ecr1(val);
+ break;
+ }
+}
+
+static uint32_t ecc_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ ECCState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (addr >> 2) {
+ case ECC_MER:
+ ret = s->regs[ECC_MER];
+ trace_ecc_mem_readl_mer(ret);
+ break;
+ case ECC_MDR:
+ ret = s->regs[ECC_MDR];
+ trace_ecc_mem_readl_mdr(ret);
+ break;
+ case ECC_MFSR:
+ ret = s->regs[ECC_MFSR];
+ trace_ecc_mem_readl_mfsr(ret);
+ break;
+ case ECC_VCR:
+ ret = s->regs[ECC_VCR];
+ trace_ecc_mem_readl_vcr(ret);
+ break;
+ case ECC_MFAR0:
+ ret = s->regs[ECC_MFAR0];
+ trace_ecc_mem_readl_mfar0(ret);
+ break;
+ case ECC_MFAR1:
+ ret = s->regs[ECC_MFAR1];
+ trace_ecc_mem_readl_mfar1(ret);
+ break;
+ case ECC_DR:
+ ret = s->regs[ECC_DR];
+ trace_ecc_mem_readl_dr(ret);
+ break;
+ case ECC_ECR0:
+ ret = s->regs[ECC_ECR0];
+ trace_ecc_mem_readl_ecr0(ret);
+ break;
+ case ECC_ECR1:
+ ret = s->regs[ECC_ECR0];
+ trace_ecc_mem_readl_ecr1(ret);
+ break;
+ }
+ return ret;
+}
+
+static CPUReadMemoryFunc * const ecc_mem_read[3] = {
+ NULL,
+ NULL,
+ ecc_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const ecc_mem_write[3] = {
+ NULL,
+ NULL,
+ ecc_mem_writel,
+};
+
+static void ecc_diag_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ ECCState *s = opaque;
+
+ trace_ecc_diag_mem_writeb(addr, val);
+ s->diag[addr & ECC_DIAG_MASK] = val;
+}
+
+static uint32_t ecc_diag_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ ECCState *s = opaque;
+ uint32_t ret = s->diag[(int)addr];
+
+ trace_ecc_diag_mem_readb(addr, ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const ecc_diag_mem_read[3] = {
+ ecc_diag_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const ecc_diag_mem_write[3] = {
+ ecc_diag_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static const VMStateDescription vmstate_ecc = {
+ .name ="ECC",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(regs, ECCState, ECC_NREGS),
+ VMSTATE_BUFFER(diag, ECCState),
+ VMSTATE_UINT32(version, ECCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ecc_reset(DeviceState *d)
+{
+ ECCState *s = container_of(d, ECCState, busdev.qdev);
+
+ if (s->version == ECC_MCC)
+ s->regs[ECC_MER] &= ECC_MER_REU;
+ else
+ s->regs[ECC_MER] &= (ECC_MER_VER | ECC_MER_IMPL | ECC_MER_MRR |
+ ECC_MER_DCI);
+ s->regs[ECC_MDR] = 0x20;
+ s->regs[ECC_MFSR] = 0;
+ s->regs[ECC_VCR] = 0;
+ s->regs[ECC_MFAR0] = 0x07c00000;
+ s->regs[ECC_MFAR1] = 0;
+ s->regs[ECC_DR] = 0;
+ s->regs[ECC_ECR0] = 0;
+ s->regs[ECC_ECR1] = 0;
+}
+
+static int ecc_init1(SysBusDevice *dev)
+{
+ int ecc_io_memory;
+ ECCState *s = FROM_SYSBUS(ECCState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ s->regs[0] = s->version;
+ ecc_io_memory = cpu_register_io_memory(ecc_mem_read, ecc_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, ECC_SIZE, ecc_io_memory);
+
+ if (s->version == ECC_MCC) { // SS-600MP only
+ ecc_io_memory = cpu_register_io_memory(ecc_diag_mem_read,
+ ecc_diag_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, ECC_DIAG_SIZE, ecc_io_memory);
+ }
+
+ return 0;
+}
+
+static SysBusDeviceInfo ecc_info = {
+ .init = ecc_init1,
+ .qdev.name = "eccmemctl",
+ .qdev.size = sizeof(ECCState),
+ .qdev.vmsd = &vmstate_ecc,
+ .qdev.reset = ecc_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("version", ECCState, version, -1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+
+static void ecc_register_devices(void)
+{
+ sysbus_register_withprop(&ecc_info);
+}
+
+device_init(ecc_register_devices)
diff --git a/hw/eepro100.c b/hw/eepro100.c
new file mode 100644
index 0000000..edf48f6
--- /dev/null
+++ b/hw/eepro100.c
@@ -0,0 +1,2066 @@
+/*
+ * QEMU i8255x (PRO100) emulation
+ *
+ * Copyright (C) 2006-2010 Stefan Weil
+ *
+ * Portions of the code are copies from grub / etherboot eepro100.c
+ * and linux e100.c.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) version 3 or any later version.
+ *
+ * This program 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 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/>.
+ *
+ * Tested features (i82559):
+ * PXE boot (i386) ok
+ * Linux networking (i386) ok
+ *
+ * Untested:
+ * non-i386 platforms
+ * Windows networking
+ *
+ * References:
+ *
+ * Intel 8255x 10/100 Mbps Ethernet Controller Family
+ * Open Source Software Developer Manual
+ *
+ * TODO:
+ * * PHY emulation should be separated from nic emulation.
+ * Most nic emulations could share the same phy code.
+ * * i82550 is untested. It is programmed like the i82559.
+ * * i82562 is untested. It is programmed like the i82559.
+ * * Power management (i82558 and later) is not implemented.
+ * * Wake-on-LAN is not implemented.
+ */
+
+#include <stddef.h> /* offsetof */
+#include "hw.h"
+#include "pci.h"
+#include "net.h"
+#include "eeprom93xx.h"
+#include "sysemu.h"
+
+#define KiB 1024
+
+/* Debug EEPRO100 card. */
+#if 0
+# define DEBUG_EEPRO100
+#endif
+
+#ifdef DEBUG_EEPRO100
+#define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+/* Set flags to 0 to disable debug output. */
+#define INT 1 /* interrupt related actions */
+#define MDI 1 /* mdi related actions */
+#define OTHER 1
+#define RXTX 1
+#define EEPROM 1 /* eeprom related actions */
+
+#define TRACE(flag, command) ((flag) ? (command) : (void)0)
+
+#define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+/* This driver supports several different devices which are declared here. */
+#define i82550 0x82550
+#define i82551 0x82551
+#define i82557A 0x82557a
+#define i82557B 0x82557b
+#define i82557C 0x82557c
+#define i82558A 0x82558a
+#define i82558B 0x82558b
+#define i82559A 0x82559a
+#define i82559B 0x82559b
+#define i82559C 0x82559c
+#define i82559ER 0x82559e
+#define i82562 0x82562
+#define i82801 0x82801
+
+/* Use 64 word EEPROM. TODO: could be a runtime option. */
+#define EEPROM_SIZE 64
+
+#define PCI_MEM_SIZE (4 * KiB)
+#define PCI_IO_SIZE 64
+#define PCI_FLASH_SIZE (128 * KiB)
+
+#define BIT(n) (1 << (n))
+#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define CU_NOP 0x0000 /* No operation. */
+#define CU_START 0x0010 /* CU start. */
+#define CU_RESUME 0x0020 /* CU resume. */
+#define CU_STATSADDR 0x0040 /* Load dump counters address. */
+#define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */
+#define CU_CMD_BASE 0x0060 /* Load CU base address. */
+#define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */
+#define CU_SRESUME 0x00a0 /* CU static resume. */
+
+#define RU_NOP 0x0000
+#define RX_START 0x0001
+#define RX_RESUME 0x0002
+#define RU_ABORT 0x0004
+#define RX_ADDR_LOAD 0x0006
+#define RX_RESUMENR 0x0007
+#define INT_MASK 0x0100
+#define DRVR_INT 0x0200 /* Driver generated interrupt. */
+
+typedef struct {
+ PCIDeviceInfo pci;
+ uint32_t device;
+ uint16_t device_id;
+ uint8_t revision;
+ uint8_t stats_size;
+ bool has_extended_tcb_support;
+ bool power_management;
+} E100PCIDeviceInfo;
+
+/* Offsets to the various registers.
+ All accesses need not be longword aligned. */
+enum speedo_offsets {
+ SCBStatus = 0, /* Status Word. */
+ SCBAck = 1,
+ SCBCmd = 2, /* Rx/Command Unit command and status. */
+ SCBIntmask = 3,
+ SCBPointer = 4, /* General purpose pointer. */
+ SCBPort = 8, /* Misc. commands and operands. */
+ SCBflash = 12, /* Flash memory control. */
+ SCBeeprom = 14, /* EEPROM control. */
+ SCBCtrlMDI = 16, /* MDI interface control. */
+ SCBEarlyRx = 20, /* Early receive byte count. */
+ SCBFlow = 24, /* Flow Control. */
+ SCBpmdr = 27, /* Power Management Driver. */
+ SCBgctrl = 28, /* General Control. */
+ SCBgstat = 29, /* General Status. */
+};
+
+/* A speedo3 transmit buffer descriptor with two buffers... */
+typedef struct {
+ uint16_t status;
+ uint16_t command;
+ uint32_t link; /* void * */
+ uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */
+ uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */
+ uint8_t tx_threshold; /* transmit threshold */
+ uint8_t tbd_count; /* TBD number */
+#if 0
+ /* This constitutes two "TBD" entries: hdr and data */
+ uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */
+ int32_t tx_buf_size0; /* Length of Tx hdr. */
+ uint32_t tx_buf_addr1; /* void *, data to be transmitted. */
+ int32_t tx_buf_size1; /* Length of Tx data. */
+#endif
+} eepro100_tx_t;
+
+/* Receive frame descriptor. */
+typedef struct {
+ int16_t status;
+ uint16_t command;
+ uint32_t link; /* struct RxFD * */
+ uint32_t rx_buf_addr; /* void * */
+ uint16_t count;
+ uint16_t size;
+ char packet[MAX_ETH_FRAME_SIZE + 4];
+} eepro100_rx_t;
+
+typedef enum {
+ COMMAND_EL = BIT(15),
+ COMMAND_S = BIT(14),
+ COMMAND_I = BIT(13),
+ COMMAND_NC = BIT(4),
+ COMMAND_SF = BIT(3),
+ COMMAND_CMD = BITS(2, 0),
+} scb_command_bit;
+
+typedef enum {
+ STATUS_C = BIT(15),
+ STATUS_OK = BIT(13),
+} scb_status_bit;
+
+typedef struct {
+ uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
+ tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
+ tx_multiple_collisions, tx_total_collisions;
+ uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
+ rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
+ rx_short_frame_errors;
+ uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
+ uint16_t xmt_tco_frames, rcv_tco_frames;
+ /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
+ uint32_t reserved[4];
+} eepro100_stats_t;
+
+typedef enum {
+ cu_idle = 0,
+ cu_suspended = 1,
+ cu_active = 2,
+ cu_lpq_active = 2,
+ cu_hqp_active = 3
+} cu_state_t;
+
+typedef enum {
+ ru_idle = 0,
+ ru_suspended = 1,
+ ru_no_resources = 2,
+ ru_ready = 4
+} ru_state_t;
+
+typedef struct {
+ PCIDevice dev;
+ /* Hash register (multicast mask array, multiple individual addresses). */
+ uint8_t mult[8];
+ int mmio_index;
+ NICState *nic;
+ NICConf conf;
+ uint8_t scb_stat; /* SCB stat/ack byte */
+ uint8_t int_stat; /* PCI interrupt status */
+ /* region must not be saved by nic_save. */
+ uint32_t region[3]; /* PCI region addresses */
+ uint16_t mdimem[32];
+ eeprom_t *eeprom;
+ uint32_t device; /* device variant */
+ uint32_t pointer;
+ /* (cu_base + cu_offset) address the next command block in the command block list. */
+ uint32_t cu_base; /* CU base address */
+ uint32_t cu_offset; /* CU address offset */
+ /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
+ uint32_t ru_base; /* RU base address */
+ uint32_t ru_offset; /* RU address offset */
+ uint32_t statsaddr; /* pointer to eepro100_stats_t */
+
+ /* Temporary status information (no need to save these values),
+ * used while processing CU commands. */
+ eepro100_tx_t tx; /* transmit buffer descriptor */
+ uint32_t cb_address; /* = cu_base + cu_offset */
+
+ /* Statistical counters. Also used for wake-up packet (i82559). */
+ eepro100_stats_t statistics;
+
+ /* Configuration bytes. */
+ uint8_t configuration[22];
+
+ /* Data in mem is always in the byte order of the controller (le). */
+ uint8_t mem[PCI_MEM_SIZE];
+ /* vmstate for each particular nic */
+ VMStateDescription *vmstate;
+
+ /* Quasi static device properties (no need to save them). */
+ uint16_t stats_size;
+ bool has_extended_tcb_support;
+} EEPRO100State;
+
+/* Word indices in EEPROM. */
+typedef enum {
+ EEPROM_CNFG_MDIX = 0x03,
+ EEPROM_ID = 0x05,
+ EEPROM_PHY_ID = 0x06,
+ EEPROM_VENDOR_ID = 0x0c,
+ EEPROM_CONFIG_ASF = 0x0d,
+ EEPROM_DEVICE_ID = 0x23,
+ EEPROM_SMBUS_ADDR = 0x90,
+} EEPROMOffset;
+
+/* Bit values for EEPROM ID word. */
+typedef enum {
+ EEPROM_ID_MDM = BIT(0), /* Modem */
+ EEPROM_ID_STB = BIT(1), /* Standby Enable */
+ EEPROM_ID_WMR = BIT(2), /* ??? */
+ EEPROM_ID_WOL = BIT(5), /* Wake on LAN */
+ EEPROM_ID_DPD = BIT(6), /* Deep Power Down */
+ EEPROM_ID_ALT = BIT(7), /* */
+ /* BITS(10, 8) device revision */
+ EEPROM_ID_BD = BIT(11), /* boot disable */
+ EEPROM_ID_ID = BIT(13), /* id bit */
+ /* BITS(15, 14) signature */
+ EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */
+} eeprom_id_bit;
+
+/* Default values for MDI (PHY) registers */
+static const uint16_t eepro100_mdi_default[] = {
+ /* MDI Registers 0 - 6, 7 */
+ 0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
+ /* MDI Registers 8 - 15 */
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* MDI Registers 16 - 31 */
+ 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+/* Readonly mask for MDI (PHY) registers */
+static const uint16_t eepro100_mdi_mask[] = {
+ 0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+/* XXX: optimize */
+static void stl_le_phys(target_phys_addr_t addr, uint32_t val)
+{
+ val = cpu_to_le32(val);
+ cpu_physical_memory_write(addr, (const uint8_t *)&val, sizeof(val));
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+/* XXX: optimize */
+static unsigned compute_mcast_idx(const uint8_t * ep)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry) {
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ }
+ return (crc & BITS(7, 2)) >> 2;
+}
+
+#if defined(DEBUG_EEPRO100)
+static const char *nic_dump(const uint8_t * buf, unsigned size)
+{
+ static char dump[3 * 16 + 1];
+ char *p = &dump[0];
+ if (size > 16) {
+ size = 16;
+ }
+ while (size-- > 0) {
+ p += sprintf(p, " %02x", *buf++);
+ }
+ return dump;
+}
+#endif /* DEBUG_EEPRO100 */
+
+enum scb_stat_ack {
+ stat_ack_not_ours = 0x00,
+ stat_ack_sw_gen = 0x04,
+ stat_ack_rnr = 0x10,
+ stat_ack_cu_idle = 0x20,
+ stat_ack_frame_rx = 0x40,
+ stat_ack_cu_cmd_done = 0x80,
+ stat_ack_not_present = 0xFF,
+ stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
+ stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
+};
+
+static void disable_interrupt(EEPRO100State * s)
+{
+ if (s->int_stat) {
+ TRACE(INT, logout("interrupt disabled\n"));
+ qemu_irq_lower(s->dev.irq[0]);
+ s->int_stat = 0;
+ }
+}
+
+static void enable_interrupt(EEPRO100State * s)
+{
+ if (!s->int_stat) {
+ TRACE(INT, logout("interrupt enabled\n"));
+ qemu_irq_raise(s->dev.irq[0]);
+ s->int_stat = 1;
+ }
+}
+
+static void eepro100_acknowledge(EEPRO100State * s)
+{
+ s->scb_stat &= ~s->mem[SCBAck];
+ s->mem[SCBAck] = s->scb_stat;
+ if (s->scb_stat == 0) {
+ disable_interrupt(s);
+ }
+}
+
+static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
+{
+ uint8_t mask = ~s->mem[SCBIntmask];
+ s->mem[SCBAck] |= status;
+ status = s->scb_stat = s->mem[SCBAck];
+ status &= (mask | 0x0f);
+#if 0
+ status &= (~s->mem[SCBIntmask] | 0x0xf);
+#endif
+ if (status && (mask & 0x01)) {
+ /* SCB mask and SCB Bit M do not disable interrupt. */
+ enable_interrupt(s);
+ } else if (s->int_stat) {
+ disable_interrupt(s);
+ }
+}
+
+static void eepro100_cx_interrupt(EEPRO100State * s)
+{
+ /* CU completed action command. */
+ /* Transmit not ok (82557 only, not in emulation). */
+ eepro100_interrupt(s, 0x80);
+}
+
+static void eepro100_cna_interrupt(EEPRO100State * s)
+{
+ /* CU left the active state. */
+ eepro100_interrupt(s, 0x20);
+}
+
+static void eepro100_fr_interrupt(EEPRO100State * s)
+{
+ /* RU received a complete frame. */
+ eepro100_interrupt(s, 0x40);
+}
+
+static void eepro100_rnr_interrupt(EEPRO100State * s)
+{
+ /* RU is not ready. */
+ eepro100_interrupt(s, 0x10);
+}
+
+static void eepro100_mdi_interrupt(EEPRO100State * s)
+{
+ /* MDI completed read or write cycle. */
+ eepro100_interrupt(s, 0x08);
+}
+
+static void eepro100_swi_interrupt(EEPRO100State * s)
+{
+ /* Software has requested an interrupt. */
+ eepro100_interrupt(s, 0x04);
+}
+
+#if 0
+static void eepro100_fcp_interrupt(EEPRO100State * s)
+{
+ /* Flow control pause interrupt (82558 and later). */
+ eepro100_interrupt(s, 0x01);
+}
+#endif
+
+static void e100_pci_reset(EEPRO100State * s, E100PCIDeviceInfo *e100_device)
+{
+ uint32_t device = s->device;
+ uint8_t *pci_conf = s->dev.config;
+
+ TRACE(OTHER, logout("%p\n", s));
+
+ /* PCI Vendor ID */
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ /* PCI Device ID */
+ pci_config_set_device_id(pci_conf, e100_device->device_id);
+ /* PCI Status */
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
+ PCI_STATUS_FAST_BACK);
+ /* PCI Revision ID */
+ pci_config_set_revision(pci_conf, e100_device->revision);
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ /* PCI Latency Timer */
+ pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */
+ /* Capability Pointer is set by PCI framework. */
+ /* Interrupt Line */
+ /* Interrupt Pin */
+ pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */
+ /* Minimum Grant */
+ pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
+ /* Maximum Latency */
+ pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
+
+ s->stats_size = e100_device->stats_size;
+ s->has_extended_tcb_support = e100_device->has_extended_tcb_support;
+
+ switch (device) {
+ case i82550:
+ case i82551:
+ case i82557A:
+ case i82557B:
+ case i82557C:
+ case i82558A:
+ case i82558B:
+ case i82559A:
+ case i82559B:
+ case i82559ER:
+ case i82562:
+ case i82801:
+ break;
+ case i82559C:
+#if EEPROM_SIZE > 0
+ pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_INTEL);
+ pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0040);
+#endif
+ break;
+ default:
+ logout("Device %X is undefined!\n", device);
+ }
+
+ /* Standard TxCB. */
+ s->configuration[6] |= BIT(4);
+
+ /* Standard statistical counters. */
+ s->configuration[6] |= BIT(5);
+
+ if (s->stats_size == 80) {
+ /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
+ if (s->configuration[6] & BIT(2)) {
+ /* TCO statistical counters. */
+ assert(s->configuration[6] & BIT(5));
+ } else {
+ if (s->configuration[6] & BIT(5)) {
+ /* No extended statistical counters, i82557 compatible. */
+ s->stats_size = 64;
+ } else {
+ /* i82558 compatible. */
+ s->stats_size = 76;
+ }
+ }
+ } else {
+ if (s->configuration[6] & BIT(5)) {
+ /* No extended statistical counters. */
+ s->stats_size = 64;
+ }
+ }
+ assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
+
+ if (e100_device->power_management) {
+ /* Power Management Capabilities */
+ int cfg_offset = 0xdc;
+ int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
+ cfg_offset, PCI_PM_SIZEOF);
+ assert(r >= 0);
+ pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
+#if 0 /* TODO: replace dummy code for power management emulation. */
+ /* TODO: Power Management Control / Status. */
+ pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
+ /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
+ pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
+#endif
+ }
+
+#if EEPROM_SIZE > 0
+ if (device == i82557C || device == i82558B || device == i82559C) {
+ /*
+ TODO: get vendor id from EEPROM for i82557C or later.
+ TODO: get device id from EEPROM for i82557C or later.
+ TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
+ TODO: header type is determined by EEPROM for i82559.
+ TODO: get subsystem id from EEPROM for i82557C or later.
+ TODO: get subsystem vendor id from EEPROM for i82557C or later.
+ TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
+ TODO: capability pointer depends on EEPROM for i82558.
+ */
+ logout("Get device id and revision from EEPROM!!!\n");
+ }
+#endif /* EEPROM_SIZE > 0 */
+}
+
+static void nic_selective_reset(EEPRO100State * s)
+{
+ size_t i;
+ uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
+#if 0
+ eeprom93xx_reset(s->eeprom);
+#endif
+ memcpy(eeprom_contents, s->conf.macaddr.a, 6);
+ eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
+ if (s->device == i82557B || s->device == i82557C)
+ eeprom_contents[5] = 0x0100;
+ eeprom_contents[EEPROM_PHY_ID] = 1;
+ uint16_t sum = 0;
+ for (i = 0; i < EEPROM_SIZE - 1; i++) {
+ sum += eeprom_contents[i];
+ }
+ eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
+ TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
+
+ memset(s->mem, 0, sizeof(s->mem));
+ uint32_t val = BIT(21);
+ memcpy(&s->mem[SCBCtrlMDI], &val, sizeof(val));
+
+ assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
+ memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
+}
+
+static void nic_reset(void *opaque)
+{
+ EEPRO100State *s = opaque;
+ TRACE(OTHER, logout("%p\n", s));
+ /* TODO: Clearing of hash register for selective reset, too? */
+ memset(&s->mult[0], 0, sizeof(s->mult));
+ nic_selective_reset(s);
+}
+
+#if defined(DEBUG_EEPRO100)
+static const char * const e100_reg[PCI_IO_SIZE / 4] = {
+ "Command/Status",
+ "General Pointer",
+ "Port",
+ "EEPROM/Flash Control",
+ "MDI Control",
+ "Receive DMA Byte Count",
+ "Flow Control",
+ "General Status/Control"
+};
+
+static char *regname(uint32_t addr)
+{
+ static char buf[32];
+ if (addr < PCI_IO_SIZE) {
+ const char *r = e100_reg[addr / 4];
+ if (r != 0) {
+ snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
+ } else {
+ snprintf(buf, sizeof(buf), "0x%02x", addr);
+ }
+ } else {
+ snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
+ }
+ return buf;
+}
+#endif /* DEBUG_EEPRO100 */
+
+/*****************************************************************************
+ *
+ * Command emulation.
+ *
+ ****************************************************************************/
+
+#if 0
+static uint16_t eepro100_read_command(EEPRO100State * s)
+{
+ uint16_t val = 0xffff;
+ TRACE(OTHER, logout("val=0x%04x\n", val));
+ return val;
+}
+#endif
+
+/* Commands that can be put in a command list entry. */
+enum commands {
+ CmdNOp = 0,
+ CmdIASetup = 1,
+ CmdConfigure = 2,
+ CmdMulticastList = 3,
+ CmdTx = 4,
+ CmdTDR = 5, /* load microcode */
+ CmdDump = 6,
+ CmdDiagnose = 7,
+
+ /* And some extra flags: */
+ CmdSuspend = 0x4000, /* Suspend after completion. */
+ CmdIntr = 0x2000, /* Interrupt after completion. */
+ CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
+};
+
+static cu_state_t get_cu_state(EEPRO100State * s)
+{
+ return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
+}
+
+static void set_cu_state(EEPRO100State * s, cu_state_t state)
+{
+ s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
+}
+
+static ru_state_t get_ru_state(EEPRO100State * s)
+{
+ return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
+}
+
+static void set_ru_state(EEPRO100State * s, ru_state_t state)
+{
+ s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
+}
+
+static void dump_statistics(EEPRO100State * s)
+{
+ /* Dump statistical data. Most data is never changed by the emulation
+ * and always 0, so we first just copy the whole block and then those
+ * values which really matter.
+ * Number of data should check configuration!!!
+ */
+ cpu_physical_memory_write(s->statsaddr,
+ (uint8_t *) & s->statistics, s->stats_size);
+ stl_le_phys(s->statsaddr + 0, s->statistics.tx_good_frames);
+ stl_le_phys(s->statsaddr + 36, s->statistics.rx_good_frames);
+ stl_le_phys(s->statsaddr + 48, s->statistics.rx_resource_errors);
+ stl_le_phys(s->statsaddr + 60, s->statistics.rx_short_frame_errors);
+#if 0
+ stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames);
+ stw_le_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames);
+ missing("CU dump statistical counters");
+#endif
+}
+
+static void read_cb(EEPRO100State *s)
+{
+ cpu_physical_memory_read(s->cb_address, (uint8_t *) &s->tx, sizeof(s->tx));
+ s->tx.status = le16_to_cpu(s->tx.status);
+ s->tx.command = le16_to_cpu(s->tx.command);
+ s->tx.link = le32_to_cpu(s->tx.link);
+ s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
+ s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
+}
+
+static void tx_command(EEPRO100State *s)
+{
+ uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr);
+ uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff);
+ /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
+ uint8_t buf[2600];
+ uint16_t size = 0;
+ uint32_t tbd_address = s->cb_address + 0x10;
+ TRACE(RXTX, logout
+ ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
+ tbd_array, tcb_bytes, s->tx.tbd_count));
+
+ if (tcb_bytes > 2600) {
+ logout("TCB byte count too large, using 2600\n");
+ tcb_bytes = 2600;
+ }
+ if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
+ logout
+ ("illegal values of TBD array address and TCB byte count!\n");
+ }
+ assert(tcb_bytes <= sizeof(buf));
+ while (size < tcb_bytes) {
+ uint32_t tx_buffer_address = ldl_phys(tbd_address);
+ uint16_t tx_buffer_size = lduw_phys(tbd_address + 4);
+#if 0
+ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6);
+#endif
+ tbd_address += 8;
+ TRACE(RXTX, logout
+ ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
+ tx_buffer_address, tx_buffer_size));
+ tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
+ cpu_physical_memory_read(tx_buffer_address, &buf[size],
+ tx_buffer_size);
+ size += tx_buffer_size;
+ }
+ if (tbd_array == 0xffffffff) {
+ /* Simplified mode. Was already handled by code above. */
+ } else {
+ /* Flexible mode. */
+ uint8_t tbd_count = 0;
+ if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
+ /* Extended Flexible TCB. */
+ for (; tbd_count < 2; tbd_count++) {
+ uint32_t tx_buffer_address = ldl_phys(tbd_address);
+ uint16_t tx_buffer_size = lduw_phys(tbd_address + 4);
+ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6);
+ tbd_address += 8;
+ TRACE(RXTX, logout
+ ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
+ tx_buffer_address, tx_buffer_size));
+ tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
+ cpu_physical_memory_read(tx_buffer_address, &buf[size],
+ tx_buffer_size);
+ size += tx_buffer_size;
+ if (tx_buffer_el & 1) {
+ break;
+ }
+ }
+ }
+ tbd_address = tbd_array;
+ for (; tbd_count < s->tx.tbd_count; tbd_count++) {
+ uint32_t tx_buffer_address = ldl_phys(tbd_address);
+ uint16_t tx_buffer_size = lduw_phys(tbd_address + 4);
+ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6);
+ tbd_address += 8;
+ TRACE(RXTX, logout
+ ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
+ tx_buffer_address, tx_buffer_size));
+ tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
+ cpu_physical_memory_read(tx_buffer_address, &buf[size],
+ tx_buffer_size);
+ size += tx_buffer_size;
+ if (tx_buffer_el & 1) {
+ break;
+ }
+ }
+ }
+ TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
+ qemu_send_packet(&s->nic->nc, buf, size);
+ s->statistics.tx_good_frames++;
+ /* Transmit with bad status would raise an CX/TNO interrupt.
+ * (82557 only). Emulation never has bad status. */
+#if 0
+ eepro100_cx_interrupt(s);
+#endif
+}
+
+static void set_multicast_list(EEPRO100State *s)
+{
+ uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
+ uint16_t i;
+ memset(&s->mult[0], 0, sizeof(s->mult));
+ TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
+ for (i = 0; i < multicast_count; i += 6) {
+ uint8_t multicast_addr[6];
+ cpu_physical_memory_read(s->cb_address + 10 + i, multicast_addr, 6);
+ TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
+ unsigned mcast_idx = compute_mcast_idx(multicast_addr);
+ assert(mcast_idx < 64);
+ s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
+ }
+}
+
+static void action_command(EEPRO100State *s)
+{
+ for (;;) {
+ bool bit_el;
+ bool bit_s;
+ bool bit_i;
+ bool bit_nc;
+ uint16_t ok_status = STATUS_OK;
+ s->cb_address = s->cu_base + s->cu_offset;
+ read_cb(s);
+ bit_el = ((s->tx.command & COMMAND_EL) != 0);
+ bit_s = ((s->tx.command & COMMAND_S) != 0);
+ bit_i = ((s->tx.command & COMMAND_I) != 0);
+ bit_nc = ((s->tx.command & COMMAND_NC) != 0);
+#if 0
+ bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
+#endif
+ s->cu_offset = s->tx.link;
+ TRACE(OTHER,
+ logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
+ s->tx.status, s->tx.command, s->tx.link));
+ switch (s->tx.command & COMMAND_CMD) {
+ case CmdNOp:
+ /* Do nothing. */
+ break;
+ case CmdIASetup:
+ cpu_physical_memory_read(s->cb_address + 8, &s->conf.macaddr.a[0], 6);
+ TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
+ break;
+ case CmdConfigure:
+ cpu_physical_memory_read(s->cb_address + 8, &s->configuration[0],
+ sizeof(s->configuration));
+ TRACE(OTHER, logout("configuration: %s\n",
+ nic_dump(&s->configuration[0], 16)));
+ TRACE(OTHER, logout("configuration: %s\n",
+ nic_dump(&s->configuration[16],
+ ARRAY_SIZE(s->configuration) - 16)));
+ if (s->configuration[20] & BIT(6)) {
+ TRACE(OTHER, logout("Multiple IA bit\n"));
+ }
+ break;
+ case CmdMulticastList:
+ set_multicast_list(s);
+ break;
+ case CmdTx:
+ if (bit_nc) {
+ missing("CmdTx: NC = 0");
+ ok_status = 0;
+ break;
+ }
+ tx_command(s);
+ break;
+ case CmdTDR:
+ TRACE(OTHER, logout("load microcode\n"));
+ /* Starting with offset 8, the command contains
+ * 64 dwords microcode which we just ignore here. */
+ break;
+ case CmdDiagnose:
+ TRACE(OTHER, logout("diagnose\n"));
+ /* Make sure error flag is not set. */
+ s->tx.status = 0;
+ break;
+ default:
+ missing("undefined command");
+ ok_status = 0;
+ break;
+ }
+ /* Write new status. */
+ stw_phys(s->cb_address, s->tx.status | ok_status | STATUS_C);
+ if (bit_i) {
+ /* CU completed action. */
+ eepro100_cx_interrupt(s);
+ }
+ if (bit_el) {
+ /* CU becomes idle. Terminate command loop. */
+ set_cu_state(s, cu_idle);
+ eepro100_cna_interrupt(s);
+ break;
+ } else if (bit_s) {
+ /* CU becomes suspended. Terminate command loop. */
+ set_cu_state(s, cu_suspended);
+ eepro100_cna_interrupt(s);
+ break;
+ } else {
+ /* More entries in list. */
+ TRACE(OTHER, logout("CU list with at least one more entry\n"));
+ }
+ }
+ TRACE(OTHER, logout("CU list empty\n"));
+ /* List is empty. Now CU is idle or suspended. */
+}
+
+static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
+{
+ cu_state_t cu_state;
+ switch (val) {
+ case CU_NOP:
+ /* No operation. */
+ break;
+ case CU_START:
+ cu_state = get_cu_state(s);
+ if (cu_state != cu_idle && cu_state != cu_suspended) {
+ /* Intel documentation says that CU must be idle or suspended
+ * for the CU start command. */
+ logout("unexpected CU state is %u\n", cu_state);
+ }
+ set_cu_state(s, cu_active);
+ s->cu_offset = s->pointer;
+ action_command(s);
+ break;
+ case CU_RESUME:
+ if (get_cu_state(s) != cu_suspended) {
+ logout("bad CU resume from CU state %u\n", get_cu_state(s));
+ /* Workaround for bad Linux eepro100 driver which resumes
+ * from idle state. */
+#if 0
+ missing("cu resume");
+#endif
+ set_cu_state(s, cu_suspended);
+ }
+ if (get_cu_state(s) == cu_suspended) {
+ TRACE(OTHER, logout("CU resuming\n"));
+ set_cu_state(s, cu_active);
+ action_command(s);
+ }
+ break;
+ case CU_STATSADDR:
+ /* Load dump counters address. */
+ s->statsaddr = s->pointer;
+ TRACE(OTHER, logout("val=0x%02x (status address)\n", val));
+ break;
+ case CU_SHOWSTATS:
+ /* Dump statistical counters. */
+ TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
+ dump_statistics(s);
+ stl_le_phys(s->statsaddr + s->stats_size, 0xa005);
+ break;
+ case CU_CMD_BASE:
+ /* Load CU base. */
+ TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
+ s->cu_base = s->pointer;
+ break;
+ case CU_DUMPSTATS:
+ /* Dump and reset statistical counters. */
+ TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
+ dump_statistics(s);
+ stl_le_phys(s->statsaddr + s->stats_size, 0xa007);
+ memset(&s->statistics, 0, sizeof(s->statistics));
+ break;
+ case CU_SRESUME:
+ /* CU static resume. */
+ missing("CU static resume");
+ break;
+ default:
+ missing("Undefined CU command");
+ }
+}
+
+static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
+{
+ switch (val) {
+ case RU_NOP:
+ /* No operation. */
+ break;
+ case RX_START:
+ /* RU start. */
+ if (get_ru_state(s) != ru_idle) {
+ logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
+#if 0
+ assert(!"wrong RU state");
+#endif
+ }
+ set_ru_state(s, ru_ready);
+ s->ru_offset = s->pointer;
+ TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
+ break;
+ case RX_RESUME:
+ /* Restart RU. */
+ if (get_ru_state(s) != ru_suspended) {
+ logout("RU state is %u, should be %u\n", get_ru_state(s),
+ ru_suspended);
+#if 0
+ assert(!"wrong RU state");
+#endif
+ }
+ set_ru_state(s, ru_ready);
+ break;
+ case RU_ABORT:
+ /* RU abort. */
+ if (get_ru_state(s) == ru_ready) {
+ eepro100_rnr_interrupt(s);
+ }
+ set_ru_state(s, ru_idle);
+ break;
+ case RX_ADDR_LOAD:
+ /* Load RU base. */
+ TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
+ s->ru_base = s->pointer;
+ break;
+ default:
+ logout("val=0x%02x (undefined RU command)\n", val);
+ missing("Undefined SU command");
+ }
+}
+
+static void eepro100_write_command(EEPRO100State * s, uint8_t val)
+{
+ eepro100_ru_command(s, val & 0x0f);
+ eepro100_cu_command(s, val & 0xf0);
+ if ((val) == 0) {
+ TRACE(OTHER, logout("val=0x%02x\n", val));
+ }
+ /* Clear command byte after command was accepted. */
+ s->mem[SCBCmd] = 0;
+}
+
+/*****************************************************************************
+ *
+ * EEPROM emulation.
+ *
+ ****************************************************************************/
+
+#define EEPROM_CS 0x02
+#define EEPROM_SK 0x01
+#define EEPROM_DI 0x04
+#define EEPROM_DO 0x08
+
+static uint16_t eepro100_read_eeprom(EEPRO100State * s)
+{
+ uint16_t val;
+ memcpy(&val, &s->mem[SCBeeprom], sizeof(val));
+ if (eeprom93xx_read(s->eeprom)) {
+ val |= EEPROM_DO;
+ } else {
+ val &= ~EEPROM_DO;
+ }
+ TRACE(EEPROM, logout("val=0x%04x\n", val));
+ return val;
+}
+
+static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
+{
+ TRACE(EEPROM, logout("val=0x%02x\n", val));
+
+ /* mask unwriteable bits */
+#if 0
+ val = SET_MASKED(val, 0x31, eeprom->value);
+#endif
+
+ int eecs = ((val & EEPROM_CS) != 0);
+ int eesk = ((val & EEPROM_SK) != 0);
+ int eedi = ((val & EEPROM_DI) != 0);
+ eeprom93xx_write(eeprom, eecs, eesk, eedi);
+}
+
+static void eepro100_write_pointer(EEPRO100State * s, uint32_t val)
+{
+ s->pointer = le32_to_cpu(val);
+ TRACE(OTHER, logout("val=0x%08x\n", val));
+}
+
+/*****************************************************************************
+ *
+ * MDI emulation.
+ *
+ ****************************************************************************/
+
+#if defined(DEBUG_EEPRO100)
+static const char * const mdi_op_name[] = {
+ "opcode 0",
+ "write",
+ "read",
+ "opcode 3"
+};
+
+static const char * const mdi_reg_name[] = {
+ "Control",
+ "Status",
+ "PHY Identification (Word 1)",
+ "PHY Identification (Word 2)",
+ "Auto-Negotiation Advertisement",
+ "Auto-Negotiation Link Partner Ability",
+ "Auto-Negotiation Expansion"
+};
+
+static const char *reg2name(uint8_t reg)
+{
+ static char buffer[10];
+ const char *p = buffer;
+ if (reg < ARRAY_SIZE(mdi_reg_name)) {
+ p = mdi_reg_name[reg];
+ } else {
+ snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
+ }
+ return p;
+}
+#endif /* DEBUG_EEPRO100 */
+
+static uint32_t eepro100_read_mdi(EEPRO100State * s)
+{
+ uint32_t val;
+ memcpy(&val, &s->mem[0x10], sizeof(val));
+
+#ifdef DEBUG_EEPRO100
+ uint8_t raiseint = (val & BIT(29)) >> 29;
+ uint8_t opcode = (val & BITS(27, 26)) >> 26;
+ uint8_t phy = (val & BITS(25, 21)) >> 21;
+ uint8_t reg = (val & BITS(20, 16)) >> 16;
+ uint16_t data = (val & BITS(15, 0));
+#endif
+ /* Emulation takes no time to finish MDI transaction. */
+ val |= BIT(28);
+ TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
+ val, raiseint, mdi_op_name[opcode], phy,
+ reg2name(reg), data));
+ return val;
+}
+
+static void eepro100_write_mdi(EEPRO100State * s, uint32_t val)
+{
+ uint8_t raiseint = (val & BIT(29)) >> 29;
+ uint8_t opcode = (val & BITS(27, 26)) >> 26;
+ uint8_t phy = (val & BITS(25, 21)) >> 21;
+ uint8_t reg = (val & BITS(20, 16)) >> 16;
+ uint16_t data = (val & BITS(15, 0));
+ TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
+ val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
+ if (phy != 1) {
+ /* Unsupported PHY address. */
+#if 0
+ logout("phy must be 1 but is %u\n", phy);
+#endif
+ data = 0;
+ } else if (opcode != 1 && opcode != 2) {
+ /* Unsupported opcode. */
+ logout("opcode must be 1 or 2 but is %u\n", opcode);
+ data = 0;
+ } else if (reg > 6) {
+ /* Unsupported register. */
+ logout("register must be 0...6 but is %u\n", reg);
+ data = 0;
+ } else {
+ TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
+ val, raiseint, mdi_op_name[opcode], phy,
+ reg2name(reg), data));
+ if (opcode == 1) {
+ /* MDI write */
+ switch (reg) {
+ case 0: /* Control Register */
+ if (data & 0x8000) {
+ /* Reset status and control registers to default. */
+ s->mdimem[0] = eepro100_mdi_default[0];
+ s->mdimem[1] = eepro100_mdi_default[1];
+ data = s->mdimem[reg];
+ } else {
+ /* Restart Auto Configuration = Normal Operation */
+ data &= ~0x0200;
+ }
+ break;
+ case 1: /* Status Register */
+ missing("not writable");
+ data = s->mdimem[reg];
+ break;
+ case 2: /* PHY Identification Register (Word 1) */
+ case 3: /* PHY Identification Register (Word 2) */
+ missing("not implemented");
+ break;
+ case 4: /* Auto-Negotiation Advertisement Register */
+ case 5: /* Auto-Negotiation Link Partner Ability Register */
+ break;
+ case 6: /* Auto-Negotiation Expansion Register */
+ default:
+ missing("not implemented");
+ }
+ s->mdimem[reg] = data;
+ } else if (opcode == 2) {
+ /* MDI read */
+ switch (reg) {
+ case 0: /* Control Register */
+ if (data & 0x8000) {
+ /* Reset status and control registers to default. */
+ s->mdimem[0] = eepro100_mdi_default[0];
+ s->mdimem[1] = eepro100_mdi_default[1];
+ }
+ break;
+ case 1: /* Status Register */
+ s->mdimem[reg] |= 0x0020;
+ break;
+ case 2: /* PHY Identification Register (Word 1) */
+ case 3: /* PHY Identification Register (Word 2) */
+ case 4: /* Auto-Negotiation Advertisement Register */
+ break;
+ case 5: /* Auto-Negotiation Link Partner Ability Register */
+ s->mdimem[reg] = 0x41fe;
+ break;
+ case 6: /* Auto-Negotiation Expansion Register */
+ s->mdimem[reg] = 0x0001;
+ break;
+ }
+ data = s->mdimem[reg];
+ }
+ /* Emulation takes no time to finish MDI transaction.
+ * Set MDI bit in SCB status register. */
+ s->mem[SCBAck] |= 0x08;
+ val |= BIT(28);
+ if (raiseint) {
+ eepro100_mdi_interrupt(s);
+ }
+ }
+ val = (val & 0xffff0000) + data;
+ memcpy(&s->mem[0x10], &val, sizeof(val));
+}
+
+/*****************************************************************************
+ *
+ * Port emulation.
+ *
+ ****************************************************************************/
+
+#define PORT_SOFTWARE_RESET 0
+#define PORT_SELFTEST 1
+#define PORT_SELECTIVE_RESET 2
+#define PORT_DUMP 3
+#define PORT_SELECTION_MASK 3
+
+typedef struct {
+ uint32_t st_sign; /* Self Test Signature */
+ uint32_t st_result; /* Self Test Results */
+} eepro100_selftest_t;
+
+static uint32_t eepro100_read_port(EEPRO100State * s)
+{
+ return 0;
+}
+
+static void eepro100_write_port(EEPRO100State * s, uint32_t val)
+{
+ val = le32_to_cpu(val);
+ uint32_t address = (val & ~PORT_SELECTION_MASK);
+ uint8_t selection = (val & PORT_SELECTION_MASK);
+ switch (selection) {
+ case PORT_SOFTWARE_RESET:
+ nic_reset(s);
+ break;
+ case PORT_SELFTEST:
+ TRACE(OTHER, logout("selftest address=0x%08x\n", address));
+ eepro100_selftest_t data;
+ cpu_physical_memory_read(address, (uint8_t *) & data, sizeof(data));
+ data.st_sign = 0xffffffff;
+ data.st_result = 0;
+ cpu_physical_memory_write(address, (uint8_t *) & data, sizeof(data));
+ break;
+ case PORT_SELECTIVE_RESET:
+ TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
+ nic_selective_reset(s);
+ break;
+ default:
+ logout("val=0x%08x\n", val);
+ missing("unknown port selection");
+ }
+}
+
+/*****************************************************************************
+ *
+ * General hardware emulation.
+ *
+ ****************************************************************************/
+
+static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
+{
+ uint8_t val = 0;
+ if (addr <= sizeof(s->mem) - sizeof(val)) {
+ memcpy(&val, &s->mem[addr], sizeof(val));
+ }
+
+ switch (addr) {
+ case SCBStatus:
+ case SCBAck:
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+ break;
+ case SCBCmd:
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+#if 0
+ val = eepro100_read_command(s);
+#endif
+ break;
+ case SCBIntmask:
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+ break;
+ case SCBPort + 3:
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+ break;
+ case SCBeeprom:
+ val = eepro100_read_eeprom(s);
+ break;
+ case SCBpmdr: /* Power Management Driver Register */
+ val = 0;
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+ break;
+ case SCBgstat: /* General Status Register */
+ /* 100 Mbps full duplex, valid link */
+ val = 0x07;
+ TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
+ break;
+ default:
+ logout("addr=%s val=0x%02x\n", regname(addr), val);
+ missing("unknown byte read");
+ }
+ return val;
+}
+
+static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
+{
+ uint16_t val = 0;
+ if (addr <= sizeof(s->mem) - sizeof(val)) {
+ memcpy(&val, &s->mem[addr], sizeof(val));
+ }
+
+ switch (addr) {
+ case SCBStatus:
+ case SCBCmd:
+ TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+ break;
+ case SCBeeprom:
+ val = eepro100_read_eeprom(s);
+ TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+ break;
+ default:
+ logout("addr=%s val=0x%04x\n", regname(addr), val);
+ missing("unknown word read");
+ }
+ return val;
+}
+
+static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
+{
+ uint32_t val = 0;
+ if (addr <= sizeof(s->mem) - sizeof(val)) {
+ memcpy(&val, &s->mem[addr], sizeof(val));
+ }
+
+ switch (addr) {
+ case SCBStatus:
+ TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+ break;
+ case SCBPointer:
+#if 0
+ val = eepro100_read_pointer(s);
+#endif
+ TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+ break;
+ case SCBPort:
+ val = eepro100_read_port(s);
+ TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+ break;
+ case SCBCtrlMDI:
+ val = eepro100_read_mdi(s);
+ break;
+ default:
+ logout("addr=%s val=0x%08x\n", regname(addr), val);
+ missing("unknown longword read");
+ }
+ return val;
+}
+
+static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
+{
+ /* SCBStatus is readonly. */
+ if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
+ memcpy(&s->mem[addr], &val, sizeof(val));
+ }
+
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+
+ switch (addr) {
+ case SCBStatus:
+ break;
+ case SCBAck:
+ eepro100_acknowledge(s);
+ break;
+ case SCBCmd:
+ eepro100_write_command(s, val);
+ break;
+ case SCBIntmask:
+ if (val & BIT(1)) {
+ eepro100_swi_interrupt(s);
+ }
+ eepro100_interrupt(s, 0);
+ break;
+ case SCBPort + 3:
+ case SCBFlow: /* does not exist on 82557 */
+ case SCBFlow + 1:
+ case SCBFlow + 2:
+ case SCBpmdr: /* does not exist on 82557 */
+ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+ break;
+ case SCBeeprom:
+ eepro100_write_eeprom(s->eeprom, val);
+ break;
+ default:
+ logout("addr=%s val=0x%02x\n", regname(addr), val);
+ missing("unknown byte write");
+ }
+}
+
+static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
+{
+ /* SCBStatus is readonly. */
+ if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
+ memcpy(&s->mem[addr], &val, sizeof(val));
+ }
+
+ TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+
+ switch (addr) {
+ case SCBStatus:
+ s->mem[SCBAck] = (val >> 8);
+ eepro100_acknowledge(s);
+ break;
+ case SCBCmd:
+ eepro100_write_command(s, val);
+ eepro100_write1(s, SCBIntmask, val >> 8);
+ break;
+ case SCBeeprom:
+ eepro100_write_eeprom(s->eeprom, val);
+ break;
+ default:
+ logout("addr=%s val=0x%04x\n", regname(addr), val);
+ missing("unknown word write");
+ }
+}
+
+static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
+{
+ if (addr <= sizeof(s->mem) - sizeof(val)) {
+ memcpy(&s->mem[addr], &val, sizeof(val));
+ }
+
+ switch (addr) {
+ case SCBPointer:
+ eepro100_write_pointer(s, val);
+ break;
+ case SCBPort:
+ TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+ eepro100_write_port(s, val);
+ break;
+ case SCBCtrlMDI:
+ eepro100_write_mdi(s, val);
+ break;
+ default:
+ logout("addr=%s val=0x%08x\n", regname(addr), val);
+ missing("unknown longword write");
+ }
+}
+
+/*****************************************************************************
+ *
+ * Port mapped I/O.
+ *
+ ****************************************************************************/
+
+static uint32_t ioport_read1(void *opaque, uint32_t addr)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s\n", regname(addr));
+#endif
+ return eepro100_read1(s, addr - s->region[1]);
+}
+
+static uint32_t ioport_read2(void *opaque, uint32_t addr)
+{
+ EEPRO100State *s = opaque;
+ return eepro100_read2(s, addr - s->region[1]);
+}
+
+static uint32_t ioport_read4(void *opaque, uint32_t addr)
+{
+ EEPRO100State *s = opaque;
+ return eepro100_read4(s, addr - s->region[1]);
+}
+
+static void ioport_write1(void *opaque, uint32_t addr, uint32_t val)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s val=0x%02x\n", regname(addr), val);
+#endif
+ eepro100_write1(s, addr - s->region[1], val);
+}
+
+static void ioport_write2(void *opaque, uint32_t addr, uint32_t val)
+{
+ EEPRO100State *s = opaque;
+ eepro100_write2(s, addr - s->region[1], val);
+}
+
+static void ioport_write4(void *opaque, uint32_t addr, uint32_t val)
+{
+ EEPRO100State *s = opaque;
+ eepro100_write4(s, addr - s->region[1], val);
+}
+
+/***********************************************************/
+/* PCI EEPRO100 definitions */
+
+static void pci_map(PCIDevice * pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
+
+ TRACE(OTHER, logout("region %d, addr=0x%08"FMT_PCIBUS", "
+ "size=0x%08"FMT_PCIBUS", type=%d\n",
+ region_num, addr, size, type));
+
+ assert(region_num == 1);
+ register_ioport_write(addr, size, 1, ioport_write1, s);
+ register_ioport_read(addr, size, 1, ioport_read1, s);
+ register_ioport_write(addr, size, 2, ioport_write2, s);
+ register_ioport_read(addr, size, 2, ioport_read2, s);
+ register_ioport_write(addr, size, 4, ioport_write4, s);
+ register_ioport_read(addr, size, 4, ioport_read4, s);
+
+ s->region[region_num] = addr;
+}
+
+/*****************************************************************************
+ *
+ * Memory mapped I/O.
+ *
+ ****************************************************************************/
+
+static void pci_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s val=0x%02x\n", regname(addr), val);
+#endif
+ eepro100_write1(s, addr, val);
+}
+
+static void pci_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s val=0x%02x\n", regname(addr), val);
+#endif
+ eepro100_write2(s, addr, val);
+}
+
+static void pci_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s val=0x%02x\n", regname(addr), val);
+#endif
+ eepro100_write4(s, addr, val);
+}
+
+static uint32_t pci_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s\n", regname(addr));
+#endif
+ return eepro100_read1(s, addr);
+}
+
+static uint32_t pci_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s\n", regname(addr));
+#endif
+ return eepro100_read2(s, addr);
+}
+
+static uint32_t pci_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ EEPRO100State *s = opaque;
+#if 0
+ logout("addr=%s\n", regname(addr));
+#endif
+ return eepro100_read4(s, addr);
+}
+
+static CPUWriteMemoryFunc * const pci_mmio_write[] = {
+ pci_mmio_writeb,
+ pci_mmio_writew,
+ pci_mmio_writel
+};
+
+static CPUReadMemoryFunc * const pci_mmio_read[] = {
+ pci_mmio_readb,
+ pci_mmio_readw,
+ pci_mmio_readl
+};
+
+static void pci_mmio_map(PCIDevice * pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
+
+ TRACE(OTHER, logout("region %d, addr=0x%08"FMT_PCIBUS", "
+ "size=0x%08"FMT_PCIBUS", type=%d\n",
+ region_num, addr, size, type));
+
+ assert(region_num == 0 || region_num == 2);
+
+ /* Map control / status registers and flash. */
+ cpu_register_physical_memory(addr, size, s->mmio_index);
+ s->region[region_num] = addr;
+}
+
+static int nic_can_receive(VLANClientState *nc)
+{
+ EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ TRACE(RXTX, logout("%p\n", s));
+ return get_ru_state(s) == ru_ready;
+#if 0
+ return !eepro100_buffer_full(s);
+#endif
+}
+
+static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size)
+{
+ /* TODO:
+ * - Magic packets should set bit 30 in power management driver register.
+ * - Interesting packets should set bit 29 in power management driver register.
+ */
+ EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ uint16_t rfd_status = 0xa000;
+ static const uint8_t broadcast_macaddr[6] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ if (s->configuration[8] & 0x80) {
+ /* CSMA is disabled. */
+ logout("%p received while CSMA is disabled\n", s);
+ return -1;
+ } else if (size < 64 && (s->configuration[7] & BIT(0))) {
+ /* Short frame and configuration byte 7/0 (discard short receive) set:
+ * Short frame is discarded */
+ logout("%p received short frame (%zu byte)\n", s, size);
+ s->statistics.rx_short_frame_errors++;
+#if 0
+ return -1;
+#endif
+ } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
+ /* Long frame and configuration byte 18/3 (long receive ok) not set:
+ * Long frames are discarded. */
+ logout("%p received long frame (%zu byte), ignored\n", s, size);
+ return -1;
+ } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */
+ /* Frame matches individual address. */
+ /* TODO: check configuration byte 15/4 (ignore U/L). */
+ TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
+ } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
+ /* Broadcast frame. */
+ TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
+ rfd_status |= 0x0002;
+ } else if (buf[0] & 0x01) {
+ /* Multicast frame. */
+ TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
+ if (s->configuration[21] & BIT(3)) {
+ /* Multicast all bit is set, receive all multicast frames. */
+ } else {
+ unsigned mcast_idx = compute_mcast_idx(buf);
+ assert(mcast_idx < 64);
+ if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
+ /* Multicast frame is allowed in hash table. */
+ } else if (s->configuration[15] & BIT(0)) {
+ /* Promiscuous: receive all. */
+ rfd_status |= 0x0004;
+ } else {
+ TRACE(RXTX, logout("%p multicast ignored\n", s));
+ return -1;
+ }
+ }
+ /* TODO: Next not for promiscuous mode? */
+ rfd_status |= 0x0002;
+ } else if (s->configuration[15] & BIT(0)) {
+ /* Promiscuous: receive all. */
+ TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
+ rfd_status |= 0x0004;
+ } else if (s->configuration[20] & BIT(6)) {
+ /* Multiple IA bit set. */
+ unsigned mcast_idx = compute_mcast_idx(buf);
+ assert(mcast_idx < 64);
+ if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
+ TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
+ } else {
+ TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
+ return -1;
+ }
+ } else {
+ TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
+ nic_dump(buf, size)));
+ return size;
+ }
+
+ if (get_ru_state(s) != ru_ready) {
+ /* No resources available. */
+ logout("no resources, state=%u\n", get_ru_state(s));
+ /* TODO: RNR interrupt only at first failed frame? */
+ eepro100_rnr_interrupt(s);
+ s->statistics.rx_resource_errors++;
+#if 0
+ assert(!"no resources");
+#endif
+ return -1;
+ }
+ /* !!! */
+ eepro100_rx_t rx;
+ cpu_physical_memory_read(s->ru_base + s->ru_offset, (uint8_t *) & rx,
+ offsetof(eepro100_rx_t, packet));
+ uint16_t rfd_command = le16_to_cpu(rx.command);
+ uint16_t rfd_size = le16_to_cpu(rx.size);
+
+ if (size > rfd_size) {
+ logout("Receive buffer (%" PRId16 " bytes) too small for data "
+ "(%zu bytes); data truncated\n", rfd_size, size);
+ size = rfd_size;
+ }
+ if (size < 64) {
+ rfd_status |= 0x0080;
+ }
+ TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
+ rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
+ stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, status),
+ rfd_status);
+ stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, count), size);
+ /* Early receive interrupt not supported. */
+#if 0
+ eepro100_er_interrupt(s);
+#endif
+ /* Receive CRC Transfer not supported. */
+ if (s->configuration[18] & BIT(2)) {
+ missing("Receive CRC Transfer");
+ return -1;
+ }
+ /* TODO: check stripping enable bit. */
+#if 0
+ assert(!(s->configuration[17] & BIT(0)));
+#endif
+ cpu_physical_memory_write(s->ru_base + s->ru_offset +
+ offsetof(eepro100_rx_t, packet), buf, size);
+ s->statistics.rx_good_frames++;
+ eepro100_fr_interrupt(s);
+ s->ru_offset = le32_to_cpu(rx.link);
+ if (rfd_command & COMMAND_EL) {
+ /* EL bit is set, so this was the last frame. */
+ logout("receive: Running out of frames\n");
+ set_ru_state(s, ru_suspended);
+ }
+ if (rfd_command & COMMAND_S) {
+ /* S bit is set. */
+ set_ru_state(s, ru_suspended);
+ }
+ return size;
+}
+
+static const VMStateDescription vmstate_eepro100 = {
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, EEPRO100State),
+ VMSTATE_UNUSED(32),
+ VMSTATE_BUFFER(mult, EEPRO100State),
+ VMSTATE_BUFFER(mem, EEPRO100State),
+ /* Save all members of struct between scb_stat and mem. */
+ VMSTATE_UINT8(scb_stat, EEPRO100State),
+ VMSTATE_UINT8(int_stat, EEPRO100State),
+ VMSTATE_UNUSED(3*4),
+ VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
+ VMSTATE_UNUSED(19*4),
+ VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
+ /* The eeprom should be saved and restored by its own routines. */
+ VMSTATE_UINT32(device, EEPRO100State),
+ /* TODO check device. */
+ VMSTATE_UINT32(pointer, EEPRO100State),
+ VMSTATE_UINT32(cu_base, EEPRO100State),
+ VMSTATE_UINT32(cu_offset, EEPRO100State),
+ VMSTATE_UINT32(ru_base, EEPRO100State),
+ VMSTATE_UINT32(ru_offset, EEPRO100State),
+ VMSTATE_UINT32(statsaddr, EEPRO100State),
+ /* Save eepro100_stats_t statistics. */
+ VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
+ VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
+ VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
+ VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
+ VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
+ VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
+ VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
+ VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
+ /* Configuration bytes. */
+ VMSTATE_BUFFER(configuration, EEPRO100State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void nic_cleanup(VLANClientState *nc)
+{
+ EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static int pci_nic_uninit(PCIDevice *pci_dev)
+{
+ EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
+
+ cpu_unregister_io_memory(s->mmio_index);
+ vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
+ eeprom93xx_free(&pci_dev->qdev, s->eeprom);
+ qemu_del_vlan_client(&s->nic->nc);
+ return 0;
+}
+
+static NetClientInfo net_eepro100_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = nic_can_receive,
+ .receive = nic_receive,
+ .cleanup = nic_cleanup,
+};
+
+static int e100_nic_init(PCIDevice *pci_dev)
+{
+ EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
+ E100PCIDeviceInfo *e100_device = DO_UPCAST(E100PCIDeviceInfo, pci.qdev,
+ pci_dev->qdev.info);
+
+ TRACE(OTHER, logout("\n"));
+
+ s->device = e100_device->device;
+
+ e100_pci_reset(s, e100_device);
+
+ /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
+ * i82559 and later support 64 or 256 word EEPROM. */
+ s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
+
+ /* Handler for memory-mapped I/O */
+ s->mmio_index =
+ cpu_register_io_memory(pci_mmio_read, pci_mmio_write, s,
+ DEVICE_NATIVE_ENDIAN);
+
+ pci_register_bar(&s->dev, 0, PCI_MEM_SIZE,
+ PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH, pci_mmio_map);
+ pci_register_bar(&s->dev, 1, PCI_IO_SIZE, PCI_BASE_ADDRESS_SPACE_IO,
+ pci_map);
+ pci_register_bar(&s->dev, 2, PCI_FLASH_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ pci_mmio_map);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
+ assert(s->region[1] == 0);
+
+ nic_reset(s);
+
+ s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
+ pci_dev->qdev.info->name, pci_dev->qdev.id, s);
+
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ TRACE(OTHER, logout("%s\n", s->nic->nc.info_str));
+
+ qemu_register_reset(nic_reset, s);
+
+ s->vmstate = qemu_malloc(sizeof(vmstate_eepro100));
+ memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100));
+ s->vmstate->name = s->nic->nc.model;
+ vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
+
+ add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
+ return 0;
+}
+
+static E100PCIDeviceInfo e100_devices[] = {
+ {
+ .pci.qdev.name = "i82550",
+ .pci.qdev.desc = "Intel i82550 Ethernet",
+ .device = i82550,
+ /* TODO: check device id. */
+ .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+ /* Revision ID: 0x0c, 0x0d, 0x0e. */
+ .revision = 0x0e,
+ /* TODO: check size of statistical counters. */
+ .stats_size = 80,
+ /* TODO: check extended tcb support. */
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82551",
+ .pci.qdev.desc = "Intel i82551 Ethernet",
+ .device = i82551,
+ .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+ /* Revision ID: 0x0f, 0x10. */
+ .revision = 0x0f,
+ /* TODO: check size of statistical counters. */
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82557a",
+ .pci.qdev.desc = "Intel i82557A Ethernet",
+ .device = i82557A,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x01,
+ .power_management = false,
+ },{
+ .pci.qdev.name = "i82557b",
+ .pci.qdev.desc = "Intel i82557B Ethernet",
+ .device = i82557B,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x02,
+ .power_management = false,
+ },{
+ .pci.qdev.name = "i82557c",
+ .pci.qdev.desc = "Intel i82557C Ethernet",
+ .device = i82557C,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x03,
+ .power_management = false,
+ },{
+ .pci.qdev.name = "i82558a",
+ .pci.qdev.desc = "Intel i82558A Ethernet",
+ .device = i82558A,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x04,
+ .stats_size = 76,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82558b",
+ .pci.qdev.desc = "Intel i82558B Ethernet",
+ .device = i82558B,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x05,
+ .stats_size = 76,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82559a",
+ .pci.qdev.desc = "Intel i82559A Ethernet",
+ .device = i82559A,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x06,
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82559b",
+ .pci.qdev.desc = "Intel i82559B Ethernet",
+ .device = i82559B,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+ .revision = 0x07,
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82559c",
+ .pci.qdev.desc = "Intel i82559C Ethernet",
+ .device = i82559C,
+ .device_id = PCI_DEVICE_ID_INTEL_82557,
+#if 0
+ .revision = 0x08,
+#endif
+ /* TODO: Windows wants revision id 0x0c. */
+ .revision = 0x0c,
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82559er",
+ .pci.qdev.desc = "Intel i82559ER Ethernet",
+ .device = i82559ER,
+ .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+ .revision = 0x09,
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ .pci.qdev.name = "i82562",
+ .pci.qdev.desc = "Intel i82562 Ethernet",
+ .device = i82562,
+ /* TODO: check device id. */
+ .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+ /* TODO: wrong revision id. */
+ .revision = 0x0e,
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ },{
+ /* Toshiba Tecra 8200. */
+ .pci.qdev.name = "i82801",
+ .pci.qdev.desc = "Intel i82801 Ethernet",
+ .device = i82801,
+ .device_id = 0x2449,
+ .revision = 0x03,
+ .stats_size = 80,
+ .has_extended_tcb_support = true,
+ .power_management = true,
+ }
+};
+
+static Property e100_properties[] = {
+ DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void eepro100_register_devices(void)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
+ PCIDeviceInfo *pci_dev = &e100_devices[i].pci;
+ /* We use the same rom file for all device ids.
+ QEMU fixes the device id during rom load. */
+ pci_dev->romfile = "gpxe-eepro100-80861209.rom";
+ pci_dev->init = e100_nic_init;
+ pci_dev->exit = pci_nic_uninit;
+ pci_dev->qdev.props = e100_properties;
+ pci_dev->qdev.size = sizeof(EEPRO100State);
+ pci_qdev_register(pci_dev);
+ }
+}
+
+device_init(eepro100_register_devices)
diff --git a/hw/eeprom93xx.c b/hw/eeprom93xx.c
new file mode 100644
index 0000000..660b28f
--- /dev/null
+++ b/hw/eeprom93xx.c
@@ -0,0 +1,337 @@
+/*
+ * QEMU EEPROM 93xx emulation
+ *
+ * Copyright (c) 2006-2007 Stefan Weil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+/* Emulation for serial EEPROMs:
+ * NMC93C06 256-Bit (16 x 16)
+ * NMC93C46 1024-Bit (64 x 16)
+ * NMC93C56 2028 Bit (128 x 16)
+ * NMC93C66 4096 Bit (256 x 16)
+ * Compatible devices include FM93C46 and others.
+ *
+ * Other drivers use these interface functions:
+ * eeprom93xx_new - add a new EEPROM (with 16, 64 or 256 words)
+ * eeprom93xx_free - destroy EEPROM
+ * eeprom93xx_read - read data from the EEPROM
+ * eeprom93xx_write - write data to the EEPROM
+ * eeprom93xx_data - get EEPROM data array for external manipulation
+ *
+ * Todo list:
+ * - No emulation of EEPROM timings.
+ */
+
+#include "hw.h"
+#include "eeprom93xx.h"
+
+/* Debug EEPROM emulation. */
+//~ #define DEBUG_EEPROM
+
+#ifdef DEBUG_EEPROM
+#define logout(fmt, ...) fprintf(stderr, "EEPROM\t%-24s" fmt, __func__, ## __VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+#define EEPROM_INSTANCE 0
+#define OLD_EEPROM_VERSION 20061112
+#define EEPROM_VERSION (OLD_EEPROM_VERSION + 1)
+
+#if 0
+typedef enum {
+ eeprom_read = 0x80, /* read register xx */
+ eeprom_write = 0x40, /* write register xx */
+ eeprom_erase = 0xc0, /* erase register xx */
+ eeprom_ewen = 0x30, /* erase / write enable */
+ eeprom_ewds = 0x00, /* erase / write disable */
+ eeprom_eral = 0x20, /* erase all registers */
+ eeprom_wral = 0x10, /* write all registers */
+ eeprom_amask = 0x0f,
+ eeprom_imask = 0xf0
+} eeprom_instruction_t;
+#endif
+
+#ifdef DEBUG_EEPROM
+static const char *opstring[] = {
+ "extended", "write", "read", "erase"
+};
+#endif
+
+struct _eeprom_t {
+ uint8_t tick;
+ uint8_t address;
+ uint8_t command;
+ uint8_t writeable;
+
+ uint8_t eecs;
+ uint8_t eesk;
+ uint8_t eedo;
+
+ uint8_t addrbits;
+ uint16_t size;
+ uint16_t data;
+ uint16_t contents[0];
+};
+
+/* Code for saving and restoring of EEPROM state. */
+
+/* Restore an uint16_t from an uint8_t
+ This is a Big hack, but it is how the old state did it.
+ */
+
+static int get_uint16_from_uint8(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ *v = qemu_get_ubyte(f);
+ return 0;
+}
+
+static void put_unused(QEMUFile *f, void *pv, size_t size)
+{
+ fprintf(stderr, "uint16_from_uint8 is used only for backwards compatibility.\n");
+ fprintf(stderr, "Never should be used to write a new state.\n");
+ exit(0);
+}
+
+static const VMStateInfo vmstate_hack_uint16_from_uint8 = {
+ .name = "uint16_from_uint8",
+ .get = get_uint16_from_uint8,
+ .put = put_unused,
+};
+
+#define VMSTATE_UINT16_HACK_TEST(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint16_from_uint8, uint16_t)
+
+static bool is_old_eeprom_version(void *opaque, int version_id)
+{
+ return version_id == OLD_EEPROM_VERSION;
+}
+
+static const VMStateDescription vmstate_eeprom = {
+ .name = "eeprom",
+ .version_id = EEPROM_VERSION,
+ .minimum_version_id = OLD_EEPROM_VERSION,
+ .minimum_version_id_old = OLD_EEPROM_VERSION,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(tick, eeprom_t),
+ VMSTATE_UINT8(address, eeprom_t),
+ VMSTATE_UINT8(command, eeprom_t),
+ VMSTATE_UINT8(writeable, eeprom_t),
+
+ VMSTATE_UINT8(eecs, eeprom_t),
+ VMSTATE_UINT8(eesk, eeprom_t),
+ VMSTATE_UINT8(eedo, eeprom_t),
+
+ VMSTATE_UINT8(addrbits, eeprom_t),
+ VMSTATE_UINT16_HACK_TEST(size, eeprom_t, is_old_eeprom_version),
+ VMSTATE_UNUSED_TEST(is_old_eeprom_version, 1),
+ VMSTATE_UINT16_EQUAL_V(size, eeprom_t, EEPROM_VERSION),
+ VMSTATE_UINT16(data, eeprom_t),
+ VMSTATE_VARRAY_UINT16_UNSAFE(contents, eeprom_t, size, 0,
+ vmstate_info_uint16, uint16_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi)
+{
+ uint8_t tick = eeprom->tick;
+ uint8_t eedo = eeprom->eedo;
+ uint16_t address = eeprom->address;
+ uint8_t command = eeprom->command;
+
+ logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
+ eecs, eesk, eedi, eedo, tick);
+
+ if (! eeprom->eecs && eecs) {
+ /* Start chip select cycle. */
+ logout("Cycle start, waiting for 1st start bit (0)\n");
+ tick = 0;
+ command = 0x0;
+ address = 0x0;
+ } else if (eeprom->eecs && ! eecs) {
+ /* End chip select cycle. This triggers write / erase. */
+ if (eeprom->writeable) {
+ uint8_t subcommand = address >> (eeprom->addrbits - 2);
+ if (command == 0 && subcommand == 2) {
+ /* Erase all. */
+ for (address = 0; address < eeprom->size; address++) {
+ eeprom->contents[address] = 0xffff;
+ }
+ } else if (command == 3) {
+ /* Erase word. */
+ eeprom->contents[address] = 0xffff;
+ } else if (tick >= 2 + 2 + eeprom->addrbits + 16) {
+ if (command == 1) {
+ /* Write word. */
+ eeprom->contents[address] &= eeprom->data;
+ } else if (command == 0 && subcommand == 1) {
+ /* Write all. */
+ for (address = 0; address < eeprom->size; address++) {
+ eeprom->contents[address] &= eeprom->data;
+ }
+ }
+ }
+ }
+ /* Output DO is tristate, read results in 1. */
+ eedo = 1;
+ } else if (eecs && ! eeprom->eesk && eesk) {
+ /* Raising edge of clock shifts data in. */
+ if (tick == 0) {
+ /* Wait for 1st start bit. */
+ if (eedi == 0) {
+ logout("Got correct 1st start bit, waiting for 2nd start bit (1)\n");
+ tick++;
+ } else {
+ logout("wrong 1st start bit (is 1, should be 0)\n");
+ tick = 2;
+ //~ assert(!"wrong start bit");
+ }
+ } else if (tick == 1) {
+ /* Wait for 2nd start bit. */
+ if (eedi != 0) {
+ logout("Got correct 2nd start bit, getting command + address\n");
+ tick++;
+ } else {
+ logout("1st start bit is longer than needed\n");
+ }
+ } else if (tick < 2 + 2) {
+ /* Got 2 start bits, transfer 2 opcode bits. */
+ tick++;
+ command <<= 1;
+ if (eedi) {
+ command += 1;
+ }
+ } else if (tick < 2 + 2 + eeprom->addrbits) {
+ /* Got 2 start bits and 2 opcode bits, transfer all address bits. */
+ tick++;
+ address = ((address << 1) | eedi);
+ if (tick == 2 + 2 + eeprom->addrbits) {
+ logout("%s command, address = 0x%02x (value 0x%04x)\n",
+ opstring[command], address, eeprom->contents[address]);
+ if (command == 2) {
+ eedo = 0;
+ }
+ address = address % eeprom->size;
+ if (command == 0) {
+ /* Command code in upper 2 bits of address. */
+ switch (address >> (eeprom->addrbits - 2)) {
+ case 0:
+ logout("write disable command\n");
+ eeprom->writeable = 0;
+ break;
+ case 1:
+ logout("write all command\n");
+ break;
+ case 2:
+ logout("erase all command\n");
+ break;
+ case 3:
+ logout("write enable command\n");
+ eeprom->writeable = 1;
+ break;
+ }
+ } else {
+ /* Read, write or erase word. */
+ eeprom->data = eeprom->contents[address];
+ }
+ }
+ } else if (tick < 2 + 2 + eeprom->addrbits + 16) {
+ /* Transfer 16 data bits. */
+ tick++;
+ if (command == 2) {
+ /* Read word. */
+ eedo = ((eeprom->data & 0x8000) != 0);
+ }
+ eeprom->data <<= 1;
+ eeprom->data += eedi;
+ } else {
+ logout("additional unneeded tick, not processed\n");
+ }
+ }
+ /* Save status of EEPROM. */
+ eeprom->tick = tick;
+ eeprom->eecs = eecs;
+ eeprom->eesk = eesk;
+ eeprom->eedo = eedo;
+ eeprom->address = address;
+ eeprom->command = command;
+}
+
+uint16_t eeprom93xx_read(eeprom_t *eeprom)
+{
+ /* Return status of pin DO (0 or 1). */
+ logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo);
+ return (eeprom->eedo);
+}
+
+#if 0
+void eeprom93xx_reset(eeprom_t *eeprom)
+{
+ /* prepare eeprom */
+ logout("eeprom = 0x%p\n", eeprom);
+ eeprom->tick = 0;
+ eeprom->command = 0;
+}
+#endif
+
+eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords)
+{
+ /* Add a new EEPROM (with 16, 64 or 256 words). */
+ eeprom_t *eeprom;
+ uint8_t addrbits;
+
+ switch (nwords) {
+ case 16:
+ case 64:
+ addrbits = 6;
+ break;
+ case 128:
+ case 256:
+ addrbits = 8;
+ break;
+ default:
+ assert(!"Unsupported EEPROM size, fallback to 64 words!");
+ nwords = 64;
+ addrbits = 6;
+ }
+
+ eeprom = (eeprom_t *)qemu_mallocz(sizeof(*eeprom) + nwords * 2);
+ eeprom->size = nwords;
+ eeprom->addrbits = addrbits;
+ /* Output DO is tristate, read results in 1. */
+ eeprom->eedo = 1;
+ logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
+ vmstate_register(dev, 0, &vmstate_eeprom, eeprom);
+ return eeprom;
+}
+
+void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom)
+{
+ /* Destroy EEPROM. */
+ logout("eeprom = 0x%p\n", eeprom);
+ vmstate_unregister(dev, &vmstate_eeprom, eeprom);
+ qemu_free(eeprom);
+}
+
+uint16_t *eeprom93xx_data(eeprom_t *eeprom)
+{
+ /* Get EEPROM data array. */
+ return &eeprom->contents[0];
+}
+
+/* eof */
diff --git a/hw/eeprom93xx.h b/hw/eeprom93xx.h
new file mode 100644
index 0000000..8ba0e28
--- /dev/null
+++ b/hw/eeprom93xx.h
@@ -0,0 +1,40 @@
+/*
+ * QEMU EEPROM 93xx emulation
+ *
+ * Copyright (c) 2006-2007 Stefan Weil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef EEPROM93XX_H
+#define EEPROM93XX_H
+
+typedef struct _eeprom_t eeprom_t;
+
+/* Create a new EEPROM with (nwords * 2) bytes. */
+eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords);
+
+/* Destroy an existing EEPROM. */
+void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom);
+
+/* Read from the EEPROM. */
+uint16_t eeprom93xx_read(eeprom_t *eeprom);
+
+/* Write to the EEPROM. */
+void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi);
+
+/* Get EEPROM data array. */
+uint16_t *eeprom93xx_data(eeprom_t *eeprom);
+
+#endif /* EEPROM93XX_H */
diff --git a/hw/elf_ops.h b/hw/elf_ops.h
new file mode 100644
index 0000000..0bd7235
--- /dev/null
+++ b/hw/elf_ops.h
@@ -0,0 +1,297 @@
+static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr)
+{
+ bswap16s(&ehdr->e_type); /* Object file type */
+ bswap16s(&ehdr->e_machine); /* Architecture */
+ bswap32s(&ehdr->e_version); /* Object file version */
+ bswapSZs(&ehdr->e_entry); /* Entry point virtual address */
+ bswapSZs(&ehdr->e_phoff); /* Program header table file offset */
+ bswapSZs(&ehdr->e_shoff); /* Section header table file offset */
+ bswap32s(&ehdr->e_flags); /* Processor-specific flags */
+ bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
+ bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
+ bswap16s(&ehdr->e_phnum); /* Program header table entry count */
+ bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
+ bswap16s(&ehdr->e_shnum); /* Section header table entry count */
+ bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
+}
+
+static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr)
+{
+ bswap32s(&phdr->p_type); /* Segment type */
+ bswapSZs(&phdr->p_offset); /* Segment file offset */
+ bswapSZs(&phdr->p_vaddr); /* Segment virtual address */
+ bswapSZs(&phdr->p_paddr); /* Segment physical address */
+ bswapSZs(&phdr->p_filesz); /* Segment size in file */
+ bswapSZs(&phdr->p_memsz); /* Segment size in memory */
+ bswap32s(&phdr->p_flags); /* Segment flags */
+ bswapSZs(&phdr->p_align); /* Segment alignment */
+}
+
+static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr)
+{
+ bswap32s(&shdr->sh_name);
+ bswap32s(&shdr->sh_type);
+ bswapSZs(&shdr->sh_flags);
+ bswapSZs(&shdr->sh_addr);
+ bswapSZs(&shdr->sh_offset);
+ bswapSZs(&shdr->sh_size);
+ bswap32s(&shdr->sh_link);
+ bswap32s(&shdr->sh_info);
+ bswapSZs(&shdr->sh_addralign);
+ bswapSZs(&shdr->sh_entsize);
+}
+
+static void glue(bswap_sym, SZ)(struct elf_sym *sym)
+{
+ bswap32s(&sym->st_name);
+ bswapSZs(&sym->st_value);
+ bswapSZs(&sym->st_size);
+ bswap16s(&sym->st_shndx);
+}
+
+static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table,
+ int n, int type)
+{
+ int i;
+ for(i=0;i<n;i++) {
+ if (shdr_table[i].sh_type == type)
+ return shdr_table + i;
+ }
+ return NULL;
+}
+
+static int glue(symfind, SZ)(const void *s0, const void *s1)
+{
+ struct elf_sym *key = (struct elf_sym *)s0;
+ struct elf_sym *sym = (struct elf_sym *)s1;
+ int result = 0;
+ if (key->st_value < sym->st_value) {
+ result = -1;
+ } else if (key->st_value >= sym->st_value + sym->st_size) {
+ result = 1;
+ }
+ return result;
+}
+
+static const char *glue(lookup_symbol, SZ)(struct syminfo *s,
+ target_phys_addr_t orig_addr)
+{
+ struct elf_sym *syms = glue(s->disas_symtab.elf, SZ);
+ struct elf_sym key;
+ struct elf_sym *sym;
+
+ key.st_value = orig_addr;
+
+ sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), glue(symfind, SZ));
+ if (sym != NULL) {
+ return s->disas_strtab + sym->st_name;
+ }
+
+ return "";
+}
+
+static int glue(symcmp, SZ)(const void *s0, const void *s1)
+{
+ struct elf_sym *sym0 = (struct elf_sym *)s0;
+ struct elf_sym *sym1 = (struct elf_sym *)s1;
+ return (sym0->st_value < sym1->st_value)
+ ? -1
+ : ((sym0->st_value > sym1->st_value) ? 1 : 0);
+}
+
+static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
+ int clear_lsb)
+{
+ struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
+ struct elf_sym *syms = NULL;
+ struct syminfo *s;
+ int nsyms, i;
+ char *str = NULL;
+
+ shdr_table = load_at(fd, ehdr->e_shoff,
+ sizeof(struct elf_shdr) * ehdr->e_shnum);
+ if (!shdr_table)
+ return -1;
+
+ if (must_swab) {
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ glue(bswap_shdr, SZ)(shdr_table + i);
+ }
+ }
+
+ symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
+ if (!symtab)
+ goto fail;
+ syms = load_at(fd, symtab->sh_offset, symtab->sh_size);
+ if (!syms)
+ goto fail;
+
+ nsyms = symtab->sh_size / sizeof(struct elf_sym);
+
+ i = 0;
+ while (i < nsyms) {
+ if (must_swab)
+ glue(bswap_sym, SZ)(&syms[i]);
+ /* We are only interested in function symbols.
+ Throw everything else away. */
+ if (syms[i].st_shndx == SHN_UNDEF ||
+ syms[i].st_shndx >= SHN_LORESERVE ||
+ ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
+ nsyms--;
+ if (i < nsyms) {
+ syms[i] = syms[nsyms];
+ }
+ continue;
+ }
+ if (clear_lsb) {
+ /* The bottom address bit marks a Thumb or MIPS16 symbol. */
+ syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1;
+ }
+ i++;
+ }
+ if (nsyms) {
+ syms = qemu_realloc(syms, nsyms * sizeof(*syms));
+
+ qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ));
+ for (i = 0; i < nsyms - 1; i++) {
+ if (syms[i].st_size == 0) {
+ syms[i].st_size = syms[i + 1].st_value - syms[i].st_value;
+ }
+ }
+ } else {
+ qemu_free(syms);
+ syms = NULL;
+ }
+
+ /* String table */
+ if (symtab->sh_link >= ehdr->e_shnum)
+ goto fail;
+ strtab = &shdr_table[symtab->sh_link];
+
+ str = load_at(fd, strtab->sh_offset, strtab->sh_size);
+ if (!str)
+ goto fail;
+
+ /* Commit */
+ s = qemu_mallocz(sizeof(*s));
+ s->lookup_symbol = glue(lookup_symbol, SZ);
+ glue(s->disas_symtab.elf, SZ) = syms;
+ s->disas_num_syms = nsyms;
+ s->disas_strtab = str;
+ s->next = syminfos;
+ syminfos = s;
+ qemu_free(shdr_table);
+ return 0;
+ fail:
+ qemu_free(syms);
+ qemu_free(str);
+ qemu_free(shdr_table);
+ return -1;
+}
+
+static int glue(load_elf, SZ)(const char *name, int fd,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque,
+ int must_swab, uint64_t *pentry,
+ uint64_t *lowaddr, uint64_t *highaddr,
+ int elf_machine, int clear_lsb)
+{
+ struct elfhdr ehdr;
+ struct elf_phdr *phdr = NULL, *ph;
+ int size, i, total_size;
+ elf_word mem_size;
+ uint64_t addr, low = (uint64_t)-1, high = 0;
+ uint8_t *data = NULL;
+ char label[128];
+
+ if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
+ goto fail;
+ if (must_swab) {
+ glue(bswap_ehdr, SZ)(&ehdr);
+ }
+
+ switch (elf_machine) {
+ case EM_PPC64:
+ if (EM_PPC64 != ehdr.e_machine)
+ if (EM_PPC != ehdr.e_machine)
+ goto fail;
+ break;
+ case EM_X86_64:
+ if (EM_X86_64 != ehdr.e_machine)
+ if (EM_386 != ehdr.e_machine)
+ goto fail;
+ break;
+ case EM_MICROBLAZE:
+ if (EM_MICROBLAZE != ehdr.e_machine)
+ if (EM_MICROBLAZE_OLD != ehdr.e_machine)
+ goto fail;
+ break;
+ default:
+ if (elf_machine != ehdr.e_machine)
+ goto fail;
+ }
+
+ if (pentry)
+ *pentry = (uint64_t)(elf_sword)ehdr.e_entry;
+
+ glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb);
+
+ size = ehdr.e_phnum * sizeof(phdr[0]);
+ lseek(fd, ehdr.e_phoff, SEEK_SET);
+ phdr = qemu_mallocz(size);
+ if (!phdr)
+ goto fail;
+ if (read(fd, phdr, size) != size)
+ goto fail;
+ if (must_swab) {
+ for(i = 0; i < ehdr.e_phnum; i++) {
+ ph = &phdr[i];
+ glue(bswap_phdr, SZ)(ph);
+ }
+ }
+
+ total_size = 0;
+ for(i = 0; i < ehdr.e_phnum; i++) {
+ ph = &phdr[i];
+ if (ph->p_type == PT_LOAD) {
+ mem_size = ph->p_memsz;
+ /* XXX: avoid allocating */
+ data = qemu_mallocz(mem_size);
+ if (ph->p_filesz > 0) {
+ if (lseek(fd, ph->p_offset, SEEK_SET) < 0)
+ goto fail;
+ if (read(fd, data, ph->p_filesz) != ph->p_filesz)
+ goto fail;
+ }
+ /* address_offset is hack for kernel images that are
+ linked at the wrong physical address. */
+ if (translate_fn) {
+ addr = translate_fn(translate_opaque, ph->p_paddr);
+ } else {
+ addr = ph->p_paddr;
+ }
+
+ snprintf(label, sizeof(label), "phdr #%d: %s", i, name);
+ rom_add_blob_fixed(label, data, mem_size, addr);
+
+ total_size += mem_size;
+ if (addr < low)
+ low = addr;
+ if ((addr + mem_size) > high)
+ high = addr + mem_size;
+
+ qemu_free(data);
+ data = NULL;
+ }
+ }
+ qemu_free(phdr);
+ if (lowaddr)
+ *lowaddr = (uint64_t)(elf_sword)low;
+ if (highaddr)
+ *highaddr = (uint64_t)(elf_sword)high;
+ return total_size;
+ fail:
+ qemu_free(data);
+ qemu_free(phdr);
+ return -1;
+}
diff --git a/hw/empty_slot.c b/hw/empty_slot.c
new file mode 100644
index 0000000..664b8d9
--- /dev/null
+++ b/hw/empty_slot.c
@@ -0,0 +1,93 @@
+/*
+ * QEMU Empty Slot
+ *
+ * The empty_slot device emulates known to a bus but not connected devices.
+ *
+ * Copyright (c) 2010 Artyom Tarasenko
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any later
+ * version.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "empty_slot.h"
+
+//#define DEBUG_EMPTY_SLOT
+
+#ifdef DEBUG_EMPTY_SLOT
+#define DPRINTF(fmt, ...) \
+ do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+typedef struct EmptySlot {
+ SysBusDevice busdev;
+ uint64_t size;
+} EmptySlot;
+
+static uint32_t empty_slot_readl(void *opaque, target_phys_addr_t addr)
+{
+ DPRINTF("read from " TARGET_FMT_plx "\n", addr);
+ return 0;
+}
+
+static void empty_slot_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", val, addr);
+}
+
+CPUReadMemoryFunc * const empty_slot_read[3] = {
+ empty_slot_readl,
+ empty_slot_readl,
+ empty_slot_readl,
+};
+
+static CPUWriteMemoryFunc * const empty_slot_write[3] = {
+ empty_slot_writel,
+ empty_slot_writel,
+ empty_slot_writel,
+};
+
+void empty_slot_init(target_phys_addr_t addr, uint64_t slot_size)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ EmptySlot *e;
+
+ dev = qdev_create(NULL, "empty_slot");
+ s = sysbus_from_qdev(dev);
+ e = FROM_SYSBUS(EmptySlot, s);
+ e->size = slot_size;
+
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+}
+
+static int empty_slot_init1(SysBusDevice *dev)
+{
+ EmptySlot *s = FROM_SYSBUS(EmptySlot, dev);
+ ram_addr_t empty_slot_offset;
+
+ empty_slot_offset = cpu_register_io_memory(empty_slot_read,
+ empty_slot_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, s->size, empty_slot_offset | IO_MEM_RAM);
+ return 0;
+}
+
+static SysBusDeviceInfo empty_slot_info = {
+ .init = empty_slot_init1,
+ .qdev.name = "empty_slot",
+ .qdev.size = sizeof(EmptySlot),
+};
+
+static void empty_slot_register_devices(void)
+{
+ sysbus_register_withprop(&empty_slot_info);
+}
+
+device_init(empty_slot_register_devices);
diff --git a/hw/empty_slot.h b/hw/empty_slot.h
new file mode 100644
index 0000000..78dc91d
--- /dev/null
+++ b/hw/empty_slot.h
@@ -0,0 +1,2 @@
+/* empty_slot.c */
+void empty_slot_init(target_phys_addr_t addr, uint64_t slot_size);
diff --git a/hw/es1370.c b/hw/es1370.c
new file mode 100644
index 0000000..40cb48c
--- /dev/null
+++ b/hw/es1370.c
@@ -0,0 +1,1053 @@
+/*
+ * QEMU ES1370 emulation
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* #define DEBUG_ES1370 */
+/* #define VERBOSE_ES1370 */
+#define SILENT_ES1370
+
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "pci.h"
+
+/* Missing stuff:
+ SCTRL_P[12](END|ST)INC
+ SCTRL_P1SCTRLD
+ SCTRL_P2DACSEN
+ CTRL_DAC_SYNC
+ MIDI
+ non looped mode
+ surely more
+*/
+
+/*
+ Following macros and samplerate array were copied verbatim from
+ Linux kernel 2.4.30: drivers/sound/es1370.c
+
+ Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
+*/
+
+/* Start blatant GPL violation */
+
+#define ES1370_REG_CONTROL 0x00
+#define ES1370_REG_STATUS 0x04
+#define ES1370_REG_UART_DATA 0x08
+#define ES1370_REG_UART_STATUS 0x09
+#define ES1370_REG_UART_CONTROL 0x09
+#define ES1370_REG_UART_TEST 0x0a
+#define ES1370_REG_MEMPAGE 0x0c
+#define ES1370_REG_CODEC 0x10
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT 0x24
+#define ES1370_REG_DAC2_SCOUNT 0x28
+#define ES1370_REG_ADC_SCOUNT 0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR 0xc30
+#define ES1370_REG_DAC1_FRAMECNT 0xc34
+#define ES1370_REG_DAC2_FRAMEADR 0xc38
+#define ES1370_REG_DAC2_FRAMECNT 0xc3c
+#define ES1370_REG_ADC_FRAMEADR 0xd30
+#define ES1370_REG_ADC_FRAMECNT 0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
+
+static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
+
+#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
+#define DAC2_DIVTOSR(x) (1411200/((x)+2))
+
+#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
+#define CTRL_XCTL1 0x40000000 /* electret mic bias */
+#define CTRL_OPEN 0x20000000 /* no function, can be read and written */
+#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */
+#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */
+#define CTRL_ADC_EN 0x00000010 /* enable ADC */
+#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */
+#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably at address 0x200) */
+#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */
+
+#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */
+#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in progress */
+#define STAT_CBUSY 0x00000200 /* 1 = codec busy */
+#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */
+#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC 5
+#define STAT_MCCB 0x00000010 /* CCB int pending */
+#define STAT_UART 0x00000008 /* UART int pending */
+#define STAT_DAC1 0x00000004 /* DAC1 int pending */
+#define STAT_DAC2 0x00000002 /* DAC2 int pending */
+#define STAT_ADC 0x00000001 /* ADC int pending */
+
+#define USTAT_RXINT 0x80 /* UART rx int pending */
+#define USTAT_TXINT 0x04 /* UART tx int pending */
+#define USTAT_TXRDY 0x02 /* UART tx ready */
+#define USTAT_RXRDY 0x01 /* UART rx ready */
+
+#define UCTRL_RXINTEN 0x80 /* 1 = enable RX ints */
+#define UCTRL_TXINTEN 0x60 /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20 /* enable TX int */
+#define UCTRL_CNTRL 0x03 /* control field */
+#define UCTRL_CNTRL_SWR 0x03 /* software reset command */
+
+#define SCTRL_P2ENDINC 0x00380000 /* */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC 0x00070000 /* */
+#define SCTRL_SH_P2STINC 16
+#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */
+#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */
+#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */
+#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */
+#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */
+#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */
+#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */
+#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */
+#define SCTRL_R1FMT 0x00000030 /* format mask */
+#define SCTRL_SH_R1FMT 4
+#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */
+#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */
+#define SCTRL_P2FMT 0x0000000c /* format mask */
+#define SCTRL_SH_P2FMT 2
+#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */
+#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */
+#define SCTRL_P1FMT 0x00000003 /* format mask */
+#define SCTRL_SH_P1FMT 0
+
+/* End blatant GPL violation */
+
+#define NB_CHANNELS 3
+#define DAC1_CHANNEL 0
+#define DAC2_CHANNEL 1
+#define ADC_CHANNEL 2
+
+#define IO_READ_PROTO(n) \
+static uint32_t n (void *opaque, uint32_t addr)
+#define IO_WRITE_PROTO(n) \
+static void n (void *opaque, uint32_t addr, uint32_t val)
+
+static void es1370_dac1_callback (void *opaque, int free);
+static void es1370_dac2_callback (void *opaque, int free);
+static void es1370_adc_callback (void *opaque, int avail);
+
+#ifdef DEBUG_ES1370
+
+#define ldebug(...) AUD_log ("es1370", __VA_ARGS__)
+
+static void print_ctl (uint32_t val)
+{
+ char buf[1024];
+
+ buf[0] = '\0';
+#define a(n) if (val & CTRL_##n) strcat (buf, " "#n)
+ a (ADC_STOP);
+ a (XCTL1);
+ a (OPEN);
+ a (MSFMTSEL);
+ a (M_SBB);
+ a (DAC_SYNC);
+ a (CCB_INTRM);
+ a (M_CB);
+ a (XCTL0);
+ a (BREQ);
+ a (DAC1_EN);
+ a (DAC2_EN);
+ a (ADC_EN);
+ a (UART_EN);
+ a (JYSTK_EN);
+ a (CDC_EN);
+ a (SERR_DIS);
+#undef a
+ AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n",
+ (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV,
+ DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+ dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
+ buf);
+}
+
+static void print_sctl (uint32_t val)
+{
+ static const char *fmt_names[] = {"8M", "8S", "16M", "16S"};
+ char buf[1024];
+
+ buf[0] = '\0';
+
+#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n)
+#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n)
+ b (R1LOOPSEL);
+ b (P2LOOPSEL);
+ b (P1LOOPSEL);
+ a (P2PAUSE);
+ a (P1PAUSE);
+ a (R1INTEN);
+ a (P2INTEN);
+ a (P1INTEN);
+ a (P1SCTRLD);
+ a (P2DACSEN);
+ if (buf[0]) {
+ strcat (buf, "\n ");
+ }
+ else {
+ buf[0] = ' ';
+ buf[1] = '\0';
+ }
+#undef b
+#undef a
+ AUD_log ("es1370",
+ "%s"
+ "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n",
+ buf,
+ (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC,
+ (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC,
+ fmt_names [(val >> SCTRL_SH_R1FMT) & 3],
+ fmt_names [(val >> SCTRL_SH_P2FMT) & 3],
+ fmt_names [(val >> SCTRL_SH_P1FMT) & 3]
+ );
+}
+#else
+#define ldebug(...)
+#define print_ctl(...)
+#define print_sctl(...)
+#endif
+
+#ifdef VERBOSE_ES1370
+#define dolog(...) AUD_log ("es1370", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#ifndef SILENT_ES1370
+#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__)
+#else
+#define lwarn(...)
+#endif
+
+struct chan {
+ uint32_t shift;
+ uint32_t leftover;
+ uint32_t scount;
+ uint32_t frame_addr;
+ uint32_t frame_cnt;
+};
+
+typedef struct ES1370State {
+ PCIDevice dev;
+ QEMUSoundCard card;
+ struct chan chan[NB_CHANNELS];
+ SWVoiceOut *dac_voice[2];
+ SWVoiceIn *adc_voice;
+
+ uint32_t ctl;
+ uint32_t status;
+ uint32_t mempage;
+ uint32_t codec;
+ uint32_t sctl;
+} ES1370State;
+
+struct chan_bits {
+ uint32_t ctl_en;
+ uint32_t stat_int;
+ uint32_t sctl_pause;
+ uint32_t sctl_inten;
+ uint32_t sctl_fmt;
+ uint32_t sctl_sh_fmt;
+ uint32_t sctl_loopsel;
+ void (*calc_freq) (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq);
+};
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq);
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq,
+ uint32_t *new_freq);
+
+static const struct chan_bits es1370_chan_bits[] = {
+ {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN,
+ SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL,
+ es1370_dac1_calc_freq},
+
+ {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN,
+ SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL,
+ es1370_dac2_and_adc_calc_freq},
+
+ {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN,
+ SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL,
+ es1370_dac2_and_adc_calc_freq}
+};
+
+static void es1370_update_status (ES1370State *s, uint32_t new_status)
+{
+ uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC);
+
+ if (level) {
+ s->status = new_status | STAT_INTR;
+ }
+ else {
+ s->status = new_status & ~STAT_INTR;
+ }
+ qemu_set_irq (s->dev.irq[0], !!level);
+}
+
+static void es1370_reset (ES1370State *s)
+{
+ size_t i;
+
+ s->ctl = 1;
+ s->status = 0x60;
+ s->mempage = 0;
+ s->codec = 0;
+ s->sctl = 0;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ d->scount = 0;
+ d->leftover = 0;
+ if (i == ADC_CHANNEL) {
+ AUD_close_in (&s->card, s->adc_voice);
+ s->adc_voice = NULL;
+ }
+ else {
+ AUD_close_out (&s->card, s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+ }
+ qemu_irq_lower (s->dev.irq[0]);
+}
+
+static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl)
+{
+ uint32_t new_status = s->status;
+
+ if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) {
+ new_status &= ~STAT_DAC1;
+ }
+
+ if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) {
+ new_status &= ~STAT_DAC2;
+ }
+
+ if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) {
+ new_status &= ~STAT_ADC;
+ }
+
+ if (new_status != s->status) {
+ es1370_update_status (s, new_status);
+ }
+}
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq, uint32_t *new_freq)
+
+{
+ *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+ *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+}
+
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+ uint32_t *old_freq,
+ uint32_t *new_freq)
+
+{
+ uint32_t old_pclkdiv, new_pclkdiv;
+
+ new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+ old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+ *new_freq = DAC2_DIVTOSR (new_pclkdiv);
+ *old_freq = DAC2_DIVTOSR (old_pclkdiv);
+}
+
+static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
+{
+ size_t i;
+ uint32_t old_freq, new_freq, old_fmt, new_fmt;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ struct chan *d = &s->chan[i];
+ const struct chan_bits *b = &es1370_chan_bits[i];
+
+ new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+ old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+
+ b->calc_freq (s, ctl, &old_freq, &new_freq);
+
+ if ((old_fmt != new_fmt) || (old_freq != new_freq)) {
+ d->shift = (new_fmt & 1) + (new_fmt >> 1);
+ ldebug ("channel %d, freq = %d, nchannels %d, fmt %d, shift %d\n",
+ i,
+ new_freq,
+ 1 << (new_fmt & 1),
+ (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+ d->shift);
+ if (new_freq) {
+ struct audsettings as;
+
+ as.freq = new_freq;
+ as.nchannels = 1 << (new_fmt & 1);
+ as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+ as.endianness = 0;
+
+ if (i == ADC_CHANNEL) {
+ s->adc_voice =
+ AUD_open_in (
+ &s->card,
+ s->adc_voice,
+ "es1370.adc",
+ s,
+ es1370_adc_callback,
+ &as
+ );
+ }
+ else {
+ s->dac_voice[i] =
+ AUD_open_out (
+ &s->card,
+ s->dac_voice[i],
+ i ? "es1370.dac2" : "es1370.dac1",
+ s,
+ i ? es1370_dac2_callback : es1370_dac1_callback,
+ &as
+ );
+ }
+ }
+ }
+
+ if (((ctl ^ s->ctl) & b->ctl_en)
+ || ((sctl ^ s->sctl) & b->sctl_pause)) {
+ int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause);
+
+ if (i == ADC_CHANNEL) {
+ AUD_set_active_in (s->adc_voice, on);
+ }
+ else {
+ AUD_set_active_out (s->dac_voice[i], on);
+ }
+ }
+ }
+
+ s->ctl = ctl;
+ s->sctl = sctl;
+}
+
+static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
+{
+ addr &= 0xff;
+ if (addr >= 0x30 && addr <= 0x3f)
+ addr |= s->mempage << 8;
+ return addr;
+}
+
+IO_WRITE_PROTO (es1370_writeb)
+{
+ ES1370State *s = opaque;
+ uint32_t shift, mask;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ case ES1370_REG_CONTROL + 1:
+ case ES1370_REG_CONTROL + 2:
+ case ES1370_REG_CONTROL + 3:
+ shift = (addr - ES1370_REG_CONTROL) << 3;
+ mask = 0xff << shift;
+ val = (s->ctl & ~mask) | ((val & 0xff) << shift);
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+ case ES1370_REG_MEMPAGE:
+ s->mempage = val;
+ break;
+ case ES1370_REG_SERIAL_CONTROL:
+ case ES1370_REG_SERIAL_CONTROL + 1:
+ case ES1370_REG_SERIAL_CONTROL + 2:
+ case ES1370_REG_SERIAL_CONTROL + 3:
+ shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
+ mask = 0xff << shift;
+ val = (s->sctl & ~mask) | ((val & 0xff) << shift);
+ es1370_maybe_lower_irq (s, val);
+ es1370_update_voices (s, s->ctl, val);
+ print_sctl (val);
+ break;
+ default:
+ lwarn ("writeb %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_WRITE_PROTO (es1370_writew)
+{
+ ES1370State *s = opaque;
+ addr = es1370_fixup (s, addr);
+ uint32_t shift, mask;
+ struct chan *d = &s->chan[0];
+
+ switch (addr) {
+ case ES1370_REG_CODEC:
+ dolog ("ignored codec write address %#x, data %#x\n",
+ (val >> 8) & 0xff, val & 0xff);
+ s->codec = val;
+ break;
+
+ case ES1370_REG_CONTROL:
+ case ES1370_REG_CONTROL + 2:
+ shift = (addr != ES1370_REG_CONTROL) << 4;
+ mask = 0xffff << shift;
+ val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ d->scount = (d->scount & ~0xffff) | (val & 0xffff);
+ break;
+
+ default:
+ lwarn ("writew %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_WRITE_PROTO (es1370_writel)
+{
+ ES1370State *s = opaque;
+ struct chan *d = &s->chan[0];
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ es1370_update_voices (s, val, s->sctl);
+ print_ctl (val);
+ break;
+
+ case ES1370_REG_MEMPAGE:
+ s->mempage = val & 0xf;
+ break;
+
+ case ES1370_REG_SERIAL_CONTROL:
+ es1370_maybe_lower_irq (s, val);
+ es1370_update_voices (s, s->ctl, val);
+ print_sctl (val);
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ d->scount = (val & 0xffff) | (d->scount & ~0xffff);
+ ldebug ("chan %d CURR_SAMP_CT %d, SAMP_CT %d\n",
+ d - &s->chan[0], val >> 16, (val & 0xffff));
+ break;
+
+ case ES1370_REG_ADC_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC2_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC1_FRAMEADR:
+ d->frame_addr = val;
+ ldebug ("chan %d frame address %#x\n", d - &s->chan[0], val);
+ break;
+
+ case ES1370_REG_PHANTOM_FRAMECNT:
+ lwarn ("writing to phantom frame count %#x\n", val);
+ break;
+ case ES1370_REG_PHANTOM_FRAMEADR:
+ lwarn ("writing to phantom frame address %#x\n", val);
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ d->frame_cnt = val;
+ d->leftover = 0;
+ ldebug ("chan %d frame count %d, buffer size %d\n",
+ d - &s->chan[0], val >> 16, val & 0xffff);
+ break;
+
+ default:
+ lwarn ("writel %#x <- %#x\n", addr, val);
+ break;
+ }
+}
+
+IO_READ_PROTO (es1370_readb)
+{
+ ES1370State *s = opaque;
+ uint32_t val;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case 0x1b: /* Legacy */
+ lwarn ("Attempt to read from legacy register\n");
+ val = 5;
+ break;
+ case ES1370_REG_MEMPAGE:
+ val = s->mempage;
+ break;
+ case ES1370_REG_CONTROL + 0:
+ case ES1370_REG_CONTROL + 1:
+ case ES1370_REG_CONTROL + 2:
+ case ES1370_REG_CONTROL + 3:
+ val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
+ break;
+ case ES1370_REG_STATUS + 0:
+ case ES1370_REG_STATUS + 1:
+ case ES1370_REG_STATUS + 2:
+ case ES1370_REG_STATUS + 3:
+ val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
+ break;
+ default:
+ val = ~0;
+ lwarn ("readb %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+IO_READ_PROTO (es1370_readw)
+{
+ ES1370State *s = opaque;
+ struct chan *d = &s->chan[0];
+ uint32_t val;
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_ADC_SCOUNT + 2:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT + 2:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT + 2:
+ val = d->scount >> 16;
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ val = d->frame_cnt & 0xffff;
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT + 2:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT + 2:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT + 2:
+ val = d->frame_cnt >> 16;
+ break;
+
+ default:
+ val = ~0;
+ lwarn ("readw %#x -> %#x\n", addr, val);
+ break;
+ }
+
+ return val;
+}
+
+IO_READ_PROTO (es1370_readl)
+{
+ ES1370State *s = opaque;
+ uint32_t val;
+ struct chan *d = &s->chan[0];
+
+ addr = es1370_fixup (s, addr);
+
+ switch (addr) {
+ case ES1370_REG_CONTROL:
+ val = s->ctl;
+ break;
+ case ES1370_REG_STATUS:
+ val = s->status;
+ break;
+ case ES1370_REG_MEMPAGE:
+ val = s->mempage;
+ break;
+ case ES1370_REG_CODEC:
+ val = s->codec;
+ break;
+ case ES1370_REG_SERIAL_CONTROL:
+ val = s->sctl;
+ break;
+
+ case ES1370_REG_ADC_SCOUNT:
+ d++;
+ case ES1370_REG_DAC2_SCOUNT:
+ d++;
+ case ES1370_REG_DAC1_SCOUNT:
+ val = d->scount;
+#ifdef DEBUG_ES1370
+ {
+ uint32_t curr_count = d->scount >> 16;
+ uint32_t count = d->scount & 0xffff;
+
+ curr_count <<= d->shift;
+ count <<= d->shift;
+ dolog ("read scount curr %d, total %d\n", curr_count, count);
+ }
+#endif
+ break;
+
+ case ES1370_REG_ADC_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC2_FRAMECNT:
+ d++;
+ case ES1370_REG_DAC1_FRAMECNT:
+ val = d->frame_cnt;
+#ifdef DEBUG_ES1370
+ {
+ uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2;
+ uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2;
+ if (curr > size)
+ dolog ("read framecnt curr %d, size %d %d\n", curr, size,
+ curr > size);
+ }
+#endif
+ break;
+
+ case ES1370_REG_ADC_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC2_FRAMEADR:
+ d++;
+ case ES1370_REG_DAC1_FRAMEADR:
+ val = d->frame_addr;
+ break;
+
+ case ES1370_REG_PHANTOM_FRAMECNT:
+ val = ~0U;
+ lwarn ("reading from phantom frame count\n");
+ break;
+ case ES1370_REG_PHANTOM_FRAMEADR:
+ val = ~0U;
+ lwarn ("reading from phantom frame address\n");
+ break;
+
+ default:
+ val = ~0U;
+ lwarn ("readl %#x -> %#x\n", addr, val);
+ break;
+ }
+ return val;
+}
+
+
+static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
+ int max, int *irq)
+{
+ uint8_t tmpbuf[4096];
+ uint32_t addr = d->frame_addr;
+ int sc = d->scount & 0xffff;
+ int csc = d->scount >> 16;
+ int csc_bytes = (csc + 1) << d->shift;
+ int cnt = d->frame_cnt >> 16;
+ int size = d->frame_cnt & 0xffff;
+ int left = ((size - cnt + 1) << 2) + d->leftover;
+ int transfered = 0;
+ int temp = audio_MIN (max, audio_MIN (left, csc_bytes));
+ int index = d - &s->chan[0];
+
+ addr += (cnt << 2) + d->leftover;
+
+ if (index == ADC_CHANNEL) {
+ while (temp) {
+ int acquired, to_copy;
+
+ to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
+ acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
+ if (!acquired)
+ break;
+
+ cpu_physical_memory_write (addr, tmpbuf, acquired);
+
+ temp -= acquired;
+ addr += acquired;
+ transfered += acquired;
+ }
+ }
+ else {
+ SWVoiceOut *voice = s->dac_voice[index];
+
+ while (temp) {
+ int copied, to_copy;
+
+ to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
+ cpu_physical_memory_read (addr, tmpbuf, to_copy);
+ copied = AUD_write (voice, tmpbuf, to_copy);
+ if (!copied)
+ break;
+ temp -= copied;
+ addr += copied;
+ transfered += copied;
+ }
+ }
+
+ if (csc_bytes == transfered) {
+ *irq = 1;
+ d->scount = sc | (sc << 16);
+ ldebug ("sc = %d, rate = %f\n",
+ (sc + 1) << d->shift,
+ (sc + 1) / (double) 44100);
+ }
+ else {
+ *irq = 0;
+ d->scount = sc | (((csc_bytes - transfered - 1) >> d->shift) << 16);
+ }
+
+ cnt += (transfered + d->leftover) >> 2;
+
+ if (s->sctl & loop_sel) {
+ /* Bah, how stupid is that having a 0 represent true value?
+ i just spent few hours on this shit */
+ AUD_log ("es1370: warning", "non looping mode\n");
+ }
+ else {
+ d->frame_cnt = size;
+
+ if ((uint32_t) cnt <= d->frame_cnt)
+ d->frame_cnt |= cnt << 16;
+ }
+
+ d->leftover = (transfered + d->leftover) & 3;
+}
+
+static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail)
+{
+ uint32_t new_status = s->status;
+ int max_bytes, irq;
+ struct chan *d = &s->chan[chan];
+ const struct chan_bits *b = &es1370_chan_bits[chan];
+
+ if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) {
+ return;
+ }
+
+ max_bytes = free_or_avail;
+ max_bytes &= ~((1 << d->shift) - 1);
+ if (!max_bytes) {
+ return;
+ }
+
+ es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq);
+
+ if (irq) {
+ if (s->sctl & b->sctl_inten) {
+ new_status |= b->stat_int;
+ }
+ }
+
+ if (new_status != s->status) {
+ es1370_update_status (s, new_status);
+ }
+}
+
+static void es1370_dac1_callback (void *opaque, int free)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, DAC1_CHANNEL, free);
+}
+
+static void es1370_dac2_callback (void *opaque, int free)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, DAC2_CHANNEL, free);
+}
+
+static void es1370_adc_callback (void *opaque, int avail)
+{
+ ES1370State *s = opaque;
+
+ es1370_run_channel (s, ADC_CHANNEL, avail);
+}
+
+static void es1370_map (PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ ES1370State *s = DO_UPCAST (ES1370State, dev, pci_dev);
+
+ (void) region_num;
+ (void) size;
+ (void) type;
+
+ register_ioport_write (addr, 0x40 * 4, 1, es1370_writeb, s);
+ register_ioport_write (addr, 0x40 * 2, 2, es1370_writew, s);
+ register_ioport_write (addr, 0x40, 4, es1370_writel, s);
+
+ register_ioport_read (addr, 0x40 * 4, 1, es1370_readb, s);
+ register_ioport_read (addr, 0x40 * 2, 2, es1370_readw, s);
+ register_ioport_read (addr, 0x40, 4, es1370_readl, s);
+}
+
+static const VMStateDescription vmstate_es1370_channel = {
+ .name = "es1370_channel",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(shift, struct chan),
+ VMSTATE_UINT32(leftover, struct chan),
+ VMSTATE_UINT32(scount, struct chan),
+ VMSTATE_UINT32(frame_addr, struct chan),
+ VMSTATE_UINT32(frame_cnt, struct chan),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int es1370_post_load (void *opaque, int version_id)
+{
+ uint32_t ctl, sctl;
+ ES1370State *s = opaque;
+ size_t i;
+
+ for (i = 0; i < NB_CHANNELS; ++i) {
+ if (i == ADC_CHANNEL) {
+ if (s->adc_voice) {
+ AUD_close_in (&s->card, s->adc_voice);
+ s->adc_voice = NULL;
+ }
+ }
+ else {
+ if (s->dac_voice[i]) {
+ AUD_close_out (&s->card, s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+ }
+ }
+
+ ctl = s->ctl;
+ sctl = s->sctl;
+ s->ctl = 0;
+ s->sctl = 0;
+ es1370_update_voices (s, ctl, sctl);
+ return 0;
+}
+
+static const VMStateDescription vmstate_es1370 = {
+ .name = "es1370",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .post_load = es1370_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, ES1370State),
+ VMSTATE_STRUCT_ARRAY(chan, ES1370State, NB_CHANNELS, 2,
+ vmstate_es1370_channel, struct chan),
+ VMSTATE_UINT32(ctl, ES1370State),
+ VMSTATE_UINT32(status, ES1370State),
+ VMSTATE_UINT32(mempage, ES1370State),
+ VMSTATE_UINT32(codec, ES1370State),
+ VMSTATE_UINT32(sctl, ES1370State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void es1370_on_reset (void *opaque)
+{
+ ES1370State *s = opaque;
+ es1370_reset (s);
+}
+
+static int es1370_initfn (PCIDevice *dev)
+{
+ ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
+ uint8_t *c = s->dev.config;
+
+ pci_config_set_vendor_id (c, PCI_VENDOR_ID_ENSONIQ);
+ pci_config_set_device_id (c, PCI_DEVICE_ID_ENSONIQ_ES1370);
+ c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8;
+ pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO);
+
+#if 1
+ c[PCI_SUBSYSTEM_VENDOR_ID] = 0x42;
+ c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x49;
+ c[PCI_SUBSYSTEM_ID] = 0x4c;
+ c[PCI_SUBSYSTEM_ID + 1] = 0x4c;
+#else
+ c[PCI_SUBSYSTEM_VENDOR_ID] = 0x74;
+ c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x12;
+ c[PCI_SUBSYSTEM_ID] = 0x71;
+ c[PCI_SUBSYSTEM_ID + 1] = 0x13;
+ c[PCI_CAPABILITY_LIST] = 0xdc;
+ c[PCI_INTERRUPT_LINE] = 10;
+ c[0xdc] = 0x00;
+#endif
+
+ /* TODO: RST# value should be 0. */
+ c[PCI_INTERRUPT_PIN] = 1;
+ c[PCI_MIN_GNT] = 0x0c;
+ c[PCI_MAX_LAT] = 0x80;
+
+ pci_register_bar (&s->dev, 0, 256, PCI_BASE_ADDRESS_SPACE_IO, es1370_map);
+ qemu_register_reset (es1370_on_reset, s);
+
+ AUD_register_card ("es1370", &s->card);
+ es1370_reset (s);
+ return 0;
+}
+
+int es1370_init (PCIBus *bus)
+{
+ pci_create_simple (bus, -1, "ES1370");
+ return 0;
+}
+
+static PCIDeviceInfo es1370_info = {
+ .qdev.name = "ES1370",
+ .qdev.desc = "ENSONIQ AudioPCI ES1370",
+ .qdev.size = sizeof (ES1370State),
+ .qdev.vmsd = &vmstate_es1370,
+ .init = es1370_initfn,
+};
+
+static void es1370_register (void)
+{
+ pci_qdev_register (&es1370_info);
+}
+device_init (es1370_register);
+
diff --git a/hw/escc.c b/hw/escc.c
new file mode 100644
index 0000000..f6fd919
--- /dev/null
+++ b/hw/escc.c
@@ -0,0 +1,961 @@
+/*
+ * QEMU ESCC (Z8030/Z8530/Z85C30/SCC/ESCC) serial port emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "escc.h"
+#include "qemu-char.h"
+#include "console.h"
+
+/* debug serial */
+//#define DEBUG_SERIAL
+
+/* debug keyboard */
+//#define DEBUG_KBD
+
+/* debug mouse */
+//#define DEBUG_MOUSE
+
+/*
+ * Chipset docs:
+ * "Z80C30/Z85C30/Z80230/Z85230/Z85233 SCC/ESCC User Manual",
+ * http://www.zilog.com/docs/serial/scc_escc_um.pdf
+ *
+ * On Sparc32 this is the serial port, mouse and keyboard part of chip STP2001
+ * (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The serial ports implement full AMD AM8530 or Zilog Z8530 chips,
+ * mouse and keyboard ports don't implement all functions and they are
+ * only asynchronous. There is no DMA.
+ *
+ * Z85C30 is also used on PowerMacs. There are some small differences
+ * between Sparc version (sunzilog) and PowerMac (pmac):
+ * Offset between control and data registers
+ * There is some kind of lockup bug, but we can ignore it
+ * CTS is inverted
+ * DMA on pmac using DBDMA chip
+ * pmac can do IRDA and faster rates, sunzilog can only do 38400
+ * pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
+ */
+
+/*
+ * Modifications:
+ * 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented
+ * serial mouse queue.
+ * Implemented serial mouse protocol.
+ *
+ * 2010-May-23 Artyom Tarasenko: Reworked IUS logic
+ */
+
+#ifdef DEBUG_SERIAL
+#define SER_DPRINTF(fmt, ...) \
+ do { printf("SER: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define SER_DPRINTF(fmt, ...)
+#endif
+#ifdef DEBUG_KBD
+#define KBD_DPRINTF(fmt, ...) \
+ do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define KBD_DPRINTF(fmt, ...)
+#endif
+#ifdef DEBUG_MOUSE
+#define MS_DPRINTF(fmt, ...) \
+ do { printf("MSC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define MS_DPRINTF(fmt, ...)
+#endif
+
+typedef enum {
+ chn_a, chn_b,
+} ChnID;
+
+#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a')
+
+typedef enum {
+ ser, kbd, mouse,
+} ChnType;
+
+#define SERIO_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[SERIO_QUEUE_SIZE];
+ int rptr, wptr, count;
+} SERIOQueue;
+
+#define SERIAL_REGS 16
+typedef struct ChannelState {
+ qemu_irq irq;
+ uint32_t reg;
+ uint32_t rxint, txint, rxint_under_svc, txint_under_svc;
+ ChnID chn; // this channel, A (base+4) or B (base+0)
+ ChnType type;
+ struct ChannelState *otherchn;
+ uint8_t rx, tx, wregs[SERIAL_REGS], rregs[SERIAL_REGS];
+ SERIOQueue queue;
+ CharDriverState *chr;
+ int e0_mode, led_mode, caps_lock_mode, num_lock_mode;
+ int disabled;
+ int clock;
+ uint32_t vmstate_dummy;
+} ChannelState;
+
+struct SerialState {
+ SysBusDevice busdev;
+ struct ChannelState chn[2];
+ uint32_t it_shift;
+ int mmio_index;
+ uint32_t disabled;
+ uint32_t frequency;
+};
+
+#define SERIAL_CTRL 0
+#define SERIAL_DATA 1
+
+#define W_CMD 0
+#define CMD_PTR_MASK 0x07
+#define CMD_CMD_MASK 0x38
+#define CMD_HI 0x08
+#define CMD_CLR_TXINT 0x28
+#define CMD_CLR_IUS 0x38
+#define W_INTR 1
+#define INTR_INTALL 0x01
+#define INTR_TXINT 0x02
+#define INTR_RXMODEMSK 0x18
+#define INTR_RXINT1ST 0x08
+#define INTR_RXINTALL 0x10
+#define W_IVEC 2
+#define W_RXCTRL 3
+#define RXCTRL_RXEN 0x01
+#define W_TXCTRL1 4
+#define TXCTRL1_PAREN 0x01
+#define TXCTRL1_PAREV 0x02
+#define TXCTRL1_1STOP 0x04
+#define TXCTRL1_1HSTOP 0x08
+#define TXCTRL1_2STOP 0x0c
+#define TXCTRL1_STPMSK 0x0c
+#define TXCTRL1_CLK1X 0x00
+#define TXCTRL1_CLK16X 0x40
+#define TXCTRL1_CLK32X 0x80
+#define TXCTRL1_CLK64X 0xc0
+#define TXCTRL1_CLKMSK 0xc0
+#define W_TXCTRL2 5
+#define TXCTRL2_TXEN 0x08
+#define TXCTRL2_BITMSK 0x60
+#define TXCTRL2_5BITS 0x00
+#define TXCTRL2_7BITS 0x20
+#define TXCTRL2_6BITS 0x40
+#define TXCTRL2_8BITS 0x60
+#define W_SYNC1 6
+#define W_SYNC2 7
+#define W_TXBUF 8
+#define W_MINTR 9
+#define MINTR_STATUSHI 0x10
+#define MINTR_RST_MASK 0xc0
+#define MINTR_RST_B 0x40
+#define MINTR_RST_A 0x80
+#define MINTR_RST_ALL 0xc0
+#define W_MISC1 10
+#define W_CLOCK 11
+#define CLOCK_TRXC 0x08
+#define W_BRGLO 12
+#define W_BRGHI 13
+#define W_MISC2 14
+#define MISC2_PLLDIS 0x30
+#define W_EXTINT 15
+#define EXTINT_DCD 0x08
+#define EXTINT_SYNCINT 0x10
+#define EXTINT_CTSINT 0x20
+#define EXTINT_TXUNDRN 0x40
+#define EXTINT_BRKINT 0x80
+
+#define R_STATUS 0
+#define STATUS_RXAV 0x01
+#define STATUS_ZERO 0x02
+#define STATUS_TXEMPTY 0x04
+#define STATUS_DCD 0x08
+#define STATUS_SYNC 0x10
+#define STATUS_CTS 0x20
+#define STATUS_TXUNDRN 0x40
+#define STATUS_BRK 0x80
+#define R_SPEC 1
+#define SPEC_ALLSENT 0x01
+#define SPEC_BITS8 0x06
+#define R_IVEC 2
+#define IVEC_TXINTB 0x00
+#define IVEC_LONOINT 0x06
+#define IVEC_LORXINTA 0x0c
+#define IVEC_LORXINTB 0x04
+#define IVEC_LOTXINTA 0x08
+#define IVEC_HINOINT 0x60
+#define IVEC_HIRXINTA 0x30
+#define IVEC_HIRXINTB 0x20
+#define IVEC_HITXINTA 0x10
+#define R_INTR 3
+#define INTR_EXTINTB 0x01
+#define INTR_TXINTB 0x02
+#define INTR_RXINTB 0x04
+#define INTR_EXTINTA 0x08
+#define INTR_TXINTA 0x10
+#define INTR_RXINTA 0x20
+#define R_IPEN 4
+#define R_TXCTRL1 5
+#define R_TXCTRL2 6
+#define R_BC 7
+#define R_RXBUF 8
+#define R_RXCTRL 9
+#define R_MISC 10
+#define R_MISC1 11
+#define R_BRGLO 12
+#define R_BRGHI 13
+#define R_MISC1I 14
+#define R_EXTINT 15
+
+static void handle_kbd_command(ChannelState *s, int val);
+static int serial_can_receive(void *opaque);
+static void serial_receive_byte(ChannelState *s, int ch);
+
+static void clear_queue(void *opaque)
+{
+ ChannelState *s = opaque;
+ SERIOQueue *q = &s->queue;
+ q->rptr = q->wptr = q->count = 0;
+}
+
+static void put_queue(void *opaque, int b)
+{
+ ChannelState *s = opaque;
+ SERIOQueue *q = &s->queue;
+
+ SER_DPRINTF("channel %c put: 0x%02x\n", CHN_C(s), b);
+ if (q->count >= SERIO_QUEUE_SIZE)
+ return;
+ q->data[q->wptr] = b;
+ if (++q->wptr == SERIO_QUEUE_SIZE)
+ q->wptr = 0;
+ q->count++;
+ serial_receive_byte(s, 0);
+}
+
+static uint32_t get_queue(void *opaque)
+{
+ ChannelState *s = opaque;
+ SERIOQueue *q = &s->queue;
+ int val;
+
+ if (q->count == 0) {
+ return 0;
+ } else {
+ val = q->data[q->rptr];
+ if (++q->rptr == SERIO_QUEUE_SIZE)
+ q->rptr = 0;
+ q->count--;
+ }
+ SER_DPRINTF("channel %c get 0x%02x\n", CHN_C(s), val);
+ if (q->count > 0)
+ serial_receive_byte(s, 0);
+ return val;
+}
+
+static int escc_update_irq_chn(ChannelState *s)
+{
+ if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
+ // tx ints enabled, pending
+ ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
+ ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
+ s->rxint == 1) || // rx ints enabled, pending
+ ((s->wregs[W_EXTINT] & EXTINT_BRKINT) &&
+ (s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p
+ return 1;
+ }
+ return 0;
+}
+
+static void escc_update_irq(ChannelState *s)
+{
+ int irq;
+
+ irq = escc_update_irq_chn(s);
+ irq |= escc_update_irq_chn(s->otherchn);
+
+ SER_DPRINTF("IRQ = %d\n", irq);
+ qemu_set_irq(s->irq, irq);
+}
+
+static void escc_reset_chn(ChannelState *s)
+{
+ int i;
+
+ s->reg = 0;
+ for (i = 0; i < SERIAL_REGS; i++) {
+ s->rregs[i] = 0;
+ s->wregs[i] = 0;
+ }
+ s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity
+ s->wregs[W_MINTR] = MINTR_RST_ALL;
+ s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC
+ s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled
+ s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT |
+ EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts
+ if (s->disabled)
+ s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC |
+ STATUS_CTS | STATUS_TXUNDRN;
+ else
+ s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN;
+ s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT;
+
+ s->rx = s->tx = 0;
+ s->rxint = s->txint = 0;
+ s->rxint_under_svc = s->txint_under_svc = 0;
+ s->e0_mode = s->led_mode = s->caps_lock_mode = s->num_lock_mode = 0;
+ clear_queue(s);
+}
+
+static void escc_reset(DeviceState *d)
+{
+ SerialState *s = container_of(d, SerialState, busdev.qdev);
+
+ escc_reset_chn(&s->chn[0]);
+ escc_reset_chn(&s->chn[1]);
+}
+
+static inline void set_rxint(ChannelState *s)
+{
+ s->rxint = 1;
+ /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
+ than chn_a rx/tx/special_condition service*/
+ s->rxint_under_svc = 1;
+ if (s->chn == chn_a) {
+ s->rregs[R_INTR] |= INTR_RXINTA;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
+ } else {
+ s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HIRXINTB;
+ else
+ s->rregs[R_IVEC] = IVEC_LORXINTB;
+ }
+ escc_update_irq(s);
+}
+
+static inline void set_txint(ChannelState *s)
+{
+ s->txint = 1;
+ if (!s->rxint_under_svc) {
+ s->txint_under_svc = 1;
+ if (s->chn == chn_a) {
+ if (s->wregs[W_INTR] & INTR_TXINT) {
+ s->rregs[R_INTR] |= INTR_TXINTA;
+ }
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
+ } else {
+ s->rregs[R_IVEC] = IVEC_TXINTB;
+ if (s->wregs[W_INTR] & INTR_TXINT) {
+ s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
+ }
+ }
+ escc_update_irq(s);
+ }
+}
+
+static inline void clr_rxint(ChannelState *s)
+{
+ s->rxint = 0;
+ s->rxint_under_svc = 0;
+ if (s->chn == chn_a) {
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+ s->rregs[R_INTR] &= ~INTR_RXINTA;
+ } else {
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->rregs[R_IVEC] = IVEC_LONOINT;
+ s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
+ }
+ if (s->txint)
+ set_txint(s);
+ escc_update_irq(s);
+}
+
+static inline void clr_txint(ChannelState *s)
+{
+ s->txint = 0;
+ s->txint_under_svc = 0;
+ if (s->chn == chn_a) {
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+ s->rregs[R_INTR] &= ~INTR_TXINTA;
+ } else {
+ s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
+ if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+ s->rregs[R_IVEC] = IVEC_HINOINT;
+ else
+ s->rregs[R_IVEC] = IVEC_LONOINT;
+ s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
+ }
+ if (s->rxint)
+ set_rxint(s);
+ escc_update_irq(s);
+}
+
+static void escc_update_parameters(ChannelState *s)
+{
+ int speed, parity, data_bits, stop_bits;
+ QEMUSerialSetParams ssp;
+
+ if (!s->chr || s->type != ser)
+ return;
+
+ if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) {
+ if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV)
+ parity = 'E';
+ else
+ parity = 'O';
+ } else {
+ parity = 'N';
+ }
+ if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP)
+ stop_bits = 2;
+ else
+ stop_bits = 1;
+ switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) {
+ case TXCTRL2_5BITS:
+ data_bits = 5;
+ break;
+ case TXCTRL2_7BITS:
+ data_bits = 7;
+ break;
+ case TXCTRL2_6BITS:
+ data_bits = 6;
+ break;
+ default:
+ case TXCTRL2_8BITS:
+ data_bits = 8;
+ break;
+ }
+ speed = s->clock / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2);
+ switch (s->wregs[W_TXCTRL1] & TXCTRL1_CLKMSK) {
+ case TXCTRL1_CLK1X:
+ break;
+ case TXCTRL1_CLK16X:
+ speed /= 16;
+ break;
+ case TXCTRL1_CLK32X:
+ speed /= 32;
+ break;
+ default:
+ case TXCTRL1_CLK64X:
+ speed /= 64;
+ break;
+ }
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ SER_DPRINTF("channel %c: speed=%d parity=%c data=%d stop=%d\n", CHN_C(s),
+ speed, parity, data_bits, stop_bits);
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static void escc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ SerialState *serial = opaque;
+ ChannelState *s;
+ uint32_t saddr;
+ int newreg, channel;
+
+ val &= 0xff;
+ saddr = (addr >> serial->it_shift) & 1;
+ channel = (addr >> (serial->it_shift + 1)) & 1;
+ s = &serial->chn[channel];
+ switch (saddr) {
+ case SERIAL_CTRL:
+ SER_DPRINTF("Write channel %c, reg[%d] = %2.2x\n", CHN_C(s), s->reg,
+ val & 0xff);
+ newreg = 0;
+ switch (s->reg) {
+ case W_CMD:
+ newreg = val & CMD_PTR_MASK;
+ val &= CMD_CMD_MASK;
+ switch (val) {
+ case CMD_HI:
+ newreg |= CMD_HI;
+ break;
+ case CMD_CLR_TXINT:
+ clr_txint(s);
+ break;
+ case CMD_CLR_IUS:
+ if (s->rxint_under_svc) {
+ s->rxint_under_svc = 0;
+ if (s->txint) {
+ set_txint(s);
+ }
+ } else if (s->txint_under_svc) {
+ s->txint_under_svc = 0;
+ }
+ escc_update_irq(s);
+ break;
+ default:
+ break;
+ }
+ break;
+ case W_INTR ... W_RXCTRL:
+ case W_SYNC1 ... W_TXBUF:
+ case W_MISC1 ... W_CLOCK:
+ case W_MISC2 ... W_EXTINT:
+ s->wregs[s->reg] = val;
+ break;
+ case W_TXCTRL1:
+ case W_TXCTRL2:
+ s->wregs[s->reg] = val;
+ escc_update_parameters(s);
+ break;
+ case W_BRGLO:
+ case W_BRGHI:
+ s->wregs[s->reg] = val;
+ s->rregs[s->reg] = val;
+ escc_update_parameters(s);
+ break;
+ case W_MINTR:
+ switch (val & MINTR_RST_MASK) {
+ case 0:
+ default:
+ break;
+ case MINTR_RST_B:
+ escc_reset_chn(&serial->chn[0]);
+ return;
+ case MINTR_RST_A:
+ escc_reset_chn(&serial->chn[1]);
+ return;
+ case MINTR_RST_ALL:
+ escc_reset(&serial->busdev.qdev);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ if (s->reg == 0)
+ s->reg = newreg;
+ else
+ s->reg = 0;
+ break;
+ case SERIAL_DATA:
+ SER_DPRINTF("Write channel %c, ch %d\n", CHN_C(s), val);
+ s->tx = val;
+ if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled
+ if (s->chr)
+ qemu_chr_write(s->chr, &s->tx, 1);
+ else if (s->type == kbd && !s->disabled) {
+ handle_kbd_command(s, val);
+ }
+ }
+ s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty
+ s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent
+ set_txint(s);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t escc_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *serial = opaque;
+ ChannelState *s;
+ uint32_t saddr;
+ uint32_t ret;
+ int channel;
+
+ saddr = (addr >> serial->it_shift) & 1;
+ channel = (addr >> (serial->it_shift + 1)) & 1;
+ s = &serial->chn[channel];
+ switch (saddr) {
+ case SERIAL_CTRL:
+ SER_DPRINTF("Read channel %c, reg[%d] = %2.2x\n", CHN_C(s), s->reg,
+ s->rregs[s->reg]);
+ ret = s->rregs[s->reg];
+ s->reg = 0;
+ return ret;
+ case SERIAL_DATA:
+ s->rregs[R_STATUS] &= ~STATUS_RXAV;
+ clr_rxint(s);
+ if (s->type == kbd || s->type == mouse)
+ ret = get_queue(s);
+ else
+ ret = s->rx;
+ SER_DPRINTF("Read channel %c, ch %d\n", CHN_C(s), ret);
+ if (s->chr)
+ qemu_chr_accept_input(s->chr);
+ return ret;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int serial_can_receive(void *opaque)
+{
+ ChannelState *s = opaque;
+ int ret;
+
+ if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled
+ || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV))
+ // char already available
+ ret = 0;
+ else
+ ret = 1;
+ return ret;
+}
+
+static void serial_receive_byte(ChannelState *s, int ch)
+{
+ SER_DPRINTF("channel %c put ch %d\n", CHN_C(s), ch);
+ s->rregs[R_STATUS] |= STATUS_RXAV;
+ s->rx = ch;
+ set_rxint(s);
+}
+
+static void serial_receive_break(ChannelState *s)
+{
+ s->rregs[R_STATUS] |= STATUS_BRK;
+ escc_update_irq(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ ChannelState *s = opaque;
+ serial_receive_byte(s, buf[0]);
+}
+
+static void serial_event(void *opaque, int event)
+{
+ ChannelState *s = opaque;
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+}
+
+static CPUReadMemoryFunc * const escc_mem_read[3] = {
+ escc_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const escc_mem_write[3] = {
+ escc_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static const VMStateDescription vmstate_escc_chn = {
+ .name ="escc_chn",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(vmstate_dummy, ChannelState),
+ VMSTATE_UINT32(reg, ChannelState),
+ VMSTATE_UINT32(rxint, ChannelState),
+ VMSTATE_UINT32(txint, ChannelState),
+ VMSTATE_UINT32(rxint_under_svc, ChannelState),
+ VMSTATE_UINT32(txint_under_svc, ChannelState),
+ VMSTATE_UINT8(rx, ChannelState),
+ VMSTATE_UINT8(tx, ChannelState),
+ VMSTATE_BUFFER(wregs, ChannelState),
+ VMSTATE_BUFFER(rregs, ChannelState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_escc = {
+ .name ="escc",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT_ARRAY(chn, SerialState, 2, 2, vmstate_escc_chn,
+ ChannelState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+int escc_init(target_phys_addr_t base, qemu_irq irqA, qemu_irq irqB,
+ CharDriverState *chrA, CharDriverState *chrB,
+ int clock, int it_shift)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ SerialState *d;
+
+ dev = qdev_create(NULL, "escc");
+ qdev_prop_set_uint32(dev, "disabled", 0);
+ qdev_prop_set_uint32(dev, "frequency", clock);
+ qdev_prop_set_uint32(dev, "it_shift", it_shift);
+ qdev_prop_set_chr(dev, "chrB", chrB);
+ qdev_prop_set_chr(dev, "chrA", chrA);
+ qdev_prop_set_uint32(dev, "chnBtype", ser);
+ qdev_prop_set_uint32(dev, "chnAtype", ser);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irqB);
+ sysbus_connect_irq(s, 1, irqA);
+ if (base) {
+ sysbus_mmio_map(s, 0, base);
+ }
+
+ d = FROM_SYSBUS(SerialState, s);
+ return d->mmio_index;
+}
+
+static const uint8_t keycodes[128] = {
+ 127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12,
+ 14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112,
+ 113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0,
+ 90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66,
+ 0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67,
+};
+
+static const uint8_t e0_keycodes[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 76, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 109, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 68, 69, 70, 0, 91, 0, 93, 0, 112,
+ 113, 114, 94, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 3, 25, 26, 49, 52, 72, 73, 97, 99, 111, 118, 120, 122, 67, 0,
+};
+
+static void sunkbd_event(void *opaque, int ch)
+{
+ ChannelState *s = opaque;
+ int release = ch & 0x80;
+
+ KBD_DPRINTF("Untranslated keycode %2.2x (%s)\n", ch, release? "release" :
+ "press");
+ switch (ch) {
+ case 58: // Caps lock press
+ s->caps_lock_mode ^= 1;
+ if (s->caps_lock_mode == 2)
+ return; // Drop second press
+ break;
+ case 69: // Num lock press
+ s->num_lock_mode ^= 1;
+ if (s->num_lock_mode == 2)
+ return; // Drop second press
+ break;
+ case 186: // Caps lock release
+ s->caps_lock_mode ^= 2;
+ if (s->caps_lock_mode == 3)
+ return; // Drop first release
+ break;
+ case 197: // Num lock release
+ s->num_lock_mode ^= 2;
+ if (s->num_lock_mode == 3)
+ return; // Drop first release
+ break;
+ case 0xe0:
+ s->e0_mode = 1;
+ return;
+ default:
+ break;
+ }
+ if (s->e0_mode) {
+ s->e0_mode = 0;
+ ch = e0_keycodes[ch & 0x7f];
+ } else {
+ ch = keycodes[ch & 0x7f];
+ }
+ KBD_DPRINTF("Translated keycode %2.2x\n", ch);
+ put_queue(s, ch | release);
+}
+
+static void handle_kbd_command(ChannelState *s, int val)
+{
+ KBD_DPRINTF("Command %d\n", val);
+ if (s->led_mode) { // Ignore led byte
+ s->led_mode = 0;
+ return;
+ }
+ switch (val) {
+ case 1: // Reset, return type code
+ clear_queue(s);
+ put_queue(s, 0xff);
+ put_queue(s, 4); // Type 4
+ put_queue(s, 0x7f);
+ break;
+ case 0xe: // Set leds
+ s->led_mode = 1;
+ break;
+ case 7: // Query layout
+ case 0xf:
+ clear_queue(s);
+ put_queue(s, 0xfe);
+ put_queue(s, 0); // XXX, layout?
+ break;
+ default:
+ break;
+ }
+}
+
+static void sunmouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ ChannelState *s = opaque;
+ int ch;
+
+ MS_DPRINTF("dx=%d dy=%d buttons=%01x\n", dx, dy, buttons_state);
+
+ ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */
+
+ if (buttons_state & MOUSE_EVENT_LBUTTON)
+ ch ^= 0x4;
+ if (buttons_state & MOUSE_EVENT_MBUTTON)
+ ch ^= 0x2;
+ if (buttons_state & MOUSE_EVENT_RBUTTON)
+ ch ^= 0x1;
+
+ put_queue(s, ch);
+
+ ch = dx;
+
+ if (ch > 127)
+ ch = 127;
+ else if (ch < -127)
+ ch = -127;
+
+ put_queue(s, ch & 0xff);
+
+ ch = -dy;
+
+ if (ch > 127)
+ ch = 127;
+ else if (ch < -127)
+ ch = -127;
+
+ put_queue(s, ch & 0xff);
+
+ // MSC protocol specify two extra motion bytes
+
+ put_queue(s, 0);
+ put_queue(s, 0);
+}
+
+void slavio_serial_ms_kbd_init(target_phys_addr_t base, qemu_irq irq,
+ int disabled, int clock, int it_shift)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "escc");
+ qdev_prop_set_uint32(dev, "disabled", disabled);
+ qdev_prop_set_uint32(dev, "frequency", clock);
+ qdev_prop_set_uint32(dev, "it_shift", it_shift);
+ qdev_prop_set_chr(dev, "chrB", NULL);
+ qdev_prop_set_chr(dev, "chrA", NULL);
+ qdev_prop_set_uint32(dev, "chnBtype", mouse);
+ qdev_prop_set_uint32(dev, "chnAtype", kbd);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irq);
+ sysbus_connect_irq(s, 1, irq);
+ sysbus_mmio_map(s, 0, base);
+}
+
+static int escc_init1(SysBusDevice *dev)
+{
+ SerialState *s = FROM_SYSBUS(SerialState, dev);
+ int io;
+ unsigned int i;
+
+ s->chn[0].disabled = s->disabled;
+ s->chn[1].disabled = s->disabled;
+ for (i = 0; i < 2; i++) {
+ sysbus_init_irq(dev, &s->chn[i].irq);
+ s->chn[i].chn = 1 - i;
+ s->chn[i].clock = s->frequency / 2;
+ if (s->chn[i].chr) {
+ qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
+ serial_receive1, serial_event, &s->chn[i]);
+ }
+ }
+ s->chn[0].otherchn = &s->chn[1];
+ s->chn[1].otherchn = &s->chn[0];
+
+ io = cpu_register_io_memory(escc_mem_read, escc_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, ESCC_SIZE << s->it_shift, io);
+ s->mmio_index = io;
+
+ if (s->chn[0].type == mouse) {
+ qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
+ "QEMU Sun Mouse");
+ }
+ if (s->chn[1].type == kbd) {
+ qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+ }
+
+ return 0;
+}
+
+static SysBusDeviceInfo escc_info = {
+ .init = escc_init1,
+ .qdev.name = "escc",
+ .qdev.size = sizeof(SerialState),
+ .qdev.vmsd = &vmstate_escc,
+ .qdev.reset = escc_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("frequency", SerialState, frequency, 0),
+ DEFINE_PROP_UINT32("it_shift", SerialState, it_shift, 0),
+ DEFINE_PROP_UINT32("disabled", SerialState, disabled, 0),
+ DEFINE_PROP_UINT32("disabled", SerialState, disabled, 0),
+ DEFINE_PROP_UINT32("chnBtype", SerialState, chn[0].type, 0),
+ DEFINE_PROP_UINT32("chnAtype", SerialState, chn[1].type, 0),
+ DEFINE_PROP_CHR("chrB", SerialState, chn[0].chr),
+ DEFINE_PROP_CHR("chrA", SerialState, chn[1].chr),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void escc_register_devices(void)
+{
+ sysbus_register_withprop(&escc_info);
+}
+
+device_init(escc_register_devices)
diff --git a/hw/escc.h b/hw/escc.h
new file mode 100644
index 0000000..015b9d0
--- /dev/null
+++ b/hw/escc.h
@@ -0,0 +1,8 @@
+/* escc.c */
+#define ESCC_SIZE 4
+int escc_init(target_phys_addr_t base, qemu_irq irqA, qemu_irq irqB,
+ CharDriverState *chrA, CharDriverState *chrB,
+ int clock, int it_shift);
+
+void slavio_serial_ms_kbd_init(target_phys_addr_t base, qemu_irq irq,
+ int disabled, int clock, int it_shift);
diff --git a/hw/esp.c b/hw/esp.c
new file mode 100644
index 0000000..fa9d2a2
--- /dev/null
+++ b/hw/esp.c
@@ -0,0 +1,751 @@
+/*
+ * QEMU ESP/NCR53C9x emulation
+ *
+ * Copyright (c) 2005-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "scsi.h"
+#include "esp.h"
+
+/* debug ESP card */
+//#define DEBUG_ESP
+
+/*
+ * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O),
+ * also produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
+ */
+
+#ifdef DEBUG_ESP
+#define DPRINTF(fmt, ...) \
+ do { printf("ESP: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define ESP_ERROR(fmt, ...) \
+ do { printf("ESP ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+
+#define ESP_REGS 16
+#define TI_BUFSZ 16
+
+typedef struct ESPState ESPState;
+
+struct ESPState {
+ SysBusDevice busdev;
+ uint32_t it_shift;
+ qemu_irq irq;
+ uint8_t rregs[ESP_REGS];
+ uint8_t wregs[ESP_REGS];
+ int32_t ti_size;
+ uint32_t ti_rptr, ti_wptr;
+ uint8_t ti_buf[TI_BUFSZ];
+ uint32_t sense;
+ uint32_t dma;
+ SCSIBus bus;
+ SCSIDevice *current_dev;
+ uint8_t cmdbuf[TI_BUFSZ];
+ uint32_t cmdlen;
+ uint32_t do_cmd;
+
+ /* The amount of data left in the current DMA transfer. */
+ uint32_t dma_left;
+ /* The size of the current DMA transfer. Zero if no transfer is in
+ progress. */
+ uint32_t dma_counter;
+ uint8_t *async_buf;
+ uint32_t async_len;
+
+ ESPDMAMemoryReadWriteFunc dma_memory_read;
+ ESPDMAMemoryReadWriteFunc dma_memory_write;
+ void *dma_opaque;
+ int dma_enabled;
+ void (*dma_cb)(ESPState *s);
+};
+
+#define ESP_TCLO 0x0
+#define ESP_TCMID 0x1
+#define ESP_FIFO 0x2
+#define ESP_CMD 0x3
+#define ESP_RSTAT 0x4
+#define ESP_WBUSID 0x4
+#define ESP_RINTR 0x5
+#define ESP_WSEL 0x5
+#define ESP_RSEQ 0x6
+#define ESP_WSYNTP 0x6
+#define ESP_RFLAGS 0x7
+#define ESP_WSYNO 0x7
+#define ESP_CFG1 0x8
+#define ESP_RRES1 0x9
+#define ESP_WCCF 0x9
+#define ESP_RRES2 0xa
+#define ESP_WTEST 0xa
+#define ESP_CFG2 0xb
+#define ESP_CFG3 0xc
+#define ESP_RES3 0xd
+#define ESP_TCHI 0xe
+#define ESP_RES4 0xf
+
+#define CMD_DMA 0x80
+#define CMD_CMD 0x7f
+
+#define CMD_NOP 0x00
+#define CMD_FLUSH 0x01
+#define CMD_RESET 0x02
+#define CMD_BUSRESET 0x03
+#define CMD_TI 0x10
+#define CMD_ICCS 0x11
+#define CMD_MSGACC 0x12
+#define CMD_PAD 0x18
+#define CMD_SATN 0x1a
+#define CMD_SEL 0x41
+#define CMD_SELATN 0x42
+#define CMD_SELATNS 0x43
+#define CMD_ENSEL 0x44
+
+#define STAT_DO 0x00
+#define STAT_DI 0x01
+#define STAT_CD 0x02
+#define STAT_ST 0x03
+#define STAT_MO 0x06
+#define STAT_MI 0x07
+#define STAT_PIO_MASK 0x06
+
+#define STAT_TC 0x10
+#define STAT_PE 0x20
+#define STAT_GE 0x40
+#define STAT_INT 0x80
+
+#define BUSID_DID 0x07
+
+#define INTR_FC 0x08
+#define INTR_BS 0x10
+#define INTR_DC 0x20
+#define INTR_RST 0x80
+
+#define SEQ_0 0x0
+#define SEQ_CD 0x4
+
+#define CFG1_RESREPT 0x40
+
+#define TCHI_FAS100A 0x4
+
+static void esp_raise_irq(ESPState *s)
+{
+ if (!(s->rregs[ESP_RSTAT] & STAT_INT)) {
+ s->rregs[ESP_RSTAT] |= STAT_INT;
+ qemu_irq_raise(s->irq);
+ DPRINTF("Raise IRQ\n");
+ }
+}
+
+static void esp_lower_irq(ESPState *s)
+{
+ if (s->rregs[ESP_RSTAT] & STAT_INT) {
+ s->rregs[ESP_RSTAT] &= ~STAT_INT;
+ qemu_irq_lower(s->irq);
+ DPRINTF("Lower IRQ\n");
+ }
+}
+
+static void esp_dma_enable(void *opaque, int irq, int level)
+{
+ DeviceState *d = opaque;
+ ESPState *s = container_of(d, ESPState, busdev.qdev);
+
+ if (level) {
+ s->dma_enabled = 1;
+ DPRINTF("Raise enable\n");
+ if (s->dma_cb) {
+ s->dma_cb(s);
+ s->dma_cb = NULL;
+ }
+ } else {
+ DPRINTF("Lower enable\n");
+ s->dma_enabled = 0;
+ }
+}
+
+static uint32_t get_cmd(ESPState *s, uint8_t *buf)
+{
+ uint32_t dmalen;
+ int target;
+
+ target = s->wregs[ESP_WBUSID] & BUSID_DID;
+ if (s->dma) {
+ dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8);
+ s->dma_memory_read(s->dma_opaque, buf, dmalen);
+ } else {
+ dmalen = s->ti_size;
+ memcpy(buf, s->ti_buf, dmalen);
+ buf[0] = 0;
+ }
+ DPRINTF("get_cmd: len %d target %d\n", dmalen, target);
+
+ s->ti_size = 0;
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+
+ 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->async_len = 0;
+ }
+
+ if (target >= ESP_MAX_DEVS || !s->bus.devs[target]) {
+ // No such drive
+ s->rregs[ESP_RSTAT] = 0;
+ s->rregs[ESP_RINTR] = INTR_DC;
+ s->rregs[ESP_RSEQ] = SEQ_0;
+ esp_raise_irq(s);
+ return 0;
+ }
+ s->current_dev = s->bus.devs[target];
+ return dmalen;
+}
+
+static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
+{
+ int32_t datalen;
+ int lun;
+
+ 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->ti_size = datalen;
+ if (datalen != 0) {
+ s->rregs[ESP_RSTAT] = STAT_TC;
+ s->dma_left = 0;
+ s->dma_counter = 0;
+ if (datalen > 0) {
+ s->rregs[ESP_RSTAT] |= STAT_DI;
+ s->current_dev->info->read_data(s->current_dev, 0);
+ } else {
+ s->rregs[ESP_RSTAT] |= STAT_DO;
+ s->current_dev->info->write_data(s->current_dev, 0);
+ }
+ }
+ s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
+ s->rregs[ESP_RSEQ] = SEQ_CD;
+ esp_raise_irq(s);
+}
+
+static void do_cmd(ESPState *s, uint8_t *buf)
+{
+ uint8_t busid = buf[0];
+
+ do_busid_cmd(s, &buf[1], busid);
+}
+
+static void handle_satn(ESPState *s)
+{
+ uint8_t buf[32];
+ int len;
+
+ if (!s->dma_enabled) {
+ s->dma_cb = handle_satn;
+ return;
+ }
+ len = get_cmd(s, buf);
+ if (len)
+ do_cmd(s, buf);
+}
+
+static void handle_s_without_atn(ESPState *s)
+{
+ uint8_t buf[32];
+ int len;
+
+ if (!s->dma_enabled) {
+ s->dma_cb = handle_s_without_atn;
+ return;
+ }
+ len = get_cmd(s, buf);
+ if (len) {
+ do_busid_cmd(s, buf, 0);
+ }
+}
+
+static void handle_satn_stop(ESPState *s)
+{
+ if (!s->dma_enabled) {
+ s->dma_cb = handle_satn_stop;
+ return;
+ }
+ s->cmdlen = get_cmd(s, s->cmdbuf);
+ if (s->cmdlen) {
+ DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen);
+ s->do_cmd = 1;
+ s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD;
+ s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
+ s->rregs[ESP_RSEQ] = SEQ_CD;
+ esp_raise_irq(s);
+ }
+}
+
+static void write_response(ESPState *s)
+{
+ DPRINTF("Transfer status (sense=%d)\n", s->sense);
+ s->ti_buf[0] = s->sense;
+ s->ti_buf[1] = 0;
+ if (s->dma) {
+ s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
+ s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST;
+ s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
+ s->rregs[ESP_RSEQ] = SEQ_CD;
+ } else {
+ s->ti_size = 2;
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+ s->rregs[ESP_RFLAGS] = 2;
+ }
+ esp_raise_irq(s);
+}
+
+static void esp_dma_done(ESPState *s)
+{
+ s->rregs[ESP_RSTAT] |= STAT_TC;
+ s->rregs[ESP_RINTR] = INTR_BS;
+ s->rregs[ESP_RSEQ] = 0;
+ s->rregs[ESP_RFLAGS] = 0;
+ s->rregs[ESP_TCLO] = 0;
+ s->rregs[ESP_TCMID] = 0;
+ esp_raise_irq(s);
+}
+
+static void esp_do_dma(ESPState *s)
+{
+ uint32_t len;
+ int to_device;
+
+ to_device = (s->ti_size < 0);
+ len = s->dma_left;
+ if (s->do_cmd) {
+ DPRINTF("command len %d + %d\n", s->cmdlen, len);
+ s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
+ s->ti_size = 0;
+ s->cmdlen = 0;
+ s->do_cmd = 0;
+ do_cmd(s, s->cmdbuf);
+ return;
+ }
+ if (s->async_len == 0) {
+ /* Defer until data is available. */
+ return;
+ }
+ if (len > s->async_len) {
+ len = s->async_len;
+ }
+ if (to_device) {
+ s->dma_memory_read(s->dma_opaque, s->async_buf, len);
+ } else {
+ s->dma_memory_write(s->dma_opaque, s->async_buf, len);
+ }
+ s->dma_left -= len;
+ s->async_buf += len;
+ s->async_len -= len;
+ if (to_device)
+ s->ti_size += len;
+ 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_dev, 0);
+ } else {
+ s->current_dev->info->read_data(s->current_dev, 0);
+ /* 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);
+ }
+ }
+ } else {
+ /* Partially filled a scsi buffer. Complete immediately. */
+ esp_dma_done(s);
+ }
+}
+
+static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
+ uint32_t arg)
+{
+ ESPState *s = DO_UPCAST(ESPState, busdev.qdev, 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->sense = arg;
+ s->rregs[ESP_RSTAT] = STAT_ST;
+ esp_dma_done(s);
+ 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);
+ 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);
+ }
+ }
+}
+
+static void handle_ti(ESPState *s)
+{
+ uint32_t dmalen, minlen;
+
+ dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8);
+ if (dmalen==0) {
+ dmalen=0x10000;
+ }
+ s->dma_counter = dmalen;
+
+ if (s->do_cmd)
+ minlen = (dmalen < 32) ? dmalen : 32;
+ else if (s->ti_size < 0)
+ minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size;
+ else
+ minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
+ DPRINTF("Transfer Information len %d\n", minlen);
+ if (s->dma) {
+ s->dma_left = minlen;
+ s->rregs[ESP_RSTAT] &= ~STAT_TC;
+ esp_do_dma(s);
+ } else if (s->do_cmd) {
+ DPRINTF("command len %d\n", s->cmdlen);
+ s->ti_size = 0;
+ s->cmdlen = 0;
+ s->do_cmd = 0;
+ do_cmd(s, s->cmdbuf);
+ return;
+ }
+}
+
+static void esp_hard_reset(DeviceState *d)
+{
+ ESPState *s = container_of(d, ESPState, busdev.qdev);
+
+ memset(s->rregs, 0, ESP_REGS);
+ memset(s->wregs, 0, ESP_REGS);
+ s->rregs[ESP_TCHI] = TCHI_FAS100A; // Indicate fas100a
+ s->ti_size = 0;
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+ s->dma = 0;
+ s->do_cmd = 0;
+ s->dma_cb = NULL;
+
+ s->rregs[ESP_CFG1] = 7;
+}
+
+static void esp_soft_reset(DeviceState *d)
+{
+ ESPState *s = container_of(d, ESPState, busdev.qdev);
+
+ qemu_irq_lower(s->irq);
+ esp_hard_reset(d);
+}
+
+static void parent_esp_reset(void *opaque, int irq, int level)
+{
+ if (level) {
+ esp_soft_reset(opaque);
+ }
+}
+
+static void esp_gpio_demux(void *opaque, int irq, int level)
+{
+ switch (irq) {
+ case 0:
+ parent_esp_reset(opaque, irq, level);
+ break;
+ case 1:
+ esp_dma_enable(opaque, irq, level);
+ break;
+ }
+}
+
+static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ ESPState *s = opaque;
+ uint32_t saddr, old_val;
+
+ saddr = addr >> s->it_shift;
+ DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
+ switch (saddr) {
+ case ESP_FIFO:
+ if (s->ti_size > 0) {
+ s->ti_size--;
+ if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) {
+ /* Data out. */
+ ESP_ERROR("PIO data read not implemented\n");
+ s->rregs[ESP_FIFO] = 0;
+ } else {
+ s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++];
+ }
+ esp_raise_irq(s);
+ }
+ if (s->ti_size == 0) {
+ s->ti_rptr = 0;
+ s->ti_wptr = 0;
+ }
+ break;
+ case ESP_RINTR:
+ /* Clear sequence step, interrupt register and all status bits
+ except TC */
+ old_val = s->rregs[ESP_RINTR];
+ s->rregs[ESP_RINTR] = 0;
+ s->rregs[ESP_RSTAT] &= ~STAT_TC;
+ s->rregs[ESP_RSEQ] = SEQ_CD;
+ esp_lower_irq(s);
+
+ return old_val;
+ default:
+ break;
+ }
+ return s->rregs[saddr];
+}
+
+static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ ESPState *s = opaque;
+ uint32_t saddr;
+
+ saddr = addr >> s->it_shift;
+ DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr],
+ val);
+ switch (saddr) {
+ case ESP_TCLO:
+ case ESP_TCMID:
+ s->rregs[ESP_RSTAT] &= ~STAT_TC;
+ break;
+ case ESP_FIFO:
+ if (s->do_cmd) {
+ s->cmdbuf[s->cmdlen++] = val & 0xff;
+ } else if (s->ti_size == TI_BUFSZ - 1) {
+ ESP_ERROR("fifo overrun\n");
+ } else {
+ s->ti_size++;
+ s->ti_buf[s->ti_wptr++] = val & 0xff;
+ }
+ break;
+ case ESP_CMD:
+ s->rregs[saddr] = val;
+ if (val & CMD_DMA) {
+ s->dma = 1;
+ /* Reload DMA counter. */
+ s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO];
+ s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID];
+ } else {
+ s->dma = 0;
+ }
+ switch(val & CMD_CMD) {
+ case CMD_NOP:
+ DPRINTF("NOP (%2.2x)\n", val);
+ break;
+ case CMD_FLUSH:
+ DPRINTF("Flush FIFO (%2.2x)\n", val);
+ //s->ti_size = 0;
+ s->rregs[ESP_RINTR] = INTR_FC;
+ s->rregs[ESP_RSEQ] = 0;
+ s->rregs[ESP_RFLAGS] = 0;
+ break;
+ case CMD_RESET:
+ DPRINTF("Chip reset (%2.2x)\n", val);
+ esp_soft_reset(&s->busdev.qdev);
+ break;
+ case CMD_BUSRESET:
+ DPRINTF("Bus reset (%2.2x)\n", val);
+ s->rregs[ESP_RINTR] = INTR_RST;
+ if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) {
+ esp_raise_irq(s);
+ }
+ break;
+ case CMD_TI:
+ handle_ti(s);
+ break;
+ case CMD_ICCS:
+ DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
+ write_response(s);
+ s->rregs[ESP_RINTR] = INTR_FC;
+ s->rregs[ESP_RSTAT] |= STAT_MI;
+ break;
+ case CMD_MSGACC:
+ DPRINTF("Message Accepted (%2.2x)\n", val);
+ s->rregs[ESP_RINTR] = INTR_DC;
+ s->rregs[ESP_RSEQ] = 0;
+ s->rregs[ESP_RFLAGS] = 0;
+ esp_raise_irq(s);
+ break;
+ case CMD_PAD:
+ DPRINTF("Transfer padding (%2.2x)\n", val);
+ s->rregs[ESP_RSTAT] = STAT_TC;
+ s->rregs[ESP_RINTR] = INTR_FC;
+ s->rregs[ESP_RSEQ] = 0;
+ break;
+ case CMD_SATN:
+ DPRINTF("Set ATN (%2.2x)\n", val);
+ break;
+ case CMD_SEL:
+ DPRINTF("Select without ATN (%2.2x)\n", val);
+ handle_s_without_atn(s);
+ break;
+ case CMD_SELATN:
+ DPRINTF("Select with ATN (%2.2x)\n", val);
+ handle_satn(s);
+ break;
+ case CMD_SELATNS:
+ DPRINTF("Select with ATN & stop (%2.2x)\n", val);
+ handle_satn_stop(s);
+ break;
+ case CMD_ENSEL:
+ DPRINTF("Enable selection (%2.2x)\n", val);
+ s->rregs[ESP_RINTR] = 0;
+ break;
+ default:
+ ESP_ERROR("Unhandled ESP command (%2.2x)\n", val);
+ break;
+ }
+ break;
+ case ESP_WBUSID ... ESP_WSYNO:
+ break;
+ case ESP_CFG1:
+ s->rregs[saddr] = val;
+ break;
+ case ESP_WCCF ... ESP_WTEST:
+ break;
+ case ESP_CFG2 ... ESP_RES4:
+ s->rregs[saddr] = val;
+ break;
+ default:
+ ESP_ERROR("invalid write of 0x%02x at [0x%x]\n", val, saddr);
+ return;
+ }
+ s->wregs[saddr] = val;
+}
+
+static CPUReadMemoryFunc * const esp_mem_read[3] = {
+ esp_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const esp_mem_write[3] = {
+ esp_mem_writeb,
+ NULL,
+ esp_mem_writeb,
+};
+
+static const VMStateDescription vmstate_esp = {
+ .name ="esp",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_BUFFER(rregs, ESPState),
+ VMSTATE_BUFFER(wregs, ESPState),
+ VMSTATE_INT32(ti_size, ESPState),
+ VMSTATE_UINT32(ti_rptr, ESPState),
+ VMSTATE_UINT32(ti_wptr, ESPState),
+ VMSTATE_BUFFER(ti_buf, ESPState),
+ VMSTATE_UINT32(sense, ESPState),
+ VMSTATE_UINT32(dma, ESPState),
+ VMSTATE_BUFFER(cmdbuf, ESPState),
+ VMSTATE_UINT32(cmdlen, ESPState),
+ VMSTATE_UINT32(do_cmd, ESPState),
+ VMSTATE_UINT32(dma_left, ESPState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void esp_init(target_phys_addr_t espaddr, int it_shift,
+ ESPDMAMemoryReadWriteFunc dma_memory_read,
+ ESPDMAMemoryReadWriteFunc dma_memory_write,
+ void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+ qemu_irq *dma_enable)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ ESPState *esp;
+
+ dev = qdev_create(NULL, "esp");
+ esp = DO_UPCAST(ESPState, busdev.qdev, dev);
+ esp->dma_memory_read = dma_memory_read;
+ esp->dma_memory_write = dma_memory_write;
+ esp->dma_opaque = dma_opaque;
+ esp->it_shift = it_shift;
+ /* XXX for now until rc4030 has been changed to use DMA enable signal */
+ esp->dma_enabled = 1;
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irq);
+ sysbus_mmio_map(s, 0, espaddr);
+ *reset = qdev_get_gpio_in(dev, 0);
+ *dma_enable = qdev_get_gpio_in(dev, 1);
+}
+
+static int esp_init1(SysBusDevice *dev)
+{
+ ESPState *s = FROM_SYSBUS(ESPState, dev);
+ int esp_io_memory;
+
+ sysbus_init_irq(dev, &s->irq);
+ assert(s->it_shift != -1);
+
+ esp_io_memory = cpu_register_io_memory(esp_mem_read, esp_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, ESP_REGS << s->it_shift, esp_io_memory);
+
+ qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
+
+ scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
+ return scsi_bus_legacy_handle_cmdline(&s->bus);
+}
+
+static SysBusDeviceInfo esp_info = {
+ .init = esp_init1,
+ .qdev.name = "esp",
+ .qdev.size = sizeof(ESPState),
+ .qdev.vmsd = &vmstate_esp,
+ .qdev.reset = esp_hard_reset,
+ .qdev.props = (Property[]) {
+ {.name = NULL}
+ }
+};
+
+static void esp_register_devices(void)
+{
+ sysbus_register_withprop(&esp_info);
+}
+
+device_init(esp_register_devices)
diff --git a/hw/esp.h b/hw/esp.h
new file mode 100644
index 0000000..62bfd4d
--- /dev/null
+++ b/hw/esp.h
@@ -0,0 +1,13 @@
+#ifndef QEMU_HW_ESP_H
+#define QEMU_HW_ESP_H
+
+/* esp.c */
+#define ESP_MAX_DEVS 7
+typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len);
+void esp_init(target_phys_addr_t espaddr, int it_shift,
+ ESPDMAMemoryReadWriteFunc dma_memory_read,
+ ESPDMAMemoryReadWriteFunc dma_memory_write,
+ void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+ qemu_irq *dma_enable);
+
+#endif
diff --git a/hw/etraxfs.c b/hw/etraxfs.c
new file mode 100644
index 0000000..5ee5f97
--- /dev/null
+++ b/hw/etraxfs.c
@@ -0,0 +1,160 @@
+/*
+ * QEMU ETRAX System Emulator
+ *
+ * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "boards.h"
+#include "sysemu.h"
+#include "net.h"
+#include "flash.h"
+#include "etraxfs.h"
+#include "loader.h"
+#include "elf.h"
+#include "cris-boot.h"
+#include "blockdev.h"
+
+#define FLASH_SIZE 0x2000000
+#define INTMEM_SIZE (128 * 1024)
+
+static struct cris_load_info li;
+
+static void flash_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+static
+void bareetraxfs_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ CPUState *env;
+ qemu_irq irq[30], nmi[2], *cpu_irq;
+ void *etraxfs_dmac;
+ struct etraxfs_dma_client *eth[2] = {NULL, NULL};
+ DriveInfo *dinfo;
+ int i;
+ ram_addr_t phys_ram;
+ ram_addr_t phys_flash;
+ ram_addr_t phys_intmem;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "crisv32";
+ }
+ env = cpu_init(cpu_model);
+
+ /* allocate RAM */
+ phys_ram = qemu_ram_alloc(NULL, "etraxfs.ram", ram_size);
+ cpu_register_physical_memory(0x40000000, ram_size, phys_ram | IO_MEM_RAM);
+
+ /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the
+ internal memory. */
+ phys_intmem = qemu_ram_alloc(NULL, "etraxfs.chipram", INTMEM_SIZE);
+ cpu_register_physical_memory(0x38000000, INTMEM_SIZE,
+ phys_intmem | IO_MEM_RAM);
+
+
+ phys_flash = qemu_ram_alloc(NULL, "etraxfs.flash", FLASH_SIZE);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi02_register(0x0, phys_flash,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ FLASH_SIZE >> 16,
+ 1, 2, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x555, 0x2aa, 0);
+ cpu_irq = cris_pic_init_cpu(env);
+ dev = qdev_create(NULL, "etraxfs,pic");
+ /* FIXME: Is there a proper way to signal vectors to the CPU core? */
+ qdev_prop_set_ptr(dev, "interrupt_vector", &env->interrupt_vector);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, 0x3001c000);
+ sysbus_connect_irq(s, 0, cpu_irq[0]);
+ sysbus_connect_irq(s, 1, cpu_irq[1]);
+ for (i = 0; i < 30; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+ nmi[0] = qdev_get_gpio_in(dev, 30);
+ nmi[1] = qdev_get_gpio_in(dev, 31);
+
+ etraxfs_dmac = etraxfs_dmac_init(0x30000000, 10);
+ for (i = 0; i < 10; i++) {
+ /* On ETRAX, odd numbered channels are inputs. */
+ etraxfs_dmac_connect(etraxfs_dmac, i, irq + 7 + i, i & 1);
+ }
+
+ /* Add the two ethernet blocks. */
+ eth[0] = etraxfs_eth_init(&nd_table[0], 0x30034000, 1);
+ if (nb_nics > 1)
+ eth[1] = etraxfs_eth_init(&nd_table[1], 0x30036000, 2);
+
+ /* The DMA Connector block is missing, hardwire things for now. */
+ etraxfs_dmac_connect_client(etraxfs_dmac, 0, eth[0]);
+ etraxfs_dmac_connect_client(etraxfs_dmac, 1, eth[0] + 1);
+ if (eth[1]) {
+ etraxfs_dmac_connect_client(etraxfs_dmac, 6, eth[1]);
+ etraxfs_dmac_connect_client(etraxfs_dmac, 7, eth[1] + 1);
+ }
+
+ /* 2 timers. */
+ sysbus_create_varargs("etraxfs,timer", 0x3001e000, irq[0x1b], nmi[1], NULL);
+ sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL);
+
+ for (i = 0; i < 4; i++) {
+ sysbus_create_simple("etraxfs,serial", 0x30026000 + i * 0x2000,
+ irq[0x14 + i]);
+ }
+
+ if (kernel_filename) {
+ li.image_filename = kernel_filename;
+ li.cmdline = kernel_cmdline;
+ cris_load_image(env, &li);
+ } else {
+ if (!dinfo) {
+ fprintf(stderr,
+ "Provide a kernel image or a flash image to boot from.\n");
+ exit(1);
+ }
+
+ /* Nothing more to do for flash images, those boot from addr 0. */
+ qemu_register_reset(flash_cpu_reset, env);
+ }
+}
+
+static QEMUMachine bareetraxfs_machine = {
+ .name = "bareetraxfs",
+ .desc = "Bare ETRAX FS board",
+ .init = bareetraxfs_init,
+ .is_default = 1,
+};
+
+static void bareetraxfs_machine_init(void)
+{
+ qemu_register_machine(&bareetraxfs_machine);
+}
+
+machine_init(bareetraxfs_machine_init);
diff --git a/hw/etraxfs.h b/hw/etraxfs.h
new file mode 100644
index 0000000..01fb9d3
--- /dev/null
+++ b/hw/etraxfs.h
@@ -0,0 +1,28 @@
+/*
+ * QEMU ETRAX System Emulator
+ *
+ * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "etraxfs_dma.h"
+
+qemu_irq *cris_pic_init_cpu(CPUState *env);
+void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr);
diff --git a/hw/etraxfs_dma.c b/hw/etraxfs_dma.c
new file mode 100644
index 0000000..c205ec1
--- /dev/null
+++ b/hw/etraxfs_dma.c
@@ -0,0 +1,756 @@
+/*
+ * QEMU ETRAX DMA Controller.
+ *
+ * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdio.h>
+#include <sys/time.h>
+#include "hw.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+
+#include "etraxfs_dma.h"
+
+#define D(x)
+
+#define RW_DATA (0x0 / 4)
+#define RW_SAVED_DATA (0x58 / 4)
+#define RW_SAVED_DATA_BUF (0x5c / 4)
+#define RW_GROUP (0x60 / 4)
+#define RW_GROUP_DOWN (0x7c / 4)
+#define RW_CMD (0x80 / 4)
+#define RW_CFG (0x84 / 4)
+#define RW_STAT (0x88 / 4)
+#define RW_INTR_MASK (0x8c / 4)
+#define RW_ACK_INTR (0x90 / 4)
+#define R_INTR (0x94 / 4)
+#define R_MASKED_INTR (0x98 / 4)
+#define RW_STREAM_CMD (0x9c / 4)
+
+#define DMA_REG_MAX (0x100 / 4)
+
+/* descriptors */
+
+// ------------------------------------------------------------ dma_descr_group
+typedef struct dma_descr_group {
+ uint32_t next;
+ unsigned eol : 1;
+ unsigned tol : 1;
+ unsigned bol : 1;
+ unsigned : 1;
+ unsigned intr : 1;
+ unsigned : 2;
+ unsigned en : 1;
+ unsigned : 7;
+ unsigned dis : 1;
+ unsigned md : 16;
+ struct dma_descr_group *up;
+ union {
+ struct dma_descr_context *context;
+ struct dma_descr_group *group;
+ } down;
+} dma_descr_group;
+
+// ---------------------------------------------------------- dma_descr_context
+typedef struct dma_descr_context {
+ uint32_t next;
+ unsigned eol : 1;
+ unsigned : 3;
+ unsigned intr : 1;
+ unsigned : 1;
+ unsigned store_mode : 1;
+ unsigned en : 1;
+ unsigned : 7;
+ unsigned dis : 1;
+ unsigned md0 : 16;
+ unsigned md1;
+ unsigned md2;
+ unsigned md3;
+ unsigned md4;
+ uint32_t saved_data;
+ uint32_t saved_data_buf;
+} dma_descr_context;
+
+// ------------------------------------------------------------- dma_descr_data
+typedef struct dma_descr_data {
+ uint32_t next;
+ uint32_t buf;
+ unsigned eol : 1;
+ unsigned : 2;
+ unsigned out_eop : 1;
+ unsigned intr : 1;
+ unsigned wait : 1;
+ unsigned : 2;
+ unsigned : 3;
+ unsigned in_eop : 1;
+ unsigned : 4;
+ unsigned md : 16;
+ uint32_t after;
+} dma_descr_data;
+
+/* Constants */
+enum {
+ regk_dma_ack_pkt = 0x00000100,
+ regk_dma_anytime = 0x00000001,
+ regk_dma_array = 0x00000008,
+ regk_dma_burst = 0x00000020,
+ regk_dma_client = 0x00000002,
+ regk_dma_copy_next = 0x00000010,
+ regk_dma_copy_up = 0x00000020,
+ regk_dma_data_at_eol = 0x00000001,
+ regk_dma_dis_c = 0x00000010,
+ regk_dma_dis_g = 0x00000020,
+ regk_dma_idle = 0x00000001,
+ regk_dma_intern = 0x00000004,
+ regk_dma_load_c = 0x00000200,
+ regk_dma_load_c_n = 0x00000280,
+ regk_dma_load_c_next = 0x00000240,
+ regk_dma_load_d = 0x00000140,
+ regk_dma_load_g = 0x00000300,
+ regk_dma_load_g_down = 0x000003c0,
+ regk_dma_load_g_next = 0x00000340,
+ regk_dma_load_g_up = 0x00000380,
+ regk_dma_next_en = 0x00000010,
+ regk_dma_next_pkt = 0x00000010,
+ regk_dma_no = 0x00000000,
+ regk_dma_only_at_wait = 0x00000000,
+ regk_dma_restore = 0x00000020,
+ regk_dma_rst = 0x00000001,
+ regk_dma_running = 0x00000004,
+ regk_dma_rw_cfg_default = 0x00000000,
+ regk_dma_rw_cmd_default = 0x00000000,
+ regk_dma_rw_intr_mask_default = 0x00000000,
+ regk_dma_rw_stat_default = 0x00000101,
+ regk_dma_rw_stream_cmd_default = 0x00000000,
+ regk_dma_save_down = 0x00000020,
+ regk_dma_save_up = 0x00000020,
+ regk_dma_set_reg = 0x00000050,
+ regk_dma_set_w_size1 = 0x00000190,
+ regk_dma_set_w_size2 = 0x000001a0,
+ regk_dma_set_w_size4 = 0x000001c0,
+ regk_dma_stopped = 0x00000002,
+ regk_dma_store_c = 0x00000002,
+ regk_dma_store_descr = 0x00000000,
+ regk_dma_store_g = 0x00000004,
+ regk_dma_store_md = 0x00000001,
+ regk_dma_sw = 0x00000008,
+ regk_dma_update_down = 0x00000020,
+ regk_dma_yes = 0x00000001
+};
+
+enum dma_ch_state
+{
+ RST = 1,
+ STOPPED = 2,
+ RUNNING = 4
+};
+
+struct fs_dma_channel
+{
+ qemu_irq irq;
+ struct etraxfs_dma_client *client;
+
+ /* Internal status. */
+ int stream_cmd_src;
+ enum dma_ch_state state;
+
+ unsigned int input : 1;
+ unsigned int eol : 1;
+
+ struct dma_descr_group current_g;
+ struct dma_descr_context current_c;
+ struct dma_descr_data current_d;
+
+ /* Controll registers. */
+ uint32_t regs[DMA_REG_MAX];
+};
+
+struct fs_dma_ctrl
+{
+ int map;
+ int nr_channels;
+ struct fs_dma_channel *channels;
+
+ QEMUBH *bh;
+};
+
+static void DMA_run(void *opaque);
+static int channel_out_run(struct fs_dma_ctrl *ctrl, int c);
+
+static inline uint32_t channel_reg(struct fs_dma_ctrl *ctrl, int c, int reg)
+{
+ return ctrl->channels[c].regs[reg];
+}
+
+static inline int channel_stopped(struct fs_dma_ctrl *ctrl, int c)
+{
+ return channel_reg(ctrl, c, RW_CFG) & 2;
+}
+
+static inline int channel_en(struct fs_dma_ctrl *ctrl, int c)
+{
+ return (channel_reg(ctrl, c, RW_CFG) & 1)
+ && ctrl->channels[c].client;
+}
+
+static inline int fs_channel(target_phys_addr_t addr)
+{
+ /* Every channel has a 0x2000 ctrl register map. */
+ return addr >> 13;
+}
+
+#ifdef USE_THIS_DEAD_CODE
+static void channel_load_g(struct fs_dma_ctrl *ctrl, int c)
+{
+ target_phys_addr_t addr = channel_reg(ctrl, c, RW_GROUP);
+
+ /* Load and decode. FIXME: handle endianness. */
+ cpu_physical_memory_read (addr,
+ (void *) &ctrl->channels[c].current_g,
+ sizeof ctrl->channels[c].current_g);
+}
+
+static void dump_c(int ch, struct dma_descr_context *c)
+{
+ printf("%s ch=%d\n", __func__, ch);
+ printf("next=%x\n", c->next);
+ printf("saved_data=%x\n", c->saved_data);
+ printf("saved_data_buf=%x\n", c->saved_data_buf);
+ printf("eol=%x\n", (uint32_t) c->eol);
+}
+
+static void dump_d(int ch, struct dma_descr_data *d)
+{
+ printf("%s ch=%d\n", __func__, ch);
+ printf("next=%x\n", d->next);
+ printf("buf=%x\n", d->buf);
+ printf("after=%x\n", d->after);
+ printf("intr=%x\n", (uint32_t) d->intr);
+ printf("out_eop=%x\n", (uint32_t) d->out_eop);
+ printf("in_eop=%x\n", (uint32_t) d->in_eop);
+ printf("eol=%x\n", (uint32_t) d->eol);
+}
+#endif
+
+static void channel_load_c(struct fs_dma_ctrl *ctrl, int c)
+{
+ target_phys_addr_t addr = channel_reg(ctrl, c, RW_GROUP_DOWN);
+
+ /* Load and decode. FIXME: handle endianness. */
+ cpu_physical_memory_read (addr,
+ (void *) &ctrl->channels[c].current_c,
+ sizeof ctrl->channels[c].current_c);
+
+ D(dump_c(c, &ctrl->channels[c].current_c));
+ /* I guess this should update the current pos. */
+ ctrl->channels[c].regs[RW_SAVED_DATA] =
+ (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data;
+ ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
+ (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data_buf;
+}
+
+static void channel_load_d(struct fs_dma_ctrl *ctrl, int c)
+{
+ target_phys_addr_t addr = channel_reg(ctrl, c, RW_SAVED_DATA);
+
+ /* Load and decode. FIXME: handle endianness. */
+ D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr));
+ cpu_physical_memory_read (addr,
+ (void *) &ctrl->channels[c].current_d,
+ sizeof ctrl->channels[c].current_d);
+
+ D(dump_d(c, &ctrl->channels[c].current_d));
+ ctrl->channels[c].regs[RW_DATA] = addr;
+}
+
+static void channel_store_c(struct fs_dma_ctrl *ctrl, int c)
+{
+ target_phys_addr_t addr = channel_reg(ctrl, c, RW_GROUP_DOWN);
+
+ /* Encode and store. FIXME: handle endianness. */
+ D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr));
+ D(dump_d(c, &ctrl->channels[c].current_d));
+ cpu_physical_memory_write (addr,
+ (void *) &ctrl->channels[c].current_c,
+ sizeof ctrl->channels[c].current_c);
+}
+
+static void channel_store_d(struct fs_dma_ctrl *ctrl, int c)
+{
+ target_phys_addr_t addr = channel_reg(ctrl, c, RW_SAVED_DATA);
+
+ /* Encode and store. FIXME: handle endianness. */
+ D(printf("%s ch=%d addr=" TARGET_FMT_plx "\n", __func__, c, addr));
+ cpu_physical_memory_write (addr,
+ (void *) &ctrl->channels[c].current_d,
+ sizeof ctrl->channels[c].current_d);
+}
+
+static inline void channel_stop(struct fs_dma_ctrl *ctrl, int c)
+{
+ /* FIXME: */
+}
+
+static inline void channel_start(struct fs_dma_ctrl *ctrl, int c)
+{
+ if (ctrl->channels[c].client)
+ {
+ ctrl->channels[c].eol = 0;
+ ctrl->channels[c].state = RUNNING;
+ if (!ctrl->channels[c].input)
+ channel_out_run(ctrl, c);
+ } else
+ printf("WARNING: starting DMA ch %d with no client\n", c);
+
+ qemu_bh_schedule_idle(ctrl->bh);
+}
+
+static void channel_continue(struct fs_dma_ctrl *ctrl, int c)
+{
+ if (!channel_en(ctrl, c)
+ || channel_stopped(ctrl, c)
+ || ctrl->channels[c].state != RUNNING
+ /* Only reload the current data descriptor if it has eol set. */
+ || !ctrl->channels[c].current_d.eol) {
+ D(printf("continue failed ch=%d state=%d stopped=%d en=%d eol=%d\n",
+ c, ctrl->channels[c].state,
+ channel_stopped(ctrl, c),
+ channel_en(ctrl,c),
+ ctrl->channels[c].eol));
+ D(dump_d(c, &ctrl->channels[c].current_d));
+ return;
+ }
+
+ /* Reload the current descriptor. */
+ channel_load_d(ctrl, c);
+
+ /* If the current descriptor cleared the eol flag and we had already
+ reached eol state, do the continue. */
+ if (!ctrl->channels[c].current_d.eol && ctrl->channels[c].eol) {
+ D(printf("continue %d ok %x\n", c,
+ ctrl->channels[c].current_d.next));
+ ctrl->channels[c].regs[RW_SAVED_DATA] =
+ (uint32_t)(unsigned long)ctrl->channels[c].current_d.next;
+ channel_load_d(ctrl, c);
+ ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
+ (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf;
+
+ channel_start(ctrl, c);
+ }
+ ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
+ (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf;
+}
+
+static void channel_stream_cmd(struct fs_dma_ctrl *ctrl, int c, uint32_t v)
+{
+ unsigned int cmd = v & ((1 << 10) - 1);
+
+ D(printf("%s ch=%d cmd=%x\n",
+ __func__, c, cmd));
+ if (cmd & regk_dma_load_d) {
+ channel_load_d(ctrl, c);
+ if (cmd & regk_dma_burst)
+ channel_start(ctrl, c);
+ }
+
+ if (cmd & regk_dma_load_c) {
+ channel_load_c(ctrl, c);
+ }
+}
+
+static void channel_update_irq(struct fs_dma_ctrl *ctrl, int c)
+{
+ D(printf("%s %d\n", __func__, c));
+ ctrl->channels[c].regs[R_INTR] &=
+ ~(ctrl->channels[c].regs[RW_ACK_INTR]);
+
+ ctrl->channels[c].regs[R_MASKED_INTR] =
+ ctrl->channels[c].regs[R_INTR]
+ & ctrl->channels[c].regs[RW_INTR_MASK];
+
+ D(printf("%s: chan=%d masked_intr=%x\n", __func__,
+ c,
+ ctrl->channels[c].regs[R_MASKED_INTR]));
+
+ qemu_set_irq(ctrl->channels[c].irq,
+ !!ctrl->channels[c].regs[R_MASKED_INTR]);
+}
+
+static int channel_out_run(struct fs_dma_ctrl *ctrl, int c)
+{
+ uint32_t len;
+ uint32_t saved_data_buf;
+ unsigned char buf[2 * 1024];
+
+ if (ctrl->channels[c].eol)
+ return 0;
+
+ do {
+ D(printf("ch=%d buf=%x after=%x\n",
+ c,
+ (uint32_t)ctrl->channels[c].current_d.buf,
+ (uint32_t)ctrl->channels[c].current_d.after));
+
+ channel_load_d(ctrl, c);
+ saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF);
+ len = (uint32_t)(unsigned long)
+ ctrl->channels[c].current_d.after;
+ len -= saved_data_buf;
+
+ if (len > sizeof buf)
+ len = sizeof buf;
+ cpu_physical_memory_read (saved_data_buf, buf, len);
+
+ D(printf("channel %d pushes %x %u bytes\n", c,
+ saved_data_buf, len));
+
+ if (ctrl->channels[c].client->client.push)
+ ctrl->channels[c].client->client.push(
+ ctrl->channels[c].client->client.opaque,
+ buf, len);
+ else
+ printf("WARNING: DMA ch%d dataloss,"
+ " no attached client.\n", c);
+
+ saved_data_buf += len;
+
+ if (saved_data_buf == (uint32_t)(unsigned long)
+ ctrl->channels[c].current_d.after) {
+ /* Done. Step to next. */
+ if (ctrl->channels[c].current_d.out_eop) {
+ /* TODO: signal eop to the client. */
+ D(printf("signal eop\n"));
+ }
+ if (ctrl->channels[c].current_d.intr) {
+ /* TODO: signal eop to the client. */
+ /* data intr. */
+ D(printf("signal intr %d eol=%d\n",
+ len, ctrl->channels[c].current_d.eol));
+ ctrl->channels[c].regs[R_INTR] |= (1 << 2);
+ channel_update_irq(ctrl, c);
+ }
+ channel_store_d(ctrl, c);
+ if (ctrl->channels[c].current_d.eol) {
+ D(printf("channel %d EOL\n", c));
+ ctrl->channels[c].eol = 1;
+
+ /* Mark the context as disabled. */
+ ctrl->channels[c].current_c.dis = 1;
+ channel_store_c(ctrl, c);
+
+ channel_stop(ctrl, c);
+ } else {
+ ctrl->channels[c].regs[RW_SAVED_DATA] =
+ (uint32_t)(unsigned long)ctrl->
+ channels[c].current_d.next;
+ /* Load new descriptor. */
+ channel_load_d(ctrl, c);
+ saved_data_buf = (uint32_t)(unsigned long)
+ ctrl->channels[c].current_d.buf;
+ }
+
+ ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
+ saved_data_buf;
+ D(dump_d(c, &ctrl->channels[c].current_d));
+ }
+ ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf;
+ } while (!ctrl->channels[c].eol);
+ return 1;
+}
+
+static int channel_in_process(struct fs_dma_ctrl *ctrl, int c,
+ unsigned char *buf, int buflen, int eop)
+{
+ uint32_t len;
+ uint32_t saved_data_buf;
+
+ if (ctrl->channels[c].eol == 1)
+ return 0;
+
+ channel_load_d(ctrl, c);
+ saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF);
+ len = (uint32_t)(unsigned long)ctrl->channels[c].current_d.after;
+ len -= saved_data_buf;
+
+ if (len > buflen)
+ len = buflen;
+
+ cpu_physical_memory_write (saved_data_buf, buf, len);
+ saved_data_buf += len;
+
+ if (saved_data_buf ==
+ (uint32_t)(unsigned long)ctrl->channels[c].current_d.after
+ || eop) {
+ uint32_t r_intr = ctrl->channels[c].regs[R_INTR];
+
+ D(printf("in dscr end len=%d\n",
+ ctrl->channels[c].current_d.after
+ - ctrl->channels[c].current_d.buf));
+ ctrl->channels[c].current_d.after = saved_data_buf;
+
+ /* Done. Step to next. */
+ if (ctrl->channels[c].current_d.intr) {
+ /* TODO: signal eop to the client. */
+ /* data intr. */
+ ctrl->channels[c].regs[R_INTR] |= 3;
+ }
+ if (eop) {
+ ctrl->channels[c].current_d.in_eop = 1;
+ ctrl->channels[c].regs[R_INTR] |= 8;
+ }
+ if (r_intr != ctrl->channels[c].regs[R_INTR])
+ channel_update_irq(ctrl, c);
+
+ channel_store_d(ctrl, c);
+ D(dump_d(c, &ctrl->channels[c].current_d));
+
+ if (ctrl->channels[c].current_d.eol) {
+ D(printf("channel %d EOL\n", c));
+ ctrl->channels[c].eol = 1;
+
+ /* Mark the context as disabled. */
+ ctrl->channels[c].current_c.dis = 1;
+ channel_store_c(ctrl, c);
+
+ channel_stop(ctrl, c);
+ } else {
+ ctrl->channels[c].regs[RW_SAVED_DATA] =
+ (uint32_t)(unsigned long)ctrl->
+ channels[c].current_d.next;
+ /* Load new descriptor. */
+ channel_load_d(ctrl, c);
+ saved_data_buf = (uint32_t)(unsigned long)
+ ctrl->channels[c].current_d.buf;
+ }
+ }
+
+ ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf;
+ return len;
+}
+
+static inline int channel_in_run(struct fs_dma_ctrl *ctrl, int c)
+{
+ if (ctrl->channels[c].client->client.pull) {
+ ctrl->channels[c].client->client.pull(
+ ctrl->channels[c].client->client.opaque);
+ return 1;
+ } else
+ return 0;
+}
+
+static uint32_t dma_rinvalid (void *opaque, target_phys_addr_t addr)
+{
+ hw_error("Unsupported short raccess. reg=" TARGET_FMT_plx "\n", addr);
+ return 0;
+}
+
+static uint32_t
+dma_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct fs_dma_ctrl *ctrl = opaque;
+ int c;
+ uint32_t r = 0;
+
+ /* Make addr relative to this channel and bounded to nr regs. */
+ c = fs_channel(addr);
+ addr &= 0xff;
+ addr >>= 2;
+ switch (addr)
+ {
+ case RW_STAT:
+ r = ctrl->channels[c].state & 7;
+ r |= ctrl->channels[c].eol << 5;
+ r |= ctrl->channels[c].stream_cmd_src << 8;
+ break;
+
+ default:
+ r = ctrl->channels[c].regs[addr];
+ D(printf ("%s c=%d addr=" TARGET_FMT_plx "\n",
+ __func__, c, addr));
+ break;
+ }
+ return r;
+}
+
+static void
+dma_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ hw_error("Unsupported short waccess. reg=" TARGET_FMT_plx "\n", addr);
+}
+
+static void
+dma_update_state(struct fs_dma_ctrl *ctrl, int c)
+{
+ if ((ctrl->channels[c].regs[RW_CFG] & 1) != 3) {
+ if (ctrl->channels[c].regs[RW_CFG] & 2)
+ ctrl->channels[c].state = STOPPED;
+ if (!(ctrl->channels[c].regs[RW_CFG] & 1))
+ ctrl->channels[c].state = RST;
+ }
+}
+
+static void
+dma_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct fs_dma_ctrl *ctrl = opaque;
+ int c;
+
+ /* Make addr relative to this channel and bounded to nr regs. */
+ c = fs_channel(addr);
+ addr &= 0xff;
+ addr >>= 2;
+ switch (addr)
+ {
+ case RW_DATA:
+ ctrl->channels[c].regs[addr] = value;
+ break;
+
+ case RW_CFG:
+ ctrl->channels[c].regs[addr] = value;
+ dma_update_state(ctrl, c);
+ break;
+ case RW_CMD:
+ /* continue. */
+ if (value & ~1)
+ printf("Invalid store to ch=%d RW_CMD %x\n",
+ c, value);
+ ctrl->channels[c].regs[addr] = value;
+ channel_continue(ctrl, c);
+ break;
+
+ case RW_SAVED_DATA:
+ case RW_SAVED_DATA_BUF:
+ case RW_GROUP:
+ case RW_GROUP_DOWN:
+ ctrl->channels[c].regs[addr] = value;
+ break;
+
+ case RW_ACK_INTR:
+ case RW_INTR_MASK:
+ ctrl->channels[c].regs[addr] = value;
+ channel_update_irq(ctrl, c);
+ if (addr == RW_ACK_INTR)
+ ctrl->channels[c].regs[RW_ACK_INTR] = 0;
+ break;
+
+ case RW_STREAM_CMD:
+ if (value & ~1023)
+ printf("Invalid store to ch=%d "
+ "RW_STREAMCMD %x\n",
+ c, value);
+ ctrl->channels[c].regs[addr] = value;
+ D(printf("stream_cmd ch=%d\n", c));
+ channel_stream_cmd(ctrl, c, value);
+ break;
+
+ default:
+ D(printf ("%s c=%d " TARGET_FMT_plx "\n",
+ __func__, c, addr));
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const dma_read[] = {
+ &dma_rinvalid,
+ &dma_rinvalid,
+ &dma_readl,
+};
+
+static CPUWriteMemoryFunc * const dma_write[] = {
+ &dma_winvalid,
+ &dma_winvalid,
+ &dma_writel,
+};
+
+static int etraxfs_dmac_run(void *opaque)
+{
+ struct fs_dma_ctrl *ctrl = opaque;
+ int i;
+ int p = 0;
+
+ for (i = 0;
+ i < ctrl->nr_channels;
+ i++)
+ {
+ if (ctrl->channels[i].state == RUNNING)
+ {
+ if (ctrl->channels[i].input) {
+ p += channel_in_run(ctrl, i);
+ } else {
+ p += channel_out_run(ctrl, i);
+ }
+ }
+ }
+ return p;
+}
+
+int etraxfs_dmac_input(struct etraxfs_dma_client *client,
+ void *buf, int len, int eop)
+{
+ return channel_in_process(client->ctrl, client->channel,
+ buf, len, eop);
+}
+
+/* Connect an IRQ line with a channel. */
+void etraxfs_dmac_connect(void *opaque, int c, qemu_irq *line, int input)
+{
+ struct fs_dma_ctrl *ctrl = opaque;
+ ctrl->channels[c].irq = *line;
+ ctrl->channels[c].input = input;
+}
+
+void etraxfs_dmac_connect_client(void *opaque, int c,
+ struct etraxfs_dma_client *cl)
+{
+ struct fs_dma_ctrl *ctrl = opaque;
+ cl->ctrl = ctrl;
+ cl->channel = c;
+ ctrl->channels[c].client = cl;
+}
+
+
+static void DMA_run(void *opaque)
+{
+ struct fs_dma_ctrl *etraxfs_dmac = opaque;
+ int p = 1;
+
+ if (vm_running)
+ p = etraxfs_dmac_run(etraxfs_dmac);
+
+ if (p)
+ qemu_bh_schedule_idle(etraxfs_dmac->bh);
+}
+
+void *etraxfs_dmac_init(target_phys_addr_t base, int nr_channels)
+{
+ struct fs_dma_ctrl *ctrl = NULL;
+
+ ctrl = qemu_mallocz(sizeof *ctrl);
+
+ ctrl->bh = qemu_bh_new(DMA_run, ctrl);
+
+ ctrl->nr_channels = nr_channels;
+ ctrl->channels = qemu_mallocz(sizeof ctrl->channels[0] * nr_channels);
+
+ ctrl->map = cpu_register_io_memory(dma_read, dma_write, ctrl, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, nr_channels * 0x2000, ctrl->map);
+ return ctrl;
+}
diff --git a/hw/etraxfs_dma.h b/hw/etraxfs_dma.h
new file mode 100644
index 0000000..96408ab
--- /dev/null
+++ b/hw/etraxfs_dma.h
@@ -0,0 +1,22 @@
+struct etraxfs_dma_client
+{
+ /* DMA controller. */
+ int channel;
+ void *ctrl;
+
+ /* client. */
+ struct
+ {
+ int (*push)(void *opaque, unsigned char *buf, int len);
+ void (*pull)(void *opaque);
+ void *opaque;
+ } client;
+};
+
+void *etraxfs_dmac_init(target_phys_addr_t base, int nr_channels);
+void etraxfs_dmac_connect(void *opaque, int channel, qemu_irq *line,
+ int input);
+void etraxfs_dmac_connect_client(void *opaque, int c,
+ struct etraxfs_dma_client *cl);
+int etraxfs_dmac_input(struct etraxfs_dma_client *client,
+ void *buf, int len, int eop);
diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c
new file mode 100644
index 0000000..6aa4007
--- /dev/null
+++ b/hw/etraxfs_eth.c
@@ -0,0 +1,613 @@
+/*
+ * QEMU ETRAX Ethernet Controller.
+ *
+ * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include "hw.h"
+#include "net.h"
+#include "etraxfs.h"
+
+#define D(x)
+
+/* Advertisement control register. */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+
+/*
+ * The MDIO extensions in the TDK PHY model were reversed engineered from the
+ * linux driver (PHYID and Diagnostics reg).
+ * TODO: Add friendly names for the register nums.
+ */
+struct qemu_phy
+{
+ uint32_t regs[32];
+
+ int link;
+
+ unsigned int (*read)(struct qemu_phy *phy, unsigned int req);
+ void (*write)(struct qemu_phy *phy, unsigned int req,
+ unsigned int data);
+};
+
+static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
+{
+ int regnum;
+ unsigned r = 0;
+
+ regnum = req & 0x1f;
+
+ switch (regnum) {
+ case 1:
+ if (!phy->link)
+ break;
+ /* MR1. */
+ /* Speeds and modes. */
+ r |= (1 << 13) | (1 << 14);
+ r |= (1 << 11) | (1 << 12);
+ r |= (1 << 5); /* Autoneg complete. */
+ r |= (1 << 3); /* Autoneg able. */
+ r |= (1 << 2); /* link. */
+ break;
+ case 5:
+ /* Link partner ability.
+ We are kind; always agree with whatever best mode
+ the guest advertises. */
+ r = 1 << 14; /* Success. */
+ /* Copy advertised modes. */
+ r |= phy->regs[4] & (15 << 5);
+ /* Autoneg support. */
+ r |= 1;
+ break;
+ case 18:
+ {
+ /* Diagnostics reg. */
+ int duplex = 0;
+ int speed_100 = 0;
+
+ if (!phy->link)
+ break;
+
+ /* Are we advertising 100 half or 100 duplex ? */
+ speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
+ speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
+
+ /* Are we advertising 10 duplex or 100 duplex ? */
+ duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
+ duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
+ r = (speed_100 << 10) | (duplex << 11);
+ }
+ break;
+
+ default:
+ r = phy->regs[regnum];
+ break;
+ }
+ D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum));
+ return r;
+}
+
+static void
+tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data)
+{
+ int regnum;
+
+ regnum = req & 0x1f;
+ D(printf("%s reg[%d] = %x\n", __func__, regnum, data));
+ switch (regnum) {
+ default:
+ phy->regs[regnum] = data;
+ break;
+ }
+}
+
+static void
+tdk_init(struct qemu_phy *phy)
+{
+ phy->regs[0] = 0x3100;
+ /* PHY Id. */
+ phy->regs[2] = 0x0300;
+ phy->regs[3] = 0xe400;
+ /* Autonegotiation advertisement reg. */
+ phy->regs[4] = 0x01E1;
+ phy->link = 1;
+
+ phy->read = tdk_read;
+ phy->write = tdk_write;
+}
+
+struct qemu_mdio
+{
+ /* bus. */
+ int mdc;
+ int mdio;
+
+ /* decoder. */
+ enum {
+ PREAMBLE,
+ SOF,
+ OPC,
+ ADDR,
+ REQ,
+ TURNAROUND,
+ DATA
+ } state;
+ unsigned int drive;
+
+ unsigned int cnt;
+ unsigned int addr;
+ unsigned int opc;
+ unsigned int req;
+ unsigned int data;
+
+ struct qemu_phy *devs[32];
+};
+
+static void
+mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
+{
+ bus->devs[addr & 0x1f] = phy;
+}
+
+#ifdef USE_THIS_DEAD_CODE
+static void
+mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
+{
+ bus->devs[addr & 0x1f] = NULL;
+}
+#endif
+
+static void mdio_read_req(struct qemu_mdio *bus)
+{
+ struct qemu_phy *phy;
+
+ phy = bus->devs[bus->addr];
+ if (phy && phy->read)
+ bus->data = phy->read(phy, bus->req);
+ else
+ bus->data = 0xffff;
+}
+
+static void mdio_write_req(struct qemu_mdio *bus)
+{
+ struct qemu_phy *phy;
+
+ phy = bus->devs[bus->addr];
+ if (phy && phy->write)
+ phy->write(phy, bus->req, bus->data);
+}
+
+static void mdio_cycle(struct qemu_mdio *bus)
+{
+ bus->cnt++;
+
+ D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n",
+ bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive));
+#if 0
+ if (bus->mdc)
+ printf("%d", bus->mdio);
+#endif
+ switch (bus->state)
+ {
+ case PREAMBLE:
+ if (bus->mdc) {
+ if (bus->cnt >= (32 * 2) && !bus->mdio) {
+ bus->cnt = 0;
+ bus->state = SOF;
+ bus->data = 0;
+ }
+ }
+ break;
+ case SOF:
+ if (bus->mdc) {
+ if (bus->mdio != 1)
+ printf("WARNING: no SOF\n");
+ if (bus->cnt == 1*2) {
+ bus->cnt = 0;
+ bus->opc = 0;
+ bus->state = OPC;
+ }
+ }
+ break;
+ case OPC:
+ if (bus->mdc) {
+ bus->opc <<= 1;
+ bus->opc |= bus->mdio & 1;
+ if (bus->cnt == 2*2) {
+ bus->cnt = 0;
+ bus->addr = 0;
+ bus->state = ADDR;
+ }
+ }
+ break;
+ case ADDR:
+ if (bus->mdc) {
+ bus->addr <<= 1;
+ bus->addr |= bus->mdio & 1;
+
+ if (bus->cnt == 5*2) {
+ bus->cnt = 0;
+ bus->req = 0;
+ bus->state = REQ;
+ }
+ }
+ break;
+ case REQ:
+ if (bus->mdc) {
+ bus->req <<= 1;
+ bus->req |= bus->mdio & 1;
+ if (bus->cnt == 5*2) {
+ bus->cnt = 0;
+ bus->state = TURNAROUND;
+ }
+ }
+ break;
+ case TURNAROUND:
+ if (bus->mdc && bus->cnt == 2*2) {
+ bus->mdio = 0;
+ bus->cnt = 0;
+
+ if (bus->opc == 2) {
+ bus->drive = 1;
+ mdio_read_req(bus);
+ bus->mdio = bus->data & 1;
+ }
+ bus->state = DATA;
+ }
+ break;
+ case DATA:
+ if (!bus->mdc) {
+ if (bus->drive) {
+ bus->mdio = !!(bus->data & (1 << 15));
+ bus->data <<= 1;
+ }
+ } else {
+ if (!bus->drive) {
+ bus->data <<= 1;
+ bus->data |= bus->mdio;
+ }
+ if (bus->cnt == 16 * 2) {
+ bus->cnt = 0;
+ bus->state = PREAMBLE;
+ if (!bus->drive)
+ mdio_write_req(bus);
+ bus->drive = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* ETRAX-FS Ethernet MAC block starts here. */
+
+#define RW_MA0_LO 0x00
+#define RW_MA0_HI 0x01
+#define RW_MA1_LO 0x02
+#define RW_MA1_HI 0x03
+#define RW_GA_LO 0x04
+#define RW_GA_HI 0x05
+#define RW_GEN_CTRL 0x06
+#define RW_REC_CTRL 0x07
+#define RW_TR_CTRL 0x08
+#define RW_CLR_ERR 0x09
+#define RW_MGM_CTRL 0x0a
+#define R_STAT 0x0b
+#define FS_ETH_MAX_REGS 0x17
+
+struct fs_eth
+{
+ NICState *nic;
+ NICConf conf;
+ int ethregs;
+
+ /* Two addrs in the filter. */
+ uint8_t macaddr[2][6];
+ uint32_t regs[FS_ETH_MAX_REGS];
+
+ struct etraxfs_dma_client *dma_out;
+ struct etraxfs_dma_client *dma_in;
+
+ /* MDIO bus. */
+ struct qemu_mdio mdio_bus;
+ unsigned int phyaddr;
+ int duplex_mismatch;
+
+ /* PHY. */
+ struct qemu_phy phy;
+};
+
+static void eth_validate_duplex(struct fs_eth *eth)
+{
+ struct qemu_phy *phy;
+ unsigned int phy_duplex;
+ unsigned int mac_duplex;
+ int new_mm = 0;
+
+ phy = eth->mdio_bus.devs[eth->phyaddr];
+ phy_duplex = !!(phy->read(phy, 18) & (1 << 11));
+ mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128);
+
+ if (mac_duplex != phy_duplex)
+ new_mm = 1;
+
+ if (eth->regs[RW_GEN_CTRL] & 1) {
+ if (new_mm != eth->duplex_mismatch) {
+ if (new_mm)
+ printf("HW: WARNING "
+ "ETH duplex mismatch MAC=%d PHY=%d\n",
+ mac_duplex, phy_duplex);
+ else
+ printf("HW: ETH duplex ok.\n");
+ }
+ eth->duplex_mismatch = new_mm;
+ }
+}
+
+static uint32_t eth_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct fs_eth *eth = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+
+ switch (addr) {
+ case R_STAT:
+ r = eth->mdio_bus.mdio & 1;
+ break;
+ default:
+ r = eth->regs[addr];
+ D(printf ("%s %x\n", __func__, addr * 4));
+ break;
+ }
+ return r;
+}
+
+static void eth_update_ma(struct fs_eth *eth, int ma)
+{
+ int reg;
+ int i = 0;
+
+ ma &= 1;
+
+ reg = RW_MA0_LO;
+ if (ma)
+ reg = RW_MA1_LO;
+
+ eth->macaddr[ma][i++] = eth->regs[reg];
+ eth->macaddr[ma][i++] = eth->regs[reg] >> 8;
+ eth->macaddr[ma][i++] = eth->regs[reg] >> 16;
+ eth->macaddr[ma][i++] = eth->regs[reg] >> 24;
+ eth->macaddr[ma][i++] = eth->regs[reg + 1];
+ eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8;
+
+ D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma,
+ eth->macaddr[ma][0], eth->macaddr[ma][1],
+ eth->macaddr[ma][2], eth->macaddr[ma][3],
+ eth->macaddr[ma][4], eth->macaddr[ma][5]));
+}
+
+static void
+eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct fs_eth *eth = opaque;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case RW_MA0_LO:
+ case RW_MA0_HI:
+ eth->regs[addr] = value;
+ eth_update_ma(eth, 0);
+ break;
+ case RW_MA1_LO:
+ case RW_MA1_HI:
+ eth->regs[addr] = value;
+ eth_update_ma(eth, 1);
+ break;
+
+ case RW_MGM_CTRL:
+ /* Attach an MDIO/PHY abstraction. */
+ if (value & 2)
+ eth->mdio_bus.mdio = value & 1;
+ if (eth->mdio_bus.mdc != (value & 4)) {
+ mdio_cycle(&eth->mdio_bus);
+ eth_validate_duplex(eth);
+ }
+ eth->mdio_bus.mdc = !!(value & 4);
+ eth->regs[addr] = value;
+ break;
+
+ case RW_REC_CTRL:
+ eth->regs[addr] = value;
+ eth_validate_duplex(eth);
+ break;
+
+ default:
+ eth->regs[addr] = value;
+ D(printf ("%s %x %x\n",
+ __func__, addr, value));
+ break;
+ }
+}
+
+/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom
+ filter dropping group addresses we have not joined. The filter has 64
+ bits (m). The has function is a simple nible xor of the group addr. */
+static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa)
+{
+ unsigned int hsh;
+ int m_individual = eth->regs[RW_REC_CTRL] & 4;
+ int match;
+
+ /* First bit on the wire of a MAC address signals multicast or
+ physical address. */
+ if (!m_individual && !(sa[0] & 1))
+ return 0;
+
+ /* Calculate the hash index for the GA registers. */
+ hsh = 0;
+ hsh ^= (*sa) & 0x3f;
+ hsh ^= ((*sa) >> 6) & 0x03;
+ ++sa;
+ hsh ^= ((*sa) << 2) & 0x03c;
+ hsh ^= ((*sa) >> 4) & 0xf;
+ ++sa;
+ hsh ^= ((*sa) << 4) & 0x30;
+ hsh ^= ((*sa) >> 2) & 0x3f;
+ ++sa;
+ hsh ^= (*sa) & 0x3f;
+ hsh ^= ((*sa) >> 6) & 0x03;
+ ++sa;
+ hsh ^= ((*sa) << 2) & 0x03c;
+ hsh ^= ((*sa) >> 4) & 0xf;
+ ++sa;
+ hsh ^= ((*sa) << 4) & 0x30;
+ hsh ^= ((*sa) >> 2) & 0x3f;
+
+ hsh &= 63;
+ if (hsh > 31)
+ match = eth->regs[RW_GA_HI] & (1 << (hsh - 32));
+ else
+ match = eth->regs[RW_GA_LO] & (1 << hsh);
+ D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh,
+ eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match));
+ return match;
+}
+
+static int eth_can_receive(VLANClientState *nc)
+{
+ return 1;
+}
+
+static ssize_t eth_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque;
+ int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
+ int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
+ int r_bcast = eth->regs[RW_REC_CTRL] & 8;
+
+ if (size < 12)
+ return -1;
+
+ D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
+ use_ma0, use_ma1, r_bcast));
+
+ /* Does the frame get through the address filters? */
+ if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6))
+ && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6))
+ && (!r_bcast || memcmp(buf, sa_bcast, 6))
+ && !eth_match_groupaddr(eth, buf))
+ return size;
+
+ /* FIXME: Find another way to pass on the fake csum. */
+ etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1);
+
+ return size;
+}
+
+static int eth_tx_push(void *opaque, unsigned char *buf, int len)
+{
+ struct fs_eth *eth = opaque;
+
+ D(printf("%s buf=%p len=%d\n", __func__, buf, len));
+ qemu_send_packet(&eth->nic->nc, buf, len);
+ return len;
+}
+
+static void eth_set_link(VLANClientState *nc)
+{
+ struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque;
+ D(printf("%s %d\n", __func__, nc->link_down));
+ eth->phy.link = !nc->link_down;
+}
+
+static CPUReadMemoryFunc * const eth_read[] = {
+ NULL, NULL,
+ &eth_readl,
+};
+
+static CPUWriteMemoryFunc * const eth_write[] = {
+ NULL, NULL,
+ &eth_writel,
+};
+
+static void eth_cleanup(VLANClientState *nc)
+{
+ struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ cpu_unregister_io_memory(eth->ethregs);
+
+ qemu_free(eth->dma_out);
+ qemu_free(eth);
+}
+
+static NetClientInfo net_etraxfs_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_receive,
+ .receive = eth_receive,
+ .cleanup = eth_cleanup,
+ .link_status_changed = eth_set_link,
+};
+
+void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr)
+{
+ struct etraxfs_dma_client *dma = NULL;
+ struct fs_eth *eth = NULL;
+
+ qemu_check_nic_model(nd, "fseth");
+
+ dma = qemu_mallocz(sizeof *dma * 2);
+ eth = qemu_mallocz(sizeof *eth);
+
+ dma[0].client.push = eth_tx_push;
+ dma[0].client.opaque = eth;
+ dma[1].client.opaque = eth;
+ dma[1].client.pull = NULL;
+
+ eth->dma_out = dma;
+ eth->dma_in = dma + 1;
+
+ /* Connect the phy. */
+ eth->phyaddr = phyaddr & 0x1f;
+ tdk_init(&eth->phy);
+ mdio_attach(&eth->mdio_bus, &eth->phy, eth->phyaddr);
+
+ eth->ethregs = cpu_register_io_memory(eth_read, eth_write, eth,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory (base, 0x5c, eth->ethregs);
+
+ memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
+ eth->conf.vlan = nd->vlan;
+ eth->conf.peer = nd->netdev;
+
+ eth->nic = qemu_new_nic(&net_etraxfs_info, &eth->conf,
+ nd->model, nd->name, eth);
+
+ return dma;
+}
diff --git a/hw/etraxfs_pic.c b/hw/etraxfs_pic.c
new file mode 100644
index 0000000..4feffda
--- /dev/null
+++ b/hw/etraxfs_pic.c
@@ -0,0 +1,169 @@
+/*
+ * QEMU ETRAX Interrupt Controller.
+ *
+ * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+//#include "pc.h"
+//#include "etraxfs.h"
+
+#define D(x)
+
+#define R_RW_MASK 0
+#define R_R_VECT 1
+#define R_R_MASKED_VECT 2
+#define R_R_NMI 3
+#define R_R_GURU 4
+#define R_MAX 5
+
+struct etrax_pic
+{
+ SysBusDevice busdev;
+ void *interrupt_vector;
+ qemu_irq parent_irq;
+ qemu_irq parent_nmi;
+ uint32_t regs[R_MAX];
+};
+
+static void pic_update(struct etrax_pic *fs)
+{
+ uint32_t vector = 0;
+ int i;
+
+ fs->regs[R_R_MASKED_VECT] = fs->regs[R_R_VECT] & fs->regs[R_RW_MASK];
+
+ /* The ETRAX interrupt controller signals interrupts to teh core
+ through an interrupt request wire and an irq vector bus. If
+ multiple interrupts are simultaneously active it chooses vector
+ 0x30 and lets the sw choose the priorities. */
+ if (fs->regs[R_R_MASKED_VECT]) {
+ uint32_t mv = fs->regs[R_R_MASKED_VECT];
+ for (i = 0; i < 31; i++) {
+ if (mv & 1) {
+ vector = 0x31 + i;
+ /* Check for multiple interrupts. */
+ if (mv > 1)
+ vector = 0x30;
+ break;
+ }
+ mv >>= 1;
+ }
+ }
+
+ if (fs->interrupt_vector) {
+ /* hack alert: ptr property */
+ *(uint32_t*)(fs->interrupt_vector) = vector;
+ }
+ qemu_set_irq(fs->parent_irq, !!vector);
+}
+
+static uint32_t pic_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct etrax_pic *fs = opaque;
+ uint32_t rval;
+
+ rval = fs->regs[addr >> 2];
+ D(printf("%s %x=%x\n", __func__, addr, rval));
+ return rval;
+}
+
+static void
+pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct etrax_pic *fs = opaque;
+ D(printf("%s addr=%x val=%x\n", __func__, addr, value));
+
+ if (addr == R_RW_MASK) {
+ fs->regs[R_RW_MASK] = value;
+ pic_update(fs);
+ }
+}
+
+static CPUReadMemoryFunc * const pic_read[] = {
+ NULL, NULL,
+ &pic_readl,
+};
+
+static CPUWriteMemoryFunc * const pic_write[] = {
+ NULL, NULL,
+ &pic_writel,
+};
+
+static void nmi_handler(void *opaque, int irq, int level)
+{
+ struct etrax_pic *fs = (void *)opaque;
+ uint32_t mask;
+
+ mask = 1 << irq;
+ if (level)
+ fs->regs[R_R_NMI] |= mask;
+ else
+ fs->regs[R_R_NMI] &= ~mask;
+
+ qemu_set_irq(fs->parent_nmi, !!fs->regs[R_R_NMI]);
+}
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ struct etrax_pic *fs = (void *)opaque;
+
+ if (irq >= 30)
+ return nmi_handler(opaque, irq, level);
+
+ irq -= 1;
+ fs->regs[R_R_VECT] &= ~(1 << irq);
+ fs->regs[R_R_VECT] |= (!!level << irq);
+ pic_update(fs);
+}
+
+static int etraxfs_pic_init(SysBusDevice *dev)
+{
+ struct etrax_pic *s = FROM_SYSBUS(typeof (*s), dev);
+ int intr_vect_regs;
+
+ qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+ sysbus_init_irq(dev, &s->parent_irq);
+ sysbus_init_irq(dev, &s->parent_nmi);
+
+ intr_vect_regs = cpu_register_io_memory(pic_read, pic_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, R_MAX * 4, intr_vect_regs);
+ return 0;
+}
+
+static SysBusDeviceInfo etraxfs_pic_info = {
+ .init = etraxfs_pic_init,
+ .qdev.name = "etraxfs,pic",
+ .qdev.size = sizeof(struct etrax_pic),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_PTR("interrupt_vector", struct etrax_pic, interrupt_vector),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void etraxfs_pic_register(void)
+{
+ sysbus_register_withprop(&etraxfs_pic_info);
+}
+
+device_init(etraxfs_pic_register)
diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c
new file mode 100644
index 0000000..2787ebd
--- /dev/null
+++ b/hw/etraxfs_ser.c
@@ -0,0 +1,220 @@
+/*
+ * QEMU ETRAX System Emulator
+ *
+ * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+
+#define D(x)
+
+#define RW_TR_CTRL (0x00 / 4)
+#define RW_TR_DMA_EN (0x04 / 4)
+#define RW_REC_CTRL (0x08 / 4)
+#define RW_DOUT (0x1c / 4)
+#define RS_STAT_DIN (0x20 / 4)
+#define R_STAT_DIN (0x24 / 4)
+#define RW_INTR_MASK (0x2c / 4)
+#define RW_ACK_INTR (0x30 / 4)
+#define R_INTR (0x34 / 4)
+#define R_MASKED_INTR (0x38 / 4)
+#define R_MAX (0x3c / 4)
+
+#define STAT_DAV 16
+#define STAT_TR_IDLE 22
+#define STAT_TR_RDY 24
+
+struct etrax_serial
+{
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ int pending_tx;
+
+ uint8_t rx_fifo[16];
+ unsigned int rx_fifo_pos;
+ unsigned int rx_fifo_len;
+
+ /* Control registers. */
+ uint32_t regs[R_MAX];
+};
+
+static void ser_update_irq(struct etrax_serial *s)
+{
+
+ if (s->rx_fifo_len) {
+ s->regs[R_INTR] |= 8;
+ } else {
+ s->regs[R_INTR] &= ~8;
+ }
+
+ s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK];
+ qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]);
+}
+
+static uint32_t ser_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct etrax_serial *s = opaque;
+ D(CPUState *env = s->env);
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_STAT_DIN:
+ r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15];
+ if (s->rx_fifo_len) {
+ r |= 1 << STAT_DAV;
+ }
+ r |= 1 << STAT_TR_RDY;
+ r |= 1 << STAT_TR_IDLE;
+ break;
+ case RS_STAT_DIN:
+ r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15];
+ if (s->rx_fifo_len) {
+ r |= 1 << STAT_DAV;
+ s->rx_fifo_len--;
+ }
+ r |= 1 << STAT_TR_RDY;
+ r |= 1 << STAT_TR_IDLE;
+ break;
+ default:
+ r = s->regs[addr];
+ D(printf ("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r));
+ break;
+ }
+ return r;
+}
+
+static void
+ser_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct etrax_serial *s = opaque;
+ unsigned char ch = value;
+ D(CPUState *env = s->env);
+
+ D(printf ("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value));
+ addr >>= 2;
+ switch (addr)
+ {
+ case RW_DOUT:
+ qemu_chr_write(s->chr, &ch, 1);
+ s->regs[R_INTR] |= 3;
+ s->pending_tx = 1;
+ s->regs[addr] = value;
+ break;
+ case RW_ACK_INTR:
+ if (s->pending_tx) {
+ value &= ~1;
+ s->pending_tx = 0;
+ D(printf("fixedup value=%x r_intr=%x\n", value, s->regs[R_INTR]));
+ }
+ s->regs[addr] = value;
+ s->regs[R_INTR] &= ~value;
+ D(printf("r_intr=%x\n", s->regs[R_INTR]));
+ break;
+ default:
+ s->regs[addr] = value;
+ break;
+ }
+ ser_update_irq(s);
+}
+
+static CPUReadMemoryFunc * const ser_read[] = {
+ NULL, NULL,
+ &ser_readl,
+};
+
+static CPUWriteMemoryFunc * const ser_write[] = {
+ NULL, NULL,
+ &ser_writel,
+};
+
+static void serial_receive(void *opaque, const uint8_t *buf, int size)
+{
+ struct etrax_serial *s = opaque;
+ int i;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= 16) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+
+ for (i = 0; i < size; i++) {
+ s->rx_fifo[s->rx_fifo_pos] = buf[i];
+ s->rx_fifo_pos++;
+ s->rx_fifo_pos &= 15;
+ s->rx_fifo_len++;
+ }
+
+ ser_update_irq(s);
+}
+
+static int serial_can_receive(void *opaque)
+{
+ struct etrax_serial *s = opaque;
+ int r;
+
+ /* Is the receiver enabled? */
+ if (!(s->regs[RW_REC_CTRL] & (1 << 3))) {
+ return 0;
+ }
+
+ r = sizeof(s->rx_fifo) - s->rx_fifo_len;
+ return r;
+}
+
+static void serial_event(void *opaque, int event)
+{
+
+}
+
+static int etraxfs_ser_init(SysBusDevice *dev)
+{
+ struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev);
+ int ser_regs;
+
+ /* transmitter begins ready and idle. */
+ s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY);
+ s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE);
+
+ sysbus_init_irq(dev, &s->irq);
+ ser_regs = cpu_register_io_memory(ser_read, ser_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, R_MAX * 4, ser_regs);
+ s->chr = qdev_init_chardev(&dev->qdev);
+ if (s->chr)
+ qemu_chr_add_handlers(s->chr,
+ serial_can_receive, serial_receive,
+ serial_event, s);
+ return 0;
+}
+
+static void etraxfs_serial_register(void)
+{
+ sysbus_register_dev("etraxfs,serial", sizeof (struct etrax_serial),
+ etraxfs_ser_init);
+}
+
+device_init(etraxfs_serial_register)
diff --git a/hw/etraxfs_timer.c b/hw/etraxfs_timer.c
new file mode 100644
index 0000000..133741b
--- /dev/null
+++ b/hw/etraxfs_timer.c
@@ -0,0 +1,336 @@
+/*
+ * QEMU ETRAX Timers
+ *
+ * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+
+#define D(x)
+
+#define RW_TMR0_DIV 0x00
+#define R_TMR0_DATA 0x04
+#define RW_TMR0_CTRL 0x08
+#define RW_TMR1_DIV 0x10
+#define R_TMR1_DATA 0x14
+#define RW_TMR1_CTRL 0x18
+#define R_TIME 0x38
+#define RW_WD_CTRL 0x40
+#define R_WD_STAT 0x44
+#define RW_INTR_MASK 0x48
+#define RW_ACK_INTR 0x4c
+#define R_INTR 0x50
+#define R_MASKED_INTR 0x54
+
+struct etrax_timer {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq nmi;
+
+ QEMUBH *bh_t0;
+ QEMUBH *bh_t1;
+ QEMUBH *bh_wd;
+ ptimer_state *ptimer_t0;
+ ptimer_state *ptimer_t1;
+ ptimer_state *ptimer_wd;
+
+ int wd_hits;
+
+ /* Control registers. */
+ uint32_t rw_tmr0_div;
+ uint32_t r_tmr0_data;
+ uint32_t rw_tmr0_ctrl;
+
+ uint32_t rw_tmr1_div;
+ uint32_t r_tmr1_data;
+ uint32_t rw_tmr1_ctrl;
+
+ uint32_t rw_wd_ctrl;
+
+ uint32_t rw_intr_mask;
+ uint32_t rw_ack_intr;
+ uint32_t r_intr;
+ uint32_t r_masked_intr;
+};
+
+static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct etrax_timer *t = opaque;
+ uint32_t r = 0;
+
+ switch (addr) {
+ case R_TMR0_DATA:
+ r = ptimer_get_count(t->ptimer_t0);
+ break;
+ case R_TMR1_DATA:
+ r = ptimer_get_count(t->ptimer_t1);
+ break;
+ case R_TIME:
+ r = qemu_get_clock(vm_clock) / 10;
+ break;
+ case RW_INTR_MASK:
+ r = t->rw_intr_mask;
+ break;
+ case R_MASKED_INTR:
+ r = t->r_intr & t->rw_intr_mask;
+ break;
+ default:
+ D(printf ("%s %x\n", __func__, addr));
+ break;
+ }
+ return r;
+}
+
+static void update_ctrl(struct etrax_timer *t, int tnum)
+{
+ unsigned int op;
+ unsigned int freq;
+ unsigned int freq_hz;
+ unsigned int div;
+ uint32_t ctrl;
+
+ ptimer_state *timer;
+
+ if (tnum == 0) {
+ ctrl = t->rw_tmr0_ctrl;
+ div = t->rw_tmr0_div;
+ timer = t->ptimer_t0;
+ } else {
+ ctrl = t->rw_tmr1_ctrl;
+ div = t->rw_tmr1_div;
+ timer = t->ptimer_t1;
+ }
+
+
+ op = ctrl & 3;
+ freq = ctrl >> 2;
+ freq_hz = 32000000;
+
+ switch (freq)
+ {
+ case 0:
+ case 1:
+ D(printf ("extern or disabled timer clock?\n"));
+ break;
+ case 4: freq_hz = 29493000; break;
+ case 5: freq_hz = 32000000; break;
+ case 6: freq_hz = 32768000; break;
+ case 7: freq_hz = 100000000; break;
+ default:
+ abort();
+ break;
+ }
+
+ D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
+ ptimer_set_freq(timer, freq_hz);
+ ptimer_set_limit(timer, div, 0);
+
+ switch (op)
+ {
+ case 0:
+ /* Load. */
+ ptimer_set_limit(timer, div, 1);
+ break;
+ case 1:
+ /* Hold. */
+ ptimer_stop(timer);
+ break;
+ case 2:
+ /* Run. */
+ ptimer_run(timer, 0);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+static void timer_update_irq(struct etrax_timer *t)
+{
+ t->r_intr &= ~(t->rw_ack_intr);
+ t->r_masked_intr = t->r_intr & t->rw_intr_mask;
+
+ D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
+ qemu_set_irq(t->irq, !!t->r_masked_intr);
+}
+
+static void timer0_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 1;
+ timer_update_irq(t);
+}
+
+static void timer1_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 2;
+ timer_update_irq(t);
+}
+
+static void watchdog_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ if (t->wd_hits == 0) {
+ /* real hw gives a single tick before reseting but we are
+ a bit friendlier to compensate for our slower execution. */
+ ptimer_set_count(t->ptimer_wd, 10);
+ ptimer_run(t->ptimer_wd, 1);
+ qemu_irq_raise(t->nmi);
+ }
+ else
+ qemu_system_reset_request();
+
+ t->wd_hits++;
+}
+
+static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value)
+{
+ unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
+ unsigned int wd_key = t->rw_wd_ctrl >> 9;
+ unsigned int wd_cnt = t->rw_wd_ctrl & 511;
+ unsigned int new_key = value >> 9 & ((1 << 7) - 1);
+ unsigned int new_cmd = (value >> 8) & 1;
+
+ /* If the watchdog is enabled, they written key must match the
+ complement of the previous. */
+ wd_key = ~wd_key & ((1 << 7) - 1);
+
+ if (wd_en && wd_key != new_key)
+ return;
+
+ D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n",
+ wd_en, new_key, wd_key, new_cmd, wd_cnt));
+
+ if (t->wd_hits)
+ qemu_irq_lower(t->nmi);
+
+ t->wd_hits = 0;
+
+ ptimer_set_freq(t->ptimer_wd, 760);
+ if (wd_cnt == 0)
+ wd_cnt = 256;
+ ptimer_set_count(t->ptimer_wd, wd_cnt);
+ if (new_cmd)
+ ptimer_run(t->ptimer_wd, 1);
+ else
+ ptimer_stop(t->ptimer_wd);
+
+ t->rw_wd_ctrl = value;
+}
+
+static void
+timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct etrax_timer *t = opaque;
+
+ switch (addr)
+ {
+ case RW_TMR0_DIV:
+ t->rw_tmr0_div = value;
+ break;
+ case RW_TMR0_CTRL:
+ D(printf ("RW_TMR0_CTRL=%x\n", value));
+ t->rw_tmr0_ctrl = value;
+ update_ctrl(t, 0);
+ break;
+ case RW_TMR1_DIV:
+ t->rw_tmr1_div = value;
+ break;
+ case RW_TMR1_CTRL:
+ D(printf ("RW_TMR1_CTRL=%x\n", value));
+ t->rw_tmr1_ctrl = value;
+ update_ctrl(t, 1);
+ break;
+ case RW_INTR_MASK:
+ D(printf ("RW_INTR_MASK=%x\n", value));
+ t->rw_intr_mask = value;
+ timer_update_irq(t);
+ break;
+ case RW_WD_CTRL:
+ timer_watchdog_update(t, value);
+ break;
+ case RW_ACK_INTR:
+ t->rw_ack_intr = value;
+ timer_update_irq(t);
+ t->rw_ack_intr = 0;
+ break;
+ default:
+ printf ("%s " TARGET_FMT_plx " %x\n",
+ __func__, addr, value);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const timer_read[] = {
+ NULL, NULL,
+ &timer_readl,
+};
+
+static CPUWriteMemoryFunc * const timer_write[] = {
+ NULL, NULL,
+ &timer_writel,
+};
+
+static void etraxfs_timer_reset(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+
+ ptimer_stop(t->ptimer_t0);
+ ptimer_stop(t->ptimer_t1);
+ ptimer_stop(t->ptimer_wd);
+ t->rw_wd_ctrl = 0;
+ t->r_intr = 0;
+ t->rw_intr_mask = 0;
+ qemu_irq_lower(t->irq);
+}
+
+static int etraxfs_timer_init(SysBusDevice *dev)
+{
+ struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev);
+ int timer_regs;
+
+ t->bh_t0 = qemu_bh_new(timer0_hit, t);
+ t->bh_t1 = qemu_bh_new(timer1_hit, t);
+ t->bh_wd = qemu_bh_new(watchdog_hit, t);
+ t->ptimer_t0 = ptimer_init(t->bh_t0);
+ t->ptimer_t1 = ptimer_init(t->bh_t1);
+ t->ptimer_wd = ptimer_init(t->bh_wd);
+
+ sysbus_init_irq(dev, &t->irq);
+ sysbus_init_irq(dev, &t->nmi);
+
+ timer_regs = cpu_register_io_memory(timer_read, timer_write, t,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x5c, timer_regs);
+
+ qemu_register_reset(etraxfs_timer_reset, t);
+ return 0;
+}
+
+static void etraxfs_timer_register(void)
+{
+ sysbus_register_dev("etraxfs,timer", sizeof (struct etrax_timer),
+ etraxfs_timer_init);
+}
+
+device_init(etraxfs_timer_register)
diff --git a/hw/event_notifier.c b/hw/event_notifier.c
new file mode 100644
index 0000000..13f3656
--- /dev/null
+++ b/hw/event_notifier.c
@@ -0,0 +1,62 @@
+/*
+ * event notifier support
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Authors:
+ * Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "hw.h"
+#include "event_notifier.h"
+#ifdef CONFIG_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+int event_notifier_init(EventNotifier *e, int active)
+{
+#ifdef CONFIG_EVENTFD
+ int fd = eventfd(!!active, EFD_NONBLOCK | EFD_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+ e->fd = fd;
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+void event_notifier_cleanup(EventNotifier *e)
+{
+ close(e->fd);
+}
+
+int event_notifier_get_fd(EventNotifier *e)
+{
+ return e->fd;
+}
+
+int event_notifier_test_and_clear(EventNotifier *e)
+{
+ uint64_t value;
+ int r = read(e->fd, &value, sizeof(value));
+ return r == sizeof(value);
+}
+
+int event_notifier_test(EventNotifier *e)
+{
+ uint64_t value;
+ int r = read(e->fd, &value, sizeof(value));
+ if (r == sizeof(value)) {
+ /* restore previous value. */
+ int s = write(e->fd, &value, sizeof(value));
+ /* never blocks because we use EFD_SEMAPHORE.
+ * If we didn't we'd get EAGAIN on overflow
+ * and we'd have to write code to ignore it. */
+ assert(s == sizeof(value));
+ }
+ return r == sizeof(value);
+}
diff --git a/hw/event_notifier.h b/hw/event_notifier.h
new file mode 100644
index 0000000..24117ea
--- /dev/null
+++ b/hw/event_notifier.h
@@ -0,0 +1,16 @@
+#ifndef QEMU_EVENT_NOTIFIER_H
+#define QEMU_EVENT_NOTIFIER_H
+
+#include "qemu-common.h"
+
+struct EventNotifier {
+ int fd;
+};
+
+int event_notifier_init(EventNotifier *, int active);
+void event_notifier_cleanup(EventNotifier *);
+int event_notifier_get_fd(EventNotifier *);
+int event_notifier_test_and_clear(EventNotifier *);
+int event_notifier_test(EventNotifier *);
+
+#endif
diff --git a/hw/extboot.c b/hw/extboot.c
new file mode 100644
index 0000000..8ada21b
--- /dev/null
+++ b/hw/extboot.c
@@ -0,0 +1,123 @@
+/*
+ * Extended boot option ROM support.
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "block.h"
+
+/* Extended Boot ROM suport */
+
+union extboot_cmd
+{
+ uint16_t type;
+ struct {
+ uint16_t type;
+ uint16_t cylinders;
+ uint16_t heads;
+ uint16_t sectors;
+ uint64_t nb_sectors;
+ } query_geometry;
+ struct {
+ uint16_t type;
+ uint16_t nb_sectors;
+ uint16_t segment;
+ uint16_t offset;
+ uint64_t sector;
+ } xfer;
+};
+
+static void get_translated_chs(BlockDriverState *bs, int *c, int *h, int *s)
+{
+ bdrv_get_geometry_hint(bs, c, h, s);
+
+ if (*c <= 1024) {
+ *c >>= 0;
+ *h <<= 0;
+ } else if (*c <= 2048) {
+ *c >>= 1;
+ *h <<= 1;
+ } else if (*c <= 4096) {
+ *c >>= 2;
+ *h <<= 2;
+ } else if (*c <= 8192) {
+ *c >>= 3;
+ *h <<= 3;
+ } else {
+ *c >>= 4;
+ *h <<= 4;
+ }
+
+ /* what is the correct algorithm for this?? */
+ if (*h == 256) {
+ *h = 255;
+ *c = *c + 1;
+ }
+}
+
+static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value)
+{
+ union extboot_cmd cmd;
+ BlockDriverState *bs = opaque;
+ int cylinders, heads, sectors, err;
+ uint64_t nb_sectors;
+ target_phys_addr_t pa = 0;
+ int blen = 0;
+ void *buf = NULL;
+
+ cpu_physical_memory_read((value & 0xFFFF) << 4, (uint8_t *)&cmd,
+ sizeof(cmd));
+
+ if (cmd.type == 0x01 || cmd.type == 0x02) {
+ pa = cmd.xfer.segment * 16 + cmd.xfer.offset;
+ blen = cmd.xfer.nb_sectors * 512;
+ buf = qemu_memalign(512, blen);
+ }
+
+ switch (cmd.type) {
+ case 0x00:
+ get_translated_chs(bs, &cylinders, &heads, &sectors);
+ bdrv_get_geometry(bs, &nb_sectors);
+ cmd.query_geometry.cylinders = cylinders;
+ cmd.query_geometry.heads = heads;
+ cmd.query_geometry.sectors = sectors;
+ cmd.query_geometry.nb_sectors = nb_sectors;
+ break;
+ case 0x01:
+ err = bdrv_read(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
+ if (err)
+ printf("Read failed\n");
+
+ cpu_physical_memory_write(pa, buf, blen);
+
+ break;
+ case 0x02:
+ cpu_physical_memory_read(pa, buf, blen);
+
+ err = bdrv_write(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
+ if (err)
+ printf("Write failed\n");
+
+ break;
+ }
+
+ cpu_physical_memory_write((value & 0xFFFF) << 4, (uint8_t *)&cmd,
+ sizeof(cmd));
+ if (buf)
+ qemu_free(buf);
+}
+
+void extboot_init(BlockDriverState *bs)
+{
+ register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs);
+}
diff --git a/hw/fdc.c b/hw/fdc.c
new file mode 100644
index 0000000..4bbcc47
--- /dev/null
+++ b/hw/fdc.c
@@ -0,0 +1,2110 @@
+/*
+ * QEMU Floppy disk emulator (Intel 82078)
+ *
+ * Copyright (c) 2003, 2007 Jocelyn Mayer
+ * Copyright (c) 2008 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * The controller is used in Sun4m systems in a slightly different
+ * way. There are changes in DOR register and DMA is not available.
+ */
+
+#include "hw.h"
+#include "fdc.h"
+#include "qemu-error.h"
+#include "qemu-timer.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "qdev-addr.h"
+#include "blockdev.h"
+#include "sysemu.h"
+
+/********************************************************/
+/* debug Floppy devices */
+//#define DEBUG_FLOPPY
+
+#ifdef DEBUG_FLOPPY
+#define FLOPPY_DPRINTF(fmt, ...) \
+ do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define FLOPPY_DPRINTF(fmt, ...)
+#endif
+
+#define FLOPPY_ERROR(fmt, ...) \
+ do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+
+/********************************************************/
+/* Floppy drive emulation */
+
+#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
+#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
+
+/* Will always be a fixed parameter for us */
+#define FD_SECTOR_LEN 512
+#define FD_SECTOR_SC 2 /* Sector size code */
+#define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */
+
+/* Floppy disk drive emulation */
+typedef enum FDiskType {
+ FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */
+ FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */
+ FDRIVE_DISK_720 = 0x03, /* 720 kB disk */
+ FDRIVE_DISK_USER = 0x04, /* User defined geometry */
+ FDRIVE_DISK_NONE = 0x05, /* No disk */
+} FDiskType;
+
+typedef enum FDriveType {
+ FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
+ FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
+ FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
+ FDRIVE_DRV_NONE = 0x03, /* No drive connected */
+} FDriveType;
+
+typedef enum FDiskFlags {
+ FDISK_DBL_SIDES = 0x01,
+} FDiskFlags;
+
+typedef struct FDrive {
+ BlockDriverState *bs;
+ /* Drive status */
+ FDriveType drive;
+ uint8_t perpendicular; /* 2.88 MB access mode */
+ /* Position */
+ uint8_t head;
+ uint8_t track;
+ uint8_t sect;
+ /* Media */
+ FDiskFlags flags;
+ uint8_t last_sect; /* Nb sector per track */
+ uint8_t max_track; /* Nb of tracks */
+ uint16_t bps; /* Bytes per sector */
+ uint8_t ro; /* Is read-only */
+} FDrive;
+
+static void fd_init(FDrive *drv)
+{
+ /* Drive */
+ drv->drive = FDRIVE_DRV_NONE;
+ drv->perpendicular = 0;
+ /* Disk */
+ drv->last_sect = 0;
+ drv->max_track = 0;
+}
+
+static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
+ uint8_t last_sect)
+{
+ return (((track * 2) + head) * last_sect) + sect - 1;
+}
+
+/* Returns current position, in sectors, for given drive */
+static int fd_sector(FDrive *drv)
+{
+ return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect);
+}
+
+/* Seek to a new position:
+ * returns 0 if already on right track
+ * returns 1 if track changed
+ * returns 2 if track is invalid
+ * returns 3 if sector is invalid
+ * returns 4 if seek is disabled
+ */
+static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
+ int enable_seek)
+{
+ uint32_t sector;
+ int ret;
+
+ if (track > drv->max_track ||
+ (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
+ FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
+ head, track, sect, 1,
+ (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
+ drv->max_track, drv->last_sect);
+ return 2;
+ }
+ if (sect > drv->last_sect) {
+ FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
+ head, track, sect, 1,
+ (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
+ drv->max_track, drv->last_sect);
+ return 3;
+ }
+ sector = fd_sector_calc(head, track, sect, drv->last_sect);
+ ret = 0;
+ if (sector != fd_sector(drv)) {
+#if 0
+ if (!enable_seek) {
+ FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
+ head, track, sect, 1, drv->max_track, drv->last_sect);
+ return 4;
+ }
+#endif
+ drv->head = head;
+ if (drv->track != track)
+ ret = 1;
+ drv->track = track;
+ drv->sect = sect;
+ }
+
+ return ret;
+}
+
+/* Set drive back to track 0 */
+static void fd_recalibrate(FDrive *drv)
+{
+ FLOPPY_DPRINTF("recalibrate\n");
+ drv->head = 0;
+ drv->track = 0;
+ drv->sect = 1;
+}
+
+/* Recognize floppy formats */
+typedef struct FDFormat {
+ FDriveType drive;
+ FDiskType disk;
+ uint8_t last_sect;
+ uint8_t max_track;
+ uint8_t max_head;
+ const char *str;
+} FDFormat;
+
+static const FDFormat fd_formats[] = {
+ /* First entry is default format */
+ /* 1.44 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", },
+ /* 2.88 MB 3"1/2 floppy disks */
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", },
+ { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", },
+ /* 720 kB 3"1/2 floppy disks */
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", },
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", },
+ /* 1.2 MB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", },
+ /* 720 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", },
+ /* 360 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", },
+ /* 320 kB 5"1/4 floppy disks */
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", },
+ { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", },
+ /* 360 kB must match 5"1/4 better than 3"1/2... */
+ { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", },
+ /* end */
+ { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },
+};
+
+/* Revalidate a disk drive after a disk change */
+static void fd_revalidate(FDrive *drv)
+{
+ const FDFormat *parse;
+ uint64_t nb_sectors, size;
+ int i, first_match, match;
+ int nb_heads, max_track, last_sect, ro;
+
+ FLOPPY_DPRINTF("revalidate\n");
+ if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
+ ro = bdrv_is_read_only(drv->bs);
+ bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
+ if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
+ FLOPPY_DPRINTF("User defined disk (%d %d %d)",
+ nb_heads - 1, max_track, last_sect);
+ } else {
+ bdrv_get_geometry(drv->bs, &nb_sectors);
+ match = -1;
+ first_match = -1;
+ for (i = 0;; i++) {
+ parse = &fd_formats[i];
+ if (parse->drive == FDRIVE_DRV_NONE)
+ break;
+ if (drv->drive == parse->drive ||
+ drv->drive == FDRIVE_DRV_NONE) {
+ size = (parse->max_head + 1) * parse->max_track *
+ parse->last_sect;
+ if (nb_sectors == size) {
+ match = i;
+ break;
+ }
+ if (first_match == -1)
+ first_match = i;
+ }
+ }
+ if (match == -1) {
+ if (first_match == -1)
+ match = 1;
+ else
+ match = first_match;
+ parse = &fd_formats[match];
+ }
+ nb_heads = parse->max_head + 1;
+ max_track = parse->max_track;
+ last_sect = parse->last_sect;
+ drv->drive = parse->drive;
+ FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
+ nb_heads, max_track, last_sect, ro ? "ro" : "rw");
+ }
+ if (nb_heads == 1) {
+ drv->flags &= ~FDISK_DBL_SIDES;
+ } else {
+ drv->flags |= FDISK_DBL_SIDES;
+ }
+ drv->max_track = max_track;
+ drv->last_sect = last_sect;
+ drv->ro = ro;
+ } else {
+ FLOPPY_DPRINTF("No disk in drive\n");
+ drv->last_sect = 0;
+ drv->max_track = 0;
+ drv->flags &= ~FDISK_DBL_SIDES;
+ }
+}
+
+/********************************************************/
+/* Intel 82078 floppy disk controller emulation */
+
+static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
+static void fdctrl_reset_fifo(FDCtrl *fdctrl);
+static int fdctrl_transfer_handler (void *opaque, int nchan,
+ int dma_pos, int dma_len);
+static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0);
+
+static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl);
+static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl);
+static uint32_t fdctrl_read_dor(FDCtrl *fdctrl);
+static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_tape(FDCtrl *fdctrl);
+static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl);
+static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_data(FDCtrl *fdctrl);
+static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_dir(FDCtrl *fdctrl);
+
+enum {
+ FD_DIR_WRITE = 0,
+ FD_DIR_READ = 1,
+ FD_DIR_SCANE = 2,
+ FD_DIR_SCANL = 3,
+ FD_DIR_SCANH = 4,
+};
+
+enum {
+ FD_STATE_MULTI = 0x01, /* multi track flag */
+ FD_STATE_FORMAT = 0x02, /* format flag */
+ FD_STATE_SEEK = 0x04, /* seek flag */
+};
+
+enum {
+ FD_REG_SRA = 0x00,
+ FD_REG_SRB = 0x01,
+ FD_REG_DOR = 0x02,
+ FD_REG_TDR = 0x03,
+ FD_REG_MSR = 0x04,
+ FD_REG_DSR = 0x04,
+ FD_REG_FIFO = 0x05,
+ FD_REG_DIR = 0x07,
+};
+
+enum {
+ FD_CMD_READ_TRACK = 0x02,
+ FD_CMD_SPECIFY = 0x03,
+ FD_CMD_SENSE_DRIVE_STATUS = 0x04,
+ FD_CMD_WRITE = 0x05,
+ FD_CMD_READ = 0x06,
+ FD_CMD_RECALIBRATE = 0x07,
+ FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
+ FD_CMD_WRITE_DELETED = 0x09,
+ FD_CMD_READ_ID = 0x0a,
+ FD_CMD_READ_DELETED = 0x0c,
+ FD_CMD_FORMAT_TRACK = 0x0d,
+ FD_CMD_DUMPREG = 0x0e,
+ FD_CMD_SEEK = 0x0f,
+ FD_CMD_VERSION = 0x10,
+ FD_CMD_SCAN_EQUAL = 0x11,
+ FD_CMD_PERPENDICULAR_MODE = 0x12,
+ FD_CMD_CONFIGURE = 0x13,
+ FD_CMD_LOCK = 0x14,
+ FD_CMD_VERIFY = 0x16,
+ FD_CMD_POWERDOWN_MODE = 0x17,
+ FD_CMD_PART_ID = 0x18,
+ FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
+ FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
+ FD_CMD_SAVE = 0x2e,
+ FD_CMD_OPTION = 0x33,
+ FD_CMD_RESTORE = 0x4e,
+ FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
+ FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
+ FD_CMD_FORMAT_AND_WRITE = 0xcd,
+ FD_CMD_RELATIVE_SEEK_IN = 0xcf,
+};
+
+enum {
+ FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */
+ FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */
+ FD_CONFIG_POLL = 0x10, /* Poll enabled */
+ FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */
+ FD_CONFIG_EIS = 0x40, /* No implied seeks */
+};
+
+enum {
+ FD_SR0_EQPMT = 0x10,
+ FD_SR0_SEEK = 0x20,
+ FD_SR0_ABNTERM = 0x40,
+ FD_SR0_INVCMD = 0x80,
+ FD_SR0_RDYCHG = 0xc0,
+};
+
+enum {
+ FD_SR1_EC = 0x80, /* End of cylinder */
+};
+
+enum {
+ FD_SR2_SNS = 0x04, /* Scan not satisfied */
+ FD_SR2_SEH = 0x08, /* Scan equal hit */
+};
+
+enum {
+ FD_SRA_DIR = 0x01,
+ FD_SRA_nWP = 0x02,
+ FD_SRA_nINDX = 0x04,
+ FD_SRA_HDSEL = 0x08,
+ FD_SRA_nTRK0 = 0x10,
+ FD_SRA_STEP = 0x20,
+ FD_SRA_nDRV2 = 0x40,
+ FD_SRA_INTPEND = 0x80,
+};
+
+enum {
+ FD_SRB_MTR0 = 0x01,
+ FD_SRB_MTR1 = 0x02,
+ FD_SRB_WGATE = 0x04,
+ FD_SRB_RDATA = 0x08,
+ FD_SRB_WDATA = 0x10,
+ FD_SRB_DR0 = 0x20,
+};
+
+enum {
+#if MAX_FD == 4
+ FD_DOR_SELMASK = 0x03,
+#else
+ FD_DOR_SELMASK = 0x01,
+#endif
+ FD_DOR_nRESET = 0x04,
+ FD_DOR_DMAEN = 0x08,
+ FD_DOR_MOTEN0 = 0x10,
+ FD_DOR_MOTEN1 = 0x20,
+ FD_DOR_MOTEN2 = 0x40,
+ FD_DOR_MOTEN3 = 0x80,
+};
+
+enum {
+#if MAX_FD == 4
+ FD_TDR_BOOTSEL = 0x0c,
+#else
+ FD_TDR_BOOTSEL = 0x04,
+#endif
+};
+
+enum {
+ FD_DSR_DRATEMASK= 0x03,
+ FD_DSR_PWRDOWN = 0x40,
+ FD_DSR_SWRESET = 0x80,
+};
+
+enum {
+ FD_MSR_DRV0BUSY = 0x01,
+ FD_MSR_DRV1BUSY = 0x02,
+ FD_MSR_DRV2BUSY = 0x04,
+ FD_MSR_DRV3BUSY = 0x08,
+ FD_MSR_CMDBUSY = 0x10,
+ FD_MSR_NONDMA = 0x20,
+ FD_MSR_DIO = 0x40,
+ FD_MSR_RQM = 0x80,
+};
+
+enum {
+ FD_DIR_DSKCHG = 0x80,
+};
+
+#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
+#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
+#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
+
+struct FDCtrl {
+ /* Controller's identification */
+ uint8_t version;
+ /* HW */
+ qemu_irq irq;
+ int dma_chann;
+ /* Controller state */
+ QEMUTimer *result_timer;
+ uint8_t sra;
+ uint8_t srb;
+ uint8_t dor;
+ uint8_t dor_vmstate; /* only used as temp during vmstate */
+ uint8_t tdr;
+ uint8_t dsr;
+ uint8_t msr;
+ uint8_t cur_drv;
+ uint8_t status0;
+ uint8_t status1;
+ uint8_t status2;
+ /* Command FIFO */
+ uint8_t *fifo;
+ int32_t fifo_size;
+ uint32_t data_pos;
+ uint32_t data_len;
+ uint8_t data_state;
+ uint8_t data_dir;
+ uint8_t eot; /* last wanted sector */
+ /* States kept only to be returned back */
+ /* Timers state */
+ uint8_t timer0;
+ uint8_t timer1;
+ /* precompensation */
+ uint8_t precomp_trk;
+ uint8_t config;
+ uint8_t lock;
+ /* Power down config (also with status regB access mode */
+ uint8_t pwrd;
+ /* Sun4m quirks? */
+ int sun4m;
+ /* Floppy drives */
+ uint8_t num_floppies;
+ FDrive drives[MAX_FD];
+ int reset_sensei;
+};
+
+typedef struct FDCtrlSysBus {
+ SysBusDevice busdev;
+ struct FDCtrl state;
+} FDCtrlSysBus;
+
+typedef struct FDCtrlISABus {
+ ISADevice busdev;
+ struct FDCtrl state;
+ int32_t bootindexA;
+ int32_t bootindexB;
+} FDCtrlISABus;
+
+static uint32_t fdctrl_read (void *opaque, uint32_t reg)
+{
+ FDCtrl *fdctrl = opaque;
+ uint32_t retval;
+
+ switch (reg) {
+ case FD_REG_SRA:
+ retval = fdctrl_read_statusA(fdctrl);
+ break;
+ case FD_REG_SRB:
+ retval = fdctrl_read_statusB(fdctrl);
+ break;
+ case FD_REG_DOR:
+ retval = fdctrl_read_dor(fdctrl);
+ break;
+ case FD_REG_TDR:
+ retval = fdctrl_read_tape(fdctrl);
+ break;
+ case FD_REG_MSR:
+ retval = fdctrl_read_main_status(fdctrl);
+ break;
+ case FD_REG_FIFO:
+ retval = fdctrl_read_data(fdctrl);
+ break;
+ case FD_REG_DIR:
+ retval = fdctrl_read_dir(fdctrl);
+ break;
+ default:
+ retval = (uint32_t)(-1);
+ break;
+ }
+ FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
+
+ return retval;
+}
+
+static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
+{
+ FDCtrl *fdctrl = opaque;
+
+ FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
+
+ switch (reg) {
+ case FD_REG_DOR:
+ fdctrl_write_dor(fdctrl, value);
+ break;
+ case FD_REG_TDR:
+ fdctrl_write_tape(fdctrl, value);
+ break;
+ case FD_REG_DSR:
+ fdctrl_write_rate(fdctrl, value);
+ break;
+ case FD_REG_FIFO:
+ fdctrl_write_data(fdctrl, value);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t fdctrl_read_port (void *opaque, uint32_t reg)
+{
+ return fdctrl_read(opaque, reg & 7);
+}
+
+static void fdctrl_write_port (void *opaque, uint32_t reg, uint32_t value)
+{
+ fdctrl_write(opaque, reg & 7, value);
+}
+
+static uint32_t fdctrl_read_mem (void *opaque, target_phys_addr_t reg)
+{
+ return fdctrl_read(opaque, (uint32_t)reg);
+}
+
+static void fdctrl_write_mem (void *opaque,
+ target_phys_addr_t reg, uint32_t value)
+{
+ fdctrl_write(opaque, (uint32_t)reg, value);
+}
+
+static CPUReadMemoryFunc * const fdctrl_mem_read[3] = {
+ fdctrl_read_mem,
+ fdctrl_read_mem,
+ fdctrl_read_mem,
+};
+
+static CPUWriteMemoryFunc * const fdctrl_mem_write[3] = {
+ fdctrl_write_mem,
+ fdctrl_write_mem,
+ fdctrl_write_mem,
+};
+
+static CPUReadMemoryFunc * const fdctrl_mem_read_strict[3] = {
+ fdctrl_read_mem,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const fdctrl_mem_write_strict[3] = {
+ fdctrl_write_mem,
+ NULL,
+ NULL,
+};
+
+static const VMStateDescription vmstate_fdrive = {
+ .name = "fdrive",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(head, FDrive),
+ VMSTATE_UINT8(track, FDrive),
+ VMSTATE_UINT8(sect, FDrive),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void fdc_pre_save(void *opaque)
+{
+ FDCtrl *s = opaque;
+
+ s->dor_vmstate = s->dor | GET_CUR_DRV(s);
+}
+
+static int fdc_post_load(void *opaque, int version_id)
+{
+ FDCtrl *s = opaque;
+
+ SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
+ s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
+ return 0;
+}
+
+static const VMStateDescription vmstate_fdc = {
+ .name = "fdc",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .pre_save = fdc_pre_save,
+ .post_load = fdc_post_load,
+ .fields = (VMStateField []) {
+ /* Controller State */
+ VMSTATE_UINT8(sra, FDCtrl),
+ VMSTATE_UINT8(srb, FDCtrl),
+ VMSTATE_UINT8(dor_vmstate, FDCtrl),
+ VMSTATE_UINT8(tdr, FDCtrl),
+ VMSTATE_UINT8(dsr, FDCtrl),
+ VMSTATE_UINT8(msr, FDCtrl),
+ VMSTATE_UINT8(status0, FDCtrl),
+ VMSTATE_UINT8(status1, FDCtrl),
+ VMSTATE_UINT8(status2, FDCtrl),
+ /* Command FIFO */
+ VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8,
+ uint8_t),
+ VMSTATE_UINT32(data_pos, FDCtrl),
+ VMSTATE_UINT32(data_len, FDCtrl),
+ VMSTATE_UINT8(data_state, FDCtrl),
+ VMSTATE_UINT8(data_dir, FDCtrl),
+ VMSTATE_UINT8(eot, FDCtrl),
+ /* States kept only to be returned back */
+ VMSTATE_UINT8(timer0, FDCtrl),
+ VMSTATE_UINT8(timer1, FDCtrl),
+ VMSTATE_UINT8(precomp_trk, FDCtrl),
+ VMSTATE_UINT8(config, FDCtrl),
+ VMSTATE_UINT8(lock, FDCtrl),
+ VMSTATE_UINT8(pwrd, FDCtrl),
+ VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl),
+ VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1,
+ vmstate_fdrive, FDrive),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void fdctrl_external_reset_sysbus(DeviceState *d)
+{
+ FDCtrlSysBus *sys = container_of(d, FDCtrlSysBus, busdev.qdev);
+ FDCtrl *s = &sys->state;
+
+ fdctrl_reset(s, 0);
+}
+
+static void fdctrl_external_reset_isa(DeviceState *d)
+{
+ FDCtrlISABus *isa = container_of(d, FDCtrlISABus, busdev.qdev);
+ FDCtrl *s = &isa->state;
+
+ fdctrl_reset(s, 0);
+}
+
+static void fdctrl_handle_tc(void *opaque, int irq, int level)
+{
+ //FDCtrl *s = opaque;
+
+ if (level) {
+ // XXX
+ FLOPPY_DPRINTF("TC pulsed\n");
+ }
+}
+
+/* XXX: may change if moved to bdrv */
+int fdctrl_get_drive_type(FDCtrl *fdctrl, int drive_num)
+{
+ return fdctrl->drives[drive_num].drive;
+}
+
+/* Change IRQ state */
+static void fdctrl_reset_irq(FDCtrl *fdctrl)
+{
+ if (!(fdctrl->sra & FD_SRA_INTPEND))
+ return;
+ FLOPPY_DPRINTF("Reset interrupt\n");
+ qemu_set_irq(fdctrl->irq, 0);
+ fdctrl->sra &= ~FD_SRA_INTPEND;
+}
+
+static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0)
+{
+ /* Sparc mutation */
+ if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) {
+ /* XXX: not sure */
+ fdctrl->msr &= ~FD_MSR_CMDBUSY;
+ fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
+ fdctrl->status0 = status0;
+ return;
+ }
+ if (!(fdctrl->sra & FD_SRA_INTPEND)) {
+ qemu_set_irq(fdctrl->irq, 1);
+ fdctrl->sra |= FD_SRA_INTPEND;
+ }
+ fdctrl->reset_sensei = 0;
+ fdctrl->status0 = status0;
+ FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
+}
+
+/* Reset controller */
+static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
+{
+ int i;
+
+ FLOPPY_DPRINTF("reset controller\n");
+ fdctrl_reset_irq(fdctrl);
+ /* Initialise controller */
+ fdctrl->sra = 0;
+ fdctrl->srb = 0xc0;
+ if (!fdctrl->drives[1].bs)
+ fdctrl->sra |= FD_SRA_nDRV2;
+ fdctrl->cur_drv = 0;
+ fdctrl->dor = FD_DOR_nRESET;
+ fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
+ fdctrl->msr = FD_MSR_RQM;
+ /* FIFO state */
+ fdctrl->data_pos = 0;
+ fdctrl->data_len = 0;
+ fdctrl->data_state = 0;
+ fdctrl->data_dir = FD_DIR_WRITE;
+ for (i = 0; i < MAX_FD; i++)
+ fd_recalibrate(&fdctrl->drives[i]);
+ fdctrl_reset_fifo(fdctrl);
+ if (do_irq) {
+ fdctrl_raise_irq(fdctrl, FD_SR0_RDYCHG);
+ fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
+ }
+}
+
+static inline FDrive *drv0(FDCtrl *fdctrl)
+{
+ return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
+}
+
+static inline FDrive *drv1(FDCtrl *fdctrl)
+{
+ if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
+ return &fdctrl->drives[1];
+ else
+ return &fdctrl->drives[0];
+}
+
+#if MAX_FD == 4
+static inline FDrive *drv2(FDCtrl *fdctrl)
+{
+ if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
+ return &fdctrl->drives[2];
+ else
+ return &fdctrl->drives[1];
+}
+
+static inline FDrive *drv3(FDCtrl *fdctrl)
+{
+ if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
+ return &fdctrl->drives[3];
+ else
+ return &fdctrl->drives[2];
+}
+#endif
+
+static FDrive *get_cur_drv(FDCtrl *fdctrl)
+{
+ switch (fdctrl->cur_drv) {
+ case 0: return drv0(fdctrl);
+ case 1: return drv1(fdctrl);
+#if MAX_FD == 4
+ case 2: return drv2(fdctrl);
+ case 3: return drv3(fdctrl);
+#endif
+ default: return NULL;
+ }
+}
+
+/* Status A register : 0x00 (read-only) */
+static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl)
+{
+ uint32_t retval = fdctrl->sra;
+
+ FLOPPY_DPRINTF("status register A: 0x%02x\n", retval);
+
+ return retval;
+}
+
+/* Status B register : 0x01 (read-only) */
+static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl)
+{
+ uint32_t retval = fdctrl->srb;
+
+ FLOPPY_DPRINTF("status register B: 0x%02x\n", retval);
+
+ return retval;
+}
+
+/* Digital output register : 0x02 */
+static uint32_t fdctrl_read_dor(FDCtrl *fdctrl)
+{
+ uint32_t retval = fdctrl->dor;
+
+ /* Selected drive */
+ retval |= fdctrl->cur_drv;
+ FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value)
+{
+ FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
+
+ /* Motors */
+ if (value & FD_DOR_MOTEN0)
+ fdctrl->srb |= FD_SRB_MTR0;
+ else
+ fdctrl->srb &= ~FD_SRB_MTR0;
+ if (value & FD_DOR_MOTEN1)
+ fdctrl->srb |= FD_SRB_MTR1;
+ else
+ fdctrl->srb &= ~FD_SRB_MTR1;
+
+ /* Drive */
+ if (value & 1)
+ fdctrl->srb |= FD_SRB_DR0;
+ else
+ fdctrl->srb &= ~FD_SRB_DR0;
+
+ /* Reset */
+ if (!(value & FD_DOR_nRESET)) {
+ if (fdctrl->dor & FD_DOR_nRESET) {
+ FLOPPY_DPRINTF("controller enter RESET state\n");
+ }
+ } else {
+ if (!(fdctrl->dor & FD_DOR_nRESET)) {
+ FLOPPY_DPRINTF("controller out of RESET state\n");
+ fdctrl_reset(fdctrl, 1);
+ fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+ }
+ }
+ /* Selected drive */
+ fdctrl->cur_drv = value & FD_DOR_SELMASK;
+
+ fdctrl->dor = value;
+}
+
+/* Tape drive register : 0x03 */
+static uint32_t fdctrl_read_tape(FDCtrl *fdctrl)
+{
+ uint32_t retval = fdctrl->tdr;
+
+ FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value)
+{
+ /* Reset mode */
+ if (!(fdctrl->dor & FD_DOR_nRESET)) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
+ /* Disk boot selection indicator */
+ fdctrl->tdr = value & FD_TDR_BOOTSEL;
+ /* Tape indicators: never allow */
+}
+
+/* Main status register : 0x04 (read) */
+static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
+{
+ uint32_t retval = fdctrl->msr;
+
+ fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+ fdctrl->dor |= FD_DOR_nRESET;
+
+ /* Sparc mutation */
+ if (fdctrl->sun4m) {
+ retval |= FD_MSR_DIO;
+ fdctrl_reset_irq(fdctrl);
+ };
+
+ FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+/* Data select rate register : 0x04 (write) */
+static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
+{
+ /* Reset mode */
+ if (!(fdctrl->dor & FD_DOR_nRESET)) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
+ /* Reset: autoclear */
+ if (value & FD_DSR_SWRESET) {
+ fdctrl->dor &= ~FD_DOR_nRESET;
+ fdctrl_reset(fdctrl, 1);
+ fdctrl->dor |= FD_DOR_nRESET;
+ }
+ if (value & FD_DSR_PWRDOWN) {
+ fdctrl_reset(fdctrl, 1);
+ }
+ fdctrl->dsr = value;
+}
+
+static int fdctrl_media_changed(FDrive *drv)
+{
+ int ret;
+
+ if (!drv->bs)
+ return 0;
+ ret = bdrv_media_changed(drv->bs);
+ if (ret) {
+ fd_revalidate(drv);
+ }
+ return ret;
+}
+
+/* Digital input register : 0x07 (read-only) */
+static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
+{
+ uint32_t retval = 0;
+
+ if (fdctrl_media_changed(drv0(fdctrl))
+ || fdctrl_media_changed(drv1(fdctrl))
+#if MAX_FD == 4
+ || fdctrl_media_changed(drv2(fdctrl))
+ || fdctrl_media_changed(drv3(fdctrl))
+#endif
+ )
+ retval |= FD_DIR_DSKCHG;
+ if (retval != 0) {
+ FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
+ }
+
+ return retval;
+}
+
+/* FIFO state control */
+static void fdctrl_reset_fifo(FDCtrl *fdctrl)
+{
+ fdctrl->data_dir = FD_DIR_WRITE;
+ fdctrl->data_pos = 0;
+ fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
+}
+
+/* Set FIFO status for the host to read */
+static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len, int do_irq)
+{
+ fdctrl->data_dir = FD_DIR_READ;
+ fdctrl->data_len = fifo_len;
+ fdctrl->data_pos = 0;
+ fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
+ if (do_irq)
+ fdctrl_raise_irq(fdctrl, 0x00);
+}
+
+/* Set an error: unimplemented/unknown command */
+static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
+{
+ FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]);
+ fdctrl->fifo[0] = FD_SR0_INVCMD;
+ fdctrl_set_fifo(fdctrl, 1, 0);
+}
+
+/* Seek to next sector */
+static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
+{
+ FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
+ cur_drv->head, cur_drv->track, cur_drv->sect,
+ fd_sector(cur_drv));
+ /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
+ error in fact */
+ if (cur_drv->sect >= cur_drv->last_sect ||
+ cur_drv->sect == fdctrl->eot) {
+ cur_drv->sect = 1;
+ if (FD_MULTI_TRACK(fdctrl->data_state)) {
+ if (cur_drv->head == 0 &&
+ (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
+ cur_drv->head = 1;
+ } else {
+ cur_drv->head = 0;
+ cur_drv->track++;
+ if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
+ return 0;
+ }
+ } else {
+ cur_drv->track++;
+ return 0;
+ }
+ FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
+ cur_drv->head, cur_drv->track,
+ cur_drv->sect, fd_sector(cur_drv));
+ } else {
+ cur_drv->sect++;
+ }
+ return 1;
+}
+
+/* Callback for transfer end (stop or abort) */
+static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
+ uint8_t status1, uint8_t status2)
+{
+ FDrive *cur_drv;
+
+ cur_drv = get_cur_drv(fdctrl);
+ FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
+ status0, status1, status2,
+ status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl));
+ fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+ fdctrl->fifo[1] = status1;
+ fdctrl->fifo[2] = status2;
+ fdctrl->fifo[3] = cur_drv->track;
+ fdctrl->fifo[4] = cur_drv->head;
+ fdctrl->fifo[5] = cur_drv->sect;
+ fdctrl->fifo[6] = FD_SECTOR_SC;
+ fdctrl->data_dir = FD_DIR_READ;
+ if (!(fdctrl->msr & FD_MSR_NONDMA)) {
+ DMA_release_DREQ(fdctrl->dma_chann);
+ }
+ fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
+ fdctrl->msr &= ~FD_MSR_NONDMA;
+ fdctrl_set_fifo(fdctrl, 7, 1);
+}
+
+/* Prepare a data transfer (either DMA or FIFO) */
+static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+ uint8_t kh, kt, ks;
+ int did_seek = 0;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ kt = fdctrl->fifo[2];
+ kh = fdctrl->fifo[3];
+ ks = fdctrl->fifo[4];
+ FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
+ GET_CUR_DRV(fdctrl), kh, kt, ks,
+ fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
+ switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
+ case 2:
+ /* sect too big */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 3:
+ /* track too big */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 4:
+ /* No seek enabled */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 1:
+ did_seek = 1;
+ break;
+ default:
+ break;
+ }
+
+ /* Set the FIFO state */
+ fdctrl->data_dir = direction;
+ fdctrl->data_pos = 0;
+ fdctrl->msr |= FD_MSR_CMDBUSY;
+ if (fdctrl->fifo[0] & 0x80)
+ fdctrl->data_state |= FD_STATE_MULTI;
+ else
+ fdctrl->data_state &= ~FD_STATE_MULTI;
+ if (did_seek)
+ fdctrl->data_state |= FD_STATE_SEEK;
+ else
+ fdctrl->data_state &= ~FD_STATE_SEEK;
+ if (fdctrl->fifo[5] == 00) {
+ fdctrl->data_len = fdctrl->fifo[8];
+ } else {
+ int tmp;
+ fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
+ tmp = (fdctrl->fifo[6] - ks + 1);
+ if (fdctrl->fifo[0] & 0x80)
+ tmp += fdctrl->fifo[6];
+ fdctrl->data_len *= tmp;
+ }
+ fdctrl->eot = fdctrl->fifo[6];
+ if (fdctrl->dor & FD_DOR_DMAEN) {
+ int dma_mode;
+ /* DMA transfer are enabled. Check if DMA channel is well programmed */
+ dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
+ dma_mode = (dma_mode >> 2) & 3;
+ FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
+ dma_mode, direction,
+ (128 << fdctrl->fifo[5]) *
+ (cur_drv->last_sect - ks + 1), fdctrl->data_len);
+ if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
+ direction == FD_DIR_SCANH) && dma_mode == 0) ||
+ (direction == FD_DIR_WRITE && dma_mode == 2) ||
+ (direction == FD_DIR_READ && dma_mode == 1)) {
+ /* No access is allowed until DMA transfer has completed */
+ fdctrl->msr &= ~FD_MSR_RQM;
+ /* Now, we just have to wait for the DMA controller to
+ * recall us...
+ */
+ DMA_hold_DREQ(fdctrl->dma_chann);
+ DMA_schedule(fdctrl->dma_chann);
+ return;
+ } else {
+ FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
+ }
+ }
+ FLOPPY_DPRINTF("start non-DMA transfer\n");
+ fdctrl->msr |= FD_MSR_NONDMA;
+ if (direction != FD_DIR_WRITE)
+ fdctrl->msr |= FD_MSR_DIO;
+ /* IO based transfer: calculate len */
+ fdctrl_raise_irq(fdctrl, 0x00);
+
+ return;
+}
+
+/* Prepare a transfer of deleted data */
+static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction)
+{
+ FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n");
+
+ /* We don't handle deleted data,
+ * so we don't return *ANYTHING*
+ */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+}
+
+/* handlers for DMA transfers */
+static int fdctrl_transfer_handler (void *opaque, int nchan,
+ int dma_pos, int dma_len)
+{
+ FDCtrl *fdctrl;
+ FDrive *cur_drv;
+ int len, start_pos, rel_pos;
+ uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
+
+ fdctrl = opaque;
+ if (fdctrl->msr & FD_MSR_RQM) {
+ FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
+ return 0;
+ }
+ cur_drv = get_cur_drv(fdctrl);
+ if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
+ fdctrl->data_dir == FD_DIR_SCANH)
+ status2 = FD_SR2_SNS;
+ if (dma_len > fdctrl->data_len)
+ dma_len = fdctrl->data_len;
+ if (cur_drv->bs == NULL) {
+ if (fdctrl->data_dir == FD_DIR_WRITE)
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+ else
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ len = 0;
+ goto transfer_error;
+ }
+ rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+ for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
+ len = dma_len - fdctrl->data_pos;
+ if (len + rel_pos > FD_SECTOR_LEN)
+ len = FD_SECTOR_LEN - rel_pos;
+ FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
+ "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
+ fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
+ cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
+ fd_sector(cur_drv) * FD_SECTOR_LEN);
+ if (fdctrl->data_dir != FD_DIR_WRITE ||
+ len < FD_SECTOR_LEN || rel_pos != 0) {
+ /* READ & SCAN commands and realign to a sector for WRITE */
+ if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
+ fdctrl->fifo, 1) < 0) {
+ FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
+ fd_sector(cur_drv));
+ /* Sure, image size is too small... */
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ }
+ }
+ switch (fdctrl->data_dir) {
+ case FD_DIR_READ:
+ /* READ commands */
+ DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
+ break;
+ case FD_DIR_WRITE:
+ /* WRITE commands */
+ DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
+ if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
+ fdctrl->fifo, 1) < 0) {
+ FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv));
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+ goto transfer_error;
+ }
+ break;
+ default:
+ /* SCAN commands */
+ {
+ uint8_t tmpbuf[FD_SECTOR_LEN];
+ int ret;
+ DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
+ ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
+ if (ret == 0) {
+ status2 = FD_SR2_SEH;
+ goto end_transfer;
+ }
+ if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
+ (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
+ status2 = 0x00;
+ goto end_transfer;
+ }
+ }
+ break;
+ }
+ fdctrl->data_pos += len;
+ rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+ if (rel_pos == 0) {
+ /* Seek to next sector */
+ if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
+ break;
+ }
+ }
+ end_transfer:
+ len = fdctrl->data_pos - start_pos;
+ FLOPPY_DPRINTF("end transfer %d %d %d\n",
+ fdctrl->data_pos, len, fdctrl->data_len);
+ if (fdctrl->data_dir == FD_DIR_SCANE ||
+ fdctrl->data_dir == FD_DIR_SCANL ||
+ fdctrl->data_dir == FD_DIR_SCANH)
+ status2 = FD_SR2_SEH;
+ if (FD_DID_SEEK(fdctrl->data_state))
+ status0 |= FD_SR0_SEEK;
+ fdctrl->data_len -= len;
+ fdctrl_stop_transfer(fdctrl, status0, status1, status2);
+ transfer_error:
+
+ return len;
+}
+
+/* Data register : 0x05 */
+static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
+{
+ FDrive *cur_drv;
+ uint32_t retval = 0;
+ int pos;
+
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+ if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
+ FLOPPY_ERROR("controller not ready for reading\n");
+ return 0;
+ }
+ pos = fdctrl->data_pos;
+ if (fdctrl->msr & FD_MSR_NONDMA) {
+ pos %= FD_SECTOR_LEN;
+ if (pos == 0) {
+ if (fdctrl->data_pos != 0)
+ if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
+ FLOPPY_DPRINTF("error seeking to next sector %d\n",
+ fd_sector(cur_drv));
+ return 0;
+ }
+ if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ FLOPPY_DPRINTF("error getting sector %d\n",
+ fd_sector(cur_drv));
+ /* Sure, image size is too small... */
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ }
+ }
+ }
+ retval = fdctrl->fifo[pos];
+ if (++fdctrl->data_pos == fdctrl->data_len) {
+ fdctrl->data_pos = 0;
+ /* Switch from transfer mode to status mode
+ * then from status mode to command mode
+ */
+ if (fdctrl->msr & FD_MSR_NONDMA) {
+ fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
+ } else {
+ fdctrl_reset_fifo(fdctrl);
+ fdctrl_reset_irq(fdctrl);
+ }
+ }
+ FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
+
+ return retval;
+}
+
+static void fdctrl_format_sector(FDCtrl *fdctrl)
+{
+ FDrive *cur_drv;
+ uint8_t kh, kt, ks;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ kt = fdctrl->fifo[6];
+ kh = fdctrl->fifo[7];
+ ks = fdctrl->fifo[8];
+ FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
+ GET_CUR_DRV(fdctrl), kh, kt, ks,
+ fd_sector_calc(kh, kt, ks, cur_drv->last_sect));
+ switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
+ case 2:
+ /* sect too big */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 3:
+ /* track too big */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 4:
+ /* No seek enabled */
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+ fdctrl->fifo[3] = kt;
+ fdctrl->fifo[4] = kh;
+ fdctrl->fifo[5] = ks;
+ return;
+ case 1:
+ fdctrl->data_state |= FD_STATE_SEEK;
+ break;
+ default:
+ break;
+ }
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+ if (cur_drv->bs == NULL ||
+ bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ FLOPPY_ERROR("formatting sector %d\n", fd_sector(cur_drv));
+ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+ } else {
+ if (cur_drv->sect == cur_drv->last_sect) {
+ fdctrl->data_state &= ~FD_STATE_FORMAT;
+ /* Last sector done */
+ if (FD_DID_SEEK(fdctrl->data_state))
+ fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
+ else
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+ } else {
+ /* More to do */
+ fdctrl->data_pos = 0;
+ fdctrl->data_len = 4;
+ }
+ }
+}
+
+static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
+{
+ fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
+ fdctrl->fifo[0] = fdctrl->lock << 4;
+ fdctrl_set_fifo(fdctrl, 1, fdctrl->lock);
+}
+
+static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ /* Drives position */
+ fdctrl->fifo[0] = drv0(fdctrl)->track;
+ fdctrl->fifo[1] = drv1(fdctrl)->track;
+#if MAX_FD == 4
+ fdctrl->fifo[2] = drv2(fdctrl)->track;
+ fdctrl->fifo[3] = drv3(fdctrl)->track;
+#else
+ fdctrl->fifo[2] = 0;
+ fdctrl->fifo[3] = 0;
+#endif
+ /* timers */
+ fdctrl->fifo[4] = fdctrl->timer0;
+ fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
+ fdctrl->fifo[6] = cur_drv->last_sect;
+ fdctrl->fifo[7] = (fdctrl->lock << 7) |
+ (cur_drv->perpendicular << 2);
+ fdctrl->fifo[8] = fdctrl->config;
+ fdctrl->fifo[9] = fdctrl->precomp_trk;
+ fdctrl_set_fifo(fdctrl, 10, 0);
+}
+
+static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
+{
+ /* Controller's version */
+ fdctrl->fifo[0] = fdctrl->version;
+ fdctrl_set_fifo(fdctrl, 1, 1);
+}
+
+static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
+{
+ fdctrl->fifo[0] = 0x41; /* Stepping 1 */
+ fdctrl_set_fifo(fdctrl, 1, 0);
+}
+
+static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ /* Drives position */
+ drv0(fdctrl)->track = fdctrl->fifo[3];
+ drv1(fdctrl)->track = fdctrl->fifo[4];
+#if MAX_FD == 4
+ drv2(fdctrl)->track = fdctrl->fifo[5];
+ drv3(fdctrl)->track = fdctrl->fifo[6];
+#endif
+ /* timers */
+ fdctrl->timer0 = fdctrl->fifo[7];
+ fdctrl->timer1 = fdctrl->fifo[8];
+ cur_drv->last_sect = fdctrl->fifo[9];
+ fdctrl->lock = fdctrl->fifo[10] >> 7;
+ cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
+ fdctrl->config = fdctrl->fifo[11];
+ fdctrl->precomp_trk = fdctrl->fifo[12];
+ fdctrl->pwrd = fdctrl->fifo[13];
+ fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ fdctrl->fifo[0] = 0;
+ fdctrl->fifo[1] = 0;
+ /* Drives position */
+ fdctrl->fifo[2] = drv0(fdctrl)->track;
+ fdctrl->fifo[3] = drv1(fdctrl)->track;
+#if MAX_FD == 4
+ fdctrl->fifo[4] = drv2(fdctrl)->track;
+ fdctrl->fifo[5] = drv3(fdctrl)->track;
+#else
+ fdctrl->fifo[4] = 0;
+ fdctrl->fifo[5] = 0;
+#endif
+ /* timers */
+ fdctrl->fifo[6] = fdctrl->timer0;
+ fdctrl->fifo[7] = fdctrl->timer1;
+ fdctrl->fifo[8] = cur_drv->last_sect;
+ fdctrl->fifo[9] = (fdctrl->lock << 7) |
+ (cur_drv->perpendicular << 2);
+ fdctrl->fifo[10] = fdctrl->config;
+ fdctrl->fifo[11] = fdctrl->precomp_trk;
+ fdctrl->fifo[12] = fdctrl->pwrd;
+ fdctrl->fifo[13] = 0;
+ fdctrl->fifo[14] = 0;
+ fdctrl_set_fifo(fdctrl, 15, 1);
+}
+
+static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ /* XXX: should set main status register to busy */
+ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+ qemu_mod_timer(fdctrl->result_timer,
+ qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 50));
+}
+
+static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl->data_state |= FD_STATE_FORMAT;
+ if (fdctrl->fifo[0] & 0x80)
+ fdctrl->data_state |= FD_STATE_MULTI;
+ else
+ fdctrl->data_state &= ~FD_STATE_MULTI;
+ fdctrl->data_state &= ~FD_STATE_SEEK;
+ cur_drv->bps =
+ fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
+#if 0
+ cur_drv->last_sect =
+ cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
+ fdctrl->fifo[3] / 2;
+#else
+ cur_drv->last_sect = fdctrl->fifo[3];
+#endif
+ /* TODO: implement format using DMA expected by the Bochs BIOS
+ * and Linux fdformat (read 3 bytes per sector via DMA and fill
+ * the sector with the specified fill byte
+ */
+ fdctrl->data_state &= ~FD_STATE_FORMAT;
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+}
+
+static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
+{
+ fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
+ fdctrl->timer1 = fdctrl->fifo[2] >> 1;
+ if (fdctrl->fifo[2] & 1)
+ fdctrl->dor &= ~FD_DOR_DMAEN;
+ else
+ fdctrl->dor |= FD_DOR_DMAEN;
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+ /* 1 Byte status back */
+ fdctrl->fifo[0] = (cur_drv->ro << 6) |
+ (cur_drv->track == 0 ? 0x10 : 0x00) |
+ (cur_drv->head << 2) |
+ GET_CUR_DRV(fdctrl) |
+ 0x28;
+ fdctrl_set_fifo(fdctrl, 1, 0);
+}
+
+static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ fd_recalibrate(cur_drv);
+ fdctrl_reset_fifo(fdctrl);
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+}
+
+static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ if(fdctrl->reset_sensei > 0) {
+ fdctrl->fifo[0] =
+ FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
+ fdctrl->reset_sensei--;
+ } else {
+ /* XXX: status0 handling is broken for read/write
+ commands, so we do this hack. It should be suppressed
+ ASAP */
+ fdctrl->fifo[0] =
+ FD_SR0_SEEK | (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+ }
+
+ fdctrl->fifo[1] = cur_drv->track;
+ fdctrl_set_fifo(fdctrl, 2, 0);
+ fdctrl_reset_irq(fdctrl);
+ fdctrl->status0 = FD_SR0_RDYCHG;
+}
+
+static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ fdctrl_reset_fifo(fdctrl);
+ if (fdctrl->fifo[2] > cur_drv->max_track) {
+ fdctrl_raise_irq(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK);
+ } else {
+ cur_drv->track = fdctrl->fifo[2];
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+ }
+}
+
+static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ if (fdctrl->fifo[1] & 0x80)
+ cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
+{
+ fdctrl->config = fdctrl->fifo[2];
+ fdctrl->precomp_trk = fdctrl->fifo[3];
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
+{
+ fdctrl->pwrd = fdctrl->fifo[1];
+ fdctrl->fifo[0] = fdctrl->fifo[1];
+ fdctrl_set_fifo(fdctrl, 1, 1);
+}
+
+static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
+{
+ /* No result back */
+ fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
+ /* Command parameters done */
+ if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
+ fdctrl->fifo[0] = fdctrl->fifo[1];
+ fdctrl->fifo[2] = 0;
+ fdctrl->fifo[3] = 0;
+ fdctrl_set_fifo(fdctrl, 4, 1);
+ } else {
+ fdctrl_reset_fifo(fdctrl);
+ }
+ } else if (fdctrl->data_len > 7) {
+ /* ERROR */
+ fdctrl->fifo[0] = 0x80 |
+ (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+ fdctrl_set_fifo(fdctrl, 1, 1);
+ }
+}
+
+static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
+ cur_drv->track = cur_drv->max_track - 1;
+ } else {
+ cur_drv->track += fdctrl->fifo[2];
+ }
+ fdctrl_reset_fifo(fdctrl);
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+}
+
+static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
+{
+ FDrive *cur_drv;
+
+ SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+ cur_drv = get_cur_drv(fdctrl);
+ if (fdctrl->fifo[2] > cur_drv->track) {
+ cur_drv->track = 0;
+ } else {
+ cur_drv->track -= fdctrl->fifo[2];
+ }
+ fdctrl_reset_fifo(fdctrl);
+ /* Raise Interrupt */
+ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK);
+}
+
+static const struct {
+ uint8_t value;
+ uint8_t mask;
+ const char* name;
+ int parameters;
+ void (*handler)(FDCtrl *fdctrl, int direction);
+ int direction;
+} handlers[] = {
+ { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
+ { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
+ { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
+ { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
+ { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
+ { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
+ { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
+ { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
+ { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
+ { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
+ { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
+ { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_unimplemented },
+ { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
+ { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
+ { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
+ { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
+ { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
+ { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
+ { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
+ { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
+ { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
+ { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
+ { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
+ { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
+ { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
+ { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
+ { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
+ { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
+ { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
+ { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
+ { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
+ { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
+};
+/* Associate command to an index in the 'handlers' array */
+static uint8_t command_to_handler[256];
+
+static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
+{
+ FDrive *cur_drv;
+ int pos;
+
+ /* Reset mode */
+ if (!(fdctrl->dor & FD_DOR_nRESET)) {
+ FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+ return;
+ }
+ if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
+ FLOPPY_ERROR("controller not ready for writing\n");
+ return;
+ }
+ fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+ /* Is it write command time ? */
+ if (fdctrl->msr & FD_MSR_NONDMA) {
+ /* FIFO data write */
+ pos = fdctrl->data_pos++;
+ pos %= FD_SECTOR_LEN;
+ fdctrl->fifo[pos] = value;
+ if (pos == FD_SECTOR_LEN - 1 ||
+ fdctrl->data_pos == fdctrl->data_len) {
+ cur_drv = get_cur_drv(fdctrl);
+ if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+ FLOPPY_ERROR("writing sector %d\n", fd_sector(cur_drv));
+ return;
+ }
+ if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
+ FLOPPY_DPRINTF("error seeking to next sector %d\n",
+ fd_sector(cur_drv));
+ return;
+ }
+ }
+ /* Switch from transfer mode to status mode
+ * then from status mode to command mode
+ */
+ if (fdctrl->data_pos == fdctrl->data_len)
+ fdctrl_stop_transfer(fdctrl, FD_SR0_SEEK, 0x00, 0x00);
+ return;
+ }
+ if (fdctrl->data_pos == 0) {
+ /* Command */
+ pos = command_to_handler[value & 0xff];
+ FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
+ fdctrl->data_len = handlers[pos].parameters + 1;
+ }
+
+ FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
+ fdctrl->fifo[fdctrl->data_pos++] = value;
+ if (fdctrl->data_pos == fdctrl->data_len) {
+ /* We now have all parameters
+ * and will be able to treat the command
+ */
+ if (fdctrl->data_state & FD_STATE_FORMAT) {
+ fdctrl_format_sector(fdctrl);
+ return;
+ }
+
+ pos = command_to_handler[fdctrl->fifo[0] & 0xff];
+ FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
+ (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
+ }
+}
+
+static void fdctrl_result_timer(void *opaque)
+{
+ FDCtrl *fdctrl = opaque;
+ FDrive *cur_drv = get_cur_drv(fdctrl);
+
+ /* Pretend we are spinning.
+ * This is needed for Coherent, which uses READ ID to check for
+ * sector interleaving.
+ */
+ if (cur_drv->last_sect != 0) {
+ cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
+ }
+ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+}
+
+/* Init functions */
+static int fdctrl_connect_drives(FDCtrl *fdctrl)
+{
+ unsigned int i;
+ FDrive *drive;
+
+ for (i = 0; i < MAX_FD; i++) {
+ drive = &fdctrl->drives[i];
+
+ if (drive->bs) {
+ if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
+ error_report("fdc doesn't support drive option werror");
+ return -1;
+ }
+ if (bdrv_get_on_error(drive->bs, 1) != BLOCK_ERR_REPORT) {
+ error_report("fdc doesn't support drive option rerror");
+ return -1;
+ }
+ }
+
+ fd_init(drive);
+ fd_revalidate(drive);
+ if (drive->bs) {
+ bdrv_set_removable(drive->bs, 1);
+ }
+ }
+ return 0;
+}
+
+FDCtrl *fdctrl_init_isa(DriveInfo **fds)
+{
+ ISADevice *dev;
+
+ dev = isa_create("isa-fdc");
+ if (fds[0]) {
+ qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv);
+ }
+ if (fds[1]) {
+ qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv);
+ }
+ qdev_init_nofail(&dev->qdev);
+ return &(DO_UPCAST(FDCtrlISABus, busdev, dev)->state);
+}
+
+FDCtrl *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
+ target_phys_addr_t mmio_base, DriveInfo **fds)
+{
+ FDCtrl *fdctrl;
+ DeviceState *dev;
+ FDCtrlSysBus *sys;
+
+ dev = qdev_create(NULL, "sysbus-fdc");
+ sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
+ fdctrl = &sys->state;
+ fdctrl->dma_chann = dma_chann; /* FIXME */
+ if (fds[0]) {
+ qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
+ }
+ if (fds[1]) {
+ qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
+ }
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(&sys->busdev, 0, irq);
+ sysbus_mmio_map(&sys->busdev, 0, mmio_base);
+
+ return fdctrl;
+}
+
+FDCtrl *sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base,
+ DriveInfo **fds, qemu_irq *fdc_tc)
+{
+ DeviceState *dev;
+ FDCtrlSysBus *sys;
+ FDCtrl *fdctrl;
+
+ dev = qdev_create(NULL, "SUNW,fdtwo");
+ if (fds[0]) {
+ qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
+ }
+ qdev_init_nofail(dev);
+ sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
+ fdctrl = &sys->state;
+ sysbus_connect_irq(&sys->busdev, 0, irq);
+ sysbus_mmio_map(&sys->busdev, 0, io_base);
+ *fdc_tc = qdev_get_gpio_in(dev, 0);
+
+ return fdctrl;
+}
+
+static int fdctrl_init_common(FDCtrl *fdctrl)
+{
+ int i, j;
+ static int command_tables_inited = 0;
+
+ /* Fill 'command_to_handler' lookup table */
+ if (!command_tables_inited) {
+ command_tables_inited = 1;
+ for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) {
+ for (j = 0; j < sizeof(command_to_handler); j++) {
+ if ((j & handlers[i].mask) == handlers[i].value) {
+ command_to_handler[j] = i;
+ }
+ }
+ }
+ }
+
+ FLOPPY_DPRINTF("init controller\n");
+ fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
+ fdctrl->fifo_size = 512;
+ fdctrl->result_timer = qemu_new_timer(vm_clock,
+ fdctrl_result_timer, fdctrl);
+
+ fdctrl->version = 0x90; /* Intel 82078 controller */
+ fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
+ fdctrl->num_floppies = MAX_FD;
+
+ if (fdctrl->dma_chann != -1)
+ DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
+ return fdctrl_connect_drives(fdctrl);
+}
+
+static int isabus_fdc_init1(ISADevice *dev)
+{
+ FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev);
+ FDCtrl *fdctrl = &isa->state;
+ int iobase = 0x3f0;
+ int isairq = 6;
+ int dma_chann = 2;
+ int ret;
+
+ register_ioport_read(iobase + 0x01, 5, 1,
+ &fdctrl_read_port, fdctrl);
+ register_ioport_read(iobase + 0x07, 1, 1,
+ &fdctrl_read_port, fdctrl);
+ register_ioport_write(iobase + 0x01, 5, 1,
+ &fdctrl_write_port, fdctrl);
+ register_ioport_write(iobase + 0x07, 1, 1,
+ &fdctrl_write_port, fdctrl);
+ isa_init_ioport_range(dev, iobase, 6);
+ isa_init_ioport(dev, iobase + 7);
+
+ isa_init_irq(&isa->busdev, &fdctrl->irq, isairq);
+ fdctrl->dma_chann = dma_chann;
+
+ qdev_set_legacy_instance_id(&dev->qdev, iobase, 2);
+ ret = fdctrl_init_common(fdctrl);
+
+ add_boot_device_path(isa->bootindexA, &dev->qdev, "/floppy@0");
+ add_boot_device_path(isa->bootindexB, &dev->qdev, "/floppy@1");
+
+ return ret;
+}
+
+static int sysbus_fdc_init1(SysBusDevice *dev)
+{
+ FDCtrlSysBus *sys = DO_UPCAST(FDCtrlSysBus, busdev, dev);
+ FDCtrl *fdctrl = &sys->state;
+ int io;
+ int ret;
+
+ io = cpu_register_io_memory(fdctrl_mem_read, fdctrl_mem_write, fdctrl,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x08, io);
+ sysbus_init_irq(dev, &fdctrl->irq);
+ qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
+ fdctrl->dma_chann = -1;
+
+ qdev_set_legacy_instance_id(&dev->qdev, io, 2);
+ ret = fdctrl_init_common(fdctrl);
+
+ return ret;
+}
+
+static int sun4m_fdc_init1(SysBusDevice *dev)
+{
+ FDCtrl *fdctrl = &(FROM_SYSBUS(FDCtrlSysBus, dev)->state);
+ int io;
+
+ io = cpu_register_io_memory(fdctrl_mem_read_strict,
+ fdctrl_mem_write_strict, fdctrl,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x08, io);
+ sysbus_init_irq(dev, &fdctrl->irq);
+ qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
+
+ fdctrl->sun4m = 1;
+ qdev_set_legacy_instance_id(&dev->qdev, io, 2);
+ return fdctrl_init_common(fdctrl);
+}
+
+static const VMStateDescription vmstate_isa_fdc ={
+ .name = "fdc",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static ISADeviceInfo isa_fdc_info = {
+ .init = isabus_fdc_init1,
+ .qdev.name = "isa-fdc",
+ .qdev.fw_name = "fdc",
+ .qdev.size = sizeof(FDCtrlISABus),
+ .qdev.no_user = 1,
+ .qdev.vmsd = &vmstate_isa_fdc,
+ .qdev.reset = fdctrl_external_reset_isa,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs),
+ DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
+ DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1),
+ DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static const VMStateDescription vmstate_sysbus_fdc ={
+ .name = "fdc",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo sysbus_fdc_info = {
+ .init = sysbus_fdc_init1,
+ .qdev.name = "sysbus-fdc",
+ .qdev.size = sizeof(FDCtrlSysBus),
+ .qdev.vmsd = &vmstate_sysbus_fdc,
+ .qdev.reset = fdctrl_external_reset_sysbus,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].bs),
+ DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].bs),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo sun4m_fdc_info = {
+ .init = sun4m_fdc_init1,
+ .qdev.name = "SUNW,fdtwo",
+ .qdev.size = sizeof(FDCtrlSysBus),
+ .qdev.vmsd = &vmstate_sysbus_fdc,
+ .qdev.reset = fdctrl_external_reset_sysbus,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].bs),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void fdc_register_devices(void)
+{
+ isa_qdev_register(&isa_fdc_info);
+ sysbus_register_withprop(&sysbus_fdc_info);
+ sysbus_register_withprop(&sun4m_fdc_info);
+}
+
+device_init(fdc_register_devices)
diff --git a/hw/fdc.h b/hw/fdc.h
new file mode 100644
index 0000000..242730a
--- /dev/null
+++ b/hw/fdc.h
@@ -0,0 +1,16 @@
+#ifndef HW_FDC_H
+#define HW_FDC_H
+
+/* fdc.c */
+#define MAX_FD 2
+
+typedef struct FDCtrl FDCtrl;
+
+FDCtrl *fdctrl_init_isa(DriveInfo **fds);
+FDCtrl *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
+ target_phys_addr_t mmio_base, DriveInfo **fds);
+FDCtrl *sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base,
+ DriveInfo **fds, qemu_irq *fdc_tc);
+int fdctrl_get_drive_type(FDCtrl *fdctrl, int drive_num);
+
+#endif
diff --git a/hw/file-op-9p.h b/hw/file-op-9p.h
new file mode 100644
index 0000000..126e60e
--- /dev/null
+++ b/hw/file-op-9p.h
@@ -0,0 +1,107 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef _FILEOP_H
+#define _FILEOP_H
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/vfs.h>
+#define SM_LOCAL_MODE_BITS 0600
+#define SM_LOCAL_DIR_MODE_BITS 0700
+
+typedef enum
+{
+ /*
+ * Server will try to set uid/gid.
+ * On failure ignore the error.
+ */
+ SM_NONE = 0,
+ /*
+ * uid/gid set on fileserver files
+ */
+ SM_PASSTHROUGH = 1,
+ /*
+ * uid/gid part of xattr
+ */
+ SM_MAPPED,
+} SecModel;
+
+typedef struct FsCred
+{
+ uid_t fc_uid;
+ gid_t fc_gid;
+ mode_t fc_mode;
+ dev_t fc_rdev;
+} FsCred;
+
+struct xattr_operations;
+
+typedef struct FsContext
+{
+ char *fs_root;
+ SecModel fs_sm;
+ uid_t uid;
+ struct xattr_operations **xops;
+} FsContext;
+
+void cred_init(FsCred *);
+
+typedef struct FileOperations
+{
+ int (*lstat)(FsContext *, const char *, struct stat *);
+ ssize_t (*readlink)(FsContext *, const char *, char *, size_t);
+ int (*chmod)(FsContext *, const char *, FsCred *);
+ int (*chown)(FsContext *, const char *, FsCred *);
+ int (*mknod)(FsContext *, const char *, FsCred *);
+ int (*utimensat)(FsContext *, const char *, const struct timespec *);
+ int (*remove)(FsContext *, const char *);
+ int (*symlink)(FsContext *, const char *, const char *, FsCred *);
+ int (*link)(FsContext *, const char *, const char *);
+ int (*setuid)(FsContext *, uid_t);
+ int (*close)(FsContext *, int);
+ int (*closedir)(FsContext *, DIR *);
+ DIR *(*opendir)(FsContext *, const char *);
+ int (*open)(FsContext *, const char *, int);
+ int (*open2)(FsContext *, const char *, int, FsCred *);
+ void (*rewinddir)(FsContext *, DIR *);
+ off_t (*telldir)(FsContext *, DIR *);
+ struct dirent *(*readdir)(FsContext *, DIR *);
+ void (*seekdir)(FsContext *, DIR *, off_t);
+ ssize_t (*preadv)(FsContext *, int, const struct iovec *, int, off_t);
+ ssize_t (*pwritev)(FsContext *, int, const struct iovec *, int, off_t);
+ int (*mkdir)(FsContext *, const char *, FsCred *);
+ int (*fstat)(FsContext *, int, struct stat *);
+ int (*rename)(FsContext *, const char *, const char *);
+ int (*truncate)(FsContext *, const char *, off_t);
+ int (*fsync)(FsContext *, int, int);
+ int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf);
+ ssize_t (*lgetxattr)(FsContext *, const char *,
+ const char *, void *, size_t);
+ ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t);
+ int (*lsetxattr)(FsContext *, const char *,
+ const char *, void *, size_t, int);
+ int (*lremovexattr)(FsContext *, const char *, const char *);
+ void *opaque;
+} FileOperations;
+
+static inline const char *rpath(FsContext *ctx, const char *path)
+{
+ /* FIXME: so wrong... */
+ static char buffer[4096];
+ snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
+ return buffer;
+}
+#endif
diff --git a/hw/firmware_abi.h b/hw/firmware_abi.h
new file mode 100644
index 0000000..5e6e5d4
--- /dev/null
+++ b/hw/firmware_abi.h
@@ -0,0 +1,73 @@
+#ifndef FIRMWARE_ABI_H
+#define FIRMWARE_ABI_H
+
+/* OpenBIOS NVRAM partition */
+struct OpenBIOS_nvpart_v1 {
+ uint8_t signature;
+ uint8_t checksum;
+ uint16_t len; // BE, length divided by 16
+ char name[12];
+};
+
+#define OPENBIOS_PART_SYSTEM 0x70
+#define OPENBIOS_PART_FREE 0x7f
+
+static inline void
+OpenBIOS_finish_partition(struct OpenBIOS_nvpart_v1 *header, uint32_t size)
+{
+ unsigned int i, sum;
+ uint8_t *tmpptr;
+
+ // Length divided by 16
+ header->len = cpu_to_be16(size >> 4);
+
+ // Checksum
+ tmpptr = (uint8_t *)header;
+ sum = *tmpptr;
+ for (i = 0; i < 14; i++) {
+ sum += tmpptr[2 + i];
+ sum = (sum + ((sum & 0xff00) >> 8)) & 0xff;
+ }
+ header->checksum = sum & 0xff;
+}
+
+static inline uint32_t
+OpenBIOS_set_var(uint8_t *nvram, uint32_t addr, const char *str)
+{
+ uint32_t len;
+
+ len = strlen(str) + 1;
+ memcpy(&nvram[addr], str, len);
+
+ return addr + len;
+}
+
+/* Sun IDPROM structure at the end of NVRAM */
+/* from http://www.squirrel.com/squirrel/sun-nvram-hostid.faq.html */
+struct Sun_nvram {
+ uint8_t type; /* always 01 */
+ uint8_t machine_id; /* first byte of host id (machine type) */
+ uint8_t macaddr[6]; /* 6 byte ethernet address (first 3 bytes 08, 00, 20) */
+ uint8_t date[4]; /* date of manufacture */
+ uint8_t hostid[3]; /* remaining 3 bytes of host id (serial number) */
+ uint8_t checksum; /* bitwise xor of previous bytes */
+};
+
+static inline void
+Sun_init_header(struct Sun_nvram *header, const uint8_t *macaddr, int machine_id)
+{
+ uint8_t tmp, *tmpptr;
+ unsigned int i;
+
+ header->type = 1;
+ header->machine_id = machine_id & 0xff;
+ memcpy(&header->macaddr, macaddr, 6);
+ /* Calculate checksum */
+ tmp = 0;
+ tmpptr = (uint8_t *)header;
+ for (i = 0; i < 15; i++)
+ tmp ^= tmpptr[i];
+
+ header->checksum = tmp;
+}
+#endif /* FIRMWARE_ABI_H */
diff --git a/hw/flash.h b/hw/flash.h
new file mode 100644
index 0000000..d7d103e
--- /dev/null
+++ b/hw/flash.h
@@ -0,0 +1,54 @@
+/* NOR flash devices */
+typedef struct pflash_t pflash_t;
+
+/* pflash_cfi01.c */
+pflash_t *pflash_cfi01_register(target_phys_addr_t base, ram_addr_t off,
+ BlockDriverState *bs,
+ uint32_t sector_len, int nb_blocs, int width,
+ uint16_t id0, uint16_t id1,
+ uint16_t id2, uint16_t id3, int be);
+
+/* pflash_cfi02.c */
+pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off,
+ BlockDriverState *bs, uint32_t sector_len,
+ int nb_blocs, int nb_mappings, int width,
+ uint16_t id0, uint16_t id1,
+ uint16_t id2, uint16_t id3,
+ uint16_t unlock_addr0, uint16_t unlock_addr1,
+ int be);
+
+/* nand.c */
+typedef struct NANDFlashState NANDFlashState;
+NANDFlashState *nand_init(int manf_id, int chip_id);
+void nand_done(NANDFlashState *s);
+void nand_setpins(NANDFlashState *s,
+ int cle, int ale, int ce, int wp, int gnd);
+void nand_getpins(NANDFlashState *s, int *rb);
+void nand_setio(NANDFlashState *s, uint8_t value);
+uint8_t nand_getio(NANDFlashState *s);
+
+#define NAND_MFR_TOSHIBA 0x98
+#define NAND_MFR_SAMSUNG 0xec
+#define NAND_MFR_FUJITSU 0x04
+#define NAND_MFR_NATIONAL 0x8f
+#define NAND_MFR_RENESAS 0x07
+#define NAND_MFR_STMICRO 0x20
+#define NAND_MFR_HYNIX 0xad
+#define NAND_MFR_MICRON 0x2c
+
+/* onenand.c */
+void onenand_base_update(void *opaque, target_phys_addr_t new);
+void onenand_base_unmap(void *opaque);
+void *onenand_init(uint32_t id, int regshift, qemu_irq irq);
+void *onenand_raw_otp(void *opaque);
+
+/* ecc.c */
+typedef struct {
+ uint8_t cp; /* Column parity */
+ uint16_t lp[2]; /* Line parity */
+ uint16_t count;
+} ECCState;
+
+uint8_t ecc_digest(ECCState *s, uint8_t sample);
+void ecc_reset(ECCState *s);
+extern VMStateDescription vmstate_ecc_state;
diff --git a/hw/fmopl.c b/hw/fmopl.c
new file mode 100644
index 0000000..3df1806
--- /dev/null
+++ b/hw/fmopl.c
@@ -0,0 +1,1391 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+**
+** Version 0.37a
+**
+*/
+
+/*
+ preliminary :
+ Problem :
+ note:
+*/
+
+/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL.
+ *
+ * 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.1 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/>.
+ */
+
+#define INLINE static inline
+#define HAS_YM3812 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+//#include "driver.h" /* use M.A.M.E. */
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- for debug --------------------- */
+/* #define OPL_OUTPUT_LOG */
+#ifdef OPL_OUTPUT_LOG
+static FILE *opl_dbg_fp = NULL;
+static FM_OPL *opl_dbg_opl[16];
+static int opl_dbg_maxchip,opl_dbg_chip;
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT 4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+
+#define EG_OFF ((2*EG_ENT)<<ENV_BITS) /* OFF */
+#define EG_DED EG_OFF
+#define EG_DST (EG_ENT<<ENV_BITS) /* DECAY START */
+#define EG_AED EG_DST
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
+#define DV (EG_STEP/2)
+static const UINT32 KSL_TABLE[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static INT32 *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static INT32 **SIN_TABLE;
+
+/* LFO table */
+static INT32 *AMS_TABLE;
+static INT32 *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML 2
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32 *ams_table;
+INT32 *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2; /* connect for SLOT 2 */
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+
+//#define LOG_LEVEL LOG_INF
+#define LOG_LEVEL LOG_ERR
+
+//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
+#define LOG(n,x)
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+ if( SLOT->evm > ENV_MOD_RR)
+ {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc&EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+ /* calcrate envelope generator */
+ if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+ {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ)
+ {
+ SLOT->evs = 0;
+ }
+ else
+ {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF+1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm( OPL_CH *CH)
+{
+ INT32 *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = MUL_TABLE[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_typ = (v&0x20)>>5;
+ SLOT->vib = (v&0x40);
+ SLOT->ams = (v&0x80);
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
+
+ if( !(OPL->mode&0x80) )
+ { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ar = v>>4;
+ int dr = v&0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int sl = v>>4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+ UINT32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB)
+ {
+ int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ *CH->connect1 += OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+ UINT32 env_tam,env_sd,env_top,env_hh;
+ int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
+ INT32 tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB)
+ {
+ int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+ }
+ else
+ {
+ feedback2 = OP_OUT(SLOT,env_out,0);
+ }
+ }else
+ {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if( env_out < EG_ENT-1 )
+ {
+ /* PG */
+ if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+ else SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam=OPL_CALC_SLOT(SLOT8_1);
+ env_top=OPL_CALC_SLOT(SLOT8_2);
+ env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+ else SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+ if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+ else SLOT7_2->Cnt += (CH[7].fc*8);
+ if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+ else SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+ else SLOT8_2->Cnt += (CH[8].fc*48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if( env_sd < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+ /* TAM */
+ if( env_tam < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+ /* TOP-CY */
+ if( env_top < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+ /* HH */
+ if( env_hh < EG_ENT-1 )
+ outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4;i <= 60;i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT<<ENV_BITS);
+ OPL->AR_TABLE[i] = rate / ARRATE;
+ OPL->DR_TABLE[i] = rate / DRRATE;
+ }
+ for (i = 60;i < 76;i++)
+ {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+#if 0
+ for (i = 0;i < 64 ;i++){ /* make for overflow area */
+ LOG(LOG_WAR,("rate %2d , ar %f ms , dr %f ms \n",i,
+ ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
+ ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
+ }
+#endif
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+ return 0;
+ if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+ {
+ free(TL_TABLE);
+ return 0;
+ }
+ if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+ {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0;t < EG_ENT-1 ;t++){
+ rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
+/* LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
+ }
+ /* fill volume off area */
+ for ( t = EG_ENT-1; t < TL_MAX ;t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[EG_ENT-1];
+ for (s = 1;s <= SIN_ENT/4;s++){
+ pom = sin(2*PI*s/SIN_ENT); /* sin */
+ pom = 20*log10(1/pom); /* decibel */
+ j = pom / EG_STEP; /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j];
+/* LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+ }
+ for (s = 0;s < SIN_ENT;s++)
+ {
+ SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
+ SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i<EG_ENT; i++)
+ {
+ /* ATTACK curve */
+ pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+ /* make LFO ams table */
+ for (i=0; i<AMS_ENT; i++)
+ {
+ pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+ AMS_TABLE[i] = (1.0/EG_STEP)*pom; /* 1dB */
+ AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i<VIB_ENT; i++)
+ {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = VIB_RATE + (pom*0.07); /* +- 7cent */
+ VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
+ /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
+ }
+ return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+ /* make fnumber -> increment counter table */
+ for( fn=0 ; fn < 1024 ; fn++ )
+ {
+ OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
+ OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
+}
+
+/* ---------- write a OPL registers ---------- */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:controll */
+ switch(r&0x1f)
+ {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ if(!OPL->wavesel)
+ {
+ /* preset compatible mode */
+ int c;
+ for(c=0;c<OPL->max_ch;c++)
+ {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f);
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL,v&0x78);
+ OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2)
+ {
+ double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1)
+ {
+ double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+ }
+ }
+ return;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
+ }
+ return;
+ case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+ case 0x08: /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+ v&=0x1f; /* for DELTA-T unit */
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* EG-CTRL */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ return;
+#if 0
+ case 0x15: /* DAC data */
+ case 0x16:
+ case 0x17: /* SHIFT */
+ return;
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ return;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ return;
+ case 0x1a: /* PCM data */
+ return;
+#endif
+#endif
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r)
+ {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ UINT8 rkey = OPL->rythm^v;
+ OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
+ OPL->rythm = v&0x3f;
+ if(OPL->rythm&0x20)
+ {
+#if 0
+ usrintf_showmessage("OPL Rythm mode select");
+#endif
+ /* BD key on/off */
+ if(rkey&0x10)
+ {
+ if(v&0x10)
+ {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey&0x08)
+ {
+ if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey&0x04)
+ {
+ if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey&0x02)
+ {
+ if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey&0x01)
+ {
+ if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ int keyon = (v>>5)&1;
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+ if(CH->keyon != keyon)
+ {
+ if( (CH->keyon=keyon) )
+ {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else
+ {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ int blockRv = 7-(block_fnum>>10);
+ int fnum = block_fnum&0x3ff;
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = KSL_TABLE[block_fnum>>6];
+ CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+ CH->kcode = CH->block_fnum>>9;
+ if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v>>1)&7;
+ CH->FB = feedback ? (8+1) - feedback : 0;
+ CH->CON = v&1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r&0x1f];
+ if(slot == -1) return;
+ CH = &OPL->P_CH[slot/2];
+ if(OPL->wavesel)
+ {
+ /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !OPLOpenTable() )
+ {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+#if (BUILD_YM3812 || BUILD_YM3526)
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256);
+ }
+#endif
+}
+#endif /* (BUILD_YM3812 || BUILD_YM3526) */
+
+#if BUILD_Y8950
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+ int i;
+ int data;
+ OPLSAMPLE *buf = buffer;
+ UINT32 amsCnt = OPL->amsCnt;
+ UINT32 vibCnt = OPL->vibCnt;
+ UINT8 rythm = OPL->rythm&0x20;
+ OPL_CH *CH,*R_CH;
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ /* setup DELTA-T unit */
+ YM_DELTAT_DECODE_PRESET(DELTAT);
+
+ if( (void *)OPL != cur_chip ){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for( i=0; i < length ; i++ )
+ {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+ vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+ outd[0] = 0;
+ /* deltaT ADPCM */
+ if( DELTAT->portstate )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+ /* FM part */
+ for(CH=S_CH ; CH < R_CH ; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+ /* limit check */
+ data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+ /* store to sound buffer */
+ buf[i] = data >> OPL_OUTSB;
+ }
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+ /* deltaT START flag */
+ if( !DELTAT->portstate )
+ OPL->status &= 0xfe;
+}
+#endif
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+ /* reset OPerator paramater */
+ for( c = 0 ; c < OPL->max_ch ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF+1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = outd;
+ DELTAT->portshift = 5;
+ DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0);
+ }
+#endif
+}
+
+/* ---------- Create one of vietual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+ int max_ch = 9; /* normaly 9 channels */
+
+ if( OPL_LockTable() ==-1) return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+ /* allocate memory block */
+ ptr = malloc(state_size);
+ if(ptr==NULL) return NULL;
+ /* clear */
+ memset(ptr,0,state_size);
+ OPL = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+ if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
+#endif
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+ /* init grobal tables */
+ OPL_initalize(OPL);
+ /* reset chip */
+ OPLResetChip(OPL);
+#ifdef OPL_OUTPUT_LOG
+ if(!opl_dbg_fp)
+ {
+ opl_dbg_fp = fopen("opllog.opl","wb");
+ opl_dbg_maxchip = 0;
+ }
+ if(opl_dbg_fp)
+ {
+ opl_dbg_opl[opl_dbg_maxchip] = OPL;
+ fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip,
+ type,
+ clock&0xff,
+ (clock/0x100)&0xff,
+ (clock/0x10000)&0xff,
+ (clock/0x1000000)&0xff);
+ opl_dbg_maxchip++;
+ }
+#endif
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL)
+{
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ fclose(opl_dbg_fp);
+ opl_dbg_fp = NULL;
+ }
+#endif
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+#if BUILD_Y8950
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
+{
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
+{
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+#endif
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+#ifdef OPL_OUTPUT_LOG
+ if(opl_dbg_fp)
+ {
+ for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+ if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+ fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v);
+ }
+#endif
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ { /* status port */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else {
+ LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+ }
+ }
+ return 0;
+#if 0
+ case 0x0f: /* ADPCM-DATA */
+ return 0;
+#endif
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else {
+ LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+ }
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ return 0;
+ }
+ return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0;ch<9;ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+ return OPL->status>>7;
+}
diff --git a/hw/fmopl.h b/hw/fmopl.h
new file mode 100644
index 0000000..a01ff90
--- /dev/null
+++ b/hw/fmopl.h
@@ -0,0 +1,174 @@
+#ifndef __FMOPL_H_
+#define __FMOPL_H_
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (HAS_YM3812)
+//#define BUILD_YM3526 (HAS_YM3526)
+//#define BUILD_Y8950 (HAS_Y8950)
+
+/* --- system optimize --- */
+/* select bit size of output : 8 or 16 */
+#define OPL_OUTPUT_BIT 16
+
+/* compiler dependence */
+#ifndef OSD_CPU_H
+#define OSD_CPU_H
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+#endif
+
+#if (OPL_OUTPUT_BIT==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_OUTPUT_BIT==8)
+typedef unsigned char OPLSAMPLE;
+#endif
+
+
+#if BUILD_Y8950
+#include "ymdeltat.h"
+#endif
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
+
+/* !!!!! here is private section , do not access there member direct !!!!! */
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ INT32 TL; /* total level :TL << 8 */
+ INT32 TLL; /* adjusted now TL */
+ UINT8 KSR; /* key scale rate :(shift down bit) */
+ INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
+ INT32 SL; /* sustin level :SL_TALBE[SL] */
+ INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
+ UINT8 ksl; /* keyscale level :(shift down bits) */
+ UINT8 ksr; /* key scale rate :kcode>>KSR */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+ UINT32 Cnt; /* frequency count : */
+ UINT32 Incr; /* frequency step : */
+ /* envelope generator state */
+ UINT8 eg_typ; /* envelope type flag */
+ UINT8 evm; /* envelope phase */
+ INT32 evc; /* envelope counter */
+ INT32 eve; /* envelope counter end point */
+ INT32 evs; /* envelope counter step */
+ INT32 evsa; /* envelope step for AR :AR[ksr] */
+ INT32 evsd; /* envelope step for DR :DR[ksr] */
+ INT32 evsr; /* envelope step for RR :RR[ksr] */
+ /* LFO */
+ UINT8 ams; /* ams flag */
+ UINT8 vib; /* vibrate flag */
+ /* wave selector */
+ INT32 **wavetable;
+}OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ UINT8 CON; /* connection type */
+ UINT8 FB; /* feed back :(shift down bit) */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 *connect2; /* slot2 output pointer */
+ INT32 op1_out[2]; /* slot1 output for selfeedback */
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum : */
+ UINT8 kcode; /* key code : KeyScaleCode */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ UINT8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
+ /* Timer */
+ int T[2]; /* timer counter */
+ UINT8 st[2]; /* timer enable */
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+ /* Rythm sention */
+ UINT8 rythm; /* Rythm mode , key flag */
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+ YM_DELTAT *deltat; /* DELTA-T ADPCM */
+#endif
+ /* Keyboard / I/O interface unit (Y8950) */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ int port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ int keyboard_param;
+ /* time tables */
+ INT32 AR_TABLE[75]; /* atttack rate tables */
+ INT32 DR_TABLE[75]; /* decay rate tables */
+ UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
+ /* LFO */
+ INT32 *ams_table;
+ INT32 *vib_table;
+ INT32 amsCnt;
+ INT32 amsIncr;
+ INT32 vibCnt;
+ INT32 vibIncr;
+ /* wave selector enable flag */
+ UINT8 wavesel;
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
+/* Y8950 port handlers */
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL,int a,int v);
+unsigned char OPLRead(FM_OPL *OPL,int a);
+int OPLTimerOver(FM_OPL *OPL,int c);
+
+/* YM3626/YM3812 local section */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+
+#endif
diff --git a/hw/framebuffer.c b/hw/framebuffer.c
new file mode 100644
index 0000000..24cdf25
--- /dev/null
+++ b/hw/framebuffer.c
@@ -0,0 +1,116 @@
+/*
+ * Framebuffer device helper routines
+ *
+ * Copyright (c) 2009 CodeSourcery
+ * Written by Paul Brook <paul@codesourcery.com>
+ *
+ * This code is licensed under the GNU GPLv2.
+ */
+
+/* TODO:
+ - Do something similar for framebuffers with local ram
+ - Handle rotation here instead of hacking dest_pitch
+ - Use common pixel conversion routines instead of per-device drawfn
+ - Remove all DisplayState knowledge from devices.
+ */
+
+#include "hw.h"
+#include "console.h"
+#include "framebuffer.h"
+
+/* Render an image from a shared memory framebuffer. */
+
+void framebuffer_update_display(
+ DisplayState *ds,
+ target_phys_addr_t base,
+ int cols, /* Width in pixels. */
+ int rows, /* Leight in pixels. */
+ int src_width, /* Length of source line, in bytes. */
+ int dest_row_pitch, /* Bytes between adjacent horizontal output pixels. */
+ int dest_col_pitch, /* Bytes between adjacent vertical output pixels. */
+ int invalidate, /* nonzero to redraw the whole image. */
+ drawfn fn,
+ void *opaque,
+ int *first_row, /* Input and output. */
+ int *last_row /* Output only */)
+{
+ target_phys_addr_t src_len;
+ uint8_t *dest;
+ uint8_t *src;
+ uint8_t *src_base;
+ int first, last = 0;
+ int dirty;
+ int i;
+ ram_addr_t addr;
+ ram_addr_t pd;
+ ram_addr_t pd2;
+
+ i = *first_row;
+ *first_row = -1;
+ src_len = src_width * rows;
+
+ cpu_physical_sync_dirty_bitmap(base, base + src_len);
+ pd = cpu_get_physical_page_desc(base);
+ pd2 = cpu_get_physical_page_desc(base + src_len - 1);
+ /* We should reall check that this is a continuous ram region.
+ Instead we just check that the first and last pages are
+ both ram, and the right distance apart. */
+ if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM
+ || (pd2 & ~TARGET_PAGE_MASK) > IO_MEM_ROM) {
+ return;
+ }
+ pd = (pd & TARGET_PAGE_MASK) + (base & ~TARGET_PAGE_MASK);
+ if (((pd + src_len - 1) & TARGET_PAGE_MASK) != (pd2 & TARGET_PAGE_MASK)) {
+ return;
+ }
+
+ src_base = cpu_physical_memory_map(base, &src_len, 0);
+ /* If we can't map the framebuffer then bail. We could try harder,
+ but it's not really worth it as dirty flag tracking will probably
+ already have failed above. */
+ if (!src_base)
+ return;
+ if (src_len != src_width * rows) {
+ cpu_physical_memory_unmap(src_base, src_len, 0, 0);
+ return;
+ }
+ src = src_base;
+ dest = ds_get_data(ds);
+ if (dest_col_pitch < 0)
+ dest -= dest_col_pitch * (cols - 1);
+ first = -1;
+ addr = pd;
+
+ addr += i * src_width;
+ src += i * src_width;
+ dest += i * dest_row_pitch;
+
+ for (; i < rows; i++) {
+ target_phys_addr_t dirty_offset;
+ dirty = 0;
+ dirty_offset = 0;
+ while (addr + dirty_offset < TARGET_PAGE_ALIGN(addr + src_width)) {
+ dirty |= cpu_physical_memory_get_dirty(addr + dirty_offset,
+ VGA_DIRTY_FLAG);
+ dirty_offset += TARGET_PAGE_SIZE;
+ }
+
+ if (dirty || invalidate) {
+ fn(opaque, dest, src, cols, dest_col_pitch);
+ if (first == -1)
+ first = i;
+ last = i;
+ }
+ addr += src_width;
+ src += src_width;
+ dest += dest_row_pitch;
+ }
+ cpu_physical_memory_unmap(src_base, src_len, 0, 0);
+ if (first < 0) {
+ return;
+ }
+ cpu_physical_memory_reset_dirty(pd, pd + src_len, VGA_DIRTY_FLAG);
+ *first_row = first;
+ *last_row = last;
+ return;
+}
diff --git a/hw/framebuffer.h b/hw/framebuffer.h
new file mode 100644
index 0000000..a3a2146
--- /dev/null
+++ b/hw/framebuffer.h
@@ -0,0 +1,22 @@
+#ifndef QEMU_FRAMEBUFFER_H
+#define QEMU_FRAMEBUFFER_H
+
+/* Framebuffer device helper routines. */
+
+typedef void (*drawfn)(void *, uint8_t *, const uint8_t *, int, int);
+
+void framebuffer_update_display(
+ DisplayState *ds,
+ target_phys_addr_t base,
+ int cols,
+ int rows,
+ int src_width,
+ int dest_row_pitch,
+ int dest_col_pitch,
+ int invalidate,
+ drawfn fn,
+ void *opaque,
+ int *first_row,
+ int *last_row);
+
+#endif
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
new file mode 100644
index 0000000..85c8c3c
--- /dev/null
+++ b/hw/fw_cfg.c
@@ -0,0 +1,407 @@
+/*
+ * QEMU Firmware configuration device emulation
+ *
+ * Copyright (c) 2008 Gleb Natapov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "sysemu.h"
+#include "isa.h"
+#include "fw_cfg.h"
+#include "sysbus.h"
+
+/* debug firmware config */
+//#define DEBUG_FW_CFG
+
+#ifdef DEBUG_FW_CFG
+#define FW_CFG_DPRINTF(fmt, ...) \
+ do { printf("FW_CFG: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define FW_CFG_DPRINTF(fmt, ...)
+#endif
+
+#define FW_CFG_SIZE 2
+
+typedef struct FWCfgEntry {
+ uint32_t len;
+ uint8_t *data;
+ void *callback_opaque;
+ FWCfgCallback callback;
+} FWCfgEntry;
+
+struct FWCfgState {
+ SysBusDevice busdev;
+ uint32_t ctl_iobase, data_iobase;
+ FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
+ FWCfgFiles *files;
+ uint16_t cur_entry;
+ uint32_t cur_offset;
+ Notifier machine_ready;
+};
+
+static void fw_cfg_write(FWCfgState *s, uint8_t value)
+{
+ int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+ FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+
+ FW_CFG_DPRINTF("write %d\n", value);
+
+ if (s->cur_entry & FW_CFG_WRITE_CHANNEL && s->cur_offset < e->len) {
+ e->data[s->cur_offset++] = value;
+ if (s->cur_offset == e->len) {
+ e->callback(e->callback_opaque, e->data);
+ s->cur_offset = 0;
+ }
+ }
+}
+
+static int fw_cfg_select(FWCfgState *s, uint16_t key)
+{
+ int ret;
+
+ s->cur_offset = 0;
+ if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
+ s->cur_entry = FW_CFG_INVALID;
+ ret = 0;
+ } else {
+ s->cur_entry = key;
+ ret = 1;
+ }
+
+ FW_CFG_DPRINTF("select key %d (%sfound)\n", key, ret ? "" : "not ");
+
+ return ret;
+}
+
+static uint8_t fw_cfg_read(FWCfgState *s)
+{
+ int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+ FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+ uint8_t ret;
+
+ if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
+ ret = 0;
+ else
+ ret = e->data[s->cur_offset++];
+
+ FW_CFG_DPRINTF("read %d\n", ret);
+
+ return ret;
+}
+
+static uint32_t fw_cfg_io_readb(void *opaque, uint32_t addr)
+{
+ return fw_cfg_read(opaque);
+}
+
+static void fw_cfg_io_writeb(void *opaque, uint32_t addr, uint32_t value)
+{
+ fw_cfg_write(opaque, (uint8_t)value);
+}
+
+static void fw_cfg_io_writew(void *opaque, uint32_t addr, uint32_t value)
+{
+ fw_cfg_select(opaque, (uint16_t)value);
+}
+
+static uint32_t fw_cfg_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return fw_cfg_read(opaque);
+}
+
+static void fw_cfg_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ fw_cfg_write(opaque, (uint8_t)value);
+}
+
+static void fw_cfg_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ fw_cfg_select(opaque, (uint16_t)value);
+}
+
+static CPUReadMemoryFunc * const fw_cfg_ctl_mem_read[3] = {
+ NULL,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const fw_cfg_ctl_mem_write[3] = {
+ NULL,
+ fw_cfg_mem_writew,
+ NULL,
+};
+
+static CPUReadMemoryFunc * const fw_cfg_data_mem_read[3] = {
+ fw_cfg_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const fw_cfg_data_mem_write[3] = {
+ fw_cfg_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static void fw_cfg_reset(DeviceState *d)
+{
+ FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d);
+
+ fw_cfg_select(s, 0);
+}
+
+/* Save restore 32 bit int as uint16_t
+ This is a Big hack, but it is how the old state did it.
+ Or we broke compatibility in the state, or we can't use struct tm
+ */
+
+static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ uint32_t *v = pv;
+ *v = qemu_get_be16(f);
+ return 0;
+}
+
+static void put_unused(QEMUFile *f, void *pv, size_t size)
+{
+ fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
+ fprintf(stderr, "This functions shouldn't be called.\n");
+}
+
+static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
+ .name = "int32_as_uint16",
+ .get = get_uint32_as_uint16,
+ .put = put_unused,
+};
+
+#define VMSTATE_UINT16_HACK(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint32_as_uint16, uint32_t)
+
+
+static bool is_version_1(void *opaque, int version_id)
+{
+ return version_id == 1;
+}
+
+static const VMStateDescription vmstate_fw_cfg = {
+ .name = "fw_cfg",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(cur_entry, FWCfgState),
+ VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
+ VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+int fw_cfg_add_bytes(FWCfgState *s, uint16_t key, uint8_t *data, uint32_t len)
+{
+ int arch = !!(key & FW_CFG_ARCH_LOCAL);
+
+ key &= FW_CFG_ENTRY_MASK;
+
+ if (key >= FW_CFG_MAX_ENTRY)
+ return 0;
+
+ s->entries[arch][key].data = data;
+ s->entries[arch][key].len = len;
+
+ return 1;
+}
+
+int fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
+{
+ uint16_t *copy;
+
+ copy = qemu_malloc(sizeof(value));
+ *copy = cpu_to_le16(value);
+ return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value));
+}
+
+int fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
+{
+ uint32_t *copy;
+
+ copy = qemu_malloc(sizeof(value));
+ *copy = cpu_to_le32(value);
+ return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value));
+}
+
+int fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
+{
+ uint64_t *copy;
+
+ copy = qemu_malloc(sizeof(value));
+ *copy = cpu_to_le64(value);
+ return fw_cfg_add_bytes(s, key, (uint8_t *)copy, sizeof(value));
+}
+
+int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
+ void *callback_opaque, uint8_t *data, size_t len)
+{
+ int arch = !!(key & FW_CFG_ARCH_LOCAL);
+
+ if (!(key & FW_CFG_WRITE_CHANNEL))
+ return 0;
+
+ key &= FW_CFG_ENTRY_MASK;
+
+ if (key >= FW_CFG_MAX_ENTRY || len > 65535)
+ return 0;
+
+ s->entries[arch][key].data = data;
+ s->entries[arch][key].len = len;
+ s->entries[arch][key].callback_opaque = callback_opaque;
+ s->entries[arch][key].callback = callback;
+
+ return 1;
+}
+
+int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data,
+ uint32_t len)
+{
+ int i, index;
+
+ if (!s->files) {
+ int dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
+ s->files = qemu_mallocz(dsize);
+ fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, (uint8_t*)s->files, dsize);
+ }
+
+ index = be32_to_cpu(s->files->count);
+ if (index == FW_CFG_FILE_SLOTS) {
+ fprintf(stderr, "fw_cfg: out of file slots\n");
+ return 0;
+ }
+
+ fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len);
+
+ pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
+ filename);
+ for (i = 0; i < index; i++) {
+ if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
+ FW_CFG_DPRINTF("%s: skip duplicate: %s\n", __FUNCTION__,
+ s->files->f[index].name);
+ return 1;
+ }
+ }
+
+ s->files->f[index].size = cpu_to_be32(len);
+ s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
+ FW_CFG_DPRINTF("%s: #%d: %s (%d bytes)\n", __FUNCTION__,
+ index, s->files->f[index].name, len);
+
+ s->files->count = cpu_to_be32(index+1);
+ return 1;
+}
+
+static void fw_cfg_machine_ready(struct Notifier* n)
+{
+ uint32_t len;
+ FWCfgState *s = container_of(n, FWCfgState, machine_ready);
+ char *bootindex = get_boot_devices_list(&len);
+
+ fw_cfg_add_file(s, "bootorder", (uint8_t*)bootindex, len);
+}
+
+FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
+ target_phys_addr_t ctl_addr, target_phys_addr_t data_addr)
+{
+ DeviceState *dev;
+ SysBusDevice *d;
+ FWCfgState *s;
+
+ dev = qdev_create(NULL, "fw_cfg");
+ qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port);
+ qdev_prop_set_uint32(dev, "data_iobase", data_port);
+ qdev_init_nofail(dev);
+ d = sysbus_from_qdev(dev);
+
+ s = DO_UPCAST(FWCfgState, busdev.qdev, dev);
+
+ if (ctl_addr) {
+ sysbus_mmio_map(d, 0, ctl_addr);
+ }
+ if (data_addr) {
+ sysbus_mmio_map(d, 1, data_addr);
+ }
+ fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (uint8_t *)"QEMU", 4);
+ fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
+ fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
+ fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
+ fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
+ fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
+
+
+ s->machine_ready.notify = fw_cfg_machine_ready;
+ qemu_add_machine_init_done_notifier(&s->machine_ready);
+
+ return s;
+}
+
+static int fw_cfg_init1(SysBusDevice *dev)
+{
+ FWCfgState *s = FROM_SYSBUS(FWCfgState, dev);
+ int io_ctl_memory, io_data_memory;
+
+ io_ctl_memory = cpu_register_io_memory(fw_cfg_ctl_mem_read,
+ fw_cfg_ctl_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, FW_CFG_SIZE, io_ctl_memory);
+
+ io_data_memory = cpu_register_io_memory(fw_cfg_data_mem_read,
+ fw_cfg_data_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, FW_CFG_SIZE, io_data_memory);
+
+ if (s->ctl_iobase) {
+ register_ioport_write(s->ctl_iobase, 2, 2, fw_cfg_io_writew, s);
+ }
+ if (s->data_iobase) {
+ register_ioport_read(s->data_iobase, 1, 1, fw_cfg_io_readb, s);
+ register_ioport_write(s->data_iobase, 1, 1, fw_cfg_io_writeb, s);
+ }
+ return 0;
+}
+
+static SysBusDeviceInfo fw_cfg_info = {
+ .init = fw_cfg_init1,
+ .qdev.name = "fw_cfg",
+ .qdev.size = sizeof(FWCfgState),
+ .qdev.vmsd = &vmstate_fw_cfg,
+ .qdev.reset = fw_cfg_reset,
+ .qdev.no_user = 1,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1),
+ DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void fw_cfg_register_devices(void)
+{
+ sysbus_register_withprop(&fw_cfg_info);
+}
+
+device_init(fw_cfg_register_devices)
diff --git a/hw/fw_cfg.h b/hw/fw_cfg.h
new file mode 100644
index 0000000..856bf91
--- /dev/null
+++ b/hw/fw_cfg.h
@@ -0,0 +1,70 @@
+#ifndef FW_CFG_H
+#define FW_CFG_H
+
+#define FW_CFG_SIGNATURE 0x00
+#define FW_CFG_ID 0x01
+#define FW_CFG_UUID 0x02
+#define FW_CFG_RAM_SIZE 0x03
+#define FW_CFG_NOGRAPHIC 0x04
+#define FW_CFG_NB_CPUS 0x05
+#define FW_CFG_MACHINE_ID 0x06
+#define FW_CFG_KERNEL_ADDR 0x07
+#define FW_CFG_KERNEL_SIZE 0x08
+#define FW_CFG_KERNEL_CMDLINE 0x09
+#define FW_CFG_INITRD_ADDR 0x0a
+#define FW_CFG_INITRD_SIZE 0x0b
+#define FW_CFG_BOOT_DEVICE 0x0c
+#define FW_CFG_NUMA 0x0d
+#define FW_CFG_BOOT_MENU 0x0e
+#define FW_CFG_MAX_CPUS 0x0f
+#define FW_CFG_KERNEL_ENTRY 0x10
+#define FW_CFG_KERNEL_DATA 0x11
+#define FW_CFG_INITRD_DATA 0x12
+#define FW_CFG_CMDLINE_ADDR 0x13
+#define FW_CFG_CMDLINE_SIZE 0x14
+#define FW_CFG_CMDLINE_DATA 0x15
+#define FW_CFG_SETUP_ADDR 0x16
+#define FW_CFG_SETUP_SIZE 0x17
+#define FW_CFG_SETUP_DATA 0x18
+#define FW_CFG_FILE_DIR 0x19
+
+#define FW_CFG_FILE_FIRST 0x20
+#define FW_CFG_FILE_SLOTS 0x10
+#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS)
+
+#define FW_CFG_WRITE_CHANNEL 0x4000
+#define FW_CFG_ARCH_LOCAL 0x8000
+#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
+
+#define FW_CFG_INVALID 0xffff
+
+#ifndef NO_QEMU_PROTOS
+typedef struct FWCfgFile {
+ uint32_t size; /* file size */
+ uint16_t select; /* write this to 0x510 to read it */
+ uint16_t reserved;
+ char name[56];
+} FWCfgFile;
+
+typedef struct FWCfgFiles {
+ uint32_t count;
+ FWCfgFile f[];
+} FWCfgFiles;
+
+typedef void (*FWCfgCallback)(void *opaque, uint8_t *data);
+
+typedef struct FWCfgState FWCfgState;
+int fw_cfg_add_bytes(FWCfgState *s, uint16_t key, uint8_t *data, uint32_t len);
+int fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value);
+int fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value);
+int fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value);
+int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
+ void *callback_opaque, uint8_t *data, size_t len);
+int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data,
+ uint32_t len);
+FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
+ target_phys_addr_t crl_addr, target_phys_addr_t data_addr);
+
+#endif /* NO_QEMU_PROTOS */
+
+#endif
diff --git a/hw/g364fb.c b/hw/g364fb.c
new file mode 100644
index 0000000..a41e988
--- /dev/null
+++ b/hw/g364fb.c
@@ -0,0 +1,615 @@
+/*
+ * QEMU G364 framebuffer Emulator.
+ *
+ * Copyright (c) 2007-2009 Herve Poussineau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "mips.h"
+#include "console.h"
+#include "pixel_ops.h"
+
+//#define DEBUG_G364
+
+#ifdef DEBUG_G364
+#define DPRINTF(fmt, ...) \
+do { printf("g364: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "g364 ERROR: " fmt , ## __VA_ARGS__);} while (0)
+
+typedef struct G364State {
+ /* hardware */
+ uint8_t *vram;
+ ram_addr_t vram_offset;
+ int vram_size;
+ qemu_irq irq;
+ /* registers */
+ uint8_t color_palette[256][3];
+ uint8_t cursor_palette[3][3];
+ uint16_t cursor[512];
+ uint32_t cursor_position;
+ uint32_t ctla;
+ uint32_t top_of_screen;
+ uint32_t width, height; /* in pixels */
+ /* display refresh support */
+ DisplayState *ds;
+ int depth;
+ int blanked;
+} G364State;
+
+#define REG_ID 0x000000
+#define REG_BOOT 0x080000
+#define REG_DISPLAY 0x080118
+#define REG_VDISPLAY 0x080150
+#define REG_CTLA 0x080300
+#define REG_TOP 0x080400
+#define REG_CURS_PAL 0x080508
+#define REG_CURS_POS 0x080638
+#define REG_CLR_PAL 0x080800
+#define REG_CURS_PAT 0x081000
+#define REG_RESET 0x180000
+
+#define CTLA_FORCE_BLANK 0x00000400
+#define CTLA_NO_CURSOR 0x00800000
+
+static inline int check_dirty(ram_addr_t page)
+{
+ return cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
+}
+
+static inline void reset_dirty(G364State *s,
+ ram_addr_t page_min, ram_addr_t page_max)
+{
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE - 1,
+ VGA_DIRTY_FLAG);
+}
+
+static void g364fb_draw_graphic8(G364State *s)
+{
+ int i, w;
+ uint8_t *vram;
+ uint8_t *data_display, *dd;
+ ram_addr_t page, page_min, page_max;
+ int x, y;
+ int xmin, xmax;
+ int ymin, ymax;
+ int xcursor, ycursor;
+ unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 8:
+ rgb_to_pixel = rgb_to_pixel8;
+ w = 1;
+ break;
+ case 15:
+ rgb_to_pixel = rgb_to_pixel15;
+ w = 2;
+ break;
+ case 16:
+ rgb_to_pixel = rgb_to_pixel16;
+ w = 2;
+ break;
+ case 32:
+ rgb_to_pixel = rgb_to_pixel32;
+ w = 4;
+ break;
+ default:
+ BADF("unknown host depth %d\n", ds_get_bits_per_pixel(s->ds));
+ return;
+ }
+
+ page = s->vram_offset;
+ page_min = (ram_addr_t)-1;
+ page_max = 0;
+
+ x = y = 0;
+ xmin = s->width;
+ xmax = 0;
+ ymin = s->height;
+ ymax = 0;
+
+ if (!(s->ctla & CTLA_NO_CURSOR)) {
+ xcursor = s->cursor_position >> 12;
+ ycursor = s->cursor_position & 0xfff;
+ } else {
+ xcursor = ycursor = -65;
+ }
+
+ vram = s->vram + s->top_of_screen;
+ /* XXX: out of range in vram? */
+ data_display = dd = ds_get_data(s->ds);
+ while (y < s->height) {
+ if (check_dirty(page)) {
+ if (y < ymin)
+ ymin = ymax = y;
+ if (page_min == (ram_addr_t)-1)
+ page_min = page;
+ page_max = page;
+ if (x < xmin)
+ xmin = x;
+ for (i = 0; i < TARGET_PAGE_SIZE; i++) {
+ uint8_t index;
+ unsigned int color;
+ if (unlikely((y >= ycursor && y < ycursor + 64) &&
+ (x >= xcursor && x < xcursor + 64))) {
+ /* pointer area */
+ int xdiff = x - xcursor;
+ uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8];
+ int op = (curs >> ((xdiff & 7) * 2)) & 3;
+ if (likely(op == 0)) {
+ /* transparent */
+ index = *vram;
+ color = (*rgb_to_pixel)(
+ s->color_palette[index][0],
+ s->color_palette[index][1],
+ s->color_palette[index][2]);
+ } else {
+ /* get cursor color */
+ index = op - 1;
+ color = (*rgb_to_pixel)(
+ s->cursor_palette[index][0],
+ s->cursor_palette[index][1],
+ s->cursor_palette[index][2]);
+ }
+ } else {
+ /* normal area */
+ index = *vram;
+ color = (*rgb_to_pixel)(
+ s->color_palette[index][0],
+ s->color_palette[index][1],
+ s->color_palette[index][2]);
+ }
+ memcpy(dd, &color, w);
+ dd += w;
+ x++;
+ vram++;
+ if (x == s->width) {
+ xmax = s->width - 1;
+ y++;
+ if (y == s->height) {
+ ymax = s->height - 1;
+ goto done;
+ }
+ data_display = dd = data_display + ds_get_linesize(s->ds);
+ xmin = 0;
+ x = 0;
+ }
+ }
+ if (x > xmax)
+ xmax = x;
+ if (y > ymax)
+ ymax = y;
+ } else {
+ int dy;
+ if (page_min != (ram_addr_t)-1) {
+ reset_dirty(s, page_min, page_max);
+ page_min = (ram_addr_t)-1;
+ page_max = 0;
+ dpy_update(s->ds, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
+ xmin = s->width;
+ xmax = 0;
+ ymin = s->height;
+ ymax = 0;
+ }
+ x += TARGET_PAGE_SIZE;
+ dy = x / s->width;
+ x = x % s->width;
+ y += dy;
+ vram += TARGET_PAGE_SIZE;
+ data_display += dy * ds_get_linesize(s->ds);
+ dd = data_display + x * w;
+ }
+ page += TARGET_PAGE_SIZE;
+ }
+
+done:
+ if (page_min != (ram_addr_t)-1) {
+ dpy_update(s->ds, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
+ reset_dirty(s, page_min, page_max);
+ }
+}
+
+static void g364fb_draw_blank(G364State *s)
+{
+ int i, w;
+ uint8_t *d;
+
+ if (s->blanked) {
+ /* Screen is already blank. No need to redraw it */
+ return;
+ }
+
+ w = s->width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
+ d = ds_get_data(s->ds);
+ for (i = 0; i < s->height; i++) {
+ memset(d, 0, w);
+ d += ds_get_linesize(s->ds);
+ }
+
+ dpy_update(s->ds, 0, 0, s->width, s->height);
+ s->blanked = 1;
+}
+
+static void g364fb_update_display(void *opaque)
+{
+ G364State *s = opaque;
+
+ if (s->width == 0 || s->height == 0)
+ return;
+
+ if (s->width != ds_get_width(s->ds) || s->height != ds_get_height(s->ds)) {
+ qemu_console_resize(s->ds, s->width, s->height);
+ }
+
+ if (s->ctla & CTLA_FORCE_BLANK) {
+ g364fb_draw_blank(s);
+ } else if (s->depth == 8) {
+ g364fb_draw_graphic8(s);
+ } else {
+ BADF("unknown guest depth %d\n", s->depth);
+ }
+
+ qemu_irq_raise(s->irq);
+}
+
+static inline void g364fb_invalidate_display(void *opaque)
+{
+ G364State *s = opaque;
+ int i;
+
+ s->blanked = 0;
+ for (i = 0; i < s->vram_size; i += TARGET_PAGE_SIZE) {
+ cpu_physical_memory_set_dirty(s->vram_offset + i);
+ }
+}
+
+static void g364fb_reset(void *opaque)
+{
+ G364State *s = opaque;
+ qemu_irq_lower(s->irq);
+
+ memset(s->color_palette, 0, sizeof(s->color_palette));
+ memset(s->cursor_palette, 0, sizeof(s->cursor_palette));
+ memset(s->cursor, 0, sizeof(s->cursor));
+ s->cursor_position = 0;
+ s->ctla = 0;
+ s->top_of_screen = 0;
+ s->width = s->height = 0;
+ memset(s->vram, 0, s->vram_size);
+ g364fb_invalidate_display(opaque);
+}
+
+static void g364fb_screen_dump(void *opaque, const char *filename)
+{
+ G364State *s = opaque;
+ int y, x;
+ uint8_t index;
+ uint8_t *data_buffer;
+ FILE *f;
+
+ if (s->depth != 8) {
+ BADF("unknown guest depth %d\n", s->depth);
+ return;
+ }
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return;
+
+ if (s->ctla & CTLA_FORCE_BLANK) {
+ /* blank screen */
+ fprintf(f, "P4\n%d %d\n",
+ s->width, s->height);
+ for (y = 0; y < s->height; y++)
+ for (x = 0; x < s->width; x++)
+ fputc(0, f);
+ } else {
+ data_buffer = s->vram + s->top_of_screen;
+ fprintf(f, "P6\n%d %d\n%d\n",
+ s->width, s->height, 255);
+ for (y = 0; y < s->height; y++)
+ for (x = 0; x < s->width; x++, data_buffer++) {
+ index = *data_buffer;
+ fputc(s->color_palette[index][0], f);
+ fputc(s->color_palette[index][1], f);
+ fputc(s->color_palette[index][2], f);
+ }
+ }
+
+ fclose(f);
+}
+
+/* called for accesses to io ports */
+static uint32_t g364fb_ctrl_readl(void *opaque, target_phys_addr_t addr)
+{
+ G364State *s = opaque;
+ uint32_t val;
+
+ if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
+ /* cursor pattern */
+ int idx = (addr - REG_CURS_PAT) >> 3;
+ val = s->cursor[idx];
+ } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
+ /* cursor palette */
+ int idx = (addr - REG_CURS_PAL) >> 3;
+ val = ((uint32_t)s->cursor_palette[idx][0] << 16);
+ val |= ((uint32_t)s->cursor_palette[idx][1] << 8);
+ val |= ((uint32_t)s->cursor_palette[idx][2] << 0);
+ } else {
+ switch (addr) {
+ case REG_ID:
+ val = 0x10; /* Mips G364 */
+ break;
+ case REG_DISPLAY:
+ val = s->width / 4;
+ break;
+ case REG_VDISPLAY:
+ val = s->height * 2;
+ break;
+ case REG_CTLA:
+ val = s->ctla;
+ break;
+ default:
+ {
+ BADF("invalid read at [" TARGET_FMT_plx "]\n", addr);
+ val = 0;
+ break;
+ }
+ }
+ }
+
+ DPRINTF("read 0x%08x at [" TARGET_FMT_plx "]\n", val, addr);
+
+ return val;
+}
+
+static uint32_t g364fb_ctrl_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v = g364fb_ctrl_readl(opaque, addr & ~0x3);
+ if (addr & 0x2)
+ return v >> 16;
+ else
+ return v & 0xffff;
+}
+
+static uint32_t g364fb_ctrl_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v = g364fb_ctrl_readl(opaque, addr & ~0x3);
+ return (v >> (8 * (addr & 0x3))) & 0xff;
+}
+
+static void g364fb_update_depth(G364State *s)
+{
+ static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 };
+ s->depth = depths[(s->ctla & 0x00700000) >> 20];
+}
+
+static void g364_invalidate_cursor_position(G364State *s)
+{
+ int ymin, ymax, start, end, i;
+
+ /* invalidate only near the cursor */
+ ymin = s->cursor_position & 0xfff;
+ ymax = MIN(s->height, ymin + 64);
+ start = ymin * ds_get_linesize(s->ds);
+ end = (ymax + 1) * ds_get_linesize(s->ds);
+
+ for (i = start; i < end; i += TARGET_PAGE_SIZE) {
+ cpu_physical_memory_set_dirty(s->vram_offset + i);
+ }
+}
+
+static void g364fb_ctrl_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ G364State *s = opaque;
+
+ DPRINTF("write 0x%08x at [" TARGET_FMT_plx "]\n", val, addr);
+
+ if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) {
+ /* color palette */
+ int idx = (addr - REG_CLR_PAL) >> 3;
+ s->color_palette[idx][0] = (val >> 16) & 0xff;
+ s->color_palette[idx][1] = (val >> 8) & 0xff;
+ s->color_palette[idx][2] = val & 0xff;
+ g364fb_invalidate_display(s);
+ } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
+ /* cursor pattern */
+ int idx = (addr - REG_CURS_PAT) >> 3;
+ s->cursor[idx] = val;
+ g364fb_invalidate_display(s);
+ } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
+ /* cursor palette */
+ int idx = (addr - REG_CURS_PAL) >> 3;
+ s->cursor_palette[idx][0] = (val >> 16) & 0xff;
+ s->cursor_palette[idx][1] = (val >> 8) & 0xff;
+ s->cursor_palette[idx][2] = val & 0xff;
+ g364fb_invalidate_display(s);
+ } else {
+ switch (addr) {
+ case REG_ID: /* Card identifier; read-only */
+ case REG_BOOT: /* Boot timing */
+ case 0x80108: /* Line timing: half sync */
+ case 0x80110: /* Line timing: back porch */
+ case 0x80120: /* Line timing: short display */
+ case 0x80128: /* Frame timing: broad pulse */
+ case 0x80130: /* Frame timing: v sync */
+ case 0x80138: /* Frame timing: v preequalise */
+ case 0x80140: /* Frame timing: v postequalise */
+ case 0x80148: /* Frame timing: v blank */
+ case 0x80158: /* Line timing: line time */
+ case 0x80160: /* Frame store: line start */
+ case 0x80168: /* vram cycle: mem init */
+ case 0x80170: /* vram cycle: transfer delay */
+ case 0x80200: /* vram cycle: mask register */
+ /* ignore */
+ break;
+ case REG_TOP:
+ s->top_of_screen = val;
+ g364fb_invalidate_display(s);
+ break;
+ case REG_DISPLAY:
+ s->width = val * 4;
+ break;
+ case REG_VDISPLAY:
+ s->height = val / 2;
+ break;
+ case REG_CTLA:
+ s->ctla = val;
+ g364fb_update_depth(s);
+ g364fb_invalidate_display(s);
+ break;
+ case REG_CURS_POS:
+ g364_invalidate_cursor_position(s);
+ s->cursor_position = val;
+ g364_invalidate_cursor_position(s);
+ break;
+ case REG_RESET:
+ g364fb_reset(s);
+ break;
+ default:
+ BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr);
+ break;
+ }
+ }
+ qemu_irq_lower(s->irq);
+}
+
+static void g364fb_ctrl_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ uint32_t old_val = g364fb_ctrl_readl(opaque, addr & ~0x3);
+
+ if (addr & 0x2)
+ val = (val << 16) | (old_val & 0x0000ffff);
+ else
+ val = val | (old_val & 0xffff0000);
+ g364fb_ctrl_writel(opaque, addr & ~0x3, val);
+}
+
+static void g364fb_ctrl_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ uint32_t old_val = g364fb_ctrl_readl(opaque, addr & ~0x3);
+
+ switch (addr & 3) {
+ case 0:
+ val = val | (old_val & 0xffffff00);
+ break;
+ case 1:
+ val = (val << 8) | (old_val & 0xffff00ff);
+ break;
+ case 2:
+ val = (val << 16) | (old_val & 0xff00ffff);
+ break;
+ case 3:
+ val = (val << 24) | (old_val & 0x00ffffff);
+ break;
+ }
+ g364fb_ctrl_writel(opaque, addr & ~0x3, val);
+}
+
+static CPUReadMemoryFunc * const g364fb_ctrl_read[3] = {
+ g364fb_ctrl_readb,
+ g364fb_ctrl_readw,
+ g364fb_ctrl_readl,
+};
+
+static CPUWriteMemoryFunc * const g364fb_ctrl_write[3] = {
+ g364fb_ctrl_writeb,
+ g364fb_ctrl_writew,
+ g364fb_ctrl_writel,
+};
+
+static int g364fb_load(QEMUFile *f, void *opaque, int version_id)
+{
+ G364State *s = opaque;
+ unsigned int i, vram_size;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ vram_size = qemu_get_be32(f);
+ if (vram_size < s->vram_size)
+ return -EINVAL;
+ qemu_get_buffer(f, s->vram, s->vram_size);
+ for (i = 0; i < 256; i++)
+ qemu_get_buffer(f, s->color_palette[i], 3);
+ for (i = 0; i < 3; i++)
+ qemu_get_buffer(f, s->cursor_palette[i], 3);
+ qemu_get_buffer(f, (uint8_t *)s->cursor, sizeof(s->cursor));
+ s->cursor_position = qemu_get_be32(f);
+ s->ctla = qemu_get_be32(f);
+ s->top_of_screen = qemu_get_be32(f);
+ s->width = qemu_get_be32(f);
+ s->height = qemu_get_be32(f);
+
+ /* force refresh */
+ g364fb_update_depth(s);
+ g364fb_invalidate_display(s);
+
+ return 0;
+}
+
+static void g364fb_save(QEMUFile *f, void *opaque)
+{
+ G364State *s = opaque;
+ int i;
+
+ qemu_put_be32(f, s->vram_size);
+ qemu_put_buffer(f, s->vram, s->vram_size);
+ for (i = 0; i < 256; i++)
+ qemu_put_buffer(f, s->color_palette[i], 3);
+ for (i = 0; i < 3; i++)
+ qemu_put_buffer(f, s->cursor_palette[i], 3);
+ qemu_put_buffer(f, (uint8_t *)s->cursor, sizeof(s->cursor));
+ qemu_put_be32(f, s->cursor_position);
+ qemu_put_be32(f, s->ctla);
+ qemu_put_be32(f, s->top_of_screen);
+ qemu_put_be32(f, s->width);
+ qemu_put_be32(f, s->height);
+}
+
+int g364fb_mm_init(target_phys_addr_t vram_base,
+ target_phys_addr_t ctrl_base, int it_shift,
+ qemu_irq irq)
+{
+ G364State *s;
+ int io_ctrl;
+
+ s = qemu_mallocz(sizeof(G364State));
+
+ s->vram_size = 8 * 1024 * 1024;
+ s->vram_offset = qemu_ram_alloc(NULL, "g364fb.vram", s->vram_size);
+ s->vram = qemu_get_ram_ptr(s->vram_offset);
+ s->irq = irq;
+
+ qemu_register_reset(g364fb_reset, s);
+ register_savevm(NULL, "g364fb", 0, 1, g364fb_save, g364fb_load, s);
+ g364fb_reset(s);
+
+ s->ds = graphic_console_init(g364fb_update_display,
+ g364fb_invalidate_display,
+ g364fb_screen_dump, NULL, s);
+
+ cpu_register_physical_memory(vram_base, s->vram_size, s->vram_offset);
+
+ io_ctrl = cpu_register_io_memory(g364fb_ctrl_read, g364fb_ctrl_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(ctrl_base, 0x200000, io_ctrl);
+
+ return 0;
+}
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
new file mode 100644
index 0000000..bd3d6b0
--- /dev/null
+++ b/hw/grackle_pci.c
@@ -0,0 +1,147 @@
+/*
+ * QEMU Grackle PCI host (heathrow OldWorld PowerMac)
+ *
+ * Copyright (c) 2006-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "ppc_mac.h"
+#include "pci.h"
+#include "pci_host.h"
+
+/* debug Grackle */
+//#define DEBUG_GRACKLE
+
+#ifdef DEBUG_GRACKLE
+#define GRACKLE_DPRINTF(fmt, ...) \
+ do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define GRACKLE_DPRINTF(fmt, ...)
+#endif
+
+typedef struct GrackleState {
+ SysBusDevice busdev;
+ PCIHostState host_state;
+} GrackleState;
+
+/* Don't know if this matches real hardware, but it agrees with OHW. */
+static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return (irq_num + (pci_dev->devfn >> 3)) & 3;
+}
+
+static void pci_grackle_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+
+ GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level);
+ qemu_set_irq(pic[irq_num + 0x15], level);
+}
+
+static void pci_grackle_save(QEMUFile* f, void *opaque)
+{
+ PCIDevice *d = opaque;
+
+ pci_device_save(d, f);
+}
+
+static int pci_grackle_load(QEMUFile* f, void *opaque, int version_id)
+{
+ PCIDevice *d = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ return pci_device_load(d, f);
+}
+
+static void pci_grackle_reset(void *opaque)
+{
+}
+
+PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ GrackleState *d;
+
+ dev = qdev_create(NULL, "grackle");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ d = FROM_SYSBUS(GrackleState, s);
+ d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
+ pci_grackle_set_irq,
+ pci_grackle_map_irq,
+ pic, 0, 4);
+
+ pci_create_simple(d->host_state.bus, 0, "grackle");
+
+ sysbus_mmio_map(s, 0, base);
+ sysbus_mmio_map(s, 1, base + 0x00200000);
+
+ return d->host_state.bus;
+}
+
+static int pci_grackle_init_device(SysBusDevice *dev)
+{
+ GrackleState *s;
+ int pci_mem_config, pci_mem_data;
+
+ s = FROM_SYSBUS(GrackleState, dev);
+
+ pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ pci_mem_data = pci_host_data_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+
+ register_savevm(&dev->qdev, "grackle", 0, 1, pci_grackle_save,
+ pci_grackle_load, &s->host_state);
+ qemu_register_reset(pci_grackle_reset, &s->host_state);
+ return 0;
+}
+
+static int grackle_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MOTOROLA);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_MOTOROLA_MPC106);
+ d->config[0x08] = 0x00; // revision
+ d->config[0x09] = 0x01;
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ return 0;
+}
+
+static PCIDeviceInfo grackle_pci_host_info = {
+ .qdev.name = "grackle",
+ .qdev.size = sizeof(PCIDevice),
+ .init = grackle_pci_host_init,
+};
+
+static void grackle_register_devices(void)
+{
+ sysbus_register_dev("grackle", sizeof(GrackleState),
+ pci_grackle_init_device);
+ pci_qdev_register(&grackle_pci_host_info);
+}
+
+device_init(grackle_register_devices)
diff --git a/hw/grlib.h b/hw/grlib.h
new file mode 100644
index 0000000..fdf4b11
--- /dev/null
+++ b/hw/grlib.h
@@ -0,0 +1,126 @@
+/*
+ * QEMU GRLIB Components
+ *
+ * Copyright (c) 2010-2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _GRLIB_H_
+#define _GRLIB_H_
+
+#include "qdev.h"
+#include "sysbus.h"
+
+/* Emulation of GrLib device is base on the GRLIB IP Core User's Manual:
+ * http://www.gaisler.com/products/grlib/grip.pdf
+ */
+
+/* IRQMP */
+
+typedef void (*set_pil_in_fn) (void *opaque, uint32_t pil_in);
+
+void grlib_irqmp_set_irq(void *opaque, int irq, int level);
+
+void grlib_irqmp_ack(DeviceState *dev, int intno);
+
+static inline
+DeviceState *grlib_irqmp_create(target_phys_addr_t base,
+ CPUState *env,
+ qemu_irq **cpu_irqs,
+ uint32_t nr_irqs,
+ set_pil_in_fn set_pil_in)
+{
+ DeviceState *dev;
+
+ assert(cpu_irqs != NULL);
+
+ dev = qdev_create(NULL, "grlib,irqmp");
+ qdev_prop_set_ptr(dev, "set_pil_in", set_pil_in);
+ qdev_prop_set_ptr(dev, "set_pil_in_opaque", env);
+
+ if (qdev_init(dev)) {
+ return NULL;
+ }
+
+ env->irq_manager = dev;
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+ *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq,
+ dev,
+ nr_irqs);
+
+ return dev;
+}
+
+/* GPTimer */
+
+static inline
+DeviceState *grlib_gptimer_create(target_phys_addr_t base,
+ uint32_t nr_timers,
+ uint32_t freq,
+ qemu_irq *cpu_irqs,
+ int base_irq)
+{
+ DeviceState *dev;
+ int i;
+
+ dev = qdev_create(NULL, "grlib,gptimer");
+ qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
+ qdev_prop_set_uint32(dev, "frequency", freq);
+ qdev_prop_set_uint32(dev, "irq-line", base_irq);
+
+ if (qdev_init(dev)) {
+ return NULL;
+ }
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+ for (i = 0; i < nr_timers; i++) {
+ sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
+ }
+
+ return dev;
+}
+
+/* APB UART */
+
+static inline
+DeviceState *grlib_apbuart_create(target_phys_addr_t base,
+ CharDriverState *serial,
+ qemu_irq irq)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "grlib,apbuart");
+ qdev_prop_set_chr(dev, "chrdev", serial);
+
+ if (qdev_init(dev)) {
+ return NULL;
+ }
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+
+ return dev;
+}
+
+#endif /* ! _GRLIB_H_ */
diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c
new file mode 100644
index 0000000..101b150
--- /dev/null
+++ b/hw/grlib_apbuart.c
@@ -0,0 +1,187 @@
+/*
+ * QEMU GRLIB APB UART Emulator
+ *
+ * Copyright (c) 2010-2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+
+#include "trace.h"
+
+#define UART_REG_SIZE 20 /* Size of memory mapped registers */
+
+/* UART status register fields */
+#define UART_DATA_READY (1 << 0)
+#define UART_TRANSMIT_SHIFT_EMPTY (1 << 1)
+#define UART_TRANSMIT_FIFO_EMPTY (1 << 2)
+#define UART_BREAK_RECEIVED (1 << 3)
+#define UART_OVERRUN (1 << 4)
+#define UART_PARITY_ERROR (1 << 5)
+#define UART_FRAMING_ERROR (1 << 6)
+#define UART_TRANSMIT_FIFO_HALF (1 << 7)
+#define UART_RECEIVE_FIFO_HALF (1 << 8)
+#define UART_TRANSMIT_FIFO_FULL (1 << 9)
+#define UART_RECEIVE_FIFO_FULL (1 << 10)
+
+/* UART control register fields */
+#define UART_RECEIVE_ENABLE (1 << 0)
+#define UART_TRANSMIT_ENABLE (1 << 1)
+#define UART_RECEIVE_INTERRUPT (1 << 2)
+#define UART_TRANSMIT_INTERRUPT (1 << 3)
+#define UART_PARITY_SELECT (1 << 4)
+#define UART_PARITY_ENABLE (1 << 5)
+#define UART_FLOW_CONTROL (1 << 6)
+#define UART_LOOPBACK (1 << 7)
+#define UART_EXTERNAL_CLOCK (1 << 8)
+#define UART_RECEIVE_FIFO_INTERRUPT (1 << 9)
+#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10)
+#define UART_FIFO_DEBUG_MODE (1 << 11)
+#define UART_OUTPUT_ENABLE (1 << 12)
+#define UART_FIFO_AVAILABLE (1 << 31)
+
+/* Memory mapped register offsets */
+#define DATA_OFFSET 0x00
+#define STATUS_OFFSET 0x04
+#define CONTROL_OFFSET 0x08
+#define SCALER_OFFSET 0x0C /* not supported */
+#define FIFO_DEBUG_OFFSET 0x10 /* not supported */
+
+typedef struct UART {
+ SysBusDevice busdev;
+
+ qemu_irq irq;
+
+ CharDriverState *chr;
+
+ /* registers */
+ uint32_t receive;
+ uint32_t status;
+ uint32_t control;
+} UART;
+
+static int grlib_apbuart_can_receive(void *opaque)
+{
+ UART *uart = opaque;
+
+ return !!(uart->status & UART_DATA_READY);
+}
+
+static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ UART *uart = opaque;
+
+ uart->receive = *buf;
+ uart->status |= UART_DATA_READY;
+
+ if (uart->control & UART_RECEIVE_INTERRUPT) {
+ qemu_irq_pulse(uart->irq);
+ }
+}
+
+static void grlib_apbuart_event(void *opaque, int event)
+{
+ trace_grlib_apbuart_event(event);
+}
+
+static void
+grlib_apbuart_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ UART *uart = opaque;
+ unsigned char c = 0;
+
+ addr &= 0xff;
+
+ /* Unit registers */
+ switch (addr) {
+ case DATA_OFFSET:
+ c = value & 0xFF;
+ qemu_chr_write(uart->chr, &c, 1);
+ return;
+
+ case STATUS_OFFSET:
+ /* Read Only */
+ return;
+
+ case CONTROL_OFFSET:
+ /* Not supported */
+ return;
+
+ case SCALER_OFFSET:
+ /* Not supported */
+ return;
+
+ default:
+ break;
+ }
+
+ trace_grlib_apbuart_unknown_register("write", addr);
+}
+
+static CPUReadMemoryFunc * const grlib_apbuart_read[] = {
+ NULL, NULL, NULL,
+};
+
+static CPUWriteMemoryFunc * const grlib_apbuart_write[] = {
+ NULL, NULL, grlib_apbuart_writel,
+};
+
+static int grlib_apbuart_init(SysBusDevice *dev)
+{
+ UART *uart = FROM_SYSBUS(typeof(*uart), dev);
+ int uart_regs = 0;
+
+ qemu_chr_add_handlers(uart->chr,
+ grlib_apbuart_can_receive,
+ grlib_apbuart_receive,
+ grlib_apbuart_event,
+ uart);
+
+ sysbus_init_irq(dev, &uart->irq);
+
+ uart_regs = cpu_register_io_memory(grlib_apbuart_read,
+ grlib_apbuart_write,
+ uart, DEVICE_NATIVE_ENDIAN);
+ if (uart_regs < 0) {
+ return -1;
+ }
+
+ sysbus_init_mmio(dev, UART_REG_SIZE, uart_regs);
+
+ return 0;
+}
+
+static SysBusDeviceInfo grlib_gptimer_info = {
+ .init = grlib_apbuart_init,
+ .qdev.name = "grlib,apbuart",
+ .qdev.size = sizeof(UART),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chrdev", UART, chr),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+static void grlib_gptimer_register(void)
+{
+ sysbus_register_withprop(&grlib_gptimer_info);
+}
+
+device_init(grlib_gptimer_register)
diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
new file mode 100644
index 0000000..596a900
--- /dev/null
+++ b/hw/grlib_gptimer.c
@@ -0,0 +1,395 @@
+/*
+ * QEMU GRLIB GPTimer Emulator
+ *
+ * Copyright (c) 2010-2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+#include "trace.h"
+
+#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */
+#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */
+
+#define GPTIMER_MAX_TIMERS 8
+
+/* GPTimer Config register fields */
+#define GPTIMER_ENABLE (1 << 0)
+#define GPTIMER_RESTART (1 << 1)
+#define GPTIMER_LOAD (1 << 2)
+#define GPTIMER_INT_ENABLE (1 << 3)
+#define GPTIMER_INT_PENDING (1 << 4)
+#define GPTIMER_CHAIN (1 << 5) /* Not supported */
+#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */
+
+/* Memory mapped register offsets */
+#define SCALER_OFFSET 0x00
+#define SCALER_RELOAD_OFFSET 0x04
+#define CONFIG_OFFSET 0x08
+#define COUNTER_OFFSET 0x00
+#define COUNTER_RELOAD_OFFSET 0x04
+#define TIMER_BASE 0x10
+
+typedef struct GPTimer GPTimer;
+typedef struct GPTimerUnit GPTimerUnit;
+
+struct GPTimer {
+ QEMUBH *bh;
+ struct ptimer_state *ptimer;
+
+ qemu_irq irq;
+ int id;
+ GPTimerUnit *unit;
+
+ /* registers */
+ uint32_t counter;
+ uint32_t reload;
+ uint32_t config;
+};
+
+struct GPTimerUnit {
+ SysBusDevice busdev;
+
+ uint32_t nr_timers; /* Number of timers available */
+ uint32_t freq_hz; /* System frequency */
+ uint32_t irq_line; /* Base irq line */
+
+ GPTimer *timers;
+
+ /* registers */
+ uint32_t scaler;
+ uint32_t reload;
+ uint32_t config;
+};
+
+static void grlib_gptimer_enable(GPTimer *timer)
+{
+ assert(timer != NULL);
+
+
+ ptimer_stop(timer->ptimer);
+
+ if (!(timer->config & GPTIMER_ENABLE)) {
+ /* Timer disabled */
+ trace_grlib_gptimer_disabled(timer->id, timer->config);
+ return;
+ }
+
+ /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
+ underflow. Set count + 1 to simulate the GPTimer behavior. */
+
+ trace_grlib_gptimer_enable(timer->id, timer->counter + 1);
+
+ ptimer_set_count(timer->ptimer, timer->counter + 1);
+ ptimer_run(timer->ptimer, 1);
+}
+
+static void grlib_gptimer_restart(GPTimer *timer)
+{
+ assert(timer != NULL);
+
+ trace_grlib_gptimer_restart(timer->id, timer->reload);
+
+ timer->counter = timer->reload;
+ grlib_gptimer_enable(timer);
+}
+
+static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
+{
+ int i = 0;
+ uint32_t value = 0;
+
+ assert(unit != NULL);
+
+ if (scaler > 0) {
+ value = unit->freq_hz / (scaler + 1);
+ } else {
+ value = unit->freq_hz;
+ }
+
+ trace_grlib_gptimer_set_scaler(scaler, value);
+
+ for (i = 0; i < unit->nr_timers; i++) {
+ ptimer_set_freq(unit->timers[i].ptimer, value);
+ }
+}
+
+static void grlib_gptimer_hit(void *opaque)
+{
+ GPTimer *timer = opaque;
+ assert(timer != NULL);
+
+ trace_grlib_gptimer_hit(timer->id);
+
+ /* Timer expired */
+
+ if (timer->config & GPTIMER_INT_ENABLE) {
+ /* Set the pending bit (only unset by write in the config register) */
+ timer->config |= GPTIMER_INT_PENDING;
+ qemu_irq_pulse(timer->irq);
+ }
+
+ if (timer->config & GPTIMER_RESTART) {
+ grlib_gptimer_restart(timer);
+ }
+}
+
+static uint32_t grlib_gptimer_readl(void *opaque, target_phys_addr_t addr)
+{
+ GPTimerUnit *unit = opaque;
+ target_phys_addr_t timer_addr;
+ int id;
+ uint32_t value = 0;
+
+ addr &= 0xff;
+
+ /* Unit registers */
+ switch (addr) {
+ case SCALER_OFFSET:
+ trace_grlib_gptimer_readl(-1, "scaler:", unit->scaler);
+ return unit->scaler;
+
+ case SCALER_RELOAD_OFFSET:
+ trace_grlib_gptimer_readl(-1, "reload:", unit->reload);
+ return unit->reload;
+
+ case CONFIG_OFFSET:
+ trace_grlib_gptimer_readl(-1, "config:", unit->config);
+ return unit->config;
+
+ default:
+ break;
+ }
+
+ timer_addr = (addr % TIMER_BASE);
+ id = (addr - TIMER_BASE) / TIMER_BASE;
+
+ if (id >= 0 && id < unit->nr_timers) {
+
+ /* GPTimer registers */
+ switch (timer_addr) {
+ case COUNTER_OFFSET:
+ value = ptimer_get_count(unit->timers[id].ptimer);
+ trace_grlib_gptimer_readl(id, "counter value:", value);
+ return value;
+
+ case COUNTER_RELOAD_OFFSET:
+ value = unit->timers[id].reload;
+ trace_grlib_gptimer_readl(id, "reload value:", value);
+ return value;
+
+ case CONFIG_OFFSET:
+ trace_grlib_gptimer_readl(id, "scaler value:",
+ unit->timers[id].config);
+ return unit->timers[id].config;
+
+ default:
+ break;
+ }
+
+ }
+
+ trace_grlib_gptimer_unknown_register("read", addr);
+ return 0;
+}
+
+static void
+grlib_gptimer_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ GPTimerUnit *unit = opaque;
+ target_phys_addr_t timer_addr;
+ int id;
+
+ addr &= 0xff;
+
+ /* Unit registers */
+ switch (addr) {
+ case SCALER_OFFSET:
+ value &= 0xFFFF; /* clean up the value */
+ unit->scaler = value;
+ trace_grlib_gptimer_writel(-1, "scaler:", unit->scaler);
+ return;
+
+ case SCALER_RELOAD_OFFSET:
+ value &= 0xFFFF; /* clean up the value */
+ unit->reload = value;
+ trace_grlib_gptimer_writel(-1, "reload:", unit->reload);
+ grlib_gptimer_set_scaler(unit, value);
+ return;
+
+ case CONFIG_OFFSET:
+ /* Read Only (disable timer freeze not supported) */
+ trace_grlib_gptimer_writel(-1, "config (Read Only):", 0);
+ return;
+
+ default:
+ break;
+ }
+
+ timer_addr = (addr % TIMER_BASE);
+ id = (addr - TIMER_BASE) / TIMER_BASE;
+
+ if (id >= 0 && id < unit->nr_timers) {
+
+ /* GPTimer registers */
+ switch (timer_addr) {
+ case COUNTER_OFFSET:
+ trace_grlib_gptimer_writel(id, "counter:", value);
+ unit->timers[id].counter = value;
+ grlib_gptimer_enable(&unit->timers[id]);
+ return;
+
+ case COUNTER_RELOAD_OFFSET:
+ trace_grlib_gptimer_writel(id, "reload:", value);
+ unit->timers[id].reload = value;
+ return;
+
+ case CONFIG_OFFSET:
+ trace_grlib_gptimer_writel(id, "config:", value);
+
+ if (value & GPTIMER_INT_PENDING) {
+ /* clear pending bit */
+ value &= ~GPTIMER_INT_PENDING;
+ } else {
+ /* keep pending bit */
+ value |= unit->timers[id].config & GPTIMER_INT_PENDING;
+ }
+
+ unit->timers[id].config = value;
+
+ /* gptimer_restart calls gptimer_enable, so if "enable" and "load"
+ bits are present, we just have to call restart. */
+
+ if (value & GPTIMER_LOAD) {
+ grlib_gptimer_restart(&unit->timers[id]);
+ } else if (value & GPTIMER_ENABLE) {
+ grlib_gptimer_enable(&unit->timers[id]);
+ }
+
+ /* These fields must always be read as 0 */
+ value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT);
+
+ unit->timers[id].config = value;
+ return;
+
+ default:
+ break;
+ }
+
+ }
+
+ trace_grlib_gptimer_unknown_register("write", addr);
+}
+
+static CPUReadMemoryFunc * const grlib_gptimer_read[] = {
+ NULL, NULL, grlib_gptimer_readl,
+};
+
+static CPUWriteMemoryFunc * const grlib_gptimer_write[] = {
+ NULL, NULL, grlib_gptimer_writel,
+};
+
+static void grlib_gptimer_reset(DeviceState *d)
+{
+ GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev);
+ int i = 0;
+
+ assert(unit != NULL);
+
+ unit->scaler = 0;
+ unit->reload = 0;
+ unit->config = 0;
+
+ unit->config = unit->nr_timers;
+ unit->config |= unit->irq_line << 3;
+ unit->config |= 1 << 8; /* separate interrupt */
+ unit->config |= 1 << 9; /* Disable timer freeze */
+
+
+ for (i = 0; i < unit->nr_timers; i++) {
+ GPTimer *timer = &unit->timers[i];
+
+ timer->counter = 0;
+ timer->reload = 0;
+ timer->config = 0;
+ ptimer_stop(timer->ptimer);
+ ptimer_set_count(timer->ptimer, 0);
+ ptimer_set_freq(timer->ptimer, unit->freq_hz);
+ }
+}
+
+static int grlib_gptimer_init(SysBusDevice *dev)
+{
+ GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev);
+ unsigned int i;
+ int timer_regs;
+
+ assert(unit->nr_timers > 0);
+ assert(unit->nr_timers <= GPTIMER_MAX_TIMERS);
+
+ unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers);
+
+ for (i = 0; i < unit->nr_timers; i++) {
+ GPTimer *timer = &unit->timers[i];
+
+ timer->unit = unit;
+ timer->bh = qemu_bh_new(grlib_gptimer_hit, timer);
+ timer->ptimer = ptimer_init(timer->bh);
+ timer->id = i;
+
+ /* One IRQ line for each timer */
+ sysbus_init_irq(dev, &timer->irq);
+
+ ptimer_set_freq(timer->ptimer, unit->freq_hz);
+ }
+
+ timer_regs = cpu_register_io_memory(grlib_gptimer_read,
+ grlib_gptimer_write,
+ unit, DEVICE_NATIVE_ENDIAN);
+ if (timer_regs < 0) {
+ return -1;
+ }
+
+ sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers,
+ timer_regs);
+ return 0;
+}
+
+static SysBusDeviceInfo grlib_gptimer_info = {
+ .init = grlib_gptimer_init,
+ .qdev.name = "grlib,gptimer",
+ .qdev.reset = grlib_gptimer_reset,
+ .qdev.size = sizeof(GPTimerUnit),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000),
+ DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8),
+ DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+static void grlib_gptimer_register(void)
+{
+ sysbus_register_withprop(&grlib_gptimer_info);
+}
+
+device_init(grlib_gptimer_register)
diff --git a/hw/grlib_irqmp.c b/hw/grlib_irqmp.c
new file mode 100644
index 0000000..f47c491
--- /dev/null
+++ b/hw/grlib_irqmp.c
@@ -0,0 +1,376 @@
+/*
+ * QEMU GRLIB IRQMP Emulator
+ *
+ * (Multiprocessor and extended interrupt not supported)
+ *
+ * Copyright (c) 2010-2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "cpu.h"
+
+#include "grlib.h"
+
+#include "trace.h"
+
+#define IRQMP_MAX_CPU 16
+#define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */
+
+/* Memory mapped register offsets */
+#define LEVEL_OFFSET 0x00
+#define PENDING_OFFSET 0x04
+#define FORCE0_OFFSET 0x08
+#define CLEAR_OFFSET 0x0C
+#define MP_STATUS_OFFSET 0x10
+#define BROADCAST_OFFSET 0x14
+#define MASK_OFFSET 0x40
+#define FORCE_OFFSET 0x80
+#define EXTENDED_OFFSET 0xC0
+
+typedef struct IRQMPState IRQMPState;
+
+typedef struct IRQMP {
+ SysBusDevice busdev;
+
+ void *set_pil_in;
+ void *set_pil_in_opaque;
+
+ IRQMPState *state;
+} IRQMP;
+
+struct IRQMPState {
+ uint32_t level;
+ uint32_t pending;
+ uint32_t clear;
+ uint32_t broadcast;
+
+ uint32_t mask[IRQMP_MAX_CPU];
+ uint32_t force[IRQMP_MAX_CPU];
+ uint32_t extended[IRQMP_MAX_CPU];
+
+ IRQMP *parent;
+};
+
+static void grlib_irqmp_check_irqs(IRQMPState *state)
+{
+ uint32_t pend = 0;
+ uint32_t level0 = 0;
+ uint32_t level1 = 0;
+ set_pil_in_fn set_pil_in;
+
+ assert(state != NULL);
+ assert(state->parent != NULL);
+
+ /* IRQ for CPU 0 (no SMP support) */
+ pend = (state->pending | state->force[0])
+ & state->mask[0];
+
+ level0 = pend & ~state->level;
+ level1 = pend & state->level;
+
+ trace_grlib_irqmp_check_irqs(state->pending, state->force[0],
+ state->mask[0], level1, level0);
+
+ set_pil_in = (set_pil_in_fn)state->parent->set_pil_in;
+
+ /* Trigger level1 interrupt first and level0 if there is no level1 */
+ if (level1 != 0) {
+ set_pil_in(state->parent->set_pil_in_opaque, level1);
+ } else {
+ set_pil_in(state->parent->set_pil_in_opaque, level0);
+ }
+}
+
+void grlib_irqmp_ack(DeviceState *dev, int intno)
+{
+ SysBusDevice *sdev;
+ IRQMP *irqmp;
+ IRQMPState *state;
+ uint32_t mask;
+
+ assert(dev != NULL);
+
+ sdev = sysbus_from_qdev(dev);
+ assert(sdev != NULL);
+
+ irqmp = FROM_SYSBUS(typeof(*irqmp), sdev);
+ assert(irqmp != NULL);
+
+ state = irqmp->state;
+ assert(state != NULL);
+
+ intno &= 15;
+ mask = 1 << intno;
+
+ trace_grlib_irqmp_ack(intno);
+
+ /* Clear registers */
+ state->pending &= ~mask;
+ state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
+
+ grlib_irqmp_check_irqs(state);
+}
+
+void grlib_irqmp_set_irq(void *opaque, int irq, int level)
+{
+ IRQMP *irqmp;
+ IRQMPState *s;
+ int i = 0;
+
+ assert(opaque != NULL);
+
+ irqmp = FROM_SYSBUS(typeof(*irqmp), sysbus_from_qdev(opaque));
+ assert(irqmp != NULL);
+
+ s = irqmp->state;
+ assert(s != NULL);
+ assert(s->parent != NULL);
+
+
+ if (level) {
+ trace_grlib_irqmp_set_irq(irq);
+
+ if (s->broadcast & 1 << irq) {
+ /* Broadcasted IRQ */
+ for (i = 0; i < IRQMP_MAX_CPU; i++) {
+ s->force[i] |= 1 << irq;
+ }
+ } else {
+ s->pending |= 1 << irq;
+ }
+ grlib_irqmp_check_irqs(s);
+
+ }
+}
+
+static uint32_t grlib_irqmp_readl(void *opaque, target_phys_addr_t addr)
+{
+ IRQMP *irqmp = opaque;
+ IRQMPState *state;
+
+ assert(irqmp != NULL);
+ state = irqmp->state;
+ assert(state != NULL);
+
+ addr &= 0xff;
+
+ /* global registers */
+ switch (addr) {
+ case LEVEL_OFFSET:
+ return state->level;
+
+ case PENDING_OFFSET:
+ return state->pending;
+
+ case FORCE0_OFFSET:
+ /* This register is an "alias" for the force register of CPU 0 */
+ return state->force[0];
+
+ case CLEAR_OFFSET:
+ case MP_STATUS_OFFSET:
+ /* Always read as 0 */
+ return 0;
+
+ case BROADCAST_OFFSET:
+ return state->broadcast;
+
+ default:
+ break;
+ }
+
+ /* mask registers */
+ if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
+ int cpu = (addr - MASK_OFFSET) / 4;
+ assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+ return state->mask[cpu];
+ }
+
+ /* force registers */
+ if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
+ int cpu = (addr - FORCE_OFFSET) / 4;
+ assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+ return state->force[cpu];
+ }
+
+ /* extended (not supported) */
+ if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
+ int cpu = (addr - EXTENDED_OFFSET) / 4;
+ assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+ return state->extended[cpu];
+ }
+
+ trace_grlib_irqmp_unknown_register("read", addr);
+ return 0;
+}
+
+static void
+grlib_irqmp_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ IRQMP *irqmp = opaque;
+ IRQMPState *state;
+
+ assert(irqmp != NULL);
+ state = irqmp->state;
+ assert(state != NULL);
+
+ addr &= 0xff;
+
+ /* global registers */
+ switch (addr) {
+ case LEVEL_OFFSET:
+ value &= 0xFFFF << 1; /* clean up the value */
+ state->level = value;
+ return;
+
+ case PENDING_OFFSET:
+ /* Read Only */
+ return;
+
+ case FORCE0_OFFSET:
+ /* This register is an "alias" for the force register of CPU 0 */
+
+ value &= 0xFFFE; /* clean up the value */
+ state->force[0] = value;
+ grlib_irqmp_check_irqs(irqmp->state);
+ return;
+
+ case CLEAR_OFFSET:
+ value &= ~1; /* clean up the value */
+ state->pending &= ~value;
+ return;
+
+ case MP_STATUS_OFFSET:
+ /* Read Only (no SMP support) */
+ return;
+
+ case BROADCAST_OFFSET:
+ value &= 0xFFFE; /* clean up the value */
+ state->broadcast = value;
+ return;
+
+ default:
+ break;
+ }
+
+ /* mask registers */
+ if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
+ int cpu = (addr - MASK_OFFSET) / 4;
+ assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+ value &= ~1; /* clean up the value */
+ state->mask[cpu] = value;
+ grlib_irqmp_check_irqs(irqmp->state);
+ return;
+ }
+
+ /* force registers */
+ if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
+ int cpu = (addr - FORCE_OFFSET) / 4;
+ assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+ uint32_t force = value & 0xFFFE;
+ uint32_t clear = (value >> 16) & 0xFFFE;
+ uint32_t old = state->force[cpu];
+
+ state->force[cpu] = (old | force) & ~clear;
+ grlib_irqmp_check_irqs(irqmp->state);
+ return;
+ }
+
+ /* extended (not supported) */
+ if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
+ int cpu = (addr - EXTENDED_OFFSET) / 4;
+ assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
+
+ value &= 0xF; /* clean up the value */
+ state->extended[cpu] = value;
+ return;
+ }
+
+ trace_grlib_irqmp_unknown_register("write", addr);
+}
+
+static CPUReadMemoryFunc * const grlib_irqmp_read[] = {
+ NULL, NULL, &grlib_irqmp_readl,
+};
+
+static CPUWriteMemoryFunc * const grlib_irqmp_write[] = {
+ NULL, NULL, &grlib_irqmp_writel,
+};
+
+static void grlib_irqmp_reset(DeviceState *d)
+{
+ IRQMP *irqmp = container_of(d, IRQMP, busdev.qdev);
+ assert(irqmp != NULL);
+ assert(irqmp->state != NULL);
+
+ memset(irqmp->state, 0, sizeof *irqmp->state);
+ irqmp->state->parent = irqmp;
+}
+
+static int grlib_irqmp_init(SysBusDevice *dev)
+{
+ IRQMP *irqmp = FROM_SYSBUS(typeof(*irqmp), dev);
+ int irqmp_regs;
+
+ assert(irqmp != NULL);
+
+ /* Check parameters */
+ if (irqmp->set_pil_in == NULL) {
+ return -1;
+ }
+
+ irqmp_regs = cpu_register_io_memory(grlib_irqmp_read,
+ grlib_irqmp_write,
+ irqmp, DEVICE_NATIVE_ENDIAN);
+
+ irqmp->state = qemu_mallocz(sizeof *irqmp->state);
+
+ if (irqmp_regs < 0) {
+ return -1;
+ }
+
+ sysbus_init_mmio(dev, IRQMP_REG_SIZE, irqmp_regs);
+
+ return 0;
+}
+
+static SysBusDeviceInfo grlib_irqmp_info = {
+ .init = grlib_irqmp_init,
+ .qdev.name = "grlib,irqmp",
+ .qdev.reset = grlib_irqmp_reset,
+ .qdev.size = sizeof(IRQMP),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in),
+ DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void grlib_irqmp_register(void)
+{
+ sysbus_register_withprop(&grlib_irqmp_info);
+}
+
+device_init(grlib_irqmp_register)
diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c
new file mode 100644
index 0000000..923073b
--- /dev/null
+++ b/hw/gt64xxx.c
@@ -0,0 +1,1174 @@
+/*
+ * QEMU GT64120 PCI host
+ *
+ * Copyright (c) 2006,2007 Aurelien Jarno
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "pc.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define GT_REGS (0x1000 >> 2)
+
+/* CPU Configuration */
+#define GT_CPU (0x000 >> 2)
+#define GT_MULTI (0x120 >> 2)
+
+/* CPU Address Decode */
+#define GT_SCS10LD (0x008 >> 2)
+#define GT_SCS10HD (0x010 >> 2)
+#define GT_SCS32LD (0x018 >> 2)
+#define GT_SCS32HD (0x020 >> 2)
+#define GT_CS20LD (0x028 >> 2)
+#define GT_CS20HD (0x030 >> 2)
+#define GT_CS3BOOTLD (0x038 >> 2)
+#define GT_CS3BOOTHD (0x040 >> 2)
+#define GT_PCI0IOLD (0x048 >> 2)
+#define GT_PCI0IOHD (0x050 >> 2)
+#define GT_PCI0M0LD (0x058 >> 2)
+#define GT_PCI0M0HD (0x060 >> 2)
+#define GT_PCI0M1LD (0x080 >> 2)
+#define GT_PCI0M1HD (0x088 >> 2)
+#define GT_PCI1IOLD (0x090 >> 2)
+#define GT_PCI1IOHD (0x098 >> 2)
+#define GT_PCI1M0LD (0x0a0 >> 2)
+#define GT_PCI1M0HD (0x0a8 >> 2)
+#define GT_PCI1M1LD (0x0b0 >> 2)
+#define GT_PCI1M1HD (0x0b8 >> 2)
+#define GT_ISD (0x068 >> 2)
+
+#define GT_SCS10AR (0x0d0 >> 2)
+#define GT_SCS32AR (0x0d8 >> 2)
+#define GT_CS20R (0x0e0 >> 2)
+#define GT_CS3BOOTR (0x0e8 >> 2)
+
+#define GT_PCI0IOREMAP (0x0f0 >> 2)
+#define GT_PCI0M0REMAP (0x0f8 >> 2)
+#define GT_PCI0M1REMAP (0x100 >> 2)
+#define GT_PCI1IOREMAP (0x108 >> 2)
+#define GT_PCI1M0REMAP (0x110 >> 2)
+#define GT_PCI1M1REMAP (0x118 >> 2)
+
+/* CPU Error Report */
+#define GT_CPUERR_ADDRLO (0x070 >> 2)
+#define GT_CPUERR_ADDRHI (0x078 >> 2)
+#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */
+#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */
+#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */
+
+/* CPU Sync Barrier */
+#define GT_PCI0SYNC (0x0c0 >> 2)
+#define GT_PCI1SYNC (0x0c8 >> 2)
+
+/* SDRAM and Device Address Decode */
+#define GT_SCS0LD (0x400 >> 2)
+#define GT_SCS0HD (0x404 >> 2)
+#define GT_SCS1LD (0x408 >> 2)
+#define GT_SCS1HD (0x40c >> 2)
+#define GT_SCS2LD (0x410 >> 2)
+#define GT_SCS2HD (0x414 >> 2)
+#define GT_SCS3LD (0x418 >> 2)
+#define GT_SCS3HD (0x41c >> 2)
+#define GT_CS0LD (0x420 >> 2)
+#define GT_CS0HD (0x424 >> 2)
+#define GT_CS1LD (0x428 >> 2)
+#define GT_CS1HD (0x42c >> 2)
+#define GT_CS2LD (0x430 >> 2)
+#define GT_CS2HD (0x434 >> 2)
+#define GT_CS3LD (0x438 >> 2)
+#define GT_CS3HD (0x43c >> 2)
+#define GT_BOOTLD (0x440 >> 2)
+#define GT_BOOTHD (0x444 >> 2)
+#define GT_ADERR (0x470 >> 2)
+
+/* SDRAM Configuration */
+#define GT_SDRAM_CFG (0x448 >> 2)
+#define GT_SDRAM_OPMODE (0x474 >> 2)
+#define GT_SDRAM_BM (0x478 >> 2)
+#define GT_SDRAM_ADDRDECODE (0x47c >> 2)
+
+/* SDRAM Parameters */
+#define GT_SDRAM_B0 (0x44c >> 2)
+#define GT_SDRAM_B1 (0x450 >> 2)
+#define GT_SDRAM_B2 (0x454 >> 2)
+#define GT_SDRAM_B3 (0x458 >> 2)
+
+/* Device Parameters */
+#define GT_DEV_B0 (0x45c >> 2)
+#define GT_DEV_B1 (0x460 >> 2)
+#define GT_DEV_B2 (0x464 >> 2)
+#define GT_DEV_B3 (0x468 >> 2)
+#define GT_DEV_BOOT (0x46c >> 2)
+
+/* ECC */
+#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */
+#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */
+#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */
+#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */
+#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */
+
+/* DMA Record */
+#define GT_DMA0_CNT (0x800 >> 2)
+#define GT_DMA1_CNT (0x804 >> 2)
+#define GT_DMA2_CNT (0x808 >> 2)
+#define GT_DMA3_CNT (0x80c >> 2)
+#define GT_DMA0_SA (0x810 >> 2)
+#define GT_DMA1_SA (0x814 >> 2)
+#define GT_DMA2_SA (0x818 >> 2)
+#define GT_DMA3_SA (0x81c >> 2)
+#define GT_DMA0_DA (0x820 >> 2)
+#define GT_DMA1_DA (0x824 >> 2)
+#define GT_DMA2_DA (0x828 >> 2)
+#define GT_DMA3_DA (0x82c >> 2)
+#define GT_DMA0_NEXT (0x830 >> 2)
+#define GT_DMA1_NEXT (0x834 >> 2)
+#define GT_DMA2_NEXT (0x838 >> 2)
+#define GT_DMA3_NEXT (0x83c >> 2)
+#define GT_DMA0_CUR (0x870 >> 2)
+#define GT_DMA1_CUR (0x874 >> 2)
+#define GT_DMA2_CUR (0x878 >> 2)
+#define GT_DMA3_CUR (0x87c >> 2)
+
+/* DMA Channel Control */
+#define GT_DMA0_CTRL (0x840 >> 2)
+#define GT_DMA1_CTRL (0x844 >> 2)
+#define GT_DMA2_CTRL (0x848 >> 2)
+#define GT_DMA3_CTRL (0x84c >> 2)
+
+/* DMA Arbiter */
+#define GT_DMA_ARB (0x860 >> 2)
+
+/* Timer/Counter */
+#define GT_TC0 (0x850 >> 2)
+#define GT_TC1 (0x854 >> 2)
+#define GT_TC2 (0x858 >> 2)
+#define GT_TC3 (0x85c >> 2)
+#define GT_TC_CONTROL (0x864 >> 2)
+
+/* PCI Internal */
+#define GT_PCI0_CMD (0xc00 >> 2)
+#define GT_PCI0_TOR (0xc04 >> 2)
+#define GT_PCI0_BS_SCS10 (0xc08 >> 2)
+#define GT_PCI0_BS_SCS32 (0xc0c >> 2)
+#define GT_PCI0_BS_CS20 (0xc10 >> 2)
+#define GT_PCI0_BS_CS3BT (0xc14 >> 2)
+#define GT_PCI1_IACK (0xc30 >> 2)
+#define GT_PCI0_IACK (0xc34 >> 2)
+#define GT_PCI0_BARE (0xc3c >> 2)
+#define GT_PCI0_PREFMBR (0xc40 >> 2)
+#define GT_PCI0_SCS10_BAR (0xc48 >> 2)
+#define GT_PCI0_SCS32_BAR (0xc4c >> 2)
+#define GT_PCI0_CS20_BAR (0xc50 >> 2)
+#define GT_PCI0_CS3BT_BAR (0xc54 >> 2)
+#define GT_PCI0_SSCS10_BAR (0xc58 >> 2)
+#define GT_PCI0_SSCS32_BAR (0xc5c >> 2)
+#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2)
+#define GT_PCI1_CMD (0xc80 >> 2)
+#define GT_PCI1_TOR (0xc84 >> 2)
+#define GT_PCI1_BS_SCS10 (0xc88 >> 2)
+#define GT_PCI1_BS_SCS32 (0xc8c >> 2)
+#define GT_PCI1_BS_CS20 (0xc90 >> 2)
+#define GT_PCI1_BS_CS3BT (0xc94 >> 2)
+#define GT_PCI1_BARE (0xcbc >> 2)
+#define GT_PCI1_PREFMBR (0xcc0 >> 2)
+#define GT_PCI1_SCS10_BAR (0xcc8 >> 2)
+#define GT_PCI1_SCS32_BAR (0xccc >> 2)
+#define GT_PCI1_CS20_BAR (0xcd0 >> 2)
+#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2)
+#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2)
+#define GT_PCI1_SSCS32_BAR (0xcdc >> 2)
+#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2)
+#define GT_PCI1_CFGADDR (0xcf0 >> 2)
+#define GT_PCI1_CFGDATA (0xcf4 >> 2)
+#define GT_PCI0_CFGADDR (0xcf8 >> 2)
+#define GT_PCI0_CFGDATA (0xcfc >> 2)
+
+/* Interrupts */
+#define GT_INTRCAUSE (0xc18 >> 2)
+#define GT_INTRMASK (0xc1c >> 2)
+#define GT_PCI0_ICMASK (0xc24 >> 2)
+#define GT_PCI0_SERR0MASK (0xc28 >> 2)
+#define GT_CPU_INTSEL (0xc70 >> 2)
+#define GT_PCI0_INTSEL (0xc74 >> 2)
+#define GT_HINTRCAUSE (0xc98 >> 2)
+#define GT_HINTRMASK (0xc9c >> 2)
+#define GT_PCI0_HICMASK (0xca4 >> 2)
+#define GT_PCI1_SERR1MASK (0xca8 >> 2)
+
+#define PCI_MAPPING_ENTRY(regname) \
+ target_phys_addr_t regname ##_start; \
+ target_phys_addr_t regname ##_length; \
+ int regname ##_handle
+
+typedef struct GT64120State {
+ SysBusDevice busdev;
+ PCIHostState pci;
+ uint32_t regs[GT_REGS];
+ PCI_MAPPING_ENTRY(PCI0IO);
+ PCI_MAPPING_ENTRY(ISD);
+} GT64120State;
+
+/* Adjust range to avoid touching space which isn't mappable via PCI */
+/* XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000
+ 0x1fc00000 - 0x1fd00000 */
+static void check_reserved_space (target_phys_addr_t *start,
+ target_phys_addr_t *length)
+{
+ target_phys_addr_t begin = *start;
+ target_phys_addr_t end = *start + *length;
+
+ if (end >= 0x1e000000LL && end < 0x1f100000LL)
+ end = 0x1e000000LL;
+ if (begin >= 0x1e000000LL && begin < 0x1f100000LL)
+ begin = 0x1f100000LL;
+ if (end >= 0x1fc00000LL && end < 0x1fd00000LL)
+ end = 0x1fc00000LL;
+ if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL)
+ begin = 0x1fd00000LL;
+ /* XXX: This is broken when a reserved range splits the requested range */
+ if (end >= 0x1f100000LL && begin < 0x1e000000LL)
+ end = 0x1e000000LL;
+ if (end >= 0x1fd00000LL && begin < 0x1fc00000LL)
+ end = 0x1fc00000LL;
+
+ *start = begin;
+ *length = end - begin;
+}
+
+static void gt64120_isd_mapping(GT64120State *s)
+{
+ target_phys_addr_t start = s->regs[GT_ISD] << 21;
+ target_phys_addr_t length = 0x1000;
+
+ if (s->ISD_length)
+ cpu_register_physical_memory(s->ISD_start, s->ISD_length,
+ IO_MEM_UNASSIGNED);
+ check_reserved_space(&start, &length);
+ length = 0x1000;
+ /* Map new address */
+ DPRINTF("ISD: "TARGET_FMT_plx"@"TARGET_FMT_plx" -> "TARGET_FMT_plx"@"TARGET_FMT_plx", %x\n", s->ISD_length, s->ISD_start,
+ length, start, s->ISD_handle);
+ s->ISD_start = start;
+ s->ISD_length = length;
+ cpu_register_physical_memory(s->ISD_start, s->ISD_length, s->ISD_handle);
+}
+
+static void gt64120_pci_mapping(GT64120State *s)
+{
+ /* Update IO mapping */
+ if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD])
+ {
+ /* Unmap old IO address */
+ if (s->PCI0IO_length)
+ {
+ cpu_register_physical_memory(s->PCI0IO_start, s->PCI0IO_length, IO_MEM_UNASSIGNED);
+ }
+ /* Map new IO address */
+ s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21;
+ s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) - (s->regs[GT_PCI0IOLD] & 0x7f)) << 21;
+ isa_mem_base = s->PCI0IO_start;
+ isa_mmio_init(s->PCI0IO_start, s->PCI0IO_length);
+ }
+}
+
+static void gt64120_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ GT64120State *s = opaque;
+ uint32_t saddr;
+
+ if (!(s->regs[GT_CPU] & 0x00001000))
+ val = bswap32(val);
+
+ saddr = (addr & 0xfff) >> 2;
+ switch (saddr) {
+
+ /* CPU Configuration */
+ case GT_CPU:
+ s->regs[GT_CPU] = val;
+ break;
+ case GT_MULTI:
+ /* Read-only register as only one GT64xxx is present on the CPU bus */
+ break;
+
+ /* CPU Address Decode */
+ case GT_PCI0IOLD:
+ s->regs[GT_PCI0IOLD] = val & 0x00007fff;
+ s->regs[GT_PCI0IOREMAP] = val & 0x000007ff;
+ gt64120_pci_mapping(s);
+ break;
+ case GT_PCI0M0LD:
+ s->regs[GT_PCI0M0LD] = val & 0x00007fff;
+ s->regs[GT_PCI0M0REMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI0M1LD:
+ s->regs[GT_PCI0M1LD] = val & 0x00007fff;
+ s->regs[GT_PCI0M1REMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI1IOLD:
+ s->regs[GT_PCI1IOLD] = val & 0x00007fff;
+ s->regs[GT_PCI1IOREMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI1M0LD:
+ s->regs[GT_PCI1M0LD] = val & 0x00007fff;
+ s->regs[GT_PCI1M0REMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI1M1LD:
+ s->regs[GT_PCI1M1LD] = val & 0x00007fff;
+ s->regs[GT_PCI1M1REMAP] = val & 0x000007ff;
+ break;
+ case GT_PCI0IOHD:
+ s->regs[saddr] = val & 0x0000007f;
+ gt64120_pci_mapping(s);
+ break;
+ case GT_PCI0M0HD:
+ case GT_PCI0M1HD:
+ case GT_PCI1IOHD:
+ case GT_PCI1M0HD:
+ case GT_PCI1M1HD:
+ s->regs[saddr] = val & 0x0000007f;
+ break;
+ case GT_ISD:
+ s->regs[saddr] = val & 0x00007fff;
+ gt64120_isd_mapping(s);
+ break;
+
+ case GT_PCI0IOREMAP:
+ case GT_PCI0M0REMAP:
+ case GT_PCI0M1REMAP:
+ case GT_PCI1IOREMAP:
+ case GT_PCI1M0REMAP:
+ case GT_PCI1M1REMAP:
+ s->regs[saddr] = val & 0x000007ff;
+ break;
+
+ /* CPU Error Report */
+ case GT_CPUERR_ADDRLO:
+ case GT_CPUERR_ADDRHI:
+ case GT_CPUERR_DATALO:
+ case GT_CPUERR_DATAHI:
+ case GT_CPUERR_PARITY:
+ /* Read-only registers, do nothing */
+ break;
+
+ /* CPU Sync Barrier */
+ case GT_PCI0SYNC:
+ case GT_PCI1SYNC:
+ /* Read-only registers, do nothing */
+ break;
+
+ /* SDRAM and Device Address Decode */
+ case GT_SCS0LD:
+ case GT_SCS0HD:
+ case GT_SCS1LD:
+ case GT_SCS1HD:
+ case GT_SCS2LD:
+ case GT_SCS2HD:
+ case GT_SCS3LD:
+ case GT_SCS3HD:
+ case GT_CS0LD:
+ case GT_CS0HD:
+ case GT_CS1LD:
+ case GT_CS1HD:
+ case GT_CS2LD:
+ case GT_CS2HD:
+ case GT_CS3LD:
+ case GT_CS3HD:
+ case GT_BOOTLD:
+ case GT_BOOTHD:
+ case GT_ADERR:
+ /* SDRAM Configuration */
+ case GT_SDRAM_CFG:
+ case GT_SDRAM_OPMODE:
+ case GT_SDRAM_BM:
+ case GT_SDRAM_ADDRDECODE:
+ /* Accept and ignore SDRAM interleave configuration */
+ s->regs[saddr] = val;
+ break;
+
+ /* Device Parameters */
+ case GT_DEV_B0:
+ case GT_DEV_B1:
+ case GT_DEV_B2:
+ case GT_DEV_B3:
+ case GT_DEV_BOOT:
+ /* Not implemented */
+ DPRINTF ("Unimplemented device register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* ECC */
+ case GT_ECC_ERRDATALO:
+ case GT_ECC_ERRDATAHI:
+ case GT_ECC_MEM:
+ case GT_ECC_CALC:
+ case GT_ECC_ERRADDR:
+ /* Read-only registers, do nothing */
+ break;
+
+ /* DMA Record */
+ case GT_DMA0_CNT:
+ case GT_DMA1_CNT:
+ case GT_DMA2_CNT:
+ case GT_DMA3_CNT:
+ case GT_DMA0_SA:
+ case GT_DMA1_SA:
+ case GT_DMA2_SA:
+ case GT_DMA3_SA:
+ case GT_DMA0_DA:
+ case GT_DMA1_DA:
+ case GT_DMA2_DA:
+ case GT_DMA3_DA:
+ case GT_DMA0_NEXT:
+ case GT_DMA1_NEXT:
+ case GT_DMA2_NEXT:
+ case GT_DMA3_NEXT:
+ case GT_DMA0_CUR:
+ case GT_DMA1_CUR:
+ case GT_DMA2_CUR:
+ case GT_DMA3_CUR:
+ /* Not implemented */
+ DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* DMA Channel Control */
+ case GT_DMA0_CTRL:
+ case GT_DMA1_CTRL:
+ case GT_DMA2_CTRL:
+ case GT_DMA3_CTRL:
+ /* Not implemented */
+ DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* DMA Arbiter */
+ case GT_DMA_ARB:
+ /* Not implemented */
+ DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* Timer/Counter */
+ case GT_TC0:
+ case GT_TC1:
+ case GT_TC2:
+ case GT_TC3:
+ case GT_TC_CONTROL:
+ /* Not implemented */
+ DPRINTF ("Unimplemented timer register offset 0x%x\n", saddr << 2);
+ break;
+
+ /* PCI Internal */
+ case GT_PCI0_CMD:
+ case GT_PCI1_CMD:
+ s->regs[saddr] = val & 0x0401fc0f;
+ break;
+ case GT_PCI0_TOR:
+ case GT_PCI0_BS_SCS10:
+ case GT_PCI0_BS_SCS32:
+ case GT_PCI0_BS_CS20:
+ case GT_PCI0_BS_CS3BT:
+ case GT_PCI1_IACK:
+ case GT_PCI0_IACK:
+ case GT_PCI0_BARE:
+ case GT_PCI0_PREFMBR:
+ case GT_PCI0_SCS10_BAR:
+ case GT_PCI0_SCS32_BAR:
+ case GT_PCI0_CS20_BAR:
+ case GT_PCI0_CS3BT_BAR:
+ case GT_PCI0_SSCS10_BAR:
+ case GT_PCI0_SSCS32_BAR:
+ case GT_PCI0_SCS3BT_BAR:
+ case GT_PCI1_TOR:
+ case GT_PCI1_BS_SCS10:
+ case GT_PCI1_BS_SCS32:
+ case GT_PCI1_BS_CS20:
+ case GT_PCI1_BS_CS3BT:
+ case GT_PCI1_BARE:
+ case GT_PCI1_PREFMBR:
+ case GT_PCI1_SCS10_BAR:
+ case GT_PCI1_SCS32_BAR:
+ case GT_PCI1_CS20_BAR:
+ case GT_PCI1_CS3BT_BAR:
+ case GT_PCI1_SSCS10_BAR:
+ case GT_PCI1_SSCS32_BAR:
+ case GT_PCI1_SCS3BT_BAR:
+ case GT_PCI1_CFGADDR:
+ case GT_PCI1_CFGDATA:
+ /* not implemented */
+ break;
+ case GT_PCI0_CFGADDR:
+ s->pci.config_reg = val & 0x80fffffc;
+ break;
+ case GT_PCI0_CFGDATA:
+ if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800))
+ val = bswap32(val);
+ if (s->pci.config_reg & (1u << 31))
+ pci_data_write(s->pci.bus, s->pci.config_reg, val, 4);
+ break;
+
+ /* Interrupts */
+ case GT_INTRCAUSE:
+ /* not really implemented */
+ s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe));
+ s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe);
+ DPRINTF("INTRCAUSE %x\n", val);
+ break;
+ case GT_INTRMASK:
+ s->regs[saddr] = val & 0x3c3ffffe;
+ DPRINTF("INTRMASK %x\n", val);
+ break;
+ case GT_PCI0_ICMASK:
+ s->regs[saddr] = val & 0x03fffffe;
+ DPRINTF("ICMASK %x\n", val);
+ break;
+ case GT_PCI0_SERR0MASK:
+ s->regs[saddr] = val & 0x0000003f;
+ DPRINTF("SERR0MASK %x\n", val);
+ break;
+
+ /* Reserved when only PCI_0 is configured. */
+ case GT_HINTRCAUSE:
+ case GT_CPU_INTSEL:
+ case GT_PCI0_INTSEL:
+ case GT_HINTRMASK:
+ case GT_PCI0_HICMASK:
+ case GT_PCI1_SERR1MASK:
+ /* not implemented */
+ break;
+
+ /* SDRAM Parameters */
+ case GT_SDRAM_B0:
+ case GT_SDRAM_B1:
+ case GT_SDRAM_B2:
+ case GT_SDRAM_B3:
+ /* We don't simulate electrical parameters of the SDRAM.
+ Accept, but ignore the values. */
+ s->regs[saddr] = val;
+ break;
+
+ default:
+ DPRINTF ("Bad register offset 0x%x\n", (int)addr);
+ break;
+ }
+}
+
+static uint32_t gt64120_readl (void *opaque,
+ target_phys_addr_t addr)
+{
+ GT64120State *s = opaque;
+ uint32_t val;
+ uint32_t saddr;
+
+ saddr = (addr & 0xfff) >> 2;
+ switch (saddr) {
+
+ /* CPU Configuration */
+ case GT_MULTI:
+ /* Only one GT64xxx is present on the CPU bus, return
+ the initial value */
+ val = s->regs[saddr];
+ break;
+
+ /* CPU Error Report */
+ case GT_CPUERR_ADDRLO:
+ case GT_CPUERR_ADDRHI:
+ case GT_CPUERR_DATALO:
+ case GT_CPUERR_DATAHI:
+ case GT_CPUERR_PARITY:
+ /* Emulated memory has no error, always return the initial
+ values */
+ val = s->regs[saddr];
+ break;
+
+ /* CPU Sync Barrier */
+ case GT_PCI0SYNC:
+ case GT_PCI1SYNC:
+ /* Reading those register should empty all FIFO on the PCI
+ bus, which are not emulated. The return value should be
+ a random value that should be ignored. */
+ val = 0xc000ffee;
+ break;
+
+ /* ECC */
+ case GT_ECC_ERRDATALO:
+ case GT_ECC_ERRDATAHI:
+ case GT_ECC_MEM:
+ case GT_ECC_CALC:
+ case GT_ECC_ERRADDR:
+ /* Emulated memory has no error, always return the initial
+ values */
+ val = s->regs[saddr];
+ break;
+
+ case GT_CPU:
+ case GT_SCS10LD:
+ case GT_SCS10HD:
+ case GT_SCS32LD:
+ case GT_SCS32HD:
+ case GT_CS20LD:
+ case GT_CS20HD:
+ case GT_CS3BOOTLD:
+ case GT_CS3BOOTHD:
+ case GT_SCS10AR:
+ case GT_SCS32AR:
+ case GT_CS20R:
+ case GT_CS3BOOTR:
+ case GT_PCI0IOLD:
+ case GT_PCI0M0LD:
+ case GT_PCI0M1LD:
+ case GT_PCI1IOLD:
+ case GT_PCI1M0LD:
+ case GT_PCI1M1LD:
+ case GT_PCI0IOHD:
+ case GT_PCI0M0HD:
+ case GT_PCI0M1HD:
+ case GT_PCI1IOHD:
+ case GT_PCI1M0HD:
+ case GT_PCI1M1HD:
+ case GT_PCI0IOREMAP:
+ case GT_PCI0M0REMAP:
+ case GT_PCI0M1REMAP:
+ case GT_PCI1IOREMAP:
+ case GT_PCI1M0REMAP:
+ case GT_PCI1M1REMAP:
+ case GT_ISD:
+ val = s->regs[saddr];
+ break;
+ case GT_PCI0_IACK:
+ /* Read the IRQ number */
+ val = pic_read_irq(isa_pic);
+ break;
+
+ /* SDRAM and Device Address Decode */
+ case GT_SCS0LD:
+ case GT_SCS0HD:
+ case GT_SCS1LD:
+ case GT_SCS1HD:
+ case GT_SCS2LD:
+ case GT_SCS2HD:
+ case GT_SCS3LD:
+ case GT_SCS3HD:
+ case GT_CS0LD:
+ case GT_CS0HD:
+ case GT_CS1LD:
+ case GT_CS1HD:
+ case GT_CS2LD:
+ case GT_CS2HD:
+ case GT_CS3LD:
+ case GT_CS3HD:
+ case GT_BOOTLD:
+ case GT_BOOTHD:
+ case GT_ADERR:
+ val = s->regs[saddr];
+ break;
+
+ /* SDRAM Configuration */
+ case GT_SDRAM_CFG:
+ case GT_SDRAM_OPMODE:
+ case GT_SDRAM_BM:
+ case GT_SDRAM_ADDRDECODE:
+ val = s->regs[saddr];
+ break;
+
+ /* SDRAM Parameters */
+ case GT_SDRAM_B0:
+ case GT_SDRAM_B1:
+ case GT_SDRAM_B2:
+ case GT_SDRAM_B3:
+ /* We don't simulate electrical parameters of the SDRAM.
+ Just return the last written value. */
+ val = s->regs[saddr];
+ break;
+
+ /* Device Parameters */
+ case GT_DEV_B0:
+ case GT_DEV_B1:
+ case GT_DEV_B2:
+ case GT_DEV_B3:
+ case GT_DEV_BOOT:
+ val = s->regs[saddr];
+ break;
+
+ /* DMA Record */
+ case GT_DMA0_CNT:
+ case GT_DMA1_CNT:
+ case GT_DMA2_CNT:
+ case GT_DMA3_CNT:
+ case GT_DMA0_SA:
+ case GT_DMA1_SA:
+ case GT_DMA2_SA:
+ case GT_DMA3_SA:
+ case GT_DMA0_DA:
+ case GT_DMA1_DA:
+ case GT_DMA2_DA:
+ case GT_DMA3_DA:
+ case GT_DMA0_NEXT:
+ case GT_DMA1_NEXT:
+ case GT_DMA2_NEXT:
+ case GT_DMA3_NEXT:
+ case GT_DMA0_CUR:
+ case GT_DMA1_CUR:
+ case GT_DMA2_CUR:
+ case GT_DMA3_CUR:
+ val = s->regs[saddr];
+ break;
+
+ /* DMA Channel Control */
+ case GT_DMA0_CTRL:
+ case GT_DMA1_CTRL:
+ case GT_DMA2_CTRL:
+ case GT_DMA3_CTRL:
+ val = s->regs[saddr];
+ break;
+
+ /* DMA Arbiter */
+ case GT_DMA_ARB:
+ val = s->regs[saddr];
+ break;
+
+ /* Timer/Counter */
+ case GT_TC0:
+ case GT_TC1:
+ case GT_TC2:
+ case GT_TC3:
+ case GT_TC_CONTROL:
+ val = s->regs[saddr];
+ break;
+
+ /* PCI Internal */
+ case GT_PCI0_CFGADDR:
+ val = s->pci.config_reg;
+ break;
+ case GT_PCI0_CFGDATA:
+ if (!(s->pci.config_reg & (1 << 31)))
+ val = 0xffffffff;
+ else
+ val = pci_data_read(s->pci.bus, s->pci.config_reg, 4);
+ if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800))
+ val = bswap32(val);
+ break;
+
+ case GT_PCI0_CMD:
+ case GT_PCI0_TOR:
+ case GT_PCI0_BS_SCS10:
+ case GT_PCI0_BS_SCS32:
+ case GT_PCI0_BS_CS20:
+ case GT_PCI0_BS_CS3BT:
+ case GT_PCI1_IACK:
+ case GT_PCI0_BARE:
+ case GT_PCI0_PREFMBR:
+ case GT_PCI0_SCS10_BAR:
+ case GT_PCI0_SCS32_BAR:
+ case GT_PCI0_CS20_BAR:
+ case GT_PCI0_CS3BT_BAR:
+ case GT_PCI0_SSCS10_BAR:
+ case GT_PCI0_SSCS32_BAR:
+ case GT_PCI0_SCS3BT_BAR:
+ case GT_PCI1_CMD:
+ case GT_PCI1_TOR:
+ case GT_PCI1_BS_SCS10:
+ case GT_PCI1_BS_SCS32:
+ case GT_PCI1_BS_CS20:
+ case GT_PCI1_BS_CS3BT:
+ case GT_PCI1_BARE:
+ case GT_PCI1_PREFMBR:
+ case GT_PCI1_SCS10_BAR:
+ case GT_PCI1_SCS32_BAR:
+ case GT_PCI1_CS20_BAR:
+ case GT_PCI1_CS3BT_BAR:
+ case GT_PCI1_SSCS10_BAR:
+ case GT_PCI1_SSCS32_BAR:
+ case GT_PCI1_SCS3BT_BAR:
+ case GT_PCI1_CFGADDR:
+ case GT_PCI1_CFGDATA:
+ val = s->regs[saddr];
+ break;
+
+ /* Interrupts */
+ case GT_INTRCAUSE:
+ val = s->regs[saddr];
+ DPRINTF("INTRCAUSE %x\n", val);
+ break;
+ case GT_INTRMASK:
+ val = s->regs[saddr];
+ DPRINTF("INTRMASK %x\n", val);
+ break;
+ case GT_PCI0_ICMASK:
+ val = s->regs[saddr];
+ DPRINTF("ICMASK %x\n", val);
+ break;
+ case GT_PCI0_SERR0MASK:
+ val = s->regs[saddr];
+ DPRINTF("SERR0MASK %x\n", val);
+ break;
+
+ /* Reserved when only PCI_0 is configured. */
+ case GT_HINTRCAUSE:
+ case GT_CPU_INTSEL:
+ case GT_PCI0_INTSEL:
+ case GT_HINTRMASK:
+ case GT_PCI0_HICMASK:
+ case GT_PCI1_SERR1MASK:
+ val = s->regs[saddr];
+ break;
+
+ default:
+ val = s->regs[saddr];
+ DPRINTF ("Bad register offset 0x%x\n", (int)addr);
+ break;
+ }
+
+ if (!(s->regs[GT_CPU] & 0x00001000))
+ val = bswap32(val);
+
+ return val;
+}
+
+static CPUWriteMemoryFunc * const gt64120_write[] = {
+ &gt64120_writel,
+ &gt64120_writel,
+ &gt64120_writel,
+};
+
+static CPUReadMemoryFunc * const gt64120_read[] = {
+ &gt64120_readl,
+ &gt64120_readl,
+ &gt64120_readl,
+};
+
+static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot;
+
+ slot = (pci_dev->devfn >> 3);
+
+ switch (slot) {
+ /* PIIX4 USB */
+ case 10:
+ return 3;
+ /* AMD 79C973 Ethernet */
+ case 11:
+ return 1;
+ /* Crystal 4281 Sound */
+ case 12:
+ return 2;
+ /* PCI slot 1 to 4 */
+ case 18 ... 21:
+ return ((slot - 18) + irq_num) & 0x03;
+ /* Unknown device, don't do any translation */
+ default:
+ return irq_num;
+ }
+}
+
+static int pci_irq_levels[4];
+
+static void gt64120_pci_set_irq(void *opaque, int irq_num, int level)
+{
+ int i, pic_irq, pic_level;
+ qemu_irq *pic = opaque;
+
+ pci_irq_levels[irq_num] = level;
+
+ /* now we change the pic irq level according to the piix irq mappings */
+ /* XXX: optimize */
+ pic_irq = piix4_dev->config[0x60 + irq_num];
+ if (pic_irq < 16) {
+ /* The pic level is the logical OR of all the PCI irqs mapped
+ to it */
+ pic_level = 0;
+ for (i = 0; i < 4; i++) {
+ if (pic_irq == piix4_dev->config[0x60 + i])
+ pic_level |= pci_irq_levels[i];
+ }
+ qemu_set_irq(pic[pic_irq], pic_level);
+ }
+}
+
+
+static void gt64120_reset(void *opaque)
+{
+ GT64120State *s = opaque;
+
+ /* FIXME: Malta specific hw assumptions ahead */
+
+ /* CPU Configuration */
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->regs[GT_CPU] = 0x00000000;
+#else
+ s->regs[GT_CPU] = 0x00001000;
+#endif
+ s->regs[GT_MULTI] = 0x00000003;
+
+ /* CPU Address decode */
+ s->regs[GT_SCS10LD] = 0x00000000;
+ s->regs[GT_SCS10HD] = 0x00000007;
+ s->regs[GT_SCS32LD] = 0x00000008;
+ s->regs[GT_SCS32HD] = 0x0000000f;
+ s->regs[GT_CS20LD] = 0x000000e0;
+ s->regs[GT_CS20HD] = 0x00000070;
+ s->regs[GT_CS3BOOTLD] = 0x000000f8;
+ s->regs[GT_CS3BOOTHD] = 0x0000007f;
+
+ s->regs[GT_PCI0IOLD] = 0x00000080;
+ s->regs[GT_PCI0IOHD] = 0x0000000f;
+ s->regs[GT_PCI0M0LD] = 0x00000090;
+ s->regs[GT_PCI0M0HD] = 0x0000001f;
+ s->regs[GT_ISD] = 0x000000a0;
+ s->regs[GT_PCI0M1LD] = 0x00000790;
+ s->regs[GT_PCI0M1HD] = 0x0000001f;
+ s->regs[GT_PCI1IOLD] = 0x00000100;
+ s->regs[GT_PCI1IOHD] = 0x0000000f;
+ s->regs[GT_PCI1M0LD] = 0x00000110;
+ s->regs[GT_PCI1M0HD] = 0x0000001f;
+ s->regs[GT_PCI1M1LD] = 0x00000120;
+ s->regs[GT_PCI1M1HD] = 0x0000002f;
+
+ s->regs[GT_SCS10AR] = 0x00000000;
+ s->regs[GT_SCS32AR] = 0x00000008;
+ s->regs[GT_CS20R] = 0x000000e0;
+ s->regs[GT_CS3BOOTR] = 0x000000f8;
+
+ s->regs[GT_PCI0IOREMAP] = 0x00000080;
+ s->regs[GT_PCI0M0REMAP] = 0x00000090;
+ s->regs[GT_PCI0M1REMAP] = 0x00000790;
+ s->regs[GT_PCI1IOREMAP] = 0x00000100;
+ s->regs[GT_PCI1M0REMAP] = 0x00000110;
+ s->regs[GT_PCI1M1REMAP] = 0x00000120;
+
+ /* CPU Error Report */
+ s->regs[GT_CPUERR_ADDRLO] = 0x00000000;
+ s->regs[GT_CPUERR_ADDRHI] = 0x00000000;
+ s->regs[GT_CPUERR_DATALO] = 0xffffffff;
+ s->regs[GT_CPUERR_DATAHI] = 0xffffffff;
+ s->regs[GT_CPUERR_PARITY] = 0x000000ff;
+
+ /* CPU Sync Barrier */
+ s->regs[GT_PCI0SYNC] = 0x00000000;
+ s->regs[GT_PCI1SYNC] = 0x00000000;
+
+ /* SDRAM and Device Address Decode */
+ s->regs[GT_SCS0LD] = 0x00000000;
+ s->regs[GT_SCS0HD] = 0x00000007;
+ s->regs[GT_SCS1LD] = 0x00000008;
+ s->regs[GT_SCS1HD] = 0x0000000f;
+ s->regs[GT_SCS2LD] = 0x00000010;
+ s->regs[GT_SCS2HD] = 0x00000017;
+ s->regs[GT_SCS3LD] = 0x00000018;
+ s->regs[GT_SCS3HD] = 0x0000001f;
+ s->regs[GT_CS0LD] = 0x000000c0;
+ s->regs[GT_CS0HD] = 0x000000c7;
+ s->regs[GT_CS1LD] = 0x000000c8;
+ s->regs[GT_CS1HD] = 0x000000cf;
+ s->regs[GT_CS2LD] = 0x000000d0;
+ s->regs[GT_CS2HD] = 0x000000df;
+ s->regs[GT_CS3LD] = 0x000000f0;
+ s->regs[GT_CS3HD] = 0x000000fb;
+ s->regs[GT_BOOTLD] = 0x000000fc;
+ s->regs[GT_BOOTHD] = 0x000000ff;
+ s->regs[GT_ADERR] = 0xffffffff;
+
+ /* SDRAM Configuration */
+ s->regs[GT_SDRAM_CFG] = 0x00000200;
+ s->regs[GT_SDRAM_OPMODE] = 0x00000000;
+ s->regs[GT_SDRAM_BM] = 0x00000007;
+ s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002;
+
+ /* SDRAM Parameters */
+ s->regs[GT_SDRAM_B0] = 0x00000005;
+ s->regs[GT_SDRAM_B1] = 0x00000005;
+ s->regs[GT_SDRAM_B2] = 0x00000005;
+ s->regs[GT_SDRAM_B3] = 0x00000005;
+
+ /* ECC */
+ s->regs[GT_ECC_ERRDATALO] = 0x00000000;
+ s->regs[GT_ECC_ERRDATAHI] = 0x00000000;
+ s->regs[GT_ECC_MEM] = 0x00000000;
+ s->regs[GT_ECC_CALC] = 0x00000000;
+ s->regs[GT_ECC_ERRADDR] = 0x00000000;
+
+ /* Device Parameters */
+ s->regs[GT_DEV_B0] = 0x386fffff;
+ s->regs[GT_DEV_B1] = 0x386fffff;
+ s->regs[GT_DEV_B2] = 0x386fffff;
+ s->regs[GT_DEV_B3] = 0x386fffff;
+ s->regs[GT_DEV_BOOT] = 0x146fffff;
+
+ /* DMA registers are all zeroed at reset */
+
+ /* Timer/Counter */
+ s->regs[GT_TC0] = 0xffffffff;
+ s->regs[GT_TC1] = 0x00ffffff;
+ s->regs[GT_TC2] = 0x00ffffff;
+ s->regs[GT_TC3] = 0x00ffffff;
+ s->regs[GT_TC_CONTROL] = 0x00000000;
+
+ /* PCI Internal */
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->regs[GT_PCI0_CMD] = 0x00000000;
+#else
+ s->regs[GT_PCI0_CMD] = 0x00010001;
+#endif
+ s->regs[GT_PCI0_TOR] = 0x0000070f;
+ s->regs[GT_PCI0_BS_SCS10] = 0x00fff000;
+ s->regs[GT_PCI0_BS_SCS32] = 0x00fff000;
+ s->regs[GT_PCI0_BS_CS20] = 0x01fff000;
+ s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000;
+ s->regs[GT_PCI1_IACK] = 0x00000000;
+ s->regs[GT_PCI0_IACK] = 0x00000000;
+ s->regs[GT_PCI0_BARE] = 0x0000000f;
+ s->regs[GT_PCI0_PREFMBR] = 0x00000040;
+ s->regs[GT_PCI0_SCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI0_SCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI0_CS20_BAR] = 0x1c000000;
+ s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000;
+ s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000;
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->regs[GT_PCI1_CMD] = 0x00000000;
+#else
+ s->regs[GT_PCI1_CMD] = 0x00010001;
+#endif
+ s->regs[GT_PCI1_TOR] = 0x0000070f;
+ s->regs[GT_PCI1_BS_SCS10] = 0x00fff000;
+ s->regs[GT_PCI1_BS_SCS32] = 0x00fff000;
+ s->regs[GT_PCI1_BS_CS20] = 0x01fff000;
+ s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000;
+ s->regs[GT_PCI1_BARE] = 0x0000000f;
+ s->regs[GT_PCI1_PREFMBR] = 0x00000040;
+ s->regs[GT_PCI1_SCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI1_SCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI1_CS20_BAR] = 0x1c000000;
+ s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000;
+ s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000;
+ s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000;
+ s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000;
+ s->regs[GT_PCI1_CFGADDR] = 0x00000000;
+ s->regs[GT_PCI1_CFGDATA] = 0x00000000;
+ s->regs[GT_PCI0_CFGADDR] = 0x00000000;
+
+ /* Interrupt registers are all zeroed at reset */
+
+ gt64120_isd_mapping(s);
+ gt64120_pci_mapping(s);
+}
+
+static void gt64120_save(QEMUFile* f, void *opaque)
+{
+ PCIDevice *d = opaque;
+ pci_device_save(d, f);
+}
+
+static int gt64120_load(QEMUFile* f, void *opaque, int version_id)
+{
+ PCIDevice *d = opaque;
+ int ret;
+
+ if (version_id != 1)
+ return -EINVAL;
+ ret = pci_device_load(d, f);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+PCIBus *gt64120_register(qemu_irq *pic)
+{
+ SysBusDevice *s;
+ GT64120State *d;
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "gt64120");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ d = FROM_SYSBUS(GT64120State, s);
+ d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci",
+ gt64120_pci_set_irq, gt64120_pci_map_irq,
+ pic, PCI_DEVFN(18, 0), 4);
+ d->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, d,
+ DEVICE_NATIVE_ENDIAN);
+
+ pci_create_simple(d->pci.bus, PCI_DEVFN(0, 0), "gt64120_pci");
+ return d->pci.bus;
+}
+
+static int gt64120_init(SysBusDevice *dev)
+{
+ GT64120State *s;
+
+ s = FROM_SYSBUS(GT64120State, dev);
+
+ /* FIXME: This value is computed from registers during reset, but some
+ devices (e.g. VGA card) need to know it when they are registered.
+ This also mean that changing the register to change the mapping
+ does not fully work. */
+ isa_mem_base = 0x10000000;
+ qemu_register_reset(gt64120_reset, s);
+ register_savevm(&dev->qdev, "GT64120 PCI Bus", 0, 1,
+ gt64120_save, gt64120_load, &s->pci);
+ return 0;
+}
+
+static int gt64120_pci_init(PCIDevice *d)
+{
+ /* FIXME: Malta specific hw assumptions ahead */
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MARVELL);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_MARVELL_GT6412X);
+ pci_set_word(d->config + PCI_COMMAND, 0);
+ pci_set_word(d->config + PCI_STATUS,
+ PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
+ pci_set_byte(d->config + PCI_CLASS_REVISION, 0x10);
+ pci_config_set_prog_interface(d->config, 0);
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000);
+ pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001);
+ pci_set_byte(d->config + 0x3d, 0x01);
+
+ return 0;
+}
+
+static PCIDeviceInfo gt64120_pci_info = {
+ .qdev.name = "gt64120_pci",
+ .qdev.size = sizeof(PCIDevice),
+ .init = gt64120_pci_init,
+};
+
+static void gt64120_pci_register_devices(void)
+{
+ sysbus_register_dev("gt64120", sizeof(GT64120State),
+ gt64120_init);
+ pci_qdev_register(&gt64120_pci_info);
+}
+
+device_init(gt64120_pci_register_devices)
diff --git a/hw/gumstix.c b/hw/gumstix.c
new file mode 100644
index 0000000..ee63f63
--- /dev/null
+++ b/hw/gumstix.c
@@ -0,0 +1,141 @@
+/*
+ * Gumstix Platforms
+ *
+ * Copyright (c) 2007 by Thorsten Zitterell <info@bitmux.org>
+ *
+ * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+/*
+ * Example usage:
+ *
+ * connex:
+ * =======
+ * create image:
+ * # dd of=flash bs=1k count=16k if=/dev/zero
+ * # dd of=flash bs=1k conv=notrunc if=u-boot.bin
+ * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2
+ * start it:
+ * # qemu-system-arm -M connex -pflash flash -monitor null -nographic
+ *
+ * verdex:
+ * =======
+ * create image:
+ * # dd of=flash bs=1k count=32k if=/dev/zero
+ * # dd of=flash bs=1k conv=notrunc if=u-boot.bin
+ * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2
+ * # dd of=flash bs=1k conv=notrunc seek=31744 if=uImage
+ * start it:
+ * # qemu-system-arm -M verdex -pflash flash -monitor null -nographic -m 289
+ */
+
+#include "hw.h"
+#include "pxa.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "blockdev.h"
+
+static const int sector_len = 128 * 1024;
+
+static void connex_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ PXA2xxState *cpu;
+ DriveInfo *dinfo;
+ int be;
+
+ uint32_t connex_rom = 0x01000000;
+ uint32_t connex_ram = 0x04000000;
+
+ cpu = pxa255_init(connex_ram);
+
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "A flash image must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ if (!pflash_cfi01_register(0x00000000, qemu_ram_alloc(NULL, "connext.rom",
+ connex_rom),
+ dinfo->bdrv, sector_len, connex_rom / sector_len,
+ 2, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+
+ /* Interrupt line of NIC is connected to GPIO line 36 */
+ smc91c111_init(&nd_table[0], 0x04000300,
+ qdev_get_gpio_in(cpu->gpio, 36));
+}
+
+static void verdex_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ PXA2xxState *cpu;
+ DriveInfo *dinfo;
+ int be;
+
+ uint32_t verdex_rom = 0x02000000;
+ uint32_t verdex_ram = 0x10000000;
+
+ cpu = pxa270_init(verdex_ram, cpu_model ?: "pxa270-c0");
+
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "A flash image must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ if (!pflash_cfi01_register(0x00000000, qemu_ram_alloc(NULL, "verdex.rom",
+ verdex_rom),
+ dinfo->bdrv, sector_len, verdex_rom / sector_len,
+ 2, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+
+ /* Interrupt line of NIC is connected to GPIO line 99 */
+ smc91c111_init(&nd_table[0], 0x04000300,
+ qdev_get_gpio_in(cpu->gpio, 99));
+}
+
+static QEMUMachine connex_machine = {
+ .name = "connex",
+ .desc = "Gumstix Connex (PXA255)",
+ .init = connex_init,
+};
+
+static QEMUMachine verdex_machine = {
+ .name = "verdex",
+ .desc = "Gumstix Verdex (PXA270)",
+ .init = verdex_init,
+};
+
+static void gumstix_machine_init(void)
+{
+ qemu_register_machine(&connex_machine);
+ qemu_register_machine(&verdex_machine);
+}
+
+machine_init(gumstix_machine_init);
diff --git a/hw/gus.c b/hw/gus.c
new file mode 100644
index 0000000..ff9e7c7
--- /dev/null
+++ b/hw/gus.c
@@ -0,0 +1,322 @@
+/*
+ * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
+ *
+ * Copyright (c) 2002-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "isa.h"
+#include "gusemu.h"
+#include "gustate.h"
+
+#define dolog(...) AUD_log ("audio", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define GUS_ENDIANNESS 1
+#else
+#define GUS_ENDIANNESS 0
+#endif
+
+#define IO_READ_PROTO(name) \
+ static uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+ static void name (void *opaque, uint32_t nport, uint32_t val)
+
+typedef struct GUSState {
+ ISADevice dev;
+ GUSEmuState emu;
+ QEMUSoundCard card;
+ uint32_t freq;
+ uint32_t port;
+ int pos, left, shift, irqs;
+ GUSsample *mixbuf;
+ uint8_t himem[1024 * 1024 + 32 + 4096];
+ int samples;
+ SWVoiceOut *voice;
+ int64_t last_ticks;
+ qemu_irq pic;
+} GUSState;
+
+IO_READ_PROTO (gus_readb)
+{
+ GUSState *s = opaque;
+
+ return gus_read (&s->emu, nport, 1);
+}
+
+IO_READ_PROTO (gus_readw)
+{
+ GUSState *s = opaque;
+
+ return gus_read (&s->emu, nport, 2);
+}
+
+IO_WRITE_PROTO (gus_writeb)
+{
+ GUSState *s = opaque;
+
+ gus_write (&s->emu, nport, 1, val);
+}
+
+IO_WRITE_PROTO (gus_writew)
+{
+ GUSState *s = opaque;
+
+ gus_write (&s->emu, nport, 2, val);
+}
+
+static int write_audio (GUSState *s, int samples)
+{
+ int net = 0;
+ int pos = s->pos;
+
+ while (samples) {
+ int nbytes, wbytes, wsampl;
+
+ nbytes = samples << s->shift;
+ wbytes = AUD_write (
+ s->voice,
+ s->mixbuf + (pos << (s->shift - 1)),
+ nbytes
+ );
+
+ if (wbytes) {
+ wsampl = wbytes >> s->shift;
+
+ samples -= wsampl;
+ pos = (pos + wsampl) % s->samples;
+
+ net += wsampl;
+ }
+ else {
+ break;
+ }
+ }
+
+ return net;
+}
+
+static void GUS_callback (void *opaque, int free)
+{
+ int samples, to_play, net = 0;
+ GUSState *s = opaque;
+
+ samples = free >> s->shift;
+ to_play = audio_MIN (samples, s->left);
+
+ while (to_play) {
+ int written = write_audio (s, to_play);
+
+ if (!written) {
+ goto reset;
+ }
+
+ s->left -= written;
+ to_play -= written;
+ samples -= written;
+ net += written;
+ }
+
+ samples = audio_MIN (samples, s->samples);
+ if (samples) {
+ gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
+
+ while (samples) {
+ int written = write_audio (s, samples);
+ if (!written) {
+ break;
+ }
+ samples -= written;
+ net += written;
+ }
+ }
+ s->left = samples;
+
+ reset:
+ gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq));
+}
+
+int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
+{
+ GUSState *s = emu->opaque;
+ /* qemu_irq_lower (s->pic); */
+ qemu_irq_raise (s->pic);
+ s->irqs += n;
+ ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
+ return n;
+}
+
+void GUS_irqclear (GUSEmuState *emu, int hwirq)
+{
+ GUSState *s = emu->opaque;
+ ldebug ("irqclear %d %d\n", hwirq, s->irqs);
+ qemu_irq_lower (s->pic);
+ s->irqs -= 1;
+#ifdef IRQ_STORM
+ if (s->irqs > 0) {
+ qemu_irq_raise (s->pic[hwirq]);
+ }
+#endif
+}
+
+void GUS_dmarequest (GUSEmuState *der)
+{
+ /* GUSState *s = (GUSState *) der; */
+ ldebug ("dma request %d\n", der->gusdma);
+ DMA_hold_DREQ (der->gusdma);
+}
+
+static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ GUSState *s = opaque;
+ char tmpbuf[4096];
+ int pos = dma_pos, mode, left = dma_len - dma_pos;
+
+ ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
+ mode = DMA_get_channel_mode (s->emu.gusdma);
+ while (left) {
+ int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
+ int copied;
+
+ ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
+ copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
+ gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
+ left -= copied;
+ pos += copied;
+ }
+
+ if (0 == ((mode >> 4) & 1)) {
+ DMA_release_DREQ (s->emu.gusdma);
+ }
+ return dma_len;
+}
+
+static const VMStateDescription vmstate_gus = {
+ .name = "gus",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(pos, GUSState),
+ VMSTATE_INT32(left, GUSState),
+ VMSTATE_INT32(shift, GUSState),
+ VMSTATE_INT32(irqs, GUSState),
+ VMSTATE_INT32(samples, GUSState),
+ VMSTATE_INT64(last_ticks, GUSState),
+ VMSTATE_BUFFER(himem, GUSState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int gus_initfn (ISADevice *dev)
+{
+ GUSState *s = DO_UPCAST(GUSState, dev, dev);
+ struct audsettings as;
+
+ AUD_register_card ("gus", &s->card);
+
+ as.freq = s->freq;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = GUS_ENDIANNESS;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ NULL,
+ "gus",
+ s,
+ GUS_callback,
+ &as
+ );
+
+ if (!s->voice) {
+ AUD_remove_card (&s->card);
+ return -1;
+ }
+
+ s->shift = 2;
+ s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
+ s->mixbuf = qemu_mallocz (s->samples << s->shift);
+
+ register_ioport_write (s->port, 1, 1, gus_writeb, s);
+ register_ioport_write (s->port, 1, 2, gus_writew, s);
+ isa_init_ioport_range(dev, s->port, 2);
+
+ register_ioport_read ((s->port + 0x100) & 0xf00, 1, 1, gus_readb, s);
+ register_ioport_read ((s->port + 0x100) & 0xf00, 1, 2, gus_readw, s);
+ isa_init_ioport_range(dev, (s->port + 0x100) & 0xf00, 2);
+
+ register_ioport_write (s->port + 6, 10, 1, gus_writeb, s);
+ register_ioport_write (s->port + 6, 10, 2, gus_writew, s);
+ register_ioport_read (s->port + 6, 10, 1, gus_readb, s);
+ register_ioport_read (s->port + 6, 10, 2, gus_readw, s);
+ isa_init_ioport_range(dev, s->port + 6, 10);
+
+
+ register_ioport_write (s->port + 0x100, 8, 1, gus_writeb, s);
+ register_ioport_write (s->port + 0x100, 8, 2, gus_writew, s);
+ register_ioport_read (s->port + 0x100, 8, 1, gus_readb, s);
+ register_ioport_read (s->port + 0x100, 8, 2, gus_readw, s);
+ isa_init_ioport_range(dev, s->port + 0x100, 8);
+
+ DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s);
+ s->emu.himemaddr = s->himem;
+ s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
+ s->emu.opaque = s;
+ isa_init_irq (dev, &s->pic, s->emu.gusirq);
+
+ AUD_set_active_out (s->voice, 1);
+
+ return 0;
+}
+
+int GUS_init (qemu_irq *pic)
+{
+ isa_create_simple ("gus");
+ return 0;
+}
+
+static ISADeviceInfo gus_info = {
+ .qdev.name = "gus",
+ .qdev.desc = "Gravis Ultrasound GF1",
+ .qdev.size = sizeof (GUSState),
+ .qdev.vmsd = &vmstate_gus,
+ .init = gus_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100),
+ DEFINE_PROP_HEX32 ("iobase", GUSState, port, 0x240),
+ DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7),
+ DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3),
+ DEFINE_PROP_END_OF_LIST (),
+ },
+};
+
+static void gus_register (void)
+{
+ isa_qdev_register (&gus_info);
+}
+device_init (gus_register)
diff --git a/hw/gusemu.h b/hw/gusemu.h
new file mode 100644
index 0000000..5093767
--- /dev/null
+++ b/hw/gusemu.h
@@ -0,0 +1,105 @@
+/*
+ * GUSEMU32 - API
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUSEMU_H
+#define GUSEMU_H
+
+/* data types (need to be adjusted if neither a VC6 nor a C99 compatible compiler is used) */
+
+#if defined _WIN32 && defined _MSC_VER /* doesnt support other win32 compilers yet, do it yourself... */
+ typedef unsigned char GUSbyte;
+ typedef unsigned short GUSword;
+ typedef unsigned int GUSdword;
+ typedef signed char GUSchar;
+ typedef signed short GUSsample;
+#else
+ #include <stdint.h>
+ typedef int8_t GUSchar;
+ typedef uint8_t GUSbyte;
+ typedef uint16_t GUSword;
+ typedef uint32_t GUSdword;
+ typedef int16_t GUSsample;
+#endif
+
+typedef struct _GUSEmuState
+{
+ GUSbyte *himemaddr; /* 1024*1024 bytes used for storing uploaded samples (+32 additional bytes for read padding) */
+ GUSbyte *gusdatapos; /* (gusdataend-gusdata) bytes used for storing emulated GF1/mixer register states (32*32+4 bytes in initial GUSemu32 version) */
+ uint32_t gusirq;
+ uint32_t gusdma;
+ unsigned int timer1fraction;
+ unsigned int timer2fraction;
+ void *opaque;
+} GUSEmuState;
+
+/* ** Callback functions needed: */
+/* NMI is defined as hwirq=-1 (not supported (yet?)) */
+/* GUS_irqrequest returns the number of IRQs actually scheduled into the virtual machine */
+/* Level triggered IRQ simulations normally return 1 */
+/* Event triggered IRQ simulation can safely ignore GUS_irqclear calls */
+int GUS_irqrequest(GUSEmuState *state, int hwirq, int num);/* needed in both mixer and bus emulation functions. */
+void GUS_irqclear( GUSEmuState *state, int hwirq); /* used by gus_write() only - can be left empty for mixer functions */
+void GUS_dmarequest(GUSEmuState *state); /* used by gus_write() only - can be left empty for mixer functions */
+
+/* ** ISA bus interface functions: */
+
+/* Port I/O handlers */
+/* support the following ports: */
+/* 2x0,2x6,2x8...2xF,3x0...3x7; */
+/* optional: 388,389 (at least writes should be forwarded or some GUS detection algorithms will fail) */
+/* data is passed in host byte order */
+unsigned int gus_read( GUSEmuState *state, int port, int size);
+void gus_write(GUSEmuState *state, int port, int size, unsigned int data);
+/* size is given in bytes (1 for byte, 2 for word) */
+
+/* DMA data transfer function */
+/* data pointed to is passed in native x86 order */
+void gus_dma_transferdata(GUSEmuState *state, char *dma_addr, unsigned int count, int TC);
+/* Called back by GUS_start_DMA as soon as the emulated DMA controller is ready for a transfer to or from GUS */
+/* (might be immediately if the DMA controller was programmed first) */
+/* dma_addr is an already translated address directly pointing to the beginning of the memory block */
+/* do not forget to update DMA states after the call, including the DREQ and TC flags */
+/* it is possible to break down a single transfer into multiple ones, but take care that: */
+/* -dma_count is actually count-1 */
+/* -before and during a transfer, DREQ is set and TC cleared */
+/* -when calling gus_dma_transferdata(), TC is only set true for call transfering the last byte */
+/* -after the last transfer, DREQ is cleared and TC is set */
+
+/* ** GF1 mixer emulation functions: */
+/* Usually, gus_irqgen should be called directly after gus_mixvoices if you can meet the recommended ranges. */
+/* If the interrupts are executed immediately (i.e., are synchronous), it may be useful to break this */
+/* down into a sequence of gus_mixvoice();gus_irqgen(); calls while mixing an audio block. */
+/* If the interrupts are asynchronous, it may be needed to use a separate thread mixing into a temporary */
+/* audio buffer in order to avoid quality loss caused by large numsamples and elapsed_time values. */
+
+void gus_mixvoices(GUSEmuState *state, unsigned int playback_freq, unsigned int numsamples, GUSsample *bufferpos);
+/* recommended range: 10 < numsamples < 100 */
+/* lower values may result in increased rounding error, higher values often cause audible timing delays */
+
+void gus_irqgen(GUSEmuState *state, unsigned int elapsed_time);
+/* recommended range: 80us < elapsed_time < max(1000us, numsamples/playback_freq) */
+/* lower values won´t provide any benefit at all, higher values can cause audible timing delays */
+/* note: masked timers are also calculated by this function, thus it might be needed even without any IRQs in use! */
+
+#endif /* gusemu.h */
diff --git a/hw/gusemu_hal.c b/hw/gusemu_hal.c
new file mode 100644
index 0000000..c6f9537
--- /dev/null
+++ b/hw/gusemu_hal.c
@@ -0,0 +1,554 @@
+/*
+ * GUSEMU32 - bus interface part
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)?
+ */
+
+#include "gustate.h"
+#include "gusemu.h"
+
+#define GUSregb(position) (* (gusptr+(position)))
+#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
+#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
+
+/* size given in bytes */
+unsigned int gus_read(GUSEmuState * state, int port, int size)
+{
+ int value_read = 0;
+
+ GUSbyte *gusptr;
+ gusptr = state->gusdatapos;
+ GUSregd(portaccesses)++;
+
+ switch (port & 0xff0f)
+ {
+ /* MixerCtrlReg (read not supported on GUS classic) */
+ /* case 0x200: return GUSregb(MixerCtrlReg2x0); */
+ case 0x206: /* IRQstatReg / SB2x6IRQ */
+ /* adlib/sb bits set in port handlers */
+ /* timer/voice bits set in gus_irqgen() */
+ /* dma bit set in gus_dma_transferdata */
+ /* midi not implemented yet */
+ return GUSregb(IRQStatReg2x6);
+ /* case 0x308: */ /* AdLib388 */
+ case 0x208:
+ if (GUSregb(GUS45TimerCtrl) & 1)
+ return GUSregb(TimerStatus2x8);
+ return GUSregb(AdLibStatus2x8); /* AdLibStatus */
+ case 0x309: /* AdLib389 */
+ case 0x209:
+ return GUSregb(AdLibData2x9); /* AdLibData */
+ case 0x20A:
+ return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */
+
+#if 0
+ case 0x20B: /* GUS hidden registers (read not supported on GUS classic) */
+ switch (GUSregb(RegCtrl_2xF) & 0x07)
+ {
+ case 0: /* IRQ/DMA select */
+ if (GUSregb(MixerCtrlReg2x0) & 0x40)
+ return GUSregb(IRQ_2xB); /* control register select bit */
+ else
+ return GUSregb(DMA_2xB);
+ /* case 1-5: */ /* general purpose emulation regs */
+ /* return ... */ /* + status reset reg (write only) */
+ case 6:
+ return GUSregb(Jumper_2xB); /* Joystick/MIDI enable (JumperReg) */
+ default:;
+ }
+ break;
+#endif
+
+ case 0x20C: /* SB2xCd */
+ value_read = GUSregb(SB2xCd);
+ if (GUSregb(StatRead_2xF) & 0x20)
+ GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */
+ return value_read;
+ /* case 0x20D: */ /* SB2xD is write only -> 2xE writes to it*/
+ case 0x20E:
+ if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */
+ {
+ GUSregb(StatRead_2xF) |= 0x80;
+ GUS_irqrequest(state, state->gusirq, 1);
+ }
+ return GUSregb(SB2xE); /* SB2xE */
+ case 0x20F: /* StatRead_2xF */
+ /*set/clear fixed bits */
+ /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/
+ value_read = (GUSregb(StatRead_2xF) & 0xf9);
+ if (GUSregb(MixerCtrlReg2x0) & 0x08)
+ value_read |= 2; /* DMA/IRQ enabled flag */
+ return value_read;
+ /* case 0x300: */ /* MIDI (not implemented) */
+ /* case 0x301: */ /* MIDI (not implemented) */
+ case 0x302:
+ return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */
+ case 0x303:
+ return GUSregb(FunkSelReg3x3); /* FunkSelReg */
+ case 0x304: /* DataRegLoByte3x4 + DataRegWord3x4 */
+ case 0x305: /* DataRegHiByte3x5 */
+ switch (GUSregb(FunkSelReg3x3))
+ {
+ /* common functions */
+ case 0x41: /* DramDMAContrReg */
+ value_read = GUSregb(GUS41DMACtrl); /* &0xfb */
+ GUSregb(GUS41DMACtrl) &= 0xbb;
+ if (state->gusdma >= 4)
+ value_read |= 0x04;
+ if (GUSregb(IRQStatReg2x6) & 0x80)
+ {
+ value_read |= 0x40;
+ GUSregb(IRQStatReg2x6) &= 0x7f;
+ if (!GUSregb(IRQStatReg2x6))
+ GUS_irqclear(state, state->gusirq);
+ }
+ return (GUSbyte) value_read;
+ /* DramDMAmemPosReg */
+ /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/
+ /* 43h+44h write only */
+ case 0x45:
+ return GUSregb(GUS45TimerCtrl); /* TimerCtrlReg */
+ /* 46h+47h write only */
+ /* 48h: samp freq - write only */
+ case 0x49:
+ return GUSregb(GUS49SampCtrl) & 0xbf; /* SampCtrlReg */
+ /* case 4bh: */ /* joystick trim not supported */
+ /* case 0x4c: return GUSregb(GUS4cReset); */ /* GUSreset: write only*/
+ /* voice specific functions */
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8a:
+ case 0x8b:
+ case 0x8c:
+ case 0x8d:
+ {
+ int offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
+ offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
+ value_read = GUSregw(offset);
+ }
+ break;
+ /* voice unspecific functions */
+ case 0x8e: /* NumVoice */
+ return GUSregb(NumVoices);
+ case 0x8f: /* irqstatreg */
+ /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */
+ return GUSregb(SynVoiceIRQ8f);
+ default:
+ return 0xffff;
+ }
+ if (size == 1)
+ {
+ if ((port & 0xff0f) == 0x305)
+ value_read = value_read >> 8;
+ value_read &= 0xff;
+ }
+ return (GUSword) value_read;
+ /* case 0x306: */ /* Mixer/Version info */
+ /* return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */
+ case 0x307: /* DRAMaccess */
+ {
+ GUSbyte *adr;
+ adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
+ return *adr;
+ }
+ default:;
+ }
+ return 0xffff;
+}
+
+void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
+{
+ GUSbyte *gusptr;
+ gusptr = state->gusdatapos;
+ GUSregd(portaccesses)++;
+
+ switch (port & 0xff0f)
+ {
+ case 0x200: /* MixerCtrlReg */
+ GUSregb(MixerCtrlReg2x0) = (GUSbyte) data;
+ break;
+ case 0x206: /* IRQstatReg / SB2x6IRQ */
+ if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */
+ {
+ GUSregb(TimerStatus2x8) |= 0x08;
+ GUSregb(IRQStatReg2x6) = 0x10;
+ GUS_irqrequest(state, state->gusirq, 1);
+ }
+ break;
+ case 0x308: /* AdLib 388h */
+ case 0x208: /* AdLibCommandReg */
+ GUSregb(AdLibCommand2xA) = (GUSbyte) data;
+ break;
+ case 0x309: /* AdLib 389h */
+ case 0x209: /* AdLibDataReg */
+ if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */
+ {
+ if (data & 0x80)
+ GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */
+ else
+ GUSregb(TimerDataReg2x9) = (GUSbyte) data;
+ }
+ else
+ {
+ GUSregb(AdLibData2x9) = (GUSbyte) data;
+ if (GUSregb(GUS45TimerCtrl) & 0x02)
+ {
+ GUSregb(TimerStatus2x8) |= 0x01;
+ GUSregb(IRQStatReg2x6) = 0x10;
+ GUS_irqrequest(state, state->gusirq, 1);
+ }
+ }
+ break;
+ case 0x20A:
+ GUSregb(AdLibStatus2x8) = (GUSbyte) data;
+ break; /* AdLibStatus2x8 */
+ case 0x20B: /* GUS hidden registers */
+ switch (GUSregb(RegCtrl_2xF) & 0x7)
+ {
+ case 0:
+ if (GUSregb(MixerCtrlReg2x0) & 0x40)
+ GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */
+ else
+ GUSregb(DMA_2xB) = (GUSbyte) data;
+ break;
+ /* case 1-4: general purpose emulation regs */
+ case 5: /* clear stat reg 2xF */
+ GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */
+ if (!GUSregb(IRQStatReg2x6))
+ GUS_irqclear(state, state->gusirq);
+ break;
+ case 6: /* Jumper reg (Joystick/MIDI enable) */
+ GUSregb(Jumper_2xB) = (GUSbyte) data;
+ break;
+ default:;
+ }
+ break;
+ case 0x20C: /* SB2xCd */
+ if (GUSregb(GUS45TimerCtrl) & 0x20)
+ {
+ GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */
+ GUSregb(IRQStatReg2x6) = 0x10;
+ GUS_irqrequest(state, state->gusirq, 1);
+ }
+ case 0x20D: /* SB2xCd no IRQ */
+ GUSregb(SB2xCd) = (GUSbyte) data;
+ break;
+ case 0x20E: /* SB2xE */
+ GUSregb(SB2xE) = (GUSbyte) data;
+ break;
+ case 0x20F:
+ GUSregb(RegCtrl_2xF) = (GUSbyte) data;
+ break; /* CtrlReg2xF */
+ case 0x302: /* VoiceSelReg */
+ GUSregb(VoiceSelReg3x2) = (GUSbyte) data;
+ break;
+ case 0x303: /* FunkSelReg */
+ GUSregb(FunkSelReg3x3) = (GUSbyte) data;
+ if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */
+ {
+ int voice;
+ if (GUSregd(voicewavetableirq)) /* WavetableIRQ */
+ {
+ for (voice = 0; voice < 31; voice++)
+ {
+ if (GUSregd(voicewavetableirq) & (1 << voice))
+ {
+ GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */
+ GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */
+ if (!GUSregd(voicewavetableirq))
+ GUSregb(IRQStatReg2x6) &= 0xdf;
+ if (!GUSregb(IRQStatReg2x6))
+ GUS_irqclear(state, state->gusirq);
+ GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */
+ return;
+ }
+ }
+ }
+ else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */
+ {
+ for (voice = 0; voice < 31; voice++)
+ {
+ if (GUSregd(voicevolrampirq) & (1 << voice))
+ {
+ GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */
+ GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */
+ if (!GUSregd(voicevolrampirq))
+ GUSregb(IRQStatReg2x6) &= 0xbf;
+ if (!GUSregb(IRQStatReg2x6))
+ GUS_irqclear(state, state->gusirq);
+ GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */
+ return;
+ }
+ }
+ }
+ GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */
+ }
+ break;
+ case 0x304:
+ case 0x305:
+ {
+ GUSword writedata = (GUSword) data;
+ GUSword readmask = 0x0000;
+ if (size == 1)
+ {
+ readmask = 0xff00;
+ writedata &= 0xff;
+ if ((port & 0xff0f) == 0x305)
+ {
+ writedata = (GUSword) (writedata << 8);
+ readmask = 0x00ff;
+ }
+ }
+ switch (GUSregb(FunkSelReg3x3))
+ {
+ /* voice specific functions */
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ {
+ int offset;
+ if (!(GUSregb(GUS4cReset) & 0x01))
+ break; /* reset flag active? */
+ offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
+ offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
+ GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata);
+ }
+ break;
+ /* voice unspecific functions */
+ case 0x0e: /* NumVoices */
+ GUSregb(NumVoices) = (GUSbyte) data;
+ break;
+ /* case 0x0f: */ /* read only */
+ /* common functions */
+ case 0x41: /* DramDMAContrReg */
+ GUSregb(GUS41DMACtrl) = (GUSbyte) data;
+ if (data & 0x01)
+ GUS_dmarequest(state);
+ break;
+ case 0x42: /* DramDMAmemPosReg */
+ GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata;
+ GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */
+ break;
+ case 0x43: /* DRAMaddrLo */
+ GUSregd(GUSDRAMPOS24bit) =
+ (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata;
+ break;
+ case 0x44: /* DRAMaddrHi */
+ GUSregd(GUSDRAMPOS24bit) =
+ (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16);
+ break;
+ case 0x45: /* TCtrlReg */
+ GUSregb(GUS45TimerCtrl) = (GUSbyte) data;
+ if (!(data & 0x20))
+ GUSregb(TimerStatus2x8) &= 0xe7; /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */
+ if (!(data & 0x02))
+ GUSregb(TimerStatus2x8) &= 0xfe; /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */
+ if (!(GUSregb(TimerStatus2x8) & 0x19))
+ GUSregb(IRQStatReg2x6) &= 0xef; /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */
+ /* catch up delayed timer IRQs: */
+ if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3))
+ {
+ if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
+ {
+ if (!(GUSregb(TimerDataReg2x9) & 0x40))
+ GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */
+ if (data & 4) /* timer1 irq enable */
+ {
+ GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */
+ GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */
+ }
+ }
+ if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */
+ {
+ if (!(GUSregb(TimerDataReg2x9) & 0x20))
+ GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */
+ if (data & 8) /* timer2 irq enable */
+ {
+ GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */
+ GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */
+ }
+ }
+ GUSregw(TimerIRQs)--;
+ if (GUSregw(BusyTimerIRQs) > 1)
+ GUSregw(BusyTimerIRQs)--;
+ else
+ GUSregw(BusyTimerIRQs) =
+ GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs));
+ }
+ else
+ GUSregw(TimerIRQs) = 0;
+
+ if (!(data & 0x04))
+ {
+ GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */
+ GUSregb(IRQStatReg2x6) &= 0xfb;
+ }
+ if (!(data & 0x08))
+ {
+ GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */
+ GUSregb(IRQStatReg2x6) &= 0xf7;
+ }
+ if (!GUSregb(IRQStatReg2x6))
+ GUS_irqclear(state, state->gusirq);
+ break;
+ case 0x46: /* Counter1 */
+ GUSregb(GUS46Counter1) = (GUSbyte) data;
+ break;
+ case 0x47: /* Counter2 */
+ GUSregb(GUS47Counter2) = (GUSbyte) data;
+ break;
+ /* case 0x48: */ /* sampling freq reg not emulated (same as interwave) */
+ case 0x49: /* SampCtrlReg */
+ GUSregb(GUS49SampCtrl) = (GUSbyte) data;
+ break;
+ /* case 0x4b: */ /* joystick trim not emulated */
+ case 0x4c: /* GUSreset */
+ GUSregb(GUS4cReset) = (GUSbyte) data;
+ if (!(GUSregb(GUS4cReset) & 1)) /* reset... */
+ {
+ GUSregd(voicewavetableirq) = 0;
+ GUSregd(voicevolrampirq) = 0;
+ GUSregw(TimerIRQs) = 0;
+ GUSregw(BusyTimerIRQs) = 0;
+ GUSregb(NumVoices) = 0xcd;
+ GUSregb(IRQStatReg2x6) = 0;
+ GUSregb(TimerStatus2x8) = 0;
+ GUSregb(AdLibData2x9) = 0;
+ GUSregb(TimerDataReg2x9) = 0;
+ GUSregb(GUS41DMACtrl) = 0;
+ GUSregb(GUS45TimerCtrl) = 0;
+ GUSregb(GUS49SampCtrl) = 0;
+ GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */
+ GUS_irqclear(state, state->gusirq);
+ }
+ /* IRQ enable bit checked elsewhere */
+ /* EnableDAC bit may be used by external callers */
+ break;
+ }
+ }
+ break;
+ case 0x307: /* DRAMaccess */
+ {
+ GUSbyte *adr;
+ adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
+ *adr = (GUSbyte) data;
+ }
+ break;
+ }
+}
+
+/* Attention when breaking up a single DMA transfer to multiple ones:
+ * it may lead to multiple terminal count interrupts and broken transfers:
+ *
+ * 1. Whenever you transfer a piece of data, the gusemu callback is invoked
+ * 2. The callback may generate a TC irq (if the register was set up to do so)
+ * 3. The irq may result in the program using the GUS to reprogram the GUS
+ *
+ * Some programs also decide to upload by just checking if TC occurs
+ * (via interrupt or a cleared GUS dma flag)
+ * and then start the next transfer, without checking DMA state
+ *
+ * Thus: Always make sure to set the TC flag correctly!
+ *
+ * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA
+ * while later cards had atomic granularity provided by an additional GUS50DMAHigh register
+ * GUSemu also uses this register to support byte-granular transfers for better compatibility
+ * with emulators other than GUSemu32
+ */
+
+void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC)
+{
+ /* this function gets called by the callback function as soon as a DMA transfer is about to start
+ * dma_addr is a translated address within accessible memory, not the physical one,
+ * count is (real dma count register)+1
+ * note that the amount of bytes transfered is fully determined by values in the DMA registers
+ * do not forget to update DMA states after transferring the entire block:
+ * DREQ cleared & TC asserted after the _whole_ transfer */
+
+ char *srcaddr;
+ char *destaddr;
+ char msbmask = 0;
+ GUSbyte *gusptr;
+ gusptr = state->gusdatapos;
+
+ srcaddr = dma_addr; /* system memory address */
+ {
+ int offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf);
+ if (state->gusdma >= 4)
+ offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */
+ destaddr = (char *) state->himemaddr + offset; /* wavetable RAM adress */
+ }
+
+ GUSregw(GUS42DMAStart) += (GUSword) (count >> 4); /* ToDo: add 16bit GUS page limit? */
+ GUSregb(GUS50DMAHigh) = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */
+
+ if (GUSregb(GUS41DMACtrl) & 0x02) /* direction, 0 := sysram->gusram */
+ {
+ char *tmpaddr = destaddr;
+ destaddr = srcaddr;
+ srcaddr = tmpaddr;
+ }
+
+ if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02)))
+ msbmask = (const char) 0x80; /* invert MSB */
+ for (; count > 0; count--)
+ {
+ if (GUSregb(GUS41DMACtrl) & 0x40)
+ *(destaddr++) = *(srcaddr++); /* 16 bit lobyte */
+ else
+ *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */
+ if (state->gusdma >= 4)
+ *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */
+ }
+
+ if (TC)
+ {
+ (GUSregb(GUS41DMACtrl)) &= 0xfe; /* clear DMA request bit */
+ if (GUSregb(GUS41DMACtrl) & 0x20) /* DMA terminal count IRQ */
+ {
+ GUSregb(IRQStatReg2x6) |= 0x80;
+ GUS_irqrequest(state, state->gusirq, 1);
+ }
+ }
+}
diff --git a/hw/gusemu_mixer.c b/hw/gusemu_mixer.c
new file mode 100644
index 0000000..6d8d9ce
--- /dev/null
+++ b/hw/gusemu_mixer.c
@@ -0,0 +1,240 @@
+/*
+ * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility)
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "gusemu.h"
+#include "gustate.h"
+
+#define GUSregb(position) (* (gusptr+(position)))
+#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
+#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
+
+#define GUSvoice(position) (*(GUSword *)(voiceptr+(position)))
+
+/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
+void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
+ GUSsample *bufferpos)
+{
+ /* note that byte registers are stored in the upper half of each voice register! */
+ GUSbyte *gusptr;
+ int Voice;
+ GUSword *voiceptr;
+
+ unsigned int count;
+ for (count = 0; count < numsamples * 2; count++)
+ *(bufferpos + count) = 0; /* clear */
+
+ gusptr = state->gusdatapos;
+ voiceptr = (GUSword *) gusptr;
+ if (!(GUSregb(GUS4cReset) & 0x01)) /* reset flag active? */
+ return;
+
+ for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++)
+ {
+ if (GUSvoice(wVSRControl) & 0x200)
+ GUSvoice(wVSRControl) |= 0x100; /* voice stop request */
+ if (GUSvoice(wVSRVolRampControl) & 0x200)
+ GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */
+ if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */
+ {
+ unsigned int sample;
+
+ unsigned int LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */
+ unsigned int LoopEnd = (GUSvoice(wVSRLoopEndHi) << 16) | GUSvoice(wVSRLoopEndLo); /* 23.9 format */
+ unsigned int CurrPos = (GUSvoice(wVSRCurrPosHi) << 16) | GUSvoice(wVSRCurrPosLo); /* 23.9 format */
+ int VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) /
+ ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */
+
+ int PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf;
+
+ unsigned int Volume32 = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */
+ unsigned int StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32;
+ unsigned int EndVol32 = (GUSvoice(wVSRVolRampEndVol) & 0xff00) * 32;
+ int VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */
+ VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */
+
+ if (GUSvoice(wVSRControl) & 0x4000)
+ VoiceIncrement = -VoiceIncrement; /* reverse playback */
+ if (GUSvoice(wVSRVolRampControl) & 0x4000)
+ VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */
+
+ for (sample = 0; sample < numsamples; sample++)
+ {
+ int sample1, sample2, Volume;
+ if (GUSvoice(wVSRControl) & 0x400) /* 16bit */
+ {
+ int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
+ GUSchar *adr;
+ adr = (GUSchar *) state->himemaddr + offset;
+ sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
+ sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
+ }
+ else /* 8bit */
+ {
+ int offset = (CurrPos >> 9) & 0xfffff;
+ GUSchar *adr;
+ adr = (GUSchar *) state->himemaddr + offset;
+ sample1 = (*adr) * 256;
+ sample2 = (*(adr + 1)) * 256;
+ }
+
+ Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */
+ sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512;
+ sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512;
+ sample1 += sample2;
+
+ if (!(GUSvoice(wVSRVolRampControl) & 0x100))
+ {
+ Volume32 += VolumeIncrement32;
+ if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */
+ {
+ if (GUSvoice(wVSRVolRampControl) & 0x2000)
+ GUSvoice(wVSRVolRampControl) |= 0x8000; /* volramp IRQ enabled? -> IRQ wait flag */
+ if (GUSvoice(wVSRVolRampControl) & 0x800) /* loop enabled */
+ {
+ if (GUSvoice(wVSRVolRampControl) & 0x1000) /* bidir. loop */
+ {
+ GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */
+ VolumeIncrement32 = -VolumeIncrement32;
+ }
+ else
+ Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */
+ }
+ else
+ {
+ GUSvoice(wVSRVolRampControl) |= 0x100;
+ Volume32 =
+ (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32;
+ }
+ }
+ }
+ if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000) /* volramp IRQ set and enabled? */
+ {
+ GUSregd(voicevolrampirq) |= 1 << Voice; /* set irq slot */
+ }
+ else
+ {
+ GUSregd(voicevolrampirq) &= (~(1 << Voice)); /* clear irq slot */
+ GUSvoice(wVSRVolRampControl) &= 0x7f00;
+ }
+
+ if (!(GUSvoice(wVSRControl) & 0x100))
+ {
+ CurrPos += VoiceIncrement;
+ if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */
+ {
+ if (GUSvoice(wVSRControl) & 0x2000)
+ GUSvoice(wVSRControl) |= 0x8000; /* voice IRQ enabled -> IRQ wait flag */
+ if (GUSvoice(wVSRControl) & 0x800) /* loop enabled */
+ {
+ if (GUSvoice(wVSRControl) & 0x1000) /* pingpong loop */
+ {
+ GUSvoice(wVSRControl) ^= 0x4000; /* toggle dir */
+ VoiceIncrement = -VoiceIncrement;
+ }
+ else
+ CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */
+ }
+ else if (!(GUSvoice(wVSRVolRampControl) & 0x400))
+ GUSvoice(wVSRControl) |= 0x100; /* loop disabled, rollover check */
+ }
+ }
+ if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000) /* wavetable IRQ set and enabled? */
+ {
+ GUSregd(voicewavetableirq) |= 1 << Voice; /* set irq slot */
+ }
+ else
+ {
+ GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */
+ GUSvoice(wVSRControl) &= 0x7f00;
+ }
+
+ /* mix samples into buffer */
+ *(bufferpos + 2 * sample) += (GUSsample) ((sample1 * PanningPos) >> 4); /* right */
+ *(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */
+ }
+ /* write back voice and volume */
+ GUSvoice(wVSRCurrVol) = Volume32 / 32;
+ GUSvoice(wVSRCurrPosHi) = CurrPos >> 16;
+ GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff;
+ }
+ voiceptr += 16; /* next voice */
+ }
+}
+
+void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
+/* time given in microseconds */
+{
+ int requestedIRQs = 0;
+ GUSbyte *gusptr;
+ gusptr = state->gusdatapos;
+ if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
+ {
+ unsigned int timer1fraction = state->timer1fraction;
+ int newtimerirqs;
+ newtimerirqs = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1)));
+ state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1)));
+ if (newtimerirqs)
+ {
+ if (!(GUSregb(TimerDataReg2x9) & 0x40))
+ GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */
+ if (GUSregb(GUS45TimerCtrl) & 4) /* timer1 irq enable */
+ {
+ GUSregb(TimerStatus2x8) |= 4; /* nonmaskable bit */
+ GUSregb(IRQStatReg2x6) |= 4; /* timer 1 irq pending */
+ GUSregw(TimerIRQs) += newtimerirqs;
+ requestedIRQs += newtimerirqs;
+ }
+ }
+ }
+ if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */
+ {
+ unsigned int timer2fraction = state->timer2fraction;
+ int newtimerirqs;
+ newtimerirqs = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2)));
+ state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2)));
+ if (newtimerirqs)
+ {
+ if (!(GUSregb(TimerDataReg2x9) & 0x20))
+ GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */
+ if (GUSregb(GUS45TimerCtrl) & 8) /* timer2 irq enable */
+ {
+ GUSregb(TimerStatus2x8) |= 2; /* nonmaskable bit */
+ GUSregb(IRQStatReg2x6) |= 8; /* timer 2 irq pending */
+ GUSregw(TimerIRQs) += newtimerirqs;
+ requestedIRQs += newtimerirqs;
+ }
+ }
+ }
+ if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */
+ {
+ if (GUSregd(voicewavetableirq))
+ GUSregb(IRQStatReg2x6) |= 0x20;
+ if (GUSregd(voicevolrampirq))
+ GUSregb(IRQStatReg2x6) |= 0x40;
+ }
+ if ((!requestedIRQs) && GUSregb(IRQStatReg2x6))
+ requestedIRQs++;
+ if (GUSregb(IRQStatReg2x6))
+ GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs);
+}
diff --git a/hw/gustate.h b/hw/gustate.h
new file mode 100644
index 0000000..ece903a
--- /dev/null
+++ b/hw/gustate.h
@@ -0,0 +1,132 @@
+/*
+ * GUSEMU32 - persistent GUS register state
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUSTATE_H
+#define GUSTATE_H
+
+/*state block offset*/
+#define gusdata (0)
+
+/* data stored using this structure is in host byte order! */
+
+/*access type*/
+#define PortRead (0)
+#define PortWrite (1)
+
+#define Port8Bitacc (0)
+#define Port16Bitacc (1)
+
+/*voice register offsets (in bytes)*/
+#define VSRegs (0)
+#define VSRControl (0)
+#define VSRegsEnd (VSRControl+VSRegs + 32*(16*2))
+#define VSRFreq (2)
+#define VSRLoopStartHi (4)
+#define VSRLoopStartLo (6)
+#define VSRLoopEndHi (8)
+#define VSRLoopEndLo (10)
+#define VSRVolRampRate (12)
+#define VSRVolRampStartVol (14)
+#define VSRVolRampEndVol (16)
+#define VSRCurrVol (18)
+#define VSRCurrPosHi (20)
+#define VSRCurrPosLo (22)
+#define VSRPanning (24)
+#define VSRVolRampControl (26)
+
+/*voice register offsets (in words)*/
+#define wVSRegs (0)
+#define wVSRControl (0)
+#define wVSRegsEnd (wVSRControl+wVSRegs + 32*(16))
+#define wVSRFreq (1)
+#define wVSRLoopStartHi (2)
+#define wVSRLoopStartLo (3)
+#define wVSRLoopEndHi (4)
+#define wVSRLoopEndLo (5)
+#define wVSRVolRampRate (6)
+#define wVSRVolRampStartVol (7)
+#define wVSRVolRampEndVol (8)
+#define wVSRCurrVol (9)
+#define wVSRCurrPosHi (10)
+#define wVSRCurrPosLo (11)
+#define wVSRPanning (12)
+#define wVSRVolRampControl (13)
+
+/*GUS register state block: 32 voices, padding filled with remaining registers*/
+#define DataRegLoByte3x4 (VSRVolRampControl+2)
+#define DataRegWord3x4 (DataRegLoByte3x4)
+#define DataRegHiByte3x5 (VSRVolRampControl+2 +1)
+#define DMA_2xB (VSRVolRampControl+2+2)
+#define IRQ_2xB (VSRVolRampControl+2+3)
+
+#define RegCtrl_2xF (VSRVolRampControl+2+(16*2))
+#define Jumper_2xB (VSRVolRampControl+2+(16*2)+1)
+#define GUS42DMAStart (VSRVolRampControl+2+(16*2)+2)
+
+#define GUS43DRAMIOlo (VSRVolRampControl+2+(16*2)*2)
+#define GUSDRAMPOS24bit (GUS43DRAMIOlo)
+#define GUS44DRAMIOhi (VSRVolRampControl+2+(16*2)*2+2)
+
+#define voicewavetableirq (VSRVolRampControl+2+(16*2)*3) /* voice IRQ pseudoqueue: 1 bit per voice */
+
+#define voicevolrampirq (VSRVolRampControl+2+(16*2)*4) /* voice IRQ pseudoqueue: 1 bit per voice */
+
+#define startvoices (VSRVolRampControl+2+(16*2)*5) /* statistics / optimizations */
+
+#define IRQStatReg2x6 (VSRVolRampControl+2+(16*2)*6)
+#define TimerStatus2x8 (VSRVolRampControl+2+(16*2)*6+1)
+#define TimerDataReg2x9 (VSRVolRampControl+2+(16*2)*6+2)
+#define MixerCtrlReg2x0 (VSRVolRampControl+2+(16*2)*6+3)
+
+#define VoiceSelReg3x2 (VSRVolRampControl+2+(16*2)*7)
+#define FunkSelReg3x3 (VSRVolRampControl+2+(16*2)*7+1)
+#define AdLibStatus2x8 (VSRVolRampControl+2+(16*2)*7+2)
+#define StatRead_2xF (VSRVolRampControl+2+(16*2)*7+3)
+
+#define GUS48SampSpeed (VSRVolRampControl+2+(16*2)*8)
+#define GUS41DMACtrl (VSRVolRampControl+2+(16*2)*8+1)
+#define GUS45TimerCtrl (VSRVolRampControl+2+(16*2)*8+2)
+#define GUS46Counter1 (VSRVolRampControl+2+(16*2)*8+3)
+
+#define GUS47Counter2 (VSRVolRampControl+2+(16*2)*9)
+#define GUS49SampCtrl (VSRVolRampControl+2+(16*2)*9+1)
+#define GUS4cReset (VSRVolRampControl+2+(16*2)*9+2)
+#define NumVoices (VSRVolRampControl+2+(16*2)*9+3)
+
+#define TimerIRQs (VSRVolRampControl+2+(16*2)*10) /* delayed IRQ, statistics */
+#define BusyTimerIRQs (VSRVolRampControl+2+(16*2)*10+2) /* delayed IRQ, statistics */
+
+#define AdLibCommand2xA (VSRVolRampControl+2+(16*2)*11)
+#define AdLibData2x9 (VSRVolRampControl+2+(16*2)*11+1)
+#define SB2xCd (VSRVolRampControl+2+(16*2)*11+2)
+#define SB2xE (VSRVolRampControl+2+(16*2)*11+3)
+
+#define SynVoiceIRQ8f (VSRVolRampControl+2+(16*2)*12)
+#define GUS50DMAHigh (VSRVolRampControl+2+(16*2)*12+1)
+
+#define portaccesses (VSRegsEnd) /* statistics / suspend mode */
+
+#define gusdataend (VSRegsEnd+4)
+
+#endif /* gustate.h */
diff --git a/hw/hda-audio.c b/hw/hda-audio.c
new file mode 100644
index 0000000..c699d6f
--- /dev/null
+++ b/hw/hda-audio.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * written by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "intel-hda.h"
+#include "intel-hda-defs.h"
+#include "audio/audio.h"
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct desc_param {
+ uint32_t id;
+ uint32_t val;
+} desc_param;
+
+typedef struct desc_node {
+ uint32_t nid;
+ const char *name;
+ const desc_param *params;
+ uint32_t nparams;
+ uint32_t config;
+ uint32_t pinctl;
+ uint32_t *conn;
+ uint32_t stindex;
+} desc_node;
+
+typedef struct desc_codec {
+ const char *name;
+ uint32_t iid;
+ const desc_node *nodes;
+ uint32_t nnodes;
+} desc_codec;
+
+static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
+{
+ int i;
+
+ for (i = 0; i < node->nparams; i++) {
+ if (node->params[i].id == id) {
+ return &node->params[i];
+ }
+ }
+ return NULL;
+}
+
+static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
+{
+ int i;
+
+ for (i = 0; i < codec->nnodes; i++) {
+ if (codec->nodes[i].nid == nid) {
+ return &codec->nodes[i];
+ }
+ }
+ return NULL;
+}
+
+static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
+{
+ if (format & AC_FMT_TYPE_NON_PCM) {
+ return;
+ }
+
+ as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
+
+ switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
+ case 1: as->freq *= 2; break;
+ case 2: as->freq *= 3; break;
+ case 3: as->freq *= 4; break;
+ }
+
+ switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
+ case 1: as->freq /= 2; break;
+ case 2: as->freq /= 3; break;
+ case 3: as->freq /= 4; break;
+ case 4: as->freq /= 5; break;
+ case 5: as->freq /= 6; break;
+ case 6: as->freq /= 7; break;
+ case 7: as->freq /= 8; break;
+ }
+
+ switch (format & AC_FMT_BITS_MASK) {
+ case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break;
+ case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
+ case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
+ }
+
+ as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * HDA codec descriptions
+ */
+
+/* some defines */
+
+#define QEMU_HDA_ID_VENDOR 0x1af4
+#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x10)
+#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x20)
+
+#define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \
+ 0x1fc /* 16 -> 96 kHz */)
+#define QEMU_HDA_AMP_NONE (0)
+#define QEMU_HDA_AMP_STEPS 0x4a
+
+#ifdef CONFIG_MIXEMU
+#define QEMU_HDA_AMP_CAPS \
+ (AC_AMPCAP_MUTE | \
+ (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \
+ (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \
+ (3 << AC_AMPCAP_STEP_SIZE_SHIFT))
+#else
+#define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE
+#endif
+
+/* common: audio output widget */
+static const desc_param common_params_audio_dac[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_FORMAT_OVRD |
+ AC_WCAP_AMP_OVRD |
+ AC_WCAP_OUT_AMP |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_CAPS,
+ },
+};
+
+/* common: pin widget (line-out) */
+static const desc_param common_params_audio_lineout[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_CONN_LIST |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_PIN_CAP,
+ .val = AC_PINCAP_OUT,
+ },{
+ .id = AC_PAR_CONNLIST_LEN,
+ .val = 1,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },
+};
+
+/* output: root node */
+static const desc_param output_params_root[] = {
+ {
+ .id = AC_PAR_VENDOR_ID,
+ .val = QEMU_HDA_ID_OUTPUT,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_OUTPUT,
+ },{
+ .id = AC_PAR_REV_ID,
+ .val = 0x00100101,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00010001,
+ },
+};
+
+/* output: audio function */
+static const desc_param output_params_audio_func[] = {
+ {
+ .id = AC_PAR_FUNCTION_TYPE,
+ .val = AC_GRP_AUDIO_FUNCTION,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_OUTPUT,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00020002,
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_GPIO_CAP,
+ .val = 0,
+ },{
+ .id = AC_PAR_AUDIO_FG_CAP,
+ .val = 0x00000808,
+ },{
+ .id = AC_PAR_POWER_STATE,
+ .val = 0,
+ },
+};
+
+/* output: nodes */
+static const desc_node output_nodes[] = {
+ {
+ .nid = AC_NODE_ROOT,
+ .name = "root",
+ .params = output_params_root,
+ .nparams = ARRAY_SIZE(output_params_root),
+ },{
+ .nid = 1,
+ .name = "func",
+ .params = output_params_audio_func,
+ .nparams = ARRAY_SIZE(output_params_audio_func),
+ },{
+ .nid = 2,
+ .name = "dac",
+ .params = common_params_audio_dac,
+ .nparams = ARRAY_SIZE(common_params_audio_dac),
+ .stindex = 0,
+ },{
+ .nid = 3,
+ .name = "out",
+ .params = common_params_audio_lineout,
+ .nparams = ARRAY_SIZE(common_params_audio_lineout),
+ .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+ (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) |
+ (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+ (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) |
+ 0x10),
+ .pinctl = AC_PINCTL_OUT_EN,
+ .conn = (uint32_t[]) { 2 },
+ }
+};
+
+/* output: codec */
+static const desc_codec output = {
+ .name = "output",
+ .iid = QEMU_HDA_ID_OUTPUT,
+ .nodes = output_nodes,
+ .nnodes = ARRAY_SIZE(output_nodes),
+};
+
+/* duplex: root node */
+static const desc_param duplex_params_root[] = {
+ {
+ .id = AC_PAR_VENDOR_ID,
+ .val = QEMU_HDA_ID_DUPLEX,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_DUPLEX,
+ },{
+ .id = AC_PAR_REV_ID,
+ .val = 0x00100101,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00010001,
+ },
+};
+
+/* duplex: audio input widget */
+static const desc_param duplex_params_audio_adc[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_CONN_LIST |
+ AC_WCAP_FORMAT_OVRD |
+ AC_WCAP_AMP_OVRD |
+ AC_WCAP_IN_AMP |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_CONNLIST_LEN,
+ .val = 1,
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_CAPS,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },
+};
+
+/* duplex: pin widget (line-in) */
+static const desc_param duplex_params_audio_linein[] = {
+ {
+ .id = AC_PAR_AUDIO_WIDGET_CAP,
+ .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
+ AC_WCAP_STEREO),
+ },{
+ .id = AC_PAR_PIN_CAP,
+ .val = AC_PINCAP_IN,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },
+};
+
+/* duplex: audio function */
+static const desc_param duplex_params_audio_func[] = {
+ {
+ .id = AC_PAR_FUNCTION_TYPE,
+ .val = AC_GRP_AUDIO_FUNCTION,
+ },{
+ .id = AC_PAR_SUBSYSTEM_ID,
+ .val = QEMU_HDA_ID_DUPLEX,
+ },{
+ .id = AC_PAR_NODE_COUNT,
+ .val = 0x00020004,
+ },{
+ .id = AC_PAR_PCM,
+ .val = QEMU_HDA_PCM_FORMATS,
+ },{
+ .id = AC_PAR_STREAM,
+ .val = AC_SUPFMT_PCM,
+ },{
+ .id = AC_PAR_AMP_IN_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_AMP_OUT_CAP,
+ .val = QEMU_HDA_AMP_NONE,
+ },{
+ .id = AC_PAR_GPIO_CAP,
+ .val = 0,
+ },{
+ .id = AC_PAR_AUDIO_FG_CAP,
+ .val = 0x00000808,
+ },{
+ .id = AC_PAR_POWER_STATE,
+ .val = 0,
+ },
+};
+
+/* duplex: nodes */
+static const desc_node duplex_nodes[] = {
+ {
+ .nid = AC_NODE_ROOT,
+ .name = "root",
+ .params = duplex_params_root,
+ .nparams = ARRAY_SIZE(duplex_params_root),
+ },{
+ .nid = 1,
+ .name = "func",
+ .params = duplex_params_audio_func,
+ .nparams = ARRAY_SIZE(duplex_params_audio_func),
+ },{
+ .nid = 2,
+ .name = "dac",
+ .params = common_params_audio_dac,
+ .nparams = ARRAY_SIZE(common_params_audio_dac),
+ .stindex = 0,
+ },{
+ .nid = 3,
+ .name = "out",
+ .params = common_params_audio_lineout,
+ .nparams = ARRAY_SIZE(common_params_audio_lineout),
+ .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+ (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) |
+ (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+ (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) |
+ 0x10),
+ .pinctl = AC_PINCTL_OUT_EN,
+ .conn = (uint32_t[]) { 2 },
+ },{
+ .nid = 4,
+ .name = "adc",
+ .params = duplex_params_audio_adc,
+ .nparams = ARRAY_SIZE(duplex_params_audio_adc),
+ .stindex = 1,
+ .conn = (uint32_t[]) { 5 },
+ },{
+ .nid = 5,
+ .name = "in",
+ .params = duplex_params_audio_linein,
+ .nparams = ARRAY_SIZE(duplex_params_audio_linein),
+ .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+ (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) |
+ (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+ (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) |
+ 0x20),
+ .pinctl = AC_PINCTL_IN_EN,
+ }
+};
+
+/* duplex: codec */
+static const desc_codec duplex = {
+ .name = "duplex",
+ .iid = QEMU_HDA_ID_DUPLEX,
+ .nodes = duplex_nodes,
+ .nnodes = ARRAY_SIZE(duplex_nodes),
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const char *fmt2name[] = {
+ [ AUD_FMT_U8 ] = "PCM-U8",
+ [ AUD_FMT_S8 ] = "PCM-S8",
+ [ AUD_FMT_U16 ] = "PCM-U16",
+ [ AUD_FMT_S16 ] = "PCM-S16",
+ [ AUD_FMT_U32 ] = "PCM-U32",
+ [ AUD_FMT_S32 ] = "PCM-S32",
+};
+
+typedef struct HDAAudioState HDAAudioState;
+typedef struct HDAAudioStream HDAAudioStream;
+
+struct HDAAudioStream {
+ HDAAudioState *state;
+ const desc_node *node;
+ bool output, running;
+ uint32_t stream;
+ uint32_t channel;
+ uint32_t format;
+ uint32_t gain_left, gain_right;
+ bool mute_left, mute_right;
+ struct audsettings as;
+ union {
+ SWVoiceIn *in;
+ SWVoiceOut *out;
+ } voice;
+ uint8_t buf[HDA_BUFFER_SIZE];
+ uint32_t bpos;
+};
+
+struct HDAAudioState {
+ HDACodecDevice hda;
+ const char *name;
+
+ QEMUSoundCard card;
+ const desc_codec *desc;
+ HDAAudioStream st[4];
+ bool running[16];
+
+ /* properties */
+ uint32_t debug;
+};
+
+static void hda_audio_input_cb(void *opaque, int avail)
+{
+ HDAAudioStream *st = opaque;
+ int recv = 0;
+ int len;
+ bool rc;
+
+ while (avail - recv >= sizeof(st->buf)) {
+ if (st->bpos != sizeof(st->buf)) {
+ len = AUD_read(st->voice.in, st->buf + st->bpos,
+ sizeof(st->buf) - st->bpos);
+ st->bpos += len;
+ recv += len;
+ if (st->bpos != sizeof(st->buf)) {
+ break;
+ }
+ }
+ rc = hda_codec_xfer(&st->state->hda, st->stream, false,
+ st->buf, sizeof(st->buf));
+ if (!rc) {
+ break;
+ }
+ st->bpos = 0;
+ }
+}
+
+static void hda_audio_output_cb(void *opaque, int avail)
+{
+ HDAAudioStream *st = opaque;
+ int sent = 0;
+ int len;
+ bool rc;
+
+ while (avail - sent >= sizeof(st->buf)) {
+ if (st->bpos == sizeof(st->buf)) {
+ rc = hda_codec_xfer(&st->state->hda, st->stream, true,
+ st->buf, sizeof(st->buf));
+ if (!rc) {
+ break;
+ }
+ st->bpos = 0;
+ }
+ len = AUD_write(st->voice.out, st->buf + st->bpos,
+ sizeof(st->buf) - st->bpos);
+ st->bpos += len;
+ sent += len;
+ if (st->bpos != sizeof(st->buf)) {
+ break;
+ }
+ }
+}
+
+static void hda_audio_set_running(HDAAudioStream *st, bool running)
+{
+ if (st->node == NULL) {
+ return;
+ }
+ if (st->running == running) {
+ return;
+ }
+ st->running = running;
+ dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
+ st->running ? "on" : "off", st->stream);
+ if (st->output) {
+ AUD_set_active_out(st->voice.out, st->running);
+ } else {
+ AUD_set_active_in(st->voice.in, st->running);
+ }
+}
+
+static void hda_audio_set_amp(HDAAudioStream *st)
+{
+ bool muted;
+ uint32_t left, right;
+
+ if (st->node == NULL) {
+ return;
+ }
+
+ muted = st->mute_left && st->mute_right;
+ left = st->mute_left ? 0 : st->gain_left;
+ right = st->mute_right ? 0 : st->gain_right;
+
+ left = left * 255 / QEMU_HDA_AMP_STEPS;
+ right = right * 255 / QEMU_HDA_AMP_STEPS;
+
+ if (st->output) {
+ AUD_set_volume_out(st->voice.out, muted, left, right);
+ } else {
+ AUD_set_volume_in(st->voice.in, muted, left, right);
+ }
+}
+
+static void hda_audio_setup(HDAAudioStream *st)
+{
+ if (st->node == NULL) {
+ return;
+ }
+
+ dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
+ st->node->name, st->as.nchannels,
+ fmt2name[st->as.fmt], st->as.freq);
+
+ if (st->output) {
+ st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
+ st->node->name, st,
+ hda_audio_output_cb, &st->as);
+ } else {
+ st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
+ st->node->name, st,
+ hda_audio_input_cb, &st->as);
+ }
+}
+
+static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioStream *st;
+ const desc_node *node = NULL;
+ const desc_param *param;
+ uint32_t verb, payload, response, count, shift;
+
+ if ((data & 0x70000) == 0x70000) {
+ /* 12/8 id/payload */
+ verb = (data >> 8) & 0xfff;
+ payload = data & 0x00ff;
+ } else {
+ /* 4/16 id/payload */
+ verb = (data >> 8) & 0xf00;
+ payload = data & 0xffff;
+ }
+
+ node = hda_codec_find_node(a->desc, nid);
+ if (node == NULL) {
+ goto fail;
+ }
+ dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
+ __FUNCTION__, nid, node->name, verb, payload);
+
+ switch (verb) {
+ /* all nodes */
+ case AC_VERB_PARAMETERS:
+ param = hda_codec_find_param(node, payload);
+ if (param == NULL) {
+ goto fail;
+ }
+ hda_codec_response(hda, true, param->val);
+ break;
+ case AC_VERB_GET_SUBSYSTEM_ID:
+ hda_codec_response(hda, true, a->desc->iid);
+ break;
+
+ /* all functions */
+ case AC_VERB_GET_CONNECT_LIST:
+ param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
+ count = param ? param->val : 0;
+ response = 0;
+ shift = 0;
+ while (payload < count && shift < 32) {
+ response |= node->conn[payload] << shift;
+ payload++;
+ shift += 8;
+ }
+ hda_codec_response(hda, true, response);
+ break;
+
+ /* pin widget */
+ case AC_VERB_GET_CONFIG_DEFAULT:
+ hda_codec_response(hda, true, node->config);
+ break;
+ case AC_VERB_GET_PIN_WIDGET_CONTROL:
+ hda_codec_response(hda, true, node->pinctl);
+ break;
+ case AC_VERB_SET_PIN_WIDGET_CONTROL:
+ if (node->pinctl != payload) {
+ dprint(a, 1, "unhandled pin control bit\n");
+ }
+ hda_codec_response(hda, true, 0);
+ break;
+
+ /* audio in/out widget */
+ case AC_VERB_SET_CHANNEL_STREAMID:
+ st = a->st + node->stindex;
+ if (st->node == NULL) {
+ goto fail;
+ }
+ hda_audio_set_running(st, false);
+ st->stream = (payload >> 4) & 0x0f;
+ st->channel = payload & 0x0f;
+ dprint(a, 2, "%s: stream %d, channel %d\n",
+ st->node->name, st->stream, st->channel);
+ hda_audio_set_running(st, a->running[st->stream]);
+ hda_codec_response(hda, true, 0);
+ break;
+ case AC_VERB_GET_CONV:
+ st = a->st + node->stindex;
+ if (st->node == NULL) {
+ goto fail;
+ }
+ response = st->stream << 4 | st->channel;
+ hda_codec_response(hda, true, response);
+ break;
+ case AC_VERB_SET_STREAM_FORMAT:
+ st = a->st + node->stindex;
+ if (st->node == NULL) {
+ goto fail;
+ }
+ st->format = payload;
+ hda_codec_parse_fmt(st->format, &st->as);
+ hda_audio_setup(st);
+ hda_codec_response(hda, true, 0);
+ break;
+ case AC_VERB_GET_STREAM_FORMAT:
+ st = a->st + node->stindex;
+ if (st->node == NULL) {
+ goto fail;
+ }
+ hda_codec_response(hda, true, st->format);
+ break;
+ case AC_VERB_GET_AMP_GAIN_MUTE:
+ st = a->st + node->stindex;
+ if (st->node == NULL) {
+ goto fail;
+ }
+ if (payload & AC_AMP_GET_LEFT) {
+ response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
+ } else {
+ response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
+ }
+ hda_codec_response(hda, true, response);
+ break;
+ case AC_VERB_SET_AMP_GAIN_MUTE:
+ st = a->st + node->stindex;
+ if (st->node == NULL) {
+ goto fail;
+ }
+ dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n",
+ st->node->name,
+ (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
+ (payload & AC_AMP_SET_INPUT) ? "i" : "-",
+ (payload & AC_AMP_SET_LEFT) ? "l" : "-",
+ (payload & AC_AMP_SET_RIGHT) ? "r" : "-",
+ (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
+ (payload & AC_AMP_GAIN),
+ (payload & AC_AMP_MUTE) ? "muted" : "");
+ if (payload & AC_AMP_SET_LEFT) {
+ st->gain_left = payload & AC_AMP_GAIN;
+ st->mute_left = payload & AC_AMP_MUTE;
+ }
+ if (payload & AC_AMP_SET_RIGHT) {
+ st->gain_right = payload & AC_AMP_GAIN;
+ st->mute_right = payload & AC_AMP_MUTE;
+ }
+ hda_audio_set_amp(st);
+ hda_codec_response(hda, true, 0);
+ break;
+
+ /* not supported */
+ case AC_VERB_SET_POWER_STATE:
+ case AC_VERB_GET_POWER_STATE:
+ case AC_VERB_GET_SDI_SELECT:
+ hda_codec_response(hda, true, 0);
+ break;
+ default:
+ goto fail;
+ }
+ return;
+
+fail:
+ dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
+ __FUNCTION__, nid, node ? node->name : "?", verb, payload);
+ hda_codec_response(hda, true, 0);
+}
+
+static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ int s;
+
+ a->running[stnr] = running;
+ for (s = 0; s < ARRAY_SIZE(a->st); s++) {
+ if (a->st[s].node == NULL) {
+ continue;
+ }
+ if (a->st[s].stream != stnr) {
+ continue;
+ }
+ hda_audio_set_running(&a->st[s], running);
+ }
+}
+
+static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioStream *st;
+ const desc_node *node;
+ const desc_param *param;
+ uint32_t i, type;
+
+ a->desc = desc;
+ a->name = a->hda.qdev.info->name;
+ dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
+
+ AUD_register_card("hda", &a->card);
+ for (i = 0; i < a->desc->nnodes; i++) {
+ node = a->desc->nodes + i;
+ param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
+ if (NULL == param)
+ continue;
+ type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ switch (type) {
+ case AC_WID_AUD_OUT:
+ case AC_WID_AUD_IN:
+ assert(node->stindex < ARRAY_SIZE(a->st));
+ st = a->st + node->stindex;
+ st->state = a;
+ st->node = node;
+ if (type == AC_WID_AUD_OUT) {
+ /* unmute output by default */
+ st->gain_left = QEMU_HDA_AMP_STEPS;
+ st->gain_right = QEMU_HDA_AMP_STEPS;
+ st->bpos = sizeof(st->buf);
+ st->output = true;
+ } else {
+ st->output = false;
+ }
+ st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
+ (1 << AC_FMT_CHAN_SHIFT);
+ hda_codec_parse_fmt(st->format, &st->as);
+ hda_audio_setup(st);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int hda_audio_exit(HDACodecDevice *hda)
+{
+ HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+ HDAAudioStream *st;
+ int i;
+
+ dprint(a, 1, "%s\n", __FUNCTION__);
+ for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+ st = a->st + i;
+ if (st->node == NULL) {
+ continue;
+ }
+ if (st->output) {
+ AUD_close_out(&a->card, st->voice.out);
+ } else {
+ AUD_close_in(&a->card, st->voice.in);
+ }
+ }
+ AUD_remove_card(&a->card);
+ return 0;
+}
+
+static int hda_audio_post_load(void *opaque, int version)
+{
+ HDAAudioState *a = opaque;
+ HDAAudioStream *st;
+ int i;
+
+ dprint(a, 1, "%s\n", __FUNCTION__);
+ for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+ st = a->st + i;
+ if (st->node == NULL)
+ continue;
+ hda_codec_parse_fmt(st->format, &st->as);
+ hda_audio_setup(st);
+ hda_audio_set_amp(st);
+ hda_audio_set_running(st, a->running[st->stream]);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_hda_audio_stream = {
+ .name = "hda-audio-stream",
+ .version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(stream, HDAAudioStream),
+ VMSTATE_UINT32(channel, HDAAudioStream),
+ VMSTATE_UINT32(format, HDAAudioStream),
+ VMSTATE_UINT32(gain_left, HDAAudioStream),
+ VMSTATE_UINT32(gain_right, HDAAudioStream),
+ VMSTATE_BOOL(mute_left, HDAAudioStream),
+ VMSTATE_BOOL(mute_right, HDAAudioStream),
+ VMSTATE_UINT32(bpos, HDAAudioStream),
+ VMSTATE_BUFFER(buf, HDAAudioStream),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_hda_audio = {
+ .name = "hda-audio",
+ .version_id = 1,
+ .post_load = hda_audio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
+ vmstate_hda_audio_stream,
+ HDAAudioStream),
+ VMSTATE_BOOL_ARRAY(running, HDAAudioState, 16),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property hda_audio_properties[] = {
+ DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static int hda_audio_init_output(HDACodecDevice *hda)
+{
+ return hda_audio_init(hda, &output);
+}
+
+static int hda_audio_init_duplex(HDACodecDevice *hda)
+{
+ return hda_audio_init(hda, &duplex);
+}
+
+static HDACodecDeviceInfo hda_audio_info_output = {
+ .qdev.name = "hda-output",
+ .qdev.desc = "HDA Audio Codec, output-only",
+ .qdev.size = sizeof(HDAAudioState),
+ .qdev.vmsd = &vmstate_hda_audio,
+ .qdev.props = hda_audio_properties,
+ .init = hda_audio_init_output,
+ .exit = hda_audio_exit,
+ .command = hda_audio_command,
+ .stream = hda_audio_stream,
+};
+
+static HDACodecDeviceInfo hda_audio_info_duplex = {
+ .qdev.name = "hda-duplex",
+ .qdev.desc = "HDA Audio Codec, duplex",
+ .qdev.size = sizeof(HDAAudioState),
+ .qdev.vmsd = &vmstate_hda_audio,
+ .qdev.props = hda_audio_properties,
+ .init = hda_audio_init_duplex,
+ .exit = hda_audio_exit,
+ .command = hda_audio_command,
+ .stream = hda_audio_stream,
+};
+
+static void hda_audio_register(void)
+{
+ hda_codec_register(&hda_audio_info_output);
+ hda_codec_register(&hda_audio_info_duplex);
+}
+device_init(hda_audio_register);
diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c
new file mode 100644
index 0000000..b19b754
--- /dev/null
+++ b/hw/heathrow_pic.c
@@ -0,0 +1,230 @@
+/*
+ * Heathrow PIC support (OldWorld PowerMac)
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc_mac.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+#ifdef DEBUG_PIC
+#define PIC_DPRINTF(fmt, ...) \
+ do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define PIC_DPRINTF(fmt, ...)
+#endif
+
+typedef struct HeathrowPIC {
+ uint32_t events;
+ uint32_t mask;
+ uint32_t levels;
+ uint32_t level_triggered;
+} HeathrowPIC;
+
+typedef struct HeathrowPICS {
+ HeathrowPIC pics[2];
+ qemu_irq *irqs;
+} HeathrowPICS;
+
+static inline int check_irq(HeathrowPIC *pic)
+{
+ return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
+}
+
+/* update the CPU irq state */
+static void heathrow_pic_update(HeathrowPICS *s)
+{
+ if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
+ qemu_irq_raise(s->irqs[0]);
+ } else {
+ qemu_irq_lower(s->irqs[0]);
+ }
+}
+
+static void pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int n;
+
+ n = ((addr & 0xfff) - 0x10) >> 4;
+ PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+ if (n >= 2)
+ return;
+ pic = &s->pics[n];
+ switch(addr & 0xf) {
+ case 0x04:
+ pic->mask = value;
+ heathrow_pic_update(s);
+ break;
+ case 0x08:
+ /* do not reset level triggered IRQs */
+ value &= ~pic->level_triggered;
+ pic->events &= ~value;
+ heathrow_pic_update(s);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t pic_readl (void *opaque, target_phys_addr_t addr)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int n;
+ uint32_t value;
+
+ n = ((addr & 0xfff) - 0x10) >> 4;
+ if (n >= 2) {
+ value = 0;
+ } else {
+ pic = &s->pics[n];
+ switch(addr & 0xf) {
+ case 0x0:
+ value = pic->events;
+ break;
+ case 0x4:
+ value = pic->mask;
+ break;
+ case 0xc:
+ value = pic->levels;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ }
+ PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+ return value;
+}
+
+static CPUWriteMemoryFunc * const pic_write[] = {
+ &pic_writel,
+ &pic_writel,
+ &pic_writel,
+};
+
+static CPUReadMemoryFunc * const pic_read[] = {
+ &pic_readl,
+ &pic_readl,
+ &pic_readl,
+};
+
+
+static void heathrow_pic_set_irq(void *opaque, int num, int level)
+{
+ HeathrowPICS *s = opaque;
+ HeathrowPIC *pic;
+ unsigned int irq_bit;
+
+#if defined(DEBUG)
+ {
+ static int last_level[64];
+ if (last_level[num] != level) {
+ PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level);
+ last_level[num] = level;
+ }
+ }
+#endif
+ pic = &s->pics[1 - (num >> 5)];
+ irq_bit = 1 << (num & 0x1f);
+ if (level) {
+ pic->events |= irq_bit & ~pic->level_triggered;
+ pic->levels |= irq_bit;
+ } else {
+ pic->levels &= ~irq_bit;
+ }
+ heathrow_pic_update(s);
+}
+
+static void heathrow_pic_save_one(QEMUFile *f, HeathrowPIC *s)
+{
+ qemu_put_be32s(f, &s->events);
+ qemu_put_be32s(f, &s->mask);
+ qemu_put_be32s(f, &s->levels);
+ qemu_put_be32s(f, &s->level_triggered);
+}
+
+static void heathrow_pic_save(QEMUFile *f, void *opaque)
+{
+ HeathrowPICS *s = (HeathrowPICS *)opaque;
+
+ heathrow_pic_save_one(f, &s->pics[0]);
+ heathrow_pic_save_one(f, &s->pics[1]);
+}
+
+static void heathrow_pic_load_one(QEMUFile *f, HeathrowPIC *s)
+{
+ qemu_get_be32s(f, &s->events);
+ qemu_get_be32s(f, &s->mask);
+ qemu_get_be32s(f, &s->levels);
+ qemu_get_be32s(f, &s->level_triggered);
+}
+
+static int heathrow_pic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ HeathrowPICS *s = (HeathrowPICS *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ heathrow_pic_load_one(f, &s->pics[0]);
+ heathrow_pic_load_one(f, &s->pics[1]);
+
+ return 0;
+}
+
+static void heathrow_pic_reset_one(HeathrowPIC *s)
+{
+ memset(s, '\0', sizeof(HeathrowPIC));
+}
+
+static void heathrow_pic_reset(void *opaque)
+{
+ HeathrowPICS *s = opaque;
+
+ heathrow_pic_reset_one(&s->pics[0]);
+ heathrow_pic_reset_one(&s->pics[1]);
+
+ s->pics[0].level_triggered = 0;
+ s->pics[1].level_triggered = 0x1ff00000;
+}
+
+qemu_irq *heathrow_pic_init(int *pmem_index,
+ int nb_cpus, qemu_irq **irqs)
+{
+ HeathrowPICS *s;
+
+ s = qemu_mallocz(sizeof(HeathrowPICS));
+ /* only 1 CPU */
+ s->irqs = irqs[0];
+ *pmem_index = cpu_register_io_memory(pic_read, pic_write, s,
+ DEVICE_LITTLE_ENDIAN);
+
+ register_savevm(NULL, "heathrow_pic", -1, 1, heathrow_pic_save,
+ heathrow_pic_load, s);
+ qemu_register_reset(heathrow_pic_reset, s);
+ return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64);
+}
diff --git a/hw/hpet.c b/hw/hpet.c
new file mode 100644
index 0000000..a48a664
--- /dev/null
+++ b/hw/hpet.c
@@ -0,0 +1,752 @@
+/*
+ * High Precisition Event Timer emulation
+ *
+ * Copyright (c) 2007 Alexander Graf
+ * Copyright (c) 2008 IBM Corporation
+ *
+ * Authors: Beth Kon <bkon@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * *****************************************************************
+ *
+ * This driver attempts to emulate an HPET device in software.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "console.h"
+#include "qemu-timer.h"
+#include "hpet_emul.h"
+#include "sysbus.h"
+#include "mc146818rtc.h"
+
+//#define HPET_DEBUG
+#ifdef HPET_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#define HPET_MSI_SUPPORT 0
+
+struct HPETState;
+typedef struct HPETTimer { /* timers */
+ uint8_t tn; /*timer number*/
+ QEMUTimer *qemu_timer;
+ struct HPETState *state;
+ /* Memory-mapped, software visible timer registers */
+ uint64_t config; /* configuration/cap */
+ uint64_t cmp; /* comparator */
+ uint64_t fsb; /* FSB route */
+ /* Hidden register state */
+ uint64_t period; /* Last value written to comparator */
+ uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit
+ * mode. Next pop will be actual timer expiration.
+ */
+} HPETTimer;
+
+typedef struct HPETState {
+ SysBusDevice busdev;
+ uint64_t hpet_offset;
+ qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
+ uint32_t flags;
+ uint8_t rtc_irq_level;
+ uint8_t num_timers;
+ HPETTimer timer[HPET_MAX_TIMERS];
+
+ /* Memory-mapped, software visible registers */
+ uint64_t capability; /* capabilities */
+ uint64_t config; /* configuration */
+ uint64_t isr; /* interrupt status reg */
+ uint64_t hpet_counter; /* main counter */
+ uint8_t hpet_id; /* instance id */
+} HPETState;
+
+struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
+
+static uint32_t hpet_in_legacy_mode(HPETState *s)
+{
+ return s->config & HPET_CFG_LEGACY;
+}
+
+static uint32_t timer_int_route(struct HPETTimer *timer)
+{
+ return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
+}
+
+static uint32_t timer_fsb_route(HPETTimer *t)
+{
+ return t->config & HPET_TN_FSB_ENABLE;
+}
+
+static uint32_t hpet_enabled(HPETState *s)
+{
+ return s->config & HPET_CFG_ENABLE;
+}
+
+static uint32_t timer_is_periodic(HPETTimer *t)
+{
+ return t->config & HPET_TN_PERIODIC;
+}
+
+static uint32_t timer_enabled(HPETTimer *t)
+{
+ return t->config & HPET_TN_ENABLE;
+}
+
+static uint32_t hpet_time_after(uint64_t a, uint64_t b)
+{
+ return ((int32_t)(b) - (int32_t)(a) < 0);
+}
+
+static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
+{
+ return ((int64_t)(b) - (int64_t)(a) < 0);
+}
+
+static uint64_t ticks_to_ns(uint64_t value)
+{
+ return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
+}
+
+static uint64_t ns_to_ticks(uint64_t value)
+{
+ return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
+}
+
+static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
+{
+ new &= mask;
+ new |= old & ~mask;
+ return new;
+}
+
+static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
+{
+ return (!(old & mask) && (new & mask));
+}
+
+static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
+{
+ return ((old & mask) && !(new & mask));
+}
+
+static uint64_t hpet_get_ticks(HPETState *s)
+{
+ return ns_to_ticks(qemu_get_clock(vm_clock) + s->hpet_offset);
+}
+
+/*
+ * calculate diff between comparator value and current ticks
+ */
+static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
+{
+
+ if (t->config & HPET_TN_32BIT) {
+ uint32_t diff, cmp;
+
+ cmp = (uint32_t)t->cmp;
+ diff = cmp - (uint32_t)current;
+ diff = (int32_t)diff > 0 ? diff : (uint32_t)0;
+ return (uint64_t)diff;
+ } else {
+ uint64_t diff, cmp;
+
+ cmp = t->cmp;
+ diff = cmp - current;
+ diff = (int64_t)diff > 0 ? diff : (uint64_t)0;
+ return diff;
+ }
+}
+
+static void update_irq(struct HPETTimer *timer, int set)
+{
+ uint64_t mask;
+ HPETState *s;
+ int route;
+
+ if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
+ /* if LegacyReplacementRoute bit is set, HPET specification requires
+ * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
+ * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
+ */
+ route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
+ } else {
+ route = timer_int_route(timer);
+ }
+ s = timer->state;
+ mask = 1 << timer->tn;
+ if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
+ s->isr &= ~mask;
+ if (!timer_fsb_route(timer)) {
+ qemu_irq_lower(s->irqs[route]);
+ }
+ } else if (timer_fsb_route(timer)) {
+ stl_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
+ } else if (timer->config & HPET_TN_TYPE_LEVEL) {
+ s->isr |= mask;
+ qemu_irq_raise(s->irqs[route]);
+ } else {
+ s->isr &= ~mask;
+ qemu_irq_pulse(s->irqs[route]);
+ }
+}
+
+static void hpet_pre_save(void *opaque)
+{
+ HPETState *s = opaque;
+
+ /* save current counter value */
+ s->hpet_counter = hpet_get_ticks(s);
+}
+
+static int hpet_pre_load(void *opaque)
+{
+ HPETState *s = opaque;
+
+ /* version 1 only supports 3, later versions will load the actual value */
+ s->num_timers = HPET_MIN_TIMERS;
+ return 0;
+}
+
+static int hpet_post_load(void *opaque, int version_id)
+{
+ HPETState *s = opaque;
+
+ /* Recalculate the offset between the main counter and guest time */
+ s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock);
+
+ /* Push number of timers into capability returned via HPET_ID */
+ s->capability &= ~HPET_ID_NUM_TIM_MASK;
+ s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
+ hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
+
+ /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
+ s->flags &= ~(1 << HPET_MSI_SUPPORT);
+ if (s->timer[0].config & HPET_TN_FSB_CAP) {
+ s->flags |= 1 << HPET_MSI_SUPPORT;
+ }
+
+ if (hpet_in_legacy_mode(s)) {
+ hpet_pit_disable();
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_hpet_timer = {
+ .name = "hpet_timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(tn, HPETTimer),
+ VMSTATE_UINT64(config, HPETTimer),
+ VMSTATE_UINT64(cmp, HPETTimer),
+ VMSTATE_UINT64(fsb, HPETTimer),
+ VMSTATE_UINT64(period, HPETTimer),
+ VMSTATE_UINT8(wrap_flag, HPETTimer),
+ VMSTATE_TIMER(qemu_timer, HPETTimer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_hpet = {
+ .name = "hpet",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = hpet_pre_save,
+ .pre_load = hpet_pre_load,
+ .post_load = hpet_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(config, HPETState),
+ VMSTATE_UINT64(isr, HPETState),
+ VMSTATE_UINT64(hpet_counter, HPETState),
+ VMSTATE_UINT8_V(num_timers, HPETState, 2),
+ VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
+ vmstate_hpet_timer, HPETTimer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * timer expiration callback
+ */
+static void hpet_timer(void *opaque)
+{
+ HPETTimer *t = opaque;
+ uint64_t diff;
+
+ uint64_t period = t->period;
+ uint64_t cur_tick = hpet_get_ticks(t->state);
+
+ if (timer_is_periodic(t) && period != 0) {
+ if (t->config & HPET_TN_32BIT) {
+ while (hpet_time_after(cur_tick, t->cmp)) {
+ t->cmp = (uint32_t)(t->cmp + t->period);
+ }
+ } else {
+ while (hpet_time_after64(cur_tick, t->cmp)) {
+ t->cmp += period;
+ }
+ }
+ diff = hpet_calculate_diff(t, cur_tick);
+ qemu_mod_timer(t->qemu_timer,
+ qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff));
+ } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
+ if (t->wrap_flag) {
+ diff = hpet_calculate_diff(t, cur_tick);
+ qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) +
+ (int64_t)ticks_to_ns(diff));
+ t->wrap_flag = 0;
+ }
+ }
+ update_irq(t, 1);
+}
+
+static void hpet_set_timer(HPETTimer *t)
+{
+ uint64_t diff;
+ uint32_t wrap_diff; /* how many ticks until we wrap? */
+ uint64_t cur_tick = hpet_get_ticks(t->state);
+
+ /* whenever new timer is being set up, make sure wrap_flag is 0 */
+ t->wrap_flag = 0;
+ diff = hpet_calculate_diff(t, cur_tick);
+
+ /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
+ * counter wraps in addition to an interrupt with comparator match.
+ */
+ if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
+ wrap_diff = 0xffffffff - (uint32_t)cur_tick;
+ if (wrap_diff < (uint32_t)diff) {
+ diff = wrap_diff;
+ t->wrap_flag = 1;
+ }
+ }
+ qemu_mod_timer(t->qemu_timer,
+ qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff));
+}
+
+static void hpet_del_timer(HPETTimer *t)
+{
+ qemu_del_timer(t->qemu_timer);
+ update_irq(t, 0);
+}
+
+#ifdef HPET_DEBUG
+static uint32_t hpet_ram_readb(void *opaque, target_phys_addr_t addr)
+{
+ printf("qemu: hpet_read b at %" PRIx64 "\n", addr);
+ return 0;
+}
+
+static uint32_t hpet_ram_readw(void *opaque, target_phys_addr_t addr)
+{
+ printf("qemu: hpet_read w at %" PRIx64 "\n", addr);
+ return 0;
+}
+#endif
+
+static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr)
+{
+ HPETState *s = opaque;
+ uint64_t cur_tick, index;
+
+ DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr);
+ index = addr;
+ /*address range of all TN regs*/
+ if (index >= 0x100 && index <= 0x3ff) {
+ uint8_t timer_id = (addr - 0x100) / 0x20;
+ HPETTimer *timer = &s->timer[timer_id];
+
+ if (timer_id > s->num_timers) {
+ DPRINTF("qemu: timer id out of range\n");
+ return 0;
+ }
+
+ switch ((addr - 0x100) % 0x20) {
+ case HPET_TN_CFG:
+ return timer->config;
+ case HPET_TN_CFG + 4: // Interrupt capabilities
+ return timer->config >> 32;
+ case HPET_TN_CMP: // comparator register
+ return timer->cmp;
+ case HPET_TN_CMP + 4:
+ return timer->cmp >> 32;
+ case HPET_TN_ROUTE:
+ return timer->fsb;
+ case HPET_TN_ROUTE + 4:
+ return timer->fsb >> 32;
+ default:
+ DPRINTF("qemu: invalid hpet_ram_readl\n");
+ break;
+ }
+ } else {
+ switch (index) {
+ case HPET_ID:
+ return s->capability;
+ case HPET_PERIOD:
+ return s->capability >> 32;
+ case HPET_CFG:
+ return s->config;
+ case HPET_CFG + 4:
+ DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n");
+ return 0;
+ case HPET_COUNTER:
+ if (hpet_enabled(s)) {
+ cur_tick = hpet_get_ticks(s);
+ } else {
+ cur_tick = s->hpet_counter;
+ }
+ DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick);
+ return cur_tick;
+ case HPET_COUNTER + 4:
+ if (hpet_enabled(s)) {
+ cur_tick = hpet_get_ticks(s);
+ } else {
+ cur_tick = s->hpet_counter;
+ }
+ DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick);
+ return cur_tick >> 32;
+ case HPET_STATUS:
+ return s->isr;
+ default:
+ DPRINTF("qemu: invalid hpet_ram_readl\n");
+ break;
+ }
+ }
+ return 0;
+}
+
+#ifdef HPET_DEBUG
+static void hpet_ram_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ printf("qemu: invalid hpet_write b at %" PRIx64 " = %#x\n",
+ addr, value);
+}
+
+static void hpet_ram_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ printf("qemu: invalid hpet_write w at %" PRIx64 " = %#x\n",
+ addr, value);
+}
+#endif
+
+static void hpet_ram_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ int i;
+ HPETState *s = opaque;
+ uint64_t old_val, new_val, val, index;
+
+ DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value);
+ index = addr;
+ old_val = hpet_ram_readl(opaque, addr);
+ new_val = value;
+
+ /*address range of all TN regs*/
+ if (index >= 0x100 && index <= 0x3ff) {
+ uint8_t timer_id = (addr - 0x100) / 0x20;
+ HPETTimer *timer = &s->timer[timer_id];
+
+ DPRINTF("qemu: hpet_ram_writel timer_id = %#x \n", timer_id);
+ if (timer_id > s->num_timers) {
+ DPRINTF("qemu: timer id out of range\n");
+ return;
+ }
+ switch ((addr - 0x100) % 0x20) {
+ case HPET_TN_CFG:
+ DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n");
+ if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
+ update_irq(timer, 0);
+ }
+ val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
+ timer->config = (timer->config & 0xffffffff00000000ULL) | val;
+ if (new_val & HPET_TN_32BIT) {
+ timer->cmp = (uint32_t)timer->cmp;
+ timer->period = (uint32_t)timer->period;
+ }
+ if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+ hpet_set_timer(timer);
+ } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+ hpet_del_timer(timer);
+ }
+ break;
+ case HPET_TN_CFG + 4: // Interrupt capabilities
+ DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n");
+ break;
+ case HPET_TN_CMP: // comparator register
+ DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP \n");
+ if (timer->config & HPET_TN_32BIT) {
+ new_val = (uint32_t)new_val;
+ }
+ if (!timer_is_periodic(timer)
+ || (timer->config & HPET_TN_SETVAL)) {
+ timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
+ }
+ if (timer_is_periodic(timer)) {
+ /*
+ * FIXME: Clamp period to reasonable min value?
+ * Clamp period to reasonable max value
+ */
+ new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
+ timer->period =
+ (timer->period & 0xffffffff00000000ULL) | new_val;
+ }
+ timer->config &= ~HPET_TN_SETVAL;
+ if (hpet_enabled(s)) {
+ hpet_set_timer(timer);
+ }
+ break;
+ case HPET_TN_CMP + 4: // comparator register high order
+ DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n");
+ if (!timer_is_periodic(timer)
+ || (timer->config & HPET_TN_SETVAL)) {
+ timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32;
+ } else {
+ /*
+ * FIXME: Clamp period to reasonable min value?
+ * Clamp period to reasonable max value
+ */
+ new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
+ timer->period =
+ (timer->period & 0xffffffffULL) | new_val << 32;
+ }
+ timer->config &= ~HPET_TN_SETVAL;
+ if (hpet_enabled(s)) {
+ hpet_set_timer(timer);
+ }
+ break;
+ case HPET_TN_ROUTE:
+ timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
+ break;
+ case HPET_TN_ROUTE + 4:
+ timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
+ break;
+ default:
+ DPRINTF("qemu: invalid hpet_ram_writel\n");
+ break;
+ }
+ return;
+ } else {
+ switch (index) {
+ case HPET_ID:
+ return;
+ case HPET_CFG:
+ val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
+ s->config = (s->config & 0xffffffff00000000ULL) | val;
+ if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
+ /* Enable main counter and interrupt generation. */
+ s->hpet_offset =
+ ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock);
+ for (i = 0; i < s->num_timers; i++) {
+ if ((&s->timer[i])->cmp != ~0ULL) {
+ hpet_set_timer(&s->timer[i]);
+ }
+ }
+ } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
+ /* Halt main counter and disable interrupt generation. */
+ s->hpet_counter = hpet_get_ticks(s);
+ for (i = 0; i < s->num_timers; i++) {
+ hpet_del_timer(&s->timer[i]);
+ }
+ }
+ /* i8254 and RTC are disabled when HPET is in legacy mode */
+ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
+ hpet_pit_disable();
+ qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
+ } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
+ hpet_pit_enable();
+ qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
+ }
+ break;
+ case HPET_CFG + 4:
+ DPRINTF("qemu: invalid HPET_CFG+4 write \n");
+ break;
+ case HPET_STATUS:
+ val = new_val & s->isr;
+ for (i = 0; i < s->num_timers; i++) {
+ if (val & (1 << i)) {
+ update_irq(&s->timer[i], 0);
+ }
+ }
+ break;
+ case HPET_COUNTER:
+ if (hpet_enabled(s)) {
+ DPRINTF("qemu: Writing counter while HPET enabled!\n");
+ }
+ s->hpet_counter =
+ (s->hpet_counter & 0xffffffff00000000ULL) | value;
+ DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n",
+ value, s->hpet_counter);
+ break;
+ case HPET_COUNTER + 4:
+ if (hpet_enabled(s)) {
+ DPRINTF("qemu: Writing counter while HPET enabled!\n");
+ }
+ s->hpet_counter =
+ (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32);
+ DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n",
+ value, s->hpet_counter);
+ break;
+ default:
+ DPRINTF("qemu: invalid hpet_ram_writel\n");
+ break;
+ }
+ }
+}
+
+static CPUReadMemoryFunc * const hpet_ram_read[] = {
+#ifdef HPET_DEBUG
+ hpet_ram_readb,
+ hpet_ram_readw,
+#else
+ NULL,
+ NULL,
+#endif
+ hpet_ram_readl,
+};
+
+static CPUWriteMemoryFunc * const hpet_ram_write[] = {
+#ifdef HPET_DEBUG
+ hpet_ram_writeb,
+ hpet_ram_writew,
+#else
+ NULL,
+ NULL,
+#endif
+ hpet_ram_writel,
+};
+
+static void hpet_reset(DeviceState *d)
+{
+ HPETState *s = FROM_SYSBUS(HPETState, sysbus_from_qdev(d));
+ int i;
+ static int count = 0;
+
+ for (i = 0; i < s->num_timers; i++) {
+ HPETTimer *timer = &s->timer[i];
+
+ hpet_del_timer(timer);
+ timer->cmp = ~0ULL;
+ timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
+ if (s->flags & (1 << HPET_MSI_SUPPORT)) {
+ timer->config |= HPET_TN_FSB_CAP;
+ }
+ /* advertise availability of ioapic inti2 */
+ timer->config |= 0x00000004ULL << 32;
+ timer->period = 0ULL;
+ timer->wrap_flag = 0;
+ }
+
+ s->hpet_counter = 0ULL;
+ s->hpet_offset = 0ULL;
+ s->config = 0ULL;
+ if (count > 0) {
+ /* we don't enable pit when hpet_reset is first called (by hpet_init)
+ * because hpet is taking over for pit here. On subsequent invocations,
+ * hpet_reset is called due to system reset. At this point control must
+ * be returned to pit until SW reenables hpet.
+ */
+ hpet_pit_enable();
+ }
+ hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
+ hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr;
+ count = 1;
+}
+
+static void hpet_handle_rtc_irq(void *opaque, int n, int level)
+{
+ HPETState *s = FROM_SYSBUS(HPETState, opaque);
+
+ s->rtc_irq_level = level;
+ if (!hpet_in_legacy_mode(s)) {
+ qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
+ }
+}
+
+static int hpet_init(SysBusDevice *dev)
+{
+ HPETState *s = FROM_SYSBUS(HPETState, dev);
+ int i, iomemtype;
+ HPETTimer *timer;
+
+ if (hpet_cfg.count == UINT8_MAX) {
+ /* first instance */
+ hpet_cfg.count = 0;
+ }
+
+ if (hpet_cfg.count == 8) {
+ fprintf(stderr, "Only 8 instances of HPET is allowed\n");
+ return -1;
+ }
+
+ s->hpet_id = hpet_cfg.count++;
+
+ for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
+ sysbus_init_irq(dev, &s->irqs[i]);
+ }
+
+ if (s->num_timers < HPET_MIN_TIMERS) {
+ s->num_timers = HPET_MIN_TIMERS;
+ } else if (s->num_timers > HPET_MAX_TIMERS) {
+ s->num_timers = HPET_MAX_TIMERS;
+ }
+ for (i = 0; i < HPET_MAX_TIMERS; i++) {
+ timer = &s->timer[i];
+ timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer);
+ timer->tn = i;
+ timer->state = s;
+ }
+
+ /* 64-bit main counter; LegacyReplacementRoute. */
+ s->capability = 0x8086a001ULL;
+ s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
+ s->capability |= ((HPET_CLK_PERIOD) << 32);
+
+ qdev_init_gpio_in(&dev->qdev, hpet_handle_rtc_irq, 1);
+
+ /* HPET Area */
+ iomemtype = cpu_register_io_memory(hpet_ram_read,
+ hpet_ram_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x400, iomemtype);
+ return 0;
+}
+
+static SysBusDeviceInfo hpet_device_info = {
+ .qdev.name = "hpet",
+ .qdev.size = sizeof(HPETState),
+ .qdev.no_user = 1,
+ .qdev.vmsd = &vmstate_hpet,
+ .qdev.reset = hpet_reset,
+ .init = hpet_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
+ DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void hpet_register_device(void)
+{
+ sysbus_register_withprop(&hpet_device_info);
+}
+
+device_init(hpet_register_device)
diff --git a/hw/hpet_emul.h b/hw/hpet_emul.h
new file mode 100644
index 0000000..8bf312a
--- /dev/null
+++ b/hw/hpet_emul.h
@@ -0,0 +1,71 @@
+/*
+ * QEMU Emulated HPET support
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Beth Kon <bkon@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef QEMU_HPET_EMUL_H
+#define QEMU_HPET_EMUL_H
+
+#define HPET_BASE 0xfed00000
+#define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/
+
+#define FS_PER_NS 1000000
+#define HPET_MIN_TIMERS 3
+#define HPET_MAX_TIMERS 32
+
+#define HPET_NUM_IRQ_ROUTES 32
+
+#define HPET_CFG_ENABLE 0x001
+#define HPET_CFG_LEGACY 0x002
+
+#define HPET_ID 0x000
+#define HPET_PERIOD 0x004
+#define HPET_CFG 0x010
+#define HPET_STATUS 0x020
+#define HPET_COUNTER 0x0f0
+#define HPET_TN_CFG 0x000
+#define HPET_TN_CMP 0x008
+#define HPET_TN_ROUTE 0x010
+#define HPET_CFG_WRITE_MASK 0x3
+
+#define HPET_ID_NUM_TIM_SHIFT 8
+#define HPET_ID_NUM_TIM_MASK 0x1f00
+
+#define HPET_TN_TYPE_LEVEL 0x002
+#define HPET_TN_ENABLE 0x004
+#define HPET_TN_PERIODIC 0x008
+#define HPET_TN_PERIODIC_CAP 0x010
+#define HPET_TN_SIZE_CAP 0x020
+#define HPET_TN_SETVAL 0x040
+#define HPET_TN_32BIT 0x100
+#define HPET_TN_INT_ROUTE_MASK 0x3e00
+#define HPET_TN_FSB_ENABLE 0x4000
+#define HPET_TN_FSB_CAP 0x8000
+#define HPET_TN_CFG_WRITE_MASK 0x7f4e
+#define HPET_TN_INT_ROUTE_SHIFT 9
+#define HPET_TN_INT_ROUTE_CAP_SHIFT 32
+#define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
+
+struct hpet_fw_entry
+{
+ uint32_t event_timer_block_id;
+ uint64_t address;
+ uint16_t min_tick;
+ uint8_t page_prot;
+} __attribute__ ((packed));
+
+struct hpet_fw_config
+{
+ uint8_t count;
+ struct hpet_fw_entry hpet[8];
+} __attribute__ ((packed));
+
+extern struct hpet_fw_config hpet_cfg;
+#endif
diff --git a/hw/hw.h b/hw/hw.h
new file mode 100644
index 0000000..6d3d106
--- /dev/null
+++ b/hw/hw.h
@@ -0,0 +1,837 @@
+/* Declarations for use by hardware emulation. */
+#ifndef QEMU_HW_H
+#define QEMU_HW_H
+
+#include "qemu-common.h"
+
+#if defined(TARGET_PHYS_ADDR_BITS) && !defined(NEED_CPU_H)
+#include "cpu-common.h"
+#endif
+
+#include "ioport.h"
+#include "irq.h"
+
+/* VM Load/Save */
+
+/* This function writes a chunk of data to a file at the given position.
+ * The pos argument can be ignored if the file is only being used for
+ * streaming. The handler should try to write all of the data it can.
+ */
+typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf,
+ int64_t pos, int size);
+
+/* Read a chunk of data from a file at the given position. The pos argument
+ * can be ignored if the file is only be used for streaming. The number of
+ * bytes actually read should be returned.
+ */
+typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf,
+ int64_t pos, int size);
+
+/* Close a file and return an error code */
+typedef int (QEMUFileCloseFunc)(void *opaque);
+
+/* Called to determine if the file has exceeded it's bandwidth allocation. The
+ * bandwidth capping is a soft limit, not a hard limit.
+ */
+typedef int (QEMUFileRateLimit)(void *opaque);
+
+/* Called to change the current bandwidth allocation. This function must return
+ * the new actual bandwidth. It should be new_rate if everything goes ok, and
+ * the old rate otherwise
+ */
+typedef int64_t (QEMUFileSetRateLimit)(void *opaque, int64_t new_rate);
+typedef int64_t (QEMUFileGetRateLimit)(void *opaque);
+
+QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer,
+ QEMUFileCloseFunc *close,
+ QEMUFileRateLimit *rate_limit,
+ QEMUFileSetRateLimit *set_rate_limit,
+ QEMUFileGetRateLimit *get_rate_limit);
+QEMUFile *qemu_fopen(const char *filename, const char *mode);
+QEMUFile *qemu_fdopen(int fd, const char *mode);
+QEMUFile *qemu_fopen_socket(int fd);
+QEMUFile *qemu_popen(FILE *popen_file, const char *mode);
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
+int qemu_stdio_fd(QEMUFile *f);
+void qemu_fflush(QEMUFile *f);
+int qemu_fclose(QEMUFile *f);
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
+void qemu_put_byte(QEMUFile *f, int v);
+
+static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, (int)v);
+}
+
+#define qemu_put_sbyte qemu_put_byte
+
+void qemu_put_be16(QEMUFile *f, unsigned int v);
+void qemu_put_be32(QEMUFile *f, unsigned int v);
+void qemu_put_be64(QEMUFile *f, uint64_t v);
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
+int qemu_get_byte(QEMUFile *f);
+
+static inline unsigned int qemu_get_ubyte(QEMUFile *f)
+{
+ return (unsigned int)qemu_get_byte(f);
+}
+
+#define qemu_get_sbyte qemu_get_byte
+
+unsigned int qemu_get_be16(QEMUFile *f);
+unsigned int qemu_get_be32(QEMUFile *f);
+uint64_t qemu_get_be64(QEMUFile *f);
+int qemu_file_rate_limit(QEMUFile *f);
+int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
+int64_t qemu_file_get_rate_limit(QEMUFile *f);
+int qemu_file_has_error(QEMUFile *f);
+void qemu_file_set_error(QEMUFile *f);
+
+/* Try to send any outstanding data. This function is useful when output is
+ * halted due to rate limiting or EAGAIN errors occur as it can be used to
+ * resume output. */
+void qemu_file_put_notify(QEMUFile *f);
+
+static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
+{
+ qemu_put_be64(f, *pv);
+}
+
+static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv)
+{
+ qemu_put_be32(f, *pv);
+}
+
+static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv)
+{
+ qemu_put_be16(f, *pv);
+}
+
+static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv)
+{
+ qemu_put_byte(f, *pv);
+}
+
+static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv)
+{
+ *pv = qemu_get_be64(f);
+}
+
+static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv)
+{
+ *pv = qemu_get_be32(f);
+}
+
+static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv)
+{
+ *pv = qemu_get_be16(f);
+}
+
+static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv)
+{
+ *pv = qemu_get_byte(f);
+}
+
+// Signed versions for type safety
+static inline void qemu_put_sbuffer(QEMUFile *f, const int8_t *buf, int size)
+{
+ qemu_put_buffer(f, (const uint8_t *)buf, size);
+}
+
+static inline void qemu_put_sbe16(QEMUFile *f, int v)
+{
+ qemu_put_be16(f, (unsigned int)v);
+}
+
+static inline void qemu_put_sbe32(QEMUFile *f, int v)
+{
+ qemu_put_be32(f, (unsigned int)v);
+}
+
+static inline void qemu_put_sbe64(QEMUFile *f, int64_t v)
+{
+ qemu_put_be64(f, (uint64_t)v);
+}
+
+static inline size_t qemu_get_sbuffer(QEMUFile *f, int8_t *buf, int size)
+{
+ return qemu_get_buffer(f, (uint8_t *)buf, size);
+}
+
+static inline int qemu_get_sbe16(QEMUFile *f)
+{
+ return (int)qemu_get_be16(f);
+}
+
+static inline int qemu_get_sbe32(QEMUFile *f)
+{
+ return (int)qemu_get_be32(f);
+}
+
+static inline int64_t qemu_get_sbe64(QEMUFile *f)
+{
+ return (int64_t)qemu_get_be64(f);
+}
+
+static inline void qemu_put_s8s(QEMUFile *f, const int8_t *pv)
+{
+ qemu_put_8s(f, (const uint8_t *)pv);
+}
+
+static inline void qemu_put_sbe16s(QEMUFile *f, const int16_t *pv)
+{
+ qemu_put_be16s(f, (const uint16_t *)pv);
+}
+
+static inline void qemu_put_sbe32s(QEMUFile *f, const int32_t *pv)
+{
+ qemu_put_be32s(f, (const uint32_t *)pv);
+}
+
+static inline void qemu_put_sbe64s(QEMUFile *f, const int64_t *pv)
+{
+ qemu_put_be64s(f, (const uint64_t *)pv);
+}
+
+static inline void qemu_get_s8s(QEMUFile *f, int8_t *pv)
+{
+ qemu_get_8s(f, (uint8_t *)pv);
+}
+
+static inline void qemu_get_sbe16s(QEMUFile *f, int16_t *pv)
+{
+ qemu_get_be16s(f, (uint16_t *)pv);
+}
+
+static inline void qemu_get_sbe32s(QEMUFile *f, int32_t *pv)
+{
+ qemu_get_be32s(f, (uint32_t *)pv);
+}
+
+static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv)
+{
+ qemu_get_be64s(f, (uint64_t *)pv);
+}
+
+#ifdef NEED_CPU_H
+#if TARGET_LONG_BITS == 64
+#define qemu_put_betl qemu_put_be64
+#define qemu_get_betl qemu_get_be64
+#define qemu_put_betls qemu_put_be64s
+#define qemu_get_betls qemu_get_be64s
+#define qemu_put_sbetl qemu_put_sbe64
+#define qemu_get_sbetl qemu_get_sbe64
+#define qemu_put_sbetls qemu_put_sbe64s
+#define qemu_get_sbetls qemu_get_sbe64s
+#else
+#define qemu_put_betl qemu_put_be32
+#define qemu_get_betl qemu_get_be32
+#define qemu_put_betls qemu_put_be32s
+#define qemu_get_betls qemu_get_be32s
+#define qemu_put_sbetl qemu_put_sbe32
+#define qemu_get_sbetl qemu_get_sbe32
+#define qemu_put_sbetls qemu_put_sbe32s
+#define qemu_get_sbetls qemu_get_sbe32s
+#endif
+#endif
+
+int64_t qemu_ftell(QEMUFile *f);
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
+
+typedef void SaveSetParamsHandler(int blk_enable, int shared, void * opaque);
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int SaveLiveStateHandler(Monitor *mon, QEMUFile *f, int stage,
+ void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+int register_savevm(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+
+int register_savevm_live(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveSetParamsHandler *set_params,
+ SaveLiveStateHandler *save_live_state,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque);
+
+void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque);
+void register_device_unmigratable(DeviceState *dev, const char *idstr,
+ void *opaque);
+
+typedef void QEMUResetHandler(void *opaque);
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque);
+void qemu_unregister_reset(QEMUResetHandler *func, void *opaque);
+
+/* handler to set the boot_device order for a specific type of QEMUMachine */
+/* return 0 if success */
+typedef int QEMUBootSetHandler(void *opaque, const char *boot_devices);
+void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque);
+int qemu_boot_set(const char *boot_devices);
+
+typedef struct VMStateInfo VMStateInfo;
+typedef struct VMStateDescription VMStateDescription;
+
+struct VMStateInfo {
+ const char *name;
+ int (*get)(QEMUFile *f, void *pv, size_t size);
+ void (*put)(QEMUFile *f, void *pv, size_t size);
+};
+
+enum VMStateFlags {
+ VMS_SINGLE = 0x001,
+ VMS_POINTER = 0x002,
+ VMS_ARRAY = 0x004,
+ VMS_STRUCT = 0x008,
+ VMS_VARRAY_INT32 = 0x010, /* Array with size in int32_t field*/
+ VMS_BUFFER = 0x020, /* static sized buffer */
+ VMS_ARRAY_OF_POINTER = 0x040,
+ VMS_VARRAY_UINT16 = 0x080, /* Array with size in uint16_t field */
+ VMS_VBUFFER = 0x100, /* Buffer with size in int32_t field */
+ VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */
+};
+
+typedef struct {
+ const char *name;
+ size_t offset;
+ size_t size;
+ size_t start;
+ int num;
+ size_t num_offset;
+ size_t size_offset;
+ const VMStateInfo *info;
+ enum VMStateFlags flags;
+ const VMStateDescription *vmsd;
+ int version_id;
+ bool (*field_exists)(void *opaque, int version_id);
+} VMStateField;
+
+typedef struct VMStateSubsection {
+ const VMStateDescription *vmsd;
+ bool (*needed)(void *opaque);
+} VMStateSubsection;
+
+struct VMStateDescription {
+ const char *name;
+ int version_id;
+ int minimum_version_id;
+ int minimum_version_id_old;
+ LoadStateHandler *load_state_old;
+ int (*pre_load)(void *opaque);
+ int (*post_load)(void *opaque, int version_id);
+ void (*pre_save)(void *opaque);
+ VMStateField *fields;
+ const VMStateSubsection *subsections;
+};
+
+extern const VMStateInfo vmstate_info_bool;
+
+extern const VMStateInfo vmstate_info_int8;
+extern const VMStateInfo vmstate_info_int16;
+extern const VMStateInfo vmstate_info_int32;
+extern const VMStateInfo vmstate_info_int64;
+
+extern const VMStateInfo vmstate_info_uint8_equal;
+extern const VMStateInfo vmstate_info_uint16_equal;
+extern const VMStateInfo vmstate_info_int32_equal;
+extern const VMStateInfo vmstate_info_int32_le;
+
+extern const VMStateInfo vmstate_info_uint8;
+extern const VMStateInfo vmstate_info_uint16;
+extern const VMStateInfo vmstate_info_uint32;
+extern const VMStateInfo vmstate_info_uint64;
+
+#ifdef __linux__
+extern const VMStateInfo vmstate_info_u64;
+#endif
+
+extern const VMStateInfo vmstate_info_timer;
+extern const VMStateInfo vmstate_info_ptimer;
+extern const VMStateInfo vmstate_info_buffer;
+extern const VMStateInfo vmstate_info_unused_buffer;
+
+#define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
+#define type_check_pointer(t1,t2) ((t1**)0 - (t2*)0)
+
+#define vmstate_offset_value(_state, _field, _type) \
+ (offsetof(_state, _field) + \
+ type_check(_type, typeof_field(_state, _field)))
+
+#define vmstate_offset_pointer(_state, _field, _type) \
+ (offsetof(_state, _field) + \
+ type_check_pointer(_type, typeof_field(_state, _field)))
+
+#define vmstate_offset_array(_state, _field, _type, _num) \
+ (offsetof(_state, _field) + \
+ type_check_array(_type, typeof_field(_state, _field), _num))
+
+#define vmstate_offset_sub_array(_state, _field, _type, _start) \
+ (offsetof(_state, _field[_start]))
+
+#define vmstate_offset_buffer(_state, _field) \
+ vmstate_offset_array(_state, _field, uint8_t, \
+ sizeof(typeof_field(_state, _field)))
+
+#define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size = sizeof(_type), \
+ .info = &(_info), \
+ .flags = VMS_SINGLE, \
+ .offset = vmstate_offset_value(_state, _field, _type), \
+}
+
+#define VMSTATE_POINTER(_field, _state, _version, _info, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_SINGLE|VMS_POINTER, \
+ .offset = vmstate_offset_value(_state, _field, _type), \
+}
+
+#define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) {\
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num = (_num), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_ARRAY, \
+ .offset = vmstate_offset_array(_state, _field, _type, _num), \
+}
+
+#define VMSTATE_ARRAY_TEST(_field, _state, _num, _test, _info, _type) {\
+ .name = (stringify(_field)), \
+ .field_exists = (_test), \
+ .num = (_num), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_ARRAY, \
+ .offset = vmstate_offset_array(_state, _field, _type, _num),\
+}
+
+#define VMSTATE_SUB_ARRAY(_field, _state, _start, _num, _version, _info, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num = (_num), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_ARRAY, \
+ .offset = vmstate_offset_sub_array(_state, _field, _type, _start), \
+}
+
+#define VMSTATE_VARRAY_INT32(_field, _state, _field_num, _version, _info, _type) {\
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num_offset = vmstate_offset_value(_state, _field_num, int32_t), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_VARRAY_INT32|VMS_POINTER, \
+ .offset = vmstate_offset_pointer(_state, _field, _type), \
+}
+
+#define VMSTATE_VARRAY_UINT16_UNSAFE(_field, _state, _field_num, _version, _info, _type) {\
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num_offset = vmstate_offset_value(_state, _field_num, uint16_t),\
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_VARRAY_UINT16, \
+ .offset = offsetof(_state, _field), \
+}
+
+#define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type), \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, _type), \
+}
+
+#define VMSTATE_STRUCT_POINTER_TEST(_field, _state, _test, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .field_exists = (_test), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type), \
+ .flags = VMS_STRUCT|VMS_POINTER, \
+ .offset = vmstate_offset_value(_state, _field, _type), \
+}
+
+#define VMSTATE_ARRAY_OF_POINTER(_field, _state, _num, _version, _info, _type) {\
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num = (_num), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_ARRAY|VMS_ARRAY_OF_POINTER, \
+ .offset = vmstate_offset_array(_state, _field, _type, _num), \
+}
+
+#define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .num = (_num), \
+ .version_id = (_version), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type), \
+ .flags = VMS_STRUCT|VMS_ARRAY, \
+ .offset = vmstate_offset_array(_state, _field, _type, _num), \
+}
+
+#define VMSTATE_STRUCT_VARRAY_UINT8(_field, _state, _field_num, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \
+ .version_id = (_version), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type), \
+ .flags = VMS_STRUCT|VMS_VARRAY_INT32, \
+ .offset = offsetof(_state, _field), \
+}
+
+#define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size = (_size - _start), \
+ .info = &vmstate_info_buffer, \
+ .flags = VMS_BUFFER, \
+ .offset = vmstate_offset_buffer(_state, _field) + _start, \
+}
+
+#define VMSTATE_BUFFER_MULTIPLY(_field, _state, _version, _test, _start, _field_size, _multiply) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
+ .size = (_multiply), \
+ .info = &vmstate_info_buffer, \
+ .flags = VMS_VBUFFER|VMS_MULTIPLY, \
+ .offset = offsetof(_state, _field), \
+ .start = (_start), \
+}
+
+#define VMSTATE_VBUFFER(_field, _state, _version, _test, _start, _field_size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size_offset = vmstate_offset_value(_state, _field_size, int32_t),\
+ .info = &vmstate_info_buffer, \
+ .flags = VMS_VBUFFER|VMS_POINTER, \
+ .offset = offsetof(_state, _field), \
+ .start = (_start), \
+}
+
+#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
+ .info = &vmstate_info_buffer, \
+ .flags = VMS_VBUFFER|VMS_POINTER, \
+ .offset = offsetof(_state, _field), \
+ .start = (_start), \
+}
+
+#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .size = (_size), \
+ .info = &(_info), \
+ .flags = VMS_BUFFER, \
+ .offset = offsetof(_state, _field), \
+}
+
+#define VMSTATE_UNUSED_BUFFER(_test, _version, _size) { \
+ .name = "unused", \
+ .field_exists = (_test), \
+ .version_id = (_version), \
+ .size = (_size), \
+ .info = &vmstate_info_unused_buffer, \
+ .flags = VMS_BUFFER, \
+}
+extern const VMStateDescription vmstate_pci_device;
+
+#define VMSTATE_PCI_DEVICE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(PCIDevice), \
+ .vmsd = &vmstate_pci_device, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, PCIDevice), \
+}
+
+extern const VMStateDescription vmstate_pcie_device;
+
+#define VMSTATE_PCIE_DEVICE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .version_id = 2, \
+ .size = sizeof(PCIDevice), \
+ .vmsd = &vmstate_pcie_device, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, PCIDevice), \
+}
+
+extern const VMStateDescription vmstate_i2c_slave;
+
+#define VMSTATE_I2C_SLAVE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(i2c_slave), \
+ .vmsd = &vmstate_i2c_slave, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, i2c_slave), \
+}
+
+extern const VMStateDescription vmstate_usb_device;
+
+#define VMSTATE_USB_DEVICE(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(USBDevice), \
+ .vmsd = &vmstate_usb_device, \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, USBDevice), \
+}
+
+#define vmstate_offset_macaddr(_state, _field) \
+ vmstate_offset_array(_state, _field.a, uint8_t, \
+ sizeof(typeof_field(_state, _field)))
+
+#define VMSTATE_MACADDR(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(MACAddr), \
+ .info = &vmstate_info_buffer, \
+ .flags = VMS_BUFFER, \
+ .offset = vmstate_offset_macaddr(_state, _field), \
+}
+
+/* _f : field name
+ _f_n : num of elements field_name
+ _n : num of elements
+ _s : struct state name
+ _v : version
+*/
+
+#define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \
+ VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type)
+
+#define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \
+ VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type)
+
+#define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \
+ VMSTATE_STRUCT_POINTER_TEST(_field, _state, NULL, _vmsd, _type)
+
+#define VMSTATE_BOOL_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_bool, bool)
+
+#define VMSTATE_INT8_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int8, int8_t)
+#define VMSTATE_INT16_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int16, int16_t)
+#define VMSTATE_INT32_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int32, int32_t)
+#define VMSTATE_INT64_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int64, int64_t)
+
+#define VMSTATE_UINT8_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint8, uint8_t)
+#define VMSTATE_UINT16_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint16, uint16_t)
+#define VMSTATE_UINT32_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint32, uint32_t)
+#define VMSTATE_UINT64_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint64, uint64_t)
+
+#define VMSTATE_BOOL(_f, _s) \
+ VMSTATE_BOOL_V(_f, _s, 0)
+
+#define VMSTATE_INT8(_f, _s) \
+ VMSTATE_INT8_V(_f, _s, 0)
+#define VMSTATE_INT16(_f, _s) \
+ VMSTATE_INT16_V(_f, _s, 0)
+#define VMSTATE_INT32(_f, _s) \
+ VMSTATE_INT32_V(_f, _s, 0)
+#define VMSTATE_INT64(_f, _s) \
+ VMSTATE_INT64_V(_f, _s, 0)
+
+#define VMSTATE_UINT8(_f, _s) \
+ VMSTATE_UINT8_V(_f, _s, 0)
+#define VMSTATE_UINT16(_f, _s) \
+ VMSTATE_UINT16_V(_f, _s, 0)
+#define VMSTATE_UINT32(_f, _s) \
+ VMSTATE_UINT32_V(_f, _s, 0)
+#define VMSTATE_UINT64(_f, _s) \
+ VMSTATE_UINT64_V(_f, _s, 0)
+
+/* This is needed because on linux __u64 is unsigned long long
+ and on glibc uint64_t is unsigned long on 64 bits */
+#ifdef __linux__
+#define VMSTATE_U64_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_u64, __u64)
+#define VMSTATE_U64(_f, _s) \
+ VMSTATE_U64_V(_f, _s, 0)
+#endif
+
+#define VMSTATE_UINT8_EQUAL(_f, _s) \
+ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_uint8_equal, uint8_t)
+
+#define VMSTATE_UINT16_EQUAL(_f, _s) \
+ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_uint16_equal, uint16_t)
+
+#define VMSTATE_UINT16_EQUAL_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint16_equal, uint16_t)
+
+#define VMSTATE_INT32_EQUAL(_f, _s) \
+ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_equal, int32_t)
+
+#define VMSTATE_INT32_LE(_f, _s) \
+ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t)
+
+#define VMSTATE_UINT16_TEST(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint16, uint16_t)
+
+#define VMSTATE_UINT32_TEST(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint32, uint32_t)
+
+#define VMSTATE_TIMER_V(_f, _s, _v) \
+ VMSTATE_POINTER(_f, _s, _v, vmstate_info_timer, QEMUTimer *)
+
+#define VMSTATE_TIMER(_f, _s) \
+ VMSTATE_TIMER_V(_f, _s, 0)
+
+#define VMSTATE_TIMER_ARRAY(_f, _s, _n) \
+ VMSTATE_ARRAY_OF_POINTER(_f, _s, _n, 0, vmstate_info_timer, QEMUTimer *)
+
+#define VMSTATE_PTIMER_V(_f, _s, _v) \
+ VMSTATE_POINTER(_f, _s, _v, vmstate_info_ptimer, ptimer_state *)
+
+#define VMSTATE_PTIMER(_f, _s) \
+ VMSTATE_PTIMER_V(_f, _s, 0)
+
+#define VMSTATE_BOOL_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_bool, bool)
+
+#define VMSTATE_BOOL_ARRAY(_f, _s, _n) \
+ VMSTATE_BOOL_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint16, uint16_t)
+
+#define VMSTATE_UINT16_ARRAY(_f, _s, _n) \
+ VMSTATE_UINT16_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_UINT8_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint8, uint8_t)
+
+#define VMSTATE_UINT8_ARRAY(_f, _s, _n) \
+ VMSTATE_UINT8_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
+
+#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \
+ VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint64, uint64_t)
+
+#define VMSTATE_UINT64_ARRAY(_f, _s, _n) \
+ VMSTATE_UINT64_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_INT16_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_int16, int16_t)
+
+#define VMSTATE_INT16_ARRAY(_f, _s, _n) \
+ VMSTATE_INT16_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_INT32_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_int32, int32_t)
+
+#define VMSTATE_INT32_ARRAY(_f, _s, _n) \
+ VMSTATE_INT32_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_UINT32_SUB_ARRAY(_f, _s, _start, _num) \
+ VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_uint32, uint32_t)
+
+#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \
+ VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_BUFFER_V(_f, _s, _v) \
+ VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f)))
+
+#define VMSTATE_BUFFER(_f, _s) \
+ VMSTATE_BUFFER_V(_f, _s, 0)
+
+#define VMSTATE_PARTIAL_BUFFER(_f, _s, _size) \
+ VMSTATE_STATIC_BUFFER(_f, _s, 0, NULL, 0, _size)
+
+#define VMSTATE_BUFFER_START_MIDDLE(_f, _s, _start) \
+ VMSTATE_STATIC_BUFFER(_f, _s, 0, NULL, _start, sizeof(typeof_field(_s, _f)))
+
+#define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
+ VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
+
+#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \
+ VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size)
+
+#define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \
+ VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size)
+
+#define VMSTATE_BUFFER_TEST(_f, _s, _test) \
+ VMSTATE_STATIC_BUFFER(_f, _s, 0, _test, 0, sizeof(typeof_field(_s, _f)))
+
+#define VMSTATE_BUFFER_UNSAFE(_field, _state, _version, _size) \
+ VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, vmstate_info_buffer, _size)
+
+#define VMSTATE_UNUSED_V(_v, _size) \
+ VMSTATE_UNUSED_BUFFER(NULL, _v, _size)
+
+#define VMSTATE_UNUSED(_size) \
+ VMSTATE_UNUSED_V(0, _size)
+
+#define VMSTATE_UNUSED_TEST(_test, _size) \
+ VMSTATE_UNUSED_BUFFER(_test, 0, _size)
+
+#ifdef NEED_CPU_H
+#if TARGET_LONG_BITS == 64
+#define VMSTATE_UINTTL_V(_f, _s, _v) \
+ VMSTATE_UINT64_V(_f, _s, _v)
+#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v)
+#else
+#define VMSTATE_UINTTL_V(_f, _s, _v) \
+ VMSTATE_UINT32_V(_f, _s, _v)
+#define VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v)
+#endif
+#define VMSTATE_UINTTL(_f, _s) \
+ VMSTATE_UINTTL_V(_f, _s, 0)
+#define VMSTATE_UINTTL_ARRAY(_f, _s, _n) \
+ VMSTATE_UINTTL_ARRAY_V(_f, _s, _n, 0)
+
+#endif
+
+#define VMSTATE_END_OF_LIST() \
+ {}
+
+int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque, int version_id);
+void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+int vmstate_register(DeviceState *dev, int instance_id,
+ const VMStateDescription *vmsd, void *base);
+int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
+ const VMStateDescription *vmsd,
+ void *base, int alias_id,
+ int required_for_version);
+void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
+ void *opaque);
+#endif
diff --git a/hw/i2c.c b/hw/i2c.c
new file mode 100644
index 0000000..f80d12d
--- /dev/null
+++ b/hw/i2c.c
@@ -0,0 +1,196 @@
+/*
+ * QEMU I2C bus interface.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "i2c.h"
+
+struct i2c_bus
+{
+ BusState qbus;
+ i2c_slave *current_dev;
+ i2c_slave *dev;
+ uint8_t saved_address;
+};
+
+static struct BusInfo i2c_bus_info = {
+ .name = "I2C",
+ .size = sizeof(i2c_bus),
+ .props = (Property[]) {
+ DEFINE_PROP_UINT8("address", struct i2c_slave, address, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void i2c_bus_pre_save(void *opaque)
+{
+ i2c_bus *bus = opaque;
+
+ bus->saved_address = bus->current_dev ? bus->current_dev->address : -1;
+}
+
+static int i2c_bus_post_load(void *opaque, int version_id)
+{
+ i2c_bus *bus = opaque;
+
+ /* The bus is loaded before attached devices, so load and save the
+ current device id. Devices will check themselves as loaded. */
+ bus->current_dev = NULL;
+ return 0;
+}
+
+static const VMStateDescription vmstate_i2c_bus = {
+ .name = "i2c_bus",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = i2c_bus_pre_save,
+ .post_load = i2c_bus_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(saved_address, i2c_bus),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* Create a new I2C bus. */
+i2c_bus *i2c_init_bus(DeviceState *parent, const char *name)
+{
+ i2c_bus *bus;
+
+ bus = FROM_QBUS(i2c_bus, qbus_create(&i2c_bus_info, parent, name));
+ vmstate_register(NULL, -1, &vmstate_i2c_bus, bus);
+ return bus;
+}
+
+void i2c_set_slave_address(i2c_slave *dev, uint8_t address)
+{
+ dev->address = address;
+}
+
+/* Return nonzero if bus is busy. */
+int i2c_bus_busy(i2c_bus *bus)
+{
+ return bus->current_dev != NULL;
+}
+
+/* Returns non-zero if the address is not valid. */
+/* TODO: Make this handle multiple masters. */
+int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
+{
+ DeviceState *qdev;
+ i2c_slave *slave = NULL;
+
+ QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
+ i2c_slave *candidate = I2C_SLAVE_FROM_QDEV(qdev);
+ if (candidate->address == address) {
+ slave = candidate;
+ break;
+ }
+ }
+
+ if (!slave)
+ return 1;
+
+ /* If the bus is already busy, assume this is a repeated
+ start condition. */
+ bus->current_dev = slave;
+ slave->info->event(slave, recv ? I2C_START_RECV : I2C_START_SEND);
+ return 0;
+}
+
+void i2c_end_transfer(i2c_bus *bus)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return;
+
+ dev->info->event(dev, I2C_FINISH);
+
+ bus->current_dev = NULL;
+}
+
+int i2c_send(i2c_bus *bus, uint8_t data)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return -1;
+
+ return dev->info->send(dev, data);
+}
+
+int i2c_recv(i2c_bus *bus)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return -1;
+
+ return dev->info->recv(dev);
+}
+
+void i2c_nack(i2c_bus *bus)
+{
+ i2c_slave *dev = bus->current_dev;
+
+ if (!dev)
+ return;
+
+ dev->info->event(dev, I2C_NACK);
+}
+
+static int i2c_slave_post_load(void *opaque, int version_id)
+{
+ i2c_slave *dev = opaque;
+ i2c_bus *bus;
+ bus = FROM_QBUS(i2c_bus, qdev_get_parent_bus(&dev->qdev));
+ if (bus->saved_address == dev->address) {
+ bus->current_dev = dev;
+ }
+ return 0;
+}
+
+const VMStateDescription vmstate_i2c_slave = {
+ .name = "i2c_slave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = i2c_slave_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(address, i2c_slave),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int i2c_slave_qdev_init(DeviceState *dev, DeviceInfo *base)
+{
+ I2CSlaveInfo *info = container_of(base, I2CSlaveInfo, qdev);
+ i2c_slave *s = I2C_SLAVE_FROM_QDEV(dev);
+
+ s->info = info;
+
+ return info->init(s);
+}
+
+void i2c_register_slave(I2CSlaveInfo *info)
+{
+ assert(info->qdev.size >= sizeof(i2c_slave));
+ info->qdev.init = i2c_slave_qdev_init;
+ info->qdev.bus_info = &i2c_bus_info;
+ qdev_register(&info->qdev);
+}
+
+DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(&bus->qbus, name);
+ qdev_prop_set_uint8(dev, "address", addr);
+ qdev_init_nofail(dev);
+ return dev;
+}
diff --git a/hw/i2c.h b/hw/i2c.h
new file mode 100644
index 0000000..83fd917
--- /dev/null
+++ b/hw/i2c.h
@@ -0,0 +1,82 @@
+#ifndef QEMU_I2C_H
+#define QEMU_I2C_H
+
+#include "qdev.h"
+
+/* The QEMU I2C implementation only supports simple transfers that complete
+ immediately. It does not support slave devices that need to be able to
+ defer their response (eg. CPU slave interfaces where the data is supplied
+ by the device driver in response to an interrupt). */
+
+enum i2c_event {
+ I2C_START_RECV,
+ I2C_START_SEND,
+ I2C_FINISH,
+ I2C_NACK /* Masker NACKed a receive byte. */
+};
+
+/* Master to slave. */
+typedef int (*i2c_send_cb)(i2c_slave *s, uint8_t data);
+/* Slave to master. */
+typedef int (*i2c_recv_cb)(i2c_slave *s);
+/* Notify the slave of a bus state change. */
+typedef void (*i2c_event_cb)(i2c_slave *s, enum i2c_event event);
+
+typedef int (*i2c_slave_initfn)(i2c_slave *dev);
+
+typedef struct {
+ DeviceInfo qdev;
+
+ /* Callbacks provided by the device. */
+ i2c_slave_initfn init;
+ i2c_event_cb event;
+ i2c_recv_cb recv;
+ i2c_send_cb send;
+} I2CSlaveInfo;
+
+struct i2c_slave
+{
+ DeviceState qdev;
+ I2CSlaveInfo *info;
+
+ /* Remaining fields for internal use by the I2C code. */
+ uint8_t address;
+};
+
+i2c_bus *i2c_init_bus(DeviceState *parent, const char *name);
+void i2c_set_slave_address(i2c_slave *dev, uint8_t address);
+int i2c_bus_busy(i2c_bus *bus);
+int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv);
+void i2c_end_transfer(i2c_bus *bus);
+void i2c_nack(i2c_bus *bus);
+int i2c_send(i2c_bus *bus, uint8_t data);
+int i2c_recv(i2c_bus *bus);
+
+#define I2C_SLAVE_FROM_QDEV(dev) DO_UPCAST(i2c_slave, qdev, dev)
+#define FROM_I2C_SLAVE(type, dev) DO_UPCAST(type, i2c, dev)
+
+void i2c_register_slave(I2CSlaveInfo *type);
+
+DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr);
+
+/* max7310.c */
+void max7310_reset(i2c_slave *i2c);
+qemu_irq *max7310_gpio_in_get(i2c_slave *i2c);
+void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler);
+
+/* wm8750.c */
+void wm8750_data_req_set(DeviceState *dev,
+ void (*data_req)(void *, int, int), void *opaque);
+void wm8750_dac_dat(void *opaque, uint32_t sample);
+uint32_t wm8750_adc_dat(void *opaque);
+void *wm8750_dac_buffer(void *opaque, int samples);
+void wm8750_dac_commit(void *opaque);
+void wm8750_set_bclk_in(void *opaque, int new_hz);
+
+/* tmp105.c */
+void tmp105_set(i2c_slave *i2c, int temp);
+
+/* lm832x.c */
+void lm832x_key_event(i2c_slave *i2c, int key, int state);
+
+#endif
diff --git a/hw/i8254-kvm.c b/hw/i8254-kvm.c
new file mode 100644
index 0000000..6125213
--- /dev/null
+++ b/hw/i8254-kvm.c
@@ -0,0 +1,122 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qemu-timer.h"
+#include "i8254.h"
+#include "qemu-kvm.h"
+
+extern VMStateDescription vmstate_pit;
+
+static PITState pit_state;
+
+static void kvm_pit_pre_save(void *opaque)
+{
+ PITState *s = (void *)opaque;
+ struct kvm_pit_state2 pit2;
+ struct kvm_pit_channel_state *c;
+ struct PITChannelState *sc;
+ int i;
+
+ if(qemu_kvm_has_pit_state2()) {
+ kvm_get_pit2(kvm_context, &pit2);
+ s->flags = pit2.flags;
+ } else {
+ /* pit2 is superset of pit struct so just cast it and use it */
+ kvm_get_pit(kvm_context, (struct kvm_pit_state *)&pit2);
+ }
+ for (i = 0; i < 3; i++) {
+ c = &pit2.channels[i];
+ sc = &s->channels[i];
+ sc->count = c->count;
+ sc->latched_count = c->latched_count;
+ sc->count_latched = c->count_latched;
+ sc->status_latched = c->status_latched;
+ sc->status = c->status;
+ sc->read_state = c->read_state;
+ sc->write_state = c->write_state;
+ sc->write_latch = c->write_latch;
+ sc->rw_mode = c->rw_mode;
+ sc->mode = c->mode;
+ sc->bcd = c->bcd;
+ sc->gate = c->gate;
+ sc->count_load_time = c->count_load_time;
+ }
+}
+
+static int kvm_pit_post_load(void *opaque, int version_id)
+{
+ PITState *s = opaque;
+ struct kvm_pit_state2 pit2;
+ struct kvm_pit_channel_state *c;
+ struct PITChannelState *sc;
+ int i;
+
+ pit2.flags = s->flags;
+ for (i = 0; i < 3; i++) {
+ c = &pit2.channels[i];
+ sc = &s->channels[i];
+ c->count = sc->count;
+ c->latched_count = sc->latched_count;
+ c->count_latched = sc->count_latched;
+ c->status_latched = sc->status_latched;
+ c->status = sc->status;
+ c->read_state = sc->read_state;
+ c->write_state = sc->write_state;
+ c->write_latch = sc->write_latch;
+ c->rw_mode = sc->rw_mode;
+ c->mode = sc->mode;
+ c->bcd = sc->bcd;
+ c->gate = sc->gate;
+ c->count_load_time = sc->count_load_time;
+ }
+
+ if(qemu_kvm_has_pit_state2()) {
+ kvm_set_pit2(kvm_context, &pit2);
+ } else {
+ kvm_set_pit(kvm_context, (struct kvm_pit_state *)&pit2);
+ }
+ return 0;
+}
+
+static void dummy_timer(void *opaque)
+{
+}
+
+PITState *kvm_pit_init(int base, qemu_irq irq)
+{
+ PITState *pit = &pit_state;
+ PITChannelState *s;
+
+ s = &pit->channels[0];
+ s->irq_timer = qemu_new_timer(vm_clock, dummy_timer, s);
+ vmstate_pit.pre_save = kvm_pit_pre_save;
+ vmstate_pit.post_load = kvm_pit_post_load;
+ vmstate_register(NULL, base, &vmstate_pit, pit);
+ qemu_register_reset(pit_reset, pit);
+ pit_reset(pit);
+
+ return pit;
+}
diff --git a/hw/i8254.c b/hw/i8254.c
new file mode 100644
index 0000000..321a5d5
--- /dev/null
+++ b/hw/i8254.c
@@ -0,0 +1,548 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qemu-timer.h"
+#include "kvm.h"
+#include "i8254.h"
+
+//#define DEBUG_PIT
+
+static PITState pit_state;
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
+
+static int pit_get_count(PITChannelState *s)
+{
+ uint64_t d;
+ int counter;
+
+ d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ,
+ get_ticks_per_sec());
+ switch(s->mode) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ counter = (s->count - d) & 0xffff;
+ break;
+ case 3:
+ /* XXX: may be incorrect for odd counts */
+ counter = s->count - ((2 * d) % s->count);
+ break;
+ default:
+ counter = s->count - (d % s->count);
+ break;
+ }
+ return counter;
+}
+
+/* get pit output bit */
+static int pit_get_out1(PITChannelState *s, int64_t current_time)
+{
+ uint64_t d;
+ int out;
+
+ d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
+ get_ticks_per_sec());
+ switch(s->mode) {
+ default:
+ case 0:
+ out = (d >= s->count);
+ break;
+ case 1:
+ out = (d < s->count);
+ break;
+ case 2:
+ if ((d % s->count) == 0 && d != 0)
+ out = 1;
+ else
+ out = 0;
+ break;
+ case 3:
+ out = (d % s->count) < ((s->count + 1) >> 1);
+ break;
+ case 4:
+ case 5:
+ out = (d == s->count);
+ break;
+ }
+ return out;
+}
+
+int pit_get_out(PITState *pit, int channel, int64_t current_time)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return pit_get_out1(s, current_time);
+}
+
+/* return -1 if no transition will occur. */
+static int64_t pit_get_next_transition_time(PITChannelState *s,
+ int64_t current_time)
+{
+ uint64_t d, next_time, base;
+ int period2;
+
+ d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
+ get_ticks_per_sec());
+ switch(s->mode) {
+ default:
+ case 0:
+ case 1:
+ if (d < s->count)
+ next_time = s->count;
+ else
+ return -1;
+ break;
+ case 2:
+ base = (d / s->count) * s->count;
+ if ((d - base) == 0 && d != 0)
+ next_time = base + s->count;
+ else
+ next_time = base + s->count + 1;
+ break;
+ case 3:
+ base = (d / s->count) * s->count;
+ period2 = ((s->count + 1) >> 1);
+ if ((d - base) < period2)
+ next_time = base + period2;
+ else
+ next_time = base + s->count;
+ break;
+ case 4:
+ case 5:
+ if (d < s->count)
+ next_time = s->count;
+ else if (d == s->count)
+ next_time = s->count + 1;
+ else
+ return -1;
+ break;
+ }
+ /* convert to timer units */
+ next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
+ PIT_FREQ);
+ /* fix potential rounding problems */
+ /* XXX: better solution: use a clock at PIT_FREQ Hz */
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+/* val must be 0 or 1 */
+void pit_set_gate(PITState *pit, int channel, int val)
+{
+ PITChannelState *s = &pit->channels[channel];
+
+ switch(s->mode) {
+ default:
+ case 0:
+ case 4:
+ /* XXX: just disable/enable counting */
+ break;
+ case 1:
+ case 5:
+ if (s->gate < val) {
+ /* restart counting on rising edge */
+ s->count_load_time = qemu_get_clock(vm_clock);
+ pit_irq_timer_update(s, s->count_load_time);
+ }
+ break;
+ case 2:
+ case 3:
+ if (s->gate < val) {
+ /* restart counting on rising edge */
+ s->count_load_time = qemu_get_clock(vm_clock);
+ pit_irq_timer_update(s, s->count_load_time);
+ }
+ /* XXX: disable/enable counting */
+ break;
+ }
+ s->gate = val;
+}
+
+int pit_get_gate(PITState *pit, int channel)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return s->gate;
+}
+
+int pit_get_initial_count(PITState *pit, int channel)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return s->count;
+}
+
+int pit_get_mode(PITState *pit, int channel)
+{
+ PITChannelState *s = &pit->channels[channel];
+ return s->mode;
+}
+
+static inline void pit_load_count(PITState *s, int val, int chan)
+{
+ if (val == 0)
+ val = 0x10000;
+ s->channels[chan].count_load_time = qemu_get_clock(vm_clock);
+ s->channels[chan].count = val;
+#ifdef TARGET_I386
+ if (chan == 0 && pit_state.flags & PIT_FLAGS_HPET_LEGACY) {
+ return;
+ }
+#endif
+ pit_irq_timer_update(&s->channels[chan], s->channels[chan].count_load_time);
+}
+
+/* if already latched, do not latch again */
+static void pit_latch_count(PITChannelState *s)
+{
+ if (!s->count_latched) {
+ s->latched_count = pit_get_count(s);
+ s->count_latched = s->rw_mode;
+ }
+}
+
+static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PITState *pit = opaque;
+ int channel, access;
+ PITChannelState *s;
+
+ addr &= 3;
+ if (addr == 3) {
+ channel = val >> 6;
+ if (channel == 3) {
+ /* read back command */
+ for(channel = 0; channel < 3; channel++) {
+ s = &pit->channels[channel];
+ if (val & (2 << channel)) {
+ if (!(val & 0x20)) {
+ pit_latch_count(s);
+ }
+ if (!(val & 0x10) && !s->status_latched) {
+ /* status latch */
+ /* XXX: add BCD and null count */
+ s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
+ (s->rw_mode << 4) |
+ (s->mode << 1) |
+ s->bcd;
+ s->status_latched = 1;
+ }
+ }
+ }
+ } else {
+ s = &pit->channels[channel];
+ access = (val >> 4) & 3;
+ if (access == 0) {
+ pit_latch_count(s);
+ } else {
+ s->rw_mode = access;
+ s->read_state = access;
+ s->write_state = access;
+
+ s->mode = (val >> 1) & 7;
+ s->bcd = val & 1;
+ /* XXX: update irq timer ? */
+ }
+ }
+ } else {
+ s = &pit->channels[addr];
+ switch(s->write_state) {
+ default:
+ case RW_STATE_LSB:
+ pit_load_count(pit, val, addr);
+ break;
+ case RW_STATE_MSB:
+ pit_load_count(pit, val << 8, addr);
+ break;
+ case RW_STATE_WORD0:
+ s->write_latch = val;
+ s->write_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ pit_load_count(pit, s->write_latch | (val << 8), addr);
+ s->write_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+}
+
+static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
+{
+ PITState *pit = opaque;
+ int ret, count;
+ PITChannelState *s;
+
+ addr &= 3;
+ s = &pit->channels[addr];
+ if (s->status_latched) {
+ s->status_latched = 0;
+ ret = s->status;
+ } else if (s->count_latched) {
+ switch(s->count_latched) {
+ default:
+ case RW_STATE_LSB:
+ ret = s->latched_count & 0xff;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_MSB:
+ ret = s->latched_count >> 8;
+ s->count_latched = 0;
+ break;
+ case RW_STATE_WORD0:
+ ret = s->latched_count & 0xff;
+ s->count_latched = RW_STATE_MSB;
+ break;
+ }
+ } else {
+ switch(s->read_state) {
+ default:
+ case RW_STATE_LSB:
+ count = pit_get_count(s);
+ ret = count & 0xff;
+ break;
+ case RW_STATE_MSB:
+ count = pit_get_count(s);
+ ret = (count >> 8) & 0xff;
+ break;
+ case RW_STATE_WORD0:
+ count = pit_get_count(s);
+ ret = count & 0xff;
+ s->read_state = RW_STATE_WORD1;
+ break;
+ case RW_STATE_WORD1:
+ count = pit_get_count(s);
+ ret = (count >> 8) & 0xff;
+ s->read_state = RW_STATE_WORD0;
+ break;
+ }
+ }
+ return ret;
+}
+
+/* global counters for time-drift fix */
+int64_t timer_acks=0, timer_interrupts=0, timer_ints_to_push=0;
+
+extern int time_drift_fix;
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
+{
+ int64_t expire_time;
+ int irq_level;
+
+ if (!s->irq_timer)
+ return;
+ expire_time = pit_get_next_transition_time(s, current_time);
+ irq_level = pit_get_out1(s, current_time);
+ qemu_set_irq(s->irq, irq_level);
+ if (time_drift_fix && irq_level==1) {
+ /* FIXME: fine tune timer_max_fix (max fix per tick).
+ * Should it be 1 (double time), 2 , 4, 10 ?
+ * Currently setting it to 5% of PIT-ticks-per-second (per PIT-tick)
+ */
+ const long pit_ticks_per_sec = (s->count>0) ? (PIT_FREQ/s->count) : 0;
+ const long timer_max_fix = pit_ticks_per_sec/20;
+ const long delta = timer_interrupts - timer_acks;
+ const long max_delta = pit_ticks_per_sec * 60; /* one minute */
+ if ((delta > max_delta) && (pit_ticks_per_sec > 0)) {
+ printf("time drift is too long, %ld seconds were lost\n", delta/pit_ticks_per_sec);
+ timer_acks = timer_interrupts;
+ timer_ints_to_push = 0;
+ } else if (delta > 0) {
+ timer_ints_to_push = MIN(delta, timer_max_fix);
+ }
+ timer_interrupts++;
+ }
+#ifdef DEBUG_PIT
+ printf("irq_level=%d next_delay=%f\n",
+ irq_level,
+ (double)(expire_time - current_time) / get_ticks_per_sec());
+#endif
+ s->next_transition_time = expire_time;
+ if (expire_time != -1) {
+ qemu_mod_timer(s->irq_timer, expire_time);
+ } else {
+ qemu_del_timer(s->irq_timer);
+ }
+}
+
+static void pit_irq_timer(void *opaque)
+{
+ PITChannelState *s = opaque;
+
+ pit_irq_timer_update(s, s->next_transition_time);
+}
+
+static const VMStateDescription vmstate_pit_channel = {
+ .name = "pit channel",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(count, PITChannelState),
+ VMSTATE_UINT16(latched_count, PITChannelState),
+ VMSTATE_UINT8(count_latched, PITChannelState),
+ VMSTATE_UINT8(status_latched, PITChannelState),
+ VMSTATE_UINT8(status, PITChannelState),
+ VMSTATE_UINT8(read_state, PITChannelState),
+ VMSTATE_UINT8(write_state, PITChannelState),
+ VMSTATE_UINT8(write_latch, PITChannelState),
+ VMSTATE_UINT8(rw_mode, PITChannelState),
+ VMSTATE_UINT8(mode, PITChannelState),
+ VMSTATE_UINT8(bcd, PITChannelState),
+ VMSTATE_UINT8(gate, PITChannelState),
+ VMSTATE_INT64(count_load_time, PITChannelState),
+ VMSTATE_INT64(next_transition_time, PITChannelState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
+{
+ PITState *pit = opaque;
+ PITChannelState *s;
+ int i;
+
+ if (version_id != PIT_SAVEVM_VERSION)
+ return -EINVAL;
+
+ pit->flags = qemu_get_be32(f);
+ for(i = 0; i < 3; i++) {
+ s = &pit->channels[i];
+ s->count=qemu_get_be32(f);
+ qemu_get_be16s(f, &s->latched_count);
+ qemu_get_8s(f, &s->count_latched);
+ qemu_get_8s(f, &s->status_latched);
+ qemu_get_8s(f, &s->status);
+ qemu_get_8s(f, &s->read_state);
+ qemu_get_8s(f, &s->write_state);
+ qemu_get_8s(f, &s->write_latch);
+ qemu_get_8s(f, &s->rw_mode);
+ qemu_get_8s(f, &s->mode);
+ qemu_get_8s(f, &s->bcd);
+ qemu_get_8s(f, &s->gate);
+ s->count_load_time=qemu_get_be64(f);
+ if (s->irq_timer) {
+ s->next_transition_time=qemu_get_be64(f);
+ qemu_get_timer(f, s->irq_timer);
+ }
+ }
+
+ return 0;
+}
+
+VMStateDescription vmstate_pit = {
+ .name = "i8254",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 1,
+ .load_state_old = pit_load_old,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(flags, PITState),
+ VMSTATE_STRUCT_ARRAY(channels, PITState, 3, 2, vmstate_pit_channel, PITChannelState),
+ VMSTATE_TIMER(channels[0].irq_timer, PITState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void pit_reset(void *opaque)
+{
+ PITState *pit = opaque;
+ PITChannelState *s;
+ int i;
+
+#ifdef TARGET_I386
+ pit->flags &= ~PIT_FLAGS_HPET_LEGACY;
+#endif
+ for(i = 0;i < 3; i++) {
+ s = &pit->channels[i];
+ s->mode = 3;
+ s->gate = (i != 2);
+ pit_load_count(pit, 0, i);
+ }
+}
+
+#ifdef TARGET_I386
+/* When HPET is operating in legacy mode, i8254 timer0 is disabled */
+
+void hpet_pit_disable(void)
+{
+ PITChannelState *s = &pit_state.channels[0];
+
+ if (kvm_enabled() && kvm_pit_in_kernel()) {
+ if (qemu_kvm_has_pit_state2()) {
+ kvm_hpet_disable_kpit();
+ } else {
+ fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__);
+ exit(1);
+ }
+ } else {
+ pit_state.flags |= PIT_FLAGS_HPET_LEGACY;
+ if (s->irq_timer) {
+ qemu_del_timer(s->irq_timer);
+ }
+ }
+}
+
+/* When HPET is reset or leaving legacy mode, it must reenable i8254
+ * timer 0
+ */
+
+void hpet_pit_enable(void)
+{
+ PITState *pit = &pit_state;
+ PITChannelState *s = &pit->channels[0];
+
+ if (kvm_enabled() && kvm_pit_in_kernel()) {
+ if (qemu_kvm_has_pit_state2()) {
+ kvm_hpet_enable_kpit();
+ } else {
+ fprintf(stderr, "%s: kvm does not support pit_state2!\n", __FUNCTION__);
+ exit(1);
+ }
+ } else {
+ pit_state.flags &= ~PIT_FLAGS_HPET_LEGACY;
+ pit_load_count(pit, s->count, 0);
+ }
+}
+#endif
+
+PITState *pit_init(int base, qemu_irq irq)
+{
+ PITState *pit = &pit_state;
+ PITChannelState *s;
+
+ s = &pit->channels[0];
+ /* the timer 0 is connected to an IRQ */
+ s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
+ s->irq = irq;
+
+ vmstate_register(NULL, base, &vmstate_pit, pit);
+ qemu_register_reset(pit_reset, pit);
+ register_ioport_write(base, 4, 1, pit_ioport_write, pit);
+ register_ioport_read(base, 3, 1, pit_ioport_read, pit);
+
+ return pit;
+}
diff --git a/hw/i8254.h b/hw/i8254.h
new file mode 100644
index 0000000..d23303a
--- /dev/null
+++ b/hw/i8254.h
@@ -0,0 +1,69 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_I8254_H
+#define QEMU_I8254_H
+
+#define PIT_SAVEVM_NAME "i8254"
+#define PIT_SAVEVM_VERSION 2
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+#define PIT_FLAGS_HPET_LEGACY 1
+
+typedef struct PITChannelState {
+ int count; /* can be 65536 */
+ uint16_t latched_count;
+ uint8_t count_latched;
+ uint8_t status_latched;
+ uint8_t status;
+ uint8_t read_state;
+ uint8_t write_state;
+ uint8_t write_latch;
+ uint8_t rw_mode;
+ uint8_t mode;
+ uint8_t bcd; /* not supported */
+ uint8_t gate; /* timer start */
+ int64_t count_load_time;
+ /* irq handling */
+ int64_t next_transition_time;
+ QEMUTimer *irq_timer;
+ qemu_irq irq;
+} PITChannelState;
+
+struct PITState {
+ PITChannelState channels[3];
+ uint32_t flags;
+};
+
+void pit_save(QEMUFile *f, void *opaque);
+
+int pit_load(QEMUFile *f, void *opaque, int version_id);
+
+void pit_reset(void *opaque);
+
+#endif
diff --git a/hw/i8259.c b/hw/i8259.c
new file mode 100644
index 0000000..986dcf5
--- /dev/null
+++ b/hw/i8259.c
@@ -0,0 +1,682 @@
+/*
+ * QEMU 8259 interrupt controller emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "apic.h"
+#include "isa.h"
+#include "monitor.h"
+#include "qemu-timer.h"
+
+#include "kvm.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+#ifdef DEBUG_PIC
+#define DPRINTF(fmt, ...) \
+ do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+//#define DEBUG_IRQ_LATENCY
+//#define DEBUG_IRQ_COUNT
+
+typedef struct PicState {
+ uint8_t last_irr; /* edge detection */
+ uint8_t irr; /* interrupt request register */
+ uint8_t imr; /* interrupt mask register */
+ uint8_t isr; /* interrupt service register */
+ uint8_t priority_add; /* highest irq priority */
+ uint8_t irq_base;
+ uint8_t read_reg_select;
+ uint8_t poll;
+ uint8_t special_mask;
+ uint8_t init_state;
+ uint8_t auto_eoi;
+ uint8_t rotate_on_auto_eoi;
+ uint8_t special_fully_nested_mode;
+ uint8_t init4; /* true if 4 byte init */
+ uint8_t single_mode; /* true if slave pic is not initialized */
+ uint8_t elcr; /* PIIX edge/trigger selection*/
+ uint8_t elcr_mask;
+ PicState2 *pics_state;
+} PicState;
+
+struct PicState2 {
+ /* 0 is master pic, 1 is slave pic */
+ /* XXX: better separation between the two pics */
+ PicState pics[2];
+ qemu_irq parent_irq;
+ void *irq_request_opaque;
+};
+
+#if defined(DEBUG_PIC) || defined (DEBUG_IRQ_COUNT)
+static int irq_level[16];
+#endif
+#ifdef DEBUG_IRQ_COUNT
+static uint64_t irq_count[16];
+#endif
+PicState2 *isa_pic;
+
+/* set irq level. If an edge is detected, then the IRR is set to 1 */
+static inline void pic_set_irq1(PicState *s, int irq, int level)
+{
+ int mask;
+ mask = 1 << irq;
+ if (s->elcr & mask) {
+ /* level triggered */
+ if (level) {
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->irr &= ~mask;
+ s->last_irr &= ~mask;
+ }
+ } else {
+ /* edge triggered */
+ if (level) {
+ if ((s->last_irr & mask) == 0)
+ s->irr |= mask;
+ s->last_irr |= mask;
+ } else {
+ s->last_irr &= ~mask;
+ }
+ }
+}
+
+/* return the highest priority found in mask (highest = smallest
+ number). Return 8 if no irq */
+static inline int get_priority(PicState *s, int mask)
+{
+ int priority;
+ if (mask == 0)
+ return 8;
+ priority = 0;
+ while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0)
+ priority++;
+ return priority;
+}
+
+/* return the pic wanted interrupt. return -1 if none */
+static int pic_get_irq(PicState *s)
+{
+ int mask, cur_priority, priority;
+
+ mask = s->irr & ~s->imr;
+ priority = get_priority(s, mask);
+ if (priority == 8)
+ return -1;
+ /* compute current priority. If special fully nested mode on the
+ master, the IRQ coming from the slave is not taken into account
+ for the priority computation. */
+ mask = s->isr;
+ if (s->special_mask)
+ mask &= ~s->imr;
+ if (s->special_fully_nested_mode && s == &s->pics_state->pics[0])
+ mask &= ~(1 << 2);
+ cur_priority = get_priority(s, mask);
+ if (priority < cur_priority) {
+ /* higher priority found: an irq should be generated */
+ return (priority + s->priority_add) & 7;
+ } else {
+ return -1;
+ }
+}
+
+/* raise irq to CPU if necessary. must be called every time the active
+ irq may change */
+/* XXX: should not export it, but it is needed for an APIC kludge */
+void pic_update_irq(PicState2 *s)
+{
+ int irq2, irq;
+
+ /* first look at slave pic */
+ irq2 = pic_get_irq(&s->pics[1]);
+ if (irq2 >= 0) {
+ /* if irq request by slave pic, signal master PIC */
+ pic_set_irq1(&s->pics[0], 2, 1);
+ pic_set_irq1(&s->pics[0], 2, 0);
+ }
+ /* look at requested irq */
+ irq = pic_get_irq(&s->pics[0]);
+ if (irq >= 0) {
+#if defined(DEBUG_PIC)
+ {
+ int i;
+ for(i = 0; i < 2; i++) {
+ printf("pic%d: imr=%x irr=%x padd=%d\n",
+ i, s->pics[i].imr, s->pics[i].irr,
+ s->pics[i].priority_add);
+
+ }
+ }
+ printf("pic: cpu_interrupt\n");
+#endif
+ qemu_irq_raise(s->parent_irq);
+ }
+
+/* all targets should do this rather than acking the IRQ in the cpu */
+#if defined(TARGET_MIPS) || defined(TARGET_PPC) || defined(TARGET_ALPHA)
+ else {
+ qemu_irq_lower(s->parent_irq);
+ }
+#endif
+}
+
+#ifdef DEBUG_IRQ_LATENCY
+int64_t irq_time[16];
+#endif
+
+static void i8259_set_irq(void *opaque, int irq, int level)
+{
+ PicState2 *s = opaque;
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+ if (level != irq_level[irq]) {
+ DPRINTF("i8259_set_irq: irq=%d level=%d\n", irq, level);
+ irq_level[irq] = level;
+#ifdef DEBUG_IRQ_COUNT
+ if (level == 1)
+ irq_count[irq]++;
+#endif
+ }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+ if (level) {
+ irq_time[irq] = qemu_get_clock(vm_clock);
+ }
+#endif
+ pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
+ pic_update_irq(s);
+}
+
+/* acknowledge interrupt 'irq' */
+static inline void pic_intack(PicState *s, int irq)
+{
+ if (s->auto_eoi) {
+ if (s->rotate_on_auto_eoi)
+ s->priority_add = (irq + 1) & 7;
+ } else {
+ s->isr |= (1 << irq);
+ }
+
+ /* We don't clear a level sensitive interrupt here */
+ if (!(s->elcr & (1 << irq)))
+ s->irr &= ~(1 << irq);
+
+}
+
+extern int time_drift_fix;
+extern int64_t timer_acks, timer_ints_to_push;
+
+int pic_read_irq(PicState2 *s)
+{
+ int irq, irq2, intno;
+
+ irq = pic_get_irq(&s->pics[0]);
+ if (irq >= 0) {
+
+ pic_intack(&s->pics[0], irq);
+#ifdef TARGET_I386
+ if (time_drift_fix && irq == 0) {
+ timer_acks++;
+ if (timer_ints_to_push > 0) {
+ timer_ints_to_push--;
+ /* simulate an edge irq0, like the one generated by i8254 */
+ pic_set_irq1(&s->pics[0], 0, 0);
+ pic_set_irq1(&s->pics[0], 0, 1);
+ }
+ }
+#endif
+ if (irq == 2) {
+ irq2 = pic_get_irq(&s->pics[1]);
+ if (irq2 >= 0) {
+ pic_intack(&s->pics[1], irq2);
+ } else {
+ /* spurious IRQ on slave controller */
+ irq2 = 7;
+ }
+ intno = s->pics[1].irq_base + irq2;
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
+ irq = irq2 + 8;
+#endif
+ } else {
+ intno = s->pics[0].irq_base + irq;
+ }
+ } else {
+ /* spurious IRQ on host controller */
+ irq = 7;
+ intno = s->pics[0].irq_base + irq;
+ }
+ pic_update_irq(s);
+
+#ifdef DEBUG_IRQ_LATENCY
+ printf("IRQ%d latency=%0.3fus\n",
+ irq,
+ (double)(qemu_get_clock(vm_clock) -
+ irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
+#endif
+ DPRINTF("pic_interrupt: irq=%d\n", irq);
+ return intno;
+}
+
+static void pic_reset(void *opaque)
+{
+ PicState *s = opaque;
+
+ s->last_irr = 0;
+ s->irr = 0;
+ s->imr = 0;
+ s->isr = 0;
+ s->priority_add = 0;
+ s->irq_base = 0;
+ s->read_reg_select = 0;
+ s->poll = 0;
+ s->special_mask = 0;
+ s->init_state = 0;
+ s->auto_eoi = 0;
+ s->rotate_on_auto_eoi = 0;
+ s->special_fully_nested_mode = 0;
+ s->init4 = 0;
+ s->single_mode = 0;
+ /* Note: ELCR is not reset */
+}
+
+static void pic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PicState *s = opaque;
+ int priority, cmd, irq;
+
+ DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
+ addr &= 1;
+ if (addr == 0) {
+ if (val & 0x10) {
+ /* init */
+ pic_reset(s);
+ /* deassert a pending interrupt */
+ qemu_irq_lower(s->pics_state->parent_irq);
+ s->init_state = 1;
+ s->init4 = val & 1;
+ s->single_mode = val & 2;
+ if (val & 0x08)
+ hw_error("level sensitive irq not supported");
+ } else if (val & 0x08) {
+ if (val & 0x04)
+ s->poll = 1;
+ if (val & 0x02)
+ s->read_reg_select = val & 1;
+ if (val & 0x40)
+ s->special_mask = (val >> 5) & 1;
+ } else {
+ cmd = val >> 5;
+ switch(cmd) {
+ case 0:
+ case 4:
+ s->rotate_on_auto_eoi = cmd >> 2;
+ break;
+ case 1: /* end of interrupt */
+ case 5:
+ priority = get_priority(s, s->isr);
+ if (priority != 8) {
+ irq = (priority + s->priority_add) & 7;
+ s->isr &= ~(1 << irq);
+ if (cmd == 5)
+ s->priority_add = (irq + 1) & 7;
+ pic_update_irq(s->pics_state);
+ }
+ break;
+ case 3:
+ irq = val & 7;
+ s->isr &= ~(1 << irq);
+ pic_update_irq(s->pics_state);
+ break;
+ case 6:
+ s->priority_add = (val + 1) & 7;
+ pic_update_irq(s->pics_state);
+ break;
+ case 7:
+ irq = val & 7;
+ s->isr &= ~(1 << irq);
+ s->priority_add = (irq + 1) & 7;
+ pic_update_irq(s->pics_state);
+ break;
+ default:
+ /* no operation */
+ break;
+ }
+ }
+ } else {
+ switch(s->init_state) {
+ case 0:
+ /* normal mode */
+ s->imr = val;
+ pic_update_irq(s->pics_state);
+ break;
+ case 1:
+ s->irq_base = val & 0xf8;
+ s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
+ break;
+ case 2:
+ if (s->init4) {
+ s->init_state = 3;
+ } else {
+ s->init_state = 0;
+ }
+ break;
+ case 3:
+ s->special_fully_nested_mode = (val >> 4) & 1;
+ s->auto_eoi = (val >> 1) & 1;
+ s->init_state = 0;
+ break;
+ }
+ }
+}
+
+static uint32_t pic_poll_read (PicState *s, uint32_t addr1)
+{
+ int ret;
+
+ ret = pic_get_irq(s);
+ if (ret >= 0) {
+ if (addr1 >> 7) {
+ s->pics_state->pics[0].isr &= ~(1 << 2);
+ s->pics_state->pics[0].irr &= ~(1 << 2);
+ }
+ s->irr &= ~(1 << ret);
+ s->isr &= ~(1 << ret);
+ if (addr1 >> 7 || ret != 2)
+ pic_update_irq(s->pics_state);
+ } else {
+ ret = 0x07;
+ pic_update_irq(s->pics_state);
+ }
+
+ return ret;
+}
+
+static uint32_t pic_ioport_read(void *opaque, uint32_t addr1)
+{
+ PicState *s = opaque;
+ unsigned int addr;
+ int ret;
+
+ addr = addr1;
+ addr &= 1;
+ if (s->poll) {
+ ret = pic_poll_read(s, addr1);
+ s->poll = 0;
+ } else {
+ if (addr == 0) {
+ if (s->read_reg_select)
+ ret = s->isr;
+ else
+ ret = s->irr;
+ } else {
+ ret = s->imr;
+ }
+ }
+ DPRINTF("read: addr=0x%02x val=0x%02x\n", addr1, ret);
+ return ret;
+}
+
+/* memory mapped interrupt status */
+/* XXX: may be the same than pic_read_irq() */
+uint32_t pic_intack_read(PicState2 *s)
+{
+ int ret;
+
+ ret = pic_poll_read(&s->pics[0], 0x00);
+ if (ret == 2)
+ ret = pic_poll_read(&s->pics[1], 0x80) + 8;
+ /* Prepare for ISR read */
+ s->pics[0].read_reg_select = 1;
+
+ return ret;
+}
+
+static void elcr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PicState *s = opaque;
+ s->elcr = val & s->elcr_mask;
+}
+
+static uint32_t elcr_ioport_read(void *opaque, uint32_t addr1)
+{
+ PicState *s = opaque;
+ return s->elcr;
+}
+
+static void kvm_kernel_pic_save_to_user(PicState *s);
+static int kvm_kernel_pic_load_from_user(PicState *s);
+
+static void pic_pre_save(void *opaque)
+{
+ PicState *s = opaque;
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_pic_save_to_user(s);
+ }
+}
+
+static int pic_post_load(void *opaque, int version_id)
+{
+ PicState *s = opaque;
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_pic_load_from_user(s);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_pic = {
+ .name = "i8259",
+ .version_id = 1,
+ .pre_save = pic_pre_save,
+ .post_load = pic_post_load,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(last_irr, PicState),
+ VMSTATE_UINT8(irr, PicState),
+ VMSTATE_UINT8(imr, PicState),
+ VMSTATE_UINT8(isr, PicState),
+ VMSTATE_UINT8(priority_add, PicState),
+ VMSTATE_UINT8(irq_base, PicState),
+ VMSTATE_UINT8(read_reg_select, PicState),
+ VMSTATE_UINT8(poll, PicState),
+ VMSTATE_UINT8(special_mask, PicState),
+ VMSTATE_UINT8(init_state, PicState),
+ VMSTATE_UINT8(auto_eoi, PicState),
+ VMSTATE_UINT8(rotate_on_auto_eoi, PicState),
+ VMSTATE_UINT8(special_fully_nested_mode, PicState),
+ VMSTATE_UINT8(init4, PicState),
+ VMSTATE_UINT8(single_mode, PicState),
+ VMSTATE_UINT8(elcr, PicState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* XXX: add generic master/slave system */
+static void pic_init1(int io_addr, int elcr_addr, PicState *s)
+{
+ register_ioport_write(io_addr, 2, 1, pic_ioport_write, s);
+ register_ioport_read(io_addr, 2, 1, pic_ioport_read, s);
+ if (elcr_addr >= 0) {
+ register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s);
+ register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s);
+ }
+ vmstate_register(NULL, io_addr, &vmstate_pic, s);
+ qemu_register_reset(pic_reset, s);
+}
+
+void pic_info(Monitor *mon)
+{
+ int i;
+ PicState *s;
+
+ if (!isa_pic)
+ return;
+
+ for(i=0;i<2;i++) {
+ s = &isa_pic->pics[i];
+ monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
+ "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
+ i, s->irr, s->imr, s->isr, s->priority_add,
+ s->irq_base, s->read_reg_select, s->elcr,
+ s->special_fully_nested_mode);
+ }
+}
+
+void irq_info(Monitor *mon)
+{
+#ifndef DEBUG_IRQ_COUNT
+ monitor_printf(mon, "irq statistic code not compiled.\n");
+#else
+ int i;
+ int64_t count;
+
+ monitor_printf(mon, "IRQ statistics:\n");
+ for (i = 0; i < 16; i++) {
+ count = irq_count[i];
+ if (count > 0)
+ monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
+ }
+#endif
+}
+
+qemu_irq *i8259_init(qemu_irq parent_irq)
+{
+ PicState2 *s;
+
+ s = qemu_mallocz(sizeof(PicState2));
+ pic_init1(0x20, 0x4d0, &s->pics[0]);
+ pic_init1(0xa0, 0x4d1, &s->pics[1]);
+ s->pics[0].elcr_mask = 0xf8;
+ s->pics[1].elcr_mask = 0xde;
+ s->parent_irq = parent_irq;
+ s->pics[0].pics_state = s;
+ s->pics[1].pics_state = s;
+ isa_pic = s;
+ return qemu_allocate_irqs(i8259_set_irq, s, 16);
+}
+
+static void kvm_kernel_pic_save_to_user(PicState *s)
+{
+#ifdef KVM_CAP_IRQCHIP
+ struct kvm_irqchip chip;
+ struct kvm_pic_state *kpic;
+
+ chip.chip_id = (&s->pics_state->pics[0] == s) ?
+ KVM_IRQCHIP_PIC_MASTER :
+ KVM_IRQCHIP_PIC_SLAVE;
+ kvm_get_irqchip(kvm_context, &chip);
+ kpic = &chip.chip.pic;
+
+ s->last_irr = kpic->last_irr;
+ s->irr = kpic->irr;
+ s->imr = kpic->imr;
+ s->isr = kpic->isr;
+ s->priority_add = kpic->priority_add;
+ s->irq_base = kpic->irq_base;
+ s->read_reg_select = kpic->read_reg_select;
+ s->poll = kpic->poll;
+ s->special_mask = kpic->special_mask;
+ s->init_state = kpic->init_state;
+ s->auto_eoi = kpic->auto_eoi;
+ s->rotate_on_auto_eoi = kpic->rotate_on_auto_eoi;
+ s->special_fully_nested_mode = kpic->special_fully_nested_mode;
+ s->init4 = kpic->init4;
+ s->elcr = kpic->elcr;
+ s->elcr_mask = kpic->elcr_mask;
+#endif
+}
+
+static int kvm_kernel_pic_load_from_user(PicState *s)
+{
+#ifdef KVM_CAP_IRQCHIP
+ struct kvm_irqchip chip;
+ struct kvm_pic_state *kpic;
+
+ chip.chip_id = (&s->pics_state->pics[0] == s) ?
+ KVM_IRQCHIP_PIC_MASTER :
+ KVM_IRQCHIP_PIC_SLAVE;
+ kpic = &chip.chip.pic;
+
+ kpic->last_irr = s->last_irr;
+ kpic->irr = s->irr;
+ kpic->imr = s->imr;
+ kpic->isr = s->isr;
+ kpic->priority_add = s->priority_add;
+ kpic->irq_base = s->irq_base;
+ kpic->read_reg_select = s->read_reg_select;
+ kpic->poll = s->poll;
+ kpic->special_mask = s->special_mask;
+ kpic->init_state = s->init_state;
+ kpic->auto_eoi = s->auto_eoi;
+ kpic->rotate_on_auto_eoi = s->rotate_on_auto_eoi;
+ kpic->special_fully_nested_mode = s->special_fully_nested_mode;
+ kpic->init4 = s->init4;
+ kpic->elcr = s->elcr;
+ kpic->elcr_mask = s->elcr_mask;
+
+ kvm_set_irqchip(kvm_context, &chip);
+#endif
+ return 0;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+static void kvm_i8259_set_irq(void *opaque, int irq, int level)
+{
+ int pic_ret;
+ if (kvm_set_irq(irq, level, &pic_ret)) {
+ if (pic_ret != 0)
+ apic_set_irq_delivered();
+ return;
+ }
+}
+
+static void kvm_pic_init1(int io_addr, PicState *s)
+{
+ vmstate_register(NULL, io_addr, &vmstate_pic, s);
+ qemu_register_reset(pic_reset, s);
+}
+
+qemu_irq *kvm_i8259_init(qemu_irq parent_irq)
+{
+ PicState2 *s;
+
+ s = qemu_mallocz(sizeof(PicState2));
+
+ kvm_pic_init1(0x20, &s->pics[0]);
+ kvm_pic_init1(0xa0, &s->pics[1]);
+ s->parent_irq = parent_irq;
+ s->pics[0].pics_state = s;
+ s->pics[1].pics_state = s;
+ isa_pic = s;
+ return qemu_allocate_irqs(kvm_i8259_set_irq, s, 24);
+}
+#endif
+
+
+
diff --git a/hw/ide.h b/hw/ide.h
new file mode 100644
index 0000000..73fb550
--- /dev/null
+++ b/hw/ide.h
@@ -0,0 +1,31 @@
+#ifndef HW_IDE_H
+#define HW_IDE_H
+
+#include "isa.h"
+#include "pci.h"
+
+#define MAX_IDE_DEVS 2
+
+/* ide-isa.c */
+ISADevice *isa_ide_init(int iobase, int iobase2, int isairq,
+ DriveInfo *hd0, DriveInfo *hd1);
+
+/* ide-pci.c */
+void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
+ int secondary_ide_enabled);
+PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
+PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
+void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn);
+
+/* ide-macio.c */
+int pmac_ide_init (DriveInfo **hd_table, qemu_irq irq,
+ void *dbdma, int channel, qemu_irq dma_irq);
+
+/* ide-mmio.c */
+void mmio_ide_init (target_phys_addr_t membase, target_phys_addr_t membase2,
+ qemu_irq irq, int shift,
+ DriveInfo *hd0, DriveInfo *hd1);
+
+void ide_get_bs(BlockDriverState *bs[], BusState *qbus);
+
+#endif /* HW_IDE_H */
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
new file mode 100644
index 0000000..98bdf70
--- /dev/null
+++ b/hw/ide/ahci.c
@@ -0,0 +1,1152 @@
+/*
+ * QEMU AHCI Emulation
+ *
+ * Copyright (c) 2010 qiaochong@loongson.cn
+ * Copyright (c) 2010 Roland Elek <elek.roland@gmail.com>
+ * Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
+ * Copyright (c) 2010 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <hw/hw.h>
+#include <hw/msi.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+
+#include "monitor.h"
+#include "dma.h"
+#include "cpu-common.h"
+#include "internal.h"
+#include <hw/ide/pci.h>
+#include <hw/ide/ahci.h>
+
+/* #define DEBUG_AHCI */
+
+#ifdef DEBUG_AHCI
+#define DPRINTF(port, fmt, ...) \
+do { fprintf(stderr, "ahci: %s: [%d] ", __FUNCTION__, port); \
+ fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(port, fmt, ...) do {} while(0)
+#endif
+
+static void check_cmd(AHCIState *s, int port);
+static int handle_cmd(AHCIState *s,int port,int slot);
+static void ahci_reset_port(AHCIState *s, int port);
+static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
+static void ahci_init_d2h(AHCIDevice *ad);
+
+static uint32_t ahci_port_read(AHCIState *s, int port, int offset)
+{
+ uint32_t val;
+ AHCIPortRegs *pr;
+ pr = &s->dev[port].port_regs;
+
+ switch (offset) {
+ case PORT_LST_ADDR:
+ val = pr->lst_addr;
+ break;
+ case PORT_LST_ADDR_HI:
+ val = pr->lst_addr_hi;
+ break;
+ case PORT_FIS_ADDR:
+ val = pr->fis_addr;
+ break;
+ case PORT_FIS_ADDR_HI:
+ val = pr->fis_addr_hi;
+ break;
+ case PORT_IRQ_STAT:
+ val = pr->irq_stat;
+ break;
+ case PORT_IRQ_MASK:
+ val = pr->irq_mask;
+ break;
+ case PORT_CMD:
+ val = pr->cmd;
+ break;
+ case PORT_TFDATA:
+ val = ((uint16_t)s->dev[port].port.ifs[0].error << 8) |
+ s->dev[port].port.ifs[0].status;
+ break;
+ case PORT_SIG:
+ val = pr->sig;
+ break;
+ case PORT_SCR_STAT:
+ if (s->dev[port].port.ifs[0].bs) {
+ val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP |
+ SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE;
+ } else {
+ val = SATA_SCR_SSTATUS_DET_NODEV;
+ }
+ break;
+ case PORT_SCR_CTL:
+ val = pr->scr_ctl;
+ break;
+ case PORT_SCR_ERR:
+ val = pr->scr_err;
+ break;
+ case PORT_SCR_ACT:
+ pr->scr_act &= ~s->dev[port].finished;
+ s->dev[port].finished = 0;
+ val = pr->scr_act;
+ break;
+ case PORT_CMD_ISSUE:
+ val = pr->cmd_issue;
+ break;
+ case PORT_RESERVED:
+ default:
+ val = 0;
+ }
+ DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val);
+ return val;
+
+}
+
+static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev)
+{
+ struct AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
+
+ DPRINTF(0, "raise irq\n");
+
+ if (msi_enabled(&d->card)) {
+ msi_notify(&d->card, 0);
+ } else {
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev)
+{
+ struct AHCIPCIState *d = container_of(s, AHCIPCIState, ahci);
+
+ DPRINTF(0, "lower irq\n");
+
+ if (!msi_enabled(&d->card)) {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void ahci_check_irq(AHCIState *s)
+{
+ int i;
+
+ DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus);
+
+ for (i = 0; i < s->ports; i++) {
+ AHCIPortRegs *pr = &s->dev[i].port_regs;
+ if (pr->irq_stat & pr->irq_mask) {
+ s->control_regs.irqstatus |= (1 << i);
+ }
+ }
+
+ if (s->control_regs.irqstatus &&
+ (s->control_regs.ghc & HOST_CTL_IRQ_EN)) {
+ ahci_irq_raise(s, NULL);
+ } else {
+ ahci_irq_lower(s, NULL);
+ }
+}
+
+static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d,
+ int irq_type)
+{
+ DPRINTF(d->port_no, "trigger irq %#x -> %x\n",
+ irq_type, d->port_regs.irq_mask & irq_type);
+
+ d->port_regs.irq_stat |= irq_type;
+ ahci_check_irq(s);
+}
+
+static void map_page(uint8_t **ptr, uint64_t addr, uint32_t wanted)
+{
+ target_phys_addr_t len = wanted;
+
+ if (*ptr) {
+ cpu_physical_memory_unmap(*ptr, len, 1, len);
+ }
+
+ *ptr = cpu_physical_memory_map(addr, &len, 1);
+ if (len < wanted) {
+ cpu_physical_memory_unmap(*ptr, len, 1, len);
+ *ptr = NULL;
+ }
+}
+
+static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
+{
+ AHCIPortRegs *pr = &s->dev[port].port_regs;
+
+ DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val);
+ switch (offset) {
+ case PORT_LST_ADDR:
+ pr->lst_addr = val;
+ map_page(&s->dev[port].lst,
+ ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
+ s->dev[port].cur_cmd = NULL;
+ break;
+ case PORT_LST_ADDR_HI:
+ pr->lst_addr_hi = val;
+ map_page(&s->dev[port].lst,
+ ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024);
+ s->dev[port].cur_cmd = NULL;
+ break;
+ case PORT_FIS_ADDR:
+ pr->fis_addr = val;
+ map_page(&s->dev[port].res_fis,
+ ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
+ break;
+ case PORT_FIS_ADDR_HI:
+ pr->fis_addr_hi = val;
+ map_page(&s->dev[port].res_fis,
+ ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256);
+ break;
+ case PORT_IRQ_STAT:
+ pr->irq_stat &= ~val;
+ break;
+ case PORT_IRQ_MASK:
+ pr->irq_mask = val & 0xfdc000ff;
+ ahci_check_irq(s);
+ break;
+ case PORT_CMD:
+ pr->cmd = val & ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON);
+
+ if (pr->cmd & PORT_CMD_START) {
+ pr->cmd |= PORT_CMD_LIST_ON;
+ }
+
+ if (pr->cmd & PORT_CMD_FIS_RX) {
+ pr->cmd |= PORT_CMD_FIS_ON;
+ }
+
+ /* XXX usually the FIS would be pending on the bus here and
+ issuing deferred until the OS enables FIS receival.
+ Instead, we only submit it once - which works in most
+ cases, but is a hack. */
+ if ((pr->cmd & PORT_CMD_FIS_ON) &&
+ !s->dev[port].init_d2h_sent) {
+ ahci_init_d2h(&s->dev[port]);
+ s->dev[port].init_d2h_sent = 1;
+ }
+
+ check_cmd(s, port);
+ break;
+ case PORT_TFDATA:
+ s->dev[port].port.ifs[0].error = (val >> 8) & 0xff;
+ s->dev[port].port.ifs[0].status = val & 0xff;
+ break;
+ case PORT_SIG:
+ pr->sig = val;
+ break;
+ case PORT_SCR_STAT:
+ pr->scr_stat = val;
+ break;
+ case PORT_SCR_CTL:
+ if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) &&
+ ((val & AHCI_SCR_SCTL_DET) == 0)) {
+ ahci_reset_port(s, port);
+ }
+ pr->scr_ctl = val;
+ break;
+ case PORT_SCR_ERR:
+ pr->scr_err &= ~val;
+ break;
+ case PORT_SCR_ACT:
+ /* RW1 */
+ pr->scr_act |= val;
+ break;
+ case PORT_CMD_ISSUE:
+ pr->cmd_issue |= val;
+ check_cmd(s, port);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t ahci_mem_readl(void *ptr, target_phys_addr_t addr)
+{
+ AHCIState *s = ptr;
+ uint32_t val = 0;
+
+ addr = addr & 0xfff;
+ if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
+ switch (addr) {
+ case HOST_CAP:
+ val = s->control_regs.cap;
+ break;
+ case HOST_CTL:
+ val = s->control_regs.ghc;
+ break;
+ case HOST_IRQ_STAT:
+ val = s->control_regs.irqstatus;
+ break;
+ case HOST_PORTS_IMPL:
+ val = s->control_regs.impl;
+ break;
+ case HOST_VERSION:
+ val = s->control_regs.version;
+ break;
+ }
+
+ DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val);
+ } else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
+ (addr < (AHCI_PORT_REGS_START_ADDR +
+ (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
+ val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
+ addr & AHCI_PORT_ADDR_OFFSET_MASK);
+ }
+
+ return val;
+}
+
+
+
+static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ AHCIState *s = ptr;
+ addr = addr & 0xfff;
+
+ /* Only aligned reads are allowed on AHCI */
+ if (addr & 3) {
+ fprintf(stderr, "ahci: Mis-aligned write to addr 0x"
+ TARGET_FMT_plx "\n", addr);
+ return;
+ }
+
+ if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
+ DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val);
+
+ switch (addr) {
+ case HOST_CAP: /* R/WO, RO */
+ /* FIXME handle R/WO */
+ break;
+ case HOST_CTL: /* R/W */
+ if (val & HOST_CTL_RESET) {
+ DPRINTF(-1, "HBA Reset\n");
+ ahci_reset(container_of(s, AHCIPCIState, ahci));
+ } else {
+ s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN;
+ ahci_check_irq(s);
+ }
+ break;
+ case HOST_IRQ_STAT: /* R/WC, RO */
+ s->control_regs.irqstatus &= ~val;
+ ahci_check_irq(s);
+ break;
+ case HOST_PORTS_IMPL: /* R/WO, RO */
+ /* FIXME handle R/WO */
+ break;
+ case HOST_VERSION: /* RO */
+ /* FIXME report write? */
+ break;
+ default:
+ DPRINTF(-1, "write to unknown register 0x%x\n", (unsigned)addr);
+ }
+ } else if ((addr >= AHCI_PORT_REGS_START_ADDR) &&
+ (addr < (AHCI_PORT_REGS_START_ADDR +
+ (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) {
+ ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7,
+ addr & AHCI_PORT_ADDR_OFFSET_MASK, val);
+ }
+
+}
+
+static CPUReadMemoryFunc * const ahci_readfn[3]={
+ ahci_mem_readl,
+ ahci_mem_readl,
+ ahci_mem_readl
+};
+
+static CPUWriteMemoryFunc * const ahci_writefn[3]={
+ ahci_mem_writel,
+ ahci_mem_writel,
+ ahci_mem_writel
+};
+
+static void ahci_reg_init(AHCIState *s)
+{
+ int i;
+
+ s->control_regs.cap = (s->ports - 1) |
+ (AHCI_NUM_COMMAND_SLOTS << 8) |
+ (AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) |
+ HOST_CAP_NCQ | HOST_CAP_AHCI;
+
+ s->control_regs.impl = (1 << s->ports) - 1;
+
+ s->control_regs.version = AHCI_VERSION_1_0;
+
+ for (i = 0; i < s->ports; i++) {
+ s->dev[i].port_state = STATE_RUN;
+ }
+}
+
+static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len,
+ QEMUSGList *sglist)
+{
+ uint32_t i = 0;
+ uint32_t total = 0, once;
+ ScatterGatherEntry *cur_prd;
+ uint32_t sgcount;
+
+ cur_prd = sglist->sg;
+ sgcount = sglist->nsg;
+ for (i = 0; len && sgcount; i++) {
+ once = MIN(cur_prd->len, len);
+ cpu_physical_memory_read(cur_prd->base, buffer, once);
+ cur_prd++;
+ sgcount--;
+ len -= once;
+ buffer += once;
+ total += once;
+ }
+
+ return total;
+}
+
+static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len,
+ QEMUSGList *sglist)
+{
+ uint32_t i = 0;
+ uint32_t total = 0, once;
+ ScatterGatherEntry *cur_prd;
+ uint32_t sgcount;
+
+ DPRINTF(-1, "total: 0x%x bytes\n", len);
+
+ cur_prd = sglist->sg;
+ sgcount = sglist->nsg;
+ for (i = 0; len && sgcount; i++) {
+ once = MIN(cur_prd->len, len);
+ DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base);
+ cpu_physical_memory_write(cur_prd->base, buffer, once);
+ cur_prd++;
+ sgcount--;
+ len -= once;
+ buffer += once;
+ total += once;
+ }
+
+ return total;
+}
+
+static void check_cmd(AHCIState *s, int port)
+{
+ AHCIPortRegs *pr = &s->dev[port].port_regs;
+ int slot;
+
+ if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
+ for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
+ if ((pr->cmd_issue & (1 << slot)) &&
+ !handle_cmd(s, port, slot)) {
+ pr->cmd_issue &= ~(1 << slot);
+ }
+ }
+ }
+}
+
+static void ahci_check_cmd_bh(void *opaque)
+{
+ AHCIDevice *ad = opaque;
+
+ qemu_bh_delete(ad->check_bh);
+ ad->check_bh = NULL;
+
+ if ((ad->busy_slot != -1) &&
+ !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) {
+ /* no longer busy */
+ ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
+ ad->busy_slot = -1;
+ }
+
+ check_cmd(ad->hba, ad->port_no);
+}
+
+static void ahci_init_d2h(AHCIDevice *ad)
+{
+ uint8_t init_fis[0x20];
+ IDEState *ide_state = &ad->port.ifs[0];
+
+ memset(init_fis, 0, sizeof(init_fis));
+
+ init_fis[4] = 1;
+ init_fis[12] = 1;
+
+ if (ide_state->drive_kind == IDE_CD) {
+ init_fis[5] = ide_state->lcyl;
+ init_fis[6] = ide_state->hcyl;
+ }
+
+ ahci_write_fis_d2h(ad, init_fis);
+}
+
+static void ahci_reset_port(AHCIState *s, int port)
+{
+ AHCIDevice *d = &s->dev[port];
+ AHCIPortRegs *pr = &d->port_regs;
+ IDEState *ide_state = &d->port.ifs[0];
+ int i;
+
+ DPRINTF(port, "reset port\n");
+
+ ide_bus_reset(&d->port);
+ ide_state->ncq_queues = AHCI_MAX_CMDS;
+
+ pr->irq_stat = 0;
+ pr->irq_mask = 0;
+ pr->scr_stat = 0;
+ pr->scr_ctl = 0;
+ pr->scr_err = 0;
+ pr->scr_act = 0;
+ d->busy_slot = -1;
+ d->init_d2h_sent = 0;
+
+ ide_state = &s->dev[port].port.ifs[0];
+ if (!ide_state->bs) {
+ return;
+ }
+
+ /* reset ncq queue */
+ for (i = 0; i < AHCI_MAX_CMDS; i++) {
+ NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i];
+ if (!ncq_tfs->used) {
+ continue;
+ }
+
+ if (ncq_tfs->aiocb) {
+ bdrv_aio_cancel(ncq_tfs->aiocb);
+ ncq_tfs->aiocb = NULL;
+ }
+
+ qemu_sglist_destroy(&ncq_tfs->sglist);
+ ncq_tfs->used = 0;
+ }
+
+ s->dev[port].port_state = STATE_RUN;
+ if (!ide_state->bs) {
+ s->dev[port].port_regs.sig = 0;
+ ide_state->status = SEEK_STAT | WRERR_STAT;
+ } else if (ide_state->drive_kind == IDE_CD) {
+ s->dev[port].port_regs.sig = SATA_SIGNATURE_CDROM;
+ ide_state->lcyl = 0x14;
+ ide_state->hcyl = 0xeb;
+ DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
+ ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
+ } else {
+ s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK;
+ ide_state->status = SEEK_STAT | WRERR_STAT;
+ }
+
+ ide_state->error = 1;
+ ahci_init_d2h(d);
+}
+
+static void debug_print_fis(uint8_t *fis, int cmd_len)
+{
+#ifdef DEBUG_AHCI
+ int i;
+
+ fprintf(stderr, "fis:");
+ for (i = 0; i < cmd_len; i++) {
+ if ((i & 0xf) == 0) {
+ fprintf(stderr, "\n%02x:",i);
+ }
+ fprintf(stderr, "%02x ",fis[i]);
+ }
+ fprintf(stderr, "\n");
+#endif
+}
+
+static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished)
+{
+ AHCIPortRegs *pr = &s->dev[port].port_regs;
+ IDEState *ide_state;
+ uint8_t *sdb_fis;
+
+ if (!s->dev[port].res_fis ||
+ !(pr->cmd & PORT_CMD_FIS_RX)) {
+ return;
+ }
+
+ sdb_fis = &s->dev[port].res_fis[RES_FIS_SDBFIS];
+ ide_state = &s->dev[port].port.ifs[0];
+
+ /* clear memory */
+ *(uint32_t*)sdb_fis = 0;
+
+ /* write values */
+ sdb_fis[0] = ide_state->error;
+ sdb_fis[2] = ide_state->status & 0x77;
+ s->dev[port].finished |= finished;
+ *(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished);
+
+ ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_STAT_SDBS);
+}
+
+static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
+{
+ AHCIPortRegs *pr = &ad->port_regs;
+ uint8_t *d2h_fis;
+ int i;
+ target_phys_addr_t cmd_len = 0x80;
+ int cmd_mapped = 0;
+
+ if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
+ return;
+ }
+
+ if (!cmd_fis) {
+ /* map cmd_fis */
+ uint64_t tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr);
+ cmd_fis = cpu_physical_memory_map(tbl_addr, &cmd_len, 0);
+ cmd_mapped = 1;
+ }
+
+ d2h_fis = &ad->res_fis[RES_FIS_RFIS];
+
+ d2h_fis[0] = 0x34;
+ d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+ d2h_fis[2] = ad->port.ifs[0].status;
+ d2h_fis[3] = ad->port.ifs[0].error;
+
+ d2h_fis[4] = cmd_fis[4];
+ d2h_fis[5] = cmd_fis[5];
+ d2h_fis[6] = cmd_fis[6];
+ d2h_fis[7] = cmd_fis[7];
+ d2h_fis[8] = cmd_fis[8];
+ d2h_fis[9] = cmd_fis[9];
+ d2h_fis[10] = cmd_fis[10];
+ d2h_fis[11] = cmd_fis[11];
+ d2h_fis[12] = cmd_fis[12];
+ d2h_fis[13] = cmd_fis[13];
+ for (i = 14; i < 0x20; i++) {
+ d2h_fis[i] = 0;
+ }
+
+ if (d2h_fis[2] & ERR_STAT) {
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_TFES);
+ }
+
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
+
+ if (cmd_mapped) {
+ cpu_physical_memory_unmap(cmd_fis, cmd_len, 0, cmd_len);
+ }
+}
+
+static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist)
+{
+ AHCICmdHdr *cmd = ad->cur_cmd;
+ uint32_t opts = le32_to_cpu(cmd->opts);
+ uint64_t prdt_addr = le64_to_cpu(cmd->tbl_addr) + 0x80;
+ int sglist_alloc_hint = opts >> AHCI_CMD_HDR_PRDT_LEN;
+ target_phys_addr_t prdt_len = (sglist_alloc_hint * sizeof(AHCI_SG));
+ target_phys_addr_t real_prdt_len = prdt_len;
+ uint8_t *prdt;
+ int i;
+ int r = 0;
+
+ if (!sglist_alloc_hint) {
+ DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts);
+ return -1;
+ }
+
+ /* map PRDT */
+ if (!(prdt = cpu_physical_memory_map(prdt_addr, &prdt_len, 0))){
+ DPRINTF(ad->port_no, "map failed\n");
+ return -1;
+ }
+
+ if (prdt_len < real_prdt_len) {
+ DPRINTF(ad->port_no, "mapped less than expected\n");
+ r = -1;
+ goto out;
+ }
+
+ /* Get entries in the PRDT, init a qemu sglist accordingly */
+ if (sglist_alloc_hint > 0) {
+ AHCI_SG *tbl = (AHCI_SG *)prdt;
+
+ qemu_sglist_init(sglist, sglist_alloc_hint);
+ for (i = 0; i < sglist_alloc_hint; i++) {
+ /* flags_size is zero-based */
+ qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr),
+ le32_to_cpu(tbl[i].flags_size) + 1);
+ }
+ }
+
+out:
+ cpu_physical_memory_unmap(prdt, prdt_len, 0, prdt_len);
+ return r;
+}
+
+static void ncq_cb(void *opaque, int ret)
+{
+ NCQTransferState *ncq_tfs = (NCQTransferState *)opaque;
+ IDEState *ide_state = &ncq_tfs->drive->port.ifs[0];
+
+ /* Clear bit for this tag in SActive */
+ ncq_tfs->drive->port_regs.scr_act &= ~(1 << ncq_tfs->tag);
+
+ if (ret < 0) {
+ /* error */
+ ide_state->error = ABRT_ERR;
+ ide_state->status = READY_STAT | ERR_STAT;
+ ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
+ } else {
+ ide_state->status = READY_STAT | SEEK_STAT;
+ }
+
+ ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs->drive->port_no,
+ (1 << ncq_tfs->tag));
+
+ DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n",
+ ncq_tfs->tag);
+
+ qemu_sglist_destroy(&ncq_tfs->sglist);
+ ncq_tfs->used = 0;
+}
+
+static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
+ int slot)
+{
+ NCQFrame *ncq_fis = (NCQFrame*)cmd_fis;
+ uint8_t tag = ncq_fis->tag >> 3;
+ NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[tag];
+
+ if (ncq_tfs->used) {
+ /* error - already in use */
+ fprintf(stderr, "%s: tag %d already used\n", __FUNCTION__, tag);
+ return;
+ }
+
+ ncq_tfs->used = 1;
+ ncq_tfs->drive = &s->dev[port];
+ ncq_tfs->slot = slot;
+ ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) |
+ ((uint64_t)ncq_fis->lba4 << 32) |
+ ((uint64_t)ncq_fis->lba3 << 24) |
+ ((uint64_t)ncq_fis->lba2 << 16) |
+ ((uint64_t)ncq_fis->lba1 << 8) |
+ (uint64_t)ncq_fis->lba0;
+
+ /* Note: We calculate the sector count, but don't currently rely on it.
+ * The total size of the DMA buffer tells us the transfer size instead. */
+ ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
+ ncq_fis->sector_count_low;
+
+ DPRINTF(port, "NCQ transfer LBA from %ld to %ld, drive max %ld\n",
+ ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
+ s->dev[port].port.ifs[0].nb_sectors - 1);
+
+ ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist);
+ ncq_tfs->tag = tag;
+
+ switch(ncq_fis->command) {
+ case READ_FPDMA_QUEUED:
+ DPRINTF(port, "NCQ reading %d sectors from LBA %ld, tag %d\n",
+ ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
+ ncq_tfs->is_read = 1;
+
+ DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba);
+ ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs,
+ &ncq_tfs->sglist, ncq_tfs->lba,
+ ncq_cb, ncq_tfs);
+ break;
+ case WRITE_FPDMA_QUEUED:
+ DPRINTF(port, "NCQ writing %d sectors to LBA %ld, tag %d\n",
+ ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
+ ncq_tfs->is_read = 0;
+
+ DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba);
+ ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs,
+ &ncq_tfs->sglist, ncq_tfs->lba,
+ ncq_cb, ncq_tfs);
+ break;
+ default:
+ DPRINTF(port, "error: tried to process non-NCQ command as NCQ\n");
+ qemu_sglist_destroy(&ncq_tfs->sglist);
+ break;
+ }
+}
+
+static int handle_cmd(AHCIState *s, int port, int slot)
+{
+ IDEState *ide_state;
+ uint32_t opts;
+ uint64_t tbl_addr;
+ AHCICmdHdr *cmd;
+ uint8_t *cmd_fis;
+ target_phys_addr_t cmd_len;
+
+ if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
+ /* Engine currently busy, try again later */
+ DPRINTF(port, "engine busy\n");
+ return -1;
+ }
+
+ cmd = &((AHCICmdHdr *)s->dev[port].lst)[slot];
+
+ if (!s->dev[port].lst) {
+ DPRINTF(port, "error: lst not given but cmd handled");
+ return -1;
+ }
+
+ /* remember current slot handle for later */
+ s->dev[port].cur_cmd = cmd;
+
+ opts = le32_to_cpu(cmd->opts);
+ tbl_addr = le64_to_cpu(cmd->tbl_addr);
+
+ cmd_len = 0x80;
+ cmd_fis = cpu_physical_memory_map(tbl_addr, &cmd_len, 1);
+
+ if (!cmd_fis) {
+ DPRINTF(port, "error: guest passed us an invalid cmd fis\n");
+ return -1;
+ }
+
+ /* The device we are working for */
+ ide_state = &s->dev[port].port.ifs[0];
+
+ if (!ide_state->bs) {
+ DPRINTF(port, "error: guest accessed unused port");
+ goto out;
+ }
+
+ debug_print_fis(cmd_fis, 0x90);
+ //debug_print_fis(cmd_fis, (opts & AHCI_CMD_HDR_CMD_FIS_LEN) * 4);
+
+ switch (cmd_fis[0]) {
+ case SATA_FIS_TYPE_REGISTER_H2D:
+ break;
+ default:
+ DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x "
+ "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1],
+ cmd_fis[2]);
+ goto out;
+ break;
+ }
+
+ switch (cmd_fis[1]) {
+ case SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER:
+ break;
+ case 0:
+ break;
+ default:
+ DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x "
+ "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1],
+ cmd_fis[2]);
+ goto out;
+ break;
+ }
+
+ switch (s->dev[port].port_state) {
+ case STATE_RUN:
+ if (cmd_fis[15] & ATA_SRST) {
+ s->dev[port].port_state = STATE_RESET;
+ }
+ break;
+ case STATE_RESET:
+ if (!(cmd_fis[15] & ATA_SRST)) {
+ ahci_reset_port(s, port);
+ }
+ break;
+ }
+
+ if (cmd_fis[1] == SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER) {
+
+ /* Check for NCQ command */
+ if ((cmd_fis[2] == READ_FPDMA_QUEUED) ||
+ (cmd_fis[2] == WRITE_FPDMA_QUEUED)) {
+ process_ncq_command(s, port, cmd_fis, slot);
+ goto out;
+ }
+
+ /* Decompose the FIS */
+ ide_state->nsector = (int64_t)((cmd_fis[13] << 8) | cmd_fis[12]);
+ ide_state->feature = cmd_fis[3];
+ if (!ide_state->nsector) {
+ ide_state->nsector = 256;
+ }
+
+ if (ide_state->drive_kind != IDE_CD) {
+ ide_set_sector(ide_state, (cmd_fis[6] << 16) | (cmd_fis[5] << 8) |
+ cmd_fis[4]);
+ }
+
+ /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command
+ * table to ide_state->io_buffer
+ */
+ if (opts & AHCI_CMD_ATAPI) {
+ memcpy(ide_state->io_buffer, &cmd_fis[AHCI_COMMAND_TABLE_ACMD], 0x10);
+ ide_state->lcyl = 0x14;
+ ide_state->hcyl = 0xeb;
+ debug_print_fis(ide_state->io_buffer, 0x10);
+ ide_state->feature = IDE_FEATURE_DMA;
+ s->dev[port].done_atapi_packet = 0;
+ /* XXX send PIO setup FIS */
+ }
+
+ ide_state->error = 0;
+
+ /* Reset transferred byte counter */
+ cmd->status = 0;
+
+ /* We're ready to process the command in FIS byte 2. */
+ ide_exec_cmd(&s->dev[port].port, cmd_fis[2]);
+
+ if (s->dev[port].port.ifs[0].status & READY_STAT) {
+ ahci_write_fis_d2h(&s->dev[port], cmd_fis);
+ }
+ }
+
+out:
+ cpu_physical_memory_unmap(cmd_fis, cmd_len, 1, cmd_len);
+
+ if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
+ /* async command, complete later */
+ s->dev[port].busy_slot = slot;
+ return -1;
+ }
+
+ /* done handling the command */
+ return 0;
+}
+
+/* DMA dev <-> ram */
+static int ahci_start_transfer(IDEDMA *dma)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+ IDEState *s = &ad->port.ifs[0];
+ uint32_t size = (uint32_t)(s->data_end - s->data_ptr);
+ /* write == ram -> device */
+ uint32_t opts = le32_to_cpu(ad->cur_cmd->opts);
+ int is_write = opts & AHCI_CMD_WRITE;
+ int is_atapi = opts & AHCI_CMD_ATAPI;
+ int has_sglist = 0;
+
+ if (is_atapi && !ad->done_atapi_packet) {
+ /* already prepopulated iobuffer */
+ ad->done_atapi_packet = 1;
+ goto out;
+ }
+
+ if (!ahci_populate_sglist(ad, &s->sg)) {
+ has_sglist = 1;
+ }
+
+ DPRINTF(ad->port_no, "%sing %d bytes on %s w/%s sglist\n",
+ is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata",
+ has_sglist ? "" : "o");
+
+ if (is_write && has_sglist && (s->data_ptr < s->data_end)) {
+ read_from_sglist(s->data_ptr, size, &s->sg);
+ }
+
+ if (!is_write && has_sglist && (s->data_ptr < s->data_end)) {
+ write_to_sglist(s->data_ptr, size, &s->sg);
+ }
+
+ /* update number of transferred bytes */
+ ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size);
+
+out:
+ /* declare that we processed everything */
+ s->data_ptr = s->data_end;
+
+ if (has_sglist) {
+ qemu_sglist_destroy(&s->sg);
+ }
+
+ s->end_transfer_func(s);
+
+ if (!(s->status & DRQ_STAT)) {
+ /* done with DMA */
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS);
+ }
+
+ return 0;
+}
+
+static void ahci_start_dma(IDEDMA *dma, IDEState *s,
+ BlockDriverCompletionFunc *dma_cb)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+
+ DPRINTF(ad->port_no, "\n");
+ ad->dma_cb = dma_cb;
+ ad->dma_status |= BM_STATUS_DMAING;
+ dma_cb(s, 0);
+}
+
+static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+ IDEState *s = &ad->port.ifs[0];
+ int i;
+
+ ahci_populate_sglist(ad, &s->sg);
+
+ s->io_buffer_size = 0;
+ for (i = 0; i < s->sg.nsg; i++) {
+ s->io_buffer_size += s->sg.sg[i].len;
+ }
+
+ DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size);
+ return s->io_buffer_size != 0;
+}
+
+static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+ IDEState *s = &ad->port.ifs[0];
+ uint8_t *p = s->io_buffer + s->io_buffer_index;
+ int l = s->io_buffer_size - s->io_buffer_index;
+
+ if (ahci_populate_sglist(ad, &s->sg)) {
+ return 0;
+ }
+
+ if (is_write) {
+ write_to_sglist(p, l, &s->sg);
+ } else {
+ read_from_sglist(p, l, &s->sg);
+ }
+
+ /* update number of transferred bytes */
+ ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l);
+ s->io_buffer_index += l;
+
+ DPRINTF(ad->port_no, "len=%#x\n", l);
+
+ return 1;
+}
+
+static int ahci_dma_set_unit(IDEDMA *dma, int unit)
+{
+ /* only a single unit per link */
+ return 0;
+}
+
+static int ahci_dma_add_status(IDEDMA *dma, int status)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+ ad->dma_status |= status;
+ DPRINTF(ad->port_no, "set status: %x\n", status);
+
+ if (status & BM_STATUS_INT) {
+ ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS);
+ }
+
+ return 0;
+}
+
+static int ahci_dma_set_inactive(IDEDMA *dma)
+{
+ AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
+
+ DPRINTF(ad->port_no, "dma done\n");
+
+ /* update d2h status */
+ ahci_write_fis_d2h(ad, NULL);
+
+ 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);
+
+ return 0;
+}
+
+static void ahci_irq_set(void *opaque, int n, int level)
+{
+}
+
+static void ahci_dma_restart_cb(void *opaque, int running, int reason)
+{
+}
+
+static int ahci_dma_reset(IDEDMA *dma)
+{
+ return 0;
+}
+
+static const IDEDMAOps ahci_dma_ops = {
+ .start_dma = ahci_start_dma,
+ .start_transfer = ahci_start_transfer,
+ .prepare_buf = ahci_dma_prepare_buf,
+ .rw_buf = ahci_dma_rw_buf,
+ .set_unit = ahci_dma_set_unit,
+ .add_status = ahci_dma_add_status,
+ .set_inactive = ahci_dma_set_inactive,
+ .restart_cb = ahci_dma_restart_cb,
+ .reset = ahci_dma_reset,
+};
+
+void ahci_init(AHCIState *s, DeviceState *qdev, int ports)
+{
+ qemu_irq *irqs;
+ int i;
+
+ s->ports = ports;
+ s->dev = qemu_mallocz(sizeof(AHCIDevice) * ports);
+ ahci_reg_init(s);
+ s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s,
+ DEVICE_LITTLE_ENDIAN);
+ irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports);
+
+ for (i = 0; i < s->ports; i++) {
+ AHCIDevice *ad = &s->dev[i];
+
+ ide_bus_new(&ad->port, qdev, i);
+ ide_init2(&ad->port, irqs[i]);
+
+ ad->hba = s;
+ ad->port_no = i;
+ ad->port.dma = &ad->dma;
+ ad->port.dma->ops = &ahci_dma_ops;
+ ad->port_regs.cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON;
+ }
+}
+
+void ahci_uninit(AHCIState *s)
+{
+ qemu_free(s->dev);
+}
+
+void ahci_pci_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ struct AHCIPCIState *d = (struct AHCIPCIState *)pci_dev;
+ AHCIState *s = &d->ahci;
+
+ cpu_register_physical_memory(addr, size, s->mem);
+}
+
+void ahci_reset(void *opaque)
+{
+ struct AHCIPCIState *d = opaque;
+ int i;
+
+ d->ahci.control_regs.irqstatus = 0;
+ d->ahci.control_regs.ghc = 0;
+
+ for (i = 0; i < d->ahci.ports; i++) {
+ ahci_reset_port(&d->ahci, i);
+ }
+}
diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h
new file mode 100644
index 0000000..a4560c4
--- /dev/null
+++ b/hw/ide/ahci.h
@@ -0,0 +1,333 @@
+/*
+ * QEMU AHCI Emulation
+ *
+ * Copyright (c) 2010 qiaochong@loongson.cn
+ * Copyright (c) 2010 Roland Elek <elek.roland@gmail.com>
+ * Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
+ * Copyright (c) 2010 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef HW_IDE_AHCI_H
+#define HW_IDE_AHCI_H
+
+#define AHCI_PCI_BAR 5
+#define AHCI_MAX_PORTS 32
+#define AHCI_MAX_SG 168 /* hardware max is 64K */
+#define AHCI_DMA_BOUNDARY 0xffffffff
+#define AHCI_USE_CLUSTERING 0
+#define AHCI_MAX_CMDS 32
+#define AHCI_CMD_SZ 32
+#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ)
+#define AHCI_RX_FIS_SZ 256
+#define AHCI_CMD_TBL_CDB 0x40
+#define AHCI_CMD_TBL_HDR_SZ 0x80
+#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16))
+#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS)
+#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \
+ AHCI_RX_FIS_SZ)
+
+#define AHCI_IRQ_ON_SG (1 << 31)
+#define AHCI_CMD_ATAPI (1 << 5)
+#define AHCI_CMD_WRITE (1 << 6)
+#define AHCI_CMD_PREFETCH (1 << 7)
+#define AHCI_CMD_RESET (1 << 8)
+#define AHCI_CMD_CLR_BUSY (1 << 10)
+
+#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */
+#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */
+#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */
+
+/* global controller registers */
+#define HOST_CAP 0x00 /* host capabilities */
+#define HOST_CTL 0x04 /* global host control */
+#define HOST_IRQ_STAT 0x08 /* interrupt status */
+#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */
+#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */
+
+/* HOST_CTL bits */
+#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */
+#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */
+#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */
+
+/* HOST_CAP bits */
+#define HOST_CAP_SSC (1 << 14) /* Slumber capable */
+#define HOST_CAP_AHCI (1 << 18) /* AHCI only */
+#define HOST_CAP_CLO (1 << 24) /* Command List Override support */
+#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */
+#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */
+#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */
+
+/* registers for each SATA port */
+#define PORT_LST_ADDR 0x00 /* command list DMA addr */
+#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */
+#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */
+#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */
+#define PORT_IRQ_STAT 0x10 /* interrupt status */
+#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */
+#define PORT_CMD 0x18 /* port command */
+#define PORT_TFDATA 0x20 /* taskfile data */
+#define PORT_SIG 0x24 /* device TF signature */
+#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */
+#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */
+#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */
+#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */
+#define PORT_CMD_ISSUE 0x38 /* command issue */
+#define PORT_RESERVED 0x3c /* reserved */
+
+/* PORT_IRQ_{STAT,MASK} bits */
+#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */
+#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */
+#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */
+#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */
+#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */
+#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */
+#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */
+#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */
+
+#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */
+#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */
+#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */
+#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */
+#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */
+#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */
+#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */
+#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */
+#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */
+
+#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \
+ PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \
+ PORT_IRQ_UNK_FIS)
+#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \
+ PORT_IRQ_HBUS_DATA_ERR)
+#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \
+ PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \
+ PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS)
+
+/* PORT_CMD bits */
+#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */
+#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */
+#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */
+#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */
+#define PORT_CMD_CLO (1 << 3) /* Command list override */
+#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */
+#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */
+#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */
+
+#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */
+#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */
+#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */
+#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */
+
+#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */
+#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */
+#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */
+#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */
+#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */
+#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */
+#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */
+#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence
+ Status */
+#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */
+#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier
+ Status */
+#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */
+#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error
+ Status */
+#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */
+#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */
+#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */
+#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */
+#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */
+
+/* ap->flags bits */
+#define AHCI_FLAG_NO_NCQ (1 << 24)
+#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */
+#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */
+#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */
+#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */
+
+#define ATA_SRST (1 << 2) /* software reset */
+
+#define STATE_RUN 0
+#define STATE_RESET 1
+
+#define SATA_SCR_SSTATUS_DET_NODEV 0x0
+#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3
+
+#define SATA_SCR_SSTATUS_SPD_NODEV 0x00
+#define SATA_SCR_SSTATUS_SPD_GEN1 0x10
+
+#define SATA_SCR_SSTATUS_IPM_NODEV 0x000
+#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100
+
+#define AHCI_SCR_SCTL_DET 0xf
+
+#define SATA_FIS_TYPE_REGISTER_H2D 0x27
+#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80
+
+#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f
+#define AHCI_CMD_HDR_PRDT_LEN 16
+
+#define SATA_SIGNATURE_CDROM 0xeb140000
+#define SATA_SIGNATURE_DISK 0x00000101
+
+#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20
+ /* Shouldn't this be 0x2c? */
+
+#define AHCI_PORT_REGS_START_ADDR 0x100
+#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f
+#define AHCI_PORT_ADDR_OFFSET_LEN 0x80
+
+#define AHCI_NUM_COMMAND_SLOTS 31
+#define AHCI_SUPPORTED_SPEED 20
+#define AHCI_SUPPORTED_SPEED_GEN1 1
+#define AHCI_VERSION_1_0 0x10000
+
+#define AHCI_PROGMODE_MAJOR_REV_1 1
+
+#define AHCI_COMMAND_TABLE_ACMD 0x40
+
+#define IDE_FEATURE_DMA 1
+
+#define READ_FPDMA_QUEUED 0x60
+#define WRITE_FPDMA_QUEUED 0x61
+
+#define RES_FIS_DSFIS 0x00
+#define RES_FIS_PSFIS 0x20
+#define RES_FIS_RFIS 0x40
+#define RES_FIS_SDBFIS 0x58
+#define RES_FIS_UFIS 0x60
+
+typedef struct AHCIControlRegs {
+ uint32_t cap;
+ uint32_t ghc;
+ uint32_t irqstatus;
+ uint32_t impl;
+ uint32_t version;
+} AHCIControlRegs;
+
+typedef struct AHCIPortRegs {
+ uint32_t lst_addr;
+ uint32_t lst_addr_hi;
+ uint32_t fis_addr;
+ uint32_t fis_addr_hi;
+ uint32_t irq_stat;
+ uint32_t irq_mask;
+ uint32_t cmd;
+ uint32_t unused0;
+ uint32_t tfdata;
+ uint32_t sig;
+ uint32_t scr_stat;
+ uint32_t scr_ctl;
+ uint32_t scr_err;
+ uint32_t scr_act;
+ uint32_t cmd_issue;
+ uint32_t reserved;
+} AHCIPortRegs;
+
+typedef struct AHCICmdHdr {
+ uint32_t opts;
+ uint32_t status;
+ uint64_t tbl_addr;
+ uint32_t reserved[4];
+} __attribute__ ((packed)) AHCICmdHdr;
+
+typedef struct AHCI_SG {
+ uint64_t addr;
+ uint32_t reserved;
+ uint32_t flags_size;
+} __attribute__ ((packed)) AHCI_SG;
+
+typedef struct AHCIDevice AHCIDevice;
+
+typedef struct NCQTransferState {
+ AHCIDevice *drive;
+ BlockDriverAIOCB *aiocb;
+ QEMUSGList sglist;
+ int is_read;
+ uint16_t sector_count;
+ uint64_t lba;
+ uint8_t tag;
+ int slot;
+ int used;
+} NCQTransferState;
+
+struct AHCIDevice {
+ IDEDMA dma;
+ IDEBus port;
+ int port_no;
+ uint32_t port_state;
+ uint32_t finished;
+ AHCIPortRegs port_regs;
+ struct AHCIState *hba;
+ QEMUBH *check_bh;
+ uint8_t *lst;
+ uint8_t *res_fis;
+ int dma_status;
+ int done_atapi_packet;
+ int busy_slot;
+ int init_d2h_sent;
+ BlockDriverCompletionFunc *dma_cb;
+ AHCICmdHdr *cur_cmd;
+ NCQTransferState ncq_tfs[AHCI_MAX_CMDS];
+};
+
+typedef struct AHCIState {
+ AHCIDevice *dev;
+ AHCIControlRegs control_regs;
+ int mem;
+ int ports;
+ qemu_irq irq;
+} AHCIState;
+
+typedef struct AHCIPCIState {
+ PCIDevice card;
+ AHCIState ahci;
+} AHCIPCIState;
+
+typedef struct NCQFrame {
+ uint8_t fis_type;
+ uint8_t c;
+ uint8_t command;
+ uint8_t sector_count_low;
+ uint8_t lba0;
+ uint8_t lba1;
+ uint8_t lba2;
+ uint8_t fua;
+ uint8_t lba3;
+ uint8_t lba4;
+ uint8_t lba5;
+ uint8_t sector_count_high;
+ uint8_t tag;
+ uint8_t reserved5;
+ uint8_t reserved6;
+ uint8_t control;
+ uint8_t reserved7;
+ uint8_t reserved8;
+ uint8_t reserved9;
+ uint8_t reserved10;
+} __attribute__ ((packed)) NCQFrame;
+
+void ahci_init(AHCIState *s, DeviceState *qdev, int ports);
+void ahci_uninit(AHCIState *s);
+
+void ahci_pci_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type);
+
+void ahci_reset(void *opaque);
+
+#endif /* HW_IDE_AHCI_H */
diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c
new file mode 100644
index 0000000..5d5464a
--- /dev/null
+++ b/hw/ide/cmd646.c
@@ -0,0 +1,298 @@
+/*
+ * QEMU IDE Emulation: PCI cmd646 support.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+#include <hw/isa.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/pci.h>
+
+/* CMD646 specific */
+#define MRDMODE 0x71
+#define MRDMODE_INTR_CH0 0x04
+#define MRDMODE_INTR_CH1 0x08
+#define MRDMODE_BLK_CH0 0x10
+#define MRDMODE_BLK_CH1 0x20
+#define UDIDETCR0 0x73
+#define UDIDETCR1 0x7B
+
+static void cmd646_update_irq(PCIIDEState *d);
+
+static void ide_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, pci_dev);
+ IDEBus *bus;
+
+ if (region_num <= 3) {
+ bus = &d->bus[(region_num >> 1)];
+ if (region_num & 1) {
+ register_ioport_read(addr + 2, 1, 1, ide_status_read, bus);
+ register_ioport_write(addr + 2, 1, 1, ide_cmd_write, bus);
+ } else {
+ register_ioport_write(addr, 8, 1, ide_ioport_write, bus);
+ register_ioport_read(addr, 8, 1, ide_ioport_read, bus);
+
+ /* data ports */
+ register_ioport_write(addr, 2, 2, ide_data_writew, bus);
+ register_ioport_read(addr, 2, 2, ide_data_readw, bus);
+ register_ioport_write(addr, 4, 4, ide_data_writel, bus);
+ register_ioport_read(addr, 4, 4, ide_data_readl, bus);
+ }
+ }
+}
+
+static uint32_t bmdma_readb_common(PCIIDEState *pci_dev, BMDMAState *bm,
+ uint32_t addr)
+{
+ uint32_t val;
+
+ switch(addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 1:
+ val = pci_dev->dev.config[MRDMODE];
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ case 3:
+ if (bm == &pci_dev->bmdma[0]) {
+ val = pci_dev->dev.config[UDIDETCR0];
+ } else {
+ val = pci_dev->dev.config[UDIDETCR1];
+ }
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static uint32_t bmdma_readb_0(void *opaque, uint32_t addr)
+{
+ PCIIDEState *pci_dev = opaque;
+ BMDMAState *bm = &pci_dev->bmdma[0];
+
+ return bmdma_readb_common(pci_dev, bm, addr);
+}
+
+static uint32_t bmdma_readb_1(void *opaque, uint32_t addr)
+{
+ PCIIDEState *pci_dev = opaque;
+ BMDMAState *bm = &pci_dev->bmdma[1];
+
+ return bmdma_readb_common(pci_dev, bm, addr);
+}
+
+static void bmdma_writeb_common(PCIIDEState *pci_dev, BMDMAState *bm,
+ uint32_t addr, uint32_t val)
+{
+#ifdef DEBUG_IDE
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ switch(addr & 3) {
+ case 0:
+ bmdma_cmd_writeb(bm, addr, val);
+ break;
+ case 1:
+ pci_dev->dev.config[MRDMODE] =
+ (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30);
+ cmd646_update_irq(pci_dev);
+ break;
+ case 2:
+ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ break;
+ case 3:
+ if (bm == &pci_dev->bmdma[0])
+ pci_dev->dev.config[UDIDETCR0] = val;
+ else
+ pci_dev->dev.config[UDIDETCR1] = val;
+ break;
+ }
+}
+
+static void bmdma_writeb_0(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCIIDEState *pci_dev = opaque;
+ BMDMAState *bm = &pci_dev->bmdma[0];
+
+ bmdma_writeb_common(pci_dev, bm, addr, val);
+}
+
+static void bmdma_writeb_1(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCIIDEState *pci_dev = opaque;
+ BMDMAState *bm = &pci_dev->bmdma[1];
+
+ bmdma_writeb_common(pci_dev, bm, addr, val);
+}
+
+static void bmdma_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, pci_dev);
+ int i;
+
+ for(i = 0;i < 2; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+
+ if (i == 0) {
+ register_ioport_write(addr, 4, 1, bmdma_writeb_0, d);
+ register_ioport_read(addr, 4, 1, bmdma_readb_0, d);
+ } else {
+ register_ioport_write(addr, 4, 1, bmdma_writeb_1, d);
+ register_ioport_read(addr, 4, 1, bmdma_readb_1, d);
+ }
+
+ iorange_init(&bm->addr_ioport, &bmdma_addr_ioport_ops, addr + 4, 4);
+ ioport_register(&bm->addr_ioport);
+ addr += 8;
+ }
+}
+
+/* XXX: call it also when the MRDMODE is changed from the PCI config
+ registers */
+static void cmd646_update_irq(PCIIDEState *d)
+{
+ int pci_level;
+ pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) ||
+ ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1));
+ qemu_set_irq(d->dev.irq[0], pci_level);
+}
+
+/* the PCI irq level is the logical OR of the two channels */
+static void cmd646_set_irq(void *opaque, int channel, int level)
+{
+ PCIIDEState *d = opaque;
+ int irq_mask;
+
+ irq_mask = MRDMODE_INTR_CH0 << channel;
+ if (level)
+ d->dev.config[MRDMODE] |= irq_mask;
+ else
+ d->dev.config[MRDMODE] &= ~irq_mask;
+ cmd646_update_irq(d);
+}
+
+static void cmd646_reset(void *opaque)
+{
+ PCIIDEState *d = opaque;
+ unsigned int i;
+
+ for (i = 0; i < 2; i++) {
+ ide_bus_reset(&d->bus[i]);
+ }
+}
+
+/* CMD646 PCI IDE controller */
+static int pci_cmd646_ide_initfn(PCIDevice *dev)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
+ uint8_t *pci_conf = d->dev.config;
+ qemu_irq *irq;
+ int i;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_CMD);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_CMD_646);
+
+ pci_conf[PCI_REVISION_ID] = 0x07; // IDE controller revision
+ pci_conf[PCI_CLASS_PROG] = 0x8f;
+
+ pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
+
+ pci_conf[0x51] = 0x04; // enable IDE0
+ if (d->secondary) {
+ /* XXX: if not enabled, really disable the seconday IDE controller */
+ pci_conf[0x51] |= 0x08; /* enable IDE1 */
+ }
+
+ pci_register_bar(dev, 0, 0x8, PCI_BASE_ADDRESS_SPACE_IO, ide_map);
+ pci_register_bar(dev, 1, 0x4, PCI_BASE_ADDRESS_SPACE_IO, ide_map);
+ pci_register_bar(dev, 2, 0x8, PCI_BASE_ADDRESS_SPACE_IO, ide_map);
+ pci_register_bar(dev, 3, 0x4, PCI_BASE_ADDRESS_SPACE_IO, ide_map);
+ pci_register_bar(dev, 4, 0x10, PCI_BASE_ADDRESS_SPACE_IO, bmdma_map);
+
+ /* TODO: RST# value should be 0 */
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1
+
+ irq = qemu_allocate_irqs(cmd646_set_irq, d, 2);
+ for (i = 0; i < 2; i++) {
+ ide_bus_new(&d->bus[i], &d->dev.qdev, i);
+ ide_init2(&d->bus[i], irq[i]);
+
+ bmdma_init(&d->bus[i], &d->bmdma[i]);
+ d->bmdma[i].bus = &d->bus[i];
+ qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
+ &d->bmdma[i].dma);
+ }
+
+ vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d);
+ qemu_register_reset(cmd646_reset, d);
+ return 0;
+}
+
+void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
+ int secondary_ide_enabled)
+{
+ PCIDevice *dev;
+
+ dev = pci_create(bus, -1, "cmd646-ide");
+ qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled);
+ qdev_init_nofail(&dev->qdev);
+
+ pci_ide_create_devs(dev, hd_table);
+}
+
+static PCIDeviceInfo cmd646_ide_info[] = {
+ {
+ .qdev.name = "cmd646-ide",
+ .qdev.size = sizeof(PCIIDEState),
+ .init = pci_cmd646_ide_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ },{
+ /* end of list */
+ }
+};
+
+static void cmd646_ide_register(void)
+{
+ pci_qdev_register_many(cmd646_ide_info);
+}
+device_init(cmd646_ide_register);
diff --git a/hw/ide/core.c b/hw/ide/core.c
new file mode 100644
index 0000000..dd63664
--- /dev/null
+++ b/hw/ide/core.c
@@ -0,0 +1,2813 @@
+/*
+ * QEMU IDE disk and CD/DVD-ROM Emulator
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+#include <hw/scsi.h>
+#include "qemu-error.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "dma.h"
+#include "blockdev.h"
+
+#include <hw/ide/internal.h>
+
+static const int smart_attributes[][5] = {
+ /* id, flags, val, wrst, thrsh */
+ { 0x01, 0x03, 0x64, 0x64, 0x06}, /* raw read */
+ { 0x03, 0x03, 0x64, 0x64, 0x46}, /* spin up */
+ { 0x04, 0x02, 0x64, 0x64, 0x14}, /* start stop count */
+ { 0x05, 0x03, 0x64, 0x64, 0x36}, /* remapped sectors */
+ { 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+/* XXX: DVDs that could fit on a CD will be reported as a CD */
+static inline int media_present(IDEState *s)
+{
+ return (s->nb_sectors > 0);
+}
+
+static inline int media_is_dvd(IDEState *s)
+{
+ return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS);
+}
+
+static inline int media_is_cd(IDEState *s)
+{
+ return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS);
+}
+
+static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret);
+static int ide_handle_rw_error(IDEState *s, int error, int op);
+
+static void padstr(char *str, const char *src, int len)
+{
+ int i, v;
+ for(i = 0; i < len; i++) {
+ if (*src)
+ v = *src++;
+ else
+ v = ' ';
+ str[i^1] = v;
+ }
+}
+
+static void padstr8(uint8_t *buf, int buf_size, const char *src)
+{
+ int i;
+ for(i = 0; i < buf_size; i++) {
+ if (*src)
+ buf[i] = *src++;
+ else
+ buf[i] = ' ';
+ }
+}
+
+static void put_le16(uint16_t *p, unsigned int v)
+{
+ *p = cpu_to_le16(v);
+}
+
+static void ide_identify(IDEState *s)
+{
+ uint16_t *p;
+ unsigned int oldsize;
+ IDEDevice *dev;
+
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
+ memset(s->io_buffer, 0, 512);
+ p = (uint16_t *)s->io_buffer;
+ put_le16(p + 0, 0x0040);
+ put_le16(p + 1, s->cylinders);
+ put_le16(p + 3, s->heads);
+ put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
+ put_le16(p + 5, 512); /* XXX: retired, remove ? */
+ put_le16(p + 6, s->sectors);
+ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
+ put_le16(p + 20, 3); /* XXX: retired, remove ? */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
+ padstr((char *)(p + 23), s->version, 8); /* firmware version */
+ padstr((char *)(p + 27), "QEMU HARDDISK", 40); /* model */
+#if MAX_MULT_SECTORS > 1
+ put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
+#endif
+ put_le16(p + 48, 1); /* dword I/O */
+ put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
+ put_le16(p + 51, 0x200); /* PIO transfer cycle */
+ put_le16(p + 52, 0x200); /* DMA transfer cycle */
+ put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */
+ put_le16(p + 54, s->cylinders);
+ put_le16(p + 55, s->heads);
+ put_le16(p + 56, s->sectors);
+ oldsize = s->cylinders * s->heads * s->sectors;
+ put_le16(p + 57, oldsize);
+ put_le16(p + 58, oldsize >> 16);
+ if (s->mult_sectors)
+ put_le16(p + 59, 0x100 | s->mult_sectors);
+ put_le16(p + 60, s->nb_sectors);
+ put_le16(p + 61, s->nb_sectors >> 16);
+ put_le16(p + 62, 0x07); /* single word dma0-2 supported */
+ put_le16(p + 63, 0x07); /* mdma0-2 supported */
+ put_le16(p + 64, 0x03); /* pio3-4 supported */
+ put_le16(p + 65, 120);
+ put_le16(p + 66, 120);
+ put_le16(p + 67, 120);
+ put_le16(p + 68, 120);
+
+ if (s->ncq_queues) {
+ put_le16(p + 75, s->ncq_queues - 1);
+ /* NCQ supported */
+ put_le16(p + 76, (1 << 8));
+ }
+
+ put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+ put_le16(p + 81, 0x16); /* conforms to ata5 */
+ /* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */
+ put_le16(p + 82, (1 << 14) | (1 << 5) | 1);
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ /* 14=set to 1, 1=SMART self test, 0=SMART error logging */
+ put_le16(p + 84, (1 << 14) | 0);
+ /* 14 = NOP supported, 5=WCACHE enabled, 0=SMART feature set enabled */
+ if (bdrv_enable_write_cache(s->bs))
+ put_le16(p + 85, (1 << 14) | (1 << 5) | 1);
+ else
+ put_le16(p + 85, (1 << 14) | 1);
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ /* 14=set to 1, 1=smart self test, 0=smart error logging */
+ put_le16(p + 87, (1 << 14) | 0);
+ put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+ put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+ put_le16(p + 100, s->nb_sectors);
+ put_le16(p + 101, s->nb_sectors >> 16);
+ put_le16(p + 102, s->nb_sectors >> 32);
+ put_le16(p + 103, s->nb_sectors >> 48);
+ dev = s->unit ? s->bus->slave : s->bus->master;
+ if (dev && dev->conf.physical_block_size)
+ put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf));
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
+}
+
+static void ide_atapi_identify(IDEState *s)
+{
+ uint16_t *p;
+
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
+ memset(s->io_buffer, 0, 512);
+ p = (uint16_t *)s->io_buffer;
+ /* Removable CDROM, 50us response, 12 byte packets */
+ put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
+ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
+ put_le16(p + 20, 3); /* buffer type */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
+ padstr((char *)(p + 23), s->version, 8); /* firmware version */
+ padstr((char *)(p + 27), "QEMU DVD-ROM", 40); /* model */
+ put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
+#ifdef USE_DMA_CDROM
+ put_le16(p + 49, 1 << 9 | 1 << 8); /* DMA and LBA supported */
+ put_le16(p + 53, 7); /* words 64-70, 54-58, 88 valid */
+ put_le16(p + 62, 7); /* single word dma0-2 supported */
+ put_le16(p + 63, 7); /* mdma0-2 supported */
+#else
+ put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
+ put_le16(p + 53, 3); /* words 64-70, 54-58 valid */
+ put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
+#endif
+ put_le16(p + 64, 3); /* pio3-4 supported */
+ put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
+ put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
+ put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
+ put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
+
+ put_le16(p + 71, 30); /* in ns */
+ put_le16(p + 72, 30); /* in ns */
+
+ if (s->ncq_queues) {
+ put_le16(p + 75, s->ncq_queues - 1);
+ /* NCQ supported */
+ put_le16(p + 76, (1 << 8));
+ }
+
+ put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+#ifdef USE_DMA_CDROM
+ put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+#endif
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
+}
+
+static void ide_cfata_identify(IDEState *s)
+{
+ uint16_t *p;
+ uint32_t cur_sec;
+
+ p = (uint16_t *) s->identify_data;
+ if (s->identify_set)
+ goto fill_buffer;
+
+ memset(p, 0, sizeof(s->identify_data));
+
+ cur_sec = s->cylinders * s->heads * s->sectors;
+
+ put_le16(p + 0, 0x848a); /* CF Storage Card signature */
+ put_le16(p + 1, s->cylinders); /* Default cylinders */
+ put_le16(p + 3, s->heads); /* Default heads */
+ put_le16(p + 6, s->sectors); /* Default sectors per track */
+ put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */
+ put_le16(p + 8, s->nb_sectors); /* Sectors per card */
+ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */
+ put_le16(p + 22, 0x0004); /* ECC bytes */
+ padstr((char *) (p + 23), s->version, 8); /* Firmware Revision */
+ padstr((char *) (p + 27), "QEMU MICRODRIVE", 40);/* Model number */
+#if MAX_MULT_SECTORS > 1
+ put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
+#else
+ put_le16(p + 47, 0x0000);
+#endif
+ put_le16(p + 49, 0x0f00); /* Capabilities */
+ put_le16(p + 51, 0x0002); /* PIO cycle timing mode */
+ put_le16(p + 52, 0x0001); /* DMA cycle timing mode */
+ put_le16(p + 53, 0x0003); /* Translation params valid */
+ put_le16(p + 54, s->cylinders); /* Current cylinders */
+ put_le16(p + 55, s->heads); /* Current heads */
+ put_le16(p + 56, s->sectors); /* Current sectors */
+ put_le16(p + 57, cur_sec); /* Current capacity */
+ put_le16(p + 58, cur_sec >> 16); /* Current capacity */
+ if (s->mult_sectors) /* Multiple sector setting */
+ put_le16(p + 59, 0x100 | s->mult_sectors);
+ put_le16(p + 60, s->nb_sectors); /* Total LBA sectors */
+ put_le16(p + 61, s->nb_sectors >> 16); /* Total LBA sectors */
+ put_le16(p + 63, 0x0203); /* Multiword DMA capability */
+ put_le16(p + 64, 0x0001); /* Flow Control PIO support */
+ put_le16(p + 65, 0x0096); /* Min. Multiword DMA cycle */
+ put_le16(p + 66, 0x0096); /* Rec. Multiword DMA cycle */
+ put_le16(p + 68, 0x00b4); /* Min. PIO cycle time */
+ put_le16(p + 82, 0x400c); /* Command Set supported */
+ put_le16(p + 83, 0x7068); /* Command Set supported */
+ put_le16(p + 84, 0x4000); /* Features supported */
+ put_le16(p + 85, 0x000c); /* Command Set enabled */
+ put_le16(p + 86, 0x7044); /* Command Set enabled */
+ put_le16(p + 87, 0x4000); /* Features enabled */
+ put_le16(p + 91, 0x4060); /* Current APM level */
+ put_le16(p + 129, 0x0002); /* Current features option */
+ put_le16(p + 130, 0x0005); /* Reassigned sectors */
+ put_le16(p + 131, 0x0001); /* Initial power mode */
+ put_le16(p + 132, 0x0000); /* User signature */
+ put_le16(p + 160, 0x8100); /* Power requirement */
+ put_le16(p + 161, 0x8001); /* CF command set */
+
+ s->identify_set = 1;
+
+fill_buffer:
+ memcpy(s->io_buffer, p, sizeof(s->identify_data));
+}
+
+static void ide_set_signature(IDEState *s)
+{
+ s->select &= 0xf0; /* clear head */
+ /* put signature */
+ s->nsector = 1;
+ s->sector = 1;
+ if (s->drive_kind == IDE_CD) {
+ s->lcyl = 0x14;
+ s->hcyl = 0xeb;
+ } else if (s->bs) {
+ s->lcyl = 0;
+ s->hcyl = 0;
+ } else {
+ s->lcyl = 0xff;
+ s->hcyl = 0xff;
+ }
+}
+
+static inline void ide_abort_command(IDEState *s)
+{
+ s->status = READY_STAT | ERR_STAT;
+ s->error = ABRT_ERR;
+}
+
+/* prepare data transfer and tell what to do after */
+static void ide_transfer_start(IDEState *s, uint8_t *buf, int size,
+ EndTransferFunc *end_transfer_func)
+{
+ s->end_transfer_func = end_transfer_func;
+ s->data_ptr = buf;
+ s->data_end = buf + size;
+ if (!(s->status & ERR_STAT)) {
+ s->status |= DRQ_STAT;
+ }
+ s->bus->dma->ops->start_transfer(s->bus->dma);
+}
+
+static void ide_transfer_stop(IDEState *s)
+{
+ s->end_transfer_func = ide_transfer_stop;
+ s->data_ptr = s->io_buffer;
+ s->data_end = s->io_buffer;
+ s->status &= ~DRQ_STAT;
+}
+
+int64_t ide_get_sector(IDEState *s)
+{
+ int64_t sector_num;
+ if (s->select & 0x40) {
+ /* lba */
+ if (!s->lba48) {
+ sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+ (s->lcyl << 8) | s->sector;
+ } else {
+ sector_num = ((int64_t)s->hob_hcyl << 40) |
+ ((int64_t) s->hob_lcyl << 32) |
+ ((int64_t) s->hob_sector << 24) |
+ ((int64_t) s->hcyl << 16) |
+ ((int64_t) s->lcyl << 8) | s->sector;
+ }
+ } else {
+ sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
+ (s->select & 0x0f) * s->sectors + (s->sector - 1);
+ }
+ return sector_num;
+}
+
+void ide_set_sector(IDEState *s, int64_t sector_num)
+{
+ unsigned int cyl, r;
+ if (s->select & 0x40) {
+ if (!s->lba48) {
+ s->select = (s->select & 0xf0) | (sector_num >> 24);
+ s->hcyl = (sector_num >> 16);
+ s->lcyl = (sector_num >> 8);
+ s->sector = (sector_num);
+ } else {
+ s->sector = sector_num;
+ s->lcyl = sector_num >> 8;
+ s->hcyl = sector_num >> 16;
+ s->hob_sector = sector_num >> 24;
+ s->hob_lcyl = sector_num >> 32;
+ s->hob_hcyl = sector_num >> 40;
+ }
+ } else {
+ cyl = sector_num / (s->heads * s->sectors);
+ r = sector_num % (s->heads * s->sectors);
+ s->hcyl = cyl >> 8;
+ s->lcyl = cyl;
+ s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f);
+ s->sector = (r % s->sectors) + 1;
+ }
+}
+
+static void ide_rw_error(IDEState *s) {
+ ide_abort_command(s);
+ ide_set_irq(s->bus);
+}
+
+void ide_sector_read(IDEState *s)
+{
+ int64_t sector_num;
+ int ret, n;
+
+ s->status = READY_STAT | SEEK_STAT;
+ s->error = 0; /* not needed by IDE spec, but needed by Windows */
+ sector_num = ide_get_sector(s);
+ n = s->nsector;
+ if (n == 0) {
+ /* no more sector to read from disk */
+ ide_transfer_stop(s);
+ } else {
+#if defined(DEBUG_IDE)
+ printf("read sector=%" PRId64 "\n", sector_num);
+#endif
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ ret = bdrv_read(s->bs, sector_num, s->io_buffer, n);
+ if (ret != 0) {
+ if (ide_handle_rw_error(s, -ret,
+ BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ))
+ {
+ return;
+ }
+ }
+ ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read);
+ ide_set_irq(s->bus);
+ ide_set_sector(s, sector_num + n);
+ s->nsector -= n;
+ }
+}
+
+static void dma_buf_commit(IDEState *s, int is_write)
+{
+ qemu_sglist_destroy(&s->sg);
+}
+
+static void ide_set_inactive(IDEState *s)
+{
+ s->bus->dma->aiocb = NULL;
+ s->bus->dma->ops->set_inactive(s->bus->dma);
+}
+
+void ide_dma_error(IDEState *s)
+{
+ ide_transfer_stop(s);
+ s->error = ABRT_ERR;
+ s->status = READY_STAT | ERR_STAT;
+ ide_set_inactive(s);
+ s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
+ ide_set_irq(s->bus);
+}
+
+static int ide_handle_rw_error(IDEState *s, int error, int op)
+{
+ int is_read = (op & BM_STATUS_RETRY_READ);
+ BlockErrorAction action = bdrv_get_on_error(s->bs, is_read);
+
+ if (action == BLOCK_ERR_IGNORE) {
+ bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ return 0;
+ }
+
+ if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
+ || action == BLOCK_ERR_STOP_ANY) {
+ s->bus->dma->ops->set_unit(s->bus->dma, s->unit);
+ s->bus->dma->ops->add_status(s->bus->dma, op);
+ bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ vm_stop(0);
+ } else {
+ if (op & BM_STATUS_DMA_RETRY) {
+ dma_buf_commit(s, 0);
+ ide_dma_error(s);
+ } else {
+ ide_rw_error(s);
+ }
+ bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ }
+
+ return 1;
+}
+
+void ide_dma_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+ int n;
+ int64_t sector_num;
+
+handle_rw_error:
+ if (ret < 0) {
+ int op = BM_STATUS_DMA_RETRY;
+
+ if (s->is_read)
+ op |= BM_STATUS_RETRY_READ;
+ if (ide_handle_rw_error(s, -ret, op)) {
+ return;
+ }
+ }
+
+ n = s->io_buffer_size >> 9;
+ sector_num = ide_get_sector(s);
+ if (n > 0) {
+ dma_buf_commit(s, s->is_read);
+ sector_num += n;
+ ide_set_sector(s, sector_num);
+ s->nsector -= n;
+ }
+
+ /* end of transfer ? */
+ if (s->nsector == 0) {
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ goto eot;
+ }
+
+ /* launch next transfer */
+ n = s->nsector;
+ s->io_buffer_index = 0;
+ s->io_buffer_size = n * 512;
+ if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0)
+ goto eot;
+
+#ifdef DEBUG_AIO
+ printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, is_read=%d\n",
+ sector_num, n, s->is_read);
+#endif
+
+ if (s->is_read) {
+ s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num,
+ ide_dma_cb, s);
+ } else {
+ s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num,
+ ide_dma_cb, s);
+ }
+
+ if (!s->bus->dma->aiocb) {
+ ret = -1;
+ goto handle_rw_error;
+ }
+ return;
+
+eot:
+ s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
+ ide_set_inactive(s);
+}
+
+static void ide_sector_start_dma(IDEState *s, int is_read)
+{
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
+ s->io_buffer_index = 0;
+ s->io_buffer_size = 0;
+ s->is_read = is_read;
+ s->bus->dma->ops->start_dma(s->bus->dma, s, ide_dma_cb);
+}
+
+static void ide_sector_write_timer_cb(void *opaque)
+{
+ IDEState *s = opaque;
+ ide_set_irq(s->bus);
+}
+
+void ide_sector_write(IDEState *s)
+{
+ int64_t sector_num;
+ int ret, n, n1;
+
+ s->status = READY_STAT | SEEK_STAT;
+ sector_num = ide_get_sector(s);
+#if defined(DEBUG_IDE)
+ printf("write sector=%" PRId64 "\n", sector_num);
+#endif
+ n = s->nsector;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ ret = bdrv_write(s->bs, sector_num, s->io_buffer, n);
+
+ if (ret != 0) {
+ if (ide_handle_rw_error(s, -ret, BM_STATUS_PIO_RETRY))
+ return;
+ }
+
+ s->nsector -= n;
+ if (s->nsector == 0) {
+ /* no more sectors to write */
+ ide_transfer_stop(s);
+ } else {
+ n1 = s->nsector;
+ if (n1 > s->req_nb_sectors)
+ n1 = s->req_nb_sectors;
+ ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write);
+ }
+ ide_set_sector(s, sector_num + n);
+
+ if (win2k_install_hack && ((++s->irq_count % 16) == 0)) {
+ /* It seems there is a bug in the Windows 2000 installer HDD
+ IDE driver which fills the disk with empty logs when the
+ IDE write IRQ comes too early. This hack tries to correct
+ that at the expense of slower write performances. Use this
+ option _only_ to install Windows 2000. You must disable it
+ for normal use. */
+ qemu_mod_timer(s->sector_write_timer,
+ qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 1000));
+ } else {
+ ide_set_irq(s->bus);
+ }
+}
+
+void ide_atapi_cmd_ok(IDEState *s)
+{
+ s->error = 0;
+ s->status = READY_STAT | SEEK_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ ide_set_irq(s->bus);
+}
+
+void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc)
+{
+#ifdef DEBUG_IDE_ATAPI
+ printf("atapi_cmd_error: sense=0x%x asc=0x%x\n", sense_key, asc);
+#endif
+ s->error = sense_key << 4;
+ s->status = READY_STAT | ERR_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ s->sense_key = sense_key;
+ s->asc = asc;
+ ide_set_irq(s->bus);
+}
+
+static void ide_atapi_cmd_check_status(IDEState *s)
+{
+#ifdef DEBUG_IDE_ATAPI
+ printf("atapi_cmd_check_status\n");
+#endif
+ s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4);
+ s->status = ERR_STAT;
+ s->nsector = 0;
+ ide_set_irq(s->bus);
+}
+
+static void ide_flush_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+
+ if (ret < 0) {
+ /* XXX: What sector number to set here? */
+ if (ide_handle_rw_error(s, -ret, BM_STATUS_RETRY_FLUSH)) {
+ return;
+ }
+ }
+
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+}
+
+void ide_flush_cache(IDEState *s)
+{
+ BlockDriverAIOCB *acb;
+
+ if (s->bs == NULL) {
+ ide_flush_cb(s, 0);
+ return;
+ }
+
+ acb = bdrv_aio_flush(s->bs, ide_flush_cb, s);
+ if (acb == NULL) {
+ ide_flush_cb(s, -EIO);
+ }
+}
+
+static inline void cpu_to_ube16(uint8_t *buf, int val)
+{
+ buf[0] = val >> 8;
+ buf[1] = val & 0xff;
+}
+
+static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val & 0xff;
+}
+
+static inline int ube16_to_cpu(const uint8_t *buf)
+{
+ return (buf[0] << 8) | buf[1];
+}
+
+static inline int ube32_to_cpu(const uint8_t *buf)
+{
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+}
+
+static void cd_data_to_raw(uint8_t *buf, int lba)
+{
+ /* sync bytes */
+ buf[0] = 0x00;
+ memset(buf + 1, 0xff, 10);
+ buf[11] = 0x00;
+ buf += 12;
+ /* MSF */
+ lba_to_msf(buf, lba);
+ buf[3] = 0x01; /* mode 1 data */
+ buf += 4;
+ /* data */
+ buf += 2048;
+ /* XXX: ECC not computed */
+ memset(buf, 0, 288);
+}
+
+static int cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf,
+ int sector_size)
+{
+ int ret;
+
+ switch(sector_size) {
+ case 2048:
+ ret = bdrv_read(bs, (int64_t)lba << 2, buf, 4);
+ break;
+ case 2352:
+ ret = bdrv_read(bs, (int64_t)lba << 2, buf + 16, 4);
+ if (ret < 0)
+ return ret;
+ cd_data_to_raw(buf, lba);
+ break;
+ default:
+ ret = -EIO;
+ break;
+ }
+ return ret;
+}
+
+void ide_atapi_io_error(IDEState *s, int ret)
+{
+ /* XXX: handle more errors */
+ if (ret == -ENOMEDIUM) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ } else {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_LOGICAL_BLOCK_OOR);
+ }
+}
+
+/* The whole ATAPI transfer logic is handled in this function */
+static void ide_atapi_cmd_reply_end(IDEState *s)
+{
+ int byte_count_limit, size, ret;
+#ifdef DEBUG_IDE_ATAPI
+ printf("reply: tx_size=%d elem_tx_size=%d index=%d\n",
+ s->packet_transfer_size,
+ s->elementary_transfer_size,
+ s->io_buffer_index);
+#endif
+ if (s->packet_transfer_size <= 0) {
+ /* end of transfer */
+ ide_transfer_stop(s);
+ s->status = READY_STAT | SEEK_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ ide_set_irq(s->bus);
+#ifdef DEBUG_IDE_ATAPI
+ printf("status=0x%x\n", s->status);
+#endif
+ } else {
+ /* see if a new sector must be read */
+ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
+ ret = cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
+ if (ret < 0) {
+ ide_transfer_stop(s);
+ ide_atapi_io_error(s, ret);
+ return;
+ }
+ s->lba++;
+ s->io_buffer_index = 0;
+ }
+ if (s->elementary_transfer_size > 0) {
+ /* there are some data left to transmit in this elementary
+ transfer */
+ size = s->cd_sector_size - s->io_buffer_index;
+ if (size > s->elementary_transfer_size)
+ size = s->elementary_transfer_size;
+ s->packet_transfer_size -= size;
+ s->elementary_transfer_size -= size;
+ s->io_buffer_index += size;
+ ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
+ size, ide_atapi_cmd_reply_end);
+ } else {
+ /* a new transfer is needed */
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+ byte_count_limit = s->lcyl | (s->hcyl << 8);
+#ifdef DEBUG_IDE_ATAPI
+ printf("byte_count_limit=%d\n", byte_count_limit);
+#endif
+ if (byte_count_limit == 0xffff)
+ byte_count_limit--;
+ size = s->packet_transfer_size;
+ if (size > byte_count_limit) {
+ /* byte count limit must be even if this case */
+ if (byte_count_limit & 1)
+ byte_count_limit--;
+ size = byte_count_limit;
+ }
+ s->lcyl = size;
+ s->hcyl = size >> 8;
+ s->elementary_transfer_size = size;
+ /* we cannot transmit more than one sector at a time */
+ if (s->lba != -1) {
+ if (size > (s->cd_sector_size - s->io_buffer_index))
+ size = (s->cd_sector_size - s->io_buffer_index);
+ }
+ s->packet_transfer_size -= size;
+ s->elementary_transfer_size -= size;
+ s->io_buffer_index += size;
+ ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size,
+ size, ide_atapi_cmd_reply_end);
+ ide_set_irq(s->bus);
+#ifdef DEBUG_IDE_ATAPI
+ printf("status=0x%x\n", s->status);
+#endif
+ }
+ }
+}
+
+/* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */
+static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
+{
+ if (size > max_size)
+ size = max_size;
+ s->lba = -1; /* no sector read */
+ s->packet_transfer_size = size;
+ s->io_buffer_size = size; /* dma: send the reply data as one chunk */
+ s->elementary_transfer_size = 0;
+ s->io_buffer_index = 0;
+
+ if (s->atapi_dma) {
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ s->bus->dma->ops->start_dma(s->bus->dma, s,
+ ide_atapi_cmd_read_dma_cb);
+ } else {
+ s->status = READY_STAT | SEEK_STAT;
+ ide_atapi_cmd_reply_end(s);
+ }
+}
+
+/* start a CD-CDROM read command */
+static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
+ int sector_size)
+{
+ s->lba = lba;
+ s->packet_transfer_size = nb_sectors * sector_size;
+ s->elementary_transfer_size = 0;
+ s->io_buffer_index = sector_size;
+ s->cd_sector_size = sector_size;
+
+ s->status = READY_STAT | SEEK_STAT;
+ ide_atapi_cmd_reply_end(s);
+}
+
+/* ATAPI DMA support */
+
+/* XXX: handle read errors */
+static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+ int data_offset, n;
+
+ if (ret < 0) {
+ ide_atapi_io_error(s, ret);
+ goto eot;
+ }
+
+ if (s->io_buffer_size > 0) {
+ /*
+ * For a cdrom read sector command (s->lba != -1),
+ * adjust the lba for the next s->io_buffer_size chunk
+ * and dma the current chunk.
+ * For a command != read (s->lba == -1), just transfer
+ * the reply data.
+ */
+ if (s->lba != -1) {
+ if (s->cd_sector_size == 2352) {
+ n = 1;
+ cd_data_to_raw(s->io_buffer, s->lba);
+ } else {
+ n = s->io_buffer_size >> 11;
+ }
+ s->lba += n;
+ }
+ s->packet_transfer_size -= s->io_buffer_size;
+ if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0)
+ goto eot;
+ }
+
+ if (s->packet_transfer_size <= 0) {
+ s->status = READY_STAT | SEEK_STAT;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ ide_set_irq(s->bus);
+ eot:
+ s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT);
+ ide_set_inactive(s);
+ return;
+ }
+
+ s->io_buffer_index = 0;
+ if (s->cd_sector_size == 2352) {
+ n = 1;
+ s->io_buffer_size = s->cd_sector_size;
+ data_offset = 16;
+ } else {
+ n = s->packet_transfer_size >> 11;
+ if (n > (IDE_DMA_BUF_SECTORS / 4))
+ n = (IDE_DMA_BUF_SECTORS / 4);
+ s->io_buffer_size = n * 2048;
+ data_offset = 0;
+ }
+#ifdef DEBUG_AIO
+ printf("aio_read_cd: lba=%u n=%d\n", s->lba, n);
+#endif
+ s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset);
+ s->bus->dma->iov.iov_len = n * 4 * 512;
+ qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1);
+ s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2,
+ &s->bus->dma->qiov, n * 4,
+ ide_atapi_cmd_read_dma_cb, s);
+ if (!s->bus->dma->aiocb) {
+ /* Note: media not present is the most likely case */
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ goto eot;
+ }
+}
+
+/* start a CD-CDROM read command with DMA */
+/* XXX: test if DMA is available */
+static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
+ int sector_size)
+{
+ s->lba = lba;
+ s->packet_transfer_size = nb_sectors * sector_size;
+ s->io_buffer_index = 0;
+ s->io_buffer_size = 0;
+ s->cd_sector_size = sector_size;
+
+ /* XXX: check if BUSY_STAT should be set */
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
+ s->bus->dma->ops->start_dma(s->bus->dma, s,
+ ide_atapi_cmd_read_dma_cb);
+}
+
+static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
+ int sector_size)
+{
+#ifdef DEBUG_IDE_ATAPI
+ printf("read %s: LBA=%d nb_sectors=%d\n", s->atapi_dma ? "dma" : "pio",
+ lba, nb_sectors);
+#endif
+ if (s->atapi_dma) {
+ ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size);
+ } else {
+ ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size);
+ }
+}
+
+static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index,
+ uint16_t profile)
+{
+ uint8_t *buf_profile = buf + 12; /* start of profiles */
+
+ buf_profile += ((*index) * 4); /* start of indexed profile */
+ cpu_to_ube16 (buf_profile, profile);
+ buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7]));
+
+ /* each profile adds 4 bytes to the response */
+ (*index)++;
+ buf[11] += 4; /* Additional Length */
+
+ return 4;
+}
+
+static int ide_dvd_read_structure(IDEState *s, int format,
+ const uint8_t *packet, uint8_t *buf)
+{
+ switch (format) {
+ case 0x0: /* Physical format information */
+ {
+ int layer = packet[6];
+ uint64_t total_sectors;
+
+ if (layer != 0)
+ return -ASC_INV_FIELD_IN_CMD_PACKET;
+
+ bdrv_get_geometry(s->bs, &total_sectors);
+ total_sectors >>= 2;
+ if (total_sectors == 0)
+ return -ASC_MEDIUM_NOT_PRESENT;
+
+ buf[4] = 1; /* DVD-ROM, part version 1 */
+ buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+ buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
+ buf[7] = 0; /* default densities */
+
+ /* FIXME: 0x30000 per spec? */
+ cpu_to_ube32(buf + 8, 0); /* start sector */
+ cpu_to_ube32(buf + 12, total_sectors - 1); /* end sector */
+ cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */
+
+ /* Size of buffer, not including 2 byte size field */
+ cpu_to_be16wu((uint16_t *)buf, 2048 + 2);
+
+ /* 2k data + 4 byte header */
+ return (2048 + 4);
+ }
+
+ case 0x01: /* DVD copyright information */
+ buf[4] = 0; /* no copyright data */
+ buf[5] = 0; /* no region restrictions */
+
+ /* Size of buffer, not including 2 byte size field */
+ cpu_to_be16wu((uint16_t *)buf, 4 + 2);
+
+ /* 4 byte header + 4 byte data */
+ return (4 + 4);
+
+ case 0x03: /* BCA information - invalid field for no BCA info */
+ return -ASC_INV_FIELD_IN_CMD_PACKET;
+
+ case 0x04: /* DVD disc manufacturing information */
+ /* Size of buffer, not including 2 byte size field */
+ cpu_to_be16wu((uint16_t *)buf, 2048 + 2);
+
+ /* 2k data + 4 byte header */
+ return (2048 + 4);
+
+ case 0xff:
+ /*
+ * This lists all the command capabilities above. Add new ones
+ * in order and update the length and buffer return values.
+ */
+
+ buf[4] = 0x00; /* Physical format */
+ buf[5] = 0x40; /* Not writable, is readable */
+ cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4);
+
+ buf[8] = 0x01; /* Copyright info */
+ buf[9] = 0x40; /* Not writable, is readable */
+ cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4);
+
+ buf[12] = 0x03; /* BCA info */
+ buf[13] = 0x40; /* Not writable, is readable */
+ cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4);
+
+ buf[16] = 0x04; /* Manufacturing info */
+ buf[17] = 0x40; /* Not writable, is readable */
+ cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4);
+
+ /* Size of buffer, not including 2 byte size field */
+ cpu_to_be16wu((uint16_t *)buf, 16 + 2);
+
+ /* data written + 4 byte header */
+ return (16 + 4);
+
+ default: /* TODO: formats beyond DVD-ROM requires */
+ return -ASC_INV_FIELD_IN_CMD_PACKET;
+ }
+}
+
+static void ide_atapi_cmd(IDEState *s)
+{
+ const uint8_t *packet;
+ uint8_t *buf;
+ int max_len;
+
+ packet = s->io_buffer;
+ buf = s->io_buffer;
+#ifdef DEBUG_IDE_ATAPI
+ {
+ int i;
+ printf("ATAPI limit=0x%x packet:", s->lcyl | (s->hcyl << 8));
+ for(i = 0; i < ATAPI_PACKET_SIZE; i++) {
+ printf(" %02x", packet[i]);
+ }
+ printf("\n");
+ }
+#endif
+ /* If there's a UNIT_ATTENTION condition pending, only
+ REQUEST_SENSE and INQUIRY commands are allowed to complete. */
+ if (s->sense_key == SENSE_UNIT_ATTENTION &&
+ s->io_buffer[0] != GPCMD_REQUEST_SENSE &&
+ s->io_buffer[0] != GPCMD_INQUIRY) {
+ ide_atapi_cmd_check_status(s);
+ return;
+ }
+ switch(s->io_buffer[0]) {
+ case GPCMD_TEST_UNIT_READY:
+ if (bdrv_is_inserted(s->bs) && !s->cdrom_changed) {
+ ide_atapi_cmd_ok(s);
+ } else {
+ s->cdrom_changed = 0;
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ }
+ break;
+ case GPCMD_MODE_SENSE_6:
+ case GPCMD_MODE_SENSE_10:
+ {
+ int action, code;
+ if (packet[0] == GPCMD_MODE_SENSE_10)
+ max_len = ube16_to_cpu(packet + 7);
+ else
+ max_len = packet[4];
+ action = packet[2] >> 6;
+ code = packet[2] & 0x3f;
+ switch(action) {
+ case 0: /* current values */
+ switch(code) {
+ case GPMODE_R_W_ERROR_PAGE: /* error recovery */
+ cpu_to_ube16(&buf[0], 16 + 6);
+ buf[2] = 0x70;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+
+ buf[8] = 0x01;
+ buf[9] = 0x06;
+ buf[10] = 0x00;
+ buf[11] = 0x05;
+ buf[12] = 0x00;
+ buf[13] = 0x00;
+ buf[14] = 0x00;
+ buf[15] = 0x00;
+ ide_atapi_cmd_reply(s, 16, max_len);
+ break;
+ case GPMODE_AUDIO_CTL_PAGE:
+ cpu_to_ube16(&buf[0], 24 + 6);
+ buf[2] = 0x70;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+
+ /* Fill with CDROM audio volume */
+ buf[17] = 0;
+ buf[19] = 0;
+ buf[21] = 0;
+ buf[23] = 0;
+
+ ide_atapi_cmd_reply(s, 24, max_len);
+ break;
+ case GPMODE_CAPABILITIES_PAGE:
+ cpu_to_ube16(&buf[0], 28 + 6);
+ buf[2] = 0x70;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+
+ buf[8] = 0x2a;
+ buf[9] = 0x12;
+ buf[10] = 0x00;
+ buf[11] = 0x00;
+
+ /* Claim PLAY_AUDIO capability (0x01) since some Linux
+ code checks for this to automount media. */
+ buf[12] = 0x71;
+ buf[13] = 3 << 5;
+ buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
+ if (bdrv_is_locked(s->bs))
+ buf[6] |= 1 << 1;
+ buf[15] = 0x00;
+ cpu_to_ube16(&buf[16], 706);
+ buf[18] = 0;
+ buf[19] = 2;
+ cpu_to_ube16(&buf[20], 512);
+ cpu_to_ube16(&buf[22], 706);
+ buf[24] = 0;
+ buf[25] = 0;
+ buf[26] = 0;
+ buf[27] = 0;
+ ide_atapi_cmd_reply(s, 28, max_len);
+ break;
+ default:
+ goto error_cmd;
+ }
+ break;
+ case 1: /* changeable values */
+ goto error_cmd;
+ case 2: /* default values */
+ goto error_cmd;
+ default:
+ case 3: /* saved values */
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+ break;
+ }
+ }
+ break;
+ case GPCMD_REQUEST_SENSE:
+ max_len = packet[4];
+ memset(buf, 0, 18);
+ buf[0] = 0x70 | (1 << 7);
+ buf[2] = s->sense_key;
+ buf[7] = 10;
+ buf[12] = s->asc;
+ if (s->sense_key == SENSE_UNIT_ATTENTION)
+ s->sense_key = SENSE_NONE;
+ ide_atapi_cmd_reply(s, 18, max_len);
+ break;
+ case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ if (bdrv_is_inserted(s->bs)) {
+ bdrv_set_locked(s->bs, packet[4] & 1);
+ ide_atapi_cmd_ok(s);
+ } else {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ }
+ break;
+ case GPCMD_READ_10:
+ case GPCMD_READ_12:
+ {
+ int nb_sectors, lba;
+
+ if (packet[0] == GPCMD_READ_10)
+ nb_sectors = ube16_to_cpu(packet + 7);
+ else
+ nb_sectors = ube32_to_cpu(packet + 6);
+ lba = ube32_to_cpu(packet + 2);
+ if (nb_sectors == 0) {
+ ide_atapi_cmd_ok(s);
+ break;
+ }
+ ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+ }
+ break;
+ case GPCMD_READ_CD:
+ {
+ int nb_sectors, lba, transfer_request;
+
+ nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8];
+ lba = ube32_to_cpu(packet + 2);
+ if (nb_sectors == 0) {
+ ide_atapi_cmd_ok(s);
+ break;
+ }
+ transfer_request = packet[9];
+ switch(transfer_request & 0xf8) {
+ case 0x00:
+ /* nothing */
+ ide_atapi_cmd_ok(s);
+ break;
+ case 0x10:
+ /* normal read */
+ ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
+ break;
+ case 0xf8:
+ /* read all data */
+ ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
+ break;
+ default:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+ }
+ break;
+ case GPCMD_SEEK:
+ {
+ unsigned int lba;
+ uint64_t total_sectors;
+
+ bdrv_get_geometry(s->bs, &total_sectors);
+ total_sectors >>= 2;
+ if (total_sectors == 0) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ lba = ube32_to_cpu(packet + 2);
+ if (lba >= total_sectors) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_LOGICAL_BLOCK_OOR);
+ break;
+ }
+ ide_atapi_cmd_ok(s);
+ }
+ break;
+ case GPCMD_START_STOP_UNIT:
+ {
+ int start, eject, err = 0;
+ start = packet[4] & 1;
+ eject = (packet[4] >> 1) & 1;
+
+ if (eject) {
+ err = bdrv_eject(s->bs, !start);
+ }
+
+ switch (err) {
+ case 0:
+ ide_atapi_cmd_ok(s);
+ break;
+ case -EBUSY:
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIA_REMOVAL_PREVENTED);
+ break;
+ default:
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ }
+ break;
+ case GPCMD_MECHANISM_STATUS:
+ {
+ max_len = ube16_to_cpu(packet + 8);
+ cpu_to_ube16(buf, 0);
+ /* no current LBA */
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 1;
+ cpu_to_ube16(buf + 6, 0);
+ ide_atapi_cmd_reply(s, 8, max_len);
+ }
+ break;
+ case GPCMD_READ_TOC_PMA_ATIP:
+ {
+ int format, msf, start_track, len;
+ uint64_t total_sectors;
+
+ bdrv_get_geometry(s->bs, &total_sectors);
+ total_sectors >>= 2;
+ if (total_sectors == 0) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ max_len = ube16_to_cpu(packet + 7);
+ format = packet[9] >> 6;
+ msf = (packet[1] >> 1) & 1;
+ start_track = packet[6];
+ switch(format) {
+ case 0:
+ len = cdrom_read_toc(total_sectors, buf, msf, start_track);
+ if (len < 0)
+ goto error_cmd;
+ ide_atapi_cmd_reply(s, len, max_len);
+ break;
+ case 1:
+ /* multi session : only a single session defined */
+ memset(buf, 0, 12);
+ buf[1] = 0x0a;
+ buf[2] = 0x01;
+ buf[3] = 0x01;
+ ide_atapi_cmd_reply(s, 12, max_len);
+ break;
+ case 2:
+ len = cdrom_read_toc_raw(total_sectors, buf, msf, start_track);
+ if (len < 0)
+ goto error_cmd;
+ ide_atapi_cmd_reply(s, len, max_len);
+ break;
+ default:
+ error_cmd:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+ }
+ break;
+ case GPCMD_READ_CDVD_CAPACITY:
+ {
+ uint64_t total_sectors;
+
+ bdrv_get_geometry(s->bs, &total_sectors);
+ total_sectors >>= 2;
+ if (total_sectors == 0) {
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ break;
+ }
+ /* NOTE: it is really the number of sectors minus 1 */
+ cpu_to_ube32(buf, total_sectors - 1);
+ cpu_to_ube32(buf + 4, 2048);
+ ide_atapi_cmd_reply(s, 8, 8);
+ }
+ break;
+ case GPCMD_READ_DVD_STRUCTURE:
+ {
+ int media = packet[1];
+ int format = packet[7];
+ int ret;
+
+ max_len = ube16_to_cpu(packet + 8);
+
+ if (format < 0xff) {
+ if (media_is_cd(s)) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INCOMPATIBLE_FORMAT);
+ break;
+ } else if (!media_present(s)) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+ }
+
+ memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * 512 + 4 ?
+ IDE_DMA_BUF_SECTORS * 512 + 4 : max_len);
+
+ switch (format) {
+ case 0x00 ... 0x7f:
+ case 0xff:
+ if (media == 0) {
+ ret = ide_dvd_read_structure(s, format, packet, buf);
+
+ if (ret < 0)
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
+ else
+ ide_atapi_cmd_reply(s, ret, max_len);
+
+ break;
+ }
+ /* TODO: BD support, fall through for now */
+
+ /* Generic disk structures */
+ case 0x80: /* TODO: AACS volume identifier */
+ case 0x81: /* TODO: AACS media serial number */
+ case 0x82: /* TODO: AACS media identifier */
+ case 0x83: /* TODO: AACS media key block */
+ case 0x90: /* TODO: List of recognized format layers */
+ case 0xc0: /* TODO: Write protection status */
+ default:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+ }
+ break;
+ case GPCMD_SET_SPEED:
+ ide_atapi_cmd_ok(s);
+ break;
+ case GPCMD_INQUIRY:
+ max_len = packet[4];
+ buf[0] = 0x05; /* CD-ROM */
+ buf[1] = 0x80; /* removable */
+ buf[2] = 0x00; /* ISO */
+ buf[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
+ buf[4] = 31; /* additional length */
+ buf[5] = 0; /* reserved */
+ buf[6] = 0; /* reserved */
+ buf[7] = 0; /* reserved */
+ padstr8(buf + 8, 8, "QEMU");
+ padstr8(buf + 16, 16, "QEMU DVD-ROM");
+ padstr8(buf + 32, 4, s->version);
+ ide_atapi_cmd_reply(s, 36, max_len);
+ break;
+ case GPCMD_GET_CONFIGURATION:
+ {
+ uint32_t len;
+ uint8_t index = 0;
+
+ /* only feature 0 is supported */
+ if (packet[2] != 0 || packet[3] != 0) {
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
+ }
+
+ /* XXX: could result in alignment problems in some architectures */
+ max_len = ube16_to_cpu(packet + 7);
+
+ /*
+ * XXX: avoid overflow for io_buffer if max_len is bigger than
+ * the size of that buffer (dimensioned to max number of
+ * sectors to transfer at once)
+ *
+ * Only a problem if the feature/profiles grow.
+ */
+ if (max_len > 512) /* XXX: assume 1 sector */
+ max_len = 512;
+
+ memset(buf, 0, max_len);
+ /*
+ * the number of sectors from the media tells us which profile
+ * to use as current. 0 means there is no media
+ */
+ if (media_is_dvd(s))
+ cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM);
+ else if (media_is_cd(s))
+ cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM);
+
+ buf[10] = 0x02 | 0x01; /* persistent and current */
+ len = 12; /* headers: 8 + 4 */
+ len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
+ len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
+ cpu_to_ube32(buf, len - 4); /* data length */
+
+ ide_atapi_cmd_reply(s, len, max_len);
+ break;
+ }
+ case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
+ max_len = ube16_to_cpu(packet + 7);
+
+ if (packet[1] & 0x01) { /* polling */
+ /* We don't support any event class (yet). */
+ cpu_to_ube16(buf, 0x00); /* No event descriptor returned */
+ buf[2] = 0x80; /* No Event Available (NEA) */
+ buf[3] = 0x00; /* Empty supported event classes */
+ ide_atapi_cmd_reply(s, 4, max_len);
+ } else { /* asynchronous mode */
+ /* Only polling is supported, asynchronous mode is not. */
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ }
+ break;
+ default:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_ILLEGAL_OPCODE);
+ break;
+ }
+}
+
+static void ide_cfata_metadata_inquiry(IDEState *s)
+{
+ uint16_t *p;
+ uint32_t spd;
+
+ p = (uint16_t *) s->io_buffer;
+ memset(p, 0, 0x200);
+ spd = ((s->mdata_size - 1) >> 9) + 1;
+
+ put_le16(p + 0, 0x0001); /* Data format revision */
+ put_le16(p + 1, 0x0000); /* Media property: silicon */
+ put_le16(p + 2, s->media_changed); /* Media status */
+ put_le16(p + 3, s->mdata_size & 0xffff); /* Capacity in bytes (low) */
+ put_le16(p + 4, s->mdata_size >> 16); /* Capacity in bytes (high) */
+ put_le16(p + 5, spd & 0xffff); /* Sectors per device (low) */
+ put_le16(p + 6, spd >> 16); /* Sectors per device (high) */
+}
+
+static void ide_cfata_metadata_read(IDEState *s)
+{
+ uint16_t *p;
+
+ if (((s->hcyl << 16) | s->lcyl) << 9 > s->mdata_size + 2) {
+ s->status = ERR_STAT;
+ s->error = ABRT_ERR;
+ return;
+ }
+
+ p = (uint16_t *) s->io_buffer;
+ memset(p, 0, 0x200);
+
+ put_le16(p + 0, s->media_changed); /* Media status */
+ memcpy(p + 1, s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9),
+ MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9),
+ s->nsector << 9), 0x200 - 2));
+}
+
+static void ide_cfata_metadata_write(IDEState *s)
+{
+ if (((s->hcyl << 16) | s->lcyl) << 9 > s->mdata_size + 2) {
+ s->status = ERR_STAT;
+ s->error = ABRT_ERR;
+ return;
+ }
+
+ s->media_changed = 0;
+
+ memcpy(s->mdata_storage + (((s->hcyl << 16) | s->lcyl) << 9),
+ s->io_buffer + 2,
+ MIN(MIN(s->mdata_size - (((s->hcyl << 16) | s->lcyl) << 9),
+ s->nsector << 9), 0x200 - 2));
+}
+
+/* called when the inserted state of the media has changed */
+static void cdrom_change_cb(void *opaque, int reason)
+{
+ IDEState *s = opaque;
+ uint64_t nb_sectors;
+
+ if (!(reason & CHANGE_MEDIA)) {
+ return;
+ }
+
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ s->nb_sectors = nb_sectors;
+
+ s->sense_key = SENSE_UNIT_ATTENTION;
+ s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
+ s->cdrom_changed = 1;
+ ide_set_irq(s->bus);
+}
+
+static void ide_cmd_lba48_transform(IDEState *s, int lba48)
+{
+ s->lba48 = lba48;
+
+ /* handle the 'magic' 0 nsector count conversion here. to avoid
+ * fiddling with the rest of the read logic, we just store the
+ * full sector count in ->nsector and ignore ->hob_nsector from now
+ */
+ if (!s->lba48) {
+ if (!s->nsector)
+ s->nsector = 256;
+ } else {
+ if (!s->nsector && !s->hob_nsector)
+ s->nsector = 65536;
+ else {
+ int lo = s->nsector;
+ int hi = s->hob_nsector;
+
+ s->nsector = (hi << 8) | lo;
+ }
+ }
+}
+
+static void ide_clear_hob(IDEBus *bus)
+{
+ /* any write clears HOB high bit of device control register */
+ bus->ifs[0].select &= ~(1 << 7);
+ bus->ifs[1].select &= ~(1 << 7);
+}
+
+void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEBus *bus = opaque;
+
+#ifdef DEBUG_IDE
+ printf("IDE: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+
+ addr &= 7;
+
+ /* ignore writes to command block while busy with previous command */
+ if (addr != 7 && (idebus_active_if(bus)->status & (BUSY_STAT|DRQ_STAT)))
+ return;
+
+ switch(addr) {
+ case 0:
+ break;
+ case 1:
+ ide_clear_hob(bus);
+ /* NOTE: data is written to the two drives */
+ bus->ifs[0].hob_feature = bus->ifs[0].feature;
+ bus->ifs[1].hob_feature = bus->ifs[1].feature;
+ bus->ifs[0].feature = val;
+ bus->ifs[1].feature = val;
+ break;
+ case 2:
+ ide_clear_hob(bus);
+ bus->ifs[0].hob_nsector = bus->ifs[0].nsector;
+ bus->ifs[1].hob_nsector = bus->ifs[1].nsector;
+ bus->ifs[0].nsector = val;
+ bus->ifs[1].nsector = val;
+ break;
+ case 3:
+ ide_clear_hob(bus);
+ bus->ifs[0].hob_sector = bus->ifs[0].sector;
+ bus->ifs[1].hob_sector = bus->ifs[1].sector;
+ bus->ifs[0].sector = val;
+ bus->ifs[1].sector = val;
+ break;
+ case 4:
+ ide_clear_hob(bus);
+ bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl;
+ bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl;
+ bus->ifs[0].lcyl = val;
+ bus->ifs[1].lcyl = val;
+ break;
+ case 5:
+ ide_clear_hob(bus);
+ bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl;
+ bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl;
+ bus->ifs[0].hcyl = val;
+ bus->ifs[1].hcyl = val;
+ break;
+ case 6:
+ /* FIXME: HOB readback uses bit 7 */
+ bus->ifs[0].select = (val & ~0x10) | 0xa0;
+ bus->ifs[1].select = (val | 0x10) | 0xa0;
+ /* select drive */
+ bus->unit = (val >> 4) & 1;
+ break;
+ default:
+ case 7:
+ /* command */
+ ide_exec_cmd(bus, val);
+ break;
+ }
+}
+
+
+void ide_exec_cmd(IDEBus *bus, uint32_t val)
+{
+ IDEState *s;
+ int n;
+ int lba48 = 0;
+
+#if defined(DEBUG_IDE)
+ printf("ide: CMD=%02x\n", val);
+#endif
+ s = idebus_active_if(bus);
+ /* ignore commands to non existant slave */
+ if (s != bus->ifs && !s->bs)
+ return;
+
+ /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
+ if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
+ return;
+
+ switch(val) {
+ case WIN_IDENTIFY:
+ if (s->bs && s->drive_kind != IDE_CD) {
+ if (s->drive_kind != IDE_CFATA)
+ ide_identify(s);
+ else
+ ide_cfata_identify(s);
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
+ } else {
+ if (s->drive_kind == IDE_CD) {
+ ide_set_signature(s);
+ }
+ ide_abort_command(s);
+ }
+ ide_set_irq(s->bus);
+ break;
+ case WIN_SPECIFY:
+ case WIN_RECAL:
+ s->error = 0;
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case WIN_SETMULT:
+ if (s->drive_kind == IDE_CFATA && s->nsector == 0) {
+ /* Disable Read and Write Multiple */
+ s->mult_sectors = 0;
+ s->status = READY_STAT | SEEK_STAT;
+ } else if ((s->nsector & 0xff) != 0 &&
+ ((s->nsector & 0xff) > MAX_MULT_SECTORS ||
+ (s->nsector & (s->nsector - 1)) != 0)) {
+ ide_abort_command(s);
+ } else {
+ s->mult_sectors = s->nsector & 0xff;
+ s->status = READY_STAT | SEEK_STAT;
+ }
+ ide_set_irq(s->bus);
+ break;
+ case WIN_VERIFY_EXT:
+ lba48 = 1;
+ case WIN_VERIFY:
+ case WIN_VERIFY_ONCE:
+ /* do sector number check ? */
+ ide_cmd_lba48_transform(s, lba48);
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case WIN_READ_EXT:
+ lba48 = 1;
+ case WIN_READ:
+ case WIN_READ_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ s->req_nb_sectors = 1;
+ ide_sector_read(s);
+ break;
+ case WIN_WRITE_EXT:
+ lba48 = 1;
+ case WIN_WRITE:
+ case WIN_WRITE_ONCE:
+ case CFA_WRITE_SECT_WO_ERASE:
+ case WIN_WRITE_VERIFY:
+ ide_cmd_lba48_transform(s, lba48);
+ s->error = 0;
+ s->status = SEEK_STAT | READY_STAT;
+ s->req_nb_sectors = 1;
+ ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
+ s->media_changed = 1;
+ break;
+ case WIN_MULTREAD_EXT:
+ lba48 = 1;
+ case WIN_MULTREAD:
+ if (!s->mult_sectors)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ s->req_nb_sectors = s->mult_sectors;
+ ide_sector_read(s);
+ break;
+ case WIN_MULTWRITE_EXT:
+ lba48 = 1;
+ case WIN_MULTWRITE:
+ case CFA_WRITE_MULTI_WO_ERASE:
+ if (!s->mult_sectors)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ s->error = 0;
+ s->status = SEEK_STAT | READY_STAT;
+ s->req_nb_sectors = s->mult_sectors;
+ n = s->nsector;
+ if (n > s->req_nb_sectors)
+ n = s->req_nb_sectors;
+ ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
+ s->media_changed = 1;
+ break;
+ case WIN_READDMA_EXT:
+ lba48 = 1;
+ case WIN_READDMA:
+ case WIN_READDMA_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ ide_sector_start_dma(s, 1);
+ break;
+ case WIN_WRITEDMA_EXT:
+ lba48 = 1;
+ case WIN_WRITEDMA:
+ case WIN_WRITEDMA_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ ide_sector_start_dma(s, 0);
+ s->media_changed = 1;
+ break;
+ case WIN_READ_NATIVE_MAX_EXT:
+ lba48 = 1;
+ case WIN_READ_NATIVE_MAX:
+ ide_cmd_lba48_transform(s, lba48);
+ ide_set_sector(s, s->nb_sectors - 1);
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case WIN_CHECKPOWERMODE1:
+ case WIN_CHECKPOWERMODE2:
+ s->nsector = 0xff; /* device active or idle */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case WIN_SETFEATURES:
+ if (!s->bs)
+ goto abort_cmd;
+ /* XXX: valid for CDROM ? */
+ switch(s->feature) {
+ case 0xcc: /* reverting to power-on defaults enable */
+ case 0x66: /* reverting to power-on defaults disable */
+ case 0x02: /* write cache enable */
+ case 0x82: /* write cache disable */
+ case 0xaa: /* read look-ahead enable */
+ case 0x55: /* read look-ahead disable */
+ case 0x05: /* set advanced power management mode */
+ case 0x85: /* disable advanced power management mode */
+ case 0x69: /* NOP */
+ case 0x67: /* NOP */
+ case 0x96: /* NOP */
+ case 0x9a: /* NOP */
+ case 0x42: /* enable Automatic Acoustic Mode */
+ case 0xc2: /* disable Automatic Acoustic Mode */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case 0x03: { /* set transfer mode */
+ uint8_t val = s->nsector & 0x07;
+ uint16_t *identify_data = (uint16_t *)s->identify_data;
+
+ switch (s->nsector >> 3) {
+ case 0x00: /* pio default */
+ case 0x01: /* pio mode */
+ put_le16(identify_data + 62,0x07);
+ put_le16(identify_data + 63,0x07);
+ put_le16(identify_data + 88,0x3f);
+ break;
+ case 0x02: /* sigle word dma mode*/
+ put_le16(identify_data + 62,0x07 | (1 << (val + 8)));
+ put_le16(identify_data + 63,0x07);
+ put_le16(identify_data + 88,0x3f);
+ break;
+ case 0x04: /* mdma mode */
+ put_le16(identify_data + 62,0x07);
+ put_le16(identify_data + 63,0x07 | (1 << (val + 8)));
+ put_le16(identify_data + 88,0x3f);
+ break;
+ case 0x08: /* udma mode */
+ put_le16(identify_data + 62,0x07);
+ put_le16(identify_data + 63,0x07);
+ put_le16(identify_data + 88,0x3f | (1 << (val + 8)));
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ }
+ default:
+ goto abort_cmd;
+ }
+ break;
+ case WIN_FLUSH_CACHE:
+ case WIN_FLUSH_CACHE_EXT:
+ ide_flush_cache(s);
+ break;
+ case WIN_STANDBY:
+ case WIN_STANDBY2:
+ case WIN_STANDBYNOW1:
+ case WIN_STANDBYNOW2:
+ case WIN_IDLEIMMEDIATE:
+ case CFA_IDLEIMMEDIATE:
+ case WIN_SETIDLE1:
+ case WIN_SETIDLE2:
+ case WIN_SLEEPNOW1:
+ case WIN_SLEEPNOW2:
+ s->status = READY_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case WIN_SEEK:
+ if(s->drive_kind == IDE_CD)
+ goto abort_cmd;
+ /* XXX: Check that seek is within bounds */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ /* ATAPI commands */
+ case WIN_PIDENTIFY:
+ if (s->drive_kind == IDE_CD) {
+ ide_atapi_identify(s);
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
+ } else {
+ ide_abort_command(s);
+ }
+ ide_set_irq(s->bus);
+ break;
+ case WIN_DIAGNOSE:
+ ide_set_signature(s);
+ if (s->drive_kind == IDE_CD)
+ s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet
+ * devices to return a clear status register
+ * with READY_STAT *not* set. */
+ else
+ s->status = READY_STAT | SEEK_STAT;
+ s->error = 0x01; /* Device 0 passed, Device 1 passed or not
+ * present.
+ */
+ ide_set_irq(s->bus);
+ break;
+ case WIN_SRST:
+ if (s->drive_kind != IDE_CD)
+ goto abort_cmd;
+ ide_set_signature(s);
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ s->error = 0x01;
+ break;
+ case WIN_PACKETCMD:
+ if (s->drive_kind != IDE_CD)
+ goto abort_cmd;
+ /* overlapping commands not supported */
+ if (s->feature & 0x02)
+ goto abort_cmd;
+ s->status = READY_STAT | SEEK_STAT;
+ s->atapi_dma = s->feature & 1;
+ s->nsector = 1;
+ ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
+ ide_atapi_cmd);
+ break;
+ /* CF-ATA commands */
+ case CFA_REQ_EXT_ERROR_CODE:
+ if (s->drive_kind != IDE_CFATA)
+ goto abort_cmd;
+ s->error = 0x09; /* miscellaneous error */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case CFA_ERASE_SECTORS:
+ case CFA_WEAR_LEVEL:
+ if (s->drive_kind != IDE_CFATA)
+ goto abort_cmd;
+ if (val == CFA_WEAR_LEVEL)
+ s->nsector = 0;
+ if (val == CFA_ERASE_SECTORS)
+ s->media_changed = 1;
+ s->error = 0x00;
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case CFA_TRANSLATE_SECTOR:
+ if (s->drive_kind != IDE_CFATA)
+ goto abort_cmd;
+ s->error = 0x00;
+ s->status = READY_STAT | SEEK_STAT;
+ memset(s->io_buffer, 0, 0x200);
+ s->io_buffer[0x00] = s->hcyl; /* Cyl MSB */
+ s->io_buffer[0x01] = s->lcyl; /* Cyl LSB */
+ s->io_buffer[0x02] = s->select; /* Head */
+ s->io_buffer[0x03] = s->sector; /* Sector */
+ s->io_buffer[0x04] = ide_get_sector(s) >> 16; /* LBA MSB */
+ s->io_buffer[0x05] = ide_get_sector(s) >> 8; /* LBA */
+ s->io_buffer[0x06] = ide_get_sector(s) >> 0; /* LBA LSB */
+ s->io_buffer[0x13] = 0x00; /* Erase flag */
+ s->io_buffer[0x18] = 0x00; /* Hot count */
+ s->io_buffer[0x19] = 0x00; /* Hot count */
+ s->io_buffer[0x1a] = 0x01; /* Hot count */
+ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+ ide_set_irq(s->bus);
+ break;
+ case CFA_ACCESS_METADATA_STORAGE:
+ if (s->drive_kind != IDE_CFATA)
+ goto abort_cmd;
+ switch (s->feature) {
+ case 0x02: /* Inquiry Metadata Storage */
+ ide_cfata_metadata_inquiry(s);
+ break;
+ case 0x03: /* Read Metadata Storage */
+ ide_cfata_metadata_read(s);
+ break;
+ case 0x04: /* Write Metadata Storage */
+ ide_cfata_metadata_write(s);
+ break;
+ default:
+ goto abort_cmd;
+ }
+ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ ide_set_irq(s->bus);
+ break;
+ case IBM_SENSE_CONDITION:
+ if (s->drive_kind != IDE_CFATA)
+ goto abort_cmd;
+ switch (s->feature) {
+ case 0x01: /* sense temperature in device */
+ s->nsector = 0x50; /* +20 C */
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+
+ case WIN_SMART:
+ if (s->drive_kind == IDE_CD)
+ goto abort_cmd;
+ if (s->hcyl != 0xc2 || s->lcyl != 0x4f)
+ goto abort_cmd;
+ if (!s->smart_enabled && s->feature != SMART_ENABLE)
+ goto abort_cmd;
+ switch (s->feature) {
+ case SMART_DISABLE:
+ s->smart_enabled = 0;
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case SMART_ENABLE:
+ s->smart_enabled = 1;
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case SMART_ATTR_AUTOSAVE:
+ switch (s->sector) {
+ case 0x00:
+ s->smart_autosave = 0;
+ break;
+ case 0xf1:
+ s->smart_autosave = 1;
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case SMART_STATUS:
+ if (!s->smart_errors) {
+ s->hcyl = 0xc2;
+ s->lcyl = 0x4f;
+ } else {
+ s->hcyl = 0x2c;
+ s->lcyl = 0xf4;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ case SMART_READ_THRESH:
+ memset(s->io_buffer, 0, 0x200);
+ s->io_buffer[0] = 0x01; /* smart struct version */
+ for (n=0; n<30; n++) {
+ if (smart_attributes[n][0] == 0)
+ break;
+ s->io_buffer[2+0+(n*12)] = smart_attributes[n][0];
+ s->io_buffer[2+1+(n*12)] = smart_attributes[n][4];
+ }
+ for (n=0; n<511; n++) /* checksum */
+ s->io_buffer[511] += s->io_buffer[n];
+ s->io_buffer[511] = 0x100 - s->io_buffer[511];
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+ ide_set_irq(s->bus);
+ break;
+ case SMART_READ_DATA:
+ memset(s->io_buffer, 0, 0x200);
+ s->io_buffer[0] = 0x01; /* smart struct version */
+ for (n=0; n<30; n++) {
+ if (smart_attributes[n][0] == 0)
+ break;
+ s->io_buffer[2+0+(n*12)] = smart_attributes[n][0];
+ s->io_buffer[2+1+(n*12)] = smart_attributes[n][1];
+ s->io_buffer[2+3+(n*12)] = smart_attributes[n][2];
+ s->io_buffer[2+4+(n*12)] = smart_attributes[n][3];
+ }
+ s->io_buffer[362] = 0x02 | (s->smart_autosave?0x80:0x00);
+ if (s->smart_selftest_count == 0) {
+ s->io_buffer[363] = 0;
+ } else {
+ s->io_buffer[363] =
+ s->smart_selftest_data[3 +
+ (s->smart_selftest_count - 1) *
+ 24];
+ }
+ s->io_buffer[364] = 0x20;
+ s->io_buffer[365] = 0x01;
+ /* offline data collection capacity: execute + self-test*/
+ s->io_buffer[367] = (1<<4 | 1<<3 | 1);
+ s->io_buffer[368] = 0x03; /* smart capability (1) */
+ s->io_buffer[369] = 0x00; /* smart capability (2) */
+ s->io_buffer[370] = 0x01; /* error logging supported */
+ s->io_buffer[372] = 0x02; /* minutes for poll short test */
+ s->io_buffer[373] = 0x36; /* minutes for poll ext test */
+ s->io_buffer[374] = 0x01; /* minutes for poll conveyance */
+
+ for (n=0; n<511; n++)
+ s->io_buffer[511] += s->io_buffer[n];
+ s->io_buffer[511] = 0x100 - s->io_buffer[511];
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+ ide_set_irq(s->bus);
+ break;
+ case SMART_READ_LOG:
+ switch (s->sector) {
+ case 0x01: /* summary smart error log */
+ memset(s->io_buffer, 0, 0x200);
+ s->io_buffer[0] = 0x01;
+ s->io_buffer[1] = 0x00; /* no error entries */
+ s->io_buffer[452] = s->smart_errors & 0xff;
+ s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8;
+
+ for (n=0; n<511; n++)
+ s->io_buffer[511] += s->io_buffer[n];
+ s->io_buffer[511] = 0x100 - s->io_buffer[511];
+ break;
+ case 0x06: /* smart self test log */
+ memset(s->io_buffer, 0, 0x200);
+ s->io_buffer[0] = 0x01;
+ if (s->smart_selftest_count == 0) {
+ s->io_buffer[508] = 0;
+ } else {
+ s->io_buffer[508] = s->smart_selftest_count;
+ for (n=2; n<506; n++)
+ s->io_buffer[n] = s->smart_selftest_data[n];
+ }
+ for (n=0; n<511; n++)
+ s->io_buffer[511] += s->io_buffer[n];
+ s->io_buffer[511] = 0x100 - s->io_buffer[511];
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+ ide_set_irq(s->bus);
+ break;
+ case SMART_EXECUTE_OFFLINE:
+ switch (s->sector) {
+ case 0: /* off-line routine */
+ case 1: /* short self test */
+ case 2: /* extended self test */
+ s->smart_selftest_count++;
+ if(s->smart_selftest_count > 21)
+ s->smart_selftest_count = 0;
+ n = 2 + (s->smart_selftest_count - 1) * 24;
+ s->smart_selftest_data[n] = s->sector;
+ s->smart_selftest_data[n+1] = 0x00; /* OK and finished */
+ s->smart_selftest_data[n+2] = 0x34; /* hour count lsb */
+ s->smart_selftest_data[n+3] = 0x12; /* hour count msb */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ break;
+ default:
+ goto abort_cmd;
+ }
+ break;
+ default:
+ goto abort_cmd;
+ }
+ break;
+ default:
+ abort_cmd:
+ ide_abort_command(s);
+ ide_set_irq(s->bus);
+ break;
+ }
+}
+
+uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
+{
+ IDEBus *bus = opaque;
+ IDEState *s = idebus_active_if(bus);
+ uint32_t addr;
+ int ret, hob;
+
+ addr = addr1 & 7;
+ /* FIXME: HOB readback uses bit 7, but it's always set right now */
+ //hob = s->select & (1 << 7);
+ hob = 0;
+ switch(addr) {
+ case 0:
+ ret = 0xff;
+ break;
+ case 1:
+ if ((!bus->ifs[0].bs && !bus->ifs[1].bs) ||
+ (s != bus->ifs && !s->bs))
+ ret = 0;
+ else if (!hob)
+ ret = s->error;
+ else
+ ret = s->hob_feature;
+ break;
+ case 2:
+ if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->nsector & 0xff;
+ else
+ ret = s->hob_nsector;
+ break;
+ case 3:
+ if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->sector;
+ else
+ ret = s->hob_sector;
+ break;
+ case 4:
+ if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->lcyl;
+ else
+ ret = s->hob_lcyl;
+ break;
+ case 5:
+ if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->hcyl;
+ else
+ ret = s->hob_hcyl;
+ break;
+ case 6:
+ if (!bus->ifs[0].bs && !bus->ifs[1].bs)
+ ret = 0;
+ else
+ ret = s->select;
+ break;
+ default:
+ case 7:
+ if ((!bus->ifs[0].bs && !bus->ifs[1].bs) ||
+ (s != bus->ifs && !s->bs))
+ ret = 0;
+ else
+ ret = s->status;
+ qemu_irq_lower(bus->irq);
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("ide: read addr=0x%x val=%02x\n", addr1, ret);
+#endif
+ return ret;
+}
+
+uint32_t ide_status_read(void *opaque, uint32_t addr)
+{
+ IDEBus *bus = opaque;
+ IDEState *s = idebus_active_if(bus);
+ int ret;
+
+ if ((!bus->ifs[0].bs && !bus->ifs[1].bs) ||
+ (s != bus->ifs && !s->bs))
+ ret = 0;
+ else
+ ret = s->status;
+#ifdef DEBUG_IDE
+ printf("ide: read status addr=0x%x val=%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEBus *bus = opaque;
+ IDEState *s;
+ int i;
+
+#ifdef DEBUG_IDE
+ printf("ide: write control addr=0x%x val=%02x\n", addr, val);
+#endif
+ /* common for both drives */
+ if (!(bus->cmd & IDE_CMD_RESET) &&
+ (val & IDE_CMD_RESET)) {
+ /* reset low to high */
+ for(i = 0;i < 2; i++) {
+ s = &bus->ifs[i];
+ s->status = BUSY_STAT | SEEK_STAT;
+ s->error = 0x01;
+ }
+ } else if ((bus->cmd & IDE_CMD_RESET) &&
+ !(val & IDE_CMD_RESET)) {
+ /* high to low */
+ for(i = 0;i < 2; i++) {
+ s = &bus->ifs[i];
+ if (s->drive_kind == IDE_CD)
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ else
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_signature(s);
+ }
+ }
+
+ bus->cmd = val;
+}
+
+void ide_data_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEBus *bus = opaque;
+ IDEState *s = idebus_active_if(bus);
+ uint8_t *p;
+
+ /* PIO data access allowed only when DRQ bit is set */
+ if (!(s->status & DRQ_STAT))
+ return;
+
+ p = s->data_ptr;
+ *(uint16_t *)p = le16_to_cpu(val);
+ p += 2;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+}
+
+uint32_t ide_data_readw(void *opaque, uint32_t addr)
+{
+ IDEBus *bus = opaque;
+ IDEState *s = idebus_active_if(bus);
+ uint8_t *p;
+ int ret;
+
+ /* PIO data access allowed only when DRQ bit is set */
+ if (!(s->status & DRQ_STAT))
+ return 0;
+
+ p = s->data_ptr;
+ ret = cpu_to_le16(*(uint16_t *)p);
+ p += 2;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+ return ret;
+}
+
+void ide_data_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEBus *bus = opaque;
+ IDEState *s = idebus_active_if(bus);
+ uint8_t *p;
+
+ /* PIO data access allowed only when DRQ bit is set */
+ if (!(s->status & DRQ_STAT))
+ return;
+
+ p = s->data_ptr;
+ *(uint32_t *)p = le32_to_cpu(val);
+ p += 4;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+}
+
+uint32_t ide_data_readl(void *opaque, uint32_t addr)
+{
+ IDEBus *bus = opaque;
+ IDEState *s = idebus_active_if(bus);
+ uint8_t *p;
+ int ret;
+
+ /* PIO data access allowed only when DRQ bit is set */
+ if (!(s->status & DRQ_STAT))
+ return 0;
+
+ p = s->data_ptr;
+ ret = cpu_to_le32(*(uint32_t *)p);
+ p += 4;
+ s->data_ptr = p;
+ if (p >= s->data_end)
+ s->end_transfer_func(s);
+ return ret;
+}
+
+static void ide_dummy_transfer_stop(IDEState *s)
+{
+ s->data_ptr = s->io_buffer;
+ s->data_end = s->io_buffer;
+ s->io_buffer[0] = 0xff;
+ s->io_buffer[1] = 0xff;
+ s->io_buffer[2] = 0xff;
+ s->io_buffer[3] = 0xff;
+}
+
+static void ide_reset(IDEState *s)
+{
+#ifdef DEBUG_IDE
+ printf("ide: reset\n");
+#endif
+ if (s->drive_kind == IDE_CFATA)
+ s->mult_sectors = 0;
+ else
+ s->mult_sectors = MAX_MULT_SECTORS;
+ /* ide regs */
+ s->feature = 0;
+ s->error = 0;
+ s->nsector = 0;
+ s->sector = 0;
+ s->lcyl = 0;
+ s->hcyl = 0;
+
+ /* lba48 */
+ s->hob_feature = 0;
+ s->hob_sector = 0;
+ s->hob_nsector = 0;
+ s->hob_lcyl = 0;
+ s->hob_hcyl = 0;
+
+ s->select = 0xa0;
+ s->status = READY_STAT | SEEK_STAT;
+
+ s->lba48 = 0;
+
+ /* ATAPI specific */
+ s->sense_key = 0;
+ s->asc = 0;
+ s->cdrom_changed = 0;
+ s->packet_transfer_size = 0;
+ s->elementary_transfer_size = 0;
+ s->io_buffer_index = 0;
+ s->cd_sector_size = 0;
+ s->atapi_dma = 0;
+ /* ATA DMA state */
+ s->io_buffer_size = 0;
+ s->req_nb_sectors = 0;
+
+ ide_set_signature(s);
+ /* init the transfer handler so that 0xffff is returned on data
+ accesses */
+ s->end_transfer_func = ide_dummy_transfer_stop;
+ ide_dummy_transfer_stop(s);
+ s->media_changed = 0;
+}
+
+void ide_bus_reset(IDEBus *bus)
+{
+ bus->unit = 0;
+ bus->cmd = 0;
+ ide_reset(&bus->ifs[0]);
+ ide_reset(&bus->ifs[1]);
+ ide_clear_hob(bus);
+
+ /* pending async DMA */
+ if (bus->dma->aiocb) {
+#ifdef DEBUG_AIO
+ printf("aio_cancel\n");
+#endif
+ bdrv_aio_cancel(bus->dma->aiocb);
+ bus->dma->aiocb = NULL;
+ }
+
+ /* reset dma provider too */
+ bus->dma->ops->reset(bus->dma);
+}
+
+int ide_init_drive(IDEState *s, BlockDriverState *bs,
+ const char *version, const char *serial)
+{
+ int cylinders, heads, secs;
+ uint64_t nb_sectors;
+
+ s->bs = bs;
+ bdrv_get_geometry(bs, &nb_sectors);
+ bdrv_guess_geometry(bs, &cylinders, &heads, &secs);
+ if (cylinders < 1 || cylinders > 16383) {
+ error_report("cyls must be between 1 and 16383");
+ return -1;
+ }
+ if (heads < 1 || heads > 16) {
+ error_report("heads must be between 1 and 16");
+ return -1;
+ }
+ if (secs < 1 || secs > 63) {
+ error_report("secs must be between 1 and 63");
+ return -1;
+ }
+ s->cylinders = cylinders;
+ s->heads = heads;
+ s->sectors = secs;
+ s->nb_sectors = nb_sectors;
+ /* The SMART values should be preserved across power cycles
+ but they aren't. */
+ s->smart_enabled = 1;
+ 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;
+ bdrv_set_change_cb(bs, cdrom_change_cb, s);
+ bs->buffer_alignment = 2048;
+ } else {
+ if (!bdrv_is_inserted(s->bs)) {
+ error_report("Device needs media, but drive is empty");
+ return -1;
+ }
+ if (bdrv_is_read_only(bs)) {
+ error_report("Can't use a read-only drive");
+ return -1;
+ }
+ }
+ if (serial) {
+ strncpy(s->drive_serial_str, serial, sizeof(s->drive_serial_str));
+ } else {
+ snprintf(s->drive_serial_str, sizeof(s->drive_serial_str),
+ "QM%05d", s->drive_serial);
+ }
+ if (version) {
+ pstrcpy(s->version, sizeof(s->version), version);
+ } else {
+ pstrcpy(s->version, sizeof(s->version), QEMU_VERSION);
+ }
+
+ ide_reset(s);
+ bdrv_set_removable(bs, s->drive_kind == IDE_CD);
+ return 0;
+}
+
+static void ide_init1(IDEBus *bus, int unit)
+{
+ static int drive_serial = 1;
+ IDEState *s = &bus->ifs[unit];
+
+ s->bus = bus;
+ s->unit = unit;
+ s->drive_serial = drive_serial++;
+ /* we need at least 2k alignment for accessing CDROMs using O_DIRECT */
+ s->io_buffer = qemu_memalign(2048, IDE_DMA_BUF_SECTORS*512 + 4);
+ s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4;
+ s->smart_selftest_data = qemu_blockalign(s->bs, 512);
+ s->sector_write_timer = qemu_new_timer(vm_clock,
+ ide_sector_write_timer_cb, s);
+}
+
+static void ide_nop_start(IDEDMA *dma, IDEState *s,
+ BlockDriverCompletionFunc *cb)
+{
+}
+
+static int ide_nop(IDEDMA *dma)
+{
+ return 0;
+}
+
+static int ide_nop_int(IDEDMA *dma, int x)
+{
+ return 0;
+}
+
+static void ide_nop_restart(void *opaque, int x, int y)
+{
+}
+
+static const IDEDMAOps ide_dma_nop_ops = {
+ .start_dma = ide_nop_start,
+ .start_transfer = ide_nop,
+ .prepare_buf = ide_nop_int,
+ .rw_buf = ide_nop_int,
+ .set_unit = ide_nop_int,
+ .add_status = ide_nop_int,
+ .set_inactive = ide_nop,
+ .restart_cb = ide_nop_restart,
+ .reset = ide_nop,
+};
+
+static IDEDMA ide_dma_nop = {
+ .ops = &ide_dma_nop_ops,
+ .aiocb = NULL,
+};
+
+void ide_init2(IDEBus *bus, qemu_irq irq)
+{
+ int i;
+
+ for(i = 0; i < 2; i++) {
+ ide_init1(bus, i);
+ ide_reset(&bus->ifs[i]);
+ }
+ bus->irq = irq;
+ bus->dma = &ide_dma_nop;
+}
+
+/* TODO convert users to qdev and remove */
+void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0,
+ DriveInfo *hd1, qemu_irq irq)
+{
+ int i;
+ DriveInfo *dinfo;
+
+ for(i = 0; i < 2; i++) {
+ dinfo = i == 0 ? hd0 : hd1;
+ ide_init1(bus, i);
+ if (dinfo) {
+ if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, NULL,
+ *dinfo->serial ? dinfo->serial : NULL) < 0) {
+ error_report("Can't set up IDE drive %s", dinfo->id);
+ exit(1);
+ }
+ } else {
+ ide_reset(&bus->ifs[i]);
+ }
+ }
+ bus->irq = irq;
+ bus->dma = &ide_dma_nop;
+}
+
+void ide_init_ioport(IDEBus *bus, int iobase, int iobase2)
+{
+ register_ioport_write(iobase, 8, 1, ide_ioport_write, bus);
+ register_ioport_read(iobase, 8, 1, ide_ioport_read, bus);
+ if (iobase2) {
+ register_ioport_read(iobase2, 1, 1, ide_status_read, bus);
+ register_ioport_write(iobase2, 1, 1, ide_cmd_write, bus);
+ }
+
+ /* data ports */
+ register_ioport_write(iobase, 2, 2, ide_data_writew, bus);
+ register_ioport_read(iobase, 2, 2, ide_data_readw, bus);
+ register_ioport_write(iobase, 4, 4, ide_data_writel, bus);
+ register_ioport_read(iobase, 4, 4, ide_data_readl, bus);
+}
+
+static bool is_identify_set(void *opaque, int version_id)
+{
+ IDEState *s = opaque;
+
+ return s->identify_set != 0;
+}
+
+static EndTransferFunc* transfer_end_table[] = {
+ ide_sector_read,
+ ide_sector_write,
+ ide_transfer_stop,
+ ide_atapi_cmd_reply_end,
+ ide_atapi_cmd,
+ ide_dummy_transfer_stop,
+};
+
+static int transfer_end_table_idx(EndTransferFunc *fn)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(transfer_end_table); i++)
+ if (transfer_end_table[i] == fn)
+ return i;
+
+ return -1;
+}
+
+static int ide_drive_post_load(void *opaque, int version_id)
+{
+ IDEState *s = opaque;
+
+ if (version_id < 3) {
+ if (s->sense_key == SENSE_UNIT_ATTENTION &&
+ s->asc == ASC_MEDIUM_MAY_HAVE_CHANGED) {
+ s->cdrom_changed = 1;
+ }
+ }
+ return 0;
+}
+
+static int ide_drive_pio_post_load(void *opaque, int version_id)
+{
+ IDEState *s = opaque;
+
+ if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) {
+ return -EINVAL;
+ }
+ s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
+ s->data_ptr = s->io_buffer + s->cur_io_buffer_offset;
+ s->data_end = s->data_ptr + s->cur_io_buffer_len;
+
+ return 0;
+}
+
+static void ide_drive_pio_pre_save(void *opaque)
+{
+ IDEState *s = opaque;
+ int idx;
+
+ s->cur_io_buffer_offset = s->data_ptr - s->io_buffer;
+ s->cur_io_buffer_len = s->data_end - s->data_ptr;
+
+ idx = transfer_end_table_idx(s->end_transfer_func);
+ if (idx == -1) {
+ fprintf(stderr, "%s: invalid end_transfer_func for DRQ_STAT\n",
+ __func__);
+ s->end_transfer_fn_idx = 2;
+ } else {
+ s->end_transfer_fn_idx = idx;
+ }
+}
+
+static bool ide_drive_pio_state_needed(void *opaque)
+{
+ IDEState *s = opaque;
+
+ return (s->status & DRQ_STAT) != 0;
+}
+
+const VMStateDescription vmstate_ide_drive_pio_state = {
+ .name = "ide_drive/pio_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = ide_drive_pio_pre_save,
+ .post_load = ide_drive_pio_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(req_nb_sectors, IDEState),
+ VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1,
+ vmstate_info_uint8, uint8_t),
+ VMSTATE_INT32(cur_io_buffer_offset, IDEState),
+ VMSTATE_INT32(cur_io_buffer_len, IDEState),
+ VMSTATE_UINT8(end_transfer_fn_idx, IDEState),
+ VMSTATE_INT32(elementary_transfer_size, IDEState),
+ VMSTATE_INT32(packet_transfer_size, IDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+const VMStateDescription vmstate_ide_drive = {
+ .name = "ide_drive",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = ide_drive_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(mult_sectors, IDEState),
+ VMSTATE_INT32(identify_set, IDEState),
+ VMSTATE_BUFFER_TEST(identify_data, IDEState, is_identify_set),
+ VMSTATE_UINT8(feature, IDEState),
+ VMSTATE_UINT8(error, IDEState),
+ VMSTATE_UINT32(nsector, IDEState),
+ VMSTATE_UINT8(sector, IDEState),
+ VMSTATE_UINT8(lcyl, IDEState),
+ VMSTATE_UINT8(hcyl, IDEState),
+ VMSTATE_UINT8(hob_feature, IDEState),
+ VMSTATE_UINT8(hob_sector, IDEState),
+ VMSTATE_UINT8(hob_nsector, IDEState),
+ VMSTATE_UINT8(hob_lcyl, IDEState),
+ VMSTATE_UINT8(hob_hcyl, IDEState),
+ VMSTATE_UINT8(select, IDEState),
+ VMSTATE_UINT8(status, IDEState),
+ VMSTATE_UINT8(lba48, IDEState),
+ VMSTATE_UINT8(sense_key, IDEState),
+ VMSTATE_UINT8(asc, IDEState),
+ VMSTATE_UINT8_V(cdrom_changed, IDEState, 3),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_ide_drive_pio_state,
+ .needed = ide_drive_pio_state_needed,
+ }, {
+ /* empty */
+ }
+ }
+};
+
+const VMStateDescription vmstate_ide_bus = {
+ .name = "ide_bus",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(cmd, IDEBus),
+ VMSTATE_UINT8(unit, IDEBus),
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
new file mode 100644
index 0000000..f242d7a
--- /dev/null
+++ b/hw/ide/ich.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU ICH Emulation
+ *
+ * Copyright (c) 2010 Sebastian Herbszt <herbszt@gmx.de>
+ * Copyright (c) 2010 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * lspci dump of a ICH-9 real device
+ *
+ * 00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] (rev 02) (prog-if 01 [AHCI 1.0])
+ * Subsystem: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922]
+ * Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+
+ * Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
+ * Latency: 0
+ * Interrupt: pin B routed to IRQ 222
+ * Region 0: I/O ports at d000 [size=8]
+ * Region 1: I/O ports at cc00 [size=4]
+ * Region 2: I/O ports at c880 [size=8]
+ * Region 3: I/O ports at c800 [size=4]
+ * Region 4: I/O ports at c480 [size=32]
+ * Region 5: Memory at febf9000 (32-bit, non-prefetchable) [size=2K]
+ * Capabilities: [80] Message Signalled Interrupts: Mask- 64bit- Count=1/16 Enable+
+ * Address: fee0f00c Data: 41d9
+ * Capabilities: [70] Power Management version 3
+ * Flags: PMEClk- DSI- D1- D2- AuxCurrent=0mA PME(D0-,D1-,D2-,D3hot+,D3cold-)
+ * Status: D0 PME-Enable- DSel=0 DScale=0 PME-
+ * Capabilities: [a8] SATA HBA <?>
+ * Capabilities: [b0] Vendor Specific Information <?>
+ * Kernel driver in use: ahci
+ * Kernel modules: ahci
+ * 00: 86 80 22 29 07 04 b0 02 02 01 06 01 00 00 00 00
+ * 10: 01 d0 00 00 01 cc 00 00 81 c8 00 00 01 c8 00 00
+ * 20: 81 c4 00 00 00 90 bf fe 00 00 00 00 86 80 22 29
+ * 30: 00 00 00 00 80 00 00 00 00 00 00 00 0f 02 00 00
+ * 40: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00
+ * 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * 70: 01 a8 03 40 08 00 00 00 00 00 00 00 00 00 00 00
+ * 80: 05 70 09 00 0c f0 e0 fe d9 41 00 00 00 00 00 00
+ * 90: 40 00 0f 82 93 01 00 00 00 00 00 00 00 00 00 00
+ * a0: ac 00 00 00 0a 00 12 00 12 b0 10 00 48 00 00 00
+ * b0: 09 00 06 20 00 00 00 00 00 00 00 00 00 00 00 00
+ * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ * f0: 00 00 00 00 00 00 00 00 86 0f 02 00 00 00 00 00
+ *
+ */
+
+#include <hw/hw.h>
+#include <hw/msi.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+#include <hw/isa.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/pci.h>
+#include <hw/ide/ahci.h>
+
+static int pci_ich9_ahci_init(PCIDevice *dev)
+{
+ struct AHCIPCIState *d;
+ d = DO_UPCAST(struct AHCIPCIState, card, dev);
+
+ pci_config_set_vendor_id(d->card.config, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(d->card.config, PCI_DEVICE_ID_INTEL_82801IR);
+
+ pci_config_set_class(d->card.config, PCI_CLASS_STORAGE_SATA);
+ pci_config_set_revision(d->card.config, 0x02);
+ pci_config_set_prog_interface(d->card.config, AHCI_PROGMODE_MAJOR_REV_1);
+
+ d->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */
+ d->card.config[PCI_LATENCY_TIMER] = 0x00; /* Latency timer */
+ pci_config_set_interrupt_pin(d->card.config, 1);
+
+ /* XXX Software should program this register */
+ d->card.config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */
+
+ qemu_register_reset(ahci_reset, d);
+
+ /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */
+ pci_register_bar(&d->card, 5, 0x1000, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ ahci_pci_map);
+
+ msi_init(dev, 0x50, 1, true, false);
+
+ ahci_init(&d->ahci, &dev->qdev, 6);
+ d->ahci.irq = d->card.irq[0];
+
+ return 0;
+}
+
+static int pci_ich9_uninit(PCIDevice *dev)
+{
+ struct AHCIPCIState *d;
+ d = DO_UPCAST(struct AHCIPCIState, card, dev);
+
+ if (msi_enabled(dev)) {
+ msi_uninit(dev);
+ }
+
+ qemu_unregister_reset(ahci_reset, d);
+ ahci_uninit(&d->ahci);
+
+ return 0;
+}
+
+static void pci_ich9_write_config(PCIDevice *pci, uint32_t addr,
+ uint32_t val, int len)
+{
+ pci_default_write_config(pci, addr, val, len);
+ msi_write_config(pci, addr, val, len);
+}
+
+static PCIDeviceInfo ich_ahci_info[] = {
+ {
+ .qdev.name = "ich9-ahci",
+ .qdev.alias = "ahci",
+ .qdev.size = sizeof(AHCIPCIState),
+ .init = pci_ich9_ahci_init,
+ .exit = pci_ich9_uninit,
+ .config_write = pci_ich9_write_config,
+ },{
+ /* end of list */
+ }
+};
+
+static void ich_ahci_register(void)
+{
+ pci_qdev_register_many(ich_ahci_info);
+}
+device_init(ich_ahci_register);
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
new file mode 100644
index 0000000..d533fb6
--- /dev/null
+++ b/hw/ide/internal.h
@@ -0,0 +1,571 @@
+#ifndef HW_IDE_INTERNAL_H
+#define HW_IDE_INTERNAL_H
+
+/*
+ * QEMU IDE Emulation -- internal header file
+ * only files in hw/ide/ are supposed to include this file.
+ * non-internal declarations are in hw/ide.h
+ */
+#include <hw/ide.h>
+#include "block_int.h"
+#include "iorange.h"
+
+/* debug IDE devices */
+//#define DEBUG_IDE
+//#define DEBUG_IDE_ATAPI
+//#define DEBUG_AIO
+#define USE_DMA_CDROM
+
+typedef struct IDEBus IDEBus;
+typedef struct IDEDevice IDEDevice;
+typedef struct IDEDeviceInfo IDEDeviceInfo;
+typedef struct IDEState IDEState;
+typedef struct IDEDMA IDEDMA;
+typedef struct IDEDMAOps IDEDMAOps;
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define SRV_STAT 0x10
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define MCR_ERR 0x08 /* media change request */
+#define ID_ERR 0x10 /* ID field not found */
+#define MC_ERR 0x20 /* media changed */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
+#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
+
+/* Bits of HD_NSECTOR */
+#define CD 0x01
+#define IO 0x02
+#define REL 0x04
+#define TAG_MASK 0xf8
+
+#define IDE_CMD_RESET 0x04
+#define IDE_CMD_DISABLE_IRQ 0x02
+
+/* ATA/ATAPI Commands pre T13 Spec */
+#define WIN_NOP 0x00
+/*
+ * 0x01->0x02 Reserved
+ */
+#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */
+/*
+ * 0x04->0x07 Reserved
+ */
+#define WIN_SRST 0x08 /* ATAPI soft reset command */
+#define WIN_DEVICE_RESET 0x08
+/*
+ * 0x09->0x0F Reserved
+ */
+#define WIN_RECAL 0x10
+#define WIN_RESTORE WIN_RECAL
+/*
+ * 0x10->0x1F Reserved
+ */
+#define WIN_READ 0x20 /* 28-Bit */
+#define WIN_READ_ONCE 0x21 /* 28-Bit without retries */
+#define WIN_READ_LONG 0x22 /* 28-Bit */
+#define WIN_READ_LONG_ONCE 0x23 /* 28-Bit without retries */
+#define WIN_READ_EXT 0x24 /* 48-Bit */
+#define WIN_READDMA_EXT 0x25 /* 48-Bit */
+#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit */
+#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */
+/*
+ * 0x28
+ */
+#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */
+/*
+ * 0x2A->0x2F Reserved
+ */
+#define WIN_WRITE 0x30 /* 28-Bit */
+#define WIN_WRITE_ONCE 0x31 /* 28-Bit without retries */
+#define WIN_WRITE_LONG 0x32 /* 28-Bit */
+#define WIN_WRITE_LONG_ONCE 0x33 /* 28-Bit without retries */
+#define WIN_WRITE_EXT 0x34 /* 48-Bit */
+#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */
+#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */
+#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */
+#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */
+#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */
+/*
+ * 0x3A->0x3B Reserved
+ */
+#define WIN_WRITE_VERIFY 0x3C /* 28-Bit */
+/*
+ * 0x3D->0x3F Reserved
+ */
+#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */
+#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - without retries */
+#define WIN_VERIFY_EXT 0x42 /* 48-Bit */
+/*
+ * 0x43->0x4F Reserved
+ */
+#define WIN_FORMAT 0x50
+/*
+ * 0x51->0x5F Reserved
+ */
+#define WIN_INIT 0x60
+/*
+ * 0x61->0x5F Reserved
+ */
+#define WIN_SEEK 0x70 /* 0x70-0x7F Reserved */
+#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */
+#define WIN_DIAGNOSE 0x90
+#define WIN_SPECIFY 0x91 /* set drive geometry translation */
+#define WIN_DOWNLOAD_MICROCODE 0x92
+#define WIN_STANDBYNOW2 0x94
+#define CFA_IDLEIMMEDIATE 0x95 /* force drive to become "ready" */
+#define WIN_STANDBY2 0x96
+#define WIN_SETIDLE2 0x97
+#define WIN_CHECKPOWERMODE2 0x98
+#define WIN_SLEEPNOW2 0x99
+/*
+ * 0x9A VENDOR
+ */
+#define WIN_PACKETCMD 0xA0 /* Send a packet command. */
+#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
+#define WIN_QUEUED_SERVICE 0xA2
+#define WIN_SMART 0xB0 /* self-monitoring and reporting */
+#define CFA_ACCESS_METADATA_STORAGE 0xB8
+#define CFA_ERASE_SECTORS 0xC0 /* microdrives implement as NOP */
+#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/
+#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
+#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
+#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers */
+#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */
+#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - without retries */
+#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */
+#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - without retries */
+#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers */
+#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */
+#define WIN_GETMEDIASTATUS 0xDA
+#define WIN_ACKMEDIACHANGE 0xDB /* ATA-1, ATA-2 vendor */
+#define WIN_POSTBOOT 0xDC
+#define WIN_PREBOOT 0xDD
+#define WIN_DOORLOCK 0xDE /* lock door on removable drives */
+#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives */
+#define WIN_STANDBYNOW1 0xE0
+#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */
+#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */
+#define WIN_SETIDLE1 0xE3
+#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */
+#define WIN_CHECKPOWERMODE1 0xE5
+#define WIN_SLEEPNOW1 0xE6
+#define WIN_FLUSH_CACHE 0xE7
+#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */
+#define WIN_WRITE_SAME 0xE9 /* read ata-2 to use */
+ /* SET_FEATURES 0x22 or 0xDD */
+#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */
+#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
+#define WIN_MEDIAEJECT 0xED
+#define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */
+#define WIN_SETFEATURES 0xEF /* set special drive features */
+#define EXABYTE_ENABLE_NEST 0xF0
+#define IBM_SENSE_CONDITION 0xF0 /* measure disk temperature */
+#define WIN_SECURITY_SET_PASS 0xF1
+#define WIN_SECURITY_UNLOCK 0xF2
+#define WIN_SECURITY_ERASE_PREPARE 0xF3
+#define WIN_SECURITY_ERASE_UNIT 0xF4
+#define WIN_SECURITY_FREEZE_LOCK 0xF5
+#define CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP */
+#define WIN_SECURITY_DISABLE 0xF6
+#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */
+#define WIN_SET_MAX 0xF9
+#define DISABLE_SEAGATE 0xFB
+
+/* set to 1 set disable mult support */
+#define MAX_MULT_SECTORS 16
+
+#define IDE_DMA_BUF_SECTORS 256
+
+#if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS)
+#error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS"
+#endif
+
+/* ATAPI defines */
+
+#define ATAPI_PACKET_SIZE 12
+
+/* The generic packet command opcodes for CD/DVD Logical Units,
+ * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
+#define GPCMD_BLANK 0xa1
+#define GPCMD_CLOSE_TRACK 0x5b
+#define GPCMD_FLUSH_CACHE 0x35
+#define GPCMD_FORMAT_UNIT 0x04
+#define GPCMD_GET_CONFIGURATION 0x46
+#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
+#define GPCMD_GET_PERFORMANCE 0xac
+#define GPCMD_INQUIRY 0x12
+#define GPCMD_LOAD_UNLOAD 0xa6
+#define GPCMD_MECHANISM_STATUS 0xbd
+#define GPCMD_MODE_SELECT_10 0x55
+#define GPCMD_MODE_SENSE_10 0x5a
+#define GPCMD_PAUSE_RESUME 0x4b
+#define GPCMD_PLAY_AUDIO_10 0x45
+#define GPCMD_PLAY_AUDIO_MSF 0x47
+#define GPCMD_PLAY_AUDIO_TI 0x48
+#define GPCMD_PLAY_CD 0xbc
+#define GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define GPCMD_READ_10 0x28
+#define GPCMD_READ_12 0xa8
+#define GPCMD_READ_CDVD_CAPACITY 0x25
+#define GPCMD_READ_CD 0xbe
+#define GPCMD_READ_CD_MSF 0xb9
+#define GPCMD_READ_DISC_INFO 0x51
+#define GPCMD_READ_DVD_STRUCTURE 0xad
+#define GPCMD_READ_FORMAT_CAPACITIES 0x23
+#define GPCMD_READ_HEADER 0x44
+#define GPCMD_READ_TRACK_RZONE_INFO 0x52
+#define GPCMD_READ_SUBCHANNEL 0x42
+#define GPCMD_READ_TOC_PMA_ATIP 0x43
+#define GPCMD_REPAIR_RZONE_TRACK 0x58
+#define GPCMD_REPORT_KEY 0xa4
+#define GPCMD_REQUEST_SENSE 0x03
+#define GPCMD_RESERVE_RZONE_TRACK 0x53
+#define GPCMD_SCAN 0xba
+#define GPCMD_SEEK 0x2b
+#define GPCMD_SEND_DVD_STRUCTURE 0xad
+#define GPCMD_SEND_EVENT 0xa2
+#define GPCMD_SEND_KEY 0xa3
+#define GPCMD_SEND_OPC 0x54
+#define GPCMD_SET_READ_AHEAD 0xa7
+#define GPCMD_SET_STREAMING 0xb6
+#define GPCMD_START_STOP_UNIT 0x1b
+#define GPCMD_STOP_PLAY_SCAN 0x4e
+#define GPCMD_TEST_UNIT_READY 0x00
+#define GPCMD_VERIFY_10 0x2f
+#define GPCMD_WRITE_10 0x2a
+#define GPCMD_WRITE_AND_VERIFY_10 0x2e
+/* This is listed as optional in ATAPI 2.6, but is (curiously)
+ * missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji
+ * Table 377 as an MMC command for SCSi devices though... Most ATAPI
+ * drives support it. */
+#define GPCMD_SET_SPEED 0xbb
+/* This seems to be a SCSI specific CD-ROM opcode
+ * to play data at track/index */
+#define GPCMD_PLAYAUDIO_TI 0x48
+/*
+ * From MS Media Status Notification Support Specification. For
+ * older drives only.
+ */
+#define GPCMD_GET_MEDIA_STATUS 0xda
+#define GPCMD_MODE_SENSE_6 0x1a
+
+/* Mode page codes for mode sense/set */
+#define GPMODE_R_W_ERROR_PAGE 0x01
+#define GPMODE_WRITE_PARMS_PAGE 0x05
+#define GPMODE_AUDIO_CTL_PAGE 0x0e
+#define GPMODE_POWER_PAGE 0x1a
+#define GPMODE_FAULT_FAIL_PAGE 0x1c
+#define GPMODE_TO_PROTECT_PAGE 0x1d
+#define GPMODE_CAPABILITIES_PAGE 0x2a
+#define GPMODE_ALL_PAGES 0x3f
+/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
+ * of MODE_SENSE_POWER_PAGE */
+#define GPMODE_CDROM_PAGE 0x0d
+
+/*
+ * Based on values from <linux/cdrom.h> but extending CD_MINS
+ * to the maximum common size allowed by the Orange's Book ATIP
+ *
+ * 90 and 99 min CDs are also available but using them as the
+ * upper limit reduces the effectiveness of the heuristic to
+ * detect DVDs burned to less than 25% of their maximum capacity
+ */
+
+/* Some generally useful CD-ROM information */
+#define CD_MINS 80 /* max. minutes per CD */
+#define CD_SECS 60 /* seconds per minute */
+#define CD_FRAMES 75 /* frames per second */
+#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
+#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
+#define CD_MAX_SECTORS (CD_MAX_BYTES / 512)
+
+/*
+ * The MMC values are not IDE specific and might need to be moved
+ * to a common header if they are also needed for the SCSI emulation
+ */
+
+/* Profile list from MMC-6 revision 1 table 91 */
+#define MMC_PROFILE_NONE 0x0000
+#define MMC_PROFILE_CD_ROM 0x0008
+#define MMC_PROFILE_CD_R 0x0009
+#define MMC_PROFILE_CD_RW 0x000A
+#define MMC_PROFILE_DVD_ROM 0x0010
+#define MMC_PROFILE_DVD_R_SR 0x0011
+#define MMC_PROFILE_DVD_RAM 0x0012
+#define MMC_PROFILE_DVD_RW_RO 0x0013
+#define MMC_PROFILE_DVD_RW_SR 0x0014
+#define MMC_PROFILE_DVD_R_DL_SR 0x0015
+#define MMC_PROFILE_DVD_R_DL_JR 0x0016
+#define MMC_PROFILE_DVD_RW_DL 0x0017
+#define MMC_PROFILE_DVD_DDR 0x0018
+#define MMC_PROFILE_DVD_PLUS_RW 0x001A
+#define MMC_PROFILE_DVD_PLUS_R 0x001B
+#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A
+#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B
+#define MMC_PROFILE_BD_ROM 0x0040
+#define MMC_PROFILE_BD_R_SRM 0x0041
+#define MMC_PROFILE_BD_R_RRM 0x0042
+#define MMC_PROFILE_BD_RE 0x0043
+#define MMC_PROFILE_HDDVD_ROM 0x0050
+#define MMC_PROFILE_HDDVD_R 0x0051
+#define MMC_PROFILE_HDDVD_RAM 0x0052
+#define MMC_PROFILE_HDDVD_RW 0x0053
+#define MMC_PROFILE_HDDVD_R_DL 0x0058
+#define MMC_PROFILE_HDDVD_RW_DL 0x005A
+#define MMC_PROFILE_INVALID 0xFFFF
+
+#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */
+#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */
+#define ATAPI_INT_REASON_REL 0x04
+#define ATAPI_INT_REASON_TAG 0xf8
+
+/* same constants as bochs */
+#define ASC_ILLEGAL_OPCODE 0x20
+#define ASC_LOGICAL_BLOCK_OOR 0x21
+#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
+#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28
+#define ASC_INCOMPATIBLE_FORMAT 0x30
+#define ASC_MEDIUM_NOT_PRESENT 0x3a
+#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39
+#define ASC_MEDIA_REMOVAL_PREVENTED 0x53
+
+#define CFA_NO_ERROR 0x00
+#define CFA_MISC_ERROR 0x09
+#define CFA_INVALID_COMMAND 0x20
+#define CFA_INVALID_ADDRESS 0x21
+#define CFA_ADDRESS_OVERFLOW 0x2f
+
+#define SENSE_NONE 0
+#define SENSE_NOT_READY 2
+#define SENSE_ILLEGAL_REQUEST 5
+#define SENSE_UNIT_ATTENTION 6
+
+#define SMART_READ_DATA 0xd0
+#define SMART_READ_THRESH 0xd1
+#define SMART_ATTR_AUTOSAVE 0xd2
+#define SMART_SAVE_ATTR 0xd3
+#define SMART_EXECUTE_OFFLINE 0xd4
+#define SMART_READ_LOG 0xd5
+#define SMART_WRITE_LOG 0xd6
+#define SMART_ENABLE 0xd8
+#define SMART_DISABLE 0xd9
+#define SMART_STATUS 0xda
+
+typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind;
+
+typedef void EndTransferFunc(IDEState *);
+
+typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockDriverCompletionFunc *);
+typedef int DMAFunc(IDEDMA *);
+typedef int DMAIntFunc(IDEDMA *, int);
+typedef void DMARestartFunc(void *, int, int);
+
+/* NOTE: IDEState represents in fact one drive */
+struct IDEState {
+ IDEBus *bus;
+ uint8_t unit;
+ /* ide config */
+ IDEDriveKind drive_kind;
+ int cylinders, heads, sectors;
+ int64_t nb_sectors;
+ int mult_sectors;
+ int identify_set;
+ uint8_t identify_data[512];
+ int drive_serial;
+ char drive_serial_str[21];
+ /* ide regs */
+ uint8_t feature;
+ uint8_t error;
+ uint32_t nsector;
+ uint8_t sector;
+ uint8_t lcyl;
+ uint8_t hcyl;
+ /* other part of tf for lba48 support */
+ uint8_t hob_feature;
+ uint8_t hob_nsector;
+ uint8_t hob_sector;
+ uint8_t hob_lcyl;
+ uint8_t hob_hcyl;
+
+ uint8_t select;
+ uint8_t status;
+
+ /* set for lba48 access */
+ uint8_t lba48;
+ BlockDriverState *bs;
+ char version[9];
+ /* ATAPI specific */
+ uint8_t sense_key;
+ uint8_t asc;
+ uint8_t cdrom_changed;
+ int packet_transfer_size;
+ int elementary_transfer_size;
+ int io_buffer_index;
+ int lba;
+ int cd_sector_size;
+ int atapi_dma; /* true if dma is requested for the packet cmd */
+ /* ATA DMA state */
+ int io_buffer_size;
+ QEMUSGList sg;
+ /* PIO transfer handling */
+ int req_nb_sectors; /* number of sectors per interrupt */
+ EndTransferFunc *end_transfer_func;
+ uint8_t *data_ptr;
+ uint8_t *data_end;
+ uint8_t *io_buffer;
+ /* PIO save/restore */
+ int32_t io_buffer_total_len;
+ int cur_io_buffer_offset;
+ int cur_io_buffer_len;
+ uint8_t end_transfer_fn_idx;
+ QEMUTimer *sector_write_timer; /* only used for win2k install hack */
+ uint32_t irq_count; /* counts IRQs when using win2k install hack */
+ /* CF-ATA extended error */
+ uint8_t ext_error;
+ /* CF-ATA metadata storage */
+ uint32_t mdata_size;
+ uint8_t *mdata_storage;
+ int media_changed;
+ int is_read;
+ /* SMART */
+ uint8_t smart_enabled;
+ uint8_t smart_autosave;
+ int smart_errors;
+ uint8_t smart_selftest_count;
+ uint8_t *smart_selftest_data;
+ /* AHCI */
+ int ncq_queues;
+};
+
+struct IDEDMAOps {
+ DMAStartFunc *start_dma;
+ DMAFunc *start_transfer;
+ DMAIntFunc *prepare_buf;
+ DMAIntFunc *rw_buf;
+ DMAIntFunc *set_unit;
+ DMAIntFunc *add_status;
+ DMAFunc *set_inactive;
+ DMARestartFunc *restart_cb;
+ DMAFunc *reset;
+};
+
+struct IDEDMA {
+ const struct IDEDMAOps *ops;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ BlockDriverAIOCB *aiocb;
+};
+
+struct IDEBus {
+ BusState qbus;
+ IDEDevice *master;
+ IDEDevice *slave;
+ IDEState ifs[2];
+ int bus_id;
+ IDEDMA *dma;
+ uint8_t unit;
+ uint8_t cmd;
+ qemu_irq irq;
+};
+
+struct IDEDevice {
+ DeviceState qdev;
+ uint32_t unit;
+ BlockConf conf;
+ char *version;
+ char *serial;
+};
+
+typedef int (*ide_qdev_initfn)(IDEDevice *dev);
+struct IDEDeviceInfo {
+ DeviceInfo qdev;
+ ide_qdev_initfn init;
+};
+
+#define BM_STATUS_DMAING 0x01
+#define BM_STATUS_ERROR 0x02
+#define BM_STATUS_INT 0x04
+#define BM_STATUS_DMA_RETRY 0x08
+#define BM_STATUS_PIO_RETRY 0x10
+#define BM_STATUS_RETRY_READ 0x20
+#define BM_STATUS_RETRY_FLUSH 0x40
+
+#define BM_CMD_START 0x01
+#define BM_CMD_READ 0x08
+
+static inline IDEState *idebus_active_if(IDEBus *bus)
+{
+ return bus->ifs + bus->unit;
+}
+
+static inline void ide_set_irq(IDEBus *bus)
+{
+ if (!(bus->cmd & IDE_CMD_DISABLE_IRQ)) {
+ qemu_irq_raise(bus->irq);
+ }
+}
+
+/* hw/ide/core.c */
+extern const VMStateDescription vmstate_ide_bus;
+
+#define VMSTATE_IDE_BUS(_field, _state) \
+ VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_bus, IDEBus)
+
+#define VMSTATE_IDE_BUS_ARRAY(_field, _state, _num) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, _num, 1, vmstate_ide_bus, IDEBus)
+
+extern const VMStateDescription vmstate_ide_drive;
+
+#define VMSTATE_IDE_DRIVES(_field, _state) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState)
+
+void ide_bus_reset(IDEBus *bus);
+int64_t ide_get_sector(IDEState *s);
+void ide_set_sector(IDEState *s, int64_t sector_num);
+
+void ide_dma_error(IDEState *s);
+
+void ide_atapi_cmd_ok(IDEState *s);
+void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc);
+void ide_atapi_io_error(IDEState *s, int ret);
+
+void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t ide_ioport_read(void *opaque, uint32_t addr1);
+uint32_t ide_status_read(void *opaque, uint32_t addr);
+void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val);
+void ide_data_writew(void *opaque, uint32_t addr, uint32_t val);
+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,
+ 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,
+ DriveInfo *hd1, qemu_irq irq);
+void ide_init_ioport(IDEBus *bus, int iobase, int iobase2);
+
+void ide_exec_cmd(IDEBus *bus, uint32_t val);
+void ide_dma_cb(void *opaque, int ret);
+void ide_sector_write(IDEState *s);
+void ide_sector_read(IDEState *s);
+void ide_flush_cache(IDEState *s);
+
+/* hw/ide/qdev.c */
+void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id);
+IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive);
+
+#endif /* HW_IDE_INTERNAL_H */
diff --git a/hw/ide/isa.c b/hw/ide/isa.c
new file mode 100644
index 0000000..8c59c5a
--- /dev/null
+++ b/hw/ide/isa.c
@@ -0,0 +1,120 @@
+/*
+ * QEMU IDE Emulation: ISA Bus support.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/isa.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/internal.h>
+
+/***********************************************************/
+/* ISA IDE definitions */
+
+typedef struct ISAIDEState {
+ ISADevice dev;
+ IDEBus bus;
+ uint32_t iobase;
+ uint32_t iobase2;
+ uint32_t isairq;
+ qemu_irq irq;
+} ISAIDEState;
+
+static void isa_ide_reset(DeviceState *d)
+{
+ ISAIDEState *s = container_of(d, ISAIDEState, dev.qdev);
+
+ ide_bus_reset(&s->bus);
+}
+
+static const VMStateDescription vmstate_ide_isa = {
+ .name = "isa-ide",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_IDE_BUS(bus, ISAIDEState),
+ VMSTATE_IDE_DRIVES(bus.ifs, ISAIDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int isa_ide_initfn(ISADevice *dev)
+{
+ ISAIDEState *s = DO_UPCAST(ISAIDEState, dev, dev);
+
+ ide_bus_new(&s->bus, &s->dev.qdev, 0);
+ ide_init_ioport(&s->bus, s->iobase, s->iobase2);
+ isa_init_irq(dev, &s->irq, s->isairq);
+ isa_init_ioport_range(dev, s->iobase, 8);
+ isa_init_ioport(dev, s->iobase2);
+ ide_init2(&s->bus, s->irq);
+ vmstate_register(&dev->qdev, 0, &vmstate_ide_isa, s);
+ return 0;
+};
+
+ISADevice *isa_ide_init(int iobase, int iobase2, int isairq,
+ DriveInfo *hd0, DriveInfo *hd1)
+{
+ ISADevice *dev;
+ ISAIDEState *s;
+
+ dev = isa_create("isa-ide");
+ qdev_prop_set_uint32(&dev->qdev, "iobase", iobase);
+ qdev_prop_set_uint32(&dev->qdev, "iobase2", iobase2);
+ qdev_prop_set_uint32(&dev->qdev, "irq", isairq);
+ if (qdev_init(&dev->qdev) < 0)
+ return NULL;
+
+ s = DO_UPCAST(ISAIDEState, dev, dev);
+ if (hd0)
+ ide_create_drive(&s->bus, 0, hd0);
+ if (hd1)
+ ide_create_drive(&s->bus, 1, hd1);
+ return dev;
+}
+
+static ISADeviceInfo isa_ide_info = {
+ .qdev.name = "isa-ide",
+ .qdev.fw_name = "ide",
+ .qdev.size = sizeof(ISAIDEState),
+ .init = isa_ide_initfn,
+ .qdev.reset = isa_ide_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("iobase", ISAIDEState, iobase, 0x1f0),
+ DEFINE_PROP_HEX32("iobase2", ISAIDEState, iobase2, 0x3f6),
+ DEFINE_PROP_UINT32("irq", ISAIDEState, isairq, 14),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void isa_ide_register_devices(void)
+{
+ isa_qdev_register(&isa_ide_info);
+}
+
+device_init(isa_ide_register_devices)
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
new file mode 100644
index 0000000..c1b4caa
--- /dev/null
+++ b/hw/ide/macio.c
@@ -0,0 +1,329 @@
+/*
+ * QEMU IDE Emulation: MacIO support.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/ppc_mac.h>
+#include <hw/mac_dbdma.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/internal.h>
+
+/***********************************************************/
+/* MacIO based PowerPC IDE */
+
+typedef struct MACIOIDEState {
+ IDEBus bus;
+ BlockDriverAIOCB *aiocb;
+} MACIOIDEState;
+
+#define MACIO_PAGE_SIZE 4096
+
+static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
+{
+ DBDMA_io *io = opaque;
+ MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
+
+ if (ret < 0) {
+ m->aiocb = NULL;
+ qemu_sglist_destroy(&s->sg);
+ ide_atapi_io_error(s, ret);
+ io->dma_end(opaque);
+ return;
+ }
+
+ if (s->io_buffer_size > 0) {
+ m->aiocb = NULL;
+ qemu_sglist_destroy(&s->sg);
+
+ s->packet_transfer_size -= s->io_buffer_size;
+
+ s->io_buffer_index += s->io_buffer_size;
+ s->lba += s->io_buffer_index >> 11;
+ s->io_buffer_index &= 0x7ff;
+ }
+
+ if (s->packet_transfer_size <= 0)
+ ide_atapi_cmd_ok(s);
+
+ if (io->len == 0) {
+ io->dma_end(opaque);
+ return;
+ }
+
+ /* launch next transfer */
+
+ s->io_buffer_size = io->len;
+
+ qemu_sglist_init(&s->sg, io->len / MACIO_PAGE_SIZE + 1);
+ qemu_sglist_add(&s->sg, io->addr, io->len);
+ io->addr += io->len;
+ io->len = 0;
+
+ m->aiocb = dma_bdrv_read(s->bs, &s->sg,
+ (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9),
+ pmac_ide_atapi_transfer_cb, io);
+ if (!m->aiocb) {
+ qemu_sglist_destroy(&s->sg);
+ /* Note: media not present is the most likely case */
+ ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ASC_MEDIUM_NOT_PRESENT);
+ io->dma_end(opaque);
+ return;
+ }
+}
+
+static void pmac_ide_transfer_cb(void *opaque, int ret)
+{
+ DBDMA_io *io = opaque;
+ MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
+ int n;
+ int64_t sector_num;
+
+ if (ret < 0) {
+ m->aiocb = NULL;
+ qemu_sglist_destroy(&s->sg);
+ ide_dma_error(s);
+ io->dma_end(io);
+ return;
+ }
+
+ sector_num = ide_get_sector(s);
+ if (s->io_buffer_size > 0) {
+ m->aiocb = NULL;
+ qemu_sglist_destroy(&s->sg);
+ n = (s->io_buffer_size + 0x1ff) >> 9;
+ sector_num += n;
+ ide_set_sector(s, sector_num);
+ s->nsector -= n;
+ }
+
+ /* end of transfer ? */
+ if (s->nsector == 0) {
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s->bus);
+ }
+
+ /* end of DMA ? */
+
+ if (io->len == 0) {
+ io->dma_end(io);
+ return;
+ }
+
+ /* launch next transfer */
+
+ s->io_buffer_index = 0;
+ s->io_buffer_size = io->len;
+
+ qemu_sglist_init(&s->sg, io->len / MACIO_PAGE_SIZE + 1);
+ qemu_sglist_add(&s->sg, io->addr, io->len);
+ io->addr += io->len;
+ io->len = 0;
+
+ if (s->is_read)
+ m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num,
+ pmac_ide_transfer_cb, io);
+ else
+ m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num,
+ pmac_ide_transfer_cb, io);
+ if (!m->aiocb)
+ pmac_ide_transfer_cb(io, -1);
+}
+
+static void pmac_ide_transfer(DBDMA_io *io)
+{
+ MACIOIDEState *m = io->opaque;
+ IDEState *s = idebus_active_if(&m->bus);
+
+ s->io_buffer_size = 0;
+ if (s->drive_kind == IDE_CD) {
+ pmac_ide_atapi_transfer_cb(io, 0);
+ return;
+ }
+
+ pmac_ide_transfer_cb(io, 0);
+}
+
+static void pmac_ide_flush(DBDMA_io *io)
+{
+ MACIOIDEState *m = io->opaque;
+
+ if (m->aiocb)
+ qemu_aio_flush();
+}
+
+/* PowerMac IDE memory IO */
+static void pmac_ide_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ MACIOIDEState *d = opaque;
+
+ addr = (addr & 0xFFF) >> 4;
+ switch (addr) {
+ case 1 ... 7:
+ ide_ioport_write(&d->bus, addr, val);
+ break;
+ case 8:
+ case 22:
+ ide_cmd_write(&d->bus, 0, val);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr)
+{
+ uint8_t retval;
+ MACIOIDEState *d = opaque;
+
+ addr = (addr & 0xFFF) >> 4;
+ switch (addr) {
+ case 1 ... 7:
+ retval = ide_ioport_read(&d->bus, addr);
+ break;
+ case 8:
+ case 22:
+ retval = ide_status_read(&d->bus, 0);
+ break;
+ default:
+ retval = 0xFF;
+ break;
+ }
+ return retval;
+}
+
+static void pmac_ide_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ MACIOIDEState *d = opaque;
+
+ addr = (addr & 0xFFF) >> 4;
+ val = bswap16(val);
+ if (addr == 0) {
+ ide_data_writew(&d->bus, 0, val);
+ }
+}
+
+static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
+{
+ uint16_t retval;
+ MACIOIDEState *d = opaque;
+
+ addr = (addr & 0xFFF) >> 4;
+ if (addr == 0) {
+ retval = ide_data_readw(&d->bus, 0);
+ } else {
+ retval = 0xFFFF;
+ }
+ retval = bswap16(retval);
+ return retval;
+}
+
+static void pmac_ide_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ MACIOIDEState *d = opaque;
+
+ addr = (addr & 0xFFF) >> 4;
+ val = bswap32(val);
+ if (addr == 0) {
+ ide_data_writel(&d->bus, 0, val);
+ }
+}
+
+static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr)
+{
+ uint32_t retval;
+ MACIOIDEState *d = opaque;
+
+ addr = (addr & 0xFFF) >> 4;
+ if (addr == 0) {
+ retval = ide_data_readl(&d->bus, 0);
+ } else {
+ retval = 0xFFFFFFFF;
+ }
+ retval = bswap32(retval);
+ return retval;
+}
+
+static CPUWriteMemoryFunc * const pmac_ide_write[] = {
+ pmac_ide_writeb,
+ pmac_ide_writew,
+ pmac_ide_writel,
+};
+
+static CPUReadMemoryFunc * const pmac_ide_read[] = {
+ pmac_ide_readb,
+ pmac_ide_readw,
+ pmac_ide_readl,
+};
+
+static const VMStateDescription vmstate_pmac = {
+ .name = "ide",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_IDE_BUS(bus, MACIOIDEState),
+ VMSTATE_IDE_DRIVES(bus.ifs, MACIOIDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pmac_ide_reset(void *opaque)
+{
+ MACIOIDEState *d = opaque;
+
+ ide_bus_reset(&d->bus);
+}
+
+/* hd_table must contain 4 block drivers */
+/* PowerMac uses memory mapped registers, not I/O. Return the memory
+ I/O index to access the ide. */
+int pmac_ide_init (DriveInfo **hd_table, qemu_irq irq,
+ void *dbdma, int channel, qemu_irq dma_irq)
+{
+ MACIOIDEState *d;
+ int pmac_ide_memory;
+
+ d = qemu_mallocz(sizeof(MACIOIDEState));
+ ide_init2_with_non_qdev_drives(&d->bus, hd_table[0], hd_table[1], irq);
+
+ if (dbdma)
+ DBDMA_register_channel(dbdma, channel, dma_irq, pmac_ide_transfer, pmac_ide_flush, d);
+
+ pmac_ide_memory = cpu_register_io_memory(pmac_ide_read,
+ pmac_ide_write, d,
+ DEVICE_NATIVE_ENDIAN);
+ vmstate_register(NULL, 0, &vmstate_pmac, d);
+ qemu_register_reset(pmac_ide_reset, d);
+
+ return pmac_ide_memory;
+}
diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c
new file mode 100644
index 0000000..2ceeb87
--- /dev/null
+++ b/hw/ide/microdrive.c
@@ -0,0 +1,551 @@
+/*
+ * QEMU IDE Emulation: microdrive (CF / PCMCIA)
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/pcmcia.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/internal.h>
+
+/***********************************************************/
+/* CF-ATA Microdrive */
+
+#define METADATA_SIZE 0x20
+
+/* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */
+typedef struct {
+ IDEBus bus;
+ PCMCIACardState card;
+ uint32_t attr_base;
+ uint32_t io_base;
+
+ /* Card state */
+ uint8_t opt;
+ uint8_t stat;
+ uint8_t pins;
+
+ uint8_t ctrl;
+ uint16_t io;
+ uint8_t cycle;
+} MicroDriveState;
+
+/* Register bitfields */
+enum md_opt {
+ OPT_MODE_MMAP = 0,
+ OPT_MODE_IOMAP16 = 1,
+ OPT_MODE_IOMAP1 = 2,
+ OPT_MODE_IOMAP2 = 3,
+ OPT_MODE = 0x3f,
+ OPT_LEVIREQ = 0x40,
+ OPT_SRESET = 0x80,
+};
+enum md_cstat {
+ STAT_INT = 0x02,
+ STAT_PWRDWN = 0x04,
+ STAT_XE = 0x10,
+ STAT_IOIS8 = 0x20,
+ STAT_SIGCHG = 0x40,
+ STAT_CHANGED = 0x80,
+};
+enum md_pins {
+ PINS_MRDY = 0x02,
+ PINS_CRDY = 0x20,
+};
+enum md_ctrl {
+ CTRL_IEN = 0x02,
+ CTRL_SRST = 0x04,
+};
+
+static inline void md_interrupt_update(MicroDriveState *s)
+{
+ if (!s->card.slot)
+ return;
+
+ qemu_set_irq(s->card.slot->irq,
+ !(s->stat & STAT_INT) && /* Inverted */
+ !(s->ctrl & (CTRL_IEN | CTRL_SRST)) &&
+ !(s->opt & OPT_SRESET));
+}
+
+static void md_set_irq(void *opaque, int irq, int level)
+{
+ MicroDriveState *s = opaque;
+ if (level)
+ s->stat |= STAT_INT;
+ else
+ s->stat &= ~STAT_INT;
+
+ md_interrupt_update(s);
+}
+
+static void md_reset(MicroDriveState *s)
+{
+ s->opt = OPT_MODE_MMAP;
+ s->stat = 0;
+ s->pins = 0;
+ s->cycle = 0;
+ s->ctrl = 0;
+ ide_bus_reset(&s->bus);
+}
+
+static uint8_t md_attr_read(void *opaque, uint32_t at)
+{
+ MicroDriveState *s = opaque;
+ if (at < s->attr_base) {
+ if (at < s->card.cis_len)
+ return s->card.cis[at];
+ else
+ return 0x00;
+ }
+
+ at -= s->attr_base;
+
+ switch (at) {
+ case 0x00: /* Configuration Option Register */
+ return s->opt;
+ case 0x02: /* Card Configuration Status Register */
+ if (s->ctrl & CTRL_IEN)
+ return s->stat & ~STAT_INT;
+ else
+ return s->stat;
+ case 0x04: /* Pin Replacement Register */
+ return (s->pins & PINS_CRDY) | 0x0c;
+ case 0x06: /* Socket and Copy Register */
+ return 0x00;
+#ifdef VERBOSE
+ default:
+ printf("%s: Bad attribute space register %02x\n", __FUNCTION__, at);
+#endif
+ }
+
+ return 0;
+}
+
+static void md_attr_write(void *opaque, uint32_t at, uint8_t value)
+{
+ MicroDriveState *s = opaque;
+ at -= s->attr_base;
+
+ switch (at) {
+ case 0x00: /* Configuration Option Register */
+ s->opt = value & 0xcf;
+ if (value & OPT_SRESET)
+ md_reset(s);
+ md_interrupt_update(s);
+ break;
+ case 0x02: /* Card Configuration Status Register */
+ if ((s->stat ^ value) & STAT_PWRDWN)
+ s->pins |= PINS_CRDY;
+ s->stat &= 0x82;
+ s->stat |= value & 0x74;
+ md_interrupt_update(s);
+ /* Word 170 in Identify Device must be equal to STAT_XE */
+ break;
+ case 0x04: /* Pin Replacement Register */
+ s->pins &= PINS_CRDY;
+ s->pins |= value & PINS_MRDY;
+ break;
+ case 0x06: /* Socket and Copy Register */
+ break;
+ default:
+ printf("%s: Bad attribute space register %02x\n", __FUNCTION__, at);
+ }
+}
+
+static uint16_t md_common_read(void *opaque, uint32_t at)
+{
+ MicroDriveState *s = opaque;
+ IDEState *ifs;
+ uint16_t ret;
+ at -= s->io_base;
+
+ switch (s->opt & OPT_MODE) {
+ case OPT_MODE_MMAP:
+ if ((at & ~0x3ff) == 0x400)
+ at = 0;
+ break;
+ case OPT_MODE_IOMAP16:
+ at &= 0xf;
+ break;
+ case OPT_MODE_IOMAP1:
+ if ((at & ~0xf) == 0x3f0)
+ at -= 0x3e8;
+ else if ((at & ~0xf) == 0x1f0)
+ at -= 0x1f0;
+ break;
+ case OPT_MODE_IOMAP2:
+ if ((at & ~0xf) == 0x370)
+ at -= 0x368;
+ else if ((at & ~0xf) == 0x170)
+ at -= 0x170;
+ }
+
+ switch (at) {
+ case 0x0: /* Even RD Data */
+ case 0x8:
+ return ide_data_readw(&s->bus, 0);
+
+ /* TODO: 8-bit accesses */
+ if (s->cycle)
+ ret = s->io >> 8;
+ else {
+ s->io = ide_data_readw(&s->bus, 0);
+ ret = s->io & 0xff;
+ }
+ s->cycle = !s->cycle;
+ return ret;
+ case 0x9: /* Odd RD Data */
+ return s->io >> 8;
+ case 0xd: /* Error */
+ return ide_ioport_read(&s->bus, 0x1);
+ case 0xe: /* Alternate Status */
+ ifs = idebus_active_if(&s->bus);
+ if (ifs->bs)
+ return ifs->status;
+ else
+ return 0;
+ case 0xf: /* Device Address */
+ ifs = idebus_active_if(&s->bus);
+ return 0xc2 | ((~ifs->select << 2) & 0x3c);
+ default:
+ return ide_ioport_read(&s->bus, at);
+ }
+
+ return 0;
+}
+
+static void md_common_write(void *opaque, uint32_t at, uint16_t value)
+{
+ MicroDriveState *s = opaque;
+ at -= s->io_base;
+
+ switch (s->opt & OPT_MODE) {
+ case OPT_MODE_MMAP:
+ if ((at & ~0x3ff) == 0x400)
+ at = 0;
+ break;
+ case OPT_MODE_IOMAP16:
+ at &= 0xf;
+ break;
+ case OPT_MODE_IOMAP1:
+ if ((at & ~0xf) == 0x3f0)
+ at -= 0x3e8;
+ else if ((at & ~0xf) == 0x1f0)
+ at -= 0x1f0;
+ break;
+ case OPT_MODE_IOMAP2:
+ if ((at & ~0xf) == 0x370)
+ at -= 0x368;
+ else if ((at & ~0xf) == 0x170)
+ at -= 0x170;
+ }
+
+ switch (at) {
+ case 0x0: /* Even WR Data */
+ case 0x8:
+ ide_data_writew(&s->bus, 0, value);
+ break;
+
+ /* TODO: 8-bit accesses */
+ if (s->cycle)
+ ide_data_writew(&s->bus, 0, s->io | (value << 8));
+ else
+ s->io = value & 0xff;
+ s->cycle = !s->cycle;
+ break;
+ case 0x9:
+ s->io = value & 0xff;
+ s->cycle = !s->cycle;
+ break;
+ case 0xd: /* Features */
+ ide_ioport_write(&s->bus, 0x1, value);
+ break;
+ case 0xe: /* Device Control */
+ s->ctrl = value;
+ if (value & CTRL_SRST)
+ md_reset(s);
+ md_interrupt_update(s);
+ break;
+ default:
+ if (s->stat & STAT_PWRDWN) {
+ s->pins |= PINS_CRDY;
+ s->stat &= ~STAT_PWRDWN;
+ }
+ ide_ioport_write(&s->bus, at, value);
+ }
+}
+
+static const VMStateDescription vmstate_microdrive = {
+ .name = "microdrive",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(opt, MicroDriveState),
+ VMSTATE_UINT8(stat, MicroDriveState),
+ VMSTATE_UINT8(pins, MicroDriveState),
+ VMSTATE_UINT8(ctrl, MicroDriveState),
+ VMSTATE_UINT16(io, MicroDriveState),
+ VMSTATE_UINT8(cycle, MicroDriveState),
+ VMSTATE_IDE_BUS(bus, MicroDriveState),
+ VMSTATE_IDE_DRIVES(bus.ifs, MicroDriveState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const uint8_t dscm1xxxx_cis[0x14a] = {
+ [0x000] = CISTPL_DEVICE, /* 5V Device Information */
+ [0x002] = 0x03, /* Tuple length = 4 bytes */
+ [0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */
+ [0x006] = 0x01, /* Size = 2K bytes */
+ [0x008] = CISTPL_ENDMARK,
+
+ [0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */
+ [0x00c] = 0x04, /* Tuple length = 4 byest */
+ [0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */
+ [0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */
+ [0x012] = 0x01, /* Size = 2K bytes */
+ [0x014] = CISTPL_ENDMARK,
+
+ [0x016] = CISTPL_JEDEC_C, /* JEDEC ID */
+ [0x018] = 0x02, /* Tuple length = 2 bytes */
+ [0x01a] = 0xdf, /* PC Card ATA with no Vpp required */
+ [0x01c] = 0x01,
+
+ [0x01e] = CISTPL_MANFID, /* Manufacture ID */
+ [0x020] = 0x04, /* Tuple length = 4 bytes */
+ [0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */
+ [0x024] = 0x00,
+ [0x026] = 0x00, /* PLMID_CARD = 0000 */
+ [0x028] = 0x00,
+
+ [0x02a] = CISTPL_VERS_1, /* Level 1 Version */
+ [0x02c] = 0x12, /* Tuple length = 23 bytes */
+ [0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */
+ [0x030] = 0x01, /* Minor Version = 1 */
+ [0x032] = 'I',
+ [0x034] = 'B',
+ [0x036] = 'M',
+ [0x038] = 0x00,
+ [0x03a] = 'm',
+ [0x03c] = 'i',
+ [0x03e] = 'c',
+ [0x040] = 'r',
+ [0x042] = 'o',
+ [0x044] = 'd',
+ [0x046] = 'r',
+ [0x048] = 'i',
+ [0x04a] = 'v',
+ [0x04c] = 'e',
+ [0x04e] = 0x00,
+ [0x050] = CISTPL_ENDMARK,
+
+ [0x052] = CISTPL_FUNCID, /* Function ID */
+ [0x054] = 0x02, /* Tuple length = 2 bytes */
+ [0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */
+ [0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */
+
+ [0x05a] = CISTPL_FUNCE, /* Function Extension */
+ [0x05c] = 0x02, /* Tuple length = 2 bytes */
+ [0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */
+ [0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */
+
+ [0x062] = CISTPL_FUNCE, /* Function Extension */
+ [0x064] = 0x03, /* Tuple length = 3 bytes */
+ [0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */
+ [0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */
+ [0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */
+
+ [0x06c] = CISTPL_CONFIG, /* Configuration */
+ [0x06e] = 0x05, /* Tuple length = 5 bytes */
+ [0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */
+ [0x072] = 0x07, /* TPCC_LAST = 7 */
+ [0x074] = 0x00, /* TPCC_RADR = 0200 */
+ [0x076] = 0x02,
+ [0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */
+
+ [0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x07c] = 0x0b, /* Tuple length = 11 bytes */
+ [0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */
+ [0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */
+ [0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */
+ [0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
+ [0x086] = 0x55, /* NomV: 5.0 V */
+ [0x088] = 0x4d, /* MinV: 4.5 V */
+ [0x08a] = 0x5d, /* MaxV: 5.5 V */
+ [0x08c] = 0x4e, /* Peakl: 450 mA */
+ [0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */
+ [0x090] = 0x00, /* Window descriptor: Window length = 0 */
+ [0x092] = 0x20, /* TPCE_MI: support power down mode, RW */
+
+ [0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x096] = 0x06, /* Tuple length = 6 bytes */
+ [0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */
+ [0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
+ [0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
+ [0x09e] = 0xb5, /* NomV: 3.3 V */
+ [0x0a0] = 0x1e,
+ [0x0a2] = 0x3e, /* Peakl: 350 mA */
+
+ [0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x0a6] = 0x0d, /* Tuple length = 13 bytes */
+ [0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */
+ [0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
+ [0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
+ [0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
+ [0x0b0] = 0x55, /* NomV: 5.0 V */
+ [0x0b2] = 0x4d, /* MinV: 4.5 V */
+ [0x0b4] = 0x5d, /* MaxV: 5.5 V */
+ [0x0b6] = 0x4e, /* Peakl: 450 mA */
+ [0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */
+ [0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */
+ [0x0bc] = 0xff, /* IRQ0..IRQ7 supported */
+ [0x0be] = 0xff, /* IRQ8..IRQ15 supported */
+ [0x0c0] = 0x20, /* TPCE_MI = support power down mode */
+
+ [0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x0c4] = 0x06, /* Tuple length = 6 bytes */
+ [0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */
+ [0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
+ [0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
+ [0x0cc] = 0xb5, /* NomV: 3.3 V */
+ [0x0ce] = 0x1e,
+ [0x0d0] = 0x3e, /* Peakl: 350 mA */
+
+ [0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x0d4] = 0x12, /* Tuple length = 18 bytes */
+ [0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */
+ [0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
+ [0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
+ [0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
+ [0x0de] = 0x55, /* NomV: 5.0 V */
+ [0x0e0] = 0x4d, /* MinV: 4.5 V */
+ [0x0e2] = 0x5d, /* MaxV: 5.5 V */
+ [0x0e4] = 0x4e, /* Peakl: 450 mA */
+ [0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */
+ [0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */
+ [0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */
+ [0x0ec] = 0x01,
+ [0x0ee] = 0x07, /* Address block length = 8 */
+ [0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */
+ [0x0f2] = 0x03,
+ [0x0f4] = 0x01, /* Address block length = 2 */
+ [0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */
+ [0x0f8] = 0x20, /* TPCE_MI = support power down mode */
+
+ [0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x0fc] = 0x06, /* Tuple length = 6 bytes */
+ [0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */
+ [0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
+ [0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
+ [0x104] = 0xb5, /* NomV: 3.3 V */
+ [0x106] = 0x1e,
+ [0x108] = 0x3e, /* Peakl: 350 mA */
+
+ [0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x10c] = 0x12, /* Tuple length = 18 bytes */
+ [0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */
+ [0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
+ [0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
+ [0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
+ [0x116] = 0x55, /* NomV: 5.0 V */
+ [0x118] = 0x4d, /* MinV: 4.5 V */
+ [0x11a] = 0x5d, /* MaxV: 5.5 V */
+ [0x11c] = 0x4e, /* Peakl: 450 mA */
+ [0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */
+ [0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */
+ [0x122] = 0x70, /* Field 1 address = 0x0170 */
+ [0x124] = 0x01,
+ [0x126] = 0x07, /* Address block length = 8 */
+ [0x128] = 0x76, /* Field 2 address = 0x0376 */
+ [0x12a] = 0x03,
+ [0x12c] = 0x01, /* Address block length = 2 */
+ [0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */
+ [0x130] = 0x20, /* TPCE_MI = support power down mode */
+
+ [0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
+ [0x134] = 0x06, /* Tuple length = 6 bytes */
+ [0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */
+ [0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
+ [0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
+ [0x13c] = 0xb5, /* NomV: 3.3 V */
+ [0x13e] = 0x1e,
+ [0x140] = 0x3e, /* Peakl: 350 mA */
+
+ [0x142] = CISTPL_NO_LINK, /* No Link */
+ [0x144] = 0x00, /* Tuple length = 0 bytes */
+
+ [0x146] = CISTPL_END, /* Tuple End */
+};
+
+static int dscm1xxxx_attach(void *opaque)
+{
+ MicroDriveState *md = opaque;
+ md->card.attr_read = md_attr_read;
+ md->card.attr_write = md_attr_write;
+ md->card.common_read = md_common_read;
+ md->card.common_write = md_common_write;
+ md->card.io_read = md_common_read;
+ md->card.io_write = md_common_write;
+
+ md->attr_base = md->card.cis[0x74] | (md->card.cis[0x76] << 8);
+ md->io_base = 0x0;
+
+ md_reset(md);
+ md_interrupt_update(md);
+
+ md->card.slot->card_string = "DSCM-1xxxx Hitachi Microdrive";
+ return 0;
+}
+
+static int dscm1xxxx_detach(void *opaque)
+{
+ MicroDriveState *md = opaque;
+ md_reset(md);
+ return 0;
+}
+
+PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv)
+{
+ MicroDriveState *md = (MicroDriveState *) qemu_mallocz(sizeof(MicroDriveState));
+ md->card.state = md;
+ md->card.attach = dscm1xxxx_attach;
+ md->card.detach = dscm1xxxx_detach;
+ md->card.cis = dscm1xxxx_cis;
+ md->card.cis_len = sizeof(dscm1xxxx_cis);
+
+ ide_init2_with_non_qdev_drives(&md->bus, bdrv, NULL,
+ qemu_allocate_irqs(md_set_irq, md, 1)[0]);
+ md->bus.ifs[0].drive_kind = IDE_CFATA;
+ md->bus.ifs[0].mdata_size = METADATA_SIZE;
+ md->bus.ifs[0].mdata_storage = (uint8_t *) qemu_mallocz(METADATA_SIZE);
+
+ vmstate_register(NULL, -1, &vmstate_microdrive, md);
+
+ return &md->card;
+}
diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c
new file mode 100644
index 0000000..82b24b6
--- /dev/null
+++ b/hw/ide/mmio.c
@@ -0,0 +1,141 @@
+/*
+ * QEMU IDE Emulation: mmio support (for embedded).
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/internal.h>
+
+/***********************************************************/
+/* MMIO based ide port
+ * This emulates IDE device connected directly to the CPU bus without
+ * dedicated ide controller, which is often seen on embedded boards.
+ */
+
+typedef struct {
+ IDEBus bus;
+ int shift;
+} MMIOState;
+
+static void mmio_ide_reset(void *opaque)
+{
+ MMIOState *s = opaque;
+
+ ide_bus_reset(&s->bus);
+}
+
+static uint32_t mmio_ide_read (void *opaque, target_phys_addr_t addr)
+{
+ MMIOState *s = opaque;
+ addr >>= s->shift;
+ if (addr & 7)
+ return ide_ioport_read(&s->bus, addr);
+ else
+ return ide_data_readw(&s->bus, 0);
+}
+
+static void mmio_ide_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MMIOState *s = opaque;
+ addr >>= s->shift;
+ if (addr & 7)
+ ide_ioport_write(&s->bus, addr, val);
+ else
+ ide_data_writew(&s->bus, 0, val);
+}
+
+static CPUReadMemoryFunc * const mmio_ide_reads[] = {
+ mmio_ide_read,
+ mmio_ide_read,
+ mmio_ide_read,
+};
+
+static CPUWriteMemoryFunc * const mmio_ide_writes[] = {
+ mmio_ide_write,
+ mmio_ide_write,
+ mmio_ide_write,
+};
+
+static uint32_t mmio_ide_status_read (void *opaque, target_phys_addr_t addr)
+{
+ MMIOState *s= opaque;
+ return ide_status_read(&s->bus, 0);
+}
+
+static void mmio_ide_cmd_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MMIOState *s = opaque;
+ ide_cmd_write(&s->bus, 0, val);
+}
+
+static CPUReadMemoryFunc * const mmio_ide_status[] = {
+ mmio_ide_status_read,
+ mmio_ide_status_read,
+ mmio_ide_status_read,
+};
+
+static CPUWriteMemoryFunc * const mmio_ide_cmd[] = {
+ mmio_ide_cmd_write,
+ mmio_ide_cmd_write,
+ mmio_ide_cmd_write,
+};
+
+static const VMStateDescription vmstate_ide_mmio = {
+ .name = "mmio-ide",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_IDE_BUS(bus, MMIOState),
+ VMSTATE_IDE_DRIVES(bus.ifs, MMIOState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void mmio_ide_init (target_phys_addr_t membase, target_phys_addr_t membase2,
+ qemu_irq irq, int shift,
+ DriveInfo *hd0, DriveInfo *hd1)
+{
+ MMIOState *s = qemu_mallocz(sizeof(MMIOState));
+ int mem1, mem2;
+
+ ide_init2_with_non_qdev_drives(&s->bus, hd0, hd1, irq);
+
+ s->shift = shift;
+
+ mem1 = cpu_register_io_memory(mmio_ide_reads, mmio_ide_writes, s,
+ DEVICE_NATIVE_ENDIAN);
+ mem2 = cpu_register_io_memory(mmio_ide_status, mmio_ide_cmd, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(membase, 16 << shift, mem1);
+ cpu_register_physical_memory(membase2, 2 << shift, mem2);
+ vmstate_register(NULL, 0, &vmstate_ide_mmio, s);
+ qemu_register_reset(mmio_ide_reset, s);
+}
+
diff --git a/hw/ide/pci.c b/hw/ide/pci.c
new file mode 100644
index 0000000..35168cb
--- /dev/null
+++ b/hw/ide/pci.c
@@ -0,0 +1,467 @@
+/*
+ * QEMU IDE Emulation: PCI Bus support.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+#include <hw/isa.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/pci.h>
+
+#define BMDMA_PAGE_SIZE 4096
+
+static void bmdma_start_dma(IDEDMA *dma, IDEState *s,
+ BlockDriverCompletionFunc *dma_cb)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+
+ bm->unit = s->unit;
+ bm->dma_cb = dma_cb;
+ bm->cur_prd_last = 0;
+ bm->cur_prd_addr = 0;
+ bm->cur_prd_len = 0;
+ bm->sector_num = ide_get_sector(s);
+ bm->nsector = s->nsector;
+
+ if (bm->status & BM_STATUS_DMAING) {
+ bm->dma_cb(bmdma_active_if(bm), 0);
+ }
+}
+
+/* return 0 if buffer completed */
+static int bmdma_prepare_buf(IDEDMA *dma, int is_write)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+ IDEState *s = bmdma_active_if(bm);
+ struct {
+ uint32_t addr;
+ uint32_t size;
+ } prd;
+ int l, len;
+
+ qemu_sglist_init(&s->sg, s->nsector / (BMDMA_PAGE_SIZE / 512) + 1);
+ s->io_buffer_size = 0;
+ for(;;) {
+ if (bm->cur_prd_len == 0) {
+ /* end of table (with a fail safe of one page) */
+ if (bm->cur_prd_last ||
+ (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE)
+ return s->io_buffer_size != 0;
+ cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8);
+ bm->cur_addr += 8;
+ prd.addr = le32_to_cpu(prd.addr);
+ prd.size = le32_to_cpu(prd.size);
+ len = prd.size & 0xfffe;
+ if (len == 0)
+ len = 0x10000;
+ bm->cur_prd_len = len;
+ bm->cur_prd_addr = prd.addr;
+ bm->cur_prd_last = (prd.size & 0x80000000);
+ }
+ l = bm->cur_prd_len;
+ if (l > 0) {
+ qemu_sglist_add(&s->sg, bm->cur_prd_addr, l);
+ bm->cur_prd_addr += l;
+ bm->cur_prd_len -= l;
+ s->io_buffer_size += l;
+ }
+ }
+ return 1;
+}
+
+/* return 0 if buffer completed */
+static int bmdma_rw_buf(IDEDMA *dma, int is_write)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+ IDEState *s = bmdma_active_if(bm);
+ struct {
+ uint32_t addr;
+ uint32_t size;
+ } prd;
+ int l, len;
+
+ for(;;) {
+ l = s->io_buffer_size - s->io_buffer_index;
+ if (l <= 0)
+ break;
+ if (bm->cur_prd_len == 0) {
+ /* end of table (with a fail safe of one page) */
+ if (bm->cur_prd_last ||
+ (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE)
+ return 0;
+ cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8);
+ bm->cur_addr += 8;
+ prd.addr = le32_to_cpu(prd.addr);
+ prd.size = le32_to_cpu(prd.size);
+ len = prd.size & 0xfffe;
+ if (len == 0)
+ len = 0x10000;
+ bm->cur_prd_len = len;
+ bm->cur_prd_addr = prd.addr;
+ bm->cur_prd_last = (prd.size & 0x80000000);
+ }
+ if (l > bm->cur_prd_len)
+ l = bm->cur_prd_len;
+ if (l > 0) {
+ if (is_write) {
+ cpu_physical_memory_write(bm->cur_prd_addr,
+ s->io_buffer + s->io_buffer_index, l);
+ } else {
+ cpu_physical_memory_read(bm->cur_prd_addr,
+ s->io_buffer + s->io_buffer_index, l);
+ }
+ bm->cur_prd_addr += l;
+ bm->cur_prd_len -= l;
+ s->io_buffer_index += l;
+ }
+ }
+ return 1;
+}
+
+static int bmdma_set_unit(IDEDMA *dma, int unit)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+ bm->unit = unit;
+
+ return 0;
+}
+
+static int bmdma_add_status(IDEDMA *dma, int status)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+ bm->status |= status;
+
+ return 0;
+}
+
+static int bmdma_set_inactive(IDEDMA *dma)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+
+ bm->status &= ~BM_STATUS_DMAING;
+ bm->dma_cb = NULL;
+ bm->unit = -1;
+
+ return 0;
+}
+
+static void bmdma_restart_dma(BMDMAState *bm, int is_read)
+{
+ IDEState *s = bmdma_active_if(bm);
+
+ ide_set_sector(s, bm->sector_num);
+ s->io_buffer_index = 0;
+ s->io_buffer_size = 0;
+ s->nsector = bm->nsector;
+ s->is_read = is_read;
+ bm->cur_addr = bm->addr;
+ bm->dma_cb = ide_dma_cb;
+ bmdma_start_dma(&bm->dma, s, bm->dma_cb);
+}
+
+static void bmdma_restart_bh(void *opaque)
+{
+ BMDMAState *bm = opaque;
+ int is_read;
+
+ qemu_bh_delete(bm->bh);
+ bm->bh = NULL;
+
+ is_read = !!(bm->status & BM_STATUS_RETRY_READ);
+
+ if (bm->status & BM_STATUS_DMA_RETRY) {
+ bm->status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ);
+ bmdma_restart_dma(bm, is_read);
+ } else if (bm->status & BM_STATUS_PIO_RETRY) {
+ bm->status &= ~(BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ);
+ if (is_read) {
+ ide_sector_read(bmdma_active_if(bm));
+ } else {
+ ide_sector_write(bmdma_active_if(bm));
+ }
+ } else if (bm->status & BM_STATUS_RETRY_FLUSH) {
+ ide_flush_cache(bmdma_active_if(bm));
+ }
+}
+
+static void bmdma_restart_cb(void *opaque, int running, int reason)
+{
+ IDEDMA *dma = opaque;
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+
+ if (!running)
+ return;
+
+ if (!bm->bh) {
+ bm->bh = qemu_bh_new(bmdma_restart_bh, &bm->dma);
+ qemu_bh_schedule(bm->bh);
+ }
+}
+
+static void bmdma_cancel(BMDMAState *bm)
+{
+ if (bm->status & BM_STATUS_DMAING) {
+ /* cancel DMA request */
+ bmdma_set_inactive(&bm->dma);
+ }
+}
+
+static int bmdma_reset(IDEDMA *dma)
+{
+ BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma);
+
+#ifdef DEBUG_IDE
+ printf("ide: dma_reset\n");
+#endif
+ bmdma_cancel(bm);
+ bm->cmd = 0;
+ bm->status = 0;
+ bm->addr = 0;
+ bm->cur_addr = 0;
+ bm->cur_prd_last = 0;
+ bm->cur_prd_addr = 0;
+ bm->cur_prd_len = 0;
+ bm->sector_num = 0;
+ bm->nsector = 0;
+
+ return 0;
+}
+
+static int bmdma_start_transfer(IDEDMA *dma)
+{
+ return 0;
+}
+
+static void bmdma_irq(void *opaque, int n, int level)
+{
+ BMDMAState *bm = opaque;
+
+ if (!level) {
+ /* pass through lower */
+ qemu_set_irq(bm->irq, level);
+ return;
+ }
+
+ bm->status |= BM_STATUS_INT;
+
+ /* trigger the real irq */
+ qemu_set_irq(bm->irq, level);
+}
+
+void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ BMDMAState *bm = opaque;
+#ifdef DEBUG_IDE
+ printf("%s: 0x%08x\n", __func__, val);
+#endif
+
+ /* Ignore writes to SSBM if it keeps the old value */
+ if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) {
+ if (!(val & BM_CMD_START)) {
+ /*
+ * We can't cancel Scatter Gather DMA in the middle of the
+ * operation or a partial (not full) DMA transfer would reach
+ * the storage so we wait for completion instead (we beahve
+ * like if the DMA was completed by the time the guest trying
+ * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not
+ * set).
+ *
+ * In the future we'll be able to safely cancel the I/O if the
+ * whole DMA operation will be submitted to disk with a single
+ * aio operation with preadv/pwritev.
+ */
+ if (bm->bus->dma->aiocb) {
+ qemu_aio_flush();
+#ifdef DEBUG_IDE
+ if (bm->bus->dma->aiocb)
+ printf("ide_dma_cancel: aiocb still pending");
+ if (bm->status & BM_STATUS_DMAING)
+ printf("ide_dma_cancel: BM_STATUS_DMAING still pending");
+#endif
+ }
+ } else {
+ bm->cur_addr = bm->addr;
+ if (!(bm->status & BM_STATUS_DMAING)) {
+ bm->status |= BM_STATUS_DMAING;
+ /* start dma transfer if possible */
+ if (bm->dma_cb)
+ bm->dma_cb(bmdma_active_if(bm), 0);
+ }
+ }
+ }
+
+ bm->cmd = val & 0x09;
+}
+
+static void bmdma_addr_read(IORange *ioport, uint64_t addr,
+ unsigned width, uint64_t *data)
+{
+ BMDMAState *bm = container_of(ioport, BMDMAState, addr_ioport);
+ uint32_t mask = (1ULL << (width * 8)) - 1;
+
+ *data = (bm->addr >> (addr * 8)) & mask;
+#ifdef DEBUG_IDE
+ printf("%s: 0x%08x\n", __func__, (unsigned)*data);
+#endif
+}
+
+static void bmdma_addr_write(IORange *ioport, uint64_t addr,
+ unsigned width, uint64_t data)
+{
+ BMDMAState *bm = container_of(ioport, BMDMAState, addr_ioport);
+ int shift = addr * 8;
+ uint32_t mask = (1ULL << (width * 8)) - 1;
+
+#ifdef DEBUG_IDE
+ printf("%s: 0x%08x\n", __func__, (unsigned)data);
+#endif
+ bm->addr &= ~(mask << shift);
+ bm->addr |= ((data & mask) << shift) & ~3;
+}
+
+const IORangeOps bmdma_addr_ioport_ops = {
+ .read = bmdma_addr_read,
+ .write = bmdma_addr_write,
+};
+
+static bool ide_bmdma_current_needed(void *opaque)
+{
+ BMDMAState *bm = opaque;
+
+ return (bm->cur_prd_len != 0);
+}
+
+static const VMStateDescription vmstate_bmdma_current = {
+ .name = "ide bmdma_current",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(cur_addr, BMDMAState),
+ VMSTATE_UINT32(cur_prd_last, BMDMAState),
+ VMSTATE_UINT32(cur_prd_addr, BMDMAState),
+ VMSTATE_UINT32(cur_prd_len, BMDMAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static const VMStateDescription vmstate_bmdma = {
+ .name = "ide bmdma",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(cmd, BMDMAState),
+ VMSTATE_UINT8(status, BMDMAState),
+ VMSTATE_UINT32(addr, BMDMAState),
+ VMSTATE_INT64(sector_num, BMDMAState),
+ VMSTATE_UINT32(nsector, BMDMAState),
+ VMSTATE_UINT8(unit, BMDMAState),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_bmdma_current,
+ .needed = ide_bmdma_current_needed,
+ }, {
+ /* empty */
+ }
+ }
+};
+
+static int ide_pci_post_load(void *opaque, int version_id)
+{
+ PCIIDEState *d = opaque;
+ int i;
+
+ for(i = 0; i < 2; i++) {
+ /* current versions always store 0/1, but older version
+ stored bigger values. We only need last bit */
+ d->bmdma[i].unit &= 1;
+ }
+ return 0;
+}
+
+const VMStateDescription vmstate_ide_pci = {
+ .name = "ide",
+ .version_id = 3,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = ide_pci_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCIIDEState),
+ VMSTATE_STRUCT_ARRAY(bmdma, PCIIDEState, 2, 0,
+ vmstate_bmdma, BMDMAState),
+ VMSTATE_IDE_BUS_ARRAY(bus, PCIIDEState, 2),
+ VMSTATE_IDE_DRIVES(bus[0].ifs, PCIIDEState),
+ VMSTATE_IDE_DRIVES(bus[1].ifs, PCIIDEState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
+ static const int bus[4] = { 0, 0, 1, 1 };
+ static const int unit[4] = { 0, 1, 0, 1 };
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (hd_table[i] == NULL)
+ continue;
+ ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]);
+ }
+}
+
+static const struct IDEDMAOps bmdma_ops = {
+ .start_dma = bmdma_start_dma,
+ .start_transfer = bmdma_start_transfer,
+ .prepare_buf = bmdma_prepare_buf,
+ .rw_buf = bmdma_rw_buf,
+ .set_unit = bmdma_set_unit,
+ .add_status = bmdma_add_status,
+ .set_inactive = bmdma_set_inactive,
+ .restart_cb = bmdma_restart_cb,
+ .reset = bmdma_reset,
+};
+
+void bmdma_init(IDEBus *bus, BMDMAState *bm)
+{
+ qemu_irq *irq;
+
+ if (bus->dma == &bm->dma) {
+ return;
+ }
+
+ bm->dma.ops = &bmdma_ops;
+ bus->dma = &bm->dma;
+ bm->irq = bus->irq;
+ irq = qemu_allocate_irqs(bmdma_irq, bm, 1);
+ bus->irq = *irq;
+}
diff --git a/hw/ide/pci.h b/hw/ide/pci.h
new file mode 100644
index 0000000..cd72cba
--- /dev/null
+++ b/hw/ide/pci.h
@@ -0,0 +1,48 @@
+#ifndef HW_IDE_PCI_H
+#define HW_IDE_PCI_H
+
+#include <hw/ide/internal.h>
+
+typedef struct BMDMAState {
+ IDEDMA dma;
+ uint8_t cmd;
+ uint8_t status;
+ uint32_t addr;
+
+ IDEBus *bus;
+ /* current transfer state */
+ uint32_t cur_addr;
+ uint32_t cur_prd_last;
+ uint32_t cur_prd_addr;
+ uint32_t cur_prd_len;
+ uint8_t unit;
+ BlockDriverCompletionFunc *dma_cb;
+ int64_t sector_num;
+ uint32_t nsector;
+ IORange addr_ioport;
+ QEMUBH *bh;
+ qemu_irq irq;
+} BMDMAState;
+
+typedef struct PCIIDEState {
+ PCIDevice dev;
+ IDEBus bus[2];
+ BMDMAState bmdma[2];
+ uint32_t secondary; /* used only for cmd646 */
+} PCIIDEState;
+
+
+static inline IDEState *bmdma_active_if(BMDMAState *bmdma)
+{
+ assert(bmdma->unit != (uint8_t)-1);
+ return bmdma->bus->ifs + bmdma->unit;
+}
+
+
+void bmdma_init(IDEBus *bus, BMDMAState *bm);
+void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val);
+extern const IORangeOps bmdma_addr_ioport_ops;
+void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table);
+
+extern const VMStateDescription vmstate_ide_pci;
+#endif
diff --git a/hw/ide/piix.c b/hw/ide/piix.c
new file mode 100644
index 0000000..c349644
--- /dev/null
+++ b/hw/ide/piix.c
@@ -0,0 +1,214 @@
+/*
+ * QEMU IDE Emulation: PCI PIIX3/4 support.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+#include <hw/isa.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/pci.h>
+
+static uint32_t bmdma_readb(void *opaque, uint32_t addr)
+{
+ BMDMAState *bm = opaque;
+ uint32_t val;
+
+ switch(addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ BMDMAState *bm = opaque;
+#ifdef DEBUG_IDE
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ switch(addr & 3) {
+ case 2:
+ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ break;
+ }
+}
+
+static void bmdma_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, pci_dev);
+ int i;
+
+ for(i = 0;i < 2; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+
+ register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm);
+
+ register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm);
+ register_ioport_read(addr, 4, 1, bmdma_readb, bm);
+
+ iorange_init(&bm->addr_ioport, &bmdma_addr_ioport_ops, addr + 4, 4);
+ ioport_register(&bm->addr_ioport);
+ addr += 8;
+ }
+}
+
+static void piix3_reset(void *opaque)
+{
+ PCIIDEState *d = opaque;
+ uint8_t *pci_conf = d->dev.config;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ ide_bus_reset(&d->bus[i]);
+ }
+
+ /* TODO: this is the default. do not override. */
+ pci_conf[PCI_COMMAND] = 0x00;
+ /* TODO: this is the default. do not override. */
+ pci_conf[PCI_COMMAND + 1] = 0x00;
+ /* TODO: use pci_set_word */
+ pci_conf[PCI_STATUS] = PCI_STATUS_FAST_BACK;
+ pci_conf[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8;
+ pci_conf[0x20] = 0x01; /* BMIBA: 20-23h */
+}
+
+static void pci_piix_init_ports(PCIIDEState *d) {
+ int i;
+ struct {
+ int iobase;
+ int iobase2;
+ int isairq;
+ } port_info[] = {
+ {0x1f0, 0x3f6, 14},
+ {0x170, 0x376, 15},
+ };
+
+ for (i = 0; i < 2; i++) {
+ ide_bus_new(&d->bus[i], &d->dev.qdev, i);
+ ide_init_ioport(&d->bus[i], port_info[i].iobase, port_info[i].iobase2);
+ ide_init2(&d->bus[i], isa_get_irq(port_info[i].isairq));
+
+ bmdma_init(&d->bus[i], &d->bmdma[i]);
+ d->bmdma[i].bus = &d->bus[i];
+ qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
+ &d->bmdma[i].dma);
+ }
+}
+
+static int pci_piix_ide_initfn(PCIIDEState *d)
+{
+ uint8_t *pci_conf = d->dev.config;
+
+ pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode
+ pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
+
+ qemu_register_reset(piix3_reset, d);
+
+ pci_register_bar(&d->dev, 4, 0x10, PCI_BASE_ADDRESS_SPACE_IO, bmdma_map);
+
+ vmstate_register(&d->dev.qdev, 0, &vmstate_ide_pci, d);
+
+ pci_piix_init_ports(d);
+
+ return 0;
+}
+
+static int pci_piix3_ide_initfn(PCIDevice *dev)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
+
+ pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82371SB_1);
+ return pci_piix_ide_initfn(d);
+}
+
+static int pci_piix4_ide_initfn(PCIDevice *dev)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
+
+ pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82371AB);
+ return pci_piix_ide_initfn(d);
+}
+
+/* hd_table must contain 4 block drivers */
+/* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */
+PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn)
+{
+ PCIDevice *dev;
+
+ dev = pci_create_simple(bus, devfn, "piix3-ide");
+ pci_ide_create_devs(dev, hd_table);
+ return dev;
+}
+
+/* hd_table must contain 4 block drivers */
+/* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */
+PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn)
+{
+ PCIDevice *dev;
+
+ dev = pci_create_simple(bus, devfn, "piix4-ide");
+ pci_ide_create_devs(dev, hd_table);
+ return dev;
+}
+
+static PCIDeviceInfo piix_ide_info[] = {
+ {
+ .qdev.name = "piix3-ide",
+ .qdev.size = sizeof(PCIIDEState),
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
+ .init = pci_piix3_ide_initfn,
+ },{
+ .qdev.name = "piix4-ide",
+ .qdev.size = sizeof(PCIIDEState),
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
+ .init = pci_piix4_ide_initfn,
+ },{
+ /* end of list */
+ }
+};
+
+static void piix_ide_register(void)
+{
+ pci_qdev_register_many(piix_ide_info);
+}
+device_init(piix_ide_register);
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
new file mode 100644
index 0000000..2bb5c27
--- /dev/null
+++ b/hw/ide/qdev.c
@@ -0,0 +1,172 @@
+/*
+ * ide bus support for qdev.
+ *
+ * Copyright (c) 2009 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <hw/hw.h>
+#include "dma.h"
+#include "qemu-error.h"
+#include <hw/ide/internal.h>
+#include "blockdev.h"
+#include "sysemu.h"
+
+/* --------------------------------- */
+
+static char *idebus_get_fw_dev_path(DeviceState *dev);
+
+static struct BusInfo ide_bus_info = {
+ .name = "IDE",
+ .size = sizeof(IDEBus),
+ .get_fw_dev_path = idebus_get_fw_dev_path,
+};
+
+void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id)
+{
+ qbus_create_inplace(&idebus->qbus, &ide_bus_info, dev, NULL);
+ idebus->bus_id = bus_id;
+}
+
+static char *idebus_get_fw_dev_path(DeviceState *dev)
+{
+ char path[30];
+
+ snprintf(path, sizeof(path), "%s@%d", qdev_fw_name(dev),
+ ((IDEBus*)dev->parent_bus)->bus_id);
+
+ return strdup(path);
+}
+
+static int ide_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ IDEDevice *dev = DO_UPCAST(IDEDevice, qdev, qdev);
+ IDEDeviceInfo *info = DO_UPCAST(IDEDeviceInfo, qdev, base);
+ IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus);
+
+ if (!dev->conf.bs) {
+ error_report("No drive specified");
+ goto err;
+ }
+ if (dev->unit == -1) {
+ dev->unit = bus->master ? 1 : 0;
+ }
+ switch (dev->unit) {
+ case 0:
+ if (bus->master) {
+ error_report("IDE unit %d is in use", dev->unit);
+ goto err;
+ }
+ bus->master = dev;
+ break;
+ case 1:
+ if (bus->slave) {
+ error_report("IDE unit %d is in use", dev->unit);
+ goto err;
+ }
+ bus->slave = dev;
+ break;
+ default:
+ error_report("Invalid IDE unit %d", dev->unit);
+ goto err;
+ }
+ return info->init(dev);
+
+err:
+ return -1;
+}
+
+static void ide_qdev_register(IDEDeviceInfo *info)
+{
+ info->qdev.init = ide_qdev_init;
+ info->qdev.bus_info = &ide_bus_info;
+ qdev_register(&info->qdev);
+}
+
+IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(&bus->qbus, "ide-drive");
+ qdev_prop_set_uint32(dev, "unit", unit);
+ qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv);
+ qdev_init_nofail(dev);
+ return DO_UPCAST(IDEDevice, qdev, dev);
+}
+
+void ide_get_bs(BlockDriverState *bs[], BusState *qbus)
+{
+ IDEBus *bus = DO_UPCAST(IDEBus, qbus, qbus);
+ bs[0] = bus->master ? bus->master->conf.bs : NULL;
+ bs[1] = bus->slave ? bus->slave->conf.bs : NULL;
+}
+
+/* --------------------------------- */
+
+typedef struct IDEDrive {
+ IDEDevice dev;
+} IDEDrive;
+
+static int ide_drive_initfn(IDEDevice *dev)
+{
+ IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
+ IDEState *s = bus->ifs + dev->unit;
+ const char *serial;
+ DriveInfo *dinfo;
+
+ serial = dev->serial;
+ if (!serial) {
+ /* try to fall back to value set with legacy -drive serial=... */
+ dinfo = drive_get_by_blockdev(dev->conf.bs);
+ if (*dinfo->serial) {
+ serial = dinfo->serial;
+ }
+ }
+
+ if (ide_init_drive(s, dev->conf.bs, dev->version, serial) < 0) {
+ return -1;
+ }
+
+ if (!dev->version) {
+ dev->version = qemu_strdup(s->version);
+ }
+ if (!dev->serial) {
+ dev->serial = qemu_strdup(s->drive_serial_str);
+ }
+
+ add_boot_device_path(dev->conf.bootindex, &dev->qdev,
+ dev->unit ? "/disk@1" : "/disk@0");
+
+ 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 void ide_drive_register(void)
+{
+ ide_qdev_register(&ide_drive_info);
+}
+device_init(ide_drive_register);
diff --git a/hw/ide/via.c b/hw/ide/via.c
new file mode 100644
index 0000000..04f3290
--- /dev/null
+++ b/hw/ide/via.c
@@ -0,0 +1,200 @@
+/*
+ * QEMU IDE Emulation: PCI VIA82C686B support.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2010 Huacai Chen <zltjiangshi@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <hw/hw.h>
+#include <hw/pc.h>
+#include <hw/pci.h>
+#include <hw/isa.h>
+#include "block.h"
+#include "block_int.h"
+#include "sysemu.h"
+#include "dma.h"
+
+#include <hw/ide/pci.h>
+
+static uint32_t bmdma_readb(void *opaque, uint32_t addr)
+{
+ BMDMAState *bm = opaque;
+ uint32_t val;
+
+ switch (addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ BMDMAState *bm = opaque;
+#ifdef DEBUG_IDE
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ switch (addr & 3) {
+ case 2:
+ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ break;
+ default:;
+ }
+}
+
+static void bmdma_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, pci_dev);
+ int i;
+
+ for(i = 0;i < 2; i++) {
+ BMDMAState *bm = &d->bmdma[i];
+
+ register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm);
+
+ register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm);
+ register_ioport_read(addr, 4, 1, bmdma_readb, bm);
+
+ iorange_init(&bm->addr_ioport, &bmdma_addr_ioport_ops, addr + 4, 4);
+ ioport_register(&bm->addr_ioport);
+ addr += 8;
+ }
+}
+
+static void via_reset(void *opaque)
+{
+ PCIIDEState *d = opaque;
+ uint8_t *pci_conf = d->dev.config;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ ide_bus_reset(&d->bus[i]);
+ }
+
+ pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_WAIT);
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
+ PCI_STATUS_DEVSEL_MEDIUM);
+
+ pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0);
+ pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4);
+ pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170);
+ pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374);
+ pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */
+ pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e);
+
+ /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/
+ pci_set_long(pci_conf + 0x40, 0x0a090600);
+ /* IDE misc configuration 1/2/3 */
+ pci_set_long(pci_conf + 0x44, 0x00c00068);
+ /* IDE Timing control */
+ pci_set_long(pci_conf + 0x48, 0xa8a8a8a8);
+ /* IDE Address Setup Time */
+ pci_set_long(pci_conf + 0x4c, 0x000000ff);
+ /* UltraDMA Extended Timing Control*/
+ pci_set_long(pci_conf + 0x50, 0x07070707);
+ /* UltraDMA FIFO Control */
+ pci_set_long(pci_conf + 0x54, 0x00000004);
+ /* IDE primary sector size */
+ pci_set_long(pci_conf + 0x60, 0x00000200);
+ /* IDE secondary sector size */
+ pci_set_long(pci_conf + 0x68, 0x00000200);
+ /* PCI PM Block */
+ pci_set_long(pci_conf + 0xc0, 0x00020001);
+}
+
+static void vt82c686b_init_ports(PCIIDEState *d) {
+ int i;
+ struct {
+ int iobase;
+ int iobase2;
+ int isairq;
+ } port_info[] = {
+ {0x1f0, 0x3f6, 14},
+ {0x170, 0x376, 15},
+ };
+
+ for (i = 0; i < 2; i++) {
+ ide_bus_new(&d->bus[i], &d->dev.qdev, i);
+ ide_init_ioport(&d->bus[i], port_info[i].iobase, port_info[i].iobase2);
+ ide_init2(&d->bus[i], isa_get_irq(port_info[i].isairq));
+
+ bmdma_init(&d->bus[i], &d->bmdma[i]);
+ d->bmdma[i].bus = &d->bus[i];
+ qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
+ &d->bmdma[i].dma);
+ }
+}
+
+/* via ide func */
+static int vt82c686b_ide_initfn(PCIDevice *dev)
+{
+ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);;
+ uint8_t *pci_conf = d->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_IDE);
+ pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE);
+ pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy ATA mode */
+ pci_config_set_revision(pci_conf,0x06); /* Revision 0.6 */
+ pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
+
+ qemu_register_reset(via_reset, d);
+ pci_register_bar(&d->dev, 4, 0x10,
+ PCI_BASE_ADDRESS_SPACE_IO, bmdma_map);
+
+ vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d);
+
+ vt82c686b_init_ports(d);
+
+ return 0;
+}
+
+void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn)
+{
+ PCIDevice *dev;
+
+ dev = pci_create_simple(bus, devfn, "via-ide");
+ pci_ide_create_devs(dev, hd_table);
+}
+
+static PCIDeviceInfo via_ide_info = {
+ .qdev.name = "via-ide",
+ .qdev.size = sizeof(PCIIDEState),
+ .qdev.no_user = 1,
+ .init = vt82c686b_ide_initfn,
+};
+
+static void via_ide_register(void)
+{
+ pci_qdev_register(&via_ide_info);
+}
+device_init(via_ide_register);
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
new file mode 100644
index 0000000..b049940
--- /dev/null
+++ b/hw/integratorcp.c
@@ -0,0 +1,545 @@
+/*
+ * ARM Integrator CP System emulation.
+ *
+ * Copyright (c) 2005-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "sysbus.h"
+#include "primecell.h"
+#include "devices.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "arm-misc.h"
+#include "net.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t memsz;
+ uint32_t flash_offset;
+ uint32_t cm_osc;
+ uint32_t cm_ctrl;
+ uint32_t cm_lock;
+ uint32_t cm_auxosc;
+ uint32_t cm_sdram;
+ uint32_t cm_init;
+ uint32_t cm_flags;
+ uint32_t cm_nvflags;
+ uint32_t int_level;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+} integratorcm_state;
+
+static uint8_t integrator_spd[128] = {
+ 128, 8, 4, 11, 9, 1, 64, 0, 2, 0xa0, 0xa0, 0, 0, 8, 0, 1,
+ 0xe, 4, 0x1c, 1, 2, 0x20, 0xc0, 0, 0, 0, 0, 0x30, 0x28, 0x30, 0x28, 0x40
+};
+
+static uint32_t integratorcm_read(void *opaque, target_phys_addr_t offset)
+{
+ integratorcm_state *s = (integratorcm_state *)opaque;
+ if (offset >= 0x100 && offset < 0x200) {
+ /* CM_SPD */
+ if (offset >= 0x180)
+ return 0;
+ return integrator_spd[offset >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* CM_ID */
+ return 0x411a3001;
+ case 1: /* CM_PROC */
+ return 0;
+ case 2: /* CM_OSC */
+ return s->cm_osc;
+ case 3: /* CM_CTRL */
+ return s->cm_ctrl;
+ case 4: /* CM_STAT */
+ return 0x00100000;
+ case 5: /* CM_LOCK */
+ if (s->cm_lock == 0xa05f) {
+ return 0x1a05f;
+ } else {
+ return s->cm_lock;
+ }
+ case 6: /* CM_LMBUSCNT */
+ /* ??? High frequency timer. */
+ hw_error("integratorcm_read: CM_LMBUSCNT");
+ case 7: /* CM_AUXOSC */
+ return s->cm_auxosc;
+ case 8: /* CM_SDRAM */
+ return s->cm_sdram;
+ case 9: /* CM_INIT */
+ return s->cm_init;
+ case 10: /* CM_REFCT */
+ /* ??? High frequency timer. */
+ hw_error("integratorcm_read: CM_REFCT");
+ case 12: /* CM_FLAGS */
+ return s->cm_flags;
+ case 14: /* CM_NVFLAGS */
+ return s->cm_nvflags;
+ case 16: /* CM_IRQ_STAT */
+ return s->int_level & s->irq_enabled;
+ case 17: /* CM_IRQ_RSTAT */
+ return s->int_level;
+ case 18: /* CM_IRQ_ENSET */
+ return s->irq_enabled;
+ case 20: /* CM_SOFT_INTSET */
+ return s->int_level & 1;
+ case 24: /* CM_FIQ_STAT */
+ return s->int_level & s->fiq_enabled;
+ case 25: /* CM_FIQ_RSTAT */
+ return s->int_level;
+ case 26: /* CM_FIQ_ENSET */
+ return s->fiq_enabled;
+ case 32: /* CM_VOLTAGE_CTL0 */
+ case 33: /* CM_VOLTAGE_CTL1 */
+ case 34: /* CM_VOLTAGE_CTL2 */
+ case 35: /* CM_VOLTAGE_CTL3 */
+ /* ??? Voltage control unimplemented. */
+ return 0;
+ default:
+ hw_error("integratorcm_read: Unimplemented offset 0x%x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void integratorcm_do_remap(integratorcm_state *s, int flash)
+{
+ if (flash) {
+ cpu_register_physical_memory(0, 0x100000, IO_MEM_RAM);
+ } else {
+ cpu_register_physical_memory(0, 0x100000, s->flash_offset | IO_MEM_RAM);
+ }
+ //??? tlb_flush (cpu_single_env, 1);
+}
+
+static void integratorcm_set_ctrl(integratorcm_state *s, uint32_t value)
+{
+ if (value & 8) {
+ hw_error("Board reset\n");
+ }
+ if ((s->cm_init ^ value) & 4) {
+ integratorcm_do_remap(s, (value & 4) == 0);
+ }
+ if ((s->cm_init ^ value) & 1) {
+ printf("Green LED %s\n", (value & 1) ? "on" : "off");
+ }
+ s->cm_init = (s->cm_init & ~ 5) | (value ^ 5);
+}
+
+static void integratorcm_update(integratorcm_state *s)
+{
+ /* ??? The CPU irq/fiq is raised when either the core module or base PIC
+ are active. */
+ if (s->int_level & (s->irq_enabled | s->fiq_enabled))
+ hw_error("Core module interrupt\n");
+}
+
+static void integratorcm_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ integratorcm_state *s = (integratorcm_state *)opaque;
+ switch (offset >> 2) {
+ case 2: /* CM_OSC */
+ if (s->cm_lock == 0xa05f)
+ s->cm_osc = value;
+ break;
+ case 3: /* CM_CTRL */
+ integratorcm_set_ctrl(s, value);
+ break;
+ case 5: /* CM_LOCK */
+ s->cm_lock = value & 0xffff;
+ break;
+ case 7: /* CM_AUXOSC */
+ if (s->cm_lock == 0xa05f)
+ s->cm_auxosc = value;
+ break;
+ case 8: /* CM_SDRAM */
+ s->cm_sdram = value;
+ break;
+ case 9: /* CM_INIT */
+ /* ??? This can change the memory bus frequency. */
+ s->cm_init = value;
+ break;
+ case 12: /* CM_FLAGSS */
+ s->cm_flags |= value;
+ break;
+ case 13: /* CM_FLAGSC */
+ s->cm_flags &= ~value;
+ break;
+ case 14: /* CM_NVFLAGSS */
+ s->cm_nvflags |= value;
+ break;
+ case 15: /* CM_NVFLAGSS */
+ s->cm_nvflags &= ~value;
+ break;
+ case 18: /* CM_IRQ_ENSET */
+ s->irq_enabled |= value;
+ integratorcm_update(s);
+ break;
+ case 19: /* CM_IRQ_ENCLR */
+ s->irq_enabled &= ~value;
+ integratorcm_update(s);
+ break;
+ case 20: /* CM_SOFT_INTSET */
+ s->int_level |= (value & 1);
+ integratorcm_update(s);
+ break;
+ case 21: /* CM_SOFT_INTCLR */
+ s->int_level &= ~(value & 1);
+ integratorcm_update(s);
+ break;
+ case 26: /* CM_FIQ_ENSET */
+ s->fiq_enabled |= value;
+ integratorcm_update(s);
+ break;
+ case 27: /* CM_FIQ_ENCLR */
+ s->fiq_enabled &= ~value;
+ integratorcm_update(s);
+ break;
+ case 32: /* CM_VOLTAGE_CTL0 */
+ case 33: /* CM_VOLTAGE_CTL1 */
+ case 34: /* CM_VOLTAGE_CTL2 */
+ case 35: /* CM_VOLTAGE_CTL3 */
+ /* ??? Voltage control unimplemented. */
+ break;
+ default:
+ hw_error("integratorcm_write: Unimplemented offset 0x%x\n",
+ (int)offset);
+ break;
+ }
+}
+
+/* Integrator/CM control registers. */
+
+static CPUReadMemoryFunc * const integratorcm_readfn[] = {
+ integratorcm_read,
+ integratorcm_read,
+ integratorcm_read
+};
+
+static CPUWriteMemoryFunc * const integratorcm_writefn[] = {
+ integratorcm_write,
+ integratorcm_write,
+ integratorcm_write
+};
+
+static int integratorcm_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ integratorcm_state *s = FROM_SYSBUS(integratorcm_state, dev);
+
+ s->cm_osc = 0x01000048;
+ /* ??? What should the high bits of this value be? */
+ s->cm_auxosc = 0x0007feff;
+ s->cm_sdram = 0x00011122;
+ if (s->memsz >= 256) {
+ integrator_spd[31] = 64;
+ s->cm_sdram |= 0x10;
+ } else if (s->memsz >= 128) {
+ integrator_spd[31] = 32;
+ s->cm_sdram |= 0x0c;
+ } else if (s->memsz >= 64) {
+ integrator_spd[31] = 16;
+ s->cm_sdram |= 0x08;
+ } else if (s->memsz >= 32) {
+ integrator_spd[31] = 4;
+ s->cm_sdram |= 0x04;
+ } else {
+ integrator_spd[31] = 2;
+ }
+ memcpy(integrator_spd + 73, "QEMU-MEMORY", 11);
+ s->cm_init = 0x00000112;
+ s->flash_offset = qemu_ram_alloc(NULL, "integrator.flash", 0x100000);
+
+ iomemtype = cpu_register_io_memory(integratorcm_readfn,
+ integratorcm_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x00800000, iomemtype);
+ integratorcm_do_remap(s, 1);
+ /* ??? Save/restore. */
+ return 0;
+}
+
+/* Integrator/CP hardware emulation. */
+/* Primary interrupt controller. */
+
+typedef struct icp_pic_state
+{
+ SysBusDevice busdev;
+ uint32_t level;
+ uint32_t irq_enabled;
+ uint32_t fiq_enabled;
+ qemu_irq parent_irq;
+ qemu_irq parent_fiq;
+} icp_pic_state;
+
+static void icp_pic_update(icp_pic_state *s)
+{
+ uint32_t flags;
+
+ flags = (s->level & s->irq_enabled);
+ qemu_set_irq(s->parent_irq, flags != 0);
+ flags = (s->level & s->fiq_enabled);
+ qemu_set_irq(s->parent_fiq, flags != 0);
+}
+
+static void icp_pic_set_irq(void *opaque, int irq, int level)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+ if (level)
+ s->level |= 1 << irq;
+ else
+ s->level &= ~(1 << irq);
+ icp_pic_update(s);
+}
+
+static uint32_t icp_pic_read(void *opaque, target_phys_addr_t offset)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* IRQ_STATUS */
+ return s->level & s->irq_enabled;
+ case 1: /* IRQ_RAWSTAT */
+ return s->level;
+ case 2: /* IRQ_ENABLESET */
+ return s->irq_enabled;
+ case 4: /* INT_SOFTSET */
+ return s->level & 1;
+ case 8: /* FRQ_STATUS */
+ return s->level & s->fiq_enabled;
+ case 9: /* FRQ_RAWSTAT */
+ return s->level;
+ case 10: /* FRQ_ENABLESET */
+ return s->fiq_enabled;
+ case 3: /* IRQ_ENABLECLR */
+ case 5: /* INT_SOFTCLR */
+ case 11: /* FRQ_ENABLECLR */
+ default:
+ printf ("icp_pic_read: Bad register offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void icp_pic_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ icp_pic_state *s = (icp_pic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 2: /* IRQ_ENABLESET */
+ s->irq_enabled |= value;
+ break;
+ case 3: /* IRQ_ENABLECLR */
+ s->irq_enabled &= ~value;
+ break;
+ case 4: /* INT_SOFTSET */
+ if (value & 1)
+ icp_pic_set_irq(s, 0, 1);
+ break;
+ case 5: /* INT_SOFTCLR */
+ if (value & 1)
+ icp_pic_set_irq(s, 0, 0);
+ break;
+ case 10: /* FRQ_ENABLESET */
+ s->fiq_enabled |= value;
+ break;
+ case 11: /* FRQ_ENABLECLR */
+ s->fiq_enabled &= ~value;
+ break;
+ case 0: /* IRQ_STATUS */
+ case 1: /* IRQ_RAWSTAT */
+ case 8: /* FRQ_STATUS */
+ case 9: /* FRQ_RAWSTAT */
+ default:
+ printf ("icp_pic_write: Bad register offset 0x%x\n", (int)offset);
+ return;
+ }
+ icp_pic_update(s);
+}
+
+static CPUReadMemoryFunc * const icp_pic_readfn[] = {
+ icp_pic_read,
+ icp_pic_read,
+ icp_pic_read
+};
+
+static CPUWriteMemoryFunc * const icp_pic_writefn[] = {
+ icp_pic_write,
+ icp_pic_write,
+ icp_pic_write
+};
+
+static int icp_pic_init(SysBusDevice *dev)
+{
+ icp_pic_state *s = FROM_SYSBUS(icp_pic_state, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, icp_pic_set_irq, 32);
+ sysbus_init_irq(dev, &s->parent_irq);
+ sysbus_init_irq(dev, &s->parent_fiq);
+ iomemtype = cpu_register_io_memory(icp_pic_readfn,
+ icp_pic_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x00800000, iomemtype);
+ return 0;
+}
+
+/* CP control registers. */
+static uint32_t icp_control_read(void *opaque, target_phys_addr_t offset)
+{
+ switch (offset >> 2) {
+ case 0: /* CP_IDFIELD */
+ return 0x41034003;
+ case 1: /* CP_FLASHPROG */
+ return 0;
+ case 2: /* CP_INTREG */
+ return 0;
+ case 3: /* CP_DECODE */
+ return 0x11;
+ default:
+ hw_error("icp_control_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void icp_control_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ switch (offset >> 2) {
+ case 1: /* CP_FLASHPROG */
+ case 2: /* CP_INTREG */
+ case 3: /* CP_DECODE */
+ /* Nothing interesting implemented yet. */
+ break;
+ default:
+ hw_error("icp_control_write: Bad offset %x\n", (int)offset);
+ }
+}
+static CPUReadMemoryFunc * const icp_control_readfn[] = {
+ icp_control_read,
+ icp_control_read,
+ icp_control_read
+};
+
+static CPUWriteMemoryFunc * const icp_control_writefn[] = {
+ icp_control_write,
+ icp_control_write,
+ icp_control_write
+};
+
+static void icp_control_init(uint32_t base)
+{
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(icp_control_readfn,
+ icp_control_writefn, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00800000, iomemtype);
+ /* ??? Save/restore. */
+}
+
+
+/* Board init. */
+
+static struct arm_boot_info integrator_binfo = {
+ .loader_start = 0x0,
+ .board_id = 0x113,
+};
+
+static void integratorcp_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ ram_addr_t ram_offset;
+ qemu_irq pic[32];
+ qemu_irq *cpu_pic;
+ DeviceState *dev;
+ int i;
+
+ if (!cpu_model)
+ cpu_model = "arm926";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ ram_offset = qemu_ram_alloc(NULL, "integrator.ram", ram_size);
+ /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */
+ /* ??? RAM should repeat to fill physical memory space. */
+ /* SDRAM at address zero*/
+ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
+ /* And again at address 0x80000000 */
+ cpu_register_physical_memory(0x80000000, ram_size, ram_offset | IO_MEM_RAM);
+
+ dev = qdev_create(NULL, "integrator_core");
+ qdev_prop_set_uint32(dev, "memsz", ram_size >> 20);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map((SysBusDevice *)dev, 0, 0x10000000);
+
+ cpu_pic = arm_pic_init_cpu(env);
+ dev = sysbus_create_varargs("integrator_pic", 0x14000000,
+ cpu_pic[ARM_PIC_CPU_IRQ],
+ cpu_pic[ARM_PIC_CPU_FIQ], NULL);
+ for (i = 0; i < 32; i++) {
+ pic[i] = qdev_get_gpio_in(dev, i);
+ }
+ sysbus_create_simple("integrator_pic", 0xca000000, pic[26]);
+ sysbus_create_varargs("integrator_pit", 0x13000000,
+ pic[5], pic[6], pic[7], NULL);
+ sysbus_create_simple("pl031", 0x15000000, pic[8]);
+ sysbus_create_simple("pl011", 0x16000000, pic[1]);
+ sysbus_create_simple("pl011", 0x17000000, pic[2]);
+ icp_control_init(0xcb000000);
+ sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]);
+ sysbus_create_simple("pl050_mouse", 0x19000000, pic[4]);
+ sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL);
+ if (nd_table[0].vlan)
+ smc91c111_init(&nd_table[0], 0xc8000000, pic[27]);
+
+ sysbus_create_simple("pl110", 0xc0000000, pic[22]);
+
+ integrator_binfo.ram_size = ram_size;
+ integrator_binfo.kernel_filename = kernel_filename;
+ integrator_binfo.kernel_cmdline = kernel_cmdline;
+ integrator_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(env, &integrator_binfo);
+}
+
+static QEMUMachine integratorcp_machine = {
+ .name = "integratorcp",
+ .desc = "ARM Integrator/CP (ARM926EJ-S)",
+ .init = integratorcp_init,
+ .is_default = 1,
+};
+
+static void integratorcp_machine_init(void)
+{
+ qemu_register_machine(&integratorcp_machine);
+}
+
+machine_init(integratorcp_machine_init);
+
+static SysBusDeviceInfo core_info = {
+ .init = integratorcm_init,
+ .qdev.name = "integrator_core",
+ .qdev.size = sizeof(integratorcm_state),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("memsz", integratorcm_state, memsz, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void integratorcp_register_devices(void)
+{
+ sysbus_register_dev("integrator_pic", sizeof(icp_pic_state), icp_pic_init);
+ sysbus_register_withprop(&core_info);
+}
+
+device_init(integratorcp_register_devices)
diff --git a/hw/intel-hda-defs.h b/hw/intel-hda-defs.h
new file mode 100644
index 0000000..2e37e5b
--- /dev/null
+++ b/hw/intel-hda-defs.h
@@ -0,0 +1,717 @@
+#ifndef HW_INTEL_HDA_DEFS_H
+#define HW_INTEL_HDA_DEFS_H
+
+/* qemu */
+#define HDA_BUFFER_SIZE 256
+
+/* --------------------------------------------------------------------- */
+/* from linux/sound/pci/hda/hda_intel.c */
+
+/*
+ * registers
+ */
+#define ICH6_REG_GCAP 0x00
+#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
+#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
+#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
+#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
+#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
+#define ICH6_REG_VMIN 0x02
+#define ICH6_REG_VMAJ 0x03
+#define ICH6_REG_OUTPAY 0x04
+#define ICH6_REG_INPAY 0x06
+#define ICH6_REG_GCTL 0x08
+#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
+#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
+#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
+#define ICH6_REG_WAKEEN 0x0c
+#define ICH6_REG_STATESTS 0x0e
+#define ICH6_REG_GSTS 0x10
+#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
+#define ICH6_REG_INTCTL 0x20
+#define ICH6_REG_INTSTS 0x24
+#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
+#define ICH6_REG_SYNC 0x34
+#define ICH6_REG_CORBLBASE 0x40
+#define ICH6_REG_CORBUBASE 0x44
+#define ICH6_REG_CORBWP 0x48
+#define ICH6_REG_CORBRP 0x4a
+#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
+#define ICH6_REG_CORBCTL 0x4c
+#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
+#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
+#define ICH6_REG_CORBSTS 0x4d
+#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
+#define ICH6_REG_CORBSIZE 0x4e
+
+#define ICH6_REG_RIRBLBASE 0x50
+#define ICH6_REG_RIRBUBASE 0x54
+#define ICH6_REG_RIRBWP 0x58
+#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
+#define ICH6_REG_RINTCNT 0x5a
+#define ICH6_REG_RIRBCTL 0x5c
+#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
+#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
+#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
+#define ICH6_REG_RIRBSTS 0x5d
+#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
+#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
+#define ICH6_REG_RIRBSIZE 0x5e
+
+#define ICH6_REG_IC 0x60
+#define ICH6_REG_IR 0x64
+#define ICH6_REG_IRS 0x68
+#define ICH6_IRS_VALID (1<<1)
+#define ICH6_IRS_BUSY (1<<0)
+
+#define ICH6_REG_DPLBASE 0x70
+#define ICH6_REG_DPUBASE 0x74
+#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
+
+/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
+
+/* stream register offsets from stream base */
+#define ICH6_REG_SD_CTL 0x00
+#define ICH6_REG_SD_STS 0x03
+#define ICH6_REG_SD_LPIB 0x04
+#define ICH6_REG_SD_CBL 0x08
+#define ICH6_REG_SD_LVI 0x0c
+#define ICH6_REG_SD_FIFOW 0x0e
+#define ICH6_REG_SD_FIFOSIZE 0x10
+#define ICH6_REG_SD_FORMAT 0x12
+#define ICH6_REG_SD_BDLPL 0x18
+#define ICH6_REG_SD_BDLPU 0x1c
+
+/* PCI space */
+#define ICH6_PCIREG_TCSEL 0x44
+
+/*
+ * other constants
+ */
+
+/* max number of SDs */
+/* ICH, ATI and VIA have 4 playback and 4 capture */
+#define ICH6_NUM_CAPTURE 4
+#define ICH6_NUM_PLAYBACK 4
+
+/* ULI has 6 playback and 5 capture */
+#define ULI_NUM_CAPTURE 5
+#define ULI_NUM_PLAYBACK 6
+
+/* ATI HDMI has 1 playback and 0 capture */
+#define ATIHDMI_NUM_CAPTURE 0
+#define ATIHDMI_NUM_PLAYBACK 1
+
+/* TERA has 4 playback and 3 capture */
+#define TERA_NUM_CAPTURE 3
+#define TERA_NUM_PLAYBACK 4
+
+/* this number is statically defined for simplicity */
+#define MAX_AZX_DEV 16
+
+/* max number of fragments - we may use more if allocating more pages for BDL */
+#define BDL_SIZE 4096
+#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
+#define AZX_MAX_FRAG 32
+/* max buffer size - no h/w limit, you can increase as you like */
+#define AZX_MAX_BUF_SIZE (1024*1024*1024)
+
+/* RIRB int mask: overrun[2], response[0] */
+#define RIRB_INT_RESPONSE 0x01
+#define RIRB_INT_OVERRUN 0x04
+#define RIRB_INT_MASK 0x05
+
+/* STATESTS int mask: S3,SD2,SD1,SD0 */
+#define AZX_MAX_CODECS 8
+#define AZX_DEFAULT_CODECS 4
+#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
+
+/* SD_CTL bits */
+#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
+#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
+#define SD_CTL_STRIPE (3 << 16) /* stripe control */
+#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
+#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
+#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
+#define SD_CTL_STREAM_TAG_SHIFT 20
+
+/* SD_CTL and SD_STS */
+#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
+#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
+#define SD_INT_COMPLETE 0x04 /* completion interrupt */
+#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
+ SD_INT_COMPLETE)
+
+/* SD_STS */
+#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
+
+/* INTCTL and INTSTS */
+#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
+#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
+
+/* below are so far hardcoded - should read registers in future */
+#define ICH6_MAX_CORB_ENTRIES 256
+#define ICH6_MAX_RIRB_ENTRIES 256
+
+/* position fix mode */
+enum {
+ POS_FIX_AUTO,
+ POS_FIX_LPIB,
+ POS_FIX_POSBUF,
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
+
+/* Defines for Nvidia HDA support */
+#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
+#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
+#define NVIDIA_HDA_ISTRM_COH 0x4d
+#define NVIDIA_HDA_OSTRM_COH 0x4c
+#define NVIDIA_HDA_ENABLE_COHBIT 0x01
+
+/* Defines for Intel SCH HDA snoop control */
+#define INTEL_SCH_HDA_DEVC 0x78
+#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+
+/* Define IN stream 0 FIFO size offset in VIA controller */
+#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
+/* Define VIA HD Audio Device ID*/
+#define VIA_HDAC_DEVICE_ID 0x3288
+
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+/* --------------------------------------------------------------------- */
+/* from linux/sound/pci/hda/hda_codec.h */
+
+/*
+ * nodes
+ */
+#define AC_NODE_ROOT 0x00
+
+/*
+ * function group types
+ */
+enum {
+ AC_GRP_AUDIO_FUNCTION = 0x01,
+ AC_GRP_MODEM_FUNCTION = 0x02,
+};
+
+/*
+ * widget types
+ */
+enum {
+ AC_WID_AUD_OUT, /* Audio Out */
+ AC_WID_AUD_IN, /* Audio In */
+ AC_WID_AUD_MIX, /* Audio Mixer */
+ AC_WID_AUD_SEL, /* Audio Selector */
+ AC_WID_PIN, /* Pin Complex */
+ AC_WID_POWER, /* Power */
+ AC_WID_VOL_KNB, /* Volume Knob */
+ AC_WID_BEEP, /* Beep Generator */
+ AC_WID_VENDOR = 0x0f /* Vendor specific */
+};
+
+/*
+ * GET verbs
+ */
+#define AC_VERB_GET_STREAM_FORMAT 0x0a00
+#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
+#define AC_VERB_GET_PROC_COEF 0x0c00
+#define AC_VERB_GET_COEF_INDEX 0x0d00
+#define AC_VERB_PARAMETERS 0x0f00
+#define AC_VERB_GET_CONNECT_SEL 0x0f01
+#define AC_VERB_GET_CONNECT_LIST 0x0f02
+#define AC_VERB_GET_PROC_STATE 0x0f03
+#define AC_VERB_GET_SDI_SELECT 0x0f04
+#define AC_VERB_GET_POWER_STATE 0x0f05
+#define AC_VERB_GET_CONV 0x0f06
+#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
+#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
+#define AC_VERB_GET_PIN_SENSE 0x0f09
+#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
+#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
+#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
+#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
+/* f10-f1a: GPIO */
+#define AC_VERB_GET_GPIO_DATA 0x0f15
+#define AC_VERB_GET_GPIO_MASK 0x0f16
+#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
+#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
+#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
+#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
+#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
+/* f20: AFG/MFG */
+#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
+#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
+#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
+#define AC_VERB_GET_HDMI_ELDD 0x0f2f
+#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
+#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
+#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
+#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
+#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
+
+/*
+ * SET verbs
+ */
+#define AC_VERB_SET_STREAM_FORMAT 0x200
+#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
+#define AC_VERB_SET_PROC_COEF 0x400
+#define AC_VERB_SET_COEF_INDEX 0x500
+#define AC_VERB_SET_CONNECT_SEL 0x701
+#define AC_VERB_SET_PROC_STATE 0x703
+#define AC_VERB_SET_SDI_SELECT 0x704
+#define AC_VERB_SET_POWER_STATE 0x705
+#define AC_VERB_SET_CHANNEL_STREAMID 0x706
+#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
+#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define AC_VERB_SET_PIN_SENSE 0x709
+#define AC_VERB_SET_BEEP_CONTROL 0x70a
+#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
+#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
+#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
+#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
+#define AC_VERB_SET_GPIO_DATA 0x715
+#define AC_VERB_SET_GPIO_MASK 0x716
+#define AC_VERB_SET_GPIO_DIRECTION 0x717
+#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
+#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
+#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
+#define AC_VERB_SET_EAPD 0x788
+#define AC_VERB_SET_CODEC_RESET 0x7ff
+#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
+#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
+#define AC_VERB_SET_HDMI_DIP_DATA 0x731
+#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
+#define AC_VERB_SET_HDMI_CP_CTRL 0x733
+#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
+
+/*
+ * Parameter IDs
+ */
+#define AC_PAR_VENDOR_ID 0x00
+#define AC_PAR_SUBSYSTEM_ID 0x01
+#define AC_PAR_REV_ID 0x02
+#define AC_PAR_NODE_COUNT 0x04
+#define AC_PAR_FUNCTION_TYPE 0x05
+#define AC_PAR_AUDIO_FG_CAP 0x08
+#define AC_PAR_AUDIO_WIDGET_CAP 0x09
+#define AC_PAR_PCM 0x0a
+#define AC_PAR_STREAM 0x0b
+#define AC_PAR_PIN_CAP 0x0c
+#define AC_PAR_AMP_IN_CAP 0x0d
+#define AC_PAR_CONNLIST_LEN 0x0e
+#define AC_PAR_POWER_STATE 0x0f
+#define AC_PAR_PROC_CAP 0x10
+#define AC_PAR_GPIO_CAP 0x11
+#define AC_PAR_AMP_OUT_CAP 0x12
+#define AC_PAR_VOL_KNB_CAP 0x13
+#define AC_PAR_HDMI_LPCM_CAP 0x20
+
+/*
+ * AC_VERB_PARAMETERS results (32bit)
+ */
+
+/* Function Group Type */
+#define AC_FGT_TYPE (0xff<<0)
+#define AC_FGT_TYPE_SHIFT 0
+#define AC_FGT_UNSOL_CAP (1<<8)
+
+/* Audio Function Group Capabilities */
+#define AC_AFG_OUT_DELAY (0xf<<0)
+#define AC_AFG_IN_DELAY (0xf<<8)
+#define AC_AFG_BEEP_GEN (1<<16)
+
+/* Audio Widget Capabilities */
+#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
+#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
+#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
+#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
+#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
+#define AC_WCAP_STRIPE (1<<5) /* stripe */
+#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
+#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
+#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
+#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
+#define AC_WCAP_POWER (1<<10) /* power control */
+#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
+#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
+#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
+#define AC_WCAP_DELAY (0xf<<16)
+#define AC_WCAP_DELAY_SHIFT 16
+#define AC_WCAP_TYPE (0xf<<20)
+#define AC_WCAP_TYPE_SHIFT 20
+
+/* supported PCM rates and bits */
+#define AC_SUPPCM_RATES (0xfff << 0)
+#define AC_SUPPCM_BITS_8 (1<<16)
+#define AC_SUPPCM_BITS_16 (1<<17)
+#define AC_SUPPCM_BITS_20 (1<<18)
+#define AC_SUPPCM_BITS_24 (1<<19)
+#define AC_SUPPCM_BITS_32 (1<<20)
+
+/* supported PCM stream format */
+#define AC_SUPFMT_PCM (1<<0)
+#define AC_SUPFMT_FLOAT32 (1<<1)
+#define AC_SUPFMT_AC3 (1<<2)
+
+/* GP I/O count */
+#define AC_GPIO_IO_COUNT (0xff<<0)
+#define AC_GPIO_O_COUNT (0xff<<8)
+#define AC_GPIO_O_COUNT_SHIFT 8
+#define AC_GPIO_I_COUNT (0xff<<16)
+#define AC_GPIO_I_COUNT_SHIFT 16
+#define AC_GPIO_UNSOLICITED (1<<30)
+#define AC_GPIO_WAKE (1<<31)
+
+/* Converter stream, channel */
+#define AC_CONV_CHANNEL (0xf<<0)
+#define AC_CONV_STREAM (0xf<<4)
+#define AC_CONV_STREAM_SHIFT 4
+
+/* Input converter SDI select */
+#define AC_SDI_SELECT (0xf<<0)
+
+/* stream format id */
+#define AC_FMT_CHAN_SHIFT 0
+#define AC_FMT_CHAN_MASK (0x0f << 0)
+#define AC_FMT_BITS_SHIFT 4
+#define AC_FMT_BITS_MASK (7 << 4)
+#define AC_FMT_BITS_8 (0 << 4)
+#define AC_FMT_BITS_16 (1 << 4)
+#define AC_FMT_BITS_20 (2 << 4)
+#define AC_FMT_BITS_24 (3 << 4)
+#define AC_FMT_BITS_32 (4 << 4)
+#define AC_FMT_DIV_SHIFT 8
+#define AC_FMT_DIV_MASK (7 << 8)
+#define AC_FMT_MULT_SHIFT 11
+#define AC_FMT_MULT_MASK (7 << 11)
+#define AC_FMT_BASE_SHIFT 14
+#define AC_FMT_BASE_48K (0 << 14)
+#define AC_FMT_BASE_44K (1 << 14)
+#define AC_FMT_TYPE_SHIFT 15
+#define AC_FMT_TYPE_PCM (0 << 15)
+#define AC_FMT_TYPE_NON_PCM (1 << 15)
+
+/* Unsolicited response control */
+#define AC_UNSOL_TAG (0x3f<<0)
+#define AC_UNSOL_ENABLED (1<<7)
+#define AC_USRSP_EN AC_UNSOL_ENABLED
+
+/* Unsolicited responses */
+#define AC_UNSOL_RES_TAG (0x3f<<26)
+#define AC_UNSOL_RES_TAG_SHIFT 26
+#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
+#define AC_UNSOL_RES_SUBTAG_SHIFT 21
+#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
+#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
+#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
+#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
+
+/* Pin widget capabilies */
+#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
+#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
+#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
+#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
+#define AC_PINCAP_OUT (1<<4) /* output capable */
+#define AC_PINCAP_IN (1<<5) /* input capable */
+#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
+/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
+ * but is marked reserved in the Intel HDA specification.
+ */
+#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
+/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
+ * in HD-audio specification
+ */
+#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
+#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can
+ * coexist with AC_PINCAP_HDMI
+ */
+#define AC_PINCAP_VREF (0x37<<8)
+#define AC_PINCAP_VREF_SHIFT 8
+#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
+#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
+/* Vref status (used in pin cap) */
+#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
+#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
+#define AC_PINCAP_VREF_GRD (1<<2) /* ground */
+#define AC_PINCAP_VREF_80 (1<<4) /* 80% */
+#define AC_PINCAP_VREF_100 (1<<5) /* 100% */
+
+/* Amplifier capabilities */
+#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
+#define AC_AMPCAP_OFFSET_SHIFT 0
+#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
+#define AC_AMPCAP_NUM_STEPS_SHIFT 8
+#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB
+ * in 0.25dB
+ */
+#define AC_AMPCAP_STEP_SIZE_SHIFT 16
+#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
+#define AC_AMPCAP_MUTE_SHIFT 31
+
+/* Connection list */
+#define AC_CLIST_LENGTH (0x7f<<0)
+#define AC_CLIST_LONG (1<<7)
+
+/* Supported power status */
+#define AC_PWRST_D0SUP (1<<0)
+#define AC_PWRST_D1SUP (1<<1)
+#define AC_PWRST_D2SUP (1<<2)
+#define AC_PWRST_D3SUP (1<<3)
+#define AC_PWRST_D3COLDSUP (1<<4)
+#define AC_PWRST_S3D3COLDSUP (1<<29)
+#define AC_PWRST_CLKSTOP (1<<30)
+#define AC_PWRST_EPSS (1U<<31)
+
+/* Power state values */
+#define AC_PWRST_SETTING (0xf<<0)
+#define AC_PWRST_ACTUAL (0xf<<4)
+#define AC_PWRST_ACTUAL_SHIFT 4
+#define AC_PWRST_D0 0x00
+#define AC_PWRST_D1 0x01
+#define AC_PWRST_D2 0x02
+#define AC_PWRST_D3 0x03
+
+/* Processing capabilies */
+#define AC_PCAP_BENIGN (1<<0)
+#define AC_PCAP_NUM_COEF (0xff<<8)
+#define AC_PCAP_NUM_COEF_SHIFT 8
+
+/* Volume knobs capabilities */
+#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
+#define AC_KNBCAP_DELTA (1<<7)
+
+/* HDMI LPCM capabilities */
+#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
+#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
+#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
+#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
+#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
+#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
+#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
+#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
+#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
+#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
+#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
+#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
+#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
+#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
+
+/*
+ * Control Parameters
+ */
+
+/* Amp gain/mute */
+#define AC_AMP_MUTE (1<<7)
+#define AC_AMP_GAIN (0x7f)
+#define AC_AMP_GET_INDEX (0xf<<0)
+
+#define AC_AMP_GET_LEFT (1<<13)
+#define AC_AMP_GET_RIGHT (0<<13)
+#define AC_AMP_GET_OUTPUT (1<<15)
+#define AC_AMP_GET_INPUT (0<<15)
+
+#define AC_AMP_SET_INDEX (0xf<<8)
+#define AC_AMP_SET_INDEX_SHIFT 8
+#define AC_AMP_SET_RIGHT (1<<12)
+#define AC_AMP_SET_LEFT (1<<13)
+#define AC_AMP_SET_INPUT (1<<14)
+#define AC_AMP_SET_OUTPUT (1<<15)
+
+/* DIGITAL1 bits */
+#define AC_DIG1_ENABLE (1<<0)
+#define AC_DIG1_V (1<<1)
+#define AC_DIG1_VCFG (1<<2)
+#define AC_DIG1_EMPHASIS (1<<3)
+#define AC_DIG1_COPYRIGHT (1<<4)
+#define AC_DIG1_NONAUDIO (1<<5)
+#define AC_DIG1_PROFESSIONAL (1<<6)
+#define AC_DIG1_LEVEL (1<<7)
+
+/* DIGITAL2 bits */
+#define AC_DIG2_CC (0x7f<<0)
+
+/* Pin widget control - 8bit */
+#define AC_PINCTL_EPT (0x3<<0)
+#define AC_PINCTL_EPT_NATIVE 0
+#define AC_PINCTL_EPT_HBR 3
+#define AC_PINCTL_VREFEN (0x7<<0)
+#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
+#define AC_PINCTL_VREF_50 1 /* 50% */
+#define AC_PINCTL_VREF_GRD 2 /* ground */
+#define AC_PINCTL_VREF_80 4 /* 80% */
+#define AC_PINCTL_VREF_100 5 /* 100% */
+#define AC_PINCTL_IN_EN (1<<5)
+#define AC_PINCTL_OUT_EN (1<<6)
+#define AC_PINCTL_HP_EN (1<<7)
+
+/* Pin sense - 32bit */
+#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
+#define AC_PINSENSE_PRESENCE (1<<31)
+#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
+
+/* EAPD/BTL enable - 32bit */
+#define AC_EAPDBTL_BALANCED (1<<0)
+#define AC_EAPDBTL_EAPD (1<<1)
+#define AC_EAPDBTL_LR_SWAP (1<<2)
+
+/* HDMI ELD data */
+#define AC_ELDD_ELD_VALID (1<<31)
+#define AC_ELDD_ELD_DATA 0xff
+
+/* HDMI DIP size */
+#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
+#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
+
+/* HDMI DIP index */
+#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
+#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
+
+/* HDMI DIP xmit (transmit) control */
+#define AC_DIPXMIT_MASK (0x3<<6)
+#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
+#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
+#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
+
+/* HDMI content protection (CP) control */
+#define AC_CPCTRL_CES (1<<9) /* current encryption state */
+#define AC_CPCTRL_READY (1<<8) /* ready bit */
+#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
+#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
+
+/* Converter channel <-> HDMI slot mapping */
+#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
+#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
+
+/* configuration default - 32bit */
+#define AC_DEFCFG_SEQUENCE (0xf<<0)
+#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
+#define AC_DEFCFG_ASSOC_SHIFT 4
+#define AC_DEFCFG_MISC (0xf<<8)
+#define AC_DEFCFG_MISC_SHIFT 8
+#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
+#define AC_DEFCFG_COLOR (0xf<<12)
+#define AC_DEFCFG_COLOR_SHIFT 12
+#define AC_DEFCFG_CONN_TYPE (0xf<<16)
+#define AC_DEFCFG_CONN_TYPE_SHIFT 16
+#define AC_DEFCFG_DEVICE (0xf<<20)
+#define AC_DEFCFG_DEVICE_SHIFT 20
+#define AC_DEFCFG_LOCATION (0x3f<<24)
+#define AC_DEFCFG_LOCATION_SHIFT 24
+#define AC_DEFCFG_PORT_CONN (0x3<<30)
+#define AC_DEFCFG_PORT_CONN_SHIFT 30
+
+/* device device types (0x0-0xf) */
+enum {
+ AC_JACK_LINE_OUT,
+ AC_JACK_SPEAKER,
+ AC_JACK_HP_OUT,
+ AC_JACK_CD,
+ AC_JACK_SPDIF_OUT,
+ AC_JACK_DIG_OTHER_OUT,
+ AC_JACK_MODEM_LINE_SIDE,
+ AC_JACK_MODEM_HAND_SIDE,
+ AC_JACK_LINE_IN,
+ AC_JACK_AUX,
+ AC_JACK_MIC_IN,
+ AC_JACK_TELEPHONY,
+ AC_JACK_SPDIF_IN,
+ AC_JACK_DIG_OTHER_IN,
+ AC_JACK_OTHER = 0xf,
+};
+
+/* jack connection types (0x0-0xf) */
+enum {
+ AC_JACK_CONN_UNKNOWN,
+ AC_JACK_CONN_1_8,
+ AC_JACK_CONN_1_4,
+ AC_JACK_CONN_ATAPI,
+ AC_JACK_CONN_RCA,
+ AC_JACK_CONN_OPTICAL,
+ AC_JACK_CONN_OTHER_DIGITAL,
+ AC_JACK_CONN_OTHER_ANALOG,
+ AC_JACK_CONN_DIN,
+ AC_JACK_CONN_XLR,
+ AC_JACK_CONN_RJ11,
+ AC_JACK_CONN_COMB,
+ AC_JACK_CONN_OTHER = 0xf,
+};
+
+/* jack colors (0x0-0xf) */
+enum {
+ AC_JACK_COLOR_UNKNOWN,
+ AC_JACK_COLOR_BLACK,
+ AC_JACK_COLOR_GREY,
+ AC_JACK_COLOR_BLUE,
+ AC_JACK_COLOR_GREEN,
+ AC_JACK_COLOR_RED,
+ AC_JACK_COLOR_ORANGE,
+ AC_JACK_COLOR_YELLOW,
+ AC_JACK_COLOR_PURPLE,
+ AC_JACK_COLOR_PINK,
+ AC_JACK_COLOR_WHITE = 0xe,
+ AC_JACK_COLOR_OTHER,
+};
+
+/* Jack location (0x0-0x3f) */
+/* common case */
+enum {
+ AC_JACK_LOC_NONE,
+ AC_JACK_LOC_REAR,
+ AC_JACK_LOC_FRONT,
+ AC_JACK_LOC_LEFT,
+ AC_JACK_LOC_RIGHT,
+ AC_JACK_LOC_TOP,
+ AC_JACK_LOC_BOTTOM,
+};
+/* bits 4-5 */
+enum {
+ AC_JACK_LOC_EXTERNAL = 0x00,
+ AC_JACK_LOC_INTERNAL = 0x10,
+ AC_JACK_LOC_SEPARATE = 0x20,
+ AC_JACK_LOC_OTHER = 0x30,
+};
+enum {
+ /* external on primary chasis */
+ AC_JACK_LOC_REAR_PANEL = 0x07,
+ AC_JACK_LOC_DRIVE_BAY,
+ /* internal */
+ AC_JACK_LOC_RISER = 0x17,
+ AC_JACK_LOC_HDMI,
+ AC_JACK_LOC_ATAPI,
+ /* others */
+ AC_JACK_LOC_MOBILE_IN = 0x37,
+ AC_JACK_LOC_MOBILE_OUT,
+};
+
+/* Port connectivity (0-3) */
+enum {
+ AC_JACK_PORT_COMPLEX,
+ AC_JACK_PORT_NONE,
+ AC_JACK_PORT_FIXED,
+ AC_JACK_PORT_BOTH,
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 32
+
+/* max. codec address */
+#define HDA_MAX_CODEC_ADDRESS 0x0f
+
+/* max number of PCM devics per card */
+#define HDA_MAX_PCMS 10
+
+/* --------------------------------------------------------------------- */
+
+#endif
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
new file mode 100644
index 0000000..b2b6708
--- /dev/null
+++ b/hw/intel-hda.c
@@ -0,0 +1,1308 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * written by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "msi.h"
+#include "qemu-timer.h"
+#include "audiodev.h"
+#include "intel-hda.h"
+#include "intel-hda-defs.h"
+
+/* --------------------------------------------------------------------- */
+/* hda bus */
+
+static struct BusInfo hda_codec_bus_info = {
+ .name = "HDA",
+ .size = sizeof(HDACodecBus),
+ .props = (Property[]) {
+ DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus,
+ hda_codec_response_func response,
+ hda_codec_xfer_func xfer)
+{
+ qbus_create_inplace(&bus->qbus, &hda_codec_bus_info, dev, NULL);
+ bus->response = response;
+ bus->xfer = xfer;
+}
+
+static int hda_codec_dev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus);
+ HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ HDACodecDeviceInfo *info = DO_UPCAST(HDACodecDeviceInfo, qdev, base);
+
+ dev->info = info;
+ if (dev->cad == -1) {
+ dev->cad = bus->next_cad;
+ }
+ if (dev->cad >= 15) {
+ return -1;
+ }
+ bus->next_cad = dev->cad + 1;
+ return info->init(dev);
+}
+
+static int hda_codec_dev_exit(DeviceState *qdev)
+{
+ HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+
+ if (dev->info->exit) {
+ dev->info->exit(dev);
+ }
+ return 0;
+}
+
+void hda_codec_register(HDACodecDeviceInfo *info)
+{
+ info->qdev.init = hda_codec_dev_init;
+ info->qdev.exit = hda_codec_dev_exit;
+ info->qdev.bus_info = &hda_codec_bus_info;
+ qdev_register(&info->qdev);
+}
+
+HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
+{
+ DeviceState *qdev;
+ HDACodecDevice *cdev;
+
+ QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
+ cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ if (cdev->cad == cad) {
+ return cdev;
+ }
+ }
+ return NULL;
+}
+
+void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ bus->response(dev, solicited, response);
+}
+
+bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ return bus->xfer(dev, stnr, output, buf, len);
+}
+
+/* --------------------------------------------------------------------- */
+/* intel hda emulation */
+
+typedef struct IntelHDAStream IntelHDAStream;
+typedef struct IntelHDAState IntelHDAState;
+typedef struct IntelHDAReg IntelHDAReg;
+
+typedef struct bpl {
+ uint64_t addr;
+ uint32_t len;
+ uint32_t flags;
+} bpl;
+
+struct IntelHDAStream {
+ /* registers */
+ uint32_t ctl;
+ uint32_t lpib;
+ uint32_t cbl;
+ uint32_t lvi;
+ uint32_t fmt;
+ uint32_t bdlp_lbase;
+ uint32_t bdlp_ubase;
+
+ /* state */
+ bpl *bpl;
+ uint32_t bentries;
+ uint32_t bsize, be, bp;
+};
+
+struct IntelHDAState {
+ PCIDevice pci;
+ const char *name;
+ HDACodecBus codecs;
+
+ /* registers */
+ uint32_t g_ctl;
+ uint32_t wake_en;
+ uint32_t state_sts;
+ uint32_t int_ctl;
+ uint32_t int_sts;
+ uint32_t wall_clk;
+
+ uint32_t corb_lbase;
+ uint32_t corb_ubase;
+ uint32_t corb_rp;
+ uint32_t corb_wp;
+ uint32_t corb_ctl;
+ uint32_t corb_sts;
+ uint32_t corb_size;
+
+ uint32_t rirb_lbase;
+ uint32_t rirb_ubase;
+ uint32_t rirb_wp;
+ uint32_t rirb_cnt;
+ uint32_t rirb_ctl;
+ uint32_t rirb_sts;
+ uint32_t rirb_size;
+
+ uint32_t dp_lbase;
+ uint32_t dp_ubase;
+
+ uint32_t icw;
+ uint32_t irr;
+ uint32_t ics;
+
+ /* streams */
+ IntelHDAStream st[8];
+
+ /* state */
+ int mmio_addr;
+ uint32_t rirb_count;
+ int64_t wall_base_ns;
+
+ /* debug logging */
+ const IntelHDAReg *last_reg;
+ uint32_t last_val;
+ uint32_t last_write;
+ uint32_t last_sec;
+ uint32_t repeat_count;
+
+ /* properties */
+ uint32_t debug;
+ uint32_t msi;
+};
+
+struct IntelHDAReg {
+ const char *name; /* register name */
+ uint32_t size; /* size in bytes */
+ uint32_t reset; /* reset value */
+ uint32_t wmask; /* write mask */
+ uint32_t wclear; /* write 1 to clear bits */
+ uint32_t offset; /* location in IntelHDAState */
+ uint32_t shift; /* byte access entries for dwords */
+ uint32_t stream;
+ void (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old);
+ void (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg);
+};
+
+static void intel_hda_reset(DeviceState *dev);
+
+/* --------------------------------------------------------------------- */
+
+static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase)
+{
+ target_phys_addr_t addr;
+
+#if TARGET_PHYS_ADDR_BITS == 32
+ addr = lbase;
+#else
+ addr = ubase;
+ addr <<= 32;
+ addr |= lbase;
+#endif
+ return addr;
+}
+
+static void stl_phys_le(target_phys_addr_t addr, uint32_t value)
+{
+ uint32_t value_le = cpu_to_le32(value);
+ cpu_physical_memory_write(addr, (uint8_t*)(&value_le), sizeof(value_le));
+}
+
+static uint32_t ldl_phys_le(target_phys_addr_t addr)
+{
+ uint32_t value_le;
+ cpu_physical_memory_read(addr, (uint8_t*)(&value_le), sizeof(value_le));
+ return le32_to_cpu(value_le);
+}
+
+static void intel_hda_update_int_sts(IntelHDAState *d)
+{
+ uint32_t sts = 0;
+ uint32_t i;
+
+ /* update controller status */
+ if (d->rirb_sts & ICH6_RBSTS_IRQ) {
+ sts |= (1 << 30);
+ }
+ if (d->rirb_sts & ICH6_RBSTS_OVERRUN) {
+ sts |= (1 << 30);
+ }
+ if (d->state_sts & d->wake_en) {
+ sts |= (1 << 30);
+ }
+
+ /* update stream status */
+ for (i = 0; i < 8; i++) {
+ /* buffer completion interrupt */
+ if (d->st[i].ctl & (1 << 26)) {
+ sts |= (1 << i);
+ }
+ }
+
+ /* update global status */
+ if (sts & d->int_ctl) {
+ sts |= (1 << 31);
+ }
+
+ d->int_sts = sts;
+}
+
+static void intel_hda_update_irq(IntelHDAState *d)
+{
+ int msi = d->msi && msi_enabled(&d->pci);
+ int level;
+
+ intel_hda_update_int_sts(d);
+ if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) {
+ level = 1;
+ } else {
+ level = 0;
+ }
+ dprint(d, 2, "%s: level %d [%s]\n", __FUNCTION__,
+ level, msi ? "msi" : "intx");
+ if (msi) {
+ if (level) {
+ msi_notify(&d->pci, 0);
+ }
+ } else {
+ qemu_set_irq(d->pci.irq[0], level);
+ }
+}
+
+static int intel_hda_send_command(IntelHDAState *d, uint32_t verb)
+{
+ uint32_t cad, nid, data;
+ HDACodecDevice *codec;
+
+ cad = (verb >> 28) & 0x0f;
+ if (verb & (1 << 27)) {
+ /* indirect node addressing, not specified in HDA 1.0 */
+ dprint(d, 1, "%s: indirect node addressing (guest bug?)\n", __FUNCTION__);
+ return -1;
+ }
+ nid = (verb >> 20) & 0x7f;
+ data = verb & 0xfffff;
+
+ codec = hda_codec_find(&d->codecs, cad);
+ if (codec == NULL) {
+ dprint(d, 1, "%s: addressed non-existing codec\n", __FUNCTION__);
+ return -1;
+ }
+ codec->info->command(codec, nid, data);
+ return 0;
+}
+
+static void intel_hda_corb_run(IntelHDAState *d)
+{
+ target_phys_addr_t addr;
+ uint32_t rp, verb;
+
+ if (d->ics & ICH6_IRS_BUSY) {
+ dprint(d, 2, "%s: [icw] verb 0x%08x\n", __FUNCTION__, d->icw);
+ intel_hda_send_command(d, d->icw);
+ return;
+ }
+
+ for (;;) {
+ if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) {
+ dprint(d, 2, "%s: !run\n", __FUNCTION__);
+ return;
+ }
+ if ((d->corb_rp & 0xff) == d->corb_wp) {
+ dprint(d, 2, "%s: corb ring empty\n", __FUNCTION__);
+ return;
+ }
+ if (d->rirb_count == d->rirb_cnt) {
+ dprint(d, 2, "%s: rirb count reached\n", __FUNCTION__);
+ return;
+ }
+
+ rp = (d->corb_rp + 1) & 0xff;
+ addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
+ verb = ldl_phys_le(addr + 4*rp);
+ d->corb_rp = rp;
+
+ dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb);
+ intel_hda_send_command(d, verb);
+ }
+}
+
+static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
+ target_phys_addr_t addr;
+ uint32_t wp, ex;
+
+ if (d->ics & ICH6_IRS_BUSY) {
+ dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n",
+ __FUNCTION__, response, dev->cad);
+ d->irr = response;
+ d->ics &= ~(ICH6_IRS_BUSY | 0xf0);
+ d->ics |= (ICH6_IRS_VALID | (dev->cad << 4));
+ return;
+ }
+
+ if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) {
+ dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __FUNCTION__);
+ return;
+ }
+
+ ex = (solicited ? 0 : (1 << 4)) | dev->cad;
+ wp = (d->rirb_wp + 1) & 0xff;
+ addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase);
+ stl_phys_le(addr + 8*wp, response);
+ stl_phys_le(addr + 8*wp + 4, ex);
+ d->rirb_wp = wp;
+
+ dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n",
+ __FUNCTION__, wp, response, ex);
+
+ d->rirb_count++;
+ if (d->rirb_count == d->rirb_cnt) {
+ dprint(d, 2, "%s: rirb count reached (%d)\n", __FUNCTION__, d->rirb_count);
+ if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
+ d->rirb_sts |= ICH6_RBSTS_IRQ;
+ intel_hda_update_irq(d);
+ }
+ } else if ((d->corb_rp & 0xff) == d->corb_wp) {
+ dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __FUNCTION__,
+ d->rirb_count, d->rirb_cnt);
+ if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
+ d->rirb_sts |= ICH6_RBSTS_IRQ;
+ intel_hda_update_irq(d);
+ }
+ }
+}
+
+static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len)
+{
+ HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+ IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
+ IntelHDAStream *st = NULL;
+ target_phys_addr_t addr;
+ uint32_t s, copy, left;
+ bool irq = false;
+
+ for (s = 0; s < ARRAY_SIZE(d->st); s++) {
+ if (stnr == ((d->st[s].ctl >> 20) & 0x0f)) {
+ st = d->st + s;
+ break;
+ }
+ }
+ if (st == NULL) {
+ return false;
+ }
+ if (st->bpl == NULL) {
+ return false;
+ }
+ if (st->ctl & (1 << 26)) {
+ /*
+ * Wait with the next DMA xfer until the guest
+ * has acked the buffer completion interrupt
+ */
+ return false;
+ }
+
+ left = len;
+ while (left > 0) {
+ copy = left;
+ if (copy > st->bsize - st->lpib)
+ copy = st->bsize - st->lpib;
+ if (copy > st->bpl[st->be].len - st->bp)
+ copy = st->bpl[st->be].len - st->bp;
+
+ dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n",
+ st->be, st->bp, st->bpl[st->be].len, copy);
+
+ cpu_physical_memory_rw(st->bpl[st->be].addr + st->bp,
+ buf, copy, !output);
+ st->lpib += copy;
+ st->bp += copy;
+ buf += copy;
+ left -= copy;
+
+ if (st->bpl[st->be].len == st->bp) {
+ /* bpl entry filled */
+ if (st->bpl[st->be].flags & 0x01) {
+ irq = true;
+ }
+ st->bp = 0;
+ st->be++;
+ if (st->be == st->bentries) {
+ /* bpl wrap around */
+ st->be = 0;
+ st->lpib = 0;
+ }
+ }
+ }
+ if (d->dp_lbase & 0x01) {
+ addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase);
+ stl_phys_le(addr + 8*s, st->lpib);
+ }
+ dprint(d, 3, "dma: --\n");
+
+ if (irq) {
+ st->ctl |= (1 << 26); /* buffer completion interrupt */
+ intel_hda_update_irq(d);
+ }
+ return true;
+}
+
+static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
+{
+ target_phys_addr_t addr;
+ uint8_t buf[16];
+ uint32_t i;
+
+ addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase);
+ st->bentries = st->lvi +1;
+ qemu_free(st->bpl);
+ st->bpl = qemu_malloc(sizeof(bpl) * st->bentries);
+ for (i = 0; i < st->bentries; i++, addr += 16) {
+ cpu_physical_memory_read(addr, buf, 16);
+ st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf);
+ st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8));
+ st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12));
+ dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n",
+ i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags);
+ }
+
+ st->bsize = st->cbl;
+ st->lpib = 0;
+ st->be = 0;
+ st->bp = 0;
+}
+
+static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running)
+{
+ DeviceState *qdev;
+ HDACodecDevice *cdev;
+
+ QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
+ cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ if (cdev->info->stream) {
+ cdev->info->stream(cdev, stream, running);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ if ((d->g_ctl & ICH6_GCTL_RESET) == 0) {
+ intel_hda_reset(&d->pci.qdev);
+ }
+}
+
+static void intel_hda_set_wake_en(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+}
+
+static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+}
+
+static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+}
+
+static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg)
+{
+ int64_t ns;
+
+ ns = qemu_get_clock_ns(vm_clock) - d->wall_base_ns;
+ d->wall_clk = (uint32_t)(ns * 24 / 1000); /* 24 MHz */
+}
+
+static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_corb_run(d);
+}
+
+static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_corb_run(d);
+}
+
+static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ if (d->rirb_wp & ICH6_RIRBWP_RST) {
+ d->rirb_wp = 0;
+ }
+}
+
+static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ intel_hda_update_irq(d);
+
+ if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) {
+ /* cleared ICH6_RBSTS_IRQ */
+ d->rirb_count = 0;
+ intel_hda_corb_run(d);
+ }
+}
+
+static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ if (d->ics & ICH6_IRS_BUSY) {
+ intel_hda_corb_run(d);
+ }
+}
+
+static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+ IntelHDAStream *st = d->st + reg->stream;
+
+ if (st->ctl & 0x01) {
+ /* reset */
+ dprint(d, 1, "st #%d: reset\n", reg->stream);
+ st->ctl = 0;
+ }
+ if ((st->ctl & 0x02) != (old & 0x02)) {
+ uint32_t stnr = (st->ctl >> 20) & 0x0f;
+ /* run bit flipped */
+ if (st->ctl & 0x02) {
+ /* start */
+ dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
+ reg->stream, stnr, st->cbl);
+ intel_hda_parse_bdl(d, st);
+ intel_hda_notify_codecs(d, stnr, true);
+ } else {
+ /* stop */
+ dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
+ intel_hda_notify_codecs(d, stnr, false);
+ }
+ }
+ intel_hda_update_irq(d);
+}
+
+/* --------------------------------------------------------------------- */
+
+#define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o))
+
+static const struct IntelHDAReg regtab[] = {
+ /* global */
+ [ ICH6_REG_GCAP ] = {
+ .name = "GCAP",
+ .size = 2,
+ .reset = 0x4401,
+ },
+ [ ICH6_REG_VMIN ] = {
+ .name = "VMIN",
+ .size = 1,
+ },
+ [ ICH6_REG_VMAJ ] = {
+ .name = "VMAJ",
+ .size = 1,
+ .reset = 1,
+ },
+ [ ICH6_REG_OUTPAY ] = {
+ .name = "OUTPAY",
+ .size = 2,
+ .reset = 0x3c,
+ },
+ [ ICH6_REG_INPAY ] = {
+ .name = "INPAY",
+ .size = 2,
+ .reset = 0x1d,
+ },
+ [ ICH6_REG_GCTL ] = {
+ .name = "GCTL",
+ .size = 4,
+ .wmask = 0x0103,
+ .offset = offsetof(IntelHDAState, g_ctl),
+ .whandler = intel_hda_set_g_ctl,
+ },
+ [ ICH6_REG_WAKEEN ] = {
+ .name = "WAKEEN",
+ .size = 2,
+ .wmask = 0x7fff,
+ .offset = offsetof(IntelHDAState, wake_en),
+ .whandler = intel_hda_set_wake_en,
+ },
+ [ ICH6_REG_STATESTS ] = {
+ .name = "STATESTS",
+ .size = 2,
+ .wmask = 0x7fff,
+ .wclear = 0x7fff,
+ .offset = offsetof(IntelHDAState, state_sts),
+ .whandler = intel_hda_set_state_sts,
+ },
+
+ /* interrupts */
+ [ ICH6_REG_INTCTL ] = {
+ .name = "INTCTL",
+ .size = 4,
+ .wmask = 0xc00000ff,
+ .offset = offsetof(IntelHDAState, int_ctl),
+ .whandler = intel_hda_set_int_ctl,
+ },
+ [ ICH6_REG_INTSTS ] = {
+ .name = "INTSTS",
+ .size = 4,
+ .wmask = 0xc00000ff,
+ .wclear = 0xc00000ff,
+ .offset = offsetof(IntelHDAState, int_sts),
+ },
+
+ /* misc */
+ [ ICH6_REG_WALLCLK ] = {
+ .name = "WALLCLK",
+ .size = 4,
+ .offset = offsetof(IntelHDAState, wall_clk),
+ .rhandler = intel_hda_get_wall_clk,
+ },
+ [ ICH6_REG_WALLCLK + 0x2000 ] = {
+ .name = "WALLCLK(alias)",
+ .size = 4,
+ .offset = offsetof(IntelHDAState, wall_clk),
+ .rhandler = intel_hda_get_wall_clk,
+ },
+
+ /* dma engine */
+ [ ICH6_REG_CORBLBASE ] = {
+ .name = "CORBLBASE",
+ .size = 4,
+ .wmask = 0xffffff80,
+ .offset = offsetof(IntelHDAState, corb_lbase),
+ },
+ [ ICH6_REG_CORBUBASE ] = {
+ .name = "CORBUBASE",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, corb_ubase),
+ },
+ [ ICH6_REG_CORBWP ] = {
+ .name = "CORBWP",
+ .size = 2,
+ .wmask = 0xff,
+ .offset = offsetof(IntelHDAState, corb_wp),
+ .whandler = intel_hda_set_corb_wp,
+ },
+ [ ICH6_REG_CORBRP ] = {
+ .name = "CORBRP",
+ .size = 2,
+ .wmask = 0x80ff,
+ .offset = offsetof(IntelHDAState, corb_rp),
+ },
+ [ ICH6_REG_CORBCTL ] = {
+ .name = "CORBCTL",
+ .size = 1,
+ .wmask = 0x03,
+ .offset = offsetof(IntelHDAState, corb_ctl),
+ .whandler = intel_hda_set_corb_ctl,
+ },
+ [ ICH6_REG_CORBSTS ] = {
+ .name = "CORBSTS",
+ .size = 1,
+ .wmask = 0x01,
+ .wclear = 0x01,
+ .offset = offsetof(IntelHDAState, corb_sts),
+ },
+ [ ICH6_REG_CORBSIZE ] = {
+ .name = "CORBSIZE",
+ .size = 1,
+ .reset = 0x42,
+ .offset = offsetof(IntelHDAState, corb_size),
+ },
+ [ ICH6_REG_RIRBLBASE ] = {
+ .name = "RIRBLBASE",
+ .size = 4,
+ .wmask = 0xffffff80,
+ .offset = offsetof(IntelHDAState, rirb_lbase),
+ },
+ [ ICH6_REG_RIRBUBASE ] = {
+ .name = "RIRBUBASE",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, rirb_ubase),
+ },
+ [ ICH6_REG_RIRBWP ] = {
+ .name = "RIRBWP",
+ .size = 2,
+ .wmask = 0x8000,
+ .offset = offsetof(IntelHDAState, rirb_wp),
+ .whandler = intel_hda_set_rirb_wp,
+ },
+ [ ICH6_REG_RINTCNT ] = {
+ .name = "RINTCNT",
+ .size = 2,
+ .wmask = 0xff,
+ .offset = offsetof(IntelHDAState, rirb_cnt),
+ },
+ [ ICH6_REG_RIRBCTL ] = {
+ .name = "RIRBCTL",
+ .size = 1,
+ .wmask = 0x07,
+ .offset = offsetof(IntelHDAState, rirb_ctl),
+ },
+ [ ICH6_REG_RIRBSTS ] = {
+ .name = "RIRBSTS",
+ .size = 1,
+ .wmask = 0x05,
+ .wclear = 0x05,
+ .offset = offsetof(IntelHDAState, rirb_sts),
+ .whandler = intel_hda_set_rirb_sts,
+ },
+ [ ICH6_REG_RIRBSIZE ] = {
+ .name = "RIRBSIZE",
+ .size = 1,
+ .reset = 0x42,
+ .offset = offsetof(IntelHDAState, rirb_size),
+ },
+
+ [ ICH6_REG_DPLBASE ] = {
+ .name = "DPLBASE",
+ .size = 4,
+ .wmask = 0xffffff81,
+ .offset = offsetof(IntelHDAState, dp_lbase),
+ },
+ [ ICH6_REG_DPUBASE ] = {
+ .name = "DPUBASE",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, dp_ubase),
+ },
+
+ [ ICH6_REG_IC ] = {
+ .name = "ICW",
+ .size = 4,
+ .wmask = 0xffffffff,
+ .offset = offsetof(IntelHDAState, icw),
+ },
+ [ ICH6_REG_IR ] = {
+ .name = "IRR",
+ .size = 4,
+ .offset = offsetof(IntelHDAState, irr),
+ },
+ [ ICH6_REG_IRS ] = {
+ .name = "ICS",
+ .size = 2,
+ .wmask = 0x0003,
+ .wclear = 0x0002,
+ .offset = offsetof(IntelHDAState, ics),
+ .whandler = intel_hda_set_ics,
+ },
+
+#define HDA_STREAM(_t, _i) \
+ [ ST_REG(_i, ICH6_REG_SD_CTL) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CTL", \
+ .size = 4, \
+ .wmask = 0x1cff001f, \
+ .offset = offsetof(IntelHDAState, st[_i].ctl), \
+ .whandler = intel_hda_set_st_ctl, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CTL(stnr)", \
+ .size = 1, \
+ .shift = 16, \
+ .wmask = 0x00ff0000, \
+ .offset = offsetof(IntelHDAState, st[_i].ctl), \
+ .whandler = intel_hda_set_st_ctl, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_STS)] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CTL(sts)", \
+ .size = 1, \
+ .shift = 24, \
+ .wmask = 0x1c000000, \
+ .wclear = 0x1c000000, \
+ .offset = offsetof(IntelHDAState, st[_i].ctl), \
+ .whandler = intel_hda_set_st_ctl, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " LPIB", \
+ .size = 4, \
+ .offset = offsetof(IntelHDAState, st[_i].lpib), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " LPIB(alias)", \
+ .size = 4, \
+ .offset = offsetof(IntelHDAState, st[_i].lpib), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_CBL) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " CBL", \
+ .size = 4, \
+ .wmask = 0xffffffff, \
+ .offset = offsetof(IntelHDAState, st[_i].cbl), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_LVI) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " LVI", \
+ .size = 2, \
+ .wmask = 0x00ff, \
+ .offset = offsetof(IntelHDAState, st[_i].lvi), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " FIFOS", \
+ .size = 2, \
+ .reset = HDA_BUFFER_SIZE, \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " FMT", \
+ .size = 2, \
+ .wmask = 0x7f7f, \
+ .offset = offsetof(IntelHDAState, st[_i].fmt), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " BDLPL", \
+ .size = 4, \
+ .wmask = 0xffffff80, \
+ .offset = offsetof(IntelHDAState, st[_i].bdlp_lbase), \
+ }, \
+ [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = { \
+ .stream = _i, \
+ .name = _t stringify(_i) " BDLPU", \
+ .size = 4, \
+ .wmask = 0xffffffff, \
+ .offset = offsetof(IntelHDAState, st[_i].bdlp_ubase), \
+ }, \
+
+ HDA_STREAM("IN", 0)
+ HDA_STREAM("IN", 1)
+ HDA_STREAM("IN", 2)
+ HDA_STREAM("IN", 3)
+
+ HDA_STREAM("OUT", 4)
+ HDA_STREAM("OUT", 5)
+ HDA_STREAM("OUT", 6)
+ HDA_STREAM("OUT", 7)
+
+};
+
+static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, target_phys_addr_t addr)
+{
+ const IntelHDAReg *reg;
+
+ if (addr >= sizeof(regtab)/sizeof(regtab[0])) {
+ goto noreg;
+ }
+ reg = regtab+addr;
+ if (reg->name == NULL) {
+ goto noreg;
+ }
+ return reg;
+
+noreg:
+ dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr);
+ return NULL;
+}
+
+static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg)
+{
+ uint8_t *addr = (void*)d;
+
+ addr += reg->offset;
+ return (uint32_t*)addr;
+}
+
+static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val,
+ uint32_t wmask)
+{
+ uint32_t *addr;
+ uint32_t old;
+
+ if (!reg) {
+ return;
+ }
+
+ if (d->debug) {
+ time_t now = time(NULL);
+ if (d->last_write && d->last_reg == reg && d->last_val == val) {
+ d->repeat_count++;
+ if (d->last_sec != now) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ } else {
+ if (d->repeat_count) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ }
+ dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask);
+ d->last_write = 1;
+ d->last_reg = reg;
+ d->last_val = val;
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ }
+ assert(reg->offset != 0);
+
+ addr = intel_hda_reg_addr(d, reg);
+ old = *addr;
+
+ if (reg->shift) {
+ val <<= reg->shift;
+ wmask <<= reg->shift;
+ }
+ wmask &= reg->wmask;
+ *addr &= ~wmask;
+ *addr |= wmask & val;
+ *addr &= ~(val & reg->wclear);
+
+ if (reg->whandler) {
+ reg->whandler(d, reg, old);
+ }
+}
+
+static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg,
+ uint32_t rmask)
+{
+ uint32_t *addr, ret;
+
+ if (!reg) {
+ return 0;
+ }
+
+ if (reg->rhandler) {
+ reg->rhandler(d, reg);
+ }
+
+ if (reg->offset == 0) {
+ /* constant read-only register */
+ ret = reg->reset;
+ } else {
+ addr = intel_hda_reg_addr(d, reg);
+ ret = *addr;
+ if (reg->shift) {
+ ret >>= reg->shift;
+ }
+ ret &= rmask;
+ }
+ if (d->debug) {
+ time_t now = time(NULL);
+ if (!d->last_write && d->last_reg == reg && d->last_val == ret) {
+ d->repeat_count++;
+ if (d->last_sec != now) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ } else {
+ if (d->repeat_count) {
+ dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+ }
+ dprint(d, 2, "read %-16s: 0x%x (%x)\n", reg->name, ret, rmask);
+ d->last_write = 0;
+ d->last_reg = reg;
+ d->last_val = ret;
+ d->last_sec = now;
+ d->repeat_count = 0;
+ }
+ }
+ return ret;
+}
+
+static void intel_hda_regs_reset(IntelHDAState *d)
+{
+ uint32_t *addr;
+ int i;
+
+ for (i = 0; i < sizeof(regtab)/sizeof(regtab[0]); i++) {
+ if (regtab[i].name == NULL) {
+ continue;
+ }
+ if (regtab[i].offset == 0) {
+ continue;
+ }
+ addr = intel_hda_reg_addr(d, regtab + i);
+ *addr = regtab[i].reset;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ intel_hda_reg_write(d, reg, val, 0xff);
+}
+
+static void intel_hda_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ intel_hda_reg_write(d, reg, val, 0xffff);
+}
+
+static void intel_hda_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ intel_hda_reg_write(d, reg, val, 0xffffffff);
+}
+
+static uint32_t intel_hda_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ return intel_hda_reg_read(d, reg, 0xff);
+}
+
+static uint32_t intel_hda_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ return intel_hda_reg_read(d, reg, 0xffff);
+}
+
+static uint32_t intel_hda_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ IntelHDAState *d = opaque;
+ const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+ return intel_hda_reg_read(d, reg, 0xffffffff);
+}
+
+static CPUReadMemoryFunc * const intel_hda_mmio_read[3] = {
+ intel_hda_mmio_readb,
+ intel_hda_mmio_readw,
+ intel_hda_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const intel_hda_mmio_write[3] = {
+ intel_hda_mmio_writeb,
+ intel_hda_mmio_writew,
+ intel_hda_mmio_writel,
+};
+
+static void intel_hda_map(PCIDevice *pci, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+
+ cpu_register_physical_memory(addr, 0x4000, d->mmio_addr);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_reset(DeviceState *dev)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci.qdev, dev);
+ DeviceState *qdev;
+ HDACodecDevice *cdev;
+
+ intel_hda_regs_reset(d);
+ d->wall_base_ns = qemu_get_clock(vm_clock);
+
+ /* reset codecs */
+ QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
+ cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+ if (qdev->info->reset) {
+ qdev->info->reset(qdev);
+ }
+ d->state_sts |= (1 << cdev->cad);
+ }
+ intel_hda_update_irq(d);
+}
+
+static int intel_hda_init(PCIDevice *pci)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+ uint8_t *conf = d->pci.config;
+
+ d->name = d->pci.qdev.info->name;
+
+ pci_config_set_vendor_id(conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(conf, 0x2668);
+ pci_config_set_revision(conf, 1);
+ pci_config_set_class(conf, PCI_CLASS_MULTIMEDIA_HD_AUDIO);
+ pci_config_set_interrupt_pin(conf, 1);
+
+ /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
+ conf[0x40] = 0x01;
+
+ d->mmio_addr = cpu_register_io_memory(intel_hda_mmio_read,
+ intel_hda_mmio_write, d,
+ DEVICE_NATIVE_ENDIAN);
+ pci_register_bar(&d->pci, 0, 0x4000, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ intel_hda_map);
+ if (d->msi) {
+ msi_init(&d->pci, 0x50, 1, true, false);
+ }
+
+ hda_codec_bus_init(&d->pci.qdev, &d->codecs,
+ intel_hda_response, intel_hda_xfer);
+
+ return 0;
+}
+
+static int intel_hda_exit(PCIDevice *pci)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+
+ if (d->msi) {
+ msi_uninit(&d->pci);
+ }
+ cpu_unregister_io_memory(d->mmio_addr);
+ return 0;
+}
+
+static void intel_hda_write_config(PCIDevice *pci, uint32_t addr,
+ uint32_t val, int len)
+{
+ IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+
+ pci_default_write_config(pci, addr, val, len);
+ if (d->msi) {
+ msi_write_config(pci, addr, val, len);
+ }
+}
+
+static int intel_hda_post_load(void *opaque, int version)
+{
+ IntelHDAState* d = opaque;
+ int i;
+
+ dprint(d, 1, "%s\n", __FUNCTION__);
+ for (i = 0; i < ARRAY_SIZE(d->st); i++) {
+ if (d->st[i].ctl & 0x02) {
+ intel_hda_parse_bdl(d, &d->st[i]);
+ }
+ }
+ intel_hda_update_irq(d);
+ return 0;
+}
+
+static const VMStateDescription vmstate_intel_hda_stream = {
+ .name = "intel-hda-stream",
+ .version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(ctl, IntelHDAStream),
+ VMSTATE_UINT32(lpib, IntelHDAStream),
+ VMSTATE_UINT32(cbl, IntelHDAStream),
+ VMSTATE_UINT32(lvi, IntelHDAStream),
+ VMSTATE_UINT32(fmt, IntelHDAStream),
+ VMSTATE_UINT32(bdlp_lbase, IntelHDAStream),
+ VMSTATE_UINT32(bdlp_ubase, IntelHDAStream),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_intel_hda = {
+ .name = "intel-hda",
+ .version_id = 1,
+ .post_load = intel_hda_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(pci, IntelHDAState),
+
+ /* registers */
+ VMSTATE_UINT32(g_ctl, IntelHDAState),
+ VMSTATE_UINT32(wake_en, IntelHDAState),
+ VMSTATE_UINT32(state_sts, IntelHDAState),
+ VMSTATE_UINT32(int_ctl, IntelHDAState),
+ VMSTATE_UINT32(int_sts, IntelHDAState),
+ VMSTATE_UINT32(wall_clk, IntelHDAState),
+ VMSTATE_UINT32(corb_lbase, IntelHDAState),
+ VMSTATE_UINT32(corb_ubase, IntelHDAState),
+ VMSTATE_UINT32(corb_rp, IntelHDAState),
+ VMSTATE_UINT32(corb_wp, IntelHDAState),
+ VMSTATE_UINT32(corb_ctl, IntelHDAState),
+ VMSTATE_UINT32(corb_sts, IntelHDAState),
+ VMSTATE_UINT32(corb_size, IntelHDAState),
+ VMSTATE_UINT32(rirb_lbase, IntelHDAState),
+ VMSTATE_UINT32(rirb_ubase, IntelHDAState),
+ VMSTATE_UINT32(rirb_wp, IntelHDAState),
+ VMSTATE_UINT32(rirb_cnt, IntelHDAState),
+ VMSTATE_UINT32(rirb_ctl, IntelHDAState),
+ VMSTATE_UINT32(rirb_sts, IntelHDAState),
+ VMSTATE_UINT32(rirb_size, IntelHDAState),
+ VMSTATE_UINT32(dp_lbase, IntelHDAState),
+ VMSTATE_UINT32(dp_ubase, IntelHDAState),
+ VMSTATE_UINT32(icw, IntelHDAState),
+ VMSTATE_UINT32(irr, IntelHDAState),
+ VMSTATE_UINT32(ics, IntelHDAState),
+ VMSTATE_STRUCT_ARRAY(st, IntelHDAState, 8, 0,
+ vmstate_intel_hda_stream,
+ IntelHDAStream),
+
+ /* additional state info */
+ VMSTATE_UINT32(rirb_count, IntelHDAState),
+ VMSTATE_INT64(wall_base_ns, IntelHDAState),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo intel_hda_info = {
+ .qdev.name = "intel-hda",
+ .qdev.desc = "Intel HD Audio Controller",
+ .qdev.size = sizeof(IntelHDAState),
+ .qdev.vmsd = &vmstate_intel_hda,
+ .qdev.reset = intel_hda_reset,
+ .init = intel_hda_init,
+ .exit = intel_hda_exit,
+ .config_write = intel_hda_write_config,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0),
+ DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void intel_hda_register(void)
+{
+ pci_qdev_register(&intel_hda_info);
+}
+device_init(intel_hda_register);
+
+/*
+ * create intel hda controller with codec attached to it,
+ * so '-soundhw hda' works.
+ */
+int intel_hda_and_codec_init(PCIBus *bus)
+{
+ PCIDevice *controller;
+ BusState *hdabus;
+ DeviceState *codec;
+
+ controller = pci_create_simple(bus, -1, "intel-hda");
+ hdabus = QLIST_FIRST(&controller->qdev.child_bus);
+ codec = qdev_create(hdabus, "hda-duplex");
+ qdev_init_nofail(codec);
+ return 0;
+}
+
diff --git a/hw/intel-hda.h b/hw/intel-hda.h
new file mode 100644
index 0000000..4e44e38
--- /dev/null
+++ b/hw/intel-hda.h
@@ -0,0 +1,62 @@
+#ifndef HW_INTEL_HDA_H
+#define HW_INTEL_HDA_H
+
+#include "qdev.h"
+
+/* --------------------------------------------------------------------- */
+/* hda bus */
+
+typedef struct HDACodecBus HDACodecBus;
+typedef struct HDACodecDevice HDACodecDevice;
+typedef struct HDACodecDeviceInfo HDACodecDeviceInfo;
+
+typedef void (*hda_codec_response_func)(HDACodecDevice *dev,
+ bool solicited, uint32_t response);
+typedef bool (*hda_codec_xfer_func)(HDACodecDevice *dev,
+ uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len);
+
+struct HDACodecBus {
+ BusState qbus;
+ uint32_t next_cad;
+ hda_codec_response_func response;
+ hda_codec_xfer_func xfer;
+};
+
+struct HDACodecDevice {
+ DeviceState qdev;
+ HDACodecDeviceInfo *info;
+ uint32_t cad; /* codec address */
+};
+
+struct HDACodecDeviceInfo {
+ DeviceInfo qdev;
+ int (*init)(HDACodecDevice *dev);
+ int (*exit)(HDACodecDevice *dev);
+ void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data);
+ void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running);
+};
+
+void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus,
+ hda_codec_response_func response,
+ hda_codec_xfer_func xfer);
+void hda_codec_register(HDACodecDeviceInfo *info);
+HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad);
+
+void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response);
+bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ uint8_t *buf, uint32_t len);
+
+/* --------------------------------------------------------------------- */
+
+#define dprint(_dev, _level, _fmt, ...) \
+ do { \
+ if (_dev->debug >= _level) { \
+ fprintf(stderr, "%s: ", _dev->name); \
+ fprintf(stderr, _fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+/* --------------------------------------------------------------------- */
+
+#endif
diff --git a/hw/ioapic.c b/hw/ioapic.c
new file mode 100644
index 0000000..8fab34f
--- /dev/null
+++ b/hw/ioapic.c
@@ -0,0 +1,439 @@
+/*
+ * ioapic.c IOAPIC emulation logic
+ *
+ * Copyright (c) 2004-2005 Fabrice Bellard
+ *
+ * Split the ioapic logic from apic.c
+ * Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "apic.h"
+#include "sysemu.h"
+#include "apic.h"
+#include "ioapic.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+#include "sysbus.h"
+
+#include "kvm.h"
+
+//#define DEBUG_IOAPIC
+
+#ifdef DEBUG_IOAPIC
+#define DPRINTF(fmt, ...) \
+ do { printf("ioapic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define IOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000
+#define MAX_IOAPICS 1
+
+#define IOAPIC_VERSION 0x11
+
+#define IOAPIC_LVT_DEST_SHIFT 56
+#define IOAPIC_LVT_MASKED_SHIFT 16
+#define IOAPIC_LVT_TRIGGER_MODE_SHIFT 15
+#define IOAPIC_LVT_REMOTE_IRR_SHIFT 14
+#define IOAPIC_LVT_POLARITY_SHIFT 13
+#define IOAPIC_LVT_DELIV_STATUS_SHIFT 12
+#define IOAPIC_LVT_DEST_MODE_SHIFT 11
+#define IOAPIC_LVT_DELIV_MODE_SHIFT 8
+
+#define IOAPIC_LVT_MASKED (1 << IOAPIC_LVT_MASKED_SHIFT)
+#define IOAPIC_LVT_REMOTE_IRR (1 << IOAPIC_LVT_REMOTE_IRR_SHIFT)
+
+#define IOAPIC_TRIGGER_EDGE 0
+#define IOAPIC_TRIGGER_LEVEL 1
+
+/*io{apic,sapic} delivery mode*/
+#define IOAPIC_DM_FIXED 0x0
+#define IOAPIC_DM_LOWEST_PRIORITY 0x1
+#define IOAPIC_DM_PMI 0x2
+#define IOAPIC_DM_NMI 0x4
+#define IOAPIC_DM_INIT 0x5
+#define IOAPIC_DM_SIPI 0x6
+#define IOAPIC_DM_EXTINT 0x7
+#define IOAPIC_DM_MASK 0x7
+
+#define IOAPIC_VECTOR_MASK 0xff
+
+#define IOAPIC_IOREGSEL 0x00
+#define IOAPIC_IOWIN 0x10
+
+#define IOAPIC_REG_ID 0x00
+#define IOAPIC_REG_VER 0x01
+#define IOAPIC_REG_ARB 0x02
+#define IOAPIC_REG_REDTBL_BASE 0x10
+#define IOAPIC_ID 0x00
+
+#define IOAPIC_ID_SHIFT 24
+#define IOAPIC_ID_MASK 0xf
+
+#define IOAPIC_VER_ENTRIES_SHIFT 16
+
+typedef struct IOAPICState IOAPICState;
+
+struct IOAPICState {
+ SysBusDevice busdev;
+ uint8_t id;
+ uint8_t ioregsel;
+ uint64_t base_address;
+ uint32_t irr;
+ uint64_t ioredtbl[IOAPIC_NUM_PINS];
+};
+
+static IOAPICState *ioapics[MAX_IOAPICS];
+
+static void ioapic_service(IOAPICState *s)
+{
+ uint8_t i;
+ uint8_t trig_mode;
+ uint8_t vector;
+ uint8_t delivery_mode;
+ uint32_t mask;
+ uint64_t entry;
+ uint8_t dest;
+ uint8_t dest_mode;
+ uint8_t polarity;
+
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ mask = 1 << i;
+ if (s->irr & mask) {
+ entry = s->ioredtbl[i];
+ if (!(entry & IOAPIC_LVT_MASKED)) {
+ trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
+ dest = entry >> IOAPIC_LVT_DEST_SHIFT;
+ dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
+ delivery_mode =
+ (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
+ polarity = (entry >> IOAPIC_LVT_POLARITY_SHIFT) & 1;
+ if (trig_mode == IOAPIC_TRIGGER_EDGE) {
+ s->irr &= ~mask;
+ } else {
+ s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
+ }
+ if (delivery_mode == IOAPIC_DM_EXTINT) {
+ vector = pic_read_irq(isa_pic);
+ } else {
+ vector = entry & IOAPIC_VECTOR_MASK;
+ }
+ apic_deliver_irq(dest, dest_mode, delivery_mode,
+ vector, polarity, trig_mode);
+ }
+ }
+ }
+}
+
+static void ioapic_set_irq(void *opaque, int vector, int level)
+{
+ IOAPICState *s = opaque;
+
+ /* ISA IRQs map to GSI 1-1 except for IRQ0 which maps
+ * to GSI 2. GSI maps to ioapic 1-1. This is not
+ * the cleanest way of doing it but it should work. */
+
+ DPRINTF("%s: %s vec %x\n", __func__, level ? "raise" : "lower", vector);
+ if (vector == 0 && irq0override) {
+ vector = 2;
+ }
+ if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
+ uint32_t mask = 1 << vector;
+ uint64_t entry = s->ioredtbl[vector];
+
+ if (((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) ==
+ IOAPIC_TRIGGER_LEVEL) {
+ /* level triggered */
+ if (level) {
+ s->irr |= mask;
+ ioapic_service(s);
+ } else {
+ s->irr &= ~mask;
+ }
+ } else {
+ /* edge triggered */
+ if (level) {
+ s->irr |= mask;
+ ioapic_service(s);
+ }
+ }
+ }
+}
+
+void ioapic_eoi_broadcast(int vector)
+{
+ IOAPICState *s;
+ uint64_t entry;
+ int i, n;
+
+ for (i = 0; i < MAX_IOAPICS; i++) {
+ s = ioapics[i];
+ if (!s) {
+ continue;
+ }
+ for (n = 0; n < IOAPIC_NUM_PINS; n++) {
+ entry = s->ioredtbl[n];
+ if ((entry & IOAPIC_LVT_REMOTE_IRR)
+ && (entry & IOAPIC_VECTOR_MASK) == vector) {
+ s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR;
+ if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) {
+ ioapic_service(s);
+ }
+ }
+ }
+ }
+}
+
+static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ IOAPICState *s = opaque;
+ int index;
+ uint32_t val = 0;
+
+ switch (addr & 0xff) {
+ case IOAPIC_IOREGSEL:
+ val = s->ioregsel;
+ break;
+ case IOAPIC_IOWIN:
+ switch (s->ioregsel) {
+ case IOAPIC_REG_ID:
+ val = s->id << IOAPIC_ID_SHIFT;
+ break;
+ case IOAPIC_REG_VER:
+ val = IOAPIC_VERSION |
+ ((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT);
+ break;
+ case IOAPIC_REG_ARB:
+ val = 0;
+ break;
+ default:
+ index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
+ if (index >= 0 && index < IOAPIC_NUM_PINS) {
+ if (s->ioregsel & 1) {
+ val = s->ioredtbl[index] >> 32;
+ } else {
+ val = s->ioredtbl[index] & 0xffffffff;
+ }
+ }
+ }
+ DPRINTF("read: %08x = %08x\n", s->ioregsel, val);
+ break;
+ }
+ return val;
+}
+
+static void
+ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ IOAPICState *s = opaque;
+ int index;
+
+ switch (addr & 0xff) {
+ case IOAPIC_IOREGSEL:
+ s->ioregsel = val;
+ break;
+ case IOAPIC_IOWIN:
+ DPRINTF("write: %08x = %08x\n", s->ioregsel, val);
+ switch (s->ioregsel) {
+ case IOAPIC_REG_ID:
+ s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK;
+ break;
+ case IOAPIC_REG_VER:
+ case IOAPIC_REG_ARB:
+ break;
+ default:
+ index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1;
+ if (index >= 0 && index < IOAPIC_NUM_PINS) {
+ if (s->ioregsel & 1) {
+ s->ioredtbl[index] &= 0xffffffff;
+ s->ioredtbl[index] |= (uint64_t)val << 32;
+ } else {
+ s->ioredtbl[index] &= ~0xffffffffULL;
+ s->ioredtbl[index] |= val;
+ }
+ ioapic_service(s);
+ }
+ }
+ break;
+ }
+}
+
+static void kvm_kernel_ioapic_save_to_user(IOAPICState *s)
+{
+#if defined(KVM_CAP_IRQCHIP) && defined(TARGET_I386)
+ struct kvm_irqchip chip;
+ struct kvm_ioapic_state *kioapic;
+ int i;
+
+ chip.chip_id = KVM_IRQCHIP_IOAPIC;
+ kvm_get_irqchip(kvm_context, &chip);
+ kioapic = &chip.chip.ioapic;
+
+ s->id = kioapic->id;
+ s->ioregsel = kioapic->ioregsel;
+ s->base_address = kioapic->base_address;
+ s->irr = kioapic->irr;
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ s->ioredtbl[i] = kioapic->redirtbl[i].bits;
+ }
+#endif
+}
+
+static void kvm_kernel_ioapic_load_from_user(IOAPICState *s)
+{
+#if defined(KVM_CAP_IRQCHIP) && defined(TARGET_I386)
+ struct kvm_irqchip chip;
+ struct kvm_ioapic_state *kioapic;
+ int i;
+
+ chip.chip_id = KVM_IRQCHIP_IOAPIC;
+ kioapic = &chip.chip.ioapic;
+
+ kioapic->id = s->id;
+ kioapic->ioregsel = s->ioregsel;
+ kioapic->base_address = s->base_address;
+ kioapic->irr = s->irr;
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ kioapic->redirtbl[i].bits = s->ioredtbl[i];
+ }
+
+ kvm_set_irqchip(kvm_context, &chip);
+#endif
+}
+
+static void ioapic_pre_save(void *opaque)
+{
+ IOAPICState *s = (void *)opaque;
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_ioapic_save_to_user(s);
+ }
+}
+
+static int ioapic_pre_load(void *opaque)
+{
+ IOAPICState *s = opaque;
+
+ /* in case we are doing version 1, we just set these to sane values */
+ s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS;
+ s->irr = 0;
+ return 0;
+}
+
+static int ioapic_post_load(void *opaque, int version_id)
+{
+ IOAPICState *s = opaque;
+
+ if (version_id == 1) {
+ /* set sane value */
+ s->irr = 0;
+ }
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_ioapic_load_from_user(s);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_ioapic = {
+ .name = "ioapic",
+ .version_id = 3,
+ .post_load = ioapic_post_load,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_load = ioapic_pre_load,
+ .pre_save = ioapic_pre_save,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(id, IOAPICState),
+ VMSTATE_UINT8(ioregsel, IOAPICState),
+ VMSTATE_UINT64_V(base_address, IOAPICState, 2),
+ VMSTATE_UINT32_V(irr, IOAPICState, 2),
+ VMSTATE_UINT64_ARRAY(ioredtbl, IOAPICState, IOAPIC_NUM_PINS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ioapic_reset(DeviceState *d)
+{
+ IOAPICState *s = DO_UPCAST(IOAPICState, busdev.qdev, d);
+ int i;
+
+ s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS;
+ s->id = 0;
+ s->ioregsel = 0;
+ s->irr = 0;
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ s->ioredtbl[i] = 1 << IOAPIC_LVT_MASKED_SHIFT;
+ }
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_kernel_ioapic_load_from_user(s);
+ }
+#endif
+}
+
+static CPUReadMemoryFunc * const ioapic_mem_read[3] = {
+ ioapic_mem_readl,
+ ioapic_mem_readl,
+ ioapic_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const ioapic_mem_write[3] = {
+ ioapic_mem_writel,
+ ioapic_mem_writel,
+ ioapic_mem_writel,
+};
+
+static int ioapic_init1(SysBusDevice *dev)
+{
+ IOAPICState *s = FROM_SYSBUS(IOAPICState, dev);
+ int io_memory;
+ static int ioapic_no;
+
+ if (ioapic_no >= MAX_IOAPICS) {
+ return -1;
+ }
+
+ io_memory = cpu_register_io_memory(ioapic_mem_read,
+ ioapic_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, io_memory);
+
+ qdev_init_gpio_in(&dev->qdev, ioapic_set_irq, IOAPIC_NUM_PINS);
+
+ ioapics[ioapic_no++] = s;
+
+ return 0;
+}
+
+static SysBusDeviceInfo ioapic_info = {
+ .init = ioapic_init1,
+ .qdev.name = "ioapic",
+ .qdev.size = sizeof(IOAPICState),
+ .qdev.vmsd = &vmstate_ioapic,
+ .qdev.reset = ioapic_reset,
+ .qdev.no_user = 1,
+};
+
+static void ioapic_register_devices(void)
+{
+ sysbus_register_withprop(&ioapic_info);
+}
+
+device_init(ioapic_register_devices)
diff --git a/hw/ioapic.h b/hw/ioapic.h
new file mode 100644
index 0000000..cb2642a
--- /dev/null
+++ b/hw/ioapic.h
@@ -0,0 +1,20 @@
+/*
+ * ioapic.c IOAPIC emulation logic
+ *
+ * Copyright (c) 2011 Jan Kiszka, Siemens AG
+ *
+ * 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/>.
+ */
+
+void ioapic_eoi_broadcast(int vector);
diff --git a/hw/ioh3420.c b/hw/ioh3420.c
new file mode 100644
index 0000000..95adf09
--- /dev/null
+++ b/hw/ioh3420.c
@@ -0,0 +1,246 @@
+/*
+ * ioh3420.c
+ * Intel X58 north bridge IOH
+ * PCI Express root port device id 3420
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "pci_ids.h"
+#include "msi.h"
+#include "pcie.h"
+#include "ioh3420.h"
+
+#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */
+#define PCI_DEVICE_ID_IOH_REV 0x2
+#define IOH_EP_SSVID_OFFSET 0x40
+#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL
+#define IOH_EP_SSVID_SSID 0
+#define IOH_EP_MSI_OFFSET 0x60
+#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT
+#define IOH_EP_MSI_NR_VECTOR 2
+#define IOH_EP_EXP_OFFSET 0x90
+#define IOH_EP_AER_OFFSET 0x100
+
+/*
+ * If two MSI vector are allocated, Advanced Error Interrupt Message Number
+ * is 1. otherwise 0.
+ * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number.
+ */
+static uint8_t ioh3420_aer_vector(const PCIDevice *d)
+{
+ switch (msi_nr_vectors_allocated(d)) {
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ default:
+ break;
+ }
+ abort();
+ return 0;
+}
+
+static void ioh3420_aer_vector_update(PCIDevice *d)
+{
+ pcie_aer_root_set_vector(d, ioh3420_aer_vector(d));
+}
+
+static void ioh3420_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ uint32_t root_cmd =
+ pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
+
+ pci_bridge_write_config(d, address, val, len);
+ msi_write_config(d, address, val, len);
+ ioh3420_aer_vector_update(d);
+ pcie_cap_slot_write_config(d, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+ pcie_aer_root_write_config(d, address, val, len, root_cmd);
+}
+
+static void ioh3420_reset(DeviceState *qdev)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+ msi_reset(d);
+ ioh3420_aer_vector_update(d);
+ pcie_cap_root_reset(d);
+ pcie_cap_deverr_reset(d);
+ pcie_cap_slot_reset(d);
+ pcie_aer_root_reset(d);
+ pci_bridge_reset(qdev);
+ pci_bridge_disable_base_limit(d);
+}
+
+static int ioh3420_initfn(PCIDevice *d)
+{
+ PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+ PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+ PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+ int rc;
+ int tmp;
+
+ rc = pci_bridge_initfn(d);
+ if (rc < 0) {
+ return rc;
+ }
+
+ d->config[PCI_REVISION_ID] = PCI_DEVICE_ID_IOH_REV;
+ pcie_port_init_reg(d);
+
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_IOH_EPORT);
+
+ rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
+ IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
+ IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port);
+ if (rc < 0) {
+ goto err_msi;
+ }
+ pcie_cap_deverr_init(d);
+ pcie_cap_slot_init(d, s->slot);
+ pcie_chassis_create(s->chassis);
+ rc = pcie_chassis_add_slot(s);
+ if (rc < 0) {
+ goto err_pcie_cap;
+ return rc;
+ }
+ pcie_cap_root_init(d);
+ rc = pcie_aer_init(d, IOH_EP_AER_OFFSET);
+ if (rc < 0) {
+ goto err;
+ }
+ pcie_aer_root_init(d);
+ ioh3420_aer_vector_update(d);
+ return 0;
+
+err:
+ pcie_chassis_del_slot(s);
+err_pcie_cap:
+ pcie_cap_exit(d);
+err_msi:
+ msi_uninit(d);
+err_bridge:
+ tmp = pci_bridge_exitfn(d);
+ assert(!tmp);
+ return rc;
+}
+
+static int ioh3420_exitfn(PCIDevice *d)
+{
+ PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+ PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+ PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+
+ pcie_aer_exit(d);
+ pcie_chassis_del_slot(s);
+ pcie_cap_exit(d);
+ msi_uninit(d);
+ return pci_bridge_exitfn(d);
+}
+
+PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port, uint8_t chassis, uint16_t slot)
+{
+ PCIDevice *d;
+ PCIBridge *br;
+ DeviceState *qdev;
+
+ d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420");
+ if (!d) {
+ return NULL;
+ }
+ br = DO_UPCAST(PCIBridge, dev, d);
+
+ qdev = &br->dev.qdev;
+ pci_bridge_map_irq(br, bus_name, map_irq);
+ qdev_prop_set_uint8(qdev, "port", port);
+ qdev_prop_set_uint8(qdev, "chassis", chassis);
+ qdev_prop_set_uint16(qdev, "slot", slot);
+ qdev_init_nofail(qdev);
+
+ return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
+}
+
+static const VMStateDescription vmstate_ioh3420 = {
+ .name = "ioh-3240-express-root-port",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = pcie_cap_slot_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
+ VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0,
+ vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo ioh3420_info = {
+ .qdev.name = "ioh3420",
+ .qdev.desc = "Intel IOH device id 3420 PCIE Root Port",
+ .qdev.size = sizeof(PCIESlot),
+ .qdev.reset = ioh3420_reset,
+ .qdev.vmsd = &vmstate_ioh3420,
+
+ .is_express = 1,
+ .is_bridge = 1,
+ .config_write = ioh3420_write_config,
+ .init = ioh3420_initfn,
+ .exit = ioh3420_exitfn,
+
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
+ DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
+ DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
+ DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
+ port.br.dev.exp.aer_log.log_max,
+ PCIE_AER_LOG_MAX_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ioh3420_register(void)
+{
+ pci_qdev_register(&ioh3420_info);
+}
+
+device_init(ioh3420_register);
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/ioh3420.h b/hw/ioh3420.h
new file mode 100644
index 0000000..68c523a
--- /dev/null
+++ b/hw/ioh3420.h
@@ -0,0 +1,10 @@
+#ifndef QEMU_IOH3420_H
+#define QEMU_IOH3420_H
+
+#include "pcie_port.h"
+
+PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port, uint8_t chassis, uint16_t slot);
+
+#endif /* QEMU_IOH3420_H */
diff --git a/hw/ipf.c b/hw/ipf.c
new file mode 100644
index 0000000..10c21eb
--- /dev/null
+++ b/hw/ipf.c
@@ -0,0 +1,707 @@
+/*
+ * Itanium Platform Emulator derived from QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Copyright (c) 2007 Intel
+ * Ported for IA64 Platform Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "pci.h"
+#include "block.h"
+#include "sysemu.h"
+#include "audio/audio.h"
+#include "net.h"
+#include "smbus.h"
+#include "boards.h"
+#include "firmware.h"
+#include "ia64intrin.h"
+#include <unistd.h>
+#include "device-assignment.h"
+#include "virtio-blk.h"
+
+#include "qemu-kvm.h"
+
+#define FW_FILENAME "Flash.fd"
+
+/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */
+#define ACPI_DATA_SIZE 0x10000
+
+#define MAX_IDE_BUS 2
+
+static fdctrl_t *floppy_controller;
+static RTCState *rtc_state;
+static PCIDevice *i440fx_state;
+
+static uint32_t ipf_to_legacy_io(target_phys_addr_t addr)
+{
+ return (uint32_t)(((addr&0x3ffffff) >> 12 << 2)|((addr) & 0x3));
+}
+
+static void ipf_legacy_io_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val) {
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ cpu_outb(0, port, val);
+}
+
+static void ipf_legacy_io_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val) {
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ cpu_outw(0, port, val);
+}
+
+static void ipf_legacy_io_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val) {
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ cpu_outl(0, port, val);
+}
+
+static uint32_t ipf_legacy_io_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ return cpu_inb(0, port);
+}
+
+static uint32_t ipf_legacy_io_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ return cpu_inw(0, port);
+}
+
+static uint32_t ipf_legacy_io_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t port = ipf_to_legacy_io(addr);
+
+ return cpu_inl(0, port);
+}
+
+static CPUReadMemoryFunc *ipf_legacy_io_read[3] = {
+ ipf_legacy_io_readb,
+ ipf_legacy_io_readw,
+ ipf_legacy_io_readl,
+};
+
+static CPUWriteMemoryFunc *ipf_legacy_io_write[3] = {
+ ipf_legacy_io_writeb,
+ ipf_legacy_io_writew,
+ ipf_legacy_io_writel,
+};
+
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+ fprintf(stderr,"pic_irq_request called!\n");
+}
+
+/* PC cmos mappings */
+
+#define REG_EQUIPMENT_BYTE 0x14
+
+static int cmos_get_fd_drive_type(int fd0)
+{
+ int val;
+
+ switch (fd0) {
+ case 0:
+ /* 1.44 Mb 3"5 drive */
+ val = 4;
+ break;
+ case 1:
+ /* 2.88 Mb 3"5 drive */
+ val = 5;
+ break;
+ case 2:
+ /* 1.2 Mb 5"5 drive */
+ val = 2;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
+{
+ RTCState *s = rtc_state;
+ int cylinders, heads, sectors;
+
+ bdrv_get_geometry_hint(hd, &cylinders, &heads, &sectors);
+ rtc_set_memory(s, type_ofs, 47);
+ rtc_set_memory(s, info_ofs, cylinders);
+ rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
+ rtc_set_memory(s, info_ofs + 2, heads);
+ rtc_set_memory(s, info_ofs + 3, 0xff);
+ rtc_set_memory(s, info_ofs + 4, 0xff);
+ rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
+ rtc_set_memory(s, info_ofs + 6, cylinders);
+ rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
+ rtc_set_memory(s, info_ofs + 8, sectors);
+}
+
+/* convert boot_device letter to something recognizable by the bios */
+static int boot_device2nibble(char boot_device)
+{
+ switch(boot_device) {
+ case 'a':
+ case 'b':
+ return 0x01; /* floppy boot */
+ case 'c':
+ return 0x02; /* hard drive boot */
+ case 'd':
+ return 0x03; /* CD-ROM boot */
+ case 'n':
+ return 0x04; /* Network boot */
+ }
+ return 0;
+}
+
+/* hd_table must contain 4 block drivers */
+static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device, BlockDriverState **hd_table)
+{
+ RTCState *s = rtc_state;
+ int nbds, bds[3] = { 0, };
+ int val;
+ int fd0, fd1, nb;
+ int i;
+
+ /* various important CMOS locations needed by PC/Bochs bios */
+
+ /* memory size */
+ val = 640; /* base memory in K */
+ rtc_set_memory(s, 0x15, val);
+ rtc_set_memory(s, 0x16, val >> 8);
+
+ val = (ram_size / 1024) - 1024;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x17, val);
+ rtc_set_memory(s, 0x18, val >> 8);
+ rtc_set_memory(s, 0x30, val);
+ rtc_set_memory(s, 0x31, val >> 8);
+
+ if (above_4g_mem_size) {
+ rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16);
+ rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
+ rtc_set_memory(s, 0x5d, above_4g_mem_size >> 32);
+ }
+ rtc_set_memory(s, 0x5f, smp_cpus - 1);
+
+ if (ram_size > (16 * 1024 * 1024))
+ val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
+ else
+ val = 0;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x34, val);
+ rtc_set_memory(s, 0x35, val >> 8);
+
+ /* set boot devices, and disable floppy signature check if requested */
+#define PC_MAX_BOOT_DEVICES 3
+ nbds = strlen(boot_device);
+
+ if (nbds > PC_MAX_BOOT_DEVICES) {
+ fprintf(stderr, "Too many boot devices for PC\n");
+ exit(1);
+ }
+
+ for (i = 0; i < nbds; i++) {
+ bds[i] = boot_device2nibble(boot_device[i]);
+ if (bds[i] == 0) {
+ fprintf(stderr, "Invalid boot device for PC: '%c'\n",
+ boot_device[i]);
+ exit(1);
+ }
+ }
+
+ rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+ rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1));
+
+ /* floppy type */
+
+ fd0 = fdctrl_get_drive_type(floppy_controller, 0);
+ fd1 = fdctrl_get_drive_type(floppy_controller, 1);
+
+ val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
+ rtc_set_memory(s, 0x10, val);
+
+ val = 0;
+ nb = 0;
+ if (fd0 < 3)
+ nb++;
+ if (fd1 < 3)
+ nb++;
+
+ switch (nb) {
+ case 0:
+ break;
+ case 1:
+ val |= 0x01; /* 1 drive, ready for boot */
+ break;
+ case 2:
+ val |= 0x41; /* 2 drives, ready for boot */
+ break;
+ }
+
+ val |= 0x02; /* FPU is there */
+ val |= 0x04; /* PS/2 mouse installed */
+ rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
+
+ /* hard drives */
+
+ rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
+ if (hd_table[0])
+ cmos_init_hd(0x19, 0x1b, hd_table[0]);
+ if (hd_table[1])
+ cmos_init_hd(0x1a, 0x24, hd_table[1]);
+
+ val = 0;
+ for (i = 0; i < 4; i++) {
+ if (hd_table[i]) {
+ int cylinders, heads, sectors, translation;
+ /* NOTE: bdrv_get_geometry_hint() returns the physical
+ geometry. It is always such that: 1 <= sects <= 63, 1
+ <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+ geometry can be different if a translation is done. */
+ translation = bdrv_get_translation_hint(hd_table[i]);
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_get_geometry_hint(hd_table[i], &cylinders,
+ &heads, &sectors);
+ if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
+ /* No translation. */
+ translation = 0;
+ } else {
+ /* LBA translation. */
+ translation = 1;
+ }
+ } else {
+ translation--;
+ }
+ val |= translation << (i * 2);
+ }
+ }
+ rtc_set_memory(s, 0x39, val);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+#define NE2000_NB_MAX 6
+
+static int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340,
+ 0x360, 0x280, 0x380 };
+static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+static int serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static int serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+
+#ifdef HAS_AUDIO
+static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
+{
+ struct soundhw *c;
+ int audio_enabled = 0;
+
+ for (c = soundhw; !audio_enabled && c->name; ++c) {
+ audio_enabled = c->enabled;
+ }
+
+ if (audio_enabled) {
+ AudioState *s;
+
+ s = AUD_init ();
+ if (s) {
+ for (c = soundhw; c->name; ++c) {
+ if (c->enabled) {
+ if (c->isa) {
+ c->init.init_isa (s, pic);
+ } else {
+ if (pci_bus) {
+ c->init.init_pci (pci_bus, s);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+static void pc_init_ne2k_isa(NICInfo *nd, qemu_irq *pic)
+{
+ static int nb_ne2k = 0;
+
+ if (nb_ne2k == NE2000_NB_MAX)
+ return;
+ isa_ne2000_init(ne2000_io[nb_ne2k], pic[ne2000_irq[nb_ne2k]], nd);
+ nb_ne2k++;
+}
+
+/* Itanium hardware initialisation */
+static void ipf_init1(ram_addr_t ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename,
+ int pci_enabled, const char *cpu_model)
+{
+ char buf[1024];
+ int i;
+ ram_addr_t ram_addr;
+ ram_addr_t above_4g_mem_size = 0;
+ PCIBus *pci_bus;
+ PCIDevice *pci_dev;
+ int piix3_devfn = -1;
+ CPUState *env;
+ qemu_irq *cpu_irq;
+ qemu_irq *i8259;
+ int page_size;
+ int index;
+ unsigned long ipf_legacy_io_base, ipf_legacy_io_mem;
+ BlockDriverState *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ BlockDriverState *fd[MAX_FD];
+
+ page_size = getpagesize();
+ if (page_size != TARGET_PAGE_SIZE) {
+ fprintf(stderr,"Error! Host page size != qemu target page size,"
+ " you may need to change TARGET_PAGE_BITS in qemu!"
+ "host page size:0x%x\n", page_size);
+ exit(-1);
+ };
+
+ if (ram_size >= 0xc0000000 ) {
+ above_4g_mem_size = ram_size - 0xc0000000;
+ ram_size = 0xc0000000;
+ }
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "IA64";
+ }
+
+ for(i = 0; i < smp_cpus; i++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ if (i != 0)
+ env->hflags |= HF_HALTED_MASK;
+ register_savevm("cpu", i, 4, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, 0, env);
+ }
+
+ /* allocate RAM */
+ if (kvm_enabled()) {
+ ram_addr = qemu_ram_alloc(0xa0000);
+ cpu_register_physical_memory(0, 0xa0000, ram_addr);
+
+ ram_addr = qemu_ram_alloc(0x20000); // Workaround 0xa0000-0xc0000
+
+ ram_addr = qemu_ram_alloc(0x40000);
+ cpu_register_physical_memory(0xc0000, 0x40000, ram_addr);
+
+ ram_addr = qemu_ram_alloc(ram_size - 0x100000);
+ cpu_register_physical_memory(0x100000, ram_size - 0x100000, ram_addr);
+ } else {
+ ram_addr = qemu_ram_alloc(ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_addr);
+ }
+
+ /* above 4giga memory allocation */
+ if (above_4g_mem_size > 0) {
+ ram_addr = qemu_ram_alloc(above_4g_mem_size);
+ cpu_register_physical_memory(0x100000000, above_4g_mem_size, ram_addr);
+ }
+
+ /*Load firware to its proper position.*/
+ if (kvm_enabled()) {
+ unsigned long image_size;
+ uint8_t *image = NULL;
+ unsigned long nvram_addr;
+ unsigned long nvram_fd = 0;
+ unsigned long type = READ_FROM_NVRAM;
+ unsigned long i = 0;
+ unsigned long fw_offset;
+ ram_addr_t fw_mem = qemu_ram_alloc(GFW_SIZE);
+
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, FW_FILENAME);
+ image = read_image(buf, &image_size );
+ if (NULL == image || !image_size) {
+ fprintf(stderr, "Error when reading Guest Firmware!\n");
+ fprintf(stderr, "Please check Guest firmware at %s\n", buf);
+ exit(1);
+ }
+ fw_offset = GFW_START + GFW_SIZE - image_size;
+
+ cpu_register_physical_memory(GFW_START, GFW_SIZE, fw_mem);
+ cpu_physical_memory_write(fw_offset, image, image_size);
+
+ free(image);
+
+ if (nvram) {
+ nvram_addr = NVRAM_START;
+ nvram_fd = kvm_ia64_nvram_init(type);
+ if (nvram_fd != -1) {
+ kvm_ia64_copy_from_nvram_to_GFW(nvram_fd);
+ close(nvram_fd);
+ }
+ i = atexit((void *)kvm_ia64_copy_from_GFW_to_nvram);
+ if (i != 0)
+ fprintf(stderr, "cannot set exit function\n");
+ } else
+ nvram_addr = 0;
+
+ kvm_ia64_build_hob(ram_size + above_4g_mem_size, smp_cpus, nvram_addr);
+ }
+
+ /*Register legacy io address space, size:64M*/
+ ipf_legacy_io_base = 0xE0000000;
+ ipf_legacy_io_mem = cpu_register_io_memory(0, ipf_legacy_io_read,
+ ipf_legacy_io_write, NULL);
+ cpu_register_physical_memory(ipf_legacy_io_base, 64*1024*1024,
+ ipf_legacy_io_mem);
+
+ cpu_irq = qemu_allocate_irqs(pic_irq_request, first_cpu, 1);
+ i8259 = kvm_i8259_init(cpu_irq[0]);
+
+ if (pci_enabled) {
+ pci_bus = i440fx_init(&i440fx_state, i8259);
+ piix3_devfn = piix3_init(pci_bus, -1);
+ } else {
+ pci_bus = NULL;
+ }
+
+ if (cirrus_vga_enabled) {
+ if (pci_enabled)
+ pci_cirrus_vga_init(pci_bus);
+ else
+ isa_cirrus_vga_init();
+ } else {
+ if (pci_enabled)
+ pci_vga_init(pci_bus, 0, 0);
+ else
+ isa_vga_init();
+ }
+
+ rtc_state = rtc_init(0x70, i8259[8], 2000);
+
+ if (pci_enabled) {
+ pic_set_alt_irq_func(isa_pic, NULL, NULL);
+ }
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_init(serial_io[i], i8259[serial_irq[i]], 115200,
+ serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(parallel_io[i], i8259[parallel_irq[i]],
+ parallel_hds[i]);
+ }
+ }
+
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
+ pc_init_ne2k_isa(nd, i8259);
+ else
+ pci_nic_init(nd, "e1000", NULL);
+ }
+
+#undef USE_HYPERCALL //Disable it now, need to implement later!
+#ifdef USE_HYPERCALL
+ pci_hypercall_init(pci_bus);
+#endif
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ index = drive_get_index(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ if (index != -1)
+ hd[i] = drives_table[index].bdrv;
+ else
+ hd[i] = NULL;
+ }
+
+ if (pci_enabled) {
+ pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1, i8259);
+ } else {
+ for(i = 0; i < MAX_IDE_BUS; i++) {
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], i8259[ide_irq[i]],
+ hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
+ }
+ }
+
+ i8042_init(i8259[1], i8259[12], 0x60);
+ DMA_init(0);
+#ifdef HAS_AUDIO
+ audio_init(pci_enabled ? pci_bus : NULL, i8259);
+#endif
+
+ for(i = 0; i < MAX_FD; i++) {
+ index = drive_get_index(IF_FLOPPY, 0, i);
+ if (index != -1)
+ fd[i] = drives_table[index].bdrv;
+ else
+ fd[i] = NULL;
+ }
+ floppy_controller = fdctrl_init(i8259[6], 2, 0, 0x3f0, fd);
+
+ cmos_init(ram_size, above_4g_mem_size, boot_device, hd);
+
+ if (pci_enabled && usb_enabled) {
+ usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+ }
+
+ if (pci_enabled && acpi_enabled) {
+ uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+ i2c_bus *smbus;
+
+ /* TODO: Populate SPD eeprom data. */
+ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, i8259[9]);
+ for (i = 0; i < 8; i++) {
+ DeviceState *eeprom;
+ eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+ qdev_set_prop_int(eeprom, "address", 0x50 + i);
+ qdev_set_prop_ptr(eeprom, "data", eeprom_buf + (i * 256));
+ qdev_init(eeprom);
+ }
+ }
+
+ if (i440fx_state) {
+ i440fx_init_memory_mappings(i440fx_state);
+ }
+
+ if (pci_enabled) {
+ int max_bus;
+ int bus;
+
+ max_bus = drive_get_max_bus(IF_SCSI);
+ for (bus = 0; bus <= max_bus; bus++) {
+ pci_create_simple(pci_bus, -1, "lsi53c895a");
+ }
+ }
+ /* Add virtio block devices */
+ if (pci_enabled) {
+ int index;
+ int unit_id = 0;
+
+ while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) {
+ pci_dev = pci_create("virtio-blk-pci",
+ drives_table[index].devaddr);
+ qdev_init(&pci_dev->qdev);
+ unit_id++;
+ }
+ }
+}
+
+static void ipf_init_pci(ram_addr_t ram_size,
+ const char *boot_device, DisplayState *ds,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ ipf_init1(ram_size, boot_device, ds, kernel_filename,
+ kernel_cmdline, initrd_filename, 1, cpu_model);
+}
+
+QEMUMachine ipf_machine = {
+ .name = "itanium",
+ .desc = "Itanium Platform",
+ .init = (QEMUMachineInitFunc *)ipf_init_pci,
+ .max_cpus = 255,
+ .is_default = 1,
+};
+
+static void ipf_machine_init(void)
+{
+ qemu_register_machine(&ipf_machine);
+}
+
+machine_init(ipf_machine_init);
+
+#define IOAPIC_NUM_PINS 48
+
+static int ioapic_irq_count[IOAPIC_NUM_PINS];
+
+static int ioapic_map_irq(int devfn, int irq_num)
+{
+ int irq, dev;
+ dev = devfn >> 3;
+ irq = ((((dev << 2) + (dev >> 3) + irq_num) & 31) + 16);
+ return irq;
+}
+
+/*
+ * Dummy function to provide match for call from hw/apic.c
+ */
+void apic_set_irq_delivered(void) {
+}
+
+void ioapic_set_irq(void *opaque, int irq_num, int level)
+{
+ int vector, pic_ret;
+
+ PCIDevice *pci_dev = (PCIDevice *)opaque;
+ vector = ioapic_map_irq(pci_dev->devfn, irq_num);
+
+ if (level)
+ ioapic_irq_count[vector] += 1;
+ else
+ ioapic_irq_count[vector] -= 1;
+
+ if (kvm_enabled()) {
+ if (kvm_set_irq(vector, ioapic_irq_count[vector] == 0, &pic_ret))
+ if (pic_ret != 0)
+ apic_set_irq_delivered();
+ return;
+ }
+}
+
+int ipf_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return ioapic_map_irq(pci_dev->devfn, irq_num);
+}
diff --git a/hw/irq.c b/hw/irq.c
new file mode 100644
index 0000000..7703f62
--- /dev/null
+++ b/hw/irq.c
@@ -0,0 +1,77 @@
+/*
+ * QEMU IRQ/GPIO common code.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "irq.h"
+
+struct IRQState {
+ qemu_irq_handler handler;
+ void *opaque;
+ int n;
+};
+
+void qemu_set_irq(qemu_irq irq, int level)
+{
+ if (!irq)
+ return;
+
+ irq->handler(irq->opaque, irq->n, level);
+}
+
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+ qemu_irq *s;
+ struct IRQState *p;
+ int i;
+
+ s = (qemu_irq *)qemu_mallocz(sizeof(qemu_irq) * n);
+ p = (struct IRQState *)qemu_mallocz(sizeof(struct IRQState) * n);
+ for (i = 0; i < n; i++) {
+ p->handler = handler;
+ p->opaque = opaque;
+ p->n = i;
+ s[i] = p;
+ p++;
+ }
+ return s;
+}
+
+void qemu_free_irqs(qemu_irq *s)
+{
+ qemu_free(s[0]);
+ qemu_free(s);
+}
+
+static void qemu_notirq(void *opaque, int line, int level)
+{
+ struct IRQState *irq = opaque;
+
+ irq->handler(irq->opaque, irq->n, !level);
+}
+
+qemu_irq qemu_irq_invert(qemu_irq irq)
+{
+ /* The default state for IRQs is low, so raise the output now. */
+ qemu_irq_raise(irq);
+ return qemu_allocate_irqs(qemu_notirq, irq, 1)[0];
+}
diff --git a/hw/irq.h b/hw/irq.h
new file mode 100644
index 0000000..5daae44
--- /dev/null
+++ b/hw/irq.h
@@ -0,0 +1,35 @@
+#ifndef QEMU_IRQ_H
+#define QEMU_IRQ_H
+
+/* Generic IRQ/GPIO pin infrastructure. */
+
+/* FIXME: Rmove one of these. */
+typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
+typedef void SetIRQFunc(void *opaque, int irq_num, int level);
+
+void qemu_set_irq(qemu_irq irq, int level);
+
+static inline void qemu_irq_raise(qemu_irq irq)
+{
+ qemu_set_irq(irq, 1);
+}
+
+static inline void qemu_irq_lower(qemu_irq irq)
+{
+ qemu_set_irq(irq, 0);
+}
+
+static inline void qemu_irq_pulse(qemu_irq irq)
+{
+ qemu_set_irq(irq, 1);
+ qemu_set_irq(irq, 0);
+}
+
+/* Returns an array of N IRQs. */
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n);
+void qemu_free_irqs(qemu_irq *s);
+
+/* Returns a new IRQ with opposite polarity. */
+qemu_irq qemu_irq_invert(qemu_irq irq);
+
+#endif
diff --git a/hw/isa-bus.c b/hw/isa-bus.c
new file mode 100644
index 0000000..9e33e71
--- /dev/null
+++ b/hw/isa-bus.c
@@ -0,0 +1,194 @@
+/*
+ * isa bus support for qdev.
+ *
+ * Copyright (c) 2009 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hw.h"
+#include "sysemu.h"
+#include "monitor.h"
+#include "sysbus.h"
+#include "isa.h"
+
+struct ISABus {
+ BusState qbus;
+ qemu_irq *irqs;
+};
+static ISABus *isabus;
+target_phys_addr_t isa_mem_base = 0;
+
+static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *isabus_get_fw_dev_path(DeviceState *dev);
+
+static struct BusInfo isa_bus_info = {
+ .name = "ISA",
+ .size = sizeof(ISABus),
+ .print_dev = isabus_dev_print,
+ .get_fw_dev_path = isabus_get_fw_dev_path,
+};
+
+ISABus *isa_bus_new(DeviceState *dev)
+{
+ if (isabus) {
+ fprintf(stderr, "Can't create a second ISA bus\n");
+ return NULL;
+ }
+ if (NULL == dev) {
+ dev = qdev_create(NULL, "isabus-bridge");
+ qdev_init_nofail(dev);
+ }
+
+ isabus = FROM_QBUS(ISABus, qbus_create(&isa_bus_info, dev, NULL));
+ return isabus;
+}
+
+void isa_bus_irqs(qemu_irq *irqs)
+{
+ isabus->irqs = irqs;
+}
+
+/*
+ * isa_get_irq() returns the corresponding qemu_irq entry for the i8259.
+ *
+ * This function is only for special cases such as the 'ferr', and
+ * temporary use for normal devices until they are converted to qdev.
+ */
+qemu_irq isa_get_irq(int isairq)
+{
+ if (isairq < 0 || isairq > 15) {
+ hw_error("isa irq %d invalid", isairq);
+ }
+ return isabus->irqs[isairq];
+}
+
+void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq)
+{
+ assert(dev->nirqs < ARRAY_SIZE(dev->isairq));
+ dev->isairq[dev->nirqs] = isairq;
+ *p = isa_get_irq(isairq);
+ dev->nirqs++;
+}
+
+static void isa_init_ioport_one(ISADevice *dev, uint16_t ioport)
+{
+ assert(dev->nioports < ARRAY_SIZE(dev->ioports));
+ dev->ioports[dev->nioports++] = ioport;
+}
+
+static int isa_cmp_ports(const void *p1, const void *p2)
+{
+ return *(uint16_t*)p1 - *(uint16_t*)p2;
+}
+
+void isa_init_ioport_range(ISADevice *dev, uint16_t start, uint16_t length)
+{
+ int i;
+ for (i = start; i < start + length; i++) {
+ isa_init_ioport_one(dev, i);
+ }
+ qsort(dev->ioports, dev->nioports, sizeof(dev->ioports[0]), isa_cmp_ports);
+}
+
+void isa_init_ioport(ISADevice *dev, uint16_t ioport)
+{
+ isa_init_ioport_range(dev, ioport, 1);
+}
+
+static int isa_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ ISADevice *dev = DO_UPCAST(ISADevice, qdev, qdev);
+ ISADeviceInfo *info = DO_UPCAST(ISADeviceInfo, qdev, base);
+
+ dev->isairq[0] = -1;
+ dev->isairq[1] = -1;
+
+ return info->init(dev);
+}
+
+void isa_qdev_register(ISADeviceInfo *info)
+{
+ info->qdev.init = isa_qdev_init;
+ info->qdev.bus_info = &isa_bus_info;
+ qdev_register(&info->qdev);
+}
+
+ISADevice *isa_create(const char *name)
+{
+ DeviceState *dev;
+
+ if (!isabus) {
+ hw_error("Tried to create isa device %s with no isa bus present.",
+ name);
+ }
+ dev = qdev_create(&isabus->qbus, name);
+ return DO_UPCAST(ISADevice, qdev, dev);
+}
+
+ISADevice *isa_create_simple(const char *name)
+{
+ ISADevice *dev;
+
+ dev = isa_create(name);
+ qdev_init_nofail(&dev->qdev);
+ return dev;
+}
+
+static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ ISADevice *d = DO_UPCAST(ISADevice, qdev, dev);
+
+ if (d->isairq[1] != -1) {
+ monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "",
+ d->isairq[0], d->isairq[1]);
+ } else if (d->isairq[0] != -1) {
+ monitor_printf(mon, "%*sisa irq %d\n", indent, "",
+ d->isairq[0]);
+ }
+}
+
+static int isabus_bridge_init(SysBusDevice *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+static SysBusDeviceInfo isabus_bridge_info = {
+ .init = isabus_bridge_init,
+ .qdev.name = "isabus-bridge",
+ .qdev.fw_name = "isa",
+ .qdev.size = sizeof(SysBusDevice),
+ .qdev.no_user = 1,
+};
+
+static void isabus_register_devices(void)
+{
+ sysbus_register_withprop(&isabus_bridge_info);
+}
+
+static char *isabus_get_fw_dev_path(DeviceState *dev)
+{
+ ISADevice *d = (ISADevice*)dev;
+ char path[40];
+ int off;
+
+ off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
+ if (d->nioports) {
+ snprintf(path + off, sizeof(path) - off, "@%04x", d->ioports[0]);
+ }
+
+ return strdup(path);
+}
+
+device_init(isabus_register_devices)
diff --git a/hw/isa.h b/hw/isa.h
new file mode 100644
index 0000000..f1335ff
--- /dev/null
+++ b/hw/isa.h
@@ -0,0 +1,52 @@
+#ifndef HW_ISA_H
+#define HW_ISA_H
+
+/* ISA bus */
+
+#include "ioport.h"
+#include "qdev.h"
+
+typedef struct ISABus ISABus;
+typedef struct ISADevice ISADevice;
+typedef struct ISADeviceInfo ISADeviceInfo;
+
+struct ISADevice {
+ DeviceState qdev;
+ uint32_t isairq[2];
+ int nirqs;
+ uint16_t ioports[32];
+ int nioports;
+};
+
+typedef int (*isa_qdev_initfn)(ISADevice *dev);
+struct ISADeviceInfo {
+ DeviceInfo qdev;
+ isa_qdev_initfn init;
+};
+
+ISABus *isa_bus_new(DeviceState *dev);
+void isa_bus_irqs(qemu_irq *irqs);
+qemu_irq isa_get_irq(int isairq);
+void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq);
+void isa_init_ioport(ISADevice *dev, uint16_t ioport);
+void isa_init_ioport_range(ISADevice *dev, uint16_t start, uint16_t length);
+void isa_qdev_register(ISADeviceInfo *info);
+ISADevice *isa_create(const char *name);
+ISADevice *isa_create_simple(const char *name);
+
+extern target_phys_addr_t isa_mem_base;
+
+void isa_mmio_init(target_phys_addr_t base, target_phys_addr_t size);
+
+/* dma.c */
+int DMA_get_channel_mode (int nchan);
+int DMA_read_memory (int nchan, void *buf, int pos, int size);
+int DMA_write_memory (int nchan, void *buf, int pos, int size);
+void DMA_hold_DREQ (int nchan);
+void DMA_release_DREQ (int nchan);
+void DMA_schedule(int nchan);
+void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit);
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque);
+#endif
diff --git a/hw/isa_mmio.c b/hw/isa_mmio.c
new file mode 100644
index 0000000..ca957fb
--- /dev/null
+++ b/hw/isa_mmio.c
@@ -0,0 +1,82 @@
+/*
+ * Memory mapped access to ISA IO space.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "isa.h"
+
+static void isa_mmio_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outb(addr & IOPORTS_MASK, val);
+}
+
+static void isa_mmio_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outw(addr & IOPORTS_MASK, val);
+}
+
+static void isa_mmio_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ cpu_outl(addr & IOPORTS_MASK, val);
+}
+
+static uint32_t isa_mmio_readb (void *opaque, target_phys_addr_t addr)
+{
+ return cpu_inb(addr & IOPORTS_MASK);
+}
+
+static uint32_t isa_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ return cpu_inw(addr & IOPORTS_MASK);
+}
+
+static uint32_t isa_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ return cpu_inl(addr & IOPORTS_MASK);
+}
+
+static CPUWriteMemoryFunc * const isa_mmio_write[] = {
+ &isa_mmio_writeb,
+ &isa_mmio_writew,
+ &isa_mmio_writel,
+};
+
+static CPUReadMemoryFunc * const isa_mmio_read[] = {
+ &isa_mmio_readb,
+ &isa_mmio_readw,
+ &isa_mmio_readl,
+};
+
+void isa_mmio_init(target_phys_addr_t base, target_phys_addr_t size)
+{
+ int isa_mmio_iomemtype;
+
+ isa_mmio_iomemtype = cpu_register_io_memory(isa_mmio_read,
+ isa_mmio_write,
+ NULL,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(base, size, isa_mmio_iomemtype);
+}
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
new file mode 100644
index 0000000..7b19a81
--- /dev/null
+++ b/hw/ivshmem.c
@@ -0,0 +1,829 @@
+/*
+ * Inter-VM Shared Memory PCI device.
+ *
+ * Author:
+ * Cam Macdonell <cam@cs.ualberta.ca>
+ *
+ * Based On: cirrus_vga.c
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2004 Makoto Suzuki (suzu)
+ *
+ * and rtl8139.c
+ * Copyright (c) 2006 Igor Kovalenko
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "msix.h"
+#include "kvm.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#define IVSHMEM_IOEVENTFD 0
+#define IVSHMEM_MSI 1
+
+#define IVSHMEM_PEER 0
+#define IVSHMEM_MASTER 1
+
+#define IVSHMEM_REG_BAR_SIZE 0x100
+
+//#define DEBUG_IVSHMEM
+#ifdef DEBUG_IVSHMEM
+#define IVSHMEM_DPRINTF(fmt, ...) \
+ do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define IVSHMEM_DPRINTF(fmt, ...)
+#endif
+
+typedef struct Peer {
+ int nb_eventfds;
+ int *eventfds;
+} Peer;
+
+typedef struct EventfdEntry {
+ PCIDevice *pdev;
+ int vector;
+} EventfdEntry;
+
+typedef struct IVShmemState {
+ PCIDevice dev;
+ uint32_t intrmask;
+ uint32_t intrstatus;
+ uint32_t doorbell;
+
+ CharDriverState **eventfd_chr;
+ CharDriverState *server_chr;
+ int ivshmem_mmio_io_addr;
+
+ pcibus_t mmio_addr;
+ pcibus_t shm_pci_addr;
+ uint64_t ivshmem_offset;
+ uint64_t ivshmem_size; /* size of shared memory region */
+ int shm_fd; /* shared memory file descriptor */
+
+ Peer *peers;
+ int nb_peers; /* how many guests we have space for */
+ int max_peer; /* maximum numbered peer */
+
+ int vm_id;
+ uint32_t vectors;
+ uint32_t features;
+ EventfdEntry *eventfd_table;
+
+ char * shmobj;
+ char * sizearg;
+ char * role;
+ int role_val; /* scalar to avoid multiple string comparisons */
+} IVShmemState;
+
+/* registers for the Inter-VM shared memory device */
+enum ivshmem_registers {
+ INTRMASK = 0,
+ INTRSTATUS = 4,
+ IVPOSITION = 8,
+ DOORBELL = 12,
+};
+
+static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
+ unsigned int feature) {
+ return (ivs->features & (1 << feature));
+}
+
+static inline bool is_power_of_two(uint64_t x) {
+ return (x & (x - 1)) == 0;
+}
+
+static void ivshmem_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev);
+
+ s->shm_pci_addr = addr;
+
+ if (s->ivshmem_offset > 0) {
+ cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size,
+ s->ivshmem_offset);
+ }
+
+ IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %"
+ PRIu64 ", size = %" FMT_PCIBUS "\n", addr, s->ivshmem_offset, size);
+
+}
+
+/* accessing registers - based on rtl8139 */
+static void ivshmem_update_irq(IVShmemState *s, int val)
+{
+ int isr;
+ isr = (s->intrstatus & s->intrmask) & 0xffffffff;
+
+ /* don't print ISR resets */
+ if (isr) {
+ IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
+ isr ? 1 : 0, s->intrstatus, s->intrmask);
+ }
+
+ qemu_set_irq(s->dev.irq[0], (isr != 0));
+}
+
+static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
+{
+ IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
+
+ s->intrmask = val;
+
+ ivshmem_update_irq(s, val);
+}
+
+static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
+{
+ uint32_t ret = s->intrmask;
+
+ IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
+
+ return ret;
+}
+
+static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
+{
+ IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
+
+ s->intrstatus = val;
+
+ ivshmem_update_irq(s, val);
+ return;
+}
+
+static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
+{
+ uint32_t ret = s->intrstatus;
+
+ /* reading ISR clears all interrupts */
+ s->intrstatus = 0;
+
+ ivshmem_update_irq(s, 0);
+
+ return ret;
+}
+
+static void ivshmem_io_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+
+ IVSHMEM_DPRINTF("We shouldn't be writing words\n");
+}
+
+static void ivshmem_io_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ IVShmemState *s = opaque;
+
+ uint64_t write_one = 1;
+ uint16_t dest = val >> 16;
+ uint16_t vector = val & 0xff;
+
+ addr &= 0xfc;
+
+ IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr);
+ switch (addr)
+ {
+ case INTRMASK:
+ ivshmem_IntrMask_write(s, val);
+ break;
+
+ case INTRSTATUS:
+ ivshmem_IntrStatus_write(s, val);
+ break;
+
+ case DOORBELL:
+ /* check that dest VM ID is reasonable */
+ if (dest > s->max_peer) {
+ IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
+ break;
+ }
+
+ /* check doorbell range */
+ if (vector < s->peers[dest].nb_eventfds) {
+ IVSHMEM_DPRINTF("Writing %" PRId64 " to VM %d on vector %d\n",
+ write_one, dest, vector);
+ if (write(s->peers[dest].eventfds[vector],
+ &(write_one), 8) != 8) {
+ IVSHMEM_DPRINTF("error writing to eventfd\n");
+ }
+ }
+ break;
+ default:
+ IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest);
+ }
+}
+
+static void ivshmem_io_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ IVSHMEM_DPRINTF("We shouldn't be writing bytes\n");
+}
+
+static uint32_t ivshmem_io_readw(void *opaque, target_phys_addr_t addr)
+{
+
+ IVSHMEM_DPRINTF("We shouldn't be reading words\n");
+ return 0;
+}
+
+static uint32_t ivshmem_io_readl(void *opaque, target_phys_addr_t addr)
+{
+
+ IVShmemState *s = opaque;
+ uint32_t ret;
+
+ switch (addr)
+ {
+ case INTRMASK:
+ ret = ivshmem_IntrMask_read(s);
+ break;
+
+ case INTRSTATUS:
+ ret = ivshmem_IntrStatus_read(s);
+ break;
+
+ case IVPOSITION:
+ /* return my VM ID if the memory is mapped */
+ if (s->shm_fd > 0) {
+ ret = s->vm_id;
+ } else {
+ ret = -1;
+ }
+ break;
+
+ default:
+ IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static uint32_t ivshmem_io_readb(void *opaque, target_phys_addr_t addr)
+{
+ IVSHMEM_DPRINTF("We shouldn't be reading bytes\n");
+
+ return 0;
+}
+
+static CPUReadMemoryFunc * const ivshmem_mmio_read[3] = {
+ ivshmem_io_readb,
+ ivshmem_io_readw,
+ ivshmem_io_readl,
+};
+
+static CPUWriteMemoryFunc * const ivshmem_mmio_write[3] = {
+ ivshmem_io_writeb,
+ ivshmem_io_writew,
+ ivshmem_io_writel,
+};
+
+static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
+{
+ IVShmemState *s = opaque;
+
+ ivshmem_IntrStatus_write(s, *buf);
+
+ IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
+}
+
+static int ivshmem_can_receive(void * opaque)
+{
+ return 8;
+}
+
+static void ivshmem_event(void *opaque, int event)
+{
+ IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
+}
+
+static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
+
+ EventfdEntry *entry = opaque;
+ PCIDevice *pdev = entry->pdev;
+
+ IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector);
+ msix_notify(pdev, entry->vector);
+}
+
+static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd,
+ int vector)
+{
+ /* create a event character device based on the passed eventfd */
+ IVShmemState *s = opaque;
+ CharDriverState * chr;
+
+ chr = qemu_chr_open_eventfd(eventfd);
+
+ if (chr == NULL) {
+ fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
+ exit(-1);
+ }
+
+ /* if MSI is supported we need multiple interrupts */
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ s->eventfd_table[vector].pdev = &s->dev;
+ s->eventfd_table[vector].vector = vector;
+
+ qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
+ ivshmem_event, &s->eventfd_table[vector]);
+ } else {
+ qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
+ ivshmem_event, s);
+ }
+
+ return chr;
+
+}
+
+static int check_shm_size(IVShmemState *s, int fd) {
+ /* check that the guest isn't going to try and map more memory than the
+ * the object has allocated return -1 to indicate error */
+
+ struct stat buf;
+
+ fstat(fd, &buf);
+
+ if (s->ivshmem_size > buf.st_size) {
+ fprintf(stderr,
+ "IVSHMEM ERROR: Requested memory size greater"
+ " than shared object size (%" PRIu64 " > %" PRIu64")\n",
+ s->ivshmem_size, (uint64_t)buf.st_size);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/* create the shared memory BAR when we are not using the server, so we can
+ * create the BAR and map the memory immediately */
+static void create_shared_memory_BAR(IVShmemState *s, int fd) {
+
+ void * ptr;
+
+ s->shm_fd = fd;
+
+ ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+
+ s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, "ivshmem.bar2",
+ s->ivshmem_size, ptr);
+
+ /* region for shared memory */
+ pci_register_bar(&s->dev, 2, s->ivshmem_size,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map);
+}
+
+static void close_guest_eventfds(IVShmemState *s, int posn)
+{
+ int i, guest_curr_max;
+
+ guest_curr_max = s->peers[posn].nb_eventfds;
+
+ for (i = 0; i < guest_curr_max; i++) {
+ kvm_set_ioeventfd_mmio_long(s->peers[posn].eventfds[i],
+ s->mmio_addr + DOORBELL, (posn << 16) | i, 0);
+ close(s->peers[posn].eventfds[i]);
+ }
+
+ qemu_free(s->peers[posn].eventfds);
+ s->peers[posn].nb_eventfds = 0;
+}
+
+static void setup_ioeventfds(IVShmemState *s) {
+
+ int i, j;
+
+ for (i = 0; i <= s->max_peer; i++) {
+ for (j = 0; j < s->peers[i].nb_eventfds; j++) {
+ kvm_set_ioeventfd_mmio_long(s->peers[i].eventfds[j],
+ s->mmio_addr + DOORBELL, (i << 16) | j, 1);
+ }
+ }
+}
+
+/* this function increase the dynamic storage need to store data about other
+ * guests */
+static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
+
+ int j, old_nb_alloc;
+
+ old_nb_alloc = s->nb_peers;
+
+ while (new_min_size >= s->nb_peers)
+ s->nb_peers = s->nb_peers * 2;
+
+ IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
+ s->peers = qemu_realloc(s->peers, s->nb_peers * sizeof(Peer));
+
+ /* zero out new pointers */
+ for (j = old_nb_alloc; j < s->nb_peers; j++) {
+ s->peers[j].eventfds = NULL;
+ s->peers[j].nb_eventfds = 0;
+ }
+}
+
+static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
+{
+ IVShmemState *s = opaque;
+ int incoming_fd, tmp_fd;
+ int guest_max_eventfd;
+ long incoming_posn;
+
+ memcpy(&incoming_posn, buf, sizeof(long));
+ /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
+ tmp_fd = qemu_chr_get_msgfd(s->server_chr);
+ IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
+
+ /* make sure we have enough space for this guest */
+ if (incoming_posn >= s->nb_peers) {
+ increase_dynamic_storage(s, incoming_posn);
+ }
+
+ if (tmp_fd == -1) {
+ /* if posn is positive and unseen before then this is our posn*/
+ if ((incoming_posn >= 0) &&
+ (s->peers[incoming_posn].eventfds == NULL)) {
+ /* receive our posn */
+ s->vm_id = incoming_posn;
+ return;
+ } else {
+ /* otherwise an fd == -1 means an existing guest has gone away */
+ IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
+ close_guest_eventfds(s, incoming_posn);
+ return;
+ }
+ }
+
+ /* because of the implementation of get_msgfd, we need a dup */
+ incoming_fd = dup(tmp_fd);
+
+ if (incoming_fd == -1) {
+ fprintf(stderr, "could not allocate file descriptor %s\n",
+ strerror(errno));
+ return;
+ }
+
+ /* if the position is -1, then it's shared memory region fd */
+ if (incoming_posn == -1) {
+
+ void * map_ptr;
+
+ s->max_peer = 0;
+
+ if (check_shm_size(s, incoming_fd) == -1) {
+ exit(-1);
+ }
+
+ /* mmap the region and map into the BAR2 */
+ map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ incoming_fd, 0);
+ s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev,
+ "ivshmem.bar2", s->ivshmem_size, map_ptr);
+
+ IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %"
+ PRIu64 ", size = %" PRIu64 "\n", s->shm_pci_addr,
+ s->ivshmem_offset, s->ivshmem_size);
+
+ if (s->shm_pci_addr > 0) {
+ /* map memory into BAR2 */
+ cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size,
+ s->ivshmem_offset);
+ }
+
+ /* only store the fd if it is successfully mapped */
+ s->shm_fd = incoming_fd;
+
+ return;
+ }
+
+ /* each guest has an array of eventfds, and we keep track of how many
+ * guests for each VM */
+ guest_max_eventfd = s->peers[incoming_posn].nb_eventfds;
+
+ if (guest_max_eventfd == 0) {
+ /* one eventfd per MSI vector */
+ s->peers[incoming_posn].eventfds = (int *) qemu_malloc(s->vectors *
+ sizeof(int));
+ }
+
+ /* this is an eventfd for a particular guest VM */
+ IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
+ guest_max_eventfd, incoming_fd);
+ s->peers[incoming_posn].eventfds[guest_max_eventfd] = incoming_fd;
+
+ /* increment count for particular guest */
+ s->peers[incoming_posn].nb_eventfds++;
+
+ /* keep track of the maximum VM ID */
+ if (incoming_posn > s->max_peer) {
+ s->max_peer = incoming_posn;
+ }
+
+ if (incoming_posn == s->vm_id) {
+ s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
+ s->peers[s->vm_id].eventfds[guest_max_eventfd],
+ guest_max_eventfd);
+ }
+
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ if (kvm_set_ioeventfd_mmio_long(incoming_fd, s->mmio_addr + DOORBELL,
+ (incoming_posn << 16) | guest_max_eventfd, 1) < 0) {
+ fprintf(stderr, "ivshmem: ioeventfd not available\n");
+ }
+ }
+
+ return;
+}
+
+static void ivshmem_reset(DeviceState *d)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev.qdev, d);
+
+ s->intrstatus = 0;
+ return;
+}
+
+static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev);
+
+ s->mmio_addr = addr;
+ cpu_register_physical_memory(addr + 0, IVSHMEM_REG_BAR_SIZE,
+ s->ivshmem_mmio_io_addr);
+
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
+ setup_ioeventfds(s);
+ }
+}
+
+static uint64_t ivshmem_get_size(IVShmemState * s) {
+
+ uint64_t value;
+ char *ptr;
+
+ value = strtoull(s->sizearg, &ptr, 10);
+ switch (*ptr) {
+ case 0: case 'M': case 'm':
+ value <<= 20;
+ break;
+ case 'G': case 'g':
+ value <<= 30;
+ break;
+ default:
+ fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg);
+ exit(1);
+ }
+
+ /* BARs must be a power of 2 */
+ if (!is_power_of_two(value)) {
+ fprintf(stderr, "ivshmem: size must be power of 2\n");
+ exit(1);
+ }
+
+ return value;
+}
+
+static void ivshmem_setup_msi(IVShmemState * s) {
+
+ int i;
+
+ /* allocate the MSI-X vectors */
+
+ if (!msix_init(&s->dev, s->vectors, 1, 0)) {
+ pci_register_bar(&s->dev, 1,
+ msix_bar_size(&s->dev),
+ PCI_BASE_ADDRESS_SPACE_MEMORY,
+ msix_mmio_map);
+ IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
+ } else {
+ IVSHMEM_DPRINTF("msix initialization failed\n");
+ exit(1);
+ }
+
+ /* 'activate' the vectors */
+ for (i = 0; i < s->vectors; i++) {
+ msix_vector_use(&s->dev, i);
+ }
+
+ /* allocate Qemu char devices for receiving interrupts */
+ s->eventfd_table = qemu_mallocz(s->vectors * sizeof(EventfdEntry));
+}
+
+static void ivshmem_save(QEMUFile* f, void *opaque)
+{
+ IVShmemState *proxy = opaque;
+
+ IVSHMEM_DPRINTF("ivshmem_save\n");
+ pci_device_save(&proxy->dev, f);
+
+ if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
+ msix_save(&proxy->dev, f);
+ } else {
+ qemu_put_be32(f, proxy->intrstatus);
+ qemu_put_be32(f, proxy->intrmask);
+ }
+
+}
+
+static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
+{
+ IVSHMEM_DPRINTF("ivshmem_load\n");
+
+ IVShmemState *proxy = opaque;
+ int ret, i;
+
+ if (version_id > 0) {
+ return -EINVAL;
+ }
+
+ if (proxy->role_val == IVSHMEM_PEER) {
+ fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n");
+ return -EINVAL;
+ }
+
+ ret = pci_device_load(&proxy->dev, f);
+ if (ret) {
+ return ret;
+ }
+
+ if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
+ msix_load(&proxy->dev, f);
+ for (i = 0; i < proxy->vectors; i++) {
+ msix_vector_use(&proxy->dev, i);
+ }
+ } else {
+ proxy->intrstatus = qemu_get_be32(f);
+ proxy->intrmask = qemu_get_be32(f);
+ }
+
+ return 0;
+}
+
+static int pci_ivshmem_init(PCIDevice *dev)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev);
+ uint8_t *pci_conf;
+
+ if (s->sizearg == NULL)
+ s->ivshmem_size = 4 << 20; /* 4 MB default */
+ else {
+ s->ivshmem_size = ivshmem_get_size(s);
+ }
+
+ register_savevm(&s->dev.qdev, "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
+ dev);
+
+ /* IRQFD requires MSI */
+ if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
+ !ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n");
+ exit(1);
+ }
+
+ /* check that role is reasonable */
+ if (s->role) {
+ if (strncmp(s->role, "peer", 5) == 0) {
+ s->role_val = IVSHMEM_PEER;
+ } else if (strncmp(s->role, "master", 7) == 0) {
+ s->role_val = IVSHMEM_MASTER;
+ } else {
+ fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n");
+ exit(1);
+ }
+ } else {
+ s->role_val = IVSHMEM_MASTER; /* default */
+ }
+
+ if (s->role_val == IVSHMEM_PEER) {
+ register_device_unmigratable(&s->dev.qdev, "ivshmem", s);
+ }
+
+ pci_conf = s->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT_QUMRANET);
+ pci_conf[0x02] = 0x10;
+ pci_conf[0x03] = 0x11;
+ pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_RAM);
+ pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL;
+
+ pci_config_set_interrupt_pin(pci_conf, 1);
+
+ s->shm_pci_addr = 0;
+ s->ivshmem_offset = 0;
+ s->shm_fd = 0;
+
+ s->ivshmem_mmio_io_addr = cpu_register_io_memory(ivshmem_mmio_read,
+ ivshmem_mmio_write, s, DEVICE_NATIVE_ENDIAN);
+ /* region for registers*/
+ pci_register_bar(&s->dev, 0, IVSHMEM_REG_BAR_SIZE,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_mmio_map);
+
+ if ((s->server_chr != NULL) &&
+ (strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
+ /* if we get a UNIX socket as the parameter we will talk
+ * to the ivshmem server to receive the memory region */
+
+ if (s->shmobj != NULL) {
+ fprintf(stderr, "WARNING: do not specify both 'chardev' "
+ "and 'shm' with ivshmem\n");
+ }
+
+ IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
+ s->server_chr->filename);
+
+ if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
+ ivshmem_setup_msi(s);
+ }
+
+ /* we allocate enough space for 16 guests and grow as needed */
+ s->nb_peers = 16;
+ s->vm_id = -1;
+
+ /* allocate/initialize space for interrupt handling */
+ s->peers = qemu_mallocz(s->nb_peers * sizeof(Peer));
+
+ pci_register_bar(&s->dev, 2, s->ivshmem_size,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map);
+
+ s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *));
+
+ qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
+ ivshmem_event, s);
+ } else {
+ /* just map the file immediately, we're not using a server */
+ int fd;
+
+ if (s->shmobj == NULL) {
+ fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n");
+ }
+
+ IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
+
+ /* try opening with O_EXCL and if it succeeds zero the memory
+ * by truncating to 0 */
+ if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
+ S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
+ /* truncate file to length PCI device's memory */
+ if (ftruncate(fd, s->ivshmem_size) != 0) {
+ fprintf(stderr, "ivshmem: could not truncate shared file\n");
+ }
+
+ } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
+ S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
+ fprintf(stderr, "ivshmem: could not open shared file\n");
+ exit(-1);
+
+ }
+
+ if (check_shm_size(s, fd) == -1) {
+ exit(-1);
+ }
+
+ create_shared_memory_BAR(s, fd);
+
+ }
+
+ return 0;
+}
+
+static int pci_ivshmem_uninit(PCIDevice *dev)
+{
+ IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev);
+
+ cpu_unregister_io_memory(s->ivshmem_mmio_io_addr);
+ unregister_savevm(&dev->qdev, "ivshmem", s);
+
+ return 0;
+}
+
+static PCIDeviceInfo ivshmem_info = {
+ .qdev.name = "ivshmem",
+ .qdev.size = sizeof(IVShmemState),
+ .qdev.reset = ivshmem_reset,
+ .init = pci_ivshmem_init,
+ .exit = pci_ivshmem_uninit,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
+ DEFINE_PROP_STRING("size", IVShmemState, sizearg),
+ DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
+ DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
+ DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
+ DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
+ DEFINE_PROP_STRING("role", IVShmemState, role),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ivshmem_register_devices(void)
+{
+ pci_qdev_register(&ivshmem_info);
+}
+
+device_init(ivshmem_register_devices)
diff --git a/hw/jazz_led.c b/hw/jazz_led.c
new file mode 100644
index 0000000..1dc22cf
--- /dev/null
+++ b/hw/jazz_led.c
@@ -0,0 +1,328 @@
+/*
+ * QEMU JAZZ LED emulator.
+ *
+ * Copyright (c) 2007 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips.h"
+#include "console.h"
+#include "pixel_ops.h"
+
+//#define DEBUG_LED
+
+#ifdef DEBUG_LED
+#define DPRINTF(fmt, ...) \
+do { printf("jazz led: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "jazz led ERROR: " fmt , ## __VA_ARGS__);} while (0)
+
+typedef enum {
+ REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
+} screen_state_t;
+
+typedef struct LedState {
+ uint8_t segments;
+ DisplayState *ds;
+ screen_state_t state;
+} LedState;
+
+static uint32_t led_readb(void *opaque, target_phys_addr_t addr)
+{
+ LedState *s = opaque;
+ uint32_t val;
+
+ switch (addr) {
+ case 0:
+ val = s->segments;
+ break;
+ default:
+ BADF("invalid read at [" TARGET_FMT_plx "]\n", addr);
+ val = 0;
+ }
+
+ DPRINTF("read addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+
+ return val;
+}
+
+static uint32_t led_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = led_readb(opaque, addr) << 8;
+ v |= led_readb(opaque, addr + 1);
+#else
+ v = led_readb(opaque, addr);
+ v |= led_readb(opaque, addr + 1) << 8;
+#endif
+ return v;
+}
+
+static uint32_t led_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+#ifdef TARGET_WORDS_BIGENDIAN
+ v = led_readb(opaque, addr) << 24;
+ v |= led_readb(opaque, addr + 1) << 16;
+ v |= led_readb(opaque, addr + 2) << 8;
+ v |= led_readb(opaque, addr + 3);
+#else
+ v = led_readb(opaque, addr);
+ v |= led_readb(opaque, addr + 1) << 8;
+ v |= led_readb(opaque, addr + 2) << 16;
+ v |= led_readb(opaque, addr + 3) << 24;
+#endif
+ return v;
+}
+
+static void led_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LedState *s = opaque;
+
+ DPRINTF("write addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val);
+
+ switch (addr) {
+ case 0:
+ s->segments = val;
+ s->state |= REDRAW_SEGMENTS;
+ break;
+ default:
+ BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr);
+ break;
+ }
+}
+
+static void led_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ led_writeb(opaque, addr, (val >> 8) & 0xff);
+ led_writeb(opaque, addr + 1, val & 0xff);
+#else
+ led_writeb(opaque, addr, val & 0xff);
+ led_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
+}
+
+static void led_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ led_writeb(opaque, addr, (val >> 24) & 0xff);
+ led_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+ led_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+ led_writeb(opaque, addr + 3, val & 0xff);
+#else
+ led_writeb(opaque, addr, val & 0xff);
+ led_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ led_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ led_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
+}
+
+static CPUReadMemoryFunc * const led_read[3] = {
+ led_readb,
+ led_readw,
+ led_readl,
+};
+
+static CPUWriteMemoryFunc * const led_write[3] = {
+ led_writeb,
+ led_writew,
+ led_writel,
+};
+
+/***********************************************************/
+/* jazz_led display */
+
+static void draw_horizontal_line(DisplayState *ds, int posy, int posx1, int posx2, uint32_t color)
+{
+ uint8_t *d;
+ int x, bpp;
+
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ d = ds_get_data(ds) + ds_get_linesize(ds) * posy + bpp * posx1;
+ switch(bpp) {
+ case 1:
+ for (x = posx1; x <= posx2; x++) {
+ *((uint8_t *)d) = color;
+ d++;
+ }
+ break;
+ case 2:
+ for (x = posx1; x <= posx2; x++) {
+ *((uint16_t *)d) = color;
+ d += 2;
+ }
+ break;
+ case 4:
+ for (x = posx1; x <= posx2; x++) {
+ *((uint32_t *)d) = color;
+ d += 4;
+ }
+ break;
+ }
+}
+
+static void draw_vertical_line(DisplayState *ds, int posx, int posy1, int posy2, uint32_t color)
+{
+ uint8_t *d;
+ int y, bpp;
+
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ d = ds_get_data(ds) + ds_get_linesize(ds) * posy1 + bpp * posx;
+ switch(bpp) {
+ case 1:
+ for (y = posy1; y <= posy2; y++) {
+ *((uint8_t *)d) = color;
+ d += ds_get_linesize(ds);
+ }
+ break;
+ case 2:
+ for (y = posy1; y <= posy2; y++) {
+ *((uint16_t *)d) = color;
+ d += ds_get_linesize(ds);
+ }
+ break;
+ case 4:
+ for (y = posy1; y <= posy2; y++) {
+ *((uint32_t *)d) = color;
+ d += ds_get_linesize(ds);
+ }
+ break;
+ }
+}
+
+static void jazz_led_update_display(void *opaque)
+{
+ LedState *s = opaque;
+ DisplayState *ds = s->ds;
+ uint8_t *d1;
+ uint32_t color_segment, color_led;
+ int y, bpp;
+
+ if (s->state & REDRAW_BACKGROUND) {
+ /* clear screen */
+ bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
+ d1 = ds_get_data(ds);
+ for (y = 0; y < ds_get_height(ds); y++) {
+ memset(d1, 0x00, ds_get_width(ds) * bpp);
+ d1 += ds_get_linesize(ds);
+ }
+ }
+
+ if (s->state & REDRAW_SEGMENTS) {
+ /* set colors according to bpp */
+ switch (ds_get_bits_per_pixel(ds)) {
+ case 8:
+ color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel8(0x00, 0xff, 0x00);
+ break;
+ case 15:
+ color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel15(0x00, 0xff, 0x00);
+ break;
+ case 16:
+ color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
+ case 24:
+ color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
+ break;
+ case 32:
+ color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel32(0x00, 0xff, 0x00);
+ break;
+ default:
+ return;
+ }
+
+ /* display segments */
+ draw_horizontal_line(ds, 40, 10, 40, (s->segments & 0x02) ? color_segment : 0);
+ draw_vertical_line(ds, 10, 10, 40, (s->segments & 0x04) ? color_segment : 0);
+ draw_vertical_line(ds, 10, 40, 70, (s->segments & 0x08) ? color_segment : 0);
+ draw_horizontal_line(ds, 70, 10, 40, (s->segments & 0x10) ? color_segment : 0);
+ draw_vertical_line(ds, 40, 40, 70, (s->segments & 0x20) ? color_segment : 0);
+ draw_vertical_line(ds, 40, 10, 40, (s->segments & 0x40) ? color_segment : 0);
+ draw_horizontal_line(ds, 10, 10, 40, (s->segments & 0x80) ? color_segment : 0);
+
+ /* display led */
+ if (!(s->segments & 0x01))
+ color_led = 0; /* black */
+ draw_horizontal_line(ds, 68, 50, 50, color_led);
+ draw_horizontal_line(ds, 69, 49, 51, color_led);
+ draw_horizontal_line(ds, 70, 48, 52, color_led);
+ draw_horizontal_line(ds, 71, 49, 51, color_led);
+ draw_horizontal_line(ds, 72, 50, 50, color_led);
+ }
+
+ s->state = REDRAW_NONE;
+ dpy_update(ds, 0, 0, ds_get_width(ds), ds_get_height(ds));
+}
+
+static void jazz_led_invalidate_display(void *opaque)
+{
+ LedState *s = opaque;
+ s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+}
+
+static void jazz_led_screen_dump(void *opaque, const char *filename)
+{
+ printf("jazz_led_screen_dump() not implemented\n");
+}
+
+static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
+{
+ LedState *s = opaque;
+ char buf[2];
+
+ dpy_cursor(s->ds, -1, -1);
+ qemu_console_resize(s->ds, 2, 1);
+
+ /* TODO: draw the segments */
+ snprintf(buf, 2, "%02hhx\n", s->segments);
+ console_write_ch(chardata++, 0x00200100 | buf[0]);
+ console_write_ch(chardata++, 0x00200100 | buf[1]);
+
+ dpy_update(s->ds, 0, 0, 2, 1);
+}
+
+void jazz_led_init(target_phys_addr_t base)
+{
+ LedState *s;
+ int io;
+
+ s = qemu_mallocz(sizeof(LedState));
+
+ s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+
+ io = cpu_register_io_memory(led_read, led_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 1, io);
+
+ s->ds = graphic_console_init(jazz_led_update_display,
+ jazz_led_invalidate_display,
+ jazz_led_screen_dump,
+ jazz_led_text_update, s);
+ qemu_console_resize(s->ds, 60, 80);
+}
diff --git a/hw/lan9118.c b/hw/lan9118.c
new file mode 100644
index 0000000..4ce9eb3
--- /dev/null
+++ b/hw/lan9118.c
@@ -0,0 +1,1190 @@
+/*
+ * SMSC LAN9118 Ethernet interface emulation
+ *
+ * Copyright (c) 2009 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2
+ */
+
+#include "sysbus.h"
+#include "net.h"
+#include "devices.h"
+#include "sysemu.h"
+/* For crc32 */
+#include <zlib.h>
+
+//#define DEBUG_LAN9118
+
+#ifdef DEBUG_LAN9118
+#define DPRINTF(fmt, ...) \
+do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define CSR_ID_REV 0x50
+#define CSR_IRQ_CFG 0x54
+#define CSR_INT_STS 0x58
+#define CSR_INT_EN 0x5c
+#define CSR_BYTE_TEST 0x64
+#define CSR_FIFO_INT 0x68
+#define CSR_RX_CFG 0x6c
+#define CSR_TX_CFG 0x70
+#define CSR_HW_CFG 0x74
+#define CSR_RX_DP_CTRL 0x78
+#define CSR_RX_FIFO_INF 0x7c
+#define CSR_TX_FIFO_INF 0x80
+#define CSR_PMT_CTRL 0x84
+#define CSR_GPIO_CFG 0x88
+#define CSR_GPT_CFG 0x8c
+#define CSR_GPT_CNT 0x90
+#define CSR_WORD_SWAP 0x98
+#define CSR_FREE_RUN 0x9c
+#define CSR_RX_DROP 0xa0
+#define CSR_MAC_CSR_CMD 0xa4
+#define CSR_MAC_CSR_DATA 0xa8
+#define CSR_AFC_CFG 0xac
+#define CSR_E2P_CMD 0xb0
+#define CSR_E2P_DATA 0xb4
+
+/* IRQ_CFG */
+#define IRQ_INT 0x00001000
+#define IRQ_EN 0x00000100
+#define IRQ_POL 0x00000010
+#define IRQ_TYPE 0x00000001
+
+/* INT_STS/INT_EN */
+#define SW_INT 0x80000000
+#define TXSTOP_INT 0x02000000
+#define RXSTOP_INT 0x01000000
+#define RXDFH_INT 0x00800000
+#define TX_IOC_INT 0x00200000
+#define RXD_INT 0x00100000
+#define GPT_INT 0x00080000
+#define PHY_INT 0x00040000
+#define PME_INT 0x00020000
+#define TXSO_INT 0x00010000
+#define RWT_INT 0x00008000
+#define RXE_INT 0x00004000
+#define TXE_INT 0x00002000
+#define TDFU_INT 0x00000800
+#define TDFO_INT 0x00000400
+#define TDFA_INT 0x00000200
+#define TSFF_INT 0x00000100
+#define TSFL_INT 0x00000080
+#define RXDF_INT 0x00000040
+#define RDFL_INT 0x00000020
+#define RSFF_INT 0x00000010
+#define RSFL_INT 0x00000008
+#define GPIO2_INT 0x00000004
+#define GPIO1_INT 0x00000002
+#define GPIO0_INT 0x00000001
+#define RESERVED_INT 0x7c001000
+
+#define MAC_CR 1
+#define MAC_ADDRH 2
+#define MAC_ADDRL 3
+#define MAC_HASHH 4
+#define MAC_HASHL 5
+#define MAC_MII_ACC 6
+#define MAC_MII_DATA 7
+#define MAC_FLOW 8
+#define MAC_VLAN1 9 /* TODO */
+#define MAC_VLAN2 10 /* TODO */
+#define MAC_WUFF 11 /* TODO */
+#define MAC_WUCSR 12 /* TODO */
+
+#define MAC_CR_RXALL 0x80000000
+#define MAC_CR_RCVOWN 0x00800000
+#define MAC_CR_LOOPBK 0x00200000
+#define MAC_CR_FDPX 0x00100000
+#define MAC_CR_MCPAS 0x00080000
+#define MAC_CR_PRMS 0x00040000
+#define MAC_CR_INVFILT 0x00020000
+#define MAC_CR_PASSBAD 0x00010000
+#define MAC_CR_HO 0x00008000
+#define MAC_CR_HPFILT 0x00002000
+#define MAC_CR_LCOLL 0x00001000
+#define MAC_CR_BCAST 0x00000800
+#define MAC_CR_DISRTY 0x00000400
+#define MAC_CR_PADSTR 0x00000100
+#define MAC_CR_BOLMT 0x000000c0
+#define MAC_CR_DFCHK 0x00000020
+#define MAC_CR_TXEN 0x00000008
+#define MAC_CR_RXEN 0x00000004
+#define MAC_CR_RESERVED 0x7f404213
+
+#define PHY_INT_ENERGYON 0x80
+#define PHY_INT_AUTONEG_COMPLETE 0x40
+#define PHY_INT_FAULT 0x20
+#define PHY_INT_DOWN 0x10
+#define PHY_INT_AUTONEG_LP 0x08
+#define PHY_INT_PARFAULT 0x04
+#define PHY_INT_AUTONEG_PAGE 0x02
+
+#define GPT_TIMER_EN 0x20000000
+
+enum tx_state {
+ TX_IDLE,
+ TX_B,
+ TX_DATA
+};
+
+typedef struct {
+ enum tx_state state;
+ uint32_t cmd_a;
+ uint32_t cmd_b;
+ int buffer_size;
+ int offset;
+ int pad;
+ int fifo_used;
+ int len;
+ uint8_t data[2048];
+} LAN9118Packet;
+
+typedef struct {
+ SysBusDevice busdev;
+ NICState *nic;
+ NICConf conf;
+ qemu_irq irq;
+ int mmio_index;
+ ptimer_state *timer;
+
+ uint32_t irq_cfg;
+ uint32_t int_sts;
+ uint32_t int_en;
+ uint32_t fifo_int;
+ uint32_t rx_cfg;
+ uint32_t tx_cfg;
+ uint32_t hw_cfg;
+ uint32_t pmt_ctrl;
+ uint32_t gpio_cfg;
+ uint32_t gpt_cfg;
+ uint32_t word_swap;
+ uint32_t free_timer_start;
+ uint32_t mac_cmd;
+ uint32_t mac_data;
+ uint32_t afc_cfg;
+ uint32_t e2p_cmd;
+ uint32_t e2p_data;
+
+ uint32_t mac_cr;
+ uint32_t mac_hashh;
+ uint32_t mac_hashl;
+ uint32_t mac_mii_acc;
+ uint32_t mac_mii_data;
+ uint32_t mac_flow;
+
+ uint32_t phy_status;
+ uint32_t phy_control;
+ uint32_t phy_advertise;
+ uint32_t phy_int;
+ uint32_t phy_int_mask;
+
+ int eeprom_writable;
+ uint8_t eeprom[128];
+
+ int tx_fifo_size;
+ LAN9118Packet *txp;
+ LAN9118Packet tx_packet;
+
+ int tx_status_fifo_used;
+ int tx_status_fifo_head;
+ uint32_t tx_status_fifo[512];
+
+ int rx_status_fifo_size;
+ int rx_status_fifo_used;
+ int rx_status_fifo_head;
+ uint32_t rx_status_fifo[896];
+ int rx_fifo_size;
+ int rx_fifo_used;
+ int rx_fifo_head;
+ uint32_t rx_fifo[3360];
+ int rx_packet_size_head;
+ int rx_packet_size_tail;
+ int rx_packet_size[1024];
+
+ int rxp_offset;
+ int rxp_size;
+ int rxp_pad;
+} lan9118_state;
+
+static void lan9118_update(lan9118_state *s)
+{
+ int level;
+
+ /* TODO: Implement FIFO level IRQs. */
+ level = (s->int_sts & s->int_en) != 0;
+ if (level) {
+ s->irq_cfg |= IRQ_INT;
+ } else {
+ s->irq_cfg &= ~IRQ_INT;
+ }
+ if ((s->irq_cfg & IRQ_EN) == 0) {
+ level = 0;
+ }
+ qemu_set_irq(s->irq, level);
+}
+
+static void lan9118_mac_changed(lan9118_state *s)
+{
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+}
+
+static void lan9118_reload_eeprom(lan9118_state *s)
+{
+ int i;
+ if (s->eeprom[0] != 0xa5) {
+ s->e2p_cmd &= ~0x10;
+ DPRINTF("MACADDR load failed\n");
+ return;
+ }
+ for (i = 0; i < 6; i++) {
+ s->conf.macaddr.a[i] = s->eeprom[i + 1];
+ }
+ s->e2p_cmd |= 0x10;
+ DPRINTF("MACADDR loaded from eeprom\n");
+ lan9118_mac_changed(s);
+}
+
+static void phy_update_irq(lan9118_state *s)
+{
+ if (s->phy_int & s->phy_int_mask) {
+ s->int_sts |= PHY_INT;
+ } else {
+ s->int_sts &= ~PHY_INT;
+ }
+ lan9118_update(s);
+}
+
+static void phy_update_link(lan9118_state *s)
+{
+ /* Autonegotiation status mirrors link status. */
+ if (s->nic->nc.link_down) {
+ s->phy_status &= ~0x0024;
+ s->phy_int |= PHY_INT_DOWN;
+ } else {
+ s->phy_status |= 0x0024;
+ s->phy_int |= PHY_INT_ENERGYON;
+ s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
+ }
+ phy_update_irq(s);
+}
+
+static void lan9118_set_link(VLANClientState *nc)
+{
+ phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque);
+}
+
+static void phy_reset(lan9118_state *s)
+{
+ s->phy_status = 0x7809;
+ s->phy_control = 0x3000;
+ s->phy_advertise = 0x01e1;
+ s->phy_int_mask = 0;
+ s->phy_int = 0;
+ phy_update_link(s);
+}
+
+static void lan9118_reset(DeviceState *d)
+{
+ lan9118_state *s = FROM_SYSBUS(lan9118_state, sysbus_from_qdev(d));
+
+ s->irq_cfg &= ~(IRQ_TYPE | IRQ_POL);
+ s->int_sts = 0;
+ s->int_en = 0;
+ s->fifo_int = 0x48000000;
+ s->rx_cfg = 0;
+ s->tx_cfg = 0;
+ s->hw_cfg = 0x00050000;
+ s->pmt_ctrl &= 0x45;
+ s->gpio_cfg = 0;
+ s->txp->fifo_used = 0;
+ s->txp->state = TX_IDLE;
+ s->txp->cmd_a = 0xffffffffu;
+ s->txp->cmd_b = 0xffffffffu;
+ s->txp->len = 0;
+ s->txp->fifo_used = 0;
+ s->tx_fifo_size = 4608;
+ s->tx_status_fifo_used = 0;
+ s->rx_status_fifo_size = 704;
+ s->rx_fifo_size = 2640;
+ s->rx_fifo_used = 0;
+ s->rx_status_fifo_size = 176;
+ s->rx_status_fifo_used = 0;
+ s->rxp_offset = 0;
+ s->rxp_size = 0;
+ s->rxp_pad = 0;
+ s->rx_packet_size_tail = s->rx_packet_size_head;
+ s->rx_packet_size[s->rx_packet_size_head] = 0;
+ s->mac_cmd = 0;
+ s->mac_data = 0;
+ s->afc_cfg = 0;
+ s->e2p_cmd = 0;
+ s->e2p_data = 0;
+ s->free_timer_start = qemu_get_clock(vm_clock) / 40;
+
+ ptimer_stop(s->timer);
+ ptimer_set_count(s->timer, 0xffff);
+ s->gpt_cfg = 0xffff;
+
+ s->mac_cr = MAC_CR_PRMS;
+ s->mac_hashh = 0;
+ s->mac_hashl = 0;
+ s->mac_mii_acc = 0;
+ s->mac_mii_data = 0;
+ s->mac_flow = 0;
+
+ phy_reset(s);
+
+ s->eeprom_writable = 0;
+ lan9118_reload_eeprom(s);
+}
+
+static int lan9118_can_receive(VLANClientState *nc)
+{
+ return 1;
+}
+
+static void rx_fifo_push(lan9118_state *s, uint32_t val)
+{
+ int fifo_pos;
+ fifo_pos = s->rx_fifo_head + s->rx_fifo_used;
+ if (fifo_pos >= s->rx_fifo_size)
+ fifo_pos -= s->rx_fifo_size;
+ s->rx_fifo[fifo_pos] = val;
+ s->rx_fifo_used++;
+}
+
+/* Return nonzero if the packet is accepted by the filter. */
+static int lan9118_filter(lan9118_state *s, const uint8_t *addr)
+{
+ int multicast;
+ uint32_t hash;
+
+ if (s->mac_cr & MAC_CR_PRMS) {
+ return 1;
+ }
+ if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
+ addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
+ return (s->mac_cr & MAC_CR_BCAST) == 0;
+ }
+
+ multicast = addr[0] & 1;
+ if (multicast &&s->mac_cr & MAC_CR_MCPAS) {
+ return 1;
+ }
+ if (multicast ? (s->mac_cr & MAC_CR_HPFILT) == 0
+ : (s->mac_cr & MAC_CR_HO) == 0) {
+ /* Exact matching. */
+ hash = memcmp(addr, s->conf.macaddr.a, 6);
+ if (s->mac_cr & MAC_CR_INVFILT) {
+ return hash != 0;
+ } else {
+ return hash == 0;
+ }
+ } else {
+ /* Hash matching */
+ hash = (crc32(~0, addr, 6) >> 26);
+ if (hash & 0x20) {
+ return (s->mac_hashh >> (hash & 0x1f)) & 1;
+ } else {
+ return (s->mac_hashl >> (hash & 0x1f)) & 1;
+ }
+ }
+}
+
+static ssize_t lan9118_receive(VLANClientState *nc, const uint8_t *buf,
+ size_t size)
+{
+ lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int fifo_len;
+ int offset;
+ int src_pos;
+ int n;
+ int filter;
+ uint32_t val;
+ uint32_t crc;
+ uint32_t status;
+
+ if ((s->mac_cr & MAC_CR_RXEN) == 0) {
+ return -1;
+ }
+
+ if (size >= 2048 || size < 14) {
+ return -1;
+ }
+
+ /* TODO: Implement FIFO overflow notification. */
+ if (s->rx_status_fifo_used == s->rx_status_fifo_size) {
+ return -1;
+ }
+
+ filter = lan9118_filter(s, buf);
+ if (!filter && (s->mac_cr & MAC_CR_RXALL) == 0) {
+ return size;
+ }
+
+ offset = (s->rx_cfg >> 8) & 0x1f;
+ n = offset & 3;
+ fifo_len = (size + n + 3) >> 2;
+ /* Add a word for the CRC. */
+ fifo_len++;
+ if (s->rx_fifo_size - s->rx_fifo_used < fifo_len) {
+ return -1;
+ }
+
+ DPRINTF("Got packet len:%d fifo:%d filter:%s\n",
+ (int)size, fifo_len, filter ? "pass" : "fail");
+ val = 0;
+ crc = bswap32(crc32(~0, buf, size));
+ for (src_pos = 0; src_pos < size; src_pos++) {
+ val = (val >> 8) | ((uint32_t)buf[src_pos] << 24);
+ n++;
+ if (n == 4) {
+ n = 0;
+ rx_fifo_push(s, val);
+ val = 0;
+ }
+ }
+ if (n) {
+ val >>= ((4 - n) * 8);
+ val |= crc << (n * 8);
+ rx_fifo_push(s, val);
+ val = crc >> ((4 - n) * 8);
+ rx_fifo_push(s, val);
+ } else {
+ rx_fifo_push(s, crc);
+ }
+ n = s->rx_status_fifo_head + s->rx_status_fifo_used;
+ if (n >= s->rx_status_fifo_size) {
+ n -= s->rx_status_fifo_size;
+ }
+ s->rx_packet_size[s->rx_packet_size_tail] = fifo_len;
+ s->rx_packet_size_tail = (s->rx_packet_size_tail + 1023) & 1023;
+ s->rx_status_fifo_used++;
+
+ status = (size + 4) << 16;
+ if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff &&
+ buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) {
+ status |= 0x00002000;
+ } else if (buf[0] & 1) {
+ status |= 0x00000400;
+ }
+ if (!filter) {
+ status |= 0x40000000;
+ }
+ s->rx_status_fifo[n] = status;
+
+ if (s->rx_status_fifo_used > (s->fifo_int & 0xff)) {
+ s->int_sts |= RSFL_INT;
+ }
+ lan9118_update(s);
+
+ return size;
+}
+
+static uint32_t rx_fifo_pop(lan9118_state *s)
+{
+ int n;
+ uint32_t val;
+
+ if (s->rxp_size == 0 && s->rxp_pad == 0) {
+ s->rxp_size = s->rx_packet_size[s->rx_packet_size_head];
+ s->rx_packet_size[s->rx_packet_size_head] = 0;
+ if (s->rxp_size != 0) {
+ s->rx_packet_size_head = (s->rx_packet_size_head + 1023) & 1023;
+ s->rxp_offset = (s->rx_cfg >> 10) & 7;
+ n = s->rxp_offset + s->rxp_size;
+ switch (s->rx_cfg >> 30) {
+ case 1:
+ n = (-n) & 3;
+ break;
+ case 2:
+ n = (-n) & 7;
+ break;
+ default:
+ n = 0;
+ break;
+ }
+ s->rxp_pad = n;
+ DPRINTF("Pop packet size:%d offset:%d pad: %d\n",
+ s->rxp_size, s->rxp_offset, s->rxp_pad);
+ }
+ }
+ if (s->rxp_offset > 0) {
+ s->rxp_offset--;
+ val = 0;
+ } else if (s->rxp_size > 0) {
+ s->rxp_size--;
+ val = s->rx_fifo[s->rx_fifo_head++];
+ if (s->rx_fifo_head >= s->rx_fifo_size) {
+ s->rx_fifo_head -= s->rx_fifo_size;
+ }
+ s->rx_fifo_used--;
+ } else if (s->rxp_pad > 0) {
+ s->rxp_pad--;
+ val = 0;
+ } else {
+ DPRINTF("RX underflow\n");
+ s->int_sts |= RXE_INT;
+ val = 0;
+ }
+ lan9118_update(s);
+ return val;
+}
+
+static void do_tx_packet(lan9118_state *s)
+{
+ int n;
+ uint32_t status;
+
+ /* FIXME: Honor TX disable, and allow queueing of packets. */
+ if (s->phy_control & 0x4000) {
+ /* This assumes the receive routine doesn't touch the VLANClient. */
+ lan9118_receive(&s->nic->nc, s->txp->data, s->txp->len);
+ } else {
+ qemu_send_packet(&s->nic->nc, s->txp->data, s->txp->len);
+ }
+ s->txp->fifo_used = 0;
+
+ if (s->tx_status_fifo_used == 512) {
+ /* Status FIFO full */
+ return;
+ }
+ /* Add entry to status FIFO. */
+ status = s->txp->cmd_b & 0xffff0000u;
+ DPRINTF("Sent packet tag:%04x len %d\n", status >> 16, s->txp->len);
+ n = (s->tx_status_fifo_head + s->tx_status_fifo_used) & 511;
+ s->tx_status_fifo[n] = status;
+ s->tx_status_fifo_used++;
+ if (s->tx_status_fifo_used == 512) {
+ s->int_sts |= TSFF_INT;
+ /* TODO: Stop transmission. */
+ }
+}
+
+static uint32_t rx_status_fifo_pop(lan9118_state *s)
+{
+ uint32_t val;
+
+ val = s->rx_status_fifo[s->rx_status_fifo_head];
+ if (s->rx_status_fifo_used != 0) {
+ s->rx_status_fifo_used--;
+ s->rx_status_fifo_head++;
+ if (s->rx_status_fifo_head >= s->rx_status_fifo_size) {
+ s->rx_status_fifo_head -= s->rx_status_fifo_size;
+ }
+ /* ??? What value should be returned when the FIFO is empty? */
+ DPRINTF("RX status pop 0x%08x\n", val);
+ }
+ return val;
+}
+
+static uint32_t tx_status_fifo_pop(lan9118_state *s)
+{
+ uint32_t val;
+
+ val = s->tx_status_fifo[s->tx_status_fifo_head];
+ if (s->tx_status_fifo_used != 0) {
+ s->tx_status_fifo_used--;
+ s->tx_status_fifo_head = (s->tx_status_fifo_head + 1) & 511;
+ /* ??? What value should be returned when the FIFO is empty? */
+ }
+ return val;
+}
+
+static void tx_fifo_push(lan9118_state *s, uint32_t val)
+{
+ int n;
+
+ if (s->txp->fifo_used == s->tx_fifo_size) {
+ s->int_sts |= TDFO_INT;
+ return;
+ }
+ switch (s->txp->state) {
+ case TX_IDLE:
+ s->txp->cmd_a = val & 0x831f37ff;
+ s->txp->fifo_used++;
+ s->txp->state = TX_B;
+ break;
+ case TX_B:
+ if (s->txp->cmd_a & 0x2000) {
+ /* First segment */
+ s->txp->cmd_b = val;
+ s->txp->fifo_used++;
+ s->txp->buffer_size = s->txp->cmd_a & 0x7ff;
+ s->txp->offset = (s->txp->cmd_a >> 16) & 0x1f;
+ /* End alignment does not include command words. */
+ n = (s->txp->buffer_size + s->txp->offset + 3) >> 2;
+ switch ((n >> 24) & 3) {
+ case 1:
+ n = (-n) & 3;
+ break;
+ case 2:
+ n = (-n) & 7;
+ break;
+ default:
+ n = 0;
+ }
+ s->txp->pad = n;
+ s->txp->len = 0;
+ }
+ DPRINTF("Block len:%d offset:%d pad:%d cmd %08x\n",
+ s->txp->buffer_size, s->txp->offset, s->txp->pad,
+ s->txp->cmd_a);
+ s->txp->state = TX_DATA;
+ break;
+ case TX_DATA:
+ if (s->txp->offset >= 4) {
+ s->txp->offset -= 4;
+ break;
+ }
+ if (s->txp->buffer_size <= 0 && s->txp->pad != 0) {
+ s->txp->pad--;
+ } else {
+ n = 4;
+ while (s->txp->offset) {
+ val >>= 8;
+ n--;
+ s->txp->offset--;
+ }
+ /* Documentation is somewhat unclear on the ordering of bytes
+ in FIFO words. Empirical results show it to be little-endian.
+ */
+ /* TODO: FIFO overflow checking. */
+ while (n--) {
+ s->txp->data[s->txp->len] = val & 0xff;
+ s->txp->len++;
+ val >>= 8;
+ s->txp->buffer_size--;
+ }
+ s->txp->fifo_used++;
+ }
+ if (s->txp->buffer_size <= 0 && s->txp->pad == 0) {
+ if (s->txp->cmd_a & 0x1000) {
+ do_tx_packet(s);
+ }
+ if (s->txp->cmd_a & 0x80000000) {
+ s->int_sts |= TX_IOC_INT;
+ }
+ s->txp->state = TX_IDLE;
+ }
+ break;
+ }
+}
+
+static uint32_t do_phy_read(lan9118_state *s, int reg)
+{
+ uint32_t val;
+
+ switch (reg) {
+ case 0: /* Basic Control */
+ return s->phy_control;
+ case 1: /* Basic Status */
+ return s->phy_status;
+ case 2: /* ID1 */
+ return 0x0007;
+ case 3: /* ID2 */
+ return 0xc0d1;
+ case 4: /* Auto-neg advertisment */
+ return s->phy_advertise;
+ case 5: /* Auto-neg Link Partner Ability */
+ return 0x0f71;
+ case 6: /* Auto-neg Expansion */
+ return 1;
+ /* TODO 17, 18, 27, 29, 30, 31 */
+ case 29: /* Interrupt source. */
+ val = s->phy_int;
+ s->phy_int = 0;
+ phy_update_irq(s);
+ return val;
+ case 30: /* Interrupt mask */
+ return s->phy_int_mask;
+ default:
+ BADF("PHY read reg %d\n", reg);
+ return 0;
+ }
+}
+
+static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
+{
+ switch (reg) {
+ case 0: /* Basic Control */
+ if (val & 0x8000) {
+ phy_reset(s);
+ break;
+ }
+ s->phy_control = val & 0x7980;
+ /* Complete autonegotiation imediately. */
+ if (val & 0x1000) {
+ s->phy_status |= 0x0020;
+ }
+ break;
+ case 4: /* Auto-neg advertisment */
+ s->phy_advertise = (val & 0x2d7f) | 0x80;
+ break;
+ /* TODO 17, 18, 27, 31 */
+ case 30: /* Interrupt mask */
+ s->phy_int_mask = val & 0xff;
+ phy_update_irq(s);
+ break;
+ default:
+ BADF("PHY write reg %d = 0x%04x\n", reg, val);
+ }
+}
+
+static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
+{
+ switch (reg) {
+ case MAC_CR:
+ if ((s->mac_cr & MAC_CR_RXEN) != 0 && (val & MAC_CR_RXEN) == 0) {
+ s->int_sts |= RXSTOP_INT;
+ }
+ s->mac_cr = val & ~MAC_CR_RESERVED;
+ DPRINTF("MAC_CR: %08x\n", val);
+ break;
+ case MAC_ADDRH:
+ s->conf.macaddr.a[4] = val & 0xff;
+ s->conf.macaddr.a[5] = (val >> 8) & 0xff;
+ lan9118_mac_changed(s);
+ break;
+ case MAC_ADDRL:
+ s->conf.macaddr.a[0] = val & 0xff;
+ s->conf.macaddr.a[1] = (val >> 8) & 0xff;
+ s->conf.macaddr.a[2] = (val >> 16) & 0xff;
+ s->conf.macaddr.a[3] = (val >> 24) & 0xff;
+ lan9118_mac_changed(s);
+ break;
+ case MAC_HASHH:
+ s->mac_hashh = val;
+ break;
+ case MAC_HASHL:
+ s->mac_hashl = val;
+ break;
+ case MAC_MII_ACC:
+ s->mac_mii_acc = val & 0xffc2;
+ if (val & 2) {
+ DPRINTF("PHY write %d = 0x%04x\n",
+ (val >> 6) & 0x1f, s->mac_mii_data);
+ do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data);
+ } else {
+ s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f);
+ DPRINTF("PHY read %d = 0x%04x\n",
+ (val >> 6) & 0x1f, s->mac_mii_data);
+ }
+ break;
+ case MAC_MII_DATA:
+ s->mac_mii_data = val & 0xffff;
+ break;
+ case MAC_FLOW:
+ s->mac_flow = val & 0xffff0000;
+ break;
+ case MAC_VLAN1:
+ /* Writing to this register changes a condition for
+ * FrameTooLong bit in rx_status. Since we do not set
+ * FrameTooLong anyway, just ignore write to this.
+ */
+ break;
+ default:
+ hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n",
+ s->mac_cmd & 0xf, val);
+ }
+}
+
+static uint32_t do_mac_read(lan9118_state *s, int reg)
+{
+ switch (reg) {
+ case MAC_CR:
+ return s->mac_cr;
+ case MAC_ADDRH:
+ return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
+ case MAC_ADDRL:
+ return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
+ | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
+ case MAC_HASHH:
+ return s->mac_hashh;
+ break;
+ case MAC_HASHL:
+ return s->mac_hashl;
+ break;
+ case MAC_MII_ACC:
+ return s->mac_mii_acc;
+ case MAC_MII_DATA:
+ return s->mac_mii_data;
+ case MAC_FLOW:
+ return s->mac_flow;
+ default:
+ hw_error("lan9118: Unimplemented MAC register read: %d\n",
+ s->mac_cmd & 0xf);
+ }
+}
+
+static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
+{
+ s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr;
+ switch (cmd) {
+ case 0:
+ s->e2p_data = s->eeprom[addr];
+ DPRINTF("EEPROM Read %d = 0x%02x\n", addr, s->e2p_data);
+ break;
+ case 1:
+ s->eeprom_writable = 0;
+ DPRINTF("EEPROM Write Disable\n");
+ break;
+ case 2: /* EWEN */
+ s->eeprom_writable = 1;
+ DPRINTF("EEPROM Write Enable\n");
+ break;
+ case 3: /* WRITE */
+ if (s->eeprom_writable) {
+ s->eeprom[addr] &= s->e2p_data;
+ DPRINTF("EEPROM Write %d = 0x%02x\n", addr, s->e2p_data);
+ } else {
+ DPRINTF("EEPROM Write %d (ignored)\n", addr);
+ }
+ break;
+ case 4: /* WRAL */
+ if (s->eeprom_writable) {
+ for (addr = 0; addr < 128; addr++) {
+ s->eeprom[addr] &= s->e2p_data;
+ }
+ DPRINTF("EEPROM Write All 0x%02x\n", s->e2p_data);
+ } else {
+ DPRINTF("EEPROM Write All (ignored)\n");
+ }
+ case 5: /* ERASE */
+ if (s->eeprom_writable) {
+ s->eeprom[addr] = 0xff;
+ DPRINTF("EEPROM Erase %d\n", addr);
+ } else {
+ DPRINTF("EEPROM Erase %d (ignored)\n", addr);
+ }
+ break;
+ case 6: /* ERAL */
+ if (s->eeprom_writable) {
+ memset(s->eeprom, 0xff, 128);
+ DPRINTF("EEPROM Erase All\n");
+ } else {
+ DPRINTF("EEPROM Erase All (ignored)\n");
+ }
+ break;
+ case 7: /* RELOAD */
+ lan9118_reload_eeprom(s);
+ break;
+ }
+}
+
+static void lan9118_tick(void *opaque)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+ if (s->int_en & GPT_INT) {
+ s->int_sts |= GPT_INT;
+ }
+ lan9118_update(s);
+}
+
+static void lan9118_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+ offset &= 0xff;
+
+ //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
+ if (offset >= 0x20 && offset < 0x40) {
+ /* TX FIFO */
+ tx_fifo_push(s, val);
+ return;
+ }
+ switch (offset) {
+ case CSR_IRQ_CFG:
+ /* TODO: Implement interrupt deassertion intervals. */
+ s->irq_cfg = (s->irq_cfg & IRQ_INT) | (val & IRQ_EN);
+ break;
+ case CSR_INT_STS:
+ s->int_sts &= ~val;
+ break;
+ case CSR_INT_EN:
+ s->int_en = val & ~RESERVED_INT;
+ s->int_sts |= val & SW_INT;
+ break;
+ case CSR_FIFO_INT:
+ DPRINTF("FIFO INT levels %08x\n", val);
+ s->fifo_int = val;
+ break;
+ case CSR_RX_CFG:
+ if (val & 0x8000) {
+ /* RX_DUMP */
+ s->rx_fifo_used = 0;
+ s->rx_status_fifo_used = 0;
+ s->rx_packet_size_tail = s->rx_packet_size_head;
+ s->rx_packet_size[s->rx_packet_size_head] = 0;
+ }
+ s->rx_cfg = val & 0xcfff1ff0;
+ break;
+ case CSR_TX_CFG:
+ if (val & 0x8000) {
+ s->tx_status_fifo_used = 0;
+ }
+ if (val & 0x4000) {
+ s->txp->state = TX_IDLE;
+ s->txp->fifo_used = 0;
+ s->txp->cmd_a = 0xffffffff;
+ }
+ s->tx_cfg = val & 6;
+ break;
+ case CSR_HW_CFG:
+ if (val & 1) {
+ /* SRST */
+ lan9118_reset(&s->busdev.qdev);
+ } else {
+ s->hw_cfg = val & 0x003f300;
+ }
+ break;
+ case CSR_RX_DP_CTRL:
+ if (val & 0x80000000) {
+ /* Skip forward to next packet. */
+ s->rxp_pad = 0;
+ s->rxp_offset = 0;
+ if (s->rxp_size == 0) {
+ /* Pop a word to start the next packet. */
+ rx_fifo_pop(s);
+ s->rxp_pad = 0;
+ s->rxp_offset = 0;
+ }
+ s->rx_fifo_head += s->rxp_size;
+ if (s->rx_fifo_head >= s->rx_fifo_size) {
+ s->rx_fifo_head -= s->rx_fifo_size;
+ }
+ }
+ break;
+ case CSR_PMT_CTRL:
+ if (val & 0x400) {
+ phy_reset(s);
+ }
+ s->pmt_ctrl &= ~0x34e;
+ s->pmt_ctrl |= (val & 0x34e);
+ break;
+ case CSR_GPIO_CFG:
+ /* Probably just enabling LEDs. */
+ s->gpio_cfg = val & 0x7777071f;
+ break;
+ case CSR_GPT_CFG:
+ if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
+ if (val & GPT_TIMER_EN) {
+ ptimer_set_count(s->timer, val & 0xffff);
+ ptimer_run(s->timer, 0);
+ } else {
+ ptimer_stop(s->timer);
+ ptimer_set_count(s->timer, 0xffff);
+ }
+ }
+ s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
+ break;
+ case CSR_WORD_SWAP:
+ /* Ignored because we're in 32-bit mode. */
+ s->word_swap = val;
+ break;
+ case CSR_MAC_CSR_CMD:
+ s->mac_cmd = val & 0x4000000f;
+ if (val & 0x80000000) {
+ if (val & 0x40000000) {
+ s->mac_data = do_mac_read(s, val & 0xf);
+ DPRINTF("MAC read %d = 0x%08x\n", val & 0xf, s->mac_data);
+ } else {
+ DPRINTF("MAC write %d = 0x%08x\n", val & 0xf, s->mac_data);
+ do_mac_write(s, val & 0xf, s->mac_data);
+ }
+ }
+ break;
+ case CSR_MAC_CSR_DATA:
+ s->mac_data = val;
+ break;
+ case CSR_AFC_CFG:
+ s->afc_cfg = val & 0x00ffffff;
+ break;
+ case CSR_E2P_CMD:
+ lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f);
+ break;
+ case CSR_E2P_DATA:
+ s->e2p_data = val & 0xff;
+ break;
+
+ default:
+ hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, val);
+ break;
+ }
+ lan9118_update(s);
+}
+
+static uint32_t lan9118_readl(void *opaque, target_phys_addr_t offset)
+{
+ lan9118_state *s = (lan9118_state *)opaque;
+
+ //DPRINTF("Read reg 0x%02x\n", (int)offset);
+ if (offset < 0x20) {
+ /* RX FIFO */
+ return rx_fifo_pop(s);
+ }
+ switch (offset) {
+ case 0x40:
+ return rx_status_fifo_pop(s);
+ case 0x44:
+ return s->rx_status_fifo[s->tx_status_fifo_head];
+ case 0x48:
+ return tx_status_fifo_pop(s);
+ case 0x4c:
+ return s->tx_status_fifo[s->tx_status_fifo_head];
+ case CSR_ID_REV:
+ return 0x01180001;
+ case CSR_IRQ_CFG:
+ return s->irq_cfg;
+ case CSR_INT_STS:
+ return s->int_sts;
+ case CSR_INT_EN:
+ return s->int_en;
+ case CSR_BYTE_TEST:
+ return 0x87654321;
+ case CSR_FIFO_INT:
+ return s->fifo_int;
+ case CSR_RX_CFG:
+ return s->rx_cfg;
+ case CSR_TX_CFG:
+ return s->tx_cfg;
+ case CSR_HW_CFG:
+ return s->hw_cfg | 0x4;
+ case CSR_RX_DP_CTRL:
+ return 0;
+ case CSR_RX_FIFO_INF:
+ return (s->rx_status_fifo_used << 16) | (s->rx_fifo_used << 2);
+ case CSR_TX_FIFO_INF:
+ return (s->tx_status_fifo_used << 16)
+ | (s->tx_fifo_size - s->txp->fifo_used);
+ case CSR_PMT_CTRL:
+ return s->pmt_ctrl;
+ case CSR_GPIO_CFG:
+ return s->gpio_cfg;
+ case CSR_GPT_CFG:
+ return s->gpt_cfg;
+ case CSR_GPT_CNT:
+ return ptimer_get_count(s->timer);
+ case CSR_WORD_SWAP:
+ return s->word_swap;
+ case CSR_FREE_RUN:
+ return (qemu_get_clock(vm_clock) / 40) - s->free_timer_start;
+ case CSR_RX_DROP:
+ /* TODO: Implement dropped frames counter. */
+ return 0;
+ case CSR_MAC_CSR_CMD:
+ return s->mac_cmd;
+ case CSR_MAC_CSR_DATA:
+ return s->mac_data;
+ case CSR_AFC_CFG:
+ return s->afc_cfg;
+ case CSR_E2P_CMD:
+ return s->e2p_cmd;
+ case CSR_E2P_DATA:
+ return s->e2p_data;
+ }
+ hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset);
+ return 0;
+}
+
+static CPUReadMemoryFunc * const lan9118_readfn[] = {
+ lan9118_readl,
+ lan9118_readl,
+ lan9118_readl
+};
+
+static CPUWriteMemoryFunc * const lan9118_writefn[] = {
+ lan9118_writel,
+ lan9118_writel,
+ lan9118_writel
+};
+
+static void lan9118_cleanup(VLANClientState *nc)
+{
+ lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_lan9118_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = lan9118_can_receive,
+ .receive = lan9118_receive,
+ .cleanup = lan9118_cleanup,
+ .link_status_changed = lan9118_set_link,
+};
+
+static int lan9118_init1(SysBusDevice *dev)
+{
+ lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
+ QEMUBH *bh;
+ int i;
+
+ s->mmio_index = cpu_register_io_memory(lan9118_readfn,
+ lan9118_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x100, s->mmio_index);
+ sysbus_init_irq(dev, &s->irq);
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ s->eeprom[0] = 0xa5;
+ for (i = 0; i < 6; i++) {
+ s->eeprom[i + 1] = s->conf.macaddr.a[i];
+ }
+ s->pmt_ctrl = 1;
+ s->txp = &s->tx_packet;
+
+ bh = qemu_bh_new(lan9118_tick, s);
+ s->timer = ptimer_init(bh);
+ ptimer_set_freq(s->timer, 10000);
+ ptimer_set_limit(s->timer, 0xffff, 1);
+
+ /* ??? Save/restore. */
+ return 0;
+}
+
+static SysBusDeviceInfo lan9118_info = {
+ .init = lan9118_init1,
+ .qdev.name = "lan9118",
+ .qdev.size = sizeof(lan9118_state),
+ .qdev.reset = lan9118_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(lan9118_state, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void lan9118_register_devices(void)
+{
+ sysbus_register_withprop(&lan9118_info);
+}
+
+/* Legacy helper function. Should go away when machine config files are
+ implemented. */
+void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(nd, "lan9118");
+ dev = qdev_create(NULL, "lan9118");
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, irq);
+}
+
+device_init(lan9118_register_devices)
diff --git a/hw/lance.c b/hw/lance.c
new file mode 100644
index 0000000..ddb1cbb
--- /dev/null
+++ b/hw/lance.c
@@ -0,0 +1,160 @@
+/*
+ * QEMU AMD PC-Net II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
+ */
+
+/*
+ * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
+ * produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
+ */
+
+#include "sysbus.h"
+#include "net.h"
+#include "qemu-timer.h"
+#include "qemu_socket.h"
+#include "sun4m.h"
+#include "pcnet.h"
+#include "trace.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ PCNetState state;
+} SysBusPCNetState;
+
+static void parent_lance_reset(void *opaque, int irq, int level)
+{
+ SysBusPCNetState *d = opaque;
+ if (level)
+ pcnet_h_reset(&d->state);
+}
+
+static void lance_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SysBusPCNetState *d = opaque;
+
+ trace_lance_mem_writew(addr, val & 0xffff);
+ pcnet_ioport_writew(&d->state, addr, val & 0xffff);
+}
+
+static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ SysBusPCNetState *d = opaque;
+ uint32_t val;
+
+ val = pcnet_ioport_readw(&d->state, addr);
+ trace_lance_mem_readw(addr, val & 0xffff);
+ return val & 0xffff;
+}
+
+static CPUReadMemoryFunc * const lance_mem_read[3] = {
+ NULL,
+ lance_mem_readw,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const lance_mem_write[3] = {
+ NULL,
+ lance_mem_writew,
+ NULL,
+};
+
+static void lance_cleanup(VLANClientState *nc)
+{
+ PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ pcnet_common_cleanup(d);
+}
+
+static NetClientInfo net_lance_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = pcnet_can_receive,
+ .receive = pcnet_receive,
+ .cleanup = lance_cleanup,
+};
+
+static const VMStateDescription vmstate_lance = {
+ .name = "pcnet",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int lance_init(SysBusDevice *dev)
+{
+ SysBusPCNetState *d = FROM_SYSBUS(SysBusPCNetState, dev);
+ PCNetState *s = &d->state;
+
+ s->mmio_index =
+ cpu_register_io_memory(lance_mem_read, lance_mem_write, d,
+ DEVICE_NATIVE_ENDIAN);
+
+ qdev_init_gpio_in(&dev->qdev, parent_lance_reset, 1);
+
+ sysbus_init_mmio(dev, 4, s->mmio_index);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ s->phys_mem_read = ledma_memory_read;
+ s->phys_mem_write = ledma_memory_write;
+ return pcnet_common_init(&dev->qdev, s, &net_lance_info);
+}
+
+static void lance_reset(DeviceState *dev)
+{
+ SysBusPCNetState *d = DO_UPCAST(SysBusPCNetState, busdev.qdev, dev);
+
+ pcnet_h_reset(&d->state);
+}
+
+static SysBusDeviceInfo lance_info = {
+ .init = lance_init,
+ .qdev.name = "lance",
+ .qdev.fw_name = "ethernet",
+ .qdev.size = sizeof(SysBusPCNetState),
+ .qdev.reset = lance_reset,
+ .qdev.vmsd = &vmstate_lance,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque),
+ DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void lance_register_devices(void)
+{
+ sysbus_register_withprop(&lance_info);
+}
+device_init(lance_register_devices)
diff --git a/hw/leon3.c b/hw/leon3.c
new file mode 100644
index 0000000..919f49f
--- /dev/null
+++ b/hw/leon3.c
@@ -0,0 +1,217 @@
+/*
+ * QEMU Leon3 System Emulator
+ *
+ * Copyright (c) 2010-2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "loader.h"
+#include "elf.h"
+#include "trace.h"
+
+#include "grlib.h"
+
+/* Default system clock. */
+#define CPU_CLK (40 * 1000 * 1000)
+
+#define PROM_FILENAME "u-boot.bin"
+
+#define MAX_PILS 16
+
+typedef struct ResetData {
+ CPUState *env;
+ uint32_t entry; /* save kernel entry in case of reset */
+} ResetData;
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUState *env = s->env;
+
+ cpu_reset(env);
+
+ env->halted = 0;
+ env->pc = s->entry;
+ env->npc = s->entry + 4;
+}
+
+void leon3_irq_ack(void *irq_manager, int intno)
+{
+ grlib_irqmp_ack((DeviceState *)irq_manager, intno);
+}
+
+static void leon3_set_pil_in(void *opaque, uint32_t pil_in)
+{
+ CPUState *env = (CPUState *)opaque;
+
+ assert(env != NULL);
+
+ env->pil_in = pil_in;
+
+ if (env->pil_in && (env->interrupt_index == 0 ||
+ (env->interrupt_index & ~15) == TT_EXTINT)) {
+ unsigned int i;
+
+ for (i = 15; i > 0; i--) {
+ if (env->pil_in & (1 << i)) {
+ int old_interrupt = env->interrupt_index;
+
+ env->interrupt_index = TT_EXTINT | i;
+ if (old_interrupt != env->interrupt_index) {
+ trace_leon3_set_irq(i);
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ break;
+ }
+ }
+ } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) {
+ trace_leon3_reset_irq(env->interrupt_index & 15);
+ env->interrupt_index = 0;
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+}
+
+static void leon3_generic_hw_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env;
+ ram_addr_t ram_offset, prom_offset;
+ int ret;
+ char *filename;
+ qemu_irq *cpu_irqs = NULL;
+ int bios_size;
+ int prom_size;
+ ResetData *reset_info;
+
+ /* Init CPU */
+ if (!cpu_model) {
+ cpu_model = "LEON3";
+ }
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
+ exit(1);
+ }
+
+ cpu_sparc_set_id(env, 0);
+
+ /* Reset data */
+ reset_info = qemu_mallocz(sizeof(ResetData));
+ reset_info->env = env;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ /* Allocate IRQ manager */
+ grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS, &leon3_set_pil_in);
+
+ env->qemu_irq_ack = leon3_irq_manager;
+
+ /* Allocate RAM */
+ if ((uint64_t)ram_size > (1UL << 30)) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d, maximum 1G\n",
+ (unsigned int)(ram_size / (1024 * 1024)));
+ exit(1);
+ }
+
+ ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size);
+ cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM);
+
+ /* Allocate BIOS */
+ prom_size = 8 * 1024 * 1024; /* 8Mb */
+ prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size);
+ cpu_register_physical_memory(0x00000000, prom_size,
+ prom_offset | IO_MEM_ROM);
+
+ /* Load boot prom */
+ if (bios_name == NULL) {
+ bios_name = PROM_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+
+ bios_size = get_image_size(filename);
+
+ if (bios_size > prom_size) {
+ fprintf(stderr, "qemu: could not load prom '%s': file too big\n",
+ filename);
+ exit(1);
+ }
+
+ if (bios_size > 0) {
+ ret = load_image_targphys(filename, 0x00000000, bios_size);
+ if (ret < 0 || ret > prom_size) {
+ fprintf(stderr, "qemu: could not load prom '%s'\n", filename);
+ exit(1);
+ }
+ } else if (kernel_filename == NULL) {
+ fprintf(stderr, "Can't read bios image %s\n", filename);
+ exit(1);
+ }
+
+ /* Can directly load an application. */
+ if (kernel_filename != NULL) {
+ long kernel_size;
+ uint64_t entry;
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
+ 1 /* big endian */, ELF_MACHINE, 0);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ if (bios_size <= 0) {
+ /* If there is no bios/monitor, start the application. */
+ env->pc = entry;
+ env->npc = entry + 4;
+ reset_info->entry = entry;
+ }
+ }
+
+ /* Allocate timers */
+ grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6);
+
+ /* Allocate uart */
+ if (serial_hds[0]) {
+ grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]);
+ }
+}
+
+QEMUMachine leon3_generic_machine = {
+ .name = "leon3_generic",
+ .desc = "Leon-3 generic",
+ .init = leon3_generic_hw_init,
+ .use_scsi = 0,
+};
+
+static void leon3_machine_init(void)
+{
+ qemu_register_machine(&leon3_generic_machine);
+}
+
+machine_init(leon3_machine_init);
diff --git a/hw/lm832x.c b/hw/lm832x.c
new file mode 100644
index 0000000..ce7dcac
--- /dev/null
+++ b/hw/lm832x.c
@@ -0,0 +1,512 @@
+/*
+ * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "i2c.h"
+#include "qemu-timer.h"
+#include "console.h"
+
+typedef struct {
+ i2c_slave i2c;
+ uint8_t i2c_dir;
+ uint8_t i2c_cycle;
+ uint8_t reg;
+
+ qemu_irq nirq;
+ uint16_t model;
+
+ struct {
+ qemu_irq out[2];
+ int in[2][2];
+ } mux;
+
+ uint8_t config;
+ uint8_t status;
+ uint8_t acttime;
+ uint8_t error;
+ uint8_t clock;
+
+ struct {
+ uint16_t pull;
+ uint16_t mask;
+ uint16_t dir;
+ uint16_t level;
+ qemu_irq out[16];
+ } gpio;
+
+ struct {
+ uint8_t dbnctime;
+ uint8_t size;
+ uint8_t start;
+ uint8_t len;
+ uint8_t fifo[16];
+ } kbd;
+
+ struct {
+ uint16_t file[256];
+ uint8_t faddr;
+ uint8_t addr[3];
+ QEMUTimer *tm[3];
+ } pwm;
+} LM823KbdState;
+
+#define INT_KEYPAD (1 << 0)
+#define INT_ERROR (1 << 3)
+#define INT_NOINIT (1 << 4)
+#define INT_PWMEND(n) (1 << (5 + n))
+
+#define ERR_BADPAR (1 << 0)
+#define ERR_CMDUNK (1 << 1)
+#define ERR_KEYOVR (1 << 2)
+#define ERR_FIFOOVR (1 << 6)
+
+static void lm_kbd_irq_update(LM823KbdState *s)
+{
+ qemu_set_irq(s->nirq, !s->status);
+}
+
+static void lm_kbd_gpio_update(LM823KbdState *s)
+{
+}
+
+static void lm_kbd_reset(LM823KbdState *s)
+{
+ s->config = 0x80;
+ s->status = INT_NOINIT;
+ s->acttime = 125;
+ s->kbd.dbnctime = 3;
+ s->kbd.size = 0x33;
+ s->clock = 0x08;
+
+ lm_kbd_irq_update(s);
+ lm_kbd_gpio_update(s);
+}
+
+static void lm_kbd_error(LM823KbdState *s, int err)
+{
+ s->error |= err;
+ s->status |= INT_ERROR;
+ lm_kbd_irq_update(s);
+}
+
+static void lm_kbd_pwm_tick(LM823KbdState *s, int line)
+{
+}
+
+static void lm_kbd_pwm_start(LM823KbdState *s, int line)
+{
+ lm_kbd_pwm_tick(s, line);
+}
+
+static void lm_kbd_pwm0_tick(void *opaque)
+{
+ lm_kbd_pwm_tick(opaque, 0);
+}
+static void lm_kbd_pwm1_tick(void *opaque)
+{
+ lm_kbd_pwm_tick(opaque, 1);
+}
+static void lm_kbd_pwm2_tick(void *opaque)
+{
+ lm_kbd_pwm_tick(opaque, 2);
+}
+
+enum {
+ LM832x_CMD_READ_ID = 0x80, /* Read chip ID. */
+ LM832x_CMD_WRITE_CFG = 0x81, /* Set configuration item. */
+ LM832x_CMD_READ_INT = 0x82, /* Get interrupt status. */
+ LM832x_CMD_RESET = 0x83, /* Reset, same as external one */
+ LM823x_CMD_WRITE_PULL_DOWN = 0x84, /* Select GPIO pull-up/down. */
+ LM832x_CMD_WRITE_PORT_SEL = 0x85, /* Select GPIO in/out. */
+ LM832x_CMD_WRITE_PORT_STATE = 0x86, /* Set GPIO pull-up/down. */
+ LM832x_CMD_READ_PORT_SEL = 0x87, /* Get GPIO in/out. */
+ LM832x_CMD_READ_PORT_STATE = 0x88, /* Get GPIO pull-up/down. */
+ LM832x_CMD_READ_FIFO = 0x89, /* Read byte from FIFO. */
+ LM832x_CMD_RPT_READ_FIFO = 0x8a, /* Read FIFO (no increment). */
+ LM832x_CMD_SET_ACTIVE = 0x8b, /* Set active time. */
+ LM832x_CMD_READ_ERROR = 0x8c, /* Get error status. */
+ LM832x_CMD_READ_ROTATOR = 0x8e, /* Read rotator status. */
+ LM832x_CMD_SET_DEBOUNCE = 0x8f, /* Set debouncing time. */
+ LM832x_CMD_SET_KEY_SIZE = 0x90, /* Set keypad size. */
+ LM832x_CMD_READ_KEY_SIZE = 0x91, /* Get keypad size. */
+ LM832x_CMD_READ_CFG = 0x92, /* Get configuration item. */
+ LM832x_CMD_WRITE_CLOCK = 0x93, /* Set clock config. */
+ LM832x_CMD_READ_CLOCK = 0x94, /* Get clock config. */
+ LM832x_CMD_PWM_WRITE = 0x95, /* Write PWM script. */
+ LM832x_CMD_PWM_START = 0x96, /* Start PWM engine. */
+ LM832x_CMD_PWM_STOP = 0x97, /* Stop PWM engine. */
+ LM832x_GENERAL_ERROR = 0xff, /* There was one error.
+ Previously was represented by -1
+ This is not a command */
+};
+
+#define LM832x_MAX_KPX 8
+#define LM832x_MAX_KPY 12
+
+static uint8_t lm_kbd_read(LM823KbdState *s, int reg, int byte)
+{
+ int ret;
+
+ switch (reg) {
+ case LM832x_CMD_READ_ID:
+ ret = 0x0400;
+ break;
+
+ case LM832x_CMD_READ_INT:
+ ret = s->status;
+ if (!(s->status & INT_NOINIT)) {
+ s->status = 0;
+ lm_kbd_irq_update(s);
+ }
+ break;
+
+ case LM832x_CMD_READ_PORT_SEL:
+ ret = s->gpio.dir;
+ break;
+ case LM832x_CMD_READ_PORT_STATE:
+ ret = s->gpio.mask;
+ break;
+
+ case LM832x_CMD_READ_FIFO:
+ if (s->kbd.len <= 1)
+ return 0x00;
+
+ /* Example response from the two commands after a INT_KEYPAD
+ * interrupt caused by the key 0x3c being pressed:
+ * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+ * READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+ * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+ *
+ * 55 is the code of the key release event serviced in the previous
+ * interrupt handling.
+ *
+ * TODO: find out whether the FIFO is advanced a single character
+ * before reading every byte or the whole size of the FIFO at the
+ * last LM832x_CMD_READ_FIFO. This affects LM832x_CMD_RPT_READ_FIFO
+ * output in cases where there are more than one event in the FIFO.
+ * Assume 0xbc and 0x3c events are in the FIFO:
+ * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9
+ * READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9
+ * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c?
+ */
+ s->kbd.start ++;
+ s->kbd.start &= sizeof(s->kbd.fifo) - 1;
+ s->kbd.len --;
+
+ return s->kbd.fifo[s->kbd.start];
+ case LM832x_CMD_RPT_READ_FIFO:
+ if (byte >= s->kbd.len)
+ return 0x00;
+
+ return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)];
+
+ case LM832x_CMD_READ_ERROR:
+ return s->error;
+
+ case LM832x_CMD_READ_ROTATOR:
+ return 0;
+
+ case LM832x_CMD_READ_KEY_SIZE:
+ return s->kbd.size;
+
+ case LM832x_CMD_READ_CFG:
+ return s->config & 0xf;
+
+ case LM832x_CMD_READ_CLOCK:
+ return (s->clock & 0xfc) | 2;
+
+ default:
+ lm_kbd_error(s, ERR_CMDUNK);
+ fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
+ return 0x00;
+ }
+
+ return ret >> (byte << 3);
+}
+
+static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value)
+{
+ switch (reg) {
+ case LM832x_CMD_WRITE_CFG:
+ s->config = value;
+ /* This must be done whenever s->mux.in is updated (never). */
+ if ((s->config >> 1) & 1) /* MUX1EN */
+ qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]);
+ if ((s->config >> 3) & 1) /* MUX2EN */
+ qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]);
+ /* TODO: check that this is issued only following the chip reset
+ * and not in the middle of operation and that it is followed by
+ * the GPIO ports re-resablishing through WRITE_PORT_SEL and
+ * WRITE_PORT_STATE (using a timer perhaps) and otherwise output
+ * warnings. */
+ s->status = 0;
+ lm_kbd_irq_update(s);
+ s->kbd.len = 0;
+ s->kbd.start = 0;
+ s->reg = LM832x_GENERAL_ERROR;
+ break;
+
+ case LM832x_CMD_RESET:
+ if (value == 0xaa)
+ lm_kbd_reset(s);
+ else
+ lm_kbd_error(s, ERR_BADPAR);
+ s->reg = LM832x_GENERAL_ERROR;
+ break;
+
+ case LM823x_CMD_WRITE_PULL_DOWN:
+ if (!byte)
+ s->gpio.pull = value;
+ else {
+ s->gpio.pull |= value << 8;
+ lm_kbd_gpio_update(s);
+ s->reg = LM832x_GENERAL_ERROR;
+ }
+ break;
+ case LM832x_CMD_WRITE_PORT_SEL:
+ if (!byte)
+ s->gpio.dir = value;
+ else {
+ s->gpio.dir |= value << 8;
+ lm_kbd_gpio_update(s);
+ s->reg = LM832x_GENERAL_ERROR;
+ }
+ break;
+ case LM832x_CMD_WRITE_PORT_STATE:
+ if (!byte)
+ s->gpio.mask = value;
+ else {
+ s->gpio.mask |= value << 8;
+ lm_kbd_gpio_update(s);
+ s->reg = LM832x_GENERAL_ERROR;
+ }
+ break;
+
+ case LM832x_CMD_SET_ACTIVE:
+ s->acttime = value;
+ s->reg = LM832x_GENERAL_ERROR;
+ break;
+
+ case LM832x_CMD_SET_DEBOUNCE:
+ s->kbd.dbnctime = value;
+ s->reg = LM832x_GENERAL_ERROR;
+ if (!value)
+ lm_kbd_error(s, ERR_BADPAR);
+ break;
+
+ case LM832x_CMD_SET_KEY_SIZE:
+ s->kbd.size = value;
+ s->reg = LM832x_GENERAL_ERROR;
+ if (
+ (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY ||
+ (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX)
+ lm_kbd_error(s, ERR_BADPAR);
+ break;
+
+ case LM832x_CMD_WRITE_CLOCK:
+ s->clock = value;
+ s->reg = LM832x_GENERAL_ERROR;
+ if ((value & 3) && (value & 3) != 3) {
+ lm_kbd_error(s, ERR_BADPAR);
+ fprintf(stderr, "%s: invalid clock setting in RCPWM\n",
+ __FUNCTION__);
+ }
+ /* TODO: Validate that the command is only issued once */
+ break;
+
+ case LM832x_CMD_PWM_WRITE:
+ if (byte == 0) {
+ if (!(value & 3) || (value >> 2) > 59) {
+ lm_kbd_error(s, ERR_BADPAR);
+ s->reg = LM832x_GENERAL_ERROR;
+ break;
+ }
+
+ s->pwm.faddr = value;
+ s->pwm.file[s->pwm.faddr] = 0;
+ } else if (byte == 1) {
+ s->pwm.file[s->pwm.faddr] |= value << 8;
+ } else if (byte == 2) {
+ s->pwm.file[s->pwm.faddr] |= value << 0;
+ s->reg = LM832x_GENERAL_ERROR;
+ }
+ break;
+ case LM832x_CMD_PWM_START:
+ s->reg = LM832x_GENERAL_ERROR;
+ if (!(value & 3) || (value >> 2) > 59) {
+ lm_kbd_error(s, ERR_BADPAR);
+ break;
+ }
+
+ s->pwm.addr[(value & 3) - 1] = value >> 2;
+ lm_kbd_pwm_start(s, (value & 3) - 1);
+ break;
+ case LM832x_CMD_PWM_STOP:
+ s->reg = LM832x_GENERAL_ERROR;
+ if (!(value & 3)) {
+ lm_kbd_error(s, ERR_BADPAR);
+ break;
+ }
+
+ qemu_del_timer(s->pwm.tm[(value & 3) - 1]);
+ break;
+
+ case LM832x_GENERAL_ERROR:
+ lm_kbd_error(s, ERR_BADPAR);
+ break;
+ default:
+ lm_kbd_error(s, ERR_CMDUNK);
+ fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
+ break;
+ }
+}
+
+static void lm_i2c_event(i2c_slave *i2c, enum i2c_event event)
+{
+ LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+ switch (event) {
+ case I2C_START_RECV:
+ case I2C_START_SEND:
+ s->i2c_cycle = 0;
+ s->i2c_dir = (event == I2C_START_SEND);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int lm_i2c_rx(i2c_slave *i2c)
+{
+ LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+ return lm_kbd_read(s, s->reg, s->i2c_cycle ++);
+}
+
+static int lm_i2c_tx(i2c_slave *i2c, uint8_t data)
+{
+ LM823KbdState *s = (LM823KbdState *) i2c;
+
+ if (!s->i2c_cycle)
+ s->reg = data;
+ else
+ lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data);
+ s->i2c_cycle ++;
+
+ return 0;
+}
+
+static int lm_kbd_post_load(void *opaque, int version_id)
+{
+ LM823KbdState *s = opaque;
+
+ lm_kbd_irq_update(s);
+ lm_kbd_gpio_update(s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_lm_kbd = {
+ .name = "LM8323",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = lm_kbd_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_I2C_SLAVE(i2c, LM823KbdState),
+ VMSTATE_UINT8(i2c_dir, LM823KbdState),
+ VMSTATE_UINT8(i2c_cycle, LM823KbdState),
+ VMSTATE_UINT8(reg, LM823KbdState),
+ VMSTATE_UINT8(config, LM823KbdState),
+ VMSTATE_UINT8(status, LM823KbdState),
+ VMSTATE_UINT8(acttime, LM823KbdState),
+ VMSTATE_UINT8(error, LM823KbdState),
+ VMSTATE_UINT8(clock, LM823KbdState),
+ VMSTATE_UINT16(gpio.pull, LM823KbdState),
+ VMSTATE_UINT16(gpio.mask, LM823KbdState),
+ VMSTATE_UINT16(gpio.dir, LM823KbdState),
+ VMSTATE_UINT16(gpio.level, LM823KbdState),
+ VMSTATE_UINT8(kbd.dbnctime, LM823KbdState),
+ VMSTATE_UINT8(kbd.size, LM823KbdState),
+ VMSTATE_UINT8(kbd.start, LM823KbdState),
+ VMSTATE_UINT8(kbd.len, LM823KbdState),
+ VMSTATE_BUFFER(kbd.fifo, LM823KbdState),
+ VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256),
+ VMSTATE_UINT8(pwm.faddr, LM823KbdState),
+ VMSTATE_BUFFER(pwm.addr, LM823KbdState),
+ VMSTATE_TIMER_ARRAY(pwm.tm, LM823KbdState, 3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static int lm8323_init(i2c_slave *i2c)
+{
+ LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+ s->model = 0x8323;
+ s->pwm.tm[0] = qemu_new_timer(vm_clock, lm_kbd_pwm0_tick, s);
+ s->pwm.tm[1] = qemu_new_timer(vm_clock, lm_kbd_pwm1_tick, s);
+ s->pwm.tm[2] = qemu_new_timer(vm_clock, lm_kbd_pwm2_tick, s);
+ qdev_init_gpio_out(&i2c->qdev, &s->nirq, 1);
+
+ lm_kbd_reset(s);
+
+ qemu_register_reset((void *) lm_kbd_reset, s);
+ return 0;
+}
+
+void lm832x_key_event(struct i2c_slave *i2c, int key, int state)
+{
+ LM823KbdState *s = (LM823KbdState *) i2c;
+
+ if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))
+ return;
+
+ if (s->kbd.len >= sizeof(s->kbd.fifo)) {
+ lm_kbd_error(s, ERR_FIFOOVR);
+ return;
+ }
+
+ s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] =
+ key | (state << 7);
+
+ /* We never set ERR_KEYOVR because we support multiple keys fine. */
+ s->status |= INT_KEYPAD;
+ lm_kbd_irq_update(s);
+}
+
+static I2CSlaveInfo lm8323_info = {
+ .qdev.name = "lm8323",
+ .qdev.size = sizeof(LM823KbdState),
+ .qdev.vmsd = &vmstate_lm_kbd,
+ .init = lm8323_init,
+ .event = lm_i2c_event,
+ .recv = lm_i2c_rx,
+ .send = lm_i2c_tx
+};
+
+static void lm832x_register_devices(void)
+{
+ i2c_register_slave(&lm8323_info);
+}
+
+device_init(lm832x_register_devices)
diff --git a/hw/loader.c b/hw/loader.c
new file mode 100644
index 0000000..35d792e
--- /dev/null
+++ b/hw/loader.c
@@ -0,0 +1,795 @@
+/*
+ * QEMU Executable loader
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Gunzip functionality in this file is derived from u-boot:
+ *
+ * (C) Copyright 2008 Semihalf
+ *
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "disas.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "uboot_image.h"
+#include "loader.h"
+#include "fw_cfg.h"
+
+#include <zlib.h>
+
+static int roms_loaded;
+
+/* return the size or -1 if error */
+int get_image_size(const char *filename)
+{
+ int fd, size;
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+ size = lseek(fd, 0, SEEK_END);
+ close(fd);
+ return size;
+}
+
+/* return the size or -1 if error */
+/* deprecated, because caller does not specify buffer size! */
+int load_image(const char *filename, uint8_t *addr)
+{
+ int fd, size;
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+ size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, addr, size) != size) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return size;
+}
+
+/* read()-like version */
+int read_targphys(const char *name,
+ int fd, target_phys_addr_t dst_addr, size_t nbytes)
+{
+ uint8_t *buf;
+ size_t did;
+
+ buf = qemu_malloc(nbytes);
+ did = read(fd, buf, nbytes);
+ if (did > 0)
+ rom_add_blob_fixed("read", buf, did, dst_addr);
+ qemu_free(buf);
+ return did;
+}
+
+/* return the size or -1 if error */
+int load_image_targphys(const char *filename,
+ target_phys_addr_t addr, int max_sz)
+{
+ int size;
+
+ size = get_image_size(filename);
+ if (size > 0)
+ rom_add_file_fixed(filename, addr, -1);
+ return size;
+}
+
+void pstrcpy_targphys(const char *name, target_phys_addr_t dest, int buf_size,
+ const char *source)
+{
+ const char *nulp;
+ char *ptr;
+
+ if (buf_size <= 0) return;
+ nulp = memchr(source, 0, buf_size);
+ if (nulp) {
+ rom_add_blob_fixed(name, source, (nulp - source) + 1, dest);
+ } else {
+ rom_add_blob_fixed(name, source, buf_size, dest);
+ ptr = rom_ptr(dest + buf_size - 1);
+ *ptr = 0;
+ }
+}
+
+/* A.OUT loader */
+
+struct exec
+{
+ uint32_t a_info; /* Use macros N_MAGIC, etc for access */
+ uint32_t a_text; /* length of text, in bytes */
+ uint32_t a_data; /* length of data, in bytes */
+ uint32_t a_bss; /* length of uninitialized data area, in bytes */
+ uint32_t a_syms; /* length of symbol table data in file, in bytes */
+ uint32_t a_entry; /* start address */
+ uint32_t a_trsize; /* length of relocation info for text, in bytes */
+ uint32_t a_drsize; /* length of relocation info for data, in bytes */
+};
+
+static void bswap_ahdr(struct exec *e)
+{
+ bswap32s(&e->a_info);
+ bswap32s(&e->a_text);
+ bswap32s(&e->a_data);
+ bswap32s(&e->a_bss);
+ bswap32s(&e->a_syms);
+ bswap32s(&e->a_entry);
+ bswap32s(&e->a_trsize);
+ bswap32s(&e->a_drsize);
+}
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
+ (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0)
+#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1))
+
+#define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text)
+
+#define N_DATADDR(x, target_page_size) \
+ (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \
+ : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size)))
+
+
+int load_aout(const char *filename, target_phys_addr_t addr, int max_sz,
+ int bswap_needed, target_phys_addr_t target_page_size)
+{
+ int fd, size, ret;
+ struct exec e;
+ uint32_t magic;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+
+ size = read(fd, &e, sizeof(e));
+ if (size < 0)
+ goto fail;
+
+ if (bswap_needed) {
+ bswap_ahdr(&e);
+ }
+
+ magic = N_MAGIC(e);
+ switch (magic) {
+ case ZMAGIC:
+ case QMAGIC:
+ case OMAGIC:
+ if (e.a_text + e.a_data > max_sz)
+ goto fail;
+ lseek(fd, N_TXTOFF(e), SEEK_SET);
+ size = read_targphys(filename, fd, addr, e.a_text + e.a_data);
+ if (size < 0)
+ goto fail;
+ break;
+ case NMAGIC:
+ if (N_DATADDR(e, target_page_size) + e.a_data > max_sz)
+ goto fail;
+ lseek(fd, N_TXTOFF(e), SEEK_SET);
+ size = read_targphys(filename, fd, addr, e.a_text);
+ if (size < 0)
+ goto fail;
+ ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size),
+ e.a_data);
+ if (ret < 0)
+ goto fail;
+ size += ret;
+ break;
+ default:
+ goto fail;
+ }
+ close(fd);
+ return size;
+ fail:
+ close(fd);
+ return -1;
+}
+
+/* ELF loader */
+
+static void *load_at(int fd, int offset, int size)
+{
+ void *ptr;
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ return NULL;
+ ptr = qemu_malloc(size);
+ if (read(fd, ptr, size) != size) {
+ qemu_free(ptr);
+ return NULL;
+ }
+ return ptr;
+}
+
+#ifdef ELF_CLASS
+#undef ELF_CLASS
+#endif
+
+#define ELF_CLASS ELFCLASS32
+#include "elf.h"
+
+#define SZ 32
+#define elf_word uint32_t
+#define elf_sword int32_t
+#define bswapSZs bswap32s
+#include "elf_ops.h"
+
+#undef elfhdr
+#undef elf_phdr
+#undef elf_shdr
+#undef elf_sym
+#undef elf_note
+#undef elf_word
+#undef elf_sword
+#undef bswapSZs
+#undef SZ
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+#define elf_shdr elf64_shdr
+#define elf_sym elf64_sym
+#define elf_word uint64_t
+#define elf_sword int64_t
+#define bswapSZs bswap64s
+#define SZ 64
+#include "elf_ops.h"
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
+{
+ int fd, data_order, target_data_order, must_swab, ret;
+ uint8_t e_ident[EI_NIDENT];
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ perror(filename);
+ return -1;
+ }
+ if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
+ goto fail;
+ if (e_ident[0] != ELFMAG0 ||
+ e_ident[1] != ELFMAG1 ||
+ e_ident[2] != ELFMAG2 ||
+ e_ident[3] != ELFMAG3)
+ goto fail;
+#ifdef HOST_WORDS_BIGENDIAN
+ data_order = ELFDATA2MSB;
+#else
+ data_order = ELFDATA2LSB;
+#endif
+ must_swab = data_order != e_ident[EI_DATA];
+ if (big_endian) {
+ target_data_order = ELFDATA2MSB;
+ } else {
+ target_data_order = ELFDATA2LSB;
+ }
+
+ if (target_data_order != e_ident[EI_DATA]) {
+ goto fail;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ if (e_ident[EI_CLASS] == ELFCLASS64) {
+ ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ } else {
+ ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
+ pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+ }
+
+ close(fd);
+ return ret;
+
+ fail:
+ close(fd);
+ return -1;
+}
+
+static void bswap_uboot_header(uboot_image_header_t *hdr)
+{
+#ifndef HOST_WORDS_BIGENDIAN
+ bswap32s(&hdr->ih_magic);
+ bswap32s(&hdr->ih_hcrc);
+ bswap32s(&hdr->ih_time);
+ bswap32s(&hdr->ih_size);
+ bswap32s(&hdr->ih_load);
+ bswap32s(&hdr->ih_ep);
+ bswap32s(&hdr->ih_dcrc);
+#endif
+}
+
+
+#define ZALLOC_ALIGNMENT 16
+
+static void *zalloc(void *x, unsigned items, unsigned size)
+{
+ void *p;
+
+ size *= items;
+ size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
+
+ p = qemu_malloc(size);
+
+ return (p);
+}
+
+static void zfree(void *x, void *addr)
+{
+ qemu_free(addr);
+}
+
+
+#define HEAD_CRC 2
+#define EXTRA_FIELD 4
+#define ORIG_NAME 8
+#define COMMENT 0x10
+#define RESERVED 0xe0
+
+#define DEFLATED 8
+
+/* This is the maximum in uboot, so if a uImage overflows this, it would
+ * overflow on real hardware too. */
+#define UBOOT_MAX_GUNZIP_BYTES 0x800000
+
+static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
+ size_t srclen)
+{
+ z_stream s;
+ ssize_t dstbytes;
+ int r, i, flags;
+
+ /* skip header */
+ i = 10;
+ flags = src[3];
+ if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+ puts ("Error: Bad gzipped data\n");
+ return -1;
+ }
+ if ((flags & EXTRA_FIELD) != 0)
+ i = 12 + src[10] + (src[11] << 8);
+ if ((flags & ORIG_NAME) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & COMMENT) != 0)
+ while (src[i++] != 0)
+ ;
+ if ((flags & HEAD_CRC) != 0)
+ i += 2;
+ if (i >= srclen) {
+ puts ("Error: gunzip out of data in header\n");
+ return -1;
+ }
+
+ s.zalloc = zalloc;
+ s.zfree = zfree;
+
+ r = inflateInit2(&s, -MAX_WBITS);
+ if (r != Z_OK) {
+ printf ("Error: inflateInit2() returned %d\n", r);
+ return (-1);
+ }
+ s.next_in = src + i;
+ s.avail_in = srclen - i;
+ s.next_out = dst;
+ s.avail_out = dstlen;
+ r = inflate(&s, Z_FINISH);
+ if (r != Z_OK && r != Z_STREAM_END) {
+ printf ("Error: inflate() returned %d\n", r);
+ return -1;
+ }
+ dstbytes = s.next_out - (unsigned char *) dst;
+ inflateEnd(&s);
+
+ return dstbytes;
+}
+
+/* Load a U-Boot image. */
+int load_uimage(const char *filename, target_phys_addr_t *ep,
+ target_phys_addr_t *loadaddr, int *is_linux)
+{
+ int fd;
+ int size;
+ uboot_image_header_t h;
+ uboot_image_header_t *hdr = &h;
+ uint8_t *data = NULL;
+ int ret = -1;
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return -1;
+
+ size = read(fd, hdr, sizeof(uboot_image_header_t));
+ if (size < 0)
+ goto out;
+
+ bswap_uboot_header(hdr);
+
+ if (hdr->ih_magic != IH_MAGIC)
+ goto out;
+
+ /* TODO: Implement other image types. */
+ if (hdr->ih_type != IH_TYPE_KERNEL) {
+ fprintf(stderr, "Can only load u-boot image type \"kernel\"\n");
+ goto out;
+ }
+
+ switch (hdr->ih_comp) {
+ case IH_COMP_NONE:
+ case IH_COMP_GZIP:
+ break;
+ default:
+ fprintf(stderr,
+ "Unable to load u-boot images with compression type %d\n",
+ hdr->ih_comp);
+ goto out;
+ }
+
+ /* TODO: Check CPU type. */
+ if (is_linux) {
+ if (hdr->ih_os == IH_OS_LINUX)
+ *is_linux = 1;
+ else
+ *is_linux = 0;
+ }
+
+ *ep = hdr->ih_ep;
+ data = qemu_malloc(hdr->ih_size);
+
+ if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
+ fprintf(stderr, "Error reading file\n");
+ goto out;
+ }
+
+ if (hdr->ih_comp == IH_COMP_GZIP) {
+ uint8_t *compressed_data;
+ size_t max_bytes;
+ ssize_t bytes;
+
+ compressed_data = data;
+ max_bytes = UBOOT_MAX_GUNZIP_BYTES;
+ data = qemu_malloc(max_bytes);
+
+ bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
+ qemu_free(compressed_data);
+ if (bytes < 0) {
+ fprintf(stderr, "Unable to decompress gzipped image!\n");
+ goto out;
+ }
+ hdr->ih_size = bytes;
+ }
+
+ rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
+
+ if (loadaddr)
+ *loadaddr = hdr->ih_load;
+
+ ret = hdr->ih_size;
+
+out:
+ if (data)
+ qemu_free(data);
+ close(fd);
+ return ret;
+}
+
+/*
+ * Functions for reboot-persistent memory regions.
+ * - used for vga bios and option roms.
+ * - also linux kernel (-kernel / -initrd).
+ */
+
+typedef struct Rom Rom;
+
+struct Rom {
+ char *name;
+ char *path;
+ size_t romsize;
+ uint8_t *data;
+ int isrom;
+ char *fw_dir;
+ char *fw_file;
+
+ target_phys_addr_t addr;
+ QTAILQ_ENTRY(Rom) next;
+};
+
+static FWCfgState *fw_cfg;
+static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
+
+static void rom_insert(Rom *rom)
+{
+ Rom *item;
+
+ if (roms_loaded) {
+ hw_error ("ROM images must be loaded at startup\n");
+ }
+
+ /* list is ordered by load address */
+ QTAILQ_FOREACH(item, &roms, next) {
+ if (rom->addr >= item->addr)
+ continue;
+ QTAILQ_INSERT_BEFORE(item, rom, next);
+ return;
+ }
+ QTAILQ_INSERT_TAIL(&roms, rom, next);
+}
+
+int rom_add_file(const char *file, const char *fw_dir,
+ target_phys_addr_t addr, int32_t bootindex)
+{
+ Rom *rom;
+ int rc, fd = -1;
+ char devpath[100];
+
+ rom = qemu_mallocz(sizeof(*rom));
+ rom->name = qemu_strdup(file);
+ rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name);
+ if (rom->path == NULL) {
+ rom->path = qemu_strdup(file);
+ }
+
+ fd = open(rom->path, O_RDONLY | O_BINARY);
+ if (fd == -1) {
+ fprintf(stderr, "Could not open option rom '%s': %s\n",
+ rom->path, strerror(errno));
+ goto err;
+ }
+
+ if (fw_dir) {
+ rom->fw_dir = qemu_strdup(fw_dir);
+ rom->fw_file = qemu_strdup(file);
+ }
+ rom->addr = addr;
+ rom->romsize = lseek(fd, 0, SEEK_END);
+ rom->data = qemu_mallocz(rom->romsize);
+ lseek(fd, 0, SEEK_SET);
+ rc = read(fd, rom->data, rom->romsize);
+ if (rc != rom->romsize) {
+ fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
+ rom->name, rc, rom->romsize);
+ goto err;
+ }
+ close(fd);
+ rom_insert(rom);
+ if (rom->fw_file && fw_cfg) {
+ const char *basename;
+ char fw_file_name[56];
+
+ basename = strrchr(rom->fw_file, '/');
+ if (basename) {
+ basename++;
+ } else {
+ basename = rom->fw_file;
+ }
+ snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir,
+ basename);
+ fw_cfg_add_file(fw_cfg, fw_file_name, rom->data, rom->romsize);
+ snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
+ } else {
+ snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ }
+
+ add_boot_device_path(bootindex, NULL, devpath);
+ return 0;
+
+err:
+ if (fd != -1)
+ close(fd);
+ qemu_free(rom->data);
+ qemu_free(rom->path);
+ qemu_free(rom->name);
+ qemu_free(rom);
+ return -1;
+}
+
+int rom_add_blob(const char *name, const void *blob, size_t len,
+ target_phys_addr_t addr)
+{
+ Rom *rom;
+
+ rom = qemu_mallocz(sizeof(*rom));
+ rom->name = qemu_strdup(name);
+ rom->addr = addr;
+ rom->romsize = len;
+ rom->data = qemu_mallocz(rom->romsize);
+ memcpy(rom->data, blob, len);
+ rom_insert(rom);
+ return 0;
+}
+
+int rom_add_vga(const char *file)
+{
+ return rom_add_file(file, "vgaroms", 0, -1);
+}
+
+int rom_add_option(const char *file, int32_t bootindex)
+{
+ return rom_add_file(file, "genroms", 0, bootindex);
+}
+
+static void rom_reset(void *unused)
+{
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (rom->data == NULL) {
+ continue;
+ }
+ cpu_physical_memory_write_rom(rom->addr, rom->data, rom->romsize);
+ if (rom->isrom) {
+ /* rom needs to be written only once */
+ qemu_free(rom->data);
+ rom->data = NULL;
+ }
+ }
+}
+
+int rom_load_all(void)
+{
+ target_phys_addr_t addr = 0;
+ int memtype;
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (addr > rom->addr) {
+ fprintf(stderr, "rom: requested regions overlap "
+ "(rom %s. free=0x" TARGET_FMT_plx
+ ", addr=0x" TARGET_FMT_plx ")\n",
+ rom->name, addr, rom->addr);
+ return -1;
+ }
+ addr = rom->addr;
+ addr += rom->romsize;
+ memtype = cpu_get_physical_page_desc(rom->addr) & (3 << IO_MEM_SHIFT);
+ if (memtype == IO_MEM_ROM)
+ rom->isrom = 1;
+ }
+ qemu_register_reset(rom_reset, NULL);
+ roms_loaded = 1;
+ return 0;
+}
+
+void rom_set_fw(void *f)
+{
+ fw_cfg = f;
+}
+
+static Rom *find_rom(target_phys_addr_t addr)
+{
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (rom->addr > addr) {
+ continue;
+ }
+ if (rom->addr + rom->romsize < addr) {
+ continue;
+ }
+ return rom;
+ }
+ return NULL;
+}
+
+/*
+ * Copies memory from registered ROMs to dest. Any memory that is contained in
+ * a ROM between addr and addr + size is copied. Note that this can involve
+ * multiple ROMs, which need not start at addr and need not end at addr + size.
+ */
+int rom_copy(uint8_t *dest, target_phys_addr_t addr, size_t size)
+{
+ target_phys_addr_t end = addr + size;
+ uint8_t *s, *d = dest;
+ size_t l = 0;
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (rom->fw_file) {
+ continue;
+ }
+ if (rom->addr + rom->romsize < addr) {
+ continue;
+ }
+ if (rom->addr > end) {
+ break;
+ }
+ if (!rom->data) {
+ continue;
+ }
+
+ d = dest + (rom->addr - addr);
+ s = rom->data;
+ l = rom->romsize;
+
+ if ((d + l) > (dest + size)) {
+ l = dest - d;
+ }
+
+ memcpy(d, s, l);
+ }
+
+ return (d + l) - dest;
+}
+
+void *rom_ptr(target_phys_addr_t addr)
+{
+ Rom *rom;
+
+ rom = find_rom(addr);
+ if (!rom || !rom->data)
+ return NULL;
+ return rom->data + (addr - rom->addr);
+}
+
+void do_info_roms(Monitor *mon)
+{
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ if (!rom->fw_file) {
+ monitor_printf(mon, "addr=" TARGET_FMT_plx
+ " size=0x%06zx mem=%s name=\"%s\" \n",
+ rom->addr, rom->romsize,
+ rom->isrom ? "rom" : "ram",
+ rom->name);
+ } else {
+ monitor_printf(mon, "fw=%s/%s"
+ " size=0x%06zx name=\"%s\" \n",
+ rom->fw_dir,
+ rom->fw_file,
+ rom->romsize,
+ rom->name);
+ }
+ }
+}
diff --git a/hw/loader.h b/hw/loader.h
new file mode 100644
index 0000000..fc6bdff
--- /dev/null
+++ b/hw/loader.h
@@ -0,0 +1,48 @@
+#ifndef LOADER_H
+#define LOADER_H
+
+/* loader.c */
+int get_image_size(const char *filename);
+int load_image(const char *filename, uint8_t *addr); /* deprecated */
+int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz);
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+ uint64_t *highaddr, int big_endian, int elf_machine,
+ int clear_lsb);
+int load_aout(const char *filename, target_phys_addr_t addr, int max_sz,
+ int bswap_needed, target_phys_addr_t target_page_size);
+int load_uimage(const char *filename, target_phys_addr_t *ep,
+ target_phys_addr_t *loadaddr, int *is_linux);
+
+int read_targphys(const char *name,
+ int fd, target_phys_addr_t dst_addr, size_t nbytes);
+void pstrcpy_targphys(const char *name,
+ target_phys_addr_t dest, int buf_size,
+ const char *source);
+
+
+int rom_add_file(const char *file, const char *fw_dir,
+ target_phys_addr_t addr, int32_t bootindex);
+int rom_add_blob(const char *name, const void *blob, size_t len,
+ target_phys_addr_t addr);
+int rom_load_all(void);
+void rom_set_fw(void *f);
+int rom_copy(uint8_t *dest, target_phys_addr_t addr, size_t size);
+void *rom_ptr(target_phys_addr_t addr);
+void do_info_roms(Monitor *mon);
+
+#define rom_add_file_fixed(_f, _a, _i) \
+ rom_add_file(_f, NULL, _a, _i)
+#define rom_add_blob_fixed(_f, _b, _l, _a) \
+ rom_add_blob(_f, _b, _l, _a)
+
+#define PC_ROM_MIN_VGA 0xc0000
+#define PC_ROM_MIN_OPTION 0xc8000
+#define PC_ROM_MAX 0xe0000
+#define PC_ROM_ALIGN 0x800
+#define PC_ROM_SIZE (PC_ROM_MAX - PC_ROM_MIN_VGA)
+
+int rom_add_vga(const char *file);
+int rom_add_option(const char *file, int32_t bootindex);
+
+#endif
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
new file mode 100644
index 0000000..e4b51a8
--- /dev/null
+++ b/hw/lsi53c895a.c
@@ -0,0 +1,2276 @@
+/*
+ * QEMU LSI53C895A SCSI Host Bus Adapter emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+/* ??? Need to check if the {read,write}[wl] routines work properly on
+ big-endian targets. */
+
+#include <assert.h>
+
+#include "hw.h"
+#include "pci.h"
+#include "scsi.h"
+#include "block_int.h"
+
+//#define DEBUG_LSI
+//#define DEBUG_LSI_REG
+
+#ifdef DEBUG_LSI
+#define DPRINTF(fmt, ...) \
+do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define LSI_MAX_DEVS 7
+
+#define LSI_SCNTL0_TRG 0x01
+#define LSI_SCNTL0_AAP 0x02
+#define LSI_SCNTL0_EPC 0x08
+#define LSI_SCNTL0_WATN 0x10
+#define LSI_SCNTL0_START 0x20
+
+#define LSI_SCNTL1_SST 0x01
+#define LSI_SCNTL1_IARB 0x02
+#define LSI_SCNTL1_AESP 0x04
+#define LSI_SCNTL1_RST 0x08
+#define LSI_SCNTL1_CON 0x10
+#define LSI_SCNTL1_DHP 0x20
+#define LSI_SCNTL1_ADB 0x40
+#define LSI_SCNTL1_EXC 0x80
+
+#define LSI_SCNTL2_WSR 0x01
+#define LSI_SCNTL2_VUE0 0x02
+#define LSI_SCNTL2_VUE1 0x04
+#define LSI_SCNTL2_WSS 0x08
+#define LSI_SCNTL2_SLPHBEN 0x10
+#define LSI_SCNTL2_SLPMD 0x20
+#define LSI_SCNTL2_CHM 0x40
+#define LSI_SCNTL2_SDU 0x80
+
+#define LSI_ISTAT0_DIP 0x01
+#define LSI_ISTAT0_SIP 0x02
+#define LSI_ISTAT0_INTF 0x04
+#define LSI_ISTAT0_CON 0x08
+#define LSI_ISTAT0_SEM 0x10
+#define LSI_ISTAT0_SIGP 0x20
+#define LSI_ISTAT0_SRST 0x40
+#define LSI_ISTAT0_ABRT 0x80
+
+#define LSI_ISTAT1_SI 0x01
+#define LSI_ISTAT1_SRUN 0x02
+#define LSI_ISTAT1_FLSH 0x04
+
+#define LSI_SSTAT0_SDP0 0x01
+#define LSI_SSTAT0_RST 0x02
+#define LSI_SSTAT0_WOA 0x04
+#define LSI_SSTAT0_LOA 0x08
+#define LSI_SSTAT0_AIP 0x10
+#define LSI_SSTAT0_OLF 0x20
+#define LSI_SSTAT0_ORF 0x40
+#define LSI_SSTAT0_ILF 0x80
+
+#define LSI_SIST0_PAR 0x01
+#define LSI_SIST0_RST 0x02
+#define LSI_SIST0_UDC 0x04
+#define LSI_SIST0_SGE 0x08
+#define LSI_SIST0_RSL 0x10
+#define LSI_SIST0_SEL 0x20
+#define LSI_SIST0_CMP 0x40
+#define LSI_SIST0_MA 0x80
+
+#define LSI_SIST1_HTH 0x01
+#define LSI_SIST1_GEN 0x02
+#define LSI_SIST1_STO 0x04
+#define LSI_SIST1_SBMC 0x10
+
+#define LSI_SOCL_IO 0x01
+#define LSI_SOCL_CD 0x02
+#define LSI_SOCL_MSG 0x04
+#define LSI_SOCL_ATN 0x08
+#define LSI_SOCL_SEL 0x10
+#define LSI_SOCL_BSY 0x20
+#define LSI_SOCL_ACK 0x40
+#define LSI_SOCL_REQ 0x80
+
+#define LSI_DSTAT_IID 0x01
+#define LSI_DSTAT_SIR 0x04
+#define LSI_DSTAT_SSI 0x08
+#define LSI_DSTAT_ABRT 0x10
+#define LSI_DSTAT_BF 0x20
+#define LSI_DSTAT_MDPE 0x40
+#define LSI_DSTAT_DFE 0x80
+
+#define LSI_DCNTL_COM 0x01
+#define LSI_DCNTL_IRQD 0x02
+#define LSI_DCNTL_STD 0x04
+#define LSI_DCNTL_IRQM 0x08
+#define LSI_DCNTL_SSM 0x10
+#define LSI_DCNTL_PFEN 0x20
+#define LSI_DCNTL_PFF 0x40
+#define LSI_DCNTL_CLSE 0x80
+
+#define LSI_DMODE_MAN 0x01
+#define LSI_DMODE_BOF 0x02
+#define LSI_DMODE_ERMP 0x04
+#define LSI_DMODE_ERL 0x08
+#define LSI_DMODE_DIOM 0x10
+#define LSI_DMODE_SIOM 0x20
+
+#define LSI_CTEST2_DACK 0x01
+#define LSI_CTEST2_DREQ 0x02
+#define LSI_CTEST2_TEOP 0x04
+#define LSI_CTEST2_PCICIE 0x08
+#define LSI_CTEST2_CM 0x10
+#define LSI_CTEST2_CIO 0x20
+#define LSI_CTEST2_SIGP 0x40
+#define LSI_CTEST2_DDIR 0x80
+
+#define LSI_CTEST5_BL2 0x04
+#define LSI_CTEST5_DDIR 0x08
+#define LSI_CTEST5_MASR 0x10
+#define LSI_CTEST5_DFSN 0x20
+#define LSI_CTEST5_BBCK 0x40
+#define LSI_CTEST5_ADCK 0x80
+
+#define LSI_CCNTL0_DILS 0x01
+#define LSI_CCNTL0_DISFC 0x10
+#define LSI_CCNTL0_ENNDJ 0x20
+#define LSI_CCNTL0_PMJCTL 0x40
+#define LSI_CCNTL0_ENPMJ 0x80
+
+#define LSI_CCNTL1_EN64DBMV 0x01
+#define LSI_CCNTL1_EN64TIBMV 0x02
+#define LSI_CCNTL1_64TIMOD 0x04
+#define LSI_CCNTL1_DDAC 0x08
+#define LSI_CCNTL1_ZMOD 0x80
+
+/* Enable Response to Reselection */
+#define LSI_SCID_RRE 0x60
+
+#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD)
+
+#define PHASE_DO 0
+#define PHASE_DI 1
+#define PHASE_CMD 2
+#define PHASE_ST 3
+#define PHASE_MO 6
+#define PHASE_MI 7
+#define PHASE_MASK 7
+
+/* Maximum length of MSG IN data. */
+#define LSI_MAX_MSGIN_LEN 8
+
+/* Flag set if this is a tagged command. */
+#define LSI_TAG_VALID (1 << 16)
+
+typedef struct lsi_request {
+ uint32_t tag;
+ uint32_t dma_len;
+ uint8_t *dma_buf;
+ uint32_t pending;
+ int out;
+ QTAILQ_ENTRY(lsi_request) next;
+} lsi_request;
+
+typedef struct {
+ PCIDevice dev;
+ int mmio_io_addr;
+ int ram_io_addr;
+ uint32_t script_ram_base;
+
+ int carry; /* ??? Should this be an a visible register somewhere? */
+ int sense;
+ /* Action to take at the end of a MSG IN phase.
+ 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */
+ int msg_action;
+ int msg_len;
+ uint8_t msg[LSI_MAX_MSGIN_LEN];
+ /* 0 if SCRIPTS are running or stopped.
+ * 1 if a Wait Reselect instruction has been issued.
+ * 2 if processing DMA from lsi_execute_script.
+ * 3 if a DMA operation is in progress. */
+ int waiting;
+ SCSIBus bus;
+ int current_lun;
+ /* The tag is a combination of the device ID and the SCSI tag. */
+ uint32_t select_tag;
+ int command_complete;
+ QTAILQ_HEAD(, lsi_request) queue;
+ lsi_request *current;
+
+ uint32_t dsa;
+ uint32_t temp;
+ uint32_t dnad;
+ uint32_t dbc;
+ uint8_t istat0;
+ uint8_t istat1;
+ uint8_t dcmd;
+ uint8_t dstat;
+ uint8_t dien;
+ uint8_t sist0;
+ uint8_t sist1;
+ uint8_t sien0;
+ uint8_t sien1;
+ uint8_t mbox0;
+ uint8_t mbox1;
+ uint8_t dfifo;
+ uint8_t ctest2;
+ uint8_t ctest3;
+ uint8_t ctest4;
+ uint8_t ctest5;
+ uint8_t ccntl0;
+ uint8_t ccntl1;
+ uint32_t dsp;
+ uint32_t dsps;
+ uint8_t dmode;
+ uint8_t dcntl;
+ uint8_t scntl0;
+ uint8_t scntl1;
+ uint8_t scntl2;
+ uint8_t scntl3;
+ uint8_t sstat0;
+ uint8_t sstat1;
+ uint8_t scid;
+ uint8_t sxfer;
+ uint8_t socl;
+ uint8_t sdid;
+ uint8_t ssid;
+ uint8_t sfbr;
+ uint8_t stest1;
+ uint8_t stest2;
+ uint8_t stest3;
+ uint8_t sidl;
+ uint8_t stime0;
+ uint8_t respid0;
+ uint8_t respid1;
+ uint32_t mmrs;
+ uint32_t mmws;
+ uint32_t sfs;
+ uint32_t drs;
+ uint32_t sbms;
+ uint32_t dbms;
+ uint32_t dnad64;
+ uint32_t pmjad1;
+ uint32_t pmjad2;
+ uint32_t rbc;
+ uint32_t ua;
+ uint32_t ia;
+ uint32_t sbc;
+ uint32_t csbc;
+ uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */
+ uint8_t sbr;
+
+ /* Script ram is stored as 32-bit words in host byteorder. */
+ uint32_t script_ram[2048];
+} LSIState;
+
+static inline int lsi_irq_on_rsl(LSIState *s)
+{
+ return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE);
+}
+
+static void lsi_soft_reset(LSIState *s)
+{
+ lsi_request *p;
+
+ DPRINTF("Reset\n");
+ s->carry = 0;
+
+ s->msg_action = 0;
+ s->msg_len = 0;
+ s->waiting = 0;
+ s->dsa = 0;
+ s->dnad = 0;
+ s->dbc = 0;
+ s->temp = 0;
+ memset(s->scratch, 0, sizeof(s->scratch));
+ s->istat0 = 0;
+ s->istat1 = 0;
+ s->dcmd = 0x40;
+ s->dstat = LSI_DSTAT_DFE;
+ s->dien = 0;
+ s->sist0 = 0;
+ s->sist1 = 0;
+ s->sien0 = 0;
+ s->sien1 = 0;
+ s->mbox0 = 0;
+ s->mbox1 = 0;
+ s->dfifo = 0;
+ s->ctest2 = LSI_CTEST2_DACK;
+ s->ctest3 = 0;
+ s->ctest4 = 0;
+ s->ctest5 = 0;
+ s->ccntl0 = 0;
+ s->ccntl1 = 0;
+ s->dsp = 0;
+ s->dsps = 0;
+ s->dmode = 0;
+ s->dcntl = 0;
+ s->scntl0 = 0xc0;
+ s->scntl1 = 0;
+ s->scntl2 = 0;
+ s->scntl3 = 0;
+ s->sstat0 = 0;
+ s->sstat1 = 0;
+ s->scid = 7;
+ s->sxfer = 0;
+ s->socl = 0;
+ s->sdid = 0;
+ s->ssid = 0;
+ s->stest1 = 0;
+ s->stest2 = 0;
+ s->stest3 = 0;
+ s->sidl = 0;
+ s->stime0 = 0;
+ s->respid0 = 0x80;
+ s->respid1 = 0;
+ s->mmrs = 0;
+ s->mmws = 0;
+ s->sfs = 0;
+ s->drs = 0;
+ s->sbms = 0;
+ s->dbms = 0;
+ s->dnad64 = 0;
+ s->pmjad1 = 0;
+ s->pmjad2 = 0;
+ s->rbc = 0;
+ s->ua = 0;
+ s->ia = 0;
+ s->sbc = 0;
+ s->csbc = 0;
+ s->sbr = 0;
+ while (!QTAILQ_EMPTY(&s->queue)) {
+ p = QTAILQ_FIRST(&s->queue);
+ QTAILQ_REMOVE(&s->queue, p, next);
+ qemu_free(p);
+ }
+ if (s->current) {
+ qemu_free(s->current);
+ s->current = NULL;
+ }
+}
+
+static int lsi_dma_40bit(LSIState *s)
+{
+ if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT)
+ return 1;
+ return 0;
+}
+
+static int lsi_dma_ti64bit(LSIState *s)
+{
+ if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV)
+ return 1;
+ return 0;
+}
+
+static int lsi_dma_64bit(LSIState *s)
+{
+ if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV)
+ return 1;
+ return 0;
+}
+
+static uint8_t lsi_reg_readb(LSIState *s, int offset);
+static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val);
+static void lsi_execute_script(LSIState *s);
+static void lsi_reselect(LSIState *s, lsi_request *p);
+
+static inline uint32_t read_dword(LSIState *s, uint32_t addr)
+{
+ uint32_t buf;
+
+ /* Optimize reading from SCRIPTS RAM. */
+ if ((addr & 0xffffe000) == s->script_ram_base) {
+ return s->script_ram[(addr & 0x1fff) >> 2];
+ }
+ cpu_physical_memory_read(addr, (uint8_t *)&buf, 4);
+ return cpu_to_le32(buf);
+}
+
+static void lsi_stop_script(LSIState *s)
+{
+ s->istat1 &= ~LSI_ISTAT1_SRUN;
+}
+
+static void lsi_update_irq(LSIState *s)
+{
+ int level;
+ static int last_level;
+ lsi_request *p;
+
+ /* It's unclear whether the DIP/SIP bits should be cleared when the
+ Interrupt Status Registers are cleared or when istat0 is read.
+ We currently do the formwer, which seems to work. */
+ level = 0;
+ if (s->dstat) {
+ if (s->dstat & s->dien)
+ level = 1;
+ s->istat0 |= LSI_ISTAT0_DIP;
+ } else {
+ s->istat0 &= ~LSI_ISTAT0_DIP;
+ }
+
+ if (s->sist0 || s->sist1) {
+ if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1))
+ level = 1;
+ s->istat0 |= LSI_ISTAT0_SIP;
+ } else {
+ s->istat0 &= ~LSI_ISTAT0_SIP;
+ }
+ if (s->istat0 & LSI_ISTAT0_INTF)
+ level = 1;
+
+ if (level != last_level) {
+ DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n",
+ level, s->dstat, s->sist1, s->sist0);
+ last_level = level;
+ }
+ qemu_set_irq(s->dev.irq[0], level);
+
+ if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) {
+ DPRINTF("Handled IRQs & disconnected, looking for pending "
+ "processes\n");
+ QTAILQ_FOREACH(p, &s->queue, next) {
+ if (p->pending) {
+ lsi_reselect(s, p);
+ break;
+ }
+ }
+ }
+}
+
+/* Stop SCRIPTS execution and raise a SCSI interrupt. */
+static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
+{
+ uint32_t mask0;
+ uint32_t mask1;
+
+ DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n",
+ stat1, stat0, s->sist1, s->sist0);
+ s->sist0 |= stat0;
+ s->sist1 |= stat1;
+ /* Stop processor on fatal or unmasked interrupt. As a special hack
+ we don't stop processing when raising STO. Instead continue
+ execution and stop at the next insn that accesses the SCSI bus. */
+ mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL);
+ mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH);
+ mask1 &= ~LSI_SIST1_STO;
+ if (s->sist0 & mask0 || s->sist1 & mask1) {
+ lsi_stop_script(s);
+ }
+ lsi_update_irq(s);
+}
+
+/* Stop SCRIPTS execution and raise a DMA interrupt. */
+static void lsi_script_dma_interrupt(LSIState *s, int stat)
+{
+ DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat);
+ s->dstat |= stat;
+ lsi_update_irq(s);
+ lsi_stop_script(s);
+}
+
+static inline void lsi_set_phase(LSIState *s, int phase)
+{
+ s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase;
+}
+
+static void lsi_bad_phase(LSIState *s, int out, int new_phase)
+{
+ /* Trigger a phase mismatch. */
+ if (s->ccntl0 & LSI_CCNTL0_ENPMJ) {
+ if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) {
+ s->dsp = out ? s->pmjad1 : s->pmjad2;
+ } else {
+ s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1);
+ }
+ DPRINTF("Data phase mismatch jump to %08x\n", s->dsp);
+ } else {
+ DPRINTF("Phase mismatch interrupt\n");
+ lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
+ lsi_stop_script(s);
+ }
+ lsi_set_phase(s, new_phase);
+}
+
+
+/* Resume SCRIPTS execution after a DMA operation. */
+static void lsi_resume_script(LSIState *s)
+{
+ if (s->waiting != 2) {
+ s->waiting = 0;
+ lsi_execute_script(s);
+ } else {
+ s->waiting = 0;
+ }
+}
+
+static void lsi_disconnect(LSIState *s)
+{
+ s->scntl1 &= ~LSI_SCNTL1_CON;
+ s->sstat1 &= ~PHASE_MASK;
+}
+
+static void lsi_bad_selection(LSIState *s, uint32_t id)
+{
+ DPRINTF("Selected absent target %d\n", id);
+ lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
+ lsi_disconnect(s);
+}
+
+/* Initiate a SCSI layer data transfer. */
+static void lsi_do_dma(LSIState *s, int out)
+{
+ uint32_t count, id;
+ target_phys_addr_t addr;
+ SCSIDevice *dev;
+
+ assert(s->current);
+ if (!s->current->dma_len) {
+ /* Wait until data is available. */
+ DPRINTF("DMA no data available\n");
+ return;
+ }
+
+ id = (s->current->tag >> 8) & 0xf;
+ dev = s->bus.devs[id];
+ if (!dev) {
+ lsi_bad_selection(s, id);
+ return;
+ }
+
+ count = s->dbc;
+ if (count > s->current->dma_len)
+ count = s->current->dma_len;
+
+ addr = s->dnad;
+ /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */
+ if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s))
+ addr |= ((uint64_t)s->dnad64 << 32);
+ else if (s->dbms)
+ addr |= ((uint64_t)s->dbms << 32);
+ else if (s->sbms)
+ addr |= ((uint64_t)s->sbms << 32);
+
+ DPRINTF("DMA addr=0x" TARGET_FMT_plx " len=%d\n", addr, count);
+ 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);
+ }
+
+ /* ??? Set SFBR to first data byte. */
+ if (out) {
+ cpu_physical_memory_read(addr, s->current->dma_buf, count);
+ } else {
+ cpu_physical_memory_write(addr, s->current->dma_buf, count);
+ }
+ 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(dev, s->current->tag);
+ } else {
+ /* Request any remaining data. */
+ dev->info->read_data(dev, s->current->tag);
+ }
+ } else {
+ s->current->dma_buf += count;
+ lsi_resume_script(s);
+ }
+}
+
+
+/* Add a command to the queue. */
+static void lsi_queue_command(LSIState *s)
+{
+ lsi_request *p = s->current;
+
+ DPRINTF("Queueing tag=0x%x\n", p->tag);
+ assert(s->current != NULL);
+ assert(s->current->dma_len == 0);
+ QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
+ s->current = NULL;
+
+ p->pending = 0;
+ p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+}
+
+/* Queue a byte for a MSG IN phase. */
+static void lsi_add_msg_byte(LSIState *s, uint8_t data)
+{
+ if (s->msg_len >= LSI_MAX_MSGIN_LEN) {
+ BADF("MSG IN data too long\n");
+ } else {
+ DPRINTF("MSG IN 0x%02x\n", data);
+ s->msg[s->msg_len++] = data;
+ }
+}
+
+/* Perform reselection to continue a command. */
+static void lsi_reselect(LSIState *s, lsi_request *p)
+{
+ int id;
+
+ assert(s->current == NULL);
+ QTAILQ_REMOVE(&s->queue, p, next);
+ s->current = p;
+
+ id = (p->tag >> 8) & 0xf;
+ s->ssid = id | 0x80;
+ /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */
+ if (!(s->dcntl & LSI_DCNTL_COM)) {
+ s->sfbr = 1 << (id & 0x7);
+ }
+ DPRINTF("Reselected target %d\n", id);
+ s->scntl1 |= LSI_SCNTL1_CON;
+ lsi_set_phase(s, PHASE_MI);
+ s->msg_action = p->out ? 2 : 3;
+ s->current->dma_len = p->pending;
+ lsi_add_msg_byte(s, 0x80);
+ if (s->current->tag & LSI_TAG_VALID) {
+ lsi_add_msg_byte(s, 0x20);
+ lsi_add_msg_byte(s, p->tag & 0xff);
+ }
+
+ if (lsi_irq_on_rsl(s)) {
+ lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0);
+ }
+}
+
+/* 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;
+
+ 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;
+ }
+ }
+ }
+ BADF("IO with unknown tag %d\n", tag);
+ 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)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
+ int out;
+
+ out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+ if (reason == SCSI_REASON_DONE) {
+ DPRINTF("Command complete sense=%d\n", (int)arg);
+ s->sense = 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);
+ }
+
+ qemu_free(s->current);
+ s->current = NULL;
+
+ lsi_resume_script(s);
+ return;
+ }
+
+ if (s->waiting == 1 || !s->current || tag != s->current->tag ||
+ (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
+ if (lsi_queue_tag(s, tag, arg))
+ return;
+ }
+
+ /* host adapter (re)connected */
+ DPRINTF("Data ready tag=0x%x len=%d\n", 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);
+ }
+}
+
+static void lsi_do_command(LSIState *s)
+{
+ SCSIDevice *dev;
+ uint8_t buf[16];
+ uint32_t id;
+ int n;
+
+ DPRINTF("Send command len=%d\n", s->dbc);
+ if (s->dbc > 16)
+ s->dbc = 16;
+ cpu_physical_memory_read(s->dnad, buf, s->dbc);
+ s->sfbr = buf[0];
+ s->command_complete = 0;
+
+ id = (s->select_tag >> 8) & 0xf;
+ dev = s->bus.devs[id];
+ if (!dev) {
+ lsi_bad_selection(s, id);
+ return;
+ }
+
+ assert(s->current == NULL);
+ s->current = qemu_mallocz(sizeof(lsi_request));
+ s->current->tag = s->select_tag;
+
+ n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun);
+ if (n > 0) {
+ lsi_set_phase(s, PHASE_DI);
+ dev->info->read_data(dev, s->current->tag);
+ } else if (n < 0) {
+ lsi_set_phase(s, PHASE_DO);
+ dev->info->write_data(dev, s->current->tag);
+ }
+
+ if (!s->command_complete) {
+ if (n) {
+ /* Command did not complete immediately so disconnect. */
+ lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */
+ lsi_add_msg_byte(s, 4); /* DISCONNECT */
+ /* wait data */
+ lsi_set_phase(s, PHASE_MI);
+ s->msg_action = 1;
+ lsi_queue_command(s);
+ } else {
+ /* wait command complete */
+ lsi_set_phase(s, PHASE_DI);
+ }
+ }
+}
+
+static void lsi_do_status(LSIState *s)
+{
+ uint8_t sense;
+ DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense);
+ if (s->dbc != 1)
+ BADF("Bad Status move\n");
+ s->dbc = 1;
+ sense = s->sense;
+ s->sfbr = sense;
+ cpu_physical_memory_write(s->dnad, &sense, 1);
+ lsi_set_phase(s, PHASE_MI);
+ s->msg_action = 1;
+ lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */
+}
+
+static void lsi_do_msgin(LSIState *s)
+{
+ int len;
+ DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len);
+ s->sfbr = s->msg[0];
+ len = s->msg_len;
+ if (len > s->dbc)
+ len = s->dbc;
+ cpu_physical_memory_write(s->dnad, s->msg, len);
+ /* Linux drivers rely on the last byte being in the SIDL. */
+ s->sidl = s->msg[len - 1];
+ s->msg_len -= len;
+ if (s->msg_len) {
+ memmove(s->msg, s->msg + len, s->msg_len);
+ } else {
+ /* ??? Check if ATN (not yet implemented) is asserted and maybe
+ switch to PHASE_MO. */
+ switch (s->msg_action) {
+ case 0:
+ lsi_set_phase(s, PHASE_CMD);
+ break;
+ case 1:
+ lsi_disconnect(s);
+ break;
+ case 2:
+ lsi_set_phase(s, PHASE_DO);
+ break;
+ case 3:
+ lsi_set_phase(s, PHASE_DI);
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
+/* Read the next byte during a MSGOUT phase. */
+static uint8_t lsi_get_msgbyte(LSIState *s)
+{
+ uint8_t data;
+ cpu_physical_memory_read(s->dnad, &data, 1);
+ s->dnad++;
+ s->dbc--;
+ return data;
+}
+
+/* Skip the next n bytes during a MSGOUT phase. */
+static void lsi_skip_msgbytes(LSIState *s, unsigned int n)
+{
+ s->dnad += n;
+ s->dbc -= n;
+}
+
+static void lsi_do_msgout(LSIState *s)
+{
+ uint8_t msg;
+ int len;
+ uint32_t current_tag;
+ SCSIDevice *current_dev;
+ lsi_request *p, *p_next;
+ int id;
+
+ if (s->current) {
+ current_tag = s->current->tag;
+ } else {
+ current_tag = s->select_tag;
+ }
+ id = (current_tag >> 8) & 0xf;
+ current_dev = s->bus.devs[id];
+
+ DPRINTF("MSG out len=%d\n", s->dbc);
+ while (s->dbc) {
+ msg = lsi_get_msgbyte(s);
+ s->sfbr = msg;
+
+ switch (msg) {
+ case 0x04:
+ DPRINTF("MSG: Disconnect\n");
+ lsi_disconnect(s);
+ break;
+ case 0x08:
+ DPRINTF("MSG: No Operation\n");
+ lsi_set_phase(s, PHASE_CMD);
+ break;
+ case 0x01:
+ len = lsi_get_msgbyte(s);
+ msg = lsi_get_msgbyte(s);
+ (void)len; /* avoid a warning about unused variable*/
+ DPRINTF("Extended message 0x%x (len %d)\n", msg, len);
+ switch (msg) {
+ case 1:
+ DPRINTF("SDTR (ignored)\n");
+ lsi_skip_msgbytes(s, 2);
+ break;
+ case 3:
+ DPRINTF("WDTR (ignored)\n");
+ lsi_skip_msgbytes(s, 1);
+ break;
+ default:
+ goto bad;
+ }
+ break;
+ case 0x20: /* SIMPLE queue */
+ s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+ DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff);
+ break;
+ case 0x21: /* HEAD of queue */
+ BADF("HEAD queue not implemented\n");
+ s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+ break;
+ case 0x22: /* ORDERED queue */
+ BADF("ORDERED queue not implemented\n");
+ s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+ break;
+ 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);
+ lsi_disconnect(s);
+ break;
+ case 0x06:
+ case 0x0e:
+ case 0x0c:
+ /* The ABORT message clears all I/O processes for the selecting
+ initiator on the specified logical unit of the target. */
+ if (msg == 0x06) {
+ DPRINTF("MSG: ABORT tag=0x%x\n", current_tag);
+ }
+ /* The CLEAR QUEUE message clears all I/O processes for all
+ initiators on the specified logical unit of the target. */
+ if (msg == 0x0e) {
+ DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag);
+ }
+ /* The BUS DEVICE RESET message clears all I/O processes for all
+ initiators on all logical units of the target. */
+ if (msg == 0x0c) {
+ DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag);
+ }
+
+ /* clear the current I/O process */
+ current_dev->info->cancel_io(current_dev, current_tag);
+
+ /* As the current implemented devices scsi_disk and scsi_generic
+ only support one LUN, we don't need to keep track of LUNs.
+ Clearing I/O processes for other initiators could be possible
+ for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX
+ device, but this is currently not implemented (and seems not
+ to be really necessary). So let's simply clear all queued
+ commands for the current device: */
+ 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);
+ QTAILQ_REMOVE(&s->queue, p, next);
+ }
+ }
+
+ lsi_disconnect(s);
+ break;
+ default:
+ if ((msg & 0x80) == 0) {
+ goto bad;
+ }
+ s->current_lun = msg & 7;
+ DPRINTF("Select LUN %d\n", s->current_lun);
+ lsi_set_phase(s, PHASE_CMD);
+ break;
+ }
+ }
+ return;
+bad:
+ BADF("Unimplemented message 0x%02x\n", msg);
+ lsi_set_phase(s, PHASE_MI);
+ lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */
+ s->msg_action = 0;
+}
+
+/* Sign extend a 24-bit value. */
+static inline int32_t sxt24(int32_t n)
+{
+ return (n << 8) >> 8;
+}
+
+#define LSI_BUF_SIZE 4096
+static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
+{
+ int n;
+ uint8_t buf[LSI_BUF_SIZE];
+
+ DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count);
+ while (count) {
+ n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count;
+ cpu_physical_memory_read(src, buf, n);
+ cpu_physical_memory_write(dest, buf, n);
+ src += n;
+ dest += n;
+ count -= n;
+ }
+}
+
+static void lsi_wait_reselect(LSIState *s)
+{
+ lsi_request *p;
+
+ DPRINTF("Wait Reselect\n");
+
+ QTAILQ_FOREACH(p, &s->queue, next) {
+ if (p->pending) {
+ lsi_reselect(s, p);
+ break;
+ }
+ }
+ if (s->current == NULL) {
+ s->waiting = 1;
+ }
+}
+
+static void lsi_execute_script(LSIState *s)
+{
+ uint32_t insn;
+ uint32_t addr, addr_high;
+ int opcode;
+ int insn_processed = 0;
+
+ s->istat1 |= LSI_ISTAT1_SRUN;
+again:
+ insn_processed++;
+ insn = read_dword(s, s->dsp);
+ if (!insn) {
+ /* If we receive an empty opcode increment the DSP by 4 bytes
+ instead of 8 and execute the next opcode at that location */
+ s->dsp += 4;
+ goto again;
+ }
+ addr = read_dword(s, s->dsp + 4);
+ addr_high = 0;
+ DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr);
+ s->dsps = addr;
+ s->dcmd = insn >> 24;
+ s->dsp += 8;
+ switch (insn >> 30) {
+ case 0: /* Block move. */
+ if (s->sist1 & LSI_SIST1_STO) {
+ DPRINTF("Delayed select timeout\n");
+ lsi_stop_script(s);
+ break;
+ }
+ s->dbc = insn & 0xffffff;
+ s->rbc = s->dbc;
+ /* ??? Set ESA. */
+ s->ia = s->dsp - 8;
+ if (insn & (1 << 29)) {
+ /* Indirect addressing. */
+ addr = read_dword(s, addr);
+ } else if (insn & (1 << 28)) {
+ uint32_t buf[2];
+ int32_t offset;
+ /* Table indirect addressing. */
+
+ /* 32-bit Table indirect */
+ offset = sxt24(addr);
+ cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8);
+ /* byte count is stored in bits 0:23 only */
+ s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
+ s->rbc = s->dbc;
+ addr = cpu_to_le32(buf[1]);
+
+ /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of
+ * table, bits [31:24] */
+ if (lsi_dma_40bit(s))
+ addr_high = cpu_to_le32(buf[0]) >> 24;
+ else if (lsi_dma_ti64bit(s)) {
+ int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f;
+ switch (selector) {
+ case 0 ... 0x0f:
+ /* offset index into scratch registers since
+ * TI64 mode can use registers C to R */
+ addr_high = s->scratch[2 + selector];
+ break;
+ case 0x10:
+ addr_high = s->mmrs;
+ break;
+ case 0x11:
+ addr_high = s->mmws;
+ break;
+ case 0x12:
+ addr_high = s->sfs;
+ break;
+ case 0x13:
+ addr_high = s->drs;
+ break;
+ case 0x14:
+ addr_high = s->sbms;
+ break;
+ case 0x15:
+ addr_high = s->dbms;
+ break;
+ default:
+ BADF("Illegal selector specified (0x%x > 0x15)"
+ " for 64-bit DMA block move", selector);
+ break;
+ }
+ }
+ } else if (lsi_dma_64bit(s)) {
+ /* fetch a 3rd dword if 64-bit direct move is enabled and
+ only if we're not doing table indirect or indirect addressing */
+ s->dbms = read_dword(s, s->dsp);
+ s->dsp += 4;
+ s->ia = s->dsp - 12;
+ }
+ if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) {
+ DPRINTF("Wrong phase got %d expected %d\n",
+ s->sstat1 & PHASE_MASK, (insn >> 24) & 7);
+ lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
+ break;
+ }
+ s->dnad = addr;
+ s->dnad64 = addr_high;
+ switch (s->sstat1 & 0x7) {
+ case PHASE_DO:
+ s->waiting = 2;
+ lsi_do_dma(s, 1);
+ if (s->waiting)
+ s->waiting = 3;
+ break;
+ case PHASE_DI:
+ s->waiting = 2;
+ lsi_do_dma(s, 0);
+ if (s->waiting)
+ s->waiting = 3;
+ break;
+ case PHASE_CMD:
+ lsi_do_command(s);
+ break;
+ case PHASE_ST:
+ lsi_do_status(s);
+ break;
+ case PHASE_MO:
+ lsi_do_msgout(s);
+ break;
+ case PHASE_MI:
+ lsi_do_msgin(s);
+ break;
+ default:
+ BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK);
+ exit(1);
+ }
+ s->dfifo = s->dbc & 0xff;
+ s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3);
+ s->sbc = s->dbc;
+ s->rbc -= s->dbc;
+ s->ua = addr + s->dbc;
+ break;
+
+ case 1: /* IO or Read/Write instruction. */
+ opcode = (insn >> 27) & 7;
+ if (opcode < 5) {
+ uint32_t id;
+
+ if (insn & (1 << 25)) {
+ id = read_dword(s, s->dsa + sxt24(insn));
+ } else {
+ id = insn;
+ }
+ id = (id >> 16) & 0xf;
+ if (insn & (1 << 26)) {
+ addr = s->dsp + sxt24(addr);
+ }
+ s->dnad = addr;
+ switch (opcode) {
+ case 0: /* Select */
+ s->sdid = id;
+ if (s->scntl1 & LSI_SCNTL1_CON) {
+ DPRINTF("Already reselected, jumping to alternative address\n");
+ s->dsp = s->dnad;
+ break;
+ }
+ s->sstat0 |= LSI_SSTAT0_WOA;
+ s->scntl1 &= ~LSI_SCNTL1_IARB;
+ if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) {
+ lsi_bad_selection(s, id);
+ break;
+ }
+ DPRINTF("Selected target %d%s\n",
+ id, insn & (1 << 3) ? " ATN" : "");
+ /* ??? Linux drivers compain when this is set. Maybe
+ it only applies in low-level mode (unimplemented).
+ lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
+ s->select_tag = id << 8;
+ s->scntl1 |= LSI_SCNTL1_CON;
+ if (insn & (1 << 3)) {
+ s->socl |= LSI_SOCL_ATN;
+ }
+ lsi_set_phase(s, PHASE_MO);
+ break;
+ case 1: /* Disconnect */
+ DPRINTF("Wait Disconnect\n");
+ s->scntl1 &= ~LSI_SCNTL1_CON;
+ break;
+ case 2: /* Wait Reselect */
+ if (!lsi_irq_on_rsl(s)) {
+ lsi_wait_reselect(s);
+ }
+ break;
+ case 3: /* Set */
+ DPRINTF("Set%s%s%s%s\n",
+ insn & (1 << 3) ? " ATN" : "",
+ insn & (1 << 6) ? " ACK" : "",
+ insn & (1 << 9) ? " TM" : "",
+ insn & (1 << 10) ? " CC" : "");
+ if (insn & (1 << 3)) {
+ s->socl |= LSI_SOCL_ATN;
+ lsi_set_phase(s, PHASE_MO);
+ }
+ if (insn & (1 << 9)) {
+ BADF("Target mode not implemented\n");
+ exit(1);
+ }
+ if (insn & (1 << 10))
+ s->carry = 1;
+ break;
+ case 4: /* Clear */
+ DPRINTF("Clear%s%s%s%s\n",
+ insn & (1 << 3) ? " ATN" : "",
+ insn & (1 << 6) ? " ACK" : "",
+ insn & (1 << 9) ? " TM" : "",
+ insn & (1 << 10) ? " CC" : "");
+ if (insn & (1 << 3)) {
+ s->socl &= ~LSI_SOCL_ATN;
+ }
+ if (insn & (1 << 10))
+ s->carry = 0;
+ break;
+ }
+ } else {
+ uint8_t op0;
+ uint8_t op1;
+ uint8_t data8;
+ int reg;
+ int operator;
+#ifdef DEBUG_LSI
+ static const char *opcode_names[3] =
+ {"Write", "Read", "Read-Modify-Write"};
+ static const char *operator_names[8] =
+ {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"};
+#endif
+
+ reg = ((insn >> 16) & 0x7f) | (insn & 0x80);
+ data8 = (insn >> 8) & 0xff;
+ opcode = (insn >> 27) & 7;
+ operator = (insn >> 24) & 7;
+ DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n",
+ opcode_names[opcode - 5], reg,
+ operator_names[operator], data8, s->sfbr,
+ (insn & (1 << 23)) ? " SFBR" : "");
+ op0 = op1 = 0;
+ switch (opcode) {
+ case 5: /* From SFBR */
+ op0 = s->sfbr;
+ op1 = data8;
+ break;
+ case 6: /* To SFBR */
+ if (operator)
+ op0 = lsi_reg_readb(s, reg);
+ op1 = data8;
+ break;
+ case 7: /* Read-modify-write */
+ if (operator)
+ op0 = lsi_reg_readb(s, reg);
+ if (insn & (1 << 23)) {
+ op1 = s->sfbr;
+ } else {
+ op1 = data8;
+ }
+ break;
+ }
+
+ switch (operator) {
+ case 0: /* move */
+ op0 = op1;
+ break;
+ case 1: /* Shift left */
+ op1 = op0 >> 7;
+ op0 = (op0 << 1) | s->carry;
+ s->carry = op1;
+ break;
+ case 2: /* OR */
+ op0 |= op1;
+ break;
+ case 3: /* XOR */
+ op0 ^= op1;
+ break;
+ case 4: /* AND */
+ op0 &= op1;
+ break;
+ case 5: /* SHR */
+ op1 = op0 & 1;
+ op0 = (op0 >> 1) | (s->carry << 7);
+ s->carry = op1;
+ break;
+ case 6: /* ADD */
+ op0 += op1;
+ s->carry = op0 < op1;
+ break;
+ case 7: /* ADC */
+ op0 += op1 + s->carry;
+ if (s->carry)
+ s->carry = op0 <= op1;
+ else
+ s->carry = op0 < op1;
+ break;
+ }
+
+ switch (opcode) {
+ case 5: /* From SFBR */
+ case 7: /* Read-modify-write */
+ lsi_reg_writeb(s, reg, op0);
+ break;
+ case 6: /* To SFBR */
+ s->sfbr = op0;
+ break;
+ }
+ }
+ break;
+
+ case 2: /* Transfer Control. */
+ {
+ int cond;
+ int jmp;
+
+ if ((insn & 0x002e0000) == 0) {
+ DPRINTF("NOP\n");
+ break;
+ }
+ if (s->sist1 & LSI_SIST1_STO) {
+ DPRINTF("Delayed select timeout\n");
+ lsi_stop_script(s);
+ break;
+ }
+ cond = jmp = (insn & (1 << 19)) != 0;
+ if (cond == jmp && (insn & (1 << 21))) {
+ DPRINTF("Compare carry %d\n", s->carry == jmp);
+ cond = s->carry != 0;
+ }
+ if (cond == jmp && (insn & (1 << 17))) {
+ DPRINTF("Compare phase %d %c= %d\n",
+ (s->sstat1 & PHASE_MASK),
+ jmp ? '=' : '!',
+ ((insn >> 24) & 7));
+ cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7);
+ }
+ if (cond == jmp && (insn & (1 << 18))) {
+ uint8_t mask;
+
+ mask = (~insn >> 8) & 0xff;
+ DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n",
+ s->sfbr, mask, jmp ? '=' : '!', insn & mask);
+ cond = (s->sfbr & mask) == (insn & mask);
+ }
+ if (cond == jmp) {
+ if (insn & (1 << 23)) {
+ /* Relative address. */
+ addr = s->dsp + sxt24(addr);
+ }
+ switch ((insn >> 27) & 7) {
+ case 0: /* Jump */
+ DPRINTF("Jump to 0x%08x\n", addr);
+ s->dsp = addr;
+ break;
+ case 1: /* Call */
+ DPRINTF("Call 0x%08x\n", addr);
+ s->temp = s->dsp;
+ s->dsp = addr;
+ break;
+ case 2: /* Return */
+ DPRINTF("Return to 0x%08x\n", s->temp);
+ s->dsp = s->temp;
+ break;
+ case 3: /* Interrupt */
+ DPRINTF("Interrupt 0x%08x\n", s->dsps);
+ if ((insn & (1 << 20)) != 0) {
+ s->istat0 |= LSI_ISTAT0_INTF;
+ lsi_update_irq(s);
+ } else {
+ lsi_script_dma_interrupt(s, LSI_DSTAT_SIR);
+ }
+ break;
+ default:
+ DPRINTF("Illegal transfer control\n");
+ lsi_script_dma_interrupt(s, LSI_DSTAT_IID);
+ break;
+ }
+ } else {
+ DPRINTF("Control condition failed\n");
+ }
+ }
+ break;
+
+ case 3:
+ if ((insn & (1 << 29)) == 0) {
+ /* Memory move. */
+ uint32_t dest;
+ /* ??? The docs imply the destination address is loaded into
+ the TEMP register. However the Linux drivers rely on
+ the value being presrved. */
+ dest = read_dword(s, s->dsp);
+ s->dsp += 4;
+ lsi_memcpy(s, dest, addr, insn & 0xffffff);
+ } else {
+ uint8_t data[7];
+ int reg;
+ int n;
+ int i;
+
+ if (insn & (1 << 28)) {
+ addr = s->dsa + sxt24(addr);
+ }
+ n = (insn & 7);
+ reg = (insn >> 16) & 0xff;
+ if (insn & (1 << 24)) {
+ cpu_physical_memory_read(addr, data, n);
+ DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n,
+ addr, *(int *)data);
+ for (i = 0; i < n; i++) {
+ lsi_reg_writeb(s, reg + i, data[i]);
+ }
+ } else {
+ DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
+ for (i = 0; i < n; i++) {
+ data[i] = lsi_reg_readb(s, reg + i);
+ }
+ cpu_physical_memory_write(addr, data, n);
+ }
+ }
+ }
+ if (insn_processed > 10000 && !s->waiting) {
+ /* Some windows drivers make the device spin waiting for a memory
+ location to change. If we have been executed a lot of code then
+ assume this is the case and force an unexpected device disconnect.
+ This is apparently sufficient to beat the drivers into submission.
+ */
+ if (!(s->sien0 & LSI_SIST0_UDC))
+ fprintf(stderr, "inf. loop with UDC masked\n");
+ lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0);
+ lsi_disconnect(s);
+ } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
+ if (s->dcntl & LSI_DCNTL_SSM) {
+ lsi_script_dma_interrupt(s, LSI_DSTAT_SSI);
+ } else {
+ goto again;
+ }
+ }
+ DPRINTF("SCRIPTS execution stopped\n");
+}
+
+static uint8_t lsi_reg_readb(LSIState *s, int offset)
+{
+ uint8_t tmp;
+#define CASE_GET_REG24(name, addr) \
+ case addr: return s->name & 0xff; \
+ case addr + 1: return (s->name >> 8) & 0xff; \
+ case addr + 2: return (s->name >> 16) & 0xff;
+
+#define CASE_GET_REG32(name, addr) \
+ case addr: return s->name & 0xff; \
+ case addr + 1: return (s->name >> 8) & 0xff; \
+ case addr + 2: return (s->name >> 16) & 0xff; \
+ case addr + 3: return (s->name >> 24) & 0xff;
+
+#ifdef DEBUG_LSI_REG
+ DPRINTF("Read reg %x\n", offset);
+#endif
+ switch (offset) {
+ case 0x00: /* SCNTL0 */
+ return s->scntl0;
+ case 0x01: /* SCNTL1 */
+ return s->scntl1;
+ case 0x02: /* SCNTL2 */
+ return s->scntl2;
+ case 0x03: /* SCNTL3 */
+ return s->scntl3;
+ case 0x04: /* SCID */
+ return s->scid;
+ case 0x05: /* SXFER */
+ return s->sxfer;
+ case 0x06: /* SDID */
+ return s->sdid;
+ case 0x07: /* GPREG0 */
+ return 0x7f;
+ case 0x08: /* Revision ID */
+ return 0x00;
+ case 0xa: /* SSID */
+ return s->ssid;
+ case 0xb: /* SBCL */
+ /* ??? This is not correct. However it's (hopefully) only
+ used for diagnostics, so should be ok. */
+ return 0;
+ case 0xc: /* DSTAT */
+ tmp = s->dstat | 0x80;
+ if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
+ s->dstat = 0;
+ lsi_update_irq(s);
+ return tmp;
+ case 0x0d: /* SSTAT0 */
+ return s->sstat0;
+ case 0x0e: /* SSTAT1 */
+ return s->sstat1;
+ case 0x0f: /* SSTAT2 */
+ return s->scntl1 & LSI_SCNTL1_CON ? 0 : 2;
+ CASE_GET_REG32(dsa, 0x10)
+ case 0x14: /* ISTAT0 */
+ return s->istat0;
+ case 0x15: /* ISTAT1 */
+ return s->istat1;
+ case 0x16: /* MBOX0 */
+ return s->mbox0;
+ case 0x17: /* MBOX1 */
+ return s->mbox1;
+ case 0x18: /* CTEST0 */
+ return 0xff;
+ case 0x19: /* CTEST1 */
+ return 0;
+ case 0x1a: /* CTEST2 */
+ tmp = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM;
+ if (s->istat0 & LSI_ISTAT0_SIGP) {
+ s->istat0 &= ~LSI_ISTAT0_SIGP;
+ tmp |= LSI_CTEST2_SIGP;
+ }
+ return tmp;
+ case 0x1b: /* CTEST3 */
+ return s->ctest3;
+ CASE_GET_REG32(temp, 0x1c)
+ case 0x20: /* DFIFO */
+ return 0;
+ case 0x21: /* CTEST4 */
+ return s->ctest4;
+ case 0x22: /* CTEST5 */
+ return s->ctest5;
+ case 0x23: /* CTEST6 */
+ return 0;
+ CASE_GET_REG24(dbc, 0x24)
+ case 0x27: /* DCMD */
+ return s->dcmd;
+ CASE_GET_REG32(dnad, 0x28)
+ CASE_GET_REG32(dsp, 0x2c)
+ CASE_GET_REG32(dsps, 0x30)
+ CASE_GET_REG32(scratch[0], 0x34)
+ case 0x38: /* DMODE */
+ return s->dmode;
+ case 0x39: /* DIEN */
+ return s->dien;
+ case 0x3a: /* SBR */
+ return s->sbr;
+ case 0x3b: /* DCNTL */
+ return s->dcntl;
+ case 0x40: /* SIEN0 */
+ return s->sien0;
+ case 0x41: /* SIEN1 */
+ return s->sien1;
+ case 0x42: /* SIST0 */
+ tmp = s->sist0;
+ s->sist0 = 0;
+ lsi_update_irq(s);
+ return tmp;
+ case 0x43: /* SIST1 */
+ tmp = s->sist1;
+ s->sist1 = 0;
+ lsi_update_irq(s);
+ return tmp;
+ case 0x46: /* MACNTL */
+ return 0x0f;
+ case 0x47: /* GPCNTL0 */
+ return 0x0f;
+ case 0x48: /* STIME0 */
+ return s->stime0;
+ case 0x4a: /* RESPID0 */
+ return s->respid0;
+ case 0x4b: /* RESPID1 */
+ return s->respid1;
+ case 0x4d: /* STEST1 */
+ return s->stest1;
+ case 0x4e: /* STEST2 */
+ return s->stest2;
+ case 0x4f: /* STEST3 */
+ return s->stest3;
+ case 0x50: /* SIDL */
+ /* This is needed by the linux drivers. We currently only update it
+ during the MSG IN phase. */
+ return s->sidl;
+ case 0x52: /* STEST4 */
+ return 0xe0;
+ case 0x56: /* CCNTL0 */
+ return s->ccntl0;
+ case 0x57: /* CCNTL1 */
+ return s->ccntl1;
+ case 0x58: /* SBDL */
+ /* Some drivers peek at the data bus during the MSG IN phase. */
+ if ((s->sstat1 & PHASE_MASK) == PHASE_MI)
+ return s->msg[0];
+ return 0;
+ case 0x59: /* SBDL high */
+ return 0;
+ CASE_GET_REG32(mmrs, 0xa0)
+ CASE_GET_REG32(mmws, 0xa4)
+ CASE_GET_REG32(sfs, 0xa8)
+ CASE_GET_REG32(drs, 0xac)
+ CASE_GET_REG32(sbms, 0xb0)
+ CASE_GET_REG32(dbms, 0xb4)
+ CASE_GET_REG32(dnad64, 0xb8)
+ CASE_GET_REG32(pmjad1, 0xc0)
+ CASE_GET_REG32(pmjad2, 0xc4)
+ CASE_GET_REG32(rbc, 0xc8)
+ CASE_GET_REG32(ua, 0xcc)
+ CASE_GET_REG32(ia, 0xd4)
+ CASE_GET_REG32(sbc, 0xd8)
+ CASE_GET_REG32(csbc, 0xdc)
+ }
+ if (offset >= 0x5c && offset < 0xa0) {
+ int n;
+ int shift;
+ n = (offset - 0x58) >> 2;
+ shift = (offset & 3) * 8;
+ return (s->scratch[n] >> shift) & 0xff;
+ }
+ BADF("readb 0x%x\n", offset);
+ exit(1);
+#undef CASE_GET_REG24
+#undef CASE_GET_REG32
+}
+
+static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
+{
+#define CASE_SET_REG24(name, addr) \
+ case addr : s->name &= 0xffffff00; s->name |= val; break; \
+ case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \
+ case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break;
+
+#define CASE_SET_REG32(name, addr) \
+ case addr : s->name &= 0xffffff00; s->name |= val; break; \
+ case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8; break; \
+ case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \
+ case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break;
+
+#ifdef DEBUG_LSI_REG
+ DPRINTF("Write reg %x = %02x\n", offset, val);
+#endif
+ switch (offset) {
+ case 0x00: /* SCNTL0 */
+ s->scntl0 = val;
+ if (val & LSI_SCNTL0_START) {
+ BADF("Start sequence not implemented\n");
+ }
+ break;
+ case 0x01: /* SCNTL1 */
+ s->scntl1 = val & ~LSI_SCNTL1_SST;
+ if (val & LSI_SCNTL1_IARB) {
+ BADF("Immediate Arbritration not implemented\n");
+ }
+ if (val & LSI_SCNTL1_RST) {
+ if (!(s->sstat0 & LSI_SSTAT0_RST)) {
+ DeviceState *dev;
+ int id;
+
+ for (id = 0; id < s->bus.ndev; id++) {
+ if (s->bus.devs[id]) {
+ dev = &s->bus.devs[id]->qdev;
+ dev->info->reset(dev);
+ }
+ }
+ s->sstat0 |= LSI_SSTAT0_RST;
+ lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
+ }
+ } else {
+ s->sstat0 &= ~LSI_SSTAT0_RST;
+ }
+ break;
+ case 0x02: /* SCNTL2 */
+ val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS);
+ s->scntl2 = val;
+ break;
+ case 0x03: /* SCNTL3 */
+ s->scntl3 = val;
+ break;
+ case 0x04: /* SCID */
+ s->scid = val;
+ break;
+ case 0x05: /* SXFER */
+ s->sxfer = val;
+ break;
+ case 0x06: /* SDID */
+ if ((val & 0xf) != (s->ssid & 0xf))
+ BADF("Destination ID does not match SSID\n");
+ s->sdid = val & 0xf;
+ break;
+ case 0x07: /* GPREG0 */
+ break;
+ case 0x08: /* SFBR */
+ /* The CPU is not allowed to write to this register. However the
+ SCRIPTS register move instructions are. */
+ s->sfbr = val;
+ break;
+ case 0x0a: case 0x0b:
+ /* Openserver writes to these readonly registers on startup */
+ return;
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ /* Linux writes to these readonly registers on startup. */
+ return;
+ CASE_SET_REG32(dsa, 0x10)
+ case 0x14: /* ISTAT0 */
+ s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0);
+ if (val & LSI_ISTAT0_ABRT) {
+ lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT);
+ }
+ if (val & LSI_ISTAT0_INTF) {
+ s->istat0 &= ~LSI_ISTAT0_INTF;
+ lsi_update_irq(s);
+ }
+ if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
+ DPRINTF("Woken by SIGP\n");
+ s->waiting = 0;
+ s->dsp = s->dnad;
+ lsi_execute_script(s);
+ }
+ if (val & LSI_ISTAT0_SRST) {
+ lsi_soft_reset(s);
+ }
+ break;
+ case 0x16: /* MBOX0 */
+ s->mbox0 = val;
+ break;
+ case 0x17: /* MBOX1 */
+ s->mbox1 = val;
+ break;
+ case 0x1a: /* CTEST2 */
+ s->ctest2 = val & LSI_CTEST2_PCICIE;
+ break;
+ case 0x1b: /* CTEST3 */
+ s->ctest3 = val & 0x0f;
+ break;
+ CASE_SET_REG32(temp, 0x1c)
+ case 0x21: /* CTEST4 */
+ if (val & 7) {
+ BADF("Unimplemented CTEST4-FBL 0x%x\n", val);
+ }
+ s->ctest4 = val;
+ break;
+ case 0x22: /* CTEST5 */
+ if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) {
+ BADF("CTEST5 DMA increment not implemented\n");
+ }
+ s->ctest5 = val;
+ break;
+ CASE_SET_REG24(dbc, 0x24)
+ CASE_SET_REG32(dnad, 0x28)
+ case 0x2c: /* DSP[0:7] */
+ s->dsp &= 0xffffff00;
+ s->dsp |= val;
+ break;
+ case 0x2d: /* DSP[8:15] */
+ s->dsp &= 0xffff00ff;
+ s->dsp |= val << 8;
+ break;
+ case 0x2e: /* DSP[16:23] */
+ s->dsp &= 0xff00ffff;
+ s->dsp |= val << 16;
+ break;
+ case 0x2f: /* DSP[24:31] */
+ s->dsp &= 0x00ffffff;
+ s->dsp |= val << 24;
+ if ((s->dmode & LSI_DMODE_MAN) == 0
+ && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
+ lsi_execute_script(s);
+ break;
+ CASE_SET_REG32(dsps, 0x30)
+ CASE_SET_REG32(scratch[0], 0x34)
+ case 0x38: /* DMODE */
+ if (val & (LSI_DMODE_SIOM | LSI_DMODE_DIOM)) {
+ BADF("IO mappings not implemented\n");
+ }
+ s->dmode = val;
+ break;
+ case 0x39: /* DIEN */
+ s->dien = val;
+ lsi_update_irq(s);
+ break;
+ case 0x3a: /* SBR */
+ s->sbr = val;
+ break;
+ case 0x3b: /* DCNTL */
+ s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD);
+ if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
+ lsi_execute_script(s);
+ break;
+ case 0x40: /* SIEN0 */
+ s->sien0 = val;
+ lsi_update_irq(s);
+ break;
+ case 0x41: /* SIEN1 */
+ s->sien1 = val;
+ lsi_update_irq(s);
+ break;
+ case 0x47: /* GPCNTL0 */
+ break;
+ case 0x48: /* STIME0 */
+ s->stime0 = val;
+ break;
+ case 0x49: /* STIME1 */
+ if (val & 0xf) {
+ DPRINTF("General purpose timer not implemented\n");
+ /* ??? Raising the interrupt immediately seems to be sufficient
+ to keep the FreeBSD driver happy. */
+ lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN);
+ }
+ break;
+ case 0x4a: /* RESPID0 */
+ s->respid0 = val;
+ break;
+ case 0x4b: /* RESPID1 */
+ s->respid1 = val;
+ break;
+ case 0x4d: /* STEST1 */
+ s->stest1 = val;
+ break;
+ case 0x4e: /* STEST2 */
+ if (val & 1) {
+ BADF("Low level mode not implemented\n");
+ }
+ s->stest2 = val;
+ break;
+ case 0x4f: /* STEST3 */
+ if (val & 0x41) {
+ BADF("SCSI FIFO test mode not implemented\n");
+ }
+ s->stest3 = val;
+ break;
+ case 0x56: /* CCNTL0 */
+ s->ccntl0 = val;
+ break;
+ case 0x57: /* CCNTL1 */
+ s->ccntl1 = val;
+ break;
+ CASE_SET_REG32(mmrs, 0xa0)
+ CASE_SET_REG32(mmws, 0xa4)
+ CASE_SET_REG32(sfs, 0xa8)
+ CASE_SET_REG32(drs, 0xac)
+ CASE_SET_REG32(sbms, 0xb0)
+ CASE_SET_REG32(dbms, 0xb4)
+ CASE_SET_REG32(dnad64, 0xb8)
+ CASE_SET_REG32(pmjad1, 0xc0)
+ CASE_SET_REG32(pmjad2, 0xc4)
+ CASE_SET_REG32(rbc, 0xc8)
+ CASE_SET_REG32(ua, 0xcc)
+ CASE_SET_REG32(ia, 0xd4)
+ CASE_SET_REG32(sbc, 0xd8)
+ CASE_SET_REG32(csbc, 0xdc)
+ default:
+ if (offset >= 0x5c && offset < 0xa0) {
+ int n;
+ int shift;
+ n = (offset - 0x58) >> 2;
+ shift = (offset & 3) * 8;
+ s->scratch[n] &= ~(0xff << shift);
+ s->scratch[n] |= (val & 0xff) << shift;
+ } else {
+ BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
+ }
+ }
+#undef CASE_SET_REG24
+#undef CASE_SET_REG32
+}
+
+static void lsi_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+
+ lsi_reg_writeb(s, addr & 0xff, val);
+}
+
+static void lsi_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+}
+
+static void lsi_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+ lsi_reg_writeb(s, addr + 2, (val >> 16) & 0xff);
+ lsi_reg_writeb(s, addr + 3, (val >> 24) & 0xff);
+}
+
+static uint32_t lsi_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = opaque;
+
+ return lsi_reg_readb(s, addr & 0xff);
+}
+
+static uint32_t lsi_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ return val;
+}
+
+static uint32_t lsi_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = opaque;
+ uint32_t val;
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ val |= lsi_reg_readb(s, addr + 2) << 16;
+ val |= lsi_reg_readb(s, addr + 3) << 24;
+ return val;
+}
+
+static CPUReadMemoryFunc * const lsi_mmio_readfn[3] = {
+ lsi_mmio_readb,
+ lsi_mmio_readw,
+ lsi_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const lsi_mmio_writefn[3] = {
+ lsi_mmio_writeb,
+ lsi_mmio_writew,
+ lsi_mmio_writel,
+};
+
+static void lsi_ram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+ uint32_t newval;
+ int shift;
+
+ addr &= 0x1fff;
+ newval = s->script_ram[addr >> 2];
+ shift = (addr & 3) * 8;
+ newval &= ~(0xff << shift);
+ newval |= val << shift;
+ s->script_ram[addr >> 2] = newval;
+}
+
+static void lsi_ram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+ uint32_t newval;
+
+ addr &= 0x1fff;
+ newval = s->script_ram[addr >> 2];
+ if (addr & 2) {
+ newval = (newval & 0xffff) | (val << 16);
+ } else {
+ newval = (newval & 0xffff0000) | val;
+ }
+ s->script_ram[addr >> 2] = newval;
+}
+
+
+static void lsi_ram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+
+ addr &= 0x1fff;
+ s->script_ram[addr >> 2] = val;
+}
+
+static uint32_t lsi_ram_readb(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1fff;
+ val = s->script_ram[addr >> 2];
+ val >>= (addr & 3) * 8;
+ return val & 0xff;
+}
+
+static uint32_t lsi_ram_readw(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1fff;
+ val = s->script_ram[addr >> 2];
+ if (addr & 2)
+ val >>= 16;
+ return val;
+}
+
+static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr)
+{
+ LSIState *s = opaque;
+
+ addr &= 0x1fff;
+ return s->script_ram[addr >> 2];
+}
+
+static CPUReadMemoryFunc * const lsi_ram_readfn[3] = {
+ lsi_ram_readb,
+ lsi_ram_readw,
+ lsi_ram_readl,
+};
+
+static CPUWriteMemoryFunc * const lsi_ram_writefn[3] = {
+ lsi_ram_writeb,
+ lsi_ram_writew,
+ lsi_ram_writel,
+};
+
+static uint32_t lsi_io_readb(void *opaque, uint32_t addr)
+{
+ LSIState *s = opaque;
+ return lsi_reg_readb(s, addr & 0xff);
+}
+
+static uint32_t lsi_io_readw(void *opaque, uint32_t addr)
+{
+ LSIState *s = opaque;
+ uint32_t val;
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ return val;
+}
+
+static uint32_t lsi_io_readl(void *opaque, uint32_t addr)
+{
+ LSIState *s = opaque;
+ uint32_t val;
+ addr &= 0xff;
+ val = lsi_reg_readb(s, addr);
+ val |= lsi_reg_readb(s, addr + 1) << 8;
+ val |= lsi_reg_readb(s, addr + 2) << 16;
+ val |= lsi_reg_readb(s, addr + 3) << 24;
+ return val;
+}
+
+static void lsi_io_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+ lsi_reg_writeb(s, addr & 0xff, val);
+}
+
+static void lsi_io_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+}
+
+static void lsi_io_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ LSIState *s = opaque;
+ addr &= 0xff;
+ lsi_reg_writeb(s, addr, val & 0xff);
+ lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff);
+ lsi_reg_writeb(s, addr + 2, (val >> 16) & 0xff);
+ lsi_reg_writeb(s, addr + 3, (val >> 24) & 0xff);
+}
+
+static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev, pci_dev);
+
+ DPRINTF("Mapping IO at %08"FMT_PCIBUS"\n", addr);
+
+ register_ioport_write(addr, 256, 1, lsi_io_writeb, s);
+ register_ioport_read(addr, 256, 1, lsi_io_readb, s);
+ register_ioport_write(addr, 256, 2, lsi_io_writew, s);
+ register_ioport_read(addr, 256, 2, lsi_io_readw, s);
+ register_ioport_write(addr, 256, 4, lsi_io_writel, s);
+ register_ioport_read(addr, 256, 4, lsi_io_readl, s);
+}
+
+static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev, pci_dev);
+
+ DPRINTF("Mapping ram at %08"FMT_PCIBUS"\n", addr);
+ s->script_ram_base = addr;
+ cpu_register_physical_memory(addr + 0, 0x2000, s->ram_io_addr);
+}
+
+static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev, pci_dev);
+
+ DPRINTF("Mapping registers at %08"FMT_PCIBUS"\n", addr);
+ cpu_register_physical_memory(addr + 0, 0x400, s->mmio_io_addr);
+}
+
+static void lsi_scsi_reset(DeviceState *dev)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, dev);
+
+ lsi_soft_reset(s);
+}
+
+static void lsi_pre_save(void *opaque)
+{
+ LSIState *s = opaque;
+
+ if (s->current) {
+ assert(s->current->dma_buf == NULL);
+ assert(s->current->dma_len == 0);
+ }
+ assert(QTAILQ_EMPTY(&s->queue));
+}
+
+static const VMStateDescription vmstate_lsi_scsi = {
+ .name = "lsiscsi",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = lsi_pre_save,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, LSIState),
+
+ VMSTATE_INT32(carry, LSIState),
+ VMSTATE_INT32(sense, LSIState),
+ VMSTATE_INT32(msg_action, LSIState),
+ VMSTATE_INT32(msg_len, LSIState),
+ VMSTATE_BUFFER(msg, LSIState),
+ VMSTATE_INT32(waiting, LSIState),
+
+ VMSTATE_UINT32(dsa, LSIState),
+ VMSTATE_UINT32(temp, LSIState),
+ VMSTATE_UINT32(dnad, LSIState),
+ VMSTATE_UINT32(dbc, LSIState),
+ VMSTATE_UINT8(istat0, LSIState),
+ VMSTATE_UINT8(istat1, LSIState),
+ VMSTATE_UINT8(dcmd, LSIState),
+ VMSTATE_UINT8(dstat, LSIState),
+ VMSTATE_UINT8(dien, LSIState),
+ VMSTATE_UINT8(sist0, LSIState),
+ VMSTATE_UINT8(sist1, LSIState),
+ VMSTATE_UINT8(sien0, LSIState),
+ VMSTATE_UINT8(sien1, LSIState),
+ VMSTATE_UINT8(mbox0, LSIState),
+ VMSTATE_UINT8(mbox1, LSIState),
+ VMSTATE_UINT8(dfifo, LSIState),
+ VMSTATE_UINT8(ctest2, LSIState),
+ VMSTATE_UINT8(ctest3, LSIState),
+ VMSTATE_UINT8(ctest4, LSIState),
+ VMSTATE_UINT8(ctest5, LSIState),
+ VMSTATE_UINT8(ccntl0, LSIState),
+ VMSTATE_UINT8(ccntl1, LSIState),
+ VMSTATE_UINT32(dsp, LSIState),
+ VMSTATE_UINT32(dsps, LSIState),
+ VMSTATE_UINT8(dmode, LSIState),
+ VMSTATE_UINT8(dcntl, LSIState),
+ VMSTATE_UINT8(scntl0, LSIState),
+ VMSTATE_UINT8(scntl1, LSIState),
+ VMSTATE_UINT8(scntl2, LSIState),
+ VMSTATE_UINT8(scntl3, LSIState),
+ VMSTATE_UINT8(sstat0, LSIState),
+ VMSTATE_UINT8(sstat1, LSIState),
+ VMSTATE_UINT8(scid, LSIState),
+ VMSTATE_UINT8(sxfer, LSIState),
+ VMSTATE_UINT8(socl, LSIState),
+ VMSTATE_UINT8(sdid, LSIState),
+ VMSTATE_UINT8(ssid, LSIState),
+ VMSTATE_UINT8(sfbr, LSIState),
+ VMSTATE_UINT8(stest1, LSIState),
+ VMSTATE_UINT8(stest2, LSIState),
+ VMSTATE_UINT8(stest3, LSIState),
+ VMSTATE_UINT8(sidl, LSIState),
+ VMSTATE_UINT8(stime0, LSIState),
+ VMSTATE_UINT8(respid0, LSIState),
+ VMSTATE_UINT8(respid1, LSIState),
+ VMSTATE_UINT32(mmrs, LSIState),
+ VMSTATE_UINT32(mmws, LSIState),
+ VMSTATE_UINT32(sfs, LSIState),
+ VMSTATE_UINT32(drs, LSIState),
+ VMSTATE_UINT32(sbms, LSIState),
+ VMSTATE_UINT32(dbms, LSIState),
+ VMSTATE_UINT32(dnad64, LSIState),
+ VMSTATE_UINT32(pmjad1, LSIState),
+ VMSTATE_UINT32(pmjad2, LSIState),
+ VMSTATE_UINT32(rbc, LSIState),
+ VMSTATE_UINT32(ua, LSIState),
+ VMSTATE_UINT32(ia, LSIState),
+ VMSTATE_UINT32(sbc, LSIState),
+ VMSTATE_UINT32(csbc, LSIState),
+ VMSTATE_BUFFER_UNSAFE(scratch, LSIState, 0, 18 * sizeof(uint32_t)),
+ VMSTATE_UINT8(sbr, LSIState),
+
+ VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 2048 * sizeof(uint32_t)),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int lsi_scsi_uninit(PCIDevice *d)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev, d);
+
+ cpu_unregister_io_memory(s->mmio_io_addr);
+ cpu_unregister_io_memory(s->ram_io_addr);
+
+ return 0;
+}
+
+static int lsi_scsi_init(PCIDevice *dev)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev, dev);
+ uint8_t *pci_conf;
+
+ pci_conf = s->dev.config;
+
+ /* PCI Vendor ID (word) */
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_LSI_LOGIC);
+ /* PCI device ID (word) */
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_LSI_53C895A);
+ /* PCI base class code */
+ pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_SCSI);
+ /* PCI subsystem ID */
+ pci_conf[PCI_SUBSYSTEM_ID] = 0x00;
+ pci_conf[PCI_SUBSYSTEM_ID + 1] = 0x10;
+ /* PCI latency timer = 255 */
+ pci_conf[PCI_LATENCY_TIMER] = 0xff;
+ /* TODO: RST# value should be 0 */
+ /* Interrupt pin 1 */
+ pci_conf[PCI_INTERRUPT_PIN] = 0x01;
+
+ s->mmio_io_addr = cpu_register_io_memory(lsi_mmio_readfn,
+ lsi_mmio_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ s->ram_io_addr = cpu_register_io_memory(lsi_ram_readfn,
+ lsi_ram_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+
+ pci_register_bar(&s->dev, 0, 256,
+ PCI_BASE_ADDRESS_SPACE_IO, lsi_io_mapfunc);
+ pci_register_bar(&s->dev, 1, 0x400,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_mmio_mapfunc);
+ pci_register_bar(&s->dev, 2, 0x2000,
+ 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);
+ if (!dev->qdev.hotplugged) {
+ return scsi_bus_legacy_handle_cmdline(&s->bus);
+ }
+ return 0;
+}
+
+static PCIDeviceInfo lsi_info = {
+ .qdev.name = "lsi53c895a",
+ .qdev.alias = "lsi",
+ .qdev.size = sizeof(LSIState),
+ .qdev.reset = lsi_scsi_reset,
+ .qdev.vmsd = &vmstate_lsi_scsi,
+ .init = lsi_scsi_init,
+ .exit = lsi_scsi_uninit,
+};
+
+static void lsi53c895a_register_devices(void)
+{
+ pci_qdev_register(&lsi_info);
+}
+
+device_init(lsi53c895a_register_devices);
diff --git a/hw/m48t59.c b/hw/m48t59.c
new file mode 100644
index 0000000..2020487
--- /dev/null
+++ b/hw/m48t59.c
@@ -0,0 +1,762 @@
+/*
+ * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
+ *
+ * Copyright (c) 2003-2005, 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "nvram.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "isa.h"
+
+//#define DEBUG_NVRAM
+
+#if defined(DEBUG_NVRAM)
+#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0)
+#else
+#define NVRAM_PRINTF(fmt, ...) do { } while (0)
+#endif
+
+/*
+ * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has
+ * alarm and a watchdog timer and related control registers. In the
+ * PPC platform there is also a nvram lock function.
+ */
+
+/*
+ * Chipset docs:
+ * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf
+ * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf
+ * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf
+ */
+
+struct M48t59State {
+ /* Model parameters */
+ uint32_t type; // 2 = m48t02, 8 = m48t08, 59 = m48t59
+ /* Hardware parameters */
+ qemu_irq IRQ;
+ uint32_t io_base;
+ uint32_t size;
+ /* RTC management */
+ time_t time_offset;
+ time_t stop_time;
+ /* Alarm & watchdog */
+ struct tm alarm;
+ struct QEMUTimer *alrm_timer;
+ struct QEMUTimer *wd_timer;
+ /* NVRAM storage */
+ uint8_t lock;
+ uint16_t addr;
+ uint8_t *buffer;
+};
+
+typedef struct M48t59ISAState {
+ ISADevice busdev;
+ M48t59State state;
+} M48t59ISAState;
+
+typedef struct M48t59SysBusState {
+ SysBusDevice busdev;
+ M48t59State state;
+} M48t59SysBusState;
+
+/* Fake timer functions */
+
+/* Alarm management */
+static void alarm_cb (void *opaque)
+{
+ struct tm tm;
+ uint64_t next_time;
+ M48t59State *NVRAM = opaque;
+
+ qemu_set_irq(NVRAM->IRQ, 1);
+ if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once a month */
+ qemu_get_timedate(&tm, NVRAM->time_offset);
+ tm.tm_mon++;
+ if (tm.tm_mon == 13) {
+ tm.tm_mon = 1;
+ tm.tm_year++;
+ }
+ next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset;
+ } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once a day */
+ next_time = 24 * 60 * 60;
+ } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once an hour */
+ next_time = 60 * 60;
+ } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
+ (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+ /* Repeat once a minute */
+ next_time = 60;
+ } else {
+ /* Repeat once a second */
+ next_time = 1;
+ }
+ qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock(vm_clock) +
+ next_time * 1000);
+ qemu_set_irq(NVRAM->IRQ, 0);
+}
+
+static void set_alarm(M48t59State *NVRAM)
+{
+ int diff;
+ if (NVRAM->alrm_timer != NULL) {
+ qemu_del_timer(NVRAM->alrm_timer);
+ diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset;
+ if (diff > 0)
+ qemu_mod_timer(NVRAM->alrm_timer, diff * 1000);
+ }
+}
+
+/* RTC management helpers */
+static inline void get_time(M48t59State *NVRAM, struct tm *tm)
+{
+ qemu_get_timedate(tm, NVRAM->time_offset);
+}
+
+static void set_time(M48t59State *NVRAM, struct tm *tm)
+{
+ NVRAM->time_offset = qemu_timedate_diff(tm);
+ set_alarm(NVRAM);
+}
+
+/* Watchdog management */
+static void watchdog_cb (void *opaque)
+{
+ M48t59State *NVRAM = opaque;
+
+ NVRAM->buffer[0x1FF0] |= 0x80;
+ if (NVRAM->buffer[0x1FF7] & 0x80) {
+ NVRAM->buffer[0x1FF7] = 0x00;
+ NVRAM->buffer[0x1FFC] &= ~0x40;
+ /* May it be a hw CPU Reset instead ? */
+ qemu_system_reset_request();
+ } else {
+ qemu_set_irq(NVRAM->IRQ, 1);
+ qemu_set_irq(NVRAM->IRQ, 0);
+ }
+}
+
+static void set_up_watchdog(M48t59State *NVRAM, uint8_t value)
+{
+ uint64_t interval; /* in 1/16 seconds */
+
+ NVRAM->buffer[0x1FF0] &= ~0x80;
+ if (NVRAM->wd_timer != NULL) {
+ qemu_del_timer(NVRAM->wd_timer);
+ if (value != 0) {
+ interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
+ qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
+ ((interval * 1000) >> 4));
+ }
+ }
+}
+
+/* Direct access to NVRAM */
+void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ M48t59State *NVRAM = opaque;
+ struct tm tm;
+ int tmp;
+
+ if (addr > 0x1FF8 && addr < 0x2000)
+ NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
+
+ /* check for NVRAM access */
+ if ((NVRAM->type == 2 && addr < 0x7f8) ||
+ (NVRAM->type == 8 && addr < 0x1ff8) ||
+ (NVRAM->type == 59 && addr < 0x1ff0))
+ goto do_write;
+
+ /* TOD access */
+ switch (addr) {
+ case 0x1FF0:
+ /* flags register : read-only */
+ break;
+ case 0x1FF1:
+ /* unused */
+ break;
+ case 0x1FF2:
+ /* alarm seconds */
+ tmp = from_bcd(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ NVRAM->alarm.tm_sec = tmp;
+ NVRAM->buffer[0x1FF2] = val;
+ set_alarm(NVRAM);
+ }
+ break;
+ case 0x1FF3:
+ /* alarm minutes */
+ tmp = from_bcd(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ NVRAM->alarm.tm_min = tmp;
+ NVRAM->buffer[0x1FF3] = val;
+ set_alarm(NVRAM);
+ }
+ break;
+ case 0x1FF4:
+ /* alarm hours */
+ tmp = from_bcd(val & 0x3F);
+ if (tmp >= 0 && tmp <= 23) {
+ NVRAM->alarm.tm_hour = tmp;
+ NVRAM->buffer[0x1FF4] = val;
+ set_alarm(NVRAM);
+ }
+ break;
+ case 0x1FF5:
+ /* alarm date */
+ tmp = from_bcd(val & 0x1F);
+ if (tmp != 0) {
+ NVRAM->alarm.tm_mday = tmp;
+ NVRAM->buffer[0x1FF5] = val;
+ set_alarm(NVRAM);
+ }
+ break;
+ case 0x1FF6:
+ /* interrupts */
+ NVRAM->buffer[0x1FF6] = val;
+ break;
+ case 0x1FF7:
+ /* watchdog */
+ NVRAM->buffer[0x1FF7] = val;
+ set_up_watchdog(NVRAM, val);
+ break;
+ case 0x1FF8:
+ case 0x07F8:
+ /* control */
+ NVRAM->buffer[addr] = (val & ~0xA0) | 0x90;
+ break;
+ case 0x1FF9:
+ case 0x07F9:
+ /* seconds (BCD) */
+ tmp = from_bcd(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ get_time(NVRAM, &tm);
+ tm.tm_sec = tmp;
+ set_time(NVRAM, &tm);
+ }
+ if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) {
+ if (val & 0x80) {
+ NVRAM->stop_time = time(NULL);
+ } else {
+ NVRAM->time_offset += NVRAM->stop_time - time(NULL);
+ NVRAM->stop_time = 0;
+ }
+ }
+ NVRAM->buffer[addr] = val & 0x80;
+ break;
+ case 0x1FFA:
+ case 0x07FA:
+ /* minutes (BCD) */
+ tmp = from_bcd(val & 0x7F);
+ if (tmp >= 0 && tmp <= 59) {
+ get_time(NVRAM, &tm);
+ tm.tm_min = tmp;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFB:
+ case 0x07FB:
+ /* hours (BCD) */
+ tmp = from_bcd(val & 0x3F);
+ if (tmp >= 0 && tmp <= 23) {
+ get_time(NVRAM, &tm);
+ tm.tm_hour = tmp;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFC:
+ case 0x07FC:
+ /* day of the week / century */
+ tmp = from_bcd(val & 0x07);
+ get_time(NVRAM, &tm);
+ tm.tm_wday = tmp;
+ set_time(NVRAM, &tm);
+ NVRAM->buffer[addr] = val & 0x40;
+ break;
+ case 0x1FFD:
+ case 0x07FD:
+ /* date */
+ tmp = from_bcd(val & 0x1F);
+ if (tmp != 0) {
+ get_time(NVRAM, &tm);
+ tm.tm_mday = tmp;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFE:
+ case 0x07FE:
+ /* month */
+ tmp = from_bcd(val & 0x1F);
+ if (tmp >= 1 && tmp <= 12) {
+ get_time(NVRAM, &tm);
+ tm.tm_mon = tmp - 1;
+ set_time(NVRAM, &tm);
+ }
+ break;
+ case 0x1FFF:
+ case 0x07FF:
+ /* year */
+ tmp = from_bcd(val);
+ if (tmp >= 0 && tmp <= 99) {
+ get_time(NVRAM, &tm);
+ if (NVRAM->type == 8)
+ tm.tm_year = from_bcd(val) + 68; // Base year is 1968
+ else
+ tm.tm_year = from_bcd(val);
+ set_time(NVRAM, &tm);
+ }
+ break;
+ default:
+ /* Check lock registers state */
+ if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
+ break;
+ if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
+ break;
+ do_write:
+ if (addr < NVRAM->size) {
+ NVRAM->buffer[addr] = val & 0xFF;
+ }
+ break;
+ }
+}
+
+uint32_t m48t59_read (void *opaque, uint32_t addr)
+{
+ M48t59State *NVRAM = opaque;
+ struct tm tm;
+ uint32_t retval = 0xFF;
+
+ /* check for NVRAM access */
+ if ((NVRAM->type == 2 && addr < 0x078f) ||
+ (NVRAM->type == 8 && addr < 0x1ff8) ||
+ (NVRAM->type == 59 && addr < 0x1ff0))
+ goto do_read;
+
+ /* TOD access */
+ switch (addr) {
+ case 0x1FF0:
+ /* flags register */
+ goto do_read;
+ case 0x1FF1:
+ /* unused */
+ retval = 0;
+ break;
+ case 0x1FF2:
+ /* alarm seconds */
+ goto do_read;
+ case 0x1FF3:
+ /* alarm minutes */
+ goto do_read;
+ case 0x1FF4:
+ /* alarm hours */
+ goto do_read;
+ case 0x1FF5:
+ /* alarm date */
+ goto do_read;
+ case 0x1FF6:
+ /* interrupts */
+ goto do_read;
+ case 0x1FF7:
+ /* A read resets the watchdog */
+ set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
+ goto do_read;
+ case 0x1FF8:
+ case 0x07F8:
+ /* control */
+ goto do_read;
+ case 0x1FF9:
+ case 0x07F9:
+ /* seconds (BCD) */
+ get_time(NVRAM, &tm);
+ retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec);
+ break;
+ case 0x1FFA:
+ case 0x07FA:
+ /* minutes (BCD) */
+ get_time(NVRAM, &tm);
+ retval = to_bcd(tm.tm_min);
+ break;
+ case 0x1FFB:
+ case 0x07FB:
+ /* hours (BCD) */
+ get_time(NVRAM, &tm);
+ retval = to_bcd(tm.tm_hour);
+ break;
+ case 0x1FFC:
+ case 0x07FC:
+ /* day of the week / century */
+ get_time(NVRAM, &tm);
+ retval = NVRAM->buffer[addr] | tm.tm_wday;
+ break;
+ case 0x1FFD:
+ case 0x07FD:
+ /* date */
+ get_time(NVRAM, &tm);
+ retval = to_bcd(tm.tm_mday);
+ break;
+ case 0x1FFE:
+ case 0x07FE:
+ /* month */
+ get_time(NVRAM, &tm);
+ retval = to_bcd(tm.tm_mon + 1);
+ break;
+ case 0x1FFF:
+ case 0x07FF:
+ /* year */
+ get_time(NVRAM, &tm);
+ if (NVRAM->type == 8)
+ retval = to_bcd(tm.tm_year - 68); // Base year is 1968
+ else
+ retval = to_bcd(tm.tm_year);
+ break;
+ default:
+ /* Check lock registers state */
+ if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
+ break;
+ if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
+ break;
+ do_read:
+ if (addr < NVRAM->size) {
+ retval = NVRAM->buffer[addr];
+ }
+ break;
+ }
+ if (addr > 0x1FF9 && addr < 0x2000)
+ NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
+
+ return retval;
+}
+
+void m48t59_set_addr (void *opaque, uint32_t addr)
+{
+ M48t59State *NVRAM = opaque;
+
+ NVRAM->addr = addr;
+}
+
+void m48t59_toggle_lock (void *opaque, int lock)
+{
+ M48t59State *NVRAM = opaque;
+
+ NVRAM->lock ^= 1 << lock;
+}
+
+/* IO access to NVRAM */
+static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+ M48t59State *NVRAM = opaque;
+
+ addr -= NVRAM->io_base;
+ NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
+ switch (addr) {
+ case 0:
+ NVRAM->addr &= ~0x00FF;
+ NVRAM->addr |= val;
+ break;
+ case 1:
+ NVRAM->addr &= ~0xFF00;
+ NVRAM->addr |= val << 8;
+ break;
+ case 3:
+ m48t59_write(NVRAM, val, NVRAM->addr);
+ NVRAM->addr = 0x0000;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t NVRAM_readb (void *opaque, uint32_t addr)
+{
+ M48t59State *NVRAM = opaque;
+ uint32_t retval;
+
+ addr -= NVRAM->io_base;
+ switch (addr) {
+ case 3:
+ retval = m48t59_read(NVRAM, NVRAM->addr);
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+ NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
+
+ return retval;
+}
+
+static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ M48t59State *NVRAM = opaque;
+
+ m48t59_write(NVRAM, addr, value & 0xff);
+}
+
+static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ M48t59State *NVRAM = opaque;
+
+ m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
+ m48t59_write(NVRAM, addr + 1, value & 0xff);
+}
+
+static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ M48t59State *NVRAM = opaque;
+
+ m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
+ m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
+ m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
+ m48t59_write(NVRAM, addr + 3, value & 0xff);
+}
+
+static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr)
+{
+ M48t59State *NVRAM = opaque;
+ uint32_t retval;
+
+ retval = m48t59_read(NVRAM, addr);
+ return retval;
+}
+
+static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr)
+{
+ M48t59State *NVRAM = opaque;
+ uint32_t retval;
+
+ retval = m48t59_read(NVRAM, addr) << 8;
+ retval |= m48t59_read(NVRAM, addr + 1);
+ return retval;
+}
+
+static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr)
+{
+ M48t59State *NVRAM = opaque;
+ uint32_t retval;
+
+ retval = m48t59_read(NVRAM, addr) << 24;
+ retval |= m48t59_read(NVRAM, addr + 1) << 16;
+ retval |= m48t59_read(NVRAM, addr + 2) << 8;
+ retval |= m48t59_read(NVRAM, addr + 3);
+ return retval;
+}
+
+static CPUWriteMemoryFunc * const nvram_write[] = {
+ &nvram_writeb,
+ &nvram_writew,
+ &nvram_writel,
+};
+
+static CPUReadMemoryFunc * const nvram_read[] = {
+ &nvram_readb,
+ &nvram_readw,
+ &nvram_readl,
+};
+
+static void m48t59_save(QEMUFile *f, void *opaque)
+{
+ M48t59State *s = opaque;
+
+ qemu_put_8s(f, &s->lock);
+ qemu_put_be16s(f, &s->addr);
+ qemu_put_buffer(f, s->buffer, s->size);
+}
+
+static int m48t59_load(QEMUFile *f, void *opaque, int version_id)
+{
+ M48t59State *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_8s(f, &s->lock);
+ qemu_get_be16s(f, &s->addr);
+ qemu_get_buffer(f, s->buffer, s->size);
+
+ return 0;
+}
+
+static void m48t59_reset_common(M48t59State *NVRAM)
+{
+ NVRAM->addr = 0;
+ NVRAM->lock = 0;
+ if (NVRAM->alrm_timer != NULL)
+ qemu_del_timer(NVRAM->alrm_timer);
+
+ if (NVRAM->wd_timer != NULL)
+ qemu_del_timer(NVRAM->wd_timer);
+}
+
+static void m48t59_reset_isa(DeviceState *d)
+{
+ M48t59ISAState *isa = container_of(d, M48t59ISAState, busdev.qdev);
+ M48t59State *NVRAM = &isa->state;
+
+ m48t59_reset_common(NVRAM);
+}
+
+static void m48t59_reset_sysbus(DeviceState *d)
+{
+ M48t59SysBusState *sys = container_of(d, M48t59SysBusState, busdev.qdev);
+ M48t59State *NVRAM = &sys->state;
+
+ m48t59_reset_common(NVRAM);
+}
+
+/* Initialisation routine */
+M48t59State *m48t59_init(qemu_irq IRQ, target_phys_addr_t mem_base,
+ uint32_t io_base, uint16_t size, int type)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ M48t59SysBusState *d;
+ M48t59State *state;
+
+ dev = qdev_create(NULL, "m48t59");
+ qdev_prop_set_uint32(dev, "type", type);
+ qdev_prop_set_uint32(dev, "size", size);
+ qdev_prop_set_uint32(dev, "io_base", io_base);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ d = FROM_SYSBUS(M48t59SysBusState, s);
+ state = &d->state;
+ sysbus_connect_irq(s, 0, IRQ);
+ if (io_base != 0) {
+ register_ioport_read(io_base, 0x04, 1, NVRAM_readb, state);
+ register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, state);
+ }
+ if (mem_base != 0) {
+ sysbus_mmio_map(s, 0, mem_base);
+ }
+
+ return state;
+}
+
+M48t59State *m48t59_init_isa(uint32_t io_base, uint16_t size, int type)
+{
+ M48t59ISAState *d;
+ ISADevice *dev;
+ M48t59State *s;
+
+ dev = isa_create("m48t59_isa");
+ qdev_prop_set_uint32(&dev->qdev, "type", type);
+ qdev_prop_set_uint32(&dev->qdev, "size", size);
+ qdev_prop_set_uint32(&dev->qdev, "io_base", io_base);
+ qdev_init_nofail(&dev->qdev);
+ d = DO_UPCAST(M48t59ISAState, busdev, dev);
+ s = &d->state;
+
+ if (io_base != 0) {
+ register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s);
+ register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s);
+ isa_init_ioport_range(dev, io_base, 4);
+ }
+
+ return s;
+}
+
+static void m48t59_init_common(M48t59State *s)
+{
+ s->buffer = qemu_mallocz(s->size);
+ if (s->type == 59) {
+ s->alrm_timer = qemu_new_timer(vm_clock, &alarm_cb, s);
+ s->wd_timer = qemu_new_timer(vm_clock, &watchdog_cb, s);
+ }
+ qemu_get_timedate(&s->alarm, 0);
+
+ register_savevm(NULL, "m48t59", -1, 1, m48t59_save, m48t59_load, s);
+}
+
+static int m48t59_init_isa1(ISADevice *dev)
+{
+ M48t59ISAState *d = DO_UPCAST(M48t59ISAState, busdev, dev);
+ M48t59State *s = &d->state;
+
+ isa_init_irq(dev, &s->IRQ, 8);
+ m48t59_init_common(s);
+
+ return 0;
+}
+
+static int m48t59_init1(SysBusDevice *dev)
+{
+ M48t59SysBusState *d = FROM_SYSBUS(M48t59SysBusState, dev);
+ M48t59State *s = &d->state;
+ int mem_index;
+
+ sysbus_init_irq(dev, &s->IRQ);
+
+ mem_index = cpu_register_io_memory(nvram_read, nvram_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, s->size, mem_index);
+ m48t59_init_common(s);
+
+ return 0;
+}
+
+static ISADeviceInfo m48t59_isa_info = {
+ .init = m48t59_init_isa1,
+ .qdev.name = "m48t59_isa",
+ .qdev.size = sizeof(M48t59ISAState),
+ .qdev.reset = m48t59_reset_isa,
+ .qdev.no_user = 1,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("size", M48t59ISAState, state.size, -1),
+ DEFINE_PROP_UINT32("type", M48t59ISAState, state.type, -1),
+ DEFINE_PROP_HEX32( "io_base", M48t59ISAState, state.io_base, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static SysBusDeviceInfo m48t59_info = {
+ .init = m48t59_init1,
+ .qdev.name = "m48t59",
+ .qdev.size = sizeof(M48t59SysBusState),
+ .qdev.reset = m48t59_reset_sysbus,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("size", M48t59SysBusState, state.size, -1),
+ DEFINE_PROP_UINT32("type", M48t59SysBusState, state.type, -1),
+ DEFINE_PROP_HEX32( "io_base", M48t59SysBusState, state.io_base, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void m48t59_register_devices(void)
+{
+ sysbus_register_withprop(&m48t59_info);
+ isa_qdev_register(&m48t59_isa_info);
+}
+
+device_init(m48t59_register_devices)
diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c
new file mode 100644
index 0000000..5680fa9
--- /dev/null
+++ b/hw/mac_dbdma.c
@@ -0,0 +1,852 @@
+/*
+ * PowerMac descriptor-based DMA emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h
+ *
+ * Definitions for using the Apple Descriptor-Based DMA controller
+ * in Power Macintosh computers.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * some parts from mol 0.9.71
+ *
+ * Descriptor based DMA emulation
+ *
+ * Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+#include "mac_dbdma.h"
+
+/* debug DBDMA */
+//#define DEBUG_DBDMA
+
+#ifdef DEBUG_DBDMA
+#define DBDMA_DPRINTF(fmt, ...) \
+ do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DBDMA_DPRINTF(fmt, ...)
+#endif
+
+/*
+ */
+
+/*
+ * DBDMA control/status registers. All little-endian.
+ */
+
+#define DBDMA_CONTROL 0x00
+#define DBDMA_STATUS 0x01
+#define DBDMA_CMDPTR_HI 0x02
+#define DBDMA_CMDPTR_LO 0x03
+#define DBDMA_INTR_SEL 0x04
+#define DBDMA_BRANCH_SEL 0x05
+#define DBDMA_WAIT_SEL 0x06
+#define DBDMA_XFER_MODE 0x07
+#define DBDMA_DATA2PTR_HI 0x08
+#define DBDMA_DATA2PTR_LO 0x09
+#define DBDMA_RES1 0x0A
+#define DBDMA_ADDRESS_HI 0x0B
+#define DBDMA_BRANCH_ADDR_HI 0x0C
+#define DBDMA_RES2 0x0D
+#define DBDMA_RES3 0x0E
+#define DBDMA_RES4 0x0F
+
+#define DBDMA_REGS 16
+#define DBDMA_SIZE (DBDMA_REGS * sizeof(uint32_t))
+
+#define DBDMA_CHANNEL_SHIFT 7
+#define DBDMA_CHANNEL_SIZE (1 << DBDMA_CHANNEL_SHIFT)
+
+#define DBDMA_CHANNELS (0x1000 >> DBDMA_CHANNEL_SHIFT)
+
+/* Bits in control and status registers */
+
+#define RUN 0x8000
+#define PAUSE 0x4000
+#define FLUSH 0x2000
+#define WAKE 0x1000
+#define DEAD 0x0800
+#define ACTIVE 0x0400
+#define BT 0x0100
+#define DEVSTAT 0x00ff
+
+/*
+ * DBDMA command structure. These fields are all little-endian!
+ */
+
+typedef struct dbdma_cmd {
+ uint16_t req_count; /* requested byte transfer count */
+ uint16_t command; /* command word (has bit-fields) */
+ uint32_t phy_addr; /* physical data address */
+ uint32_t cmd_dep; /* command-dependent field */
+ uint16_t res_count; /* residual count after completion */
+ uint16_t xfer_status; /* transfer status */
+} dbdma_cmd;
+
+/* DBDMA command values in command field */
+
+#define COMMAND_MASK 0xf000
+#define OUTPUT_MORE 0x0000 /* transfer memory data to stream */
+#define OUTPUT_LAST 0x1000 /* ditto followed by end marker */
+#define INPUT_MORE 0x2000 /* transfer stream data to memory */
+#define INPUT_LAST 0x3000 /* ditto, expect end marker */
+#define STORE_WORD 0x4000 /* write word (4 bytes) to device reg */
+#define LOAD_WORD 0x5000 /* read word (4 bytes) from device reg */
+#define DBDMA_NOP 0x6000 /* do nothing */
+#define DBDMA_STOP 0x7000 /* suspend processing */
+
+/* Key values in command field */
+
+#define KEY_MASK 0x0700
+#define KEY_STREAM0 0x0000 /* usual data stream */
+#define KEY_STREAM1 0x0100 /* control/status stream */
+#define KEY_STREAM2 0x0200 /* device-dependent stream */
+#define KEY_STREAM3 0x0300 /* device-dependent stream */
+#define KEY_STREAM4 0x0400 /* reserved */
+#define KEY_REGS 0x0500 /* device register space */
+#define KEY_SYSTEM 0x0600 /* system memory-mapped space */
+#define KEY_DEVICE 0x0700 /* device memory-mapped space */
+
+/* Interrupt control values in command field */
+
+#define INTR_MASK 0x0030
+#define INTR_NEVER 0x0000 /* don't interrupt */
+#define INTR_IFSET 0x0010 /* intr if condition bit is 1 */
+#define INTR_IFCLR 0x0020 /* intr if condition bit is 0 */
+#define INTR_ALWAYS 0x0030 /* always interrupt */
+
+/* Branch control values in command field */
+
+#define BR_MASK 0x000c
+#define BR_NEVER 0x0000 /* don't branch */
+#define BR_IFSET 0x0004 /* branch if condition bit is 1 */
+#define BR_IFCLR 0x0008 /* branch if condition bit is 0 */
+#define BR_ALWAYS 0x000c /* always branch */
+
+/* Wait control values in command field */
+
+#define WAIT_MASK 0x0003
+#define WAIT_NEVER 0x0000 /* don't wait */
+#define WAIT_IFSET 0x0001 /* wait if condition bit is 1 */
+#define WAIT_IFCLR 0x0002 /* wait if condition bit is 0 */
+#define WAIT_ALWAYS 0x0003 /* always wait */
+
+typedef struct DBDMA_channel {
+ int channel;
+ uint32_t regs[DBDMA_REGS];
+ qemu_irq irq;
+ DBDMA_io io;
+ DBDMA_rw rw;
+ DBDMA_flush flush;
+ dbdma_cmd current;
+ int processing;
+} DBDMA_channel;
+
+#ifdef DEBUG_DBDMA
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
+{
+ printf("dbdma_cmd %p\n", cmd);
+ printf(" req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
+ printf(" command 0x%04x\n", le16_to_cpu(cmd->command));
+ printf(" phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
+ printf(" cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
+ printf(" res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
+ printf(" xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
+}
+#else
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
+{
+}
+#endif
+static void dbdma_cmdptr_load(DBDMA_channel *ch)
+{
+ DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n",
+ ch->regs[DBDMA_CMDPTR_LO]);
+ cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO],
+ (uint8_t*)&ch->current, sizeof(dbdma_cmd));
+}
+
+static void dbdma_cmdptr_save(DBDMA_channel *ch)
+{
+ DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n",
+ ch->regs[DBDMA_CMDPTR_LO]);
+ DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n",
+ le16_to_cpu(ch->current.xfer_status),
+ le16_to_cpu(ch->current.res_count));
+ cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO],
+ (uint8_t*)&ch->current, sizeof(dbdma_cmd));
+}
+
+static void kill_channel(DBDMA_channel *ch)
+{
+ DBDMA_DPRINTF("kill_channel\n");
+
+ ch->regs[DBDMA_STATUS] |= DEAD;
+ ch->regs[DBDMA_STATUS] &= ~ACTIVE;
+
+ qemu_irq_raise(ch->irq);
+}
+
+static void conditional_interrupt(DBDMA_channel *ch)
+{
+ dbdma_cmd *current = &ch->current;
+ uint16_t intr;
+ uint16_t sel_mask, sel_value;
+ uint32_t status;
+ int cond;
+
+ DBDMA_DPRINTF("conditional_interrupt\n");
+
+ intr = le16_to_cpu(current->command) & INTR_MASK;
+
+ switch(intr) {
+ case INTR_NEVER: /* don't interrupt */
+ return;
+ case INTR_ALWAYS: /* always interrupt */
+ qemu_irq_raise(ch->irq);
+ return;
+ }
+
+ status = ch->regs[DBDMA_STATUS] & DEVSTAT;
+
+ sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f;
+ sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f;
+
+ cond = (status & sel_mask) == (sel_value & sel_mask);
+
+ switch(intr) {
+ case INTR_IFSET: /* intr if condition bit is 1 */
+ if (cond)
+ qemu_irq_raise(ch->irq);
+ return;
+ case INTR_IFCLR: /* intr if condition bit is 0 */
+ if (!cond)
+ qemu_irq_raise(ch->irq);
+ return;
+ }
+}
+
+static int conditional_wait(DBDMA_channel *ch)
+{
+ dbdma_cmd *current = &ch->current;
+ uint16_t wait;
+ uint16_t sel_mask, sel_value;
+ uint32_t status;
+ int cond;
+
+ DBDMA_DPRINTF("conditional_wait\n");
+
+ wait = le16_to_cpu(current->command) & WAIT_MASK;
+
+ switch(wait) {
+ case WAIT_NEVER: /* don't wait */
+ return 0;
+ case WAIT_ALWAYS: /* always wait */
+ return 1;
+ }
+
+ status = ch->regs[DBDMA_STATUS] & DEVSTAT;
+
+ sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f;
+ sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f;
+
+ cond = (status & sel_mask) == (sel_value & sel_mask);
+
+ switch(wait) {
+ case WAIT_IFSET: /* wait if condition bit is 1 */
+ if (cond)
+ return 1;
+ return 0;
+ case WAIT_IFCLR: /* wait if condition bit is 0 */
+ if (!cond)
+ return 1;
+ return 0;
+ }
+ return 0;
+}
+
+static void next(DBDMA_channel *ch)
+{
+ uint32_t cp;
+
+ ch->regs[DBDMA_STATUS] &= ~BT;
+
+ cp = ch->regs[DBDMA_CMDPTR_LO];
+ ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd);
+ dbdma_cmdptr_load(ch);
+}
+
+static void branch(DBDMA_channel *ch)
+{
+ dbdma_cmd *current = &ch->current;
+
+ ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep;
+ ch->regs[DBDMA_STATUS] |= BT;
+ dbdma_cmdptr_load(ch);
+}
+
+static void conditional_branch(DBDMA_channel *ch)
+{
+ dbdma_cmd *current = &ch->current;
+ uint16_t br;
+ uint16_t sel_mask, sel_value;
+ uint32_t status;
+ int cond;
+
+ DBDMA_DPRINTF("conditional_branch\n");
+
+ /* check if we must branch */
+
+ br = le16_to_cpu(current->command) & BR_MASK;
+
+ switch(br) {
+ case BR_NEVER: /* don't branch */
+ next(ch);
+ return;
+ case BR_ALWAYS: /* always branch */
+ branch(ch);
+ return;
+ }
+
+ status = ch->regs[DBDMA_STATUS] & DEVSTAT;
+
+ sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f;
+ sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f;
+
+ cond = (status & sel_mask) == (sel_value & sel_mask);
+
+ switch(br) {
+ case BR_IFSET: /* branch if condition bit is 1 */
+ if (cond)
+ branch(ch);
+ else
+ next(ch);
+ return;
+ case BR_IFCLR: /* branch if condition bit is 0 */
+ if (!cond)
+ branch(ch);
+ else
+ next(ch);
+ return;
+ }
+}
+
+static QEMUBH *dbdma_bh;
+static void channel_run(DBDMA_channel *ch);
+
+static void dbdma_end(DBDMA_io *io)
+{
+ DBDMA_channel *ch = io->channel;
+ dbdma_cmd *current = &ch->current;
+
+ if (conditional_wait(ch))
+ goto wait;
+
+ current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+ current->res_count = cpu_to_le16(io->len);
+ dbdma_cmdptr_save(ch);
+ if (io->is_last)
+ ch->regs[DBDMA_STATUS] &= ~FLUSH;
+
+ conditional_interrupt(ch);
+ conditional_branch(ch);
+
+wait:
+ ch->processing = 0;
+ if ((ch->regs[DBDMA_STATUS] & RUN) &&
+ (ch->regs[DBDMA_STATUS] & ACTIVE))
+ channel_run(ch);
+}
+
+static void start_output(DBDMA_channel *ch, int key, uint32_t addr,
+ uint16_t req_count, int is_last)
+{
+ DBDMA_DPRINTF("start_output\n");
+
+ /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+ * are not implemented in the mac-io chip
+ */
+
+ DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
+ if (!addr || key > KEY_STREAM3) {
+ kill_channel(ch);
+ return;
+ }
+
+ ch->io.addr = addr;
+ ch->io.len = req_count;
+ ch->io.is_last = is_last;
+ ch->io.dma_end = dbdma_end;
+ ch->io.is_dma_out = 1;
+ ch->processing = 1;
+ if (ch->rw) {
+ ch->rw(&ch->io);
+ }
+}
+
+static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
+ uint16_t req_count, int is_last)
+{
+ DBDMA_DPRINTF("start_input\n");
+
+ /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+ * are not implemented in the mac-io chip
+ */
+
+ if (!addr || key > KEY_STREAM3) {
+ kill_channel(ch);
+ return;
+ }
+
+ ch->io.addr = addr;
+ ch->io.len = req_count;
+ ch->io.is_last = is_last;
+ ch->io.dma_end = dbdma_end;
+ ch->io.is_dma_out = 0;
+ ch->processing = 1;
+ if (ch->rw) {
+ ch->rw(&ch->io);
+ }
+}
+
+static void load_word(DBDMA_channel *ch, int key, uint32_t addr,
+ uint16_t len)
+{
+ dbdma_cmd *current = &ch->current;
+ uint32_t val;
+
+ DBDMA_DPRINTF("load_word\n");
+
+ /* only implements KEY_SYSTEM */
+
+ if (key != KEY_SYSTEM) {
+ printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key);
+ kill_channel(ch);
+ return;
+ }
+
+ cpu_physical_memory_read(addr, (uint8_t*)&val, len);
+
+ if (len == 2)
+ val = (val << 16) | (current->cmd_dep & 0x0000ffff);
+ else if (len == 1)
+ val = (val << 24) | (current->cmd_dep & 0x00ffffff);
+
+ current->cmd_dep = val;
+
+ if (conditional_wait(ch))
+ goto wait;
+
+ current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+ dbdma_cmdptr_save(ch);
+ ch->regs[DBDMA_STATUS] &= ~FLUSH;
+
+ conditional_interrupt(ch);
+ next(ch);
+
+wait:
+ qemu_bh_schedule(dbdma_bh);
+}
+
+static void store_word(DBDMA_channel *ch, int key, uint32_t addr,
+ uint16_t len)
+{
+ dbdma_cmd *current = &ch->current;
+ uint32_t val;
+
+ DBDMA_DPRINTF("store_word\n");
+
+ /* only implements KEY_SYSTEM */
+
+ if (key != KEY_SYSTEM) {
+ printf("DBDMA: STORE_WORD, unimplemented key %x\n", key);
+ kill_channel(ch);
+ return;
+ }
+
+ val = current->cmd_dep;
+ if (len == 2)
+ val >>= 16;
+ else if (len == 1)
+ val >>= 24;
+
+ cpu_physical_memory_write(addr, (uint8_t*)&val, len);
+
+ if (conditional_wait(ch))
+ goto wait;
+
+ current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+ dbdma_cmdptr_save(ch);
+ ch->regs[DBDMA_STATUS] &= ~FLUSH;
+
+ conditional_interrupt(ch);
+ next(ch);
+
+wait:
+ qemu_bh_schedule(dbdma_bh);
+}
+
+static void nop(DBDMA_channel *ch)
+{
+ dbdma_cmd *current = &ch->current;
+
+ if (conditional_wait(ch))
+ goto wait;
+
+ current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+ dbdma_cmdptr_save(ch);
+
+ conditional_interrupt(ch);
+ conditional_branch(ch);
+
+wait:
+ qemu_bh_schedule(dbdma_bh);
+}
+
+static void stop(DBDMA_channel *ch)
+{
+ ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH);
+
+ /* the stop command does not increment command pointer */
+}
+
+static void channel_run(DBDMA_channel *ch)
+{
+ dbdma_cmd *current = &ch->current;
+ uint16_t cmd, key;
+ uint16_t req_count;
+ uint32_t phy_addr;
+
+ DBDMA_DPRINTF("channel_run\n");
+ dump_dbdma_cmd(current);
+
+ /* clear WAKE flag at command fetch */
+
+ ch->regs[DBDMA_STATUS] &= ~WAKE;
+
+ cmd = le16_to_cpu(current->command) & COMMAND_MASK;
+
+ switch (cmd) {
+ case DBDMA_NOP:
+ nop(ch);
+ return;
+
+ case DBDMA_STOP:
+ stop(ch);
+ return;
+ }
+
+ key = le16_to_cpu(current->command) & 0x0700;
+ req_count = le16_to_cpu(current->req_count);
+ phy_addr = le32_to_cpu(current->phy_addr);
+
+ if (key == KEY_STREAM4) {
+ printf("command %x, invalid key 4\n", cmd);
+ kill_channel(ch);
+ return;
+ }
+
+ switch (cmd) {
+ case OUTPUT_MORE:
+ start_output(ch, key, phy_addr, req_count, 0);
+ return;
+
+ case OUTPUT_LAST:
+ start_output(ch, key, phy_addr, req_count, 1);
+ return;
+
+ case INPUT_MORE:
+ start_input(ch, key, phy_addr, req_count, 0);
+ return;
+
+ case INPUT_LAST:
+ start_input(ch, key, phy_addr, req_count, 1);
+ return;
+ }
+
+ if (key < KEY_REGS) {
+ printf("command %x, invalid key %x\n", cmd, key);
+ key = KEY_SYSTEM;
+ }
+
+ /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits
+ * and BRANCH is invalid
+ */
+
+ req_count = req_count & 0x0007;
+ if (req_count & 0x4) {
+ req_count = 4;
+ phy_addr &= ~3;
+ } else if (req_count & 0x2) {
+ req_count = 2;
+ phy_addr &= ~1;
+ } else
+ req_count = 1;
+
+ switch (cmd) {
+ case LOAD_WORD:
+ load_word(ch, key, phy_addr, req_count);
+ return;
+
+ case STORE_WORD:
+ store_word(ch, key, phy_addr, req_count);
+ return;
+ }
+}
+
+static void DBDMA_run (DBDMA_channel *ch)
+{
+ int channel;
+
+ for (channel = 0; channel < DBDMA_CHANNELS; channel++, ch++) {
+ uint32_t status = ch->regs[DBDMA_STATUS];
+ if (!ch->processing && (status & RUN) && (status & ACTIVE))
+ channel_run(ch);
+ }
+}
+
+static void DBDMA_run_bh(void *opaque)
+{
+ DBDMA_channel *ch = opaque;
+
+ DBDMA_DPRINTF("DBDMA_run_bh\n");
+
+ DBDMA_run(ch);
+}
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+ DBDMA_rw rw, DBDMA_flush flush,
+ void *opaque)
+{
+ DBDMA_channel *ch = ( DBDMA_channel *)dbdma + nchan;
+
+ DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
+
+ ch->irq = irq;
+ ch->channel = nchan;
+ ch->rw = rw;
+ ch->flush = flush;
+ ch->io.opaque = opaque;
+ ch->io.channel = ch;
+}
+
+void DBDMA_schedule(void)
+{
+ qemu_notify_event();
+}
+
+static void
+dbdma_control_write(DBDMA_channel *ch)
+{
+ uint16_t mask, value;
+ uint32_t status;
+
+ mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff;
+ value = ch->regs[DBDMA_CONTROL] & 0xffff;
+
+ value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
+
+ status = ch->regs[DBDMA_STATUS];
+
+ status = (value & mask) | (status & ~mask);
+
+ if (status & WAKE)
+ status |= ACTIVE;
+ if (status & RUN) {
+ status |= ACTIVE;
+ status &= ~DEAD;
+ }
+ if (status & PAUSE)
+ status &= ~ACTIVE;
+ if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
+ /* RUN is cleared */
+ status &= ~(ACTIVE|DEAD);
+ }
+
+ DBDMA_DPRINTF(" status 0x%08x\n", status);
+
+ ch->regs[DBDMA_STATUS] = status;
+
+ if (status & ACTIVE)
+ qemu_bh_schedule(dbdma_bh);
+ if ((status & FLUSH) && ch->flush)
+ ch->flush(&ch->io);
+}
+
+static void dbdma_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ int channel = addr >> DBDMA_CHANNEL_SHIFT;
+ DBDMA_channel *ch = (DBDMA_channel *)opaque + channel;
+ int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+ DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+ DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+ (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+ /* cmdptr cannot be modified if channel is RUN or ACTIVE */
+
+ if (reg == DBDMA_CMDPTR_LO &&
+ (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE)))
+ return;
+
+ ch->regs[reg] = value;
+
+ switch(reg) {
+ case DBDMA_CONTROL:
+ dbdma_control_write(ch);
+ break;
+ case DBDMA_CMDPTR_LO:
+ /* 16-byte aligned */
+ ch->regs[DBDMA_CMDPTR_LO] &= ~0xf;
+ dbdma_cmdptr_load(ch);
+ break;
+ case DBDMA_STATUS:
+ case DBDMA_INTR_SEL:
+ case DBDMA_BRANCH_SEL:
+ case DBDMA_WAIT_SEL:
+ /* nothing to do */
+ break;
+ case DBDMA_XFER_MODE:
+ case DBDMA_CMDPTR_HI:
+ case DBDMA_DATA2PTR_HI:
+ case DBDMA_DATA2PTR_LO:
+ case DBDMA_ADDRESS_HI:
+ case DBDMA_BRANCH_ADDR_HI:
+ case DBDMA_RES1:
+ case DBDMA_RES2:
+ case DBDMA_RES3:
+ case DBDMA_RES4:
+ /* unused */
+ break;
+ }
+}
+
+static uint32_t dbdma_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t value;
+ int channel = addr >> DBDMA_CHANNEL_SHIFT;
+ DBDMA_channel *ch = (DBDMA_channel *)opaque + channel;
+ int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+ value = ch->regs[reg];
+
+ DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
+ DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+ (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+ switch(reg) {
+ case DBDMA_CONTROL:
+ value = 0;
+ break;
+ case DBDMA_STATUS:
+ case DBDMA_CMDPTR_LO:
+ case DBDMA_INTR_SEL:
+ case DBDMA_BRANCH_SEL:
+ case DBDMA_WAIT_SEL:
+ /* nothing to do */
+ break;
+ case DBDMA_XFER_MODE:
+ case DBDMA_CMDPTR_HI:
+ case DBDMA_DATA2PTR_HI:
+ case DBDMA_DATA2PTR_LO:
+ case DBDMA_ADDRESS_HI:
+ case DBDMA_BRANCH_ADDR_HI:
+ /* unused */
+ value = 0;
+ break;
+ case DBDMA_RES1:
+ case DBDMA_RES2:
+ case DBDMA_RES3:
+ case DBDMA_RES4:
+ /* reserved */
+ break;
+ }
+
+ return value;
+}
+
+static CPUWriteMemoryFunc * const dbdma_write[] = {
+ NULL,
+ NULL,
+ dbdma_writel,
+};
+
+static CPUReadMemoryFunc * const dbdma_read[] = {
+ NULL,
+ NULL,
+ dbdma_readl,
+};
+
+static void dbdma_save(QEMUFile *f, void *opaque)
+{
+ DBDMA_channel *s = opaque;
+ unsigned int i, j;
+
+ for (i = 0; i < DBDMA_CHANNELS; i++)
+ for (j = 0; j < DBDMA_REGS; j++)
+ qemu_put_be32s(f, &s[i].regs[j]);
+}
+
+static int dbdma_load(QEMUFile *f, void *opaque, int version_id)
+{
+ DBDMA_channel *s = opaque;
+ unsigned int i, j;
+
+ if (version_id != 2)
+ return -EINVAL;
+
+ for (i = 0; i < DBDMA_CHANNELS; i++)
+ for (j = 0; j < DBDMA_REGS; j++)
+ qemu_get_be32s(f, &s[i].regs[j]);
+
+ return 0;
+}
+
+static void dbdma_reset(void *opaque)
+{
+ DBDMA_channel *s = opaque;
+ int i;
+
+ for (i = 0; i < DBDMA_CHANNELS; i++)
+ memset(s[i].regs, 0, DBDMA_SIZE);
+}
+
+void* DBDMA_init (int *dbdma_mem_index)
+{
+ DBDMA_channel *s;
+
+ s = qemu_mallocz(sizeof(DBDMA_channel) * DBDMA_CHANNELS);
+
+ *dbdma_mem_index = cpu_register_io_memory(dbdma_read, dbdma_write, s,
+ DEVICE_LITTLE_ENDIAN);
+ register_savevm(NULL, "dbdma", -1, 1, dbdma_save, dbdma_load, s);
+ qemu_register_reset(dbdma_reset, s);
+
+ dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
+
+ return s;
+}
diff --git a/hw/mac_dbdma.h b/hw/mac_dbdma.h
new file mode 100644
index 0000000..d236c5b
--- /dev/null
+++ b/hw/mac_dbdma.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+typedef struct DBDMA_io DBDMA_io;
+
+typedef void (*DBDMA_flush)(DBDMA_io *io);
+typedef void (*DBDMA_rw)(DBDMA_io *io);
+typedef void (*DBDMA_end)(DBDMA_io *io);
+struct DBDMA_io {
+ void *opaque;
+ void *channel;
+ target_phys_addr_t addr;
+ int len;
+ int is_last;
+ int is_dma_out;
+ DBDMA_end dma_end;
+};
+
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+ DBDMA_rw rw, DBDMA_flush flush,
+ void *opaque);
+void DBDMA_schedule(void);
+void* DBDMA_init (int *dbdma_mem_index);
diff --git a/hw/mac_nvram.c b/hw/mac_nvram.c
new file mode 100644
index 0000000..c2a2fc2
--- /dev/null
+++ b/hw/mac_nvram.c
@@ -0,0 +1,195 @@
+/*
+ * PowerMac NVRAM emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "firmware_abi.h"
+#include "sysemu.h"
+#include "ppc_mac.h"
+
+/* debug NVR */
+//#define DEBUG_NVR
+
+#ifdef DEBUG_NVR
+#define NVR_DPRINTF(fmt, ...) \
+ do { printf("NVR: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define NVR_DPRINTF(fmt, ...)
+#endif
+
+struct MacIONVRAMState {
+ target_phys_addr_t size;
+ int mem_index;
+ unsigned int it_shift;
+ uint8_t *data;
+};
+
+#define DEF_SYSTEM_SIZE 0xc10
+
+/* Direct access to NVRAM */
+uint32_t macio_nvram_read (void *opaque, uint32_t addr)
+{
+ MacIONVRAMState *s = opaque;
+ uint32_t ret;
+
+ if (addr < s->size)
+ ret = s->data[addr];
+ else
+ ret = -1;
+ NVR_DPRINTF("read addr %04x val %x\n", addr, ret);
+
+ return ret;
+}
+
+void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ MacIONVRAMState *s = opaque;
+
+ NVR_DPRINTF("write addr %04x val %x\n", addr, val);
+ if (addr < s->size)
+ s->data[addr] = val;
+}
+
+/* macio style NVRAM device */
+static void macio_nvram_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ MacIONVRAMState *s = opaque;
+
+ addr = (addr >> s->it_shift) & (s->size - 1);
+ s->data[addr] = value;
+ NVR_DPRINTF("writeb addr %04x val %x\n", (int)addr, value);
+}
+
+static uint32_t macio_nvram_readb (void *opaque, target_phys_addr_t addr)
+{
+ MacIONVRAMState *s = opaque;
+ uint32_t value;
+
+ addr = (addr >> s->it_shift) & (s->size - 1);
+ value = s->data[addr];
+ NVR_DPRINTF("readb addr %04x val %x\n", (int)addr, value);
+
+ return value;
+}
+
+static CPUWriteMemoryFunc * const nvram_write[] = {
+ &macio_nvram_writeb,
+ &macio_nvram_writeb,
+ &macio_nvram_writeb,
+};
+
+static CPUReadMemoryFunc * const nvram_read[] = {
+ &macio_nvram_readb,
+ &macio_nvram_readb,
+ &macio_nvram_readb,
+};
+
+static void macio_nvram_save(QEMUFile *f, void *opaque)
+{
+ MacIONVRAMState *s = (MacIONVRAMState *)opaque;
+
+ qemu_put_buffer(f, s->data, s->size);
+}
+
+static int macio_nvram_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MacIONVRAMState *s = (MacIONVRAMState *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_buffer(f, s->data, s->size);
+
+ return 0;
+}
+
+static void macio_nvram_reset(void *opaque)
+{
+}
+
+MacIONVRAMState *macio_nvram_init (int *mem_index, target_phys_addr_t size,
+ unsigned int it_shift)
+{
+ MacIONVRAMState *s;
+
+ s = qemu_mallocz(sizeof(MacIONVRAMState));
+ s->data = qemu_mallocz(size);
+ s->size = size;
+ s->it_shift = it_shift;
+
+ s->mem_index = cpu_register_io_memory(nvram_read, nvram_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ *mem_index = s->mem_index;
+ register_savevm(NULL, "macio_nvram", -1, 1, macio_nvram_save,
+ macio_nvram_load, s);
+ qemu_register_reset(macio_nvram_reset, s);
+
+ return s;
+}
+
+void macio_nvram_map (void *opaque, target_phys_addr_t mem_base)
+{
+ MacIONVRAMState *s;
+
+ s = opaque;
+ cpu_register_physical_memory(mem_base, s->size << s->it_shift,
+ s->mem_index);
+}
+
+/* Set up a system OpenBIOS NVRAM partition */
+void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len)
+{
+ unsigned int i;
+ uint32_t start = 0, end;
+ struct OpenBIOS_nvpart_v1 *part_header;
+
+ // OpenBIOS nvram variables
+ // Variable partition
+ part_header = (struct OpenBIOS_nvpart_v1 *)nvr->data;
+ part_header->signature = OPENBIOS_PART_SYSTEM;
+ pstrcpy(part_header->name, sizeof(part_header->name), "system");
+
+ end = start + sizeof(struct OpenBIOS_nvpart_v1);
+ for (i = 0; i < nb_prom_envs; i++)
+ end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]);
+
+ // End marker
+ nvr->data[end++] = '\0';
+
+ end = start + ((end - start + 15) & ~15);
+ /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for
+ new variables. */
+ if (end < DEF_SYSTEM_SIZE)
+ end = DEF_SYSTEM_SIZE;
+ OpenBIOS_finish_partition(part_header, end - start);
+
+ // free partition
+ start = end;
+ part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start];
+ part_header->signature = OPENBIOS_PART_FREE;
+ pstrcpy(part_header->name, sizeof(part_header->name), "free");
+
+ end = len;
+ OpenBIOS_finish_partition(part_header, end - start);
+}
diff --git a/hw/macio.c b/hw/macio.c
new file mode 100644
index 0000000..789ca55
--- /dev/null
+++ b/hw/macio.c
@@ -0,0 +1,118 @@
+/*
+ * PowerMac MacIO device emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc_mac.h"
+#include "pci.h"
+#include "escc.h"
+
+typedef struct macio_state_t macio_state_t;
+struct macio_state_t {
+ int is_oldworld;
+ int pic_mem_index;
+ int dbdma_mem_index;
+ int cuda_mem_index;
+ int escc_mem_index;
+ void *nvram;
+ int nb_ide;
+ int ide_mem_index[4];
+};
+
+static void macio_map (PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ macio_state_t *macio_state;
+ int i;
+
+ macio_state = (macio_state_t *)(pci_dev + 1);
+ if (macio_state->pic_mem_index >= 0) {
+ if (macio_state->is_oldworld) {
+ /* Heathrow PIC */
+ cpu_register_physical_memory(addr + 0x00000, 0x1000,
+ macio_state->pic_mem_index);
+ } else {
+ /* OpenPIC */
+ cpu_register_physical_memory(addr + 0x40000, 0x40000,
+ macio_state->pic_mem_index);
+ }
+ }
+ if (macio_state->dbdma_mem_index >= 0) {
+ cpu_register_physical_memory(addr + 0x08000, 0x1000,
+ macio_state->dbdma_mem_index);
+ }
+ if (macio_state->escc_mem_index >= 0) {
+ cpu_register_physical_memory(addr + 0x13000, ESCC_SIZE << 4,
+ macio_state->escc_mem_index);
+ }
+ if (macio_state->cuda_mem_index >= 0) {
+ cpu_register_physical_memory(addr + 0x16000, 0x2000,
+ macio_state->cuda_mem_index);
+ }
+ for (i = 0; i < macio_state->nb_ide; i++) {
+ if (macio_state->ide_mem_index[i] >= 0) {
+ cpu_register_physical_memory(addr + 0x1f000 + (i * 0x1000), 0x1000,
+ macio_state->ide_mem_index[i]);
+ }
+ }
+ if (macio_state->nvram != NULL)
+ macio_nvram_map(macio_state->nvram, addr + 0x60000);
+}
+
+void macio_init (PCIBus *bus, int device_id, int is_oldworld, int pic_mem_index,
+ int dbdma_mem_index, int cuda_mem_index, void *nvram,
+ int nb_ide, int *ide_mem_index, int escc_mem_index)
+{
+ PCIDevice *d;
+ macio_state_t *macio_state;
+ int i;
+
+ d = pci_register_device(bus, "macio",
+ sizeof(PCIDevice) + sizeof(macio_state_t),
+ -1, NULL, NULL);
+ macio_state = (macio_state_t *)(d + 1);
+ macio_state->is_oldworld = is_oldworld;
+ macio_state->pic_mem_index = pic_mem_index;
+ macio_state->dbdma_mem_index = dbdma_mem_index;
+ macio_state->cuda_mem_index = cuda_mem_index;
+ macio_state->escc_mem_index = escc_mem_index;
+ macio_state->nvram = nvram;
+ if (nb_ide > 4)
+ nb_ide = 4;
+ macio_state->nb_ide = nb_ide;
+ for (i = 0; i < nb_ide; i++)
+ macio_state->ide_mem_index[i] = ide_mem_index[i];
+ for (; i < 4; i++)
+ macio_state->ide_mem_index[i] = -1;
+ /* Note: this code is strongly inspirated from the corresponding code
+ in PearPC */
+
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
+ pci_config_set_device_id(d->config, device_id);
+ pci_config_set_class(d->config, PCI_CLASS_OTHERS << 8);
+
+ d->config[0x3d] = 0x01; // interrupt on pin 1
+
+ pci_register_bar(d, 0, 0x80000,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, macio_map);
+}
diff --git a/hw/mainstone.c b/hw/mainstone.c
new file mode 100644
index 0000000..58e3f86
--- /dev/null
+++ b/hw/mainstone.c
@@ -0,0 +1,158 @@
+/*
+ * PXA270-based Intel Mainstone platforms.
+ *
+ * Copyright (c) 2007 by Armin Kuster <akuster@kama-aina.net> or
+ * <akuster@mvista.com>
+ *
+ * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "pxa.h"
+#include "arm-misc.h"
+#include "net.h"
+#include "devices.h"
+#include "boards.h"
+#include "mainstone.h"
+#include "sysemu.h"
+#include "flash.h"
+#include "blockdev.h"
+
+static struct keymap map[0xE0] = {
+ [0 ... 0xDF] = { -1, -1 },
+ [0x1e] = {0,0}, /* a */
+ [0x30] = {0,1}, /* b */
+ [0x2e] = {0,2}, /* c */
+ [0x20] = {0,3}, /* d */
+ [0x12] = {0,4}, /* e */
+ [0x21] = {0,5}, /* f */
+ [0x22] = {1,0}, /* g */
+ [0x23] = {1,1}, /* h */
+ [0x17] = {1,2}, /* i */
+ [0x24] = {1,3}, /* j */
+ [0x25] = {1,4}, /* k */
+ [0x26] = {1,5}, /* l */
+ [0x32] = {2,0}, /* m */
+ [0x31] = {2,1}, /* n */
+ [0x18] = {2,2}, /* o */
+ [0x19] = {2,3}, /* p */
+ [0x10] = {2,4}, /* q */
+ [0x13] = {2,5}, /* r */
+ [0x1f] = {3,0}, /* s */
+ [0x14] = {3,1}, /* t */
+ [0x16] = {3,2}, /* u */
+ [0x2f] = {3,3}, /* v */
+ [0x11] = {3,4}, /* w */
+ [0x2d] = {3,5}, /* x */
+ [0x15] = {4,2}, /* y */
+ [0x2c] = {4,3}, /* z */
+ [0xc7] = {5,0}, /* Home */
+ [0x2a] = {5,1}, /* shift */
+ [0x39] = {5,2}, /* space */
+ [0x39] = {5,3}, /* space */
+ [0x1c] = {5,5}, /* enter */
+ [0xc8] = {6,0}, /* up */
+ [0xd0] = {6,1}, /* down */
+ [0xcb] = {6,2}, /* left */
+ [0xcd] = {6,3}, /* right */
+};
+
+enum mainstone_model_e { mainstone };
+
+#define MAINSTONE_RAM 0x04000000
+#define MAINSTONE_ROM 0x00800000
+#define MAINSTONE_FLASH 0x02000000
+
+static struct arm_boot_info mainstone_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = 0x04000000,
+};
+
+static void mainstone_common_init(ram_addr_t ram_size,
+ const char *kernel_filename,
+ const char *kernel_cmdline, const char *initrd_filename,
+ const char *cpu_model, enum mainstone_model_e model, int arm_id)
+{
+ uint32_t sector_len = 256 * 1024;
+ target_phys_addr_t mainstone_flash_base[] = { MST_FLASH_0, MST_FLASH_1 };
+ PXA2xxState *cpu;
+ qemu_irq *mst_irq;
+ DriveInfo *dinfo;
+ int i;
+ int be;
+
+ if (!cpu_model)
+ cpu_model = "pxa270-c5";
+
+ /* Setup CPU & memory */
+ cpu = pxa270_init(mainstone_binfo.ram_size, cpu_model);
+ cpu_register_physical_memory(0, MAINSTONE_ROM,
+ qemu_ram_alloc(NULL, "mainstone.rom",
+ MAINSTONE_ROM) | IO_MEM_ROM);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ /* There are two 32MiB flash devices on the board */
+ for (i = 0; i < 2; i ++) {
+ dinfo = drive_get(IF_PFLASH, 0, i);
+ if (!dinfo) {
+ fprintf(stderr, "Two flash images must be given with the "
+ "'pflash' parameter\n");
+ exit(1);
+ }
+
+ if (!pflash_cfi01_register(mainstone_flash_base[i],
+ qemu_ram_alloc(NULL, i ? "mainstone.flash1" :
+ "mainstone.flash0",
+ MAINSTONE_FLASH),
+ dinfo->bdrv, sector_len,
+ MAINSTONE_FLASH / sector_len, 4, 0, 0, 0, 0,
+ be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ exit(1);
+ }
+ }
+
+ mst_irq = mst_irq_init(cpu, MST_FPGA_PHYS, PXA2XX_PIC_GPIO_0);
+
+ /* setup keypad */
+ printf("map addr %p\n", &map);
+ pxa27x_register_keypad(cpu->kp, map, 0xe0);
+
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(cpu->mmc, NULL, mst_irq[MMC_IRQ]);
+
+ smc91c111_init(&nd_table[0], MST_ETH_PHYS, mst_irq[ETHERNET_IRQ]);
+
+ mainstone_binfo.kernel_filename = kernel_filename;
+ mainstone_binfo.kernel_cmdline = kernel_cmdline;
+ mainstone_binfo.initrd_filename = initrd_filename;
+ mainstone_binfo.board_id = arm_id;
+ arm_load_kernel(cpu->env, &mainstone_binfo);
+}
+
+static void mainstone_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ mainstone_common_init(ram_size, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, mainstone, 0x196);
+}
+
+static QEMUMachine mainstone2_machine = {
+ .name = "mainstone",
+ .desc = "Mainstone II (PXA27x)",
+ .init = mainstone_init,
+};
+
+static void mainstone_machine_init(void)
+{
+ qemu_register_machine(&mainstone2_machine);
+}
+
+machine_init(mainstone_machine_init);
diff --git a/hw/mainstone.h b/hw/mainstone.h
new file mode 100644
index 0000000..9618c06
--- /dev/null
+++ b/hw/mainstone.h
@@ -0,0 +1,38 @@
+/*
+ * PXA270-based Intel Mainstone platforms.
+ *
+ * Copyright (c) 2007 by Armin Kuster <akuster@kama-aina.net> or
+ * <akuster@mvista.com>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#ifndef __MAINSTONE_H__
+#define __MAINSTONE_H__
+
+/* Device addresses */
+#define MST_FPGA_PHYS 0x08000000
+#define MST_ETH_PHYS 0x10000300
+#define MST_FLASH_0 0x00000000
+#define MST_FLASH_1 0x04000000
+
+/* IRQ definitions */
+#define MMC_IRQ 0
+#define USIM_IRQ 1
+#define USBC_IRQ 2
+#define ETHERNET_IRQ 3
+#define AC97_IRQ 4
+#define PEN_IRQ 5
+#define MSINS_IRQ 6
+#define EXBRD_IRQ 7
+#define S0_CD_IRQ 9
+#define S0_STSCHG_IRQ 10
+#define S0_IRQ 11
+#define S1_CD_IRQ 13
+#define S1_STSCHG_IRQ 14
+#define S1_IRQ 15
+
+extern qemu_irq
+*mst_irq_init(PXA2xxState *cpu, uint32_t base, int irq);
+
+#endif /* __MAINSTONE_H__ */
diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c
new file mode 100644
index 0000000..3eff925
--- /dev/null
+++ b/hw/marvell_88w8618_audio.c
@@ -0,0 +1,299 @@
+/*
+ * Marvell 88w8618 audio emulation extracted from
+ * Marvell MV88w8618 / Freecom MusicPal emulation.
+ *
+ * Copyright (c) 2008 Jan Kiszka
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include "sysbus.h"
+#include "hw.h"
+#include "i2c.h"
+#include "sysbus.h"
+#include "audio/audio.h"
+
+#define MP_AUDIO_SIZE 0x00001000
+
+/* Audio register offsets */
+#define MP_AUDIO_PLAYBACK_MODE 0x00
+#define MP_AUDIO_CLOCK_DIV 0x18
+#define MP_AUDIO_IRQ_STATUS 0x20
+#define MP_AUDIO_IRQ_ENABLE 0x24
+#define MP_AUDIO_TX_START_LO 0x28
+#define MP_AUDIO_TX_THRESHOLD 0x2C
+#define MP_AUDIO_TX_STATUS 0x38
+#define MP_AUDIO_TX_START_HI 0x40
+
+/* Status register and IRQ enable bits */
+#define MP_AUDIO_TX_HALF (1 << 6)
+#define MP_AUDIO_TX_FULL (1 << 7)
+
+/* Playback mode bits */
+#define MP_AUDIO_16BIT_SAMPLE (1 << 0)
+#define MP_AUDIO_PLAYBACK_EN (1 << 7)
+#define MP_AUDIO_CLOCK_24MHZ (1 << 9)
+#define MP_AUDIO_MONO (1 << 14)
+
+typedef struct mv88w8618_audio_state {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t playback_mode;
+ uint32_t status;
+ uint32_t irq_enable;
+ uint32_t phys_buf;
+ uint32_t target_buffer;
+ uint32_t threshold;
+ uint32_t play_pos;
+ uint32_t last_free;
+ uint32_t clock_div;
+ DeviceState *wm;
+} mv88w8618_audio_state;
+
+static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in)
+{
+ mv88w8618_audio_state *s = opaque;
+ int16_t *codec_buffer;
+ int8_t buf[4096];
+ int8_t *mem_buffer;
+ int pos, block_size;
+
+ if (!(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) {
+ return;
+ }
+ if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {
+ free_out <<= 1;
+ }
+ if (!(s->playback_mode & MP_AUDIO_MONO)) {
+ free_out <<= 1;
+ }
+ block_size = s->threshold / 2;
+ if (free_out - s->last_free < block_size) {
+ return;
+ }
+ if (block_size > 4096) {
+ return;
+ }
+ cpu_physical_memory_read(s->target_buffer + s->play_pos, (void *)buf,
+ block_size);
+ mem_buffer = buf;
+ if (s->playback_mode & MP_AUDIO_16BIT_SAMPLE) {
+ if (s->playback_mode & MP_AUDIO_MONO) {
+ codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
+ for (pos = 0; pos < block_size; pos += 2) {
+ *codec_buffer++ = *(int16_t *)mem_buffer;
+ *codec_buffer++ = *(int16_t *)mem_buffer;
+ mem_buffer += 2;
+ }
+ } else {
+ memcpy(wm8750_dac_buffer(s->wm, block_size >> 2),
+ (uint32_t *)mem_buffer, block_size);
+ }
+ } else {
+ if (s->playback_mode & MP_AUDIO_MONO) {
+ codec_buffer = wm8750_dac_buffer(s->wm, block_size);
+ for (pos = 0; pos < block_size; pos++) {
+ *codec_buffer++ = cpu_to_le16(256 * *mem_buffer);
+ *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
+ }
+ } else {
+ codec_buffer = wm8750_dac_buffer(s->wm, block_size >> 1);
+ for (pos = 0; pos < block_size; pos += 2) {
+ *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
+ *codec_buffer++ = cpu_to_le16(256 * *mem_buffer++);
+ }
+ }
+ }
+ wm8750_dac_commit(s->wm);
+
+ s->last_free = free_out - block_size;
+
+ if (s->play_pos == 0) {
+ s->status |= MP_AUDIO_TX_HALF;
+ s->play_pos = block_size;
+ } else {
+ s->status |= MP_AUDIO_TX_FULL;
+ s->play_pos = 0;
+ }
+
+ if (s->status & s->irq_enable) {
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static void mv88w8618_audio_clock_update(mv88w8618_audio_state *s)
+{
+ int rate;
+
+ if (s->playback_mode & MP_AUDIO_CLOCK_24MHZ) {
+ rate = 24576000 / 64; /* 24.576MHz */
+ } else {
+ rate = 11289600 / 64; /* 11.2896MHz */
+ }
+ rate /= ((s->clock_div >> 8) & 0xff) + 1;
+
+ wm8750_set_bclk_in(s->wm, rate);
+}
+
+static uint32_t mv88w8618_audio_read(void *opaque, target_phys_addr_t offset)
+{
+ mv88w8618_audio_state *s = opaque;
+
+ switch (offset) {
+ case MP_AUDIO_PLAYBACK_MODE:
+ return s->playback_mode;
+
+ case MP_AUDIO_CLOCK_DIV:
+ return s->clock_div;
+
+ case MP_AUDIO_IRQ_STATUS:
+ return s->status;
+
+ case MP_AUDIO_IRQ_ENABLE:
+ return s->irq_enable;
+
+ case MP_AUDIO_TX_STATUS:
+ return s->play_pos >> 2;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_audio_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mv88w8618_audio_state *s = opaque;
+
+ switch (offset) {
+ case MP_AUDIO_PLAYBACK_MODE:
+ if (value & MP_AUDIO_PLAYBACK_EN &&
+ !(s->playback_mode & MP_AUDIO_PLAYBACK_EN)) {
+ s->status = 0;
+ s->last_free = 0;
+ s->play_pos = 0;
+ }
+ s->playback_mode = value;
+ mv88w8618_audio_clock_update(s);
+ break;
+
+ case MP_AUDIO_CLOCK_DIV:
+ s->clock_div = value;
+ s->last_free = 0;
+ s->play_pos = 0;
+ mv88w8618_audio_clock_update(s);
+ break;
+
+ case MP_AUDIO_IRQ_STATUS:
+ s->status &= ~value;
+ break;
+
+ case MP_AUDIO_IRQ_ENABLE:
+ s->irq_enable = value;
+ if (s->status & s->irq_enable) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_AUDIO_TX_START_LO:
+ s->phys_buf = (s->phys_buf & 0xFFFF0000) | (value & 0xFFFF);
+ s->target_buffer = s->phys_buf;
+ s->play_pos = 0;
+ s->last_free = 0;
+ break;
+
+ case MP_AUDIO_TX_THRESHOLD:
+ s->threshold = (value + 1) * 4;
+ break;
+
+ case MP_AUDIO_TX_START_HI:
+ s->phys_buf = (s->phys_buf & 0xFFFF) | (value << 16);
+ s->target_buffer = s->phys_buf;
+ s->play_pos = 0;
+ s->last_free = 0;
+ break;
+ }
+}
+
+static void mv88w8618_audio_reset(DeviceState *d)
+{
+ mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state,
+ sysbus_from_qdev(d));
+
+ s->playback_mode = 0;
+ s->status = 0;
+ s->irq_enable = 0;
+ s->clock_div = 0;
+ s->threshold = 0;
+ s->phys_buf = 0;
+}
+
+static CPUReadMemoryFunc * const mv88w8618_audio_readfn[] = {
+ mv88w8618_audio_read,
+ mv88w8618_audio_read,
+ mv88w8618_audio_read
+};
+
+static CPUWriteMemoryFunc * const mv88w8618_audio_writefn[] = {
+ mv88w8618_audio_write,
+ mv88w8618_audio_write,
+ mv88w8618_audio_write
+};
+
+static int mv88w8618_audio_init(SysBusDevice *dev)
+{
+ mv88w8618_audio_state *s = FROM_SYSBUS(mv88w8618_audio_state, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s);
+
+ iomemtype = cpu_register_io_memory(mv88w8618_audio_readfn,
+ mv88w8618_audio_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_AUDIO_SIZE, iomemtype);
+
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_audio_vmsd = {
+ .name = "mv88w8618_audio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(playback_mode, mv88w8618_audio_state),
+ VMSTATE_UINT32(status, mv88w8618_audio_state),
+ VMSTATE_UINT32(irq_enable, mv88w8618_audio_state),
+ VMSTATE_UINT32(phys_buf, mv88w8618_audio_state),
+ VMSTATE_UINT32(target_buffer, mv88w8618_audio_state),
+ VMSTATE_UINT32(threshold, mv88w8618_audio_state),
+ VMSTATE_UINT32(play_pos, mv88w8618_audio_state),
+ VMSTATE_UINT32(last_free, mv88w8618_audio_state),
+ VMSTATE_UINT32(clock_div, mv88w8618_audio_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo mv88w8618_audio_info = {
+ .init = mv88w8618_audio_init,
+ .qdev.name = "mv88w8618_audio",
+ .qdev.size = sizeof(mv88w8618_audio_state),
+ .qdev.reset = mv88w8618_audio_reset,
+ .qdev.vmsd = &mv88w8618_audio_vmsd,
+ .qdev.props = (Property[]) {
+ {
+ .name = "wm8750",
+ .info = &qdev_prop_ptr,
+ .offset = offsetof(mv88w8618_audio_state, wm),
+ },
+ {/* end of list */}
+ }
+};
+
+static void mv88w8618_register_devices(void)
+{
+ sysbus_register_withprop(&mv88w8618_audio_info);
+}
+
+device_init(mv88w8618_register_devices)
diff --git a/hw/max111x.c b/hw/max111x.c
new file mode 100644
index 0000000..2844665
--- /dev/null
+++ b/hw/max111x.c
@@ -0,0 +1,188 @@
+/*
+ * Maxim MAX1110/1111 ADC chip emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPLv2.
+ */
+
+#include "ssi.h"
+
+typedef struct {
+ SSISlave ssidev;
+ qemu_irq interrupt;
+ uint8_t tb1, rb2, rb3;
+ int cycle;
+
+ int input[8];
+ int inputs, com;
+} MAX111xState;
+
+/* Control-byte bitfields */
+#define CB_PD0 (1 << 0)
+#define CB_PD1 (1 << 1)
+#define CB_SGL (1 << 2)
+#define CB_UNI (1 << 3)
+#define CB_SEL0 (1 << 4)
+#define CB_SEL1 (1 << 5)
+#define CB_SEL2 (1 << 6)
+#define CB_START (1 << 7)
+
+#define CHANNEL_NUM(v, b0, b1, b2) \
+ ((((v) >> (2 + (b0))) & 4) | \
+ (((v) >> (3 + (b1))) & 2) | \
+ (((v) >> (4 + (b2))) & 1))
+
+static uint32_t max111x_read(MAX111xState *s)
+{
+ if (!s->tb1)
+ return 0;
+
+ switch (s->cycle ++) {
+ case 1:
+ return s->rb2;
+ case 2:
+ return s->rb3;
+ }
+
+ return 0;
+}
+
+/* Interpret a control-byte */
+static void max111x_write(MAX111xState *s, uint32_t value)
+{
+ int measure, chan;
+
+ /* Ignore the value if START bit is zero */
+ if (!(value & CB_START))
+ return;
+
+ s->cycle = 0;
+
+ if (!(value & CB_PD1)) {
+ s->tb1 = 0;
+ return;
+ }
+
+ s->tb1 = value;
+
+ if (s->inputs == 8)
+ chan = CHANNEL_NUM(value, 1, 0, 2);
+ else
+ chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
+
+ if (value & CB_SGL)
+ measure = s->input[chan] - s->com;
+ else
+ measure = s->input[chan] - s->input[chan ^ 1];
+
+ if (!(value & CB_UNI))
+ measure ^= 0x80;
+
+ s->rb2 = (measure >> 2) & 0x3f;
+ s->rb3 = (measure << 6) & 0xc0;
+
+ /* FIXME: When should the IRQ be lowered? */
+ qemu_irq_raise(s->interrupt);
+}
+
+static uint32_t max111x_transfer(SSISlave *dev, uint32_t value)
+{
+ MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
+ max111x_write(s, value);
+ return max111x_read(s);
+}
+
+static void max111x_save(QEMUFile *f, void *opaque)
+{
+ MAX111xState *s = (MAX111xState *) opaque;
+ int i;
+
+ qemu_put_8s(f, &s->tb1);
+ qemu_put_8s(f, &s->rb2);
+ qemu_put_8s(f, &s->rb3);
+ qemu_put_be32(f, s->inputs);
+ qemu_put_be32(f, s->com);
+ for (i = 0; i < s->inputs; i ++)
+ qemu_put_byte(f, s->input[i]);
+}
+
+static int max111x_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MAX111xState *s = (MAX111xState *) opaque;
+ int i;
+
+ qemu_get_8s(f, &s->tb1);
+ qemu_get_8s(f, &s->rb2);
+ qemu_get_8s(f, &s->rb3);
+ if (s->inputs != qemu_get_be32(f))
+ return -EINVAL;
+ s->com = qemu_get_be32(f);
+ for (i = 0; i < s->inputs; i ++)
+ s->input[i] = qemu_get_byte(f);
+
+ return 0;
+}
+
+static int max111x_init(SSISlave *dev, int inputs)
+{
+ MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
+
+ qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
+
+ s->inputs = inputs;
+ /* TODO: add a user interface for setting these */
+ s->input[0] = 0xf0;
+ s->input[1] = 0xe0;
+ s->input[2] = 0xd0;
+ s->input[3] = 0xc0;
+ s->input[4] = 0xb0;
+ s->input[5] = 0xa0;
+ s->input[6] = 0x90;
+ s->input[7] = 0x80;
+ s->com = 0;
+
+ register_savevm(&dev->qdev, "max111x", -1, 0,
+ max111x_save, max111x_load, s);
+ return 0;
+}
+
+static int max1110_init(SSISlave *dev)
+{
+ return max111x_init(dev, 8);
+}
+
+static int max1111_init(SSISlave *dev)
+{
+ return max111x_init(dev, 4);
+}
+
+void max111x_set_input(DeviceState *dev, int line, uint8_t value)
+{
+ MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, SSI_SLAVE_FROM_QDEV(dev));
+ assert(line >= 0 && line < s->inputs);
+ s->input[line] = value;
+}
+
+static SSISlaveInfo max1110_info = {
+ .qdev.name = "max1110",
+ .qdev.size = sizeof(MAX111xState),
+ .init = max1110_init,
+ .transfer = max111x_transfer
+};
+
+static SSISlaveInfo max1111_info = {
+ .qdev.name = "max1111",
+ .qdev.size = sizeof(MAX111xState),
+ .init = max1111_init,
+ .transfer = max111x_transfer
+};
+
+static void max111x_register_devices(void)
+{
+ ssi_register_slave(&max1110_info);
+ ssi_register_slave(&max1111_info);
+}
+
+device_init(max111x_register_devices)
diff --git a/hw/max7310.c b/hw/max7310.c
new file mode 100644
index 0000000..c302eb6
--- /dev/null
+++ b/hw/max7310.c
@@ -0,0 +1,220 @@
+/*
+ * MAX7310 8-port GPIO expansion chip.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This file is licensed under GNU GPL.
+ */
+
+#include "i2c.h"
+
+typedef struct {
+ i2c_slave i2c;
+ int i2c_command_byte;
+ int len;
+
+ uint8_t level;
+ uint8_t direction;
+ uint8_t polarity;
+ uint8_t status;
+ uint8_t command;
+ qemu_irq handler[8];
+ qemu_irq *gpio_in;
+} MAX7310State;
+
+void max7310_reset(i2c_slave *i2c)
+{
+ MAX7310State *s = (MAX7310State *) i2c;
+ s->level &= s->direction;
+ s->direction = 0xff;
+ s->polarity = 0xf0;
+ s->status = 0x01;
+ s->command = 0x00;
+}
+
+static int max7310_rx(i2c_slave *i2c)
+{
+ MAX7310State *s = (MAX7310State *) i2c;
+
+ switch (s->command) {
+ case 0x00: /* Input port */
+ return s->level ^ s->polarity;
+ break;
+
+ case 0x01: /* Output port */
+ return s->level & ~s->direction;
+ break;
+
+ case 0x02: /* Polarity inversion */
+ return s->polarity;
+
+ case 0x03: /* Configuration */
+ return s->direction;
+
+ case 0x04: /* Timeout */
+ return s->status;
+ break;
+
+ case 0xff: /* Reserved */
+ return 0xff;
+
+ default:
+#ifdef VERBOSE
+ printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
+#endif
+ break;
+ }
+ return 0xff;
+}
+
+static int max7310_tx(i2c_slave *i2c, uint8_t data)
+{
+ MAX7310State *s = (MAX7310State *) i2c;
+ uint8_t diff;
+ int line;
+
+ if (s->len ++ > 1) {
+#ifdef VERBOSE
+ printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
+#endif
+ return 1;
+ }
+
+ if (s->i2c_command_byte) {
+ s->command = data;
+ s->i2c_command_byte = 0;
+ return 0;
+ }
+
+ switch (s->command) {
+ case 0x01: /* Output port */
+ for (diff = (data ^ s->level) & ~s->direction; diff;
+ diff &= ~(1 << line)) {
+ line = ffs(diff) - 1;
+ if (s->handler[line])
+ qemu_set_irq(s->handler[line], (data >> line) & 1);
+ }
+ s->level = (s->level & s->direction) | (data & ~s->direction);
+ break;
+
+ case 0x02: /* Polarity inversion */
+ s->polarity = data;
+ break;
+
+ case 0x03: /* Configuration */
+ s->level &= ~(s->direction ^ data);
+ s->direction = data;
+ break;
+
+ case 0x04: /* Timeout */
+ s->status = data;
+ break;
+
+ case 0x00: /* Input port - ignore writes */
+ break;
+ default:
+#ifdef VERBOSE
+ printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
+#endif
+ return 1;
+ }
+
+ return 0;
+}
+
+static void max7310_event(i2c_slave *i2c, enum i2c_event event)
+{
+ MAX7310State *s = (MAX7310State *) i2c;
+ s->len = 0;
+
+ switch (event) {
+ case I2C_START_SEND:
+ s->i2c_command_byte = 1;
+ break;
+ case I2C_FINISH:
+#ifdef VERBOSE
+ if (s->len == 1)
+ printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+static const VMStateDescription vmstate_max7310 = {
+ .name = "max7310",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(i2c_command_byte, MAX7310State),
+ VMSTATE_INT32(len, MAX7310State),
+ VMSTATE_UINT8(level, MAX7310State),
+ VMSTATE_UINT8(direction, MAX7310State),
+ VMSTATE_UINT8(polarity, MAX7310State),
+ VMSTATE_UINT8(status, MAX7310State),
+ VMSTATE_UINT8(command, MAX7310State),
+ VMSTATE_I2C_SLAVE(i2c, MAX7310State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void max7310_gpio_set(void *opaque, int line, int level)
+{
+ MAX7310State *s = (MAX7310State *) opaque;
+ if (line >= ARRAY_SIZE(s->handler) || line < 0)
+ hw_error("bad GPIO line");
+
+ if (level)
+ s->level |= s->direction & (1 << line);
+ else
+ s->level &= ~(s->direction & (1 << line));
+}
+
+/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
+ * but also accepts sequences that are not SMBus so return an I2C device. */
+static int max7310_init(i2c_slave *i2c)
+{
+ MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, i2c);
+
+ s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s,
+ ARRAY_SIZE(s->handler));
+
+ max7310_reset(&s->i2c);
+
+ return 0;
+}
+
+qemu_irq *max7310_gpio_in_get(i2c_slave *i2c)
+{
+ MAX7310State *s = (MAX7310State *) i2c;
+ return s->gpio_in;
+}
+
+void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
+{
+ MAX7310State *s = (MAX7310State *) i2c;
+ if (line >= ARRAY_SIZE(s->handler) || line < 0)
+ hw_error("bad GPIO line");
+
+ s->handler[line] = handler;
+}
+
+static I2CSlaveInfo max7310_info = {
+ .qdev.name = "max7310",
+ .qdev.size = sizeof(MAX7310State),
+ .qdev.vmsd = &vmstate_max7310,
+ .init = max7310_init,
+ .event = max7310_event,
+ .recv = max7310_rx,
+ .send = max7310_tx
+};
+
+static void max7310_register_devices(void)
+{
+ i2c_register_slave(&max7310_info);
+}
+
+device_init(max7310_register_devices)
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
new file mode 100644
index 0000000..a1b0e31
--- /dev/null
+++ b/hw/mc146818rtc.c
@@ -0,0 +1,657 @@
+/*
+ * QEMU MC146818 RTC emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "pc.h"
+#include "apic.h"
+#include "isa.h"
+#include "mc146818rtc.h"
+
+//#define DEBUG_CMOS
+//#define DEBUG_COALESCED
+
+#ifdef DEBUG_CMOS
+# define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
+#else
+# define CMOS_DPRINTF(format, ...) do { } while (0)
+#endif
+
+#ifdef DEBUG_COALESCED
+# define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__)
+#else
+# define DPRINTF_C(format, ...) do { } while (0)
+#endif
+
+#define RTC_REINJECT_ON_ACK_COUNT 20
+
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+#define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+#define REG_A_UIP 0x80
+
+#define REG_B_SET 0x80
+#define REG_B_PIE 0x40
+#define REG_B_AIE 0x20
+#define REG_B_UIE 0x10
+#define REG_B_SQWE 0x08
+#define REG_B_DM 0x04
+#define REG_B_24H 0x02
+
+#define REG_C_UF 0x10
+#define REG_C_IRQF 0x80
+#define REG_C_PF 0x40
+#define REG_C_AF 0x20
+
+typedef struct RTCState {
+ ISADevice dev;
+ uint8_t cmos_data[128];
+ uint8_t cmos_index;
+ struct tm current_tm;
+ int32_t base_year;
+ qemu_irq irq;
+ qemu_irq sqw_irq;
+ int it_shift;
+ /* periodic timer */
+ QEMUTimer *periodic_timer;
+ int64_t next_periodic_time;
+ /* second update */
+ int64_t next_second_time;
+ uint16_t irq_reinject_on_ack_count;
+ uint32_t irq_coalesced;
+ uint32_t period;
+ QEMUTimer *coalesced_timer;
+ QEMUTimer *second_timer;
+ QEMUTimer *second_timer2;
+} RTCState;
+
+static void rtc_set_time(RTCState *s);
+static void rtc_copy_date(RTCState *s);
+
+#ifdef TARGET_I386
+static void rtc_coalesced_timer_update(RTCState *s)
+{
+ if (s->irq_coalesced == 0) {
+ qemu_del_timer(s->coalesced_timer);
+ } else {
+ /* divide each RTC interval to 2 - 8 smaller intervals */
+ int c = MIN(s->irq_coalesced, 7) + 1;
+ int64_t next_clock = qemu_get_clock(rtc_clock) +
+ muldiv64(s->period / c, get_ticks_per_sec(), 32768);
+ qemu_mod_timer(s->coalesced_timer, next_clock);
+ }
+}
+
+static void rtc_coalesced_timer(void *opaque)
+{
+ RTCState *s = opaque;
+
+ if (s->irq_coalesced != 0) {
+ apic_reset_irq_delivered();
+ s->cmos_data[RTC_REG_C] |= 0xc0;
+ DPRINTF_C("cmos: injecting from timer\n");
+ qemu_irq_raise(s->irq);
+ if (apic_get_irq_delivered()) {
+ s->irq_coalesced--;
+ DPRINTF_C("cmos: coalesced irqs decreased to %d\n",
+ s->irq_coalesced);
+ }
+ }
+
+ rtc_coalesced_timer_update(s);
+}
+#endif
+
+static void rtc_timer_update(RTCState *s, int64_t current_time)
+{
+ int period_code, period;
+ int64_t cur_clock, next_irq_clock;
+
+ period_code = s->cmos_data[RTC_REG_A] & 0x0f;
+ if (period_code != 0
+ && ((s->cmos_data[RTC_REG_B] & REG_B_PIE)
+ || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) {
+ if (period_code <= 2)
+ period_code += 7;
+ /* period in 32 Khz cycles */
+ period = 1 << (period_code - 1);
+#ifdef TARGET_I386
+ if (period != s->period) {
+ s->irq_coalesced = (s->irq_coalesced * s->period) / period;
+ DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced);
+ }
+ s->period = period;
+#endif
+ /* compute 32 khz clock */
+ cur_clock = muldiv64(current_time, 32768, get_ticks_per_sec());
+ next_irq_clock = (cur_clock & ~(period - 1)) + period;
+ s->next_periodic_time =
+ muldiv64(next_irq_clock, get_ticks_per_sec(), 32768) + 1;
+ qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
+ } else {
+#ifdef TARGET_I386
+ s->irq_coalesced = 0;
+#endif
+ qemu_del_timer(s->periodic_timer);
+ }
+}
+
+static void rtc_periodic_timer(void *opaque)
+{
+ RTCState *s = opaque;
+
+ rtc_timer_update(s, s->next_periodic_time);
+ if (s->cmos_data[RTC_REG_B] & REG_B_PIE) {
+ s->cmos_data[RTC_REG_C] |= 0xc0;
+#ifdef TARGET_I386
+ if(rtc_td_hack) {
+ if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT)
+ s->irq_reinject_on_ack_count = 0;
+ apic_reset_irq_delivered();
+ qemu_irq_raise(s->irq);
+ if (!apic_get_irq_delivered()) {
+ s->irq_coalesced++;
+ rtc_coalesced_timer_update(s);
+ DPRINTF_C("cmos: coalesced irqs increased to %d\n",
+ s->irq_coalesced);
+ }
+ } else
+#endif
+ qemu_irq_raise(s->irq);
+ }
+ if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) {
+ /* Not square wave at all but we don't want 2048Hz interrupts!
+ Must be seen as a pulse. */
+ qemu_irq_raise(s->sqw_irq);
+ }
+}
+
+static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ RTCState *s = opaque;
+
+ if ((addr & 1) == 0) {
+ s->cmos_index = data & 0x7f;
+ } else {
+ CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02x\n",
+ s->cmos_index, data);
+ switch(s->cmos_index) {
+ case RTC_SECONDS_ALARM:
+ case RTC_MINUTES_ALARM:
+ case RTC_HOURS_ALARM:
+ s->cmos_data[s->cmos_index] = data;
+ break;
+ case RTC_SECONDS:
+ case RTC_MINUTES:
+ case RTC_HOURS:
+ case RTC_DAY_OF_WEEK:
+ case RTC_DAY_OF_MONTH:
+ case RTC_MONTH:
+ case RTC_YEAR:
+ s->cmos_data[s->cmos_index] = data;
+ /* if in set mode, do not update the time */
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_set_time(s);
+ }
+ break;
+ case RTC_REG_A:
+ /* UIP bit is read only */
+ s->cmos_data[RTC_REG_A] = (data & ~REG_A_UIP) |
+ (s->cmos_data[RTC_REG_A] & REG_A_UIP);
+ rtc_timer_update(s, qemu_get_clock(rtc_clock));
+ break;
+ case RTC_REG_B:
+ if (data & REG_B_SET) {
+ /* set mode: reset UIP mode */
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+ data &= ~REG_B_UIE;
+ } else {
+ /* if disabling set mode, update the time */
+ if (s->cmos_data[RTC_REG_B] & REG_B_SET) {
+ rtc_set_time(s);
+ }
+ }
+ if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) &&
+ !(data & REG_B_SET)) {
+ /* If the time format has changed and not in set mode,
+ update the registers immediately. */
+ s->cmos_data[RTC_REG_B] = data;
+ rtc_copy_date(s);
+ } else {
+ s->cmos_data[RTC_REG_B] = data;
+ }
+ rtc_timer_update(s, qemu_get_clock(rtc_clock));
+ break;
+ case RTC_REG_C:
+ case RTC_REG_D:
+ /* cannot write to them */
+ break;
+ default:
+ s->cmos_data[s->cmos_index] = data;
+ break;
+ }
+ }
+}
+
+static inline int rtc_to_bcd(RTCState *s, int a)
+{
+ if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
+ return a;
+ } else {
+ return ((a / 10) << 4) | (a % 10);
+ }
+}
+
+static inline int rtc_from_bcd(RTCState *s, int a)
+{
+ if (s->cmos_data[RTC_REG_B] & REG_B_DM) {
+ return a;
+ } else {
+ return ((a >> 4) * 10) + (a & 0x0f);
+ }
+}
+
+static void rtc_set_time(RTCState *s)
+{
+ struct tm *tm = &s->current_tm;
+
+ tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]);
+ tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]);
+ tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f);
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_24H) &&
+ (s->cmos_data[RTC_HOURS] & 0x80)) {
+ tm->tm_hour += 12;
+ }
+ tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1;
+ tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]);
+ tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1;
+ tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900;
+
+ rtc_change_mon_event(tm);
+}
+
+static void rtc_copy_date(RTCState *s)
+{
+ const struct tm *tm = &s->current_tm;
+ int year;
+
+ s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec);
+ s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min);
+ if (s->cmos_data[RTC_REG_B] & REG_B_24H) {
+ /* 24 hour format */
+ s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour);
+ } else {
+ /* 12 hour format */
+ s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour % 12);
+ if (tm->tm_hour >= 12)
+ s->cmos_data[RTC_HOURS] |= 0x80;
+ }
+ s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1);
+ s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday);
+ s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1);
+ year = (tm->tm_year - s->base_year) % 100;
+ if (year < 0)
+ year += 100;
+ s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year);
+}
+
+/* month is between 0 and 11. */
+static int get_days_in_month(int month, int year)
+{
+ static const int days_tab[12] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ int d;
+ if ((unsigned )month >= 12)
+ return 31;
+ d = days_tab[month];
+ if (month == 1) {
+ if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
+ d++;
+ }
+ return d;
+}
+
+/* update 'tm' to the next second */
+static void rtc_next_second(struct tm *tm)
+{
+ int days_in_month;
+
+ tm->tm_sec++;
+ if ((unsigned)tm->tm_sec >= 60) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if ((unsigned)tm->tm_min >= 60) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if ((unsigned)tm->tm_hour >= 24) {
+ tm->tm_hour = 0;
+ /* next day */
+ tm->tm_wday++;
+ if ((unsigned)tm->tm_wday >= 7)
+ tm->tm_wday = 0;
+ days_in_month = get_days_in_month(tm->tm_mon,
+ tm->tm_year + 1900);
+ tm->tm_mday++;
+ if (tm->tm_mday < 1) {
+ tm->tm_mday = 1;
+ } else if (tm->tm_mday > days_in_month) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon >= 12) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void rtc_update_second(void *opaque)
+{
+ RTCState *s = opaque;
+ int64_t delay;
+
+ /* if the oscillator is not in normal operation, we do not update */
+ if ((s->cmos_data[RTC_REG_A] & 0x70) != 0x20) {
+ s->next_second_time += get_ticks_per_sec();
+ qemu_mod_timer(s->second_timer, s->next_second_time);
+ } else {
+ rtc_next_second(&s->current_tm);
+
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ /* update in progress bit */
+ s->cmos_data[RTC_REG_A] |= REG_A_UIP;
+ }
+ /* should be 244 us = 8 / 32768 seconds, but currently the
+ timers do not have the necessary resolution. */
+ delay = (get_ticks_per_sec() * 1) / 100;
+ if (delay < 1)
+ delay = 1;
+ qemu_mod_timer(s->second_timer2,
+ s->next_second_time + delay);
+ }
+}
+
+static void rtc_update_second2(void *opaque)
+{
+ RTCState *s = opaque;
+
+ if (!(s->cmos_data[RTC_REG_B] & REG_B_SET)) {
+ rtc_copy_date(s);
+ }
+
+ /* check alarm */
+ if (s->cmos_data[RTC_REG_B] & REG_B_AIE) {
+ if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 ||
+ rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) &&
+ ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 ||
+ rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) &&
+ ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 ||
+ rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) {
+
+ s->cmos_data[RTC_REG_C] |= 0xa0;
+ qemu_irq_raise(s->irq);
+ }
+ }
+
+ /* update ended interrupt */
+ s->cmos_data[RTC_REG_C] |= REG_C_UF;
+ if (s->cmos_data[RTC_REG_B] & REG_B_UIE) {
+ s->cmos_data[RTC_REG_C] |= REG_C_IRQF;
+ qemu_irq_raise(s->irq);
+ }
+
+ /* clear update in progress bit */
+ s->cmos_data[RTC_REG_A] &= ~REG_A_UIP;
+
+ s->next_second_time += get_ticks_per_sec();
+ qemu_mod_timer(s->second_timer, s->next_second_time);
+}
+
+static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
+{
+ RTCState *s = opaque;
+ int ret;
+ if ((addr & 1) == 0) {
+ return 0xff;
+ } else {
+ switch(s->cmos_index) {
+ case RTC_SECONDS:
+ case RTC_MINUTES:
+ case RTC_HOURS:
+ case RTC_DAY_OF_WEEK:
+ case RTC_DAY_OF_MONTH:
+ case RTC_MONTH:
+ case RTC_YEAR:
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ case RTC_REG_A:
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ case RTC_REG_C:
+ ret = s->cmos_data[s->cmos_index];
+ qemu_irq_lower(s->irq);
+#ifdef TARGET_I386
+ if(s->irq_coalesced &&
+ s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) {
+ s->irq_reinject_on_ack_count++;
+ apic_reset_irq_delivered();
+ DPRINTF_C("cmos: injecting on ack\n");
+ qemu_irq_raise(s->irq);
+ if (apic_get_irq_delivered()) {
+ s->irq_coalesced--;
+ DPRINTF_C("cmos: coalesced irqs decreased to %d\n",
+ s->irq_coalesced);
+ }
+ break;
+ }
+#endif
+
+ s->cmos_data[RTC_REG_C] = 0x00;
+ break;
+ default:
+ ret = s->cmos_data[s->cmos_index];
+ break;
+ }
+ CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n",
+ s->cmos_index, ret);
+ return ret;
+ }
+}
+
+void rtc_set_memory(ISADevice *dev, int addr, int val)
+{
+ RTCState *s = DO_UPCAST(RTCState, dev, dev);
+ if (addr >= 0 && addr <= 127)
+ s->cmos_data[addr] = val;
+}
+
+void rtc_set_date(ISADevice *dev, const struct tm *tm)
+{
+ RTCState *s = DO_UPCAST(RTCState, dev, dev);
+ s->current_tm = *tm;
+ rtc_copy_date(s);
+}
+
+/* PC cmos mappings */
+#define REG_IBM_CENTURY_BYTE 0x32
+#define REG_IBM_PS2_CENTURY_BYTE 0x37
+
+static void rtc_set_date_from_host(ISADevice *dev)
+{
+ RTCState *s = DO_UPCAST(RTCState, dev, dev);
+ struct tm tm;
+ int val;
+
+ /* set the CMOS date */
+ qemu_get_timedate(&tm, 0);
+ rtc_set_date(dev, &tm);
+
+ val = rtc_to_bcd(s, (tm.tm_year / 100) + 19);
+ rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val);
+ rtc_set_memory(dev, REG_IBM_PS2_CENTURY_BYTE, val);
+}
+
+static int rtc_post_load(void *opaque, int version_id)
+{
+#ifdef TARGET_I386
+ RTCState *s = opaque;
+
+ if (version_id >= 2) {
+ if (rtc_td_hack) {
+ rtc_coalesced_timer_update(s);
+ }
+ }
+#endif
+ return 0;
+}
+
+static const VMStateDescription vmstate_rtc = {
+ .name = "mc146818rtc",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = rtc_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_BUFFER(cmos_data, RTCState),
+ VMSTATE_UINT8(cmos_index, RTCState),
+ VMSTATE_INT32(current_tm.tm_sec, RTCState),
+ VMSTATE_INT32(current_tm.tm_min, RTCState),
+ VMSTATE_INT32(current_tm.tm_hour, RTCState),
+ VMSTATE_INT32(current_tm.tm_wday, RTCState),
+ VMSTATE_INT32(current_tm.tm_mday, RTCState),
+ VMSTATE_INT32(current_tm.tm_mon, RTCState),
+ VMSTATE_INT32(current_tm.tm_year, RTCState),
+ VMSTATE_TIMER(periodic_timer, RTCState),
+ VMSTATE_INT64(next_periodic_time, RTCState),
+ VMSTATE_INT64(next_second_time, RTCState),
+ VMSTATE_TIMER(second_timer, RTCState),
+ VMSTATE_TIMER(second_timer2, RTCState),
+ VMSTATE_UINT32_V(irq_coalesced, RTCState, 2),
+ VMSTATE_UINT32_V(period, RTCState, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void rtc_reset(void *opaque)
+{
+ RTCState *s = opaque;
+
+ s->cmos_data[RTC_REG_B] &= ~(REG_B_PIE | REG_B_AIE | REG_B_SQWE);
+ s->cmos_data[RTC_REG_C] &= ~(REG_C_UF | REG_C_IRQF | REG_C_PF | REG_C_AF);
+
+ qemu_irq_lower(s->irq);
+
+#ifdef TARGET_I386
+ if (rtc_td_hack)
+ s->irq_coalesced = 0;
+#endif
+}
+
+static int rtc_initfn(ISADevice *dev)
+{
+ RTCState *s = DO_UPCAST(RTCState, dev, dev);
+ int base = 0x70;
+
+ s->cmos_data[RTC_REG_A] = 0x26;
+ s->cmos_data[RTC_REG_B] = 0x02;
+ s->cmos_data[RTC_REG_C] = 0x00;
+ s->cmos_data[RTC_REG_D] = 0x80;
+
+ rtc_set_date_from_host(dev);
+
+ s->periodic_timer = qemu_new_timer(rtc_clock, rtc_periodic_timer, s);
+#ifdef TARGET_I386
+ if (rtc_td_hack)
+ s->coalesced_timer =
+ qemu_new_timer(rtc_clock, rtc_coalesced_timer, s);
+#endif
+ s->second_timer = qemu_new_timer(rtc_clock, rtc_update_second, s);
+ s->second_timer2 = qemu_new_timer(rtc_clock, rtc_update_second2, s);
+
+ s->next_second_time =
+ qemu_get_clock(rtc_clock) + (get_ticks_per_sec() * 99) / 100;
+ qemu_mod_timer(s->second_timer2, s->next_second_time);
+
+ register_ioport_write(base, 2, 1, cmos_ioport_write, s);
+ register_ioport_read(base, 2, 1, cmos_ioport_read, s);
+ isa_init_ioport_range(dev, base, 2);
+
+ qdev_set_legacy_instance_id(&dev->qdev, base, 2);
+ qemu_register_reset(rtc_reset, s);
+ return 0;
+}
+
+ISADevice *rtc_init(int base_year, qemu_irq intercept_irq)
+{
+ ISADevice *dev;
+ RTCState *s;
+
+ dev = isa_create("mc146818rtc");
+ s = DO_UPCAST(RTCState, dev, dev);
+ qdev_prop_set_int32(&dev->qdev, "base_year", base_year);
+ qdev_init_nofail(&dev->qdev);
+ if (intercept_irq) {
+ s->irq = intercept_irq;
+ } else {
+ isa_init_irq(dev, &s->irq, RTC_ISA_IRQ);
+ }
+ return dev;
+}
+
+static ISADeviceInfo mc146818rtc_info = {
+ .qdev.name = "mc146818rtc",
+ .qdev.size = sizeof(RTCState),
+ .qdev.no_user = 1,
+ .qdev.vmsd = &vmstate_rtc,
+ .init = rtc_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void mc146818rtc_register(void)
+{
+ isa_qdev_register(&mc146818rtc_info);
+}
+device_init(mc146818rtc_register)
diff --git a/hw/mc146818rtc.h b/hw/mc146818rtc.h
new file mode 100644
index 0000000..575968c
--- /dev/null
+++ b/hw/mc146818rtc.h
@@ -0,0 +1,12 @@
+#ifndef MC146818RTC_H
+#define MC146818RTC_H
+
+#include "isa.h"
+
+#define RTC_ISA_IRQ 8
+
+ISADevice *rtc_init(int base_year, qemu_irq intercept_irq);
+void rtc_set_memory(ISADevice *dev, int addr, int val);
+void rtc_set_date(ISADevice *dev, const struct tm *tm);
+
+#endif /* !MC146818RTC_H */
diff --git a/hw/mcf.h b/hw/mcf.h
new file mode 100644
index 0000000..91f2821
--- /dev/null
+++ b/hw/mcf.h
@@ -0,0 +1,21 @@
+#ifndef HW_MCF_H
+#define HW_MCF_H
+/* Motorola ColdFire device prototypes. */
+
+/* mcf_uart.c */
+uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr);
+void mcf_uart_write(void *opaque, target_phys_addr_t addr, uint32_t val);
+void *mcf_uart_init(qemu_irq irq, CharDriverState *chr);
+void mcf_uart_mm_init(target_phys_addr_t base, qemu_irq irq,
+ CharDriverState *chr);
+
+/* mcf_intc.c */
+qemu_irq *mcf_intc_init(target_phys_addr_t base, CPUState *env);
+
+/* mcf_fec.c */
+void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq);
+
+/* mcf5206.c */
+qemu_irq *mcf5206_init(uint32_t base, CPUState *env);
+
+#endif
diff --git a/hw/mcf5206.c b/hw/mcf5206.c
new file mode 100644
index 0000000..2a618d4
--- /dev/null
+++ b/hw/mcf5206.c
@@ -0,0 +1,541 @@
+/*
+ * Motorola ColdFire MCF5206 SoC embedded peripheral emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+#include "hw.h"
+#include "mcf.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+/* General purpose timer module. */
+typedef struct {
+ uint16_t tmr;
+ uint16_t trr;
+ uint16_t tcr;
+ uint16_t ter;
+ ptimer_state *timer;
+ qemu_irq irq;
+ int irq_state;
+} m5206_timer_state;
+
+#define TMR_RST 0x01
+#define TMR_CLK 0x06
+#define TMR_FRR 0x08
+#define TMR_ORI 0x10
+#define TMR_OM 0x20
+#define TMR_CE 0xc0
+
+#define TER_CAP 0x01
+#define TER_REF 0x02
+
+static void m5206_timer_update(m5206_timer_state *s)
+{
+ if ((s->tmr & TMR_ORI) != 0 && (s->ter & TER_REF))
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void m5206_timer_reset(m5206_timer_state *s)
+{
+ s->tmr = 0;
+ s->trr = 0;
+}
+
+static void m5206_timer_recalibrate(m5206_timer_state *s)
+{
+ int prescale;
+ int mode;
+
+ ptimer_stop(s->timer);
+
+ if ((s->tmr & TMR_RST) == 0)
+ return;
+
+ prescale = (s->tmr >> 8) + 1;
+ mode = (s->tmr >> 1) & 3;
+ if (mode == 2)
+ prescale *= 16;
+
+ if (mode == 3 || mode == 0)
+ hw_error("m5206_timer: mode %d not implemented\n", mode);
+ if ((s->tmr & TMR_FRR) == 0)
+ hw_error("m5206_timer: free running mode not implemented\n");
+
+ /* Assume 66MHz system clock. */
+ ptimer_set_freq(s->timer, 66000000 / prescale);
+
+ ptimer_set_limit(s->timer, s->trr, 0);
+
+ ptimer_run(s->timer, 0);
+}
+
+static void m5206_timer_trigger(void *opaque)
+{
+ m5206_timer_state *s = (m5206_timer_state *)opaque;
+ s->ter |= TER_REF;
+ m5206_timer_update(s);
+}
+
+static uint32_t m5206_timer_read(m5206_timer_state *s, uint32_t addr)
+{
+ switch (addr) {
+ case 0:
+ return s->tmr;
+ case 4:
+ return s->trr;
+ case 8:
+ return s->tcr;
+ case 0xc:
+ return s->trr - ptimer_get_count(s->timer);
+ case 0x11:
+ return s->ter;
+ default:
+ return 0;
+ }
+}
+
+static void m5206_timer_write(m5206_timer_state *s, uint32_t addr, uint32_t val)
+{
+ switch (addr) {
+ case 0:
+ if ((s->tmr & TMR_RST) != 0 && (val & TMR_RST) == 0) {
+ m5206_timer_reset(s);
+ }
+ s->tmr = val;
+ m5206_timer_recalibrate(s);
+ break;
+ case 4:
+ s->trr = val;
+ m5206_timer_recalibrate(s);
+ break;
+ case 8:
+ s->tcr = val;
+ break;
+ case 0xc:
+ ptimer_set_count(s->timer, val);
+ break;
+ case 0x11:
+ s->ter &= ~val;
+ break;
+ default:
+ break;
+ }
+ m5206_timer_update(s);
+}
+
+static m5206_timer_state *m5206_timer_init(qemu_irq irq)
+{
+ m5206_timer_state *s;
+ QEMUBH *bh;
+
+ s = (m5206_timer_state *)qemu_mallocz(sizeof(m5206_timer_state));
+ bh = qemu_bh_new(m5206_timer_trigger, s);
+ s->timer = ptimer_init(bh);
+ s->irq = irq;
+ m5206_timer_reset(s);
+ return s;
+}
+
+/* System Integration Module. */
+
+typedef struct {
+ CPUState *env;
+ m5206_timer_state *timer[2];
+ void *uart[2];
+ uint8_t scr;
+ uint8_t icr[14];
+ uint16_t imr; /* 1 == interrupt is masked. */
+ uint16_t ipr;
+ uint8_t rsr;
+ uint8_t swivr;
+ uint8_t par;
+ /* Include the UART vector registers here. */
+ uint8_t uivr[2];
+} m5206_mbar_state;
+
+/* Interrupt controller. */
+
+static int m5206_find_pending_irq(m5206_mbar_state *s)
+{
+ int level;
+ int vector;
+ uint16_t active;
+ int i;
+
+ level = 0;
+ vector = 0;
+ active = s->ipr & ~s->imr;
+ if (!active)
+ return 0;
+
+ for (i = 1; i < 14; i++) {
+ if (active & (1 << i)) {
+ if ((s->icr[i] & 0x1f) > level) {
+ level = s->icr[i] & 0x1f;
+ vector = i;
+ }
+ }
+ }
+
+ if (level < 4)
+ vector = 0;
+
+ return vector;
+}
+
+static void m5206_mbar_update(m5206_mbar_state *s)
+{
+ int irq;
+ int vector;
+ int level;
+
+ irq = m5206_find_pending_irq(s);
+ if (irq) {
+ int tmp;
+ tmp = s->icr[irq];
+ level = (tmp >> 2) & 7;
+ if (tmp & 0x80) {
+ /* Autovector. */
+ vector = 24 + level;
+ } else {
+ switch (irq) {
+ case 8: /* SWT */
+ vector = s->swivr;
+ break;
+ case 12: /* UART1 */
+ vector = s->uivr[0];
+ break;
+ case 13: /* UART2 */
+ vector = s->uivr[1];
+ break;
+ default:
+ /* Unknown vector. */
+ fprintf(stderr, "Unhandled vector for IRQ %d\n", irq);
+ vector = 0xf;
+ break;
+ }
+ }
+ } else {
+ level = 0;
+ vector = 0;
+ }
+ m68k_set_irq_level(s->env, level, vector);
+}
+
+static void m5206_mbar_set_irq(void *opaque, int irq, int level)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ if (level) {
+ s->ipr |= 1 << irq;
+ } else {
+ s->ipr &= ~(1 << irq);
+ }
+ m5206_mbar_update(s);
+}
+
+/* System Integration Module. */
+
+static void m5206_mbar_reset(m5206_mbar_state *s)
+{
+ s->scr = 0xc0;
+ s->icr[1] = 0x04;
+ s->icr[2] = 0x08;
+ s->icr[3] = 0x0c;
+ s->icr[4] = 0x10;
+ s->icr[5] = 0x14;
+ s->icr[6] = 0x18;
+ s->icr[7] = 0x1c;
+ s->icr[8] = 0x1c;
+ s->icr[9] = 0x80;
+ s->icr[10] = 0x80;
+ s->icr[11] = 0x80;
+ s->icr[12] = 0x00;
+ s->icr[13] = 0x00;
+ s->imr = 0x3ffe;
+ s->rsr = 0x80;
+ s->swivr = 0x0f;
+ s->par = 0;
+}
+
+static uint32_t m5206_mbar_read(m5206_mbar_state *s, uint32_t offset)
+{
+ if (offset >= 0x100 && offset < 0x120) {
+ return m5206_timer_read(s->timer[0], offset - 0x100);
+ } else if (offset >= 0x120 && offset < 0x140) {
+ return m5206_timer_read(s->timer[1], offset - 0x120);
+ } else if (offset >= 0x140 && offset < 0x160) {
+ return mcf_uart_read(s->uart[0], offset - 0x140);
+ } else if (offset >= 0x180 && offset < 0x1a0) {
+ return mcf_uart_read(s->uart[1], offset - 0x180);
+ }
+ switch (offset) {
+ case 0x03: return s->scr;
+ case 0x14 ... 0x20: return s->icr[offset - 0x13];
+ case 0x36: return s->imr;
+ case 0x3a: return s->ipr;
+ case 0x40: return s->rsr;
+ case 0x41: return 0;
+ case 0x42: return s->swivr;
+ case 0x50:
+ /* DRAM mask register. */
+ /* FIXME: currently hardcoded to 128Mb. */
+ {
+ uint32_t mask = ~0;
+ while (mask > ram_size)
+ mask >>= 1;
+ return mask & 0x0ffe0000;
+ }
+ case 0x5c: return 1; /* DRAM bank 1 empty. */
+ case 0xcb: return s->par;
+ case 0x170: return s->uivr[0];
+ case 0x1b0: return s->uivr[1];
+ }
+ hw_error("Bad MBAR read offset 0x%x", (int)offset);
+ return 0;
+}
+
+static void m5206_mbar_write(m5206_mbar_state *s, uint32_t offset,
+ uint32_t value)
+{
+ if (offset >= 0x100 && offset < 0x120) {
+ m5206_timer_write(s->timer[0], offset - 0x100, value);
+ return;
+ } else if (offset >= 0x120 && offset < 0x140) {
+ m5206_timer_write(s->timer[1], offset - 0x120, value);
+ return;
+ } else if (offset >= 0x140 && offset < 0x160) {
+ mcf_uart_write(s->uart[0], offset - 0x140, value);
+ return;
+ } else if (offset >= 0x180 && offset < 0x1a0) {
+ mcf_uart_write(s->uart[1], offset - 0x180, value);
+ return;
+ }
+ switch (offset) {
+ case 0x03:
+ s->scr = value;
+ break;
+ case 0x14 ... 0x20:
+ s->icr[offset - 0x13] = value;
+ m5206_mbar_update(s);
+ break;
+ case 0x36:
+ s->imr = value;
+ m5206_mbar_update(s);
+ break;
+ case 0x40:
+ s->rsr &= ~value;
+ break;
+ case 0x41:
+ /* TODO: implement watchdog. */
+ break;
+ case 0x42:
+ s->swivr = value;
+ break;
+ case 0xcb:
+ s->par = value;
+ break;
+ case 0x170:
+ s->uivr[0] = value;
+ break;
+ case 0x178: case 0x17c: case 0x1c8: case 0x1bc:
+ /* Not implemented: UART Output port bits. */
+ break;
+ case 0x1b0:
+ s->uivr[1] = value;
+ break;
+ default:
+ hw_error("Bad MBAR write offset 0x%x", (int)offset);
+ break;
+ }
+}
+
+/* Internal peripherals use a variety of register widths.
+ This lookup table allows a single routine to handle all of them. */
+static const int m5206_mbar_width[] =
+{
+ /* 000-040 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ /* 040-080 */ 1, 2, 2, 2, 4, 1, 2, 4, 1, 2, 4, 2, 2, 4, 2, 2,
+ /* 080-0c0 */ 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4, 2, 2, 4,
+ /* 0c0-100 */ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 100-140 */ 2, 2, 2, 2, 1, 0, 0, 0, 2, 2, 2, 2, 1, 0, 0, 0,
+ /* 140-180 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 180-1c0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 1c0-200 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+static uint32_t m5206_mbar_readw(void *opaque, target_phys_addr_t offset);
+static uint32_t m5206_mbar_readl(void *opaque, target_phys_addr_t offset);
+
+static uint32_t m5206_mbar_readb(void *opaque, target_phys_addr_t offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ hw_error("Bad MBAR read offset 0x%x", (int)offset);
+ }
+ if (m5206_mbar_width[offset >> 2] > 1) {
+ uint16_t val;
+ val = m5206_mbar_readw(opaque, offset & ~1);
+ if ((offset & 1) == 0) {
+ val >>= 8;
+ }
+ return val & 0xff;
+ }
+ return m5206_mbar_read(s, offset);
+}
+
+static uint32_t m5206_mbar_readw(void *opaque, target_phys_addr_t offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ hw_error("Bad MBAR read offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 2) {
+ uint32_t val;
+ val = m5206_mbar_readl(opaque, offset & ~3);
+ if ((offset & 3) == 0)
+ val >>= 16;
+ return val & 0xffff;
+ } else if (width < 2) {
+ uint16_t val;
+ val = m5206_mbar_readb(opaque, offset) << 8;
+ val |= m5206_mbar_readb(opaque, offset + 1);
+ return val;
+ }
+ return m5206_mbar_read(s, offset);
+}
+
+static uint32_t m5206_mbar_readl(void *opaque, target_phys_addr_t offset)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ hw_error("Bad MBAR read offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width < 4) {
+ uint32_t val;
+ val = m5206_mbar_readw(opaque, offset) << 16;
+ val |= m5206_mbar_readw(opaque, offset + 2);
+ return val;
+ }
+ return m5206_mbar_read(s, offset);
+}
+
+static void m5206_mbar_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value);
+static void m5206_mbar_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value);
+
+static void m5206_mbar_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ hw_error("Bad MBAR write offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 1) {
+ uint32_t tmp;
+ tmp = m5206_mbar_readw(opaque, offset & ~1);
+ if (offset & 1) {
+ tmp = (tmp & 0xff00) | value;
+ } else {
+ tmp = (tmp & 0x00ff) | (value << 8);
+ }
+ m5206_mbar_writew(opaque, offset & ~1, tmp);
+ return;
+ }
+ m5206_mbar_write(s, offset, value);
+}
+
+static void m5206_mbar_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ hw_error("Bad MBAR write offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width > 2) {
+ uint32_t tmp;
+ tmp = m5206_mbar_readl(opaque, offset & ~3);
+ if (offset & 3) {
+ tmp = (tmp & 0xffff0000) | value;
+ } else {
+ tmp = (tmp & 0x0000ffff) | (value << 16);
+ }
+ m5206_mbar_writel(opaque, offset & ~3, tmp);
+ return;
+ } else if (width < 2) {
+ m5206_mbar_writeb(opaque, offset, value >> 8);
+ m5206_mbar_writeb(opaque, offset + 1, value & 0xff);
+ return;
+ }
+ m5206_mbar_write(s, offset, value);
+}
+
+static void m5206_mbar_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5206_mbar_state *s = (m5206_mbar_state *)opaque;
+ int width;
+ offset &= 0x3ff;
+ if (offset > 0x200) {
+ hw_error("Bad MBAR write offset 0x%x", (int)offset);
+ }
+ width = m5206_mbar_width[offset >> 2];
+ if (width < 4) {
+ m5206_mbar_writew(opaque, offset, value >> 16);
+ m5206_mbar_writew(opaque, offset + 2, value & 0xffff);
+ return;
+ }
+ m5206_mbar_write(s, offset, value);
+}
+
+static CPUReadMemoryFunc * const m5206_mbar_readfn[] = {
+ m5206_mbar_readb,
+ m5206_mbar_readw,
+ m5206_mbar_readl
+};
+
+static CPUWriteMemoryFunc * const m5206_mbar_writefn[] = {
+ m5206_mbar_writeb,
+ m5206_mbar_writew,
+ m5206_mbar_writel
+};
+
+qemu_irq *mcf5206_init(uint32_t base, CPUState *env)
+{
+ m5206_mbar_state *s;
+ qemu_irq *pic;
+ int iomemtype;
+
+ s = (m5206_mbar_state *)qemu_mallocz(sizeof(m5206_mbar_state));
+ iomemtype = cpu_register_io_memory(m5206_mbar_readfn,
+ m5206_mbar_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+
+ pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14);
+ s->timer[0] = m5206_timer_init(pic[9]);
+ s->timer[1] = m5206_timer_init(pic[10]);
+ s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]);
+ s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]);
+ s->env = env;
+
+ m5206_mbar_reset(s);
+ return pic;
+}
diff --git a/hw/mcf5208.c b/hw/mcf5208.c
new file mode 100644
index 0000000..17a692d
--- /dev/null
+++ b/hw/mcf5208.c
@@ -0,0 +1,306 @@
+/*
+ * Motorola ColdFire MCF5208 SoC emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+#include "hw.h"
+#include "mcf.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "net.h"
+#include "boards.h"
+#include "loader.h"
+#include "elf.h"
+
+#define SYS_FREQ 66000000
+
+#define PCSR_EN 0x0001
+#define PCSR_RLD 0x0002
+#define PCSR_PIF 0x0004
+#define PCSR_PIE 0x0008
+#define PCSR_OVW 0x0010
+#define PCSR_DBG 0x0020
+#define PCSR_DOZE 0x0040
+#define PCSR_PRE_SHIFT 8
+#define PCSR_PRE_MASK 0x0f00
+
+typedef struct {
+ qemu_irq irq;
+ ptimer_state *timer;
+ uint16_t pcsr;
+ uint16_t pmr;
+ uint16_t pcntr;
+} m5208_timer_state;
+
+static void m5208_timer_update(m5208_timer_state *s)
+{
+ if ((s->pcsr & (PCSR_PIE | PCSR_PIF)) == (PCSR_PIE | PCSR_PIF))
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void m5208_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ m5208_timer_state *s = (m5208_timer_state *)opaque;
+ int prescale;
+ int limit;
+ switch (offset) {
+ case 0:
+ /* The PIF bit is set-to-clear. */
+ if (value & PCSR_PIF) {
+ s->pcsr &= ~PCSR_PIF;
+ value &= ~PCSR_PIF;
+ }
+ /* Avoid frobbing the timer if we're just twiddling IRQ bits. */
+ if (((s->pcsr ^ value) & ~PCSR_PIE) == 0) {
+ s->pcsr = value;
+ m5208_timer_update(s);
+ return;
+ }
+
+ if (s->pcsr & PCSR_EN)
+ ptimer_stop(s->timer);
+
+ s->pcsr = value;
+
+ prescale = 1 << ((s->pcsr & PCSR_PRE_MASK) >> PCSR_PRE_SHIFT);
+ ptimer_set_freq(s->timer, (SYS_FREQ / 2) / prescale);
+ if (s->pcsr & PCSR_RLD)
+ limit = s->pmr;
+ else
+ limit = 0xffff;
+ ptimer_set_limit(s->timer, limit, 0);
+
+ if (s->pcsr & PCSR_EN)
+ ptimer_run(s->timer, 0);
+ break;
+ case 2:
+ s->pmr = value;
+ s->pcsr &= ~PCSR_PIF;
+ if ((s->pcsr & PCSR_RLD) == 0) {
+ if (s->pcsr & PCSR_OVW)
+ ptimer_set_count(s->timer, value);
+ } else {
+ ptimer_set_limit(s->timer, value, s->pcsr & PCSR_OVW);
+ }
+ break;
+ case 4:
+ break;
+ default:
+ hw_error("m5208_timer_write: Bad offset 0x%x\n", (int)offset);
+ break;
+ }
+ m5208_timer_update(s);
+}
+
+static void m5208_timer_trigger(void *opaque)
+{
+ m5208_timer_state *s = (m5208_timer_state *)opaque;
+ s->pcsr |= PCSR_PIF;
+ m5208_timer_update(s);
+}
+
+static uint32_t m5208_timer_read(void *opaque, target_phys_addr_t addr)
+{
+ m5208_timer_state *s = (m5208_timer_state *)opaque;
+ switch (addr) {
+ case 0:
+ return s->pcsr;
+ case 2:
+ return s->pmr;
+ case 4:
+ return ptimer_get_count(s->timer);
+ default:
+ hw_error("m5208_timer_read: Bad offset 0x%x\n", (int)addr);
+ return 0;
+ }
+}
+
+static CPUReadMemoryFunc * const m5208_timer_readfn[] = {
+ m5208_timer_read,
+ m5208_timer_read,
+ m5208_timer_read
+};
+
+static CPUWriteMemoryFunc * const m5208_timer_writefn[] = {
+ m5208_timer_write,
+ m5208_timer_write,
+ m5208_timer_write
+};
+
+static uint32_t m5208_sys_read(void *opaque, target_phys_addr_t addr)
+{
+ switch (addr) {
+ case 0x110: /* SDCS0 */
+ {
+ int n;
+ for (n = 0; n < 32; n++) {
+ if (ram_size < (2u << n))
+ break;
+ }
+ return (n - 1) | 0x40000000;
+ }
+ case 0x114: /* SDCS1 */
+ return 0;
+
+ default:
+ hw_error("m5208_sys_read: Bad offset 0x%x\n", (int)addr);
+ return 0;
+ }
+}
+
+static void m5208_sys_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ hw_error("m5208_sys_write: Bad offset 0x%x\n", (int)addr);
+}
+
+static CPUReadMemoryFunc * const m5208_sys_readfn[] = {
+ m5208_sys_read,
+ m5208_sys_read,
+ m5208_sys_read
+};
+
+static CPUWriteMemoryFunc * const m5208_sys_writefn[] = {
+ m5208_sys_write,
+ m5208_sys_write,
+ m5208_sys_write
+};
+
+static void mcf5208_sys_init(qemu_irq *pic)
+{
+ int iomemtype;
+ m5208_timer_state *s;
+ QEMUBH *bh;
+ int i;
+
+ iomemtype = cpu_register_io_memory(m5208_sys_readfn,
+ m5208_sys_writefn, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ /* SDRAMC. */
+ cpu_register_physical_memory(0xfc0a8000, 0x00004000, iomemtype);
+ /* Timers. */
+ for (i = 0; i < 2; i++) {
+ s = (m5208_timer_state *)qemu_mallocz(sizeof(m5208_timer_state));
+ bh = qemu_bh_new(m5208_timer_trigger, s);
+ s->timer = ptimer_init(bh);
+ iomemtype = cpu_register_io_memory(m5208_timer_readfn,
+ m5208_timer_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0xfc080000 + 0x4000 * i, 0x00004000,
+ iomemtype);
+ s->irq = pic[4 + i];
+ }
+}
+
+static void mcf5208evb_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ int kernel_size;
+ uint64_t elf_entry;
+ target_phys_addr_t entry;
+ qemu_irq *pic;
+
+ if (!cpu_model)
+ cpu_model = "m5208";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find m68k CPU definition\n");
+ exit(1);
+ }
+
+ /* Initialize CPU registers. */
+ env->vbr = 0;
+ /* TODO: Configure BARs. */
+
+ /* DRAM at 0x40000000 */
+ cpu_register_physical_memory(0x40000000, ram_size,
+ qemu_ram_alloc(NULL, "mcf5208.ram", ram_size) | IO_MEM_RAM);
+
+ /* Internal SRAM. */
+ cpu_register_physical_memory(0x80000000, 16384,
+ qemu_ram_alloc(NULL, "mcf5208.sram", 16384) | IO_MEM_RAM);
+
+ /* Internal peripherals. */
+ pic = mcf_intc_init(0xfc048000, env);
+
+ mcf_uart_mm_init(0xfc060000, pic[26], serial_hds[0]);
+ mcf_uart_mm_init(0xfc064000, pic[27], serial_hds[1]);
+ mcf_uart_mm_init(0xfc068000, pic[28], serial_hds[2]);
+
+ mcf5208_sys_init(pic);
+
+ if (nb_nics > 1) {
+ fprintf(stderr, "Too many NICs\n");
+ exit(1);
+ }
+ if (nd_table[0].vlan)
+ mcf_fec_init(&nd_table[0], 0xfc030000, pic + 36);
+
+ /* 0xfc000000 SCM. */
+ /* 0xfc004000 XBS. */
+ /* 0xfc008000 FlexBus CS. */
+ /* 0xfc030000 FEC. */
+ /* 0xfc040000 SCM + Power management. */
+ /* 0xfc044000 eDMA. */
+ /* 0xfc048000 INTC. */
+ /* 0xfc058000 I2C. */
+ /* 0xfc05c000 QSPI. */
+ /* 0xfc060000 UART0. */
+ /* 0xfc064000 UART0. */
+ /* 0xfc068000 UART0. */
+ /* 0xfc070000 DMA timers. */
+ /* 0xfc080000 PIT0. */
+ /* 0xfc084000 PIT1. */
+ /* 0xfc088000 EPORT. */
+ /* 0xfc08c000 Watchdog. */
+ /* 0xfc090000 clock module. */
+ /* 0xfc0a0000 CCM + reset. */
+ /* 0xfc0a4000 GPIO. */
+ /* 0xfc0a8000 SDRAM controller. */
+
+ /* Load kernel. */
+ if (!kernel_filename) {
+ fprintf(stderr, "Kernel image must be specified\n");
+ exit(1);
+ }
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ NULL, NULL, 1, ELF_MACHINE, 0);
+ entry = elf_entry;
+ if (kernel_size < 0) {
+ kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
+ }
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, 0x40000000,
+ ram_size);
+ entry = 0x40000000;
+ }
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+
+ env->pc = entry;
+}
+
+static QEMUMachine mcf5208evb_machine = {
+ .name = "mcf5208evb",
+ .desc = "MCF5206EVB",
+ .init = mcf5208evb_init,
+ .is_default = 1,
+};
+
+static void mcf5208evb_machine_init(void)
+{
+ qemu_register_machine(&mcf5208evb_machine);
+}
+
+machine_init(mcf5208evb_machine_init);
diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c
new file mode 100644
index 0000000..21035da
--- /dev/null
+++ b/hw/mcf_fec.c
@@ -0,0 +1,481 @@
+/*
+ * ColdFire Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+#include "hw.h"
+#include "net.h"
+#include "mcf.h"
+/* For crc32 */
+#include <zlib.h>
+
+//#define DEBUG_FEC 1
+
+#ifdef DEBUG_FEC
+#define DPRINTF(fmt, ...) \
+do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define FEC_MAX_FRAME_SIZE 2032
+
+typedef struct {
+ qemu_irq *irq;
+ int mmio_index;
+ NICState *nic;
+ NICConf conf;
+ uint32_t irq_state;
+ uint32_t eir;
+ uint32_t eimr;
+ int rx_enabled;
+ uint32_t rx_descriptor;
+ uint32_t tx_descriptor;
+ uint32_t ecr;
+ uint32_t mmfr;
+ uint32_t mscr;
+ uint32_t rcr;
+ uint32_t tcr;
+ uint32_t tfwr;
+ uint32_t rfsr;
+ uint32_t erdsr;
+ uint32_t etdsr;
+ uint32_t emrbr;
+} mcf_fec_state;
+
+#define FEC_INT_HB 0x80000000
+#define FEC_INT_BABR 0x40000000
+#define FEC_INT_BABT 0x20000000
+#define FEC_INT_GRA 0x10000000
+#define FEC_INT_TXF 0x08000000
+#define FEC_INT_TXB 0x04000000
+#define FEC_INT_RXF 0x02000000
+#define FEC_INT_RXB 0x01000000
+#define FEC_INT_MII 0x00800000
+#define FEC_INT_EB 0x00400000
+#define FEC_INT_LC 0x00200000
+#define FEC_INT_RL 0x00100000
+#define FEC_INT_UN 0x00080000
+
+#define FEC_EN 2
+#define FEC_RESET 1
+
+/* Map interrupt flags onto IRQ lines. */
+#define FEC_NUM_IRQ 13
+static const uint32_t mcf_fec_irq_map[FEC_NUM_IRQ] = {
+ FEC_INT_TXF,
+ FEC_INT_TXB,
+ FEC_INT_UN,
+ FEC_INT_RL,
+ FEC_INT_RXF,
+ FEC_INT_RXB,
+ FEC_INT_MII,
+ FEC_INT_LC,
+ FEC_INT_HB,
+ FEC_INT_GRA,
+ FEC_INT_EB,
+ FEC_INT_BABT,
+ FEC_INT_BABR
+};
+
+/* Buffer Descriptor. */
+typedef struct {
+ uint16_t flags;
+ uint16_t length;
+ uint32_t data;
+} mcf_fec_bd;
+
+#define FEC_BD_R 0x8000
+#define FEC_BD_E 0x8000
+#define FEC_BD_O1 0x4000
+#define FEC_BD_W 0x2000
+#define FEC_BD_O2 0x1000
+#define FEC_BD_L 0x0800
+#define FEC_BD_TC 0x0400
+#define FEC_BD_ABC 0x0200
+#define FEC_BD_M 0x0100
+#define FEC_BD_BC 0x0080
+#define FEC_BD_MC 0x0040
+#define FEC_BD_LG 0x0020
+#define FEC_BD_NO 0x0010
+#define FEC_BD_CR 0x0004
+#define FEC_BD_OV 0x0002
+#define FEC_BD_TR 0x0001
+
+static void mcf_fec_read_bd(mcf_fec_bd *bd, uint32_t addr)
+{
+ cpu_physical_memory_read(addr, (uint8_t *)bd, sizeof(*bd));
+ be16_to_cpus(&bd->flags);
+ be16_to_cpus(&bd->length);
+ be32_to_cpus(&bd->data);
+}
+
+static void mcf_fec_write_bd(mcf_fec_bd *bd, uint32_t addr)
+{
+ mcf_fec_bd tmp;
+ tmp.flags = cpu_to_be16(bd->flags);
+ tmp.length = cpu_to_be16(bd->length);
+ tmp.data = cpu_to_be32(bd->data);
+ cpu_physical_memory_write(addr, (uint8_t *)&tmp, sizeof(tmp));
+}
+
+static void mcf_fec_update(mcf_fec_state *s)
+{
+ uint32_t active;
+ uint32_t changed;
+ uint32_t mask;
+ int i;
+
+ active = s->eir & s->eimr;
+ changed = active ^s->irq_state;
+ for (i = 0; i < FEC_NUM_IRQ; i++) {
+ mask = mcf_fec_irq_map[i];
+ if (changed & mask) {
+ DPRINTF("IRQ %d = %d\n", i, (active & mask) != 0);
+ qemu_set_irq(s->irq[i], (active & mask) != 0);
+ }
+ }
+ s->irq_state = active;
+}
+
+static void mcf_fec_do_tx(mcf_fec_state *s)
+{
+ uint32_t addr;
+ mcf_fec_bd bd;
+ int frame_size;
+ int len;
+ uint8_t frame[FEC_MAX_FRAME_SIZE];
+ uint8_t *ptr;
+
+ DPRINTF("do_tx\n");
+ ptr = frame;
+ frame_size = 0;
+ addr = s->tx_descriptor;
+ while (1) {
+ mcf_fec_read_bd(&bd, addr);
+ DPRINTF("tx_bd %x flags %04x len %d data %08x\n",
+ addr, bd.flags, bd.length, bd.data);
+ if ((bd.flags & FEC_BD_R) == 0) {
+ /* Run out of descriptors to transmit. */
+ break;
+ }
+ len = bd.length;
+ if (frame_size + len > FEC_MAX_FRAME_SIZE) {
+ len = FEC_MAX_FRAME_SIZE - frame_size;
+ s->eir |= FEC_INT_BABT;
+ }
+ cpu_physical_memory_read(bd.data, ptr, len);
+ ptr += len;
+ frame_size += len;
+ if (bd.flags & FEC_BD_L) {
+ /* Last buffer in frame. */
+ DPRINTF("Sending packet\n");
+ qemu_send_packet(&s->nic->nc, frame, len);
+ ptr = frame;
+ frame_size = 0;
+ s->eir |= FEC_INT_TXF;
+ }
+ s->eir |= FEC_INT_TXB;
+ bd.flags &= ~FEC_BD_R;
+ /* Write back the modified descriptor. */
+ mcf_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->etdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ s->tx_descriptor = addr;
+}
+
+static void mcf_fec_enable_rx(mcf_fec_state *s)
+{
+ mcf_fec_bd bd;
+
+ mcf_fec_read_bd(&bd, s->rx_descriptor);
+ s->rx_enabled = ((bd.flags & FEC_BD_E) != 0);
+ if (!s->rx_enabled)
+ DPRINTF("RX buffer full\n");
+}
+
+static void mcf_fec_reset(mcf_fec_state *s)
+{
+ s->eir = 0;
+ s->eimr = 0;
+ s->rx_enabled = 0;
+ s->ecr = 0;
+ s->mscr = 0;
+ s->rcr = 0x05ee0001;
+ s->tcr = 0;
+ s->tfwr = 0;
+ s->rfsr = 0x500;
+}
+
+static uint32_t mcf_fec_read(void *opaque, target_phys_addr_t addr)
+{
+ mcf_fec_state *s = (mcf_fec_state *)opaque;
+ switch (addr & 0x3ff) {
+ case 0x004: return s->eir;
+ case 0x008: return s->eimr;
+ case 0x010: return s->rx_enabled ? (1 << 24) : 0; /* RDAR */
+ case 0x014: return 0; /* TDAR */
+ case 0x024: return s->ecr;
+ case 0x040: return s->mmfr;
+ case 0x044: return s->mscr;
+ case 0x064: return 0; /* MIBC */
+ case 0x084: return s->rcr;
+ case 0x0c4: return s->tcr;
+ case 0x0e4: /* PALR */
+ return (s->conf.macaddr.a[0] << 24) | (s->conf.macaddr.a[1] << 16)
+ | (s->conf.macaddr.a[2] << 8) | s->conf.macaddr.a[3];
+ break;
+ case 0x0e8: /* PAUR */
+ return (s->conf.macaddr.a[4] << 24) | (s->conf.macaddr.a[5] << 16) | 0x8808;
+ case 0x0ec: return 0x10000; /* OPD */
+ case 0x118: return 0;
+ case 0x11c: return 0;
+ case 0x120: return 0;
+ case 0x124: return 0;
+ case 0x144: return s->tfwr;
+ case 0x14c: return 0x600;
+ case 0x150: return s->rfsr;
+ case 0x180: return s->erdsr;
+ case 0x184: return s->etdsr;
+ case 0x188: return s->emrbr;
+ default:
+ hw_error("mcf_fec_read: Bad address 0x%x\n", (int)addr);
+ return 0;
+ }
+}
+
+static void mcf_fec_write(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ mcf_fec_state *s = (mcf_fec_state *)opaque;
+ switch (addr & 0x3ff) {
+ case 0x004:
+ s->eir &= ~value;
+ break;
+ case 0x008:
+ s->eimr = value;
+ break;
+ case 0x010: /* RDAR */
+ if ((s->ecr & FEC_EN) && !s->rx_enabled) {
+ DPRINTF("RX enable\n");
+ mcf_fec_enable_rx(s);
+ }
+ break;
+ case 0x014: /* TDAR */
+ if (s->ecr & FEC_EN) {
+ mcf_fec_do_tx(s);
+ }
+ break;
+ case 0x024:
+ s->ecr = value;
+ if (value & FEC_RESET) {
+ DPRINTF("Reset\n");
+ mcf_fec_reset(s);
+ }
+ if ((s->ecr & FEC_EN) == 0) {
+ s->rx_enabled = 0;
+ }
+ break;
+ case 0x040:
+ /* TODO: Implement MII. */
+ s->mmfr = value;
+ break;
+ case 0x044:
+ s->mscr = value & 0xfe;
+ break;
+ case 0x064:
+ /* TODO: Implement MIB. */
+ break;
+ case 0x084:
+ s->rcr = value & 0x07ff003f;
+ /* TODO: Implement LOOP mode. */
+ break;
+ case 0x0c4: /* TCR */
+ /* We transmit immediately, so raise GRA immediately. */
+ s->tcr = value;
+ if (value & 1)
+ s->eir |= FEC_INT_GRA;
+ break;
+ case 0x0e4: /* PALR */
+ s->conf.macaddr.a[0] = value >> 24;
+ s->conf.macaddr.a[1] = value >> 16;
+ s->conf.macaddr.a[2] = value >> 8;
+ s->conf.macaddr.a[3] = value;
+ break;
+ case 0x0e8: /* PAUR */
+ s->conf.macaddr.a[4] = value >> 24;
+ s->conf.macaddr.a[5] = value >> 16;
+ break;
+ case 0x0ec:
+ /* OPD */
+ break;
+ case 0x118:
+ case 0x11c:
+ case 0x120:
+ case 0x124:
+ /* TODO: implement MAC hash filtering. */
+ break;
+ case 0x144:
+ s->tfwr = value & 3;
+ break;
+ case 0x14c:
+ /* FRBR writes ignored. */
+ break;
+ case 0x150:
+ s->rfsr = (value & 0x3fc) | 0x400;
+ break;
+ case 0x180:
+ s->erdsr = value & ~3;
+ s->rx_descriptor = s->erdsr;
+ break;
+ case 0x184:
+ s->etdsr = value & ~3;
+ s->tx_descriptor = s->etdsr;
+ break;
+ case 0x188:
+ s->emrbr = value & 0x7f0;
+ break;
+ default:
+ hw_error("mcf_fec_write Bad address 0x%x\n", (int)addr);
+ }
+ mcf_fec_update(s);
+}
+
+static int mcf_fec_can_receive(VLANClientState *nc)
+{
+ mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ return s->rx_enabled;
+}
+
+static ssize_t mcf_fec_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ mcf_fec_bd bd;
+ uint32_t flags = 0;
+ uint32_t addr;
+ uint32_t crc;
+ uint32_t buf_addr;
+ uint8_t *crc_ptr;
+ unsigned int buf_len;
+
+ DPRINTF("do_rx len %d\n", size);
+ if (!s->rx_enabled) {
+ fprintf(stderr, "mcf_fec_receive: Unexpected packet\n");
+ }
+ /* 4 bytes for the CRC. */
+ size += 4;
+ crc = cpu_to_be32(crc32(~0, buf, size));
+ crc_ptr = (uint8_t *)&crc;
+ /* Huge frames are truncted. */
+ if (size > FEC_MAX_FRAME_SIZE) {
+ size = FEC_MAX_FRAME_SIZE;
+ flags |= FEC_BD_TR | FEC_BD_LG;
+ }
+ /* Frames larger than the user limit just set error flags. */
+ if (size > (s->rcr >> 16)) {
+ flags |= FEC_BD_LG;
+ }
+ addr = s->rx_descriptor;
+ while (size > 0) {
+ mcf_fec_read_bd(&bd, addr);
+ if ((bd.flags & FEC_BD_E) == 0) {
+ /* No descriptors available. Bail out. */
+ /* FIXME: This is wrong. We should probably either save the
+ remainder for when more RX buffers are available, or
+ flag an error. */
+ fprintf(stderr, "mcf_fec: Lost end of frame\n");
+ break;
+ }
+ buf_len = (size <= s->emrbr) ? size: s->emrbr;
+ bd.length = buf_len;
+ size -= buf_len;
+ DPRINTF("rx_bd %x length %d\n", addr, bd.length);
+ /* The last 4 bytes are the CRC. */
+ if (size < 4)
+ buf_len += size - 4;
+ buf_addr = bd.data;
+ cpu_physical_memory_write(buf_addr, buf, buf_len);
+ buf += buf_len;
+ if (size < 4) {
+ cpu_physical_memory_write(buf_addr + buf_len, crc_ptr, 4 - size);
+ crc_ptr += 4 - size;
+ }
+ bd.flags &= ~FEC_BD_E;
+ if (size == 0) {
+ /* Last buffer in frame. */
+ bd.flags |= flags | FEC_BD_L;
+ DPRINTF("rx frame flags %04x\n", bd.flags);
+ s->eir |= FEC_INT_RXF;
+ } else {
+ s->eir |= FEC_INT_RXB;
+ }
+ mcf_fec_write_bd(&bd, addr);
+ /* Advance to the next descriptor. */
+ if ((bd.flags & FEC_BD_W) != 0) {
+ addr = s->erdsr;
+ } else {
+ addr += 8;
+ }
+ }
+ s->rx_descriptor = addr;
+ mcf_fec_enable_rx(s);
+ mcf_fec_update(s);
+ return size;
+}
+
+static CPUReadMemoryFunc * const mcf_fec_readfn[] = {
+ mcf_fec_read,
+ mcf_fec_read,
+ mcf_fec_read
+};
+
+static CPUWriteMemoryFunc * const mcf_fec_writefn[] = {
+ mcf_fec_write,
+ mcf_fec_write,
+ mcf_fec_write
+};
+
+static void mcf_fec_cleanup(VLANClientState *nc)
+{
+ mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ cpu_unregister_io_memory(s->mmio_index);
+
+ qemu_free(s);
+}
+
+static NetClientInfo net_mcf_fec_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = mcf_fec_can_receive,
+ .receive = mcf_fec_receive,
+ .cleanup = mcf_fec_cleanup,
+};
+
+void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq)
+{
+ mcf_fec_state *s;
+
+ qemu_check_nic_model(nd, "mcf_fec");
+
+ s = (mcf_fec_state *)qemu_mallocz(sizeof(mcf_fec_state));
+ s->irq = irq;
+ s->mmio_index = cpu_register_io_memory(mcf_fec_readfn,
+ mcf_fec_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x400, s->mmio_index);
+
+ memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
+ s->conf.vlan = nd->vlan;
+ s->conf.peer = nd->netdev;
+
+ s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s);
+
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+}
diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c
new file mode 100644
index 0000000..ac04295
--- /dev/null
+++ b/hw/mcf_intc.c
@@ -0,0 +1,157 @@
+/*
+ * ColdFire Interrupt Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+#include "hw.h"
+#include "mcf.h"
+
+typedef struct {
+ uint64_t ipr;
+ uint64_t imr;
+ uint64_t ifr;
+ uint64_t enabled;
+ uint8_t icr[64];
+ CPUState *env;
+ int active_vector;
+} mcf_intc_state;
+
+static void mcf_intc_update(mcf_intc_state *s)
+{
+ uint64_t active;
+ int i;
+ int best;
+ int best_level;
+
+ active = (s->ipr | s->ifr) & s->enabled & ~s->imr;
+ best_level = 0;
+ best = 64;
+ if (active) {
+ for (i = 0; i < 64; i++) {
+ if ((active & 1) != 0 && s->icr[i] >= best_level) {
+ best_level = s->icr[i];
+ best = i;
+ }
+ active >>= 1;
+ }
+ }
+ s->active_vector = ((best == 64) ? 24 : (best + 64));
+ m68k_set_irq_level(s->env, best_level, s->active_vector);
+}
+
+static uint32_t mcf_intc_read(void *opaque, target_phys_addr_t addr)
+{
+ int offset;
+ mcf_intc_state *s = (mcf_intc_state *)opaque;
+ offset = addr & 0xff;
+ if (offset >= 0x40 && offset < 0x80) {
+ return s->icr[offset - 0x40];
+ }
+ switch (offset) {
+ case 0x00:
+ return (uint32_t)(s->ipr >> 32);
+ case 0x04:
+ return (uint32_t)s->ipr;
+ case 0x08:
+ return (uint32_t)(s->imr >> 32);
+ case 0x0c:
+ return (uint32_t)s->imr;
+ case 0x10:
+ return (uint32_t)(s->ifr >> 32);
+ case 0x14:
+ return (uint32_t)s->ifr;
+ case 0xe0: /* SWIACK. */
+ return s->active_vector;
+ case 0xe1: case 0xe2: case 0xe3: case 0xe4:
+ case 0xe5: case 0xe6: case 0xe7:
+ /* LnIACK */
+ hw_error("mcf_intc_read: LnIACK not implemented\n");
+ default:
+ return 0;
+ }
+}
+
+static void mcf_intc_write(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ int offset;
+ mcf_intc_state *s = (mcf_intc_state *)opaque;
+ offset = addr & 0xff;
+ if (offset >= 0x40 && offset < 0x80) {
+ int n = offset - 0x40;
+ s->icr[n] = val;
+ if (val == 0)
+ s->enabled &= ~(1ull << n);
+ else
+ s->enabled |= (1ull << n);
+ mcf_intc_update(s);
+ return;
+ }
+ switch (offset) {
+ case 0x00: case 0x04:
+ /* Ignore IPR writes. */
+ return;
+ case 0x08:
+ s->imr = (s->imr & 0xffffffff) | ((uint64_t)val << 32);
+ break;
+ case 0x0c:
+ s->imr = (s->imr & 0xffffffff00000000ull) | (uint32_t)val;
+ break;
+ default:
+ hw_error("mcf_intc_write: Bad write offset %d\n", offset);
+ break;
+ }
+ mcf_intc_update(s);
+}
+
+static void mcf_intc_set_irq(void *opaque, int irq, int level)
+{
+ mcf_intc_state *s = (mcf_intc_state *)opaque;
+ if (irq >= 64)
+ return;
+ if (level)
+ s->ipr |= 1ull << irq;
+ else
+ s->ipr &= ~(1ull << irq);
+ mcf_intc_update(s);
+}
+
+static void mcf_intc_reset(mcf_intc_state *s)
+{
+ s->imr = ~0ull;
+ s->ipr = 0;
+ s->ifr = 0;
+ s->enabled = 0;
+ memset(s->icr, 0, 64);
+ s->active_vector = 24;
+}
+
+static CPUReadMemoryFunc * const mcf_intc_readfn[] = {
+ mcf_intc_read,
+ mcf_intc_read,
+ mcf_intc_read
+};
+
+static CPUWriteMemoryFunc * const mcf_intc_writefn[] = {
+ mcf_intc_write,
+ mcf_intc_write,
+ mcf_intc_write
+};
+
+qemu_irq *mcf_intc_init(target_phys_addr_t base, CPUState *env)
+{
+ mcf_intc_state *s;
+ int iomemtype;
+
+ s = qemu_mallocz(sizeof(mcf_intc_state));
+ s->env = env;
+ mcf_intc_reset(s);
+
+ iomemtype = cpu_register_io_memory(mcf_intc_readfn,
+ mcf_intc_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+
+ return qemu_allocate_irqs(mcf_intc_set_irq, s, 64);
+}
diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c
new file mode 100644
index 0000000..db57096
--- /dev/null
+++ b/hw/mcf_uart.c
@@ -0,0 +1,310 @@
+/*
+ * ColdFire UART emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+#include "hw.h"
+#include "mcf.h"
+#include "qemu-char.h"
+
+typedef struct {
+ uint8_t mr[2];
+ uint8_t sr;
+ uint8_t isr;
+ uint8_t imr;
+ uint8_t bg1;
+ uint8_t bg2;
+ uint8_t fifo[4];
+ uint8_t tb;
+ int current_mr;
+ int fifo_len;
+ int tx_enabled;
+ int rx_enabled;
+ qemu_irq irq;
+ CharDriverState *chr;
+} mcf_uart_state;
+
+/* UART Status Register bits. */
+#define MCF_UART_RxRDY 0x01
+#define MCF_UART_FFULL 0x02
+#define MCF_UART_TxRDY 0x04
+#define MCF_UART_TxEMP 0x08
+#define MCF_UART_OE 0x10
+#define MCF_UART_PE 0x20
+#define MCF_UART_FE 0x40
+#define MCF_UART_RB 0x80
+
+/* Interrupt flags. */
+#define MCF_UART_TxINT 0x01
+#define MCF_UART_RxINT 0x02
+#define MCF_UART_DBINT 0x04
+#define MCF_UART_COSINT 0x80
+
+/* UMR1 flags. */
+#define MCF_UART_BC0 0x01
+#define MCF_UART_BC1 0x02
+#define MCF_UART_PT 0x04
+#define MCF_UART_PM0 0x08
+#define MCF_UART_PM1 0x10
+#define MCF_UART_ERR 0x20
+#define MCF_UART_RxIRQ 0x40
+#define MCF_UART_RxRTS 0x80
+
+static void mcf_uart_update(mcf_uart_state *s)
+{
+ s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT);
+ if (s->sr & MCF_UART_TxRDY)
+ s->isr |= MCF_UART_TxINT;
+ if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ)
+ ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0)
+ s->isr |= MCF_UART_RxINT;
+
+ qemu_set_irq(s->irq, (s->isr & s->imr) != 0);
+}
+
+uint32_t mcf_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ mcf_uart_state *s = (mcf_uart_state *)opaque;
+ switch (addr & 0x3f) {
+ case 0x00:
+ return s->mr[s->current_mr];
+ case 0x04:
+ return s->sr;
+ case 0x0c:
+ {
+ uint8_t val;
+ int i;
+
+ if (s->fifo_len == 0)
+ return 0;
+
+ val = s->fifo[0];
+ s->fifo_len--;
+ for (i = 0; i < s->fifo_len; i++)
+ s->fifo[i] = s->fifo[i + 1];
+ s->sr &= ~MCF_UART_FFULL;
+ if (s->fifo_len == 0)
+ s->sr &= ~MCF_UART_RxRDY;
+ mcf_uart_update(s);
+ qemu_chr_accept_input(s->chr);
+ return val;
+ }
+ case 0x10:
+ /* TODO: Implement IPCR. */
+ return 0;
+ case 0x14:
+ return s->isr;
+ case 0x18:
+ return s->bg1;
+ case 0x1c:
+ return s->bg2;
+ default:
+ return 0;
+ }
+}
+
+/* Update TxRDY flag and set data if present and enabled. */
+static void mcf_uart_do_tx(mcf_uart_state *s)
+{
+ if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) {
+ if (s->chr)
+ qemu_chr_write(s->chr, (unsigned char *)&s->tb, 1);
+ s->sr |= MCF_UART_TxEMP;
+ }
+ if (s->tx_enabled) {
+ s->sr |= MCF_UART_TxRDY;
+ } else {
+ s->sr &= ~MCF_UART_TxRDY;
+ }
+}
+
+static void mcf_do_command(mcf_uart_state *s, uint8_t cmd)
+{
+ /* Misc command. */
+ switch ((cmd >> 4) & 3) {
+ case 0: /* No-op. */
+ break;
+ case 1: /* Reset mode register pointer. */
+ s->current_mr = 0;
+ break;
+ case 2: /* Reset receiver. */
+ s->rx_enabled = 0;
+ s->fifo_len = 0;
+ s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL);
+ break;
+ case 3: /* Reset transmitter. */
+ s->tx_enabled = 0;
+ s->sr |= MCF_UART_TxEMP;
+ s->sr &= ~MCF_UART_TxRDY;
+ break;
+ case 4: /* Reset error status. */
+ break;
+ case 5: /* Reset break-change interrupt. */
+ s->isr &= ~MCF_UART_DBINT;
+ break;
+ case 6: /* Start break. */
+ case 7: /* Stop break. */
+ break;
+ }
+
+ /* Transmitter command. */
+ switch ((cmd >> 2) & 3) {
+ case 0: /* No-op. */
+ break;
+ case 1: /* Enable. */
+ s->tx_enabled = 1;
+ mcf_uart_do_tx(s);
+ break;
+ case 2: /* Disable. */
+ s->tx_enabled = 0;
+ mcf_uart_do_tx(s);
+ break;
+ case 3: /* Reserved. */
+ fprintf(stderr, "mcf_uart: Bad TX command\n");
+ break;
+ }
+
+ /* Receiver command. */
+ switch (cmd & 3) {
+ case 0: /* No-op. */
+ break;
+ case 1: /* Enable. */
+ s->rx_enabled = 1;
+ break;
+ case 2:
+ s->rx_enabled = 0;
+ break;
+ case 3: /* Reserved. */
+ fprintf(stderr, "mcf_uart: Bad RX command\n");
+ break;
+ }
+}
+
+void mcf_uart_write(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ mcf_uart_state *s = (mcf_uart_state *)opaque;
+ switch (addr & 0x3f) {
+ case 0x00:
+ s->mr[s->current_mr] = val;
+ s->current_mr = 1;
+ break;
+ case 0x04:
+ /* CSR is ignored. */
+ break;
+ case 0x08: /* Command Register. */
+ mcf_do_command(s, val);
+ break;
+ case 0x0c: /* Transmit Buffer. */
+ s->sr &= ~MCF_UART_TxEMP;
+ s->tb = val;
+ mcf_uart_do_tx(s);
+ break;
+ case 0x10:
+ /* ACR is ignored. */
+ break;
+ case 0x14:
+ s->imr = val;
+ break;
+ default:
+ break;
+ }
+ mcf_uart_update(s);
+}
+
+static void mcf_uart_reset(mcf_uart_state *s)
+{
+ s->fifo_len = 0;
+ s->mr[0] = 0;
+ s->mr[1] = 0;
+ s->sr = MCF_UART_TxEMP;
+ s->tx_enabled = 0;
+ s->rx_enabled = 0;
+ s->isr = 0;
+ s->imr = 0;
+}
+
+static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data)
+{
+ /* Break events overwrite the last byte if the fifo is full. */
+ if (s->fifo_len == 4)
+ s->fifo_len--;
+
+ s->fifo[s->fifo_len] = data;
+ s->fifo_len++;
+ s->sr |= MCF_UART_RxRDY;
+ if (s->fifo_len == 4)
+ s->sr |= MCF_UART_FFULL;
+
+ mcf_uart_update(s);
+}
+
+static void mcf_uart_event(void *opaque, int event)
+{
+ mcf_uart_state *s = (mcf_uart_state *)opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ s->isr |= MCF_UART_DBINT;
+ mcf_uart_push_byte(s, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static int mcf_uart_can_receive(void *opaque)
+{
+ mcf_uart_state *s = (mcf_uart_state *)opaque;
+
+ return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0;
+}
+
+static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ mcf_uart_state *s = (mcf_uart_state *)opaque;
+
+ mcf_uart_push_byte(s, buf[0]);
+}
+
+void *mcf_uart_init(qemu_irq irq, CharDriverState *chr)
+{
+ mcf_uart_state *s;
+
+ s = qemu_mallocz(sizeof(mcf_uart_state));
+ s->chr = chr;
+ s->irq = irq;
+ if (chr) {
+ qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive,
+ mcf_uart_event, s);
+ }
+ mcf_uart_reset(s);
+ return s;
+}
+
+
+static CPUReadMemoryFunc * const mcf_uart_readfn[] = {
+ mcf_uart_read,
+ mcf_uart_read,
+ mcf_uart_read
+};
+
+static CPUWriteMemoryFunc * const mcf_uart_writefn[] = {
+ mcf_uart_write,
+ mcf_uart_write,
+ mcf_uart_write
+};
+
+void mcf_uart_mm_init(target_phys_addr_t base, qemu_irq irq,
+ CharDriverState *chr)
+{
+ mcf_uart_state *s;
+ int iomemtype;
+
+ s = mcf_uart_init(irq, chr);
+ iomemtype = cpu_register_io_memory(mcf_uart_readfn,
+ mcf_uart_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x40, iomemtype);
+}
diff --git a/hw/microblaze_pic_cpu.c b/hw/microblaze_pic_cpu.c
new file mode 100644
index 0000000..7c59382
--- /dev/null
+++ b/hw/microblaze_pic_cpu.c
@@ -0,0 +1,50 @@
+/*
+ * QEMU MicroBlaze CPU interrupt wrapper logic.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+
+#define D(x)
+
+void pic_info(Monitor *mon)
+{}
+void irq_info(Monitor *mon)
+{}
+
+static void microblaze_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ CPUState *env = (CPUState *)opaque;
+ int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+ if (level)
+ cpu_interrupt(env, type);
+ else
+ cpu_reset_interrupt(env, type);
+}
+
+qemu_irq *microblaze_pic_init_cpu(CPUState *env);
+qemu_irq *microblaze_pic_init_cpu(CPUState *env)
+{
+ return qemu_allocate_irqs(microblaze_pic_cpu_handler, env, 2);
+}
diff --git a/hw/mips-bios.h b/hw/mips-bios.h
new file mode 100644
index 0000000..b4b88ac
--- /dev/null
+++ b/hw/mips-bios.h
@@ -0,0 +1,8 @@
+#include "cpu.h"
+
+#define BIOS_SIZE (4 * 1024 * 1024)
+#ifdef TARGET_WORDS_BIGENDIAN
+#define BIOS_FILENAME "mips_bios.bin"
+#else
+#define BIOS_FILENAME "mipsel_bios.bin"
+#endif
diff --git a/hw/mips.h b/hw/mips.h
new file mode 100644
index 0000000..73aa8f8
--- /dev/null
+++ b/hw/mips.h
@@ -0,0 +1,40 @@
+#ifndef HW_MIPS_H
+#define HW_MIPS_H
+/* Definitions for mips board emulation. */
+
+/* gt64xxx.c */
+PCIBus *gt64120_register(qemu_irq *pic);
+
+/* bonito.c */
+PCIBus *bonito_init(qemu_irq *pic);
+
+/* ds1225y.c */
+void *ds1225y_init(target_phys_addr_t mem_base, const char *filename);
+void ds1225y_set_protection(void *opaque, int protection);
+
+/* g364fb.c */
+int g364fb_mm_init(target_phys_addr_t vram_base,
+ target_phys_addr_t ctrl_base, int it_shift,
+ qemu_irq irq);
+
+/* mipsnet.c */
+void mipsnet_init(int base, qemu_irq irq, NICInfo *nd);
+
+/* jazz_led.c */
+void jazz_led_init(target_phys_addr_t base);
+
+/* rc4030.c */
+typedef struct rc4030DMAState *rc4030_dma;
+void rc4030_dma_memory_rw(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write);
+void rc4030_dma_read(void *dma, uint8_t *buf, int len);
+void rc4030_dma_write(void *dma, uint8_t *buf, int len);
+
+void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
+ qemu_irq **irqs, rc4030_dma **dmas);
+
+/* dp8393x.c */
+void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift,
+ qemu_irq irq, void* mem_opaque,
+ void (*memory_rw)(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write));
+
+#endif
diff --git a/hw/mips_addr.c b/hw/mips_addr.c
new file mode 100644
index 0000000..aa1c7d8
--- /dev/null
+++ b/hw/mips_addr.c
@@ -0,0 +1,34 @@
+/*
+ * QEMU MIPS address translation support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips_cpudevs.h"
+
+uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr)
+{
+ return addr & 0x7fffffffll;
+}
+
+uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr)
+{
+ return addr | ~0x7fffffffll;
+}
diff --git a/hw/mips_cpudevs.h b/hw/mips_cpudevs.h
new file mode 100644
index 0000000..db82b41
--- /dev/null
+++ b/hw/mips_cpudevs.h
@@ -0,0 +1,15 @@
+#ifndef HW_MIPS_CPUDEVS_H
+#define HW_MIPS_CPUDEVS_H
+/* Definitions for MIPS CPU internal devices. */
+
+/* mips_addr.c */
+uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr);
+uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr);
+
+/* mips_int.c */
+void cpu_mips_irq_init_cpu(CPUState *env);
+
+/* mips_timer.c */
+void cpu_mips_clock_init(CPUState *);
+
+#endif
diff --git a/hw/mips_fulong2e.c b/hw/mips_fulong2e.c
new file mode 100644
index 0000000..3b0abdb
--- /dev/null
+++ b/hw/mips_fulong2e.c
@@ -0,0 +1,408 @@
+/*
+ * QEMU fulong 2e mini pc support
+ *
+ * Copyright (c) 2008 yajin (yajin@vm-kernel.org)
+ * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn)
+ * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com)
+ * This code is licensed under the GNU GPL v2.
+ */
+
+/*
+ * Fulong 2e mini pc is based on ICT/ST Loongson 2e CPU (MIPS III like, 800MHz)
+ * http://www.linux-mips.org/wiki/Fulong
+ *
+ * Loongson 2e user manual:
+ * http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "net.h"
+#include "boards.h"
+#include "smbus.h"
+#include "block.h"
+#include "flash.h"
+#include "mips.h"
+#include "mips_cpudevs.h"
+#include "pci.h"
+#include "usb-uhci.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "audio/audio.h"
+#include "qemu-log.h"
+#include "loader.h"
+#include "mips-bios.h"
+#include "ide.h"
+#include "elf.h"
+#include "vt82c686.h"
+#include "mc146818rtc.h"
+#include "blockdev.h"
+
+#define DEBUG_FULONG2E_INIT
+
+#define ENVP_ADDR 0x80002000l
+#define ENVP_NB_ENTRIES 16
+#define ENVP_ENTRY_SIZE 256
+
+#define MAX_IDE_BUS 2
+
+/*
+ * PMON is not part of qemu and released with BSD license, anyone
+ * who want to build a pmon binary please first git-clone the source
+ * from the git repository at:
+ * http://www.loongson.cn/support/git/pmon
+ * Then follow the "Compile Guide" available at:
+ * http://dev.lemote.com/code/pmon
+ *
+ * Notes:
+ * 1, don't use the source at http://dev.lemote.com/http_git/pmon.git
+ * 2, use "Bonito2edev" to replace "dir_corresponding_to_your_target_hardware"
+ * in the "Compile Guide".
+ */
+#define FULONG_BIOSNAME "pmon_fulong2e.bin"
+
+/* PCI SLOT in fulong 2e */
+#define FULONG2E_VIA_SLOT 5
+#define FULONG2E_ATI_SLOT 6
+#define FULONG2E_RTL8139_SLOT 7
+
+static PITState *pit;
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
+ const char *string, ...)
+{
+ va_list ap;
+ int32_t table_addr;
+
+ if (index >= ENVP_NB_ENTRIES)
+ return;
+
+ if (string == NULL) {
+ prom_buf[index] = 0;
+ return;
+ }
+
+ table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+ prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+static int64_t load_kernel (CPUState *env)
+{
+ int64_t kernel_entry, kernel_low, kernel_high;
+ int index = 0;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+ uint32_t *prom_buf;
+ long prom_size;
+
+ if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
+ (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
+ (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
+ if (initrd_offset + initrd_size > ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset, ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Setup prom parameters. */
+ prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
+ prom_buf = qemu_malloc(prom_size);
+
+ prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename);
+ if (initrd_size > 0) {
+ prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+ cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size,
+ loaderparams.kernel_cmdline);
+ } else {
+ prom_set(prom_buf, index++, "%s", loaderparams.kernel_cmdline);
+ }
+
+ /* Setup minimum environment variables */
+ prom_set(prom_buf, index++, "busclock=33000000");
+ prom_set(prom_buf, index++, "cpuclock=100000000");
+ prom_set(prom_buf, index++, "memsize=%i", loaderparams.ram_size/1024/1024);
+ prom_set(prom_buf, index++, "modetty0=38400n8r");
+ prom_set(prom_buf, index++, NULL);
+
+ rom_add_blob_fixed("prom", prom_buf, prom_size,
+ cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+
+ return kernel_entry;
+}
+
+static void write_bootloader (CPUState *env, uint8_t *base, int64_t kernel_addr)
+{
+ uint32_t *p;
+
+ /* Small bootloader */
+ p = (uint32_t *) base;
+
+ stl_raw(p++, 0x0bf00010); /* j 0x1fc00040 */
+ stl_raw(p++, 0x00000000); /* nop */
+
+ /* Second part of the bootloader */
+ p = (uint32_t *) (base + 0x040);
+
+ stl_raw(p++, 0x3c040000); /* lui a0, 0 */
+ stl_raw(p++, 0x34840002); /* ori a0, a0, 2 */
+ stl_raw(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */
+ stl_raw(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a0, low(ENVP_ADDR) */
+ stl_raw(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
+ stl_raw(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
+ stl_raw(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(env->ram_size) */
+ stl_raw(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */
+ stl_raw(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */;
+ stl_raw(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */
+ stl_raw(p++, 0x03e00008); /* jr ra */
+ stl_raw(p++, 0x00000000); /* nop */
+}
+
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ /* TODO: 2E reset stuff */
+ if (loaderparams.kernel_filename) {
+ env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
+ }
+}
+
+uint8_t eeprom_spd[0x80] = {
+ 0x80,0x08,0x07,0x0d,0x09,0x02,0x40,0x00,0x04,0x70,
+ 0x70,0x00,0x82,0x10,0x00,0x01,0x0e,0x04,0x0c,0x01,
+ 0x02,0x20,0x80,0x75,0x70,0x00,0x00,0x50,0x3c,0x50,
+ 0x2d,0x20,0xb0,0xb0,0x50,0x50,0x00,0x00,0x00,0x00,
+ 0x00,0x41,0x48,0x3c,0x32,0x75,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x9c,0x7b,0x07,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x48,0x42,0x35,0x34,0x41,0x32,
+ 0x35,0x36,0x38,0x4b,0x4e,0x2d,0x41,0x37,0x35,0x42,
+ 0x20,0x30,0x20
+};
+
+/* Audio support */
+static void audio_init (PCIBus *pci_bus)
+{
+ vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5));
+ vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6));
+}
+
+/* Network support */
+static void network_init (void)
+{
+ int i;
+
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+ const char *default_devaddr = NULL;
+
+ if (i == 0 && (!nd->model || strcmp(nd->model, "rtl8139") == 0)) {
+ /* The fulong board has a RTL8139 card using PCI SLOT 7 */
+ default_devaddr = "07";
+ }
+
+ pci_nic_init_nofail(nd, "rtl8139", default_devaddr);
+ }
+}
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env && level) {
+ cpu_exit(env);
+ }
+}
+
+static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ char *filename;
+ unsigned long ram_offset, bios_offset;
+ long bios_size;
+ int64_t kernel_entry;
+ qemu_irq *i8259;
+ qemu_irq *cpu_exit_irq;
+ int via_devfn;
+ PCIBus *pci_bus;
+ uint8_t *eeprom_buf;
+ i2c_bus *smbus;
+ int i;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DeviceState *eeprom;
+ CPUState *env;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "Loongson-2E";
+ }
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ register_savevm(NULL, "cpu", 0, 3, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+
+ /* fulong 2e has 256M ram. */
+ ram_size = 256 * 1024 * 1024;
+
+ /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */
+ bios_size = 1024 * 1024;
+
+ /* allocate RAM */
+ ram_offset = qemu_ram_alloc(NULL, "fulong2e.ram", ram_size);
+ bios_offset = qemu_ram_alloc(NULL, "fulong2e.bios", bios_size);
+
+ cpu_register_physical_memory(0, ram_size, ram_offset);
+ cpu_register_physical_memory(0x1fc00000LL,
+ bios_size, bios_offset | IO_MEM_ROM);
+
+ /* We do not support flash operation, just loading pmon.bin as raw BIOS.
+ * Please use -L to set the BIOS path and -bios to set bios name. */
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ kernel_entry = load_kernel (env);
+ write_bootloader(env, qemu_get_ram_ptr(bios_offset), kernel_entry);
+ } else {
+ if (bios_name == NULL) {
+ bios_name = FULONG_BIOSNAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0x1fc00000LL,
+ BIOS_SIZE);
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) && !kernel_filename) {
+ fprintf(stderr, "qemu: Could not load MIPS bios '%s'\n", bios_name);
+ exit(1);
+ }
+ }
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* Interrupt controller */
+ /* The 8259 -> IP5 */
+ i8259 = i8259_init(env->irq[5]);
+
+ /* North bridge, Bonito --> IP2 */
+ pci_bus = bonito_init((qemu_irq *)&(env->irq[2]));
+
+ /* South bridge */
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ }
+
+ via_devfn = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0));
+ if (via_devfn < 0) {
+ fprintf(stderr, "vt82c686b_init error \n");
+ exit(1);
+ }
+
+ isa_bus_irqs(i8259);
+ vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1));
+ usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2));
+ usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3));
+
+ smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4),
+ 0xeee1, NULL);
+ eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+ memcpy(eeprom_buf, eeprom_spd, sizeof(eeprom_spd));
+ /* TODO: Populate SPD eeprom data. */
+ eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+ qdev_prop_set_uint8(eeprom, "address", 0x50);
+ qdev_prop_set_ptr(eeprom, "data", eeprom_buf);
+ qdev_init_nofail(eeprom);
+
+ /* init other devices */
+ pit = pit_init(0x40, isa_get_irq(0));
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+
+ /* Super I/O */
+ isa_create_simple("i8042");
+
+ rtc_init(2000, NULL);
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_isa_init(i, serial_hds[i]);
+ }
+ }
+
+ if (parallel_hds[0]) {
+ parallel_init(0, parallel_hds[0]);
+ }
+
+ /* Sound card */
+ audio_init(pci_bus);
+ /* Network card */
+ network_init();
+}
+
+QEMUMachine mips_fulong2e_machine = {
+ .name = "fulong2e",
+ .desc = "Fulong 2e mini pc",
+ .init = mips_fulong2e_init,
+};
+
+static void mips_fulong2e_machine_init(void)
+{
+ qemu_register_machine(&mips_fulong2e_machine);
+}
+
+machine_init(mips_fulong2e_machine_init);
diff --git a/hw/mips_int.c b/hw/mips_int.c
new file mode 100644
index 0000000..477f6ab
--- /dev/null
+++ b/hw/mips_int.c
@@ -0,0 +1,65 @@
+/*
+ * QEMU MIPS interrupt support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips_cpudevs.h"
+#include "cpu.h"
+
+static void cpu_mips_irq_request(void *opaque, int irq, int level)
+{
+ CPUState *env = (CPUState *)opaque;
+
+ if (irq < 0 || irq > 7)
+ return;
+
+ if (level) {
+ env->CP0_Cause |= 1 << (irq + CP0Ca_IP);
+ } else {
+ env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP));
+ }
+
+ if (env->CP0_Cause & CP0Ca_IP_mask) {
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+}
+
+void cpu_mips_irq_init_cpu(CPUState *env)
+{
+ qemu_irq *qi;
+ int i;
+
+ qi = qemu_allocate_irqs(cpu_mips_irq_request, env, 8);
+ for (i = 0; i < 8; i++) {
+ env->irq[i] = qi[i];
+ }
+}
+
+void cpu_mips_soft_irq(CPUState *env, int irq, int level)
+{
+ if (irq < 0 || irq > 2) {
+ return;
+ }
+
+ qemu_set_irq(env->irq[irq], level);
+}
diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c
new file mode 100644
index 0000000..85eba5a
--- /dev/null
+++ b/hw/mips_jazz.c
@@ -0,0 +1,314 @@
+/*
+ * QEMU MIPS Jazz support
+ *
+ * Copyright (c) 2007-2008 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips.h"
+#include "mips_cpudevs.h"
+#include "pc.h"
+#include "isa.h"
+#include "fdc.h"
+#include "sysemu.h"
+#include "arch_init.h"
+#include "boards.h"
+#include "net.h"
+#include "esp.h"
+#include "mips-bios.h"
+#include "loader.h"
+#include "mc146818rtc.h"
+#include "blockdev.h"
+
+enum jazz_model_e
+{
+ JAZZ_MAGNUM,
+ JAZZ_PICA61,
+};
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+}
+
+static uint32_t rtc_readb(void *opaque, target_phys_addr_t addr)
+{
+ return cpu_inw(0x71);
+}
+
+static void rtc_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ cpu_outw(0x71, val & 0xff);
+}
+
+static CPUReadMemoryFunc * const rtc_read[3] = {
+ rtc_readb,
+ rtc_readb,
+ rtc_readb,
+};
+
+static CPUWriteMemoryFunc * const rtc_write[3] = {
+ rtc_writeb,
+ rtc_writeb,
+ rtc_writeb,
+};
+
+static void dma_dummy_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ /* Nothing to do. That is only to ensure that
+ * the current DMA acknowledge cycle is completed. */
+}
+
+static CPUReadMemoryFunc * const dma_dummy_read[3] = {
+ NULL,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const dma_dummy_write[3] = {
+ dma_dummy_writeb,
+ dma_dummy_writeb,
+ dma_dummy_writeb,
+};
+
+#define MAGNUM_BIOS_SIZE_MAX 0x7e000
+#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX)
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env && level) {
+ cpu_exit(env);
+ }
+}
+
+static
+void mips_jazz_init (ram_addr_t ram_size,
+ const char *cpu_model,
+ enum jazz_model_e jazz_model)
+{
+ char *filename;
+ int bios_size, n;
+ CPUState *env;
+ qemu_irq *rc4030, *i8259;
+ rc4030_dma *dmas;
+ void* rc4030_opaque;
+ int s_rtc, s_dma_dummy;
+ NICInfo *nd;
+ PITState *pit;
+ DriveInfo *fds[MAX_FD];
+ qemu_irq esp_reset, dma_enable;
+ qemu_irq *cpu_exit_irq;
+ ram_addr_t ram_offset;
+ ram_addr_t bios_offset;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "R4000";
+#else
+ /* FIXME: All wrong, this maybe should be R3000 for the older JAZZs. */
+ cpu_model = "24Kf";
+#endif
+ }
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ qemu_register_reset(main_cpu_reset, env);
+
+ /* allocate RAM */
+ ram_offset = qemu_ram_alloc(NULL, "mips_jazz.ram", ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
+
+ bios_offset = qemu_ram_alloc(NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE);
+ cpu_register_physical_memory(0x1fc00000LL,
+ MAGNUM_BIOS_SIZE, bios_offset | IO_MEM_ROM);
+ cpu_register_physical_memory(0xfff00000LL,
+ MAGNUM_BIOS_SIZE, bios_offset | IO_MEM_ROM);
+
+ /* load the BIOS image. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0xfff00000LL,
+ MAGNUM_BIOS_SIZE);
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size < 0 || bios_size > MAGNUM_BIOS_SIZE) {
+ fprintf(stderr, "qemu: Could not load MIPS bios '%s'\n",
+ bios_name);
+ exit(1);
+ }
+
+ /* Init CPU internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* Chipset */
+ rc4030_opaque = rc4030_init(env->irq[6], env->irq[3], &rc4030, &dmas);
+ s_dma_dummy = cpu_register_io_memory(dma_dummy_read, dma_dummy_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0x8000d000, 0x00001000, s_dma_dummy);
+
+ /* ISA devices */
+ i8259 = i8259_init(env->irq[4]);
+ isa_bus_new(NULL);
+ isa_bus_irqs(i8259);
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+ pit = pit_init(0x40, i8259[0]);
+ pcspk_init(pit);
+
+ /* ISA IO space at 0x90000000 */
+ isa_mmio_init(0x90000000, 0x01000000);
+ isa_mem_base = 0x11000000;
+
+ /* Video card */
+ switch (jazz_model) {
+ case JAZZ_MAGNUM:
+ g364fb_mm_init(0x40000000, 0x60000000, 0, rc4030[3]);
+ break;
+ case JAZZ_PICA61:
+ isa_vga_mm_init(0x40000000, 0x60000000, 0);
+ break;
+ default:
+ break;
+ }
+
+ /* Network controller */
+ for (n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+ if (!nd->model)
+ nd->model = qemu_strdup("dp83932");
+ if (strcmp(nd->model, "dp83932") == 0) {
+ dp83932_init(nd, 0x80001000, 2, rc4030[4],
+ rc4030_opaque, rc4030_dma_memory_rw);
+ break;
+ } else if (strcmp(nd->model, "?") == 0) {
+ fprintf(stderr, "qemu: Supported NICs: dp83932\n");
+ exit(1);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+ exit(1);
+ }
+ }
+
+ /* SCSI adapter */
+ esp_init(0x80002000, 0,
+ rc4030_dma_read, rc4030_dma_write, dmas[0],
+ rc4030[5], &esp_reset, &dma_enable);
+
+ /* Floppy */
+ if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) {
+ fprintf(stderr, "qemu: too many floppy drives\n");
+ exit(1);
+ }
+ for (n = 0; n < MAX_FD; n++) {
+ fds[n] = drive_get(IF_FLOPPY, 0, n);
+ }
+ fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds);
+
+ /* Real time clock */
+ rtc_init(1980, NULL);
+ s_rtc = cpu_register_io_memory(rtc_read, rtc_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0x80004000, 0x00001000, s_rtc);
+
+ /* Keyboard (i8042) */
+ i8042_mm_init(rc4030[6], rc4030[7], 0x80005000, 0x1000, 0x1);
+
+ /* Serial ports */
+ if (serial_hds[0]) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(0x80006000, 0, rc4030[8], 8000000/16, serial_hds[0], 1, 1);
+#else
+ serial_mm_init(0x80006000, 0, rc4030[8], 8000000/16, serial_hds[0], 1, 0);
+#endif
+ }
+ if (serial_hds[1]) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(0x80007000, 0, rc4030[9], 8000000/16, serial_hds[1], 1, 1);
+#else
+ serial_mm_init(0x80007000, 0, rc4030[9], 8000000/16, serial_hds[1], 1, 0);
+#endif
+ }
+
+ /* Parallel port */
+ if (parallel_hds[0])
+ parallel_mm_init(0x80008000, 0, rc4030[0], parallel_hds[0]);
+
+ /* Sound card */
+ /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */
+ audio_init(i8259, NULL);
+
+ /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */
+ ds1225y_init(0x80009000, "nvram");
+
+ /* LED indicator */
+ jazz_led_init(0x8000f000);
+}
+
+static
+void mips_magnum_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ mips_jazz_init(ram_size, cpu_model, JAZZ_MAGNUM);
+}
+
+static
+void mips_pica61_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ mips_jazz_init(ram_size, cpu_model, JAZZ_PICA61);
+}
+
+static QEMUMachine mips_magnum_machine = {
+ .name = "magnum",
+ .desc = "MIPS Magnum",
+ .init = mips_magnum_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine mips_pica61_machine = {
+ .name = "pica61",
+ .desc = "Acer Pica 61",
+ .init = mips_pica61_init,
+ .use_scsi = 1,
+};
+
+static void mips_jazz_machine_init(void)
+{
+ qemu_register_machine(&mips_magnum_machine);
+ qemu_register_machine(&mips_pica61_machine);
+}
+
+machine_init(mips_jazz_machine_init);
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
new file mode 100644
index 0000000..7211d98
--- /dev/null
+++ b/hw/mips_malta.c
@@ -0,0 +1,978 @@
+/*
+ * QEMU Malta board support
+ *
+ * Copyright (c) 2006 Aurelien Jarno
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "fdc.h"
+#include "net.h"
+#include "boards.h"
+#include "smbus.h"
+#include "block.h"
+#include "flash.h"
+#include "mips.h"
+#include "mips_cpudevs.h"
+#include "pci.h"
+#include "usb-uhci.h"
+#include "vmware_vga.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "arch_init.h"
+#include "boards.h"
+#include "qemu-log.h"
+#include "mips-bios.h"
+#include "ide.h"
+#include "loader.h"
+#include "elf.h"
+#include "mc146818rtc.h"
+#include "blockdev.h"
+
+//#define DEBUG_BOARD_INIT
+
+#define ENVP_ADDR 0x80002000l
+#define ENVP_NB_ENTRIES 16
+#define ENVP_ENTRY_SIZE 256
+
+#define MAX_IDE_BUS 2
+
+typedef struct {
+ uint32_t leds;
+ uint32_t brk;
+ uint32_t gpout;
+ uint32_t i2cin;
+ uint32_t i2coe;
+ uint32_t i2cout;
+ uint32_t i2csel;
+ CharDriverState *display;
+ char display_text[9];
+ SerialState *uart;
+} MaltaFPGAState;
+
+static PITState *pit;
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+/* Malta FPGA */
+static void malta_fpga_update_display(void *opaque)
+{
+ char leds_text[9];
+ int i;
+ MaltaFPGAState *s = opaque;
+
+ for (i = 7 ; i >= 0 ; i--) {
+ if (s->leds & (1 << i))
+ leds_text[i] = '#';
+ else
+ leds_text[i] = ' ';
+ }
+ leds_text[8] = '\0';
+
+ qemu_chr_printf(s->display, "\e[H\n\n|\e[32m%-8.8s\e[00m|\r\n", leds_text);
+ qemu_chr_printf(s->display, "\n\n\n\n|\e[31m%-8.8s\e[00m|", s->display_text);
+}
+
+/*
+ * EEPROM 24C01 / 24C02 emulation.
+ *
+ * Emulation for serial EEPROMs:
+ * 24C01 - 1024 bit (128 x 8)
+ * 24C02 - 2048 bit (256 x 8)
+ *
+ * Typical device names include Microchip 24C02SC or SGS Thomson ST24C02.
+ */
+
+//~ #define DEBUG
+
+#if defined(DEBUG)
+# define logout(fmt, ...) fprintf(stderr, "MALTA\t%-24s" fmt, __func__, ## __VA_ARGS__)
+#else
+# define logout(fmt, ...) ((void)0)
+#endif
+
+struct _eeprom24c0x_t {
+ uint8_t tick;
+ uint8_t address;
+ uint8_t command;
+ uint8_t ack;
+ uint8_t scl;
+ uint8_t sda;
+ uint8_t data;
+ //~ uint16_t size;
+ uint8_t contents[256];
+};
+
+typedef struct _eeprom24c0x_t eeprom24c0x_t;
+
+static eeprom24c0x_t eeprom = {
+ .contents = {
+ /* 00000000: */ 0x80,0x08,0x04,0x0D,0x0A,0x01,0x40,0x00,
+ /* 00000008: */ 0x01,0x75,0x54,0x00,0x82,0x08,0x00,0x01,
+ /* 00000010: */ 0x8F,0x04,0x02,0x01,0x01,0x00,0x0E,0x00,
+ /* 00000018: */ 0x00,0x00,0x00,0x14,0x0F,0x14,0x2D,0x40,
+ /* 00000020: */ 0x15,0x08,0x15,0x08,0x00,0x00,0x00,0x00,
+ /* 00000028: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000030: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000038: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xD0,
+ /* 00000040: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000048: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000050: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000058: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000060: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000068: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000070: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ /* 00000078: */ 0x00,0x00,0x00,0x00,0x00,0x00,0x64,0xF4,
+ },
+};
+
+static uint8_t eeprom24c0x_read(void)
+{
+ logout("%u: scl = %u, sda = %u, data = 0x%02x\n",
+ eeprom.tick, eeprom.scl, eeprom.sda, eeprom.data);
+ return eeprom.sda;
+}
+
+static void eeprom24c0x_write(int scl, int sda)
+{
+ if (eeprom.scl && scl && (eeprom.sda != sda)) {
+ logout("%u: scl = %u->%u, sda = %u->%u i2c %s\n",
+ eeprom.tick, eeprom.scl, scl, eeprom.sda, sda, sda ? "stop" : "start");
+ if (!sda) {
+ eeprom.tick = 1;
+ eeprom.command = 0;
+ }
+ } else if (eeprom.tick == 0 && !eeprom.ack) {
+ /* Waiting for start. */
+ logout("%u: scl = %u->%u, sda = %u->%u wait for i2c start\n",
+ eeprom.tick, eeprom.scl, scl, eeprom.sda, sda);
+ } else if (!eeprom.scl && scl) {
+ logout("%u: scl = %u->%u, sda = %u->%u trigger bit\n",
+ eeprom.tick, eeprom.scl, scl, eeprom.sda, sda);
+ if (eeprom.ack) {
+ logout("\ti2c ack bit = 0\n");
+ sda = 0;
+ eeprom.ack = 0;
+ } else if (eeprom.sda == sda) {
+ uint8_t bit = (sda != 0);
+ logout("\ti2c bit = %d\n", bit);
+ if (eeprom.tick < 9) {
+ eeprom.command <<= 1;
+ eeprom.command += bit;
+ eeprom.tick++;
+ if (eeprom.tick == 9) {
+ logout("\tcommand 0x%04x, %s\n", eeprom.command, bit ? "read" : "write");
+ eeprom.ack = 1;
+ }
+ } else if (eeprom.tick < 17) {
+ if (eeprom.command & 1) {
+ sda = ((eeprom.data & 0x80) != 0);
+ }
+ eeprom.address <<= 1;
+ eeprom.address += bit;
+ eeprom.tick++;
+ eeprom.data <<= 1;
+ if (eeprom.tick == 17) {
+ eeprom.data = eeprom.contents[eeprom.address];
+ logout("\taddress 0x%04x, data 0x%02x\n", eeprom.address, eeprom.data);
+ eeprom.ack = 1;
+ eeprom.tick = 0;
+ }
+ } else if (eeprom.tick >= 17) {
+ sda = 0;
+ }
+ } else {
+ logout("\tsda changed with raising scl\n");
+ }
+ } else {
+ logout("%u: scl = %u->%u, sda = %u->%u\n", eeprom.tick, eeprom.scl, scl, eeprom.sda, sda);
+ }
+ eeprom.scl = scl;
+ eeprom.sda = sda;
+}
+
+static uint32_t malta_fpga_readl(void *opaque, target_phys_addr_t addr)
+{
+ MaltaFPGAState *s = opaque;
+ uint32_t val = 0;
+ uint32_t saddr;
+
+ saddr = (addr & 0xfffff);
+
+ switch (saddr) {
+
+ /* SWITCH Register */
+ case 0x00200:
+ val = 0x00000000; /* All switches closed */
+ break;
+
+ /* STATUS Register */
+ case 0x00208:
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = 0x00000012;
+#else
+ val = 0x00000010;
+#endif
+ break;
+
+ /* JMPRS Register */
+ case 0x00210:
+ val = 0x00;
+ break;
+
+ /* LEDBAR Register */
+ case 0x00408:
+ val = s->leds;
+ break;
+
+ /* BRKRES Register */
+ case 0x00508:
+ val = s->brk;
+ break;
+
+ /* UART Registers are handled directly by the serial device */
+
+ /* GPOUT Register */
+ case 0x00a00:
+ val = s->gpout;
+ break;
+
+ /* XXX: implement a real I2C controller */
+
+ /* GPINP Register */
+ case 0x00a08:
+ /* IN = OUT until a real I2C control is implemented */
+ if (s->i2csel)
+ val = s->i2cout;
+ else
+ val = 0x00;
+ break;
+
+ /* I2CINP Register */
+ case 0x00b00:
+ val = ((s->i2cin & ~1) | eeprom24c0x_read());
+ break;
+
+ /* I2COE Register */
+ case 0x00b08:
+ val = s->i2coe;
+ break;
+
+ /* I2COUT Register */
+ case 0x00b10:
+ val = s->i2cout;
+ break;
+
+ /* I2CSEL Register */
+ case 0x00b18:
+ val = s->i2csel;
+ break;
+
+ default:
+#if 0
+ printf ("malta_fpga_read: Bad register offset 0x" TARGET_FMT_lx "\n",
+ addr);
+#endif
+ break;
+ }
+ return val;
+}
+
+static void malta_fpga_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MaltaFPGAState *s = opaque;
+ uint32_t saddr;
+
+ saddr = (addr & 0xfffff);
+
+ switch (saddr) {
+
+ /* SWITCH Register */
+ case 0x00200:
+ break;
+
+ /* JMPRS Register */
+ case 0x00210:
+ break;
+
+ /* LEDBAR Register */
+ /* XXX: implement a 8-LED array */
+ case 0x00408:
+ s->leds = val & 0xff;
+ break;
+
+ /* ASCIIWORD Register */
+ case 0x00410:
+ snprintf(s->display_text, 9, "%08X", val);
+ malta_fpga_update_display(s);
+ break;
+
+ /* ASCIIPOS0 to ASCIIPOS7 Registers */
+ case 0x00418:
+ case 0x00420:
+ case 0x00428:
+ case 0x00430:
+ case 0x00438:
+ case 0x00440:
+ case 0x00448:
+ case 0x00450:
+ s->display_text[(saddr - 0x00418) >> 3] = (char) val;
+ malta_fpga_update_display(s);
+ break;
+
+ /* SOFTRES Register */
+ case 0x00500:
+ if (val == 0x42)
+ qemu_system_reset_request ();
+ break;
+
+ /* BRKRES Register */
+ case 0x00508:
+ s->brk = val & 0xff;
+ break;
+
+ /* UART Registers are handled directly by the serial device */
+
+ /* GPOUT Register */
+ case 0x00a00:
+ s->gpout = val & 0xff;
+ break;
+
+ /* I2COE Register */
+ case 0x00b08:
+ s->i2coe = val & 0x03;
+ break;
+
+ /* I2COUT Register */
+ case 0x00b10:
+ eeprom24c0x_write(val & 0x02, val & 0x01);
+ s->i2cout = val;
+ break;
+
+ /* I2CSEL Register */
+ case 0x00b18:
+ s->i2csel = val & 0x01;
+ break;
+
+ default:
+#if 0
+ printf ("malta_fpga_write: Bad register offset 0x" TARGET_FMT_lx "\n",
+ addr);
+#endif
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const malta_fpga_read[] = {
+ malta_fpga_readl,
+ malta_fpga_readl,
+ malta_fpga_readl
+};
+
+static CPUWriteMemoryFunc * const malta_fpga_write[] = {
+ malta_fpga_writel,
+ malta_fpga_writel,
+ malta_fpga_writel
+};
+
+static void malta_fpga_reset(void *opaque)
+{
+ MaltaFPGAState *s = opaque;
+
+ s->leds = 0x00;
+ s->brk = 0x0a;
+ s->gpout = 0x00;
+ s->i2cin = 0x3;
+ s->i2coe = 0x0;
+ s->i2cout = 0x3;
+ s->i2csel = 0x1;
+
+ s->display_text[8] = '\0';
+ snprintf(s->display_text, 9, " ");
+}
+
+static void malta_fpga_led_init(CharDriverState *chr)
+{
+ qemu_chr_printf(chr, "\e[HMalta LEDBAR\r\n");
+ qemu_chr_printf(chr, "+--------+\r\n");
+ qemu_chr_printf(chr, "+ +\r\n");
+ qemu_chr_printf(chr, "+--------+\r\n");
+ qemu_chr_printf(chr, "\n");
+ qemu_chr_printf(chr, "Malta ASCII\r\n");
+ qemu_chr_printf(chr, "+--------+\r\n");
+ qemu_chr_printf(chr, "+ +\r\n");
+ qemu_chr_printf(chr, "+--------+\r\n");
+}
+
+static MaltaFPGAState *malta_fpga_init(target_phys_addr_t base, qemu_irq uart_irq, CharDriverState *uart_chr)
+{
+ MaltaFPGAState *s;
+ int malta;
+
+ s = (MaltaFPGAState *)qemu_mallocz(sizeof(MaltaFPGAState));
+
+ malta = cpu_register_io_memory(malta_fpga_read,
+ malta_fpga_write, s,
+ DEVICE_NATIVE_ENDIAN);
+
+ cpu_register_physical_memory(base, 0x900, malta);
+ /* 0xa00 is less than a page, so will still get the right offsets. */
+ cpu_register_physical_memory(base + 0xa00, 0x100000 - 0xa00, malta);
+
+ s->display = qemu_chr_open("fpga", "vc:320x200", malta_fpga_led_init);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->uart = serial_mm_init(base + 0x900, 3, uart_irq, 230400, uart_chr, 1, 1);
+#else
+ s->uart = serial_mm_init(base + 0x900, 3, uart_irq, 230400, uart_chr, 1, 0);
+#endif
+
+ malta_fpga_reset(s);
+ qemu_register_reset(malta_fpga_reset, s);
+
+ return s;
+}
+
+/* Network support */
+static void network_init(void)
+{
+ int i;
+
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+ const char *default_devaddr = NULL;
+
+ if (i == 0 && (!nd->model || strcmp(nd->model, "pcnet") == 0))
+ /* The malta board has a PCNet card using PCI SLOT 11 */
+ default_devaddr = "0b";
+
+ pci_nic_init_nofail(nd, "pcnet", default_devaddr);
+ }
+}
+
+/* ROM and pseudo bootloader
+
+ The following code implements a very very simple bootloader. It first
+ loads the registers a0 to a3 to the values expected by the OS, and
+ then jump at the kernel address.
+
+ The bootloader should pass the locations of the kernel arguments and
+ environment variables tables. Those tables contain the 32-bit address
+ of NULL terminated strings. The environment variables table should be
+ terminated by a NULL address.
+
+ For a simpler implementation, the number of kernel arguments is fixed
+ to two (the name of the kernel and the command line), and the two
+ tables are actually the same one.
+
+ The registers a0 to a3 should contain the following values:
+ a0 - number of kernel arguments
+ a1 - 32-bit address of the kernel arguments table
+ a2 - 32-bit address of the environment variables table
+ a3 - RAM size in bytes
+*/
+
+static void write_bootloader (CPUState *env, uint8_t *base,
+ int64_t kernel_entry)
+{
+ uint32_t *p;
+
+ /* Small bootloader */
+ p = (uint32_t *)base;
+ stl_raw(p++, 0x0bf00160); /* j 0x1fc00580 */
+ stl_raw(p++, 0x00000000); /* nop */
+
+ /* YAMON service vector */
+ stl_raw(base + 0x500, 0xbfc00580); /* start: */
+ stl_raw(base + 0x504, 0xbfc0083c); /* print_count: */
+ stl_raw(base + 0x520, 0xbfc00580); /* start: */
+ stl_raw(base + 0x52c, 0xbfc00800); /* flush_cache: */
+ stl_raw(base + 0x534, 0xbfc00808); /* print: */
+ stl_raw(base + 0x538, 0xbfc00800); /* reg_cpu_isr: */
+ stl_raw(base + 0x53c, 0xbfc00800); /* unred_cpu_isr: */
+ stl_raw(base + 0x540, 0xbfc00800); /* reg_ic_isr: */
+ stl_raw(base + 0x544, 0xbfc00800); /* unred_ic_isr: */
+ stl_raw(base + 0x548, 0xbfc00800); /* reg_esr: */
+ stl_raw(base + 0x54c, 0xbfc00800); /* unreg_esr: */
+ stl_raw(base + 0x550, 0xbfc00800); /* getchar: */
+ stl_raw(base + 0x554, 0xbfc00800); /* syscon_read: */
+
+
+ /* Second part of the bootloader */
+ p = (uint32_t *) (base + 0x580);
+ stl_raw(p++, 0x24040002); /* addiu a0, zero, 2 */
+ stl_raw(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */
+ stl_raw(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */
+ stl_raw(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */
+ stl_raw(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */
+ stl_raw(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */
+ stl_raw(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */
+ stl_raw(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(ram_size) */
+ stl_raw(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(ram_size) */
+
+ /* Load BAR registers as done by YAMON */
+ stl_raw(p++, 0x3c09b400); /* lui t1, 0xb400 */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c08df00); /* lui t0, 0xdf00 */
+#else
+ stl_raw(p++, 0x340800df); /* ori t0, r0, 0x00df */
+#endif
+ stl_raw(p++, 0xad280068); /* sw t0, 0x0068(t1) */
+
+ stl_raw(p++, 0x3c09bbe0); /* lui t1, 0xbbe0 */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c08c000); /* lui t0, 0xc000 */
+#else
+ stl_raw(p++, 0x340800c0); /* ori t0, r0, 0x00c0 */
+#endif
+ stl_raw(p++, 0xad280048); /* sw t0, 0x0048(t1) */
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c084000); /* lui t0, 0x4000 */
+#else
+ stl_raw(p++, 0x34080040); /* ori t0, r0, 0x0040 */
+#endif
+ stl_raw(p++, 0xad280050); /* sw t0, 0x0050(t1) */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c088000); /* lui t0, 0x8000 */
+#else
+ stl_raw(p++, 0x34080080); /* ori t0, r0, 0x0080 */
+#endif
+ stl_raw(p++, 0xad280058); /* sw t0, 0x0058(t1) */
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c083f00); /* lui t0, 0x3f00 */
+#else
+ stl_raw(p++, 0x3408003f); /* ori t0, r0, 0x003f */
+#endif
+ stl_raw(p++, 0xad280060); /* sw t0, 0x0060(t1) */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c08c100); /* lui t0, 0xc100 */
+#else
+ stl_raw(p++, 0x340800c1); /* ori t0, r0, 0x00c1 */
+#endif
+ stl_raw(p++, 0xad280080); /* sw t0, 0x0080(t1) */
+#ifdef TARGET_WORDS_BIGENDIAN
+ stl_raw(p++, 0x3c085e00); /* lui t0, 0x5e00 */
+#else
+ stl_raw(p++, 0x3408005e); /* ori t0, r0, 0x005e */
+#endif
+ stl_raw(p++, 0xad280088); /* sw t0, 0x0088(t1) */
+
+ /* Jump to kernel code */
+ stl_raw(p++, 0x3c1f0000 | ((kernel_entry >> 16) & 0xffff)); /* lui ra, high(kernel_entry) */
+ stl_raw(p++, 0x37ff0000 | (kernel_entry & 0xffff)); /* ori ra, ra, low(kernel_entry) */
+ stl_raw(p++, 0x03e00008); /* jr ra */
+ stl_raw(p++, 0x00000000); /* nop */
+
+ /* YAMON subroutines */
+ p = (uint32_t *) (base + 0x800);
+ stl_raw(p++, 0x03e00008); /* jr ra */
+ stl_raw(p++, 0x24020000); /* li v0,0 */
+ /* 808 YAMON print */
+ stl_raw(p++, 0x03e06821); /* move t5,ra */
+ stl_raw(p++, 0x00805821); /* move t3,a0 */
+ stl_raw(p++, 0x00a05021); /* move t2,a1 */
+ stl_raw(p++, 0x91440000); /* lbu a0,0(t2) */
+ stl_raw(p++, 0x254a0001); /* addiu t2,t2,1 */
+ stl_raw(p++, 0x10800005); /* beqz a0,834 */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x0ff0021c); /* jal 870 */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x08000205); /* j 814 */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x01a00008); /* jr t5 */
+ stl_raw(p++, 0x01602021); /* move a0,t3 */
+ /* 0x83c YAMON print_count */
+ stl_raw(p++, 0x03e06821); /* move t5,ra */
+ stl_raw(p++, 0x00805821); /* move t3,a0 */
+ stl_raw(p++, 0x00a05021); /* move t2,a1 */
+ stl_raw(p++, 0x00c06021); /* move t4,a2 */
+ stl_raw(p++, 0x91440000); /* lbu a0,0(t2) */
+ stl_raw(p++, 0x0ff0021c); /* jal 870 */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x254a0001); /* addiu t2,t2,1 */
+ stl_raw(p++, 0x258cffff); /* addiu t4,t4,-1 */
+ stl_raw(p++, 0x1580fffa); /* bnez t4,84c */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x01a00008); /* jr t5 */
+ stl_raw(p++, 0x01602021); /* move a0,t3 */
+ /* 0x870 */
+ stl_raw(p++, 0x3c08b800); /* lui t0,0xb400 */
+ stl_raw(p++, 0x350803f8); /* ori t0,t0,0x3f8 */
+ stl_raw(p++, 0x91090005); /* lbu t1,5(t0) */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x31290040); /* andi t1,t1,0x40 */
+ stl_raw(p++, 0x1120fffc); /* beqz t1,878 <outch+0x8> */
+ stl_raw(p++, 0x00000000); /* nop */
+ stl_raw(p++, 0x03e00008); /* jr ra */
+ stl_raw(p++, 0xa1040000); /* sb a0,0(t0) */
+
+}
+
+static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index,
+ const char *string, ...)
+{
+ va_list ap;
+ int32_t table_addr;
+
+ if (index >= ENVP_NB_ENTRIES)
+ return;
+
+ if (string == NULL) {
+ prom_buf[index] = 0;
+ return;
+ }
+
+ table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+ prom_buf[index] = tswap32(ENVP_ADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+/* Kernel */
+static int64_t load_kernel (void)
+{
+ int64_t kernel_entry, kernel_high;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+ int big_endian;
+ uint32_t *prom_buf;
+ long prom_size;
+ int prom_index = 0;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
+ (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
+ big_endian, ELF_MACHINE, 1) < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
+ if (initrd_offset + initrd_size > ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Setup prom parameters. */
+ prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
+ prom_buf = qemu_malloc(prom_size);
+
+ prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename);
+ if (initrd_size > 0) {
+ prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+ cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size,
+ loaderparams.kernel_cmdline);
+ } else {
+ prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline);
+ }
+
+ prom_set(prom_buf, prom_index++, "memsize");
+ prom_set(prom_buf, prom_index++, "%i", loaderparams.ram_size);
+ prom_set(prom_buf, prom_index++, "modetty0");
+ prom_set(prom_buf, prom_index++, "38400n8r");
+ prom_set(prom_buf, prom_index++, NULL);
+
+ rom_add_blob_fixed("prom", prom_buf, prom_size,
+ cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR));
+
+ return kernel_entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+
+ /* The bootloader does not need to be rewritten as it is located in a
+ read only location. The kernel location and the arguments table
+ location does not change. */
+ if (loaderparams.kernel_filename) {
+ env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL));
+ }
+}
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env && level) {
+ cpu_exit(env);
+ }
+}
+
+static
+void mips_malta_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ char *filename;
+ ram_addr_t ram_offset;
+ ram_addr_t bios_offset;
+ target_long bios_size;
+ int64_t kernel_entry;
+ PCIBus *pci_bus;
+ CPUState *env;
+ qemu_irq *i8259;
+ qemu_irq *cpu_exit_irq;
+ int piix4_devfn;
+ uint8_t *eeprom_buf;
+ i2c_bus *smbus;
+ int i;
+ DriveInfo *dinfo;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DriveInfo *fd[MAX_FD];
+ int fl_idx = 0;
+ int fl_sectors = 0;
+ int be;
+
+ /* Make sure the first 3 serial ports are associated with a device. */
+ for(i = 0; i < 3; i++) {
+ if (!serial_hds[i]) {
+ char label[32];
+ snprintf(label, sizeof(label), "serial%d", i);
+ serial_hds[i] = qemu_chr_open(label, "null", NULL);
+ }
+ }
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "20Kc";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ qemu_register_reset(main_cpu_reset, env);
+
+ /* allocate RAM */
+ if (ram_size > (256 << 20)) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d MB, maximum 256 MB\n",
+ ((unsigned int)ram_size / (1 << 20)));
+ exit(1);
+ }
+ ram_offset = qemu_ram_alloc(NULL, "mips_malta.ram", ram_size);
+ bios_offset = qemu_ram_alloc(NULL, "mips_malta.bios", BIOS_SIZE);
+
+
+ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
+
+ /* Map the bios at two physical locations, as on the real board. */
+ cpu_register_physical_memory(0x1e000000LL,
+ BIOS_SIZE, bios_offset | IO_MEM_ROM);
+ cpu_register_physical_memory(0x1fc00000LL,
+ BIOS_SIZE, bios_offset | IO_MEM_ROM);
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ /* FPGA */
+ malta_fpga_init(0x1f000000LL, env->irq[2], serial_hds[2]);
+
+ /* Load firmware in flash / BIOS unless we boot directly into a kernel. */
+ if (kernel_filename) {
+ /* Write a small bootloader to the flash location. */
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ kernel_entry = load_kernel();
+ write_bootloader(env, qemu_get_ram_ptr(bios_offset), kernel_entry);
+ } else {
+ dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+ if (dinfo) {
+ /* Load firmware from flash. */
+ bios_size = 0x400000;
+ fl_sectors = bios_size >> 16;
+#ifdef DEBUG_BOARD_INIT
+ printf("Register parallel flash %d size " TARGET_FMT_lx " at "
+ "offset %08lx addr %08llx '%s' %x\n",
+ fl_idx, bios_size, bios_offset, 0x1e000000LL,
+ bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+#endif
+ pflash_cfi01_register(0x1e000000LL, bios_offset,
+ dinfo->bdrv, 65536, fl_sectors,
+ 4, 0x0000, 0x0000, 0x0000, 0x0000, be);
+ fl_idx++;
+ } else {
+ /* Load a BIOS image. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0x1fc00000LL,
+ BIOS_SIZE);
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) && !kernel_filename) {
+ fprintf(stderr,
+ "qemu: Could not load MIPS bios '%s', and no -kernel argument was specified\n",
+ bios_name);
+ exit(1);
+ }
+ }
+ /* In little endian mode the 32bit words in the bios are swapped,
+ a neat trick which allows bi-endian firmware. */
+#ifndef TARGET_WORDS_BIGENDIAN
+ {
+ uint32_t *addr = qemu_get_ram_ptr(bios_offset);;
+ uint32_t *end = addr + bios_size;
+ while (addr < end) {
+ bswap32s(addr);
+ }
+ }
+#endif
+ }
+
+ /* Board ID = 0x420 (Malta Board with CoreLV)
+ XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should
+ map to the board ID. */
+ stl_p(qemu_get_ram_ptr(bios_offset) + 0x10, 0x00000420);
+
+ /* Init internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* Interrupt controller */
+ /* The 8259 is attached to the MIPS CPU INT0 pin, ie interrupt 2 */
+ i8259 = i8259_init(env->irq[2]);
+
+ /* Northbridge */
+ pci_bus = gt64120_register(i8259);
+
+ /* Southbridge */
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ }
+
+ piix4_devfn = piix4_init(pci_bus, 80);
+ isa_bus_irqs(i8259);
+ pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1);
+ usb_uhci_piix4_init(pci_bus, piix4_devfn + 2);
+ smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, isa_get_irq(9),
+ NULL, NULL, 0);
+ eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+ for (i = 0; i < 8; i++) {
+ /* TODO: Populate SPD eeprom data. */
+ DeviceState *eeprom;
+ eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+ qdev_prop_set_uint8(eeprom, "address", 0x50 + i);
+ qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256));
+ qdev_init_nofail(eeprom);
+ }
+ pit = pit_init(0x40, isa_get_irq(0));
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+
+ /* Super I/O */
+ isa_create_simple("i8042");
+
+ rtc_init(2000, NULL);
+ serial_isa_init(0, serial_hds[0]);
+ serial_isa_init(1, serial_hds[1]);
+ if (parallel_hds[0])
+ parallel_init(0, parallel_hds[0]);
+ for(i = 0; i < MAX_FD; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ }
+ fdctrl_init_isa(fd);
+
+ /* Sound card */
+ audio_init(NULL, pci_bus);
+
+ /* Network card */
+ network_init();
+
+ /* Optional PCI video card */
+ if (cirrus_vga_enabled) {
+ pci_cirrus_vga_init(pci_bus);
+ } else if (vmsvga_enabled) {
+ pci_vmsvga_init(pci_bus);
+ } else if (std_vga_enabled) {
+ pci_vga_init(pci_bus);
+ }
+}
+
+static QEMUMachine mips_malta_machine = {
+ .name = "malta",
+ .desc = "MIPS Malta Core LV",
+ .init = mips_malta_init,
+ .is_default = 1,
+};
+
+static void mips_malta_machine_init(void)
+{
+ qemu_register_machine(&mips_malta_machine);
+}
+
+machine_init(mips_malta_machine_init);
diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c
new file mode 100644
index 0000000..380a7eb
--- /dev/null
+++ b/hw/mips_mipssim.c
@@ -0,0 +1,212 @@
+/*
+ * QEMU/mipssim emulation
+ *
+ * Emulates a very simple machine model similiar to the one use by the
+ * proprietary MIPS emulator.
+ *
+ * Copyright (c) 2007 Thiemo Seufer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "mips.h"
+#include "mips_cpudevs.h"
+#include "pc.h"
+#include "isa.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "mips-bios.h"
+#include "loader.h"
+#include "elf.h"
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+typedef struct ResetData {
+ CPUState *env;
+ uint64_t vector;
+} ResetData;
+
+static int64_t load_kernel(void)
+{
+ int64_t entry, kernel_high;
+ long kernel_size;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+ int big_endian;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+
+ kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
+ NULL, (uint64_t *)&entry, NULL,
+ (uint64_t *)&kernel_high, big_endian,
+ ELF_MACHINE, 1);
+ if (kernel_size >= 0) {
+ if ((entry & ~0x7fffffffULL) == 0x80000000)
+ entry = (int32_t)entry;
+ } else {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
+ if (initrd_offset + initrd_size > loaderparams.ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset, loaderparams.ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+ return entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUState *env = s->env;
+
+ cpu_reset(env);
+ env->active_tc.PC = s->vector & ~(target_ulong)1;
+ if (s->vector & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ }
+}
+
+static void
+mips_mipssim_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ char *filename;
+ ram_addr_t ram_offset;
+ ram_addr_t bios_offset;
+ CPUState *env;
+ ResetData *reset_info;
+ int bios_size;
+
+ /* Init CPUs. */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "5Kf";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ reset_info = qemu_mallocz(sizeof(ResetData));
+ reset_info->env = env;
+ reset_info->vector = env->active_tc.PC;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ /* Allocate RAM. */
+ ram_offset = qemu_ram_alloc(NULL, "mips_mipssim.ram", ram_size);
+ bios_offset = qemu_ram_alloc(NULL, "mips_mipssim.bios", BIOS_SIZE);
+
+ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
+
+ /* Map the BIOS / boot exception handler. */
+ cpu_register_physical_memory(0x1fc00000LL,
+ BIOS_SIZE, bios_offset | IO_MEM_ROM);
+ /* Load a BIOS / boot exception handler image. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE);
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if ((bios_size < 0 || bios_size > BIOS_SIZE) && !kernel_filename) {
+ /* Bail out if we have neither a kernel image nor boot vector code. */
+ fprintf(stderr,
+ "qemu: Could not load MIPS bios '%s', and no -kernel argument was specified\n",
+ filename);
+ exit(1);
+ } else {
+ /* We have a boot vector start address. */
+ env->active_tc.PC = (target_long)(int32_t)0xbfc00000;
+ }
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ reset_info->vector = load_kernel();
+ }
+
+ /* Init CPU internal devices. */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* Register 64 KB of ISA IO space at 0x1fd00000. */
+ isa_mmio_init(0x1fd00000, 0x00010000);
+
+ /* A single 16450 sits at offset 0x3f8. It is attached to
+ MIPS CPU INT2, which is interrupt 4. */
+ if (serial_hds[0])
+ serial_init(0x3f8, env->irq[4], 115200, serial_hds[0]);
+
+ if (nd_table[0].vlan)
+ /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */
+ mipsnet_init(0x4200, env->irq[2], &nd_table[0]);
+}
+
+static QEMUMachine mips_mipssim_machine = {
+ .name = "mipssim",
+ .desc = "MIPS MIPSsim platform",
+ .init = mips_mipssim_init,
+};
+
+static void mips_mipssim_machine_init(void)
+{
+ qemu_register_machine(&mips_mipssim_machine);
+}
+
+machine_init(mips_mipssim_machine_init);
diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c
new file mode 100644
index 0000000..fb34dcf
--- /dev/null
+++ b/hw/mips_r4k.c
@@ -0,0 +1,318 @@
+/*
+ * QEMU/MIPS pseudo-board
+ *
+ * emulates a simple machine with ISA-like bus.
+ * ISA IO space mapped to the 0x14000000 (PHYS) and
+ * ISA memory at the 0x10000000 (PHYS, 16Mb in size).
+ * All peripherial devices are attached to this "bus" with
+ * the standard PC ISA addresses.
+*/
+#include "hw.h"
+#include "mips.h"
+#include "mips_cpudevs.h"
+#include "pc.h"
+#include "isa.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "flash.h"
+#include "qemu-log.h"
+#include "mips-bios.h"
+#include "ide.h"
+#include "loader.h"
+#include "elf.h"
+#include "mc146818rtc.h"
+#include "blockdev.h"
+
+#define MAX_IDE_BUS 2
+
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 14, 15 };
+
+static PITState *pit; /* PIT i8254 */
+
+/* i8254 PIT is attached to the IRQ0 at PIC i8259 */
+
+static struct _loaderparams {
+ int ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+} loaderparams;
+
+static void mips_qemu_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ if ((addr & 0xffff) == 0 && val == 42)
+ qemu_system_reset_request ();
+ else if ((addr & 0xffff) == 4 && val == 42)
+ qemu_system_shutdown_request ();
+}
+
+static uint32_t mips_qemu_readl (void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static CPUWriteMemoryFunc * const mips_qemu_write[] = {
+ &mips_qemu_writel,
+ &mips_qemu_writel,
+ &mips_qemu_writel,
+};
+
+static CPUReadMemoryFunc * const mips_qemu_read[] = {
+ &mips_qemu_readl,
+ &mips_qemu_readl,
+ &mips_qemu_readl,
+};
+
+static int mips_qemu_iomemtype = 0;
+
+typedef struct ResetData {
+ CPUState *env;
+ uint64_t vector;
+} ResetData;
+
+static int64_t load_kernel(void)
+{
+ int64_t entry, kernel_high;
+ long kernel_size, initrd_size, params_size;
+ ram_addr_t initrd_offset;
+ uint32_t *params_buf;
+ int big_endian;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ big_endian = 1;
+#else
+ big_endian = 0;
+#endif
+ kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
+ NULL, (uint64_t *)&entry, NULL,
+ (uint64_t *)&kernel_high, big_endian,
+ ELF_MACHINE, 1);
+ if (kernel_size >= 0) {
+ if ((entry & ~0x7fffffffULL) == 0x80000000)
+ entry = (int32_t)entry;
+ } else {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ loaderparams.kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size (loaderparams.initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
+ if (initrd_offset + initrd_size > ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ ram_size - initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ loaderparams.initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* Store command line. */
+ params_size = 264;
+ params_buf = qemu_malloc(params_size);
+
+ params_buf[0] = tswap32(ram_size);
+ params_buf[1] = tswap32(0x12345678);
+
+ if (initrd_size > 0) {
+ snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+ cpu_mips_phys_to_kseg0(NULL, initrd_offset),
+ initrd_size, loaderparams.kernel_cmdline);
+ } else {
+ snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline);
+ }
+
+ rom_add_blob_fixed("params", params_buf, params_size,
+ (16 << 20) - 264);
+
+ return entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUState *env = s->env;
+
+ cpu_reset(env);
+ env->active_tc.PC = s->vector;
+}
+
+static const int sector_len = 32 * 1024;
+static
+void mips_r4k_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ char *filename;
+ ram_addr_t ram_offset;
+ ram_addr_t bios_offset;
+ int bios_size;
+ CPUState *env;
+ ResetData *reset_info;
+ int i;
+ qemu_irq *i8259;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DriveInfo *dinfo;
+ int be;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "R4000";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ reset_info = qemu_mallocz(sizeof(ResetData));
+ reset_info->env = env;
+ reset_info->vector = env->active_tc.PC;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ /* allocate RAM */
+ if (ram_size > (256 << 20)) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d MB, maximum 256 MB\n",
+ ((unsigned int)ram_size / (1 << 20)));
+ exit(1);
+ }
+ ram_offset = qemu_ram_alloc(NULL, "mips_r4k.ram", ram_size);
+
+ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
+
+ if (!mips_qemu_iomemtype) {
+ mips_qemu_iomemtype = cpu_register_io_memory(mips_qemu_read,
+ mips_qemu_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ }
+ cpu_register_physical_memory(0x1fbf0000, 0x10000, mips_qemu_iomemtype);
+
+ /* Try to load a BIOS image. If this fails, we continue regardless,
+ but initialize the hardware ourselves. When a kernel gets
+ preloaded we also initialize the hardware, since the BIOS wasn't
+ run. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+ if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) {
+ bios_offset = qemu_ram_alloc(NULL, "mips_r4k.bios", BIOS_SIZE);
+ cpu_register_physical_memory(0x1fc00000, BIOS_SIZE,
+ bios_offset | IO_MEM_ROM);
+
+ load_image_targphys(filename, 0x1fc00000, BIOS_SIZE);
+ } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) {
+ uint32_t mips_rom = 0x00400000;
+ bios_offset = qemu_ram_alloc(NULL, "mips_r4k.bios", mips_rom);
+ if (!pflash_cfi01_register(0x1fc00000, bios_offset,
+ dinfo->bdrv, sector_len,
+ mips_rom / sector_len,
+ 4, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory.\n");
+ }
+ }
+ else {
+ /* not fatal */
+ fprintf(stderr, "qemu: Warning, could not load MIPS bios '%s'\n",
+ bios_name);
+ }
+ if (filename) {
+ qemu_free(filename);
+ }
+
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ reset_info->vector = load_kernel();
+ }
+
+ /* Init CPU internal devices */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+
+ /* The PIC is attached to the MIPS CPU INT0 pin */
+ i8259 = i8259_init(env->irq[2]);
+ isa_bus_new(NULL);
+ isa_bus_irqs(i8259);
+
+ rtc_init(2000, NULL);
+
+ /* Register 64 KB of ISA IO space at 0x14000000 */
+ isa_mmio_init(0x14000000, 0x00010000);
+ isa_mem_base = 0x10000000;
+
+ pit = pit_init(0x40, i8259[0]);
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_isa_init(i, serial_hds[i]);
+ }
+ }
+
+ isa_vga_init();
+
+ if (nd_table[0].vlan)
+ isa_ne2000_init(0x300, 9, &nd_table[0]);
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS; i++)
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
+ hd[MAX_IDE_DEVS * i],
+ hd[MAX_IDE_DEVS * i + 1]);
+
+ isa_create_simple("i8042");
+}
+
+static QEMUMachine mips_machine = {
+ .name = "mips",
+ .desc = "mips r4k platform",
+ .init = mips_r4k_init,
+};
+
+static void mips_machine_init(void)
+{
+ qemu_register_machine(&mips_machine);
+}
+
+machine_init(mips_machine_init);
diff --git a/hw/mips_timer.c b/hw/mips_timer.c
new file mode 100644
index 0000000..9c95f28
--- /dev/null
+++ b/hw/mips_timer.c
@@ -0,0 +1,147 @@
+/*
+ * QEMU MIPS timer support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips_cpudevs.h"
+#include "qemu-timer.h"
+
+#define TIMER_FREQ 100 * 1000 * 1000
+
+/* XXX: do not use a global */
+uint32_t cpu_mips_get_random (CPUState *env)
+{
+ static uint32_t lfsr = 1;
+ static uint32_t prev_idx = 0;
+ uint32_t idx;
+ /* Don't return same value twice, so get another value */
+ do {
+ lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xd0000001u);
+ idx = lfsr % (env->tlb->nb_tlb - env->CP0_Wired) + env->CP0_Wired;
+ } while (idx == prev_idx);
+ prev_idx = idx;
+ return idx;
+}
+
+/* MIPS R4K timer */
+static void cpu_mips_timer_update(CPUState *env)
+{
+ uint64_t now, next;
+ uint32_t wait;
+
+ now = qemu_get_clock(vm_clock);
+ wait = env->CP0_Compare - env->CP0_Count -
+ (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
+ next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+ qemu_mod_timer(env->timer, next);
+}
+
+/* Expire the timer. */
+static void cpu_mips_timer_expire(CPUState *env)
+{
+ cpu_mips_timer_update(env);
+ if (env->insn_flags & ISA_MIPS32R2) {
+ env->CP0_Cause |= 1 << CP0Ca_TI;
+ }
+ qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+}
+
+uint32_t cpu_mips_get_count (CPUState *env)
+{
+ if (env->CP0_Cause & (1 << CP0Ca_DC)) {
+ return env->CP0_Count;
+ } else {
+ uint64_t now;
+
+ now = qemu_get_clock(vm_clock);
+ if (qemu_timer_pending(env->timer)
+ && qemu_timer_expired(env->timer, now)) {
+ /* The timer has already expired. */
+ cpu_mips_timer_expire(env);
+ }
+
+ return env->CP0_Count +
+ (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec());
+ }
+}
+
+void cpu_mips_store_count (CPUState *env, uint32_t count)
+{
+ if (env->CP0_Cause & (1 << CP0Ca_DC))
+ env->CP0_Count = count;
+ else {
+ /* Store new count register */
+ env->CP0_Count =
+ count - (uint32_t)muldiv64(qemu_get_clock(vm_clock),
+ TIMER_FREQ, get_ticks_per_sec());
+ /* Update timer timer */
+ cpu_mips_timer_update(env);
+ }
+}
+
+void cpu_mips_store_compare (CPUState *env, uint32_t value)
+{
+ env->CP0_Compare = value;
+ if (!(env->CP0_Cause & (1 << CP0Ca_DC)))
+ cpu_mips_timer_update(env);
+ if (env->insn_flags & ISA_MIPS32R2)
+ env->CP0_Cause &= ~(1 << CP0Ca_TI);
+ qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+}
+
+void cpu_mips_start_count(CPUState *env)
+{
+ cpu_mips_store_count(env, env->CP0_Count);
+}
+
+void cpu_mips_stop_count(CPUState *env)
+{
+ /* Store the current value */
+ env->CP0_Count += (uint32_t)muldiv64(qemu_get_clock(vm_clock),
+ TIMER_FREQ, get_ticks_per_sec());
+}
+
+static void mips_timer_cb (void *opaque)
+{
+ CPUState *env;
+
+ env = opaque;
+#if 0
+ qemu_log("%s\n", __func__);
+#endif
+
+ if (env->CP0_Cause & (1 << CP0Ca_DC))
+ return;
+
+ /* ??? This callback should occur when the counter is exactly equal to
+ the comparator value. Offset the count by one to avoid immediately
+ retriggering the callback before any virtual time has passed. */
+ env->CP0_Count++;
+ cpu_mips_timer_expire(env);
+ env->CP0_Count--;
+}
+
+void cpu_mips_clock_init (CPUState *env)
+{
+ env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env);
+ env->CP0_Compare = 0;
+ cpu_mips_store_count(env, 1);
+}
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
new file mode 100644
index 0000000..c5e54ff
--- /dev/null
+++ b/hw/mipsnet.c
@@ -0,0 +1,288 @@
+#include "hw.h"
+#include "mips.h"
+#include "net.h"
+#include "isa.h"
+
+//#define DEBUG_MIPSNET_SEND
+//#define DEBUG_MIPSNET_RECEIVE
+//#define DEBUG_MIPSNET_DATA
+//#define DEBUG_MIPSNET_IRQ
+
+/* MIPSnet register offsets */
+
+#define MIPSNET_DEV_ID 0x00
+#define MIPSNET_BUSY 0x08
+#define MIPSNET_RX_DATA_COUNT 0x0c
+#define MIPSNET_TX_DATA_COUNT 0x10
+#define MIPSNET_INT_CTL 0x14
+# define MIPSNET_INTCTL_TXDONE 0x00000001
+# define MIPSNET_INTCTL_RXDONE 0x00000002
+# define MIPSNET_INTCTL_TESTBIT 0x80000000
+#define MIPSNET_INTERRUPT_INFO 0x18
+#define MIPSNET_RX_DATA_BUFFER 0x1c
+#define MIPSNET_TX_DATA_BUFFER 0x20
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+typedef struct MIPSnetState {
+ uint32_t busy;
+ uint32_t rx_count;
+ uint32_t rx_read;
+ uint32_t tx_count;
+ uint32_t tx_written;
+ uint32_t intctl;
+ uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
+ uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
+ int io_base;
+ qemu_irq irq;
+ NICState *nic;
+ NICConf conf;
+} MIPSnetState;
+
+static void mipsnet_reset(MIPSnetState *s)
+{
+ s->busy = 1;
+ s->rx_count = 0;
+ s->rx_read = 0;
+ s->tx_count = 0;
+ s->tx_written = 0;
+ s->intctl = 0;
+ memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
+ memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
+}
+
+static void mipsnet_update_irq(MIPSnetState *s)
+{
+ int isr = !!s->intctl;
+#ifdef DEBUG_MIPSNET_IRQ
+ printf("mipsnet: Set IRQ to %d (%02x)\n", isr, s->intctl);
+#endif
+ qemu_set_irq(s->irq, isr);
+}
+
+static int mipsnet_buffer_full(MIPSnetState *s)
+{
+ if (s->rx_count >= MAX_ETH_FRAME_SIZE)
+ return 1;
+ return 0;
+}
+
+static int mipsnet_can_receive(VLANClientState *nc)
+{
+ MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if (s->busy)
+ return 0;
+ return !mipsnet_buffer_full(s);
+}
+
+static ssize_t mipsnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+#ifdef DEBUG_MIPSNET_RECEIVE
+ printf("mipsnet: receiving len=%zu\n", size);
+#endif
+ if (!mipsnet_can_receive(nc))
+ return -1;
+
+ s->busy = 1;
+
+ /* Just accept everything. */
+
+ /* Write packet data. */
+ memcpy(s->rx_buffer, buf, size);
+
+ s->rx_count = size;
+ s->rx_read = 0;
+
+ /* Now we can signal we have received something. */
+ s->intctl |= MIPSNET_INTCTL_RXDONE;
+ mipsnet_update_irq(s);
+
+ return size;
+}
+
+static uint32_t mipsnet_ioport_read(void *opaque, uint32_t addr)
+{
+ MIPSnetState *s = opaque;
+ int ret = 0;
+
+ addr &= 0x3f;
+ switch (addr) {
+ case MIPSNET_DEV_ID:
+ ret = be32_to_cpu(0x4d495053); /* MIPS */
+ break;
+ case MIPSNET_DEV_ID + 4:
+ ret = be32_to_cpu(0x4e455430); /* NET0 */
+ break;
+ case MIPSNET_BUSY:
+ ret = s->busy;
+ break;
+ case MIPSNET_RX_DATA_COUNT:
+ ret = s->rx_count;
+ break;
+ case MIPSNET_TX_DATA_COUNT:
+ ret = s->tx_count;
+ break;
+ case MIPSNET_INT_CTL:
+ ret = s->intctl;
+ s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
+ break;
+ case MIPSNET_INTERRUPT_INFO:
+ /* XXX: This seems to be a per-VPE interrupt number. */
+ ret = 0;
+ break;
+ case MIPSNET_RX_DATA_BUFFER:
+ if (s->rx_count) {
+ s->rx_count--;
+ ret = s->rx_buffer[s->rx_read++];
+ }
+ break;
+ /* Reads as zero. */
+ case MIPSNET_TX_DATA_BUFFER:
+ default:
+ break;
+ }
+#ifdef DEBUG_MIPSNET_DATA
+ printf("mipsnet: read addr=0x%02x val=0x%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static void mipsnet_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ MIPSnetState *s = opaque;
+
+ addr &= 0x3f;
+#ifdef DEBUG_MIPSNET_DATA
+ printf("mipsnet: write addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+ switch (addr) {
+ case MIPSNET_TX_DATA_COUNT:
+ s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
+ s->tx_written = 0;
+ break;
+ case MIPSNET_INT_CTL:
+ if (val & MIPSNET_INTCTL_TXDONE) {
+ s->intctl &= ~MIPSNET_INTCTL_TXDONE;
+ } else if (val & MIPSNET_INTCTL_RXDONE) {
+ s->intctl &= ~MIPSNET_INTCTL_RXDONE;
+ } else if (val & MIPSNET_INTCTL_TESTBIT) {
+ mipsnet_reset(s);
+ s->intctl |= MIPSNET_INTCTL_TESTBIT;
+ } else if (!val) {
+ /* ACK testbit interrupt, flag was cleared on read. */
+ }
+ s->busy = !!s->intctl;
+ mipsnet_update_irq(s);
+ break;
+ case MIPSNET_TX_DATA_BUFFER:
+ s->tx_buffer[s->tx_written++] = val;
+ if (s->tx_written == s->tx_count) {
+ /* Send buffer. */
+#ifdef DEBUG_MIPSNET_SEND
+ printf("mipsnet: sending len=%d\n", s->tx_count);
+#endif
+ qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count);
+ s->tx_count = s->tx_written = 0;
+ s->intctl |= MIPSNET_INTCTL_TXDONE;
+ s->busy = 1;
+ mipsnet_update_irq(s);
+ }
+ break;
+ /* Read-only registers */
+ case MIPSNET_DEV_ID:
+ case MIPSNET_BUSY:
+ case MIPSNET_RX_DATA_COUNT:
+ case MIPSNET_INTERRUPT_INFO:
+ case MIPSNET_RX_DATA_BUFFER:
+ default:
+ break;
+ }
+}
+
+static void mipsnet_save(QEMUFile *f, void *opaque)
+{
+ MIPSnetState *s = opaque;
+
+ qemu_put_be32s(f, &s->busy);
+ qemu_put_be32s(f, &s->rx_count);
+ qemu_put_be32s(f, &s->rx_read);
+ qemu_put_be32s(f, &s->tx_count);
+ qemu_put_be32s(f, &s->tx_written);
+ qemu_put_be32s(f, &s->intctl);
+ qemu_put_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
+ qemu_put_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
+}
+
+static int mipsnet_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MIPSnetState *s = opaque;
+
+ if (version_id > 0)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->busy);
+ qemu_get_be32s(f, &s->rx_count);
+ qemu_get_be32s(f, &s->rx_read);
+ qemu_get_be32s(f, &s->tx_count);
+ qemu_get_be32s(f, &s->tx_written);
+ qemu_get_be32s(f, &s->intctl);
+ qemu_get_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
+ qemu_get_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
+
+ return 0;
+}
+
+static void mipsnet_cleanup(VLANClientState *nc)
+{
+ MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ unregister_savevm(NULL, "mipsnet", s);
+
+ isa_unassign_ioport(s->io_base, 36);
+
+ qemu_free(s);
+}
+
+static NetClientInfo net_mipsnet_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = mipsnet_can_receive,
+ .receive = mipsnet_receive,
+ .cleanup = mipsnet_cleanup,
+};
+
+void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
+{
+ MIPSnetState *s;
+
+ qemu_check_nic_model(nd, "mipsnet");
+
+ s = qemu_mallocz(sizeof(MIPSnetState));
+
+ register_ioport_write(base, 36, 1, mipsnet_ioport_write, s);
+ register_ioport_read(base, 36, 1, mipsnet_ioport_read, s);
+ register_ioport_write(base, 36, 2, mipsnet_ioport_write, s);
+ register_ioport_read(base, 36, 2, mipsnet_ioport_read, s);
+ register_ioport_write(base, 36, 4, mipsnet_ioport_write, s);
+ register_ioport_read(base, 36, 4, mipsnet_ioport_read, s);
+
+ s->io_base = base;
+ s->irq = irq;
+
+ if (nd) {
+ memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
+ s->conf.vlan = nd->vlan;
+ s->conf.peer = nd->netdev;
+
+ s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
+ nd->model, nd->name, s);
+
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ }
+
+ mipsnet_reset(s);
+ register_savevm(NULL, "mipsnet", 0, 0, mipsnet_save, mipsnet_load, s);
+}
diff --git a/hw/mpcore.c b/hw/mpcore.c
new file mode 100644
index 0000000..fc05215
--- /dev/null
+++ b/hw/mpcore.c
@@ -0,0 +1,286 @@
+/*
+ * ARM MPCore internal peripheral emulation (common code).
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+#define NCPU 4
+
+static inline int
+gic_get_current_cpu(void)
+{
+ return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* MPCore private memory region. */
+
+typedef struct {
+ uint32_t count;
+ uint32_t load;
+ uint32_t control;
+ uint32_t status;
+ uint32_t old_status;
+ int64_t tick;
+ QEMUTimer *timer;
+ struct mpcore_priv_state *mpcore;
+ int id; /* Encodes both timer/watchdog and CPU. */
+} mpcore_timer_state;
+
+typedef struct mpcore_priv_state {
+ gic_state gic;
+ uint32_t scu_control;
+ int iomemtype;
+ mpcore_timer_state timer[8];
+ uint32_t num_cpu;
+} mpcore_priv_state;
+
+/* Per-CPU Timers. */
+
+static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
+{
+ if (s->status & ~s->old_status) {
+ gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
+ }
+ s->old_status = s->status;
+}
+
+/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */
+static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
+{
+ return (((s->control >> 8) & 0xff) + 1) * 10;
+}
+
+static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
+{
+ if (s->count == 0)
+ return;
+ if (restart)
+ s->tick = qemu_get_clock(vm_clock);
+ s->tick += (int64_t)s->count * mpcore_timer_scale(s);
+ qemu_mod_timer(s->timer, s->tick);
+}
+
+static void mpcore_timer_tick(void *opaque)
+{
+ mpcore_timer_state *s = (mpcore_timer_state *)opaque;
+ s->status = 1;
+ if (s->control & 2) {
+ s->count = s->load;
+ mpcore_timer_reload(s, 0);
+ } else {
+ s->count = 0;
+ }
+ mpcore_timer_update_irq(s);
+}
+
+static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
+{
+ int64_t val;
+ switch (offset) {
+ case 0: /* Load */
+ return s->load;
+ /* Fall through. */
+ case 4: /* Counter. */
+ if (((s->control & 1) == 0) || (s->count == 0))
+ return 0;
+ /* Slow and ugly, but hopefully won't happen too often. */
+ val = s->tick - qemu_get_clock(vm_clock);
+ val /= mpcore_timer_scale(s);
+ if (val < 0)
+ val = 0;
+ return val;
+ case 8: /* Control. */
+ return s->control;
+ case 12: /* Interrupt status. */
+ return s->status;
+ default:
+ return 0;
+ }
+}
+
+static void mpcore_timer_write(mpcore_timer_state *s, int offset,
+ uint32_t value)
+{
+ int64_t old;
+ switch (offset) {
+ case 0: /* Load */
+ s->load = value;
+ /* Fall through. */
+ case 4: /* Counter. */
+ if ((s->control & 1) && s->count) {
+ /* Cancel the previous timer. */
+ qemu_del_timer(s->timer);
+ }
+ s->count = value;
+ if (s->control & 1) {
+ mpcore_timer_reload(s, 1);
+ }
+ break;
+ case 8: /* Control. */
+ old = s->control;
+ s->control = value;
+ if (((old & 1) == 0) && (value & 1)) {
+ if (s->count == 0 && (s->control & 2))
+ s->count = s->load;
+ mpcore_timer_reload(s, 1);
+ }
+ break;
+ case 12: /* Interrupt status. */
+ s->status &= ~value;
+ mpcore_timer_update_irq(s);
+ break;
+ }
+}
+
+static void mpcore_timer_init(mpcore_priv_state *mpcore,
+ mpcore_timer_state *s, int id)
+{
+ s->id = id;
+ s->mpcore = mpcore;
+ s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
+}
+
+
+/* Per-CPU private memory mapped IO. */
+
+static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ int id;
+ offset &= 0xfff;
+ if (offset < 0x100) {
+ /* SCU */
+ switch (offset) {
+ case 0x00: /* Control. */
+ return s->scu_control;
+ case 0x04: /* Configuration. */
+ id = ((1 << s->num_cpu) - 1) << 4;
+ return id | (s->num_cpu - 1);
+ case 0x08: /* CPU status. */
+ return 0;
+ case 0x0c: /* Invalidate all. */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ } else if (offset < 0x600) {
+ /* Interrupt controller. */
+ if (offset < 0x200) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x200) >> 8;
+ if (id >= s->num_cpu) {
+ return 0;
+ }
+ }
+ return gic_cpu_read(&s->gic, id, offset & 0xff);
+ } else if (offset < 0xb00) {
+ /* Timers. */
+ if (offset < 0x700) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x700) >> 8;
+ if (id >= s->num_cpu) {
+ return 0;
+ }
+ }
+ id <<= 1;
+ if (offset & 0x20)
+ id++;
+ return mpcore_timer_read(&s->timer[id], offset & 0xf);
+ }
+bad_reg:
+ hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
+ return 0;
+}
+
+static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+ int id;
+ offset &= 0xfff;
+ if (offset < 0x100) {
+ /* SCU */
+ switch (offset) {
+ case 0: /* Control register. */
+ s->scu_control = value & 1;
+ break;
+ case 0x0c: /* Invalidate all. */
+ /* This is a no-op as cache is not emulated. */
+ break;
+ default:
+ goto bad_reg;
+ }
+ } else if (offset < 0x600) {
+ /* Interrupt controller. */
+ if (offset < 0x200) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x200) >> 8;
+ }
+ if (id < s->num_cpu) {
+ gic_cpu_write(&s->gic, id, offset & 0xff, value);
+ }
+ } else if (offset < 0xb00) {
+ /* Timers. */
+ if (offset < 0x700) {
+ id = gic_get_current_cpu();
+ } else {
+ id = (offset - 0x700) >> 8;
+ }
+ if (id < s->num_cpu) {
+ id <<= 1;
+ if (offset & 0x20)
+ id++;
+ mpcore_timer_write(&s->timer[id], offset & 0xf, value);
+ }
+ return;
+ }
+ return;
+bad_reg:
+ hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
+}
+
+static CPUReadMemoryFunc * const mpcore_priv_readfn[] = {
+ mpcore_priv_read,
+ mpcore_priv_read,
+ mpcore_priv_read
+};
+
+static CPUWriteMemoryFunc * const mpcore_priv_writefn[] = {
+ mpcore_priv_write,
+ mpcore_priv_write,
+ mpcore_priv_write
+};
+
+static void mpcore_priv_map(SysBusDevice *dev, target_phys_addr_t base)
+{
+ mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
+ cpu_register_physical_memory(base, 0x1000, s->iomemtype);
+ cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype);
+}
+
+static int mpcore_priv_init(SysBusDevice *dev)
+{
+ mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
+ int i;
+
+ gic_init(&s->gic, s->num_cpu);
+ s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn,
+ mpcore_priv_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map);
+ for (i = 0; i < s->num_cpu * 2; i++) {
+ mpcore_timer_init(s, &s->timer[i], i);
+ }
+ return 0;
+}
diff --git a/hw/msi.c b/hw/msi.c
new file mode 100644
index 0000000..3dc3a24
--- /dev/null
+++ b/hw/msi.c
@@ -0,0 +1,344 @@
+/*
+ * msi.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 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/>.
+ */
+
+#include "msi.h"
+#include "range.h"
+
+/* Eventually those constants should go to Linux pci_regs.h */
+#define PCI_MSI_PENDING_32 0x10
+#define PCI_MSI_PENDING_64 0x14
+
+/* PCI_MSI_ADDRESS_LO */
+#define PCI_MSI_ADDRESS_LO_MASK (~0x3)
+
+/* If we get rid of cap allocator, we won't need those. */
+#define PCI_MSI_32_SIZEOF 0x0a
+#define PCI_MSI_64_SIZEOF 0x0e
+#define PCI_MSI_32M_SIZEOF 0x14
+#define PCI_MSI_64M_SIZEOF 0x18
+
+#define PCI_MSI_VECTORS_MAX 32
+
+/* If we get rid of cap allocator, we won't need this. */
+static inline uint8_t msi_cap_sizeof(uint16_t flags)
+{
+ switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) {
+ case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT:
+ return PCI_MSI_64M_SIZEOF;
+ case PCI_MSI_FLAGS_64BIT:
+ return PCI_MSI_64_SIZEOF;
+ case PCI_MSI_FLAGS_MASKBIT:
+ return PCI_MSI_32M_SIZEOF;
+ case 0:
+ return PCI_MSI_32_SIZEOF;
+ default:
+ abort();
+ break;
+ }
+ return 0;
+}
+
+//#define MSI_DEBUG
+
+#ifdef MSI_DEBUG
+# define MSI_DPRINTF(fmt, ...) \
+ fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
+#else
+# define MSI_DPRINTF(fmt, ...) do { } while (0)
+#endif
+#define MSI_DEV_PRINTF(dev, fmt, ...) \
+ MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
+
+static inline unsigned int msi_nr_vectors(uint16_t flags)
+{
+ return 1U <<
+ ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1));
+}
+
+static inline uint8_t msi_flags_off(const PCIDevice* dev)
+{
+ return dev->msi_cap + PCI_MSI_FLAGS;
+}
+
+static inline uint8_t msi_address_lo_off(const PCIDevice* dev)
+{
+ return dev->msi_cap + PCI_MSI_ADDRESS_LO;
+}
+
+static inline uint8_t msi_address_hi_off(const PCIDevice* dev)
+{
+ return dev->msi_cap + PCI_MSI_ADDRESS_HI;
+}
+
+static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit)
+{
+ return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32);
+}
+
+static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit)
+{
+ return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32);
+}
+
+static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit)
+{
+ return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32);
+}
+
+bool msi_enabled(const PCIDevice *dev)
+{
+ return msi_present(dev) &&
+ (pci_get_word(dev->config + msi_flags_off(dev)) &
+ PCI_MSI_FLAGS_ENABLE);
+}
+
+int msi_init(struct PCIDevice *dev, uint8_t offset,
+ unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask)
+{
+ unsigned int vectors_order;
+ uint16_t flags;
+ uint8_t cap_size;
+ int config_offset;
+ MSI_DEV_PRINTF(dev,
+ "init offset: 0x%"PRIx8" vector: %"PRId8
+ " 64bit %d mask %d\n",
+ offset, nr_vectors, msi64bit, msi_per_vector_mask);
+
+ assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */
+ assert(nr_vectors > 0);
+ assert(nr_vectors <= PCI_MSI_VECTORS_MAX);
+ /* the nr of MSI vectors is up to 32 */
+ vectors_order = ffs(nr_vectors) - 1;
+
+ flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1);
+ if (msi64bit) {
+ flags |= PCI_MSI_FLAGS_64BIT;
+ }
+ if (msi_per_vector_mask) {
+ flags |= PCI_MSI_FLAGS_MASKBIT;
+ }
+
+ cap_size = msi_cap_sizeof(flags);
+ config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size);
+ if (config_offset < 0) {
+ return config_offset;
+ }
+
+ dev->msi_cap = config_offset;
+ dev->cap_present |= QEMU_PCI_CAP_MSI;
+
+ pci_set_word(dev->config + msi_flags_off(dev), flags);
+ pci_set_word(dev->wmask + msi_flags_off(dev),
+ PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+ pci_set_long(dev->wmask + msi_address_lo_off(dev),
+ PCI_MSI_ADDRESS_LO_MASK);
+ if (msi64bit) {
+ pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff);
+ }
+ pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff);
+
+ if (msi_per_vector_mask) {
+ /* Make mask bits 0 to nr_vectors - 1 writeable. */
+ pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit),
+ 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors));
+ }
+ return config_offset;
+}
+
+void msi_uninit(struct PCIDevice *dev)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ uint8_t cap_size = msi_cap_sizeof(flags);
+ pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size);
+ MSI_DEV_PRINTF(dev, "uninit\n");
+}
+
+void msi_reset(PCIDevice *dev)
+{
+ uint16_t flags;
+ bool msi64bit;
+
+ flags = pci_get_word(dev->config + msi_flags_off(dev));
+ flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
+ msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+
+ pci_set_word(dev->config + msi_flags_off(dev), flags);
+ pci_set_long(dev->config + msi_address_lo_off(dev), 0);
+ if (msi64bit) {
+ pci_set_long(dev->config + msi_address_hi_off(dev), 0);
+ }
+ pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0);
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0);
+ pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0);
+ }
+ MSI_DEV_PRINTF(dev, "reset\n");
+}
+
+static bool msi_is_masked(const PCIDevice *dev, unsigned int vector)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ uint32_t mask;
+ assert(vector < PCI_MSI_VECTORS_MAX);
+
+ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
+ return false;
+ }
+
+ mask = pci_get_long(dev->config +
+ msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT));
+ return mask & (1U << vector);
+}
+
+void msi_notify(PCIDevice *dev, unsigned int vector)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+ unsigned int nr_vectors = msi_nr_vectors(flags);
+ uint64_t address;
+ uint32_t data;
+
+ assert(vector < nr_vectors);
+ if (msi_is_masked(dev, vector)) {
+ assert(flags & PCI_MSI_FLAGS_MASKBIT);
+ pci_long_test_and_set_mask(
+ dev->config + msi_pending_off(dev, msi64bit), 1U << vector);
+ MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector);
+ return;
+ }
+
+ if (msi64bit) {
+ address = pci_get_quad(dev->config + msi_address_lo_off(dev));
+ } else {
+ address = pci_get_long(dev->config + msi_address_lo_off(dev));
+ }
+
+ /* upper bit 31:16 is zero */
+ data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
+ if (nr_vectors > 1) {
+ data &= ~(nr_vectors - 1);
+ data |= vector;
+ }
+
+ MSI_DEV_PRINTF(dev,
+ "notify vector 0x%x"
+ " address: 0x%"PRIx64" data: 0x%"PRIx32"\n",
+ vector, address, data);
+ stl_phys(address, data);
+}
+
+/* call this function after updating configs by pci_default_write_config(). */
+void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
+ bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT;
+ unsigned int nr_vectors;
+ uint8_t log_num_vecs;
+ uint8_t log_max_vecs;
+ unsigned int vector;
+ uint32_t pending;
+
+ if (!ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) {
+ return;
+ }
+
+#ifdef MSI_DEBUG
+ MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n",
+ addr, val, len);
+ MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32,
+ flags,
+ pci_get_long(dev->config + msi_address_lo_off(dev)));
+ if (msi64bit) {
+ fprintf(stderr, " address-hi: 0x%"PRIx32,
+ pci_get_long(dev->config + msi_address_hi_off(dev)));
+ }
+ fprintf(stderr, " data: 0x%"PRIx16,
+ pci_get_word(dev->config + msi_data_off(dev, msi64bit)));
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32,
+ pci_get_long(dev->config + msi_mask_off(dev, msi64bit)),
+ pci_get_long(dev->config + msi_pending_off(dev, msi64bit)));
+ }
+ fprintf(stderr, "\n");
+#endif
+
+ if (!(flags & PCI_MSI_FLAGS_ENABLE)) {
+ return;
+ }
+
+ /*
+ * Now MSI is enabled, clear INTx# interrupts.
+ * the driver is prohibited from writing enable bit to mask
+ * a service request. But the guest OS could do this.
+ * So we just discard the interrupts as moderate fallback.
+ *
+ * 6.8.3.3. Enabling Operation
+ * While enabled for MSI or MSI-X operation, a function is prohibited
+ * from using its INTx# pin (if implemented) to request
+ * service (MSI, MSI-X, and INTx# are mutually exclusive).
+ */
+ pci_device_deassert_intx(dev);
+
+ /*
+ * nr_vectors might be set bigger than capable. So clamp it.
+ * This is not legal by spec, so we can do anything we like,
+ * just don't crash the host
+ */
+ log_num_vecs =
+ (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
+ log_max_vecs =
+ (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1);
+ if (log_num_vecs > log_max_vecs) {
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
+ pci_set_word(dev->config + msi_flags_off(dev), flags);
+ }
+
+ if (!msi_per_vector_mask) {
+ /* if per vector masking isn't supported,
+ there is no pending interrupt. */
+ return;
+ }
+
+ nr_vectors = msi_nr_vectors(flags);
+
+ /* This will discard pending interrupts, if any. */
+ pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit));
+ pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors);
+ pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending);
+
+ /* deliver pending interrupts which are unmasked */
+ for (vector = 0; vector < nr_vectors; ++vector) {
+ if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) {
+ continue;
+ }
+
+ pci_long_test_and_clear_mask(
+ dev->config + msi_pending_off(dev, msi64bit), 1U << vector);
+ msi_notify(dev, vector);
+ }
+}
+
+unsigned int msi_nr_vectors_allocated(const PCIDevice *dev)
+{
+ uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
+ return msi_nr_vectors(flags);
+}
diff --git a/hw/msi.h b/hw/msi.h
new file mode 100644
index 0000000..5766018
--- /dev/null
+++ b/hw/msi.h
@@ -0,0 +1,41 @@
+/*
+ * msi.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 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/>.
+ */
+
+#ifndef QEMU_MSI_H
+#define QEMU_MSI_H
+
+#include "qemu-common.h"
+#include "pci.h"
+
+bool msi_enabled(const PCIDevice *dev);
+int msi_init(struct PCIDevice *dev, uint8_t offset,
+ unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask);
+void msi_uninit(struct PCIDevice *dev);
+void msi_reset(PCIDevice *dev);
+void msi_notify(PCIDevice *dev, unsigned int vector);
+void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len);
+unsigned int msi_nr_vectors_allocated(const PCIDevice *dev);
+
+static inline bool msi_present(const PCIDevice *dev)
+{
+ return dev->cap_present & QEMU_PCI_CAP_MSI;
+}
+
+#endif /* QEMU_MSI_H */
diff --git a/hw/msix.c b/hw/msix.c
new file mode 100644
index 0000000..8b68a6a
--- /dev/null
+++ b/hw/msix.c
@@ -0,0 +1,654 @@
+/*
+ * MSI-X device support
+ *
+ * This module includes support for MSI-X in pci devices.
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * Copyright (c) 2009, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "hw.h"
+#include "msix.h"
+#include "pci.h"
+#include "range.h"
+#include "kvm.h"
+
+/* MSI-X capability structure */
+#define MSIX_TABLE_OFFSET 4
+#define MSIX_PBA_OFFSET 8
+#define MSIX_CAP_LENGTH 12
+
+/* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
+#define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1)
+#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
+#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
+
+/* MSI-X table format */
+#define MSIX_MSG_ADDR 0
+#define MSIX_MSG_UPPER_ADDR 4
+#define MSIX_MSG_DATA 8
+#define MSIX_VECTOR_CTRL 12
+#define MSIX_ENTRY_SIZE 16
+#define MSIX_VECTOR_MASK 0x1
+
+/* How much space does an MSIX table need. */
+/* The spec requires giving the table structure
+ * a 4K aligned region all by itself. */
+#define MSIX_PAGE_SIZE 0x1000
+/* Reserve second half of the page for pending bits */
+#define MSIX_PAGE_PENDING (MSIX_PAGE_SIZE / 2)
+#define MSIX_MAX_ENTRIES 32
+
+
+/* Flag for interrupt controller to declare MSI-X support */
+int msix_supported;
+
+/* KVM specific MSIX helpers */
+static void kvm_msix_free(PCIDevice *dev)
+{
+ int vector, changed = 0;
+ struct kvm_msix_message *kmm;
+
+ for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
+ if (dev->msix_entry_used[vector]) {
+ kmm = &dev->msix_irq_entries[vector];
+ kvm_del_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data);
+ changed = 1;
+ }
+ }
+ if (changed) {
+ kvm_commit_irq_routes();
+ }
+}
+
+static void kvm_msix_message_from_vector(PCIDevice *dev, unsigned vector,
+ struct kvm_msix_message *kmm)
+{
+ uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE;
+
+ kmm->addr_lo = pci_get_long(table_entry + MSIX_MSG_ADDR);
+ kmm->addr_hi = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR);
+ kmm->data = pci_get_long(table_entry + MSIX_MSG_DATA);
+}
+
+static void kvm_msix_update(PCIDevice *dev, int vector,
+ int was_masked, int is_masked)
+{
+ struct kvm_msix_message e = {}, *entry;
+ int mask_cleared = was_masked && !is_masked;
+ /* It is only legal to change an entry when it is masked. Therefore, it is
+ * enough to update the routing in kernel when mask is being cleared. */
+ if (!mask_cleared) {
+ return;
+ }
+ if (!dev->msix_entry_used[vector]) {
+ return;
+ }
+ entry = dev->msix_irq_entries + vector;
+ e.gsi = entry->gsi;
+ kvm_msix_message_from_vector(dev, vector, &e);
+ if (memcmp(entry, &e, sizeof e) != 0) {
+ int r;
+
+ r = kvm_update_msix(entry->gsi, entry->addr_lo,
+ entry->addr_hi, entry->data,
+ e.gsi, e.addr_lo, e.addr_hi, e.data);
+ if (r) {
+ fprintf(stderr, "%s: kvm_update_msix failed: %s\n", __func__,
+ strerror(-r));
+ exit(1);
+ }
+ *entry = e;
+ r = kvm_commit_irq_routes();
+ if (r) {
+ fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__,
+ strerror(-r));
+ exit(1);
+ }
+ }
+}
+
+static int kvm_msix_add(PCIDevice *dev, unsigned vector)
+{
+ struct kvm_msix_message *kmm = dev->msix_irq_entries + vector;
+ int r;
+
+ if (!kvm_has_gsi_routing()) {
+ fprintf(stderr, "Warning: no MSI-X support found. "
+ "At least kernel 2.6.30 is required for MSI-X support.\n"
+ );
+ return -EOPNOTSUPP;
+ }
+
+ r = kvm_get_irq_route_gsi();
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_get_irq_route_gsi failed: %s\n", __func__, strerror(-r));
+ return r;
+ }
+ kmm->gsi = r;
+ kvm_msix_message_from_vector(dev, vector, kmm);
+ r = kvm_add_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data);
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_add_msix failed: %s\n", __func__, strerror(-r));
+ return r;
+ }
+
+ r = kvm_commit_irq_routes();
+ if (r < 0) {
+ fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, strerror(-r));
+ return r;
+ }
+ return 0;
+}
+
+static void kvm_msix_del(PCIDevice *dev, unsigned vector)
+{
+ struct kvm_msix_message *kmm;
+
+ if (dev->msix_entry_used[vector]) {
+ return;
+ }
+ kmm = &dev->msix_irq_entries[vector];
+ kvm_del_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data);
+ kvm_commit_irq_routes();
+}
+
+/* Add MSI-X capability to the config space for the device. */
+/* Given a bar and its size, add MSI-X table on top of it
+ * and fill MSI-X capability in the config space.
+ * Original bar size must be a power of 2 or 0.
+ * New bar size is returned. */
+static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries,
+ unsigned bar_nr, unsigned bar_size)
+{
+ int config_offset;
+ uint8_t *config;
+
+ pdev->msix_bar_size = bar_size;
+
+ config_offset = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+
+ if (!config_offset) {
+ uint32_t new_size;
+
+ if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1)
+ return -EINVAL;
+ if (bar_size > 0x80000000)
+ return -ENOSPC;
+
+ /* Add space for MSI-X structures */
+ if (!bar_size) {
+ new_size = MSIX_PAGE_SIZE;
+ } else if (bar_size < MSIX_PAGE_SIZE) {
+ bar_size = MSIX_PAGE_SIZE;
+ new_size = MSIX_PAGE_SIZE * 2;
+ } else {
+ new_size = bar_size * 2;
+ }
+
+ pdev->msix_bar_size = new_size;
+ config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX,
+ 0, MSIX_CAP_LENGTH);
+ if (config_offset < 0)
+ return config_offset;
+ config = pdev->config + config_offset;
+
+ pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1);
+ /* Table on top of BAR */
+ pci_set_long(config + MSIX_TABLE_OFFSET, bar_size | bar_nr);
+ /* Pending bits on top of that */
+ pci_set_long(config + MSIX_PBA_OFFSET, (bar_size + MSIX_PAGE_PENDING) |
+ bar_nr);
+ }
+ pdev->msix_cap = config_offset;
+ /* Make flags bit writeable. */
+ pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK |
+ MSIX_MASKALL_MASK;
+ return 0;
+}
+
+static uint32_t msix_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ PCIDevice *dev = opaque;
+ unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3;
+ void *page = dev->msix_table_page;
+
+ return pci_get_long(page + offset);
+}
+
+static uint32_t msix_mmio_read_unallowed(void *opaque, target_phys_addr_t addr)
+{
+ fprintf(stderr, "MSI-X: only dword read is allowed!\n");
+ return 0;
+}
+
+static uint8_t msix_pending_mask(int vector)
+{
+ return 1 << (vector % 8);
+}
+
+static uint8_t *msix_pending_byte(PCIDevice *dev, int vector)
+{
+ return dev->msix_table_page + MSIX_PAGE_PENDING + vector / 8;
+}
+
+static int msix_is_pending(PCIDevice *dev, int vector)
+{
+ return *msix_pending_byte(dev, vector) & msix_pending_mask(vector);
+}
+
+static void msix_set_pending(PCIDevice *dev, int vector)
+{
+ *msix_pending_byte(dev, vector) |= msix_pending_mask(vector);
+}
+
+static void msix_clr_pending(PCIDevice *dev, int vector)
+{
+ *msix_pending_byte(dev, vector) &= ~msix_pending_mask(vector);
+}
+
+static int msix_function_masked(PCIDevice *dev)
+{
+ return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
+}
+
+static int msix_is_masked(PCIDevice *dev, int vector)
+{
+ unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL;
+ return msix_function_masked(dev) ||
+ dev->msix_table_page[offset] & MSIX_VECTOR_MASK;
+}
+
+static void msix_handle_mask_update(PCIDevice *dev, int vector)
+{
+ if (!msix_is_masked(dev, vector) && msix_is_pending(dev, vector)) {
+ msix_clr_pending(dev, vector);
+ msix_notify(dev, vector);
+ }
+}
+
+/* Handle MSI-X capability config write. */
+void msix_write_config(PCIDevice *dev, uint32_t addr,
+ uint32_t val, int len)
+{
+ unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
+ int vector;
+
+ if (!range_covers_byte(addr, len, enable_pos)) {
+ return;
+ }
+
+ if (!msix_enabled(dev)) {
+ return;
+ }
+
+ pci_device_deassert_intx(dev);
+
+ if (msix_function_masked(dev)) {
+ return;
+ }
+
+ for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
+ msix_handle_mask_update(dev, vector);
+ }
+}
+
+static void msix_mmio_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ PCIDevice *dev = opaque;
+ unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3;
+ int vector = offset / MSIX_ENTRY_SIZE;
+ int was_masked = msix_is_masked(dev, vector);
+ pci_set_long(dev->msix_table_page + offset, val);
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msix_update(dev, vector, was_masked, msix_is_masked(dev, vector));
+ }
+ if (was_masked != msix_is_masked(dev, vector) && dev->msix_mask_notifier) {
+ int r = dev->msix_mask_notifier(dev, vector,
+ msix_is_masked(dev, vector));
+ assert(r >= 0);
+ }
+ msix_handle_mask_update(dev, vector);
+}
+
+static void msix_mmio_write_unallowed(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ fprintf(stderr, "MSI-X: only dword write is allowed!\n");
+}
+
+static CPUWriteMemoryFunc * const msix_mmio_write[] = {
+ msix_mmio_write_unallowed, msix_mmio_write_unallowed, msix_mmio_writel
+};
+
+static CPUReadMemoryFunc * const msix_mmio_read[] = {
+ msix_mmio_read_unallowed, msix_mmio_read_unallowed, msix_mmio_readl
+};
+
+/* Should be called from device's map method. */
+void msix_mmio_map(PCIDevice *d, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ uint8_t *config = d->config + d->msix_cap;
+ uint32_t table = pci_get_long(config + MSIX_TABLE_OFFSET);
+ uint32_t offset = table & ~(MSIX_PAGE_SIZE - 1);
+ /* TODO: for assigned devices, we'll want to make it possible to map
+ * pending bits separately in case they are in a separate bar. */
+ int table_bir = table & PCI_MSIX_FLAGS_BIRMASK;
+
+ if (table_bir != region_num)
+ return;
+ if (size <= offset)
+ return;
+ cpu_register_physical_memory(addr + offset,
+ MIN(size - offset, MSIX_PAGE_SIZE),
+ d->msix_mmio_index);
+}
+
+static void msix_mask_all(struct PCIDevice *dev, unsigned nentries)
+{
+ int vector, r;
+ for (vector = 0; vector < nentries; ++vector) {
+ unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL;
+ int was_masked = msix_is_masked(dev, vector);
+ dev->msix_table_page[offset] |= MSIX_VECTOR_MASK;
+ if (was_masked != msix_is_masked(dev, vector) &&
+ dev->msix_mask_notifier) {
+ r = dev->msix_mask_notifier(dev, vector,
+ msix_is_masked(dev, vector));
+ assert(r >= 0);
+ }
+ }
+}
+
+/* Initialize the MSI-X structures. Note: if MSI-X is supported, BAR size is
+ * modified, it should be retrieved with msix_bar_size. */
+int msix_init(struct PCIDevice *dev, unsigned short nentries,
+ unsigned bar_nr, unsigned bar_size)
+{
+ int ret;
+ /* Nothing to do if MSI is not supported by interrupt controller */
+ if (!msix_supported)
+ return -ENOTSUP;
+
+ if (nentries > MSIX_MAX_ENTRIES)
+ return -EINVAL;
+
+ dev->msix_mask_notifier = NULL;
+ dev->msix_entry_used = qemu_mallocz(MSIX_MAX_ENTRIES *
+ sizeof *dev->msix_entry_used);
+
+ dev->msix_table_page = qemu_mallocz(MSIX_PAGE_SIZE);
+ msix_mask_all(dev, nentries);
+
+ dev->msix_mmio_index = cpu_register_io_memory(msix_mmio_read,
+ msix_mmio_write, dev,
+ DEVICE_NATIVE_ENDIAN);
+ if (dev->msix_mmio_index == -1) {
+ ret = -EBUSY;
+ goto err_index;
+ }
+
+ dev->msix_entries_nr = nentries;
+ ret = msix_add_config(dev, nentries, bar_nr, bar_size);
+ if (ret)
+ goto err_config;
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ dev->msix_irq_entries = qemu_malloc(nentries *
+ sizeof *dev->msix_irq_entries);
+ }
+
+ dev->cap_present |= QEMU_PCI_CAP_MSIX;
+ return 0;
+
+err_config:
+ dev->msix_entries_nr = 0;
+ cpu_unregister_io_memory(dev->msix_mmio_index);
+err_index:
+ qemu_free(dev->msix_table_page);
+ dev->msix_table_page = NULL;
+ qemu_free(dev->msix_entry_used);
+ dev->msix_entry_used = NULL;
+ return ret;
+}
+
+static void msix_free_irq_entries(PCIDevice *dev)
+{
+ int vector;
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msix_free(dev);
+ }
+
+ for (vector = 0; vector < dev->msix_entries_nr; ++vector) {
+ dev->msix_entry_used[vector] = 0;
+ msix_clr_pending(dev, vector);
+ }
+}
+
+/* Clean up resources for the device. */
+int msix_uninit(PCIDevice *dev)
+{
+ if (!(dev->cap_present & QEMU_PCI_CAP_MSIX))
+ return 0;
+ pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
+ dev->msix_cap = 0;
+ msix_free_irq_entries(dev);
+ dev->msix_entries_nr = 0;
+ cpu_unregister_io_memory(dev->msix_mmio_index);
+ qemu_free(dev->msix_table_page);
+ dev->msix_table_page = NULL;
+ qemu_free(dev->msix_entry_used);
+ dev->msix_entry_used = NULL;
+ qemu_free(dev->msix_irq_entries);
+ dev->msix_irq_entries = NULL;
+ dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
+ return 0;
+}
+
+void msix_save(PCIDevice *dev, QEMUFile *f)
+{
+ unsigned n = dev->msix_entries_nr;
+
+ if (!msix_supported) {
+ return;
+ }
+
+ if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) {
+ return;
+ }
+ qemu_put_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE);
+ qemu_put_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
+}
+
+/* Should be called after restoring the config space. */
+void msix_load(PCIDevice *dev, QEMUFile *f)
+{
+ unsigned n = dev->msix_entries_nr;
+
+ if (!msix_supported)
+ return;
+
+ if (!(dev->cap_present & QEMU_PCI_CAP_MSIX)) {
+ return;
+ }
+
+ msix_free_irq_entries(dev);
+ qemu_get_buffer(f, dev->msix_table_page, n * MSIX_ENTRY_SIZE);
+ qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8);
+}
+
+/* Does device support MSI-X? */
+int msix_present(PCIDevice *dev)
+{
+ return dev->cap_present & QEMU_PCI_CAP_MSIX;
+}
+
+/* Is MSI-X enabled? */
+int msix_enabled(PCIDevice *dev)
+{
+ return (dev->cap_present & QEMU_PCI_CAP_MSIX) &&
+ (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &
+ MSIX_ENABLE_MASK);
+}
+
+/* Size of bar where MSI-X table resides, or 0 if MSI-X not supported. */
+uint32_t msix_bar_size(PCIDevice *dev)
+{
+ return (dev->cap_present & QEMU_PCI_CAP_MSIX) ?
+ dev->msix_bar_size : 0;
+}
+
+/* Send an MSI-X message */
+void msix_notify(PCIDevice *dev, unsigned vector)
+{
+ uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE;
+ uint64_t address;
+ uint32_t data;
+
+ if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector])
+ return;
+ if (msix_is_masked(dev, vector)) {
+ msix_set_pending(dev, vector);
+ return;
+ }
+
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_set_irq(dev->msix_irq_entries[vector].gsi, 1, NULL);
+ return;
+ }
+
+ address = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR);
+ address = (address << 32) | pci_get_long(table_entry + MSIX_MSG_ADDR);
+ data = pci_get_long(table_entry + MSIX_MSG_DATA);
+ stl_phys(address, data);
+}
+
+void msix_reset(PCIDevice *dev)
+{
+ if (!(dev->cap_present & QEMU_PCI_CAP_MSIX))
+ return;
+ msix_free_irq_entries(dev);
+ dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
+ ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
+ memset(dev->msix_table_page, 0, MSIX_PAGE_SIZE);
+ msix_mask_all(dev, dev->msix_entries_nr);
+}
+
+/* PCI spec suggests that devices make it possible for software to configure
+ * less vectors than supported by the device, but does not specify a standard
+ * mechanism for devices to do so.
+ *
+ * We support this by asking devices to declare vectors software is going to
+ * actually use, and checking this on the notification path. Devices that
+ * don't want to follow the spec suggestion can declare all vectors as used. */
+
+/* Mark vector as used. */
+int msix_vector_use(PCIDevice *dev, unsigned vector)
+{
+ int ret;
+ if (vector >= dev->msix_entries_nr)
+ return -EINVAL;
+ if (dev->msix_entry_used[vector]) {
+ return 0;
+ }
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ ret = kvm_msix_add(dev, vector);
+ if (ret) {
+ return ret;
+ }
+ }
+ ++dev->msix_entry_used[vector];
+ return 0;
+}
+
+/* Mark vector as unused. */
+void msix_vector_unuse(PCIDevice *dev, unsigned vector)
+{
+ if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
+ return;
+ }
+ if (--dev->msix_entry_used[vector]) {
+ return;
+ }
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ kvm_msix_del(dev, vector);
+ }
+ msix_clr_pending(dev, vector);
+}
+
+void msix_unuse_all_vectors(PCIDevice *dev)
+{
+ if (!(dev->cap_present & QEMU_PCI_CAP_MSIX))
+ return;
+ msix_free_irq_entries(dev);
+}
+
+/* Invoke the notifier if vector entry is used and unmasked. */
+static int msix_notify_if_unmasked(PCIDevice *dev, unsigned vector, int masked)
+{
+ assert(dev->msix_mask_notifier);
+ if (!dev->msix_entry_used[vector] || msix_is_masked(dev, vector)) {
+ return 0;
+ }
+ return dev->msix_mask_notifier(dev, vector, masked);
+}
+
+static int msix_set_mask_notifier_for_vector(PCIDevice *dev, unsigned vector)
+{
+ /* Notifier has been set. Invoke it on unmasked vectors. */
+ return msix_notify_if_unmasked(dev, vector, 0);
+}
+
+static int msix_unset_mask_notifier_for_vector(PCIDevice *dev, unsigned vector)
+{
+ /* Notifier will be unset. Invoke it to mask unmasked entries. */
+ return msix_notify_if_unmasked(dev, vector, 1);
+}
+
+int msix_set_mask_notifier(PCIDevice *dev, msix_mask_notifier_func f)
+{
+ int r, n;
+ assert(!dev->msix_mask_notifier);
+ dev->msix_mask_notifier = f;
+ for (n = 0; n < dev->msix_entries_nr; ++n) {
+ r = msix_set_mask_notifier_for_vector(dev, n);
+ if (r < 0) {
+ goto undo;
+ }
+ }
+ return 0;
+
+undo:
+ while (--n >= 0) {
+ msix_unset_mask_notifier_for_vector(dev, n);
+ }
+ dev->msix_mask_notifier = NULL;
+ return r;
+}
+
+int msix_unset_mask_notifier(PCIDevice *dev)
+{
+ int r, n;
+ assert(dev->msix_mask_notifier);
+ for (n = 0; n < dev->msix_entries_nr; ++n) {
+ r = msix_unset_mask_notifier_for_vector(dev, n);
+ if (r < 0) {
+ goto undo;
+ }
+ }
+ dev->msix_mask_notifier = NULL;
+ return 0;
+
+undo:
+ while (--n >= 0) {
+ msix_set_mask_notifier_for_vector(dev, n);
+ }
+ return r;
+}
diff --git a/hw/msix.h b/hw/msix.h
new file mode 100644
index 0000000..5a81df5
--- /dev/null
+++ b/hw/msix.h
@@ -0,0 +1,38 @@
+#ifndef QEMU_MSIX_H
+#define QEMU_MSIX_H
+
+#include "qemu-common.h"
+#include "pci.h"
+
+int msix_init(PCIDevice *pdev, unsigned short nentries,
+ unsigned bar_nr, unsigned bar_size);
+
+void msix_write_config(PCIDevice *pci_dev, uint32_t address,
+ uint32_t val, int len);
+
+void msix_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type);
+
+int msix_uninit(PCIDevice *d);
+
+void msix_save(PCIDevice *dev, QEMUFile *f);
+void msix_load(PCIDevice *dev, QEMUFile *f);
+
+int msix_enabled(PCIDevice *dev);
+int msix_present(PCIDevice *dev);
+
+uint32_t msix_bar_size(PCIDevice *dev);
+
+int msix_vector_use(PCIDevice *dev, unsigned vector);
+void msix_vector_unuse(PCIDevice *dev, unsigned vector);
+void msix_unuse_all_vectors(PCIDevice *dev);
+
+void msix_notify(PCIDevice *dev, unsigned vector);
+
+void msix_reset(PCIDevice *dev);
+
+extern int msix_supported;
+
+int msix_set_mask_notifier(PCIDevice *dev, msix_mask_notifier_func);
+int msix_unset_mask_notifier(PCIDevice *dev);
+#endif
diff --git a/hw/msmouse.c b/hw/msmouse.c
new file mode 100644
index 0000000..05f893c
--- /dev/null
+++ b/hw/msmouse.c
@@ -0,0 +1,78 @@
+/*
+ * QEMU Microsoft serial mouse emulation
+ *
+ * Copyright (c) 2008 Lubomir Rintel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include "../qemu-common.h"
+#include "../qemu-char.h"
+#include "../console.h"
+#include "msmouse.h"
+
+#define MSMOUSE_LO6(n) ((n) & 0x3f)
+#define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6)
+
+static void msmouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ CharDriverState *chr = (CharDriverState *)opaque;
+
+ unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
+
+ /* Movement deltas */
+ bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx);
+ bytes[1] |= MSMOUSE_LO6(dx);
+ bytes[2] |= MSMOUSE_LO6(dy);
+
+ /* Buttons */
+ bytes[0] |= (buttons_state & 0x01 ? 0x20 : 0x00);
+ bytes[0] |= (buttons_state & 0x02 ? 0x10 : 0x00);
+ bytes[3] |= (buttons_state & 0x04 ? 0x20 : 0x00);
+
+ /* We always send the packet of, so that we do not have to keep track
+ of previous state of the middle button. This can potentially confuse
+ some very old drivers for two button mice though. */
+ qemu_chr_read(chr, bytes, 4);
+}
+
+static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len)
+{
+ /* Ignore writes to mouse port */
+ return len;
+}
+
+static void msmouse_chr_close (struct CharDriverState *chr)
+{
+ qemu_free (chr);
+}
+
+CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts)
+{
+ CharDriverState *chr;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ chr->chr_write = msmouse_chr_write;
+ chr->chr_close = msmouse_chr_close;
+
+ qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse");
+
+ return chr;
+}
diff --git a/hw/msmouse.h b/hw/msmouse.h
new file mode 100644
index 0000000..456cb21
--- /dev/null
+++ b/hw/msmouse.h
@@ -0,0 +1,2 @@
+/* msmouse.c */
+CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts);
diff --git a/hw/mst_fpga.c b/hw/mst_fpga.c
new file mode 100644
index 0000000..5252fc5
--- /dev/null
+++ b/hw/mst_fpga.c
@@ -0,0 +1,240 @@
+/*
+ * PXA270-based Intel Mainstone platforms.
+ * FPGA driver
+ *
+ * Copyright (c) 2007 by Armin Kuster <akuster@kama-aina.net> or
+ * <akuster@mvista.com>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "pxa.h"
+#include "mainstone.h"
+
+/* Mainstone FPGA for extern irqs */
+#define FPGA_GPIO_PIN 0
+#define MST_NUM_IRQS 16
+#define MST_LEDDAT1 0x10
+#define MST_LEDDAT2 0x14
+#define MST_LEDCTRL 0x40
+#define MST_GPSWR 0x60
+#define MST_MSCWR1 0x80
+#define MST_MSCWR2 0x84
+#define MST_MSCWR3 0x88
+#define MST_MSCRD 0x90
+#define MST_INTMSKENA 0xc0
+#define MST_INTSETCLR 0xd0
+#define MST_PCMCIA0 0xe0
+#define MST_PCMCIA1 0xe4
+
+typedef struct mst_irq_state{
+ qemu_irq *parent;
+ qemu_irq *pins;
+
+ uint32_t prev_level;
+ uint32_t leddat1;
+ uint32_t leddat2;
+ uint32_t ledctrl;
+ uint32_t gpswr;
+ uint32_t mscwr1;
+ uint32_t mscwr2;
+ uint32_t mscwr3;
+ uint32_t mscrd;
+ uint32_t intmskena;
+ uint32_t intsetclr;
+ uint32_t pcmcia0;
+ uint32_t pcmcia1;
+}mst_irq_state;
+
+static void
+mst_fpga_update_gpio(mst_irq_state *s)
+{
+ uint32_t level, diff;
+ int bit;
+ level = s->prev_level ^ s->intsetclr;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->pins[bit], (level >> bit) & 1 );
+ }
+ s->prev_level = level;
+}
+
+static void
+mst_fpga_set_irq(void *opaque, int irq, int level)
+{
+ mst_irq_state *s = (mst_irq_state *)opaque;
+
+ if (level)
+ s->prev_level |= 1u << irq;
+ else
+ s->prev_level &= ~(1u << irq);
+
+ if(s->intmskena & (1u << irq)) {
+ s->intsetclr = 1u << irq;
+ qemu_set_irq(s->parent[0], level);
+ }
+}
+
+
+static uint32_t
+mst_fpga_readb(void *opaque, target_phys_addr_t addr)
+{
+ mst_irq_state *s = (mst_irq_state *) opaque;
+
+ switch (addr) {
+ case MST_LEDDAT1:
+ return s->leddat1;
+ case MST_LEDDAT2:
+ return s->leddat2;
+ case MST_LEDCTRL:
+ return s->ledctrl;
+ case MST_GPSWR:
+ return s->gpswr;
+ case MST_MSCWR1:
+ return s->mscwr1;
+ case MST_MSCWR2:
+ return s->mscwr2;
+ case MST_MSCWR3:
+ return s->mscwr3;
+ case MST_MSCRD:
+ return s->mscrd;
+ case MST_INTMSKENA:
+ return s->intmskena;
+ case MST_INTSETCLR:
+ return s->intsetclr;
+ case MST_PCMCIA0:
+ return s->pcmcia0;
+ case MST_PCMCIA1:
+ return s->pcmcia1;
+ default:
+ printf("Mainstone - mst_fpga_readb: Bad register offset "
+ REG_FMT " \n", addr);
+ }
+ return 0;
+}
+
+static void
+mst_fpga_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ mst_irq_state *s = (mst_irq_state *) opaque;
+ value &= 0xffffffff;
+
+ switch (addr) {
+ case MST_LEDDAT1:
+ s->leddat1 = value;
+ break;
+ case MST_LEDDAT2:
+ s->leddat2 = value;
+ break;
+ case MST_LEDCTRL:
+ s->ledctrl = value;
+ break;
+ case MST_GPSWR:
+ s->gpswr = value;
+ break;
+ case MST_MSCWR1:
+ s->mscwr1 = value;
+ break;
+ case MST_MSCWR2:
+ s->mscwr2 = value;
+ break;
+ case MST_MSCWR3:
+ s->mscwr3 = value;
+ break;
+ case MST_MSCRD:
+ s->mscrd = value;
+ break;
+ case MST_INTMSKENA: /* Mask interupt */
+ s->intmskena = (value & 0xFEEFF);
+ mst_fpga_update_gpio(s);
+ break;
+ case MST_INTSETCLR: /* clear or set interrupt */
+ s->intsetclr = (value & 0xFEEFF);
+ break;
+ case MST_PCMCIA0:
+ s->pcmcia0 = value;
+ break;
+ case MST_PCMCIA1:
+ s->pcmcia1 = value;
+ break;
+ default:
+ printf("Mainstone - mst_fpga_writeb: Bad register offset "
+ REG_FMT " \n", addr);
+ }
+}
+
+static CPUReadMemoryFunc * const mst_fpga_readfn[] = {
+ mst_fpga_readb,
+ mst_fpga_readb,
+ mst_fpga_readb,
+};
+static CPUWriteMemoryFunc * const mst_fpga_writefn[] = {
+ mst_fpga_writeb,
+ mst_fpga_writeb,
+ mst_fpga_writeb,
+};
+
+static void
+mst_fpga_save(QEMUFile *f, void *opaque)
+{
+ struct mst_irq_state *s = (mst_irq_state *) opaque;
+
+ qemu_put_be32s(f, &s->prev_level);
+ qemu_put_be32s(f, &s->leddat1);
+ qemu_put_be32s(f, &s->leddat2);
+ qemu_put_be32s(f, &s->ledctrl);
+ qemu_put_be32s(f, &s->gpswr);
+ qemu_put_be32s(f, &s->mscwr1);
+ qemu_put_be32s(f, &s->mscwr2);
+ qemu_put_be32s(f, &s->mscwr3);
+ qemu_put_be32s(f, &s->mscrd);
+ qemu_put_be32s(f, &s->intmskena);
+ qemu_put_be32s(f, &s->intsetclr);
+ qemu_put_be32s(f, &s->pcmcia0);
+ qemu_put_be32s(f, &s->pcmcia1);
+}
+
+static int
+mst_fpga_load(QEMUFile *f, void *opaque, int version_id)
+{
+ mst_irq_state *s = (mst_irq_state *) opaque;
+
+ qemu_get_be32s(f, &s->prev_level);
+ qemu_get_be32s(f, &s->leddat1);
+ qemu_get_be32s(f, &s->leddat2);
+ qemu_get_be32s(f, &s->ledctrl);
+ qemu_get_be32s(f, &s->gpswr);
+ qemu_get_be32s(f, &s->mscwr1);
+ qemu_get_be32s(f, &s->mscwr2);
+ qemu_get_be32s(f, &s->mscwr3);
+ qemu_get_be32s(f, &s->mscrd);
+ qemu_get_be32s(f, &s->intmskena);
+ qemu_get_be32s(f, &s->intsetclr);
+ qemu_get_be32s(f, &s->pcmcia0);
+ qemu_get_be32s(f, &s->pcmcia1);
+ return 0;
+}
+
+qemu_irq *mst_irq_init(PXA2xxState *cpu, uint32_t base, int irq)
+{
+ mst_irq_state *s;
+ int iomemtype;
+ qemu_irq *qi;
+
+ s = (mst_irq_state *)
+ qemu_mallocz(sizeof(mst_irq_state));
+
+ s->parent = &cpu->pic[irq];
+
+ /* alloc the external 16 irqs */
+ qi = qemu_allocate_irqs(mst_fpga_set_irq, s, MST_NUM_IRQS);
+ s->pins = qi;
+
+ iomemtype = cpu_register_io_memory(mst_fpga_readfn,
+ mst_fpga_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00100000, iomemtype);
+ register_savevm(NULL, "mainstone_fpga", 0, 0, mst_fpga_save,
+ mst_fpga_load, s);
+ return qi;
+}
diff --git a/hw/multiboot.c b/hw/multiboot.c
new file mode 100644
index 0000000..0d2bfb4
--- /dev/null
+++ b/hw/multiboot.c
@@ -0,0 +1,339 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "fw_cfg.h"
+#include "multiboot.h"
+#include "loader.h"
+#include "elf.h"
+#include "sysemu.h"
+
+/* Show multiboot debug output */
+//#define DEBUG_MULTIBOOT
+
+#ifdef DEBUG_MULTIBOOT
+#define mb_debug(a...) fprintf(stderr, ## a)
+#else
+#define mb_debug(a...)
+#endif
+
+#define MULTIBOOT_STRUCT_ADDR 0x9000
+
+#if MULTIBOOT_STRUCT_ADDR > 0xf0000
+#error multiboot struct needs to fit in 16 bit real mode
+#endif
+
+enum {
+ /* Multiboot info */
+ MBI_FLAGS = 0,
+ MBI_MEM_LOWER = 4,
+ MBI_MEM_UPPER = 8,
+ MBI_BOOT_DEVICE = 12,
+ MBI_CMDLINE = 16,
+ MBI_MODS_COUNT = 20,
+ MBI_MODS_ADDR = 24,
+ MBI_MMAP_ADDR = 48,
+
+ MBI_SIZE = 88,
+
+ /* Multiboot modules */
+ MB_MOD_START = 0,
+ MB_MOD_END = 4,
+ MB_MOD_CMDLINE = 8,
+
+ MB_MOD_SIZE = 16,
+
+ /* Region offsets */
+ ADDR_E820_MAP = MULTIBOOT_STRUCT_ADDR + 0,
+ ADDR_MBI = ADDR_E820_MAP + 0x500,
+
+ /* Multiboot flags */
+ MULTIBOOT_FLAGS_MEMORY = 1 << 0,
+ MULTIBOOT_FLAGS_BOOT_DEVICE = 1 << 1,
+ MULTIBOOT_FLAGS_CMDLINE = 1 << 2,
+ MULTIBOOT_FLAGS_MODULES = 1 << 3,
+ MULTIBOOT_FLAGS_MMAP = 1 << 6,
+};
+
+typedef struct {
+ /* buffer holding kernel, cmdlines and mb_infos */
+ void *mb_buf;
+ /* address in target */
+ target_phys_addr_t mb_buf_phys;
+ /* size of mb_buf in bytes */
+ unsigned mb_buf_size;
+ /* offset of mb-info's in bytes */
+ target_phys_addr_t offset_mbinfo;
+ /* offset in buffer for cmdlines in bytes */
+ target_phys_addr_t offset_cmdlines;
+ /* offset of modules in bytes */
+ target_phys_addr_t offset_mods;
+ /* available slots for mb modules infos */
+ int mb_mods_avail;
+ /* currently used slots of mb modules */
+ int mb_mods_count;
+} MultibootState;
+
+static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline)
+{
+ int len = strlen(cmdline) + 1;
+ target_phys_addr_t p = s->offset_cmdlines;
+
+ pstrcpy((char *)s->mb_buf + p, len, cmdline);
+ s->offset_cmdlines += len;
+ return s->mb_buf_phys + p;
+}
+
+static void mb_add_mod(MultibootState *s,
+ target_phys_addr_t start, target_phys_addr_t end,
+ target_phys_addr_t cmdline_phys)
+{
+ char *p;
+ assert(s->mb_mods_count < s->mb_mods_avail);
+
+ p = (char *)s->mb_buf + s->offset_mbinfo + MB_MOD_SIZE * s->mb_mods_count;
+
+ stl_p(p + MB_MOD_START, start);
+ stl_p(p + MB_MOD_END, end);
+ stl_p(p + MB_MOD_CMDLINE, cmdline_phys);
+
+ mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n",
+ s->mb_mods_count, start, end);
+
+ s->mb_mods_count++;
+}
+
+int load_multiboot(void *fw_cfg,
+ FILE *f,
+ const char *kernel_filename,
+ const char *initrd_filename,
+ const char *kernel_cmdline,
+ int kernel_file_size,
+ uint8_t *header)
+{
+ int i, is_multiboot = 0;
+ uint32_t flags = 0;
+ uint32_t mh_entry_addr;
+ uint32_t mh_load_addr;
+ uint32_t mb_kernel_size;
+ MultibootState mbs;
+ uint8_t bootinfo[MBI_SIZE];
+ uint8_t *mb_bootinfo_data;
+
+ /* Ok, let's see if it is a multiboot image.
+ The header is 12x32bit long, so the latest entry may be 8192 - 48. */
+ for (i = 0; i < (8192 - 48); i += 4) {
+ if (ldl_p(header+i) == 0x1BADB002) {
+ uint32_t checksum = ldl_p(header+i+8);
+ flags = ldl_p(header+i+4);
+ checksum += flags;
+ checksum += (uint32_t)0x1BADB002;
+ if (!checksum) {
+ is_multiboot = 1;
+ break;
+ }
+ }
+ }
+
+ if (!is_multiboot)
+ return 0; /* no multiboot */
+
+ mb_debug("qemu: I believe we found a multiboot image!\n");
+ memset(bootinfo, 0, sizeof(bootinfo));
+ memset(&mbs, 0, sizeof(mbs));
+
+ if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */
+ fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n");
+ }
+ if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */
+ uint64_t elf_entry;
+ uint64_t elf_low, elf_high;
+ int kernel_size;
+ fclose(f);
+
+ if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
+ fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n");
+ exit(1);
+ }
+
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ &elf_low, &elf_high, 0, ELF_MACHINE, 0);
+ if (kernel_size < 0) {
+ fprintf(stderr, "Error while loading elf kernel\n");
+ exit(1);
+ }
+ mh_load_addr = elf_low;
+ mb_kernel_size = elf_high - elf_low;
+ mh_entry_addr = elf_entry;
+
+ mbs.mb_buf = qemu_malloc(mb_kernel_size);
+ if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) {
+ fprintf(stderr, "Error while fetching elf kernel from rom\n");
+ exit(1);
+ }
+
+ mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n",
+ mb_kernel_size, (size_t)mh_entry_addr);
+ } else {
+ /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
+ uint32_t mh_header_addr = ldl_p(header+i+12);
+ mh_load_addr = ldl_p(header+i+16);
+ uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
+
+ mh_entry_addr = ldl_p(header+i+28);
+ mb_kernel_size = kernel_file_size - mb_kernel_text_offset;
+
+ /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE.
+ uint32_t mh_mode_type = ldl_p(header+i+32);
+ uint32_t mh_width = ldl_p(header+i+36);
+ uint32_t mh_height = ldl_p(header+i+40);
+ uint32_t mh_depth = ldl_p(header+i+44); */
+
+ mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr);
+ mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr);
+ mb_debug("multiboot: mh_load_end_addr = %#x\n", ldl_p(header+i+20));
+ mb_debug("multiboot: mh_bss_end_addr = %#x\n", ldl_p(header+i+24));
+ mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n",
+ mb_kernel_size, mh_load_addr);
+
+ mbs.mb_buf = qemu_malloc(mb_kernel_size);
+ fseek(f, mb_kernel_text_offset, SEEK_SET);
+ if (fread(mbs.mb_buf, 1, mb_kernel_size, f) != mb_kernel_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ fclose(f);
+ }
+
+ mbs.mb_buf_phys = mh_load_addr;
+
+ mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_kernel_size);
+ mbs.offset_mbinfo = mbs.mb_buf_size;
+
+ /* Calculate space for cmdlines and mb_mods */
+ mbs.mb_buf_size += strlen(kernel_filename) + 1;
+ mbs.mb_buf_size += strlen(kernel_cmdline) + 1;
+ if (initrd_filename) {
+ const char *r = initrd_filename;
+ mbs.mb_buf_size += strlen(r) + 1;
+ mbs.mb_mods_avail = 1;
+ while ((r = strchr(r, ','))) {
+ mbs.mb_mods_avail++;
+ r++;
+ }
+ mbs.mb_buf_size += MB_MOD_SIZE * mbs.mb_mods_avail;
+ }
+
+ mbs.mb_buf_size = TARGET_PAGE_ALIGN(mbs.mb_buf_size);
+
+ /* enlarge mb_buf to hold cmdlines and mb-info structs */
+ mbs.mb_buf = qemu_realloc(mbs.mb_buf, mbs.mb_buf_size);
+ mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE;
+
+ if (initrd_filename) {
+ char *next_initrd;
+
+ mbs.offset_mods = mbs.mb_buf_size;
+
+ do {
+ char *next_space;
+ int mb_mod_length;
+ uint32_t offs = mbs.mb_buf_size;
+
+ next_initrd = strchr(initrd_filename, ',');
+ if (next_initrd)
+ *next_initrd = '\0';
+ /* if a space comes after the module filename, treat everything
+ after that as parameters */
+ target_phys_addr_t c = mb_add_cmdline(&mbs, initrd_filename);
+ if ((next_space = strchr(initrd_filename, ' ')))
+ *next_space = '\0';
+ mb_debug("multiboot loading module: %s\n", initrd_filename);
+ mb_mod_length = get_image_size(initrd_filename);
+ if (mb_mod_length < 0) {
+ fprintf(stderr, "failed to get %s image size\n", initrd_filename);
+ exit(1);
+ }
+
+ mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size);
+ mbs.mb_buf = qemu_realloc(mbs.mb_buf, mbs.mb_buf_size);
+
+ load_image(initrd_filename, (unsigned char *)mbs.mb_buf + offs);
+ mb_add_mod(&mbs, mbs.mb_buf_phys + offs,
+ mbs.mb_buf_phys + offs + mb_mod_length, c);
+
+ mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx"\n",
+ (char *)mbs.mb_buf + offs,
+ (char *)mbs.mb_buf + offs + mb_mod_length, c);
+ initrd_filename = next_initrd+1;
+ } while (next_initrd);
+ }
+
+ /* Commandline support */
+ char kcmdline[strlen(kernel_filename) + strlen(kernel_cmdline) + 2];
+ snprintf(kcmdline, sizeof(kcmdline), "%s %s",
+ kernel_filename, kernel_cmdline);
+ stl_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline));
+
+ stl_p(bootinfo + MBI_MODS_ADDR, mbs.mb_buf_phys + mbs.offset_mbinfo);
+ stl_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */
+
+ /* the kernel is where we want it to be now */
+ stl_p(bootinfo + MBI_FLAGS, MULTIBOOT_FLAGS_MEMORY
+ | MULTIBOOT_FLAGS_BOOT_DEVICE
+ | MULTIBOOT_FLAGS_CMDLINE
+ | MULTIBOOT_FLAGS_MODULES
+ | 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_MMAP_ADDR, ADDR_E820_MAP);
+
+ mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
+ mb_debug(" mb_buf_phys = "TARGET_FMT_plx"\n", mbs.mb_buf_phys);
+ mb_debug(" mod_start = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods);
+ mb_debug(" mb_mods_count = %d\n", mbs.mb_mods_count);
+
+ /* save bootinfo off the stack */
+ mb_bootinfo_data = qemu_malloc(sizeof(bootinfo));
+ memcpy(mb_bootinfo_data, bootinfo, sizeof(bootinfo));
+
+ /* Pass variables to option rom */
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, mh_entry_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, mbs.mb_buf_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA,
+ mbs.mb_buf, mbs.mb_buf_size);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, ADDR_MBI);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, sizeof(bootinfo));
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data,
+ sizeof(bootinfo));
+
+ option_rom[nb_option_roms].name = "multiboot.bin";
+ option_rom[nb_option_roms].bootindex = 0;
+ nb_option_roms++;
+
+ return 1; /* yes, we are multiboot */
+}
diff --git a/hw/multiboot.h b/hw/multiboot.h
new file mode 100644
index 0000000..98fb1b7
--- /dev/null
+++ b/hw/multiboot.h
@@ -0,0 +1,12 @@
+#ifndef QEMU_MULTIBOOT_H
+#define QEMU_MULTIBOOT_H
+
+int load_multiboot(void *fw_cfg,
+ FILE *f,
+ const char *kernel_filename,
+ const char *initrd_filename,
+ const char *kernel_cmdline,
+ int kernel_file_size,
+ uint8_t *header);
+
+#endif
diff --git a/hw/musicpal.c b/hw/musicpal.c
new file mode 100644
index 0000000..d98aa8d
--- /dev/null
+++ b/hw/musicpal.c
@@ -0,0 +1,1665 @@
+/*
+ * Marvell MV88W8618 / Freecom MusicPal emulation.
+ *
+ * Copyright (c) 2008 Jan Kiszka
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "devices.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "block.h"
+#include "flash.h"
+#include "console.h"
+#include "i2c.h"
+#include "blockdev.h"
+
+#define MP_MISC_BASE 0x80002000
+#define MP_MISC_SIZE 0x00001000
+
+#define MP_ETH_BASE 0x80008000
+#define MP_ETH_SIZE 0x00001000
+
+#define MP_WLAN_BASE 0x8000C000
+#define MP_WLAN_SIZE 0x00000800
+
+#define MP_UART1_BASE 0x8000C840
+#define MP_UART2_BASE 0x8000C940
+
+#define MP_GPIO_BASE 0x8000D000
+#define MP_GPIO_SIZE 0x00001000
+
+#define MP_FLASHCFG_BASE 0x90006000
+#define MP_FLASHCFG_SIZE 0x00001000
+
+#define MP_AUDIO_BASE 0x90007000
+
+#define MP_PIC_BASE 0x90008000
+#define MP_PIC_SIZE 0x00001000
+
+#define MP_PIT_BASE 0x90009000
+#define MP_PIT_SIZE 0x00001000
+
+#define MP_LCD_BASE 0x9000c000
+#define MP_LCD_SIZE 0x00001000
+
+#define MP_SRAM_BASE 0xC0000000
+#define MP_SRAM_SIZE 0x00020000
+
+#define MP_RAM_DEFAULT_SIZE 32*1024*1024
+#define MP_FLASH_SIZE_MAX 32*1024*1024
+
+#define MP_TIMER1_IRQ 4
+#define MP_TIMER2_IRQ 5
+#define MP_TIMER3_IRQ 6
+#define MP_TIMER4_IRQ 7
+#define MP_EHCI_IRQ 8
+#define MP_ETH_IRQ 9
+#define MP_UART1_IRQ 11
+#define MP_UART2_IRQ 11
+#define MP_GPIO_IRQ 12
+#define MP_RTC_IRQ 28
+#define MP_AUDIO_IRQ 30
+
+/* Wolfson 8750 I2C address */
+#define MP_WM_ADDR 0x1A
+
+/* Ethernet register offsets */
+#define MP_ETH_SMIR 0x010
+#define MP_ETH_PCXR 0x408
+#define MP_ETH_SDCMR 0x448
+#define MP_ETH_ICR 0x450
+#define MP_ETH_IMR 0x458
+#define MP_ETH_FRDP0 0x480
+#define MP_ETH_FRDP1 0x484
+#define MP_ETH_FRDP2 0x488
+#define MP_ETH_FRDP3 0x48C
+#define MP_ETH_CRDP0 0x4A0
+#define MP_ETH_CRDP1 0x4A4
+#define MP_ETH_CRDP2 0x4A8
+#define MP_ETH_CRDP3 0x4AC
+#define MP_ETH_CTDP0 0x4E0
+#define MP_ETH_CTDP1 0x4E4
+#define MP_ETH_CTDP2 0x4E8
+#define MP_ETH_CTDP3 0x4EC
+
+/* MII PHY access */
+#define MP_ETH_SMIR_DATA 0x0000FFFF
+#define MP_ETH_SMIR_ADDR 0x03FF0000
+#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
+#define MP_ETH_SMIR_RDVALID (1 << 27)
+
+/* PHY registers */
+#define MP_ETH_PHY1_BMSR 0x00210000
+#define MP_ETH_PHY1_PHYSID1 0x00410000
+#define MP_ETH_PHY1_PHYSID2 0x00610000
+
+#define MP_PHY_BMSR_LINK 0x0004
+#define MP_PHY_BMSR_AUTONEG 0x0008
+
+#define MP_PHY_88E3015 0x01410E20
+
+/* TX descriptor status */
+#define MP_ETH_TX_OWN (1 << 31)
+
+/* RX descriptor status */
+#define MP_ETH_RX_OWN (1 << 31)
+
+/* Interrupt cause/mask bits */
+#define MP_ETH_IRQ_RX_BIT 0
+#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
+#define MP_ETH_IRQ_TXHI_BIT 2
+#define MP_ETH_IRQ_TXLO_BIT 3
+
+/* Port config bits */
+#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
+
+/* SDMA command bits */
+#define MP_ETH_CMD_TXHI (1 << 23)
+#define MP_ETH_CMD_TXLO (1 << 22)
+
+typedef struct mv88w8618_tx_desc {
+ uint32_t cmdstat;
+ uint16_t res;
+ uint16_t bytes;
+ uint32_t buffer;
+ uint32_t next;
+} mv88w8618_tx_desc;
+
+typedef struct mv88w8618_rx_desc {
+ uint32_t cmdstat;
+ uint16_t bytes;
+ uint16_t buffer_size;
+ uint32_t buffer;
+ uint32_t next;
+} mv88w8618_rx_desc;
+
+typedef struct mv88w8618_eth_state {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t smir;
+ uint32_t icr;
+ uint32_t imr;
+ int mmio_index;
+ uint32_t vlan_header;
+ uint32_t tx_queue[2];
+ uint32_t rx_queue[4];
+ uint32_t frx_queue[4];
+ uint32_t cur_rx[4];
+ NICState *nic;
+ NICConf conf;
+} mv88w8618_eth_state;
+
+static void eth_rx_desc_put(uint32_t addr, mv88w8618_rx_desc *desc)
+{
+ cpu_to_le32s(&desc->cmdstat);
+ cpu_to_le16s(&desc->bytes);
+ cpu_to_le16s(&desc->buffer_size);
+ cpu_to_le32s(&desc->buffer);
+ cpu_to_le32s(&desc->next);
+ cpu_physical_memory_write(addr, (void *)desc, sizeof(*desc));
+}
+
+static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc)
+{
+ cpu_physical_memory_read(addr, (void *)desc, sizeof(*desc));
+ le32_to_cpus(&desc->cmdstat);
+ le16_to_cpus(&desc->bytes);
+ le16_to_cpus(&desc->buffer_size);
+ le32_to_cpus(&desc->buffer);
+ le32_to_cpus(&desc->next);
+}
+
+static int eth_can_receive(VLANClientState *nc)
+{
+ return 1;
+}
+
+static ssize_t eth_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ uint32_t desc_addr;
+ mv88w8618_rx_desc desc;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ desc_addr = s->cur_rx[i];
+ if (!desc_addr) {
+ continue;
+ }
+ do {
+ eth_rx_desc_get(desc_addr, &desc);
+ if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
+ cpu_physical_memory_write(desc.buffer + s->vlan_header,
+ buf, size);
+ desc.bytes = size + s->vlan_header;
+ desc.cmdstat &= ~MP_ETH_RX_OWN;
+ s->cur_rx[i] = desc.next;
+
+ s->icr |= MP_ETH_IRQ_RX;
+ if (s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ eth_rx_desc_put(desc_addr, &desc);
+ return size;
+ }
+ desc_addr = desc.next;
+ } while (desc_addr != s->rx_queue[i]);
+ }
+ return size;
+}
+
+static void eth_tx_desc_put(uint32_t addr, mv88w8618_tx_desc *desc)
+{
+ cpu_to_le32s(&desc->cmdstat);
+ cpu_to_le16s(&desc->res);
+ cpu_to_le16s(&desc->bytes);
+ cpu_to_le32s(&desc->buffer);
+ cpu_to_le32s(&desc->next);
+ cpu_physical_memory_write(addr, (void *)desc, sizeof(*desc));
+}
+
+static void eth_tx_desc_get(uint32_t addr, mv88w8618_tx_desc *desc)
+{
+ cpu_physical_memory_read(addr, (void *)desc, sizeof(*desc));
+ le32_to_cpus(&desc->cmdstat);
+ le16_to_cpus(&desc->res);
+ le16_to_cpus(&desc->bytes);
+ le32_to_cpus(&desc->buffer);
+ le32_to_cpus(&desc->next);
+}
+
+static void eth_send(mv88w8618_eth_state *s, int queue_index)
+{
+ uint32_t desc_addr = s->tx_queue[queue_index];
+ mv88w8618_tx_desc desc;
+ uint32_t next_desc;
+ uint8_t buf[2048];
+ int len;
+
+ do {
+ eth_tx_desc_get(desc_addr, &desc);
+ next_desc = desc.next;
+ if (desc.cmdstat & MP_ETH_TX_OWN) {
+ len = desc.bytes;
+ if (len < 2048) {
+ cpu_physical_memory_read(desc.buffer, buf, len);
+ qemu_send_packet(&s->nic->nc, buf, len);
+ }
+ desc.cmdstat &= ~MP_ETH_TX_OWN;
+ s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
+ eth_tx_desc_put(desc_addr, &desc);
+ }
+ desc_addr = next_desc;
+ } while (desc_addr != s->tx_queue[queue_index]);
+}
+
+static uint32_t mv88w8618_eth_read(void *opaque, target_phys_addr_t offset)
+{
+ mv88w8618_eth_state *s = opaque;
+
+ switch (offset) {
+ case MP_ETH_SMIR:
+ if (s->smir & MP_ETH_SMIR_OPCODE) {
+ switch (s->smir & MP_ETH_SMIR_ADDR) {
+ case MP_ETH_PHY1_BMSR:
+ return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
+ MP_ETH_SMIR_RDVALID;
+ case MP_ETH_PHY1_PHYSID1:
+ return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
+ case MP_ETH_PHY1_PHYSID2:
+ return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
+ default:
+ return MP_ETH_SMIR_RDVALID;
+ }
+ }
+ return 0;
+
+ case MP_ETH_ICR:
+ return s->icr;
+
+ case MP_ETH_IMR:
+ return s->imr;
+
+ case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
+ return s->frx_queue[(offset - MP_ETH_FRDP0)/4];
+
+ case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
+ return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
+
+ case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
+ return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_eth_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mv88w8618_eth_state *s = opaque;
+
+ switch (offset) {
+ case MP_ETH_SMIR:
+ s->smir = value;
+ break;
+
+ case MP_ETH_PCXR:
+ s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
+ break;
+
+ case MP_ETH_SDCMR:
+ if (value & MP_ETH_CMD_TXHI) {
+ eth_send(s, 1);
+ }
+ if (value & MP_ETH_CMD_TXLO) {
+ eth_send(s, 0);
+ }
+ if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_ETH_ICR:
+ s->icr &= value;
+ break;
+
+ case MP_ETH_IMR:
+ s->imr = value;
+ if (s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
+ s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value;
+ break;
+
+ case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
+ s->rx_queue[(offset - MP_ETH_CRDP0)/4] =
+ s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
+ break;
+
+ case MP_ETH_CTDP0 ... MP_ETH_CTDP3:
+ s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const mv88w8618_eth_readfn[] = {
+ mv88w8618_eth_read,
+ mv88w8618_eth_read,
+ mv88w8618_eth_read
+};
+
+static CPUWriteMemoryFunc * const mv88w8618_eth_writefn[] = {
+ mv88w8618_eth_write,
+ mv88w8618_eth_write,
+ mv88w8618_eth_write
+};
+
+static void eth_cleanup(VLANClientState *nc)
+{
+ mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_mv88w8618_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_receive,
+ .receive = eth_receive,
+ .cleanup = eth_cleanup,
+};
+
+static int mv88w8618_eth_init(SysBusDevice *dev)
+{
+ mv88w8618_eth_state *s = FROM_SYSBUS(mv88w8618_eth_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ s->mmio_index = cpu_register_io_memory(mv88w8618_eth_readfn,
+ mv88w8618_eth_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_ETH_SIZE, s->mmio_index);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_eth_vmsd = {
+ .name = "mv88w8618_eth",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(smir, mv88w8618_eth_state),
+ VMSTATE_UINT32(icr, mv88w8618_eth_state),
+ VMSTATE_UINT32(imr, mv88w8618_eth_state),
+ VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
+ VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
+ VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
+ VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
+ VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo mv88w8618_eth_info = {
+ .init = mv88w8618_eth_init,
+ .qdev.name = "mv88w8618_eth",
+ .qdev.size = sizeof(mv88w8618_eth_state),
+ .qdev.vmsd = &mv88w8618_eth_vmsd,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+/* LCD register offsets */
+#define MP_LCD_IRQCTRL 0x180
+#define MP_LCD_IRQSTAT 0x184
+#define MP_LCD_SPICTRL 0x1ac
+#define MP_LCD_INST 0x1bc
+#define MP_LCD_DATA 0x1c0
+
+/* Mode magics */
+#define MP_LCD_SPI_DATA 0x00100011
+#define MP_LCD_SPI_CMD 0x00104011
+#define MP_LCD_SPI_INVALID 0x00000000
+
+/* Commmands */
+#define MP_LCD_INST_SETPAGE0 0xB0
+/* ... */
+#define MP_LCD_INST_SETPAGE7 0xB7
+
+#define MP_LCD_TEXTCOLOR 0xe0e0ff /* RRGGBB */
+
+typedef struct musicpal_lcd_state {
+ SysBusDevice busdev;
+ uint32_t brightness;
+ uint32_t mode;
+ uint32_t irqctrl;
+ uint32_t page;
+ uint32_t page_off;
+ DisplayState *ds;
+ uint8_t video_ram[128*64/8];
+} musicpal_lcd_state;
+
+static uint8_t scale_lcd_color(musicpal_lcd_state *s, uint8_t col)
+{
+ switch (s->brightness) {
+ case 7:
+ return col;
+ case 0:
+ return 0;
+ default:
+ return (col * s->brightness) / 7;
+ }
+}
+
+#define SET_LCD_PIXEL(depth, type) \
+static inline void glue(set_lcd_pixel, depth) \
+ (musicpal_lcd_state *s, int x, int y, type col) \
+{ \
+ int dx, dy; \
+ type *pixel = &((type *) ds_get_data(s->ds))[(y * 128 * 3 + x) * 3]; \
+\
+ for (dy = 0; dy < 3; dy++, pixel += 127 * 3) \
+ for (dx = 0; dx < 3; dx++, pixel++) \
+ *pixel = col; \
+}
+SET_LCD_PIXEL(8, uint8_t)
+SET_LCD_PIXEL(16, uint16_t)
+SET_LCD_PIXEL(32, uint32_t)
+
+#include "pixel_ops.h"
+
+static void lcd_refresh(void *opaque)
+{
+ musicpal_lcd_state *s = opaque;
+ int x, y, col;
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ return;
+#define LCD_REFRESH(depth, func) \
+ case depth: \
+ col = func(scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 16) & 0xff), \
+ scale_lcd_color(s, (MP_LCD_TEXTCOLOR >> 8) & 0xff), \
+ scale_lcd_color(s, MP_LCD_TEXTCOLOR & 0xff)); \
+ for (x = 0; x < 128; x++) { \
+ for (y = 0; y < 64; y++) { \
+ if (s->video_ram[x + (y/8)*128] & (1 << (y % 8))) { \
+ glue(set_lcd_pixel, depth)(s, x, y, col); \
+ } else { \
+ glue(set_lcd_pixel, depth)(s, x, y, 0); \
+ } \
+ } \
+ } \
+ break;
+ LCD_REFRESH(8, rgb_to_pixel8)
+ LCD_REFRESH(16, rgb_to_pixel16)
+ LCD_REFRESH(32, (is_surface_bgr(s->ds->surface) ?
+ rgb_to_pixel32bgr : rgb_to_pixel32))
+ default:
+ hw_error("unsupported colour depth %i\n",
+ ds_get_bits_per_pixel(s->ds));
+ }
+
+ dpy_update(s->ds, 0, 0, 128*3, 64*3);
+}
+
+static void lcd_invalidate(void *opaque)
+{
+}
+
+static void musicpal_lcd_gpio_brigthness_in(void *opaque, int irq, int level)
+{
+ musicpal_lcd_state *s = opaque;
+ s->brightness &= ~(1 << irq);
+ s->brightness |= level << irq;
+}
+
+static uint32_t musicpal_lcd_read(void *opaque, target_phys_addr_t offset)
+{
+ musicpal_lcd_state *s = opaque;
+
+ switch (offset) {
+ case MP_LCD_IRQCTRL:
+ return s->irqctrl;
+
+ default:
+ return 0;
+ }
+}
+
+static void musicpal_lcd_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ musicpal_lcd_state *s = opaque;
+
+ switch (offset) {
+ case MP_LCD_IRQCTRL:
+ s->irqctrl = value;
+ break;
+
+ case MP_LCD_SPICTRL:
+ if (value == MP_LCD_SPI_DATA || value == MP_LCD_SPI_CMD) {
+ s->mode = value;
+ } else {
+ s->mode = MP_LCD_SPI_INVALID;
+ }
+ break;
+
+ case MP_LCD_INST:
+ if (value >= MP_LCD_INST_SETPAGE0 && value <= MP_LCD_INST_SETPAGE7) {
+ s->page = value - MP_LCD_INST_SETPAGE0;
+ s->page_off = 0;
+ }
+ break;
+
+ case MP_LCD_DATA:
+ if (s->mode == MP_LCD_SPI_CMD) {
+ if (value >= MP_LCD_INST_SETPAGE0 &&
+ value <= MP_LCD_INST_SETPAGE7) {
+ s->page = value - MP_LCD_INST_SETPAGE0;
+ s->page_off = 0;
+ }
+ } else if (s->mode == MP_LCD_SPI_DATA) {
+ s->video_ram[s->page*128 + s->page_off] = value;
+ s->page_off = (s->page_off + 1) & 127;
+ }
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const musicpal_lcd_readfn[] = {
+ musicpal_lcd_read,
+ musicpal_lcd_read,
+ musicpal_lcd_read
+};
+
+static CPUWriteMemoryFunc * const musicpal_lcd_writefn[] = {
+ musicpal_lcd_write,
+ musicpal_lcd_write,
+ musicpal_lcd_write
+};
+
+static int musicpal_lcd_init(SysBusDevice *dev)
+{
+ musicpal_lcd_state *s = FROM_SYSBUS(musicpal_lcd_state, dev);
+ int iomemtype;
+
+ s->brightness = 7;
+
+ iomemtype = cpu_register_io_memory(musicpal_lcd_readfn,
+ musicpal_lcd_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_LCD_SIZE, iomemtype);
+
+ s->ds = graphic_console_init(lcd_refresh, lcd_invalidate,
+ NULL, NULL, s);
+ qemu_console_resize(s->ds, 128*3, 64*3);
+
+ qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3);
+
+ return 0;
+}
+
+static const VMStateDescription musicpal_lcd_vmsd = {
+ .name = "musicpal_lcd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(brightness, musicpal_lcd_state),
+ VMSTATE_UINT32(mode, musicpal_lcd_state),
+ VMSTATE_UINT32(irqctrl, musicpal_lcd_state),
+ VMSTATE_UINT32(page, musicpal_lcd_state),
+ VMSTATE_UINT32(page_off, musicpal_lcd_state),
+ VMSTATE_BUFFER(video_ram, musicpal_lcd_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo musicpal_lcd_info = {
+ .init = musicpal_lcd_init,
+ .qdev.name = "musicpal_lcd",
+ .qdev.size = sizeof(musicpal_lcd_state),
+ .qdev.vmsd = &musicpal_lcd_vmsd,
+};
+
+/* PIC register offsets */
+#define MP_PIC_STATUS 0x00
+#define MP_PIC_ENABLE_SET 0x08
+#define MP_PIC_ENABLE_CLR 0x0C
+
+typedef struct mv88w8618_pic_state
+{
+ SysBusDevice busdev;
+ uint32_t level;
+ uint32_t enabled;
+ qemu_irq parent_irq;
+} mv88w8618_pic_state;
+
+static void mv88w8618_pic_update(mv88w8618_pic_state *s)
+{
+ qemu_set_irq(s->parent_irq, (s->level & s->enabled));
+}
+
+static void mv88w8618_pic_set_irq(void *opaque, int irq, int level)
+{
+ mv88w8618_pic_state *s = opaque;
+
+ if (level) {
+ s->level |= 1 << irq;
+ } else {
+ s->level &= ~(1 << irq);
+ }
+ mv88w8618_pic_update(s);
+}
+
+static uint32_t mv88w8618_pic_read(void *opaque, target_phys_addr_t offset)
+{
+ mv88w8618_pic_state *s = opaque;
+
+ switch (offset) {
+ case MP_PIC_STATUS:
+ return s->level & s->enabled;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_pic_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mv88w8618_pic_state *s = opaque;
+
+ switch (offset) {
+ case MP_PIC_ENABLE_SET:
+ s->enabled |= value;
+ break;
+
+ case MP_PIC_ENABLE_CLR:
+ s->enabled &= ~value;
+ s->level &= ~value;
+ break;
+ }
+ mv88w8618_pic_update(s);
+}
+
+static void mv88w8618_pic_reset(DeviceState *d)
+{
+ mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state,
+ sysbus_from_qdev(d));
+
+ s->level = 0;
+ s->enabled = 0;
+}
+
+static CPUReadMemoryFunc * const mv88w8618_pic_readfn[] = {
+ mv88w8618_pic_read,
+ mv88w8618_pic_read,
+ mv88w8618_pic_read
+};
+
+static CPUWriteMemoryFunc * const mv88w8618_pic_writefn[] = {
+ mv88w8618_pic_write,
+ mv88w8618_pic_write,
+ mv88w8618_pic_write
+};
+
+static int mv88w8618_pic_init(SysBusDevice *dev)
+{
+ mv88w8618_pic_state *s = FROM_SYSBUS(mv88w8618_pic_state, dev);
+ int iomemtype;
+
+ qdev_init_gpio_in(&dev->qdev, mv88w8618_pic_set_irq, 32);
+ sysbus_init_irq(dev, &s->parent_irq);
+ iomemtype = cpu_register_io_memory(mv88w8618_pic_readfn,
+ mv88w8618_pic_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_PIC_SIZE, iomemtype);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_pic_vmsd = {
+ .name = "mv88w8618_pic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, mv88w8618_pic_state),
+ VMSTATE_UINT32(enabled, mv88w8618_pic_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo mv88w8618_pic_info = {
+ .init = mv88w8618_pic_init,
+ .qdev.name = "mv88w8618_pic",
+ .qdev.size = sizeof(mv88w8618_pic_state),
+ .qdev.reset = mv88w8618_pic_reset,
+ .qdev.vmsd = &mv88w8618_pic_vmsd,
+};
+
+/* PIT register offsets */
+#define MP_PIT_TIMER1_LENGTH 0x00
+/* ... */
+#define MP_PIT_TIMER4_LENGTH 0x0C
+#define MP_PIT_CONTROL 0x10
+#define MP_PIT_TIMER1_VALUE 0x14
+/* ... */
+#define MP_PIT_TIMER4_VALUE 0x20
+#define MP_BOARD_RESET 0x34
+
+/* Magic board reset value (probably some watchdog behind it) */
+#define MP_BOARD_RESET_MAGIC 0x10000
+
+typedef struct mv88w8618_timer_state {
+ ptimer_state *ptimer;
+ uint32_t limit;
+ int freq;
+ qemu_irq irq;
+} mv88w8618_timer_state;
+
+typedef struct mv88w8618_pit_state {
+ SysBusDevice busdev;
+ mv88w8618_timer_state timer[4];
+} mv88w8618_pit_state;
+
+static void mv88w8618_timer_tick(void *opaque)
+{
+ mv88w8618_timer_state *s = opaque;
+
+ qemu_irq_raise(s->irq);
+}
+
+static void mv88w8618_timer_init(SysBusDevice *dev, mv88w8618_timer_state *s,
+ uint32_t freq)
+{
+ QEMUBH *bh;
+
+ sysbus_init_irq(dev, &s->irq);
+ s->freq = freq;
+
+ bh = qemu_bh_new(mv88w8618_timer_tick, s);
+ s->ptimer = ptimer_init(bh);
+}
+
+static uint32_t mv88w8618_pit_read(void *opaque, target_phys_addr_t offset)
+{
+ mv88w8618_pit_state *s = opaque;
+ mv88w8618_timer_state *t;
+
+ switch (offset) {
+ case MP_PIT_TIMER1_VALUE ... MP_PIT_TIMER4_VALUE:
+ t = &s->timer[(offset-MP_PIT_TIMER1_VALUE) >> 2];
+ return ptimer_get_count(t->ptimer);
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_pit_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mv88w8618_pit_state *s = opaque;
+ mv88w8618_timer_state *t;
+ int i;
+
+ switch (offset) {
+ case MP_PIT_TIMER1_LENGTH ... MP_PIT_TIMER4_LENGTH:
+ t = &s->timer[offset >> 2];
+ t->limit = value;
+ if (t->limit > 0) {
+ ptimer_set_limit(t->ptimer, t->limit, 1);
+ } else {
+ ptimer_stop(t->ptimer);
+ }
+ break;
+
+ case MP_PIT_CONTROL:
+ for (i = 0; i < 4; i++) {
+ t = &s->timer[i];
+ if (value & 0xf && t->limit > 0) {
+ ptimer_set_limit(t->ptimer, t->limit, 0);
+ ptimer_set_freq(t->ptimer, t->freq);
+ ptimer_run(t->ptimer, 0);
+ } else {
+ ptimer_stop(t->ptimer);
+ }
+ value >>= 4;
+ }
+ break;
+
+ case MP_BOARD_RESET:
+ if (value == MP_BOARD_RESET_MAGIC) {
+ qemu_system_reset_request();
+ }
+ break;
+ }
+}
+
+static void mv88w8618_pit_reset(DeviceState *d)
+{
+ mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state,
+ sysbus_from_qdev(d));
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ ptimer_stop(s->timer[i].ptimer);
+ s->timer[i].limit = 0;
+ }
+}
+
+static CPUReadMemoryFunc * const mv88w8618_pit_readfn[] = {
+ mv88w8618_pit_read,
+ mv88w8618_pit_read,
+ mv88w8618_pit_read
+};
+
+static CPUWriteMemoryFunc * const mv88w8618_pit_writefn[] = {
+ mv88w8618_pit_write,
+ mv88w8618_pit_write,
+ mv88w8618_pit_write
+};
+
+static int mv88w8618_pit_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ mv88w8618_pit_state *s = FROM_SYSBUS(mv88w8618_pit_state, dev);
+ int i;
+
+ /* Letting them all run at 1 MHz is likely just a pragmatic
+ * simplification. */
+ for (i = 0; i < 4; i++) {
+ mv88w8618_timer_init(dev, &s->timer[i], 1000000);
+ }
+
+ iomemtype = cpu_register_io_memory(mv88w8618_pit_readfn,
+ mv88w8618_pit_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_PIT_SIZE, iomemtype);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_timer_vmsd = {
+ .name = "timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(ptimer, mv88w8618_timer_state),
+ VMSTATE_UINT32(limit, mv88w8618_timer_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription mv88w8618_pit_vmsd = {
+ .name = "mv88w8618_pit",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(timer, mv88w8618_pit_state, 4, 1,
+ mv88w8618_timer_vmsd, mv88w8618_timer_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo mv88w8618_pit_info = {
+ .init = mv88w8618_pit_init,
+ .qdev.name = "mv88w8618_pit",
+ .qdev.size = sizeof(mv88w8618_pit_state),
+ .qdev.reset = mv88w8618_pit_reset,
+ .qdev.vmsd = &mv88w8618_pit_vmsd,
+};
+
+/* Flash config register offsets */
+#define MP_FLASHCFG_CFGR0 0x04
+
+typedef struct mv88w8618_flashcfg_state {
+ SysBusDevice busdev;
+ uint32_t cfgr0;
+} mv88w8618_flashcfg_state;
+
+static uint32_t mv88w8618_flashcfg_read(void *opaque,
+ target_phys_addr_t offset)
+{
+ mv88w8618_flashcfg_state *s = opaque;
+
+ switch (offset) {
+ case MP_FLASHCFG_CFGR0:
+ return s->cfgr0;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_flashcfg_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ mv88w8618_flashcfg_state *s = opaque;
+
+ switch (offset) {
+ case MP_FLASHCFG_CFGR0:
+ s->cfgr0 = value;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const mv88w8618_flashcfg_readfn[] = {
+ mv88w8618_flashcfg_read,
+ mv88w8618_flashcfg_read,
+ mv88w8618_flashcfg_read
+};
+
+static CPUWriteMemoryFunc * const mv88w8618_flashcfg_writefn[] = {
+ mv88w8618_flashcfg_write,
+ mv88w8618_flashcfg_write,
+ mv88w8618_flashcfg_write
+};
+
+static int mv88w8618_flashcfg_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ mv88w8618_flashcfg_state *s = FROM_SYSBUS(mv88w8618_flashcfg_state, dev);
+
+ s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */
+ iomemtype = cpu_register_io_memory(mv88w8618_flashcfg_readfn,
+ mv88w8618_flashcfg_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_FLASHCFG_SIZE, iomemtype);
+ return 0;
+}
+
+static const VMStateDescription mv88w8618_flashcfg_vmsd = {
+ .name = "mv88w8618_flashcfg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cfgr0, mv88w8618_flashcfg_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo mv88w8618_flashcfg_info = {
+ .init = mv88w8618_flashcfg_init,
+ .qdev.name = "mv88w8618_flashcfg",
+ .qdev.size = sizeof(mv88w8618_flashcfg_state),
+ .qdev.vmsd = &mv88w8618_flashcfg_vmsd,
+};
+
+/* Misc register offsets */
+#define MP_MISC_BOARD_REVISION 0x18
+
+#define MP_BOARD_REVISION 0x31
+
+static uint32_t musicpal_misc_read(void *opaque, target_phys_addr_t offset)
+{
+ switch (offset) {
+ case MP_MISC_BOARD_REVISION:
+ return MP_BOARD_REVISION;
+
+ default:
+ return 0;
+ }
+}
+
+static void musicpal_misc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+}
+
+static CPUReadMemoryFunc * const musicpal_misc_readfn[] = {
+ musicpal_misc_read,
+ musicpal_misc_read,
+ musicpal_misc_read,
+};
+
+static CPUWriteMemoryFunc * const musicpal_misc_writefn[] = {
+ musicpal_misc_write,
+ musicpal_misc_write,
+ musicpal_misc_write,
+};
+
+static void musicpal_misc_init(void)
+{
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(musicpal_misc_readfn,
+ musicpal_misc_writefn, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(MP_MISC_BASE, MP_MISC_SIZE, iomemtype);
+}
+
+/* WLAN register offsets */
+#define MP_WLAN_MAGIC1 0x11c
+#define MP_WLAN_MAGIC2 0x124
+
+static uint32_t mv88w8618_wlan_read(void *opaque, target_phys_addr_t offset)
+{
+ switch (offset) {
+ /* Workaround to allow loading the binary-only wlandrv.ko crap
+ * from the original Freecom firmware. */
+ case MP_WLAN_MAGIC1:
+ return ~3;
+ case MP_WLAN_MAGIC2:
+ return -1;
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_wlan_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+}
+
+static CPUReadMemoryFunc * const mv88w8618_wlan_readfn[] = {
+ mv88w8618_wlan_read,
+ mv88w8618_wlan_read,
+ mv88w8618_wlan_read,
+};
+
+static CPUWriteMemoryFunc * const mv88w8618_wlan_writefn[] = {
+ mv88w8618_wlan_write,
+ mv88w8618_wlan_write,
+ mv88w8618_wlan_write,
+};
+
+static int mv88w8618_wlan_init(SysBusDevice *dev)
+{
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(mv88w8618_wlan_readfn,
+ mv88w8618_wlan_writefn, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_WLAN_SIZE, iomemtype);
+ return 0;
+}
+
+/* GPIO register offsets */
+#define MP_GPIO_OE_LO 0x008
+#define MP_GPIO_OUT_LO 0x00c
+#define MP_GPIO_IN_LO 0x010
+#define MP_GPIO_IER_LO 0x014
+#define MP_GPIO_IMR_LO 0x018
+#define MP_GPIO_ISR_LO 0x020
+#define MP_GPIO_OE_HI 0x508
+#define MP_GPIO_OUT_HI 0x50c
+#define MP_GPIO_IN_HI 0x510
+#define MP_GPIO_IER_HI 0x514
+#define MP_GPIO_IMR_HI 0x518
+#define MP_GPIO_ISR_HI 0x520
+
+/* GPIO bits & masks */
+#define MP_GPIO_LCD_BRIGHTNESS 0x00070000
+#define MP_GPIO_I2C_DATA_BIT 29
+#define MP_GPIO_I2C_CLOCK_BIT 30
+
+/* LCD brightness bits in GPIO_OE_HI */
+#define MP_OE_LCD_BRIGHTNESS 0x0007
+
+typedef struct musicpal_gpio_state {
+ SysBusDevice busdev;
+ uint32_t lcd_brightness;
+ uint32_t out_state;
+ uint32_t in_state;
+ uint32_t ier;
+ uint32_t imr;
+ uint32_t isr;
+ qemu_irq irq;
+ qemu_irq out[5]; /* 3 brightness out + 2 lcd (data and clock ) */
+} musicpal_gpio_state;
+
+static void musicpal_gpio_brightness_update(musicpal_gpio_state *s) {
+ int i;
+ uint32_t brightness;
+
+ /* compute brightness ratio */
+ switch (s->lcd_brightness) {
+ case 0x00000007:
+ brightness = 0;
+ break;
+
+ case 0x00020000:
+ brightness = 1;
+ break;
+
+ case 0x00020001:
+ brightness = 2;
+ break;
+
+ case 0x00040000:
+ brightness = 3;
+ break;
+
+ case 0x00010006:
+ brightness = 4;
+ break;
+
+ case 0x00020005:
+ brightness = 5;
+ break;
+
+ case 0x00040003:
+ brightness = 6;
+ break;
+
+ case 0x00030004:
+ default:
+ brightness = 7;
+ }
+
+ /* set lcd brightness GPIOs */
+ for (i = 0; i <= 2; i++) {
+ qemu_set_irq(s->out[i], (brightness >> i) & 1);
+ }
+}
+
+static void musicpal_gpio_pin_event(void *opaque, int pin, int level)
+{
+ musicpal_gpio_state *s = opaque;
+ uint32_t mask = 1 << pin;
+ uint32_t delta = level << pin;
+ uint32_t old = s->in_state & mask;
+
+ s->in_state &= ~mask;
+ s->in_state |= delta;
+
+ if ((old ^ delta) &&
+ ((level && (s->imr & mask)) || (!level && (s->ier & mask)))) {
+ s->isr = mask;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static uint32_t musicpal_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ musicpal_gpio_state *s = opaque;
+
+ switch (offset) {
+ case MP_GPIO_OE_HI: /* used for LCD brightness control */
+ return s->lcd_brightness & MP_OE_LCD_BRIGHTNESS;
+
+ case MP_GPIO_OUT_LO:
+ return s->out_state & 0xFFFF;
+ case MP_GPIO_OUT_HI:
+ return s->out_state >> 16;
+
+ case MP_GPIO_IN_LO:
+ return s->in_state & 0xFFFF;
+ case MP_GPIO_IN_HI:
+ return s->in_state >> 16;
+
+ case MP_GPIO_IER_LO:
+ return s->ier & 0xFFFF;
+ case MP_GPIO_IER_HI:
+ return s->ier >> 16;
+
+ case MP_GPIO_IMR_LO:
+ return s->imr & 0xFFFF;
+ case MP_GPIO_IMR_HI:
+ return s->imr >> 16;
+
+ case MP_GPIO_ISR_LO:
+ return s->isr & 0xFFFF;
+ case MP_GPIO_ISR_HI:
+ return s->isr >> 16;
+
+ default:
+ return 0;
+ }
+}
+
+static void musicpal_gpio_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ musicpal_gpio_state *s = opaque;
+ switch (offset) {
+ case MP_GPIO_OE_HI: /* used for LCD brightness control */
+ s->lcd_brightness = (s->lcd_brightness & MP_GPIO_LCD_BRIGHTNESS) |
+ (value & MP_OE_LCD_BRIGHTNESS);
+ musicpal_gpio_brightness_update(s);
+ break;
+
+ case MP_GPIO_OUT_LO:
+ s->out_state = (s->out_state & 0xFFFF0000) | (value & 0xFFFF);
+ break;
+ case MP_GPIO_OUT_HI:
+ s->out_state = (s->out_state & 0xFFFF) | (value << 16);
+ s->lcd_brightness = (s->lcd_brightness & 0xFFFF) |
+ (s->out_state & MP_GPIO_LCD_BRIGHTNESS);
+ musicpal_gpio_brightness_update(s);
+ qemu_set_irq(s->out[3], (s->out_state >> MP_GPIO_I2C_DATA_BIT) & 1);
+ qemu_set_irq(s->out[4], (s->out_state >> MP_GPIO_I2C_CLOCK_BIT) & 1);
+ break;
+
+ case MP_GPIO_IER_LO:
+ s->ier = (s->ier & 0xFFFF0000) | (value & 0xFFFF);
+ break;
+ case MP_GPIO_IER_HI:
+ s->ier = (s->ier & 0xFFFF) | (value << 16);
+ break;
+
+ case MP_GPIO_IMR_LO:
+ s->imr = (s->imr & 0xFFFF0000) | (value & 0xFFFF);
+ break;
+ case MP_GPIO_IMR_HI:
+ s->imr = (s->imr & 0xFFFF) | (value << 16);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const musicpal_gpio_readfn[] = {
+ musicpal_gpio_read,
+ musicpal_gpio_read,
+ musicpal_gpio_read,
+};
+
+static CPUWriteMemoryFunc * const musicpal_gpio_writefn[] = {
+ musicpal_gpio_write,
+ musicpal_gpio_write,
+ musicpal_gpio_write,
+};
+
+static void musicpal_gpio_reset(DeviceState *d)
+{
+ musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state,
+ sysbus_from_qdev(d));
+
+ s->lcd_brightness = 0;
+ s->out_state = 0;
+ s->in_state = 0xffffffff;
+ s->ier = 0;
+ s->imr = 0;
+ s->isr = 0;
+}
+
+static int musicpal_gpio_init(SysBusDevice *dev)
+{
+ musicpal_gpio_state *s = FROM_SYSBUS(musicpal_gpio_state, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ iomemtype = cpu_register_io_memory(musicpal_gpio_readfn,
+ musicpal_gpio_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MP_GPIO_SIZE, iomemtype);
+
+ qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out));
+
+ qdev_init_gpio_in(&dev->qdev, musicpal_gpio_pin_event, 32);
+
+ return 0;
+}
+
+static const VMStateDescription musicpal_gpio_vmsd = {
+ .name = "musicpal_gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(lcd_brightness, musicpal_gpio_state),
+ VMSTATE_UINT32(out_state, musicpal_gpio_state),
+ VMSTATE_UINT32(in_state, musicpal_gpio_state),
+ VMSTATE_UINT32(ier, musicpal_gpio_state),
+ VMSTATE_UINT32(imr, musicpal_gpio_state),
+ VMSTATE_UINT32(isr, musicpal_gpio_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo musicpal_gpio_info = {
+ .init = musicpal_gpio_init,
+ .qdev.name = "musicpal_gpio",
+ .qdev.size = sizeof(musicpal_gpio_state),
+ .qdev.reset = musicpal_gpio_reset,
+ .qdev.vmsd = &musicpal_gpio_vmsd,
+};
+
+/* Keyboard codes & masks */
+#define KEY_RELEASED 0x80
+#define KEY_CODE 0x7f
+
+#define KEYCODE_TAB 0x0f
+#define KEYCODE_ENTER 0x1c
+#define KEYCODE_F 0x21
+#define KEYCODE_M 0x32
+
+#define KEYCODE_EXTENDED 0xe0
+#define KEYCODE_UP 0x48
+#define KEYCODE_DOWN 0x50
+#define KEYCODE_LEFT 0x4b
+#define KEYCODE_RIGHT 0x4d
+
+#define MP_KEY_WHEEL_VOL (1 << 0)
+#define MP_KEY_WHEEL_VOL_INV (1 << 1)
+#define MP_KEY_WHEEL_NAV (1 << 2)
+#define MP_KEY_WHEEL_NAV_INV (1 << 3)
+#define MP_KEY_BTN_FAVORITS (1 << 4)
+#define MP_KEY_BTN_MENU (1 << 5)
+#define MP_KEY_BTN_VOLUME (1 << 6)
+#define MP_KEY_BTN_NAVIGATION (1 << 7)
+
+typedef struct musicpal_key_state {
+ SysBusDevice busdev;
+ uint32_t kbd_extended;
+ uint32_t pressed_keys;
+ qemu_irq out[8];
+} musicpal_key_state;
+
+static void musicpal_key_event(void *opaque, int keycode)
+{
+ musicpal_key_state *s = opaque;
+ uint32_t event = 0;
+ int i;
+
+ if (keycode == KEYCODE_EXTENDED) {
+ s->kbd_extended = 1;
+ return;
+ }
+
+ if (s->kbd_extended) {
+ switch (keycode & KEY_CODE) {
+ case KEYCODE_UP:
+ event = MP_KEY_WHEEL_NAV | MP_KEY_WHEEL_NAV_INV;
+ break;
+
+ case KEYCODE_DOWN:
+ event = MP_KEY_WHEEL_NAV;
+ break;
+
+ case KEYCODE_LEFT:
+ event = MP_KEY_WHEEL_VOL | MP_KEY_WHEEL_VOL_INV;
+ break;
+
+ case KEYCODE_RIGHT:
+ event = MP_KEY_WHEEL_VOL;
+ break;
+ }
+ } else {
+ switch (keycode & KEY_CODE) {
+ case KEYCODE_F:
+ event = MP_KEY_BTN_FAVORITS;
+ break;
+
+ case KEYCODE_TAB:
+ event = MP_KEY_BTN_VOLUME;
+ break;
+
+ case KEYCODE_ENTER:
+ event = MP_KEY_BTN_NAVIGATION;
+ break;
+
+ case KEYCODE_M:
+ event = MP_KEY_BTN_MENU;
+ break;
+ }
+ /* Do not repeat already pressed buttons */
+ if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
+ event = 0;
+ }
+ }
+
+ if (event) {
+ /* Raise GPIO pin first if repeating a key */
+ if (!(keycode & KEY_RELEASED) && (s->pressed_keys & event)) {
+ for (i = 0; i <= 7; i++) {
+ if (event & (1 << i)) {
+ qemu_set_irq(s->out[i], 1);
+ }
+ }
+ }
+ for (i = 0; i <= 7; i++) {
+ if (event & (1 << i)) {
+ qemu_set_irq(s->out[i], !!(keycode & KEY_RELEASED));
+ }
+ }
+ if (keycode & KEY_RELEASED) {
+ s->pressed_keys &= ~event;
+ } else {
+ s->pressed_keys |= event;
+ }
+ }
+
+ s->kbd_extended = 0;
+}
+
+static int musicpal_key_init(SysBusDevice *dev)
+{
+ musicpal_key_state *s = FROM_SYSBUS(musicpal_key_state, dev);
+
+ sysbus_init_mmio(dev, 0x0, 0);
+
+ s->kbd_extended = 0;
+ s->pressed_keys = 0;
+
+ qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out));
+
+ qemu_add_kbd_event_handler(musicpal_key_event, s);
+
+ return 0;
+}
+
+static const VMStateDescription musicpal_key_vmsd = {
+ .name = "musicpal_key",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(kbd_extended, musicpal_key_state),
+ VMSTATE_UINT32(pressed_keys, musicpal_key_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo musicpal_key_info = {
+ .init = musicpal_key_init,
+ .qdev.name = "musicpal_key",
+ .qdev.size = sizeof(musicpal_key_state),
+ .qdev.vmsd = &musicpal_key_vmsd,
+};
+
+static struct arm_boot_info musicpal_binfo = {
+ .loader_start = 0x0,
+ .board_id = 0x20e,
+};
+
+static void musicpal_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ qemu_irq *cpu_pic;
+ qemu_irq pic[32];
+ DeviceState *dev;
+ DeviceState *i2c_dev;
+ DeviceState *lcd_dev;
+ DeviceState *key_dev;
+ DeviceState *wm8750_dev;
+ SysBusDevice *s;
+ i2c_bus *i2c;
+ int i;
+ unsigned long flash_size;
+ DriveInfo *dinfo;
+ ram_addr_t sram_off;
+
+ if (!cpu_model) {
+ cpu_model = "arm926";
+ }
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ cpu_pic = arm_pic_init_cpu(env);
+
+ /* For now we use a fixed - the original - RAM size */
+ cpu_register_physical_memory(0, MP_RAM_DEFAULT_SIZE,
+ qemu_ram_alloc(NULL, "musicpal.ram",
+ MP_RAM_DEFAULT_SIZE));
+
+ sram_off = qemu_ram_alloc(NULL, "musicpal.sram", MP_SRAM_SIZE);
+ cpu_register_physical_memory(MP_SRAM_BASE, MP_SRAM_SIZE, sram_off);
+
+ dev = sysbus_create_simple("mv88w8618_pic", MP_PIC_BASE,
+ cpu_pic[ARM_PIC_CPU_IRQ]);
+ for (i = 0; i < 32; i++) {
+ pic[i] = qdev_get_gpio_in(dev, i);
+ }
+ sysbus_create_varargs("mv88w8618_pit", MP_PIT_BASE, pic[MP_TIMER1_IRQ],
+ pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ],
+ pic[MP_TIMER4_IRQ], NULL);
+
+ if (serial_hds[0]) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 1825000,
+ serial_hds[0], 1, 1);
+#else
+ serial_mm_init(MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 1825000,
+ serial_hds[0], 1, 0);
+#endif
+ }
+ if (serial_hds[1]) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 1825000,
+ serial_hds[1], 1, 1);
+#else
+ serial_mm_init(MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 1825000,
+ serial_hds[1], 1, 0);
+#endif
+ }
+
+ /* Register flash */
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ if (dinfo) {
+ flash_size = bdrv_getlength(dinfo->bdrv);
+ if (flash_size != 8*1024*1024 && flash_size != 16*1024*1024 &&
+ flash_size != 32*1024*1024) {
+ fprintf(stderr, "Invalid flash image size\n");
+ exit(1);
+ }
+
+ /*
+ * The original U-Boot accesses the flash at 0xFE000000 instead of
+ * 0xFF800000 (if there is 8 MB flash). So remap flash access if the
+ * image is smaller than 32 MB.
+ */
+#ifdef TARGET_WORDS_BIGENDIAN
+ pflash_cfi02_register(0-MP_FLASH_SIZE_MAX, qemu_ram_alloc(NULL,
+ "musicpal.flash", flash_size),
+ dinfo->bdrv, 0x10000,
+ (flash_size + 0xffff) >> 16,
+ MP_FLASH_SIZE_MAX / flash_size,
+ 2, 0x00BF, 0x236D, 0x0000, 0x0000,
+ 0x5555, 0x2AAA, 1);
+#else
+ pflash_cfi02_register(0-MP_FLASH_SIZE_MAX, qemu_ram_alloc(NULL,
+ "musicpal.flash", flash_size),
+ dinfo->bdrv, 0x10000,
+ (flash_size + 0xffff) >> 16,
+ MP_FLASH_SIZE_MAX / flash_size,
+ 2, 0x00BF, 0x236D, 0x0000, 0x0000,
+ 0x5555, 0x2AAA, 0);
+#endif
+
+ }
+ sysbus_create_simple("mv88w8618_flashcfg", MP_FLASHCFG_BASE, NULL);
+
+ qemu_check_nic_model(&nd_table[0], "mv88w8618");
+ dev = qdev_create(NULL, "mv88w8618_eth");
+ qdev_set_nic_properties(dev, &nd_table[0]);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, MP_ETH_BASE);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[MP_ETH_IRQ]);
+
+ sysbus_create_simple("mv88w8618_wlan", MP_WLAN_BASE, NULL);
+
+ musicpal_misc_init();
+
+ dev = sysbus_create_simple("musicpal_gpio", MP_GPIO_BASE, pic[MP_GPIO_IRQ]);
+ i2c_dev = sysbus_create_simple("gpio_i2c", 0, NULL);
+ i2c = (i2c_bus *)qdev_get_child_bus(i2c_dev, "i2c");
+
+ lcd_dev = sysbus_create_simple("musicpal_lcd", MP_LCD_BASE, NULL);
+ key_dev = sysbus_create_simple("musicpal_key", 0, NULL);
+
+ /* I2C read data */
+ qdev_connect_gpio_out(i2c_dev, 0,
+ qdev_get_gpio_in(dev, MP_GPIO_I2C_DATA_BIT));
+ /* I2C data */
+ qdev_connect_gpio_out(dev, 3, qdev_get_gpio_in(i2c_dev, 0));
+ /* I2C clock */
+ qdev_connect_gpio_out(dev, 4, qdev_get_gpio_in(i2c_dev, 1));
+
+ for (i = 0; i < 3; i++) {
+ qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(lcd_dev, i));
+ }
+ for (i = 0; i < 4; i++) {
+ qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 8));
+ }
+ for (i = 4; i < 8; i++) {
+ qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15));
+ }
+
+ wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR);
+ dev = qdev_create(NULL, "mv88w8618_audio");
+ s = sysbus_from_qdev(dev);
+ qdev_prop_set_ptr(dev, "wm8750", wm8750_dev);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(s, 0, MP_AUDIO_BASE);
+ sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]);
+
+ musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE;
+ musicpal_binfo.kernel_filename = kernel_filename;
+ musicpal_binfo.kernel_cmdline = kernel_cmdline;
+ musicpal_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(env, &musicpal_binfo);
+}
+
+static QEMUMachine musicpal_machine = {
+ .name = "musicpal",
+ .desc = "Marvell 88w8618 / MusicPal (ARM926EJ-S)",
+ .init = musicpal_init,
+};
+
+static void musicpal_machine_init(void)
+{
+ qemu_register_machine(&musicpal_machine);
+}
+
+machine_init(musicpal_machine_init);
+
+static void musicpal_register_devices(void)
+{
+ sysbus_register_withprop(&mv88w8618_pic_info);
+ sysbus_register_withprop(&mv88w8618_pit_info);
+ sysbus_register_withprop(&mv88w8618_flashcfg_info);
+ sysbus_register_withprop(&mv88w8618_eth_info);
+ sysbus_register_dev("mv88w8618_wlan", sizeof(SysBusDevice),
+ mv88w8618_wlan_init);
+ sysbus_register_withprop(&musicpal_lcd_info);
+ sysbus_register_withprop(&musicpal_gpio_info);
+ sysbus_register_withprop(&musicpal_key_info);
+}
+
+device_init(musicpal_register_devices)
diff --git a/hw/nand.c b/hw/nand.c
new file mode 100644
index 0000000..f414aa1
--- /dev/null
+++ b/hw/nand.c
@@ -0,0 +1,661 @@
+/*
+ * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash
+ * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from
+ * Samsung Electronic.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#ifndef NAND_IO
+
+# include "hw.h"
+# include "flash.h"
+# include "blockdev.h"
+/* FIXME: Pass block device as an argument. */
+
+# define NAND_CMD_READ0 0x00
+# define NAND_CMD_READ1 0x01
+# define NAND_CMD_READ2 0x50
+# define NAND_CMD_LPREAD2 0x30
+# define NAND_CMD_NOSERIALREAD2 0x35
+# define NAND_CMD_RANDOMREAD1 0x05
+# define NAND_CMD_RANDOMREAD2 0xe0
+# define NAND_CMD_READID 0x90
+# define NAND_CMD_RESET 0xff
+# define NAND_CMD_PAGEPROGRAM1 0x80
+# define NAND_CMD_PAGEPROGRAM2 0x10
+# define NAND_CMD_CACHEPROGRAM2 0x15
+# define NAND_CMD_BLOCKERASE1 0x60
+# define NAND_CMD_BLOCKERASE2 0xd0
+# define NAND_CMD_READSTATUS 0x70
+# define NAND_CMD_COPYBACKPRG1 0x85
+
+# define NAND_IOSTATUS_ERROR (1 << 0)
+# define NAND_IOSTATUS_PLANE0 (1 << 1)
+# define NAND_IOSTATUS_PLANE1 (1 << 2)
+# define NAND_IOSTATUS_PLANE2 (1 << 3)
+# define NAND_IOSTATUS_PLANE3 (1 << 4)
+# define NAND_IOSTATUS_BUSY (1 << 6)
+# define NAND_IOSTATUS_UNPROTCT (1 << 7)
+
+# define MAX_PAGE 0x800
+# define MAX_OOB 0x40
+
+struct NANDFlashState {
+ uint8_t manf_id, chip_id;
+ int size, pages;
+ int page_shift, oob_shift, erase_shift, addr_shift;
+ uint8_t *storage;
+ BlockDriverState *bdrv;
+ int mem_oob;
+
+ int cle, ale, ce, wp, gnd;
+
+ uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
+ uint8_t *ioaddr;
+ int iolen;
+
+ uint32_t cmd, addr;
+ int addrlen;
+ int status;
+ int offset;
+
+ void (*blk_write)(NANDFlashState *s);
+ void (*blk_erase)(NANDFlashState *s);
+ void (*blk_load)(NANDFlashState *s, uint32_t addr, int offset);
+};
+
+# define NAND_NO_AUTOINCR 0x00000001
+# define NAND_BUSWIDTH_16 0x00000002
+# define NAND_NO_PADDING 0x00000004
+# define NAND_CACHEPRG 0x00000008
+# define NAND_COPYBACK 0x00000010
+# define NAND_IS_AND 0x00000020
+# define NAND_4PAGE_ARRAY 0x00000040
+# define NAND_NO_READRDY 0x00000100
+# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK)
+
+# define NAND_IO
+
+# define PAGE(addr) ((addr) >> ADDR_SHIFT)
+# define PAGE_START(page) (PAGE(page) * (PAGE_SIZE + OOB_SIZE))
+# define PAGE_MASK ((1 << ADDR_SHIFT) - 1)
+# define OOB_SHIFT (PAGE_SHIFT - 5)
+# define OOB_SIZE (1 << OOB_SHIFT)
+# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT))
+# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8))
+
+# define PAGE_SIZE 256
+# define PAGE_SHIFT 8
+# define PAGE_SECTORS 1
+# define ADDR_SHIFT 8
+# include "nand.c"
+# define PAGE_SIZE 512
+# define PAGE_SHIFT 9
+# define PAGE_SECTORS 1
+# define ADDR_SHIFT 8
+# include "nand.c"
+# define PAGE_SIZE 2048
+# define PAGE_SHIFT 11
+# define PAGE_SECTORS 4
+# define ADDR_SHIFT 16
+# include "nand.c"
+
+/* Information based on Linux drivers/mtd/nand/nand_ids.c */
+static const struct {
+ int size;
+ int width;
+ int page_shift;
+ int erase_shift;
+ uint32_t options;
+} nand_flash_ids[0x100] = {
+ [0 ... 0xff] = { 0 },
+
+ [0x6e] = { 1, 8, 8, 4, 0 },
+ [0x64] = { 2, 8, 8, 4, 0 },
+ [0x6b] = { 4, 8, 9, 4, 0 },
+ [0xe8] = { 1, 8, 8, 4, 0 },
+ [0xec] = { 1, 8, 8, 4, 0 },
+ [0xea] = { 2, 8, 8, 4, 0 },
+ [0xd5] = { 4, 8, 9, 4, 0 },
+ [0xe3] = { 4, 8, 9, 4, 0 },
+ [0xe5] = { 4, 8, 9, 4, 0 },
+ [0xd6] = { 8, 8, 9, 4, 0 },
+
+ [0x39] = { 8, 8, 9, 4, 0 },
+ [0xe6] = { 8, 8, 9, 4, 0 },
+ [0x49] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 },
+ [0x59] = { 8, 16, 9, 4, NAND_BUSWIDTH_16 },
+
+ [0x33] = { 16, 8, 9, 5, 0 },
+ [0x73] = { 16, 8, 9, 5, 0 },
+ [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 },
+ [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 },
+
+ [0x35] = { 32, 8, 9, 5, 0 },
+ [0x75] = { 32, 8, 9, 5, 0 },
+ [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 },
+ [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 },
+
+ [0x36] = { 64, 8, 9, 5, 0 },
+ [0x76] = { 64, 8, 9, 5, 0 },
+ [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 },
+ [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 },
+
+ [0x78] = { 128, 8, 9, 5, 0 },
+ [0x39] = { 128, 8, 9, 5, 0 },
+ [0x79] = { 128, 8, 9, 5, 0 },
+ [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
+ [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
+ [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
+ [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 },
+
+ [0x71] = { 256, 8, 9, 5, 0 },
+
+ /*
+ * These are the new chips with large page size. The pagesize and the
+ * erasesize is determined from the extended id bytes
+ */
+# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR)
+# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
+
+ /* 512 Megabit */
+ [0xa2] = { 64, 8, 0, 0, LP_OPTIONS },
+ [0xf2] = { 64, 8, 0, 0, LP_OPTIONS },
+ [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 },
+ [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 },
+
+ /* 1 Gigabit */
+ [0xa1] = { 128, 8, 0, 0, LP_OPTIONS },
+ [0xf1] = { 128, 8, 0, 0, LP_OPTIONS },
+ [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 },
+ [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 },
+
+ /* 2 Gigabit */
+ [0xaa] = { 256, 8, 0, 0, LP_OPTIONS },
+ [0xda] = { 256, 8, 0, 0, LP_OPTIONS },
+ [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 },
+ [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 },
+
+ /* 4 Gigabit */
+ [0xac] = { 512, 8, 0, 0, LP_OPTIONS },
+ [0xdc] = { 512, 8, 0, 0, LP_OPTIONS },
+ [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 },
+ [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 },
+
+ /* 8 Gigabit */
+ [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS },
+ [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS },
+ [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 },
+ [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 },
+
+ /* 16 Gigabit */
+ [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS },
+ [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS },
+ [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 },
+ [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 },
+};
+
+static void nand_reset(NANDFlashState *s)
+{
+ s->cmd = NAND_CMD_READ0;
+ s->addr = 0;
+ s->addrlen = 0;
+ s->iolen = 0;
+ s->offset = 0;
+ s->status &= NAND_IOSTATUS_UNPROTCT;
+}
+
+static void nand_command(NANDFlashState *s)
+{
+ unsigned int offset;
+ switch (s->cmd) {
+ case NAND_CMD_READ0:
+ s->iolen = 0;
+ break;
+
+ case NAND_CMD_READID:
+ s->io[0] = s->manf_id;
+ s->io[1] = s->chip_id;
+ s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */
+ if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+ s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */
+ else
+ s->io[3] = 0xc0; /* Multi-plane */
+ s->ioaddr = s->io;
+ s->iolen = 4;
+ break;
+
+ case NAND_CMD_RANDOMREAD2:
+ case NAND_CMD_NOSERIALREAD2:
+ if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
+ break;
+ offset = s->addr & ((1 << s->addr_shift) - 1);
+ s->blk_load(s, s->addr, offset);
+ if (s->gnd)
+ s->iolen = (1 << s->page_shift) - offset;
+ else
+ s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
+ break;
+
+ case NAND_CMD_RESET:
+ nand_reset(s);
+ break;
+
+ case NAND_CMD_PAGEPROGRAM1:
+ s->ioaddr = s->io;
+ s->iolen = 0;
+ break;
+
+ case NAND_CMD_PAGEPROGRAM2:
+ if (s->wp) {
+ s->blk_write(s);
+ }
+ break;
+
+ case NAND_CMD_BLOCKERASE1:
+ break;
+
+ case NAND_CMD_BLOCKERASE2:
+ if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+ s->addr <<= 16;
+ else
+ s->addr <<= 8;
+
+ if (s->wp) {
+ s->blk_erase(s);
+ }
+ break;
+
+ case NAND_CMD_READSTATUS:
+ s->io[0] = s->status;
+ s->ioaddr = s->io;
+ s->iolen = 1;
+ break;
+
+ default:
+ printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd);
+ }
+}
+
+static void nand_save(QEMUFile *f, void *opaque)
+{
+ NANDFlashState *s = (NANDFlashState *) opaque;
+ qemu_put_byte(f, s->cle);
+ qemu_put_byte(f, s->ale);
+ qemu_put_byte(f, s->ce);
+ qemu_put_byte(f, s->wp);
+ qemu_put_byte(f, s->gnd);
+ qemu_put_buffer(f, s->io, sizeof(s->io));
+ qemu_put_be32(f, s->ioaddr - s->io);
+ qemu_put_be32(f, s->iolen);
+
+ qemu_put_be32s(f, &s->cmd);
+ qemu_put_be32s(f, &s->addr);
+ qemu_put_be32(f, s->addrlen);
+ qemu_put_be32(f, s->status);
+ qemu_put_be32(f, s->offset);
+ /* XXX: do we want to save s->storage too? */
+}
+
+static int nand_load(QEMUFile *f, void *opaque, int version_id)
+{
+ NANDFlashState *s = (NANDFlashState *) opaque;
+ s->cle = qemu_get_byte(f);
+ s->ale = qemu_get_byte(f);
+ s->ce = qemu_get_byte(f);
+ s->wp = qemu_get_byte(f);
+ s->gnd = qemu_get_byte(f);
+ qemu_get_buffer(f, s->io, sizeof(s->io));
+ s->ioaddr = s->io + qemu_get_be32(f);
+ s->iolen = qemu_get_be32(f);
+ if (s->ioaddr >= s->io + sizeof(s->io) || s->ioaddr < s->io)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->cmd);
+ qemu_get_be32s(f, &s->addr);
+ s->addrlen = qemu_get_be32(f);
+ s->status = qemu_get_be32(f);
+ s->offset = qemu_get_be32(f);
+ return 0;
+}
+
+/*
+ * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip
+ * outputs are R/B and eight I/O pins.
+ *
+ * CE, WP and R/B are active low.
+ */
+void nand_setpins(NANDFlashState *s,
+ int cle, int ale, int ce, int wp, int gnd)
+{
+ s->cle = cle;
+ s->ale = ale;
+ s->ce = ce;
+ s->wp = wp;
+ s->gnd = gnd;
+ if (wp)
+ s->status |= NAND_IOSTATUS_UNPROTCT;
+ else
+ s->status &= ~NAND_IOSTATUS_UNPROTCT;
+}
+
+void nand_getpins(NANDFlashState *s, int *rb)
+{
+ *rb = 1;
+}
+
+void nand_setio(NANDFlashState *s, uint8_t value)
+{
+ if (!s->ce && s->cle) {
+ if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+ if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
+ return;
+ if (value == NAND_CMD_RANDOMREAD1) {
+ s->addr &= ~((1 << s->addr_shift) - 1);
+ s->addrlen = 0;
+ return;
+ }
+ }
+ if (value == NAND_CMD_READ0)
+ s->offset = 0;
+ else if (value == NAND_CMD_READ1) {
+ s->offset = 0x100;
+ value = NAND_CMD_READ0;
+ }
+ else if (value == NAND_CMD_READ2) {
+ s->offset = 1 << s->page_shift;
+ value = NAND_CMD_READ0;
+ }
+
+ s->cmd = value;
+
+ if (s->cmd == NAND_CMD_READSTATUS ||
+ s->cmd == NAND_CMD_PAGEPROGRAM2 ||
+ s->cmd == NAND_CMD_BLOCKERASE1 ||
+ s->cmd == NAND_CMD_BLOCKERASE2 ||
+ s->cmd == NAND_CMD_NOSERIALREAD2 ||
+ s->cmd == NAND_CMD_RANDOMREAD2 ||
+ s->cmd == NAND_CMD_RESET)
+ nand_command(s);
+
+ if (s->cmd != NAND_CMD_RANDOMREAD2) {
+ s->addrlen = 0;
+ }
+ }
+
+ if (s->ale) {
+ unsigned int shift = s->addrlen * 8;
+ unsigned int mask = ~(0xff << shift);
+ unsigned int v = value << shift;
+
+ s->addr = (s->addr & mask) | v;
+ s->addrlen ++;
+
+ if (s->addrlen == 1 && s->cmd == NAND_CMD_READID)
+ nand_command(s);
+
+ if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+ s->addrlen == 3 && (
+ s->cmd == NAND_CMD_READ0 ||
+ s->cmd == NAND_CMD_PAGEPROGRAM1))
+ nand_command(s);
+ if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+ s->addrlen == 4 && (
+ s->cmd == NAND_CMD_READ0 ||
+ s->cmd == NAND_CMD_PAGEPROGRAM1))
+ nand_command(s);
+ }
+
+ if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
+ if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift))
+ s->io[s->iolen ++] = value;
+ } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
+ if ((s->addr & ((1 << s->addr_shift) - 1)) <
+ (1 << s->page_shift) + (1 << s->oob_shift)) {
+ s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value;
+ s->addr ++;
+ }
+ }
+}
+
+uint8_t nand_getio(NANDFlashState *s)
+{
+ int offset;
+
+ /* Allow sequential reading */
+ if (!s->iolen && s->cmd == NAND_CMD_READ0) {
+ offset = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
+ s->offset = 0;
+
+ s->blk_load(s, s->addr, offset);
+ if (s->gnd)
+ s->iolen = (1 << s->page_shift) - offset;
+ else
+ s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
+ }
+
+ if (s->ce || s->iolen <= 0)
+ return 0;
+
+ s->iolen --;
+ s->addr++;
+ return *(s->ioaddr ++);
+}
+
+NANDFlashState *nand_init(int manf_id, int chip_id)
+{
+ int pagesize;
+ NANDFlashState *s;
+ DriveInfo *dinfo;
+
+ if (nand_flash_ids[chip_id].size == 0) {
+ hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__);
+ }
+
+ s = (NANDFlashState *) qemu_mallocz(sizeof(NANDFlashState));
+ dinfo = drive_get(IF_MTD, 0, 0);
+ if (dinfo)
+ s->bdrv = dinfo->bdrv;
+ s->manf_id = manf_id;
+ s->chip_id = chip_id;
+ s->size = nand_flash_ids[s->chip_id].size << 20;
+ if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+ s->page_shift = 11;
+ s->erase_shift = 6;
+ } else {
+ s->page_shift = nand_flash_ids[s->chip_id].page_shift;
+ s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
+ }
+
+ switch (1 << s->page_shift) {
+ case 256:
+ nand_init_256(s);
+ break;
+ case 512:
+ nand_init_512(s);
+ break;
+ case 2048:
+ nand_init_2048(s);
+ break;
+ default:
+ hw_error("%s: Unsupported NAND block size.\n", __FUNCTION__);
+ }
+
+ pagesize = 1 << s->oob_shift;
+ s->mem_oob = 1;
+ if (s->bdrv && bdrv_getlength(s->bdrv) >=
+ (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
+ pagesize = 0;
+ s->mem_oob = 0;
+ }
+
+ if (!s->bdrv)
+ pagesize += 1 << s->page_shift;
+ if (pagesize)
+ s->storage = (uint8_t *) memset(qemu_malloc(s->pages * pagesize),
+ 0xff, s->pages * pagesize);
+ /* Give s->ioaddr a sane value in case we save state before it
+ is used. */
+ s->ioaddr = s->io;
+
+ register_savevm(NULL, "nand", -1, 0, nand_save, nand_load, s);
+
+ return s;
+}
+
+void nand_done(NANDFlashState *s)
+{
+ if (s->bdrv) {
+ bdrv_close(s->bdrv);
+ bdrv_delete(s->bdrv);
+ }
+
+ if (!s->bdrv || s->mem_oob)
+ qemu_free(s->storage);
+
+ qemu_free(s);
+}
+
+#else
+
+/* Program a single page */
+static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
+{
+ uint32_t off, page, sector, soff;
+ uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
+ if (PAGE(s->addr) >= s->pages)
+ return;
+
+ if (!s->bdrv) {
+ memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
+ s->offset, s->io, s->iolen);
+ } else if (s->mem_oob) {
+ sector = SECTOR(s->addr);
+ off = (s->addr & PAGE_MASK) + s->offset;
+ soff = SECTOR_OFFSET(s->addr);
+ if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1) {
+ printf("%s: read error in sector %i\n", __FUNCTION__, sector);
+ return;
+ }
+
+ memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
+ if (off + s->iolen > PAGE_SIZE) {
+ page = PAGE(s->addr);
+ memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
+ MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
+ }
+
+ if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) == -1)
+ printf("%s: write error in sector %i\n", __FUNCTION__, sector);
+ } else {
+ off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
+ sector = off >> 9;
+ soff = off & 0x1ff;
+ if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) {
+ printf("%s: read error in sector %i\n", __FUNCTION__, sector);
+ return;
+ }
+
+ memcpy(iobuf + soff, s->io, s->iolen);
+
+ if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1)
+ printf("%s: write error in sector %i\n", __FUNCTION__, sector);
+ }
+ s->offset = 0;
+}
+
+/* Erase a single block */
+static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
+{
+ uint32_t i, page, addr;
+ uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
+ addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
+
+ if (PAGE(addr) >= s->pages)
+ return;
+
+ if (!s->bdrv) {
+ memset(s->storage + PAGE_START(addr),
+ 0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift);
+ } else if (s->mem_oob) {
+ memset(s->storage + (PAGE(addr) << OOB_SHIFT),
+ 0xff, OOB_SIZE << s->erase_shift);
+ i = SECTOR(addr);
+ page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
+ for (; i < page; i ++)
+ if (bdrv_write(s->bdrv, i, iobuf, 1) == -1)
+ printf("%s: write error in sector %i\n", __FUNCTION__, i);
+ } else {
+ addr = PAGE_START(addr);
+ page = addr >> 9;
+ if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
+ printf("%s: read error in sector %i\n", __FUNCTION__, page);
+ memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
+ if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
+ printf("%s: write error in sector %i\n", __FUNCTION__, page);
+
+ memset(iobuf, 0xff, 0x200);
+ i = (addr & ~0x1ff) + 0x200;
+ for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
+ i < addr; i += 0x200)
+ if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) == -1)
+ printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9);
+
+ page = i >> 9;
+ if (bdrv_read(s->bdrv, page, iobuf, 1) == -1)
+ printf("%s: read error in sector %i\n", __FUNCTION__, page);
+ memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
+ if (bdrv_write(s->bdrv, page, iobuf, 1) == -1)
+ printf("%s: write error in sector %i\n", __FUNCTION__, page);
+ }
+}
+
+static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
+ uint32_t addr, int offset)
+{
+ if (PAGE(addr) >= s->pages)
+ return;
+
+ if (s->bdrv) {
+ if (s->mem_oob) {
+ if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1)
+ printf("%s: read error in sector %i\n",
+ __FUNCTION__, SECTOR(addr));
+ memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
+ s->storage + (PAGE(s->addr) << OOB_SHIFT),
+ OOB_SIZE);
+ s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
+ } else {
+ if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
+ s->io, (PAGE_SECTORS + 2)) == -1)
+ printf("%s: read error in sector %i\n",
+ __FUNCTION__, PAGE_START(addr) >> 9);
+ s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
+ }
+ } else {
+ memcpy(s->io, s->storage + PAGE_START(s->addr) +
+ offset, PAGE_SIZE + OOB_SIZE - offset);
+ s->ioaddr = s->io;
+ }
+}
+
+static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s)
+{
+ s->oob_shift = PAGE_SHIFT - 5;
+ s->pages = s->size >> PAGE_SHIFT;
+ s->addr_shift = ADDR_SHIFT;
+
+ s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE);
+ s->blk_write = glue(nand_blk_write_, PAGE_SIZE);
+ s->blk_load = glue(nand_blk_load_, PAGE_SIZE);
+}
+
+# undef PAGE_SIZE
+# undef PAGE_SHIFT
+# undef PAGE_SECTORS
+# undef ADDR_SHIFT
+#endif /* NAND_IO */
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
new file mode 100644
index 0000000..3ff0d89
--- /dev/null
+++ b/hw/ne2000-isa.c
@@ -0,0 +1,125 @@
+/*
+ * QEMU NE2000 emulation -- isa bus windup
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "qdev.h"
+#include "net.h"
+#include "ne2000.h"
+
+typedef struct ISANE2000State {
+ ISADevice dev;
+ uint32_t iobase;
+ uint32_t isairq;
+ NE2000State ne2000;
+} ISANE2000State;
+
+static void isa_ne2000_cleanup(VLANClientState *nc)
+{
+ NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_ne2000_isa_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = ne2000_can_receive,
+ .receive = ne2000_receive,
+ .cleanup = isa_ne2000_cleanup,
+};
+
+static const VMStateDescription vmstate_isa_ne2000 = {
+ .name = "ne2000",
+ .version_id = 2,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(ne2000, ISANE2000State, 0, vmstate_ne2000, NE2000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int isa_ne2000_initfn(ISADevice *dev)
+{
+ ISANE2000State *isa = DO_UPCAST(ISANE2000State, dev, dev);
+ NE2000State *s = &isa->ne2000;
+
+ register_ioport_write(isa->iobase, 16, 1, ne2000_ioport_write, s);
+ register_ioport_read(isa->iobase, 16, 1, ne2000_ioport_read, s);
+ isa_init_ioport_range(dev, isa->iobase, 16);
+
+ register_ioport_write(isa->iobase + 0x10, 1, 1, ne2000_asic_ioport_write, s);
+ register_ioport_read(isa->iobase + 0x10, 1, 1, ne2000_asic_ioport_read, s);
+ register_ioport_write(isa->iobase + 0x10, 2, 2, ne2000_asic_ioport_write, s);
+ register_ioport_read(isa->iobase + 0x10, 2, 2, ne2000_asic_ioport_read, s);
+ isa_init_ioport_range(dev, isa->iobase + 0x10, 2);
+
+ register_ioport_write(isa->iobase + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
+ register_ioport_read(isa->iobase + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
+ isa_init_ioport(dev, isa->iobase + 0x1f);
+
+ isa_init_irq(dev, &s->irq, isa->isairq);
+
+ qemu_macaddr_default_if_unset(&s->c.macaddr);
+ ne2000_reset(s);
+
+ s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
+
+ return 0;
+}
+
+void isa_ne2000_init(int base, int irq, NICInfo *nd)
+{
+ ISADevice *dev;
+
+ qemu_check_nic_model(nd, "ne2k_isa");
+
+ dev = isa_create("ne2k_isa");
+ qdev_prop_set_uint32(&dev->qdev, "iobase", base);
+ qdev_prop_set_uint32(&dev->qdev, "irq", irq);
+ qdev_set_nic_properties(&dev->qdev, nd);
+ qdev_init_nofail(&dev->qdev);
+}
+
+static ISADeviceInfo ne2000_isa_info = {
+ .qdev.name = "ne2k_isa",
+ .qdev.size = sizeof(ISANE2000State),
+ .init = isa_ne2000_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("iobase", ISANE2000State, iobase, 0x300),
+ DEFINE_PROP_UINT32("irq", ISANE2000State, isairq, 9),
+ DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void ne2000_isa_register_devices(void)
+{
+ isa_qdev_register(&ne2000_isa_info);
+}
+
+device_init(ne2000_isa_register_devices)
diff --git a/hw/ne2000.c b/hw/ne2000.c
new file mode 100644
index 0000000..5966359
--- /dev/null
+++ b/hw/ne2000.c
@@ -0,0 +1,781 @@
+/*
+ * QEMU NE2000 emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pci.h"
+#include "net.h"
+#include "ne2000.h"
+#include "loader.h"
+#include "sysemu.h"
+
+/* debug NE2000 card */
+//#define DEBUG_NE2000
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+#define E8390_CMD 0x00 /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+#define EN1_PHYS 0x11
+#define EN1_CURPAG 0x17
+#define EN1_MULT 0x18
+
+#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */
+#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */
+
+#define EN3_CONFIG0 0x33
+#define EN3_CONFIG1 0x34
+#define EN3_CONFIG2 0x35
+#define EN3_CONFIG3 0x36
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicast address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
+
+typedef struct PCINE2000State {
+ PCIDevice dev;
+ NE2000State ne2000;
+} PCINE2000State;
+
+void ne2000_reset(NE2000State *s)
+{
+ int i;
+
+ s->isr = ENISR_RESET;
+ memcpy(s->mem, &s->c.macaddr, 6);
+ s->mem[14] = 0x57;
+ s->mem[15] = 0x57;
+
+ /* duplicate prom data */
+ for(i = 15;i >= 0; i--) {
+ s->mem[2 * i] = s->mem[i];
+ s->mem[2 * i + 1] = s->mem[i];
+ }
+}
+
+static void ne2000_update_irq(NE2000State *s)
+{
+ int isr;
+ isr = (s->isr & s->imr) & 0x7f;
+#if defined(DEBUG_NE2000)
+ printf("NE2000: Set IRQ to %d (%02x %02x)\n",
+ isr ? 1 : 0, s->isr, s->imr);
+#endif
+ qemu_set_irq(s->irq, (isr != 0));
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+/* XXX: optimize */
+static int compute_mcast_idx(const uint8_t *ep)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return (crc >> 26);
+}
+
+static int ne2000_buffer_full(NE2000State *s)
+{
+ int avail, index, boundary;
+
+ index = s->curpag << 8;
+ boundary = s->boundary << 8;
+ if (index < boundary)
+ avail = boundary - index;
+ else
+ avail = (s->stop - s->start) - (index - boundary);
+ if (avail < (MAX_ETH_FRAME_SIZE + 4))
+ return 1;
+ return 0;
+}
+
+int ne2000_can_receive(VLANClientState *nc)
+{
+ NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if (s->cmd & E8390_STOP)
+ return 1;
+ return !ne2000_buffer_full(s);
+}
+
+#define MIN_BUF_SIZE 60
+
+ssize_t ne2000_receive(VLANClientState *nc, const uint8_t *buf, size_t size_)
+{
+ NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int size = size_;
+ uint8_t *p;
+ unsigned int total_len, next, avail, len, index, mcast_idx;
+ uint8_t buf1[60];
+ static const uint8_t broadcast_macaddr[6] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#if defined(DEBUG_NE2000)
+ printf("NE2000: received len=%d\n", size);
+#endif
+
+ if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
+ return -1;
+
+ /* XXX: check this */
+ if (s->rxcr & 0x10) {
+ /* promiscuous: receive all */
+ } else {
+ if (!memcmp(buf, broadcast_macaddr, 6)) {
+ /* broadcast address */
+ if (!(s->rxcr & 0x04))
+ return size;
+ } else if (buf[0] & 0x01) {
+ /* multicast */
+ if (!(s->rxcr & 0x08))
+ return size;
+ mcast_idx = compute_mcast_idx(buf);
+ if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+ return size;
+ } else if (s->mem[0] == buf[0] &&
+ s->mem[2] == buf[1] &&
+ s->mem[4] == buf[2] &&
+ s->mem[6] == buf[3] &&
+ s->mem[8] == buf[4] &&
+ s->mem[10] == buf[5]) {
+ /* match */
+ } else {
+ return size;
+ }
+ }
+
+
+ /* if too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ index = s->curpag << 8;
+ /* 4 bytes for header */
+ total_len = size + 4;
+ /* address for next packet (4 bytes for CRC) */
+ next = index + ((total_len + 4 + 255) & ~0xff);
+ if (next >= s->stop)
+ next -= (s->stop - s->start);
+ /* prepare packet header */
+ p = s->mem + index;
+ s->rsr = ENRSR_RXOK; /* receive status */
+ /* XXX: check this */
+ if (buf[0] & 0x01)
+ s->rsr |= ENRSR_PHY;
+ p[0] = s->rsr;
+ p[1] = next >> 8;
+ p[2] = total_len;
+ p[3] = total_len >> 8;
+ index += 4;
+
+ /* write packet data */
+ while (size > 0) {
+ if (index <= s->stop)
+ avail = s->stop - index;
+ else
+ avail = 0;
+ len = size;
+ if (len > avail)
+ len = avail;
+ memcpy(s->mem + index, buf, len);
+ buf += len;
+ index += len;
+ if (index == s->stop)
+ index = s->start;
+ size -= len;
+ }
+ s->curpag = next >> 8;
+
+ /* now we can signal we have received something */
+ s->isr |= ENISR_RX;
+ ne2000_update_irq(s);
+
+ return size_;
+}
+
+void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ NE2000State *s = opaque;
+ int offset, page, index;
+
+ addr &= 0xf;
+#ifdef DEBUG_NE2000
+ printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+ if (addr == E8390_CMD) {
+ /* control register */
+ s->cmd = val;
+ if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */
+ s->isr &= ~ENISR_RESET;
+ /* test specific case: zero length transfer */
+ if ((val & (E8390_RREAD | E8390_RWRITE)) &&
+ s->rcnt == 0) {
+ s->isr |= ENISR_RDC;
+ ne2000_update_irq(s);
+ }
+ if (val & E8390_TRANS) {
+ index = (s->tpsr << 8);
+ /* XXX: next 2 lines are a hack to make netware 3.11 work */
+ if (index >= NE2000_PMEM_END)
+ index -= NE2000_PMEM_SIZE;
+ /* fail safe: check range on the transmitted length */
+ if (index + s->tcnt <= NE2000_PMEM_END) {
+ qemu_send_packet(&s->nic->nc, s->mem + index, s->tcnt);
+ }
+ /* signal end of transfer */
+ s->tsr = ENTSR_PTX;
+ s->isr |= ENISR_TX;
+ s->cmd &= ~E8390_TRANS;
+ ne2000_update_irq(s);
+ }
+ }
+ } else {
+ page = s->cmd >> 6;
+ offset = addr | (page << 4);
+ switch(offset) {
+ case EN0_STARTPG:
+ s->start = val << 8;
+ break;
+ case EN0_STOPPG:
+ s->stop = val << 8;
+ break;
+ case EN0_BOUNDARY:
+ s->boundary = val;
+ break;
+ case EN0_IMR:
+ s->imr = val;
+ ne2000_update_irq(s);
+ break;
+ case EN0_TPSR:
+ s->tpsr = val;
+ break;
+ case EN0_TCNTLO:
+ s->tcnt = (s->tcnt & 0xff00) | val;
+ break;
+ case EN0_TCNTHI:
+ s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
+ break;
+ case EN0_RSARLO:
+ s->rsar = (s->rsar & 0xff00) | val;
+ break;
+ case EN0_RSARHI:
+ s->rsar = (s->rsar & 0x00ff) | (val << 8);
+ break;
+ case EN0_RCNTLO:
+ s->rcnt = (s->rcnt & 0xff00) | val;
+ break;
+ case EN0_RCNTHI:
+ s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
+ break;
+ case EN0_RXCR:
+ s->rxcr = val;
+ break;
+ case EN0_DCFG:
+ s->dcfg = val;
+ break;
+ case EN0_ISR:
+ s->isr &= ~(val & 0x7f);
+ ne2000_update_irq(s);
+ break;
+ case EN1_PHYS ... EN1_PHYS + 5:
+ s->phys[offset - EN1_PHYS] = val;
+ break;
+ case EN1_CURPAG:
+ s->curpag = val;
+ break;
+ case EN1_MULT ... EN1_MULT + 7:
+ s->mult[offset - EN1_MULT] = val;
+ break;
+ }
+ }
+}
+
+uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ int offset, page, ret;
+
+ addr &= 0xf;
+ if (addr == E8390_CMD) {
+ ret = s->cmd;
+ } else {
+ page = s->cmd >> 6;
+ offset = addr | (page << 4);
+ switch(offset) {
+ case EN0_TSR:
+ ret = s->tsr;
+ break;
+ case EN0_BOUNDARY:
+ ret = s->boundary;
+ break;
+ case EN0_ISR:
+ ret = s->isr;
+ break;
+ case EN0_RSARLO:
+ ret = s->rsar & 0x00ff;
+ break;
+ case EN0_RSARHI:
+ ret = s->rsar >> 8;
+ break;
+ case EN1_PHYS ... EN1_PHYS + 5:
+ ret = s->phys[offset - EN1_PHYS];
+ break;
+ case EN1_CURPAG:
+ ret = s->curpag;
+ break;
+ case EN1_MULT ... EN1_MULT + 7:
+ ret = s->mult[offset - EN1_MULT];
+ break;
+ case EN0_RSR:
+ ret = s->rsr;
+ break;
+ case EN2_STARTPG:
+ ret = s->start >> 8;
+ break;
+ case EN2_STOPPG:
+ ret = s->stop >> 8;
+ break;
+ case EN0_RTL8029ID0:
+ ret = 0x50;
+ break;
+ case EN0_RTL8029ID1:
+ ret = 0x43;
+ break;
+ case EN3_CONFIG0:
+ ret = 0; /* 10baseT media */
+ break;
+ case EN3_CONFIG2:
+ ret = 0x40; /* 10baseT active */
+ break;
+ case EN3_CONFIG3:
+ ret = 0x40; /* Full duplex */
+ break;
+ default:
+ ret = 0x00;
+ break;
+ }
+ }
+#ifdef DEBUG_NE2000
+ printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
+ uint32_t val)
+{
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ s->mem[addr] = val;
+ }
+}
+
+static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
+ uint32_t val)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ *(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
+ }
+}
+
+static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
+ uint32_t val)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
+ }
+}
+
+static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
+{
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ return s->mem[addr];
+ } else {
+ return 0xff;
+ }
+}
+
+static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ return le16_to_cpu(*(uint16_t *)(s->mem + addr));
+ } else {
+ return 0xffff;
+ }
+}
+
+static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
+{
+ addr &= ~1; /* XXX: check exact behaviour if not even */
+ if (addr < 32 ||
+ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+ return le32_to_cpupu((uint32_t *)(s->mem + addr));
+ } else {
+ return 0xffffffff;
+ }
+}
+
+static inline void ne2000_dma_update(NE2000State *s, int len)
+{
+ s->rsar += len;
+ /* wrap */
+ /* XXX: check what to do if rsar > stop */
+ if (s->rsar == s->stop)
+ s->rsar = s->start;
+
+ if (s->rcnt <= len) {
+ s->rcnt = 0;
+ /* signal end of transfer */
+ s->isr |= ENISR_RDC;
+ ne2000_update_irq(s);
+ } else {
+ s->rcnt -= len;
+ }
+}
+
+void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic write val=0x%04x\n", val);
+#endif
+ if (s->rcnt == 0)
+ return;
+ if (s->dcfg & 0x01) {
+ /* 16 bit access */
+ ne2000_mem_writew(s, s->rsar, val);
+ ne2000_dma_update(s, 2);
+ } else {
+ /* 8 bit access */
+ ne2000_mem_writeb(s, s->rsar, val);
+ ne2000_dma_update(s, 1);
+ }
+}
+
+uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ int ret;
+
+ if (s->dcfg & 0x01) {
+ /* 16 bit access */
+ ret = ne2000_mem_readw(s, s->rsar);
+ ne2000_dma_update(s, 2);
+ } else {
+ /* 8 bit access */
+ ret = ne2000_mem_readb(s, s->rsar);
+ ne2000_dma_update(s, 1);
+ }
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic read val=0x%04x\n", ret);
+#endif
+ return ret;
+}
+
+static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic writel val=0x%04x\n", val);
+#endif
+ if (s->rcnt == 0)
+ return;
+ /* 32 bit access */
+ ne2000_mem_writel(s, s->rsar, val);
+ ne2000_dma_update(s, 4);
+}
+
+static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ int ret;
+
+ /* 32 bit access */
+ ret = ne2000_mem_readl(s, s->rsar);
+ ne2000_dma_update(s, 4);
+#ifdef DEBUG_NE2000
+ printf("NE2000: asic readl val=0x%04x\n", ret);
+#endif
+ return ret;
+}
+
+void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ /* nothing to do (end of reset pulse) */
+}
+
+uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
+{
+ NE2000State *s = opaque;
+ ne2000_reset(s);
+ return 0;
+}
+
+static int ne2000_post_load(void* opaque, int version_id)
+{
+ NE2000State* s = opaque;
+
+ if (version_id < 2) {
+ s->rxcr = 0x0c;
+ }
+ return 0;
+}
+
+const VMStateDescription vmstate_ne2000 = {
+ .name = "ne2000",
+ .version_id = 2,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = ne2000_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8_V(rxcr, NE2000State, 2),
+ VMSTATE_UINT8(cmd, NE2000State),
+ VMSTATE_UINT32(start, NE2000State),
+ VMSTATE_UINT32(stop, NE2000State),
+ VMSTATE_UINT8(boundary, NE2000State),
+ VMSTATE_UINT8(tsr, NE2000State),
+ VMSTATE_UINT8(tpsr, NE2000State),
+ VMSTATE_UINT16(tcnt, NE2000State),
+ VMSTATE_UINT16(rcnt, NE2000State),
+ VMSTATE_UINT32(rsar, NE2000State),
+ VMSTATE_UINT8(rsr, NE2000State),
+ VMSTATE_UINT8(isr, NE2000State),
+ VMSTATE_UINT8(dcfg, NE2000State),
+ VMSTATE_UINT8(imr, NE2000State),
+ VMSTATE_BUFFER(phys, NE2000State),
+ VMSTATE_UINT8(curpag, NE2000State),
+ VMSTATE_BUFFER(mult, NE2000State),
+ VMSTATE_UNUSED(4), /* was irq */
+ VMSTATE_BUFFER(mem, NE2000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pci_ne2000 = {
+ .name = "ne2000",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCINE2000State),
+ VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/***********************************************************/
+/* PCI NE2000 definitions */
+
+static void ne2000_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
+ NE2000State *s = &d->ne2000;
+
+ register_ioport_write(addr, 16, 1, ne2000_ioport_write, s);
+ register_ioport_read(addr, 16, 1, ne2000_ioport_read, s);
+
+ register_ioport_write(addr + 0x10, 1, 1, ne2000_asic_ioport_write, s);
+ register_ioport_read(addr + 0x10, 1, 1, ne2000_asic_ioport_read, s);
+ register_ioport_write(addr + 0x10, 2, 2, ne2000_asic_ioport_write, s);
+ register_ioport_read(addr + 0x10, 2, 2, ne2000_asic_ioport_read, s);
+ register_ioport_write(addr + 0x10, 4, 4, ne2000_asic_ioport_writel, s);
+ register_ioport_read(addr + 0x10, 4, 4, ne2000_asic_ioport_readl, s);
+
+ register_ioport_write(addr + 0x1f, 1, 1, ne2000_reset_ioport_write, s);
+ register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s);
+}
+
+static void ne2000_cleanup(VLANClientState *nc)
+{
+ NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_ne2000_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = ne2000_can_receive,
+ .receive = ne2000_receive,
+ .cleanup = ne2000_cleanup,
+};
+
+static int pci_ne2000_init(PCIDevice *pci_dev)
+{
+ PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
+ NE2000State *s;
+ uint8_t *pci_conf;
+
+ pci_conf = d->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8029);
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ /* TODO: RST# value should be 0. PCI spec 6.2.4 */
+ pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0
+
+ pci_register_bar(&d->dev, 0, 0x100,
+ PCI_BASE_ADDRESS_SPACE_IO, ne2000_map);
+ s = &d->ne2000;
+ s->irq = d->dev.irq[0];
+
+ qemu_macaddr_default_if_unset(&s->c.macaddr);
+ ne2000_reset(s);
+
+ s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
+ pci_dev->qdev.info->name, pci_dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a);
+
+ if (!pci_dev->qdev.hotplugged) {
+ static int loaded = 0;
+ if (!loaded) {
+ rom_add_option("pxe-ne2k_pci.bin", -1);
+ loaded = 1;
+ }
+ }
+
+ add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
+ return 0;
+}
+
+static int pci_ne2000_exit(PCIDevice *pci_dev)
+{
+ PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
+ NE2000State *s = &d->ne2000;
+
+ qemu_del_vlan_client(&s->nic->nc);
+ return 0;
+}
+
+static PCIDeviceInfo ne2000_info = {
+ .qdev.name = "ne2k_pci",
+ .qdev.size = sizeof(PCINE2000State),
+ .qdev.vmsd = &vmstate_pci_ne2000,
+ .init = pci_ne2000_init,
+ .exit = pci_ne2000_exit,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ne2000_register_devices(void)
+{
+ pci_qdev_register(&ne2000_info);
+}
+
+device_init(ne2000_register_devices)
diff --git a/hw/ne2000.h b/hw/ne2000.h
new file mode 100644
index 0000000..54fdfca
--- /dev/null
+++ b/hw/ne2000.h
@@ -0,0 +1,39 @@
+#define NE2000_PMEM_SIZE (32*1024)
+#define NE2000_PMEM_START (16*1024)
+#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START)
+#define NE2000_MEM_SIZE NE2000_PMEM_END
+
+typedef struct NE2000State {
+ uint8_t cmd;
+ uint32_t start;
+ uint32_t stop;
+ uint8_t boundary;
+ uint8_t tsr;
+ uint8_t tpsr;
+ uint16_t tcnt;
+ uint16_t rcnt;
+ uint32_t rsar;
+ uint8_t rsr;
+ uint8_t rxcr;
+ uint8_t isr;
+ uint8_t dcfg;
+ uint8_t imr;
+ uint8_t phys[6]; /* mac address */
+ uint8_t curpag;
+ uint8_t mult[8]; /* multicast mask array */
+ qemu_irq irq;
+ NICState *nic;
+ NICConf c;
+ uint8_t mem[NE2000_MEM_SIZE];
+} NE2000State;
+
+void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t ne2000_ioport_read(void *opaque, uint32_t addr);
+void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr);
+void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr);
+extern const VMStateDescription vmstate_ne2000;
+void ne2000_reset(NE2000State *s);
+int ne2000_can_receive(VLANClientState *vc);
+ssize_t ne2000_receive(VLANClientState *vc, const uint8_t *buf, size_t size_);
diff --git a/hw/nseries.c b/hw/nseries.c
new file mode 100644
index 0000000..2f6f473
--- /dev/null
+++ b/hw/nseries.c
@@ -0,0 +1,1417 @@
+/*
+ * Nokia N-series internet tablets.
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "omap.h"
+#include "arm-misc.h"
+#include "irq.h"
+#include "console.h"
+#include "boards.h"
+#include "i2c.h"
+#include "devices.h"
+#include "flash.h"
+#include "hw.h"
+#include "bt.h"
+#include "loader.h"
+
+/* Nokia N8x0 support */
+struct n800_s {
+ struct omap_mpu_state_s *cpu;
+
+ struct rfbi_chip_s blizzard;
+ struct {
+ void *opaque;
+ uint32_t (*txrx)(void *opaque, uint32_t value, int len);
+ uWireSlave *chip;
+ } ts;
+ i2c_bus *i2c;
+
+ int keymap[0x80];
+ i2c_slave *kbd;
+
+ TUSBState *usb;
+ void *retu;
+ void *tahvo;
+ void *nand;
+};
+
+/* GPIO pins */
+#define N8X0_TUSB_ENABLE_GPIO 0
+#define N800_MMC2_WP_GPIO 8
+#define N800_UNKNOWN_GPIO0 9 /* out */
+#define N810_MMC2_VIOSD_GPIO 9
+#define N810_HEADSET_AMP_GPIO 10
+#define N800_CAM_TURN_GPIO 12
+#define N810_GPS_RESET_GPIO 12
+#define N800_BLIZZARD_POWERDOWN_GPIO 15
+#define N800_MMC1_WP_GPIO 23
+#define N810_MMC2_VSD_GPIO 23
+#define N8X0_ONENAND_GPIO 26
+#define N810_BLIZZARD_RESET_GPIO 30
+#define N800_UNKNOWN_GPIO2 53 /* out */
+#define N8X0_TUSB_INT_GPIO 58
+#define N8X0_BT_WKUP_GPIO 61
+#define N8X0_STI_GPIO 62
+#define N8X0_CBUS_SEL_GPIO 64
+#define N8X0_CBUS_DAT_GPIO 65
+#define N8X0_CBUS_CLK_GPIO 66
+#define N8X0_WLAN_IRQ_GPIO 87
+#define N8X0_BT_RESET_GPIO 92
+#define N8X0_TEA5761_CS_GPIO 93
+#define N800_UNKNOWN_GPIO 94
+#define N810_TSC_RESET_GPIO 94
+#define N800_CAM_ACT_GPIO 95
+#define N810_GPS_WAKEUP_GPIO 95
+#define N8X0_MMC_CS_GPIO 96
+#define N8X0_WLAN_PWR_GPIO 97
+#define N8X0_BT_HOST_WKUP_GPIO 98
+#define N810_SPEAKER_AMP_GPIO 101
+#define N810_KB_LOCK_GPIO 102
+#define N800_TSC_TS_GPIO 103
+#define N810_TSC_TS_GPIO 106
+#define N8X0_HEADPHONE_GPIO 107
+#define N8X0_RETU_GPIO 108
+#define N800_TSC_KP_IRQ_GPIO 109
+#define N810_KEYBOARD_GPIO 109
+#define N800_BAT_COVER_GPIO 110
+#define N810_SLIDE_GPIO 110
+#define N8X0_TAHVO_GPIO 111
+#define N800_UNKNOWN_GPIO4 112 /* out */
+#define N810_SLEEPX_LED_GPIO 112
+#define N800_TSC_RESET_GPIO 118 /* ? */
+#define N810_AIC33_RESET_GPIO 118
+#define N800_TSC_UNKNOWN_GPIO 119 /* out */
+#define N8X0_TMP105_GPIO 125
+
+/* Config */
+#define BT_UART 0
+#define XLDR_LL_UART 1
+
+/* Addresses on the I2C bus 0 */
+#define N810_TLV320AIC33_ADDR 0x18 /* Audio CODEC */
+#define N8X0_TCM825x_ADDR 0x29 /* Camera */
+#define N810_LP5521_ADDR 0x32 /* LEDs */
+#define N810_TSL2563_ADDR 0x3d /* Light sensor */
+#define N810_LM8323_ADDR 0x45 /* Keyboard */
+/* Addresses on the I2C bus 1 */
+#define N8X0_TMP105_ADDR 0x48 /* Temperature sensor */
+#define N8X0_MENELAUS_ADDR 0x72 /* Power management */
+
+/* Chipselects on GPMC NOR interface */
+#define N8X0_ONENAND_CS 0
+#define N8X0_USB_ASYNC_CS 1
+#define N8X0_USB_SYNC_CS 4
+
+#define N8X0_BD_ADDR 0x00, 0x1a, 0x89, 0x9e, 0x3e, 0x81
+
+static void n800_mmc_cs_cb(void *opaque, int line, int level)
+{
+ /* TODO: this seems to actually be connected to the menelaus, to
+ * which also both MMC slots connect. */
+ omap_mmc_enable((struct omap_mmc_s *) opaque, !level);
+
+ printf("%s: MMC slot %i active\n", __FUNCTION__, level + 1);
+}
+
+static void n8x0_gpio_setup(struct n800_s *s)
+{
+ qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1);
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_MMC_CS_GPIO, mmc_cs[0]);
+
+ qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]);
+}
+
+#define MAEMO_CAL_HEADER(...) \
+ 'C', 'o', 'n', 'F', 0x02, 0x00, 0x04, 0x00, \
+ __VA_ARGS__, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+static const uint8_t n8x0_cal_wlan_mac[] = {
+ MAEMO_CAL_HEADER('w', 'l', 'a', 'n', '-', 'm', 'a', 'c')
+ 0x1c, 0x00, 0x00, 0x00, 0x47, 0xd6, 0x69, 0xb3,
+ 0x30, 0x08, 0xa0, 0x83, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x89, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00,
+ 0x5d, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t n8x0_cal_bt_id[] = {
+ MAEMO_CAL_HEADER('b', 't', '-', 'i', 'd', 0, 0, 0)
+ 0x0a, 0x00, 0x00, 0x00, 0xa3, 0x4b, 0xf6, 0x96,
+ 0xa8, 0xeb, 0xb2, 0x41, 0x00, 0x00, 0x00, 0x00,
+ N8X0_BD_ADDR,
+};
+
+static void n8x0_nand_setup(struct n800_s *s)
+{
+ char *otp_region;
+
+ /* Either ec40xx or ec48xx are OK for the ID */
+ omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update,
+ onenand_base_unmap,
+ (s->nand = onenand_init(0xec4800, 1,
+ omap2_gpio_in_get(s->cpu->gpif,
+ N8X0_ONENAND_GPIO)[0])));
+ otp_region = onenand_raw_otp(s->nand);
+
+ memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac));
+ memcpy(otp_region + 0x800, n8x0_cal_bt_id, sizeof(n8x0_cal_bt_id));
+ /* XXX: in theory should also update the OOB for both pages */
+}
+
+static void n8x0_i2c_setup(struct n800_s *s)
+{
+ DeviceState *dev;
+ qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TMP105_GPIO)[0];
+
+ /* Attach the CPU on one end of our I2C bus. */
+ s->i2c = omap_i2c_bus(s->cpu->i2c[0]);
+
+ /* Attach a menelaus PM chip */
+ dev = i2c_create_slave(s->i2c, "twl92230", N8X0_MENELAUS_ADDR);
+ qdev_connect_gpio_out(dev, 3, s->cpu->irq[0][OMAP_INT_24XX_SYS_NIRQ]);
+
+ /* Attach a TMP105 PM chip (A0 wired to ground) */
+ dev = i2c_create_slave(s->i2c, "tmp105", N8X0_TMP105_ADDR);
+ qdev_connect_gpio_out(dev, 0, tmp_irq);
+}
+
+/* Touchscreen and keypad controller */
+static MouseTransformInfo n800_pointercal = {
+ .x = 800,
+ .y = 480,
+ .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 },
+};
+
+static MouseTransformInfo n810_pointercal = {
+ .x = 800,
+ .y = 480,
+ .a = { 15041, 148, -4731056, 171, -10238, 35933380, 65536 },
+};
+
+#define RETU_KEYCODE 61 /* F3 */
+
+static void n800_key_event(void *opaque, int keycode)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ int code = s->keymap[keycode & 0x7f];
+
+ if (code == -1) {
+ if ((keycode & 0x7f) == RETU_KEYCODE)
+ retu_key_event(s->retu, !(keycode & 0x80));
+ return;
+ }
+
+ tsc210x_key_event(s->ts.chip, code, !(keycode & 0x80));
+}
+
+static const int n800_keys[16] = {
+ -1,
+ 72, /* Up */
+ 63, /* Home (F5) */
+ -1,
+ 75, /* Left */
+ 28, /* Enter */
+ 77, /* Right */
+ -1,
+ 1, /* Cycle (ESC) */
+ 80, /* Down */
+ 62, /* Menu (F4) */
+ -1,
+ 66, /* Zoom- (F8) */
+ 64, /* FullScreen (F6) */
+ 65, /* Zoom+ (F7) */
+ -1,
+};
+
+static void n800_tsc_kbd_setup(struct n800_s *s)
+{
+ int i;
+
+ /* XXX: are the three pins inverted inside the chip between the
+ * tsc and the cpu (N4111)? */
+ qemu_irq penirq = NULL; /* NC */
+ qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0];
+ qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0];
+
+ s->ts.chip = tsc2301_init(penirq, kbirq, dav);
+ s->ts.opaque = s->ts.chip->opaque;
+ s->ts.txrx = tsc210x_txrx;
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < 0x10; i ++)
+ if (n800_keys[i] >= 0)
+ s->keymap[n800_keys[i]] = i;
+
+ qemu_add_kbd_event_handler(n800_key_event, s);
+
+ tsc210x_set_transform(s->ts.chip, &n800_pointercal);
+}
+
+static void n810_tsc_setup(struct n800_s *s)
+{
+ qemu_irq pintdav = omap2_gpio_in_get(s->cpu->gpif, N810_TSC_TS_GPIO)[0];
+
+ s->ts.opaque = tsc2005_init(pintdav);
+ s->ts.txrx = tsc2005_txrx;
+
+ tsc2005_set_transform(s->ts.opaque, &n810_pointercal);
+}
+
+/* N810 Keyboard controller */
+static void n810_key_event(void *opaque, int keycode)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ int code = s->keymap[keycode & 0x7f];
+
+ if (code == -1) {
+ if ((keycode & 0x7f) == RETU_KEYCODE)
+ retu_key_event(s->retu, !(keycode & 0x80));
+ return;
+ }
+
+ lm832x_key_event(s->kbd, code, !(keycode & 0x80));
+}
+
+#define M 0
+
+static int n810_keys[0x80] = {
+ [0x01] = 16, /* Q */
+ [0x02] = 37, /* K */
+ [0x03] = 24, /* O */
+ [0x04] = 25, /* P */
+ [0x05] = 14, /* Backspace */
+ [0x06] = 30, /* A */
+ [0x07] = 31, /* S */
+ [0x08] = 32, /* D */
+ [0x09] = 33, /* F */
+ [0x0a] = 34, /* G */
+ [0x0b] = 35, /* H */
+ [0x0c] = 36, /* J */
+
+ [0x11] = 17, /* W */
+ [0x12] = 62, /* Menu (F4) */
+ [0x13] = 38, /* L */
+ [0x14] = 40, /* ' (Apostrophe) */
+ [0x16] = 44, /* Z */
+ [0x17] = 45, /* X */
+ [0x18] = 46, /* C */
+ [0x19] = 47, /* V */
+ [0x1a] = 48, /* B */
+ [0x1b] = 49, /* N */
+ [0x1c] = 42, /* Shift (Left shift) */
+ [0x1f] = 65, /* Zoom+ (F7) */
+
+ [0x21] = 18, /* E */
+ [0x22] = 39, /* ; (Semicolon) */
+ [0x23] = 12, /* - (Minus) */
+ [0x24] = 13, /* = (Equal) */
+ [0x2b] = 56, /* Fn (Left Alt) */
+ [0x2c] = 50, /* M */
+ [0x2f] = 66, /* Zoom- (F8) */
+
+ [0x31] = 19, /* R */
+ [0x32] = 29 | M, /* Right Ctrl */
+ [0x34] = 57, /* Space */
+ [0x35] = 51, /* , (Comma) */
+ [0x37] = 72 | M, /* Up */
+ [0x3c] = 82 | M, /* Compose (Insert) */
+ [0x3f] = 64, /* FullScreen (F6) */
+
+ [0x41] = 20, /* T */
+ [0x44] = 52, /* . (Dot) */
+ [0x46] = 77 | M, /* Right */
+ [0x4f] = 63, /* Home (F5) */
+ [0x51] = 21, /* Y */
+ [0x53] = 80 | M, /* Down */
+ [0x55] = 28, /* Enter */
+ [0x5f] = 1, /* Cycle (ESC) */
+
+ [0x61] = 22, /* U */
+ [0x64] = 75 | M, /* Left */
+
+ [0x71] = 23, /* I */
+#if 0
+ [0x75] = 28 | M, /* KP Enter (KP Enter) */
+#else
+ [0x75] = 15, /* KP Enter (Tab) */
+#endif
+};
+
+#undef M
+
+static void n810_kbd_setup(struct n800_s *s)
+{
+ qemu_irq kbd_irq = omap2_gpio_in_get(s->cpu->gpif, N810_KEYBOARD_GPIO)[0];
+ DeviceState *dev;
+ int i;
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < 0x80; i ++)
+ if (n810_keys[i] > 0)
+ s->keymap[n810_keys[i]] = i;
+
+ qemu_add_kbd_event_handler(n810_key_event, s);
+
+ /* Attach the LM8322 keyboard to the I2C bus,
+ * should happen in n8x0_i2c_setup and s->kbd be initialised here. */
+ dev = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR);
+ qdev_connect_gpio_out(dev, 0, kbd_irq);
+}
+
+/* LCD MIPI DBI-C controller (URAL) */
+struct mipid_s {
+ int resp[4];
+ int param[4];
+ int p;
+ int pm;
+ int cmd;
+
+ int sleep;
+ int booster;
+ int te;
+ int selfcheck;
+ int partial;
+ int normal;
+ int vscr;
+ int invert;
+ int onoff;
+ int gamma;
+ uint32_t id;
+};
+
+static void mipid_reset(struct mipid_s *s)
+{
+ if (!s->sleep)
+ fprintf(stderr, "%s: Display off\n", __FUNCTION__);
+
+ s->pm = 0;
+ s->cmd = 0;
+
+ s->sleep = 1;
+ s->booster = 0;
+ s->selfcheck =
+ (1 << 7) | /* Register loading OK. */
+ (1 << 5) | /* The chip is attached. */
+ (1 << 4); /* Display glass still in one piece. */
+ s->te = 0;
+ s->partial = 0;
+ s->normal = 1;
+ s->vscr = 0;
+ s->invert = 0;
+ s->onoff = 1;
+ s->gamma = 0;
+}
+
+static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len)
+{
+ struct mipid_s *s = (struct mipid_s *) opaque;
+ uint8_t ret;
+
+ if (len > 9)
+ hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len);
+
+ if (s->p >= ARRAY_SIZE(s->resp))
+ ret = 0;
+ else
+ ret = s->resp[s->p ++];
+ if (s->pm --> 0)
+ s->param[s->pm] = cmd;
+ else
+ s->cmd = cmd;
+
+ switch (s->cmd) {
+ case 0x00: /* NOP */
+ break;
+
+ case 0x01: /* SWRESET */
+ mipid_reset(s);
+ break;
+
+ case 0x02: /* BSTROFF */
+ s->booster = 0;
+ break;
+ case 0x03: /* BSTRON */
+ s->booster = 1;
+ break;
+
+ case 0x04: /* RDDID */
+ s->p = 0;
+ s->resp[0] = (s->id >> 16) & 0xff;
+ s->resp[1] = (s->id >> 8) & 0xff;
+ s->resp[2] = (s->id >> 0) & 0xff;
+ break;
+
+ case 0x06: /* RD_RED */
+ case 0x07: /* RD_GREEN */
+ /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so
+ * for the bootloader one needs to change this. */
+ case 0x08: /* RD_BLUE */
+ s->p = 0;
+ /* TODO: return first pixel components */
+ s->resp[0] = 0x01;
+ break;
+
+ case 0x09: /* RDDST */
+ s->p = 0;
+ s->resp[0] = s->booster << 7;
+ s->resp[1] = (5 << 4) | (s->partial << 2) |
+ (s->sleep << 1) | s->normal;
+ s->resp[2] = (s->vscr << 7) | (s->invert << 5) |
+ (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2);
+ s->resp[3] = s->gamma << 6;
+ break;
+
+ case 0x0a: /* RDDPM */
+ s->p = 0;
+ s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) |
+ (s->partial << 5) | (s->sleep << 6) | (s->booster << 7);
+ break;
+ case 0x0b: /* RDDMADCTR */
+ s->p = 0;
+ s->resp[0] = 0;
+ break;
+ case 0x0c: /* RDDCOLMOD */
+ s->p = 0;
+ s->resp[0] = 5; /* 65K colours */
+ break;
+ case 0x0d: /* RDDIM */
+ s->p = 0;
+ s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma;
+ break;
+ case 0x0e: /* RDDSM */
+ s->p = 0;
+ s->resp[0] = s->te << 7;
+ break;
+ case 0x0f: /* RDDSDR */
+ s->p = 0;
+ s->resp[0] = s->selfcheck;
+ break;
+
+ case 0x10: /* SLPIN */
+ s->sleep = 1;
+ break;
+ case 0x11: /* SLPOUT */
+ s->sleep = 0;
+ s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */
+ break;
+
+ case 0x12: /* PTLON */
+ s->partial = 1;
+ s->normal = 0;
+ s->vscr = 0;
+ break;
+ case 0x13: /* NORON */
+ s->partial = 0;
+ s->normal = 1;
+ s->vscr = 0;
+ break;
+
+ case 0x20: /* INVOFF */
+ s->invert = 0;
+ break;
+ case 0x21: /* INVON */
+ s->invert = 1;
+ break;
+
+ case 0x22: /* APOFF */
+ case 0x23: /* APON */
+ goto bad_cmd;
+
+ case 0x25: /* WRCNTR */
+ if (s->pm < 0)
+ s->pm = 1;
+ goto bad_cmd;
+
+ case 0x26: /* GAMSET */
+ if (!s->pm)
+ s->gamma = ffs(s->param[0] & 0xf) - 1;
+ else if (s->pm < 0)
+ s->pm = 1;
+ break;
+
+ case 0x28: /* DISPOFF */
+ s->onoff = 0;
+ fprintf(stderr, "%s: Display off\n", __FUNCTION__);
+ break;
+ case 0x29: /* DISPON */
+ s->onoff = 1;
+ fprintf(stderr, "%s: Display on\n", __FUNCTION__);
+ break;
+
+ case 0x2a: /* CASET */
+ case 0x2b: /* RASET */
+ case 0x2c: /* RAMWR */
+ case 0x2d: /* RGBSET */
+ case 0x2e: /* RAMRD */
+ case 0x30: /* PTLAR */
+ case 0x33: /* SCRLAR */
+ goto bad_cmd;
+
+ case 0x34: /* TEOFF */
+ s->te = 0;
+ break;
+ case 0x35: /* TEON */
+ if (!s->pm)
+ s->te = 1;
+ else if (s->pm < 0)
+ s->pm = 1;
+ break;
+
+ case 0x36: /* MADCTR */
+ goto bad_cmd;
+
+ case 0x37: /* VSCSAD */
+ s->partial = 0;
+ s->normal = 0;
+ s->vscr = 1;
+ break;
+
+ case 0x38: /* IDMOFF */
+ case 0x39: /* IDMON */
+ case 0x3a: /* COLMOD */
+ goto bad_cmd;
+
+ case 0xb0: /* CLKINT / DISCTL */
+ case 0xb1: /* CLKEXT */
+ if (s->pm < 0)
+ s->pm = 2;
+ break;
+
+ case 0xb4: /* FRMSEL */
+ break;
+
+ case 0xb5: /* FRM8SEL */
+ case 0xb6: /* TMPRNG / INIESC */
+ case 0xb7: /* TMPHIS / NOP2 */
+ case 0xb8: /* TMPREAD / MADCTL */
+ case 0xba: /* DISTCTR */
+ case 0xbb: /* EPVOL */
+ goto bad_cmd;
+
+ case 0xbd: /* Unknown */
+ s->p = 0;
+ s->resp[0] = 0;
+ s->resp[1] = 1;
+ break;
+
+ case 0xc2: /* IFMOD */
+ if (s->pm < 0)
+ s->pm = 2;
+ break;
+
+ case 0xc6: /* PWRCTL */
+ case 0xc7: /* PPWRCTL */
+ case 0xd0: /* EPWROUT */
+ case 0xd1: /* EPWRIN */
+ case 0xd4: /* RDEV */
+ case 0xd5: /* RDRR */
+ goto bad_cmd;
+
+ case 0xda: /* RDID1 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 16) & 0xff;
+ break;
+ case 0xdb: /* RDID2 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 8) & 0xff;
+ break;
+ case 0xdc: /* RDID3 */
+ s->p = 0;
+ s->resp[0] = (s->id >> 0) & 0xff;
+ break;
+
+ default:
+ bad_cmd:
+ fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, s->cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static void *mipid_init(void)
+{
+ struct mipid_s *s = (struct mipid_s *) qemu_mallocz(sizeof(*s));
+
+ s->id = 0x838f03;
+ mipid_reset(s);
+
+ return s;
+}
+
+static void n8x0_spi_setup(struct n800_s *s)
+{
+ void *tsc = s->ts.opaque;
+ void *mipid = mipid_init();
+
+ omap_mcspi_attach(s->cpu->mcspi[0], s->ts.txrx, tsc, 0);
+ omap_mcspi_attach(s->cpu->mcspi[0], mipid_txrx, mipid, 1);
+}
+
+/* This task is normally performed by the bootloader. If we're loading
+ * a kernel directly, we need to enable the Blizzard ourselves. */
+static void n800_dss_init(struct rfbi_chip_s *chip)
+{
+ uint8_t *fb_blank;
+
+ chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */
+ chip->write(chip->opaque, 1, 0x64);
+ chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */
+ chip->write(chip->opaque, 1, 0x1e);
+ chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */
+ chip->write(chip->opaque, 1, 0xe0);
+ chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */
+ chip->write(chip->opaque, 1, 0x01);
+ chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */
+ chip->write(chip->opaque, 1, 0x06);
+ chip->write(chip->opaque, 0, 0x68); /* Display Mode register */
+ chip->write(chip->opaque, 1, 1); /* Enable bit */
+
+ chip->write(chip->opaque, 0, 0x6c);
+ chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */
+ chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */
+ chip->write(chip->opaque, 1, 0x03); /* Input X End Position */
+ chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
+ chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */
+ chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */
+ chip->write(chip->opaque, 1, 0x03); /* Output X End Position */
+ chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */
+ chip->write(chip->opaque, 1, 0x01); /* Input Data Format */
+ chip->write(chip->opaque, 1, 0x01); /* Data Source Select */
+
+ fb_blank = memset(qemu_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2);
+ /* Display Memory Data Port */
+ chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800);
+ qemu_free(fb_blank);
+}
+
+static void n8x0_dss_setup(struct n800_s *s)
+{
+ s->blizzard.opaque = s1d13745_init(NULL);
+ s->blizzard.block = s1d13745_write_block;
+ s->blizzard.write = s1d13745_write;
+ s->blizzard.read = s1d13745_read;
+
+ omap_rfbi_attach(s->cpu->dss, 0, &s->blizzard);
+}
+
+static void n8x0_cbus_setup(struct n800_s *s)
+{
+ qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N8X0_CBUS_DAT_GPIO)[0];
+ qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_RETU_GPIO)[0];
+ qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TAHVO_GPIO)[0];
+
+ CBus *cbus = cbus_init(dat_out);
+
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk);
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat);
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel);
+
+ cbus_attach(cbus, s->retu = retu_init(retu_irq, 1));
+ cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1));
+}
+
+static void n8x0_uart_setup(struct n800_s *s)
+{
+ CharDriverState *radio = uart_hci_init(
+ omap2_gpio_in_get(s->cpu->gpif,
+ N8X0_BT_HOST_WKUP_GPIO)[0]);
+
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_RESET_GPIO,
+ csrhci_pins_get(radio)[csrhci_pin_reset]);
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_WKUP_GPIO,
+ csrhci_pins_get(radio)[csrhci_pin_wakeup]);
+
+ omap_uart_attach(s->cpu->uart[BT_UART], radio);
+}
+
+static void n8x0_usb_power_cb(void *opaque, int line, int level)
+{
+ struct n800_s *s = opaque;
+
+ tusb6010_power(s->usb, level);
+}
+
+static void n8x0_usb_setup(struct n800_s *s)
+{
+ qemu_irq tusb_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TUSB_INT_GPIO)[0];
+ qemu_irq tusb_pwr = qemu_allocate_irqs(n8x0_usb_power_cb, s, 1)[0];
+ TUSBState *tusb = tusb6010_init(tusb_irq);
+
+ /* Using the NOR interface */
+ omap_gpmc_attach(s->cpu->gpmc, N8X0_USB_ASYNC_CS,
+ tusb6010_async_io(tusb), NULL, NULL, tusb);
+ omap_gpmc_attach(s->cpu->gpmc, N8X0_USB_SYNC_CS,
+ tusb6010_sync_io(tusb), NULL, NULL, tusb);
+
+ s->usb = tusb;
+ omap2_gpio_out_set(s->cpu->gpif, N8X0_TUSB_ENABLE_GPIO, tusb_pwr);
+}
+
+/* Setup done before the main bootloader starts by some early setup code
+ * - used when we want to run the main bootloader in emulation. This
+ * isn't documented. */
+static uint32_t n800_pinout[104] = {
+ 0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0,
+ 0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808,
+ 0x08080808, 0x180800c4, 0x00b80000, 0x08080808,
+ 0x080800bc, 0x00cc0808, 0x08081818, 0x18180128,
+ 0x01241800, 0x18181818, 0x000000f0, 0x01300000,
+ 0x00001b0b, 0x1b0f0138, 0x00e0181b, 0x1b031b0b,
+ 0x180f0078, 0x00740018, 0x0f0f0f1a, 0x00000080,
+ 0x007c0000, 0x00000000, 0x00000088, 0x00840000,
+ 0x00000000, 0x00000094, 0x00980300, 0x0f180003,
+ 0x0000008c, 0x00900f0f, 0x0f0f1b00, 0x0f00009c,
+ 0x01140000, 0x1b1b0f18, 0x0818013c, 0x01400008,
+ 0x00001818, 0x000b0110, 0x010c1800, 0x0b030b0f,
+ 0x181800f4, 0x00f81818, 0x00000018, 0x000000fc,
+ 0x00401808, 0x00000000, 0x0f1b0030, 0x003c0008,
+ 0x00000000, 0x00000038, 0x00340000, 0x00000000,
+ 0x1a080070, 0x00641a1a, 0x08080808, 0x08080060,
+ 0x005c0808, 0x08080808, 0x08080058, 0x00540808,
+ 0x08080808, 0x0808006c, 0x00680808, 0x08080808,
+ 0x000000a8, 0x00b00000, 0x08080808, 0x000000a0,
+ 0x00a40000, 0x00000000, 0x08ff0050, 0x004c0808,
+ 0xffffffff, 0xffff0048, 0x0044ffff, 0xffffffff,
+ 0x000000ac, 0x01040800, 0x08080b0f, 0x18180100,
+ 0x01081818, 0x0b0b1808, 0x1a0300e4, 0x012c0b1a,
+ 0x02020018, 0x0b000134, 0x011c0800, 0x0b1b1b00,
+ 0x0f0000c8, 0x00ec181b, 0x000f0f02, 0x00180118,
+ 0x01200000, 0x0f0b1b1b, 0x0f0200e8, 0x0000020b,
+};
+
+static void n800_setup_nolo_tags(void *sram_base)
+{
+ int i;
+ uint32_t *p = sram_base + 0x8000;
+ uint32_t *v = sram_base + 0xa000;
+
+ memset(p, 0, 0x3000);
+
+ strcpy((void *) (p + 0), "QEMU N800");
+
+ strcpy((void *) (p + 8), "F5");
+
+ stl_raw(p + 10, 0x04f70000);
+ strcpy((void *) (p + 9), "RX-34");
+
+ /* RAM size in MB? */
+ stl_raw(p + 12, 0x80);
+
+ /* Pointer to the list of tags */
+ stl_raw(p + 13, OMAP2_SRAM_BASE + 0x9000);
+
+ /* The NOLO tags start here */
+ p = sram_base + 0x9000;
+#define ADD_TAG(tag, len) \
+ stw_raw((uint16_t *) p + 0, tag); \
+ stw_raw((uint16_t *) p + 1, len); p ++; \
+ stl_raw(p ++, OMAP2_SRAM_BASE | (((void *) v - sram_base) & 0xffff));
+
+ /* OMAP STI console? Pin out settings? */
+ ADD_TAG(0x6e01, 414);
+ for (i = 0; i < ARRAY_SIZE(n800_pinout); i ++)
+ stl_raw(v ++, n800_pinout[i]);
+
+ /* Kernel memsize? */
+ ADD_TAG(0x6e05, 1);
+ stl_raw(v ++, 2);
+
+ /* NOLO serial console */
+ ADD_TAG(0x6e02, 4);
+ stl_raw(v ++, XLDR_LL_UART); /* UART number (1 - 3) */
+
+#if 0
+ /* CBUS settings (Retu/AVilma) */
+ ADD_TAG(0x6e03, 6);
+ stw_raw((uint16_t *) v + 0, 65); /* CBUS GPIO0 */
+ stw_raw((uint16_t *) v + 1, 66); /* CBUS GPIO1 */
+ stw_raw((uint16_t *) v + 2, 64); /* CBUS GPIO2 */
+ v += 2;
+#endif
+
+ /* Nokia ASIC BB5 (Retu/Tahvo) */
+ ADD_TAG(0x6e0a, 4);
+ stw_raw((uint16_t *) v + 0, 111); /* "Retu" interrupt GPIO */
+ stw_raw((uint16_t *) v + 1, 108); /* "Tahvo" interrupt GPIO */
+ v ++;
+
+ /* LCD console? */
+ ADD_TAG(0x6e04, 4);
+ stw_raw((uint16_t *) v + 0, 30); /* ??? */
+ stw_raw((uint16_t *) v + 1, 24); /* ??? */
+ v ++;
+
+#if 0
+ /* LCD settings */
+ ADD_TAG(0x6e06, 2);
+ stw_raw((uint16_t *) (v ++), 15); /* ??? */
+#endif
+
+ /* I^2C (Menelaus) */
+ ADD_TAG(0x6e07, 4);
+ stl_raw(v ++, 0x00720000); /* ??? */
+
+ /* Unknown */
+ ADD_TAG(0x6e0b, 6);
+ stw_raw((uint16_t *) v + 0, 94); /* ??? */
+ stw_raw((uint16_t *) v + 1, 23); /* ??? */
+ stw_raw((uint16_t *) v + 2, 0); /* ??? */
+ v += 2;
+
+ /* OMAP gpio switch info */
+ ADD_TAG(0x6e0c, 80);
+ strcpy((void *) v, "bat_cover"); v += 3;
+ stw_raw((uint16_t *) v + 0, 110); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 1); /* GPIO num ??? */
+ v += 2;
+ strcpy((void *) v, "cam_act"); v += 3;
+ stw_raw((uint16_t *) v + 0, 95); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 32); /* GPIO num ??? */
+ v += 2;
+ strcpy((void *) v, "cam_turn"); v += 3;
+ stw_raw((uint16_t *) v + 0, 12); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 33); /* GPIO num ??? */
+ v += 2;
+ strcpy((void *) v, "headphone"); v += 3;
+ stw_raw((uint16_t *) v + 0, 107); /* GPIO num ??? */
+ stw_raw((uint16_t *) v + 1, 17); /* GPIO num ??? */
+ v += 2;
+
+ /* Bluetooth */
+ ADD_TAG(0x6e0e, 12);
+ stl_raw(v ++, 0x5c623d01); /* ??? */
+ stl_raw(v ++, 0x00000201); /* ??? */
+ stl_raw(v ++, 0x00000000); /* ??? */
+
+ /* CX3110x WLAN settings */
+ ADD_TAG(0x6e0f, 8);
+ stl_raw(v ++, 0x00610025); /* ??? */
+ stl_raw(v ++, 0xffff0057); /* ??? */
+
+ /* MMC host settings */
+ ADD_TAG(0x6e10, 12);
+ stl_raw(v ++, 0xffff000f); /* ??? */
+ stl_raw(v ++, 0xffffffff); /* ??? */
+ stl_raw(v ++, 0x00000060); /* ??? */
+
+ /* OneNAND chip select */
+ ADD_TAG(0x6e11, 10);
+ stl_raw(v ++, 0x00000401); /* ??? */
+ stl_raw(v ++, 0x0002003a); /* ??? */
+ stl_raw(v ++, 0x00000002); /* ??? */
+
+ /* TEA5761 sensor settings */
+ ADD_TAG(0x6e12, 2);
+ stl_raw(v ++, 93); /* GPIO num ??? */
+
+#if 0
+ /* Unknown tag */
+ ADD_TAG(6e09, 0);
+
+ /* Kernel UART / console */
+ ADD_TAG(6e12, 0);
+#endif
+
+ /* End of the list */
+ stl_raw(p ++, 0x00000000);
+ stl_raw(p ++, 0x00000000);
+}
+
+/* This task is normally performed by the bootloader. If we're loading
+ * a kernel directly, we need to set up GPMC mappings ourselves. */
+static void n800_gpmc_init(struct n800_s *s)
+{
+ uint32_t config7 =
+ (0xf << 8) | /* MASKADDRESS */
+ (1 << 6) | /* CSVALID */
+ (4 << 0); /* BASEADDRESS */
+
+ cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */
+ (void *) &config7, sizeof(config7));
+}
+
+/* Setup sequence done by the bootloader */
+static void n8x0_boot_init(void *opaque)
+{
+ struct n800_s *s = (struct n800_s *) opaque;
+ uint32_t buf;
+
+ /* PRCM setup */
+#define omap_writel(addr, val) \
+ buf = (val); \
+ cpu_physical_memory_write(addr, (void *) &buf, sizeof(buf))
+
+ omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */
+ omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */
+ omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */
+ omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */
+ omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */
+ omap_writel(0x48008098, 0); /* PRCM_POLCTRL */
+ omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */
+ omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */
+ omap_writel(0x48008158, 1); /* RM_RSTST_MPU */
+ omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */
+ omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */
+ omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */
+ omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */
+ omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */
+ omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */
+ omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */
+ omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */
+ omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */
+ omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */
+ omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */
+ omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */
+ omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */
+ omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */
+ omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */
+ omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */
+ omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */
+ omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */
+ omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */
+ omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */
+ omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */
+ omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */
+ omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */
+ omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */
+ omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */
+ omap_writel(0x48008540, /* CM_CLKSEL1_PLL */
+ (0x78 << 12) | (6 << 8));
+ omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */
+
+ /* GPMC setup */
+ n800_gpmc_init(s);
+
+ /* Video setup */
+ n800_dss_init(&s->blizzard);
+
+ /* CPU setup */
+ s->cpu->env->GE = 0x5;
+
+ /* If the machine has a slided keyboard, open it */
+ if (s->kbd)
+ qemu_irq_raise(omap2_gpio_in_get(s->cpu->gpif, N810_SLIDE_GPIO)[0]);
+}
+
+#define OMAP_TAG_NOKIA_BT 0x4e01
+#define OMAP_TAG_WLAN_CX3110X 0x4e02
+#define OMAP_TAG_CBUS 0x4e03
+#define OMAP_TAG_EM_ASIC_BB5 0x4e04
+
+static struct omap_gpiosw_info_s {
+ const char *name;
+ int line;
+ int type;
+} n800_gpiosw_info[] = {
+ {
+ "bat_cover", N800_BAT_COVER_GPIO,
+ OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
+ }, {
+ "cam_act", N800_CAM_ACT_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY,
+ }, {
+ "cam_turn", N800_CAM_TURN_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED,
+ }, {
+ "headphone", N8X0_HEADPHONE_GPIO,
+ OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED,
+ },
+ { NULL }
+}, n810_gpiosw_info[] = {
+ {
+ "gps_reset", N810_GPS_RESET_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT,
+ }, {
+ "gps_wakeup", N810_GPS_WAKEUP_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT,
+ }, {
+ "headphone", N8X0_HEADPHONE_GPIO,
+ OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED,
+ }, {
+ "kb_lock", N810_KB_LOCK_GPIO,
+ OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
+ }, {
+ "sleepx_led", N810_SLEEPX_LED_GPIO,
+ OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED | OMAP_GPIOSW_OUTPUT,
+ }, {
+ "slide", N810_SLIDE_GPIO,
+ OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED,
+ },
+ { NULL }
+};
+
+static struct omap_partition_info_s {
+ uint32_t offset;
+ uint32_t size;
+ int mask;
+ const char *name;
+} n800_part_info[] = {
+ { 0x00000000, 0x00020000, 0x3, "bootloader" },
+ { 0x00020000, 0x00060000, 0x0, "config" },
+ { 0x00080000, 0x00200000, 0x0, "kernel" },
+ { 0x00280000, 0x00200000, 0x3, "initfs" },
+ { 0x00480000, 0x0fb80000, 0x3, "rootfs" },
+
+ { 0, 0, 0, NULL }
+}, n810_part_info[] = {
+ { 0x00000000, 0x00020000, 0x3, "bootloader" },
+ { 0x00020000, 0x00060000, 0x0, "config" },
+ { 0x00080000, 0x00220000, 0x0, "kernel" },
+ { 0x002a0000, 0x00400000, 0x0, "initfs" },
+ { 0x006a0000, 0x0f960000, 0x0, "rootfs" },
+
+ { 0, 0, 0, NULL }
+};
+
+static bdaddr_t n8x0_bd_addr = {{ N8X0_BD_ADDR }};
+
+static int n8x0_atag_setup(void *p, int model)
+{
+ uint8_t *b;
+ uint16_t *w;
+ uint32_t *l;
+ struct omap_gpiosw_info_s *gpiosw;
+ struct omap_partition_info_s *partition;
+ const char *tag;
+
+ w = p;
+
+ stw_raw(w ++, OMAP_TAG_UART); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */
+ w ++;
+
+#if 0
+ stw_raw(w ++, OMAP_TAG_SERIAL_CONSOLE); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, XLDR_LL_UART + 1); /* u8 console_uart */
+ stw_raw(w ++, 115200); /* u32 console_speed */
+#endif
+
+ stw_raw(w ++, OMAP_TAG_LCD); /* u16 tag */
+ stw_raw(w ++, 36); /* u16 len */
+ strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */
+ w += 8;
+ strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */
+ w += 8;
+ stw_raw(w ++, N810_BLIZZARD_RESET_GPIO); /* TODO: n800 s16 nreset_gpio */
+ stw_raw(w ++, 24); /* u8 data_lines */
+
+ stw_raw(w ++, OMAP_TAG_CBUS); /* u16 tag */
+ stw_raw(w ++, 8); /* u16 len */
+ stw_raw(w ++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */
+ stw_raw(w ++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */
+ stw_raw(w ++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */
+ w ++;
+
+ stw_raw(w ++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */
+ stw_raw(w ++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */
+
+ gpiosw = (model == 810) ? n810_gpiosw_info : n800_gpiosw_info;
+ for (; gpiosw->name; gpiosw ++) {
+ stw_raw(w ++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */
+ stw_raw(w ++, 20); /* u16 len */
+ strcpy((void *) w, gpiosw->name); /* char name[12] */
+ w += 6;
+ stw_raw(w ++, gpiosw->line); /* u16 gpio */
+ stw_raw(w ++, gpiosw->type);
+ stw_raw(w ++, 0);
+ stw_raw(w ++, 0);
+ }
+
+ stw_raw(w ++, OMAP_TAG_NOKIA_BT); /* u16 tag */
+ stw_raw(w ++, 12); /* u16 len */
+ b = (void *) w;
+ stb_raw(b ++, 0x01); /* u8 chip_type (CSR) */
+ stb_raw(b ++, N8X0_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */
+ stb_raw(b ++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */
+ stb_raw(b ++, N8X0_BT_RESET_GPIO); /* u8 reset_gpio */
+ stb_raw(b ++, BT_UART + 1); /* u8 bt_uart */
+ memcpy(b, &n8x0_bd_addr, 6); /* u8 bd_addr[6] */
+ b += 6;
+ stb_raw(b ++, 0x02); /* u8 bt_sysclk (38.4) */
+ w = (void *) b;
+
+ stw_raw(w ++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */
+ stw_raw(w ++, 8); /* u16 len */
+ stw_raw(w ++, 0x25); /* u8 chip_type */
+ stw_raw(w ++, N8X0_WLAN_PWR_GPIO); /* s16 power_gpio */
+ stw_raw(w ++, N8X0_WLAN_IRQ_GPIO); /* s16 irq_gpio */
+ stw_raw(w ++, -1); /* s16 spi_cs_gpio */
+
+ stw_raw(w ++, OMAP_TAG_MMC); /* u16 tag */
+ stw_raw(w ++, 16); /* u16 len */
+ if (model == 810) {
+ stw_raw(w ++, 0x23f); /* unsigned flags */
+ stw_raw(w ++, -1); /* s16 power_pin */
+ stw_raw(w ++, -1); /* s16 switch_pin */
+ stw_raw(w ++, -1); /* s16 wp_pin */
+ stw_raw(w ++, 0x240); /* unsigned flags */
+ stw_raw(w ++, 0xc000); /* s16 power_pin */
+ stw_raw(w ++, 0x0248); /* s16 switch_pin */
+ stw_raw(w ++, 0xc000); /* s16 wp_pin */
+ } else {
+ stw_raw(w ++, 0xf); /* unsigned flags */
+ stw_raw(w ++, -1); /* s16 power_pin */
+ stw_raw(w ++, -1); /* s16 switch_pin */
+ stw_raw(w ++, -1); /* s16 wp_pin */
+ stw_raw(w ++, 0); /* unsigned flags */
+ stw_raw(w ++, 0); /* s16 power_pin */
+ stw_raw(w ++, 0); /* s16 switch_pin */
+ stw_raw(w ++, 0); /* s16 wp_pin */
+ }
+
+ stw_raw(w ++, OMAP_TAG_TEA5761); /* u16 tag */
+ stw_raw(w ++, 4); /* u16 len */
+ stw_raw(w ++, N8X0_TEA5761_CS_GPIO); /* u16 enable_gpio */
+ w ++;
+
+ partition = (model == 810) ? n810_part_info : n800_part_info;
+ for (; partition->name; partition ++) {
+ stw_raw(w ++, OMAP_TAG_PARTITION); /* u16 tag */
+ stw_raw(w ++, 28); /* u16 len */
+ strcpy((void *) w, partition->name); /* char name[16] */
+ l = (void *) (w + 8);
+ stl_raw(l ++, partition->size); /* unsigned int size */
+ stl_raw(l ++, partition->offset); /* unsigned int offset */
+ stl_raw(l ++, partition->mask); /* unsigned int mask_flags */
+ w = (void *) l;
+ }
+
+ stw_raw(w ++, OMAP_TAG_BOOT_REASON); /* u16 tag */
+ stw_raw(w ++, 12); /* u16 len */
+#if 0
+ strcpy((void *) w, "por"); /* char reason_str[12] */
+ strcpy((void *) w, "charger"); /* char reason_str[12] */
+ strcpy((void *) w, "32wd_to"); /* char reason_str[12] */
+ strcpy((void *) w, "sw_rst"); /* char reason_str[12] */
+ strcpy((void *) w, "mbus"); /* char reason_str[12] */
+ strcpy((void *) w, "unknown"); /* char reason_str[12] */
+ strcpy((void *) w, "swdg_to"); /* char reason_str[12] */
+ strcpy((void *) w, "sec_vio"); /* char reason_str[12] */
+ strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
+ strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */
+#else
+ strcpy((void *) w, "pwr_key"); /* char reason_str[12] */
+#endif
+ w += 6;
+
+ tag = (model == 810) ? "RX-44" : "RX-34";
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "product"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, tag); /* char version[12] */
+ w += 6;
+
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "hw-build"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, "QEMU " QEMU_VERSION); /* char version[12] */
+ w += 6;
+
+ tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu";
+ stw_raw(w ++, OMAP_TAG_VERSION_STR); /* u16 tag */
+ stw_raw(w ++, 24); /* u16 len */
+ strcpy((void *) w, "nolo"); /* char component[12] */
+ w += 6;
+ strcpy((void *) w, tag); /* char version[12] */
+ w += 6;
+
+ return (void *) w - p;
+}
+
+static int n800_atag_setup(struct arm_boot_info *info, void *p)
+{
+ return n8x0_atag_setup(p, 800);
+}
+
+static int n810_atag_setup(struct arm_boot_info *info, void *p)
+{
+ return n8x0_atag_setup(p, 810);
+}
+
+static void n8x0_init(ram_addr_t ram_size, const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline, const char *initrd_filename,
+ const char *cpu_model, struct arm_boot_info *binfo, int model)
+{
+ struct n800_s *s = (struct n800_s *) qemu_mallocz(sizeof(*s));
+ int sdram_size = binfo->ram_size;
+ DisplayState *ds;
+
+ s->cpu = omap2420_mpu_init(sdram_size, cpu_model);
+
+ /* Setup peripherals
+ *
+ * Believed external peripherals layout in the N810:
+ * (spi bus 1)
+ * tsc2005
+ * lcd_mipid
+ * (spi bus 2)
+ * Conexant cx3110x (WLAN)
+ * optional: pc2400m (WiMAX)
+ * (i2c bus 0)
+ * TLV320AIC33 (audio codec)
+ * TCM825x (camera by Toshiba)
+ * lp5521 (clever LEDs)
+ * tsl2563 (light sensor, hwmon, model 7, rev. 0)
+ * lm8323 (keypad, manf 00, rev 04)
+ * (i2c bus 1)
+ * tmp105 (temperature sensor, hwmon)
+ * menelaus (pm)
+ * (somewhere on i2c - maybe N800-only)
+ * tea5761 (FM tuner)
+ * (serial 0)
+ * GPS
+ * (some serial port)
+ * csr41814 (Bluetooth)
+ */
+ n8x0_gpio_setup(s);
+ n8x0_nand_setup(s);
+ n8x0_i2c_setup(s);
+ if (model == 800)
+ n800_tsc_kbd_setup(s);
+ else if (model == 810) {
+ n810_tsc_setup(s);
+ n810_kbd_setup(s);
+ }
+ n8x0_spi_setup(s);
+ n8x0_dss_setup(s);
+ n8x0_cbus_setup(s);
+ n8x0_uart_setup(s);
+ if (usb_enabled)
+ n8x0_usb_setup(s);
+
+ if (kernel_filename) {
+ /* Or at the linux loader. */
+ binfo->kernel_filename = kernel_filename;
+ binfo->kernel_cmdline = kernel_cmdline;
+ binfo->initrd_filename = initrd_filename;
+ arm_load_kernel(s->cpu->env, binfo);
+
+ qemu_register_reset(n8x0_boot_init, s);
+ }
+
+ if (option_rom[0].name && (boot_device[0] == 'n' || !kernel_filename)) {
+ int rom_size;
+ uint8_t nolo_tags[0x10000];
+ /* No, wait, better start at the ROM. */
+ s->cpu->env->regs[15] = OMAP2_Q2_BASE + 0x400000;
+
+ /* This is intended for loading the `secondary.bin' program from
+ * Nokia images (the NOLO bootloader). The entry point seems
+ * to be at OMAP2_Q2_BASE + 0x400000.
+ *
+ * The `2nd.bin' files contain some kind of earlier boot code and
+ * for them the entry point needs to be set to OMAP2_SRAM_BASE.
+ *
+ * The code above is for loading the `zImage' file from Nokia
+ * images. */
+ rom_size = load_image_targphys(option_rom[0].name,
+ OMAP2_Q2_BASE + 0x400000,
+ sdram_size - 0x400000);
+ printf("%i bytes of image loaded\n", rom_size);
+
+ n800_setup_nolo_tags(nolo_tags);
+ cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000);
+ }
+ /* FIXME: We shouldn't really be doing this here. The LCD controller
+ will set the size once configured, so this just sets an initial
+ size until the guest activates the display. */
+ ds = get_displaystate();
+ ds->surface = qemu_resize_displaysurface(ds, 800, 480);
+ dpy_resize(ds);
+}
+
+static struct arm_boot_info n800_binfo = {
+ .loader_start = OMAP2_Q2_BASE,
+ /* Actually two chips of 0x4000000 bytes each */
+ .ram_size = 0x08000000,
+ .board_id = 0x4f7,
+ .atag_board = n800_atag_setup,
+};
+
+static struct arm_boot_info n810_binfo = {
+ .loader_start = OMAP2_Q2_BASE,
+ /* Actually two chips of 0x4000000 bytes each */
+ .ram_size = 0x08000000,
+ /* 0x60c and 0x6bf (WiMAX Edition) have been assigned but are not
+ * used by some older versions of the bootloader and 5555 is used
+ * instead (including versions that shipped with many devices). */
+ .board_id = 0x60c,
+ .atag_board = n810_atag_setup,
+};
+
+static void n800_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ return n8x0_init(ram_size, boot_device,
+ kernel_filename, kernel_cmdline, initrd_filename,
+ cpu_model, &n800_binfo, 800);
+}
+
+static void n810_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ return n8x0_init(ram_size, boot_device,
+ kernel_filename, kernel_cmdline, initrd_filename,
+ cpu_model, &n810_binfo, 810);
+}
+
+static QEMUMachine n800_machine = {
+ .name = "n800",
+ .desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)",
+ .init = n800_init,
+};
+
+static QEMUMachine n810_machine = {
+ .name = "n810",
+ .desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)",
+ .init = n810_init,
+};
+
+static void nseries_machine_init(void)
+{
+ qemu_register_machine(&n800_machine);
+ qemu_register_machine(&n810_machine);
+}
+
+machine_init(nseries_machine_init);
diff --git a/hw/nvram.h b/hw/nvram.h
new file mode 100644
index 0000000..0f55b24
--- /dev/null
+++ b/hw/nvram.h
@@ -0,0 +1,42 @@
+#ifndef NVRAM_H
+#define NVRAM_H
+
+/* NVRAM helpers */
+typedef uint32_t (*nvram_read_t)(void *private, uint32_t addr);
+typedef void (*nvram_write_t)(void *private, uint32_t addr, uint32_t val);
+typedef struct nvram_t {
+ void *opaque;
+ nvram_read_t read_fn;
+ nvram_write_t write_fn;
+} nvram_t;
+
+void NVRAM_set_byte (nvram_t *nvram, uint32_t addr, uint8_t value);
+uint8_t NVRAM_get_byte (nvram_t *nvram, uint32_t addr);
+void NVRAM_set_word (nvram_t *nvram, uint32_t addr, uint16_t value);
+uint16_t NVRAM_get_word (nvram_t *nvram, uint32_t addr);
+void NVRAM_set_lword (nvram_t *nvram, uint32_t addr, uint32_t value);
+uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr);
+void NVRAM_set_string (nvram_t *nvram, uint32_t addr,
+ const char *str, uint32_t max);
+int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max);
+void NVRAM_set_crc (nvram_t *nvram, uint32_t addr,
+ uint32_t start, uint32_t count);
+int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size,
+ const char *arch,
+ uint32_t RAM_size, int boot_device,
+ uint32_t kernel_image, uint32_t kernel_size,
+ const char *cmdline,
+ uint32_t initrd_image, uint32_t initrd_size,
+ uint32_t NVRAM_image,
+ int width, int height, int depth);
+typedef struct M48t59State M48t59State;
+
+void m48t59_write (void *private, uint32_t addr, uint32_t val);
+uint32_t m48t59_read (void *private, uint32_t addr);
+void m48t59_toggle_lock (void *private, int lock);
+M48t59State *m48t59_init_isa(uint32_t io_base, uint16_t size, int type);
+M48t59State *m48t59_init(qemu_irq IRQ, target_phys_addr_t mem_base,
+ uint32_t io_base, uint16_t size, int type);
+void m48t59_set_addr (void *opaque, uint32_t addr);
+
+#endif /* !NVRAM_H */
diff --git a/hw/omap.h b/hw/omap.h
new file mode 100644
index 0000000..c227a82
--- /dev/null
+++ b/hw/omap.h
@@ -0,0 +1,1141 @@
+/*
+ * Texas Instruments OMAP processors.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#ifndef hw_omap_h
+# define hw_omap_h "omap.h"
+
+# define OMAP_EMIFS_BASE 0x00000000
+# define OMAP2_Q0_BASE 0x00000000
+# define OMAP_CS0_BASE 0x00000000
+# define OMAP_CS1_BASE 0x04000000
+# define OMAP_CS2_BASE 0x08000000
+# define OMAP_CS3_BASE 0x0c000000
+# define OMAP_EMIFF_BASE 0x10000000
+# define OMAP_IMIF_BASE 0x20000000
+# define OMAP_LOCALBUS_BASE 0x30000000
+# define OMAP2_Q1_BASE 0x40000000
+# define OMAP2_L4_BASE 0x48000000
+# define OMAP2_SRAM_BASE 0x40200000
+# define OMAP2_L3_BASE 0x68000000
+# define OMAP2_Q2_BASE 0x80000000
+# define OMAP2_Q3_BASE 0xc0000000
+# define OMAP_MPUI_BASE 0xe1000000
+
+# define OMAP730_SRAM_SIZE 0x00032000
+# define OMAP15XX_SRAM_SIZE 0x00030000
+# define OMAP16XX_SRAM_SIZE 0x00004000
+# define OMAP1611_SRAM_SIZE 0x0003e800
+# define OMAP242X_SRAM_SIZE 0x000a0000
+# define OMAP243X_SRAM_SIZE 0x00010000
+# define OMAP_CS0_SIZE 0x04000000
+# define OMAP_CS1_SIZE 0x04000000
+# define OMAP_CS2_SIZE 0x04000000
+# define OMAP_CS3_SIZE 0x04000000
+
+/* omap_clk.c */
+struct omap_mpu_state_s;
+typedef struct clk *omap_clk;
+omap_clk omap_findclk(struct omap_mpu_state_s *mpu, const char *name);
+void omap_clk_init(struct omap_mpu_state_s *mpu);
+void omap_clk_adduser(struct clk *clk, qemu_irq user);
+void omap_clk_get(omap_clk clk);
+void omap_clk_put(omap_clk clk);
+void omap_clk_onoff(omap_clk clk, int on);
+void omap_clk_canidle(omap_clk clk, int can);
+void omap_clk_setrate(omap_clk clk, int divide, int multiply);
+int64_t omap_clk_getrate(omap_clk clk);
+void omap_clk_reparent(omap_clk clk, omap_clk parent);
+
+/* OMAP2 l4 Interconnect */
+struct omap_l4_s;
+struct omap_l4_region_s {
+ target_phys_addr_t offset;
+ size_t size;
+ int access;
+};
+struct omap_l4_agent_info_s {
+ int ta;
+ int region;
+ int regions;
+ int ta_region;
+};
+struct omap_target_agent_s {
+ struct omap_l4_s *bus;
+ int regions;
+ const struct omap_l4_region_s *start;
+ target_phys_addr_t base;
+ uint32_t component;
+ uint32_t control;
+ uint32_t status;
+};
+struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num);
+
+struct omap_target_agent_s;
+struct omap_target_agent_s *omap_l4ta_get(
+ struct omap_l4_s *bus,
+ const struct omap_l4_region_s *regions,
+ const struct omap_l4_agent_info_s *agents,
+ int cs);
+target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region,
+ int iotype);
+int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write, void *opaque);
+
+/* OMAP interrupt controller */
+struct omap_intr_handler_s;
+struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+ unsigned long size, unsigned char nbanks, qemu_irq **pins,
+ qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk clk);
+struct omap_intr_handler_s *omap2_inth_init(target_phys_addr_t base,
+ int size, int nbanks, qemu_irq **pins,
+ qemu_irq parent_irq, qemu_irq parent_fiq,
+ omap_clk fclk, omap_clk iclk);
+void omap_inth_reset(struct omap_intr_handler_s *s);
+qemu_irq omap_inth_get_pin(struct omap_intr_handler_s *s, int n);
+
+/* OMAP2 SDRAM controller */
+struct omap_sdrc_s;
+struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base);
+void omap_sdrc_reset(struct omap_sdrc_s *s);
+
+/* OMAP2 general purpose memory controller */
+struct omap_gpmc_s;
+struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq);
+void omap_gpmc_reset(struct omap_gpmc_s *s);
+void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
+ void (*base_upd)(void *opaque, target_phys_addr_t new),
+ void (*unmap)(void *opaque), void *opaque);
+
+/*
+ * Common IRQ numbers for level 1 interrupt handler
+ * See /usr/include/asm-arm/arch-omap/irqs.h in Linux.
+ */
+# define OMAP_INT_CAMERA 1
+# define OMAP_INT_FIQ 3
+# define OMAP_INT_RTDX 6
+# define OMAP_INT_DSP_MMU_ABORT 7
+# define OMAP_INT_HOST 8
+# define OMAP_INT_ABORT 9
+# define OMAP_INT_BRIDGE_PRIV 13
+# define OMAP_INT_GPIO_BANK1 14
+# define OMAP_INT_UART3 15
+# define OMAP_INT_TIMER3 16
+# define OMAP_INT_DMA_CH0_6 19
+# define OMAP_INT_DMA_CH1_7 20
+# define OMAP_INT_DMA_CH2_8 21
+# define OMAP_INT_DMA_CH3 22
+# define OMAP_INT_DMA_CH4 23
+# define OMAP_INT_DMA_CH5 24
+# define OMAP_INT_DMA_LCD 25
+# define OMAP_INT_TIMER1 26
+# define OMAP_INT_WD_TIMER 27
+# define OMAP_INT_BRIDGE_PUB 28
+# define OMAP_INT_TIMER2 30
+# define OMAP_INT_LCD_CTRL 31
+
+/*
+ * Common OMAP-15xx IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_15XX_IH2_IRQ 0
+# define OMAP_INT_15XX_LB_MMU 17
+# define OMAP_INT_15XX_LOCAL_BUS 29
+
+/*
+ * OMAP-1510 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_1510_SPI_TX 4
+# define OMAP_INT_1510_SPI_RX 5
+# define OMAP_INT_1510_DSP_MAILBOX1 10
+# define OMAP_INT_1510_DSP_MAILBOX2 11
+
+/*
+ * OMAP-310 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_310_McBSP2_TX 4
+# define OMAP_INT_310_McBSP2_RX 5
+# define OMAP_INT_310_HSB_MAILBOX1 12
+# define OMAP_INT_310_HSAB_MMU 18
+
+/*
+ * OMAP-1610 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_1610_IH2_IRQ 0
+# define OMAP_INT_1610_IH2_FIQ 2
+# define OMAP_INT_1610_McBSP2_TX 4
+# define OMAP_INT_1610_McBSP2_RX 5
+# define OMAP_INT_1610_DSP_MAILBOX1 10
+# define OMAP_INT_1610_DSP_MAILBOX2 11
+# define OMAP_INT_1610_LCD_LINE 12
+# define OMAP_INT_1610_GPTIMER1 17
+# define OMAP_INT_1610_GPTIMER2 18
+# define OMAP_INT_1610_SSR_FIFO_0 29
+
+/*
+ * OMAP-730 specific IRQ numbers for level 1 interrupt handler
+ */
+# define OMAP_INT_730_IH2_FIQ 0
+# define OMAP_INT_730_IH2_IRQ 1
+# define OMAP_INT_730_USB_NON_ISO 2
+# define OMAP_INT_730_USB_ISO 3
+# define OMAP_INT_730_ICR 4
+# define OMAP_INT_730_EAC 5
+# define OMAP_INT_730_GPIO_BANK1 6
+# define OMAP_INT_730_GPIO_BANK2 7
+# define OMAP_INT_730_GPIO_BANK3 8
+# define OMAP_INT_730_McBSP2TX 10
+# define OMAP_INT_730_McBSP2RX 11
+# define OMAP_INT_730_McBSP2RX_OVF 12
+# define OMAP_INT_730_LCD_LINE 14
+# define OMAP_INT_730_GSM_PROTECT 15
+# define OMAP_INT_730_TIMER3 16
+# define OMAP_INT_730_GPIO_BANK5 17
+# define OMAP_INT_730_GPIO_BANK6 18
+# define OMAP_INT_730_SPGIO_WR 29
+
+/*
+ * Common IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_KEYBOARD 1
+# define OMAP_INT_uWireTX 2
+# define OMAP_INT_uWireRX 3
+# define OMAP_INT_I2C 4
+# define OMAP_INT_MPUIO 5
+# define OMAP_INT_USB_HHC_1 6
+# define OMAP_INT_McBSP3TX 10
+# define OMAP_INT_McBSP3RX 11
+# define OMAP_INT_McBSP1TX 12
+# define OMAP_INT_McBSP1RX 13
+# define OMAP_INT_UART1 14
+# define OMAP_INT_UART2 15
+# define OMAP_INT_USB_W2FC 20
+# define OMAP_INT_1WIRE 21
+# define OMAP_INT_OS_TIMER 22
+# define OMAP_INT_OQN 23
+# define OMAP_INT_GAUGE_32K 24
+# define OMAP_INT_RTC_TIMER 25
+# define OMAP_INT_RTC_ALARM 26
+# define OMAP_INT_DSP_MMU 28
+
+/*
+ * OMAP-1510 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_1510_BT_MCSI1TX 16
+# define OMAP_INT_1510_BT_MCSI1RX 17
+# define OMAP_INT_1510_SoSSI_MATCH 19
+# define OMAP_INT_1510_MEM_STICK 27
+# define OMAP_INT_1510_COM_SPI_RO 31
+
+/*
+ * OMAP-310 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_310_FAC 0
+# define OMAP_INT_310_USB_HHC_2 7
+# define OMAP_INT_310_MCSI1_FE 16
+# define OMAP_INT_310_MCSI2_FE 17
+# define OMAP_INT_310_USB_W2FC_ISO 29
+# define OMAP_INT_310_USB_W2FC_NON_ISO 30
+# define OMAP_INT_310_McBSP2RX_OF 31
+
+/*
+ * OMAP-1610 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_1610_FAC 0
+# define OMAP_INT_1610_USB_HHC_2 7
+# define OMAP_INT_1610_USB_OTG 8
+# define OMAP_INT_1610_SoSSI 9
+# define OMAP_INT_1610_BT_MCSI1TX 16
+# define OMAP_INT_1610_BT_MCSI1RX 17
+# define OMAP_INT_1610_SoSSI_MATCH 19
+# define OMAP_INT_1610_MEM_STICK 27
+# define OMAP_INT_1610_McBSP2RX_OF 31
+# define OMAP_INT_1610_STI 32
+# define OMAP_INT_1610_STI_WAKEUP 33
+# define OMAP_INT_1610_GPTIMER3 34
+# define OMAP_INT_1610_GPTIMER4 35
+# define OMAP_INT_1610_GPTIMER5 36
+# define OMAP_INT_1610_GPTIMER6 37
+# define OMAP_INT_1610_GPTIMER7 38
+# define OMAP_INT_1610_GPTIMER8 39
+# define OMAP_INT_1610_GPIO_BANK2 40
+# define OMAP_INT_1610_GPIO_BANK3 41
+# define OMAP_INT_1610_MMC2 42
+# define OMAP_INT_1610_CF 43
+# define OMAP_INT_1610_WAKE_UP_REQ 46
+# define OMAP_INT_1610_GPIO_BANK4 48
+# define OMAP_INT_1610_SPI 49
+# define OMAP_INT_1610_DMA_CH6 53
+# define OMAP_INT_1610_DMA_CH7 54
+# define OMAP_INT_1610_DMA_CH8 55
+# define OMAP_INT_1610_DMA_CH9 56
+# define OMAP_INT_1610_DMA_CH10 57
+# define OMAP_INT_1610_DMA_CH11 58
+# define OMAP_INT_1610_DMA_CH12 59
+# define OMAP_INT_1610_DMA_CH13 60
+# define OMAP_INT_1610_DMA_CH14 61
+# define OMAP_INT_1610_DMA_CH15 62
+# define OMAP_INT_1610_NAND 63
+
+/*
+ * OMAP-730 specific IRQ numbers for level 2 interrupt handler
+ */
+# define OMAP_INT_730_HW_ERRORS 0
+# define OMAP_INT_730_NFIQ_PWR_FAIL 1
+# define OMAP_INT_730_CFCD 2
+# define OMAP_INT_730_CFIREQ 3
+# define OMAP_INT_730_I2C 4
+# define OMAP_INT_730_PCC 5
+# define OMAP_INT_730_MPU_EXT_NIRQ 6
+# define OMAP_INT_730_SPI_100K_1 7
+# define OMAP_INT_730_SYREN_SPI 8
+# define OMAP_INT_730_VLYNQ 9
+# define OMAP_INT_730_GPIO_BANK4 10
+# define OMAP_INT_730_McBSP1TX 11
+# define OMAP_INT_730_McBSP1RX 12
+# define OMAP_INT_730_McBSP1RX_OF 13
+# define OMAP_INT_730_UART_MODEM_IRDA_2 14
+# define OMAP_INT_730_UART_MODEM_1 15
+# define OMAP_INT_730_MCSI 16
+# define OMAP_INT_730_uWireTX 17
+# define OMAP_INT_730_uWireRX 18
+# define OMAP_INT_730_SMC_CD 19
+# define OMAP_INT_730_SMC_IREQ 20
+# define OMAP_INT_730_HDQ_1WIRE 21
+# define OMAP_INT_730_TIMER32K 22
+# define OMAP_INT_730_MMC_SDIO 23
+# define OMAP_INT_730_UPLD 24
+# define OMAP_INT_730_USB_HHC_1 27
+# define OMAP_INT_730_USB_HHC_2 28
+# define OMAP_INT_730_USB_GENI 29
+# define OMAP_INT_730_USB_OTG 30
+# define OMAP_INT_730_CAMERA_IF 31
+# define OMAP_INT_730_RNG 32
+# define OMAP_INT_730_DUAL_MODE_TIMER 33
+# define OMAP_INT_730_DBB_RF_EN 34
+# define OMAP_INT_730_MPUIO_KEYPAD 35
+# define OMAP_INT_730_SHA1_MD5 36
+# define OMAP_INT_730_SPI_100K_2 37
+# define OMAP_INT_730_RNG_IDLE 38
+# define OMAP_INT_730_MPUIO 39
+# define OMAP_INT_730_LLPC_LCD_CTRL_OFF 40
+# define OMAP_INT_730_LLPC_OE_FALLING 41
+# define OMAP_INT_730_LLPC_OE_RISING 42
+# define OMAP_INT_730_LLPC_VSYNC 43
+# define OMAP_INT_730_WAKE_UP_REQ 46
+# define OMAP_INT_730_DMA_CH6 53
+# define OMAP_INT_730_DMA_CH7 54
+# define OMAP_INT_730_DMA_CH8 55
+# define OMAP_INT_730_DMA_CH9 56
+# define OMAP_INT_730_DMA_CH10 57
+# define OMAP_INT_730_DMA_CH11 58
+# define OMAP_INT_730_DMA_CH12 59
+# define OMAP_INT_730_DMA_CH13 60
+# define OMAP_INT_730_DMA_CH14 61
+# define OMAP_INT_730_DMA_CH15 62
+# define OMAP_INT_730_NAND 63
+
+/*
+ * OMAP-24xx common IRQ numbers
+ */
+# define OMAP_INT_24XX_STI 4
+# define OMAP_INT_24XX_SYS_NIRQ 7
+# define OMAP_INT_24XX_L3_IRQ 10
+# define OMAP_INT_24XX_PRCM_MPU_IRQ 11
+# define OMAP_INT_24XX_SDMA_IRQ0 12
+# define OMAP_INT_24XX_SDMA_IRQ1 13
+# define OMAP_INT_24XX_SDMA_IRQ2 14
+# define OMAP_INT_24XX_SDMA_IRQ3 15
+# define OMAP_INT_243X_MCBSP2_IRQ 16
+# define OMAP_INT_243X_MCBSP3_IRQ 17
+# define OMAP_INT_243X_MCBSP4_IRQ 18
+# define OMAP_INT_243X_MCBSP5_IRQ 19
+# define OMAP_INT_24XX_GPMC_IRQ 20
+# define OMAP_INT_24XX_GUFFAW_IRQ 21
+# define OMAP_INT_24XX_IVA_IRQ 22
+# define OMAP_INT_24XX_EAC_IRQ 23
+# define OMAP_INT_24XX_CAM_IRQ 24
+# define OMAP_INT_24XX_DSS_IRQ 25
+# define OMAP_INT_24XX_MAIL_U0_MPU 26
+# define OMAP_INT_24XX_DSP_UMA 27
+# define OMAP_INT_24XX_DSP_MMU 28
+# define OMAP_INT_24XX_GPIO_BANK1 29
+# define OMAP_INT_24XX_GPIO_BANK2 30
+# define OMAP_INT_24XX_GPIO_BANK3 31
+# define OMAP_INT_24XX_GPIO_BANK4 32
+# define OMAP_INT_243X_GPIO_BANK5 33
+# define OMAP_INT_24XX_MAIL_U3_MPU 34
+# define OMAP_INT_24XX_WDT3 35
+# define OMAP_INT_24XX_WDT4 36
+# define OMAP_INT_24XX_GPTIMER1 37
+# define OMAP_INT_24XX_GPTIMER2 38
+# define OMAP_INT_24XX_GPTIMER3 39
+# define OMAP_INT_24XX_GPTIMER4 40
+# define OMAP_INT_24XX_GPTIMER5 41
+# define OMAP_INT_24XX_GPTIMER6 42
+# define OMAP_INT_24XX_GPTIMER7 43
+# define OMAP_INT_24XX_GPTIMER8 44
+# define OMAP_INT_24XX_GPTIMER9 45
+# define OMAP_INT_24XX_GPTIMER10 46
+# define OMAP_INT_24XX_GPTIMER11 47
+# define OMAP_INT_24XX_GPTIMER12 48
+# define OMAP_INT_24XX_PKA_IRQ 50
+# define OMAP_INT_24XX_SHA1MD5_IRQ 51
+# define OMAP_INT_24XX_RNG_IRQ 52
+# define OMAP_INT_24XX_MG_IRQ 53
+# define OMAP_INT_24XX_I2C1_IRQ 56
+# define OMAP_INT_24XX_I2C2_IRQ 57
+# define OMAP_INT_24XX_MCBSP1_IRQ_TX 59
+# define OMAP_INT_24XX_MCBSP1_IRQ_RX 60
+# define OMAP_INT_24XX_MCBSP2_IRQ_TX 62
+# define OMAP_INT_24XX_MCBSP2_IRQ_RX 63
+# define OMAP_INT_243X_MCBSP1_IRQ 64
+# define OMAP_INT_24XX_MCSPI1_IRQ 65
+# define OMAP_INT_24XX_MCSPI2_IRQ 66
+# define OMAP_INT_24XX_SSI1_IRQ0 67
+# define OMAP_INT_24XX_SSI1_IRQ1 68
+# define OMAP_INT_24XX_SSI2_IRQ0 69
+# define OMAP_INT_24XX_SSI2_IRQ1 70
+# define OMAP_INT_24XX_SSI_GDD_IRQ 71
+# define OMAP_INT_24XX_UART1_IRQ 72
+# define OMAP_INT_24XX_UART2_IRQ 73
+# define OMAP_INT_24XX_UART3_IRQ 74
+# define OMAP_INT_24XX_USB_IRQ_GEN 75
+# define OMAP_INT_24XX_USB_IRQ_NISO 76
+# define OMAP_INT_24XX_USB_IRQ_ISO 77
+# define OMAP_INT_24XX_USB_IRQ_HGEN 78
+# define OMAP_INT_24XX_USB_IRQ_HSOF 79
+# define OMAP_INT_24XX_USB_IRQ_OTG 80
+# define OMAP_INT_24XX_VLYNQ_IRQ 81
+# define OMAP_INT_24XX_MMC_IRQ 83
+# define OMAP_INT_24XX_MS_IRQ 84
+# define OMAP_INT_24XX_FAC_IRQ 85
+# define OMAP_INT_24XX_MCSPI3_IRQ 91
+# define OMAP_INT_243X_HS_USB_MC 92
+# define OMAP_INT_243X_HS_USB_DMA 93
+# define OMAP_INT_243X_CARKIT 94
+# define OMAP_INT_34XX_GPTIMER12 95
+
+/* omap_dma.c */
+enum omap_dma_model {
+ omap_dma_3_0,
+ omap_dma_3_1,
+ omap_dma_3_2,
+ omap_dma_4,
+};
+
+struct soc_dma_s;
+struct soc_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+ qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk,
+ enum omap_dma_model model);
+struct soc_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs,
+ struct omap_mpu_state_s *mpu, int fifo,
+ int chans, omap_clk iclk, omap_clk fclk);
+void omap_dma_reset(struct soc_dma_s *s);
+
+struct dma_irq_map {
+ int ih;
+ int intr;
+};
+
+/* Only used in OMAP DMA 3.x gigacells */
+enum omap_dma_port {
+ emiff = 0,
+ emifs,
+ imif, /* omap16xx: ocp_t1 */
+ tipb,
+ local, /* omap16xx: ocp_t2 */
+ tipb_mpui,
+ __omap_dma_port_last,
+};
+
+typedef enum {
+ constant = 0,
+ post_incremented,
+ single_index,
+ double_index,
+} omap_dma_addressing_t;
+
+/* Only used in OMAP DMA 3.x gigacells */
+struct omap_dma_lcd_channel_s {
+ enum omap_dma_port src;
+ target_phys_addr_t src_f1_top;
+ target_phys_addr_t src_f1_bottom;
+ target_phys_addr_t src_f2_top;
+ target_phys_addr_t src_f2_bottom;
+
+ /* Used in OMAP DMA 3.2 gigacell */
+ unsigned char brust_f1;
+ unsigned char pack_f1;
+ unsigned char data_type_f1;
+ unsigned char brust_f2;
+ unsigned char pack_f2;
+ unsigned char data_type_f2;
+ unsigned char end_prog;
+ unsigned char repeat;
+ unsigned char auto_init;
+ unsigned char priority;
+ unsigned char fs;
+ unsigned char running;
+ unsigned char bs;
+ unsigned char omap_3_1_compatible_disable;
+ unsigned char dst;
+ unsigned char lch_type;
+ int16_t element_index_f1;
+ int16_t element_index_f2;
+ int32_t frame_index_f1;
+ int32_t frame_index_f2;
+ uint16_t elements_f1;
+ uint16_t frames_f1;
+ uint16_t elements_f2;
+ uint16_t frames_f2;
+ omap_dma_addressing_t mode_f1;
+ omap_dma_addressing_t mode_f2;
+
+ /* Destination port is fixed. */
+ int interrupts;
+ int condition;
+ int dual;
+
+ int current_frame;
+ target_phys_addr_t phys_framebuffer[2];
+ qemu_irq irq;
+ struct omap_mpu_state_s *mpu;
+} *omap_dma_get_lcdch(struct soc_dma_s *s);
+
+/*
+ * DMA request numbers for OMAP1
+ * See /usr/include/asm-arm/arch-omap/dma.h in Linux.
+ */
+# define OMAP_DMA_NO_DEVICE 0
+# define OMAP_DMA_MCSI1_TX 1
+# define OMAP_DMA_MCSI1_RX 2
+# define OMAP_DMA_I2C_RX 3
+# define OMAP_DMA_I2C_TX 4
+# define OMAP_DMA_EXT_NDMA_REQ0 5
+# define OMAP_DMA_EXT_NDMA_REQ1 6
+# define OMAP_DMA_UWIRE_TX 7
+# define OMAP_DMA_MCBSP1_TX 8
+# define OMAP_DMA_MCBSP1_RX 9
+# define OMAP_DMA_MCBSP3_TX 10
+# define OMAP_DMA_MCBSP3_RX 11
+# define OMAP_DMA_UART1_TX 12
+# define OMAP_DMA_UART1_RX 13
+# define OMAP_DMA_UART2_TX 14
+# define OMAP_DMA_UART2_RX 15
+# define OMAP_DMA_MCBSP2_TX 16
+# define OMAP_DMA_MCBSP2_RX 17
+# define OMAP_DMA_UART3_TX 18
+# define OMAP_DMA_UART3_RX 19
+# define OMAP_DMA_CAMERA_IF_RX 20
+# define OMAP_DMA_MMC_TX 21
+# define OMAP_DMA_MMC_RX 22
+# define OMAP_DMA_NAND 23 /* Not in OMAP310 */
+# define OMAP_DMA_IRQ_LCD_LINE 24 /* Not in OMAP310 */
+# define OMAP_DMA_MEMORY_STICK 25 /* Not in OMAP310 */
+# define OMAP_DMA_USB_W2FC_RX0 26
+# define OMAP_DMA_USB_W2FC_RX1 27
+# define OMAP_DMA_USB_W2FC_RX2 28
+# define OMAP_DMA_USB_W2FC_TX0 29
+# define OMAP_DMA_USB_W2FC_TX1 30
+# define OMAP_DMA_USB_W2FC_TX2 31
+
+/* These are only for 1610 */
+# define OMAP_DMA_CRYPTO_DES_IN 32
+# define OMAP_DMA_SPI_TX 33
+# define OMAP_DMA_SPI_RX 34
+# define OMAP_DMA_CRYPTO_HASH 35
+# define OMAP_DMA_CCP_ATTN 36
+# define OMAP_DMA_CCP_FIFO_NOT_EMPTY 37
+# define OMAP_DMA_CMT_APE_TX_CHAN_0 38
+# define OMAP_DMA_CMT_APE_RV_CHAN_0 39
+# define OMAP_DMA_CMT_APE_TX_CHAN_1 40
+# define OMAP_DMA_CMT_APE_RV_CHAN_1 41
+# define OMAP_DMA_CMT_APE_TX_CHAN_2 42
+# define OMAP_DMA_CMT_APE_RV_CHAN_2 43
+# define OMAP_DMA_CMT_APE_TX_CHAN_3 44
+# define OMAP_DMA_CMT_APE_RV_CHAN_3 45
+# define OMAP_DMA_CMT_APE_TX_CHAN_4 46
+# define OMAP_DMA_CMT_APE_RV_CHAN_4 47
+# define OMAP_DMA_CMT_APE_TX_CHAN_5 48
+# define OMAP_DMA_CMT_APE_RV_CHAN_5 49
+# define OMAP_DMA_CMT_APE_TX_CHAN_6 50
+# define OMAP_DMA_CMT_APE_RV_CHAN_6 51
+# define OMAP_DMA_CMT_APE_TX_CHAN_7 52
+# define OMAP_DMA_CMT_APE_RV_CHAN_7 53
+# define OMAP_DMA_MMC2_TX 54
+# define OMAP_DMA_MMC2_RX 55
+# define OMAP_DMA_CRYPTO_DES_OUT 56
+
+/*
+ * DMA request numbers for the OMAP2
+ */
+# define OMAP24XX_DMA_NO_DEVICE 0
+# define OMAP24XX_DMA_XTI_DMA 1 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_EXT_DMAREQ0 2
+# define OMAP24XX_DMA_EXT_DMAREQ1 3
+# define OMAP24XX_DMA_GPMC 4
+# define OMAP24XX_DMA_GFX 5 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_DSS 6
+# define OMAP24XX_DMA_VLYNQ_TX 7 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_CWT 8 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_AES_TX 9 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_AES_RX 10 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_DES_TX 11 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_DES_RX 12 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_SHA1MD5_RX 13 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_EXT_DMAREQ2 14
+# define OMAP24XX_DMA_EXT_DMAREQ3 15
+# define OMAP24XX_DMA_EXT_DMAREQ4 16
+# define OMAP24XX_DMA_EAC_AC_RD 17
+# define OMAP24XX_DMA_EAC_AC_WR 18
+# define OMAP24XX_DMA_EAC_MD_UL_RD 19
+# define OMAP24XX_DMA_EAC_MD_UL_WR 20
+# define OMAP24XX_DMA_EAC_MD_DL_RD 21
+# define OMAP24XX_DMA_EAC_MD_DL_WR 22
+# define OMAP24XX_DMA_EAC_BT_UL_RD 23
+# define OMAP24XX_DMA_EAC_BT_UL_WR 24
+# define OMAP24XX_DMA_EAC_BT_DL_RD 25
+# define OMAP24XX_DMA_EAC_BT_DL_WR 26
+# define OMAP24XX_DMA_I2C1_TX 27
+# define OMAP24XX_DMA_I2C1_RX 28
+# define OMAP24XX_DMA_I2C2_TX 29
+# define OMAP24XX_DMA_I2C2_RX 30
+# define OMAP24XX_DMA_MCBSP1_TX 31
+# define OMAP24XX_DMA_MCBSP1_RX 32
+# define OMAP24XX_DMA_MCBSP2_TX 33
+# define OMAP24XX_DMA_MCBSP2_RX 34
+# define OMAP24XX_DMA_SPI1_TX0 35
+# define OMAP24XX_DMA_SPI1_RX0 36
+# define OMAP24XX_DMA_SPI1_TX1 37
+# define OMAP24XX_DMA_SPI1_RX1 38
+# define OMAP24XX_DMA_SPI1_TX2 39
+# define OMAP24XX_DMA_SPI1_RX2 40
+# define OMAP24XX_DMA_SPI1_TX3 41
+# define OMAP24XX_DMA_SPI1_RX3 42
+# define OMAP24XX_DMA_SPI2_TX0 43
+# define OMAP24XX_DMA_SPI2_RX0 44
+# define OMAP24XX_DMA_SPI2_TX1 45
+# define OMAP24XX_DMA_SPI2_RX1 46
+
+# define OMAP24XX_DMA_UART1_TX 49
+# define OMAP24XX_DMA_UART1_RX 50
+# define OMAP24XX_DMA_UART2_TX 51
+# define OMAP24XX_DMA_UART2_RX 52
+# define OMAP24XX_DMA_UART3_TX 53
+# define OMAP24XX_DMA_UART3_RX 54
+# define OMAP24XX_DMA_USB_W2FC_TX0 55
+# define OMAP24XX_DMA_USB_W2FC_RX0 56
+# define OMAP24XX_DMA_USB_W2FC_TX1 57
+# define OMAP24XX_DMA_USB_W2FC_RX1 58
+# define OMAP24XX_DMA_USB_W2FC_TX2 59
+# define OMAP24XX_DMA_USB_W2FC_RX2 60
+# define OMAP24XX_DMA_MMC1_TX 61
+# define OMAP24XX_DMA_MMC1_RX 62
+# define OMAP24XX_DMA_MS 63 /* Not in OMAP2420 */
+# define OMAP24XX_DMA_EXT_DMAREQ5 64
+
+/* omap[123].c */
+/* OMAP2 gp timer */
+struct omap_gp_timer_s;
+struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk);
+void omap_gp_timer_reset(struct omap_gp_timer_s *s);
+
+/* OMAP2 sysctimer */
+struct omap_synctimer_s;
+struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta,
+ struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk);
+void omap_synctimer_reset(struct omap_synctimer_s *s);
+
+struct omap_uart_s;
+struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk,
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr);
+struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk,
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr);
+void omap_uart_reset(struct omap_uart_s *s);
+void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr);
+
+struct omap_mpuio_s;
+struct omap_mpuio_s *omap_mpuio_init(target_phys_addr_t base,
+ qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup,
+ omap_clk clk);
+qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s);
+void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler);
+void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down);
+
+/* omap1 gpio module interface */
+struct omap_gpio_s;
+struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk clk);
+void omap_gpio_reset(struct omap_gpio_s *s);
+qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s);
+void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler);
+
+/* omap2 gpio interface */
+struct omap_gpif_s;
+struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta,
+ qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules);
+void omap_gpif_reset(struct omap_gpif_s *s);
+qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start);
+void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler);
+
+struct uWireSlave {
+ uint16_t (*receive)(void *opaque);
+ void (*send)(void *opaque, uint16_t data);
+ void *opaque;
+};
+struct omap_uwire_s;
+struct omap_uwire_s *omap_uwire_init(target_phys_addr_t base,
+ qemu_irq *irq, qemu_irq dma, omap_clk clk);
+void omap_uwire_attach(struct omap_uwire_s *s,
+ uWireSlave *slave, int chipselect);
+
+/* OMAP2 spi */
+struct omap_mcspi_s;
+struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
+ qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk);
+void omap_mcspi_attach(struct omap_mcspi_s *s,
+ uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
+ int chipselect);
+void omap_mcspi_reset(struct omap_mcspi_s *s);
+
+struct I2SCodec {
+ void *opaque;
+
+ /* The CPU can call this if it is generating the clock signal on the
+ * i2s port. The CODEC can ignore it if it is set up as a clock
+ * master and generates its own clock. */
+ void (*set_rate)(void *opaque, int in, int out);
+
+ void (*tx_swallow)(void *opaque);
+ qemu_irq rx_swallow;
+ qemu_irq tx_start;
+
+ int tx_rate;
+ int cts;
+ int rx_rate;
+ int rts;
+
+ struct i2s_fifo_s {
+ uint8_t *fifo;
+ int len;
+ int start;
+ int size;
+ } in, out;
+};
+struct omap_mcbsp_s;
+struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base,
+ qemu_irq *irq, qemu_irq *dma, omap_clk clk);
+void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave);
+
+void omap_tap_init(struct omap_target_agent_s *ta,
+ struct omap_mpu_state_s *mpu);
+
+/* omap_lcdc.c */
+struct omap_lcd_panel_s;
+void omap_lcdc_reset(struct omap_lcd_panel_s *s);
+struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
+ struct omap_dma_lcd_channel_s *dma,
+ ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk);
+
+/* omap_dss.c */
+struct rfbi_chip_s {
+ void *opaque;
+ void (*write)(void *opaque, int dc, uint16_t value);
+ void (*block)(void *opaque, int dc, void *buf, size_t len, int pitch);
+ uint16_t (*read)(void *opaque, int dc);
+};
+struct omap_dss_s;
+void omap_dss_reset(struct omap_dss_s *s);
+struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta,
+ target_phys_addr_t l3_base,
+ qemu_irq irq, qemu_irq drq,
+ omap_clk fck1, omap_clk fck2, omap_clk ck54m,
+ omap_clk ick1, omap_clk ick2);
+void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip);
+
+/* omap_mmc.c */
+struct omap_mmc_s;
+struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
+ BlockDriverState *bd,
+ qemu_irq irq, qemu_irq dma[], omap_clk clk);
+struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
+ BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
+ omap_clk fclk, omap_clk iclk);
+void omap_mmc_reset(struct omap_mmc_s *s);
+void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover);
+void omap_mmc_enable(struct omap_mmc_s *s, int enable);
+
+/* omap_i2c.c */
+struct omap_i2c_s;
+struct omap_i2c_s *omap_i2c_init(target_phys_addr_t base,
+ qemu_irq irq, qemu_irq *dma, omap_clk clk);
+struct omap_i2c_s *omap2_i2c_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, qemu_irq *dma, omap_clk fclk, omap_clk iclk);
+void omap_i2c_reset(struct omap_i2c_s *s);
+i2c_bus *omap_i2c_bus(struct omap_i2c_s *s);
+
+# define cpu_is_omap310(cpu) (cpu->mpu_model == omap310)
+# define cpu_is_omap1510(cpu) (cpu->mpu_model == omap1510)
+# define cpu_is_omap1610(cpu) (cpu->mpu_model == omap1610)
+# define cpu_is_omap1710(cpu) (cpu->mpu_model == omap1710)
+# define cpu_is_omap2410(cpu) (cpu->mpu_model == omap2410)
+# define cpu_is_omap2420(cpu) (cpu->mpu_model == omap2420)
+# define cpu_is_omap2430(cpu) (cpu->mpu_model == omap2430)
+# define cpu_is_omap3430(cpu) (cpu->mpu_model == omap3430)
+
+# define cpu_is_omap15xx(cpu) \
+ (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu))
+# define cpu_is_omap16xx(cpu) \
+ (cpu_is_omap1610(cpu) || cpu_is_omap1710(cpu))
+# define cpu_is_omap24xx(cpu) \
+ (cpu_is_omap2410(cpu) || cpu_is_omap2420(cpu) || cpu_is_omap2430(cpu))
+
+# define cpu_class_omap1(cpu) \
+ (cpu_is_omap15xx(cpu) || cpu_is_omap16xx(cpu))
+# define cpu_class_omap2(cpu) cpu_is_omap24xx(cpu)
+# define cpu_class_omap3(cpu) cpu_is_omap3430(cpu)
+
+struct omap_mpu_state_s {
+ enum omap_mpu_model {
+ omap310,
+ omap1510,
+ omap1610,
+ omap1710,
+ omap2410,
+ omap2420,
+ omap2422,
+ omap2423,
+ omap2430,
+ omap3430,
+ } mpu_model;
+
+ CPUState *env;
+
+ qemu_irq *irq[2];
+ qemu_irq *drq;
+
+ qemu_irq wakeup;
+
+ struct omap_dma_port_if_s {
+ uint32_t (*read[3])(struct omap_mpu_state_s *s,
+ target_phys_addr_t offset);
+ void (*write[3])(struct omap_mpu_state_s *s,
+ target_phys_addr_t offset, uint32_t value);
+ int (*addr_valid)(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr);
+ } port[__omap_dma_port_last];
+
+ unsigned long sdram_size;
+ unsigned long sram_size;
+
+ /* MPUI-TIPB peripherals */
+ struct omap_uart_s *uart[3];
+
+ struct omap_gpio_s *gpio;
+
+ struct omap_mcbsp_s *mcbsp1;
+ struct omap_mcbsp_s *mcbsp3;
+
+ /* MPU public TIPB peripherals */
+ struct omap_32khz_timer_s *os_timer;
+
+ struct omap_mmc_s *mmc;
+
+ struct omap_mpuio_s *mpuio;
+
+ struct omap_uwire_s *microwire;
+
+ struct {
+ uint8_t output;
+ uint8_t level;
+ uint8_t enable;
+ int clk;
+ } pwl;
+
+ struct {
+ uint8_t frc;
+ uint8_t vrc;
+ uint8_t gcr;
+ omap_clk clk;
+ } pwt;
+
+ struct omap_i2c_s *i2c[2];
+
+ struct omap_rtc_s *rtc;
+
+ struct omap_mcbsp_s *mcbsp2;
+
+ struct omap_lpg_s *led[2];
+
+ /* MPU private TIPB peripherals */
+ struct omap_intr_handler_s *ih[2];
+
+ struct soc_dma_s *dma;
+
+ struct omap_mpu_timer_s *timer[3];
+ struct omap_watchdog_timer_s *wdt;
+
+ struct omap_lcd_panel_s *lcd;
+
+ uint32_t ulpd_pm_regs[21];
+ int64_t ulpd_gauge_start;
+
+ uint32_t func_mux_ctrl[14];
+ uint32_t comp_mode_ctrl[1];
+ uint32_t pull_dwn_ctrl[4];
+ uint32_t gate_inh_ctrl[1];
+ uint32_t voltage_ctrl[1];
+ uint32_t test_dbg_ctrl[1];
+ uint32_t mod_conf_ctrl[1];
+ int compat1509;
+
+ uint32_t mpui_ctrl;
+
+ struct omap_tipb_bridge_s *private_tipb;
+ struct omap_tipb_bridge_s *public_tipb;
+
+ uint32_t tcmi_regs[17];
+
+ struct dpll_ctl_s {
+ uint16_t mode;
+ omap_clk dpll;
+ } dpll[3];
+
+ omap_clk clks;
+ struct {
+ int cold_start;
+ int clocking_scheme;
+ uint16_t arm_ckctl;
+ uint16_t arm_idlect1;
+ uint16_t arm_idlect2;
+ uint16_t arm_ewupct;
+ uint16_t arm_rstct1;
+ uint16_t arm_rstct2;
+ uint16_t arm_ckout1;
+ int dpll1_mode;
+ uint16_t dsp_idlect1;
+ uint16_t dsp_idlect2;
+ uint16_t dsp_rstct2;
+ } clkm;
+
+ /* OMAP2-only peripherals */
+ struct omap_l4_s *l4;
+
+ struct omap_gp_timer_s *gptimer[12];
+ struct omap_synctimer_s *synctimer;
+
+ struct omap_prcm_s *prcm;
+ struct omap_sdrc_s *sdrc;
+ struct omap_gpmc_s *gpmc;
+ struct omap_sysctl_s *sysc;
+
+ struct omap_gpif_s *gpif;
+
+ struct omap_mcspi_s *mcspi[2];
+
+ struct omap_dss_s *dss;
+
+ struct omap_eac_s *eac;
+};
+
+/* omap1.c */
+struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+ const char *core);
+
+/* omap2.c */
+struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
+ const char *core);
+
+# if TARGET_PHYS_ADDR_BITS == 32
+# define OMAP_FMT_plx "%#08x"
+# elif TARGET_PHYS_ADDR_BITS == 64
+# define OMAP_FMT_plx "%#08" PRIx64
+# else
+# error TARGET_PHYS_ADDR_BITS undefined
+# endif
+
+uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr);
+void omap_badwidth_write8(void *opaque, target_phys_addr_t addr,
+ uint32_t value);
+uint32_t omap_badwidth_read16(void *opaque, target_phys_addr_t addr);
+void omap_badwidth_write16(void *opaque, target_phys_addr_t addr,
+ uint32_t value);
+uint32_t omap_badwidth_read32(void *opaque, target_phys_addr_t addr);
+void omap_badwidth_write32(void *opaque, target_phys_addr_t addr,
+ uint32_t value);
+
+void omap_mpu_wakeup(void *opaque, int irq, int req);
+
+# define OMAP_BAD_REG(paddr) \
+ fprintf(stderr, "%s: Bad register " OMAP_FMT_plx "\n", \
+ __FUNCTION__, paddr)
+# define OMAP_RO_REG(paddr) \
+ fprintf(stderr, "%s: Read-only register " OMAP_FMT_plx "\n", \
+ __FUNCTION__, paddr)
+
+/* OMAP-specific Linux bootloader tags for the ATAG_BOARD area
+ (Board-specifc tags are not here) */
+#define OMAP_TAG_CLOCK 0x4f01
+#define OMAP_TAG_MMC 0x4f02
+#define OMAP_TAG_SERIAL_CONSOLE 0x4f03
+#define OMAP_TAG_USB 0x4f04
+#define OMAP_TAG_LCD 0x4f05
+#define OMAP_TAG_GPIO_SWITCH 0x4f06
+#define OMAP_TAG_UART 0x4f07
+#define OMAP_TAG_FBMEM 0x4f08
+#define OMAP_TAG_STI_CONSOLE 0x4f09
+#define OMAP_TAG_CAMERA_SENSOR 0x4f0a
+#define OMAP_TAG_PARTITION 0x4f0b
+#define OMAP_TAG_TEA5761 0x4f10
+#define OMAP_TAG_TMP105 0x4f11
+#define OMAP_TAG_BOOT_REASON 0x4f80
+#define OMAP_TAG_FLASH_PART_STR 0x4f81
+#define OMAP_TAG_VERSION_STR 0x4f82
+
+enum {
+ OMAP_GPIOSW_TYPE_COVER = 0 << 4,
+ OMAP_GPIOSW_TYPE_CONNECTION = 1 << 4,
+ OMAP_GPIOSW_TYPE_ACTIVITY = 2 << 4,
+};
+
+#define OMAP_GPIOSW_INVERTED 0x0001
+#define OMAP_GPIOSW_OUTPUT 0x0002
+
+# define TCMI_VERBOSE 1
+//# define MEM_VERBOSE 1
+
+# ifdef TCMI_VERBOSE
+# define OMAP_8B_REG(paddr) \
+ fprintf(stderr, "%s: 8-bit register " OMAP_FMT_plx "\n", \
+ __FUNCTION__, paddr)
+# define OMAP_16B_REG(paddr) \
+ fprintf(stderr, "%s: 16-bit register " OMAP_FMT_plx "\n", \
+ __FUNCTION__, paddr)
+# define OMAP_32B_REG(paddr) \
+ fprintf(stderr, "%s: 32-bit register " OMAP_FMT_plx "\n", \
+ __FUNCTION__, paddr)
+# else
+# define OMAP_8B_REG(paddr)
+# define OMAP_16B_REG(paddr)
+# define OMAP_32B_REG(paddr)
+# endif
+
+# define OMAP_MPUI_REG_MASK 0x000007ff
+
+# ifdef MEM_VERBOSE
+struct io_fn {
+ CPUReadMemoryFunc * const *mem_read;
+ CPUWriteMemoryFunc * const *mem_write;
+ void *opaque;
+ int in;
+};
+
+static uint32_t io_readb(void *opaque, target_phys_addr_t addr)
+{
+ struct io_fn *s = opaque;
+ uint32_t ret;
+
+ s->in ++;
+ ret = s->mem_read[0](s->opaque, addr);
+ s->in --;
+ if (!s->in)
+ fprintf(stderr, "%08x ---> %02x\n", (uint32_t) addr, ret);
+ return ret;
+}
+static uint32_t io_readh(void *opaque, target_phys_addr_t addr)
+{
+ struct io_fn *s = opaque;
+ uint32_t ret;
+
+ s->in ++;
+ ret = s->mem_read[1](s->opaque, addr);
+ s->in --;
+ if (!s->in)
+ fprintf(stderr, "%08x ---> %04x\n", (uint32_t) addr, ret);
+ return ret;
+}
+static uint32_t io_readw(void *opaque, target_phys_addr_t addr)
+{
+ struct io_fn *s = opaque;
+ uint32_t ret;
+
+ s->in ++;
+ ret = s->mem_read[2](s->opaque, addr);
+ s->in --;
+ if (!s->in)
+ fprintf(stderr, "%08x ---> %08x\n", (uint32_t) addr, ret);
+ return ret;
+}
+static void io_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct io_fn *s = opaque;
+
+ if (!s->in)
+ fprintf(stderr, "%08x <--- %02x\n", (uint32_t) addr, value);
+ s->in ++;
+ s->mem_write[0](s->opaque, addr, value);
+ s->in --;
+}
+static void io_writeh(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct io_fn *s = opaque;
+
+ if (!s->in)
+ fprintf(stderr, "%08x <--- %04x\n", (uint32_t) addr, value);
+ s->in ++;
+ s->mem_write[1](s->opaque, addr, value);
+ s->in --;
+}
+static void io_writew(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct io_fn *s = opaque;
+
+ if (!s->in)
+ fprintf(stderr, "%08x <--- %08x\n", (uint32_t) addr, value);
+ s->in ++;
+ s->mem_write[2](s->opaque, addr, value);
+ s->in --;
+}
+
+static CPUReadMemoryFunc * const io_readfn[] = { io_readb, io_readh, io_readw, };
+static CPUWriteMemoryFunc * const io_writefn[] = { io_writeb, io_writeh, io_writew, };
+
+inline static int debug_register_io_memory(CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write,
+ void *opaque)
+{
+ struct io_fn *s = qemu_malloc(sizeof(struct io_fn));
+
+ s->mem_read = mem_read;
+ s->mem_write = mem_write;
+ s->opaque = opaque;
+ s->in = 0;
+ return cpu_register_io_memory(io_readfn, io_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+}
+# define cpu_register_io_memory debug_register_io_memory
+# endif
+
+/* Define when we want to reduce the number of IO regions registered. */
+/*# define L4_MUX_HACK*/
+
+#endif /* hw_omap_h */
diff --git a/hw/omap1.c b/hw/omap1.c
new file mode 100644
index 0000000..d5e4dab
--- /dev/null
+++ b/hw/omap1.c
@@ -0,0 +1,3894 @@
+/*
+ * TI OMAP processors emulation.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "arm-misc.h"
+#include "omap.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "soc_dma.h"
+/* We use pc-style serial ports. */
+#include "pc.h"
+#include "blockdev.h"
+#include "range.h"
+
+/* Should signal the TCMI/GPMC */
+uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr)
+{
+ uint8_t ret;
+
+ OMAP_8B_REG(addr);
+ cpu_physical_memory_read(addr, (void *) &ret, 1);
+ return ret;
+}
+
+void omap_badwidth_write8(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ uint8_t val8 = value;
+
+ OMAP_8B_REG(addr);
+ cpu_physical_memory_write(addr, (void *) &val8, 1);
+}
+
+uint32_t omap_badwidth_read16(void *opaque, target_phys_addr_t addr)
+{
+ uint16_t ret;
+
+ OMAP_16B_REG(addr);
+ cpu_physical_memory_read(addr, (void *) &ret, 2);
+ return ret;
+}
+
+void omap_badwidth_write16(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ uint16_t val16 = value;
+
+ OMAP_16B_REG(addr);
+ cpu_physical_memory_write(addr, (void *) &val16, 2);
+}
+
+uint32_t omap_badwidth_read32(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ OMAP_32B_REG(addr);
+ cpu_physical_memory_read(addr, (void *) &ret, 4);
+ return ret;
+}
+
+void omap_badwidth_write32(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ OMAP_32B_REG(addr);
+ cpu_physical_memory_write(addr, (void *) &value, 4);
+}
+
+/* MPU OS timers */
+struct omap_mpu_timer_s {
+ qemu_irq irq;
+ omap_clk clk;
+ uint32_t val;
+ int64_t time;
+ QEMUTimer *timer;
+ QEMUBH *tick;
+ int64_t rate;
+ int it_ena;
+
+ int enable;
+ int ptv;
+ int ar;
+ int st;
+ uint32_t reset_val;
+};
+
+static inline uint32_t omap_timer_read(struct omap_mpu_timer_s *timer)
+{
+ uint64_t distance = qemu_get_clock(vm_clock) - timer->time;
+
+ if (timer->st && timer->enable && timer->rate)
+ return timer->val - muldiv64(distance >> (timer->ptv + 1),
+ timer->rate, get_ticks_per_sec());
+ else
+ return timer->val;
+}
+
+static inline void omap_timer_sync(struct omap_mpu_timer_s *timer)
+{
+ timer->val = omap_timer_read(timer);
+ timer->time = qemu_get_clock(vm_clock);
+}
+
+static inline void omap_timer_update(struct omap_mpu_timer_s *timer)
+{
+ int64_t expires;
+
+ if (timer->enable && timer->st && timer->rate) {
+ timer->val = timer->reset_val; /* Should skip this on clk enable */
+ expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1),
+ get_ticks_per_sec(), timer->rate);
+
+ /* If timer expiry would be sooner than in about 1 ms and
+ * auto-reload isn't set, then fire immediately. This is a hack
+ * to make systems like PalmOS run in acceptable time. PalmOS
+ * sets the interval to a very low value and polls the status bit
+ * in a busy loop when it wants to sleep just a couple of CPU
+ * ticks. */
+ if (expires > (get_ticks_per_sec() >> 10) || timer->ar)
+ qemu_mod_timer(timer->timer, timer->time + expires);
+ else
+ qemu_bh_schedule(timer->tick);
+ } else
+ qemu_del_timer(timer->timer);
+}
+
+static void omap_timer_fire(void *opaque)
+{
+ struct omap_mpu_timer_s *timer = opaque;
+
+ if (!timer->ar) {
+ timer->val = 0;
+ timer->st = 0;
+ }
+
+ if (timer->it_ena)
+ /* Edge-triggered irq */
+ qemu_irq_pulse(timer->irq);
+}
+
+static void omap_timer_tick(void *opaque)
+{
+ struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque;
+
+ omap_timer_sync(timer);
+ omap_timer_fire(timer);
+ omap_timer_update(timer);
+}
+
+static void omap_timer_clk_update(void *opaque, int line, int on)
+{
+ struct omap_mpu_timer_s *timer = (struct omap_mpu_timer_s *) opaque;
+
+ omap_timer_sync(timer);
+ timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
+ omap_timer_update(timer);
+}
+
+static void omap_timer_clk_setup(struct omap_mpu_timer_s *timer)
+{
+ omap_clk_adduser(timer->clk,
+ qemu_allocate_irqs(omap_timer_clk_update, timer, 1)[0]);
+ timer->rate = omap_clk_getrate(timer->clk);
+}
+
+static uint32_t omap_mpu_timer_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ return (s->enable << 5) | (s->ptv << 2) | (s->ar << 1) | s->st;
+
+ case 0x04: /* LOAD_TIM */
+ break;
+
+ case 0x08: /* READ_TIM */
+ return omap_timer_read(s);
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpu_timer_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ omap_timer_sync(s);
+ s->enable = (value >> 5) & 1;
+ s->ptv = (value >> 2) & 7;
+ s->ar = (value >> 1) & 1;
+ s->st = value & 1;
+ omap_timer_update(s);
+ return;
+
+ case 0x04: /* LOAD_TIM */
+ s->reset_val = value;
+ return;
+
+ case 0x08: /* READ_TIM */
+ OMAP_RO_REG(addr);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_mpu_timer_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_mpu_timer_read,
+};
+
+static CPUWriteMemoryFunc * const omap_mpu_timer_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_mpu_timer_write,
+};
+
+static void omap_mpu_timer_reset(struct omap_mpu_timer_s *s)
+{
+ qemu_del_timer(s->timer);
+ s->enable = 0;
+ s->reset_val = 31337;
+ s->val = 0;
+ s->ptv = 0;
+ s->ar = 0;
+ s->st = 0;
+ s->it_ena = 1;
+}
+
+static struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_mpu_timer_s *s = (struct omap_mpu_timer_s *)
+ qemu_mallocz(sizeof(struct omap_mpu_timer_s));
+
+ s->irq = irq;
+ s->clk = clk;
+ s->timer = qemu_new_timer(vm_clock, omap_timer_tick, s);
+ s->tick = qemu_bh_new(omap_timer_fire, s);
+ omap_mpu_timer_reset(s);
+ omap_timer_clk_setup(s);
+
+ iomemtype = cpu_register_io_memory(omap_mpu_timer_readfn,
+ omap_mpu_timer_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+
+ return s;
+}
+
+/* Watchdog timer */
+struct omap_watchdog_timer_s {
+ struct omap_mpu_timer_s timer;
+ uint8_t last_wr;
+ int mode;
+ int free;
+ int reset;
+};
+
+static uint32_t omap_wd_timer_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ return (s->timer.ptv << 9) | (s->timer.ar << 8) |
+ (s->timer.st << 7) | (s->free << 1);
+
+ case 0x04: /* READ_TIMER */
+ return omap_timer_read(&s->timer);
+
+ case 0x08: /* TIMER_MODE */
+ return s->mode << 15;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_wd_timer_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* CNTL_TIMER */
+ omap_timer_sync(&s->timer);
+ s->timer.ptv = (value >> 9) & 7;
+ s->timer.ar = (value >> 8) & 1;
+ s->timer.st = (value >> 7) & 1;
+ s->free = (value >> 1) & 1;
+ omap_timer_update(&s->timer);
+ break;
+
+ case 0x04: /* LOAD_TIMER */
+ s->timer.reset_val = value & 0xffff;
+ break;
+
+ case 0x08: /* TIMER_MODE */
+ if (!s->mode && ((value >> 15) & 1))
+ omap_clk_get(s->timer.clk);
+ s->mode |= (value >> 15) & 1;
+ if (s->last_wr == 0xf5) {
+ if ((value & 0xff) == 0xa0) {
+ if (s->mode) {
+ s->mode = 0;
+ omap_clk_put(s->timer.clk);
+ }
+ } else {
+ /* XXX: on T|E hardware somehow this has no effect,
+ * on Zire 71 it works as specified. */
+ s->reset = 1;
+ qemu_system_reset_request();
+ }
+ }
+ s->last_wr = value & 0xff;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_wd_timer_readfn[] = {
+ omap_badwidth_read16,
+ omap_wd_timer_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_wd_timer_writefn[] = {
+ omap_badwidth_write16,
+ omap_wd_timer_write,
+ omap_badwidth_write16,
+};
+
+static void omap_wd_timer_reset(struct omap_watchdog_timer_s *s)
+{
+ qemu_del_timer(s->timer.timer);
+ if (!s->mode)
+ omap_clk_get(s->timer.clk);
+ s->mode = 1;
+ s->free = 1;
+ s->reset = 0;
+ s->timer.enable = 1;
+ s->timer.it_ena = 1;
+ s->timer.reset_val = 0xffff;
+ s->timer.val = 0;
+ s->timer.st = 0;
+ s->timer.ptv = 0;
+ s->timer.ar = 0;
+ omap_timer_update(&s->timer);
+}
+
+static struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_watchdog_timer_s *s = (struct omap_watchdog_timer_s *)
+ qemu_mallocz(sizeof(struct omap_watchdog_timer_s));
+
+ s->timer.irq = irq;
+ s->timer.clk = clk;
+ s->timer.timer = qemu_new_timer(vm_clock, omap_timer_tick, &s->timer);
+ omap_wd_timer_reset(s);
+ omap_timer_clk_setup(&s->timer);
+
+ iomemtype = cpu_register_io_memory(omap_wd_timer_readfn,
+ omap_wd_timer_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+
+ return s;
+}
+
+/* 32-kHz timer */
+struct omap_32khz_timer_s {
+ struct omap_mpu_timer_s timer;
+};
+
+static uint32_t omap_os_timer_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* TVR */
+ return s->timer.reset_val;
+
+ case 0x04: /* TCR */
+ return omap_timer_read(&s->timer);
+
+ case 0x08: /* CR */
+ return (s->timer.ar << 3) | (s->timer.it_ena << 2) | s->timer.st;
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_os_timer_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* TVR */
+ s->timer.reset_val = value & 0x00ffffff;
+ break;
+
+ case 0x04: /* TCR */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x08: /* CR */
+ s->timer.ar = (value >> 3) & 1;
+ s->timer.it_ena = (value >> 2) & 1;
+ if (s->timer.st != (value & 1) || (value & 2)) {
+ omap_timer_sync(&s->timer);
+ s->timer.enable = value & 1;
+ s->timer.st = value & 1;
+ omap_timer_update(&s->timer);
+ }
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_os_timer_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_os_timer_read,
+};
+
+static CPUWriteMemoryFunc * const omap_os_timer_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_os_timer_write,
+};
+
+static void omap_os_timer_reset(struct omap_32khz_timer_s *s)
+{
+ qemu_del_timer(s->timer.timer);
+ s->timer.enable = 0;
+ s->timer.it_ena = 0;
+ s->timer.reset_val = 0x00ffffff;
+ s->timer.val = 0;
+ s->timer.st = 0;
+ s->timer.ptv = 0;
+ s->timer.ar = 1;
+}
+
+static struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_32khz_timer_s *s = (struct omap_32khz_timer_s *)
+ qemu_mallocz(sizeof(struct omap_32khz_timer_s));
+
+ s->timer.irq = irq;
+ s->timer.clk = clk;
+ s->timer.timer = qemu_new_timer(vm_clock, omap_timer_tick, &s->timer);
+ omap_os_timer_reset(s);
+ omap_timer_clk_setup(&s->timer);
+
+ iomemtype = cpu_register_io_memory(omap_os_timer_readfn,
+ omap_os_timer_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ return s;
+}
+
+/* Ultra Low-Power Device Module */
+static uint32_t omap_ulpd_pm_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case 0x14: /* IT_STATUS */
+ ret = s->ulpd_pm_regs[addr >> 2];
+ s->ulpd_pm_regs[addr >> 2] = 0;
+ qemu_irq_lower(s->irq[1][OMAP_INT_GAUGE_32K]);
+ return ret;
+
+ case 0x18: /* Reserved */
+ case 0x1c: /* Reserved */
+ case 0x20: /* Reserved */
+ case 0x28: /* Reserved */
+ case 0x2c: /* Reserved */
+ OMAP_BAD_REG(addr);
+ case 0x00: /* COUNTER_32_LSB */
+ case 0x04: /* COUNTER_32_MSB */
+ case 0x08: /* COUNTER_HIGH_FREQ_LSB */
+ case 0x0c: /* COUNTER_HIGH_FREQ_MSB */
+ case 0x10: /* GAUGING_CTRL */
+ case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */
+ case 0x30: /* CLOCK_CTRL */
+ case 0x34: /* SOFT_REQ */
+ case 0x38: /* COUNTER_32_FIQ */
+ case 0x3c: /* DPLL_CTRL */
+ case 0x40: /* STATUS_REQ */
+ /* XXX: check clk::usecount state for every clock */
+ case 0x48: /* LOCL_TIME */
+ case 0x4c: /* APLL_CTRL */
+ case 0x50: /* POWER_CTRL */
+ return s->ulpd_pm_regs[addr >> 2];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_ulpd_clk_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ if (diff & (1 << 4)) /* USB_MCLK_EN */
+ omap_clk_onoff(omap_findclk(s, "usb_clk0"), (value >> 4) & 1);
+ if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */
+ omap_clk_onoff(omap_findclk(s, "usb_w2fc_ck"), (~value >> 5) & 1);
+}
+
+static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ if (diff & (1 << 0)) /* SOFT_DPLL_REQ */
+ omap_clk_canidle(omap_findclk(s, "dpll4"), (~value >> 0) & 1);
+ if (diff & (1 << 1)) /* SOFT_COM_REQ */
+ omap_clk_canidle(omap_findclk(s, "com_mclk_out"), (~value >> 1) & 1);
+ if (diff & (1 << 2)) /* SOFT_SDW_REQ */
+ omap_clk_canidle(omap_findclk(s, "bt_mclk_out"), (~value >> 2) & 1);
+ if (diff & (1 << 3)) /* SOFT_USB_REQ */
+ omap_clk_canidle(omap_findclk(s, "usb_clk0"), (~value >> 3) & 1);
+}
+
+static void omap_ulpd_pm_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ int64_t now, ticks;
+ int div, mult;
+ static const int bypass_div[4] = { 1, 2, 4, 4 };
+ uint16_t diff;
+
+ switch (addr) {
+ case 0x00: /* COUNTER_32_LSB */
+ case 0x04: /* COUNTER_32_MSB */
+ case 0x08: /* COUNTER_HIGH_FREQ_LSB */
+ case 0x0c: /* COUNTER_HIGH_FREQ_MSB */
+ case 0x14: /* IT_STATUS */
+ case 0x40: /* STATUS_REQ */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* GAUGING_CTRL */
+ /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */
+ if ((s->ulpd_pm_regs[addr >> 2] ^ value) & 1) {
+ now = qemu_get_clock(vm_clock);
+
+ if (value & 1)
+ s->ulpd_gauge_start = now;
+ else {
+ now -= s->ulpd_gauge_start;
+
+ /* 32-kHz ticks */
+ ticks = muldiv64(now, 32768, get_ticks_per_sec());
+ s->ulpd_pm_regs[0x00 >> 2] = (ticks >> 0) & 0xffff;
+ s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff;
+ if (ticks >> 32) /* OVERFLOW_32K */
+ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2;
+
+ /* High frequency ticks */
+ ticks = muldiv64(now, 12000000, get_ticks_per_sec());
+ s->ulpd_pm_regs[0x08 >> 2] = (ticks >> 0) & 0xffff;
+ s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff;
+ if (ticks >> 32) /* OVERFLOW_HI_FREQ */
+ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 1;
+
+ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */
+ qemu_irq_raise(s->irq[1][OMAP_INT_GAUGE_32K]);
+ }
+ }
+ s->ulpd_pm_regs[addr >> 2] = value;
+ break;
+
+ case 0x18: /* Reserved */
+ case 0x1c: /* Reserved */
+ case 0x20: /* Reserved */
+ case 0x28: /* Reserved */
+ case 0x2c: /* Reserved */
+ OMAP_BAD_REG(addr);
+ case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */
+ case 0x38: /* COUNTER_32_FIQ */
+ case 0x48: /* LOCL_TIME */
+ case 0x50: /* POWER_CTRL */
+ s->ulpd_pm_regs[addr >> 2] = value;
+ break;
+
+ case 0x30: /* CLOCK_CTRL */
+ diff = s->ulpd_pm_regs[addr >> 2] ^ value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0x3f;
+ omap_ulpd_clk_update(s, diff, value);
+ break;
+
+ case 0x34: /* SOFT_REQ */
+ diff = s->ulpd_pm_regs[addr >> 2] ^ value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0x1f;
+ omap_ulpd_req_update(s, diff, value);
+ break;
+
+ case 0x3c: /* DPLL_CTRL */
+ /* XXX: OMAP310 TRM claims bit 3 is PLL_ENABLE, and bit 4 is
+ * omitted altogether, probably a typo. */
+ /* This register has identical semantics with DPLL(1:3) control
+ * registers, see omap_dpll_write() */
+ diff = s->ulpd_pm_regs[addr >> 2] & value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0x2fff;
+ if (diff & (0x3ff << 2)) {
+ if (value & (1 << 4)) { /* PLL_ENABLE */
+ div = ((value >> 5) & 3) + 1; /* PLL_DIV */
+ mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */
+ } else {
+ div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */
+ mult = 1;
+ }
+ omap_clk_setrate(omap_findclk(s, "dpll4"), div, mult);
+ }
+
+ /* Enter the desired mode. */
+ s->ulpd_pm_regs[addr >> 2] =
+ (s->ulpd_pm_regs[addr >> 2] & 0xfffe) |
+ ((s->ulpd_pm_regs[addr >> 2] >> 4) & 1);
+
+ /* Act as if the lock is restored. */
+ s->ulpd_pm_regs[addr >> 2] |= 2;
+ break;
+
+ case 0x4c: /* APLL_CTRL */
+ diff = s->ulpd_pm_regs[addr >> 2] & value;
+ s->ulpd_pm_regs[addr >> 2] = value & 0xf;
+ if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */
+ omap_clk_reparent(omap_findclk(s, "ck_48m"), omap_findclk(s,
+ (value & (1 << 0)) ? "apll" : "dpll4"));
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_ulpd_pm_readfn[] = {
+ omap_badwidth_read16,
+ omap_ulpd_pm_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_ulpd_pm_writefn[] = {
+ omap_badwidth_write16,
+ omap_ulpd_pm_write,
+ omap_badwidth_write16,
+};
+
+static void omap_ulpd_pm_reset(struct omap_mpu_state_s *mpu)
+{
+ mpu->ulpd_pm_regs[0x00 >> 2] = 0x0001;
+ mpu->ulpd_pm_regs[0x04 >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x08 >> 2] = 0x0001;
+ mpu->ulpd_pm_regs[0x0c >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x10 >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x18 >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x1c >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x20 >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x24 >> 2] = 0x03ff;
+ mpu->ulpd_pm_regs[0x28 >> 2] = 0x01;
+ mpu->ulpd_pm_regs[0x2c >> 2] = 0x01;
+ omap_ulpd_clk_update(mpu, mpu->ulpd_pm_regs[0x30 >> 2], 0x0000);
+ mpu->ulpd_pm_regs[0x30 >> 2] = 0x0000;
+ omap_ulpd_req_update(mpu, mpu->ulpd_pm_regs[0x34 >> 2], 0x0000);
+ mpu->ulpd_pm_regs[0x34 >> 2] = 0x0000;
+ mpu->ulpd_pm_regs[0x38 >> 2] = 0x0001;
+ mpu->ulpd_pm_regs[0x3c >> 2] = 0x2211;
+ mpu->ulpd_pm_regs[0x40 >> 2] = 0x0000; /* FIXME: dump a real STATUS_REQ */
+ mpu->ulpd_pm_regs[0x48 >> 2] = 0x960;
+ mpu->ulpd_pm_regs[0x4c >> 2] = 0x08;
+ mpu->ulpd_pm_regs[0x50 >> 2] = 0x08;
+ omap_clk_setrate(omap_findclk(mpu, "dpll4"), 1, 4);
+ omap_clk_reparent(omap_findclk(mpu, "ck_48m"), omap_findclk(mpu, "dpll4"));
+}
+
+static void omap_ulpd_pm_init(target_phys_addr_t base,
+ struct omap_mpu_state_s *mpu)
+{
+ int iomemtype = cpu_register_io_memory(omap_ulpd_pm_readfn,
+ omap_ulpd_pm_writefn, mpu, DEVICE_NATIVE_ENDIAN);
+
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+ omap_ulpd_pm_reset(mpu);
+}
+
+/* OMAP Pin Configuration */
+static uint32_t omap_pin_cfg_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* FUNC_MUX_CTRL_0 */
+ case 0x04: /* FUNC_MUX_CTRL_1 */
+ case 0x08: /* FUNC_MUX_CTRL_2 */
+ return s->func_mux_ctrl[addr >> 2];
+
+ case 0x0c: /* COMP_MODE_CTRL_0 */
+ return s->comp_mode_ctrl[0];
+
+ case 0x10: /* FUNC_MUX_CTRL_3 */
+ case 0x14: /* FUNC_MUX_CTRL_4 */
+ case 0x18: /* FUNC_MUX_CTRL_5 */
+ case 0x1c: /* FUNC_MUX_CTRL_6 */
+ case 0x20: /* FUNC_MUX_CTRL_7 */
+ case 0x24: /* FUNC_MUX_CTRL_8 */
+ case 0x28: /* FUNC_MUX_CTRL_9 */
+ case 0x2c: /* FUNC_MUX_CTRL_A */
+ case 0x30: /* FUNC_MUX_CTRL_B */
+ case 0x34: /* FUNC_MUX_CTRL_C */
+ case 0x38: /* FUNC_MUX_CTRL_D */
+ return s->func_mux_ctrl[(addr >> 2) - 1];
+
+ case 0x40: /* PULL_DWN_CTRL_0 */
+ case 0x44: /* PULL_DWN_CTRL_1 */
+ case 0x48: /* PULL_DWN_CTRL_2 */
+ case 0x4c: /* PULL_DWN_CTRL_3 */
+ return s->pull_dwn_ctrl[(addr & 0xf) >> 2];
+
+ case 0x50: /* GATE_INH_CTRL_0 */
+ return s->gate_inh_ctrl[0];
+
+ case 0x60: /* VOLTAGE_CTRL_0 */
+ return s->voltage_ctrl[0];
+
+ case 0x70: /* TEST_DBG_CTRL_0 */
+ return s->test_dbg_ctrl[0];
+
+ case 0x80: /* MOD_CONF_CTRL_0 */
+ return s->mod_conf_ctrl[0];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_pin_funcmux0_update(struct omap_mpu_state_s *s,
+ uint32_t diff, uint32_t value)
+{
+ if (s->compat1509) {
+ if (diff & (1 << 9)) /* BLUETOOTH */
+ omap_clk_onoff(omap_findclk(s, "bt_mclk_out"),
+ (~value >> 9) & 1);
+ if (diff & (1 << 7)) /* USB.CLKO */
+ omap_clk_onoff(omap_findclk(s, "usb.clko"),
+ (value >> 7) & 1);
+ }
+}
+
+static inline void omap_pin_funcmux1_update(struct omap_mpu_state_s *s,
+ uint32_t diff, uint32_t value)
+{
+ if (s->compat1509) {
+ if (diff & (1 << 31)) /* MCBSP3_CLK_HIZ_DI */
+ omap_clk_onoff(omap_findclk(s, "mcbsp3.clkx"),
+ (value >> 31) & 1);
+ if (diff & (1 << 1)) /* CLK32K */
+ omap_clk_onoff(omap_findclk(s, "clk32k_out"),
+ (~value >> 1) & 1);
+ }
+}
+
+static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s,
+ uint32_t diff, uint32_t value)
+{
+ if (diff & (1 << 31)) /* CONF_MOD_UART3_CLK_MODE_R */
+ omap_clk_reparent(omap_findclk(s, "uart3_ck"),
+ omap_findclk(s, ((value >> 31) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */
+ omap_clk_reparent(omap_findclk(s, "uart2_ck"),
+ omap_findclk(s, ((value >> 30) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */
+ omap_clk_reparent(omap_findclk(s, "uart1_ck"),
+ omap_findclk(s, ((value >> 29) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */
+ omap_clk_reparent(omap_findclk(s, "mmc_ck"),
+ omap_findclk(s, ((value >> 23) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */
+ omap_clk_reparent(omap_findclk(s, "com_mclk_out"),
+ omap_findclk(s, ((value >> 12) & 1) ?
+ "ck_48m" : "armper_ck"));
+ if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */
+ omap_clk_onoff(omap_findclk(s, "usb_hhc_ck"), (value >> 9) & 1);
+}
+
+static void omap_pin_cfg_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint32_t diff;
+
+ switch (addr) {
+ case 0x00: /* FUNC_MUX_CTRL_0 */
+ diff = s->func_mux_ctrl[addr >> 2] ^ value;
+ s->func_mux_ctrl[addr >> 2] = value;
+ omap_pin_funcmux0_update(s, diff, value);
+ return;
+
+ case 0x04: /* FUNC_MUX_CTRL_1 */
+ diff = s->func_mux_ctrl[addr >> 2] ^ value;
+ s->func_mux_ctrl[addr >> 2] = value;
+ omap_pin_funcmux1_update(s, diff, value);
+ return;
+
+ case 0x08: /* FUNC_MUX_CTRL_2 */
+ s->func_mux_ctrl[addr >> 2] = value;
+ return;
+
+ case 0x0c: /* COMP_MODE_CTRL_0 */
+ s->comp_mode_ctrl[0] = value;
+ s->compat1509 = (value != 0x0000eaef);
+ omap_pin_funcmux0_update(s, ~0, s->func_mux_ctrl[0]);
+ omap_pin_funcmux1_update(s, ~0, s->func_mux_ctrl[1]);
+ return;
+
+ case 0x10: /* FUNC_MUX_CTRL_3 */
+ case 0x14: /* FUNC_MUX_CTRL_4 */
+ case 0x18: /* FUNC_MUX_CTRL_5 */
+ case 0x1c: /* FUNC_MUX_CTRL_6 */
+ case 0x20: /* FUNC_MUX_CTRL_7 */
+ case 0x24: /* FUNC_MUX_CTRL_8 */
+ case 0x28: /* FUNC_MUX_CTRL_9 */
+ case 0x2c: /* FUNC_MUX_CTRL_A */
+ case 0x30: /* FUNC_MUX_CTRL_B */
+ case 0x34: /* FUNC_MUX_CTRL_C */
+ case 0x38: /* FUNC_MUX_CTRL_D */
+ s->func_mux_ctrl[(addr >> 2) - 1] = value;
+ return;
+
+ case 0x40: /* PULL_DWN_CTRL_0 */
+ case 0x44: /* PULL_DWN_CTRL_1 */
+ case 0x48: /* PULL_DWN_CTRL_2 */
+ case 0x4c: /* PULL_DWN_CTRL_3 */
+ s->pull_dwn_ctrl[(addr & 0xf) >> 2] = value;
+ return;
+
+ case 0x50: /* GATE_INH_CTRL_0 */
+ s->gate_inh_ctrl[0] = value;
+ return;
+
+ case 0x60: /* VOLTAGE_CTRL_0 */
+ s->voltage_ctrl[0] = value;
+ return;
+
+ case 0x70: /* TEST_DBG_CTRL_0 */
+ s->test_dbg_ctrl[0] = value;
+ return;
+
+ case 0x80: /* MOD_CONF_CTRL_0 */
+ diff = s->mod_conf_ctrl[0] ^ value;
+ s->mod_conf_ctrl[0] = value;
+ omap_pin_modconf1_update(s, diff, value);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_pin_cfg_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_pin_cfg_read,
+};
+
+static CPUWriteMemoryFunc * const omap_pin_cfg_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_pin_cfg_write,
+};
+
+static void omap_pin_cfg_reset(struct omap_mpu_state_s *mpu)
+{
+ /* Start in Compatibility Mode. */
+ mpu->compat1509 = 1;
+ omap_pin_funcmux0_update(mpu, mpu->func_mux_ctrl[0], 0);
+ omap_pin_funcmux1_update(mpu, mpu->func_mux_ctrl[1], 0);
+ omap_pin_modconf1_update(mpu, mpu->mod_conf_ctrl[0], 0);
+ memset(mpu->func_mux_ctrl, 0, sizeof(mpu->func_mux_ctrl));
+ memset(mpu->comp_mode_ctrl, 0, sizeof(mpu->comp_mode_ctrl));
+ memset(mpu->pull_dwn_ctrl, 0, sizeof(mpu->pull_dwn_ctrl));
+ memset(mpu->gate_inh_ctrl, 0, sizeof(mpu->gate_inh_ctrl));
+ memset(mpu->voltage_ctrl, 0, sizeof(mpu->voltage_ctrl));
+ memset(mpu->test_dbg_ctrl, 0, sizeof(mpu->test_dbg_ctrl));
+ memset(mpu->mod_conf_ctrl, 0, sizeof(mpu->mod_conf_ctrl));
+}
+
+static void omap_pin_cfg_init(target_phys_addr_t base,
+ struct omap_mpu_state_s *mpu)
+{
+ int iomemtype = cpu_register_io_memory(omap_pin_cfg_readfn,
+ omap_pin_cfg_writefn, mpu, DEVICE_NATIVE_ENDIAN);
+
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+ omap_pin_cfg_reset(mpu);
+}
+
+/* Device Identification, Die Identification */
+static uint32_t omap_id_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0xfffe1800: /* DIE_ID_LSB */
+ return 0xc9581f0e;
+ case 0xfffe1804: /* DIE_ID_MSB */
+ return 0xa8858bfa;
+
+ case 0xfffe2000: /* PRODUCT_ID_LSB */
+ return 0x00aaaafc;
+ case 0xfffe2004: /* PRODUCT_ID_MSB */
+ return 0xcafeb574;
+
+ case 0xfffed400: /* JTAG_ID_LSB */
+ switch (s->mpu_model) {
+ case omap310:
+ return 0x03310315;
+ case omap1510:
+ return 0x03310115;
+ default:
+ hw_error("%s: bad mpu model\n", __FUNCTION__);
+ }
+ break;
+
+ case 0xfffed404: /* JTAG_ID_MSB */
+ switch (s->mpu_model) {
+ case omap310:
+ return 0xfb57402f;
+ case omap1510:
+ return 0xfb47002f;
+ default:
+ hw_error("%s: bad mpu model\n", __FUNCTION__);
+ }
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_id_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ OMAP_BAD_REG(addr);
+}
+
+static CPUReadMemoryFunc * const omap_id_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_id_read,
+};
+
+static CPUWriteMemoryFunc * const omap_id_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_id_write,
+};
+
+static void omap_id_init(struct omap_mpu_state_s *mpu)
+{
+ int iomemtype = cpu_register_io_memory(omap_id_readfn,
+ omap_id_writefn, mpu, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory_offset(0xfffe1800, 0x800, iomemtype, 0xfffe1800);
+ cpu_register_physical_memory_offset(0xfffed400, 0x100, iomemtype, 0xfffed400);
+ if (!cpu_is_omap15xx(mpu))
+ cpu_register_physical_memory_offset(0xfffe2000, 0x800, iomemtype, 0xfffe2000);
+}
+
+/* MPUI Control (Dummy) */
+static uint32_t omap_mpui_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* CTRL */
+ return s->mpui_ctrl;
+ case 0x04: /* DEBUG_ADDR */
+ return 0x01ffffff;
+ case 0x08: /* DEBUG_DATA */
+ return 0xffffffff;
+ case 0x0c: /* DEBUG_FLAG */
+ return 0x00000800;
+ case 0x10: /* STATUS */
+ return 0x00000000;
+
+ /* Not in OMAP310 */
+ case 0x14: /* DSP_STATUS */
+ case 0x18: /* DSP_BOOT_CONFIG */
+ return 0x00000000;
+ case 0x1c: /* DSP_MPUI_CONFIG */
+ return 0x0000ffff;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpui_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* CTRL */
+ s->mpui_ctrl = value & 0x007fffff;
+ break;
+
+ case 0x04: /* DEBUG_ADDR */
+ case 0x08: /* DEBUG_DATA */
+ case 0x0c: /* DEBUG_FLAG */
+ case 0x10: /* STATUS */
+ /* Not in OMAP310 */
+ case 0x14: /* DSP_STATUS */
+ OMAP_RO_REG(addr);
+ case 0x18: /* DSP_BOOT_CONFIG */
+ case 0x1c: /* DSP_MPUI_CONFIG */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_mpui_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_mpui_read,
+};
+
+static CPUWriteMemoryFunc * const omap_mpui_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_mpui_write,
+};
+
+static void omap_mpui_reset(struct omap_mpu_state_s *s)
+{
+ s->mpui_ctrl = 0x0003ff1b;
+}
+
+static void omap_mpui_init(target_phys_addr_t base,
+ struct omap_mpu_state_s *mpu)
+{
+ int iomemtype = cpu_register_io_memory(omap_mpui_readfn,
+ omap_mpui_writefn, mpu, DEVICE_NATIVE_ENDIAN);
+
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+
+ omap_mpui_reset(mpu);
+}
+
+/* TIPB Bridges */
+struct omap_tipb_bridge_s {
+ qemu_irq abort;
+
+ int width_intr;
+ uint16_t control;
+ uint16_t alloc;
+ uint16_t buffer;
+ uint16_t enh_control;
+};
+
+static uint32_t omap_tipb_bridge_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* TIPB_CNTL */
+ return s->control;
+ case 0x04: /* TIPB_BUS_ALLOC */
+ return s->alloc;
+ case 0x08: /* MPU_TIPB_CNTL */
+ return s->buffer;
+ case 0x0c: /* ENHANCED_TIPB_CNTL */
+ return s->enh_control;
+ case 0x10: /* ADDRESS_DBG */
+ case 0x14: /* DATA_DEBUG_LOW */
+ case 0x18: /* DATA_DEBUG_HIGH */
+ return 0xffff;
+ case 0x1c: /* DEBUG_CNTR_SIG */
+ return 0x00f8;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_tipb_bridge_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* TIPB_CNTL */
+ s->control = value & 0xffff;
+ break;
+
+ case 0x04: /* TIPB_BUS_ALLOC */
+ s->alloc = value & 0x003f;
+ break;
+
+ case 0x08: /* MPU_TIPB_CNTL */
+ s->buffer = value & 0x0003;
+ break;
+
+ case 0x0c: /* ENHANCED_TIPB_CNTL */
+ s->width_intr = !(value & 2);
+ s->enh_control = value & 0x000f;
+ break;
+
+ case 0x10: /* ADDRESS_DBG */
+ case 0x14: /* DATA_DEBUG_LOW */
+ case 0x18: /* DATA_DEBUG_HIGH */
+ case 0x1c: /* DEBUG_CNTR_SIG */
+ OMAP_RO_REG(addr);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_tipb_bridge_readfn[] = {
+ omap_badwidth_read16,
+ omap_tipb_bridge_read,
+ omap_tipb_bridge_read,
+};
+
+static CPUWriteMemoryFunc * const omap_tipb_bridge_writefn[] = {
+ omap_badwidth_write16,
+ omap_tipb_bridge_write,
+ omap_tipb_bridge_write,
+};
+
+static void omap_tipb_bridge_reset(struct omap_tipb_bridge_s *s)
+{
+ s->control = 0xffff;
+ s->alloc = 0x0009;
+ s->buffer = 0x0000;
+ s->enh_control = 0x000f;
+}
+
+static struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base,
+ qemu_irq abort_irq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_tipb_bridge_s *s = (struct omap_tipb_bridge_s *)
+ qemu_mallocz(sizeof(struct omap_tipb_bridge_s));
+
+ s->abort = abort_irq;
+ omap_tipb_bridge_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_tipb_bridge_readfn,
+ omap_tipb_bridge_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+
+ return s;
+}
+
+/* Dummy Traffic Controller's Memory Interface */
+static uint32_t omap_tcmi_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint32_t ret;
+
+ switch (addr) {
+ case 0x00: /* IMIF_PRIO */
+ case 0x04: /* EMIFS_PRIO */
+ case 0x08: /* EMIFF_PRIO */
+ case 0x0c: /* EMIFS_CONFIG */
+ case 0x10: /* EMIFS_CS0_CONFIG */
+ case 0x14: /* EMIFS_CS1_CONFIG */
+ case 0x18: /* EMIFS_CS2_CONFIG */
+ case 0x1c: /* EMIFS_CS3_CONFIG */
+ case 0x24: /* EMIFF_MRS */
+ case 0x28: /* TIMEOUT1 */
+ case 0x2c: /* TIMEOUT2 */
+ case 0x30: /* TIMEOUT3 */
+ case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */
+ case 0x40: /* EMIFS_CFG_DYN_WAIT */
+ return s->tcmi_regs[addr >> 2];
+
+ case 0x20: /* EMIFF_SDRAM_CONFIG */
+ ret = s->tcmi_regs[addr >> 2];
+ s->tcmi_regs[addr >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */
+ /* XXX: We can try using the VGA_DIRTY flag for this */
+ return ret;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_tcmi_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* IMIF_PRIO */
+ case 0x04: /* EMIFS_PRIO */
+ case 0x08: /* EMIFF_PRIO */
+ case 0x10: /* EMIFS_CS0_CONFIG */
+ case 0x14: /* EMIFS_CS1_CONFIG */
+ case 0x18: /* EMIFS_CS2_CONFIG */
+ case 0x1c: /* EMIFS_CS3_CONFIG */
+ case 0x20: /* EMIFF_SDRAM_CONFIG */
+ case 0x24: /* EMIFF_MRS */
+ case 0x28: /* TIMEOUT1 */
+ case 0x2c: /* TIMEOUT2 */
+ case 0x30: /* TIMEOUT3 */
+ case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */
+ case 0x40: /* EMIFS_CFG_DYN_WAIT */
+ s->tcmi_regs[addr >> 2] = value;
+ break;
+ case 0x0c: /* EMIFS_CONFIG */
+ s->tcmi_regs[addr >> 2] = (value & 0xf) | (1 << 4);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_tcmi_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_tcmi_read,
+};
+
+static CPUWriteMemoryFunc * const omap_tcmi_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_tcmi_write,
+};
+
+static void omap_tcmi_reset(struct omap_mpu_state_s *mpu)
+{
+ mpu->tcmi_regs[0x00 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x04 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x08 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x0c >> 2] = 0x00000010;
+ mpu->tcmi_regs[0x10 >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x14 >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x18 >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x1c >> 2] = 0x0010fffb;
+ mpu->tcmi_regs[0x20 >> 2] = 0x00618800;
+ mpu->tcmi_regs[0x24 >> 2] = 0x00000037;
+ mpu->tcmi_regs[0x28 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x2c >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x30 >> 2] = 0x00000000;
+ mpu->tcmi_regs[0x3c >> 2] = 0x00000003;
+ mpu->tcmi_regs[0x40 >> 2] = 0x00000000;
+}
+
+static void omap_tcmi_init(target_phys_addr_t base,
+ struct omap_mpu_state_s *mpu)
+{
+ int iomemtype = cpu_register_io_memory(omap_tcmi_readfn,
+ omap_tcmi_writefn, mpu, DEVICE_NATIVE_ENDIAN);
+
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+ omap_tcmi_reset(mpu);
+}
+
+/* Digital phase-locked loops control */
+static uint32_t omap_dpll_read(void *opaque, target_phys_addr_t addr)
+{
+ struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque;
+
+ if (addr == 0x00) /* CTL_REG */
+ return s->mode;
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_dpll_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct dpll_ctl_s *s = (struct dpll_ctl_s *) opaque;
+ uint16_t diff;
+ static const int bypass_div[4] = { 1, 2, 4, 4 };
+ int div, mult;
+
+ if (addr == 0x00) { /* CTL_REG */
+ /* See omap_ulpd_pm_write() too */
+ diff = s->mode & value;
+ s->mode = value & 0x2fff;
+ if (diff & (0x3ff << 2)) {
+ if (value & (1 << 4)) { /* PLL_ENABLE */
+ div = ((value >> 5) & 3) + 1; /* PLL_DIV */
+ mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */
+ } else {
+ div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */
+ mult = 1;
+ }
+ omap_clk_setrate(s->dpll, div, mult);
+ }
+
+ /* Enter the desired mode. */
+ s->mode = (s->mode & 0xfffe) | ((s->mode >> 4) & 1);
+
+ /* Act as if the lock is restored. */
+ s->mode |= 2;
+ } else {
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_dpll_readfn[] = {
+ omap_badwidth_read16,
+ omap_dpll_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_dpll_writefn[] = {
+ omap_badwidth_write16,
+ omap_dpll_write,
+ omap_badwidth_write16,
+};
+
+static void omap_dpll_reset(struct dpll_ctl_s *s)
+{
+ s->mode = 0x2002;
+ omap_clk_setrate(s->dpll, 1, 1);
+}
+
+static void omap_dpll_init(struct dpll_ctl_s *s, target_phys_addr_t base,
+ omap_clk clk)
+{
+ int iomemtype = cpu_register_io_memory(omap_dpll_readfn,
+ omap_dpll_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ s->dpll = clk;
+ omap_dpll_reset(s);
+
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+}
+
+/* MPU Clock/Reset/Power Mode Control */
+static uint32_t omap_clkm_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* ARM_CKCTL */
+ return s->clkm.arm_ckctl;
+
+ case 0x04: /* ARM_IDLECT1 */
+ return s->clkm.arm_idlect1;
+
+ case 0x08: /* ARM_IDLECT2 */
+ return s->clkm.arm_idlect2;
+
+ case 0x0c: /* ARM_EWUPCT */
+ return s->clkm.arm_ewupct;
+
+ case 0x10: /* ARM_RSTCT1 */
+ return s->clkm.arm_rstct1;
+
+ case 0x14: /* ARM_RSTCT2 */
+ return s->clkm.arm_rstct2;
+
+ case 0x18: /* ARM_SYSST */
+ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start;
+
+ case 0x1c: /* ARM_CKOUT1 */
+ return s->clkm.arm_ckout1;
+
+ case 0x20: /* ARM_CKOUT2 */
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */
+ if (value & (1 << 14))
+ /* Reserved */;
+ else {
+ clk = omap_findclk(s, "arminth_ck");
+ omap_clk_reparent(clk, omap_findclk(s, "tc_ck"));
+ }
+ }
+ if (diff & (1 << 12)) { /* ARM_TIMXO */
+ clk = omap_findclk(s, "armtim_ck");
+ if (value & (1 << 12))
+ omap_clk_reparent(clk, omap_findclk(s, "clkin"));
+ else
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen1"));
+ }
+ /* XXX: en_dspck */
+ if (diff & (3 << 10)) { /* DSPMMUDIV */
+ clk = omap_findclk(s, "dspmmu_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 10) & 3), 1);
+ }
+ if (diff & (3 << 8)) { /* TCDIV */
+ clk = omap_findclk(s, "tc_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 8) & 3), 1);
+ }
+ if (diff & (3 << 6)) { /* DSPDIV */
+ clk = omap_findclk(s, "dsp_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 6) & 3), 1);
+ }
+ if (diff & (3 << 4)) { /* ARMDIV */
+ clk = omap_findclk(s, "arm_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 4) & 3), 1);
+ }
+ if (diff & (3 << 2)) { /* LCDDIV */
+ clk = omap_findclk(s, "lcd_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 2) & 3), 1);
+ }
+ if (diff & (3 << 0)) { /* PERDIV */
+ clk = omap_findclk(s, "armper_ck");
+ omap_clk_setrate(clk, 1 << ((value >> 0) & 3), 1);
+ }
+}
+
+static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ if (value & (1 << 11)) /* SETARM_IDLE */
+ cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+ if (!(value & (1 << 10))) /* WKUP_MODE */
+ qemu_system_shutdown_request(); /* XXX: disable wakeup from IRQ */
+
+#define SET_CANIDLE(clock, bit) \
+ if (diff & (1 << bit)) { \
+ clk = omap_findclk(s, clock); \
+ omap_clk_canidle(clk, (value >> bit) & 1); \
+ }
+ SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */
+ SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */
+ SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */
+ SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */
+ SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */
+ SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */
+ SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */
+ SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */
+ SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */
+ SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */
+ SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */
+ SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */
+ SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */
+ SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */
+}
+
+static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+#define SET_ONOFF(clock, bit) \
+ if (diff & (1 << bit)) { \
+ clk = omap_findclk(s, clock); \
+ omap_clk_onoff(clk, (value >> bit) & 1); \
+ }
+ SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */
+ SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */
+ SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */
+ SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */
+ SET_ONOFF("lb_ck", 4) /* EN_LBCK */
+ SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */
+ SET_ONOFF("mpui_ck", 6) /* EN_APICK */
+ SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */
+ SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */
+ SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */
+ SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */
+}
+
+static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ if (diff & (3 << 4)) { /* TCLKOUT */
+ clk = omap_findclk(s, "tclk_out");
+ switch ((value >> 4) & 3) {
+ case 1:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen3"));
+ omap_clk_onoff(clk, 1);
+ break;
+ case 2:
+ omap_clk_reparent(clk, omap_findclk(s, "tc_ck"));
+ omap_clk_onoff(clk, 1);
+ break;
+ default:
+ omap_clk_onoff(clk, 0);
+ }
+ }
+ if (diff & (3 << 2)) { /* DCLKOUT */
+ clk = omap_findclk(s, "dclk_out");
+ switch ((value >> 2) & 3) {
+ case 0:
+ omap_clk_reparent(clk, omap_findclk(s, "dspmmu_ck"));
+ break;
+ case 1:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen2"));
+ break;
+ case 2:
+ omap_clk_reparent(clk, omap_findclk(s, "dsp_ck"));
+ break;
+ case 3:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_ref14"));
+ break;
+ }
+ }
+ if (diff & (3 << 0)) { /* ACLKOUT */
+ clk = omap_findclk(s, "aclk_out");
+ switch ((value >> 0) & 3) {
+ case 1:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_gen1"));
+ omap_clk_onoff(clk, 1);
+ break;
+ case 2:
+ omap_clk_reparent(clk, omap_findclk(s, "arm_ck"));
+ omap_clk_onoff(clk, 1);
+ break;
+ case 3:
+ omap_clk_reparent(clk, omap_findclk(s, "ck_ref14"));
+ omap_clk_onoff(clk, 1);
+ break;
+ default:
+ omap_clk_onoff(clk, 0);
+ }
+ }
+}
+
+static void omap_clkm_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint16_t diff;
+ omap_clk clk;
+ static const char *clkschemename[8] = {
+ "fully synchronous", "fully asynchronous", "synchronous scalable",
+ "mix mode 1", "mix mode 2", "bypass mode", "mix mode 3", "mix mode 4",
+ };
+
+ switch (addr) {
+ case 0x00: /* ARM_CKCTL */
+ diff = s->clkm.arm_ckctl ^ value;
+ s->clkm.arm_ckctl = value & 0x7fff;
+ omap_clkm_ckctl_update(s, diff, value);
+ return;
+
+ case 0x04: /* ARM_IDLECT1 */
+ diff = s->clkm.arm_idlect1 ^ value;
+ s->clkm.arm_idlect1 = value & 0x0fff;
+ omap_clkm_idlect1_update(s, diff, value);
+ return;
+
+ case 0x08: /* ARM_IDLECT2 */
+ diff = s->clkm.arm_idlect2 ^ value;
+ s->clkm.arm_idlect2 = value & 0x07ff;
+ omap_clkm_idlect2_update(s, diff, value);
+ return;
+
+ case 0x0c: /* ARM_EWUPCT */
+ s->clkm.arm_ewupct = value & 0x003f;
+ return;
+
+ case 0x10: /* ARM_RSTCT1 */
+ diff = s->clkm.arm_rstct1 ^ value;
+ s->clkm.arm_rstct1 = value & 0x0007;
+ if (value & 9) {
+ qemu_system_reset_request();
+ s->clkm.cold_start = 0xa;
+ }
+ if (diff & ~value & 4) { /* DSP_RST */
+ omap_mpui_reset(s);
+ omap_tipb_bridge_reset(s->private_tipb);
+ omap_tipb_bridge_reset(s->public_tipb);
+ }
+ if (diff & 2) { /* DSP_EN */
+ clk = omap_findclk(s, "dsp_ck");
+ omap_clk_canidle(clk, (~value >> 1) & 1);
+ }
+ return;
+
+ case 0x14: /* ARM_RSTCT2 */
+ s->clkm.arm_rstct2 = value & 0x0001;
+ return;
+
+ case 0x18: /* ARM_SYSST */
+ if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) {
+ s->clkm.clocking_scheme = (value >> 11) & 7;
+ printf("%s: clocking scheme set to %s\n", __FUNCTION__,
+ clkschemename[s->clkm.clocking_scheme]);
+ }
+ s->clkm.cold_start &= value & 0x3f;
+ return;
+
+ case 0x1c: /* ARM_CKOUT1 */
+ diff = s->clkm.arm_ckout1 ^ value;
+ s->clkm.arm_ckout1 = value & 0x003f;
+ omap_clkm_ckout1_update(s, diff, value);
+ return;
+
+ case 0x20: /* ARM_CKOUT2 */
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_clkm_readfn[] = {
+ omap_badwidth_read16,
+ omap_clkm_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_clkm_writefn[] = {
+ omap_badwidth_write16,
+ omap_clkm_write,
+ omap_badwidth_write16,
+};
+
+static uint32_t omap_clkdsp_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x04: /* DSP_IDLECT1 */
+ return s->clkm.dsp_idlect1;
+
+ case 0x08: /* DSP_IDLECT2 */
+ return s->clkm.dsp_idlect2;
+
+ case 0x14: /* DSP_RSTCT2 */
+ return s->clkm.dsp_rstct2;
+
+ case 0x18: /* DSP_SYSST */
+ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start |
+ (s->env->halted << 6); /* Quite useless... */
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static inline void omap_clkdsp_idlect1_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */
+}
+
+static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s,
+ uint16_t diff, uint16_t value)
+{
+ omap_clk clk;
+
+ SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */
+}
+
+static void omap_clkdsp_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ uint16_t diff;
+
+ switch (addr) {
+ case 0x04: /* DSP_IDLECT1 */
+ diff = s->clkm.dsp_idlect1 ^ value;
+ s->clkm.dsp_idlect1 = value & 0x01f7;
+ omap_clkdsp_idlect1_update(s, diff, value);
+ break;
+
+ case 0x08: /* DSP_IDLECT2 */
+ s->clkm.dsp_idlect2 = value & 0x0037;
+ diff = s->clkm.dsp_idlect1 ^ value;
+ omap_clkdsp_idlect2_update(s, diff, value);
+ break;
+
+ case 0x14: /* DSP_RSTCT2 */
+ s->clkm.dsp_rstct2 = value & 0x0001;
+ break;
+
+ case 0x18: /* DSP_SYSST */
+ s->clkm.cold_start &= value & 0x3f;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_clkdsp_readfn[] = {
+ omap_badwidth_read16,
+ omap_clkdsp_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_clkdsp_writefn[] = {
+ omap_badwidth_write16,
+ omap_clkdsp_write,
+ omap_badwidth_write16,
+};
+
+static void omap_clkm_reset(struct omap_mpu_state_s *s)
+{
+ if (s->wdt && s->wdt->reset)
+ s->clkm.cold_start = 0x6;
+ s->clkm.clocking_scheme = 0;
+ omap_clkm_ckctl_update(s, ~0, 0x3000);
+ s->clkm.arm_ckctl = 0x3000;
+ omap_clkm_idlect1_update(s, s->clkm.arm_idlect1 ^ 0x0400, 0x0400);
+ s->clkm.arm_idlect1 = 0x0400;
+ omap_clkm_idlect2_update(s, s->clkm.arm_idlect2 ^ 0x0100, 0x0100);
+ s->clkm.arm_idlect2 = 0x0100;
+ s->clkm.arm_ewupct = 0x003f;
+ s->clkm.arm_rstct1 = 0x0000;
+ s->clkm.arm_rstct2 = 0x0000;
+ s->clkm.arm_ckout1 = 0x0015;
+ s->clkm.dpll1_mode = 0x2002;
+ omap_clkdsp_idlect1_update(s, s->clkm.dsp_idlect1 ^ 0x0040, 0x0040);
+ s->clkm.dsp_idlect1 = 0x0040;
+ omap_clkdsp_idlect2_update(s, ~0, 0x0000);
+ s->clkm.dsp_idlect2 = 0x0000;
+ s->clkm.dsp_rstct2 = 0x0000;
+}
+
+static void omap_clkm_init(target_phys_addr_t mpu_base,
+ target_phys_addr_t dsp_base, struct omap_mpu_state_s *s)
+{
+ int iomemtype[2] = {
+ cpu_register_io_memory(omap_clkm_readfn, omap_clkm_writefn, s,
+ DEVICE_NATIVE_ENDIAN),
+ cpu_register_io_memory(omap_clkdsp_readfn, omap_clkdsp_writefn, s,
+ DEVICE_NATIVE_ENDIAN),
+ };
+
+ s->clkm.arm_idlect1 = 0x03ff;
+ s->clkm.arm_idlect2 = 0x0100;
+ s->clkm.dsp_idlect1 = 0x0002;
+ omap_clkm_reset(s);
+ s->clkm.cold_start = 0x3a;
+
+ cpu_register_physical_memory(mpu_base, 0x100, iomemtype[0]);
+ cpu_register_physical_memory(dsp_base, 0x1000, iomemtype[1]);
+}
+
+/* MPU I/O */
+struct omap_mpuio_s {
+ qemu_irq irq;
+ qemu_irq kbd_irq;
+ qemu_irq *in;
+ qemu_irq handler[16];
+ qemu_irq wakeup;
+
+ uint16_t inputs;
+ uint16_t outputs;
+ uint16_t dir;
+ uint16_t edge;
+ uint16_t mask;
+ uint16_t ints;
+
+ uint16_t debounce;
+ uint16_t latch;
+ uint8_t event;
+
+ uint8_t buttons[5];
+ uint8_t row_latch;
+ uint8_t cols;
+ int kbd_mask;
+ int clk;
+};
+
+static void omap_mpuio_set(void *opaque, int line, int level)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+ uint16_t prev = s->inputs;
+
+ if (level)
+ s->inputs |= 1 << line;
+ else
+ s->inputs &= ~(1 << line);
+
+ if (((1 << line) & s->dir & ~s->mask) && s->clk) {
+ if ((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) {
+ s->ints |= 1 << line;
+ qemu_irq_raise(s->irq);
+ /* TODO: wakeup */
+ }
+ if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */
+ (s->event >> 1) == line) /* PIN_SELECT */
+ s->latch = s->inputs;
+ }
+}
+
+static void omap_mpuio_kbd_update(struct omap_mpuio_s *s)
+{
+ int i;
+ uint8_t *row, rows = 0, cols = ~s->cols;
+
+ for (row = s->buttons + 4, i = 1 << 4; i; row --, i >>= 1)
+ if (*row & cols)
+ rows |= i;
+
+ qemu_set_irq(s->kbd_irq, rows && !s->kbd_mask && s->clk);
+ s->row_latch = ~rows;
+}
+
+static uint32_t omap_mpuio_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t ret;
+
+ switch (offset) {
+ case 0x00: /* INPUT_LATCH */
+ return s->inputs;
+
+ case 0x04: /* OUTPUT_REG */
+ return s->outputs;
+
+ case 0x08: /* IO_CNTL */
+ return s->dir;
+
+ case 0x10: /* KBR_LATCH */
+ return s->row_latch;
+
+ case 0x14: /* KBC_REG */
+ return s->cols;
+
+ case 0x18: /* GPIO_EVENT_MODE_REG */
+ return s->event;
+
+ case 0x1c: /* GPIO_INT_EDGE_REG */
+ return s->edge;
+
+ case 0x20: /* KBD_INT */
+ return (~s->row_latch & 0x1f) && !s->kbd_mask;
+
+ case 0x24: /* GPIO_INT */
+ ret = s->ints;
+ s->ints &= s->mask;
+ if (ret)
+ qemu_irq_lower(s->irq);
+ return ret;
+
+ case 0x28: /* KBD_MASKIT */
+ return s->kbd_mask;
+
+ case 0x2c: /* GPIO_MASKIT */
+ return s->mask;
+
+ case 0x30: /* GPIO_DEBOUNCING_REG */
+ return s->debounce;
+
+ case 0x34: /* GPIO_LATCH_REG */
+ return s->latch;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mpuio_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t diff;
+ int ln;
+
+ switch (offset) {
+ case 0x04: /* OUTPUT_REG */
+ diff = (s->outputs ^ value) & ~s->dir;
+ s->outputs = value;
+ while ((ln = ffs(diff))) {
+ ln --;
+ if (s->handler[ln])
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+ break;
+
+ case 0x08: /* IO_CNTL */
+ diff = s->outputs & (s->dir ^ value);
+ s->dir = value;
+
+ value = s->outputs & ~s->dir;
+ while ((ln = ffs(diff))) {
+ ln --;
+ if (s->handler[ln])
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+ break;
+
+ case 0x14: /* KBC_REG */
+ s->cols = value;
+ omap_mpuio_kbd_update(s);
+ break;
+
+ case 0x18: /* GPIO_EVENT_MODE_REG */
+ s->event = value & 0x1f;
+ break;
+
+ case 0x1c: /* GPIO_INT_EDGE_REG */
+ s->edge = value;
+ break;
+
+ case 0x28: /* KBD_MASKIT */
+ s->kbd_mask = value & 1;
+ omap_mpuio_kbd_update(s);
+ break;
+
+ case 0x2c: /* GPIO_MASKIT */
+ s->mask = value;
+ break;
+
+ case 0x30: /* GPIO_DEBOUNCING_REG */
+ s->debounce = value & 0x1ff;
+ break;
+
+ case 0x00: /* INPUT_LATCH */
+ case 0x10: /* KBR_LATCH */
+ case 0x20: /* KBD_INT */
+ case 0x24: /* GPIO_INT */
+ case 0x34: /* GPIO_LATCH_REG */
+ OMAP_RO_REG(addr);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_mpuio_readfn[] = {
+ omap_badwidth_read16,
+ omap_mpuio_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_mpuio_writefn[] = {
+ omap_badwidth_write16,
+ omap_mpuio_write,
+ omap_badwidth_write16,
+};
+
+static void omap_mpuio_reset(struct omap_mpuio_s *s)
+{
+ s->inputs = 0;
+ s->outputs = 0;
+ s->dir = ~0;
+ s->event = 0;
+ s->edge = 0;
+ s->kbd_mask = 0;
+ s->mask = 0;
+ s->debounce = 0;
+ s->latch = 0;
+ s->ints = 0;
+ s->row_latch = 0x1f;
+ s->clk = 1;
+}
+
+static void omap_mpuio_onoff(void *opaque, int line, int on)
+{
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *) opaque;
+
+ s->clk = on;
+ if (on)
+ omap_mpuio_kbd_update(s);
+}
+
+struct omap_mpuio_s *omap_mpuio_init(target_phys_addr_t base,
+ qemu_irq kbd_int, qemu_irq gpio_int, qemu_irq wakeup,
+ omap_clk clk)
+{
+ int iomemtype;
+ struct omap_mpuio_s *s = (struct omap_mpuio_s *)
+ qemu_mallocz(sizeof(struct omap_mpuio_s));
+
+ s->irq = gpio_int;
+ s->kbd_irq = kbd_int;
+ s->wakeup = wakeup;
+ s->in = qemu_allocate_irqs(omap_mpuio_set, s, 16);
+ omap_mpuio_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_mpuio_readfn,
+ omap_mpuio_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ omap_clk_adduser(clk, qemu_allocate_irqs(omap_mpuio_onoff, s, 1)[0]);
+
+ return s;
+}
+
+qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s)
+{
+ return s->in;
+}
+
+void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler)
+{
+ if (line >= 16 || line < 0)
+ hw_error("%s: No GPIO line %i\n", __FUNCTION__, line);
+ s->handler[line] = handler;
+}
+
+void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down)
+{
+ if (row >= 5 || row < 0)
+ hw_error("%s: No key %i-%i\n", __FUNCTION__, col, row);
+
+ if (down)
+ s->buttons[row] |= 1 << col;
+ else
+ s->buttons[row] &= ~(1 << col);
+
+ omap_mpuio_kbd_update(s);
+}
+
+/* MicroWire Interface */
+struct omap_uwire_s {
+ qemu_irq txirq;
+ qemu_irq rxirq;
+ qemu_irq txdrq;
+
+ uint16_t txbuf;
+ uint16_t rxbuf;
+ uint16_t control;
+ uint16_t setup[5];
+
+ uWireSlave *chip[4];
+};
+
+static void omap_uwire_transfer_start(struct omap_uwire_s *s)
+{
+ int chipselect = (s->control >> 10) & 3; /* INDEX */
+ uWireSlave *slave = s->chip[chipselect];
+
+ if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */
+ if (s->control & (1 << 12)) /* CS_CMD */
+ if (slave && slave->send)
+ slave->send(slave->opaque,
+ s->txbuf >> (16 - ((s->control >> 5) & 0x1f)));
+ s->control &= ~(1 << 14); /* CSRB */
+ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
+ * a DRQ. When is the level IRQ supposed to be reset? */
+ }
+
+ if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */
+ if (s->control & (1 << 12)) /* CS_CMD */
+ if (slave && slave->receive)
+ s->rxbuf = slave->receive(slave->opaque);
+ s->control |= 1 << 15; /* RDRB */
+ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
+ * a DRQ. When is the level IRQ supposed to be reset? */
+ }
+}
+
+static uint32_t omap_uwire_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_uwire_s *s = (struct omap_uwire_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* RDR */
+ s->control &= ~(1 << 15); /* RDRB */
+ return s->rxbuf;
+
+ case 0x04: /* CSR */
+ return s->control;
+
+ case 0x08: /* SR1 */
+ return s->setup[0];
+ case 0x0c: /* SR2 */
+ return s->setup[1];
+ case 0x10: /* SR3 */
+ return s->setup[2];
+ case 0x14: /* SR4 */
+ return s->setup[3];
+ case 0x18: /* SR5 */
+ return s->setup[4];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_uwire_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_uwire_s *s = (struct omap_uwire_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* TDR */
+ s->txbuf = value; /* TD */
+ if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */
+ ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */
+ (s->control & (1 << 12)))) { /* CS_CMD */
+ s->control |= 1 << 14; /* CSRB */
+ omap_uwire_transfer_start(s);
+ }
+ break;
+
+ case 0x04: /* CSR */
+ s->control = value & 0x1fff;
+ if (value & (1 << 13)) /* START */
+ omap_uwire_transfer_start(s);
+ break;
+
+ case 0x08: /* SR1 */
+ s->setup[0] = value & 0x003f;
+ break;
+
+ case 0x0c: /* SR2 */
+ s->setup[1] = value & 0x0fc0;
+ break;
+
+ case 0x10: /* SR3 */
+ s->setup[2] = value & 0x0003;
+ break;
+
+ case 0x14: /* SR4 */
+ s->setup[3] = value & 0x0001;
+ break;
+
+ case 0x18: /* SR5 */
+ s->setup[4] = value & 0x000f;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_uwire_readfn[] = {
+ omap_badwidth_read16,
+ omap_uwire_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_uwire_writefn[] = {
+ omap_badwidth_write16,
+ omap_uwire_write,
+ omap_badwidth_write16,
+};
+
+static void omap_uwire_reset(struct omap_uwire_s *s)
+{
+ s->control = 0;
+ s->setup[0] = 0;
+ s->setup[1] = 0;
+ s->setup[2] = 0;
+ s->setup[3] = 0;
+ s->setup[4] = 0;
+}
+
+struct omap_uwire_s *omap_uwire_init(target_phys_addr_t base,
+ qemu_irq *irq, qemu_irq dma, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_uwire_s *s = (struct omap_uwire_s *)
+ qemu_mallocz(sizeof(struct omap_uwire_s));
+
+ s->txirq = irq[0];
+ s->rxirq = irq[1];
+ s->txdrq = dma;
+ omap_uwire_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_uwire_readfn,
+ omap_uwire_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ return s;
+}
+
+void omap_uwire_attach(struct omap_uwire_s *s,
+ uWireSlave *slave, int chipselect)
+{
+ if (chipselect < 0 || chipselect > 3) {
+ fprintf(stderr, "%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
+ exit(-1);
+ }
+
+ s->chip[chipselect] = slave;
+}
+
+/* Pseudonoise Pulse-Width Light Modulator */
+static void omap_pwl_update(struct omap_mpu_state_s *s)
+{
+ int output = (s->pwl.clk && s->pwl.enable) ? s->pwl.level : 0;
+
+ if (output != s->pwl.output) {
+ s->pwl.output = output;
+ printf("%s: Backlight now at %i/256\n", __FUNCTION__, output);
+ }
+}
+
+static uint32_t omap_pwl_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* PWL_LEVEL */
+ return s->pwl.level;
+ case 0x04: /* PWL_CTRL */
+ return s->pwl.enable;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_pwl_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* PWL_LEVEL */
+ s->pwl.level = value;
+ omap_pwl_update(s);
+ break;
+ case 0x04: /* PWL_CTRL */
+ s->pwl.enable = value & 1;
+ omap_pwl_update(s);
+ break;
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_pwl_readfn[] = {
+ omap_pwl_read,
+ omap_badwidth_read8,
+ omap_badwidth_read8,
+};
+
+static CPUWriteMemoryFunc * const omap_pwl_writefn[] = {
+ omap_pwl_write,
+ omap_badwidth_write8,
+ omap_badwidth_write8,
+};
+
+static void omap_pwl_reset(struct omap_mpu_state_s *s)
+{
+ s->pwl.output = 0;
+ s->pwl.level = 0;
+ s->pwl.enable = 0;
+ s->pwl.clk = 1;
+ omap_pwl_update(s);
+}
+
+static void omap_pwl_clk_update(void *opaque, int line, int on)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ s->pwl.clk = on;
+ omap_pwl_update(s);
+}
+
+static void omap_pwl_init(target_phys_addr_t base, struct omap_mpu_state_s *s,
+ omap_clk clk)
+{
+ int iomemtype;
+
+ omap_pwl_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_pwl_readfn,
+ omap_pwl_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ omap_clk_adduser(clk, qemu_allocate_irqs(omap_pwl_clk_update, s, 1)[0]);
+}
+
+/* Pulse-Width Tone module */
+static uint32_t omap_pwt_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* FRC */
+ return s->pwt.frc;
+ case 0x04: /* VCR */
+ return s->pwt.vrc;
+ case 0x08: /* GCR */
+ return s->pwt.gcr;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_pwt_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* FRC */
+ s->pwt.frc = value & 0x3f;
+ break;
+ case 0x04: /* VRC */
+ if ((value ^ s->pwt.vrc) & 1) {
+ if (value & 1)
+ printf("%s: %iHz buzz on\n", __FUNCTION__, (int)
+ /* 1.5 MHz from a 12-MHz or 13-MHz PWT_CLK */
+ ((omap_clk_getrate(s->pwt.clk) >> 3) /
+ /* Pre-multiplexer divider */
+ ((s->pwt.gcr & 2) ? 1 : 154) /
+ /* Octave multiplexer */
+ (2 << (value & 3)) *
+ /* 101/107 divider */
+ ((value & (1 << 2)) ? 101 : 107) *
+ /* 49/55 divider */
+ ((value & (1 << 3)) ? 49 : 55) *
+ /* 50/63 divider */
+ ((value & (1 << 4)) ? 50 : 63) *
+ /* 80/127 divider */
+ ((value & (1 << 5)) ? 80 : 127) /
+ (107 * 55 * 63 * 127)));
+ else
+ printf("%s: silence!\n", __FUNCTION__);
+ }
+ s->pwt.vrc = value & 0x7f;
+ break;
+ case 0x08: /* GCR */
+ s->pwt.gcr = value & 3;
+ break;
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_pwt_readfn[] = {
+ omap_pwt_read,
+ omap_badwidth_read8,
+ omap_badwidth_read8,
+};
+
+static CPUWriteMemoryFunc * const omap_pwt_writefn[] = {
+ omap_pwt_write,
+ omap_badwidth_write8,
+ omap_badwidth_write8,
+};
+
+static void omap_pwt_reset(struct omap_mpu_state_s *s)
+{
+ s->pwt.frc = 0;
+ s->pwt.vrc = 0;
+ s->pwt.gcr = 0;
+}
+
+static void omap_pwt_init(target_phys_addr_t base, struct omap_mpu_state_s *s,
+ omap_clk clk)
+{
+ int iomemtype;
+
+ s->pwt.clk = clk;
+ omap_pwt_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_pwt_readfn,
+ omap_pwt_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+}
+
+/* Real-time Clock module */
+struct omap_rtc_s {
+ qemu_irq irq;
+ qemu_irq alarm;
+ QEMUTimer *clk;
+
+ uint8_t interrupts;
+ uint8_t status;
+ int16_t comp_reg;
+ int running;
+ int pm_am;
+ int auto_comp;
+ int round;
+ struct tm alarm_tm;
+ time_t alarm_ti;
+
+ struct tm current_tm;
+ time_t ti;
+ uint64_t tick;
+};
+
+static void omap_rtc_interrupts_update(struct omap_rtc_s *s)
+{
+ /* s->alarm is level-triggered */
+ qemu_set_irq(s->alarm, (s->status >> 6) & 1);
+}
+
+static void omap_rtc_alarm_update(struct omap_rtc_s *s)
+{
+ s->alarm_ti = mktimegm(&s->alarm_tm);
+ if (s->alarm_ti == -1)
+ printf("%s: conversion failed\n", __FUNCTION__);
+}
+
+static uint32_t omap_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint8_t i;
+
+ switch (offset) {
+ case 0x00: /* SECONDS_REG */
+ return to_bcd(s->current_tm.tm_sec);
+
+ case 0x04: /* MINUTES_REG */
+ return to_bcd(s->current_tm.tm_min);
+
+ case 0x08: /* HOURS_REG */
+ if (s->pm_am)
+ return ((s->current_tm.tm_hour > 11) << 7) |
+ to_bcd(((s->current_tm.tm_hour - 1) % 12) + 1);
+ else
+ return to_bcd(s->current_tm.tm_hour);
+
+ case 0x0c: /* DAYS_REG */
+ return to_bcd(s->current_tm.tm_mday);
+
+ case 0x10: /* MONTHS_REG */
+ return to_bcd(s->current_tm.tm_mon + 1);
+
+ case 0x14: /* YEARS_REG */
+ return to_bcd(s->current_tm.tm_year % 100);
+
+ case 0x18: /* WEEK_REG */
+ return s->current_tm.tm_wday;
+
+ case 0x20: /* ALARM_SECONDS_REG */
+ return to_bcd(s->alarm_tm.tm_sec);
+
+ case 0x24: /* ALARM_MINUTES_REG */
+ return to_bcd(s->alarm_tm.tm_min);
+
+ case 0x28: /* ALARM_HOURS_REG */
+ if (s->pm_am)
+ return ((s->alarm_tm.tm_hour > 11) << 7) |
+ to_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1);
+ else
+ return to_bcd(s->alarm_tm.tm_hour);
+
+ case 0x2c: /* ALARM_DAYS_REG */
+ return to_bcd(s->alarm_tm.tm_mday);
+
+ case 0x30: /* ALARM_MONTHS_REG */
+ return to_bcd(s->alarm_tm.tm_mon + 1);
+
+ case 0x34: /* ALARM_YEARS_REG */
+ return to_bcd(s->alarm_tm.tm_year % 100);
+
+ case 0x40: /* RTC_CTRL_REG */
+ return (s->pm_am << 3) | (s->auto_comp << 2) |
+ (s->round << 1) | s->running;
+
+ case 0x44: /* RTC_STATUS_REG */
+ i = s->status;
+ s->status &= ~0x3d;
+ return i;
+
+ case 0x48: /* RTC_INTERRUPTS_REG */
+ return s->interrupts;
+
+ case 0x4c: /* RTC_COMP_LSB_REG */
+ return ((uint16_t) s->comp_reg) & 0xff;
+
+ case 0x50: /* RTC_COMP_MSB_REG */
+ return ((uint16_t) s->comp_reg) >> 8;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_rtc_s *s = (struct omap_rtc_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ struct tm new_tm;
+ time_t ti[2];
+
+ switch (offset) {
+ case 0x00: /* SECONDS_REG */
+#ifdef ALMDEBUG
+ printf("RTC SEC_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_sec;
+ s->ti += from_bcd(value);
+ return;
+
+ case 0x04: /* MINUTES_REG */
+#ifdef ALMDEBUG
+ printf("RTC MIN_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_min * 60;
+ s->ti += from_bcd(value) * 60;
+ return;
+
+ case 0x08: /* HOURS_REG */
+#ifdef ALMDEBUG
+ printf("RTC HRS_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_hour * 3600;
+ if (s->pm_am) {
+ s->ti += (from_bcd(value & 0x3f) & 12) * 3600;
+ s->ti += ((value >> 7) & 1) * 43200;
+ } else
+ s->ti += from_bcd(value & 0x3f) * 3600;
+ return;
+
+ case 0x0c: /* DAYS_REG */
+#ifdef ALMDEBUG
+ printf("RTC DAY_REG <-- %02x\n", value);
+#endif
+ s->ti -= s->current_tm.tm_mday * 86400;
+ s->ti += from_bcd(value) * 86400;
+ return;
+
+ case 0x10: /* MONTHS_REG */
+#ifdef ALMDEBUG
+ printf("RTC MTH_REG <-- %02x\n", value);
+#endif
+ memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
+ new_tm.tm_mon = from_bcd(value);
+ ti[0] = mktimegm(&s->current_tm);
+ ti[1] = mktimegm(&new_tm);
+
+ if (ti[0] != -1 && ti[1] != -1) {
+ s->ti -= ti[0];
+ s->ti += ti[1];
+ } else {
+ /* A less accurate version */
+ s->ti -= s->current_tm.tm_mon * 2592000;
+ s->ti += from_bcd(value) * 2592000;
+ }
+ return;
+
+ case 0x14: /* YEARS_REG */
+#ifdef ALMDEBUG
+ printf("RTC YRS_REG <-- %02x\n", value);
+#endif
+ memcpy(&new_tm, &s->current_tm, sizeof(new_tm));
+ new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100);
+ ti[0] = mktimegm(&s->current_tm);
+ ti[1] = mktimegm(&new_tm);
+
+ if (ti[0] != -1 && ti[1] != -1) {
+ s->ti -= ti[0];
+ s->ti += ti[1];
+ } else {
+ /* A less accurate version */
+ s->ti -= (s->current_tm.tm_year % 100) * 31536000;
+ s->ti += from_bcd(value) * 31536000;
+ }
+ return;
+
+ case 0x18: /* WEEK_REG */
+ return; /* Ignored */
+
+ case 0x20: /* ALARM_SECONDS_REG */
+#ifdef ALMDEBUG
+ printf("ALM SEC_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_sec = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x24: /* ALARM_MINUTES_REG */
+#ifdef ALMDEBUG
+ printf("ALM MIN_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_min = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x28: /* ALARM_HOURS_REG */
+#ifdef ALMDEBUG
+ printf("ALM HRS_REG <-- %02x\n", value);
+#endif
+ if (s->pm_am)
+ s->alarm_tm.tm_hour =
+ ((from_bcd(value & 0x3f)) % 12) +
+ ((value >> 7) & 1) * 12;
+ else
+ s->alarm_tm.tm_hour = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x2c: /* ALARM_DAYS_REG */
+#ifdef ALMDEBUG
+ printf("ALM DAY_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_mday = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x30: /* ALARM_MONTHS_REG */
+#ifdef ALMDEBUG
+ printf("ALM MON_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_mon = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x34: /* ALARM_YEARS_REG */
+#ifdef ALMDEBUG
+ printf("ALM YRS_REG <-- %02x\n", value);
+#endif
+ s->alarm_tm.tm_year = from_bcd(value);
+ omap_rtc_alarm_update(s);
+ return;
+
+ case 0x40: /* RTC_CTRL_REG */
+#ifdef ALMDEBUG
+ printf("RTC CONTROL <-- %02x\n", value);
+#endif
+ s->pm_am = (value >> 3) & 1;
+ s->auto_comp = (value >> 2) & 1;
+ s->round = (value >> 1) & 1;
+ s->running = value & 1;
+ s->status &= 0xfd;
+ s->status |= s->running << 1;
+ return;
+
+ case 0x44: /* RTC_STATUS_REG */
+#ifdef ALMDEBUG
+ printf("RTC STATUSL <-- %02x\n", value);
+#endif
+ s->status &= ~((value & 0xc0) ^ 0x80);
+ omap_rtc_interrupts_update(s);
+ return;
+
+ case 0x48: /* RTC_INTERRUPTS_REG */
+#ifdef ALMDEBUG
+ printf("RTC INTRS <-- %02x\n", value);
+#endif
+ s->interrupts = value;
+ return;
+
+ case 0x4c: /* RTC_COMP_LSB_REG */
+#ifdef ALMDEBUG
+ printf("RTC COMPLSB <-- %02x\n", value);
+#endif
+ s->comp_reg &= 0xff00;
+ s->comp_reg |= 0x00ff & value;
+ return;
+
+ case 0x50: /* RTC_COMP_MSB_REG */
+#ifdef ALMDEBUG
+ printf("RTC COMPMSB <-- %02x\n", value);
+#endif
+ s->comp_reg &= 0x00ff;
+ s->comp_reg |= 0xff00 & (value << 8);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_rtc_readfn[] = {
+ omap_rtc_read,
+ omap_badwidth_read8,
+ omap_badwidth_read8,
+};
+
+static CPUWriteMemoryFunc * const omap_rtc_writefn[] = {
+ omap_rtc_write,
+ omap_badwidth_write8,
+ omap_badwidth_write8,
+};
+
+static void omap_rtc_tick(void *opaque)
+{
+ struct omap_rtc_s *s = opaque;
+
+ if (s->round) {
+ /* Round to nearest full minute. */
+ if (s->current_tm.tm_sec < 30)
+ s->ti -= s->current_tm.tm_sec;
+ else
+ s->ti += 60 - s->current_tm.tm_sec;
+
+ s->round = 0;
+ }
+
+ memcpy(&s->current_tm, localtime(&s->ti), sizeof(s->current_tm));
+
+ if ((s->interrupts & 0x08) && s->ti == s->alarm_ti) {
+ s->status |= 0x40;
+ omap_rtc_interrupts_update(s);
+ }
+
+ if (s->interrupts & 0x04)
+ switch (s->interrupts & 3) {
+ case 0:
+ s->status |= 0x04;
+ qemu_irq_pulse(s->irq);
+ break;
+ case 1:
+ if (s->current_tm.tm_sec)
+ break;
+ s->status |= 0x08;
+ qemu_irq_pulse(s->irq);
+ break;
+ case 2:
+ if (s->current_tm.tm_sec || s->current_tm.tm_min)
+ break;
+ s->status |= 0x10;
+ qemu_irq_pulse(s->irq);
+ break;
+ case 3:
+ if (s->current_tm.tm_sec ||
+ s->current_tm.tm_min || s->current_tm.tm_hour)
+ break;
+ s->status |= 0x20;
+ qemu_irq_pulse(s->irq);
+ break;
+ }
+
+ /* Move on */
+ if (s->running)
+ s->ti ++;
+ s->tick += 1000;
+
+ /*
+ * Every full hour add a rough approximation of the compensation
+ * register to the 32kHz Timer (which drives the RTC) value.
+ */
+ if (s->auto_comp && !s->current_tm.tm_sec && !s->current_tm.tm_min)
+ s->tick += s->comp_reg * 1000 / 32768;
+
+ qemu_mod_timer(s->clk, s->tick);
+}
+
+static void omap_rtc_reset(struct omap_rtc_s *s)
+{
+ struct tm tm;
+
+ s->interrupts = 0;
+ s->comp_reg = 0;
+ s->running = 0;
+ s->pm_am = 0;
+ s->auto_comp = 0;
+ s->round = 0;
+ s->tick = qemu_get_clock(rt_clock);
+ memset(&s->alarm_tm, 0, sizeof(s->alarm_tm));
+ s->alarm_tm.tm_mday = 0x01;
+ s->status = 1 << 7;
+ qemu_get_timedate(&tm, 0);
+ s->ti = mktimegm(&tm);
+
+ omap_rtc_alarm_update(s);
+ omap_rtc_tick(s);
+}
+
+static struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base,
+ qemu_irq *irq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_rtc_s *s = (struct omap_rtc_s *)
+ qemu_mallocz(sizeof(struct omap_rtc_s));
+
+ s->irq = irq[0];
+ s->alarm = irq[1];
+ s->clk = qemu_new_timer(rt_clock, omap_rtc_tick, s);
+
+ omap_rtc_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_rtc_readfn,
+ omap_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ return s;
+}
+
+/* Multi-channel Buffered Serial Port interfaces */
+struct omap_mcbsp_s {
+ qemu_irq txirq;
+ qemu_irq rxirq;
+ qemu_irq txdrq;
+ qemu_irq rxdrq;
+
+ uint16_t spcr[2];
+ uint16_t rcr[2];
+ uint16_t xcr[2];
+ uint16_t srgr[2];
+ uint16_t mcr[2];
+ uint16_t pcr;
+ uint16_t rcer[8];
+ uint16_t xcer[8];
+ int tx_rate;
+ int rx_rate;
+ int tx_req;
+ int rx_req;
+
+ I2SCodec *codec;
+ QEMUTimer *source_timer;
+ QEMUTimer *sink_timer;
+};
+
+static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s)
+{
+ int irq;
+
+ switch ((s->spcr[0] >> 4) & 3) { /* RINTM */
+ case 0:
+ irq = (s->spcr[0] >> 1) & 1; /* RRDY */
+ break;
+ case 3:
+ irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */
+ break;
+ default:
+ irq = 0;
+ break;
+ }
+
+ if (irq)
+ qemu_irq_pulse(s->rxirq);
+
+ switch ((s->spcr[1] >> 4) & 3) { /* XINTM */
+ case 0:
+ irq = (s->spcr[1] >> 1) & 1; /* XRDY */
+ break;
+ case 3:
+ irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */
+ break;
+ default:
+ irq = 0;
+ break;
+ }
+
+ if (irq)
+ qemu_irq_pulse(s->txirq);
+}
+
+static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s)
+{
+ if ((s->spcr[0] >> 1) & 1) /* RRDY */
+ s->spcr[0] |= 1 << 2; /* RFULL */
+ s->spcr[0] |= 1 << 1; /* RRDY */
+ qemu_irq_raise(s->rxdrq);
+ omap_mcbsp_intr_update(s);
+}
+
+static void omap_mcbsp_source_tick(void *opaque)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 };
+
+ if (!s->rx_rate)
+ return;
+ if (s->rx_req)
+ printf("%s: Rx FIFO overrun\n", __FUNCTION__);
+
+ s->rx_req = s->rx_rate << bps[(s->rcr[0] >> 5) & 7];
+
+ omap_mcbsp_rx_newdata(s);
+ qemu_mod_timer(s->source_timer, qemu_get_clock(vm_clock) +
+ get_ticks_per_sec());
+}
+
+static void omap_mcbsp_rx_start(struct omap_mcbsp_s *s)
+{
+ if (!s->codec || !s->codec->rts)
+ omap_mcbsp_source_tick(s);
+ else if (s->codec->in.len) {
+ s->rx_req = s->codec->in.len;
+ omap_mcbsp_rx_newdata(s);
+ }
+}
+
+static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s)
+{
+ qemu_del_timer(s->source_timer);
+}
+
+static void omap_mcbsp_rx_done(struct omap_mcbsp_s *s)
+{
+ s->spcr[0] &= ~(1 << 1); /* RRDY */
+ qemu_irq_lower(s->rxdrq);
+ omap_mcbsp_intr_update(s);
+}
+
+static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s)
+{
+ s->spcr[1] |= 1 << 1; /* XRDY */
+ qemu_irq_raise(s->txdrq);
+ omap_mcbsp_intr_update(s);
+}
+
+static void omap_mcbsp_sink_tick(void *opaque)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ static const int bps[8] = { 0, 1, 1, 2, 2, 2, -255, -255 };
+
+ if (!s->tx_rate)
+ return;
+ if (s->tx_req)
+ printf("%s: Tx FIFO underrun\n", __FUNCTION__);
+
+ s->tx_req = s->tx_rate << bps[(s->xcr[0] >> 5) & 7];
+
+ omap_mcbsp_tx_newdata(s);
+ qemu_mod_timer(s->sink_timer, qemu_get_clock(vm_clock) +
+ get_ticks_per_sec());
+}
+
+static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s)
+{
+ if (!s->codec || !s->codec->cts)
+ omap_mcbsp_sink_tick(s);
+ else if (s->codec->out.size) {
+ s->tx_req = s->codec->out.size;
+ omap_mcbsp_tx_newdata(s);
+ }
+}
+
+static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s)
+{
+ s->spcr[1] &= ~(1 << 1); /* XRDY */
+ qemu_irq_lower(s->txdrq);
+ omap_mcbsp_intr_update(s);
+ if (s->codec && s->codec->cts)
+ s->codec->tx_swallow(s->codec->opaque);
+}
+
+static void omap_mcbsp_tx_stop(struct omap_mcbsp_s *s)
+{
+ s->tx_req = 0;
+ omap_mcbsp_tx_done(s);
+ qemu_del_timer(s->sink_timer);
+}
+
+static void omap_mcbsp_req_update(struct omap_mcbsp_s *s)
+{
+ int prev_rx_rate, prev_tx_rate;
+ int rx_rate = 0, tx_rate = 0;
+ int cpu_rate = 1500000; /* XXX */
+
+ /* TODO: check CLKSTP bit */
+ if (s->spcr[1] & (1 << 6)) { /* GRST */
+ if (s->spcr[0] & (1 << 0)) { /* RRST */
+ if ((s->srgr[1] & (1 << 13)) && /* CLKSM */
+ (s->pcr & (1 << 8))) { /* CLKRM */
+ if (~s->pcr & (1 << 7)) /* SCLKME */
+ rx_rate = cpu_rate /
+ ((s->srgr[0] & 0xff) + 1); /* CLKGDV */
+ } else
+ if (s->codec)
+ rx_rate = s->codec->rx_rate;
+ }
+
+ if (s->spcr[1] & (1 << 0)) { /* XRST */
+ if ((s->srgr[1] & (1 << 13)) && /* CLKSM */
+ (s->pcr & (1 << 9))) { /* CLKXM */
+ if (~s->pcr & (1 << 7)) /* SCLKME */
+ tx_rate = cpu_rate /
+ ((s->srgr[0] & 0xff) + 1); /* CLKGDV */
+ } else
+ if (s->codec)
+ tx_rate = s->codec->tx_rate;
+ }
+ }
+ prev_tx_rate = s->tx_rate;
+ prev_rx_rate = s->rx_rate;
+ s->tx_rate = tx_rate;
+ s->rx_rate = rx_rate;
+
+ if (s->codec)
+ s->codec->set_rate(s->codec->opaque, rx_rate, tx_rate);
+
+ if (!prev_tx_rate && tx_rate)
+ omap_mcbsp_tx_start(s);
+ else if (s->tx_rate && !tx_rate)
+ omap_mcbsp_tx_stop(s);
+
+ if (!prev_rx_rate && rx_rate)
+ omap_mcbsp_rx_start(s);
+ else if (prev_tx_rate && !tx_rate)
+ omap_mcbsp_rx_stop(s);
+}
+
+static uint32_t omap_mcbsp_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t ret;
+
+ switch (offset) {
+ case 0x00: /* DRR2 */
+ if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */
+ return 0x0000;
+ /* Fall through. */
+ case 0x02: /* DRR1 */
+ if (s->rx_req < 2) {
+ printf("%s: Rx FIFO underrun\n", __FUNCTION__);
+ omap_mcbsp_rx_done(s);
+ } else {
+ s->tx_req -= 2;
+ if (s->codec && s->codec->in.len >= 2) {
+ ret = s->codec->in.fifo[s->codec->in.start ++] << 8;
+ ret |= s->codec->in.fifo[s->codec->in.start ++];
+ s->codec->in.len -= 2;
+ } else
+ ret = 0x0000;
+ if (!s->tx_req)
+ omap_mcbsp_rx_done(s);
+ return ret;
+ }
+ return 0x0000;
+
+ case 0x04: /* DXR2 */
+ case 0x06: /* DXR1 */
+ return 0x0000;
+
+ case 0x08: /* SPCR2 */
+ return s->spcr[1];
+ case 0x0a: /* SPCR1 */
+ return s->spcr[0];
+ case 0x0c: /* RCR2 */
+ return s->rcr[1];
+ case 0x0e: /* RCR1 */
+ return s->rcr[0];
+ case 0x10: /* XCR2 */
+ return s->xcr[1];
+ case 0x12: /* XCR1 */
+ return s->xcr[0];
+ case 0x14: /* SRGR2 */
+ return s->srgr[1];
+ case 0x16: /* SRGR1 */
+ return s->srgr[0];
+ case 0x18: /* MCR2 */
+ return s->mcr[1];
+ case 0x1a: /* MCR1 */
+ return s->mcr[0];
+ case 0x1c: /* RCERA */
+ return s->rcer[0];
+ case 0x1e: /* RCERB */
+ return s->rcer[1];
+ case 0x20: /* XCERA */
+ return s->xcer[0];
+ case 0x22: /* XCERB */
+ return s->xcer[1];
+ case 0x24: /* PCR0 */
+ return s->pcr;
+ case 0x26: /* RCERC */
+ return s->rcer[2];
+ case 0x28: /* RCERD */
+ return s->rcer[3];
+ case 0x2a: /* XCERC */
+ return s->xcer[2];
+ case 0x2c: /* XCERD */
+ return s->xcer[3];
+ case 0x2e: /* RCERE */
+ return s->rcer[4];
+ case 0x30: /* RCERF */
+ return s->rcer[5];
+ case 0x32: /* XCERE */
+ return s->xcer[4];
+ case 0x34: /* XCERF */
+ return s->xcer[5];
+ case 0x36: /* RCERG */
+ return s->rcer[6];
+ case 0x38: /* RCERH */
+ return s->rcer[7];
+ case 0x3a: /* XCERG */
+ return s->xcer[6];
+ case 0x3c: /* XCERH */
+ return s->xcer[7];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mcbsp_writeh(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* DRR2 */
+ case 0x02: /* DRR1 */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x04: /* DXR2 */
+ if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */
+ return;
+ /* Fall through. */
+ case 0x06: /* DXR1 */
+ if (s->tx_req > 1) {
+ s->tx_req -= 2;
+ if (s->codec && s->codec->cts) {
+ s->codec->out.fifo[s->codec->out.len ++] = (value >> 8) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] = (value >> 0) & 0xff;
+ }
+ if (s->tx_req < 2)
+ omap_mcbsp_tx_done(s);
+ } else
+ printf("%s: Tx FIFO overrun\n", __FUNCTION__);
+ return;
+
+ case 0x08: /* SPCR2 */
+ s->spcr[1] &= 0x0002;
+ s->spcr[1] |= 0x03f9 & value;
+ s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */
+ if (~value & 1) /* XRST */
+ s->spcr[1] &= ~6;
+ omap_mcbsp_req_update(s);
+ return;
+ case 0x0a: /* SPCR1 */
+ s->spcr[0] &= 0x0006;
+ s->spcr[0] |= 0xf8f9 & value;
+ if (value & (1 << 15)) /* DLB */
+ printf("%s: Digital Loopback mode enable attempt\n", __FUNCTION__);
+ if (~value & 1) { /* RRST */
+ s->spcr[0] &= ~6;
+ s->rx_req = 0;
+ omap_mcbsp_rx_done(s);
+ }
+ omap_mcbsp_req_update(s);
+ return;
+
+ case 0x0c: /* RCR2 */
+ s->rcr[1] = value & 0xffff;
+ return;
+ case 0x0e: /* RCR1 */
+ s->rcr[0] = value & 0x7fe0;
+ return;
+ case 0x10: /* XCR2 */
+ s->xcr[1] = value & 0xffff;
+ return;
+ case 0x12: /* XCR1 */
+ s->xcr[0] = value & 0x7fe0;
+ return;
+ case 0x14: /* SRGR2 */
+ s->srgr[1] = value & 0xffff;
+ omap_mcbsp_req_update(s);
+ return;
+ case 0x16: /* SRGR1 */
+ s->srgr[0] = value & 0xffff;
+ omap_mcbsp_req_update(s);
+ return;
+ case 0x18: /* MCR2 */
+ s->mcr[1] = value & 0x03e3;
+ if (value & 3) /* XMCM */
+ printf("%s: Tx channel selection mode enable attempt\n",
+ __FUNCTION__);
+ return;
+ case 0x1a: /* MCR1 */
+ s->mcr[0] = value & 0x03e1;
+ if (value & 1) /* RMCM */
+ printf("%s: Rx channel selection mode enable attempt\n",
+ __FUNCTION__);
+ return;
+ case 0x1c: /* RCERA */
+ s->rcer[0] = value & 0xffff;
+ return;
+ case 0x1e: /* RCERB */
+ s->rcer[1] = value & 0xffff;
+ return;
+ case 0x20: /* XCERA */
+ s->xcer[0] = value & 0xffff;
+ return;
+ case 0x22: /* XCERB */
+ s->xcer[1] = value & 0xffff;
+ return;
+ case 0x24: /* PCR0 */
+ s->pcr = value & 0x7faf;
+ return;
+ case 0x26: /* RCERC */
+ s->rcer[2] = value & 0xffff;
+ return;
+ case 0x28: /* RCERD */
+ s->rcer[3] = value & 0xffff;
+ return;
+ case 0x2a: /* XCERC */
+ s->xcer[2] = value & 0xffff;
+ return;
+ case 0x2c: /* XCERD */
+ s->xcer[3] = value & 0xffff;
+ return;
+ case 0x2e: /* RCERE */
+ s->rcer[4] = value & 0xffff;
+ return;
+ case 0x30: /* RCERF */
+ s->rcer[5] = value & 0xffff;
+ return;
+ case 0x32: /* XCERE */
+ s->xcer[4] = value & 0xffff;
+ return;
+ case 0x34: /* XCERF */
+ s->xcer[5] = value & 0xffff;
+ return;
+ case 0x36: /* RCERG */
+ s->rcer[6] = value & 0xffff;
+ return;
+ case 0x38: /* RCERH */
+ s->rcer[7] = value & 0xffff;
+ return;
+ case 0x3a: /* XCERG */
+ s->xcer[6] = value & 0xffff;
+ return;
+ case 0x3c: /* XCERH */
+ s->xcer[7] = value & 0xffff;
+ return;
+ }
+
+ OMAP_BAD_REG(addr);
+}
+
+static void omap_mcbsp_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ if (offset == 0x04) { /* DXR */
+ if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */
+ return;
+ if (s->tx_req > 3) {
+ s->tx_req -= 4;
+ if (s->codec && s->codec->cts) {
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 24) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 16) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 8) & 0xff;
+ s->codec->out.fifo[s->codec->out.len ++] =
+ (value >> 0) & 0xff;
+ }
+ if (s->tx_req < 4)
+ omap_mcbsp_tx_done(s);
+ } else
+ printf("%s: Tx FIFO overrun\n", __FUNCTION__);
+ return;
+ }
+
+ omap_badwidth_write16(opaque, addr, value);
+}
+
+static CPUReadMemoryFunc * const omap_mcbsp_readfn[] = {
+ omap_badwidth_read16,
+ omap_mcbsp_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_mcbsp_writefn[] = {
+ omap_badwidth_write16,
+ omap_mcbsp_writeh,
+ omap_mcbsp_writew,
+};
+
+static void omap_mcbsp_reset(struct omap_mcbsp_s *s)
+{
+ memset(&s->spcr, 0, sizeof(s->spcr));
+ memset(&s->rcr, 0, sizeof(s->rcr));
+ memset(&s->xcr, 0, sizeof(s->xcr));
+ s->srgr[0] = 0x0001;
+ s->srgr[1] = 0x2000;
+ memset(&s->mcr, 0, sizeof(s->mcr));
+ memset(&s->pcr, 0, sizeof(s->pcr));
+ memset(&s->rcer, 0, sizeof(s->rcer));
+ memset(&s->xcer, 0, sizeof(s->xcer));
+ s->tx_req = 0;
+ s->rx_req = 0;
+ s->tx_rate = 0;
+ s->rx_rate = 0;
+ qemu_del_timer(s->source_timer);
+ qemu_del_timer(s->sink_timer);
+}
+
+struct omap_mcbsp_s *omap_mcbsp_init(target_phys_addr_t base,
+ qemu_irq *irq, qemu_irq *dma, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *)
+ qemu_mallocz(sizeof(struct omap_mcbsp_s));
+
+ s->txirq = irq[0];
+ s->rxirq = irq[1];
+ s->txdrq = dma[0];
+ s->rxdrq = dma[1];
+ s->sink_timer = qemu_new_timer(vm_clock, omap_mcbsp_sink_tick, s);
+ s->source_timer = qemu_new_timer(vm_clock, omap_mcbsp_source_tick, s);
+ omap_mcbsp_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_mcbsp_readfn,
+ omap_mcbsp_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ return s;
+}
+
+static void omap_mcbsp_i2s_swallow(void *opaque, int line, int level)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+
+ if (s->rx_rate) {
+ s->rx_req = s->codec->in.len;
+ omap_mcbsp_rx_newdata(s);
+ }
+}
+
+static void omap_mcbsp_i2s_start(void *opaque, int line, int level)
+{
+ struct omap_mcbsp_s *s = (struct omap_mcbsp_s *) opaque;
+
+ if (s->tx_rate) {
+ s->tx_req = s->codec->out.size;
+ omap_mcbsp_tx_newdata(s);
+ }
+}
+
+void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave)
+{
+ s->codec = slave;
+ slave->rx_swallow = qemu_allocate_irqs(omap_mcbsp_i2s_swallow, s, 1)[0];
+ slave->tx_start = qemu_allocate_irqs(omap_mcbsp_i2s_start, s, 1)[0];
+}
+
+/* LED Pulse Generators */
+struct omap_lpg_s {
+ QEMUTimer *tm;
+
+ uint8_t control;
+ uint8_t power;
+ int64_t on;
+ int64_t period;
+ int clk;
+ int cycle;
+};
+
+static void omap_lpg_tick(void *opaque)
+{
+ struct omap_lpg_s *s = opaque;
+
+ if (s->cycle)
+ qemu_mod_timer(s->tm, qemu_get_clock(rt_clock) + s->period - s->on);
+ else
+ qemu_mod_timer(s->tm, qemu_get_clock(rt_clock) + s->on);
+
+ s->cycle = !s->cycle;
+ printf("%s: LED is %s\n", __FUNCTION__, s->cycle ? "on" : "off");
+}
+
+static void omap_lpg_update(struct omap_lpg_s *s)
+{
+ int64_t on, period = 1, ticks = 1000;
+ static const int per[8] = { 1, 2, 4, 8, 12, 16, 20, 24 };
+
+ if (~s->control & (1 << 6)) /* LPGRES */
+ on = 0;
+ else if (s->control & (1 << 7)) /* PERM_ON */
+ on = period;
+ else {
+ period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */
+ 256 / 32);
+ on = (s->clk && s->power) ? muldiv64(ticks,
+ per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */
+ }
+
+ qemu_del_timer(s->tm);
+ if (on == period && s->on < s->period)
+ printf("%s: LED is on\n", __FUNCTION__);
+ else if (on == 0 && s->on)
+ printf("%s: LED is off\n", __FUNCTION__);
+ else if (on && (on != s->on || period != s->period)) {
+ s->cycle = 0;
+ s->on = on;
+ s->period = period;
+ omap_lpg_tick(s);
+ return;
+ }
+
+ s->on = on;
+ s->period = period;
+}
+
+static void omap_lpg_reset(struct omap_lpg_s *s)
+{
+ s->control = 0x00;
+ s->power = 0x00;
+ s->clk = 1;
+ omap_lpg_update(s);
+}
+
+static uint32_t omap_lpg_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* LCR */
+ return s->control;
+
+ case 0x04: /* PMR */
+ return s->power;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_lpg_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* LCR */
+ if (~value & (1 << 6)) /* LPGRES */
+ omap_lpg_reset(s);
+ s->control = value & 0xff;
+ omap_lpg_update(s);
+ return;
+
+ case 0x04: /* PMR */
+ s->power = value & 0x01;
+ omap_lpg_update(s);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_lpg_readfn[] = {
+ omap_lpg_read,
+ omap_badwidth_read8,
+ omap_badwidth_read8,
+};
+
+static CPUWriteMemoryFunc * const omap_lpg_writefn[] = {
+ omap_lpg_write,
+ omap_badwidth_write8,
+ omap_badwidth_write8,
+};
+
+static void omap_lpg_clk_update(void *opaque, int line, int on)
+{
+ struct omap_lpg_s *s = (struct omap_lpg_s *) opaque;
+
+ s->clk = on;
+ omap_lpg_update(s);
+}
+
+static struct omap_lpg_s *omap_lpg_init(target_phys_addr_t base, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_lpg_s *s = (struct omap_lpg_s *)
+ qemu_mallocz(sizeof(struct omap_lpg_s));
+
+ s->tm = qemu_new_timer(rt_clock, omap_lpg_tick, s);
+
+ omap_lpg_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_lpg_readfn,
+ omap_lpg_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ omap_clk_adduser(clk, qemu_allocate_irqs(omap_lpg_clk_update, s, 1)[0]);
+
+ return s;
+}
+
+/* MPUI Peripheral Bridge configuration */
+static uint32_t omap_mpui_io_read(void *opaque, target_phys_addr_t addr)
+{
+ if (addr == OMAP_MPUI_BASE) /* CMR */
+ return 0xfe4d;
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static CPUReadMemoryFunc * const omap_mpui_io_readfn[] = {
+ omap_badwidth_read16,
+ omap_mpui_io_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_mpui_io_writefn[] = {
+ omap_badwidth_write16,
+ omap_badwidth_write16,
+ omap_badwidth_write16,
+};
+
+static void omap_setup_mpui_io(struct omap_mpu_state_s *mpu)
+{
+ int iomemtype = cpu_register_io_memory(omap_mpui_io_readfn,
+ omap_mpui_io_writefn, mpu, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_MPUI_BASE, 0x7fff, iomemtype);
+}
+
+/* General chip reset */
+static void omap1_mpu_reset(void *opaque)
+{
+ struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+ omap_inth_reset(mpu->ih[0]);
+ omap_inth_reset(mpu->ih[1]);
+ omap_dma_reset(mpu->dma);
+ omap_mpu_timer_reset(mpu->timer[0]);
+ omap_mpu_timer_reset(mpu->timer[1]);
+ omap_mpu_timer_reset(mpu->timer[2]);
+ omap_wd_timer_reset(mpu->wdt);
+ omap_os_timer_reset(mpu->os_timer);
+ omap_lcdc_reset(mpu->lcd);
+ omap_ulpd_pm_reset(mpu);
+ omap_pin_cfg_reset(mpu);
+ omap_mpui_reset(mpu);
+ omap_tipb_bridge_reset(mpu->private_tipb);
+ omap_tipb_bridge_reset(mpu->public_tipb);
+ omap_dpll_reset(&mpu->dpll[0]);
+ omap_dpll_reset(&mpu->dpll[1]);
+ omap_dpll_reset(&mpu->dpll[2]);
+ omap_uart_reset(mpu->uart[0]);
+ omap_uart_reset(mpu->uart[1]);
+ omap_uart_reset(mpu->uart[2]);
+ omap_mmc_reset(mpu->mmc);
+ omap_mpuio_reset(mpu->mpuio);
+ omap_gpio_reset(mpu->gpio);
+ omap_uwire_reset(mpu->microwire);
+ omap_pwl_reset(mpu);
+ omap_pwt_reset(mpu);
+ omap_i2c_reset(mpu->i2c[0]);
+ omap_rtc_reset(mpu->rtc);
+ omap_mcbsp_reset(mpu->mcbsp1);
+ omap_mcbsp_reset(mpu->mcbsp2);
+ omap_mcbsp_reset(mpu->mcbsp3);
+ omap_lpg_reset(mpu->led[0]);
+ omap_lpg_reset(mpu->led[1]);
+ omap_clkm_reset(mpu);
+ cpu_reset(mpu->env);
+}
+
+static const struct omap_map_s {
+ target_phys_addr_t phys_dsp;
+ target_phys_addr_t phys_mpu;
+ uint32_t size;
+ const char *name;
+} omap15xx_dsp_mm[] = {
+ /* Strobe 0 */
+ { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */
+ { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */
+ { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */
+ { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */
+ { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */
+ { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */
+ { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */
+ { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */
+ { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */
+ { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */
+ { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */
+ { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */
+ { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */
+ { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */
+ { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */
+ { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */
+ { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */
+ /* Strobe 1 */
+ { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */
+
+ { 0 }
+};
+
+static void omap_setup_dsp_mapping(const struct omap_map_s *map)
+{
+ int io;
+
+ for (; map->phys_dsp; map ++) {
+ io = cpu_get_physical_page_desc(map->phys_mpu);
+
+ cpu_register_physical_memory(map->phys_dsp, map->size, io);
+ }
+}
+
+void omap_mpu_wakeup(void *opaque, int irq, int req)
+{
+ struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+ if (mpu->env->halted)
+ cpu_interrupt(mpu->env, CPU_INTERRUPT_EXITTB);
+}
+
+static const struct dma_irq_map omap1_dma_irq_map[] = {
+ { 0, OMAP_INT_DMA_CH0_6 },
+ { 0, OMAP_INT_DMA_CH1_7 },
+ { 0, OMAP_INT_DMA_CH2_8 },
+ { 0, OMAP_INT_DMA_CH3 },
+ { 0, OMAP_INT_DMA_CH4 },
+ { 0, OMAP_INT_DMA_CH5 },
+ { 1, OMAP_INT_1610_DMA_CH6 },
+ { 1, OMAP_INT_1610_DMA_CH7 },
+ { 1, OMAP_INT_1610_DMA_CH8 },
+ { 1, OMAP_INT_1610_DMA_CH9 },
+ { 1, OMAP_INT_1610_DMA_CH10 },
+ { 1, OMAP_INT_1610_DMA_CH11 },
+ { 1, OMAP_INT_1610_DMA_CH12 },
+ { 1, OMAP_INT_1610_DMA_CH13 },
+ { 1, OMAP_INT_1610_DMA_CH14 },
+ { 1, OMAP_INT_1610_DMA_CH15 }
+};
+
+/* DMA ports for OMAP1 */
+static int omap_validate_emiff_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return range_covers_byte(OMAP_EMIFF_BASE, s->sdram_size, addr);
+}
+
+static int omap_validate_emifs_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return range_covers_byte(OMAP_EMIFS_BASE, OMAP_EMIFF_BASE - OMAP_EMIFS_BASE,
+ addr);
+}
+
+static int omap_validate_imif_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return range_covers_byte(OMAP_IMIF_BASE, s->sram_size, addr);
+}
+
+static int omap_validate_tipb_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return range_covers_byte(0xfffb0000, 0xffff0000 - 0xfffb0000, addr);
+}
+
+static int omap_validate_local_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return range_covers_byte(OMAP_LOCALBUS_BASE, 0x1000000, addr);
+}
+
+static int omap_validate_tipb_mpui_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return range_covers_byte(0xe1010000, 0xe1020004 - 0xe1010000, addr);
+}
+
+struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size,
+ const char *core)
+{
+ int i;
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
+ qemu_mallocz(sizeof(struct omap_mpu_state_s));
+ ram_addr_t imif_base, emiff_base;
+ qemu_irq *cpu_irq;
+ qemu_irq dma_irqs[6];
+ DriveInfo *dinfo;
+
+ if (!core)
+ core = "ti925t";
+
+ /* Core */
+ s->mpu_model = omap310;
+ s->env = cpu_init(core);
+ if (!s->env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->sdram_size = sdram_size;
+ s->sram_size = OMAP15XX_SRAM_SIZE;
+
+ s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0];
+
+ /* Clocks */
+ omap_clk_init(s);
+
+ /* Memory-mapped stuff */
+ cpu_register_physical_memory(OMAP_EMIFF_BASE, s->sdram_size,
+ (emiff_base = qemu_ram_alloc(NULL, "omap1.dram",
+ s->sdram_size)) | IO_MEM_RAM);
+ cpu_register_physical_memory(OMAP_IMIF_BASE, s->sram_size,
+ (imif_base = qemu_ram_alloc(NULL, "omap1.sram",
+ s->sram_size)) | IO_MEM_RAM);
+
+ omap_clkm_init(0xfffece00, 0xe1008000, s);
+
+ cpu_irq = arm_pic_init_cpu(s->env);
+ s->ih[0] = omap_inth_init(0xfffecb00, 0x100, 1, &s->irq[0],
+ cpu_irq[ARM_PIC_CPU_IRQ], cpu_irq[ARM_PIC_CPU_FIQ],
+ omap_findclk(s, "arminth_ck"));
+ s->ih[1] = omap_inth_init(0xfffe0000, 0x800, 1, &s->irq[1],
+ omap_inth_get_pin(s->ih[0], OMAP_INT_15XX_IH2_IRQ),
+ NULL, omap_findclk(s, "arminth_ck"));
+
+ for (i = 0; i < 6; i ++)
+ dma_irqs[i] =
+ s->irq[omap1_dma_irq_map[i].ih][omap1_dma_irq_map[i].intr];
+ s->dma = omap_dma_init(0xfffed800, dma_irqs, s->irq[0][OMAP_INT_DMA_LCD],
+ s, omap_findclk(s, "dma_ck"), omap_dma_3_1);
+
+ s->port[emiff ].addr_valid = omap_validate_emiff_addr;
+ s->port[emifs ].addr_valid = omap_validate_emifs_addr;
+ s->port[imif ].addr_valid = omap_validate_imif_addr;
+ s->port[tipb ].addr_valid = omap_validate_tipb_addr;
+ s->port[local ].addr_valid = omap_validate_local_addr;
+ s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr;
+
+ /* Register SDRAM and SRAM DMA ports for fast transfers. */
+ soc_dma_port_add_mem_ram(s->dma,
+ emiff_base, OMAP_EMIFF_BASE, s->sdram_size);
+ soc_dma_port_add_mem_ram(s->dma,
+ imif_base, OMAP_IMIF_BASE, s->sram_size);
+
+ s->timer[0] = omap_mpu_timer_init(0xfffec500,
+ s->irq[0][OMAP_INT_TIMER1],
+ omap_findclk(s, "mputim_ck"));
+ s->timer[1] = omap_mpu_timer_init(0xfffec600,
+ s->irq[0][OMAP_INT_TIMER2],
+ omap_findclk(s, "mputim_ck"));
+ s->timer[2] = omap_mpu_timer_init(0xfffec700,
+ s->irq[0][OMAP_INT_TIMER3],
+ omap_findclk(s, "mputim_ck"));
+
+ s->wdt = omap_wd_timer_init(0xfffec800,
+ s->irq[0][OMAP_INT_WD_TIMER],
+ omap_findclk(s, "armwdt_ck"));
+
+ s->os_timer = omap_os_timer_init(0xfffb9000,
+ s->irq[1][OMAP_INT_OS_TIMER],
+ omap_findclk(s, "clk32-kHz"));
+
+ s->lcd = omap_lcdc_init(0xfffec000, s->irq[0][OMAP_INT_LCD_CTRL],
+ omap_dma_get_lcdch(s->dma), imif_base, emiff_base,
+ omap_findclk(s, "lcd_ck"));
+
+ omap_ulpd_pm_init(0xfffe0800, s);
+ omap_pin_cfg_init(0xfffe1000, s);
+ omap_id_init(s);
+
+ omap_mpui_init(0xfffec900, s);
+
+ s->private_tipb = omap_tipb_bridge_init(0xfffeca00,
+ s->irq[0][OMAP_INT_BRIDGE_PRIV],
+ omap_findclk(s, "tipb_ck"));
+ s->public_tipb = omap_tipb_bridge_init(0xfffed300,
+ s->irq[0][OMAP_INT_BRIDGE_PUB],
+ omap_findclk(s, "tipb_ck"));
+
+ omap_tcmi_init(0xfffecc00, s);
+
+ s->uart[0] = omap_uart_init(0xfffb0000, s->irq[1][OMAP_INT_UART1],
+ omap_findclk(s, "uart1_ck"),
+ omap_findclk(s, "uart1_ck"),
+ s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX],
+ "uart1",
+ serial_hds[0]);
+ s->uart[1] = omap_uart_init(0xfffb0800, s->irq[1][OMAP_INT_UART2],
+ omap_findclk(s, "uart2_ck"),
+ omap_findclk(s, "uart2_ck"),
+ s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX],
+ "uart2",
+ serial_hds[0] ? serial_hds[1] : NULL);
+ s->uart[2] = omap_uart_init(0xfffb9800, s->irq[0][OMAP_INT_UART3],
+ omap_findclk(s, "uart3_ck"),
+ omap_findclk(s, "uart3_ck"),
+ s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX],
+ "uart3",
+ serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL);
+
+ omap_dpll_init(&s->dpll[0], 0xfffecf00, omap_findclk(s, "dpll1"));
+ omap_dpll_init(&s->dpll[1], 0xfffed000, omap_findclk(s, "dpll2"));
+ omap_dpll_init(&s->dpll[2], 0xfffed100, omap_findclk(s, "dpll3"));
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = omap_mmc_init(0xfffb7800, dinfo->bdrv,
+ s->irq[1][OMAP_INT_OQN], &s->drq[OMAP_DMA_MMC_TX],
+ omap_findclk(s, "mmc_ck"));
+
+ s->mpuio = omap_mpuio_init(0xfffb5000,
+ s->irq[1][OMAP_INT_KEYBOARD], s->irq[1][OMAP_INT_MPUIO],
+ s->wakeup, omap_findclk(s, "clk32-kHz"));
+
+ s->gpio = omap_gpio_init(0xfffce000, s->irq[0][OMAP_INT_GPIO_BANK1],
+ omap_findclk(s, "arm_gpio_ck"));
+
+ s->microwire = omap_uwire_init(0xfffb3000, &s->irq[1][OMAP_INT_uWireTX],
+ s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck"));
+
+ omap_pwl_init(0xfffb5800, s, omap_findclk(s, "armxor_ck"));
+ omap_pwt_init(0xfffb6000, s, omap_findclk(s, "armxor_ck"));
+
+ s->i2c[0] = omap_i2c_init(0xfffb3800, s->irq[1][OMAP_INT_I2C],
+ &s->drq[OMAP_DMA_I2C_RX], omap_findclk(s, "mpuper_ck"));
+
+ s->rtc = omap_rtc_init(0xfffb4800, &s->irq[1][OMAP_INT_RTC_TIMER],
+ omap_findclk(s, "clk32-kHz"));
+
+ s->mcbsp1 = omap_mcbsp_init(0xfffb1800, &s->irq[1][OMAP_INT_McBSP1TX],
+ &s->drq[OMAP_DMA_MCBSP1_TX], omap_findclk(s, "dspxor_ck"));
+ s->mcbsp2 = omap_mcbsp_init(0xfffb1000, &s->irq[0][OMAP_INT_310_McBSP2_TX],
+ &s->drq[OMAP_DMA_MCBSP2_TX], omap_findclk(s, "mpuper_ck"));
+ s->mcbsp3 = omap_mcbsp_init(0xfffb7000, &s->irq[1][OMAP_INT_McBSP3TX],
+ &s->drq[OMAP_DMA_MCBSP3_TX], omap_findclk(s, "dspxor_ck"));
+
+ s->led[0] = omap_lpg_init(0xfffbd000, omap_findclk(s, "clk32-kHz"));
+ s->led[1] = omap_lpg_init(0xfffbd800, omap_findclk(s, "clk32-kHz"));
+
+ /* Register mappings not currenlty implemented:
+ * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310)
+ * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310)
+ * USB W2FC fffb4000 - fffb47ff
+ * Camera Interface fffb6800 - fffb6fff
+ * USB Host fffba000 - fffba7ff
+ * FAC fffba800 - fffbafff
+ * HDQ/1-Wire fffbc000 - fffbc7ff
+ * TIPB switches fffbc800 - fffbcfff
+ * Mailbox fffcf000 - fffcf7ff
+ * Local bus IF fffec100 - fffec1ff
+ * Local bus MMU fffec200 - fffec2ff
+ * DSP MMU fffed200 - fffed2ff
+ */
+
+ omap_setup_dsp_mapping(omap15xx_dsp_mm);
+ omap_setup_mpui_io(s);
+
+ qemu_register_reset(omap1_mpu_reset, s);
+
+ return s;
+}
diff --git a/hw/omap2.c b/hw/omap2.c
new file mode 100644
index 0000000..0f13272
--- /dev/null
+++ b/hw/omap2.c
@@ -0,0 +1,2616 @@
+/*
+ * TI OMAP processors emulation.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "blockdev.h"
+#include "hw.h"
+#include "arm-misc.h"
+#include "omap.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "flash.h"
+#include "soc_dma.h"
+#include "audio/audio.h"
+
+/* Enhanced Audio Controller (CODEC only) */
+struct omap_eac_s {
+ qemu_irq irq;
+
+ uint16_t sysconfig;
+ uint8_t config[4];
+ uint8_t control;
+ uint8_t address;
+ uint16_t data;
+ uint8_t vtol;
+ uint8_t vtsl;
+ uint16_t mixer;
+ uint16_t gain[4];
+ uint8_t att;
+ uint16_t max[7];
+
+ struct {
+ qemu_irq txdrq;
+ qemu_irq rxdrq;
+ uint32_t (*txrx)(void *opaque, uint32_t, int);
+ void *opaque;
+
+#define EAC_BUF_LEN 1024
+ uint32_t rxbuf[EAC_BUF_LEN];
+ int rxoff;
+ int rxlen;
+ int rxavail;
+ uint32_t txbuf[EAC_BUF_LEN];
+ int txlen;
+ int txavail;
+
+ int enable;
+ int rate;
+
+ uint16_t config[4];
+
+ /* These need to be moved to the actual codec */
+ QEMUSoundCard card;
+ SWVoiceIn *in_voice;
+ SWVoiceOut *out_voice;
+ int hw_enable;
+ } codec;
+
+ struct {
+ uint8_t control;
+ uint16_t config;
+ } modem, bt;
+};
+
+static inline void omap_eac_interrupt_update(struct omap_eac_s *s)
+{
+ qemu_set_irq(s->irq, (s->codec.config[1] >> 14) & 1); /* AURDI */
+}
+
+static inline void omap_eac_in_dmarequest_update(struct omap_eac_s *s)
+{
+ qemu_set_irq(s->codec.rxdrq, (s->codec.rxavail || s->codec.rxlen) &&
+ ((s->codec.config[1] >> 12) & 1)); /* DMAREN */
+}
+
+static inline void omap_eac_out_dmarequest_update(struct omap_eac_s *s)
+{
+ qemu_set_irq(s->codec.txdrq, s->codec.txlen < s->codec.txavail &&
+ ((s->codec.config[1] >> 11) & 1)); /* DMAWEN */
+}
+
+static inline void omap_eac_in_refill(struct omap_eac_s *s)
+{
+ int left = MIN(EAC_BUF_LEN - s->codec.rxlen, s->codec.rxavail) << 2;
+ int start = ((s->codec.rxoff + s->codec.rxlen) & (EAC_BUF_LEN - 1)) << 2;
+ int leftwrap = MIN(left, (EAC_BUF_LEN << 2) - start);
+ int recv = 1;
+ uint8_t *buf = (uint8_t *) s->codec.rxbuf + start;
+
+ left -= leftwrap;
+ start = 0;
+ while (leftwrap && (recv = AUD_read(s->codec.in_voice, buf + start,
+ leftwrap)) > 0) { /* Be defensive */
+ start += recv;
+ leftwrap -= recv;
+ }
+ if (recv <= 0)
+ s->codec.rxavail = 0;
+ else
+ s->codec.rxavail -= start >> 2;
+ s->codec.rxlen += start >> 2;
+
+ if (recv > 0 && left > 0) {
+ start = 0;
+ while (left && (recv = AUD_read(s->codec.in_voice,
+ (uint8_t *) s->codec.rxbuf + start,
+ left)) > 0) { /* Be defensive */
+ start += recv;
+ left -= recv;
+ }
+ if (recv <= 0)
+ s->codec.rxavail = 0;
+ else
+ s->codec.rxavail -= start >> 2;
+ s->codec.rxlen += start >> 2;
+ }
+}
+
+static inline void omap_eac_out_empty(struct omap_eac_s *s)
+{
+ int left = s->codec.txlen << 2;
+ int start = 0;
+ int sent = 1;
+
+ while (left && (sent = AUD_write(s->codec.out_voice,
+ (uint8_t *) s->codec.txbuf + start,
+ left)) > 0) { /* Be defensive */
+ start += sent;
+ left -= sent;
+ }
+
+ if (!sent) {
+ s->codec.txavail = 0;
+ omap_eac_out_dmarequest_update(s);
+ }
+
+ if (start)
+ s->codec.txlen = 0;
+}
+
+static void omap_eac_in_cb(void *opaque, int avail_b)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+
+ s->codec.rxavail = avail_b >> 2;
+ omap_eac_in_refill(s);
+ /* TODO: possibly discard current buffer if overrun */
+ omap_eac_in_dmarequest_update(s);
+}
+
+static void omap_eac_out_cb(void *opaque, int free_b)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+
+ s->codec.txavail = free_b >> 2;
+ if (s->codec.txlen)
+ omap_eac_out_empty(s);
+ else
+ omap_eac_out_dmarequest_update(s);
+}
+
+static void omap_eac_enable_update(struct omap_eac_s *s)
+{
+ s->codec.enable = !(s->codec.config[1] & 1) && /* EACPWD */
+ (s->codec.config[1] & 2) && /* AUDEN */
+ s->codec.hw_enable;
+}
+
+static const int omap_eac_fsint[4] = {
+ 8000,
+ 11025,
+ 22050,
+ 44100,
+};
+
+static const int omap_eac_fsint2[8] = {
+ 8000,
+ 11025,
+ 22050,
+ 44100,
+ 48000,
+ 0, 0, 0,
+};
+
+static const int omap_eac_fsint3[16] = {
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static void omap_eac_rate_update(struct omap_eac_s *s)
+{
+ int fsint[3];
+
+ fsint[2] = (s->codec.config[3] >> 9) & 0xf;
+ fsint[1] = (s->codec.config[2] >> 0) & 0x7;
+ fsint[0] = (s->codec.config[0] >> 6) & 0x3;
+ if (fsint[2] < 0xf)
+ s->codec.rate = omap_eac_fsint3[fsint[2]];
+ else if (fsint[1] < 0x7)
+ s->codec.rate = omap_eac_fsint2[fsint[1]];
+ else
+ s->codec.rate = omap_eac_fsint[fsint[0]];
+}
+
+static void omap_eac_volume_update(struct omap_eac_s *s)
+{
+ /* TODO */
+}
+
+static void omap_eac_format_update(struct omap_eac_s *s)
+{
+ struct audsettings fmt;
+
+ /* The hardware buffers at most one sample */
+ if (s->codec.rxlen)
+ s->codec.rxlen = 1;
+
+ if (s->codec.in_voice) {
+ AUD_set_active_in(s->codec.in_voice, 0);
+ AUD_close_in(&s->codec.card, s->codec.in_voice);
+ s->codec.in_voice = NULL;
+ }
+ if (s->codec.out_voice) {
+ omap_eac_out_empty(s);
+ AUD_set_active_out(s->codec.out_voice, 0);
+ AUD_close_out(&s->codec.card, s->codec.out_voice);
+ s->codec.out_voice = NULL;
+ s->codec.txavail = 0;
+ }
+ /* Discard what couldn't be written */
+ s->codec.txlen = 0;
+
+ omap_eac_enable_update(s);
+ if (!s->codec.enable)
+ return;
+
+ omap_eac_rate_update(s);
+ fmt.endianness = ((s->codec.config[0] >> 8) & 1); /* LI_BI */
+ fmt.nchannels = ((s->codec.config[0] >> 10) & 1) ? 2 : 1; /* MN_ST */
+ fmt.freq = s->codec.rate;
+ /* TODO: signedness possibly depends on the CODEC hardware - or
+ * does I2S specify it? */
+ /* All register writes are 16 bits so we we store 16-bit samples
+ * in the buffers regardless of AGCFR[B8_16] value. */
+ fmt.fmt = AUD_FMT_U16;
+
+ s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice,
+ "eac.codec.in", s, omap_eac_in_cb, &fmt);
+ s->codec.out_voice = AUD_open_out(&s->codec.card, s->codec.out_voice,
+ "eac.codec.out", s, omap_eac_out_cb, &fmt);
+
+ omap_eac_volume_update(s);
+
+ AUD_set_active_in(s->codec.in_voice, 1);
+ AUD_set_active_out(s->codec.out_voice, 1);
+}
+
+static void omap_eac_reset(struct omap_eac_s *s)
+{
+ s->sysconfig = 0;
+ s->config[0] = 0x0c;
+ s->config[1] = 0x09;
+ s->config[2] = 0xab;
+ s->config[3] = 0x03;
+ s->control = 0x00;
+ s->address = 0x00;
+ s->data = 0x0000;
+ s->vtol = 0x00;
+ s->vtsl = 0x00;
+ s->mixer = 0x0000;
+ s->gain[0] = 0xe7e7;
+ s->gain[1] = 0x6767;
+ s->gain[2] = 0x6767;
+ s->gain[3] = 0x6767;
+ s->att = 0xce;
+ s->max[0] = 0;
+ s->max[1] = 0;
+ s->max[2] = 0;
+ s->max[3] = 0;
+ s->max[4] = 0;
+ s->max[5] = 0;
+ s->max[6] = 0;
+
+ s->modem.control = 0x00;
+ s->modem.config = 0x0000;
+ s->bt.control = 0x00;
+ s->bt.config = 0x0000;
+ s->codec.config[0] = 0x0649;
+ s->codec.config[1] = 0x0000;
+ s->codec.config[2] = 0x0007;
+ s->codec.config[3] = 0x1ffc;
+ s->codec.rxoff = 0;
+ s->codec.rxlen = 0;
+ s->codec.txlen = 0;
+ s->codec.rxavail = 0;
+ s->codec.txavail = 0;
+
+ omap_eac_format_update(s);
+ omap_eac_interrupt_update(s);
+}
+
+static uint32_t omap_eac_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+ uint32_t ret;
+
+ switch (addr) {
+ case 0x000: /* CPCFR1 */
+ return s->config[0];
+ case 0x004: /* CPCFR2 */
+ return s->config[1];
+ case 0x008: /* CPCFR3 */
+ return s->config[2];
+ case 0x00c: /* CPCFR4 */
+ return s->config[3];
+
+ case 0x010: /* CPTCTL */
+ return s->control | ((s->codec.rxavail + s->codec.rxlen > 0) << 7) |
+ ((s->codec.txlen < s->codec.txavail) << 5);
+
+ case 0x014: /* CPTTADR */
+ return s->address;
+ case 0x018: /* CPTDATL */
+ return s->data & 0xff;
+ case 0x01c: /* CPTDATH */
+ return s->data >> 8;
+ case 0x020: /* CPTVSLL */
+ return s->vtol;
+ case 0x024: /* CPTVSLH */
+ return s->vtsl | (3 << 5); /* CRDY1 | CRDY2 */
+ case 0x040: /* MPCTR */
+ return s->modem.control;
+ case 0x044: /* MPMCCFR */
+ return s->modem.config;
+ case 0x060: /* BPCTR */
+ return s->bt.control;
+ case 0x064: /* BPMCCFR */
+ return s->bt.config;
+ case 0x080: /* AMSCFR */
+ return s->mixer;
+ case 0x084: /* AMVCTR */
+ return s->gain[0];
+ case 0x088: /* AM1VCTR */
+ return s->gain[1];
+ case 0x08c: /* AM2VCTR */
+ return s->gain[2];
+ case 0x090: /* AM3VCTR */
+ return s->gain[3];
+ case 0x094: /* ASTCTR */
+ return s->att;
+ case 0x098: /* APD1LCR */
+ return s->max[0];
+ case 0x09c: /* APD1RCR */
+ return s->max[1];
+ case 0x0a0: /* APD2LCR */
+ return s->max[2];
+ case 0x0a4: /* APD2RCR */
+ return s->max[3];
+ case 0x0a8: /* APD3LCR */
+ return s->max[4];
+ case 0x0ac: /* APD3RCR */
+ return s->max[5];
+ case 0x0b0: /* APD4R */
+ return s->max[6];
+ case 0x0b4: /* ADWR */
+ /* This should be write-only? Docs list it as read-only. */
+ return 0x0000;
+ case 0x0b8: /* ADRDR */
+ if (likely(s->codec.rxlen > 1)) {
+ ret = s->codec.rxbuf[s->codec.rxoff ++];
+ s->codec.rxlen --;
+ s->codec.rxoff &= EAC_BUF_LEN - 1;
+ return ret;
+ } else if (s->codec.rxlen) {
+ ret = s->codec.rxbuf[s->codec.rxoff ++];
+ s->codec.rxlen --;
+ s->codec.rxoff &= EAC_BUF_LEN - 1;
+ if (s->codec.rxavail)
+ omap_eac_in_refill(s);
+ omap_eac_in_dmarequest_update(s);
+ return ret;
+ }
+ return 0x0000;
+ case 0x0bc: /* AGCFR */
+ return s->codec.config[0];
+ case 0x0c0: /* AGCTR */
+ return s->codec.config[1] | ((s->codec.config[1] & 2) << 14);
+ case 0x0c4: /* AGCFR2 */
+ return s->codec.config[2];
+ case 0x0c8: /* AGCFR3 */
+ return s->codec.config[3];
+ case 0x0cc: /* MBPDMACTR */
+ case 0x0d0: /* MPDDMARR */
+ case 0x0d8: /* MPUDMARR */
+ case 0x0e4: /* BPDDMARR */
+ case 0x0ec: /* BPUDMARR */
+ return 0x0000;
+
+ case 0x100: /* VERSION_NUMBER */
+ return 0x0010;
+
+ case 0x104: /* SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x108: /* SYSSTATUS */
+ return 1 | 0xe; /* RESETDONE | stuff */
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_eac_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_eac_s *s = (struct omap_eac_s *) opaque;
+
+ switch (addr) {
+ case 0x098: /* APD1LCR */
+ case 0x09c: /* APD1RCR */
+ case 0x0a0: /* APD2LCR */
+ case 0x0a4: /* APD2RCR */
+ case 0x0a8: /* APD3LCR */
+ case 0x0ac: /* APD3RCR */
+ case 0x0b0: /* APD4R */
+ case 0x0b8: /* ADRDR */
+ case 0x0d0: /* MPDDMARR */
+ case 0x0d8: /* MPUDMARR */
+ case 0x0e4: /* BPDDMARR */
+ case 0x0ec: /* BPUDMARR */
+ case 0x100: /* VERSION_NUMBER */
+ case 0x108: /* SYSSTATUS */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x000: /* CPCFR1 */
+ s->config[0] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+ case 0x004: /* CPCFR2 */
+ s->config[1] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+ case 0x008: /* CPCFR3 */
+ s->config[2] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+ case 0x00c: /* CPCFR4 */
+ s->config[3] = value & 0xff;
+ omap_eac_format_update(s);
+ break;
+
+ case 0x010: /* CPTCTL */
+ /* Assuming TXF and TXE bits are read-only... */
+ s->control = value & 0x5f;
+ omap_eac_interrupt_update(s);
+ break;
+
+ case 0x014: /* CPTTADR */
+ s->address = value & 0xff;
+ break;
+ case 0x018: /* CPTDATL */
+ s->data &= 0xff00;
+ s->data |= value & 0xff;
+ break;
+ case 0x01c: /* CPTDATH */
+ s->data &= 0x00ff;
+ s->data |= value << 8;
+ break;
+ case 0x020: /* CPTVSLL */
+ s->vtol = value & 0xf8;
+ break;
+ case 0x024: /* CPTVSLH */
+ s->vtsl = value & 0x9f;
+ break;
+ case 0x040: /* MPCTR */
+ s->modem.control = value & 0x8f;
+ break;
+ case 0x044: /* MPMCCFR */
+ s->modem.config = value & 0x7fff;
+ break;
+ case 0x060: /* BPCTR */
+ s->bt.control = value & 0x8f;
+ break;
+ case 0x064: /* BPMCCFR */
+ s->bt.config = value & 0x7fff;
+ break;
+ case 0x080: /* AMSCFR */
+ s->mixer = value & 0x0fff;
+ break;
+ case 0x084: /* AMVCTR */
+ s->gain[0] = value & 0xffff;
+ break;
+ case 0x088: /* AM1VCTR */
+ s->gain[1] = value & 0xff7f;
+ break;
+ case 0x08c: /* AM2VCTR */
+ s->gain[2] = value & 0xff7f;
+ break;
+ case 0x090: /* AM3VCTR */
+ s->gain[3] = value & 0xff7f;
+ break;
+ case 0x094: /* ASTCTR */
+ s->att = value & 0xff;
+ break;
+
+ case 0x0b4: /* ADWR */
+ s->codec.txbuf[s->codec.txlen ++] = value;
+ if (unlikely(s->codec.txlen == EAC_BUF_LEN ||
+ s->codec.txlen == s->codec.txavail)) {
+ if (s->codec.txavail)
+ omap_eac_out_empty(s);
+ /* Discard what couldn't be written */
+ s->codec.txlen = 0;
+ }
+ break;
+
+ case 0x0bc: /* AGCFR */
+ s->codec.config[0] = value & 0x07ff;
+ omap_eac_format_update(s);
+ break;
+ case 0x0c0: /* AGCTR */
+ s->codec.config[1] = value & 0x780f;
+ omap_eac_format_update(s);
+ break;
+ case 0x0c4: /* AGCFR2 */
+ s->codec.config[2] = value & 0x003f;
+ omap_eac_format_update(s);
+ break;
+ case 0x0c8: /* AGCFR3 */
+ s->codec.config[3] = value & 0xffff;
+ omap_eac_format_update(s);
+ break;
+ case 0x0cc: /* MBPDMACTR */
+ case 0x0d4: /* MPDDMAWR */
+ case 0x0e0: /* MPUDMAWR */
+ case 0x0e8: /* BPDDMAWR */
+ case 0x0f0: /* BPUDMAWR */
+ break;
+
+ case 0x104: /* SYSCONFIG */
+ if (value & (1 << 1)) /* SOFTRESET */
+ omap_eac_reset(s);
+ s->sysconfig = value & 0x31d;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_eac_readfn[] = {
+ omap_badwidth_read16,
+ omap_eac_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_eac_writefn[] = {
+ omap_badwidth_write16,
+ omap_eac_write,
+ omap_badwidth_write16,
+};
+
+static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+ struct omap_eac_s *s = (struct omap_eac_s *)
+ qemu_mallocz(sizeof(struct omap_eac_s));
+
+ s->irq = irq;
+ s->codec.rxdrq = *drq ++;
+ s->codec.txdrq = *drq;
+ omap_eac_reset(s);
+
+ AUD_register_card("OMAP EAC", &s->codec.card);
+
+ iomemtype = cpu_register_io_memory(omap_eac_readfn,
+ omap_eac_writefn, s, DEVICE_NATIVE_ENDIAN);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ return s;
+}
+
+/* STI/XTI (emulation interface) console - reverse engineered only */
+struct omap_sti_s {
+ qemu_irq irq;
+ CharDriverState *chr;
+
+ uint32_t sysconfig;
+ uint32_t systest;
+ uint32_t irqst;
+ uint32_t irqen;
+ uint32_t clkcontrol;
+ uint32_t serial_config;
+};
+
+#define STI_TRACE_CONSOLE_CHANNEL 239
+#define STI_TRACE_CONTROL_CHANNEL 253
+
+static inline void omap_sti_interrupt_update(struct omap_sti_s *s)
+{
+ qemu_set_irq(s->irq, s->irqst & s->irqen);
+}
+
+static void omap_sti_reset(struct omap_sti_s *s)
+{
+ s->sysconfig = 0;
+ s->irqst = 0;
+ s->irqen = 0;
+ s->clkcontrol = 0;
+ s->serial_config = 0;
+
+ omap_sti_interrupt_update(s);
+}
+
+static uint32_t omap_sti_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* STI_REVISION */
+ return 0x10;
+
+ case 0x10: /* STI_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */
+ return 0x00;
+
+ case 0x18: /* STI_IRQSTATUS */
+ return s->irqst;
+
+ case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */
+ return s->irqen;
+
+ case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */
+ case 0x28: /* STI_RX_DR / XTI_RXDATA */
+ /* TODO */
+ return 0;
+
+ case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */
+ return s->clkcontrol;
+
+ case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */
+ return s->serial_config;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sti_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* STI_REVISION */
+ case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x10: /* STI_SYSCONFIG */
+ if (value & (1 << 1)) /* SOFTRESET */
+ omap_sti_reset(s);
+ s->sysconfig = value & 0xfe;
+ break;
+
+ case 0x18: /* STI_IRQSTATUS */
+ s->irqst &= ~value;
+ omap_sti_interrupt_update(s);
+ break;
+
+ case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */
+ s->irqen = value & 0xffff;
+ omap_sti_interrupt_update(s);
+ break;
+
+ case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */
+ s->clkcontrol = value & 0xff;
+ break;
+
+ case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */
+ s->serial_config = value & 0xff;
+ break;
+
+ case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */
+ case 0x28: /* STI_RX_DR / XTI_RXDATA */
+ /* TODO */
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_sti_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_sti_read,
+};
+
+static CPUWriteMemoryFunc * const omap_sti_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_sti_write,
+};
+
+static uint32_t omap_sti_fifo_read(void *opaque, target_phys_addr_t addr)
+{
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sti_fifo_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_sti_s *s = (struct omap_sti_s *) opaque;
+ int ch = addr >> 6;
+ uint8_t byte = value;
+
+ if (ch == STI_TRACE_CONTROL_CHANNEL) {
+ /* Flush channel <i>value</i>. */
+ qemu_chr_write(s->chr, (const uint8_t *) "\r", 1);
+ } else if (ch == STI_TRACE_CONSOLE_CHANNEL || 1) {
+ if (value == 0xc0 || value == 0xc3) {
+ /* Open channel <i>ch</i>. */
+ } else if (value == 0x00)
+ qemu_chr_write(s->chr, (const uint8_t *) "\n", 1);
+ else
+ qemu_chr_write(s->chr, &byte, 1);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_sti_fifo_readfn[] = {
+ omap_sti_fifo_read,
+ omap_badwidth_read8,
+ omap_badwidth_read8,
+};
+
+static CPUWriteMemoryFunc * const omap_sti_fifo_writefn[] = {
+ omap_sti_fifo_write,
+ omap_badwidth_write8,
+ omap_badwidth_write8,
+};
+
+static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta,
+ target_phys_addr_t channel_base, qemu_irq irq, omap_clk clk,
+ CharDriverState *chr)
+{
+ int iomemtype;
+ struct omap_sti_s *s = (struct omap_sti_s *)
+ qemu_mallocz(sizeof(struct omap_sti_s));
+
+ s->irq = irq;
+ omap_sti_reset(s);
+
+ s->chr = chr ?: qemu_chr_open("null", "null", NULL);
+
+ iomemtype = l4_register_io_memory(omap_sti_readfn,
+ omap_sti_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ iomemtype = cpu_register_io_memory(omap_sti_fifo_readfn,
+ omap_sti_fifo_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(channel_base, 0x10000, iomemtype);
+
+ return s;
+}
+
+/* L4 Interconnect */
+#define L4TA(n) (n)
+#define L4TAO(n) ((n) + 39)
+
+static const struct omap_l4_region_s omap_l4_region[125] = {
+ [ 1] = { 0x40800, 0x800, 32 }, /* Initiator agent */
+ [ 2] = { 0x41000, 0x1000, 32 }, /* Link agent */
+ [ 0] = { 0x40000, 0x800, 32 }, /* Address and protection */
+ [ 3] = { 0x00000, 0x1000, 32 | 16 | 8 }, /* System Control and Pinout */
+ [ 4] = { 0x01000, 0x1000, 32 | 16 | 8 }, /* L4TAO1 */
+ [ 5] = { 0x04000, 0x1000, 32 | 16 }, /* 32K Timer */
+ [ 6] = { 0x05000, 0x1000, 32 | 16 | 8 }, /* L4TAO2 */
+ [ 7] = { 0x08000, 0x800, 32 }, /* PRCM Region A */
+ [ 8] = { 0x08800, 0x800, 32 }, /* PRCM Region B */
+ [ 9] = { 0x09000, 0x1000, 32 | 16 | 8 }, /* L4TAO */
+ [ 10] = { 0x12000, 0x1000, 32 | 16 | 8 }, /* Test (BCM) */
+ [ 11] = { 0x13000, 0x1000, 32 | 16 | 8 }, /* L4TA1 */
+ [ 12] = { 0x14000, 0x1000, 32 }, /* Test/emulation (TAP) */
+ [ 13] = { 0x15000, 0x1000, 32 | 16 | 8 }, /* L4TA2 */
+ [ 14] = { 0x18000, 0x1000, 32 | 16 | 8 }, /* GPIO1 */
+ [ 16] = { 0x1a000, 0x1000, 32 | 16 | 8 }, /* GPIO2 */
+ [ 18] = { 0x1c000, 0x1000, 32 | 16 | 8 }, /* GPIO3 */
+ [ 19] = { 0x1e000, 0x1000, 32 | 16 | 8 }, /* GPIO4 */
+ [ 15] = { 0x19000, 0x1000, 32 | 16 | 8 }, /* Quad GPIO TOP */
+ [ 17] = { 0x1b000, 0x1000, 32 | 16 | 8 }, /* L4TA3 */
+ [ 20] = { 0x20000, 0x1000, 32 | 16 | 8 }, /* WD Timer 1 (Secure) */
+ [ 22] = { 0x22000, 0x1000, 32 | 16 | 8 }, /* WD Timer 2 (OMAP) */
+ [ 21] = { 0x21000, 0x1000, 32 | 16 | 8 }, /* Dual WD timer TOP */
+ [ 23] = { 0x23000, 0x1000, 32 | 16 | 8 }, /* L4TA4 */
+ [ 24] = { 0x28000, 0x1000, 32 | 16 | 8 }, /* GP Timer 1 */
+ [ 25] = { 0x29000, 0x1000, 32 | 16 | 8 }, /* L4TA7 */
+ [ 26] = { 0x48000, 0x2000, 32 | 16 | 8 }, /* Emulation (ARM11ETB) */
+ [ 27] = { 0x4a000, 0x1000, 32 | 16 | 8 }, /* L4TA9 */
+ [ 28] = { 0x50000, 0x400, 32 | 16 | 8 }, /* Display top */
+ [ 29] = { 0x50400, 0x400, 32 | 16 | 8 }, /* Display control */
+ [ 30] = { 0x50800, 0x400, 32 | 16 | 8 }, /* Display RFBI */
+ [ 31] = { 0x50c00, 0x400, 32 | 16 | 8 }, /* Display encoder */
+ [ 32] = { 0x51000, 0x1000, 32 | 16 | 8 }, /* L4TA10 */
+ [ 33] = { 0x52000, 0x400, 32 | 16 | 8 }, /* Camera top */
+ [ 34] = { 0x52400, 0x400, 32 | 16 | 8 }, /* Camera core */
+ [ 35] = { 0x52800, 0x400, 32 | 16 | 8 }, /* Camera DMA */
+ [ 36] = { 0x52c00, 0x400, 32 | 16 | 8 }, /* Camera MMU */
+ [ 37] = { 0x53000, 0x1000, 32 | 16 | 8 }, /* L4TA11 */
+ [ 38] = { 0x56000, 0x1000, 32 | 16 | 8 }, /* sDMA */
+ [ 39] = { 0x57000, 0x1000, 32 | 16 | 8 }, /* L4TA12 */
+ [ 40] = { 0x58000, 0x1000, 32 | 16 | 8 }, /* SSI top */
+ [ 41] = { 0x59000, 0x1000, 32 | 16 | 8 }, /* SSI GDD */
+ [ 42] = { 0x5a000, 0x1000, 32 | 16 | 8 }, /* SSI Port1 */
+ [ 43] = { 0x5b000, 0x1000, 32 | 16 | 8 }, /* SSI Port2 */
+ [ 44] = { 0x5c000, 0x1000, 32 | 16 | 8 }, /* L4TA13 */
+ [ 45] = { 0x5e000, 0x1000, 32 | 16 | 8 }, /* USB OTG */
+ [ 46] = { 0x5f000, 0x1000, 32 | 16 | 8 }, /* L4TAO4 */
+ [ 47] = { 0x60000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER1SDRC) */
+ [ 48] = { 0x61000, 0x1000, 32 | 16 | 8 }, /* L4TA14 */
+ [ 49] = { 0x62000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER2GPMC) */
+ [ 50] = { 0x63000, 0x1000, 32 | 16 | 8 }, /* L4TA15 */
+ [ 51] = { 0x64000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER3OCM) */
+ [ 52] = { 0x65000, 0x1000, 32 | 16 | 8 }, /* L4TA16 */
+ [ 53] = { 0x66000, 0x300, 32 | 16 | 8 }, /* Emulation (WIN_TRACER4L4) */
+ [ 54] = { 0x67000, 0x1000, 32 | 16 | 8 }, /* L4TA17 */
+ [ 55] = { 0x68000, 0x1000, 32 | 16 | 8 }, /* Emulation (XTI) */
+ [ 56] = { 0x69000, 0x1000, 32 | 16 | 8 }, /* L4TA18 */
+ [ 57] = { 0x6a000, 0x1000, 16 | 8 }, /* UART1 */
+ [ 58] = { 0x6b000, 0x1000, 32 | 16 | 8 }, /* L4TA19 */
+ [ 59] = { 0x6c000, 0x1000, 16 | 8 }, /* UART2 */
+ [ 60] = { 0x6d000, 0x1000, 32 | 16 | 8 }, /* L4TA20 */
+ [ 61] = { 0x6e000, 0x1000, 16 | 8 }, /* UART3 */
+ [ 62] = { 0x6f000, 0x1000, 32 | 16 | 8 }, /* L4TA21 */
+ [ 63] = { 0x70000, 0x1000, 16 }, /* I2C1 */
+ [ 64] = { 0x71000, 0x1000, 32 | 16 | 8 }, /* L4TAO5 */
+ [ 65] = { 0x72000, 0x1000, 16 }, /* I2C2 */
+ [ 66] = { 0x73000, 0x1000, 32 | 16 | 8 }, /* L4TAO6 */
+ [ 67] = { 0x74000, 0x1000, 16 }, /* McBSP1 */
+ [ 68] = { 0x75000, 0x1000, 32 | 16 | 8 }, /* L4TAO7 */
+ [ 69] = { 0x76000, 0x1000, 16 }, /* McBSP2 */
+ [ 70] = { 0x77000, 0x1000, 32 | 16 | 8 }, /* L4TAO8 */
+ [ 71] = { 0x24000, 0x1000, 32 | 16 | 8 }, /* WD Timer 3 (DSP) */
+ [ 72] = { 0x25000, 0x1000, 32 | 16 | 8 }, /* L4TA5 */
+ [ 73] = { 0x26000, 0x1000, 32 | 16 | 8 }, /* WD Timer 4 (IVA) */
+ [ 74] = { 0x27000, 0x1000, 32 | 16 | 8 }, /* L4TA6 */
+ [ 75] = { 0x2a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 2 */
+ [ 76] = { 0x2b000, 0x1000, 32 | 16 | 8 }, /* L4TA8 */
+ [ 77] = { 0x78000, 0x1000, 32 | 16 | 8 }, /* GP Timer 3 */
+ [ 78] = { 0x79000, 0x1000, 32 | 16 | 8 }, /* L4TA22 */
+ [ 79] = { 0x7a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 4 */
+ [ 80] = { 0x7b000, 0x1000, 32 | 16 | 8 }, /* L4TA23 */
+ [ 81] = { 0x7c000, 0x1000, 32 | 16 | 8 }, /* GP Timer 5 */
+ [ 82] = { 0x7d000, 0x1000, 32 | 16 | 8 }, /* L4TA24 */
+ [ 83] = { 0x7e000, 0x1000, 32 | 16 | 8 }, /* GP Timer 6 */
+ [ 84] = { 0x7f000, 0x1000, 32 | 16 | 8 }, /* L4TA25 */
+ [ 85] = { 0x80000, 0x1000, 32 | 16 | 8 }, /* GP Timer 7 */
+ [ 86] = { 0x81000, 0x1000, 32 | 16 | 8 }, /* L4TA26 */
+ [ 87] = { 0x82000, 0x1000, 32 | 16 | 8 }, /* GP Timer 8 */
+ [ 88] = { 0x83000, 0x1000, 32 | 16 | 8 }, /* L4TA27 */
+ [ 89] = { 0x84000, 0x1000, 32 | 16 | 8 }, /* GP Timer 9 */
+ [ 90] = { 0x85000, 0x1000, 32 | 16 | 8 }, /* L4TA28 */
+ [ 91] = { 0x86000, 0x1000, 32 | 16 | 8 }, /* GP Timer 10 */
+ [ 92] = { 0x87000, 0x1000, 32 | 16 | 8 }, /* L4TA29 */
+ [ 93] = { 0x88000, 0x1000, 32 | 16 | 8 }, /* GP Timer 11 */
+ [ 94] = { 0x89000, 0x1000, 32 | 16 | 8 }, /* L4TA30 */
+ [ 95] = { 0x8a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 12 */
+ [ 96] = { 0x8b000, 0x1000, 32 | 16 | 8 }, /* L4TA31 */
+ [ 97] = { 0x90000, 0x1000, 16 }, /* EAC */
+ [ 98] = { 0x91000, 0x1000, 32 | 16 | 8 }, /* L4TA32 */
+ [ 99] = { 0x92000, 0x1000, 16 }, /* FAC */
+ [100] = { 0x93000, 0x1000, 32 | 16 | 8 }, /* L4TA33 */
+ [101] = { 0x94000, 0x1000, 32 | 16 | 8 }, /* IPC (MAILBOX) */
+ [102] = { 0x95000, 0x1000, 32 | 16 | 8 }, /* L4TA34 */
+ [103] = { 0x98000, 0x1000, 32 | 16 | 8 }, /* SPI1 */
+ [104] = { 0x99000, 0x1000, 32 | 16 | 8 }, /* L4TA35 */
+ [105] = { 0x9a000, 0x1000, 32 | 16 | 8 }, /* SPI2 */
+ [106] = { 0x9b000, 0x1000, 32 | 16 | 8 }, /* L4TA36 */
+ [107] = { 0x9c000, 0x1000, 16 | 8 }, /* MMC SDIO */
+ [108] = { 0x9d000, 0x1000, 32 | 16 | 8 }, /* L4TAO9 */
+ [109] = { 0x9e000, 0x1000, 32 | 16 | 8 }, /* MS_PRO */
+ [110] = { 0x9f000, 0x1000, 32 | 16 | 8 }, /* L4TAO10 */
+ [111] = { 0xa0000, 0x1000, 32 }, /* RNG */
+ [112] = { 0xa1000, 0x1000, 32 | 16 | 8 }, /* L4TAO11 */
+ [113] = { 0xa2000, 0x1000, 32 }, /* DES3DES */
+ [114] = { 0xa3000, 0x1000, 32 | 16 | 8 }, /* L4TAO12 */
+ [115] = { 0xa4000, 0x1000, 32 }, /* SHA1MD5 */
+ [116] = { 0xa5000, 0x1000, 32 | 16 | 8 }, /* L4TAO13 */
+ [117] = { 0xa6000, 0x1000, 32 }, /* AES */
+ [118] = { 0xa7000, 0x1000, 32 | 16 | 8 }, /* L4TA37 */
+ [119] = { 0xa8000, 0x2000, 32 }, /* PKA */
+ [120] = { 0xaa000, 0x1000, 32 | 16 | 8 }, /* L4TA38 */
+ [121] = { 0xb0000, 0x1000, 32 }, /* MG */
+ [122] = { 0xb1000, 0x1000, 32 | 16 | 8 },
+ [123] = { 0xb2000, 0x1000, 32 }, /* HDQ/1-Wire */
+ [124] = { 0xb3000, 0x1000, 32 | 16 | 8 }, /* L4TA39 */
+};
+
+static const struct omap_l4_agent_info_s omap_l4_agent_info[54] = {
+ { 0, 0, 3, 2 }, /* L4IA initiatior agent */
+ { L4TAO(1), 3, 2, 1 }, /* Control and pinout module */
+ { L4TAO(2), 5, 2, 1 }, /* 32K timer */
+ { L4TAO(3), 7, 3, 2 }, /* PRCM */
+ { L4TA(1), 10, 2, 1 }, /* BCM */
+ { L4TA(2), 12, 2, 1 }, /* Test JTAG */
+ { L4TA(3), 14, 6, 3 }, /* Quad GPIO */
+ { L4TA(4), 20, 4, 3 }, /* WD timer 1/2 */
+ { L4TA(7), 24, 2, 1 }, /* GP timer 1 */
+ { L4TA(9), 26, 2, 1 }, /* ATM11 ETB */
+ { L4TA(10), 28, 5, 4 }, /* Display subsystem */
+ { L4TA(11), 33, 5, 4 }, /* Camera subsystem */
+ { L4TA(12), 38, 2, 1 }, /* sDMA */
+ { L4TA(13), 40, 5, 4 }, /* SSI */
+ { L4TAO(4), 45, 2, 1 }, /* USB */
+ { L4TA(14), 47, 2, 1 }, /* Win Tracer1 */
+ { L4TA(15), 49, 2, 1 }, /* Win Tracer2 */
+ { L4TA(16), 51, 2, 1 }, /* Win Tracer3 */
+ { L4TA(17), 53, 2, 1 }, /* Win Tracer4 */
+ { L4TA(18), 55, 2, 1 }, /* XTI */
+ { L4TA(19), 57, 2, 1 }, /* UART1 */
+ { L4TA(20), 59, 2, 1 }, /* UART2 */
+ { L4TA(21), 61, 2, 1 }, /* UART3 */
+ { L4TAO(5), 63, 2, 1 }, /* I2C1 */
+ { L4TAO(6), 65, 2, 1 }, /* I2C2 */
+ { L4TAO(7), 67, 2, 1 }, /* McBSP1 */
+ { L4TAO(8), 69, 2, 1 }, /* McBSP2 */
+ { L4TA(5), 71, 2, 1 }, /* WD Timer 3 (DSP) */
+ { L4TA(6), 73, 2, 1 }, /* WD Timer 4 (IVA) */
+ { L4TA(8), 75, 2, 1 }, /* GP Timer 2 */
+ { L4TA(22), 77, 2, 1 }, /* GP Timer 3 */
+ { L4TA(23), 79, 2, 1 }, /* GP Timer 4 */
+ { L4TA(24), 81, 2, 1 }, /* GP Timer 5 */
+ { L4TA(25), 83, 2, 1 }, /* GP Timer 6 */
+ { L4TA(26), 85, 2, 1 }, /* GP Timer 7 */
+ { L4TA(27), 87, 2, 1 }, /* GP Timer 8 */
+ { L4TA(28), 89, 2, 1 }, /* GP Timer 9 */
+ { L4TA(29), 91, 2, 1 }, /* GP Timer 10 */
+ { L4TA(30), 93, 2, 1 }, /* GP Timer 11 */
+ { L4TA(31), 95, 2, 1 }, /* GP Timer 12 */
+ { L4TA(32), 97, 2, 1 }, /* EAC */
+ { L4TA(33), 99, 2, 1 }, /* FAC */
+ { L4TA(34), 101, 2, 1 }, /* IPC */
+ { L4TA(35), 103, 2, 1 }, /* SPI1 */
+ { L4TA(36), 105, 2, 1 }, /* SPI2 */
+ { L4TAO(9), 107, 2, 1 }, /* MMC SDIO */
+ { L4TAO(10), 109, 2, 1 },
+ { L4TAO(11), 111, 2, 1 }, /* RNG */
+ { L4TAO(12), 113, 2, 1 }, /* DES3DES */
+ { L4TAO(13), 115, 2, 1 }, /* SHA1MD5 */
+ { L4TA(37), 117, 2, 1 }, /* AES */
+ { L4TA(38), 119, 2, 1 }, /* PKA */
+ { -1, 121, 2, 1 },
+ { L4TA(39), 123, 2, 1 }, /* HDQ/1-Wire */
+};
+
+#define omap_l4ta(bus, cs) \
+ omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TA(cs))
+#define omap_l4tao(bus, cs) \
+ omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TAO(cs))
+
+/* Power, Reset, and Clock Management */
+struct omap_prcm_s {
+ qemu_irq irq[3];
+ struct omap_mpu_state_s *mpu;
+
+ uint32_t irqst[3];
+ uint32_t irqen[3];
+
+ uint32_t sysconfig;
+ uint32_t voltctrl;
+ uint32_t scratch[20];
+
+ uint32_t clksrc[1];
+ uint32_t clkout[1];
+ uint32_t clkemul[1];
+ uint32_t clkpol[1];
+ uint32_t clksel[8];
+ uint32_t clken[12];
+ uint32_t clkctrl[4];
+ uint32_t clkidle[7];
+ uint32_t setuptime[2];
+
+ uint32_t wkup[3];
+ uint32_t wken[3];
+ uint32_t wkst[3];
+ uint32_t rst[4];
+ uint32_t rstctrl[1];
+ uint32_t power[4];
+ uint32_t rsttime_wkup;
+
+ uint32_t ev;
+ uint32_t evtime[2];
+
+ int dpll_lock, apll_lock[2];
+};
+
+static void omap_prcm_int_update(struct omap_prcm_s *s, int dom)
+{
+ qemu_set_irq(s->irq[dom], s->irqst[dom] & s->irqen[dom]);
+ /* XXX or is the mask applied before PRCM_IRQSTATUS_* ? */
+}
+
+static uint32_t omap_prcm_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
+ uint32_t ret;
+
+ switch (addr) {
+ case 0x000: /* PRCM_REVISION */
+ return 0x10;
+
+ case 0x010: /* PRCM_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x018: /* PRCM_IRQSTATUS_MPU */
+ return s->irqst[0];
+
+ case 0x01c: /* PRCM_IRQENABLE_MPU */
+ return s->irqen[0];
+
+ case 0x050: /* PRCM_VOLTCTRL */
+ return s->voltctrl;
+ case 0x054: /* PRCM_VOLTST */
+ return s->voltctrl & 3;
+
+ case 0x060: /* PRCM_CLKSRC_CTRL */
+ return s->clksrc[0];
+ case 0x070: /* PRCM_CLKOUT_CTRL */
+ return s->clkout[0];
+ case 0x078: /* PRCM_CLKEMUL_CTRL */
+ return s->clkemul[0];
+ case 0x080: /* PRCM_CLKCFG_CTRL */
+ case 0x084: /* PRCM_CLKCFG_STATUS */
+ return 0;
+
+ case 0x090: /* PRCM_VOLTSETUP */
+ return s->setuptime[0];
+
+ case 0x094: /* PRCM_CLKSSETUP */
+ return s->setuptime[1];
+
+ case 0x098: /* PRCM_POLCTRL */
+ return s->clkpol[0];
+
+ case 0x0b0: /* GENERAL_PURPOSE1 */
+ case 0x0b4: /* GENERAL_PURPOSE2 */
+ case 0x0b8: /* GENERAL_PURPOSE3 */
+ case 0x0bc: /* GENERAL_PURPOSE4 */
+ case 0x0c0: /* GENERAL_PURPOSE5 */
+ case 0x0c4: /* GENERAL_PURPOSE6 */
+ case 0x0c8: /* GENERAL_PURPOSE7 */
+ case 0x0cc: /* GENERAL_PURPOSE8 */
+ case 0x0d0: /* GENERAL_PURPOSE9 */
+ case 0x0d4: /* GENERAL_PURPOSE10 */
+ case 0x0d8: /* GENERAL_PURPOSE11 */
+ case 0x0dc: /* GENERAL_PURPOSE12 */
+ case 0x0e0: /* GENERAL_PURPOSE13 */
+ case 0x0e4: /* GENERAL_PURPOSE14 */
+ case 0x0e8: /* GENERAL_PURPOSE15 */
+ case 0x0ec: /* GENERAL_PURPOSE16 */
+ case 0x0f0: /* GENERAL_PURPOSE17 */
+ case 0x0f4: /* GENERAL_PURPOSE18 */
+ case 0x0f8: /* GENERAL_PURPOSE19 */
+ case 0x0fc: /* GENERAL_PURPOSE20 */
+ return s->scratch[(addr - 0xb0) >> 2];
+
+ case 0x140: /* CM_CLKSEL_MPU */
+ return s->clksel[0];
+ case 0x148: /* CM_CLKSTCTRL_MPU */
+ return s->clkctrl[0];
+
+ case 0x158: /* RM_RSTST_MPU */
+ return s->rst[0];
+ case 0x1c8: /* PM_WKDEP_MPU */
+ return s->wkup[0];
+ case 0x1d4: /* PM_EVGENCTRL_MPU */
+ return s->ev;
+ case 0x1d8: /* PM_EVEGENONTIM_MPU */
+ return s->evtime[0];
+ case 0x1dc: /* PM_EVEGENOFFTIM_MPU */
+ return s->evtime[1];
+ case 0x1e0: /* PM_PWSTCTRL_MPU */
+ return s->power[0];
+ case 0x1e4: /* PM_PWSTST_MPU */
+ return 0;
+
+ case 0x200: /* CM_FCLKEN1_CORE */
+ return s->clken[0];
+ case 0x204: /* CM_FCLKEN2_CORE */
+ return s->clken[1];
+ case 0x210: /* CM_ICLKEN1_CORE */
+ return s->clken[2];
+ case 0x214: /* CM_ICLKEN2_CORE */
+ return s->clken[3];
+ case 0x21c: /* CM_ICLKEN4_CORE */
+ return s->clken[4];
+
+ case 0x220: /* CM_IDLEST1_CORE */
+ /* TODO: check the actual iclk status */
+ return 0x7ffffff9;
+ case 0x224: /* CM_IDLEST2_CORE */
+ /* TODO: check the actual iclk status */
+ return 0x00000007;
+ case 0x22c: /* CM_IDLEST4_CORE */
+ /* TODO: check the actual iclk status */
+ return 0x0000001f;
+
+ case 0x230: /* CM_AUTOIDLE1_CORE */
+ return s->clkidle[0];
+ case 0x234: /* CM_AUTOIDLE2_CORE */
+ return s->clkidle[1];
+ case 0x238: /* CM_AUTOIDLE3_CORE */
+ return s->clkidle[2];
+ case 0x23c: /* CM_AUTOIDLE4_CORE */
+ return s->clkidle[3];
+
+ case 0x240: /* CM_CLKSEL1_CORE */
+ return s->clksel[1];
+ case 0x244: /* CM_CLKSEL2_CORE */
+ return s->clksel[2];
+
+ case 0x248: /* CM_CLKSTCTRL_CORE */
+ return s->clkctrl[1];
+
+ case 0x2a0: /* PM_WKEN1_CORE */
+ return s->wken[0];
+ case 0x2a4: /* PM_WKEN2_CORE */
+ return s->wken[1];
+
+ case 0x2b0: /* PM_WKST1_CORE */
+ return s->wkst[0];
+ case 0x2b4: /* PM_WKST2_CORE */
+ return s->wkst[1];
+ case 0x2c8: /* PM_WKDEP_CORE */
+ return 0x1e;
+
+ case 0x2e0: /* PM_PWSTCTRL_CORE */
+ return s->power[1];
+ case 0x2e4: /* PM_PWSTST_CORE */
+ return 0x000030 | (s->power[1] & 0xfc00);
+
+ case 0x300: /* CM_FCLKEN_GFX */
+ return s->clken[5];
+ case 0x310: /* CM_ICLKEN_GFX */
+ return s->clken[6];
+ case 0x320: /* CM_IDLEST_GFX */
+ /* TODO: check the actual iclk status */
+ return 0x00000001;
+ case 0x340: /* CM_CLKSEL_GFX */
+ return s->clksel[3];
+ case 0x348: /* CM_CLKSTCTRL_GFX */
+ return s->clkctrl[2];
+ case 0x350: /* RM_RSTCTRL_GFX */
+ return s->rstctrl[0];
+ case 0x358: /* RM_RSTST_GFX */
+ return s->rst[1];
+ case 0x3c8: /* PM_WKDEP_GFX */
+ return s->wkup[1];
+
+ case 0x3e0: /* PM_PWSTCTRL_GFX */
+ return s->power[2];
+ case 0x3e4: /* PM_PWSTST_GFX */
+ return s->power[2] & 3;
+
+ case 0x400: /* CM_FCLKEN_WKUP */
+ return s->clken[7];
+ case 0x410: /* CM_ICLKEN_WKUP */
+ return s->clken[8];
+ case 0x420: /* CM_IDLEST_WKUP */
+ /* TODO: check the actual iclk status */
+ return 0x0000003f;
+ case 0x430: /* CM_AUTOIDLE_WKUP */
+ return s->clkidle[4];
+ case 0x440: /* CM_CLKSEL_WKUP */
+ return s->clksel[4];
+ case 0x450: /* RM_RSTCTRL_WKUP */
+ return 0;
+ case 0x454: /* RM_RSTTIME_WKUP */
+ return s->rsttime_wkup;
+ case 0x458: /* RM_RSTST_WKUP */
+ return s->rst[2];
+ case 0x4a0: /* PM_WKEN_WKUP */
+ return s->wken[2];
+ case 0x4b0: /* PM_WKST_WKUP */
+ return s->wkst[2];
+
+ case 0x500: /* CM_CLKEN_PLL */
+ return s->clken[9];
+ case 0x520: /* CM_IDLEST_CKGEN */
+ ret = 0x0000070 | (s->apll_lock[0] << 9) | (s->apll_lock[1] << 8);
+ if (!(s->clksel[6] & 3))
+ /* Core uses 32-kHz clock */
+ ret |= 3 << 0;
+ else if (!s->dpll_lock)
+ /* DPLL not locked, core uses ref_clk */
+ ret |= 1 << 0;
+ else
+ /* Core uses DPLL */
+ ret |= 2 << 0;
+ return ret;
+ case 0x530: /* CM_AUTOIDLE_PLL */
+ return s->clkidle[5];
+ case 0x540: /* CM_CLKSEL1_PLL */
+ return s->clksel[5];
+ case 0x544: /* CM_CLKSEL2_PLL */
+ return s->clksel[6];
+
+ case 0x800: /* CM_FCLKEN_DSP */
+ return s->clken[10];
+ case 0x810: /* CM_ICLKEN_DSP */
+ return s->clken[11];
+ case 0x820: /* CM_IDLEST_DSP */
+ /* TODO: check the actual iclk status */
+ return 0x00000103;
+ case 0x830: /* CM_AUTOIDLE_DSP */
+ return s->clkidle[6];
+ case 0x840: /* CM_CLKSEL_DSP */
+ return s->clksel[7];
+ case 0x848: /* CM_CLKSTCTRL_DSP */
+ return s->clkctrl[3];
+ case 0x850: /* RM_RSTCTRL_DSP */
+ return 0;
+ case 0x858: /* RM_RSTST_DSP */
+ return s->rst[3];
+ case 0x8c8: /* PM_WKDEP_DSP */
+ return s->wkup[2];
+ case 0x8e0: /* PM_PWSTCTRL_DSP */
+ return s->power[3];
+ case 0x8e4: /* PM_PWSTST_DSP */
+ return 0x008030 | (s->power[3] & 0x3003);
+
+ case 0x8f0: /* PRCM_IRQSTATUS_DSP */
+ return s->irqst[1];
+ case 0x8f4: /* PRCM_IRQENABLE_DSP */
+ return s->irqen[1];
+
+ case 0x8f8: /* PRCM_IRQSTATUS_IVA */
+ return s->irqst[2];
+ case 0x8fc: /* PRCM_IRQENABLE_IVA */
+ return s->irqen[2];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_prcm_apll_update(struct omap_prcm_s *s)
+{
+ int mode[2];
+
+ mode[0] = (s->clken[9] >> 6) & 3;
+ s->apll_lock[0] = (mode[0] == 3);
+ mode[1] = (s->clken[9] >> 2) & 3;
+ s->apll_lock[1] = (mode[1] == 3);
+ /* TODO: update clocks */
+
+ if (mode[0] == 1 || mode[0] == 2 || mode[1] == 1 || mode[1] == 2)
+ fprintf(stderr, "%s: bad EN_54M_PLL or bad EN_96M_PLL\n",
+ __FUNCTION__);
+}
+
+static void omap_prcm_dpll_update(struct omap_prcm_s *s)
+{
+ omap_clk dpll = omap_findclk(s->mpu, "dpll");
+ omap_clk dpll_x2 = omap_findclk(s->mpu, "dpll");
+ omap_clk core = omap_findclk(s->mpu, "core_clk");
+ int mode = (s->clken[9] >> 0) & 3;
+ int mult, div;
+
+ mult = (s->clksel[5] >> 12) & 0x3ff;
+ div = (s->clksel[5] >> 8) & 0xf;
+ if (mult == 0 || mult == 1)
+ mode = 1; /* Bypass */
+
+ s->dpll_lock = 0;
+ switch (mode) {
+ case 0:
+ fprintf(stderr, "%s: bad EN_DPLL\n", __FUNCTION__);
+ break;
+ case 1: /* Low-power bypass mode (Default) */
+ case 2: /* Fast-relock bypass mode */
+ omap_clk_setrate(dpll, 1, 1);
+ omap_clk_setrate(dpll_x2, 1, 1);
+ break;
+ case 3: /* Lock mode */
+ s->dpll_lock = 1; /* After 20 FINT cycles (ref_clk / (div + 1)). */
+
+ omap_clk_setrate(dpll, div + 1, mult);
+ omap_clk_setrate(dpll_x2, div + 1, mult * 2);
+ break;
+ }
+
+ switch ((s->clksel[6] >> 0) & 3) {
+ case 0:
+ omap_clk_reparent(core, omap_findclk(s->mpu, "clk32-kHz"));
+ break;
+ case 1:
+ omap_clk_reparent(core, dpll);
+ break;
+ case 2:
+ /* Default */
+ omap_clk_reparent(core, dpll_x2);
+ break;
+ case 3:
+ fprintf(stderr, "%s: bad CORE_CLK_SRC\n", __FUNCTION__);
+ break;
+ }
+}
+
+static void omap_prcm_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_prcm_s *s = (struct omap_prcm_s *) opaque;
+
+ switch (addr) {
+ case 0x000: /* PRCM_REVISION */
+ case 0x054: /* PRCM_VOLTST */
+ case 0x084: /* PRCM_CLKCFG_STATUS */
+ case 0x1e4: /* PM_PWSTST_MPU */
+ case 0x220: /* CM_IDLEST1_CORE */
+ case 0x224: /* CM_IDLEST2_CORE */
+ case 0x22c: /* CM_IDLEST4_CORE */
+ case 0x2c8: /* PM_WKDEP_CORE */
+ case 0x2e4: /* PM_PWSTST_CORE */
+ case 0x320: /* CM_IDLEST_GFX */
+ case 0x3e4: /* PM_PWSTST_GFX */
+ case 0x420: /* CM_IDLEST_WKUP */
+ case 0x520: /* CM_IDLEST_CKGEN */
+ case 0x820: /* CM_IDLEST_DSP */
+ case 0x8e4: /* PM_PWSTST_DSP */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x010: /* PRCM_SYSCONFIG */
+ s->sysconfig = value & 1;
+ break;
+
+ case 0x018: /* PRCM_IRQSTATUS_MPU */
+ s->irqst[0] &= ~value;
+ omap_prcm_int_update(s, 0);
+ break;
+ case 0x01c: /* PRCM_IRQENABLE_MPU */
+ s->irqen[0] = value & 0x3f;
+ omap_prcm_int_update(s, 0);
+ break;
+
+ case 0x050: /* PRCM_VOLTCTRL */
+ s->voltctrl = value & 0xf1c3;
+ break;
+
+ case 0x060: /* PRCM_CLKSRC_CTRL */
+ s->clksrc[0] = value & 0xdb;
+ /* TODO update clocks */
+ break;
+
+ case 0x070: /* PRCM_CLKOUT_CTRL */
+ s->clkout[0] = value & 0xbbbb;
+ /* TODO update clocks */
+ break;
+
+ case 0x078: /* PRCM_CLKEMUL_CTRL */
+ s->clkemul[0] = value & 1;
+ /* TODO update clocks */
+ break;
+
+ case 0x080: /* PRCM_CLKCFG_CTRL */
+ break;
+
+ case 0x090: /* PRCM_VOLTSETUP */
+ s->setuptime[0] = value & 0xffff;
+ break;
+ case 0x094: /* PRCM_CLKSSETUP */
+ s->setuptime[1] = value & 0xffff;
+ break;
+
+ case 0x098: /* PRCM_POLCTRL */
+ s->clkpol[0] = value & 0x701;
+ break;
+
+ case 0x0b0: /* GENERAL_PURPOSE1 */
+ case 0x0b4: /* GENERAL_PURPOSE2 */
+ case 0x0b8: /* GENERAL_PURPOSE3 */
+ case 0x0bc: /* GENERAL_PURPOSE4 */
+ case 0x0c0: /* GENERAL_PURPOSE5 */
+ case 0x0c4: /* GENERAL_PURPOSE6 */
+ case 0x0c8: /* GENERAL_PURPOSE7 */
+ case 0x0cc: /* GENERAL_PURPOSE8 */
+ case 0x0d0: /* GENERAL_PURPOSE9 */
+ case 0x0d4: /* GENERAL_PURPOSE10 */
+ case 0x0d8: /* GENERAL_PURPOSE11 */
+ case 0x0dc: /* GENERAL_PURPOSE12 */
+ case 0x0e0: /* GENERAL_PURPOSE13 */
+ case 0x0e4: /* GENERAL_PURPOSE14 */
+ case 0x0e8: /* GENERAL_PURPOSE15 */
+ case 0x0ec: /* GENERAL_PURPOSE16 */
+ case 0x0f0: /* GENERAL_PURPOSE17 */
+ case 0x0f4: /* GENERAL_PURPOSE18 */
+ case 0x0f8: /* GENERAL_PURPOSE19 */
+ case 0x0fc: /* GENERAL_PURPOSE20 */
+ s->scratch[(addr - 0xb0) >> 2] = value;
+ break;
+
+ case 0x140: /* CM_CLKSEL_MPU */
+ s->clksel[0] = value & 0x1f;
+ /* TODO update clocks */
+ break;
+ case 0x148: /* CM_CLKSTCTRL_MPU */
+ s->clkctrl[0] = value & 0x1f;
+ break;
+
+ case 0x158: /* RM_RSTST_MPU */
+ s->rst[0] &= ~value;
+ break;
+ case 0x1c8: /* PM_WKDEP_MPU */
+ s->wkup[0] = value & 0x15;
+ break;
+
+ case 0x1d4: /* PM_EVGENCTRL_MPU */
+ s->ev = value & 0x1f;
+ break;
+ case 0x1d8: /* PM_EVEGENONTIM_MPU */
+ s->evtime[0] = value;
+ break;
+ case 0x1dc: /* PM_EVEGENOFFTIM_MPU */
+ s->evtime[1] = value;
+ break;
+
+ case 0x1e0: /* PM_PWSTCTRL_MPU */
+ s->power[0] = value & 0xc0f;
+ break;
+
+ case 0x200: /* CM_FCLKEN1_CORE */
+ s->clken[0] = value & 0xbfffffff;
+ /* TODO update clocks */
+ /* The EN_EAC bit only gets/puts func_96m_clk. */
+ break;
+ case 0x204: /* CM_FCLKEN2_CORE */
+ s->clken[1] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x210: /* CM_ICLKEN1_CORE */
+ s->clken[2] = value & 0xfffffff9;
+ /* TODO update clocks */
+ /* The EN_EAC bit only gets/puts core_l4_iclk. */
+ break;
+ case 0x214: /* CM_ICLKEN2_CORE */
+ s->clken[3] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x21c: /* CM_ICLKEN4_CORE */
+ s->clken[4] = value & 0x0000001f;
+ /* TODO update clocks */
+ break;
+
+ case 0x230: /* CM_AUTOIDLE1_CORE */
+ s->clkidle[0] = value & 0xfffffff9;
+ /* TODO update clocks */
+ break;
+ case 0x234: /* CM_AUTOIDLE2_CORE */
+ s->clkidle[1] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x238: /* CM_AUTOIDLE3_CORE */
+ s->clkidle[2] = value & 0x00000007;
+ /* TODO update clocks */
+ break;
+ case 0x23c: /* CM_AUTOIDLE4_CORE */
+ s->clkidle[3] = value & 0x0000001f;
+ /* TODO update clocks */
+ break;
+
+ case 0x240: /* CM_CLKSEL1_CORE */
+ s->clksel[1] = value & 0x0fffbf7f;
+ /* TODO update clocks */
+ break;
+
+ case 0x244: /* CM_CLKSEL2_CORE */
+ s->clksel[2] = value & 0x00fffffc;
+ /* TODO update clocks */
+ break;
+
+ case 0x248: /* CM_CLKSTCTRL_CORE */
+ s->clkctrl[1] = value & 0x7;
+ break;
+
+ case 0x2a0: /* PM_WKEN1_CORE */
+ s->wken[0] = value & 0x04667ff8;
+ break;
+ case 0x2a4: /* PM_WKEN2_CORE */
+ s->wken[1] = value & 0x00000005;
+ break;
+
+ case 0x2b0: /* PM_WKST1_CORE */
+ s->wkst[0] &= ~value;
+ break;
+ case 0x2b4: /* PM_WKST2_CORE */
+ s->wkst[1] &= ~value;
+ break;
+
+ case 0x2e0: /* PM_PWSTCTRL_CORE */
+ s->power[1] = (value & 0x00fc3f) | (1 << 2);
+ break;
+
+ case 0x300: /* CM_FCLKEN_GFX */
+ s->clken[5] = value & 6;
+ /* TODO update clocks */
+ break;
+ case 0x310: /* CM_ICLKEN_GFX */
+ s->clken[6] = value & 1;
+ /* TODO update clocks */
+ break;
+ case 0x340: /* CM_CLKSEL_GFX */
+ s->clksel[3] = value & 7;
+ /* TODO update clocks */
+ break;
+ case 0x348: /* CM_CLKSTCTRL_GFX */
+ s->clkctrl[2] = value & 1;
+ break;
+ case 0x350: /* RM_RSTCTRL_GFX */
+ s->rstctrl[0] = value & 1;
+ /* TODO: reset */
+ break;
+ case 0x358: /* RM_RSTST_GFX */
+ s->rst[1] &= ~value;
+ break;
+ case 0x3c8: /* PM_WKDEP_GFX */
+ s->wkup[1] = value & 0x13;
+ break;
+ case 0x3e0: /* PM_PWSTCTRL_GFX */
+ s->power[2] = (value & 0x00c0f) | (3 << 2);
+ break;
+
+ case 0x400: /* CM_FCLKEN_WKUP */
+ s->clken[7] = value & 0xd;
+ /* TODO update clocks */
+ break;
+ case 0x410: /* CM_ICLKEN_WKUP */
+ s->clken[8] = value & 0x3f;
+ /* TODO update clocks */
+ break;
+ case 0x430: /* CM_AUTOIDLE_WKUP */
+ s->clkidle[4] = value & 0x0000003f;
+ /* TODO update clocks */
+ break;
+ case 0x440: /* CM_CLKSEL_WKUP */
+ s->clksel[4] = value & 3;
+ /* TODO update clocks */
+ break;
+ case 0x450: /* RM_RSTCTRL_WKUP */
+ /* TODO: reset */
+ if (value & 2)
+ qemu_system_reset_request();
+ break;
+ case 0x454: /* RM_RSTTIME_WKUP */
+ s->rsttime_wkup = value & 0x1fff;
+ break;
+ case 0x458: /* RM_RSTST_WKUP */
+ s->rst[2] &= ~value;
+ break;
+ case 0x4a0: /* PM_WKEN_WKUP */
+ s->wken[2] = value & 0x00000005;
+ break;
+ case 0x4b0: /* PM_WKST_WKUP */
+ s->wkst[2] &= ~value;
+ break;
+
+ case 0x500: /* CM_CLKEN_PLL */
+ if (value & 0xffffff30)
+ fprintf(stderr, "%s: write 0s in CM_CLKEN_PLL for "
+ "future compatiblity\n", __FUNCTION__);
+ if ((s->clken[9] ^ value) & 0xcc) {
+ s->clken[9] &= ~0xcc;
+ s->clken[9] |= value & 0xcc;
+ omap_prcm_apll_update(s);
+ }
+ if ((s->clken[9] ^ value) & 3) {
+ s->clken[9] &= ~3;
+ s->clken[9] |= value & 3;
+ omap_prcm_dpll_update(s);
+ }
+ break;
+ case 0x530: /* CM_AUTOIDLE_PLL */
+ s->clkidle[5] = value & 0x000000cf;
+ /* TODO update clocks */
+ break;
+ case 0x540: /* CM_CLKSEL1_PLL */
+ if (value & 0xfc4000d7)
+ fprintf(stderr, "%s: write 0s in CM_CLKSEL1_PLL for "
+ "future compatiblity\n", __FUNCTION__);
+ if ((s->clksel[5] ^ value) & 0x003fff00) {
+ s->clksel[5] = value & 0x03bfff28;
+ omap_prcm_dpll_update(s);
+ }
+ /* TODO update the other clocks */
+
+ s->clksel[5] = value & 0x03bfff28;
+ break;
+ case 0x544: /* CM_CLKSEL2_PLL */
+ if (value & ~3)
+ fprintf(stderr, "%s: write 0s in CM_CLKSEL2_PLL[31:2] for "
+ "future compatiblity\n", __FUNCTION__);
+ if (s->clksel[6] != (value & 3)) {
+ s->clksel[6] = value & 3;
+ omap_prcm_dpll_update(s);
+ }
+ break;
+
+ case 0x800: /* CM_FCLKEN_DSP */
+ s->clken[10] = value & 0x501;
+ /* TODO update clocks */
+ break;
+ case 0x810: /* CM_ICLKEN_DSP */
+ s->clken[11] = value & 0x2;
+ /* TODO update clocks */
+ break;
+ case 0x830: /* CM_AUTOIDLE_DSP */
+ s->clkidle[6] = value & 0x2;
+ /* TODO update clocks */
+ break;
+ case 0x840: /* CM_CLKSEL_DSP */
+ s->clksel[7] = value & 0x3fff;
+ /* TODO update clocks */
+ break;
+ case 0x848: /* CM_CLKSTCTRL_DSP */
+ s->clkctrl[3] = value & 0x101;
+ break;
+ case 0x850: /* RM_RSTCTRL_DSP */
+ /* TODO: reset */
+ break;
+ case 0x858: /* RM_RSTST_DSP */
+ s->rst[3] &= ~value;
+ break;
+ case 0x8c8: /* PM_WKDEP_DSP */
+ s->wkup[2] = value & 0x13;
+ break;
+ case 0x8e0: /* PM_PWSTCTRL_DSP */
+ s->power[3] = (value & 0x03017) | (3 << 2);
+ break;
+
+ case 0x8f0: /* PRCM_IRQSTATUS_DSP */
+ s->irqst[1] &= ~value;
+ omap_prcm_int_update(s, 1);
+ break;
+ case 0x8f4: /* PRCM_IRQENABLE_DSP */
+ s->irqen[1] = value & 0x7;
+ omap_prcm_int_update(s, 1);
+ break;
+
+ case 0x8f8: /* PRCM_IRQSTATUS_IVA */
+ s->irqst[2] &= ~value;
+ omap_prcm_int_update(s, 2);
+ break;
+ case 0x8fc: /* PRCM_IRQENABLE_IVA */
+ s->irqen[2] = value & 0x7;
+ omap_prcm_int_update(s, 2);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_prcm_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_prcm_read,
+};
+
+static CPUWriteMemoryFunc * const omap_prcm_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_prcm_write,
+};
+
+static void omap_prcm_reset(struct omap_prcm_s *s)
+{
+ s->sysconfig = 0;
+ s->irqst[0] = 0;
+ s->irqst[1] = 0;
+ s->irqst[2] = 0;
+ s->irqen[0] = 0;
+ s->irqen[1] = 0;
+ s->irqen[2] = 0;
+ s->voltctrl = 0x1040;
+ s->ev = 0x14;
+ s->evtime[0] = 0;
+ s->evtime[1] = 0;
+ s->clkctrl[0] = 0;
+ s->clkctrl[1] = 0;
+ s->clkctrl[2] = 0;
+ s->clkctrl[3] = 0;
+ s->clken[1] = 7;
+ s->clken[3] = 7;
+ s->clken[4] = 0;
+ s->clken[5] = 0;
+ s->clken[6] = 0;
+ s->clken[7] = 0xc;
+ s->clken[8] = 0x3e;
+ s->clken[9] = 0x0d;
+ s->clken[10] = 0;
+ s->clken[11] = 0;
+ s->clkidle[0] = 0;
+ s->clkidle[2] = 7;
+ s->clkidle[3] = 0;
+ s->clkidle[4] = 0;
+ s->clkidle[5] = 0x0c;
+ s->clkidle[6] = 0;
+ s->clksel[0] = 0x01;
+ s->clksel[1] = 0x02100121;
+ s->clksel[2] = 0x00000000;
+ s->clksel[3] = 0x01;
+ s->clksel[4] = 0;
+ s->clksel[7] = 0x0121;
+ s->wkup[0] = 0x15;
+ s->wkup[1] = 0x13;
+ s->wkup[2] = 0x13;
+ s->wken[0] = 0x04667ff8;
+ s->wken[1] = 0x00000005;
+ s->wken[2] = 5;
+ s->wkst[0] = 0;
+ s->wkst[1] = 0;
+ s->wkst[2] = 0;
+ s->power[0] = 0x00c;
+ s->power[1] = 4;
+ s->power[2] = 0x0000c;
+ s->power[3] = 0x14;
+ s->rstctrl[0] = 1;
+ s->rst[3] = 1;
+ omap_prcm_apll_update(s);
+ omap_prcm_dpll_update(s);
+}
+
+static void omap_prcm_coldreset(struct omap_prcm_s *s)
+{
+ s->setuptime[0] = 0;
+ s->setuptime[1] = 0;
+ memset(&s->scratch, 0, sizeof(s->scratch));
+ s->rst[0] = 0x01;
+ s->rst[1] = 0x00;
+ s->rst[2] = 0x01;
+ s->clken[0] = 0;
+ s->clken[2] = 0;
+ s->clkidle[1] = 0;
+ s->clksel[5] = 0;
+ s->clksel[6] = 2;
+ s->clksrc[0] = 0x43;
+ s->clkout[0] = 0x0303;
+ s->clkemul[0] = 0;
+ s->clkpol[0] = 0x100;
+ s->rsttime_wkup = 0x1002;
+
+ omap_prcm_reset(s);
+}
+
+static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta,
+ qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int,
+ struct omap_mpu_state_s *mpu)
+{
+ int iomemtype;
+ struct omap_prcm_s *s = (struct omap_prcm_s *)
+ qemu_mallocz(sizeof(struct omap_prcm_s));
+
+ s->irq[0] = mpu_int;
+ s->irq[1] = dsp_int;
+ s->irq[2] = iva_int;
+ s->mpu = mpu;
+ omap_prcm_coldreset(s);
+
+ iomemtype = l4_register_io_memory(omap_prcm_readfn,
+ omap_prcm_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+ omap_l4_attach(ta, 1, iomemtype);
+
+ return s;
+}
+
+/* System and Pinout control */
+struct omap_sysctl_s {
+ struct omap_mpu_state_s *mpu;
+
+ uint32_t sysconfig;
+ uint32_t devconfig;
+ uint32_t psaconfig;
+ uint32_t padconf[0x45];
+ uint8_t obs;
+ uint32_t msuspendmux[5];
+};
+
+static uint32_t omap_sysctl_read8(void *opaque, target_phys_addr_t addr)
+{
+
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+ int pad_offset, byte_offset;
+ int value;
+
+ switch (addr) {
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ pad_offset = (addr - 0x30) >> 2;
+ byte_offset = (addr - 0x30) & (4 - 1);
+
+ value = s->padconf[pad_offset];
+ value = (value >> (byte_offset * 8)) & 0xff;
+
+ return value;
+
+ default:
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static uint32_t omap_sysctl_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+
+ switch (addr) {
+ case 0x000: /* CONTROL_REVISION */
+ return 0x20;
+
+ case 0x010: /* CONTROL_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ return s->padconf[(addr - 0x30) >> 2];
+
+ case 0x270: /* CONTROL_DEBOBS */
+ return s->obs;
+
+ case 0x274: /* CONTROL_DEVCONF */
+ return s->devconfig;
+
+ case 0x28c: /* CONTROL_EMU_SUPPORT */
+ return 0;
+
+ case 0x290: /* CONTROL_MSUSPENDMUX_0 */
+ return s->msuspendmux[0];
+ case 0x294: /* CONTROL_MSUSPENDMUX_1 */
+ return s->msuspendmux[1];
+ case 0x298: /* CONTROL_MSUSPENDMUX_2 */
+ return s->msuspendmux[2];
+ case 0x29c: /* CONTROL_MSUSPENDMUX_3 */
+ return s->msuspendmux[3];
+ case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */
+ return s->msuspendmux[4];
+ case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */
+ return 0;
+
+ case 0x2b8: /* CONTROL_PSA_CTRL */
+ return s->psaconfig;
+ case 0x2bc: /* CONTROL_PSA_CMD */
+ case 0x2c0: /* CONTROL_PSA_VALUE */
+ return 0;
+
+ case 0x2b0: /* CONTROL_SEC_CTRL */
+ return 0x800000f1;
+ case 0x2d0: /* CONTROL_SEC_EMU */
+ return 0x80000015;
+ case 0x2d4: /* CONTROL_SEC_TAP */
+ return 0x8000007f;
+ case 0x2b4: /* CONTROL_SEC_TEST */
+ case 0x2f0: /* CONTROL_SEC_STATUS */
+ case 0x2f4: /* CONTROL_SEC_ERR_STATUS */
+ /* Secure mode is not present on general-pusrpose device. Outside
+ * secure mode these values cannot be read or written. */
+ return 0;
+
+ case 0x2d8: /* CONTROL_OCM_RAM_PERM */
+ return 0xff;
+ case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */
+ case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */
+ case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */
+ /* No secure mode so no Extended Secure RAM present. */
+ return 0;
+
+ case 0x2f8: /* CONTROL_STATUS */
+ /* Device Type => General-purpose */
+ return 0x0300;
+ case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */
+
+ case 0x300: /* CONTROL_RPUB_KEY_H_0 */
+ case 0x304: /* CONTROL_RPUB_KEY_H_1 */
+ case 0x308: /* CONTROL_RPUB_KEY_H_2 */
+ case 0x30c: /* CONTROL_RPUB_KEY_H_3 */
+ return 0xdecafbad;
+
+ case 0x310: /* CONTROL_RAND_KEY_0 */
+ case 0x314: /* CONTROL_RAND_KEY_1 */
+ case 0x318: /* CONTROL_RAND_KEY_2 */
+ case 0x31c: /* CONTROL_RAND_KEY_3 */
+ case 0x320: /* CONTROL_CUST_KEY_0 */
+ case 0x324: /* CONTROL_CUST_KEY_1 */
+ case 0x330: /* CONTROL_TEST_KEY_0 */
+ case 0x334: /* CONTROL_TEST_KEY_1 */
+ case 0x338: /* CONTROL_TEST_KEY_2 */
+ case 0x33c: /* CONTROL_TEST_KEY_3 */
+ case 0x340: /* CONTROL_TEST_KEY_4 */
+ case 0x344: /* CONTROL_TEST_KEY_5 */
+ case 0x348: /* CONTROL_TEST_KEY_6 */
+ case 0x34c: /* CONTROL_TEST_KEY_7 */
+ case 0x350: /* CONTROL_TEST_KEY_8 */
+ case 0x354: /* CONTROL_TEST_KEY_9 */
+ /* Can only be accessed in secure mode and when C_FieldAccEnable
+ * bit is set in CONTROL_SEC_CTRL.
+ * TODO: otherwise an interconnect access error is generated. */
+ return 0;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sysctl_write8(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+ int pad_offset, byte_offset;
+ int prev_value;
+
+ switch (addr) {
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ pad_offset = (addr - 0x30) >> 2;
+ byte_offset = (addr - 0x30) & (4 - 1);
+
+ prev_value = s->padconf[pad_offset];
+ prev_value &= ~(0xff << (byte_offset * 8));
+ prev_value |= ((value & 0x1f1f1f1f) << (byte_offset * 8)) & 0x1f1f1f1f;
+ s->padconf[pad_offset] = prev_value;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ break;
+ }
+}
+
+static void omap_sysctl_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque;
+
+ switch (addr) {
+ case 0x000: /* CONTROL_REVISION */
+ case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */
+ case 0x2c0: /* CONTROL_PSA_VALUE */
+ case 0x2f8: /* CONTROL_STATUS */
+ case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */
+ case 0x300: /* CONTROL_RPUB_KEY_H_0 */
+ case 0x304: /* CONTROL_RPUB_KEY_H_1 */
+ case 0x308: /* CONTROL_RPUB_KEY_H_2 */
+ case 0x30c: /* CONTROL_RPUB_KEY_H_3 */
+ case 0x310: /* CONTROL_RAND_KEY_0 */
+ case 0x314: /* CONTROL_RAND_KEY_1 */
+ case 0x318: /* CONTROL_RAND_KEY_2 */
+ case 0x31c: /* CONTROL_RAND_KEY_3 */
+ case 0x320: /* CONTROL_CUST_KEY_0 */
+ case 0x324: /* CONTROL_CUST_KEY_1 */
+ case 0x330: /* CONTROL_TEST_KEY_0 */
+ case 0x334: /* CONTROL_TEST_KEY_1 */
+ case 0x338: /* CONTROL_TEST_KEY_2 */
+ case 0x33c: /* CONTROL_TEST_KEY_3 */
+ case 0x340: /* CONTROL_TEST_KEY_4 */
+ case 0x344: /* CONTROL_TEST_KEY_5 */
+ case 0x348: /* CONTROL_TEST_KEY_6 */
+ case 0x34c: /* CONTROL_TEST_KEY_7 */
+ case 0x350: /* CONTROL_TEST_KEY_8 */
+ case 0x354: /* CONTROL_TEST_KEY_9 */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x010: /* CONTROL_SYSCONFIG */
+ s->sysconfig = value & 0x1e;
+ break;
+
+ case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */
+ /* XXX: should check constant bits */
+ s->padconf[(addr - 0x30) >> 2] = value & 0x1f1f1f1f;
+ break;
+
+ case 0x270: /* CONTROL_DEBOBS */
+ s->obs = value & 0xff;
+ break;
+
+ case 0x274: /* CONTROL_DEVCONF */
+ s->devconfig = value & 0xffffc7ff;
+ break;
+
+ case 0x28c: /* CONTROL_EMU_SUPPORT */
+ break;
+
+ case 0x290: /* CONTROL_MSUSPENDMUX_0 */
+ s->msuspendmux[0] = value & 0x3fffffff;
+ break;
+ case 0x294: /* CONTROL_MSUSPENDMUX_1 */
+ s->msuspendmux[1] = value & 0x3fffffff;
+ break;
+ case 0x298: /* CONTROL_MSUSPENDMUX_2 */
+ s->msuspendmux[2] = value & 0x3fffffff;
+ break;
+ case 0x29c: /* CONTROL_MSUSPENDMUX_3 */
+ s->msuspendmux[3] = value & 0x3fffffff;
+ break;
+ case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */
+ s->msuspendmux[4] = value & 0x3fffffff;
+ break;
+
+ case 0x2b8: /* CONTROL_PSA_CTRL */
+ s->psaconfig = value & 0x1c;
+ s->psaconfig |= (value & 0x20) ? 2 : 1;
+ break;
+ case 0x2bc: /* CONTROL_PSA_CMD */
+ break;
+
+ case 0x2b0: /* CONTROL_SEC_CTRL */
+ case 0x2b4: /* CONTROL_SEC_TEST */
+ case 0x2d0: /* CONTROL_SEC_EMU */
+ case 0x2d4: /* CONTROL_SEC_TAP */
+ case 0x2d8: /* CONTROL_OCM_RAM_PERM */
+ case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */
+ case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */
+ case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */
+ case 0x2f0: /* CONTROL_SEC_STATUS */
+ case 0x2f4: /* CONTROL_SEC_ERR_STATUS */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_sysctl_readfn[] = {
+ omap_sysctl_read8,
+ omap_badwidth_read32, /* TODO */
+ omap_sysctl_read,
+};
+
+static CPUWriteMemoryFunc * const omap_sysctl_writefn[] = {
+ omap_sysctl_write8,
+ omap_badwidth_write32, /* TODO */
+ omap_sysctl_write,
+};
+
+static void omap_sysctl_reset(struct omap_sysctl_s *s)
+{
+ /* (power-on reset) */
+ s->sysconfig = 0;
+ s->obs = 0;
+ s->devconfig = 0x0c000000;
+ s->msuspendmux[0] = 0x00000000;
+ s->msuspendmux[1] = 0x00000000;
+ s->msuspendmux[2] = 0x00000000;
+ s->msuspendmux[3] = 0x00000000;
+ s->msuspendmux[4] = 0x00000000;
+ s->psaconfig = 1;
+
+ s->padconf[0x00] = 0x000f0f0f;
+ s->padconf[0x01] = 0x00000000;
+ s->padconf[0x02] = 0x00000000;
+ s->padconf[0x03] = 0x00000000;
+ s->padconf[0x04] = 0x00000000;
+ s->padconf[0x05] = 0x00000000;
+ s->padconf[0x06] = 0x00000000;
+ s->padconf[0x07] = 0x00000000;
+ s->padconf[0x08] = 0x08080800;
+ s->padconf[0x09] = 0x08080808;
+ s->padconf[0x0a] = 0x08080808;
+ s->padconf[0x0b] = 0x08080808;
+ s->padconf[0x0c] = 0x08080808;
+ s->padconf[0x0d] = 0x08080800;
+ s->padconf[0x0e] = 0x08080808;
+ s->padconf[0x0f] = 0x08080808;
+ s->padconf[0x10] = 0x18181808; /* | 0x07070700 if SBoot3 */
+ s->padconf[0x11] = 0x18181818; /* | 0x07070707 if SBoot3 */
+ s->padconf[0x12] = 0x18181818; /* | 0x07070707 if SBoot3 */
+ s->padconf[0x13] = 0x18181818; /* | 0x07070707 if SBoot3 */
+ s->padconf[0x14] = 0x18181818; /* | 0x00070707 if SBoot3 */
+ s->padconf[0x15] = 0x18181818;
+ s->padconf[0x16] = 0x18181818; /* | 0x07000000 if SBoot3 */
+ s->padconf[0x17] = 0x1f001f00;
+ s->padconf[0x18] = 0x1f1f1f1f;
+ s->padconf[0x19] = 0x00000000;
+ s->padconf[0x1a] = 0x1f180000;
+ s->padconf[0x1b] = 0x00001f1f;
+ s->padconf[0x1c] = 0x1f001f00;
+ s->padconf[0x1d] = 0x00000000;
+ s->padconf[0x1e] = 0x00000000;
+ s->padconf[0x1f] = 0x08000000;
+ s->padconf[0x20] = 0x08080808;
+ s->padconf[0x21] = 0x08080808;
+ s->padconf[0x22] = 0x0f080808;
+ s->padconf[0x23] = 0x0f0f0f0f;
+ s->padconf[0x24] = 0x000f0f0f;
+ s->padconf[0x25] = 0x1f1f1f0f;
+ s->padconf[0x26] = 0x080f0f1f;
+ s->padconf[0x27] = 0x070f1808;
+ s->padconf[0x28] = 0x0f070707;
+ s->padconf[0x29] = 0x000f0f1f;
+ s->padconf[0x2a] = 0x0f0f0f1f;
+ s->padconf[0x2b] = 0x08000000;
+ s->padconf[0x2c] = 0x0000001f;
+ s->padconf[0x2d] = 0x0f0f1f00;
+ s->padconf[0x2e] = 0x1f1f0f0f;
+ s->padconf[0x2f] = 0x0f1f1f1f;
+ s->padconf[0x30] = 0x0f0f0f0f;
+ s->padconf[0x31] = 0x0f1f0f1f;
+ s->padconf[0x32] = 0x0f0f0f0f;
+ s->padconf[0x33] = 0x0f1f0f1f;
+ s->padconf[0x34] = 0x1f1f0f0f;
+ s->padconf[0x35] = 0x0f0f1f1f;
+ s->padconf[0x36] = 0x0f0f1f0f;
+ s->padconf[0x37] = 0x0f0f0f0f;
+ s->padconf[0x38] = 0x1f18180f;
+ s->padconf[0x39] = 0x1f1f1f1f;
+ s->padconf[0x3a] = 0x00001f1f;
+ s->padconf[0x3b] = 0x00000000;
+ s->padconf[0x3c] = 0x00000000;
+ s->padconf[0x3d] = 0x0f0f0f0f;
+ s->padconf[0x3e] = 0x18000f0f;
+ s->padconf[0x3f] = 0x00070000;
+ s->padconf[0x40] = 0x00000707;
+ s->padconf[0x41] = 0x0f1f0700;
+ s->padconf[0x42] = 0x1f1f070f;
+ s->padconf[0x43] = 0x0008081f;
+ s->padconf[0x44] = 0x00000800;
+}
+
+static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta,
+ omap_clk iclk, struct omap_mpu_state_s *mpu)
+{
+ int iomemtype;
+ struct omap_sysctl_s *s = (struct omap_sysctl_s *)
+ qemu_mallocz(sizeof(struct omap_sysctl_s));
+
+ s->mpu = mpu;
+ omap_sysctl_reset(s);
+
+ iomemtype = l4_register_io_memory(omap_sysctl_readfn,
+ omap_sysctl_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ return s;
+}
+
+/* General chip reset */
+static void omap2_mpu_reset(void *opaque)
+{
+ struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque;
+
+ omap_inth_reset(mpu->ih[0]);
+ omap_dma_reset(mpu->dma);
+ omap_prcm_reset(mpu->prcm);
+ omap_sysctl_reset(mpu->sysc);
+ omap_gp_timer_reset(mpu->gptimer[0]);
+ omap_gp_timer_reset(mpu->gptimer[1]);
+ omap_gp_timer_reset(mpu->gptimer[2]);
+ omap_gp_timer_reset(mpu->gptimer[3]);
+ omap_gp_timer_reset(mpu->gptimer[4]);
+ omap_gp_timer_reset(mpu->gptimer[5]);
+ omap_gp_timer_reset(mpu->gptimer[6]);
+ omap_gp_timer_reset(mpu->gptimer[7]);
+ omap_gp_timer_reset(mpu->gptimer[8]);
+ omap_gp_timer_reset(mpu->gptimer[9]);
+ omap_gp_timer_reset(mpu->gptimer[10]);
+ omap_gp_timer_reset(mpu->gptimer[11]);
+ omap_synctimer_reset(mpu->synctimer);
+ omap_sdrc_reset(mpu->sdrc);
+ omap_gpmc_reset(mpu->gpmc);
+ omap_dss_reset(mpu->dss);
+ omap_uart_reset(mpu->uart[0]);
+ omap_uart_reset(mpu->uart[1]);
+ omap_uart_reset(mpu->uart[2]);
+ omap_mmc_reset(mpu->mmc);
+ omap_gpif_reset(mpu->gpif);
+ omap_mcspi_reset(mpu->mcspi[0]);
+ omap_mcspi_reset(mpu->mcspi[1]);
+ omap_i2c_reset(mpu->i2c[0]);
+ omap_i2c_reset(mpu->i2c[1]);
+ cpu_reset(mpu->env);
+}
+
+static int omap2_validate_addr(struct omap_mpu_state_s *s,
+ target_phys_addr_t addr)
+{
+ return 1;
+}
+
+static const struct dma_irq_map omap2_dma_irq_map[] = {
+ { 0, OMAP_INT_24XX_SDMA_IRQ0 },
+ { 0, OMAP_INT_24XX_SDMA_IRQ1 },
+ { 0, OMAP_INT_24XX_SDMA_IRQ2 },
+ { 0, OMAP_INT_24XX_SDMA_IRQ3 },
+};
+
+struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size,
+ const char *core)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *)
+ qemu_mallocz(sizeof(struct omap_mpu_state_s));
+ ram_addr_t sram_base, q2_base;
+ qemu_irq *cpu_irq;
+ qemu_irq dma_irqs[4];
+ omap_clk gpio_clks[4];
+ DriveInfo *dinfo;
+ int i;
+
+ /* Core */
+ s->mpu_model = omap2420;
+ s->env = cpu_init(core ?: "arm1136-r2");
+ if (!s->env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->sdram_size = sdram_size;
+ s->sram_size = OMAP242X_SRAM_SIZE;
+
+ s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0];
+
+ /* Clocks */
+ omap_clk_init(s);
+
+ /* Memory-mapped stuff */
+ cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size,
+ (q2_base = qemu_ram_alloc(NULL, "omap2.dram",
+ s->sdram_size)) | IO_MEM_RAM);
+ cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size,
+ (sram_base = qemu_ram_alloc(NULL, "omap2.sram",
+ s->sram_size)) | IO_MEM_RAM);
+
+ s->l4 = omap_l4_init(OMAP2_L4_BASE, 54);
+
+ /* Actually mapped at any 2K boundary in the ARM11 private-peripheral if */
+ cpu_irq = arm_pic_init_cpu(s->env);
+ s->ih[0] = omap2_inth_init(0x480fe000, 0x1000, 3, &s->irq[0],
+ cpu_irq[ARM_PIC_CPU_IRQ], cpu_irq[ARM_PIC_CPU_FIQ],
+ omap_findclk(s, "mpu_intc_fclk"),
+ omap_findclk(s, "mpu_intc_iclk"));
+
+ s->prcm = omap_prcm_init(omap_l4tao(s->l4, 3),
+ s->irq[0][OMAP_INT_24XX_PRCM_MPU_IRQ], NULL, NULL, s);
+
+ s->sysc = omap_sysctl_init(omap_l4tao(s->l4, 1),
+ omap_findclk(s, "omapctrl_iclk"), s);
+
+ for (i = 0; i < 4; i ++)
+ dma_irqs[i] =
+ s->irq[omap2_dma_irq_map[i].ih][omap2_dma_irq_map[i].intr];
+ s->dma = omap_dma4_init(0x48056000, dma_irqs, s, 256, 32,
+ omap_findclk(s, "sdma_iclk"),
+ omap_findclk(s, "sdma_fclk"));
+ s->port->addr_valid = omap2_validate_addr;
+
+ /* Register SDRAM and SRAM ports for fast DMA transfers. */
+ soc_dma_port_add_mem_ram(s->dma, q2_base, OMAP2_Q2_BASE, s->sdram_size);
+ soc_dma_port_add_mem_ram(s->dma, sram_base, OMAP2_SRAM_BASE, s->sram_size);
+
+ s->uart[0] = omap2_uart_init(omap_l4ta(s->l4, 19),
+ s->irq[0][OMAP_INT_24XX_UART1_IRQ],
+ omap_findclk(s, "uart1_fclk"),
+ omap_findclk(s, "uart1_iclk"),
+ s->drq[OMAP24XX_DMA_UART1_TX],
+ s->drq[OMAP24XX_DMA_UART1_RX],
+ "uart1",
+ serial_hds[0]);
+ s->uart[1] = omap2_uart_init(omap_l4ta(s->l4, 20),
+ s->irq[0][OMAP_INT_24XX_UART2_IRQ],
+ omap_findclk(s, "uart2_fclk"),
+ omap_findclk(s, "uart2_iclk"),
+ s->drq[OMAP24XX_DMA_UART2_TX],
+ s->drq[OMAP24XX_DMA_UART2_RX],
+ "uart2",
+ serial_hds[0] ? serial_hds[1] : NULL);
+ s->uart[2] = omap2_uart_init(omap_l4ta(s->l4, 21),
+ s->irq[0][OMAP_INT_24XX_UART3_IRQ],
+ omap_findclk(s, "uart3_fclk"),
+ omap_findclk(s, "uart3_iclk"),
+ s->drq[OMAP24XX_DMA_UART3_TX],
+ s->drq[OMAP24XX_DMA_UART3_RX],
+ "uart3",
+ serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL);
+
+ s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7),
+ s->irq[0][OMAP_INT_24XX_GPTIMER1],
+ omap_findclk(s, "wu_gpt1_clk"),
+ omap_findclk(s, "wu_l4_iclk"));
+ s->gptimer[1] = omap_gp_timer_init(omap_l4ta(s->l4, 8),
+ s->irq[0][OMAP_INT_24XX_GPTIMER2],
+ omap_findclk(s, "core_gpt2_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[2] = omap_gp_timer_init(omap_l4ta(s->l4, 22),
+ s->irq[0][OMAP_INT_24XX_GPTIMER3],
+ omap_findclk(s, "core_gpt3_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[3] = omap_gp_timer_init(omap_l4ta(s->l4, 23),
+ s->irq[0][OMAP_INT_24XX_GPTIMER4],
+ omap_findclk(s, "core_gpt4_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[4] = omap_gp_timer_init(omap_l4ta(s->l4, 24),
+ s->irq[0][OMAP_INT_24XX_GPTIMER5],
+ omap_findclk(s, "core_gpt5_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[5] = omap_gp_timer_init(omap_l4ta(s->l4, 25),
+ s->irq[0][OMAP_INT_24XX_GPTIMER6],
+ omap_findclk(s, "core_gpt6_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[6] = omap_gp_timer_init(omap_l4ta(s->l4, 26),
+ s->irq[0][OMAP_INT_24XX_GPTIMER7],
+ omap_findclk(s, "core_gpt7_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[7] = omap_gp_timer_init(omap_l4ta(s->l4, 27),
+ s->irq[0][OMAP_INT_24XX_GPTIMER8],
+ omap_findclk(s, "core_gpt8_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[8] = omap_gp_timer_init(omap_l4ta(s->l4, 28),
+ s->irq[0][OMAP_INT_24XX_GPTIMER9],
+ omap_findclk(s, "core_gpt9_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[9] = omap_gp_timer_init(omap_l4ta(s->l4, 29),
+ s->irq[0][OMAP_INT_24XX_GPTIMER10],
+ omap_findclk(s, "core_gpt10_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[10] = omap_gp_timer_init(omap_l4ta(s->l4, 30),
+ s->irq[0][OMAP_INT_24XX_GPTIMER11],
+ omap_findclk(s, "core_gpt11_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+ s->gptimer[11] = omap_gp_timer_init(omap_l4ta(s->l4, 31),
+ s->irq[0][OMAP_INT_24XX_GPTIMER12],
+ omap_findclk(s, "core_gpt12_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+
+ omap_tap_init(omap_l4ta(s->l4, 2), s);
+
+ s->synctimer = omap_synctimer_init(omap_l4tao(s->l4, 2), s,
+ omap_findclk(s, "clk32-kHz"),
+ omap_findclk(s, "core_l4_iclk"));
+
+ s->i2c[0] = omap2_i2c_init(omap_l4tao(s->l4, 5),
+ s->irq[0][OMAP_INT_24XX_I2C1_IRQ],
+ &s->drq[OMAP24XX_DMA_I2C1_TX],
+ omap_findclk(s, "i2c1.fclk"),
+ omap_findclk(s, "i2c1.iclk"));
+ s->i2c[1] = omap2_i2c_init(omap_l4tao(s->l4, 6),
+ s->irq[0][OMAP_INT_24XX_I2C2_IRQ],
+ &s->drq[OMAP24XX_DMA_I2C2_TX],
+ omap_findclk(s, "i2c2.fclk"),
+ omap_findclk(s, "i2c2.iclk"));
+
+ gpio_clks[0] = omap_findclk(s, "gpio1_dbclk");
+ gpio_clks[1] = omap_findclk(s, "gpio2_dbclk");
+ gpio_clks[2] = omap_findclk(s, "gpio3_dbclk");
+ gpio_clks[3] = omap_findclk(s, "gpio4_dbclk");
+ s->gpif = omap2_gpio_init(omap_l4ta(s->l4, 3),
+ &s->irq[0][OMAP_INT_24XX_GPIO_BANK1],
+ gpio_clks, omap_findclk(s, "gpio_iclk"), 4);
+
+ s->sdrc = omap_sdrc_init(0x68009000);
+ s->gpmc = omap_gpmc_init(0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ]);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), dinfo->bdrv,
+ s->irq[0][OMAP_INT_24XX_MMC_IRQ],
+ &s->drq[OMAP24XX_DMA_MMC1_TX],
+ omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk"));
+
+ s->mcspi[0] = omap_mcspi_init(omap_l4ta(s->l4, 35), 4,
+ s->irq[0][OMAP_INT_24XX_MCSPI1_IRQ],
+ &s->drq[OMAP24XX_DMA_SPI1_TX0],
+ omap_findclk(s, "spi1_fclk"),
+ omap_findclk(s, "spi1_iclk"));
+ s->mcspi[1] = omap_mcspi_init(omap_l4ta(s->l4, 36), 2,
+ s->irq[0][OMAP_INT_24XX_MCSPI2_IRQ],
+ &s->drq[OMAP24XX_DMA_SPI2_TX0],
+ omap_findclk(s, "spi2_fclk"),
+ omap_findclk(s, "spi2_iclk"));
+
+ s->dss = omap_dss_init(omap_l4ta(s->l4, 10), 0x68000800,
+ /* XXX wire M_IRQ_25, D_L2_IRQ_30 and I_IRQ_13 together */
+ s->irq[0][OMAP_INT_24XX_DSS_IRQ], s->drq[OMAP24XX_DMA_DSS],
+ omap_findclk(s, "dss_clk1"), omap_findclk(s, "dss_clk2"),
+ omap_findclk(s, "dss_54m_clk"),
+ omap_findclk(s, "dss_l3_iclk"),
+ omap_findclk(s, "dss_l4_iclk"));
+
+ omap_sti_init(omap_l4ta(s->l4, 18), 0x54000000,
+ s->irq[0][OMAP_INT_24XX_STI], omap_findclk(s, "emul_ck"),
+ serial_hds[0] && serial_hds[1] && serial_hds[2] ?
+ serial_hds[3] : NULL);
+
+ s->eac = omap_eac_init(omap_l4ta(s->l4, 32),
+ s->irq[0][OMAP_INT_24XX_EAC_IRQ],
+ /* Ten consecutive lines */
+ &s->drq[OMAP24XX_DMA_EAC_AC_RD],
+ omap_findclk(s, "func_96m_clk"),
+ omap_findclk(s, "core_l4_iclk"));
+
+ /* All register mappings (includin those not currenlty implemented):
+ * SystemControlMod 48000000 - 48000fff
+ * SystemControlL4 48001000 - 48001fff
+ * 32kHz Timer Mod 48004000 - 48004fff
+ * 32kHz Timer L4 48005000 - 48005fff
+ * PRCM ModA 48008000 - 480087ff
+ * PRCM ModB 48008800 - 48008fff
+ * PRCM L4 48009000 - 48009fff
+ * TEST-BCM Mod 48012000 - 48012fff
+ * TEST-BCM L4 48013000 - 48013fff
+ * TEST-TAP Mod 48014000 - 48014fff
+ * TEST-TAP L4 48015000 - 48015fff
+ * GPIO1 Mod 48018000 - 48018fff
+ * GPIO Top 48019000 - 48019fff
+ * GPIO2 Mod 4801a000 - 4801afff
+ * GPIO L4 4801b000 - 4801bfff
+ * GPIO3 Mod 4801c000 - 4801cfff
+ * GPIO4 Mod 4801e000 - 4801efff
+ * WDTIMER1 Mod 48020000 - 48010fff
+ * WDTIMER Top 48021000 - 48011fff
+ * WDTIMER2 Mod 48022000 - 48012fff
+ * WDTIMER L4 48023000 - 48013fff
+ * WDTIMER3 Mod 48024000 - 48014fff
+ * WDTIMER3 L4 48025000 - 48015fff
+ * WDTIMER4 Mod 48026000 - 48016fff
+ * WDTIMER4 L4 48027000 - 48017fff
+ * GPTIMER1 Mod 48028000 - 48018fff
+ * GPTIMER1 L4 48029000 - 48019fff
+ * GPTIMER2 Mod 4802a000 - 4801afff
+ * GPTIMER2 L4 4802b000 - 4801bfff
+ * L4-Config AP 48040000 - 480407ff
+ * L4-Config IP 48040800 - 48040fff
+ * L4-Config LA 48041000 - 48041fff
+ * ARM11ETB Mod 48048000 - 48049fff
+ * ARM11ETB L4 4804a000 - 4804afff
+ * DISPLAY Top 48050000 - 480503ff
+ * DISPLAY DISPC 48050400 - 480507ff
+ * DISPLAY RFBI 48050800 - 48050bff
+ * DISPLAY VENC 48050c00 - 48050fff
+ * DISPLAY L4 48051000 - 48051fff
+ * CAMERA Top 48052000 - 480523ff
+ * CAMERA core 48052400 - 480527ff
+ * CAMERA DMA 48052800 - 48052bff
+ * CAMERA MMU 48052c00 - 48052fff
+ * CAMERA L4 48053000 - 48053fff
+ * SDMA Mod 48056000 - 48056fff
+ * SDMA L4 48057000 - 48057fff
+ * SSI Top 48058000 - 48058fff
+ * SSI GDD 48059000 - 48059fff
+ * SSI Port1 4805a000 - 4805afff
+ * SSI Port2 4805b000 - 4805bfff
+ * SSI L4 4805c000 - 4805cfff
+ * USB Mod 4805e000 - 480fefff
+ * USB L4 4805f000 - 480fffff
+ * WIN_TRACER1 Mod 48060000 - 48060fff
+ * WIN_TRACER1 L4 48061000 - 48061fff
+ * WIN_TRACER2 Mod 48062000 - 48062fff
+ * WIN_TRACER2 L4 48063000 - 48063fff
+ * WIN_TRACER3 Mod 48064000 - 48064fff
+ * WIN_TRACER3 L4 48065000 - 48065fff
+ * WIN_TRACER4 Top 48066000 - 480660ff
+ * WIN_TRACER4 ETT 48066100 - 480661ff
+ * WIN_TRACER4 WT 48066200 - 480662ff
+ * WIN_TRACER4 L4 48067000 - 48067fff
+ * XTI Mod 48068000 - 48068fff
+ * XTI L4 48069000 - 48069fff
+ * UART1 Mod 4806a000 - 4806afff
+ * UART1 L4 4806b000 - 4806bfff
+ * UART2 Mod 4806c000 - 4806cfff
+ * UART2 L4 4806d000 - 4806dfff
+ * UART3 Mod 4806e000 - 4806efff
+ * UART3 L4 4806f000 - 4806ffff
+ * I2C1 Mod 48070000 - 48070fff
+ * I2C1 L4 48071000 - 48071fff
+ * I2C2 Mod 48072000 - 48072fff
+ * I2C2 L4 48073000 - 48073fff
+ * McBSP1 Mod 48074000 - 48074fff
+ * McBSP1 L4 48075000 - 48075fff
+ * McBSP2 Mod 48076000 - 48076fff
+ * McBSP2 L4 48077000 - 48077fff
+ * GPTIMER3 Mod 48078000 - 48078fff
+ * GPTIMER3 L4 48079000 - 48079fff
+ * GPTIMER4 Mod 4807a000 - 4807afff
+ * GPTIMER4 L4 4807b000 - 4807bfff
+ * GPTIMER5 Mod 4807c000 - 4807cfff
+ * GPTIMER5 L4 4807d000 - 4807dfff
+ * GPTIMER6 Mod 4807e000 - 4807efff
+ * GPTIMER6 L4 4807f000 - 4807ffff
+ * GPTIMER7 Mod 48080000 - 48080fff
+ * GPTIMER7 L4 48081000 - 48081fff
+ * GPTIMER8 Mod 48082000 - 48082fff
+ * GPTIMER8 L4 48083000 - 48083fff
+ * GPTIMER9 Mod 48084000 - 48084fff
+ * GPTIMER9 L4 48085000 - 48085fff
+ * GPTIMER10 Mod 48086000 - 48086fff
+ * GPTIMER10 L4 48087000 - 48087fff
+ * GPTIMER11 Mod 48088000 - 48088fff
+ * GPTIMER11 L4 48089000 - 48089fff
+ * GPTIMER12 Mod 4808a000 - 4808afff
+ * GPTIMER12 L4 4808b000 - 4808bfff
+ * EAC Mod 48090000 - 48090fff
+ * EAC L4 48091000 - 48091fff
+ * FAC Mod 48092000 - 48092fff
+ * FAC L4 48093000 - 48093fff
+ * MAILBOX Mod 48094000 - 48094fff
+ * MAILBOX L4 48095000 - 48095fff
+ * SPI1 Mod 48098000 - 48098fff
+ * SPI1 L4 48099000 - 48099fff
+ * SPI2 Mod 4809a000 - 4809afff
+ * SPI2 L4 4809b000 - 4809bfff
+ * MMC/SDIO Mod 4809c000 - 4809cfff
+ * MMC/SDIO L4 4809d000 - 4809dfff
+ * MS_PRO Mod 4809e000 - 4809efff
+ * MS_PRO L4 4809f000 - 4809ffff
+ * RNG Mod 480a0000 - 480a0fff
+ * RNG L4 480a1000 - 480a1fff
+ * DES3DES Mod 480a2000 - 480a2fff
+ * DES3DES L4 480a3000 - 480a3fff
+ * SHA1MD5 Mod 480a4000 - 480a4fff
+ * SHA1MD5 L4 480a5000 - 480a5fff
+ * AES Mod 480a6000 - 480a6fff
+ * AES L4 480a7000 - 480a7fff
+ * PKA Mod 480a8000 - 480a9fff
+ * PKA L4 480aa000 - 480aafff
+ * MG Mod 480b0000 - 480b0fff
+ * MG L4 480b1000 - 480b1fff
+ * HDQ/1-wire Mod 480b2000 - 480b2fff
+ * HDQ/1-wire L4 480b3000 - 480b3fff
+ * MPU interrupt 480fe000 - 480fefff
+ * STI channel base 54000000 - 5400ffff
+ * IVA RAM 5c000000 - 5c01ffff
+ * IVA ROM 5c020000 - 5c027fff
+ * IMG_BUF_A 5c040000 - 5c040fff
+ * IMG_BUF_B 5c042000 - 5c042fff
+ * VLCDS 5c048000 - 5c0487ff
+ * IMX_COEF 5c049000 - 5c04afff
+ * IMX_CMD 5c051000 - 5c051fff
+ * VLCDQ 5c053000 - 5c0533ff
+ * VLCDH 5c054000 - 5c054fff
+ * SEQ_CMD 5c055000 - 5c055fff
+ * IMX_REG 5c056000 - 5c0560ff
+ * VLCD_REG 5c056100 - 5c0561ff
+ * SEQ_REG 5c056200 - 5c0562ff
+ * IMG_BUF_REG 5c056300 - 5c0563ff
+ * SEQIRQ_REG 5c056400 - 5c0564ff
+ * OCP_REG 5c060000 - 5c060fff
+ * SYSC_REG 5c070000 - 5c070fff
+ * MMU_REG 5d000000 - 5d000fff
+ * sDMA R 68000400 - 680005ff
+ * sDMA W 68000600 - 680007ff
+ * Display Control 68000800 - 680009ff
+ * DSP subsystem 68000a00 - 68000bff
+ * MPU subsystem 68000c00 - 68000dff
+ * IVA subsystem 68001000 - 680011ff
+ * USB 68001200 - 680013ff
+ * Camera 68001400 - 680015ff
+ * VLYNQ (firewall) 68001800 - 68001bff
+ * VLYNQ 68001e00 - 68001fff
+ * SSI 68002000 - 680021ff
+ * L4 68002400 - 680025ff
+ * DSP (firewall) 68002800 - 68002bff
+ * DSP subsystem 68002e00 - 68002fff
+ * IVA (firewall) 68003000 - 680033ff
+ * IVA 68003600 - 680037ff
+ * GFX 68003a00 - 68003bff
+ * CMDWR emulation 68003c00 - 68003dff
+ * SMS 68004000 - 680041ff
+ * OCM 68004200 - 680043ff
+ * GPMC 68004400 - 680045ff
+ * RAM (firewall) 68005000 - 680053ff
+ * RAM (err login) 68005400 - 680057ff
+ * ROM (firewall) 68005800 - 68005bff
+ * ROM (err login) 68005c00 - 68005fff
+ * GPMC (firewall) 68006000 - 680063ff
+ * GPMC (err login) 68006400 - 680067ff
+ * SMS (err login) 68006c00 - 68006fff
+ * SMS registers 68008000 - 68008fff
+ * SDRC registers 68009000 - 68009fff
+ * GPMC registers 6800a000 6800afff
+ */
+
+ qemu_register_reset(omap2_mpu_reset, s);
+
+ return s;
+}
diff --git a/hw/omap_clk.c b/hw/omap_clk.c
new file mode 100644
index 0000000..6bcabef
--- /dev/null
+++ b/hw/omap_clk.c
@@ -0,0 +1,1260 @@
+/*
+ * OMAP clocks.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Clocks data comes in part from arch/arm/mach-omap1/clock.h in Linux.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "omap.h"
+
+struct clk {
+ const char *name;
+ const char *alias;
+ struct clk *parent;
+ struct clk *child1;
+ struct clk *sibling;
+#define ALWAYS_ENABLED (1 << 0)
+#define CLOCK_IN_OMAP310 (1 << 10)
+#define CLOCK_IN_OMAP730 (1 << 11)
+#define CLOCK_IN_OMAP1510 (1 << 12)
+#define CLOCK_IN_OMAP16XX (1 << 13)
+#define CLOCK_IN_OMAP242X (1 << 14)
+#define CLOCK_IN_OMAP243X (1 << 15)
+#define CLOCK_IN_OMAP343X (1 << 16)
+ uint32_t flags;
+ int id;
+
+ int running; /* Is currently ticking */
+ int enabled; /* Is enabled, regardless of its input clk */
+ unsigned long rate; /* Current rate (if .running) */
+ unsigned int divisor; /* Rate relative to input (if .enabled) */
+ unsigned int multiplier; /* Rate relative to input (if .enabled) */
+ qemu_irq users[16]; /* Who to notify on change */
+ int usecount; /* Automatically idle when unused */
+};
+
+static struct clk xtal_osc12m = {
+ .name = "xtal_osc_12m",
+ .rate = 12000000,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk xtal_osc32k = {
+ .name = "xtal_osc_32k",
+ .rate = 32768,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+};
+
+static struct clk ck_ref = {
+ .name = "ck_ref",
+ .alias = "clkin",
+ .parent = &xtal_osc12m,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+/* If a dpll is disabled it becomes a bypass, child clocks don't stop */
+static struct clk dpll1 = {
+ .name = "dpll1",
+ .parent = &ck_ref,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk dpll2 = {
+ .name = "dpll2",
+ .parent = &ck_ref,
+ .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk dpll3 = {
+ .name = "dpll3",
+ .parent = &ck_ref,
+ .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk dpll4 = {
+ .name = "dpll4",
+ .parent = &ck_ref,
+ .multiplier = 4,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk apll = {
+ .name = "apll",
+ .parent = &ck_ref,
+ .multiplier = 48,
+ .divisor = 12,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk ck_48m = {
+ .name = "ck_48m",
+ .parent = &dpll4, /* either dpll4 or apll */
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk ck_dpll1out = {
+ .name = "ck_dpll1out",
+ .parent = &dpll1,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk sossi_ck = {
+ .name = "ck_sossi",
+ .parent = &ck_dpll1out,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk clkm1 = {
+ .name = "clkm1",
+ .alias = "ck_gen1",
+ .parent = &dpll1,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk clkm2 = {
+ .name = "clkm2",
+ .alias = "ck_gen2",
+ .parent = &dpll1,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk clkm3 = {
+ .name = "clkm3",
+ .alias = "ck_gen3",
+ .parent = &dpll1, /* either dpll1 or ck_ref */
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk arm_ck = {
+ .name = "arm_ck",
+ .alias = "mpu_ck",
+ .parent = &clkm1,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk armper_ck = {
+ .name = "armper_ck",
+ .alias = "mpuper_ck",
+ .parent = &clkm1,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk arm_gpio_ck = {
+ .name = "arm_gpio_ck",
+ .alias = "mpu_gpio_ck",
+ .parent = &clkm1,
+ .divisor = 1,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk armxor_ck = {
+ .name = "armxor_ck",
+ .alias = "mpuxor_ck",
+ .parent = &ck_ref,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk armtim_ck = {
+ .name = "armtim_ck",
+ .alias = "mputim_ck",
+ .parent = &ck_ref, /* either CLKIN or DPLL1 */
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk armwdt_ck = {
+ .name = "armwdt_ck",
+ .alias = "mpuwd_ck",
+ .parent = &clkm1,
+ .divisor = 14,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk arminth_ck16xx = {
+ .name = "arminth_ck",
+ .parent = &arm_ck,
+ .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+ /* Note: On 16xx the frequency can be divided by 2 by programming
+ * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1
+ *
+ * 1510 version is in TC clocks.
+ */
+};
+
+static struct clk dsp_ck = {
+ .name = "dsp_ck",
+ .parent = &clkm2,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dspmmu_ck = {
+ .name = "dspmmu_ck",
+ .parent = &clkm2,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+ ALWAYS_ENABLED,
+};
+
+static struct clk dspper_ck = {
+ .name = "dspper_ck",
+ .parent = &clkm2,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dspxor_ck = {
+ .name = "dspxor_ck",
+ .parent = &ck_ref,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dsptim_ck = {
+ .name = "dsptim_ck",
+ .parent = &ck_ref,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk tc_ck = {
+ .name = "tc_ck",
+ .parent = &clkm3,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+ CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk arminth_ck15xx = {
+ .name = "arminth_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+ /* Note: On 1510 the frequency follows TC_CK
+ *
+ * 16xx version is in MPU clocks.
+ */
+};
+
+static struct clk tipb_ck = {
+ /* No-idle controlled by "tc_ck" */
+ .name = "tipb_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk l3_ocpi_ck = {
+ /* No-idle controlled by "tc_ck" */
+ .name = "l3_ocpi_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk tc1_ck = {
+ .name = "tc1_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk tc2_ck = {
+ .name = "tc2_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk dma_ck = {
+ /* No-idle controlled by "tc_ck" */
+ .name = "dma_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk dma_lcdfree_ck = {
+ .name = "dma_lcdfree_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+};
+
+static struct clk api_ck = {
+ .name = "api_ck",
+ .alias = "mpui_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk lb_ck = {
+ .name = "lb_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk lbfree_ck = {
+ .name = "lbfree_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk hsab_ck = {
+ .name = "hsab_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk rhea1_ck = {
+ .name = "rhea1_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+};
+
+static struct clk rhea2_ck = {
+ .name = "rhea2_ck",
+ .parent = &tc_ck,
+ .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+};
+
+static struct clk lcd_ck_16xx = {
+ .name = "lcd_ck",
+ .parent = &clkm3,
+ .flags = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730,
+};
+
+static struct clk lcd_ck_1510 = {
+ .name = "lcd_ck",
+ .parent = &clkm3,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk uart1_1510 = {
+ .name = "uart1_ck",
+ /* Direct from ULPD, no real parent */
+ .parent = &armper_ck, /* either armper_ck or dpll4 */
+ .rate = 12000000,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk uart1_16xx = {
+ .name = "uart1_ck",
+ /* Direct from ULPD, no real parent */
+ .parent = &armper_ck,
+ .rate = 48000000,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk uart2_ck = {
+ .name = "uart2_ck",
+ /* Direct from ULPD, no real parent */
+ .parent = &armper_ck, /* either armper_ck or dpll4 */
+ .rate = 12000000,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 |
+ ALWAYS_ENABLED,
+};
+
+static struct clk uart3_1510 = {
+ .name = "uart3_ck",
+ /* Direct from ULPD, no real parent */
+ .parent = &armper_ck, /* either armper_ck or dpll4 */
+ .rate = 12000000,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED,
+};
+
+static struct clk uart3_16xx = {
+ .name = "uart3_ck",
+ /* Direct from ULPD, no real parent */
+ .parent = &armper_ck,
+ .rate = 48000000,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */
+ .name = "usb_clk0",
+ .alias = "usb.clko",
+ /* Direct from ULPD, no parent */
+ .rate = 6000000,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk usb_hhc_ck1510 = {
+ .name = "usb_hhc_ck",
+ /* Direct from ULPD, no parent */
+ .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310,
+};
+
+static struct clk usb_hhc_ck16xx = {
+ .name = "usb_hhc_ck",
+ /* Direct from ULPD, no parent */
+ .rate = 48000000,
+ /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk usb_w2fc_mclk = {
+ .name = "usb_w2fc_mclk",
+ .alias = "usb_w2fc_ck",
+ .parent = &ck_48m,
+ .rate = 48000000,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk mclk_1510 = {
+ .name = "mclk",
+ /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+ .rate = 12000000,
+ .flags = CLOCK_IN_OMAP1510,
+};
+
+static struct clk bclk_310 = {
+ .name = "bt_mclk_out", /* Alias midi_mclk_out? */
+ .parent = &armper_ck,
+ .flags = CLOCK_IN_OMAP310,
+};
+
+static struct clk mclk_310 = {
+ .name = "com_mclk_out",
+ .parent = &armper_ck,
+ .flags = CLOCK_IN_OMAP310,
+};
+
+static struct clk mclk_16xx = {
+ .name = "mclk",
+ /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk bclk_1510 = {
+ .name = "bclk",
+ /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+ .rate = 12000000,
+ .flags = CLOCK_IN_OMAP1510,
+};
+
+static struct clk bclk_16xx = {
+ .name = "bclk",
+ /* Direct from ULPD, no parent. May be enabled by ext hardware. */
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk mmc1_ck = {
+ .name = "mmc_ck",
+ .id = 1,
+ /* Functional clock is direct from ULPD, interface clock is ARMPER */
+ .parent = &armper_ck, /* either armper_ck or dpll4 */
+ .rate = 48000000,
+ .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310,
+};
+
+static struct clk mmc2_ck = {
+ .name = "mmc_ck",
+ .id = 2,
+ /* Functional clock is direct from ULPD, interface clock is ARMPER */
+ .parent = &armper_ck,
+ .rate = 48000000,
+ .flags = CLOCK_IN_OMAP16XX,
+};
+
+static struct clk cam_mclk = {
+ .name = "cam.mclk",
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+ .rate = 12000000,
+};
+
+static struct clk cam_exclk = {
+ .name = "cam.exclk",
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+ /* Either 12M from cam.mclk or 48M from dpll4 */
+ .parent = &cam_mclk,
+};
+
+static struct clk cam_lclk = {
+ .name = "cam.lclk",
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX,
+};
+
+static struct clk i2c_fck = {
+ .name = "i2c_fck",
+ .id = 1,
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+ ALWAYS_ENABLED,
+ .parent = &armxor_ck,
+};
+
+static struct clk i2c_ick = {
+ .name = "i2c_ick",
+ .id = 1,
+ .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED,
+ .parent = &armper_ck,
+};
+
+static struct clk clk32k = {
+ .name = "clk32-kHz",
+ .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX |
+ CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .parent = &xtal_osc32k,
+};
+
+static struct clk ref_clk = {
+ .name = "ref_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .rate = 12000000, /* 12 MHz or 13 MHz or 19.2 MHz */
+ /*.parent = sys.xtalin */
+};
+
+static struct clk apll_96m = {
+ .name = "apll_96m",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .rate = 96000000,
+ /*.parent = ref_clk */
+};
+
+static struct clk apll_54m = {
+ .name = "apll_54m",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .rate = 54000000,
+ /*.parent = ref_clk */
+};
+
+static struct clk sys_clk = {
+ .name = "sys_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .rate = 32768,
+ /*.parent = sys.xtalin */
+};
+
+static struct clk sleep_clk = {
+ .name = "sleep_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .rate = 32768,
+ /*.parent = sys.xtalin */
+};
+
+static struct clk dpll_ck = {
+ .name = "dpll",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .parent = &ref_clk,
+};
+
+static struct clk dpll_x2_ck = {
+ .name = "dpll_x2",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .parent = &ref_clk,
+};
+
+static struct clk wdt1_sys_clk = {
+ .name = "wdt1_sys_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED,
+ .rate = 32768,
+ /*.parent = sys.xtalin */
+};
+
+static struct clk func_96m_clk = {
+ .name = "func_96m_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .divisor = 1,
+ .parent = &apll_96m,
+};
+
+static struct clk func_48m_clk = {
+ .name = "func_48m_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .divisor = 2,
+ .parent = &apll_96m,
+};
+
+static struct clk func_12m_clk = {
+ .name = "func_12m_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .divisor = 8,
+ .parent = &apll_96m,
+};
+
+static struct clk func_54m_clk = {
+ .name = "func_54m_clk",
+ .flags = CLOCK_IN_OMAP242X,
+ .divisor = 1,
+ .parent = &apll_54m,
+};
+
+static struct clk sys_clkout = {
+ .name = "clkout",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk sys_clkout2 = {
+ .name = "clkout2",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_clk = {
+ .name = "core_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &dpll_x2_ck, /* Switchable between dpll_ck and clk32k */
+};
+
+static struct clk l3_clk = {
+ .name = "l3_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+};
+
+static struct clk core_l4_iclk = {
+ .name = "core_l4_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &l3_clk,
+};
+
+static struct clk wu_l4_iclk = {
+ .name = "wu_l4_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &l3_clk,
+};
+
+static struct clk core_l3_iclk = {
+ .name = "core_l3_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+};
+
+static struct clk core_l4_usb_clk = {
+ .name = "core_l4_usb_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &l3_clk,
+};
+
+static struct clk wu_gpt1_clk = {
+ .name = "wu_gpt1_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk wu_32k_clk = {
+ .name = "wu_32k_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk uart1_fclk = {
+ .name = "uart1_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_48m_clk,
+};
+
+static struct clk uart1_iclk = {
+ .name = "uart1_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk uart2_fclk = {
+ .name = "uart2_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_48m_clk,
+};
+
+static struct clk uart2_iclk = {
+ .name = "uart2_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk uart3_fclk = {
+ .name = "uart3_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_48m_clk,
+};
+
+static struct clk uart3_iclk = {
+ .name = "uart3_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk mpu_fclk = {
+ .name = "mpu_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+};
+
+static struct clk mpu_iclk = {
+ .name = "mpu_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+};
+
+static struct clk int_m_fclk = {
+ .name = "int_m_fclk",
+ .alias = "mpu_intc_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+};
+
+static struct clk int_m_iclk = {
+ .name = "int_m_iclk",
+ .alias = "mpu_intc_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+};
+
+static struct clk core_gpt2_clk = {
+ .name = "core_gpt2_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt3_clk = {
+ .name = "core_gpt3_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt4_clk = {
+ .name = "core_gpt4_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt5_clk = {
+ .name = "core_gpt5_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt6_clk = {
+ .name = "core_gpt6_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt7_clk = {
+ .name = "core_gpt7_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt8_clk = {
+ .name = "core_gpt8_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt9_clk = {
+ .name = "core_gpt9_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt10_clk = {
+ .name = "core_gpt10_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt11_clk = {
+ .name = "core_gpt11_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk core_gpt12_clk = {
+ .name = "core_gpt12_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+};
+
+static struct clk mcbsp1_clk = {
+ .name = "mcbsp1_cg",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .divisor = 2,
+ .parent = &func_96m_clk,
+};
+
+static struct clk mcbsp2_clk = {
+ .name = "mcbsp2_cg",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .divisor = 2,
+ .parent = &func_96m_clk,
+};
+
+static struct clk emul_clk = {
+ .name = "emul_ck",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_54m_clk,
+};
+
+static struct clk sdma_fclk = {
+ .name = "sdma_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &l3_clk,
+};
+
+static struct clk sdma_iclk = {
+ .name = "sdma_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l3_iclk, /* core_l4_iclk for the configuration port */
+};
+
+static struct clk i2c1_fclk = {
+ .name = "i2c1.fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_12m_clk,
+ .divisor = 1,
+};
+
+static struct clk i2c1_iclk = {
+ .name = "i2c1.iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk i2c2_fclk = {
+ .name = "i2c2.fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_12m_clk,
+ .divisor = 1,
+};
+
+static struct clk i2c2_iclk = {
+ .name = "i2c2.iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk gpio_dbclk[4] = {
+ {
+ .name = "gpio1_dbclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &wu_32k_clk,
+ }, {
+ .name = "gpio2_dbclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &wu_32k_clk,
+ }, {
+ .name = "gpio3_dbclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &wu_32k_clk,
+ }, {
+ .name = "gpio4_dbclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &wu_32k_clk,
+ },
+};
+
+static struct clk gpio_iclk = {
+ .name = "gpio_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &wu_l4_iclk,
+};
+
+static struct clk mmc_fck = {
+ .name = "mmc_fclk",
+ .flags = CLOCK_IN_OMAP242X,
+ .parent = &func_96m_clk,
+};
+
+static struct clk mmc_ick = {
+ .name = "mmc_iclk",
+ .flags = CLOCK_IN_OMAP242X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk spi_fclk[3] = {
+ {
+ .name = "spi1_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_48m_clk,
+ }, {
+ .name = "spi2_fclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_48m_clk,
+ }, {
+ .name = "spi3_fclk",
+ .flags = CLOCK_IN_OMAP243X,
+ .parent = &func_48m_clk,
+ },
+};
+
+static struct clk dss_clk[2] = {
+ {
+ .name = "dss_clk1",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_clk,
+ }, {
+ .name = "dss_clk2",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &sys_clk,
+ },
+};
+
+static struct clk dss_54m_clk = {
+ .name = "dss_54m_clk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &func_54m_clk,
+};
+
+static struct clk dss_l3_iclk = {
+ .name = "dss_l3_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l3_iclk,
+};
+
+static struct clk dss_l4_iclk = {
+ .name = "dss_l4_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+};
+
+static struct clk spi_iclk[3] = {
+ {
+ .name = "spi1_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+ }, {
+ .name = "spi2_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+ }, {
+ .name = "spi3_iclk",
+ .flags = CLOCK_IN_OMAP243X,
+ .parent = &core_l4_iclk,
+ },
+};
+
+static struct clk omapctrl_clk = {
+ .name = "omapctrl_iclk",
+ .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X,
+ /* XXX Should be in WKUP domain */
+ .parent = &core_l4_iclk,
+};
+
+static struct clk *onchip_clks[] = {
+ /* OMAP 1 */
+
+ /* non-ULPD clocks */
+ &xtal_osc12m,
+ &xtal_osc32k,
+ &ck_ref,
+ &dpll1,
+ &dpll2,
+ &dpll3,
+ &dpll4,
+ &apll,
+ &ck_48m,
+ /* CK_GEN1 clocks */
+ &clkm1,
+ &ck_dpll1out,
+ &sossi_ck,
+ &arm_ck,
+ &armper_ck,
+ &arm_gpio_ck,
+ &armxor_ck,
+ &armtim_ck,
+ &armwdt_ck,
+ &arminth_ck15xx, &arminth_ck16xx,
+ /* CK_GEN2 clocks */
+ &clkm2,
+ &dsp_ck,
+ &dspmmu_ck,
+ &dspper_ck,
+ &dspxor_ck,
+ &dsptim_ck,
+ /* CK_GEN3 clocks */
+ &clkm3,
+ &tc_ck,
+ &tipb_ck,
+ &l3_ocpi_ck,
+ &tc1_ck,
+ &tc2_ck,
+ &dma_ck,
+ &dma_lcdfree_ck,
+ &api_ck,
+ &lb_ck,
+ &lbfree_ck,
+ &hsab_ck,
+ &rhea1_ck,
+ &rhea2_ck,
+ &lcd_ck_16xx,
+ &lcd_ck_1510,
+ /* ULPD clocks */
+ &uart1_1510,
+ &uart1_16xx,
+ &uart2_ck,
+ &uart3_1510,
+ &uart3_16xx,
+ &usb_clk0,
+ &usb_hhc_ck1510, &usb_hhc_ck16xx,
+ &mclk_1510, &mclk_16xx, &mclk_310,
+ &bclk_1510, &bclk_16xx, &bclk_310,
+ &mmc1_ck,
+ &mmc2_ck,
+ &cam_mclk,
+ &cam_exclk,
+ &cam_lclk,
+ &clk32k,
+ &usb_w2fc_mclk,
+ /* Virtual clocks */
+ &i2c_fck,
+ &i2c_ick,
+
+ /* OMAP 2 */
+
+ &ref_clk,
+ &apll_96m,
+ &apll_54m,
+ &sys_clk,
+ &sleep_clk,
+ &dpll_ck,
+ &dpll_x2_ck,
+ &wdt1_sys_clk,
+ &func_96m_clk,
+ &func_48m_clk,
+ &func_12m_clk,
+ &func_54m_clk,
+ &sys_clkout,
+ &sys_clkout2,
+ &core_clk,
+ &l3_clk,
+ &core_l4_iclk,
+ &wu_l4_iclk,
+ &core_l3_iclk,
+ &core_l4_usb_clk,
+ &wu_gpt1_clk,
+ &wu_32k_clk,
+ &uart1_fclk,
+ &uart1_iclk,
+ &uart2_fclk,
+ &uart2_iclk,
+ &uart3_fclk,
+ &uart3_iclk,
+ &mpu_fclk,
+ &mpu_iclk,
+ &int_m_fclk,
+ &int_m_iclk,
+ &core_gpt2_clk,
+ &core_gpt3_clk,
+ &core_gpt4_clk,
+ &core_gpt5_clk,
+ &core_gpt6_clk,
+ &core_gpt7_clk,
+ &core_gpt8_clk,
+ &core_gpt9_clk,
+ &core_gpt10_clk,
+ &core_gpt11_clk,
+ &core_gpt12_clk,
+ &mcbsp1_clk,
+ &mcbsp2_clk,
+ &emul_clk,
+ &sdma_fclk,
+ &sdma_iclk,
+ &i2c1_fclk,
+ &i2c1_iclk,
+ &i2c2_fclk,
+ &i2c2_iclk,
+ &gpio_dbclk[0],
+ &gpio_dbclk[1],
+ &gpio_dbclk[2],
+ &gpio_dbclk[3],
+ &gpio_iclk,
+ &mmc_fck,
+ &mmc_ick,
+ &spi_fclk[0],
+ &spi_iclk[0],
+ &spi_fclk[1],
+ &spi_iclk[1],
+ &spi_fclk[2],
+ &spi_iclk[2],
+ &dss_clk[0],
+ &dss_clk[1],
+ &dss_54m_clk,
+ &dss_l3_iclk,
+ &dss_l4_iclk,
+ &omapctrl_clk,
+
+ NULL
+};
+
+void omap_clk_adduser(struct clk *clk, qemu_irq user)
+{
+ qemu_irq *i;
+
+ for (i = clk->users; *i; i ++);
+ *i = user;
+}
+
+struct clk *omap_findclk(struct omap_mpu_state_s *mpu, const char *name)
+{
+ struct clk *i;
+
+ for (i = mpu->clks; i->name; i ++)
+ if (!strcmp(i->name, name) || (i->alias && !strcmp(i->alias, name)))
+ return i;
+ hw_error("%s: %s not found\n", __FUNCTION__, name);
+}
+
+void omap_clk_get(struct clk *clk)
+{
+ clk->usecount ++;
+}
+
+void omap_clk_put(struct clk *clk)
+{
+ if (!(clk->usecount --))
+ hw_error("%s: %s is not in use\n", __FUNCTION__, clk->name);
+}
+
+static void omap_clk_update(struct clk *clk)
+{
+ int parent, running;
+ qemu_irq *user;
+ struct clk *i;
+
+ if (clk->parent)
+ parent = clk->parent->running;
+ else
+ parent = 1;
+
+ running = parent && (clk->enabled ||
+ ((clk->flags & ALWAYS_ENABLED) && clk->usecount));
+ if (clk->running != running) {
+ clk->running = running;
+ for (user = clk->users; *user; user ++)
+ qemu_set_irq(*user, running);
+ for (i = clk->child1; i; i = i->sibling)
+ omap_clk_update(i);
+ }
+}
+
+static void omap_clk_rate_update_full(struct clk *clk, unsigned long int rate,
+ unsigned long int div, unsigned long int mult)
+{
+ struct clk *i;
+ qemu_irq *user;
+
+ clk->rate = muldiv64(rate, mult, div);
+ if (clk->running)
+ for (user = clk->users; *user; user ++)
+ qemu_irq_raise(*user);
+ for (i = clk->child1; i; i = i->sibling)
+ omap_clk_rate_update_full(i, rate,
+ div * i->divisor, mult * i->multiplier);
+}
+
+static void omap_clk_rate_update(struct clk *clk)
+{
+ struct clk *i;
+ unsigned long int div, mult = div = 1;
+
+ for (i = clk; i->parent; i = i->parent) {
+ div *= i->divisor;
+ mult *= i->multiplier;
+ }
+
+ omap_clk_rate_update_full(clk, i->rate, div, mult);
+}
+
+void omap_clk_reparent(struct clk *clk, struct clk *parent)
+{
+ struct clk **p;
+
+ if (clk->parent) {
+ for (p = &clk->parent->child1; *p != clk; p = &(*p)->sibling);
+ *p = clk->sibling;
+ }
+
+ clk->parent = parent;
+ if (parent) {
+ clk->sibling = parent->child1;
+ parent->child1 = clk;
+ omap_clk_update(clk);
+ omap_clk_rate_update(clk);
+ } else
+ clk->sibling = NULL;
+}
+
+void omap_clk_onoff(struct clk *clk, int on)
+{
+ clk->enabled = on;
+ omap_clk_update(clk);
+}
+
+void omap_clk_canidle(struct clk *clk, int can)
+{
+ if (can)
+ omap_clk_put(clk);
+ else
+ omap_clk_get(clk);
+}
+
+void omap_clk_setrate(struct clk *clk, int divide, int multiply)
+{
+ clk->divisor = divide;
+ clk->multiplier = multiply;
+ omap_clk_rate_update(clk);
+}
+
+int64_t omap_clk_getrate(omap_clk clk)
+{
+ return clk->rate;
+}
+
+void omap_clk_init(struct omap_mpu_state_s *mpu)
+{
+ struct clk **i, *j, *k;
+ int count;
+ int flag;
+
+ if (cpu_is_omap310(mpu))
+ flag = CLOCK_IN_OMAP310;
+ else if (cpu_is_omap1510(mpu))
+ flag = CLOCK_IN_OMAP1510;
+ else if (cpu_is_omap2410(mpu) || cpu_is_omap2420(mpu))
+ flag = CLOCK_IN_OMAP242X;
+ else if (cpu_is_omap2430(mpu))
+ flag = CLOCK_IN_OMAP243X;
+ else if (cpu_is_omap3430(mpu))
+ flag = CLOCK_IN_OMAP243X;
+ else
+ return;
+
+ for (i = onchip_clks, count = 0; *i; i ++)
+ if ((*i)->flags & flag)
+ count ++;
+ mpu->clks = (struct clk *) qemu_mallocz(sizeof(struct clk) * (count + 1));
+ for (i = onchip_clks, j = mpu->clks; *i; i ++)
+ if ((*i)->flags & flag) {
+ memcpy(j, *i, sizeof(struct clk));
+ for (k = mpu->clks; k < j; k ++)
+ if (j->parent && !strcmp(j->parent->name, k->name)) {
+ j->parent = k;
+ j->sibling = k->child1;
+ k->child1 = j;
+ } else if (k->parent && !strcmp(k->parent->name, j->name)) {
+ k->parent = j;
+ k->sibling = j->child1;
+ j->child1 = k;
+ }
+ j->divisor = j->divisor ?: 1;
+ j->multiplier = j->multiplier ?: 1;
+ j ++;
+ }
+ for (j = mpu->clks; count --; j ++) {
+ omap_clk_update(j);
+ omap_clk_rate_update(j);
+ }
+}
diff --git a/hw/omap_dma.c b/hw/omap_dma.c
new file mode 100644
index 0000000..8e2dcc9
--- /dev/null
+++ b/hw/omap_dma.c
@@ -0,0 +1,2082 @@
+/*
+ * TI OMAP DMA gigacell.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2007-2008 Lauro Ramos Venancio <lauro.venancio@indt.org.br>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "omap.h"
+#include "irq.h"
+#include "soc_dma.h"
+
+struct omap_dma_channel_s {
+ /* transfer data */
+ int burst[2];
+ int pack[2];
+ int endian[2];
+ int endian_lock[2];
+ int translate[2];
+ enum omap_dma_port port[2];
+ target_phys_addr_t addr[2];
+ omap_dma_addressing_t mode[2];
+ uint32_t elements;
+ uint16_t frames;
+ int32_t frame_index[2];
+ int16_t element_index[2];
+ int data_type;
+
+ /* transfer type */
+ int transparent_copy;
+ int constant_fill;
+ uint32_t color;
+ int prefetch;
+
+ /* auto init and linked channel data */
+ int end_prog;
+ int repeat;
+ int auto_init;
+ int link_enabled;
+ int link_next_ch;
+
+ /* interruption data */
+ int interrupts;
+ int status;
+ int cstatus;
+
+ /* state data */
+ int active;
+ int enable;
+ int sync;
+ int src_sync;
+ int pending_request;
+ int waiting_end_prog;
+ uint16_t cpc;
+ int set_update;
+
+ /* sync type */
+ int fs;
+ int bs;
+
+ /* compatibility */
+ int omap_3_1_compatible_disable;
+
+ qemu_irq irq;
+ struct omap_dma_channel_s *sibling;
+
+ struct omap_dma_reg_set_s {
+ target_phys_addr_t src, dest;
+ int frame;
+ int element;
+ int pck_element;
+ int frame_delta[2];
+ int elem_delta[2];
+ int frames;
+ int elements;
+ int pck_elements;
+ } active_set;
+
+ struct soc_dma_ch_s *dma;
+
+ /* unused parameters */
+ int write_mode;
+ int priority;
+ int interleave_disabled;
+ int type;
+ int suspend;
+ int buf_disable;
+};
+
+struct omap_dma_s {
+ struct soc_dma_s *dma;
+
+ struct omap_mpu_state_s *mpu;
+ omap_clk clk;
+ qemu_irq irq[4];
+ void (*intr_update)(struct omap_dma_s *s);
+ enum omap_dma_model model;
+ int omap_3_1_mapping_disabled;
+
+ uint32_t gcr;
+ uint32_t ocp;
+ uint32_t caps[5];
+ uint32_t irqen[4];
+ uint32_t irqstat[4];
+
+ int chans;
+ struct omap_dma_channel_s ch[32];
+ struct omap_dma_lcd_channel_s lcd_ch;
+};
+
+/* Interrupts */
+#define TIMEOUT_INTR (1 << 0)
+#define EVENT_DROP_INTR (1 << 1)
+#define HALF_FRAME_INTR (1 << 2)
+#define END_FRAME_INTR (1 << 3)
+#define LAST_FRAME_INTR (1 << 4)
+#define END_BLOCK_INTR (1 << 5)
+#define SYNC (1 << 6)
+#define END_PKT_INTR (1 << 7)
+#define TRANS_ERR_INTR (1 << 8)
+#define MISALIGN_INTR (1 << 11)
+
+static inline void omap_dma_interrupts_update(struct omap_dma_s *s)
+{
+ return s->intr_update(s);
+}
+
+static void omap_dma_channel_load(struct omap_dma_channel_s *ch)
+{
+ struct omap_dma_reg_set_s *a = &ch->active_set;
+ int i, normal;
+ int omap_3_1 = !ch->omap_3_1_compatible_disable;
+
+ /*
+ * TODO: verify address ranges and alignment
+ * TODO: port endianness
+ */
+
+ a->src = ch->addr[0];
+ a->dest = ch->addr[1];
+ a->frames = ch->frames;
+ a->elements = ch->elements;
+ a->pck_elements = ch->frame_index[!ch->src_sync];
+ a->frame = 0;
+ a->element = 0;
+ a->pck_element = 0;
+
+ if (unlikely(!ch->elements || !ch->frames)) {
+ printf("%s: bad DMA request\n", __FUNCTION__);
+ return;
+ }
+
+ for (i = 0; i < 2; i ++)
+ switch (ch->mode[i]) {
+ case constant:
+ a->elem_delta[i] = 0;
+ a->frame_delta[i] = 0;
+ break;
+ case post_incremented:
+ a->elem_delta[i] = ch->data_type;
+ a->frame_delta[i] = 0;
+ break;
+ case single_index:
+ a->elem_delta[i] = ch->data_type +
+ ch->element_index[omap_3_1 ? 0 : i] - 1;
+ a->frame_delta[i] = 0;
+ break;
+ case double_index:
+ a->elem_delta[i] = ch->data_type +
+ ch->element_index[omap_3_1 ? 0 : i] - 1;
+ a->frame_delta[i] = ch->frame_index[omap_3_1 ? 0 : i] -
+ ch->element_index[omap_3_1 ? 0 : i];
+ break;
+ default:
+ break;
+ }
+
+ normal = !ch->transparent_copy && !ch->constant_fill &&
+ /* FIFO is big-endian so either (ch->endian[n] == 1) OR
+ * (ch->endian_lock[n] == 1) mean no endianism conversion. */
+ (ch->endian[0] | ch->endian_lock[0]) ==
+ (ch->endian[1] | ch->endian_lock[1]);
+ for (i = 0; i < 2; i ++) {
+ /* TODO: for a->frame_delta[i] > 0 still use the fast path, just
+ * limit min_elems in omap_dma_transfer_setup to the nearest frame
+ * end. */
+ if (!a->elem_delta[i] && normal &&
+ (a->frames == 1 || !a->frame_delta[i]))
+ ch->dma->type[i] = soc_dma_access_const;
+ else if (a->elem_delta[i] == ch->data_type && normal &&
+ (a->frames == 1 || !a->frame_delta[i]))
+ ch->dma->type[i] = soc_dma_access_linear;
+ else
+ ch->dma->type[i] = soc_dma_access_other;
+
+ ch->dma->vaddr[i] = ch->addr[i];
+ }
+ soc_dma_ch_update(ch->dma);
+}
+
+static void omap_dma_activate_channel(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch)
+{
+ if (!ch->active) {
+ if (ch->set_update) {
+ /* It's not clear when the active set is supposed to be
+ * loaded from registers. We're already loading it when the
+ * channel is enabled, and for some guests this is not enough
+ * but that may be also because of a race condition (no
+ * delays in qemu) in the guest code, which we're just
+ * working around here. */
+ omap_dma_channel_load(ch);
+ ch->set_update = 0;
+ }
+
+ ch->active = 1;
+ soc_dma_set_request(ch->dma, 1);
+ if (ch->sync)
+ ch->status |= SYNC;
+ }
+}
+
+static void omap_dma_deactivate_channel(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch)
+{
+ /* Update cpc */
+ ch->cpc = ch->active_set.dest & 0xffff;
+
+ if (ch->pending_request && !ch->waiting_end_prog && ch->enable) {
+ /* Don't deactivate the channel */
+ ch->pending_request = 0;
+ return;
+ }
+
+ /* Don't deactive the channel if it is synchronized and the DMA request is
+ active */
+ if (ch->sync && ch->enable && (s->dma->drqbmp & (1 << ch->sync)))
+ return;
+
+ if (ch->active) {
+ ch->active = 0;
+ ch->status &= ~SYNC;
+ soc_dma_set_request(ch->dma, 0);
+ }
+}
+
+static void omap_dma_enable_channel(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch)
+{
+ if (!ch->enable) {
+ ch->enable = 1;
+ ch->waiting_end_prog = 0;
+ omap_dma_channel_load(ch);
+ /* TODO: theoretically if ch->sync && ch->prefetch &&
+ * !s->dma->drqbmp[ch->sync], we should also activate and fetch
+ * from source and then stall until signalled. */
+ if ((!ch->sync) || (s->dma->drqbmp & (1 << ch->sync)))
+ omap_dma_activate_channel(s, ch);
+ }
+}
+
+static void omap_dma_disable_channel(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch)
+{
+ if (ch->enable) {
+ ch->enable = 0;
+ /* Discard any pending request */
+ ch->pending_request = 0;
+ omap_dma_deactivate_channel(s, ch);
+ }
+}
+
+static void omap_dma_channel_end_prog(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch)
+{
+ if (ch->waiting_end_prog) {
+ ch->waiting_end_prog = 0;
+ if (!ch->sync || ch->pending_request) {
+ ch->pending_request = 0;
+ omap_dma_activate_channel(s, ch);
+ }
+ }
+}
+
+static void omap_dma_interrupts_3_1_update(struct omap_dma_s *s)
+{
+ struct omap_dma_channel_s *ch = s->ch;
+
+ /* First three interrupts are shared between two channels each. */
+ if (ch[0].status | ch[6].status)
+ qemu_irq_raise(ch[0].irq);
+ if (ch[1].status | ch[7].status)
+ qemu_irq_raise(ch[1].irq);
+ if (ch[2].status | ch[8].status)
+ qemu_irq_raise(ch[2].irq);
+ if (ch[3].status)
+ qemu_irq_raise(ch[3].irq);
+ if (ch[4].status)
+ qemu_irq_raise(ch[4].irq);
+ if (ch[5].status)
+ qemu_irq_raise(ch[5].irq);
+}
+
+static void omap_dma_interrupts_3_2_update(struct omap_dma_s *s)
+{
+ struct omap_dma_channel_s *ch = s->ch;
+ int i;
+
+ for (i = s->chans; i; ch ++, i --)
+ if (ch->status)
+ qemu_irq_raise(ch->irq);
+}
+
+static void omap_dma_enable_3_1_mapping(struct omap_dma_s *s)
+{
+ s->omap_3_1_mapping_disabled = 0;
+ s->chans = 9;
+ s->intr_update = omap_dma_interrupts_3_1_update;
+}
+
+static void omap_dma_disable_3_1_mapping(struct omap_dma_s *s)
+{
+ s->omap_3_1_mapping_disabled = 1;
+ s->chans = 16;
+ s->intr_update = omap_dma_interrupts_3_2_update;
+}
+
+static void omap_dma_process_request(struct omap_dma_s *s, int request)
+{
+ int channel;
+ int drop_event = 0;
+ struct omap_dma_channel_s *ch = s->ch;
+
+ for (channel = 0; channel < s->chans; channel ++, ch ++) {
+ if (ch->enable && ch->sync == request) {
+ if (!ch->active)
+ omap_dma_activate_channel(s, ch);
+ else if (!ch->pending_request)
+ ch->pending_request = 1;
+ else {
+ /* Request collision */
+ /* Second request received while processing other request */
+ ch->status |= EVENT_DROP_INTR;
+ drop_event = 1;
+ }
+ }
+ }
+
+ if (drop_event)
+ omap_dma_interrupts_update(s);
+}
+
+static void omap_dma_transfer_generic(struct soc_dma_ch_s *dma)
+{
+ uint8_t value[4];
+ struct omap_dma_channel_s *ch = dma->opaque;
+ struct omap_dma_reg_set_s *a = &ch->active_set;
+ int bytes = dma->bytes;
+#ifdef MULTI_REQ
+ uint16_t status = ch->status;
+#endif
+
+ do {
+ /* Transfer a single element */
+ /* FIXME: check the endianness */
+ if (!ch->constant_fill)
+ cpu_physical_memory_read(a->src, value, ch->data_type);
+ else
+ *(uint32_t *) value = ch->color;
+
+ if (!ch->transparent_copy || *(uint32_t *) value != ch->color)
+ cpu_physical_memory_write(a->dest, value, ch->data_type);
+
+ a->src += a->elem_delta[0];
+ a->dest += a->elem_delta[1];
+ a->element ++;
+
+#ifndef MULTI_REQ
+ if (a->element == a->elements) {
+ /* End of Frame */
+ a->element = 0;
+ a->src += a->frame_delta[0];
+ a->dest += a->frame_delta[1];
+ a->frame ++;
+
+ /* If the channel is async, update cpc */
+ if (!ch->sync)
+ ch->cpc = a->dest & 0xffff;
+ }
+ } while ((bytes -= ch->data_type));
+#else
+ /* If the channel is element synchronized, deactivate it */
+ if (ch->sync && !ch->fs && !ch->bs)
+ omap_dma_deactivate_channel(s, ch);
+
+ /* If it is the last frame, set the LAST_FRAME interrupt */
+ if (a->element == 1 && a->frame == a->frames - 1)
+ if (ch->interrupts & LAST_FRAME_INTR)
+ ch->status |= LAST_FRAME_INTR;
+
+ /* If the half of the frame was reached, set the HALF_FRAME
+ interrupt */
+ if (a->element == (a->elements >> 1))
+ if (ch->interrupts & HALF_FRAME_INTR)
+ ch->status |= HALF_FRAME_INTR;
+
+ if (ch->fs && ch->bs) {
+ a->pck_element ++;
+ /* Check if a full packet has beed transferred. */
+ if (a->pck_element == a->pck_elements) {
+ a->pck_element = 0;
+
+ /* Set the END_PKT interrupt */
+ if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync)
+ ch->status |= END_PKT_INTR;
+
+ /* If the channel is packet-synchronized, deactivate it */
+ if (ch->sync)
+ omap_dma_deactivate_channel(s, ch);
+ }
+ }
+
+ if (a->element == a->elements) {
+ /* End of Frame */
+ a->element = 0;
+ a->src += a->frame_delta[0];
+ a->dest += a->frame_delta[1];
+ a->frame ++;
+
+ /* If the channel is frame synchronized, deactivate it */
+ if (ch->sync && ch->fs && !ch->bs)
+ omap_dma_deactivate_channel(s, ch);
+
+ /* If the channel is async, update cpc */
+ if (!ch->sync)
+ ch->cpc = a->dest & 0xffff;
+
+ /* Set the END_FRAME interrupt */
+ if (ch->interrupts & END_FRAME_INTR)
+ ch->status |= END_FRAME_INTR;
+
+ if (a->frame == a->frames) {
+ /* End of Block */
+ /* Disable the channel */
+
+ if (ch->omap_3_1_compatible_disable) {
+ omap_dma_disable_channel(s, ch);
+ if (ch->link_enabled)
+ omap_dma_enable_channel(s,
+ &s->ch[ch->link_next_ch]);
+ } else {
+ if (!ch->auto_init)
+ omap_dma_disable_channel(s, ch);
+ else if (ch->repeat || ch->end_prog)
+ omap_dma_channel_load(ch);
+ else {
+ ch->waiting_end_prog = 1;
+ omap_dma_deactivate_channel(s, ch);
+ }
+ }
+
+ if (ch->interrupts & END_BLOCK_INTR)
+ ch->status |= END_BLOCK_INTR;
+ }
+ }
+ } while (status == ch->status && ch->active);
+
+ omap_dma_interrupts_update(s);
+#endif
+}
+
+enum {
+ omap_dma_intr_element_sync,
+ omap_dma_intr_last_frame,
+ omap_dma_intr_half_frame,
+ omap_dma_intr_frame,
+ omap_dma_intr_frame_sync,
+ omap_dma_intr_packet,
+ omap_dma_intr_packet_sync,
+ omap_dma_intr_block,
+ __omap_dma_intr_last,
+};
+
+static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma)
+{
+ struct omap_dma_port_if_s *src_p, *dest_p;
+ struct omap_dma_reg_set_s *a;
+ struct omap_dma_channel_s *ch = dma->opaque;
+ struct omap_dma_s *s = dma->dma->opaque;
+ int frames, min_elems, elements[__omap_dma_intr_last];
+
+ a = &ch->active_set;
+
+ src_p = &s->mpu->port[ch->port[0]];
+ dest_p = &s->mpu->port[ch->port[1]];
+ if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) ||
+ (!dest_p->addr_valid(s->mpu, a->dest))) {
+#if 0
+ /* Bus time-out */
+ if (ch->interrupts & TIMEOUT_INTR)
+ ch->status |= TIMEOUT_INTR;
+ omap_dma_deactivate_channel(s, ch);
+ continue;
+#endif
+ printf("%s: Bus time-out in DMA%i operation\n",
+ __FUNCTION__, dma->num);
+ }
+
+ min_elems = INT_MAX;
+
+ /* Check all the conditions that terminate the transfer starting
+ * with those that can occur the soonest. */
+#define INTR_CHECK(cond, id, nelements) \
+ if (cond) { \
+ elements[id] = nelements; \
+ if (elements[id] < min_elems) \
+ min_elems = elements[id]; \
+ } else \
+ elements[id] = INT_MAX;
+
+ /* Elements */
+ INTR_CHECK(
+ ch->sync && !ch->fs && !ch->bs,
+ omap_dma_intr_element_sync,
+ 1)
+
+ /* Frames */
+ /* TODO: for transfers where entire frames can be read and written
+ * using memcpy() but a->frame_delta is non-zero, try to still do
+ * transfers using soc_dma but limit min_elems to a->elements - ...
+ * See also the TODO in omap_dma_channel_load. */
+ INTR_CHECK(
+ (ch->interrupts & LAST_FRAME_INTR) &&
+ ((a->frame < a->frames - 1) || !a->element),
+ omap_dma_intr_last_frame,
+ (a->frames - a->frame - 2) * a->elements +
+ (a->elements - a->element + 1))
+ INTR_CHECK(
+ ch->interrupts & HALF_FRAME_INTR,
+ omap_dma_intr_half_frame,
+ (a->elements >> 1) +
+ (a->element >= (a->elements >> 1) ? a->elements : 0) -
+ a->element)
+ INTR_CHECK(
+ ch->sync && ch->fs && (ch->interrupts & END_FRAME_INTR),
+ omap_dma_intr_frame,
+ a->elements - a->element)
+ INTR_CHECK(
+ ch->sync && ch->fs && !ch->bs,
+ omap_dma_intr_frame_sync,
+ a->elements - a->element)
+
+ /* Packets */
+ INTR_CHECK(
+ ch->fs && ch->bs &&
+ (ch->interrupts & END_PKT_INTR) && !ch->src_sync,
+ omap_dma_intr_packet,
+ a->pck_elements - a->pck_element)
+ INTR_CHECK(
+ ch->fs && ch->bs && ch->sync,
+ omap_dma_intr_packet_sync,
+ a->pck_elements - a->pck_element)
+
+ /* Blocks */
+ INTR_CHECK(
+ 1,
+ omap_dma_intr_block,
+ (a->frames - a->frame - 1) * a->elements +
+ (a->elements - a->element))
+
+ dma->bytes = min_elems * ch->data_type;
+
+ /* Set appropriate interrupts and/or deactivate channels */
+
+#ifdef MULTI_REQ
+ /* TODO: should all of this only be done if dma->update, and otherwise
+ * inside omap_dma_transfer_generic below - check what's faster. */
+ if (dma->update) {
+#endif
+
+ /* If the channel is element synchronized, deactivate it */
+ if (min_elems == elements[omap_dma_intr_element_sync])
+ omap_dma_deactivate_channel(s, ch);
+
+ /* If it is the last frame, set the LAST_FRAME interrupt */
+ if (min_elems == elements[omap_dma_intr_last_frame])
+ ch->status |= LAST_FRAME_INTR;
+
+ /* If exactly half of the frame was reached, set the HALF_FRAME
+ interrupt */
+ if (min_elems == elements[omap_dma_intr_half_frame])
+ ch->status |= HALF_FRAME_INTR;
+
+ /* If a full packet has been transferred, set the END_PKT interrupt */
+ if (min_elems == elements[omap_dma_intr_packet])
+ ch->status |= END_PKT_INTR;
+
+ /* If the channel is packet-synchronized, deactivate it */
+ if (min_elems == elements[omap_dma_intr_packet_sync])
+ omap_dma_deactivate_channel(s, ch);
+
+ /* If the channel is frame synchronized, deactivate it */
+ if (min_elems == elements[omap_dma_intr_frame_sync])
+ omap_dma_deactivate_channel(s, ch);
+
+ /* Set the END_FRAME interrupt */
+ if (min_elems == elements[omap_dma_intr_frame])
+ ch->status |= END_FRAME_INTR;
+
+ if (min_elems == elements[omap_dma_intr_block]) {
+ /* End of Block */
+ /* Disable the channel */
+
+ if (ch->omap_3_1_compatible_disable) {
+ omap_dma_disable_channel(s, ch);
+ if (ch->link_enabled)
+ omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]);
+ } else {
+ if (!ch->auto_init)
+ omap_dma_disable_channel(s, ch);
+ else if (ch->repeat || ch->end_prog)
+ omap_dma_channel_load(ch);
+ else {
+ ch->waiting_end_prog = 1;
+ omap_dma_deactivate_channel(s, ch);
+ }
+ }
+
+ if (ch->interrupts & END_BLOCK_INTR)
+ ch->status |= END_BLOCK_INTR;
+ }
+
+ /* Update packet number */
+ if (ch->fs && ch->bs) {
+ a->pck_element += min_elems;
+ a->pck_element %= a->pck_elements;
+ }
+
+ /* TODO: check if we really need to update anything here or perhaps we
+ * can skip part of this. */
+#ifndef MULTI_REQ
+ if (dma->update) {
+#endif
+ a->element += min_elems;
+
+ frames = a->element / a->elements;
+ a->element = a->element % a->elements;
+ a->frame += frames;
+ a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0];
+ a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1];
+
+ /* If the channel is async, update cpc */
+ if (!ch->sync && frames)
+ ch->cpc = a->dest & 0xffff;
+
+ /* TODO: if the destination port is IMIF or EMIFF, set the dirty
+ * bits on it. */
+#ifndef MULTI_REQ
+ }
+#else
+ }
+#endif
+
+ omap_dma_interrupts_update(s);
+}
+
+void omap_dma_reset(struct soc_dma_s *dma)
+{
+ int i;
+ struct omap_dma_s *s = dma->opaque;
+
+ soc_dma_reset(s->dma);
+ if (s->model < omap_dma_4)
+ s->gcr = 0x0004;
+ else
+ s->gcr = 0x00010010;
+ s->ocp = 0x00000000;
+ memset(&s->irqstat, 0, sizeof(s->irqstat));
+ memset(&s->irqen, 0, sizeof(s->irqen));
+ s->lcd_ch.src = emiff;
+ s->lcd_ch.condition = 0;
+ s->lcd_ch.interrupts = 0;
+ s->lcd_ch.dual = 0;
+ if (s->model < omap_dma_4)
+ omap_dma_enable_3_1_mapping(s);
+ for (i = 0; i < s->chans; i ++) {
+ s->ch[i].suspend = 0;
+ s->ch[i].prefetch = 0;
+ s->ch[i].buf_disable = 0;
+ s->ch[i].src_sync = 0;
+ memset(&s->ch[i].burst, 0, sizeof(s->ch[i].burst));
+ memset(&s->ch[i].port, 0, sizeof(s->ch[i].port));
+ memset(&s->ch[i].mode, 0, sizeof(s->ch[i].mode));
+ memset(&s->ch[i].frame_index, 0, sizeof(s->ch[i].frame_index));
+ memset(&s->ch[i].element_index, 0, sizeof(s->ch[i].element_index));
+ memset(&s->ch[i].endian, 0, sizeof(s->ch[i].endian));
+ memset(&s->ch[i].endian_lock, 0, sizeof(s->ch[i].endian_lock));
+ memset(&s->ch[i].translate, 0, sizeof(s->ch[i].translate));
+ s->ch[i].write_mode = 0;
+ s->ch[i].data_type = 0;
+ s->ch[i].transparent_copy = 0;
+ s->ch[i].constant_fill = 0;
+ s->ch[i].color = 0x00000000;
+ s->ch[i].end_prog = 0;
+ s->ch[i].repeat = 0;
+ s->ch[i].auto_init = 0;
+ s->ch[i].link_enabled = 0;
+ if (s->model < omap_dma_4)
+ s->ch[i].interrupts = 0x0003;
+ else
+ s->ch[i].interrupts = 0x0000;
+ s->ch[i].status = 0;
+ s->ch[i].cstatus = 0;
+ s->ch[i].active = 0;
+ s->ch[i].enable = 0;
+ s->ch[i].sync = 0;
+ s->ch[i].pending_request = 0;
+ s->ch[i].waiting_end_prog = 0;
+ s->ch[i].cpc = 0x0000;
+ s->ch[i].fs = 0;
+ s->ch[i].bs = 0;
+ s->ch[i].omap_3_1_compatible_disable = 0;
+ memset(&s->ch[i].active_set, 0, sizeof(s->ch[i].active_set));
+ s->ch[i].priority = 0;
+ s->ch[i].interleave_disabled = 0;
+ s->ch[i].type = 0;
+ }
+}
+
+static int omap_dma_ch_reg_read(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch, int reg, uint16_t *value)
+{
+ switch (reg) {
+ case 0x00: /* SYS_DMA_CSDP_CH0 */
+ *value = (ch->burst[1] << 14) |
+ (ch->pack[1] << 13) |
+ (ch->port[1] << 9) |
+ (ch->burst[0] << 7) |
+ (ch->pack[0] << 6) |
+ (ch->port[0] << 2) |
+ (ch->data_type >> 1);
+ break;
+
+ case 0x02: /* SYS_DMA_CCR_CH0 */
+ if (s->model <= omap_dma_3_1)
+ *value = 0 << 10; /* FIFO_FLUSH reads as 0 */
+ else
+ *value = ch->omap_3_1_compatible_disable << 10;
+ *value |= (ch->mode[1] << 14) |
+ (ch->mode[0] << 12) |
+ (ch->end_prog << 11) |
+ (ch->repeat << 9) |
+ (ch->auto_init << 8) |
+ (ch->enable << 7) |
+ (ch->priority << 6) |
+ (ch->fs << 5) | ch->sync;
+ break;
+
+ case 0x04: /* SYS_DMA_CICR_CH0 */
+ *value = ch->interrupts;
+ break;
+
+ case 0x06: /* SYS_DMA_CSR_CH0 */
+ *value = ch->status;
+ ch->status &= SYNC;
+ if (!ch->omap_3_1_compatible_disable && ch->sibling) {
+ *value |= (ch->sibling->status & 0x3f) << 6;
+ ch->sibling->status &= SYNC;
+ }
+ qemu_irq_lower(ch->irq);
+ break;
+
+ case 0x08: /* SYS_DMA_CSSA_L_CH0 */
+ *value = ch->addr[0] & 0x0000ffff;
+ break;
+
+ case 0x0a: /* SYS_DMA_CSSA_U_CH0 */
+ *value = ch->addr[0] >> 16;
+ break;
+
+ case 0x0c: /* SYS_DMA_CDSA_L_CH0 */
+ *value = ch->addr[1] & 0x0000ffff;
+ break;
+
+ case 0x0e: /* SYS_DMA_CDSA_U_CH0 */
+ *value = ch->addr[1] >> 16;
+ break;
+
+ case 0x10: /* SYS_DMA_CEN_CH0 */
+ *value = ch->elements;
+ break;
+
+ case 0x12: /* SYS_DMA_CFN_CH0 */
+ *value = ch->frames;
+ break;
+
+ case 0x14: /* SYS_DMA_CFI_CH0 */
+ *value = ch->frame_index[0];
+ break;
+
+ case 0x16: /* SYS_DMA_CEI_CH0 */
+ *value = ch->element_index[0];
+ break;
+
+ case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */
+ if (ch->omap_3_1_compatible_disable)
+ *value = ch->active_set.src & 0xffff; /* CSAC */
+ else
+ *value = ch->cpc;
+ break;
+
+ case 0x1a: /* DMA_CDAC */
+ *value = ch->active_set.dest & 0xffff; /* CDAC */
+ break;
+
+ case 0x1c: /* DMA_CDEI */
+ *value = ch->element_index[1];
+ break;
+
+ case 0x1e: /* DMA_CDFI */
+ *value = ch->frame_index[1];
+ break;
+
+ case 0x20: /* DMA_COLOR_L */
+ *value = ch->color & 0xffff;
+ break;
+
+ case 0x22: /* DMA_COLOR_U */
+ *value = ch->color >> 16;
+ break;
+
+ case 0x24: /* DMA_CCR2 */
+ *value = (ch->bs << 2) |
+ (ch->transparent_copy << 1) |
+ ch->constant_fill;
+ break;
+
+ case 0x28: /* DMA_CLNK_CTRL */
+ *value = (ch->link_enabled << 15) |
+ (ch->link_next_ch & 0xf);
+ break;
+
+ case 0x2a: /* DMA_LCH_CTRL */
+ *value = (ch->interleave_disabled << 15) |
+ ch->type;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_ch_reg_write(struct omap_dma_s *s,
+ struct omap_dma_channel_s *ch, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* SYS_DMA_CSDP_CH0 */
+ ch->burst[1] = (value & 0xc000) >> 14;
+ ch->pack[1] = (value & 0x2000) >> 13;
+ ch->port[1] = (enum omap_dma_port) ((value & 0x1e00) >> 9);
+ ch->burst[0] = (value & 0x0180) >> 7;
+ ch->pack[0] = (value & 0x0040) >> 6;
+ ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2);
+ ch->data_type = 1 << (value & 3);
+ if (ch->port[0] >= __omap_dma_port_last)
+ printf("%s: invalid DMA port %i\n", __FUNCTION__,
+ ch->port[0]);
+ if (ch->port[1] >= __omap_dma_port_last)
+ printf("%s: invalid DMA port %i\n", __FUNCTION__,
+ ch->port[1]);
+ if ((value & 3) == 3)
+ printf("%s: bad data_type for DMA channel\n", __FUNCTION__);
+ break;
+
+ case 0x02: /* SYS_DMA_CCR_CH0 */
+ ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14);
+ ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12);
+ ch->end_prog = (value & 0x0800) >> 11;
+ if (s->model >= omap_dma_3_2)
+ ch->omap_3_1_compatible_disable = (value >> 10) & 0x1;
+ ch->repeat = (value & 0x0200) >> 9;
+ ch->auto_init = (value & 0x0100) >> 8;
+ ch->priority = (value & 0x0040) >> 6;
+ ch->fs = (value & 0x0020) >> 5;
+ ch->sync = value & 0x001f;
+
+ if (value & 0x0080)
+ omap_dma_enable_channel(s, ch);
+ else
+ omap_dma_disable_channel(s, ch);
+
+ if (ch->end_prog)
+ omap_dma_channel_end_prog(s, ch);
+
+ break;
+
+ case 0x04: /* SYS_DMA_CICR_CH0 */
+ ch->interrupts = value & 0x3f;
+ break;
+
+ case 0x06: /* SYS_DMA_CSR_CH0 */
+ OMAP_RO_REG((target_phys_addr_t) reg);
+ break;
+
+ case 0x08: /* SYS_DMA_CSSA_L_CH0 */
+ ch->addr[0] &= 0xffff0000;
+ ch->addr[0] |= value;
+ break;
+
+ case 0x0a: /* SYS_DMA_CSSA_U_CH0 */
+ ch->addr[0] &= 0x0000ffff;
+ ch->addr[0] |= (uint32_t) value << 16;
+ break;
+
+ case 0x0c: /* SYS_DMA_CDSA_L_CH0 */
+ ch->addr[1] &= 0xffff0000;
+ ch->addr[1] |= value;
+ break;
+
+ case 0x0e: /* SYS_DMA_CDSA_U_CH0 */
+ ch->addr[1] &= 0x0000ffff;
+ ch->addr[1] |= (uint32_t) value << 16;
+ break;
+
+ case 0x10: /* SYS_DMA_CEN_CH0 */
+ ch->elements = value;
+ break;
+
+ case 0x12: /* SYS_DMA_CFN_CH0 */
+ ch->frames = value;
+ break;
+
+ case 0x14: /* SYS_DMA_CFI_CH0 */
+ ch->frame_index[0] = (int16_t) value;
+ break;
+
+ case 0x16: /* SYS_DMA_CEI_CH0 */
+ ch->element_index[0] = (int16_t) value;
+ break;
+
+ case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */
+ OMAP_RO_REG((target_phys_addr_t) reg);
+ break;
+
+ case 0x1c: /* DMA_CDEI */
+ ch->element_index[1] = (int16_t) value;
+ break;
+
+ case 0x1e: /* DMA_CDFI */
+ ch->frame_index[1] = (int16_t) value;
+ break;
+
+ case 0x20: /* DMA_COLOR_L */
+ ch->color &= 0xffff0000;
+ ch->color |= value;
+ break;
+
+ case 0x22: /* DMA_COLOR_U */
+ ch->color &= 0xffff;
+ ch->color |= value << 16;
+ break;
+
+ case 0x24: /* DMA_CCR2 */
+ ch->bs = (value >> 2) & 0x1;
+ ch->transparent_copy = (value >> 1) & 0x1;
+ ch->constant_fill = value & 0x1;
+ break;
+
+ case 0x28: /* DMA_CLNK_CTRL */
+ ch->link_enabled = (value >> 15) & 0x1;
+ if (value & (1 << 14)) { /* Stop_Lnk */
+ ch->link_enabled = 0;
+ omap_dma_disable_channel(s, ch);
+ }
+ ch->link_next_ch = value & 0x1f;
+ break;
+
+ case 0x2a: /* DMA_LCH_CTRL */
+ ch->interleave_disabled = (value >> 15) & 0x1;
+ ch->type = value & 0xf;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset,
+ uint16_t value)
+{
+ switch (offset) {
+ case 0xbc0: /* DMA_LCD_CSDP */
+ s->brust_f2 = (value >> 14) & 0x3;
+ s->pack_f2 = (value >> 13) & 0x1;
+ s->data_type_f2 = (1 << ((value >> 11) & 0x3));
+ s->brust_f1 = (value >> 7) & 0x3;
+ s->pack_f1 = (value >> 6) & 0x1;
+ s->data_type_f1 = (1 << ((value >> 0) & 0x3));
+ break;
+
+ case 0xbc2: /* DMA_LCD_CCR */
+ s->mode_f2 = (value >> 14) & 0x3;
+ s->mode_f1 = (value >> 12) & 0x3;
+ s->end_prog = (value >> 11) & 0x1;
+ s->omap_3_1_compatible_disable = (value >> 10) & 0x1;
+ s->repeat = (value >> 9) & 0x1;
+ s->auto_init = (value >> 8) & 0x1;
+ s->running = (value >> 7) & 0x1;
+ s->priority = (value >> 6) & 0x1;
+ s->bs = (value >> 4) & 0x1;
+ break;
+
+ case 0xbc4: /* DMA_LCD_CTRL */
+ s->dst = (value >> 8) & 0x1;
+ s->src = ((value >> 6) & 0x3) << 1;
+ s->condition = 0;
+ /* Assume no bus errors and thus no BUS_ERROR irq bits. */
+ s->interrupts = (value >> 1) & 1;
+ s->dual = value & 1;
+ break;
+
+ case 0xbc8: /* TOP_B1_L */
+ s->src_f1_top &= 0xffff0000;
+ s->src_f1_top |= 0x0000ffff & value;
+ break;
+
+ case 0xbca: /* TOP_B1_U */
+ s->src_f1_top &= 0x0000ffff;
+ s->src_f1_top |= value << 16;
+ break;
+
+ case 0xbcc: /* BOT_B1_L */
+ s->src_f1_bottom &= 0xffff0000;
+ s->src_f1_bottom |= 0x0000ffff & value;
+ break;
+
+ case 0xbce: /* BOT_B1_U */
+ s->src_f1_bottom &= 0x0000ffff;
+ s->src_f1_bottom |= (uint32_t) value << 16;
+ break;
+
+ case 0xbd0: /* TOP_B2_L */
+ s->src_f2_top &= 0xffff0000;
+ s->src_f2_top |= 0x0000ffff & value;
+ break;
+
+ case 0xbd2: /* TOP_B2_U */
+ s->src_f2_top &= 0x0000ffff;
+ s->src_f2_top |= (uint32_t) value << 16;
+ break;
+
+ case 0xbd4: /* BOT_B2_L */
+ s->src_f2_bottom &= 0xffff0000;
+ s->src_f2_bottom |= 0x0000ffff & value;
+ break;
+
+ case 0xbd6: /* BOT_B2_U */
+ s->src_f2_bottom &= 0x0000ffff;
+ s->src_f2_bottom |= (uint32_t) value << 16;
+ break;
+
+ case 0xbd8: /* DMA_LCD_SRC_EI_B1 */
+ s->element_index_f1 = value;
+ break;
+
+ case 0xbda: /* DMA_LCD_SRC_FI_B1_L */
+ s->frame_index_f1 &= 0xffff0000;
+ s->frame_index_f1 |= 0x0000ffff & value;
+ break;
+
+ case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */
+ s->frame_index_f1 &= 0x0000ffff;
+ s->frame_index_f1 |= (uint32_t) value << 16;
+ break;
+
+ case 0xbdc: /* DMA_LCD_SRC_EI_B2 */
+ s->element_index_f2 = value;
+ break;
+
+ case 0xbde: /* DMA_LCD_SRC_FI_B2_L */
+ s->frame_index_f2 &= 0xffff0000;
+ s->frame_index_f2 |= 0x0000ffff & value;
+ break;
+
+ case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */
+ s->frame_index_f2 &= 0x0000ffff;
+ s->frame_index_f2 |= (uint32_t) value << 16;
+ break;
+
+ case 0xbe0: /* DMA_LCD_SRC_EN_B1 */
+ s->elements_f1 = value;
+ break;
+
+ case 0xbe4: /* DMA_LCD_SRC_FN_B1 */
+ s->frames_f1 = value;
+ break;
+
+ case 0xbe2: /* DMA_LCD_SRC_EN_B2 */
+ s->elements_f2 = value;
+ break;
+
+ case 0xbe6: /* DMA_LCD_SRC_FN_B2 */
+ s->frames_f2 = value;
+ break;
+
+ case 0xbea: /* DMA_LCD_LCH_CTRL */
+ s->lch_type = value & 0xf;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset,
+ uint16_t *ret)
+{
+ switch (offset) {
+ case 0xbc0: /* DMA_LCD_CSDP */
+ *ret = (s->brust_f2 << 14) |
+ (s->pack_f2 << 13) |
+ ((s->data_type_f2 >> 1) << 11) |
+ (s->brust_f1 << 7) |
+ (s->pack_f1 << 6) |
+ ((s->data_type_f1 >> 1) << 0);
+ break;
+
+ case 0xbc2: /* DMA_LCD_CCR */
+ *ret = (s->mode_f2 << 14) |
+ (s->mode_f1 << 12) |
+ (s->end_prog << 11) |
+ (s->omap_3_1_compatible_disable << 10) |
+ (s->repeat << 9) |
+ (s->auto_init << 8) |
+ (s->running << 7) |
+ (s->priority << 6) |
+ (s->bs << 4);
+ break;
+
+ case 0xbc4: /* DMA_LCD_CTRL */
+ qemu_irq_lower(s->irq);
+ *ret = (s->dst << 8) |
+ ((s->src & 0x6) << 5) |
+ (s->condition << 3) |
+ (s->interrupts << 1) |
+ s->dual;
+ break;
+
+ case 0xbc8: /* TOP_B1_L */
+ *ret = s->src_f1_top & 0xffff;
+ break;
+
+ case 0xbca: /* TOP_B1_U */
+ *ret = s->src_f1_top >> 16;
+ break;
+
+ case 0xbcc: /* BOT_B1_L */
+ *ret = s->src_f1_bottom & 0xffff;
+ break;
+
+ case 0xbce: /* BOT_B1_U */
+ *ret = s->src_f1_bottom >> 16;
+ break;
+
+ case 0xbd0: /* TOP_B2_L */
+ *ret = s->src_f2_top & 0xffff;
+ break;
+
+ case 0xbd2: /* TOP_B2_U */
+ *ret = s->src_f2_top >> 16;
+ break;
+
+ case 0xbd4: /* BOT_B2_L */
+ *ret = s->src_f2_bottom & 0xffff;
+ break;
+
+ case 0xbd6: /* BOT_B2_U */
+ *ret = s->src_f2_bottom >> 16;
+ break;
+
+ case 0xbd8: /* DMA_LCD_SRC_EI_B1 */
+ *ret = s->element_index_f1;
+ break;
+
+ case 0xbda: /* DMA_LCD_SRC_FI_B1_L */
+ *ret = s->frame_index_f1 & 0xffff;
+ break;
+
+ case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */
+ *ret = s->frame_index_f1 >> 16;
+ break;
+
+ case 0xbdc: /* DMA_LCD_SRC_EI_B2 */
+ *ret = s->element_index_f2;
+ break;
+
+ case 0xbde: /* DMA_LCD_SRC_FI_B2_L */
+ *ret = s->frame_index_f2 & 0xffff;
+ break;
+
+ case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */
+ *ret = s->frame_index_f2 >> 16;
+ break;
+
+ case 0xbe0: /* DMA_LCD_SRC_EN_B1 */
+ *ret = s->elements_f1;
+ break;
+
+ case 0xbe4: /* DMA_LCD_SRC_FN_B1 */
+ *ret = s->frames_f1;
+ break;
+
+ case 0xbe2: /* DMA_LCD_SRC_EN_B2 */
+ *ret = s->elements_f2;
+ break;
+
+ case 0xbe6: /* DMA_LCD_SRC_FN_B2 */
+ *ret = s->frames_f2;
+ break;
+
+ case 0xbea: /* DMA_LCD_LCH_CTRL */
+ *ret = s->lch_type;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset,
+ uint16_t value)
+{
+ switch (offset) {
+ case 0x300: /* SYS_DMA_LCD_CTRL */
+ s->src = (value & 0x40) ? imif : emiff;
+ s->condition = 0;
+ /* Assume no bus errors and thus no BUS_ERROR irq bits. */
+ s->interrupts = (value >> 1) & 1;
+ s->dual = value & 1;
+ break;
+
+ case 0x302: /* SYS_DMA_LCD_TOP_F1_L */
+ s->src_f1_top &= 0xffff0000;
+ s->src_f1_top |= 0x0000ffff & value;
+ break;
+
+ case 0x304: /* SYS_DMA_LCD_TOP_F1_U */
+ s->src_f1_top &= 0x0000ffff;
+ s->src_f1_top |= value << 16;
+ break;
+
+ case 0x306: /* SYS_DMA_LCD_BOT_F1_L */
+ s->src_f1_bottom &= 0xffff0000;
+ s->src_f1_bottom |= 0x0000ffff & value;
+ break;
+
+ case 0x308: /* SYS_DMA_LCD_BOT_F1_U */
+ s->src_f1_bottom &= 0x0000ffff;
+ s->src_f1_bottom |= value << 16;
+ break;
+
+ case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */
+ s->src_f2_top &= 0xffff0000;
+ s->src_f2_top |= 0x0000ffff & value;
+ break;
+
+ case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */
+ s->src_f2_top &= 0x0000ffff;
+ s->src_f2_top |= value << 16;
+ break;
+
+ case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */
+ s->src_f2_bottom &= 0xffff0000;
+ s->src_f2_bottom |= 0x0000ffff & value;
+ break;
+
+ case 0x310: /* SYS_DMA_LCD_BOT_F2_U */
+ s->src_f2_bottom &= 0x0000ffff;
+ s->src_f2_bottom |= value << 16;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset,
+ uint16_t *ret)
+{
+ int i;
+
+ switch (offset) {
+ case 0x300: /* SYS_DMA_LCD_CTRL */
+ i = s->condition;
+ s->condition = 0;
+ qemu_irq_lower(s->irq);
+ *ret = ((s->src == imif) << 6) | (i << 3) |
+ (s->interrupts << 1) | s->dual;
+ break;
+
+ case 0x302: /* SYS_DMA_LCD_TOP_F1_L */
+ *ret = s->src_f1_top & 0xffff;
+ break;
+
+ case 0x304: /* SYS_DMA_LCD_TOP_F1_U */
+ *ret = s->src_f1_top >> 16;
+ break;
+
+ case 0x306: /* SYS_DMA_LCD_BOT_F1_L */
+ *ret = s->src_f1_bottom & 0xffff;
+ break;
+
+ case 0x308: /* SYS_DMA_LCD_BOT_F1_U */
+ *ret = s->src_f1_bottom >> 16;
+ break;
+
+ case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */
+ *ret = s->src_f2_top & 0xffff;
+ break;
+
+ case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */
+ *ret = s->src_f2_top >> 16;
+ break;
+
+ case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */
+ *ret = s->src_f2_bottom & 0xffff;
+ break;
+
+ case 0x310: /* SYS_DMA_LCD_BOT_F2_U */
+ *ret = s->src_f2_bottom >> 16;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value)
+{
+ switch (offset) {
+ case 0x400: /* SYS_DMA_GCR */
+ s->gcr = value;
+ break;
+
+ case 0x404: /* DMA_GSCR */
+ if (value & 0x8)
+ omap_dma_disable_3_1_mapping(s);
+ else
+ omap_dma_enable_3_1_mapping(s);
+ break;
+
+ case 0x408: /* DMA_GRST */
+ if (value & 0x1)
+ omap_dma_reset(s->dma);
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int omap_dma_sys_read(struct omap_dma_s *s, int offset,
+ uint16_t *ret)
+{
+ switch (offset) {
+ case 0x400: /* SYS_DMA_GCR */
+ *ret = s->gcr;
+ break;
+
+ case 0x404: /* DMA_GSCR */
+ *ret = s->omap_3_1_mapping_disabled << 3;
+ break;
+
+ case 0x408: /* DMA_GRST */
+ *ret = 0;
+ break;
+
+ case 0x442: /* DMA_HW_ID */
+ case 0x444: /* DMA_PCh2_ID */
+ case 0x446: /* DMA_PCh0_ID */
+ case 0x448: /* DMA_PCh1_ID */
+ case 0x44a: /* DMA_PChG_ID */
+ case 0x44c: /* DMA_PChD_ID */
+ *ret = 1;
+ break;
+
+ case 0x44e: /* DMA_CAPS_0_U */
+ *ret = (s->caps[0] >> 16) & 0xffff;
+ break;
+ case 0x450: /* DMA_CAPS_0_L */
+ *ret = (s->caps[0] >> 0) & 0xffff;
+ break;
+
+ case 0x452: /* DMA_CAPS_1_U */
+ *ret = (s->caps[1] >> 16) & 0xffff;
+ break;
+ case 0x454: /* DMA_CAPS_1_L */
+ *ret = (s->caps[1] >> 0) & 0xffff;
+ break;
+
+ case 0x456: /* DMA_CAPS_2 */
+ *ret = s->caps[2];
+ break;
+
+ case 0x458: /* DMA_CAPS_3 */
+ *ret = s->caps[3];
+ break;
+
+ case 0x45a: /* DMA_CAPS_4 */
+ *ret = s->caps[4];
+ break;
+
+ case 0x460: /* DMA_PCh2_SR */
+ case 0x480: /* DMA_PCh0_SR */
+ case 0x482: /* DMA_PCh1_SR */
+ case 0x4c0: /* DMA_PChD_SR_0 */
+ printf("%s: Physical Channel Status Registers not implemented.\n",
+ __FUNCTION__);
+ *ret = 0xff;
+ break;
+
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static uint32_t omap_dma_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+ int reg, ch;
+ uint16_t ret;
+
+ switch (addr) {
+ case 0x300 ... 0x3fe:
+ if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) {
+ if (omap_dma_3_1_lcd_read(&s->lcd_ch, addr, &ret))
+ break;
+ return ret;
+ }
+ /* Fall through. */
+ case 0x000 ... 0x2fe:
+ reg = addr & 0x3f;
+ ch = (addr >> 6) & 0x0f;
+ if (omap_dma_ch_reg_read(s, &s->ch[ch], reg, &ret))
+ break;
+ return ret;
+
+ case 0x404 ... 0x4fe:
+ if (s->model <= omap_dma_3_1)
+ break;
+ /* Fall through. */
+ case 0x400:
+ if (omap_dma_sys_read(s, addr, &ret))
+ break;
+ return ret;
+
+ case 0xb00 ... 0xbfe:
+ if (s->model == omap_dma_3_2 && s->omap_3_1_mapping_disabled) {
+ if (omap_dma_3_2_lcd_read(&s->lcd_ch, addr, &ret))
+ break;
+ return ret;
+ }
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_dma_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+ int reg, ch;
+
+ switch (addr) {
+ case 0x300 ... 0x3fe:
+ if (s->model <= omap_dma_3_1 || !s->omap_3_1_mapping_disabled) {
+ if (omap_dma_3_1_lcd_write(&s->lcd_ch, addr, value))
+ break;
+ return;
+ }
+ /* Fall through. */
+ case 0x000 ... 0x2fe:
+ reg = addr & 0x3f;
+ ch = (addr >> 6) & 0x0f;
+ if (omap_dma_ch_reg_write(s, &s->ch[ch], reg, value))
+ break;
+ return;
+
+ case 0x404 ... 0x4fe:
+ if (s->model <= omap_dma_3_1)
+ break;
+ case 0x400:
+ /* Fall through. */
+ if (omap_dma_sys_write(s, addr, value))
+ break;
+ return;
+
+ case 0xb00 ... 0xbfe:
+ if (s->model == omap_dma_3_2 && s->omap_3_1_mapping_disabled) {
+ if (omap_dma_3_2_lcd_write(&s->lcd_ch, addr, value))
+ break;
+ return;
+ }
+ break;
+ }
+
+ OMAP_BAD_REG(addr);
+}
+
+static CPUReadMemoryFunc * const omap_dma_readfn[] = {
+ omap_badwidth_read16,
+ omap_dma_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_dma_writefn[] = {
+ omap_badwidth_write16,
+ omap_dma_write,
+ omap_badwidth_write16,
+};
+
+static void omap_dma_request(void *opaque, int drq, int req)
+{
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+ /* The request pins are level triggered in QEMU. */
+ if (req) {
+ if (~s->dma->drqbmp & (1 << drq)) {
+ s->dma->drqbmp |= 1 << drq;
+ omap_dma_process_request(s, drq);
+ }
+ } else
+ s->dma->drqbmp &= ~(1 << drq);
+}
+
+/* XXX: this won't be needed once soc_dma knows about clocks. */
+static void omap_dma_clk_update(void *opaque, int line, int on)
+{
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+ int i;
+
+ s->dma->freq = omap_clk_getrate(s->clk);
+
+ for (i = 0; i < s->chans; i ++)
+ if (s->ch[i].active)
+ soc_dma_set_request(s->ch[i].dma, on);
+}
+
+static void omap_dma_setcaps(struct omap_dma_s *s)
+{
+ switch (s->model) {
+ default:
+ case omap_dma_3_1:
+ break;
+ case omap_dma_3_2:
+ case omap_dma_4:
+ /* XXX Only available for sDMA */
+ s->caps[0] =
+ (1 << 19) | /* Constant Fill Capability */
+ (1 << 18); /* Transparent BLT Capability */
+ s->caps[1] =
+ (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */
+ s->caps[2] =
+ (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */
+ (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */
+ (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */
+ (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */
+ (1 << 4) | /* DST_CONST_ADRS_CPBLTY */
+ (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */
+ (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */
+ (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */
+ (1 << 0); /* SRC_CONST_ADRS_CPBLTY */
+ s->caps[3] =
+ (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */
+ (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */
+ (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */
+ (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */
+ (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */
+ (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */
+ (1 << 1) | /* FRAME_SYNCHR_CPBLTY */
+ (1 << 0); /* ELMNT_SYNCHR_CPBLTY */
+ s->caps[4] =
+ (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */
+ (1 << 6) | /* SYNC_STATUS_CPBLTY */
+ (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */
+ (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */
+ (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */
+ (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */
+ (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */
+ (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */
+ break;
+ }
+}
+
+struct soc_dma_s *omap_dma_init(target_phys_addr_t base, qemu_irq *irqs,
+ qemu_irq lcd_irq, struct omap_mpu_state_s *mpu, omap_clk clk,
+ enum omap_dma_model model)
+{
+ int iomemtype, num_irqs, memsize, i;
+ struct omap_dma_s *s = (struct omap_dma_s *)
+ qemu_mallocz(sizeof(struct omap_dma_s));
+
+ if (model <= omap_dma_3_1) {
+ num_irqs = 6;
+ memsize = 0x800;
+ } else {
+ num_irqs = 16;
+ memsize = 0xc00;
+ }
+ s->model = model;
+ s->mpu = mpu;
+ s->clk = clk;
+ s->lcd_ch.irq = lcd_irq;
+ s->lcd_ch.mpu = mpu;
+
+ s->dma = soc_dma_init((model <= omap_dma_3_1) ? 9 : 16);
+ s->dma->freq = omap_clk_getrate(clk);
+ s->dma->transfer_fn = omap_dma_transfer_generic;
+ s->dma->setup_fn = omap_dma_transfer_setup;
+ s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 32);
+ s->dma->opaque = s;
+
+ while (num_irqs --)
+ s->ch[num_irqs].irq = irqs[num_irqs];
+ for (i = 0; i < 3; i ++) {
+ s->ch[i].sibling = &s->ch[i + 6];
+ s->ch[i + 6].sibling = &s->ch[i];
+ }
+ for (i = (model <= omap_dma_3_1) ? 8 : 15; i >= 0; i --) {
+ s->ch[i].dma = &s->dma->ch[i];
+ s->dma->ch[i].opaque = &s->ch[i];
+ }
+
+ omap_dma_setcaps(s);
+ omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]);
+ omap_dma_reset(s->dma);
+ omap_dma_clk_update(s, 0, 1);
+
+ iomemtype = cpu_register_io_memory(omap_dma_readfn,
+ omap_dma_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, memsize, iomemtype);
+
+ mpu->drq = s->dma->drq;
+
+ return s->dma;
+}
+
+static void omap_dma_interrupts_4_update(struct omap_dma_s *s)
+{
+ struct omap_dma_channel_s *ch = s->ch;
+ uint32_t bmp, bit;
+
+ for (bmp = 0, bit = 1; bit; ch ++, bit <<= 1)
+ if (ch->status) {
+ bmp |= bit;
+ ch->cstatus |= ch->status;
+ ch->status = 0;
+ }
+ if ((s->irqstat[0] |= s->irqen[0] & bmp))
+ qemu_irq_raise(s->irq[0]);
+ if ((s->irqstat[1] |= s->irqen[1] & bmp))
+ qemu_irq_raise(s->irq[1]);
+ if ((s->irqstat[2] |= s->irqen[2] & bmp))
+ qemu_irq_raise(s->irq[2]);
+ if ((s->irqstat[3] |= s->irqen[3] & bmp))
+ qemu_irq_raise(s->irq[3]);
+}
+
+static uint32_t omap_dma4_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+ int irqn = 0, chnum;
+ struct omap_dma_channel_s *ch;
+
+ switch (addr) {
+ case 0x00: /* DMA4_REVISION */
+ return 0x40;
+
+ case 0x14: /* DMA4_IRQSTATUS_L3 */
+ irqn ++;
+ case 0x10: /* DMA4_IRQSTATUS_L2 */
+ irqn ++;
+ case 0x0c: /* DMA4_IRQSTATUS_L1 */
+ irqn ++;
+ case 0x08: /* DMA4_IRQSTATUS_L0 */
+ return s->irqstat[irqn];
+
+ case 0x24: /* DMA4_IRQENABLE_L3 */
+ irqn ++;
+ case 0x20: /* DMA4_IRQENABLE_L2 */
+ irqn ++;
+ case 0x1c: /* DMA4_IRQENABLE_L1 */
+ irqn ++;
+ case 0x18: /* DMA4_IRQENABLE_L0 */
+ return s->irqen[irqn];
+
+ case 0x28: /* DMA4_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x2c: /* DMA4_OCP_SYSCONFIG */
+ return s->ocp;
+
+ case 0x64: /* DMA4_CAPS_0 */
+ return s->caps[0];
+ case 0x6c: /* DMA4_CAPS_2 */
+ return s->caps[2];
+ case 0x70: /* DMA4_CAPS_3 */
+ return s->caps[3];
+ case 0x74: /* DMA4_CAPS_4 */
+ return s->caps[4];
+
+ case 0x78: /* DMA4_GCR */
+ return s->gcr;
+
+ case 0x80 ... 0xfff:
+ addr -= 0x80;
+ chnum = addr / 0x60;
+ ch = s->ch + chnum;
+ addr -= chnum * 0x60;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return 0;
+ }
+
+ /* Per-channel registers */
+ switch (addr) {
+ case 0x00: /* DMA4_CCR */
+ return (ch->buf_disable << 25) |
+ (ch->src_sync << 24) |
+ (ch->prefetch << 23) |
+ ((ch->sync & 0x60) << 14) |
+ (ch->bs << 18) |
+ (ch->transparent_copy << 17) |
+ (ch->constant_fill << 16) |
+ (ch->mode[1] << 14) |
+ (ch->mode[0] << 12) |
+ (0 << 10) | (0 << 9) |
+ (ch->suspend << 8) |
+ (ch->enable << 7) |
+ (ch->priority << 6) |
+ (ch->fs << 5) | (ch->sync & 0x1f);
+
+ case 0x04: /* DMA4_CLNK_CTRL */
+ return (ch->link_enabled << 15) | ch->link_next_ch;
+
+ case 0x08: /* DMA4_CICR */
+ return ch->interrupts;
+
+ case 0x0c: /* DMA4_CSR */
+ return ch->cstatus;
+
+ case 0x10: /* DMA4_CSDP */
+ return (ch->endian[0] << 21) |
+ (ch->endian_lock[0] << 20) |
+ (ch->endian[1] << 19) |
+ (ch->endian_lock[1] << 18) |
+ (ch->write_mode << 16) |
+ (ch->burst[1] << 14) |
+ (ch->pack[1] << 13) |
+ (ch->translate[1] << 9) |
+ (ch->burst[0] << 7) |
+ (ch->pack[0] << 6) |
+ (ch->translate[0] << 2) |
+ (ch->data_type >> 1);
+
+ case 0x14: /* DMA4_CEN */
+ return ch->elements;
+
+ case 0x18: /* DMA4_CFN */
+ return ch->frames;
+
+ case 0x1c: /* DMA4_CSSA */
+ return ch->addr[0];
+
+ case 0x20: /* DMA4_CDSA */
+ return ch->addr[1];
+
+ case 0x24: /* DMA4_CSEI */
+ return ch->element_index[0];
+
+ case 0x28: /* DMA4_CSFI */
+ return ch->frame_index[0];
+
+ case 0x2c: /* DMA4_CDEI */
+ return ch->element_index[1];
+
+ case 0x30: /* DMA4_CDFI */
+ return ch->frame_index[1];
+
+ case 0x34: /* DMA4_CSAC */
+ return ch->active_set.src & 0xffff;
+
+ case 0x38: /* DMA4_CDAC */
+ return ch->active_set.dest & 0xffff;
+
+ case 0x3c: /* DMA4_CCEN */
+ return ch->active_set.element;
+
+ case 0x40: /* DMA4_CCFN */
+ return ch->active_set.frame;
+
+ case 0x44: /* DMA4_COLOR */
+ /* XXX only in sDMA */
+ return ch->color;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return 0;
+ }
+}
+
+static void omap_dma4_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_dma_s *s = (struct omap_dma_s *) opaque;
+ int chnum, irqn = 0;
+ struct omap_dma_channel_s *ch;
+
+ switch (addr) {
+ case 0x14: /* DMA4_IRQSTATUS_L3 */
+ irqn ++;
+ case 0x10: /* DMA4_IRQSTATUS_L2 */
+ irqn ++;
+ case 0x0c: /* DMA4_IRQSTATUS_L1 */
+ irqn ++;
+ case 0x08: /* DMA4_IRQSTATUS_L0 */
+ s->irqstat[irqn] &= ~value;
+ if (!s->irqstat[irqn])
+ qemu_irq_lower(s->irq[irqn]);
+ return;
+
+ case 0x24: /* DMA4_IRQENABLE_L3 */
+ irqn ++;
+ case 0x20: /* DMA4_IRQENABLE_L2 */
+ irqn ++;
+ case 0x1c: /* DMA4_IRQENABLE_L1 */
+ irqn ++;
+ case 0x18: /* DMA4_IRQENABLE_L0 */
+ s->irqen[irqn] = value;
+ return;
+
+ case 0x2c: /* DMA4_OCP_SYSCONFIG */
+ if (value & 2) /* SOFTRESET */
+ omap_dma_reset(s->dma);
+ s->ocp = value & 0x3321;
+ if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */
+ fprintf(stderr, "%s: invalid DMA power mode\n", __FUNCTION__);
+ return;
+
+ case 0x78: /* DMA4_GCR */
+ s->gcr = value & 0x00ff00ff;
+ if ((value & 0xff) == 0x00) /* MAX_CHANNEL_FIFO_DEPTH */
+ fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __FUNCTION__);
+ return;
+
+ case 0x80 ... 0xfff:
+ addr -= 0x80;
+ chnum = addr / 0x60;
+ ch = s->ch + chnum;
+ addr -= chnum * 0x60;
+ break;
+
+ case 0x00: /* DMA4_REVISION */
+ case 0x28: /* DMA4_SYSSTATUS */
+ case 0x64: /* DMA4_CAPS_0 */
+ case 0x6c: /* DMA4_CAPS_2 */
+ case 0x70: /* DMA4_CAPS_3 */
+ case 0x74: /* DMA4_CAPS_4 */
+ OMAP_RO_REG(addr);
+ return;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+
+ /* Per-channel registers */
+ switch (addr) {
+ case 0x00: /* DMA4_CCR */
+ ch->buf_disable = (value >> 25) & 1;
+ ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */
+ if (ch->buf_disable && !ch->src_sync)
+ fprintf(stderr, "%s: Buffering disable is not allowed in "
+ "destination synchronised mode\n", __FUNCTION__);
+ ch->prefetch = (value >> 23) & 1;
+ ch->bs = (value >> 18) & 1;
+ ch->transparent_copy = (value >> 17) & 1;
+ ch->constant_fill = (value >> 16) & 1;
+ ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14);
+ ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12);
+ ch->suspend = (value & 0x0100) >> 8;
+ ch->priority = (value & 0x0040) >> 6;
+ ch->fs = (value & 0x0020) >> 5;
+ if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1])
+ fprintf(stderr, "%s: For a packet transfer at least one port "
+ "must be constant-addressed\n", __FUNCTION__);
+ ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060);
+ /* XXX must be 0x01 for CamDMA */
+
+ if (value & 0x0080)
+ omap_dma_enable_channel(s, ch);
+ else
+ omap_dma_disable_channel(s, ch);
+
+ break;
+
+ case 0x04: /* DMA4_CLNK_CTRL */
+ ch->link_enabled = (value >> 15) & 0x1;
+ ch->link_next_ch = value & 0x1f;
+ break;
+
+ case 0x08: /* DMA4_CICR */
+ ch->interrupts = value & 0x09be;
+ break;
+
+ case 0x0c: /* DMA4_CSR */
+ ch->cstatus &= ~value;
+ break;
+
+ case 0x10: /* DMA4_CSDP */
+ ch->endian[0] =(value >> 21) & 1;
+ ch->endian_lock[0] =(value >> 20) & 1;
+ ch->endian[1] =(value >> 19) & 1;
+ ch->endian_lock[1] =(value >> 18) & 1;
+ if (ch->endian[0] != ch->endian[1])
+ fprintf(stderr, "%s: DMA endiannes conversion enable attempt\n",
+ __FUNCTION__);
+ ch->write_mode = (value >> 16) & 3;
+ ch->burst[1] = (value & 0xc000) >> 14;
+ ch->pack[1] = (value & 0x2000) >> 13;
+ ch->translate[1] = (value & 0x1e00) >> 9;
+ ch->burst[0] = (value & 0x0180) >> 7;
+ ch->pack[0] = (value & 0x0040) >> 6;
+ ch->translate[0] = (value & 0x003c) >> 2;
+ if (ch->translate[0] | ch->translate[1])
+ fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n",
+ __FUNCTION__);
+ ch->data_type = 1 << (value & 3);
+ if ((value & 3) == 3)
+ printf("%s: bad data_type for DMA channel\n", __FUNCTION__);
+ break;
+
+ case 0x14: /* DMA4_CEN */
+ ch->set_update = 1;
+ ch->elements = value & 0xffffff;
+ break;
+
+ case 0x18: /* DMA4_CFN */
+ ch->frames = value & 0xffff;
+ ch->set_update = 1;
+ break;
+
+ case 0x1c: /* DMA4_CSSA */
+ ch->addr[0] = (target_phys_addr_t) (uint32_t) value;
+ ch->set_update = 1;
+ break;
+
+ case 0x20: /* DMA4_CDSA */
+ ch->addr[1] = (target_phys_addr_t) (uint32_t) value;
+ ch->set_update = 1;
+ break;
+
+ case 0x24: /* DMA4_CSEI */
+ ch->element_index[0] = (int16_t) value;
+ ch->set_update = 1;
+ break;
+
+ case 0x28: /* DMA4_CSFI */
+ ch->frame_index[0] = (int32_t) value;
+ ch->set_update = 1;
+ break;
+
+ case 0x2c: /* DMA4_CDEI */
+ ch->element_index[1] = (int16_t) value;
+ ch->set_update = 1;
+ break;
+
+ case 0x30: /* DMA4_CDFI */
+ ch->frame_index[1] = (int32_t) value;
+ ch->set_update = 1;
+ break;
+
+ case 0x44: /* DMA4_COLOR */
+ /* XXX only in sDMA */
+ ch->color = value;
+ break;
+
+ case 0x34: /* DMA4_CSAC */
+ case 0x38: /* DMA4_CDAC */
+ case 0x3c: /* DMA4_CCEN */
+ case 0x40: /* DMA4_CCFN */
+ OMAP_RO_REG(addr);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_dma4_readfn[] = {
+ omap_badwidth_read16,
+ omap_dma4_read,
+ omap_dma4_read,
+};
+
+static CPUWriteMemoryFunc * const omap_dma4_writefn[] = {
+ omap_badwidth_write16,
+ omap_dma4_write,
+ omap_dma4_write,
+};
+
+struct soc_dma_s *omap_dma4_init(target_phys_addr_t base, qemu_irq *irqs,
+ struct omap_mpu_state_s *mpu, int fifo,
+ int chans, omap_clk iclk, omap_clk fclk)
+{
+ int iomemtype, i;
+ struct omap_dma_s *s = (struct omap_dma_s *)
+ qemu_mallocz(sizeof(struct omap_dma_s));
+
+ s->model = omap_dma_4;
+ s->chans = chans;
+ s->mpu = mpu;
+ s->clk = fclk;
+
+ s->dma = soc_dma_init(s->chans);
+ s->dma->freq = omap_clk_getrate(fclk);
+ s->dma->transfer_fn = omap_dma_transfer_generic;
+ s->dma->setup_fn = omap_dma_transfer_setup;
+ s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 64);
+ s->dma->opaque = s;
+ for (i = 0; i < s->chans; i ++) {
+ s->ch[i].dma = &s->dma->ch[i];
+ s->dma->ch[i].opaque = &s->ch[i];
+ }
+
+ memcpy(&s->irq, irqs, sizeof(s->irq));
+ s->intr_update = omap_dma_interrupts_4_update;
+
+ omap_dma_setcaps(s);
+ omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]);
+ omap_dma_reset(s->dma);
+ omap_dma_clk_update(s, 0, !!s->dma->freq);
+
+ iomemtype = cpu_register_io_memory(omap_dma4_readfn,
+ omap_dma4_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x1000, iomemtype);
+
+ mpu->drq = s->dma->drq;
+
+ return s->dma;
+}
+
+struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct soc_dma_s *dma)
+{
+ struct omap_dma_s *s = dma->opaque;
+
+ return &s->lcd_ch;
+}
diff --git a/hw/omap_dss.c b/hw/omap_dss.c
new file mode 100644
index 0000000..afe287a
--- /dev/null
+++ b/hw/omap_dss.c
@@ -0,0 +1,1068 @@
+/*
+ * OMAP2 Display Subsystem.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "console.h"
+#include "omap.h"
+
+struct omap_dss_s {
+ qemu_irq irq;
+ qemu_irq drq;
+ DisplayState *state;
+
+ int autoidle;
+ int control;
+ int enable;
+
+ struct omap_dss_panel_s {
+ int enable;
+ int nx;
+ int ny;
+
+ int x;
+ int y;
+ } dig, lcd;
+
+ struct {
+ uint32_t idlemode;
+ uint32_t irqst;
+ uint32_t irqen;
+ uint32_t control;
+ uint32_t config;
+ uint32_t capable;
+ uint32_t timing[4];
+ int line;
+ uint32_t bg[2];
+ uint32_t trans[2];
+
+ struct omap_dss_plane_s {
+ int enable;
+ int bpp;
+ int posx;
+ int posy;
+ int nx;
+ int ny;
+
+ target_phys_addr_t addr[3];
+
+ uint32_t attr;
+ uint32_t tresh;
+ int rowinc;
+ int colinc;
+ int wininc;
+ } l[3];
+
+ int invalidate;
+ uint16_t palette[256];
+ } dispc;
+
+ struct {
+ int idlemode;
+ uint32_t control;
+ int enable;
+ int pixels;
+ int busy;
+ int skiplines;
+ uint16_t rxbuf;
+ uint32_t config[2];
+ uint32_t time[4];
+ uint32_t data[6];
+ uint16_t vsync;
+ uint16_t hsync;
+ struct rfbi_chip_s *chip[2];
+ } rfbi;
+};
+
+static void omap_dispc_interrupt_update(struct omap_dss_s *s)
+{
+ qemu_set_irq(s->irq, s->dispc.irqst & s->dispc.irqen);
+}
+
+static void omap_rfbi_reset(struct omap_dss_s *s)
+{
+ s->rfbi.idlemode = 0;
+ s->rfbi.control = 2;
+ s->rfbi.enable = 0;
+ s->rfbi.pixels = 0;
+ s->rfbi.skiplines = 0;
+ s->rfbi.busy = 0;
+ s->rfbi.config[0] = 0x00310000;
+ s->rfbi.config[1] = 0x00310000;
+ s->rfbi.time[0] = 0;
+ s->rfbi.time[1] = 0;
+ s->rfbi.time[2] = 0;
+ s->rfbi.time[3] = 0;
+ s->rfbi.data[0] = 0;
+ s->rfbi.data[1] = 0;
+ s->rfbi.data[2] = 0;
+ s->rfbi.data[3] = 0;
+ s->rfbi.data[4] = 0;
+ s->rfbi.data[5] = 0;
+ s->rfbi.vsync = 0;
+ s->rfbi.hsync = 0;
+}
+
+void omap_dss_reset(struct omap_dss_s *s)
+{
+ s->autoidle = 0;
+ s->control = 0;
+ s->enable = 0;
+
+ s->dig.enable = 0;
+ s->dig.nx = 1;
+ s->dig.ny = 1;
+
+ s->lcd.enable = 0;
+ s->lcd.nx = 1;
+ s->lcd.ny = 1;
+
+ s->dispc.idlemode = 0;
+ s->dispc.irqst = 0;
+ s->dispc.irqen = 0;
+ s->dispc.control = 0;
+ s->dispc.config = 0;
+ s->dispc.capable = 0x161;
+ s->dispc.timing[0] = 0;
+ s->dispc.timing[1] = 0;
+ s->dispc.timing[2] = 0;
+ s->dispc.timing[3] = 0;
+ s->dispc.line = 0;
+ s->dispc.bg[0] = 0;
+ s->dispc.bg[1] = 0;
+ s->dispc.trans[0] = 0;
+ s->dispc.trans[1] = 0;
+
+ s->dispc.l[0].enable = 0;
+ s->dispc.l[0].bpp = 0;
+ s->dispc.l[0].addr[0] = 0;
+ s->dispc.l[0].addr[1] = 0;
+ s->dispc.l[0].addr[2] = 0;
+ s->dispc.l[0].posx = 0;
+ s->dispc.l[0].posy = 0;
+ s->dispc.l[0].nx = 1;
+ s->dispc.l[0].ny = 1;
+ s->dispc.l[0].attr = 0;
+ s->dispc.l[0].tresh = 0;
+ s->dispc.l[0].rowinc = 1;
+ s->dispc.l[0].colinc = 1;
+ s->dispc.l[0].wininc = 0;
+
+ omap_rfbi_reset(s);
+ omap_dispc_interrupt_update(s);
+}
+
+static uint32_t omap_diss_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_dss_s *s = (struct omap_dss_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* DSS_REVISIONNUMBER */
+ return 0x20;
+
+ case 0x10: /* DSS_SYSCONFIG */
+ return s->autoidle;
+
+ case 0x14: /* DSS_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x40: /* DSS_CONTROL */
+ return s->control;
+
+ case 0x50: /* DSS_PSA_LCD_REG_1 */
+ case 0x54: /* DSS_PSA_LCD_REG_2 */
+ case 0x58: /* DSS_PSA_VIDEO_REG */
+ /* TODO: fake some values when appropriate s->control bits are set */
+ return 0;
+
+ case 0x5c: /* DSS_STATUS */
+ return 1 + (s->control & 1);
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_diss_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_dss_s *s = (struct omap_dss_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* DSS_REVISIONNUMBER */
+ case 0x14: /* DSS_SYSSTATUS */
+ case 0x50: /* DSS_PSA_LCD_REG_1 */
+ case 0x54: /* DSS_PSA_LCD_REG_2 */
+ case 0x58: /* DSS_PSA_VIDEO_REG */
+ case 0x5c: /* DSS_STATUS */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* DSS_SYSCONFIG */
+ if (value & 2) /* SOFTRESET */
+ omap_dss_reset(s);
+ s->autoidle = value & 1;
+ break;
+
+ case 0x40: /* DSS_CONTROL */
+ s->control = value & 0x3dd;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_diss1_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_diss_read,
+};
+
+static CPUWriteMemoryFunc * const omap_diss1_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_diss_write,
+};
+
+static uint32_t omap_disc_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_dss_s *s = (struct omap_dss_s *) opaque;
+
+ switch (addr) {
+ case 0x000: /* DISPC_REVISION */
+ return 0x20;
+
+ case 0x010: /* DISPC_SYSCONFIG */
+ return s->dispc.idlemode;
+
+ case 0x014: /* DISPC_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x018: /* DISPC_IRQSTATUS */
+ return s->dispc.irqst;
+
+ case 0x01c: /* DISPC_IRQENABLE */
+ return s->dispc.irqen;
+
+ case 0x040: /* DISPC_CONTROL */
+ return s->dispc.control;
+
+ case 0x044: /* DISPC_CONFIG */
+ return s->dispc.config;
+
+ case 0x048: /* DISPC_CAPABLE */
+ return s->dispc.capable;
+
+ case 0x04c: /* DISPC_DEFAULT_COLOR0 */
+ return s->dispc.bg[0];
+ case 0x050: /* DISPC_DEFAULT_COLOR1 */
+ return s->dispc.bg[1];
+ case 0x054: /* DISPC_TRANS_COLOR0 */
+ return s->dispc.trans[0];
+ case 0x058: /* DISPC_TRANS_COLOR1 */
+ return s->dispc.trans[1];
+
+ case 0x05c: /* DISPC_LINE_STATUS */
+ return 0x7ff;
+ case 0x060: /* DISPC_LINE_NUMBER */
+ return s->dispc.line;
+
+ case 0x064: /* DISPC_TIMING_H */
+ return s->dispc.timing[0];
+ case 0x068: /* DISPC_TIMING_V */
+ return s->dispc.timing[1];
+ case 0x06c: /* DISPC_POL_FREQ */
+ return s->dispc.timing[2];
+ case 0x070: /* DISPC_DIVISOR */
+ return s->dispc.timing[3];
+
+ case 0x078: /* DISPC_SIZE_DIG */
+ return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1);
+ case 0x07c: /* DISPC_SIZE_LCD */
+ return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1);
+
+ case 0x080: /* DISPC_GFX_BA0 */
+ return s->dispc.l[0].addr[0];
+ case 0x084: /* DISPC_GFX_BA1 */
+ return s->dispc.l[0].addr[1];
+ case 0x088: /* DISPC_GFX_POSITION */
+ return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx;
+ case 0x08c: /* DISPC_GFX_SIZE */
+ return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1);
+ case 0x0a0: /* DISPC_GFX_ATTRIBUTES */
+ return s->dispc.l[0].attr;
+ case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */
+ return s->dispc.l[0].tresh;
+ case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */
+ return 256;
+ case 0x0ac: /* DISPC_GFX_ROW_INC */
+ return s->dispc.l[0].rowinc;
+ case 0x0b0: /* DISPC_GFX_PIXEL_INC */
+ return s->dispc.l[0].colinc;
+ case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */
+ return s->dispc.l[0].wininc;
+ case 0x0b8: /* DISPC_GFX_TABLE_BA */
+ return s->dispc.l[0].addr[2];
+
+ case 0x0bc: /* DISPC_VID1_BA0 */
+ case 0x0c0: /* DISPC_VID1_BA1 */
+ case 0x0c4: /* DISPC_VID1_POSITION */
+ case 0x0c8: /* DISPC_VID1_SIZE */
+ case 0x0cc: /* DISPC_VID1_ATTRIBUTES */
+ case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */
+ case 0x0d4: /* DISPC_VID1_FIFO_SIZE_STATUS */
+ case 0x0d8: /* DISPC_VID1_ROW_INC */
+ case 0x0dc: /* DISPC_VID1_PIXEL_INC */
+ case 0x0e0: /* DISPC_VID1_FIR */
+ case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */
+ case 0x0e8: /* DISPC_VID1_ACCU0 */
+ case 0x0ec: /* DISPC_VID1_ACCU1 */
+ case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */
+ case 0x14c: /* DISPC_VID2_BA0 */
+ case 0x150: /* DISPC_VID2_BA1 */
+ case 0x154: /* DISPC_VID2_POSITION */
+ case 0x158: /* DISPC_VID2_SIZE */
+ case 0x15c: /* DISPC_VID2_ATTRIBUTES */
+ case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */
+ case 0x164: /* DISPC_VID2_FIFO_SIZE_STATUS */
+ case 0x168: /* DISPC_VID2_ROW_INC */
+ case 0x16c: /* DISPC_VID2_PIXEL_INC */
+ case 0x170: /* DISPC_VID2_FIR */
+ case 0x174: /* DISPC_VID2_PICTURE_SIZE */
+ case 0x178: /* DISPC_VID2_ACCU0 */
+ case 0x17c: /* DISPC_VID2_ACCU1 */
+ case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */
+ case 0x1d4: /* DISPC_DATA_CYCLE1 */
+ case 0x1d8: /* DISPC_DATA_CYCLE2 */
+ case 0x1dc: /* DISPC_DATA_CYCLE3 */
+ return 0;
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_disc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_dss_s *s = (struct omap_dss_s *) opaque;
+
+ switch (addr) {
+ case 0x010: /* DISPC_SYSCONFIG */
+ if (value & 2) /* SOFTRESET */
+ omap_dss_reset(s);
+ s->dispc.idlemode = value & 0x301b;
+ break;
+
+ case 0x018: /* DISPC_IRQSTATUS */
+ s->dispc.irqst &= ~value;
+ omap_dispc_interrupt_update(s);
+ break;
+
+ case 0x01c: /* DISPC_IRQENABLE */
+ s->dispc.irqen = value & 0xffff;
+ omap_dispc_interrupt_update(s);
+ break;
+
+ case 0x040: /* DISPC_CONTROL */
+ s->dispc.control = value & 0x07ff9fff;
+ s->dig.enable = (value >> 1) & 1;
+ s->lcd.enable = (value >> 0) & 1;
+ if (value & (1 << 12)) /* OVERLAY_OPTIMIZATION */
+ if (~((s->dispc.l[1].attr | s->dispc.l[2].attr) & 1))
+ fprintf(stderr, "%s: Overlay Optimization when no overlay "
+ "region effectively exists leads to "
+ "unpredictable behaviour!\n", __FUNCTION__);
+ if (value & (1 << 6)) { /* GODIGITAL */
+ /* XXX: Shadowed fields are:
+ * s->dispc.config
+ * s->dispc.capable
+ * s->dispc.bg[0]
+ * s->dispc.bg[1]
+ * s->dispc.trans[0]
+ * s->dispc.trans[1]
+ * s->dispc.line
+ * s->dispc.timing[0]
+ * s->dispc.timing[1]
+ * s->dispc.timing[2]
+ * s->dispc.timing[3]
+ * s->lcd.nx
+ * s->lcd.ny
+ * s->dig.nx
+ * s->dig.ny
+ * s->dispc.l[0].addr[0]
+ * s->dispc.l[0].addr[1]
+ * s->dispc.l[0].addr[2]
+ * s->dispc.l[0].posx
+ * s->dispc.l[0].posy
+ * s->dispc.l[0].nx
+ * s->dispc.l[0].ny
+ * s->dispc.l[0].tresh
+ * s->dispc.l[0].rowinc
+ * s->dispc.l[0].colinc
+ * s->dispc.l[0].wininc
+ * All they need to be loaded here from their shadow registers.
+ */
+ }
+ if (value & (1 << 5)) { /* GOLCD */
+ /* XXX: Likewise for LCD here. */
+ }
+ s->dispc.invalidate = 1;
+ break;
+
+ case 0x044: /* DISPC_CONFIG */
+ s->dispc.config = value & 0x3fff;
+ /* XXX:
+ * bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded
+ * bits 2:1 (LOADMODE) reset to 2 after set to 3 and palette loaded
+ */
+ s->dispc.invalidate = 1;
+ break;
+
+ case 0x048: /* DISPC_CAPABLE */
+ s->dispc.capable = value & 0x3ff;
+ break;
+
+ case 0x04c: /* DISPC_DEFAULT_COLOR0 */
+ s->dispc.bg[0] = value & 0xffffff;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x050: /* DISPC_DEFAULT_COLOR1 */
+ s->dispc.bg[1] = value & 0xffffff;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x054: /* DISPC_TRANS_COLOR0 */
+ s->dispc.trans[0] = value & 0xffffff;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x058: /* DISPC_TRANS_COLOR1 */
+ s->dispc.trans[1] = value & 0xffffff;
+ s->dispc.invalidate = 1;
+ break;
+
+ case 0x060: /* DISPC_LINE_NUMBER */
+ s->dispc.line = value & 0x7ff;
+ break;
+
+ case 0x064: /* DISPC_TIMING_H */
+ s->dispc.timing[0] = value & 0x0ff0ff3f;
+ break;
+ case 0x068: /* DISPC_TIMING_V */
+ s->dispc.timing[1] = value & 0x0ff0ff3f;
+ break;
+ case 0x06c: /* DISPC_POL_FREQ */
+ s->dispc.timing[2] = value & 0x0003ffff;
+ break;
+ case 0x070: /* DISPC_DIVISOR */
+ s->dispc.timing[3] = value & 0x00ff00ff;
+ break;
+
+ case 0x078: /* DISPC_SIZE_DIG */
+ s->dig.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */
+ s->dig.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */
+ s->dispc.invalidate = 1;
+ break;
+ case 0x07c: /* DISPC_SIZE_LCD */
+ s->lcd.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */
+ s->lcd.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */
+ s->dispc.invalidate = 1;
+ break;
+ case 0x080: /* DISPC_GFX_BA0 */
+ s->dispc.l[0].addr[0] = (target_phys_addr_t) value;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x084: /* DISPC_GFX_BA1 */
+ s->dispc.l[0].addr[1] = (target_phys_addr_t) value;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x088: /* DISPC_GFX_POSITION */
+ s->dispc.l[0].posx = ((value >> 0) & 0x7ff); /* GFXPOSX */
+ s->dispc.l[0].posy = ((value >> 16) & 0x7ff); /* GFXPOSY */
+ s->dispc.invalidate = 1;
+ break;
+ case 0x08c: /* DISPC_GFX_SIZE */
+ s->dispc.l[0].nx = ((value >> 0) & 0x7ff) + 1; /* GFXSIZEX */
+ s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1; /* GFXSIZEY */
+ s->dispc.invalidate = 1;
+ break;
+ case 0x0a0: /* DISPC_GFX_ATTRIBUTES */
+ s->dispc.l[0].attr = value & 0x7ff;
+ if (value & (3 << 9))
+ fprintf(stderr, "%s: Big-endian pixel format not supported\n",
+ __FUNCTION__);
+ s->dispc.l[0].enable = value & 1;
+ s->dispc.l[0].bpp = (value >> 1) & 0xf;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */
+ s->dispc.l[0].tresh = value & 0x01ff01ff;
+ break;
+ case 0x0ac: /* DISPC_GFX_ROW_INC */
+ s->dispc.l[0].rowinc = value;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x0b0: /* DISPC_GFX_PIXEL_INC */
+ s->dispc.l[0].colinc = value;
+ s->dispc.invalidate = 1;
+ break;
+ case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */
+ s->dispc.l[0].wininc = value;
+ break;
+ case 0x0b8: /* DISPC_GFX_TABLE_BA */
+ s->dispc.l[0].addr[2] = (target_phys_addr_t) value;
+ s->dispc.invalidate = 1;
+ break;
+
+ case 0x0bc: /* DISPC_VID1_BA0 */
+ case 0x0c0: /* DISPC_VID1_BA1 */
+ case 0x0c4: /* DISPC_VID1_POSITION */
+ case 0x0c8: /* DISPC_VID1_SIZE */
+ case 0x0cc: /* DISPC_VID1_ATTRIBUTES */
+ case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */
+ case 0x0d8: /* DISPC_VID1_ROW_INC */
+ case 0x0dc: /* DISPC_VID1_PIXEL_INC */
+ case 0x0e0: /* DISPC_VID1_FIR */
+ case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */
+ case 0x0e8: /* DISPC_VID1_ACCU0 */
+ case 0x0ec: /* DISPC_VID1_ACCU1 */
+ case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */
+ case 0x14c: /* DISPC_VID2_BA0 */
+ case 0x150: /* DISPC_VID2_BA1 */
+ case 0x154: /* DISPC_VID2_POSITION */
+ case 0x158: /* DISPC_VID2_SIZE */
+ case 0x15c: /* DISPC_VID2_ATTRIBUTES */
+ case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */
+ case 0x168: /* DISPC_VID2_ROW_INC */
+ case 0x16c: /* DISPC_VID2_PIXEL_INC */
+ case 0x170: /* DISPC_VID2_FIR */
+ case 0x174: /* DISPC_VID2_PICTURE_SIZE */
+ case 0x178: /* DISPC_VID2_ACCU0 */
+ case 0x17c: /* DISPC_VID2_ACCU1 */
+ case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */
+ case 0x1d4: /* DISPC_DATA_CYCLE1 */
+ case 0x1d8: /* DISPC_DATA_CYCLE2 */
+ case 0x1dc: /* DISPC_DATA_CYCLE3 */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_disc1_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_disc_read,
+};
+
+static CPUWriteMemoryFunc * const omap_disc1_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_disc_write,
+};
+
+static void omap_rfbi_transfer_stop(struct omap_dss_s *s)
+{
+ if (!s->rfbi.busy)
+ return;
+
+ /* TODO: in non-Bypass mode we probably need to just deassert the DRQ. */
+
+ s->rfbi.busy = 0;
+}
+
+static void omap_rfbi_transfer_start(struct omap_dss_s *s)
+{
+ void *data;
+ target_phys_addr_t len;
+ target_phys_addr_t data_addr;
+ int pitch;
+ static void *bounce_buffer;
+ static target_phys_addr_t bounce_len;
+
+ if (!s->rfbi.enable || s->rfbi.busy)
+ return;
+
+ if (s->rfbi.control & (1 << 1)) { /* BYPASS */
+ /* TODO: in non-Bypass mode we probably need to just assert the
+ * DRQ and wait for DMA to write the pixels. */
+ fprintf(stderr, "%s: Bypass mode unimplemented\n", __FUNCTION__);
+ return;
+ }
+
+ if (!(s->dispc.control & (1 << 11))) /* RFBIMODE */
+ return;
+ /* TODO: check that LCD output is enabled in DISPC. */
+
+ s->rfbi.busy = 1;
+
+ len = s->rfbi.pixels * 2;
+
+ data_addr = s->dispc.l[0].addr[0];
+ data = cpu_physical_memory_map(data_addr, &len, 0);
+ if (data && len != s->rfbi.pixels * 2) {
+ cpu_physical_memory_unmap(data, len, 0, 0);
+ data = NULL;
+ len = s->rfbi.pixels * 2;
+ }
+ if (!data) {
+ if (len > bounce_len) {
+ bounce_buffer = qemu_realloc(bounce_buffer, len);
+ }
+ data = bounce_buffer;
+ cpu_physical_memory_read(data_addr, data, len);
+ }
+
+ /* TODO bpp */
+ s->rfbi.pixels = 0;
+
+ /* TODO: negative values */
+ pitch = s->dispc.l[0].nx + (s->dispc.l[0].rowinc - 1) / 2;
+
+ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
+ s->rfbi.chip[0]->block(s->rfbi.chip[0]->opaque, 1, data, len, pitch);
+ if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
+ s->rfbi.chip[1]->block(s->rfbi.chip[1]->opaque, 1, data, len, pitch);
+
+ if (data != bounce_buffer) {
+ cpu_physical_memory_unmap(data, len, 0, len);
+ }
+
+ omap_rfbi_transfer_stop(s);
+
+ /* TODO */
+ s->dispc.irqst |= 1; /* FRAMEDONE */
+ omap_dispc_interrupt_update(s);
+}
+
+static uint32_t omap_rfbi_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_dss_s *s = (struct omap_dss_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* RFBI_REVISION */
+ return 0x10;
+
+ case 0x10: /* RFBI_SYSCONFIG */
+ return s->rfbi.idlemode;
+
+ case 0x14: /* RFBI_SYSSTATUS */
+ return 1 | (s->rfbi.busy << 8); /* RESETDONE */
+
+ case 0x40: /* RFBI_CONTROL */
+ return s->rfbi.control;
+
+ case 0x44: /* RFBI_PIXELCNT */
+ return s->rfbi.pixels;
+
+ case 0x48: /* RFBI_LINE_NUMBER */
+ return s->rfbi.skiplines;
+
+ case 0x58: /* RFBI_READ */
+ case 0x5c: /* RFBI_STATUS */
+ return s->rfbi.rxbuf;
+
+ case 0x60: /* RFBI_CONFIG0 */
+ return s->rfbi.config[0];
+ case 0x64: /* RFBI_ONOFF_TIME0 */
+ return s->rfbi.time[0];
+ case 0x68: /* RFBI_CYCLE_TIME0 */
+ return s->rfbi.time[1];
+ case 0x6c: /* RFBI_DATA_CYCLE1_0 */
+ return s->rfbi.data[0];
+ case 0x70: /* RFBI_DATA_CYCLE2_0 */
+ return s->rfbi.data[1];
+ case 0x74: /* RFBI_DATA_CYCLE3_0 */
+ return s->rfbi.data[2];
+
+ case 0x78: /* RFBI_CONFIG1 */
+ return s->rfbi.config[1];
+ case 0x7c: /* RFBI_ONOFF_TIME1 */
+ return s->rfbi.time[2];
+ case 0x80: /* RFBI_CYCLE_TIME1 */
+ return s->rfbi.time[3];
+ case 0x84: /* RFBI_DATA_CYCLE1_1 */
+ return s->rfbi.data[3];
+ case 0x88: /* RFBI_DATA_CYCLE2_1 */
+ return s->rfbi.data[4];
+ case 0x8c: /* RFBI_DATA_CYCLE3_1 */
+ return s->rfbi.data[5];
+
+ case 0x90: /* RFBI_VSYNC_WIDTH */
+ return s->rfbi.vsync;
+ case 0x94: /* RFBI_HSYNC_WIDTH */
+ return s->rfbi.hsync;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_rfbi_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_dss_s *s = (struct omap_dss_s *) opaque;
+
+ switch (addr) {
+ case 0x10: /* RFBI_SYSCONFIG */
+ if (value & 2) /* SOFTRESET */
+ omap_rfbi_reset(s);
+ s->rfbi.idlemode = value & 0x19;
+ break;
+
+ case 0x40: /* RFBI_CONTROL */
+ s->rfbi.control = value & 0xf;
+ s->rfbi.enable = value & 1;
+ if (value & (1 << 4) && /* ITE */
+ !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc))
+ omap_rfbi_transfer_start(s);
+ break;
+
+ case 0x44: /* RFBI_PIXELCNT */
+ s->rfbi.pixels = value;
+ break;
+
+ case 0x48: /* RFBI_LINE_NUMBER */
+ s->rfbi.skiplines = value & 0x7ff;
+ break;
+
+ case 0x4c: /* RFBI_CMD */
+ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
+ s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 0, value & 0xffff);
+ if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
+ s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 0, value & 0xffff);
+ break;
+ case 0x50: /* RFBI_PARAM */
+ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
+ s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff);
+ if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
+ s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff);
+ break;
+ case 0x54: /* RFBI_DATA */
+ /* TODO: take into account the format set up in s->rfbi.config[?] and
+ * s->rfbi.data[?], but special-case the most usual scenario so that
+ * speed doesn't suffer. */
+ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) {
+ s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff);
+ s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value >> 16);
+ }
+ if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) {
+ s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff);
+ s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value >> 16);
+ }
+ if (!-- s->rfbi.pixels)
+ omap_rfbi_transfer_stop(s);
+ break;
+ case 0x58: /* RFBI_READ */
+ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
+ s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1);
+ else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
+ s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1);
+ if (!-- s->rfbi.pixels)
+ omap_rfbi_transfer_stop(s);
+ break;
+
+ case 0x5c: /* RFBI_STATUS */
+ if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0])
+ s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0);
+ else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1])
+ s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0);
+ if (!-- s->rfbi.pixels)
+ omap_rfbi_transfer_stop(s);
+ break;
+
+ case 0x60: /* RFBI_CONFIG0 */
+ s->rfbi.config[0] = value & 0x003f1fff;
+ break;
+
+ case 0x64: /* RFBI_ONOFF_TIME0 */
+ s->rfbi.time[0] = value & 0x3fffffff;
+ break;
+ case 0x68: /* RFBI_CYCLE_TIME0 */
+ s->rfbi.time[1] = value & 0x0fffffff;
+ break;
+ case 0x6c: /* RFBI_DATA_CYCLE1_0 */
+ s->rfbi.data[0] = value & 0x0f1f0f1f;
+ break;
+ case 0x70: /* RFBI_DATA_CYCLE2_0 */
+ s->rfbi.data[1] = value & 0x0f1f0f1f;
+ break;
+ case 0x74: /* RFBI_DATA_CYCLE3_0 */
+ s->rfbi.data[2] = value & 0x0f1f0f1f;
+ break;
+ case 0x78: /* RFBI_CONFIG1 */
+ s->rfbi.config[1] = value & 0x003f1fff;
+ break;
+
+ case 0x7c: /* RFBI_ONOFF_TIME1 */
+ s->rfbi.time[2] = value & 0x3fffffff;
+ break;
+ case 0x80: /* RFBI_CYCLE_TIME1 */
+ s->rfbi.time[3] = value & 0x0fffffff;
+ break;
+ case 0x84: /* RFBI_DATA_CYCLE1_1 */
+ s->rfbi.data[3] = value & 0x0f1f0f1f;
+ break;
+ case 0x88: /* RFBI_DATA_CYCLE2_1 */
+ s->rfbi.data[4] = value & 0x0f1f0f1f;
+ break;
+ case 0x8c: /* RFBI_DATA_CYCLE3_1 */
+ s->rfbi.data[5] = value & 0x0f1f0f1f;
+ break;
+
+ case 0x90: /* RFBI_VSYNC_WIDTH */
+ s->rfbi.vsync = value & 0xffff;
+ break;
+ case 0x94: /* RFBI_HSYNC_WIDTH */
+ s->rfbi.hsync = value & 0xffff;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_rfbi1_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_rfbi_read,
+};
+
+static CPUWriteMemoryFunc * const omap_rfbi1_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_rfbi_write,
+};
+
+static uint32_t omap_venc_read(void *opaque, target_phys_addr_t addr)
+{
+ switch (addr) {
+ case 0x00: /* REV_ID */
+ case 0x04: /* STATUS */
+ case 0x08: /* F_CONTROL */
+ case 0x10: /* VIDOUT_CTRL */
+ case 0x14: /* SYNC_CTRL */
+ case 0x1c: /* LLEN */
+ case 0x20: /* FLENS */
+ case 0x24: /* HFLTR_CTRL */
+ case 0x28: /* CC_CARR_WSS_CARR */
+ case 0x2c: /* C_PHASE */
+ case 0x30: /* GAIN_U */
+ case 0x34: /* GAIN_V */
+ case 0x38: /* GAIN_Y */
+ case 0x3c: /* BLACK_LEVEL */
+ case 0x40: /* BLANK_LEVEL */
+ case 0x44: /* X_COLOR */
+ case 0x48: /* M_CONTROL */
+ case 0x4c: /* BSTAMP_WSS_DATA */
+ case 0x50: /* S_CARR */
+ case 0x54: /* LINE21 */
+ case 0x58: /* LN_SEL */
+ case 0x5c: /* L21__WC_CTL */
+ case 0x60: /* HTRIGGER_VTRIGGER */
+ case 0x64: /* SAVID__EAVID */
+ case 0x68: /* FLEN__FAL */
+ case 0x6c: /* LAL__PHASE_RESET */
+ case 0x70: /* HS_INT_START_STOP_X */
+ case 0x74: /* HS_EXT_START_STOP_X */
+ case 0x78: /* VS_INT_START_X */
+ case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */
+ case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */
+ case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */
+ case 0x88: /* VS_EXT_STOP_Y */
+ case 0x90: /* AVID_START_STOP_X */
+ case 0x94: /* AVID_START_STOP_Y */
+ case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */
+ case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */
+ case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */
+ case 0xb0: /* TVDETGP_INT_START_STOP_X */
+ case 0xb4: /* TVDETGP_INT_START_STOP_Y */
+ case 0xb8: /* GEN_CTRL */
+ case 0xc4: /* DAC_TST__DAC_A */
+ case 0xc8: /* DAC_B__DAC_C */
+ return 0;
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_venc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ switch (addr) {
+ case 0x08: /* F_CONTROL */
+ case 0x10: /* VIDOUT_CTRL */
+ case 0x14: /* SYNC_CTRL */
+ case 0x1c: /* LLEN */
+ case 0x20: /* FLENS */
+ case 0x24: /* HFLTR_CTRL */
+ case 0x28: /* CC_CARR_WSS_CARR */
+ case 0x2c: /* C_PHASE */
+ case 0x30: /* GAIN_U */
+ case 0x34: /* GAIN_V */
+ case 0x38: /* GAIN_Y */
+ case 0x3c: /* BLACK_LEVEL */
+ case 0x40: /* BLANK_LEVEL */
+ case 0x44: /* X_COLOR */
+ case 0x48: /* M_CONTROL */
+ case 0x4c: /* BSTAMP_WSS_DATA */
+ case 0x50: /* S_CARR */
+ case 0x54: /* LINE21 */
+ case 0x58: /* LN_SEL */
+ case 0x5c: /* L21__WC_CTL */
+ case 0x60: /* HTRIGGER_VTRIGGER */
+ case 0x64: /* SAVID__EAVID */
+ case 0x68: /* FLEN__FAL */
+ case 0x6c: /* LAL__PHASE_RESET */
+ case 0x70: /* HS_INT_START_STOP_X */
+ case 0x74: /* HS_EXT_START_STOP_X */
+ case 0x78: /* VS_INT_START_X */
+ case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */
+ case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */
+ case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */
+ case 0x88: /* VS_EXT_STOP_Y */
+ case 0x90: /* AVID_START_STOP_X */
+ case 0x94: /* AVID_START_STOP_Y */
+ case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */
+ case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */
+ case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */
+ case 0xb0: /* TVDETGP_INT_START_STOP_X */
+ case 0xb4: /* TVDETGP_INT_START_STOP_Y */
+ case 0xb8: /* GEN_CTRL */
+ case 0xc4: /* DAC_TST__DAC_A */
+ case 0xc8: /* DAC_B__DAC_C */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_venc1_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_venc_read,
+};
+
+static CPUWriteMemoryFunc * const omap_venc1_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_venc_write,
+};
+
+static uint32_t omap_im3_read(void *opaque, target_phys_addr_t addr)
+{
+ switch (addr) {
+ case 0x0a8: /* SBIMERRLOGA */
+ case 0x0b0: /* SBIMERRLOG */
+ case 0x190: /* SBIMSTATE */
+ case 0x198: /* SBTMSTATE_L */
+ case 0x19c: /* SBTMSTATE_H */
+ case 0x1a8: /* SBIMCONFIG_L */
+ case 0x1ac: /* SBIMCONFIG_H */
+ case 0x1f8: /* SBID_L */
+ case 0x1fc: /* SBID_H */
+ return 0;
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_im3_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ switch (addr) {
+ case 0x0b0: /* SBIMERRLOG */
+ case 0x190: /* SBIMSTATE */
+ case 0x198: /* SBTMSTATE_L */
+ case 0x19c: /* SBTMSTATE_H */
+ case 0x1a8: /* SBIMCONFIG_L */
+ case 0x1ac: /* SBIMCONFIG_H */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_im3_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_im3_read,
+};
+
+static CPUWriteMemoryFunc * const omap_im3_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_im3_write,
+};
+
+struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta,
+ target_phys_addr_t l3_base,
+ qemu_irq irq, qemu_irq drq,
+ omap_clk fck1, omap_clk fck2, omap_clk ck54m,
+ omap_clk ick1, omap_clk ick2)
+{
+ int iomemtype[5];
+ struct omap_dss_s *s = (struct omap_dss_s *)
+ qemu_mallocz(sizeof(struct omap_dss_s));
+
+ s->irq = irq;
+ s->drq = drq;
+ omap_dss_reset(s);
+
+ iomemtype[0] = l4_register_io_memory(omap_diss1_readfn,
+ omap_diss1_writefn, s);
+ iomemtype[1] = l4_register_io_memory(omap_disc1_readfn,
+ omap_disc1_writefn, s);
+ iomemtype[2] = l4_register_io_memory(omap_rfbi1_readfn,
+ omap_rfbi1_writefn, s);
+ iomemtype[3] = l4_register_io_memory(omap_venc1_readfn,
+ omap_venc1_writefn, s);
+ iomemtype[4] = cpu_register_io_memory(omap_im3_readfn,
+ omap_im3_writefn, s, DEVICE_NATIVE_ENDIAN);
+ omap_l4_attach(ta, 0, iomemtype[0]);
+ omap_l4_attach(ta, 1, iomemtype[1]);
+ omap_l4_attach(ta, 2, iomemtype[2]);
+ omap_l4_attach(ta, 3, iomemtype[3]);
+ cpu_register_physical_memory(l3_base, 0x1000, iomemtype[4]);
+
+#if 0
+ s->state = graphic_console_init(omap_update_display,
+ omap_invalidate_display, omap_screen_dump, s);
+#endif
+
+ return s;
+}
+
+void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip)
+{
+ if (cs < 0 || cs > 1)
+ hw_error("%s: wrong CS %i\n", __FUNCTION__, cs);
+ s->rfbi.chip[cs] = chip;
+}
diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c
new file mode 100644
index 0000000..478f7d9
--- /dev/null
+++ b/hw/omap_gpio.c
@@ -0,0 +1,725 @@
+/*
+ * TI OMAP processors GPIO emulation.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2007-2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "omap.h"
+/* General-Purpose I/O */
+struct omap_gpio_s {
+ qemu_irq irq;
+ qemu_irq *in;
+ qemu_irq handler[16];
+
+ uint16_t inputs;
+ uint16_t outputs;
+ uint16_t dir;
+ uint16_t edge;
+ uint16_t mask;
+ uint16_t ints;
+ uint16_t pins;
+};
+
+static void omap_gpio_set(void *opaque, int line, int level)
+{
+ struct omap_gpio_s *s = (struct omap_gpio_s *) opaque;
+ uint16_t prev = s->inputs;
+
+ if (level)
+ s->inputs |= 1 << line;
+ else
+ s->inputs &= ~(1 << line);
+
+ if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) &
+ (1 << line) & s->dir & ~s->mask) {
+ s->ints |= 1 << line;
+ qemu_irq_raise(s->irq);
+ }
+}
+
+static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_gpio_s *s = (struct omap_gpio_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* DATA_INPUT */
+ return s->inputs & s->pins;
+
+ case 0x04: /* DATA_OUTPUT */
+ return s->outputs;
+
+ case 0x08: /* DIRECTION_CONTROL */
+ return s->dir;
+
+ case 0x0c: /* INTERRUPT_CONTROL */
+ return s->edge;
+
+ case 0x10: /* INTERRUPT_MASK */
+ return s->mask;
+
+ case 0x14: /* INTERRUPT_STATUS */
+ return s->ints;
+
+ case 0x18: /* PIN_CONTROL (not in OMAP310) */
+ OMAP_BAD_REG(addr);
+ return s->pins;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_gpio_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_gpio_s *s = (struct omap_gpio_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t diff;
+ int ln;
+
+ switch (offset) {
+ case 0x00: /* DATA_INPUT */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x04: /* DATA_OUTPUT */
+ diff = (s->outputs ^ value) & ~s->dir;
+ s->outputs = value;
+ while ((ln = ffs(diff))) {
+ ln --;
+ if (s->handler[ln])
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+ break;
+
+ case 0x08: /* DIRECTION_CONTROL */
+ diff = s->outputs & (s->dir ^ value);
+ s->dir = value;
+
+ value = s->outputs & ~s->dir;
+ while ((ln = ffs(diff))) {
+ ln --;
+ if (s->handler[ln])
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+ break;
+
+ case 0x0c: /* INTERRUPT_CONTROL */
+ s->edge = value;
+ break;
+
+ case 0x10: /* INTERRUPT_MASK */
+ s->mask = value;
+ break;
+
+ case 0x14: /* INTERRUPT_STATUS */
+ s->ints &= ~value;
+ if (!s->ints)
+ qemu_irq_lower(s->irq);
+ break;
+
+ case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */
+ OMAP_BAD_REG(addr);
+ s->pins = value;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+/* *Some* sources say the memory region is 32-bit. */
+static CPUReadMemoryFunc * const omap_gpio_readfn[] = {
+ omap_badwidth_read16,
+ omap_gpio_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_gpio_writefn[] = {
+ omap_badwidth_write16,
+ omap_gpio_write,
+ omap_badwidth_write16,
+};
+
+void omap_gpio_reset(struct omap_gpio_s *s)
+{
+ s->inputs = 0;
+ s->outputs = ~0;
+ s->dir = ~0;
+ s->edge = ~0;
+ s->mask = ~0;
+ s->ints = 0;
+ s->pins = ~0;
+}
+
+struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_gpio_s *s = (struct omap_gpio_s *)
+ qemu_mallocz(sizeof(struct omap_gpio_s));
+
+ s->irq = irq;
+ s->in = qemu_allocate_irqs(omap_gpio_set, s, 16);
+ omap_gpio_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_gpio_readfn,
+ omap_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x1000, iomemtype);
+
+ return s;
+}
+
+qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s)
+{
+ return s->in;
+}
+
+void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler)
+{
+ if (line >= 16 || line < 0)
+ hw_error("%s: No GPIO line %i\n", __FUNCTION__, line);
+ s->handler[line] = handler;
+}
+
+/* General-Purpose Interface of OMAP2 */
+struct omap2_gpio_s {
+ qemu_irq irq[2];
+ qemu_irq wkup;
+ qemu_irq *in;
+ qemu_irq handler[32];
+
+ uint8_t config[2];
+ uint32_t inputs;
+ uint32_t outputs;
+ uint32_t dir;
+ uint32_t level[2];
+ uint32_t edge[2];
+ uint32_t mask[2];
+ uint32_t wumask;
+ uint32_t ints[2];
+ uint32_t debounce;
+ uint8_t delay;
+};
+
+static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s,
+ int line)
+{
+ qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]);
+}
+
+static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line)
+{
+ if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */
+ return;
+ if (!(s->config[0] & (3 << 3))) /* Force Idle */
+ return;
+ if (!(s->wumask & (1 << line)))
+ return;
+
+ qemu_irq_raise(s->wkup);
+}
+
+static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s,
+ uint32_t diff)
+{
+ int ln;
+
+ s->outputs ^= diff;
+ diff &= ~s->dir;
+ while ((ln = ffs(diff))) {
+ ln --;
+ qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1);
+ diff &= ~(1 << ln);
+ }
+}
+
+static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line)
+{
+ s->ints[line] |= s->dir &
+ ((s->inputs & s->level[1]) | (~s->inputs & s->level[0]));
+ omap2_gpio_module_int_update(s, line);
+}
+
+static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line)
+{
+ s->ints[0] |= 1 << line;
+ omap2_gpio_module_int_update(s, 0);
+ s->ints[1] |= 1 << line;
+ omap2_gpio_module_int_update(s, 1);
+ omap2_gpio_module_wake(s, line);
+}
+
+static void omap2_gpio_module_set(void *opaque, int line, int level)
+{
+ struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
+
+ if (level) {
+ if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1]))
+ omap2_gpio_module_int(s, line);
+ s->inputs |= 1 << line;
+ } else {
+ if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0]))
+ omap2_gpio_module_int(s, line);
+ s->inputs &= ~(1 << line);
+ }
+}
+
+static void omap2_gpio_module_reset(struct omap2_gpio_s *s)
+{
+ s->config[0] = 0;
+ s->config[1] = 2;
+ s->ints[0] = 0;
+ s->ints[1] = 0;
+ s->mask[0] = 0;
+ s->mask[1] = 0;
+ s->wumask = 0;
+ s->dir = ~0;
+ s->level[0] = 0;
+ s->level[1] = 0;
+ s->edge[0] = 0;
+ s->edge[1] = 0;
+ s->debounce = 0;
+ s->delay = 0;
+}
+
+static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* GPIO_REVISION */
+ return 0x18;
+
+ case 0x10: /* GPIO_SYSCONFIG */
+ return s->config[0];
+
+ case 0x14: /* GPIO_SYSSTATUS */
+ return 0x01;
+
+ case 0x18: /* GPIO_IRQSTATUS1 */
+ return s->ints[0];
+
+ case 0x1c: /* GPIO_IRQENABLE1 */
+ case 0x60: /* GPIO_CLEARIRQENABLE1 */
+ case 0x64: /* GPIO_SETIRQENABLE1 */
+ return s->mask[0];
+
+ case 0x20: /* GPIO_WAKEUPENABLE */
+ case 0x80: /* GPIO_CLEARWKUENA */
+ case 0x84: /* GPIO_SETWKUENA */
+ return s->wumask;
+
+ case 0x28: /* GPIO_IRQSTATUS2 */
+ return s->ints[1];
+
+ case 0x2c: /* GPIO_IRQENABLE2 */
+ case 0x70: /* GPIO_CLEARIRQENABLE2 */
+ case 0x74: /* GPIO_SETIREQNEABLE2 */
+ return s->mask[1];
+
+ case 0x30: /* GPIO_CTRL */
+ return s->config[1];
+
+ case 0x34: /* GPIO_OE */
+ return s->dir;
+
+ case 0x38: /* GPIO_DATAIN */
+ return s->inputs;
+
+ case 0x3c: /* GPIO_DATAOUT */
+ case 0x90: /* GPIO_CLEARDATAOUT */
+ case 0x94: /* GPIO_SETDATAOUT */
+ return s->outputs;
+
+ case 0x40: /* GPIO_LEVELDETECT0 */
+ return s->level[0];
+
+ case 0x44: /* GPIO_LEVELDETECT1 */
+ return s->level[1];
+
+ case 0x48: /* GPIO_RISINGDETECT */
+ return s->edge[0];
+
+ case 0x4c: /* GPIO_FALLINGDETECT */
+ return s->edge[1];
+
+ case 0x50: /* GPIO_DEBOUNCENABLE */
+ return s->debounce;
+
+ case 0x54: /* GPIO_DEBOUNCINGTIME */
+ return s->delay;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap2_gpio_module_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque;
+ uint32_t diff;
+ int ln;
+
+ switch (addr) {
+ case 0x00: /* GPIO_REVISION */
+ case 0x14: /* GPIO_SYSSTATUS */
+ case 0x38: /* GPIO_DATAIN */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* GPIO_SYSCONFIG */
+ if (((value >> 3) & 3) == 3)
+ fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__);
+ if (value & 2)
+ omap2_gpio_module_reset(s);
+ s->config[0] = value & 0x1d;
+ break;
+
+ case 0x18: /* GPIO_IRQSTATUS1 */
+ if (s->ints[0] & value) {
+ s->ints[0] &= ~value;
+ omap2_gpio_module_level_update(s, 0);
+ }
+ break;
+
+ case 0x1c: /* GPIO_IRQENABLE1 */
+ s->mask[0] = value;
+ omap2_gpio_module_int_update(s, 0);
+ break;
+
+ case 0x20: /* GPIO_WAKEUPENABLE */
+ s->wumask = value;
+ break;
+
+ case 0x28: /* GPIO_IRQSTATUS2 */
+ if (s->ints[1] & value) {
+ s->ints[1] &= ~value;
+ omap2_gpio_module_level_update(s, 1);
+ }
+ break;
+
+ case 0x2c: /* GPIO_IRQENABLE2 */
+ s->mask[1] = value;
+ omap2_gpio_module_int_update(s, 1);
+ break;
+
+ case 0x30: /* GPIO_CTRL */
+ s->config[1] = value & 7;
+ break;
+
+ case 0x34: /* GPIO_OE */
+ diff = s->outputs & (s->dir ^ value);
+ s->dir = value;
+
+ value = s->outputs & ~s->dir;
+ while ((ln = ffs(diff))) {
+ diff &= ~(1 <<-- ln);
+ qemu_set_irq(s->handler[ln], (value >> ln) & 1);
+ }
+
+ omap2_gpio_module_level_update(s, 0);
+ omap2_gpio_module_level_update(s, 1);
+ break;
+
+ case 0x3c: /* GPIO_DATAOUT */
+ omap2_gpio_module_out_update(s, s->outputs ^ value);
+ break;
+
+ case 0x40: /* GPIO_LEVELDETECT0 */
+ s->level[0] = value;
+ omap2_gpio_module_level_update(s, 0);
+ omap2_gpio_module_level_update(s, 1);
+ break;
+
+ case 0x44: /* GPIO_LEVELDETECT1 */
+ s->level[1] = value;
+ omap2_gpio_module_level_update(s, 0);
+ omap2_gpio_module_level_update(s, 1);
+ break;
+
+ case 0x48: /* GPIO_RISINGDETECT */
+ s->edge[0] = value;
+ break;
+
+ case 0x4c: /* GPIO_FALLINGDETECT */
+ s->edge[1] = value;
+ break;
+
+ case 0x50: /* GPIO_DEBOUNCENABLE */
+ s->debounce = value;
+ break;
+
+ case 0x54: /* GPIO_DEBOUNCINGTIME */
+ s->delay = value;
+ break;
+
+ case 0x60: /* GPIO_CLEARIRQENABLE1 */
+ s->mask[0] &= ~value;
+ omap2_gpio_module_int_update(s, 0);
+ break;
+
+ case 0x64: /* GPIO_SETIRQENABLE1 */
+ s->mask[0] |= value;
+ omap2_gpio_module_int_update(s, 0);
+ break;
+
+ case 0x70: /* GPIO_CLEARIRQENABLE2 */
+ s->mask[1] &= ~value;
+ omap2_gpio_module_int_update(s, 1);
+ break;
+
+ case 0x74: /* GPIO_SETIREQNEABLE2 */
+ s->mask[1] |= value;
+ omap2_gpio_module_int_update(s, 1);
+ break;
+
+ case 0x80: /* GPIO_CLEARWKUENA */
+ s->wumask &= ~value;
+ break;
+
+ case 0x84: /* GPIO_SETWKUENA */
+ s->wumask |= value;
+ break;
+
+ case 0x90: /* GPIO_CLEARDATAOUT */
+ omap2_gpio_module_out_update(s, s->outputs & value);
+ break;
+
+ case 0x94: /* GPIO_SETDATAOUT */
+ omap2_gpio_module_out_update(s, ~s->outputs & value);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static uint32_t omap2_gpio_module_readp(void *opaque, target_phys_addr_t addr)
+{
+ return omap2_gpio_module_readp(opaque, addr) >> ((addr & 3) << 3);
+}
+
+static void omap2_gpio_module_writep(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ uint32_t cur = 0;
+ uint32_t mask = 0xffff;
+
+ switch (addr & ~3) {
+ case 0x00: /* GPIO_REVISION */
+ case 0x14: /* GPIO_SYSSTATUS */
+ case 0x38: /* GPIO_DATAIN */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* GPIO_SYSCONFIG */
+ case 0x1c: /* GPIO_IRQENABLE1 */
+ case 0x20: /* GPIO_WAKEUPENABLE */
+ case 0x2c: /* GPIO_IRQENABLE2 */
+ case 0x30: /* GPIO_CTRL */
+ case 0x34: /* GPIO_OE */
+ case 0x3c: /* GPIO_DATAOUT */
+ case 0x40: /* GPIO_LEVELDETECT0 */
+ case 0x44: /* GPIO_LEVELDETECT1 */
+ case 0x48: /* GPIO_RISINGDETECT */
+ case 0x4c: /* GPIO_FALLINGDETECT */
+ case 0x50: /* GPIO_DEBOUNCENABLE */
+ case 0x54: /* GPIO_DEBOUNCINGTIME */
+ cur = omap2_gpio_module_read(opaque, addr & ~3) &
+ ~(mask << ((addr & 3) << 3));
+
+ /* Fall through. */
+ case 0x18: /* GPIO_IRQSTATUS1 */
+ case 0x28: /* GPIO_IRQSTATUS2 */
+ case 0x60: /* GPIO_CLEARIRQENABLE1 */
+ case 0x64: /* GPIO_SETIRQENABLE1 */
+ case 0x70: /* GPIO_CLEARIRQENABLE2 */
+ case 0x74: /* GPIO_SETIREQNEABLE2 */
+ case 0x80: /* GPIO_CLEARWKUENA */
+ case 0x84: /* GPIO_SETWKUENA */
+ case 0x90: /* GPIO_CLEARDATAOUT */
+ case 0x94: /* GPIO_SETDATAOUT */
+ value <<= (addr & 3) << 3;
+ omap2_gpio_module_write(opaque, addr, cur | value);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap2_gpio_module_readfn[] = {
+ omap2_gpio_module_readp,
+ omap2_gpio_module_readp,
+ omap2_gpio_module_read,
+};
+
+static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = {
+ omap2_gpio_module_writep,
+ omap2_gpio_module_writep,
+ omap2_gpio_module_write,
+};
+
+static void omap2_gpio_module_init(struct omap2_gpio_s *s,
+ struct omap_target_agent_s *ta, int region,
+ qemu_irq mpu, qemu_irq dsp, qemu_irq wkup,
+ omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+
+ s->irq[0] = mpu;
+ s->irq[1] = dsp;
+ s->wkup = wkup;
+ s->in = qemu_allocate_irqs(omap2_gpio_module_set, s, 32);
+
+ iomemtype = l4_register_io_memory(omap2_gpio_module_readfn,
+ omap2_gpio_module_writefn, s);
+ omap_l4_attach(ta, region, iomemtype);
+}
+
+struct omap_gpif_s {
+ struct omap2_gpio_s module[5];
+ int modules;
+
+ int autoidle;
+ int gpo;
+};
+
+void omap_gpif_reset(struct omap_gpif_s *s)
+{
+ int i;
+
+ for (i = 0; i < s->modules; i ++)
+ omap2_gpio_module_reset(s->module + i);
+
+ s->autoidle = 0;
+ s->gpo = 0;
+}
+
+static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_gpif_s *s = (struct omap_gpif_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* IPGENERICOCPSPL_REVISION */
+ return 0x18;
+
+ case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */
+ return s->autoidle;
+
+ case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */
+ return 0x01;
+
+ case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */
+ return 0x00;
+
+ case 0x40: /* IPGENERICOCPSPL_GPO */
+ return s->gpo;
+
+ case 0x50: /* IPGENERICOCPSPL_GPI */
+ return 0x00;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_gpif_s *s = (struct omap_gpif_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* IPGENERICOCPSPL_REVISION */
+ case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */
+ case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */
+ case 0x50: /* IPGENERICOCPSPL_GPI */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */
+ if (value & (1 << 1)) /* SOFTRESET */
+ omap_gpif_reset(s);
+ s->autoidle = value & 1;
+ break;
+
+ case 0x40: /* IPGENERICOCPSPL_GPO */
+ s->gpo = value & 1;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = {
+ omap_gpif_top_read,
+ omap_gpif_top_read,
+ omap_gpif_top_read,
+};
+
+static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = {
+ omap_gpif_top_write,
+ omap_gpif_top_write,
+ omap_gpif_top_write,
+};
+
+struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta,
+ qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules)
+{
+ int iomemtype, i;
+ struct omap_gpif_s *s = (struct omap_gpif_s *)
+ qemu_mallocz(sizeof(struct omap_gpif_s));
+ int region[4] = { 0, 2, 4, 5 };
+
+ s->modules = modules;
+ for (i = 0; i < modules; i ++)
+ omap2_gpio_module_init(s->module + i, ta, region[i],
+ irq[i], NULL, NULL, fclk[i], iclk);
+
+ omap_gpif_reset(s);
+
+ iomemtype = l4_register_io_memory(omap_gpif_top_readfn,
+ omap_gpif_top_writefn, s);
+ omap_l4_attach(ta, 1, iomemtype);
+
+ return s;
+}
+
+qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start)
+{
+ if (start >= s->modules * 32 || start < 0)
+ hw_error("%s: No GPIO line %i\n", __FUNCTION__, start);
+ return s->module[start >> 5].in + (start & 31);
+}
+
+void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler)
+{
+ if (line >= s->modules * 32 || line < 0)
+ hw_error("%s: No GPIO line %i\n", __FUNCTION__, line);
+ s->module[line >> 5].handler[line & 31] = handler;
+}
diff --git a/hw/omap_gpmc.c b/hw/omap_gpmc.c
new file mode 100644
index 0000000..8bf3343
--- /dev/null
+++ b/hw/omap_gpmc.c
@@ -0,0 +1,419 @@
+/*
+ * TI OMAP general purpose memory controller emulation.
+ *
+ * Copyright (C) 2007-2009 Nokia Corporation
+ * Original code written by Andrzej Zaborowski <andrew@openedhand.com>
+ * Enhancements for OMAP3 and NAND support written by Juha Riihimäki
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "flash.h"
+#include "omap.h"
+
+/* General-Purpose Memory Controller */
+struct omap_gpmc_s {
+ qemu_irq irq;
+
+ uint8_t sysconfig;
+ uint16_t irqst;
+ uint16_t irqen;
+ uint16_t timeout;
+ uint16_t config;
+ uint32_t prefconfig[2];
+ int prefcontrol;
+ int preffifo;
+ int prefcount;
+ struct omap_gpmc_cs_file_s {
+ uint32_t config[7];
+ target_phys_addr_t base;
+ size_t size;
+ int iomemtype;
+ void (*base_update)(void *opaque, target_phys_addr_t new);
+ void (*unmap)(void *opaque);
+ void *opaque;
+ } cs_file[8];
+ int ecc_cs;
+ int ecc_ptr;
+ uint32_t ecc_cfg;
+ ECCState ecc[9];
+};
+
+static void omap_gpmc_int_update(struct omap_gpmc_s *s)
+{
+ qemu_set_irq(s->irq, s->irqen & s->irqst);
+}
+
+static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask)
+{
+ /* TODO: check for overlapping regions and report access errors */
+ if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) ||
+ (base < 0 || base >= 0x40) ||
+ (base & 0x0f & ~mask)) {
+ fprintf(stderr, "%s: wrong cs address mapping/decoding!\n",
+ __FUNCTION__);
+ return;
+ }
+
+ if (!f->opaque)
+ return;
+
+ f->base = base << 24;
+ f->size = (0x0fffffff & ~(mask << 24)) + 1;
+ /* TODO: rather than setting the size of the mapping (which should be
+ * constant), the mask should cause wrapping of the address space, so
+ * that the same memory becomes accessible at every <i>size</i> bytes
+ * starting from <i>base</i>. */
+ if (f->iomemtype)
+ cpu_register_physical_memory(f->base, f->size, f->iomemtype);
+
+ if (f->base_update)
+ f->base_update(f->opaque, f->base);
+}
+
+static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f)
+{
+ if (f->size) {
+ if (f->unmap)
+ f->unmap(f->opaque);
+ if (f->iomemtype)
+ cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED);
+ f->base = 0;
+ f->size = 0;
+ }
+}
+
+void omap_gpmc_reset(struct omap_gpmc_s *s)
+{
+ int i;
+
+ s->sysconfig = 0;
+ s->irqst = 0;
+ s->irqen = 0;
+ omap_gpmc_int_update(s);
+ s->timeout = 0;
+ s->config = 0xa00;
+ s->prefconfig[0] = 0x00004000;
+ s->prefconfig[1] = 0x00000000;
+ s->prefcontrol = 0;
+ s->preffifo = 0;
+ s->prefcount = 0;
+ for (i = 0; i < 8; i ++) {
+ if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */
+ omap_gpmc_cs_unmap(s->cs_file + i);
+ s->cs_file[i].config[0] = i ? 1 << 12 : 0;
+ s->cs_file[i].config[1] = 0x101001;
+ s->cs_file[i].config[2] = 0x020201;
+ s->cs_file[i].config[3] = 0x10031003;
+ s->cs_file[i].config[4] = 0x10f1111;
+ s->cs_file[i].config[5] = 0;
+ s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6);
+ if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */
+ omap_gpmc_cs_map(&s->cs_file[i],
+ s->cs_file[i].config[6] & 0x1f, /* MASKADDR */
+ (s->cs_file[i].config[6] >> 8 & 0xf)); /* BASEADDR */
+ }
+ omap_gpmc_cs_map(s->cs_file, 0, 0xf);
+ s->ecc_cs = 0;
+ s->ecc_ptr = 0;
+ s->ecc_cfg = 0x3fcff000;
+ for (i = 0; i < 9; i ++)
+ ecc_reset(&s->ecc[i]);
+}
+
+static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
+ int cs;
+ struct omap_gpmc_cs_file_s *f;
+
+ switch (addr) {
+ case 0x000: /* GPMC_REVISION */
+ return 0x20;
+
+ case 0x010: /* GPMC_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x014: /* GPMC_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x018: /* GPMC_IRQSTATUS */
+ return s->irqst;
+
+ case 0x01c: /* GPMC_IRQENABLE */
+ return s->irqen;
+
+ case 0x040: /* GPMC_TIMEOUT_CONTROL */
+ return s->timeout;
+
+ case 0x044: /* GPMC_ERR_ADDRESS */
+ case 0x048: /* GPMC_ERR_TYPE */
+ return 0;
+
+ case 0x050: /* GPMC_CONFIG */
+ return s->config;
+
+ case 0x054: /* GPMC_STATUS */
+ return 0x001;
+
+ case 0x060 ... 0x1d4:
+ cs = (addr - 0x060) / 0x30;
+ addr -= cs * 0x30;
+ f = s->cs_file + cs;
+ switch (addr) {
+ case 0x60: /* GPMC_CONFIG1 */
+ return f->config[0];
+ case 0x64: /* GPMC_CONFIG2 */
+ return f->config[1];
+ case 0x68: /* GPMC_CONFIG3 */
+ return f->config[2];
+ case 0x6c: /* GPMC_CONFIG4 */
+ return f->config[3];
+ case 0x70: /* GPMC_CONFIG5 */
+ return f->config[4];
+ case 0x74: /* GPMC_CONFIG6 */
+ return f->config[5];
+ case 0x78: /* GPMC_CONFIG7 */
+ return f->config[6];
+ case 0x84: /* GPMC_NAND_DATA */
+ return 0;
+ }
+ break;
+
+ case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */
+ return s->prefconfig[0];
+ case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */
+ return s->prefconfig[1];
+ case 0x1ec: /* GPMC_PREFETCH_CONTROL */
+ return s->prefcontrol;
+ case 0x1f0: /* GPMC_PREFETCH_STATUS */
+ return (s->preffifo << 24) |
+ ((s->preffifo >
+ ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) |
+ s->prefcount;
+
+ case 0x1f4: /* GPMC_ECC_CONFIG */
+ return s->ecc_cs;
+ case 0x1f8: /* GPMC_ECC_CONTROL */
+ return s->ecc_ptr;
+ case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */
+ return s->ecc_cfg;
+ case 0x200 ... 0x220: /* GPMC_ECC_RESULT */
+ cs = (addr & 0x1f) >> 2;
+ /* TODO: check correctness */
+ return
+ ((s->ecc[cs].cp & 0x07) << 0) |
+ ((s->ecc[cs].cp & 0x38) << 13) |
+ ((s->ecc[cs].lp[0] & 0x1ff) << 3) |
+ ((s->ecc[cs].lp[1] & 0x1ff) << 19);
+
+ case 0x230: /* GPMC_TESTMODE_CTRL */
+ return 0;
+ case 0x234: /* GPMC_PSA_LSB */
+ case 0x238: /* GPMC_PSA_MSB */
+ return 0x00000000;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_gpmc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque;
+ int cs;
+ struct omap_gpmc_cs_file_s *f;
+
+ switch (addr) {
+ case 0x000: /* GPMC_REVISION */
+ case 0x014: /* GPMC_SYSSTATUS */
+ case 0x054: /* GPMC_STATUS */
+ case 0x1f0: /* GPMC_PREFETCH_STATUS */
+ case 0x200 ... 0x220: /* GPMC_ECC_RESULT */
+ case 0x234: /* GPMC_PSA_LSB */
+ case 0x238: /* GPMC_PSA_MSB */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x010: /* GPMC_SYSCONFIG */
+ if ((value >> 3) == 0x3)
+ fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
+ __FUNCTION__, value >> 3);
+ if (value & 2)
+ omap_gpmc_reset(s);
+ s->sysconfig = value & 0x19;
+ break;
+
+ case 0x018: /* GPMC_IRQSTATUS */
+ s->irqen = ~value;
+ omap_gpmc_int_update(s);
+ break;
+
+ case 0x01c: /* GPMC_IRQENABLE */
+ s->irqen = value & 0xf03;
+ omap_gpmc_int_update(s);
+ break;
+
+ case 0x040: /* GPMC_TIMEOUT_CONTROL */
+ s->timeout = value & 0x1ff1;
+ break;
+
+ case 0x044: /* GPMC_ERR_ADDRESS */
+ case 0x048: /* GPMC_ERR_TYPE */
+ break;
+
+ case 0x050: /* GPMC_CONFIG */
+ s->config = value & 0xf13;
+ break;
+
+ case 0x060 ... 0x1d4:
+ cs = (addr - 0x060) / 0x30;
+ addr -= cs * 0x30;
+ f = s->cs_file + cs;
+ switch (addr) {
+ case 0x60: /* GPMC_CONFIG1 */
+ f->config[0] = value & 0xffef3e13;
+ break;
+ case 0x64: /* GPMC_CONFIG2 */
+ f->config[1] = value & 0x001f1f8f;
+ break;
+ case 0x68: /* GPMC_CONFIG3 */
+ f->config[2] = value & 0x001f1f8f;
+ break;
+ case 0x6c: /* GPMC_CONFIG4 */
+ f->config[3] = value & 0x1f8f1f8f;
+ break;
+ case 0x70: /* GPMC_CONFIG5 */
+ f->config[4] = value & 0x0f1f1f1f;
+ break;
+ case 0x74: /* GPMC_CONFIG6 */
+ f->config[5] = value & 0x00000fcf;
+ break;
+ case 0x78: /* GPMC_CONFIG7 */
+ if ((f->config[6] ^ value) & 0xf7f) {
+ if (f->config[6] & (1 << 6)) /* CSVALID */
+ omap_gpmc_cs_unmap(f);
+ if (value & (1 << 6)) /* CSVALID */
+ omap_gpmc_cs_map(f, value & 0x1f, /* MASKADDR */
+ (value >> 8 & 0xf)); /* BASEADDR */
+ }
+ f->config[6] = value & 0x00000f7f;
+ break;
+ case 0x7c: /* GPMC_NAND_COMMAND */
+ case 0x80: /* GPMC_NAND_ADDRESS */
+ case 0x84: /* GPMC_NAND_DATA */
+ break;
+
+ default:
+ goto bad_reg;
+ }
+ break;
+
+ case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */
+ s->prefconfig[0] = value & 0x7f8f7fbf;
+ /* TODO: update interrupts, fifos, dmas */
+ break;
+
+ case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */
+ s->prefconfig[1] = value & 0x3fff;
+ break;
+
+ case 0x1ec: /* GPMC_PREFETCH_CONTROL */
+ s->prefcontrol = value & 1;
+ if (s->prefcontrol) {
+ if (s->prefconfig[0] & 1)
+ s->preffifo = 0x40;
+ else
+ s->preffifo = 0x00;
+ }
+ /* TODO: start */
+ break;
+
+ case 0x1f4: /* GPMC_ECC_CONFIG */
+ s->ecc_cs = 0x8f;
+ break;
+ case 0x1f8: /* GPMC_ECC_CONTROL */
+ if (value & (1 << 8))
+ for (cs = 0; cs < 9; cs ++)
+ ecc_reset(&s->ecc[cs]);
+ s->ecc_ptr = value & 0xf;
+ if (s->ecc_ptr == 0 || s->ecc_ptr > 9) {
+ s->ecc_ptr = 0;
+ s->ecc_cs &= ~1;
+ }
+ break;
+ case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */
+ s->ecc_cfg = value & 0x3fcff1ff;
+ break;
+ case 0x230: /* GPMC_TESTMODE_CTRL */
+ if (value & 7)
+ fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__);
+ break;
+
+ default:
+ bad_reg:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_gpmc_readfn[] = {
+ omap_badwidth_read32, /* TODO */
+ omap_badwidth_read32, /* TODO */
+ omap_gpmc_read,
+};
+
+static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = {
+ omap_badwidth_write32, /* TODO */
+ omap_badwidth_write32, /* TODO */
+ omap_gpmc_write,
+};
+
+struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq)
+{
+ int iomemtype;
+ struct omap_gpmc_s *s = (struct omap_gpmc_s *)
+ qemu_mallocz(sizeof(struct omap_gpmc_s));
+
+ omap_gpmc_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_gpmc_readfn,
+ omap_gpmc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x1000, iomemtype);
+
+ return s;
+}
+
+void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype,
+ void (*base_upd)(void *opaque, target_phys_addr_t new),
+ void (*unmap)(void *opaque), void *opaque)
+{
+ struct omap_gpmc_cs_file_s *f;
+
+ if (cs < 0 || cs >= 8) {
+ fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs);
+ exit(-1);
+ }
+ f = &s->cs_file[cs];
+
+ f->iomemtype = iomemtype;
+ f->base_update = base_upd;
+ f->unmap = unmap;
+ f->opaque = opaque;
+
+ if (f->config[6] & (1 << 6)) /* CSVALID */
+ omap_gpmc_cs_map(f, f->config[6] & 0x1f, /* MASKADDR */
+ (f->config[6] >> 8 & 0xf)); /* BASEADDR */
+}
diff --git a/hw/omap_gptimer.c b/hw/omap_gptimer.c
new file mode 100644
index 0000000..9c0f9f2
--- /dev/null
+++ b/hw/omap_gptimer.c
@@ -0,0 +1,484 @@
+/*
+ * TI OMAP2 general purpose timers emulation.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "omap.h"
+
+/* GP timers */
+struct omap_gp_timer_s {
+ qemu_irq irq;
+ qemu_irq wkup;
+ qemu_irq in;
+ qemu_irq out;
+ omap_clk clk;
+ QEMUTimer *timer;
+ QEMUTimer *match;
+ struct omap_target_agent_s *ta;
+
+ int in_val;
+ int out_val;
+ int64_t time;
+ int64_t rate;
+ int64_t ticks_per_sec;
+
+ int16_t config;
+ int status;
+ int it_ena;
+ int wu_ena;
+ int enable;
+ int inout;
+ int capt2;
+ int pt;
+ enum {
+ gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both
+ } trigger;
+ enum {
+ gpt_capture_none, gpt_capture_rising,
+ gpt_capture_falling, gpt_capture_both
+ } capture;
+ int scpwm;
+ int ce;
+ int pre;
+ int ptv;
+ int ar;
+ int st;
+ int posted;
+ uint32_t val;
+ uint32_t load_val;
+ uint32_t capture_val[2];
+ uint32_t match_val;
+ int capt_num;
+
+ uint16_t writeh; /* LSB */
+ uint16_t readh; /* MSB */
+};
+
+#define GPT_TCAR_IT (1 << 2)
+#define GPT_OVF_IT (1 << 1)
+#define GPT_MAT_IT (1 << 0)
+
+static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it)
+{
+ if (timer->it_ena & it) {
+ if (!timer->status)
+ qemu_irq_raise(timer->irq);
+
+ timer->status |= it;
+ /* Or are the status bits set even when masked?
+ * i.e. is masking applied before or after the status register? */
+ }
+
+ if (timer->wu_ena & it)
+ qemu_irq_pulse(timer->wkup);
+}
+
+static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level)
+{
+ if (!timer->inout && timer->out_val != level) {
+ timer->out_val = level;
+ qemu_set_irq(timer->out, level);
+ }
+}
+
+static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer)
+{
+ uint64_t distance;
+
+ if (timer->st && timer->rate) {
+ distance = qemu_get_clock(vm_clock) - timer->time;
+ distance = muldiv64(distance, timer->rate, timer->ticks_per_sec);
+
+ if (distance >= 0xffffffff - timer->val)
+ return 0xffffffff;
+ else
+ return timer->val + distance;
+ } else
+ return timer->val;
+}
+
+static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer)
+{
+ if (timer->st) {
+ timer->val = omap_gp_timer_read(timer);
+ timer->time = qemu_get_clock(vm_clock);
+ }
+}
+
+static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer)
+{
+ int64_t expires, matches;
+
+ if (timer->st && timer->rate) {
+ expires = muldiv64(0x100000000ll - timer->val,
+ timer->ticks_per_sec, timer->rate);
+ qemu_mod_timer(timer->timer, timer->time + expires);
+
+ if (timer->ce && timer->match_val >= timer->val) {
+ matches = muldiv64(timer->match_val - timer->val,
+ timer->ticks_per_sec, timer->rate);
+ qemu_mod_timer(timer->match, timer->time + matches);
+ } else
+ qemu_del_timer(timer->match);
+ } else {
+ qemu_del_timer(timer->timer);
+ qemu_del_timer(timer->match);
+ omap_gp_timer_out(timer, timer->scpwm);
+ }
+}
+
+static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer)
+{
+ if (timer->pt)
+ /* TODO in overflow-and-match mode if the first event to
+ * occur is the match, don't toggle. */
+ omap_gp_timer_out(timer, !timer->out_val);
+ else
+ /* TODO inverted pulse on timer->out_val == 1? */
+ qemu_irq_pulse(timer->out);
+}
+
+static void omap_gp_timer_tick(void *opaque)
+{
+ struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
+
+ if (!timer->ar) {
+ timer->st = 0;
+ timer->val = 0;
+ } else {
+ timer->val = timer->load_val;
+ timer->time = qemu_get_clock(vm_clock);
+ }
+
+ if (timer->trigger == gpt_trigger_overflow ||
+ timer->trigger == gpt_trigger_both)
+ omap_gp_timer_trigger(timer);
+
+ omap_gp_timer_intr(timer, GPT_OVF_IT);
+ omap_gp_timer_update(timer);
+}
+
+static void omap_gp_timer_match(void *opaque)
+{
+ struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
+
+ if (timer->trigger == gpt_trigger_both)
+ omap_gp_timer_trigger(timer);
+
+ omap_gp_timer_intr(timer, GPT_MAT_IT);
+}
+
+static void omap_gp_timer_input(void *opaque, int line, int on)
+{
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
+ int trigger;
+
+ switch (s->capture) {
+ default:
+ case gpt_capture_none:
+ trigger = 0;
+ break;
+ case gpt_capture_rising:
+ trigger = !s->in_val && on;
+ break;
+ case gpt_capture_falling:
+ trigger = s->in_val && !on;
+ break;
+ case gpt_capture_both:
+ trigger = (s->in_val == !on);
+ break;
+ }
+ s->in_val = on;
+
+ if (s->inout && trigger && s->capt_num < 2) {
+ s->capture_val[s->capt_num] = omap_gp_timer_read(s);
+
+ if (s->capt2 == s->capt_num ++)
+ omap_gp_timer_intr(s, GPT_TCAR_IT);
+ }
+}
+
+static void omap_gp_timer_clk_update(void *opaque, int line, int on)
+{
+ struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque;
+
+ omap_gp_timer_sync(timer);
+ timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
+ omap_gp_timer_update(timer);
+}
+
+static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer)
+{
+ omap_clk_adduser(timer->clk,
+ qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]);
+ timer->rate = omap_clk_getrate(timer->clk);
+}
+
+void omap_gp_timer_reset(struct omap_gp_timer_s *s)
+{
+ s->config = 0x000;
+ s->status = 0;
+ s->it_ena = 0;
+ s->wu_ena = 0;
+ s->inout = 0;
+ s->capt2 = 0;
+ s->capt_num = 0;
+ s->pt = 0;
+ s->trigger = gpt_trigger_none;
+ s->capture = gpt_capture_none;
+ s->scpwm = 0;
+ s->ce = 0;
+ s->pre = 0;
+ s->ptv = 0;
+ s->ar = 0;
+ s->st = 0;
+ s->posted = 1;
+ s->val = 0x00000000;
+ s->load_val = 0x00000000;
+ s->capture_val[0] = 0x00000000;
+ s->capture_val[1] = 0x00000000;
+ s->match_val = 0x00000000;
+ omap_gp_timer_update(s);
+}
+
+static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* TIDR */
+ return 0x21;
+
+ case 0x10: /* TIOCP_CFG */
+ return s->config;
+
+ case 0x14: /* TISTAT */
+ /* ??? When's this bit reset? */
+ return 1; /* RESETDONE */
+
+ case 0x18: /* TISR */
+ return s->status;
+
+ case 0x1c: /* TIER */
+ return s->it_ena;
+
+ case 0x20: /* TWER */
+ return s->wu_ena;
+
+ case 0x24: /* TCLR */
+ return (s->inout << 14) |
+ (s->capt2 << 13) |
+ (s->pt << 12) |
+ (s->trigger << 10) |
+ (s->capture << 8) |
+ (s->scpwm << 7) |
+ (s->ce << 6) |
+ (s->pre << 5) |
+ (s->ptv << 2) |
+ (s->ar << 1) |
+ (s->st << 0);
+
+ case 0x28: /* TCRR */
+ return omap_gp_timer_read(s);
+
+ case 0x2c: /* TLDR */
+ return s->load_val;
+
+ case 0x30: /* TTGR */
+ return 0xffffffff;
+
+ case 0x34: /* TWPS */
+ return 0x00000000; /* No posted writes pending. */
+
+ case 0x38: /* TMAR */
+ return s->match_val;
+
+ case 0x3c: /* TCAR1 */
+ return s->capture_val[0];
+
+ case 0x40: /* TSICR */
+ return s->posted << 2;
+
+ case 0x44: /* TCAR2 */
+ return s->capture_val[1];
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
+ uint32_t ret;
+
+ if (addr & 2)
+ return s->readh;
+ else {
+ ret = omap_gp_timer_readw(opaque, addr);
+ s->readh = ret >> 16;
+ return ret & 0xffff;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_gp_timer_readfn[] = {
+ omap_badwidth_read32,
+ omap_gp_timer_readh,
+ omap_gp_timer_readw,
+};
+
+static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* TIDR */
+ case 0x14: /* TISTAT */
+ case 0x34: /* TWPS */
+ case 0x3c: /* TCAR1 */
+ case 0x44: /* TCAR2 */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x10: /* TIOCP_CFG */
+ s->config = value & 0x33d;
+ if (((value >> 3) & 3) == 3) /* IDLEMODE */
+ fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
+ __FUNCTION__);
+ if (value & 2) /* SOFTRESET */
+ omap_gp_timer_reset(s);
+ break;
+
+ case 0x18: /* TISR */
+ if (value & GPT_TCAR_IT)
+ s->capt_num = 0;
+ if (s->status && !(s->status &= ~value))
+ qemu_irq_lower(s->irq);
+ break;
+
+ case 0x1c: /* TIER */
+ s->it_ena = value & 7;
+ break;
+
+ case 0x20: /* TWER */
+ s->wu_ena = value & 7;
+ break;
+
+ case 0x24: /* TCLR */
+ omap_gp_timer_sync(s);
+ s->inout = (value >> 14) & 1;
+ s->capt2 = (value >> 13) & 1;
+ s->pt = (value >> 12) & 1;
+ s->trigger = (value >> 10) & 3;
+ if (s->capture == gpt_capture_none &&
+ ((value >> 8) & 3) != gpt_capture_none)
+ s->capt_num = 0;
+ s->capture = (value >> 8) & 3;
+ s->scpwm = (value >> 7) & 1;
+ s->ce = (value >> 6) & 1;
+ s->pre = (value >> 5) & 1;
+ s->ptv = (value >> 2) & 7;
+ s->ar = (value >> 1) & 1;
+ s->st = (value >> 0) & 1;
+ if (s->inout && s->trigger != gpt_trigger_none)
+ fprintf(stderr, "%s: GP timer pin must be an output "
+ "for this trigger mode\n", __FUNCTION__);
+ if (!s->inout && s->capture != gpt_capture_none)
+ fprintf(stderr, "%s: GP timer pin must be an input "
+ "for this capture mode\n", __FUNCTION__);
+ if (s->trigger == gpt_trigger_none)
+ omap_gp_timer_out(s, s->scpwm);
+ /* TODO: make sure this doesn't overflow 32-bits */
+ s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0);
+ omap_gp_timer_update(s);
+ break;
+
+ case 0x28: /* TCRR */
+ s->time = qemu_get_clock(vm_clock);
+ s->val = value;
+ omap_gp_timer_update(s);
+ break;
+
+ case 0x2c: /* TLDR */
+ s->load_val = value;
+ break;
+
+ case 0x30: /* TTGR */
+ s->time = qemu_get_clock(vm_clock);
+ s->val = s->load_val;
+ omap_gp_timer_update(s);
+ break;
+
+ case 0x38: /* TMAR */
+ omap_gp_timer_sync(s);
+ s->match_val = value;
+ omap_gp_timer_update(s);
+ break;
+
+ case 0x40: /* TSICR */
+ s->posted = (value >> 2) & 1;
+ if (value & 2) /* How much exactly are we supposed to reset? */
+ omap_gp_timer_reset(s);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque;
+
+ if (addr & 2)
+ return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh);
+ else
+ s->writeh = (uint16_t) value;
+}
+
+static CPUWriteMemoryFunc * const omap_gp_timer_writefn[] = {
+ omap_badwidth_write32,
+ omap_gp_timer_writeh,
+ omap_gp_timer_write,
+};
+
+struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+ struct omap_gp_timer_s *s = (struct omap_gp_timer_s *)
+ qemu_mallocz(sizeof(struct omap_gp_timer_s));
+
+ s->ta = ta;
+ s->irq = irq;
+ s->clk = fclk;
+ s->timer = qemu_new_timer(vm_clock, omap_gp_timer_tick, s);
+ s->match = qemu_new_timer(vm_clock, omap_gp_timer_match, s);
+ s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0];
+ omap_gp_timer_reset(s);
+ omap_gp_timer_clk_setup(s);
+
+ iomemtype = l4_register_io_memory(omap_gp_timer_readfn,
+ omap_gp_timer_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ return s;
+}
diff --git a/hw/omap_i2c.c b/hw/omap_i2c.c
new file mode 100644
index 0000000..5cabb5a
--- /dev/null
+++ b/hw/omap_i2c.c
@@ -0,0 +1,470 @@
+/*
+ * TI OMAP on-chip I2C controller. Only "new I2C" mode supported.
+ *
+ * Copyright (C) 2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "i2c.h"
+#include "omap.h"
+
+struct omap_i2c_s {
+ qemu_irq irq;
+ qemu_irq drq[2];
+ i2c_bus *bus;
+
+ uint8_t revision;
+ uint8_t mask;
+ uint16_t stat;
+ uint16_t dma;
+ uint16_t count;
+ int count_cur;
+ uint32_t fifo;
+ int rxlen;
+ int txlen;
+ uint16_t control;
+ uint16_t addr[2];
+ uint8_t divider;
+ uint8_t times[2];
+ uint16_t test;
+};
+
+#define OMAP2_INTR_REV 0x34
+#define OMAP2_GC_REV 0x34
+
+static void omap_i2c_interrupts_update(struct omap_i2c_s *s)
+{
+ qemu_set_irq(s->irq, s->stat & s->mask);
+ if ((s->dma >> 15) & 1) /* RDMA_EN */
+ qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */
+ if ((s->dma >> 7) & 1) /* XDMA_EN */
+ qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */
+}
+
+static void omap_i2c_fifo_run(struct omap_i2c_s *s)
+{
+ int ack = 1;
+
+ if (!i2c_bus_busy(s->bus))
+ return;
+
+ if ((s->control >> 2) & 1) { /* RM */
+ if ((s->control >> 1) & 1) { /* STP */
+ i2c_end_transfer(s->bus);
+ s->control &= ~(1 << 1); /* STP */
+ s->count_cur = s->count;
+ s->txlen = 0;
+ } else if ((s->control >> 9) & 1) { /* TRX */
+ while (ack && s->txlen)
+ ack = (i2c_send(s->bus,
+ (s->fifo >> ((-- s->txlen) << 3)) &
+ 0xff) >= 0);
+ s->stat |= 1 << 4; /* XRDY */
+ } else {
+ while (s->rxlen < 4)
+ s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3);
+ s->stat |= 1 << 3; /* RRDY */
+ }
+ } else {
+ if ((s->control >> 9) & 1) { /* TRX */
+ while (ack && s->count_cur && s->txlen) {
+ ack = (i2c_send(s->bus,
+ (s->fifo >> ((-- s->txlen) << 3)) &
+ 0xff) >= 0);
+ s->count_cur --;
+ }
+ if (ack && s->count_cur)
+ s->stat |= 1 << 4; /* XRDY */
+ else
+ s->stat &= ~(1 << 4); /* XRDY */
+ if (!s->count_cur) {
+ s->stat |= 1 << 2; /* ARDY */
+ s->control &= ~(1 << 10); /* MST */
+ }
+ } else {
+ while (s->count_cur && s->rxlen < 4) {
+ s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3);
+ s->count_cur --;
+ }
+ if (s->rxlen)
+ s->stat |= 1 << 3; /* RRDY */
+ else
+ s->stat &= ~(1 << 3); /* RRDY */
+ }
+ if (!s->count_cur) {
+ if ((s->control >> 1) & 1) { /* STP */
+ i2c_end_transfer(s->bus);
+ s->control &= ~(1 << 1); /* STP */
+ s->count_cur = s->count;
+ s->txlen = 0;
+ } else {
+ s->stat |= 1 << 2; /* ARDY */
+ s->control &= ~(1 << 10); /* MST */
+ }
+ }
+ }
+
+ s->stat |= (!ack) << 1; /* NACK */
+ if (!ack)
+ s->control &= ~(1 << 1); /* STP */
+}
+
+void omap_i2c_reset(struct omap_i2c_s *s)
+{
+ s->mask = 0;
+ s->stat = 0;
+ s->dma = 0;
+ s->count = 0;
+ s->count_cur = 0;
+ s->fifo = 0;
+ s->rxlen = 0;
+ s->txlen = 0;
+ s->control = 0;
+ s->addr[0] = 0;
+ s->addr[1] = 0;
+ s->divider = 0;
+ s->times[0] = 0;
+ s->times[1] = 0;
+ s->test = 0;
+}
+
+static uint32_t omap_i2c_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ uint16_t ret;
+
+ switch (offset) {
+ case 0x00: /* I2C_REV */
+ return s->revision; /* REV */
+
+ case 0x04: /* I2C_IE */
+ return s->mask;
+
+ case 0x08: /* I2C_STAT */
+ return s->stat | (i2c_bus_busy(s->bus) << 12);
+
+ case 0x0c: /* I2C_IV */
+ if (s->revision >= OMAP2_INTR_REV)
+ break;
+ ret = ffs(s->stat & s->mask);
+ if (ret)
+ s->stat ^= 1 << (ret - 1);
+ omap_i2c_interrupts_update(s);
+ return ret;
+
+ case 0x10: /* I2C_SYSS */
+ return (s->control >> 15) & 1; /* I2C_EN */
+
+ case 0x14: /* I2C_BUF */
+ return s->dma;
+
+ case 0x18: /* I2C_CNT */
+ return s->count_cur; /* DCOUNT */
+
+ case 0x1c: /* I2C_DATA */
+ ret = 0;
+ if (s->control & (1 << 14)) { /* BE */
+ ret |= ((s->fifo >> 0) & 0xff) << 8;
+ ret |= ((s->fifo >> 8) & 0xff) << 0;
+ } else {
+ ret |= ((s->fifo >> 8) & 0xff) << 8;
+ ret |= ((s->fifo >> 0) & 0xff) << 0;
+ }
+ if (s->rxlen == 1) {
+ s->stat |= 1 << 15; /* SBD */
+ s->rxlen = 0;
+ } else if (s->rxlen > 1) {
+ if (s->rxlen > 2)
+ s->fifo >>= 16;
+ s->rxlen -= 2;
+ } else {
+ /* XXX: remote access (qualifier) error - what's that? */
+ }
+ if (!s->rxlen) {
+ s->stat &= ~(1 << 3); /* RRDY */
+ if (((s->control >> 10) & 1) && /* MST */
+ ((~s->control >> 9) & 1)) { /* TRX */
+ s->stat |= 1 << 2; /* ARDY */
+ s->control &= ~(1 << 10); /* MST */
+ }
+ }
+ s->stat &= ~(1 << 11); /* ROVR */
+ omap_i2c_fifo_run(s);
+ omap_i2c_interrupts_update(s);
+ return ret;
+
+ case 0x20: /* I2C_SYSC */
+ return 0;
+
+ case 0x24: /* I2C_CON */
+ return s->control;
+
+ case 0x28: /* I2C_OA */
+ return s->addr[0];
+
+ case 0x2c: /* I2C_SA */
+ return s->addr[1];
+
+ case 0x30: /* I2C_PSC */
+ return s->divider;
+
+ case 0x34: /* I2C_SCLL */
+ return s->times[0];
+
+ case 0x38: /* I2C_SCLH */
+ return s->times[1];
+
+ case 0x3c: /* I2C_SYSTEST */
+ if (s->test & (1 << 15)) { /* ST_EN */
+ s->test ^= 0xa;
+ return s->test;
+ } else
+ return s->test & ~0x300f;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_i2c_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+ int nack;
+
+ switch (offset) {
+ case 0x00: /* I2C_REV */
+ case 0x0c: /* I2C_IV */
+ case 0x10: /* I2C_SYSS */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x04: /* I2C_IE */
+ s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f);
+ break;
+
+ case 0x08: /* I2C_STAT */
+ if (s->revision < OMAP2_INTR_REV) {
+ OMAP_RO_REG(addr);
+ return;
+ }
+
+ /* RRDY and XRDY are reset by hardware. (in all versions???) */
+ s->stat &= ~(value & 0x27);
+ omap_i2c_interrupts_update(s);
+ break;
+
+ case 0x14: /* I2C_BUF */
+ s->dma = value & 0x8080;
+ if (value & (1 << 15)) /* RDMA_EN */
+ s->mask &= ~(1 << 3); /* RRDY_IE */
+ if (value & (1 << 7)) /* XDMA_EN */
+ s->mask &= ~(1 << 4); /* XRDY_IE */
+ break;
+
+ case 0x18: /* I2C_CNT */
+ s->count = value; /* DCOUNT */
+ break;
+
+ case 0x1c: /* I2C_DATA */
+ if (s->txlen > 2) {
+ /* XXX: remote access (qualifier) error - what's that? */
+ break;
+ }
+ s->fifo <<= 16;
+ s->txlen += 2;
+ if (s->control & (1 << 14)) { /* BE */
+ s->fifo |= ((value >> 8) & 0xff) << 8;
+ s->fifo |= ((value >> 0) & 0xff) << 0;
+ } else {
+ s->fifo |= ((value >> 0) & 0xff) << 8;
+ s->fifo |= ((value >> 8) & 0xff) << 0;
+ }
+ s->stat &= ~(1 << 10); /* XUDF */
+ if (s->txlen > 2)
+ s->stat &= ~(1 << 4); /* XRDY */
+ omap_i2c_fifo_run(s);
+ omap_i2c_interrupts_update(s);
+ break;
+
+ case 0x20: /* I2C_SYSC */
+ if (s->revision < OMAP2_INTR_REV) {
+ OMAP_BAD_REG(addr);
+ return;
+ }
+
+ if (value & 2)
+ omap_i2c_reset(s);
+ break;
+
+ case 0x24: /* I2C_CON */
+ s->control = value & 0xcf87;
+ if (~value & (1 << 15)) { /* I2C_EN */
+ if (s->revision < OMAP2_INTR_REV)
+ omap_i2c_reset(s);
+ break;
+ }
+ if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */
+ fprintf(stderr, "%s: I^2C slave mode not supported\n",
+ __FUNCTION__);
+ break;
+ }
+ if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */
+ fprintf(stderr, "%s: 10-bit addressing mode not supported\n",
+ __FUNCTION__);
+ break;
+ }
+ if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */
+ nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */
+ (~value >> 9) & 1); /* TRX */
+ s->stat |= nack << 1; /* NACK */
+ s->control &= ~(1 << 0); /* STT */
+ s->fifo = 0;
+ if (nack)
+ s->control &= ~(1 << 1); /* STP */
+ else {
+ s->count_cur = s->count;
+ omap_i2c_fifo_run(s);
+ }
+ omap_i2c_interrupts_update(s);
+ }
+ break;
+
+ case 0x28: /* I2C_OA */
+ s->addr[0] = value & 0x3ff;
+ break;
+
+ case 0x2c: /* I2C_SA */
+ s->addr[1] = value & 0x3ff;
+ break;
+
+ case 0x30: /* I2C_PSC */
+ s->divider = value;
+ break;
+
+ case 0x34: /* I2C_SCLL */
+ s->times[0] = value;
+ break;
+
+ case 0x38: /* I2C_SCLH */
+ s->times[1] = value;
+ break;
+
+ case 0x3c: /* I2C_SYSTEST */
+ s->test = value & 0xf80f;
+ if (value & (1 << 11)) /* SBB */
+ if (s->revision >= OMAP2_INTR_REV) {
+ s->stat |= 0x3f;
+ omap_i2c_interrupts_update(s);
+ }
+ if (value & (1 << 15)) /* ST_EN */
+ fprintf(stderr, "%s: System Test not supported\n", __FUNCTION__);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static void omap_i2c_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_i2c_s *s = (struct omap_i2c_s *) opaque;
+ int offset = addr & OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x1c: /* I2C_DATA */
+ if (s->txlen > 2) {
+ /* XXX: remote access (qualifier) error - what's that? */
+ break;
+ }
+ s->fifo <<= 8;
+ s->txlen += 1;
+ s->fifo |= value & 0xff;
+ s->stat &= ~(1 << 10); /* XUDF */
+ if (s->txlen > 2)
+ s->stat &= ~(1 << 4); /* XRDY */
+ omap_i2c_fifo_run(s);
+ omap_i2c_interrupts_update(s);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_i2c_readfn[] = {
+ omap_badwidth_read16,
+ omap_i2c_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_i2c_writefn[] = {
+ omap_i2c_writeb, /* Only the last fifo write can be 8 bit. */
+ omap_i2c_write,
+ omap_badwidth_write16,
+};
+
+struct omap_i2c_s *omap_i2c_init(target_phys_addr_t base,
+ qemu_irq irq, qemu_irq *dma, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_i2c_s *s = (struct omap_i2c_s *)
+ qemu_mallocz(sizeof(struct omap_i2c_s));
+
+ /* TODO: set a value greater or equal to real hardware */
+ s->revision = 0x11;
+ s->irq = irq;
+ s->drq[0] = dma[0];
+ s->drq[1] = dma[1];
+ s->bus = i2c_init_bus(NULL, "i2c");
+ omap_i2c_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_i2c_readfn,
+ omap_i2c_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ return s;
+}
+
+struct omap_i2c_s *omap2_i2c_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, qemu_irq *dma, omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+ struct omap_i2c_s *s = (struct omap_i2c_s *)
+ qemu_mallocz(sizeof(struct omap_i2c_s));
+
+ s->revision = 0x34;
+ s->irq = irq;
+ s->drq[0] = dma[0];
+ s->drq[1] = dma[1];
+ s->bus = i2c_init_bus(NULL, "i2c");
+ omap_i2c_reset(s);
+
+ iomemtype = l4_register_io_memory(omap_i2c_readfn,
+ omap_i2c_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ return s;
+}
+
+i2c_bus *omap_i2c_bus(struct omap_i2c_s *s)
+{
+ return s->bus;
+}
diff --git a/hw/omap_intc.c b/hw/omap_intc.c
new file mode 100644
index 0000000..001e20b
--- /dev/null
+++ b/hw/omap_intc.c
@@ -0,0 +1,598 @@
+/*
+ * TI OMAP interrupt controller emulation.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2007-2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "omap.h"
+
+/* Interrupt Handlers */
+struct omap_intr_handler_bank_s {
+ uint32_t irqs;
+ uint32_t inputs;
+ uint32_t mask;
+ uint32_t fiq;
+ uint32_t sens_edge;
+ uint32_t swi;
+ unsigned char priority[32];
+};
+
+struct omap_intr_handler_s {
+ qemu_irq *pins;
+ qemu_irq parent_intr[2];
+ unsigned char nbanks;
+ int level_only;
+
+ /* state */
+ uint32_t new_agr[2];
+ int sir_intr[2];
+ int autoidle;
+ uint32_t mask;
+ struct omap_intr_handler_bank_s bank[];
+};
+
+inline qemu_irq omap_inth_get_pin(struct omap_intr_handler_s *s, int n)
+{
+ return s->pins[n];
+}
+
+static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq)
+{
+ int i, j, sir_intr, p_intr, p, f;
+ uint32_t level;
+ sir_intr = 0;
+ p_intr = 255;
+
+ /* Find the interrupt line with the highest dynamic priority.
+ * Note: 0 denotes the hightest priority.
+ * If all interrupts have the same priority, the default order is IRQ_N,
+ * IRQ_N-1,...,IRQ_0. */
+ for (j = 0; j < s->nbanks; ++j) {
+ level = s->bank[j].irqs & ~s->bank[j].mask &
+ (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq);
+ for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f,
+ level >>= f) {
+ p = s->bank[j].priority[i];
+ if (p <= p_intr) {
+ p_intr = p;
+ sir_intr = 32 * j + i;
+ }
+ f = ffs(level >> 1);
+ }
+ }
+ s->sir_intr[is_fiq] = sir_intr;
+}
+
+static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq)
+{
+ int i;
+ uint32_t has_intr = 0;
+
+ for (i = 0; i < s->nbanks; ++i)
+ has_intr |= s->bank[i].irqs & ~s->bank[i].mask &
+ (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq);
+
+ if (s->new_agr[is_fiq] & has_intr & s->mask) {
+ s->new_agr[is_fiq] = 0;
+ omap_inth_sir_update(s, is_fiq);
+ qemu_set_irq(s->parent_intr[is_fiq], 1);
+ }
+}
+
+#define INT_FALLING_EDGE 0
+#define INT_LOW_LEVEL 1
+
+static void omap_set_intr(void *opaque, int irq, int req)
+{
+ struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
+ uint32_t rise;
+
+ struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
+ int n = irq & 31;
+
+ if (req) {
+ rise = ~bank->irqs & (1 << n);
+ if (~bank->sens_edge & (1 << n))
+ rise &= ~bank->inputs;
+
+ bank->inputs |= (1 << n);
+ if (rise) {
+ bank->irqs |= rise;
+ omap_inth_update(ih, 0);
+ omap_inth_update(ih, 1);
+ }
+ } else {
+ rise = bank->sens_edge & bank->irqs & (1 << n);
+ bank->irqs &= ~rise;
+ bank->inputs &= ~(1 << n);
+ }
+}
+
+/* Simplified version with no edge detection */
+static void omap_set_intr_noedge(void *opaque, int irq, int req)
+{
+ struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque;
+ uint32_t rise;
+
+ struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5];
+ int n = irq & 31;
+
+ if (req) {
+ rise = ~bank->inputs & (1 << n);
+ if (rise) {
+ bank->irqs |= bank->inputs |= rise;
+ omap_inth_update(ih, 0);
+ omap_inth_update(ih, 1);
+ }
+ } else
+ bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi;
+}
+
+static uint32_t omap_inth_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+ int i, offset = addr;
+ int bank_no = offset >> 8;
+ int line_no;
+ struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
+ offset &= 0xff;
+
+ switch (offset) {
+ case 0x00: /* ITR */
+ return bank->irqs;
+
+ case 0x04: /* MIR */
+ return bank->mask;
+
+ case 0x10: /* SIR_IRQ_CODE */
+ case 0x14: /* SIR_FIQ_CODE */
+ if (bank_no != 0)
+ break;
+ line_no = s->sir_intr[(offset - 0x10) >> 2];
+ bank = &s->bank[line_no >> 5];
+ i = line_no & 31;
+ if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE)
+ bank->irqs &= ~(1 << i);
+ return line_no;
+
+ case 0x18: /* CONTROL_REG */
+ if (bank_no != 0)
+ break;
+ return 0;
+
+ case 0x1c: /* ILR0 */
+ case 0x20: /* ILR1 */
+ case 0x24: /* ILR2 */
+ case 0x28: /* ILR3 */
+ case 0x2c: /* ILR4 */
+ case 0x30: /* ILR5 */
+ case 0x34: /* ILR6 */
+ case 0x38: /* ILR7 */
+ case 0x3c: /* ILR8 */
+ case 0x40: /* ILR9 */
+ case 0x44: /* ILR10 */
+ case 0x48: /* ILR11 */
+ case 0x4c: /* ILR12 */
+ case 0x50: /* ILR13 */
+ case 0x54: /* ILR14 */
+ case 0x58: /* ILR15 */
+ case 0x5c: /* ILR16 */
+ case 0x60: /* ILR17 */
+ case 0x64: /* ILR18 */
+ case 0x68: /* ILR19 */
+ case 0x6c: /* ILR20 */
+ case 0x70: /* ILR21 */
+ case 0x74: /* ILR22 */
+ case 0x78: /* ILR23 */
+ case 0x7c: /* ILR24 */
+ case 0x80: /* ILR25 */
+ case 0x84: /* ILR26 */
+ case 0x88: /* ILR27 */
+ case 0x8c: /* ILR28 */
+ case 0x90: /* ILR29 */
+ case 0x94: /* ILR30 */
+ case 0x98: /* ILR31 */
+ i = (offset - 0x1c) >> 2;
+ return (bank->priority[i] << 2) |
+ (((bank->sens_edge >> i) & 1) << 1) |
+ ((bank->fiq >> i) & 1);
+
+ case 0x9c: /* ISR */
+ return 0x00000000;
+
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_inth_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+ int i, offset = addr;
+ int bank_no = offset >> 8;
+ struct omap_intr_handler_bank_s *bank = &s->bank[bank_no];
+ offset &= 0xff;
+
+ switch (offset) {
+ case 0x00: /* ITR */
+ /* Important: ignore the clearing if the IRQ is level-triggered and
+ the input bit is 1 */
+ bank->irqs &= value | (bank->inputs & bank->sens_edge);
+ return;
+
+ case 0x04: /* MIR */
+ bank->mask = value;
+ omap_inth_update(s, 0);
+ omap_inth_update(s, 1);
+ return;
+
+ case 0x10: /* SIR_IRQ_CODE */
+ case 0x14: /* SIR_FIQ_CODE */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x18: /* CONTROL_REG */
+ if (bank_no != 0)
+ break;
+ if (value & 2) {
+ qemu_set_irq(s->parent_intr[1], 0);
+ s->new_agr[1] = ~0;
+ omap_inth_update(s, 1);
+ }
+ if (value & 1) {
+ qemu_set_irq(s->parent_intr[0], 0);
+ s->new_agr[0] = ~0;
+ omap_inth_update(s, 0);
+ }
+ return;
+
+ case 0x1c: /* ILR0 */
+ case 0x20: /* ILR1 */
+ case 0x24: /* ILR2 */
+ case 0x28: /* ILR3 */
+ case 0x2c: /* ILR4 */
+ case 0x30: /* ILR5 */
+ case 0x34: /* ILR6 */
+ case 0x38: /* ILR7 */
+ case 0x3c: /* ILR8 */
+ case 0x40: /* ILR9 */
+ case 0x44: /* ILR10 */
+ case 0x48: /* ILR11 */
+ case 0x4c: /* ILR12 */
+ case 0x50: /* ILR13 */
+ case 0x54: /* ILR14 */
+ case 0x58: /* ILR15 */
+ case 0x5c: /* ILR16 */
+ case 0x60: /* ILR17 */
+ case 0x64: /* ILR18 */
+ case 0x68: /* ILR19 */
+ case 0x6c: /* ILR20 */
+ case 0x70: /* ILR21 */
+ case 0x74: /* ILR22 */
+ case 0x78: /* ILR23 */
+ case 0x7c: /* ILR24 */
+ case 0x80: /* ILR25 */
+ case 0x84: /* ILR26 */
+ case 0x88: /* ILR27 */
+ case 0x8c: /* ILR28 */
+ case 0x90: /* ILR29 */
+ case 0x94: /* ILR30 */
+ case 0x98: /* ILR31 */
+ i = (offset - 0x1c) >> 2;
+ bank->priority[i] = (value >> 2) & 0x1f;
+ bank->sens_edge &= ~(1 << i);
+ bank->sens_edge |= ((value >> 1) & 1) << i;
+ bank->fiq &= ~(1 << i);
+ bank->fiq |= (value & 1) << i;
+ return;
+
+ case 0x9c: /* ISR */
+ for (i = 0; i < 32; i ++)
+ if (value & (1 << i)) {
+ omap_set_intr(s, 32 * bank_no + i, 1);
+ return;
+ }
+ return;
+ }
+ OMAP_BAD_REG(addr);
+}
+
+static CPUReadMemoryFunc * const omap_inth_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_inth_read,
+};
+
+static CPUWriteMemoryFunc * const omap_inth_writefn[] = {
+ omap_inth_write,
+ omap_inth_write,
+ omap_inth_write,
+};
+
+void omap_inth_reset(struct omap_intr_handler_s *s)
+{
+ int i;
+
+ for (i = 0; i < s->nbanks; ++i){
+ s->bank[i].irqs = 0x00000000;
+ s->bank[i].mask = 0xffffffff;
+ s->bank[i].sens_edge = 0x00000000;
+ s->bank[i].fiq = 0x00000000;
+ s->bank[i].inputs = 0x00000000;
+ s->bank[i].swi = 0x00000000;
+ memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority));
+
+ if (s->level_only)
+ s->bank[i].sens_edge = 0xffffffff;
+ }
+
+ s->new_agr[0] = ~0;
+ s->new_agr[1] = ~0;
+ s->sir_intr[0] = 0;
+ s->sir_intr[1] = 0;
+ s->autoidle = 0;
+ s->mask = ~0;
+
+ qemu_set_irq(s->parent_intr[0], 0);
+ qemu_set_irq(s->parent_intr[1], 0);
+}
+
+struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base,
+ unsigned long size, unsigned char nbanks, qemu_irq **pins,
+ qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_intr_handler_s *s = (struct omap_intr_handler_s *)
+ qemu_mallocz(sizeof(struct omap_intr_handler_s) +
+ sizeof(struct omap_intr_handler_bank_s) * nbanks);
+
+ s->parent_intr[0] = parent_irq;
+ s->parent_intr[1] = parent_fiq;
+ s->nbanks = nbanks;
+ s->pins = qemu_allocate_irqs(omap_set_intr, s, nbanks * 32);
+ if (pins)
+ *pins = s->pins;
+
+ omap_inth_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_inth_readfn,
+ omap_inth_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, size, iomemtype);
+
+ return s;
+}
+
+static uint32_t omap2_inth_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+ int offset = addr;
+ int bank_no, line_no;
+ struct omap_intr_handler_bank_s *bank = NULL;
+
+ if ((offset & 0xf80) == 0x80) {
+ bank_no = (offset & 0x60) >> 5;
+ if (bank_no < s->nbanks) {
+ offset &= ~0x60;
+ bank = &s->bank[bank_no];
+ }
+ }
+
+ switch (offset) {
+ case 0x00: /* INTC_REVISION */
+ return 0x21;
+
+ case 0x10: /* INTC_SYSCONFIG */
+ return (s->autoidle >> 2) & 1;
+
+ case 0x14: /* INTC_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x40: /* INTC_SIR_IRQ */
+ return s->sir_intr[0];
+
+ case 0x44: /* INTC_SIR_FIQ */
+ return s->sir_intr[1];
+
+ case 0x48: /* INTC_CONTROL */
+ return (!s->mask) << 2; /* GLOBALMASK */
+
+ case 0x4c: /* INTC_PROTECTION */
+ return 0;
+
+ case 0x50: /* INTC_IDLE */
+ return s->autoidle & 3;
+
+ /* Per-bank registers */
+ case 0x80: /* INTC_ITR */
+ return bank->inputs;
+
+ case 0x84: /* INTC_MIR */
+ return bank->mask;
+
+ case 0x88: /* INTC_MIR_CLEAR */
+ case 0x8c: /* INTC_MIR_SET */
+ return 0;
+
+ case 0x90: /* INTC_ISR_SET */
+ return bank->swi;
+
+ case 0x94: /* INTC_ISR_CLEAR */
+ return 0;
+
+ case 0x98: /* INTC_PENDING_IRQ */
+ return bank->irqs & ~bank->mask & ~bank->fiq;
+
+ case 0x9c: /* INTC_PENDING_FIQ */
+ return bank->irqs & ~bank->mask & bank->fiq;
+
+ /* Per-line registers */
+ case 0x100 ... 0x300: /* INTC_ILR */
+ bank_no = (offset - 0x100) >> 7;
+ if (bank_no > s->nbanks)
+ break;
+ bank = &s->bank[bank_no];
+ line_no = (offset & 0x7f) >> 2;
+ return (bank->priority[line_no] << 2) |
+ ((bank->fiq >> line_no) & 1);
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap2_inth_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque;
+ int offset = addr;
+ int bank_no, line_no;
+ struct omap_intr_handler_bank_s *bank = NULL;
+
+ if ((offset & 0xf80) == 0x80) {
+ bank_no = (offset & 0x60) >> 5;
+ if (bank_no < s->nbanks) {
+ offset &= ~0x60;
+ bank = &s->bank[bank_no];
+ }
+ }
+
+ switch (offset) {
+ case 0x10: /* INTC_SYSCONFIG */
+ s->autoidle &= 4;
+ s->autoidle |= (value & 1) << 2;
+ if (value & 2) /* SOFTRESET */
+ omap_inth_reset(s);
+ return;
+
+ case 0x48: /* INTC_CONTROL */
+ s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */
+ if (value & 2) { /* NEWFIQAGR */
+ qemu_set_irq(s->parent_intr[1], 0);
+ s->new_agr[1] = ~0;
+ omap_inth_update(s, 1);
+ }
+ if (value & 1) { /* NEWIRQAGR */
+ qemu_set_irq(s->parent_intr[0], 0);
+ s->new_agr[0] = ~0;
+ omap_inth_update(s, 0);
+ }
+ return;
+
+ case 0x4c: /* INTC_PROTECTION */
+ /* TODO: Make a bitmap (or sizeof(char)map) of access privileges
+ * for every register, see Chapter 3 and 4 for privileged mode. */
+ if (value & 1)
+ fprintf(stderr, "%s: protection mode enable attempt\n",
+ __FUNCTION__);
+ return;
+
+ case 0x50: /* INTC_IDLE */
+ s->autoidle &= ~3;
+ s->autoidle |= value & 3;
+ return;
+
+ /* Per-bank registers */
+ case 0x84: /* INTC_MIR */
+ bank->mask = value;
+ omap_inth_update(s, 0);
+ omap_inth_update(s, 1);
+ return;
+
+ case 0x88: /* INTC_MIR_CLEAR */
+ bank->mask &= ~value;
+ omap_inth_update(s, 0);
+ omap_inth_update(s, 1);
+ return;
+
+ case 0x8c: /* INTC_MIR_SET */
+ bank->mask |= value;
+ return;
+
+ case 0x90: /* INTC_ISR_SET */
+ bank->irqs |= bank->swi |= value;
+ omap_inth_update(s, 0);
+ omap_inth_update(s, 1);
+ return;
+
+ case 0x94: /* INTC_ISR_CLEAR */
+ bank->swi &= ~value;
+ bank->irqs = bank->swi & bank->inputs;
+ return;
+
+ /* Per-line registers */
+ case 0x100 ... 0x300: /* INTC_ILR */
+ bank_no = (offset - 0x100) >> 7;
+ if (bank_no > s->nbanks)
+ break;
+ bank = &s->bank[bank_no];
+ line_no = (offset & 0x7f) >> 2;
+ bank->priority[line_no] = (value >> 2) & 0x3f;
+ bank->fiq &= ~(1 << line_no);
+ bank->fiq |= (value & 1) << line_no;
+ return;
+
+ case 0x00: /* INTC_REVISION */
+ case 0x14: /* INTC_SYSSTATUS */
+ case 0x40: /* INTC_SIR_IRQ */
+ case 0x44: /* INTC_SIR_FIQ */
+ case 0x80: /* INTC_ITR */
+ case 0x98: /* INTC_PENDING_IRQ */
+ case 0x9c: /* INTC_PENDING_FIQ */
+ OMAP_RO_REG(addr);
+ return;
+ }
+ OMAP_BAD_REG(addr);
+}
+
+static CPUReadMemoryFunc * const omap2_inth_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap2_inth_read,
+};
+
+static CPUWriteMemoryFunc * const omap2_inth_writefn[] = {
+ omap2_inth_write,
+ omap2_inth_write,
+ omap2_inth_write,
+};
+
+struct omap_intr_handler_s *omap2_inth_init(target_phys_addr_t base,
+ int size, int nbanks, qemu_irq **pins,
+ qemu_irq parent_irq, qemu_irq parent_fiq,
+ omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+ struct omap_intr_handler_s *s = (struct omap_intr_handler_s *)
+ qemu_mallocz(sizeof(struct omap_intr_handler_s) +
+ sizeof(struct omap_intr_handler_bank_s) * nbanks);
+
+ s->parent_intr[0] = parent_irq;
+ s->parent_intr[1] = parent_fiq;
+ s->nbanks = nbanks;
+ s->level_only = 1;
+ s->pins = qemu_allocate_irqs(omap_set_intr_noedge, s, nbanks * 32);
+ if (pins)
+ *pins = s->pins;
+
+ omap_inth_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap2_inth_readfn,
+ omap2_inth_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, size, iomemtype);
+
+ return s;
+}
diff --git a/hw/omap_l4.c b/hw/omap_l4.c
new file mode 100644
index 0000000..4af0ca8
--- /dev/null
+++ b/hw/omap_l4.c
@@ -0,0 +1,272 @@
+/*
+ * TI OMAP L4 interconnect emulation.
+ *
+ * Copyright (C) 2007-2009 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "omap.h"
+
+#ifdef L4_MUX_HACK
+static int omap_l4_io_entries;
+static int omap_cpu_io_entry;
+static struct omap_l4_entry {
+ CPUReadMemoryFunc * const *mem_read;
+ CPUWriteMemoryFunc * const *mem_write;
+ void *opaque;
+} *omap_l4_io_entry;
+static CPUReadMemoryFunc * const *omap_l4_io_readb_fn;
+static CPUReadMemoryFunc * const *omap_l4_io_readh_fn;
+static CPUReadMemoryFunc * const *omap_l4_io_readw_fn;
+static CPUWriteMemoryFunc * const *omap_l4_io_writeb_fn;
+static CPUWriteMemoryFunc * const *omap_l4_io_writeh_fn;
+static CPUWriteMemoryFunc * const *omap_l4_io_writew_fn;
+static void **omap_l4_io_opaque;
+
+int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write, void *opaque)
+{
+ omap_l4_io_entry[omap_l4_io_entries].mem_read = mem_read;
+ omap_l4_io_entry[omap_l4_io_entries].mem_write = mem_write;
+ omap_l4_io_entry[omap_l4_io_entries].opaque = opaque;
+
+ return omap_l4_io_entries ++;
+}
+
+static uint32_t omap_l4_io_readb(void *opaque, target_phys_addr_t addr)
+{
+ unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS;
+
+ return omap_l4_io_readb_fn[i](omap_l4_io_opaque[i], addr);
+}
+
+static uint32_t omap_l4_io_readh(void *opaque, target_phys_addr_t addr)
+{
+ unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS;
+
+ return omap_l4_io_readh_fn[i](omap_l4_io_opaque[i], addr);
+}
+
+static uint32_t omap_l4_io_readw(void *opaque, target_phys_addr_t addr)
+{
+ unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS;
+
+ return omap_l4_io_readw_fn[i](omap_l4_io_opaque[i], addr);
+}
+
+static void omap_l4_io_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS;
+
+ return omap_l4_io_writeb_fn[i](omap_l4_io_opaque[i], addr, value);
+}
+
+static void omap_l4_io_writeh(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS;
+
+ return omap_l4_io_writeh_fn[i](omap_l4_io_opaque[i], addr, value);
+}
+
+static void omap_l4_io_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS;
+
+ return omap_l4_io_writew_fn[i](omap_l4_io_opaque[i], addr, value);
+}
+
+static CPUReadMemoryFunc * const omap_l4_io_readfn[] = {
+ omap_l4_io_readb,
+ omap_l4_io_readh,
+ omap_l4_io_readw,
+};
+
+static CPUWriteMemoryFunc * const omap_l4_io_writefn[] = {
+ omap_l4_io_writeb,
+ omap_l4_io_writeh,
+ omap_l4_io_writew,
+};
+#else
+int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read,
+ CPUWriteMemoryFunc * const *mem_write,
+ void *opaque)
+{
+ return cpu_register_io_memory(mem_read, mem_write, opaque,
+ DEVICE_NATIVE_ENDIAN);
+}
+#endif
+
+struct omap_l4_s {
+ target_phys_addr_t base;
+ int ta_num;
+ struct omap_target_agent_s ta[0];
+};
+
+struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num)
+{
+ struct omap_l4_s *bus = qemu_mallocz(
+ sizeof(*bus) + ta_num * sizeof(*bus->ta));
+
+ bus->ta_num = ta_num;
+ bus->base = base;
+
+#ifdef L4_MUX_HACK
+ omap_l4_io_entries = 1;
+ omap_l4_io_entry = qemu_mallocz(125 * sizeof(*omap_l4_io_entry));
+
+ omap_cpu_io_entry =
+ cpu_register_io_memory(omap_l4_io_readfn,
+ omap_l4_io_writefn, bus, DEVICE_NATIVE_ENDIAN);
+# define L4_PAGES (0xb4000 / TARGET_PAGE_SIZE)
+ omap_l4_io_readb_fn = qemu_mallocz(sizeof(void *) * L4_PAGES);
+ omap_l4_io_readh_fn = qemu_mallocz(sizeof(void *) * L4_PAGES);
+ omap_l4_io_readw_fn = qemu_mallocz(sizeof(void *) * L4_PAGES);
+ omap_l4_io_writeb_fn = qemu_mallocz(sizeof(void *) * L4_PAGES);
+ omap_l4_io_writeh_fn = qemu_mallocz(sizeof(void *) * L4_PAGES);
+ omap_l4_io_writew_fn = qemu_mallocz(sizeof(void *) * L4_PAGES);
+ omap_l4_io_opaque = qemu_mallocz(sizeof(void *) * L4_PAGES);
+#endif
+
+ return bus;
+}
+
+static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* COMPONENT */
+ return s->component;
+
+ case 0x20: /* AGENT_CONTROL */
+ return s->control;
+
+ case 0x28: /* AGENT_STATUS */
+ return s->status;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_l4ta_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* COMPONENT */
+ case 0x28: /* AGENT_STATUS */
+ OMAP_RO_REG(addr);
+ break;
+
+ case 0x20: /* AGENT_CONTROL */
+ s->control = value & 0x01000700;
+ if (value & 1) /* OCP_RESET */
+ s->status &= ~1; /* REQ_TIMEOUT */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_l4ta_readfn[] = {
+ omap_badwidth_read16,
+ omap_l4ta_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_l4ta_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_l4ta_write,
+};
+
+struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus,
+ const struct omap_l4_region_s *regions,
+ const struct omap_l4_agent_info_s *agents,
+ int cs)
+{
+ int i, iomemtype;
+ struct omap_target_agent_s *ta = NULL;
+ const struct omap_l4_agent_info_s *info = NULL;
+
+ for (i = 0; i < bus->ta_num; i ++)
+ if (agents[i].ta == cs) {
+ ta = &bus->ta[i];
+ info = &agents[i];
+ break;
+ }
+ if (!ta) {
+ fprintf(stderr, "%s: bad target agent (%i)\n", __FUNCTION__, cs);
+ exit(-1);
+ }
+
+ ta->bus = bus;
+ ta->start = &regions[info->region];
+ ta->regions = info->regions;
+
+ ta->component = ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
+ ta->status = 0x00000000;
+ ta->control = 0x00000200; /* XXX 01000200 for L4TAO */
+
+ iomemtype = l4_register_io_memory(omap_l4ta_readfn,
+ omap_l4ta_writefn, ta);
+ ta->base = omap_l4_attach(ta, info->ta_region, iomemtype);
+
+ return ta;
+}
+
+target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region,
+ int iotype)
+{
+ target_phys_addr_t base;
+ ssize_t size;
+#ifdef L4_MUX_HACK
+ int i;
+#endif
+
+ if (region < 0 || region >= ta->regions) {
+ fprintf(stderr, "%s: bad io region (%i)\n", __FUNCTION__, region);
+ exit(-1);
+ }
+
+ base = ta->bus->base + ta->start[region].offset;
+ size = ta->start[region].size;
+ if (iotype) {
+#ifndef L4_MUX_HACK
+ cpu_register_physical_memory(base, size, iotype);
+#else
+ cpu_register_physical_memory(base, size, omap_cpu_io_entry);
+ i = (base - ta->bus->base) / TARGET_PAGE_SIZE;
+ for (; size > 0; size -= TARGET_PAGE_SIZE, i ++) {
+ omap_l4_io_readb_fn[i] = omap_l4_io_entry[iotype].mem_read[0];
+ omap_l4_io_readh_fn[i] = omap_l4_io_entry[iotype].mem_read[1];
+ omap_l4_io_readw_fn[i] = omap_l4_io_entry[iotype].mem_read[2];
+ omap_l4_io_writeb_fn[i] = omap_l4_io_entry[iotype].mem_write[0];
+ omap_l4_io_writeh_fn[i] = omap_l4_io_entry[iotype].mem_write[1];
+ omap_l4_io_writew_fn[i] = omap_l4_io_entry[iotype].mem_write[2];
+ omap_l4_io_opaque[i] = omap_l4_io_entry[iotype].opaque;
+ }
+#endif
+ }
+
+ return base;
+}
diff --git a/hw/omap_lcd_template.h b/hw/omap_lcd_template.h
new file mode 100644
index 0000000..2fb96f8
--- /dev/null
+++ b/hw/omap_lcd_template.h
@@ -0,0 +1,175 @@
+/*
+ * QEMU OMAP LCD Emulator templates
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if DEPTH == 8
+# define BPP 1
+# define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+# define BPP 2
+# define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+# define BPP 4
+# define PIXEL_TYPE uint32_t
+#else
+# error unsupport depth
+#endif
+
+/*
+ * 2-bit colour
+ */
+static void glue(draw_line2_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t *pal = opaque;
+ uint8_t v, r, g, b;
+
+ do {
+ v = ldub_raw((void *) s);
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 2;
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 2;
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 2;
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ s ++;
+ width -= 4;
+ } while (width > 0);
+}
+
+/*
+ * 4-bit colour
+ */
+static void glue(draw_line4_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t *pal = opaque;
+ uint8_t v, r, g, b;
+
+ do {
+ v = ldub_raw((void *) s);
+ r = (pal[v & 0xf] >> 4) & 0xf0;
+ g = pal[v & 0xf] & 0xf0;
+ b = (pal[v & 0xf] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 4;
+ r = (pal[v & 0xf] >> 4) & 0xf0;
+ g = pal[v & 0xf] & 0xf0;
+ b = (pal[v & 0xf] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ s ++;
+ width -= 2;
+ } while (width > 0);
+}
+
+/*
+ * 8-bit colour
+ */
+static void glue(draw_line8_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t *pal = opaque;
+ uint8_t v, r, g, b;
+
+ do {
+ v = ldub_raw((void *) s);
+ r = (pal[v] >> 4) & 0xf0;
+ g = pal[v] & 0xf0;
+ b = (pal[v] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ s ++;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+/*
+ * 12-bit colour
+ */
+static void glue(draw_line12_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t v;
+ uint8_t r, g, b;
+
+ do {
+ v = lduw_raw((void *) s);
+ r = (v >> 4) & 0xf0;
+ g = v & 0xf0;
+ b = (v << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+/*
+ * 16-bit colour
+ */
+static void glue(draw_line16_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ uint16_t v;
+ uint8_t r, g, b;
+
+ do {
+ v = lduw_raw((void *) s);
+ r = (v >> 8) & 0xf8;
+ g = (v >> 3) & 0xfc;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (-- width != 0);
+#endif
+}
+
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c
new file mode 100644
index 0000000..0c2c550
--- /dev/null
+++ b/hw/omap_lcdc.c
@@ -0,0 +1,461 @@
+/*
+ * OMAP LCD controller.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "console.h"
+#include "omap.h"
+#include "framebuffer.h"
+
+struct omap_lcd_panel_s {
+ qemu_irq irq;
+ DisplayState *state;
+ ram_addr_t imif_base;
+ ram_addr_t emiff_base;
+
+ int plm;
+ int tft;
+ int mono;
+ int enable;
+ int width;
+ int height;
+ int interrupts;
+ uint32_t timing[3];
+ uint32_t subpanel;
+ uint32_t ctrl;
+
+ struct omap_dma_lcd_channel_s *dma;
+ uint16_t palette[256];
+ int palette_done;
+ int frame_done;
+ int invalidate;
+ int sync_error;
+};
+
+static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
+{
+ if (s->frame_done && (s->interrupts & 1)) {
+ qemu_irq_raise(s->irq);
+ return;
+ }
+
+ if (s->palette_done && (s->interrupts & 2)) {
+ qemu_irq_raise(s->irq);
+ return;
+ }
+
+ if (s->sync_error) {
+ qemu_irq_raise(s->irq);
+ return;
+ }
+
+ qemu_irq_lower(s->irq);
+}
+
+#include "pixel_ops.h"
+
+#define draw_line_func drawfn
+
+#define DEPTH 8
+#include "omap_lcd_template.h"
+#define DEPTH 15
+#include "omap_lcd_template.h"
+#define DEPTH 16
+#include "omap_lcd_template.h"
+#define DEPTH 32
+#include "omap_lcd_template.h"
+
+static draw_line_func draw_line_table2[33] = {
+ [0 ... 32] = NULL,
+ [8] = draw_line2_8,
+ [15] = draw_line2_15,
+ [16] = draw_line2_16,
+ [32] = draw_line2_32,
+}, draw_line_table4[33] = {
+ [0 ... 32] = NULL,
+ [8] = draw_line4_8,
+ [15] = draw_line4_15,
+ [16] = draw_line4_16,
+ [32] = draw_line4_32,
+}, draw_line_table8[33] = {
+ [0 ... 32] = NULL,
+ [8] = draw_line8_8,
+ [15] = draw_line8_15,
+ [16] = draw_line8_16,
+ [32] = draw_line8_32,
+}, draw_line_table12[33] = {
+ [0 ... 32] = NULL,
+ [8] = draw_line12_8,
+ [15] = draw_line12_15,
+ [16] = draw_line12_16,
+ [32] = draw_line12_32,
+}, draw_line_table16[33] = {
+ [0 ... 32] = NULL,
+ [8] = draw_line16_8,
+ [15] = draw_line16_15,
+ [16] = draw_line16_16,
+ [32] = draw_line16_32,
+};
+
+static void omap_update_display(void *opaque)
+{
+ struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
+ draw_line_func draw_line;
+ int size, height, first, last;
+ int width, linesize, step, bpp, frame_offset;
+ target_phys_addr_t frame_base;
+
+ if (!omap_lcd || omap_lcd->plm == 1 ||
+ !omap_lcd->enable || !ds_get_bits_per_pixel(omap_lcd->state))
+ return;
+
+ frame_offset = 0;
+ if (omap_lcd->plm != 2) {
+ cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
+ omap_lcd->dma->current_frame],
+ (void *)omap_lcd->palette, 0x200);
+ switch (omap_lcd->palette[0] >> 12 & 7) {
+ case 3 ... 7:
+ frame_offset += 0x200;
+ break;
+ default:
+ frame_offset += 0x20;
+ }
+ }
+
+ /* Colour depth */
+ switch ((omap_lcd->palette[0] >> 12) & 7) {
+ case 1:
+ draw_line = draw_line_table2[ds_get_bits_per_pixel(omap_lcd->state)];
+ bpp = 2;
+ break;
+
+ case 2:
+ draw_line = draw_line_table4[ds_get_bits_per_pixel(omap_lcd->state)];
+ bpp = 4;
+ break;
+
+ case 3:
+ draw_line = draw_line_table8[ds_get_bits_per_pixel(omap_lcd->state)];
+ bpp = 8;
+ break;
+
+ case 4 ... 7:
+ if (!omap_lcd->tft)
+ draw_line = draw_line_table12[ds_get_bits_per_pixel(omap_lcd->state)];
+ else
+ draw_line = draw_line_table16[ds_get_bits_per_pixel(omap_lcd->state)];
+ bpp = 16;
+ break;
+
+ default:
+ /* Unsupported at the moment. */
+ return;
+ }
+
+ /* Resolution */
+ width = omap_lcd->width;
+ if (width != ds_get_width(omap_lcd->state) ||
+ omap_lcd->height != ds_get_height(omap_lcd->state)) {
+ qemu_console_resize(omap_lcd->state,
+ omap_lcd->width, omap_lcd->height);
+ omap_lcd->invalidate = 1;
+ }
+
+ if (omap_lcd->dma->current_frame == 0)
+ size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
+ else
+ size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
+
+ if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
+ omap_lcd->sync_error = 1;
+ omap_lcd_interrupts(omap_lcd);
+ omap_lcd->enable = 0;
+ return;
+ }
+
+ /* Content */
+ frame_base = omap_lcd->dma->phys_framebuffer[
+ omap_lcd->dma->current_frame] + frame_offset;
+ omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
+ if (omap_lcd->dma->interrupts & 1)
+ qemu_irq_raise(omap_lcd->dma->irq);
+ if (omap_lcd->dma->dual)
+ omap_lcd->dma->current_frame ^= 1;
+
+ if (!ds_get_bits_per_pixel(omap_lcd->state))
+ return;
+
+ first = 0;
+ height = omap_lcd->height;
+ if (omap_lcd->subpanel & (1 << 31)) {
+ if (omap_lcd->subpanel & (1 << 29))
+ first = (omap_lcd->subpanel >> 16) & 0x3ff;
+ else
+ height = (omap_lcd->subpanel >> 16) & 0x3ff;
+ /* TODO: fill the rest of the panel with DPD */
+ }
+
+ step = width * bpp >> 3;
+ linesize = ds_get_linesize(omap_lcd->state);
+ framebuffer_update_display(omap_lcd->state,
+ frame_base, width, height,
+ step, linesize, 0,
+ omap_lcd->invalidate,
+ draw_line, omap_lcd->palette,
+ &first, &last);
+ if (first >= 0) {
+ dpy_update(omap_lcd->state, 0, first, width, last - first + 1);
+ }
+ omap_lcd->invalidate = 0;
+}
+
+static int ppm_save(const char *filename, uint8_t *data,
+ int w, int h, int linesize)
+{
+ FILE *f;
+ uint8_t *d, *d1;
+ unsigned int v;
+ int y, x, bpp;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return -1;
+ fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
+ d1 = data;
+ bpp = linesize / w;
+ for (y = 0; y < h; y ++) {
+ d = d1;
+ for (x = 0; x < w; x ++) {
+ v = *(uint32_t *) d;
+ switch (bpp) {
+ case 2:
+ fputc((v >> 8) & 0xf8, f);
+ fputc((v >> 3) & 0xfc, f);
+ fputc((v << 3) & 0xf8, f);
+ break;
+ case 3:
+ case 4:
+ default:
+ fputc((v >> 16) & 0xff, f);
+ fputc((v >> 8) & 0xff, f);
+ fputc((v) & 0xff, f);
+ break;
+ }
+ d += bpp;
+ }
+ d1 += linesize;
+ }
+ fclose(f);
+ return 0;
+}
+
+static void omap_screen_dump(void *opaque, const char *filename) {
+ struct omap_lcd_panel_s *omap_lcd = opaque;
+ omap_update_display(opaque);
+ if (omap_lcd && ds_get_data(omap_lcd->state))
+ ppm_save(filename, ds_get_data(omap_lcd->state),
+ omap_lcd->width, omap_lcd->height,
+ ds_get_linesize(omap_lcd->state));
+}
+
+static void omap_invalidate_display(void *opaque) {
+ struct omap_lcd_panel_s *omap_lcd = opaque;
+ omap_lcd->invalidate = 1;
+}
+
+static void omap_lcd_update(struct omap_lcd_panel_s *s) {
+ if (!s->enable) {
+ s->dma->current_frame = -1;
+ s->sync_error = 0;
+ if (s->plm != 1)
+ s->frame_done = 1;
+ omap_lcd_interrupts(s);
+ return;
+ }
+
+ if (s->dma->current_frame == -1) {
+ s->frame_done = 0;
+ s->palette_done = 0;
+ s->dma->current_frame = 0;
+ }
+
+ if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
+ s->dma->src_f1_top) ||
+ !s->dma->mpu->port[
+ s->dma->src].addr_valid(s->dma->mpu,
+ s->dma->src_f1_bottom) ||
+ (s->dma->dual &&
+ (!s->dma->mpu->port[
+ s->dma->src].addr_valid(s->dma->mpu,
+ s->dma->src_f2_top) ||
+ !s->dma->mpu->port[
+ s->dma->src].addr_valid(s->dma->mpu,
+ s->dma->src_f2_bottom)))) {
+ s->dma->condition |= 1 << 2;
+ if (s->dma->interrupts & (1 << 1))
+ qemu_irq_raise(s->dma->irq);
+ s->enable = 0;
+ return;
+ }
+
+ s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
+ s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
+
+ if (s->plm != 2 && !s->palette_done) {
+ cpu_physical_memory_read(
+ s->dma->phys_framebuffer[s->dma->current_frame],
+ (void *)s->palette, 0x200);
+ s->palette_done = 1;
+ omap_lcd_interrupts(s);
+ }
+}
+
+static uint32_t omap_lcdc_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* LCD_CONTROL */
+ return (s->tft << 23) | (s->plm << 20) |
+ (s->tft << 7) | (s->interrupts << 3) |
+ (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
+
+ case 0x04: /* LCD_TIMING0 */
+ return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
+
+ case 0x08: /* LCD_TIMING1 */
+ return (s->timing[1] << 10) | (s->height - 1);
+
+ case 0x0c: /* LCD_TIMING2 */
+ return s->timing[2] | 0xfc000000;
+
+ case 0x10: /* LCD_STATUS */
+ return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
+
+ case 0x14: /* LCD_SUBPANEL */
+ return s->subpanel;
+
+ default:
+ break;
+ }
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_lcdc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* LCD_CONTROL */
+ s->plm = (value >> 20) & 3;
+ s->tft = (value >> 7) & 1;
+ s->interrupts = (value >> 3) & 3;
+ s->mono = (value >> 1) & 1;
+ s->ctrl = value & 0x01cff300;
+ if (s->enable != (value & 1)) {
+ s->enable = value & 1;
+ omap_lcd_update(s);
+ }
+ break;
+
+ case 0x04: /* LCD_TIMING0 */
+ s->timing[0] = value >> 10;
+ s->width = (value & 0x3ff) + 1;
+ break;
+
+ case 0x08: /* LCD_TIMING1 */
+ s->timing[1] = value >> 10;
+ s->height = (value & 0x3ff) + 1;
+ break;
+
+ case 0x0c: /* LCD_TIMING2 */
+ s->timing[2] = value;
+ break;
+
+ case 0x10: /* LCD_STATUS */
+ break;
+
+ case 0x14: /* LCD_SUBPANEL */
+ s->subpanel = value & 0xa1ffffff;
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_lcdc_readfn[] = {
+ omap_lcdc_read,
+ omap_lcdc_read,
+ omap_lcdc_read,
+};
+
+static CPUWriteMemoryFunc * const omap_lcdc_writefn[] = {
+ omap_lcdc_write,
+ omap_lcdc_write,
+ omap_lcdc_write,
+};
+
+void omap_lcdc_reset(struct omap_lcd_panel_s *s)
+{
+ s->dma->current_frame = -1;
+ s->plm = 0;
+ s->tft = 0;
+ s->mono = 0;
+ s->enable = 0;
+ s->width = 0;
+ s->height = 0;
+ s->interrupts = 0;
+ s->timing[0] = 0;
+ s->timing[1] = 0;
+ s->timing[2] = 0;
+ s->subpanel = 0;
+ s->palette_done = 0;
+ s->frame_done = 0;
+ s->sync_error = 0;
+ s->invalidate = 1;
+ s->subpanel = 0;
+ s->ctrl = 0;
+}
+
+struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq,
+ struct omap_dma_lcd_channel_s *dma,
+ ram_addr_t imif_base, ram_addr_t emiff_base, omap_clk clk)
+{
+ int iomemtype;
+ struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
+ qemu_mallocz(sizeof(struct omap_lcd_panel_s));
+
+ s->irq = irq;
+ s->dma = dma;
+ s->imif_base = imif_base;
+ s->emiff_base = emiff_base;
+ omap_lcdc_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_lcdc_readfn,
+ omap_lcdc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x100, iomemtype);
+
+ s->state = graphic_console_init(omap_update_display,
+ omap_invalidate_display,
+ omap_screen_dump, NULL, s);
+
+ return s;
+}
diff --git a/hw/omap_mmc.c b/hw/omap_mmc.c
new file mode 100644
index 0000000..e9ec2f3
--- /dev/null
+++ b/hw/omap_mmc.c
@@ -0,0 +1,641 @@
+/*
+ * OMAP on-chip MMC/SD host emulation.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "omap.h"
+#include "sd.h"
+
+struct omap_mmc_s {
+ qemu_irq irq;
+ qemu_irq *dma;
+ qemu_irq coverswitch;
+ omap_clk clk;
+ SDState *card;
+ uint16_t last_cmd;
+ uint16_t sdio;
+ uint16_t rsp[8];
+ uint32_t arg;
+ int lines;
+ int dw;
+ int mode;
+ int enable;
+ int be;
+ int rev;
+ uint16_t status;
+ uint16_t mask;
+ uint8_t cto;
+ uint16_t dto;
+ int clkdiv;
+ uint16_t fifo[32];
+ int fifo_start;
+ int fifo_len;
+ uint16_t blen;
+ uint16_t blen_counter;
+ uint16_t nblk;
+ uint16_t nblk_counter;
+ int tx_dma;
+ int rx_dma;
+ int af_level;
+ int ae_level;
+
+ int ddir;
+ int transfer;
+
+ int cdet_wakeup;
+ int cdet_enable;
+ int cdet_state;
+ qemu_irq cdet;
+};
+
+static void omap_mmc_interrupts_update(struct omap_mmc_s *s)
+{
+ qemu_set_irq(s->irq, !!(s->status & s->mask));
+}
+
+static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
+{
+ if (!host->transfer && !host->fifo_len) {
+ host->status &= 0xf3ff;
+ return;
+ }
+
+ if (host->fifo_len > host->af_level && host->ddir) {
+ if (host->rx_dma) {
+ host->status &= 0xfbff;
+ qemu_irq_raise(host->dma[1]);
+ } else
+ host->status |= 0x0400;
+ } else {
+ host->status &= 0xfbff;
+ qemu_irq_lower(host->dma[1]);
+ }
+
+ if (host->fifo_len < host->ae_level && !host->ddir) {
+ if (host->tx_dma) {
+ host->status &= 0xf7ff;
+ qemu_irq_raise(host->dma[0]);
+ } else
+ host->status |= 0x0800;
+ } else {
+ qemu_irq_lower(host->dma[0]);
+ host->status &= 0xf7ff;
+ }
+}
+
+typedef enum {
+ sd_nore = 0, /* no response */
+ sd_r1, /* normal response command */
+ sd_r2, /* CID, CSD registers */
+ sd_r3, /* OCR register */
+ sd_r6 = 6, /* Published RCA response */
+ sd_r1b = -1,
+} sd_rsp_type_t;
+
+static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
+ sd_cmd_type_t type, int busy, sd_rsp_type_t resptype, int init)
+{
+ uint32_t rspstatus, mask;
+ int rsplen, timeout;
+ SDRequest request;
+ uint8_t response[16];
+
+ if (init && cmd == 0) {
+ host->status |= 0x0001;
+ return;
+ }
+
+ if (resptype == sd_r1 && busy)
+ resptype = sd_r1b;
+
+ if (type == sd_adtc) {
+ host->fifo_start = 0;
+ host->fifo_len = 0;
+ host->transfer = 1;
+ host->ddir = dir;
+ } else
+ host->transfer = 0;
+ timeout = 0;
+ mask = 0;
+ rspstatus = 0;
+
+ request.cmd = cmd;
+ request.arg = host->arg;
+ request.crc = 0; /* FIXME */
+
+ rsplen = sd_do_command(host->card, &request, response);
+
+ /* TODO: validate CRCs */
+ switch (resptype) {
+ case sd_nore:
+ rsplen = 0;
+ break;
+
+ case sd_r1:
+ case sd_r1b:
+ if (rsplen < 4) {
+ timeout = 1;
+ break;
+ }
+ rsplen = 4;
+
+ mask = OUT_OF_RANGE | ADDRESS_ERROR | BLOCK_LEN_ERROR |
+ ERASE_SEQ_ERROR | ERASE_PARAM | WP_VIOLATION |
+ LOCK_UNLOCK_FAILED | COM_CRC_ERROR | ILLEGAL_COMMAND |
+ CARD_ECC_FAILED | CC_ERROR | SD_ERROR |
+ CID_CSD_OVERWRITE;
+ if (host->sdio & (1 << 13))
+ mask |= AKE_SEQ_ERROR;
+ rspstatus = (response[0] << 24) | (response[1] << 16) |
+ (response[2] << 8) | (response[3] << 0);
+ break;
+
+ case sd_r2:
+ if (rsplen < 16) {
+ timeout = 1;
+ break;
+ }
+ rsplen = 16;
+ break;
+
+ case sd_r3:
+ if (rsplen < 4) {
+ timeout = 1;
+ break;
+ }
+ rsplen = 4;
+
+ rspstatus = (response[0] << 24) | (response[1] << 16) |
+ (response[2] << 8) | (response[3] << 0);
+ if (rspstatus & 0x80000000)
+ host->status &= 0xe000;
+ else
+ host->status |= 0x1000;
+ break;
+
+ case sd_r6:
+ if (rsplen < 4) {
+ timeout = 1;
+ break;
+ }
+ rsplen = 4;
+
+ mask = 0xe000 | AKE_SEQ_ERROR;
+ rspstatus = (response[2] << 8) | (response[3] << 0);
+ }
+
+ if (rspstatus & mask)
+ host->status |= 0x4000;
+ else
+ host->status &= 0xb000;
+
+ if (rsplen)
+ for (rsplen = 0; rsplen < 8; rsplen ++)
+ host->rsp[~rsplen & 7] = response[(rsplen << 1) | 1] |
+ (response[(rsplen << 1) | 0] << 8);
+
+ if (timeout)
+ host->status |= 0x0080;
+ else if (cmd == 12)
+ host->status |= 0x0005; /* Makes it more real */
+ else
+ host->status |= 0x0001;
+}
+
+static void omap_mmc_transfer(struct omap_mmc_s *host)
+{
+ uint8_t value;
+
+ if (!host->transfer)
+ return;
+
+ while (1) {
+ if (host->ddir) {
+ if (host->fifo_len > host->af_level)
+ break;
+
+ value = sd_read_data(host->card);
+ host->fifo[(host->fifo_start + host->fifo_len) & 31] = value;
+ if (-- host->blen_counter) {
+ value = sd_read_data(host->card);
+ host->fifo[(host->fifo_start + host->fifo_len) & 31] |=
+ value << 8;
+ host->blen_counter --;
+ }
+
+ host->fifo_len ++;
+ } else {
+ if (!host->fifo_len)
+ break;
+
+ value = host->fifo[host->fifo_start] & 0xff;
+ sd_write_data(host->card, value);
+ if (-- host->blen_counter) {
+ value = host->fifo[host->fifo_start] >> 8;
+ sd_write_data(host->card, value);
+ host->blen_counter --;
+ }
+
+ host->fifo_start ++;
+ host->fifo_len --;
+ host->fifo_start &= 31;
+ }
+
+ if (host->blen_counter == 0) {
+ host->nblk_counter --;
+ host->blen_counter = host->blen;
+
+ if (host->nblk_counter == 0) {
+ host->nblk_counter = host->nblk;
+ host->transfer = 0;
+ host->status |= 0x0008;
+ break;
+ }
+ }
+ }
+}
+
+static void omap_mmc_update(void *opaque)
+{
+ struct omap_mmc_s *s = opaque;
+ omap_mmc_transfer(s);
+ omap_mmc_fifolevel_update(s);
+ omap_mmc_interrupts_update(s);
+}
+
+void omap_mmc_reset(struct omap_mmc_s *host)
+{
+ host->last_cmd = 0;
+ memset(host->rsp, 0, sizeof(host->rsp));
+ host->arg = 0;
+ host->dw = 0;
+ host->mode = 0;
+ host->enable = 0;
+ host->status = 0;
+ host->mask = 0;
+ host->cto = 0;
+ host->dto = 0;
+ host->fifo_len = 0;
+ host->blen = 0;
+ host->blen_counter = 0;
+ host->nblk = 0;
+ host->nblk_counter = 0;
+ host->tx_dma = 0;
+ host->rx_dma = 0;
+ host->ae_level = 0x00;
+ host->af_level = 0x1f;
+ host->transfer = 0;
+ host->cdet_wakeup = 0;
+ host->cdet_enable = 0;
+ qemu_set_irq(host->coverswitch, host->cdet_state);
+ host->clkdiv = 0;
+}
+
+static uint32_t omap_mmc_read(void *opaque, target_phys_addr_t offset)
+{
+ uint16_t i;
+ struct omap_mmc_s *s = (struct omap_mmc_s *) opaque;
+ offset &= OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* MMC_CMD */
+ return s->last_cmd;
+
+ case 0x04: /* MMC_ARGL */
+ return s->arg & 0x0000ffff;
+
+ case 0x08: /* MMC_ARGH */
+ return s->arg >> 16;
+
+ case 0x0c: /* MMC_CON */
+ return (s->dw << 15) | (s->mode << 12) | (s->enable << 11) |
+ (s->be << 10) | s->clkdiv;
+
+ case 0x10: /* MMC_STAT */
+ return s->status;
+
+ case 0x14: /* MMC_IE */
+ return s->mask;
+
+ case 0x18: /* MMC_CTO */
+ return s->cto;
+
+ case 0x1c: /* MMC_DTO */
+ return s->dto;
+
+ case 0x20: /* MMC_DATA */
+ /* TODO: support 8-bit access */
+ i = s->fifo[s->fifo_start];
+ if (s->fifo_len == 0) {
+ printf("MMC: FIFO underrun\n");
+ return i;
+ }
+ s->fifo_start ++;
+ s->fifo_len --;
+ s->fifo_start &= 31;
+ omap_mmc_transfer(s);
+ omap_mmc_fifolevel_update(s);
+ omap_mmc_interrupts_update(s);
+ return i;
+
+ case 0x24: /* MMC_BLEN */
+ return s->blen_counter;
+
+ case 0x28: /* MMC_NBLK */
+ return s->nblk_counter;
+
+ case 0x2c: /* MMC_BUF */
+ return (s->rx_dma << 15) | (s->af_level << 8) |
+ (s->tx_dma << 7) | s->ae_level;
+
+ case 0x30: /* MMC_SPI */
+ return 0x0000;
+ case 0x34: /* MMC_SDIO */
+ return (s->cdet_wakeup << 2) | (s->cdet_enable) | s->sdio;
+ case 0x38: /* MMC_SYST */
+ return 0x0000;
+
+ case 0x3c: /* MMC_REV */
+ return s->rev;
+
+ case 0x40: /* MMC_RSP0 */
+ case 0x44: /* MMC_RSP1 */
+ case 0x48: /* MMC_RSP2 */
+ case 0x4c: /* MMC_RSP3 */
+ case 0x50: /* MMC_RSP4 */
+ case 0x54: /* MMC_RSP5 */
+ case 0x58: /* MMC_RSP6 */
+ case 0x5c: /* MMC_RSP7 */
+ return s->rsp[(offset - 0x40) >> 2];
+
+ /* OMAP2-specific */
+ case 0x60: /* MMC_IOSR */
+ case 0x64: /* MMC_SYSC */
+ return 0;
+ case 0x68: /* MMC_SYSS */
+ return 1; /* RSTD */
+ }
+
+ OMAP_BAD_REG(offset);
+ return 0;
+}
+
+static void omap_mmc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ int i;
+ struct omap_mmc_s *s = (struct omap_mmc_s *) opaque;
+ offset &= OMAP_MPUI_REG_MASK;
+
+ switch (offset) {
+ case 0x00: /* MMC_CMD */
+ if (!s->enable)
+ break;
+
+ s->last_cmd = value;
+ for (i = 0; i < 8; i ++)
+ s->rsp[i] = 0x0000;
+ omap_mmc_command(s, value & 63, (value >> 15) & 1,
+ (sd_cmd_type_t) ((value >> 12) & 3),
+ (value >> 11) & 1,
+ (sd_rsp_type_t) ((value >> 8) & 7),
+ (value >> 7) & 1);
+ omap_mmc_update(s);
+ break;
+
+ case 0x04: /* MMC_ARGL */
+ s->arg &= 0xffff0000;
+ s->arg |= 0x0000ffff & value;
+ break;
+
+ case 0x08: /* MMC_ARGH */
+ s->arg &= 0x0000ffff;
+ s->arg |= value << 16;
+ break;
+
+ case 0x0c: /* MMC_CON */
+ s->dw = (value >> 15) & 1;
+ s->mode = (value >> 12) & 3;
+ s->enable = (value >> 11) & 1;
+ s->be = (value >> 10) & 1;
+ s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff);
+ if (s->mode != 0)
+ printf("SD mode %i unimplemented!\n", s->mode);
+ if (s->be != 0)
+ printf("SD FIFO byte sex unimplemented!\n");
+ if (s->dw != 0 && s->lines < 4)
+ printf("4-bit SD bus enabled\n");
+ if (!s->enable)
+ omap_mmc_reset(s);
+ break;
+
+ case 0x10: /* MMC_STAT */
+ s->status &= ~value;
+ omap_mmc_interrupts_update(s);
+ break;
+
+ case 0x14: /* MMC_IE */
+ s->mask = value & 0x7fff;
+ omap_mmc_interrupts_update(s);
+ break;
+
+ case 0x18: /* MMC_CTO */
+ s->cto = value & 0xff;
+ if (s->cto > 0xfd && s->rev <= 1)
+ printf("MMC: CTO of 0xff and 0xfe cannot be used!\n");
+ break;
+
+ case 0x1c: /* MMC_DTO */
+ s->dto = value & 0xffff;
+ break;
+
+ case 0x20: /* MMC_DATA */
+ /* TODO: support 8-bit access */
+ if (s->fifo_len == 32)
+ break;
+ s->fifo[(s->fifo_start + s->fifo_len) & 31] = value;
+ s->fifo_len ++;
+ omap_mmc_transfer(s);
+ omap_mmc_fifolevel_update(s);
+ omap_mmc_interrupts_update(s);
+ break;
+
+ case 0x24: /* MMC_BLEN */
+ s->blen = (value & 0x07ff) + 1;
+ s->blen_counter = s->blen;
+ break;
+
+ case 0x28: /* MMC_NBLK */
+ s->nblk = (value & 0x07ff) + 1;
+ s->nblk_counter = s->nblk;
+ s->blen_counter = s->blen;
+ break;
+
+ case 0x2c: /* MMC_BUF */
+ s->rx_dma = (value >> 15) & 1;
+ s->af_level = (value >> 8) & 0x1f;
+ s->tx_dma = (value >> 7) & 1;
+ s->ae_level = value & 0x1f;
+
+ if (s->rx_dma)
+ s->status &= 0xfbff;
+ if (s->tx_dma)
+ s->status &= 0xf7ff;
+ omap_mmc_fifolevel_update(s);
+ omap_mmc_interrupts_update(s);
+ break;
+
+ /* SPI, SDIO and TEST modes unimplemented */
+ case 0x30: /* MMC_SPI (OMAP1 only) */
+ break;
+ case 0x34: /* MMC_SDIO */
+ s->sdio = value & (s->rev >= 2 ? 0xfbf3 : 0x2020);
+ s->cdet_wakeup = (value >> 9) & 1;
+ s->cdet_enable = (value >> 2) & 1;
+ break;
+ case 0x38: /* MMC_SYST */
+ break;
+
+ case 0x3c: /* MMC_REV */
+ case 0x40: /* MMC_RSP0 */
+ case 0x44: /* MMC_RSP1 */
+ case 0x48: /* MMC_RSP2 */
+ case 0x4c: /* MMC_RSP3 */
+ case 0x50: /* MMC_RSP4 */
+ case 0x54: /* MMC_RSP5 */
+ case 0x58: /* MMC_RSP6 */
+ case 0x5c: /* MMC_RSP7 */
+ OMAP_RO_REG(offset);
+ break;
+
+ /* OMAP2-specific */
+ case 0x60: /* MMC_IOSR */
+ if (value & 0xf)
+ printf("MMC: SDIO bits used!\n");
+ break;
+ case 0x64: /* MMC_SYSC */
+ if (value & (1 << 2)) /* SRTS */
+ omap_mmc_reset(s);
+ break;
+ case 0x68: /* MMC_SYSS */
+ OMAP_RO_REG(offset);
+ break;
+
+ default:
+ OMAP_BAD_REG(offset);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_mmc_readfn[] = {
+ omap_badwidth_read16,
+ omap_mmc_read,
+ omap_badwidth_read16,
+};
+
+static CPUWriteMemoryFunc * const omap_mmc_writefn[] = {
+ omap_badwidth_write16,
+ omap_mmc_write,
+ omap_badwidth_write16,
+};
+
+static void omap_mmc_cover_cb(void *opaque, int line, int level)
+{
+ struct omap_mmc_s *host = (struct omap_mmc_s *) opaque;
+
+ if (!host->cdet_state && level) {
+ host->status |= 0x0002;
+ omap_mmc_interrupts_update(host);
+ if (host->cdet_wakeup) {
+ /* TODO: Assert wake-up */
+ }
+ }
+
+ if (host->cdet_state != level) {
+ qemu_set_irq(host->coverswitch, level);
+ host->cdet_state = level;
+ }
+}
+
+struct omap_mmc_s *omap_mmc_init(target_phys_addr_t base,
+ BlockDriverState *bd,
+ qemu_irq irq, qemu_irq dma[], omap_clk clk)
+{
+ int iomemtype;
+ struct omap_mmc_s *s = (struct omap_mmc_s *)
+ qemu_mallocz(sizeof(struct omap_mmc_s));
+
+ s->irq = irq;
+ s->dma = dma;
+ s->clk = clk;
+ s->lines = 1; /* TODO: needs to be settable per-board */
+ s->rev = 1;
+
+ omap_mmc_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_mmc_readfn,
+ omap_mmc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x800, iomemtype);
+
+ /* Instantiate the storage */
+ s->card = sd_init(bd, 0);
+
+ return s;
+}
+
+struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta,
+ BlockDriverState *bd, qemu_irq irq, qemu_irq dma[],
+ omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+ struct omap_mmc_s *s = (struct omap_mmc_s *)
+ qemu_mallocz(sizeof(struct omap_mmc_s));
+
+ s->irq = irq;
+ s->dma = dma;
+ s->clk = fclk;
+ s->lines = 4;
+ s->rev = 2;
+
+ omap_mmc_reset(s);
+
+ iomemtype = l4_register_io_memory(omap_mmc_readfn,
+ omap_mmc_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ /* Instantiate the storage */
+ s->card = sd_init(bd, 0);
+
+ s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0];
+ sd_set_cb(s->card, NULL, s->cdet);
+
+ return s;
+}
+
+void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover)
+{
+ if (s->cdet) {
+ sd_set_cb(s->card, ro, s->cdet);
+ s->coverswitch = cover;
+ qemu_set_irq(cover, s->cdet_state);
+ } else
+ sd_set_cb(s->card, ro, cover);
+}
+
+void omap_mmc_enable(struct omap_mmc_s *s, int enable)
+{
+ sd_enable(s->card, enable);
+}
diff --git a/hw/omap_sdrc.c b/hw/omap_sdrc.c
new file mode 100644
index 0000000..e183762
--- /dev/null
+++ b/hw/omap_sdrc.c
@@ -0,0 +1,165 @@
+/*
+ * TI OMAP SDRAM controller emulation.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "omap.h"
+
+/* SDRAM Controller Subsystem */
+struct omap_sdrc_s {
+ uint8_t config;
+};
+
+void omap_sdrc_reset(struct omap_sdrc_s *s)
+{
+ s->config = 0x10;
+}
+
+static uint32_t omap_sdrc_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* SDRC_REVISION */
+ return 0x20;
+
+ case 0x10: /* SDRC_SYSCONFIG */
+ return s->config;
+
+ case 0x14: /* SDRC_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x40: /* SDRC_CS_CFG */
+ case 0x44: /* SDRC_SHARING */
+ case 0x48: /* SDRC_ERR_ADDR */
+ case 0x4c: /* SDRC_ERR_TYPE */
+ case 0x60: /* SDRC_DLLA_SCTRL */
+ case 0x64: /* SDRC_DLLA_STATUS */
+ case 0x68: /* SDRC_DLLB_CTRL */
+ case 0x6c: /* SDRC_DLLB_STATUS */
+ case 0x70: /* SDRC_POWER */
+ case 0x80: /* SDRC_MCFG_0 */
+ case 0x84: /* SDRC_MR_0 */
+ case 0x88: /* SDRC_EMR1_0 */
+ case 0x8c: /* SDRC_EMR2_0 */
+ case 0x90: /* SDRC_EMR3_0 */
+ case 0x94: /* SDRC_DCDL1_CTRL */
+ case 0x98: /* SDRC_DCDL2_CTRL */
+ case 0x9c: /* SDRC_ACTIM_CTRLA_0 */
+ case 0xa0: /* SDRC_ACTIM_CTRLB_0 */
+ case 0xa4: /* SDRC_RFR_CTRL_0 */
+ case 0xa8: /* SDRC_MANUAL_0 */
+ case 0xb0: /* SDRC_MCFG_1 */
+ case 0xb4: /* SDRC_MR_1 */
+ case 0xb8: /* SDRC_EMR1_1 */
+ case 0xbc: /* SDRC_EMR2_1 */
+ case 0xc0: /* SDRC_EMR3_1 */
+ case 0xc4: /* SDRC_ACTIM_CTRLA_1 */
+ case 0xc8: /* SDRC_ACTIM_CTRLB_1 */
+ case 0xd4: /* SDRC_RFR_CTRL_1 */
+ case 0xd8: /* SDRC_MANUAL_1 */
+ return 0x00;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_sdrc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* SDRC_REVISION */
+ case 0x14: /* SDRC_SYSSTATUS */
+ case 0x48: /* SDRC_ERR_ADDR */
+ case 0x64: /* SDRC_DLLA_STATUS */
+ case 0x6c: /* SDRC_DLLB_STATUS */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x10: /* SDRC_SYSCONFIG */
+ if ((value >> 3) != 0x2)
+ fprintf(stderr, "%s: bad SDRAM idle mode %i\n",
+ __FUNCTION__, value >> 3);
+ if (value & 2)
+ omap_sdrc_reset(s);
+ s->config = value & 0x18;
+ break;
+
+ case 0x40: /* SDRC_CS_CFG */
+ case 0x44: /* SDRC_SHARING */
+ case 0x4c: /* SDRC_ERR_TYPE */
+ case 0x60: /* SDRC_DLLA_SCTRL */
+ case 0x68: /* SDRC_DLLB_CTRL */
+ case 0x70: /* SDRC_POWER */
+ case 0x80: /* SDRC_MCFG_0 */
+ case 0x84: /* SDRC_MR_0 */
+ case 0x88: /* SDRC_EMR1_0 */
+ case 0x8c: /* SDRC_EMR2_0 */
+ case 0x90: /* SDRC_EMR3_0 */
+ case 0x94: /* SDRC_DCDL1_CTRL */
+ case 0x98: /* SDRC_DCDL2_CTRL */
+ case 0x9c: /* SDRC_ACTIM_CTRLA_0 */
+ case 0xa0: /* SDRC_ACTIM_CTRLB_0 */
+ case 0xa4: /* SDRC_RFR_CTRL_0 */
+ case 0xa8: /* SDRC_MANUAL_0 */
+ case 0xb0: /* SDRC_MCFG_1 */
+ case 0xb4: /* SDRC_MR_1 */
+ case 0xb8: /* SDRC_EMR1_1 */
+ case 0xbc: /* SDRC_EMR2_1 */
+ case 0xc0: /* SDRC_EMR3_1 */
+ case 0xc4: /* SDRC_ACTIM_CTRLA_1 */
+ case 0xc8: /* SDRC_ACTIM_CTRLB_1 */
+ case 0xd4: /* SDRC_RFR_CTRL_1 */
+ case 0xd8: /* SDRC_MANUAL_1 */
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_sdrc_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_sdrc_read,
+};
+
+static CPUWriteMemoryFunc * const omap_sdrc_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_sdrc_write,
+};
+
+struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base)
+{
+ int iomemtype;
+ struct omap_sdrc_s *s = (struct omap_sdrc_s *)
+ qemu_mallocz(sizeof(struct omap_sdrc_s));
+
+ omap_sdrc_reset(s);
+
+ iomemtype = cpu_register_io_memory(omap_sdrc_readfn,
+ omap_sdrc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x1000, iomemtype);
+
+ return s;
+}
diff --git a/hw/omap_spi.c b/hw/omap_spi.c
new file mode 100644
index 0000000..a6b0349
--- /dev/null
+++ b/hw/omap_spi.c
@@ -0,0 +1,346 @@
+/*
+ * TI OMAP processor's Multichannel SPI emulation.
+ *
+ * Copyright (C) 2007-2009 Nokia Corporation
+ *
+ * Original code for OMAP2 by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "hw.h"
+#include "omap.h"
+
+/* Multichannel SPI */
+struct omap_mcspi_s {
+ qemu_irq irq;
+ int chnum;
+
+ uint32_t sysconfig;
+ uint32_t systest;
+ uint32_t irqst;
+ uint32_t irqen;
+ uint32_t wken;
+ uint32_t control;
+
+ struct omap_mcspi_ch_s {
+ qemu_irq txdrq;
+ qemu_irq rxdrq;
+ uint32_t (*txrx)(void *opaque, uint32_t, int);
+ void *opaque;
+
+ uint32_t tx;
+ uint32_t rx;
+
+ uint32_t config;
+ uint32_t status;
+ uint32_t control;
+ } ch[4];
+};
+
+static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
+{
+ qemu_set_irq(s->irq, s->irqst & s->irqen);
+}
+
+static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
+{
+ qemu_set_irq(ch->txdrq,
+ (ch->control & 1) && /* EN */
+ (ch->config & (1 << 14)) && /* DMAW */
+ (ch->status & (1 << 1)) && /* TXS */
+ ((ch->config >> 12) & 3) != 1); /* TRM */
+ qemu_set_irq(ch->rxdrq,
+ (ch->control & 1) && /* EN */
+ (ch->config & (1 << 15)) && /* DMAW */
+ (ch->status & (1 << 0)) && /* RXS */
+ ((ch->config >> 12) & 3) != 2); /* TRM */
+}
+
+static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
+{
+ struct omap_mcspi_ch_s *ch = s->ch + chnum;
+
+ if (!(ch->control & 1)) /* EN */
+ return;
+ if ((ch->status & (1 << 0)) && /* RXS */
+ ((ch->config >> 12) & 3) != 2 && /* TRM */
+ !(ch->config & (1 << 19))) /* TURBO */
+ goto intr_update;
+ if ((ch->status & (1 << 1)) && /* TXS */
+ ((ch->config >> 12) & 3) != 1) /* TRM */
+ goto intr_update;
+
+ if (!(s->control & 1) || /* SINGLE */
+ (ch->config & (1 << 20))) { /* FORCE */
+ if (ch->txrx)
+ ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */
+ 1 + (0x1f & (ch->config >> 7)));
+ }
+
+ ch->tx = 0;
+ ch->status |= 1 << 2; /* EOT */
+ ch->status |= 1 << 1; /* TXS */
+ if (((ch->config >> 12) & 3) != 2) /* TRM */
+ ch->status |= 1 << 0; /* RXS */
+
+intr_update:
+ if ((ch->status & (1 << 0)) && /* RXS */
+ ((ch->config >> 12) & 3) != 2 && /* TRM */
+ !(ch->config & (1 << 19))) /* TURBO */
+ s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */
+ if ((ch->status & (1 << 1)) && /* TXS */
+ ((ch->config >> 12) & 3) != 1) /* TRM */
+ s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */
+ omap_mcspi_interrupt_update(s);
+ omap_mcspi_dmarequest_update(ch);
+}
+
+void omap_mcspi_reset(struct omap_mcspi_s *s)
+{
+ int ch;
+
+ s->sysconfig = 0;
+ s->systest = 0;
+ s->irqst = 0;
+ s->irqen = 0;
+ s->wken = 0;
+ s->control = 4;
+
+ for (ch = 0; ch < 4; ch ++) {
+ s->ch[ch].config = 0x060000;
+ s->ch[ch].status = 2; /* TXS */
+ s->ch[ch].control = 0;
+
+ omap_mcspi_dmarequest_update(s->ch + ch);
+ }
+
+ omap_mcspi_interrupt_update(s);
+}
+
+static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
+ int ch = 0;
+ uint32_t ret;
+
+ switch (addr) {
+ case 0x00: /* MCSPI_REVISION */
+ return 0x91;
+
+ case 0x10: /* MCSPI_SYSCONFIG */
+ return s->sysconfig;
+
+ case 0x14: /* MCSPI_SYSSTATUS */
+ return 1; /* RESETDONE */
+
+ case 0x18: /* MCSPI_IRQSTATUS */
+ return s->irqst;
+
+ case 0x1c: /* MCSPI_IRQENABLE */
+ return s->irqen;
+
+ case 0x20: /* MCSPI_WAKEUPENABLE */
+ return s->wken;
+
+ case 0x24: /* MCSPI_SYST */
+ return s->systest;
+
+ case 0x28: /* MCSPI_MODULCTRL */
+ return s->control;
+
+ case 0x68: ch ++;
+ case 0x54: ch ++;
+ case 0x40: ch ++;
+ case 0x2c: /* MCSPI_CHCONF */
+ return s->ch[ch].config;
+
+ case 0x6c: ch ++;
+ case 0x58: ch ++;
+ case 0x44: ch ++;
+ case 0x30: /* MCSPI_CHSTAT */
+ return s->ch[ch].status;
+
+ case 0x70: ch ++;
+ case 0x5c: ch ++;
+ case 0x48: ch ++;
+ case 0x34: /* MCSPI_CHCTRL */
+ return s->ch[ch].control;
+
+ case 0x74: ch ++;
+ case 0x60: ch ++;
+ case 0x4c: ch ++;
+ case 0x38: /* MCSPI_TX */
+ return s->ch[ch].tx;
+
+ case 0x78: ch ++;
+ case 0x64: ch ++;
+ case 0x50: ch ++;
+ case 0x3c: /* MCSPI_RX */
+ s->ch[ch].status &= ~(1 << 0); /* RXS */
+ ret = s->ch[ch].rx;
+ omap_mcspi_transfer_run(s, ch);
+ return ret;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_mcspi_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
+ int ch = 0;
+
+ switch (addr) {
+ case 0x00: /* MCSPI_REVISION */
+ case 0x14: /* MCSPI_SYSSTATUS */
+ case 0x30: /* MCSPI_CHSTAT0 */
+ case 0x3c: /* MCSPI_RX0 */
+ case 0x44: /* MCSPI_CHSTAT1 */
+ case 0x50: /* MCSPI_RX1 */
+ case 0x58: /* MCSPI_CHSTAT2 */
+ case 0x64: /* MCSPI_RX2 */
+ case 0x6c: /* MCSPI_CHSTAT3 */
+ case 0x78: /* MCSPI_RX3 */
+ OMAP_RO_REG(addr);
+ return;
+
+ case 0x10: /* MCSPI_SYSCONFIG */
+ if (value & (1 << 1)) /* SOFTRESET */
+ omap_mcspi_reset(s);
+ s->sysconfig = value & 0x31d;
+ break;
+
+ case 0x18: /* MCSPI_IRQSTATUS */
+ if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
+ s->irqst &= ~value;
+ omap_mcspi_interrupt_update(s);
+ }
+ break;
+
+ case 0x1c: /* MCSPI_IRQENABLE */
+ s->irqen = value & 0x1777f;
+ omap_mcspi_interrupt_update(s);
+ break;
+
+ case 0x20: /* MCSPI_WAKEUPENABLE */
+ s->wken = value & 1;
+ break;
+
+ case 0x24: /* MCSPI_SYST */
+ if (s->control & (1 << 3)) /* SYSTEM_TEST */
+ if (value & (1 << 11)) { /* SSB */
+ s->irqst |= 0x1777f;
+ omap_mcspi_interrupt_update(s);
+ }
+ s->systest = value & 0xfff;
+ break;
+
+ case 0x28: /* MCSPI_MODULCTRL */
+ if (value & (1 << 3)) /* SYSTEM_TEST */
+ if (s->systest & (1 << 11)) { /* SSB */
+ s->irqst |= 0x1777f;
+ omap_mcspi_interrupt_update(s);
+ }
+ s->control = value & 0xf;
+ break;
+
+ case 0x68: ch ++;
+ case 0x54: ch ++;
+ case 0x40: ch ++;
+ case 0x2c: /* MCSPI_CHCONF */
+ if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */
+ omap_mcspi_dmarequest_update(s->ch + ch);
+ if (((value >> 12) & 3) == 3) /* TRM */
+ fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__);
+ if (((value >> 7) & 0x1f) < 3) /* WL */
+ fprintf(stderr, "%s: invalid WL value (%i)\n",
+ __FUNCTION__, (value >> 7) & 0x1f);
+ s->ch[ch].config = value & 0x7fffff;
+ break;
+
+ case 0x70: ch ++;
+ case 0x5c: ch ++;
+ case 0x48: ch ++;
+ case 0x34: /* MCSPI_CHCTRL */
+ if (value & ~s->ch[ch].control & 1) { /* EN */
+ s->ch[ch].control |= 1;
+ omap_mcspi_transfer_run(s, ch);
+ } else
+ s->ch[ch].control = value & 1;
+ break;
+
+ case 0x74: ch ++;
+ case 0x60: ch ++;
+ case 0x4c: ch ++;
+ case 0x38: /* MCSPI_TX */
+ s->ch[ch].tx = value;
+ s->ch[ch].status &= ~(1 << 1); /* TXS */
+ omap_mcspi_transfer_run(s, ch);
+ break;
+
+ default:
+ OMAP_BAD_REG(addr);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_mcspi_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_mcspi_read,
+};
+
+static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_mcspi_write,
+};
+
+struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
+ qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
+{
+ int iomemtype;
+ struct omap_mcspi_s *s = (struct omap_mcspi_s *)
+ qemu_mallocz(sizeof(struct omap_mcspi_s));
+ struct omap_mcspi_ch_s *ch = s->ch;
+
+ s->irq = irq;
+ s->chnum = chnum;
+ while (chnum --) {
+ ch->txdrq = *drq ++;
+ ch->rxdrq = *drq ++;
+ ch ++;
+ }
+ omap_mcspi_reset(s);
+
+ iomemtype = l4_register_io_memory(omap_mcspi_readfn,
+ omap_mcspi_writefn, s);
+ omap_l4_attach(ta, 0, iomemtype);
+
+ return s;
+}
+
+void omap_mcspi_attach(struct omap_mcspi_s *s,
+ uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
+ int chipselect)
+{
+ if (chipselect < 0 || chipselect >= s->chnum)
+ hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
+
+ s->ch[chipselect].txrx = txrx;
+ s->ch[chipselect].opaque = opaque;
+}
diff --git a/hw/omap_sx1.c b/hw/omap_sx1.c
new file mode 100644
index 0000000..06bccbd
--- /dev/null
+++ b/hw/omap_sx1.c
@@ -0,0 +1,254 @@
+/* omap_sx1.c Support for the Siemens SX1 smartphone emulation.
+ *
+ * Copyright (C) 2008
+ * Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ * Copyright (C) 2007 Vladimir Ananiev <vovan888@gmail.com>
+ *
+ * based on PalmOne's (TM) PDAs support (palm.c)
+ */
+
+/*
+ * PalmOne's (TM) PDAs.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "sysemu.h"
+#include "console.h"
+#include "omap.h"
+#include "boards.h"
+#include "arm-misc.h"
+#include "flash.h"
+#include "blockdev.h"
+
+/*****************************************************************************/
+/* Siemens SX1 Cellphone V1 */
+/* - ARM OMAP310 processor
+ * - SRAM 192 kB
+ * - SDRAM 32 MB at 0x10000000
+ * - Boot flash 16 MB at 0x00000000
+ * - Application flash 8 MB at 0x04000000
+ * - 3 serial ports
+ * - 1 SecureDigital
+ * - 1 LCD display
+ * - 1 RTC
+ */
+
+/*****************************************************************************/
+/* Siemens SX1 Cellphone V2 */
+/* - ARM OMAP310 processor
+ * - SRAM 192 kB
+ * - SDRAM 32 MB at 0x10000000
+ * - Boot flash 32 MB at 0x00000000
+ * - 3 serial ports
+ * - 1 SecureDigital
+ * - 1 LCD display
+ * - 1 RTC
+ */
+
+static uint32_t static_readb(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+
+ return *val >> ((offset & 3) << 3);
+}
+
+static uint32_t static_readh(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+
+ return *val >> ((offset & 1) << 3);
+}
+
+static uint32_t static_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+
+ return *val >> ((offset & 0) << 3);
+}
+
+static void static_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+#ifdef SPY
+ printf("%s: value %08lx written at " PA_FMT "\n",
+ __FUNCTION__, value, offset);
+#endif
+}
+
+static CPUReadMemoryFunc * const static_readfn[] = {
+ static_readb,
+ static_readh,
+ static_readw,
+};
+
+static CPUWriteMemoryFunc * const static_writefn[] = {
+ static_write,
+ static_write,
+ static_write,
+};
+
+#define sdram_size 0x02000000
+#define sector_size (128 * 1024)
+#define flash0_size (16 * 1024 * 1024)
+#define flash1_size ( 8 * 1024 * 1024)
+#define flash2_size (32 * 1024 * 1024)
+#define total_ram_v1 (sdram_size + flash0_size + flash1_size + OMAP15XX_SRAM_SIZE)
+#define total_ram_v2 (sdram_size + flash2_size + OMAP15XX_SRAM_SIZE)
+
+static struct arm_boot_info sx1_binfo = {
+ .loader_start = OMAP_EMIFF_BASE,
+ .ram_size = sdram_size,
+ .board_id = 0x265,
+};
+
+static void sx1_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model,
+ const int version)
+{
+ struct omap_mpu_state_s *cpu;
+ int io;
+ static uint32_t cs0val = 0x00213090;
+ static uint32_t cs1val = 0x00215070;
+ static uint32_t cs2val = 0x00001139;
+ static uint32_t cs3val = 0x00001139;
+ DriveInfo *dinfo;
+ int fl_idx;
+ uint32_t flash_size = flash0_size;
+ int be;
+
+ if (version == 2) {
+ flash_size = flash2_size;
+ }
+
+ cpu = omap310_mpu_init(sx1_binfo.ram_size, cpu_model);
+
+ /* External Flash (EMIFS) */
+ cpu_register_physical_memory(OMAP_CS0_BASE, flash_size,
+ qemu_ram_alloc(NULL, "omap_sx1.flash0-0",
+ flash_size) | IO_MEM_ROM);
+
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs0val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS0_BASE + flash_size,
+ OMAP_CS0_SIZE - flash_size, io);
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs2val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS2_BASE, OMAP_CS2_SIZE, io);
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs3val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS3_BASE, OMAP_CS3_SIZE, io);
+
+ fl_idx = 0;
+#ifdef TARGET_WORDS_BIGENDIAN
+ be = 1;
+#else
+ be = 0;
+#endif
+
+ if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
+ if (!pflash_cfi01_register(OMAP_CS0_BASE, qemu_ram_alloc(NULL,
+ "omap_sx1.flash0-1", flash_size),
+ dinfo->bdrv, sector_size,
+ flash_size / sector_size,
+ 4, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory %d.\n",
+ fl_idx);
+ }
+ fl_idx++;
+ }
+
+ if ((version == 1) &&
+ (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) {
+ cpu_register_physical_memory(OMAP_CS1_BASE, flash1_size,
+ qemu_ram_alloc(NULL, "omap_sx1.flash1-0",
+ flash1_size) | IO_MEM_ROM);
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS1_BASE + flash1_size,
+ OMAP_CS1_SIZE - flash1_size, io);
+
+ if (!pflash_cfi01_register(OMAP_CS1_BASE, qemu_ram_alloc(NULL,
+ "omap_sx1.flash1-1", flash1_size),
+ dinfo->bdrv, sector_size,
+ flash1_size / sector_size,
+ 4, 0, 0, 0, 0, be)) {
+ fprintf(stderr, "qemu: Error registering flash memory %d.\n",
+ fl_idx);
+ }
+ fl_idx++;
+ } else {
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS1_BASE, OMAP_CS1_SIZE, io);
+ }
+
+ if (!kernel_filename && !fl_idx) {
+ fprintf(stderr, "Kernel or Flash image must be specified\n");
+ exit(1);
+ }
+
+ /* Load the kernel. */
+ if (kernel_filename) {
+ sx1_binfo.kernel_filename = kernel_filename;
+ sx1_binfo.kernel_cmdline = kernel_cmdline;
+ sx1_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(cpu->env, &sx1_binfo);
+ }
+
+ /* TODO: fix next line */
+ //~ qemu_console_resize(ds, 640, 480);
+}
+
+static void sx1_init_v1(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sx1_init(ram_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, 1);
+}
+
+static void sx1_init_v2(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sx1_init(ram_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, 2);
+}
+
+static QEMUMachine sx1_machine_v2 = {
+ .name = "sx1",
+ .desc = "Siemens SX1 (OMAP310) V2",
+ .init = sx1_init_v2,
+};
+
+static QEMUMachine sx1_machine_v1 = {
+ .name = "sx1-v1",
+ .desc = "Siemens SX1 (OMAP310) V1",
+ .init = sx1_init_v1,
+};
+
+static void sx1_machine_init(void)
+{
+ qemu_register_machine(&sx1_machine_v2);
+ qemu_register_machine(&sx1_machine_v1);
+}
+
+machine_init(sx1_machine_init);
diff --git a/hw/omap_synctimer.c b/hw/omap_synctimer.c
new file mode 100644
index 0000000..118668a
--- /dev/null
+++ b/hw/omap_synctimer.c
@@ -0,0 +1,96 @@
+/*
+ * TI OMAP2 32kHz sync timer emulation.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "omap.h"
+struct omap_synctimer_s {
+ uint32_t val;
+ uint16_t readh;
+};
+
+/* 32-kHz Sync Timer of the OMAP2 */
+static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) {
+ return muldiv64(qemu_get_clock(vm_clock), 0x8000, get_ticks_per_sec());
+}
+
+void omap_synctimer_reset(struct omap_synctimer_s *s)
+{
+ s->val = omap_synctimer_read(s);
+}
+
+static uint32_t omap_synctimer_readw(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
+
+ switch (addr) {
+ case 0x00: /* 32KSYNCNT_REV */
+ return 0x21;
+
+ case 0x10: /* CR */
+ return omap_synctimer_read(s) - s->val;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static uint32_t omap_synctimer_readh(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque;
+ uint32_t ret;
+
+ if (addr & 2)
+ return s->readh;
+ else {
+ ret = omap_synctimer_readw(opaque, addr);
+ s->readh = ret >> 16;
+ return ret & 0xffff;
+ }
+}
+
+static CPUReadMemoryFunc * const omap_synctimer_readfn[] = {
+ omap_badwidth_read32,
+ omap_synctimer_readh,
+ omap_synctimer_readw,
+};
+
+static void omap_synctimer_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ OMAP_BAD_REG(addr);
+}
+
+static CPUWriteMemoryFunc * const omap_synctimer_writefn[] = {
+ omap_badwidth_write32,
+ omap_synctimer_write,
+ omap_synctimer_write,
+};
+
+struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta,
+ struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk)
+{
+ struct omap_synctimer_s *s = qemu_mallocz(sizeof(*s));
+
+ omap_synctimer_reset(s);
+ omap_l4_attach(ta, 0, l4_register_io_memory(
+ omap_synctimer_readfn, omap_synctimer_writefn, s));
+
+ return s;
+}
diff --git a/hw/omap_tap.c b/hw/omap_tap.c
new file mode 100644
index 0000000..1f18ddd
--- /dev/null
+++ b/hw/omap_tap.c
@@ -0,0 +1,112 @@
+/*
+ * TI OMAP TEST-Chip-level TAP emulation.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) any later version of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "omap.h"
+
+/* TEST-Chip-level TAP */
+static uint32_t omap_tap_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque;
+
+ switch (addr) {
+ case 0x204: /* IDCODE_reg */
+ switch (s->mpu_model) {
+ case omap2420:
+ case omap2422:
+ case omap2423:
+ return 0x5b5d902f; /* ES 2.2 */
+ case omap2430:
+ return 0x5b68a02f; /* ES 2.2 */
+ case omap3430:
+ return 0x1b7ae02f; /* ES 2 */
+ default:
+ hw_error("%s: Bad mpu model\n", __FUNCTION__);
+ }
+
+ case 0x208: /* PRODUCTION_ID_reg for OMAP2 */
+ case 0x210: /* PRODUCTION_ID_reg for OMAP3 */
+ switch (s->mpu_model) {
+ case omap2420:
+ return 0x000254f0; /* POP ESHS2.1.1 in N91/93/95, ES2 in N800 */
+ case omap2422:
+ return 0x000400f0;
+ case omap2423:
+ return 0x000800f0;
+ case omap2430:
+ return 0x000000f0;
+ case omap3430:
+ return 0x000000f0;
+ default:
+ hw_error("%s: Bad mpu model\n", __FUNCTION__);
+ }
+
+ case 0x20c:
+ switch (s->mpu_model) {
+ case omap2420:
+ case omap2422:
+ case omap2423:
+ return 0xcafeb5d9; /* ES 2.2 */
+ case omap2430:
+ return 0xcafeb68a; /* ES 2.2 */
+ case omap3430:
+ return 0xcafeb7ae; /* ES 2 */
+ default:
+ hw_error("%s: Bad mpu model\n", __FUNCTION__);
+ }
+
+ case 0x218: /* DIE_ID_reg */
+ return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
+ case 0x21c: /* DIE_ID_reg */
+ return 0x54 << 24;
+ case 0x220: /* DIE_ID_reg */
+ return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
+ case 0x224: /* DIE_ID_reg */
+ return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0);
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_tap_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ OMAP_BAD_REG(addr);
+}
+
+static CPUReadMemoryFunc * const omap_tap_readfn[] = {
+ omap_badwidth_read32,
+ omap_badwidth_read32,
+ omap_tap_read,
+};
+
+static CPUWriteMemoryFunc * const omap_tap_writefn[] = {
+ omap_badwidth_write32,
+ omap_badwidth_write32,
+ omap_tap_write,
+};
+
+void omap_tap_init(struct omap_target_agent_s *ta,
+ struct omap_mpu_state_s *mpu)
+{
+ omap_l4_attach(ta, 0, l4_register_io_memory(
+ omap_tap_readfn, omap_tap_writefn, mpu));
+}
diff --git a/hw/omap_uart.c b/hw/omap_uart.c
new file mode 100644
index 0000000..9cee81d
--- /dev/null
+++ b/hw/omap_uart.c
@@ -0,0 +1,196 @@
+/*
+ * TI OMAP processors UART emulation.
+ *
+ * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2007-2009 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "qemu-char.h"
+#include "hw.h"
+#include "omap.h"
+/* We use pc-style serial ports. */
+#include "pc.h"
+
+/* UARTs */
+struct omap_uart_s {
+ target_phys_addr_t base;
+ SerialState *serial; /* TODO */
+ struct omap_target_agent_s *ta;
+ omap_clk fclk;
+ qemu_irq irq;
+
+ uint8_t eblr;
+ uint8_t syscontrol;
+ uint8_t wkup;
+ uint8_t cfps;
+ uint8_t mdr[2];
+ uint8_t scr;
+ uint8_t clksel;
+};
+
+void omap_uart_reset(struct omap_uart_s *s)
+{
+ s->eblr = 0x00;
+ s->syscontrol = 0;
+ s->wkup = 0x3f;
+ s->cfps = 0x69;
+ s->clksel = 0;
+}
+
+struct omap_uart_s *omap_uart_init(target_phys_addr_t base,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk,
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr)
+{
+ struct omap_uart_s *s = (struct omap_uart_s *)
+ qemu_mallocz(sizeof(struct omap_uart_s));
+
+ s->base = base;
+ s->fclk = fclk;
+ s->irq = irq;
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16,
+ chr ?: qemu_chr_open(label, "null", NULL), 1,
+ 1);
+#else
+ s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16,
+ chr ?: qemu_chr_open(label, "null", NULL), 1,
+ 0);
+#endif
+ return s;
+}
+
+static uint32_t omap_uart_read(void *opaque, target_phys_addr_t addr)
+{
+ struct omap_uart_s *s = (struct omap_uart_s *) opaque;
+
+ addr &= 0xff;
+ switch (addr) {
+ case 0x20: /* MDR1 */
+ return s->mdr[0];
+ case 0x24: /* MDR2 */
+ return s->mdr[1];
+ case 0x40: /* SCR */
+ return s->scr;
+ case 0x44: /* SSR */
+ return 0x0;
+ case 0x48: /* EBLR (OMAP2) */
+ return s->eblr;
+ case 0x4C: /* OSC_12M_SEL (OMAP1) */
+ return s->clksel;
+ case 0x50: /* MVR */
+ return 0x30;
+ case 0x54: /* SYSC (OMAP2) */
+ return s->syscontrol;
+ case 0x58: /* SYSS (OMAP2) */
+ return 1;
+ case 0x5c: /* WER (OMAP2) */
+ return s->wkup;
+ case 0x60: /* CFPS (OMAP2) */
+ return s->cfps;
+ }
+
+ OMAP_BAD_REG(addr);
+ return 0;
+}
+
+static void omap_uart_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct omap_uart_s *s = (struct omap_uart_s *) opaque;
+
+ addr &= 0xff;
+ switch (addr) {
+ case 0x20: /* MDR1 */
+ s->mdr[0] = value & 0x7f;
+ break;
+ case 0x24: /* MDR2 */
+ s->mdr[1] = value & 0xff;
+ break;
+ case 0x40: /* SCR */
+ s->scr = value & 0xff;
+ break;
+ case 0x48: /* EBLR (OMAP2) */
+ s->eblr = value & 0xff;
+ break;
+ case 0x4C: /* OSC_12M_SEL (OMAP1) */
+ s->clksel = value & 1;
+ break;
+ case 0x44: /* SSR */
+ case 0x50: /* MVR */
+ case 0x58: /* SYSS (OMAP2) */
+ OMAP_RO_REG(addr);
+ break;
+ case 0x54: /* SYSC (OMAP2) */
+ s->syscontrol = value & 0x1d;
+ if (value & 2)
+ omap_uart_reset(s);
+ break;
+ case 0x5c: /* WER (OMAP2) */
+ s->wkup = value & 0x7f;
+ break;
+ case 0x60: /* CFPS (OMAP2) */
+ s->cfps = value & 0xff;
+ break;
+ default:
+ OMAP_BAD_REG(addr);
+ }
+}
+
+static CPUReadMemoryFunc * const omap_uart_readfn[] = {
+ omap_uart_read,
+ omap_uart_read,
+ omap_badwidth_read8,
+};
+
+static CPUWriteMemoryFunc * const omap_uart_writefn[] = {
+ omap_uart_write,
+ omap_uart_write,
+ omap_badwidth_write8,
+};
+
+struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta,
+ qemu_irq irq, omap_clk fclk, omap_clk iclk,
+ qemu_irq txdma, qemu_irq rxdma,
+ const char *label, CharDriverState *chr)
+{
+ target_phys_addr_t base = omap_l4_attach(ta, 0, 0);
+ struct omap_uart_s *s = omap_uart_init(base, irq,
+ fclk, iclk, txdma, rxdma, label, chr);
+ int iomemtype = cpu_register_io_memory(omap_uart_readfn,
+ omap_uart_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ s->ta = ta;
+
+ cpu_register_physical_memory(base + 0x20, 0x100, iomemtype);
+
+ return s;
+}
+
+void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr)
+{
+ /* TODO: Should reuse or destroy current s->serial */
+#ifdef TARGET_WORDS_BIGENDIAN
+ s->serial = serial_mm_init(s->base, 2, s->irq,
+ omap_clk_getrate(s->fclk) / 16,
+ chr ?: qemu_chr_open("null", "null", NULL), 1,
+ 1);
+#else
+ s->serial = serial_mm_init(s->base, 2, s->irq,
+ omap_clk_getrate(s->fclk) / 16,
+ chr ?: qemu_chr_open("null", "null", NULL), 1,
+ 0);
+#endif
+}
diff --git a/hw/onenand.c b/hw/onenand.c
new file mode 100644
index 0000000..71c1ab4
--- /dev/null
+++ b/hw/onenand.c
@@ -0,0 +1,661 @@
+/*
+ * OneNAND flash memories emulation.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "hw.h"
+#include "flash.h"
+#include "irq.h"
+#include "blockdev.h"
+
+/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */
+#define PAGE_SHIFT 11
+
+/* Fixed */
+#define BLOCK_SHIFT (PAGE_SHIFT + 6)
+
+typedef struct {
+ uint32_t id;
+ int shift;
+ target_phys_addr_t base;
+ qemu_irq intr;
+ qemu_irq rdy;
+ BlockDriverState *bdrv;
+ BlockDriverState *bdrv_cur;
+ uint8_t *image;
+ uint8_t *otp;
+ uint8_t *current;
+ ram_addr_t ram;
+ uint8_t *boot[2];
+ uint8_t *data[2][2];
+ int iomemtype;
+ int cycle;
+ int otpmode;
+
+ uint16_t addr[8];
+ uint16_t unladdr[8];
+ int bufaddr;
+ int count;
+ uint16_t command;
+ uint16_t config[2];
+ uint16_t status;
+ uint16_t intstatus;
+ uint16_t wpstatus;
+
+ ECCState ecc;
+
+ int density_mask;
+ int secs;
+ int secs_cur;
+ int blocks;
+ uint8_t *blockwp;
+} OneNANDState;
+
+enum {
+ ONEN_BUF_BLOCK = 0,
+ ONEN_BUF_BLOCK2 = 1,
+ ONEN_BUF_DEST_BLOCK = 2,
+ ONEN_BUF_DEST_PAGE = 3,
+ ONEN_BUF_PAGE = 7,
+};
+
+enum {
+ ONEN_ERR_CMD = 1 << 10,
+ ONEN_ERR_ERASE = 1 << 11,
+ ONEN_ERR_PROG = 1 << 12,
+ ONEN_ERR_LOAD = 1 << 13,
+};
+
+enum {
+ ONEN_INT_RESET = 1 << 4,
+ ONEN_INT_ERASE = 1 << 5,
+ ONEN_INT_PROG = 1 << 6,
+ ONEN_INT_LOAD = 1 << 7,
+ ONEN_INT = 1 << 15,
+};
+
+enum {
+ ONEN_LOCK_LOCKTIGHTEN = 1 << 0,
+ ONEN_LOCK_LOCKED = 1 << 1,
+ ONEN_LOCK_UNLOCKED = 1 << 2,
+};
+
+void onenand_base_update(void *opaque, target_phys_addr_t new)
+{
+ OneNANDState *s = (OneNANDState *) opaque;
+
+ s->base = new;
+
+ /* XXX: We should use IO_MEM_ROMD but we broke it earlier...
+ * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to
+ * write boot commands. Also take note of the BWPS bit. */
+ cpu_register_physical_memory(s->base + (0x0000 << s->shift),
+ 0x0200 << s->shift, s->iomemtype);
+ cpu_register_physical_memory(s->base + (0x0200 << s->shift),
+ 0xbe00 << s->shift,
+ (s->ram +(0x0200 << s->shift)) | IO_MEM_RAM);
+ if (s->iomemtype)
+ cpu_register_physical_memory_offset(s->base + (0xc000 << s->shift),
+ 0x4000 << s->shift, s->iomemtype, (0xc000 << s->shift));
+}
+
+void onenand_base_unmap(void *opaque)
+{
+ OneNANDState *s = (OneNANDState *) opaque;
+
+ cpu_register_physical_memory(s->base,
+ 0x10000 << s->shift, IO_MEM_UNASSIGNED);
+}
+
+static void onenand_intr_update(OneNANDState *s)
+{
+ qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1);
+}
+
+/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */
+static void onenand_reset(OneNANDState *s, int cold)
+{
+ memset(&s->addr, 0, sizeof(s->addr));
+ s->command = 0;
+ s->count = 1;
+ s->bufaddr = 0;
+ s->config[0] = 0x40c0;
+ s->config[1] = 0x0000;
+ onenand_intr_update(s);
+ qemu_irq_raise(s->rdy);
+ s->status = 0x0000;
+ s->intstatus = cold ? 0x8080 : 0x8010;
+ s->unladdr[0] = 0;
+ s->unladdr[1] = 0;
+ s->wpstatus = 0x0002;
+ s->cycle = 0;
+ s->otpmode = 0;
+ s->bdrv_cur = s->bdrv;
+ s->current = s->image;
+ s->secs_cur = s->secs;
+
+ if (cold) {
+ /* Lock the whole flash */
+ memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks);
+
+ if (s->bdrv && bdrv_read(s->bdrv, 0, s->boot[0], 8) < 0)
+ hw_error("%s: Loading the BootRAM failed.\n", __FUNCTION__);
+ }
+}
+
+static inline int onenand_load_main(OneNANDState *s, int sec, int secn,
+ void *dest)
+{
+ if (s->bdrv_cur)
+ return bdrv_read(s->bdrv_cur, sec, dest, secn) < 0;
+ else if (sec + secn > s->secs_cur)
+ return 1;
+
+ memcpy(dest, s->current + (sec << 9), secn << 9);
+
+ return 0;
+}
+
+static inline int onenand_prog_main(OneNANDState *s, int sec, int secn,
+ void *src)
+{
+ if (s->bdrv_cur)
+ return bdrv_write(s->bdrv_cur, sec, src, secn) < 0;
+ else if (sec + secn > s->secs_cur)
+ return 1;
+
+ memcpy(s->current + (sec << 9), src, secn << 9);
+
+ return 0;
+}
+
+static inline int onenand_load_spare(OneNANDState *s, int sec, int secn,
+ void *dest)
+{
+ uint8_t buf[512];
+
+ if (s->bdrv_cur) {
+ if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
+ return 1;
+ memcpy(dest, buf + ((sec & 31) << 4), secn << 4);
+ } else if (sec + secn > s->secs_cur)
+ return 1;
+ else
+ memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4);
+
+ return 0;
+}
+
+static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn,
+ void *src)
+{
+ uint8_t buf[512];
+
+ if (s->bdrv_cur) {
+ if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0)
+ return 1;
+ memcpy(buf + ((sec & 31) << 4), src, secn << 4);
+ return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0;
+ } else if (sec + secn > s->secs_cur)
+ return 1;
+
+ memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4);
+
+ return 0;
+}
+
+static inline int onenand_erase(OneNANDState *s, int sec, int num)
+{
+ /* TODO: optimise */
+ uint8_t buf[512];
+
+ memset(buf, 0xff, sizeof(buf));
+ for (; num > 0; num --, sec ++) {
+ if (onenand_prog_main(s, sec, 1, buf))
+ return 1;
+ if (onenand_prog_spare(s, sec, 1, buf))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void onenand_command(OneNANDState *s, int cmd)
+{
+ int b;
+ int sec;
+ void *buf;
+#define SETADDR(block, page) \
+ sec = (s->addr[page] & 3) + \
+ ((((s->addr[page] >> 2) & 0x3f) + \
+ (((s->addr[block] & 0xfff) | \
+ (s->addr[block] >> 15 ? \
+ s->density_mask : 0)) << 6)) << (PAGE_SHIFT - 9));
+#define SETBUF_M() \
+ buf = (s->bufaddr & 8) ? \
+ s->data[(s->bufaddr >> 2) & 1][0] : s->boot[0]; \
+ buf += (s->bufaddr & 3) << 9;
+#define SETBUF_S() \
+ buf = (s->bufaddr & 8) ? \
+ s->data[(s->bufaddr >> 2) & 1][1] : s->boot[1]; \
+ buf += (s->bufaddr & 3) << 4;
+
+ switch (cmd) {
+ case 0x00: /* Load single/multiple sector data unit into buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_M()
+ if (onenand_load_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+
+#if 0
+ SETBUF_S()
+ if (onenand_load_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+#endif
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
+ break;
+ case 0x13: /* Load single/multiple spare sector into buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_S()
+ if (onenand_load_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD;
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_LOAD;
+ break;
+ case 0x80: /* Program single/multiple sector data unit from buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_M()
+ if (onenand_prog_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+#if 0
+ SETBUF_S()
+ if (onenand_prog_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+#endif
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_PROG;
+ break;
+ case 0x1a: /* Program single/multiple spare area sector from buffer */
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+
+ SETBUF_S()
+ if (onenand_prog_spare(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+ /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages)
+ * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages)
+ * then we need two split the read/write into two chunks.
+ */
+ s->intstatus |= ONEN_INT | ONEN_INT_PROG;
+ break;
+ case 0x1b: /* Copy-back program */
+ SETBUF_S()
+
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+ if (onenand_load_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+ SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE)
+ if (onenand_prog_main(s, sec, s->count, buf))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG;
+
+ /* TODO: spare areas */
+
+ s->intstatus |= ONEN_INT | ONEN_INT_PROG;
+ break;
+
+ case 0x23: /* Unlock NAND array block(s) */
+ s->intstatus |= ONEN_INT;
+
+ /* XXX the previous (?) area should be locked automatically */
+ for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
+ break;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
+ }
+ break;
+ case 0x27: /* Unlock All NAND array blocks */
+ s->intstatus |= ONEN_INT;
+
+ for (b = 0; b < s->blocks; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
+ break;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED;
+ }
+ break;
+
+ case 0x2a: /* Lock NAND array block(s) */
+ s->intstatus |= ONEN_INT;
+
+ for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN)
+ break;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED;
+ }
+ break;
+ case 0x2c: /* Lock-tight NAND array block(s) */
+ s->intstatus |= ONEN_INT;
+
+ for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) {
+ if (b >= s->blocks) {
+ s->status |= ONEN_ERR_CMD;
+ break;
+ }
+ if (s->blockwp[b] == ONEN_LOCK_UNLOCKED)
+ continue;
+
+ s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN;
+ }
+ break;
+
+ case 0x71: /* Erase-Verify-Read */
+ s->intstatus |= ONEN_INT;
+ break;
+ case 0x95: /* Multi-block erase */
+ qemu_irq_pulse(s->intr);
+ /* Fall through. */
+ case 0x94: /* Block erase */
+ sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) |
+ (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0))
+ << (BLOCK_SHIFT - 9);
+ if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9)))
+ s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE;
+
+ s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
+ break;
+ case 0xb0: /* Erase suspend */
+ break;
+ case 0x30: /* Erase resume */
+ s->intstatus |= ONEN_INT | ONEN_INT_ERASE;
+ break;
+
+ case 0xf0: /* Reset NAND Flash core */
+ onenand_reset(s, 0);
+ break;
+ case 0xf3: /* Reset OneNAND */
+ onenand_reset(s, 0);
+ break;
+
+ case 0x65: /* OTP Access */
+ s->intstatus |= ONEN_INT;
+ s->bdrv_cur = NULL;
+ s->current = s->otp;
+ s->secs_cur = 1 << (BLOCK_SHIFT - 9);
+ s->addr[ONEN_BUF_BLOCK] = 0;
+ s->otpmode = 1;
+ break;
+
+ default:
+ s->status |= ONEN_ERR_CMD;
+ s->intstatus |= ONEN_INT;
+ fprintf(stderr, "%s: unknown OneNAND command %x\n",
+ __FUNCTION__, cmd);
+ }
+
+ onenand_intr_update(s);
+}
+
+static uint32_t onenand_read(void *opaque, target_phys_addr_t addr)
+{
+ OneNANDState *s = (OneNANDState *) opaque;
+ int offset = addr >> s->shift;
+
+ switch (offset) {
+ case 0x0000 ... 0xc000:
+ return lduw_le_p(s->boot[0] + addr);
+
+ case 0xf000: /* Manufacturer ID */
+ return (s->id >> 16) & 0xff;
+ case 0xf001: /* Device ID */
+ return (s->id >> 8) & 0xff;
+ /* TODO: get the following values from a real chip! */
+ case 0xf002: /* Version ID */
+ return (s->id >> 0) & 0xff;
+ case 0xf003: /* Data Buffer size */
+ return 1 << PAGE_SHIFT;
+ case 0xf004: /* Boot Buffer size */
+ return 0x200;
+ case 0xf005: /* Amount of buffers */
+ return 1 | (2 << 8);
+ case 0xf006: /* Technology */
+ return 0;
+
+ case 0xf100 ... 0xf107: /* Start addresses */
+ return s->addr[offset - 0xf100];
+
+ case 0xf200: /* Start buffer */
+ return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10)));
+
+ case 0xf220: /* Command */
+ return s->command;
+ case 0xf221: /* System Configuration 1 */
+ return s->config[0] & 0xffe0;
+ case 0xf222: /* System Configuration 2 */
+ return s->config[1];
+
+ case 0xf240: /* Controller Status */
+ return s->status;
+ case 0xf241: /* Interrupt */
+ return s->intstatus;
+ case 0xf24c: /* Unlock Start Block Address */
+ return s->unladdr[0];
+ case 0xf24d: /* Unlock End Block Address */
+ return s->unladdr[1];
+ case 0xf24e: /* Write Protection Status */
+ return s->wpstatus;
+
+ case 0xff00: /* ECC Status */
+ return 0x00;
+ case 0xff01: /* ECC Result of main area data */
+ case 0xff02: /* ECC Result of spare area data */
+ case 0xff03: /* ECC Result of main area data */
+ case 0xff04: /* ECC Result of spare area data */
+ hw_error("%s: imeplement ECC\n", __FUNCTION__);
+ return 0x0000;
+ }
+
+ fprintf(stderr, "%s: unknown OneNAND register %x\n",
+ __FUNCTION__, offset);
+ return 0;
+}
+
+static void onenand_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ OneNANDState *s = (OneNANDState *) opaque;
+ int offset = addr >> s->shift;
+ int sec;
+
+ switch (offset) {
+ case 0x0000 ... 0x01ff:
+ case 0x8000 ... 0x800f:
+ if (s->cycle) {
+ s->cycle = 0;
+
+ if (value == 0x0000) {
+ SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE)
+ onenand_load_main(s, sec,
+ 1 << (PAGE_SHIFT - 9), s->data[0][0]);
+ s->addr[ONEN_BUF_PAGE] += 4;
+ s->addr[ONEN_BUF_PAGE] &= 0xff;
+ }
+ break;
+ }
+
+ switch (value) {
+ case 0x00f0: /* Reset OneNAND */
+ onenand_reset(s, 0);
+ break;
+
+ case 0x00e0: /* Load Data into Buffer */
+ s->cycle = 1;
+ break;
+
+ case 0x0090: /* Read Identification Data */
+ memset(s->boot[0], 0, 3 << s->shift);
+ s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff;
+ s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff;
+ s->boot[0][2 << s->shift] = s->wpstatus & 0xff;
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown OneNAND boot command %x\n",
+ __FUNCTION__, value);
+ }
+ break;
+
+ case 0xf100 ... 0xf107: /* Start addresses */
+ s->addr[offset - 0xf100] = value;
+ break;
+
+ case 0xf200: /* Start buffer */
+ s->bufaddr = (value >> 8) & 0xf;
+ if (PAGE_SHIFT == 11)
+ s->count = (value & 3) ?: 4;
+ else if (PAGE_SHIFT == 10)
+ s->count = (value & 1) ?: 2;
+ break;
+
+ case 0xf220: /* Command */
+ if (s->intstatus & (1 << 15))
+ break;
+ s->command = value;
+ onenand_command(s, s->command);
+ break;
+ case 0xf221: /* System Configuration 1 */
+ s->config[0] = value;
+ onenand_intr_update(s);
+ qemu_set_irq(s->rdy, (s->config[0] >> 7) & 1);
+ break;
+ case 0xf222: /* System Configuration 2 */
+ s->config[1] = value;
+ break;
+
+ case 0xf241: /* Interrupt */
+ s->intstatus &= value;
+ if ((1 << 15) & ~s->intstatus)
+ s->status &= ~(ONEN_ERR_CMD | ONEN_ERR_ERASE |
+ ONEN_ERR_PROG | ONEN_ERR_LOAD);
+ onenand_intr_update(s);
+ break;
+ case 0xf24c: /* Unlock Start Block Address */
+ s->unladdr[0] = value & (s->blocks - 1);
+ /* For some reason we have to set the end address to by default
+ * be same as start because the software forgets to write anything
+ * in there. */
+ s->unladdr[1] = value & (s->blocks - 1);
+ break;
+ case 0xf24d: /* Unlock End Block Address */
+ s->unladdr[1] = value & (s->blocks - 1);
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown OneNAND register %x\n",
+ __FUNCTION__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const onenand_readfn[] = {
+ onenand_read, /* TODO */
+ onenand_read,
+ onenand_read,
+};
+
+static CPUWriteMemoryFunc * const onenand_writefn[] = {
+ onenand_write, /* TODO */
+ onenand_write,
+ onenand_write,
+};
+
+void *onenand_init(uint32_t id, int regshift, qemu_irq irq)
+{
+ OneNANDState *s = (OneNANDState *) qemu_mallocz(sizeof(*s));
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
+ uint32_t size = 1 << (24 + ((id >> 12) & 7));
+ void *ram;
+
+ s->shift = regshift;
+ s->intr = irq;
+ s->rdy = NULL;
+ s->id = id;
+ s->blocks = size >> BLOCK_SHIFT;
+ s->secs = size >> 9;
+ s->blockwp = qemu_malloc(s->blocks);
+ s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0;
+ s->iomemtype = cpu_register_io_memory(onenand_readfn,
+ onenand_writefn, s, DEVICE_NATIVE_ENDIAN);
+ if (!dinfo)
+ s->image = memset(qemu_malloc(size + (size >> 5)),
+ 0xff, size + (size >> 5));
+ else
+ s->bdrv = dinfo->bdrv;
+ s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT),
+ 0xff, (64 + 2) << PAGE_SHIFT);
+ s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift);
+ ram = qemu_get_ram_ptr(s->ram);
+ s->boot[0] = ram + (0x0000 << s->shift);
+ s->boot[1] = ram + (0x8000 << s->shift);
+ s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift);
+ s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift);
+ s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift);
+ s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift);
+
+ onenand_reset(s, 1);
+
+ return s;
+}
+
+void *onenand_raw_otp(void *opaque)
+{
+ OneNANDState *s = (OneNANDState *) opaque;
+
+ return s->otp;
+}
diff --git a/hw/openpic.c b/hw/openpic.c
new file mode 100644
index 0000000..6d2cf99
--- /dev/null
+++ b/hw/openpic.c
@@ -0,0 +1,1686 @@
+/*
+ * OpenPIC emulation
+ *
+ * Copyright (c) 2004 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ *
+ * Based on OpenPic implementations:
+ * - Intel GW80314 I/O companion chip developer's manual
+ * - Motorola MPC8245 & MPC8540 user manuals.
+ * - Motorola MCP750 (aka Raven) programmer manual.
+ * - Motorola Harrier programmer manuel
+ *
+ * Serial interrupts, as implemented in Raven chipset are not supported yet.
+ *
+ */
+#include "hw.h"
+#include "ppc_mac.h"
+#include "pci.h"
+#include "openpic.h"
+
+//#define DEBUG_OPENPIC
+
+#ifdef DEBUG_OPENPIC
+#define DPRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define USE_MPCxxx /* Intel model is broken, for now */
+
+#if defined (USE_INTEL_GW80314)
+/* Intel GW80314 I/O Companion chip */
+
+#define MAX_CPU 4
+#define MAX_IRQ 32
+#define MAX_DBL 4
+#define MAX_MBX 4
+#define MAX_TMR 4
+#define VECTOR_BITS 8
+#define MAX_IPI 0
+
+#define VID (0x00000000)
+
+#elif defined(USE_MPCxxx)
+
+#define MAX_CPU 2
+#define MAX_IRQ 128
+#define MAX_DBL 0
+#define MAX_MBX 0
+#define MAX_TMR 4
+#define VECTOR_BITS 8
+#define MAX_IPI 4
+#define VID 0x03 /* MPIC version ID */
+#define VENI 0x00000000 /* Vendor ID */
+
+enum {
+ IRQ_IPVP = 0,
+ IRQ_IDE,
+};
+
+/* OpenPIC */
+#define OPENPIC_MAX_CPU 2
+#define OPENPIC_MAX_IRQ 64
+#define OPENPIC_EXT_IRQ 48
+#define OPENPIC_MAX_TMR MAX_TMR
+#define OPENPIC_MAX_IPI MAX_IPI
+
+/* Interrupt definitions */
+#define OPENPIC_IRQ_FE (OPENPIC_EXT_IRQ) /* Internal functional IRQ */
+#define OPENPIC_IRQ_ERR (OPENPIC_EXT_IRQ + 1) /* Error IRQ */
+#define OPENPIC_IRQ_TIM0 (OPENPIC_EXT_IRQ + 2) /* First timer IRQ */
+#if OPENPIC_MAX_IPI > 0
+#define OPENPIC_IRQ_IPI0 (OPENPIC_IRQ_TIM0 + OPENPIC_MAX_TMR) /* First IPI IRQ */
+#define OPENPIC_IRQ_DBL0 (OPENPIC_IRQ_IPI0 + (OPENPIC_MAX_CPU * OPENPIC_MAX_IPI)) /* First doorbell IRQ */
+#else
+#define OPENPIC_IRQ_DBL0 (OPENPIC_IRQ_TIM0 + OPENPIC_MAX_TMR) /* First doorbell IRQ */
+#define OPENPIC_IRQ_MBX0 (OPENPIC_IRQ_DBL0 + OPENPIC_MAX_DBL) /* First mailbox IRQ */
+#endif
+
+/* MPIC */
+#define MPIC_MAX_CPU 1
+#define MPIC_MAX_EXT 12
+#define MPIC_MAX_INT 64
+#define MPIC_MAX_MSG 4
+#define MPIC_MAX_MSI 8
+#define MPIC_MAX_TMR MAX_TMR
+#define MPIC_MAX_IPI MAX_IPI
+#define MPIC_MAX_IRQ (MPIC_MAX_EXT + MPIC_MAX_INT + MPIC_MAX_TMR + MPIC_MAX_MSG + MPIC_MAX_MSI + (MPIC_MAX_IPI * MPIC_MAX_CPU))
+
+/* Interrupt definitions */
+#define MPIC_EXT_IRQ 0
+#define MPIC_INT_IRQ (MPIC_EXT_IRQ + MPIC_MAX_EXT)
+#define MPIC_TMR_IRQ (MPIC_INT_IRQ + MPIC_MAX_INT)
+#define MPIC_MSG_IRQ (MPIC_TMR_IRQ + MPIC_MAX_TMR)
+#define MPIC_MSI_IRQ (MPIC_MSG_IRQ + MPIC_MAX_MSG)
+#define MPIC_IPI_IRQ (MPIC_MSI_IRQ + MPIC_MAX_MSI)
+
+#define MPIC_GLB_REG_START 0x0
+#define MPIC_GLB_REG_SIZE 0x10F0
+#define MPIC_TMR_REG_START 0x10F0
+#define MPIC_TMR_REG_SIZE 0x220
+#define MPIC_EXT_REG_START 0x10000
+#define MPIC_EXT_REG_SIZE 0x180
+#define MPIC_INT_REG_START 0x10200
+#define MPIC_INT_REG_SIZE 0x800
+#define MPIC_MSG_REG_START 0x11600
+#define MPIC_MSG_REG_SIZE 0x100
+#define MPIC_MSI_REG_START 0x11C00
+#define MPIC_MSI_REG_SIZE 0x100
+#define MPIC_CPU_REG_START 0x20000
+#define MPIC_CPU_REG_SIZE 0x100
+
+enum mpic_ide_bits {
+ IDR_EP = 0,
+ IDR_CI0 = 1,
+ IDR_CI1 = 2,
+ IDR_P1 = 30,
+ IDR_P0 = 31,
+};
+
+#else
+#error "Please select which OpenPic implementation is to be emulated"
+#endif
+
+#define OPENPIC_PAGE_SIZE 4096
+
+#define BF_WIDTH(_bits_) \
+(((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8))
+
+static inline void set_bit (uint32_t *field, int bit)
+{
+ field[bit >> 5] |= 1 << (bit & 0x1F);
+}
+
+static inline void reset_bit (uint32_t *field, int bit)
+{
+ field[bit >> 5] &= ~(1 << (bit & 0x1F));
+}
+
+static inline int test_bit (uint32_t *field, int bit)
+{
+ return (field[bit >> 5] & 1 << (bit & 0x1F)) != 0;
+}
+
+enum {
+ IRQ_EXTERNAL = 0x01,
+ IRQ_INTERNAL = 0x02,
+ IRQ_TIMER = 0x04,
+ IRQ_SPECIAL = 0x08,
+};
+
+typedef struct IRQ_queue_t {
+ uint32_t queue[BF_WIDTH(MAX_IRQ)];
+ int next;
+ int priority;
+} IRQ_queue_t;
+
+typedef struct IRQ_src_t {
+ uint32_t ipvp; /* IRQ vector/priority register */
+ uint32_t ide; /* IRQ destination register */
+ int type;
+ int last_cpu;
+ int pending; /* TRUE if IRQ is pending */
+} IRQ_src_t;
+
+enum IPVP_bits {
+ IPVP_MASK = 31,
+ IPVP_ACTIVITY = 30,
+ IPVP_MODE = 29,
+ IPVP_POLARITY = 23,
+ IPVP_SENSE = 22,
+};
+#define IPVP_PRIORITY_MASK (0x1F << 16)
+#define IPVP_PRIORITY(_ipvpr_) ((int)(((_ipvpr_) & IPVP_PRIORITY_MASK) >> 16))
+#define IPVP_VECTOR_MASK ((1 << VECTOR_BITS) - 1)
+#define IPVP_VECTOR(_ipvpr_) ((_ipvpr_) & IPVP_VECTOR_MASK)
+
+typedef struct IRQ_dst_t {
+ uint32_t tfrr;
+ uint32_t pctp; /* CPU current task priority */
+ uint32_t pcsr; /* CPU sensitivity register */
+ IRQ_queue_t raised;
+ IRQ_queue_t servicing;
+ qemu_irq *irqs;
+} IRQ_dst_t;
+
+typedef struct openpic_t {
+ PCIDevice pci_dev;
+ int mem_index;
+ /* Global registers */
+ uint32_t frep; /* Feature reporting register */
+ uint32_t glbc; /* Global configuration register */
+ uint32_t micr; /* MPIC interrupt configuration register */
+ uint32_t veni; /* Vendor identification register */
+ uint32_t pint; /* Processor initialization register */
+ uint32_t spve; /* Spurious vector register */
+ uint32_t tifr; /* Timer frequency reporting register */
+ /* Source registers */
+ IRQ_src_t src[MAX_IRQ];
+ /* Local registers per output pin */
+ IRQ_dst_t dst[MAX_CPU];
+ int nb_cpus;
+ /* Timer registers */
+ struct {
+ uint32_t ticc; /* Global timer current count register */
+ uint32_t tibc; /* Global timer base count register */
+ } timers[MAX_TMR];
+#if MAX_DBL > 0
+ /* Doorbell registers */
+ uint32_t dar; /* Doorbell activate register */
+ struct {
+ uint32_t dmr; /* Doorbell messaging register */
+ } doorbells[MAX_DBL];
+#endif
+#if MAX_MBX > 0
+ /* Mailbox registers */
+ struct {
+ uint32_t mbr; /* Mailbox register */
+ } mailboxes[MAX_MAILBOXES];
+#endif
+ /* IRQ out is used when in bypass mode (not implemented) */
+ qemu_irq irq_out;
+ int max_irq;
+ int irq_ipi0;
+ int irq_tim0;
+ void (*reset) (void *);
+ void (*irq_raise) (struct openpic_t *, int, IRQ_src_t *);
+} openpic_t;
+
+static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ)
+{
+ set_bit(q->queue, n_IRQ);
+}
+
+static inline void IRQ_resetbit (IRQ_queue_t *q, int n_IRQ)
+{
+ reset_bit(q->queue, n_IRQ);
+}
+
+static inline int IRQ_testbit (IRQ_queue_t *q, int n_IRQ)
+{
+ return test_bit(q->queue, n_IRQ);
+}
+
+static void IRQ_check (openpic_t *opp, IRQ_queue_t *q)
+{
+ int next, i;
+ int priority;
+
+ next = -1;
+ priority = -1;
+ for (i = 0; i < opp->max_irq; i++) {
+ if (IRQ_testbit(q, i)) {
+ DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n",
+ i, IPVP_PRIORITY(opp->src[i].ipvp), priority);
+ if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) {
+ next = i;
+ priority = IPVP_PRIORITY(opp->src[i].ipvp);
+ }
+ }
+ }
+ q->next = next;
+ q->priority = priority;
+}
+
+static int IRQ_get_next (openpic_t *opp, IRQ_queue_t *q)
+{
+ if (q->next == -1) {
+ /* XXX: optimize */
+ IRQ_check(opp, q);
+ }
+
+ return q->next;
+}
+
+static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ)
+{
+ IRQ_dst_t *dst;
+ IRQ_src_t *src;
+ int priority;
+
+ dst = &opp->dst[n_CPU];
+ src = &opp->src[n_IRQ];
+ priority = IPVP_PRIORITY(src->ipvp);
+ if (priority <= dst->pctp) {
+ /* Too low priority */
+ DPRINTF("%s: IRQ %d has too low priority on CPU %d\n",
+ __func__, n_IRQ, n_CPU);
+ return;
+ }
+ if (IRQ_testbit(&dst->raised, n_IRQ)) {
+ /* Interrupt miss */
+ DPRINTF("%s: IRQ %d was missed on CPU %d\n",
+ __func__, n_IRQ, n_CPU);
+ return;
+ }
+ set_bit(&src->ipvp, IPVP_ACTIVITY);
+ IRQ_setbit(&dst->raised, n_IRQ);
+ if (priority < dst->raised.priority) {
+ /* An higher priority IRQ is already raised */
+ DPRINTF("%s: IRQ %d is hidden by raised IRQ %d on CPU %d\n",
+ __func__, n_IRQ, dst->raised.next, n_CPU);
+ return;
+ }
+ IRQ_get_next(opp, &dst->raised);
+ if (IRQ_get_next(opp, &dst->servicing) != -1 &&
+ priority <= dst->servicing.priority) {
+ DPRINTF("%s: IRQ %d is hidden by servicing IRQ %d on CPU %d\n",
+ __func__, n_IRQ, dst->servicing.next, n_CPU);
+ /* Already servicing a higher priority IRQ */
+ return;
+ }
+ DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n", n_CPU, n_IRQ);
+ opp->irq_raise(opp, n_CPU, src);
+}
+
+/* update pic state because registers for n_IRQ have changed value */
+static void openpic_update_irq(openpic_t *opp, int n_IRQ)
+{
+ IRQ_src_t *src;
+ int i;
+
+ src = &opp->src[n_IRQ];
+
+ if (!src->pending) {
+ /* no irq pending */
+ DPRINTF("%s: IRQ %d is not pending\n", __func__, n_IRQ);
+ return;
+ }
+ if (test_bit(&src->ipvp, IPVP_MASK)) {
+ /* Interrupt source is disabled */
+ DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ);
+ return;
+ }
+ if (IPVP_PRIORITY(src->ipvp) == 0) {
+ /* Priority set to zero */
+ DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ);
+ return;
+ }
+ if (test_bit(&src->ipvp, IPVP_ACTIVITY)) {
+ /* IRQ already active */
+ DPRINTF("%s: IRQ %d is already active\n", __func__, n_IRQ);
+ return;
+ }
+ if (src->ide == 0x00000000) {
+ /* No target */
+ DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ);
+ return;
+ }
+
+ if (src->ide == (1 << src->last_cpu)) {
+ /* Only one CPU is allowed to receive this IRQ */
+ IRQ_local_pipe(opp, src->last_cpu, n_IRQ);
+ } else if (!test_bit(&src->ipvp, IPVP_MODE)) {
+ /* Directed delivery mode */
+ for (i = 0; i < opp->nb_cpus; i++) {
+ if (test_bit(&src->ide, i))
+ IRQ_local_pipe(opp, i, n_IRQ);
+ }
+ } else {
+ /* Distributed delivery mode */
+ for (i = src->last_cpu + 1; i != src->last_cpu; i++) {
+ if (i == opp->nb_cpus)
+ i = 0;
+ if (test_bit(&src->ide, i)) {
+ IRQ_local_pipe(opp, i, n_IRQ);
+ src->last_cpu = i;
+ break;
+ }
+ }
+ }
+}
+
+static void openpic_set_irq(void *opaque, int n_IRQ, int level)
+{
+ openpic_t *opp = opaque;
+ IRQ_src_t *src;
+
+ src = &opp->src[n_IRQ];
+ DPRINTF("openpic: set irq %d = %d ipvp=%08x\n",
+ n_IRQ, level, src->ipvp);
+ if (test_bit(&src->ipvp, IPVP_SENSE)) {
+ /* level-sensitive irq */
+ src->pending = level;
+ if (!level)
+ reset_bit(&src->ipvp, IPVP_ACTIVITY);
+ } else {
+ /* edge-sensitive irq */
+ if (level)
+ src->pending = 1;
+ }
+ openpic_update_irq(opp, n_IRQ);
+}
+
+static void openpic_reset (void *opaque)
+{
+ openpic_t *opp = (openpic_t *)opaque;
+ int i;
+
+ opp->glbc = 0x80000000;
+ /* Initialise controller registers */
+ opp->frep = ((OPENPIC_EXT_IRQ - 1) << 16) | ((MAX_CPU - 1) << 8) | VID;
+ opp->veni = VENI;
+ opp->pint = 0x00000000;
+ opp->spve = 0x000000FF;
+ opp->tifr = 0x003F7A00;
+ /* ? */
+ opp->micr = 0x00000000;
+ /* Initialise IRQ sources */
+ for (i = 0; i < opp->max_irq; i++) {
+ opp->src[i].ipvp = 0xA0000000;
+ opp->src[i].ide = 0x00000000;
+ }
+ /* Initialise IRQ destinations */
+ for (i = 0; i < MAX_CPU; i++) {
+ opp->dst[i].pctp = 0x0000000F;
+ opp->dst[i].pcsr = 0x00000000;
+ memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t));
+ opp->dst[i].raised.next = -1;
+ memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
+ opp->dst[i].servicing.next = -1;
+ }
+ /* Initialise timers */
+ for (i = 0; i < MAX_TMR; i++) {
+ opp->timers[i].ticc = 0x00000000;
+ opp->timers[i].tibc = 0x80000000;
+ }
+ /* Initialise doorbells */
+#if MAX_DBL > 0
+ opp->dar = 0x00000000;
+ for (i = 0; i < MAX_DBL; i++) {
+ opp->doorbells[i].dmr = 0x00000000;
+ }
+#endif
+ /* Initialise mailboxes */
+#if MAX_MBX > 0
+ for (i = 0; i < MAX_MBX; i++) { /* ? */
+ opp->mailboxes[i].mbr = 0x00000000;
+ }
+#endif
+ /* Go out of RESET state */
+ opp->glbc = 0x00000000;
+}
+
+static inline uint32_t read_IRQreg (openpic_t *opp, int n_IRQ, uint32_t reg)
+{
+ uint32_t retval;
+
+ switch (reg) {
+ case IRQ_IPVP:
+ retval = opp->src[n_IRQ].ipvp;
+ break;
+ case IRQ_IDE:
+ retval = opp->src[n_IRQ].ide;
+ break;
+ }
+
+ return retval;
+}
+
+static inline void write_IRQreg (openpic_t *opp, int n_IRQ,
+ uint32_t reg, uint32_t val)
+{
+ uint32_t tmp;
+
+ switch (reg) {
+ case IRQ_IPVP:
+ /* NOTE: not fully accurate for special IRQs, but simple and
+ sufficient */
+ /* ACTIVITY bit is read-only */
+ opp->src[n_IRQ].ipvp =
+ (opp->src[n_IRQ].ipvp & 0x40000000) |
+ (val & 0x800F00FF);
+ openpic_update_irq(opp, n_IRQ);
+ DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n",
+ n_IRQ, val, opp->src[n_IRQ].ipvp);
+ break;
+ case IRQ_IDE:
+ tmp = val & 0xC0000000;
+ tmp |= val & ((1 << MAX_CPU) - 1);
+ opp->src[n_IRQ].ide = tmp;
+ DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide);
+ break;
+ }
+}
+
+#if 0 // Code provision for Intel model
+#if MAX_DBL > 0
+static uint32_t read_doorbell_register (openpic_t *opp,
+ int n_dbl, uint32_t offset)
+{
+ uint32_t retval;
+
+ switch (offset) {
+ case DBL_IPVP_OFFSET:
+ retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP);
+ break;
+ case DBL_IDE_OFFSET:
+ retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE);
+ break;
+ case DBL_DMR_OFFSET:
+ retval = opp->doorbells[n_dbl].dmr;
+ break;
+ }
+
+ return retval;
+}
+
+static void write_doorbell_register (penpic_t *opp, int n_dbl,
+ uint32_t offset, uint32_t value)
+{
+ switch (offset) {
+ case DBL_IVPR_OFFSET:
+ write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value);
+ break;
+ case DBL_IDE_OFFSET:
+ write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value);
+ break;
+ case DBL_DMR_OFFSET:
+ opp->doorbells[n_dbl].dmr = value;
+ break;
+ }
+}
+#endif
+
+#if MAX_MBX > 0
+static uint32_t read_mailbox_register (openpic_t *opp,
+ int n_mbx, uint32_t offset)
+{
+ uint32_t retval;
+
+ switch (offset) {
+ case MBX_MBR_OFFSET:
+ retval = opp->mailboxes[n_mbx].mbr;
+ break;
+ case MBX_IVPR_OFFSET:
+ retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP);
+ break;
+ case MBX_DMR_OFFSET:
+ retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE);
+ break;
+ }
+
+ return retval;
+}
+
+static void write_mailbox_register (openpic_t *opp, int n_mbx,
+ uint32_t address, uint32_t value)
+{
+ switch (offset) {
+ case MBX_MBR_OFFSET:
+ opp->mailboxes[n_mbx].mbr = value;
+ break;
+ case MBX_IVPR_OFFSET:
+ write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value);
+ break;
+ case MBX_DMR_OFFSET:
+ write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value);
+ break;
+ }
+}
+#endif
+#endif /* 0 : Code provision for Intel model */
+
+static void openpic_gbl_write (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ IRQ_dst_t *dst;
+ int idx;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+ addr &= 0xFF;
+ switch (addr) {
+ case 0x00: /* FREP */
+ break;
+ case 0x20: /* GLBC */
+ if (val & 0x80000000 && opp->reset)
+ opp->reset(opp);
+ opp->glbc = val & ~0x80000000;
+ break;
+ case 0x80: /* VENI */
+ break;
+ case 0x90: /* PINT */
+ for (idx = 0; idx < opp->nb_cpus; idx++) {
+ if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) {
+ DPRINTF("Raise OpenPIC RESET output for CPU %d\n", idx);
+ dst = &opp->dst[idx];
+ qemu_irq_raise(dst->irqs[OPENPIC_OUTPUT_RESET]);
+ } else if (!(val & (1 << idx)) && (opp->pint & (1 << idx))) {
+ DPRINTF("Lower OpenPIC RESET output for CPU %d\n", idx);
+ dst = &opp->dst[idx];
+ qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_RESET]);
+ }
+ }
+ opp->pint = val;
+ break;
+#if MAX_IPI > 0
+ case 0xA0: /* IPI_IPVP */
+ case 0xB0:
+ case 0xC0:
+ case 0xD0:
+ {
+ int idx;
+ idx = (addr - 0xA0) >> 4;
+ write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP, val);
+ }
+ break;
+#endif
+ case 0xE0: /* SPVE */
+ opp->spve = val & 0x000000FF;
+ break;
+ case 0xF0: /* TIFR */
+ opp->tifr = val;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t openpic_gbl_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr &= 0xFF;
+ switch (addr) {
+ case 0x00: /* FREP */
+ retval = opp->frep;
+ break;
+ case 0x20: /* GLBC */
+ retval = opp->glbc;
+ break;
+ case 0x80: /* VENI */
+ retval = opp->veni;
+ break;
+ case 0x90: /* PINT */
+ retval = 0x00000000;
+ break;
+#if MAX_IPI > 0
+ case 0xA0: /* IPI_IPVP */
+ case 0xB0:
+ case 0xC0:
+ case 0xD0:
+ {
+ int idx;
+ idx = (addr - 0xA0) >> 4;
+ retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP);
+ }
+ break;
+#endif
+ case 0xE0: /* SPVE */
+ retval = opp->spve;
+ break;
+ case 0xF0: /* TIFR */
+ retval = opp->tifr;
+ break;
+ default:
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+
+ return retval;
+}
+
+static void openpic_timer_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ int idx;
+
+ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+ addr -= 0x1100;
+ addr &= 0xFFFF;
+ idx = (addr & 0xFFF0) >> 6;
+ addr = addr & 0x30;
+ switch (addr) {
+ case 0x00: /* TICC */
+ break;
+ case 0x10: /* TIBC */
+ if ((opp->timers[idx].ticc & 0x80000000) != 0 &&
+ (val & 0x80000000) == 0 &&
+ (opp->timers[idx].tibc & 0x80000000) != 0)
+ opp->timers[idx].ticc &= ~0x80000000;
+ opp->timers[idx].tibc = val;
+ break;
+ case 0x20: /* TIVP */
+ write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP, val);
+ break;
+ case 0x30: /* TIDE */
+ write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE, val);
+ break;
+ }
+}
+
+static uint32_t openpic_timer_read (void *opaque, uint32_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+ int idx;
+
+ DPRINTF("%s: addr %08x\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr -= 0x1100;
+ addr &= 0xFFFF;
+ idx = (addr & 0xFFF0) >> 6;
+ addr = addr & 0x30;
+ switch (addr) {
+ case 0x00: /* TICC */
+ retval = opp->timers[idx].ticc;
+ break;
+ case 0x10: /* TIBC */
+ retval = opp->timers[idx].tibc;
+ break;
+ case 0x20: /* TIPV */
+ retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP);
+ break;
+ case 0x30: /* TIDE */
+ retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE);
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+
+ return retval;
+}
+
+static void openpic_src_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ int idx;
+
+ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+ addr = addr & 0xFFF0;
+ idx = addr >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ write_IRQreg(opp, idx, IRQ_IDE, val);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ write_IRQreg(opp, idx, IRQ_IPVP, val);
+ }
+}
+
+static uint32_t openpic_src_read (void *opaque, uint32_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+ int idx;
+
+ DPRINTF("%s: addr %08x\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr = addr & 0xFFF0;
+ idx = addr >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ retval = read_IRQreg(opp, idx, IRQ_IDE);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ retval = read_IRQreg(opp, idx, IRQ_IPVP);
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+
+ return retval;
+}
+
+static void openpic_cpu_write (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+ IRQ_src_t *src;
+ IRQ_dst_t *dst;
+ int idx, s_IRQ, n_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+ addr &= 0x1FFF0;
+ idx = addr / 0x1000;
+ dst = &opp->dst[idx];
+ addr &= 0xFF0;
+ switch (addr) {
+#if MAX_IPI > 0
+ case 0x40: /* PIPD */
+ case 0x50:
+ case 0x60:
+ case 0x70:
+ idx = (addr - 0x40) >> 4;
+ write_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE, val);
+ openpic_set_irq(opp, opp->irq_ipi0 + idx, 1);
+ openpic_set_irq(opp, opp->irq_ipi0 + idx, 0);
+ break;
+#endif
+ case 0x80: /* PCTP */
+ dst->pctp = val & 0x0000000F;
+ break;
+ case 0x90: /* WHOAMI */
+ /* Read-only register */
+ break;
+ case 0xA0: /* PIAC */
+ /* Read-only register */
+ break;
+ case 0xB0: /* PEOI */
+ DPRINTF("PEOI\n");
+ s_IRQ = IRQ_get_next(opp, &dst->servicing);
+ IRQ_resetbit(&dst->servicing, s_IRQ);
+ dst->servicing.next = -1;
+ /* Set up next servicing IRQ */
+ s_IRQ = IRQ_get_next(opp, &dst->servicing);
+ /* Check queued interrupts. */
+ n_IRQ = IRQ_get_next(opp, &dst->raised);
+ src = &opp->src[n_IRQ];
+ if (n_IRQ != -1 &&
+ (s_IRQ == -1 ||
+ IPVP_PRIORITY(src->ipvp) > dst->servicing.priority)) {
+ DPRINTF("Raise OpenPIC INT output cpu %d irq %d\n",
+ idx, n_IRQ);
+ opp->irq_raise(opp, idx, src);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t openpic_cpu_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *opp = opaque;
+ IRQ_src_t *src;
+ IRQ_dst_t *dst;
+ uint32_t retval;
+ int idx, n_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr &= 0x1FFF0;
+ idx = addr / 0x1000;
+ dst = &opp->dst[idx];
+ addr &= 0xFF0;
+ switch (addr) {
+ case 0x80: /* PCTP */
+ retval = dst->pctp;
+ break;
+ case 0x90: /* WHOAMI */
+ retval = idx;
+ break;
+ case 0xA0: /* PIAC */
+ DPRINTF("Lower OpenPIC INT output\n");
+ qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]);
+ n_IRQ = IRQ_get_next(opp, &dst->raised);
+ DPRINTF("PIAC: irq=%d\n", n_IRQ);
+ if (n_IRQ == -1) {
+ /* No more interrupt pending */
+ retval = IPVP_VECTOR(opp->spve);
+ } else {
+ src = &opp->src[n_IRQ];
+ if (!test_bit(&src->ipvp, IPVP_ACTIVITY) ||
+ !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) {
+ /* - Spurious level-sensitive IRQ
+ * - Priorities has been changed
+ * and the pending IRQ isn't allowed anymore
+ */
+ reset_bit(&src->ipvp, IPVP_ACTIVITY);
+ retval = IPVP_VECTOR(opp->spve);
+ } else {
+ /* IRQ enter servicing state */
+ IRQ_setbit(&dst->servicing, n_IRQ);
+ retval = IPVP_VECTOR(src->ipvp);
+ }
+ IRQ_resetbit(&dst->raised, n_IRQ);
+ dst->raised.next = -1;
+ if (!test_bit(&src->ipvp, IPVP_SENSE)) {
+ /* edge-sensitive IRQ */
+ reset_bit(&src->ipvp, IPVP_ACTIVITY);
+ src->pending = 0;
+ }
+ }
+ break;
+ case 0xB0: /* PEOI */
+ retval = 0;
+ break;
+#if MAX_IPI > 0
+ case 0x40: /* IDE */
+ case 0x50:
+ idx = (addr - 0x40) >> 4;
+ retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IDE);
+ break;
+#endif
+ default:
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+
+ return retval;
+}
+
+static void openpic_buggy_write (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ printf("Invalid OPENPIC write access !\n");
+}
+
+static uint32_t openpic_buggy_read (void *opaque, target_phys_addr_t addr)
+{
+ printf("Invalid OPENPIC read access !\n");
+
+ return -1;
+}
+
+static void openpic_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ openpic_t *opp = opaque;
+
+ addr &= 0x3FFFF;
+ DPRINTF("%s: offset %08x val: %08x\n", __func__, (int)addr, val);
+ if (addr < 0x1100) {
+ /* Global registers */
+ openpic_gbl_write(opp, addr, val);
+ } else if (addr < 0x10000) {
+ /* Timers registers */
+ openpic_timer_write(opp, addr, val);
+ } else if (addr < 0x20000) {
+ /* Source registers */
+ openpic_src_write(opp, addr, val);
+ } else {
+ /* CPU registers */
+ openpic_cpu_write(opp, addr, val);
+ }
+}
+
+static uint32_t openpic_readl (void *opaque,target_phys_addr_t addr)
+{
+ openpic_t *opp = opaque;
+ uint32_t retval;
+
+ addr &= 0x3FFFF;
+ DPRINTF("%s: offset %08x\n", __func__, (int)addr);
+ if (addr < 0x1100) {
+ /* Global registers */
+ retval = openpic_gbl_read(opp, addr);
+ } else if (addr < 0x10000) {
+ /* Timers registers */
+ retval = openpic_timer_read(opp, addr);
+ } else if (addr < 0x20000) {
+ /* Source registers */
+ retval = openpic_src_read(opp, addr);
+ } else {
+ /* CPU registers */
+ retval = openpic_cpu_read(opp, addr);
+ }
+
+ return retval;
+}
+
+static CPUWriteMemoryFunc * const openpic_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &openpic_writel,
+};
+
+static CPUReadMemoryFunc * const openpic_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &openpic_readl,
+};
+
+static void openpic_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ openpic_t *opp;
+
+ DPRINTF("Map OpenPIC\n");
+ opp = (openpic_t *)pci_dev;
+ /* Global registers */
+ DPRINTF("Register OPENPIC gbl %08x => %08x\n",
+ addr + 0x1000, addr + 0x1000 + 0x100);
+ /* Timer registers */
+ DPRINTF("Register OPENPIC timer %08x => %08x\n",
+ addr + 0x1100, addr + 0x1100 + 0x40 * MAX_TMR);
+ /* Interrupt source registers */
+ DPRINTF("Register OPENPIC src %08x => %08x\n",
+ addr + 0x10000, addr + 0x10000 + 0x20 * (OPENPIC_EXT_IRQ + 2));
+ /* Per CPU registers */
+ DPRINTF("Register OPENPIC dst %08x => %08x\n",
+ addr + 0x20000, addr + 0x20000 + 0x1000 * MAX_CPU);
+ cpu_register_physical_memory(addr, 0x40000, opp->mem_index);
+#if 0 // Don't implement ISU for now
+ opp_io_memory = cpu_register_io_memory(openpic_src_read,
+ openpic_src_write, NULL
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2),
+ opp_io_memory);
+#endif
+}
+
+static void openpic_save_IRQ_queue(QEMUFile* f, IRQ_queue_t *q)
+{
+ unsigned int i;
+
+ for (i = 0; i < BF_WIDTH(MAX_IRQ); i++)
+ qemu_put_be32s(f, &q->queue[i]);
+
+ qemu_put_sbe32s(f, &q->next);
+ qemu_put_sbe32s(f, &q->priority);
+}
+
+static void openpic_save(QEMUFile* f, void *opaque)
+{
+ openpic_t *opp = (openpic_t *)opaque;
+ unsigned int i;
+
+ qemu_put_be32s(f, &opp->frep);
+ qemu_put_be32s(f, &opp->glbc);
+ qemu_put_be32s(f, &opp->micr);
+ qemu_put_be32s(f, &opp->veni);
+ qemu_put_be32s(f, &opp->pint);
+ qemu_put_be32s(f, &opp->spve);
+ qemu_put_be32s(f, &opp->tifr);
+
+ for (i = 0; i < opp->max_irq; i++) {
+ qemu_put_be32s(f, &opp->src[i].ipvp);
+ qemu_put_be32s(f, &opp->src[i].ide);
+ qemu_put_sbe32s(f, &opp->src[i].type);
+ qemu_put_sbe32s(f, &opp->src[i].last_cpu);
+ qemu_put_sbe32s(f, &opp->src[i].pending);
+ }
+
+ qemu_put_sbe32s(f, &opp->nb_cpus);
+
+ for (i = 0; i < opp->nb_cpus; i++) {
+ qemu_put_be32s(f, &opp->dst[i].tfrr);
+ qemu_put_be32s(f, &opp->dst[i].pctp);
+ qemu_put_be32s(f, &opp->dst[i].pcsr);
+ openpic_save_IRQ_queue(f, &opp->dst[i].raised);
+ openpic_save_IRQ_queue(f, &opp->dst[i].servicing);
+ }
+
+ for (i = 0; i < MAX_TMR; i++) {
+ qemu_put_be32s(f, &opp->timers[i].ticc);
+ qemu_put_be32s(f, &opp->timers[i].tibc);
+ }
+
+#if MAX_DBL > 0
+ qemu_put_be32s(f, &opp->dar);
+
+ for (i = 0; i < MAX_DBL; i++) {
+ qemu_put_be32s(f, &opp->doorbells[i].dmr);
+ }
+#endif
+
+#if MAX_MBX > 0
+ for (i = 0; i < MAX_MAILBOXES; i++) {
+ qemu_put_be32s(f, &opp->mailboxes[i].mbr);
+ }
+#endif
+
+ pci_device_save(&opp->pci_dev, f);
+}
+
+static void openpic_load_IRQ_queue(QEMUFile* f, IRQ_queue_t *q)
+{
+ unsigned int i;
+
+ for (i = 0; i < BF_WIDTH(MAX_IRQ); i++)
+ qemu_get_be32s(f, &q->queue[i]);
+
+ qemu_get_sbe32s(f, &q->next);
+ qemu_get_sbe32s(f, &q->priority);
+}
+
+static int openpic_load(QEMUFile* f, void *opaque, int version_id)
+{
+ openpic_t *opp = (openpic_t *)opaque;
+ unsigned int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &opp->frep);
+ qemu_get_be32s(f, &opp->glbc);
+ qemu_get_be32s(f, &opp->micr);
+ qemu_get_be32s(f, &opp->veni);
+ qemu_get_be32s(f, &opp->pint);
+ qemu_get_be32s(f, &opp->spve);
+ qemu_get_be32s(f, &opp->tifr);
+
+ for (i = 0; i < opp->max_irq; i++) {
+ qemu_get_be32s(f, &opp->src[i].ipvp);
+ qemu_get_be32s(f, &opp->src[i].ide);
+ qemu_get_sbe32s(f, &opp->src[i].type);
+ qemu_get_sbe32s(f, &opp->src[i].last_cpu);
+ qemu_get_sbe32s(f, &opp->src[i].pending);
+ }
+
+ qemu_get_sbe32s(f, &opp->nb_cpus);
+
+ for (i = 0; i < opp->nb_cpus; i++) {
+ qemu_get_be32s(f, &opp->dst[i].tfrr);
+ qemu_get_be32s(f, &opp->dst[i].pctp);
+ qemu_get_be32s(f, &opp->dst[i].pcsr);
+ openpic_load_IRQ_queue(f, &opp->dst[i].raised);
+ openpic_load_IRQ_queue(f, &opp->dst[i].servicing);
+ }
+
+ for (i = 0; i < MAX_TMR; i++) {
+ qemu_get_be32s(f, &opp->timers[i].ticc);
+ qemu_get_be32s(f, &opp->timers[i].tibc);
+ }
+
+#if MAX_DBL > 0
+ qemu_get_be32s(f, &opp->dar);
+
+ for (i = 0; i < MAX_DBL; i++) {
+ qemu_get_be32s(f, &opp->doorbells[i].dmr);
+ }
+#endif
+
+#if MAX_MBX > 0
+ for (i = 0; i < MAX_MAILBOXES; i++) {
+ qemu_get_be32s(f, &opp->mailboxes[i].mbr);
+ }
+#endif
+
+ return pci_device_load(&opp->pci_dev, f);
+}
+
+static void openpic_irq_raise(openpic_t *opp, int n_CPU, IRQ_src_t *src)
+{
+ qemu_irq_raise(opp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
+}
+
+qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
+ qemu_irq **irqs, qemu_irq irq_out)
+{
+ openpic_t *opp;
+ uint8_t *pci_conf;
+ int i, m;
+
+ /* XXX: for now, only one CPU is supported */
+ if (nb_cpus != 1)
+ return NULL;
+ if (bus) {
+ opp = (openpic_t *)pci_register_device(bus, "OpenPIC", sizeof(openpic_t),
+ -1, NULL, NULL);
+ pci_conf = opp->pci_dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_IBM);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_IBM_OPENPIC2);
+ pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER); // FIXME?
+ pci_conf[0x3d] = 0x00; // no interrupt pin
+
+ /* Register I/O spaces */
+ pci_register_bar(&opp->pci_dev, 0, 0x40000,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, &openpic_map);
+ } else {
+ opp = qemu_mallocz(sizeof(openpic_t));
+ }
+ opp->mem_index = cpu_register_io_memory(openpic_read, openpic_write, opp,
+ DEVICE_LITTLE_ENDIAN);
+
+ // isu_base &= 0xFFFC0000;
+ opp->nb_cpus = nb_cpus;
+ opp->max_irq = OPENPIC_MAX_IRQ;
+ opp->irq_ipi0 = OPENPIC_IRQ_IPI0;
+ opp->irq_tim0 = OPENPIC_IRQ_TIM0;
+ /* Set IRQ types */
+ for (i = 0; i < OPENPIC_EXT_IRQ; i++) {
+ opp->src[i].type = IRQ_EXTERNAL;
+ }
+ for (; i < OPENPIC_IRQ_TIM0; i++) {
+ opp->src[i].type = IRQ_SPECIAL;
+ }
+#if MAX_IPI > 0
+ m = OPENPIC_IRQ_IPI0;
+#else
+ m = OPENPIC_IRQ_DBL0;
+#endif
+ for (; i < m; i++) {
+ opp->src[i].type = IRQ_TIMER;
+ }
+ for (; i < OPENPIC_MAX_IRQ; i++) {
+ opp->src[i].type = IRQ_INTERNAL;
+ }
+ for (i = 0; i < nb_cpus; i++)
+ opp->dst[i].irqs = irqs[i];
+ opp->irq_out = irq_out;
+
+ register_savevm(&opp->pci_dev.qdev, "openpic", 0, 2,
+ openpic_save, openpic_load, opp);
+ qemu_register_reset(openpic_reset, opp);
+
+ opp->irq_raise = openpic_irq_raise;
+ opp->reset = openpic_reset;
+
+ if (pmem_index)
+ *pmem_index = opp->mem_index;
+
+ return qemu_allocate_irqs(openpic_set_irq, opp, opp->max_irq);
+}
+
+static void mpic_irq_raise(openpic_t *mpp, int n_CPU, IRQ_src_t *src)
+{
+ int n_ci = IDR_CI0 - n_CPU;
+
+ if(test_bit(&src->ide, n_ci)) {
+ qemu_irq_raise(mpp->dst[n_CPU].irqs[OPENPIC_OUTPUT_CINT]);
+ }
+ else {
+ qemu_irq_raise(mpp->dst[n_CPU].irqs[OPENPIC_OUTPUT_INT]);
+ }
+}
+
+static void mpic_reset (void *opaque)
+{
+ openpic_t *mpp = (openpic_t *)opaque;
+ int i;
+
+ mpp->glbc = 0x80000000;
+ /* Initialise controller registers */
+ mpp->frep = 0x004f0002;
+ mpp->veni = VENI;
+ mpp->pint = 0x00000000;
+ mpp->spve = 0x0000FFFF;
+ /* Initialise IRQ sources */
+ for (i = 0; i < mpp->max_irq; i++) {
+ mpp->src[i].ipvp = 0x80800000;
+ mpp->src[i].ide = 0x00000001;
+ }
+ /* Initialise IRQ destinations */
+ for (i = 0; i < MAX_CPU; i++) {
+ mpp->dst[i].pctp = 0x0000000F;
+ mpp->dst[i].tfrr = 0x00000000;
+ memset(&mpp->dst[i].raised, 0, sizeof(IRQ_queue_t));
+ mpp->dst[i].raised.next = -1;
+ memset(&mpp->dst[i].servicing, 0, sizeof(IRQ_queue_t));
+ mpp->dst[i].servicing.next = -1;
+ }
+ /* Initialise timers */
+ for (i = 0; i < MAX_TMR; i++) {
+ mpp->timers[i].ticc = 0x00000000;
+ mpp->timers[i].tibc = 0x80000000;
+ }
+ /* Go out of RESET state */
+ mpp->glbc = 0x00000000;
+}
+
+static void mpic_timer_write (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ openpic_t *mpp = opaque;
+ int idx, cpu;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+ addr &= 0xFFFF;
+ cpu = addr >> 12;
+ idx = (addr >> 6) & 0x3;
+ switch (addr & 0x30) {
+ case 0x00: /* gtccr */
+ break;
+ case 0x10: /* gtbcr */
+ if ((mpp->timers[idx].ticc & 0x80000000) != 0 &&
+ (val & 0x80000000) == 0 &&
+ (mpp->timers[idx].tibc & 0x80000000) != 0)
+ mpp->timers[idx].ticc &= ~0x80000000;
+ mpp->timers[idx].tibc = val;
+ break;
+ case 0x20: /* GTIVPR */
+ write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP, val);
+ break;
+ case 0x30: /* GTIDR & TFRR */
+ if ((addr & 0xF0) == 0xF0)
+ mpp->dst[cpu].tfrr = val;
+ else
+ write_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE, val);
+ break;
+ }
+}
+
+static uint32_t mpic_timer_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *mpp = opaque;
+ uint32_t retval;
+ int idx, cpu;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+ addr &= 0xFFFF;
+ cpu = addr >> 12;
+ idx = (addr >> 6) & 0x3;
+ switch (addr & 0x30) {
+ case 0x00: /* gtccr */
+ retval = mpp->timers[idx].ticc;
+ break;
+ case 0x10: /* gtbcr */
+ retval = mpp->timers[idx].tibc;
+ break;
+ case 0x20: /* TIPV */
+ retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IPVP);
+ break;
+ case 0x30: /* TIDR */
+ if ((addr &0xF0) == 0XF0)
+ retval = mpp->dst[cpu].tfrr;
+ else
+ retval = read_IRQreg(mpp, MPIC_TMR_IRQ + idx, IRQ_IDE);
+ break;
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+
+ return retval;
+}
+
+static void mpic_src_ext_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ openpic_t *mpp = opaque;
+ int idx = MPIC_EXT_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+
+ addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_EXT_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ write_IRQreg(mpp, idx, IRQ_IDE, val);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ }
+ }
+}
+
+static uint32_t mpic_src_ext_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *mpp = opaque;
+ uint32_t retval;
+ int idx = MPIC_EXT_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+
+ addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_EXT_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+ }
+
+ return retval;
+}
+
+static void mpic_src_int_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ openpic_t *mpp = opaque;
+ int idx = MPIC_INT_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+
+ addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_INT_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ write_IRQreg(mpp, idx, IRQ_IDE, val);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ }
+ }
+}
+
+static uint32_t mpic_src_int_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *mpp = opaque;
+ uint32_t retval;
+ int idx = MPIC_INT_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+
+ addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_INT_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+ }
+
+ return retval;
+}
+
+static void mpic_src_msg_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ openpic_t *mpp = opaque;
+ int idx = MPIC_MSG_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+
+ addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_MSG_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ write_IRQreg(mpp, idx, IRQ_IDE, val);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ }
+ }
+}
+
+static uint32_t mpic_src_msg_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *mpp = opaque;
+ uint32_t retval;
+ int idx = MPIC_MSG_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+
+ addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_MSG_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+ }
+
+ return retval;
+}
+
+static void mpic_src_msi_write (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ openpic_t *mpp = opaque;
+ int idx = MPIC_MSI_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val);
+ if (addr & 0xF)
+ return;
+
+ addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_MSI_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ write_IRQreg(mpp, idx, IRQ_IDE, val);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ write_IRQreg(mpp, idx, IRQ_IPVP, val);
+ }
+ }
+}
+static uint32_t mpic_src_msi_read (void *opaque, target_phys_addr_t addr)
+{
+ openpic_t *mpp = opaque;
+ uint32_t retval;
+ int idx = MPIC_MSI_IRQ;
+
+ DPRINTF("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+ retval = 0xFFFFFFFF;
+ if (addr & 0xF)
+ return retval;
+
+ addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1);
+ if (addr < MPIC_MSI_REG_SIZE) {
+ idx += (addr & 0xFFF0) >> 5;
+ if (addr & 0x10) {
+ /* EXDE / IFEDE / IEEDE */
+ retval = read_IRQreg(mpp, idx, IRQ_IDE);
+ } else {
+ /* EXVP / IFEVP / IEEVP */
+ retval = read_IRQreg(mpp, idx, IRQ_IPVP);
+ }
+ DPRINTF("%s: => %08x\n", __func__, retval);
+ }
+
+ return retval;
+}
+
+static CPUWriteMemoryFunc * const mpic_glb_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &openpic_gbl_write,
+};
+
+static CPUReadMemoryFunc * const mpic_glb_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &openpic_gbl_read,
+};
+
+static CPUWriteMemoryFunc * const mpic_tmr_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &mpic_timer_write,
+};
+
+static CPUReadMemoryFunc * const mpic_tmr_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &mpic_timer_read,
+};
+
+static CPUWriteMemoryFunc * const mpic_cpu_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &openpic_cpu_write,
+};
+
+static CPUReadMemoryFunc * const mpic_cpu_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &openpic_cpu_read,
+};
+
+static CPUWriteMemoryFunc * const mpic_ext_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &mpic_src_ext_write,
+};
+
+static CPUReadMemoryFunc * const mpic_ext_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &mpic_src_ext_read,
+};
+
+static CPUWriteMemoryFunc * const mpic_int_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &mpic_src_int_write,
+};
+
+static CPUReadMemoryFunc * const mpic_int_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &mpic_src_int_read,
+};
+
+static CPUWriteMemoryFunc * const mpic_msg_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &mpic_src_msg_write,
+};
+
+static CPUReadMemoryFunc * const mpic_msg_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &mpic_src_msg_read,
+};
+static CPUWriteMemoryFunc * const mpic_msi_write[] = {
+ &openpic_buggy_write,
+ &openpic_buggy_write,
+ &mpic_src_msi_write,
+};
+
+static CPUReadMemoryFunc * const mpic_msi_read[] = {
+ &openpic_buggy_read,
+ &openpic_buggy_read,
+ &mpic_src_msi_read,
+};
+
+qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
+ qemu_irq **irqs, qemu_irq irq_out)
+{
+ openpic_t *mpp;
+ int i;
+ struct {
+ CPUReadMemoryFunc * const *read;
+ CPUWriteMemoryFunc * const *write;
+ target_phys_addr_t start_addr;
+ ram_addr_t size;
+ } const list[] = {
+ {mpic_glb_read, mpic_glb_write, MPIC_GLB_REG_START, MPIC_GLB_REG_SIZE},
+ {mpic_tmr_read, mpic_tmr_write, MPIC_TMR_REG_START, MPIC_TMR_REG_SIZE},
+ {mpic_ext_read, mpic_ext_write, MPIC_EXT_REG_START, MPIC_EXT_REG_SIZE},
+ {mpic_int_read, mpic_int_write, MPIC_INT_REG_START, MPIC_INT_REG_SIZE},
+ {mpic_msg_read, mpic_msg_write, MPIC_MSG_REG_START, MPIC_MSG_REG_SIZE},
+ {mpic_msi_read, mpic_msi_write, MPIC_MSI_REG_START, MPIC_MSI_REG_SIZE},
+ {mpic_cpu_read, mpic_cpu_write, MPIC_CPU_REG_START, MPIC_CPU_REG_SIZE},
+ };
+
+ /* XXX: for now, only one CPU is supported */
+ if (nb_cpus != 1)
+ return NULL;
+
+ mpp = qemu_mallocz(sizeof(openpic_t));
+
+ for (i = 0; i < sizeof(list)/sizeof(list[0]); i++) {
+ int mem_index;
+
+ mem_index = cpu_register_io_memory(list[i].read, list[i].write, mpp,
+ DEVICE_BIG_ENDIAN);
+ if (mem_index < 0) {
+ goto free;
+ }
+ cpu_register_physical_memory(base + list[i].start_addr,
+ list[i].size, mem_index);
+ }
+
+ mpp->nb_cpus = nb_cpus;
+ mpp->max_irq = MPIC_MAX_IRQ;
+ mpp->irq_ipi0 = MPIC_IPI_IRQ;
+ mpp->irq_tim0 = MPIC_TMR_IRQ;
+
+ for (i = 0; i < nb_cpus; i++)
+ mpp->dst[i].irqs = irqs[i];
+ mpp->irq_out = irq_out;
+
+ mpp->irq_raise = mpic_irq_raise;
+ mpp->reset = mpic_reset;
+
+ register_savevm(NULL, "mpic", 0, 2, openpic_save, openpic_load, mpp);
+ qemu_register_reset(mpic_reset, mpp);
+
+ return qemu_allocate_irqs(openpic_set_irq, mpp, mpp->max_irq);
+
+free:
+ qemu_free(mpp);
+ return NULL;
+}
diff --git a/hw/openpic.h b/hw/openpic.h
new file mode 100644
index 0000000..0957c1f
--- /dev/null
+++ b/hw/openpic.h
@@ -0,0 +1,18 @@
+#if !defined(__OPENPIC_H__)
+#define __OPENPIC_H__
+
+/* OpenPIC have 5 outputs per CPU connected and one IRQ out single output */
+enum {
+ OPENPIC_OUTPUT_INT = 0, /* IRQ */
+ OPENPIC_OUTPUT_CINT, /* critical IRQ */
+ OPENPIC_OUTPUT_MCK, /* Machine check event */
+ OPENPIC_OUTPUT_DEBUG, /* Inconditional debug event */
+ OPENPIC_OUTPUT_RESET, /* Core reset event */
+ OPENPIC_OUTPUT_NB,
+};
+
+qemu_irq *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus,
+ qemu_irq **irqs, qemu_irq irq_out);
+qemu_irq *mpic_init (target_phys_addr_t base, int nb_cpus,
+ qemu_irq **irqs, qemu_irq irq_out);
+#endif /* __OPENPIC_H__ */
diff --git a/hw/palm.c b/hw/palm.c
new file mode 100644
index 0000000..f22d777
--- /dev/null
+++ b/hw/palm.c
@@ -0,0 +1,289 @@
+/*
+ * PalmOne's (TM) PDAs.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "audio/audio.h"
+#include "sysemu.h"
+#include "console.h"
+#include "omap.h"
+#include "boards.h"
+#include "arm-misc.h"
+#include "devices.h"
+#include "loader.h"
+
+static uint32_t static_readb(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ return *val >> ((offset & 3) << 3);
+}
+
+static uint32_t static_readh(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ return *val >> ((offset & 1) << 3);
+}
+
+static uint32_t static_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t *val = (uint32_t *) opaque;
+ return *val >> ((offset & 0) << 3);
+}
+
+static void static_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+#ifdef SPY
+ printf("%s: value %08lx written at " PA_FMT "\n",
+ __FUNCTION__, value, offset);
+#endif
+}
+
+static CPUReadMemoryFunc * const static_readfn[] = {
+ static_readb,
+ static_readh,
+ static_readw,
+};
+
+static CPUWriteMemoryFunc * const static_writefn[] = {
+ static_write,
+ static_write,
+ static_write,
+};
+
+/* Palm Tunsgten|E support */
+
+/* Shared GPIOs */
+#define PALMTE_USBDETECT_GPIO 0
+#define PALMTE_USB_OR_DC_GPIO 1
+#define PALMTE_TSC_GPIO 4
+#define PALMTE_PINTDAV_GPIO 6
+#define PALMTE_MMC_WP_GPIO 8
+#define PALMTE_MMC_POWER_GPIO 9
+#define PALMTE_HDQ_GPIO 11
+#define PALMTE_HEADPHONES_GPIO 14
+#define PALMTE_SPEAKER_GPIO 15
+/* MPU private GPIOs */
+#define PALMTE_DC_GPIO 2
+#define PALMTE_MMC_SWITCH_GPIO 4
+#define PALMTE_MMC1_GPIO 6
+#define PALMTE_MMC2_GPIO 7
+#define PALMTE_MMC3_GPIO 11
+
+static MouseTransformInfo palmte_pointercal = {
+ .x = 320,
+ .y = 320,
+ .a = { -5909, 8, 22465308, 104, 7644, -1219972, 65536 },
+};
+
+static void palmte_microwire_setup(struct omap_mpu_state_s *cpu)
+{
+ uWireSlave *tsc;
+
+ tsc = tsc2102_init(omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO]);
+
+ omap_uwire_attach(cpu->microwire, tsc, 0);
+ omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc));
+
+ tsc210x_set_transform(tsc, &palmte_pointercal);
+}
+
+static struct {
+ int row;
+ int column;
+} palmte_keymap[0x80] = {
+ [0 ... 0x7f] = { -1, -1 },
+ [0x3b] = { 0, 0 }, /* F1 -> Calendar */
+ [0x3c] = { 1, 0 }, /* F2 -> Contacts */
+ [0x3d] = { 2, 0 }, /* F3 -> Tasks List */
+ [0x3e] = { 3, 0 }, /* F4 -> Note Pad */
+ [0x01] = { 4, 0 }, /* Esc -> Power */
+ [0x4b] = { 0, 1 }, /* Left */
+ [0x50] = { 1, 1 }, /* Down */
+ [0x48] = { 2, 1 }, /* Up */
+ [0x4d] = { 3, 1 }, /* Right */
+ [0x4c] = { 4, 1 }, /* Centre */
+ [0x39] = { 4, 1 }, /* Spc -> Centre */
+};
+
+static void palmte_button_event(void *opaque, int keycode)
+{
+ struct omap_mpu_state_s *cpu = (struct omap_mpu_state_s *) opaque;
+
+ if (palmte_keymap[keycode & 0x7f].row != -1)
+ omap_mpuio_key(cpu->mpuio,
+ palmte_keymap[keycode & 0x7f].row,
+ palmte_keymap[keycode & 0x7f].column,
+ !(keycode & 0x80));
+}
+
+static void palmte_onoff_gpios(void *opaque, int line, int level)
+{
+ switch (line) {
+ case 0:
+ printf("%s: current to MMC/SD card %sabled.\n",
+ __FUNCTION__, level ? "dis" : "en");
+ break;
+ case 1:
+ printf("%s: internal speaker amplifier %s.\n",
+ __FUNCTION__, level ? "down" : "on");
+ break;
+
+ /* These LCD & Audio output signals have not been identified yet. */
+ case 2:
+ case 3:
+ case 4:
+ printf("%s: LCD GPIO%i %s.\n",
+ __FUNCTION__, line - 1, level ? "high" : "low");
+ break;
+ case 5:
+ case 6:
+ printf("%s: Audio GPIO%i %s.\n",
+ __FUNCTION__, line - 4, level ? "high" : "low");
+ break;
+ }
+}
+
+static void palmte_gpio_setup(struct omap_mpu_state_s *cpu)
+{
+ qemu_irq *misc_gpio;
+
+ omap_mmc_handlers(cpu->mmc,
+ omap_gpio_in_get(cpu->gpio)[PALMTE_MMC_WP_GPIO],
+ qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio)
+ [PALMTE_MMC_SWITCH_GPIO]));
+
+ misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7);
+ omap_gpio_out_set(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]);
+ omap_gpio_out_set(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]);
+ omap_gpio_out_set(cpu->gpio, 11, misc_gpio[2]);
+ omap_gpio_out_set(cpu->gpio, 12, misc_gpio[3]);
+ omap_gpio_out_set(cpu->gpio, 13, misc_gpio[4]);
+ omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]);
+ omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]);
+
+ /* Reset some inputs to initial state. */
+ qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USBDETECT_GPIO]);
+ qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USB_OR_DC_GPIO]);
+ qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[4]);
+ qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_HEADPHONES_GPIO]);
+ qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]);
+ qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]);
+ qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]);
+ qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[11]);
+}
+
+static struct arm_boot_info palmte_binfo = {
+ .loader_start = OMAP_EMIFF_BASE,
+ .ram_size = 0x02000000,
+ .board_id = 0x331,
+};
+
+static void palmte_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ struct omap_mpu_state_s *cpu;
+ int flash_size = 0x00800000;
+ int sdram_size = palmte_binfo.ram_size;
+ int io;
+ static uint32_t cs0val = 0xffffffff;
+ static uint32_t cs1val = 0x0000e1a0;
+ static uint32_t cs2val = 0x0000e1a0;
+ static uint32_t cs3val = 0xe1a0e1a0;
+ int rom_size, rom_loaded = 0;
+ DisplayState *ds = get_displaystate();
+
+ cpu = omap310_mpu_init(sdram_size, cpu_model);
+
+ /* External Flash (EMIFS) */
+ cpu_register_physical_memory(OMAP_CS0_BASE, flash_size,
+ qemu_ram_alloc(NULL, "palmte.flash",
+ flash_size) | IO_MEM_ROM);
+
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs0val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS0_BASE + flash_size,
+ OMAP_CS0_SIZE - flash_size, io);
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS1_BASE, OMAP_CS1_SIZE, io);
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs2val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS2_BASE, OMAP_CS2_SIZE, io);
+ io = cpu_register_io_memory(static_readfn, static_writefn, &cs3val,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(OMAP_CS3_BASE, OMAP_CS3_SIZE, io);
+
+ palmte_microwire_setup(cpu);
+
+ qemu_add_kbd_event_handler(palmte_button_event, cpu);
+
+ palmte_gpio_setup(cpu);
+
+ /* Setup initial (reset) machine state */
+ if (nb_option_roms) {
+ rom_size = get_image_size(option_rom[0].name);
+ if (rom_size > flash_size) {
+ fprintf(stderr, "%s: ROM image too big (%x > %x)\n",
+ __FUNCTION__, rom_size, flash_size);
+ rom_size = 0;
+ }
+ if (rom_size > 0) {
+ rom_size = load_image_targphys(option_rom[0].name, OMAP_CS0_BASE,
+ flash_size);
+ rom_loaded = 1;
+ }
+ if (rom_size < 0) {
+ fprintf(stderr, "%s: error loading '%s'\n",
+ __FUNCTION__, option_rom[0].name);
+ }
+ }
+
+ if (!rom_loaded && !kernel_filename) {
+ fprintf(stderr, "Kernel or ROM image must be specified\n");
+ exit(1);
+ }
+
+ /* Load the kernel. */
+ if (kernel_filename) {
+ palmte_binfo.kernel_filename = kernel_filename;
+ palmte_binfo.kernel_cmdline = kernel_cmdline;
+ palmte_binfo.initrd_filename = initrd_filename;
+ arm_load_kernel(cpu->env, &palmte_binfo);
+ }
+
+ /* FIXME: We shouldn't really be doing this here. The LCD controller
+ will set the size once configured, so this just sets an initial
+ size until the guest activates the display. */
+ ds->surface = qemu_resize_displaysurface(ds, 320, 320);
+ dpy_resize(ds);
+}
+
+static QEMUMachine palmte_machine = {
+ .name = "cheetah",
+ .desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)",
+ .init = palmte_init,
+};
+
+static void palmte_machine_init(void)
+{
+ qemu_register_machine(&palmte_machine);
+}
+
+machine_init(palmte_machine_init);
diff --git a/hw/parallel.c b/hw/parallel.c
new file mode 100644
index 0000000..ce311aa
--- /dev/null
+++ b/hw/parallel.c
@@ -0,0 +1,609 @@
+/*
+ * QEMU Parallel PORT emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2007 Marko Kohtala
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-char.h"
+#include "isa.h"
+#include "pc.h"
+#include "sysemu.h"
+
+//#define DEBUG_PARALLEL
+
+#ifdef DEBUG_PARALLEL
+#define pdebug(fmt, ...) printf("pp: " fmt, ## __VA_ARGS__)
+#else
+#define pdebug(fmt, ...) ((void)0)
+#endif
+
+#define PARA_REG_DATA 0
+#define PARA_REG_STS 1
+#define PARA_REG_CTR 2
+#define PARA_REG_EPP_ADDR 3
+#define PARA_REG_EPP_DATA 4
+
+/*
+ * These are the definitions for the Printer Status Register
+ */
+#define PARA_STS_BUSY 0x80 /* Busy complement */
+#define PARA_STS_ACK 0x40 /* Acknowledge */
+#define PARA_STS_PAPER 0x20 /* Out of paper */
+#define PARA_STS_ONLINE 0x10 /* Online */
+#define PARA_STS_ERROR 0x08 /* Error complement */
+#define PARA_STS_TMOUT 0x01 /* EPP timeout */
+
+/*
+ * These are the definitions for the Printer Control Register
+ */
+#define PARA_CTR_DIR 0x20 /* Direction (1=read, 0=write) */
+#define PARA_CTR_INTEN 0x10 /* IRQ Enable */
+#define PARA_CTR_SELECT 0x08 /* Select In complement */
+#define PARA_CTR_INIT 0x04 /* Initialize Printer complement */
+#define PARA_CTR_AUTOLF 0x02 /* Auto linefeed complement */
+#define PARA_CTR_STROBE 0x01 /* Strobe complement */
+
+#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE)
+
+struct ParallelState {
+ uint8_t dataw;
+ uint8_t datar;
+ uint8_t status;
+ uint8_t control;
+ qemu_irq irq;
+ int irq_pending;
+ CharDriverState *chr;
+ int hw_driver;
+ int epp_timeout;
+ uint32_t last_read_offset; /* For debugging */
+ /* Memory-mapped interface */
+ int it_shift;
+};
+
+typedef struct ISAParallelState {
+ ISADevice dev;
+ uint32_t index;
+ uint32_t iobase;
+ uint32_t isairq;
+ ParallelState state;
+} ISAParallelState;
+
+static void parallel_update_irq(ParallelState *s)
+{
+ if (s->irq_pending)
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void
+parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+
+ pdebug("write addr=0x%02x val=0x%02x\n", addr, val);
+
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ s->dataw = val;
+ parallel_update_irq(s);
+ break;
+ case PARA_REG_CTR:
+ val |= 0xc0;
+ if ((val & PARA_CTR_INIT) == 0 ) {
+ s->status = PARA_STS_BUSY;
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_ONLINE;
+ s->status |= PARA_STS_ERROR;
+ }
+ else if (val & PARA_CTR_SELECT) {
+ if (val & PARA_CTR_STROBE) {
+ s->status &= ~PARA_STS_BUSY;
+ if ((s->control & PARA_CTR_STROBE) == 0)
+ qemu_chr_write(s->chr, &s->dataw, 1);
+ } else {
+ if (s->control & PARA_CTR_INTEN) {
+ s->irq_pending = 1;
+ }
+ }
+ }
+ parallel_update_irq(s);
+ s->control = val;
+ break;
+ }
+}
+
+static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+ uint8_t parm = val;
+ int dir;
+
+ /* Sometimes programs do several writes for timing purposes on old
+ HW. Take care not to waste time on writes that do nothing. */
+
+ s->last_read_offset = ~0U;
+
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ if (s->dataw == val)
+ return;
+ pdebug("wd%02x\n", val);
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
+ s->dataw = val;
+ break;
+ case PARA_REG_STS:
+ pdebug("ws%02x\n", val);
+ if (val & PARA_STS_TMOUT)
+ s->epp_timeout = 0;
+ break;
+ case PARA_REG_CTR:
+ val |= 0xc0;
+ if (s->control == val)
+ return;
+ pdebug("wc%02x\n", val);
+
+ if ((val & PARA_CTR_DIR) != (s->control & PARA_CTR_DIR)) {
+ if (val & PARA_CTR_DIR) {
+ dir = 1;
+ } else {
+ dir = 0;
+ }
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_DATA_DIR, &dir);
+ parm &= ~PARA_CTR_DIR;
+ }
+
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
+ s->control = val;
+ break;
+ case PARA_REG_EPP_ADDR:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+ /* Controls not correct for EPP address cycle, so do nothing */
+ pdebug("wa%02x s\n", val);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+ if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("wa%02x t\n", val);
+ }
+ else
+ pdebug("wa%02x\n", val);
+ }
+ break;
+ case PARA_REG_EPP_DATA:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("we%02x s\n", val);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+ if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("we%02x t\n", val);
+ }
+ else
+ pdebug("we%02x\n", val);
+ }
+ break;
+ }
+}
+
+static void
+parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+ uint16_t eppdata = cpu_to_le16(val);
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("we%04x s\n", val);
+ return;
+ }
+ err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("we%04x t\n", val);
+ }
+ else
+ pdebug("we%04x\n", val);
+}
+
+static void
+parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
+{
+ ParallelState *s = opaque;
+ uint32_t eppdata = cpu_to_le32(val);
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("we%08x s\n", val);
+ return;
+ }
+ err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("we%08x t\n", val);
+ }
+ else
+ pdebug("we%08x\n", val);
+}
+
+static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret = 0xff;
+
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ if (s->control & PARA_CTR_DIR)
+ ret = s->datar;
+ else
+ ret = s->dataw;
+ break;
+ case PARA_REG_STS:
+ ret = s->status;
+ s->irq_pending = 0;
+ if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
+ /* XXX Fixme: wait 5 microseconds */
+ if (s->status & PARA_STS_ACK)
+ s->status &= ~PARA_STS_ACK;
+ else {
+ /* XXX Fixme: wait 5 microseconds */
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_BUSY;
+ }
+ }
+ parallel_update_irq(s);
+ break;
+ case PARA_REG_CTR:
+ ret = s->control;
+ break;
+ }
+ pdebug("read addr=0x%02x val=0x%02x\n", addr, ret);
+ return ret;
+}
+
+static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint8_t ret = 0xff;
+ addr &= 7;
+ switch(addr) {
+ case PARA_REG_DATA:
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret);
+ if (s->last_read_offset != addr || s->datar != ret)
+ pdebug("rd%02x\n", ret);
+ s->datar = ret;
+ break;
+ case PARA_REG_STS:
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret);
+ ret &= ~PARA_STS_TMOUT;
+ if (s->epp_timeout)
+ ret |= PARA_STS_TMOUT;
+ if (s->last_read_offset != addr || s->status != ret)
+ pdebug("rs%02x\n", ret);
+ s->status = ret;
+ break;
+ case PARA_REG_CTR:
+ /* s->control has some bits fixed to 1. It is zero only when
+ it has not been yet written to. */
+ if (s->control == 0) {
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
+ if (s->last_read_offset != addr)
+ pdebug("rc%02x\n", ret);
+ s->control = ret;
+ }
+ else {
+ ret = s->control;
+ if (s->last_read_offset != addr)
+ pdebug("rc%02x\n", ret);
+ }
+ break;
+ case PARA_REG_EPP_ADDR:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+ /* Controls not correct for EPP addr cycle, so do nothing */
+ pdebug("ra%02x s\n", ret);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+ if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("ra%02x t\n", ret);
+ }
+ else
+ pdebug("ra%02x\n", ret);
+ }
+ break;
+ case PARA_REG_EPP_DATA:
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("re%02x s\n", ret);
+ else {
+ struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+ if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
+ s->epp_timeout = 1;
+ pdebug("re%02x t\n", ret);
+ }
+ else
+ pdebug("re%02x\n", ret);
+ }
+ break;
+ }
+ s->last_read_offset = addr;
+ return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret;
+ uint16_t eppdata = ~0;
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("re%04x s\n", eppdata);
+ return eppdata;
+ }
+ err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+ ret = le16_to_cpu(eppdata);
+
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("re%04x t\n", ret);
+ }
+ else
+ pdebug("re%04x\n", ret);
+ return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr)
+{
+ ParallelState *s = opaque;
+ uint32_t ret;
+ uint32_t eppdata = ~0U;
+ int err;
+ struct ParallelIOArg ioarg = {
+ .buffer = &eppdata, .count = sizeof(eppdata)
+ };
+ if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+ /* Controls not correct for EPP data cycle, so do nothing */
+ pdebug("re%08x s\n", eppdata);
+ return eppdata;
+ }
+ err = qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+ ret = le32_to_cpu(eppdata);
+
+ if (err) {
+ s->epp_timeout = 1;
+ pdebug("re%08x t\n", ret);
+ }
+ else
+ pdebug("re%08x\n", ret);
+ return ret;
+}
+
+static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ pdebug("wecp%d=%02x\n", addr & 7, val);
+}
+
+static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr)
+{
+ uint8_t ret = 0xff;
+
+ pdebug("recp%d:%02x\n", addr & 7, ret);
+ return ret;
+}
+
+static void parallel_reset(void *opaque)
+{
+ ParallelState *s = opaque;
+
+ s->datar = ~0;
+ s->dataw = ~0;
+ s->status = PARA_STS_BUSY;
+ s->status |= PARA_STS_ACK;
+ s->status |= PARA_STS_ONLINE;
+ s->status |= PARA_STS_ERROR;
+ s->status |= PARA_STS_TMOUT;
+ s->control = PARA_CTR_SELECT;
+ s->control |= PARA_CTR_INIT;
+ s->control |= 0xc0;
+ s->irq_pending = 0;
+ s->hw_driver = 0;
+ s->epp_timeout = 0;
+ s->last_read_offset = ~0U;
+}
+
+static const int isa_parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+
+static int parallel_isa_initfn(ISADevice *dev)
+{
+ static int index;
+ ISAParallelState *isa = DO_UPCAST(ISAParallelState, dev, dev);
+ ParallelState *s = &isa->state;
+ int base;
+ uint8_t dummy;
+
+ if (!s->chr) {
+ fprintf(stderr, "Can't create parallel device, empty char device\n");
+ exit(1);
+ }
+
+ if (isa->index == -1)
+ isa->index = index;
+ if (isa->index >= MAX_PARALLEL_PORTS)
+ return -1;
+ if (isa->iobase == -1)
+ isa->iobase = isa_parallel_io[isa->index];
+ index++;
+
+ base = isa->iobase;
+ isa_init_irq(dev, &s->irq, isa->isairq);
+ qemu_register_reset(parallel_reset, s);
+
+ if (qemu_chr_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
+ s->hw_driver = 1;
+ s->status = dummy;
+ }
+
+ if (s->hw_driver) {
+ register_ioport_write(base, 8, 1, parallel_ioport_write_hw, s);
+ register_ioport_read(base, 8, 1, parallel_ioport_read_hw, s);
+ isa_init_ioport_range(dev, base, 8);
+
+ register_ioport_write(base+4, 1, 2, parallel_ioport_eppdata_write_hw2, s);
+ register_ioport_read(base+4, 1, 2, parallel_ioport_eppdata_read_hw2, s);
+ register_ioport_write(base+4, 1, 4, parallel_ioport_eppdata_write_hw4, s);
+ register_ioport_read(base+4, 1, 4, parallel_ioport_eppdata_read_hw4, s);
+ isa_init_ioport(dev, base+4);
+ register_ioport_write(base+0x400, 8, 1, parallel_ioport_ecp_write, s);
+ register_ioport_read(base+0x400, 8, 1, parallel_ioport_ecp_read, s);
+ isa_init_ioport_range(dev, base+0x400, 8);
+ }
+ else {
+ register_ioport_write(base, 8, 1, parallel_ioport_write_sw, s);
+ register_ioport_read(base, 8, 1, parallel_ioport_read_sw, s);
+ isa_init_ioport_range(dev, base, 8);
+ }
+ return 0;
+}
+
+ParallelState *parallel_init(int index, CharDriverState *chr)
+{
+ ISADevice *dev;
+
+ dev = isa_create("isa-parallel");
+ qdev_prop_set_uint32(&dev->qdev, "index", index);
+ qdev_prop_set_chr(&dev->qdev, "chardev", chr);
+ if (qdev_init(&dev->qdev) < 0)
+ return NULL;
+ return &DO_UPCAST(ISAParallelState, dev, dev)->state;
+}
+
+/* Memory mapped interface */
+static uint32_t parallel_mm_readb (void *opaque, target_phys_addr_t addr)
+{
+ ParallelState *s = opaque;
+
+ return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF;
+}
+
+static void parallel_mm_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ParallelState *s = opaque;
+
+ parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF);
+}
+
+static uint32_t parallel_mm_readw (void *opaque, target_phys_addr_t addr)
+{
+ ParallelState *s = opaque;
+
+ return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF;
+}
+
+static void parallel_mm_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ParallelState *s = opaque;
+
+ parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t parallel_mm_readl (void *opaque, target_phys_addr_t addr)
+{
+ ParallelState *s = opaque;
+
+ return parallel_ioport_read_sw(s, addr >> s->it_shift);
+}
+
+static void parallel_mm_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ParallelState *s = opaque;
+
+ parallel_ioport_write_sw(s, addr >> s->it_shift, value);
+}
+
+static CPUReadMemoryFunc * const parallel_mm_read_sw[] = {
+ &parallel_mm_readb,
+ &parallel_mm_readw,
+ &parallel_mm_readl,
+};
+
+static CPUWriteMemoryFunc * const parallel_mm_write_sw[] = {
+ &parallel_mm_writeb,
+ &parallel_mm_writew,
+ &parallel_mm_writel,
+};
+
+/* If fd is zero, it means that the parallel device uses the console */
+ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr)
+{
+ ParallelState *s;
+ int io_sw;
+
+ s = qemu_mallocz(sizeof(ParallelState));
+ s->irq = irq;
+ s->chr = chr;
+ s->it_shift = it_shift;
+ qemu_register_reset(parallel_reset, s);
+
+ io_sw = cpu_register_io_memory(parallel_mm_read_sw, parallel_mm_write_sw,
+ s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 8 << it_shift, io_sw);
+ return s;
+}
+
+static ISADeviceInfo parallel_isa_info = {
+ .qdev.name = "isa-parallel",
+ .qdev.size = sizeof(ISAParallelState),
+ .init = parallel_isa_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("index", ISAParallelState, index, -1),
+ DEFINE_PROP_HEX32("iobase", ISAParallelState, iobase, -1),
+ DEFINE_PROP_UINT32("irq", ISAParallelState, isairq, 7),
+ DEFINE_PROP_CHR("chardev", ISAParallelState, state.chr),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void parallel_register_devices(void)
+{
+ isa_qdev_register(&parallel_isa_info);
+}
+
+device_init(parallel_register_devices)
diff --git a/hw/pc.c b/hw/pc.c
new file mode 100644
index 0000000..b7bf319
--- /dev/null
+++ b/hw/pc.c
@@ -0,0 +1,1186 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "apic.h"
+#include "fdc.h"
+#include "ide.h"
+#include "pci.h"
+#include "vmware_vga.h"
+#include "monitor.h"
+#include "fw_cfg.h"
+#include "hpet_emul.h"
+#include "smbios.h"
+#include "loader.h"
+#include "elf.h"
+#include "multiboot.h"
+#include "mc146818rtc.h"
+#include "msix.h"
+#include "sysbus.h"
+#include "sysemu.h"
+#include "device-assignment.h"
+#include "kvm.h"
+#include "blockdev.h"
+#include "ui/qemu-spice.h"
+
+/* output Bochs bios info messages */
+//#define DEBUG_BIOS
+
+/* debug PC/ISA interrupts */
+//#define DEBUG_IRQ
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, ...) \
+ do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define BIOS_FILENAME "bios.bin"
+#define EXTBOOT_FILENAME "extboot.bin"
+#define VAPIC_FILENAME "vapic.bin"
+
+#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
+
+/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */
+#define ACPI_DATA_SIZE 0x10000
+#define BIOS_CFG_IOPORT 0x510
+#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
+#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
+#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
+#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3)
+#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4)
+
+#define MSI_ADDR_BASE 0xfee00000
+
+#define E820_NR_ENTRIES 16
+
+struct e820_entry {
+ uint64_t address;
+ uint64_t length;
+ uint32_t type;
+} __attribute((__packed__, __aligned__(4)));
+
+struct e820_table {
+ uint32_t count;
+ struct e820_entry entry[E820_NR_ENTRIES];
+} __attribute((__packed__, __aligned__(4)));
+
+static struct e820_table e820_table;
+
+void isa_irq_handler(void *opaque, int n, int level)
+{
+ IsaIrqState *isa = (IsaIrqState *)opaque;
+
+ DPRINTF("isa_irqs: %s irq %d\n", level? "raise" : "lower", n);
+ if (n < 16) {
+ qemu_set_irq(isa->i8259[n], level);
+ }
+ if (isa->ioapic)
+ qemu_set_irq(isa->ioapic[n], level);
+};
+
+static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
+{
+}
+
+/* MSDOS compatibility mode FPU exception support */
+static qemu_irq ferr_irq;
+
+void pc_register_ferr_irq(qemu_irq irq)
+{
+ ferr_irq = irq;
+}
+
+/* XXX: add IGNNE support */
+void cpu_set_ferr(CPUX86State *s)
+{
+ qemu_irq_raise(ferr_irq);
+}
+
+static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ qemu_irq_lower(ferr_irq);
+}
+
+/* TSC handling */
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ return cpu_get_ticks();
+}
+
+/* SMM support */
+
+static cpu_set_smm_t smm_set;
+static void *smm_arg;
+
+void cpu_smm_register(cpu_set_smm_t callback, void *arg)
+{
+ assert(smm_set == NULL);
+ assert(smm_arg == NULL);
+ smm_set = callback;
+ smm_arg = arg;
+}
+
+void cpu_smm_update(CPUState *env)
+{
+ if (smm_set && smm_arg && env == first_cpu)
+ smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg);
+}
+
+
+/* IRQ handling */
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ int intno;
+
+ intno = apic_get_interrupt(env->apic_state);
+ if (intno >= 0) {
+ /* set irq request if a PIC irq is still pending */
+ /* XXX: improve that */
+ pic_update_irq(isa_pic);
+ return intno;
+ }
+ /* read the irq from the PIC */
+ if (!apic_accept_pic_intr(env->apic_state)) {
+ return -1;
+ }
+
+ intno = pic_read_irq(isa_pic);
+ return intno;
+}
+
+static void pic_irq_request(void *opaque, int irq, int level)
+{
+ CPUState *env = first_cpu;
+
+ DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
+ if (env->apic_state) {
+ while (env) {
+ if (apic_accept_pic_intr(env->apic_state)) {
+ apic_deliver_pic_intr(env->apic_state, level);
+ }
+ env = env->next_cpu;
+ }
+ } else {
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+}
+
+/* PC cmos mappings */
+
+#define REG_EQUIPMENT_BYTE 0x14
+
+static int cmos_get_fd_drive_type(int fd0)
+{
+ int val;
+
+ switch (fd0) {
+ case 0:
+ /* 1.44 Mb 3"5 drive */
+ val = 4;
+ break;
+ case 1:
+ /* 2.88 Mb 3"5 drive */
+ val = 5;
+ break;
+ case 2:
+ /* 1.2 Mb 5"5 drive */
+ val = 2;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ return val;
+}
+
+static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd,
+ ISADevice *s)
+{
+ int cylinders, heads, sectors;
+ bdrv_get_geometry_hint(hd, &cylinders, &heads, &sectors);
+ rtc_set_memory(s, type_ofs, 47);
+ rtc_set_memory(s, info_ofs, cylinders);
+ rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
+ rtc_set_memory(s, info_ofs + 2, heads);
+ rtc_set_memory(s, info_ofs + 3, 0xff);
+ rtc_set_memory(s, info_ofs + 4, 0xff);
+ rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
+ rtc_set_memory(s, info_ofs + 6, cylinders);
+ rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
+ rtc_set_memory(s, info_ofs + 8, sectors);
+}
+
+/* convert boot_device letter to something recognizable by the bios */
+static int boot_device2nibble(char boot_device)
+{
+ switch(boot_device) {
+ case 'a':
+ case 'b':
+ return 0x01; /* floppy boot */
+ case 'c':
+ return 0x02; /* hard drive boot */
+ case 'd':
+ return 0x03; /* CD-ROM boot */
+ case 'n':
+ return 0x04; /* Network boot */
+ }
+ return 0;
+}
+
+static int set_boot_dev(ISADevice *s, const char *boot_device, int fd_bootchk)
+{
+#define PC_MAX_BOOT_DEVICES 3
+ int nbds, bds[3] = { 0, };
+ int i;
+
+ nbds = strlen(boot_device);
+ if (nbds > PC_MAX_BOOT_DEVICES) {
+ error_report("Too many boot devices for PC");
+ return(1);
+ }
+ for (i = 0; i < nbds; i++) {
+ bds[i] = boot_device2nibble(boot_device[i]);
+ if (bds[i] == 0) {
+ error_report("Invalid boot device for PC: '%c'",
+ boot_device[i]);
+ return(1);
+ }
+ }
+ rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
+ rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1));
+ return(0);
+}
+
+static int pc_boot_set(void *opaque, const char *boot_device)
+{
+ return set_boot_dev(opaque, boot_device, 0);
+}
+
+typedef struct pc_cmos_init_late_arg {
+ ISADevice *rtc_state;
+ BusState *idebus0, *idebus1;
+} pc_cmos_init_late_arg;
+
+static void pc_cmos_init_late(void *opaque)
+{
+ pc_cmos_init_late_arg *arg = opaque;
+ ISADevice *s = arg->rtc_state;
+ int val;
+ BlockDriverState *hd_table[4];
+ int i;
+
+ ide_get_bs(hd_table, arg->idebus0);
+ ide_get_bs(hd_table + 2, arg->idebus1);
+
+ rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
+ if (hd_table[0])
+ cmos_init_hd(0x19, 0x1b, hd_table[0], s);
+ if (hd_table[1])
+ cmos_init_hd(0x1a, 0x24, hd_table[1], s);
+
+ val = 0;
+ for (i = 0; i < 4; i++) {
+ if (hd_table[i]) {
+ int cylinders, heads, sectors, translation;
+ /* NOTE: bdrv_get_geometry_hint() returns the physical
+ geometry. It is always such that: 1 <= sects <= 63, 1
+ <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+ geometry can be different if a translation is done. */
+ translation = bdrv_get_translation_hint(hd_table[i]);
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, &sectors);
+ if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
+ /* No translation. */
+ translation = 0;
+ } else {
+ /* LBA translation. */
+ translation = 1;
+ }
+ } else {
+ translation--;
+ }
+ val |= translation << (i * 2);
+ }
+ }
+ rtc_set_memory(s, 0x39, val);
+
+ qemu_unregister_reset(pc_cmos_init_late, opaque);
+}
+
+void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device,
+ BusState *idebus0, BusState *idebus1,
+ FDCtrl *floppy_controller, ISADevice *s)
+{
+ int val;
+ int fd0, fd1, nb;
+ static pc_cmos_init_late_arg arg;
+
+ /* various important CMOS locations needed by PC/Bochs bios */
+
+ /* memory size */
+ val = 640; /* base memory in K */
+ rtc_set_memory(s, 0x15, val);
+ rtc_set_memory(s, 0x16, val >> 8);
+
+ val = (ram_size / 1024) - 1024;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x17, val);
+ rtc_set_memory(s, 0x18, val >> 8);
+ rtc_set_memory(s, 0x30, val);
+ rtc_set_memory(s, 0x31, val >> 8);
+
+ if (above_4g_mem_size) {
+ rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16);
+ rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
+ rtc_set_memory(s, 0x5d, (uint64_t)above_4g_mem_size >> 32);
+ }
+
+ if (ram_size > (16 * 1024 * 1024))
+ val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
+ else
+ val = 0;
+ if (val > 65535)
+ val = 65535;
+ rtc_set_memory(s, 0x34, val);
+ rtc_set_memory(s, 0x35, val >> 8);
+
+ /* set the number of CPU */
+ rtc_set_memory(s, 0x5f, smp_cpus - 1);
+
+ /* set boot devices, and disable floppy signature check if requested */
+ if (set_boot_dev(s, boot_device, fd_bootchk)) {
+ exit(1);
+ }
+
+ /* floppy type */
+
+ fd0 = fdctrl_get_drive_type(floppy_controller, 0);
+ fd1 = fdctrl_get_drive_type(floppy_controller, 1);
+
+ val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
+ rtc_set_memory(s, 0x10, val);
+
+ val = 0;
+ nb = 0;
+ if (fd0 < 3)
+ nb++;
+ if (fd1 < 3)
+ nb++;
+ switch (nb) {
+ case 0:
+ break;
+ case 1:
+ val |= 0x01; /* 1 drive, ready for boot */
+ break;
+ case 2:
+ val |= 0x41; /* 2 drives, ready for boot */
+ break;
+ }
+ val |= 0x02; /* FPU is there */
+ val |= 0x04; /* PS/2 mouse installed */
+ rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
+
+ /* hard drives */
+ arg.rtc_state = s;
+ arg.idebus0 = idebus0;
+ arg.idebus1 = idebus1;
+ qemu_register_reset(pc_cmos_init_late, &arg);
+}
+
+/* port 92 stuff: could be split off */
+typedef struct Port92State {
+ ISADevice dev;
+ uint8_t outport;
+ qemu_irq *a20_out;
+} Port92State;
+
+static void port92_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ Port92State *s = opaque;
+
+ DPRINTF("port92: write 0x%02x\n", val);
+ s->outport = val;
+ qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+ if (val & 1) {
+ qemu_system_reset_request();
+ }
+}
+
+static uint32_t port92_read(void *opaque, uint32_t addr)
+{
+ Port92State *s = opaque;
+ uint32_t ret;
+
+ ret = s->outport;
+ DPRINTF("port92: read 0x%02x\n", ret);
+ return ret;
+}
+
+static void port92_init(ISADevice *dev, qemu_irq *a20_out)
+{
+ Port92State *s = DO_UPCAST(Port92State, dev, dev);
+
+ s->a20_out = a20_out;
+}
+
+static const VMStateDescription vmstate_port92_isa = {
+ .name = "port92",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(outport, Port92State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void port92_reset(DeviceState *d)
+{
+ Port92State *s = container_of(d, Port92State, dev.qdev);
+
+ s->outport &= ~1;
+}
+
+static int port92_initfn(ISADevice *dev)
+{
+ Port92State *s = DO_UPCAST(Port92State, dev, dev);
+
+ register_ioport_read(0x92, 1, 1, port92_read, s);
+ register_ioport_write(0x92, 1, 1, port92_write, s);
+ isa_init_ioport(dev, 0x92);
+ s->outport = 0;
+ return 0;
+}
+
+static ISADeviceInfo port92_info = {
+ .qdev.name = "port92",
+ .qdev.size = sizeof(Port92State),
+ .qdev.vmsd = &vmstate_port92_isa,
+ .qdev.no_user = 1,
+ .qdev.reset = port92_reset,
+ .init = port92_initfn,
+};
+
+static void port92_register(void)
+{
+ isa_qdev_register(&port92_info);
+}
+device_init(port92_register)
+
+static void handle_a20_line_change(void *opaque, int irq, int level)
+{
+ CPUState *cpu = opaque;
+
+ /* XXX: send to all CPUs ? */
+ /* XXX: add logic to handle multiple A20 line sources */
+ cpu_x86_set_a20(cpu, level);
+}
+
+/***********************************************************/
+/* Bochs BIOS debug ports */
+
+static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ static const char shutdown_str[8] = "Shutdown";
+ static int shutdown_index = 0;
+
+ switch(addr) {
+ /* Bochs BIOS messages */
+ case 0x400:
+ case 0x401:
+ /* used to be panic, now unused */
+ break;
+ case 0x402:
+ case 0x403:
+#ifdef DEBUG_BIOS
+ fprintf(stderr, "%c", val);
+#endif
+ break;
+ case 0x8900:
+ /* same as Bochs power off */
+ if (val == shutdown_str[shutdown_index]) {
+ shutdown_index++;
+ if (shutdown_index == 8) {
+ shutdown_index = 0;
+ qemu_system_shutdown_request();
+ }
+ } else {
+ shutdown_index = 0;
+ }
+ break;
+
+ /* LGPL'ed VGA BIOS messages */
+ case 0x501:
+ case 0x502:
+ fprintf(stderr, "VGA BIOS panic, line %d\n", val);
+ exit(1);
+ case 0x500:
+ case 0x503:
+#ifdef DEBUG_BIOS
+ fprintf(stderr, "%c", val);
+#endif
+ break;
+ }
+}
+
+int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
+{
+ int index = le32_to_cpu(e820_table.count);
+ struct e820_entry *entry;
+
+ if (index >= E820_NR_ENTRIES)
+ return -EBUSY;
+ entry = &e820_table.entry[index++];
+
+ entry->address = cpu_to_le64(address);
+ entry->length = cpu_to_le64(length);
+ entry->type = cpu_to_le32(type);
+
+ e820_table.count = cpu_to_le32(index);
+ return index;
+}
+
+static void *bochs_bios_init(void)
+{
+ void *fw_cfg;
+ uint8_t *smbios_table;
+ size_t smbios_len;
+ uint64_t *numa_fw_cfg;
+ int i, j;
+
+ register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
+ register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
+ register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
+
+ register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
+ register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
+ register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
+
+ fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, (uint8_t *)acpi_tables,
+ acpi_tables_len);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_IRQ0_OVERRIDE, &irq0override, 1);
+
+ smbios_table = smbios_get_table(&smbios_len);
+ if (smbios_table)
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
+ smbios_table, smbios_len);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, (uint8_t *)&e820_table,
+ sizeof(struct e820_table));
+
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, (uint8_t *)&hpet_cfg,
+ sizeof(struct hpet_fw_config));
+ /* allocate memory for the NUMA channel: one (64bit) word for the number
+ * of nodes, one word for each VCPU->node and one word for each node to
+ * hold the amount of memory.
+ */
+ numa_fw_cfg = qemu_mallocz((1 + smp_cpus + nb_numa_nodes) * 8);
+ numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
+ for (i = 0; i < smp_cpus; i++) {
+ for (j = 0; j < nb_numa_nodes; j++) {
+ if (node_cpumask[j] & (1 << i)) {
+ numa_fw_cfg[i + 1] = cpu_to_le64(j);
+ break;
+ }
+ }
+ }
+ for (i = 0; i < nb_numa_nodes; i++) {
+ numa_fw_cfg[smp_cpus + 1 + i] = cpu_to_le64(node_mem[i]);
+ }
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, (uint8_t *)numa_fw_cfg,
+ (1 + smp_cpus + nb_numa_nodes) * 8);
+
+ return fw_cfg;
+}
+
+static long get_file_size(FILE *f)
+{
+ long where, size;
+
+ /* XXX: on Unix systems, using fstat() probably makes more sense */
+
+ where = ftell(f);
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, where, SEEK_SET);
+
+ return size;
+}
+
+static void load_linux(void *fw_cfg,
+ const char *kernel_filename,
+ const char *initrd_filename,
+ const char *kernel_cmdline,
+ target_phys_addr_t max_ram_size)
+{
+ uint16_t protocol;
+ int setup_size, kernel_size, initrd_size = 0, cmdline_size;
+ uint32_t initrd_max;
+ uint8_t header[8192], *setup, *kernel, *initrd_data;
+ target_phys_addr_t real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
+ FILE *f;
+ char *vmode;
+
+ /* Align to 16 bytes as a paranoia measure */
+ cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
+
+ /* load the kernel header */
+ f = fopen(kernel_filename, "rb");
+ if (!f || !(kernel_size = get_file_size(f)) ||
+ fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) !=
+ MIN(ARRAY_SIZE(header), kernel_size)) {
+ fprintf(stderr, "qemu: could not load kernel '%s': %s\n",
+ kernel_filename, strerror(errno));
+ exit(1);
+ }
+
+ /* kernel protocol version */
+#if 0
+ fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
+#endif
+ if (ldl_p(header+0x202) == 0x53726448)
+ protocol = lduw_p(header+0x206);
+ else {
+ /* This looks like a multiboot kernel. If it is, let's stop
+ treating it like a Linux kernel. */
+ if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
+ kernel_cmdline, kernel_size, header))
+ return;
+ protocol = 0;
+ }
+
+ if (protocol < 0x200 || !(header[0x211] & 0x01)) {
+ /* Low kernel */
+ real_addr = 0x90000;
+ cmdline_addr = 0x9a000 - cmdline_size;
+ prot_addr = 0x10000;
+ } else if (protocol < 0x202) {
+ /* High but ancient kernel */
+ real_addr = 0x90000;
+ cmdline_addr = 0x9a000 - cmdline_size;
+ prot_addr = 0x100000;
+ } else {
+ /* High and recent kernel */
+ real_addr = 0x10000;
+ cmdline_addr = 0x20000;
+ prot_addr = 0x100000;
+ }
+
+#if 0
+ fprintf(stderr,
+ "qemu: real_addr = 0x" TARGET_FMT_plx "\n"
+ "qemu: cmdline_addr = 0x" TARGET_FMT_plx "\n"
+ "qemu: prot_addr = 0x" TARGET_FMT_plx "\n",
+ real_addr,
+ cmdline_addr,
+ prot_addr);
+#endif
+
+ /* highest address for loading the initrd */
+ if (protocol >= 0x203)
+ initrd_max = ldl_p(header+0x22c);
+ else
+ initrd_max = 0x37ffffff;
+
+ if (initrd_max >= max_ram_size-ACPI_DATA_SIZE)
+ initrd_max = max_ram_size-ACPI_DATA_SIZE-1;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline)+1);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
+ (uint8_t*)strdup(kernel_cmdline),
+ strlen(kernel_cmdline)+1);
+
+ if (protocol >= 0x202) {
+ stl_p(header+0x228, cmdline_addr);
+ } else {
+ stw_p(header+0x20, 0xA33F);
+ stw_p(header+0x22, cmdline_addr-real_addr);
+ }
+
+ /* handle vga= parameter */
+ vmode = strstr(kernel_cmdline, "vga=");
+ if (vmode) {
+ unsigned int video_mode;
+ /* skip "vga=" */
+ vmode += 4;
+ if (!strncmp(vmode, "normal", 6)) {
+ video_mode = 0xffff;
+ } else if (!strncmp(vmode, "ext", 3)) {
+ video_mode = 0xfffe;
+ } else if (!strncmp(vmode, "ask", 3)) {
+ video_mode = 0xfffd;
+ } else {
+ video_mode = strtol(vmode, NULL, 0);
+ }
+ stw_p(header+0x1fa, video_mode);
+ }
+
+ /* loader type */
+ /* High nybble = B reserved for Qemu; low nybble is revision number.
+ If this code is substantially changed, you may want to consider
+ incrementing the revision. */
+ if (protocol >= 0x200)
+ header[0x210] = 0xB0;
+
+ /* heap */
+ if (protocol >= 0x201) {
+ header[0x211] |= 0x80; /* CAN_USE_HEAP */
+ stw_p(header+0x224, cmdline_addr-real_addr-0x200);
+ }
+
+ /* load initrd */
+ if (initrd_filename) {
+ if (protocol < 0x200) {
+ fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
+ exit(1);
+ }
+
+ initrd_size = get_image_size(initrd_filename);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: error reading initrd %s\n",
+ initrd_filename);
+ exit(1);
+ }
+
+ initrd_addr = (initrd_max-initrd_size) & ~4095;
+
+ initrd_data = qemu_malloc(initrd_size);
+ load_image(initrd_filename, initrd_data);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
+
+ stl_p(header+0x218, initrd_addr);
+ stl_p(header+0x21c, initrd_size);
+ }
+
+ /* load kernel and setup */
+ setup_size = header[0x1f1];
+ if (setup_size == 0)
+ setup_size = 4;
+ setup_size = (setup_size+1)*512;
+ kernel_size -= setup_size;
+
+ setup = qemu_malloc(setup_size);
+ kernel = qemu_malloc(kernel_size);
+ fseek(f, 0, SEEK_SET);
+ if (fread(setup, 1, setup_size, f) != setup_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ if (fread(kernel, 1, kernel_size, f) != kernel_size) {
+ fprintf(stderr, "fread() failed\n");
+ exit(1);
+ }
+ fclose(f);
+ memcpy(setup, header, MIN(sizeof(header), setup_size));
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
+
+ option_rom[nb_option_roms].name = "linuxboot.bin";
+ option_rom[nb_option_roms].bootindex = 0;
+ nb_option_roms++;
+}
+
+#define NE2000_NB_MAX 6
+
+static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
+ 0x280, 0x380 };
+static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
+
+void pc_init_ne2k_isa(NICInfo *nd)
+{
+ static int nb_ne2k = 0;
+
+ if (nb_ne2k == NE2000_NB_MAX)
+ return;
+ isa_ne2000_init(ne2000_io[nb_ne2k],
+ ne2000_irq[nb_ne2k], nd);
+ nb_ne2k++;
+}
+
+int cpu_is_bsp(CPUState *env)
+{
+ /* We hard-wire the BSP to the first CPU. */
+ return env->cpu_index == 0;
+}
+
+DeviceState *cpu_get_current_apic(void)
+{
+ if (cpu_single_env) {
+ return cpu_single_env->apic_state;
+ } else {
+ return NULL;
+ }
+}
+
+static DeviceState *apic_init(void *env, uint8_t apic_id)
+{
+ DeviceState *dev;
+ SysBusDevice *d;
+ static int apic_mapped;
+
+ dev = qdev_create(NULL, "apic");
+ qdev_prop_set_uint8(dev, "id", apic_id);
+ qdev_prop_set_ptr(dev, "cpu_env", env);
+ qdev_init_nofail(dev);
+ d = sysbus_from_qdev(dev);
+
+ /* XXX: mapping more APICs at the same memory location */
+ if (apic_mapped == 0) {
+ /* NOTE: the APIC is directly connected to the CPU - it is not
+ on the global memory bus. */
+ /* XXX: what if the base changes? */
+ sysbus_mmio_map(d, 0, MSI_ADDR_BASE);
+ apic_mapped = 1;
+ }
+
+ msix_supported = 1;
+
+ return dev;
+}
+
+/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
+ BIOS will read it and start S3 resume at POST Entry */
+void pc_cmos_set_s3_resume(void *opaque, int irq, int level)
+{
+ ISADevice *s = opaque;
+
+ if (level) {
+ rtc_set_memory(s, 0xF, 0xFE);
+ }
+}
+
+void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
+{
+ CPUState *s = opaque;
+
+ if (level) {
+ cpu_interrupt(s, CPU_INTERRUPT_SMI);
+ }
+}
+
+static void pc_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ env->halted = !cpu_is_bsp(env);
+}
+
+CPUState *pc_new_cpu(const char *cpu_model)
+{
+ CPUState *env;
+
+ if (cpu_model == NULL) {
+#ifdef TARGET_X86_64
+ cpu_model = "qemu64";
+#else
+ cpu_model = "qemu32";
+#endif
+ }
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find x86 CPU definition\n");
+ exit(1);
+ }
+ if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) {
+ env->cpuid_apic_id = env->cpu_index;
+ env->apic_state = apic_init(env, env->cpuid_apic_id);
+ }
+ qemu_register_reset(pc_cpu_reset, env);
+ pc_cpu_reset(env);
+ return env;
+}
+
+void pc_cpus_init(const char *cpu_model)
+{
+ int i;
+
+ /* init CPUs */
+ for(i = 0; i < smp_cpus; i++) {
+ pc_new_cpu(cpu_model);
+ }
+}
+
+void pc_memory_init(ram_addr_t ram_size,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ ram_addr_t *below_4g_mem_size_p,
+ ram_addr_t *above_4g_mem_size_p)
+{
+ char *filename;
+ int ret, linux_boot, i;
+ ram_addr_t ram_addr, bios_offset, option_rom_offset;
+ ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
+ int bios_size, isa_bios_size;
+ void *fw_cfg;
+
+ if (ram_size >= 0xe0000000 ) {
+ above_4g_mem_size = ram_size - 0xe0000000;
+ below_4g_mem_size = 0xe0000000;
+ } else {
+ below_4g_mem_size = ram_size;
+ }
+ *above_4g_mem_size_p = above_4g_mem_size;
+ *below_4g_mem_size_p = below_4g_mem_size;
+
+#if TARGET_PHYS_ADDR_BITS == 32
+ if (above_4g_mem_size > 0) {
+ hw_error("To much RAM for 32-bit physical address");
+ }
+#endif
+ linux_boot = (kernel_filename != NULL);
+
+ /* allocate RAM */
+ ram_addr = qemu_ram_alloc(NULL, "pc.ram",
+ below_4g_mem_size + above_4g_mem_size);
+ cpu_register_physical_memory(0, 0xa0000, ram_addr);
+ cpu_register_physical_memory(0x100000,
+ below_4g_mem_size - 0x100000,
+ ram_addr + 0x100000);
+#if TARGET_PHYS_ADDR_BITS > 32
+ if (above_4g_mem_size > 0) {
+ cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
+ ram_addr + below_4g_mem_size);
+ }
+#endif
+
+ /* BIOS load */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size <= 0 ||
+ (bios_size % 65536) != 0) {
+ goto bios_error;
+ }
+ bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size);
+ ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
+ if (ret != 0) {
+ bios_error:
+ fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
+ exit(1);
+ }
+ if (filename) {
+ qemu_free(filename);
+ }
+ /* map the last 128KB of the BIOS in ISA space */
+ isa_bios_size = bios_size;
+ if (isa_bios_size > (128 * 1024))
+ isa_bios_size = 128 * 1024;
+ cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size,
+ IO_MEM_UNASSIGNED);
+ cpu_register_physical_memory(0x100000 - isa_bios_size,
+ isa_bios_size,
+ (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
+
+ if (extboot_drive) {
+ option_rom[nb_option_roms].name = qemu_strdup(EXTBOOT_FILENAME);
+ option_rom[nb_option_roms].bootindex = 0;
+ nb_option_roms++;
+ }
+ option_rom[nb_option_roms].name = qemu_strdup(VAPIC_FILENAME);
+ option_rom[nb_option_roms].bootindex = -1;
+ nb_option_roms++;
+
+ option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE);
+ cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset);
+
+ /* map all the bios at the top of memory */
+ cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+
+ fw_cfg = bochs_bios_init();
+ rom_set_fw(fw_cfg);
+
+ if (linux_boot) {
+ load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
+ }
+
+ for (i = 0; i < nb_option_roms; i++) {
+ rom_add_option(option_rom[i].name, option_rom[i].bootindex);
+ }
+}
+
+qemu_irq *pc_allocate_cpu_irq(void)
+{
+ return qemu_allocate_irqs(pic_irq_request, NULL, 1);
+}
+
+void pc_vga_init(PCIBus *pci_bus)
+{
+ if (cirrus_vga_enabled) {
+ if (pci_bus) {
+ pci_cirrus_vga_init(pci_bus);
+ } else {
+ isa_cirrus_vga_init();
+ }
+ } else if (vmsvga_enabled) {
+ if (pci_bus)
+ pci_vmsvga_init(pci_bus);
+ else
+ fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+#ifdef CONFIG_SPICE
+ } else if (qxl_enabled) {
+ if (pci_bus)
+ pci_create_simple(pci_bus, -1, "qxl-vga");
+ else
+ fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
+#endif
+ } else if (std_vga_enabled) {
+ if (pci_bus) {
+ pci_vga_init(pci_bus);
+ } else {
+ isa_vga_init();
+ }
+ }
+}
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env && level) {
+ cpu_exit(env);
+ }
+}
+
+void pc_basic_device_init(qemu_irq *isa_irq,
+ FDCtrl **floppy_controller,
+ ISADevice **rtc_state)
+{
+ int i;
+ DriveInfo *fd[MAX_FD];
+ PITState *pit;
+ qemu_irq rtc_irq = NULL;
+ qemu_irq *a20_line;
+ ISADevice *i8042, *port92;
+ qemu_irq *cpu_exit_irq;
+
+ register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
+
+ register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
+
+ if (!no_hpet) {
+ DeviceState *hpet = sysbus_create_simple("hpet", HPET_BASE, NULL);
+
+ for (i = 0; i < 24; i++) {
+ sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]);
+ }
+ rtc_irq = qdev_get_gpio_in(hpet, 0);
+ }
+ *rtc_state = rtc_init(2000, rtc_irq);
+
+ qemu_register_boot_set(pc_boot_set, *rtc_state);
+
+#ifdef CONFIG_KVM_PIT
+ if (kvm_enabled() && kvm_pit_in_kernel())
+ pit = kvm_pit_init(0x40, isa_get_irq(0));
+ else
+#endif
+
+ pit = pit_init(0x40, isa_get_irq(0));
+ pcspk_init(pit);
+
+ for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_isa_init(i, serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(i, parallel_hds[i]);
+ }
+ }
+
+ a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
+ i8042 = isa_create_simple("i8042");
+ i8042_setup_a20_line(i8042, &a20_line[0]);
+ vmmouse_init(i8042);
+ port92 = isa_create_simple("port92");
+ port92_init(port92, &a20_line[1]);
+
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(0, cpu_exit_irq);
+
+ for(i = 0; i < MAX_FD; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ }
+ *floppy_controller = fdctrl_init_isa(fd);
+}
+
+void pc_pci_device_init(PCIBus *pci_bus)
+{
+ int max_bus;
+ int bus;
+
+ max_bus = drive_get_max_bus(IF_SCSI);
+ for (bus = 0; bus <= max_bus; bus++) {
+ pci_create_simple(pci_bus, -1, "lsi53c895a");
+ }
+
+ if (extboot_drive) {
+ DriveInfo *info = extboot_drive;
+ int cyls, heads, secs;
+
+ if (info->type != IF_IDE && info->type != IF_VIRTIO) {
+ bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs);
+ bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs);
+ }
+
+ extboot_init(info->bdrv);
+ }
+}
diff --git a/hw/pc.h b/hw/pc.h
new file mode 100644
index 0000000..21fd219
--- /dev/null
+++ b/hw/pc.h
@@ -0,0 +1,191 @@
+#ifndef HW_PC_H
+#define HW_PC_H
+
+#include "qemu-common.h"
+#include "ioport.h"
+#include "isa.h"
+#include "fdc.h"
+
+/* PC-style peripherals (also used by other machines). */
+
+/* serial.c */
+
+SerialState *serial_init(int base, qemu_irq irq, int baudbase,
+ CharDriverState *chr);
+SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
+ qemu_irq irq, int baudbase,
+ CharDriverState *chr, int ioregister,
+ int be);
+SerialState *serial_isa_init(int index, CharDriverState *chr);
+void serial_set_frequency(SerialState *s, uint32_t frequency);
+
+/* parallel.c */
+
+typedef struct ParallelState ParallelState;
+ParallelState *parallel_init(int index, CharDriverState *chr);
+ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr);
+
+/* i8259.c */
+
+typedef struct PicState2 PicState2;
+extern PicState2 *isa_pic;
+void pic_set_irq(int irq, int level);
+void pic_set_irq_new(void *opaque, int irq, int level);
+qemu_irq *i8259_init(qemu_irq parent_irq);
+qemu_irq *kvm_i8259_init(qemu_irq parent_irq);
+int pic_read_irq(PicState2 *s);
+void pic_update_irq(PicState2 *s);
+uint32_t pic_intack_read(PicState2 *s);
+void pic_info(Monitor *mon);
+void irq_info(Monitor *mon);
+
+/* ISA */
+#define IOAPIC_NUM_PINS 0x18
+
+typedef struct isa_irq_state {
+ qemu_irq *i8259;
+ qemu_irq ioapic[IOAPIC_NUM_PINS];
+} IsaIrqState;
+
+void isa_irq_handler(void *opaque, int n, int level);
+
+/* i8254.c */
+
+#define PIT_FREQ 1193182
+
+typedef struct PITState PITState;
+
+PITState *pit_init(int base, qemu_irq irq);
+void pit_set_gate(PITState *pit, int channel, int val);
+int pit_get_gate(PITState *pit, int channel);
+int pit_get_initial_count(PITState *pit, int channel);
+int pit_get_mode(PITState *pit, int channel);
+int pit_get_out(PITState *pit, int channel, int64_t current_time);
+
+/* i8254-kvm.c */
+
+PITState *kvm_pit_init(int base, qemu_irq irq);
+
+void hpet_pit_disable(void);
+void hpet_pit_enable(void);
+
+/* vmport.c */
+void vmport_init(void);
+void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque);
+
+/* vmmouse.c */
+void *vmmouse_init(void *m);
+
+/* pckbd.c */
+
+void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base);
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+ target_phys_addr_t base, ram_addr_t size,
+ target_phys_addr_t mask);
+void i8042_isa_mouse_fake_event(void *opaque);
+void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out);
+
+/* pc.c */
+extern int fd_bootchk;
+
+void pc_register_ferr_irq(qemu_irq irq);
+void pc_cmos_set_s3_resume(void *opaque, int irq, int level);
+void pc_acpi_smi_interrupt(void *opaque, int irq, int level);
+
+void pc_cpus_init(const char *cpu_model);
+void pc_memory_init(ram_addr_t ram_size,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ ram_addr_t *below_4g_mem_size_p,
+ ram_addr_t *above_4g_mem_size_p);
+qemu_irq *pc_allocate_cpu_irq(void);
+void pc_vga_init(PCIBus *pci_bus);
+void pc_basic_device_init(qemu_irq *isa_irq,
+ FDCtrl **floppy_controller,
+ ISADevice **rtc_state);
+void pc_init_ne2k_isa(NICInfo *nd);
+void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
+ const char *boot_device,
+ BusState *ide0, BusState *ide1,
+ FDCtrl *floppy_controller, ISADevice *s);
+void pc_pci_device_init(PCIBus *pci_bus);
+
+typedef void (*cpu_set_smm_t)(int smm, void *arg);
+void cpu_smm_register(cpu_set_smm_t callback, void *arg);
+
+/* acpi.c */
+extern int acpi_enabled;
+extern char *acpi_tables;
+extern size_t acpi_tables_len;
+
+void acpi_bios_init(void);
+int acpi_table_add(const char *table_desc);
+
+/* acpi_piix.c */
+
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+ qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq,
+ int kvm_enabled);
+void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
+
+/* hpet.c */
+extern int no_hpet;
+
+/* pcspk.c */
+void pcspk_init(PITState *);
+int pcspk_audio_init(qemu_irq *pic);
+
+/* piix_pci.c */
+/* config space register for IRQ routing */
+#define PIIX_CONFIG_IRQ_ROUTE 0x60
+
+struct PCII440FXState;
+typedef struct PCII440FXState PCII440FXState;
+
+PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size);
+void i440fx_init_memory_mappings(PCII440FXState *d);
+
+/* piix4.c */
+extern PCIDevice *piix4_dev;
+int piix4_init(PCIBus *bus, int devfn);
+
+int piix_get_irq(int pin);
+
+int ipf_map_irq(PCIDevice *pci_dev, int irq_num);
+
+/* vga.c */
+enum vga_retrace_method {
+ VGA_RETRACE_DUMB,
+ VGA_RETRACE_PRECISE
+};
+
+extern enum vga_retrace_method vga_retrace_method;
+
+int isa_vga_init(void);
+int pci_vga_init(PCIBus *bus);
+int isa_vga_mm_init(target_phys_addr_t vram_base,
+ target_phys_addr_t ctrl_base, int it_shift);
+
+/* cirrus_vga.c */
+void pci_cirrus_vga_init(PCIBus *bus);
+void isa_cirrus_vga_init(void);
+
+/* ne2000.c */
+
+void isa_ne2000_init(int base, int irq, NICInfo *nd);
+
+/* extboot.c */
+
+void extboot_init(BlockDriverState *bs);
+
+/* e820 types */
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3
+#define E820_NVS 4
+#define E820_UNUSABLE 5
+
+int e820_add_entry(uint64_t, uint64_t, uint32_t);
+
+#endif
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
new file mode 100644
index 0000000..0a51e20
--- /dev/null
+++ b/hw/pc_piix.c
@@ -0,0 +1,407 @@
+/*
+ * QEMU PC System Emulator
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "apic.h"
+#include "pci.h"
+#include "usb-uhci.h"
+#include "usb-ohci.h"
+#include "net.h"
+#include "boards.h"
+#include "ide.h"
+#include "kvm.h"
+#include "sysemu.h"
+#include "sysbus.h"
+#include "arch_init.h"
+#include "blockdev.h"
+
+qemu_irq *ioapic_irq_hack;
+
+#define MAX_IDE_BUS 2
+
+static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
+static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
+static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
+
+const char *global_cpu_model; /* cpu hotadd */
+
+static void ioapic_init(IsaIrqState *isa_irq_state)
+{
+ DeviceState *dev;
+ SysBusDevice *d;
+ unsigned int i;
+
+ dev = qdev_create(NULL, "ioapic");
+ qdev_init_nofail(dev);
+ d = sysbus_from_qdev(dev);
+ sysbus_mmio_map(d, 0, 0xfec00000);
+
+ for (i = 0; i < IOAPIC_NUM_PINS; i++) {
+ isa_irq_state->ioapic[i] = qdev_get_gpio_in(dev, i);
+ }
+}
+
+/* PC hardware initialisation */
+static void pc_init1(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model,
+ int pci_enabled)
+{
+ int i;
+ ram_addr_t below_4g_mem_size, above_4g_mem_size;
+ PCIBus *pci_bus;
+ PCII440FXState *i440fx_state;
+ int piix3_devfn = -1;
+ qemu_irq *cpu_irq;
+ qemu_irq *isa_irq;
+ qemu_irq *i8259;
+ qemu_irq *cmos_s3;
+ qemu_irq *smi_irq;
+ IsaIrqState *isa_irq_state;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ FDCtrl *floppy_controller;
+ BusState *idebus[MAX_IDE_BUS];
+ ISADevice *rtc_state;
+
+ global_cpu_model = cpu_model;
+
+ pc_cpus_init(cpu_model);
+
+ vmport_init();
+
+ /* allocate ram and load rom/bios */
+ pc_memory_init(ram_size, kernel_filename, kernel_cmdline, initrd_filename,
+ &below_4g_mem_size, &above_4g_mem_size);
+
+ cpu_irq = pc_allocate_cpu_irq();
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+ isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
+ if (pci_enabled) {
+ ioapic_init(isa_irq_state);
+ }
+ isa_irq = i8259 = kvm_i8259_init(cpu_irq[0]);
+ ioapic_irq_hack = isa_irq;
+ } else
+#endif
+ {
+ i8259 = i8259_init(cpu_irq[0]);
+ isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
+ isa_irq_state->i8259 = i8259;
+ if (pci_enabled) {
+ ioapic_init(isa_irq_state);
+ }
+ isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
+ }
+
+ if (pci_enabled) {
+ pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size);
+ } else {
+ pci_bus = NULL;
+ i440fx_state = NULL;
+ isa_bus_new(NULL);
+ }
+ isa_bus_irqs(isa_irq);
+
+ pc_register_ferr_irq(isa_get_irq(13));
+
+ pc_vga_init(pci_enabled? pci_bus: NULL);
+
+ /* init basic PC hardware */
+ pc_basic_device_init(isa_irq, &floppy_controller, &rtc_state);
+
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
+ pc_init_ne2k_isa(nd);
+ else
+ pci_nic_init_nofail(nd, "rtl8139", NULL);
+ }
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ }
+
+ if (pci_enabled) {
+ PCIDevice *dev;
+ dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
+ idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0");
+ idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1");
+ } else {
+ for(i = 0; i < MAX_IDE_BUS; i++) {
+ ISADevice *dev;
+ dev = isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
+ hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
+ idebus[i] = qdev_get_child_bus(&dev->qdev, "ide.0");
+ }
+ }
+
+ audio_init(isa_irq, pci_enabled ? pci_bus : NULL);
+
+ pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device,
+ idebus[0], idebus[1], floppy_controller, rtc_state);
+
+ if (pci_enabled && usb_enabled) {
+ usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
+ }
+
+ if (pci_enabled && acpi_enabled) {
+ uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
+ i2c_bus *smbus;
+
+ cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1);
+ smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1);
+ /* TODO: Populate SPD eeprom data. */
+ smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
+ isa_get_irq(9), *cmos_s3, *smi_irq,
+ kvm_enabled());
+ for (i = 0; i < 8; i++) {
+ DeviceState *eeprom;
+ eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+ qdev_prop_set_uint8(eeprom, "address", 0x50 + i);
+ qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256));
+ qdev_init_nofail(eeprom);
+ }
+ }
+
+ if (i440fx_state) {
+ i440fx_init_memory_mappings(i440fx_state);
+ }
+
+ if (pci_enabled) {
+ pc_pci_device_init(pci_bus);
+ }
+}
+
+static void pc_init_pci(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ pc_init1(ram_size, boot_device,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, 1);
+}
+
+static void pc_init_isa(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ if (cpu_model == NULL)
+ cpu_model = "486";
+ pc_init1(ram_size, boot_device,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, 0);
+}
+
+static QEMUMachine pc_machine = {
+ .name = "pc-0.14",
+ .alias = "pc",
+ .desc = "Standard PC",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .is_default = 1,
+};
+
+static QEMUMachine pc_machine_v0_13 = {
+ .name = "pc-0.13",
+ .desc = "Standard PC",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "virtio-9p-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "VGA",
+ .property = "rombar",
+ .value = stringify(0),
+ },{
+ .driver = "vmware-svga",
+ .property = "rombar",
+ .value = stringify(0),
+ },{
+ .driver = "PCI",
+ .property = "command_serr_enable",
+ .value = "off",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QEMUMachine pc_machine_v0_12 = {
+ .name = "pc-0.12",
+ .desc = "Standard PC",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "virtio-serial-pci",
+ .property = "max_ports",
+ .value = stringify(1),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "VGA",
+ .property = "rombar",
+ .value = stringify(0),
+ },{
+ .driver = "vmware-svga",
+ .property = "rombar",
+ .value = stringify(0),
+ },{
+ .driver = "PCI",
+ .property = "command_serr_enable",
+ .value = "off",
+ },
+ { /* end of list */ }
+ }
+};
+
+static QEMUMachine pc_machine_v0_11 = {
+ .name = "pc-0.11",
+ .desc = "Standard PC, qemu 0.11",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "virtio-blk-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "max_ports",
+ .value = stringify(1),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "ide-drive",
+ .property = "ver",
+ .value = "0.11",
+ },{
+ .driver = "scsi-disk",
+ .property = "ver",
+ .value = "0.11",
+ },{
+ .driver = "PCI",
+ .property = "rombar",
+ .value = stringify(0),
+ },{
+ .driver = "PCI",
+ .property = "command_serr_enable",
+ .value = "off",
+ },
+ { /* end of list */ }
+ }
+};
+
+static QEMUMachine pc_machine_v0_10 = {
+ .name = "pc-0.10",
+ .desc = "Standard PC, qemu 0.10",
+ .init = pc_init_pci,
+ .max_cpus = 255,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "virtio-blk-pci",
+ .property = "class",
+ .value = stringify(PCI_CLASS_STORAGE_OTHER),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "class",
+ .value = stringify(PCI_CLASS_DISPLAY_OTHER),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "max_ports",
+ .value = stringify(1),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "virtio-net-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "virtio-blk-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },{
+ .driver = "ide-drive",
+ .property = "ver",
+ .value = "0.10",
+ },{
+ .driver = "scsi-disk",
+ .property = "ver",
+ .value = "0.10",
+ },{
+ .driver = "PCI",
+ .property = "rombar",
+ .value = stringify(0),
+ },{
+ .driver = "PCI",
+ .property = "command_serr_enable",
+ .value = "off",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QEMUMachine isapc_machine = {
+ .name = "isapc",
+ .desc = "ISA-only PC",
+ .init = pc_init_isa,
+ .max_cpus = 1,
+};
+
+static void pc_machine_init(void)
+{
+ qemu_register_machine(&pc_machine);
+ qemu_register_machine(&pc_machine_v0_13);
+ qemu_register_machine(&pc_machine_v0_12);
+ qemu_register_machine(&pc_machine_v0_11);
+ qemu_register_machine(&pc_machine_v0_10);
+ qemu_register_machine(&isapc_machine);
+}
+
+machine_init(pc_machine_init);
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
new file mode 100644
index 0000000..4385aa2
--- /dev/null
+++ b/hw/pci-hotplug.c
@@ -0,0 +1,292 @@
+/*
+ * QEMU PCI hotplug support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "boards.h"
+#include "pci.h"
+#include "net.h"
+#include "pc.h"
+#include "monitor.h"
+#include "scsi.h"
+#include "virtio-blk.h"
+#include "qemu-config.h"
+#include "device-assignment.h"
+#include "blockdev.h"
+
+#if defined(TARGET_I386)
+static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon,
+ const char *devaddr,
+ const char *opts_str)
+{
+ QemuOpts *opts;
+ PCIBus *bus;
+ int ret, devfn;
+
+ bus = pci_get_bus_devfn(&devfn, devaddr);
+ if (!bus) {
+ monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
+ return NULL;
+ }
+ if (!((BusState*)bus)->allow_hotplug) {
+ monitor_printf(mon, "PCI bus doesn't support hotplug\n");
+ return NULL;
+ }
+
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
+ if (!opts) {
+ return NULL;
+ }
+
+ qemu_opt_set(opts, "type", "nic");
+
+ ret = net_client_init(mon, opts, 0);
+ if (ret < 0)
+ return NULL;
+ if (nd_table[ret].devaddr) {
+ monitor_printf(mon, "Parameter addr not supported\n");
+ return NULL;
+ }
+ return pci_nic_init(&nd_table[ret], "rtl8139", devaddr);
+}
+
+static int scsi_hot_add(Monitor *mon, DeviceState *adapter,
+ DriveInfo *dinfo, int printinfo)
+{
+ SCSIBus *scsibus;
+ SCSIDevice *scsidev;
+
+ scsibus = DO_UPCAST(SCSIBus, qbus, QLIST_FIRST(&adapter->child_bus));
+ if (!scsibus || strcmp(scsibus->qbus.info->name, "SCSI") != 0) {
+ error_report("Device is not a SCSI adapter");
+ return -1;
+ }
+
+ /*
+ * drive_init() tries to find a default for dinfo->unit. Doesn't
+ * work at all for hotplug though as we assign the device to a
+ * specific bus instead of the first bus with spare scsi ids.
+ *
+ * Ditch the calculated value and reload from option string (if
+ * specified).
+ */
+ dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1);
+ dinfo->bus = scsibus->busnr;
+ scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, false);
+ if (!scsidev) {
+ return -1;
+ }
+ dinfo->unit = scsidev->id;
+
+ if (printinfo)
+ monitor_printf(mon, "OK bus %d, unit %d\n",
+ scsibus->busnr, scsidev->id);
+ return 0;
+}
+
+void drive_hot_add(Monitor *mon, const QDict *qdict)
+{
+ int dom, pci_bus;
+ unsigned slot;
+ int type;
+ PCIDevice *dev;
+ DriveInfo *dinfo = NULL;
+ const char *pci_addr = qdict_get_str(qdict, "pci_addr");
+ const char *opts = qdict_get_str(qdict, "opts");
+
+ dinfo = add_init_drive(opts);
+ if (!dinfo)
+ goto err;
+ if (dinfo->devaddr) {
+ monitor_printf(mon, "Parameter addr not supported\n");
+ goto err;
+ }
+ type = dinfo->type;
+
+ switch (type) {
+ case IF_SCSI:
+ if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) {
+ goto err;
+ }
+ dev = pci_find_device(pci_find_root_bus(dom), pci_bus, slot, 0);
+ if (!dev) {
+ monitor_printf(mon, "no pci device with address %s\n", pci_addr);
+ goto err;
+ }
+ if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) {
+ goto err;
+ }
+ break;
+ case IF_NONE:
+ monitor_printf(mon, "OK\n");
+ break;
+ default:
+ monitor_printf(mon, "Can't hot-add drive to type %d\n", type);
+ goto err;
+ }
+ return;
+
+err:
+ if (dinfo)
+ drive_put_ref(dinfo);
+ return;
+}
+
+static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
+ const char *devaddr,
+ const char *opts)
+{
+ PCIDevice *dev;
+ DriveInfo *dinfo = NULL;
+ int type = -1;
+ char buf[128];
+ PCIBus *bus;
+ int devfn;
+
+ if (get_param_value(buf, sizeof(buf), "if", opts)) {
+ if (!strcmp(buf, "scsi"))
+ type = IF_SCSI;
+ else if (!strcmp(buf, "virtio")) {
+ type = IF_VIRTIO;
+ } else {
+ monitor_printf(mon, "type %s not a hotpluggable PCI device.\n", buf);
+ return NULL;
+ }
+ } else {
+ monitor_printf(mon, "no if= specified\n");
+ return NULL;
+ }
+
+ if (get_param_value(buf, sizeof(buf), "file", opts)) {
+ dinfo = add_init_drive(opts);
+ if (!dinfo)
+ return NULL;
+ if (dinfo->devaddr) {
+ monitor_printf(mon, "Parameter addr not supported\n");
+ return NULL;
+ }
+ } else {
+ dinfo = NULL;
+ }
+
+ bus = pci_get_bus_devfn(&devfn, devaddr);
+ if (!bus) {
+ monitor_printf(mon, "Invalid PCI device address %s\n", devaddr);
+ return NULL;
+ }
+ if (!((BusState*)bus)->allow_hotplug) {
+ monitor_printf(mon, "PCI bus doesn't support hotplug\n");
+ return NULL;
+ }
+
+ switch (type) {
+ case IF_SCSI:
+ dev = pci_create(bus, devfn, "lsi53c895a");
+ if (qdev_init(&dev->qdev) < 0)
+ dev = NULL;
+ if (dev && dinfo) {
+ if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) {
+ qdev_unplug(&dev->qdev);
+ dev = NULL;
+ }
+ }
+ break;
+ case IF_VIRTIO:
+ if (!dinfo) {
+ monitor_printf(mon, "virtio requires a backing file/device.\n");
+ return NULL;
+ }
+ dev = pci_create(bus, devfn, "virtio-blk-pci");
+ if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
+ qdev_free(&dev->qdev);
+ dev = NULL;
+ break;
+ }
+ if (qdev_init(&dev->qdev) < 0)
+ dev = NULL;
+ break;
+ default:
+ dev = NULL;
+ }
+ return dev;
+}
+
+void pci_device_hot_add(Monitor *mon, const QDict *qdict)
+{
+ PCIDevice *dev = NULL;
+ const char *pci_addr = qdict_get_str(qdict, "pci_addr");
+ const char *type = qdict_get_str(qdict, "type");
+ const char *opts = qdict_get_try_str(qdict, "opts");
+
+ /* strip legacy tag */
+ if (!strncmp(pci_addr, "pci_addr=", 9)) {
+ pci_addr += 9;
+ }
+
+ if (!opts) {
+ opts = "";
+ }
+
+ if (!strcmp(pci_addr, "auto"))
+ pci_addr = NULL;
+
+ if (strcmp(type, "nic") == 0) {
+ dev = qemu_pci_hot_add_nic(mon, pci_addr, opts);
+ } else if (strcmp(type, "storage") == 0) {
+ dev = qemu_pci_hot_add_storage(mon, pci_addr, opts);
+ } else {
+ monitor_printf(mon, "invalid type: %s\n", type);
+ }
+
+ if (dev) {
+ monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n",
+ pci_find_domain(dev->bus),
+ pci_bus_num(dev->bus), PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+ } else
+ monitor_printf(mon, "failed to add %s\n", opts);
+}
+#endif
+
+static int pci_device_hot_remove(Monitor *mon, const char *pci_addr)
+{
+ PCIDevice *d;
+ int dom, bus;
+ unsigned slot;
+
+ if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) {
+ return -1;
+ }
+
+ d = pci_find_device(pci_find_root_bus(dom), bus, slot, 0);
+ if (!d) {
+ monitor_printf(mon, "slot %d empty\n", slot);
+ return -1;
+ }
+ return qdev_unplug(&d->qdev);
+}
+
+void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict)
+{
+ pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr"));
+}
diff --git a/hw/pci-stub.c b/hw/pci-stub.c
new file mode 100644
index 0000000..c5a0aa8
--- /dev/null
+++ b/hw/pci-stub.c
@@ -0,0 +1,50 @@
+/*
+ * PCI stubs for plathome that doesn't support pci bus.
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "sysemu.h"
+#include "monitor.h"
+#include "pci.h"
+
+static void pci_error_message(Monitor *mon)
+{
+ monitor_printf(mon, "PCI devices not supported\n");
+}
+
+void do_pci_info(Monitor *mon, QObject **ret_data)
+{
+ pci_error_message(mon);
+}
+
+void do_pci_info_print(Monitor *mon, const QObject *data)
+{
+ pci_error_message(mon);
+}
+
+int do_pcie_aer_inejct_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
+{
+ pci_error_message(mon);
+ return -ENOSYS;
+}
+
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
+{
+ pci_error_message(mon);
+}
diff --git a/hw/pci.c b/hw/pci.c
new file mode 100644
index 0000000..2f4a0fa
--- /dev/null
+++ b/hw/pci.c
@@ -0,0 +1,2227 @@
+/*
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pci.h"
+#include "pci_bridge.h"
+#include "pci_internals.h"
+#include "monitor.h"
+#include "net.h"
+#include "sysemu.h"
+#include "loader.h"
+#include "hw/pc.h"
+#include "kvm.h"
+#include "device-assignment.h"
+#include "qemu-objects.h"
+#include "range.h"
+
+//#define DEBUG_PCI
+#ifdef DEBUG_PCI
+# define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
+#else
+# define PCI_DPRINTF(format, ...) do { } while (0)
+#endif
+
+static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *pcibus_get_dev_path(DeviceState *dev);
+static char *pcibus_get_fw_dev_path(DeviceState *dev);
+static int pcibus_reset(BusState *qbus);
+
+struct BusInfo pci_bus_info = {
+ .name = "PCI",
+ .size = sizeof(PCIBus),
+ .print_dev = pcibus_dev_print,
+ .get_dev_path = pcibus_get_dev_path,
+ .get_fw_dev_path = pcibus_get_fw_dev_path,
+ .reset = pcibus_reset,
+ .props = (Property[]) {
+ DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
+ DEFINE_PROP_STRING("romfile", PCIDevice, romfile),
+ DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1),
+ DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present,
+ QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false),
+ DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present,
+ QEMU_PCI_CAP_SERR_BITNR, true),
+ DEFINE_PROP_END_OF_LIST()
+ }
+};
+
+static void pci_update_mappings(PCIDevice *d);
+static void pci_set_irq(void *opaque, int irq_num, int level);
+static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom);
+static void pci_del_option_rom(PCIDevice *pdev);
+
+static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
+static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
+
+struct PCIHostBus {
+ int domain;
+ struct PCIBus *bus;
+ QLIST_ENTRY(PCIHostBus) next;
+};
+static QLIST_HEAD(, PCIHostBus) host_buses;
+
+static const VMStateDescription vmstate_pcibus = {
+ .name = "PCIBUS",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_EQUAL(nirq, PCIBus),
+ VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, int32_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int pci_bar(PCIDevice *d, int reg)
+{
+ uint8_t type;
+
+ if (reg != PCI_ROM_SLOT)
+ return PCI_BASE_ADDRESS_0 + reg * 4;
+
+ type = d->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+ return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS;
+}
+
+static inline int pci_irq_state(PCIDevice *d, int irq_num)
+{
+ return (d->irq_state >> irq_num) & 0x1;
+}
+
+static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level)
+{
+ d->irq_state &= ~(0x1 << irq_num);
+ d->irq_state |= level << irq_num;
+}
+
+static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change)
+{
+ PCIBus *bus;
+ for (;;) {
+ bus = pci_dev->bus;
+ irq_num = bus->map_irq(pci_dev, irq_num);
+ if (bus->set_irq)
+ break;
+ pci_dev = bus->parent_dev;
+ }
+ bus->irq_count[irq_num] += change;
+ bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
+}
+
+/* Update interrupt status bit in config space on interrupt
+ * state change. */
+static void pci_update_irq_status(PCIDevice *dev)
+{
+ if (dev->irq_state) {
+ dev->config[PCI_STATUS] |= PCI_STATUS_INTERRUPT;
+ } else {
+ dev->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
+ }
+}
+
+void pci_device_deassert_intx(PCIDevice *dev)
+{
+ int i;
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ qemu_set_irq(dev->irq[i], 0);
+ }
+}
+
+/*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
+{
+ int r;
+ /* TODO: call the below unconditionally once all pci devices
+ * are qdevified */
+ if (dev->qdev.info) {
+ qdev_reset_all(&dev->qdev);
+ }
+
+ dev->irq_state = 0;
+ pci_update_irq_status(dev);
+ pci_device_deassert_intx(dev);
+ /* Clear all writeable bits */
+ pci_word_test_and_clear_mask(dev->config + PCI_COMMAND,
+ pci_get_word(dev->wmask + PCI_COMMAND) |
+ pci_get_word(dev->w1cmask + PCI_COMMAND));
+ pci_word_test_and_clear_mask(dev->config + PCI_STATUS,
+ pci_get_word(dev->wmask + PCI_STATUS) |
+ pci_get_word(dev->w1cmask + PCI_STATUS));
+ dev->config[PCI_CACHE_LINE_SIZE] = 0x0;
+ dev->config[PCI_INTERRUPT_LINE] = 0x0;
+ for (r = 0; r < PCI_NUM_REGIONS; ++r) {
+ PCIIORegion *region = &dev->io_regions[r];
+ if (!region->size) {
+ continue;
+ }
+
+ if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) &&
+ region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_set_quad(dev->config + pci_bar(dev, r), region->type);
+ } else {
+ pci_set_long(dev->config + pci_bar(dev, r), region->type);
+ }
+ }
+ pci_update_mappings(dev);
+}
+
+/*
+ * Trigger pci bus reset under a given bus.
+ * To be called on RST# assert.
+ */
+void pci_bus_reset(PCIBus *bus)
+{
+ int i;
+
+ for (i = 0; i < bus->nirq; i++) {
+ bus->irq_count[i] = 0;
+ }
+ for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
+ if (bus->devices[i]) {
+ pci_device_reset(bus->devices[i]);
+ }
+ }
+}
+
+static int pcibus_reset(BusState *qbus)
+{
+ pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
+
+ /* topology traverse is done by pci_bus_reset().
+ Tell qbus/qdev walker not to traverse the tree */
+ return 1;
+}
+
+static void pci_host_bus_register(int domain, PCIBus *bus)
+{
+ struct PCIHostBus *host;
+ host = qemu_mallocz(sizeof(*host));
+ host->domain = domain;
+ host->bus = bus;
+ QLIST_INSERT_HEAD(&host_buses, host, next);
+}
+
+PCIBus *pci_find_root_bus(int domain)
+{
+ struct PCIHostBus *host;
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ if (host->domain == domain) {
+ return host->bus;
+ }
+ }
+
+ return NULL;
+}
+
+int pci_find_domain(const PCIBus *bus)
+{
+ PCIDevice *d;
+ struct PCIHostBus *host;
+
+ /* obtain root bus */
+ while ((d = bus->parent_dev) != NULL) {
+ bus = d->bus;
+ }
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ if (host->bus == bus) {
+ return host->domain;
+ }
+ }
+
+ abort(); /* should not be reached */
+ return -1;
+}
+
+void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
+ const char *name, int devfn_min)
+{
+ qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name);
+ assert(PCI_FUNC(devfn_min) == 0);
+ bus->devfn_min = devfn_min;
+
+ /* host bridge */
+ QLIST_INIT(&bus->child);
+ pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */
+
+ vmstate_register(NULL, -1, &vmstate_pcibus, bus);
+}
+
+PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min)
+{
+ PCIBus *bus;
+
+ bus = qemu_mallocz(sizeof(*bus));
+ bus->qbus.qdev_allocated = 1;
+ pci_bus_new_inplace(bus, parent, name, devfn_min);
+ return bus;
+}
+
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ void *irq_opaque, int nirq)
+{
+ bus->set_irq = set_irq;
+ bus->map_irq = map_irq;
+ bus->irq_opaque = irq_opaque;
+ bus->nirq = nirq;
+ bus->irq_count = qemu_mallocz(nirq * sizeof(bus->irq_count[0]));
+}
+
+void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev)
+{
+ bus->qbus.allow_hotplug = 1;
+ bus->hotplug = hotplug;
+ bus->hotplug_qdev = qdev;
+}
+
+void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base)
+{
+ bus->mem_base = base;
+}
+
+PCIBus *pci_register_bus(DeviceState *parent, const char *name,
+ pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ void *irq_opaque, int devfn_min, int nirq)
+{
+ PCIBus *bus;
+
+ bus = pci_bus_new(parent, name, devfn_min);
+ pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
+ return bus;
+}
+
+int pci_bus_num(PCIBus *s)
+{
+ if (!s->parent_dev)
+ return 0; /* pci host bridge */
+ return s->parent_dev->config[PCI_SECONDARY_BUS];
+}
+
+static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
+{
+ PCIDevice *s = container_of(pv, PCIDevice, config);
+ uint8_t *config;
+ int i;
+
+ assert(size == pci_config_size(s));
+ config = qemu_malloc(size);
+
+ qemu_get_buffer(f, config, size);
+ for (i = 0; i < size; ++i) {
+ if ((config[i] ^ s->config[i]) &
+ s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) {
+ qemu_free(config);
+ return -EINVAL;
+ }
+ }
+ memcpy(s->config, config, size);
+
+ pci_update_mappings(s);
+
+ qemu_free(config);
+ return 0;
+}
+
+/* just put buffer */
+static void put_pci_config_device(QEMUFile *f, void *pv, size_t size)
+{
+ const uint8_t **v = pv;
+ assert(size == pci_config_size(container_of(pv, PCIDevice, config)));
+ qemu_put_buffer(f, *v, size);
+}
+
+static VMStateInfo vmstate_info_pci_config = {
+ .name = "pci config",
+ .get = get_pci_config_device,
+ .put = put_pci_config_device,
+};
+
+static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size)
+{
+ PCIDevice *s = container_of(pv, PCIDevice, irq_state);
+ uint32_t irq_state[PCI_NUM_PINS];
+ int i;
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ irq_state[i] = qemu_get_be32(f);
+ if (irq_state[i] != 0x1 && irq_state[i] != 0) {
+ fprintf(stderr, "irq state %d: must be 0 or 1.\n",
+ irq_state[i]);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ pci_set_irq_state(s, i, irq_state[i]);
+ }
+
+ return 0;
+}
+
+static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size)
+{
+ int i;
+ PCIDevice *s = container_of(pv, PCIDevice, irq_state);
+
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ qemu_put_be32(f, pci_irq_state(s, i));
+ }
+}
+
+static VMStateInfo vmstate_info_pci_irq_state = {
+ .name = "pci irq state",
+ .get = get_pci_irq_state,
+ .put = put_pci_irq_state,
+};
+
+const VMStateDescription vmstate_pci_device = {
+ .name = "PCIDevice",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_LE(version_id, PCIDevice),
+ VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
+ vmstate_info_pci_config,
+ PCI_CONFIG_SPACE_SIZE),
+ VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
+ vmstate_info_pci_irq_state,
+ PCI_NUM_PINS * sizeof(int32_t)),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+const VMStateDescription vmstate_pcie_device = {
+ .name = "PCIDevice",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_LE(version_id, PCIDevice),
+ VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
+ vmstate_info_pci_config,
+ PCIE_CONFIG_SPACE_SIZE),
+ VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
+ vmstate_info_pci_irq_state,
+ PCI_NUM_PINS * sizeof(int32_t)),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s)
+{
+ return pci_is_express(s) ? &vmstate_pcie_device : &vmstate_pci_device;
+}
+
+void pci_device_save(PCIDevice *s, QEMUFile *f)
+{
+ /* Clear interrupt status bit: it is implicit
+ * in irq_state which we are saving.
+ * This makes us compatible with old devices
+ * which never set or clear this bit. */
+ s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
+ vmstate_save_state(f, pci_get_vmstate(s), s);
+ /* Restore the interrupt status bit. */
+ pci_update_irq_status(s);
+}
+
+int pci_device_load(PCIDevice *s, QEMUFile *f)
+{
+ int ret;
+ ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id);
+ /* Restore the interrupt status bit. */
+ pci_update_irq_status(s);
+ return ret;
+}
+
+static void pci_set_default_subsystem_id(PCIDevice *pci_dev)
+{
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
+ pci_default_sub_vendor_id);
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
+ pci_default_sub_device_id);
+}
+
+/*
+ * Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL
+ * [[<domain>:]<bus>:]<slot>.<func>, return -1 on error
+ */
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+ unsigned int *slotp, unsigned int *funcp)
+{
+ const char *p;
+ char *e;
+ unsigned long val;
+ unsigned long dom = 0, bus = 0;
+ unsigned int slot = 0;
+ unsigned int func = 0;
+
+ p = addr;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ dom = bus;
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ }
+ }
+
+ slot = val;
+
+ if (funcp != NULL) {
+ if (*e != '.')
+ return -1;
+
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+
+ func = val;
+ }
+
+ /* if funcp == NULL func is 0 */
+ if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7)
+ return -1;
+
+ if (*e)
+ return -1;
+
+ /* Note: QEMU doesn't implement domains other than 0 */
+ if (!pci_find_bus(pci_find_root_bus(dom), bus))
+ return -1;
+
+ *domp = dom;
+ *busp = bus;
+ *slotp = slot;
+ if (funcp != NULL)
+ *funcp = func;
+ return 0;
+}
+
+/*
+ * Parse device seg and bdf in device assignment command:
+ *
+ * -pcidevice host=[seg:]bus:dev.func
+ *
+ * Parse [seg:]<bus>:<slot>.<func> return -1 on error
+ */
+int pci_parse_host_devaddr(const char *addr, int *segp, int *busp,
+ int *slotp, int *funcp)
+{
+ const char *p;
+ char *e;
+ int val;
+ int seg = 0, bus = 0, slot = 0, func = 0;
+
+ /* parse optional seg */
+ p = addr;
+ val = 0;
+ while (1) {
+ p = strchr(p, ':');
+ if (p) {
+ val++;
+ p++;
+ } else
+ break;
+ }
+ if (val <= 0 || val > 2)
+ return -1;
+
+ p = addr;
+ if (val == 2) {
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ seg = val;
+ p = e + 1;
+ }
+ } else
+ seg = 0;
+
+
+ /* parse bdf */
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == ':') {
+ bus = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ if (*e == '.') {
+ slot = val;
+ p = e + 1;
+ val = strtoul(p, &e, 16);
+ if (e == p)
+ return -1;
+ func = val;
+ } else
+ return -1;
+ } else
+ return -1;
+
+ if (seg > 0xffff || bus > 0xff || slot > 0x1f || func > 0x7)
+ return -1;
+
+ if (*e)
+ return -1;
+
+ *segp = seg;
+ *busp = bus;
+ *slotp = slot;
+ *funcp = func;
+ return 0;
+}
+
+int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
+ unsigned *slotp)
+{
+ /* strip legacy tag */
+ if (!strncmp(addr, "pci_addr=", 9)) {
+ addr += 9;
+ }
+ if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
+ monitor_printf(mon, "Invalid pci address\n");
+ return -1;
+ }
+ return 0;
+}
+
+PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
+{
+ int dom, bus;
+ unsigned slot;
+
+ if (!devaddr) {
+ *devfnp = -1;
+ return pci_find_bus(pci_find_root_bus(0), 0);
+ }
+
+ if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
+ return NULL;
+ }
+
+ *devfnp = slot << 3;
+ return pci_find_bus(pci_find_root_bus(dom), bus);
+}
+
+static void pci_init_cmask(PCIDevice *dev)
+{
+ pci_set_word(dev->cmask + PCI_VENDOR_ID, 0xffff);
+ pci_set_word(dev->cmask + PCI_DEVICE_ID, 0xffff);
+ dev->cmask[PCI_STATUS] = PCI_STATUS_CAP_LIST;
+ dev->cmask[PCI_REVISION_ID] = 0xff;
+ dev->cmask[PCI_CLASS_PROG] = 0xff;
+ pci_set_word(dev->cmask + PCI_CLASS_DEVICE, 0xffff);
+ dev->cmask[PCI_HEADER_TYPE] = 0xff;
+ dev->cmask[PCI_CAPABILITY_LIST] = 0xff;
+}
+
+static void pci_init_wmask(PCIDevice *dev)
+{
+ int config_size = pci_config_size(dev);
+
+ dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff;
+ dev->wmask[PCI_INTERRUPT_LINE] = 0xff;
+ pci_set_word(dev->wmask + PCI_COMMAND,
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_INTX_DISABLE);
+ if (dev->cap_present & QEMU_PCI_CAP_SERR) {
+ pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR);
+ }
+
+ memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff,
+ config_size - PCI_CONFIG_HEADER_SIZE);
+}
+
+static void pci_init_w1cmask(PCIDevice *dev)
+{
+ /*
+ * Note: It's okay to set w1cmask even for readonly bits as
+ * long as their value is hardwired to 0.
+ */
+ pci_set_word(dev->w1cmask + PCI_STATUS,
+ PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
+ PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY);
+}
+
+static void pci_init_wmask_bridge(PCIDevice *d)
+{
+ /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and
+ PCI_SEC_LETENCY_TIMER */
+ memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4);
+
+ /* base and limit */
+ d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff;
+ d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff;
+ pci_set_word(d->wmask + PCI_MEMORY_BASE,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_set_word(d->wmask + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_set_word(d->wmask + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_set_word(d->wmask + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_MASK & 0xffff);
+
+ /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */
+ memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8);
+
+/* TODO: add this define to pci_regs.h in linux and then in qemu. */
+#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */
+#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */
+#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */
+#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */
+#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */
+ pci_set_word(d->wmask + PCI_BRIDGE_CONTROL,
+ PCI_BRIDGE_CTL_PARITY |
+ PCI_BRIDGE_CTL_SERR |
+ PCI_BRIDGE_CTL_ISA |
+ PCI_BRIDGE_CTL_VGA |
+ PCI_BRIDGE_CTL_VGA_16BIT |
+ PCI_BRIDGE_CTL_MASTER_ABORT |
+ PCI_BRIDGE_CTL_BUS_RESET |
+ PCI_BRIDGE_CTL_FAST_BACK |
+ PCI_BRIDGE_CTL_DISCARD |
+ PCI_BRIDGE_CTL_SEC_DISCARD |
+ PCI_BRIDGE_CTL_DISCARD_SERR);
+ /* Below does not do anything as we never set this bit, put here for
+ * completeness. */
+ pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL,
+ PCI_BRIDGE_CTL_DISCARD_STATUS);
+}
+
+static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev)
+{
+ uint8_t slot = PCI_SLOT(dev->devfn);
+ uint8_t func;
+
+ if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+ dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;
+ }
+
+ /*
+ * multifunction bit is interpreted in two ways as follows.
+ * - all functions must set the bit to 1.
+ * Example: Intel X53
+ * - function 0 must set the bit, but the rest function (> 0)
+ * is allowed to leave the bit to 0.
+ * Example: PIIX3(also in qemu), PIIX4(also in qemu), ICH10,
+ *
+ * So OS (at least Linux) checks the bit of only function 0,
+ * and doesn't see the bit of function > 0.
+ *
+ * The below check allows both interpretation.
+ */
+ if (PCI_FUNC(dev->devfn)) {
+ PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)];
+ if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) {
+ /* function 0 should set multifunction bit */
+ error_report("PCI: single function device can't be populated "
+ "in function %x.%x", slot, PCI_FUNC(dev->devfn));
+ return -1;
+ }
+ return 0;
+ }
+
+ if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+ return 0;
+ }
+ /* function 0 indicates single function, so function > 0 must be NULL */
+ for (func = 1; func < PCI_FUNC_MAX; ++func) {
+ if (bus->devices[PCI_DEVFN(slot, func)]) {
+ error_report("PCI: %x.0 indicates single function, "
+ "but %x.%x is already populated.",
+ slot, slot, func);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void pci_config_alloc(PCIDevice *pci_dev)
+{
+ int config_size = pci_config_size(pci_dev);
+
+ pci_dev->config = qemu_mallocz(config_size);
+ pci_dev->cmask = qemu_mallocz(config_size);
+ pci_dev->wmask = qemu_mallocz(config_size);
+ pci_dev->w1cmask = qemu_mallocz(config_size);
+ pci_dev->config_map = qemu_mallocz(config_size);
+}
+
+static void pci_config_free(PCIDevice *pci_dev)
+{
+ qemu_free(pci_dev->config);
+ qemu_free(pci_dev->cmask);
+ qemu_free(pci_dev->wmask);
+ qemu_free(pci_dev->w1cmask);
+ qemu_free(pci_dev->config_map);
+}
+
+/* -1 for devfn means auto assign */
+static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
+ const char *name, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write,
+ bool is_bridge)
+{
+ if (devfn < 0) {
+ for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
+ devfn += PCI_FUNC_MAX) {
+ if (!bus->devices[devfn])
+ goto found;
+ }
+ error_report("PCI: no slot/function available for %s, all in use", name);
+ return NULL;
+ found: ;
+ } else if (bus->devices[devfn]) {
+ error_report("PCI: slot %d function %d not available for %s, in use by %s",
+ PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name);
+ return NULL;
+ }
+ pci_dev->bus = bus;
+ pci_dev->devfn = devfn;
+ pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
+ pci_dev->irq_state = 0;
+ pci_config_alloc(pci_dev);
+
+ memset(pci_dev->config_map, 0xff, PCI_CONFIG_HEADER_SIZE);
+
+ if (!is_bridge) {
+ pci_set_default_subsystem_id(pci_dev);
+ }
+ pci_init_cmask(pci_dev);
+ pci_init_wmask(pci_dev);
+ pci_init_w1cmask(pci_dev);
+ if (is_bridge) {
+ pci_init_wmask_bridge(pci_dev);
+ }
+ if (pci_init_multifunction(bus, pci_dev)) {
+ pci_config_free(pci_dev);
+ return NULL;
+ }
+
+ if (!config_read)
+ config_read = pci_default_read_config;
+ if (!config_write)
+ config_write = pci_default_write_config;
+ pci_dev->config_read = config_read;
+ pci_dev->config_write = config_write;
+ bus->devices[devfn] = pci_dev;
+ pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, PCI_NUM_PINS);
+ pci_dev->version_id = 2; /* Current pci device vmstate version */
+ return pci_dev;
+}
+
+static void do_pci_unregister_device(PCIDevice *pci_dev)
+{
+ qemu_free_irqs(pci_dev->irq);
+ pci_dev->bus->devices[pci_dev->devfn] = NULL;
+ pci_config_free(pci_dev);
+}
+
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+ int instance_size, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write)
+{
+ PCIDevice *pci_dev;
+
+ pci_dev = qemu_mallocz(instance_size);
+ pci_dev = do_pci_register_device(pci_dev, bus, name, devfn,
+ config_read, config_write,
+ PCI_HEADER_TYPE_NORMAL);
+ if (pci_dev == NULL) {
+ hw_error("PCI: can't register device\n");
+ }
+ return pci_dev;
+}
+
+static target_phys_addr_t pci_to_cpu_addr(PCIBus *bus,
+ target_phys_addr_t addr)
+{
+ return addr + bus->mem_base;
+}
+
+static void pci_unregister_io_regions(PCIDevice *pci_dev)
+{
+ PCIIORegion *r;
+ int i;
+
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &pci_dev->io_regions[i];
+ if (!r->size || r->addr == PCI_BAR_UNMAPPED)
+ continue;
+ if (r->type == PCI_BASE_ADDRESS_SPACE_IO) {
+ isa_unassign_ioport(r->addr, r->filtered_size);
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
+ r->addr),
+ r->filtered_size,
+ IO_MEM_UNASSIGNED);
+ }
+ }
+}
+
+static int pci_unregister_device(DeviceState *dev)
+{
+ PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev);
+ PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->info);
+ int ret = 0;
+
+ if (info->exit)
+ ret = info->exit(pci_dev);
+ if (ret)
+ return ret;
+
+ pci_unregister_io_regions(pci_dev);
+ pci_del_option_rom(pci_dev);
+ qemu_free(pci_dev->romfile);
+ do_pci_unregister_device(pci_dev);
+ return 0;
+}
+
+void pci_register_bar(PCIDevice *pci_dev, int region_num,
+ pcibus_t size, uint8_t type,
+ PCIMapIORegionFunc *map_func)
+{
+ PCIIORegion *r;
+ uint32_t addr;
+ uint64_t wmask;
+
+ assert(region_num >= 0);
+ assert(region_num < PCI_NUM_REGIONS);
+ if (size & (size-1)) {
+ fprintf(stderr, "ERROR: PCI region size must be pow2 "
+ "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size);
+ exit(1);
+ }
+
+ r = &pci_dev->io_regions[region_num];
+ r->addr = PCI_BAR_UNMAPPED;
+ r->size = size;
+ r->filtered_size = size;
+ r->type = type;
+ r->map_func = map_func;
+
+ wmask = ~(size - 1);
+ addr = pci_bar(pci_dev, region_num);
+ if (region_num == PCI_ROM_SLOT) {
+ /* ROM enable bit is writeable */
+ wmask |= PCI_ROM_ADDRESS_ENABLE;
+ }
+ pci_set_long(pci_dev->config + addr, type);
+ if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) &&
+ r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_set_quad(pci_dev->wmask + addr, wmask);
+ pci_set_quad(pci_dev->cmask + addr, ~0ULL);
+ } else {
+ pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
+ pci_set_long(pci_dev->cmask + addr, 0xffffffff);
+ }
+}
+
+static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
+ uint8_t type)
+{
+ pcibus_t base = *addr;
+ pcibus_t limit = *addr + *size - 1;
+ PCIDevice *br;
+
+ for (br = d->bus->parent_dev; br; br = br->bus->parent_dev) {
+ uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ if (!(cmd & PCI_COMMAND_IO)) {
+ goto no_map;
+ }
+ } else {
+ if (!(cmd & PCI_COMMAND_MEMORY)) {
+ goto no_map;
+ }
+ }
+
+ base = MAX(base, pci_bridge_get_base(br, type));
+ limit = MIN(limit, pci_bridge_get_limit(br, type));
+ }
+
+ if (base > limit) {
+ goto no_map;
+ }
+ *addr = base;
+ *size = limit - base + 1;
+ return;
+no_map:
+ *addr = PCI_BAR_UNMAPPED;
+ *size = 0;
+}
+
+static pcibus_t pci_bar_address(PCIDevice *d,
+ int reg, uint8_t type, pcibus_t size)
+{
+ pcibus_t new_addr, last_addr;
+ int bar = pci_bar(d, reg);
+ uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ if (!(cmd & PCI_COMMAND_IO)) {
+ return PCI_BAR_UNMAPPED;
+ }
+ new_addr = pci_get_long(d->config + bar) & ~(size - 1);
+ last_addr = new_addr + size - 1;
+ /* NOTE: we have only 64K ioports on PC */
+ if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) {
+ return PCI_BAR_UNMAPPED;
+ }
+ return new_addr;
+ }
+
+ if (!(cmd & PCI_COMMAND_MEMORY)) {
+ return PCI_BAR_UNMAPPED;
+ }
+ if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ new_addr = pci_get_quad(d->config + bar);
+ } else {
+ new_addr = pci_get_long(d->config + bar);
+ }
+ /* the ROM slot has a specific enable bit */
+ if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) {
+ return PCI_BAR_UNMAPPED;
+ }
+ new_addr &= ~(size - 1);
+ last_addr = new_addr + size - 1;
+ /* NOTE: we do not support wrapping */
+ /* XXX: as we cannot support really dynamic
+ mappings, we handle specific values as invalid
+ mappings. */
+ if (last_addr <= new_addr || new_addr == 0 ||
+ last_addr == PCI_BAR_UNMAPPED) {
+ return PCI_BAR_UNMAPPED;
+ }
+
+ /* Now pcibus_t is 64bit.
+ * Check if 32 bit BAR wraps around explicitly.
+ * Without this, PC ide doesn't work well.
+ * TODO: remove this work around.
+ */
+ if (!(type & PCI_BASE_ADDRESS_MEM_TYPE_64) && last_addr >= UINT32_MAX) {
+ return PCI_BAR_UNMAPPED;
+ }
+
+ /*
+ * OS is allowed to set BAR beyond its addressable
+ * bits. For example, 32 bit OS can set 64bit bar
+ * to >4G. Check it. TODO: we might need to support
+ * it in the future for e.g. PAE.
+ */
+ if (last_addr >= TARGET_PHYS_ADDR_MAX) {
+ return PCI_BAR_UNMAPPED;
+ }
+
+ return new_addr;
+}
+
+static void pci_update_mappings(PCIDevice *d)
+{
+ PCIIORegion *r;
+ int i;
+ pcibus_t new_addr, filtered_size;
+
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+
+ /* this region isn't registered */
+ if (!r->size)
+ continue;
+
+ new_addr = pci_bar_address(d, i, r->type, r->size);
+
+ /* bridge filtering */
+ filtered_size = r->size;
+ if (new_addr != PCI_BAR_UNMAPPED) {
+ pci_bridge_filter(d, &new_addr, &filtered_size, r->type);
+ }
+
+ /* This bar isn't changed */
+ if (new_addr == r->addr && filtered_size == r->filtered_size)
+ continue;
+
+ /* now do the real mapping */
+ if (r->addr != PCI_BAR_UNMAPPED) {
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ int class;
+ /* NOTE: specific hack for IDE in PC case:
+ only one byte must be mapped. */
+ class = pci_get_word(d->config + PCI_CLASS_DEVICE);
+ if (class == 0x0101 && r->size == 4) {
+ isa_unassign_ioport(r->addr + 2, 1);
+ } else {
+ isa_unassign_ioport(r->addr, r->filtered_size);
+ }
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr),
+ r->filtered_size,
+ IO_MEM_UNASSIGNED);
+ qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
+ }
+ }
+ r->addr = new_addr;
+ r->filtered_size = filtered_size;
+ if (r->addr != PCI_BAR_UNMAPPED) {
+ /*
+ * TODO: currently almost all the map funcions assumes
+ * filtered_size == size and addr & ~(size - 1) == addr.
+ * However with bridge filtering, they aren't always true.
+ * Teach them such cases, such that filtered_size < size and
+ * addr & (size - 1) != 0.
+ */
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ r->map_func(d, i, r->addr, r->filtered_size, r->type);
+ } else {
+ r->map_func(d, i, pci_to_cpu_addr(d->bus, r->addr),
+ r->filtered_size, r->type);
+ }
+ }
+ }
+}
+
+static inline int pci_irq_disabled(PCIDevice *d)
+{
+ return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
+}
+
+/* Called after interrupt disabled field update in config space,
+ * assert/deassert interrupts if necessary.
+ * Gets original interrupt disable bit value (before update). */
+static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled)
+{
+ int i, disabled = pci_irq_disabled(d);
+ if (disabled == was_irq_disabled)
+ return;
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ int state = pci_irq_state(d, i);
+ pci_change_irq_level(d, i, disabled ? -state : state);
+ }
+}
+
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len)
+{
+ uint32_t val = 0;
+ assert(len == 1 || len == 2 || len == 4);
+ len = MIN(len, pci_config_size(d) - address);
+ memcpy(&val, d->config + address, len);
+ return le32_to_cpu(val);
+}
+
+void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
+{
+ int i, was_irq_disabled = pci_irq_disabled(d);
+ uint32_t config_size = pci_config_size(d);
+
+ for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) {
+ uint8_t wmask = d->wmask[addr + i];
+ uint8_t w1cmask = d->w1cmask[addr + i];
+ assert(!(wmask & w1cmask));
+ d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
+ d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
+ }
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+ if (kvm_enabled() && kvm_irqchip_in_kernel() &&
+ addr >= PIIX_CONFIG_IRQ_ROUTE &&
+ addr < PIIX_CONFIG_IRQ_ROUTE + 4)
+ assigned_dev_update_irqs();
+#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */
+
+ if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
+ ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
+ ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) ||
+ range_covers_byte(addr, l, PCI_COMMAND))
+ pci_update_mappings(d);
+
+ if (range_covers_byte(addr, l, PCI_COMMAND))
+ pci_update_irq_disabled(d, was_irq_disabled);
+}
+
+/***********************************************************/
+/* generic PCI irq support */
+
+/* 0 <= irq_num <= 3. level must be 0 or 1 */
+static void pci_set_irq(void *opaque, int irq_num, int level)
+{
+ PCIDevice *pci_dev = opaque;
+ int change;
+
+ change = level - pci_irq_state(pci_dev, irq_num);
+ if (!change)
+ return;
+
+#if defined(TARGET_IA64)
+ ioapic_set_irq(pci_dev, irq_num, level);
+#endif
+
+ pci_set_irq_state(pci_dev, irq_num, level);
+ pci_update_irq_status(pci_dev);
+ if (pci_irq_disabled(pci_dev))
+ return;
+ pci_change_irq_level(pci_dev, irq_num, change);
+}
+
+int pci_map_irq(PCIDevice *pci_dev, int pin)
+{
+ return pci_dev->bus->map_irq(pci_dev, pin);
+}
+
+/***********************************************************/
+/* monitor info on PCI */
+
+typedef struct {
+ uint16_t class;
+ const char *desc;
+ const char *fw_name;
+ uint16_t fw_ign_bits;
+} pci_class_desc;
+
+static const pci_class_desc pci_class_descriptions[] =
+{
+ { 0x0001, "VGA controller", "display"},
+ { 0x0100, "SCSI controller", "scsi"},
+ { 0x0101, "IDE controller", "ide"},
+ { 0x0102, "Floppy controller", "fdc"},
+ { 0x0103, "IPI controller", "ipi"},
+ { 0x0104, "RAID controller", "raid"},
+ { 0x0106, "SATA controller"},
+ { 0x0107, "SAS controller"},
+ { 0x0180, "Storage controller"},
+ { 0x0200, "Ethernet controller", "ethernet"},
+ { 0x0201, "Token Ring controller", "token-ring"},
+ { 0x0202, "FDDI controller", "fddi"},
+ { 0x0203, "ATM controller", "atm"},
+ { 0x0280, "Network controller"},
+ { 0x0300, "VGA controller", "display", 0x00ff},
+ { 0x0301, "XGA controller"},
+ { 0x0302, "3D controller"},
+ { 0x0380, "Display controller"},
+ { 0x0400, "Video controller", "video"},
+ { 0x0401, "Audio controller", "sound"},
+ { 0x0402, "Phone"},
+ { 0x0480, "Multimedia controller"},
+ { 0x0500, "RAM controller", "memory"},
+ { 0x0501, "Flash controller", "flash"},
+ { 0x0580, "Memory controller"},
+ { 0x0600, "Host bridge", "host"},
+ { 0x0601, "ISA bridge", "isa"},
+ { 0x0602, "EISA bridge", "eisa"},
+ { 0x0603, "MC bridge", "mca"},
+ { 0x0604, "PCI bridge", "pci"},
+ { 0x0605, "PCMCIA bridge", "pcmcia"},
+ { 0x0606, "NUBUS bridge", "nubus"},
+ { 0x0607, "CARDBUS bridge", "cardbus"},
+ { 0x0608, "RACEWAY bridge"},
+ { 0x0680, "Bridge"},
+ { 0x0700, "Serial port", "serial"},
+ { 0x0701, "Parallel port", "parallel"},
+ { 0x0800, "Interrupt controller", "interrupt-controller"},
+ { 0x0801, "DMA controller", "dma-controller"},
+ { 0x0802, "Timer", "timer"},
+ { 0x0803, "RTC", "rtc"},
+ { 0x0900, "Keyboard", "keyboard"},
+ { 0x0901, "Pen", "pen"},
+ { 0x0902, "Mouse", "mouse"},
+ { 0x0A00, "Dock station", "dock", 0x00ff},
+ { 0x0B00, "i386 cpu", "cpu", 0x00ff},
+ { 0x0c00, "Fireware contorller", "fireware"},
+ { 0x0c01, "Access bus controller", "access-bus"},
+ { 0x0c02, "SSA controller", "ssa"},
+ { 0x0c03, "USB controller", "usb"},
+ { 0x0c04, "Fibre channel controller", "fibre-channel"},
+ { 0, NULL}
+};
+
+static void pci_for_each_device_under_bus(PCIBus *bus,
+ void (*fn)(PCIBus *b, PCIDevice *d))
+{
+ PCIDevice *d;
+ int devfn;
+
+ for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ d = bus->devices[devfn];
+ if (d) {
+ fn(bus, d);
+ }
+ }
+}
+
+void pci_for_each_device(PCIBus *bus, int bus_num,
+ void (*fn)(PCIBus *b, PCIDevice *d))
+{
+ bus = pci_find_bus(bus, bus_num);
+
+ if (bus) {
+ pci_for_each_device_under_bus(bus, fn);
+ }
+}
+
+static void pci_device_print(Monitor *mon, QDict *device)
+{
+ QDict *qdict;
+ QListEntry *entry;
+ uint64_t addr, size;
+
+ monitor_printf(mon, " Bus %2" PRId64 ", ", qdict_get_int(device, "bus"));
+ monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n",
+ qdict_get_int(device, "slot"),
+ qdict_get_int(device, "function"));
+ monitor_printf(mon, " ");
+
+ qdict = qdict_get_qdict(device, "class_info");
+ if (qdict_haskey(qdict, "desc")) {
+ monitor_printf(mon, "%s", qdict_get_str(qdict, "desc"));
+ } else {
+ monitor_printf(mon, "Class %04" PRId64, qdict_get_int(qdict, "class"));
+ }
+
+ qdict = qdict_get_qdict(device, "id");
+ monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
+ qdict_get_int(qdict, "device"),
+ qdict_get_int(qdict, "vendor"));
+
+ if (qdict_haskey(device, "irq")) {
+ monitor_printf(mon, " IRQ %" PRId64 ".\n",
+ qdict_get_int(device, "irq"));
+ }
+
+ if (qdict_haskey(device, "pci_bridge")) {
+ QDict *info;
+
+ qdict = qdict_get_qdict(device, "pci_bridge");
+
+ info = qdict_get_qdict(qdict, "bus");
+ monitor_printf(mon, " BUS %" PRId64 ".\n",
+ qdict_get_int(info, "number"));
+ monitor_printf(mon, " secondary bus %" PRId64 ".\n",
+ qdict_get_int(info, "secondary"));
+ monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
+ qdict_get_int(info, "subordinate"));
+
+ info = qdict_get_qdict(qdict, "io_range");
+ monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
+ qdict_get_int(info, "base"),
+ qdict_get_int(info, "limit"));
+
+ info = qdict_get_qdict(qdict, "memory_range");
+ monitor_printf(mon,
+ " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
+ qdict_get_int(info, "base"),
+ qdict_get_int(info, "limit"));
+
+ info = qdict_get_qdict(qdict, "prefetchable_range");
+ monitor_printf(mon, " prefetchable memory range "
+ "[0x%08"PRIx64", 0x%08"PRIx64"]\n",
+ qdict_get_int(info, "base"),
+ qdict_get_int(info, "limit"));
+ }
+
+ QLIST_FOREACH_ENTRY(qdict_get_qlist(device, "regions"), entry) {
+ qdict = qobject_to_qdict(qlist_entry_obj(entry));
+ monitor_printf(mon, " BAR%d: ", (int) qdict_get_int(qdict, "bar"));
+
+ addr = qdict_get_int(qdict, "address");
+ size = qdict_get_int(qdict, "size");
+
+ if (!strcmp(qdict_get_str(qdict, "type"), "io")) {
+ monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS
+ " [0x%04"FMT_PCIBUS"].\n",
+ addr, addr + size - 1);
+ } else {
+ monitor_printf(mon, "%d bit%s memory at 0x%08"FMT_PCIBUS
+ " [0x%08"FMT_PCIBUS"].\n",
+ qdict_get_bool(qdict, "mem_type_64") ? 64 : 32,
+ qdict_get_bool(qdict, "prefetch") ?
+ " prefetchable" : "", addr, addr + size - 1);
+ }
+ }
+
+ monitor_printf(mon, " id \"%s\"\n", qdict_get_str(device, "qdev_id"));
+
+ if (qdict_haskey(device, "pci_bridge")) {
+ qdict = qdict_get_qdict(device, "pci_bridge");
+ if (qdict_haskey(qdict, "devices")) {
+ QListEntry *dev;
+ QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) {
+ pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev)));
+ }
+ }
+ }
+}
+
+void do_pci_info_print(Monitor *mon, const QObject *data)
+{
+ QListEntry *bus, *dev;
+
+ QLIST_FOREACH_ENTRY(qobject_to_qlist(data), bus) {
+ QDict *qdict = qobject_to_qdict(qlist_entry_obj(bus));
+ QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) {
+ pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev)));
+ }
+ }
+}
+
+static QObject *pci_get_dev_class(const PCIDevice *dev)
+{
+ int class;
+ const pci_class_desc *desc;
+
+ class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
+ desc = pci_class_descriptions;
+ while (desc->desc && class != desc->class)
+ desc++;
+
+ if (desc->desc) {
+ return qobject_from_jsonf("{ 'desc': %s, 'class': %d }",
+ desc->desc, class);
+ } else {
+ return qobject_from_jsonf("{ 'class': %d }", class);
+ }
+}
+
+static QObject *pci_get_dev_id(const PCIDevice *dev)
+{
+ return qobject_from_jsonf("{ 'device': %d, 'vendor': %d }",
+ pci_get_word(dev->config + PCI_VENDOR_ID),
+ pci_get_word(dev->config + PCI_DEVICE_ID));
+}
+
+static QObject *pci_get_regions_list(const PCIDevice *dev)
+{
+ int i;
+ QList *regions_list;
+
+ regions_list = qlist_new();
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ QObject *obj;
+ const PCIIORegion *r = &dev->io_regions[i];
+
+ if (!r->size) {
+ continue;
+ }
+
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'io', "
+ "'address': %" PRId64 ", "
+ "'size': %" PRId64 " }",
+ i, r->addr, r->size);
+ } else {
+ int mem_type_64 = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+ obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'memory', "
+ "'mem_type_64': %i, 'prefetch': %i, "
+ "'address': %" PRId64 ", "
+ "'size': %" PRId64 " }",
+ i, mem_type_64,
+ r->type & PCI_BASE_ADDRESS_MEM_PREFETCH,
+ r->addr, r->size);
+ }
+
+ qlist_append_obj(regions_list, obj);
+ }
+
+ return QOBJECT(regions_list);
+}
+
+static QObject *pci_get_devices_list(PCIBus *bus, int bus_num);
+
+static QObject *pci_get_dev_dict(PCIDevice *dev, PCIBus *bus, int bus_num)
+{
+ uint8_t type;
+ QObject *obj;
+
+ obj = qobject_from_jsonf("{ 'bus': %d, 'slot': %d, 'function': %d," "'class_info': %p, 'id': %p, 'regions': %p,"
+ " 'qdev_id': %s }",
+ bus_num,
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ pci_get_dev_class(dev), pci_get_dev_id(dev),
+ pci_get_regions_list(dev),
+ dev->qdev.id ? dev->qdev.id : "");
+
+ if (dev->config[PCI_INTERRUPT_PIN] != 0) {
+ QDict *qdict = qobject_to_qdict(obj);
+ qdict_put(qdict, "irq", qint_from_int(dev->config[PCI_INTERRUPT_LINE]));
+ }
+
+ type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+ if (type == PCI_HEADER_TYPE_BRIDGE) {
+ QDict *qdict;
+ QObject *pci_bridge;
+
+ pci_bridge = qobject_from_jsonf("{ 'bus': "
+ "{ 'number': %d, 'secondary': %d, 'subordinate': %d }, "
+ "'io_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, "
+ "'memory_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, "
+ "'prefetchable_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "} }",
+ dev->config[PCI_PRIMARY_BUS], dev->config[PCI_SECONDARY_BUS],
+ dev->config[PCI_SUBORDINATE_BUS],
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO),
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO),
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY),
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY),
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH),
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH));
+
+ if (dev->config[PCI_SECONDARY_BUS] != 0) {
+ PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]);
+
+ if (child_bus) {
+ qdict = qobject_to_qdict(pci_bridge);
+ qdict_put_obj(qdict, "devices",
+ pci_get_devices_list(child_bus,
+ dev->config[PCI_SECONDARY_BUS]));
+ }
+ }
+ qdict = qobject_to_qdict(obj);
+ qdict_put_obj(qdict, "pci_bridge", pci_bridge);
+ }
+
+ return obj;
+}
+
+static QObject *pci_get_devices_list(PCIBus *bus, int bus_num)
+{
+ int devfn;
+ PCIDevice *dev;
+ QList *dev_list;
+
+ dev_list = qlist_new();
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ dev = bus->devices[devfn];
+ if (dev) {
+ qlist_append_obj(dev_list, pci_get_dev_dict(dev, bus, bus_num));
+ }
+ }
+
+ return QOBJECT(dev_list);
+}
+
+static QObject *pci_get_bus_dict(PCIBus *bus, int bus_num)
+{
+ bus = pci_find_bus(bus, bus_num);
+ if (bus) {
+ return qobject_from_jsonf("{ 'bus': %d, 'devices': %p }",
+ bus_num, pci_get_devices_list(bus, bus_num));
+ }
+
+ return NULL;
+}
+
+void do_pci_info(Monitor *mon, QObject **ret_data)
+{
+ QList *bus_list;
+ struct PCIHostBus *host;
+
+ bus_list = qlist_new();
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ QObject *obj = pci_get_bus_dict(host->bus, 0);
+ if (obj) {
+ qlist_append_obj(bus_list, obj);
+ }
+ }
+
+ *ret_data = QOBJECT(bus_list);
+}
+
+static const char * const pci_nic_models[] = {
+ "ne2k_pci",
+ "i82551",
+ "i82557b",
+ "i82559er",
+ "rtl8139",
+ "e1000",
+ "pcnet",
+ "virtio",
+ NULL
+};
+
+static const char * const pci_nic_names[] = {
+ "ne2k_pci",
+ "i82551",
+ "i82557b",
+ "i82559er",
+ "rtl8139",
+ "e1000",
+ "pcnet",
+ "virtio-net-pci",
+ NULL
+};
+
+/* Initialize a PCI NIC. */
+/* FIXME callers should check for failure, but don't */
+PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
+ const char *default_devaddr)
+{
+ const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
+ PCIBus *bus;
+ int devfn;
+ PCIDevice *pci_dev;
+ DeviceState *dev;
+ int i;
+
+ i = qemu_find_nic_model(nd, pci_nic_models, default_model);
+ if (i < 0)
+ return NULL;
+
+ bus = pci_get_bus_devfn(&devfn, devaddr);
+ if (!bus) {
+ error_report("Invalid PCI device address %s for device %s",
+ devaddr, pci_nic_names[i]);
+ return NULL;
+ }
+
+ pci_dev = pci_create(bus, devfn, pci_nic_names[i]);
+ dev = &pci_dev->qdev;
+ qdev_set_nic_properties(dev, nd);
+ if (qdev_init(dev) < 0)
+ return NULL;
+ return pci_dev;
+}
+
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
+ const char *default_devaddr)
+{
+ PCIDevice *res;
+
+ if (qemu_show_nic_models(nd->model, pci_nic_models))
+ exit(0);
+
+ res = pci_nic_init(nd, default_model, default_devaddr);
+ if (!res)
+ exit(1);
+ return res;
+}
+
+static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d)
+{
+ pci_update_mappings(d);
+}
+
+void pci_bridge_update_mappings(PCIBus *b)
+{
+ PCIBus *child;
+
+ pci_for_each_device_under_bus(b, pci_bridge_update_mappings_fn);
+
+ QLIST_FOREACH(child, &b->child, sibling) {
+ pci_bridge_update_mappings(child);
+ }
+}
+
+/* Whether a given bus number is in range of the secondary
+ * bus of the given bridge device. */
+static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num)
+{
+ return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) &
+ PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ &&
+ dev->config[PCI_SECONDARY_BUS] < bus_num &&
+ bus_num <= dev->config[PCI_SUBORDINATE_BUS];
+}
+
+PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
+{
+ PCIBus *sec;
+
+ if (!bus) {
+ return NULL;
+ }
+
+ if (pci_bus_num(bus) == bus_num) {
+ return bus;
+ }
+
+ /* Consider all bus numbers in range for the host pci bridge. */
+ if (bus->parent_dev &&
+ !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) {
+ return NULL;
+ }
+
+ /* try child bus */
+ for (; bus; bus = sec) {
+ QLIST_FOREACH(sec, &bus->child, sibling) {
+ assert(sec->parent_dev);
+ if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) {
+ return sec;
+ }
+ if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) {
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function)
+{
+ bus = pci_find_bus(bus, bus_num);
+
+ if (!bus)
+ return NULL;
+
+ return bus->devices[PCI_DEVFN(slot, function)];
+}
+
+static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ PCIDevice *pci_dev = (PCIDevice *)qdev;
+ PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev);
+ PCIBus *bus;
+ int devfn, rc;
+ bool is_default_rom;
+
+ /* initialize cap_present for pci_is_express() and pci_config_size() */
+ if (info->is_express) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
+ bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev));
+ devfn = pci_dev->devfn;
+ pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn,
+ info->config_read, info->config_write,
+ info->is_bridge);
+ if (pci_dev == NULL)
+ return -1;
+ if (qdev->hotplugged && info->no_hotplug) {
+ qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
+ do_pci_unregister_device(pci_dev);
+ return -1;
+ }
+ rc = info->init(pci_dev);
+ if (rc != 0) {
+ do_pci_unregister_device(pci_dev);
+ return rc;
+ }
+
+ /* rom loading */
+ is_default_rom = false;
+ if (pci_dev->romfile == NULL && info->romfile != NULL) {
+ pci_dev->romfile = qemu_strdup(info->romfile);
+ is_default_rom = true;
+ }
+ pci_add_option_rom(pci_dev, is_default_rom);
+
+ if (bus->hotplug) {
+ /* Let buses differentiate between hotplug and when device is
+ * enabled during qemu machine creation. */
+ rc = bus->hotplug(bus->hotplug_qdev, pci_dev,
+ qdev->hotplugged ? PCI_HOTPLUG_ENABLED:
+ PCI_COLDPLUG_ENABLED);
+ if (rc != 0) {
+ int r = pci_unregister_device(&pci_dev->qdev);
+ assert(!r);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int pci_unplug_device(DeviceState *qdev)
+{
+ PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
+ PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
+
+ if (info->no_hotplug) {
+ qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name);
+ return -1;
+ }
+ return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
+ PCI_HOTPLUG_DISABLED);
+}
+
+void pci_qdev_register(PCIDeviceInfo *info)
+{
+ info->qdev.init = pci_qdev_init;
+ info->qdev.unplug = pci_unplug_device;
+ info->qdev.exit = pci_unregister_device;
+ info->qdev.bus_info = &pci_bus_info;
+ qdev_register(&info->qdev);
+}
+
+void pci_qdev_register_many(PCIDeviceInfo *info)
+{
+ while (info->qdev.name) {
+ pci_qdev_register(info);
+ info++;
+ }
+}
+
+PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
+ const char *name)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(&bus->qbus, name);
+ qdev_prop_set_uint32(dev, "addr", devfn);
+ qdev_prop_set_bit(dev, "multifunction", multifunction);
+ return DO_UPCAST(PCIDevice, qdev, dev);
+}
+
+PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn,
+ bool multifunction,
+ const char *name)
+{
+ PCIDevice *dev = pci_create_multifunction(bus, devfn, multifunction, name);
+ qdev_init_nofail(&dev->qdev);
+ return dev;
+}
+
+PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name)
+{
+ return pci_create_multifunction(bus, devfn, false, name);
+}
+
+PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name)
+{
+ return pci_create_simple_multifunction(bus, devfn, false, name);
+}
+
+static int pci_find_space(PCIDevice *pdev, uint8_t size)
+{
+ int config_size = pci_config_size(pdev);
+ int offset = PCI_CONFIG_HEADER_SIZE;
+ int i;
+ for (i = PCI_CONFIG_HEADER_SIZE; i < config_size; ++i)
+ if (pdev->config_map[i])
+ offset = i + 1;
+ else if (i - offset + 1 == size)
+ return offset;
+ return 0;
+}
+
+static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id,
+ uint8_t *prev_p)
+{
+ uint8_t next, prev;
+
+ if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]);
+ prev = next + PCI_CAP_LIST_NEXT)
+ if (pdev->config[next + PCI_CAP_LIST_ID] == cap_id)
+ break;
+
+ if (prev_p)
+ *prev_p = prev;
+ return next;
+}
+
+void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, pcibus_t size, int type)
+{
+ cpu_register_physical_memory(addr, size, pdev->rom_offset);
+}
+
+/* Patch the PCI vendor and device ids in a PCI rom image if necessary.
+ This is needed for an option rom which is used for more than one device. */
+static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size)
+{
+ uint16_t vendor_id;
+ uint16_t device_id;
+ uint16_t rom_vendor_id;
+ uint16_t rom_device_id;
+ uint16_t rom_magic;
+ uint16_t pcir_offset;
+ uint8_t checksum;
+
+ /* Words in rom data are little endian (like in PCI configuration),
+ so they can be read / written with pci_get_word / pci_set_word. */
+
+ /* Only a valid rom will be patched. */
+ rom_magic = pci_get_word(ptr);
+ if (rom_magic != 0xaa55) {
+ PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic);
+ return;
+ }
+ pcir_offset = pci_get_word(ptr + 0x18);
+ if (pcir_offset + 8 >= size || memcmp(ptr + pcir_offset, "PCIR", 4)) {
+ PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset);
+ return;
+ }
+
+ vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
+ device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
+ rom_vendor_id = pci_get_word(ptr + pcir_offset + 4);
+ rom_device_id = pci_get_word(ptr + pcir_offset + 6);
+
+ PCI_DPRINTF("%s: ROM id %04x%04x / PCI id %04x%04x\n", pdev->romfile,
+ vendor_id, device_id, rom_vendor_id, rom_device_id);
+
+ checksum = ptr[6];
+
+ if (vendor_id != rom_vendor_id) {
+ /* Patch vendor id and checksum (at offset 6 for etherboot roms). */
+ checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id >> 8);
+ checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id >> 8);
+ PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum);
+ ptr[6] = checksum;
+ pci_set_word(ptr + pcir_offset + 4, vendor_id);
+ }
+
+ if (device_id != rom_device_id) {
+ /* Patch device id and checksum (at offset 6 for etherboot roms). */
+ checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id >> 8);
+ checksum -= (uint8_t)device_id + (uint8_t)(device_id >> 8);
+ PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum);
+ ptr[6] = checksum;
+ pci_set_word(ptr + pcir_offset + 6, device_id);
+ }
+}
+
+/* Add an option rom for the device */
+static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom)
+{
+ int size;
+ char *path;
+ void *ptr;
+ char name[32];
+
+ if (!pdev->romfile)
+ return 0;
+ if (strlen(pdev->romfile) == 0)
+ return 0;
+
+ if (!pdev->rom_bar) {
+ /*
+ * Load rom via fw_cfg instead of creating a rom bar,
+ * for 0.11 compatibility.
+ */
+ int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
+ if (class == 0x0300) {
+ rom_add_vga(pdev->romfile);
+ } else {
+ rom_add_option(pdev->romfile, -1);
+ }
+ return 0;
+ }
+
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile);
+ if (path == NULL) {
+ path = qemu_strdup(pdev->romfile);
+ }
+
+ size = get_image_size(path);
+ if (size < 0) {
+ error_report("%s: failed to find romfile \"%s\"",
+ __FUNCTION__, pdev->romfile);
+ return -1;
+ }
+ if (size & (size - 1)) {
+ size = 1 << qemu_fls(size);
+ }
+
+ if (pdev->qdev.info->vmsd)
+ snprintf(name, sizeof(name), "%s.rom", pdev->qdev.info->vmsd->name);
+ else
+ snprintf(name, sizeof(name), "%s.rom", pdev->qdev.info->name);
+ pdev->rom_offset = qemu_ram_alloc(&pdev->qdev, name, size);
+
+ ptr = qemu_get_ram_ptr(pdev->rom_offset);
+ load_image(path, ptr);
+ qemu_free(path);
+
+ if (is_default_rom) {
+ /* Only the default rom images will be patched (if needed). */
+ pci_patch_ids(pdev, ptr, size);
+ }
+
+ pci_register_bar(pdev, PCI_ROM_SLOT, size,
+ 0, pci_map_option_rom);
+
+ return 0;
+}
+
+static void pci_del_option_rom(PCIDevice *pdev)
+{
+ if (!pdev->rom_offset)
+ return;
+
+ qemu_ram_free(pdev->rom_offset);
+ pdev->rom_offset = 0;
+}
+
+/*
+ * if !offset
+ * Reserve space and add capability to the linked list in pci config space
+ *
+ * if offset = 0,
+ * Find and reserve space and add capability to the linked list
+ * in pci config space */
+int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
+ uint8_t offset, uint8_t size)
+{
+ uint8_t *config;
+ if (!offset) {
+ offset = pci_find_space(pdev, size);
+ if (!offset) {
+ return -ENOSPC;
+ }
+ } else {
+ int i;
+
+ for (i = offset; i < offset + size; i++) {
+ if (pdev->config_map[i]) {
+ fprintf(stderr, "ERROR: %04x:%02x:%02x.%x "
+ "Attempt to add PCI capability %x at offset "
+ "%x overlaps existing capability %x at offset %x\n",
+ pci_find_domain(pdev->bus), pci_bus_num(pdev->bus),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ cap_id, offset, pdev->config_map[i], i);
+ return -EINVAL;
+ }
+ }
+ }
+
+ config = pdev->config + offset;
+ config[PCI_CAP_LIST_ID] = cap_id;
+ config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST];
+ pdev->config[PCI_CAPABILITY_LIST] = offset;
+ memset(pdev->config_map + offset, cap_id, size);
+ /* Make capability read-only by default */
+ memset(pdev->wmask + offset, 0, size);
+ /* Check capability by default */
+ memset(pdev->cmask + offset, 0xFF, size);
+
+ pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
+
+ return offset;
+}
+
+/* Unlink capability from the pci config space. */
+void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
+{
+ uint8_t prev, offset = pci_find_capability_list(pdev, cap_id, &prev);
+ if (!offset)
+ return;
+ pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT];
+ /* Make capability writeable again */
+ memset(pdev->wmask + offset, 0xff, size);
+ memset(pdev->w1cmask + offset, 0, size);
+ /* Clear cmask as device-specific registers can't be checked */
+ memset(pdev->cmask + offset, 0, size);
+ memset(pdev->config_map + offset, 0, size);
+
+ if (!pdev->config[PCI_CAPABILITY_LIST]) {
+ pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST;
+ }
+}
+
+uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id)
+{
+ return pci_find_capability_list(pdev, cap_id, NULL);
+}
+
+static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ PCIDevice *d = (PCIDevice *)dev;
+ const pci_class_desc *desc;
+ char ctxt[64];
+ PCIIORegion *r;
+ int i, class;
+
+ class = pci_get_word(d->config + PCI_CLASS_DEVICE);
+ desc = pci_class_descriptions;
+ while (desc->desc && class != desc->class)
+ desc++;
+ if (desc->desc) {
+ snprintf(ctxt, sizeof(ctxt), "%s", desc->desc);
+ } else {
+ snprintf(ctxt, sizeof(ctxt), "Class %04x", class);
+ }
+
+ monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, "
+ "pci id %04x:%04x (sub %04x:%04x)\n",
+ indent, "", ctxt, pci_bus_num(d->bus),
+ PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+ pci_get_word(d->config + PCI_VENDOR_ID),
+ pci_get_word(d->config + PCI_DEVICE_ID),
+ pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID),
+ pci_get_word(d->config + PCI_SUBSYSTEM_ID));
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[i];
+ if (!r->size)
+ continue;
+ monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS
+ " [0x%"FMT_PCIBUS"]\n",
+ indent, "",
+ i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem",
+ r->addr, r->addr + r->size - 1);
+ }
+}
+
+static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len)
+{
+ PCIDevice *d = (PCIDevice *)dev;
+ const char *name = NULL;
+ const pci_class_desc *desc = pci_class_descriptions;
+ int class = pci_get_word(d->config + PCI_CLASS_DEVICE);
+
+ while (desc->desc &&
+ (class & ~desc->fw_ign_bits) !=
+ (desc->class & ~desc->fw_ign_bits)) {
+ desc++;
+ }
+
+ if (desc->desc) {
+ name = desc->fw_name;
+ }
+
+ if (name) {
+ pstrcpy(buf, len, name);
+ } else {
+ snprintf(buf, len, "pci%04x,%04x",
+ pci_get_word(d->config + PCI_VENDOR_ID),
+ pci_get_word(d->config + PCI_DEVICE_ID));
+ }
+
+ return buf;
+}
+
+static char *pcibus_get_fw_dev_path(DeviceState *dev)
+{
+ PCIDevice *d = (PCIDevice *)dev;
+ char path[50], name[33];
+ int off;
+
+ off = snprintf(path, sizeof(path), "%s@%x",
+ pci_dev_fw_name(dev, name, sizeof name),
+ PCI_SLOT(d->devfn));
+ if (PCI_FUNC(d->devfn))
+ snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn));
+ return strdup(path);
+}
+
+static char *pcibus_get_dev_path(DeviceState *dev)
+{
+ PCIDevice *d = container_of(dev, PCIDevice, qdev);
+ PCIDevice *t;
+ int slot_depth;
+ /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function.
+ * 00 is added here to make this format compatible with
+ * domain:Bus:Slot.Func for systems without nested PCI bridges.
+ * Slot.Function list specifies the slot and function numbers for all
+ * devices on the path from root to the specific device. */
+ char domain[] = "DDDD:00";
+ char slot[] = ":SS.F";
+ int domain_len = sizeof domain - 1 /* For '\0' */;
+ int slot_len = sizeof slot - 1 /* For '\0' */;
+ int path_len;
+ char *path, *p;
+ int s;
+
+ /* Calculate # of slots on path between device and root. */;
+ slot_depth = 0;
+ for (t = d; t; t = t->bus->parent_dev) {
+ ++slot_depth;
+ }
+
+ path_len = domain_len + slot_len * slot_depth;
+
+ /* Allocate memory, fill in the terminating null byte. */
+ path = qemu_malloc(path_len + 1 /* For '\0' */);
+ path[path_len] = '\0';
+
+ /* First field is the domain. */
+ s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus));
+ assert(s == domain_len);
+ memcpy(path, domain, domain_len);
+
+ /* Fill in slot numbers. We walk up from device to root, so need to print
+ * them in the reverse order, last to first. */
+ p = path + path_len;
+ for (t = d; t; t = t->bus->parent_dev) {
+ p -= slot_len;
+ s = snprintf(slot, sizeof slot, ":%02x.%x",
+ PCI_SLOT(t->devfn), PCI_FUNC(t->devfn));
+ assert(s == slot_len);
+ memcpy(p, slot, slot_len);
+ }
+
+ return path;
+}
+
+static int pci_qdev_find_recursive(PCIBus *bus,
+ const char *id, PCIDevice **pdev)
+{
+ DeviceState *qdev = qdev_find_recursive(&bus->qbus, id);
+ if (!qdev) {
+ return -ENODEV;
+ }
+
+ /* roughly check if given qdev is pci device */
+ if (qdev->info->init == &pci_qdev_init &&
+ qdev->parent_bus->info == &pci_bus_info) {
+ *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int pci_qdev_find_device(const char *id, PCIDevice **pdev)
+{
+ struct PCIHostBus *host;
+ int rc = -ENODEV;
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ int tmp = pci_qdev_find_recursive(host->bus, id, pdev);
+ if (!tmp) {
+ rc = 0;
+ break;
+ }
+ if (tmp != -ENODEV) {
+ rc = tmp;
+ }
+ }
+
+ return rc;
+}
diff --git a/hw/pci.h b/hw/pci.h
new file mode 100644
index 0000000..140602b
--- /dev/null
+++ b/hw/pci.h
@@ -0,0 +1,496 @@
+#ifndef QEMU_PCI_H
+#define QEMU_PCI_H
+
+#include "qemu-common.h"
+#include "qobject.h"
+
+#include "qdev.h"
+
+struct kvm_irq_routing_entry;
+
+/* PCI includes legacy ISA access. */
+#include "isa.h"
+
+#include "pcie.h"
+
+/* PCI bus */
+
+#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+#define PCI_FUNC_MAX 8
+
+/* Class, Vendor and Device IDs from Linux's pci_ids.h */
+#include "pci_ids.h"
+
+/* QEMU-specific Vendor and Device ID definitions */
+
+/* IBM (0x1014) */
+#define PCI_DEVICE_ID_IBM_440GX 0x027f
+#define PCI_DEVICE_ID_IBM_OPENPIC2 0xffff
+
+/* Hitachi (0x1054) */
+#define PCI_VENDOR_ID_HITACHI 0x1054
+#define PCI_DEVICE_ID_HITACHI_SH7751R 0x350e
+
+/* Apple (0x106b) */
+#define PCI_DEVICE_ID_APPLE_343S1201 0x0010
+#define PCI_DEVICE_ID_APPLE_UNI_N_I_PCI 0x001e
+#define PCI_DEVICE_ID_APPLE_UNI_N_PCI 0x001f
+#define PCI_DEVICE_ID_APPLE_UNI_N_KEYL 0x0022
+#define PCI_DEVICE_ID_APPLE_IPID_USB 0x003f
+
+/* Realtek (0x10ec) */
+#define PCI_DEVICE_ID_REALTEK_8029 0x8029
+
+/* Xilinx (0x10ee) */
+#define PCI_DEVICE_ID_XILINX_XC2VP30 0x0300
+
+/* Marvell (0x11ab) */
+#define PCI_DEVICE_ID_MARVELL_GT6412X 0x4620
+
+/* QEMU/Bochs VGA (0x1234) */
+#define PCI_VENDOR_ID_QEMU 0x1234
+#define PCI_DEVICE_ID_QEMU_VGA 0x1111
+
+/* VMWare (0x15ad) */
+#define PCI_VENDOR_ID_VMWARE 0x15ad
+#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
+#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710
+#define PCI_DEVICE_ID_VMWARE_NET 0x0720
+#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730
+#define PCI_DEVICE_ID_VMWARE_IDE 0x1729
+
+/* Intel (0x8086) */
+#define PCI_DEVICE_ID_INTEL_82551IT 0x1209
+#define PCI_DEVICE_ID_INTEL_82557 0x1229
+#define PCI_DEVICE_ID_INTEL_82801IR 0x2922
+
+/* Red Hat / Qumranet (for QEMU) -- see pci-ids.txt */
+#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4
+#define PCI_SUBVENDOR_ID_REDHAT_QUMRANET 0x1af4
+#define PCI_SUBDEVICE_ID_QEMU 0x1100
+
+#define PCI_DEVICE_ID_VIRTIO_NET 0x1000
+#define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001
+#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
+#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003
+
+#define FMT_PCIBUS PRIx64
+
+typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
+ uint32_t address, uint32_t data, int len);
+typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
+ uint32_t address, int len);
+typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type);
+typedef int PCIUnregisterFunc(PCIDevice *pci_dev);
+
+typedef struct PCIIORegion {
+ pcibus_t addr; /* current PCI mapping address. -1 means not mapped */
+#define PCI_BAR_UNMAPPED (~(pcibus_t)0)
+ pcibus_t size;
+ pcibus_t filtered_size;
+ uint8_t type;
+ PCIMapIORegionFunc *map_func;
+} PCIIORegion;
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+
+#include "pci_regs.h"
+
+/* PCI HEADER_TYPE */
+#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80
+
+/* Size of the standard PCI config header */
+#define PCI_CONFIG_HEADER_SIZE 0x40
+/* Size of the standard PCI config space */
+#define PCI_CONFIG_SPACE_SIZE 0x100
+/* Size of the standart PCIe config space: 4KB */
+#define PCIE_CONFIG_SPACE_SIZE 0x1000
+
+#define PCI_NUM_PINS 4 /* A-D */
+
+/* Bits in cap_present field. */
+enum {
+ QEMU_PCI_CAP_MSI = 0x1,
+ QEMU_PCI_CAP_MSIX = 0x2,
+ QEMU_PCI_CAP_EXPRESS = 0x4,
+
+ /* multifunction capable device */
+#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3
+ QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR),
+
+ /* command register SERR bit enabled */
+#define QEMU_PCI_CAP_SERR_BITNR 4
+ QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR),
+};
+
+typedef int (*msix_mask_notifier_func)(PCIDevice *, unsigned vector,
+ int masked);
+
+struct kvm_msix_message {
+ uint32_t gsi;
+ uint32_t addr_lo;
+ uint32_t addr_hi;
+ uint32_t data;
+};
+
+struct PCIDevice {
+ DeviceState qdev;
+ /* PCI config space */
+ uint8_t *config;
+
+ /* Used to enable config checks on load. Note that writeable bits are
+ * never checked even if set in cmask. */
+ uint8_t *cmask;
+
+ /* Used to implement R/W bytes */
+ uint8_t *wmask;
+
+ /* Used to implement RW1C(Write 1 to Clear) bytes */
+ uint8_t *w1cmask;
+
+ /* Used to allocate config space and track capabilities. */
+ uint8_t *config_map;
+
+ /* the following fields are read only */
+ PCIBus *bus;
+ uint32_t devfn;
+ char name[64];
+ PCIIORegion io_regions[PCI_NUM_REGIONS];
+
+ /* do not access the following fields */
+ PCIConfigReadFunc *config_read;
+ PCIConfigWriteFunc *config_write;
+
+ /* IRQ objects for the INTA-INTD pins. */
+ qemu_irq *irq;
+
+ /* Current IRQ levels. Used internally by the generic PCI code. */
+ uint8_t irq_state;
+
+ /* Capability bits */
+ uint32_t cap_present;
+
+ /* Offset of MSI-X capability in config space */
+ uint8_t msix_cap;
+
+ /* MSI-X entries */
+ int msix_entries_nr;
+
+ /* Space to store MSIX table */
+ uint8_t *msix_table_page;
+ /* MMIO index used to map MSIX table and pending bit entries. */
+ int msix_mmio_index;
+ /* Reference-count for entries actually in use by driver. */
+ unsigned *msix_entry_used;
+ /* Region including the MSI-X table */
+ uint32_t msix_bar_size;
+ /* Version id needed for VMState */
+ int32_t version_id;
+
+ /* Offset of MSI capability in config space */
+ uint8_t msi_cap;
+
+ /* PCI Express */
+ PCIExpressDevice exp;
+
+ /* Location of option rom */
+ char *romfile;
+ ram_addr_t rom_offset;
+ uint32_t rom_bar;
+
+ /* How much space does an MSIX table need. */
+ /* The spec requires giving the table structure
+ * a 4K aligned region all by itself. Align it to
+ * target pages so that drivers can do passthrough
+ * on the rest of the region. */
+ target_phys_addr_t msix_page_size;
+
+ struct kvm_msix_message *msix_irq_entries;
+
+ msix_mask_notifier_func msix_mask_notifier;
+};
+
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+ int instance_size, int devfn,
+ PCIConfigReadFunc *config_read,
+ PCIConfigWriteFunc *config_write);
+
+void pci_register_bar(PCIDevice *pci_dev, int region_num,
+ pcibus_t size, uint8_t type,
+ PCIMapIORegionFunc *map_func);
+
+void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr,
+ pcibus_t size, int type);
+
+int pci_map_irq(PCIDevice *pci_dev, int pin);
+
+int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
+ uint8_t offset, uint8_t size);
+
+void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
+
+uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id);
+
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len);
+void pci_default_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len);
+void pci_device_save(PCIDevice *s, QEMUFile *f);
+int pci_device_load(PCIDevice *s, QEMUFile *f);
+typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level);
+typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
+
+typedef enum {
+ PCI_HOTPLUG_DISABLED,
+ PCI_HOTPLUG_ENABLED,
+ PCI_COLDPLUG_ENABLED,
+} PCIHotplugState;
+
+typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev,
+ PCIHotplugState state);
+void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
+ const char *name, int devfn_min);
+PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min);
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ void *irq_opaque, int nirq);
+void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
+PCIBus *pci_register_bus(DeviceState *parent, const char *name,
+ pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ void *irq_opaque, int devfn_min, int nirq);
+void pci_device_reset(PCIDevice *dev);
+void pci_bus_reset(PCIBus *bus);
+
+void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base);
+
+PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
+ const char *default_devaddr);
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
+ const char *default_devaddr);
+int pci_bus_num(PCIBus *s);
+void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d));
+PCIBus *pci_find_root_bus(int domain);
+int pci_find_domain(const PCIBus *bus);
+PCIBus *pci_find_bus(PCIBus *bus, int bus_num);
+PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function);
+int pci_qdev_find_device(const char *id, PCIDevice **pdev);
+PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
+
+int pci_parse_devaddr(const char *addr, int *domp, int *busp,
+ unsigned int *slotp, unsigned int *funcp);
+int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
+ unsigned *slotp);
+
+int pci_parse_host_devaddr(const char *addr, int *segp, int *busp,
+ int *slotp, int *funcp);
+
+void do_pci_info_print(Monitor *mon, const QObject *data);
+void do_pci_info(Monitor *mon, QObject **ret_data);
+void pci_bridge_update_mappings(PCIBus *b);
+
+void pci_device_deassert_intx(PCIDevice *dev);
+
+static inline void
+pci_set_byte(uint8_t *config, uint8_t val)
+{
+ *config = val;
+}
+
+static inline uint8_t
+pci_get_byte(const uint8_t *config)
+{
+ return *config;
+}
+
+static inline void
+pci_set_word(uint8_t *config, uint16_t val)
+{
+ cpu_to_le16wu((uint16_t *)config, val);
+}
+
+static inline uint16_t
+pci_get_word(const uint8_t *config)
+{
+ return le16_to_cpupu((const uint16_t *)config);
+}
+
+static inline void
+pci_set_long(uint8_t *config, uint32_t val)
+{
+ cpu_to_le32wu((uint32_t *)config, val);
+}
+
+static inline uint32_t
+pci_get_long(const uint8_t *config)
+{
+ return le32_to_cpupu((const uint32_t *)config);
+}
+
+static inline void
+pci_set_quad(uint8_t *config, uint64_t val)
+{
+ cpu_to_le64w((uint64_t *)config, val);
+}
+
+static inline uint64_t
+pci_get_quad(const uint8_t *config)
+{
+ return le64_to_cpup((const uint64_t *)config);
+}
+
+static inline void
+pci_config_set_vendor_id(uint8_t *pci_config, uint16_t val)
+{
+ pci_set_word(&pci_config[PCI_VENDOR_ID], val);
+}
+
+static inline void
+pci_config_set_device_id(uint8_t *pci_config, uint16_t val)
+{
+ pci_set_word(&pci_config[PCI_DEVICE_ID], val);
+}
+
+static inline void
+pci_config_set_revision(uint8_t *pci_config, uint8_t val)
+{
+ pci_set_byte(&pci_config[PCI_REVISION_ID], val);
+}
+
+static inline void
+pci_config_set_class(uint8_t *pci_config, uint16_t val)
+{
+ pci_set_word(&pci_config[PCI_CLASS_DEVICE], val);
+}
+
+static inline void
+pci_config_set_prog_interface(uint8_t *pci_config, uint8_t val)
+{
+ pci_set_byte(&pci_config[PCI_CLASS_PROG], val);
+}
+
+static inline void
+pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val)
+{
+ pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val);
+}
+
+/*
+ * helper functions to do bit mask operation on configuration space.
+ * Just to set bit, use test-and-set and discard returned value.
+ * Just to clear bit, use test-and-clear and discard returned value.
+ * NOTE: They aren't atomic.
+ */
+static inline uint8_t
+pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask)
+{
+ uint8_t val = pci_get_byte(config);
+ pci_set_byte(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint8_t
+pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask)
+{
+ uint8_t val = pci_get_byte(config);
+ pci_set_byte(config, val | mask);
+ return val & mask;
+}
+
+static inline uint16_t
+pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask)
+{
+ uint16_t val = pci_get_word(config);
+ pci_set_word(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint16_t
+pci_word_test_and_set_mask(uint8_t *config, uint16_t mask)
+{
+ uint16_t val = pci_get_word(config);
+ pci_set_word(config, val | mask);
+ return val & mask;
+}
+
+static inline uint32_t
+pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask)
+{
+ uint32_t val = pci_get_long(config);
+ pci_set_long(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint32_t
+pci_long_test_and_set_mask(uint8_t *config, uint32_t mask)
+{
+ uint32_t val = pci_get_long(config);
+ pci_set_long(config, val | mask);
+ return val & mask;
+}
+
+static inline uint64_t
+pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask)
+{
+ uint64_t val = pci_get_quad(config);
+ pci_set_quad(config, val & ~mask);
+ return val & mask;
+}
+
+static inline uint64_t
+pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask)
+{
+ uint64_t val = pci_get_quad(config);
+ pci_set_quad(config, val | mask);
+ return val & mask;
+}
+
+typedef int (*pci_qdev_initfn)(PCIDevice *dev);
+typedef struct {
+ DeviceInfo qdev;
+ pci_qdev_initfn init;
+ PCIUnregisterFunc *exit;
+ PCIConfigReadFunc *config_read;
+ PCIConfigWriteFunc *config_write;
+
+ /*
+ * pci-to-pci bridge or normal device.
+ * This doesn't mean pci host switch.
+ * When card bus bridge is supported, this would be enhanced.
+ */
+ int is_bridge;
+
+ /* pcie stuff */
+ int is_express; /* is this device pci express? */
+
+ /* device isn't hot-pluggable */
+ int no_hotplug;
+
+ /* rom bar */
+ const char *romfile;
+} PCIDeviceInfo;
+
+void pci_qdev_register(PCIDeviceInfo *info);
+void pci_qdev_register_many(PCIDeviceInfo *info);
+
+PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
+ const char *name);
+PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn,
+ bool multifunction,
+ const char *name);
+PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name);
+PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name);
+
+static inline int pci_is_express(const PCIDevice *d)
+{
+ return d->cap_present & QEMU_PCI_CAP_EXPRESS;
+}
+
+static inline uint32_t pci_config_size(const PCIDevice *d)
+{
+ return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE;
+}
+
+#endif
diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
new file mode 100644
index 0000000..464d897
--- /dev/null
+++ b/hw/pci_bridge.c
@@ -0,0 +1,275 @@
+/*
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to dea
+
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
+
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * split out from pci.c
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ */
+
+#include "pci_bridge.h"
+#include "pci_internals.h"
+#include "range.h"
+
+/* PCI bridge subsystem vendor ID helper functions */
+#define PCI_SSVID_SIZEOF 8
+#define PCI_SSVID_SVID 4
+#define PCI_SSVID_SSID 6
+
+int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
+ uint16_t svid, uint16_t ssid)
+{
+ int pos;
+ pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF);
+ if (pos < 0) {
+ return pos;
+ }
+
+ pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid);
+ pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid);
+ return pos;
+}
+
+/* Accessor function to get parent bridge device from pci bus. */
+PCIDevice *pci_bridge_get_device(PCIBus *bus)
+{
+ return bus->parent_dev;
+}
+
+/* Accessor function to get secondary bus from pci-to-pci bridge device */
+PCIBus *pci_bridge_get_sec_bus(PCIBridge *br)
+{
+ return &br->sec_bus;
+}
+
+static uint32_t pci_config_get_io_base(const PCIDevice *d,
+ uint32_t base, uint32_t base_upper16)
+{
+ uint32_t val;
+
+ val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8;
+ if (d->config[base] & PCI_IO_RANGE_TYPE_32) {
+ val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16;
+ }
+ return val;
+}
+
+static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base)
+{
+ return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK)
+ << 16;
+}
+
+static pcibus_t pci_config_get_pref_base(const PCIDevice *d,
+ uint32_t base, uint32_t upper)
+{
+ pcibus_t tmp;
+ pcibus_t val;
+
+ tmp = (pcibus_t)pci_get_word(d->config + base);
+ val = (tmp & PCI_PREF_RANGE_MASK) << 16;
+ if (tmp & PCI_PREF_RANGE_TYPE_64) {
+ val |= (pcibus_t)pci_get_long(d->config + upper) << 32;
+ }
+ return val;
+}
+
+/* accessor function to get bridge filtering base address */
+pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type)
+{
+ pcibus_t base;
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ base = pci_config_get_io_base(bridge,
+ PCI_IO_BASE, PCI_IO_BASE_UPPER16);
+ } else {
+ if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ base = pci_config_get_pref_base(
+ bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32);
+ } else {
+ base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
+ }
+ }
+
+ return base;
+}
+
+/* accessor funciton to get bridge filtering limit */
+pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type)
+{
+ pcibus_t limit;
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ limit = pci_config_get_io_base(bridge,
+ PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16);
+ limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */
+ } else {
+ if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ limit = pci_config_get_pref_base(
+ bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32);
+ } else {
+ limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT);
+ }
+ limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */
+ }
+ return limit;
+}
+
+/* default write_config function for PCI-to-PCI bridge */
+void pci_bridge_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ PCIBridge *s = container_of(d, PCIBridge, dev);
+ uint16_t oldctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL);
+ uint16_t newctl;
+
+ pci_default_write_config(d, address, val, len);
+
+ if (/* io base/limit */
+ ranges_overlap(address, len, PCI_IO_BASE, 2) ||
+
+ /* memory base/limit, prefetchable base/limit and
+ io base/limit upper 16 */
+ ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) {
+ pci_bridge_update_mappings(&s->sec_bus);
+ }
+
+ newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL);
+ if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) {
+ /* Trigger hot reset on 0->1 transition. */
+ pci_bus_reset(&s->sec_bus);
+ }
+}
+
+void pci_bridge_disable_base_limit(PCIDevice *dev)
+{
+ uint8_t *conf = dev->config;
+
+ pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
+ pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
+}
+
+/* reset bridge specific configuration registers */
+void pci_bridge_reset_reg(PCIDevice *dev)
+{
+ uint8_t *conf = dev->config;
+
+ conf[PCI_PRIMARY_BUS] = 0;
+ conf[PCI_SECONDARY_BUS] = 0;
+ conf[PCI_SUBORDINATE_BUS] = 0;
+ conf[PCI_SEC_LATENCY_TIMER] = 0;
+
+ /*
+ * the default values for base/limit registers aren't specified
+ * in the PCI-to-PCI-bridge spec. So we don't thouch them here.
+ * Each implementation can override it.
+ * typical implementation does
+ * zero base/limit registers or
+ * disable forwarding: pci_bridge_disable_base_limit()
+ * If disable forwarding is wanted, call pci_bridge_disable_base_limit()
+ * after this function.
+ */
+ pci_byte_test_and_clear_mask(conf + PCI_IO_BASE,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
+ PCI_IO_RANGE_MASK & 0xff);
+ pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
+ pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
+
+ pci_set_word(conf + PCI_BRIDGE_CONTROL, 0);
+}
+
+/* default reset function for PCI-to-PCI bridge */
+void pci_bridge_reset(DeviceState *qdev)
+{
+ PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
+ pci_bridge_reset_reg(dev);
+}
+
+/* default qdev initialization function for PCI-to-PCI bridge */
+int pci_bridge_initfn(PCIDevice *dev)
+{
+ PCIBus *parent = dev->bus;
+ PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev);
+ PCIBus *sec_bus = &br->sec_bus;
+
+ pci_set_word(dev->config + PCI_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
+ pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
+ dev->config[PCI_HEADER_TYPE] =
+ (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
+ PCI_HEADER_TYPE_BRIDGE;
+ pci_set_word(dev->config + PCI_SEC_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
+
+ qbus_create_inplace(&sec_bus->qbus, &pci_bus_info, &dev->qdev,
+ br->bus_name);
+ sec_bus->parent_dev = dev;
+ sec_bus->map_irq = br->map_irq;
+
+ QLIST_INIT(&sec_bus->child);
+ QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling);
+ return 0;
+}
+
+/* default qdev clean up function for PCI-to-PCI bridge */
+int pci_bridge_exitfn(PCIDevice *pci_dev)
+{
+ PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev);
+ assert(QLIST_EMPTY(&s->sec_bus.child));
+ QLIST_REMOVE(&s->sec_bus, sibling);
+ /* qbus_free() is called automatically by qdev_free() */
+ return 0;
+}
+
+/*
+ * before qdev initialization(qdev_init()), this function sets bus_name and
+ * map_irq callback which are necessry for pci_bridge_initfn() to
+ * initialize bus.
+ */
+void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,
+ pci_map_irq_fn map_irq)
+{
+ br->map_irq = map_irq;
+ br->bus_name = bus_name;
+}
diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h
new file mode 100644
index 0000000..84411a6
--- /dev/null
+++ b/hw/pci_bridge.h
@@ -0,0 +1,66 @@
+/*
+ * QEMU PCI bridge
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc]
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ */
+
+#ifndef QEMU_PCI_BRIDGE_H
+#define QEMU_PCI_BRIDGE_H
+
+#include "pci.h"
+
+int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
+ uint16_t svid, uint16_t ssid);
+
+PCIDevice *pci_bridge_get_device(PCIBus *bus);
+PCIBus *pci_bridge_get_sec_bus(PCIBridge *br);
+
+pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type);
+pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type);
+
+void pci_bridge_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len);
+void pci_bridge_disable_base_limit(PCIDevice *dev);
+void pci_bridge_reset_reg(PCIDevice *dev);
+void pci_bridge_reset(DeviceState *qdev);
+
+int pci_bridge_initfn(PCIDevice *pci_dev);
+int pci_bridge_exitfn(PCIDevice *pci_dev);
+
+
+/*
+ * before qdev initialization(qdev_init()), this function sets bus_name and
+ * map_irq callback which are necessry for pci_bridge_initfn() to
+ * initialize bus.
+ */
+void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,
+ pci_map_irq_fn map_irq);
+
+#endif /* QEMU_PCI_BRIDGE_H */
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pci_host.c b/hw/pci_host.c
new file mode 100644
index 0000000..7c40155
--- /dev/null
+++ b/hw/pci_host.c
@@ -0,0 +1,159 @@
+/*
+ * pci_host.c
+ *
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 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/>.
+ */
+
+#include "pci.h"
+#include "pci_host.h"
+
+/* debug PCI */
+//#define DEBUG_PCI
+
+#ifdef DEBUG_PCI
+#define PCI_DPRINTF(fmt, ...) \
+do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define PCI_DPRINTF(fmt, ...)
+#endif
+
+/*
+ * PCI address
+ * bit 16 - 24: bus number
+ * bit 8 - 15: devfun number
+ * bit 0 - 7: offset in configuration space of a given pci device
+ */
+
+/* the helper functio to get a PCIDeice* for a given pci address */
+static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr)
+{
+ uint8_t bus_num = addr >> 16;
+ uint8_t devfn = addr >> 8;
+
+ return pci_find_device(bus, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn));
+}
+
+void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len)
+{
+ PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr);
+ uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1);
+
+ if (!pci_dev)
+ return;
+
+ PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n",
+ __func__, pci_dev->name, config_addr, val, len);
+ pci_dev->config_write(pci_dev, config_addr, val, len);
+}
+
+uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len)
+{
+ PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr);
+ uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1);
+ uint32_t val;
+
+ assert(len == 1 || len == 2 || len == 4);
+ if (!pci_dev) {
+ return ~0x0;
+ }
+
+ val = pci_dev->config_read(pci_dev, config_addr, len);
+ PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRIx32" len=%d\n",
+ __func__, pci_dev->name, config_addr, val, len);
+
+ return val;
+}
+
+static void pci_host_config_write(ReadWriteHandler *handler,
+ pcibus_t addr, uint32_t val, int len)
+{
+ PCIHostState *s = container_of(handler, PCIHostState, conf_handler);
+
+ PCI_DPRINTF("%s addr %" FMT_PCIBUS " %d val %"PRIx32"\n",
+ __func__, addr, len, val);
+ s->config_reg = val;
+}
+
+static uint32_t pci_host_config_read(ReadWriteHandler *handler,
+ pcibus_t addr, int len)
+{
+ PCIHostState *s = container_of(handler, PCIHostState, conf_handler);
+ uint32_t val = s->config_reg;
+
+ PCI_DPRINTF("%s addr %" FMT_PCIBUS " len %d val %"PRIx32"\n",
+ __func__, addr, len, val);
+ return val;
+}
+
+static void pci_host_data_write(ReadWriteHandler *handler,
+ pcibus_t addr, uint32_t val, int len)
+{
+ PCIHostState *s = container_of(handler, PCIHostState, data_handler);
+ PCI_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n",
+ addr, len, val);
+ if (s->config_reg & (1u << 31))
+ pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
+}
+
+static uint32_t pci_host_data_read(ReadWriteHandler *handler,
+ pcibus_t addr, int len)
+{
+ PCIHostState *s = container_of(handler, PCIHostState, data_handler);
+ uint32_t val;
+ if (!(s->config_reg & (1 << 31)))
+ return 0xffffffff;
+ val = pci_data_read(s->bus, s->config_reg | (addr & 3), len);
+ PCI_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n",
+ addr, len, val);
+ return val;
+}
+
+static void pci_host_init(PCIHostState *s)
+{
+ s->conf_handler.write = pci_host_config_write;
+ s->conf_handler.read = pci_host_config_read;
+ s->data_handler.write = pci_host_data_write;
+ s->data_handler.read = pci_host_data_read;
+}
+
+int pci_host_conf_register_mmio(PCIHostState *s, int endian)
+{
+ pci_host_init(s);
+ return cpu_register_io_memory_simple(&s->conf_handler, endian);
+}
+
+void pci_host_conf_register_ioport(pio_addr_t ioport, PCIHostState *s)
+{
+ pci_host_init(s);
+ register_ioport_simple(&s->conf_handler, ioport, 4, 4);
+ sysbus_init_ioports(&s->busdev, ioport, 4);
+}
+
+int pci_host_data_register_mmio(PCIHostState *s, int endian)
+{
+ pci_host_init(s);
+ return cpu_register_io_memory_simple(&s->data_handler, endian);
+}
+
+void pci_host_data_register_ioport(pio_addr_t ioport, PCIHostState *s)
+{
+ pci_host_init(s);
+ register_ioport_simple(&s->data_handler, ioport, 4, 1);
+ register_ioport_simple(&s->data_handler, ioport, 4, 2);
+ register_ioport_simple(&s->data_handler, ioport, 4, 4);
+ sysbus_init_ioports(&s->busdev, ioport, 4);
+}
diff --git a/hw/pci_host.h b/hw/pci_host.h
new file mode 100644
index 0000000..0a58595
--- /dev/null
+++ b/hw/pci_host.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU Common PCI Host bridge configuration data space access routines.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Worker routines for a PCI host controller that uses an {address,data}
+ register pair to access PCI configuration space. */
+
+#ifndef PCI_HOST_H
+#define PCI_HOST_H
+
+#include "sysbus.h"
+#include "rwhandler.h"
+
+struct PCIHostState {
+ SysBusDevice busdev;
+ ReadWriteHandler conf_handler;
+ ReadWriteHandler data_handler;
+ uint32_t config_reg;
+ PCIBus *bus;
+};
+
+void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len);
+uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len);
+
+/* for mmio */
+int pci_host_conf_register_mmio(PCIHostState *s, int endian);
+int pci_host_data_register_mmio(PCIHostState *s, int endian);
+
+/* for ioio */
+void pci_host_conf_register_ioport(pio_addr_t ioport, PCIHostState *s);
+void pci_host_data_register_ioport(pio_addr_t ioport, PCIHostState *s);
+
+#endif /* PCI_HOST_H */
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
new file mode 100644
index 0000000..ea3418c
--- /dev/null
+++ b/hw/pci_ids.h
@@ -0,0 +1,110 @@
+/*
+ * PCI Class, Vendor and Device IDs
+ *
+ * Please keep sorted.
+ *
+ * Abbreviated version of linux/pci_ids.h
+ *
+ * QEMU-specific definitions belong in pci.h
+ */
+
+/* Device classes and subclasses */
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_BASE_CLASS_NETWORK 0x02
+
+#define PCI_CLASS_STORAGE_SCSI 0x0100
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_SATA 0x0106
+#define PCI_CLASS_STORAGE_OTHER 0x0180
+
+#define PCI_CLASS_NETWORK_ETHERNET 0x0200
+
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_DISPLAY_OTHER 0x0380
+
+#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
+
+#define PCI_CLASS_MEMORY_RAM 0x0500
+
+#define PCI_CLASS_SYSTEM_OTHER 0x0880
+
+#define PCI_CLASS_SERIAL_USB 0x0c03
+
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+#define PCI_CLASS_BRIDGE_ISA 0x0601
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_CLASS_BRIDGE_OTHER 0x0680
+
+#define PCI_CLASS_COMMUNICATION_OTHER 0x0780
+
+#define PCI_CLASS_PROCESSOR_CO 0x0b40
+#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20
+
+#define PCI_CLASS_OTHERS 0xff
+
+/* Vendors and devices. Sort key: vendor first, device next. */
+
+#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
+#define PCI_DEVICE_ID_LSI_53C895A 0x0012
+
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_DEVICE_ID_DEC_21154 0x0026
+
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+
+#define PCI_VENDOR_ID_IBM 0x1014
+
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_LANCE 0x2000
+
+#define PCI_VENDOR_ID_TI 0x104c
+
+#define PCI_VENDOR_ID_MOTOROLA 0x1057
+#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002
+#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801
+
+#define PCI_VENDOR_ID_APPLE 0x106b
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020
+#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b
+
+#define PCI_VENDOR_ID_SUN 0x108e
+#define PCI_DEVICE_ID_SUN_EBUS 0x1000
+#define PCI_DEVICE_ID_SUN_SIMBA 0x5000
+#define PCI_DEVICE_ID_SUN_SABRE 0xa000
+
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_DEVICE_ID_CMD_646 0x0646
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_DEVICE_ID_REALTEK_8139 0x8139
+
+#define PCI_VENDOR_ID_XILINX 0x10ee
+
+#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_DEVICE_ID_VIA_ISA_BRIDGE 0x0686
+#define PCI_DEVICE_ID_VIA_IDE 0x0571
+#define PCI_DEVICE_ID_VIA_UHCI 0x3038
+#define PCI_DEVICE_ID_VIA_ACPI 0x3057
+#define PCI_DEVICE_ID_VIA_AC97 0x3058
+#define PCI_DEVICE_ID_VIA_MC97 0x3068
+
+#define PCI_VENDOR_ID_MARVELL 0x11ab
+
+#define PCI_VENDOR_ID_ENSONIQ 0x1274
+#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000
+
+#define PCI_VENDOR_ID_FREESCALE 0x1957
+#define PCI_DEVICE_ID_MPC8533E 0x0030
+
+#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_ESB_9 0x25ab
+#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000
+#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010
+#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
diff --git a/hw/pci_internals.h b/hw/pci_internals.h
new file mode 100644
index 0000000..e3c93a3
--- /dev/null
+++ b/hw/pci_internals.h
@@ -0,0 +1,47 @@
+#ifndef QEMU_PCI_INTERNALS_H
+#define QEMU_PCI_INTERNALS_H
+
+/*
+ * This header files is private to pci.c and pci_bridge.c
+ * So following structures are opaque to others and shouldn't be
+ * accessed.
+ *
+ * For pci-to-pci bridge needs to include this header file to embed
+ * PCIBridge in its structure or to get sizeof(PCIBridge),
+ * However, they shouldn't access those following members directly.
+ * Use accessor function in pci.h, pci_bridge.h
+ */
+
+extern struct BusInfo pci_bus_info;
+
+struct PCIBus {
+ BusState qbus;
+ int devfn_min;
+ pci_set_irq_fn set_irq;
+ pci_map_irq_fn map_irq;
+ pci_hotplug_fn hotplug;
+ DeviceState *hotplug_qdev;
+ void *irq_opaque;
+ PCIDevice *devices[256];
+ PCIDevice *parent_dev;
+ target_phys_addr_t mem_base;
+
+ QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */
+ QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */
+
+ /* The bus IRQ state is the logical OR of the connected devices.
+ Keep a count of the number of devices with raised IRQs. */
+ int nirq;
+ int *irq_count;
+};
+
+struct PCIBridge {
+ PCIDevice dev;
+
+ /* private member */
+ PCIBus sec_bus;
+ pci_map_irq_fn map_irq;
+ const char *bus_name;
+};
+
+#endif /* QEMU_PCI_INTERNALS_H */
diff --git a/hw/pci_regs.h b/hw/pci_regs.h
new file mode 100644
index 0000000..1c675dc
--- /dev/null
+++ b/hw/pci_regs.h
@@ -0,0 +1,672 @@
+/*
+ * pci_regs.h
+ *
+ * PCI standard defines
+ * Copyright 1994, Drew Eckhardt
+ * Copyright 1997--1999 Martin Mares <mj@ucw.cz>
+ *
+ * For more information, please consult the following manuals (look at
+ * http://www.pcisig.com/ for how to get them):
+ *
+ * PCI BIOS Specification
+ * PCI Local Bus Specification
+ * PCI to PCI Bridge Specification
+ * PCI System Design Guide
+ *
+ * For hypertransport information, please consult the following manuals
+ * from http://www.hypertransport.org
+ *
+ * The Hypertransport I/O Link Specification
+ */
+
+#ifndef LINUX_PCI_REGS_H
+#define LINUX_PCI_REGS_H
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes are standardized as follows:
+ */
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */
+#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */
+
+#ifndef PCI_STATUS_66MHZ
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */
+#endif
+
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */
+#ifndef PCI_STATUS_FAST_BACK
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#endif
+
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_HEADER_TYPE_NORMAL 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+#define PCI_BIST 0x0f /* 8 bits */
+#define PCI_BIST_CODE_MASK 0x0f /* Return result */
+#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back. Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
+/* bit 1 is reserved if address_space = 1 */
+
+/* Header type 0 (normal devices) */
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x01
+#define PCI_ROM_ADDRESS_MASK (~0x7ffUL)
+
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+/* 0x35-0x3b are reserved */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */
+#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
+#define PCI_IO_LIMIT 0x1d
+#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */
+#define PCI_IO_RANGE_TYPE_16 0x00
+#define PCI_IO_RANGE_TYPE_32 0x01
+#define PCI_IO_RANGE_MASK (~0x0fUL)
+#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */
+#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
+#define PCI_MEMORY_LIMIT 0x22
+#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL
+#define PCI_MEMORY_RANGE_MASK (~0x0fUL)
+#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT 0x26
+#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL
+#define PCI_PREF_RANGE_TYPE_32 0x00
+#define PCI_PREF_RANGE_TYPE_64 0x01
+#define PCI_PREF_RANGE_MASK (~0x0fUL)
+#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32 0x2c
+#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16 0x32
+/* 0x34 same as for htype 0 */
+/* 0x35-0x3b is reserved */
+#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL 0x3e
+#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */
+#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */
+#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */
+#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */
+#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */
+#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */
+#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */
+
+/* Header type 2 (CardBus bridges) */
+#define PCI_CB_CAPABILITY_LIST 0x14
+/* 0x15 reserved */
+#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */
+#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */
+#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */
+#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */
+#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */
+#define PCI_CB_MEMORY_BASE_0 0x1c
+#define PCI_CB_MEMORY_LIMIT_0 0x20
+#define PCI_CB_MEMORY_BASE_1 0x24
+#define PCI_CB_MEMORY_LIMIT_1 0x28
+#define PCI_CB_IO_BASE_0 0x2c
+#define PCI_CB_IO_BASE_0_HI 0x2e
+#define PCI_CB_IO_LIMIT_0 0x30
+#define PCI_CB_IO_LIMIT_0_HI 0x32
+#define PCI_CB_IO_BASE_1 0x34
+#define PCI_CB_IO_BASE_1_HI 0x36
+#define PCI_CB_IO_LIMIT_1 0x38
+#define PCI_CB_IO_LIMIT_1_HI 0x3a
+#define PCI_CB_IO_RANGE_MASK (~0x03UL)
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_CB_BRIDGE_CONTROL 0x3e
+#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */
+#define PCI_CB_BRIDGE_CTL_SERR 0x02
+#define PCI_CB_BRIDGE_CTL_ISA 0x04
+#define PCI_CB_BRIDGE_CTL_VGA 0x08
+#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20
+#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */
+#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200
+#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400
+#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40
+#define PCI_CB_SUBSYSTEM_ID 0x42
+#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */
+/* 0x48-0x7f reserved */
+
+/* Capability lists */
+
+#define PCI_CAP_LIST_ID 0 /* Capability ID */
+#define PCI_CAP_ID_PM 0x01 /* Power Management */
+#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
+#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
+#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
+#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */
+#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
+#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
+#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
+#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */
+#define PCI_CAP_ID_DBG 0x0A /* Debug port */
+#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
+#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */
+#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */
+#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */
+#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
+#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
+#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
+#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
+#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
+#define PCI_CAP_SIZEOF 4
+
+/* Power Management Registers */
+
+#define PCI_PM_PMC 2 /* PM Capabilities Register */
+#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */
+#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */
+#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */
+#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */
+#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */
+#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */
+#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */
+#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */
+#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */
+#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */
+#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */
+#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */
+#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */
+#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
+#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */
+#define PCI_PM_CTRL 4 /* PM control and status register */
+#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
+#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */
+#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */
+#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */
+#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */
+#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */
+#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */
+#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */
+#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */
+#define PCI_PM_DATA_REGISTER 7 /* (??) */
+#define PCI_PM_SIZEOF 8
+
+/* AGP registers */
+
+#define PCI_AGP_VERSION 2 /* BCD version number */
+#define PCI_AGP_RFU 3 /* Rest of capability flags */
+#define PCI_AGP_STATUS 4 /* Status register */
+#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */
+#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */
+#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */
+#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */
+#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */
+#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */
+#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */
+#define PCI_AGP_COMMAND 8 /* Control register */
+#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */
+#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */
+#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */
+#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */
+#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */
+#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */
+#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */
+#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */
+#define PCI_AGP_SIZEOF 12
+
+/* Vital Product Data */
+
+#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */
+#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */
+#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */
+#define PCI_VPD_DATA 4 /* 32-bits of data returned here */
+
+/* Slot Identification */
+
+#define PCI_SID_ESR 2 /* Expansion Slot Register */
+#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */
+#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */
+#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */
+
+/* Message Signalled Interrupts registers */
+
+#define PCI_MSI_FLAGS 2 /* Various flags */
+#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */
+#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */
+#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */
+#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */
+#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */
+#define PCI_MSI_RFU 3 /* Rest of capability flags */
+#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
+#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
+#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
+#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */
+#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */
+
+/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */
+#define PCI_MSIX_FLAGS 2
+#define PCI_MSIX_FLAGS_QSIZE 0x7FF
+#define PCI_MSIX_FLAGS_ENABLE (1 << 15)
+#define PCI_MSIX_FLAGS_MASKALL (1 << 14)
+#define PCI_MSIX_FLAGS_BIRMASK (7 << 0)
+
+/* CompactPCI Hotswap Register */
+
+#define PCI_CHSWP_CSR 2 /* Control and Status Register */
+#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */
+#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */
+#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */
+#define PCI_CHSWP_LOO 0x08 /* LED On / Off */
+#define PCI_CHSWP_PI 0x30 /* Programming Interface */
+#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */
+#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */
+
+/* PCI Advanced Feature registers */
+
+#define PCI_AF_LENGTH 2
+#define PCI_AF_CAP 3
+#define PCI_AF_CAP_TP 0x01
+#define PCI_AF_CAP_FLR 0x02
+#define PCI_AF_CTRL 4
+#define PCI_AF_CTRL_FLR 0x01
+#define PCI_AF_STATUS 5
+#define PCI_AF_STATUS_TP 0x01
+
+/* PCI-X registers */
+
+#define PCI_X_CMD 2 /* Modes & Features */
+#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */
+#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */
+#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */
+#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */
+#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */
+#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */
+#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */
+ /* Max # of outstanding split transactions */
+#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */
+#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */
+#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */
+#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */
+#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */
+#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */
+#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */
+#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */
+#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */
+#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */
+#define PCI_X_STATUS 4 /* PCI-X capabilities */
+#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */
+#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */
+#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */
+#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */
+#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */
+#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */
+#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */
+#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */
+#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */
+#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */
+#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */
+#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */
+#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */
+
+/* PCI Express capability registers */
+
+#define PCI_EXP_FLAGS 2 /* Capabilities register */
+#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */
+#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */
+#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */
+#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */
+#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
+#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */
+#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */
+#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */
+#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */
+#define PCI_EXP_TYPE_RC_EC 0x10 /* Root Complex Event Collector */
+#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
+#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
+#define PCI_EXP_DEVCAP 4 /* Device capabilities */
+#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */
+#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */
+#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */
+#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */
+#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */
+#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */
+#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */
+#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */
+#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */
+#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */
+#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */
+#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */
+#define PCI_EXP_DEVCTL 8 /* Device Control */
+#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */
+#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */
+#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */
+#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */
+#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */
+#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */
+#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */
+#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */
+#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */
+#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */
+#define PCI_EXP_DEVSTA 10 /* Device Status */
+#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */
+#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */
+#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */
+#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */
+#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */
+#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */
+#define PCI_EXP_LNKCAP 12 /* Link Capabilities */
+#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */
+#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */
+#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */
+#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */
+#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */
+#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */
+#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Suprise Down Error Reporting Capable */
+#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */
+#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */
+#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */
+#define PCI_EXP_LNKCTL 16 /* Link Control */
+#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */
+#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */
+#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */
+#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */
+#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */
+#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */
+#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */
+#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */
+#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */
+#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */
+#define PCI_EXP_LNKSTA 18 /* Link Status */
+#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */
+#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */
+#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */
+#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */
+#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */
+#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */
+#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */
+#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */
+#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */
+#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */
+#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */
+#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */
+#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */
+#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */
+#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */
+#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */
+#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */
+#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */
+#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */
+#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */
+#define PCI_EXP_SLTCTL 24 /* Slot Control */
+#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */
+#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */
+#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */
+#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */
+#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */
+#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */
+#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */
+#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */
+#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */
+#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
+#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
+#define PCI_EXP_SLTSTA 26 /* Slot Status */
+#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
+#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */
+#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */
+#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */
+#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */
+#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */
+#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */
+#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */
+#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */
+#define PCI_EXP_RTCTL 28 /* Root Control */
+#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */
+#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */
+#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */
+#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */
+#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */
+#define PCI_EXP_RTCAP 30 /* Root Capabilities */
+#define PCI_EXP_RTSTA 32 /* Root Status */
+#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
+#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */
+#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */
+#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
+#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
+
+/* Extended Capabilities (PCI-X 2.0 and Express) */
+#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff)
+#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf)
+#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc)
+
+#define PCI_EXT_CAP_ID_ERR 1
+#define PCI_EXT_CAP_ID_VC 2
+#define PCI_EXT_CAP_ID_DSN 3
+#define PCI_EXT_CAP_ID_PWR 4
+#define PCI_EXT_CAP_ID_ARI 14
+#define PCI_EXT_CAP_ID_ATS 15
+#define PCI_EXT_CAP_ID_SRIOV 16
+
+/* Advanced Error Reporting */
+#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
+#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */
+#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */
+#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */
+#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */
+#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */
+#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */
+#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */
+#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */
+#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */
+#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */
+#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */
+#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */
+ /* Same bits as above */
+#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */
+#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */
+#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */
+#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */
+#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */
+#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */
+#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */
+#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */
+#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */
+#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */
+#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
+#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
+#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */
+#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */
+/* Correctable Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001
+/* Non-fatal Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002
+/* Fatal Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004
+#define PCI_ERR_ROOT_STATUS 48
+#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */
+/* Multi ERR_COR Received */
+#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002
+/* ERR_FATAL/NONFATAL Recevied */
+#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004
+/* Multi ERR_FATAL/NONFATAL Recevied */
+#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008
+#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */
+#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
+#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
+#define PCI_ERR_ROOT_COR_SRC 52
+#define PCI_ERR_ROOT_SRC 54
+
+/* Virtual Channel */
+#define PCI_VC_PORT_REG1 4
+#define PCI_VC_PORT_REG2 8
+#define PCI_VC_PORT_CTRL 12
+#define PCI_VC_PORT_STATUS 14
+#define PCI_VC_RES_CAP 16
+#define PCI_VC_RES_CTRL 20
+#define PCI_VC_RES_STATUS 26
+
+/* Power Budgeting */
+#define PCI_PWR_DSR 4 /* Data Select Register */
+#define PCI_PWR_DATA 8 /* Data Register */
+#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */
+#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */
+#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */
+#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */
+#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */
+#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */
+#define PCI_PWR_CAP 12 /* Capability */
+#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */
+
+/*
+ * Hypertransport sub capability types
+ *
+ * Unfortunately there are both 3 bit and 5 bit capability types defined
+ * in the HT spec, catering for that is a little messy. You probably don't
+ * want to use these directly, just use pci_find_ht_capability() and it
+ * will do the right thing for you.
+ */
+#define HT_3BIT_CAP_MASK 0xE0
+#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */
+#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */
+
+#define HT_5BIT_CAP_MASK 0xF8
+#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */
+#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */
+#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */
+#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */
+#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */
+#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */
+#define HT_MSI_FLAGS 0x02 /* Offset to flags */
+#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */
+#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */
+#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */
+#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */
+#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */
+#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */
+#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */
+#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */
+#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */
+#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */
+#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */
+
+/* Alternative Routing-ID Interpretation */
+#define PCI_ARI_CAP 0x04 /* ARI Capability Register */
+#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */
+#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */
+#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */
+#define PCI_ARI_CTRL 0x06 /* ARI Control Register */
+#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */
+#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */
+#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */
+
+/* Address Translation Service */
+#define PCI_ATS_CAP 0x04 /* ATS Capability Register */
+#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */
+#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */
+#define PCI_ATS_CTRL 0x06 /* ATS Control Register */
+#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */
+#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */
+#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */
+
+/* Single Root I/O Virtualization */
+#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */
+#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */
+#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */
+#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */
+#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */
+#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */
+#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */
+#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */
+#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */
+#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */
+#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */
+#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */
+#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */
+#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */
+#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */
+#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */
+#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */
+#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */
+#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */
+#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */
+#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */
+#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */
+#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/
+#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */
+#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */
+#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */
+#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */
+#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */
+#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */
+
+#endif /* LINUX_PCI_REGS_H */
diff --git a/hw/pcie.c b/hw/pcie.c
new file mode 100644
index 0000000..6a113a9
--- /dev/null
+++ b/hw/pcie.c
@@ -0,0 +1,544 @@
+/*
+ * pcie.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "sysemu.h"
+#include "range.h"
+#include "pci_bridge.h"
+#include "pcie.h"
+#include "msix.h"
+#include "msi.h"
+#include "pci_internals.h"
+#include "pcie_regs.h"
+#include "range.h"
+
+//#define DEBUG_PCIE
+#ifdef DEBUG_PCIE
+# define PCIE_DPRINTF(fmt, ...) \
+ fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
+#else
+# define PCIE_DPRINTF(fmt, ...) do {} while (0)
+#endif
+#define PCIE_DEV_PRINTF(dev, fmt, ...) \
+ PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
+
+
+/***************************************************************************
+ * pci express capability helper functions
+ */
+int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
+{
+ int pos;
+ uint8_t *exp_cap;
+
+ assert(pci_is_express(dev));
+
+ pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset,
+ PCI_EXP_VER2_SIZEOF);
+ if (pos < 0) {
+ return pos;
+ }
+ dev->exp.exp_cap = pos;
+ exp_cap = dev->config + pos;
+
+ /* capability register
+ interrupt message number defaults to 0 */
+ pci_set_word(exp_cap + PCI_EXP_FLAGS,
+ ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) |
+ PCI_EXP_FLAGS_VER2);
+
+ /* device capability register
+ * table 7-12:
+ * roll based error reporting bit must be set by all
+ * Functions conforming to the ECN, PCI Express Base
+ * Specification, Revision 1.1., or subsequent PCI Express Base
+ * Specification revisions.
+ */
+ pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
+
+ pci_set_long(exp_cap + PCI_EXP_LNKCAP,
+ (port << PCI_EXP_LNKCAP_PN_SHIFT) |
+ PCI_EXP_LNKCAP_ASPMS_0S |
+ PCI_EXP_LNK_MLW_1 |
+ PCI_EXP_LNK_LS_25);
+
+ pci_set_word(exp_cap + PCI_EXP_LNKSTA,
+ PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
+
+ pci_set_long(exp_cap + PCI_EXP_DEVCAP2,
+ PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP);
+
+ pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB);
+ return pos;
+}
+
+void pcie_cap_exit(PCIDevice *dev)
+{
+ pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF);
+}
+
+uint8_t pcie_cap_get_type(const PCIDevice *dev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ assert(pos > 0);
+ return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) &
+ PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT;
+}
+
+/* MSI/MSI-X */
+/* pci express interrupt message number */
+/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */
+void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector)
+{
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ assert(vector < 32);
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ);
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS,
+ vector << PCI_EXP_FLAGS_IRQ_SHIFT);
+}
+
+uint8_t pcie_cap_flags_get_vector(PCIDevice *dev)
+{
+ return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) &
+ PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT;
+}
+
+void pcie_cap_deverr_init(PCIDevice *dev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP,
+ PCI_EXP_DEVCAP_RBER);
+ pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+ pci_long_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_DEVSTA,
+ PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED |
+ PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD);
+}
+
+void pcie_cap_deverr_reset(PCIDevice *dev)
+{
+ uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
+ pci_long_test_and_clear_mask(devctl,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+}
+
+static void hotplug_event_update_event_status(PCIDevice *dev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ uint8_t *exp_cap = dev->config + pos;
+ uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL);
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+
+ dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) &&
+ (sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED);
+}
+
+static void hotplug_event_notify(PCIDevice *dev)
+{
+ bool prev = dev->exp.hpev_notified;
+
+ hotplug_event_update_event_status(dev);
+
+ if (prev == dev->exp.hpev_notified) {
+ return;
+ }
+
+ /* Note: the logic above does not take into account whether interrupts
+ * are masked. The result is that interrupt will be sent when it is
+ * subsequently unmasked. This appears to be legal: Section 6.7.3.4:
+ * The Port may optionally send an MSI when there are hot-plug events that
+ * occur while interrupt generation is disabled, and interrupt generation is
+ * subsequently enabled. */
+ if (msix_enabled(dev)) {
+ msix_notify(dev, pcie_cap_flags_get_vector(dev));
+ } else if (msi_enabled(dev)) {
+ msi_notify(dev, pcie_cap_flags_get_vector(dev));
+ } else {
+ qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified);
+ }
+}
+
+/*
+ * A PCI Express Hot-Plug Event has occured, so update slot status register
+ * and notify OS of the event if necessary.
+ *
+ * 6.7.3 PCI Express Hot-Plug Events
+ * 6.7.3.4 Software Notification of Hot-Plug Events
+ */
+static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event)
+{
+ /* Minor optimization: if nothing changed - no event is needed. */
+ if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap +
+ PCI_EXP_SLTSTA, event)) {
+ return;
+ }
+ hotplug_event_notify(dev);
+}
+
+static int pcie_cap_slot_hotplug(DeviceState *qdev,
+ PCIDevice *pci_dev, PCIHotplugState state)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+ uint8_t *exp_cap = d->config + d->exp.exp_cap;
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+
+ /* Don't send event when device is enabled during qemu machine creation:
+ * it is present on boot, no hotplug event is necessary. We do send an
+ * event when the device is disabled later. */
+ if (state == PCI_COLDPLUG_ENABLED) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ return 0;
+ }
+
+ PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state);
+ if (sltsta & PCI_EXP_SLTSTA_EIS) {
+ /* the slot is electromechanically locked.
+ * This error is propagated up to qdev and then to HMP/QMP.
+ */
+ return -EBUSY;
+ }
+
+ /* TODO: multifunction hot-plug.
+ * Right now, only a device of function = 0 is allowed to be
+ * hot plugged/unplugged.
+ */
+ assert(PCI_FUNC(pci_dev->devfn) == 0);
+
+ if (state == PCI_HOTPLUG_ENABLED) {
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
+ } else {
+ qdev_free(&pci_dev->qdev);
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_PDS);
+ pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
+ }
+ return 0;
+}
+
+/* pci express slot for pci express root/downstream port
+ PCI express capability slot registers */
+void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot)
+{
+ uint32_t pos = dev->exp.exp_cap;
+
+ pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS,
+ PCI_EXP_FLAGS_SLOT);
+
+ pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP,
+ ~PCI_EXP_SLTCAP_PSN);
+ pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP,
+ (slot << PCI_EXP_SLTCAP_PSN_SHIFT) |
+ PCI_EXP_SLTCAP_EIP |
+ PCI_EXP_SLTCAP_HPS |
+ PCI_EXP_SLTCAP_HPC |
+ PCI_EXP_SLTCAP_PIP |
+ PCI_EXP_SLTCAP_AIP |
+ PCI_EXP_SLTCAP_ABP);
+
+ pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC |
+ PCI_EXP_SLTCTL_AIC);
+ pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC_OFF |
+ PCI_EXP_SLTCTL_AIC_OFF);
+ pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC |
+ PCI_EXP_SLTCTL_AIC |
+ PCI_EXP_SLTCTL_HPIE |
+ PCI_EXP_SLTCTL_CCIE |
+ PCI_EXP_SLTCTL_PDCE |
+ PCI_EXP_SLTCTL_ABPE);
+ /* Although reading PCI_EXP_SLTCTL_EIC returns always 0,
+ * make the bit writable here in order to detect 1b is written.
+ * pcie_cap_slot_write_config() test-and-clear the bit, so
+ * this bit always returns 0 to the guest.
+ */
+ pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_EIC);
+
+ pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA,
+ PCI_EXP_HP_EV_SUPPORTED);
+
+ dev->exp.hpev_notified = false;
+
+ pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)),
+ pcie_cap_slot_hotplug, &dev->qdev);
+}
+
+void pcie_cap_slot_reset(PCIDevice *dev)
+{
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+
+ PCIE_DEV_PRINTF(dev, "reset\n");
+
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_EIC |
+ PCI_EXP_SLTCTL_PIC |
+ PCI_EXP_SLTCTL_AIC |
+ PCI_EXP_SLTCTL_HPIE |
+ PCI_EXP_SLTCTL_CCIE |
+ PCI_EXP_SLTCTL_PDCE |
+ PCI_EXP_SLTCTL_ABPE);
+ pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_PIC_OFF |
+ PCI_EXP_SLTCTL_AIC_OFF);
+
+ pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
+ PCI_EXP_SLTSTA_EIS |/* on reset,
+ the lock is released */
+ PCI_EXP_SLTSTA_CC |
+ PCI_EXP_SLTSTA_PDC |
+ PCI_EXP_SLTSTA_ABP);
+
+ hotplug_event_update_event_status(dev);
+}
+
+void pcie_cap_slot_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ uint8_t *exp_cap = dev->config + pos;
+ uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
+
+ if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
+ return;
+ }
+
+ if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_EIC)) {
+ sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */
+ pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta);
+ PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: "
+ "sltsta -> 0x%02"PRIx16"\n",
+ sltsta);
+ }
+
+ hotplug_event_notify(dev);
+
+ /*
+ * 6.7.3.2 Command Completed Events
+ *
+ * Software issues a command to a hot-plug capable Downstream Port by
+ * issuing a write transaction that targets any portion of the Port’s Slot
+ * Control register. A single write to the Slot Control register is
+ * considered to be a single command, even if the write affects more than
+ * one field in the Slot Control register. In response to this transaction,
+ * the Port must carry out the requested actions and then set the
+ * associated status field for the command completed event. */
+
+ /* Real hardware might take a while to complete requested command because
+ * physical movement would be involved like locking the electromechanical
+ * lock. However in our case, command is completed instantaneously above,
+ * so send a command completion event right now.
+ */
+ pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI);
+}
+
+int pcie_cap_slot_post_load(void *opaque, int version_id)
+{
+ PCIDevice *dev = opaque;
+ hotplug_event_update_event_status(dev);
+ return 0;
+}
+
+void pcie_cap_slot_push_attention_button(PCIDevice *dev)
+{
+ pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP);
+}
+
+/* root control/capabilities/status. PME isn't emulated for now */
+void pcie_cap_root_init(PCIDevice *dev)
+{
+ pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
+ PCI_EXP_RTCTL_SEFEE);
+}
+
+void pcie_cap_root_reset(PCIDevice *dev)
+{
+ pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0);
+}
+
+/* function level reset(FLR) */
+void pcie_cap_flr_init(PCIDevice *dev)
+{
+ pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP,
+ PCI_EXP_DEVCAP_FLR);
+
+ /* Although reading BCR_FLR returns always 0,
+ * the bit is made writable here in order to detect the 1b is written
+ * pcie_cap_flr_write_config() test-and-clear the bit, so
+ * this bit always returns 0 to the guest.
+ */
+ pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_BCR_FLR);
+}
+
+void pcie_cap_flr_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len)
+{
+ uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
+ if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) {
+ /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler
+ so the handler can detect FLR by looking at this bit. */
+ pci_device_reset(dev);
+ pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR);
+ }
+}
+
+/* Alternative Routing-ID Interpretation (ARI) */
+/* ari forwarding support for down stream port */
+void pcie_cap_ari_init(PCIDevice *dev)
+{
+ uint32_t pos = dev->exp.exp_cap;
+ pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2,
+ PCI_EXP_DEVCAP2_ARI);
+ pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2,
+ PCI_EXP_DEVCTL2_ARI);
+}
+
+void pcie_cap_ari_reset(PCIDevice *dev)
+{
+ uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2;
+ pci_long_test_and_clear_mask(devctl2, PCI_EXP_DEVCTL2_ARI);
+}
+
+bool pcie_cap_is_ari_enabled(const PCIDevice *dev)
+{
+ if (!pci_is_express(dev)) {
+ return false;
+ }
+ if (!dev->exp.exp_cap) {
+ return false;
+ }
+
+ return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) &
+ PCI_EXP_DEVCTL2_ARI;
+}
+
+/**************************************************************************
+ * pci express extended capability allocation functions
+ * uint16_t ext_cap_id (16 bit)
+ * uint8_t cap_ver (4 bit)
+ * uint16_t cap_offset (12 bit)
+ * uint16_t ext_cap_size
+ */
+
+static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id,
+ uint16_t *prev_p)
+{
+ uint16_t prev = 0;
+ uint16_t next;
+ uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE);
+
+ if (!header) {
+ /* no extended capability */
+ next = 0;
+ goto out;
+ }
+ for (next = PCI_CONFIG_SPACE_SIZE; next;
+ prev = next, next = PCI_EXT_CAP_NEXT(header)) {
+
+ assert(next >= PCI_CONFIG_SPACE_SIZE);
+ assert(next <= PCIE_CONFIG_SPACE_SIZE - 8);
+
+ header = pci_get_long(dev->config + next);
+ if (PCI_EXT_CAP_ID(header) == cap_id) {
+ break;
+ }
+ }
+
+out:
+ if (prev_p) {
+ *prev_p = prev;
+ }
+ return next;
+}
+
+uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id)
+{
+ return pcie_find_capability_list(dev, cap_id, NULL);
+}
+
+static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next)
+{
+ uint16_t header = pci_get_long(dev->config + pos);
+ assert(!(next & (PCI_EXT_CAP_ALIGN - 1)));
+ header = (header & ~PCI_EXT_CAP_NEXT_MASK) |
+ ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK);
+ pci_set_long(dev->config + pos, header);
+}
+
+/*
+ * caller must supply valid (offset, size) * such that the range shouldn't
+ * overlap with other capability or other registers.
+ * This function doesn't check it.
+ */
+void pcie_add_capability(PCIDevice *dev,
+ uint16_t cap_id, uint8_t cap_ver,
+ uint16_t offset, uint16_t size)
+{
+ uint32_t header;
+ uint16_t next;
+
+ assert(offset >= PCI_CONFIG_SPACE_SIZE);
+ assert(offset < offset + size);
+ assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
+ assert(size >= 8);
+ assert(pci_is_express(dev));
+
+ if (offset == PCI_CONFIG_SPACE_SIZE) {
+ header = pci_get_long(dev->config + offset);
+ next = PCI_EXT_CAP_NEXT(header);
+ } else {
+ uint16_t prev;
+
+ /* 0 is reserved cap id. use internally to find the last capability
+ in the linked list */
+ next = pcie_find_capability_list(dev, 0, &prev);
+
+ assert(prev >= PCI_CONFIG_SPACE_SIZE);
+ assert(next == 0);
+ pcie_ext_cap_set_next(dev, prev, offset);
+ }
+ pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next));
+
+ /* Make capability read-only by default */
+ memset(dev->wmask + offset, 0, size);
+ memset(dev->w1cmask + offset, 0, size);
+ /* Check capability by default */
+ memset(dev->cmask + offset, 0xFF, size);
+}
+
+/**************************************************************************
+ * pci express extended capability helper functions
+ */
+
+/* ARI */
+void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn)
+{
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER,
+ offset, PCI_ARI_SIZEOF);
+ pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn));
+}
diff --git a/hw/pcie.h b/hw/pcie.h
new file mode 100644
index 0000000..bc909e2
--- /dev/null
+++ b/hw/pcie.h
@@ -0,0 +1,132 @@
+/*
+ * pcie.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef QEMU_PCIE_H
+#define QEMU_PCIE_H
+
+#include "hw.h"
+#include "pci_regs.h"
+#include "pcie_regs.h"
+#include "pcie_aer.h"
+
+typedef enum {
+ /* for attention and power indicator */
+ PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED,
+ PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON,
+ PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK,
+ PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF,
+} PCIExpressIndicator;
+
+typedef enum {
+ /* these bits must match the bits in Slot Control/Status registers.
+ * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx
+ *
+ * Not all the bits of slot control register match with the ones of
+ * slot status. Not some bits of slot status register is used to
+ * show status, not to report event occurence.
+ * So such bits must be masked out when checking the software
+ * notification condition.
+ */
+ PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE,
+ /* attention button pressed */
+ PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE,
+ /* presence detect changed */
+ PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE,
+ /* command completed */
+
+ PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP |
+ PCI_EXP_HP_EV_PDC |
+ PCI_EXP_HP_EV_CCI,
+ /* supported event mask */
+
+ /* events not listed aren't supported */
+} PCIExpressHotPlugEvent;
+
+struct PCIExpressDevice {
+ /* Offset of express capability in config space */
+ uint8_t exp_cap;
+
+ /* SLOT */
+ unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#)
+ * default is 0 = INTA#
+ * If the chip wants to use other interrupt
+ * line, initialize this member with the
+ * desired number.
+ * If the chip dynamically changes this member,
+ * also initialize it when loaded as
+ * appropreately.
+ */
+ bool hpev_notified; /* Logical AND of conditions for hot plug event.
+ Following 6.7.3.4:
+ Software Notification of Hot-Plug Events, an interrupt
+ is sent whenever the logical and of these conditions
+ transitions from false to true. */
+
+ /* AER */
+ uint16_t aer_cap;
+ PCIEAERLog aer_log;
+ unsigned int aer_intx; /* INTx for error reporting
+ * default is 0 = INTA#
+ * If the chip wants to use other interrupt
+ * line, initialize this member with the
+ * desired number.
+ * If the chip dynamically changes this member,
+ * also initialize it when loaded as
+ * appropreately.
+ */
+};
+
+/* PCI express capability helper functions */
+int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port);
+void pcie_cap_exit(PCIDevice *dev);
+uint8_t pcie_cap_get_type(const PCIDevice *dev);
+void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector);
+uint8_t pcie_cap_flags_get_vector(PCIDevice *dev);
+
+void pcie_cap_deverr_init(PCIDevice *dev);
+void pcie_cap_deverr_reset(PCIDevice *dev);
+
+void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot);
+void pcie_cap_slot_reset(PCIDevice *dev);
+void pcie_cap_slot_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len);
+int pcie_cap_slot_post_load(void *opaque, int version_id);
+void pcie_cap_slot_push_attention_button(PCIDevice *dev);
+
+void pcie_cap_root_init(PCIDevice *dev);
+void pcie_cap_root_reset(PCIDevice *dev);
+
+void pcie_cap_flr_init(PCIDevice *dev);
+void pcie_cap_flr_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len);
+
+void pcie_cap_ari_init(PCIDevice *dev);
+void pcie_cap_ari_reset(PCIDevice *dev);
+bool pcie_cap_is_ari_enabled(const PCIDevice *dev);
+
+/* PCI express extended capability helper functions */
+uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id);
+void pcie_add_capability(PCIDevice *dev,
+ uint16_t cap_id, uint8_t cap_ver,
+ uint16_t offset, uint16_t size);
+
+void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn);
+
+#endif /* QEMU_PCIE_H */
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c
new file mode 100644
index 0000000..6e653dd
--- /dev/null
+++ b/hw/pcie_aer.c
@@ -0,0 +1,1031 @@
+/*
+ * pcie_aer.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "sysemu.h"
+#include "qemu-objects.h"
+#include "monitor.h"
+#include "pci_bridge.h"
+#include "pcie.h"
+#include "msix.h"
+#include "msi.h"
+#include "pci_internals.h"
+#include "pcie_regs.h"
+
+//#define DEBUG_PCIE
+#ifdef DEBUG_PCIE
+# define PCIE_DPRINTF(fmt, ...) \
+ fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
+#else
+# define PCIE_DPRINTF(fmt, ...) do {} while (0)
+#endif
+#define PCIE_DEV_PRINTF(dev, fmt, ...) \
+ PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
+
+/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */
+static uint32_t pcie_aer_uncor_default_severity(uint32_t status)
+{
+ switch (status) {
+ case PCI_ERR_UNC_INTN:
+ case PCI_ERR_UNC_DLP:
+ case PCI_ERR_UNC_SDN:
+ case PCI_ERR_UNC_RX_OVER:
+ case PCI_ERR_UNC_FCP:
+ case PCI_ERR_UNC_MALF_TLP:
+ return PCI_ERR_ROOT_CMD_FATAL_EN;
+ case PCI_ERR_UNC_POISON_TLP:
+ case PCI_ERR_UNC_ECRC:
+ case PCI_ERR_UNC_UNSUP:
+ case PCI_ERR_UNC_COMP_TIME:
+ case PCI_ERR_UNC_COMP_ABORT:
+ case PCI_ERR_UNC_UNX_COMP:
+ case PCI_ERR_UNC_ACSV:
+ case PCI_ERR_UNC_MCBTLP:
+ case PCI_ERR_UNC_ATOP_EBLOCKED:
+ case PCI_ERR_UNC_TLP_PRF_BLOCKED:
+ return PCI_ERR_ROOT_CMD_NONFATAL_EN;
+ default:
+ abort();
+ break;
+ }
+ return PCI_ERR_ROOT_CMD_FATAL_EN;
+}
+
+static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err)
+{
+ if (aer_log->log_num == aer_log->log_max) {
+ return -1;
+ }
+ memcpy(&aer_log->log[aer_log->log_num], err, sizeof *err);
+ aer_log->log_num++;
+ return 0;
+}
+
+static void aer_log_del_err(PCIEAERLog *aer_log, PCIEAERErr *err)
+{
+ assert(aer_log->log_num);
+ *err = aer_log->log[0];
+ aer_log->log_num--;
+ memmove(&aer_log->log[0], &aer_log->log[1],
+ aer_log->log_num * sizeof *err);
+}
+
+static void aer_log_clear_all_err(PCIEAERLog *aer_log)
+{
+ aer_log->log_num = 0;
+}
+
+int pcie_aer_init(PCIDevice *dev, uint16_t offset)
+{
+ PCIExpressDevice *exp;
+
+ pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER,
+ offset, PCI_ERR_SIZEOF);
+ exp = &dev->exp;
+ exp->aer_cap = offset;
+
+ /* log_max is property */
+ if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) {
+ dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT;
+ }
+ /* clip down the value to avoid unreasobale memory usage */
+ if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) {
+ return -EINVAL;
+ }
+ dev->exp.aer_log.log = qemu_mallocz(sizeof dev->exp.aer_log.log[0] *
+ dev->exp.aer_log.log_max);
+
+ pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS,
+ PCI_ERR_UNC_SUPPORTED);
+
+ pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER,
+ PCI_ERR_UNC_SEVERITY_DEFAULT);
+ pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER,
+ PCI_ERR_UNC_SUPPORTED);
+
+ pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS,
+ PCI_ERR_COR_STATUS);
+
+ pci_set_long(dev->config + offset + PCI_ERR_COR_MASK,
+ PCI_ERR_COR_MASK_DEFAULT);
+ pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK,
+ PCI_ERR_COR_SUPPORTED);
+
+ /* capabilities and control. multiple header logging is supported */
+ if (dev->exp.aer_log.log_max > 0) {
+ pci_set_long(dev->config + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC |
+ PCI_ERR_CAP_MHRC);
+ pci_set_long(dev->wmask + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE |
+ PCI_ERR_CAP_MHRE);
+ } else {
+ pci_set_long(dev->config + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC);
+ pci_set_long(dev->wmask + offset + PCI_ERR_CAP,
+ PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE);
+ }
+
+ switch (pcie_cap_get_type(dev)) {
+ case PCI_EXP_TYPE_ROOT_PORT:
+ /* this case will be set by pcie_aer_root_init() */
+ /* fallthrough */
+ case PCI_EXP_TYPE_DOWNSTREAM:
+ case PCI_EXP_TYPE_UPSTREAM:
+ pci_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL,
+ PCI_BRIDGE_CTL_SERR);
+ pci_long_test_and_set_mask(dev->w1cmask + PCI_STATUS,
+ PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ return 0;
+}
+
+void pcie_aer_exit(PCIDevice *dev)
+{
+ qemu_free(dev->exp.aer_log.log);
+}
+
+static void pcie_aer_update_uncor_status(PCIDevice *dev)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ PCIEAERLog *aer_log = &dev->exp.aer_log;
+
+ uint16_t i;
+ for (i = 0; i < aer_log->log_num; i++) {
+ pci_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS,
+ dev->exp.aer_log.log[i].status);
+ }
+}
+
+/*
+ * return value:
+ * true: error message needs to be sent up
+ * false: error message is masked
+ *
+ * 6.2.6 Error Message Control
+ * Figure 6-3
+ * all pci express devices part
+ */
+static bool
+pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ if (!(pcie_aer_msg_is_uncor(msg) &&
+ (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) {
+ return false;
+ }
+
+ /* Signaled System Error
+ *
+ * 7.5.1.1 Command register
+ * Bit 8 SERR# Enable
+ *
+ * When Set, this bit enables reporting of Non-fatal and Fatal
+ * errors detected by the Function to the Root Complex. Note that
+ * errors are reported if enabled either through this bit or through
+ * the PCI Express specific bits in the Device Control register (see
+ * Section 7.8.4).
+ */
+ pci_word_test_and_set_mask(dev->config + PCI_STATUS,
+ PCI_STATUS_SIG_SYSTEM_ERROR);
+
+ if (!(msg->severity &
+ pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) {
+ return false;
+ }
+
+ /* send up error message */
+ return true;
+}
+
+/*
+ * return value:
+ * true: error message is sent up
+ * false: error message is masked
+ *
+ * 6.2.6 Error Message Control
+ * Figure 6-3
+ * virtual pci bridge part
+ */
+static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL);
+
+ if (pcie_aer_msg_is_uncor(msg)) {
+ /* Received System Error */
+ pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS,
+ PCI_SEC_STATUS_RCV_SYSTEM_ERROR);
+ }
+
+ if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) {
+ return false;
+ }
+ return true;
+}
+
+void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ assert(vector < PCI_ERR_ROOT_IRQ_MAX);
+ pci_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS,
+ PCI_ERR_ROOT_IRQ);
+ pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS,
+ vector << PCI_ERR_ROOT_IRQ_SHIFT);
+}
+
+static unsigned int pcie_aer_root_get_vector(PCIDevice *dev)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+ return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT;
+}
+
+/* Given a status register, get corresponding bits in the command register */
+static uint32_t pcie_aer_status_to_cmd(uint32_t status)
+{
+ uint32_t cmd = 0;
+ if (status & PCI_ERR_ROOT_COR_RCV) {
+ cmd |= PCI_ERR_ROOT_CMD_COR_EN;
+ }
+ if (status & PCI_ERR_ROOT_NONFATAL_RCV) {
+ cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN;
+ }
+ if (status & PCI_ERR_ROOT_FATAL_RCV) {
+ cmd |= PCI_ERR_ROOT_CMD_FATAL_EN;
+ }
+ return cmd;
+}
+
+static void pcie_aer_root_notify(PCIDevice *dev)
+{
+ if (msix_enabled(dev)) {
+ msix_notify(dev, pcie_aer_root_get_vector(dev));
+ } else if (msi_enabled(dev)) {
+ msi_notify(dev, pcie_aer_root_get_vector(dev));
+ } else {
+ qemu_set_irq(dev->irq[dev->exp.aer_intx], 1);
+ }
+}
+
+/*
+ * 6.2.6 Error Message Control
+ * Figure 6-3
+ * root port part
+ */
+static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ uint16_t cmd;
+ uint8_t *aer_cap;
+ uint32_t root_cmd;
+ uint32_t root_status, prev_status;
+
+ cmd = pci_get_word(dev->config + PCI_COMMAND);
+ aer_cap = dev->config + dev->exp.aer_cap;
+ root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
+ prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+
+ if (cmd & PCI_COMMAND_SERR) {
+ /* System Error.
+ *
+ * The way to report System Error is platform specific and
+ * it isn't implemented in qemu right now.
+ * So just discard the error for now.
+ * OS which cares of aer would receive errors via
+ * native aer mechanims, so this wouldn't matter.
+ */
+ }
+
+ /* Errro Message Received: Root Error Status register */
+ switch (msg->severity) {
+ case PCI_ERR_ROOT_CMD_COR_EN:
+ if (root_status & PCI_ERR_ROOT_COR_RCV) {
+ root_status |= PCI_ERR_ROOT_MULTI_COR_RCV;
+ } else {
+ pci_set_word(aer_cap + PCI_ERR_ROOT_COR_SRC, msg->source_id);
+ }
+ root_status |= PCI_ERR_ROOT_COR_RCV;
+ break;
+ case PCI_ERR_ROOT_CMD_NONFATAL_EN:
+ root_status |= PCI_ERR_ROOT_NONFATAL_RCV;
+ break;
+ case PCI_ERR_ROOT_CMD_FATAL_EN:
+ if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) {
+ root_status |= PCI_ERR_ROOT_FIRST_FATAL;
+ }
+ root_status |= PCI_ERR_ROOT_FATAL_RCV;
+ break;
+ default:
+ abort();
+ break;
+ }
+ if (pcie_aer_msg_is_uncor(msg)) {
+ if (root_status & PCI_ERR_ROOT_UNCOR_RCV) {
+ root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV;
+ } else {
+ pci_set_word(aer_cap + PCI_ERR_ROOT_SRC, msg->source_id);
+ }
+ root_status |= PCI_ERR_ROOT_UNCOR_RCV;
+ }
+ pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status);
+
+ /* 6.2.4.1.2 Interrupt Generation */
+ /* All the above did was set some bits in the status register.
+ * Specifically these that match message severity.
+ * The below code relies on this fact. */
+ if (!(root_cmd & msg->severity) ||
+ (pcie_aer_status_to_cmd(prev_status) & root_cmd)) {
+ /* Condition is not being set or was already true so nothing to do. */
+ return;
+ }
+
+ pcie_aer_root_notify(dev);
+}
+
+/*
+ * 6.2.6 Error Message Control Figure 6-3
+ *
+ * Walk up the bus tree from the device, propagate the error message.
+ */
+static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg)
+{
+ uint8_t type;
+
+ while (dev) {
+ if (!pci_is_express(dev)) {
+ /* just ignore it */
+ /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR?
+ * Consider e.g. a PCI bridge above a PCI Express device. */
+ return;
+ }
+
+ type = pcie_cap_get_type(dev);
+ if ((type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_UPSTREAM ||
+ type == PCI_EXP_TYPE_DOWNSTREAM) &&
+ !pcie_aer_msg_vbridge(dev, msg)) {
+ return;
+ }
+ if (!pcie_aer_msg_alldev(dev, msg)) {
+ return;
+ }
+ if (type == PCI_EXP_TYPE_ROOT_PORT) {
+ pcie_aer_msg_root_port(dev, msg);
+ /* Root port can notify system itself,
+ or send the error message to root complex event collector. */
+ /*
+ * if root port is associated with an event collector,
+ * return the root complex event collector here.
+ * For now root complex event collector isn't supported.
+ */
+ return;
+ }
+ dev = pci_bridge_get_device(dev->bus);
+ }
+}
+
+static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ uint8_t first_bit = ffs(err->status) - 1;
+ uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+ int i;
+
+ assert(err->status);
+ assert(err->status & (err->status - 1));
+
+ errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
+ errcap |= PCI_ERR_CAP_FEP(first_bit);
+
+ if (err->flags & PCIE_AER_ERR_HEADER_VALID) {
+ for (i = 0; i < ARRAY_SIZE(err->header); ++i) {
+ /* 7.10.8 Header Log Register */
+ uint8_t *header_log =
+ aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0];
+ cpu_to_be32wu((uint32_t*)header_log, err->header[i]);
+ }
+ } else {
+ assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT));
+ memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE);
+ }
+
+ if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) &&
+ (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) &
+ PCI_EXP_DEVCAP2_EETLPP)) {
+ for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) {
+ /* 7.10.12 tlp prefix log register */
+ uint8_t *prefix_log =
+ aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0];
+ cpu_to_be32wu((uint32_t*)prefix_log, err->prefix[i]);
+ }
+ errcap |= PCI_ERR_CAP_TLP;
+ } else {
+ memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0,
+ PCI_ERR_TLP_PREFIX_LOG_SIZE);
+ }
+ pci_set_long(aer_cap + PCI_ERR_CAP, errcap);
+}
+
+static void pcie_aer_clear_log(PCIDevice *dev)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+
+ pci_long_test_and_clear_mask(aer_cap + PCI_ERR_CAP,
+ PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
+
+ memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE);
+ memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, PCI_ERR_TLP_PREFIX_LOG_SIZE);
+}
+
+static void pcie_aer_clear_error(PCIDevice *dev)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+ PCIEAERLog *aer_log = &dev->exp.aer_log;
+ PCIEAERErr err;
+
+ if (!(errcap & PCI_ERR_CAP_MHRE) || !aer_log->log_num) {
+ pcie_aer_clear_log(dev);
+ return;
+ }
+
+ /*
+ * If more errors are queued, set corresponding bits in uncorrectable
+ * error status.
+ * We emulate uncorrectable error status register as W1CS.
+ * So set bit in uncorrectable error status here again for multiple
+ * error recording support.
+ *
+ * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability)
+ */
+ pcie_aer_update_uncor_status(dev);
+
+ aer_log_del_err(aer_log, &err);
+ pcie_aer_update_log(dev, &err);
+}
+
+static int pcie_aer_record_error(PCIDevice *dev,
+ const PCIEAERErr *err)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+ int fep = PCI_ERR_CAP_FEP(errcap);
+
+ assert(err->status);
+ assert(err->status & (err->status - 1));
+
+ if (errcap & PCI_ERR_CAP_MHRE &&
+ (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) {
+ /* Not first error. queue error */
+ if (aer_log_add_err(&dev->exp.aer_log, err) < 0) {
+ /* overflow */
+ return -1;
+ }
+ return 0;
+ }
+
+ pcie_aer_update_log(dev, err);
+ return 0;
+}
+
+typedef struct PCIEAERInject {
+ PCIDevice *dev;
+ uint8_t *aer_cap;
+ const PCIEAERErr *err;
+ uint16_t devctl;
+ uint16_t devsta;
+ uint32_t error_status;
+ bool unsupported_request;
+ bool log_overflow;
+ PCIEAERMsg msg;
+} PCIEAERInject;
+
+static bool pcie_aer_inject_cor_error(PCIEAERInject *inj,
+ uint32_t uncor_status,
+ bool is_advisory_nonfatal)
+{
+ PCIDevice *dev = inj->dev;
+
+ inj->devsta |= PCI_EXP_DEVSTA_CED;
+ if (inj->unsupported_request) {
+ inj->devsta |= PCI_EXP_DEVSTA_URD;
+ }
+ pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta);
+
+ if (inj->aer_cap) {
+ uint32_t mask;
+ pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_COR_STATUS,
+ inj->error_status);
+ mask = pci_get_long(inj->aer_cap + PCI_ERR_COR_MASK);
+ if (mask & inj->error_status) {
+ return false;
+ }
+ if (is_advisory_nonfatal) {
+ uint32_t uncor_mask =
+ pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK);
+ if (!(uncor_mask & uncor_status)) {
+ inj->log_overflow = !!pcie_aer_record_error(dev, inj->err);
+ }
+ pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS,
+ uncor_status);
+ }
+ }
+
+ if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE)) {
+ return false;
+ }
+ if (!(inj->devctl & PCI_EXP_DEVCTL_CERE)) {
+ return false;
+ }
+
+ inj->msg.severity = PCI_ERR_ROOT_CMD_COR_EN;
+ return true;
+}
+
+static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal)
+{
+ PCIDevice *dev = inj->dev;
+ uint16_t cmd;
+
+ if (is_fatal) {
+ inj->devsta |= PCI_EXP_DEVSTA_FED;
+ } else {
+ inj->devsta |= PCI_EXP_DEVSTA_NFED;
+ }
+ if (inj->unsupported_request) {
+ inj->devsta |= PCI_EXP_DEVSTA_URD;
+ }
+ pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta);
+
+ if (inj->aer_cap) {
+ uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK);
+ if (mask & inj->error_status) {
+ pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS,
+ inj->error_status);
+ return false;
+ }
+
+ inj->log_overflow = !!pcie_aer_record_error(dev, inj->err);
+ pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS,
+ inj->error_status);
+ }
+
+ cmd = pci_get_word(dev->config + PCI_COMMAND);
+ if (inj->unsupported_request &&
+ !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) {
+ return false;
+ }
+ if (is_fatal) {
+ if (!((cmd & PCI_COMMAND_SERR) ||
+ (inj->devctl & PCI_EXP_DEVCTL_FERE))) {
+ return false;
+ }
+ inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN;
+ } else {
+ if (!((cmd & PCI_COMMAND_SERR) ||
+ (inj->devctl & PCI_EXP_DEVCTL_NFERE))) {
+ return false;
+ }
+ inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN;
+ }
+ return true;
+}
+
+/*
+ * non-Function specific error must be recorded in all functions.
+ * It is the responsibility of the caller of this function.
+ * It is also caller's responsiblity to determine which function should
+ * report the rerror.
+ *
+ * 6.2.4 Error Logging
+ * 6.2.5 Sqeunce of Device Error Signaling and Logging Operations
+ * table 6-2: Flowchard Showing Sequence of Device Error Signaling and Logging
+ * Operations
+ */
+int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err)
+{
+ uint8_t *aer_cap = NULL;
+ uint16_t devctl = 0;
+ uint16_t devsta = 0;
+ uint32_t error_status = err->status;
+ PCIEAERInject inj;
+
+ if (!pci_is_express(dev)) {
+ return -ENOSYS;
+ }
+
+ if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) {
+ error_status &= PCI_ERR_COR_SUPPORTED;
+ } else {
+ error_status &= PCI_ERR_UNC_SUPPORTED;
+ }
+
+ /* invalid status bit. one and only one bit must be set */
+ if (!error_status || (error_status & (error_status - 1))) {
+ return -EINVAL;
+ }
+
+ if (dev->exp.aer_cap) {
+ uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
+ aer_cap = dev->config + dev->exp.aer_cap;
+ devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL);
+ devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA);
+ }
+
+ inj.dev = dev;
+ inj.aer_cap = aer_cap;
+ inj.err = err;
+ inj.devctl = devctl;
+ inj.devsta = devsta;
+ inj.error_status = error_status;
+ inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) &&
+ err->status == PCI_ERR_UNC_UNSUP;
+ inj.log_overflow = false;
+
+ if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) {
+ if (!pcie_aer_inject_cor_error(&inj, 0, false)) {
+ return 0;
+ }
+ } else {
+ bool is_fatal =
+ pcie_aer_uncor_default_severity(error_status) ==
+ PCI_ERR_ROOT_CMD_FATAL_EN;
+ if (aer_cap) {
+ is_fatal =
+ error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER);
+ }
+ if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) {
+ inj.error_status = PCI_ERR_COR_ADV_NONFATAL;
+ if (!pcie_aer_inject_cor_error(&inj, error_status, true)) {
+ return 0;
+ }
+ } else {
+ if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) {
+ return 0;
+ }
+ }
+ }
+
+ /* send up error message */
+ inj.msg.source_id = err->source_id;
+ pcie_aer_msg(dev, &inj.msg);
+
+ if (inj.log_overflow) {
+ PCIEAERErr header_log_overflow = {
+ .status = PCI_ERR_COR_HL_OVERFLOW,
+ .flags = PCIE_AER_ERR_IS_CORRECTABLE,
+ };
+ int ret = pcie_aer_inject_error(dev, &header_log_overflow);
+ assert(!ret);
+ }
+ return 0;
+}
+
+void pcie_aer_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP);
+ uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap);
+ uint32_t uncorsta = pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS);
+
+ /* uncorrectable error */
+ if (!(uncorsta & first_error)) {
+ /* the bit that corresponds to the first error is cleared */
+ pcie_aer_clear_error(dev);
+ } else if (errcap & PCI_ERR_CAP_MHRE) {
+ /* When PCI_ERR_CAP_MHRE is enabled and the first error isn't cleared
+ * nothing should happen. So we have to revert the modification to
+ * the register.
+ */
+ pcie_aer_update_uncor_status(dev);
+ } else {
+ /* capability & control
+ * PCI_ERR_CAP_MHRE might be cleared, so clear of header log.
+ */
+ aer_log_clear_all_err(&dev->exp.aer_log);
+ }
+}
+
+void pcie_aer_root_init(PCIDevice *dev)
+{
+ uint16_t pos = dev->exp.aer_cap;
+
+ pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND,
+ PCI_ERR_ROOT_CMD_EN_MASK);
+ pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS,
+ PCI_ERR_ROOT_STATUS_REPORT_MASK);
+}
+
+void pcie_aer_root_reset(PCIDevice *dev)
+{
+ uint8_t* aer_cap = dev->config + dev->exp.aer_cap;
+
+ pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0);
+
+ /*
+ * Advanced Error Interrupt Message Number in Root Error Status Register
+ * must be updated by chip dependent code because it's chip dependent
+ * which number is used.
+ */
+}
+
+void pcie_aer_root_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len,
+ uint32_t root_cmd_prev)
+{
+ uint8_t *aer_cap = dev->config + dev->exp.aer_cap;
+ uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS);
+ uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status);
+ uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND);
+ /* 6.2.4.1.2 Interrupt Generation */
+ if (!msix_enabled(dev) && !msi_enabled(dev)) {
+ qemu_set_irq(dev->irq[dev->exp.aer_intx], !!(root_cmd & enabled_cmd));
+ return;
+ }
+
+ if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) {
+ /* Send MSI on transition from false to true. */
+ return;
+ }
+
+ pcie_aer_root_notify(dev);
+}
+
+static const VMStateDescription vmstate_pcie_aer_err = {
+ .name = "PCIE_AER_ERROR",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(status, PCIEAERErr),
+ VMSTATE_UINT16(source_id, PCIEAERErr),
+ VMSTATE_UINT16(flags, PCIEAERErr),
+ VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4),
+ VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define VMSTATE_PCIE_AER_ERRS(_field, _state, _field_num, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = 0, \
+ .num_offset = vmstate_offset_value(_state, _field_num, uint16_t), \
+ .size = sizeof(_type), \
+ .vmsd = &(_vmsd), \
+ .flags = VMS_POINTER | VMS_VARRAY_UINT16 | VMS_STRUCT, \
+ .offset = vmstate_offset_pointer(_state, _field, _type), \
+}
+
+const VMStateDescription vmstate_pcie_aer_log = {
+ .name = "PCIE_AER_ERROR_LOG",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(log_num, PCIEAERLog),
+ VMSTATE_UINT16(log_max, PCIEAERLog),
+ VMSTATE_PCIE_AER_ERRS(log, PCIEAERLog, log_num,
+ vmstate_pcie_aer_err, PCIEAERErr),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+ int devfn;
+ assert(qobject_type(data) == QTYPE_QDICT);
+ qdict = qobject_to_qdict(data);
+
+ devfn = (int)qdict_get_int(qdict, "devfn");
+ monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n",
+ qdict_get_str(qdict, "id"),
+ (int) qdict_get_int(qdict, "domain"),
+ (int) qdict_get_int(qdict, "bus"),
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+}
+
+typedef struct PCIEAERErrorName {
+ const char *name;
+ uint32_t val;
+ bool correctable;
+} PCIEAERErrorName;
+
+/*
+ * AER error name -> value convertion table
+ * This naming scheme is same to linux aer-injection tool.
+ */
+static const struct PCIEAERErrorName pcie_aer_error_list[] = {
+ {
+ .name = "TRAIN",
+ .val = PCI_ERR_UNC_TRAIN,
+ .correctable = false,
+ }, {
+ .name = "DLP",
+ .val = PCI_ERR_UNC_DLP,
+ .correctable = false,
+ }, {
+ .name = "SDN",
+ .val = PCI_ERR_UNC_SDN,
+ .correctable = false,
+ }, {
+ .name = "POISON_TLP",
+ .val = PCI_ERR_UNC_POISON_TLP,
+ .correctable = false,
+ }, {
+ .name = "FCP",
+ .val = PCI_ERR_UNC_FCP,
+ .correctable = false,
+ }, {
+ .name = "COMP_TIME",
+ .val = PCI_ERR_UNC_COMP_TIME,
+ .correctable = false,
+ }, {
+ .name = "COMP_ABORT",
+ .val = PCI_ERR_UNC_COMP_ABORT,
+ .correctable = false,
+ }, {
+ .name = "UNX_COMP",
+ .val = PCI_ERR_UNC_UNX_COMP,
+ .correctable = false,
+ }, {
+ .name = "RX_OVER",
+ .val = PCI_ERR_UNC_RX_OVER,
+ .correctable = false,
+ }, {
+ .name = "MALF_TLP",
+ .val = PCI_ERR_UNC_MALF_TLP,
+ .correctable = false,
+ }, {
+ .name = "ECRC",
+ .val = PCI_ERR_UNC_ECRC,
+ .correctable = false,
+ }, {
+ .name = "UNSUP",
+ .val = PCI_ERR_UNC_UNSUP,
+ .correctable = false,
+ }, {
+ .name = "ACSV",
+ .val = PCI_ERR_UNC_ACSV,
+ .correctable = false,
+ }, {
+ .name = "INTN",
+ .val = PCI_ERR_UNC_INTN,
+ .correctable = false,
+ }, {
+ .name = "MCBTLP",
+ .val = PCI_ERR_UNC_MCBTLP,
+ .correctable = false,
+ }, {
+ .name = "ATOP_EBLOCKED",
+ .val = PCI_ERR_UNC_ATOP_EBLOCKED,
+ .correctable = false,
+ }, {
+ .name = "TLP_PRF_BLOCKED",
+ .val = PCI_ERR_UNC_TLP_PRF_BLOCKED,
+ .correctable = false,
+ }, {
+ .name = "RCVR",
+ .val = PCI_ERR_COR_RCVR,
+ .correctable = true,
+ }, {
+ .name = "BAD_TLP",
+ .val = PCI_ERR_COR_BAD_TLP,
+ .correctable = true,
+ }, {
+ .name = "BAD_DLLP",
+ .val = PCI_ERR_COR_BAD_DLLP,
+ .correctable = true,
+ }, {
+ .name = "REP_ROLL",
+ .val = PCI_ERR_COR_REP_ROLL,
+ .correctable = true,
+ }, {
+ .name = "REP_TIMER",
+ .val = PCI_ERR_COR_REP_TIMER,
+ .correctable = true,
+ }, {
+ .name = "ADV_NONFATAL",
+ .val = PCI_ERR_COR_ADV_NONFATAL,
+ .correctable = true,
+ }, {
+ .name = "INTERNAL",
+ .val = PCI_ERR_COR_INTERNAL,
+ .correctable = true,
+ }, {
+ .name = "HL_OVERFLOW",
+ .val = PCI_ERR_COR_HL_OVERFLOW,
+ .correctable = true,
+ },
+};
+
+static int pcie_aer_parse_error_string(const char *error_name,
+ uint32_t *status, bool *correctable)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) {
+ const PCIEAERErrorName *e = &pcie_aer_error_list[i];
+ if (strcmp(error_name, e->name)) {
+ continue;
+ }
+
+ *status = e->val;
+ *correctable = e->correctable;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int do_pcie_aer_inejct_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ const char *error_name;
+ uint32_t error_status;
+ bool correctable;
+ PCIDevice *dev;
+ PCIEAERErr err;
+ int ret;
+
+ ret = pci_qdev_find_device(id, &dev);
+ if (ret < 0) {
+ monitor_printf(mon,
+ "id or pci device path is invalid or device not "
+ "found. %s\n", id);
+ return ret;
+ }
+ if (!pci_is_express(dev)) {
+ monitor_printf(mon, "the device doesn't support pci express. %s\n",
+ id);
+ return -ENOSYS;
+ }
+
+ error_name = qdict_get_str(qdict, "error_status");
+ if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) {
+ char *e = NULL;
+ error_status = strtoul(error_name, &e, 0);
+ correctable = !!qdict_get_int(qdict, "correctable");
+ if (!e || *e != '\0') {
+ monitor_printf(mon, "invalid error status value. \"%s\"",
+ error_name);
+ return -EINVAL;
+ }
+ }
+ err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
+
+ err.flags = 0;
+ if (correctable) {
+ err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
+ }
+ if (qdict_get_int(qdict, "advisory_non_fatal")) {
+ err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
+ }
+ if (qdict_haskey(qdict, "header0")) {
+ err.flags |= PCIE_AER_ERR_HEADER_VALID;
+ }
+ if (qdict_haskey(qdict, "prefix0")) {
+ err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT;
+ }
+
+ err.header[0] = qdict_get_try_int(qdict, "header0", 0);
+ err.header[1] = qdict_get_try_int(qdict, "header1", 0);
+ err.header[2] = qdict_get_try_int(qdict, "header2", 0);
+ err.header[3] = qdict_get_try_int(qdict, "header3", 0);
+
+ err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0);
+ err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0);
+ err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0);
+ err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0);
+
+ ret = pcie_aer_inject_error(dev, &err);
+ *ret_data = qobject_from_jsonf("{'id': %s, "
+ "'domain': %d, 'bus': %d, 'devfn': %d, "
+ "'ret': %d}",
+ id,
+ pci_find_domain(dev->bus),
+ pci_bus_num(dev->bus), dev->devfn,
+ ret);
+ assert(*ret_data);
+
+ return 0;
+}
diff --git a/hw/pcie_aer.h b/hw/pcie_aer.h
new file mode 100644
index 0000000..7539500
--- /dev/null
+++ b/hw/pcie_aer.h
@@ -0,0 +1,106 @@
+/*
+ * pcie_aer.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef QEMU_PCIE_AER_H
+#define QEMU_PCIE_AER_H
+
+#include "hw.h"
+
+/* definitions which PCIExpressDevice uses */
+
+/* AER log */
+struct PCIEAERLog {
+ /* This structure is saved/loaded.
+ So explicitly size them instead of unsigned int */
+
+ /* the number of currently recorded log in log member */
+ uint16_t log_num;
+
+ /*
+ * The maximum number of the log. Errors can be logged up to this.
+ *
+ * This is configurable property.
+ * The specified value will be clipped down to PCIE_AER_LOG_MAX_LIMIT
+ * to avoid unreasonable memory usage.
+ * I bet that 128 log size would be big enough, otherwise too many errors
+ * for system to function normaly. But could consecutive errors occur?
+ */
+#define PCIE_AER_LOG_MAX_DEFAULT 8
+#define PCIE_AER_LOG_MAX_LIMIT 128
+#define PCIE_AER_LOG_MAX_UNSET 0xffff
+ uint16_t log_max;
+
+ /* Error log. log_max-sized array */
+ PCIEAERErr *log;
+};
+
+/* aer error message: error signaling message has only error sevirity and
+ source id. See 2.2.8.3 error signaling messages */
+struct PCIEAERMsg {
+ /*
+ * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN
+ * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE}
+ */
+ uint32_t severity;
+
+ uint16_t source_id; /* bdf */
+};
+
+static inline bool
+pcie_aer_msg_is_uncor(const PCIEAERMsg *msg)
+{
+ return msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN ||
+ msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN;
+}
+
+/* error */
+struct PCIEAERErr {
+ uint32_t status; /* error status bits */
+ uint16_t source_id; /* bdf */
+
+#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */
+#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */
+#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */
+#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */
+ uint16_t flags;
+
+ uint32_t header[4]; /* TLP header */
+ uint32_t prefix[4]; /* TLP header prefix */
+};
+
+extern const VMStateDescription vmstate_pcie_aer_log;
+
+int pcie_aer_init(PCIDevice *dev, uint16_t offset);
+void pcie_aer_exit(PCIDevice *dev);
+void pcie_aer_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len);
+
+/* aer root port */
+void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector);
+void pcie_aer_root_init(PCIDevice *dev);
+void pcie_aer_root_reset(PCIDevice *dev);
+void pcie_aer_root_write_config(PCIDevice *dev,
+ uint32_t addr, uint32_t val, int len,
+ uint32_t root_cmd_prev);
+
+/* error injection */
+int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err);
+
+#endif /* QEMU_PCIE_AER_H */
diff --git a/hw/pcie_host.c b/hw/pcie_host.c
new file mode 100644
index 0000000..21069ee
--- /dev/null
+++ b/hw/pcie_host.c
@@ -0,0 +1,177 @@
+/*
+ * pcie_host.c
+ * utility functions for pci express host bridge.
+ *
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "pcie_host.h"
+
+/*
+ * PCI express mmcfig address
+ * bit 20 - 28: bus number
+ * bit 15 - 19: device number
+ * bit 12 - 14: function number
+ * bit 0 - 11: offset in configuration space of a given device
+ */
+#define PCIE_MMCFG_SIZE_MAX (1ULL << 28)
+#define PCIE_MMCFG_SIZE_MIN (1ULL << 20)
+#define PCIE_MMCFG_BUS_BIT 20
+#define PCIE_MMCFG_BUS_MASK 0x1ff
+#define PCIE_MMCFG_DEVFN_BIT 12
+#define PCIE_MMCFG_DEVFN_MASK 0xff
+#define PCIE_MMCFG_CONFOFFSET_MASK 0xfff
+#define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \
+ PCIE_MMCFG_BUS_MASK)
+#define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \
+ PCIE_MMCFG_DEVFN_MASK)
+#define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK)
+
+
+/* a helper function to get a PCIDevice for a given mmconfig address */
+static inline PCIDevice *pcie_dev_find_by_mmcfg_addr(PCIBus *s,
+ uint32_t mmcfg_addr)
+{
+ return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr),
+ PCI_SLOT(PCIE_MMCFG_DEVFN(mmcfg_addr)),
+ PCI_FUNC(PCIE_MMCFG_DEVFN(mmcfg_addr)));
+}
+
+static void pcie_mmcfg_data_write(PCIBus *s,
+ uint32_t mmcfg_addr, uint32_t val, int len)
+{
+ PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, mmcfg_addr);
+
+ if (!pci_dev)
+ return;
+
+ pci_dev->config_write(pci_dev,
+ PCIE_MMCFG_CONFOFFSET(mmcfg_addr), val, len);
+}
+
+static uint32_t pcie_mmcfg_data_read(PCIBus *s, uint32_t addr, int len)
+{
+ PCIDevice *pci_dev = pcie_dev_find_by_mmcfg_addr(s, addr);
+
+ assert(len == 1 || len == 2 || len == 4);
+ if (!pci_dev) {
+ return ~0x0;
+ }
+ return pci_dev->config_read(pci_dev, PCIE_MMCFG_CONFOFFSET(addr), len);
+}
+
+static void pcie_mmcfg_data_writeb(void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ PCIExpressHost *e = opaque;
+ pcie_mmcfg_data_write(e->pci.bus, addr - e->base_addr, value, 1);
+}
+
+static void pcie_mmcfg_data_writew(void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ PCIExpressHost *e = opaque;
+ pcie_mmcfg_data_write(e->pci.bus, addr - e->base_addr, value, 2);
+}
+
+static void pcie_mmcfg_data_writel(void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ PCIExpressHost *e = opaque;
+ pcie_mmcfg_data_write(e->pci.bus, addr - e->base_addr, value, 4);
+}
+
+static uint32_t pcie_mmcfg_data_readb(void *opaque, target_phys_addr_t addr)
+{
+ PCIExpressHost *e = opaque;
+ return pcie_mmcfg_data_read(e->pci.bus, addr - e->base_addr, 1);
+}
+
+static uint32_t pcie_mmcfg_data_readw(void *opaque, target_phys_addr_t addr)
+{
+ PCIExpressHost *e = opaque;
+ return pcie_mmcfg_data_read(e->pci.bus, addr - e->base_addr, 2);
+}
+
+static uint32_t pcie_mmcfg_data_readl(void *opaque, target_phys_addr_t addr)
+{
+ PCIExpressHost *e = opaque;
+ return pcie_mmcfg_data_read(e->pci.bus, addr - e->base_addr, 4);
+}
+
+
+static CPUWriteMemoryFunc * const pcie_mmcfg_write[] =
+{
+ pcie_mmcfg_data_writeb,
+ pcie_mmcfg_data_writew,
+ pcie_mmcfg_data_writel,
+};
+
+static CPUReadMemoryFunc * const pcie_mmcfg_read[] =
+{
+ pcie_mmcfg_data_readb,
+ pcie_mmcfg_data_readw,
+ pcie_mmcfg_data_readl,
+};
+
+/* pcie_host::base_addr == PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. */
+#define PCIE_BASE_ADDR_UNMAPPED ((target_phys_addr_t)-1ULL)
+
+int pcie_host_init(PCIExpressHost *e)
+{
+ e->base_addr = PCIE_BASE_ADDR_UNMAPPED;
+ e->mmio_index =
+ cpu_register_io_memory(pcie_mmcfg_read, pcie_mmcfg_write, e,
+ DEVICE_NATIVE_ENDIAN);
+ if (e->mmio_index < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void pcie_host_mmcfg_unmap(PCIExpressHost *e)
+{
+ if (e->base_addr != PCIE_BASE_ADDR_UNMAPPED) {
+ cpu_register_physical_memory(e->base_addr, e->size, IO_MEM_UNASSIGNED);
+ e->base_addr = PCIE_BASE_ADDR_UNMAPPED;
+ }
+}
+
+void pcie_host_mmcfg_map(PCIExpressHost *e,
+ target_phys_addr_t addr, uint32_t size)
+{
+ assert(!(size & (size - 1))); /* power of 2 */
+ assert(size >= PCIE_MMCFG_SIZE_MIN);
+ assert(size <= PCIE_MMCFG_SIZE_MAX);
+
+ e->base_addr = addr;
+ e->size = size;
+ cpu_register_physical_memory(e->base_addr, e->size, e->mmio_index);
+}
+
+void pcie_host_mmcfg_update(PCIExpressHost *e,
+ int enable,
+ target_phys_addr_t addr, uint32_t size)
+{
+ pcie_host_mmcfg_unmap(e);
+ if (enable) {
+ pcie_host_mmcfg_map(e, addr, size);
+ }
+}
diff --git a/hw/pcie_host.h b/hw/pcie_host.h
new file mode 100644
index 0000000..a202661
--- /dev/null
+++ b/hw/pcie_host.h
@@ -0,0 +1,49 @@
+/*
+ * pcie_host.h
+ *
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program 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 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/>.
+ */
+
+#ifndef PCIE_HOST_H
+#define PCIE_HOST_H
+
+#include "pci_host.h"
+
+struct PCIExpressHost {
+ PCIHostState pci;
+
+ /* express part */
+
+ /* base address where MMCONFIG area is mapped. */
+ target_phys_addr_t base_addr;
+
+ /* the size of MMCONFIG area. It's host bridge dependent */
+ target_phys_addr_t size;
+
+ /* result of cpu_register_io_memory() to map MMCONFIG area */
+ int mmio_index;
+};
+
+int pcie_host_init(PCIExpressHost *e);
+void pcie_host_mmcfg_unmap(PCIExpressHost *e);
+void pcie_host_mmcfg_map(PCIExpressHost *e,
+ target_phys_addr_t addr, uint32_t size);
+void pcie_host_mmcfg_update(PCIExpressHost *e,
+ int enable,
+ target_phys_addr_t addr, uint32_t size);
+
+#endif /* PCIE_HOST_H */
diff --git a/hw/pcie_port.c b/hw/pcie_port.c
new file mode 100644
index 0000000..340dcdb
--- /dev/null
+++ b/hw/pcie_port.c
@@ -0,0 +1,124 @@
+/*
+ * pcie_port.c
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "pcie_port.h"
+
+void pcie_port_init_reg(PCIDevice *d)
+{
+ /* Unlike pci bridge,
+ 66MHz and fast back to back don't apply to pci express port. */
+ pci_set_word(d->config + PCI_STATUS, 0);
+ pci_set_word(d->config + PCI_SEC_STATUS, 0);
+
+ /* Unlike conventional pci bridge, some bits are hardwared to 0. */
+ pci_set_word(d->wmask + PCI_BRIDGE_CONTROL,
+ PCI_BRIDGE_CTL_PARITY |
+ PCI_BRIDGE_CTL_ISA |
+ PCI_BRIDGE_CTL_VGA |
+ PCI_BRIDGE_CTL_SERR |
+ PCI_BRIDGE_CTL_BUS_RESET);
+
+ /* 7.5.3.5 Prefetchable Memory Base Limit
+ * The Prefetchable Memory Base and Prefetchable Memory Limit registers
+ * must indicate that 64-bit addresses are supported, as defined in
+ * PCI-to-PCI Bridge Architecture Specification, Revision 1.2.
+ */
+ pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_TYPE_64);
+ pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_TYPE_64);
+}
+
+/**************************************************************************
+ * (chassis number, pcie physical slot number) -> pcie slot conversion
+ */
+struct PCIEChassis {
+ uint8_t number;
+
+ QLIST_HEAD(, PCIESlot) slots;
+ QLIST_ENTRY(PCIEChassis) next;
+};
+
+static QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis);
+
+static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number)
+{
+ struct PCIEChassis *c;
+ QLIST_FOREACH(c, &chassis, next) {
+ if (c->number == chassis_number) {
+ break;
+ }
+ }
+ return c;
+}
+
+void pcie_chassis_create(uint8_t chassis_number)
+{
+ struct PCIEChassis *c;
+ c = pcie_chassis_find(chassis_number);
+ if (c) {
+ return;
+ }
+ c = qemu_mallocz(sizeof(*c));
+ c->number = chassis_number;
+ QLIST_INIT(&c->slots);
+ QLIST_INSERT_HEAD(&chassis, c, next);
+}
+
+static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c,
+ uint8_t slot)
+{
+ PCIESlot *s;
+ QLIST_FOREACH(s, &c->slots, next) {
+ if (s->slot == slot) {
+ break;
+ }
+ }
+ return s;
+}
+
+PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot)
+{
+ struct PCIEChassis *c;
+ c = pcie_chassis_find(chassis_number);
+ if (!c) {
+ return NULL;
+ }
+ return pcie_chassis_find_slot_with_chassis(c, slot);
+}
+
+int pcie_chassis_add_slot(struct PCIESlot *slot)
+{
+ struct PCIEChassis *c;
+ c = pcie_chassis_find(slot->chassis);
+ if (!c) {
+ return -ENODEV;
+ }
+ if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) {
+ return -EBUSY;
+ }
+ QLIST_INSERT_HEAD(&c->slots, slot, next);
+ return 0;
+}
+
+void pcie_chassis_del_slot(PCIESlot *s)
+{
+ QLIST_REMOVE(s, next);
+}
diff --git a/hw/pcie_port.h b/hw/pcie_port.h
new file mode 100644
index 0000000..3709583
--- /dev/null
+++ b/hw/pcie_port.h
@@ -0,0 +1,51 @@
+/*
+ * pcie_port.h
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef QEMU_PCIE_PORT_H
+#define QEMU_PCIE_PORT_H
+
+#include "pci_bridge.h"
+#include "pci_internals.h"
+
+struct PCIEPort {
+ PCIBridge br;
+
+ /* pci express switch port */
+ uint8_t port;
+};
+
+void pcie_port_init_reg(PCIDevice *d);
+
+struct PCIESlot {
+ PCIEPort port;
+
+ /* pci express switch port with slot */
+ uint8_t chassis;
+ uint16_t slot;
+ QLIST_ENTRY(PCIESlot) next;
+};
+
+void pcie_chassis_create(uint8_t chassis_number);
+void pcie_main_chassis_create(void);
+PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot);
+int pcie_chassis_add_slot(struct PCIESlot *slot);
+void pcie_chassis_del_slot(PCIESlot *s);
+
+#endif /* QEMU_PCIE_PORT_H */
diff --git a/hw/pcie_regs.h b/hw/pcie_regs.h
new file mode 100644
index 0000000..4d123d9
--- /dev/null
+++ b/hw/pcie_regs.h
@@ -0,0 +1,156 @@
+/*
+ * constants for pcie configurations space from pci express spec.
+ *
+ * TODO:
+ * Those constants and macros should go to Linux pci_regs.h
+ * Once they're merged, they will go away.
+ */
+#ifndef QEMU_PCIE_REGS_H
+#define QEMU_PCIE_REGS_H
+
+
+/* express capability */
+
+#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */
+#define PCI_EXT_CAP_VER_SHIFT 16
+#define PCI_EXT_CAP_NEXT_SHIFT 20
+#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT)
+
+#define PCI_EXT_CAP(id, ver, next) \
+ ((id) | \
+ ((ver) << PCI_EXT_CAP_VER_SHIFT) | \
+ ((next) << PCI_EXT_CAP_NEXT_SHIFT))
+
+#define PCI_EXT_CAP_ALIGN 4
+#define PCI_EXT_CAP_ALIGNUP(x) \
+ (((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1))
+
+/* PCI_EXP_FLAGS */
+#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */
+#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1)
+#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1)
+
+
+/* PCI_EXP_LINK{CAP, STA} */
+/* link speed */
+#define PCI_EXP_LNK_LS_25 1
+
+#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1)
+#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT)
+
+/* PCI_EXP_LINKCAP */
+#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1)
+#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT)
+
+#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1)
+
+#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1)
+
+#define PCI_EXP_SLTCTL_IND_RESERVED 0x0
+#define PCI_EXP_SLTCTL_IND_ON 0x1
+#define PCI_EXP_SLTCTL_IND_BLINK 0x2
+#define PCI_EXP_SLTCTL_IND_OFF 0x3
+#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1)
+#define PCI_EXP_SLTCTL_AIC_OFF \
+ (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT)
+
+#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1)
+#define PCI_EXP_SLTCTL_PIC_OFF \
+ (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT)
+
+#define PCI_EXP_SLTCTL_SUPPORTED \
+ (PCI_EXP_SLTCTL_ABPE | \
+ PCI_EXP_SLTCTL_PDCE | \
+ PCI_EXP_SLTCTL_CCIE | \
+ PCI_EXP_SLTCTL_HPIE | \
+ PCI_EXP_SLTCTL_AIC | \
+ PCI_EXP_SLTCTL_PCC | \
+ PCI_EXP_SLTCTL_EIC)
+
+#define PCI_EXP_DEVCAP2_EFF 0x100000
+#define PCI_EXP_DEVCAP2_EETLPP 0x200000
+
+#define PCI_EXP_DEVCTL2_EETLPPB 0x80
+
+/* ARI */
+#define PCI_ARI_VER 1
+#define PCI_ARI_SIZEOF 8
+
+/* AER */
+#define PCI_ERR_VER 2
+#define PCI_ERR_SIZEOF 0x48
+
+#define PCI_ERR_UNC_SDN 0x00000020 /* surprise down */
+#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */
+#define PCI_ERR_UNC_INTN 0x00400000 /* Internal Error */
+#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC Blcoked TLP */
+#define PCI_ERR_UNC_ATOP_EBLOCKED 0x01000000 /* atomic op egress blocked */
+#define PCI_ERR_UNC_TLP_PRF_BLOCKED 0x02000000 /* TLP Prefix Blocked */
+#define PCI_ERR_COR_ADV_NONFATAL 0x00002000 /* Advisory Non-Fatal */
+#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */
+#define PCI_ERR_COR_HL_OVERFLOW 0x00008000 /* Header Long Overflow */
+#define PCI_ERR_CAP_FEP_MASK 0x0000001f
+#define PCI_ERR_CAP_MHRC 0x00000200
+#define PCI_ERR_CAP_MHRE 0x00000400
+#define PCI_ERR_CAP_TLP 0x00000800
+
+#define PCI_ERR_HEADER_LOG_SIZE 16
+#define PCI_ERR_TLP_PREFIX_LOG 0x38
+#define PCI_ERR_TLP_PREFIX_LOG_SIZE 16
+
+#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000
+
+/* aer root error command/status */
+#define PCI_ERR_ROOT_CMD_EN_MASK (PCI_ERR_ROOT_CMD_COR_EN | \
+ PCI_ERR_ROOT_CMD_NONFATAL_EN | \
+ PCI_ERR_ROOT_CMD_FATAL_EN)
+
+#define PCI_ERR_ROOT_IRQ_MAX 32
+#define PCI_ERR_ROOT_IRQ 0xf8000000
+#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1)
+#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV | \
+ PCI_ERR_ROOT_MULTI_COR_RCV | \
+ PCI_ERR_ROOT_UNCOR_RCV | \
+ PCI_ERR_ROOT_MULTI_UNCOR_RCV | \
+ PCI_ERR_ROOT_FIRST_FATAL | \
+ PCI_ERR_ROOT_NONFATAL_RCV | \
+ PCI_ERR_ROOT_FATAL_RCV)
+
+#define PCI_ERR_UNC_SUPPORTED (PCI_ERR_UNC_DLP | \
+ PCI_ERR_UNC_SDN | \
+ PCI_ERR_UNC_POISON_TLP | \
+ PCI_ERR_UNC_FCP | \
+ PCI_ERR_UNC_COMP_TIME | \
+ PCI_ERR_UNC_COMP_ABORT | \
+ PCI_ERR_UNC_UNX_COMP | \
+ PCI_ERR_UNC_RX_OVER | \
+ PCI_ERR_UNC_MALF_TLP | \
+ PCI_ERR_UNC_ECRC | \
+ PCI_ERR_UNC_UNSUP | \
+ PCI_ERR_UNC_ACSV | \
+ PCI_ERR_UNC_INTN | \
+ PCI_ERR_UNC_MCBTLP | \
+ PCI_ERR_UNC_ATOP_EBLOCKED | \
+ PCI_ERR_UNC_TLP_PRF_BLOCKED)
+
+#define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \
+ PCI_ERR_UNC_SDN | \
+ PCI_ERR_UNC_FCP | \
+ PCI_ERR_UNC_RX_OVER | \
+ PCI_ERR_UNC_MALF_TLP | \
+ PCI_ERR_UNC_INTN)
+
+#define PCI_ERR_COR_SUPPORTED (PCI_ERR_COR_RCVR | \
+ PCI_ERR_COR_BAD_TLP | \
+ PCI_ERR_COR_BAD_DLLP | \
+ PCI_ERR_COR_REP_ROLL | \
+ PCI_ERR_COR_REP_TIMER | \
+ PCI_ERR_COR_ADV_NONFATAL | \
+ PCI_ERR_COR_INTERNAL | \
+ PCI_ERR_COR_HL_OVERFLOW)
+
+#define PCI_ERR_COR_MASK_DEFAULT (PCI_ERR_COR_ADV_NONFATAL | \
+ PCI_ERR_COR_INTERNAL | \
+ PCI_ERR_COR_HL_OVERFLOW)
+
+#endif /* QEMU_PCIE_REGS_H */
diff --git a/hw/pckbd.c b/hw/pckbd.c
new file mode 100644
index 0000000..ae65c04
--- /dev/null
+++ b/hw/pckbd.c
@@ -0,0 +1,499 @@
+/*
+ * QEMU PC keyboard emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "ps2.h"
+#include "sysemu.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+#ifdef DEBUG_KBD
+#define DPRINTF(fmt, ...) \
+ do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/* Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
+#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
+#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
+#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
+#define KBD_CCMD_WRITE_OBUF 0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
+ initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
+#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */
+#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */
+#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_ECHO 0xEE
+#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/* Status Register Bits */
+#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
+#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
+#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
+#define KBD_STAT_PERR 0x80 /* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS 0x04 /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
+#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
+#define KBD_MODE_RFU 0x80
+
+/* Output Port Bits */
+#define KBD_OUT_RESET 0x01 /* 1=normal mode, 0=reset */
+#define KBD_OUT_A20 0x02 /* x86 only */
+#define KBD_OUT_OBF 0x10 /* Keyboard output buffer full */
+#define KBD_OUT_MOUSE_OBF 0x20 /* Mouse output buffer full */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_POLL 0xEB /* Poll */
+#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
+#define AUX_SET_WRAP 0xEE /* Set wrap mode */
+#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
+#define AUX_GET_TYPE 0xF2 /* Get type */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_SET_DEFAULT 0xF6
+#define AUX_RESET 0xFF /* Reset aux device */
+#define AUX_ACK 0xFA /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE 0x40
+#define MOUSE_STATUS_ENABLED 0x20
+#define MOUSE_STATUS_SCALE21 0x10
+
+#define KBD_PENDING_KBD 1
+#define KBD_PENDING_AUX 2
+
+typedef struct KBDState {
+ uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
+ uint8_t status;
+ uint8_t mode;
+ uint8_t outport;
+ /* Bitmask of devices with data available. */
+ uint8_t pending;
+ void *kbd;
+ void *mouse;
+
+ qemu_irq irq_kbd;
+ qemu_irq irq_mouse;
+ qemu_irq *a20_out;
+ target_phys_addr_t mask;
+} KBDState;
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
+ incorrect, but it avoids having to simulate exact delays */
+static void kbd_update_irq(KBDState *s)
+{
+ int irq_kbd_level, irq_mouse_level;
+
+ irq_kbd_level = 0;
+ irq_mouse_level = 0;
+ s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+ s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
+ if (s->pending) {
+ s->status |= KBD_STAT_OBF;
+ s->outport |= KBD_OUT_OBF;
+ /* kbd data takes priority over aux data. */
+ if (s->pending == KBD_PENDING_AUX) {
+ s->status |= KBD_STAT_MOUSE_OBF;
+ s->outport |= KBD_OUT_MOUSE_OBF;
+ if (s->mode & KBD_MODE_MOUSE_INT)
+ irq_mouse_level = 1;
+ } else {
+ if ((s->mode & KBD_MODE_KBD_INT) &&
+ !(s->mode & KBD_MODE_DISABLE_KBD))
+ irq_kbd_level = 1;
+ }
+ }
+ qemu_set_irq(s->irq_kbd, irq_kbd_level);
+ qemu_set_irq(s->irq_mouse, irq_mouse_level);
+}
+
+static void kbd_update_kbd_irq(void *opaque, int level)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (level)
+ s->pending |= KBD_PENDING_KBD;
+ else
+ s->pending &= ~KBD_PENDING_KBD;
+ kbd_update_irq(s);
+}
+
+static void kbd_update_aux_irq(void *opaque, int level)
+{
+ KBDState *s = (KBDState *)opaque;
+
+ if (level)
+ s->pending |= KBD_PENDING_AUX;
+ else
+ s->pending &= ~KBD_PENDING_AUX;
+ kbd_update_irq(s);
+}
+
+static uint32_t kbd_read_status(void *opaque, uint32_t addr)
+{
+ KBDState *s = opaque;
+ int val;
+ val = s->status;
+ DPRINTF("kbd: read status=0x%02x\n", val);
+ return val;
+}
+
+static void kbd_queue(KBDState *s, int b, int aux)
+{
+ if (aux)
+ ps2_queue(s->mouse, b);
+ else
+ ps2_queue(s->kbd, b);
+}
+
+static void outport_write(KBDState *s, uint32_t val)
+{
+ DPRINTF("kbd: write outport=0x%02x\n", val);
+ s->outport = val;
+ if (s->a20_out) {
+ qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+ }
+ if (!(val & 1)) {
+ qemu_system_reset_request();
+ }
+}
+
+static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val)
+{
+ KBDState *s = opaque;
+
+ DPRINTF("kbd: write cmd=0x%02x\n", val);
+
+ /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed
+ * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE
+ * command specify the output port bits to be pulsed.
+ * 0: Bit should be pulsed. 1: Bit should not be modified.
+ * The only useful version of this command is pulsing bit 0,
+ * which does a CPU reset.
+ */
+ if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
+ if(!(val & 1))
+ val = KBD_CCMD_RESET;
+ else
+ val = KBD_CCMD_NO_OP;
+ }
+
+ switch(val) {
+ case KBD_CCMD_READ_MODE:
+ kbd_queue(s, s->mode, 0);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ case KBD_CCMD_WRITE_OBUF:
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ case KBD_CCMD_WRITE_MOUSE:
+ case KBD_CCMD_WRITE_OUTPORT:
+ s->write_cmd = val;
+ break;
+ case KBD_CCMD_MOUSE_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_MOUSE;
+ break;
+ case KBD_CCMD_MOUSE_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ break;
+ case KBD_CCMD_TEST_MOUSE:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_SELF_TEST:
+ s->status |= KBD_STAT_SELFTEST;
+ kbd_queue(s, 0x55, 0);
+ break;
+ case KBD_CCMD_KBD_TEST:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_KBD_DISABLE:
+ s->mode |= KBD_MODE_DISABLE_KBD;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_KBD_ENABLE:
+ s->mode &= ~KBD_MODE_DISABLE_KBD;
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_READ_INPORT:
+ kbd_queue(s, 0x00, 0);
+ break;
+ case KBD_CCMD_READ_OUTPORT:
+ kbd_queue(s, s->outport, 0);
+ break;
+ case KBD_CCMD_ENABLE_A20:
+ if (s->a20_out) {
+ qemu_irq_raise(*s->a20_out);
+ }
+ s->outport |= KBD_OUT_A20;
+ break;
+ case KBD_CCMD_DISABLE_A20:
+ if (s->a20_out) {
+ qemu_irq_lower(*s->a20_out);
+ }
+ s->outport &= ~KBD_OUT_A20;
+ break;
+ case KBD_CCMD_RESET:
+ qemu_system_reset_request();
+ break;
+ case KBD_CCMD_NO_OP:
+ /* ignore that */
+ break;
+ default:
+ fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
+ break;
+ }
+}
+
+static uint32_t kbd_read_data(void *opaque, uint32_t addr)
+{
+ KBDState *s = opaque;
+ uint32_t val;
+
+ if (s->pending == KBD_PENDING_AUX)
+ val = ps2_read_data(s->mouse);
+ else
+ val = ps2_read_data(s->kbd);
+
+ DPRINTF("kbd: read data=0x%02x\n", val);
+ return val;
+}
+
+static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+ KBDState *s = opaque;
+
+ DPRINTF("kbd: write data=0x%02x\n", val);
+
+ switch(s->write_cmd) {
+ case 0:
+ ps2_write_keyboard(s->kbd, val);
+ break;
+ case KBD_CCMD_WRITE_MODE:
+ s->mode = val;
+ ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
+ /* ??? */
+ kbd_update_irq(s);
+ break;
+ case KBD_CCMD_WRITE_OBUF:
+ kbd_queue(s, val, 0);
+ break;
+ case KBD_CCMD_WRITE_AUX_OBUF:
+ kbd_queue(s, val, 1);
+ break;
+ case KBD_CCMD_WRITE_OUTPORT:
+ outport_write(s, val);
+ break;
+ case KBD_CCMD_WRITE_MOUSE:
+ ps2_write_mouse(s->mouse, val);
+ break;
+ default:
+ break;
+ }
+ s->write_cmd = 0;
+}
+
+static void kbd_reset(void *opaque)
+{
+ KBDState *s = opaque;
+
+ s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+ s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+ s->outport = KBD_OUT_RESET | KBD_OUT_A20;
+}
+
+static const VMStateDescription vmstate_kbd = {
+ .name = "pckbd",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(write_cmd, KBDState),
+ VMSTATE_UINT8(status, KBDState),
+ VMSTATE_UINT8(mode, KBDState),
+ VMSTATE_UINT8(pending, KBDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* Memory mapped interface */
+static uint32_t kbd_mm_readb (void *opaque, target_phys_addr_t addr)
+{
+ KBDState *s = opaque;
+
+ if (addr & s->mask)
+ return kbd_read_status(s, 0) & 0xff;
+ else
+ return kbd_read_data(s, 0) & 0xff;
+}
+
+static void kbd_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ KBDState *s = opaque;
+
+ if (addr & s->mask)
+ kbd_write_command(s, 0, value & 0xff);
+ else
+ kbd_write_data(s, 0, value & 0xff);
+}
+
+static CPUReadMemoryFunc * const kbd_mm_read[] = {
+ &kbd_mm_readb,
+ &kbd_mm_readb,
+ &kbd_mm_readb,
+};
+
+static CPUWriteMemoryFunc * const kbd_mm_write[] = {
+ &kbd_mm_writeb,
+ &kbd_mm_writeb,
+ &kbd_mm_writeb,
+};
+
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+ target_phys_addr_t base, ram_addr_t size,
+ target_phys_addr_t mask)
+{
+ KBDState *s = qemu_mallocz(sizeof(KBDState));
+ int s_io_memory;
+
+ s->irq_kbd = kbd_irq;
+ s->irq_mouse = mouse_irq;
+ s->mask = mask;
+
+ vmstate_register(NULL, 0, &vmstate_kbd, s);
+ s_io_memory = cpu_register_io_memory(kbd_mm_read, kbd_mm_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, size, s_io_memory);
+
+ s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+ s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+ qemu_register_reset(kbd_reset, s);
+}
+
+typedef struct ISAKBDState {
+ ISADevice dev;
+ KBDState kbd;
+} ISAKBDState;
+
+void i8042_isa_mouse_fake_event(void *opaque)
+{
+ ISADevice *dev = opaque;
+ KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+ ps2_mouse_fake_event(s->mouse);
+}
+
+void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out)
+{
+ KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+ s->a20_out = a20_out;
+}
+
+static const VMStateDescription vmstate_kbd_isa = {
+ .name = "pckbd",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int i8042_initfn(ISADevice *dev)
+{
+ KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+ isa_init_irq(dev, &s->irq_kbd, 1);
+ isa_init_irq(dev, &s->irq_mouse, 12);
+
+ register_ioport_read(0x60, 1, 1, kbd_read_data, s);
+ register_ioport_write(0x60, 1, 1, kbd_write_data, s);
+ isa_init_ioport(dev, 0x60);
+ register_ioport_read(0x64, 1, 1, kbd_read_status, s);
+ register_ioport_write(0x64, 1, 1, kbd_write_command, s);
+ isa_init_ioport(dev, 0x64);
+
+ s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+ s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+ qemu_register_reset(kbd_reset, s);
+ return 0;
+}
+
+static ISADeviceInfo i8042_info = {
+ .qdev.name = "i8042",
+ .qdev.size = sizeof(ISAKBDState),
+ .qdev.vmsd = &vmstate_kbd_isa,
+ .qdev.no_user = 1,
+ .init = i8042_initfn,
+};
+
+static void i8042_register(void)
+{
+ isa_qdev_register(&i8042_info);
+}
+device_init(i8042_register)
diff --git a/hw/pcmcia.h b/hw/pcmcia.h
new file mode 100644
index 0000000..50648c9
--- /dev/null
+++ b/hw/pcmcia.h
@@ -0,0 +1,51 @@
+/* PCMCIA/Cardbus */
+
+#include "qemu-common.h"
+
+typedef struct {
+ qemu_irq irq;
+ int attached;
+ const char *slot_string;
+ const char *card_string;
+} PCMCIASocket;
+
+void pcmcia_socket_register(PCMCIASocket *socket);
+void pcmcia_socket_unregister(PCMCIASocket *socket);
+void pcmcia_info(Monitor *mon);
+
+struct PCMCIACardState {
+ void *state;
+ PCMCIASocket *slot;
+ int (*attach)(void *state);
+ int (*detach)(void *state);
+ const uint8_t *cis;
+ int cis_len;
+
+ /* Only valid if attached */
+ uint8_t (*attr_read)(void *state, uint32_t address);
+ void (*attr_write)(void *state, uint32_t address, uint8_t value);
+ uint16_t (*common_read)(void *state, uint32_t address);
+ void (*common_write)(void *state, uint32_t address, uint16_t value);
+ uint16_t (*io_read)(void *state, uint32_t address);
+ void (*io_write)(void *state, uint32_t address, uint16_t value);
+};
+
+#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */
+#define CISTPL_NO_LINK 0x14 /* No Link Tuple */
+#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */
+#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */
+#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */
+#define CISTPL_CONFIG 0x1a /* Configuration Tuple */
+#define CISTPL_CFTABLE_ENTRY 0x1b /* 16-bit PCCard Configuration */
+#define CISTPL_DEVICE_OC 0x1c /* Additional Device Information */
+#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */
+#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */
+#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */
+#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */
+#define CISTPL_FUNCID 0x21 /* Function ID Tuple */
+#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */
+#define CISTPL_END 0xff /* Tuple End */
+#define CISTPL_ENDMARK 0xff
+
+/* dscm1xxxx.c */
+PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv);
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
new file mode 100644
index 0000000..339a401
--- /dev/null
+++ b/hw/pcnet-pci.c
@@ -0,0 +1,346 @@
+/*
+ * QEMU AMD PC-Net II (Am79C970A) PCI emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
+ */
+
+#include "pci.h"
+#include "net.h"
+#include "loader.h"
+#include "qemu-timer.h"
+
+#include "pcnet.h"
+
+//#define PCNET_DEBUG
+//#define PCNET_DEBUG_IO
+//#define PCNET_DEBUG_BCR
+//#define PCNET_DEBUG_CSR
+//#define PCNET_DEBUG_RMD
+//#define PCNET_DEBUG_TMD
+//#define PCNET_DEBUG_MATCH
+
+
+typedef struct {
+ PCIDevice pci_dev;
+ PCNetState state;
+} PCIPCNetState;
+
+static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCNetState *s = opaque;
+#ifdef PCNET_DEBUG
+ printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+ /* Check APROMWE bit to enable write access */
+ if (pcnet_bcr_readw(s,2) & 0x100)
+ s->prom[addr & 15] = val;
+}
+
+static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr)
+{
+ PCNetState *s = opaque;
+ uint32_t val = s->prom[addr & 15];
+#ifdef PCNET_DEBUG
+ printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+static void pcnet_ioport_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCNetState *d = &DO_UPCAST(PCIPCNetState, pci_dev, pci_dev)->state;
+
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_map addr=0x%04"FMT_PCIBUS" size=0x%04"FMT_PCIBUS"\n",
+ addr, size);
+#endif
+
+ register_ioport_write(addr, 16, 1, pcnet_aprom_writeb, d);
+ register_ioport_read(addr, 16, 1, pcnet_aprom_readb, d);
+
+ register_ioport_write(addr + 0x10, 0x10, 2, pcnet_ioport_writew, d);
+ register_ioport_read(addr + 0x10, 0x10, 2, pcnet_ioport_readw, d);
+ register_ioport_write(addr + 0x10, 0x10, 4, pcnet_ioport_writel, d);
+ register_ioport_read(addr + 0x10, 0x10, 4, pcnet_ioport_readl, d);
+}
+
+static void pcnet_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr,
+ val);
+#endif
+ if (!(addr & 0x10))
+ pcnet_aprom_writeb(d, addr & 0x0f, val);
+}
+
+static uint32_t pcnet_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ PCNetState *d = opaque;
+ uint32_t val = -1;
+ if (!(addr & 0x10))
+ val = pcnet_aprom_readb(d, addr & 0x0f);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr,
+ val & 0xff);
+#endif
+ return val;
+}
+
+static void pcnet_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr,
+ val);
+#endif
+ if (addr & 0x10)
+ pcnet_ioport_writew(d, addr & 0x0f, val);
+ else {
+ addr &= 0x0f;
+ pcnet_aprom_writeb(d, addr, val & 0xff);
+ pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
+ }
+}
+
+static uint32_t pcnet_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ PCNetState *d = opaque;
+ uint32_t val = -1;
+ if (addr & 0x10)
+ val = pcnet_ioport_readw(d, addr & 0x0f);
+ else {
+ addr &= 0x0f;
+ val = pcnet_aprom_readb(d, addr+1);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr);
+ }
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr,
+ val & 0xffff);
+#endif
+ return val;
+}
+
+static void pcnet_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr,
+ val);
+#endif
+ if (addr & 0x10)
+ pcnet_ioport_writel(d, addr & 0x0f, val);
+ else {
+ addr &= 0x0f;
+ pcnet_aprom_writeb(d, addr, val & 0xff);
+ pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
+ pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16);
+ pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24);
+ }
+}
+
+static uint32_t pcnet_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ PCNetState *d = opaque;
+ uint32_t val;
+ if (addr & 0x10)
+ val = pcnet_ioport_readl(d, addr & 0x0f);
+ else {
+ addr &= 0x0f;
+ val = pcnet_aprom_readb(d, addr+3);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr+2);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr+1);
+ val <<= 8;
+ val |= pcnet_aprom_readb(d, addr);
+ }
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr,
+ val);
+#endif
+ return val;
+}
+
+static const VMStateDescription vmstate_pci_pcnet = {
+ .name = "pcnet",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(pci_dev, PCIPCNetState),
+ VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* PCI interface */
+
+static CPUWriteMemoryFunc * const pcnet_mmio_write[] = {
+ &pcnet_mmio_writeb,
+ &pcnet_mmio_writew,
+ &pcnet_mmio_writel
+};
+
+static CPUReadMemoryFunc * const pcnet_mmio_read[] = {
+ &pcnet_mmio_readb,
+ &pcnet_mmio_readw,
+ &pcnet_mmio_readl
+};
+
+static void pcnet_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev);
+
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_mmio_map addr=0x%08"FMT_PCIBUS" 0x%08"FMT_PCIBUS"\n",
+ addr, size);
+#endif
+
+ cpu_register_physical_memory(addr, PCNET_PNPMMIO_SIZE, d->state.mmio_index);
+}
+
+static void pci_physical_memory_write(void *dma_opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap)
+{
+ cpu_physical_memory_write(addr, buf, len);
+}
+
+static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap)
+{
+ cpu_physical_memory_read(addr, buf, len);
+}
+
+static void pci_pcnet_cleanup(VLANClientState *nc)
+{
+ PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ pcnet_common_cleanup(d);
+}
+
+static int pci_pcnet_uninit(PCIDevice *dev)
+{
+ PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev);
+
+ cpu_unregister_io_memory(d->state.mmio_index);
+ qemu_del_timer(d->state.poll_timer);
+ qemu_free_timer(d->state.poll_timer);
+ qemu_del_vlan_client(&d->state.nic->nc);
+ return 0;
+}
+
+static NetClientInfo net_pci_pcnet_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = pcnet_can_receive,
+ .receive = pcnet_receive,
+ .cleanup = pci_pcnet_cleanup,
+};
+
+static int pci_pcnet_init(PCIDevice *pci_dev)
+{
+ PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev);
+ PCNetState *s = &d->state;
+ uint8_t *pci_conf;
+
+#if 0
+ printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n",
+ sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD));
+#endif
+
+ pci_conf = pci_dev->config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_AMD);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_AMD_LANCE);
+ pci_set_word(pci_conf + PCI_STATUS,
+ PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
+ pci_conf[PCI_REVISION_ID] = 0x10;
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+
+ pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0);
+ pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0);
+
+ pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0
+ pci_conf[PCI_MIN_GNT] = 0x06;
+ pci_conf[PCI_MAX_LAT] = 0xff;
+
+ /* Handler for memory-mapped I/O */
+ s->mmio_index =
+ cpu_register_io_memory(pcnet_mmio_read, pcnet_mmio_write, &d->state,
+ DEVICE_NATIVE_ENDIAN);
+
+ pci_register_bar(pci_dev, 0, PCNET_IOPORT_SIZE,
+ PCI_BASE_ADDRESS_SPACE_IO, pcnet_ioport_map);
+
+ pci_register_bar(pci_dev, 1, PCNET_PNPMMIO_SIZE,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, pcnet_mmio_map);
+
+ s->irq = pci_dev->irq[0];
+ s->phys_mem_read = pci_physical_memory_read;
+ s->phys_mem_write = pci_physical_memory_write;
+
+ if (!pci_dev->qdev.hotplugged) {
+ static int loaded = 0;
+ if (!loaded) {
+ rom_add_option("pxe-pcnet.bin", -1);
+ loaded = 1;
+ }
+ }
+
+ return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info);
+}
+
+static void pci_reset(DeviceState *dev)
+{
+ PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev.qdev, dev);
+
+ pcnet_h_reset(&d->state);
+}
+
+static PCIDeviceInfo pcnet_info = {
+ .qdev.name = "pcnet",
+ .qdev.size = sizeof(PCIPCNetState),
+ .qdev.reset = pci_reset,
+ .qdev.vmsd = &vmstate_pci_pcnet,
+ .init = pci_pcnet_init,
+ .exit = pci_pcnet_uninit,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void pci_pcnet_register_devices(void)
+{
+ pci_qdev_register(&pcnet_info);
+}
+
+device_init(pci_pcnet_register_devices)
diff --git a/hw/pcnet.c b/hw/pcnet.c
new file mode 100644
index 0000000..db52dc5
--- /dev/null
+++ b/hw/pcnet.c
@@ -0,0 +1,1748 @@
+/*
+ * QEMU AMD PC-Net II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
+ */
+
+/*
+ * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
+ * produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
+ */
+
+#include "qdev.h"
+#include "net.h"
+#include "qemu-timer.h"
+#include "qemu_socket.h"
+#include "sysemu.h"
+
+#include "pcnet.h"
+
+//#define PCNET_DEBUG
+//#define PCNET_DEBUG_IO
+//#define PCNET_DEBUG_BCR
+//#define PCNET_DEBUG_CSR
+//#define PCNET_DEBUG_RMD
+//#define PCNET_DEBUG_TMD
+//#define PCNET_DEBUG_MATCH
+
+
+struct qemu_ether_header {
+ uint8_t ether_dhost[6];
+ uint8_t ether_shost[6];
+ uint16_t ether_type;
+};
+
+/* BUS CONFIGURATION REGISTERS */
+#define BCR_MSRDA 0
+#define BCR_MSWRA 1
+#define BCR_MC 2
+#define BCR_LNKST 4
+#define BCR_LED1 5
+#define BCR_LED2 6
+#define BCR_LED3 7
+#define BCR_FDC 9
+#define BCR_BSBC 18
+#define BCR_EECAS 19
+#define BCR_SWS 20
+#define BCR_PLAT 22
+
+#define BCR_DWIO(S) !!((S)->bcr[BCR_BSBC] & 0x0080)
+#define BCR_SSIZE32(S) !!((S)->bcr[BCR_SWS ] & 0x0100)
+#define BCR_SWSTYLE(S) ((S)->bcr[BCR_SWS ] & 0x00FF)
+
+#define CSR_INIT(S) !!(((S)->csr[0])&0x0001)
+#define CSR_STRT(S) !!(((S)->csr[0])&0x0002)
+#define CSR_STOP(S) !!(((S)->csr[0])&0x0004)
+#define CSR_TDMD(S) !!(((S)->csr[0])&0x0008)
+#define CSR_TXON(S) !!(((S)->csr[0])&0x0010)
+#define CSR_RXON(S) !!(((S)->csr[0])&0x0020)
+#define CSR_INEA(S) !!(((S)->csr[0])&0x0040)
+#define CSR_BSWP(S) !!(((S)->csr[3])&0x0004)
+#define CSR_LAPPEN(S) !!(((S)->csr[3])&0x0020)
+#define CSR_DXSUFLO(S) !!(((S)->csr[3])&0x0040)
+#define CSR_ASTRP_RCV(S) !!(((S)->csr[4])&0x0800)
+#define CSR_DPOLL(S) !!(((S)->csr[4])&0x1000)
+#define CSR_SPND(S) !!(((S)->csr[5])&0x0001)
+#define CSR_LTINTEN(S) !!(((S)->csr[5])&0x4000)
+#define CSR_TOKINTD(S) !!(((S)->csr[5])&0x8000)
+#define CSR_DRX(S) !!(((S)->csr[15])&0x0001)
+#define CSR_DTX(S) !!(((S)->csr[15])&0x0002)
+#define CSR_LOOP(S) !!(((S)->csr[15])&0x0004)
+#define CSR_DXMTFCS(S) !!(((S)->csr[15])&0x0008)
+#define CSR_DRCVPA(S) !!(((S)->csr[15])&0x2000)
+#define CSR_DRCVBC(S) !!(((S)->csr[15])&0x4000)
+#define CSR_PROM(S) !!(((S)->csr[15])&0x8000)
+
+#define CSR_CRBC(S) ((S)->csr[40])
+#define CSR_CRST(S) ((S)->csr[41])
+#define CSR_CXBC(S) ((S)->csr[42])
+#define CSR_CXST(S) ((S)->csr[43])
+#define CSR_NRBC(S) ((S)->csr[44])
+#define CSR_NRST(S) ((S)->csr[45])
+#define CSR_POLL(S) ((S)->csr[46])
+#define CSR_PINT(S) ((S)->csr[47])
+#define CSR_RCVRC(S) ((S)->csr[72])
+#define CSR_XMTRC(S) ((S)->csr[74])
+#define CSR_RCVRL(S) ((S)->csr[76])
+#define CSR_XMTRL(S) ((S)->csr[78])
+#define CSR_MISSC(S) ((S)->csr[112])
+
+#define CSR_IADR(S) ((S)->csr[ 1] | ((S)->csr[ 2] << 16))
+#define CSR_CRBA(S) ((S)->csr[18] | ((S)->csr[19] << 16))
+#define CSR_CXBA(S) ((S)->csr[20] | ((S)->csr[21] << 16))
+#define CSR_NRBA(S) ((S)->csr[22] | ((S)->csr[23] << 16))
+#define CSR_BADR(S) ((S)->csr[24] | ((S)->csr[25] << 16))
+#define CSR_NRDA(S) ((S)->csr[26] | ((S)->csr[27] << 16))
+#define CSR_CRDA(S) ((S)->csr[28] | ((S)->csr[29] << 16))
+#define CSR_BADX(S) ((S)->csr[30] | ((S)->csr[31] << 16))
+#define CSR_NXDA(S) ((S)->csr[32] | ((S)->csr[33] << 16))
+#define CSR_CXDA(S) ((S)->csr[34] | ((S)->csr[35] << 16))
+#define CSR_NNRD(S) ((S)->csr[36] | ((S)->csr[37] << 16))
+#define CSR_NNXD(S) ((S)->csr[38] | ((S)->csr[39] << 16))
+#define CSR_PXDA(S) ((S)->csr[60] | ((S)->csr[61] << 16))
+#define CSR_NXBA(S) ((S)->csr[64] | ((S)->csr[65] << 16))
+
+#define PHYSADDR(S,A) \
+ (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(s)->csr[2])<<16))
+
+struct pcnet_initblk16 {
+ uint16_t mode;
+ uint16_t padr[3];
+ uint16_t ladrf[4];
+ uint32_t rdra;
+ uint32_t tdra;
+};
+
+struct pcnet_initblk32 {
+ uint16_t mode;
+ uint8_t rlen;
+ uint8_t tlen;
+ uint16_t padr[3];
+ uint16_t _res;
+ uint16_t ladrf[4];
+ uint32_t rdra;
+ uint32_t tdra;
+};
+
+struct pcnet_TMD {
+ uint32_t tbadr;
+ int16_t length;
+ int16_t status;
+ uint32_t misc;
+ uint32_t res;
+};
+
+#define TMDL_BCNT_MASK 0x0fff
+#define TMDL_BCNT_SH 0
+#define TMDL_ONES_MASK 0xf000
+#define TMDL_ONES_SH 12
+
+#define TMDS_BPE_MASK 0x0080
+#define TMDS_BPE_SH 7
+#define TMDS_ENP_MASK 0x0100
+#define TMDS_ENP_SH 8
+#define TMDS_STP_MASK 0x0200
+#define TMDS_STP_SH 9
+#define TMDS_DEF_MASK 0x0400
+#define TMDS_DEF_SH 10
+#define TMDS_ONE_MASK 0x0800
+#define TMDS_ONE_SH 11
+#define TMDS_LTINT_MASK 0x1000
+#define TMDS_LTINT_SH 12
+#define TMDS_NOFCS_MASK 0x2000
+#define TMDS_NOFCS_SH 13
+#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK
+#define TMDS_ADDFCS_SH TMDS_NOFCS_SH
+#define TMDS_ERR_MASK 0x4000
+#define TMDS_ERR_SH 14
+#define TMDS_OWN_MASK 0x8000
+#define TMDS_OWN_SH 15
+
+#define TMDM_TRC_MASK 0x0000000f
+#define TMDM_TRC_SH 0
+#define TMDM_TDR_MASK 0x03ff0000
+#define TMDM_TDR_SH 16
+#define TMDM_RTRY_MASK 0x04000000
+#define TMDM_RTRY_SH 26
+#define TMDM_LCAR_MASK 0x08000000
+#define TMDM_LCAR_SH 27
+#define TMDM_LCOL_MASK 0x10000000
+#define TMDM_LCOL_SH 28
+#define TMDM_EXDEF_MASK 0x20000000
+#define TMDM_EXDEF_SH 29
+#define TMDM_UFLO_MASK 0x40000000
+#define TMDM_UFLO_SH 30
+#define TMDM_BUFF_MASK 0x80000000
+#define TMDM_BUFF_SH 31
+
+struct pcnet_RMD {
+ uint32_t rbadr;
+ int16_t buf_length;
+ int16_t status;
+ uint32_t msg_length;
+ uint32_t res;
+};
+
+#define RMDL_BCNT_MASK 0x0fff
+#define RMDL_BCNT_SH 0
+#define RMDL_ONES_MASK 0xf000
+#define RMDL_ONES_SH 12
+
+#define RMDS_BAM_MASK 0x0010
+#define RMDS_BAM_SH 4
+#define RMDS_LFAM_MASK 0x0020
+#define RMDS_LFAM_SH 5
+#define RMDS_PAM_MASK 0x0040
+#define RMDS_PAM_SH 6
+#define RMDS_BPE_MASK 0x0080
+#define RMDS_BPE_SH 7
+#define RMDS_ENP_MASK 0x0100
+#define RMDS_ENP_SH 8
+#define RMDS_STP_MASK 0x0200
+#define RMDS_STP_SH 9
+#define RMDS_BUFF_MASK 0x0400
+#define RMDS_BUFF_SH 10
+#define RMDS_CRC_MASK 0x0800
+#define RMDS_CRC_SH 11
+#define RMDS_OFLO_MASK 0x1000
+#define RMDS_OFLO_SH 12
+#define RMDS_FRAM_MASK 0x2000
+#define RMDS_FRAM_SH 13
+#define RMDS_ERR_MASK 0x4000
+#define RMDS_ERR_SH 14
+#define RMDS_OWN_MASK 0x8000
+#define RMDS_OWN_SH 15
+
+#define RMDM_MCNT_MASK 0x00000fff
+#define RMDM_MCNT_SH 0
+#define RMDM_ZEROS_MASK 0x0000f000
+#define RMDM_ZEROS_SH 12
+#define RMDM_RPC_MASK 0x00ff0000
+#define RMDM_RPC_SH 16
+#define RMDM_RCC_MASK 0xff000000
+#define RMDM_RCC_SH 24
+
+#define SET_FIELD(regp, name, field, value) \
+ (*(regp) = (*(regp) & ~(name ## _ ## field ## _MASK)) \
+ | ((value) << name ## _ ## field ## _SH))
+
+#define GET_FIELD(reg, name, field) \
+ (((reg) & name ## _ ## field ## _MASK) >> name ## _ ## field ## _SH)
+
+#define PRINT_TMD(T) printf( \
+ "TMD0 : TBADR=0x%08x\n" \
+ "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \
+ "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \
+ " BPE=%d, BCNT=%d\n" \
+ "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \
+ "LCA=%d, RTR=%d,\n" \
+ " TDR=%d, TRC=%d\n", \
+ (T)->tbadr, \
+ GET_FIELD((T)->status, TMDS, OWN), \
+ GET_FIELD((T)->status, TMDS, ERR), \
+ GET_FIELD((T)->status, TMDS, NOFCS), \
+ GET_FIELD((T)->status, TMDS, LTINT), \
+ GET_FIELD((T)->status, TMDS, ONE), \
+ GET_FIELD((T)->status, TMDS, DEF), \
+ GET_FIELD((T)->status, TMDS, STP), \
+ GET_FIELD((T)->status, TMDS, ENP), \
+ GET_FIELD((T)->status, TMDS, BPE), \
+ 4096-GET_FIELD((T)->length, TMDL, BCNT), \
+ GET_FIELD((T)->misc, TMDM, BUFF), \
+ GET_FIELD((T)->misc, TMDM, UFLO), \
+ GET_FIELD((T)->misc, TMDM, EXDEF), \
+ GET_FIELD((T)->misc, TMDM, LCOL), \
+ GET_FIELD((T)->misc, TMDM, LCAR), \
+ GET_FIELD((T)->misc, TMDM, RTRY), \
+ GET_FIELD((T)->misc, TMDM, TDR), \
+ GET_FIELD((T)->misc, TMDM, TRC))
+
+#define PRINT_RMD(R) printf( \
+ "RMD0 : RBADR=0x%08x\n" \
+ "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \
+ "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \
+ "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
+ "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \
+ (R)->rbadr, \
+ GET_FIELD((R)->status, RMDS, OWN), \
+ GET_FIELD((R)->status, RMDS, ERR), \
+ GET_FIELD((R)->status, RMDS, FRAM), \
+ GET_FIELD((R)->status, RMDS, OFLO), \
+ GET_FIELD((R)->status, RMDS, CRC), \
+ GET_FIELD((R)->status, RMDS, BUFF), \
+ GET_FIELD((R)->status, RMDS, STP), \
+ GET_FIELD((R)->status, RMDS, ENP), \
+ GET_FIELD((R)->status, RMDS, BPE), \
+ GET_FIELD((R)->status, RMDS, PAM), \
+ GET_FIELD((R)->status, RMDS, LFAM), \
+ GET_FIELD((R)->status, RMDS, BAM), \
+ GET_FIELD((R)->buf_length, RMDL, ONES), \
+ 4096-GET_FIELD((R)->buf_length, RMDL, BCNT), \
+ GET_FIELD((R)->msg_length, RMDM, RCC), \
+ GET_FIELD((R)->msg_length, RMDM, RPC), \
+ GET_FIELD((R)->msg_length, RMDM, MCNT), \
+ GET_FIELD((R)->msg_length, RMDM, ZEROS))
+
+static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd,
+ target_phys_addr_t addr)
+{
+ if (!BCR_SSIZE32(s)) {
+ struct {
+ uint32_t tbadr;
+ int16_t length;
+ int16_t status;
+ } xda;
+ s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
+ tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff;
+ tmd->length = le16_to_cpu(xda.length);
+ tmd->status = (le32_to_cpu(xda.tbadr) >> 16) & 0xff00;
+ tmd->misc = le16_to_cpu(xda.status) << 16;
+ tmd->res = 0;
+ } else {
+ s->phys_mem_read(s->dma_opaque, addr, (void *)tmd, sizeof(*tmd), 0);
+ le32_to_cpus(&tmd->tbadr);
+ le16_to_cpus((uint16_t *)&tmd->length);
+ le16_to_cpus((uint16_t *)&tmd->status);
+ le32_to_cpus(&tmd->misc);
+ le32_to_cpus(&tmd->res);
+ if (BCR_SWSTYLE(s) == 3) {
+ uint32_t tmp = tmd->tbadr;
+ tmd->tbadr = tmd->misc;
+ tmd->misc = tmp;
+ }
+ }
+}
+
+static inline void pcnet_tmd_store(PCNetState *s, const struct pcnet_TMD *tmd,
+ target_phys_addr_t addr)
+{
+ if (!BCR_SSIZE32(s)) {
+ struct {
+ uint32_t tbadr;
+ int16_t length;
+ int16_t status;
+ } xda;
+ xda.tbadr = cpu_to_le32((tmd->tbadr & 0xffffff) |
+ ((tmd->status & 0xff00) << 16));
+ xda.length = cpu_to_le16(tmd->length);
+ xda.status = cpu_to_le16(tmd->misc >> 16);
+ s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
+ } else {
+ struct {
+ uint32_t tbadr;
+ int16_t length;
+ int16_t status;
+ uint32_t misc;
+ uint32_t res;
+ } xda;
+ xda.tbadr = cpu_to_le32(tmd->tbadr);
+ xda.length = cpu_to_le16(tmd->length);
+ xda.status = cpu_to_le16(tmd->status);
+ xda.misc = cpu_to_le32(tmd->misc);
+ xda.res = cpu_to_le32(tmd->res);
+ if (BCR_SWSTYLE(s) == 3) {
+ uint32_t tmp = xda.tbadr;
+ xda.tbadr = xda.misc;
+ xda.misc = tmp;
+ }
+ s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
+ }
+}
+
+static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd,
+ target_phys_addr_t addr)
+{
+ if (!BCR_SSIZE32(s)) {
+ struct {
+ uint32_t rbadr;
+ int16_t buf_length;
+ int16_t msg_length;
+ } rda;
+ s->phys_mem_read(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
+ rmd->rbadr = le32_to_cpu(rda.rbadr) & 0xffffff;
+ rmd->buf_length = le16_to_cpu(rda.buf_length);
+ rmd->status = (le32_to_cpu(rda.rbadr) >> 16) & 0xff00;
+ rmd->msg_length = le16_to_cpu(rda.msg_length);
+ rmd->res = 0;
+ } else {
+ s->phys_mem_read(s->dma_opaque, addr, (void *)rmd, sizeof(*rmd), 0);
+ le32_to_cpus(&rmd->rbadr);
+ le16_to_cpus((uint16_t *)&rmd->buf_length);
+ le16_to_cpus((uint16_t *)&rmd->status);
+ le32_to_cpus(&rmd->msg_length);
+ le32_to_cpus(&rmd->res);
+ if (BCR_SWSTYLE(s) == 3) {
+ uint32_t tmp = rmd->rbadr;
+ rmd->rbadr = rmd->msg_length;
+ rmd->msg_length = tmp;
+ }
+ }
+}
+
+static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd,
+ target_phys_addr_t addr)
+{
+ if (!BCR_SSIZE32(s)) {
+ struct {
+ uint32_t rbadr;
+ int16_t buf_length;
+ int16_t msg_length;
+ } rda;
+ rda.rbadr = cpu_to_le32((rmd->rbadr & 0xffffff) |
+ ((rmd->status & 0xff00) << 16));
+ rda.buf_length = cpu_to_le16(rmd->buf_length);
+ rda.msg_length = cpu_to_le16(rmd->msg_length);
+ s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
+ } else {
+ struct {
+ uint32_t rbadr;
+ int16_t buf_length;
+ int16_t status;
+ uint32_t msg_length;
+ uint32_t res;
+ } rda;
+ rda.rbadr = cpu_to_le32(rmd->rbadr);
+ rda.buf_length = cpu_to_le16(rmd->buf_length);
+ rda.status = cpu_to_le16(rmd->status);
+ rda.msg_length = cpu_to_le32(rmd->msg_length);
+ rda.res = cpu_to_le32(rmd->res);
+ if (BCR_SWSTYLE(s) == 3) {
+ uint32_t tmp = rda.rbadr;
+ rda.rbadr = rda.msg_length;
+ rda.msg_length = tmp;
+ }
+ s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
+ }
+}
+
+
+#define TMDLOAD(TMD,ADDR) pcnet_tmd_load(s,TMD,ADDR)
+
+#define TMDSTORE(TMD,ADDR) pcnet_tmd_store(s,TMD,ADDR)
+
+#define RMDLOAD(RMD,ADDR) pcnet_rmd_load(s,RMD,ADDR)
+
+#define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR)
+
+#if 1
+
+#define CHECK_RMD(ADDR,RES) do { \
+ struct pcnet_RMD rmd; \
+ RMDLOAD(&rmd,(ADDR)); \
+ (RES) |= (GET_FIELD(rmd.buf_length, RMDL, ONES) != 15) \
+ || (GET_FIELD(rmd.msg_length, RMDM, ZEROS) != 0); \
+} while (0)
+
+#define CHECK_TMD(ADDR,RES) do { \
+ struct pcnet_TMD tmd; \
+ TMDLOAD(&tmd,(ADDR)); \
+ (RES) |= (GET_FIELD(tmd.length, TMDL, ONES) != 15); \
+} while (0)
+
+#else
+
+#define CHECK_RMD(ADDR,RES) do { \
+ switch (BCR_SWSTYLE(s)) { \
+ case 0x00: \
+ do { \
+ uint16_t rda[4]; \
+ s->phys_mem_read(s->dma_opaque, (ADDR), \
+ (void *)&rda[0], sizeof(rda), 0); \
+ (RES) |= (rda[2] & 0xf000)!=0xf000; \
+ (RES) |= (rda[3] & 0xf000)!=0x0000; \
+ } while (0); \
+ break; \
+ case 0x01: \
+ case 0x02: \
+ do { \
+ uint32_t rda[4]; \
+ s->phys_mem_read(s->dma_opaque, (ADDR), \
+ (void *)&rda[0], sizeof(rda), 0); \
+ (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
+ (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \
+ } while (0); \
+ break; \
+ case 0x03: \
+ do { \
+ uint32_t rda[4]; \
+ s->phys_mem_read(s->dma_opaque, (ADDR), \
+ (void *)&rda[0], sizeof(rda), 0); \
+ (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \
+ (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
+ } while (0); \
+ break; \
+ } \
+} while (0)
+
+#define CHECK_TMD(ADDR,RES) do { \
+ switch (BCR_SWSTYLE(s)) { \
+ case 0x00: \
+ do { \
+ uint16_t xda[4]; \
+ s->phys_mem_read(s->dma_opaque, (ADDR), \
+ (void *)&xda[0], sizeof(xda), 0); \
+ (RES) |= (xda[2] & 0xf000)!=0xf000; \
+ } while (0); \
+ break; \
+ case 0x01: \
+ case 0x02: \
+ case 0x03: \
+ do { \
+ uint32_t xda[4]; \
+ s->phys_mem_read(s->dma_opaque, (ADDR), \
+ (void *)&xda[0], sizeof(xda), 0); \
+ (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \
+ } while (0); \
+ break; \
+ } \
+} while (0)
+
+#endif
+
+#define PRINT_PKTHDR(BUF) do { \
+ struct qemu_ether_header *hdr = (void *)(BUF); \
+ printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \
+ "shost=%02x:%02x:%02x:%02x:%02x:%02x, " \
+ "type=0x%04x\n", \
+ hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
+ hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
+ hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
+ hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
+ be16_to_cpu(hdr->ether_type)); \
+} while (0)
+
+#define MULTICAST_FILTER_LEN 8
+
+static inline uint32_t lnc_mchash(const uint8_t *ether_addr)
+{
+#define LNC_POLYNOMIAL 0xEDB88320UL
+ uint32_t crc = 0xFFFFFFFF;
+ int idx, bit;
+ uint8_t data;
+
+ for (idx = 0; idx < 6; idx++) {
+ for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++) {
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
+ data >>= 1;
+ }
+ }
+ return crc;
+#undef LNC_POLYNOMIAL
+}
+
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const uint32_t crctab[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+static inline int padr_match(PCNetState *s, const uint8_t *buf, int size)
+{
+ struct qemu_ether_header *hdr = (void *)buf;
+ uint8_t padr[6] = {
+ s->csr[12] & 0xff, s->csr[12] >> 8,
+ s->csr[13] & 0xff, s->csr[13] >> 8,
+ s->csr[14] & 0xff, s->csr[14] >> 8
+ };
+ int result = (!CSR_DRCVPA(s)) && !memcmp(hdr->ether_dhost, padr, 6);
+#ifdef PCNET_DEBUG_MATCH
+ printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, "
+ "padr=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
+ hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
+ padr[0],padr[1],padr[2],padr[3],padr[4],padr[5]);
+ printf("padr_match result=%d\n", result);
+#endif
+ return result;
+}
+
+static inline int padr_bcast(PCNetState *s, const uint8_t *buf, int size)
+{
+ static const uint8_t BCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct qemu_ether_header *hdr = (void *)buf;
+ int result = !CSR_DRCVBC(s) && !memcmp(hdr->ether_dhost, BCAST, 6);
+#ifdef PCNET_DEBUG_MATCH
+ printf("padr_bcast result=%d\n", result);
+#endif
+ return result;
+}
+
+static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size)
+{
+ struct qemu_ether_header *hdr = (void *)buf;
+ if ((*(hdr->ether_dhost)&0x01) &&
+ ((uint64_t *)&s->csr[8])[0] != 0LL) {
+ uint8_t ladr[8] = {
+ s->csr[8] & 0xff, s->csr[8] >> 8,
+ s->csr[9] & 0xff, s->csr[9] >> 8,
+ s->csr[10] & 0xff, s->csr[10] >> 8,
+ s->csr[11] & 0xff, s->csr[11] >> 8
+ };
+ int index = lnc_mchash(hdr->ether_dhost) >> 26;
+ return !!(ladr[index >> 3] & (1 << (index & 7)));
+ }
+ return 0;
+}
+
+static inline target_phys_addr_t pcnet_rdra_addr(PCNetState *s, int idx)
+{
+ while (idx < 1) idx += CSR_RCVRL(s);
+ return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8));
+}
+
+static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
+{
+ int64_t next_time = current_time +
+ muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)),
+ get_ticks_per_sec(), 33000000L);
+ if (next_time <= current_time)
+ next_time = current_time + 1;
+ return next_time;
+}
+
+static void pcnet_poll(PCNetState *s);
+static void pcnet_poll_timer(void *opaque);
+
+static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap);
+static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value);
+static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val);
+
+static void pcnet_s_reset(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_s_reset\n");
+#endif
+
+ s->lnkst = 0x40;
+ s->rdra = 0;
+ s->tdra = 0;
+ s->rap = 0;
+
+ s->bcr[BCR_BSBC] &= ~0x0080;
+
+ s->csr[0] = 0x0004;
+ s->csr[3] = 0x0000;
+ s->csr[4] = 0x0115;
+ s->csr[5] = 0x0000;
+ s->csr[6] = 0x0000;
+ s->csr[8] = 0;
+ s->csr[9] = 0;
+ s->csr[10] = 0;
+ s->csr[11] = 0;
+ s->csr[12] = le16_to_cpu(((uint16_t *)&s->prom[0])[0]);
+ s->csr[13] = le16_to_cpu(((uint16_t *)&s->prom[0])[1]);
+ s->csr[14] = le16_to_cpu(((uint16_t *)&s->prom[0])[2]);
+ s->csr[15] &= 0x21c4;
+ s->csr[72] = 1;
+ s->csr[74] = 1;
+ s->csr[76] = 1;
+ s->csr[78] = 1;
+ s->csr[80] = 0x1410;
+ s->csr[88] = 0x1003;
+ s->csr[89] = 0x0262;
+ s->csr[94] = 0x0000;
+ s->csr[100] = 0x0200;
+ s->csr[103] = 0x0105;
+ s->csr[103] = 0x0105;
+ s->csr[112] = 0x0000;
+ s->csr[114] = 0x0000;
+ s->csr[122] = 0x0000;
+ s->csr[124] = 0x0000;
+
+ s->tx_busy = 0;
+}
+
+static void pcnet_update_irq(PCNetState *s)
+{
+ int isr = 0;
+ s->csr[0] &= ~0x0080;
+
+#if 1
+ if (((s->csr[0] & ~s->csr[3]) & 0x5f00) ||
+ (((s->csr[4]>>1) & ~s->csr[4]) & 0x0115) ||
+ (((s->csr[5]>>1) & s->csr[5]) & 0x0048))
+#else
+ if ((!(s->csr[3] & 0x4000) && !!(s->csr[0] & 0x4000)) /* BABL */ ||
+ (!(s->csr[3] & 0x1000) && !!(s->csr[0] & 0x1000)) /* MISS */ ||
+ (!(s->csr[3] & 0x0100) && !!(s->csr[0] & 0x0100)) /* IDON */ ||
+ (!(s->csr[3] & 0x0200) && !!(s->csr[0] & 0x0200)) /* TINT */ ||
+ (!(s->csr[3] & 0x0400) && !!(s->csr[0] & 0x0400)) /* RINT */ ||
+ (!(s->csr[3] & 0x0800) && !!(s->csr[0] & 0x0800)) /* MERR */ ||
+ (!(s->csr[4] & 0x0001) && !!(s->csr[4] & 0x0002)) /* JAB */ ||
+ (!(s->csr[4] & 0x0004) && !!(s->csr[4] & 0x0008)) /* TXSTRT */ ||
+ (!(s->csr[4] & 0x0010) && !!(s->csr[4] & 0x0020)) /* RCVO */ ||
+ (!(s->csr[4] & 0x0100) && !!(s->csr[4] & 0x0200)) /* MFCO */ ||
+ (!!(s->csr[5] & 0x0040) && !!(s->csr[5] & 0x0080)) /* EXDINT */ ||
+ (!!(s->csr[5] & 0x0008) && !!(s->csr[5] & 0x0010)) /* MPINT */)
+#endif
+ {
+
+ isr = CSR_INEA(s);
+ s->csr[0] |= 0x0080;
+ }
+
+ if (!!(s->csr[4] & 0x0080) && CSR_INEA(s)) { /* UINT */
+ s->csr[4] &= ~0x0080;
+ s->csr[4] |= 0x0040;
+ s->csr[0] |= 0x0080;
+ isr = 1;
+#ifdef PCNET_DEBUG
+ printf("pcnet user int\n");
+#endif
+ }
+
+#if 1
+ if (((s->csr[5]>>1) & s->csr[5]) & 0x0500)
+#else
+ if ((!!(s->csr[5] & 0x0400) && !!(s->csr[5] & 0x0800)) /* SINT */ ||
+ (!!(s->csr[5] & 0x0100) && !!(s->csr[5] & 0x0200)) /* SLPINT */ )
+#endif
+ {
+ isr = 1;
+ s->csr[0] |= 0x0080;
+ }
+
+ if (isr != s->isr) {
+#ifdef PCNET_DEBUG
+ printf("pcnet: INTA=%d\n", isr);
+#endif
+ }
+ qemu_set_irq(s->irq, isr);
+ s->isr = isr;
+}
+
+static void pcnet_init(PCNetState *s)
+{
+ int rlen, tlen;
+ uint16_t padr[3], ladrf[4], mode;
+ uint32_t rdra, tdra;
+
+#ifdef PCNET_DEBUG
+ printf("pcnet_init init_addr=0x%08x\n", PHYSADDR(s,CSR_IADR(s)));
+#endif
+
+ if (BCR_SSIZE32(s)) {
+ struct pcnet_initblk32 initblk;
+ s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
+ (uint8_t *)&initblk, sizeof(initblk), 0);
+ mode = le16_to_cpu(initblk.mode);
+ rlen = initblk.rlen >> 4;
+ tlen = initblk.tlen >> 4;
+ ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
+ ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
+ ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
+ ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
+ padr[0] = le16_to_cpu(initblk.padr[0]);
+ padr[1] = le16_to_cpu(initblk.padr[1]);
+ padr[2] = le16_to_cpu(initblk.padr[2]);
+ rdra = le32_to_cpu(initblk.rdra);
+ tdra = le32_to_cpu(initblk.tdra);
+ } else {
+ struct pcnet_initblk16 initblk;
+ s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
+ (uint8_t *)&initblk, sizeof(initblk), 0);
+ mode = le16_to_cpu(initblk.mode);
+ ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
+ ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
+ ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
+ ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
+ padr[0] = le16_to_cpu(initblk.padr[0]);
+ padr[1] = le16_to_cpu(initblk.padr[1]);
+ padr[2] = le16_to_cpu(initblk.padr[2]);
+ rdra = le32_to_cpu(initblk.rdra);
+ tdra = le32_to_cpu(initblk.tdra);
+ rlen = rdra >> 29;
+ tlen = tdra >> 29;
+ rdra &= 0x00ffffff;
+ tdra &= 0x00ffffff;
+ }
+
+#if defined(PCNET_DEBUG)
+ printf("rlen=%d tlen=%d\n", rlen, tlen);
+#endif
+
+ CSR_RCVRL(s) = (rlen < 9) ? (1 << rlen) : 512;
+ CSR_XMTRL(s) = (tlen < 9) ? (1 << tlen) : 512;
+ s->csr[ 6] = (tlen << 12) | (rlen << 8);
+ s->csr[15] = mode;
+ s->csr[ 8] = ladrf[0];
+ s->csr[ 9] = ladrf[1];
+ s->csr[10] = ladrf[2];
+ s->csr[11] = ladrf[3];
+ s->csr[12] = padr[0];
+ s->csr[13] = padr[1];
+ s->csr[14] = padr[2];
+ s->rdra = PHYSADDR(s, rdra);
+ s->tdra = PHYSADDR(s, tdra);
+
+ CSR_RCVRC(s) = CSR_RCVRL(s);
+ CSR_XMTRC(s) = CSR_XMTRL(s);
+
+#ifdef PCNET_DEBUG
+ printf("pcnet ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]\n",
+ BCR_SSIZE32(s),
+ s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s));
+#endif
+
+ s->csr[0] |= 0x0101;
+ s->csr[0] &= ~0x0004; /* clear STOP bit */
+}
+
+static void pcnet_start(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_start\n");
+#endif
+
+ if (!CSR_DTX(s))
+ s->csr[0] |= 0x0010; /* set TXON */
+
+ if (!CSR_DRX(s))
+ s->csr[0] |= 0x0020; /* set RXON */
+
+ s->csr[0] &= ~0x0004; /* clear STOP bit */
+ s->csr[0] |= 0x0002;
+ pcnet_poll_timer(s);
+}
+
+static void pcnet_stop(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+ printf("pcnet_stop\n");
+#endif
+ s->csr[0] &= ~0x7feb;
+ s->csr[0] |= 0x0014;
+ s->csr[4] &= ~0x02c2;
+ s->csr[5] &= ~0x0011;
+ pcnet_poll_timer(s);
+}
+
+static void pcnet_rdte_poll(PCNetState *s)
+{
+ s->csr[28] = s->csr[29] = 0;
+ if (s->rdra) {
+ int bad = 0;
+#if 1
+ target_phys_addr_t crda = pcnet_rdra_addr(s, CSR_RCVRC(s));
+ target_phys_addr_t nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s));
+ target_phys_addr_t nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s));
+#else
+ target_phys_addr_t crda = s->rdra +
+ (CSR_RCVRL(s) - CSR_RCVRC(s)) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1;
+ target_phys_addr_t nrda = s->rdra +
+ (CSR_RCVRL(s) - nrdc) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1;
+ target_phys_addr_t nnrd = s->rdra +
+ (CSR_RCVRL(s) - nnrc) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+#endif
+
+ CHECK_RMD(crda, bad);
+ if (!bad) {
+ CHECK_RMD(nrda, bad);
+ if (bad || (nrda == crda)) nrda = 0;
+ CHECK_RMD(nnrd, bad);
+ if (bad || (nnrd == crda)) nnrd = 0;
+
+ s->csr[28] = crda & 0xffff;
+ s->csr[29] = crda >> 16;
+ s->csr[26] = nrda & 0xffff;
+ s->csr[27] = nrda >> 16;
+ s->csr[36] = nnrd & 0xffff;
+ s->csr[37] = nnrd >> 16;
+#ifdef PCNET_DEBUG
+ if (bad) {
+ printf("pcnet: BAD RMD RECORDS AFTER 0x" TARGET_FMT_plx "\n",
+ crda);
+ }
+ } else {
+ printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n",
+ crda);
+#endif
+ }
+ }
+
+ if (CSR_CRDA(s)) {
+ struct pcnet_RMD rmd;
+ RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s)));
+ CSR_CRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
+ CSR_CRST(s) = rmd.status;
+#ifdef PCNET_DEBUG_RMD_X
+ printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMDL=0x%04x RMDS=0x%04x RMDM=0x%08x\n",
+ PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s),
+ rmd.buf_length, rmd.status, rmd.msg_length);
+ PRINT_RMD(&rmd);
+#endif
+ } else {
+ CSR_CRBC(s) = CSR_CRST(s) = 0;
+ }
+
+ if (CSR_NRDA(s)) {
+ struct pcnet_RMD rmd;
+ RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s)));
+ CSR_NRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
+ CSR_NRST(s) = rmd.status;
+ } else {
+ CSR_NRBC(s) = CSR_NRST(s) = 0;
+ }
+
+}
+
+static int pcnet_tdte_poll(PCNetState *s)
+{
+ s->csr[34] = s->csr[35] = 0;
+ if (s->tdra) {
+ target_phys_addr_t cxda = s->tdra +
+ (CSR_XMTRL(s) - CSR_XMTRC(s)) *
+ (BCR_SWSTYLE(s) ? 16 : 8);
+ int bad = 0;
+ CHECK_TMD(cxda, bad);
+ if (!bad) {
+ if (CSR_CXDA(s) != cxda) {
+ s->csr[60] = s->csr[34];
+ s->csr[61] = s->csr[35];
+ s->csr[62] = CSR_CXBC(s);
+ s->csr[63] = CSR_CXST(s);
+ }
+ s->csr[34] = cxda & 0xffff;
+ s->csr[35] = cxda >> 16;
+#ifdef PCNET_DEBUG_X
+ printf("pcnet: BAD TMD XDA=0x%08x\n", cxda);
+#endif
+ }
+ }
+
+ if (CSR_CXDA(s)) {
+ struct pcnet_TMD tmd;
+
+ TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+
+ CSR_CXBC(s) = GET_FIELD(tmd.length, TMDL, BCNT);
+ CSR_CXST(s) = tmd.status;
+ } else {
+ CSR_CXBC(s) = CSR_CXST(s) = 0;
+ }
+
+ return !!(CSR_CXST(s) & 0x8000);
+}
+
+int pcnet_can_receive(VLANClientState *nc)
+{
+ PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ if (CSR_STOP(s) || CSR_SPND(s))
+ return 0;
+
+ return sizeof(s->buffer)-16;
+}
+
+#define MIN_BUF_SIZE 60
+
+ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_)
+{
+ PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int is_padr = 0, is_bcast = 0, is_ladr = 0;
+ uint8_t buf1[60];
+ int remaining;
+ int crc_err = 0;
+ int size = size_;
+
+ if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
+ (CSR_LOOP(s) && !s->looptest)) {
+ return -1;
+ }
+#ifdef PCNET_DEBUG
+ printf("pcnet_receive size=%d\n", size);
+#endif
+
+ /* if too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ if (CSR_PROM(s)
+ || (is_padr=padr_match(s, buf, size))
+ || (is_bcast=padr_bcast(s, buf, size))
+ || (is_ladr=ladr_match(s, buf, size))) {
+
+ pcnet_rdte_poll(s);
+
+ if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
+ struct pcnet_RMD rmd;
+ int rcvrc = CSR_RCVRC(s)-1,i;
+ target_phys_addr_t nrda;
+ for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
+ if (rcvrc <= 1)
+ rcvrc = CSR_RCVRL(s);
+ nrda = s->rdra +
+ (CSR_RCVRL(s) - rcvrc) *
+ (BCR_SWSTYLE(s) ? 16 : 8 );
+ RMDLOAD(&rmd, nrda);
+ if (GET_FIELD(rmd.status, RMDS, OWN)) {
+#ifdef PCNET_DEBUG_RMD
+ printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
+ rcvrc, CSR_RCVRC(s));
+#endif
+ CSR_RCVRC(s) = rcvrc;
+ pcnet_rdte_poll(s);
+ break;
+ }
+ }
+ }
+
+ if (!(CSR_CRST(s) & 0x8000)) {
+#ifdef PCNET_DEBUG_RMD
+ printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
+#endif
+ s->csr[0] |= 0x1000; /* Set MISS flag */
+ CSR_MISSC(s)++;
+ } else {
+ uint8_t *src = s->buffer;
+ target_phys_addr_t crda = CSR_CRDA(s);
+ struct pcnet_RMD rmd;
+ int pktcount = 0;
+
+ if (!s->looptest) {
+ memcpy(src, buf, size);
+ /* no need to compute the CRC */
+ src[size] = 0;
+ src[size + 1] = 0;
+ src[size + 2] = 0;
+ src[size + 3] = 0;
+ size += 4;
+ } else if (s->looptest == PCNET_LOOPTEST_CRC ||
+ !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
+ uint32_t fcs = ~0;
+ uint8_t *p = src;
+
+ while (p != &src[size])
+ CRC(fcs, *p++);
+ *(uint32_t *)p = htonl(fcs);
+ size += 4;
+ } else {
+ uint32_t fcs = ~0;
+ uint8_t *p = src;
+
+ while (p != &src[size-4])
+ CRC(fcs, *p++);
+ crc_err = (*(uint32_t *)p != htonl(fcs));
+ }
+
+#ifdef PCNET_DEBUG_MATCH
+ PRINT_PKTHDR(buf);
+#endif
+
+ RMDLOAD(&rmd, PHYSADDR(s,crda));
+ /*if (!CSR_LAPPEN(s))*/
+ SET_FIELD(&rmd.status, RMDS, STP, 1);
+
+#define PCNET_RECV_STORE() do { \
+ int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
+ target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr); \
+ s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
+ src += count; remaining -= count; \
+ SET_FIELD(&rmd.status, RMDS, OWN, 0); \
+ RMDSTORE(&rmd, PHYSADDR(s,crda)); \
+ pktcount++; \
+} while (0)
+
+ remaining = size;
+ PCNET_RECV_STORE();
+ if ((remaining > 0) && CSR_NRDA(s)) {
+ target_phys_addr_t nrda = CSR_NRDA(s);
+#ifdef PCNET_DEBUG_RMD
+ PRINT_RMD(&rmd);
+#endif
+ RMDLOAD(&rmd, PHYSADDR(s,nrda));
+ if (GET_FIELD(rmd.status, RMDS, OWN)) {
+ crda = nrda;
+ PCNET_RECV_STORE();
+#ifdef PCNET_DEBUG_RMD
+ PRINT_RMD(&rmd);
+#endif
+ if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
+ RMDLOAD(&rmd, PHYSADDR(s,nrda));
+ if (GET_FIELD(rmd.status, RMDS, OWN)) {
+ crda = nrda;
+ PCNET_RECV_STORE();
+ }
+ }
+ }
+ }
+
+#undef PCNET_RECV_STORE
+
+ RMDLOAD(&rmd, PHYSADDR(s,crda));
+ if (remaining == 0) {
+ SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
+ SET_FIELD(&rmd.status, RMDS, ENP, 1);
+ SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
+ SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
+ SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
+ if (crc_err) {
+ SET_FIELD(&rmd.status, RMDS, CRC, 1);
+ SET_FIELD(&rmd.status, RMDS, ERR, 1);
+ }
+ } else {
+ SET_FIELD(&rmd.status, RMDS, OFLO, 1);
+ SET_FIELD(&rmd.status, RMDS, BUFF, 1);
+ SET_FIELD(&rmd.status, RMDS, ERR, 1);
+ }
+ RMDSTORE(&rmd, PHYSADDR(s,crda));
+ s->csr[0] |= 0x0400;
+
+#ifdef PCNET_DEBUG
+ printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
+ CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
+#endif
+#ifdef PCNET_DEBUG_RMD
+ PRINT_RMD(&rmd);
+#endif
+
+ while (pktcount--) {
+ if (CSR_RCVRC(s) <= 1)
+ CSR_RCVRC(s) = CSR_RCVRL(s);
+ else
+ CSR_RCVRC(s)--;
+ }
+
+ pcnet_rdte_poll(s);
+
+ }
+ }
+
+ pcnet_poll(s);
+ pcnet_update_irq(s);
+
+ return size_;
+}
+
+static void pcnet_transmit(PCNetState *s)
+{
+ target_phys_addr_t xmit_cxda = 0;
+ int count = CSR_XMTRL(s)-1;
+ int add_crc = 0;
+
+ s->xmit_pos = -1;
+
+ if (!CSR_TXON(s)) {
+ s->csr[0] &= ~0x0008;
+ return;
+ }
+
+ s->tx_busy = 1;
+
+ txagain:
+ if (pcnet_tdte_poll(s)) {
+ struct pcnet_TMD tmd;
+
+ TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+
+#ifdef PCNET_DEBUG_TMD
+ printf(" TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
+ PRINT_TMD(&tmd);
+#endif
+ if (GET_FIELD(tmd.status, TMDS, STP)) {
+ s->xmit_pos = 0;
+ xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
+ if (BCR_SWSTYLE(s) != 1)
+ add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
+ }
+ if (!GET_FIELD(tmd.status, TMDS, ENP)) {
+ int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
+ s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
+ s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
+ s->xmit_pos += bcnt;
+ } else if (s->xmit_pos >= 0) {
+ int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
+ s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
+ s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
+ s->xmit_pos += bcnt;
+#ifdef PCNET_DEBUG
+ printf("pcnet_transmit size=%d\n", s->xmit_pos);
+#endif
+ if (CSR_LOOP(s)) {
+ if (BCR_SWSTYLE(s) == 1)
+ add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
+ s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
+ pcnet_receive(&s->nic->nc, s->buffer, s->xmit_pos);
+ s->looptest = 0;
+ } else
+ if (s->nic)
+ qemu_send_packet(&s->nic->nc, s->buffer, s->xmit_pos);
+
+ s->csr[0] &= ~0x0008; /* clear TDMD */
+ s->csr[4] |= 0x0004; /* set TXSTRT */
+ s->xmit_pos = -1;
+ }
+
+ SET_FIELD(&tmd.status, TMDS, OWN, 0);
+ TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+ if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT)))
+ s->csr[0] |= 0x0200; /* set TINT */
+
+ if (CSR_XMTRC(s)<=1)
+ CSR_XMTRC(s) = CSR_XMTRL(s);
+ else
+ CSR_XMTRC(s)--;
+ if (count--)
+ goto txagain;
+
+ } else
+ if (s->xmit_pos >= 0) {
+ struct pcnet_TMD tmd;
+ TMDLOAD(&tmd, xmit_cxda);
+ SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
+ SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
+ SET_FIELD(&tmd.status, TMDS, ERR, 1);
+ SET_FIELD(&tmd.status, TMDS, OWN, 0);
+ TMDSTORE(&tmd, xmit_cxda);
+ s->csr[0] |= 0x0200; /* set TINT */
+ if (!CSR_DXSUFLO(s)) {
+ s->csr[0] &= ~0x0010;
+ } else
+ if (count--)
+ goto txagain;
+ }
+
+ s->tx_busy = 0;
+}
+
+static void pcnet_poll(PCNetState *s)
+{
+ if (CSR_RXON(s)) {
+ pcnet_rdte_poll(s);
+ }
+
+ if (CSR_TDMD(s) ||
+ (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s)))
+ {
+ /* prevent recursion */
+ if (s->tx_busy)
+ return;
+
+ pcnet_transmit(s);
+ }
+}
+
+static void pcnet_poll_timer(void *opaque)
+{
+ PCNetState *s = opaque;
+
+ qemu_del_timer(s->poll_timer);
+
+ if (CSR_TDMD(s)) {
+ pcnet_transmit(s);
+ }
+
+ pcnet_update_irq(s);
+
+ if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) {
+ uint64_t now = qemu_get_clock(vm_clock) * 33;
+ if (!s->timer || !now)
+ s->timer = now;
+ else {
+ uint64_t t = now - s->timer + CSR_POLL(s);
+ if (t > 0xffffLL) {
+ pcnet_poll(s);
+ CSR_POLL(s) = CSR_PINT(s);
+ } else
+ CSR_POLL(s) = t;
+ }
+ qemu_mod_timer(s->poll_timer,
+ pcnet_get_next_poll_time(s,qemu_get_clock(vm_clock)));
+ }
+}
+
+
+static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value)
+{
+ uint16_t val = new_value;
+#ifdef PCNET_DEBUG_CSR
+ printf("pcnet_csr_writew rap=%d val=0x%04x\n", rap, val);
+#endif
+ switch (rap) {
+ case 0:
+ s->csr[0] &= ~(val & 0x7f00); /* Clear any interrupt flags */
+
+ s->csr[0] = (s->csr[0] & ~0x0040) | (val & 0x0048);
+
+ val = (val & 0x007f) | (s->csr[0] & 0x7f00);
+
+ /* IFF STOP, STRT and INIT are set, clear STRT and INIT */
+ if ((val&7) == 7)
+ val &= ~3;
+
+ if (!CSR_STOP(s) && (val & 4))
+ pcnet_stop(s);
+
+ if (!CSR_INIT(s) && (val & 1))
+ pcnet_init(s);
+
+ if (!CSR_STRT(s) && (val & 2))
+ pcnet_start(s);
+
+ if (CSR_TDMD(s))
+ pcnet_transmit(s);
+
+ return;
+ case 1:
+ case 2:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 18: /* CRBAL */
+ case 19: /* CRBAU */
+ case 20: /* CXBAL */
+ case 21: /* CXBAU */
+ case 22: /* NRBAU */
+ case 23: /* NRBAU */
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39:
+ case 40: /* CRBC */
+ case 41:
+ case 42: /* CXBC */
+ case 43:
+ case 44:
+ case 45:
+ case 46: /* POLL */
+ case 47: /* POLLINT */
+ case 72:
+ case 74:
+ case 76: /* RCVRL */
+ case 78: /* XMTRL */
+ case 112:
+ if (CSR_STOP(s) || CSR_SPND(s))
+ break;
+ return;
+ case 3:
+ break;
+ case 4:
+ s->csr[4] &= ~(val & 0x026a);
+ val &= ~0x026a; val |= s->csr[4] & 0x026a;
+ break;
+ case 5:
+ s->csr[5] &= ~(val & 0x0a90);
+ val &= ~0x0a90; val |= s->csr[5] & 0x0a90;
+ break;
+ case 16:
+ pcnet_csr_writew(s,1,val);
+ return;
+ case 17:
+ pcnet_csr_writew(s,2,val);
+ return;
+ case 58:
+ pcnet_bcr_writew(s,BCR_SWS,val);
+ break;
+ default:
+ return;
+ }
+ s->csr[rap] = val;
+}
+
+static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap)
+{
+ uint32_t val;
+ switch (rap) {
+ case 0:
+ pcnet_update_irq(s);
+ val = s->csr[0];
+ val |= (val & 0x7800) ? 0x8000 : 0;
+ break;
+ case 16:
+ return pcnet_csr_readw(s,1);
+ case 17:
+ return pcnet_csr_readw(s,2);
+ case 58:
+ return pcnet_bcr_readw(s,BCR_SWS);
+ case 88:
+ val = s->csr[89];
+ val <<= 16;
+ val |= s->csr[88];
+ break;
+ default:
+ val = s->csr[rap];
+ }
+#ifdef PCNET_DEBUG_CSR
+ printf("pcnet_csr_readw rap=%d val=0x%04x\n", rap, val);
+#endif
+ return val;
+}
+
+static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val)
+{
+ rap &= 127;
+#ifdef PCNET_DEBUG_BCR
+ printf("pcnet_bcr_writew rap=%d val=0x%04x\n", rap, val);
+#endif
+ switch (rap) {
+ case BCR_SWS:
+ if (!(CSR_STOP(s) || CSR_SPND(s)))
+ return;
+ val &= ~0x0300;
+ switch (val & 0x00ff) {
+ case 0:
+ val |= 0x0200;
+ break;
+ case 1:
+ val |= 0x0100;
+ break;
+ case 2:
+ case 3:
+ val |= 0x0300;
+ break;
+ default:
+ printf("Bad SWSTYLE=0x%02x\n", val & 0xff);
+ val = 0x0200;
+ break;
+ }
+#ifdef PCNET_DEBUG
+ printf("BCR_SWS=0x%04x\n", val);
+#endif
+ case BCR_LNKST:
+ case BCR_LED1:
+ case BCR_LED2:
+ case BCR_LED3:
+ case BCR_MC:
+ case BCR_FDC:
+ case BCR_BSBC:
+ case BCR_EECAS:
+ case BCR_PLAT:
+ s->bcr[rap] = val;
+ break;
+ default:
+ break;
+ }
+}
+
+uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap)
+{
+ uint32_t val;
+ rap &= 127;
+ switch (rap) {
+ case BCR_LNKST:
+ case BCR_LED1:
+ case BCR_LED2:
+ case BCR_LED3:
+ val = s->bcr[rap] & ~0x8000;
+ val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0;
+ break;
+ default:
+ val = rap < 32 ? s->bcr[rap] : 0;
+ break;
+ }
+#ifdef PCNET_DEBUG_BCR
+ printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val);
+#endif
+ return val;
+}
+
+void pcnet_h_reset(void *opaque)
+{
+ PCNetState *s = opaque;
+ int i;
+ uint16_t checksum;
+
+ /* Initialize the PROM */
+
+ memcpy(s->prom, s->conf.macaddr.a, 6);
+ s->prom[12] = s->prom[13] = 0x00;
+ s->prom[14] = s->prom[15] = 0x57;
+
+ for (i = 0,checksum = 0; i < 16; i++)
+ checksum += s->prom[i];
+ *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum);
+
+
+ s->bcr[BCR_MSRDA] = 0x0005;
+ s->bcr[BCR_MSWRA] = 0x0005;
+ s->bcr[BCR_MC ] = 0x0002;
+ s->bcr[BCR_LNKST] = 0x00c0;
+ s->bcr[BCR_LED1 ] = 0x0084;
+ s->bcr[BCR_LED2 ] = 0x0088;
+ s->bcr[BCR_LED3 ] = 0x0090;
+ s->bcr[BCR_FDC ] = 0x0000;
+ s->bcr[BCR_BSBC ] = 0x9001;
+ s->bcr[BCR_EECAS] = 0x0002;
+ s->bcr[BCR_SWS ] = 0x0200;
+ s->bcr[BCR_PLAT ] = 0xff06;
+
+ pcnet_s_reset(s);
+ pcnet_update_irq(s);
+ pcnet_poll_timer(s);
+}
+
+void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCNetState *s = opaque;
+ pcnet_poll_timer(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_writew addr=0x%08x val=0x%04x\n", addr, val);
+#endif
+ if (!BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ pcnet_csr_writew(s, s->rap, val);
+ break;
+ case 0x02:
+ s->rap = val & 0x7f;
+ break;
+ case 0x06:
+ pcnet_bcr_writew(s, s->rap, val);
+ break;
+ }
+ }
+ pcnet_update_irq(s);
+}
+
+uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr)
+{
+ PCNetState *s = opaque;
+ uint32_t val = -1;
+ pcnet_poll_timer(s);
+ if (!BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ val = pcnet_csr_readw(s, s->rap);
+ break;
+ case 0x02:
+ val = s->rap;
+ break;
+ case 0x04:
+ pcnet_s_reset(s);
+ val = 0;
+ break;
+ case 0x06:
+ val = pcnet_bcr_readw(s, s->rap);
+ break;
+ }
+ }
+ pcnet_update_irq(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_readw addr=0x%08x val=0x%04x\n", addr, val & 0xffff);
+#endif
+ return val;
+}
+
+void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCNetState *s = opaque;
+ pcnet_poll_timer(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_writel addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+ if (BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ pcnet_csr_writew(s, s->rap, val & 0xffff);
+ break;
+ case 0x04:
+ s->rap = val & 0x7f;
+ break;
+ case 0x0c:
+ pcnet_bcr_writew(s, s->rap, val & 0xffff);
+ break;
+ }
+ } else
+ if ((addr & 0x0f) == 0) {
+ /* switch device to dword i/o mode */
+ pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080);
+#ifdef PCNET_DEBUG_IO
+ printf("device switched into dword i/o mode\n");
+#endif
+ }
+ pcnet_update_irq(s);
+}
+
+uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr)
+{
+ PCNetState *s = opaque;
+ uint32_t val = -1;
+ pcnet_poll_timer(s);
+ if (BCR_DWIO(s)) {
+ switch (addr & 0x0f) {
+ case 0x00: /* RDP */
+ val = pcnet_csr_readw(s, s->rap);
+ break;
+ case 0x04:
+ val = s->rap;
+ break;
+ case 0x08:
+ pcnet_s_reset(s);
+ val = 0;
+ break;
+ case 0x0c:
+ val = pcnet_bcr_readw(s, s->rap);
+ break;
+ }
+ }
+ pcnet_update_irq(s);
+#ifdef PCNET_DEBUG_IO
+ printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+ return val;
+}
+
+static bool is_version_2(void *opaque, int version_id)
+{
+ return version_id == 2;
+}
+
+const VMStateDescription vmstate_pcnet = {
+ .name = "pcnet",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(rap, PCNetState),
+ VMSTATE_INT32(isr, PCNetState),
+ VMSTATE_INT32(lnkst, PCNetState),
+ VMSTATE_UINT32(rdra, PCNetState),
+ VMSTATE_UINT32(tdra, PCNetState),
+ VMSTATE_BUFFER(prom, PCNetState),
+ VMSTATE_UINT16_ARRAY(csr, PCNetState, 128),
+ VMSTATE_UINT16_ARRAY(bcr, PCNetState, 32),
+ VMSTATE_UINT64(timer, PCNetState),
+ VMSTATE_INT32(xmit_pos, PCNetState),
+ VMSTATE_BUFFER(buffer, PCNetState),
+ VMSTATE_UNUSED_TEST(is_version_2, 4),
+ VMSTATE_INT32(tx_busy, PCNetState),
+ VMSTATE_TIMER(poll_timer, PCNetState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void pcnet_common_cleanup(PCNetState *d)
+{
+ d->nic = NULL;
+}
+
+int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
+{
+ s->poll_timer = qemu_new_timer(vm_clock, pcnet_poll_timer, s);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(info, &s->conf, dev->info->name, dev->id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+ add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
+
+ return 0;
+}
diff --git a/hw/pcnet.h b/hw/pcnet.h
new file mode 100644
index 0000000..534bdf9
--- /dev/null
+++ b/hw/pcnet.h
@@ -0,0 +1,42 @@
+#define PCNET_IOPORT_SIZE 0x20
+#define PCNET_PNPMMIO_SIZE 0x20
+
+#define PCNET_LOOPTEST_CRC 1
+#define PCNET_LOOPTEST_NOCRC 2
+
+
+typedef struct PCNetState_st PCNetState;
+
+struct PCNetState_st {
+ NICState *nic;
+ NICConf conf;
+ QEMUTimer *poll_timer;
+ int rap, isr, lnkst;
+ uint32_t rdra, tdra;
+ uint8_t prom[16];
+ uint16_t csr[128];
+ uint16_t bcr[32];
+ uint64_t timer;
+ int mmio_index, xmit_pos;
+ uint8_t buffer[4096];
+ int tx_busy;
+ qemu_irq irq;
+ void (*phys_mem_read)(void *dma_opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap);
+ void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap);
+ void *dma_opaque;
+ int looptest;
+};
+
+void pcnet_h_reset(void *opaque);
+void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val);
+uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr);
+void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val);
+uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr);
+uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap);
+int pcnet_can_receive(VLANClientState *nc);
+ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_);
+void pcnet_common_cleanup(PCNetState *d);
+int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info);
+extern const VMStateDescription vmstate_pcnet;
diff --git a/hw/pcspk.c b/hw/pcspk.c
new file mode 100644
index 0000000..fb5f763
--- /dev/null
+++ b/hw/pcspk.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU PC speaker emulation
+ *
+ * Copyright (c) 2006 Joachim Henke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "audio/audio.h"
+#include "qemu-timer.h"
+#include "i8254.h"
+#include "qemu-kvm.h"
+
+#define PCSPK_BUF_LEN 1792
+#define PCSPK_SAMPLE_RATE 32000
+#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1)
+#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ)
+
+typedef struct {
+ uint8_t sample_buf[PCSPK_BUF_LEN];
+ QEMUSoundCard card;
+ SWVoiceOut *voice;
+ PITState *pit;
+ unsigned int pit_count;
+ unsigned int samples;
+ unsigned int play_pos;
+ int data_on;
+ int dummy_refresh_clock;
+} PCSpkState;
+
+static const char *s_spk = "pcspk";
+static PCSpkState pcspk_state;
+
+#ifdef CONFIG_KVM_PIT
+static void kvm_get_pit_ch2(PITState *pit,
+ struct kvm_pit_state *inkernel_state)
+{
+ struct kvm_pit_state pit_state;
+
+ if (kvm_enabled() && kvm_pit_in_kernel()) {
+ kvm_get_pit(kvm_context, &pit_state);
+ pit->channels[2].mode = pit_state.channels[2].mode;
+ pit->channels[2].count = pit_state.channels[2].count;
+ pit->channels[2].count_load_time = pit_state.channels[2].count_load_time;
+ pit->channels[2].gate = pit_state.channels[2].gate;
+ if (inkernel_state) {
+ memcpy(inkernel_state, &pit_state, sizeof(*inkernel_state));
+ }
+ }
+}
+
+static void kvm_set_pit_ch2(PITState *pit,
+ struct kvm_pit_state *inkernel_state)
+{
+ if (kvm_enabled() && kvm_pit_in_kernel()) {
+ inkernel_state->channels[2].mode = pit->channels[2].mode;
+ inkernel_state->channels[2].count = pit->channels[2].count;
+ inkernel_state->channels[2].count_load_time =
+ pit->channels[2].count_load_time;
+ inkernel_state->channels[2].gate = pit->channels[2].gate;
+ kvm_set_pit(kvm_context, inkernel_state);
+ }
+}
+#else
+static inline void kvm_get_pit_ch2(PITState *pit,
+ struct kvm_pit_state *inkernel_state) { }
+static inline void kvm_set_pit_ch2(PITState *pit,
+ struct kvm_pit_state *inkernel_state) { }
+#endif
+
+static inline void generate_samples(PCSpkState *s)
+{
+ unsigned int i;
+
+ if (s->pit_count) {
+ const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count;
+ const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m;
+
+ /* multiple of wavelength for gapless looping */
+ s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1;
+ for (i = 0; i < s->samples; ++i)
+ s->sample_buf[i] = (64 & (n * i >> 25)) - 32;
+ } else {
+ s->samples = PCSPK_BUF_LEN;
+ for (i = 0; i < PCSPK_BUF_LEN; ++i)
+ s->sample_buf[i] = 128; /* silence */
+ }
+}
+
+static void pcspk_callback(void *opaque, int free)
+{
+ PCSpkState *s = opaque;
+ unsigned int n;
+
+ kvm_get_pit_ch2(s->pit, NULL);
+
+ if (pit_get_mode(s->pit, 2) != 3)
+ return;
+
+ n = pit_get_initial_count(s->pit, 2);
+ /* avoid frequencies that are not reproducible with sample rate */
+ if (n < PCSPK_MIN_COUNT)
+ n = 0;
+
+ if (s->pit_count != n) {
+ s->pit_count = n;
+ s->play_pos = 0;
+ generate_samples(s);
+ }
+
+ while (free > 0) {
+ n = audio_MIN(s->samples - s->play_pos, (unsigned int)free);
+ n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
+ if (!n)
+ break;
+ s->play_pos = (s->play_pos + n) % s->samples;
+ free -= n;
+ }
+}
+
+int pcspk_audio_init(qemu_irq *pic)
+{
+ PCSpkState *s = &pcspk_state;
+ struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
+
+ AUD_register_card(s_spk, &s->card);
+
+ s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as);
+ if (!s->voice) {
+ AUD_log(s_spk, "Could not open voice\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)
+{
+ PCSpkState *s = opaque;
+ int out;
+
+ kvm_get_pit_ch2(s->pit, NULL);
+
+ s->dummy_refresh_clock ^= (1 << 4);
+ out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5;
+
+ return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out;
+}
+
+static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ struct kvm_pit_state inkernel_state;
+ PCSpkState *s = opaque;
+ const int gate = val & 1;
+
+ kvm_get_pit_ch2(s->pit, &inkernel_state);
+
+ s->data_on = (val >> 1) & 1;
+ pit_set_gate(s->pit, 2, gate);
+ if (s->voice) {
+ if (gate) /* restart */
+ s->play_pos = 0;
+ AUD_set_active_out(s->voice, gate & s->data_on);
+ }
+
+ kvm_set_pit_ch2(s->pit, &inkernel_state);
+}
+
+void pcspk_init(PITState *pit)
+{
+ PCSpkState *s = &pcspk_state;
+
+ s->pit = pit;
+ register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s);
+ register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s);
+}
diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c
new file mode 100644
index 0000000..42de459
--- /dev/null
+++ b/hw/petalogix_s3adsp1800_mmu.c
@@ -0,0 +1,225 @@
+/*
+ * Model of Petalogix linux reference design targeting Xilinx Spartan 3ADSP-1800
+ * boards.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "xilinx.h"
+#include "loader.h"
+#include "elf.h"
+#include "blockdev.h"
+
+#define LMB_BRAM_SIZE (128 * 1024)
+#define FLASH_SIZE (16 * 1024 * 1024)
+
+static struct
+{
+ uint32_t bootstrap_pc;
+ uint32_t cmdline;
+ uint32_t fdt;
+} boot_info;
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ env->regs[5] = boot_info.cmdline;
+ env->regs[7] = boot_info.fdt;
+ env->sregs[SR_PC] = boot_info.bootstrap_pc;
+}
+
+#define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb"
+static int petalogix_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ char *path;
+ int fdt_size;
+#ifdef CONFIG_FDT
+ void *fdt;
+ int r;
+
+ /* Try the local "mb.dtb" override. */
+ fdt = load_device_tree("mb.dtb", &fdt_size);
+ if (!fdt) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt = load_device_tree(path, &fdt_size);
+ qemu_free(path);
+ }
+ if (!fdt)
+ return 0;
+ }
+
+ r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
+ if (r < 0)
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
+#else
+ /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
+ to the kernel. */
+ fdt_size = load_image_targphys("mb.dtb", addr, 0x10000);
+ if (fdt_size < 0) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt_size = load_image_targphys(path, addr, 0x10000);
+ qemu_free(path);
+ }
+ }
+
+ if (kernel_cmdline) {
+ fprintf(stderr,
+ "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
+ }
+#endif
+ return fdt_size;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return addr - 0x30000000LL;
+}
+
+static void
+petalogix_s3adsp1800_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ DeviceState *dev;
+ CPUState *env;
+ int kernel_size;
+ DriveInfo *dinfo;
+ int i;
+ target_phys_addr_t ddr_base = 0x90000000;
+ ram_addr_t phys_lmb_bram;
+ ram_addr_t phys_ram;
+ ram_addr_t phys_flash;
+ qemu_irq irq[32], *cpu_irq;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "microblaze";
+ }
+ env = cpu_init(cpu_model);
+
+ env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
+ qemu_register_reset(main_cpu_reset, env);
+
+ /* Attach emulated BRAM through the LMB. */
+ phys_lmb_bram = qemu_ram_alloc(NULL, "petalogix_s3adsp1800.lmb_bram",
+ LMB_BRAM_SIZE);
+ cpu_register_physical_memory(0x00000000, LMB_BRAM_SIZE,
+ phys_lmb_bram | IO_MEM_RAM);
+
+ phys_ram = qemu_ram_alloc(NULL, "petalogix_s3adsp1800.ram", ram_size);
+ cpu_register_physical_memory(ddr_base, ram_size, phys_ram | IO_MEM_RAM);
+
+ phys_flash = qemu_ram_alloc(NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(0xa0000000, phys_flash,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ FLASH_SIZE >> 16,
+ 1, 0x89, 0x18, 0x0000, 0x0, 1);
+
+ cpu_irq = microblaze_pic_init_cpu(env);
+ dev = xilinx_intc_create(0x81800000, cpu_irq[0], 2);
+ for (i = 0; i < 32; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+
+ sysbus_create_simple("xilinx,uartlite", 0x84000000, irq[3]);
+ /* 2 timers at irq 2 @ 62 Mhz. */
+ xilinx_timer_create(0x83c00000, irq[0], 2, 62 * 1000000);
+ xilinx_ethlite_create(&nd_table[0], 0x81000000, irq[1], 0, 0);
+
+ if (kernel_filename) {
+ uint64_t entry, low, high;
+ uint32_t base32;
+
+ /* Boots a kernel elf binary. */
+ kernel_size = load_elf(kernel_filename, NULL, NULL,
+ &entry, &low, &high,
+ 1, ELF_MACHINE, 0);
+ base32 = entry;
+ if (base32 == 0xc0000000) {
+ kernel_size = load_elf(kernel_filename, translate_kernel_address,
+ NULL, &entry, NULL, NULL,
+ 1, ELF_MACHINE, 0);
+ }
+ /* Always boot into physical ram. */
+ boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff);
+
+ /* If it wasn't an ELF image, try an u-boot image. */
+ if (kernel_size < 0) {
+ target_phys_addr_t uentry, loadaddr;
+
+ kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+ boot_info.bootstrap_pc = uentry;
+ high = (loadaddr + kernel_size + 3) & ~3;
+ }
+
+ /* Not an ELF image nor an u-boot image, try a RAW image. */
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, ddr_base,
+ ram_size);
+ boot_info.bootstrap_pc = ddr_base;
+ high = (ddr_base + kernel_size + 3) & ~3;
+ }
+
+ boot_info.cmdline = high + 4096;
+ if (kernel_cmdline && strlen(kernel_cmdline)) {
+ pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
+ }
+ /* Provide a device-tree. */
+ boot_info.fdt = boot_info.cmdline + 4096;
+ petalogix_load_device_tree(boot_info.fdt, ram_size,
+ 0, 0,
+ kernel_cmdline);
+ }
+}
+
+static QEMUMachine petalogix_s3adsp1800_machine = {
+ .name = "petalogix-s3adsp1800",
+ .desc = "Petalogix linux refdesign for xilinx Spartan 3ADSP1800",
+ .init = petalogix_s3adsp1800_init,
+ .is_default = 1
+};
+
+static void petalogix_s3adsp1800_machine_init(void)
+{
+ qemu_register_machine(&petalogix_s3adsp1800_machine);
+}
+
+machine_init(petalogix_s3adsp1800_machine_init);
diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c
new file mode 100644
index 0000000..fb20dfb
--- /dev/null
+++ b/hw/pflash_cfi01.c
@@ -0,0 +1,726 @@
+/*
+ * CFI parallel flash with Intel command set emulation
+ *
+ * Copyright (c) 2006 Thorsten Zitterell
+ * Copyright (c) 2005 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+/*
+ * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
+ * Supported commands/modes are:
+ * - flash read
+ * - flash write
+ * - flash ID read
+ * - sector erase
+ * - CFI queries
+ *
+ * It does not support timings
+ * It does not support flash interleaving
+ * It does not implement software data protection as found in many real chips
+ * It does not implement erase suspend/resume commands
+ * It does not implement multiple sectors erase
+ *
+ * It does not implement much more ...
+ */
+
+#include "hw.h"
+#include "flash.h"
+#include "block.h"
+#include "qemu-timer.h"
+
+#define PFLASH_BUG(fmt, ...) \
+do { \
+ printf("PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \
+ exit(1); \
+} while(0)
+
+/* #define PFLASH_DEBUG */
+#ifdef PFLASH_DEBUG
+#define DPRINTF(fmt, ...) \
+do { \
+ printf("PFLASH: " fmt , ## __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+struct pflash_t {
+ BlockDriverState *bs;
+ target_phys_addr_t base;
+ target_phys_addr_t sector_len;
+ target_phys_addr_t total_len;
+ int width;
+ int wcycle; /* if 0, the flash is read normally */
+ int bypass;
+ int ro;
+ uint8_t cmd;
+ uint8_t status;
+ uint16_t ident[4];
+ uint8_t cfi_len;
+ uint8_t cfi_table[0x52];
+ target_phys_addr_t counter;
+ unsigned int writeblock_size;
+ QEMUTimer *timer;
+ ram_addr_t off;
+ int fl_mem;
+ void *storage;
+};
+
+static void pflash_timer (void *opaque)
+{
+ pflash_t *pfl = opaque;
+
+ DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
+ /* Reset flash */
+ pfl->status ^= 0x80;
+ if (pfl->bypass) {
+ pfl->wcycle = 2;
+ } else {
+ cpu_register_physical_memory(pfl->base, pfl->total_len,
+ pfl->off | IO_MEM_ROMD | pfl->fl_mem);
+ pfl->wcycle = 0;
+ }
+ pfl->cmd = 0;
+}
+
+static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
+ int width, int be)
+{
+ target_phys_addr_t boff;
+ uint32_t ret;
+ uint8_t *p;
+
+ ret = -1;
+ boff = offset & 0xFF; /* why this here ?? */
+
+ if (pfl->width == 2)
+ boff = boff >> 1;
+ else if (pfl->width == 4)
+ boff = boff >> 2;
+
+#if 0
+ DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
+ __func__, offset, pfl->cmd, width);
+#endif
+ switch (pfl->cmd) {
+ case 0x00:
+ /* Flash area read */
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ ret = p[offset];
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
+ __func__, offset, ret);
+ break;
+ case 2:
+ if (be) {
+ ret = p[offset] << 8;
+ ret |= p[offset + 1];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ }
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
+ __func__, offset, ret);
+ break;
+ case 4:
+ if (be) {
+ ret = p[offset] << 24;
+ ret |= p[offset + 1] << 16;
+ ret |= p[offset + 2] << 8;
+ ret |= p[offset + 3];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 2] << 16;
+ ret |= p[offset + 3] << 24;
+ }
+ DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
+ __func__, offset, ret);
+ break;
+ default:
+ DPRINTF("BUG in %s\n", __func__);
+ }
+
+ break;
+ case 0x20: /* Block erase */
+ case 0x50: /* Clear status register */
+ case 0x60: /* Block /un)lock */
+ case 0x70: /* Status Register */
+ case 0xe8: /* Write block */
+ /* Status register read */
+ ret = pfl->status;
+ DPRINTF("%s: status %x\n", __func__, ret);
+ break;
+ case 0x90:
+ switch (boff) {
+ case 0:
+ ret = pfl->ident[0] << 8 | pfl->ident[1];
+ DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
+ break;
+ case 1:
+ ret = pfl->ident[2] << 8 | pfl->ident[3];
+ DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
+ break;
+ default:
+ DPRINTF("%s: Read Device Information boff=%x\n", __func__, boff);
+ ret = 0;
+ break;
+ }
+ break;
+ case 0x98: /* Query mode */
+ if (boff > pfl->cfi_len)
+ ret = 0;
+ else
+ ret = pfl->cfi_table[boff];
+ break;
+ default:
+ /* This should never happen : reset state & treat it as a read */
+ DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ }
+ return ret;
+}
+
+/* update flash content on disk */
+static void pflash_update(pflash_t *pfl, int offset,
+ int size)
+{
+ int offset_end;
+ if (pfl->bs) {
+ offset_end = offset + size;
+ /* round to sectors */
+ offset = offset >> 9;
+ offset_end = (offset_end + 511) >> 9;
+ bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
+ offset_end - offset);
+ }
+}
+
+static inline void pflash_data_write(pflash_t *pfl, target_phys_addr_t offset,
+ uint32_t value, int width, int be)
+{
+ uint8_t *p = pfl->storage;
+
+ DPRINTF("%s: block write offset " TARGET_FMT_plx
+ " value %x counter " TARGET_FMT_plx "\n",
+ __func__, offset, value, pfl->counter);
+ switch (width) {
+ case 1:
+ p[offset] = value;
+ break;
+ case 2:
+ if (be) {
+ p[offset] = value >> 8;
+ p[offset + 1] = value;
+ } else {
+ p[offset] = value;
+ p[offset + 1] = value >> 8;
+ }
+ break;
+ case 4:
+ if (be) {
+ p[offset] = value >> 24;
+ p[offset + 1] = value >> 16;
+ p[offset + 2] = value >> 8;
+ p[offset + 3] = value;
+ } else {
+ p[offset] = value;
+ p[offset + 1] = value >> 8;
+ p[offset + 2] = value >> 16;
+ p[offset + 3] = value >> 24;
+ }
+ break;
+ }
+
+}
+
+static void pflash_write(pflash_t *pfl, target_phys_addr_t offset,
+ uint32_t value, int width, int be)
+{
+ uint8_t *p;
+ uint8_t cmd;
+
+ cmd = value;
+
+ DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n",
+ __func__, offset, value, width, pfl->wcycle);
+
+ if (!pfl->wcycle) {
+ /* Set the device in I/O access mode */
+ cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem);
+ }
+
+ switch (pfl->wcycle) {
+ case 0:
+ /* read mode */
+ switch (cmd) {
+ case 0x00: /* ??? */
+ goto reset_flash;
+ case 0x10: /* Single Byte Program */
+ case 0x40: /* Single Byte Program */
+ DPRINTF("%s: Single Byte Program\n", __func__);
+ break;
+ case 0x20: /* Block erase */
+ p = pfl->storage;
+ offset &= ~(pfl->sector_len - 1);
+
+ DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes "
+ TARGET_FMT_plx "\n",
+ __func__, offset, pfl->sector_len);
+
+ memset(p + offset, 0xff, pfl->sector_len);
+ pflash_update(pfl, offset, pfl->sector_len);
+ pfl->status |= 0x80; /* Ready! */
+ break;
+ case 0x50: /* Clear status bits */
+ DPRINTF("%s: Clear status bits\n", __func__);
+ pfl->status = 0x0;
+ goto reset_flash;
+ case 0x60: /* Block (un)lock */
+ DPRINTF("%s: Block unlock\n", __func__);
+ break;
+ case 0x70: /* Status Register */
+ DPRINTF("%s: Read status register\n", __func__);
+ pfl->cmd = cmd;
+ return;
+ case 0x90: /* Read Device ID */
+ DPRINTF("%s: Read Device information\n", __func__);
+ pfl->cmd = cmd;
+ return;
+ case 0x98: /* CFI query */
+ DPRINTF("%s: CFI query\n", __func__);
+ break;
+ case 0xe8: /* Write to buffer */
+ DPRINTF("%s: Write to buffer\n", __func__);
+ pfl->status |= 0x80; /* Ready! */
+ break;
+ case 0xff: /* Read array mode */
+ DPRINTF("%s: Read array mode\n", __func__);
+ goto reset_flash;
+ default:
+ goto error_flash;
+ }
+ pfl->wcycle++;
+ pfl->cmd = cmd;
+ return;
+ case 1:
+ switch (pfl->cmd) {
+ case 0x10: /* Single Byte Program */
+ case 0x40: /* Single Byte Program */
+ DPRINTF("%s: Single Byte Program\n", __func__);
+ pflash_data_write(pfl, offset, value, width, be);
+ pflash_update(pfl, offset, width);
+ pfl->status |= 0x80; /* Ready! */
+ pfl->wcycle = 0;
+ break;
+ case 0x20: /* Block erase */
+ case 0x28:
+ if (cmd == 0xd0) { /* confirm */
+ pfl->wcycle = 0;
+ pfl->status |= 0x80;
+ } else if (cmd == 0xff) { /* read array mode */
+ goto reset_flash;
+ } else
+ goto error_flash;
+
+ break;
+ case 0xe8:
+ DPRINTF("%s: block write of %x bytes\n", __func__, value);
+ pfl->counter = value;
+ pfl->wcycle++;
+ break;
+ case 0x60:
+ if (cmd == 0xd0) {
+ pfl->wcycle = 0;
+ pfl->status |= 0x80;
+ } else if (cmd == 0x01) {
+ pfl->wcycle = 0;
+ pfl->status |= 0x80;
+ } else if (cmd == 0xff) {
+ goto reset_flash;
+ } else {
+ DPRINTF("%s: Unknown (un)locking command\n", __func__);
+ goto reset_flash;
+ }
+ break;
+ case 0x98:
+ if (cmd == 0xff) {
+ goto reset_flash;
+ } else {
+ DPRINTF("%s: leaving query mode\n", __func__);
+ }
+ break;
+ default:
+ goto error_flash;
+ }
+ return;
+ case 2:
+ switch (pfl->cmd) {
+ case 0xe8: /* Block write */
+ pflash_data_write(pfl, offset, value, width, be);
+
+ pfl->status |= 0x80;
+
+ if (!pfl->counter) {
+ target_phys_addr_t mask = pfl->writeblock_size - 1;
+ mask = ~mask;
+
+ DPRINTF("%s: block write finished\n", __func__);
+ pfl->wcycle++;
+ /* Flush the entire write buffer onto backing storage. */
+ pflash_update(pfl, offset & mask, pfl->writeblock_size);
+ }
+
+ pfl->counter--;
+ break;
+ default:
+ goto error_flash;
+ }
+ return;
+ case 3: /* Confirm mode */
+ switch (pfl->cmd) {
+ case 0xe8: /* Block write */
+ if (cmd == 0xd0) {
+ pfl->wcycle = 0;
+ pfl->status |= 0x80;
+ } else {
+ DPRINTF("%s: unknown command for \"write block\"\n", __func__);
+ PFLASH_BUG("Write block confirm");
+ goto reset_flash;
+ }
+ break;
+ default:
+ goto error_flash;
+ }
+ return;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid write state\n", __func__);
+ goto reset_flash;
+ }
+ return;
+
+ error_flash:
+ printf("%s: Unimplemented flash cmd sequence "
+ "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)\n",
+ __func__, offset, pfl->wcycle, pfl->cmd, value);
+
+ reset_flash:
+ cpu_register_physical_memory(pfl->base, pfl->total_len,
+ pfl->off | IO_MEM_ROMD | pfl->fl_mem);
+
+ pfl->bypass = 0;
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ return;
+}
+
+
+static uint32_t pflash_readb_be(void *opaque, target_phys_addr_t addr)
+{
+ return pflash_read(opaque, addr, 1, 1);
+}
+
+static uint32_t pflash_readb_le(void *opaque, target_phys_addr_t addr)
+{
+ return pflash_read(opaque, addr, 1, 0);
+}
+
+static uint32_t pflash_readw_be(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 2, 1);
+}
+
+static uint32_t pflash_readw_le(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 2, 0);
+}
+
+static uint32_t pflash_readl_be(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 4, 1);
+}
+
+static uint32_t pflash_readl_le(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 4, 0);
+}
+
+static void pflash_writeb_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_write(opaque, addr, value, 1, 1);
+}
+
+static void pflash_writeb_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_write(opaque, addr, value, 1, 0);
+}
+
+static void pflash_writew_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 2, 1);
+}
+
+static void pflash_writew_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 2, 0);
+}
+
+static void pflash_writel_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 4, 1);
+}
+
+static void pflash_writel_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 4, 0);
+}
+
+static CPUWriteMemoryFunc * const pflash_write_ops_be[] = {
+ &pflash_writeb_be,
+ &pflash_writew_be,
+ &pflash_writel_be,
+};
+
+static CPUReadMemoryFunc * const pflash_read_ops_be[] = {
+ &pflash_readb_be,
+ &pflash_readw_be,
+ &pflash_readl_be,
+};
+
+static CPUWriteMemoryFunc * const pflash_write_ops_le[] = {
+ &pflash_writeb_le,
+ &pflash_writew_le,
+ &pflash_writel_le,
+};
+
+static CPUReadMemoryFunc * const pflash_read_ops_le[] = {
+ &pflash_readb_le,
+ &pflash_readw_le,
+ &pflash_readl_le,
+};
+
+/* Count trailing zeroes of a 32 bits quantity */
+static int ctz32 (uint32_t n)
+{
+ int ret;
+
+ ret = 0;
+ if (!(n & 0xFFFF)) {
+ ret += 16;
+ n = n >> 16;
+ }
+ if (!(n & 0xFF)) {
+ ret += 8;
+ n = n >> 8;
+ }
+ if (!(n & 0xF)) {
+ ret += 4;
+ n = n >> 4;
+ }
+ if (!(n & 0x3)) {
+ ret += 2;
+ n = n >> 2;
+ }
+ if (!(n & 0x1)) {
+ ret++;
+#if 0 /* This is not necessary as n is never 0 */
+ n = n >> 1;
+#endif
+ }
+#if 0 /* This is not necessary as n is never 0 */
+ if (!n)
+ ret++;
+#endif
+
+ return ret;
+}
+
+pflash_t *pflash_cfi01_register(target_phys_addr_t base, ram_addr_t off,
+ BlockDriverState *bs, uint32_t sector_len,
+ int nb_blocs, int width,
+ uint16_t id0, uint16_t id1,
+ uint16_t id2, uint16_t id3,
+ int be)
+{
+ pflash_t *pfl;
+ target_phys_addr_t total_len;
+ int ret;
+
+ total_len = sector_len * nb_blocs;
+
+ /* XXX: to be fixed */
+#if 0
+ if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
+ total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
+ return NULL;
+#endif
+
+ pfl = qemu_mallocz(sizeof(pflash_t));
+
+ /* FIXME: Allocate ram ourselves. */
+ pfl->storage = qemu_get_ram_ptr(off);
+ if (be) {
+ pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_be,
+ pflash_write_ops_be, pfl,
+ DEVICE_NATIVE_ENDIAN);
+ } else {
+ pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_le,
+ pflash_write_ops_le, pfl,
+ DEVICE_NATIVE_ENDIAN);
+ }
+ pfl->off = off;
+ cpu_register_physical_memory(base, total_len,
+ off | pfl->fl_mem | IO_MEM_ROMD);
+
+ pfl->bs = bs;
+ if (pfl->bs) {
+ /* read the initial flash content */
+ ret = bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
+ if (ret < 0) {
+ cpu_unregister_io_memory(pfl->fl_mem);
+ qemu_free(pfl);
+ return NULL;
+ }
+ }
+#if 0 /* XXX: there should be a bit to set up read-only,
+ * the same way the hardware does (with WP pin).
+ */
+ pfl->ro = 1;
+#else
+ pfl->ro = 0;
+#endif
+ pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl);
+ pfl->base = base;
+ pfl->sector_len = sector_len;
+ pfl->total_len = total_len;
+ pfl->width = width;
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ pfl->status = 0;
+ pfl->ident[0] = id0;
+ pfl->ident[1] = id1;
+ pfl->ident[2] = id2;
+ pfl->ident[3] = id3;
+ /* Hardcoded CFI table */
+ pfl->cfi_len = 0x52;
+ /* Standard "QRY" string */
+ pfl->cfi_table[0x10] = 'Q';
+ pfl->cfi_table[0x11] = 'R';
+ pfl->cfi_table[0x12] = 'Y';
+ /* Command set (Intel) */
+ pfl->cfi_table[0x13] = 0x01;
+ pfl->cfi_table[0x14] = 0x00;
+ /* Primary extended table address (none) */
+ pfl->cfi_table[0x15] = 0x31;
+ pfl->cfi_table[0x16] = 0x00;
+ /* Alternate command set (none) */
+ pfl->cfi_table[0x17] = 0x00;
+ pfl->cfi_table[0x18] = 0x00;
+ /* Alternate extended table (none) */
+ pfl->cfi_table[0x19] = 0x00;
+ pfl->cfi_table[0x1A] = 0x00;
+ /* Vcc min */
+ pfl->cfi_table[0x1B] = 0x45;
+ /* Vcc max */
+ pfl->cfi_table[0x1C] = 0x55;
+ /* Vpp min (no Vpp pin) */
+ pfl->cfi_table[0x1D] = 0x00;
+ /* Vpp max (no Vpp pin) */
+ pfl->cfi_table[0x1E] = 0x00;
+ /* Reserved */
+ pfl->cfi_table[0x1F] = 0x07;
+ /* Timeout for min size buffer write */
+ pfl->cfi_table[0x20] = 0x07;
+ /* Typical timeout for block erase */
+ pfl->cfi_table[0x21] = 0x0a;
+ /* Typical timeout for full chip erase (4096 ms) */
+ pfl->cfi_table[0x22] = 0x00;
+ /* Reserved */
+ pfl->cfi_table[0x23] = 0x04;
+ /* Max timeout for buffer write */
+ pfl->cfi_table[0x24] = 0x04;
+ /* Max timeout for block erase */
+ pfl->cfi_table[0x25] = 0x04;
+ /* Max timeout for chip erase */
+ pfl->cfi_table[0x26] = 0x00;
+ /* Device size */
+ pfl->cfi_table[0x27] = ctz32(total_len); // + 1;
+ /* Flash device interface (8 & 16 bits) */
+ pfl->cfi_table[0x28] = 0x02;
+ pfl->cfi_table[0x29] = 0x00;
+ /* Max number of bytes in multi-bytes write */
+ if (width == 1) {
+ pfl->cfi_table[0x2A] = 0x08;
+ } else {
+ pfl->cfi_table[0x2A] = 0x0B;
+ }
+ pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
+
+ pfl->cfi_table[0x2B] = 0x00;
+ /* Number of erase block regions (uniform) */
+ pfl->cfi_table[0x2C] = 0x01;
+ /* Erase block region 1 */
+ pfl->cfi_table[0x2D] = nb_blocs - 1;
+ pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
+ pfl->cfi_table[0x2F] = sector_len >> 8;
+ pfl->cfi_table[0x30] = sector_len >> 16;
+
+ /* Extended */
+ pfl->cfi_table[0x31] = 'P';
+ pfl->cfi_table[0x32] = 'R';
+ pfl->cfi_table[0x33] = 'I';
+
+ pfl->cfi_table[0x34] = '1';
+ pfl->cfi_table[0x35] = '1';
+
+ pfl->cfi_table[0x36] = 0x00;
+ pfl->cfi_table[0x37] = 0x00;
+ pfl->cfi_table[0x38] = 0x00;
+ pfl->cfi_table[0x39] = 0x00;
+
+ pfl->cfi_table[0x3a] = 0x00;
+
+ pfl->cfi_table[0x3b] = 0x00;
+ pfl->cfi_table[0x3c] = 0x00;
+
+ return pfl;
+}
diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
new file mode 100644
index 0000000..3594a36
--- /dev/null
+++ b/hw/pflash_cfi02.c
@@ -0,0 +1,741 @@
+/*
+ * CFI parallel flash with AMD command set emulation
+ *
+ * Copyright (c) 2005 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+/*
+ * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
+ * Supported commands/modes are:
+ * - flash read
+ * - flash write
+ * - flash ID read
+ * - sector erase
+ * - chip erase
+ * - unlock bypass command
+ * - CFI queries
+ *
+ * It does not support flash interleaving.
+ * It does not implement boot blocs with reduced size
+ * It does not implement software data protection as found in many real chips
+ * It does not implement erase suspend/resume commands
+ * It does not implement multiple sectors erase
+ */
+
+#include "hw.h"
+#include "flash.h"
+#include "qemu-timer.h"
+#include "block.h"
+
+//#define PFLASH_DEBUG
+#ifdef PFLASH_DEBUG
+#define DPRINTF(fmt, ...) \
+do { \
+ printf("PFLASH: " fmt , ## __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+struct pflash_t {
+ BlockDriverState *bs;
+ target_phys_addr_t base;
+ uint32_t sector_len;
+ uint32_t chip_len;
+ int mappings;
+ int width;
+ int wcycle; /* if 0, the flash is read normally */
+ int bypass;
+ int ro;
+ uint8_t cmd;
+ uint8_t status;
+ uint16_t ident[4];
+ uint16_t unlock_addr[2];
+ uint8_t cfi_len;
+ uint8_t cfi_table[0x52];
+ QEMUTimer *timer;
+ ram_addr_t off;
+ int fl_mem;
+ int rom_mode;
+ void *storage;
+};
+
+static void pflash_register_memory(pflash_t *pfl, int rom_mode)
+{
+ unsigned long phys_offset = pfl->fl_mem;
+ int i;
+
+ if (rom_mode)
+ phys_offset |= pfl->off | IO_MEM_ROMD;
+ pfl->rom_mode = rom_mode;
+
+ for (i = 0; i < pfl->mappings; i++)
+ cpu_register_physical_memory(pfl->base + i * pfl->chip_len,
+ pfl->chip_len, phys_offset);
+}
+
+static void pflash_timer (void *opaque)
+{
+ pflash_t *pfl = opaque;
+
+ DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
+ /* Reset flash */
+ pfl->status ^= 0x80;
+ if (pfl->bypass) {
+ pfl->wcycle = 2;
+ } else {
+ pflash_register_memory(pfl, 1);
+ pfl->wcycle = 0;
+ }
+ pfl->cmd = 0;
+}
+
+static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
+ int width, int be)
+{
+ target_phys_addr_t boff;
+ uint32_t ret;
+ uint8_t *p;
+
+ DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset);
+ ret = -1;
+ if (pfl->rom_mode) {
+ /* Lazy reset of to ROMD mode */
+ if (pfl->wcycle == 0)
+ pflash_register_memory(pfl, 1);
+ }
+ offset &= pfl->chip_len - 1;
+ boff = offset & 0xFF;
+ if (pfl->width == 2)
+ boff = boff >> 1;
+ else if (pfl->width == 4)
+ boff = boff >> 2;
+ switch (pfl->cmd) {
+ default:
+ /* This should never happen : reset state & treat it as a read*/
+ DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ case 0x80:
+ /* We accept reads during second unlock sequence... */
+ case 0x00:
+ flash_read:
+ /* Flash area read */
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ ret = p[offset];
+// DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
+ break;
+ case 2:
+ if (be) {
+ ret = p[offset] << 8;
+ ret |= p[offset + 1];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ }
+// DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
+ break;
+ case 4:
+ if (be) {
+ ret = p[offset] << 24;
+ ret |= p[offset + 1] << 16;
+ ret |= p[offset + 2] << 8;
+ ret |= p[offset + 3];
+ } else {
+ ret = p[offset];
+ ret |= p[offset + 1] << 8;
+ ret |= p[offset + 2] << 16;
+ ret |= p[offset + 3] << 24;
+ }
+// DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
+ break;
+ }
+ break;
+ case 0x90:
+ /* flash ID read */
+ switch (boff) {
+ case 0x00:
+ case 0x01:
+ ret = pfl->ident[boff & 0x01];
+ break;
+ case 0x02:
+ ret = 0x00; /* Pretend all sectors are unprotected */
+ break;
+ case 0x0E:
+ case 0x0F:
+ if (pfl->ident[2 + (boff & 0x01)] == (uint8_t)-1)
+ goto flash_read;
+ ret = pfl->ident[2 + (boff & 0x01)];
+ break;
+ default:
+ goto flash_read;
+ }
+ DPRINTF("%s: ID " TARGET_FMT_pld " %x\n", __func__, boff, ret);
+ break;
+ case 0xA0:
+ case 0x10:
+ case 0x30:
+ /* Status register read */
+ ret = pfl->status;
+ DPRINTF("%s: status %x\n", __func__, ret);
+ /* Toggle bit 6 */
+ pfl->status ^= 0x40;
+ break;
+ case 0x98:
+ /* CFI query mode */
+ if (boff > pfl->cfi_len)
+ ret = 0;
+ else
+ ret = pfl->cfi_table[boff];
+ break;
+ }
+
+ return ret;
+}
+
+/* update flash content on disk */
+static void pflash_update(pflash_t *pfl, int offset,
+ int size)
+{
+ int offset_end;
+ if (pfl->bs) {
+ offset_end = offset + size;
+ /* round to sectors */
+ offset = offset >> 9;
+ offset_end = (offset_end + 511) >> 9;
+ bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
+ offset_end - offset);
+ }
+}
+
+static void pflash_write (pflash_t *pfl, target_phys_addr_t offset,
+ uint32_t value, int width, int be)
+{
+ target_phys_addr_t boff;
+ uint8_t *p;
+ uint8_t cmd;
+
+ cmd = value;
+ if (pfl->cmd != 0xA0 && cmd == 0xF0) {
+#if 0
+ DPRINTF("%s: flash reset asked (%02x %02x)\n",
+ __func__, pfl->cmd, cmd);
+#endif
+ goto reset_flash;
+ }
+ DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__,
+ offset, value, width, pfl->wcycle);
+ offset &= pfl->chip_len - 1;
+
+ DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__,
+ offset, value, width);
+ boff = offset & (pfl->sector_len - 1);
+ if (pfl->width == 2)
+ boff = boff >> 1;
+ else if (pfl->width == 4)
+ boff = boff >> 2;
+ switch (pfl->wcycle) {
+ case 0:
+ /* Set the device in I/O access mode if required */
+ if (pfl->rom_mode)
+ pflash_register_memory(pfl, 0);
+ /* We're in read mode */
+ check_unlock0:
+ if (boff == 0x55 && cmd == 0x98) {
+ enter_CFI_mode:
+ /* Enter CFI query mode */
+ pfl->wcycle = 7;
+ pfl->cmd = 0x98;
+ return;
+ }
+ if (boff != pfl->unlock_addr[0] || cmd != 0xAA) {
+ DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
+ __func__, boff, cmd, pfl->unlock_addr[0]);
+ goto reset_flash;
+ }
+ DPRINTF("%s: unlock sequence started\n", __func__);
+ break;
+ case 1:
+ /* We started an unlock sequence */
+ check_unlock1:
+ if (boff != pfl->unlock_addr[1] || cmd != 0x55) {
+ DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
+ boff, cmd);
+ goto reset_flash;
+ }
+ DPRINTF("%s: unlock sequence done\n", __func__);
+ break;
+ case 2:
+ /* We finished an unlock sequence */
+ if (!pfl->bypass && boff != pfl->unlock_addr[0]) {
+ DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
+ boff, cmd);
+ goto reset_flash;
+ }
+ switch (cmd) {
+ case 0x20:
+ pfl->bypass = 1;
+ goto do_bypass;
+ case 0x80:
+ case 0x90:
+ case 0xA0:
+ pfl->cmd = cmd;
+ DPRINTF("%s: starting command %02x\n", __func__, cmd);
+ break;
+ default:
+ DPRINTF("%s: unknown command %02x\n", __func__, cmd);
+ goto reset_flash;
+ }
+ break;
+ case 3:
+ switch (pfl->cmd) {
+ case 0x80:
+ /* We need another unlock sequence */
+ goto check_unlock0;
+ case 0xA0:
+ DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n",
+ __func__, offset, value, width);
+ p = pfl->storage;
+ switch (width) {
+ case 1:
+ p[offset] &= value;
+ pflash_update(pfl, offset, 1);
+ break;
+ case 2:
+ if (be) {
+ p[offset] &= value >> 8;
+ p[offset + 1] &= value;
+ } else {
+ p[offset] &= value;
+ p[offset + 1] &= value >> 8;
+ }
+ pflash_update(pfl, offset, 2);
+ break;
+ case 4:
+ if (be) {
+ p[offset] &= value >> 24;
+ p[offset + 1] &= value >> 16;
+ p[offset + 2] &= value >> 8;
+ p[offset + 3] &= value;
+ } else {
+ p[offset] &= value;
+ p[offset + 1] &= value >> 8;
+ p[offset + 2] &= value >> 16;
+ p[offset + 3] &= value >> 24;
+ }
+ pflash_update(pfl, offset, 4);
+ break;
+ }
+ pfl->status = 0x00 | ~(value & 0x80);
+ /* Let's pretend write is immediate */
+ if (pfl->bypass)
+ goto do_bypass;
+ goto reset_flash;
+ case 0x90:
+ if (pfl->bypass && cmd == 0x00) {
+ /* Unlock bypass reset */
+ goto reset_flash;
+ }
+ /* We can enter CFI query mode from autoselect mode */
+ if (boff == 0x55 && cmd == 0x98)
+ goto enter_CFI_mode;
+ /* No break here */
+ default:
+ DPRINTF("%s: invalid write for command %02x\n",
+ __func__, pfl->cmd);
+ goto reset_flash;
+ }
+ case 4:
+ switch (pfl->cmd) {
+ case 0xA0:
+ /* Ignore writes while flash data write is occuring */
+ /* As we suppose write is immediate, this should never happen */
+ return;
+ case 0x80:
+ goto check_unlock1;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid command state %02x (wc 4)\n",
+ __func__, pfl->cmd);
+ goto reset_flash;
+ }
+ break;
+ case 5:
+ switch (cmd) {
+ case 0x10:
+ if (boff != pfl->unlock_addr[0]) {
+ DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
+ __func__, offset);
+ goto reset_flash;
+ }
+ /* Chip erase */
+ DPRINTF("%s: start chip erase\n", __func__);
+ memset(pfl->storage, 0xFF, pfl->chip_len);
+ pfl->status = 0x00;
+ pflash_update(pfl, 0, pfl->chip_len);
+ /* Let's wait 5 seconds before chip erase is done */
+ qemu_mod_timer(pfl->timer,
+ qemu_get_clock(vm_clock) + (get_ticks_per_sec() * 5));
+ break;
+ case 0x30:
+ /* Sector erase */
+ p = pfl->storage;
+ offset &= ~(pfl->sector_len - 1);
+ DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
+ offset);
+ memset(p + offset, 0xFF, pfl->sector_len);
+ pflash_update(pfl, offset, pfl->sector_len);
+ pfl->status = 0x00;
+ /* Let's wait 1/2 second before sector erase is done */
+ qemu_mod_timer(pfl->timer,
+ qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 2));
+ break;
+ default:
+ DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
+ goto reset_flash;
+ }
+ pfl->cmd = cmd;
+ break;
+ case 6:
+ switch (pfl->cmd) {
+ case 0x10:
+ /* Ignore writes during chip erase */
+ return;
+ case 0x30:
+ /* Ignore writes during sector erase */
+ return;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid command state %02x (wc 6)\n",
+ __func__, pfl->cmd);
+ goto reset_flash;
+ }
+ break;
+ case 7: /* Special value for CFI queries */
+ DPRINTF("%s: invalid write in CFI query mode\n", __func__);
+ goto reset_flash;
+ default:
+ /* Should never happen */
+ DPRINTF("%s: invalid write state (wc 7)\n", __func__);
+ goto reset_flash;
+ }
+ pfl->wcycle++;
+
+ return;
+
+ /* Reset flash */
+ reset_flash:
+ pfl->bypass = 0;
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ return;
+
+ do_bypass:
+ pfl->wcycle = 2;
+ pfl->cmd = 0;
+ return;
+}
+
+
+static uint32_t pflash_readb_be(void *opaque, target_phys_addr_t addr)
+{
+ return pflash_read(opaque, addr, 1, 1);
+}
+
+static uint32_t pflash_readb_le(void *opaque, target_phys_addr_t addr)
+{
+ return pflash_read(opaque, addr, 1, 0);
+}
+
+static uint32_t pflash_readw_be(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 2, 1);
+}
+
+static uint32_t pflash_readw_le(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 2, 0);
+}
+
+static uint32_t pflash_readl_be(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 4, 1);
+}
+
+static uint32_t pflash_readl_le(void *opaque, target_phys_addr_t addr)
+{
+ pflash_t *pfl = opaque;
+
+ return pflash_read(pfl, addr, 4, 0);
+}
+
+static void pflash_writeb_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_write(opaque, addr, value, 1, 1);
+}
+
+static void pflash_writeb_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_write(opaque, addr, value, 1, 0);
+}
+
+static void pflash_writew_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 2, 1);
+}
+
+static void pflash_writew_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 2, 0);
+}
+
+static void pflash_writel_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 4, 1);
+}
+
+static void pflash_writel_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ pflash_t *pfl = opaque;
+
+ pflash_write(pfl, addr, value, 4, 0);
+}
+
+static CPUWriteMemoryFunc * const pflash_write_ops_be[] = {
+ &pflash_writeb_be,
+ &pflash_writew_be,
+ &pflash_writel_be,
+};
+
+static CPUReadMemoryFunc * const pflash_read_ops_be[] = {
+ &pflash_readb_be,
+ &pflash_readw_be,
+ &pflash_readl_be,
+};
+
+static CPUWriteMemoryFunc * const pflash_write_ops_le[] = {
+ &pflash_writeb_le,
+ &pflash_writew_le,
+ &pflash_writel_le,
+};
+
+static CPUReadMemoryFunc * const pflash_read_ops_le[] = {
+ &pflash_readb_le,
+ &pflash_readw_le,
+ &pflash_readl_le,
+};
+
+/* Count trailing zeroes of a 32 bits quantity */
+static int ctz32 (uint32_t n)
+{
+ int ret;
+
+ ret = 0;
+ if (!(n & 0xFFFF)) {
+ ret += 16;
+ n = n >> 16;
+ }
+ if (!(n & 0xFF)) {
+ ret += 8;
+ n = n >> 8;
+ }
+ if (!(n & 0xF)) {
+ ret += 4;
+ n = n >> 4;
+ }
+ if (!(n & 0x3)) {
+ ret += 2;
+ n = n >> 2;
+ }
+ if (!(n & 0x1)) {
+ ret++;
+#if 0 /* This is not necessary as n is never 0 */
+ n = n >> 1;
+#endif
+ }
+#if 0 /* This is not necessary as n is never 0 */
+ if (!n)
+ ret++;
+#endif
+
+ return ret;
+}
+
+pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off,
+ BlockDriverState *bs, uint32_t sector_len,
+ int nb_blocs, int nb_mappings, int width,
+ uint16_t id0, uint16_t id1,
+ uint16_t id2, uint16_t id3,
+ uint16_t unlock_addr0, uint16_t unlock_addr1,
+ int be)
+{
+ pflash_t *pfl;
+ int32_t chip_len;
+ int ret;
+
+ chip_len = sector_len * nb_blocs;
+ /* XXX: to be fixed */
+#if 0
+ if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
+ total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
+ return NULL;
+#endif
+ pfl = qemu_mallocz(sizeof(pflash_t));
+ /* FIXME: Allocate ram ourselves. */
+ pfl->storage = qemu_get_ram_ptr(off);
+ if (be) {
+ pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_be,
+ pflash_write_ops_be,
+ pfl, DEVICE_NATIVE_ENDIAN);
+ } else {
+ pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_le,
+ pflash_write_ops_le,
+ pfl, DEVICE_NATIVE_ENDIAN);
+ }
+ pfl->off = off;
+ pfl->base = base;
+ pfl->chip_len = chip_len;
+ pfl->mappings = nb_mappings;
+ pflash_register_memory(pfl, 1);
+ pfl->bs = bs;
+ if (pfl->bs) {
+ /* read the initial flash content */
+ ret = bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
+ if (ret < 0) {
+ cpu_unregister_io_memory(pfl->fl_mem);
+ qemu_free(pfl);
+ return NULL;
+ }
+ }
+#if 0 /* XXX: there should be a bit to set up read-only,
+ * the same way the hardware does (with WP pin).
+ */
+ pfl->ro = 1;
+#else
+ pfl->ro = 0;
+#endif
+ pfl->timer = qemu_new_timer(vm_clock, pflash_timer, pfl);
+ pfl->sector_len = sector_len;
+ pfl->width = width;
+ pfl->wcycle = 0;
+ pfl->cmd = 0;
+ pfl->status = 0;
+ pfl->ident[0] = id0;
+ pfl->ident[1] = id1;
+ pfl->ident[2] = id2;
+ pfl->ident[3] = id3;
+ pfl->unlock_addr[0] = unlock_addr0;
+ pfl->unlock_addr[1] = unlock_addr1;
+ /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
+ pfl->cfi_len = 0x52;
+ /* Standard "QRY" string */
+ pfl->cfi_table[0x10] = 'Q';
+ pfl->cfi_table[0x11] = 'R';
+ pfl->cfi_table[0x12] = 'Y';
+ /* Command set (AMD/Fujitsu) */
+ pfl->cfi_table[0x13] = 0x02;
+ pfl->cfi_table[0x14] = 0x00;
+ /* Primary extended table address */
+ pfl->cfi_table[0x15] = 0x31;
+ pfl->cfi_table[0x16] = 0x00;
+ /* Alternate command set (none) */
+ pfl->cfi_table[0x17] = 0x00;
+ pfl->cfi_table[0x18] = 0x00;
+ /* Alternate extended table (none) */
+ pfl->cfi_table[0x19] = 0x00;
+ pfl->cfi_table[0x1A] = 0x00;
+ /* Vcc min */
+ pfl->cfi_table[0x1B] = 0x27;
+ /* Vcc max */
+ pfl->cfi_table[0x1C] = 0x36;
+ /* Vpp min (no Vpp pin) */
+ pfl->cfi_table[0x1D] = 0x00;
+ /* Vpp max (no Vpp pin) */
+ pfl->cfi_table[0x1E] = 0x00;
+ /* Reserved */
+ pfl->cfi_table[0x1F] = 0x07;
+ /* Timeout for min size buffer write (NA) */
+ pfl->cfi_table[0x20] = 0x00;
+ /* Typical timeout for block erase (512 ms) */
+ pfl->cfi_table[0x21] = 0x09;
+ /* Typical timeout for full chip erase (4096 ms) */
+ pfl->cfi_table[0x22] = 0x0C;
+ /* Reserved */
+ pfl->cfi_table[0x23] = 0x01;
+ /* Max timeout for buffer write (NA) */
+ pfl->cfi_table[0x24] = 0x00;
+ /* Max timeout for block erase */
+ pfl->cfi_table[0x25] = 0x0A;
+ /* Max timeout for chip erase */
+ pfl->cfi_table[0x26] = 0x0D;
+ /* Device size */
+ pfl->cfi_table[0x27] = ctz32(chip_len);
+ /* Flash device interface (8 & 16 bits) */
+ pfl->cfi_table[0x28] = 0x02;
+ pfl->cfi_table[0x29] = 0x00;
+ /* Max number of bytes in multi-bytes write */
+ /* XXX: disable buffered write as it's not supported */
+ // pfl->cfi_table[0x2A] = 0x05;
+ pfl->cfi_table[0x2A] = 0x00;
+ pfl->cfi_table[0x2B] = 0x00;
+ /* Number of erase block regions (uniform) */
+ pfl->cfi_table[0x2C] = 0x01;
+ /* Erase block region 1 */
+ pfl->cfi_table[0x2D] = nb_blocs - 1;
+ pfl->cfi_table[0x2E] = (nb_blocs - 1) >> 8;
+ pfl->cfi_table[0x2F] = sector_len >> 8;
+ pfl->cfi_table[0x30] = sector_len >> 16;
+
+ /* Extended */
+ pfl->cfi_table[0x31] = 'P';
+ pfl->cfi_table[0x32] = 'R';
+ pfl->cfi_table[0x33] = 'I';
+
+ pfl->cfi_table[0x34] = '1';
+ pfl->cfi_table[0x35] = '0';
+
+ pfl->cfi_table[0x36] = 0x00;
+ pfl->cfi_table[0x37] = 0x00;
+ pfl->cfi_table[0x38] = 0x00;
+ pfl->cfi_table[0x39] = 0x00;
+
+ pfl->cfi_table[0x3a] = 0x00;
+
+ pfl->cfi_table[0x3b] = 0x00;
+ pfl->cfi_table[0x3c] = 0x00;
+
+ return pfl;
+}
diff --git a/hw/piix4.c b/hw/piix4.c
new file mode 100644
index 0000000..72073cd
--- /dev/null
+++ b/hw/piix4.c
@@ -0,0 +1,127 @@
+/*
+ * QEMU PIIX4 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "isa.h"
+#include "sysbus.h"
+
+PCIDevice *piix4_dev;
+
+static void piix4_reset(void *opaque)
+{
+ PCIDevice *d = opaque;
+ uint8_t *pci_conf = d->config;
+
+ pci_conf[0x04] = 0x07; // master, memory and I/O
+ pci_conf[0x05] = 0x00;
+ pci_conf[0x06] = 0x00;
+ pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
+ pci_conf[0x4c] = 0x4d;
+ pci_conf[0x4e] = 0x03;
+ pci_conf[0x4f] = 0x00;
+ pci_conf[0x60] = 0x0a; // PCI A -> IRQ 10
+ pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10
+ pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11
+ pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11
+ pci_conf[0x69] = 0x02;
+ pci_conf[0x70] = 0x80;
+ pci_conf[0x76] = 0x0c;
+ pci_conf[0x77] = 0x0c;
+ pci_conf[0x78] = 0x02;
+ pci_conf[0x79] = 0x00;
+ pci_conf[0x80] = 0x00;
+ pci_conf[0x82] = 0x00;
+ pci_conf[0xa0] = 0x08;
+ pci_conf[0xa2] = 0x00;
+ pci_conf[0xa3] = 0x00;
+ pci_conf[0xa4] = 0x00;
+ pci_conf[0xa5] = 0x00;
+ pci_conf[0xa6] = 0x00;
+ pci_conf[0xa7] = 0x00;
+ pci_conf[0xa8] = 0x0f;
+ pci_conf[0xaa] = 0x00;
+ pci_conf[0xab] = 0x00;
+ pci_conf[0xac] = 0x00;
+ pci_conf[0xae] = 0x00;
+}
+
+static void piix_save(QEMUFile* f, void *opaque)
+{
+ PCIDevice *d = opaque;
+ pci_device_save(d, f);
+}
+
+static int piix_load(QEMUFile* f, void *opaque, int version_id)
+{
+ PCIDevice *d = opaque;
+ if (version_id != 2)
+ return -EINVAL;
+ return pci_device_load(d, f);
+}
+
+static int piix4_initfn(PCIDevice *d)
+{
+ uint8_t *pci_conf;
+
+ isa_bus_new(&d->qdev);
+ register_savevm(&d->qdev, "PIIX4", 0, 2, piix_save, piix_load, d);
+
+ pci_conf = d->config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_0); // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge
+ pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
+
+ piix4_dev = d;
+ qemu_register_reset(piix4_reset, d);
+ return 0;
+}
+
+int piix4_init(PCIBus *bus, int devfn)
+{
+ PCIDevice *d;
+
+ d = pci_create_simple_multifunction(bus, devfn, true, "PIIX4");
+ return d->devfn;
+}
+
+static PCIDeviceInfo piix4_info[] = {
+ {
+ .qdev.name = "PIIX4",
+ .qdev.desc = "ISA bridge",
+ .qdev.size = sizeof(PCIDevice),
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
+ .init = piix4_initfn,
+ },{
+ /* end of list */
+ }
+};
+
+static void piix4_register(void)
+{
+ pci_qdev_register_many(piix4_info);
+}
+device_init(piix4_register);
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
new file mode 100644
index 0000000..24bfe8e
--- /dev/null
+++ b/hw/piix_pci.c
@@ -0,0 +1,392 @@
+/*
+ * QEMU i440FX/PIIX3 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "range.h"
+#include "kvm.h"
+
+/*
+ * I440FX chipset data sheet.
+ * http://download.intel.com/design/chipsets/datashts/29054901.pdf
+ */
+
+typedef PCIHostState I440FXState;
+
+typedef struct PIIX3State {
+ PCIDevice dev;
+ int pci_irq_levels[4];
+ qemu_irq *pic;
+} PIIX3State;
+
+struct PCII440FXState {
+ PCIDevice dev;
+ target_phys_addr_t isa_page_descs[384 / 4];
+ uint8_t smm_enabled;
+ PIIX3State *piix3;
+};
+
+
+#define I440FX_PAM 0x59
+#define I440FX_PAM_SIZE 7
+#define I440FX_SMRAM 0x72
+
+static void piix3_set_irq(void *opaque, int irq_num, int level);
+
+/* return the global irq number corresponding to a given device irq
+ pin. We could also use the bus number to have a more precise
+ mapping. */
+static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot_addend;
+ slot_addend = (pci_dev->devfn >> 3) - 1;
+ return (irq_num + slot_addend) & 3;
+}
+
+static void update_pam(PCII440FXState *d, uint32_t start, uint32_t end, int r)
+{
+ uint32_t addr;
+
+ // printf("ISA mapping %08x-0x%08x: %d\n", start, end, r);
+ switch(r) {
+ case 3:
+ /* RAM */
+ cpu_register_physical_memory(start, end - start,
+ start);
+ break;
+ case 1:
+ /* ROM (XXX: not quite correct) */
+ cpu_register_physical_memory(start, end - start,
+ start | IO_MEM_ROM);
+ break;
+ case 2:
+ case 0:
+ /* XXX: should distinguish read/write cases */
+ for(addr = start; addr < end; addr += 4096) {
+ cpu_register_physical_memory(addr, 4096,
+ d->isa_page_descs[(addr - 0xa0000) >> 12]);
+ }
+ break;
+ }
+}
+
+static void i440fx_update_memory_mappings(PCII440FXState *d)
+{
+ int i, r;
+ uint32_t smram, addr;
+
+ update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3);
+ for(i = 0; i < 12; i++) {
+ r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3;
+ update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r);
+ }
+ smram = d->dev.config[I440FX_SMRAM];
+ if ((d->smm_enabled && (smram & 0x08)) || (smram & 0x40)) {
+ cpu_register_physical_memory(0xa0000, 0x20000, 0xa0000);
+ } else {
+ for(addr = 0xa0000; addr < 0xc0000; addr += 4096) {
+ cpu_register_physical_memory(addr, 4096,
+ d->isa_page_descs[(addr - 0xa0000) >> 12]);
+ }
+ }
+}
+
+static void i440fx_set_smm(int val, void *arg)
+{
+ PCII440FXState *d = arg;
+
+ val = (val != 0);
+ if (d->smm_enabled != val) {
+ d->smm_enabled = val;
+ i440fx_update_memory_mappings(d);
+ }
+}
+
+
+/* XXX: suppress when better memory API. We make the assumption that
+ no device (in particular the VGA) changes the memory mappings in
+ the 0xa0000-0x100000 range */
+void i440fx_init_memory_mappings(PCII440FXState *d)
+{
+ int i;
+ for(i = 0; i < 96; i++) {
+ d->isa_page_descs[i] = cpu_get_physical_page_desc(0xa0000 + i * 0x1000);
+ }
+}
+
+static void i440fx_write_config(PCIDevice *dev,
+ uint32_t address, uint32_t val, int len)
+{
+ PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev);
+
+ /* XXX: implement SMRAM.D_LOCK */
+ pci_default_write_config(dev, address, val, len);
+ if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) ||
+ range_covers_byte(address, len, I440FX_SMRAM)) {
+ i440fx_update_memory_mappings(d);
+ }
+}
+
+static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id)
+{
+ PCII440FXState *d = opaque;
+ int ret, i;
+
+ ret = pci_device_load(&d->dev, f);
+ if (ret < 0)
+ return ret;
+ i440fx_update_memory_mappings(d);
+ qemu_get_8s(f, &d->smm_enabled);
+
+ if (version_id == 2)
+ for (i = 0; i < 4; i++)
+ d->piix3->pci_irq_levels[i] = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int i440fx_post_load(void *opaque, int version_id)
+{
+ PCII440FXState *d = opaque;
+
+ i440fx_update_memory_mappings(d);
+ return 0;
+}
+
+static const VMStateDescription vmstate_i440fx = {
+ .name = "I440FX",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 1,
+ .load_state_old = i440fx_load_old,
+ .post_load = i440fx_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCII440FXState),
+ VMSTATE_UINT8(smm_enabled, PCII440FXState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int i440fx_pcihost_initfn(SysBusDevice *dev)
+{
+ I440FXState *s = FROM_SYSBUS(I440FXState, dev);
+
+ pci_host_conf_register_ioport(0xcf8, s);
+
+ pci_host_data_register_ioport(0xcfc, s);
+ return 0;
+}
+
+static int i440fx_initfn(PCIDevice *dev)
+{
+ PCII440FXState *d = DO_UPCAST(PCII440FXState, dev, dev);
+
+ pci_config_set_vendor_id(d->dev.config, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82441);
+ d->dev.config[0x08] = 0x02; // revision
+ pci_config_set_class(d->dev.config, PCI_CLASS_BRIDGE_HOST);
+
+ d->dev.config[I440FX_SMRAM] = 0x02;
+
+ cpu_smm_register(&i440fx_set_smm, d);
+ return 0;
+}
+
+static PIIX3State *piix3_dev;
+
+PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, ram_addr_t ram_size)
+{
+ DeviceState *dev;
+ PCIBus *b;
+ PCIDevice *d;
+ I440FXState *s;
+ PIIX3State *piix3;
+
+ dev = qdev_create(NULL, "i440FX-pcihost");
+ s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev));
+ b = pci_bus_new(&s->busdev.qdev, NULL, 0);
+ s->bus = b;
+ qdev_init_nofail(dev);
+
+ d = pci_create_simple(b, 0, "i440FX");
+ *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d);
+
+ piix3 = DO_UPCAST(PIIX3State, dev,
+ pci_create_simple_multifunction(b, -1, true, "PIIX3"));
+ piix3->pic = pic;
+ pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, 4);
+ (*pi440fx_state)->piix3 = piix3;
+
+ *piix3_devfn = piix3->dev.devfn;
+
+ ram_size = ram_size / 8 / 1024 / 1024;
+ if (ram_size > 255)
+ ram_size = 255;
+ (*pi440fx_state)->dev.config[0x57]=ram_size;
+
+ piix3_dev = piix3;
+
+ return b;
+}
+
+/* PIIX3 PCI to ISA bridge */
+
+static void piix3_set_irq(void *opaque, int irq_num, int level)
+{
+ int i, pic_irq, pic_level;
+ PIIX3State *piix3 = opaque;
+
+ piix3->pci_irq_levels[irq_num] = level;
+
+ /* now we change the pic irq level according to the piix irq mappings */
+ /* XXX: optimize */
+ pic_irq = piix3->dev.config[0x60 + irq_num];
+ if (pic_irq < 16) {
+ /* The pic level is the logical OR of all the PCI irqs mapped
+ to it */
+ pic_level = 0;
+ for (i = 0; i < 4; i++) {
+ if (pic_irq == piix3->dev.config[0x60 + i])
+ pic_level |= piix3->pci_irq_levels[i];
+ }
+ qemu_set_irq(piix3->pic[pic_irq], pic_level);
+ }
+}
+
+int piix_get_irq(int pin)
+{
+ if (piix3_dev)
+ return piix3_dev->dev.config[0x60+pin];
+ return 0;
+}
+
+static void piix3_reset(void *opaque)
+{
+ PIIX3State *d = opaque;
+ uint8_t *pci_conf = d->dev.config;
+
+ pci_conf[0x04] = 0x07; // master, memory and I/O
+ pci_conf[0x05] = 0x00;
+ pci_conf[0x06] = 0x00;
+ pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
+ pci_conf[0x4c] = 0x4d;
+ pci_conf[0x4e] = 0x03;
+ pci_conf[0x4f] = 0x00;
+ pci_conf[0x60] = 0x80;
+ pci_conf[0x61] = 0x80;
+ pci_conf[0x62] = 0x80;
+ pci_conf[0x63] = 0x80;
+ pci_conf[0x69] = 0x02;
+ pci_conf[0x70] = 0x80;
+ pci_conf[0x76] = 0x0c;
+ pci_conf[0x77] = 0x0c;
+ pci_conf[0x78] = 0x02;
+ pci_conf[0x79] = 0x00;
+ pci_conf[0x80] = 0x00;
+ pci_conf[0x82] = 0x00;
+ pci_conf[0xa0] = 0x08;
+ pci_conf[0xa2] = 0x00;
+ pci_conf[0xa3] = 0x00;
+ pci_conf[0xa4] = 0x00;
+ pci_conf[0xa5] = 0x00;
+ pci_conf[0xa6] = 0x00;
+ pci_conf[0xa7] = 0x00;
+ pci_conf[0xa8] = 0x0f;
+ pci_conf[0xaa] = 0x00;
+ pci_conf[0xab] = 0x00;
+ pci_conf[0xac] = 0x00;
+ pci_conf[0xae] = 0x00;
+
+ memset(d->pci_irq_levels, 0, sizeof(d->pci_irq_levels));
+}
+
+static const VMStateDescription vmstate_piix3 = {
+ .name = "PIIX3",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PIIX3State),
+ VMSTATE_INT32_ARRAY_V(pci_irq_levels, PIIX3State, 4, 3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int piix3_initfn(PCIDevice *dev)
+{
+ PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev);
+ uint8_t *pci_conf;
+
+ isa_bus_new(&d->dev.qdev);
+
+ pci_conf = d->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
+ pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
+
+ qemu_register_reset(piix3_reset, d);
+ return 0;
+}
+
+static PCIDeviceInfo i440fx_info[] = {
+ {
+ .qdev.name = "i440FX",
+ .qdev.desc = "Host bridge",
+ .qdev.size = sizeof(PCII440FXState),
+ .qdev.vmsd = &vmstate_i440fx,
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
+ .init = i440fx_initfn,
+ .config_write = i440fx_write_config,
+ },{
+ .qdev.name = "PIIX3",
+ .qdev.desc = "ISA bridge",
+ .qdev.size = sizeof(PIIX3State),
+ .qdev.vmsd = &vmstate_piix3,
+ .qdev.no_user = 1,
+ .no_hotplug = 1,
+ .init = piix3_initfn,
+ },{
+ /* end of list */
+ }
+};
+
+static SysBusDeviceInfo i440fx_pcihost_info = {
+ .init = i440fx_pcihost_initfn,
+ .qdev.name = "i440FX-pcihost",
+ .qdev.fw_name = "pci",
+ .qdev.size = sizeof(I440FXState),
+ .qdev.no_user = 1,
+};
+
+static void i440fx_register(void)
+{
+ sysbus_register_withprop(&i440fx_pcihost_info);
+ pci_qdev_register_many(i440fx_info);
+}
+device_init(i440fx_register);
diff --git a/hw/pixel_ops.h b/hw/pixel_ops.h
new file mode 100644
index 0000000..d390adf
--- /dev/null
+++ b/hw/pixel_ops.h
@@ -0,0 +1,53 @@
+static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
+}
+
+static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+}
+
+static inline unsigned int rgb_to_pixel15bgr(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return ((b >> 3) << 10) | ((g >> 3) << 5) | (r >> 3);
+}
+
+static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+}
+
+static inline unsigned int rgb_to_pixel16bgr(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return ((b >> 3) << 11) | ((g >> 2) << 5) | (r >> 3);
+}
+
+static inline unsigned int rgb_to_pixel24(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return (r << 16) | (g << 8) | b;
+}
+
+static inline unsigned int rgb_to_pixel24bgr(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return (b << 16) | (g << 8) | r;
+}
+
+static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return (r << 16) | (g << 8) | b;
+}
+
+static inline unsigned int rgb_to_pixel32bgr(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ return (b << 16) | (g << 8) | r;
+}
diff --git a/hw/pl011.c b/hw/pl011.c
new file mode 100644
index 0000000..77f0dbf
--- /dev/null
+++ b/hw/pl011.c
@@ -0,0 +1,332 @@
+/*
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t readbuff;
+ uint32_t flags;
+ uint32_t lcr;
+ uint32_t cr;
+ uint32_t dmacr;
+ uint32_t int_enabled;
+ uint32_t int_level;
+ uint32_t read_fifo[16];
+ uint32_t ilpr;
+ uint32_t ibrd;
+ uint32_t fbrd;
+ uint32_t ifl;
+ int read_pos;
+ int read_count;
+ int read_trigger;
+ CharDriverState *chr;
+ qemu_irq irq;
+ const unsigned char *id;
+} pl011_state;
+
+#define PL011_INT_TX 0x20
+#define PL011_INT_RX 0x10
+
+#define PL011_FLAG_TXFE 0x80
+#define PL011_FLAG_RXFF 0x40
+#define PL011_FLAG_TXFF 0x20
+#define PL011_FLAG_RXFE 0x10
+
+static const unsigned char pl011_id_arm[8] =
+ { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+static const unsigned char pl011_id_luminary[8] =
+ { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl011_update(pl011_state *s)
+{
+ uint32_t flags;
+
+ flags = s->int_level & s->int_enabled;
+ qemu_set_irq(s->irq, flags != 0);
+}
+
+static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ uint32_t c;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return s->id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ s->flags &= ~PL011_FLAG_RXFF;
+ c = s->read_fifo[s->read_pos];
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == 16)
+ s->read_pos = 0;
+ }
+ if (s->read_count == 0) {
+ s->flags |= PL011_FLAG_RXFE;
+ }
+ if (s->read_count == s->read_trigger - 1)
+ s->int_level &= ~ PL011_INT_RX;
+ pl011_update(s);
+ qemu_chr_accept_input(s->chr);
+ return c;
+ case 1: /* UARTCR */
+ return 0;
+ case 6: /* UARTFR */
+ return s->flags;
+ case 8: /* UARTILPR */
+ return s->ilpr;
+ case 9: /* UARTIBRD */
+ return s->ibrd;
+ case 10: /* UARTFBRD */
+ return s->fbrd;
+ case 11: /* UARTLCR_H */
+ return s->lcr;
+ case 12: /* UARTCR */
+ return s->cr;
+ case 13: /* UARTIFLS */
+ return s->ifl;
+ case 14: /* UARTIMSC */
+ return s->int_enabled;
+ case 15: /* UARTRIS */
+ return s->int_level;
+ case 16: /* UARTMIS */
+ return s->int_level & s->int_enabled;
+ case 18: /* UARTDMACR */
+ return s->dmacr;
+ default:
+ hw_error("pl011_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl011_set_read_trigger(pl011_state *s)
+{
+#if 0
+ /* The docs say the RX interrupt is triggered when the FIFO exceeds
+ the threshold. However linux only reads the FIFO in response to an
+ interrupt. Triggering the interrupt when the FIFO is non-empty seems
+ to make things work. */
+ if (s->lcr & 0x10)
+ s->read_trigger = (s->ifl >> 1) & 0x1c;
+ else
+#endif
+ s->read_trigger = 1;
+}
+
+static void pl011_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ unsigned char ch;
+
+ switch (offset >> 2) {
+ case 0: /* UARTDR */
+ /* ??? Check if transmitter is enabled. */
+ ch = value;
+ if (s->chr)
+ qemu_chr_write(s->chr, &ch, 1);
+ s->int_level |= PL011_INT_TX;
+ pl011_update(s);
+ break;
+ case 1: /* UARTCR */
+ s->cr = value;
+ break;
+ case 6: /* UARTFR */
+ /* Writes to Flag register are ignored. */
+ break;
+ case 8: /* UARTUARTILPR */
+ s->ilpr = value;
+ break;
+ case 9: /* UARTIBRD */
+ s->ibrd = value;
+ break;
+ case 10: /* UARTFBRD */
+ s->fbrd = value;
+ break;
+ case 11: /* UARTLCR_H */
+ s->lcr = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 12: /* UARTCR */
+ /* ??? Need to implement the enable and loopback bits. */
+ s->cr = value;
+ break;
+ case 13: /* UARTIFS */
+ s->ifl = value;
+ pl011_set_read_trigger(s);
+ break;
+ case 14: /* UARTIMSC */
+ s->int_enabled = value;
+ pl011_update(s);
+ break;
+ case 17: /* UARTICR */
+ s->int_level &= ~value;
+ pl011_update(s);
+ break;
+ case 18: /* UARTDMACR */
+ s->dmacr = value;
+ if (value & 3)
+ hw_error("PL011: DMA not implemented\n");
+ break;
+ default:
+ hw_error("pl011_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static int pl011_can_receive(void *opaque)
+{
+ pl011_state *s = (pl011_state *)opaque;
+
+ if (s->lcr & 0x10)
+ return s->read_count < 16;
+ else
+ return s->read_count < 1;
+}
+
+static void pl011_put_fifo(void *opaque, uint32_t value)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ int slot;
+
+ slot = s->read_pos + s->read_count;
+ if (slot >= 16)
+ slot -= 16;
+ s->read_fifo[slot] = value;
+ s->read_count++;
+ s->flags &= ~PL011_FLAG_RXFE;
+ if (s->cr & 0x10 || s->read_count == 16) {
+ s->flags |= PL011_FLAG_RXFF;
+ }
+ if (s->read_count == s->read_trigger) {
+ s->int_level |= PL011_INT_RX;
+ pl011_update(s);
+ }
+}
+
+static void pl011_receive(void *opaque, const uint8_t *buf, int size)
+{
+ pl011_put_fifo(opaque, *buf);
+}
+
+static void pl011_event(void *opaque, int event)
+{
+ if (event == CHR_EVENT_BREAK)
+ pl011_put_fifo(opaque, 0x400);
+}
+
+static CPUReadMemoryFunc * const pl011_readfn[] = {
+ pl011_read,
+ pl011_read,
+ pl011_read
+};
+
+static CPUWriteMemoryFunc * const pl011_writefn[] = {
+ pl011_write,
+ pl011_write,
+ pl011_write
+};
+
+static void pl011_save(QEMUFile *f, void *opaque)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->readbuff);
+ qemu_put_be32(f, s->flags);
+ qemu_put_be32(f, s->lcr);
+ qemu_put_be32(f, s->cr);
+ qemu_put_be32(f, s->dmacr);
+ qemu_put_be32(f, s->int_enabled);
+ qemu_put_be32(f, s->int_level);
+ for (i = 0; i < 16; i++)
+ qemu_put_be32(f, s->read_fifo[i]);
+ qemu_put_be32(f, s->ilpr);
+ qemu_put_be32(f, s->ibrd);
+ qemu_put_be32(f, s->fbrd);
+ qemu_put_be32(f, s->ifl);
+ qemu_put_be32(f, s->read_pos);
+ qemu_put_be32(f, s->read_count);
+ qemu_put_be32(f, s->read_trigger);
+}
+
+static int pl011_load(QEMUFile *f, void *opaque, int version_id)
+{
+ pl011_state *s = (pl011_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->readbuff = qemu_get_be32(f);
+ s->flags = qemu_get_be32(f);
+ s->lcr = qemu_get_be32(f);
+ s->cr = qemu_get_be32(f);
+ s->dmacr = qemu_get_be32(f);
+ s->int_enabled = qemu_get_be32(f);
+ s->int_level = qemu_get_be32(f);
+ for (i = 0; i < 16; i++)
+ s->read_fifo[i] = qemu_get_be32(f);
+ s->ilpr = qemu_get_be32(f);
+ s->ibrd = qemu_get_be32(f);
+ s->fbrd = qemu_get_be32(f);
+ s->ifl = qemu_get_be32(f);
+ s->read_pos = qemu_get_be32(f);
+ s->read_count = qemu_get_be32(f);
+ s->read_trigger = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int pl011_init(SysBusDevice *dev, const unsigned char *id)
+{
+ int iomemtype;
+ pl011_state *s = FROM_SYSBUS(pl011_state, dev);
+
+ iomemtype = cpu_register_io_memory(pl011_readfn,
+ pl011_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000,iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ s->id = id;
+ s->chr = qdev_init_chardev(&dev->qdev);
+
+ s->read_trigger = 1;
+ s->ifl = 0x12;
+ s->cr = 0x300;
+ s->flags = 0x90;
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
+ pl011_event, s);
+ }
+ register_savevm(&dev->qdev, "pl011_uart", -1, 1, pl011_save, pl011_load, s);
+ return 0;
+}
+
+static int pl011_init_arm(SysBusDevice *dev)
+{
+ return pl011_init(dev, pl011_id_arm);
+}
+
+static int pl011_init_luminary(SysBusDevice *dev)
+{
+ return pl011_init(dev, pl011_id_luminary);
+}
+
+static void pl011_register_devices(void)
+{
+ sysbus_register_dev("pl011", sizeof(pl011_state),
+ pl011_init_arm);
+ sysbus_register_dev("pl011_luminary", sizeof(pl011_state),
+ pl011_init_luminary);
+}
+
+device_init(pl011_register_devices)
diff --git a/hw/pl022.c b/hw/pl022.c
new file mode 100644
index 0000000..ffe05ab
--- /dev/null
+++ b/hw/pl022.c
@@ -0,0 +1,312 @@
+/*
+ * Arm PrimeCell PL022 Synchronous Serial Port
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "ssi.h"
+#include "primecell.h"
+
+//#define DEBUG_PL022 1
+
+#ifdef DEBUG_PL022
+#define DPRINTF(fmt, ...) \
+do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define PL022_CR1_LBM 0x01
+#define PL022_CR1_SSE 0x02
+#define PL022_CR1_MS 0x04
+#define PL022_CR1_SDO 0x08
+
+#define PL022_SR_TFE 0x01
+#define PL022_SR_TNF 0x02
+#define PL022_SR_RNE 0x04
+#define PL022_SR_RFF 0x08
+#define PL022_SR_BSY 0x10
+
+#define PL022_INT_ROR 0x01
+#define PL022_INT_RT 0x04
+#define PL022_INT_RX 0x04
+#define PL022_INT_TX 0x08
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t cr0;
+ uint32_t cr1;
+ uint32_t bitmask;
+ uint32_t sr;
+ uint32_t cpsr;
+ uint32_t is;
+ uint32_t im;
+ /* The FIFO head points to the next empty entry. */
+ int tx_fifo_head;
+ int rx_fifo_head;
+ int tx_fifo_len;
+ int rx_fifo_len;
+ uint16_t tx_fifo[8];
+ uint16_t rx_fifo[8];
+ qemu_irq irq;
+ SSIBus *ssi;
+} pl022_state;
+
+static const unsigned char pl022_id[8] =
+ { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl022_update(pl022_state *s)
+{
+ s->sr = 0;
+ if (s->tx_fifo_len == 0)
+ s->sr |= PL022_SR_TFE;
+ if (s->tx_fifo_len != 8)
+ s->sr |= PL022_SR_TNF;
+ if (s->rx_fifo_len != 0)
+ s->sr |= PL022_SR_RNE;
+ if (s->rx_fifo_len == 8)
+ s->sr |= PL022_SR_RFF;
+ if (s->tx_fifo_len)
+ s->sr |= PL022_SR_BSY;
+ s->is = 0;
+ if (s->rx_fifo_len >= 4)
+ s->is |= PL022_INT_RX;
+ if (s->tx_fifo_len <= 4)
+ s->is |= PL022_INT_TX;
+
+ qemu_set_irq(s->irq, (s->is & s->im) != 0);
+}
+
+static void pl022_xfer(pl022_state *s)
+{
+ int i;
+ int o;
+ int val;
+
+ if ((s->cr1 & PL022_CR1_SSE) == 0) {
+ pl022_update(s);
+ DPRINTF("Disabled\n");
+ return;
+ }
+
+ DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
+ i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
+ o = s->rx_fifo_head;
+ /* ??? We do not emulate the line speed.
+ This may break some applications. The are two problematic cases:
+ (a) A driver feeds data into the TX FIFO until it is full,
+ and only then drains the RX FIFO. On real hardware the CPU can
+ feed data fast enough that the RX fifo never gets chance to overflow.
+ (b) A driver transmits data, deliberately allowing the RX FIFO to
+ overflow because it ignores the RX data anyway.
+
+ We choose to support (a) by stalling the transmit engine if it would
+ cause the RX FIFO to overflow. In practice much transmit-only code
+ falls into (a) because it flushes the RX FIFO to determine when
+ the transfer has completed. */
+ while (s->tx_fifo_len && s->rx_fifo_len < 8) {
+ DPRINTF("xfer\n");
+ val = s->tx_fifo[i];
+ if (s->cr1 & PL022_CR1_LBM) {
+ /* Loopback mode. */
+ } else {
+ val = ssi_transfer(s->ssi, val);
+ }
+ s->rx_fifo[o] = val & s->bitmask;
+ i = (i + 1) & 7;
+ o = (o + 1) & 7;
+ s->tx_fifo_len--;
+ s->rx_fifo_len++;
+ }
+ s->rx_fifo_head = o;
+ pl022_update(s);
+}
+
+static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
+{
+ pl022_state *s = (pl022_state *)opaque;
+ int val;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl022_id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset) {
+ case 0x00: /* CR0 */
+ return s->cr0;
+ case 0x04: /* CR1 */
+ return s->cr1;
+ case 0x08: /* DR */
+ if (s->rx_fifo_len) {
+ val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
+ DPRINTF("RX %02x\n", val);
+ s->rx_fifo_len--;
+ pl022_xfer(s);
+ } else {
+ val = 0;
+ }
+ return val;
+ case 0x0c: /* SR */
+ return s->sr;
+ case 0x10: /* CPSR */
+ return s->cpsr;
+ case 0x14: /* IMSC */
+ return s->im;
+ case 0x18: /* RIS */
+ return s->is;
+ case 0x1c: /* MIS */
+ return s->im & s->is;
+ case 0x20: /* DMACR */
+ /* Not implemented. */
+ return 0;
+ default:
+ hw_error("pl022_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl022_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl022_state *s = (pl022_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* CR0 */
+ s->cr0 = value;
+ /* Clock rate and format are ignored. */
+ s->bitmask = (1 << ((value & 15) + 1)) - 1;
+ break;
+ case 0x04: /* CR1 */
+ s->cr1 = value;
+ if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
+ == (PL022_CR1_MS | PL022_CR1_SSE)) {
+ BADF("SPI slave mode not implemented\n");
+ }
+ pl022_xfer(s);
+ break;
+ case 0x08: /* DR */
+ if (s->tx_fifo_len < 8) {
+ DPRINTF("TX %02x\n", value);
+ s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
+ s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
+ s->tx_fifo_len++;
+ pl022_xfer(s);
+ }
+ break;
+ case 0x10: /* CPSR */
+ /* Prescaler. Ignored. */
+ s->cpsr = value & 0xff;
+ break;
+ case 0x14: /* IMSC */
+ s->im = value;
+ pl022_update(s);
+ break;
+ case 0x20: /* DMACR */
+ if (value) {
+ hw_error("pl022: DMA not implemented\n");
+ }
+ break;
+ default:
+ hw_error("pl022_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static void pl022_reset(pl022_state *s)
+{
+ s->rx_fifo_len = 0;
+ s->tx_fifo_len = 0;
+ s->im = 0;
+ s->is = PL022_INT_TX;
+ s->sr = PL022_SR_TFE | PL022_SR_TNF;
+}
+
+static CPUReadMemoryFunc * const pl022_readfn[] = {
+ pl022_read,
+ pl022_read,
+ pl022_read
+};
+
+static CPUWriteMemoryFunc * const pl022_writefn[] = {
+ pl022_write,
+ pl022_write,
+ pl022_write
+};
+
+static void pl022_save(QEMUFile *f, void *opaque)
+{
+ pl022_state *s = (pl022_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->cr0);
+ qemu_put_be32(f, s->cr1);
+ qemu_put_be32(f, s->bitmask);
+ qemu_put_be32(f, s->sr);
+ qemu_put_be32(f, s->cpsr);
+ qemu_put_be32(f, s->is);
+ qemu_put_be32(f, s->im);
+ qemu_put_be32(f, s->tx_fifo_head);
+ qemu_put_be32(f, s->rx_fifo_head);
+ qemu_put_be32(f, s->tx_fifo_len);
+ qemu_put_be32(f, s->rx_fifo_len);
+ for (i = 0; i < 8; i++) {
+ qemu_put_be16(f, s->tx_fifo[i]);
+ qemu_put_be16(f, s->rx_fifo[i]);
+ }
+}
+
+static int pl022_load(QEMUFile *f, void *opaque, int version_id)
+{
+ pl022_state *s = (pl022_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->cr0 = qemu_get_be32(f);
+ s->cr1 = qemu_get_be32(f);
+ s->bitmask = qemu_get_be32(f);
+ s->sr = qemu_get_be32(f);
+ s->cpsr = qemu_get_be32(f);
+ s->is = qemu_get_be32(f);
+ s->im = qemu_get_be32(f);
+ s->tx_fifo_head = qemu_get_be32(f);
+ s->rx_fifo_head = qemu_get_be32(f);
+ s->tx_fifo_len = qemu_get_be32(f);
+ s->rx_fifo_len = qemu_get_be32(f);
+ for (i = 0; i < 8; i++) {
+ s->tx_fifo[i] = qemu_get_be16(f);
+ s->rx_fifo[i] = qemu_get_be16(f);
+ }
+
+ return 0;
+}
+
+static int pl022_init(SysBusDevice *dev)
+{
+ pl022_state *s = FROM_SYSBUS(pl022_state, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(pl022_readfn,
+ pl022_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ s->ssi = ssi_create_bus(&dev->qdev, "ssi");
+ pl022_reset(s);
+ register_savevm(&dev->qdev, "pl022_ssp", -1, 1, pl022_save, pl022_load, s);
+ return 0;
+}
+
+static void pl022_register_devices(void)
+{
+ sysbus_register_dev("pl022", sizeof(pl022_state), pl022_init);
+}
+
+device_init(pl022_register_devices)
diff --git a/hw/pl031.c b/hw/pl031.c
new file mode 100644
index 0000000..c488f69
--- /dev/null
+++ b/hw/pl031.c
@@ -0,0 +1,237 @@
+/*
+ * ARM AMBA PrimeCell PL031 RTC
+ *
+ * Copyright (c) 2007 CodeSourcery
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+//#define DEBUG_PL031
+
+#ifdef DEBUG_PL031
+#define DPRINTF(fmt, ...) \
+do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define RTC_DR 0x00 /* Data read register */
+#define RTC_MR 0x04 /* Match register */
+#define RTC_LR 0x08 /* Data load register */
+#define RTC_CR 0x0c /* Control register */
+#define RTC_IMSC 0x10 /* Interrupt mask and set register */
+#define RTC_RIS 0x14 /* Raw interrupt status register */
+#define RTC_MIS 0x18 /* Masked interrupt status register */
+#define RTC_ICR 0x1c /* Interrupt clear register */
+
+typedef struct {
+ SysBusDevice busdev;
+ QEMUTimer *timer;
+ qemu_irq irq;
+
+ uint32_t tick_offset;
+
+ uint32_t mr;
+ uint32_t lr;
+ uint32_t cr;
+ uint32_t im;
+ uint32_t is;
+} pl031_state;
+
+static const VMStateDescription vmstate_pl031 = {
+ .name = "pl031",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(tick_offset, pl031_state),
+ VMSTATE_UINT32(mr, pl031_state),
+ VMSTATE_UINT32(lr, pl031_state),
+ VMSTATE_UINT32(cr, pl031_state),
+ VMSTATE_UINT32(im, pl031_state),
+ VMSTATE_UINT32(is, pl031_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const unsigned char pl031_id[] = {
+ 0x31, 0x10, 0x14, 0x00, /* Device ID */
+ 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */
+};
+
+static void pl031_update(pl031_state *s)
+{
+ qemu_set_irq(s->irq, s->is & s->im);
+}
+
+static void pl031_interrupt(void * opaque)
+{
+ pl031_state *s = (pl031_state *)opaque;
+
+ s->im = 1;
+ DPRINTF("Alarm raised\n");
+ pl031_update(s);
+}
+
+static uint32_t pl031_get_count(pl031_state *s)
+{
+ /* This assumes qemu_get_clock returns the time since the machine was
+ created. */
+ return s->tick_offset + qemu_get_clock(vm_clock) / get_ticks_per_sec();
+}
+
+static void pl031_set_alarm(pl031_state *s)
+{
+ int64_t now;
+ uint32_t ticks;
+
+ now = qemu_get_clock(vm_clock);
+ ticks = s->tick_offset + now / get_ticks_per_sec();
+
+ /* The timer wraps around. This subtraction also wraps in the same way,
+ and gives correct results when alarm < now_ticks. */
+ ticks = s->mr - ticks;
+ DPRINTF("Alarm set in %ud ticks\n", ticks);
+ if (ticks == 0) {
+ qemu_del_timer(s->timer);
+ pl031_interrupt(s);
+ } else {
+ qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
+ }
+}
+
+static uint32_t pl031_read(void *opaque, target_phys_addr_t offset)
+{
+ pl031_state *s = (pl031_state *)opaque;
+
+ if (offset >= 0xfe0 && offset < 0x1000)
+ return pl031_id[(offset - 0xfe0) >> 2];
+
+ switch (offset) {
+ case RTC_DR:
+ return pl031_get_count(s);
+ case RTC_MR:
+ return s->mr;
+ case RTC_IMSC:
+ return s->im;
+ case RTC_RIS:
+ return s->is;
+ case RTC_LR:
+ return s->lr;
+ case RTC_CR:
+ /* RTC is permanently enabled. */
+ return 1;
+ case RTC_MIS:
+ return s->is & s->im;
+ case RTC_ICR:
+ fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n",
+ (int)offset);
+ break;
+ default:
+ hw_error("pl031_read: Bad offset 0x%x\n", (int)offset);
+ break;
+ }
+
+ return 0;
+}
+
+static void pl031_write(void * opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl031_state *s = (pl031_state *)opaque;
+
+
+ switch (offset) {
+ case RTC_LR:
+ s->tick_offset += value - pl031_get_count(s);
+ pl031_set_alarm(s);
+ break;
+ case RTC_MR:
+ s->mr = value;
+ pl031_set_alarm(s);
+ break;
+ case RTC_IMSC:
+ s->im = value & 1;
+ DPRINTF("Interrupt mask %d\n", s->im);
+ pl031_update(s);
+ break;
+ case RTC_ICR:
+ /* The PL031 documentation (DDI0224B) states that the interupt is
+ cleared when bit 0 of the written value is set. However the
+ arm926e documentation (DDI0287B) states that the interrupt is
+ cleared when any value is written. */
+ DPRINTF("Interrupt cleared");
+ s->is = 0;
+ pl031_update(s);
+ break;
+ case RTC_CR:
+ /* Written value is ignored. */
+ break;
+
+ case RTC_DR:
+ case RTC_MIS:
+ case RTC_RIS:
+ fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n",
+ (int)offset);
+ break;
+
+ default:
+ hw_error("pl031_write: Bad offset 0x%x\n", (int)offset);
+ break;
+ }
+}
+
+static CPUWriteMemoryFunc * const pl031_writefn[] = {
+ pl031_write,
+ pl031_write,
+ pl031_write
+};
+
+static CPUReadMemoryFunc * const pl031_readfn[] = {
+ pl031_read,
+ pl031_read,
+ pl031_read
+};
+
+static int pl031_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ pl031_state *s = FROM_SYSBUS(pl031_state, dev);
+ struct tm tm;
+
+ iomemtype = cpu_register_io_memory(pl031_readfn, pl031_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ if (iomemtype == -1) {
+ hw_error("pl031_init: Can't register I/O memory\n");
+ }
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ sysbus_init_irq(dev, &s->irq);
+ /* ??? We assume vm_clock is zero at this point. */
+ qemu_get_timedate(&tm, 0);
+ s->tick_offset = mktimegm(&tm);
+
+ s->timer = qemu_new_timer(vm_clock, pl031_interrupt, s);
+ return 0;
+}
+
+static SysBusDeviceInfo pl031_info = {
+ .init = pl031_init,
+ .qdev.name = "pl031",
+ .qdev.size = sizeof(pl031_state),
+ .qdev.vmsd = &vmstate_pl031,
+ .qdev.no_user = 1,
+};
+
+static void pl031_register_devices(void)
+{
+ sysbus_register_withprop(&pl031_info);
+}
+
+device_init(pl031_register_devices)
diff --git a/hw/pl050.c b/hw/pl050.c
new file mode 100644
index 0000000..b155cc0
--- /dev/null
+++ b/hw/pl050.c
@@ -0,0 +1,187 @@
+/*
+ * Arm PrimeCell PL050 Keyboard / Mouse Interface
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "ps2.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ void *dev;
+ uint32_t cr;
+ uint32_t clk;
+ uint32_t last;
+ int pending;
+ qemu_irq irq;
+ int is_mouse;
+} pl050_state;
+
+static const VMStateDescription vmstate_pl050 = {
+ .name = "pl050",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cr, pl050_state),
+ VMSTATE_UINT32(clk, pl050_state),
+ VMSTATE_UINT32(last, pl050_state),
+ VMSTATE_INT32(pending, pl050_state),
+ VMSTATE_INT32(is_mouse, pl050_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define PL050_TXEMPTY (1 << 6)
+#define PL050_TXBUSY (1 << 5)
+#define PL050_RXFULL (1 << 4)
+#define PL050_RXBUSY (1 << 3)
+#define PL050_RXPARITY (1 << 2)
+#define PL050_KMIC (1 << 1)
+#define PL050_KMID (1 << 0)
+
+static const unsigned char pl050_id[] =
+{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl050_update(void *opaque, int level)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ int raise;
+
+ s->pending = level;
+ raise = (s->pending && (s->cr & 0x10) != 0)
+ || (s->cr & 0x08) != 0;
+ qemu_set_irq(s->irq, raise);
+}
+
+static uint32_t pl050_read(void *opaque, target_phys_addr_t offset)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ if (offset >= 0xfe0 && offset < 0x1000)
+ return pl050_id[(offset - 0xfe0) >> 2];
+
+ switch (offset >> 2) {
+ case 0: /* KMICR */
+ return s->cr;
+ case 1: /* KMISTAT */
+ {
+ uint8_t val;
+ uint32_t stat;
+
+ val = s->last;
+ val = val ^ (val >> 4);
+ val = val ^ (val >> 2);
+ val = (val ^ (val >> 1)) & 1;
+
+ stat = PL050_TXEMPTY;
+ if (val)
+ stat |= PL050_RXPARITY;
+ if (s->pending)
+ stat |= PL050_RXFULL;
+
+ return stat;
+ }
+ case 2: /* KMIDATA */
+ if (s->pending)
+ s->last = ps2_read_data(s->dev);
+ return s->last;
+ case 3: /* KMICLKDIV */
+ return s->clk;
+ case 4: /* KMIIR */
+ return s->pending | 2;
+ default:
+ hw_error("pl050_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl050_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl050_state *s = (pl050_state *)opaque;
+ switch (offset >> 2) {
+ case 0: /* KMICR */
+ s->cr = value;
+ pl050_update(s, s->pending);
+ /* ??? Need to implement the enable/disable bit. */
+ break;
+ case 2: /* KMIDATA */
+ /* ??? This should toggle the TX interrupt line. */
+ /* ??? This means kbd/mouse can block each other. */
+ if (s->is_mouse) {
+ ps2_write_mouse(s->dev, value);
+ } else {
+ ps2_write_keyboard(s->dev, value);
+ }
+ break;
+ case 3: /* KMICLKDIV */
+ s->clk = value;
+ return;
+ default:
+ hw_error("pl050_write: Bad offset %x\n", (int)offset);
+ }
+}
+static CPUReadMemoryFunc * const pl050_readfn[] = {
+ pl050_read,
+ pl050_read,
+ pl050_read
+};
+
+static CPUWriteMemoryFunc * const pl050_writefn[] = {
+ pl050_write,
+ pl050_write,
+ pl050_write
+};
+
+static int pl050_init(SysBusDevice *dev, int is_mouse)
+{
+ pl050_state *s = FROM_SYSBUS(pl050_state, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(pl050_readfn,
+ pl050_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ s->is_mouse = is_mouse;
+ if (s->is_mouse)
+ s->dev = ps2_mouse_init(pl050_update, s);
+ else
+ s->dev = ps2_kbd_init(pl050_update, s);
+ return 0;
+}
+
+static int pl050_init_keyboard(SysBusDevice *dev)
+{
+ return pl050_init(dev, 0);
+}
+
+static int pl050_init_mouse(SysBusDevice *dev)
+{
+ return pl050_init(dev, 1);
+}
+
+static SysBusDeviceInfo pl050_kbd_info = {
+ .init = pl050_init_keyboard,
+ .qdev.name = "pl050_keyboard",
+ .qdev.size = sizeof(pl050_state),
+ .qdev.vmsd = &vmstate_pl050,
+};
+
+static SysBusDeviceInfo pl050_mouse_info = {
+ .init = pl050_init_mouse,
+ .qdev.name = "pl050_mouse",
+ .qdev.size = sizeof(pl050_state),
+ .qdev.vmsd = &vmstate_pl050,
+};
+
+static void pl050_register_devices(void)
+{
+ sysbus_register_withprop(&pl050_kbd_info);
+ sysbus_register_withprop(&pl050_mouse_info);
+}
+
+device_init(pl050_register_devices)
diff --git a/hw/pl061.c b/hw/pl061.c
new file mode 100644
index 0000000..1997b7c
--- /dev/null
+++ b/hw/pl061.c
@@ -0,0 +1,317 @@
+/*
+ * Arm PrimeCell PL061 General Purpose IO with additional
+ * Luminary Micro Stellaris bits.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+
+//#define DEBUG_PL061 1
+
+#ifdef DEBUG_PL061
+#define DPRINTF(fmt, ...) \
+do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+static const uint8_t pl061_id[12] =
+ { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+typedef struct {
+ SysBusDevice busdev;
+ int locked;
+ uint8_t data;
+ uint8_t old_data;
+ uint8_t dir;
+ uint8_t isense;
+ uint8_t ibe;
+ uint8_t iev;
+ uint8_t im;
+ uint8_t istate;
+ uint8_t afsel;
+ uint8_t dr2r;
+ uint8_t dr4r;
+ uint8_t dr8r;
+ uint8_t odr;
+ uint8_t pur;
+ uint8_t pdr;
+ uint8_t slr;
+ uint8_t den;
+ uint8_t cr;
+ uint8_t float_high;
+ qemu_irq irq;
+ qemu_irq out[8];
+} pl061_state;
+
+static void pl061_update(pl061_state *s)
+{
+ uint8_t changed;
+ uint8_t mask;
+ uint8_t out;
+ int i;
+
+ /* Outputs float high. */
+ /* FIXME: This is board dependent. */
+ out = (s->data & s->dir) | ~s->dir;
+ changed = s->old_data ^ out;
+ if (!changed)
+ return;
+
+ s->old_data = out;
+ for (i = 0; i < 8; i++) {
+ mask = 1 << i;
+ if ((changed & mask) && s->out) {
+ DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
+ qemu_set_irq(s->out[i], (out & mask) != 0);
+ }
+ }
+
+ /* FIXME: Implement input interrupts. */
+}
+
+static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
+{
+ pl061_state *s = (pl061_state *)opaque;
+
+ if (offset >= 0xfd0 && offset < 0x1000) {
+ return pl061_id[(offset - 0xfd0) >> 2];
+ }
+ if (offset < 0x400) {
+ return s->data & (offset >> 2);
+ }
+ switch (offset) {
+ case 0x400: /* Direction */
+ return s->dir;
+ case 0x404: /* Interrupt sense */
+ return s->isense;
+ case 0x408: /* Interrupt both edges */
+ return s->ibe;
+ case 0x40c: /* Interupt event */
+ return s->iev;
+ case 0x410: /* Interrupt mask */
+ return s->im;
+ case 0x414: /* Raw interrupt status */
+ return s->istate;
+ case 0x418: /* Masked interrupt status */
+ return s->istate | s->im;
+ case 0x420: /* Alternate function select */
+ return s->afsel;
+ case 0x500: /* 2mA drive */
+ return s->dr2r;
+ case 0x504: /* 4mA drive */
+ return s->dr4r;
+ case 0x508: /* 8mA drive */
+ return s->dr8r;
+ case 0x50c: /* Open drain */
+ return s->odr;
+ case 0x510: /* Pull-up */
+ return s->pur;
+ case 0x514: /* Pull-down */
+ return s->pdr;
+ case 0x518: /* Slew rate control */
+ return s->slr;
+ case 0x51c: /* Digital enable */
+ return s->den;
+ case 0x520: /* Lock */
+ return s->locked;
+ case 0x524: /* Commit */
+ return s->cr;
+ default:
+ hw_error("pl061_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl061_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl061_state *s = (pl061_state *)opaque;
+ uint8_t mask;
+
+ if (offset < 0x400) {
+ mask = (offset >> 2) & s->dir;
+ s->data = (s->data & ~mask) | (value & mask);
+ pl061_update(s);
+ return;
+ }
+ switch (offset) {
+ case 0x400: /* Direction */
+ s->dir = value;
+ break;
+ case 0x404: /* Interrupt sense */
+ s->isense = value;
+ break;
+ case 0x408: /* Interrupt both edges */
+ s->ibe = value;
+ break;
+ case 0x40c: /* Interupt event */
+ s->iev = value;
+ break;
+ case 0x410: /* Interrupt mask */
+ s->im = value;
+ break;
+ case 0x41c: /* Interrupt clear */
+ s->istate &= ~value;
+ break;
+ case 0x420: /* Alternate function select */
+ mask = s->cr;
+ s->afsel = (s->afsel & ~mask) | (value & mask);
+ break;
+ case 0x500: /* 2mA drive */
+ s->dr2r = value;
+ break;
+ case 0x504: /* 4mA drive */
+ s->dr4r = value;
+ break;
+ case 0x508: /* 8mA drive */
+ s->dr8r = value;
+ break;
+ case 0x50c: /* Open drain */
+ s->odr = value;
+ break;
+ case 0x510: /* Pull-up */
+ s->pur = value;
+ break;
+ case 0x514: /* Pull-down */
+ s->pdr = value;
+ break;
+ case 0x518: /* Slew rate control */
+ s->slr = value;
+ break;
+ case 0x51c: /* Digital enable */
+ s->den = value;
+ break;
+ case 0x520: /* Lock */
+ s->locked = (value != 0xacce551);
+ break;
+ case 0x524: /* Commit */
+ if (!s->locked)
+ s->cr = value;
+ break;
+ default:
+ hw_error("pl061_write: Bad offset %x\n", (int)offset);
+ }
+ pl061_update(s);
+}
+
+static void pl061_reset(pl061_state *s)
+{
+ s->locked = 1;
+ s->cr = 0xff;
+}
+
+static void pl061_set_irq(void * opaque, int irq, int level)
+{
+ pl061_state *s = (pl061_state *)opaque;
+ uint8_t mask;
+
+ mask = 1 << irq;
+ if ((s->dir & mask) == 0) {
+ s->data &= ~mask;
+ if (level)
+ s->data |= mask;
+ pl061_update(s);
+ }
+}
+
+static CPUReadMemoryFunc * const pl061_readfn[] = {
+ pl061_read,
+ pl061_read,
+ pl061_read
+};
+
+static CPUWriteMemoryFunc * const pl061_writefn[] = {
+ pl061_write,
+ pl061_write,
+ pl061_write
+};
+
+static void pl061_save(QEMUFile *f, void *opaque)
+{
+ pl061_state *s = (pl061_state *)opaque;
+
+ qemu_put_be32(f, s->locked);
+ qemu_put_be32(f, s->data);
+ qemu_put_be32(f, s->old_data);
+ qemu_put_be32(f, s->dir);
+ qemu_put_be32(f, s->isense);
+ qemu_put_be32(f, s->ibe);
+ qemu_put_be32(f, s->iev);
+ qemu_put_be32(f, s->im);
+ qemu_put_be32(f, s->istate);
+ qemu_put_be32(f, s->afsel);
+ qemu_put_be32(f, s->dr2r);
+ qemu_put_be32(f, s->dr4r);
+ qemu_put_be32(f, s->dr8r);
+ qemu_put_be32(f, s->odr);
+ qemu_put_be32(f, s->pur);
+ qemu_put_be32(f, s->pdr);
+ qemu_put_be32(f, s->slr);
+ qemu_put_be32(f, s->den);
+ qemu_put_be32(f, s->cr);
+ qemu_put_be32(f, s->float_high);
+}
+
+static int pl061_load(QEMUFile *f, void *opaque, int version_id)
+{
+ pl061_state *s = (pl061_state *)opaque;
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->locked = qemu_get_be32(f);
+ s->data = qemu_get_be32(f);
+ s->old_data = qemu_get_be32(f);
+ s->dir = qemu_get_be32(f);
+ s->isense = qemu_get_be32(f);
+ s->ibe = qemu_get_be32(f);
+ s->iev = qemu_get_be32(f);
+ s->im = qemu_get_be32(f);
+ s->istate = qemu_get_be32(f);
+ s->afsel = qemu_get_be32(f);
+ s->dr2r = qemu_get_be32(f);
+ s->dr4r = qemu_get_be32(f);
+ s->dr8r = qemu_get_be32(f);
+ s->odr = qemu_get_be32(f);
+ s->pur = qemu_get_be32(f);
+ s->pdr = qemu_get_be32(f);
+ s->slr = qemu_get_be32(f);
+ s->den = qemu_get_be32(f);
+ s->cr = qemu_get_be32(f);
+ s->float_high = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int pl061_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ pl061_state *s = FROM_SYSBUS(pl061_state, dev);
+
+ iomemtype = cpu_register_io_memory(pl061_readfn,
+ pl061_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ qdev_init_gpio_in(&dev->qdev, pl061_set_irq, 8);
+ qdev_init_gpio_out(&dev->qdev, s->out, 8);
+ pl061_reset(s);
+ register_savevm(&dev->qdev, "pl061_gpio", -1, 1, pl061_save, pl061_load, s);
+ return 0;
+}
+
+static void pl061_register_devices(void)
+{
+ sysbus_register_dev("pl061", sizeof(pl061_state),
+ pl061_init);
+}
+
+device_init(pl061_register_devices)
diff --git a/hw/pl080.c b/hw/pl080.c
new file mode 100644
index 0000000..901f04a
--- /dev/null
+++ b/hw/pl080.c
@@ -0,0 +1,407 @@
+/*
+ * Arm PrimeCell PL080/PL081 DMA controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+
+#define PL080_MAX_CHANNELS 8
+#define PL080_CONF_E 0x1
+#define PL080_CONF_M1 0x2
+#define PL080_CONF_M2 0x4
+
+#define PL080_CCONF_H 0x40000
+#define PL080_CCONF_A 0x20000
+#define PL080_CCONF_L 0x10000
+#define PL080_CCONF_ITC 0x08000
+#define PL080_CCONF_IE 0x04000
+#define PL080_CCONF_E 0x00001
+
+#define PL080_CCTRL_I 0x80000000
+#define PL080_CCTRL_DI 0x08000000
+#define PL080_CCTRL_SI 0x04000000
+#define PL080_CCTRL_D 0x02000000
+#define PL080_CCTRL_S 0x01000000
+
+typedef struct {
+ uint32_t src;
+ uint32_t dest;
+ uint32_t lli;
+ uint32_t ctrl;
+ uint32_t conf;
+} pl080_channel;
+
+typedef struct {
+ SysBusDevice busdev;
+ uint8_t tc_int;
+ uint8_t tc_mask;
+ uint8_t err_int;
+ uint8_t err_mask;
+ uint32_t conf;
+ uint32_t sync;
+ uint32_t req_single;
+ uint32_t req_burst;
+ pl080_channel chan[PL080_MAX_CHANNELS];
+ int nchannels;
+ /* Flag to avoid recursive DMA invocations. */
+ int running;
+ qemu_irq irq;
+} pl080_state;
+
+static const VMStateDescription vmstate_pl080_channel = {
+ .name = "pl080_channel",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(src, pl080_channel),
+ VMSTATE_UINT32(dest, pl080_channel),
+ VMSTATE_UINT32(lli, pl080_channel),
+ VMSTATE_UINT32(ctrl, pl080_channel),
+ VMSTATE_UINT32(conf, pl080_channel),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pl080 = {
+ .name = "pl080",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(tc_int, pl080_state),
+ VMSTATE_UINT8(tc_mask, pl080_state),
+ VMSTATE_UINT8(err_int, pl080_state),
+ VMSTATE_UINT8(err_mask, pl080_state),
+ VMSTATE_UINT32(conf, pl080_state),
+ VMSTATE_UINT32(sync, pl080_state),
+ VMSTATE_UINT32(req_single, pl080_state),
+ VMSTATE_UINT32(req_burst, pl080_state),
+ VMSTATE_UINT8(tc_int, pl080_state),
+ VMSTATE_UINT8(tc_int, pl080_state),
+ VMSTATE_UINT8(tc_int, pl080_state),
+ VMSTATE_STRUCT_ARRAY(chan, pl080_state, PL080_MAX_CHANNELS,
+ 1, vmstate_pl080_channel, pl080_channel),
+ VMSTATE_INT32(running, pl080_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const unsigned char pl080_id[] =
+{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static const unsigned char pl081_id[] =
+{ 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl080_update(pl080_state *s)
+{
+ if ((s->tc_int & s->tc_mask)
+ || (s->err_int & s->err_mask))
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static void pl080_run(pl080_state *s)
+{
+ int c;
+ int flow;
+ pl080_channel *ch;
+ int swidth;
+ int dwidth;
+ int xsize;
+ int n;
+ int src_id;
+ int dest_id;
+ int size;
+ uint8_t buff[4];
+ uint32_t req;
+
+ s->tc_mask = 0;
+ for (c = 0; c < s->nchannels; c++) {
+ if (s->chan[c].conf & PL080_CCONF_ITC)
+ s->tc_mask |= 1 << c;
+ if (s->chan[c].conf & PL080_CCONF_IE)
+ s->err_mask |= 1 << c;
+ }
+
+ if ((s->conf & PL080_CONF_E) == 0)
+ return;
+
+hw_error("DMA active\n");
+ /* If we are already in the middle of a DMA operation then indicate that
+ there may be new DMA requests and return immediately. */
+ if (s->running) {
+ s->running++;
+ return;
+ }
+ s->running = 1;
+ while (s->running) {
+ for (c = 0; c < s->nchannels; c++) {
+ ch = &s->chan[c];
+again:
+ /* Test if thiws channel has any pending DMA requests. */
+ if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
+ != PL080_CCONF_E)
+ continue;
+ flow = (ch->conf >> 11) & 7;
+ if (flow >= 4) {
+ hw_error(
+ "pl080_run: Peripheral flow control not implemented\n");
+ }
+ src_id = (ch->conf >> 1) & 0x1f;
+ dest_id = (ch->conf >> 6) & 0x1f;
+ size = ch->ctrl & 0xfff;
+ req = s->req_single | s->req_burst;
+ switch (flow) {
+ case 0:
+ break;
+ case 1:
+ if ((req & (1u << dest_id)) == 0)
+ size = 0;
+ break;
+ case 2:
+ if ((req & (1u << src_id)) == 0)
+ size = 0;
+ break;
+ case 3:
+ if ((req & (1u << src_id)) == 0
+ || (req & (1u << dest_id)) == 0)
+ size = 0;
+ break;
+ }
+ if (!size)
+ continue;
+
+ /* Transfer one element. */
+ /* ??? Should transfer multiple elements for a burst request. */
+ /* ??? Unclear what the proper behavior is when source and
+ destination widths are different. */
+ swidth = 1 << ((ch->ctrl >> 18) & 7);
+ dwidth = 1 << ((ch->ctrl >> 21) & 7);
+ for (n = 0; n < dwidth; n+= swidth) {
+ cpu_physical_memory_read(ch->src, buff + n, swidth);
+ if (ch->ctrl & PL080_CCTRL_SI)
+ ch->src += swidth;
+ }
+ xsize = (dwidth < swidth) ? swidth : dwidth;
+ /* ??? This may pad the value incorrectly for dwidth < 32. */
+ for (n = 0; n < xsize; n += dwidth) {
+ cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
+ if (ch->ctrl & PL080_CCTRL_DI)
+ ch->dest += swidth;
+ }
+
+ size--;
+ ch->ctrl = (ch->ctrl & 0xfffff000) | size;
+ if (size == 0) {
+ /* Transfer complete. */
+ if (ch->lli) {
+ ch->src = ldl_phys(ch->lli);
+ ch->dest = ldl_phys(ch->lli + 4);
+ ch->ctrl = ldl_phys(ch->lli + 12);
+ ch->lli = ldl_phys(ch->lli + 8);
+ } else {
+ ch->conf &= ~PL080_CCONF_E;
+ }
+ if (ch->ctrl & PL080_CCTRL_I) {
+ s->tc_int |= 1 << c;
+ }
+ }
+ goto again;
+ }
+ if (--s->running)
+ s->running = 1;
+ }
+}
+
+static uint32_t pl080_read(void *opaque, target_phys_addr_t offset)
+{
+ pl080_state *s = (pl080_state *)opaque;
+ uint32_t i;
+ uint32_t mask;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ if (s->nchannels == 8) {
+ return pl080_id[(offset - 0xfe0) >> 2];
+ } else {
+ return pl081_id[(offset - 0xfe0) >> 2];
+ }
+ }
+ if (offset >= 0x100 && offset < 0x200) {
+ i = (offset & 0xe0) >> 5;
+ if (i >= s->nchannels)
+ goto bad_offset;
+ switch (offset >> 2) {
+ case 0: /* SrcAddr */
+ return s->chan[i].src;
+ case 1: /* DestAddr */
+ return s->chan[i].dest;
+ case 2: /* LLI */
+ return s->chan[i].lli;
+ case 3: /* Control */
+ return s->chan[i].ctrl;
+ case 4: /* Configuration */
+ return s->chan[i].conf;
+ default:
+ goto bad_offset;
+ }
+ }
+ switch (offset >> 2) {
+ case 0: /* IntStatus */
+ return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
+ case 1: /* IntTCStatus */
+ return (s->tc_int & s->tc_mask);
+ case 3: /* IntErrorStatus */
+ return (s->err_int & s->err_mask);
+ case 5: /* RawIntTCStatus */
+ return s->tc_int;
+ case 6: /* RawIntErrorStatus */
+ return s->err_int;
+ case 7: /* EnbldChns */
+ mask = 0;
+ for (i = 0; i < s->nchannels; i++) {
+ if (s->chan[i].conf & PL080_CCONF_E)
+ mask |= 1 << i;
+ }
+ return mask;
+ case 8: /* SoftBReq */
+ case 9: /* SoftSReq */
+ case 10: /* SoftLBReq */
+ case 11: /* SoftLSReq */
+ /* ??? Implement these. */
+ return 0;
+ case 12: /* Configuration */
+ return s->conf;
+ case 13: /* Sync */
+ return s->sync;
+ default:
+ bad_offset:
+ hw_error("pl080_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl080_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl080_state *s = (pl080_state *)opaque;
+ int i;
+
+ if (offset >= 0x100 && offset < 0x200) {
+ i = (offset & 0xe0) >> 5;
+ if (i >= s->nchannels)
+ goto bad_offset;
+ switch (offset >> 2) {
+ case 0: /* SrcAddr */
+ s->chan[i].src = value;
+ break;
+ case 1: /* DestAddr */
+ s->chan[i].dest = value;
+ break;
+ case 2: /* LLI */
+ s->chan[i].lli = value;
+ break;
+ case 3: /* Control */
+ s->chan[i].ctrl = value;
+ break;
+ case 4: /* Configuration */
+ s->chan[i].conf = value;
+ pl080_run(s);
+ break;
+ }
+ }
+ switch (offset >> 2) {
+ case 2: /* IntTCClear */
+ s->tc_int &= ~value;
+ break;
+ case 4: /* IntErrorClear */
+ s->err_int &= ~value;
+ break;
+ case 8: /* SoftBReq */
+ case 9: /* SoftSReq */
+ case 10: /* SoftLBReq */
+ case 11: /* SoftLSReq */
+ /* ??? Implement these. */
+ hw_error("pl080_write: Soft DMA not implemented\n");
+ break;
+ case 12: /* Configuration */
+ s->conf = value;
+ if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
+ hw_error("pl080_write: Big-endian DMA not implemented\n");
+ }
+ pl080_run(s);
+ break;
+ case 13: /* Sync */
+ s->sync = value;
+ break;
+ default:
+ bad_offset:
+ hw_error("pl080_write: Bad offset %x\n", (int)offset);
+ }
+ pl080_update(s);
+}
+
+static CPUReadMemoryFunc * const pl080_readfn[] = {
+ pl080_read,
+ pl080_read,
+ pl080_read
+};
+
+static CPUWriteMemoryFunc * const pl080_writefn[] = {
+ pl080_write,
+ pl080_write,
+ pl080_write
+};
+
+static int pl08x_init(SysBusDevice *dev, int nchannels)
+{
+ int iomemtype;
+ pl080_state *s = FROM_SYSBUS(pl080_state, dev);
+
+ iomemtype = cpu_register_io_memory(pl080_readfn,
+ pl080_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ s->nchannels = nchannels;
+ return 0;
+}
+
+static int pl080_init(SysBusDevice *dev)
+{
+ return pl08x_init(dev, 8);
+}
+
+static int pl081_init(SysBusDevice *dev)
+{
+ return pl08x_init(dev, 2);
+}
+
+static SysBusDeviceInfo pl080_info = {
+ .init = pl080_init,
+ .qdev.name = "pl080",
+ .qdev.size = sizeof(pl080_state),
+ .qdev.vmsd = &vmstate_pl080,
+ .qdev.no_user = 1,
+};
+
+static SysBusDeviceInfo pl081_info = {
+ .init = pl081_init,
+ .qdev.name = "pl081",
+ .qdev.size = sizeof(pl080_state),
+ .qdev.vmsd = &vmstate_pl080,
+ .qdev.no_user = 1,
+};
+
+/* The PL080 and PL081 are the same except for the number of channels
+ they implement (8 and 2 respectively). */
+static void pl080_register_devices(void)
+{
+ sysbus_register_withprop(&pl080_info);
+ sysbus_register_withprop(&pl081_info);
+}
+
+device_init(pl080_register_devices)
diff --git a/hw/pl110.c b/hw/pl110.c
new file mode 100644
index 0000000..06d2dfa
--- /dev/null
+++ b/hw/pl110.c
@@ -0,0 +1,422 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU LGPL
+ */
+
+#include "sysbus.h"
+#include "console.h"
+#include "framebuffer.h"
+
+#define PL110_CR_EN 0x001
+#define PL110_CR_BGR 0x100
+#define PL110_CR_BEBO 0x200
+#define PL110_CR_BEPO 0x400
+#define PL110_CR_PWR 0x800
+
+enum pl110_bppmode
+{
+ BPP_1,
+ BPP_2,
+ BPP_4,
+ BPP_8,
+ BPP_16,
+ BPP_32
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ DisplayState *ds;
+
+ /* The Versatile/PB uses a slightly modified PL110 controller. */
+ int versatile;
+ uint32_t timing[4];
+ uint32_t cr;
+ uint32_t upbase;
+ uint32_t lpbase;
+ uint32_t int_status;
+ uint32_t int_mask;
+ int cols;
+ int rows;
+ enum pl110_bppmode bpp;
+ int invalidate;
+ uint32_t pallette[256];
+ uint32_t raw_pallette[128];
+ qemu_irq irq;
+} pl110_state;
+
+static const VMStateDescription vmstate_pl110 = {
+ .name = "pl110",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(versatile, pl110_state),
+ VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
+ VMSTATE_UINT32(cr, pl110_state),
+ VMSTATE_UINT32(upbase, pl110_state),
+ VMSTATE_UINT32(lpbase, pl110_state),
+ VMSTATE_UINT32(int_status, pl110_state),
+ VMSTATE_UINT32(int_mask, pl110_state),
+ VMSTATE_INT32(cols, pl110_state),
+ VMSTATE_INT32(rows, pl110_state),
+ VMSTATE_UINT32(bpp, pl110_state),
+ VMSTATE_INT32(invalidate, pl110_state),
+ VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256),
+ VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const unsigned char pl110_id[] =
+{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
+ has a different ID. However Linux only looks for the normal ID. */
+#if 0
+static const unsigned char pl110_versatile_id[] =
+{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#else
+#define pl110_versatile_id pl110_id
+#endif
+
+#include "pixel_ops.h"
+
+#define BITS 8
+#include "pl110_template.h"
+#define BITS 15
+#include "pl110_template.h"
+#define BITS 16
+#include "pl110_template.h"
+#define BITS 24
+#include "pl110_template.h"
+#define BITS 32
+#include "pl110_template.h"
+
+static int pl110_enabled(pl110_state *s)
+{
+ return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
+}
+
+static void pl110_update_display(void *opaque)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ drawfn* fntable;
+ drawfn fn;
+ int dest_width;
+ int src_width;
+ int bpp_offset;
+ int first;
+ int last;
+
+ if (!pl110_enabled(s))
+ return;
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ return;
+ case 8:
+ fntable = pl110_draw_fn_8;
+ dest_width = 1;
+ break;
+ case 15:
+ fntable = pl110_draw_fn_15;
+ dest_width = 2;
+ break;
+ case 16:
+ fntable = pl110_draw_fn_16;
+ dest_width = 2;
+ break;
+ case 24:
+ fntable = pl110_draw_fn_24;
+ dest_width = 3;
+ break;
+ case 32:
+ fntable = pl110_draw_fn_32;
+ dest_width = 4;
+ break;
+ default:
+ fprintf(stderr, "pl110: Bad color depth\n");
+ exit(1);
+ }
+ if (s->cr & PL110_CR_BGR)
+ bpp_offset = 0;
+ else
+ bpp_offset = 18;
+
+ if (s->cr & PL110_CR_BEBO)
+ fn = fntable[s->bpp + 6 + bpp_offset];
+ else if (s->cr & PL110_CR_BEPO)
+ fn = fntable[s->bpp + 12 + bpp_offset];
+ else
+ fn = fntable[s->bpp + bpp_offset];
+
+ src_width = s->cols;
+ switch (s->bpp) {
+ case BPP_1:
+ src_width >>= 3;
+ break;
+ case BPP_2:
+ src_width >>= 2;
+ break;
+ case BPP_4:
+ src_width >>= 1;
+ break;
+ case BPP_8:
+ break;
+ case BPP_16:
+ src_width <<= 1;
+ break;
+ case BPP_32:
+ src_width <<= 2;
+ break;
+ }
+ dest_width *= s->cols;
+ first = 0;
+ framebuffer_update_display(s->ds,
+ s->upbase, s->cols, s->rows,
+ src_width, dest_width, 0,
+ s->invalidate,
+ fn, s->pallette,
+ &first, &last);
+ if (first >= 0) {
+ dpy_update(s->ds, 0, first, s->cols, last - first + 1);
+ }
+ s->invalidate = 0;
+}
+
+static void pl110_invalidate_display(void * opaque)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ s->invalidate = 1;
+ if (pl110_enabled(s)) {
+ qemu_console_resize(s->ds, s->cols, s->rows);
+ }
+}
+
+static void pl110_update_pallette(pl110_state *s, int n)
+{
+ int i;
+ uint32_t raw;
+ unsigned int r, g, b;
+
+ raw = s->raw_pallette[n];
+ n <<= 1;
+ for (i = 0; i < 2; i++) {
+ r = (raw & 0x1f) << 3;
+ raw >>= 5;
+ g = (raw & 0x1f) << 3;
+ raw >>= 5;
+ b = (raw & 0x1f) << 3;
+ /* The I bit is ignored. */
+ raw >>= 6;
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 8:
+ s->pallette[n] = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ s->pallette[n] = rgb_to_pixel15(r, g, b);
+ break;
+ case 16:
+ s->pallette[n] = rgb_to_pixel16(r, g, b);
+ break;
+ case 24:
+ case 32:
+ s->pallette[n] = rgb_to_pixel32(r, g, b);
+ break;
+ }
+ n++;
+ }
+}
+
+static void pl110_resize(pl110_state *s, int width, int height)
+{
+ if (width != s->cols || height != s->rows) {
+ if (pl110_enabled(s)) {
+ qemu_console_resize(s->ds, width, height);
+ }
+ }
+ s->cols = width;
+ s->rows = height;
+}
+
+/* Update interrupts. */
+static void pl110_update(pl110_state *s)
+{
+ /* TODO: Implement interrupts. */
+}
+
+static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
+{
+ pl110_state *s = (pl110_state *)opaque;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ if (s->versatile)
+ return pl110_versatile_id[(offset - 0xfe0) >> 2];
+ else
+ return pl110_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x400) {
+ return s->raw_pallette[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* LCDTiming0 */
+ return s->timing[0];
+ case 1: /* LCDTiming1 */
+ return s->timing[1];
+ case 2: /* LCDTiming2 */
+ return s->timing[2];
+ case 3: /* LCDTiming3 */
+ return s->timing[3];
+ case 4: /* LCDUPBASE */
+ return s->upbase;
+ case 5: /* LCDLPBASE */
+ return s->lpbase;
+ case 6: /* LCDIMSC */
+ if (s->versatile)
+ return s->cr;
+ return s->int_mask;
+ case 7: /* LCDControl */
+ if (s->versatile)
+ return s->int_mask;
+ return s->cr;
+ case 8: /* LCDRIS */
+ return s->int_status;
+ case 9: /* LCDMIS */
+ return s->int_status & s->int_mask;
+ case 11: /* LCDUPCURR */
+ /* TODO: Implement vertical refresh. */
+ return s->upbase;
+ case 12: /* LCDLPCURR */
+ return s->lpbase;
+ default:
+ hw_error("pl110_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl110_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ int n;
+
+ /* For simplicity invalidate the display whenever a control register
+ is writen to. */
+ s->invalidate = 1;
+ if (offset >= 0x200 && offset < 0x400) {
+ /* Pallette. */
+ n = (offset - 0x200) >> 2;
+ s->raw_pallette[(offset - 0x200) >> 2] = val;
+ pl110_update_pallette(s, n);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* LCDTiming0 */
+ s->timing[0] = val;
+ n = ((val & 0xfc) + 4) * 4;
+ pl110_resize(s, n, s->rows);
+ break;
+ case 1: /* LCDTiming1 */
+ s->timing[1] = val;
+ n = (val & 0x3ff) + 1;
+ pl110_resize(s, s->cols, n);
+ break;
+ case 2: /* LCDTiming2 */
+ s->timing[2] = val;
+ break;
+ case 3: /* LCDTiming3 */
+ s->timing[3] = val;
+ break;
+ case 4: /* LCDUPBASE */
+ s->upbase = val;
+ break;
+ case 5: /* LCDLPBASE */
+ s->lpbase = val;
+ break;
+ case 6: /* LCDIMSC */
+ if (s->versatile)
+ goto control;
+ imsc:
+ s->int_mask = val;
+ pl110_update(s);
+ break;
+ case 7: /* LCDControl */
+ if (s->versatile)
+ goto imsc;
+ control:
+ s->cr = val;
+ s->bpp = (val >> 1) & 7;
+ if (pl110_enabled(s)) {
+ qemu_console_resize(s->ds, s->cols, s->rows);
+ }
+ break;
+ case 10: /* LCDICR */
+ s->int_status &= ~val;
+ pl110_update(s);
+ break;
+ default:
+ hw_error("pl110_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static CPUReadMemoryFunc * const pl110_readfn[] = {
+ pl110_read,
+ pl110_read,
+ pl110_read
+};
+
+static CPUWriteMemoryFunc * const pl110_writefn[] = {
+ pl110_write,
+ pl110_write,
+ pl110_write
+};
+
+static int pl110_init(SysBusDevice *dev)
+{
+ pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(pl110_readfn,
+ pl110_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq);
+ s->ds = graphic_console_init(pl110_update_display,
+ pl110_invalidate_display,
+ NULL, NULL, s);
+ return 0;
+}
+
+static int pl110_versatile_init(SysBusDevice *dev)
+{
+ pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+ s->versatile = 1;
+ return pl110_init(dev);
+}
+
+static SysBusDeviceInfo pl110_info = {
+ .init = pl110_init,
+ .qdev.name = "pl110",
+ .qdev.size = sizeof(pl110_state),
+ .qdev.vmsd = &vmstate_pl110,
+ .qdev.no_user = 1,
+};
+
+static SysBusDeviceInfo pl110_versatile_info = {
+ .init = pl110_versatile_init,
+ .qdev.name = "pl110_versatile",
+ .qdev.size = sizeof(pl110_state),
+ .qdev.vmsd = &vmstate_pl110,
+ .qdev.no_user = 1,
+};
+
+static void pl110_register_devices(void)
+{
+ sysbus_register_withprop(&pl110_info);
+ sysbus_register_withprop(&pl110_versatile_info);
+}
+
+device_init(pl110_register_devices)
diff --git a/hw/pl110_template.h b/hw/pl110_template.h
new file mode 100644
index 0000000..b3c9077
--- /dev/null
+++ b/hw/pl110_template.h
@@ -0,0 +1,307 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU LGPL
+ *
+ * Framebuffer format conversion routines.
+ */
+
+#ifndef ORDER
+
+#if BITS == 8
+#define COPY_PIXEL(to, from) *(to++) = from
+#elif BITS == 15 || BITS == 16
+#define COPY_PIXEL(to, from) *(uint16_t *)to = from; to += 2;
+#elif BITS == 24
+#define COPY_PIXEL(to, from) \
+ *(to++) = from; *(to++) = (from) >> 8; *(to++) = (from) >> 16
+#elif BITS == 32
+#define COPY_PIXEL(to, from) *(uint32_t *)to = from; to += 4;
+#else
+#error unknown bit depth
+#endif
+
+#undef RGB
+#define BORDER bgr
+#define ORDER 0
+#include "pl110_template.h"
+#define ORDER 1
+#include "pl110_template.h"
+#define ORDER 2
+#include "pl110_template.h"
+#undef BORDER
+#define RGB
+#define BORDER rgb
+#define ORDER 0
+#include "pl110_template.h"
+#define ORDER 1
+#include "pl110_template.h"
+#define ORDER 2
+#include "pl110_template.h"
+#undef BORDER
+
+static drawfn glue(pl110_draw_fn_,BITS)[36] =
+{
+ glue(pl110_draw_line1_lblp_bgr,BITS),
+ glue(pl110_draw_line2_lblp_bgr,BITS),
+ glue(pl110_draw_line4_lblp_bgr,BITS),
+ glue(pl110_draw_line8_lblp_bgr,BITS),
+ glue(pl110_draw_line16_lblp_bgr,BITS),
+ glue(pl110_draw_line32_lblp_bgr,BITS),
+
+ glue(pl110_draw_line1_bbbp_bgr,BITS),
+ glue(pl110_draw_line2_bbbp_bgr,BITS),
+ glue(pl110_draw_line4_bbbp_bgr,BITS),
+ glue(pl110_draw_line8_bbbp_bgr,BITS),
+ glue(pl110_draw_line16_bbbp_bgr,BITS),
+ glue(pl110_draw_line32_bbbp_bgr,BITS),
+
+ glue(pl110_draw_line1_lbbp_bgr,BITS),
+ glue(pl110_draw_line2_lbbp_bgr,BITS),
+ glue(pl110_draw_line4_lbbp_bgr,BITS),
+ glue(pl110_draw_line8_lbbp_bgr,BITS),
+ glue(pl110_draw_line16_lbbp_bgr,BITS),
+ glue(pl110_draw_line32_lbbp_bgr,BITS),
+
+ glue(pl110_draw_line1_lblp_rgb,BITS),
+ glue(pl110_draw_line2_lblp_rgb,BITS),
+ glue(pl110_draw_line4_lblp_rgb,BITS),
+ glue(pl110_draw_line8_lblp_rgb,BITS),
+ glue(pl110_draw_line16_lblp_rgb,BITS),
+ glue(pl110_draw_line32_lblp_rgb,BITS),
+
+ glue(pl110_draw_line1_bbbp_rgb,BITS),
+ glue(pl110_draw_line2_bbbp_rgb,BITS),
+ glue(pl110_draw_line4_bbbp_rgb,BITS),
+ glue(pl110_draw_line8_bbbp_rgb,BITS),
+ glue(pl110_draw_line16_bbbp_rgb,BITS),
+ glue(pl110_draw_line32_bbbp_rgb,BITS),
+
+ glue(pl110_draw_line1_lbbp_rgb,BITS),
+ glue(pl110_draw_line2_lbbp_rgb,BITS),
+ glue(pl110_draw_line4_lbbp_rgb,BITS),
+ glue(pl110_draw_line8_lbbp_rgb,BITS),
+ glue(pl110_draw_line16_lbbp_rgb,BITS),
+ glue(pl110_draw_line32_lbbp_rgb,BITS),
+};
+
+#undef BITS
+#undef COPY_PIXEL
+
+#else
+
+#if ORDER == 0
+#define NAME glue(glue(lblp_, BORDER), BITS)
+#ifdef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#elif ORDER == 1
+#define NAME glue(glue(bbbp_, BORDER), BITS)
+#ifndef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#else
+#define SWAP_PIXELS 1
+#define NAME glue(glue(lbbp_, BORDER), BITS)
+#ifdef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#endif
+
+#define FN_2(x, y) FN(x, y) FN(x+1, y)
+#define FN_4(x, y) FN_2(x, y) FN_2(x+2, y)
+#define FN_8(y) FN_4(0, y) FN_4(4, y)
+
+static void glue(pl110_draw_line1_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *pallette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 7 - (x))) & 1]);
+#else
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x) + y)) & 1]);
+#endif
+#ifdef SWAP_WORDS
+ FN_8(24)
+ FN_8(16)
+ FN_8(8)
+ FN_8(0)
+#else
+ FN_8(0)
+ FN_8(8)
+ FN_8(16)
+ FN_8(24)
+#endif
+#undef FN
+ width -= 32;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line2_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *pallette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 6 - (x)*2)) & 3]);
+#else
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x)*2 + y)) & 3]);
+#endif
+#ifdef SWAP_WORDS
+ FN_4(0, 24)
+ FN_4(0, 16)
+ FN_4(0, 8)
+ FN_4(0, 0)
+#else
+ FN_4(0, 0)
+ FN_4(0, 8)
+ FN_4(0, 16)
+ FN_4(0, 24)
+#endif
+#undef FN
+ width -= 16;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line4_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *pallette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> (y + 4 - (x)*4)) & 0xf]);
+#else
+#define FN(x, y) COPY_PIXEL(d, pallette[(data >> ((x)*4 + y)) & 0xf]);
+#endif
+#ifdef SWAP_WORDS
+ FN_2(0, 24)
+ FN_2(0, 16)
+ FN_2(0, 8)
+ FN_2(0, 0)
+#else
+ FN_2(0, 0)
+ FN_2(0, 8)
+ FN_2(0, 16)
+ FN_2(0, 24)
+#endif
+#undef FN
+ width -= 8;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line8_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *pallette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#define FN(x) COPY_PIXEL(d, pallette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+ FN(24)
+ FN(16)
+ FN(8)
+ FN(0)
+#else
+ FN(0)
+ FN(8)
+ FN(16)
+ FN(24)
+#endif
+#undef FN
+ width -= 4;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line16_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+#if 0
+ LSB = data & 0x1f;
+ data >>= 5;
+ g = data & 0x3f;
+ data >>= 6;
+ MSB = data & 0x1f;
+ data >>= 5;
+#else
+ LSB = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ MSB = (data & 0x1f) << 3;
+ data >>= 5;
+#endif
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ LSB = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ MSB = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+#undef MSB
+#undef LSB
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line32_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+#ifndef SWAP_WORDS
+ LSB = data & 0xff;
+ g = (data >> 8) & 0xff;
+ MSB = (data >> 16) & 0xff;
+#else
+ LSB = (data >> 24) & 0xff;
+ g = (data >> 16) & 0xff;
+ MSB = (data >> 8) & 0xff;
+#endif
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+#undef MSB
+#undef LSB
+ width--;
+ src += 4;
+ }
+}
+
+#undef SWAP_PIXELS
+#undef NAME
+#undef SWAP_WORDS
+#undef ORDER
+
+#endif
diff --git a/hw/pl181.c b/hw/pl181.c
new file mode 100644
index 0000000..36d9d02
--- /dev/null
+++ b/hw/pl181.c
@@ -0,0 +1,473 @@
+/*
+ * Arm PrimeCell PL181 MultiMedia Card Interface
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "blockdev.h"
+#include "sysbus.h"
+#include "sd.h"
+
+//#define DEBUG_PL181 1
+
+#ifdef DEBUG_PL181
+#define DPRINTF(fmt, ...) \
+do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define PL181_FIFO_LEN 16
+
+typedef struct {
+ SysBusDevice busdev;
+ SDState *card;
+ uint32_t clock;
+ uint32_t power;
+ uint32_t cmdarg;
+ uint32_t cmd;
+ uint32_t datatimer;
+ uint32_t datalength;
+ uint32_t respcmd;
+ uint32_t response[4];
+ uint32_t datactrl;
+ uint32_t datacnt;
+ uint32_t status;
+ uint32_t mask[2];
+ int fifo_pos;
+ int fifo_len;
+ /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
+ while it is reading the FIFO. We hack around this be defering
+ subsequent transfers until after the driver polls the status word.
+ http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
+ */
+ int linux_hack;
+ uint32_t fifo[PL181_FIFO_LEN];
+ qemu_irq irq[2];
+} pl181_state;
+
+#define PL181_CMD_INDEX 0x3f
+#define PL181_CMD_RESPONSE (1 << 6)
+#define PL181_CMD_LONGRESP (1 << 7)
+#define PL181_CMD_INTERRUPT (1 << 8)
+#define PL181_CMD_PENDING (1 << 9)
+#define PL181_CMD_ENABLE (1 << 10)
+
+#define PL181_DATA_ENABLE (1 << 0)
+#define PL181_DATA_DIRECTION (1 << 1)
+#define PL181_DATA_MODE (1 << 2)
+#define PL181_DATA_DMAENABLE (1 << 3)
+
+#define PL181_STATUS_CMDCRCFAIL (1 << 0)
+#define PL181_STATUS_DATACRCFAIL (1 << 1)
+#define PL181_STATUS_CMDTIMEOUT (1 << 2)
+#define PL181_STATUS_DATATIMEOUT (1 << 3)
+#define PL181_STATUS_TXUNDERRUN (1 << 4)
+#define PL181_STATUS_RXOVERRUN (1 << 5)
+#define PL181_STATUS_CMDRESPEND (1 << 6)
+#define PL181_STATUS_CMDSENT (1 << 7)
+#define PL181_STATUS_DATAEND (1 << 8)
+#define PL181_STATUS_DATABLOCKEND (1 << 10)
+#define PL181_STATUS_CMDACTIVE (1 << 11)
+#define PL181_STATUS_TXACTIVE (1 << 12)
+#define PL181_STATUS_RXACTIVE (1 << 13)
+#define PL181_STATUS_TXFIFOHALFEMPTY (1 << 14)
+#define PL181_STATUS_RXFIFOHALFFULL (1 << 15)
+#define PL181_STATUS_TXFIFOFULL (1 << 16)
+#define PL181_STATUS_RXFIFOFULL (1 << 17)
+#define PL181_STATUS_TXFIFOEMPTY (1 << 18)
+#define PL181_STATUS_RXFIFOEMPTY (1 << 19)
+#define PL181_STATUS_TXDATAAVLBL (1 << 20)
+#define PL181_STATUS_RXDATAAVLBL (1 << 21)
+
+#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
+ |PL181_STATUS_TXFIFOHALFEMPTY \
+ |PL181_STATUS_TXFIFOFULL \
+ |PL181_STATUS_TXFIFOEMPTY \
+ |PL181_STATUS_TXDATAAVLBL)
+#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
+ |PL181_STATUS_RXFIFOHALFFULL \
+ |PL181_STATUS_RXFIFOFULL \
+ |PL181_STATUS_RXFIFOEMPTY \
+ |PL181_STATUS_RXDATAAVLBL)
+
+static const unsigned char pl181_id[] =
+{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl181_update(pl181_state *s)
+{
+ int i;
+ for (i = 0; i < 2; i++) {
+ qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
+ }
+}
+
+static void pl181_fifo_push(pl181_state *s, uint32_t value)
+{
+ int n;
+
+ if (s->fifo_len == PL181_FIFO_LEN) {
+ fprintf(stderr, "pl181: FIFO overflow\n");
+ return;
+ }
+ n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
+ s->fifo_len++;
+ s->fifo[n] = value;
+ DPRINTF("FIFO push %08x\n", (int)value);
+}
+
+static uint32_t pl181_fifo_pop(pl181_state *s)
+{
+ uint32_t value;
+
+ if (s->fifo_len == 0) {
+ fprintf(stderr, "pl181: FIFO underflow\n");
+ return 0;
+ }
+ value = s->fifo[s->fifo_pos];
+ s->fifo_len--;
+ s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
+ DPRINTF("FIFO pop %08x\n", (int)value);
+ return value;
+}
+
+static void pl181_send_command(pl181_state *s)
+{
+ SDRequest request;
+ uint8_t response[16];
+ int rlen;
+
+ request.cmd = s->cmd & PL181_CMD_INDEX;
+ request.arg = s->cmdarg;
+ DPRINTF("Command %d %08x\n", request.cmd, request.arg);
+ rlen = sd_do_command(s->card, &request, response);
+ if (rlen < 0)
+ goto error;
+ if (s->cmd & PL181_CMD_RESPONSE) {
+#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
+ | (response[n + 2] << 8) | response[n + 3])
+ if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
+ goto error;
+ if (rlen != 4 && rlen != 16)
+ goto error;
+ s->response[0] = RWORD(0);
+ if (rlen == 4) {
+ s->response[1] = s->response[2] = s->response[3] = 0;
+ } else {
+ s->response[1] = RWORD(4);
+ s->response[2] = RWORD(8);
+ s->response[3] = RWORD(12) & ~1;
+ }
+ DPRINTF("Response received\n");
+ s->status |= PL181_STATUS_CMDRESPEND;
+#undef RWORD
+ } else {
+ DPRINTF("Command sent\n");
+ s->status |= PL181_STATUS_CMDSENT;
+ }
+ return;
+
+error:
+ DPRINTF("Timeout\n");
+ s->status |= PL181_STATUS_CMDTIMEOUT;
+}
+
+/* Transfer data between the card and the FIFO. This is complicated by
+ the FIFO holding 32-bit words and the card taking data in single byte
+ chunks. FIFO bytes are transferred in little-endian order. */
+
+static void pl181_fifo_run(pl181_state *s)
+{
+ uint32_t bits;
+ uint32_t value = 0;
+ int n;
+ int is_read;
+
+ is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
+ if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
+ && !s->linux_hack) {
+ if (is_read) {
+ n = 0;
+ while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
+ value |= (uint32_t)sd_read_data(s->card) << (n * 8);
+ s->datacnt--;
+ n++;
+ if (n == 4) {
+ pl181_fifo_push(s, value);
+ n = 0;
+ value = 0;
+ }
+ }
+ if (n != 0) {
+ pl181_fifo_push(s, value);
+ }
+ } else { /* write */
+ n = 0;
+ while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
+ if (n == 0) {
+ value = pl181_fifo_pop(s);
+ n = 4;
+ }
+ n--;
+ s->datacnt--;
+ sd_write_data(s->card, value & 0xff);
+ value >>= 8;
+ }
+ }
+ }
+ s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
+ if (s->datacnt == 0) {
+ s->status |= PL181_STATUS_DATAEND;
+ /* HACK: */
+ s->status |= PL181_STATUS_DATABLOCKEND;
+ DPRINTF("Transfer Complete\n");
+ }
+ if (s->datacnt == 0 && s->fifo_len == 0) {
+ s->datactrl &= ~PL181_DATA_ENABLE;
+ DPRINTF("Data engine idle\n");
+ } else {
+ /* Update FIFO bits. */
+ bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
+ if (s->fifo_len == 0) {
+ bits |= PL181_STATUS_TXFIFOEMPTY;
+ bits |= PL181_STATUS_RXFIFOEMPTY;
+ } else {
+ bits |= PL181_STATUS_TXDATAAVLBL;
+ bits |= PL181_STATUS_RXDATAAVLBL;
+ }
+ if (s->fifo_len == 16) {
+ bits |= PL181_STATUS_TXFIFOFULL;
+ bits |= PL181_STATUS_RXFIFOFULL;
+ }
+ if (s->fifo_len <= 8) {
+ bits |= PL181_STATUS_TXFIFOHALFEMPTY;
+ }
+ if (s->fifo_len >= 8) {
+ bits |= PL181_STATUS_RXFIFOHALFFULL;
+ }
+ if (s->datactrl & PL181_DATA_DIRECTION) {
+ bits &= PL181_STATUS_RX_FIFO;
+ } else {
+ bits &= PL181_STATUS_TX_FIFO;
+ }
+ s->status |= bits;
+ }
+}
+
+static uint32_t pl181_read(void *opaque, target_phys_addr_t offset)
+{
+ pl181_state *s = (pl181_state *)opaque;
+ uint32_t tmp;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl181_id[(offset - 0xfe0) >> 2];
+ }
+ switch (offset) {
+ case 0x00: /* Power */
+ return s->power;
+ case 0x04: /* Clock */
+ return s->clock;
+ case 0x08: /* Argument */
+ return s->cmdarg;
+ case 0x0c: /* Command */
+ return s->cmd;
+ case 0x10: /* RespCmd */
+ return s->respcmd;
+ case 0x14: /* Response0 */
+ return s->response[0];
+ case 0x18: /* Response1 */
+ return s->response[1];
+ case 0x1c: /* Response2 */
+ return s->response[2];
+ case 0x20: /* Response3 */
+ return s->response[3];
+ case 0x24: /* DataTimer */
+ return s->datatimer;
+ case 0x28: /* DataLength */
+ return s->datalength;
+ case 0x2c: /* DataCtrl */
+ return s->datactrl;
+ case 0x30: /* DataCnt */
+ return s->datacnt;
+ case 0x34: /* Status */
+ tmp = s->status;
+ if (s->linux_hack) {
+ s->linux_hack = 0;
+ pl181_fifo_run(s);
+ pl181_update(s);
+ }
+ return tmp;
+ case 0x3c: /* Mask0 */
+ return s->mask[0];
+ case 0x40: /* Mask1 */
+ return s->mask[1];
+ case 0x48: /* FifoCnt */
+ /* The documentation is somewhat vague about exactly what FifoCnt
+ does. On real hardware it appears to be when decrememnted
+ when a word is transfered between the FIFO and the serial
+ data engine. DataCnt is decremented after each byte is
+ transfered between the serial engine and the card.
+ We don't emulate this level of detail, so both can be the same. */
+ tmp = (s->datacnt + 3) >> 2;
+ if (s->linux_hack) {
+ s->linux_hack = 0;
+ pl181_fifo_run(s);
+ pl181_update(s);
+ }
+ return tmp;
+ case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
+ case 0x90: case 0x94: case 0x98: case 0x9c:
+ case 0xa0: case 0xa4: case 0xa8: case 0xac:
+ case 0xb0: case 0xb4: case 0xb8: case 0xbc:
+ if (s->fifo_len == 0) {
+ fprintf(stderr, "pl181: Unexpected FIFO read\n");
+ return 0;
+ } else {
+ uint32_t value;
+ value = pl181_fifo_pop(s);
+ s->linux_hack = 1;
+ pl181_fifo_run(s);
+ pl181_update(s);
+ return value;
+ }
+ default:
+ hw_error("pl181_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl181_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ pl181_state *s = (pl181_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* Power */
+ s->power = value & 0xff;
+ break;
+ case 0x04: /* Clock */
+ s->clock = value & 0xff;
+ break;
+ case 0x08: /* Argument */
+ s->cmdarg = value;
+ break;
+ case 0x0c: /* Command */
+ s->cmd = value;
+ if (s->cmd & PL181_CMD_ENABLE) {
+ if (s->cmd & PL181_CMD_INTERRUPT) {
+ fprintf(stderr, "pl181: Interrupt mode not implemented\n");
+ abort();
+ } if (s->cmd & PL181_CMD_PENDING) {
+ fprintf(stderr, "pl181: Pending commands not implemented\n");
+ abort();
+ } else {
+ pl181_send_command(s);
+ pl181_fifo_run(s);
+ }
+ /* The command has completed one way or the other. */
+ s->cmd &= ~PL181_CMD_ENABLE;
+ }
+ break;
+ case 0x24: /* DataTimer */
+ s->datatimer = value;
+ break;
+ case 0x28: /* DataLength */
+ s->datalength = value & 0xffff;
+ break;
+ case 0x2c: /* DataCtrl */
+ s->datactrl = value & 0xff;
+ if (value & PL181_DATA_ENABLE) {
+ s->datacnt = s->datalength;
+ pl181_fifo_run(s);
+ }
+ break;
+ case 0x38: /* Clear */
+ s->status &= ~(value & 0x7ff);
+ break;
+ case 0x3c: /* Mask0 */
+ s->mask[0] = value;
+ break;
+ case 0x40: /* Mask1 */
+ s->mask[1] = value;
+ break;
+ case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
+ case 0x90: case 0x94: case 0x98: case 0x9c:
+ case 0xa0: case 0xa4: case 0xa8: case 0xac:
+ case 0xb0: case 0xb4: case 0xb8: case 0xbc:
+ if (s->datacnt == 0) {
+ fprintf(stderr, "pl181: Unexpected FIFO write\n");
+ } else {
+ pl181_fifo_push(s, value);
+ pl181_fifo_run(s);
+ }
+ break;
+ default:
+ hw_error("pl181_write: Bad offset %x\n", (int)offset);
+ }
+ pl181_update(s);
+}
+
+static CPUReadMemoryFunc * const pl181_readfn[] = {
+ pl181_read,
+ pl181_read,
+ pl181_read
+};
+
+static CPUWriteMemoryFunc * const pl181_writefn[] = {
+ pl181_write,
+ pl181_write,
+ pl181_write
+};
+
+static void pl181_reset(void *opaque)
+{
+ pl181_state *s = (pl181_state *)opaque;
+
+ s->power = 0;
+ s->cmdarg = 0;
+ s->cmd = 0;
+ s->datatimer = 0;
+ s->datalength = 0;
+ s->respcmd = 0;
+ s->response[0] = 0;
+ s->response[1] = 0;
+ s->response[2] = 0;
+ s->response[3] = 0;
+ s->datatimer = 0;
+ s->datalength = 0;
+ s->datactrl = 0;
+ s->datacnt = 0;
+ s->status = 0;
+ s->linux_hack = 0;
+ s->mask[0] = 0;
+ s->mask[1] = 0;
+}
+
+static int pl181_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ pl181_state *s = FROM_SYSBUS(pl181_state, dev);
+ DriveInfo *dinfo;
+
+ iomemtype = cpu_register_io_memory(pl181_readfn, pl181_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq[0]);
+ sysbus_init_irq(dev, &s->irq[1]);
+ dinfo = drive_get_next(IF_SD);
+ s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+ qemu_register_reset(pl181_reset, s);
+ pl181_reset(s);
+ /* ??? Save/restore. */
+ return 0;
+}
+
+static void pl181_register_devices(void)
+{
+ sysbus_register_dev("pl181", sizeof(pl181_state), pl181_init);
+}
+
+device_init(pl181_register_devices)
diff --git a/hw/pl190.c b/hw/pl190.c
new file mode 100644
index 0000000..75f2ba1
--- /dev/null
+++ b/hw/pl190.c
@@ -0,0 +1,278 @@
+/*
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+
+/* The number of virtual priority levels. 16 user vectors plus the
+ unvectored IRQ. Chained interrupts would require an additional level
+ if implemented. */
+
+#define PL190_NUM_PRIO 17
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t level;
+ uint32_t soft_level;
+ uint32_t irq_enable;
+ uint32_t fiq_select;
+ uint8_t vect_control[16];
+ uint32_t vect_addr[PL190_NUM_PRIO];
+ /* Mask containing interrupts with higher priority than this one. */
+ uint32_t prio_mask[PL190_NUM_PRIO + 1];
+ int protected;
+ /* Current priority level. */
+ int priority;
+ int prev_prio[PL190_NUM_PRIO];
+ qemu_irq irq;
+ qemu_irq fiq;
+} pl190_state;
+
+static const unsigned char pl190_id[] =
+{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
+
+static inline uint32_t pl190_irq_level(pl190_state *s)
+{
+ return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
+}
+
+/* Update interrupts. */
+static void pl190_update(pl190_state *s)
+{
+ uint32_t level = pl190_irq_level(s);
+ int set;
+
+ set = (level & s->prio_mask[s->priority]) != 0;
+ qemu_set_irq(s->irq, set);
+ set = ((s->level | s->soft_level) & s->fiq_select) != 0;
+ qemu_set_irq(s->fiq, set);
+}
+
+static void pl190_set_irq(void *opaque, int irq, int level)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ pl190_update(s);
+}
+
+static void pl190_update_vectors(pl190_state *s)
+{
+ uint32_t mask;
+ int i;
+ int n;
+
+ mask = 0;
+ for (i = 0; i < 16; i++)
+ {
+ s->prio_mask[i] = mask;
+ if (s->vect_control[i] & 0x20)
+ {
+ n = s->vect_control[i] & 0x1f;
+ mask |= 1 << n;
+ }
+ }
+ s->prio_mask[16] = mask;
+ pl190_update(s);
+}
+
+static uint32_t pl190_read(void *opaque, target_phys_addr_t offset)
+{
+ pl190_state *s = (pl190_state *)opaque;
+ int i;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return pl190_id[(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x100 && offset < 0x140) {
+ return s->vect_addr[(offset - 0x100) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ return s->vect_control[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* IRQSTATUS */
+ return pl190_irq_level(s);
+ case 1: /* FIQSATUS */
+ return (s->level | s->soft_level) & s->fiq_select;
+ case 2: /* RAWINTR */
+ return s->level | s->soft_level;
+ case 3: /* INTSELECT */
+ return s->fiq_select;
+ case 4: /* INTENABLE */
+ return s->irq_enable;
+ case 6: /* SOFTINT */
+ return s->soft_level;
+ case 8: /* PROTECTION */
+ return s->protected;
+ case 12: /* VECTADDR */
+ /* Read vector address at the start of an ISR. Increases the
+ current priority level to that of the current interrupt. */
+ for (i = 0; i < s->priority; i++)
+ {
+ if ((s->level | s->soft_level) & s->prio_mask[i])
+ break;
+ }
+ /* Reading this value with no pending interrupts is undefined.
+ We return the default address. */
+ if (i == PL190_NUM_PRIO)
+ return s->vect_addr[16];
+ if (i < s->priority)
+ {
+ s->prev_prio[i] = s->priority;
+ s->priority = i;
+ pl190_update(s);
+ }
+ return s->vect_addr[s->priority];
+ case 13: /* DEFVECTADDR */
+ return s->vect_addr[16];
+ default:
+ hw_error("pl190_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ pl190_state *s = (pl190_state *)opaque;
+
+ if (offset >= 0x100 && offset < 0x140) {
+ s->vect_addr[(offset - 0x100) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ if (offset >= 0x200 && offset < 0x240) {
+ s->vect_control[(offset - 0x200) >> 2] = val;
+ pl190_update_vectors(s);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* SELECT */
+ /* This is a readonly register, but linux tries to write to it
+ anyway. Ignore the write. */
+ break;
+ case 3: /* INTSELECT */
+ s->fiq_select = val;
+ break;
+ case 4: /* INTENABLE */
+ s->irq_enable |= val;
+ break;
+ case 5: /* INTENCLEAR */
+ s->irq_enable &= ~val;
+ break;
+ case 6: /* SOFTINT */
+ s->soft_level |= val;
+ break;
+ case 7: /* SOFTINTCLEAR */
+ s->soft_level &= ~val;
+ break;
+ case 8: /* PROTECTION */
+ /* TODO: Protection (supervisor only access) is not implemented. */
+ s->protected = val & 1;
+ break;
+ case 12: /* VECTADDR */
+ /* Restore the previous priority level. The value written is
+ ignored. */
+ if (s->priority < PL190_NUM_PRIO)
+ s->priority = s->prev_prio[s->priority];
+ break;
+ case 13: /* DEFVECTADDR */
+ s->vect_addr[16] = val;
+ break;
+ case 0xc0: /* ITCR */
+ if (val) {
+ hw_error("pl190: Test mode not implemented\n");
+ }
+ break;
+ default:
+ hw_error("pl190_write: Bad offset %x\n", (int)offset);
+ return;
+ }
+ pl190_update(s);
+}
+
+static CPUReadMemoryFunc * const pl190_readfn[] = {
+ pl190_read,
+ pl190_read,
+ pl190_read
+};
+
+static CPUWriteMemoryFunc * const pl190_writefn[] = {
+ pl190_write,
+ pl190_write,
+ pl190_write
+};
+
+static void pl190_reset(DeviceState *d)
+{
+ pl190_state *s = DO_UPCAST(pl190_state, busdev.qdev, d);
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ s->vect_addr[i] = 0;
+ s->vect_control[i] = 0;
+ }
+ s->vect_addr[16] = 0;
+ s->prio_mask[17] = 0xffffffff;
+ s->priority = PL190_NUM_PRIO;
+ pl190_update_vectors(s);
+}
+
+static int pl190_init(SysBusDevice *dev)
+{
+ pl190_state *s = FROM_SYSBUS(pl190_state, dev);
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(pl190_readfn,
+ pl190_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fiq);
+ return 0;
+}
+
+static const VMStateDescription vmstate_pl190 = {
+ .name = "pl190",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, pl190_state),
+ VMSTATE_UINT32(soft_level, pl190_state),
+ VMSTATE_UINT32(irq_enable, pl190_state),
+ VMSTATE_UINT32(fiq_select, pl190_state),
+ VMSTATE_UINT8_ARRAY(vect_control, pl190_state, 16),
+ VMSTATE_UINT32_ARRAY(vect_addr, pl190_state, PL190_NUM_PRIO),
+ VMSTATE_UINT32_ARRAY(prio_mask, pl190_state, PL190_NUM_PRIO+1),
+ VMSTATE_INT32(protected, pl190_state),
+ VMSTATE_INT32(priority, pl190_state),
+ VMSTATE_INT32_ARRAY(prev_prio, pl190_state, PL190_NUM_PRIO),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo pl190_info = {
+ .init = pl190_init,
+ .qdev.name = "pl190",
+ .qdev.size = sizeof(pl190_state),
+ .qdev.vmsd = &vmstate_pl190,
+ .qdev.reset = pl190_reset,
+ .qdev.no_user = 1,
+};
+
+static void pl190_register_devices(void)
+{
+ sysbus_register_withprop(&pl190_info);
+}
+
+device_init(pl190_register_devices)
diff --git a/hw/pm_smbus.c b/hw/pm_smbus.c
new file mode 100644
index 0000000..5d6046d
--- /dev/null
+++ b/hw/pm_smbus.c
@@ -0,0 +1,176 @@
+/*
+ * PC SMBus implementation
+ * splitted from acpi.c
+ *
+ * Copyright (c) 2006 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 version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "hw.h"
+#include "pc.h"
+#include "pm_smbus.h"
+#include "smbus.h"
+
+/* no save/load? */
+
+#define SMBHSTSTS 0x00
+#define SMBHSTCNT 0x02
+#define SMBHSTCMD 0x03
+#define SMBHSTADD 0x04
+#define SMBHSTDAT0 0x05
+#define SMBHSTDAT1 0x06
+#define SMBBLKDAT 0x07
+
+//#define DEBUG
+
+#ifdef DEBUG
+# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
+#else
+# define SMBUS_DPRINTF(format, ...) do { } while (0)
+#endif
+
+
+static void smb_transaction(PMSMBus *s)
+{
+ uint8_t prot = (s->smb_ctl >> 2) & 0x07;
+ uint8_t read = s->smb_addr & 0x01;
+ uint8_t cmd = s->smb_cmd;
+ uint8_t addr = s->smb_addr >> 1;
+ i2c_bus *bus = s->smbus;
+
+ SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
+ switch(prot) {
+ case 0x0:
+ smbus_quick_command(bus, addr, read);
+ break;
+ case 0x1:
+ if (read) {
+ s->smb_data0 = smbus_receive_byte(bus, addr);
+ } else {
+ smbus_send_byte(bus, addr, cmd);
+ }
+ break;
+ case 0x2:
+ if (read) {
+ s->smb_data0 = smbus_read_byte(bus, addr, cmd);
+ } else {
+ smbus_write_byte(bus, addr, cmd, s->smb_data0);
+ }
+ break;
+ case 0x3:
+ if (read) {
+ uint16_t val;
+ val = smbus_read_word(bus, addr, cmd);
+ s->smb_data0 = val;
+ s->smb_data1 = val >> 8;
+ } else {
+ smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
+ }
+ break;
+ case 0x5:
+ if (read) {
+ s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data);
+ } else {
+ smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
+ }
+ break;
+ default:
+ goto error;
+ }
+ return;
+
+ error:
+ s->smb_stat |= 0x04;
+}
+
+void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ PMSMBus *s = opaque;
+ addr &= 0x3f;
+ SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val);
+ switch(addr) {
+ case SMBHSTSTS:
+ s->smb_stat = 0;
+ s->smb_index = 0;
+ break;
+ case SMBHSTCNT:
+ s->smb_ctl = val;
+ if (val & 0x40)
+ smb_transaction(s);
+ break;
+ case SMBHSTCMD:
+ s->smb_cmd = val;
+ break;
+ case SMBHSTADD:
+ s->smb_addr = val;
+ break;
+ case SMBHSTDAT0:
+ s->smb_data0 = val;
+ break;
+ case SMBHSTDAT1:
+ s->smb_data1 = val;
+ break;
+ case SMBBLKDAT:
+ s->smb_data[s->smb_index++] = val;
+ if (s->smb_index > 31)
+ s->smb_index = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+uint32_t smb_ioport_readb(void *opaque, uint32_t addr)
+{
+ PMSMBus *s = opaque;
+ uint32_t val;
+
+ addr &= 0x3f;
+ switch(addr) {
+ case SMBHSTSTS:
+ val = s->smb_stat;
+ break;
+ case SMBHSTCNT:
+ s->smb_index = 0;
+ val = s->smb_ctl & 0x1f;
+ break;
+ case SMBHSTCMD:
+ val = s->smb_cmd;
+ break;
+ case SMBHSTADD:
+ val = s->smb_addr;
+ break;
+ case SMBHSTDAT0:
+ val = s->smb_data0;
+ break;
+ case SMBHSTDAT1:
+ val = s->smb_data1;
+ break;
+ case SMBBLKDAT:
+ val = s->smb_data[s->smb_index++];
+ if (s->smb_index > 31)
+ s->smb_index = 0;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val);
+ return val;
+}
+
+void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
+{
+ smb->smbus = i2c_init_bus(parent, "i2c");
+}
diff --git a/hw/pm_smbus.h b/hw/pm_smbus.h
new file mode 100644
index 0000000..4750a40
--- /dev/null
+++ b/hw/pm_smbus.h
@@ -0,0 +1,21 @@
+#ifndef PM_SMBUS_H
+#define PM_SMBUS_H
+
+typedef struct PMSMBus {
+ i2c_bus *smbus;
+
+ uint8_t smb_stat;
+ uint8_t smb_ctl;
+ uint8_t smb_cmd;
+ uint8_t smb_addr;
+ uint8_t smb_data0;
+ uint8_t smb_data1;
+ uint8_t smb_data[32];
+ uint8_t smb_index;
+} PMSMBus;
+
+void pm_smbus_init(DeviceState *parent, PMSMBus *smb);
+void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val);
+uint32_t smb_ioport_readb(void *opaque, uint32_t addr);
+
+#endif /* !PM_SMBUS_H */
diff --git a/hw/ppc.c b/hw/ppc.c
new file mode 100644
index 0000000..968aec1
--- /dev/null
+++ b/hw/ppc.c
@@ -0,0 +1,1288 @@
+/*
+ * QEMU generic PowerPC hardware System Emulator
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "nvram.h"
+#include "qemu-log.h"
+#include "loader.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+
+//#define PPC_DEBUG_IRQ
+//#define PPC_DEBUG_TB
+
+#ifdef PPC_DEBUG_IRQ
+# define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
+#else
+# define LOG_IRQ(...) do { } while (0)
+#endif
+
+
+#ifdef PPC_DEBUG_TB
+# define LOG_TB(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_TB(...) do { } while (0)
+#endif
+
+static void cpu_ppc_tb_stop (CPUState *env);
+static void cpu_ppc_tb_start (CPUState *env);
+
+static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
+{
+ unsigned int old_pending = env->pending_interrupts;
+
+ if (level) {
+ env->pending_interrupts |= 1 << n_IRQ;
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ } else {
+ env->pending_interrupts &= ~(1 << n_IRQ);
+ if (env->pending_interrupts == 0)
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+
+ if (old_pending != env->pending_interrupts) {
+#ifdef CONFIG_KVM
+ kvmppc_set_interrupt(env, n_IRQ, level);
+#endif
+ }
+
+ LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
+ "req %08x\n", __func__, env, n_IRQ, level,
+ env->pending_interrupts, env->interrupt_request);
+}
+
+/* PowerPC 6xx / 7xx internal IRQ controller */
+static void ppc6xx_set_irq (void *opaque, int pin, int level)
+{
+ CPUState *env = opaque;
+ int cur_level;
+
+ LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
+ env, pin, level);
+ cur_level = (env->irq_input_state >> pin) & 1;
+ /* Don't generate spurious events */
+ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
+ switch (pin) {
+ case PPC6xx_INPUT_TBEN:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: %s the time base\n",
+ __func__, level ? "start" : "stop");
+ if (level) {
+ cpu_ppc_tb_start(env);
+ } else {
+ cpu_ppc_tb_stop(env);
+ }
+ case PPC6xx_INPUT_INT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the external IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
+ break;
+ case PPC6xx_INPUT_SMI:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the SMI IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_SMI, level);
+ break;
+ case PPC6xx_INPUT_MCP:
+ /* Negative edge sensitive */
+ /* XXX: TODO: actual reaction may depends on HID0 status
+ * 603/604/740/750: check HID0[EMCP]
+ */
+ if (cur_level == 1 && level == 0) {
+ LOG_IRQ("%s: raise machine check state\n",
+ __func__);
+ ppc_set_irq(env, PPC_INTERRUPT_MCK, 1);
+ }
+ break;
+ case PPC6xx_INPUT_CKSTP_IN:
+ /* Level sensitive - active low */
+ /* XXX: TODO: relay the signal to CKSTP_OUT pin */
+ /* XXX: Note that the only way to restart the CPU is to reset it */
+ if (level) {
+ LOG_IRQ("%s: stop the CPU\n", __func__);
+ env->halted = 1;
+ }
+ break;
+ case PPC6xx_INPUT_HRESET:
+ /* Level sensitive - active low */
+ if (level) {
+ LOG_IRQ("%s: reset the CPU\n", __func__);
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ /* XXX: TOFIX */
+#if 0
+ cpu_reset(env);
+#else
+ qemu_system_reset_request();
+#endif
+ }
+ break;
+ case PPC6xx_INPUT_SRESET:
+ LOG_IRQ("%s: set the RESET IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_RESET, level);
+ break;
+ default:
+ /* Unknown pin - do nothing */
+ LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+ return;
+ }
+ if (level)
+ env->irq_input_state |= 1 << pin;
+ else
+ env->irq_input_state &= ~(1 << pin);
+ }
+}
+
+void ppc6xx_irq_init (CPUState *env)
+{
+ env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, env,
+ PPC6xx_INPUT_NB);
+}
+
+#if defined(TARGET_PPC64)
+/* PowerPC 970 internal IRQ controller */
+static void ppc970_set_irq (void *opaque, int pin, int level)
+{
+ CPUState *env = opaque;
+ int cur_level;
+
+ LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
+ env, pin, level);
+ cur_level = (env->irq_input_state >> pin) & 1;
+ /* Don't generate spurious events */
+ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
+ switch (pin) {
+ case PPC970_INPUT_INT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the external IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
+ break;
+ case PPC970_INPUT_THINT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__,
+ level);
+ ppc_set_irq(env, PPC_INTERRUPT_THERM, level);
+ break;
+ case PPC970_INPUT_MCP:
+ /* Negative edge sensitive */
+ /* XXX: TODO: actual reaction may depends on HID0 status
+ * 603/604/740/750: check HID0[EMCP]
+ */
+ if (cur_level == 1 && level == 0) {
+ LOG_IRQ("%s: raise machine check state\n",
+ __func__);
+ ppc_set_irq(env, PPC_INTERRUPT_MCK, 1);
+ }
+ break;
+ case PPC970_INPUT_CKSTP:
+ /* Level sensitive - active low */
+ /* XXX: TODO: relay the signal to CKSTP_OUT pin */
+ if (level) {
+ LOG_IRQ("%s: stop the CPU\n", __func__);
+ env->halted = 1;
+ } else {
+ LOG_IRQ("%s: restart the CPU\n", __func__);
+ env->halted = 0;
+ }
+ break;
+ case PPC970_INPUT_HRESET:
+ /* Level sensitive - active low */
+ if (level) {
+#if 0 // XXX: TOFIX
+ LOG_IRQ("%s: reset the CPU\n", __func__);
+ cpu_reset(env);
+#endif
+ }
+ break;
+ case PPC970_INPUT_SRESET:
+ LOG_IRQ("%s: set the RESET IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_RESET, level);
+ break;
+ case PPC970_INPUT_TBEN:
+ LOG_IRQ("%s: set the TBEN state to %d\n", __func__,
+ level);
+ /* XXX: TODO */
+ break;
+ default:
+ /* Unknown pin - do nothing */
+ LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+ return;
+ }
+ if (level)
+ env->irq_input_state |= 1 << pin;
+ else
+ env->irq_input_state &= ~(1 << pin);
+ }
+}
+
+void ppc970_irq_init (CPUState *env)
+{
+ env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env,
+ PPC970_INPUT_NB);
+}
+#endif /* defined(TARGET_PPC64) */
+
+/* PowerPC 40x internal IRQ controller */
+static void ppc40x_set_irq (void *opaque, int pin, int level)
+{
+ CPUState *env = opaque;
+ int cur_level;
+
+ LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
+ env, pin, level);
+ cur_level = (env->irq_input_state >> pin) & 1;
+ /* Don't generate spurious events */
+ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
+ switch (pin) {
+ case PPC40x_INPUT_RESET_SYS:
+ if (level) {
+ LOG_IRQ("%s: reset the PowerPC system\n",
+ __func__);
+ ppc40x_system_reset(env);
+ }
+ break;
+ case PPC40x_INPUT_RESET_CHIP:
+ if (level) {
+ LOG_IRQ("%s: reset the PowerPC chip\n", __func__);
+ ppc40x_chip_reset(env);
+ }
+ break;
+ case PPC40x_INPUT_RESET_CORE:
+ /* XXX: TODO: update DBSR[MRR] */
+ if (level) {
+ LOG_IRQ("%s: reset the PowerPC core\n", __func__);
+ ppc40x_core_reset(env);
+ }
+ break;
+ case PPC40x_INPUT_CINT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the critical IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_CEXT, level);
+ break;
+ case PPC40x_INPUT_INT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the external IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
+ break;
+ case PPC40x_INPUT_HALT:
+ /* Level sensitive - active low */
+ if (level) {
+ LOG_IRQ("%s: stop the CPU\n", __func__);
+ env->halted = 1;
+ } else {
+ LOG_IRQ("%s: restart the CPU\n", __func__);
+ env->halted = 0;
+ }
+ break;
+ case PPC40x_INPUT_DEBUG:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the debug pin state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_DEBUG, level);
+ break;
+ default:
+ /* Unknown pin - do nothing */
+ LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+ return;
+ }
+ if (level)
+ env->irq_input_state |= 1 << pin;
+ else
+ env->irq_input_state &= ~(1 << pin);
+ }
+}
+
+void ppc40x_irq_init (CPUState *env)
+{
+ env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq,
+ env, PPC40x_INPUT_NB);
+}
+
+/* PowerPC E500 internal IRQ controller */
+static void ppce500_set_irq (void *opaque, int pin, int level)
+{
+ CPUState *env = opaque;
+ int cur_level;
+
+ LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
+ env, pin, level);
+ cur_level = (env->irq_input_state >> pin) & 1;
+ /* Don't generate spurious events */
+ if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
+ switch (pin) {
+ case PPCE500_INPUT_MCK:
+ if (level) {
+ LOG_IRQ("%s: reset the PowerPC system\n",
+ __func__);
+ qemu_system_reset_request();
+ }
+ break;
+ case PPCE500_INPUT_RESET_CORE:
+ if (level) {
+ LOG_IRQ("%s: reset the PowerPC core\n", __func__);
+ ppc_set_irq(env, PPC_INTERRUPT_MCK, level);
+ }
+ break;
+ case PPCE500_INPUT_CINT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the critical IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_CEXT, level);
+ break;
+ case PPCE500_INPUT_INT:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the core IRQ state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
+ break;
+ case PPCE500_INPUT_DEBUG:
+ /* Level sensitive - active high */
+ LOG_IRQ("%s: set the debug pin state to %d\n",
+ __func__, level);
+ ppc_set_irq(env, PPC_INTERRUPT_DEBUG, level);
+ break;
+ default:
+ /* Unknown pin - do nothing */
+ LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
+ return;
+ }
+ if (level)
+ env->irq_input_state |= 1 << pin;
+ else
+ env->irq_input_state &= ~(1 << pin);
+ }
+}
+
+void ppce500_irq_init (CPUState *env)
+{
+ env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq,
+ env, PPCE500_INPUT_NB);
+}
+/*****************************************************************************/
+/* PowerPC time base and decrementer emulation */
+struct ppc_tb_t {
+ /* Time base management */
+ int64_t tb_offset; /* Compensation */
+ int64_t atb_offset; /* Compensation */
+ uint32_t tb_freq; /* TB frequency */
+ /* Decrementer management */
+ uint64_t decr_next; /* Tick for next decr interrupt */
+ uint32_t decr_freq; /* decrementer frequency */
+ struct QEMUTimer *decr_timer;
+ /* Hypervisor decrementer management */
+ uint64_t hdecr_next; /* Tick for next hdecr interrupt */
+ struct QEMUTimer *hdecr_timer;
+ uint64_t purr_load;
+ uint64_t purr_start;
+ void *opaque;
+};
+
+static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk,
+ int64_t tb_offset)
+{
+ /* TB time in tb periods */
+ return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
+}
+
+uint64_t cpu_ppc_load_tbl (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
+ LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
+
+ return tb;
+}
+
+static inline uint32_t _cpu_ppc_load_tbu(CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
+ LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
+
+ return tb >> 32;
+}
+
+uint32_t cpu_ppc_load_tbu (CPUState *env)
+{
+ return _cpu_ppc_load_tbu(env);
+}
+
+static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
+ int64_t *tb_offsetp, uint64_t value)
+{
+ *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec());
+ LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n",
+ __func__, value, *tb_offsetp);
+}
+
+void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
+ tb &= 0xFFFFFFFF00000000ULL;
+ cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
+ &tb_env->tb_offset, tb | (uint64_t)value);
+}
+
+static inline void _cpu_ppc_store_tbu(CPUState *env, uint32_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
+ tb &= 0x00000000FFFFFFFFULL;
+ cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
+ &tb_env->tb_offset, ((uint64_t)value << 32) | tb);
+}
+
+void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
+{
+ _cpu_ppc_store_tbu(env, value);
+}
+
+uint64_t cpu_ppc_load_atbl (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
+ LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
+
+ return tb;
+}
+
+uint32_t cpu_ppc_load_atbu (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
+ LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
+
+ return tb >> 32;
+}
+
+void cpu_ppc_store_atbl (CPUState *env, uint32_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
+ tb &= 0xFFFFFFFF00000000ULL;
+ cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
+ &tb_env->atb_offset, tb | (uint64_t)value);
+}
+
+void cpu_ppc_store_atbu (CPUState *env, uint32_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb;
+
+ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
+ tb &= 0x00000000FFFFFFFFULL;
+ cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
+ &tb_env->atb_offset, ((uint64_t)value << 32) | tb);
+}
+
+static void cpu_ppc_tb_stop (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb, atb, vmclk;
+
+ /* If the time base is already frozen, do nothing */
+ if (tb_env->tb_freq != 0) {
+ vmclk = qemu_get_clock(vm_clock);
+ /* Get the time base */
+ tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset);
+ /* Get the alternate time base */
+ atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset);
+ /* Store the time base value (ie compute the current offset) */
+ cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
+ /* Store the alternate time base value (compute the current offset) */
+ cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
+ /* Set the time base frequency to zero */
+ tb_env->tb_freq = 0;
+ /* Now, the time bases are frozen to tb_offset / atb_offset value */
+ }
+}
+
+static void cpu_ppc_tb_start (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t tb, atb, vmclk;
+
+ /* If the time base is not frozen, do nothing */
+ if (tb_env->tb_freq == 0) {
+ vmclk = qemu_get_clock(vm_clock);
+ /* Get the time base from tb_offset */
+ tb = tb_env->tb_offset;
+ /* Get the alternate time base from atb_offset */
+ atb = tb_env->atb_offset;
+ /* Restore the tb frequency from the decrementer frequency */
+ tb_env->tb_freq = tb_env->decr_freq;
+ /* Store the time base value */
+ cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
+ /* Store the alternate time base value */
+ cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
+ }
+}
+
+static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint32_t decr;
+ int64_t diff;
+
+ diff = next - qemu_get_clock(vm_clock);
+ if (diff >= 0)
+ decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
+ else
+ decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
+ LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
+
+ return decr;
+}
+
+uint32_t cpu_ppc_load_decr (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ return _cpu_ppc_load_decr(env, tb_env->decr_next);
+}
+
+uint32_t cpu_ppc_load_hdecr (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ return _cpu_ppc_load_decr(env, tb_env->hdecr_next);
+}
+
+uint64_t cpu_ppc_load_purr (CPUState *env)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t diff;
+
+ diff = qemu_get_clock(vm_clock) - tb_env->purr_start;
+
+ return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec());
+}
+
+/* When decrementer expires,
+ * all we need to do is generate or queue a CPU exception
+ */
+static inline void cpu_ppc_decr_excp(CPUState *env)
+{
+ /* Raise it */
+ LOG_TB("raise decrementer exception\n");
+ ppc_set_irq(env, PPC_INTERRUPT_DECR, 1);
+}
+
+static inline void cpu_ppc_hdecr_excp(CPUState *env)
+{
+ /* Raise it */
+ LOG_TB("raise decrementer exception\n");
+ ppc_set_irq(env, PPC_INTERRUPT_HDECR, 1);
+}
+
+static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
+ struct QEMUTimer *timer,
+ void (*raise_excp)(CPUState *),
+ uint32_t decr, uint32_t value,
+ int is_excp)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t now, next;
+
+ LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__,
+ decr, value);
+ now = qemu_get_clock(vm_clock);
+ next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
+ if (is_excp)
+ next += *nextp - now;
+ if (next == now)
+ next++;
+ *nextp = next;
+ /* Adjust timer */
+ qemu_mod_timer(timer, next);
+ /* If we set a negative value and the decrementer was positive,
+ * raise an exception.
+ */
+ if ((value & 0x80000000) && !(decr & 0x80000000))
+ (*raise_excp)(env);
+}
+
+static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
+ uint32_t value, int is_excp)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ __cpu_ppc_store_decr(env, &tb_env->decr_next, tb_env->decr_timer,
+ &cpu_ppc_decr_excp, decr, value, is_excp);
+}
+
+void cpu_ppc_store_decr (CPUState *env, uint32_t value)
+{
+ _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
+}
+
+static void cpu_ppc_decr_cb (void *opaque)
+{
+ _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
+}
+
+static inline void _cpu_ppc_store_hdecr(CPUState *env, uint32_t hdecr,
+ uint32_t value, int is_excp)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ if (tb_env->hdecr_timer != NULL) {
+ __cpu_ppc_store_decr(env, &tb_env->hdecr_next, tb_env->hdecr_timer,
+ &cpu_ppc_hdecr_excp, hdecr, value, is_excp);
+ }
+}
+
+void cpu_ppc_store_hdecr (CPUState *env, uint32_t value)
+{
+ _cpu_ppc_store_hdecr(env, cpu_ppc_load_hdecr(env), value, 0);
+}
+
+static void cpu_ppc_hdecr_cb (void *opaque)
+{
+ _cpu_ppc_store_hdecr(opaque, 0x00000000, 0xFFFFFFFF, 1);
+}
+
+void cpu_ppc_store_purr (CPUState *env, uint64_t value)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+
+ tb_env->purr_load = value;
+ tb_env->purr_start = qemu_get_clock(vm_clock);
+}
+
+static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
+{
+ CPUState *env = opaque;
+ ppc_tb_t *tb_env = env->tb_env;
+
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ /* There is a bug in Linux 2.4 kernels:
+ * if a decrementer exception is pending when it enables msr_ee at startup,
+ * it's not ready to handle it...
+ */
+ _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
+ _cpu_ppc_store_hdecr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
+ cpu_ppc_store_purr(env, 0x0000000000000000ULL);
+}
+
+/* Set up (once) timebase frequency (in Hz) */
+clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
+{
+ ppc_tb_t *tb_env;
+
+ tb_env = qemu_mallocz(sizeof(ppc_tb_t));
+ env->tb_env = tb_env;
+ /* Create new timer */
+ tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
+ if (0) {
+ /* XXX: find a suitable condition to enable the hypervisor decrementer
+ */
+ tb_env->hdecr_timer = qemu_new_timer(vm_clock, &cpu_ppc_hdecr_cb, env);
+ } else {
+ tb_env->hdecr_timer = NULL;
+ }
+ cpu_ppc_set_tb_clk(env, freq);
+
+ return &cpu_ppc_set_tb_clk;
+}
+
+/* Specific helpers for POWER & PowerPC 601 RTC */
+#if 0
+static clk_setup_cb cpu_ppc601_rtc_init (CPUState *env)
+{
+ return cpu_ppc_tb_init(env, 7812500);
+}
+#endif
+
+void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value)
+{
+ _cpu_ppc_store_tbu(env, value);
+}
+
+uint32_t cpu_ppc601_load_rtcu (CPUState *env)
+{
+ return _cpu_ppc_load_tbu(env);
+}
+
+void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value)
+{
+ cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
+}
+
+uint32_t cpu_ppc601_load_rtcl (CPUState *env)
+{
+ return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
+}
+
+/*****************************************************************************/
+/* Embedded PowerPC timers */
+
+/* PIT, FIT & WDT */
+typedef struct ppcemb_timer_t ppcemb_timer_t;
+struct ppcemb_timer_t {
+ uint64_t pit_reload; /* PIT auto-reload value */
+ uint64_t fit_next; /* Tick for next FIT interrupt */
+ struct QEMUTimer *fit_timer;
+ uint64_t wdt_next; /* Tick for next WDT interrupt */
+ struct QEMUTimer *wdt_timer;
+
+ /* 405 have the PIT, 440 have a DECR. */
+ unsigned int decr_excp;
+};
+
+/* Fixed interval timer */
+static void cpu_4xx_fit_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
+ case 0:
+ next = 1 << 9;
+ break;
+ case 1:
+ next = 1 << 13;
+ break;
+ case 2:
+ next = 1 << 17;
+ break;
+ case 3:
+ next = 1 << 21;
+ break;
+ default:
+ /* Cannot occur, but makes gcc happy */
+ return;
+ }
+ next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
+ if (next == now)
+ next++;
+ qemu_mod_timer(ppcemb_timer->fit_timer, next);
+ env->spr[SPR_40x_TSR] |= 1 << 26;
+ if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
+ ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
+ LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
+ (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1),
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
+}
+
+/* Programmable interval timer */
+static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
+{
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ ppcemb_timer = tb_env->opaque;
+ if (ppcemb_timer->pit_reload <= 1 ||
+ !((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
+ (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
+ /* Stop PIT */
+ LOG_TB("%s: stop PIT\n", __func__);
+ qemu_del_timer(tb_env->decr_timer);
+ } else {
+ LOG_TB("%s: start PIT %016" PRIx64 "\n",
+ __func__, ppcemb_timer->pit_reload);
+ now = qemu_get_clock(vm_clock);
+ next = now + muldiv64(ppcemb_timer->pit_reload,
+ get_ticks_per_sec(), tb_env->decr_freq);
+ if (is_excp)
+ next += tb_env->decr_next - now;
+ if (next == now)
+ next++;
+ qemu_mod_timer(tb_env->decr_timer, next);
+ tb_env->decr_next = next;
+ }
+}
+
+static void cpu_4xx_pit_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ env->spr[SPR_40x_TSR] |= 1 << 27;
+ if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
+ ppc_set_irq(env, ppcemb_timer->decr_excp, 1);
+ start_stop_pit(env, tb_env, 1);
+ LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
+ "%016" PRIx64 "\n", __func__,
+ (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
+ (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
+ ppcemb_timer->pit_reload);
+}
+
+/* Watchdog timer */
+static void cpu_4xx_wdt_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
+ case 0:
+ next = 1 << 17;
+ break;
+ case 1:
+ next = 1 << 21;
+ break;
+ case 2:
+ next = 1 << 25;
+ break;
+ case 3:
+ next = 1 << 29;
+ break;
+ default:
+ /* Cannot occur, but makes gcc happy */
+ return;
+ }
+ next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq);
+ if (next == now)
+ next++;
+ LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
+ switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
+ case 0x0:
+ case 0x1:
+ qemu_mod_timer(ppcemb_timer->wdt_timer, next);
+ ppcemb_timer->wdt_next = next;
+ env->spr[SPR_40x_TSR] |= 1 << 31;
+ break;
+ case 0x2:
+ qemu_mod_timer(ppcemb_timer->wdt_timer, next);
+ ppcemb_timer->wdt_next = next;
+ env->spr[SPR_40x_TSR] |= 1 << 30;
+ if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
+ ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
+ break;
+ case 0x3:
+ env->spr[SPR_40x_TSR] &= ~0x30000000;
+ env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
+ switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
+ case 0x0:
+ /* No reset */
+ break;
+ case 0x1: /* Core reset */
+ ppc40x_core_reset(env);
+ break;
+ case 0x2: /* Chip reset */
+ ppc40x_chip_reset(env);
+ break;
+ case 0x3: /* System reset */
+ ppc40x_system_reset(env);
+ break;
+ }
+ }
+}
+
+void store_40x_pit (CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
+ ppcemb_timer->pit_reload = val;
+ start_stop_pit(env, tb_env, 0);
+}
+
+target_ulong load_40x_pit (CPUState *env)
+{
+ return cpu_ppc_load_decr(env);
+}
+
+void store_booke_tsr (CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ ppcemb_timer = tb_env->opaque;
+
+ LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
+ env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
+ if (val & 0x80000000)
+ ppc_set_irq(env, ppcemb_timer->decr_excp, 0);
+}
+
+void store_booke_tcr (CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env;
+
+ tb_env = env->tb_env;
+ LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
+ env->spr[SPR_40x_TCR] = val & 0xFFC00000;
+ start_stop_pit(env, tb_env, 1);
+ cpu_4xx_wdt_cb(env);
+}
+
+static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
+{
+ CPUState *env = opaque;
+ ppc_tb_t *tb_env = env->tb_env;
+
+ LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__,
+ freq);
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ /* XXX: we should also update all timers */
+}
+
+clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+ unsigned int decr_excp)
+{
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ tb_env = qemu_mallocz(sizeof(ppc_tb_t));
+ env->tb_env = tb_env;
+ ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ tb_env->opaque = ppcemb_timer;
+ LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
+ if (ppcemb_timer != NULL) {
+ /* We use decr timer for PIT */
+ tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env);
+ ppcemb_timer->fit_timer =
+ qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
+ ppcemb_timer->wdt_timer =
+ qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
+ ppcemb_timer->decr_excp = decr_excp;
+ }
+
+ return &ppc_emb_set_tb_clk;
+}
+
+/*****************************************************************************/
+/* Embedded PowerPC Device Control Registers */
+typedef struct ppc_dcrn_t ppc_dcrn_t;
+struct ppc_dcrn_t {
+ dcr_read_cb dcr_read;
+ dcr_write_cb dcr_write;
+ void *opaque;
+};
+
+/* XXX: on 460, DCR addresses are 32 bits wide,
+ * using DCRIPR to get the 22 upper bits of the DCR address
+ */
+#define DCRN_NB 1024
+struct ppc_dcr_t {
+ ppc_dcrn_t dcrn[DCRN_NB];
+ int (*read_error)(int dcrn);
+ int (*write_error)(int dcrn);
+};
+
+int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
+{
+ ppc_dcrn_t *dcr;
+
+ if (dcrn < 0 || dcrn >= DCRN_NB)
+ goto error;
+ dcr = &dcr_env->dcrn[dcrn];
+ if (dcr->dcr_read == NULL)
+ goto error;
+ *valp = (*dcr->dcr_read)(dcr->opaque, dcrn);
+
+ return 0;
+
+ error:
+ if (dcr_env->read_error != NULL)
+ return (*dcr_env->read_error)(dcrn);
+
+ return -1;
+}
+
+int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
+{
+ ppc_dcrn_t *dcr;
+
+ if (dcrn < 0 || dcrn >= DCRN_NB)
+ goto error;
+ dcr = &dcr_env->dcrn[dcrn];
+ if (dcr->dcr_write == NULL)
+ goto error;
+ (*dcr->dcr_write)(dcr->opaque, dcrn, val);
+
+ return 0;
+
+ error:
+ if (dcr_env->write_error != NULL)
+ return (*dcr_env->write_error)(dcrn);
+
+ return -1;
+}
+
+int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
+ dcr_read_cb dcr_read, dcr_write_cb dcr_write)
+{
+ ppc_dcr_t *dcr_env;
+ ppc_dcrn_t *dcr;
+
+ dcr_env = env->dcr_env;
+ if (dcr_env == NULL)
+ return -1;
+ if (dcrn < 0 || dcrn >= DCRN_NB)
+ return -1;
+ dcr = &dcr_env->dcrn[dcrn];
+ if (dcr->opaque != NULL ||
+ dcr->dcr_read != NULL ||
+ dcr->dcr_write != NULL)
+ return -1;
+ dcr->opaque = opaque;
+ dcr->dcr_read = dcr_read;
+ dcr->dcr_write = dcr_write;
+
+ return 0;
+}
+
+int ppc_dcr_init (CPUState *env, int (*read_error)(int dcrn),
+ int (*write_error)(int dcrn))
+{
+ ppc_dcr_t *dcr_env;
+
+ dcr_env = qemu_mallocz(sizeof(ppc_dcr_t));
+ dcr_env->read_error = read_error;
+ dcr_env->write_error = write_error;
+ env->dcr_env = dcr_env;
+
+ return 0;
+}
+
+/*****************************************************************************/
+/* Debug port */
+void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ addr &= 0xF;
+ switch (addr) {
+ case 0:
+ printf("%c", val);
+ break;
+ case 1:
+ printf("\n");
+ fflush(stdout);
+ break;
+ case 2:
+ printf("Set loglevel to %04" PRIx32 "\n", val);
+ cpu_set_log(val | 0x100);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* NVRAM helpers */
+static inline uint32_t nvram_read (nvram_t *nvram, uint32_t addr)
+{
+ return (*nvram->read_fn)(nvram->opaque, addr);;
+}
+
+static inline void nvram_write (nvram_t *nvram, uint32_t addr, uint32_t val)
+{
+ (*nvram->write_fn)(nvram->opaque, addr, val);
+}
+
+void NVRAM_set_byte (nvram_t *nvram, uint32_t addr, uint8_t value)
+{
+ nvram_write(nvram, addr, value);
+}
+
+uint8_t NVRAM_get_byte (nvram_t *nvram, uint32_t addr)
+{
+ return nvram_read(nvram, addr);
+}
+
+void NVRAM_set_word (nvram_t *nvram, uint32_t addr, uint16_t value)
+{
+ nvram_write(nvram, addr, value >> 8);
+ nvram_write(nvram, addr + 1, value & 0xFF);
+}
+
+uint16_t NVRAM_get_word (nvram_t *nvram, uint32_t addr)
+{
+ uint16_t tmp;
+
+ tmp = nvram_read(nvram, addr) << 8;
+ tmp |= nvram_read(nvram, addr + 1);
+
+ return tmp;
+}
+
+void NVRAM_set_lword (nvram_t *nvram, uint32_t addr, uint32_t value)
+{
+ nvram_write(nvram, addr, value >> 24);
+ nvram_write(nvram, addr + 1, (value >> 16) & 0xFF);
+ nvram_write(nvram, addr + 2, (value >> 8) & 0xFF);
+ nvram_write(nvram, addr + 3, value & 0xFF);
+}
+
+uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr)
+{
+ uint32_t tmp;
+
+ tmp = nvram_read(nvram, addr) << 24;
+ tmp |= nvram_read(nvram, addr + 1) << 16;
+ tmp |= nvram_read(nvram, addr + 2) << 8;
+ tmp |= nvram_read(nvram, addr + 3);
+
+ return tmp;
+}
+
+void NVRAM_set_string (nvram_t *nvram, uint32_t addr,
+ const char *str, uint32_t max)
+{
+ int i;
+
+ for (i = 0; i < max && str[i] != '\0'; i++) {
+ nvram_write(nvram, addr + i, str[i]);
+ }
+ nvram_write(nvram, addr + i, str[i]);
+ nvram_write(nvram, addr + max - 1, '\0');
+}
+
+int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max)
+{
+ int i;
+
+ memset(dst, 0, max);
+ for (i = 0; i < max; i++) {
+ dst[i] = NVRAM_get_byte(nvram, addr + i);
+ if (dst[i] == '\0')
+ break;
+ }
+
+ return i;
+}
+
+static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
+{
+ uint16_t tmp;
+ uint16_t pd, pd1, pd2;
+
+ tmp = prev >> 8;
+ pd = prev ^ value;
+ pd1 = pd & 0x000F;
+ pd2 = ((pd >> 4) & 0x000F) ^ pd1;
+ tmp ^= (pd1 << 3) | (pd1 << 8);
+ tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
+
+ return tmp;
+}
+
+static uint16_t NVRAM_compute_crc (nvram_t *nvram, uint32_t start, uint32_t count)
+{
+ uint32_t i;
+ uint16_t crc = 0xFFFF;
+ int odd;
+
+ odd = count & 1;
+ count &= ~1;
+ for (i = 0; i != count; i++) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
+ }
+ if (odd) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
+ }
+
+ return crc;
+}
+
+#define CMDLINE_ADDR 0x017ff000
+
+int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size,
+ const char *arch,
+ uint32_t RAM_size, int boot_device,
+ uint32_t kernel_image, uint32_t kernel_size,
+ const char *cmdline,
+ uint32_t initrd_image, uint32_t initrd_size,
+ uint32_t NVRAM_image,
+ int width, int height, int depth)
+{
+ uint16_t crc;
+
+ /* Set parameters for Open Hack'Ware BIOS */
+ NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
+ NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */
+ NVRAM_set_word(nvram, 0x14, NVRAM_size);
+ NVRAM_set_string(nvram, 0x20, arch, 16);
+ NVRAM_set_lword(nvram, 0x30, RAM_size);
+ NVRAM_set_byte(nvram, 0x34, boot_device);
+ NVRAM_set_lword(nvram, 0x38, kernel_image);
+ NVRAM_set_lword(nvram, 0x3C, kernel_size);
+ if (cmdline) {
+ /* XXX: put the cmdline in NVRAM too ? */
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, cmdline);
+ NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR);
+ NVRAM_set_lword(nvram, 0x44, strlen(cmdline));
+ } else {
+ NVRAM_set_lword(nvram, 0x40, 0);
+ NVRAM_set_lword(nvram, 0x44, 0);
+ }
+ NVRAM_set_lword(nvram, 0x48, initrd_image);
+ NVRAM_set_lword(nvram, 0x4C, initrd_size);
+ NVRAM_set_lword(nvram, 0x50, NVRAM_image);
+
+ NVRAM_set_word(nvram, 0x54, width);
+ NVRAM_set_word(nvram, 0x56, height);
+ NVRAM_set_word(nvram, 0x58, depth);
+ crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
+ NVRAM_set_word(nvram, 0xFC, crc);
+
+ return 0;
+}
diff --git a/hw/ppc.h b/hw/ppc.h
new file mode 100644
index 0000000..34f54cf
--- /dev/null
+++ b/hw/ppc.h
@@ -0,0 +1,56 @@
+/* PowerPC hardware exceptions management helpers */
+typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
+typedef struct clk_setup_t clk_setup_t;
+struct clk_setup_t {
+ clk_setup_cb cb;
+ void *opaque;
+};
+static inline void clk_setup (clk_setup_t *clk, uint32_t freq)
+{
+ if (clk->cb != NULL)
+ (*clk->cb)(clk->opaque, freq);
+}
+
+clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq);
+/* Embedded PowerPC DCR management */
+typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn);
+typedef void (*dcr_write_cb)(void *opaque, int dcrn, uint32_t val);
+int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn),
+ int (*dcr_write_error)(int dcrn));
+int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
+ dcr_read_cb drc_read, dcr_write_cb dcr_write);
+clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+ unsigned int decr_excp);
+
+/* Embedded PowerPC reset */
+void ppc40x_core_reset (CPUState *env);
+void ppc40x_chip_reset (CPUState *env);
+void ppc40x_system_reset (CPUState *env);
+void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val);
+
+extern CPUWriteMemoryFunc * const PPC_io_write[];
+extern CPUReadMemoryFunc * const PPC_io_read[];
+void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val);
+
+void ppc40x_irq_init (CPUState *env);
+void ppce500_irq_init (CPUState *env);
+void ppc6xx_irq_init (CPUState *env);
+void ppc970_irq_init (CPUState *env);
+
+/* PPC machines for OpenBIOS */
+enum {
+ ARCH_PREP = 0,
+ ARCH_MAC99,
+ ARCH_HEATHROW,
+ ARCH_MAC99_U3,
+};
+
+#define FW_CFG_PPC_WIDTH (FW_CFG_ARCH_LOCAL + 0x00)
+#define FW_CFG_PPC_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
+#define FW_CFG_PPC_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
+#define FW_CFG_PPC_TBFREQ (FW_CFG_ARCH_LOCAL + 0x03)
+#define FW_CFG_PPC_IS_KVM (FW_CFG_ARCH_LOCAL + 0x05)
+#define FW_CFG_PPC_KVM_HC (FW_CFG_ARCH_LOCAL + 0x06)
+#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07)
+
+#define PPC_SERIAL_MM_BAUDBASE 399193
diff --git a/hw/ppc405.h b/hw/ppc405.h
new file mode 100644
index 0000000..e042a05
--- /dev/null
+++ b/hw/ppc405.h
@@ -0,0 +1,76 @@
+/*
+ * QEMU PowerPC 405 shared definitions
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if !defined(PPC_405_H)
+#define PPC_405_H
+
+#include "ppc4xx.h"
+
+/* Bootinfo as set-up by u-boot */
+typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t;
+struct ppc4xx_bd_info_t {
+ uint32_t bi_memstart;
+ uint32_t bi_memsize;
+ uint32_t bi_flashstart;
+ uint32_t bi_flashsize;
+ uint32_t bi_flashoffset; /* 0x10 */
+ uint32_t bi_sramstart;
+ uint32_t bi_sramsize;
+ uint32_t bi_bootflags;
+ uint32_t bi_ipaddr; /* 0x20 */
+ uint8_t bi_enetaddr[6];
+ uint16_t bi_ethspeed;
+ uint32_t bi_intfreq;
+ uint32_t bi_busfreq; /* 0x30 */
+ uint32_t bi_baudrate;
+ uint8_t bi_s_version[4];
+ uint8_t bi_r_version[32];
+ uint32_t bi_procfreq;
+ uint32_t bi_plb_busfreq;
+ uint32_t bi_pci_busfreq;
+ uint8_t bi_pci_enetaddr[6];
+ uint32_t bi_pci_enetaddr2[6];
+ uint32_t bi_opbfreq;
+ uint32_t bi_iic_fast[2];
+};
+
+/* PowerPC 405 core */
+ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
+ uint32_t flags);
+
+CPUState *ppc405cr_init (target_phys_addr_t ram_bases[4],
+ target_phys_addr_t ram_sizes[4],
+ uint32_t sysclk, qemu_irq **picp,
+ int do_init);
+CPUState *ppc405ep_init (target_phys_addr_t ram_bases[2],
+ target_phys_addr_t ram_sizes[2],
+ uint32_t sysclk, qemu_irq **picp,
+ int do_init);
+/* IBM STBxxx microcontrollers */
+CPUState *ppc_stb025_init (target_phys_addr_t ram_bases[2],
+ target_phys_addr_t ram_sizes[2],
+ uint32_t sysclk, qemu_irq **picp,
+ ram_addr_t *offsetp);
+
+#endif /* !defined(PPC_405_H) */
diff --git a/hw/ppc405_boards.c b/hw/ppc405_boards.c
new file mode 100644
index 0000000..9abede7
--- /dev/null
+++ b/hw/ppc405_boards.c
@@ -0,0 +1,653 @@
+/*
+ * QEMU PowerPC 405 evaluation boards emulation
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "ppc405.h"
+#include "nvram.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "block.h"
+#include "boards.h"
+#include "qemu-log.h"
+#include "loader.h"
+#include "blockdev.h"
+
+#define BIOS_FILENAME "ppc405_rom.bin"
+#define BIOS_SIZE (2048 * 1024)
+
+#define KERNEL_LOAD_ADDR 0x00000000
+#define INITRD_LOAD_ADDR 0x01800000
+
+#define USE_FLASH_BIOS
+
+#define DEBUG_BOARD_INIT
+
+/*****************************************************************************/
+/* PPC405EP reference board (IBM) */
+/* Standalone board with:
+ * - PowerPC 405EP CPU
+ * - SDRAM (0x00000000)
+ * - Flash (0xFFF80000)
+ * - SRAM (0xFFF00000)
+ * - NVRAM (0xF0000000)
+ * - FPGA (0xF0300000)
+ */
+typedef struct ref405ep_fpga_t ref405ep_fpga_t;
+struct ref405ep_fpga_t {
+ uint8_t reg0;
+ uint8_t reg1;
+};
+
+static uint32_t ref405ep_fpga_readb (void *opaque, target_phys_addr_t addr)
+{
+ ref405ep_fpga_t *fpga;
+ uint32_t ret;
+
+ fpga = opaque;
+ switch (addr) {
+ case 0x0:
+ ret = fpga->reg0;
+ break;
+ case 0x1:
+ ret = fpga->reg1;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void ref405ep_fpga_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ref405ep_fpga_t *fpga;
+
+ fpga = opaque;
+ switch (addr) {
+ case 0x0:
+ /* Read only */
+ break;
+ case 0x1:
+ fpga->reg1 = value;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t ref405ep_fpga_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ ret = ref405ep_fpga_readb(opaque, addr) << 8;
+ ret |= ref405ep_fpga_readb(opaque, addr + 1);
+
+ return ret;
+}
+
+static void ref405ep_fpga_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ref405ep_fpga_writeb(opaque, addr, (value >> 8) & 0xFF);
+ ref405ep_fpga_writeb(opaque, addr + 1, value & 0xFF);
+}
+
+static uint32_t ref405ep_fpga_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ ret = ref405ep_fpga_readb(opaque, addr) << 24;
+ ret |= ref405ep_fpga_readb(opaque, addr + 1) << 16;
+ ret |= ref405ep_fpga_readb(opaque, addr + 2) << 8;
+ ret |= ref405ep_fpga_readb(opaque, addr + 3);
+
+ return ret;
+}
+
+static void ref405ep_fpga_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ref405ep_fpga_writeb(opaque, addr, (value >> 24) & 0xFF);
+ ref405ep_fpga_writeb(opaque, addr + 1, (value >> 16) & 0xFF);
+ ref405ep_fpga_writeb(opaque, addr + 2, (value >> 8) & 0xFF);
+ ref405ep_fpga_writeb(opaque, addr + 3, value & 0xFF);
+}
+
+static CPUReadMemoryFunc * const ref405ep_fpga_read[] = {
+ &ref405ep_fpga_readb,
+ &ref405ep_fpga_readw,
+ &ref405ep_fpga_readl,
+};
+
+static CPUWriteMemoryFunc * const ref405ep_fpga_write[] = {
+ &ref405ep_fpga_writeb,
+ &ref405ep_fpga_writew,
+ &ref405ep_fpga_writel,
+};
+
+static void ref405ep_fpga_reset (void *opaque)
+{
+ ref405ep_fpga_t *fpga;
+
+ fpga = opaque;
+ fpga->reg0 = 0x00;
+ fpga->reg1 = 0x0F;
+}
+
+static void ref405ep_fpga_init (uint32_t base)
+{
+ ref405ep_fpga_t *fpga;
+ int fpga_memory;
+
+ fpga = qemu_mallocz(sizeof(ref405ep_fpga_t));
+ fpga_memory = cpu_register_io_memory(ref405ep_fpga_read,
+ ref405ep_fpga_write, fpga,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00000100, fpga_memory);
+ qemu_register_reset(&ref405ep_fpga_reset, fpga);
+}
+
+static void ref405ep_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ char *filename;
+ ppc4xx_bd_info_t bd;
+ CPUPPCState *env;
+ qemu_irq *pic;
+ ram_addr_t sram_offset, bios_offset, bdloc;
+ target_phys_addr_t ram_bases[2], ram_sizes[2];
+ target_ulong sram_size;
+ long bios_size;
+ //int phy_addr = 0;
+ //static int phy_addr = 1;
+ target_ulong kernel_base, initrd_base;
+ long kernel_size, initrd_size;
+ int linux_boot;
+ int fl_idx, fl_sectors, len;
+ DriveInfo *dinfo;
+
+ /* XXX: fix this */
+ ram_bases[0] = qemu_ram_alloc(NULL, "ef405ep.ram", 0x08000000);
+ ram_sizes[0] = 0x08000000;
+ ram_bases[1] = 0x00000000;
+ ram_sizes[1] = 0x00000000;
+ ram_size = 128 * 1024 * 1024;
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register cpu\n", __func__);
+#endif
+ env = ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic,
+ kernel_filename == NULL ? 0 : 1);
+ /* allocate SRAM */
+ sram_size = 512 * 1024;
+ sram_offset = qemu_ram_alloc(NULL, "ef405ep.sram", sram_size);
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset);
+#endif
+ cpu_register_physical_memory(0xFFF00000, sram_size,
+ sram_offset | IO_MEM_RAM);
+ /* allocate and load BIOS */
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register BIOS\n", __func__);
+#endif
+ fl_idx = 0;
+#ifdef USE_FLASH_BIOS
+ dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+ if (dinfo) {
+ bios_size = bdrv_getlength(dinfo->bdrv);
+ bios_offset = qemu_ram_alloc(NULL, "ef405ep.bios", bios_size);
+ fl_sectors = (bios_size + 65535) >> 16;
+#ifdef DEBUG_BOARD_INIT
+ printf("Register parallel flash %d size %lx"
+ " at offset %08lx addr %lx '%s' %d\n",
+ fl_idx, bios_size, bios_offset, -bios_size,
+ bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+#endif
+ pflash_cfi02_register((uint32_t)(-bios_size), bios_offset,
+ dinfo->bdrv, 65536, fl_sectors, 1,
+ 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
+ 1);
+ fl_idx++;
+ } else
+#endif
+ {
+#ifdef DEBUG_BOARD_INIT
+ printf("Load BIOS from file\n");
+#endif
+ bios_offset = qemu_ram_alloc(NULL, "ef405ep.bios", BIOS_SIZE);
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image(filename, qemu_get_ram_ptr(bios_offset));
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n",
+ bios_name);
+ exit(1);
+ }
+ bios_size = (bios_size + 0xfff) & ~0xfff;
+ cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+ }
+ /* Register FPGA */
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register FPGA\n", __func__);
+#endif
+ ref405ep_fpga_init(0xF0300000);
+ /* Register NVRAM */
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register NVRAM\n", __func__);
+#endif
+ m48t59_init(NULL, 0xF0000000, 0, 8192, 8);
+ /* Load kernel */
+ linux_boot = (kernel_filename != NULL);
+ if (linux_boot) {
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: load kernel\n", __func__);
+#endif
+ memset(&bd, 0, sizeof(bd));
+ bd.bi_memstart = 0x00000000;
+ bd.bi_memsize = ram_size;
+ bd.bi_flashstart = -bios_size;
+ bd.bi_flashsize = -bios_size;
+ bd.bi_flashoffset = 0;
+ bd.bi_sramstart = 0xFFF00000;
+ bd.bi_sramsize = sram_size;
+ bd.bi_bootflags = 0;
+ bd.bi_intfreq = 133333333;
+ bd.bi_busfreq = 33333333;
+ bd.bi_baudrate = 115200;
+ bd.bi_s_version[0] = 'Q';
+ bd.bi_s_version[1] = 'M';
+ bd.bi_s_version[2] = 'U';
+ bd.bi_s_version[3] = '\0';
+ bd.bi_r_version[0] = 'Q';
+ bd.bi_r_version[1] = 'E';
+ bd.bi_r_version[2] = 'M';
+ bd.bi_r_version[3] = 'U';
+ bd.bi_r_version[4] = '\0';
+ bd.bi_procfreq = 133333333;
+ bd.bi_plb_busfreq = 33333333;
+ bd.bi_pci_busfreq = 33333333;
+ bd.bi_opbfreq = 33333333;
+ bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001);
+ env->gpr[3] = bdloc;
+ kernel_base = KERNEL_LOAD_ADDR;
+ /* now we can load the kernel */
+ kernel_size = load_image_targphys(kernel_filename, kernel_base,
+ ram_size - kernel_base);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ printf("Load kernel size %ld at " TARGET_FMT_lx,
+ kernel_size, kernel_base);
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ env->gpr[4] = initrd_base;
+ env->gpr[5] = initrd_size;
+ if (kernel_cmdline != NULL) {
+ len = strlen(kernel_cmdline);
+ bdloc -= ((len + 255) & ~255);
+ cpu_physical_memory_write(bdloc, (void *)kernel_cmdline, len + 1);
+ env->gpr[6] = bdloc;
+ env->gpr[7] = bdloc + len;
+ } else {
+ env->gpr[6] = 0;
+ env->gpr[7] = 0;
+ }
+ env->nip = KERNEL_LOAD_ADDR;
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ bdloc = 0;
+ }
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: Done\n", __func__);
+#endif
+ printf("bdloc %016lx\n", (unsigned long)bdloc);
+}
+
+static QEMUMachine ref405ep_machine = {
+ .name = "ref405ep",
+ .desc = "ref405ep",
+ .init = ref405ep_init,
+};
+
+/*****************************************************************************/
+/* AMCC Taihu evaluation board */
+/* - PowerPC 405EP processor
+ * - SDRAM 128 MB at 0x00000000
+ * - Boot flash 2 MB at 0xFFE00000
+ * - Application flash 32 MB at 0xFC000000
+ * - 2 serial ports
+ * - 2 ethernet PHY
+ * - 1 USB 1.1 device 0x50000000
+ * - 1 LCD display 0x50100000
+ * - 1 CPLD 0x50100000
+ * - 1 I2C EEPROM
+ * - 1 I2C thermal sensor
+ * - a set of LEDs
+ * - bit-bang SPI port using GPIOs
+ * - 1 EBC interface connector 0 0x50200000
+ * - 1 cardbus controller + expansion slot.
+ * - 1 PCI expansion slot.
+ */
+typedef struct taihu_cpld_t taihu_cpld_t;
+struct taihu_cpld_t {
+ uint8_t reg0;
+ uint8_t reg1;
+};
+
+static uint32_t taihu_cpld_readb (void *opaque, target_phys_addr_t addr)
+{
+ taihu_cpld_t *cpld;
+ uint32_t ret;
+
+ cpld = opaque;
+ switch (addr) {
+ case 0x0:
+ ret = cpld->reg0;
+ break;
+ case 0x1:
+ ret = cpld->reg1;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void taihu_cpld_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ taihu_cpld_t *cpld;
+
+ cpld = opaque;
+ switch (addr) {
+ case 0x0:
+ /* Read only */
+ break;
+ case 0x1:
+ cpld->reg1 = value;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t taihu_cpld_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ ret = taihu_cpld_readb(opaque, addr) << 8;
+ ret |= taihu_cpld_readb(opaque, addr + 1);
+
+ return ret;
+}
+
+static void taihu_cpld_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ taihu_cpld_writeb(opaque, addr, (value >> 8) & 0xFF);
+ taihu_cpld_writeb(opaque, addr + 1, value & 0xFF);
+}
+
+static uint32_t taihu_cpld_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+ ret = taihu_cpld_readb(opaque, addr) << 24;
+ ret |= taihu_cpld_readb(opaque, addr + 1) << 16;
+ ret |= taihu_cpld_readb(opaque, addr + 2) << 8;
+ ret |= taihu_cpld_readb(opaque, addr + 3);
+
+ return ret;
+}
+
+static void taihu_cpld_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ taihu_cpld_writel(opaque, addr, (value >> 24) & 0xFF);
+ taihu_cpld_writel(opaque, addr + 1, (value >> 16) & 0xFF);
+ taihu_cpld_writel(opaque, addr + 2, (value >> 8) & 0xFF);
+ taihu_cpld_writeb(opaque, addr + 3, value & 0xFF);
+}
+
+static CPUReadMemoryFunc * const taihu_cpld_read[] = {
+ &taihu_cpld_readb,
+ &taihu_cpld_readw,
+ &taihu_cpld_readl,
+};
+
+static CPUWriteMemoryFunc * const taihu_cpld_write[] = {
+ &taihu_cpld_writeb,
+ &taihu_cpld_writew,
+ &taihu_cpld_writel,
+};
+
+static void taihu_cpld_reset (void *opaque)
+{
+ taihu_cpld_t *cpld;
+
+ cpld = opaque;
+ cpld->reg0 = 0x01;
+ cpld->reg1 = 0x80;
+}
+
+static void taihu_cpld_init (uint32_t base)
+{
+ taihu_cpld_t *cpld;
+ int cpld_memory;
+
+ cpld = qemu_mallocz(sizeof(taihu_cpld_t));
+ cpld_memory = cpu_register_io_memory(taihu_cpld_read,
+ taihu_cpld_write, cpld,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00000100, cpld_memory);
+ qemu_register_reset(&taihu_cpld_reset, cpld);
+}
+
+static void taihu_405ep_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ char *filename;
+ qemu_irq *pic;
+ ram_addr_t bios_offset;
+ target_phys_addr_t ram_bases[2], ram_sizes[2];
+ long bios_size;
+ target_ulong kernel_base, initrd_base;
+ long kernel_size, initrd_size;
+ int linux_boot;
+ int fl_idx, fl_sectors;
+ DriveInfo *dinfo;
+
+ /* RAM is soldered to the board so the size cannot be changed */
+ ram_bases[0] = qemu_ram_alloc(NULL, "taihu_405ep.ram-0", 0x04000000);
+ ram_sizes[0] = 0x04000000;
+ ram_bases[1] = qemu_ram_alloc(NULL, "taihu_405ep.ram-1", 0x04000000);
+ ram_sizes[1] = 0x04000000;
+ ram_size = 0x08000000;
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register cpu\n", __func__);
+#endif
+ ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic,
+ kernel_filename == NULL ? 0 : 1);
+ /* allocate and load BIOS */
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register BIOS\n", __func__);
+#endif
+ fl_idx = 0;
+#if defined(USE_FLASH_BIOS)
+ dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+ if (dinfo) {
+ bios_size = bdrv_getlength(dinfo->bdrv);
+ /* XXX: should check that size is 2MB */
+ // bios_size = 2 * 1024 * 1024;
+ fl_sectors = (bios_size + 65535) >> 16;
+ bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.bios", bios_size);
+#ifdef DEBUG_BOARD_INIT
+ printf("Register parallel flash %d size %lx"
+ " at offset %08lx addr %lx '%s' %d\n",
+ fl_idx, bios_size, bios_offset, -bios_size,
+ bdrv_get_device_name(dinfo->bdrv), fl_sectors);
+#endif
+ pflash_cfi02_register((uint32_t)(-bios_size), bios_offset,
+ dinfo->bdrv, 65536, fl_sectors, 1,
+ 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
+ 1);
+ fl_idx++;
+ } else
+#endif
+ {
+#ifdef DEBUG_BOARD_INIT
+ printf("Load BIOS from file\n");
+#endif
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.bios", BIOS_SIZE);
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = load_image(filename, qemu_get_ram_ptr(bios_offset));
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n",
+ bios_name);
+ exit(1);
+ }
+ bios_size = (bios_size + 0xfff) & ~0xfff;
+ cpu_register_physical_memory((uint32_t)(-bios_size),
+ bios_size, bios_offset | IO_MEM_ROM);
+ }
+ /* Register Linux flash */
+ dinfo = drive_get(IF_PFLASH, 0, fl_idx);
+ if (dinfo) {
+ bios_size = bdrv_getlength(dinfo->bdrv);
+ /* XXX: should check that size is 32MB */
+ bios_size = 32 * 1024 * 1024;
+ fl_sectors = (bios_size + 65535) >> 16;
+#ifdef DEBUG_BOARD_INIT
+ printf("Register parallel flash %d size %lx"
+ " at offset %08lx addr " TARGET_FMT_lx " '%s'\n",
+ fl_idx, bios_size, bios_offset, (target_ulong)0xfc000000,
+ bdrv_get_device_name(dinfo->bdrv));
+#endif
+ bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.flash", bios_size);
+ pflash_cfi02_register(0xfc000000, bios_offset,
+ dinfo->bdrv, 65536, fl_sectors, 1,
+ 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
+ 1);
+ fl_idx++;
+ }
+ /* Register CLPD & LCD display */
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: register CPLD\n", __func__);
+#endif
+ taihu_cpld_init(0x50100000);
+ /* Load kernel */
+ linux_boot = (kernel_filename != NULL);
+ if (linux_boot) {
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: load kernel\n", __func__);
+#endif
+ kernel_base = KERNEL_LOAD_ADDR;
+ /* now we can load the kernel */
+ kernel_size = load_image_targphys(kernel_filename, kernel_base,
+ ram_size - kernel_base);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size < 0) {
+ fprintf(stderr,
+ "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+#ifdef DEBUG_BOARD_INIT
+ printf("%s: Done\n", __func__);
+#endif
+}
+
+static QEMUMachine taihu_machine = {
+ .name = "taihu",
+ .desc = "taihu",
+ .init = taihu_405ep_init,
+};
+
+static void ppc405_machine_init(void)
+{
+ qemu_register_machine(&ref405ep_machine);
+ qemu_register_machine(&taihu_machine);
+}
+
+machine_init(ppc405_machine_init);
diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c
new file mode 100644
index 0000000..334187e
--- /dev/null
+++ b/hw/ppc405_uc.c
@@ -0,0 +1,2544 @@
+/*
+ * QEMU PowerPC 405 embedded processors emulation
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "ppc405.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "qemu-log.h"
+
+#define DEBUG_OPBA
+#define DEBUG_SDRAM
+#define DEBUG_GPIO
+#define DEBUG_SERIAL
+#define DEBUG_OCM
+//#define DEBUG_I2C
+#define DEBUG_GPT
+#define DEBUG_MAL
+#define DEBUG_CLOCKS
+//#define DEBUG_CLOCKS_LL
+
+ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
+ uint32_t flags)
+{
+ ram_addr_t bdloc;
+ int i, n;
+
+ /* We put the bd structure at the top of memory */
+ if (bd->bi_memsize >= 0x01000000UL)
+ bdloc = 0x01000000UL - sizeof(struct ppc4xx_bd_info_t);
+ else
+ bdloc = bd->bi_memsize - sizeof(struct ppc4xx_bd_info_t);
+ stl_phys(bdloc + 0x00, bd->bi_memstart);
+ stl_phys(bdloc + 0x04, bd->bi_memsize);
+ stl_phys(bdloc + 0x08, bd->bi_flashstart);
+ stl_phys(bdloc + 0x0C, bd->bi_flashsize);
+ stl_phys(bdloc + 0x10, bd->bi_flashoffset);
+ stl_phys(bdloc + 0x14, bd->bi_sramstart);
+ stl_phys(bdloc + 0x18, bd->bi_sramsize);
+ stl_phys(bdloc + 0x1C, bd->bi_bootflags);
+ stl_phys(bdloc + 0x20, bd->bi_ipaddr);
+ for (i = 0; i < 6; i++)
+ stb_phys(bdloc + 0x24 + i, bd->bi_enetaddr[i]);
+ stw_phys(bdloc + 0x2A, bd->bi_ethspeed);
+ stl_phys(bdloc + 0x2C, bd->bi_intfreq);
+ stl_phys(bdloc + 0x30, bd->bi_busfreq);
+ stl_phys(bdloc + 0x34, bd->bi_baudrate);
+ for (i = 0; i < 4; i++)
+ stb_phys(bdloc + 0x38 + i, bd->bi_s_version[i]);
+ for (i = 0; i < 32; i++) {
+ stb_phys(bdloc + 0x3C + i, bd->bi_r_version[i]);
+ }
+ stl_phys(bdloc + 0x5C, bd->bi_plb_busfreq);
+ stl_phys(bdloc + 0x60, bd->bi_pci_busfreq);
+ for (i = 0; i < 6; i++)
+ stb_phys(bdloc + 0x64 + i, bd->bi_pci_enetaddr[i]);
+ n = 0x6A;
+ if (flags & 0x00000001) {
+ for (i = 0; i < 6; i++)
+ stb_phys(bdloc + n++, bd->bi_pci_enetaddr2[i]);
+ }
+ stl_phys(bdloc + n, bd->bi_opbfreq);
+ n += 4;
+ for (i = 0; i < 2; i++) {
+ stl_phys(bdloc + n, bd->bi_iic_fast[i]);
+ n += 4;
+ }
+
+ return bdloc;
+}
+
+/*****************************************************************************/
+/* Shared peripherals */
+
+/*****************************************************************************/
+/* Peripheral local bus arbitrer */
+enum {
+ PLB0_BESR = 0x084,
+ PLB0_BEAR = 0x086,
+ PLB0_ACR = 0x087,
+};
+
+typedef struct ppc4xx_plb_t ppc4xx_plb_t;
+struct ppc4xx_plb_t {
+ uint32_t acr;
+ uint32_t bear;
+ uint32_t besr;
+};
+
+static uint32_t dcr_read_plb (void *opaque, int dcrn)
+{
+ ppc4xx_plb_t *plb;
+ uint32_t ret;
+
+ plb = opaque;
+ switch (dcrn) {
+ case PLB0_ACR:
+ ret = plb->acr;
+ break;
+ case PLB0_BEAR:
+ ret = plb->bear;
+ break;
+ case PLB0_BESR:
+ ret = plb->besr;
+ break;
+ default:
+ /* Avoid gcc warning */
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_plb (void *opaque, int dcrn, uint32_t val)
+{
+ ppc4xx_plb_t *plb;
+
+ plb = opaque;
+ switch (dcrn) {
+ case PLB0_ACR:
+ /* We don't care about the actual parameters written as
+ * we don't manage any priorities on the bus
+ */
+ plb->acr = val & 0xF8000000;
+ break;
+ case PLB0_BEAR:
+ /* Read only */
+ break;
+ case PLB0_BESR:
+ /* Write-clear */
+ plb->besr &= ~val;
+ break;
+ }
+}
+
+static void ppc4xx_plb_reset (void *opaque)
+{
+ ppc4xx_plb_t *plb;
+
+ plb = opaque;
+ plb->acr = 0x00000000;
+ plb->bear = 0x00000000;
+ plb->besr = 0x00000000;
+}
+
+static void ppc4xx_plb_init(CPUState *env)
+{
+ ppc4xx_plb_t *plb;
+
+ plb = qemu_mallocz(sizeof(ppc4xx_plb_t));
+ ppc_dcr_register(env, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb);
+ ppc_dcr_register(env, PLB0_BEAR, plb, &dcr_read_plb, &dcr_write_plb);
+ ppc_dcr_register(env, PLB0_BESR, plb, &dcr_read_plb, &dcr_write_plb);
+ qemu_register_reset(ppc4xx_plb_reset, plb);
+}
+
+/*****************************************************************************/
+/* PLB to OPB bridge */
+enum {
+ POB0_BESR0 = 0x0A0,
+ POB0_BESR1 = 0x0A2,
+ POB0_BEAR = 0x0A4,
+};
+
+typedef struct ppc4xx_pob_t ppc4xx_pob_t;
+struct ppc4xx_pob_t {
+ uint32_t bear;
+ uint32_t besr[2];
+};
+
+static uint32_t dcr_read_pob (void *opaque, int dcrn)
+{
+ ppc4xx_pob_t *pob;
+ uint32_t ret;
+
+ pob = opaque;
+ switch (dcrn) {
+ case POB0_BEAR:
+ ret = pob->bear;
+ break;
+ case POB0_BESR0:
+ case POB0_BESR1:
+ ret = pob->besr[dcrn - POB0_BESR0];
+ break;
+ default:
+ /* Avoid gcc warning */
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_pob (void *opaque, int dcrn, uint32_t val)
+{
+ ppc4xx_pob_t *pob;
+
+ pob = opaque;
+ switch (dcrn) {
+ case POB0_BEAR:
+ /* Read only */
+ break;
+ case POB0_BESR0:
+ case POB0_BESR1:
+ /* Write-clear */
+ pob->besr[dcrn - POB0_BESR0] &= ~val;
+ break;
+ }
+}
+
+static void ppc4xx_pob_reset (void *opaque)
+{
+ ppc4xx_pob_t *pob;
+
+ pob = opaque;
+ /* No error */
+ pob->bear = 0x00000000;
+ pob->besr[0] = 0x0000000;
+ pob->besr[1] = 0x0000000;
+}
+
+static void ppc4xx_pob_init(CPUState *env)
+{
+ ppc4xx_pob_t *pob;
+
+ pob = qemu_mallocz(sizeof(ppc4xx_pob_t));
+ ppc_dcr_register(env, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob);
+ ppc_dcr_register(env, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob);
+ ppc_dcr_register(env, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob);
+ qemu_register_reset(ppc4xx_pob_reset, pob);
+}
+
+/*****************************************************************************/
+/* OPB arbitrer */
+typedef struct ppc4xx_opba_t ppc4xx_opba_t;
+struct ppc4xx_opba_t {
+ uint8_t cr;
+ uint8_t pr;
+};
+
+static uint32_t opba_readb (void *opaque, target_phys_addr_t addr)
+{
+ ppc4xx_opba_t *opba;
+ uint32_t ret;
+
+#ifdef DEBUG_OPBA
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ opba = opaque;
+ switch (addr) {
+ case 0x00:
+ ret = opba->cr;
+ break;
+ case 0x01:
+ ret = opba->pr;
+ break;
+ default:
+ ret = 0x00;
+ break;
+ }
+
+ return ret;
+}
+
+static void opba_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ppc4xx_opba_t *opba;
+
+#ifdef DEBUG_OPBA
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ opba = opaque;
+ switch (addr) {
+ case 0x00:
+ opba->cr = value & 0xF8;
+ break;
+ case 0x01:
+ opba->pr = value & 0xFF;
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t opba_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+#ifdef DEBUG_OPBA
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ ret = opba_readb(opaque, addr) << 8;
+ ret |= opba_readb(opaque, addr + 1);
+
+ return ret;
+}
+
+static void opba_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_OPBA
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ opba_writeb(opaque, addr, value >> 8);
+ opba_writeb(opaque, addr + 1, value);
+}
+
+static uint32_t opba_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+#ifdef DEBUG_OPBA
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ ret = opba_readb(opaque, addr) << 24;
+ ret |= opba_readb(opaque, addr + 1) << 16;
+
+ return ret;
+}
+
+static void opba_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_OPBA
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ opba_writeb(opaque, addr, value >> 24);
+ opba_writeb(opaque, addr + 1, value >> 16);
+}
+
+static CPUReadMemoryFunc * const opba_read[] = {
+ &opba_readb,
+ &opba_readw,
+ &opba_readl,
+};
+
+static CPUWriteMemoryFunc * const opba_write[] = {
+ &opba_writeb,
+ &opba_writew,
+ &opba_writel,
+};
+
+static void ppc4xx_opba_reset (void *opaque)
+{
+ ppc4xx_opba_t *opba;
+
+ opba = opaque;
+ opba->cr = 0x00; /* No dynamic priorities - park disabled */
+ opba->pr = 0x11;
+}
+
+static void ppc4xx_opba_init(target_phys_addr_t base)
+{
+ ppc4xx_opba_t *opba;
+ int io;
+
+ opba = qemu_mallocz(sizeof(ppc4xx_opba_t));
+#ifdef DEBUG_OPBA
+ printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
+#endif
+ io = cpu_register_io_memory(opba_read, opba_write, opba,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x002, io);
+ qemu_register_reset(ppc4xx_opba_reset, opba);
+}
+
+/*****************************************************************************/
+/* Code decompression controller */
+/* XXX: TODO */
+
+/*****************************************************************************/
+/* Peripheral controller */
+typedef struct ppc4xx_ebc_t ppc4xx_ebc_t;
+struct ppc4xx_ebc_t {
+ uint32_t addr;
+ uint32_t bcr[8];
+ uint32_t bap[8];
+ uint32_t bear;
+ uint32_t besr0;
+ uint32_t besr1;
+ uint32_t cfg;
+};
+
+enum {
+ EBC0_CFGADDR = 0x012,
+ EBC0_CFGDATA = 0x013,
+};
+
+static uint32_t dcr_read_ebc (void *opaque, int dcrn)
+{
+ ppc4xx_ebc_t *ebc;
+ uint32_t ret;
+
+ ebc = opaque;
+ switch (dcrn) {
+ case EBC0_CFGADDR:
+ ret = ebc->addr;
+ break;
+ case EBC0_CFGDATA:
+ switch (ebc->addr) {
+ case 0x00: /* B0CR */
+ ret = ebc->bcr[0];
+ break;
+ case 0x01: /* B1CR */
+ ret = ebc->bcr[1];
+ break;
+ case 0x02: /* B2CR */
+ ret = ebc->bcr[2];
+ break;
+ case 0x03: /* B3CR */
+ ret = ebc->bcr[3];
+ break;
+ case 0x04: /* B4CR */
+ ret = ebc->bcr[4];
+ break;
+ case 0x05: /* B5CR */
+ ret = ebc->bcr[5];
+ break;
+ case 0x06: /* B6CR */
+ ret = ebc->bcr[6];
+ break;
+ case 0x07: /* B7CR */
+ ret = ebc->bcr[7];
+ break;
+ case 0x10: /* B0AP */
+ ret = ebc->bap[0];
+ break;
+ case 0x11: /* B1AP */
+ ret = ebc->bap[1];
+ break;
+ case 0x12: /* B2AP */
+ ret = ebc->bap[2];
+ break;
+ case 0x13: /* B3AP */
+ ret = ebc->bap[3];
+ break;
+ case 0x14: /* B4AP */
+ ret = ebc->bap[4];
+ break;
+ case 0x15: /* B5AP */
+ ret = ebc->bap[5];
+ break;
+ case 0x16: /* B6AP */
+ ret = ebc->bap[6];
+ break;
+ case 0x17: /* B7AP */
+ ret = ebc->bap[7];
+ break;
+ case 0x20: /* BEAR */
+ ret = ebc->bear;
+ break;
+ case 0x21: /* BESR0 */
+ ret = ebc->besr0;
+ break;
+ case 0x22: /* BESR1 */
+ ret = ebc->besr1;
+ break;
+ case 0x23: /* CFG */
+ ret = ebc->cfg;
+ break;
+ default:
+ ret = 0x00000000;
+ break;
+ }
+ break;
+ default:
+ ret = 0x00000000;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_ebc (void *opaque, int dcrn, uint32_t val)
+{
+ ppc4xx_ebc_t *ebc;
+
+ ebc = opaque;
+ switch (dcrn) {
+ case EBC0_CFGADDR:
+ ebc->addr = val;
+ break;
+ case EBC0_CFGDATA:
+ switch (ebc->addr) {
+ case 0x00: /* B0CR */
+ break;
+ case 0x01: /* B1CR */
+ break;
+ case 0x02: /* B2CR */
+ break;
+ case 0x03: /* B3CR */
+ break;
+ case 0x04: /* B4CR */
+ break;
+ case 0x05: /* B5CR */
+ break;
+ case 0x06: /* B6CR */
+ break;
+ case 0x07: /* B7CR */
+ break;
+ case 0x10: /* B0AP */
+ break;
+ case 0x11: /* B1AP */
+ break;
+ case 0x12: /* B2AP */
+ break;
+ case 0x13: /* B3AP */
+ break;
+ case 0x14: /* B4AP */
+ break;
+ case 0x15: /* B5AP */
+ break;
+ case 0x16: /* B6AP */
+ break;
+ case 0x17: /* B7AP */
+ break;
+ case 0x20: /* BEAR */
+ break;
+ case 0x21: /* BESR0 */
+ break;
+ case 0x22: /* BESR1 */
+ break;
+ case 0x23: /* CFG */
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void ebc_reset (void *opaque)
+{
+ ppc4xx_ebc_t *ebc;
+ int i;
+
+ ebc = opaque;
+ ebc->addr = 0x00000000;
+ ebc->bap[0] = 0x7F8FFE80;
+ ebc->bcr[0] = 0xFFE28000;
+ for (i = 0; i < 8; i++) {
+ ebc->bap[i] = 0x00000000;
+ ebc->bcr[i] = 0x00000000;
+ }
+ ebc->besr0 = 0x00000000;
+ ebc->besr1 = 0x00000000;
+ ebc->cfg = 0x80400000;
+}
+
+static void ppc405_ebc_init(CPUState *env)
+{
+ ppc4xx_ebc_t *ebc;
+
+ ebc = qemu_mallocz(sizeof(ppc4xx_ebc_t));
+ qemu_register_reset(&ebc_reset, ebc);
+ ppc_dcr_register(env, EBC0_CFGADDR,
+ ebc, &dcr_read_ebc, &dcr_write_ebc);
+ ppc_dcr_register(env, EBC0_CFGDATA,
+ ebc, &dcr_read_ebc, &dcr_write_ebc);
+}
+
+/*****************************************************************************/
+/* DMA controller */
+enum {
+ DMA0_CR0 = 0x100,
+ DMA0_CT0 = 0x101,
+ DMA0_DA0 = 0x102,
+ DMA0_SA0 = 0x103,
+ DMA0_SG0 = 0x104,
+ DMA0_CR1 = 0x108,
+ DMA0_CT1 = 0x109,
+ DMA0_DA1 = 0x10A,
+ DMA0_SA1 = 0x10B,
+ DMA0_SG1 = 0x10C,
+ DMA0_CR2 = 0x110,
+ DMA0_CT2 = 0x111,
+ DMA0_DA2 = 0x112,
+ DMA0_SA2 = 0x113,
+ DMA0_SG2 = 0x114,
+ DMA0_CR3 = 0x118,
+ DMA0_CT3 = 0x119,
+ DMA0_DA3 = 0x11A,
+ DMA0_SA3 = 0x11B,
+ DMA0_SG3 = 0x11C,
+ DMA0_SR = 0x120,
+ DMA0_SGC = 0x123,
+ DMA0_SLP = 0x125,
+ DMA0_POL = 0x126,
+};
+
+typedef struct ppc405_dma_t ppc405_dma_t;
+struct ppc405_dma_t {
+ qemu_irq irqs[4];
+ uint32_t cr[4];
+ uint32_t ct[4];
+ uint32_t da[4];
+ uint32_t sa[4];
+ uint32_t sg[4];
+ uint32_t sr;
+ uint32_t sgc;
+ uint32_t slp;
+ uint32_t pol;
+};
+
+static uint32_t dcr_read_dma (void *opaque, int dcrn)
+{
+ return 0;
+}
+
+static void dcr_write_dma (void *opaque, int dcrn, uint32_t val)
+{
+}
+
+static void ppc405_dma_reset (void *opaque)
+{
+ ppc405_dma_t *dma;
+ int i;
+
+ dma = opaque;
+ for (i = 0; i < 4; i++) {
+ dma->cr[i] = 0x00000000;
+ dma->ct[i] = 0x00000000;
+ dma->da[i] = 0x00000000;
+ dma->sa[i] = 0x00000000;
+ dma->sg[i] = 0x00000000;
+ }
+ dma->sr = 0x00000000;
+ dma->sgc = 0x00000000;
+ dma->slp = 0x7C000000;
+ dma->pol = 0x00000000;
+}
+
+static void ppc405_dma_init(CPUState *env, qemu_irq irqs[4])
+{
+ ppc405_dma_t *dma;
+
+ dma = qemu_mallocz(sizeof(ppc405_dma_t));
+ memcpy(dma->irqs, irqs, 4 * sizeof(qemu_irq));
+ qemu_register_reset(&ppc405_dma_reset, dma);
+ ppc_dcr_register(env, DMA0_CR0,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CT0,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_DA0,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SA0,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SG0,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CR1,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CT1,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_DA1,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SA1,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SG1,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CR2,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CT2,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_DA2,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SA2,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SG2,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CR3,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_CT3,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_DA3,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SA3,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SG3,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SR,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SGC,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_SLP,
+ dma, &dcr_read_dma, &dcr_write_dma);
+ ppc_dcr_register(env, DMA0_POL,
+ dma, &dcr_read_dma, &dcr_write_dma);
+}
+
+/*****************************************************************************/
+/* GPIO */
+typedef struct ppc405_gpio_t ppc405_gpio_t;
+struct ppc405_gpio_t {
+ uint32_t or;
+ uint32_t tcr;
+ uint32_t osrh;
+ uint32_t osrl;
+ uint32_t tsrh;
+ uint32_t tsrl;
+ uint32_t odr;
+ uint32_t ir;
+ uint32_t rr1;
+ uint32_t isr1h;
+ uint32_t isr1l;
+};
+
+static uint32_t ppc405_gpio_readb (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_GPIO
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+
+ return 0;
+}
+
+static void ppc405_gpio_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_GPIO
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+}
+
+static uint32_t ppc405_gpio_readw (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_GPIO
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+
+ return 0;
+}
+
+static void ppc405_gpio_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_GPIO
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+}
+
+static uint32_t ppc405_gpio_readl (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_GPIO
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+
+ return 0;
+}
+
+static void ppc405_gpio_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_GPIO
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+}
+
+static CPUReadMemoryFunc * const ppc405_gpio_read[] = {
+ &ppc405_gpio_readb,
+ &ppc405_gpio_readw,
+ &ppc405_gpio_readl,
+};
+
+static CPUWriteMemoryFunc * const ppc405_gpio_write[] = {
+ &ppc405_gpio_writeb,
+ &ppc405_gpio_writew,
+ &ppc405_gpio_writel,
+};
+
+static void ppc405_gpio_reset (void *opaque)
+{
+}
+
+static void ppc405_gpio_init(target_phys_addr_t base)
+{
+ ppc405_gpio_t *gpio;
+ int io;
+
+ gpio = qemu_mallocz(sizeof(ppc405_gpio_t));
+#ifdef DEBUG_GPIO
+ printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
+#endif
+ io = cpu_register_io_memory(ppc405_gpio_read, ppc405_gpio_write, gpio,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x038, io);
+ qemu_register_reset(&ppc405_gpio_reset, gpio);
+}
+
+/*****************************************************************************/
+/* On Chip Memory */
+enum {
+ OCM0_ISARC = 0x018,
+ OCM0_ISACNTL = 0x019,
+ OCM0_DSARC = 0x01A,
+ OCM0_DSACNTL = 0x01B,
+};
+
+typedef struct ppc405_ocm_t ppc405_ocm_t;
+struct ppc405_ocm_t {
+ target_ulong offset;
+ uint32_t isarc;
+ uint32_t isacntl;
+ uint32_t dsarc;
+ uint32_t dsacntl;
+};
+
+static void ocm_update_mappings (ppc405_ocm_t *ocm,
+ uint32_t isarc, uint32_t isacntl,
+ uint32_t dsarc, uint32_t dsacntl)
+{
+#ifdef DEBUG_OCM
+ printf("OCM update ISA %08" PRIx32 " %08" PRIx32 " (%08" PRIx32
+ " %08" PRIx32 ") DSA %08" PRIx32 " %08" PRIx32
+ " (%08" PRIx32 " %08" PRIx32 ")\n",
+ isarc, isacntl, dsarc, dsacntl,
+ ocm->isarc, ocm->isacntl, ocm->dsarc, ocm->dsacntl);
+#endif
+ if (ocm->isarc != isarc ||
+ (ocm->isacntl & 0x80000000) != (isacntl & 0x80000000)) {
+ if (ocm->isacntl & 0x80000000) {
+ /* Unmap previously assigned memory region */
+ printf("OCM unmap ISA %08" PRIx32 "\n", ocm->isarc);
+ cpu_register_physical_memory(ocm->isarc, 0x04000000,
+ IO_MEM_UNASSIGNED);
+ }
+ if (isacntl & 0x80000000) {
+ /* Map new instruction memory region */
+#ifdef DEBUG_OCM
+ printf("OCM map ISA %08" PRIx32 "\n", isarc);
+#endif
+ cpu_register_physical_memory(isarc, 0x04000000,
+ ocm->offset | IO_MEM_RAM);
+ }
+ }
+ if (ocm->dsarc != dsarc ||
+ (ocm->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) {
+ if (ocm->dsacntl & 0x80000000) {
+ /* Beware not to unmap the region we just mapped */
+ if (!(isacntl & 0x80000000) || ocm->dsarc != isarc) {
+ /* Unmap previously assigned memory region */
+#ifdef DEBUG_OCM
+ printf("OCM unmap DSA %08" PRIx32 "\n", ocm->dsarc);
+#endif
+ cpu_register_physical_memory(ocm->dsarc, 0x04000000,
+ IO_MEM_UNASSIGNED);
+ }
+ }
+ if (dsacntl & 0x80000000) {
+ /* Beware not to remap the region we just mapped */
+ if (!(isacntl & 0x80000000) || dsarc != isarc) {
+ /* Map new data memory region */
+#ifdef DEBUG_OCM
+ printf("OCM map DSA %08" PRIx32 "\n", dsarc);
+#endif
+ cpu_register_physical_memory(dsarc, 0x04000000,
+ ocm->offset | IO_MEM_RAM);
+ }
+ }
+ }
+}
+
+static uint32_t dcr_read_ocm (void *opaque, int dcrn)
+{
+ ppc405_ocm_t *ocm;
+ uint32_t ret;
+
+ ocm = opaque;
+ switch (dcrn) {
+ case OCM0_ISARC:
+ ret = ocm->isarc;
+ break;
+ case OCM0_ISACNTL:
+ ret = ocm->isacntl;
+ break;
+ case OCM0_DSARC:
+ ret = ocm->dsarc;
+ break;
+ case OCM0_DSACNTL:
+ ret = ocm->dsacntl;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_ocm (void *opaque, int dcrn, uint32_t val)
+{
+ ppc405_ocm_t *ocm;
+ uint32_t isarc, dsarc, isacntl, dsacntl;
+
+ ocm = opaque;
+ isarc = ocm->isarc;
+ dsarc = ocm->dsarc;
+ isacntl = ocm->isacntl;
+ dsacntl = ocm->dsacntl;
+ switch (dcrn) {
+ case OCM0_ISARC:
+ isarc = val & 0xFC000000;
+ break;
+ case OCM0_ISACNTL:
+ isacntl = val & 0xC0000000;
+ break;
+ case OCM0_DSARC:
+ isarc = val & 0xFC000000;
+ break;
+ case OCM0_DSACNTL:
+ isacntl = val & 0xC0000000;
+ break;
+ }
+ ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl);
+ ocm->isarc = isarc;
+ ocm->dsarc = dsarc;
+ ocm->isacntl = isacntl;
+ ocm->dsacntl = dsacntl;
+}
+
+static void ocm_reset (void *opaque)
+{
+ ppc405_ocm_t *ocm;
+ uint32_t isarc, dsarc, isacntl, dsacntl;
+
+ ocm = opaque;
+ isarc = 0x00000000;
+ isacntl = 0x00000000;
+ dsarc = 0x00000000;
+ dsacntl = 0x00000000;
+ ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl);
+ ocm->isarc = isarc;
+ ocm->dsarc = dsarc;
+ ocm->isacntl = isacntl;
+ ocm->dsacntl = dsacntl;
+}
+
+static void ppc405_ocm_init(CPUState *env)
+{
+ ppc405_ocm_t *ocm;
+
+ ocm = qemu_mallocz(sizeof(ppc405_ocm_t));
+ ocm->offset = qemu_ram_alloc(NULL, "ppc405.ocm", 4096);
+ qemu_register_reset(&ocm_reset, ocm);
+ ppc_dcr_register(env, OCM0_ISARC,
+ ocm, &dcr_read_ocm, &dcr_write_ocm);
+ ppc_dcr_register(env, OCM0_ISACNTL,
+ ocm, &dcr_read_ocm, &dcr_write_ocm);
+ ppc_dcr_register(env, OCM0_DSARC,
+ ocm, &dcr_read_ocm, &dcr_write_ocm);
+ ppc_dcr_register(env, OCM0_DSACNTL,
+ ocm, &dcr_read_ocm, &dcr_write_ocm);
+}
+
+/*****************************************************************************/
+/* I2C controller */
+typedef struct ppc4xx_i2c_t ppc4xx_i2c_t;
+struct ppc4xx_i2c_t {
+ qemu_irq irq;
+ uint8_t mdata;
+ uint8_t lmadr;
+ uint8_t hmadr;
+ uint8_t cntl;
+ uint8_t mdcntl;
+ uint8_t sts;
+ uint8_t extsts;
+ uint8_t sdata;
+ uint8_t lsadr;
+ uint8_t hsadr;
+ uint8_t clkdiv;
+ uint8_t intrmsk;
+ uint8_t xfrcnt;
+ uint8_t xtcntlss;
+ uint8_t directcntl;
+};
+
+static uint32_t ppc4xx_i2c_readb (void *opaque, target_phys_addr_t addr)
+{
+ ppc4xx_i2c_t *i2c;
+ uint32_t ret;
+
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ i2c = opaque;
+ switch (addr) {
+ case 0x00:
+ // i2c_readbyte(&i2c->mdata);
+ ret = i2c->mdata;
+ break;
+ case 0x02:
+ ret = i2c->sdata;
+ break;
+ case 0x04:
+ ret = i2c->lmadr;
+ break;
+ case 0x05:
+ ret = i2c->hmadr;
+ break;
+ case 0x06:
+ ret = i2c->cntl;
+ break;
+ case 0x07:
+ ret = i2c->mdcntl;
+ break;
+ case 0x08:
+ ret = i2c->sts;
+ break;
+ case 0x09:
+ ret = i2c->extsts;
+ break;
+ case 0x0A:
+ ret = i2c->lsadr;
+ break;
+ case 0x0B:
+ ret = i2c->hsadr;
+ break;
+ case 0x0C:
+ ret = i2c->clkdiv;
+ break;
+ case 0x0D:
+ ret = i2c->intrmsk;
+ break;
+ case 0x0E:
+ ret = i2c->xfrcnt;
+ break;
+ case 0x0F:
+ ret = i2c->xtcntlss;
+ break;
+ case 0x10:
+ ret = i2c->directcntl;
+ break;
+ default:
+ ret = 0x00;
+ break;
+ }
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " %02" PRIx32 "\n", __func__, addr, ret);
+#endif
+
+ return ret;
+}
+
+static void ppc4xx_i2c_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ppc4xx_i2c_t *i2c;
+
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ i2c = opaque;
+ switch (addr) {
+ case 0x00:
+ i2c->mdata = value;
+ // i2c_sendbyte(&i2c->mdata);
+ break;
+ case 0x02:
+ i2c->sdata = value;
+ break;
+ case 0x04:
+ i2c->lmadr = value;
+ break;
+ case 0x05:
+ i2c->hmadr = value;
+ break;
+ case 0x06:
+ i2c->cntl = value;
+ break;
+ case 0x07:
+ i2c->mdcntl = value & 0xDF;
+ break;
+ case 0x08:
+ i2c->sts &= ~(value & 0x0A);
+ break;
+ case 0x09:
+ i2c->extsts &= ~(value & 0x8F);
+ break;
+ case 0x0A:
+ i2c->lsadr = value;
+ break;
+ case 0x0B:
+ i2c->hsadr = value;
+ break;
+ case 0x0C:
+ i2c->clkdiv = value;
+ break;
+ case 0x0D:
+ i2c->intrmsk = value;
+ break;
+ case 0x0E:
+ i2c->xfrcnt = value & 0x77;
+ break;
+ case 0x0F:
+ i2c->xtcntlss = value;
+ break;
+ case 0x10:
+ i2c->directcntl = value & 0x7;
+ break;
+ }
+}
+
+static uint32_t ppc4xx_i2c_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ ret = ppc4xx_i2c_readb(opaque, addr) << 8;
+ ret |= ppc4xx_i2c_readb(opaque, addr + 1);
+
+ return ret;
+}
+
+static void ppc4xx_i2c_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ ppc4xx_i2c_writeb(opaque, addr, value >> 8);
+ ppc4xx_i2c_writeb(opaque, addr + 1, value);
+}
+
+static uint32_t ppc4xx_i2c_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret;
+
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ ret = ppc4xx_i2c_readb(opaque, addr) << 24;
+ ret |= ppc4xx_i2c_readb(opaque, addr + 1) << 16;
+ ret |= ppc4xx_i2c_readb(opaque, addr + 2) << 8;
+ ret |= ppc4xx_i2c_readb(opaque, addr + 3);
+
+ return ret;
+}
+
+static void ppc4xx_i2c_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ ppc4xx_i2c_writeb(opaque, addr, value >> 24);
+ ppc4xx_i2c_writeb(opaque, addr + 1, value >> 16);
+ ppc4xx_i2c_writeb(opaque, addr + 2, value >> 8);
+ ppc4xx_i2c_writeb(opaque, addr + 3, value);
+}
+
+static CPUReadMemoryFunc * const i2c_read[] = {
+ &ppc4xx_i2c_readb,
+ &ppc4xx_i2c_readw,
+ &ppc4xx_i2c_readl,
+};
+
+static CPUWriteMemoryFunc * const i2c_write[] = {
+ &ppc4xx_i2c_writeb,
+ &ppc4xx_i2c_writew,
+ &ppc4xx_i2c_writel,
+};
+
+static void ppc4xx_i2c_reset (void *opaque)
+{
+ ppc4xx_i2c_t *i2c;
+
+ i2c = opaque;
+ i2c->mdata = 0x00;
+ i2c->sdata = 0x00;
+ i2c->cntl = 0x00;
+ i2c->mdcntl = 0x00;
+ i2c->sts = 0x00;
+ i2c->extsts = 0x00;
+ i2c->clkdiv = 0x00;
+ i2c->xfrcnt = 0x00;
+ i2c->directcntl = 0x0F;
+}
+
+static void ppc405_i2c_init(target_phys_addr_t base, qemu_irq irq)
+{
+ ppc4xx_i2c_t *i2c;
+ int io;
+
+ i2c = qemu_mallocz(sizeof(ppc4xx_i2c_t));
+ i2c->irq = irq;
+#ifdef DEBUG_I2C
+ printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
+#endif
+ io = cpu_register_io_memory(i2c_read, i2c_write, i2c,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x011, io);
+ qemu_register_reset(ppc4xx_i2c_reset, i2c);
+}
+
+/*****************************************************************************/
+/* General purpose timers */
+typedef struct ppc4xx_gpt_t ppc4xx_gpt_t;
+struct ppc4xx_gpt_t {
+ int64_t tb_offset;
+ uint32_t tb_freq;
+ struct QEMUTimer *timer;
+ qemu_irq irqs[5];
+ uint32_t oe;
+ uint32_t ol;
+ uint32_t im;
+ uint32_t is;
+ uint32_t ie;
+ uint32_t comp[5];
+ uint32_t mask[5];
+};
+
+static uint32_t ppc4xx_gpt_readb (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_GPT
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ /* XXX: generate a bus fault */
+ return -1;
+}
+
+static void ppc4xx_gpt_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ /* XXX: generate a bus fault */
+}
+
+static uint32_t ppc4xx_gpt_readw (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_GPT
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ /* XXX: generate a bus fault */
+ return -1;
+}
+
+static void ppc4xx_gpt_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ /* XXX: generate a bus fault */
+}
+
+static int ppc4xx_gpt_compare (ppc4xx_gpt_t *gpt, int n)
+{
+ /* XXX: TODO */
+ return 0;
+}
+
+static void ppc4xx_gpt_set_output (ppc4xx_gpt_t *gpt, int n, int level)
+{
+ /* XXX: TODO */
+}
+
+static void ppc4xx_gpt_set_outputs (ppc4xx_gpt_t *gpt)
+{
+ uint32_t mask;
+ int i;
+
+ mask = 0x80000000;
+ for (i = 0; i < 5; i++) {
+ if (gpt->oe & mask) {
+ /* Output is enabled */
+ if (ppc4xx_gpt_compare(gpt, i)) {
+ /* Comparison is OK */
+ ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask);
+ } else {
+ /* Comparison is KO */
+ ppc4xx_gpt_set_output(gpt, i, gpt->ol & mask ? 0 : 1);
+ }
+ }
+ mask = mask >> 1;
+ }
+}
+
+static void ppc4xx_gpt_set_irqs (ppc4xx_gpt_t *gpt)
+{
+ uint32_t mask;
+ int i;
+
+ mask = 0x00008000;
+ for (i = 0; i < 5; i++) {
+ if (gpt->is & gpt->im & mask)
+ qemu_irq_raise(gpt->irqs[i]);
+ else
+ qemu_irq_lower(gpt->irqs[i]);
+ mask = mask >> 1;
+ }
+}
+
+static void ppc4xx_gpt_compute_timer (ppc4xx_gpt_t *gpt)
+{
+ /* XXX: TODO */
+}
+
+static uint32_t ppc4xx_gpt_readl (void *opaque, target_phys_addr_t addr)
+{
+ ppc4xx_gpt_t *gpt;
+ uint32_t ret;
+ int idx;
+
+#ifdef DEBUG_GPT
+ printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+ gpt = opaque;
+ switch (addr) {
+ case 0x00:
+ /* Time base counter */
+ ret = muldiv64(qemu_get_clock(vm_clock) + gpt->tb_offset,
+ gpt->tb_freq, get_ticks_per_sec());
+ break;
+ case 0x10:
+ /* Output enable */
+ ret = gpt->oe;
+ break;
+ case 0x14:
+ /* Output level */
+ ret = gpt->ol;
+ break;
+ case 0x18:
+ /* Interrupt mask */
+ ret = gpt->im;
+ break;
+ case 0x1C:
+ case 0x20:
+ /* Interrupt status */
+ ret = gpt->is;
+ break;
+ case 0x24:
+ /* Interrupt enable */
+ ret = gpt->ie;
+ break;
+ case 0x80 ... 0x90:
+ /* Compare timer */
+ idx = (addr - 0x80) >> 2;
+ ret = gpt->comp[idx];
+ break;
+ case 0xC0 ... 0xD0:
+ /* Compare mask */
+ idx = (addr - 0xC0) >> 2;
+ ret = gpt->mask[idx];
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+static void ppc4xx_gpt_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ppc4xx_gpt_t *gpt;
+ int idx;
+
+#ifdef DEBUG_I2C
+ printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+ gpt = opaque;
+ switch (addr) {
+ case 0x00:
+ /* Time base counter */
+ gpt->tb_offset = muldiv64(value, get_ticks_per_sec(), gpt->tb_freq)
+ - qemu_get_clock(vm_clock);
+ ppc4xx_gpt_compute_timer(gpt);
+ break;
+ case 0x10:
+ /* Output enable */
+ gpt->oe = value & 0xF8000000;
+ ppc4xx_gpt_set_outputs(gpt);
+ break;
+ case 0x14:
+ /* Output level */
+ gpt->ol = value & 0xF8000000;
+ ppc4xx_gpt_set_outputs(gpt);
+ break;
+ case 0x18:
+ /* Interrupt mask */
+ gpt->im = value & 0x0000F800;
+ break;
+ case 0x1C:
+ /* Interrupt status set */
+ gpt->is |= value & 0x0000F800;
+ ppc4xx_gpt_set_irqs(gpt);
+ break;
+ case 0x20:
+ /* Interrupt status clear */
+ gpt->is &= ~(value & 0x0000F800);
+ ppc4xx_gpt_set_irqs(gpt);
+ break;
+ case 0x24:
+ /* Interrupt enable */
+ gpt->ie = value & 0x0000F800;
+ ppc4xx_gpt_set_irqs(gpt);
+ break;
+ case 0x80 ... 0x90:
+ /* Compare timer */
+ idx = (addr - 0x80) >> 2;
+ gpt->comp[idx] = value & 0xF8000000;
+ ppc4xx_gpt_compute_timer(gpt);
+ break;
+ case 0xC0 ... 0xD0:
+ /* Compare mask */
+ idx = (addr - 0xC0) >> 2;
+ gpt->mask[idx] = value & 0xF8000000;
+ ppc4xx_gpt_compute_timer(gpt);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const gpt_read[] = {
+ &ppc4xx_gpt_readb,
+ &ppc4xx_gpt_readw,
+ &ppc4xx_gpt_readl,
+};
+
+static CPUWriteMemoryFunc * const gpt_write[] = {
+ &ppc4xx_gpt_writeb,
+ &ppc4xx_gpt_writew,
+ &ppc4xx_gpt_writel,
+};
+
+static void ppc4xx_gpt_cb (void *opaque)
+{
+ ppc4xx_gpt_t *gpt;
+
+ gpt = opaque;
+ ppc4xx_gpt_set_irqs(gpt);
+ ppc4xx_gpt_set_outputs(gpt);
+ ppc4xx_gpt_compute_timer(gpt);
+}
+
+static void ppc4xx_gpt_reset (void *opaque)
+{
+ ppc4xx_gpt_t *gpt;
+ int i;
+
+ gpt = opaque;
+ qemu_del_timer(gpt->timer);
+ gpt->oe = 0x00000000;
+ gpt->ol = 0x00000000;
+ gpt->im = 0x00000000;
+ gpt->is = 0x00000000;
+ gpt->ie = 0x00000000;
+ for (i = 0; i < 5; i++) {
+ gpt->comp[i] = 0x00000000;
+ gpt->mask[i] = 0x00000000;
+ }
+}
+
+static void ppc4xx_gpt_init(target_phys_addr_t base, qemu_irq irqs[5])
+{
+ ppc4xx_gpt_t *gpt;
+ int i;
+ int io;
+
+ gpt = qemu_mallocz(sizeof(ppc4xx_gpt_t));
+ for (i = 0; i < 5; i++) {
+ gpt->irqs[i] = irqs[i];
+ }
+ gpt->timer = qemu_new_timer(vm_clock, &ppc4xx_gpt_cb, gpt);
+#ifdef DEBUG_GPT
+ printf("%s: offset " TARGET_FMT_plx "\n", __func__, base);
+#endif
+ io = cpu_register_io_memory(gpt_read, gpt_write, gpt, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x0d4, io);
+ qemu_register_reset(ppc4xx_gpt_reset, gpt);
+}
+
+/*****************************************************************************/
+/* MAL */
+enum {
+ MAL0_CFG = 0x180,
+ MAL0_ESR = 0x181,
+ MAL0_IER = 0x182,
+ MAL0_TXCASR = 0x184,
+ MAL0_TXCARR = 0x185,
+ MAL0_TXEOBISR = 0x186,
+ MAL0_TXDEIR = 0x187,
+ MAL0_RXCASR = 0x190,
+ MAL0_RXCARR = 0x191,
+ MAL0_RXEOBISR = 0x192,
+ MAL0_RXDEIR = 0x193,
+ MAL0_TXCTP0R = 0x1A0,
+ MAL0_TXCTP1R = 0x1A1,
+ MAL0_TXCTP2R = 0x1A2,
+ MAL0_TXCTP3R = 0x1A3,
+ MAL0_RXCTP0R = 0x1C0,
+ MAL0_RXCTP1R = 0x1C1,
+ MAL0_RCBS0 = 0x1E0,
+ MAL0_RCBS1 = 0x1E1,
+};
+
+typedef struct ppc40x_mal_t ppc40x_mal_t;
+struct ppc40x_mal_t {
+ qemu_irq irqs[4];
+ uint32_t cfg;
+ uint32_t esr;
+ uint32_t ier;
+ uint32_t txcasr;
+ uint32_t txcarr;
+ uint32_t txeobisr;
+ uint32_t txdeir;
+ uint32_t rxcasr;
+ uint32_t rxcarr;
+ uint32_t rxeobisr;
+ uint32_t rxdeir;
+ uint32_t txctpr[4];
+ uint32_t rxctpr[2];
+ uint32_t rcbs[2];
+};
+
+static void ppc40x_mal_reset (void *opaque);
+
+static uint32_t dcr_read_mal (void *opaque, int dcrn)
+{
+ ppc40x_mal_t *mal;
+ uint32_t ret;
+
+ mal = opaque;
+ switch (dcrn) {
+ case MAL0_CFG:
+ ret = mal->cfg;
+ break;
+ case MAL0_ESR:
+ ret = mal->esr;
+ break;
+ case MAL0_IER:
+ ret = mal->ier;
+ break;
+ case MAL0_TXCASR:
+ ret = mal->txcasr;
+ break;
+ case MAL0_TXCARR:
+ ret = mal->txcarr;
+ break;
+ case MAL0_TXEOBISR:
+ ret = mal->txeobisr;
+ break;
+ case MAL0_TXDEIR:
+ ret = mal->txdeir;
+ break;
+ case MAL0_RXCASR:
+ ret = mal->rxcasr;
+ break;
+ case MAL0_RXCARR:
+ ret = mal->rxcarr;
+ break;
+ case MAL0_RXEOBISR:
+ ret = mal->rxeobisr;
+ break;
+ case MAL0_RXDEIR:
+ ret = mal->rxdeir;
+ break;
+ case MAL0_TXCTP0R:
+ ret = mal->txctpr[0];
+ break;
+ case MAL0_TXCTP1R:
+ ret = mal->txctpr[1];
+ break;
+ case MAL0_TXCTP2R:
+ ret = mal->txctpr[2];
+ break;
+ case MAL0_TXCTP3R:
+ ret = mal->txctpr[3];
+ break;
+ case MAL0_RXCTP0R:
+ ret = mal->rxctpr[0];
+ break;
+ case MAL0_RXCTP1R:
+ ret = mal->rxctpr[1];
+ break;
+ case MAL0_RCBS0:
+ ret = mal->rcbs[0];
+ break;
+ case MAL0_RCBS1:
+ ret = mal->rcbs[1];
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_mal (void *opaque, int dcrn, uint32_t val)
+{
+ ppc40x_mal_t *mal;
+ int idx;
+
+ mal = opaque;
+ switch (dcrn) {
+ case MAL0_CFG:
+ if (val & 0x80000000)
+ ppc40x_mal_reset(mal);
+ mal->cfg = val & 0x00FFC087;
+ break;
+ case MAL0_ESR:
+ /* Read/clear */
+ mal->esr &= ~val;
+ break;
+ case MAL0_IER:
+ mal->ier = val & 0x0000001F;
+ break;
+ case MAL0_TXCASR:
+ mal->txcasr = val & 0xF0000000;
+ break;
+ case MAL0_TXCARR:
+ mal->txcarr = val & 0xF0000000;
+ break;
+ case MAL0_TXEOBISR:
+ /* Read/clear */
+ mal->txeobisr &= ~val;
+ break;
+ case MAL0_TXDEIR:
+ /* Read/clear */
+ mal->txdeir &= ~val;
+ break;
+ case MAL0_RXCASR:
+ mal->rxcasr = val & 0xC0000000;
+ break;
+ case MAL0_RXCARR:
+ mal->rxcarr = val & 0xC0000000;
+ break;
+ case MAL0_RXEOBISR:
+ /* Read/clear */
+ mal->rxeobisr &= ~val;
+ break;
+ case MAL0_RXDEIR:
+ /* Read/clear */
+ mal->rxdeir &= ~val;
+ break;
+ case MAL0_TXCTP0R:
+ idx = 0;
+ goto update_tx_ptr;
+ case MAL0_TXCTP1R:
+ idx = 1;
+ goto update_tx_ptr;
+ case MAL0_TXCTP2R:
+ idx = 2;
+ goto update_tx_ptr;
+ case MAL0_TXCTP3R:
+ idx = 3;
+ update_tx_ptr:
+ mal->txctpr[idx] = val;
+ break;
+ case MAL0_RXCTP0R:
+ idx = 0;
+ goto update_rx_ptr;
+ case MAL0_RXCTP1R:
+ idx = 1;
+ update_rx_ptr:
+ mal->rxctpr[idx] = val;
+ break;
+ case MAL0_RCBS0:
+ idx = 0;
+ goto update_rx_size;
+ case MAL0_RCBS1:
+ idx = 1;
+ update_rx_size:
+ mal->rcbs[idx] = val & 0x000000FF;
+ break;
+ }
+}
+
+static void ppc40x_mal_reset (void *opaque)
+{
+ ppc40x_mal_t *mal;
+
+ mal = opaque;
+ mal->cfg = 0x0007C000;
+ mal->esr = 0x00000000;
+ mal->ier = 0x00000000;
+ mal->rxcasr = 0x00000000;
+ mal->rxdeir = 0x00000000;
+ mal->rxeobisr = 0x00000000;
+ mal->txcasr = 0x00000000;
+ mal->txdeir = 0x00000000;
+ mal->txeobisr = 0x00000000;
+}
+
+static void ppc405_mal_init(CPUState *env, qemu_irq irqs[4])
+{
+ ppc40x_mal_t *mal;
+ int i;
+
+ mal = qemu_mallocz(sizeof(ppc40x_mal_t));
+ for (i = 0; i < 4; i++)
+ mal->irqs[i] = irqs[i];
+ qemu_register_reset(&ppc40x_mal_reset, mal);
+ ppc_dcr_register(env, MAL0_CFG,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_ESR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_IER,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXCASR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXCARR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXEOBISR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXDEIR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RXCASR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RXCARR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RXEOBISR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RXDEIR,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXCTP0R,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXCTP1R,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXCTP2R,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_TXCTP3R,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RXCTP0R,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RXCTP1R,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RCBS0,
+ mal, &dcr_read_mal, &dcr_write_mal);
+ ppc_dcr_register(env, MAL0_RCBS1,
+ mal, &dcr_read_mal, &dcr_write_mal);
+}
+
+/*****************************************************************************/
+/* SPR */
+void ppc40x_core_reset (CPUState *env)
+{
+ target_ulong dbsr;
+
+ printf("Reset PowerPC core\n");
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ /* XXX: TOFIX */
+#if 0
+ cpu_reset(env);
+#else
+ qemu_system_reset_request();
+#endif
+ dbsr = env->spr[SPR_40x_DBSR];
+ dbsr &= ~0x00000300;
+ dbsr |= 0x00000100;
+ env->spr[SPR_40x_DBSR] = dbsr;
+}
+
+void ppc40x_chip_reset (CPUState *env)
+{
+ target_ulong dbsr;
+
+ printf("Reset PowerPC chip\n");
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ /* XXX: TOFIX */
+#if 0
+ cpu_reset(env);
+#else
+ qemu_system_reset_request();
+#endif
+ /* XXX: TODO reset all internal peripherals */
+ dbsr = env->spr[SPR_40x_DBSR];
+ dbsr &= ~0x00000300;
+ dbsr |= 0x00000200;
+ env->spr[SPR_40x_DBSR] = dbsr;
+}
+
+void ppc40x_system_reset (CPUState *env)
+{
+ printf("Reset PowerPC system\n");
+ qemu_system_reset_request();
+}
+
+void store_40x_dbcr0 (CPUState *env, uint32_t val)
+{
+ switch ((val >> 28) & 0x3) {
+ case 0x0:
+ /* No action */
+ break;
+ case 0x1:
+ /* Core reset */
+ ppc40x_core_reset(env);
+ break;
+ case 0x2:
+ /* Chip reset */
+ ppc40x_chip_reset(env);
+ break;
+ case 0x3:
+ /* System reset */
+ ppc40x_system_reset(env);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* PowerPC 405CR */
+enum {
+ PPC405CR_CPC0_PLLMR = 0x0B0,
+ PPC405CR_CPC0_CR0 = 0x0B1,
+ PPC405CR_CPC0_CR1 = 0x0B2,
+ PPC405CR_CPC0_PSR = 0x0B4,
+ PPC405CR_CPC0_JTAGID = 0x0B5,
+ PPC405CR_CPC0_ER = 0x0B9,
+ PPC405CR_CPC0_FR = 0x0BA,
+ PPC405CR_CPC0_SR = 0x0BB,
+};
+
+enum {
+ PPC405CR_CPU_CLK = 0,
+ PPC405CR_TMR_CLK = 1,
+ PPC405CR_PLB_CLK = 2,
+ PPC405CR_SDRAM_CLK = 3,
+ PPC405CR_OPB_CLK = 4,
+ PPC405CR_EXT_CLK = 5,
+ PPC405CR_UART_CLK = 6,
+ PPC405CR_CLK_NB = 7,
+};
+
+typedef struct ppc405cr_cpc_t ppc405cr_cpc_t;
+struct ppc405cr_cpc_t {
+ clk_setup_t clk_setup[PPC405CR_CLK_NB];
+ uint32_t sysclk;
+ uint32_t psr;
+ uint32_t cr0;
+ uint32_t cr1;
+ uint32_t jtagid;
+ uint32_t pllmr;
+ uint32_t er;
+ uint32_t fr;
+};
+
+static void ppc405cr_clk_setup (ppc405cr_cpc_t *cpc)
+{
+ uint64_t VCO_out, PLL_out;
+ uint32_t CPU_clk, TMR_clk, SDRAM_clk, PLB_clk, OPB_clk, EXT_clk, UART_clk;
+ int M, D0, D1, D2;
+
+ D0 = ((cpc->pllmr >> 26) & 0x3) + 1; /* CBDV */
+ if (cpc->pllmr & 0x80000000) {
+ D1 = (((cpc->pllmr >> 20) - 1) & 0xF) + 1; /* FBDV */
+ D2 = 8 - ((cpc->pllmr >> 16) & 0x7); /* FWDVA */
+ M = D0 * D1 * D2;
+ VCO_out = cpc->sysclk * M;
+ if (VCO_out < 400000000 || VCO_out > 800000000) {
+ /* PLL cannot lock */
+ cpc->pllmr &= ~0x80000000;
+ goto bypass_pll;
+ }
+ PLL_out = VCO_out / D2;
+ } else {
+ /* Bypass PLL */
+ bypass_pll:
+ M = D0;
+ PLL_out = cpc->sysclk * M;
+ }
+ CPU_clk = PLL_out;
+ if (cpc->cr1 & 0x00800000)
+ TMR_clk = cpc->sysclk; /* Should have a separate clock */
+ else
+ TMR_clk = CPU_clk;
+ PLB_clk = CPU_clk / D0;
+ SDRAM_clk = PLB_clk;
+ D0 = ((cpc->pllmr >> 10) & 0x3) + 1;
+ OPB_clk = PLB_clk / D0;
+ D0 = ((cpc->pllmr >> 24) & 0x3) + 2;
+ EXT_clk = PLB_clk / D0;
+ D0 = ((cpc->cr0 >> 1) & 0x1F) + 1;
+ UART_clk = CPU_clk / D0;
+ /* Setup CPU clocks */
+ clk_setup(&cpc->clk_setup[PPC405CR_CPU_CLK], CPU_clk);
+ /* Setup time-base clock */
+ clk_setup(&cpc->clk_setup[PPC405CR_TMR_CLK], TMR_clk);
+ /* Setup PLB clock */
+ clk_setup(&cpc->clk_setup[PPC405CR_PLB_CLK], PLB_clk);
+ /* Setup SDRAM clock */
+ clk_setup(&cpc->clk_setup[PPC405CR_SDRAM_CLK], SDRAM_clk);
+ /* Setup OPB clock */
+ clk_setup(&cpc->clk_setup[PPC405CR_OPB_CLK], OPB_clk);
+ /* Setup external clock */
+ clk_setup(&cpc->clk_setup[PPC405CR_EXT_CLK], EXT_clk);
+ /* Setup UART clock */
+ clk_setup(&cpc->clk_setup[PPC405CR_UART_CLK], UART_clk);
+}
+
+static uint32_t dcr_read_crcpc (void *opaque, int dcrn)
+{
+ ppc405cr_cpc_t *cpc;
+ uint32_t ret;
+
+ cpc = opaque;
+ switch (dcrn) {
+ case PPC405CR_CPC0_PLLMR:
+ ret = cpc->pllmr;
+ break;
+ case PPC405CR_CPC0_CR0:
+ ret = cpc->cr0;
+ break;
+ case PPC405CR_CPC0_CR1:
+ ret = cpc->cr1;
+ break;
+ case PPC405CR_CPC0_PSR:
+ ret = cpc->psr;
+ break;
+ case PPC405CR_CPC0_JTAGID:
+ ret = cpc->jtagid;
+ break;
+ case PPC405CR_CPC0_ER:
+ ret = cpc->er;
+ break;
+ case PPC405CR_CPC0_FR:
+ ret = cpc->fr;
+ break;
+ case PPC405CR_CPC0_SR:
+ ret = ~(cpc->er | cpc->fr) & 0xFFFF0000;
+ break;
+ default:
+ /* Avoid gcc warning */
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_crcpc (void *opaque, int dcrn, uint32_t val)
+{
+ ppc405cr_cpc_t *cpc;
+
+ cpc = opaque;
+ switch (dcrn) {
+ case PPC405CR_CPC0_PLLMR:
+ cpc->pllmr = val & 0xFFF77C3F;
+ break;
+ case PPC405CR_CPC0_CR0:
+ cpc->cr0 = val & 0x0FFFFFFE;
+ break;
+ case PPC405CR_CPC0_CR1:
+ cpc->cr1 = val & 0x00800000;
+ break;
+ case PPC405CR_CPC0_PSR:
+ /* Read-only */
+ break;
+ case PPC405CR_CPC0_JTAGID:
+ /* Read-only */
+ break;
+ case PPC405CR_CPC0_ER:
+ cpc->er = val & 0xBFFC0000;
+ break;
+ case PPC405CR_CPC0_FR:
+ cpc->fr = val & 0xBFFC0000;
+ break;
+ case PPC405CR_CPC0_SR:
+ /* Read-only */
+ break;
+ }
+}
+
+static void ppc405cr_cpc_reset (void *opaque)
+{
+ ppc405cr_cpc_t *cpc;
+ int D;
+
+ cpc = opaque;
+ /* Compute PLLMR value from PSR settings */
+ cpc->pllmr = 0x80000000;
+ /* PFWD */
+ switch ((cpc->psr >> 30) & 3) {
+ case 0:
+ /* Bypass */
+ cpc->pllmr &= ~0x80000000;
+ break;
+ case 1:
+ /* Divide by 3 */
+ cpc->pllmr |= 5 << 16;
+ break;
+ case 2:
+ /* Divide by 4 */
+ cpc->pllmr |= 4 << 16;
+ break;
+ case 3:
+ /* Divide by 6 */
+ cpc->pllmr |= 2 << 16;
+ break;
+ }
+ /* PFBD */
+ D = (cpc->psr >> 28) & 3;
+ cpc->pllmr |= (D + 1) << 20;
+ /* PT */
+ D = (cpc->psr >> 25) & 7;
+ switch (D) {
+ case 0x2:
+ cpc->pllmr |= 0x13;
+ break;
+ case 0x4:
+ cpc->pllmr |= 0x15;
+ break;
+ case 0x5:
+ cpc->pllmr |= 0x16;
+ break;
+ default:
+ break;
+ }
+ /* PDC */
+ D = (cpc->psr >> 23) & 3;
+ cpc->pllmr |= D << 26;
+ /* ODP */
+ D = (cpc->psr >> 21) & 3;
+ cpc->pllmr |= D << 10;
+ /* EBPD */
+ D = (cpc->psr >> 17) & 3;
+ cpc->pllmr |= D << 24;
+ cpc->cr0 = 0x0000003C;
+ cpc->cr1 = 0x2B0D8800;
+ cpc->er = 0x00000000;
+ cpc->fr = 0x00000000;
+ ppc405cr_clk_setup(cpc);
+}
+
+static void ppc405cr_clk_init (ppc405cr_cpc_t *cpc)
+{
+ int D;
+
+ /* XXX: this should be read from IO pins */
+ cpc->psr = 0x00000000; /* 8 bits ROM */
+ /* PFWD */
+ D = 0x2; /* Divide by 4 */
+ cpc->psr |= D << 30;
+ /* PFBD */
+ D = 0x1; /* Divide by 2 */
+ cpc->psr |= D << 28;
+ /* PDC */
+ D = 0x1; /* Divide by 2 */
+ cpc->psr |= D << 23;
+ /* PT */
+ D = 0x5; /* M = 16 */
+ cpc->psr |= D << 25;
+ /* ODP */
+ D = 0x1; /* Divide by 2 */
+ cpc->psr |= D << 21;
+ /* EBDP */
+ D = 0x2; /* Divide by 4 */
+ cpc->psr |= D << 17;
+}
+
+static void ppc405cr_cpc_init (CPUState *env, clk_setup_t clk_setup[7],
+ uint32_t sysclk)
+{
+ ppc405cr_cpc_t *cpc;
+
+ cpc = qemu_mallocz(sizeof(ppc405cr_cpc_t));
+ memcpy(cpc->clk_setup, clk_setup,
+ PPC405CR_CLK_NB * sizeof(clk_setup_t));
+ cpc->sysclk = sysclk;
+ cpc->jtagid = 0x42051049;
+ ppc_dcr_register(env, PPC405CR_CPC0_PSR, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_CR0, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_CR1, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_JTAGID, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_PLLMR, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_ER, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_FR, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc_dcr_register(env, PPC405CR_CPC0_SR, cpc,
+ &dcr_read_crcpc, &dcr_write_crcpc);
+ ppc405cr_clk_init(cpc);
+ qemu_register_reset(ppc405cr_cpc_reset, cpc);
+}
+
+CPUState *ppc405cr_init (target_phys_addr_t ram_bases[4],
+ target_phys_addr_t ram_sizes[4],
+ uint32_t sysclk, qemu_irq **picp,
+ int do_init)
+{
+ clk_setup_t clk_setup[PPC405CR_CLK_NB];
+ qemu_irq dma_irqs[4];
+ CPUState *env;
+ qemu_irq *pic, *irqs;
+
+ memset(clk_setup, 0, sizeof(clk_setup));
+ env = ppc4xx_init("405cr", &clk_setup[PPC405CR_CPU_CLK],
+ &clk_setup[PPC405CR_TMR_CLK], sysclk);
+ /* Memory mapped devices registers */
+ /* PLB arbitrer */
+ ppc4xx_plb_init(env);
+ /* PLB to OPB bridge */
+ ppc4xx_pob_init(env);
+ /* OBP arbitrer */
+ ppc4xx_opba_init(0xef600600);
+ /* Universal interrupt controller */
+ irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+ irqs[PPCUIC_OUTPUT_INT] =
+ ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
+ irqs[PPCUIC_OUTPUT_CINT] =
+ ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
+ pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
+ *picp = pic;
+ /* SDRAM controller */
+ ppc4xx_sdram_init(env, pic[14], 1, ram_bases, ram_sizes, do_init);
+ /* External bus controller */
+ ppc405_ebc_init(env);
+ /* DMA controller */
+ dma_irqs[0] = pic[26];
+ dma_irqs[1] = pic[25];
+ dma_irqs[2] = pic[24];
+ dma_irqs[3] = pic[23];
+ ppc405_dma_init(env, dma_irqs);
+ /* Serial ports */
+ if (serial_hds[0] != NULL) {
+ serial_mm_init(0xef600300, 0, pic[0], PPC_SERIAL_MM_BAUDBASE,
+ serial_hds[0], 1, 1);
+ }
+ if (serial_hds[1] != NULL) {
+ serial_mm_init(0xef600400, 0, pic[1], PPC_SERIAL_MM_BAUDBASE,
+ serial_hds[1], 1, 1);
+ }
+ /* IIC controller */
+ ppc405_i2c_init(0xef600500, pic[2]);
+ /* GPIO */
+ ppc405_gpio_init(0xef600700);
+ /* CPU control */
+ ppc405cr_cpc_init(env, clk_setup, sysclk);
+
+ return env;
+}
+
+/*****************************************************************************/
+/* PowerPC 405EP */
+/* CPU control */
+enum {
+ PPC405EP_CPC0_PLLMR0 = 0x0F0,
+ PPC405EP_CPC0_BOOT = 0x0F1,
+ PPC405EP_CPC0_EPCTL = 0x0F3,
+ PPC405EP_CPC0_PLLMR1 = 0x0F4,
+ PPC405EP_CPC0_UCR = 0x0F5,
+ PPC405EP_CPC0_SRR = 0x0F6,
+ PPC405EP_CPC0_JTAGID = 0x0F7,
+ PPC405EP_CPC0_PCI = 0x0F9,
+#if 0
+ PPC405EP_CPC0_ER = xxx,
+ PPC405EP_CPC0_FR = xxx,
+ PPC405EP_CPC0_SR = xxx,
+#endif
+};
+
+enum {
+ PPC405EP_CPU_CLK = 0,
+ PPC405EP_PLB_CLK = 1,
+ PPC405EP_OPB_CLK = 2,
+ PPC405EP_EBC_CLK = 3,
+ PPC405EP_MAL_CLK = 4,
+ PPC405EP_PCI_CLK = 5,
+ PPC405EP_UART0_CLK = 6,
+ PPC405EP_UART1_CLK = 7,
+ PPC405EP_CLK_NB = 8,
+};
+
+typedef struct ppc405ep_cpc_t ppc405ep_cpc_t;
+struct ppc405ep_cpc_t {
+ uint32_t sysclk;
+ clk_setup_t clk_setup[PPC405EP_CLK_NB];
+ uint32_t boot;
+ uint32_t epctl;
+ uint32_t pllmr[2];
+ uint32_t ucr;
+ uint32_t srr;
+ uint32_t jtagid;
+ uint32_t pci;
+ /* Clock and power management */
+ uint32_t er;
+ uint32_t fr;
+ uint32_t sr;
+};
+
+static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc)
+{
+ uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk;
+ uint32_t UART0_clk, UART1_clk;
+ uint64_t VCO_out, PLL_out;
+ int M, D;
+
+ VCO_out = 0;
+ if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) {
+ M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */
+#ifdef DEBUG_CLOCKS_LL
+ printf("FBMUL %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 20) & 0xF, M);
+#endif
+ D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */
+#ifdef DEBUG_CLOCKS_LL
+ printf("FWDA %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 16) & 0x7, D);
+#endif
+ VCO_out = cpc->sysclk * M * D;
+ if (VCO_out < 500000000UL || VCO_out > 1000000000UL) {
+ /* Error - unlock the PLL */
+ printf("VCO out of range %" PRIu64 "\n", VCO_out);
+#if 0
+ cpc->pllmr[1] &= ~0x80000000;
+ goto pll_bypass;
+#endif
+ }
+ PLL_out = VCO_out / D;
+ /* Pretend the PLL is locked */
+ cpc->boot |= 0x00000001;
+ } else {
+#if 0
+ pll_bypass:
+#endif
+ PLL_out = cpc->sysclk;
+ if (cpc->pllmr[1] & 0x40000000) {
+ /* Pretend the PLL is not locked */
+ cpc->boot &= ~0x00000001;
+ }
+ }
+ /* Now, compute all other clocks */
+ D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("CCDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 20) & 0x3, D);
+#endif
+ CPU_clk = PLL_out / D;
+ D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("CBDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 16) & 0x3, D);
+#endif
+ PLB_clk = CPU_clk / D;
+ D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("OPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 12) & 0x3, D);
+#endif
+ OPB_clk = PLB_clk / D;
+ D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("EPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 8) & 0x3, D);
+#endif
+ EBC_clk = PLB_clk / D;
+ D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("MPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 4) & 0x3, D);
+#endif
+ MAL_clk = PLB_clk / D;
+ D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("PPDV %01" PRIx32 " %d\n", cpc->pllmr[0] & 0x3, D);
+#endif
+ PCI_clk = PLB_clk / D;
+ D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("U0DIV %01" PRIx32 " %d\n", cpc->ucr & 0x7F, D);
+#endif
+ UART0_clk = PLL_out / D;
+ D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */
+#ifdef DEBUG_CLOCKS_LL
+ printf("U1DIV %01" PRIx32 " %d\n", (cpc->ucr >> 8) & 0x7F, D);
+#endif
+ UART1_clk = PLL_out / D;
+#ifdef DEBUG_CLOCKS
+ printf("Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64
+ " PLL out %" PRIu64 " Hz\n", cpc->sysclk, VCO_out, PLL_out);
+ printf("CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32
+ " MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32
+ " UART1 %" PRIu32 "\n",
+ CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk,
+ UART0_clk, UART1_clk);
+#endif
+ /* Setup CPU clocks */
+ clk_setup(&cpc->clk_setup[PPC405EP_CPU_CLK], CPU_clk);
+ /* Setup PLB clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_PLB_CLK], PLB_clk);
+ /* Setup OPB clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_OPB_CLK], OPB_clk);
+ /* Setup external clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_EBC_CLK], EBC_clk);
+ /* Setup MAL clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_MAL_CLK], MAL_clk);
+ /* Setup PCI clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_PCI_CLK], PCI_clk);
+ /* Setup UART0 clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_UART0_CLK], UART0_clk);
+ /* Setup UART1 clock */
+ clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk);
+}
+
+static uint32_t dcr_read_epcpc (void *opaque, int dcrn)
+{
+ ppc405ep_cpc_t *cpc;
+ uint32_t ret;
+
+ cpc = opaque;
+ switch (dcrn) {
+ case PPC405EP_CPC0_BOOT:
+ ret = cpc->boot;
+ break;
+ case PPC405EP_CPC0_EPCTL:
+ ret = cpc->epctl;
+ break;
+ case PPC405EP_CPC0_PLLMR0:
+ ret = cpc->pllmr[0];
+ break;
+ case PPC405EP_CPC0_PLLMR1:
+ ret = cpc->pllmr[1];
+ break;
+ case PPC405EP_CPC0_UCR:
+ ret = cpc->ucr;
+ break;
+ case PPC405EP_CPC0_SRR:
+ ret = cpc->srr;
+ break;
+ case PPC405EP_CPC0_JTAGID:
+ ret = cpc->jtagid;
+ break;
+ case PPC405EP_CPC0_PCI:
+ ret = cpc->pci;
+ break;
+ default:
+ /* Avoid gcc warning */
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_epcpc (void *opaque, int dcrn, uint32_t val)
+{
+ ppc405ep_cpc_t *cpc;
+
+ cpc = opaque;
+ switch (dcrn) {
+ case PPC405EP_CPC0_BOOT:
+ /* Read-only register */
+ break;
+ case PPC405EP_CPC0_EPCTL:
+ /* Don't care for now */
+ cpc->epctl = val & 0xC00000F3;
+ break;
+ case PPC405EP_CPC0_PLLMR0:
+ cpc->pllmr[0] = val & 0x00633333;
+ ppc405ep_compute_clocks(cpc);
+ break;
+ case PPC405EP_CPC0_PLLMR1:
+ cpc->pllmr[1] = val & 0xC0F73FFF;
+ ppc405ep_compute_clocks(cpc);
+ break;
+ case PPC405EP_CPC0_UCR:
+ /* UART control - don't care for now */
+ cpc->ucr = val & 0x003F7F7F;
+ break;
+ case PPC405EP_CPC0_SRR:
+ cpc->srr = val;
+ break;
+ case PPC405EP_CPC0_JTAGID:
+ /* Read-only */
+ break;
+ case PPC405EP_CPC0_PCI:
+ cpc->pci = val;
+ break;
+ }
+}
+
+static void ppc405ep_cpc_reset (void *opaque)
+{
+ ppc405ep_cpc_t *cpc = opaque;
+
+ cpc->boot = 0x00000010; /* Boot from PCI - IIC EEPROM disabled */
+ cpc->epctl = 0x00000000;
+ cpc->pllmr[0] = 0x00011010;
+ cpc->pllmr[1] = 0x40000000;
+ cpc->ucr = 0x00000000;
+ cpc->srr = 0x00040000;
+ cpc->pci = 0x00000000;
+ cpc->er = 0x00000000;
+ cpc->fr = 0x00000000;
+ cpc->sr = 0x00000000;
+ ppc405ep_compute_clocks(cpc);
+}
+
+/* XXX: sysclk should be between 25 and 100 MHz */
+static void ppc405ep_cpc_init (CPUState *env, clk_setup_t clk_setup[8],
+ uint32_t sysclk)
+{
+ ppc405ep_cpc_t *cpc;
+
+ cpc = qemu_mallocz(sizeof(ppc405ep_cpc_t));
+ memcpy(cpc->clk_setup, clk_setup,
+ PPC405EP_CLK_NB * sizeof(clk_setup_t));
+ cpc->jtagid = 0x20267049;
+ cpc->sysclk = sysclk;
+ qemu_register_reset(&ppc405ep_cpc_reset, cpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_BOOT, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_EPCTL, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_PLLMR0, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_PLLMR1, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_UCR, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_SRR, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_JTAGID, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_PCI, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+#if 0
+ ppc_dcr_register(env, PPC405EP_CPC0_ER, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_FR, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+ ppc_dcr_register(env, PPC405EP_CPC0_SR, cpc,
+ &dcr_read_epcpc, &dcr_write_epcpc);
+#endif
+}
+
+CPUState *ppc405ep_init (target_phys_addr_t ram_bases[2],
+ target_phys_addr_t ram_sizes[2],
+ uint32_t sysclk, qemu_irq **picp,
+ int do_init)
+{
+ clk_setup_t clk_setup[PPC405EP_CLK_NB], tlb_clk_setup;
+ qemu_irq dma_irqs[4], gpt_irqs[5], mal_irqs[4];
+ CPUState *env;
+ qemu_irq *pic, *irqs;
+
+ memset(clk_setup, 0, sizeof(clk_setup));
+ /* init CPUs */
+ env = ppc4xx_init("405ep", &clk_setup[PPC405EP_CPU_CLK],
+ &tlb_clk_setup, sysclk);
+ clk_setup[PPC405EP_CPU_CLK].cb = tlb_clk_setup.cb;
+ clk_setup[PPC405EP_CPU_CLK].opaque = tlb_clk_setup.opaque;
+ /* Internal devices init */
+ /* Memory mapped devices registers */
+ /* PLB arbitrer */
+ ppc4xx_plb_init(env);
+ /* PLB to OPB bridge */
+ ppc4xx_pob_init(env);
+ /* OBP arbitrer */
+ ppc4xx_opba_init(0xef600600);
+ /* Universal interrupt controller */
+ irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+ irqs[PPCUIC_OUTPUT_INT] =
+ ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
+ irqs[PPCUIC_OUTPUT_CINT] =
+ ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
+ pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
+ *picp = pic;
+ /* SDRAM controller */
+ /* XXX 405EP has no ECC interrupt */
+ ppc4xx_sdram_init(env, pic[17], 2, ram_bases, ram_sizes, do_init);
+ /* External bus controller */
+ ppc405_ebc_init(env);
+ /* DMA controller */
+ dma_irqs[0] = pic[5];
+ dma_irqs[1] = pic[6];
+ dma_irqs[2] = pic[7];
+ dma_irqs[3] = pic[8];
+ ppc405_dma_init(env, dma_irqs);
+ /* IIC controller */
+ ppc405_i2c_init(0xef600500, pic[2]);
+ /* GPIO */
+ ppc405_gpio_init(0xef600700);
+ /* Serial ports */
+ if (serial_hds[0] != NULL) {
+ serial_mm_init(0xef600300, 0, pic[0], PPC_SERIAL_MM_BAUDBASE,
+ serial_hds[0], 1, 1);
+ }
+ if (serial_hds[1] != NULL) {
+ serial_mm_init(0xef600400, 0, pic[1], PPC_SERIAL_MM_BAUDBASE,
+ serial_hds[1], 1, 1);
+ }
+ /* OCM */
+ ppc405_ocm_init(env);
+ /* GPT */
+ gpt_irqs[0] = pic[19];
+ gpt_irqs[1] = pic[20];
+ gpt_irqs[2] = pic[21];
+ gpt_irqs[3] = pic[22];
+ gpt_irqs[4] = pic[23];
+ ppc4xx_gpt_init(0xef600000, gpt_irqs);
+ /* PCI */
+ /* Uses pic[3], pic[16], pic[18] */
+ /* MAL */
+ mal_irqs[0] = pic[11];
+ mal_irqs[1] = pic[12];
+ mal_irqs[2] = pic[13];
+ mal_irqs[3] = pic[14];
+ ppc405_mal_init(env, mal_irqs);
+ /* Ethernet */
+ /* Uses pic[9], pic[15], pic[17] */
+ /* CPU control */
+ ppc405ep_cpc_init(env, clk_setup, sysclk);
+
+ return env;
+}
diff --git a/hw/ppc440.c b/hw/ppc440.c
new file mode 100644
index 0000000..7d651e9
--- /dev/null
+++ b/hw/ppc440.c
@@ -0,0 +1,101 @@
+/*
+ * Qemu PowerPC 440 chip emulation
+ *
+ * Copyright 2007 IBM Corporation.
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "isa.h"
+#include "ppc.h"
+#include "ppc4xx.h"
+#include "ppc440.h"
+#include "ppc405.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "qemu-kvm.h"
+
+#define PPC440EP_PCI_CONFIG 0xeec00000
+#define PPC440EP_PCI_INTACK 0xeed00000
+#define PPC440EP_PCI_SPECIAL 0xeed00000
+#define PPC440EP_PCI_REGS 0xef400000
+#define PPC440EP_PCI_IO 0xe8000000
+#define PPC440EP_PCI_IOLEN 0x00010000
+
+#define PPC440EP_SDRAM_NR_BANKS 4
+
+static const unsigned int ppc440ep_sdram_bank_sizes[] = {
+ 256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0
+};
+
+CPUState *ppc440ep_init(ram_addr_t *ram_size, PCIBus **pcip,
+ const unsigned int pci_irq_nrs[4], int do_init,
+ const char *cpu_model)
+{
+ target_phys_addr_t ram_bases[PPC440EP_SDRAM_NR_BANKS];
+ target_phys_addr_t ram_sizes[PPC440EP_SDRAM_NR_BANKS];
+ CPUState *env;
+ qemu_irq *pic;
+ qemu_irq *irqs;
+ qemu_irq *pci_irqs;
+
+ if (cpu_model == NULL)
+ cpu_model = "405"; // XXX: should be 440EP
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to initialize CPU!\n");
+ exit(1);
+ }
+
+ ppc_dcr_init(env, NULL, NULL);
+
+ /* interrupt controller */
+ irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+ irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
+ irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
+ pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
+
+ /* SDRAM controller */
+ memset(ram_bases, 0, sizeof(ram_bases));
+ memset(ram_sizes, 0, sizeof(ram_sizes));
+ *ram_size = ppc4xx_sdram_adjust(*ram_size, PPC440EP_SDRAM_NR_BANKS,
+ ram_bases, ram_sizes,
+ ppc440ep_sdram_bank_sizes);
+ /* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */
+ ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_bases,
+ ram_sizes, do_init);
+
+ /* PCI */
+ pci_irqs = qemu_malloc(sizeof(qemu_irq) * 4);
+ pci_irqs[0] = pic[pci_irq_nrs[0]];
+ pci_irqs[1] = pic[pci_irq_nrs[1]];
+ pci_irqs[2] = pic[pci_irq_nrs[2]];
+ pci_irqs[3] = pic[pci_irq_nrs[3]];
+ *pcip = ppc4xx_pci_init(env, pci_irqs,
+ PPC440EP_PCI_CONFIG,
+ PPC440EP_PCI_INTACK,
+ PPC440EP_PCI_SPECIAL,
+ PPC440EP_PCI_REGS);
+ if (!*pcip)
+ printf("couldn't create PCI controller!\n");
+
+ isa_mmio_init(PPC440EP_PCI_IO, PPC440EP_PCI_IOLEN);
+
+ if (serial_hds[0] != NULL) {
+ serial_mm_init(0xef600300, 0, pic[0], PPC_SERIAL_MM_BAUDBASE,
+ serial_hds[0], 1, 1);
+ }
+ if (serial_hds[1] != NULL) {
+ serial_mm_init(0xef600400, 0, pic[1], PPC_SERIAL_MM_BAUDBASE,
+ serial_hds[1], 1, 1);
+ }
+
+ return env;
+}
diff --git a/hw/ppc440.h b/hw/ppc440.h
new file mode 100644
index 0000000..a40f917
--- /dev/null
+++ b/hw/ppc440.h
@@ -0,0 +1,21 @@
+/*
+ * Qemu PowerPC 440 board emualtion
+ *
+ * Copyright 2007 IBM Corporation.
+ * Authors: Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the GNU GPL licence version 2 or later
+ *
+ */
+
+#ifndef QEMU_PPC440_H
+#define QEMU_PPC440_H
+
+#include "hw.h"
+
+CPUState *ppc440ep_init(ram_addr_t *ram_size, PCIBus **pcip,
+ const unsigned int pci_irq_nrs[4], int do_init,
+ const char *cpu_model);
+
+#endif
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
new file mode 100644
index 0000000..b16864f
--- /dev/null
+++ b/hw/ppc440_bamboo.c
@@ -0,0 +1,204 @@
+/*
+ * Qemu PowerPC 440 Bamboo board emulation
+ *
+ * Copyright 2007 IBM Corporation.
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#include "config.h"
+#include "qemu-common.h"
+#include "net.h"
+#include "hw.h"
+#include "pci.h"
+#include "boards.h"
+#include "sysemu.h"
+#include "ppc440.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+#include "device_tree.h"
+#include "loader.h"
+#include "elf.h"
+#include "qemu-kvm.h"
+
+#define BINARY_DEVICE_TREE_FILE "bamboo.dtb"
+
+/* from u-boot */
+#define KERNEL_ADDR 0x1000000
+#define FDT_ADDR 0x1800000
+#define RAMDISK_ADDR 0x1900000
+
+static int bamboo_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ int ret = -1;
+#ifdef CONFIG_FDT
+ uint32_t mem_reg_property[] = { 0, 0, ramsize };
+ char *filename;
+ int fdt_size;
+ void *fdt;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (!filename) {
+ goto out;
+ }
+ fdt = load_device_tree(filename, &fdt_size);
+ qemu_free(filename);
+ if (fdt == NULL) {
+ goto out;
+ }
+
+ /* Manipulate device tree in memory. */
+
+ ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
+ sizeof(mem_reg_property));
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /memory/reg\n");
+
+ ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ initrd_base);
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
+
+ ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ (initrd_base + initrd_size));
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
+
+ ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+ kernel_cmdline);
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+
+ if (kvm_enabled())
+ kvmppc_fdt_update(fdt);
+
+ ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
+ qemu_free(fdt);
+
+out:
+#endif
+
+ return ret;
+}
+
+static void bamboo_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 };
+ PCIBus *pcibus;
+ CPUState *env;
+ uint64_t elf_entry;
+ uint64_t elf_lowaddr;
+ target_phys_addr_t entry = 0;
+ target_phys_addr_t loadaddr = 0;
+ target_long initrd_size = 0;
+ int success;
+ int i;
+
+ /* Setup CPU. */
+ env = ppc440ep_init(&ram_size, &pcibus, pci_irq_nrs, 1, cpu_model);
+
+ if (pcibus) {
+ /* Register network interfaces. */
+ for (i = 0; i < nb_nics; i++) {
+ /* There are no PCI NICs on the Bamboo board, but there are
+ * PCI slots, so we can pick whatever default model we want. */
+ pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
+ }
+ }
+
+ /* Load kernel. */
+ if (kernel_filename) {
+ success = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
+ if (success < 0) {
+ success = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ entry = elf_entry;
+ loadaddr = elf_lowaddr;
+ }
+ /* XXX try again as binary */
+ if (success < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ }
+
+ /* Load initrd. */
+ if (initrd_filename) {
+ initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR,
+ ram_size - RAMDISK_ADDR);
+
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n",
+ initrd_filename, RAMDISK_ADDR);
+ exit(1);
+ }
+ }
+
+ /* If we're loading a kernel directly, we must load the device tree too. */
+ if (kernel_filename) {
+ if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR,
+ initrd_size, kernel_cmdline) < 0) {
+ fprintf(stderr, "couldn't load device tree\n");
+ exit(1);
+ }
+
+ cpu_synchronize_state(env);
+
+ /* Set initial guest state. */
+ env->gpr[1] = (16<<20) - 8;
+ env->gpr[3] = FDT_ADDR;
+ env->nip = entry;
+ /* XXX we currently depend on KVM to create some initial TLB entries. */
+ }
+
+ if (kvm_enabled())
+ kvmppc_init();
+}
+
+static QEMUMachine bamboo_machine = {
+ .name = "bamboo-0.13",
+ .alias = "bamboo",
+ .desc = "bamboo",
+ .init = bamboo_init,
+};
+
+static QEMUMachine bamboo_machine_v0_12 = {
+ .name = "bamboo-0.12",
+ .desc = "bamboo",
+ .init = bamboo_init,
+ .compat_props = (GlobalProperty[]) {
+ {
+ .driver = "virtio-serial-pci",
+ .property = "max_ports",
+ .value = stringify(1),
+ },{
+ .driver = "virtio-serial-pci",
+ .property = "vectors",
+ .value = stringify(0),
+ },
+ { /* end of list */ }
+ },
+};
+
+static void bamboo_machine_init(void)
+{
+ qemu_register_machine(&bamboo_machine);
+ qemu_register_machine(&bamboo_machine_v0_12);
+}
+
+machine_init(bamboo_machine_init);
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
new file mode 100644
index 0000000..bc4ee01
--- /dev/null
+++ b/hw/ppc4xx.h
@@ -0,0 +1,60 @@
+/*
+ * QEMU PowerPC 4xx emulation shared definitions
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if !defined(PPC_4XX_H)
+#define PPC_4XX_H
+
+#include "pci.h"
+
+/* PowerPC 4xx core initialization */
+CPUState *ppc4xx_init (const char *cpu_model,
+ clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+ uint32_t sysclk);
+
+/* PowerPC 4xx universal interrupt controller */
+enum {
+ PPCUIC_OUTPUT_INT = 0,
+ PPCUIC_OUTPUT_CINT = 1,
+ PPCUIC_OUTPUT_NB,
+};
+qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
+ uint32_t dcr_base, int has_ssr, int has_vr);
+
+ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
+ target_phys_addr_t ram_bases[],
+ target_phys_addr_t ram_sizes[],
+ const unsigned int sdram_bank_sizes[]);
+
+void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
+ target_phys_addr_t *ram_bases,
+ target_phys_addr_t *ram_sizes,
+ int do_init);
+
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers);
+
+#endif /* !defined(PPC_4XX_H) */
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
new file mode 100644
index 0000000..5f581fe
--- /dev/null
+++ b/hw/ppc4xx_devs.c
@@ -0,0 +1,691 @@
+/*
+ * QEMU PowerPC 4xx embedded processors shared devices emulation
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "ppc4xx.h"
+#include "sysemu.h"
+#include "qemu-log.h"
+
+//#define DEBUG_MMIO
+//#define DEBUG_UNASSIGNED
+#define DEBUG_UIC
+
+
+#ifdef DEBUG_UIC
+# define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
+#else
+# define LOG_UIC(...) do { } while (0)
+#endif
+
+/*****************************************************************************/
+/* Generic PowerPC 4xx processor instanciation */
+CPUState *ppc4xx_init (const char *cpu_model,
+ clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+ uint32_t sysclk)
+{
+ CPUState *env;
+
+ /* init CPUs */
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find PowerPC %s CPU definition\n",
+ cpu_model);
+ exit(1);
+ }
+ cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
+ cpu_clk->opaque = env;
+ /* Set time-base frequency to sysclk */
+ tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
+ tb_clk->opaque = env;
+ ppc_dcr_init(env, NULL, NULL);
+ /* Register qemu callbacks */
+ qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+
+ return env;
+}
+
+/*****************************************************************************/
+/* "Universal" Interrupt controller */
+enum {
+ DCR_UICSR = 0x000,
+ DCR_UICSRS = 0x001,
+ DCR_UICER = 0x002,
+ DCR_UICCR = 0x003,
+ DCR_UICPR = 0x004,
+ DCR_UICTR = 0x005,
+ DCR_UICMSR = 0x006,
+ DCR_UICVR = 0x007,
+ DCR_UICVCR = 0x008,
+ DCR_UICMAX = 0x009,
+};
+
+#define UIC_MAX_IRQ 32
+typedef struct ppcuic_t ppcuic_t;
+struct ppcuic_t {
+ uint32_t dcr_base;
+ int use_vectors;
+ uint32_t level; /* Remembers the state of level-triggered interrupts. */
+ uint32_t uicsr; /* Status register */
+ uint32_t uicer; /* Enable register */
+ uint32_t uiccr; /* Critical register */
+ uint32_t uicpr; /* Polarity register */
+ uint32_t uictr; /* Triggering register */
+ uint32_t uicvcr; /* Vector configuration register */
+ uint32_t uicvr;
+ qemu_irq *irqs;
+};
+
+static void ppcuic_trigger_irq (ppcuic_t *uic)
+{
+ uint32_t ir, cr;
+ int start, end, inc, i;
+
+ /* Trigger interrupt if any is pending */
+ ir = uic->uicsr & uic->uicer & (~uic->uiccr);
+ cr = uic->uicsr & uic->uicer & uic->uiccr;
+ LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32
+ " uiccr %08" PRIx32 "\n"
+ " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n",
+ __func__, uic->uicsr, uic->uicer, uic->uiccr,
+ uic->uicsr & uic->uicer, ir, cr);
+ if (ir != 0x0000000) {
+ LOG_UIC("Raise UIC interrupt\n");
+ qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]);
+ } else {
+ LOG_UIC("Lower UIC interrupt\n");
+ qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]);
+ }
+ /* Trigger critical interrupt if any is pending and update vector */
+ if (cr != 0x0000000) {
+ qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]);
+ if (uic->use_vectors) {
+ /* Compute critical IRQ vector */
+ if (uic->uicvcr & 1) {
+ start = 31;
+ end = 0;
+ inc = -1;
+ } else {
+ start = 0;
+ end = 31;
+ inc = 1;
+ }
+ uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
+ for (i = start; i <= end; i += inc) {
+ if (cr & (1 << i)) {
+ uic->uicvr += (i - start) * 512 * inc;
+ break;
+ }
+ }
+ }
+ LOG_UIC("Raise UIC critical interrupt - "
+ "vector %08" PRIx32 "\n", uic->uicvr);
+ } else {
+ LOG_UIC("Lower UIC critical interrupt\n");
+ qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]);
+ uic->uicvr = 0x00000000;
+ }
+}
+
+static void ppcuic_set_irq (void *opaque, int irq_num, int level)
+{
+ ppcuic_t *uic;
+ uint32_t mask, sr;
+
+ uic = opaque;
+ mask = 1 << (31-irq_num);
+ LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32
+ " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n",
+ __func__, irq_num, level,
+ uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
+ if (irq_num < 0 || irq_num > 31)
+ return;
+ sr = uic->uicsr;
+
+ /* Update status register */
+ if (uic->uictr & mask) {
+ /* Edge sensitive interrupt */
+ if (level == 1)
+ uic->uicsr |= mask;
+ } else {
+ /* Level sensitive interrupt */
+ if (level == 1) {
+ uic->uicsr |= mask;
+ uic->level |= mask;
+ } else {
+ uic->uicsr &= ~mask;
+ uic->level &= ~mask;
+ }
+ }
+ LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => "
+ "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr);
+ if (sr != uic->uicsr)
+ ppcuic_trigger_irq(uic);
+}
+
+static uint32_t dcr_read_uic (void *opaque, int dcrn)
+{
+ ppcuic_t *uic;
+ uint32_t ret;
+
+ uic = opaque;
+ dcrn -= uic->dcr_base;
+ switch (dcrn) {
+ case DCR_UICSR:
+ case DCR_UICSRS:
+ ret = uic->uicsr;
+ break;
+ case DCR_UICER:
+ ret = uic->uicer;
+ break;
+ case DCR_UICCR:
+ ret = uic->uiccr;
+ break;
+ case DCR_UICPR:
+ ret = uic->uicpr;
+ break;
+ case DCR_UICTR:
+ ret = uic->uictr;
+ break;
+ case DCR_UICMSR:
+ ret = uic->uicsr & uic->uicer;
+ break;
+ case DCR_UICVR:
+ if (!uic->use_vectors)
+ goto no_read;
+ ret = uic->uicvr;
+ break;
+ case DCR_UICVCR:
+ if (!uic->use_vectors)
+ goto no_read;
+ ret = uic->uicvcr;
+ break;
+ default:
+ no_read:
+ ret = 0x00000000;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_uic (void *opaque, int dcrn, uint32_t val)
+{
+ ppcuic_t *uic;
+
+ uic = opaque;
+ dcrn -= uic->dcr_base;
+ LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val);
+ switch (dcrn) {
+ case DCR_UICSR:
+ uic->uicsr &= ~val;
+ uic->uicsr |= uic->level;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICSRS:
+ uic->uicsr |= val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICER:
+ uic->uicer = val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICCR:
+ uic->uiccr = val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICPR:
+ uic->uicpr = val;
+ break;
+ case DCR_UICTR:
+ uic->uictr = val;
+ ppcuic_trigger_irq(uic);
+ break;
+ case DCR_UICMSR:
+ break;
+ case DCR_UICVR:
+ break;
+ case DCR_UICVCR:
+ uic->uicvcr = val & 0xFFFFFFFD;
+ ppcuic_trigger_irq(uic);
+ break;
+ }
+}
+
+static void ppcuic_reset (void *opaque)
+{
+ ppcuic_t *uic;
+
+ uic = opaque;
+ uic->uiccr = 0x00000000;
+ uic->uicer = 0x00000000;
+ uic->uicpr = 0x00000000;
+ uic->uicsr = 0x00000000;
+ uic->uictr = 0x00000000;
+ if (uic->use_vectors) {
+ uic->uicvcr = 0x00000000;
+ uic->uicvr = 0x0000000;
+ }
+}
+
+qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
+ uint32_t dcr_base, int has_ssr, int has_vr)
+{
+ ppcuic_t *uic;
+ int i;
+
+ uic = qemu_mallocz(sizeof(ppcuic_t));
+ uic->dcr_base = dcr_base;
+ uic->irqs = irqs;
+ if (has_vr)
+ uic->use_vectors = 1;
+ for (i = 0; i < DCR_UICMAX; i++) {
+ ppc_dcr_register(env, dcr_base + i, uic,
+ &dcr_read_uic, &dcr_write_uic);
+ }
+ qemu_register_reset(ppcuic_reset, uic);
+
+ return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ);
+}
+
+/*****************************************************************************/
+/* SDRAM controller */
+typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
+struct ppc4xx_sdram_t {
+ uint32_t addr;
+ int nbanks;
+ target_phys_addr_t ram_bases[4];
+ target_phys_addr_t ram_sizes[4];
+ uint32_t besr0;
+ uint32_t besr1;
+ uint32_t bear;
+ uint32_t cfg;
+ uint32_t status;
+ uint32_t rtr;
+ uint32_t pmit;
+ uint32_t bcr[4];
+ uint32_t tr;
+ uint32_t ecccfg;
+ uint32_t eccesr;
+ qemu_irq irq;
+};
+
+enum {
+ SDRAM0_CFGADDR = 0x010,
+ SDRAM0_CFGDATA = 0x011,
+};
+
+/* XXX: TOFIX: some patches have made this code become inconsistent:
+ * there are type inconsistencies, mixing target_phys_addr_t, target_ulong
+ * and uint32_t
+ */
+static uint32_t sdram_bcr (target_phys_addr_t ram_base,
+ target_phys_addr_t ram_size)
+{
+ uint32_t bcr;
+
+ switch (ram_size) {
+ case (4 * 1024 * 1024):
+ bcr = 0x00000000;
+ break;
+ case (8 * 1024 * 1024):
+ bcr = 0x00020000;
+ break;
+ case (16 * 1024 * 1024):
+ bcr = 0x00040000;
+ break;
+ case (32 * 1024 * 1024):
+ bcr = 0x00060000;
+ break;
+ case (64 * 1024 * 1024):
+ bcr = 0x00080000;
+ break;
+ case (128 * 1024 * 1024):
+ bcr = 0x000A0000;
+ break;
+ case (256 * 1024 * 1024):
+ bcr = 0x000C0000;
+ break;
+ default:
+ printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__,
+ ram_size);
+ return 0x00000000;
+ }
+ bcr |= ram_base & 0xFF800000;
+ bcr |= 1;
+
+ return bcr;
+}
+
+static inline target_phys_addr_t sdram_base(uint32_t bcr)
+{
+ return bcr & 0xFF800000;
+}
+
+static target_ulong sdram_size (uint32_t bcr)
+{
+ target_ulong size;
+ int sh;
+
+ sh = (bcr >> 17) & 0x7;
+ if (sh == 7)
+ size = -1;
+ else
+ size = (4 * 1024 * 1024) << sh;
+
+ return size;
+}
+
+static void sdram_set_bcr (uint32_t *bcrp, uint32_t bcr, int enabled)
+{
+ if (*bcrp & 0x00000001) {
+ /* Unmap RAM */
+#ifdef DEBUG_SDRAM
+ printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ __func__, sdram_base(*bcrp), sdram_size(*bcrp));
+#endif
+ cpu_register_physical_memory(sdram_base(*bcrp), sdram_size(*bcrp),
+ IO_MEM_UNASSIGNED);
+ }
+ *bcrp = bcr & 0xFFDEE001;
+ if (enabled && (bcr & 0x00000001)) {
+#ifdef DEBUG_SDRAM
+ printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ __func__, sdram_base(bcr), sdram_size(bcr));
+#endif
+ cpu_register_physical_memory(sdram_base(bcr), sdram_size(bcr),
+ sdram_base(bcr) | IO_MEM_RAM);
+ }
+}
+
+static void sdram_map_bcr (ppc4xx_sdram_t *sdram)
+{
+ int i;
+
+ for (i = 0; i < sdram->nbanks; i++) {
+ if (sdram->ram_sizes[i] != 0) {
+ sdram_set_bcr(&sdram->bcr[i],
+ sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]),
+ 1);
+ } else {
+ sdram_set_bcr(&sdram->bcr[i], 0x00000000, 0);
+ }
+ }
+}
+
+static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
+{
+ int i;
+
+ for (i = 0; i < sdram->nbanks; i++) {
+#ifdef DEBUG_SDRAM
+ printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i]));
+#endif
+ cpu_register_physical_memory(sdram_base(sdram->bcr[i]),
+ sdram_size(sdram->bcr[i]),
+ IO_MEM_UNASSIGNED);
+ }
+}
+
+static uint32_t dcr_read_sdram (void *opaque, int dcrn)
+{
+ ppc4xx_sdram_t *sdram;
+ uint32_t ret;
+
+ sdram = opaque;
+ switch (dcrn) {
+ case SDRAM0_CFGADDR:
+ ret = sdram->addr;
+ break;
+ case SDRAM0_CFGDATA:
+ switch (sdram->addr) {
+ case 0x00: /* SDRAM_BESR0 */
+ ret = sdram->besr0;
+ break;
+ case 0x08: /* SDRAM_BESR1 */
+ ret = sdram->besr1;
+ break;
+ case 0x10: /* SDRAM_BEAR */
+ ret = sdram->bear;
+ break;
+ case 0x20: /* SDRAM_CFG */
+ ret = sdram->cfg;
+ break;
+ case 0x24: /* SDRAM_STATUS */
+ ret = sdram->status;
+ break;
+ case 0x30: /* SDRAM_RTR */
+ ret = sdram->rtr;
+ break;
+ case 0x34: /* SDRAM_PMIT */
+ ret = sdram->pmit;
+ break;
+ case 0x40: /* SDRAM_B0CR */
+ ret = sdram->bcr[0];
+ break;
+ case 0x44: /* SDRAM_B1CR */
+ ret = sdram->bcr[1];
+ break;
+ case 0x48: /* SDRAM_B2CR */
+ ret = sdram->bcr[2];
+ break;
+ case 0x4C: /* SDRAM_B3CR */
+ ret = sdram->bcr[3];
+ break;
+ case 0x80: /* SDRAM_TR */
+ ret = -1; /* ? */
+ break;
+ case 0x94: /* SDRAM_ECCCFG */
+ ret = sdram->ecccfg;
+ break;
+ case 0x98: /* SDRAM_ECCESR */
+ ret = sdram->eccesr;
+ break;
+ default: /* Error */
+ ret = -1;
+ break;
+ }
+ break;
+ default:
+ /* Avoid gcc warning */
+ ret = 0x00000000;
+ break;
+ }
+
+ return ret;
+}
+
+static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val)
+{
+ ppc4xx_sdram_t *sdram;
+
+ sdram = opaque;
+ switch (dcrn) {
+ case SDRAM0_CFGADDR:
+ sdram->addr = val;
+ break;
+ case SDRAM0_CFGDATA:
+ switch (sdram->addr) {
+ case 0x00: /* SDRAM_BESR0 */
+ sdram->besr0 &= ~val;
+ break;
+ case 0x08: /* SDRAM_BESR1 */
+ sdram->besr1 &= ~val;
+ break;
+ case 0x10: /* SDRAM_BEAR */
+ sdram->bear = val;
+ break;
+ case 0x20: /* SDRAM_CFG */
+ val &= 0xFFE00000;
+ if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
+#ifdef DEBUG_SDRAM
+ printf("%s: enable SDRAM controller\n", __func__);
+#endif
+ /* validate all RAM mappings */
+ sdram_map_bcr(sdram);
+ sdram->status &= ~0x80000000;
+ } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
+#ifdef DEBUG_SDRAM
+ printf("%s: disable SDRAM controller\n", __func__);
+#endif
+ /* invalidate all RAM mappings */
+ sdram_unmap_bcr(sdram);
+ sdram->status |= 0x80000000;
+ }
+ if (!(sdram->cfg & 0x40000000) && (val & 0x40000000))
+ sdram->status |= 0x40000000;
+ else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000))
+ sdram->status &= ~0x40000000;
+ sdram->cfg = val;
+ break;
+ case 0x24: /* SDRAM_STATUS */
+ /* Read-only register */
+ break;
+ case 0x30: /* SDRAM_RTR */
+ sdram->rtr = val & 0x3FF80000;
+ break;
+ case 0x34: /* SDRAM_PMIT */
+ sdram->pmit = (val & 0xF8000000) | 0x07C00000;
+ break;
+ case 0x40: /* SDRAM_B0CR */
+ sdram_set_bcr(&sdram->bcr[0], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x44: /* SDRAM_B1CR */
+ sdram_set_bcr(&sdram->bcr[1], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x48: /* SDRAM_B2CR */
+ sdram_set_bcr(&sdram->bcr[2], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x4C: /* SDRAM_B3CR */
+ sdram_set_bcr(&sdram->bcr[3], val, sdram->cfg & 0x80000000);
+ break;
+ case 0x80: /* SDRAM_TR */
+ sdram->tr = val & 0x018FC01F;
+ break;
+ case 0x94: /* SDRAM_ECCCFG */
+ sdram->ecccfg = val & 0x00F00000;
+ break;
+ case 0x98: /* SDRAM_ECCESR */
+ val &= 0xFFF0F000;
+ if (sdram->eccesr == 0 && val != 0)
+ qemu_irq_raise(sdram->irq);
+ else if (sdram->eccesr != 0 && val == 0)
+ qemu_irq_lower(sdram->irq);
+ sdram->eccesr = val;
+ break;
+ default: /* Error */
+ break;
+ }
+ break;
+ }
+}
+
+static void sdram_reset (void *opaque)
+{
+ ppc4xx_sdram_t *sdram;
+
+ sdram = opaque;
+ sdram->addr = 0x00000000;
+ sdram->bear = 0x00000000;
+ sdram->besr0 = 0x00000000; /* No error */
+ sdram->besr1 = 0x00000000; /* No error */
+ sdram->cfg = 0x00000000;
+ sdram->ecccfg = 0x00000000; /* No ECC */
+ sdram->eccesr = 0x00000000; /* No error */
+ sdram->pmit = 0x07C00000;
+ sdram->rtr = 0x05F00000;
+ sdram->tr = 0x00854009;
+ /* We pre-initialize RAM banks */
+ sdram->status = 0x00000000;
+ sdram->cfg = 0x00800000;
+}
+
+void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
+ target_phys_addr_t *ram_bases,
+ target_phys_addr_t *ram_sizes,
+ int do_init)
+{
+ ppc4xx_sdram_t *sdram;
+
+ sdram = qemu_mallocz(sizeof(ppc4xx_sdram_t));
+ sdram->irq = irq;
+ sdram->nbanks = nbanks;
+ memset(sdram->ram_bases, 0, 4 * sizeof(target_phys_addr_t));
+ memcpy(sdram->ram_bases, ram_bases,
+ nbanks * sizeof(target_phys_addr_t));
+ memset(sdram->ram_sizes, 0, 4 * sizeof(target_phys_addr_t));
+ memcpy(sdram->ram_sizes, ram_sizes,
+ nbanks * sizeof(target_phys_addr_t));
+ qemu_register_reset(&sdram_reset, sdram);
+ ppc_dcr_register(env, SDRAM0_CFGADDR,
+ sdram, &dcr_read_sdram, &dcr_write_sdram);
+ ppc_dcr_register(env, SDRAM0_CFGDATA,
+ sdram, &dcr_read_sdram, &dcr_write_sdram);
+ if (do_init)
+ sdram_map_bcr(sdram);
+}
+
+/* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory.
+ *
+ * sdram_bank_sizes[] must be 0-terminated.
+ *
+ * The 4xx SDRAM controller supports a small number of banks, and each bank
+ * must be one of a small set of sizes. The number of banks and the supported
+ * sizes varies by SoC. */
+ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
+ target_phys_addr_t ram_bases[],
+ target_phys_addr_t ram_sizes[],
+ const unsigned int sdram_bank_sizes[])
+{
+ ram_addr_t size_left = ram_size;
+ int i;
+ int j;
+
+ for (i = 0; i < nr_banks; i++) {
+ for (j = 0; sdram_bank_sizes[j] != 0; j++) {
+ unsigned int bank_size = sdram_bank_sizes[j];
+
+ if (bank_size <= size_left) {
+ char name[32];
+ snprintf(name, sizeof(name), "ppc4xx.sdram%d", i);
+ ram_bases[i] = qemu_ram_alloc(NULL, name, bank_size);
+ ram_sizes[i] = bank_size;
+ size_left -= bank_size;
+ break;
+ }
+ }
+
+ if (!size_left) {
+ /* No need to use the remaining banks. */
+ break;
+ }
+ }
+
+ ram_size -= size_left;
+ if (size_left)
+ printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n",
+ (int)(ram_size >> 20));
+
+ return ram_size;
+}
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
new file mode 100644
index 0000000..f62f1f9
--- /dev/null
+++ b/hw/ppc4xx_pci.c
@@ -0,0 +1,393 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 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/>.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+/* This file implements emulation of the 32-bit PCI controller found in some
+ * 4xx SoCs, such as the 440EP. */
+
+#include "hw.h"
+#include "ppc.h"
+#include "ppc4xx.h"
+#include "pci.h"
+#include "pci_host.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif /* DEBUG */
+
+struct PCIMasterMap {
+ uint32_t la;
+ uint32_t ma;
+ uint32_t pcila;
+ uint32_t pciha;
+};
+
+struct PCITargetMap {
+ uint32_t ms;
+ uint32_t la;
+};
+
+#define PPC4xx_PCI_NR_PMMS 3
+#define PPC4xx_PCI_NR_PTMS 2
+
+struct PPC4xxPCIState {
+ struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS];
+ struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS];
+
+ PCIHostState pci_state;
+ PCIDevice *pci_dev;
+};
+typedef struct PPC4xxPCIState PPC4xxPCIState;
+
+#define PCIC0_CFGADDR 0x0
+#define PCIC0_CFGDATA 0x4
+
+/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
+ * PCI accesses. */
+#define PCIL0_PMM0LA 0x0
+#define PCIL0_PMM0MA 0x4
+#define PCIL0_PMM0PCILA 0x8
+#define PCIL0_PMM0PCIHA 0xc
+#define PCIL0_PMM1LA 0x10
+#define PCIL0_PMM1MA 0x14
+#define PCIL0_PMM1PCILA 0x18
+#define PCIL0_PMM1PCIHA 0x1c
+#define PCIL0_PMM2LA 0x20
+#define PCIL0_PMM2MA 0x24
+#define PCIL0_PMM2PCILA 0x28
+#define PCIL0_PMM2PCIHA 0x2c
+
+/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
+ * PLB accesses. */
+#define PCIL0_PTM1MS 0x30
+#define PCIL0_PTM1LA 0x34
+#define PCIL0_PTM2MS 0x38
+#define PCIL0_PTM2LA 0x3c
+#define PCI_REG_SIZE 0x40
+
+
+static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr)
+{
+ PPC4xxPCIState *ppc4xx_pci = opaque;
+
+ return ppc4xx_pci->pci_state.config_reg;
+}
+
+static CPUReadMemoryFunc * const pci4xx_cfgaddr_read[] = {
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+};
+
+static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PPC4xxPCIState *ppc4xx_pci = opaque;
+
+ ppc4xx_pci->pci_state.config_reg = value & ~0x3;
+}
+
+static CPUWriteMemoryFunc * const pci4xx_cfgaddr_write[] = {
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+};
+
+static void ppc4xx_pci_reg_write4(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ struct PPC4xxPCIState *pci = opaque;
+
+ /* We ignore all target attempts at PCI configuration, effectively
+ * assuming a bidirectional 1:1 mapping of PLB and PCI space. */
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ pci->pmm[0].la = value;
+ break;
+ case PCIL0_PMM0MA:
+ pci->pmm[0].ma = value;
+ break;
+ case PCIL0_PMM0PCIHA:
+ pci->pmm[0].pciha = value;
+ break;
+ case PCIL0_PMM0PCILA:
+ pci->pmm[0].pcila = value;
+ break;
+
+ case PCIL0_PMM1LA:
+ pci->pmm[1].la = value;
+ break;
+ case PCIL0_PMM1MA:
+ pci->pmm[1].ma = value;
+ break;
+ case PCIL0_PMM1PCIHA:
+ pci->pmm[1].pciha = value;
+ break;
+ case PCIL0_PMM1PCILA:
+ pci->pmm[1].pcila = value;
+ break;
+
+ case PCIL0_PMM2LA:
+ pci->pmm[2].la = value;
+ break;
+ case PCIL0_PMM2MA:
+ pci->pmm[2].ma = value;
+ break;
+ case PCIL0_PMM2PCIHA:
+ pci->pmm[2].pciha = value;
+ break;
+ case PCIL0_PMM2PCILA:
+ pci->pmm[2].pcila = value;
+ break;
+
+ case PCIL0_PTM1MS:
+ pci->ptm[0].ms = value;
+ break;
+ case PCIL0_PTM1LA:
+ pci->ptm[0].la = value;
+ break;
+ case PCIL0_PTM2MS:
+ pci->ptm[1].ms = value;
+ break;
+ case PCIL0_PTM2LA:
+ pci->ptm[1].la = value;
+ break;
+
+ default:
+ printf("%s: unhandled PCI internal register 0x%lx\n", __func__,
+ (unsigned long)offset);
+ break;
+ }
+}
+
+static uint32_t ppc4xx_pci_reg_read4(void *opaque, target_phys_addr_t offset)
+{
+ struct PPC4xxPCIState *pci = opaque;
+ uint32_t value;
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ value = pci->pmm[0].la;
+ break;
+ case PCIL0_PMM0MA:
+ value = pci->pmm[0].ma;
+ break;
+ case PCIL0_PMM0PCIHA:
+ value = pci->pmm[0].pciha;
+ break;
+ case PCIL0_PMM0PCILA:
+ value = pci->pmm[0].pcila;
+ break;
+
+ case PCIL0_PMM1LA:
+ value = pci->pmm[1].la;
+ break;
+ case PCIL0_PMM1MA:
+ value = pci->pmm[1].ma;
+ break;
+ case PCIL0_PMM1PCIHA:
+ value = pci->pmm[1].pciha;
+ break;
+ case PCIL0_PMM1PCILA:
+ value = pci->pmm[1].pcila;
+ break;
+
+ case PCIL0_PMM2LA:
+ value = pci->pmm[2].la;
+ break;
+ case PCIL0_PMM2MA:
+ value = pci->pmm[2].ma;
+ break;
+ case PCIL0_PMM2PCIHA:
+ value = pci->pmm[2].pciha;
+ break;
+ case PCIL0_PMM2PCILA:
+ value = pci->pmm[2].pcila;
+ break;
+
+ case PCIL0_PTM1MS:
+ value = pci->ptm[0].ms;
+ break;
+ case PCIL0_PTM1LA:
+ value = pci->ptm[0].la;
+ break;
+ case PCIL0_PTM2MS:
+ value = pci->ptm[1].ms;
+ break;
+ case PCIL0_PTM2LA:
+ value = pci->ptm[1].la;
+ break;
+
+ default:
+ printf("%s: invalid PCI internal register 0x%lx\n", __func__,
+ (unsigned long)offset);
+ value = 0;
+ }
+
+ return value;
+}
+
+static CPUReadMemoryFunc * const pci_reg_read[] = {
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+};
+
+static CPUWriteMemoryFunc * const pci_reg_write[] = {
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+};
+
+static void ppc4xx_pci_reset(void *opaque)
+{
+ struct PPC4xxPCIState *pci = opaque;
+
+ memset(pci->pmm, 0, sizeof(pci->pmm));
+ memset(pci->ptm, 0, sizeof(pci->ptm));
+}
+
+/* On Bamboo, all pins from each slot are tied to a single board IRQ. This
+ * may need further refactoring for other boards. */
+static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot = pci_dev->devfn >> 3;
+
+ DPRINTF("%s: devfn %x irq %d -> %d\n", __func__,
+ pci_dev->devfn, irq_num, slot);
+
+ return slot - 1;
+}
+
+static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pci_irqs = opaque;
+
+ DPRINTF("%s: PCI irq %d\n", __func__, irq_num);
+ qemu_set_irq(pci_irqs[irq_num], level);
+}
+
+static void ppc4xx_pci_save(QEMUFile *f, void *opaque)
+{
+ PPC4xxPCIState *controller = opaque;
+ int i;
+
+ pci_device_save(controller->pci_dev, f);
+
+ for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) {
+ qemu_put_be32s(f, &controller->pmm[i].la);
+ qemu_put_be32s(f, &controller->pmm[i].ma);
+ qemu_put_be32s(f, &controller->pmm[i].pcila);
+ qemu_put_be32s(f, &controller->pmm[i].pciha);
+ }
+
+ for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) {
+ qemu_put_be32s(f, &controller->ptm[i].ms);
+ qemu_put_be32s(f, &controller->ptm[i].la);
+ }
+}
+
+static int ppc4xx_pci_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PPC4xxPCIState *controller = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ pci_device_load(controller->pci_dev, f);
+
+ for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) {
+ qemu_get_be32s(f, &controller->pmm[i].la);
+ qemu_get_be32s(f, &controller->pmm[i].ma);
+ qemu_get_be32s(f, &controller->pmm[i].pcila);
+ qemu_get_be32s(f, &controller->pmm[i].pciha);
+ }
+
+ for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) {
+ qemu_get_be32s(f, &controller->ptm[i].ms);
+ qemu_get_be32s(f, &controller->ptm[i].la);
+ }
+
+ return 0;
+}
+
+/* XXX Interrupt acknowledge cycles not supported. */
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers)
+{
+ PPC4xxPCIState *controller;
+ int index;
+ static int ppc4xx_pci_id;
+ uint8_t *pci_conf;
+
+ controller = qemu_mallocz(sizeof(PPC4xxPCIState));
+
+ controller->pci_state.bus = pci_register_bus(NULL, "pci",
+ ppc4xx_pci_set_irq,
+ ppc4xx_pci_map_irq,
+ pci_irqs, 0, 4);
+
+ controller->pci_dev = pci_register_device(controller->pci_state.bus,
+ "host bridge", sizeof(PCIDevice),
+ 0, NULL, NULL);
+ pci_conf = controller->pci_dev->config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_IBM);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_IBM_440GX);
+ pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER);
+
+ /* CFGADDR */
+ index = cpu_register_io_memory(pci4xx_cfgaddr_read,
+ pci4xx_cfgaddr_write, controller,
+ DEVICE_LITTLE_ENDIAN);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index);
+
+ /* CFGDATA */
+ index = pci_host_data_register_mmio(&controller->pci_state, 1);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index);
+
+ /* Internal registers */
+ index = cpu_register_io_memory(pci_reg_read, pci_reg_write, controller,
+ DEVICE_LITTLE_ENDIAN);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers, PCI_REG_SIZE, index);
+
+ qemu_register_reset(ppc4xx_pci_reset, controller);
+
+ /* XXX load/save code not tested. */
+ register_savevm(&controller->pci_dev->qdev, "ppc4xx_pci", ppc4xx_pci_id++,
+ 1, ppc4xx_pci_save, ppc4xx_pci_load, controller);
+
+ return controller->pci_state.bus;
+
+free:
+ printf("%s error\n", __func__);
+ qemu_free(controller);
+ return NULL;
+}
diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h
new file mode 100644
index 0000000..ea87593
--- /dev/null
+++ b/hw/ppc_mac.h
@@ -0,0 +1,113 @@
+/*
+ * QEMU PowerMac emulation shared definitions and prototypes
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#if !defined(__PPC_MAC_H__)
+#define __PPC_MAC_H__
+
+/* SMP is not enabled, for now */
+#define MAX_CPUS 1
+
+#define BIOS_SIZE (1024 * 1024)
+#define BIOS_FILENAME "ppc_rom.bin"
+#define NVRAM_SIZE 0x2000
+#define PROM_FILENAME "openbios-ppc"
+#define PROM_ADDR 0xfff00000
+
+#define KERNEL_LOAD_ADDR 0x01000000
+#define CMDLINE_ADDR 0x027ff000
+#define INITRD_LOAD_ADDR 0x02800000
+
+#define ESCC_CLOCK 3686400
+
+/* Cuda */
+void cuda_init (int *cuda_mem_index, qemu_irq irq);
+
+/* MacIO */
+void macio_init (PCIBus *bus, int device_id, int is_oldworld, int pic_mem_index,
+ int dbdma_mem_index, int cuda_mem_index, void *nvram,
+ int nb_ide, int *ide_mem_index, int escc_mem_index);
+
+/* Heathrow PIC */
+qemu_irq *heathrow_pic_init(int *pmem_index,
+ int nb_cpus, qemu_irq **irqs);
+
+/* Grackle PCI */
+PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic);
+
+/* UniNorth PCI */
+PCIBus *pci_pmac_init(qemu_irq *pic);
+PCIBus *pci_pmac_u3_init(qemu_irq *pic);
+
+/* Mac NVRAM */
+typedef struct MacIONVRAMState MacIONVRAMState;
+
+MacIONVRAMState *macio_nvram_init (int *mem_index, target_phys_addr_t size,
+ unsigned int it_shift);
+void macio_nvram_map (void *opaque, target_phys_addr_t mem_base);
+void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len);
+uint32_t macio_nvram_read (void *opaque, uint32_t addr);
+void macio_nvram_write (void *opaque, uint32_t addr, uint32_t val);
+
+/* adb.c */
+
+#define MAX_ADB_DEVICES 16
+
+#define ADB_MAX_OUT_LEN 16
+
+typedef struct ADBDevice ADBDevice;
+
+/* buf = NULL means polling */
+typedef int ADBDeviceRequest(ADBDevice *d, uint8_t *buf_out,
+ const uint8_t *buf, int len);
+typedef int ADBDeviceReset(ADBDevice *d);
+
+struct ADBDevice {
+ struct ADBBusState *bus;
+ int devaddr;
+ int handler;
+ ADBDeviceRequest *devreq;
+ ADBDeviceReset *devreset;
+ void *opaque;
+};
+
+typedef struct ADBBusState {
+ ADBDevice devices[MAX_ADB_DEVICES];
+ int nb_devices;
+ int poll_index;
+} ADBBusState;
+
+int adb_request(ADBBusState *s, uint8_t *buf_out,
+ const uint8_t *buf, int len);
+int adb_poll(ADBBusState *s, uint8_t *buf_out);
+
+ADBDevice *adb_register_device(ADBBusState *s, int devaddr,
+ ADBDeviceRequest *devreq,
+ ADBDeviceReset *devreset,
+ void *opaque);
+void adb_kbd_init(ADBBusState *bus);
+void adb_mouse_init(ADBBusState *bus);
+
+extern ADBBusState adb_bus;
+
+#endif /* !defined(__PPC_MAC_H__) */
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
new file mode 100644
index 0000000..b9245f0
--- /dev/null
+++ b/hw/ppc_newworld.c
@@ -0,0 +1,429 @@
+/*
+ * QEMU PowerPC CHRP (currently NewWorld PowerMac) hardware System Emulator
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * PCI bus layout on a real G5 (U3 based):
+ *
+ * 0000:f0:0b.0 Host bridge [0600]: Apple Computer Inc. U3 AGP [106b:004b]
+ * 0000:f0:10.0 VGA compatible controller [0300]: ATI Technologies Inc RV350 AP [Radeon 9600] [1002:4150]
+ * 0001:00:00.0 Host bridge [0600]: Apple Computer Inc. CPC945 HT Bridge [106b:004a]
+ * 0001:00:01.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12)
+ * 0001:00:02.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12)
+ * 0001:00:03.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0045]
+ * 0001:00:04.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0046]
+ * 0001:00:05.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0047]
+ * 0001:00:06.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0048]
+ * 0001:00:07.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0049]
+ * 0001:01:07.0 Class [ff00]: Apple Computer Inc. K2 KeyLargo Mac/IO [106b:0041] (rev 20)
+ * 0001:01:08.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040]
+ * 0001:01:09.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040]
+ * 0001:02:0b.0 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43)
+ * 0001:02:0b.1 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43)
+ * 0001:02:0b.2 USB Controller [0c03]: NEC Corporation USB 2.0 [1033:00e0] (rev 04)
+ * 0001:03:0d.0 Class [ff00]: Apple Computer Inc. K2 ATA/100 [106b:0043]
+ * 0001:03:0e.0 FireWire (IEEE 1394) [0c00]: Apple Computer Inc. K2 FireWire [106b:0042]
+ * 0001:04:0f.0 Ethernet controller [0200]: Apple Computer Inc. K2 GMAC (Sun GEM) [106b:004c]
+ * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240]
+ *
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "ppc_mac.h"
+#include "mac_dbdma.h"
+#include "nvram.h"
+#include "pc.h"
+#include "pci.h"
+#include "usb-ohci.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "fw_cfg.h"
+#include "escc.h"
+#include "openpic.h"
+#include "ide.h"
+#include "loader.h"
+#include "elf.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+#include "hw/usb.h"
+#include "blockdev.h"
+
+#define MAX_IDE_BUS 2
+#define CFG_ADDR 0xf0000510
+
+/* debug UniNorth */
+//#define DEBUG_UNIN
+
+#ifdef DEBUG_UNIN
+#define UNIN_DPRINTF(fmt, ...) \
+ do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define UNIN_DPRINTF(fmt, ...)
+#endif
+
+/* UniN device */
+static void unin_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ UNIN_DPRINTF("writel addr " TARGET_FMT_plx " val %x\n", addr, value);
+}
+
+static uint32_t unin_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t value;
+
+ value = 0;
+ UNIN_DPRINTF("readl addr " TARGET_FMT_plx " val %x\n", addr, value);
+
+ return value;
+}
+
+static CPUWriteMemoryFunc * const unin_write[] = {
+ &unin_writel,
+ &unin_writel,
+ &unin_writel,
+};
+
+static CPUReadMemoryFunc * const unin_read[] = {
+ &unin_readl,
+ &unin_readl,
+ &unin_readl,
+};
+
+static int fw_cfg_boot_set(void *opaque, const char *boot_device)
+{
+ fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ return 0;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
+}
+
+/* PowerPC Mac99 hardware initialisation */
+static void ppc_core99_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env = NULL;
+ char *filename;
+ qemu_irq *pic, **openpic_irqs;
+ int unin_memory;
+ int linux_boot, i;
+ ram_addr_t ram_offset, bios_offset;
+ uint32_t kernel_base, initrd_base;
+ long kernel_size, initrd_size;
+ PCIBus *pci_bus;
+ MacIONVRAMState *nvr;
+ int nvram_mem_index;
+ int bios_size;
+ int pic_mem_index, dbdma_mem_index, cuda_mem_index, escc_mem_index;
+ int ide_mem_index[3];
+ int ppc_boot_device;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ void *fw_cfg;
+ void *dbdma;
+ int machine_arch;
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+ if (cpu_model == NULL)
+#ifdef TARGET_PPC64
+ cpu_model = "970fx";
+#else
+ cpu_model = "G4";
+#endif
+ for (i = 0; i < smp_cpus; i++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+ exit(1);
+ }
+ /* Set time-base frequency to 100 Mhz */
+ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+ qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+ }
+
+ /* allocate RAM */
+ ram_offset = qemu_ram_alloc(NULL, "ppc_core99.ram", ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_offset);
+
+ /* allocate and load BIOS */
+ bios_offset = qemu_ram_alloc(NULL, "ppc_core99.bios", BIOS_SIZE);
+ if (bios_name == NULL)
+ bios_name = PROM_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ cpu_register_physical_memory(PROM_ADDR, BIOS_SIZE, bios_offset | IO_MEM_ROM);
+
+ /* Load OpenBIOS (ELF) */
+ if (filename) {
+ bios_size = load_elf(filename, NULL, NULL, NULL,
+ NULL, NULL, 1, ELF_MACHINE, 0);
+
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name);
+ exit(1);
+ }
+
+ if (linux_boot) {
+ uint64_t lowaddr = 0;
+ int bswap_needed;
+
+#ifdef BSWAP_NEEDED
+ bswap_needed = 1;
+#else
+ bswap_needed = 0;
+#endif
+ kernel_base = KERNEL_LOAD_ADDR;
+
+ kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+ NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ if (kernel_size < 0)
+ kernel_size = load_aout(kernel_filename, kernel_base,
+ ram_size - kernel_base, bswap_needed,
+ TARGET_PAGE_SIZE);
+ if (kernel_size < 0)
+ kernel_size = load_image_targphys(kernel_filename,
+ kernel_base,
+ ram_size - kernel_base);
+ if (kernel_size < 0) {
+ hw_error("qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size < 0) {
+ hw_error("qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ ppc_boot_device = 'm';
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ ppc_boot_device = '\0';
+ /* We consider that NewWorld PowerMac never have any floppy drive
+ * For now, OHW cannot boot from the network.
+ */
+ for (i = 0; boot_device[i] != '\0'; i++) {
+ if (boot_device[i] >= 'c' && boot_device[i] <= 'f') {
+ ppc_boot_device = boot_device[i];
+ break;
+ }
+ }
+ if (ppc_boot_device == '\0') {
+ fprintf(stderr, "No valid boot device for Mac99 machine\n");
+ exit(1);
+ }
+ }
+
+ isa_mem_base = 0x80000000;
+
+ /* Register 8 MB of ISA IO space */
+ isa_mmio_init(0xf2000000, 0x00800000);
+
+ /* UniN init */
+ unin_memory = cpu_register_io_memory(unin_read, unin_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory);
+
+ openpic_irqs = qemu_mallocz(smp_cpus * sizeof(qemu_irq *));
+ openpic_irqs[0] =
+ qemu_mallocz(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
+ for (i = 0; i < smp_cpus; i++) {
+ /* Mac99 IRQ connection between OpenPIC outputs pins
+ * and PowerPC input pins
+ */
+ switch (PPC_INPUT(env)) {
+ case PPC_FLAGS_INPUT_6xx:
+ openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB);
+ openpic_irqs[i][OPENPIC_OUTPUT_INT] =
+ ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT];
+ openpic_irqs[i][OPENPIC_OUTPUT_CINT] =
+ ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT];
+ openpic_irqs[i][OPENPIC_OUTPUT_MCK] =
+ ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_MCP];
+ /* Not connected ? */
+ openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL;
+ /* Check this */
+ openpic_irqs[i][OPENPIC_OUTPUT_RESET] =
+ ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_HRESET];
+ break;
+#if defined(TARGET_PPC64)
+ case PPC_FLAGS_INPUT_970:
+ openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB);
+ openpic_irqs[i][OPENPIC_OUTPUT_INT] =
+ ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT];
+ openpic_irqs[i][OPENPIC_OUTPUT_CINT] =
+ ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT];
+ openpic_irqs[i][OPENPIC_OUTPUT_MCK] =
+ ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_MCP];
+ /* Not connected ? */
+ openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL;
+ /* Check this */
+ openpic_irqs[i][OPENPIC_OUTPUT_RESET] =
+ ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_HRESET];
+ break;
+#endif /* defined(TARGET_PPC64) */
+ default:
+ hw_error("Bus model not supported on mac99 machine\n");
+ exit(1);
+ }
+ }
+ pic = openpic_init(NULL, &pic_mem_index, smp_cpus, openpic_irqs, NULL);
+ if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) {
+ /* 970 gets a U3 bus */
+ pci_bus = pci_pmac_u3_init(pic);
+ machine_arch = ARCH_MAC99_U3;
+ } else {
+ pci_bus = pci_pmac_init(pic);
+ machine_arch = ARCH_MAC99;
+ }
+ /* init basic PC hardware */
+ pci_vga_init(pci_bus);
+
+ escc_mem_index = escc_init(0x80013000, pic[0x25], pic[0x24],
+ serial_hds[0], serial_hds[1], ESCC_CLOCK, 4);
+
+ for(i = 0; i < nb_nics; i++)
+ pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+ dbdma = DBDMA_init(&dbdma_mem_index);
+
+ /* We only emulate 2 out of 3 IDE controllers for now */
+ ide_mem_index[0] = -1;
+ hd[0] = drive_get(IF_IDE, 0, 0);
+ hd[1] = drive_get(IF_IDE, 0, 1);
+ ide_mem_index[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]);
+ hd[0] = drive_get(IF_IDE, 1, 0);
+ hd[1] = drive_get(IF_IDE, 1, 1);
+ ide_mem_index[2] = pmac_ide_init(hd, pic[0x0e], dbdma, 0x1a, pic[0x02]);
+
+ /* cuda also initialize ADB */
+ if (machine_arch == ARCH_MAC99_U3) {
+ usb_enabled = 1;
+ }
+ cuda_init(&cuda_mem_index, pic[0x19]);
+
+ adb_kbd_init(&adb_bus);
+ adb_mouse_init(&adb_bus);
+
+ macio_init(pci_bus, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, 0, pic_mem_index,
+ dbdma_mem_index, cuda_mem_index, NULL, 3, ide_mem_index,
+ escc_mem_index);
+
+ if (usb_enabled) {
+ usb_ohci_init_pci(pci_bus, -1);
+ }
+
+ /* U3 needs to use USB for input because Linux doesn't support via-cuda
+ on PPC64 */
+ if (machine_arch == ARCH_MAC99_U3) {
+ usbdevice_create("keyboard");
+ usbdevice_create("mouse");
+ }
+
+ if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
+ graphic_depth = 15;
+
+ /* The NewWorld NVRAM is not located in the MacIO device */
+ nvr = macio_nvram_init(&nvram_mem_index, 0x2000, 1);
+ pmac_format_nvram_partition(nvr, 0x2000);
+ macio_nvram_map(nvr, 0xFFF04000);
+ /* No PCI init: the BIOS will do it */
+
+ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ if (kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline);
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device);
+
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
+ if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+ uint8_t *hypercall;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+ hypercall = qemu_malloc(16);
+ kvmppc_get_hypercall(env, hypercall, 16);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
+#endif
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+ }
+
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+static QEMUMachine core99_machine = {
+ .name = "mac99",
+ .desc = "Mac99 based PowerMAC",
+ .init = ppc_core99_init,
+ .max_cpus = MAX_CPUS,
+#ifdef TARGET_PPC64
+ .is_default = 1,
+#endif
+};
+
+static void core99_machine_init(void)
+{
+ qemu_register_machine(&core99_machine);
+}
+
+machine_init(core99_machine_init);
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
new file mode 100644
index 0000000..8a4e088
--- /dev/null
+++ b/hw/ppc_oldworld.c
@@ -0,0 +1,332 @@
+
+/*
+ * QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "ppc_mac.h"
+#include "mac_dbdma.h"
+#include "nvram.h"
+#include "pc.h"
+#include "sysemu.h"
+#include "net.h"
+#include "isa.h"
+#include "pci.h"
+#include "usb-ohci.h"
+#include "boards.h"
+#include "fw_cfg.h"
+#include "escc.h"
+#include "ide.h"
+#include "loader.h"
+#include "elf.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+#include "blockdev.h"
+
+#define MAX_IDE_BUS 2
+#define CFG_ADDR 0xf0000510
+
+static int fw_cfg_boot_set(void *opaque, const char *boot_device)
+{
+ fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ return 0;
+}
+
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
+}
+
+static void ppc_heathrow_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env = NULL;
+ char *filename;
+ qemu_irq *pic, **heathrow_irqs;
+ int linux_boot, i;
+ ram_addr_t ram_offset, bios_offset;
+ uint32_t kernel_base, initrd_base;
+ int32_t kernel_size, initrd_size;
+ PCIBus *pci_bus;
+ MacIONVRAMState *nvr;
+ int bios_size;
+ int pic_mem_index, nvram_mem_index, dbdma_mem_index, cuda_mem_index;
+ int escc_mem_index, ide_mem_index[2];
+ uint16_t ppc_boot_device;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ void *fw_cfg;
+ void *dbdma;
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+ if (cpu_model == NULL)
+ cpu_model = "G3";
+ for (i = 0; i < smp_cpus; i++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+ exit(1);
+ }
+ /* Set time-base frequency to 16.6 Mhz */
+ cpu_ppc_tb_init(env, 16600000UL);
+ qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+ }
+
+ /* allocate RAM */
+ if (ram_size > (2047 << 20)) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d MB, maximum 2047 MB\n",
+ ((unsigned int)ram_size / (1 << 20)));
+ exit(1);
+ }
+
+ ram_offset = qemu_ram_alloc(NULL, "ppc_heathrow.ram", ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_offset);
+
+ /* allocate and load BIOS */
+ bios_offset = qemu_ram_alloc(NULL, "ppc_heathrow.bios", BIOS_SIZE);
+ if (bios_name == NULL)
+ bios_name = PROM_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ cpu_register_physical_memory(PROM_ADDR, BIOS_SIZE, bios_offset | IO_MEM_ROM);
+
+ /* Load OpenBIOS (ELF) */
+ if (filename) {
+ bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL,
+ 1, ELF_MACHINE, 0);
+ qemu_free(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name);
+ exit(1);
+ }
+
+ if (linux_boot) {
+ uint64_t lowaddr = 0;
+ int bswap_needed;
+
+#ifdef BSWAP_NEEDED
+ bswap_needed = 1;
+#else
+ bswap_needed = 0;
+#endif
+ kernel_base = KERNEL_LOAD_ADDR;
+ kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+ NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
+ if (kernel_size < 0)
+ kernel_size = load_aout(kernel_filename, kernel_base,
+ ram_size - kernel_base, bswap_needed,
+ TARGET_PAGE_SIZE);
+ if (kernel_size < 0)
+ kernel_size = load_image_targphys(kernel_filename,
+ kernel_base,
+ ram_size - kernel_base);
+ if (kernel_size < 0) {
+ hw_error("qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size < 0) {
+ hw_error("qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ ppc_boot_device = 'm';
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ ppc_boot_device = '\0';
+ for (i = 0; boot_device[i] != '\0'; i++) {
+ /* TOFIX: for now, the second IDE channel is not properly
+ * used by OHW. The Mac floppy disk are not emulated.
+ * For now, OHW cannot boot from the network.
+ */
+#if 0
+ if (boot_device[i] >= 'a' && boot_device[i] <= 'f') {
+ ppc_boot_device = boot_device[i];
+ break;
+ }
+#else
+ if (boot_device[i] >= 'c' && boot_device[i] <= 'd') {
+ ppc_boot_device = boot_device[i];
+ break;
+ }
+#endif
+ }
+ if (ppc_boot_device == '\0') {
+ fprintf(stderr, "No valid boot device for G3 Beige machine\n");
+ exit(1);
+ }
+ }
+
+ isa_mem_base = 0x80000000;
+
+ /* Register 2 MB of ISA IO space */
+ isa_mmio_init(0xfe000000, 0x00200000);
+
+ /* XXX: we register only 1 output pin for heathrow PIC */
+ heathrow_irqs = qemu_mallocz(smp_cpus * sizeof(qemu_irq *));
+ heathrow_irqs[0] =
+ qemu_mallocz(smp_cpus * sizeof(qemu_irq) * 1);
+ /* Connect the heathrow PIC outputs to the 6xx bus */
+ for (i = 0; i < smp_cpus; i++) {
+ switch (PPC_INPUT(env)) {
+ case PPC_FLAGS_INPUT_6xx:
+ heathrow_irqs[i] = heathrow_irqs[0] + (i * 1);
+ heathrow_irqs[i][0] =
+ ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT];
+ break;
+ default:
+ hw_error("Bus model not supported on OldWorld Mac machine\n");
+ }
+ }
+
+ /* init basic PC hardware */
+ if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
+ hw_error("Only 6xx bus is supported on heathrow machine\n");
+ }
+ pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs);
+ pci_bus = pci_grackle_init(0xfec00000, pic);
+ pci_vga_init(pci_bus);
+
+ escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0],
+ serial_hds[1], ESCC_CLOCK, 4);
+
+ for(i = 0; i < nb_nics; i++)
+ pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ /* First IDE channel is a MAC IDE on the MacIO bus */
+ hd[0] = drive_get(IF_IDE, 0, 0);
+ hd[1] = drive_get(IF_IDE, 0, 1);
+ dbdma = DBDMA_init(&dbdma_mem_index);
+ ide_mem_index[0] = -1;
+ ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]);
+
+ /* Second IDE channel is a CMD646 on the PCI bus */
+ hd[0] = drive_get(IF_IDE, 1, 0);
+ hd[1] = drive_get(IF_IDE, 1, 1);
+ hd[3] = hd[2] = NULL;
+ pci_cmd646_ide_init(pci_bus, hd, 0);
+
+ /* cuda also initialize ADB */
+ cuda_init(&cuda_mem_index, pic[0x12]);
+
+ adb_kbd_init(&adb_bus);
+ adb_mouse_init(&adb_bus);
+
+ nvr = macio_nvram_init(&nvram_mem_index, 0x2000, 4);
+ pmac_format_nvram_partition(nvr, 0x2000);
+
+ macio_init(pci_bus, PCI_DEVICE_ID_APPLE_343S1201, 1, pic_mem_index,
+ dbdma_mem_index, cuda_mem_index, nvr, 2, ide_mem_index,
+ escc_mem_index);
+
+ if (usb_enabled) {
+ usb_ohci_init_pci(pci_bus, -1);
+ }
+
+ if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
+ graphic_depth = 15;
+
+ /* No PCI init: the BIOS will do it */
+
+ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ if (kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline);
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device);
+
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
+ if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+ uint8_t *hypercall;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+ hypercall = qemu_malloc(16);
+ kvmppc_get_hypercall(env, hypercall, 16);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
+#endif
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
+ }
+
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+static QEMUMachine heathrow_machine = {
+ .name = "g3beige",
+ .desc = "Heathrow based PowerMAC",
+ .init = ppc_heathrow_init,
+ .max_cpus = MAX_CPUS,
+#ifndef TARGET_PPC64
+ .is_default = 1,
+#endif
+};
+
+static void heathrow_machine_init(void)
+{
+ qemu_register_machine(&heathrow_machine);
+}
+
+machine_init(heathrow_machine_init);
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
new file mode 100644
index 0000000..6b22122
--- /dev/null
+++ b/hw/ppc_prep.c
@@ -0,0 +1,771 @@
+/*
+ * QEMU PPC PREP hardware System Emulator
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "nvram.h"
+#include "pc.h"
+#include "fdc.h"
+#include "net.h"
+#include "sysemu.h"
+#include "isa.h"
+#include "pci.h"
+#include "prep_pci.h"
+#include "usb-ohci.h"
+#include "ppc.h"
+#include "boards.h"
+#include "qemu-log.h"
+#include "ide.h"
+#include "loader.h"
+#include "mc146818rtc.h"
+#include "blockdev.h"
+
+//#define HARD_DEBUG_PPC_IO
+//#define DEBUG_PPC_IO
+
+/* SMP is not enabled, for now */
+#define MAX_CPUS 1
+
+#define MAX_IDE_BUS 2
+
+#define BIOS_SIZE (1024 * 1024)
+#define BIOS_FILENAME "ppc_rom.bin"
+#define KERNEL_LOAD_ADDR 0x01000000
+#define INITRD_LOAD_ADDR 0x01800000
+
+#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO)
+#define DEBUG_PPC_IO
+#endif
+
+#if defined (HARD_DEBUG_PPC_IO)
+#define PPC_IO_DPRINTF(fmt, ...) \
+do { \
+ if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \
+ qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \
+ } else { \
+ printf("%s : " fmt, __func__ , ## __VA_ARGS__); \
+ } \
+} while (0)
+#elif defined (DEBUG_PPC_IO)
+#define PPC_IO_DPRINTF(fmt, ...) \
+qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__)
+#else
+#define PPC_IO_DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+/* Constants for devices init */
+static const int ide_iobase[2] = { 0x1f0, 0x170 };
+static const int ide_iobase2[2] = { 0x3f6, 0x376 };
+static const int ide_irq[2] = { 13, 13 };
+
+#define NE2000_NB_MAX 6
+
+static uint32_t ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, 0x280, 0x380 };
+static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
+
+//static PITState *pit;
+
+/* ISA IO ports bridge */
+#define PPC_IO_BASE 0x80000000
+
+#if 0
+/* Speaker port 0x61 */
+static int speaker_data_on;
+static int dummy_refresh_clock;
+#endif
+
+static void speaker_ioport_write (void *opaque, uint32_t addr, uint32_t val)
+{
+#if 0
+ speaker_data_on = (val >> 1) & 1;
+ pit_set_gate(pit, 2, val & 1);
+#endif
+}
+
+static uint32_t speaker_ioport_read (void *opaque, uint32_t addr)
+{
+#if 0
+ int out;
+ out = pit_get_out(pit, 2, qemu_get_clock(vm_clock));
+ dummy_refresh_clock ^= 1;
+ return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) |
+ (dummy_refresh_clock << 4);
+#endif
+ return 0;
+}
+
+/* PCI intack register */
+/* Read-only register (?) */
+static void _PPC_intack_write (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+#if 0
+ printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
+ value);
+#endif
+}
+
+static inline uint32_t _PPC_intack_read(target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ if ((addr & 0xf) == 0)
+ retval = pic_intack_read(isa_pic);
+#if 0
+ printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
+ retval);
+#endif
+
+ return retval;
+}
+
+static uint32_t PPC_intack_readb (void *opaque, target_phys_addr_t addr)
+{
+ return _PPC_intack_read(addr);
+}
+
+static uint32_t PPC_intack_readw (void *opaque, target_phys_addr_t addr)
+{
+ return _PPC_intack_read(addr);
+}
+
+static uint32_t PPC_intack_readl (void *opaque, target_phys_addr_t addr)
+{
+ return _PPC_intack_read(addr);
+}
+
+static CPUWriteMemoryFunc * const PPC_intack_write[] = {
+ &_PPC_intack_write,
+ &_PPC_intack_write,
+ &_PPC_intack_write,
+};
+
+static CPUReadMemoryFunc * const PPC_intack_read[] = {
+ &PPC_intack_readb,
+ &PPC_intack_readw,
+ &PPC_intack_readl,
+};
+
+/* PowerPC control and status registers */
+#if 0 // Not used
+static struct {
+ /* IDs */
+ uint32_t veni_devi;
+ uint32_t revi;
+ /* Control and status */
+ uint32_t gcsr;
+ uint32_t xcfr;
+ uint32_t ct32;
+ uint32_t mcsr;
+ /* General purpose registers */
+ uint32_t gprg[6];
+ /* Exceptions */
+ uint32_t feen;
+ uint32_t fest;
+ uint32_t fema;
+ uint32_t fecl;
+ uint32_t eeen;
+ uint32_t eest;
+ uint32_t eecl;
+ uint32_t eeint;
+ uint32_t eemck0;
+ uint32_t eemck1;
+ /* Error diagnostic */
+} XCSR;
+
+static void PPC_XCSR_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
+ value);
+}
+
+static void PPC_XCSR_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
+ value);
+}
+
+static void PPC_XCSR_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr,
+ value);
+}
+
+static uint32_t PPC_XCSR_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
+ retval);
+
+ return retval;
+}
+
+static uint32_t PPC_XCSR_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
+ retval);
+
+ return retval;
+}
+
+static uint32_t PPC_XCSR_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t retval = 0;
+
+ printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr,
+ retval);
+
+ return retval;
+}
+
+static CPUWriteMemoryFunc * const PPC_XCSR_write[] = {
+ &PPC_XCSR_writeb,
+ &PPC_XCSR_writew,
+ &PPC_XCSR_writel,
+};
+
+static CPUReadMemoryFunc * const PPC_XCSR_read[] = {
+ &PPC_XCSR_readb,
+ &PPC_XCSR_readw,
+ &PPC_XCSR_readl,
+};
+#endif
+
+/* Fake super-io ports for PREP platform (Intel 82378ZB) */
+typedef struct sysctrl_t {
+ qemu_irq reset_irq;
+ M48t59State *nvram;
+ uint8_t state;
+ uint8_t syscontrol;
+ uint8_t fake_io[2];
+ int contiguous_map;
+ int endian;
+} sysctrl_t;
+
+enum {
+ STATE_HARDFILE = 0x01,
+};
+
+static sysctrl_t *sysctrl;
+
+static void PREP_io_write (void *opaque, uint32_t addr, uint32_t val)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n", addr - PPC_IO_BASE,
+ val);
+ sysctrl->fake_io[addr - 0x0398] = val;
+}
+
+static uint32_t PREP_io_read (void *opaque, uint32_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n", addr - PPC_IO_BASE,
+ sysctrl->fake_io[addr - 0x0398]);
+ return sysctrl->fake_io[addr - 0x0398];
+}
+
+static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n",
+ addr - PPC_IO_BASE, val);
+ switch (addr) {
+ case 0x0092:
+ /* Special port 92 */
+ /* Check soft reset asked */
+ if (val & 0x01) {
+ qemu_irq_raise(sysctrl->reset_irq);
+ } else {
+ qemu_irq_lower(sysctrl->reset_irq);
+ }
+ /* Check LE mode */
+ if (val & 0x02) {
+ sysctrl->endian = 1;
+ } else {
+ sysctrl->endian = 0;
+ }
+ break;
+ case 0x0800:
+ /* Motorola CPU configuration register : read-only */
+ break;
+ case 0x0802:
+ /* Motorola base module feature register : read-only */
+ break;
+ case 0x0803:
+ /* Motorola base module status register : read-only */
+ break;
+ case 0x0808:
+ /* Hardfile light register */
+ if (val & 1)
+ sysctrl->state |= STATE_HARDFILE;
+ else
+ sysctrl->state &= ~STATE_HARDFILE;
+ break;
+ case 0x0810:
+ /* Password protect 1 register */
+ if (sysctrl->nvram != NULL)
+ m48t59_toggle_lock(sysctrl->nvram, 1);
+ break;
+ case 0x0812:
+ /* Password protect 2 register */
+ if (sysctrl->nvram != NULL)
+ m48t59_toggle_lock(sysctrl->nvram, 2);
+ break;
+ case 0x0814:
+ /* L2 invalidate register */
+ // tlb_flush(first_cpu, 1);
+ break;
+ case 0x081C:
+ /* system control register */
+ sysctrl->syscontrol = val & 0x0F;
+ break;
+ case 0x0850:
+ /* I/O map type register */
+ sysctrl->contiguous_map = val & 0x01;
+ break;
+ default:
+ printf("ERROR: unaffected IO port write: %04" PRIx32
+ " => %02" PRIx32"\n", addr, val);
+ break;
+ }
+}
+
+static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t retval = 0xFF;
+
+ switch (addr) {
+ case 0x0092:
+ /* Special port 92 */
+ retval = 0x00;
+ break;
+ case 0x0800:
+ /* Motorola CPU configuration register */
+ retval = 0xEF; /* MPC750 */
+ break;
+ case 0x0802:
+ /* Motorola Base module feature register */
+ retval = 0xAD; /* No ESCC, PMC slot neither ethernet */
+ break;
+ case 0x0803:
+ /* Motorola base module status register */
+ retval = 0xE0; /* Standard MPC750 */
+ break;
+ case 0x080C:
+ /* Equipment present register:
+ * no L2 cache
+ * no upgrade processor
+ * no cards in PCI slots
+ * SCSI fuse is bad
+ */
+ retval = 0x3C;
+ break;
+ case 0x0810:
+ /* Motorola base module extended feature register */
+ retval = 0x39; /* No USB, CF and PCI bridge. NVRAM present */
+ break;
+ case 0x0814:
+ /* L2 invalidate: don't care */
+ break;
+ case 0x0818:
+ /* Keylock */
+ retval = 0x00;
+ break;
+ case 0x081C:
+ /* system control register
+ * 7 - 6 / 1 - 0: L2 cache enable
+ */
+ retval = sysctrl->syscontrol;
+ break;
+ case 0x0823:
+ /* */
+ retval = 0x03; /* no L2 cache */
+ break;
+ case 0x0850:
+ /* I/O map type register */
+ retval = sysctrl->contiguous_map;
+ break;
+ default:
+ printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr);
+ break;
+ }
+ PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n",
+ addr - PPC_IO_BASE, retval);
+
+ return retval;
+}
+
+static inline target_phys_addr_t prep_IO_address(sysctrl_t *sysctrl,
+ target_phys_addr_t addr)
+{
+ if (sysctrl->contiguous_map == 0) {
+ /* 64 KB contiguous space for IOs */
+ addr &= 0xFFFF;
+ } else {
+ /* 8 MB non-contiguous space for IOs */
+ addr = (addr & 0x1F) | ((addr & 0x007FFF000) >> 7);
+ }
+
+ return addr;
+}
+
+static void PPC_prep_io_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ addr = prep_IO_address(sysctrl, addr);
+ cpu_outb(addr, value);
+}
+
+static uint32_t PPC_prep_io_readb (void *opaque, target_phys_addr_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t ret;
+
+ addr = prep_IO_address(sysctrl, addr);
+ ret = cpu_inb(addr);
+
+ return ret;
+}
+
+static void PPC_prep_io_writew (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ addr = prep_IO_address(sysctrl, addr);
+ PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value);
+ cpu_outw(addr, value);
+}
+
+static uint32_t PPC_prep_io_readw (void *opaque, target_phys_addr_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t ret;
+
+ addr = prep_IO_address(sysctrl, addr);
+ ret = cpu_inw(addr);
+ PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret);
+
+ return ret;
+}
+
+static void PPC_prep_io_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ sysctrl_t *sysctrl = opaque;
+
+ addr = prep_IO_address(sysctrl, addr);
+ PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value);
+ cpu_outl(addr, value);
+}
+
+static uint32_t PPC_prep_io_readl (void *opaque, target_phys_addr_t addr)
+{
+ sysctrl_t *sysctrl = opaque;
+ uint32_t ret;
+
+ addr = prep_IO_address(sysctrl, addr);
+ ret = cpu_inl(addr);
+ PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret);
+
+ return ret;
+}
+
+static CPUWriteMemoryFunc * const PPC_prep_io_write[] = {
+ &PPC_prep_io_writeb,
+ &PPC_prep_io_writew,
+ &PPC_prep_io_writel,
+};
+
+static CPUReadMemoryFunc * const PPC_prep_io_read[] = {
+ &PPC_prep_io_readb,
+ &PPC_prep_io_readw,
+ &PPC_prep_io_readl,
+};
+
+#define NVRAM_SIZE 0x2000
+
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+ CPUState *env = cpu_single_env;
+
+ if (env && level) {
+ cpu_exit(env);
+ }
+}
+
+/* PowerPC PREP hardware initialisation */
+static void ppc_prep_init (ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env = NULL;
+ char *filename;
+ nvram_t nvram;
+ M48t59State *m48t59;
+ int PPC_io_memory;
+ int linux_boot, i, nb_nics1, bios_size;
+ ram_addr_t ram_offset, bios_offset;
+ uint32_t kernel_base, initrd_base;
+ long kernel_size, initrd_size;
+ PCIBus *pci_bus;
+ qemu_irq *i8259;
+ qemu_irq *cpu_exit_irq;
+ int ppc_boot_device;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DriveInfo *fd[MAX_FD];
+
+ sysctrl = qemu_mallocz(sizeof(sysctrl_t));
+
+ linux_boot = (kernel_filename != NULL);
+
+ /* init CPUs */
+ if (cpu_model == NULL)
+ cpu_model = "602";
+ for (i = 0; i < smp_cpus; i++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find PowerPC CPU definition\n");
+ exit(1);
+ }
+ if (env->flags & POWERPC_FLAG_RTC_CLK) {
+ /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */
+ cpu_ppc_tb_init(env, 7812500UL);
+ } else {
+ /* Set time-base frequency to 100 Mhz */
+ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+ }
+ qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
+ }
+
+ /* allocate RAM */
+ ram_offset = qemu_ram_alloc(NULL, "ppc_prep.ram", ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_offset);
+
+ /* allocate and load BIOS */
+ bios_offset = qemu_ram_alloc(NULL, "ppc_prep.bios", BIOS_SIZE);
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ bios_size = get_image_size(filename);
+ } else {
+ bios_size = -1;
+ }
+ if (bios_size > 0 && bios_size <= BIOS_SIZE) {
+ target_phys_addr_t bios_addr;
+ bios_size = (bios_size + 0xfff) & ~0xfff;
+ bios_addr = (uint32_t)(-bios_size);
+ cpu_register_physical_memory(bios_addr, bios_size,
+ bios_offset | IO_MEM_ROM);
+ bios_size = load_image_targphys(filename, bios_addr, bios_size);
+ }
+ if (bios_size < 0 || bios_size > BIOS_SIZE) {
+ hw_error("qemu: could not load PPC PREP bios '%s'\n", bios_name);
+ }
+ if (filename) {
+ qemu_free(filename);
+ }
+
+ if (linux_boot) {
+ kernel_base = KERNEL_LOAD_ADDR;
+ /* now we can load the kernel */
+ kernel_size = load_image_targphys(kernel_filename, kernel_base,
+ ram_size - kernel_base);
+ if (kernel_size < 0) {
+ hw_error("qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+ /* load initrd */
+ if (initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size < 0) {
+ hw_error("qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ }
+ } else {
+ initrd_base = 0;
+ initrd_size = 0;
+ }
+ ppc_boot_device = 'm';
+ } else {
+ kernel_base = 0;
+ kernel_size = 0;
+ initrd_base = 0;
+ initrd_size = 0;
+ ppc_boot_device = '\0';
+ /* For now, OHW cannot boot from the network. */
+ for (i = 0; boot_device[i] != '\0'; i++) {
+ if (boot_device[i] >= 'a' && boot_device[i] <= 'f') {
+ ppc_boot_device = boot_device[i];
+ break;
+ }
+ }
+ if (ppc_boot_device == '\0') {
+ fprintf(stderr, "No valid boot device for Mac99 machine\n");
+ exit(1);
+ }
+ }
+
+ isa_mem_base = 0xc0000000;
+ if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
+ hw_error("Only 6xx bus is supported on PREP machine\n");
+ }
+ i8259 = i8259_init(first_cpu->irq_inputs[PPC6xx_INPUT_INT]);
+ pci_bus = pci_prep_init(i8259);
+ /* Hmm, prep has no pci-isa bridge ??? */
+ isa_bus_new(NULL);
+ isa_bus_irqs(i8259);
+ // pci_bus = i440fx_init();
+ /* Register 8 MB of ISA IO space (needed for non-contiguous map) */
+ PPC_io_memory = cpu_register_io_memory(PPC_prep_io_read,
+ PPC_prep_io_write, sysctrl,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(0x80000000, 0x00800000, PPC_io_memory);
+
+ /* init basic PC hardware */
+ pci_vga_init(pci_bus);
+ // openpic = openpic_init(0x00000000, 0xF0000000, 1);
+ // pit = pit_init(0x40, i8259[0]);
+ rtc_init(2000, NULL);
+
+ if (serial_hds[0])
+ serial_isa_init(0, serial_hds[0]);
+ nb_nics1 = nb_nics;
+ if (nb_nics1 > NE2000_NB_MAX)
+ nb_nics1 = NE2000_NB_MAX;
+ for(i = 0; i < nb_nics1; i++) {
+ if (nd_table[i].model == NULL) {
+ nd_table[i].model = qemu_strdup("ne2k_isa");
+ }
+ if (strcmp(nd_table[i].model, "ne2k_isa") == 0) {
+ isa_ne2000_init(ne2000_io[i], ne2000_irq[i], &nd_table[i]);
+ } else {
+ pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+ }
+ }
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
+ }
+
+ for(i = 0; i < MAX_IDE_BUS; i++) {
+ isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
+ hd[2 * i],
+ hd[2 * i + 1]);
+ }
+ isa_create_simple("i8042");
+
+ cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+ DMA_init(1, cpu_exit_irq);
+
+ // SB16_init();
+
+ for(i = 0; i < MAX_FD; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ }
+ fdctrl_init_isa(fd);
+
+ /* Register speaker port */
+ register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL);
+ register_ioport_write(0x61, 1, 1, speaker_ioport_write, NULL);
+ /* Register fake IO ports for PREP */
+ sysctrl->reset_irq = first_cpu->irq_inputs[PPC6xx_INPUT_HRESET];
+ register_ioport_read(0x398, 2, 1, &PREP_io_read, sysctrl);
+ register_ioport_write(0x398, 2, 1, &PREP_io_write, sysctrl);
+ /* System control ports */
+ register_ioport_read(0x0092, 0x01, 1, &PREP_io_800_readb, sysctrl);
+ register_ioport_write(0x0092, 0x01, 1, &PREP_io_800_writeb, sysctrl);
+ register_ioport_read(0x0800, 0x52, 1, &PREP_io_800_readb, sysctrl);
+ register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl);
+ /* PCI intack location */
+ PPC_io_memory = cpu_register_io_memory(PPC_intack_read,
+ PPC_intack_write, NULL,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory);
+ /* PowerPC control and status register group */
+#if 0
+ PPC_io_memory = cpu_register_io_memory(PPC_XCSR_read, PPC_XCSR_write,
+ NULL, DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory);
+#endif
+
+ if (usb_enabled) {
+ usb_ohci_init_pci(pci_bus, -1);
+ }
+
+ m48t59 = m48t59_init(i8259[8], 0, 0x0074, NVRAM_SIZE, 59);
+ if (m48t59 == NULL)
+ return;
+ sysctrl->nvram = m48t59;
+
+ /* Initialise NVRAM */
+ nvram.opaque = m48t59;
+ nvram.read_fn = &m48t59_read;
+ nvram.write_fn = &m48t59_write;
+ PPC_NVRAM_set_params(&nvram, NVRAM_SIZE, "PREP", ram_size, ppc_boot_device,
+ kernel_base, kernel_size,
+ kernel_cmdline,
+ initrd_base, initrd_size,
+ /* XXX: need an option to load a NVRAM image */
+ 0,
+ graphic_width, graphic_height, graphic_depth);
+
+ /* Special port to get debug messages from Open-Firmware */
+ register_ioport_write(0x0F00, 4, 1, &PPC_debug_write, NULL);
+}
+
+static QEMUMachine prep_machine = {
+ .name = "prep",
+ .desc = "PowerPC PREP platform",
+ .init = ppc_prep_init,
+ .max_cpus = MAX_CPUS,
+};
+
+static void prep_machine_init(void)
+{
+ qemu_register_machine(&prep_machine);
+}
+
+machine_init(prep_machine_init);
diff --git a/hw/ppce500.h b/hw/ppce500.h
new file mode 100644
index 0000000..24d49bb
--- /dev/null
+++ b/hw/ppce500.h
@@ -0,0 +1,22 @@
+/*
+ * QEMU PowerPC E500 emulation shared definitions
+ *
+ * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Yu Liu, <yu.liu@freescale.com>
+ *
+ * This file is derived from hw/ppc440.h
+ * the copyright for that material belongs to the original owners.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#if !defined(PPC_E500_H)
+#define PPC_E500_H
+
+PCIBus *ppce500_pci_init(qemu_irq *pic, target_phys_addr_t registers);
+
+#endif /* !defined(PPC_E500_H) */
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
new file mode 100644
index 0000000..820a269
--- /dev/null
+++ b/hw/ppce500_mpc8544ds.c
@@ -0,0 +1,298 @@
+/*
+ * Qemu PowerPC MPC8544DS board emualtion
+ *
+ * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Yu Liu, <yu.liu@freescale.com>
+ *
+ * This file is derived from hw/ppc440_bamboo.c,
+ * the copyright for that material belongs to the original owners.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <dirent.h>
+
+#include "config.h"
+#include "qemu-common.h"
+#include "net.h"
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "boards.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+#include "device_tree.h"
+#include "openpic.h"
+#include "ppce500.h"
+#include "loader.h"
+#include "elf.h"
+#include "qemu-kvm.h"
+
+#define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb"
+#define UIMAGE_LOAD_BASE 0
+#define DTC_LOAD_PAD 0x500000
+#define DTC_PAD_MASK 0xFFFFF
+#define INITRD_LOAD_PAD 0x2000000
+#define INITRD_PAD_MASK 0xFFFFFF
+
+#define RAM_SIZES_ALIGN (64UL << 20)
+
+#define MPC8544_CCSRBAR_BASE 0xE0000000
+#define MPC8544_MPIC_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x40000)
+#define MPC8544_SERIAL0_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4500)
+#define MPC8544_SERIAL1_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x4600)
+#define MPC8544_PCI_REGS_BASE (MPC8544_CCSRBAR_BASE + 0x8000)
+#define MPC8544_PCI_REGS_SIZE 0x1000
+#define MPC8544_PCI_IO 0xE1000000
+#define MPC8544_PCI_IOLEN 0x10000
+
+#ifdef CONFIG_FDT
+static int mpc8544_copy_soc_cell(void *fdt, const char *node, const char *prop)
+{
+ uint32_t cell;
+ int ret;
+
+ ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell));
+ if (ret < 0) {
+ fprintf(stderr, "couldn't read host %s/%s\n", node, prop);
+ goto out;
+ }
+
+ ret = qemu_devtree_setprop_cell(fdt, "/cpus/PowerPC,8544@0",
+ prop, cell);
+ if (ret < 0) {
+ fprintf(stderr, "couldn't set guest /cpus/PowerPC,8544@0/%s\n", prop);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+#endif
+
+static int mpc8544_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ int ret = -1;
+#ifdef CONFIG_FDT
+ uint32_t mem_reg_property[] = {0, ramsize};
+ char *filename;
+ int fdt_size;
+ void *fdt;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (!filename) {
+ goto out;
+ }
+ fdt = load_device_tree(filename, &fdt_size);
+ qemu_free(filename);
+ if (fdt == NULL) {
+ goto out;
+ }
+
+ /* Manipulate device tree in memory. */
+ ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
+ sizeof(mem_reg_property));
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /memory/reg\n");
+
+ ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
+ initrd_base);
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
+
+ ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+ (initrd_base + initrd_size));
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
+
+ ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+ kernel_cmdline);
+ if (ret < 0)
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+
+ if (kvm_enabled()) {
+ struct dirent *dirp;
+ DIR *dp;
+ char buf[128];
+
+ if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) {
+ printf("Can't open directory /proc/device-tree/cpus/\n");
+ ret = -1;
+ goto out;
+ }
+
+ buf[0] = '\0';
+ while ((dirp = readdir(dp)) != NULL) {
+ if (strncmp(dirp->d_name, "PowerPC", 7) == 0) {
+ snprintf(buf, 128, "/cpus/%s", dirp->d_name);
+ break;
+ }
+ }
+ closedir(dp);
+ if (buf[0] == '\0') {
+ printf("Unknow host!\n");
+ ret = -1;
+ goto out;
+ }
+
+ mpc8544_copy_soc_cell(fdt, buf, "clock-frequency");
+ mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency");
+ }
+
+ ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
+ qemu_free(fdt);
+
+out:
+#endif
+
+ return ret;
+}
+
+static void mpc8544ds_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ PCIBus *pci_bus;
+ CPUState *env;
+ uint64_t elf_entry;
+ uint64_t elf_lowaddr;
+ target_phys_addr_t entry=0;
+ target_phys_addr_t loadaddr=UIMAGE_LOAD_BASE;
+ target_long kernel_size=0;
+ target_ulong dt_base = 0;
+ target_ulong initrd_base = 0;
+ target_long initrd_size=0;
+ int i=0;
+ unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
+ qemu_irq *irqs, *mpic, *pci_irqs;
+
+ /* Setup CPU */
+ env = cpu_ppc_init("e500v2_v30");
+ if (!env) {
+ fprintf(stderr, "Unable to initialize CPU!\n");
+ exit(1);
+ }
+
+ /* Fixup Memory size on a alignment boundary */
+ ram_size &= ~(RAM_SIZES_ALIGN - 1);
+
+ /* Register Memory */
+ cpu_register_physical_memory(0, ram_size, qemu_ram_alloc(NULL,
+ "mpc8544ds.ram", ram_size));
+
+ /* MPIC */
+ irqs = qemu_mallocz(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
+ irqs[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_INT];
+ irqs[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPCE500_INPUT_CINT];
+ mpic = mpic_init(MPC8544_MPIC_REGS_BASE, 1, &irqs, NULL);
+
+ /* Serial */
+ if (serial_hds[0]) {
+ serial_mm_init(MPC8544_SERIAL0_REGS_BASE,
+ 0, mpic[12+26], 399193,
+ serial_hds[0], 1, 1);
+ }
+
+ if (serial_hds[1]) {
+ serial_mm_init(MPC8544_SERIAL1_REGS_BASE,
+ 0, mpic[12+26], 399193,
+ serial_hds[0], 1, 1);
+ }
+
+ /* PCI */
+ pci_irqs = qemu_malloc(sizeof(qemu_irq) * 4);
+ pci_irqs[0] = mpic[pci_irq_nrs[0]];
+ pci_irqs[1] = mpic[pci_irq_nrs[1]];
+ pci_irqs[2] = mpic[pci_irq_nrs[2]];
+ pci_irqs[3] = mpic[pci_irq_nrs[3]];
+ pci_bus = ppce500_pci_init(pci_irqs, MPC8544_PCI_REGS_BASE);
+ if (!pci_bus)
+ printf("couldn't create PCI controller!\n");
+
+ isa_mmio_init(MPC8544_PCI_IO, MPC8544_PCI_IOLEN);
+
+ if (pci_bus) {
+ /* Register network interfaces. */
+ for (i = 0; i < nb_nics; i++) {
+ pci_nic_init_nofail(&nd_table[i], "virtio", NULL);
+ }
+ }
+
+ /* Load kernel. */
+ if (kernel_filename) {
+ kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
+ if (kernel_size < 0) {
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
+ entry = elf_entry;
+ loadaddr = elf_lowaddr;
+ }
+ /* XXX try again as binary */
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+ }
+
+ /* Load initrd. */
+ if (initrd_filename) {
+ initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK;
+ initrd_size = load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+
+ /* If we're loading a kernel directly, we must load the device tree too. */
+ if (kernel_filename) {
+ dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
+ if (mpc8544_load_device_tree(dt_base, ram_size,
+ initrd_base, initrd_size, kernel_cmdline) < 0) {
+ fprintf(stderr, "couldn't load device tree\n");
+ exit(1);
+ }
+
+ cpu_synchronize_state(env);
+
+ /* Set initial guest state. */
+ env->gpr[1] = (16<<20) - 8;
+ env->gpr[3] = dt_base;
+ env->nip = entry;
+ /* XXX we currently depend on KVM to create some initial TLB entries. */
+ }
+
+ if (kvm_enabled())
+ kvmppc_init();
+
+ return;
+}
+
+static QEMUMachine mpc8544ds_machine = {
+ .name = "mpc8544ds",
+ .desc = "mpc8544ds",
+ .init = mpc8544ds_init,
+};
+
+static void mpc8544ds_machine_init(void)
+{
+ qemu_register_machine(&mpc8544ds_machine);
+}
+
+machine_init(mpc8544ds_machine_init);
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
new file mode 100644
index 0000000..11edd03
--- /dev/null
+++ b/hw/ppce500_pci.c
@@ -0,0 +1,326 @@
+/*
+ * QEMU PowerPC E500 embedded processors pci controller emulation
+ *
+ * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Yu Liu, <yu.liu@freescale.com>
+ *
+ * This file is derived from hw/ppc4xx_pci.c,
+ * the copyright for that material belongs to the original owners.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "hw.h"
+#include "ppce500.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "bswap.h"
+
+#ifdef DEBUG_PCI
+#define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#else
+#define pci_debug(fmt, ...)
+#endif
+
+#define PCIE500_CFGADDR 0x0
+#define PCIE500_CFGDATA 0x4
+#define PCIE500_REG_BASE 0xC00
+#define PCIE500_REG_SIZE (0x1000 - PCIE500_REG_BASE)
+
+#define PPCE500_PCI_CONFIG_ADDR 0x0
+#define PPCE500_PCI_CONFIG_DATA 0x4
+#define PPCE500_PCI_INTACK 0x8
+
+#define PPCE500_PCI_OW1 (0xC20 - PCIE500_REG_BASE)
+#define PPCE500_PCI_OW2 (0xC40 - PCIE500_REG_BASE)
+#define PPCE500_PCI_OW3 (0xC60 - PCIE500_REG_BASE)
+#define PPCE500_PCI_OW4 (0xC80 - PCIE500_REG_BASE)
+#define PPCE500_PCI_IW3 (0xDA0 - PCIE500_REG_BASE)
+#define PPCE500_PCI_IW2 (0xDC0 - PCIE500_REG_BASE)
+#define PPCE500_PCI_IW1 (0xDE0 - PCIE500_REG_BASE)
+
+#define PPCE500_PCI_GASKET_TIMR (0xE20 - PCIE500_REG_BASE)
+
+#define PCI_POTAR 0x0
+#define PCI_POTEAR 0x4
+#define PCI_POWBAR 0x8
+#define PCI_POWAR 0x10
+
+#define PCI_PITAR 0x0
+#define PCI_PIWBAR 0x8
+#define PCI_PIWBEAR 0xC
+#define PCI_PIWAR 0x10
+
+#define PPCE500_PCI_NR_POBS 5
+#define PPCE500_PCI_NR_PIBS 3
+
+struct pci_outbound {
+ uint32_t potar;
+ uint32_t potear;
+ uint32_t powbar;
+ uint32_t powar;
+};
+
+struct pci_inbound {
+ uint32_t pitar;
+ uint32_t piwbar;
+ uint32_t piwbear;
+ uint32_t piwar;
+};
+
+struct PPCE500PCIState {
+ struct pci_outbound pob[PPCE500_PCI_NR_POBS];
+ struct pci_inbound pib[PPCE500_PCI_NR_PIBS];
+ uint32_t gasket_time;
+ PCIHostState pci_state;
+ PCIDevice *pci_dev;
+};
+
+typedef struct PPCE500PCIState PPCE500PCIState;
+
+static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr)
+{
+ PPCE500PCIState *pci = opaque;
+ unsigned long win;
+ uint32_t value = 0;
+
+ win = addr & 0xfe0;
+
+ switch (win) {
+ case PPCE500_PCI_OW1:
+ case PPCE500_PCI_OW2:
+ case PPCE500_PCI_OW3:
+ case PPCE500_PCI_OW4:
+ switch (addr & 0xC) {
+ case PCI_POTAR: value = pci->pob[(addr >> 5) & 0x7].potar; break;
+ case PCI_POTEAR: value = pci->pob[(addr >> 5) & 0x7].potear; break;
+ case PCI_POWBAR: value = pci->pob[(addr >> 5) & 0x7].powbar; break;
+ case PCI_POWAR: value = pci->pob[(addr >> 5) & 0x7].powar; break;
+ default: break;
+ }
+ break;
+
+ case PPCE500_PCI_IW3:
+ case PPCE500_PCI_IW2:
+ case PPCE500_PCI_IW1:
+ switch (addr & 0xC) {
+ case PCI_PITAR: value = pci->pib[(addr >> 5) & 0x3].pitar; break;
+ case PCI_PIWBAR: value = pci->pib[(addr >> 5) & 0x3].piwbar; break;
+ case PCI_PIWBEAR: value = pci->pib[(addr >> 5) & 0x3].piwbear; break;
+ case PCI_PIWAR: value = pci->pib[(addr >> 5) & 0x3].piwar; break;
+ default: break;
+ };
+ break;
+
+ case PPCE500_PCI_GASKET_TIMR:
+ value = pci->gasket_time;
+ break;
+
+ default:
+ break;
+ }
+
+ pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__,
+ win, addr, value);
+ return value;
+}
+
+static CPUReadMemoryFunc * const e500_pci_reg_read[] = {
+ &pci_reg_read4,
+ &pci_reg_read4,
+ &pci_reg_read4,
+};
+
+static void pci_reg_write4(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PPCE500PCIState *pci = opaque;
+ unsigned long win;
+
+ win = addr & 0xfe0;
+
+ pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n",
+ __func__, value, win, addr);
+
+ switch (win) {
+ case PPCE500_PCI_OW1:
+ case PPCE500_PCI_OW2:
+ case PPCE500_PCI_OW3:
+ case PPCE500_PCI_OW4:
+ switch (addr & 0xC) {
+ case PCI_POTAR: pci->pob[(addr >> 5) & 0x7].potar = value; break;
+ case PCI_POTEAR: pci->pob[(addr >> 5) & 0x7].potear = value; break;
+ case PCI_POWBAR: pci->pob[(addr >> 5) & 0x7].powbar = value; break;
+ case PCI_POWAR: pci->pob[(addr >> 5) & 0x7].powar = value; break;
+ default: break;
+ };
+ break;
+
+ case PPCE500_PCI_IW3:
+ case PPCE500_PCI_IW2:
+ case PPCE500_PCI_IW1:
+ switch (addr & 0xC) {
+ case PCI_PITAR: pci->pib[(addr >> 5) & 0x3].pitar = value; break;
+ case PCI_PIWBAR: pci->pib[(addr >> 5) & 0x3].piwbar = value; break;
+ case PCI_PIWBEAR: pci->pib[(addr >> 5) & 0x3].piwbear = value; break;
+ case PCI_PIWAR: pci->pib[(addr >> 5) & 0x3].piwar = value; break;
+ default: break;
+ };
+ break;
+
+ case PPCE500_PCI_GASKET_TIMR:
+ pci->gasket_time = value;
+ break;
+
+ default:
+ break;
+ };
+}
+
+static CPUWriteMemoryFunc * const e500_pci_reg_write[] = {
+ &pci_reg_write4,
+ &pci_reg_write4,
+ &pci_reg_write4,
+};
+
+static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int devno = pci_dev->devfn >> 3, ret = 0;
+
+ switch (devno) {
+ /* Two PCI slot */
+ case 0x11:
+ case 0x12:
+ ret = (irq_num + devno - 0x10) % 4;
+ break;
+ default:
+ printf("Error:%s:unknow dev number\n", __func__);
+ }
+
+ pci_debug("%s: devfn %x irq %d -> %d devno:%x\n", __func__,
+ pci_dev->devfn, irq_num, ret, devno);
+
+ return ret;
+}
+
+static void mpc85xx_pci_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+
+ pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level);
+
+ qemu_set_irq(pic[irq_num], level);
+}
+
+static void ppce500_pci_save(QEMUFile *f, void *opaque)
+{
+ PPCE500PCIState *controller = opaque;
+ int i;
+
+ pci_device_save(controller->pci_dev, f);
+
+ for (i = 0; i < PPCE500_PCI_NR_POBS; i++) {
+ qemu_put_be32s(f, &controller->pob[i].potar);
+ qemu_put_be32s(f, &controller->pob[i].potear);
+ qemu_put_be32s(f, &controller->pob[i].powbar);
+ qemu_put_be32s(f, &controller->pob[i].powar);
+ }
+
+ for (i = 0; i < PPCE500_PCI_NR_PIBS; i++) {
+ qemu_put_be32s(f, &controller->pib[i].pitar);
+ qemu_put_be32s(f, &controller->pib[i].piwbar);
+ qemu_put_be32s(f, &controller->pib[i].piwbear);
+ qemu_put_be32s(f, &controller->pib[i].piwar);
+ }
+ qemu_put_be32s(f, &controller->gasket_time);
+}
+
+static int ppce500_pci_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PPCE500PCIState *controller = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ pci_device_load(controller->pci_dev, f);
+
+ for (i = 0; i < PPCE500_PCI_NR_POBS; i++) {
+ qemu_get_be32s(f, &controller->pob[i].potar);
+ qemu_get_be32s(f, &controller->pob[i].potear);
+ qemu_get_be32s(f, &controller->pob[i].powbar);
+ qemu_get_be32s(f, &controller->pob[i].powar);
+ }
+
+ for (i = 0; i < PPCE500_PCI_NR_PIBS; i++) {
+ qemu_get_be32s(f, &controller->pib[i].pitar);
+ qemu_get_be32s(f, &controller->pib[i].piwbar);
+ qemu_get_be32s(f, &controller->pib[i].piwbear);
+ qemu_get_be32s(f, &controller->pib[i].piwar);
+ }
+ qemu_get_be32s(f, &controller->gasket_time);
+
+ return 0;
+}
+
+PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers)
+{
+ PPCE500PCIState *controller;
+ PCIDevice *d;
+ int index;
+ static int ppce500_pci_id;
+
+ controller = qemu_mallocz(sizeof(PPCE500PCIState));
+
+ controller->pci_state.bus = pci_register_bus(NULL, "pci",
+ mpc85xx_pci_set_irq,
+ mpc85xx_pci_map_irq,
+ pci_irqs, PCI_DEVFN(0x11, 0),
+ 4);
+ d = pci_register_device(controller->pci_state.bus,
+ "host bridge", sizeof(PCIDevice),
+ 0, NULL, NULL);
+
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_FREESCALE);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_MPC8533E);
+ pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_POWERPC);
+
+ controller->pci_dev = d;
+
+ /* CFGADDR */
+ index = pci_host_conf_register_mmio(&controller->pci_state,
+ DEVICE_BIG_ENDIAN);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers + PCIE500_CFGADDR, 4, index);
+
+ /* CFGDATA */
+ index = pci_host_data_register_mmio(&controller->pci_state,
+ DEVICE_BIG_ENDIAN);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers + PCIE500_CFGDATA, 4, index);
+
+ index = cpu_register_io_memory(e500_pci_reg_read,
+ e500_pci_reg_write, controller,
+ DEVICE_NATIVE_ENDIAN);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers + PCIE500_REG_BASE,
+ PCIE500_REG_SIZE, index);
+
+ /* XXX load/save code not tested. */
+ register_savevm(&d->qdev, "ppce500_pci", ppce500_pci_id++,
+ 1, ppce500_pci_save, ppce500_pci_load, controller);
+
+ return controller->pci_state.bus;
+
+free:
+ printf("%s error\n", __func__);
+ qemu_free(controller);
+ return NULL;
+}
diff --git a/hw/prep_pci.c b/hw/prep_pci.c
new file mode 100644
index 0000000..f88b825
--- /dev/null
+++ b/hw/prep_pci.c
@@ -0,0 +1,144 @@
+/*
+ * QEMU PREP PCI host
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "prep_pci.h"
+
+typedef PCIHostState PREPPCIState;
+
+static inline uint32_t PPC_PCIIO_config(target_phys_addr_t addr)
+{
+ int i;
+
+ for(i = 0; i < 11; i++) {
+ if ((addr & (1 << (11 + i))) != 0)
+ break;
+ }
+ return (addr & 0x7ff) | (i << 11);
+}
+
+static void PPC_PCIIO_writeb (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+ pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 1);
+}
+
+static void PPC_PCIIO_writew (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+ val = bswap16(val);
+ pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 2);
+}
+
+static void PPC_PCIIO_writel (void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ PREPPCIState *s = opaque;
+ val = bswap32(val);
+ pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 4);
+}
+
+static uint32_t PPC_PCIIO_readb (void *opaque, target_phys_addr_t addr)
+{
+ PREPPCIState *s = opaque;
+ uint32_t val;
+ val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 1);
+ return val;
+}
+
+static uint32_t PPC_PCIIO_readw (void *opaque, target_phys_addr_t addr)
+{
+ PREPPCIState *s = opaque;
+ uint32_t val;
+ val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 2);
+ val = bswap16(val);
+ return val;
+}
+
+static uint32_t PPC_PCIIO_readl (void *opaque, target_phys_addr_t addr)
+{
+ PREPPCIState *s = opaque;
+ uint32_t val;
+ val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 4);
+ val = bswap32(val);
+ return val;
+}
+
+static CPUWriteMemoryFunc * const PPC_PCIIO_write[] = {
+ &PPC_PCIIO_writeb,
+ &PPC_PCIIO_writew,
+ &PPC_PCIIO_writel,
+};
+
+static CPUReadMemoryFunc * const PPC_PCIIO_read[] = {
+ &PPC_PCIIO_readb,
+ &PPC_PCIIO_readw,
+ &PPC_PCIIO_readl,
+};
+
+static int prep_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ return (irq_num + (pci_dev->devfn >> 3)) & 1;
+}
+
+static void prep_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+
+ qemu_set_irq(pic[(irq_num & 1) ? 11 : 9] , level);
+}
+
+PCIBus *pci_prep_init(qemu_irq *pic)
+{
+ PREPPCIState *s;
+ PCIDevice *d;
+ int PPC_io_memory;
+
+ s = qemu_mallocz(sizeof(PREPPCIState));
+ s->bus = pci_register_bus(NULL, "pci",
+ prep_set_irq, prep_map_irq, pic, 0, 4);
+
+ pci_host_conf_register_ioport(0xcf8, s);
+
+ pci_host_data_register_ioport(0xcfc, s);
+
+ PPC_io_memory = cpu_register_io_memory(PPC_PCIIO_read,
+ PPC_PCIIO_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0x80800000, 0x00400000, PPC_io_memory);
+
+ /* PCI host bridge */
+ d = pci_register_device(s->bus, "PREP Host Bridge - Motorola Raven",
+ sizeof(PCIDevice), 0, NULL, NULL);
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MOTOROLA);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_MOTOROLA_RAVEN);
+ d->config[0x08] = 0x00; // revision
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x34] = 0x00; // capabilities_pointer
+
+ return s->bus;
+}
diff --git a/hw/prep_pci.h b/hw/prep_pci.h
new file mode 100644
index 0000000..cd68512
--- /dev/null
+++ b/hw/prep_pci.h
@@ -0,0 +1,8 @@
+#ifndef QEMU_PREP_PCI_H
+#define QEMU_PREP_PCI_H
+
+#include "qemu-common.h"
+
+PCIBus *pci_prep_init(qemu_irq *pic);
+
+#endif
diff --git a/hw/primecell.h b/hw/primecell.h
new file mode 100644
index 0000000..fb456ad
--- /dev/null
+++ b/hw/primecell.h
@@ -0,0 +1,14 @@
+#ifndef PRIMECELL_H
+#define PRIMECELL_H
+
+/* Declarations for ARM PrimeCell based periperals. */
+/* Also includes some devices that are currently only used by the
+ ARM boards. */
+
+/* pl080.c */
+void *pl080_init(uint32_t base, qemu_irq irq, int nchannels);
+
+/* arm_sysctl.c */
+void arm_sysctl_init(uint32_t base, uint32_t sys_id, uint32_t proc_id);
+
+#endif
diff --git a/hw/ps2.c b/hw/ps2.c
new file mode 100644
index 0000000..762bb00
--- /dev/null
+++ b/hw/ps2.c
@@ -0,0 +1,614 @@
+/*
+ * QEMU PS/2 keyboard/mouse emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ps2.h"
+#include "console.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
+#define KBD_CMD_ECHO 0xEE
+#define KBD_CMD_SCANCODE 0xF0 /* Get/set scancode set */
+#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
+#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
+#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
+#define KBD_CMD_RESET 0xFF /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR 0xAA /* Power on reset */
+#define KBD_REPLY_ID 0xAB /* Keyboard ID */
+#define KBD_REPLY_ACK 0xFA /* Command ACK */
+#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
+#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
+#define AUX_SET_RES 0xE8 /* Set resolution */
+#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
+#define AUX_SET_STREAM 0xEA /* Set stream mode */
+#define AUX_POLL 0xEB /* Poll */
+#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
+#define AUX_SET_WRAP 0xEE /* Set wrap mode */
+#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
+#define AUX_GET_TYPE 0xF2 /* Get type */
+#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
+#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
+#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
+#define AUX_SET_DEFAULT 0xF6
+#define AUX_RESET 0xFF /* Reset aux device */
+#define AUX_ACK 0xFA /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE 0x40
+#define MOUSE_STATUS_ENABLED 0x20
+#define MOUSE_STATUS_SCALE21 0x10
+
+#define PS2_QUEUE_SIZE 256
+
+typedef struct {
+ uint8_t data[PS2_QUEUE_SIZE];
+ int rptr, wptr, count;
+} PS2Queue;
+
+typedef struct {
+ PS2Queue queue;
+ int32_t write_cmd;
+ void (*update_irq)(void *, int);
+ void *update_arg;
+} PS2State;
+
+typedef struct {
+ PS2State common;
+ int scan_enabled;
+ /* Qemu uses translated PC scancodes internally. To avoid multiple
+ conversions we do the translation (if any) in the PS/2 emulation
+ not the keyboard controller. */
+ int translate;
+ int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
+} PS2KbdState;
+
+typedef struct {
+ PS2State common;
+ uint8_t mouse_status;
+ uint8_t mouse_resolution;
+ uint8_t mouse_sample_rate;
+ uint8_t mouse_wrap;
+ uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
+ uint8_t mouse_detect_state;
+ int mouse_dx; /* current values, needed for 'poll' mode */
+ int mouse_dy;
+ int mouse_dz;
+ uint8_t mouse_buttons;
+} PS2MouseState;
+
+/* Table to convert from PC scancodes to raw scancodes. */
+static const unsigned char ps2_raw_keycode[128] = {
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+void ps2_queue(void *opaque, int b)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q = &s->queue;
+
+ if (q->count >= PS2_QUEUE_SIZE)
+ return;
+ q->data[q->wptr] = b;
+ if (++q->wptr == PS2_QUEUE_SIZE)
+ q->wptr = 0;
+ q->count++;
+ s->update_irq(s->update_arg, 1);
+}
+
+/*
+ keycode is expressed as follow:
+ bit 7 - 0 key pressed, 1 = key released
+ bits 6-0 - translated scancode set 2
+ */
+static void ps2_put_keycode(void *opaque, int keycode)
+{
+ PS2KbdState *s = opaque;
+
+ /* XXX: add support for scancode sets 1 and 3 */
+ if (!s->translate && keycode < 0xe0 && s->scancode_set == 2)
+ {
+ if (keycode & 0x80)
+ ps2_queue(&s->common, 0xf0);
+ keycode = ps2_raw_keycode[keycode & 0x7f];
+ }
+ ps2_queue(&s->common, keycode);
+}
+
+uint32_t ps2_read_data(void *opaque)
+{
+ PS2State *s = (PS2State *)opaque;
+ PS2Queue *q;
+ int val, index;
+
+ q = &s->queue;
+ if (q->count == 0) {
+ /* NOTE: if no data left, we return the last keyboard one
+ (needed for EMM386) */
+ /* XXX: need a timer to do things correctly */
+ index = q->rptr - 1;
+ if (index < 0)
+ index = PS2_QUEUE_SIZE - 1;
+ val = q->data[index];
+ } else {
+ val = q->data[q->rptr];
+ if (++q->rptr == PS2_QUEUE_SIZE)
+ q->rptr = 0;
+ q->count--;
+ /* reading deasserts IRQ */
+ s->update_irq(s->update_arg, 0);
+ /* reassert IRQs if data left */
+ s->update_irq(s->update_arg, q->count != 0);
+ }
+ return val;
+}
+
+static void ps2_reset_keyboard(PS2KbdState *s)
+{
+ s->scan_enabled = 1;
+ s->scancode_set = 2;
+ kbd_put_ledstate(0);
+}
+
+void ps2_write_keyboard(void *opaque, int val)
+{
+ PS2KbdState *s = (PS2KbdState *)opaque;
+
+ switch(s->common.write_cmd) {
+ default:
+ case -1:
+ switch(val) {
+ case 0x00:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case 0x05:
+ ps2_queue(&s->common, KBD_REPLY_RESEND);
+ break;
+ case KBD_CMD_GET_ID:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ /* We emulate a MF2 AT keyboard here */
+ ps2_queue(&s->common, KBD_REPLY_ID);
+ if (s->translate)
+ ps2_queue(&s->common, 0x41);
+ else
+ ps2_queue(&s->common, 0x83);
+ break;
+ case KBD_CMD_ECHO:
+ ps2_queue(&s->common, KBD_CMD_ECHO);
+ break;
+ case KBD_CMD_ENABLE:
+ s->scan_enabled = 1;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_SCANCODE:
+ case KBD_CMD_SET_LEDS:
+ case KBD_CMD_SET_RATE:
+ s->common.write_cmd = val;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET_DISABLE:
+ ps2_reset_keyboard(s);
+ s->scan_enabled = 0;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET_ENABLE:
+ ps2_reset_keyboard(s);
+ s->scan_enabled = 1;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ case KBD_CMD_RESET:
+ ps2_reset_keyboard(s);
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ ps2_queue(&s->common, KBD_REPLY_POR);
+ break;
+ default:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ break;
+ }
+ break;
+ case KBD_CMD_SCANCODE:
+ if (val == 0) {
+ if (s->scancode_set == 1)
+ ps2_put_keycode(s, 0x43);
+ else if (s->scancode_set == 2)
+ ps2_put_keycode(s, 0x41);
+ else if (s->scancode_set == 3)
+ ps2_put_keycode(s, 0x3f);
+ } else {
+ if (val >= 1 && val <= 3)
+ s->scancode_set = val;
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ }
+ s->common.write_cmd = -1;
+ break;
+ case KBD_CMD_SET_LEDS:
+ kbd_put_ledstate(val);
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ s->common.write_cmd = -1;
+ break;
+ case KBD_CMD_SET_RATE:
+ ps2_queue(&s->common, KBD_REPLY_ACK);
+ s->common.write_cmd = -1;
+ break;
+ }
+}
+
+/* Set the scancode translation mode.
+ 0 = raw scancodes.
+ 1 = translated scancodes (used by qemu internally). */
+
+void ps2_keyboard_set_translation(void *opaque, int mode)
+{
+ PS2KbdState *s = (PS2KbdState *)opaque;
+ s->translate = mode;
+}
+
+static void ps2_mouse_send_packet(PS2MouseState *s)
+{
+ unsigned int b;
+ int dx1, dy1, dz1;
+
+ dx1 = s->mouse_dx;
+ dy1 = s->mouse_dy;
+ dz1 = s->mouse_dz;
+ /* XXX: increase range to 8 bits ? */
+ if (dx1 > 127)
+ dx1 = 127;
+ else if (dx1 < -127)
+ dx1 = -127;
+ if (dy1 > 127)
+ dy1 = 127;
+ else if (dy1 < -127)
+ dy1 = -127;
+ b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+ ps2_queue(&s->common, b);
+ ps2_queue(&s->common, dx1 & 0xff);
+ ps2_queue(&s->common, dy1 & 0xff);
+ /* extra byte for IMPS/2 or IMEX */
+ switch(s->mouse_type) {
+ default:
+ break;
+ case 3:
+ if (dz1 > 127)
+ dz1 = 127;
+ else if (dz1 < -127)
+ dz1 = -127;
+ ps2_queue(&s->common, dz1 & 0xff);
+ break;
+ case 4:
+ if (dz1 > 7)
+ dz1 = 7;
+ else if (dz1 < -7)
+ dz1 = -7;
+ b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+ ps2_queue(&s->common, b);
+ break;
+ }
+
+ /* update deltas */
+ s->mouse_dx -= dx1;
+ s->mouse_dy -= dy1;
+ s->mouse_dz -= dz1;
+}
+
+static void ps2_mouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ PS2MouseState *s = opaque;
+
+ /* check if deltas are recorded when disabled */
+ if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+ return;
+
+ s->mouse_dx += dx;
+ s->mouse_dy -= dy;
+ s->mouse_dz += dz;
+ /* XXX: SDL sometimes generates nul events: we delete them */
+ if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
+ s->mouse_buttons == buttons_state)
+ return;
+ s->mouse_buttons = buttons_state;
+
+ if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
+ (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
+ for(;;) {
+ /* if not remote, send event. Multiple events are sent if
+ too big deltas */
+ ps2_mouse_send_packet(s);
+ if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+ break;
+ }
+ }
+}
+
+void ps2_mouse_fake_event(void *opaque)
+{
+ ps2_mouse_event(opaque, 1, 0, 0, 0);
+}
+
+void ps2_write_mouse(void *opaque, int val)
+{
+ PS2MouseState *s = (PS2MouseState *)opaque;
+#ifdef DEBUG_MOUSE
+ printf("kbd: write mouse 0x%02x\n", val);
+#endif
+ switch(s->common.write_cmd) {
+ default:
+ case -1:
+ /* mouse command */
+ if (s->mouse_wrap) {
+ if (val == AUX_RESET_WRAP) {
+ s->mouse_wrap = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ return;
+ } else if (val != AUX_RESET) {
+ ps2_queue(&s->common, val);
+ return;
+ }
+ }
+ switch(val) {
+ case AUX_SET_SCALE11:
+ s->mouse_status &= ~MOUSE_STATUS_SCALE21;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_SCALE21:
+ s->mouse_status |= MOUSE_STATUS_SCALE21;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_STREAM:
+ s->mouse_status &= ~MOUSE_STATUS_REMOTE;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_WRAP:
+ s->mouse_wrap = 1;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_REMOTE:
+ s->mouse_status |= MOUSE_STATUS_REMOTE;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_GET_TYPE:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, s->mouse_type);
+ break;
+ case AUX_SET_RES:
+ case AUX_SET_SAMPLE:
+ s->common.write_cmd = val;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_GET_SCALE:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, s->mouse_status);
+ ps2_queue(&s->common, s->mouse_resolution);
+ ps2_queue(&s->common, s->mouse_sample_rate);
+ break;
+ case AUX_POLL:
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_mouse_send_packet(s);
+ break;
+ case AUX_ENABLE_DEV:
+ s->mouse_status |= MOUSE_STATUS_ENABLED;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_DISABLE_DEV:
+ s->mouse_status &= ~MOUSE_STATUS_ENABLED;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_SET_DEFAULT:
+ s->mouse_sample_rate = 100;
+ s->mouse_resolution = 2;
+ s->mouse_status = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ break;
+ case AUX_RESET:
+ s->mouse_sample_rate = 100;
+ s->mouse_resolution = 2;
+ s->mouse_status = 0;
+ s->mouse_type = 0;
+ ps2_queue(&s->common, AUX_ACK);
+ ps2_queue(&s->common, 0xaa);
+ ps2_queue(&s->common, s->mouse_type);
+ break;
+ default:
+ break;
+ }
+ break;
+ case AUX_SET_SAMPLE:
+ s->mouse_sample_rate = val;
+ /* detect IMPS/2 or IMEX */
+ switch(s->mouse_detect_state) {
+ default:
+ case 0:
+ if (val == 200)
+ s->mouse_detect_state = 1;
+ break;
+ case 1:
+ if (val == 100)
+ s->mouse_detect_state = 2;
+ else if (val == 200)
+ s->mouse_detect_state = 3;
+ else
+ s->mouse_detect_state = 0;
+ break;
+ case 2:
+ if (val == 80)
+ s->mouse_type = 3; /* IMPS/2 */
+ s->mouse_detect_state = 0;
+ break;
+ case 3:
+ if (val == 80)
+ s->mouse_type = 4; /* IMEX */
+ s->mouse_detect_state = 0;
+ break;
+ }
+ ps2_queue(&s->common, AUX_ACK);
+ s->common.write_cmd = -1;
+ break;
+ case AUX_SET_RES:
+ s->mouse_resolution = val;
+ ps2_queue(&s->common, AUX_ACK);
+ s->common.write_cmd = -1;
+ break;
+ }
+}
+
+static void ps2_common_reset(PS2State *s)
+{
+ PS2Queue *q;
+ s->write_cmd = -1;
+ q = &s->queue;
+ q->rptr = 0;
+ q->wptr = 0;
+ q->count = 0;
+ s->update_irq(s->update_arg, 0);
+}
+
+static void ps2_kbd_reset(void *opaque)
+{
+ PS2KbdState *s = (PS2KbdState *) opaque;
+
+ ps2_common_reset(&s->common);
+ s->scan_enabled = 0;
+ s->translate = 0;
+ s->scancode_set = 0;
+}
+
+static void ps2_mouse_reset(void *opaque)
+{
+ PS2MouseState *s = (PS2MouseState *) opaque;
+
+ ps2_common_reset(&s->common);
+ s->mouse_status = 0;
+ s->mouse_resolution = 0;
+ s->mouse_sample_rate = 0;
+ s->mouse_wrap = 0;
+ s->mouse_type = 0;
+ s->mouse_detect_state = 0;
+ s->mouse_dx = 0;
+ s->mouse_dy = 0;
+ s->mouse_dz = 0;
+ s->mouse_buttons = 0;
+}
+
+static const VMStateDescription vmstate_ps2_common = {
+ .name = "PS2 Common State",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(write_cmd, PS2State),
+ VMSTATE_INT32(queue.rptr, PS2State),
+ VMSTATE_INT32(queue.wptr, PS2State),
+ VMSTATE_INT32(queue.count, PS2State),
+ VMSTATE_BUFFER(queue.data, PS2State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ps2_kbd_post_load(void* opaque, int version_id)
+{
+ PS2KbdState *s = (PS2KbdState*)opaque;
+
+ if (version_id == 2)
+ s->scancode_set=2;
+ return 0;
+}
+
+static const VMStateDescription vmstate_ps2_keyboard = {
+ .name = "ps2kbd",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .post_load = ps2_kbd_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
+ VMSTATE_INT32(scan_enabled, PS2KbdState),
+ VMSTATE_INT32(translate, PS2KbdState),
+ VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_ps2_mouse = {
+ .name = "ps2mouse",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
+ VMSTATE_UINT8(mouse_status, PS2MouseState),
+ VMSTATE_UINT8(mouse_resolution, PS2MouseState),
+ VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
+ VMSTATE_UINT8(mouse_wrap, PS2MouseState),
+ VMSTATE_UINT8(mouse_type, PS2MouseState),
+ VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
+ VMSTATE_INT32(mouse_dx, PS2MouseState),
+ VMSTATE_INT32(mouse_dy, PS2MouseState),
+ VMSTATE_INT32(mouse_dz, PS2MouseState),
+ VMSTATE_UINT8(mouse_buttons, PS2MouseState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
+{
+ PS2KbdState *s = (PS2KbdState *)qemu_mallocz(sizeof(PS2KbdState));
+
+ s->common.update_irq = update_irq;
+ s->common.update_arg = update_arg;
+ s->scancode_set = 2;
+ vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
+ qemu_add_kbd_event_handler(ps2_put_keycode, s);
+ qemu_register_reset(ps2_kbd_reset, s);
+ return s;
+}
+
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
+{
+ PS2MouseState *s = (PS2MouseState *)qemu_mallocz(sizeof(PS2MouseState));
+
+ s->common.update_irq = update_irq;
+ s->common.update_arg = update_arg;
+ vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
+ qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
+ qemu_register_reset(ps2_mouse_reset, s);
+ return s;
+}
diff --git a/hw/ps2.h b/hw/ps2.h
new file mode 100644
index 0000000..32a4231
--- /dev/null
+++ b/hw/ps2.h
@@ -0,0 +1,9 @@
+/* ps2.c */
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
+void ps2_write_mouse(void *, int val);
+void ps2_write_keyboard(void *, int val);
+uint32_t ps2_read_data(void *);
+void ps2_queue(void *, int b);
+void ps2_keyboard_set_translation(void *opaque, int mode);
+void ps2_mouse_fake_event(void *opaque);
diff --git a/hw/ptimer.c b/hw/ptimer.c
new file mode 100644
index 0000000..4ddbc59
--- /dev/null
+++ b/hw/ptimer.c
@@ -0,0 +1,244 @@
+/*
+ * General purpose implementation of a simple periodic countdown timer.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GNU LGPL.
+ */
+#include "hw.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+
+struct ptimer_state
+{
+ int enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot. */
+ uint64_t limit;
+ uint64_t delta;
+ uint32_t period_frac;
+ int64_t period;
+ int64_t last_event;
+ int64_t next_event;
+ QEMUBH *bh;
+ QEMUTimer *timer;
+};
+
+/* Use a bottom-half routine to avoid reentrancy issues. */
+static void ptimer_trigger(ptimer_state *s)
+{
+ if (s->bh) {
+ qemu_bh_schedule(s->bh);
+ }
+}
+
+static void ptimer_reload(ptimer_state *s)
+{
+ if (s->delta == 0) {
+ ptimer_trigger(s);
+ s->delta = s->limit;
+ }
+ if (s->delta == 0 || s->period == 0) {
+ fprintf(stderr, "Timer with period zero, disabling\n");
+ s->enabled = 0;
+ return;
+ }
+
+ s->last_event = s->next_event;
+ s->next_event = s->last_event + s->delta * s->period;
+ if (s->period_frac) {
+ s->next_event += ((int64_t)s->period_frac * s->delta) >> 32;
+ }
+ qemu_mod_timer(s->timer, s->next_event);
+}
+
+static void ptimer_tick(void *opaque)
+{
+ ptimer_state *s = (ptimer_state *)opaque;
+ ptimer_trigger(s);
+ s->delta = 0;
+ if (s->enabled == 2) {
+ s->enabled = 0;
+ } else {
+ ptimer_reload(s);
+ }
+}
+
+uint64_t ptimer_get_count(ptimer_state *s)
+{
+ int64_t now;
+ uint64_t counter;
+
+ if (s->enabled) {
+ now = qemu_get_clock(vm_clock);
+ /* Figure out the current counter value. */
+ if (now - s->next_event > 0
+ || s->period == 0) {
+ /* Prevent timer underflowing if it should already have
+ triggered. */
+ counter = 0;
+ } else {
+ uint64_t rem;
+ uint64_t div;
+ int clz1, clz2;
+ int shift;
+
+ /* We need to divide time by period, where time is stored in
+ rem (64-bit integer) and period is stored in period/period_frac
+ (64.32 fixed point).
+
+ Doing full precision division is hard, so scale values and
+ do a 64-bit division. The result should be rounded down,
+ so that the rounding error never causes the timer to go
+ backwards.
+ */
+
+ rem = s->next_event - now;
+ div = s->period;
+
+ clz1 = clz64(rem);
+ clz2 = clz64(div);
+ shift = clz1 < clz2 ? clz1 : clz2;
+
+ rem <<= shift;
+ div <<= shift;
+ if (shift >= 32) {
+ div |= ((uint64_t)s->period_frac << (shift - 32));
+ } else {
+ if (shift != 0)
+ div |= (s->period_frac >> (32 - shift));
+ /* Look at remaining bits of period_frac and round div up if
+ necessary. */
+ if ((uint32_t)(s->period_frac << shift))
+ div += 1;
+ }
+ counter = rem / div;
+ }
+ } else {
+ counter = s->delta;
+ }
+ return counter;
+}
+
+void ptimer_set_count(ptimer_state *s, uint64_t count)
+{
+ s->delta = count;
+ if (s->enabled) {
+ s->next_event = qemu_get_clock(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+void ptimer_run(ptimer_state *s, int oneshot)
+{
+ if (s->enabled) {
+ return;
+ }
+ if (s->period == 0) {
+ fprintf(stderr, "Timer with period zero, disabling\n");
+ return;
+ }
+ s->enabled = oneshot ? 2 : 1;
+ s->next_event = qemu_get_clock(vm_clock);
+ ptimer_reload(s);
+}
+
+/* Pause a timer. Note that this may cause it to "lose" time, even if it
+ is immediately restarted. */
+void ptimer_stop(ptimer_state *s)
+{
+ if (!s->enabled)
+ return;
+
+ s->delta = ptimer_get_count(s);
+ qemu_del_timer(s->timer);
+ s->enabled = 0;
+}
+
+/* Set counter increment interval in nanoseconds. */
+void ptimer_set_period(ptimer_state *s, int64_t period)
+{
+ s->period = period;
+ s->period_frac = 0;
+ if (s->enabled) {
+ s->next_event = qemu_get_clock(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+/* Set counter frequency in Hz. */
+void ptimer_set_freq(ptimer_state *s, uint32_t freq)
+{
+ s->period = 1000000000ll / freq;
+ s->period_frac = (1000000000ll << 32) / freq;
+ if (s->enabled) {
+ s->next_event = qemu_get_clock(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+/* Set the initial countdown value. If reload is nonzero then also set
+ count = limit. */
+void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
+{
+ s->limit = limit;
+ if (reload)
+ s->delta = limit;
+ if (s->enabled && reload) {
+ s->next_event = qemu_get_clock(vm_clock);
+ ptimer_reload(s);
+ }
+}
+
+void qemu_put_ptimer(QEMUFile *f, ptimer_state *s)
+{
+ qemu_put_byte(f, s->enabled);
+ qemu_put_be64s(f, &s->limit);
+ qemu_put_be64s(f, &s->delta);
+ qemu_put_be32s(f, &s->period_frac);
+ qemu_put_sbe64s(f, &s->period);
+ qemu_put_sbe64s(f, &s->last_event);
+ qemu_put_sbe64s(f, &s->next_event);
+ qemu_put_timer(f, s->timer);
+}
+
+void qemu_get_ptimer(QEMUFile *f, ptimer_state *s)
+{
+ s->enabled = qemu_get_byte(f);
+ qemu_get_be64s(f, &s->limit);
+ qemu_get_be64s(f, &s->delta);
+ qemu_get_be32s(f, &s->period_frac);
+ qemu_get_sbe64s(f, &s->period);
+ qemu_get_sbe64s(f, &s->last_event);
+ qemu_get_sbe64s(f, &s->next_event);
+ qemu_get_timer(f, s->timer);
+}
+
+static int get_ptimer(QEMUFile *f, void *pv, size_t size)
+{
+ ptimer_state *v = pv;
+
+ qemu_get_ptimer(f, v);
+ return 0;
+}
+
+static void put_ptimer(QEMUFile *f, void *pv, size_t size)
+{
+ ptimer_state *v = pv;
+
+ qemu_put_ptimer(f, v);
+}
+
+const VMStateInfo vmstate_info_ptimer = {
+ .name = "ptimer",
+ .get = get_ptimer,
+ .put = put_ptimer,
+};
+
+ptimer_state *ptimer_init(QEMUBH *bh)
+{
+ ptimer_state *s;
+
+ s = (ptimer_state *)qemu_mallocz(sizeof(ptimer_state));
+ s->bh = bh;
+ s->timer = qemu_new_timer(vm_clock, ptimer_tick, s);
+ return s;
+}
diff --git a/hw/pxa.h b/hw/pxa.h
new file mode 100644
index 0000000..f73d33b
--- /dev/null
+++ b/hw/pxa.h
@@ -0,0 +1,212 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#ifndef PXA_H
+# define PXA_H "pxa.h"
+
+/* Interrupt numbers */
+# define PXA2XX_PIC_SSP3 0
+# define PXA2XX_PIC_USBH2 2
+# define PXA2XX_PIC_USBH1 3
+# define PXA2XX_PIC_KEYPAD 4
+# define PXA2XX_PIC_PWRI2C 6
+# define PXA25X_PIC_HWUART 7
+# define PXA27X_PIC_OST_4_11 7
+# define PXA2XX_PIC_GPIO_0 8
+# define PXA2XX_PIC_GPIO_1 9
+# define PXA2XX_PIC_GPIO_X 10
+# define PXA2XX_PIC_I2S 13
+# define PXA26X_PIC_ASSP 15
+# define PXA25X_PIC_NSSP 16
+# define PXA27X_PIC_SSP2 16
+# define PXA2XX_PIC_LCD 17
+# define PXA2XX_PIC_I2C 18
+# define PXA2XX_PIC_ICP 19
+# define PXA2XX_PIC_STUART 20
+# define PXA2XX_PIC_BTUART 21
+# define PXA2XX_PIC_FFUART 22
+# define PXA2XX_PIC_MMC 23
+# define PXA2XX_PIC_SSP 24
+# define PXA2XX_PIC_DMA 25
+# define PXA2XX_PIC_OST_0 26
+# define PXA2XX_PIC_RTC1HZ 30
+# define PXA2XX_PIC_RTCALARM 31
+
+/* DMA requests */
+# define PXA2XX_RX_RQ_I2S 2
+# define PXA2XX_TX_RQ_I2S 3
+# define PXA2XX_RX_RQ_BTUART 4
+# define PXA2XX_TX_RQ_BTUART 5
+# define PXA2XX_RX_RQ_FFUART 6
+# define PXA2XX_TX_RQ_FFUART 7
+# define PXA2XX_RX_RQ_SSP1 13
+# define PXA2XX_TX_RQ_SSP1 14
+# define PXA2XX_RX_RQ_SSP2 15
+# define PXA2XX_TX_RQ_SSP2 16
+# define PXA2XX_RX_RQ_ICP 17
+# define PXA2XX_TX_RQ_ICP 18
+# define PXA2XX_RX_RQ_STUART 19
+# define PXA2XX_TX_RQ_STUART 20
+# define PXA2XX_RX_RQ_MMCI 21
+# define PXA2XX_TX_RQ_MMCI 22
+# define PXA2XX_USB_RQ(x) ((x) + 24)
+# define PXA2XX_RX_RQ_SSP3 66
+# define PXA2XX_TX_RQ_SSP3 67
+
+# define PXA2XX_SDRAM_BASE 0xa0000000
+# define PXA2XX_INTERNAL_BASE 0x5c000000
+# define PXA2XX_INTERNAL_SIZE 0x40000
+
+/* pxa2xx_pic.c */
+qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env);
+
+/* pxa2xx_timer.c */
+void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs);
+void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4);
+
+/* pxa2xx_gpio.c */
+DeviceState *pxa2xx_gpio_init(target_phys_addr_t base,
+ CPUState *env, qemu_irq *pic, int lines);
+void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler);
+
+/* pxa2xx_dma.c */
+typedef struct PXA2xxDMAState PXA2xxDMAState;
+PXA2xxDMAState *pxa255_dma_init(target_phys_addr_t base,
+ qemu_irq irq);
+PXA2xxDMAState *pxa27x_dma_init(target_phys_addr_t base,
+ qemu_irq irq);
+void pxa2xx_dma_request(PXA2xxDMAState *s, int req_num, int on);
+
+/* pxa2xx_lcd.c */
+typedef struct PXA2xxLCDState PXA2xxLCDState;
+PXA2xxLCDState *pxa2xx_lcdc_init(target_phys_addr_t base,
+ qemu_irq irq);
+void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler);
+void pxa2xx_lcdc_oritentation(void *opaque, int angle);
+
+/* pxa2xx_mmci.c */
+typedef struct PXA2xxMMCIState PXA2xxMMCIState;
+PXA2xxMMCIState *pxa2xx_mmci_init(target_phys_addr_t base,
+ BlockDriverState *bd, qemu_irq irq, void *dma);
+void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
+ qemu_irq coverswitch);
+
+/* pxa2xx_pcmcia.c */
+typedef struct PXA2xxPCMCIAState PXA2xxPCMCIAState;
+PXA2xxPCMCIAState *pxa2xx_pcmcia_init(target_phys_addr_t base);
+int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card);
+int pxa2xx_pcmcia_dettach(void *opaque);
+void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq);
+
+/* pxa2xx_keypad.c */
+struct keymap {
+ int column;
+ int row;
+};
+typedef struct PXA2xxKeyPadState PXA2xxKeyPadState;
+PXA2xxKeyPadState *pxa27x_keypad_init(target_phys_addr_t base,
+ qemu_irq irq);
+void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
+ int size);
+
+/* pxa2xx.c */
+typedef struct PXA2xxI2CState PXA2xxI2CState;
+PXA2xxI2CState *pxa2xx_i2c_init(target_phys_addr_t base,
+ qemu_irq irq, uint32_t page_size);
+i2c_bus *pxa2xx_i2c_bus(PXA2xxI2CState *s);
+
+typedef struct PXA2xxI2SState PXA2xxI2SState;
+typedef struct PXA2xxFIrState PXA2xxFIrState;
+
+typedef struct {
+ CPUState *env;
+ qemu_irq *pic;
+ qemu_irq reset;
+ PXA2xxDMAState *dma;
+ DeviceState *gpio;
+ PXA2xxLCDState *lcd;
+ SSIBus **ssp;
+ PXA2xxI2CState *i2c[2];
+ PXA2xxMMCIState *mmc;
+ PXA2xxPCMCIAState *pcmcia[2];
+ PXA2xxI2SState *i2s;
+ PXA2xxFIrState *fir;
+ PXA2xxKeyPadState *kp;
+
+ /* Power management */
+ target_phys_addr_t pm_base;
+ uint32_t pm_regs[0x40];
+
+ /* Clock management */
+ target_phys_addr_t cm_base;
+ uint32_t cm_regs[4];
+ uint32_t clkcfg;
+
+ /* Memory management */
+ target_phys_addr_t mm_base;
+ uint32_t mm_regs[0x1a];
+
+ /* Performance monitoring */
+ uint32_t pmnc;
+
+ /* Real-Time clock */
+ target_phys_addr_t rtc_base;
+ uint32_t rttr;
+ uint32_t rtsr;
+ uint32_t rtar;
+ uint32_t rdar1;
+ uint32_t rdar2;
+ uint32_t ryar1;
+ uint32_t ryar2;
+ uint32_t swar1;
+ uint32_t swar2;
+ uint32_t piar;
+ uint32_t last_rcnr;
+ uint32_t last_rdcr;
+ uint32_t last_rycr;
+ uint32_t last_swcr;
+ uint32_t last_rtcpicr;
+ int64_t last_hz;
+ int64_t last_sw;
+ int64_t last_pi;
+ QEMUTimer *rtc_hz;
+ QEMUTimer *rtc_rdal1;
+ QEMUTimer *rtc_rdal2;
+ QEMUTimer *rtc_swal1;
+ QEMUTimer *rtc_swal2;
+ QEMUTimer *rtc_pi;
+} PXA2xxState;
+
+struct PXA2xxI2SState {
+ qemu_irq irq;
+ PXA2xxDMAState *dma;
+ void (*data_req)(void *, int, int);
+
+ uint32_t control[2];
+ uint32_t status;
+ uint32_t mask;
+ uint32_t clk;
+
+ int enable;
+ int rx_len;
+ int tx_len;
+ void (*codec_out)(void *, uint32_t);
+ uint32_t (*codec_in)(void *);
+ void *opaque;
+
+ int fifo_len;
+ uint32_t fifo[16];
+};
+
+# define PA_FMT "0x%08lx"
+# define REG_FMT "0x" TARGET_FMT_plx
+
+PXA2xxState *pxa270_init(unsigned int sdram_size, const char *revision);
+PXA2xxState *pxa255_init(unsigned int sdram_size);
+
+#endif /* PXA_H */
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
new file mode 100644
index 0000000..d966846
--- /dev/null
+++ b/hw/pxa2xx.c
@@ -0,0 +1,2292 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "pxa.h"
+#include "sysemu.h"
+#include "pc.h"
+#include "i2c.h"
+#include "ssi.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "blockdev.h"
+
+static struct {
+ target_phys_addr_t io_base;
+ int irqn;
+} pxa255_serial[] = {
+ { 0x40100000, PXA2XX_PIC_FFUART },
+ { 0x40200000, PXA2XX_PIC_BTUART },
+ { 0x40700000, PXA2XX_PIC_STUART },
+ { 0x41600000, PXA25X_PIC_HWUART },
+ { 0, 0 }
+}, pxa270_serial[] = {
+ { 0x40100000, PXA2XX_PIC_FFUART },
+ { 0x40200000, PXA2XX_PIC_BTUART },
+ { 0x40700000, PXA2XX_PIC_STUART },
+ { 0, 0 }
+};
+
+typedef struct PXASSPDef {
+ target_phys_addr_t io_base;
+ int irqn;
+} PXASSPDef;
+
+#if 0
+static PXASSPDef pxa250_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0, 0 }
+};
+#endif
+
+static PXASSPDef pxa255_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0x41400000, PXA25X_PIC_NSSP },
+ { 0, 0 }
+};
+
+#if 0
+static PXASSPDef pxa26x_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0x41400000, PXA25X_PIC_NSSP },
+ { 0x41500000, PXA26X_PIC_ASSP },
+ { 0, 0 }
+};
+#endif
+
+static PXASSPDef pxa27x_ssp[] = {
+ { 0x41000000, PXA2XX_PIC_SSP },
+ { 0x41700000, PXA27X_PIC_SSP2 },
+ { 0x41900000, PXA2XX_PIC_SSP3 },
+ { 0, 0 }
+};
+
+#define PMCR 0x00 /* Power Manager Control register */
+#define PSSR 0x04 /* Power Manager Sleep Status register */
+#define PSPR 0x08 /* Power Manager Scratch-Pad register */
+#define PWER 0x0c /* Power Manager Wake-Up Enable register */
+#define PRER 0x10 /* Power Manager Rising-Edge Detect Enable register */
+#define PFER 0x14 /* Power Manager Falling-Edge Detect Enable register */
+#define PEDR 0x18 /* Power Manager Edge-Detect Status register */
+#define PCFR 0x1c /* Power Manager General Configuration register */
+#define PGSR0 0x20 /* Power Manager GPIO Sleep-State register 0 */
+#define PGSR1 0x24 /* Power Manager GPIO Sleep-State register 1 */
+#define PGSR2 0x28 /* Power Manager GPIO Sleep-State register 2 */
+#define PGSR3 0x2c /* Power Manager GPIO Sleep-State register 3 */
+#define RCSR 0x30 /* Reset Controller Status register */
+#define PSLR 0x34 /* Power Manager Sleep Configuration register */
+#define PTSR 0x38 /* Power Manager Standby Configuration register */
+#define PVCR 0x40 /* Power Manager Voltage Change Control register */
+#define PUCR 0x4c /* Power Manager USIM Card Control/Status register */
+#define PKWR 0x50 /* Power Manager Keyboard Wake-Up Enable register */
+#define PKSR 0x54 /* Power Manager Keyboard Level-Detect Status */
+#define PCMD0 0x80 /* Power Manager I2C Command register File 0 */
+#define PCMD31 0xfc /* Power Manager I2C Command register File 31 */
+
+static uint32_t pxa2xx_pm_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case PMCR ... PCMD31:
+ if (addr & 3)
+ goto fail;
+
+ return s->pm_regs[addr >> 2];
+ default:
+ fail:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_pm_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case PMCR:
+ s->pm_regs[addr >> 2] &= 0x15 & ~(value & 0x2a);
+ s->pm_regs[addr >> 2] |= value & 0x15;
+ break;
+
+ case PSSR: /* Read-clean registers */
+ case RCSR:
+ case PKSR:
+ s->pm_regs[addr >> 2] &= ~value;
+ break;
+
+ default: /* Read-write registers */
+ if (!(addr & 3)) {
+ s->pm_regs[addr >> 2] = value;
+ break;
+ }
+
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_pm_readfn[] = {
+ pxa2xx_pm_read,
+ pxa2xx_pm_read,
+ pxa2xx_pm_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_pm_writefn[] = {
+ pxa2xx_pm_write,
+ pxa2xx_pm_write,
+ pxa2xx_pm_write,
+};
+
+static void pxa2xx_pm_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ int i;
+
+ for (i = 0; i < 0x40; i ++)
+ qemu_put_be32s(f, &s->pm_regs[i]);
+}
+
+static int pxa2xx_pm_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ int i;
+
+ for (i = 0; i < 0x40; i ++)
+ qemu_get_be32s(f, &s->pm_regs[i]);
+
+ return 0;
+}
+
+#define CCCR 0x00 /* Core Clock Configuration register */
+#define CKEN 0x04 /* Clock Enable register */
+#define OSCC 0x08 /* Oscillator Configuration register */
+#define CCSR 0x0c /* Core Clock Status register */
+
+static uint32_t pxa2xx_cm_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case CCCR:
+ case CKEN:
+ case OSCC:
+ return s->cm_regs[addr >> 2];
+
+ case CCSR:
+ return s->cm_regs[CCCR >> 2] | (3 << 28);
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_cm_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case CCCR:
+ case CKEN:
+ s->cm_regs[addr >> 2] = value;
+ break;
+
+ case OSCC:
+ s->cm_regs[addr >> 2] &= ~0x6c;
+ s->cm_regs[addr >> 2] |= value & 0x6e;
+ if ((value >> 1) & 1) /* OON */
+ s->cm_regs[addr >> 2] |= 1 << 0; /* Oscillator is now stable */
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_cm_readfn[] = {
+ pxa2xx_cm_read,
+ pxa2xx_cm_read,
+ pxa2xx_cm_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_cm_writefn[] = {
+ pxa2xx_cm_write,
+ pxa2xx_cm_write,
+ pxa2xx_cm_write,
+};
+
+static void pxa2xx_cm_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ int i;
+
+ for (i = 0; i < 4; i ++)
+ qemu_put_be32s(f, &s->cm_regs[i]);
+ qemu_put_be32s(f, &s->clkcfg);
+ qemu_put_be32s(f, &s->pmnc);
+}
+
+static int pxa2xx_cm_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ int i;
+
+ for (i = 0; i < 4; i ++)
+ qemu_get_be32s(f, &s->cm_regs[i]);
+ qemu_get_be32s(f, &s->clkcfg);
+ qemu_get_be32s(f, &s->pmnc);
+
+ return 0;
+}
+
+static uint32_t pxa2xx_clkpwr_read(void *opaque, int op2, int reg, int crm)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (reg) {
+ case 6: /* Clock Configuration register */
+ return s->clkcfg;
+
+ case 7: /* Power Mode register */
+ return 0;
+
+ default:
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_clkpwr_write(void *opaque, int op2, int reg, int crm,
+ uint32_t value)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ static const char *pwrmode[8] = {
+ "Normal", "Idle", "Deep-idle", "Standby",
+ "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep",
+ };
+
+ switch (reg) {
+ case 6: /* Clock Configuration register */
+ s->clkcfg = value & 0xf;
+ if (value & 2)
+ printf("%s: CPU frequency change attempt\n", __FUNCTION__);
+ break;
+
+ case 7: /* Power Mode register */
+ if (value & 8)
+ printf("%s: CPU voltage change attempt\n", __FUNCTION__);
+ switch (value & 7) {
+ case 0:
+ /* Do nothing */
+ break;
+
+ case 1:
+ /* Idle */
+ if (!(s->cm_regs[CCCR >> 2] & (1 << 31))) { /* CPDIS */
+ cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+ break;
+ }
+ /* Fall through. */
+
+ case 2:
+ /* Deep-Idle */
+ cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+ s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
+ goto message;
+
+ case 3:
+ s->env->uncached_cpsr =
+ ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
+ s->env->cp15.c1_sys = 0;
+ s->env->cp15.c1_coproc = 0;
+ s->env->cp15.c2_base0 = 0;
+ s->env->cp15.c3 = 0;
+ s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
+ s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
+
+ /*
+ * The scratch-pad register is almost universally used
+ * for storing the return address on suspend. For the
+ * lack of a resuming bootloader, perform a jump
+ * directly to that address.
+ */
+ memset(s->env->regs, 0, 4 * 15);
+ s->env->regs[15] = s->pm_regs[PSPR >> 2];
+
+#if 0
+ buffer = 0xe59ff000; /* ldr pc, [pc, #0] */
+ cpu_physical_memory_write(0, &buffer, 4);
+ buffer = s->pm_regs[PSPR >> 2];
+ cpu_physical_memory_write(8, &buffer, 4);
+#endif
+
+ /* Suspend */
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
+
+ goto message;
+
+ default:
+ message:
+ printf("%s: machine entered %s mode\n", __FUNCTION__,
+ pwrmode[value & 7]);
+ }
+ break;
+
+ default:
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ break;
+ }
+}
+
+/* Performace Monitoring Registers */
+#define CPPMNC 0 /* Performance Monitor Control register */
+#define CPCCNT 1 /* Clock Counter register */
+#define CPINTEN 4 /* Interrupt Enable register */
+#define CPFLAG 5 /* Overflow Flag register */
+#define CPEVTSEL 8 /* Event Selection register */
+
+#define CPPMN0 0 /* Performance Count register 0 */
+#define CPPMN1 1 /* Performance Count register 1 */
+#define CPPMN2 2 /* Performance Count register 2 */
+#define CPPMN3 3 /* Performance Count register 3 */
+
+static uint32_t pxa2xx_perf_read(void *opaque, int op2, int reg, int crm)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (reg) {
+ case CPPMNC:
+ return s->pmnc;
+ case CPCCNT:
+ if (s->pmnc & 1)
+ return qemu_get_clock(vm_clock);
+ else
+ return 0;
+ case CPINTEN:
+ case CPFLAG:
+ case CPEVTSEL:
+ return 0;
+
+ default:
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_perf_write(void *opaque, int op2, int reg, int crm,
+ uint32_t value)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (reg) {
+ case CPPMNC:
+ s->pmnc = value;
+ break;
+
+ case CPCCNT:
+ case CPINTEN:
+ case CPFLAG:
+ case CPEVTSEL:
+ break;
+
+ default:
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ break;
+ }
+}
+
+static uint32_t pxa2xx_cp14_read(void *opaque, int op2, int reg, int crm)
+{
+ switch (crm) {
+ case 0:
+ return pxa2xx_clkpwr_read(opaque, op2, reg, crm);
+ case 1:
+ return pxa2xx_perf_read(opaque, op2, reg, crm);
+ case 2:
+ switch (reg) {
+ case CPPMN0:
+ case CPPMN1:
+ case CPPMN2:
+ case CPPMN3:
+ return 0;
+ }
+ /* Fall through */
+ default:
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_cp14_write(void *opaque, int op2, int reg, int crm,
+ uint32_t value)
+{
+ switch (crm) {
+ case 0:
+ pxa2xx_clkpwr_write(opaque, op2, reg, crm, value);
+ break;
+ case 1:
+ pxa2xx_perf_write(opaque, op2, reg, crm, value);
+ break;
+ case 2:
+ switch (reg) {
+ case CPPMN0:
+ case CPPMN1:
+ case CPPMN2:
+ case CPPMN3:
+ return;
+ }
+ /* Fall through */
+ default:
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ break;
+ }
+}
+
+#define MDCNFG 0x00 /* SDRAM Configuration register */
+#define MDREFR 0x04 /* SDRAM Refresh Control register */
+#define MSC0 0x08 /* Static Memory Control register 0 */
+#define MSC1 0x0c /* Static Memory Control register 1 */
+#define MSC2 0x10 /* Static Memory Control register 2 */
+#define MECR 0x14 /* Expansion Memory Bus Config register */
+#define SXCNFG 0x1c /* Synchronous Static Memory Config register */
+#define MCMEM0 0x28 /* PC Card Memory Socket 0 Timing register */
+#define MCMEM1 0x2c /* PC Card Memory Socket 1 Timing register */
+#define MCATT0 0x30 /* PC Card Attribute Socket 0 register */
+#define MCATT1 0x34 /* PC Card Attribute Socket 1 register */
+#define MCIO0 0x38 /* PC Card I/O Socket 0 Timing register */
+#define MCIO1 0x3c /* PC Card I/O Socket 1 Timing register */
+#define MDMRS 0x40 /* SDRAM Mode Register Set Config register */
+#define BOOT_DEF 0x44 /* Boot-time Default Configuration register */
+#define ARB_CNTL 0x48 /* Arbiter Control register */
+#define BSCNTR0 0x4c /* Memory Buffer Strength Control register 0 */
+#define BSCNTR1 0x50 /* Memory Buffer Strength Control register 1 */
+#define LCDBSCNTR 0x54 /* LCD Buffer Strength Control register */
+#define MDMRSLP 0x58 /* Low Power SDRAM Mode Set Config register */
+#define BSCNTR2 0x5c /* Memory Buffer Strength Control register 2 */
+#define BSCNTR3 0x60 /* Memory Buffer Strength Control register 3 */
+#define SA1110 0x64 /* SA-1110 Memory Compatibility register */
+
+static uint32_t pxa2xx_mm_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case MDCNFG ... SA1110:
+ if ((addr & 3) == 0)
+ return s->mm_regs[addr >> 2];
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_mm_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case MDCNFG ... SA1110:
+ if ((addr & 3) == 0) {
+ s->mm_regs[addr >> 2] = value;
+ break;
+ }
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_mm_readfn[] = {
+ pxa2xx_mm_read,
+ pxa2xx_mm_read,
+ pxa2xx_mm_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_mm_writefn[] = {
+ pxa2xx_mm_write,
+ pxa2xx_mm_write,
+ pxa2xx_mm_write,
+};
+
+static void pxa2xx_mm_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ int i;
+
+ for (i = 0; i < 0x1a; i ++)
+ qemu_put_be32s(f, &s->mm_regs[i]);
+}
+
+static int pxa2xx_mm_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ int i;
+
+ for (i = 0; i < 0x1a; i ++)
+ qemu_get_be32s(f, &s->mm_regs[i]);
+
+ return 0;
+}
+
+/* Synchronous Serial Ports */
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ int enable;
+ SSIBus *bus;
+
+ uint32_t sscr[2];
+ uint32_t sspsp;
+ uint32_t ssto;
+ uint32_t ssitr;
+ uint32_t sssr;
+ uint8_t sstsa;
+ uint8_t ssrsa;
+ uint8_t ssacd;
+
+ uint32_t rx_fifo[16];
+ int rx_level;
+ int rx_start;
+} PXA2xxSSPState;
+
+#define SSCR0 0x00 /* SSP Control register 0 */
+#define SSCR1 0x04 /* SSP Control register 1 */
+#define SSSR 0x08 /* SSP Status register */
+#define SSITR 0x0c /* SSP Interrupt Test register */
+#define SSDR 0x10 /* SSP Data register */
+#define SSTO 0x28 /* SSP Time-Out register */
+#define SSPSP 0x2c /* SSP Programmable Serial Protocol register */
+#define SSTSA 0x30 /* SSP TX Time Slot Active register */
+#define SSRSA 0x34 /* SSP RX Time Slot Active register */
+#define SSTSS 0x38 /* SSP Time Slot Status register */
+#define SSACD 0x3c /* SSP Audio Clock Divider register */
+
+/* Bitfields for above registers */
+#define SSCR0_SPI(x) (((x) & 0x30) == 0x00)
+#define SSCR0_SSP(x) (((x) & 0x30) == 0x10)
+#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20)
+#define SSCR0_PSP(x) (((x) & 0x30) == 0x30)
+#define SSCR0_SSE (1 << 7)
+#define SSCR0_RIM (1 << 22)
+#define SSCR0_TIM (1 << 23)
+#define SSCR0_MOD (1 << 31)
+#define SSCR0_DSS(x) (((((x) >> 16) & 0x10) | ((x) & 0xf)) + 1)
+#define SSCR1_RIE (1 << 0)
+#define SSCR1_TIE (1 << 1)
+#define SSCR1_LBM (1 << 2)
+#define SSCR1_MWDS (1 << 5)
+#define SSCR1_TFT(x) ((((x) >> 6) & 0xf) + 1)
+#define SSCR1_RFT(x) ((((x) >> 10) & 0xf) + 1)
+#define SSCR1_EFWR (1 << 14)
+#define SSCR1_PINTE (1 << 18)
+#define SSCR1_TINTE (1 << 19)
+#define SSCR1_RSRE (1 << 20)
+#define SSCR1_TSRE (1 << 21)
+#define SSCR1_EBCEI (1 << 29)
+#define SSITR_INT (7 << 5)
+#define SSSR_TNF (1 << 2)
+#define SSSR_RNE (1 << 3)
+#define SSSR_TFS (1 << 5)
+#define SSSR_RFS (1 << 6)
+#define SSSR_ROR (1 << 7)
+#define SSSR_PINT (1 << 18)
+#define SSSR_TINT (1 << 19)
+#define SSSR_EOC (1 << 20)
+#define SSSR_TUR (1 << 21)
+#define SSSR_BCE (1 << 23)
+#define SSSR_RW 0x00bc0080
+
+static void pxa2xx_ssp_int_update(PXA2xxSSPState *s)
+{
+ int level = 0;
+
+ level |= s->ssitr & SSITR_INT;
+ level |= (s->sssr & SSSR_BCE) && (s->sscr[1] & SSCR1_EBCEI);
+ level |= (s->sssr & SSSR_TUR) && !(s->sscr[0] & SSCR0_TIM);
+ level |= (s->sssr & SSSR_EOC) && (s->sssr & (SSSR_TINT | SSSR_PINT));
+ level |= (s->sssr & SSSR_TINT) && (s->sscr[1] & SSCR1_TINTE);
+ level |= (s->sssr & SSSR_PINT) && (s->sscr[1] & SSCR1_PINTE);
+ level |= (s->sssr & SSSR_ROR) && !(s->sscr[0] & SSCR0_RIM);
+ level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE);
+ level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE);
+ qemu_set_irq(s->irq, !!level);
+}
+
+static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s)
+{
+ s->sssr &= ~(0xf << 12); /* Clear RFL */
+ s->sssr &= ~(0xf << 8); /* Clear TFL */
+ s->sssr &= ~SSSR_TFS;
+ s->sssr &= ~SSSR_TNF;
+ if (s->enable) {
+ s->sssr |= ((s->rx_level - 1) & 0xf) << 12;
+ if (s->rx_level >= SSCR1_RFT(s->sscr[1]))
+ s->sssr |= SSSR_RFS;
+ else
+ s->sssr &= ~SSSR_RFS;
+ if (s->rx_level)
+ s->sssr |= SSSR_RNE;
+ else
+ s->sssr &= ~SSSR_RNE;
+ /* TX FIFO is never filled, so it is always in underrun
+ condition if SSP is enabled */
+ s->sssr |= SSSR_TFS;
+ s->sssr |= SSSR_TNF;
+ }
+
+ pxa2xx_ssp_int_update(s);
+}
+
+static uint32_t pxa2xx_ssp_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ uint32_t retval;
+
+ switch (addr) {
+ case SSCR0:
+ return s->sscr[0];
+ case SSCR1:
+ return s->sscr[1];
+ case SSPSP:
+ return s->sspsp;
+ case SSTO:
+ return s->ssto;
+ case SSITR:
+ return s->ssitr;
+ case SSSR:
+ return s->sssr | s->ssitr;
+ case SSDR:
+ if (!s->enable)
+ return 0xffffffff;
+ if (s->rx_level < 1) {
+ printf("%s: SSP Rx Underrun\n", __FUNCTION__);
+ return 0xffffffff;
+ }
+ s->rx_level --;
+ retval = s->rx_fifo[s->rx_start ++];
+ s->rx_start &= 0xf;
+ pxa2xx_ssp_fifo_update(s);
+ return retval;
+ case SSTSA:
+ return s->sstsa;
+ case SSRSA:
+ return s->ssrsa;
+ case SSTSS:
+ return 0;
+ case SSACD:
+ return s->ssacd;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_ssp_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+
+ switch (addr) {
+ case SSCR0:
+ s->sscr[0] = value & 0xc7ffffff;
+ s->enable = value & SSCR0_SSE;
+ if (value & SSCR0_MOD)
+ printf("%s: Attempt to use network mode\n", __FUNCTION__);
+ if (s->enable && SSCR0_DSS(value) < 4)
+ printf("%s: Wrong data size: %i bits\n", __FUNCTION__,
+ SSCR0_DSS(value));
+ if (!(value & SSCR0_SSE)) {
+ s->sssr = 0;
+ s->ssitr = 0;
+ s->rx_level = 0;
+ }
+ pxa2xx_ssp_fifo_update(s);
+ break;
+
+ case SSCR1:
+ s->sscr[1] = value;
+ if (value & (SSCR1_LBM | SSCR1_EFWR))
+ printf("%s: Attempt to use SSP test mode\n", __FUNCTION__);
+ pxa2xx_ssp_fifo_update(s);
+ break;
+
+ case SSPSP:
+ s->sspsp = value;
+ break;
+
+ case SSTO:
+ s->ssto = value;
+ break;
+
+ case SSITR:
+ s->ssitr = value & SSITR_INT;
+ pxa2xx_ssp_int_update(s);
+ break;
+
+ case SSSR:
+ s->sssr &= ~(value & SSSR_RW);
+ pxa2xx_ssp_int_update(s);
+ break;
+
+ case SSDR:
+ if (SSCR0_UWIRE(s->sscr[0])) {
+ if (s->sscr[1] & SSCR1_MWDS)
+ value &= 0xffff;
+ else
+ value &= 0xff;
+ } else
+ /* Note how 32bits overflow does no harm here */
+ value &= (1 << SSCR0_DSS(s->sscr[0])) - 1;
+
+ /* Data goes from here to the Tx FIFO and is shifted out from
+ * there directly to the slave, no need to buffer it.
+ */
+ if (s->enable) {
+ uint32_t readval;
+ readval = ssi_transfer(s->bus, value);
+ if (s->rx_level < 0x10) {
+ s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = readval;
+ } else {
+ s->sssr |= SSSR_ROR;
+ }
+ }
+ pxa2xx_ssp_fifo_update(s);
+ break;
+
+ case SSTSA:
+ s->sstsa = value;
+ break;
+
+ case SSRSA:
+ s->ssrsa = value;
+ break;
+
+ case SSACD:
+ s->ssacd = value;
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_ssp_readfn[] = {
+ pxa2xx_ssp_read,
+ pxa2xx_ssp_read,
+ pxa2xx_ssp_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_ssp_writefn[] = {
+ pxa2xx_ssp_write,
+ pxa2xx_ssp_write,
+ pxa2xx_ssp_write,
+};
+
+static void pxa2xx_ssp_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ int i;
+
+ qemu_put_be32(f, s->enable);
+
+ qemu_put_be32s(f, &s->sscr[0]);
+ qemu_put_be32s(f, &s->sscr[1]);
+ qemu_put_be32s(f, &s->sspsp);
+ qemu_put_be32s(f, &s->ssto);
+ qemu_put_be32s(f, &s->ssitr);
+ qemu_put_be32s(f, &s->sssr);
+ qemu_put_8s(f, &s->sstsa);
+ qemu_put_8s(f, &s->ssrsa);
+ qemu_put_8s(f, &s->ssacd);
+
+ qemu_put_byte(f, s->rx_level);
+ for (i = 0; i < s->rx_level; i ++)
+ qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]);
+}
+
+static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxSSPState *s = (PXA2xxSSPState *) opaque;
+ int i;
+
+ s->enable = qemu_get_be32(f);
+
+ qemu_get_be32s(f, &s->sscr[0]);
+ qemu_get_be32s(f, &s->sscr[1]);
+ qemu_get_be32s(f, &s->sspsp);
+ qemu_get_be32s(f, &s->ssto);
+ qemu_get_be32s(f, &s->ssitr);
+ qemu_get_be32s(f, &s->sssr);
+ qemu_get_8s(f, &s->sstsa);
+ qemu_get_8s(f, &s->ssrsa);
+ qemu_get_8s(f, &s->ssacd);
+
+ s->rx_level = qemu_get_byte(f);
+ s->rx_start = 0;
+ for (i = 0; i < s->rx_level; i ++)
+ s->rx_fifo[i] = qemu_get_byte(f);
+
+ return 0;
+}
+
+static int pxa2xx_ssp_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ PXA2xxSSPState *s = FROM_SYSBUS(PXA2xxSSPState, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_ssp_readfn,
+ pxa2xx_ssp_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ register_savevm(&dev->qdev, "pxa2xx_ssp", -1, 0,
+ pxa2xx_ssp_save, pxa2xx_ssp_load, s);
+
+ s->bus = ssi_create_bus(&dev->qdev, "ssi");
+ return 0;
+}
+
+/* Real-Time Clock */
+#define RCNR 0x00 /* RTC Counter register */
+#define RTAR 0x04 /* RTC Alarm register */
+#define RTSR 0x08 /* RTC Status register */
+#define RTTR 0x0c /* RTC Timer Trim register */
+#define RDCR 0x10 /* RTC Day Counter register */
+#define RYCR 0x14 /* RTC Year Counter register */
+#define RDAR1 0x18 /* RTC Wristwatch Day Alarm register 1 */
+#define RYAR1 0x1c /* RTC Wristwatch Year Alarm register 1 */
+#define RDAR2 0x20 /* RTC Wristwatch Day Alarm register 2 */
+#define RYAR2 0x24 /* RTC Wristwatch Year Alarm register 2 */
+#define SWCR 0x28 /* RTC Stopwatch Counter register */
+#define SWAR1 0x2c /* RTC Stopwatch Alarm register 1 */
+#define SWAR2 0x30 /* RTC Stopwatch Alarm register 2 */
+#define RTCPICR 0x34 /* RTC Periodic Interrupt Counter register */
+#define PIAR 0x38 /* RTC Periodic Interrupt Alarm register */
+
+static inline void pxa2xx_rtc_int_update(PXA2xxState *s)
+{
+ qemu_set_irq(s->pic[PXA2XX_PIC_RTCALARM], !!(s->rtsr & 0x2553));
+}
+
+static void pxa2xx_rtc_hzupdate(PXA2xxState *s)
+{
+ int64_t rt = qemu_get_clock(rt_clock);
+ s->last_rcnr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_rdcr += ((rt - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ s->last_hz = rt;
+}
+
+static void pxa2xx_rtc_swupdate(PXA2xxState *s)
+{
+ int64_t rt = qemu_get_clock(rt_clock);
+ if (s->rtsr & (1 << 12))
+ s->last_swcr += (rt - s->last_sw) / 10;
+ s->last_sw = rt;
+}
+
+static void pxa2xx_rtc_piupdate(PXA2xxState *s)
+{
+ int64_t rt = qemu_get_clock(rt_clock);
+ if (s->rtsr & (1 << 15))
+ s->last_swcr += rt - s->last_pi;
+ s->last_pi = rt;
+}
+
+static inline void pxa2xx_rtc_alarm_update(PXA2xxState *s,
+ uint32_t rtsr)
+{
+ if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0)))
+ qemu_mod_timer(s->rtc_hz, s->last_hz +
+ (((s->rtar - s->last_rcnr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15));
+ else
+ qemu_del_timer(s->rtc_hz);
+
+ if ((rtsr & (1 << 5)) && !(rtsr & (1 << 4)))
+ qemu_mod_timer(s->rtc_rdal1, s->last_hz +
+ (((s->rdar1 - s->last_rdcr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_rdal1);
+
+ if ((rtsr & (1 << 7)) && !(rtsr & (1 << 6)))
+ qemu_mod_timer(s->rtc_rdal2, s->last_hz +
+ (((s->rdar2 - s->last_rdcr) * 1000 *
+ ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_rdal2);
+
+ if ((rtsr & 0x1200) == 0x1200 && !(rtsr & (1 << 8)))
+ qemu_mod_timer(s->rtc_swal1, s->last_sw +
+ (s->swar1 - s->last_swcr) * 10); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_swal1);
+
+ if ((rtsr & 0x1800) == 0x1800 && !(rtsr & (1 << 10)))
+ qemu_mod_timer(s->rtc_swal2, s->last_sw +
+ (s->swar2 - s->last_swcr) * 10); /* TODO: fixup */
+ else
+ qemu_del_timer(s->rtc_swal2);
+
+ if ((rtsr & 0xc000) == 0xc000 && !(rtsr & (1 << 13)))
+ qemu_mod_timer(s->rtc_pi, s->last_pi +
+ (s->piar & 0xffff) - s->last_rtcpicr);
+ else
+ qemu_del_timer(s->rtc_pi);
+}
+
+static inline void pxa2xx_rtc_hz_tick(void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ s->rtsr |= (1 << 0);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_rdal1_tick(void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ s->rtsr |= (1 << 4);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_rdal2_tick(void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ s->rtsr |= (1 << 6);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_swal1_tick(void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ s->rtsr |= (1 << 8);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_swal2_tick(void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ s->rtsr |= (1 << 10);
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static inline void pxa2xx_rtc_pi_tick(void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+ s->rtsr |= (1 << 13);
+ pxa2xx_rtc_piupdate(s);
+ s->last_rtcpicr = 0;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ pxa2xx_rtc_int_update(s);
+}
+
+static uint32_t pxa2xx_rtc_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ return s->rttr;
+ case RTSR:
+ return s->rtsr;
+ case RTAR:
+ return s->rtar;
+ case RDAR1:
+ return s->rdar1;
+ case RDAR2:
+ return s->rdar2;
+ case RYAR1:
+ return s->ryar1;
+ case RYAR2:
+ return s->ryar2;
+ case SWAR1:
+ return s->swar1;
+ case SWAR2:
+ return s->swar2;
+ case PIAR:
+ return s->piar;
+ case RCNR:
+ return s->last_rcnr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ case RDCR:
+ return s->last_rdcr + ((qemu_get_clock(rt_clock) - s->last_hz) << 15) /
+ (1000 * ((s->rttr & 0xffff) + 1));
+ case RYCR:
+ return s->last_rycr;
+ case SWCR:
+ if (s->rtsr & (1 << 12))
+ return s->last_swcr + (qemu_get_clock(rt_clock) - s->last_sw) / 10;
+ else
+ return s->last_swcr;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_rtc_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ switch (addr) {
+ case RTTR:
+ if (!(s->rttr & (1 << 31))) {
+ pxa2xx_rtc_hzupdate(s);
+ s->rttr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ }
+ break;
+
+ case RTSR:
+ if ((s->rtsr ^ value) & (1 << 15))
+ pxa2xx_rtc_piupdate(s);
+
+ if ((s->rtsr ^ value) & (1 << 12))
+ pxa2xx_rtc_swupdate(s);
+
+ if (((s->rtsr ^ value) & 0x4aac) | (value & ~0xdaac))
+ pxa2xx_rtc_alarm_update(s, value);
+
+ s->rtsr = (value & 0xdaac) | (s->rtsr & ~(value & ~0xdaac));
+ pxa2xx_rtc_int_update(s);
+ break;
+
+ case RTAR:
+ s->rtar = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RDAR1:
+ s->rdar1 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RDAR2:
+ s->rdar2 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RYAR1:
+ s->ryar1 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RYAR2:
+ s->ryar2 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case SWAR1:
+ pxa2xx_rtc_swupdate(s);
+ s->swar1 = value;
+ s->last_swcr = 0;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case SWAR2:
+ s->swar2 = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case PIAR:
+ s->piar = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RCNR:
+ pxa2xx_rtc_hzupdate(s);
+ s->last_rcnr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RDCR:
+ pxa2xx_rtc_hzupdate(s);
+ s->last_rdcr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RYCR:
+ s->last_rycr = value;
+ break;
+
+ case SWCR:
+ pxa2xx_rtc_swupdate(s);
+ s->last_swcr = value;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ case RTCPICR:
+ pxa2xx_rtc_piupdate(s);
+ s->last_rtcpicr = value & 0xffff;
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_rtc_readfn[] = {
+ pxa2xx_rtc_read,
+ pxa2xx_rtc_read,
+ pxa2xx_rtc_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_rtc_writefn[] = {
+ pxa2xx_rtc_write,
+ pxa2xx_rtc_write,
+ pxa2xx_rtc_write,
+};
+
+static void pxa2xx_rtc_init(PXA2xxState *s)
+{
+ struct tm tm;
+ int wom;
+
+ s->rttr = 0x7fff;
+ s->rtsr = 0;
+
+ qemu_get_timedate(&tm, 0);
+ wom = ((tm.tm_mday - 1) / 7) + 1;
+
+ s->last_rcnr = (uint32_t) mktimegm(&tm);
+ s->last_rdcr = (wom << 20) | ((tm.tm_wday + 1) << 17) |
+ (tm.tm_hour << 12) | (tm.tm_min << 6) | tm.tm_sec;
+ s->last_rycr = ((tm.tm_year + 1900) << 9) |
+ ((tm.tm_mon + 1) << 5) | tm.tm_mday;
+ s->last_swcr = (tm.tm_hour << 19) |
+ (tm.tm_min << 13) | (tm.tm_sec << 7);
+ s->last_rtcpicr = 0;
+ s->last_hz = s->last_sw = s->last_pi = qemu_get_clock(rt_clock);
+
+ s->rtc_hz = qemu_new_timer(rt_clock, pxa2xx_rtc_hz_tick, s);
+ s->rtc_rdal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal1_tick, s);
+ s->rtc_rdal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_rdal2_tick, s);
+ s->rtc_swal1 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal1_tick, s);
+ s->rtc_swal2 = qemu_new_timer(rt_clock, pxa2xx_rtc_swal2_tick, s);
+ s->rtc_pi = qemu_new_timer(rt_clock, pxa2xx_rtc_pi_tick, s);
+}
+
+static void pxa2xx_rtc_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ pxa2xx_rtc_hzupdate(s);
+ pxa2xx_rtc_piupdate(s);
+ pxa2xx_rtc_swupdate(s);
+
+ qemu_put_be32s(f, &s->rttr);
+ qemu_put_be32s(f, &s->rtsr);
+ qemu_put_be32s(f, &s->rtar);
+ qemu_put_be32s(f, &s->rdar1);
+ qemu_put_be32s(f, &s->rdar2);
+ qemu_put_be32s(f, &s->ryar1);
+ qemu_put_be32s(f, &s->ryar2);
+ qemu_put_be32s(f, &s->swar1);
+ qemu_put_be32s(f, &s->swar2);
+ qemu_put_be32s(f, &s->piar);
+ qemu_put_be32s(f, &s->last_rcnr);
+ qemu_put_be32s(f, &s->last_rdcr);
+ qemu_put_be32s(f, &s->last_rycr);
+ qemu_put_be32s(f, &s->last_swcr);
+ qemu_put_be32s(f, &s->last_rtcpicr);
+ qemu_put_sbe64s(f, &s->last_hz);
+ qemu_put_sbe64s(f, &s->last_sw);
+ qemu_put_sbe64s(f, &s->last_pi);
+}
+
+static int pxa2xx_rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ qemu_get_be32s(f, &s->rttr);
+ qemu_get_be32s(f, &s->rtsr);
+ qemu_get_be32s(f, &s->rtar);
+ qemu_get_be32s(f, &s->rdar1);
+ qemu_get_be32s(f, &s->rdar2);
+ qemu_get_be32s(f, &s->ryar1);
+ qemu_get_be32s(f, &s->ryar2);
+ qemu_get_be32s(f, &s->swar1);
+ qemu_get_be32s(f, &s->swar2);
+ qemu_get_be32s(f, &s->piar);
+ qemu_get_be32s(f, &s->last_rcnr);
+ qemu_get_be32s(f, &s->last_rdcr);
+ qemu_get_be32s(f, &s->last_rycr);
+ qemu_get_be32s(f, &s->last_swcr);
+ qemu_get_be32s(f, &s->last_rtcpicr);
+ qemu_get_sbe64s(f, &s->last_hz);
+ qemu_get_sbe64s(f, &s->last_sw);
+ qemu_get_sbe64s(f, &s->last_pi);
+
+ pxa2xx_rtc_alarm_update(s, s->rtsr);
+
+ return 0;
+}
+
+/* I2C Interface */
+typedef struct {
+ i2c_slave i2c;
+ PXA2xxI2CState *host;
+} PXA2xxI2CSlaveState;
+
+struct PXA2xxI2CState {
+ PXA2xxI2CSlaveState *slave;
+ i2c_bus *bus;
+ qemu_irq irq;
+ target_phys_addr_t offset;
+
+ uint16_t control;
+ uint16_t status;
+ uint8_t ibmr;
+ uint8_t data;
+};
+
+#define IBMR 0x80 /* I2C Bus Monitor register */
+#define IDBR 0x88 /* I2C Data Buffer register */
+#define ICR 0x90 /* I2C Control register */
+#define ISR 0x98 /* I2C Status register */
+#define ISAR 0xa0 /* I2C Slave Address register */
+
+static void pxa2xx_i2c_update(PXA2xxI2CState *s)
+{
+ uint16_t level = 0;
+ level |= s->status & s->control & (1 << 10); /* BED */
+ level |= (s->status & (1 << 7)) && (s->control & (1 << 9)); /* IRF */
+ level |= (s->status & (1 << 6)) && (s->control & (1 << 8)); /* ITE */
+ level |= s->status & (1 << 9); /* SAD */
+ qemu_set_irq(s->irq, !!level);
+}
+
+/* These are only stubs now. */
+static void pxa2xx_i2c_event(i2c_slave *i2c, enum i2c_event event)
+{
+ PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
+ PXA2xxI2CState *s = slave->host;
+
+ switch (event) {
+ case I2C_START_SEND:
+ s->status |= (1 << 9); /* set SAD */
+ s->status &= ~(1 << 0); /* clear RWM */
+ break;
+ case I2C_START_RECV:
+ s->status |= (1 << 9); /* set SAD */
+ s->status |= 1 << 0; /* set RWM */
+ break;
+ case I2C_FINISH:
+ s->status |= (1 << 4); /* set SSD */
+ break;
+ case I2C_NACK:
+ s->status |= 1 << 1; /* set ACKNAK */
+ break;
+ }
+ pxa2xx_i2c_update(s);
+}
+
+static int pxa2xx_i2c_rx(i2c_slave *i2c)
+{
+ PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
+ PXA2xxI2CState *s = slave->host;
+ if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+ return 0;
+
+ if (s->status & (1 << 0)) { /* RWM */
+ s->status |= 1 << 6; /* set ITE */
+ }
+ pxa2xx_i2c_update(s);
+
+ return s->data;
+}
+
+static int pxa2xx_i2c_tx(i2c_slave *i2c, uint8_t data)
+{
+ PXA2xxI2CSlaveState *slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, i2c);
+ PXA2xxI2CState *s = slave->host;
+ if ((s->control & (1 << 14)) || !(s->control & (1 << 6)))
+ return 1;
+
+ if (!(s->status & (1 << 0))) { /* RWM */
+ s->status |= 1 << 7; /* set IRF */
+ s->data = data;
+ }
+ pxa2xx_i2c_update(s);
+
+ return 1;
+}
+
+static uint32_t pxa2xx_i2c_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxI2CState *s = (PXA2xxI2CState *) opaque;
+
+ addr -= s->offset;
+ switch (addr) {
+ case ICR:
+ return s->control;
+ case ISR:
+ return s->status | (i2c_bus_busy(s->bus) << 2);
+ case ISAR:
+ return s->slave->i2c.address;
+ case IDBR:
+ return s->data;
+ case IBMR:
+ if (s->status & (1 << 2))
+ s->ibmr ^= 3; /* Fake SCL and SDA pin changes */
+ else
+ s->ibmr = 0;
+ return s->ibmr;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_i2c_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxI2CState *s = (PXA2xxI2CState *) opaque;
+ int ack;
+
+ addr -= s->offset;
+ switch (addr) {
+ case ICR:
+ s->control = value & 0xfff7;
+ if ((value & (1 << 3)) && (value & (1 << 6))) { /* TB and IUE */
+ /* TODO: slave mode */
+ if (value & (1 << 0)) { /* START condition */
+ if (s->data & 1)
+ s->status |= 1 << 0; /* set RWM */
+ else
+ s->status &= ~(1 << 0); /* clear RWM */
+ ack = !i2c_start_transfer(s->bus, s->data >> 1, s->data & 1);
+ } else {
+ if (s->status & (1 << 0)) { /* RWM */
+ s->data = i2c_recv(s->bus);
+ if (value & (1 << 2)) /* ACKNAK */
+ i2c_nack(s->bus);
+ ack = 1;
+ } else
+ ack = !i2c_send(s->bus, s->data);
+ }
+
+ if (value & (1 << 1)) /* STOP condition */
+ i2c_end_transfer(s->bus);
+
+ if (ack) {
+ if (value & (1 << 0)) /* START condition */
+ s->status |= 1 << 6; /* set ITE */
+ else
+ if (s->status & (1 << 0)) /* RWM */
+ s->status |= 1 << 7; /* set IRF */
+ else
+ s->status |= 1 << 6; /* set ITE */
+ s->status &= ~(1 << 1); /* clear ACKNAK */
+ } else {
+ s->status |= 1 << 6; /* set ITE */
+ s->status |= 1 << 10; /* set BED */
+ s->status |= 1 << 1; /* set ACKNAK */
+ }
+ }
+ if (!(value & (1 << 3)) && (value & (1 << 6))) /* !TB and IUE */
+ if (value & (1 << 4)) /* MA */
+ i2c_end_transfer(s->bus);
+ pxa2xx_i2c_update(s);
+ break;
+
+ case ISR:
+ s->status &= ~(value & 0x07f0);
+ pxa2xx_i2c_update(s);
+ break;
+
+ case ISAR:
+ i2c_set_slave_address(&s->slave->i2c, value & 0x7f);
+ break;
+
+ case IDBR:
+ s->data = value & 0xff;
+ break;
+
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_i2c_readfn[] = {
+ pxa2xx_i2c_read,
+ pxa2xx_i2c_read,
+ pxa2xx_i2c_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_i2c_writefn[] = {
+ pxa2xx_i2c_write,
+ pxa2xx_i2c_write,
+ pxa2xx_i2c_write,
+};
+
+static const VMStateDescription vmstate_pxa2xx_i2c_slave = {
+ .name = "pxa2xx_i2c_slave",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_I2C_SLAVE(i2c, PXA2xxI2CSlaveState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pxa2xx_i2c = {
+ .name = "pxa2xx_i2c",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(control, PXA2xxI2CState),
+ VMSTATE_UINT16(status, PXA2xxI2CState),
+ VMSTATE_UINT8(ibmr, PXA2xxI2CState),
+ VMSTATE_UINT8(data, PXA2xxI2CState),
+ VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState,
+ vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState *),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int pxa2xx_i2c_slave_init(i2c_slave *i2c)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static I2CSlaveInfo pxa2xx_i2c_slave_info = {
+ .qdev.name = "pxa2xx-i2c-slave",
+ .qdev.size = sizeof(PXA2xxI2CSlaveState),
+ .init = pxa2xx_i2c_slave_init,
+ .event = pxa2xx_i2c_event,
+ .recv = pxa2xx_i2c_rx,
+ .send = pxa2xx_i2c_tx
+};
+
+PXA2xxI2CState *pxa2xx_i2c_init(target_phys_addr_t base,
+ qemu_irq irq, uint32_t region_size)
+{
+ int iomemtype;
+ DeviceState *dev;
+ PXA2xxI2CState *s = qemu_mallocz(sizeof(PXA2xxI2CState));
+
+ /* FIXME: Should the slave device really be on a separate bus? */
+ dev = i2c_create_slave(i2c_init_bus(NULL, "dummy"), "pxa2xx-i2c-slave", 0);
+ s->slave = FROM_I2C_SLAVE(PXA2xxI2CSlaveState, I2C_SLAVE_FROM_QDEV(dev));
+ s->slave->host = s;
+
+ s->irq = irq;
+ s->bus = i2c_init_bus(NULL, "i2c");
+ s->offset = base - (base & (~region_size) & TARGET_PAGE_MASK);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_i2c_readfn,
+ pxa2xx_i2c_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base & ~region_size,
+ region_size + 1, iomemtype);
+
+ vmstate_register(NULL, base, &vmstate_pxa2xx_i2c, s);
+
+ return s;
+}
+
+i2c_bus *pxa2xx_i2c_bus(PXA2xxI2CState *s)
+{
+ return s->bus;
+}
+
+/* PXA Inter-IC Sound Controller */
+static void pxa2xx_i2s_reset(PXA2xxI2SState *i2s)
+{
+ i2s->rx_len = 0;
+ i2s->tx_len = 0;
+ i2s->fifo_len = 0;
+ i2s->clk = 0x1a;
+ i2s->control[0] = 0x00;
+ i2s->control[1] = 0x00;
+ i2s->status = 0x00;
+ i2s->mask = 0x00;
+}
+
+#define SACR_TFTH(val) ((val >> 8) & 0xf)
+#define SACR_RFTH(val) ((val >> 12) & 0xf)
+#define SACR_DREC(val) (val & (1 << 3))
+#define SACR_DPRL(val) (val & (1 << 4))
+
+static inline void pxa2xx_i2s_update(PXA2xxI2SState *i2s)
+{
+ int rfs, tfs;
+ rfs = SACR_RFTH(i2s->control[0]) < i2s->rx_len &&
+ !SACR_DREC(i2s->control[1]);
+ tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) &&
+ i2s->enable && !SACR_DPRL(i2s->control[1]);
+
+ pxa2xx_dma_request(i2s->dma, PXA2XX_RX_RQ_I2S, rfs);
+ pxa2xx_dma_request(i2s->dma, PXA2XX_TX_RQ_I2S, tfs);
+
+ i2s->status &= 0xe0;
+ if (i2s->fifo_len < 16 || !i2s->enable)
+ i2s->status |= 1 << 0; /* TNF */
+ if (i2s->rx_len)
+ i2s->status |= 1 << 1; /* RNE */
+ if (i2s->enable)
+ i2s->status |= 1 << 2; /* BSY */
+ if (tfs)
+ i2s->status |= 1 << 3; /* TFS */
+ if (rfs)
+ i2s->status |= 1 << 4; /* RFS */
+ if (!(i2s->tx_len && i2s->enable))
+ i2s->status |= i2s->fifo_len << 8; /* TFL */
+ i2s->status |= MAX(i2s->rx_len, 0xf) << 12; /* RFL */
+
+ qemu_set_irq(i2s->irq, i2s->status & i2s->mask);
+}
+
+#define SACR0 0x00 /* Serial Audio Global Control register */
+#define SACR1 0x04 /* Serial Audio I2S/MSB-Justified Control register */
+#define SASR0 0x0c /* Serial Audio Interface and FIFO Status register */
+#define SAIMR 0x14 /* Serial Audio Interrupt Mask register */
+#define SAICR 0x18 /* Serial Audio Interrupt Clear register */
+#define SADIV 0x60 /* Serial Audio Clock Divider register */
+#define SADR 0x80 /* Serial Audio Data register */
+
+static uint32_t pxa2xx_i2s_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+
+ switch (addr) {
+ case SACR0:
+ return s->control[0];
+ case SACR1:
+ return s->control[1];
+ case SASR0:
+ return s->status;
+ case SAIMR:
+ return s->mask;
+ case SAICR:
+ return 0;
+ case SADIV:
+ return s->clk;
+ case SADR:
+ if (s->rx_len > 0) {
+ s->rx_len --;
+ pxa2xx_i2s_update(s);
+ return s->codec_in(s->opaque);
+ }
+ return 0;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_i2s_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+ uint32_t *sample;
+
+ switch (addr) {
+ case SACR0:
+ if (value & (1 << 3)) /* RST */
+ pxa2xx_i2s_reset(s);
+ s->control[0] = value & 0xff3d;
+ if (!s->enable && (value & 1) && s->tx_len) { /* ENB */
+ for (sample = s->fifo; s->fifo_len > 0; s->fifo_len --, sample ++)
+ s->codec_out(s->opaque, *sample);
+ s->status &= ~(1 << 7); /* I2SOFF */
+ }
+ if (value & (1 << 4)) /* EFWR */
+ printf("%s: Attempt to use special function\n", __FUNCTION__);
+ s->enable = ((value ^ 4) & 5) == 5; /* ENB && !RST*/
+ pxa2xx_i2s_update(s);
+ break;
+ case SACR1:
+ s->control[1] = value & 0x0039;
+ if (value & (1 << 5)) /* ENLBF */
+ printf("%s: Attempt to use loopback function\n", __FUNCTION__);
+ if (value & (1 << 4)) /* DPRL */
+ s->fifo_len = 0;
+ pxa2xx_i2s_update(s);
+ break;
+ case SAIMR:
+ s->mask = value & 0x0078;
+ pxa2xx_i2s_update(s);
+ break;
+ case SAICR:
+ s->status &= ~(value & (3 << 5));
+ pxa2xx_i2s_update(s);
+ break;
+ case SADIV:
+ s->clk = value & 0x007f;
+ break;
+ case SADR:
+ if (s->tx_len && s->enable) {
+ s->tx_len --;
+ pxa2xx_i2s_update(s);
+ s->codec_out(s->opaque, value);
+ } else if (s->fifo_len < 16) {
+ s->fifo[s->fifo_len ++] = value;
+ pxa2xx_i2s_update(s);
+ }
+ break;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_i2s_readfn[] = {
+ pxa2xx_i2s_read,
+ pxa2xx_i2s_read,
+ pxa2xx_i2s_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_i2s_writefn[] = {
+ pxa2xx_i2s_write,
+ pxa2xx_i2s_write,
+ pxa2xx_i2s_write,
+};
+
+static void pxa2xx_i2s_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+
+ qemu_put_be32s(f, &s->control[0]);
+ qemu_put_be32s(f, &s->control[1]);
+ qemu_put_be32s(f, &s->status);
+ qemu_put_be32s(f, &s->mask);
+ qemu_put_be32s(f, &s->clk);
+
+ qemu_put_be32(f, s->enable);
+ qemu_put_be32(f, s->rx_len);
+ qemu_put_be32(f, s->tx_len);
+ qemu_put_be32(f, s->fifo_len);
+}
+
+static int pxa2xx_i2s_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+
+ qemu_get_be32s(f, &s->control[0]);
+ qemu_get_be32s(f, &s->control[1]);
+ qemu_get_be32s(f, &s->status);
+ qemu_get_be32s(f, &s->mask);
+ qemu_get_be32s(f, &s->clk);
+
+ s->enable = qemu_get_be32(f);
+ s->rx_len = qemu_get_be32(f);
+ s->tx_len = qemu_get_be32(f);
+ s->fifo_len = qemu_get_be32(f);
+
+ return 0;
+}
+
+static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx)
+{
+ PXA2xxI2SState *s = (PXA2xxI2SState *) opaque;
+ uint32_t *sample;
+
+ /* Signal FIFO errors */
+ if (s->enable && s->tx_len)
+ s->status |= 1 << 5; /* TUR */
+ if (s->enable && s->rx_len)
+ s->status |= 1 << 6; /* ROR */
+
+ /* Should be tx - MIN(tx, s->fifo_len) but we don't really need to
+ * handle the cases where it makes a difference. */
+ s->tx_len = tx - s->fifo_len;
+ s->rx_len = rx;
+ /* Note that is s->codec_out wasn't set, we wouldn't get called. */
+ if (s->enable)
+ for (sample = s->fifo; s->fifo_len; s->fifo_len --, sample ++)
+ s->codec_out(s->opaque, *sample);
+ pxa2xx_i2s_update(s);
+}
+
+static PXA2xxI2SState *pxa2xx_i2s_init(target_phys_addr_t base,
+ qemu_irq irq, PXA2xxDMAState *dma)
+{
+ int iomemtype;
+ PXA2xxI2SState *s = (PXA2xxI2SState *)
+ qemu_mallocz(sizeof(PXA2xxI2SState));
+
+ s->irq = irq;
+ s->dma = dma;
+ s->data_req = pxa2xx_i2s_data_req;
+
+ pxa2xx_i2s_reset(s);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_i2s_readfn,
+ pxa2xx_i2s_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x100000, iomemtype);
+
+ register_savevm(NULL, "pxa2xx_i2s", base, 0,
+ pxa2xx_i2s_save, pxa2xx_i2s_load, s);
+
+ return s;
+}
+
+/* PXA Fast Infra-red Communications Port */
+struct PXA2xxFIrState {
+ qemu_irq irq;
+ PXA2xxDMAState *dma;
+ int enable;
+ CharDriverState *chr;
+
+ uint8_t control[3];
+ uint8_t status[2];
+
+ int rx_len;
+ int rx_start;
+ uint8_t rx_fifo[64];
+};
+
+static void pxa2xx_fir_reset(PXA2xxFIrState *s)
+{
+ s->control[0] = 0x00;
+ s->control[1] = 0x00;
+ s->control[2] = 0x00;
+ s->status[0] = 0x00;
+ s->status[1] = 0x00;
+ s->enable = 0;
+}
+
+static inline void pxa2xx_fir_update(PXA2xxFIrState *s)
+{
+ static const int tresh[4] = { 8, 16, 32, 0 };
+ int intr = 0;
+ if ((s->control[0] & (1 << 4)) && /* RXE */
+ s->rx_len >= tresh[s->control[2] & 3]) /* TRIG */
+ s->status[0] |= 1 << 4; /* RFS */
+ else
+ s->status[0] &= ~(1 << 4); /* RFS */
+ if (s->control[0] & (1 << 3)) /* TXE */
+ s->status[0] |= 1 << 3; /* TFS */
+ else
+ s->status[0] &= ~(1 << 3); /* TFS */
+ if (s->rx_len)
+ s->status[1] |= 1 << 2; /* RNE */
+ else
+ s->status[1] &= ~(1 << 2); /* RNE */
+ if (s->control[0] & (1 << 4)) /* RXE */
+ s->status[1] |= 1 << 0; /* RSY */
+ else
+ s->status[1] &= ~(1 << 0); /* RSY */
+
+ intr |= (s->control[0] & (1 << 5)) && /* RIE */
+ (s->status[0] & (1 << 4)); /* RFS */
+ intr |= (s->control[0] & (1 << 6)) && /* TIE */
+ (s->status[0] & (1 << 3)); /* TFS */
+ intr |= (s->control[2] & (1 << 4)) && /* TRAIL */
+ (s->status[0] & (1 << 6)); /* EOC */
+ intr |= (s->control[0] & (1 << 2)) && /* TUS */
+ (s->status[0] & (1 << 1)); /* TUR */
+ intr |= s->status[0] & 0x25; /* FRE, RAB, EIF */
+
+ pxa2xx_dma_request(s->dma, PXA2XX_RX_RQ_ICP, (s->status[0] >> 4) & 1);
+ pxa2xx_dma_request(s->dma, PXA2XX_TX_RQ_ICP, (s->status[0] >> 3) & 1);
+
+ qemu_set_irq(s->irq, intr && s->enable);
+}
+
+#define ICCR0 0x00 /* FICP Control register 0 */
+#define ICCR1 0x04 /* FICP Control register 1 */
+#define ICCR2 0x08 /* FICP Control register 2 */
+#define ICDR 0x0c /* FICP Data register */
+#define ICSR0 0x14 /* FICP Status register 0 */
+#define ICSR1 0x18 /* FICP Status register 1 */
+#define ICFOR 0x1c /* FICP FIFO Occupancy Status register */
+
+static uint32_t pxa2xx_fir_read(void *opaque, target_phys_addr_t addr)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ uint8_t ret;
+
+ switch (addr) {
+ case ICCR0:
+ return s->control[0];
+ case ICCR1:
+ return s->control[1];
+ case ICCR2:
+ return s->control[2];
+ case ICDR:
+ s->status[0] &= ~0x01;
+ s->status[1] &= ~0x72;
+ if (s->rx_len) {
+ s->rx_len --;
+ ret = s->rx_fifo[s->rx_start ++];
+ s->rx_start &= 63;
+ pxa2xx_fir_update(s);
+ return ret;
+ }
+ printf("%s: Rx FIFO underrun.\n", __FUNCTION__);
+ break;
+ case ICSR0:
+ return s->status[0];
+ case ICSR1:
+ return s->status[1] | (1 << 3); /* TNF */
+ case ICFOR:
+ return s->rx_len;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ break;
+ }
+ return 0;
+}
+
+static void pxa2xx_fir_write(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ uint8_t ch;
+
+ switch (addr) {
+ case ICCR0:
+ s->control[0] = value;
+ if (!(value & (1 << 4))) /* RXE */
+ s->rx_len = s->rx_start = 0;
+ if (!(value & (1 << 3))) { /* TXE */
+ /* Nop */
+ }
+ s->enable = value & 1; /* ITR */
+ if (!s->enable)
+ s->status[0] = 0;
+ pxa2xx_fir_update(s);
+ break;
+ case ICCR1:
+ s->control[1] = value;
+ break;
+ case ICCR2:
+ s->control[2] = value & 0x3f;
+ pxa2xx_fir_update(s);
+ break;
+ case ICDR:
+ if (s->control[2] & (1 << 2)) /* TXP */
+ ch = value;
+ else
+ ch = ~value;
+ if (s->chr && s->enable && (s->control[0] & (1 << 3))) /* TXE */
+ qemu_chr_write(s->chr, &ch, 1);
+ break;
+ case ICSR0:
+ s->status[0] &= ~(value & 0x66);
+ pxa2xx_fir_update(s);
+ break;
+ case ICFOR:
+ break;
+ default:
+ printf("%s: Bad register " REG_FMT "\n", __FUNCTION__, addr);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_fir_readfn[] = {
+ pxa2xx_fir_read,
+ pxa2xx_fir_read,
+ pxa2xx_fir_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_fir_writefn[] = {
+ pxa2xx_fir_write,
+ pxa2xx_fir_write,
+ pxa2xx_fir_write,
+};
+
+static int pxa2xx_fir_is_empty(void *opaque)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ return (s->rx_len < 64);
+}
+
+static void pxa2xx_fir_rx(void *opaque, const uint8_t *buf, int size)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ if (!(s->control[0] & (1 << 4))) /* RXE */
+ return;
+
+ while (size --) {
+ s->status[1] |= 1 << 4; /* EOF */
+ if (s->rx_len >= 64) {
+ s->status[1] |= 1 << 6; /* ROR */
+ break;
+ }
+
+ if (s->control[2] & (1 << 3)) /* RXP */
+ s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = *(buf ++);
+ else
+ s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = ~*(buf ++);
+ }
+
+ pxa2xx_fir_update(s);
+}
+
+static void pxa2xx_fir_event(void *opaque, int event)
+{
+}
+
+static void pxa2xx_fir_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ int i;
+
+ qemu_put_be32(f, s->enable);
+
+ qemu_put_8s(f, &s->control[0]);
+ qemu_put_8s(f, &s->control[1]);
+ qemu_put_8s(f, &s->control[2]);
+ qemu_put_8s(f, &s->status[0]);
+ qemu_put_8s(f, &s->status[1]);
+
+ qemu_put_byte(f, s->rx_len);
+ for (i = 0; i < s->rx_len; i ++)
+ qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]);
+}
+
+static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxFIrState *s = (PXA2xxFIrState *) opaque;
+ int i;
+
+ s->enable = qemu_get_be32(f);
+
+ qemu_get_8s(f, &s->control[0]);
+ qemu_get_8s(f, &s->control[1]);
+ qemu_get_8s(f, &s->control[2]);
+ qemu_get_8s(f, &s->status[0]);
+ qemu_get_8s(f, &s->status[1]);
+
+ s->rx_len = qemu_get_byte(f);
+ s->rx_start = 0;
+ for (i = 0; i < s->rx_len; i ++)
+ s->rx_fifo[i] = qemu_get_byte(f);
+
+ return 0;
+}
+
+static PXA2xxFIrState *pxa2xx_fir_init(target_phys_addr_t base,
+ qemu_irq irq, PXA2xxDMAState *dma,
+ CharDriverState *chr)
+{
+ int iomemtype;
+ PXA2xxFIrState *s = (PXA2xxFIrState *)
+ qemu_mallocz(sizeof(PXA2xxFIrState));
+
+ s->irq = irq;
+ s->dma = dma;
+ s->chr = chr;
+
+ pxa2xx_fir_reset(s);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_fir_readfn,
+ pxa2xx_fir_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x1000, iomemtype);
+
+ if (chr)
+ qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty,
+ pxa2xx_fir_rx, pxa2xx_fir_event, s);
+
+ register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save,
+ pxa2xx_fir_load, s);
+
+ return s;
+}
+
+static void pxa2xx_reset(void *opaque, int line, int level)
+{
+ PXA2xxState *s = (PXA2xxState *) opaque;
+
+ if (level && (s->pm_regs[PCFR >> 2] & 0x10)) { /* GPR_EN */
+ cpu_reset(s->env);
+ /* TODO: reset peripherals */
+ }
+}
+
+/* Initialise a PXA270 integrated chip (ARM based core). */
+PXA2xxState *pxa270_init(unsigned int sdram_size, const char *revision)
+{
+ PXA2xxState *s;
+ int iomemtype, i;
+ DriveInfo *dinfo;
+ s = (PXA2xxState *) qemu_mallocz(sizeof(PXA2xxState));
+
+ if (revision && strncmp(revision, "pxa27", 5)) {
+ fprintf(stderr, "Machine requires a PXA27x processor.\n");
+ exit(1);
+ }
+ if (!revision)
+ revision = "pxa270";
+
+ s->env = cpu_init(revision);
+ if (!s->env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0];
+
+ /* SDRAM & Internal Memory Storage */
+ cpu_register_physical_memory(PXA2XX_SDRAM_BASE,
+ sdram_size, qemu_ram_alloc(NULL, "pxa270.sdram",
+ sdram_size) | IO_MEM_RAM);
+ cpu_register_physical_memory(PXA2XX_INTERNAL_BASE,
+ 0x40000, qemu_ram_alloc(NULL, "pxa270.internal",
+ 0x40000) | IO_MEM_RAM);
+
+ s->pic = pxa2xx_pic_init(0x40d00000, s->env);
+
+ s->dma = pxa27x_dma_init(0x40000000, s->pic[PXA2XX_PIC_DMA]);
+
+ pxa27x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0],
+ s->pic[PXA27X_PIC_OST_4_11]);
+
+ s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 121);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = pxa2xx_mmci_init(0x41100000, dinfo->bdrv,
+ s->pic[PXA2XX_PIC_MMC], s->dma);
+
+ for (i = 0; pxa270_serial[i].io_base; i ++)
+ if (serial_hds[i])
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(pxa270_serial[i].io_base, 2,
+ s->pic[pxa270_serial[i].irqn], 14857000/16,
+ serial_hds[i], 1, 1);
+#else
+ serial_mm_init(pxa270_serial[i].io_base, 2,
+ s->pic[pxa270_serial[i].irqn], 14857000/16,
+ serial_hds[i], 1, 0);
+#endif
+ else
+ break;
+ if (serial_hds[i])
+ s->fir = pxa2xx_fir_init(0x40800000, s->pic[PXA2XX_PIC_ICP],
+ s->dma, serial_hds[i]);
+
+ s->lcd = pxa2xx_lcdc_init(0x44000000, s->pic[PXA2XX_PIC_LCD]);
+
+ s->cm_base = 0x41300000;
+ s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */
+ s->clkcfg = 0x00000009; /* Turbo mode active */
+ iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn,
+ pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype);
+ register_savevm(NULL, "pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s);
+
+ cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s);
+
+ s->mm_base = 0x48000000;
+ s->mm_regs[MDMRS >> 2] = 0x00020002;
+ s->mm_regs[MDREFR >> 2] = 0x03ca4000;
+ s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */
+ iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn,
+ pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype);
+ register_savevm(NULL, "pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s);
+
+ s->pm_base = 0x40f00000;
+ iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn,
+ pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->pm_base, 0x100, iomemtype);
+ register_savevm(NULL, "pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s);
+
+ for (i = 0; pxa27x_ssp[i].io_base; i ++);
+ s->ssp = (SSIBus **)qemu_mallocz(sizeof(SSIBus *) * i);
+ for (i = 0; pxa27x_ssp[i].io_base; i ++) {
+ DeviceState *dev;
+ dev = sysbus_create_simple("pxa2xx-ssp", pxa27x_ssp[i].io_base,
+ s->pic[pxa27x_ssp[i].irqn]);
+ s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi");
+ }
+
+ if (usb_enabled) {
+ sysbus_create_simple("sysbus-ohci", 0x4c000000,
+ s->pic[PXA2XX_PIC_USBH1]);
+ }
+
+ s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000);
+ s->pcmcia[1] = pxa2xx_pcmcia_init(0x30000000);
+
+ s->rtc_base = 0x40900000;
+ iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn,
+ pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->rtc_base, 0x1000, iomemtype);
+ pxa2xx_rtc_init(s);
+ register_savevm(NULL, "pxa2xx_rtc", 0, 0, pxa2xx_rtc_save,
+ pxa2xx_rtc_load, s);
+
+ s->i2c[0] = pxa2xx_i2c_init(0x40301600, s->pic[PXA2XX_PIC_I2C], 0xffff);
+ s->i2c[1] = pxa2xx_i2c_init(0x40f00100, s->pic[PXA2XX_PIC_PWRI2C], 0xff);
+
+ s->i2s = pxa2xx_i2s_init(0x40400000, s->pic[PXA2XX_PIC_I2S], s->dma);
+
+ s->kp = pxa27x_keypad_init(0x41500000, s->pic[PXA2XX_PIC_KEYPAD]);
+
+ /* GPIO1 resets the processor */
+ /* The handler can be overridden by board-specific code */
+ qdev_connect_gpio_out(s->gpio, 1, s->reset);
+ return s;
+}
+
+/* Initialise a PXA255 integrated chip (ARM based core). */
+PXA2xxState *pxa255_init(unsigned int sdram_size)
+{
+ PXA2xxState *s;
+ int iomemtype, i;
+ DriveInfo *dinfo;
+
+ s = (PXA2xxState *) qemu_mallocz(sizeof(PXA2xxState));
+
+ s->env = cpu_init("pxa255");
+ if (!s->env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0];
+
+ /* SDRAM & Internal Memory Storage */
+ cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size,
+ qemu_ram_alloc(NULL, "pxa255.sdram",
+ sdram_size) | IO_MEM_RAM);
+ cpu_register_physical_memory(PXA2XX_INTERNAL_BASE, PXA2XX_INTERNAL_SIZE,
+ qemu_ram_alloc(NULL, "pxa255.internal",
+ PXA2XX_INTERNAL_SIZE) | IO_MEM_RAM);
+
+ s->pic = pxa2xx_pic_init(0x40d00000, s->env);
+
+ s->dma = pxa255_dma_init(0x40000000, s->pic[PXA2XX_PIC_DMA]);
+
+ pxa25x_timer_init(0x40a00000, &s->pic[PXA2XX_PIC_OST_0]);
+
+ s->gpio = pxa2xx_gpio_init(0x40e00000, s->env, s->pic, 85);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ if (!dinfo) {
+ fprintf(stderr, "qemu: missing SecureDigital device\n");
+ exit(1);
+ }
+ s->mmc = pxa2xx_mmci_init(0x41100000, dinfo->bdrv,
+ s->pic[PXA2XX_PIC_MMC], s->dma);
+
+ for (i = 0; pxa255_serial[i].io_base; i ++)
+ if (serial_hds[i]) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(pxa255_serial[i].io_base, 2,
+ s->pic[pxa255_serial[i].irqn], 14745600/16,
+ serial_hds[i], 1, 1);
+#else
+ serial_mm_init(pxa255_serial[i].io_base, 2,
+ s->pic[pxa255_serial[i].irqn], 14745600/16,
+ serial_hds[i], 1, 0);
+#endif
+ } else {
+ break;
+ }
+ if (serial_hds[i])
+ s->fir = pxa2xx_fir_init(0x40800000, s->pic[PXA2XX_PIC_ICP],
+ s->dma, serial_hds[i]);
+
+ s->lcd = pxa2xx_lcdc_init(0x44000000, s->pic[PXA2XX_PIC_LCD]);
+
+ s->cm_base = 0x41300000;
+ s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */
+ s->clkcfg = 0x00000009; /* Turbo mode active */
+ iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn,
+ pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype);
+ register_savevm(NULL, "pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s);
+
+ cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s);
+
+ s->mm_base = 0x48000000;
+ s->mm_regs[MDMRS >> 2] = 0x00020002;
+ s->mm_regs[MDREFR >> 2] = 0x03ca4000;
+ s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */
+ iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn,
+ pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype);
+ register_savevm(NULL, "pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s);
+
+ s->pm_base = 0x40f00000;
+ iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn,
+ pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->pm_base, 0x100, iomemtype);
+ register_savevm(NULL, "pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s);
+
+ for (i = 0; pxa255_ssp[i].io_base; i ++);
+ s->ssp = (SSIBus **)qemu_mallocz(sizeof(SSIBus *) * i);
+ for (i = 0; pxa255_ssp[i].io_base; i ++) {
+ DeviceState *dev;
+ dev = sysbus_create_simple("pxa2xx-ssp", pxa255_ssp[i].io_base,
+ s->pic[pxa255_ssp[i].irqn]);
+ s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi");
+ }
+
+ if (usb_enabled) {
+ sysbus_create_simple("sysbus-ohci", 0x4c000000,
+ s->pic[PXA2XX_PIC_USBH1]);
+ }
+
+ s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000);
+ s->pcmcia[1] = pxa2xx_pcmcia_init(0x30000000);
+
+ s->rtc_base = 0x40900000;
+ iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn,
+ pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(s->rtc_base, 0x1000, iomemtype);
+ pxa2xx_rtc_init(s);
+ register_savevm(NULL, "pxa2xx_rtc", 0, 0, pxa2xx_rtc_save,
+ pxa2xx_rtc_load, s);
+
+ s->i2c[0] = pxa2xx_i2c_init(0x40301600, s->pic[PXA2XX_PIC_I2C], 0xffff);
+ s->i2c[1] = pxa2xx_i2c_init(0x40f00100, s->pic[PXA2XX_PIC_PWRI2C], 0xff);
+
+ s->i2s = pxa2xx_i2s_init(0x40400000, s->pic[PXA2XX_PIC_I2S], s->dma);
+
+ /* GPIO1 resets the processor */
+ /* The handler can be overridden by board-specific code */
+ qdev_connect_gpio_out(s->gpio, 1, s->reset);
+ return s;
+}
+
+static void pxa2xx_register_devices(void)
+{
+ i2c_register_slave(&pxa2xx_i2c_slave_info);
+ sysbus_register_dev("pxa2xx-ssp", sizeof(PXA2xxSSPState), pxa2xx_ssp_init);
+}
+
+device_init(pxa2xx_register_devices)
diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c
new file mode 100644
index 0000000..b512d34
--- /dev/null
+++ b/hw/pxa2xx_dma.c
@@ -0,0 +1,549 @@
+/*
+ * Intel XScale PXA255/270 DMA controller.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+
+typedef struct {
+ target_phys_addr_t descr;
+ target_phys_addr_t src;
+ target_phys_addr_t dest;
+ uint32_t cmd;
+ uint32_t state;
+ int request;
+} PXA2xxDMAChannel;
+
+/* Allow the DMA to be used as a PIC. */
+typedef void (*pxa2xx_dma_handler_t)(void *opaque, int irq, int level);
+
+struct PXA2xxDMAState {
+ pxa2xx_dma_handler_t handler;
+ qemu_irq irq;
+
+ uint32_t stopintr;
+ uint32_t eorintr;
+ uint32_t rasintr;
+ uint32_t startintr;
+ uint32_t endintr;
+
+ uint32_t align;
+ uint32_t pio;
+
+ int channels;
+ PXA2xxDMAChannel *chan;
+
+ uint8_t *req;
+
+ /* Flag to avoid recursive DMA invocations. */
+ int running;
+};
+
+#define PXA255_DMA_NUM_CHANNELS 16
+#define PXA27X_DMA_NUM_CHANNELS 32
+
+#define PXA2XX_DMA_NUM_REQUESTS 75
+
+#define DCSR0 0x0000 /* DMA Control / Status register for Channel 0 */
+#define DCSR31 0x007c /* DMA Control / Status register for Channel 31 */
+#define DALGN 0x00a0 /* DMA Alignment register */
+#define DPCSR 0x00a4 /* DMA Programmed I/O Control Status register */
+#define DRQSR0 0x00e0 /* DMA DREQ<0> Status register */
+#define DRQSR1 0x00e4 /* DMA DREQ<1> Status register */
+#define DRQSR2 0x00e8 /* DMA DREQ<2> Status register */
+#define DINT 0x00f0 /* DMA Interrupt register */
+#define DRCMR0 0x0100 /* Request to Channel Map register 0 */
+#define DRCMR63 0x01fc /* Request to Channel Map register 63 */
+#define D_CH0 0x0200 /* Channel 0 Descriptor start */
+#define DRCMR64 0x1100 /* Request to Channel Map register 64 */
+#define DRCMR74 0x1128 /* Request to Channel Map register 74 */
+
+/* Per-channel register */
+#define DDADR 0x00
+#define DSADR 0x01
+#define DTADR 0x02
+#define DCMD 0x03
+
+/* Bit-field masks */
+#define DRCMR_CHLNUM 0x1f
+#define DRCMR_MAPVLD (1 << 7)
+#define DDADR_STOP (1 << 0)
+#define DDADR_BREN (1 << 1)
+#define DCMD_LEN 0x1fff
+#define DCMD_WIDTH(x) (1 << ((((x) >> 14) & 3) - 1))
+#define DCMD_SIZE(x) (4 << (((x) >> 16) & 3))
+#define DCMD_FLYBYT (1 << 19)
+#define DCMD_FLYBYS (1 << 20)
+#define DCMD_ENDIRQEN (1 << 21)
+#define DCMD_STARTIRQEN (1 << 22)
+#define DCMD_CMPEN (1 << 25)
+#define DCMD_FLOWTRG (1 << 28)
+#define DCMD_FLOWSRC (1 << 29)
+#define DCMD_INCTRGADDR (1 << 30)
+#define DCMD_INCSRCADDR (1 << 31)
+#define DCSR_BUSERRINTR (1 << 0)
+#define DCSR_STARTINTR (1 << 1)
+#define DCSR_ENDINTR (1 << 2)
+#define DCSR_STOPINTR (1 << 3)
+#define DCSR_RASINTR (1 << 4)
+#define DCSR_REQPEND (1 << 8)
+#define DCSR_EORINT (1 << 9)
+#define DCSR_CMPST (1 << 10)
+#define DCSR_MASKRUN (1 << 22)
+#define DCSR_RASIRQEN (1 << 23)
+#define DCSR_CLRCMPST (1 << 24)
+#define DCSR_SETCMPST (1 << 25)
+#define DCSR_EORSTOPEN (1 << 26)
+#define DCSR_EORJMPEN (1 << 27)
+#define DCSR_EORIRQEN (1 << 28)
+#define DCSR_STOPIRQEN (1 << 29)
+#define DCSR_NODESCFETCH (1 << 30)
+#define DCSR_RUN (1 << 31)
+
+static inline void pxa2xx_dma_update(PXA2xxDMAState *s, int ch)
+{
+ if (ch >= 0) {
+ if ((s->chan[ch].state & DCSR_STOPIRQEN) &&
+ (s->chan[ch].state & DCSR_STOPINTR))
+ s->stopintr |= 1 << ch;
+ else
+ s->stopintr &= ~(1 << ch);
+
+ if ((s->chan[ch].state & DCSR_EORIRQEN) &&
+ (s->chan[ch].state & DCSR_EORINT))
+ s->eorintr |= 1 << ch;
+ else
+ s->eorintr &= ~(1 << ch);
+
+ if ((s->chan[ch].state & DCSR_RASIRQEN) &&
+ (s->chan[ch].state & DCSR_RASINTR))
+ s->rasintr |= 1 << ch;
+ else
+ s->rasintr &= ~(1 << ch);
+
+ if (s->chan[ch].state & DCSR_STARTINTR)
+ s->startintr |= 1 << ch;
+ else
+ s->startintr &= ~(1 << ch);
+
+ if (s->chan[ch].state & DCSR_ENDINTR)
+ s->endintr |= 1 << ch;
+ else
+ s->endintr &= ~(1 << ch);
+ }
+
+ if (s->stopintr | s->eorintr | s->rasintr | s->startintr | s->endintr)
+ qemu_irq_raise(s->irq);
+ else
+ qemu_irq_lower(s->irq);
+}
+
+static inline void pxa2xx_dma_descriptor_fetch(
+ PXA2xxDMAState *s, int ch)
+{
+ uint32_t desc[4];
+ target_phys_addr_t daddr = s->chan[ch].descr & ~0xf;
+ if ((s->chan[ch].descr & DDADR_BREN) && (s->chan[ch].state & DCSR_CMPST))
+ daddr += 32;
+
+ cpu_physical_memory_read(daddr, (uint8_t *) desc, 16);
+ s->chan[ch].descr = desc[DDADR];
+ s->chan[ch].src = desc[DSADR];
+ s->chan[ch].dest = desc[DTADR];
+ s->chan[ch].cmd = desc[DCMD];
+
+ if (s->chan[ch].cmd & DCMD_FLOWSRC)
+ s->chan[ch].src &= ~3;
+ if (s->chan[ch].cmd & DCMD_FLOWTRG)
+ s->chan[ch].dest &= ~3;
+
+ if (s->chan[ch].cmd & (DCMD_CMPEN | DCMD_FLYBYS | DCMD_FLYBYT))
+ printf("%s: unsupported mode in channel %i\n", __FUNCTION__, ch);
+
+ if (s->chan[ch].cmd & DCMD_STARTIRQEN)
+ s->chan[ch].state |= DCSR_STARTINTR;
+}
+
+static void pxa2xx_dma_run(PXA2xxDMAState *s)
+{
+ int c, srcinc, destinc;
+ uint32_t n, size;
+ uint32_t width;
+ uint32_t length;
+ uint8_t buffer[32];
+ PXA2xxDMAChannel *ch;
+
+ if (s->running ++)
+ return;
+
+ while (s->running) {
+ s->running = 1;
+ for (c = 0; c < s->channels; c ++) {
+ ch = &s->chan[c];
+
+ while ((ch->state & DCSR_RUN) && !(ch->state & DCSR_STOPINTR)) {
+ /* Test for pending requests */
+ if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && !ch->request)
+ break;
+
+ length = ch->cmd & DCMD_LEN;
+ size = DCMD_SIZE(ch->cmd);
+ width = DCMD_WIDTH(ch->cmd);
+
+ srcinc = (ch->cmd & DCMD_INCSRCADDR) ? width : 0;
+ destinc = (ch->cmd & DCMD_INCTRGADDR) ? width : 0;
+
+ while (length) {
+ size = MIN(length, size);
+
+ for (n = 0; n < size; n += width) {
+ cpu_physical_memory_read(ch->src, buffer + n, width);
+ ch->src += srcinc;
+ }
+
+ for (n = 0; n < size; n += width) {
+ cpu_physical_memory_write(ch->dest, buffer + n, width);
+ ch->dest += destinc;
+ }
+
+ length -= size;
+
+ if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) &&
+ !ch->request) {
+ ch->state |= DCSR_EORINT;
+ if (ch->state & DCSR_EORSTOPEN)
+ ch->state |= DCSR_STOPINTR;
+ if ((ch->state & DCSR_EORJMPEN) &&
+ !(ch->state & DCSR_NODESCFETCH))
+ pxa2xx_dma_descriptor_fetch(s, c);
+ break;
+ }
+ }
+
+ ch->cmd = (ch->cmd & ~DCMD_LEN) | length;
+
+ /* Is the transfer complete now? */
+ if (!length) {
+ if (ch->cmd & DCMD_ENDIRQEN)
+ ch->state |= DCSR_ENDINTR;
+
+ if ((ch->state & DCSR_NODESCFETCH) ||
+ (ch->descr & DDADR_STOP) ||
+ (ch->state & DCSR_EORSTOPEN)) {
+ ch->state |= DCSR_STOPINTR;
+ ch->state &= ~DCSR_RUN;
+
+ break;
+ }
+
+ ch->state |= DCSR_STOPINTR;
+ break;
+ }
+ }
+ }
+
+ s->running --;
+ }
+}
+
+static uint32_t pxa2xx_dma_read(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxDMAState *s = (PXA2xxDMAState *) opaque;
+ unsigned int channel;
+
+ switch (offset) {
+ case DRCMR64 ... DRCMR74:
+ offset -= DRCMR64 - DRCMR0 - (64 << 2);
+ /* Fall through */
+ case DRCMR0 ... DRCMR63:
+ channel = (offset - DRCMR0) >> 2;
+ return s->req[channel];
+
+ case DRQSR0:
+ case DRQSR1:
+ case DRQSR2:
+ return 0;
+
+ case DCSR0 ... DCSR31:
+ channel = offset >> 2;
+ if (s->chan[channel].request)
+ return s->chan[channel].state | DCSR_REQPEND;
+ return s->chan[channel].state;
+
+ case DINT:
+ return s->stopintr | s->eorintr | s->rasintr |
+ s->startintr | s->endintr;
+
+ case DALGN:
+ return s->align;
+
+ case DPCSR:
+ return s->pio;
+ }
+
+ if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) {
+ channel = (offset - D_CH0) >> 4;
+ switch ((offset & 0x0f) >> 2) {
+ case DDADR:
+ return s->chan[channel].descr;
+ case DSADR:
+ return s->chan[channel].src;
+ case DTADR:
+ return s->chan[channel].dest;
+ case DCMD:
+ return s->chan[channel].cmd;
+ }
+ }
+
+ hw_error("%s: Bad offset 0x" TARGET_FMT_plx "\n", __FUNCTION__, offset);
+ return 7;
+}
+
+static void pxa2xx_dma_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxDMAState *s = (PXA2xxDMAState *) opaque;
+ unsigned int channel;
+
+ switch (offset) {
+ case DRCMR64 ... DRCMR74:
+ offset -= DRCMR64 - DRCMR0 - (64 << 2);
+ /* Fall through */
+ case DRCMR0 ... DRCMR63:
+ channel = (offset - DRCMR0) >> 2;
+
+ if (value & DRCMR_MAPVLD)
+ if ((value & DRCMR_CHLNUM) > s->channels)
+ hw_error("%s: Bad DMA channel %i\n",
+ __FUNCTION__, value & DRCMR_CHLNUM);
+
+ s->req[channel] = value;
+ break;
+
+ case DRQSR0:
+ case DRQSR1:
+ case DRQSR2:
+ /* Nothing to do */
+ break;
+
+ case DCSR0 ... DCSR31:
+ channel = offset >> 2;
+ s->chan[channel].state &= 0x0000071f & ~(value &
+ (DCSR_EORINT | DCSR_ENDINTR |
+ DCSR_STARTINTR | DCSR_BUSERRINTR));
+ s->chan[channel].state |= value & 0xfc800000;
+
+ if (s->chan[channel].state & DCSR_STOPIRQEN)
+ s->chan[channel].state &= ~DCSR_STOPINTR;
+
+ if (value & DCSR_NODESCFETCH) {
+ /* No-descriptor-fetch mode */
+ if (value & DCSR_RUN) {
+ s->chan[channel].state &= ~DCSR_STOPINTR;
+ pxa2xx_dma_run(s);
+ }
+ } else {
+ /* Descriptor-fetch mode */
+ if (value & DCSR_RUN) {
+ s->chan[channel].state &= ~DCSR_STOPINTR;
+ pxa2xx_dma_descriptor_fetch(s, channel);
+ pxa2xx_dma_run(s);
+ }
+ }
+
+ /* Shouldn't matter as our DMA is synchronous. */
+ if (!(value & (DCSR_RUN | DCSR_MASKRUN)))
+ s->chan[channel].state |= DCSR_STOPINTR;
+
+ if (value & DCSR_CLRCMPST)
+ s->chan[channel].state &= ~DCSR_CMPST;
+ if (value & DCSR_SETCMPST)
+ s->chan[channel].state |= DCSR_CMPST;
+
+ pxa2xx_dma_update(s, channel);
+ break;
+
+ case DALGN:
+ s->align = value;
+ break;
+
+ case DPCSR:
+ s->pio = value & 0x80000001;
+ break;
+
+ default:
+ if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) {
+ channel = (offset - D_CH0) >> 4;
+ switch ((offset & 0x0f) >> 2) {
+ case DDADR:
+ s->chan[channel].descr = value;
+ break;
+ case DSADR:
+ s->chan[channel].src = value;
+ break;
+ case DTADR:
+ s->chan[channel].dest = value;
+ break;
+ case DCMD:
+ s->chan[channel].cmd = value;
+ break;
+ default:
+ goto fail;
+ }
+
+ break;
+ }
+ fail:
+ hw_error("%s: Bad offset " TARGET_FMT_plx "\n", __FUNCTION__, offset);
+ }
+}
+
+static uint32_t pxa2xx_dma_readbad(void *opaque, target_phys_addr_t offset)
+{
+ hw_error("%s: Bad access width\n", __FUNCTION__);
+ return 5;
+}
+
+static void pxa2xx_dma_writebad(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ hw_error("%s: Bad access width\n", __FUNCTION__);
+}
+
+static CPUReadMemoryFunc * const pxa2xx_dma_readfn[] = {
+ pxa2xx_dma_readbad,
+ pxa2xx_dma_readbad,
+ pxa2xx_dma_read
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_dma_writefn[] = {
+ pxa2xx_dma_writebad,
+ pxa2xx_dma_writebad,
+ pxa2xx_dma_write
+};
+
+static void pxa2xx_dma_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxDMAState *s = (PXA2xxDMAState *) opaque;
+ int i;
+
+ qemu_put_be32(f, s->channels);
+
+ qemu_put_be32s(f, &s->stopintr);
+ qemu_put_be32s(f, &s->eorintr);
+ qemu_put_be32s(f, &s->rasintr);
+ qemu_put_be32s(f, &s->startintr);
+ qemu_put_be32s(f, &s->endintr);
+ qemu_put_be32s(f, &s->align);
+ qemu_put_be32s(f, &s->pio);
+
+ qemu_put_buffer(f, s->req, PXA2XX_DMA_NUM_REQUESTS);
+ for (i = 0; i < s->channels; i ++) {
+ qemu_put_betl(f, s->chan[i].descr);
+ qemu_put_betl(f, s->chan[i].src);
+ qemu_put_betl(f, s->chan[i].dest);
+ qemu_put_be32s(f, &s->chan[i].cmd);
+ qemu_put_be32s(f, &s->chan[i].state);
+ qemu_put_be32(f, s->chan[i].request);
+ };
+}
+
+static int pxa2xx_dma_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxDMAState *s = (PXA2xxDMAState *) opaque;
+ int i;
+
+ if (qemu_get_be32(f) != s->channels)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->stopintr);
+ qemu_get_be32s(f, &s->eorintr);
+ qemu_get_be32s(f, &s->rasintr);
+ qemu_get_be32s(f, &s->startintr);
+ qemu_get_be32s(f, &s->endintr);
+ qemu_get_be32s(f, &s->align);
+ qemu_get_be32s(f, &s->pio);
+
+ qemu_get_buffer(f, s->req, PXA2XX_DMA_NUM_REQUESTS);
+ for (i = 0; i < s->channels; i ++) {
+ s->chan[i].descr = qemu_get_betl(f);
+ s->chan[i].src = qemu_get_betl(f);
+ s->chan[i].dest = qemu_get_betl(f);
+ qemu_get_be32s(f, &s->chan[i].cmd);
+ qemu_get_be32s(f, &s->chan[i].state);
+ s->chan[i].request = qemu_get_be32(f);
+ };
+
+ return 0;
+}
+
+static PXA2xxDMAState *pxa2xx_dma_init(target_phys_addr_t base,
+ qemu_irq irq, int channels)
+{
+ int i, iomemtype;
+ PXA2xxDMAState *s;
+ s = (PXA2xxDMAState *)
+ qemu_mallocz(sizeof(PXA2xxDMAState));
+
+ s->channels = channels;
+ s->chan = qemu_mallocz(sizeof(PXA2xxDMAChannel) * s->channels);
+ s->irq = irq;
+ s->handler = (pxa2xx_dma_handler_t) pxa2xx_dma_request;
+ s->req = qemu_mallocz(sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS);
+
+ memset(s->chan, 0, sizeof(PXA2xxDMAChannel) * s->channels);
+ for (i = 0; i < s->channels; i ++)
+ s->chan[i].state = DCSR_STOPINTR;
+
+ memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_dma_readfn,
+ pxa2xx_dma_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00010000, iomemtype);
+
+ register_savevm(NULL, "pxa2xx_dma", 0, 0, pxa2xx_dma_save, pxa2xx_dma_load, s);
+
+ return s;
+}
+
+PXA2xxDMAState *pxa27x_dma_init(target_phys_addr_t base,
+ qemu_irq irq)
+{
+ return pxa2xx_dma_init(base, irq, PXA27X_DMA_NUM_CHANNELS);
+}
+
+PXA2xxDMAState *pxa255_dma_init(target_phys_addr_t base,
+ qemu_irq irq)
+{
+ return pxa2xx_dma_init(base, irq, PXA255_DMA_NUM_CHANNELS);
+}
+
+void pxa2xx_dma_request(PXA2xxDMAState *s, int req_num, int on)
+{
+ int ch;
+ if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS)
+ hw_error("%s: Bad DMA request %i\n", __FUNCTION__, req_num);
+
+ if (!(s->req[req_num] & DRCMR_MAPVLD))
+ return;
+ ch = s->req[req_num] & DRCMR_CHLNUM;
+
+ if (!s->chan[ch].request && on)
+ s->chan[ch].state |= DCSR_RASINTR;
+ else
+ s->chan[ch].state &= ~DCSR_RASINTR;
+ if (s->chan[ch].request && !on)
+ s->chan[ch].state |= DCSR_EORINT;
+
+ s->chan[ch].request = on;
+ if (on) {
+ pxa2xx_dma_run(s);
+ pxa2xx_dma_update(s, ch);
+ }
+}
diff --git a/hw/pxa2xx_gpio.c b/hw/pxa2xx_gpio.c
new file mode 100644
index 0000000..789965d
--- /dev/null
+++ b/hw/pxa2xx_gpio.c
@@ -0,0 +1,340 @@
+/*
+ * Intel XScale PXA255/270 GPIO controller emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw.h"
+#include "sysbus.h"
+#include "pxa.h"
+
+#define PXA2XX_GPIO_BANKS 4
+
+typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo;
+struct PXA2xxGPIOInfo {
+ SysBusDevice busdev;
+ qemu_irq irq0, irq1, irqX;
+ int lines;
+ int ncpu;
+ CPUState *cpu_env;
+
+ /* XXX: GNU C vectors are more suitable */
+ uint32_t ilevel[PXA2XX_GPIO_BANKS];
+ uint32_t olevel[PXA2XX_GPIO_BANKS];
+ uint32_t dir[PXA2XX_GPIO_BANKS];
+ uint32_t rising[PXA2XX_GPIO_BANKS];
+ uint32_t falling[PXA2XX_GPIO_BANKS];
+ uint32_t status[PXA2XX_GPIO_BANKS];
+ uint32_t gpsr[PXA2XX_GPIO_BANKS];
+ uint32_t gafr[PXA2XX_GPIO_BANKS * 2];
+
+ uint32_t prev_level[PXA2XX_GPIO_BANKS];
+ qemu_irq handler[PXA2XX_GPIO_BANKS * 32];
+ qemu_irq read_notify;
+};
+
+static struct {
+ enum {
+ GPIO_NONE,
+ GPLR,
+ GPSR,
+ GPCR,
+ GPDR,
+ GRER,
+ GFER,
+ GEDR,
+ GAFR_L,
+ GAFR_U,
+ } reg;
+ int bank;
+} pxa2xx_gpio_regs[0x200] = {
+ [0 ... 0x1ff] = { GPIO_NONE, 0 },
+#define PXA2XX_REG(reg, a0, a1, a2, a3) \
+ [a0] = { reg, 0 }, [a1] = { reg, 1 }, [a2] = { reg, 2 }, [a3] = { reg, 3 },
+
+ PXA2XX_REG(GPLR, 0x000, 0x004, 0x008, 0x100)
+ PXA2XX_REG(GPSR, 0x018, 0x01c, 0x020, 0x118)
+ PXA2XX_REG(GPCR, 0x024, 0x028, 0x02c, 0x124)
+ PXA2XX_REG(GPDR, 0x00c, 0x010, 0x014, 0x10c)
+ PXA2XX_REG(GRER, 0x030, 0x034, 0x038, 0x130)
+ PXA2XX_REG(GFER, 0x03c, 0x040, 0x044, 0x13c)
+ PXA2XX_REG(GEDR, 0x048, 0x04c, 0x050, 0x148)
+ PXA2XX_REG(GAFR_L, 0x054, 0x05c, 0x064, 0x06c)
+ PXA2XX_REG(GAFR_U, 0x058, 0x060, 0x068, 0x070)
+};
+
+static void pxa2xx_gpio_irq_update(PXA2xxGPIOInfo *s)
+{
+ if (s->status[0] & (1 << 0))
+ qemu_irq_raise(s->irq0);
+ else
+ qemu_irq_lower(s->irq0);
+
+ if (s->status[0] & (1 << 1))
+ qemu_irq_raise(s->irq1);
+ else
+ qemu_irq_lower(s->irq1);
+
+ if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3])
+ qemu_irq_raise(s->irqX);
+ else
+ qemu_irq_lower(s->irqX);
+}
+
+/* Bitmap of pins used as standby and sleep wake-up sources. */
+static const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = {
+ 0x8003fe1b, 0x002001fc, 0xec080000, 0x0012007f,
+};
+
+static void pxa2xx_gpio_set(void *opaque, int line, int level)
+{
+ PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque;
+ int bank;
+ uint32_t mask;
+
+ if (line >= s->lines) {
+ printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
+ return;
+ }
+
+ bank = line >> 5;
+ mask = 1 << (line & 31);
+
+ if (level) {
+ s->status[bank] |= s->rising[bank] & mask &
+ ~s->ilevel[bank] & ~s->dir[bank];
+ s->ilevel[bank] |= mask;
+ } else {
+ s->status[bank] |= s->falling[bank] & mask &
+ s->ilevel[bank] & ~s->dir[bank];
+ s->ilevel[bank] &= ~mask;
+ }
+
+ if (s->status[bank] & mask)
+ pxa2xx_gpio_irq_update(s);
+
+ /* Wake-up GPIOs */
+ if (s->cpu_env->halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank]))
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_EXITTB);
+}
+
+static void pxa2xx_gpio_handler_update(PXA2xxGPIOInfo *s) {
+ uint32_t level, diff;
+ int i, bit, line;
+ for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) {
+ level = s->olevel[i] & s->dir[i];
+
+ for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ line = bit + 32 * i;
+ qemu_set_irq(s->handler[line], (level >> bit) & 1);
+ }
+
+ s->prev_level[i] = level;
+ }
+}
+
+static uint32_t pxa2xx_gpio_read(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque;
+ uint32_t ret;
+ int bank;
+ if (offset >= 0x200)
+ return 0;
+
+ bank = pxa2xx_gpio_regs[offset].bank;
+ switch (pxa2xx_gpio_regs[offset].reg) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ return s->dir[bank];
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ printf("%s: Read from a write-only register " REG_FMT "\n",
+ __FUNCTION__, offset);
+ return s->gpsr[bank]; /* Return last written value. */
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ printf("%s: Read from a write-only register " REG_FMT "\n",
+ __FUNCTION__, offset);
+ return 31337; /* Specified as unpredictable in the docs. */
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ return s->rising[bank];
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ return s->falling[bank];
+
+ case GAFR_L: /* GPIO Alternate Function registers */
+ return s->gafr[bank * 2];
+
+ case GAFR_U: /* GPIO Alternate Function registers */
+ return s->gafr[bank * 2 + 1];
+
+ case GPLR: /* GPIO Pin-Level registers */
+ ret = (s->olevel[bank] & s->dir[bank]) |
+ (s->ilevel[bank] & ~s->dir[bank]);
+ qemu_irq_raise(s->read_notify);
+ return ret;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ return s->status[bank];
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_gpio_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque;
+ int bank;
+ if (offset >= 0x200)
+ return;
+
+ bank = pxa2xx_gpio_regs[offset].bank;
+ switch (pxa2xx_gpio_regs[offset].reg) {
+ case GPDR: /* GPIO Pin-Direction registers */
+ s->dir[bank] = value;
+ pxa2xx_gpio_handler_update(s);
+ break;
+
+ case GPSR: /* GPIO Pin-Output Set registers */
+ s->olevel[bank] |= value;
+ pxa2xx_gpio_handler_update(s);
+ s->gpsr[bank] = value;
+ break;
+
+ case GPCR: /* GPIO Pin-Output Clear registers */
+ s->olevel[bank] &= ~value;
+ pxa2xx_gpio_handler_update(s);
+ break;
+
+ case GRER: /* GPIO Rising-Edge Detect Enable registers */
+ s->rising[bank] = value;
+ break;
+
+ case GFER: /* GPIO Falling-Edge Detect Enable registers */
+ s->falling[bank] = value;
+ break;
+
+ case GAFR_L: /* GPIO Alternate Function registers */
+ s->gafr[bank * 2] = value;
+ break;
+
+ case GAFR_U: /* GPIO Alternate Function registers */
+ s->gafr[bank * 2 + 1] = value;
+ break;
+
+ case GEDR: /* GPIO Edge Detect Status registers */
+ s->status[bank] &= ~value;
+ pxa2xx_gpio_irq_update(s);
+ break;
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_gpio_readfn[] = {
+ pxa2xx_gpio_read,
+ pxa2xx_gpio_read,
+ pxa2xx_gpio_read
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_gpio_writefn[] = {
+ pxa2xx_gpio_write,
+ pxa2xx_gpio_write,
+ pxa2xx_gpio_write
+};
+
+DeviceState *pxa2xx_gpio_init(target_phys_addr_t base,
+ CPUState *env, qemu_irq *pic, int lines)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "pxa2xx-gpio");
+ qdev_prop_set_int32(dev, "lines", lines);
+ qdev_prop_set_int32(dev, "ncpu", env->cpu_index);
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[PXA2XX_PIC_GPIO_0]);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 1, pic[PXA2XX_PIC_GPIO_1]);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 2, pic[PXA2XX_PIC_GPIO_X]);
+
+ return dev;
+}
+
+static int pxa2xx_gpio_initfn(SysBusDevice *dev)
+{
+ int iomemtype;
+ PXA2xxGPIOInfo *s;
+
+ s = FROM_SYSBUS(PXA2xxGPIOInfo, dev);
+
+ s->cpu_env = qemu_get_cpu(s->ncpu);
+
+ qdev_init_gpio_in(&dev->qdev, pxa2xx_gpio_set, s->lines);
+ qdev_init_gpio_out(&dev->qdev, s->handler, s->lines);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_gpio_readfn,
+ pxa2xx_gpio_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ sysbus_init_irq(dev, &s->irq0);
+ sysbus_init_irq(dev, &s->irq1);
+ sysbus_init_irq(dev, &s->irqX);
+
+ return 0;
+}
+
+/*
+ * Registers a callback to notify on GPLR reads. This normally
+ * shouldn't be needed but it is used for the hack on Spitz machines.
+ */
+void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler)
+{
+ PXA2xxGPIOInfo *s = FROM_SYSBUS(PXA2xxGPIOInfo, sysbus_from_qdev(dev));
+ s->read_notify = handler;
+}
+
+static const VMStateDescription vmstate_pxa2xx_gpio_regs = {
+ .name = "pxa2xx-gpio",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(lines, PXA2xxGPIOInfo),
+ VMSTATE_UINT32_ARRAY(ilevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(olevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(dir, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(rising, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(falling, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(status, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS),
+ VMSTATE_UINT32_ARRAY(gafr, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS * 2),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo pxa2xx_gpio_info = {
+ .init = pxa2xx_gpio_initfn,
+ .qdev.name = "pxa2xx-gpio",
+ .qdev.desc = "PXA2xx GPIO controller",
+ .qdev.size = sizeof(PXA2xxGPIOInfo),
+ .qdev.props = (Property []) {
+ DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0),
+ DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void pxa2xx_gpio_register(void)
+{
+ sysbus_register_withprop(&pxa2xx_gpio_info);
+}
+device_init(pxa2xx_gpio_register);
diff --git a/hw/pxa2xx_keypad.c b/hw/pxa2xx_keypad.c
new file mode 100644
index 0000000..4c99917
--- /dev/null
+++ b/hw/pxa2xx_keypad.c
@@ -0,0 +1,336 @@
+/*
+ * Intel PXA27X Keypad Controller emulation.
+ *
+ * Copyright (c) 2007 MontaVista Software, Inc
+ * Written by Armin Kuster <akuster@kama-aina.net>
+ * or <Akuster@mvista.com>
+ *
+ * This code is licensed under the GPLv2.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+#include "console.h"
+
+/*
+ * Keypad
+ */
+#define KPC 0x00 /* Keypad Interface Control register */
+#define KPDK 0x08 /* Keypad Interface Direct Key register */
+#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */
+#define KPMK 0x18 /* Keypad Interface Matrix Key register */
+#define KPAS 0x20 /* Keypad Interface Automatic Scan register */
+#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 0 */
+#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 1 */
+#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 2 */
+#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple
+ Key Presser register 3 */
+#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval
+ register */
+
+/* Keypad defines */
+#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
+#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
+#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
+#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
+#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */
+#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */
+#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */
+#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */
+#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */
+#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */
+#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */
+#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */
+#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
+#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
+#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
+#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
+#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
+#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
+#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
+#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
+#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
+
+#define KPDK_DKP (0x1 << 31)
+#define KPDK_DK7 (0x1 << 7)
+#define KPDK_DK6 (0x1 << 6)
+#define KPDK_DK5 (0x1 << 5)
+#define KPDK_DK4 (0x1 << 4)
+#define KPDK_DK3 (0x1 << 3)
+#define KPDK_DK2 (0x1 << 2)
+#define KPDK_DK1 (0x1 << 1)
+#define KPDK_DK0 (0x1 << 0)
+
+#define KPREC_OF1 (0x1 << 31)
+#define KPREC_UF1 (0x1 << 30)
+#define KPREC_OF0 (0x1 << 15)
+#define KPREC_UF0 (0x1 << 14)
+
+#define KPMK_MKP (0x1 << 31)
+#define KPAS_SO (0x1 << 31)
+#define KPASMKPx_SO (0x1 << 31)
+
+
+#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2)))
+
+#define PXAKBD_MAXROW 8
+#define PXAKBD_MAXCOL 8
+
+struct PXA2xxKeyPadState {
+ qemu_irq irq;
+ struct keymap *map;
+
+ uint32_t kpc;
+ uint32_t kpdk;
+ uint32_t kprec;
+ uint32_t kpmk;
+ uint32_t kpas;
+ uint32_t kpasmkp0;
+ uint32_t kpasmkp1;
+ uint32_t kpasmkp2;
+ uint32_t kpasmkp3;
+ uint32_t kpkdi;
+};
+
+static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
+{
+ int row, col,rel;
+
+ if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
+ return;
+
+ if(kp->kpc & KPC_AS || kp->kpc & KPC_ASACT) {
+ if(kp->kpc & KPC_AS)
+ kp->kpc &= ~(KPC_AS);
+
+ rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
+ keycode &= ~(0x80); /* strip qemu key release bit */
+ row = kp->map[keycode].row;
+ col = kp->map[keycode].column;
+ if(row == -1 || col == -1)
+ return;
+ switch (col) {
+ case 0:
+ case 1:
+ if(rel)
+ kp->kpasmkp0 = ~(0xffffffff);
+ else
+ kp->kpasmkp0 |= KPASMKPx_MKC(row,col);
+ break;
+ case 2:
+ case 3:
+ if(rel)
+ kp->kpasmkp1 = ~(0xffffffff);
+ else
+ kp->kpasmkp1 |= KPASMKPx_MKC(row,col);
+ break;
+ case 4:
+ case 5:
+ if(rel)
+ kp->kpasmkp2 = ~(0xffffffff);
+ else
+ kp->kpasmkp2 |= KPASMKPx_MKC(row,col);
+ break;
+ case 6:
+ case 7:
+ if(rel)
+ kp->kpasmkp3 = ~(0xffffffff);
+ else
+ kp->kpasmkp3 |= KPASMKPx_MKC(row,col);
+ break;
+ } /* switch */
+ goto out;
+ }
+ return;
+
+out:
+ if(kp->kpc & KPC_MIE) {
+ kp->kpc |= KPC_MI;
+ qemu_irq_raise(kp->irq);
+ }
+ return;
+}
+
+static uint32_t pxa2xx_keypad_read(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
+ uint32_t tmp;
+
+ switch (offset) {
+ case KPC:
+ tmp = s->kpc;
+ if(tmp & KPC_MI)
+ s->kpc &= ~(KPC_MI);
+ if(tmp & KPC_DI)
+ s->kpc &= ~(KPC_DI);
+ qemu_irq_lower(s->irq);
+ return tmp;
+ break;
+ case KPDK:
+ return s->kpdk;
+ break;
+ case KPREC:
+ tmp = s->kprec;
+ if(tmp & KPREC_OF1)
+ s->kprec &= ~(KPREC_OF1);
+ if(tmp & KPREC_UF1)
+ s->kprec &= ~(KPREC_UF1);
+ if(tmp & KPREC_OF0)
+ s->kprec &= ~(KPREC_OF0);
+ if(tmp & KPREC_UF0)
+ s->kprec &= ~(KPREC_UF0);
+ return tmp;
+ break;
+ case KPMK:
+ tmp = s->kpmk;
+ if(tmp & KPMK_MKP)
+ s->kpmk &= ~(KPMK_MKP);
+ return tmp;
+ break;
+ case KPAS:
+ return s->kpas;
+ break;
+ case KPASMKP0:
+ return s->kpasmkp0;
+ break;
+ case KPASMKP1:
+ return s->kpasmkp1;
+ break;
+ case KPASMKP2:
+ return s->kpasmkp2;
+ break;
+ case KPASMKP3:
+ return s->kpasmkp3;
+ break;
+ case KPKDI:
+ return s->kpkdi;
+ break;
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_keypad_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
+
+ switch (offset) {
+ case KPC:
+ s->kpc = value;
+ break;
+ case KPDK:
+ s->kpdk = value;
+ break;
+ case KPREC:
+ s->kprec = value;
+ break;
+ case KPMK:
+ s->kpmk = value;
+ break;
+ case KPAS:
+ s->kpas = value;
+ break;
+ case KPASMKP0:
+ s->kpasmkp0 = value;
+ break;
+ case KPASMKP1:
+ s->kpasmkp1 = value;
+ break;
+ case KPASMKP2:
+ s->kpasmkp2 = value;
+ break;
+ case KPASMKP3:
+ s->kpasmkp3 = value;
+ break;
+ case KPKDI:
+ s->kpkdi = value;
+ break;
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_keypad_readfn[] = {
+ pxa2xx_keypad_read,
+ pxa2xx_keypad_read,
+ pxa2xx_keypad_read
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_keypad_writefn[] = {
+ pxa2xx_keypad_write,
+ pxa2xx_keypad_write,
+ pxa2xx_keypad_write
+};
+
+static void pxa2xx_keypad_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
+
+ qemu_put_be32s(f, &s->kpc);
+ qemu_put_be32s(f, &s->kpdk);
+ qemu_put_be32s(f, &s->kprec);
+ qemu_put_be32s(f, &s->kpmk);
+ qemu_put_be32s(f, &s->kpas);
+ qemu_put_be32s(f, &s->kpasmkp0);
+ qemu_put_be32s(f, &s->kpasmkp1);
+ qemu_put_be32s(f, &s->kpasmkp2);
+ qemu_put_be32s(f, &s->kpasmkp3);
+ qemu_put_be32s(f, &s->kpkdi);
+
+}
+
+static int pxa2xx_keypad_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
+
+ qemu_get_be32s(f, &s->kpc);
+ qemu_get_be32s(f, &s->kpdk);
+ qemu_get_be32s(f, &s->kprec);
+ qemu_get_be32s(f, &s->kpmk);
+ qemu_get_be32s(f, &s->kpas);
+ qemu_get_be32s(f, &s->kpasmkp0);
+ qemu_get_be32s(f, &s->kpasmkp1);
+ qemu_get_be32s(f, &s->kpasmkp2);
+ qemu_get_be32s(f, &s->kpasmkp3);
+ qemu_get_be32s(f, &s->kpkdi);
+
+ return 0;
+}
+
+PXA2xxKeyPadState *pxa27x_keypad_init(target_phys_addr_t base,
+ qemu_irq irq)
+{
+ int iomemtype;
+ PXA2xxKeyPadState *s;
+
+ s = (PXA2xxKeyPadState *) qemu_mallocz(sizeof(PXA2xxKeyPadState));
+ s->irq = irq;
+
+ iomemtype = cpu_register_io_memory(pxa2xx_keypad_readfn,
+ pxa2xx_keypad_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00100000, iomemtype);
+
+ register_savevm(NULL, "pxa2xx_keypad", 0, 0,
+ pxa2xx_keypad_save, pxa2xx_keypad_load, s);
+
+ return s;
+}
+
+void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
+ int size)
+{
+ if(!map || size < 0x80) {
+ fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__);
+ exit(-1);
+ }
+
+ kp->map = map;
+ qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
+}
diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c
new file mode 100644
index 0000000..5b2b07e
--- /dev/null
+++ b/hw/pxa2xx_lcd.c
@@ -0,0 +1,982 @@
+/*
+ * Intel XScale PXA255/270 LCDC emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPLv2.
+ */
+
+#include "hw.h"
+#include "console.h"
+#include "pxa.h"
+#include "pixel_ops.h"
+/* FIXME: For graphic_rotate. Should probably be done in common code. */
+#include "sysemu.h"
+#include "framebuffer.h"
+
+struct PXA2xxLCDState {
+ qemu_irq irq;
+ int irqlevel;
+
+ int invalidated;
+ DisplayState *ds;
+ drawfn *line_fn[2];
+ int dest_width;
+ int xres, yres;
+ int pal_for;
+ int transp;
+ enum {
+ pxa_lcdc_2bpp = 1,
+ pxa_lcdc_4bpp = 2,
+ pxa_lcdc_8bpp = 3,
+ pxa_lcdc_16bpp = 4,
+ pxa_lcdc_18bpp = 5,
+ pxa_lcdc_18pbpp = 6,
+ pxa_lcdc_19bpp = 7,
+ pxa_lcdc_19pbpp = 8,
+ pxa_lcdc_24bpp = 9,
+ pxa_lcdc_25bpp = 10,
+ } bpp;
+
+ uint32_t control[6];
+ uint32_t status[2];
+ uint32_t ovl1c[2];
+ uint32_t ovl2c[2];
+ uint32_t ccr;
+ uint32_t cmdcr;
+ uint32_t trgbr;
+ uint32_t tcr;
+ uint32_t liidr;
+ uint8_t bscntr;
+
+ struct {
+ target_phys_addr_t branch;
+ int up;
+ uint8_t palette[1024];
+ uint8_t pbuffer[1024];
+ void (*redraw)(PXA2xxLCDState *s, target_phys_addr_t addr,
+ int *miny, int *maxy);
+
+ target_phys_addr_t descriptor;
+ target_phys_addr_t source;
+ uint32_t id;
+ uint32_t command;
+ } dma_ch[7];
+
+ qemu_irq vsync_cb;
+ int orientation;
+};
+
+typedef struct __attribute__ ((__packed__)) {
+ uint32_t fdaddr;
+ uint32_t fsaddr;
+ uint32_t fidr;
+ uint32_t ldcmd;
+} PXAFrameDescriptor;
+
+#define LCCR0 0x000 /* LCD Controller Control register 0 */
+#define LCCR1 0x004 /* LCD Controller Control register 1 */
+#define LCCR2 0x008 /* LCD Controller Control register 2 */
+#define LCCR3 0x00c /* LCD Controller Control register 3 */
+#define LCCR4 0x010 /* LCD Controller Control register 4 */
+#define LCCR5 0x014 /* LCD Controller Control register 5 */
+
+#define FBR0 0x020 /* DMA Channel 0 Frame Branch register */
+#define FBR1 0x024 /* DMA Channel 1 Frame Branch register */
+#define FBR2 0x028 /* DMA Channel 2 Frame Branch register */
+#define FBR3 0x02c /* DMA Channel 3 Frame Branch register */
+#define FBR4 0x030 /* DMA Channel 4 Frame Branch register */
+#define FBR5 0x110 /* DMA Channel 5 Frame Branch register */
+#define FBR6 0x114 /* DMA Channel 6 Frame Branch register */
+
+#define LCSR1 0x034 /* LCD Controller Status register 1 */
+#define LCSR0 0x038 /* LCD Controller Status register 0 */
+#define LIIDR 0x03c /* LCD Controller Interrupt ID register */
+
+#define TRGBR 0x040 /* TMED RGB Seed register */
+#define TCR 0x044 /* TMED Control register */
+
+#define OVL1C1 0x050 /* Overlay 1 Control register 1 */
+#define OVL1C2 0x060 /* Overlay 1 Control register 2 */
+#define OVL2C1 0x070 /* Overlay 2 Control register 1 */
+#define OVL2C2 0x080 /* Overlay 2 Control register 2 */
+#define CCR 0x090 /* Cursor Control register */
+
+#define CMDCR 0x100 /* Command Control register */
+#define PRSR 0x104 /* Panel Read Status register */
+
+#define PXA_LCDDMA_CHANS 7
+#define DMA_FDADR 0x00 /* Frame Descriptor Address register */
+#define DMA_FSADR 0x04 /* Frame Source Address register */
+#define DMA_FIDR 0x08 /* Frame ID register */
+#define DMA_LDCMD 0x0c /* Command register */
+
+/* LCD Buffer Strength Control register */
+#define BSCNTR 0x04000054
+
+/* Bitfield masks */
+#define LCCR0_ENB (1 << 0)
+#define LCCR0_CMS (1 << 1)
+#define LCCR0_SDS (1 << 2)
+#define LCCR0_LDM (1 << 3)
+#define LCCR0_SOFM0 (1 << 4)
+#define LCCR0_IUM (1 << 5)
+#define LCCR0_EOFM0 (1 << 6)
+#define LCCR0_PAS (1 << 7)
+#define LCCR0_DPD (1 << 9)
+#define LCCR0_DIS (1 << 10)
+#define LCCR0_QDM (1 << 11)
+#define LCCR0_PDD (0xff << 12)
+#define LCCR0_BSM0 (1 << 20)
+#define LCCR0_OUM (1 << 21)
+#define LCCR0_LCDT (1 << 22)
+#define LCCR0_RDSTM (1 << 23)
+#define LCCR0_CMDIM (1 << 24)
+#define LCCR0_OUC (1 << 25)
+#define LCCR0_LDDALT (1 << 26)
+#define LCCR1_PPL(x) ((x) & 0x3ff)
+#define LCCR2_LPP(x) ((x) & 0x3ff)
+#define LCCR3_API (15 << 16)
+#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8))
+#define LCCR3_PDFOR(x) (((x) >> 30) & 3)
+#define LCCR4_K1(x) (((x) >> 0) & 7)
+#define LCCR4_K2(x) (((x) >> 3) & 7)
+#define LCCR4_K3(x) (((x) >> 6) & 7)
+#define LCCR4_PALFOR(x) (((x) >> 15) & 3)
+#define LCCR5_SOFM(ch) (1 << (ch - 1))
+#define LCCR5_EOFM(ch) (1 << (ch + 7))
+#define LCCR5_BSM(ch) (1 << (ch + 15))
+#define LCCR5_IUM(ch) (1 << (ch + 23))
+#define OVLC1_EN (1 << 31)
+#define CCR_CEN (1 << 31)
+#define FBR_BRA (1 << 0)
+#define FBR_BINT (1 << 1)
+#define FBR_SRCADDR (0xfffffff << 4)
+#define LCSR0_LDD (1 << 0)
+#define LCSR0_SOF0 (1 << 1)
+#define LCSR0_BER (1 << 2)
+#define LCSR0_ABC (1 << 3)
+#define LCSR0_IU0 (1 << 4)
+#define LCSR0_IU1 (1 << 5)
+#define LCSR0_OU (1 << 6)
+#define LCSR0_QD (1 << 7)
+#define LCSR0_EOF0 (1 << 8)
+#define LCSR0_BS0 (1 << 9)
+#define LCSR0_SINT (1 << 10)
+#define LCSR0_RDST (1 << 11)
+#define LCSR0_CMDINT (1 << 12)
+#define LCSR0_BERCH(x) (((x) & 7) << 28)
+#define LCSR1_SOF(ch) (1 << (ch - 1))
+#define LCSR1_EOF(ch) (1 << (ch + 7))
+#define LCSR1_BS(ch) (1 << (ch + 15))
+#define LCSR1_IU(ch) (1 << (ch + 23))
+#define LDCMD_LENGTH(x) ((x) & 0x001ffffc)
+#define LDCMD_EOFINT (1 << 21)
+#define LDCMD_SOFINT (1 << 22)
+#define LDCMD_PAL (1 << 26)
+
+/* Route internal interrupt lines to the global IC */
+static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s)
+{
+ int level = 0;
+ level |= (s->status[0] & LCSR0_LDD) && !(s->control[0] & LCCR0_LDM);
+ level |= (s->status[0] & LCSR0_SOF0) && !(s->control[0] & LCCR0_SOFM0);
+ level |= (s->status[0] & LCSR0_IU0) && !(s->control[0] & LCCR0_IUM);
+ level |= (s->status[0] & LCSR0_IU1) && !(s->control[5] & LCCR5_IUM(1));
+ level |= (s->status[0] & LCSR0_OU) && !(s->control[0] & LCCR0_OUM);
+ level |= (s->status[0] & LCSR0_QD) && !(s->control[0] & LCCR0_QDM);
+ level |= (s->status[0] & LCSR0_EOF0) && !(s->control[0] & LCCR0_EOFM0);
+ level |= (s->status[0] & LCSR0_BS0) && !(s->control[0] & LCCR0_BSM0);
+ level |= (s->status[0] & LCSR0_RDST) && !(s->control[0] & LCCR0_RDSTM);
+ level |= (s->status[0] & LCSR0_CMDINT) && !(s->control[0] & LCCR0_CMDIM);
+ level |= (s->status[1] & ~s->control[5]);
+
+ qemu_set_irq(s->irq, !!level);
+ s->irqlevel = level;
+}
+
+/* Set Branch Status interrupt high and poke associated registers */
+static inline void pxa2xx_dma_bs_set(PXA2xxLCDState *s, int ch)
+{
+ int unmasked;
+ if (ch == 0) {
+ s->status[0] |= LCSR0_BS0;
+ unmasked = !(s->control[0] & LCCR0_BSM0);
+ } else {
+ s->status[1] |= LCSR1_BS(ch);
+ unmasked = !(s->control[5] & LCCR5_BSM(ch));
+ }
+
+ if (unmasked) {
+ if (s->irqlevel)
+ s->status[0] |= LCSR0_SINT;
+ else
+ s->liidr = s->dma_ch[ch].id;
+ }
+}
+
+/* Set Start Of Frame Status interrupt high and poke associated registers */
+static inline void pxa2xx_dma_sof_set(PXA2xxLCDState *s, int ch)
+{
+ int unmasked;
+ if (!(s->dma_ch[ch].command & LDCMD_SOFINT))
+ return;
+
+ if (ch == 0) {
+ s->status[0] |= LCSR0_SOF0;
+ unmasked = !(s->control[0] & LCCR0_SOFM0);
+ } else {
+ s->status[1] |= LCSR1_SOF(ch);
+ unmasked = !(s->control[5] & LCCR5_SOFM(ch));
+ }
+
+ if (unmasked) {
+ if (s->irqlevel)
+ s->status[0] |= LCSR0_SINT;
+ else
+ s->liidr = s->dma_ch[ch].id;
+ }
+}
+
+/* Set End Of Frame Status interrupt high and poke associated registers */
+static inline void pxa2xx_dma_eof_set(PXA2xxLCDState *s, int ch)
+{
+ int unmasked;
+ if (!(s->dma_ch[ch].command & LDCMD_EOFINT))
+ return;
+
+ if (ch == 0) {
+ s->status[0] |= LCSR0_EOF0;
+ unmasked = !(s->control[0] & LCCR0_EOFM0);
+ } else {
+ s->status[1] |= LCSR1_EOF(ch);
+ unmasked = !(s->control[5] & LCCR5_EOFM(ch));
+ }
+
+ if (unmasked) {
+ if (s->irqlevel)
+ s->status[0] |= LCSR0_SINT;
+ else
+ s->liidr = s->dma_ch[ch].id;
+ }
+}
+
+/* Set Bus Error Status interrupt high and poke associated registers */
+static inline void pxa2xx_dma_ber_set(PXA2xxLCDState *s, int ch)
+{
+ s->status[0] |= LCSR0_BERCH(ch) | LCSR0_BER;
+ if (s->irqlevel)
+ s->status[0] |= LCSR0_SINT;
+ else
+ s->liidr = s->dma_ch[ch].id;
+}
+
+/* Set Read Status interrupt high and poke associated registers */
+static inline void pxa2xx_dma_rdst_set(PXA2xxLCDState *s)
+{
+ s->status[0] |= LCSR0_RDST;
+ if (s->irqlevel && !(s->control[0] & LCCR0_RDSTM))
+ s->status[0] |= LCSR0_SINT;
+}
+
+/* Load new Frame Descriptors from DMA */
+static void pxa2xx_descriptor_load(PXA2xxLCDState *s)
+{
+ PXAFrameDescriptor desc;
+ target_phys_addr_t descptr;
+ int i;
+
+ for (i = 0; i < PXA_LCDDMA_CHANS; i ++) {
+ s->dma_ch[i].source = 0;
+
+ if (!s->dma_ch[i].up)
+ continue;
+
+ if (s->dma_ch[i].branch & FBR_BRA) {
+ descptr = s->dma_ch[i].branch & FBR_SRCADDR;
+ if (s->dma_ch[i].branch & FBR_BINT)
+ pxa2xx_dma_bs_set(s, i);
+ s->dma_ch[i].branch &= ~FBR_BRA;
+ } else
+ descptr = s->dma_ch[i].descriptor;
+
+ if (!(descptr >= PXA2XX_SDRAM_BASE && descptr +
+ sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size))
+ continue;
+
+ cpu_physical_memory_read(descptr, (void *)&desc, sizeof(desc));
+ s->dma_ch[i].descriptor = tswap32(desc.fdaddr);
+ s->dma_ch[i].source = tswap32(desc.fsaddr);
+ s->dma_ch[i].id = tswap32(desc.fidr);
+ s->dma_ch[i].command = tswap32(desc.ldcmd);
+ }
+}
+
+static uint32_t pxa2xx_lcdc_read(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+ int ch;
+
+ switch (offset) {
+ case LCCR0:
+ return s->control[0];
+ case LCCR1:
+ return s->control[1];
+ case LCCR2:
+ return s->control[2];
+ case LCCR3:
+ return s->control[3];
+ case LCCR4:
+ return s->control[4];
+ case LCCR5:
+ return s->control[5];
+
+ case OVL1C1:
+ return s->ovl1c[0];
+ case OVL1C2:
+ return s->ovl1c[1];
+ case OVL2C1:
+ return s->ovl2c[0];
+ case OVL2C2:
+ return s->ovl2c[1];
+
+ case CCR:
+ return s->ccr;
+
+ case CMDCR:
+ return s->cmdcr;
+
+ case TRGBR:
+ return s->trgbr;
+ case TCR:
+ return s->tcr;
+
+ case 0x200 ... 0x1000: /* DMA per-channel registers */
+ ch = (offset - 0x200) >> 4;
+ if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS))
+ goto fail;
+
+ switch (offset & 0xf) {
+ case DMA_FDADR:
+ return s->dma_ch[ch].descriptor;
+ case DMA_FSADR:
+ return s->dma_ch[ch].source;
+ case DMA_FIDR:
+ return s->dma_ch[ch].id;
+ case DMA_LDCMD:
+ return s->dma_ch[ch].command;
+ default:
+ goto fail;
+ }
+
+ case FBR0:
+ return s->dma_ch[0].branch;
+ case FBR1:
+ return s->dma_ch[1].branch;
+ case FBR2:
+ return s->dma_ch[2].branch;
+ case FBR3:
+ return s->dma_ch[3].branch;
+ case FBR4:
+ return s->dma_ch[4].branch;
+ case FBR5:
+ return s->dma_ch[5].branch;
+ case FBR6:
+ return s->dma_ch[6].branch;
+
+ case BSCNTR:
+ return s->bscntr;
+
+ case PRSR:
+ return 0;
+
+ case LCSR0:
+ return s->status[0];
+ case LCSR1:
+ return s->status[1];
+ case LIIDR:
+ return s->liidr;
+
+ default:
+ fail:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_lcdc_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+ int ch;
+
+ switch (offset) {
+ case LCCR0:
+ /* ACK Quick Disable done */
+ if ((s->control[0] & LCCR0_ENB) && !(value & LCCR0_ENB))
+ s->status[0] |= LCSR0_QD;
+
+ if (!(s->control[0] & LCCR0_LCDT) && (value & LCCR0_LCDT))
+ printf("%s: internal frame buffer unsupported\n", __FUNCTION__);
+
+ if ((s->control[3] & LCCR3_API) &&
+ (value & LCCR0_ENB) && !(value & LCCR0_LCDT))
+ s->status[0] |= LCSR0_ABC;
+
+ s->control[0] = value & 0x07ffffff;
+ pxa2xx_lcdc_int_update(s);
+
+ s->dma_ch[0].up = !!(value & LCCR0_ENB);
+ s->dma_ch[1].up = (s->ovl1c[0] & OVLC1_EN) || (value & LCCR0_SDS);
+ break;
+
+ case LCCR1:
+ s->control[1] = value;
+ break;
+
+ case LCCR2:
+ s->control[2] = value;
+ break;
+
+ case LCCR3:
+ s->control[3] = value & 0xefffffff;
+ s->bpp = LCCR3_BPP(value);
+ break;
+
+ case LCCR4:
+ s->control[4] = value & 0x83ff81ff;
+ break;
+
+ case LCCR5:
+ s->control[5] = value & 0x3f3f3f3f;
+ break;
+
+ case OVL1C1:
+ if (!(s->ovl1c[0] & OVLC1_EN) && (value & OVLC1_EN))
+ printf("%s: Overlay 1 not supported\n", __FUNCTION__);
+
+ s->ovl1c[0] = value & 0x80ffffff;
+ s->dma_ch[1].up = (value & OVLC1_EN) || (s->control[0] & LCCR0_SDS);
+ break;
+
+ case OVL1C2:
+ s->ovl1c[1] = value & 0x000fffff;
+ break;
+
+ case OVL2C1:
+ if (!(s->ovl2c[0] & OVLC1_EN) && (value & OVLC1_EN))
+ printf("%s: Overlay 2 not supported\n", __FUNCTION__);
+
+ s->ovl2c[0] = value & 0x80ffffff;
+ s->dma_ch[2].up = !!(value & OVLC1_EN);
+ s->dma_ch[3].up = !!(value & OVLC1_EN);
+ s->dma_ch[4].up = !!(value & OVLC1_EN);
+ break;
+
+ case OVL2C2:
+ s->ovl2c[1] = value & 0x007fffff;
+ break;
+
+ case CCR:
+ if (!(s->ccr & CCR_CEN) && (value & CCR_CEN))
+ printf("%s: Hardware cursor unimplemented\n", __FUNCTION__);
+
+ s->ccr = value & 0x81ffffe7;
+ s->dma_ch[5].up = !!(value & CCR_CEN);
+ break;
+
+ case CMDCR:
+ s->cmdcr = value & 0xff;
+ break;
+
+ case TRGBR:
+ s->trgbr = value & 0x00ffffff;
+ break;
+
+ case TCR:
+ s->tcr = value & 0x7fff;
+ break;
+
+ case 0x200 ... 0x1000: /* DMA per-channel registers */
+ ch = (offset - 0x200) >> 4;
+ if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS))
+ goto fail;
+
+ switch (offset & 0xf) {
+ case DMA_FDADR:
+ s->dma_ch[ch].descriptor = value & 0xfffffff0;
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+
+ case FBR0:
+ s->dma_ch[0].branch = value & 0xfffffff3;
+ break;
+ case FBR1:
+ s->dma_ch[1].branch = value & 0xfffffff3;
+ break;
+ case FBR2:
+ s->dma_ch[2].branch = value & 0xfffffff3;
+ break;
+ case FBR3:
+ s->dma_ch[3].branch = value & 0xfffffff3;
+ break;
+ case FBR4:
+ s->dma_ch[4].branch = value & 0xfffffff3;
+ break;
+ case FBR5:
+ s->dma_ch[5].branch = value & 0xfffffff3;
+ break;
+ case FBR6:
+ s->dma_ch[6].branch = value & 0xfffffff3;
+ break;
+
+ case BSCNTR:
+ s->bscntr = value & 0xf;
+ break;
+
+ case PRSR:
+ break;
+
+ case LCSR0:
+ s->status[0] &= ~(value & 0xfff);
+ if (value & LCSR0_BER)
+ s->status[0] &= ~LCSR0_BERCH(7);
+ break;
+
+ case LCSR1:
+ s->status[1] &= ~(value & 0x3e3f3f);
+ break;
+
+ default:
+ fail:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_lcdc_readfn[] = {
+ pxa2xx_lcdc_read,
+ pxa2xx_lcdc_read,
+ pxa2xx_lcdc_read
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_lcdc_writefn[] = {
+ pxa2xx_lcdc_write,
+ pxa2xx_lcdc_write,
+ pxa2xx_lcdc_write
+};
+
+/* Load new palette for a given DMA channel, convert to internal format */
+static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp)
+{
+ int i, n, format, r, g, b, alpha;
+ uint32_t *dest, *src;
+ s->pal_for = LCCR4_PALFOR(s->control[4]);
+ format = s->pal_for;
+
+ switch (bpp) {
+ case pxa_lcdc_2bpp:
+ n = 4;
+ break;
+ case pxa_lcdc_4bpp:
+ n = 16;
+ break;
+ case pxa_lcdc_8bpp:
+ n = 256;
+ break;
+ default:
+ format = 0;
+ return;
+ }
+
+ src = (uint32_t *) s->dma_ch[ch].pbuffer;
+ dest = (uint32_t *) s->dma_ch[ch].palette;
+ alpha = r = g = b = 0;
+
+ for (i = 0; i < n; i ++) {
+ switch (format) {
+ case 0: /* 16 bpp, no transparency */
+ alpha = 0;
+ if (s->control[0] & LCCR0_CMS)
+ r = g = b = *src & 0xff;
+ else {
+ r = (*src & 0xf800) >> 8;
+ g = (*src & 0x07e0) >> 3;
+ b = (*src & 0x001f) << 3;
+ }
+ break;
+ case 1: /* 16 bpp plus transparency */
+ alpha = *src & (1 << 24);
+ if (s->control[0] & LCCR0_CMS)
+ r = g = b = *src & 0xff;
+ else {
+ r = (*src & 0xf800) >> 8;
+ g = (*src & 0x07e0) >> 3;
+ b = (*src & 0x001f) << 3;
+ }
+ break;
+ case 2: /* 18 bpp plus transparency */
+ alpha = *src & (1 << 24);
+ if (s->control[0] & LCCR0_CMS)
+ r = g = b = *src & 0xff;
+ else {
+ r = (*src & 0xf80000) >> 16;
+ g = (*src & 0x00fc00) >> 8;
+ b = (*src & 0x0000f8);
+ }
+ break;
+ case 3: /* 24 bpp plus transparency */
+ alpha = *src & (1 << 24);
+ if (s->control[0] & LCCR0_CMS)
+ r = g = b = *src & 0xff;
+ else {
+ r = (*src & 0xff0000) >> 16;
+ g = (*src & 0x00ff00) >> 8;
+ b = (*src & 0x0000ff);
+ }
+ break;
+ }
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 8:
+ *dest = rgb_to_pixel8(r, g, b) | alpha;
+ break;
+ case 15:
+ *dest = rgb_to_pixel15(r, g, b) | alpha;
+ break;
+ case 16:
+ *dest = rgb_to_pixel16(r, g, b) | alpha;
+ break;
+ case 24:
+ *dest = rgb_to_pixel24(r, g, b) | alpha;
+ break;
+ case 32:
+ *dest = rgb_to_pixel32(r, g, b) | alpha;
+ break;
+ }
+ src ++;
+ dest ++;
+ }
+}
+
+static void pxa2xx_lcdc_dma0_redraw_horiz(PXA2xxLCDState *s,
+ target_phys_addr_t addr, int *miny, int *maxy)
+{
+ int src_width, dest_width;
+ drawfn fn = NULL;
+ if (s->dest_width)
+ fn = s->line_fn[s->transp][s->bpp];
+ if (!fn)
+ return;
+
+ src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */
+ if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp)
+ src_width *= 3;
+ else if (s->bpp > pxa_lcdc_16bpp)
+ src_width *= 4;
+ else if (s->bpp > pxa_lcdc_8bpp)
+ src_width *= 2;
+
+ dest_width = s->xres * s->dest_width;
+ *miny = 0;
+ framebuffer_update_display(s->ds,
+ addr, s->xres, s->yres,
+ src_width, dest_width, s->dest_width,
+ s->invalidated,
+ fn, s->dma_ch[0].palette, miny, maxy);
+}
+
+static void pxa2xx_lcdc_dma0_redraw_vert(PXA2xxLCDState *s,
+ target_phys_addr_t addr, int *miny, int *maxy)
+{
+ int src_width, dest_width;
+ drawfn fn = NULL;
+ if (s->dest_width)
+ fn = s->line_fn[s->transp][s->bpp];
+ if (!fn)
+ return;
+
+ src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */
+ if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp)
+ src_width *= 3;
+ else if (s->bpp > pxa_lcdc_16bpp)
+ src_width *= 4;
+ else if (s->bpp > pxa_lcdc_8bpp)
+ src_width *= 2;
+
+ dest_width = s->yres * s->dest_width;
+ *miny = 0;
+ framebuffer_update_display(s->ds,
+ addr, s->xres, s->yres,
+ src_width, s->dest_width, -dest_width,
+ s->invalidated,
+ fn, s->dma_ch[0].palette,
+ miny, maxy);
+}
+
+static void pxa2xx_lcdc_resize(PXA2xxLCDState *s)
+{
+ int width, height;
+ if (!(s->control[0] & LCCR0_ENB))
+ return;
+
+ width = LCCR1_PPL(s->control[1]) + 1;
+ height = LCCR2_LPP(s->control[2]) + 1;
+
+ if (width != s->xres || height != s->yres) {
+ if (s->orientation)
+ qemu_console_resize(s->ds, height, width);
+ else
+ qemu_console_resize(s->ds, width, height);
+ s->invalidated = 1;
+ s->xres = width;
+ s->yres = height;
+ }
+}
+
+static void pxa2xx_update_display(void *opaque)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+ target_phys_addr_t fbptr;
+ int miny, maxy;
+ int ch;
+ if (!(s->control[0] & LCCR0_ENB))
+ return;
+
+ pxa2xx_descriptor_load(s);
+
+ pxa2xx_lcdc_resize(s);
+ miny = s->yres;
+ maxy = 0;
+ s->transp = s->dma_ch[2].up || s->dma_ch[3].up;
+ /* Note: With overlay planes the order depends on LCCR0 bit 25. */
+ for (ch = 0; ch < PXA_LCDDMA_CHANS; ch ++)
+ if (s->dma_ch[ch].up) {
+ if (!s->dma_ch[ch].source) {
+ pxa2xx_dma_ber_set(s, ch);
+ continue;
+ }
+ fbptr = s->dma_ch[ch].source;
+ if (!(fbptr >= PXA2XX_SDRAM_BASE &&
+ fbptr <= PXA2XX_SDRAM_BASE + ram_size)) {
+ pxa2xx_dma_ber_set(s, ch);
+ continue;
+ }
+
+ if (s->dma_ch[ch].command & LDCMD_PAL) {
+ cpu_physical_memory_read(fbptr, s->dma_ch[ch].pbuffer,
+ MAX(LDCMD_LENGTH(s->dma_ch[ch].command),
+ sizeof(s->dma_ch[ch].pbuffer)));
+ pxa2xx_palette_parse(s, ch, s->bpp);
+ } else {
+ /* Do we need to reparse palette */
+ if (LCCR4_PALFOR(s->control[4]) != s->pal_for)
+ pxa2xx_palette_parse(s, ch, s->bpp);
+
+ /* ACK frame start */
+ pxa2xx_dma_sof_set(s, ch);
+
+ s->dma_ch[ch].redraw(s, fbptr, &miny, &maxy);
+ s->invalidated = 0;
+
+ /* ACK frame completed */
+ pxa2xx_dma_eof_set(s, ch);
+ }
+ }
+
+ if (s->control[0] & LCCR0_DIS) {
+ /* ACK last frame completed */
+ s->control[0] &= ~LCCR0_ENB;
+ s->status[0] |= LCSR0_LDD;
+ }
+
+ if (miny >= 0) {
+ if (s->orientation)
+ dpy_update(s->ds, miny, 0, maxy - miny, s->xres);
+ else
+ dpy_update(s->ds, 0, miny, s->xres, maxy - miny);
+ }
+ pxa2xx_lcdc_int_update(s);
+
+ qemu_irq_raise(s->vsync_cb);
+}
+
+static void pxa2xx_invalidate_display(void *opaque)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+ s->invalidated = 1;
+}
+
+static void pxa2xx_screen_dump(void *opaque, const char *filename)
+{
+ /* TODO */
+}
+
+static void pxa2xx_lcdc_orientation(void *opaque, int angle)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+
+ if (angle) {
+ s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_vert;
+ } else {
+ s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_horiz;
+ }
+
+ s->orientation = angle;
+ s->xres = s->yres = -1;
+ pxa2xx_lcdc_resize(s);
+}
+
+static void pxa2xx_lcdc_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+ int i;
+
+ qemu_put_be32(f, s->irqlevel);
+ qemu_put_be32(f, s->transp);
+
+ for (i = 0; i < 6; i ++)
+ qemu_put_be32s(f, &s->control[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_put_be32s(f, &s->status[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_put_be32s(f, &s->ovl1c[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_put_be32s(f, &s->ovl2c[i]);
+ qemu_put_be32s(f, &s->ccr);
+ qemu_put_be32s(f, &s->cmdcr);
+ qemu_put_be32s(f, &s->trgbr);
+ qemu_put_be32s(f, &s->tcr);
+ qemu_put_be32s(f, &s->liidr);
+ qemu_put_8s(f, &s->bscntr);
+
+ for (i = 0; i < 7; i ++) {
+ qemu_put_betl(f, s->dma_ch[i].branch);
+ qemu_put_byte(f, s->dma_ch[i].up);
+ qemu_put_buffer(f, s->dma_ch[i].pbuffer, sizeof(s->dma_ch[i].pbuffer));
+
+ qemu_put_betl(f, s->dma_ch[i].descriptor);
+ qemu_put_betl(f, s->dma_ch[i].source);
+ qemu_put_be32s(f, &s->dma_ch[i].id);
+ qemu_put_be32s(f, &s->dma_ch[i].command);
+ }
+}
+
+static int pxa2xx_lcdc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxLCDState *s = (PXA2xxLCDState *) opaque;
+ int i;
+
+ s->irqlevel = qemu_get_be32(f);
+ s->transp = qemu_get_be32(f);
+
+ for (i = 0; i < 6; i ++)
+ qemu_get_be32s(f, &s->control[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_get_be32s(f, &s->status[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_get_be32s(f, &s->ovl1c[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_get_be32s(f, &s->ovl2c[i]);
+ qemu_get_be32s(f, &s->ccr);
+ qemu_get_be32s(f, &s->cmdcr);
+ qemu_get_be32s(f, &s->trgbr);
+ qemu_get_be32s(f, &s->tcr);
+ qemu_get_be32s(f, &s->liidr);
+ qemu_get_8s(f, &s->bscntr);
+
+ for (i = 0; i < 7; i ++) {
+ s->dma_ch[i].branch = qemu_get_betl(f);
+ s->dma_ch[i].up = qemu_get_byte(f);
+ qemu_get_buffer(f, s->dma_ch[i].pbuffer, sizeof(s->dma_ch[i].pbuffer));
+
+ s->dma_ch[i].descriptor = qemu_get_betl(f);
+ s->dma_ch[i].source = qemu_get_betl(f);
+ qemu_get_be32s(f, &s->dma_ch[i].id);
+ qemu_get_be32s(f, &s->dma_ch[i].command);
+ }
+
+ s->bpp = LCCR3_BPP(s->control[3]);
+ s->xres = s->yres = s->pal_for = -1;
+
+ return 0;
+}
+
+#define BITS 8
+#include "pxa2xx_template.h"
+#define BITS 15
+#include "pxa2xx_template.h"
+#define BITS 16
+#include "pxa2xx_template.h"
+#define BITS 24
+#include "pxa2xx_template.h"
+#define BITS 32
+#include "pxa2xx_template.h"
+
+PXA2xxLCDState *pxa2xx_lcdc_init(target_phys_addr_t base, qemu_irq irq)
+{
+ int iomemtype;
+ PXA2xxLCDState *s;
+
+ s = (PXA2xxLCDState *) qemu_mallocz(sizeof(PXA2xxLCDState));
+ s->invalidated = 1;
+ s->irq = irq;
+
+ pxa2xx_lcdc_orientation(s, graphic_rotate);
+
+ iomemtype = cpu_register_io_memory(pxa2xx_lcdc_readfn,
+ pxa2xx_lcdc_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00100000, iomemtype);
+
+ s->ds = graphic_console_init(pxa2xx_update_display,
+ pxa2xx_invalidate_display,
+ pxa2xx_screen_dump, NULL, s);
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ s->dest_width = 0;
+ break;
+ case 8:
+ s->line_fn[0] = pxa2xx_draw_fn_8;
+ s->line_fn[1] = pxa2xx_draw_fn_8t;
+ s->dest_width = 1;
+ break;
+ case 15:
+ s->line_fn[0] = pxa2xx_draw_fn_15;
+ s->line_fn[1] = pxa2xx_draw_fn_15t;
+ s->dest_width = 2;
+ break;
+ case 16:
+ s->line_fn[0] = pxa2xx_draw_fn_16;
+ s->line_fn[1] = pxa2xx_draw_fn_16t;
+ s->dest_width = 2;
+ break;
+ case 24:
+ s->line_fn[0] = pxa2xx_draw_fn_24;
+ s->line_fn[1] = pxa2xx_draw_fn_24t;
+ s->dest_width = 3;
+ break;
+ case 32:
+ s->line_fn[0] = pxa2xx_draw_fn_32;
+ s->line_fn[1] = pxa2xx_draw_fn_32t;
+ s->dest_width = 4;
+ break;
+ default:
+ fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
+ exit(1);
+ }
+
+ register_savevm(NULL, "pxa2xx_lcdc", 0, 0,
+ pxa2xx_lcdc_save, pxa2xx_lcdc_load, s);
+
+ return s;
+}
+
+void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler)
+{
+ s->vsync_cb = handler;
+}
diff --git a/hw/pxa2xx_mmci.c b/hw/pxa2xx_mmci.c
new file mode 100644
index 0000000..24d409d
--- /dev/null
+++ b/hw/pxa2xx_mmci.c
@@ -0,0 +1,547 @@
+/*
+ * Intel XScale PXA255/270 MultiMediaCard/SD/SDIO Controller emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPLv2.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+#include "sd.h"
+
+struct PXA2xxMMCIState {
+ qemu_irq irq;
+ void *dma;
+
+ SDState *card;
+
+ uint32_t status;
+ uint32_t clkrt;
+ uint32_t spi;
+ uint32_t cmdat;
+ uint32_t resp_tout;
+ uint32_t read_tout;
+ int blklen;
+ int numblk;
+ uint32_t intmask;
+ uint32_t intreq;
+ int cmd;
+ uint32_t arg;
+
+ int active;
+ int bytesleft;
+ uint8_t tx_fifo[64];
+ int tx_start;
+ int tx_len;
+ uint8_t rx_fifo[32];
+ int rx_start;
+ int rx_len;
+ uint16_t resp_fifo[9];
+ int resp_len;
+
+ int cmdreq;
+ int ac_width;
+};
+
+#define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */
+#define MMC_STAT 0x04 /* MMC Status register */
+#define MMC_CLKRT 0x08 /* MMC Clock Rate register */
+#define MMC_SPI 0x0c /* MMC SPI Mode register */
+#define MMC_CMDAT 0x10 /* MMC Command/Data register */
+#define MMC_RESTO 0x14 /* MMC Response Time-Out register */
+#define MMC_RDTO 0x18 /* MMC Read Time-Out register */
+#define MMC_BLKLEN 0x1c /* MMC Block Length register */
+#define MMC_NUMBLK 0x20 /* MMC Number of Blocks register */
+#define MMC_PRTBUF 0x24 /* MMC Buffer Partly Full register */
+#define MMC_I_MASK 0x28 /* MMC Interrupt Mask register */
+#define MMC_I_REG 0x2c /* MMC Interrupt Request register */
+#define MMC_CMD 0x30 /* MMC Command register */
+#define MMC_ARGH 0x34 /* MMC Argument High register */
+#define MMC_ARGL 0x38 /* MMC Argument Low register */
+#define MMC_RES 0x3c /* MMC Response FIFO */
+#define MMC_RXFIFO 0x40 /* MMC Receive FIFO */
+#define MMC_TXFIFO 0x44 /* MMC Transmit FIFO */
+#define MMC_RDWAIT 0x48 /* MMC RD_WAIT register */
+#define MMC_BLKS_REM 0x4c /* MMC Blocks Remaining register */
+
+/* Bitfield masks */
+#define STRPCL_STOP_CLK (1 << 0)
+#define STRPCL_STRT_CLK (1 << 1)
+#define STAT_TOUT_RES (1 << 1)
+#define STAT_CLK_EN (1 << 8)
+#define STAT_DATA_DONE (1 << 11)
+#define STAT_PRG_DONE (1 << 12)
+#define STAT_END_CMDRES (1 << 13)
+#define SPI_SPI_MODE (1 << 0)
+#define CMDAT_RES_TYPE (3 << 0)
+#define CMDAT_DATA_EN (1 << 2)
+#define CMDAT_WR_RD (1 << 3)
+#define CMDAT_DMA_EN (1 << 7)
+#define CMDAT_STOP_TRAN (1 << 10)
+#define INT_DATA_DONE (1 << 0)
+#define INT_PRG_DONE (1 << 1)
+#define INT_END_CMD (1 << 2)
+#define INT_STOP_CMD (1 << 3)
+#define INT_CLK_OFF (1 << 4)
+#define INT_RXFIFO_REQ (1 << 5)
+#define INT_TXFIFO_REQ (1 << 6)
+#define INT_TINT (1 << 7)
+#define INT_DAT_ERR (1 << 8)
+#define INT_RES_ERR (1 << 9)
+#define INT_RD_STALLED (1 << 10)
+#define INT_SDIO_INT (1 << 11)
+#define INT_SDIO_SACK (1 << 12)
+#define PRTBUF_PRT_BUF (1 << 0)
+
+/* Route internal interrupt lines to the global IC and DMA */
+static void pxa2xx_mmci_int_update(PXA2xxMMCIState *s)
+{
+ uint32_t mask = s->intmask;
+ if (s->cmdat & CMDAT_DMA_EN) {
+ mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ;
+
+ pxa2xx_dma_request(s->dma,
+ PXA2XX_RX_RQ_MMCI, !!(s->intreq & INT_RXFIFO_REQ));
+ pxa2xx_dma_request(s->dma,
+ PXA2XX_TX_RQ_MMCI, !!(s->intreq & INT_TXFIFO_REQ));
+ }
+
+ qemu_set_irq(s->irq, !!(s->intreq & ~mask));
+}
+
+static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s)
+{
+ if (!s->active)
+ return;
+
+ if (s->cmdat & CMDAT_WR_RD) {
+ while (s->bytesleft && s->tx_len) {
+ sd_write_data(s->card, s->tx_fifo[s->tx_start ++]);
+ s->tx_start &= 0x1f;
+ s->tx_len --;
+ s->bytesleft --;
+ }
+ if (s->bytesleft)
+ s->intreq |= INT_TXFIFO_REQ;
+ } else
+ while (s->bytesleft && s->rx_len < 32) {
+ s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] =
+ sd_read_data(s->card);
+ s->bytesleft --;
+ s->intreq |= INT_RXFIFO_REQ;
+ }
+
+ if (!s->bytesleft) {
+ s->active = 0;
+ s->intreq |= INT_DATA_DONE;
+ s->status |= STAT_DATA_DONE;
+
+ if (s->cmdat & CMDAT_WR_RD) {
+ s->intreq |= INT_PRG_DONE;
+ s->status |= STAT_PRG_DONE;
+ }
+ }
+
+ pxa2xx_mmci_int_update(s);
+}
+
+static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s)
+{
+ int rsplen, i;
+ SDRequest request;
+ uint8_t response[16];
+
+ s->active = 1;
+ s->rx_len = 0;
+ s->tx_len = 0;
+ s->cmdreq = 0;
+
+ request.cmd = s->cmd;
+ request.arg = s->arg;
+ request.crc = 0; /* FIXME */
+
+ rsplen = sd_do_command(s->card, &request, response);
+ s->intreq |= INT_END_CMD;
+
+ memset(s->resp_fifo, 0, sizeof(s->resp_fifo));
+ switch (s->cmdat & CMDAT_RES_TYPE) {
+#define PXAMMCI_RESP(wd, value0, value1) \
+ s->resp_fifo[(wd) + 0] |= (value0); \
+ s->resp_fifo[(wd) + 1] |= (value1) << 8;
+ case 0: /* No response */
+ goto complete;
+
+ case 1: /* R1, R4, R5 or R6 */
+ if (rsplen < 4)
+ goto timeout;
+ goto complete;
+
+ case 2: /* R2 */
+ if (rsplen < 16)
+ goto timeout;
+ goto complete;
+
+ case 3: /* R3 */
+ if (rsplen < 4)
+ goto timeout;
+ goto complete;
+
+ complete:
+ for (i = 0; rsplen > 0; i ++, rsplen -= 2) {
+ PXAMMCI_RESP(i, response[i * 2], response[i * 2 + 1]);
+ }
+ s->status |= STAT_END_CMDRES;
+
+ if (!(s->cmdat & CMDAT_DATA_EN))
+ s->active = 0;
+ else
+ s->bytesleft = s->numblk * s->blklen;
+
+ s->resp_len = 0;
+ break;
+
+ timeout:
+ s->active = 0;
+ s->status |= STAT_TOUT_RES;
+ break;
+ }
+
+ pxa2xx_mmci_fifo_update(s);
+}
+
+static uint32_t pxa2xx_mmci_read(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ uint32_t ret;
+
+ switch (offset) {
+ case MMC_STRPCL:
+ return 0;
+ case MMC_STAT:
+ return s->status;
+ case MMC_CLKRT:
+ return s->clkrt;
+ case MMC_SPI:
+ return s->spi;
+ case MMC_CMDAT:
+ return s->cmdat;
+ case MMC_RESTO:
+ return s->resp_tout;
+ case MMC_RDTO:
+ return s->read_tout;
+ case MMC_BLKLEN:
+ return s->blklen;
+ case MMC_NUMBLK:
+ return s->numblk;
+ case MMC_PRTBUF:
+ return 0;
+ case MMC_I_MASK:
+ return s->intmask;
+ case MMC_I_REG:
+ return s->intreq;
+ case MMC_CMD:
+ return s->cmd | 0x40;
+ case MMC_ARGH:
+ return s->arg >> 16;
+ case MMC_ARGL:
+ return s->arg & 0xffff;
+ case MMC_RES:
+ if (s->resp_len < 9)
+ return s->resp_fifo[s->resp_len ++];
+ return 0;
+ case MMC_RXFIFO:
+ ret = 0;
+ while (s->ac_width -- && s->rx_len) {
+ ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3);
+ s->rx_start &= 0x1f;
+ s->rx_len --;
+ }
+ s->intreq &= ~INT_RXFIFO_REQ;
+ pxa2xx_mmci_fifo_update(s);
+ return ret;
+ case MMC_RDWAIT:
+ return 0;
+ case MMC_BLKS_REM:
+ return s->numblk;
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_mmci_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+
+ switch (offset) {
+ case MMC_STRPCL:
+ if (value & STRPCL_STRT_CLK) {
+ s->status |= STAT_CLK_EN;
+ s->intreq &= ~INT_CLK_OFF;
+
+ if (s->cmdreq && !(s->cmdat & CMDAT_STOP_TRAN)) {
+ s->status &= STAT_CLK_EN;
+ pxa2xx_mmci_wakequeues(s);
+ }
+ }
+
+ if (value & STRPCL_STOP_CLK) {
+ s->status &= ~STAT_CLK_EN;
+ s->intreq |= INT_CLK_OFF;
+ s->active = 0;
+ }
+
+ pxa2xx_mmci_int_update(s);
+ break;
+
+ case MMC_CLKRT:
+ s->clkrt = value & 7;
+ break;
+
+ case MMC_SPI:
+ s->spi = value & 0xf;
+ if (value & SPI_SPI_MODE)
+ printf("%s: attempted to use card in SPI mode\n", __FUNCTION__);
+ break;
+
+ case MMC_CMDAT:
+ s->cmdat = value & 0x3dff;
+ s->active = 0;
+ s->cmdreq = 1;
+ if (!(value & CMDAT_STOP_TRAN)) {
+ s->status &= STAT_CLK_EN;
+
+ if (s->status & STAT_CLK_EN)
+ pxa2xx_mmci_wakequeues(s);
+ }
+
+ pxa2xx_mmci_int_update(s);
+ break;
+
+ case MMC_RESTO:
+ s->resp_tout = value & 0x7f;
+ break;
+
+ case MMC_RDTO:
+ s->read_tout = value & 0xffff;
+ break;
+
+ case MMC_BLKLEN:
+ s->blklen = value & 0xfff;
+ break;
+
+ case MMC_NUMBLK:
+ s->numblk = value & 0xffff;
+ break;
+
+ case MMC_PRTBUF:
+ if (value & PRTBUF_PRT_BUF) {
+ s->tx_start ^= 32;
+ s->tx_len = 0;
+ }
+ pxa2xx_mmci_fifo_update(s);
+ break;
+
+ case MMC_I_MASK:
+ s->intmask = value & 0x1fff;
+ pxa2xx_mmci_int_update(s);
+ break;
+
+ case MMC_CMD:
+ s->cmd = value & 0x3f;
+ break;
+
+ case MMC_ARGH:
+ s->arg &= 0x0000ffff;
+ s->arg |= value << 16;
+ break;
+
+ case MMC_ARGL:
+ s->arg &= 0xffff0000;
+ s->arg |= value & 0x0000ffff;
+ break;
+
+ case MMC_TXFIFO:
+ while (s->ac_width -- && s->tx_len < 0x20)
+ s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] =
+ (value >> (s->ac_width << 3)) & 0xff;
+ s->intreq &= ~INT_TXFIFO_REQ;
+ pxa2xx_mmci_fifo_update(s);
+ break;
+
+ case MMC_RDWAIT:
+ case MMC_BLKS_REM:
+ break;
+
+ default:
+ hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
+ }
+}
+
+static uint32_t pxa2xx_mmci_readb(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ s->ac_width = 1;
+ return pxa2xx_mmci_read(opaque, offset);
+}
+
+static uint32_t pxa2xx_mmci_readh(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ s->ac_width = 2;
+ return pxa2xx_mmci_read(opaque, offset);
+}
+
+static uint32_t pxa2xx_mmci_readw(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ s->ac_width = 4;
+ return pxa2xx_mmci_read(opaque, offset);
+}
+
+static CPUReadMemoryFunc * const pxa2xx_mmci_readfn[] = {
+ pxa2xx_mmci_readb,
+ pxa2xx_mmci_readh,
+ pxa2xx_mmci_readw
+};
+
+static void pxa2xx_mmci_writeb(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ s->ac_width = 1;
+ pxa2xx_mmci_write(opaque, offset, value);
+}
+
+static void pxa2xx_mmci_writeh(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ s->ac_width = 2;
+ pxa2xx_mmci_write(opaque, offset, value);
+}
+
+static void pxa2xx_mmci_writew(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ s->ac_width = 4;
+ pxa2xx_mmci_write(opaque, offset, value);
+}
+
+static CPUWriteMemoryFunc * const pxa2xx_mmci_writefn[] = {
+ pxa2xx_mmci_writeb,
+ pxa2xx_mmci_writeh,
+ pxa2xx_mmci_writew
+};
+
+static void pxa2xx_mmci_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ int i;
+
+ qemu_put_be32s(f, &s->status);
+ qemu_put_be32s(f, &s->clkrt);
+ qemu_put_be32s(f, &s->spi);
+ qemu_put_be32s(f, &s->cmdat);
+ qemu_put_be32s(f, &s->resp_tout);
+ qemu_put_be32s(f, &s->read_tout);
+ qemu_put_be32(f, s->blklen);
+ qemu_put_be32(f, s->numblk);
+ qemu_put_be32s(f, &s->intmask);
+ qemu_put_be32s(f, &s->intreq);
+ qemu_put_be32(f, s->cmd);
+ qemu_put_be32s(f, &s->arg);
+ qemu_put_be32(f, s->cmdreq);
+ qemu_put_be32(f, s->active);
+ qemu_put_be32(f, s->bytesleft);
+
+ qemu_put_byte(f, s->tx_len);
+ for (i = 0; i < s->tx_len; i ++)
+ qemu_put_byte(f, s->tx_fifo[(s->tx_start + i) & 63]);
+
+ qemu_put_byte(f, s->rx_len);
+ for (i = 0; i < s->rx_len; i ++)
+ qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 31]);
+
+ qemu_put_byte(f, s->resp_len);
+ for (i = s->resp_len; i < 9; i ++)
+ qemu_put_be16s(f, &s->resp_fifo[i]);
+}
+
+static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque;
+ int i;
+
+ qemu_get_be32s(f, &s->status);
+ qemu_get_be32s(f, &s->clkrt);
+ qemu_get_be32s(f, &s->spi);
+ qemu_get_be32s(f, &s->cmdat);
+ qemu_get_be32s(f, &s->resp_tout);
+ qemu_get_be32s(f, &s->read_tout);
+ s->blklen = qemu_get_be32(f);
+ s->numblk = qemu_get_be32(f);
+ qemu_get_be32s(f, &s->intmask);
+ qemu_get_be32s(f, &s->intreq);
+ s->cmd = qemu_get_be32(f);
+ qemu_get_be32s(f, &s->arg);
+ s->cmdreq = qemu_get_be32(f);
+ s->active = qemu_get_be32(f);
+ s->bytesleft = qemu_get_be32(f);
+
+ s->tx_len = qemu_get_byte(f);
+ s->tx_start = 0;
+ if (s->tx_len >= sizeof(s->tx_fifo) || s->tx_len < 0)
+ return -EINVAL;
+ for (i = 0; i < s->tx_len; i ++)
+ s->tx_fifo[i] = qemu_get_byte(f);
+
+ s->rx_len = qemu_get_byte(f);
+ s->rx_start = 0;
+ if (s->rx_len >= sizeof(s->rx_fifo) || s->rx_len < 0)
+ return -EINVAL;
+ for (i = 0; i < s->rx_len; i ++)
+ s->rx_fifo[i] = qemu_get_byte(f);
+
+ s->resp_len = qemu_get_byte(f);
+ if (s->resp_len > 9 || s->resp_len < 0)
+ return -EINVAL;
+ for (i = s->resp_len; i < 9; i ++)
+ qemu_get_be16s(f, &s->resp_fifo[i]);
+
+ return 0;
+}
+
+PXA2xxMMCIState *pxa2xx_mmci_init(target_phys_addr_t base,
+ BlockDriverState *bd, qemu_irq irq, void *dma)
+{
+ int iomemtype;
+ PXA2xxMMCIState *s;
+
+ s = (PXA2xxMMCIState *) qemu_mallocz(sizeof(PXA2xxMMCIState));
+ s->irq = irq;
+ s->dma = dma;
+
+ iomemtype = cpu_register_io_memory(pxa2xx_mmci_readfn,
+ pxa2xx_mmci_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00100000, iomemtype);
+
+ /* Instantiate the actual storage */
+ s->card = sd_init(bd, 0);
+
+ register_savevm(NULL, "pxa2xx_mmci", 0, 0,
+ pxa2xx_mmci_save, pxa2xx_mmci_load, s);
+
+ return s;
+}
+
+void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly,
+ qemu_irq coverswitch)
+{
+ sd_set_cb(s->card, readonly, coverswitch);
+}
diff --git a/hw/pxa2xx_pcmcia.c b/hw/pxa2xx_pcmcia.c
new file mode 100644
index 0000000..50d4649
--- /dev/null
+++ b/hw/pxa2xx_pcmcia.c
@@ -0,0 +1,215 @@
+/*
+ * Intel XScale PXA255/270 PC Card and CompactFlash Interface.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPLv2.
+ */
+
+#include "hw.h"
+#include "pcmcia.h"
+#include "pxa.h"
+
+struct PXA2xxPCMCIAState {
+ PCMCIASocket slot;
+ PCMCIACardState *card;
+
+ qemu_irq irq;
+ qemu_irq cd_irq;
+};
+
+static uint32_t pxa2xx_pcmcia_common_read(void *opaque,
+ target_phys_addr_t offset)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+
+ if (s->slot.attached) {
+ return s->card->common_read(s->card->state, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_pcmcia_common_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+
+ if (s->slot.attached) {
+ s->card->common_write(s->card->state, offset, value);
+ }
+}
+
+static uint32_t pxa2xx_pcmcia_attr_read(void *opaque,
+ target_phys_addr_t offset)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+
+ if (s->slot.attached) {
+ return s->card->attr_read(s->card->state, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_pcmcia_attr_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+
+ if (s->slot.attached) {
+ s->card->attr_write(s->card->state, offset, value);
+ }
+}
+
+static uint32_t pxa2xx_pcmcia_io_read(void *opaque,
+ target_phys_addr_t offset)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+
+ if (s->slot.attached) {
+ return s->card->io_read(s->card->state, offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_pcmcia_io_write(void *opaque,
+ target_phys_addr_t offset, uint32_t value)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+
+ if (s->slot.attached) {
+ s->card->io_write(s->card->state, offset, value);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_pcmcia_common_readfn[] = {
+ pxa2xx_pcmcia_common_read,
+ pxa2xx_pcmcia_common_read,
+ pxa2xx_pcmcia_common_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_pcmcia_common_writefn[] = {
+ pxa2xx_pcmcia_common_write,
+ pxa2xx_pcmcia_common_write,
+ pxa2xx_pcmcia_common_write,
+};
+
+static CPUReadMemoryFunc * const pxa2xx_pcmcia_attr_readfn[] = {
+ pxa2xx_pcmcia_attr_read,
+ pxa2xx_pcmcia_attr_read,
+ pxa2xx_pcmcia_attr_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_pcmcia_attr_writefn[] = {
+ pxa2xx_pcmcia_attr_write,
+ pxa2xx_pcmcia_attr_write,
+ pxa2xx_pcmcia_attr_write,
+};
+
+static CPUReadMemoryFunc * const pxa2xx_pcmcia_io_readfn[] = {
+ pxa2xx_pcmcia_io_read,
+ pxa2xx_pcmcia_io_read,
+ pxa2xx_pcmcia_io_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_pcmcia_io_writefn[] = {
+ pxa2xx_pcmcia_io_write,
+ pxa2xx_pcmcia_io_write,
+ pxa2xx_pcmcia_io_write,
+};
+
+static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+ if (!s->irq)
+ return;
+
+ qemu_set_irq(s->irq, level);
+}
+
+PXA2xxPCMCIAState *pxa2xx_pcmcia_init(target_phys_addr_t base)
+{
+ int iomemtype;
+ PXA2xxPCMCIAState *s;
+
+ s = (PXA2xxPCMCIAState *)
+ qemu_mallocz(sizeof(PXA2xxPCMCIAState));
+
+ /* Socket I/O Memory Space */
+ iomemtype = cpu_register_io_memory(pxa2xx_pcmcia_io_readfn,
+ pxa2xx_pcmcia_io_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base | 0x00000000, 0x04000000, iomemtype);
+
+ /* Then next 64 MB is reserved */
+
+ /* Socket Attribute Memory Space */
+ iomemtype = cpu_register_io_memory(pxa2xx_pcmcia_attr_readfn,
+ pxa2xx_pcmcia_attr_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base | 0x08000000, 0x04000000, iomemtype);
+
+ /* Socket Common Memory Space */
+ iomemtype = cpu_register_io_memory(pxa2xx_pcmcia_common_readfn,
+ pxa2xx_pcmcia_common_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base | 0x0c000000, 0x04000000, iomemtype);
+
+ if (base == 0x30000000)
+ s->slot.slot_string = "PXA PC Card Socket 1";
+ else
+ s->slot.slot_string = "PXA PC Card Socket 0";
+ s->slot.irq = qemu_allocate_irqs(pxa2xx_pcmcia_set_irq, s, 1)[0];
+ pcmcia_socket_register(&s->slot);
+
+ return s;
+}
+
+/* Insert a new card into a slot */
+int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+ if (s->slot.attached)
+ return -EEXIST;
+
+ if (s->cd_irq) {
+ qemu_irq_raise(s->cd_irq);
+ }
+
+ s->card = card;
+
+ s->slot.attached = 1;
+ s->card->slot = &s->slot;
+ s->card->attach(s->card->state);
+
+ return 0;
+}
+
+/* Eject card from the slot */
+int pxa2xx_pcmcia_dettach(void *opaque)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+ if (!s->slot.attached)
+ return -ENOENT;
+
+ s->card->detach(s->card->state);
+ s->card->slot = NULL;
+ s->card = NULL;
+
+ s->slot.attached = 0;
+
+ if (s->irq)
+ qemu_irq_lower(s->irq);
+ if (s->cd_irq)
+ qemu_irq_lower(s->cd_irq);
+
+ return 0;
+}
+
+/* Who to notify on card events */
+void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq)
+{
+ PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque;
+ s->irq = irq;
+ s->cd_irq = cd_irq;
+}
diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c
new file mode 100644
index 0000000..a36da23
--- /dev/null
+++ b/hw/pxa2xx_pic.c
@@ -0,0 +1,313 @@
+/*
+ * Intel XScale PXA Programmable Interrupt Controller.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+
+#define ICIP 0x00 /* Interrupt Controller IRQ Pending register */
+#define ICMR 0x04 /* Interrupt Controller Mask register */
+#define ICLR 0x08 /* Interrupt Controller Level register */
+#define ICFP 0x0c /* Interrupt Controller FIQ Pending register */
+#define ICPR 0x10 /* Interrupt Controller Pending register */
+#define ICCR 0x14 /* Interrupt Controller Control register */
+#define ICHP 0x18 /* Interrupt Controller Highest Priority register */
+#define IPR0 0x1c /* Interrupt Controller Priority register 0 */
+#define IPR31 0x98 /* Interrupt Controller Priority register 31 */
+#define ICIP2 0x9c /* Interrupt Controller IRQ Pending register 2 */
+#define ICMR2 0xa0 /* Interrupt Controller Mask register 2 */
+#define ICLR2 0xa4 /* Interrupt Controller Level register 2 */
+#define ICFP2 0xa8 /* Interrupt Controller FIQ Pending register 2 */
+#define ICPR2 0xac /* Interrupt Controller Pending register 2 */
+#define IPR32 0xb0 /* Interrupt Controller Priority register 32 */
+#define IPR39 0xcc /* Interrupt Controller Priority register 39 */
+
+#define PXA2XX_PIC_SRCS 40
+
+typedef struct {
+ CPUState *cpu_env;
+ uint32_t int_enabled[2];
+ uint32_t int_pending[2];
+ uint32_t is_fiq[2];
+ uint32_t int_idle;
+ uint32_t priority[PXA2XX_PIC_SRCS];
+} PXA2xxPICState;
+
+static void pxa2xx_pic_update(void *opaque)
+{
+ uint32_t mask[2];
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+
+ if (s->cpu_env->halted) {
+ mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle);
+ mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle);
+ if (mask[0] || mask[1])
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_EXITTB);
+ }
+
+ mask[0] = s->int_pending[0] & s->int_enabled[0];
+ mask[1] = s->int_pending[1] & s->int_enabled[1];
+
+ if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1]))
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+ else
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ);
+
+ if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1]))
+ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
+}
+
+/* Note: Here level means state of the signal on a pin, not
+ * IRQ/FIQ distinction as in PXA Developer Manual. */
+static void pxa2xx_pic_set_irq(void *opaque, int irq, int level)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+ int int_set = (irq >= 32);
+ irq &= 31;
+
+ if (level)
+ s->int_pending[int_set] |= 1 << irq;
+ else
+ s->int_pending[int_set] &= ~(1 << irq);
+
+ pxa2xx_pic_update(opaque);
+}
+
+static inline uint32_t pxa2xx_pic_highest(PXA2xxPICState *s) {
+ int i, int_set, irq;
+ uint32_t bit, mask[2];
+ uint32_t ichp = 0x003f003f; /* Both IDs invalid */
+
+ mask[0] = s->int_pending[0] & s->int_enabled[0];
+ mask[1] = s->int_pending[1] & s->int_enabled[1];
+
+ for (i = PXA2XX_PIC_SRCS - 1; i >= 0; i --) {
+ irq = s->priority[i] & 0x3f;
+ if ((s->priority[i] & (1 << 31)) && irq < PXA2XX_PIC_SRCS) {
+ /* Source peripheral ID is valid. */
+ bit = 1 << (irq & 31);
+ int_set = (irq >= 32);
+
+ if (mask[int_set] & bit & s->is_fiq[int_set]) {
+ /* FIQ asserted */
+ ichp &= 0xffff0000;
+ ichp |= (1 << 15) | irq;
+ }
+
+ if (mask[int_set] & bit & ~s->is_fiq[int_set]) {
+ /* IRQ asserted */
+ ichp &= 0x0000ffff;
+ ichp |= (1 << 31) | (irq << 16);
+ }
+ }
+ }
+
+ return ichp;
+}
+
+static uint32_t pxa2xx_pic_mem_read(void *opaque, target_phys_addr_t offset)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+
+ switch (offset) {
+ case ICIP: /* IRQ Pending register */
+ return s->int_pending[0] & ~s->is_fiq[0] & s->int_enabled[0];
+ case ICIP2: /* IRQ Pending register 2 */
+ return s->int_pending[1] & ~s->is_fiq[1] & s->int_enabled[1];
+ case ICMR: /* Mask register */
+ return s->int_enabled[0];
+ case ICMR2: /* Mask register 2 */
+ return s->int_enabled[1];
+ case ICLR: /* Level register */
+ return s->is_fiq[0];
+ case ICLR2: /* Level register 2 */
+ return s->is_fiq[1];
+ case ICCR: /* Idle mask */
+ return (s->int_idle == 0);
+ case ICFP: /* FIQ Pending register */
+ return s->int_pending[0] & s->is_fiq[0] & s->int_enabled[0];
+ case ICFP2: /* FIQ Pending register 2 */
+ return s->int_pending[1] & s->is_fiq[1] & s->int_enabled[1];
+ case ICPR: /* Pending register */
+ return s->int_pending[0];
+ case ICPR2: /* Pending register 2 */
+ return s->int_pending[1];
+ case IPR0 ... IPR31:
+ return s->priority[0 + ((offset - IPR0 ) >> 2)];
+ case IPR32 ... IPR39:
+ return s->priority[32 + ((offset - IPR32) >> 2)];
+ case ICHP: /* Highest Priority register */
+ return pxa2xx_pic_highest(s);
+ default:
+ printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset);
+ return 0;
+ }
+}
+
+static void pxa2xx_pic_mem_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+
+ switch (offset) {
+ case ICMR: /* Mask register */
+ s->int_enabled[0] = value;
+ break;
+ case ICMR2: /* Mask register 2 */
+ s->int_enabled[1] = value;
+ break;
+ case ICLR: /* Level register */
+ s->is_fiq[0] = value;
+ break;
+ case ICLR2: /* Level register 2 */
+ s->is_fiq[1] = value;
+ break;
+ case ICCR: /* Idle mask */
+ s->int_idle = (value & 1) ? 0 : ~0;
+ break;
+ case IPR0 ... IPR31:
+ s->priority[0 + ((offset - IPR0 ) >> 2)] = value & 0x8000003f;
+ break;
+ case IPR32 ... IPR39:
+ s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f;
+ break;
+ default:
+ printf("%s: Bad register offset " REG_FMT "\n", __FUNCTION__, offset);
+ return;
+ }
+ pxa2xx_pic_update(opaque);
+}
+
+/* Interrupt Controller Coprocessor Space Register Mapping */
+static const int pxa2xx_cp_reg_map[0x10] = {
+ [0x0 ... 0xf] = -1,
+ [0x0] = ICIP,
+ [0x1] = ICMR,
+ [0x2] = ICLR,
+ [0x3] = ICFP,
+ [0x4] = ICPR,
+ [0x5] = ICHP,
+ [0x6] = ICIP2,
+ [0x7] = ICMR2,
+ [0x8] = ICLR2,
+ [0x9] = ICFP2,
+ [0xa] = ICPR2,
+};
+
+static uint32_t pxa2xx_pic_cp_read(void *opaque, int op2, int reg, int crm)
+{
+ target_phys_addr_t offset;
+
+ if (pxa2xx_cp_reg_map[reg] == -1) {
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ return 0;
+ }
+
+ offset = pxa2xx_cp_reg_map[reg];
+ return pxa2xx_pic_mem_read(opaque, offset);
+}
+
+static void pxa2xx_pic_cp_write(void *opaque, int op2, int reg, int crm,
+ uint32_t value)
+{
+ target_phys_addr_t offset;
+
+ if (pxa2xx_cp_reg_map[reg] == -1) {
+ printf("%s: Bad register 0x%x\n", __FUNCTION__, reg);
+ return;
+ }
+
+ offset = pxa2xx_cp_reg_map[reg];
+ pxa2xx_pic_mem_write(opaque, offset, value);
+}
+
+static CPUReadMemoryFunc * const pxa2xx_pic_readfn[] = {
+ pxa2xx_pic_mem_read,
+ pxa2xx_pic_mem_read,
+ pxa2xx_pic_mem_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_pic_writefn[] = {
+ pxa2xx_pic_mem_write,
+ pxa2xx_pic_mem_write,
+ pxa2xx_pic_mem_write,
+};
+
+static void pxa2xx_pic_save(QEMUFile *f, void *opaque)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+ int i;
+
+ for (i = 0; i < 2; i ++)
+ qemu_put_be32s(f, &s->int_enabled[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_put_be32s(f, &s->int_pending[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_put_be32s(f, &s->is_fiq[i]);
+ qemu_put_be32s(f, &s->int_idle);
+ for (i = 0; i < PXA2XX_PIC_SRCS; i ++)
+ qemu_put_be32s(f, &s->priority[i]);
+}
+
+static int pxa2xx_pic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PXA2xxPICState *s = (PXA2xxPICState *) opaque;
+ int i;
+
+ for (i = 0; i < 2; i ++)
+ qemu_get_be32s(f, &s->int_enabled[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_get_be32s(f, &s->int_pending[i]);
+ for (i = 0; i < 2; i ++)
+ qemu_get_be32s(f, &s->is_fiq[i]);
+ qemu_get_be32s(f, &s->int_idle);
+ for (i = 0; i < PXA2XX_PIC_SRCS; i ++)
+ qemu_get_be32s(f, &s->priority[i]);
+
+ pxa2xx_pic_update(opaque);
+ return 0;
+}
+
+qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env)
+{
+ PXA2xxPICState *s;
+ int iomemtype;
+ qemu_irq *qi;
+
+ s = (PXA2xxPICState *)
+ qemu_mallocz(sizeof(PXA2xxPICState));
+ if (!s)
+ return NULL;
+
+ s->cpu_env = env;
+
+ s->int_pending[0] = 0;
+ s->int_pending[1] = 0;
+ s->int_enabled[0] = 0;
+ s->int_enabled[1] = 0;
+ s->is_fiq[0] = 0;
+ s->is_fiq[1] = 0;
+
+ qi = qemu_allocate_irqs(pxa2xx_pic_set_irq, s, PXA2XX_PIC_SRCS);
+
+ /* Enable IC memory-mapped registers access. */
+ iomemtype = cpu_register_io_memory(pxa2xx_pic_readfn,
+ pxa2xx_pic_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00100000, iomemtype);
+
+ /* Enable IC coprocessor access. */
+ cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s);
+
+ register_savevm(NULL, "pxa2xx_pic", 0, 0, pxa2xx_pic_save,
+ pxa2xx_pic_load, s);
+
+ return qi;
+}
diff --git a/hw/pxa2xx_template.h b/hw/pxa2xx_template.h
new file mode 100644
index 0000000..1cbe36c
--- /dev/null
+++ b/hw/pxa2xx_template.h
@@ -0,0 +1,435 @@
+/*
+ * Intel XScale PXA255/270 LCDC emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPLv2.
+ *
+ * Framebuffer format conversion routines.
+ */
+
+# define SKIP_PIXEL(to) to += deststep
+#if BITS == 8
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+#elif BITS == 15 || BITS == 16
+# define COPY_PIXEL(to, from) *(uint16_t *) to = from; SKIP_PIXEL(to)
+#elif BITS == 24
+# define COPY_PIXEL(to, from) \
+ *(uint16_t *) to = from; *(to + 2) = (from) >> 16; SKIP_PIXEL(to)
+#elif BITS == 32
+# define COPY_PIXEL(to, from) *(uint32_t *) to = from; SKIP_PIXEL(to)
+#else
+# error unknown bit depth
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+# define SWAP_WORDS 1
+#endif
+
+#define FN_2(x) FN(x + 1) FN(x)
+#define FN_4(x) FN_2(x + 2) FN_2(x)
+
+static void glue(pxa2xx_draw_line2_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]);
+#ifdef SWAP_WORDS
+ FN_4(12)
+ FN_4(8)
+ FN_4(4)
+ FN_4(0)
+#else
+ FN_4(0)
+ FN_4(4)
+ FN_4(8)
+ FN_4(12)
+#endif
+#undef FN
+ width -= 16;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line4_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]);
+#ifdef SWAP_WORDS
+ FN_2(6)
+ FN_2(4)
+ FN_2(2)
+ FN_2(0)
+#else
+ FN_2(0)
+ FN_2(2)
+ FN_2(4)
+ FN_2(6)
+#endif
+#undef FN
+ width -= 8;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line8_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+ FN(24)
+ FN(16)
+ FN(8)
+ FN(0)
+#else
+ FN(0)
+ FN(8)
+ FN(16)
+ FN(24)
+#endif
+#undef FN
+ width -= 4;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line16_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line16t_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x1f) << 3;
+ data >>= 5;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data >>= 1;
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x1f) << 3;
+ data >>= 5;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line18_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x3f) << 2;
+ data >>= 6;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x3f) << 2;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+/* The wicked packed format */
+static void glue(pxa2xx_draw_line18p_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data[3];
+ unsigned int r, g, b;
+ while (width > 0) {
+ data[0] = *(uint32_t *) src;
+ src += 4;
+ data[1] = *(uint32_t *) src;
+ src += 4;
+ data[2] = *(uint32_t *) src;
+ src += 4;
+#ifdef SWAP_WORDS
+ data[0] = bswap32(data[0]);
+ data[1] = bswap32(data[1]);
+ data[2] = bswap32(data[2]);
+#endif
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ r = (data[0] & 0x3f) << 2;
+ data[0] >>= 12;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = ((data[1] & 0xf) << 4) | (data[0] << 2);
+ data[1] >>= 4;
+ r = (data[1] & 0x3f) << 2;
+ data[1] >>= 12;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ g = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ r = ((data[2] & 0x3) << 6) | (data[1] << 2);
+ data[2] >>= 8;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ g = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ r = data[2] << 2;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line19_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x3f) << 2;
+ data >>= 6;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x3f) << 2;
+ data >>= 6;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+/* The wicked packed format */
+static void glue(pxa2xx_draw_line19p_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data[3];
+ unsigned int r, g, b;
+ while (width > 0) {
+ data[0] = *(uint32_t *) src;
+ src += 4;
+ data[1] = *(uint32_t *) src;
+ src += 4;
+ data[2] = *(uint32_t *) src;
+ src += 4;
+# ifdef SWAP_WORDS
+ data[0] = bswap32(data[0]);
+ data[1] = bswap32(data[1]);
+ data[2] = bswap32(data[2]);
+# endif
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ r = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ if (data[0] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data[0] >>= 6;
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = ((data[1] & 0xf) << 4) | (data[0] << 2);
+ data[1] >>= 4;
+ r = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ if (data[1] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data[1] >>= 6;
+ b = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ g = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ r = ((data[2] & 0x3) << 6) | (data[1] << 2);
+ data[2] >>= 2;
+ if (data[2] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data[2] >>= 6;
+ b = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ g = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ r = data[2] << 2;
+ data[2] >>= 6;
+ if (data[2] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line24_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = data & 0xff;
+ data >>= 8;
+ g = data & 0xff;
+ data >>= 8;
+ r = data & 0xff;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line24t_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x7f) << 1;
+ data >>= 7;
+ g = data & 0xff;
+ data >>= 8;
+ r = data & 0xff;
+ data >>= 8;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line25_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = data & 0xff;
+ data >>= 8;
+ g = data & 0xff;
+ data >>= 8;
+ r = data & 0xff;
+ data >>= 8;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+/* Overlay planes disabled, no transparency */
+static drawfn glue(pxa2xx_draw_fn_, BITS)[16] =
+{
+ [0 ... 0xf] = NULL,
+ [pxa_lcdc_2bpp] = glue(pxa2xx_draw_line2_, BITS),
+ [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS),
+ [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS),
+ [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16_, BITS),
+ [pxa_lcdc_18bpp] = glue(pxa2xx_draw_line18_, BITS),
+ [pxa_lcdc_18pbpp] = glue(pxa2xx_draw_line18p_, BITS),
+ [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24_, BITS),
+};
+
+/* Overlay planes enabled, transparency used */
+static drawfn glue(glue(pxa2xx_draw_fn_, BITS), t)[16] =
+{
+ [0 ... 0xf] = NULL,
+ [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS),
+ [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS),
+ [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16t_, BITS),
+ [pxa_lcdc_19bpp] = glue(pxa2xx_draw_line19_, BITS),
+ [pxa_lcdc_19pbpp] = glue(pxa2xx_draw_line19p_, BITS),
+ [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24t_, BITS),
+ [pxa_lcdc_25bpp] = glue(pxa2xx_draw_line25_, BITS),
+};
+
+#undef BITS
+#undef COPY_PIXEL
+#undef SKIP_PIXEL
+
+#ifdef SWAP_WORDS
+# undef SWAP_WORDS
+#endif
diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c
new file mode 100644
index 0000000..b556d11
--- /dev/null
+++ b/hw/pxa2xx_timer.c
@@ -0,0 +1,490 @@
+/*
+ * Intel XScale PXA255/270 OS Timers.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Copyright (c) 2006 Thorsten Zitterell
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "pxa.h"
+
+#define OSMR0 0x00
+#define OSMR1 0x04
+#define OSMR2 0x08
+#define OSMR3 0x0c
+#define OSMR4 0x80
+#define OSMR5 0x84
+#define OSMR6 0x88
+#define OSMR7 0x8c
+#define OSMR8 0x90
+#define OSMR9 0x94
+#define OSMR10 0x98
+#define OSMR11 0x9c
+#define OSCR 0x10 /* OS Timer Count */
+#define OSCR4 0x40
+#define OSCR5 0x44
+#define OSCR6 0x48
+#define OSCR7 0x4c
+#define OSCR8 0x50
+#define OSCR9 0x54
+#define OSCR10 0x58
+#define OSCR11 0x5c
+#define OSSR 0x14 /* Timer status register */
+#define OWER 0x18
+#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */
+#define OMCR4 0xc0 /* OS Match Control registers */
+#define OMCR5 0xc4
+#define OMCR6 0xc8
+#define OMCR7 0xcc
+#define OMCR8 0xd0
+#define OMCR9 0xd4
+#define OMCR10 0xd8
+#define OMCR11 0xdc
+#define OSNR 0x20
+
+#define PXA25X_FREQ 3686400 /* 3.6864 MHz */
+#define PXA27X_FREQ 3250000 /* 3.25 MHz */
+
+static int pxa2xx_timer4_freq[8] = {
+ [0] = 0,
+ [1] = 32768,
+ [2] = 1000,
+ [3] = 1,
+ [4] = 1000000,
+ /* [5] is the "Externally supplied clock". Assign if necessary. */
+ [5 ... 7] = 0,
+};
+
+typedef struct {
+ uint32_t value;
+ int level;
+ qemu_irq irq;
+ QEMUTimer *qtimer;
+ int num;
+ void *info;
+} PXA2xxTimer0;
+
+typedef struct {
+ PXA2xxTimer0 tm;
+ int32_t oldclock;
+ int32_t clock;
+ uint64_t lastload;
+ uint32_t freq;
+ uint32_t control;
+} PXA2xxTimer4;
+
+typedef struct {
+ int32_t clock;
+ int32_t oldclock;
+ uint64_t lastload;
+ uint32_t freq;
+ PXA2xxTimer0 timer[4];
+ PXA2xxTimer4 *tm4;
+ uint32_t events;
+ uint32_t irq_enabled;
+ uint32_t reset3;
+ uint32_t snapshot;
+} pxa2xx_timer_info;
+
+static void pxa2xx_timer_update(void *opaque, uint64_t now_qemu)
+{
+ pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque;
+ int i;
+ uint32_t now_vm;
+ uint64_t new_qemu;
+
+ now_vm = s->clock +
+ muldiv64(now_qemu - s->lastload, s->freq, get_ticks_per_sec());
+
+ for (i = 0; i < 4; i ++) {
+ new_qemu = now_qemu + muldiv64((uint32_t) (s->timer[i].value - now_vm),
+ get_ticks_per_sec(), s->freq);
+ qemu_mod_timer(s->timer[i].qtimer, new_qemu);
+ }
+}
+
+static void pxa2xx_timer_update4(void *opaque, uint64_t now_qemu, int n)
+{
+ pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque;
+ uint32_t now_vm;
+ uint64_t new_qemu;
+ static const int counters[8] = { 0, 0, 0, 0, 4, 4, 6, 6 };
+ int counter;
+
+ if (s->tm4[n].control & (1 << 7))
+ counter = n;
+ else
+ counter = counters[n];
+
+ if (!s->tm4[counter].freq) {
+ qemu_del_timer(s->tm4[n].tm.qtimer);
+ return;
+ }
+
+ now_vm = s->tm4[counter].clock + muldiv64(now_qemu -
+ s->tm4[counter].lastload,
+ s->tm4[counter].freq, get_ticks_per_sec());
+
+ new_qemu = now_qemu + muldiv64((uint32_t) (s->tm4[n].tm.value - now_vm),
+ get_ticks_per_sec(), s->tm4[counter].freq);
+ qemu_mod_timer(s->tm4[n].tm.qtimer, new_qemu);
+}
+
+static uint32_t pxa2xx_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque;
+ int tm = 0;
+
+ switch (offset) {
+ case OSMR3: tm ++;
+ case OSMR2: tm ++;
+ case OSMR1: tm ++;
+ case OSMR0:
+ return s->timer[tm].value;
+ case OSMR11: tm ++;
+ case OSMR10: tm ++;
+ case OSMR9: tm ++;
+ case OSMR8: tm ++;
+ case OSMR7: tm ++;
+ case OSMR6: tm ++;
+ case OSMR5: tm ++;
+ case OSMR4:
+ if (!s->tm4)
+ goto badreg;
+ return s->tm4[tm].tm.value;
+ case OSCR:
+ return s->clock + muldiv64(qemu_get_clock(vm_clock) -
+ s->lastload, s->freq, get_ticks_per_sec());
+ case OSCR11: tm ++;
+ case OSCR10: tm ++;
+ case OSCR9: tm ++;
+ case OSCR8: tm ++;
+ case OSCR7: tm ++;
+ case OSCR6: tm ++;
+ case OSCR5: tm ++;
+ case OSCR4:
+ if (!s->tm4)
+ goto badreg;
+
+ if ((tm == 9 - 4 || tm == 11 - 4) && (s->tm4[tm].control & (1 << 9))) {
+ if (s->tm4[tm - 1].freq)
+ s->snapshot = s->tm4[tm - 1].clock + muldiv64(
+ qemu_get_clock(vm_clock) -
+ s->tm4[tm - 1].lastload,
+ s->tm4[tm - 1].freq, get_ticks_per_sec());
+ else
+ s->snapshot = s->tm4[tm - 1].clock;
+ }
+
+ if (!s->tm4[tm].freq)
+ return s->tm4[tm].clock;
+ return s->tm4[tm].clock + muldiv64(qemu_get_clock(vm_clock) -
+ s->tm4[tm].lastload, s->tm4[tm].freq, get_ticks_per_sec());
+ case OIER:
+ return s->irq_enabled;
+ case OSSR: /* Status register */
+ return s->events;
+ case OWER:
+ return s->reset3;
+ case OMCR11: tm ++;
+ case OMCR10: tm ++;
+ case OMCR9: tm ++;
+ case OMCR8: tm ++;
+ case OMCR7: tm ++;
+ case OMCR6: tm ++;
+ case OMCR5: tm ++;
+ case OMCR4:
+ if (!s->tm4)
+ goto badreg;
+ return s->tm4[tm].control;
+ case OSNR:
+ return s->snapshot;
+ default:
+ badreg:
+ hw_error("pxa2xx_timer_read: Bad offset " REG_FMT "\n", offset);
+ }
+
+ return 0;
+}
+
+static void pxa2xx_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ int i, tm = 0;
+ pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque;
+
+ switch (offset) {
+ case OSMR3: tm ++;
+ case OSMR2: tm ++;
+ case OSMR1: tm ++;
+ case OSMR0:
+ s->timer[tm].value = value;
+ pxa2xx_timer_update(s, qemu_get_clock(vm_clock));
+ break;
+ case OSMR11: tm ++;
+ case OSMR10: tm ++;
+ case OSMR9: tm ++;
+ case OSMR8: tm ++;
+ case OSMR7: tm ++;
+ case OSMR6: tm ++;
+ case OSMR5: tm ++;
+ case OSMR4:
+ if (!s->tm4)
+ goto badreg;
+ s->tm4[tm].tm.value = value;
+ pxa2xx_timer_update4(s, qemu_get_clock(vm_clock), tm);
+ break;
+ case OSCR:
+ s->oldclock = s->clock;
+ s->lastload = qemu_get_clock(vm_clock);
+ s->clock = value;
+ pxa2xx_timer_update(s, s->lastload);
+ break;
+ case OSCR11: tm ++;
+ case OSCR10: tm ++;
+ case OSCR9: tm ++;
+ case OSCR8: tm ++;
+ case OSCR7: tm ++;
+ case OSCR6: tm ++;
+ case OSCR5: tm ++;
+ case OSCR4:
+ if (!s->tm4)
+ goto badreg;
+ s->tm4[tm].oldclock = s->tm4[tm].clock;
+ s->tm4[tm].lastload = qemu_get_clock(vm_clock);
+ s->tm4[tm].clock = value;
+ pxa2xx_timer_update4(s, s->tm4[tm].lastload, tm);
+ break;
+ case OIER:
+ s->irq_enabled = value & 0xfff;
+ break;
+ case OSSR: /* Status register */
+ s->events &= ~value;
+ for (i = 0; i < 4; i ++, value >>= 1) {
+ if (s->timer[i].level && (value & 1)) {
+ s->timer[i].level = 0;
+ qemu_irq_lower(s->timer[i].irq);
+ }
+ }
+ if (s->tm4) {
+ for (i = 0; i < 8; i ++, value >>= 1)
+ if (s->tm4[i].tm.level && (value & 1))
+ s->tm4[i].tm.level = 0;
+ if (!(s->events & 0xff0))
+ qemu_irq_lower(s->tm4->tm.irq);
+ }
+ break;
+ case OWER: /* XXX: Reset on OSMR3 match? */
+ s->reset3 = value;
+ break;
+ case OMCR7: tm ++;
+ case OMCR6: tm ++;
+ case OMCR5: tm ++;
+ case OMCR4:
+ if (!s->tm4)
+ goto badreg;
+ s->tm4[tm].control = value & 0x0ff;
+ /* XXX Stop if running (shouldn't happen) */
+ if ((value & (1 << 7)) || tm == 0)
+ s->tm4[tm].freq = pxa2xx_timer4_freq[value & 7];
+ else {
+ s->tm4[tm].freq = 0;
+ pxa2xx_timer_update4(s, qemu_get_clock(vm_clock), tm);
+ }
+ break;
+ case OMCR11: tm ++;
+ case OMCR10: tm ++;
+ case OMCR9: tm ++;
+ case OMCR8: tm += 4;
+ if (!s->tm4)
+ goto badreg;
+ s->tm4[tm].control = value & 0x3ff;
+ /* XXX Stop if running (shouldn't happen) */
+ if ((value & (1 << 7)) || !(tm & 1))
+ s->tm4[tm].freq =
+ pxa2xx_timer4_freq[(value & (1 << 8)) ? 0 : (value & 7)];
+ else {
+ s->tm4[tm].freq = 0;
+ pxa2xx_timer_update4(s, qemu_get_clock(vm_clock), tm);
+ }
+ break;
+ default:
+ badreg:
+ hw_error("pxa2xx_timer_write: Bad offset " REG_FMT "\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc * const pxa2xx_timer_readfn[] = {
+ pxa2xx_timer_read,
+ pxa2xx_timer_read,
+ pxa2xx_timer_read,
+};
+
+static CPUWriteMemoryFunc * const pxa2xx_timer_writefn[] = {
+ pxa2xx_timer_write,
+ pxa2xx_timer_write,
+ pxa2xx_timer_write,
+};
+
+static void pxa2xx_timer_tick(void *opaque)
+{
+ PXA2xxTimer0 *t = (PXA2xxTimer0 *) opaque;
+ pxa2xx_timer_info *i = (pxa2xx_timer_info *) t->info;
+
+ if (i->irq_enabled & (1 << t->num)) {
+ t->level = 1;
+ i->events |= 1 << t->num;
+ qemu_irq_raise(t->irq);
+ }
+
+ if (t->num == 3)
+ if (i->reset3 & 1) {
+ i->reset3 = 0;
+ qemu_system_reset_request();
+ }
+}
+
+static void pxa2xx_timer_tick4(void *opaque)
+{
+ PXA2xxTimer4 *t = (PXA2xxTimer4 *) opaque;
+ pxa2xx_timer_info *i = (pxa2xx_timer_info *) t->tm.info;
+
+ pxa2xx_timer_tick(&t->tm);
+ if (t->control & (1 << 3))
+ t->clock = 0;
+ if (t->control & (1 << 6))
+ pxa2xx_timer_update4(i, qemu_get_clock(vm_clock), t->tm.num - 4);
+}
+
+static void pxa2xx_timer_save(QEMUFile *f, void *opaque)
+{
+ pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque;
+ int i;
+
+ qemu_put_be32s(f, (uint32_t *) &s->clock);
+ qemu_put_be32s(f, (uint32_t *) &s->oldclock);
+ qemu_put_be64s(f, &s->lastload);
+
+ for (i = 0; i < 4; i ++) {
+ qemu_put_be32s(f, &s->timer[i].value);
+ qemu_put_be32(f, s->timer[i].level);
+ }
+ if (s->tm4)
+ for (i = 0; i < 8; i ++) {
+ qemu_put_be32s(f, &s->tm4[i].tm.value);
+ qemu_put_be32(f, s->tm4[i].tm.level);
+ qemu_put_sbe32s(f, &s->tm4[i].oldclock);
+ qemu_put_sbe32s(f, &s->tm4[i].clock);
+ qemu_put_be64s(f, &s->tm4[i].lastload);
+ qemu_put_be32s(f, &s->tm4[i].freq);
+ qemu_put_be32s(f, &s->tm4[i].control);
+ }
+
+ qemu_put_be32s(f, &s->events);
+ qemu_put_be32s(f, &s->irq_enabled);
+ qemu_put_be32s(f, &s->reset3);
+ qemu_put_be32s(f, &s->snapshot);
+}
+
+static int pxa2xx_timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+ pxa2xx_timer_info *s = (pxa2xx_timer_info *) opaque;
+ int64_t now;
+ int i;
+
+ qemu_get_be32s(f, (uint32_t *) &s->clock);
+ qemu_get_be32s(f, (uint32_t *) &s->oldclock);
+ qemu_get_be64s(f, &s->lastload);
+
+ now = qemu_get_clock(vm_clock);
+ for (i = 0; i < 4; i ++) {
+ qemu_get_be32s(f, &s->timer[i].value);
+ s->timer[i].level = qemu_get_be32(f);
+ }
+ pxa2xx_timer_update(s, now);
+
+ if (s->tm4)
+ for (i = 0; i < 8; i ++) {
+ qemu_get_be32s(f, &s->tm4[i].tm.value);
+ s->tm4[i].tm.level = qemu_get_be32(f);
+ qemu_get_sbe32s(f, &s->tm4[i].oldclock);
+ qemu_get_sbe32s(f, &s->tm4[i].clock);
+ qemu_get_be64s(f, &s->tm4[i].lastload);
+ qemu_get_be32s(f, &s->tm4[i].freq);
+ qemu_get_be32s(f, &s->tm4[i].control);
+ pxa2xx_timer_update4(s, now, i);
+ }
+
+ qemu_get_be32s(f, &s->events);
+ qemu_get_be32s(f, &s->irq_enabled);
+ qemu_get_be32s(f, &s->reset3);
+ qemu_get_be32s(f, &s->snapshot);
+
+ return 0;
+}
+
+static pxa2xx_timer_info *pxa2xx_timer_init(target_phys_addr_t base,
+ qemu_irq *irqs)
+{
+ int i;
+ int iomemtype;
+ pxa2xx_timer_info *s;
+
+ s = (pxa2xx_timer_info *) qemu_mallocz(sizeof(pxa2xx_timer_info));
+ s->irq_enabled = 0;
+ s->oldclock = 0;
+ s->clock = 0;
+ s->lastload = qemu_get_clock(vm_clock);
+ s->reset3 = 0;
+
+ for (i = 0; i < 4; i ++) {
+ s->timer[i].value = 0;
+ s->timer[i].irq = irqs[i];
+ s->timer[i].info = s;
+ s->timer[i].num = i;
+ s->timer[i].level = 0;
+ s->timer[i].qtimer = qemu_new_timer(vm_clock,
+ pxa2xx_timer_tick, &s->timer[i]);
+ }
+
+ iomemtype = cpu_register_io_memory(pxa2xx_timer_readfn,
+ pxa2xx_timer_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+
+ register_savevm(NULL, "pxa2xx_timer", 0, 0,
+ pxa2xx_timer_save, pxa2xx_timer_load, s);
+
+ return s;
+}
+
+void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs)
+{
+ pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs);
+ s->freq = PXA25X_FREQ;
+ s->tm4 = NULL;
+}
+
+void pxa27x_timer_init(target_phys_addr_t base,
+ qemu_irq *irqs, qemu_irq irq4)
+{
+ pxa2xx_timer_info *s = pxa2xx_timer_init(base, irqs);
+ int i;
+ s->freq = PXA27X_FREQ;
+ s->tm4 = (PXA2xxTimer4 *) qemu_mallocz(8 *
+ sizeof(PXA2xxTimer4));
+ for (i = 0; i < 8; i ++) {
+ s->tm4[i].tm.value = 0;
+ s->tm4[i].tm.irq = irq4;
+ s->tm4[i].tm.info = s;
+ s->tm4[i].tm.num = i + 4;
+ s->tm4[i].tm.level = 0;
+ s->tm4[i].freq = 0;
+ s->tm4[i].control = 0x0;
+ s->tm4[i].tm.qtimer = qemu_new_timer(vm_clock,
+ pxa2xx_timer_tick4, &s->tm4[i]);
+ }
+}
diff --git a/hw/qdev-addr.c b/hw/qdev-addr.c
new file mode 100644
index 0000000..305c2d3
--- /dev/null
+++ b/hw/qdev-addr.c
@@ -0,0 +1,32 @@
+#include "qdev.h"
+#include "qdev-addr.h"
+#include "targphys.h"
+
+/* --- target physical address --- */
+
+static int parse_taddr(DeviceState *dev, Property *prop, const char *str)
+{
+ target_phys_addr_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ *ptr = strtoull(str, NULL, 16);
+ return 0;
+}
+
+static int print_taddr(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ target_phys_addr_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x" TARGET_FMT_plx, *ptr);
+}
+
+PropertyInfo qdev_prop_taddr = {
+ .name = "taddr",
+ .type = PROP_TYPE_TADDR,
+ .size = sizeof(target_phys_addr_t),
+ .parse = parse_taddr,
+ .print = print_taddr,
+};
+
+void qdev_prop_set_taddr(DeviceState *dev, const char *name, target_phys_addr_t value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_TADDR);
+}
diff --git a/hw/qdev-addr.h b/hw/qdev-addr.h
new file mode 100644
index 0000000..a0ddf38
--- /dev/null
+++ b/hw/qdev-addr.h
@@ -0,0 +1,5 @@
+#define DEFINE_PROP_TADDR(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_taddr, target_phys_addr_t)
+
+extern PropertyInfo qdev_prop_taddr;
+void qdev_prop_set_taddr(DeviceState *dev, const char *name, target_phys_addr_t value);
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
new file mode 100644
index 0000000..a493087
--- /dev/null
+++ b/hw/qdev-properties.c
@@ -0,0 +1,777 @@
+#include "net.h"
+#include "qdev.h"
+#include "qerror.h"
+#include "blockdev.h"
+
+void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
+{
+ void *ptr = dev;
+ ptr += prop->offset;
+ return ptr;
+}
+
+static uint32_t qdev_get_prop_mask(Property *prop)
+{
+ assert(prop->info->type == PROP_TYPE_BIT);
+ return 0x1 << prop->bitnr;
+}
+
+static void bit_prop_set(DeviceState *dev, Property *props, bool val)
+{
+ uint32_t *p = qdev_get_prop_ptr(dev, props);
+ uint32_t mask = qdev_get_prop_mask(props);
+ if (val)
+ *p |= mask;
+ else
+ *p &= ~mask;
+}
+
+static void qdev_prop_cpy(DeviceState *dev, Property *props, void *src)
+{
+ if (props->info->type == PROP_TYPE_BIT) {
+ bool *defval = src;
+ bit_prop_set(dev, props, *defval);
+ } else {
+ char *dst = qdev_get_prop_ptr(dev, props);
+ memcpy(dst, src, props->info->size);
+ }
+}
+
+/* Bit */
+static int parse_bit(DeviceState *dev, Property *prop, const char *str)
+{
+ if (!strncasecmp(str, "on", 2))
+ bit_prop_set(dev, prop, true);
+ else if (!strncasecmp(str, "off", 3))
+ bit_prop_set(dev, prop, false);
+ else
+ return -EINVAL;
+ return 0;
+}
+
+static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint8_t *p = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off");
+}
+
+PropertyInfo qdev_prop_bit = {
+ .name = "on/off",
+ .type = PROP_TYPE_BIT,
+ .size = sizeof(uint32_t),
+ .parse = parse_bit,
+ .print = print_bit,
+};
+
+/* --- 8bit integer --- */
+
+static int parse_uint8(DeviceState *dev, Property *prop, const char *str)
+{
+ uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ /* accept both hex and decimal */
+ *ptr = strtoul(str, &end, 0);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_uint8(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "%" PRIu8, *ptr);
+}
+
+PropertyInfo qdev_prop_uint8 = {
+ .name = "uint8",
+ .type = PROP_TYPE_UINT8,
+ .size = sizeof(uint8_t),
+ .parse = parse_uint8,
+ .print = print_uint8,
+};
+
+/* --- 16bit integer --- */
+
+static int parse_uint16(DeviceState *dev, Property *prop, const char *str)
+{
+ uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ /* accept both hex and decimal */
+ *ptr = strtoul(str, &end, 0);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_uint16(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "%" PRIu16, *ptr);
+}
+
+PropertyInfo qdev_prop_uint16 = {
+ .name = "uint16",
+ .type = PROP_TYPE_UINT16,
+ .size = sizeof(uint16_t),
+ .parse = parse_uint16,
+ .print = print_uint16,
+};
+
+/* --- 32bit integer --- */
+
+static int parse_uint32(DeviceState *dev, Property *prop, const char *str)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ /* accept both hex and decimal */
+ *ptr = strtoul(str, &end, 0);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_uint32(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "%" PRIu32, *ptr);
+}
+
+PropertyInfo qdev_prop_uint32 = {
+ .name = "uint32",
+ .type = PROP_TYPE_UINT32,
+ .size = sizeof(uint32_t),
+ .parse = parse_uint32,
+ .print = print_uint32,
+};
+
+static int parse_int32(DeviceState *dev, Property *prop, const char *str)
+{
+ int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ *ptr = strtol(str, &end, 10);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_int32(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "%" PRId32, *ptr);
+}
+
+PropertyInfo qdev_prop_int32 = {
+ .name = "int32",
+ .type = PROP_TYPE_INT32,
+ .size = sizeof(int32_t),
+ .parse = parse_int32,
+ .print = print_int32,
+};
+
+/* --- 32bit hex value --- */
+
+static int parse_hex32(DeviceState *dev, Property *prop, const char *str)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ *ptr = strtoul(str, &end, 16);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_hex32(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x%" PRIx32, *ptr);
+}
+
+PropertyInfo qdev_prop_hex32 = {
+ .name = "hex32",
+ .type = PROP_TYPE_UINT32,
+ .size = sizeof(uint32_t),
+ .parse = parse_hex32,
+ .print = print_hex32,
+};
+
+/* --- 64bit integer --- */
+
+static int parse_uint64(DeviceState *dev, Property *prop, const char *str)
+{
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ /* accept both hex and decimal */
+ *ptr = strtoull(str, &end, 0);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_uint64(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "%" PRIu64, *ptr);
+}
+
+PropertyInfo qdev_prop_uint64 = {
+ .name = "uint64",
+ .type = PROP_TYPE_UINT64,
+ .size = sizeof(uint64_t),
+ .parse = parse_uint64,
+ .print = print_uint64,
+};
+
+/* --- 64bit hex value --- */
+
+static int parse_hex64(DeviceState *dev, Property *prop, const char *str)
+{
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+ char *end;
+
+ *ptr = strtoull(str, &end, 16);
+ if ((*end != '\0') || (end == str)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int print_hex64(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "0x%" PRIx64, *ptr);
+}
+
+PropertyInfo qdev_prop_hex64 = {
+ .name = "hex64",
+ .type = PROP_TYPE_UINT64,
+ .size = sizeof(uint64_t),
+ .parse = parse_hex64,
+ .print = print_hex64,
+};
+
+/* --- string --- */
+
+static int parse_string(DeviceState *dev, Property *prop, const char *str)
+{
+ char **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr)
+ qemu_free(*ptr);
+ *ptr = qemu_strdup(str);
+ return 0;
+}
+
+static void free_string(DeviceState *dev, Property *prop)
+{
+ qemu_free(*(char **)qdev_get_prop_ptr(dev, prop));
+}
+
+static int print_string(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ char **ptr = qdev_get_prop_ptr(dev, prop);
+ if (!*ptr)
+ return snprintf(dest, len, "<null>");
+ return snprintf(dest, len, "\"%s\"", *ptr);
+}
+
+PropertyInfo qdev_prop_string = {
+ .name = "string",
+ .type = PROP_TYPE_STRING,
+ .size = sizeof(char*),
+ .parse = parse_string,
+ .print = print_string,
+ .free = free_string,
+};
+
+/* --- drive --- */
+
+static int parse_drive(DeviceState *dev, Property *prop, const char *str)
+{
+ BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+ BlockDriverState *bs;
+
+ bs = bdrv_find(str);
+ if (bs == NULL)
+ return -ENOENT;
+ if (bdrv_attach(bs, dev) < 0)
+ return -EEXIST;
+ *ptr = bs;
+ return 0;
+}
+
+static void free_drive(DeviceState *dev, Property *prop)
+{
+ BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr) {
+ bdrv_detach(*ptr, dev);
+ blockdev_auto_del(*ptr);
+ }
+}
+
+static int print_drive(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+ return snprintf(dest, len, "%s",
+ *ptr ? bdrv_get_device_name(*ptr) : "<null>");
+}
+
+PropertyInfo qdev_prop_drive = {
+ .name = "drive",
+ .type = PROP_TYPE_DRIVE,
+ .size = sizeof(BlockDriverState *),
+ .parse = parse_drive,
+ .print = print_drive,
+ .free = free_drive,
+};
+
+/* --- character device --- */
+
+static int parse_chr(DeviceState *dev, Property *prop, const char *str)
+{
+ CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ *ptr = qemu_chr_find(str);
+ if (*ptr == NULL)
+ return -ENOENT;
+ return 0;
+}
+
+static int print_chr(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr && (*ptr)->label) {
+ return snprintf(dest, len, "%s", (*ptr)->label);
+ } else {
+ return snprintf(dest, len, "<null>");
+ }
+}
+
+PropertyInfo qdev_prop_chr = {
+ .name = "chr",
+ .type = PROP_TYPE_CHR,
+ .size = sizeof(CharDriverState*),
+ .parse = parse_chr,
+ .print = print_chr,
+};
+
+/* --- netdev device --- */
+
+static int parse_netdev(DeviceState *dev, Property *prop, const char *str)
+{
+ VLANClientState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ *ptr = qemu_find_netdev(str);
+ if (*ptr == NULL)
+ return -ENOENT;
+ if ((*ptr)->peer) {
+ return -EEXIST;
+ }
+ return 0;
+}
+
+static int print_netdev(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ VLANClientState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr && (*ptr)->name) {
+ return snprintf(dest, len, "%s", (*ptr)->name);
+ } else {
+ return snprintf(dest, len, "<null>");
+ }
+}
+
+PropertyInfo qdev_prop_netdev = {
+ .name = "netdev",
+ .type = PROP_TYPE_NETDEV,
+ .size = sizeof(VLANClientState*),
+ .parse = parse_netdev,
+ .print = print_netdev,
+};
+
+/* --- vlan --- */
+
+static int parse_vlan(DeviceState *dev, Property *prop, const char *str)
+{
+ VLANState **ptr = qdev_get_prop_ptr(dev, prop);
+ int id;
+
+ if (sscanf(str, "%d", &id) != 1)
+ return -EINVAL;
+ *ptr = qemu_find_vlan(id, 1);
+ if (*ptr == NULL)
+ return -ENOENT;
+ return 0;
+}
+
+static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ VLANState **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr) {
+ return snprintf(dest, len, "%d", (*ptr)->id);
+ } else {
+ return snprintf(dest, len, "<null>");
+ }
+}
+
+PropertyInfo qdev_prop_vlan = {
+ .name = "vlan",
+ .type = PROP_TYPE_VLAN,
+ .size = sizeof(VLANClientState*),
+ .parse = parse_vlan,
+ .print = print_vlan,
+};
+
+/* --- pointer --- */
+
+/* Not a proper property, just for dirty hacks. TODO Remove it! */
+PropertyInfo qdev_prop_ptr = {
+ .name = "ptr",
+ .type = PROP_TYPE_PTR,
+ .size = sizeof(void*),
+};
+
+/* --- mac address --- */
+
+/*
+ * accepted syntax versions:
+ * 01:02:03:04:05:06
+ * 01-02-03-04-05-06
+ */
+static int parse_mac(DeviceState *dev, Property *prop, const char *str)
+{
+ MACAddr *mac = qdev_get_prop_ptr(dev, prop);
+ int i, pos;
+ char *p;
+
+ for (i = 0, pos = 0; i < 6; i++, pos += 3) {
+ if (!qemu_isxdigit(str[pos]))
+ return -EINVAL;
+ if (!qemu_isxdigit(str[pos+1]))
+ return -EINVAL;
+ if (i == 5) {
+ if (str[pos+2] != '\0')
+ return -EINVAL;
+ } else {
+ if (str[pos+2] != ':' && str[pos+2] != '-')
+ return -EINVAL;
+ }
+ mac->a[i] = strtol(str+pos, &p, 16);
+ }
+ return 0;
+}
+
+static int print_mac(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ MACAddr *mac = qdev_get_prop_ptr(dev, prop);
+
+ return snprintf(dest, len, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac->a[0], mac->a[1], mac->a[2],
+ mac->a[3], mac->a[4], mac->a[5]);
+}
+
+PropertyInfo qdev_prop_macaddr = {
+ .name = "macaddr",
+ .type = PROP_TYPE_MACADDR,
+ .size = sizeof(MACAddr),
+ .parse = parse_mac,
+ .print = print_mac,
+};
+
+/* --- pci address --- */
+
+/*
+ * bus-local address, i.e. "$slot" or "$slot.$fn"
+ */
+static int parse_pci_devfn(DeviceState *dev, Property *prop, const char *str)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+ unsigned int slot, fn, n;
+
+ if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) {
+ fn = 0;
+ if (sscanf(str, "%x%n", &slot, &n) != 1) {
+ return -EINVAL;
+ }
+ }
+ if (str[n] != '\0')
+ return -EINVAL;
+ if (fn > 7)
+ return -EINVAL;
+ *ptr = slot << 3 | fn;
+ return 0;
+}
+
+static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+ uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr == -1) {
+ return snprintf(dest, len, "<unset>");
+ } else {
+ return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7);
+ }
+}
+
+PropertyInfo qdev_prop_pci_devfn = {
+ .name = "pci-devfn",
+ .type = PROP_TYPE_UINT32,
+ .size = sizeof(uint32_t),
+ .parse = parse_pci_devfn,
+ .print = print_pci_devfn,
+};
+
+/* --- public helpers --- */
+
+static Property *qdev_prop_walk(Property *props, const char *name)
+{
+ if (!props)
+ return NULL;
+ while (props->name) {
+ if (strcmp(props->name, name) == 0)
+ return props;
+ props++;
+ }
+ return NULL;
+}
+
+static Property *qdev_prop_find(DeviceState *dev, const char *name)
+{
+ Property *prop;
+
+ /* device properties */
+ prop = qdev_prop_walk(dev->info->props, name);
+ if (prop)
+ return prop;
+
+ /* bus properties */
+ prop = qdev_prop_walk(dev->parent_bus->info->props, name);
+ if (prop)
+ return prop;
+
+ return NULL;
+}
+
+int qdev_prop_exists(DeviceState *dev, const char *name)
+{
+ return qdev_prop_find(dev, name) ? true : false;
+}
+
+int qdev_prop_parse(DeviceState *dev, const char *name, const char *value)
+{
+ Property *prop;
+ int ret;
+
+ prop = qdev_prop_find(dev, name);
+ /*
+ * TODO Properties without a parse method are just for dirty
+ * hacks. qdev_prop_ptr is the only such PropertyInfo. It's
+ * marked for removal. The test !prop->info->parse should be
+ * removed along with it.
+ */
+ if (!prop || !prop->info->parse) {
+ qerror_report(QERR_PROPERTY_NOT_FOUND, dev->info->name, name);
+ return -1;
+ }
+ ret = prop->info->parse(dev, prop, value);
+ if (ret < 0) {
+ switch (ret) {
+ case -EEXIST:
+ qerror_report(QERR_PROPERTY_VALUE_IN_USE,
+ dev->info->name, name, value);
+ break;
+ default:
+ case -EINVAL:
+ qerror_report(QERR_PROPERTY_VALUE_BAD,
+ dev->info->name, name, value);
+ break;
+ case -ENOENT:
+ qerror_report(QERR_PROPERTY_VALUE_NOT_FOUND,
+ dev->info->name, name, value);
+ break;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+void qdev_prop_set(DeviceState *dev, const char *name, void *src, enum PropertyType type)
+{
+ Property *prop;
+
+ prop = qdev_prop_find(dev, name);
+ if (!prop) {
+ fprintf(stderr, "%s: property \"%s.%s\" not found\n",
+ __FUNCTION__, dev->info->name, name);
+ abort();
+ }
+ if (prop->info->type != type) {
+ fprintf(stderr, "%s: property \"%s.%s\" type mismatch\n",
+ __FUNCTION__, dev->info->name, name);
+ abort();
+ }
+ qdev_prop_cpy(dev, prop, src);
+}
+
+void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_BIT);
+}
+
+void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_UINT8);
+}
+
+void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_UINT16);
+}
+
+void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_UINT32);
+}
+
+void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_INT32);
+}
+
+void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_UINT64);
+}
+
+void qdev_prop_set_string(DeviceState *dev, const char *name, char *value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_STRING);
+}
+
+int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value)
+{
+ int res;
+
+ res = bdrv_attach(value, dev);
+ if (res < 0) {
+ error_report("Can't attach drive %s to %s.%s: %s",
+ bdrv_get_device_name(value),
+ dev->id ? dev->id : dev->info->name,
+ name, strerror(-res));
+ return -1;
+ }
+ qdev_prop_set(dev, name, &value, PROP_TYPE_DRIVE);
+ return 0;
+}
+
+void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value)
+{
+ if (qdev_prop_set_drive(dev, name, value) < 0) {
+ exit(1);
+ }
+}
+void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_CHR);
+}
+
+void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_NETDEV);
+}
+
+void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_VLAN);
+}
+
+void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
+{
+ qdev_prop_set(dev, name, value, PROP_TYPE_MACADDR);
+}
+
+void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value)
+{
+ qdev_prop_set(dev, name, &value, PROP_TYPE_PTR);
+}
+
+void qdev_prop_set_defaults(DeviceState *dev, Property *props)
+{
+ if (!props)
+ return;
+ while (props->name) {
+ if (props->defval) {
+ qdev_prop_cpy(dev, props, props->defval);
+ }
+ props++;
+ }
+}
+
+static QTAILQ_HEAD(, GlobalProperty) global_props = QTAILQ_HEAD_INITIALIZER(global_props);
+
+static void qdev_prop_register_global(GlobalProperty *prop)
+{
+ QTAILQ_INSERT_TAIL(&global_props, prop, next);
+}
+
+void qdev_prop_register_global_list(GlobalProperty *props)
+{
+ int i;
+
+ for (i = 0; props[i].driver != NULL; i++) {
+ qdev_prop_register_global(props+i);
+ }
+}
+
+void qdev_prop_set_globals(DeviceState *dev)
+{
+ GlobalProperty *prop;
+
+ QTAILQ_FOREACH(prop, &global_props, next) {
+ if (strcmp(dev->info->name, prop->driver) != 0 &&
+ strcmp(dev->info->bus_info->name, prop->driver) != 0) {
+ continue;
+ }
+ if (qdev_prop_parse(dev, prop->property, prop->value) != 0) {
+ exit(1);
+ }
+ }
+}
+
+static int qdev_add_one_global(QemuOpts *opts, void *opaque)
+{
+ GlobalProperty *g;
+
+ g = qemu_mallocz(sizeof(*g));
+ g->driver = qemu_opt_get(opts, "driver");
+ g->property = qemu_opt_get(opts, "property");
+ g->value = qemu_opt_get(opts, "value");
+ qdev_prop_register_global(g);
+ return 0;
+}
+
+void qemu_add_globals(void)
+{
+ qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
+}
diff --git a/hw/qdev.c b/hw/qdev.c
new file mode 100644
index 0000000..c7fec44
--- /dev/null
+++ b/hw/qdev.c
@@ -0,0 +1,926 @@
+/*
+ * Dynamic device configuration and creation.
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * 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/>.
+ */
+
+/* The theory here is that it should be possible to create a machine without
+ knowledge of specific devices. Historically board init routines have
+ passed a bunch of arguments to each device, requiring the board know
+ exactly which device it is dealing with. This file provides an abstract
+ API for device configuration and initialization. Devices will generally
+ inherit from a particular bus (e.g. PCI or I2C) rather than
+ this API directly. */
+
+#include "net.h"
+#include "qdev.h"
+#include "sysemu.h"
+#include "monitor.h"
+
+static int qdev_hotplug = 0;
+static bool qdev_hot_added = false;
+static bool qdev_hot_removed = false;
+
+/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
+static BusState *main_system_bus;
+
+DeviceInfo *device_info_list;
+
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+ const BusInfo *info);
+static BusState *qbus_find(const char *path);
+
+/* Register a new device type. */
+void qdev_register(DeviceInfo *info)
+{
+ assert(info->size >= sizeof(DeviceState));
+ assert(!info->next);
+
+ info->next = device_info_list;
+ device_info_list = info;
+}
+
+static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name)
+{
+ DeviceInfo *info;
+
+ /* first check device names */
+ for (info = device_info_list; info != NULL; info = info->next) {
+ if (bus_info && info->bus_info != bus_info)
+ continue;
+ if (strcmp(info->name, name) != 0)
+ continue;
+ return info;
+ }
+
+ /* failing that check the aliases */
+ for (info = device_info_list; info != NULL; info = info->next) {
+ if (bus_info && info->bus_info != bus_info)
+ continue;
+ if (!info->alias)
+ continue;
+ if (strcmp(info->alias, name) != 0)
+ continue;
+ return info;
+ }
+ return NULL;
+}
+
+static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
+{
+ DeviceState *dev;
+
+ assert(bus->info == info->bus_info);
+ dev = qemu_mallocz(info->size);
+ dev->info = info;
+ dev->parent_bus = bus;
+ qdev_prop_set_defaults(dev, dev->info->props);
+ qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
+ qdev_prop_set_globals(dev);
+ QLIST_INSERT_HEAD(&bus->children, dev, sibling);
+ if (qdev_hotplug) {
+ assert(bus->allow_hotplug);
+ dev->hotplugged = 1;
+ qdev_hot_added = true;
+ }
+ dev->instance_id_alias = -1;
+ dev->state = DEV_STATE_CREATED;
+ return dev;
+}
+
+/* Create a new device. This only initializes the device state structure
+ and allows properties to be set. qdev_init should be called to
+ initialize the actual device emulation. */
+DeviceState *qdev_create(BusState *bus, const char *name)
+{
+ DeviceInfo *info;
+
+ if (!bus) {
+ bus = sysbus_get_default();
+ }
+
+ info = qdev_find_info(bus->info, name);
+ if (!info) {
+ hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name);
+ }
+
+ return qdev_create_from_info(bus, info);
+}
+
+static void qdev_print_devinfo(DeviceInfo *info)
+{
+ error_printf("name \"%s\", bus %s",
+ info->name, info->bus_info->name);
+ if (info->alias) {
+ error_printf(", alias \"%s\"", info->alias);
+ }
+ if (info->desc) {
+ error_printf(", desc \"%s\"", info->desc);
+ }
+ if (info->no_user) {
+ error_printf(", no-user");
+ }
+ error_printf("\n");
+}
+
+static int set_property(const char *name, const char *value, void *opaque)
+{
+ DeviceState *dev = opaque;
+
+ if (strcmp(name, "driver") == 0)
+ return 0;
+ if (strcmp(name, "bus") == 0)
+ return 0;
+
+ if (qdev_prop_parse(dev, name, value) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+int qdev_device_help(QemuOpts *opts)
+{
+ const char *driver;
+ DeviceInfo *info;
+ Property *prop;
+
+ driver = qemu_opt_get(opts, "driver");
+ if (driver && !strcmp(driver, "?")) {
+ for (info = device_info_list; info != NULL; info = info->next) {
+ if (info->no_user) {
+ continue; /* not available, don't show */
+ }
+ qdev_print_devinfo(info);
+ }
+ return 1;
+ }
+
+ if (!qemu_opt_get(opts, "?")) {
+ return 0;
+ }
+
+ info = qdev_find_info(NULL, driver);
+ if (!info) {
+ return 0;
+ }
+
+ for (prop = info->props; prop && prop->name; prop++) {
+ /*
+ * TODO Properties without a parser are just for dirty hacks.
+ * qdev_prop_ptr is the only such PropertyInfo. It's marked
+ * for removal. This conditional should be removed along with
+ * it.
+ */
+ if (!prop->info->parse) {
+ continue; /* no way to set it, don't show */
+ }
+ error_printf("%s.%s=%s\n", info->name, prop->name, prop->info->name);
+ }
+ return 1;
+}
+
+DeviceState *qdev_device_add(QemuOpts *opts)
+{
+ const char *driver, *path, *id;
+ DeviceInfo *info;
+ DeviceState *qdev;
+ BusState *bus;
+
+ driver = qemu_opt_get(opts, "driver");
+ if (!driver) {
+ qerror_report(QERR_MISSING_PARAMETER, "driver");
+ return NULL;
+ }
+
+ /* find driver */
+ info = qdev_find_info(NULL, driver);
+ if (!info || info->no_user) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "a driver name");
+ error_printf_unless_qmp("Try with argument '?' for a list.\n");
+ return NULL;
+ }
+
+ /* find bus */
+ path = qemu_opt_get(opts, "bus");
+ if (path != NULL) {
+ bus = qbus_find(path);
+ if (!bus) {
+ return NULL;
+ }
+ if (bus->info != info->bus_info) {
+ qerror_report(QERR_BAD_BUS_FOR_DEVICE,
+ driver, bus->info->name);
+ return NULL;
+ }
+ } else {
+ bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
+ if (!bus) {
+ qerror_report(QERR_NO_BUS_FOR_DEVICE,
+ info->name, info->bus_info->name);
+ return NULL;
+ }
+ }
+ if (qdev_hotplug && !bus->allow_hotplug) {
+ qerror_report(QERR_BUS_NO_HOTPLUG, bus->name);
+ return NULL;
+ }
+
+ /* create device, set properties */
+ qdev = qdev_create_from_info(bus, info);
+ id = qemu_opts_id(opts);
+ if (id) {
+ qdev->id = id;
+ }
+ if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
+ qdev_free(qdev);
+ return NULL;
+ }
+ if (qdev_init(qdev) < 0) {
+ qerror_report(QERR_DEVICE_INIT_FAILED, driver);
+ return NULL;
+ }
+ qdev->opts = opts;
+ return qdev;
+}
+
+/* Initialize a device. Device properties should be set before calling
+ this function. IRQs and MMIO regions should be connected/mapped after
+ calling this function.
+ On failure, destroy the device and return negative value.
+ Return 0 on success. */
+int qdev_init(DeviceState *dev)
+{
+ int rc;
+
+ assert(dev->state == DEV_STATE_CREATED);
+ rc = dev->info->init(dev, dev->info);
+ if (rc < 0) {
+ qdev_free(dev);
+ return rc;
+ }
+ if (dev->info->vmsd) {
+ vmstate_register_with_alias_id(dev, -1, dev->info->vmsd, dev,
+ dev->instance_id_alias,
+ dev->alias_required_for_version);
+ }
+ dev->state = DEV_STATE_INITIALIZED;
+ return 0;
+}
+
+void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
+ int required_for_version)
+{
+ assert(dev->state == DEV_STATE_CREATED);
+ dev->instance_id_alias = alias_id;
+ dev->alias_required_for_version = required_for_version;
+}
+
+int qdev_unplug(DeviceState *dev)
+{
+ if (!dev->parent_bus->allow_hotplug) {
+ qerror_report(QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
+ return -1;
+ }
+ assert(dev->info->unplug != NULL);
+
+ qdev_hot_removed = true;
+
+ return dev->info->unplug(dev);
+}
+
+static int qdev_reset_one(DeviceState *dev, void *opaque)
+{
+ if (dev->info->reset) {
+ dev->info->reset(dev);
+ }
+
+ return 0;
+}
+
+BusState *sysbus_get_default(void)
+{
+ if (!main_system_bus) {
+ main_system_bus = qbus_create(&system_bus_info, NULL,
+ "main-system-bus");
+ }
+ return main_system_bus;
+}
+
+static int qbus_reset_one(BusState *bus, void *opaque)
+{
+ if (bus->info->reset) {
+ return bus->info->reset(bus);
+ }
+ return 0;
+}
+
+void qdev_reset_all(DeviceState *dev)
+{
+ qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qbus_reset_all_fn(void *opaque)
+{
+ BusState *bus = opaque;
+ qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+}
+
+/* can be used as ->unplug() callback for the simple cases */
+int qdev_simple_unplug_cb(DeviceState *dev)
+{
+ /* just zap it */
+ qdev_free(dev);
+ return 0;
+}
+
+/* Like qdev_init(), but terminate program via hw_error() instead of
+ returning an error value. This is okay during machine creation.
+ Don't use for hotplug, because there callers need to recover from
+ failure. Exception: if you know the device's init() callback can't
+ fail, then qdev_init_nofail() can't fail either, and is therefore
+ usable even then. But relying on the device implementation that
+ way is somewhat unclean, and best avoided. */
+void qdev_init_nofail(DeviceState *dev)
+{
+ DeviceInfo *info = dev->info;
+
+ if (qdev_init(dev) < 0) {
+ error_report("Initialization of device %s failed\n", info->name);
+ exit(1);
+ }
+}
+
+/* Unlink device from bus and free the structure. */
+void qdev_free(DeviceState *dev)
+{
+ BusState *bus;
+ Property *prop;
+
+ if (dev->state == DEV_STATE_INITIALIZED) {
+ while (dev->num_child_bus) {
+ bus = QLIST_FIRST(&dev->child_bus);
+ qbus_free(bus);
+ }
+ if (dev->info->vmsd)
+ vmstate_unregister(dev, dev->info->vmsd, dev);
+ if (dev->info->exit)
+ dev->info->exit(dev);
+ if (dev->opts)
+ qemu_opts_del(dev->opts);
+ }
+ QLIST_REMOVE(dev, sibling);
+ for (prop = dev->info->props; prop && prop->name; prop++) {
+ if (prop->info->free) {
+ prop->info->free(dev, prop);
+ }
+ }
+ qemu_free(dev);
+}
+
+void qdev_machine_creation_done(void)
+{
+ /*
+ * ok, initial machine setup is done, starting from now we can
+ * only create hotpluggable devices
+ */
+ qdev_hotplug = 1;
+}
+
+bool qdev_machine_modified(void)
+{
+ return qdev_hot_added || qdev_hot_removed;
+}
+
+/* Get a character (serial) device interface. */
+CharDriverState *qdev_init_chardev(DeviceState *dev)
+{
+ static int next_serial;
+
+ /* FIXME: This function needs to go away: use chardev properties! */
+ return serial_hds[next_serial++];
+}
+
+BusState *qdev_get_parent_bus(DeviceState *dev)
+{
+ return dev->parent_bus;
+}
+
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
+{
+ assert(dev->num_gpio_in == 0);
+ dev->num_gpio_in = n;
+ dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
+}
+
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
+{
+ assert(dev->num_gpio_out == 0);
+ dev->num_gpio_out = n;
+ dev->gpio_out = pins;
+}
+
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
+{
+ assert(n >= 0 && n < dev->num_gpio_in);
+ return dev->gpio_in[n];
+}
+
+void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
+{
+ assert(n >= 0 && n < dev->num_gpio_out);
+ dev->gpio_out[n] = pin;
+}
+
+void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
+{
+ qdev_prop_set_macaddr(dev, "mac", nd->macaddr);
+ if (nd->vlan)
+ qdev_prop_set_vlan(dev, "vlan", nd->vlan);
+ if (nd->netdev)
+ qdev_prop_set_netdev(dev, "netdev", nd->netdev);
+ if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
+ qdev_prop_exists(dev, "vectors")) {
+ qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
+ }
+}
+
+BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
+{
+ BusState *bus;
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ if (strcmp(name, bus->name) == 0) {
+ return bus;
+ }
+ }
+ return NULL;
+}
+
+int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
+ qbus_walkerfn *busfn, void *opaque)
+{
+ DeviceState *dev;
+ int err;
+
+ if (busfn) {
+ err = busfn(bus, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ err = qdev_walk_children(dev, devfn, busfn, opaque);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
+ qbus_walkerfn *busfn, void *opaque)
+{
+ BusState *bus;
+ int err;
+
+ if (devfn) {
+ err = devfn(dev, opaque);
+ if (err) {
+ return err;
+ }
+ }
+
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+ err = qbus_walk_children(bus, devfn, busfn, opaque);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+ const BusInfo *info)
+{
+ DeviceState *dev;
+ BusState *child, *ret;
+ int match = 1;
+
+ if (name && (strcmp(bus->name, name) != 0)) {
+ match = 0;
+ }
+ if (info && (bus->info != info)) {
+ match = 0;
+ }
+ if (match) {
+ return bus;
+ }
+
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qbus_find_recursive(child, name, info);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+ DeviceState *dev, *ret;
+ BusState *child;
+
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->id && strcmp(dev->id, id) == 0)
+ return dev;
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qdev_find_recursive(child, id);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void qbus_list_bus(DeviceState *dev)
+{
+ BusState *child;
+ const char *sep = " ";
+
+ error_printf("child busses at \"%s\":",
+ dev->id ? dev->id : dev->info->name);
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ error_printf("%s\"%s\"", sep, child->name);
+ sep = ", ";
+ }
+ error_printf("\n");
+}
+
+static void qbus_list_dev(BusState *bus)
+{
+ DeviceState *dev;
+ const char *sep = " ";
+
+ error_printf("devices at \"%s\":", bus->name);
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ error_printf("%s\"%s\"", sep, dev->info->name);
+ if (dev->id)
+ error_printf("/\"%s\"", dev->id);
+ sep = ", ";
+ }
+ error_printf("\n");
+}
+
+static BusState *qbus_find_bus(DeviceState *dev, char *elem)
+{
+ BusState *child;
+
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ if (strcmp(child->name, elem) == 0) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static DeviceState *qbus_find_dev(BusState *bus, char *elem)
+{
+ DeviceState *dev;
+
+ /*
+ * try to match in order:
+ * (1) instance id, if present
+ * (2) driver name
+ * (3) driver alias, if present
+ */
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->id && strcmp(dev->id, elem) == 0) {
+ return dev;
+ }
+ }
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (strcmp(dev->info->name, elem) == 0) {
+ return dev;
+ }
+ }
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+static BusState *qbus_find(const char *path)
+{
+ DeviceState *dev;
+ BusState *bus;
+ char elem[128];
+ int pos, len;
+
+ /* find start element */
+ if (path[0] == '/') {
+ bus = main_system_bus;
+ pos = 0;
+ } else {
+ if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
+ assert(!path[0]);
+ elem[0] = len = 0;
+ }
+ bus = qbus_find_recursive(main_system_bus, elem, NULL);
+ if (!bus) {
+ qerror_report(QERR_BUS_NOT_FOUND, elem);
+ return NULL;
+ }
+ pos = len;
+ }
+
+ for (;;) {
+ assert(path[pos] == '/' || !path[pos]);
+ while (path[pos] == '/') {
+ pos++;
+ }
+ if (path[pos] == '\0') {
+ return bus;
+ }
+
+ /* find device */
+ if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
+ assert(0);
+ elem[0] = len = 0;
+ }
+ pos += len;
+ dev = qbus_find_dev(bus, elem);
+ if (!dev) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, elem);
+ if (!monitor_cur_is_qmp()) {
+ qbus_list_dev(bus);
+ }
+ return NULL;
+ }
+
+ assert(path[pos] == '/' || !path[pos]);
+ while (path[pos] == '/') {
+ pos++;
+ }
+ if (path[pos] == '\0') {
+ /* last specified element is a device. If it has exactly
+ * one child bus accept it nevertheless */
+ switch (dev->num_child_bus) {
+ case 0:
+ qerror_report(QERR_DEVICE_NO_BUS, elem);
+ return NULL;
+ case 1:
+ return QLIST_FIRST(&dev->child_bus);
+ default:
+ qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem);
+ if (!monitor_cur_is_qmp()) {
+ qbus_list_bus(dev);
+ }
+ return NULL;
+ }
+ }
+
+ /* find bus */
+ if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) {
+ assert(0);
+ elem[0] = len = 0;
+ }
+ pos += len;
+ bus = qbus_find_bus(dev, elem);
+ if (!bus) {
+ qerror_report(QERR_BUS_NOT_FOUND, elem);
+ if (!monitor_cur_is_qmp()) {
+ qbus_list_bus(dev);
+ }
+ return NULL;
+ }
+ }
+}
+
+void qbus_create_inplace(BusState *bus, BusInfo *info,
+ DeviceState *parent, const char *name)
+{
+ char *buf;
+ int i,len;
+
+ bus->info = info;
+ bus->parent = parent;
+
+ if (name) {
+ /* use supplied name */
+ bus->name = qemu_strdup(name);
+ } else if (parent && parent->id) {
+ /* parent device has id -> use it for bus name */
+ len = strlen(parent->id) + 16;
+ buf = qemu_malloc(len);
+ snprintf(buf, len, "%s.%d", parent->id, parent->num_child_bus);
+ bus->name = buf;
+ } else {
+ /* no id -> use lowercase bus type for bus name */
+ len = strlen(info->name) + 16;
+ buf = qemu_malloc(len);
+ len = snprintf(buf, len, "%s.%d", info->name,
+ parent ? parent->num_child_bus : 0);
+ for (i = 0; i < len; i++)
+ buf[i] = qemu_tolower(buf[i]);
+ bus->name = buf;
+ }
+
+ QLIST_INIT(&bus->children);
+ if (parent) {
+ QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
+ parent->num_child_bus++;
+ } else if (bus != main_system_bus) {
+ /* TODO: once all bus devices are qdevified,
+ only reset handler for main_system_bus should be registered here. */
+ qemu_register_reset(qbus_reset_all_fn, bus);
+ }
+}
+
+BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
+{
+ BusState *bus;
+
+ bus = qemu_mallocz(info->size);
+ bus->qdev_allocated = 1;
+ qbus_create_inplace(bus, info, parent, name);
+ return bus;
+}
+
+void qbus_free(BusState *bus)
+{
+ DeviceState *dev;
+
+ while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
+ qdev_free(dev);
+ }
+ if (bus->parent) {
+ QLIST_REMOVE(bus, sibling);
+ bus->parent->num_child_bus--;
+ } else {
+ assert(bus != main_system_bus); /* main_system_bus is never freed */
+ qemu_unregister_reset(qbus_reset_all_fn, bus);
+ }
+ qemu_free((void*)bus->name);
+ if (bus->qdev_allocated) {
+ qemu_free(bus);
+ }
+}
+
+#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
+static void qbus_print(Monitor *mon, BusState *bus, int indent);
+
+static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
+ const char *prefix, int indent)
+{
+ char buf[64];
+
+ if (!props)
+ return;
+ while (props->name) {
+ /*
+ * TODO Properties without a print method are just for dirty
+ * hacks. qdev_prop_ptr is the only such PropertyInfo. It's
+ * marked for removal. The test props->info->print should be
+ * removed along with it.
+ */
+ if (props->info->print) {
+ props->info->print(dev, props, buf, sizeof(buf));
+ qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf);
+ }
+ props++;
+ }
+}
+
+static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ BusState *child;
+ qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
+ dev->id ? dev->id : "");
+ indent += 2;
+ if (dev->num_gpio_in) {
+ qdev_printf("gpio-in %d\n", dev->num_gpio_in);
+ }
+ if (dev->num_gpio_out) {
+ qdev_printf("gpio-out %d\n", dev->num_gpio_out);
+ }
+ qdev_print_props(mon, dev, dev->info->props, "dev", indent);
+ qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
+ if (dev->parent_bus->info->print_dev)
+ dev->parent_bus->info->print_dev(mon, dev, indent);
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ qbus_print(mon, child, indent);
+ }
+}
+
+static void qbus_print(Monitor *mon, BusState *bus, int indent)
+{
+ struct DeviceState *dev;
+
+ qdev_printf("bus: %s\n", bus->name);
+ indent += 2;
+ qdev_printf("type %s\n", bus->info->name);
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ qdev_print(mon, dev, indent);
+ }
+}
+#undef qdev_printf
+
+void do_info_qtree(Monitor *mon)
+{
+ if (main_system_bus)
+ qbus_print(mon, main_system_bus, 0);
+}
+
+void do_info_qdm(Monitor *mon)
+{
+ DeviceInfo *info;
+
+ for (info = device_info_list; info != NULL; info = info->next) {
+ qdev_print_devinfo(info);
+ }
+}
+
+int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ QemuOpts *opts;
+
+ opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict);
+ if (!opts) {
+ return -1;
+ }
+ if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
+ qemu_opts_del(opts);
+ return 0;
+ }
+ if (!qdev_device_add(opts)) {
+ qemu_opts_del(opts);
+ return -1;
+ }
+ return 0;
+}
+
+int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ DeviceState *dev;
+
+ dev = qdev_find_recursive(main_system_bus, id);
+ if (NULL == dev) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, id);
+ return -1;
+ }
+ return qdev_unplug(dev);
+}
+
+static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size)
+{
+ int l = 0;
+
+ if (dev && dev->parent_bus) {
+ char *d;
+ l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size);
+ if (dev->parent_bus->info->get_fw_dev_path) {
+ d = dev->parent_bus->info->get_fw_dev_path(dev);
+ l += snprintf(p + l, size - l, "%s", d);
+ qemu_free(d);
+ } else {
+ l += snprintf(p + l, size - l, "%s", dev->info->name);
+ }
+ }
+ l += snprintf(p + l , size - l, "/");
+
+ return l;
+}
+
+char* qdev_get_fw_dev_path(DeviceState *dev)
+{
+ char path[128];
+ int l;
+
+ l = qdev_get_fw_dev_path_helper(dev, path, 128);
+
+ path[l-1] = '\0';
+
+ return strdup(path);
+}
diff --git a/hw/qdev.h b/hw/qdev.h
new file mode 100644
index 0000000..9808f85
--- /dev/null
+++ b/hw/qdev.h
@@ -0,0 +1,328 @@
+#ifndef QDEV_H
+#define QDEV_H
+
+#include "hw.h"
+#include "qemu-queue.h"
+#include "qemu-char.h"
+#include "qemu-option.h"
+
+typedef struct Property Property;
+
+typedef struct PropertyInfo PropertyInfo;
+
+typedef struct CompatProperty CompatProperty;
+
+typedef struct DeviceInfo DeviceInfo;
+
+typedef struct BusState BusState;
+
+typedef struct BusInfo BusInfo;
+
+enum DevState {
+ DEV_STATE_CREATED = 1,
+ DEV_STATE_INITIALIZED,
+};
+
+enum {
+ DEV_NVECTORS_UNSPECIFIED = -1,
+};
+
+/* This structure should not be accessed directly. We declare it here
+ so that it can be embedded in individual device state structures. */
+struct DeviceState {
+ const char *id;
+ enum DevState state;
+ QemuOpts *opts;
+ int hotplugged;
+ DeviceInfo *info;
+ BusState *parent_bus;
+ int num_gpio_out;
+ qemu_irq *gpio_out;
+ int num_gpio_in;
+ qemu_irq *gpio_in;
+ QLIST_HEAD(, BusState) child_bus;
+ int num_child_bus;
+ QLIST_ENTRY(DeviceState) sibling;
+ int instance_id_alias;
+ int alias_required_for_version;
+};
+
+typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent);
+typedef char *(*bus_get_dev_path)(DeviceState *dev);
+/*
+ * This callback is used to create Open Firmware device path in accordance with
+ * OF spec http://forthworks.com/standards/of1275.pdf. Indicidual bus bindings
+ * can be found here http://playground.sun.com/1275/bindings/.
+ */
+typedef char *(*bus_get_fw_dev_path)(DeviceState *dev);
+typedef int (qbus_resetfn)(BusState *bus);
+
+struct BusInfo {
+ const char *name;
+ size_t size;
+ bus_dev_printfn print_dev;
+ bus_get_dev_path get_dev_path;
+ bus_get_fw_dev_path get_fw_dev_path;
+ qbus_resetfn *reset;
+ Property *props;
+};
+
+struct BusState {
+ DeviceState *parent;
+ BusInfo *info;
+ const char *name;
+ int allow_hotplug;
+ int qdev_allocated;
+ QLIST_HEAD(, DeviceState) children;
+ QLIST_ENTRY(BusState) sibling;
+};
+
+struct Property {
+ const char *name;
+ PropertyInfo *info;
+ int offset;
+ int bitnr;
+ void *defval;
+};
+
+enum PropertyType {
+ PROP_TYPE_UNSPEC = 0,
+ PROP_TYPE_UINT8,
+ PROP_TYPE_UINT16,
+ PROP_TYPE_UINT32,
+ PROP_TYPE_INT32,
+ PROP_TYPE_UINT64,
+ PROP_TYPE_TADDR,
+ PROP_TYPE_MACADDR,
+ PROP_TYPE_DRIVE,
+ PROP_TYPE_CHR,
+ PROP_TYPE_STRING,
+ PROP_TYPE_NETDEV,
+ PROP_TYPE_VLAN,
+ PROP_TYPE_PTR,
+ PROP_TYPE_BIT,
+};
+
+struct PropertyInfo {
+ const char *name;
+ size_t size;
+ enum PropertyType type;
+ int (*parse)(DeviceState *dev, Property *prop, const char *str);
+ int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len);
+ void (*free)(DeviceState *dev, Property *prop);
+};
+
+typedef struct GlobalProperty {
+ const char *driver;
+ const char *property;
+ const char *value;
+ QTAILQ_ENTRY(GlobalProperty) next;
+} GlobalProperty;
+
+/*** Board API. This should go away once we have a machine config file. ***/
+
+DeviceState *qdev_create(BusState *bus, const char *name);
+int qdev_device_help(QemuOpts *opts);
+DeviceState *qdev_device_add(QemuOpts *opts);
+int qdev_init(DeviceState *dev) QEMU_WARN_UNUSED_RESULT;
+void qdev_init_nofail(DeviceState *dev);
+void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
+ int required_for_version);
+int qdev_unplug(DeviceState *dev);
+void qdev_free(DeviceState *dev);
+int qdev_simple_unplug_cb(DeviceState *dev);
+void qdev_machine_creation_done(void);
+bool qdev_machine_modified(void);
+
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n);
+void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin);
+
+BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
+
+/*** Device API. ***/
+
+typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info);
+typedef int (*qdev_event)(DeviceState *dev);
+typedef void (*qdev_resetfn)(DeviceState *dev);
+
+struct DeviceInfo {
+ const char *name;
+ const char *fw_name;
+ const char *alias;
+ const char *desc;
+ size_t size;
+ Property *props;
+ int no_user;
+
+ /* callbacks */
+ qdev_resetfn reset;
+
+ /* device state */
+ const VMStateDescription *vmsd;
+
+ /* Private to qdev / bus. */
+ qdev_initfn init;
+ qdev_event unplug;
+ qdev_event exit;
+ BusInfo *bus_info;
+ struct DeviceInfo *next;
+};
+extern DeviceInfo *device_info_list;
+
+void qdev_register(DeviceInfo *info);
+
+/* Register device properties. */
+/* GPIO inputs also double as IRQ sinks. */
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n);
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n);
+
+CharDriverState *qdev_init_chardev(DeviceState *dev);
+
+BusState *qdev_get_parent_bus(DeviceState *dev);
+
+/*** BUS API. ***/
+
+DeviceState *qdev_find_recursive(BusState *bus, const char *id);
+
+/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */
+typedef int (qbus_walkerfn)(BusState *bus, void *opaque);
+typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque);
+
+void qbus_create_inplace(BusState *bus, BusInfo *info,
+ DeviceState *parent, const char *name);
+BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name);
+/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
+ * < 0 if either devfn or busfn terminate walk somewhere in cursion,
+ * 0 otherwise. */
+int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
+ qbus_walkerfn *busfn, void *opaque);
+int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
+ qbus_walkerfn *busfn, void *opaque);
+void qdev_reset_all(DeviceState *dev);
+void qbus_reset_all_fn(void *opaque);
+
+void qbus_free(BusState *bus);
+
+#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
+
+/* This should go away once we get rid of the NULL bus hack */
+BusState *sysbus_get_default(void);
+
+/*** monitor commands ***/
+
+void do_info_qtree(Monitor *mon);
+void do_info_qdm(Monitor *mon);
+int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+/*** qdev-properties.c ***/
+
+extern PropertyInfo qdev_prop_bit;
+extern PropertyInfo qdev_prop_uint8;
+extern PropertyInfo qdev_prop_uint16;
+extern PropertyInfo qdev_prop_uint32;
+extern PropertyInfo qdev_prop_int32;
+extern PropertyInfo qdev_prop_uint64;
+extern PropertyInfo qdev_prop_hex32;
+extern PropertyInfo qdev_prop_hex64;
+extern PropertyInfo qdev_prop_string;
+extern PropertyInfo qdev_prop_chr;
+extern PropertyInfo qdev_prop_ptr;
+extern PropertyInfo qdev_prop_macaddr;
+extern PropertyInfo qdev_prop_drive;
+extern PropertyInfo qdev_prop_netdev;
+extern PropertyInfo qdev_prop_vlan;
+extern PropertyInfo qdev_prop_pci_devfn;
+
+#define DEFINE_PROP(_name, _state, _field, _prop, _type) { \
+ .name = (_name), \
+ .info = &(_prop), \
+ .offset = offsetof(_state, _field) \
+ + type_check(_type,typeof_field(_state, _field)), \
+ }
+#define DEFINE_PROP_DEFAULT(_name, _state, _field, _defval, _prop, _type) { \
+ .name = (_name), \
+ .info = &(_prop), \
+ .offset = offsetof(_state, _field) \
+ + type_check(_type,typeof_field(_state, _field)), \
+ .defval = (_type[]) { _defval }, \
+ }
+#define DEFINE_PROP_BIT(_name, _state, _field, _bit, _defval) { \
+ .name = (_name), \
+ .info = &(qdev_prop_bit), \
+ .bitnr = (_bit), \
+ .offset = offsetof(_state, _field) \
+ + type_check(uint32_t,typeof_field(_state, _field)), \
+ .defval = (bool[]) { (_defval) }, \
+ }
+
+#define DEFINE_PROP_UINT8(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t)
+#define DEFINE_PROP_UINT16(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint16, uint16_t)
+#define DEFINE_PROP_UINT32(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint32, uint32_t)
+#define DEFINE_PROP_INT32(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_int32, int32_t)
+#define DEFINE_PROP_UINT64(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint64, uint64_t)
+#define DEFINE_PROP_HEX32(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex32, uint32_t)
+#define DEFINE_PROP_HEX64(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_hex64, uint64_t)
+#define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \
+ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_pci_devfn, uint32_t)
+
+#define DEFINE_PROP_PTR(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_ptr, void*)
+#define DEFINE_PROP_CHR(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharDriverState*)
+#define DEFINE_PROP_STRING(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*)
+#define DEFINE_PROP_NETDEV(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, VLANClientState*)
+#define DEFINE_PROP_VLAN(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, VLANState*)
+#define DEFINE_PROP_DRIVE(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *)
+#define DEFINE_PROP_MACADDR(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr)
+
+#define DEFINE_PROP_END_OF_LIST() \
+ {}
+
+/* Set properties between creation and init. */
+void *qdev_get_prop_ptr(DeviceState *dev, Property *prop);
+int qdev_prop_exists(DeviceState *dev, const char *name);
+int qdev_prop_parse(DeviceState *dev, const char *name, const char *value);
+void qdev_prop_set(DeviceState *dev, const char *name, void *src, enum PropertyType type);
+void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value);
+void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value);
+void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value);
+void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value);
+void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value);
+void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value);
+void qdev_prop_set_string(DeviceState *dev, const char *name, char *value);
+void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value);
+void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value);
+void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value);
+int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) QEMU_WARN_UNUSED_RESULT;
+void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value);
+void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value);
+/* FIXME: Remove opaque pointer properties. */
+void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value);
+void qdev_prop_set_defaults(DeviceState *dev, Property *props);
+
+void qdev_prop_register_global_list(GlobalProperty *props);
+void qdev_prop_set_globals(DeviceState *dev);
+
+static inline const char *qdev_fw_name(DeviceState *dev)
+{
+ return dev->info->fw_name ? : dev->info->alias ? : dev->info->name;
+}
+
+char *qdev_get_fw_dev_path(DeviceState *dev);
+/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
+extern struct BusInfo system_bus_info;
+
+#endif
diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c
new file mode 100644
index 0000000..76f43e6
--- /dev/null
+++ b/hw/qxl-logger.c
@@ -0,0 +1,248 @@
+/*
+ * qxl command logging -- for debug purposes
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * maintained by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qxl.h"
+
+static const char *qxl_type[] = {
+ [ QXL_CMD_NOP ] = "nop",
+ [ QXL_CMD_DRAW ] = "draw",
+ [ QXL_CMD_UPDATE ] = "update",
+ [ QXL_CMD_CURSOR ] = "cursor",
+ [ QXL_CMD_MESSAGE ] = "message",
+ [ QXL_CMD_SURFACE ] = "surface",
+};
+
+static const char *qxl_draw_type[] = {
+ [ QXL_DRAW_NOP ] = "nop",
+ [ QXL_DRAW_FILL ] = "fill",
+ [ QXL_DRAW_OPAQUE ] = "opaque",
+ [ QXL_DRAW_COPY ] = "copy",
+ [ QXL_COPY_BITS ] = "copy-bits",
+ [ QXL_DRAW_BLEND ] = "blend",
+ [ QXL_DRAW_BLACKNESS ] = "blackness",
+ [ QXL_DRAW_WHITENESS ] = "whitemess",
+ [ QXL_DRAW_INVERS ] = "invers",
+ [ QXL_DRAW_ROP3 ] = "rop3",
+ [ QXL_DRAW_STROKE ] = "stroke",
+ [ QXL_DRAW_TEXT ] = "text",
+ [ QXL_DRAW_TRANSPARENT ] = "transparent",
+ [ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend",
+};
+
+static const char *qxl_draw_effect[] = {
+ [ QXL_EFFECT_BLEND ] = "blend",
+ [ QXL_EFFECT_OPAQUE ] = "opaque",
+ [ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup",
+ [ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup",
+ [ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup",
+ [ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup",
+ [ QXL_EFFECT_NOP ] = "nop",
+ [ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush",
+};
+
+static const char *qxl_surface_cmd[] = {
+ [ QXL_SURFACE_CMD_CREATE ] = "create",
+ [ QXL_SURFACE_CMD_DESTROY ] = "destroy",
+};
+
+static const char *spice_surface_fmt[] = {
+ [ SPICE_SURFACE_FMT_INVALID ] = "invalid",
+ [ SPICE_SURFACE_FMT_1_A ] = "alpha/1",
+ [ SPICE_SURFACE_FMT_8_A ] = "alpha/8",
+ [ SPICE_SURFACE_FMT_16_555 ] = "555/16",
+ [ SPICE_SURFACE_FMT_16_565 ] = "565/16",
+ [ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32",
+ [ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32",
+};
+
+static const char *qxl_cursor_cmd[] = {
+ [ QXL_CURSOR_SET ] = "set",
+ [ QXL_CURSOR_MOVE ] = "move",
+ [ QXL_CURSOR_HIDE ] = "hide",
+ [ QXL_CURSOR_TRAIL ] = "trail",
+};
+
+static const char *spice_cursor_type[] = {
+ [ SPICE_CURSOR_TYPE_ALPHA ] = "alpha",
+ [ SPICE_CURSOR_TYPE_MONO ] = "mono",
+ [ SPICE_CURSOR_TYPE_COLOR4 ] = "color4",
+ [ SPICE_CURSOR_TYPE_COLOR8 ] = "color8",
+ [ SPICE_CURSOR_TYPE_COLOR16 ] = "color16",
+ [ SPICE_CURSOR_TYPE_COLOR24 ] = "color24",
+ [ SPICE_CURSOR_TYPE_COLOR32 ] = "color32",
+};
+
+static const char *qxl_v2n(const char *n[], size_t l, int v)
+{
+ if (v >= l || !n[v]) {
+ return "???";
+ }
+ return n[v];
+}
+#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value)
+
+static void qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id)
+{
+ QXLImage *image;
+ QXLImageDescriptor *desc;
+
+ image = qxl_phys2virt(qxl, addr, group_id);
+ desc = &image->descriptor;
+ fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d",
+ desc->id, desc->type, desc->flags, desc->width, desc->height);
+ switch (desc->type) {
+ case SPICE_IMAGE_TYPE_BITMAP:
+ fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d"
+ " palette %" PRIx64 " data %" PRIx64,
+ image->bitmap.format, image->bitmap.flags,
+ image->bitmap.x, image->bitmap.y,
+ image->bitmap.stride,
+ image->bitmap.palette, image->bitmap.data);
+ break;
+ }
+ fprintf(stderr, ")");
+}
+
+static void qxl_log_rect(QXLRect *rect)
+{
+ fprintf(stderr, " %dx%d+%d+%d",
+ rect->right - rect->left,
+ rect->bottom - rect->top,
+ rect->left, rect->top);
+}
+
+static void qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, int group_id)
+{
+ fprintf(stderr, " src %" PRIx64,
+ copy->src_bitmap);
+ qxl_log_image(qxl, copy->src_bitmap, group_id);
+ fprintf(stderr, " area");
+ qxl_log_rect(&copy->src_area);
+ fprintf(stderr, " rop %d", copy->rop_descriptor);
+}
+
+static void qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id)
+{
+ fprintf(stderr, ": surface_id %d type %s effect %s",
+ draw->surface_id,
+ qxl_name(qxl_draw_type, draw->type),
+ qxl_name(qxl_draw_effect, draw->effect));
+ switch (draw->type) {
+ case QXL_DRAW_COPY:
+ qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id);
+ break;
+ }
+}
+
+static void qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw,
+ int group_id)
+{
+ fprintf(stderr, ": type %s effect %s",
+ qxl_name(qxl_draw_type, draw->type),
+ qxl_name(qxl_draw_effect, draw->effect));
+ if (draw->bitmap_offset) {
+ fprintf(stderr, ": bitmap %d",
+ draw->bitmap_offset);
+ qxl_log_rect(&draw->bitmap_area);
+ }
+ switch (draw->type) {
+ case QXL_DRAW_COPY:
+ qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id);
+ break;
+ }
+}
+
+static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
+{
+ fprintf(stderr, ": %s id %d",
+ qxl_name(qxl_surface_cmd, cmd->type),
+ cmd->surface_id);
+ if (cmd->type == QXL_SURFACE_CMD_CREATE) {
+ fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)",
+ cmd->u.surface_create.width,
+ cmd->u.surface_create.height,
+ cmd->u.surface_create.stride,
+ qxl_name(spice_surface_fmt, cmd->u.surface_create.format),
+ qxl->guest_surfaces.count, qxl->guest_surfaces.max);
+ }
+ if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
+ fprintf(stderr, " (count %d)", qxl->guest_surfaces.count);
+ }
+}
+
+void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
+{
+ QXLCursor *cursor;
+
+ fprintf(stderr, ": %s",
+ qxl_name(qxl_cursor_cmd, cmd->type));
+ switch (cmd->type) {
+ case QXL_CURSOR_SET:
+ fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64,
+ cmd->u.set.position.x,
+ cmd->u.set.position.y,
+ cmd->u.set.visible ? "yes" : "no",
+ cmd->u.set.shape);
+ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id);
+ fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d"
+ " unique 0x%" PRIx64 " data-size %d",
+ qxl_name(spice_cursor_type, cursor->header.type),
+ cursor->header.width, cursor->header.height,
+ cursor->header.hot_spot_x, cursor->header.hot_spot_y,
+ cursor->header.unique, cursor->data_size);
+ break;
+ case QXL_CURSOR_MOVE:
+ fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y);
+ break;
+ }
+}
+
+void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
+{
+ bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
+ void *data;
+
+ if (!qxl->cmdlog) {
+ return;
+ }
+ fprintf(stderr, "qxl-%d/%s:", qxl->id, ring);
+ fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data,
+ qxl_name(qxl_type, ext->cmd.type),
+ compat ? "(compat)" : "");
+
+ data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+ switch (ext->cmd.type) {
+ case QXL_CMD_DRAW:
+ if (!compat) {
+ qxl_log_cmd_draw(qxl, data, ext->group_id);
+ } else {
+ qxl_log_cmd_draw_compat(qxl, data, ext->group_id);
+ }
+ break;
+ case QXL_CMD_SURFACE:
+ qxl_log_cmd_surface(qxl, data);
+ break;
+ case QXL_CMD_CURSOR:
+ qxl_log_cmd_cursor(qxl, data, ext->group_id);
+ break;
+ }
+ fprintf(stderr, "\n");
+}
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
new file mode 100644
index 0000000..58965e0
--- /dev/null
+++ b/hw/qxl-render.c
@@ -0,0 +1,226 @@
+/*
+ * qxl local rendering (aka display on sdl/vnc)
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * maintained by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qxl.h"
+
+static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
+{
+ uint8_t *src = qxl->guest_primary.data;
+ uint8_t *dst = qxl->guest_primary.flipped;
+ int len, i;
+
+ src += (qxl->guest_primary.surface.height - rect->top - 1) *
+ qxl->guest_primary.stride;
+ dst += rect->top * qxl->guest_primary.stride;
+ src += rect->left * qxl->guest_primary.bytes_pp;
+ dst += rect->left * qxl->guest_primary.bytes_pp;
+ len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
+
+ for (i = rect->top; i < rect->bottom; i++) {
+ memcpy(dst, src, len);
+ dst += qxl->guest_primary.stride;
+ src -= qxl->guest_primary.stride;
+ }
+}
+
+void qxl_render_resize(PCIQXLDevice *qxl)
+{
+ QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
+
+ qxl->guest_primary.stride = sc->stride;
+ qxl->guest_primary.resized++;
+ switch (sc->format) {
+ case SPICE_SURFACE_FMT_16_555:
+ qxl->guest_primary.bytes_pp = 2;
+ qxl->guest_primary.bits_pp = 15;
+ break;
+ case SPICE_SURFACE_FMT_16_565:
+ qxl->guest_primary.bytes_pp = 2;
+ qxl->guest_primary.bits_pp = 16;
+ break;
+ case SPICE_SURFACE_FMT_32_xRGB:
+ case SPICE_SURFACE_FMT_32_ARGB:
+ qxl->guest_primary.bytes_pp = 4;
+ qxl->guest_primary.bits_pp = 32;
+ break;
+ default:
+ fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
+ qxl->guest_primary.surface.format);
+ qxl->guest_primary.bytes_pp = 4;
+ qxl->guest_primary.bits_pp = 32;
+ break;
+ }
+}
+
+void qxl_render_update(PCIQXLDevice *qxl)
+{
+ VGACommonState *vga = &qxl->vga;
+ QXLRect dirty[32], update;
+ void *ptr;
+ int i;
+
+ if (qxl->guest_primary.resized) {
+ qxl->guest_primary.resized = 0;
+
+ if (qxl->guest_primary.flipped) {
+ qemu_free(qxl->guest_primary.flipped);
+ qxl->guest_primary.flipped = NULL;
+ }
+ qemu_free_displaysurface(vga->ds);
+
+ qxl->guest_primary.data = qemu_get_ram_ptr(qxl->vga.vram_offset);
+ if (qxl->guest_primary.stride < 0) {
+ /* spice surface is upside down -> need extra buffer to flip */
+ qxl->guest_primary.stride = -qxl->guest_primary.stride;
+ qxl->guest_primary.flipped = qemu_malloc(qxl->guest_primary.surface.width *
+ qxl->guest_primary.stride);
+ ptr = qxl->guest_primary.flipped;
+ } else {
+ ptr = qxl->guest_primary.data;
+ }
+ dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
+ __FUNCTION__,
+ qxl->guest_primary.surface.width,
+ qxl->guest_primary.surface.height,
+ qxl->guest_primary.stride,
+ qxl->guest_primary.bytes_pp,
+ qxl->guest_primary.bits_pp,
+ qxl->guest_primary.flipped ? "yes" : "no");
+ vga->ds->surface =
+ qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
+ qxl->guest_primary.surface.height,
+ qxl->guest_primary.bits_pp,
+ qxl->guest_primary.stride,
+ ptr);
+ dpy_resize(vga->ds);
+ }
+
+ if (!qxl->guest_primary.commands) {
+ return;
+ }
+ qxl->guest_primary.commands = 0;
+
+ update.left = 0;
+ update.right = qxl->guest_primary.surface.width;
+ update.top = 0;
+ update.bottom = qxl->guest_primary.surface.height;
+
+ memset(dirty, 0, sizeof(dirty));
+ qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update,
+ dirty, ARRAY_SIZE(dirty), 1);
+
+ for (i = 0; i < ARRAY_SIZE(dirty); i++) {
+ if (qemu_spice_rect_is_empty(dirty+i)) {
+ break;
+ }
+ if (qxl->guest_primary.flipped) {
+ qxl_flip(qxl, dirty+i);
+ }
+ dpy_update(vga->ds,
+ dirty[i].left, dirty[i].top,
+ dirty[i].right - dirty[i].left,
+ dirty[i].bottom - dirty[i].top);
+ }
+}
+
+static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
+{
+ QEMUCursor *c;
+ uint8_t *image, *mask;
+ int size;
+
+ c = cursor_alloc(cursor->header.width, cursor->header.height);
+ c->hot_x = cursor->header.hot_spot_x;
+ c->hot_y = cursor->header.hot_spot_y;
+ switch (cursor->header.type) {
+ case SPICE_CURSOR_TYPE_ALPHA:
+ size = cursor->header.width * cursor->header.height * sizeof(uint32_t);
+ memcpy(c->data, cursor->chunk.data, size);
+ if (qxl->debug > 2) {
+ cursor_print_ascii_art(c, "qxl/alpha");
+ }
+ break;
+ case SPICE_CURSOR_TYPE_MONO:
+ mask = cursor->chunk.data;
+ image = mask + cursor_get_mono_bpl(c) * c->width;
+ cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
+ if (qxl->debug > 2) {
+ cursor_print_ascii_art(c, "qxl/mono");
+ }
+ break;
+ default:
+ fprintf(stderr, "%s: not implemented: type %d\n",
+ __FUNCTION__, cursor->header.type);
+ goto fail;
+ }
+ return c;
+
+fail:
+ cursor_put(c);
+ return NULL;
+}
+
+
+/* called from spice server thread context only */
+void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
+{
+ QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+ QXLCursor *cursor;
+ QEMUCursor *c;
+ int x = -1, y = -1;
+
+ if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) {
+ return;
+ }
+
+ if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
+ fprintf(stderr, "%s", __FUNCTION__);
+ qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
+ fprintf(stderr, "\n");
+ }
+ switch (cmd->type) {
+ case QXL_CURSOR_SET:
+ x = cmd->u.set.position.x;
+ y = cmd->u.set.position.y;
+ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
+ if (cursor->chunk.data_size != cursor->data_size) {
+ fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
+ return;
+ }
+ c = qxl_cursor(qxl, cursor);
+ if (c == NULL) {
+ c = cursor_builtin_left_ptr();
+ }
+ qemu_mutex_lock_iothread();
+ qxl->ssd.ds->cursor_define(c);
+ qxl->ssd.ds->mouse_set(x, y, 1);
+ qemu_mutex_unlock_iothread();
+ cursor_put(c);
+ break;
+ case QXL_CURSOR_MOVE:
+ x = cmd->u.position.x;
+ y = cmd->u.position.y;
+ qemu_mutex_lock_iothread();
+ qxl->ssd.ds->mouse_set(x, y, 1);
+ qemu_mutex_unlock_iothread();
+ break;
+ }
+}
diff --git a/hw/qxl.c b/hw/qxl.c
new file mode 100644
index 0000000..fe4212b
--- /dev/null
+++ b/hw/qxl.c
@@ -0,0 +1,1531 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann
+ * maintained by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <pthread.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "monitor.h"
+#include "sysemu.h"
+
+#include "qxl.h"
+
+#undef SPICE_RING_PROD_ITEM
+#define SPICE_RING_PROD_ITEM(r, ret) { \
+ typeof(r) start = r; \
+ typeof(r) end = r + 1; \
+ uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \
+ typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \
+ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+ abort(); \
+ } \
+ ret = &m_item->el; \
+ }
+
+#undef SPICE_RING_CONS_ITEM
+#define SPICE_RING_CONS_ITEM(r, ret) { \
+ typeof(r) start = r; \
+ typeof(r) end = r + 1; \
+ uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \
+ typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \
+ if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+ abort(); \
+ } \
+ ret = &m_item->el; \
+ }
+
+#undef ALIGN
+#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
+
+#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9"
+
+#define QXL_MODE(_x, _y, _b, _o) \
+ { .x_res = _x, \
+ .y_res = _y, \
+ .bits = _b, \
+ .stride = (_x) * (_b) / 8, \
+ .x_mili = PIXEL_SIZE * (_x), \
+ .y_mili = PIXEL_SIZE * (_y), \
+ .orientation = _o, \
+ }
+
+#define QXL_MODE_16_32(x_res, y_res, orientation) \
+ QXL_MODE(x_res, y_res, 16, orientation), \
+ QXL_MODE(x_res, y_res, 32, orientation)
+
+#define QXL_MODE_EX(x_res, y_res) \
+ QXL_MODE_16_32(x_res, y_res, 0), \
+ QXL_MODE_16_32(y_res, x_res, 1), \
+ QXL_MODE_16_32(x_res, y_res, 2), \
+ QXL_MODE_16_32(y_res, x_res, 3)
+
+static QXLMode qxl_modes[] = {
+ QXL_MODE_EX(640, 480),
+ QXL_MODE_EX(800, 480),
+ QXL_MODE_EX(800, 600),
+ QXL_MODE_EX(832, 624),
+ QXL_MODE_EX(960, 640),
+ QXL_MODE_EX(1024, 600),
+ QXL_MODE_EX(1024, 768),
+ QXL_MODE_EX(1152, 864),
+ QXL_MODE_EX(1152, 870),
+ QXL_MODE_EX(1280, 720),
+ QXL_MODE_EX(1280, 760),
+ QXL_MODE_EX(1280, 768),
+ QXL_MODE_EX(1280, 800),
+ QXL_MODE_EX(1280, 960),
+ QXL_MODE_EX(1280, 1024),
+ QXL_MODE_EX(1360, 768),
+ QXL_MODE_EX(1366, 768),
+ QXL_MODE_EX(1400, 1050),
+ QXL_MODE_EX(1440, 900),
+ QXL_MODE_EX(1600, 900),
+ QXL_MODE_EX(1600, 1200),
+ QXL_MODE_EX(1680, 1050),
+ QXL_MODE_EX(1920, 1080),
+#if VGA_RAM_SIZE >= (16 * 1024 * 1024)
+ /* these modes need more than 8 MB video memory */
+ QXL_MODE_EX(1920, 1200),
+ QXL_MODE_EX(1920, 1440),
+ QXL_MODE_EX(2048, 1536),
+ QXL_MODE_EX(2560, 1440),
+ QXL_MODE_EX(2560, 1600),
+#endif
+#if VGA_RAM_SIZE >= (32 * 1024 * 1024)
+ /* these modes need more than 16 MB video memory */
+ QXL_MODE_EX(2560, 2048),
+ QXL_MODE_EX(2800, 2100),
+ QXL_MODE_EX(3200, 2400),
+#endif
+};
+
+static PCIQXLDevice *qxl0;
+
+static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
+static void qxl_destroy_primary(PCIQXLDevice *d);
+static void qxl_reset_memslots(PCIQXLDevice *d);
+static void qxl_reset_surfaces(PCIQXLDevice *d);
+static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
+
+static inline uint32_t msb_mask(uint32_t val)
+{
+ uint32_t mask;
+
+ do {
+ mask = ~(val - 1) & val;
+ val &= ~mask;
+ } while (mask < val);
+
+ return mask;
+}
+
+static ram_addr_t qxl_rom_size(void)
+{
+ uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
+ rom_size = MAX(rom_size, TARGET_PAGE_SIZE);
+ rom_size = msb_mask(rom_size * 2 - 1);
+ return rom_size;
+}
+
+static void init_qxl_rom(PCIQXLDevice *d)
+{
+ QXLRom *rom = qemu_get_ram_ptr(d->rom_offset);
+ QXLModes *modes = (QXLModes *)(rom + 1);
+ uint32_t ram_header_size;
+ uint32_t surface0_area_size;
+ uint32_t num_pages;
+ uint32_t fb, maxfb = 0;
+ int i;
+
+ memset(rom, 0, d->rom_size);
+
+ rom->magic = cpu_to_le32(QXL_ROM_MAGIC);
+ rom->id = cpu_to_le32(d->id);
+ rom->log_level = cpu_to_le32(d->guestdebug);
+ rom->modes_offset = cpu_to_le32(sizeof(QXLRom));
+
+ rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
+ rom->slot_id_bits = MEMSLOT_SLOT_BITS;
+ rom->slots_start = 1;
+ rom->slots_end = NUM_MEMSLOTS - 1;
+ rom->n_surfaces = cpu_to_le32(NUM_SURFACES);
+
+ modes->n_modes = cpu_to_le32(ARRAY_SIZE(qxl_modes));
+ for (i = 0; i < modes->n_modes; i++) {
+ fb = qxl_modes[i].y_res * qxl_modes[i].stride;
+ if (maxfb < fb) {
+ maxfb = fb;
+ }
+ modes->modes[i].id = cpu_to_le32(i);
+ modes->modes[i].x_res = cpu_to_le32(qxl_modes[i].x_res);
+ modes->modes[i].y_res = cpu_to_le32(qxl_modes[i].y_res);
+ modes->modes[i].bits = cpu_to_le32(qxl_modes[i].bits);
+ modes->modes[i].stride = cpu_to_le32(qxl_modes[i].stride);
+ modes->modes[i].x_mili = cpu_to_le32(qxl_modes[i].x_mili);
+ modes->modes[i].y_mili = cpu_to_le32(qxl_modes[i].y_mili);
+ modes->modes[i].orientation = cpu_to_le32(qxl_modes[i].orientation);
+ }
+ if (maxfb < VGA_RAM_SIZE && d->id == 0)
+ maxfb = VGA_RAM_SIZE;
+
+ ram_header_size = ALIGN(sizeof(QXLRam), 4096);
+ surface0_area_size = ALIGN(maxfb, 4096);
+ num_pages = d->vga.vram_size;
+ num_pages -= ram_header_size;
+ num_pages -= surface0_area_size;
+ num_pages = num_pages / TARGET_PAGE_SIZE;
+
+ rom->draw_area_offset = cpu_to_le32(0);
+ rom->surface0_area_size = cpu_to_le32(surface0_area_size);
+ rom->pages_offset = cpu_to_le32(surface0_area_size);
+ rom->num_pages = cpu_to_le32(num_pages);
+ rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size);
+
+ d->shadow_rom = *rom;
+ d->rom = rom;
+ d->modes = modes;
+}
+
+static void init_qxl_ram(PCIQXLDevice *d)
+{
+ uint8_t *buf;
+ uint64_t *item;
+
+ buf = d->vga.vram_ptr;
+ d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset));
+ d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC);
+ d->ram->int_pending = cpu_to_le32(0);
+ d->ram->int_mask = cpu_to_le32(0);
+ SPICE_RING_INIT(&d->ram->cmd_ring);
+ SPICE_RING_INIT(&d->ram->cursor_ring);
+ SPICE_RING_INIT(&d->ram->release_ring);
+ SPICE_RING_PROD_ITEM(&d->ram->release_ring, item);
+ *item = 0;
+ qxl_ring_set_dirty(d);
+}
+
+/* can be called from spice server thread context */
+static void qxl_set_dirty(ram_addr_t addr, ram_addr_t end)
+{
+ while (addr < end) {
+ cpu_physical_memory_set_dirty(addr);
+ addr += TARGET_PAGE_SIZE;
+ }
+}
+
+static void qxl_rom_set_dirty(PCIQXLDevice *qxl)
+{
+ ram_addr_t addr = qxl->rom_offset;
+ qxl_set_dirty(addr, addr + qxl->rom_size);
+}
+
+/* called from spice server thread context only */
+static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
+{
+ ram_addr_t addr = qxl->vga.vram_offset;
+ void *base = qxl->vga.vram_ptr;
+ intptr_t offset;
+
+ offset = ptr - base;
+ offset &= ~(TARGET_PAGE_SIZE-1);
+ assert(offset < qxl->vga.vram_size);
+ qxl_set_dirty(addr + offset, addr + offset + TARGET_PAGE_SIZE);
+}
+
+/* can be called from spice server thread context */
+static void qxl_ring_set_dirty(PCIQXLDevice *qxl)
+{
+ ram_addr_t addr = qxl->vga.vram_offset + qxl->shadow_rom.ram_header_offset;
+ ram_addr_t end = qxl->vga.vram_offset + qxl->vga.vram_size;
+ qxl_set_dirty(addr, end);
+}
+
+/*
+ * keep track of some command state, for savevm/loadvm.
+ * called from spice server thread context only
+ */
+static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
+{
+ switch (le32_to_cpu(ext->cmd.type)) {
+ case QXL_CMD_SURFACE:
+ {
+ QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+ uint32_t id = le32_to_cpu(cmd->surface_id);
+ PANIC_ON(id >= NUM_SURFACES);
+ if (cmd->type == QXL_SURFACE_CMD_CREATE) {
+ qxl->guest_surfaces.cmds[id] = ext->cmd.data;
+ qxl->guest_surfaces.count++;
+ if (qxl->guest_surfaces.max < qxl->guest_surfaces.count)
+ qxl->guest_surfaces.max = qxl->guest_surfaces.count;
+ }
+ if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
+ qxl->guest_surfaces.cmds[id] = 0;
+ qxl->guest_surfaces.count--;
+ }
+ break;
+ }
+ case QXL_CMD_CURSOR:
+ {
+ QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+ if (cmd->type == QXL_CURSOR_SET) {
+ qxl->guest_cursor = ext->cmd.data;
+ }
+ break;
+ }
+ }
+}
+
+/* spice display interface callbacks */
+
+static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+ dprint(qxl, 1, "%s:\n", __FUNCTION__);
+ qxl->ssd.worker = qxl_worker;
+}
+
+static void interface_set_compression_level(QXLInstance *sin, int level)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+ dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level);
+ qxl->shadow_rom.compression_level = cpu_to_le32(level);
+ qxl->rom->compression_level = cpu_to_le32(level);
+ qxl_rom_set_dirty(qxl);
+}
+
+static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+ qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
+ qxl->rom->mm_clock = cpu_to_le32(mm_time);
+ qxl_rom_set_dirty(qxl);
+}
+
+static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+ dprint(qxl, 1, "%s:\n", __FUNCTION__);
+ info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
+ info->memslot_id_bits = MEMSLOT_SLOT_BITS;
+ info->num_memslots = NUM_MEMSLOTS;
+ info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+ info->internal_groupslot_id = 0;
+ info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
+ info->n_surfaces = NUM_SURFACES;
+}
+
+/* called from spice server thread context only */
+static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ SimpleSpiceUpdate *update;
+ QXLCommandRing *ring;
+ QXLCommand *cmd;
+ int notify;
+
+ switch (qxl->mode) {
+ case QXL_MODE_VGA:
+ dprint(qxl, 2, "%s: vga\n", __FUNCTION__);
+ update = qemu_spice_create_update(&qxl->ssd);
+ if (update == NULL) {
+ return false;
+ }
+ *ext = update->ext;
+ qxl_log_command(qxl, "vga", ext);
+ return true;
+ case QXL_MODE_COMPAT:
+ case QXL_MODE_NATIVE:
+ case QXL_MODE_UNDEFINED:
+ dprint(qxl, 2, "%s: %s\n", __FUNCTION__,
+ qxl->cmdflags ? "compat" : "native");
+ ring = &qxl->ram->cmd_ring;
+ if (SPICE_RING_IS_EMPTY(ring)) {
+ return false;
+ }
+ SPICE_RING_CONS_ITEM(ring, cmd);
+ ext->cmd = *cmd;
+ ext->group_id = MEMSLOT_GROUP_GUEST;
+ ext->flags = qxl->cmdflags;
+ SPICE_RING_POP(ring, notify);
+ qxl_ring_set_dirty(qxl);
+ if (notify) {
+ qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
+ }
+ qxl->guest_primary.commands++;
+ qxl_track_command(qxl, ext);
+ qxl_log_command(qxl, "cmd", ext);
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* called from spice server thread context only */
+static int interface_req_cmd_notification(QXLInstance *sin)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ int wait = 1;
+
+ switch (qxl->mode) {
+ case QXL_MODE_COMPAT:
+ case QXL_MODE_NATIVE:
+ case QXL_MODE_UNDEFINED:
+ SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait);
+ qxl_ring_set_dirty(qxl);
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ return wait;
+}
+
+/* called from spice server thread context only */
+static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
+{
+ QXLReleaseRing *ring = &d->ram->release_ring;
+ uint64_t *item;
+ int notify;
+
+#define QXL_FREE_BUNCH_SIZE 32
+
+ if (ring->prod - ring->cons + 1 == ring->num_items) {
+ /* ring full -- can't push */
+ return;
+ }
+ if (!flush && d->oom_running) {
+ /* collect everything from oom handler before pushing */
+ return;
+ }
+ if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) {
+ /* collect a bit more before pushing */
+ return;
+ }
+
+ SPICE_RING_PUSH(ring, notify);
+ dprint(d, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n",
+ d->num_free_res, notify ? "yes" : "no",
+ ring->prod - ring->cons, ring->num_items,
+ ring->prod, ring->cons);
+ if (notify) {
+ qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+ }
+ SPICE_RING_PROD_ITEM(ring, item);
+ *item = 0;
+ d->num_free_res = 0;
+ d->last_release = NULL;
+ qxl_ring_set_dirty(d);
+}
+
+/* called from spice server thread context only */
+static void interface_release_resource(QXLInstance *sin,
+ struct QXLReleaseInfoExt ext)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ QXLReleaseRing *ring;
+ uint64_t *item, id;
+
+ if (ext.group_id == MEMSLOT_GROUP_HOST) {
+ /* host group -> vga mode update request */
+ qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id);
+ return;
+ }
+
+ /*
+ * ext->info points into guest-visible memory
+ * pci bar 0, $command.release_info
+ */
+ ring = &qxl->ram->release_ring;
+ SPICE_RING_PROD_ITEM(ring, item);
+ if (*item == 0) {
+ /* stick head into the ring */
+ id = ext.info->id;
+ ext.info->next = 0;
+ qxl_ram_set_dirty(qxl, &ext.info->next);
+ *item = id;
+ qxl_ring_set_dirty(qxl);
+ } else {
+ /* append item to the list */
+ qxl->last_release->next = ext.info->id;
+ qxl_ram_set_dirty(qxl, &qxl->last_release->next);
+ ext.info->next = 0;
+ qxl_ram_set_dirty(qxl, &ext.info->next);
+ }
+ qxl->last_release = ext.info;
+ qxl->num_free_res++;
+ dprint(qxl, 3, "%4d\r", qxl->num_free_res);
+ qxl_push_free_res(qxl, 0);
+}
+
+/* called from spice server thread context only */
+static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ QXLCursorRing *ring;
+ QXLCommand *cmd;
+ int notify;
+
+ switch (qxl->mode) {
+ case QXL_MODE_COMPAT:
+ case QXL_MODE_NATIVE:
+ case QXL_MODE_UNDEFINED:
+ ring = &qxl->ram->cursor_ring;
+ if (SPICE_RING_IS_EMPTY(ring)) {
+ return false;
+ }
+ SPICE_RING_CONS_ITEM(ring, cmd);
+ ext->cmd = *cmd;
+ ext->group_id = MEMSLOT_GROUP_GUEST;
+ ext->flags = qxl->cmdflags;
+ SPICE_RING_POP(ring, notify);
+ qxl_ring_set_dirty(qxl);
+ if (notify) {
+ qxl_send_events(qxl, QXL_INTERRUPT_CURSOR);
+ }
+ qxl->guest_primary.commands++;
+ qxl_track_command(qxl, ext);
+ qxl_log_command(qxl, "csr", ext);
+ if (qxl->id == 0) {
+ qxl_render_cursor(qxl, ext);
+ }
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* called from spice server thread context only */
+static int interface_req_cursor_notification(QXLInstance *sin)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ int wait = 1;
+
+ switch (qxl->mode) {
+ case QXL_MODE_COMPAT:
+ case QXL_MODE_NATIVE:
+ case QXL_MODE_UNDEFINED:
+ SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait);
+ qxl_ring_set_dirty(qxl);
+ break;
+ default:
+ /* nothing */
+ break;
+ }
+ return wait;
+}
+
+/* called from spice server thread context */
+static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
+{
+ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+ abort();
+}
+
+/* called from spice server thread context only */
+static int interface_flush_resources(QXLInstance *sin)
+{
+ PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+ int ret;
+
+ dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res);
+ ret = qxl->num_free_res;
+ if (ret) {
+ qxl_push_free_res(qxl, 1);
+ }
+ return ret;
+}
+
+static const QXLInterface qxl_interface = {
+ .base.type = SPICE_INTERFACE_QXL,
+ .base.description = "qxl gpu",
+ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+
+ .attache_worker = interface_attach_worker,
+ .set_compression_level = interface_set_compression_level,
+ .set_mm_time = interface_set_mm_time,
+ .get_init_info = interface_get_init_info,
+
+ /* the callbacks below are called from spice server thread context */
+ .get_command = interface_get_command,
+ .req_cmd_notification = interface_req_cmd_notification,
+ .release_resource = interface_release_resource,
+ .get_cursor_command = interface_get_cursor_command,
+ .req_cursor_notification = interface_req_cursor_notification,
+ .notify_update = interface_notify_update,
+ .flush_resources = interface_flush_resources,
+};
+
+static void qxl_enter_vga_mode(PCIQXLDevice *d)
+{
+ if (d->mode == QXL_MODE_VGA) {
+ return;
+ }
+ dprint(d, 1, "%s\n", __FUNCTION__);
+ qemu_spice_create_host_primary(&d->ssd);
+ d->mode = QXL_MODE_VGA;
+ memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
+}
+
+static void qxl_exit_vga_mode(PCIQXLDevice *d)
+{
+ if (d->mode != QXL_MODE_VGA) {
+ return;
+ }
+ dprint(d, 1, "%s\n", __FUNCTION__);
+ qxl_destroy_primary(d);
+}
+
+static void qxl_set_irq(PCIQXLDevice *d)
+{
+ uint32_t pending = le32_to_cpu(d->ram->int_pending);
+ uint32_t mask = le32_to_cpu(d->ram->int_mask);
+ int level = !!(pending & mask);
+ qemu_set_irq(d->pci.irq[0], level);
+ qxl_ring_set_dirty(d);
+}
+
+static void qxl_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, d);
+ VGACommonState *vga = &qxl->vga;
+
+ vga_dirty_log_stop(vga);
+ pci_default_write_config(d, address, val, len);
+ if (vga->map_addr && qxl->pci.io_regions[0].addr == -1) {
+ vga->map_addr = 0;
+ }
+ vga_dirty_log_start(vga);
+}
+
+static void qxl_check_state(PCIQXLDevice *d)
+{
+ QXLRam *ram = d->ram;
+
+ assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+ assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+}
+
+static void qxl_reset_state(PCIQXLDevice *d)
+{
+ QXLRam *ram = d->ram;
+ QXLRom *rom = d->rom;
+
+ assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+ assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+ d->shadow_rom.update_id = cpu_to_le32(0);
+ *rom = d->shadow_rom;
+ qxl_rom_set_dirty(d);
+ init_qxl_ram(d);
+ d->num_free_res = 0;
+ d->last_release = NULL;
+ memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
+}
+
+static void qxl_soft_reset(PCIQXLDevice *d)
+{
+ dprint(d, 1, "%s:\n", __FUNCTION__);
+ qxl_check_state(d);
+
+ if (d->id == 0) {
+ qxl_enter_vga_mode(d);
+ } else {
+ d->mode = QXL_MODE_UNDEFINED;
+ }
+}
+
+static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
+{
+ dprint(d, 1, "%s: start%s\n", __FUNCTION__,
+ loadvm ? " (loadvm)" : "");
+
+ qemu_mutex_unlock_iothread();
+ d->ssd.worker->reset_cursor(d->ssd.worker);
+ d->ssd.worker->reset_image_cache(d->ssd.worker);
+ qemu_mutex_lock_iothread();
+ qxl_reset_surfaces(d);
+ qxl_reset_memslots(d);
+
+ /* pre loadvm reset must not touch QXLRam. This lives in
+ * device memory, is migrated together with RAM and thus
+ * already loaded at this point */
+ if (!loadvm) {
+ qxl_reset_state(d);
+ }
+ qemu_spice_create_host_memslot(&d->ssd);
+ qxl_soft_reset(d);
+
+ dprint(d, 1, "%s: done\n", __FUNCTION__);
+}
+
+static void qxl_reset_handler(DeviceState *dev)
+{
+ PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
+ qxl_hard_reset(d, 0);
+}
+
+static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGACommonState *vga = opaque;
+ PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);
+
+ if (qxl->mode != QXL_MODE_VGA) {
+ dprint(qxl, 1, "%s\n", __FUNCTION__);
+ qxl_destroy_primary(qxl);
+ qxl_soft_reset(qxl);
+ }
+ vga_ioport_write(opaque, addr, val);
+}
+
+static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta)
+{
+ static const int regions[] = {
+ QXL_RAM_RANGE_INDEX,
+ QXL_VRAM_RANGE_INDEX,
+ };
+ uint64_t guest_start;
+ uint64_t guest_end;
+ int pci_region;
+ pcibus_t pci_start;
+ pcibus_t pci_end;
+ intptr_t virt_start;
+ QXLDevMemSlot memslot;
+ int i;
+
+ guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start);
+ guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end);
+
+ dprint(d, 1, "%s: slot %d: guest phys 0x%" PRIx64 " - 0x%" PRIx64 "\n",
+ __FUNCTION__, slot_id,
+ guest_start, guest_end);
+
+ PANIC_ON(slot_id >= NUM_MEMSLOTS);
+ PANIC_ON(guest_start > guest_end);
+
+ for (i = 0; i < ARRAY_SIZE(regions); i++) {
+ pci_region = regions[i];
+ pci_start = d->pci.io_regions[pci_region].addr;
+ pci_end = pci_start + d->pci.io_regions[pci_region].size;
+ /* mapped? */
+ if (pci_start == -1) {
+ continue;
+ }
+ /* start address in range ? */
+ if (guest_start < pci_start || guest_start > pci_end) {
+ continue;
+ }
+ /* end address in range ? */
+ if (guest_end > pci_end) {
+ continue;
+ }
+ /* passed */
+ break;
+ }
+ PANIC_ON(i == ARRAY_SIZE(regions)); /* finished loop without match */
+
+ switch (pci_region) {
+ case QXL_RAM_RANGE_INDEX:
+ virt_start = (intptr_t)qemu_get_ram_ptr(d->vga.vram_offset);
+ break;
+ case QXL_VRAM_RANGE_INDEX:
+ virt_start = (intptr_t)qemu_get_ram_ptr(d->vram_offset);
+ break;
+ default:
+ /* should not happen */
+ abort();
+ }
+
+ memslot.slot_id = slot_id;
+ memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */
+ memslot.virt_start = virt_start + (guest_start - pci_start);
+ memslot.virt_end = virt_start + (guest_end - pci_start);
+ memslot.addr_delta = memslot.virt_start - delta;
+ memslot.generation = d->rom->slot_generation = 0;
+ qxl_rom_set_dirty(d);
+
+ dprint(d, 1, "%s: slot %d: host virt 0x%" PRIx64 " - 0x%" PRIx64 "\n",
+ __FUNCTION__, memslot.slot_id,
+ memslot.virt_start, memslot.virt_end);
+
+ d->ssd.worker->add_memslot(d->ssd.worker, &memslot);
+ d->guest_slots[slot_id].ptr = (void*)memslot.virt_start;
+ d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start;
+ d->guest_slots[slot_id].delta = delta;
+ d->guest_slots[slot_id].active = 1;
+}
+
+static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
+{
+ dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id);
+ d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id);
+ d->guest_slots[slot_id].active = 0;
+}
+
+static void qxl_reset_memslots(PCIQXLDevice *d)
+{
+ dprint(d, 1, "%s:\n", __FUNCTION__);
+ d->ssd.worker->reset_memslots(d->ssd.worker);
+ memset(&d->guest_slots, 0, sizeof(d->guest_slots));
+}
+
+static void qxl_reset_surfaces(PCIQXLDevice *d)
+{
+ dprint(d, 1, "%s:\n", __FUNCTION__);
+ d->mode = QXL_MODE_UNDEFINED;
+ qemu_mutex_unlock_iothread();
+ d->ssd.worker->destroy_surfaces(d->ssd.worker);
+ qemu_mutex_lock_iothread();
+ memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds));
+}
+
+/* called from spice server thread context only */
+void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
+{
+ uint64_t phys = le64_to_cpu(pqxl);
+ uint32_t slot = (phys >> (64 - 8)) & 0xff;
+ uint64_t offset = phys & 0xffffffffffff;
+
+ switch (group_id) {
+ case MEMSLOT_GROUP_HOST:
+ return (void*)offset;
+ case MEMSLOT_GROUP_GUEST:
+ PANIC_ON(slot > NUM_MEMSLOTS);
+ PANIC_ON(!qxl->guest_slots[slot].active);
+ PANIC_ON(offset < qxl->guest_slots[slot].delta);
+ offset -= qxl->guest_slots[slot].delta;
+ PANIC_ON(offset > qxl->guest_slots[slot].size)
+ return qxl->guest_slots[slot].ptr + offset;
+ default:
+ PANIC_ON(1);
+ }
+}
+
+static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm)
+{
+ QXLDevSurfaceCreate surface;
+ QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
+
+ assert(qxl->mode != QXL_MODE_NATIVE);
+ qxl_exit_vga_mode(qxl);
+
+ dprint(qxl, 1, "%s: %dx%d\n", __FUNCTION__,
+ le32_to_cpu(sc->width), le32_to_cpu(sc->height));
+
+ surface.format = le32_to_cpu(sc->format);
+ surface.height = le32_to_cpu(sc->height);
+ surface.mem = le64_to_cpu(sc->mem);
+ surface.position = le32_to_cpu(sc->position);
+ surface.stride = le32_to_cpu(sc->stride);
+ surface.width = le32_to_cpu(sc->width);
+ surface.type = le32_to_cpu(sc->type);
+ surface.flags = le32_to_cpu(sc->flags);
+
+ surface.mouse_mode = true;
+ surface.group_id = MEMSLOT_GROUP_GUEST;
+ if (loadvm) {
+ surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
+ }
+
+ qxl->mode = QXL_MODE_NATIVE;
+ qxl->cmdflags = 0;
+ qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface);
+
+ /* for local rendering */
+ qxl_render_resize(qxl);
+}
+
+static void qxl_destroy_primary(PCIQXLDevice *d)
+{
+ if (d->mode == QXL_MODE_UNDEFINED) {
+ return;
+ }
+
+ dprint(d, 1, "%s\n", __FUNCTION__);
+
+ d->mode = QXL_MODE_UNDEFINED;
+ qemu_mutex_unlock_iothread();
+ d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0);
+ qemu_mutex_lock_iothread();
+}
+
+static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
+{
+ pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
+ pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
+ QXLMode *mode = d->modes->modes + modenr;
+ uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
+ QXLMemSlot slot = {
+ .mem_start = start,
+ .mem_end = end
+ };
+ QXLSurfaceCreate surface = {
+ .width = mode->x_res,
+ .height = mode->y_res,
+ .stride = -mode->x_res * 4,
+ .format = SPICE_SURFACE_FMT_32_xRGB,
+ .flags = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0,
+ .mouse_mode = true,
+ .mem = devmem + d->shadow_rom.draw_area_offset,
+ };
+
+ dprint(d, 1, "%s: mode %d [ %d x %d @ %d bpp devmem 0x%lx ]\n", __FUNCTION__,
+ modenr, mode->x_res, mode->y_res, mode->bits, devmem);
+ if (!loadvm) {
+ qxl_hard_reset(d, 0);
+ }
+
+ d->guest_slots[0].slot = slot;
+ qxl_add_memslot(d, 0, devmem);
+
+ d->guest_primary.surface = surface;
+ qxl_create_guest_primary(d, 0);
+
+ d->mode = QXL_MODE_COMPAT;
+ d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
+#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
+ if (mode->bits == 16) {
+ d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
+ }
+#endif
+ d->shadow_rom.mode = cpu_to_le32(modenr);
+ d->rom->mode = cpu_to_le32(modenr);
+ qxl_rom_set_dirty(d);
+}
+
+static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ PCIQXLDevice *d = opaque;
+ uint32_t io_port = addr - d->io_base;
+
+ switch (io_port) {
+ case QXL_IO_RESET:
+ case QXL_IO_SET_MODE:
+ case QXL_IO_MEMSLOT_ADD:
+ case QXL_IO_MEMSLOT_DEL:
+ case QXL_IO_CREATE_PRIMARY:
+ break;
+ default:
+ if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT)
+ break;
+ dprint(d, 1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port);
+ return;
+ }
+
+ switch (io_port) {
+ case QXL_IO_UPDATE_AREA:
+ {
+ QXLRect update = d->ram->update_area;
+ qemu_mutex_unlock_iothread();
+ d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface,
+ &update, NULL, 0, 0);
+ qemu_mutex_lock_iothread();
+ break;
+ }
+ case QXL_IO_NOTIFY_CMD:
+ d->ssd.worker->wakeup(d->ssd.worker);
+ break;
+ case QXL_IO_NOTIFY_CURSOR:
+ d->ssd.worker->wakeup(d->ssd.worker);
+ break;
+ case QXL_IO_UPDATE_IRQ:
+ qxl_set_irq(d);
+ break;
+ case QXL_IO_NOTIFY_OOM:
+ if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
+ break;
+ }
+ pthread_yield();
+ if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
+ break;
+ }
+ d->oom_running = 1;
+ d->ssd.worker->oom(d->ssd.worker);
+ d->oom_running = 0;
+ break;
+ case QXL_IO_SET_MODE:
+ dprint(d, 1, "QXL_SET_MODE %d\n", val);
+ qxl_set_mode(d, val, 0);
+ break;
+ case QXL_IO_LOG:
+ if (d->guestdebug) {
+ fprintf(stderr, "qxl/guest: %s", d->ram->log_buf);
+ }
+ break;
+ case QXL_IO_RESET:
+ dprint(d, 1, "QXL_IO_RESET\n");
+ qxl_hard_reset(d, 0);
+ break;
+ case QXL_IO_MEMSLOT_ADD:
+ PANIC_ON(val >= NUM_MEMSLOTS);
+ PANIC_ON(d->guest_slots[val].active);
+ d->guest_slots[val].slot = d->ram->mem_slot;
+ qxl_add_memslot(d, val, 0);
+ break;
+ case QXL_IO_MEMSLOT_DEL:
+ qxl_del_memslot(d, val);
+ break;
+ case QXL_IO_CREATE_PRIMARY:
+ PANIC_ON(val != 0);
+ dprint(d, 1, "QXL_IO_CREATE_PRIMARY\n");
+ d->guest_primary.surface = d->ram->create_surface;
+ qxl_create_guest_primary(d, 0);
+ break;
+ case QXL_IO_DESTROY_PRIMARY:
+ PANIC_ON(val != 0);
+ dprint(d, 1, "QXL_IO_DESTROY_PRIMARY\n");
+ qxl_destroy_primary(d);
+ break;
+ case QXL_IO_DESTROY_SURFACE_WAIT:
+ d->ssd.worker->destroy_surface_wait(d->ssd.worker, val);
+ break;
+ case QXL_IO_DESTROY_ALL_SURFACES:
+ d->ssd.worker->destroy_surfaces(d->ssd.worker);
+ break;
+ default:
+ fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port);
+ abort();
+ }
+}
+
+static uint32_t ioport_read(void *opaque, uint32_t addr)
+{
+ PCIQXLDevice *d = opaque;
+
+ dprint(d, 1, "%s: unexpected\n", __FUNCTION__);
+ return 0xff;
+}
+
+static void qxl_map(PCIDevice *pci, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ static const char *names[] = {
+ [ QXL_IO_RANGE_INDEX ] = "ioports",
+ [ QXL_RAM_RANGE_INDEX ] = "devram",
+ [ QXL_ROM_RANGE_INDEX ] = "rom",
+ [ QXL_VRAM_RANGE_INDEX ] = "vram",
+ };
+ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, pci);
+
+ dprint(qxl, 1, "%s: bar %d [%s] addr 0x%lx size 0x%lx\n", __FUNCTION__,
+ region_num, names[region_num], addr, size);
+
+ switch (region_num) {
+ case QXL_IO_RANGE_INDEX:
+ register_ioport_write(addr, size, 1, ioport_write, pci);
+ register_ioport_read(addr, size, 1, ioport_read, pci);
+ qxl->io_base = addr;
+ break;
+ case QXL_RAM_RANGE_INDEX:
+ cpu_register_physical_memory(addr, size, qxl->vga.vram_offset | IO_MEM_RAM);
+ qxl->vga.map_addr = addr;
+ qxl->vga.map_end = addr + size;
+ if (qxl->id == 0) {
+ vga_dirty_log_start(&qxl->vga);
+ }
+ break;
+ case QXL_ROM_RANGE_INDEX:
+ cpu_register_physical_memory(addr, size, qxl->rom_offset | IO_MEM_ROM);
+ break;
+ case QXL_VRAM_RANGE_INDEX:
+ cpu_register_physical_memory(addr, size, qxl->vram_offset | IO_MEM_RAM);
+ break;
+ }
+}
+
+static void pipe_read(void *opaque)
+{
+ PCIQXLDevice *d = opaque;
+ char dummy;
+ int len;
+
+ do {
+ len = read(d->pipe[0], &dummy, sizeof(dummy));
+ } while (len == sizeof(dummy));
+ qxl_set_irq(d);
+}
+
+/* called from spice server thread context only */
+static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
+{
+ uint32_t old_pending;
+ uint32_t le_events = cpu_to_le32(events);
+
+ assert(d->ssd.running);
+ old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
+ if ((old_pending & le_events) == le_events) {
+ return;
+ }
+ if (pthread_self() == d->main) {
+ qxl_set_irq(d);
+ } else {
+ if (write(d->pipe[1], d, 1) != 1) {
+ dprint(d, 1, "%s: write to pipe failed\n", __FUNCTION__);
+ }
+ }
+}
+
+static void init_pipe_signaling(PCIQXLDevice *d)
+{
+ if (pipe(d->pipe) < 0) {
+ dprint(d, 1, "%s: pipe creation failed\n", __FUNCTION__);
+ return;
+ }
+#ifdef CONFIG_IOTHREAD
+ fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
+#else
+ fcntl(d->pipe[0], F_SETFL, O_NONBLOCK /* | O_ASYNC */);
+#endif
+ fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
+ fcntl(d->pipe[0], F_SETOWN, getpid());
+
+ d->main = pthread_self();
+ qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
+}
+
+/* graphics console */
+
+static void qxl_hw_update(void *opaque)
+{
+ PCIQXLDevice *qxl = opaque;
+ VGACommonState *vga = &qxl->vga;
+
+ switch (qxl->mode) {
+ case QXL_MODE_VGA:
+ vga->update(vga);
+ break;
+ case QXL_MODE_COMPAT:
+ case QXL_MODE_NATIVE:
+ qxl_render_update(qxl);
+ break;
+ default:
+ break;
+ }
+}
+
+static void qxl_hw_invalidate(void *opaque)
+{
+ PCIQXLDevice *qxl = opaque;
+ VGACommonState *vga = &qxl->vga;
+
+ vga->invalidate(vga);
+}
+
+static void qxl_hw_screen_dump(void *opaque, const char *filename)
+{
+ PCIQXLDevice *qxl = opaque;
+ VGACommonState *vga = &qxl->vga;
+
+ switch (qxl->mode) {
+ case QXL_MODE_COMPAT:
+ case QXL_MODE_NATIVE:
+ qxl_render_update(qxl);
+ ppm_save(filename, qxl->ssd.ds->surface);
+ break;
+ case QXL_MODE_VGA:
+ vga->screen_dump(vga, filename);
+ break;
+ default:
+ break;
+ }
+}
+
+static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
+{
+ PCIQXLDevice *qxl = opaque;
+ VGACommonState *vga = &qxl->vga;
+
+ if (qxl->mode == QXL_MODE_VGA) {
+ vga->text_update(vga, chardata);
+ return;
+ }
+}
+
+static void qxl_vm_change_state_handler(void *opaque, int running, int reason)
+{
+ PCIQXLDevice *qxl = opaque;
+ qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason);
+
+ if (!running && qxl->mode == QXL_MODE_NATIVE) {
+ /* dirty all vram (which holds surfaces) to make sure it is saved */
+ /* FIXME #1: should go out during "live" stage */
+ /* FIXME #2: we only need to save the areas which are actually used */
+ ram_addr_t addr = qxl->vram_offset;
+ qxl_set_dirty(addr, addr + qxl->vram_size);
+ }
+}
+
+/* display change listener */
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+ if (qxl0->mode == QXL_MODE_VGA) {
+ qemu_spice_display_update(&qxl0->ssd, x, y, w, h);
+ }
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+ if (qxl0->mode == QXL_MODE_VGA) {
+ qemu_spice_display_resize(&qxl0->ssd);
+ }
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+ if (qxl0->mode == QXL_MODE_VGA) {
+ qemu_spice_display_refresh(&qxl0->ssd);
+ }
+}
+
+static DisplayChangeListener display_listener = {
+ .dpy_update = display_update,
+ .dpy_resize = display_resize,
+ .dpy_refresh = display_refresh,
+};
+
+static int qxl_init_common(PCIQXLDevice *qxl)
+{
+ uint8_t* config = qxl->pci.config;
+ uint32_t pci_device_id;
+ uint32_t pci_device_rev;
+ uint32_t io_size;
+
+ qxl->mode = QXL_MODE_UNDEFINED;
+ qxl->generation = 1;
+ qxl->num_memslots = NUM_MEMSLOTS;
+ qxl->num_surfaces = NUM_SURFACES;
+
+ switch (qxl->revision) {
+ case 1: /* spice 0.4 -- qxl-1 */
+ pci_device_id = QXL_DEVICE_ID_STABLE;
+ pci_device_rev = QXL_REVISION_STABLE_V04;
+ break;
+ case 2: /* spice 0.6 -- qxl-2 */
+ pci_device_id = QXL_DEVICE_ID_STABLE;
+ pci_device_rev = QXL_REVISION_STABLE_V06;
+ break;
+ default: /* experimental */
+ pci_device_id = QXL_DEVICE_ID_DEVEL;
+ pci_device_rev = 1;
+ break;
+ }
+
+ pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID);
+ pci_config_set_device_id(config, pci_device_id);
+ pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
+ pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
+
+ qxl->rom_size = qxl_rom_size();
+ qxl->rom_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vrom", qxl->rom_size);
+ init_qxl_rom(qxl);
+ init_qxl_ram(qxl);
+
+ if (qxl->vram_size < 16 * 1024 * 1024) {
+ qxl->vram_size = 16 * 1024 * 1024;
+ }
+ if (qxl->revision == 1) {
+ qxl->vram_size = 4096;
+ }
+ qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+ qxl->vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vram", qxl->vram_size);
+
+ io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
+ if (qxl->revision == 1) {
+ io_size = 8;
+ }
+
+ pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
+ io_size, PCI_BASE_ADDRESS_SPACE_IO, qxl_map);
+
+ pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX,
+ qxl->rom_size, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ qxl_map);
+
+ pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX,
+ qxl->vga.vram_size, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ qxl_map);
+
+ pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, qxl->vram_size,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, qxl_map);
+
+ qxl->ssd.qxl.base.sif = &qxl_interface.base;
+ qxl->ssd.qxl.id = qxl->id;
+ qemu_spice_add_interface(&qxl->ssd.qxl.base);
+ qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
+
+ init_pipe_signaling(qxl);
+ qxl_reset_state(qxl);
+
+ return 0;
+}
+
+static int qxl_init_primary(PCIDevice *dev)
+{
+ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+ VGACommonState *vga = &qxl->vga;
+ ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+
+ qxl->id = 0;
+
+ if (ram_size < 32 * 1024 * 1024) {
+ ram_size = 32 * 1024 * 1024;
+ }
+ vga_common_init(vga, ram_size);
+ vga_init(vga);
+ register_ioport_write(0x3c0, 16, 1, qxl_vga_ioport_write, vga);
+ register_ioport_write(0x3b4, 2, 1, qxl_vga_ioport_write, vga);
+ register_ioport_write(0x3d4, 2, 1, qxl_vga_ioport_write, vga);
+ register_ioport_write(0x3ba, 1, 1, qxl_vga_ioport_write, vga);
+ register_ioport_write(0x3da, 1, 1, qxl_vga_ioport_write, vga);
+
+ vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
+ qxl_hw_screen_dump, qxl_hw_text_update, qxl);
+ qxl->ssd.ds = vga->ds;
+ qxl->ssd.bufsize = (16 * 1024 * 1024);
+ qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize);
+
+ qxl0 = qxl;
+ register_displaychangelistener(vga->ds, &display_listener);
+
+ pci_config_set_class(dev->config, PCI_CLASS_DISPLAY_VGA);
+ return qxl_init_common(qxl);
+}
+
+static int qxl_init_secondary(PCIDevice *dev)
+{
+ static int device_id = 1;
+ PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+ ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+
+ qxl->id = device_id++;
+
+ if (ram_size < 16 * 1024 * 1024) {
+ ram_size = 16 * 1024 * 1024;
+ }
+ qxl->vga.vram_size = ram_size;
+ qxl->vga.vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vgavram",
+ qxl->vga.vram_size);
+ qxl->vga.vram_ptr = qemu_get_ram_ptr(qxl->vga.vram_offset);
+
+ pci_config_set_class(dev->config, PCI_CLASS_DISPLAY_OTHER);
+ return qxl_init_common(qxl);
+}
+
+static void qxl_pre_save(void *opaque)
+{
+ PCIQXLDevice* d = opaque;
+ uint8_t *ram_start = d->vga.vram_ptr;
+
+ dprint(d, 1, "%s:\n", __FUNCTION__);
+ if (d->last_release == NULL) {
+ d->last_release_offset = 0;
+ } else {
+ d->last_release_offset = (uint8_t *)d->last_release - ram_start;
+ }
+ assert(d->last_release_offset < d->vga.vram_size);
+}
+
+static int qxl_pre_load(void *opaque)
+{
+ PCIQXLDevice* d = opaque;
+
+ dprint(d, 1, "%s: start\n", __FUNCTION__);
+ qxl_hard_reset(d, 1);
+ qxl_exit_vga_mode(d);
+ dprint(d, 1, "%s: done\n", __FUNCTION__);
+ return 0;
+}
+
+static int qxl_post_load(void *opaque, int version)
+{
+ PCIQXLDevice* d = opaque;
+ uint8_t *ram_start = d->vga.vram_ptr;
+ QXLCommandExt *cmds;
+ int in, out, i, newmode;
+
+ dprint(d, 1, "%s: start\n", __FUNCTION__);
+
+ assert(d->last_release_offset < d->vga.vram_size);
+ if (d->last_release_offset == 0) {
+ d->last_release = NULL;
+ } else {
+ d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
+ }
+
+ d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
+
+ dprint(d, 1, "%s: restore mode\n", __FUNCTION__);
+ newmode = d->mode;
+ d->mode = QXL_MODE_UNDEFINED;
+ switch (newmode) {
+ case QXL_MODE_UNDEFINED:
+ break;
+ case QXL_MODE_VGA:
+ qxl_enter_vga_mode(d);
+ break;
+ case QXL_MODE_NATIVE:
+ for (i = 0; i < NUM_MEMSLOTS; i++) {
+ if (!d->guest_slots[i].active) {
+ continue;
+ }
+ qxl_add_memslot(d, i, 0);
+ }
+ qxl_create_guest_primary(d, 1);
+
+ /* replay surface-create and cursor-set commands */
+ cmds = qemu_mallocz(sizeof(QXLCommandExt) * (NUM_SURFACES + 1));
+ for (in = 0, out = 0; in < NUM_SURFACES; in++) {
+ if (d->guest_surfaces.cmds[in] == 0) {
+ continue;
+ }
+ cmds[out].cmd.data = d->guest_surfaces.cmds[in];
+ cmds[out].cmd.type = QXL_CMD_SURFACE;
+ cmds[out].group_id = MEMSLOT_GROUP_GUEST;
+ out++;
+ }
+ cmds[out].cmd.data = d->guest_cursor;
+ cmds[out].cmd.type = QXL_CMD_CURSOR;
+ cmds[out].group_id = MEMSLOT_GROUP_GUEST;
+ out++;
+ d->ssd.worker->loadvm_commands(d->ssd.worker, cmds, out);
+ qemu_free(cmds);
+
+ break;
+ case QXL_MODE_COMPAT:
+ qxl_set_mode(d, d->shadow_rom.mode, 1);
+ break;
+ }
+ dprint(d, 1, "%s: done\n", __FUNCTION__);
+
+ return 0;
+}
+
+#define QXL_SAVE_VERSION 21
+
+static VMStateDescription qxl_memslot = {
+ .name = "qxl-memslot",
+ .version_id = QXL_SAVE_VERSION,
+ .minimum_version_id = QXL_SAVE_VERSION,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(slot.mem_start, struct guest_slots),
+ VMSTATE_UINT64(slot.mem_end, struct guest_slots),
+ VMSTATE_UINT32(active, struct guest_slots),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static VMStateDescription qxl_surface = {
+ .name = "qxl-surface",
+ .version_id = QXL_SAVE_VERSION,
+ .minimum_version_id = QXL_SAVE_VERSION,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(width, QXLSurfaceCreate),
+ VMSTATE_UINT32(height, QXLSurfaceCreate),
+ VMSTATE_INT32(stride, QXLSurfaceCreate),
+ VMSTATE_UINT32(format, QXLSurfaceCreate),
+ VMSTATE_UINT32(position, QXLSurfaceCreate),
+ VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate),
+ VMSTATE_UINT32(flags, QXLSurfaceCreate),
+ VMSTATE_UINT32(type, QXLSurfaceCreate),
+ VMSTATE_UINT64(mem, QXLSurfaceCreate),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static VMStateDescription qxl_vmstate = {
+ .name = "qxl",
+ .version_id = QXL_SAVE_VERSION,
+ .minimum_version_id = QXL_SAVE_VERSION,
+ .pre_save = qxl_pre_save,
+ .pre_load = qxl_pre_load,
+ .post_load = qxl_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
+ VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState),
+ VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
+ VMSTATE_UINT32(num_free_res, PCIQXLDevice),
+ VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
+ VMSTATE_UINT32(mode, PCIQXLDevice),
+ VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
+ VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice),
+ VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
+ qxl_memslot, struct guest_slots),
+ VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
+ qxl_surface, QXLSurfaceCreate),
+ VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
+ VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
+ vmstate_info_uint64, uint64_t),
+ VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+static PCIDeviceInfo qxl_info_primary = {
+ .qdev.name = "qxl-vga",
+ .qdev.desc = "Spice QXL GPU (primary, vga compatible)",
+ .qdev.size = sizeof(PCIQXLDevice),
+ .qdev.reset = qxl_reset_handler,
+ .qdev.vmsd = &qxl_vmstate,
+ .no_hotplug = 1,
+ .init = qxl_init_primary,
+ .config_write = qxl_write_config,
+ .romfile = "vgabios-qxl.bin",
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2),
+ DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
+ DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
+ DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static PCIDeviceInfo qxl_info_secondary = {
+ .qdev.name = "qxl",
+ .qdev.desc = "Spice QXL GPU (secondary)",
+ .qdev.size = sizeof(PCIQXLDevice),
+ .qdev.reset = qxl_reset_handler,
+ .qdev.vmsd = &qxl_vmstate,
+ .init = qxl_init_secondary,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2),
+ DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
+ DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
+ DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void qxl_register(void)
+{
+ pci_qdev_register(&qxl_info_primary);
+ pci_qdev_register(&qxl_info_secondary);
+}
+
+device_init(qxl_register);
diff --git a/hw/qxl.h b/hw/qxl.h
new file mode 100644
index 0000000..f6c450d
--- /dev/null
+++ b/hw/qxl.h
@@ -0,0 +1,108 @@
+#include "qemu-common.h"
+
+#include "console.h"
+#include "hw.h"
+#include "pci.h"
+#include "vga_int.h"
+
+#include "ui/qemu-spice.h"
+#include "ui/spice-display.h"
+
+enum qxl_mode {
+ QXL_MODE_UNDEFINED,
+ QXL_MODE_VGA,
+ QXL_MODE_COMPAT, /* spice 0.4.x */
+ QXL_MODE_NATIVE,
+};
+
+typedef struct PCIQXLDevice {
+ PCIDevice pci;
+ SimpleSpiceDisplay ssd;
+ int id;
+ uint32_t debug;
+ uint32_t guestdebug;
+ uint32_t cmdlog;
+ enum qxl_mode mode;
+ uint32_t cmdflags;
+ int generation;
+ uint32_t revision;
+
+ int32_t num_memslots;
+ int32_t num_surfaces;
+
+ struct guest_slots {
+ QXLMemSlot slot;
+ void *ptr;
+ uint64_t size;
+ uint64_t delta;
+ uint32_t active;
+ } guest_slots[NUM_MEMSLOTS];
+
+ struct guest_primary {
+ QXLSurfaceCreate surface;
+ uint32_t commands;
+ uint32_t resized;
+ int32_t stride;
+ uint32_t bits_pp;
+ uint32_t bytes_pp;
+ uint8_t *data, *flipped;
+ } guest_primary;
+
+ struct surfaces {
+ QXLPHYSICAL cmds[NUM_SURFACES];
+ uint32_t count;
+ uint32_t max;
+ } guest_surfaces;
+ QXLPHYSICAL guest_cursor;
+
+ /* thread signaling */
+ pthread_t main;
+ int pipe[2];
+
+ /* ram pci bar */
+ QXLRam *ram;
+ VGACommonState vga;
+ uint32_t num_free_res;
+ QXLReleaseInfo *last_release;
+ uint32_t last_release_offset;
+ uint32_t oom_running;
+
+ /* rom pci bar */
+ QXLRom shadow_rom;
+ QXLRom *rom;
+ QXLModes *modes;
+ uint32_t rom_size;
+ uint64_t rom_offset;
+
+ /* vram pci bar */
+ uint32_t vram_size;
+ uint64_t vram_offset;
+
+ /* io bar */
+ uint32_t io_base;
+} PCIQXLDevice;
+
+#define PANIC_ON(x) if ((x)) { \
+ printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
+ exit(-1); \
+}
+
+#define dprint(_qxl, _level, _fmt, ...) \
+ do { \
+ if (_qxl->debug >= _level) { \
+ fprintf(stderr, "qxl-%d: ", _qxl->id); \
+ fprintf(stderr, _fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+/* qxl.c */
+void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
+
+/* qxl-logger.c */
+void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
+void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
+
+/* qxl-render.c */
+void qxl_render_resize(PCIQXLDevice *qxl);
+void qxl_render_update(PCIQXLDevice *qxl);
+void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
diff --git a/hw/r2d.c b/hw/r2d.c
new file mode 100644
index 0000000..a0f8c1f
--- /dev/null
+++ b/hw/r2d.c
@@ -0,0 +1,341 @@
+/*
+ * Renesas SH7751R R2D-PLUS emulation
+ *
+ * Copyright (c) 2007 Magnus Damm
+ * Copyright (c) 2008 Paul Mundt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "sh.h"
+#include "devices.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "pci.h"
+#include "net.h"
+#include "sh7750_regs.h"
+#include "ide.h"
+#include "loader.h"
+#include "usb.h"
+#include "flash.h"
+#include "blockdev.h"
+
+#define FLASH_BASE 0x00000000
+#define FLASH_SIZE 0x02000000
+
+#define SDRAM_BASE 0x0c000000 /* Physical location of SDRAM: Area 3 */
+#define SDRAM_SIZE 0x04000000
+
+#define SM501_VRAM_SIZE 0x800000
+
+#define BOOT_PARAMS_OFFSET 0x0010000
+/* CONFIG_BOOT_LINK_OFFSET of Linux kernel */
+#define LINUX_LOAD_OFFSET 0x0800000
+#define INITRD_LOAD_OFFSET 0x1800000
+
+#define PA_IRLMSK 0x00
+#define PA_POWOFF 0x30
+#define PA_VERREG 0x32
+#define PA_OUTPORT 0x36
+
+typedef struct {
+ uint16_t bcr;
+ uint16_t irlmsk;
+ uint16_t irlmon;
+ uint16_t cfctl;
+ uint16_t cfpow;
+ uint16_t dispctl;
+ uint16_t sdmpow;
+ uint16_t rtcce;
+ uint16_t pcicd;
+ uint16_t voyagerrts;
+ uint16_t cfrst;
+ uint16_t admrts;
+ uint16_t extrst;
+ uint16_t cfcdintclr;
+ uint16_t keyctlclr;
+ uint16_t pad0;
+ uint16_t pad1;
+ uint16_t verreg;
+ uint16_t inport;
+ uint16_t outport;
+ uint16_t bverreg;
+
+/* output pin */
+ qemu_irq irl;
+} r2d_fpga_t;
+
+enum r2d_fpga_irq {
+ PCI_INTD, CF_IDE, CF_CD, PCI_INTC, SM501, KEY, RTC_A, RTC_T,
+ SDCARD, PCI_INTA, PCI_INTB, EXT, TP,
+ NR_IRQS
+};
+
+static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = {
+ [CF_IDE] = { 1, 1<<9 },
+ [CF_CD] = { 2, 1<<8 },
+ [PCI_INTA] = { 9, 1<<14 },
+ [PCI_INTB] = { 10, 1<<13 },
+ [PCI_INTC] = { 3, 1<<12 },
+ [PCI_INTD] = { 0, 1<<11 },
+ [SM501] = { 4, 1<<10 },
+ [KEY] = { 5, 1<<6 },
+ [RTC_A] = { 6, 1<<5 },
+ [RTC_T] = { 7, 1<<4 },
+ [SDCARD] = { 8, 1<<7 },
+ [EXT] = { 11, 1<<0 },
+ [TP] = { 12, 1<<15 },
+};
+
+static void update_irl(r2d_fpga_t *fpga)
+{
+ int i, irl = 15;
+ for (i = 0; i < NR_IRQS; i++)
+ if (fpga->irlmon & fpga->irlmsk & irqtab[i].msk)
+ if (irqtab[i].irl < irl)
+ irl = irqtab[i].irl;
+ qemu_set_irq(fpga->irl, irl ^ 15);
+}
+
+static void r2d_fpga_irq_set(void *opaque, int n, int level)
+{
+ r2d_fpga_t *fpga = opaque;
+ if (level)
+ fpga->irlmon |= irqtab[n].msk;
+ else
+ fpga->irlmon &= ~irqtab[n].msk;
+ update_irl(fpga);
+}
+
+static uint32_t r2d_fpga_read(void *opaque, target_phys_addr_t addr)
+{
+ r2d_fpga_t *s = opaque;
+
+ switch (addr) {
+ case PA_IRLMSK:
+ return s->irlmsk;
+ case PA_OUTPORT:
+ return s->outport;
+ case PA_POWOFF:
+ return 0x00;
+ case PA_VERREG:
+ return 0x10;
+ }
+
+ return 0;
+}
+
+static void
+r2d_fpga_write(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ r2d_fpga_t *s = opaque;
+
+ switch (addr) {
+ case PA_IRLMSK:
+ s->irlmsk = value;
+ update_irl(s);
+ break;
+ case PA_OUTPORT:
+ s->outport = value;
+ break;
+ case PA_POWOFF:
+ if (value & 1) {
+ qemu_system_shutdown_request();
+ }
+ break;
+ case PA_VERREG:
+ /* Discard writes */
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const r2d_fpga_readfn[] = {
+ r2d_fpga_read,
+ r2d_fpga_read,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const r2d_fpga_writefn[] = {
+ r2d_fpga_write,
+ r2d_fpga_write,
+ NULL,
+};
+
+static qemu_irq *r2d_fpga_init(target_phys_addr_t base, qemu_irq irl)
+{
+ int iomemtype;
+ r2d_fpga_t *s;
+
+ s = qemu_mallocz(sizeof(r2d_fpga_t));
+
+ s->irl = irl;
+
+ iomemtype = cpu_register_io_memory(r2d_fpga_readfn,
+ r2d_fpga_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x40, iomemtype);
+ return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS);
+}
+
+typedef struct ResetData {
+ CPUState *env;
+ uint32_t vector;
+} ResetData;
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUState *env = s->env;
+
+ cpu_reset(env);
+ env->pc = s->vector;
+}
+
+static struct __attribute__((__packed__))
+{
+ int mount_root_rdonly;
+ int ramdisk_flags;
+ int orig_root_dev;
+ int loader_type;
+ int initrd_start;
+ int initrd_size;
+
+ char pad[232];
+
+ char kernel_cmdline[256];
+} boot_params;
+
+static void r2d_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ ResetData *reset_info;
+ struct SH7750State *s;
+ ram_addr_t sdram_addr;
+ qemu_irq *irq;
+ DriveInfo *dinfo;
+ int i;
+
+ if (!cpu_model)
+ cpu_model = "SH7751R";
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ reset_info = qemu_mallocz(sizeof(ResetData));
+ reset_info->env = env;
+ reset_info->vector = env->pc;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ /* Allocate memory space */
+ sdram_addr = qemu_ram_alloc(NULL, "r2d.sdram", SDRAM_SIZE);
+ cpu_register_physical_memory(SDRAM_BASE, SDRAM_SIZE, sdram_addr);
+ /* Register peripherals */
+ s = sh7750_init(env);
+ irq = r2d_fpga_init(0x04000000, sh7750_irl(s));
+ sysbus_create_varargs("sh_pci", 0x1e200000, irq[PCI_INTA], irq[PCI_INTB],
+ irq[PCI_INTC], irq[PCI_INTD], NULL);
+
+ sm501_init(0x10000000, SM501_VRAM_SIZE, irq[SM501], serial_hds[2]);
+
+ /* onboard CF (True IDE mode, Master only). */
+ dinfo = drive_get(IF_IDE, 0, 0);
+ mmio_ide_init(0x14001000, 0x1400080c, irq[CF_IDE], 1,
+ dinfo, NULL);
+
+ /* onboard flash memory */
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi02_register(0x0, qemu_ram_alloc(NULL, "r2d.flash", FLASH_SIZE),
+ dinfo ? dinfo->bdrv : NULL, (16 * 1024),
+ FLASH_SIZE >> 16,
+ 1, 4, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x555, 0x2aa, 0);
+
+ /* NIC: rtl8139 on-board, and 2 slots. */
+ for (i = 0; i < nb_nics; i++)
+ pci_nic_init_nofail(&nd_table[i], "rtl8139", i==0 ? "2" : NULL);
+
+ /* USB keyboard */
+ usbdevice_create("keyboard");
+
+ /* Todo: register on board registers */
+ memset(&boot_params, 0, sizeof(boot_params));
+
+ if (kernel_filename) {
+ int kernel_size;
+
+ kernel_size = load_image_targphys(kernel_filename,
+ SDRAM_BASE + LINUX_LOAD_OFFSET,
+ INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename);
+ exit(1);
+ }
+
+ /* initialization which should be done by firmware */
+ stl_phys(SH7750_BCR1, 1<<3); /* cs3 SDRAM */
+ stw_phys(SH7750_BCR2, 3<<(3*2)); /* cs3 32bit */
+ reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */
+ }
+
+ if (initrd_filename) {
+ int initrd_size;
+
+ initrd_size = load_image_targphys(initrd_filename,
+ SDRAM_BASE + INITRD_LOAD_OFFSET,
+ SDRAM_SIZE - INITRD_LOAD_OFFSET);
+
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename);
+ exit(1);
+ }
+
+ /* initialization which should be done by firmware */
+ boot_params.loader_type = 1;
+ boot_params.initrd_start = INITRD_LOAD_OFFSET;
+ boot_params.initrd_size = initrd_size;
+ }
+
+ if (kernel_cmdline) {
+ strncpy(boot_params.kernel_cmdline, kernel_cmdline,
+ sizeof(boot_params.kernel_cmdline));
+ }
+
+ rom_add_blob_fixed("boot_params", &boot_params, sizeof(boot_params),
+ SDRAM_BASE + BOOT_PARAMS_OFFSET);
+}
+
+static QEMUMachine r2d_machine = {
+ .name = "r2d",
+ .desc = "r2d-plus board",
+ .init = r2d_init,
+};
+
+static void r2d_machine_init(void)
+{
+ qemu_register_machine(&r2d_machine);
+}
+
+machine_init(r2d_machine_init);
diff --git a/hw/rc4030.c b/hw/rc4030.c
new file mode 100644
index 0000000..0a9d98d
--- /dev/null
+++ b/hw/rc4030.c
@@ -0,0 +1,830 @@
+/*
+ * QEMU JAZZ RC4030 chipset
+ *
+ * Copyright (c) 2007-2009 Herve Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "mips.h"
+#include "qemu-timer.h"
+
+/********************************************************/
+/* debug rc4030 */
+
+//#define DEBUG_RC4030
+//#define DEBUG_RC4030_DMA
+
+#ifdef DEBUG_RC4030
+#define DPRINTF(fmt, ...) \
+do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0)
+static const char* irq_names[] = { "parallel", "floppy", "sound", "video",
+ "network", "scsi", "keyboard", "mouse", "serial0", "serial1" };
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define RC4030_ERROR(fmt, ...) \
+do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+
+/********************************************************/
+/* rc4030 emulation */
+
+typedef struct dma_pagetable_entry {
+ int32_t frame;
+ int32_t owner;
+} __attribute__((packed)) dma_pagetable_entry;
+
+#define DMA_PAGESIZE 4096
+#define DMA_REG_ENABLE 1
+#define DMA_REG_COUNT 2
+#define DMA_REG_ADDRESS 3
+
+#define DMA_FLAG_ENABLE 0x0001
+#define DMA_FLAG_MEM_TO_DEV 0x0002
+#define DMA_FLAG_TC_INTR 0x0100
+#define DMA_FLAG_MEM_INTR 0x0200
+#define DMA_FLAG_ADDR_INTR 0x0400
+
+typedef struct rc4030State
+{
+ uint32_t config; /* 0x0000: RC4030 config register */
+ uint32_t revision; /* 0x0008: RC4030 Revision register */
+ uint32_t invalid_address_register; /* 0x0010: Invalid Address register */
+
+ /* DMA */
+ uint32_t dma_regs[8][4];
+ uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */
+ uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */
+
+ /* cache */
+ uint32_t cache_maint; /* 0x0030: Cache Maintenance */
+ uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */
+ uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */
+ uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */
+ uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */
+ uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */
+
+ uint32_t nmi_interrupt; /* 0x0200: interrupt source */
+ uint32_t offset210;
+ uint32_t nvram_protect; /* 0x0220: NV ram protect register */
+ uint32_t rem_speed[16];
+ uint32_t imr_jazz; /* Local bus int enable mask */
+ uint32_t isr_jazz; /* Local bus int source */
+
+ /* timer */
+ QEMUTimer *periodic_timer;
+ uint32_t itr; /* Interval timer reload */
+
+ qemu_irq timer_irq;
+ qemu_irq jazz_bus_irq;
+} rc4030State;
+
+static void set_next_tick(rc4030State *s)
+{
+ qemu_irq_lower(s->timer_irq);
+ uint32_t tm_hz;
+
+ tm_hz = 1000 / (s->itr + 1);
+
+ qemu_mod_timer(s->periodic_timer, qemu_get_clock(vm_clock) +
+ get_ticks_per_sec() / tm_hz);
+}
+
+/* called for accesses to rc4030 */
+static uint32_t rc4030_readl(void *opaque, target_phys_addr_t addr)
+{
+ rc4030State *s = opaque;
+ uint32_t val;
+
+ addr &= 0x3fff;
+ switch (addr & ~0x3) {
+ /* Global config register */
+ case 0x0000:
+ val = s->config;
+ break;
+ /* Revision register */
+ case 0x0008:
+ val = s->revision;
+ break;
+ /* Invalid Address register */
+ case 0x0010:
+ val = s->invalid_address_register;
+ break;
+ /* DMA transl. table base */
+ case 0x0018:
+ val = s->dma_tl_base;
+ break;
+ /* DMA transl. table limit */
+ case 0x0020:
+ val = s->dma_tl_limit;
+ break;
+ /* Remote Failed Address */
+ case 0x0038:
+ val = s->remote_failed_address;
+ break;
+ /* Memory Failed Address */
+ case 0x0040:
+ val = s->memory_failed_address;
+ break;
+ /* I/O Cache Byte Mask */
+ case 0x0058:
+ val = s->cache_bmask;
+ /* HACK */
+ if (s->cache_bmask == (uint32_t)-1)
+ s->cache_bmask = 0;
+ break;
+ /* Remote Speed Registers */
+ case 0x0070:
+ case 0x0078:
+ case 0x0080:
+ case 0x0088:
+ case 0x0090:
+ case 0x0098:
+ case 0x00a0:
+ case 0x00a8:
+ case 0x00b0:
+ case 0x00b8:
+ case 0x00c0:
+ case 0x00c8:
+ case 0x00d0:
+ case 0x00d8:
+ case 0x00e0:
+ case 0x00e8:
+ val = s->rem_speed[(addr - 0x0070) >> 3];
+ break;
+ /* DMA channel base address */
+ case 0x0100:
+ case 0x0108:
+ case 0x0110:
+ case 0x0118:
+ case 0x0120:
+ case 0x0128:
+ case 0x0130:
+ case 0x0138:
+ case 0x0140:
+ case 0x0148:
+ case 0x0150:
+ case 0x0158:
+ case 0x0160:
+ case 0x0168:
+ case 0x0170:
+ case 0x0178:
+ case 0x0180:
+ case 0x0188:
+ case 0x0190:
+ case 0x0198:
+ case 0x01a0:
+ case 0x01a8:
+ case 0x01b0:
+ case 0x01b8:
+ case 0x01c0:
+ case 0x01c8:
+ case 0x01d0:
+ case 0x01d8:
+ case 0x01e0:
+ case 0x01e8:
+ case 0x01f0:
+ case 0x01f8:
+ {
+ int entry = (addr - 0x0100) >> 5;
+ int idx = (addr & 0x1f) >> 3;
+ val = s->dma_regs[entry][idx];
+ }
+ break;
+ /* Interrupt source */
+ case 0x0200:
+ val = s->nmi_interrupt;
+ break;
+ /* Error type */
+ case 0x0208:
+ val = 0;
+ break;
+ /* Offset 0x0210 */
+ case 0x0210:
+ val = s->offset210;
+ break;
+ /* NV ram protect register */
+ case 0x0220:
+ val = s->nvram_protect;
+ break;
+ /* Interval timer count */
+ case 0x0230:
+ val = 0;
+ qemu_irq_lower(s->timer_irq);
+ break;
+ /* EISA interrupt */
+ case 0x0238:
+ val = 7; /* FIXME: should be read from EISA controller */
+ break;
+ default:
+ RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr);
+ val = 0;
+ break;
+ }
+
+ if ((addr & ~3) != 0x230) {
+ DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+ }
+
+ return val;
+}
+
+static uint32_t rc4030_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v = rc4030_readl(opaque, addr & ~0x3);
+ if (addr & 0x2)
+ return v >> 16;
+ else
+ return v & 0xffff;
+}
+
+static uint32_t rc4030_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v = rc4030_readl(opaque, addr & ~0x3);
+ return (v >> (8 * (addr & 0x3))) & 0xff;
+}
+
+static void rc4030_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rc4030State *s = opaque;
+ addr &= 0x3fff;
+
+ DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+
+ switch (addr & ~0x3) {
+ /* Global config register */
+ case 0x0000:
+ s->config = val;
+ break;
+ /* DMA transl. table base */
+ case 0x0018:
+ s->dma_tl_base = val;
+ break;
+ /* DMA transl. table limit */
+ case 0x0020:
+ s->dma_tl_limit = val;
+ break;
+ /* DMA transl. table invalidated */
+ case 0x0028:
+ break;
+ /* Cache Maintenance */
+ case 0x0030:
+ s->cache_maint = val;
+ break;
+ /* I/O Cache Physical Tag */
+ case 0x0048:
+ s->cache_ptag = val;
+ break;
+ /* I/O Cache Logical Tag */
+ case 0x0050:
+ s->cache_ltag = val;
+ break;
+ /* I/O Cache Byte Mask */
+ case 0x0058:
+ s->cache_bmask |= val; /* HACK */
+ break;
+ /* I/O Cache Buffer Window */
+ case 0x0060:
+ /* HACK */
+ if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) {
+ target_phys_addr_t dest = s->cache_ptag & ~0x1;
+ dest += (s->cache_maint & 0x3) << 3;
+ cpu_physical_memory_rw(dest, (uint8_t*)&val, 4, 1);
+ }
+ break;
+ /* Remote Speed Registers */
+ case 0x0070:
+ case 0x0078:
+ case 0x0080:
+ case 0x0088:
+ case 0x0090:
+ case 0x0098:
+ case 0x00a0:
+ case 0x00a8:
+ case 0x00b0:
+ case 0x00b8:
+ case 0x00c0:
+ case 0x00c8:
+ case 0x00d0:
+ case 0x00d8:
+ case 0x00e0:
+ case 0x00e8:
+ s->rem_speed[(addr - 0x0070) >> 3] = val;
+ break;
+ /* DMA channel base address */
+ case 0x0100:
+ case 0x0108:
+ case 0x0110:
+ case 0x0118:
+ case 0x0120:
+ case 0x0128:
+ case 0x0130:
+ case 0x0138:
+ case 0x0140:
+ case 0x0148:
+ case 0x0150:
+ case 0x0158:
+ case 0x0160:
+ case 0x0168:
+ case 0x0170:
+ case 0x0178:
+ case 0x0180:
+ case 0x0188:
+ case 0x0190:
+ case 0x0198:
+ case 0x01a0:
+ case 0x01a8:
+ case 0x01b0:
+ case 0x01b8:
+ case 0x01c0:
+ case 0x01c8:
+ case 0x01d0:
+ case 0x01d8:
+ case 0x01e0:
+ case 0x01e8:
+ case 0x01f0:
+ case 0x01f8:
+ {
+ int entry = (addr - 0x0100) >> 5;
+ int idx = (addr & 0x1f) >> 3;
+ s->dma_regs[entry][idx] = val;
+ }
+ break;
+ /* Offset 0x0210 */
+ case 0x0210:
+ s->offset210 = val;
+ break;
+ /* Interval timer reload */
+ case 0x0228:
+ s->itr = val;
+ qemu_irq_lower(s->timer_irq);
+ set_next_tick(s);
+ break;
+ /* EISA interrupt */
+ case 0x0238:
+ break;
+ default:
+ RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr);
+ break;
+ }
+}
+
+static void rc4030_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
+
+ if (addr & 0x2)
+ val = (val << 16) | (old_val & 0x0000ffff);
+ else
+ val = val | (old_val & 0xffff0000);
+ rc4030_writel(opaque, addr & ~0x3, val);
+}
+
+static void rc4030_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
+
+ switch (addr & 3) {
+ case 0:
+ val = val | (old_val & 0xffffff00);
+ break;
+ case 1:
+ val = (val << 8) | (old_val & 0xffff00ff);
+ break;
+ case 2:
+ val = (val << 16) | (old_val & 0xff00ffff);
+ break;
+ case 3:
+ val = (val << 24) | (old_val & 0x00ffffff);
+ break;
+ }
+ rc4030_writel(opaque, addr & ~0x3, val);
+}
+
+static CPUReadMemoryFunc * const rc4030_read[3] = {
+ rc4030_readb,
+ rc4030_readw,
+ rc4030_readl,
+};
+
+static CPUWriteMemoryFunc * const rc4030_write[3] = {
+ rc4030_writeb,
+ rc4030_writew,
+ rc4030_writel,
+};
+
+static void update_jazz_irq(rc4030State *s)
+{
+ uint16_t pending;
+
+ pending = s->isr_jazz & s->imr_jazz;
+
+#ifdef DEBUG_RC4030
+ if (s->isr_jazz != 0) {
+ uint32_t irq = 0;
+ DPRINTF("pending irqs:");
+ for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) {
+ if (s->isr_jazz & (1 << irq)) {
+ printf(" %s", irq_names[irq]);
+ if (!(s->imr_jazz & (1 << irq))) {
+ printf("(ignored)");
+ }
+ }
+ }
+ printf("\n");
+ }
+#endif
+
+ if (pending != 0)
+ qemu_irq_raise(s->jazz_bus_irq);
+ else
+ qemu_irq_lower(s->jazz_bus_irq);
+}
+
+static void rc4030_irq_jazz_request(void *opaque, int irq, int level)
+{
+ rc4030State *s = opaque;
+
+ if (level) {
+ s->isr_jazz |= 1 << irq;
+ } else {
+ s->isr_jazz &= ~(1 << irq);
+ }
+
+ update_jazz_irq(s);
+}
+
+static void rc4030_periodic_timer(void *opaque)
+{
+ rc4030State *s = opaque;
+
+ set_next_tick(s);
+ qemu_irq_raise(s->timer_irq);
+}
+
+static uint32_t jazzio_readw(void *opaque, target_phys_addr_t addr)
+{
+ rc4030State *s = opaque;
+ uint32_t val;
+ uint32_t irq;
+ addr &= 0xfff;
+
+ switch (addr) {
+ /* Local bus int source */
+ case 0x00: {
+ uint32_t pending = s->isr_jazz & s->imr_jazz;
+ val = 0;
+ irq = 0;
+ while (pending) {
+ if (pending & 1) {
+ DPRINTF("returning irq %s\n", irq_names[irq]);
+ val = (irq + 1) << 2;
+ break;
+ }
+ irq++;
+ pending >>= 1;
+ }
+ break;
+ }
+ /* Local bus int enable mask */
+ case 0x02:
+ val = s->imr_jazz;
+ break;
+ default:
+ RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr);
+ val = 0;
+ }
+
+ DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+
+ return val;
+}
+
+static uint32_t jazzio_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = jazzio_readw(opaque, addr & ~0x1);
+ return (v >> (8 * (addr & 0x1))) & 0xff;
+}
+
+static uint32_t jazzio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = jazzio_readw(opaque, addr);
+ v |= jazzio_readw(opaque, addr + 2) << 16;
+ return v;
+}
+
+static void jazzio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rc4030State *s = opaque;
+ addr &= 0xfff;
+
+ DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+
+ switch (addr) {
+ /* Local bus int enable mask */
+ case 0x02:
+ s->imr_jazz = val;
+ update_jazz_irq(s);
+ break;
+ default:
+ RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr);
+ break;
+ }
+}
+
+static void jazzio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ uint32_t old_val = jazzio_readw(opaque, addr & ~0x1);
+
+ switch (addr & 1) {
+ case 0:
+ val = val | (old_val & 0xff00);
+ break;
+ case 1:
+ val = (val << 8) | (old_val & 0x00ff);
+ break;
+ }
+ jazzio_writew(opaque, addr & ~0x1, val);
+}
+
+static void jazzio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ jazzio_writew(opaque, addr, val & 0xffff);
+ jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff);
+}
+
+static CPUReadMemoryFunc * const jazzio_read[3] = {
+ jazzio_readb,
+ jazzio_readw,
+ jazzio_readl,
+};
+
+static CPUWriteMemoryFunc * const jazzio_write[3] = {
+ jazzio_writeb,
+ jazzio_writew,
+ jazzio_writel,
+};
+
+static void rc4030_reset(void *opaque)
+{
+ rc4030State *s = opaque;
+ int i;
+
+ s->config = 0x410; /* some boards seem to accept 0x104 too */
+ s->revision = 1;
+ s->invalid_address_register = 0;
+
+ memset(s->dma_regs, 0, sizeof(s->dma_regs));
+ s->dma_tl_base = s->dma_tl_limit = 0;
+
+ s->remote_failed_address = s->memory_failed_address = 0;
+ s->cache_maint = 0;
+ s->cache_ptag = s->cache_ltag = 0;
+ s->cache_bmask = 0;
+
+ s->offset210 = 0x18186;
+ s->nvram_protect = 7;
+ for (i = 0; i < 15; i++)
+ s->rem_speed[i] = 7;
+ s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */
+ s->isr_jazz = 0;
+
+ s->itr = 0;
+
+ qemu_irq_lower(s->timer_irq);
+ qemu_irq_lower(s->jazz_bus_irq);
+}
+
+static int rc4030_load(QEMUFile *f, void *opaque, int version_id)
+{
+ rc4030State* s = opaque;
+ int i, j;
+
+ if (version_id != 2)
+ return -EINVAL;
+
+ s->config = qemu_get_be32(f);
+ s->invalid_address_register = qemu_get_be32(f);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 4; j++)
+ s->dma_regs[i][j] = qemu_get_be32(f);
+ s->dma_tl_base = qemu_get_be32(f);
+ s->dma_tl_limit = qemu_get_be32(f);
+ s->cache_maint = qemu_get_be32(f);
+ s->remote_failed_address = qemu_get_be32(f);
+ s->memory_failed_address = qemu_get_be32(f);
+ s->cache_ptag = qemu_get_be32(f);
+ s->cache_ltag = qemu_get_be32(f);
+ s->cache_bmask = qemu_get_be32(f);
+ s->offset210 = qemu_get_be32(f);
+ s->nvram_protect = qemu_get_be32(f);
+ for (i = 0; i < 15; i++)
+ s->rem_speed[i] = qemu_get_be32(f);
+ s->imr_jazz = qemu_get_be32(f);
+ s->isr_jazz = qemu_get_be32(f);
+ s->itr = qemu_get_be32(f);
+
+ set_next_tick(s);
+ update_jazz_irq(s);
+
+ return 0;
+}
+
+static void rc4030_save(QEMUFile *f, void *opaque)
+{
+ rc4030State* s = opaque;
+ int i, j;
+
+ qemu_put_be32(f, s->config);
+ qemu_put_be32(f, s->invalid_address_register);
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 4; j++)
+ qemu_put_be32(f, s->dma_regs[i][j]);
+ qemu_put_be32(f, s->dma_tl_base);
+ qemu_put_be32(f, s->dma_tl_limit);
+ qemu_put_be32(f, s->cache_maint);
+ qemu_put_be32(f, s->remote_failed_address);
+ qemu_put_be32(f, s->memory_failed_address);
+ qemu_put_be32(f, s->cache_ptag);
+ qemu_put_be32(f, s->cache_ltag);
+ qemu_put_be32(f, s->cache_bmask);
+ qemu_put_be32(f, s->offset210);
+ qemu_put_be32(f, s->nvram_protect);
+ for (i = 0; i < 15; i++)
+ qemu_put_be32(f, s->rem_speed[i]);
+ qemu_put_be32(f, s->imr_jazz);
+ qemu_put_be32(f, s->isr_jazz);
+ qemu_put_be32(f, s->itr);
+}
+
+void rc4030_dma_memory_rw(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write)
+{
+ rc4030State *s = opaque;
+ target_phys_addr_t entry_addr;
+ target_phys_addr_t phys_addr;
+ dma_pagetable_entry entry;
+ int index;
+ int ncpy, i;
+
+ i = 0;
+ for (;;) {
+ if (i == len) {
+ break;
+ }
+
+ ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1));
+ if (ncpy > len - i)
+ ncpy = len - i;
+
+ /* Get DMA translation table entry */
+ index = addr / DMA_PAGESIZE;
+ if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
+ break;
+ }
+ entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
+ /* XXX: not sure. should we really use only lowest bits? */
+ entry_addr &= 0x7fffffff;
+ cpu_physical_memory_rw(entry_addr, (uint8_t *)&entry, sizeof(entry), 0);
+
+ /* Read/write data at right place */
+ phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1));
+ cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
+
+ i += ncpy;
+ addr += ncpy;
+ }
+}
+
+static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
+{
+ rc4030State *s = opaque;
+ target_phys_addr_t dma_addr;
+ int dev_to_mem;
+
+ s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR);
+
+ /* Check DMA channel consistency */
+ dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1;
+ if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) ||
+ (is_write != dev_to_mem)) {
+ s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR;
+ s->nmi_interrupt |= 1 << n;
+ return;
+ }
+
+ /* Get start address and len */
+ if (len > s->dma_regs[n][DMA_REG_COUNT])
+ len = s->dma_regs[n][DMA_REG_COUNT];
+ dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
+
+ /* Read/write data at right place */
+ rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write);
+
+ s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
+ s->dma_regs[n][DMA_REG_COUNT] -= len;
+
+#ifdef DEBUG_RC4030_DMA
+ {
+ int i, j;
+ printf("rc4030 dma: Copying %d bytes %s host %p\n",
+ len, is_write ? "from" : "to", buf);
+ for (i = 0; i < len; i += 16) {
+ int n = 16;
+ if (n > len - i) {
+ n = len - i;
+ }
+ for (j = 0; j < n; j++)
+ printf("%02x ", buf[i + j]);
+ while (j++ < 16)
+ printf(" ");
+ printf("| ");
+ for (j = 0; j < n; j++)
+ printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
+ printf("\n");
+ }
+ }
+#endif
+}
+
+struct rc4030DMAState {
+ void *opaque;
+ int n;
+};
+
+void rc4030_dma_read(void *dma, uint8_t *buf, int len)
+{
+ rc4030_dma s = dma;
+ rc4030_do_dma(s->opaque, s->n, buf, len, 0);
+}
+
+void rc4030_dma_write(void *dma, uint8_t *buf, int len)
+{
+ rc4030_dma s = dma;
+ rc4030_do_dma(s->opaque, s->n, buf, len, 1);
+}
+
+static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
+{
+ rc4030_dma *s;
+ struct rc4030DMAState *p;
+ int i;
+
+ s = (rc4030_dma *)qemu_mallocz(sizeof(rc4030_dma) * n);
+ p = (struct rc4030DMAState *)qemu_mallocz(sizeof(struct rc4030DMAState) * n);
+ for (i = 0; i < n; i++) {
+ p->opaque = opaque;
+ p->n = i;
+ s[i] = p;
+ p++;
+ }
+ return s;
+}
+
+void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
+ qemu_irq **irqs, rc4030_dma **dmas)
+{
+ rc4030State *s;
+ int s_chipset, s_jazzio;
+
+ s = qemu_mallocz(sizeof(rc4030State));
+
+ *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16);
+ *dmas = rc4030_allocate_dmas(s, 4);
+
+ s->periodic_timer = qemu_new_timer(vm_clock, rc4030_periodic_timer, s);
+ s->timer_irq = timer;
+ s->jazz_bus_irq = jazz_bus;
+
+ qemu_register_reset(rc4030_reset, s);
+ register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s);
+ rc4030_reset(s);
+
+ s_chipset = cpu_register_io_memory(rc4030_read, rc4030_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0x80000000, 0x300, s_chipset);
+ s_jazzio = cpu_register_io_memory(jazzio_read, jazzio_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0xf0000000, 0x00001000, s_jazzio);
+
+ return s;
+}
diff --git a/hw/realview.c b/hw/realview.c
new file mode 100644
index 0000000..6eb6c6a
--- /dev/null
+++ b/hw/realview.c
@@ -0,0 +1,459 @@
+/*
+ * ARM RealView Baseboard System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "primecell.h"
+#include "devices.h"
+#include "pci.h"
+#include "usb-ohci.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "bitbang_i2c.h"
+#include "sysbus.h"
+#include "blockdev.h"
+
+#define SMP_BOOT_ADDR 0xe0000000
+
+typedef struct {
+ SysBusDevice busdev;
+ bitbang_i2c_interface *bitbang;
+ int out;
+ int in;
+} RealViewI2CState;
+
+static uint32_t realview_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+ RealViewI2CState *s = (RealViewI2CState *)opaque;
+
+ if (offset == 0) {
+ return (s->out & 1) | (s->in << 1);
+ } else {
+ hw_error("realview_i2c_read: Bad offset 0x%x\n", (int)offset);
+ return -1;
+ }
+}
+
+static void realview_i2c_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ RealViewI2CState *s = (RealViewI2CState *)opaque;
+
+ switch (offset) {
+ case 0:
+ s->out |= value & 3;
+ break;
+ case 4:
+ s->out &= ~value;
+ break;
+ default:
+ hw_error("realview_i2c_write: Bad offset 0x%x\n", (int)offset);
+ }
+ bitbang_i2c_set(s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0);
+ s->in = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0);
+}
+
+static CPUReadMemoryFunc * const realview_i2c_readfn[] = {
+ realview_i2c_read,
+ realview_i2c_read,
+ realview_i2c_read
+};
+
+static CPUWriteMemoryFunc * const realview_i2c_writefn[] = {
+ realview_i2c_write,
+ realview_i2c_write,
+ realview_i2c_write
+};
+
+static int realview_i2c_init(SysBusDevice *dev)
+{
+ RealViewI2CState *s = FROM_SYSBUS(RealViewI2CState, dev);
+ i2c_bus *bus;
+ int iomemtype;
+
+ bus = i2c_init_bus(&dev->qdev, "i2c");
+ s->bitbang = bitbang_i2c_init(bus);
+ iomemtype = cpu_register_io_memory(realview_i2c_readfn,
+ realview_i2c_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ return 0;
+}
+
+static SysBusDeviceInfo realview_i2c_info = {
+ .init = realview_i2c_init,
+ .qdev.name = "realview_i2c",
+ .qdev.size = sizeof(RealViewI2CState),
+};
+
+static void realview_register_devices(void)
+{
+ sysbus_register_withprop(&realview_i2c_info);
+}
+
+/* Board init. */
+
+static struct arm_boot_info realview_binfo = {
+ .smp_loader_start = SMP_BOOT_ADDR,
+};
+
+static void secondary_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ /* Set entry point for secondary CPUs. This assumes we're using
+ the init code from arm_boot.c. Real hardware resets all CPUs
+ the same. */
+ env->regs[15] = SMP_BOOT_ADDR;
+}
+
+/* The following two lists must be consistent. */
+enum realview_board_type {
+ BOARD_EB,
+ BOARD_EB_MPCORE,
+ BOARD_PB_A8,
+ BOARD_PBX_A9,
+};
+
+static const int realview_board_id[] = {
+ 0x33b,
+ 0x33b,
+ 0x769,
+ 0x76d
+};
+
+static void realview_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model,
+ enum realview_board_type board_type)
+{
+ CPUState *env = NULL;
+ ram_addr_t ram_offset;
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ qemu_irq *irqp;
+ qemu_irq pic[64];
+ PCIBus *pci_bus;
+ NICInfo *nd;
+ i2c_bus *i2c;
+ int n;
+ int done_nic = 0;
+ qemu_irq cpu_irq[4];
+ int is_mpcore = 0;
+ int is_pb = 0;
+ uint32_t proc_id = 0;
+ uint32_t sys_id;
+ ram_addr_t low_ram_size;
+
+ switch (board_type) {
+ case BOARD_EB:
+ break;
+ case BOARD_EB_MPCORE:
+ is_mpcore = 1;
+ break;
+ case BOARD_PB_A8:
+ is_pb = 1;
+ break;
+ case BOARD_PBX_A9:
+ is_mpcore = 1;
+ is_pb = 1;
+ break;
+ }
+ for (n = 0; n < smp_cpus; n++) {
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ irqp = arm_pic_init_cpu(env);
+ cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+ if (n > 0) {
+ qemu_register_reset(secondary_cpu_reset, env);
+ }
+ }
+ if (arm_feature(env, ARM_FEATURE_V7)) {
+ if (is_mpcore) {
+ proc_id = 0x0c000000;
+ } else {
+ proc_id = 0x0e000000;
+ }
+ } else if (arm_feature(env, ARM_FEATURE_V6K)) {
+ proc_id = 0x06000000;
+ } else if (arm_feature(env, ARM_FEATURE_V6)) {
+ proc_id = 0x04000000;
+ } else {
+ proc_id = 0x02000000;
+ }
+
+ if (is_pb && ram_size > 0x20000000) {
+ /* Core tile RAM. */
+ low_ram_size = ram_size - 0x20000000;
+ ram_size = 0x20000000;
+ ram_offset = qemu_ram_alloc(NULL, "realview.lowmem", low_ram_size);
+ cpu_register_physical_memory(0x20000000, low_ram_size,
+ ram_offset | IO_MEM_RAM);
+ }
+
+ ram_offset = qemu_ram_alloc(NULL, "realview.highmem", ram_size);
+ low_ram_size = ram_size;
+ if (low_ram_size > 0x10000000)
+ low_ram_size = 0x10000000;
+ /* SDRAM at address zero. */
+ cpu_register_physical_memory(0, low_ram_size, ram_offset | IO_MEM_RAM);
+ if (is_pb) {
+ /* And again at a high address. */
+ cpu_register_physical_memory(0x70000000, ram_size,
+ ram_offset | IO_MEM_RAM);
+ } else {
+ ram_size = low_ram_size;
+ }
+
+ sys_id = is_pb ? 0x01780500 : 0xc1400400;
+ arm_sysctl_init(0x10000000, sys_id, proc_id);
+
+ if (is_mpcore) {
+ dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore");
+ qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+ qdev_init_nofail(dev);
+ busdev = sysbus_from_qdev(dev);
+ if (is_pb) {
+ realview_binfo.smp_priv_base = 0x1f000000;
+ } else {
+ realview_binfo.smp_priv_base = 0x10100000;
+ }
+ sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base);
+ for (n = 0; n < smp_cpus; n++) {
+ sysbus_connect_irq(busdev, n, cpu_irq[n]);
+ }
+ } else {
+ uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000;
+ /* For now just create the nIRQ GIC, and ignore the others. */
+ dev = sysbus_create_simple("realview_gic", gic_addr, cpu_irq[0]);
+ }
+ for (n = 0; n < 64; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]);
+ sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]);
+
+ sysbus_create_simple("pl011", 0x10009000, pic[12]);
+ sysbus_create_simple("pl011", 0x1000a000, pic[13]);
+ sysbus_create_simple("pl011", 0x1000b000, pic[14]);
+ sysbus_create_simple("pl011", 0x1000c000, pic[15]);
+
+ /* DMA controller is optional, apparently. */
+ sysbus_create_simple("pl081", 0x10030000, pic[24]);
+
+ sysbus_create_simple("sp804", 0x10011000, pic[4]);
+ sysbus_create_simple("sp804", 0x10012000, pic[5]);
+
+ sysbus_create_simple("pl110_versatile", 0x10020000, pic[23]);
+
+ sysbus_create_varargs("pl181", 0x10005000, pic[17], pic[18], NULL);
+
+ sysbus_create_simple("pl031", 0x10017000, pic[10]);
+
+ if (!is_pb) {
+ dev = sysbus_create_varargs("realview_pci", 0x60000000,
+ pic[48], pic[49], pic[50], pic[51], NULL);
+ pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
+ if (usb_enabled) {
+ usb_ohci_init_pci(pci_bus, -1);
+ }
+ n = drive_get_max_bus(IF_SCSI);
+ while (n >= 0) {
+ pci_create_simple(pci_bus, -1, "lsi53c895a");
+ n--;
+ }
+ }
+ for(n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+
+ if ((!nd->model && !done_nic)
+ || strcmp(nd->model, is_pb ? "lan9118" : "smc91c111") == 0) {
+ if (is_pb) {
+ lan9118_init(nd, 0x4e000000, pic[28]);
+ } else {
+ smc91c111_init(nd, 0x4e000000, pic[28]);
+ }
+ done_nic = 1;
+ } else {
+ pci_nic_init_nofail(nd, "rtl8139", NULL);
+ }
+ }
+
+ dev = sysbus_create_simple("realview_i2c", 0x10002000, NULL);
+ i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ i2c_create_slave(i2c, "ds1338", 0x68);
+
+ /* Memory map for RealView Emulation Baseboard: */
+ /* 0x10000000 System registers. */
+ /* 0x10001000 System controller. */
+ /* 0x10002000 Two-Wire Serial Bus. */
+ /* 0x10003000 Reserved. */
+ /* 0x10004000 AACI. */
+ /* 0x10005000 MCI. */
+ /* 0x10006000 KMI0. */
+ /* 0x10007000 KMI1. */
+ /* 0x10008000 Character LCD. (EB) */
+ /* 0x10009000 UART0. */
+ /* 0x1000a000 UART1. */
+ /* 0x1000b000 UART2. */
+ /* 0x1000c000 UART3. */
+ /* 0x1000d000 SSPI. */
+ /* 0x1000e000 SCI. */
+ /* 0x1000f000 Reserved. */
+ /* 0x10010000 Watchdog. */
+ /* 0x10011000 Timer 0+1. */
+ /* 0x10012000 Timer 2+3. */
+ /* 0x10013000 GPIO 0. */
+ /* 0x10014000 GPIO 1. */
+ /* 0x10015000 GPIO 2. */
+ /* 0x10002000 Two-Wire Serial Bus - DVI. (PB) */
+ /* 0x10017000 RTC. */
+ /* 0x10018000 DMC. */
+ /* 0x10019000 PCI controller config. */
+ /* 0x10020000 CLCD. */
+ /* 0x10030000 DMA Controller. */
+ /* 0x10040000 GIC1. (EB) */
+ /* 0x10050000 GIC2. (EB) */
+ /* 0x10060000 GIC3. (EB) */
+ /* 0x10070000 GIC4. (EB) */
+ /* 0x10080000 SMC. */
+ /* 0x1e000000 GIC1. (PB) */
+ /* 0x1e001000 GIC2. (PB) */
+ /* 0x1e002000 GIC3. (PB) */
+ /* 0x1e003000 GIC4. (PB) */
+ /* 0x40000000 NOR flash. */
+ /* 0x44000000 DoC flash. */
+ /* 0x48000000 SRAM. */
+ /* 0x4c000000 Configuration flash. */
+ /* 0x4e000000 Ethernet. */
+ /* 0x4f000000 USB. */
+ /* 0x50000000 PISMO. */
+ /* 0x54000000 PISMO. */
+ /* 0x58000000 PISMO. */
+ /* 0x5c000000 PISMO. */
+ /* 0x60000000 PCI. */
+ /* 0x61000000 PCI Self Config. */
+ /* 0x62000000 PCI Config. */
+ /* 0x63000000 PCI IO. */
+ /* 0x64000000 PCI mem 0. */
+ /* 0x68000000 PCI mem 1. */
+ /* 0x6c000000 PCI mem 2. */
+
+ /* ??? Hack to map an additional page of ram for the secondary CPU
+ startup code. I guess this works on real hardware because the
+ BootROM happens to be in ROM/flash or in memory that isn't clobbered
+ until after Linux boots the secondary CPUs. */
+ ram_offset = qemu_ram_alloc(NULL, "realview.hack", 0x1000);
+ cpu_register_physical_memory(SMP_BOOT_ADDR, 0x1000,
+ ram_offset | IO_MEM_RAM);
+
+ realview_binfo.ram_size = ram_size;
+ realview_binfo.kernel_filename = kernel_filename;
+ realview_binfo.kernel_cmdline = kernel_cmdline;
+ realview_binfo.initrd_filename = initrd_filename;
+ realview_binfo.nb_cpus = smp_cpus;
+ realview_binfo.board_id = realview_board_id[board_type];
+ realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0);
+ arm_load_kernel(first_cpu, &realview_binfo);
+}
+
+static void realview_eb_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ if (!cpu_model) {
+ cpu_model = "arm926";
+ }
+ realview_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, BOARD_EB);
+}
+
+static void realview_eb_mpcore_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ if (!cpu_model) {
+ cpu_model = "arm11mpcore";
+ }
+ realview_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, BOARD_EB_MPCORE);
+}
+
+static void realview_pb_a8_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ if (!cpu_model) {
+ cpu_model = "cortex-a8";
+ }
+ realview_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, BOARD_PB_A8);
+}
+
+static void realview_pbx_a9_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ if (!cpu_model) {
+ cpu_model = "cortex-a9";
+ }
+ realview_init(ram_size, boot_device, kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, BOARD_PBX_A9);
+}
+
+static QEMUMachine realview_eb_machine = {
+ .name = "realview-eb",
+ .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)",
+ .init = realview_eb_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine realview_eb_mpcore_machine = {
+ .name = "realview-eb-mpcore",
+ .desc = "ARM RealView Emulation Baseboard (ARM11MPCore)",
+ .init = realview_eb_mpcore_init,
+ .use_scsi = 1,
+ .max_cpus = 4,
+};
+
+static QEMUMachine realview_pb_a8_machine = {
+ .name = "realview-pb-a8",
+ .desc = "ARM RealView Platform Baseboard for Cortex-A8",
+ .init = realview_pb_a8_init,
+};
+
+static QEMUMachine realview_pbx_a9_machine = {
+ .name = "realview-pbx-a9",
+ .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9",
+ .init = realview_pbx_a9_init,
+ .use_scsi = 1,
+ .max_cpus = 4,
+};
+
+static void realview_machine_init(void)
+{
+ qemu_register_machine(&realview_eb_machine);
+ qemu_register_machine(&realview_eb_mpcore_machine);
+ qemu_register_machine(&realview_pb_a8_machine);
+ qemu_register_machine(&realview_pbx_a9_machine);
+}
+
+machine_init(realview_machine_init);
+device_init(realview_register_devices)
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
new file mode 100644
index 0000000..db908b6
--- /dev/null
+++ b/hw/realview_gic.c
@@ -0,0 +1,79 @@
+/*
+ * ARM RealView Emulation Baseboard Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+
+#define GIC_NIRQ 96
+#define NCPU 1
+
+/* Only a single "CPU" interface is present. */
+static inline int
+gic_get_current_cpu(void)
+{
+ return 0;
+}
+
+#include "arm_gic.c"
+
+typedef struct {
+ gic_state gic;
+ int iomemtype;
+} RealViewGICState;
+
+static uint32_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset)
+{
+ gic_state *s = (gic_state *)opaque;
+ return gic_cpu_read(s, gic_get_current_cpu(), offset);
+}
+
+static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ gic_state *s = (gic_state *)opaque;
+ gic_cpu_write(s, gic_get_current_cpu(), offset, value);
+}
+
+static CPUReadMemoryFunc * const realview_gic_cpu_readfn[] = {
+ realview_gic_cpu_read,
+ realview_gic_cpu_read,
+ realview_gic_cpu_read
+};
+
+static CPUWriteMemoryFunc * const realview_gic_cpu_writefn[] = {
+ realview_gic_cpu_write,
+ realview_gic_cpu_write,
+ realview_gic_cpu_write
+};
+
+static void realview_gic_map(SysBusDevice *dev, target_phys_addr_t base)
+{
+ RealViewGICState *s = FROM_SYSBUSGIC(RealViewGICState, dev);
+ cpu_register_physical_memory(base, 0x1000, s->iomemtype);
+ cpu_register_physical_memory(base + 0x1000, 0x1000, s->gic.iomemtype);
+}
+
+static int realview_gic_init(SysBusDevice *dev)
+{
+ RealViewGICState *s = FROM_SYSBUSGIC(RealViewGICState, dev);
+
+ gic_init(&s->gic);
+ s->iomemtype = cpu_register_io_memory(realview_gic_cpu_readfn,
+ realview_gic_cpu_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio_cb(dev, 0x2000, realview_gic_map);
+ return 0;
+}
+
+static void realview_gic_register_devices(void)
+{
+ sysbus_register_dev("realview_gic", sizeof(RealViewGICState),
+ realview_gic_init);
+}
+
+device_init(realview_gic_register_devices)
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
new file mode 100644
index 0000000..a22530c
--- /dev/null
+++ b/hw/rtl8139.c
@@ -0,0 +1,3431 @@
+/**
+ * QEMU RTL8139 emulation
+ *
+ * Copyright (c) 2006 Igor Kovalenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+ * Modifications:
+ * 2006-Jan-28 Mark Malakanov : TSAD and CSCR implementation (for Windows driver)
+ *
+ * 2006-Apr-28 Juergen Lock : EEPROM emulation changes for FreeBSD driver
+ * HW revision ID changes for FreeBSD driver
+ *
+ * 2006-Jul-01 Igor Kovalenko : Implemented loopback mode for FreeBSD driver
+ * Corrected packet transfer reassembly routine for 8139C+ mode
+ * Rearranged debugging print statements
+ * Implemented PCI timer interrupt (disabled by default)
+ * Implemented Tally Counters, increased VM load/save version
+ * Implemented IP/TCP/UDP checksum task offloading
+ *
+ * 2006-Jul-04 Igor Kovalenko : Implemented TCP segmentation offloading
+ * Fixed MTU=1500 for produced ethernet frames
+ *
+ * 2006-Jul-09 Igor Kovalenko : Fixed TCP header length calculation while processing
+ * segmentation offloading
+ * Removed slirp.h dependency
+ * Added rx/tx buffer reset when enabling rx/tx operation
+ *
+ * 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only
+ * when strictly needed (required for for
+ * Darwin)
+ */
+
+#include "hw.h"
+#include "pci.h"
+#include "qemu-timer.h"
+#include "net.h"
+#include "loader.h"
+#include "sysemu.h"
+
+/* debug RTL8139 card */
+//#define DEBUG_RTL8139 1
+
+#define PCI_FREQUENCY 33000000L
+
+/* debug RTL8139 card C+ mode only */
+//#define DEBUG_RTL8139CP 1
+
+/* Calculate CRCs properly on Rx packets */
+#define RTL8139_CALCULATE_RXCRC 1
+
+#if defined(RTL8139_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#define SET_MASKED(input, mask, curr) \
+ ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
+
+/* arg % size for size which is a power of 2 */
+#define MOD2(input, size) \
+ ( ( input ) & ( size - 1 ) )
+
+#if defined (DEBUG_RTL8139)
+# define DEBUG_PRINT(x) do { printf x ; } while (0)
+#else
+# define DEBUG_PRINT(x)
+#endif
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+ MAC0 = 0, /* Ethernet hardware address. */
+ MAR0 = 8, /* Multicast filter. */
+ TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */
+ /* Dump Tally Conter control register(64bit). C+ mode only */
+ TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */
+ RxBuf = 0x30,
+ ChipCmd = 0x37,
+ RxBufPtr = 0x38,
+ RxBufAddr = 0x3A,
+ IntrMask = 0x3C,
+ IntrStatus = 0x3E,
+ TxConfig = 0x40,
+ RxConfig = 0x44,
+ Timer = 0x48, /* A general-purpose counter. */
+ RxMissed = 0x4C, /* 24 bits valid, write clears. */
+ Cfg9346 = 0x50,
+ Config0 = 0x51,
+ Config1 = 0x52,
+ FlashReg = 0x54,
+ MediaStatus = 0x58,
+ Config3 = 0x59,
+ Config4 = 0x5A, /* absent on RTL-8139A */
+ HltClk = 0x5B,
+ MultiIntr = 0x5C,
+ PCIRevisionID = 0x5E,
+ TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/
+ BasicModeCtrl = 0x62,
+ BasicModeStatus = 0x64,
+ NWayAdvert = 0x66,
+ NWayLPAR = 0x68,
+ NWayExpansion = 0x6A,
+ /* Undocumented registers, but required for proper operation. */
+ FIFOTMS = 0x70, /* FIFO Control and test. */
+ CSCR = 0x74, /* Chip Status and Configuration Register. */
+ PARA78 = 0x78,
+ PARA7c = 0x7c, /* Magic transceiver parameter register. */
+ Config5 = 0xD8, /* absent on RTL-8139A */
+ /* C+ mode */
+ TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */
+ RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */
+ CpCmd = 0xE0, /* C+ Command register (C+ mode only) */
+ IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */
+ RxRingAddrLO = 0xE4, /* 64-bit start addr of Rx ring */
+ RxRingAddrHI = 0xE8, /* 64-bit start addr of Rx ring */
+ TxThresh = 0xEC, /* Early Tx threshold */
+};
+
+enum ClearBitMasks {
+ MultiIntrClear = 0xF000,
+ ChipCmdClear = 0xE2,
+ Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+ CmdReset = 0x10,
+ CmdRxEnb = 0x08,
+ CmdTxEnb = 0x04,
+ RxBufEmpty = 0x01,
+};
+
+/* C+ mode */
+enum CplusCmdBits {
+ CPlusRxVLAN = 0x0040, /* enable receive VLAN detagging */
+ CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */
+ CPlusRxEnb = 0x0002,
+ CPlusTxEnb = 0x0001,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+ PCIErr = 0x8000,
+ PCSTimeout = 0x4000,
+ RxFIFOOver = 0x40,
+ RxUnderrun = 0x20,
+ RxOverflow = 0x10,
+ TxErr = 0x08,
+ TxOK = 0x04,
+ RxErr = 0x02,
+ RxOK = 0x01,
+
+ RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+ TxHostOwns = 0x2000,
+ TxUnderrun = 0x4000,
+ TxStatOK = 0x8000,
+ TxOutOfWindow = 0x20000000,
+ TxAborted = 0x40000000,
+ TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+ RxMulticast = 0x8000,
+ RxPhysical = 0x4000,
+ RxBroadcast = 0x2000,
+ RxBadSymbol = 0x0020,
+ RxRunt = 0x0010,
+ RxTooLong = 0x0008,
+ RxCRCErr = 0x0004,
+ RxBadAlign = 0x0002,
+ RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+ AcceptErr = 0x20,
+ AcceptRunt = 0x10,
+ AcceptBroadcast = 0x08,
+ AcceptMulticast = 0x04,
+ AcceptMyPhys = 0x02,
+ AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+ /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+ TxIFGShift = 24,
+ TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
+ TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
+ TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
+ TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */
+
+ TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */
+ TxClearAbt = (1 << 0), /* Clear abort (WO) */
+ TxDMAShift = 8, /* DMA burst value (0-7) is shifted this many bits */
+ TxRetryShift = 4, /* TXRR value (0-15) is shifted this many bits */
+
+ TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+
+/* Transmit Status of All Descriptors (TSAD) Register */
+enum TSAD_bits {
+ TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3
+ TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2
+ TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1
+ TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0
+ TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3
+ TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2
+ TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1
+ TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0
+ TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3
+ TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2
+ TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1
+ TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0
+ TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3
+ TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2
+ TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1
+ TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0
+};
+
+
+/* Bits in Config1 */
+enum Config1Bits {
+ Cfg1_PM_Enable = 0x01,
+ Cfg1_VPD_Enable = 0x02,
+ Cfg1_PIO = 0x04,
+ Cfg1_MMIO = 0x08,
+ LWAKE = 0x10, /* not on 8139, 8139A */
+ Cfg1_Driver_Load = 0x20,
+ Cfg1_LED0 = 0x40,
+ Cfg1_LED1 = 0x80,
+ SLEEP = (1 << 1), /* only on 8139, 8139A */
+ PWRDN = (1 << 0), /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+ Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */
+ Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+ Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+ Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */
+ Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */
+ Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+ Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */
+ Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+ LWPTN = (1 << 2), /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+ Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */
+ Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */
+ Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */
+ Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+ Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */
+ Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */
+ Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+ /* rx fifo threshold */
+ RxCfgFIFOShift = 13,
+ RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+ /* Max DMA burst */
+ RxCfgDMAShift = 8,
+ RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+ /* rx ring buffer length */
+ RxCfgRcv8K = 0,
+ RxCfgRcv16K = (1 << 11),
+ RxCfgRcv32K = (1 << 12),
+ RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+ /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+ RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+ Completely undocumented, but required to tune bad links on some boards. */
+/*
+enum CSCRBits {
+ CSCR_LinkOKBit = 0x0400,
+ CSCR_LinkChangeBit = 0x0800,
+ CSCR_LinkStatusBits = 0x0f000,
+ CSCR_LinkDownOffCmd = 0x003c0,
+ CSCR_LinkDownCmd = 0x0f3c0,
+*/
+enum CSCRBits {
+ CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */
+ CSCR_LD = 1<<9, /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/
+ CSCR_HEART_BIT = 1<<8, /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/
+ CSCR_JBEN = 1<<7, /* 1 = enable jabber function. 0 = disable jabber function, def 1*/
+ CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/
+ CSCR_F_Connect = 1<<5, /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/
+ CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/
+ CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/
+ CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/
+};
+
+enum Cfg9346Bits {
+ Cfg9346_Lock = 0x00,
+ Cfg9346_Unlock = 0xC0,
+};
+
+typedef enum {
+ CH_8139 = 0,
+ CH_8139_K,
+ CH_8139A,
+ CH_8139A_G,
+ CH_8139B,
+ CH_8130,
+ CH_8139C,
+ CH_8100,
+ CH_8100B_8139D,
+ CH_8101,
+} chip_t;
+
+enum chip_flags {
+ HasHltClk = (1 << 0),
+ HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+ (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+#define RTL8139_PCI_REVID_8139 0x10
+#define RTL8139_PCI_REVID_8139CPLUS 0x20
+
+#define RTL8139_PCI_REVID RTL8139_PCI_REVID_8139CPLUS
+
+/* Size is 64 * 16bit words */
+#define EEPROM_9346_ADDR_BITS 6
+#define EEPROM_9346_SIZE (1 << EEPROM_9346_ADDR_BITS)
+#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)
+
+enum Chip9346Operation
+{
+ Chip9346_op_mask = 0xc0, /* 10 zzzzzz */
+ Chip9346_op_read = 0x80, /* 10 AAAAAA */
+ Chip9346_op_write = 0x40, /* 01 AAAAAA D(15)..D(0) */
+ Chip9346_op_ext_mask = 0xf0, /* 11 zzzzzz */
+ Chip9346_op_write_enable = 0x30, /* 00 11zzzz */
+ Chip9346_op_write_all = 0x10, /* 00 01zzzz */
+ Chip9346_op_write_disable = 0x00, /* 00 00zzzz */
+};
+
+enum Chip9346Mode
+{
+ Chip9346_none = 0,
+ Chip9346_enter_command_mode,
+ Chip9346_read_command,
+ Chip9346_data_read, /* from output register */
+ Chip9346_data_write, /* to input register, then to contents at specified address */
+ Chip9346_data_write_all, /* to input register, then filling contents */
+};
+
+typedef struct EEprom9346
+{
+ uint16_t contents[EEPROM_9346_SIZE];
+ int mode;
+ uint32_t tick;
+ uint8_t address;
+ uint16_t input;
+ uint16_t output;
+
+ uint8_t eecs;
+ uint8_t eesk;
+ uint8_t eedi;
+ uint8_t eedo;
+} EEprom9346;
+
+typedef struct RTL8139TallyCounters
+{
+ /* Tally counters */
+ uint64_t TxOk;
+ uint64_t RxOk;
+ uint64_t TxERR;
+ uint32_t RxERR;
+ uint16_t MissPkt;
+ uint16_t FAE;
+ uint32_t Tx1Col;
+ uint32_t TxMCol;
+ uint64_t RxOkPhy;
+ uint64_t RxOkBrd;
+ uint32_t RxOkMul;
+ uint16_t TxAbt;
+ uint16_t TxUndrn;
+} RTL8139TallyCounters;
+
+/* Clears all tally counters */
+static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters);
+
+/* Writes tally counters to specified physical memory address */
+static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* counters);
+
+typedef struct RTL8139State {
+ PCIDevice dev;
+ uint8_t phys[8]; /* mac address */
+ uint8_t mult[8]; /* multicast mask array */
+
+ uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */
+ uint32_t TxAddr[4]; /* TxAddr0 */
+ uint32_t RxBuf; /* Receive buffer */
+ uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */
+ uint32_t RxBufPtr;
+ uint32_t RxBufAddr;
+
+ uint16_t IntrStatus;
+ uint16_t IntrMask;
+
+ uint32_t TxConfig;
+ uint32_t RxConfig;
+ uint32_t RxMissed;
+
+ uint16_t CSCR;
+
+ uint8_t Cfg9346;
+ uint8_t Config0;
+ uint8_t Config1;
+ uint8_t Config3;
+ uint8_t Config4;
+ uint8_t Config5;
+
+ uint8_t clock_enabled;
+ uint8_t bChipCmdState;
+
+ uint16_t MultiIntr;
+
+ uint16_t BasicModeCtrl;
+ uint16_t BasicModeStatus;
+ uint16_t NWayAdvert;
+ uint16_t NWayLPAR;
+ uint16_t NWayExpansion;
+
+ uint16_t CpCmd;
+ uint8_t TxThresh;
+
+ NICState *nic;
+ NICConf conf;
+ int rtl8139_mmio_io_addr;
+
+ /* C ring mode */
+ uint32_t currTxDesc;
+
+ /* C+ mode */
+ uint32_t cplus_enabled;
+
+ uint32_t currCPlusRxDesc;
+ uint32_t currCPlusTxDesc;
+
+ uint32_t RxRingAddrLO;
+ uint32_t RxRingAddrHI;
+
+ EEprom9346 eeprom;
+
+ uint32_t TCTR;
+ uint32_t TimerInt;
+ int64_t TCTR_base;
+
+ /* Tally counters */
+ RTL8139TallyCounters tally_counters;
+
+ /* Non-persistent data */
+ uint8_t *cplus_txbuffer;
+ int cplus_txbuffer_len;
+ int cplus_txbuffer_offset;
+
+ /* PCI interrupt timer */
+ QEMUTimer *timer;
+ int64_t TimerExpire;
+
+ /* Support migration to/from old versions */
+ int rtl8139_mmio_io_addr_dummy;
+} RTL8139State;
+
+static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time);
+
+static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
+{
+ DEBUG_PRINT(("RTL8139: eeprom command 0x%02x\n", command));
+
+ switch (command & Chip9346_op_mask)
+ {
+ case Chip9346_op_read:
+ {
+ eeprom->address = command & EEPROM_9346_ADDR_MASK;
+ eeprom->output = eeprom->contents[eeprom->address];
+ eeprom->eedo = 0;
+ eeprom->tick = 0;
+ eeprom->mode = Chip9346_data_read;
+ DEBUG_PRINT(("RTL8139: eeprom read from address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->output));
+ }
+ break;
+
+ case Chip9346_op_write:
+ {
+ eeprom->address = command & EEPROM_9346_ADDR_MASK;
+ eeprom->input = 0;
+ eeprom->tick = 0;
+ eeprom->mode = Chip9346_none; /* Chip9346_data_write */
+ DEBUG_PRINT(("RTL8139: eeprom begin write to address 0x%02x\n",
+ eeprom->address));
+ }
+ break;
+ default:
+ eeprom->mode = Chip9346_none;
+ switch (command & Chip9346_op_ext_mask)
+ {
+ case Chip9346_op_write_enable:
+ DEBUG_PRINT(("RTL8139: eeprom write enabled\n"));
+ break;
+ case Chip9346_op_write_all:
+ DEBUG_PRINT(("RTL8139: eeprom begin write all\n"));
+ break;
+ case Chip9346_op_write_disable:
+ DEBUG_PRINT(("RTL8139: eeprom write disabled\n"));
+ break;
+ }
+ break;
+ }
+}
+
+static void prom9346_shift_clock(EEprom9346 *eeprom)
+{
+ int bit = eeprom->eedi?1:0;
+
+ ++ eeprom->tick;
+
+ DEBUG_PRINT(("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi, eeprom->eedo));
+
+ switch (eeprom->mode)
+ {
+ case Chip9346_enter_command_mode:
+ if (bit)
+ {
+ eeprom->mode = Chip9346_read_command;
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ DEBUG_PRINT(("eeprom: +++ synchronized, begin command read\n"));
+ }
+ break;
+
+ case Chip9346_read_command:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 8)
+ {
+ prom9346_decode_command(eeprom, eeprom->input & 0xff);
+ }
+ break;
+
+ case Chip9346_data_read:
+ eeprom->eedo = (eeprom->output & 0x8000)?1:0;
+ eeprom->output <<= 1;
+ if (eeprom->tick == 16)
+ {
+#if 1
+ // the FreeBSD drivers (rl and re) don't explicitly toggle
+ // CS between reads (or does setting Cfg9346 to 0 count too?),
+ // so we need to enter wait-for-command state here
+ eeprom->mode = Chip9346_enter_command_mode;
+ eeprom->input = 0;
+ eeprom->tick = 0;
+
+ DEBUG_PRINT(("eeprom: +++ end of read, awaiting next command\n"));
+#else
+ // original behaviour
+ ++eeprom->address;
+ eeprom->address &= EEPROM_9346_ADDR_MASK;
+ eeprom->output = eeprom->contents[eeprom->address];
+ eeprom->tick = 0;
+
+ DEBUG_PRINT(("eeprom: +++ read next address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->output));
+#endif
+ }
+ break;
+
+ case Chip9346_data_write:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 16)
+ {
+ DEBUG_PRINT(("RTL8139: eeprom write to address 0x%02x data=0x%04x\n",
+ eeprom->address, eeprom->input));
+
+ eeprom->contents[eeprom->address] = eeprom->input;
+ eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ }
+ break;
+
+ case Chip9346_data_write_all:
+ eeprom->input = (eeprom->input << 1) | (bit & 1);
+ if (eeprom->tick == 16)
+ {
+ int i;
+ for (i = 0; i < EEPROM_9346_SIZE; i++)
+ {
+ eeprom->contents[i] = eeprom->input;
+ }
+ DEBUG_PRINT(("RTL8139: eeprom filled with data=0x%04x\n",
+ eeprom->input));
+
+ eeprom->mode = Chip9346_enter_command_mode;
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int prom9346_get_wire(RTL8139State *s)
+{
+ EEprom9346 *eeprom = &s->eeprom;
+ if (!eeprom->eecs)
+ return 0;
+
+ return eeprom->eedo;
+}
+
+/* FIXME: This should be merged into/replaced by eeprom93xx.c. */
+static void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi)
+{
+ EEprom9346 *eeprom = &s->eeprom;
+ uint8_t old_eecs = eeprom->eecs;
+ uint8_t old_eesk = eeprom->eesk;
+
+ eeprom->eecs = eecs;
+ eeprom->eesk = eesk;
+ eeprom->eedi = eedi;
+
+ DEBUG_PRINT(("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n",
+ eeprom->eecs, eeprom->eesk, eeprom->eedi, eeprom->eedo));
+
+ if (!old_eecs && eecs)
+ {
+ /* Synchronize start */
+ eeprom->tick = 0;
+ eeprom->input = 0;
+ eeprom->output = 0;
+ eeprom->mode = Chip9346_enter_command_mode;
+
+ DEBUG_PRINT(("=== eeprom: begin access, enter command mode\n"));
+ }
+
+ if (!eecs)
+ {
+ DEBUG_PRINT(("=== eeprom: end access\n"));
+ return;
+ }
+
+ if (!old_eesk && eesk)
+ {
+ /* SK front rules */
+ prom9346_shift_clock(eeprom);
+ }
+}
+
+static void rtl8139_update_irq(RTL8139State *s)
+{
+ int isr;
+ isr = (s->IntrStatus & s->IntrMask) & 0xffff;
+
+ DEBUG_PRINT(("RTL8139: Set IRQ to %d (%04x %04x)\n",
+ isr ? 1 : 0, s->IntrStatus, s->IntrMask));
+
+ qemu_set_irq(s->dev.irq[0], (isr != 0));
+}
+
+#define POLYNOMIAL 0x04c11db6
+
+/* From FreeBSD */
+/* XXX: optimize */
+static int compute_mcast_idx(const uint8_t *ep)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return (crc >> 26);
+}
+
+static int rtl8139_RxWrap(RTL8139State *s)
+{
+ /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */
+ return (s->RxConfig & (1 << 7));
+}
+
+static int rtl8139_receiver_enabled(RTL8139State *s)
+{
+ return s->bChipCmdState & CmdRxEnb;
+}
+
+static int rtl8139_transmitter_enabled(RTL8139State *s)
+{
+ return s->bChipCmdState & CmdTxEnb;
+}
+
+static int rtl8139_cp_receiver_enabled(RTL8139State *s)
+{
+ return s->CpCmd & CPlusRxEnb;
+}
+
+static int rtl8139_cp_transmitter_enabled(RTL8139State *s)
+{
+ return s->CpCmd & CPlusTxEnb;
+}
+
+static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
+{
+ if (s->RxBufAddr + size > s->RxBufferSize)
+ {
+ int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize);
+
+ /* write packet data */
+ if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s)))
+ {
+ DEBUG_PRINT((">>> RTL8139: rx packet wrapped in buffer at %d\n", size-wrapped));
+
+ if (size > wrapped)
+ {
+ cpu_physical_memory_write( s->RxBuf + s->RxBufAddr,
+ buf, size-wrapped );
+ }
+
+ /* reset buffer pointer */
+ s->RxBufAddr = 0;
+
+ cpu_physical_memory_write( s->RxBuf + s->RxBufAddr,
+ buf + (size-wrapped), wrapped );
+
+ s->RxBufAddr = wrapped;
+
+ return;
+ }
+ }
+
+ /* non-wrapping path or overwrapping enabled */
+ cpu_physical_memory_write( s->RxBuf + s->RxBufAddr, buf, size );
+
+ s->RxBufAddr += size;
+}
+
+#define MIN_BUF_SIZE 60
+static inline target_phys_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
+{
+#if TARGET_PHYS_ADDR_BITS > 32
+ return low | ((target_phys_addr_t)high << 32);
+#else
+ return low;
+#endif
+}
+
+static int rtl8139_can_receive(VLANClientState *nc)
+{
+ RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int avail;
+
+ /* Receive (drop) packets if card is disabled. */
+ if (!s->clock_enabled)
+ return 1;
+ if (!rtl8139_receiver_enabled(s))
+ return 1;
+
+ if (rtl8139_cp_receiver_enabled(s)) {
+ /* ??? Flow control not implemented in c+ mode.
+ This is a hack to work around slirp deficiencies anyway. */
+ return 1;
+ } else {
+ avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr,
+ s->RxBufferSize);
+ return (avail == 0 || avail >= 1514);
+ }
+}
+
+static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt)
+{
+ RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int size = size_;
+
+ uint32_t packet_header = 0;
+
+ uint8_t buf1[60];
+ static const uint8_t broadcast_macaddr[6] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ DEBUG_PRINT((">>> RTL8139: received len=%d\n", size));
+
+ /* test if board clock is stopped */
+ if (!s->clock_enabled)
+ {
+ DEBUG_PRINT(("RTL8139: stopped ==========================\n"));
+ return -1;
+ }
+
+ /* first check if receiver is enabled */
+
+ if (!rtl8139_receiver_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: receiver disabled ================\n"));
+ return -1;
+ }
+
+ /* XXX: check this */
+ if (s->RxConfig & AcceptAllPhys) {
+ /* promiscuous: receive all */
+ DEBUG_PRINT((">>> RTL8139: packet received in promiscuous mode\n"));
+
+ } else {
+ if (!memcmp(buf, broadcast_macaddr, 6)) {
+ /* broadcast address */
+ if (!(s->RxConfig & AcceptBroadcast))
+ {
+ DEBUG_PRINT((">>> RTL8139: broadcast packet rejected\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return size;
+ }
+
+ packet_header |= RxBroadcast;
+
+ DEBUG_PRINT((">>> RTL8139: broadcast packet received\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxOkBrd;
+
+ } else if (buf[0] & 0x01) {
+ /* multicast */
+ if (!(s->RxConfig & AcceptMulticast))
+ {
+ DEBUG_PRINT((">>> RTL8139: multicast packet rejected\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return size;
+ }
+
+ int mcast_idx = compute_mcast_idx(buf);
+
+ if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+ {
+ DEBUG_PRINT((">>> RTL8139: multicast address mismatch\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return size;
+ }
+
+ packet_header |= RxMulticast;
+
+ DEBUG_PRINT((">>> RTL8139: multicast packet received\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxOkMul;
+
+ } else if (s->phys[0] == buf[0] &&
+ s->phys[1] == buf[1] &&
+ s->phys[2] == buf[2] &&
+ s->phys[3] == buf[3] &&
+ s->phys[4] == buf[4] &&
+ s->phys[5] == buf[5]) {
+ /* match */
+ if (!(s->RxConfig & AcceptMyPhys))
+ {
+ DEBUG_PRINT((">>> RTL8139: rejecting physical address matching packet\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return size;
+ }
+
+ packet_header |= RxPhysical;
+
+ DEBUG_PRINT((">>> RTL8139: physical address matching packet received\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxOkPhy;
+
+ } else {
+
+ DEBUG_PRINT((">>> RTL8139: unknown packet\n"));
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+
+ return size;
+ }
+ }
+
+ /* if too small buffer, then expand it */
+ if (size < MIN_BUF_SIZE) {
+ memcpy(buf1, buf, size);
+ memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+ buf = buf1;
+ size = MIN_BUF_SIZE;
+ }
+
+ if (rtl8139_cp_receiver_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: in C+ Rx mode ================\n"));
+
+ /* begin C+ receiver mode */
+
+/* w0 ownership flag */
+#define CP_RX_OWN (1<<31)
+/* w0 end of ring flag */
+#define CP_RX_EOR (1<<30)
+/* w0 bits 0...12 : buffer size */
+#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1)
+/* w1 tag available flag */
+#define CP_RX_TAVA (1<<16)
+/* w1 bits 0...15 : VLAN tag */
+#define CP_RX_VLAN_TAG_MASK ((1<<16) - 1)
+/* w2 low 32bit of Rx buffer ptr */
+/* w3 high 32bit of Rx buffer ptr */
+
+ int descriptor = s->currCPlusRxDesc;
+ target_phys_addr_t cplus_rx_ring_desc;
+
+ cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI);
+ cplus_rx_ring_desc += 16 * descriptor;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode reading RX descriptor %d from host memory at %08x %08x = %016" PRIx64 "\n",
+ descriptor, s->RxRingAddrHI, s->RxRingAddrLO, (uint64_t)cplus_rx_ring_desc));
+
+ uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI;
+
+ cpu_physical_memory_read(cplus_rx_ring_desc, (uint8_t *)&val, 4);
+ rxdw0 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
+ rxdw1 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_rx_ring_desc+8, (uint8_t *)&val, 4);
+ rxbufLO = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_rx_ring_desc+12, (uint8_t *)&val, 4);
+ rxbufHI = le32_to_cpu(val);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode RX descriptor %d %08x %08x %08x %08x\n",
+ descriptor,
+ rxdw0, rxdw1, rxbufLO, rxbufHI));
+
+ if (!(rxdw0 & CP_RX_OWN))
+ {
+ DEBUG_PRINT(("RTL8139: C+ Rx mode : descriptor %d is owned by host\n", descriptor));
+
+ s->IntrStatus |= RxOverflow;
+ ++s->RxMissed;
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+ ++s->tally_counters.MissPkt;
+
+ rtl8139_update_irq(s);
+ return size_;
+ }
+
+ uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
+
+ /* TODO: scatter the packet over available receive ring descriptors space */
+
+ if (size+4 > rx_space)
+ {
+ DEBUG_PRINT(("RTL8139: C+ Rx mode : descriptor %d size %d received %d + 4\n",
+ descriptor, rx_space, size));
+
+ s->IntrStatus |= RxOverflow;
+ ++s->RxMissed;
+
+ /* update tally counter */
+ ++s->tally_counters.RxERR;
+ ++s->tally_counters.MissPkt;
+
+ rtl8139_update_irq(s);
+ return size_;
+ }
+
+ target_phys_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
+
+ /* receive/copy to target memory */
+ cpu_physical_memory_write( rx_addr, buf, size );
+
+ if (s->CpCmd & CPlusRxChkSum)
+ {
+ /* do some packet checksumming */
+ }
+
+ /* write checksum */
+#if defined (RTL8139_CALCULATE_RXCRC)
+ val = cpu_to_le32(crc32(0, buf, size));
+#else
+ val = 0;
+#endif
+ cpu_physical_memory_write( rx_addr+size, (uint8_t *)&val, 4);
+
+/* first segment of received packet flag */
+#define CP_RX_STATUS_FS (1<<29)
+/* last segment of received packet flag */
+#define CP_RX_STATUS_LS (1<<28)
+/* multicast packet flag */
+#define CP_RX_STATUS_MAR (1<<26)
+/* physical-matching packet flag */
+#define CP_RX_STATUS_PAM (1<<25)
+/* broadcast packet flag */
+#define CP_RX_STATUS_BAR (1<<24)
+/* runt packet flag */
+#define CP_RX_STATUS_RUNT (1<<19)
+/* crc error flag */
+#define CP_RX_STATUS_CRC (1<<18)
+/* IP checksum error flag */
+#define CP_RX_STATUS_IPF (1<<15)
+/* UDP checksum error flag */
+#define CP_RX_STATUS_UDPF (1<<14)
+/* TCP checksum error flag */
+#define CP_RX_STATUS_TCPF (1<<13)
+
+ /* transfer ownership to target */
+ rxdw0 &= ~CP_RX_OWN;
+
+ /* set first segment bit */
+ rxdw0 |= CP_RX_STATUS_FS;
+
+ /* set last segment bit */
+ rxdw0 |= CP_RX_STATUS_LS;
+
+ /* set received packet type flags */
+ if (packet_header & RxBroadcast)
+ rxdw0 |= CP_RX_STATUS_BAR;
+ if (packet_header & RxMulticast)
+ rxdw0 |= CP_RX_STATUS_MAR;
+ if (packet_header & RxPhysical)
+ rxdw0 |= CP_RX_STATUS_PAM;
+
+ /* set received size */
+ rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK;
+ rxdw0 |= (size+4);
+
+ /* reset VLAN tag flag */
+ rxdw1 &= ~CP_RX_TAVA;
+
+ /* update ring data */
+ val = cpu_to_le32(rxdw0);
+ cpu_physical_memory_write(cplus_rx_ring_desc, (uint8_t *)&val, 4);
+ val = cpu_to_le32(rxdw1);
+ cpu_physical_memory_write(cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
+
+ /* update tally counter */
+ ++s->tally_counters.RxOk;
+
+ /* seek to next Rx descriptor */
+ if (rxdw0 & CP_RX_EOR)
+ {
+ s->currCPlusRxDesc = 0;
+ }
+ else
+ {
+ ++s->currCPlusRxDesc;
+ }
+
+ DEBUG_PRINT(("RTL8139: done C+ Rx mode ----------------\n"));
+
+ }
+ else
+ {
+ DEBUG_PRINT(("RTL8139: in ring Rx mode ================\n"));
+
+ /* begin ring receiver mode */
+ int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize);
+
+ /* if receiver buffer is empty then avail == 0 */
+
+ if (avail != 0 && size + 8 >= avail)
+ {
+ DEBUG_PRINT(("rx overflow: rx buffer length %d head 0x%04x read 0x%04x === available 0x%04x need 0x%04x\n",
+ s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8));
+
+ s->IntrStatus |= RxOverflow;
+ ++s->RxMissed;
+ rtl8139_update_irq(s);
+ return size_;
+ }
+
+ packet_header |= RxStatusOK;
+
+ packet_header |= (((size+4) << 16) & 0xffff0000);
+
+ /* write header */
+ uint32_t val = cpu_to_le32(packet_header);
+
+ rtl8139_write_buffer(s, (uint8_t *)&val, 4);
+
+ rtl8139_write_buffer(s, buf, size);
+
+ /* write checksum */
+#if defined (RTL8139_CALCULATE_RXCRC)
+ val = cpu_to_le32(crc32(0, buf, size));
+#else
+ val = 0;
+#endif
+
+ rtl8139_write_buffer(s, (uint8_t *)&val, 4);
+
+ /* correct buffer write pointer */
+ s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize);
+
+ /* now we can signal we have received something */
+
+ DEBUG_PRINT((" received: rx buffer length %d head 0x%04x read 0x%04x\n",
+ s->RxBufferSize, s->RxBufAddr, s->RxBufPtr));
+ }
+
+ s->IntrStatus |= RxOK;
+
+ if (do_interrupt)
+ {
+ rtl8139_update_irq(s);
+ }
+
+ return size_;
+}
+
+static ssize_t rtl8139_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ return rtl8139_do_receive(nc, buf, size, 1);
+}
+
+static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
+{
+ s->RxBufferSize = bufferSize;
+ s->RxBufPtr = 0;
+ s->RxBufAddr = 0;
+}
+
+static void rtl8139_reset(DeviceState *d)
+{
+ RTL8139State *s = container_of(d, RTL8139State, dev.qdev);
+ int i;
+
+ /* restore MAC address */
+ memcpy(s->phys, s->conf.macaddr.a, 6);
+
+ /* reset interrupt mask */
+ s->IntrStatus = 0;
+ s->IntrMask = 0;
+
+ rtl8139_update_irq(s);
+
+ /* prepare eeprom */
+ s->eeprom.contents[0] = 0x8129;
+#if 1
+ // PCI vendor and device ID should be mirrored here
+ s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK;
+ s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139;
+#endif
+
+ s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8;
+ s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
+ s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
+
+ /* mark all status registers as owned by host */
+ for (i = 0; i < 4; ++i)
+ {
+ s->TxStatus[i] = TxHostOwns;
+ }
+
+ s->currTxDesc = 0;
+ s->currCPlusRxDesc = 0;
+ s->currCPlusTxDesc = 0;
+
+ s->RxRingAddrLO = 0;
+ s->RxRingAddrHI = 0;
+
+ s->RxBuf = 0;
+
+ rtl8139_reset_rxring(s, 8192);
+
+ /* ACK the reset */
+ s->TxConfig = 0;
+
+#if 0
+// s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139 HasHltClk
+ s->clock_enabled = 0;
+#else
+ s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
+ s->clock_enabled = 1;
+#endif
+
+ s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;
+
+ /* set initial state data */
+ s->Config0 = 0x0; /* No boot ROM */
+ s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
+ s->Config3 = 0x1; /* fast back-to-back compatible */
+ s->Config5 = 0x0;
+
+ s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
+
+ s->CpCmd = 0x0; /* reset C+ mode */
+ s->cplus_enabled = 0;
+
+
+// s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
+// s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
+ s->BasicModeCtrl = 0x1000; // autonegotiation
+
+ s->BasicModeStatus = 0x7809;
+ //s->BasicModeStatus |= 0x0040; /* UTP medium */
+ s->BasicModeStatus |= 0x0020; /* autonegotiation completed */
+ s->BasicModeStatus |= 0x0004; /* link is up */
+
+ s->NWayAdvert = 0x05e1; /* all modes, full duplex */
+ s->NWayLPAR = 0x05e1; /* all modes, full duplex */
+ s->NWayExpansion = 0x0001; /* autonegotiation supported */
+
+ /* also reset timer and disable timer interrupt */
+ s->TCTR = 0;
+ s->TimerInt = 0;
+ s->TCTR_base = 0;
+
+ /* reset tally counters */
+ RTL8139TallyCounters_clear(&s->tally_counters);
+}
+
+static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
+{
+ counters->TxOk = 0;
+ counters->RxOk = 0;
+ counters->TxERR = 0;
+ counters->RxERR = 0;
+ counters->MissPkt = 0;
+ counters->FAE = 0;
+ counters->Tx1Col = 0;
+ counters->TxMCol = 0;
+ counters->RxOkPhy = 0;
+ counters->RxOkBrd = 0;
+ counters->RxOkMul = 0;
+ counters->TxAbt = 0;
+ counters->TxUndrn = 0;
+}
+
+static void RTL8139TallyCounters_physical_memory_write(target_phys_addr_t tc_addr, RTL8139TallyCounters* tally_counters)
+{
+ uint16_t val16;
+ uint32_t val32;
+ uint64_t val64;
+
+ val64 = cpu_to_le64(tally_counters->TxOk);
+ cpu_physical_memory_write(tc_addr + 0, (uint8_t *)&val64, 8);
+
+ val64 = cpu_to_le64(tally_counters->RxOk);
+ cpu_physical_memory_write(tc_addr + 8, (uint8_t *)&val64, 8);
+
+ val64 = cpu_to_le64(tally_counters->TxERR);
+ cpu_physical_memory_write(tc_addr + 16, (uint8_t *)&val64, 8);
+
+ val32 = cpu_to_le32(tally_counters->RxERR);
+ cpu_physical_memory_write(tc_addr + 24, (uint8_t *)&val32, 4);
+
+ val16 = cpu_to_le16(tally_counters->MissPkt);
+ cpu_physical_memory_write(tc_addr + 28, (uint8_t *)&val16, 2);
+
+ val16 = cpu_to_le16(tally_counters->FAE);
+ cpu_physical_memory_write(tc_addr + 30, (uint8_t *)&val16, 2);
+
+ val32 = cpu_to_le32(tally_counters->Tx1Col);
+ cpu_physical_memory_write(tc_addr + 32, (uint8_t *)&val32, 4);
+
+ val32 = cpu_to_le32(tally_counters->TxMCol);
+ cpu_physical_memory_write(tc_addr + 36, (uint8_t *)&val32, 4);
+
+ val64 = cpu_to_le64(tally_counters->RxOkPhy);
+ cpu_physical_memory_write(tc_addr + 40, (uint8_t *)&val64, 8);
+
+ val64 = cpu_to_le64(tally_counters->RxOkBrd);
+ cpu_physical_memory_write(tc_addr + 48, (uint8_t *)&val64, 8);
+
+ val32 = cpu_to_le32(tally_counters->RxOkMul);
+ cpu_physical_memory_write(tc_addr + 56, (uint8_t *)&val32, 4);
+
+ val16 = cpu_to_le16(tally_counters->TxAbt);
+ cpu_physical_memory_write(tc_addr + 60, (uint8_t *)&val16, 2);
+
+ val16 = cpu_to_le16(tally_counters->TxUndrn);
+ cpu_physical_memory_write(tc_addr + 62, (uint8_t *)&val16, 2);
+}
+
+/* Loads values of tally counters from VM state file */
+
+static const VMStateDescription vmstate_tally_counters = {
+ .name = "tally_counters",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(TxOk, RTL8139TallyCounters),
+ VMSTATE_UINT64(RxOk, RTL8139TallyCounters),
+ VMSTATE_UINT64(TxERR, RTL8139TallyCounters),
+ VMSTATE_UINT32(RxERR, RTL8139TallyCounters),
+ VMSTATE_UINT16(MissPkt, RTL8139TallyCounters),
+ VMSTATE_UINT16(FAE, RTL8139TallyCounters),
+ VMSTATE_UINT32(Tx1Col, RTL8139TallyCounters),
+ VMSTATE_UINT32(TxMCol, RTL8139TallyCounters),
+ VMSTATE_UINT64(RxOkPhy, RTL8139TallyCounters),
+ VMSTATE_UINT64(RxOkBrd, RTL8139TallyCounters),
+ VMSTATE_UINT16(TxAbt, RTL8139TallyCounters),
+ VMSTATE_UINT16(TxUndrn, RTL8139TallyCounters),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: ChipCmd write val=0x%08x\n", val));
+
+ if (val & CmdReset)
+ {
+ DEBUG_PRINT(("RTL8139: ChipCmd reset\n"));
+ rtl8139_reset(&s->dev.qdev);
+ }
+ if (val & CmdRxEnb)
+ {
+ DEBUG_PRINT(("RTL8139: ChipCmd enable receiver\n"));
+
+ s->currCPlusRxDesc = 0;
+ }
+ if (val & CmdTxEnb)
+ {
+ DEBUG_PRINT(("RTL8139: ChipCmd enable transmitter\n"));
+
+ s->currCPlusTxDesc = 0;
+ }
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xe3, s->bChipCmdState);
+
+ /* Deassert reset pin before next read */
+ val &= ~CmdReset;
+
+ s->bChipCmdState = val;
+}
+
+static int rtl8139_RxBufferEmpty(RTL8139State *s)
+{
+ int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize);
+
+ if (unread != 0)
+ {
+ DEBUG_PRINT(("RTL8139: receiver buffer data available 0x%04x\n", unread));
+ return 0;
+ }
+
+ DEBUG_PRINT(("RTL8139: receiver buffer is empty\n"));
+
+ return 1;
+}
+
+static uint32_t rtl8139_ChipCmd_read(RTL8139State *s)
+{
+ uint32_t ret = s->bChipCmdState;
+
+ if (rtl8139_RxBufferEmpty(s))
+ ret |= RxBufEmpty;
+
+ DEBUG_PRINT(("RTL8139: ChipCmd read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xffff;
+
+ DEBUG_PRINT(("RTL8139C+ command register write(w) val=0x%04x\n", val));
+
+ s->cplus_enabled = 1;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xff84, s->CpCmd);
+
+ s->CpCmd = val;
+}
+
+static uint32_t rtl8139_CpCmd_read(RTL8139State *s)
+{
+ uint32_t ret = s->CpCmd;
+
+ DEBUG_PRINT(("RTL8139C+ command register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139C+ IntrMitigate register write(w) val=0x%04x\n", val));
+}
+
+static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s)
+{
+ uint32_t ret = 0;
+
+ DEBUG_PRINT(("RTL8139C+ IntrMitigate register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static int rtl8139_config_writeable(RTL8139State *s)
+{
+ if (s->Cfg9346 & Cfg9346_Unlock)
+ {
+ return 1;
+ }
+
+ DEBUG_PRINT(("RTL8139: Configuration registers are write-protected\n"));
+
+ return 0;
+}
+
+static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xffff;
+
+ DEBUG_PRINT(("RTL8139: BasicModeCtrl register write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ uint32_t mask = 0x4cff;
+
+ if (1 || !rtl8139_config_writeable(s))
+ {
+ /* Speed setting and autonegotiation enable bits are read-only */
+ mask |= 0x3000;
+ /* Duplex mode setting is read-only */
+ mask |= 0x0100;
+ }
+
+ val = SET_MASKED(val, mask, s->BasicModeCtrl);
+
+ s->BasicModeCtrl = val;
+}
+
+static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s)
+{
+ uint32_t ret = s->BasicModeCtrl;
+
+ DEBUG_PRINT(("RTL8139: BasicModeCtrl register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xffff;
+
+ DEBUG_PRINT(("RTL8139: BasicModeStatus register write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xff3f, s->BasicModeStatus);
+
+ s->BasicModeStatus = val;
+}
+
+static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s)
+{
+ uint32_t ret = s->BasicModeStatus;
+
+ DEBUG_PRINT(("RTL8139: BasicModeStatus register read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Cfg9346 write val=0x%02x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x31, s->Cfg9346);
+
+ uint32_t opmode = val & 0xc0;
+ uint32_t eeprom_val = val & 0xf;
+
+ if (opmode == 0x80) {
+ /* eeprom access */
+ int eecs = (eeprom_val & 0x08)?1:0;
+ int eesk = (eeprom_val & 0x04)?1:0;
+ int eedi = (eeprom_val & 0x02)?1:0;
+ prom9346_set_wire(s, eecs, eesk, eedi);
+ } else if (opmode == 0x40) {
+ /* Reset. */
+ val = 0;
+ rtl8139_reset(&s->dev.qdev);
+ }
+
+ s->Cfg9346 = val;
+}
+
+static uint32_t rtl8139_Cfg9346_read(RTL8139State *s)
+{
+ uint32_t ret = s->Cfg9346;
+
+ uint32_t opmode = ret & 0xc0;
+
+ if (opmode == 0x80)
+ {
+ /* eeprom access */
+ int eedo = prom9346_get_wire(s);
+ if (eedo)
+ {
+ ret |= 0x01;
+ }
+ else
+ {
+ ret &= ~0x01;
+ }
+ }
+
+ DEBUG_PRINT(("RTL8139: Cfg9346 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config0_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config0 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xf8, s->Config0);
+
+ s->Config0 = val;
+}
+
+static uint32_t rtl8139_Config0_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config0;
+
+ DEBUG_PRINT(("RTL8139: Config0 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config1_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config1 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xC, s->Config1);
+
+ s->Config1 = val;
+}
+
+static uint32_t rtl8139_Config1_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config1;
+
+ DEBUG_PRINT(("RTL8139: Config1 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config3_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config3 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x8F, s->Config3);
+
+ s->Config3 = val;
+}
+
+static uint32_t rtl8139_Config3_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config3;
+
+ DEBUG_PRINT(("RTL8139: Config3 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config4_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config4 write val=0x%02x\n", val));
+
+ if (!rtl8139_config_writeable(s))
+ return;
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x0a, s->Config4);
+
+ s->Config4 = val;
+}
+
+static uint32_t rtl8139_Config4_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config4;
+
+ DEBUG_PRINT(("RTL8139: Config4 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_Config5_write(RTL8139State *s, uint32_t val)
+{
+ val &= 0xff;
+
+ DEBUG_PRINT(("RTL8139: Config5 write val=0x%02x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x80, s->Config5);
+
+ s->Config5 = val;
+}
+
+static uint32_t rtl8139_Config5_read(RTL8139State *s)
+{
+ uint32_t ret = s->Config5;
+
+ DEBUG_PRINT(("RTL8139: Config5 read val=0x%02x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
+{
+ if (!rtl8139_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: transmitter disabled; no TxConfig write val=0x%08x\n", val));
+ return;
+ }
+
+ DEBUG_PRINT(("RTL8139: TxConfig write val=0x%08x\n", val));
+
+ val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
+
+ s->TxConfig = val;
+}
+
+static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139C TxConfig via write(b) val=0x%02x\n", val));
+
+ uint32_t tc = s->TxConfig;
+ tc &= 0xFFFFFF00;
+ tc |= (val & 0x000000FF);
+ rtl8139_TxConfig_write(s, tc);
+}
+
+static uint32_t rtl8139_TxConfig_read(RTL8139State *s)
+{
+ uint32_t ret = s->TxConfig;
+
+ DEBUG_PRINT(("RTL8139: TxConfig read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: RxConfig write val=0x%08x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xf0fc0040, s->RxConfig);
+
+ s->RxConfig = val;
+
+ /* reset buffer size and read/write pointers */
+ rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3));
+
+ DEBUG_PRINT(("RTL8139: RxConfig write reset buffer size to %d\n", s->RxBufferSize));
+}
+
+static uint32_t rtl8139_RxConfig_read(RTL8139State *s)
+{
+ uint32_t ret = s->RxConfig;
+
+ DEBUG_PRINT(("RTL8139: RxConfig read val=0x%08x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size, int do_interrupt)
+{
+ if (!size)
+ {
+ DEBUG_PRINT(("RTL8139: +++ empty ethernet frame\n"));
+ return;
+ }
+
+ if (TxLoopBack == (s->TxConfig & TxLoopBack))
+ {
+ DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n"));
+ rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt);
+ }
+ else
+ {
+ qemu_send_packet(&s->nic->nc, buf, size);
+ }
+}
+
+static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
+{
+ if (!rtl8139_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: transmitter disabled\n",
+ descriptor));
+ return 0;
+ }
+
+ if (s->TxStatus[descriptor] & TxHostOwns)
+ {
+ DEBUG_PRINT(("RTL8139: +++ cannot transmit from descriptor %d: owned by host (%08x)\n",
+ descriptor, s->TxStatus[descriptor]));
+ return 0;
+ }
+
+ DEBUG_PRINT(("RTL8139: +++ transmitting from descriptor %d\n", descriptor));
+
+ int txsize = s->TxStatus[descriptor] & 0x1fff;
+ uint8_t txbuffer[0x2000];
+
+ DEBUG_PRINT(("RTL8139: +++ transmit reading %d bytes from host memory at 0x%08x\n",
+ txsize, s->TxAddr[descriptor]));
+
+ cpu_physical_memory_read(s->TxAddr[descriptor], txbuffer, txsize);
+
+ /* Mark descriptor as transferred */
+ s->TxStatus[descriptor] |= TxHostOwns;
+ s->TxStatus[descriptor] |= TxStatOK;
+
+ rtl8139_transfer_frame(s, txbuffer, txsize, 0);
+
+ DEBUG_PRINT(("RTL8139: +++ transmitted %d bytes from descriptor %d\n", txsize, descriptor));
+
+ /* update interrupt */
+ s->IntrStatus |= TxOK;
+ rtl8139_update_irq(s);
+
+ return 1;
+}
+
+/* structures and macros for task offloading */
+typedef struct ip_header
+{
+ uint8_t ip_ver_len; /* version and header length */
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
+ uint32_t ip_src,ip_dst; /* source and dest address */
+} ip_header;
+
+#define IP_HEADER_VERSION_4 4
+#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf)
+#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)
+
+typedef struct tcp_header
+{
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
+ uint32_t th_seq; /* sequence number */
+ uint32_t th_ack; /* acknowledgement number */
+ uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
+} tcp_header;
+
+typedef struct udp_header
+{
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ uint16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
+} udp_header;
+
+typedef struct ip_pseudo_header
+{
+ uint32_t ip_src;
+ uint32_t ip_dst;
+ uint8_t zeros;
+ uint8_t ip_proto;
+ uint16_t ip_payload;
+} ip_pseudo_header;
+
+#define IP_PROTO_TCP 6
+#define IP_PROTO_UDP 17
+
+#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2)
+#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
+#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags))
+
+#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
+
+#define TCP_FLAG_FIN 0x01
+#define TCP_FLAG_PUSH 0x08
+
+/* produces ones' complement sum of data */
+static uint16_t ones_complement_sum(uint8_t *data, size_t len)
+{
+ uint32_t result = 0;
+
+ for (; len > 1; data+=2, len-=2)
+ {
+ result += *(uint16_t*)data;
+ }
+
+ /* add the remainder byte */
+ if (len)
+ {
+ uint8_t odd[2] = {*data, 0};
+ result += *(uint16_t*)odd;
+ }
+
+ while (result>>16)
+ result = (result & 0xffff) + (result >> 16);
+
+ return result;
+}
+
+static uint16_t ip_checksum(void *data, size_t len)
+{
+ return ~ones_complement_sum((uint8_t*)data, len);
+}
+
+static int rtl8139_cplus_transmit_one(RTL8139State *s)
+{
+ if (!rtl8139_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode: transmitter disabled\n"));
+ return 0;
+ }
+
+ if (!rtl8139_cp_transmitter_enabled(s))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode: C+ transmitter disabled\n"));
+ return 0 ;
+ }
+
+ int descriptor = s->currCPlusTxDesc;
+
+ target_phys_addr_t cplus_tx_ring_desc =
+ rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]);
+
+ /* Normal priority ring */
+ cplus_tx_ring_desc += 16 * descriptor;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode reading TX descriptor %d from host memory at %08x0x%08x = 0x%8lx\n",
+ descriptor, s->TxAddr[1], s->TxAddr[0], cplus_tx_ring_desc));
+
+ uint32_t val, txdw0,txdw1,txbufLO,txbufHI;
+
+ cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4);
+ txdw0 = le32_to_cpu(val);
+ /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */
+ cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4);
+ txdw1 = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4);
+ txbufLO = le32_to_cpu(val);
+ cpu_physical_memory_read(cplus_tx_ring_desc+12, (uint8_t *)&val, 4);
+ txbufHI = le32_to_cpu(val);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TX descriptor %d %08x %08x %08x %08x\n",
+ descriptor,
+ txdw0, txdw1, txbufLO, txbufHI));
+
+ /* TODO: the following discard cast should clean clang analyzer output */
+ (void)txdw1;
+
+/* w0 ownership flag */
+#define CP_TX_OWN (1<<31)
+/* w0 end of ring flag */
+#define CP_TX_EOR (1<<30)
+/* first segment of received packet flag */
+#define CP_TX_FS (1<<29)
+/* last segment of received packet flag */
+#define CP_TX_LS (1<<28)
+/* large send packet flag */
+#define CP_TX_LGSEN (1<<27)
+/* large send MSS mask, bits 16...25 */
+#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1)
+
+/* IP checksum offload flag */
+#define CP_TX_IPCS (1<<18)
+/* UDP checksum offload flag */
+#define CP_TX_UDPCS (1<<17)
+/* TCP checksum offload flag */
+#define CP_TX_TCPCS (1<<16)
+
+/* w0 bits 0...15 : buffer size */
+#define CP_TX_BUFFER_SIZE (1<<16)
+#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1)
+/* w1 tag available flag */
+#define CP_RX_TAGC (1<<17)
+/* w1 bits 0...15 : VLAN tag */
+#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1)
+/* w2 low 32bit of Rx buffer ptr */
+/* w3 high 32bit of Rx buffer ptr */
+
+/* set after transmission */
+/* FIFO underrun flag */
+#define CP_TX_STATUS_UNF (1<<25)
+/* transmit error summary flag, valid if set any of three below */
+#define CP_TX_STATUS_TES (1<<23)
+/* out-of-window collision flag */
+#define CP_TX_STATUS_OWC (1<<22)
+/* link failure flag */
+#define CP_TX_STATUS_LNKF (1<<21)
+/* excessive collisions flag */
+#define CP_TX_STATUS_EXC (1<<20)
+
+ if (!(txdw0 & CP_TX_OWN))
+ {
+ DEBUG_PRINT(("RTL8139: C+ Tx mode : descriptor %d is owned by host\n", descriptor));
+ return 0 ;
+ }
+
+ DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : transmitting from descriptor %d\n", descriptor));
+
+ if (txdw0 & CP_TX_FS)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is first segment descriptor\n", descriptor));
+
+ /* reset internal buffer offset */
+ s->cplus_txbuffer_offset = 0;
+ }
+
+ int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK;
+ target_phys_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI);
+
+ /* make sure we have enough space to assemble the packet */
+ if (!s->cplus_txbuffer)
+ {
+ s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE;
+ s->cplus_txbuffer = qemu_malloc(s->cplus_txbuffer_len);
+ s->cplus_txbuffer_offset = 0;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len));
+ }
+
+ while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len)
+ {
+ s->cplus_txbuffer_len += CP_TX_BUFFER_SIZE;
+ s->cplus_txbuffer = qemu_realloc(s->cplus_txbuffer, s->cplus_txbuffer_len);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space changed to %d\n", s->cplus_txbuffer_len));
+ }
+
+ if (!s->cplus_txbuffer)
+ {
+ /* out of memory */
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmiter failed to reallocate %d bytes\n", s->cplus_txbuffer_len));
+
+ /* update tally counter */
+ ++s->tally_counters.TxERR;
+ ++s->tally_counters.TxAbt;
+
+ return 0;
+ }
+
+ /* append more data to the packet */
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmit reading %d bytes from host memory at %016" PRIx64 " to offset %d\n",
+ txsize, (uint64_t)tx_addr, s->cplus_txbuffer_offset));
+
+ cpu_physical_memory_read(tx_addr, s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize);
+ s->cplus_txbuffer_offset += txsize;
+
+ /* seek to next Rx descriptor */
+ if (txdw0 & CP_TX_EOR)
+ {
+ s->currCPlusTxDesc = 0;
+ }
+ else
+ {
+ ++s->currCPlusTxDesc;
+ if (s->currCPlusTxDesc >= 64)
+ s->currCPlusTxDesc = 0;
+ }
+
+ /* transfer ownership to target */
+ txdw0 &= ~CP_RX_OWN;
+
+ /* reset error indicator bits */
+ txdw0 &= ~CP_TX_STATUS_UNF;
+ txdw0 &= ~CP_TX_STATUS_TES;
+ txdw0 &= ~CP_TX_STATUS_OWC;
+ txdw0 &= ~CP_TX_STATUS_LNKF;
+ txdw0 &= ~CP_TX_STATUS_EXC;
+
+ /* update ring data */
+ val = cpu_to_le32(txdw0);
+ cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4);
+ /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */
+// val = cpu_to_le32(txdw1);
+// cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4);
+
+ /* Now decide if descriptor being processed is holding the last segment of packet */
+ if (txdw0 & CP_TX_LS)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ Tx mode : descriptor %d is last segment descriptor\n", descriptor));
+
+ /* can transfer fully assembled packet */
+
+ uint8_t *saved_buffer = s->cplus_txbuffer;
+ int saved_size = s->cplus_txbuffer_offset;
+ int saved_buffer_len = s->cplus_txbuffer_len;
+
+ /* reset the card space to protect from recursive call */
+ s->cplus_txbuffer = NULL;
+ s->cplus_txbuffer_offset = 0;
+ s->cplus_txbuffer_len = 0;
+
+ if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task checksum\n"));
+
+ #define ETH_P_IP 0x0800 /* Internet Protocol packet */
+ #define ETH_HLEN 14
+ #define ETH_MTU 1500
+
+ /* ip packet header */
+ ip_header *ip = NULL;
+ int hlen = 0;
+ uint8_t ip_protocol = 0;
+ uint16_t ip_data_len = 0;
+
+ uint8_t *eth_payload_data = NULL;
+ size_t eth_payload_len = 0;
+
+ int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
+ if (proto == ETH_P_IP)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode has IP packet\n"));
+
+ /* not aligned */
+ eth_payload_data = saved_buffer + ETH_HLEN;
+ eth_payload_len = saved_size - ETH_HLEN;
+
+ ip = (ip_header*)eth_payload_data;
+
+ if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode packet has bad IP version %d expected %d\n", IP_HEADER_VERSION(ip), IP_HEADER_VERSION_4));
+ ip = NULL;
+ } else {
+ hlen = IP_HEADER_LENGTH(ip);
+ ip_protocol = ip->ip_p;
+ ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
+ }
+ }
+
+ if (ip)
+ {
+ if (txdw0 & CP_TX_IPCS)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode need IP checksum\n"));
+
+ if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
+ /* bad packet header len */
+ /* or packet too short */
+ }
+ else
+ {
+ ip->ip_sum = 0;
+ ip->ip_sum = ip_checksum(ip, hlen);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode IP header len=%d checksum=%04x\n", hlen, ip->ip_sum));
+ }
+ }
+
+ if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
+ {
+#if defined (DEBUG_RTL8139)
+ int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
+#endif
+ DEBUG_PRINT(("RTL8139: +++ C+ mode offloaded task TSO MTU=%d IP data %d frame data %d specified MSS=%d\n",
+ ETH_MTU, ip_data_len, saved_size - ETH_HLEN, large_send_mss));
+
+ int tcp_send_offset = 0;
+ int send_count = 0;
+
+ /* maximum IP header length is 60 bytes */
+ uint8_t saved_ip_header[60];
+
+ /* save IP header template; data area is used in tcp checksum calculation */
+ memcpy(saved_ip_header, eth_payload_data, hlen);
+
+ /* a placeholder for checksum calculation routine in tcp case */
+ uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
+ // size_t data_to_checksum_len = eth_payload_len - hlen + 12;
+
+ /* pointer to TCP header */
+ tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
+
+ int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
+
+ /* ETH_MTU = ip header len + tcp header len + payload */
+ int tcp_data_len = ip_data_len - tcp_hlen;
+ int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP data len %d TCP hlen %d TCP data len %d TCP chunk size %d\n",
+ ip_data_len, tcp_hlen, tcp_data_len, tcp_chunk_size));
+
+ /* note the cycle below overwrites IP header data,
+ but restores it from saved_ip_header before sending packet */
+
+ int is_last_frame = 0;
+
+ for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
+ {
+ uint16_t chunk_size = tcp_chunk_size;
+
+ /* check if this is the last frame */
+ if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
+ {
+ is_last_frame = 1;
+ chunk_size = tcp_data_len - tcp_send_offset;
+ }
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP seqno %08x\n", be32_to_cpu(p_tcp_hdr->th_seq)));
+
+ /* add 4 TCP pseudoheader fields */
+ /* copy IP source and destination fields */
+ memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO calculating TCP checksum for packet with %d bytes data\n", tcp_hlen + chunk_size));
+
+ if (tcp_send_offset)
+ {
+ memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
+ }
+
+ /* keep PUSH and FIN flags only for the last frame */
+ if (!is_last_frame)
+ {
+ TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
+ }
+
+ /* recalculate TCP checksum */
+ ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+ p_tcpip_hdr->zeros = 0;
+ p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
+ p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
+
+ p_tcp_hdr->th_sum = 0;
+
+ int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO TCP checksum %04x\n", tcp_checksum));
+
+ p_tcp_hdr->th_sum = tcp_checksum;
+
+ /* restore IP header */
+ memcpy(eth_payload_data, saved_ip_header, hlen);
+
+ /* set IP data length and recalculate IP checksum */
+ ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
+
+ /* increment IP id for subsequent frames */
+ ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
+
+ ip->ip_sum = 0;
+ ip->ip_sum = ip_checksum(eth_payload_data, hlen);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO IP header len=%d checksum=%04x\n", hlen, ip->ip_sum));
+
+ int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TSO transferring packet size %d\n", tso_send_size));
+ rtl8139_transfer_frame(s, saved_buffer, tso_send_size, 0);
+
+ /* add transferred count to TCP sequence number */
+ p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
+ ++send_count;
+ }
+
+ /* Stop sending this frame */
+ saved_size = 0;
+ }
+ else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode need TCP or UDP checksum\n"));
+
+ /* maximum IP header length is 60 bytes */
+ uint8_t saved_ip_header[60];
+ memcpy(saved_ip_header, eth_payload_data, hlen);
+
+ uint8_t *data_to_checksum = eth_payload_data + hlen - 12;
+ // size_t data_to_checksum_len = eth_payload_len - hlen + 12;
+
+ /* add 4 TCP pseudoheader fields */
+ /* copy IP source and destination fields */
+ memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+ if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode calculating TCP checksum for packet with %d bytes data\n", ip_data_len));
+
+ ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+ p_tcpip_hdr->zeros = 0;
+ p_tcpip_hdr->ip_proto = IP_PROTO_TCP;
+ p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+ tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
+
+ p_tcp_hdr->th_sum = 0;
+
+ int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode TCP checksum %04x\n", tcp_checksum));
+
+ p_tcp_hdr->th_sum = tcp_checksum;
+ }
+ else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode calculating UDP checksum for packet with %d bytes data\n", ip_data_len));
+
+ ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
+ p_udpip_hdr->zeros = 0;
+ p_udpip_hdr->ip_proto = IP_PROTO_UDP;
+ p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+ udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
+
+ p_udp_hdr->uh_sum = 0;
+
+ int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+ DEBUG_PRINT(("RTL8139: +++ C+ mode UDP checksum %04x\n", udp_checksum));
+
+ p_udp_hdr->uh_sum = udp_checksum;
+ }
+
+ /* restore IP header */
+ memcpy(eth_payload_data, saved_ip_header, hlen);
+ }
+ }
+ }
+
+ /* update tally counter */
+ ++s->tally_counters.TxOk;
+
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmitting %d bytes packet\n", saved_size));
+
+ rtl8139_transfer_frame(s, saved_buffer, saved_size, 1);
+
+ /* restore card space if there was no recursion and reset offset */
+ if (!s->cplus_txbuffer)
+ {
+ s->cplus_txbuffer = saved_buffer;
+ s->cplus_txbuffer_len = saved_buffer_len;
+ s->cplus_txbuffer_offset = 0;
+ }
+ else
+ {
+ qemu_free(saved_buffer);
+ }
+ }
+ else
+ {
+ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission continue to next descriptor\n"));
+ }
+
+ return 1;
+}
+
+static void rtl8139_cplus_transmit(RTL8139State *s)
+{
+ int txcount = 0;
+
+ while (rtl8139_cplus_transmit_one(s))
+ {
+ ++txcount;
+ }
+
+ /* Mark transfer completed */
+ if (!txcount)
+ {
+ DEBUG_PRINT(("RTL8139: C+ mode : transmitter queue stalled, current TxDesc = %d\n",
+ s->currCPlusTxDesc));
+ }
+ else
+ {
+ /* update interrupt status */
+ s->IntrStatus |= TxOK;
+ rtl8139_update_irq(s);
+ }
+}
+
+static void rtl8139_transmit(RTL8139State *s)
+{
+ int descriptor = s->currTxDesc, txcount = 0;
+
+ /*while*/
+ if (rtl8139_transmit_one(s, descriptor))
+ {
+ ++s->currTxDesc;
+ s->currTxDesc %= 4;
+ ++txcount;
+ }
+
+ /* Mark transfer completed */
+ if (!txcount)
+ {
+ DEBUG_PRINT(("RTL8139: transmitter queue stalled, current TxDesc = %d\n", s->currTxDesc));
+ }
+}
+
+static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val)
+{
+
+ int descriptor = txRegOffset/4;
+
+ /* handle C+ transmit mode register configuration */
+
+ if (s->cplus_enabled)
+ {
+ DEBUG_PRINT(("RTL8139C+ DTCCR write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor));
+
+ /* handle Dump Tally Counters command */
+ s->TxStatus[descriptor] = val;
+
+ if (descriptor == 0 && (val & 0x8))
+ {
+ target_phys_addr_t tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]);
+
+ /* dump tally counters to specified memory location */
+ RTL8139TallyCounters_physical_memory_write( tc_addr, &s->tally_counters);
+
+ /* mark dump completed */
+ s->TxStatus[0] &= ~0x8;
+ }
+
+ return;
+ }
+
+ DEBUG_PRINT(("RTL8139: TxStatus write offset=0x%x val=0x%08x descriptor=%d\n", txRegOffset, val, descriptor));
+
+ /* mask only reserved bits */
+ val &= ~0xff00c000; /* these bits are reset on write */
+ val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
+
+ s->TxStatus[descriptor] = val;
+
+ /* attempt to start transmission */
+ rtl8139_transmit(s);
+}
+
+static uint32_t rtl8139_TxStatus_read(RTL8139State *s, uint32_t txRegOffset)
+{
+ uint32_t ret = s->TxStatus[txRegOffset/4];
+
+ DEBUG_PRINT(("RTL8139: TxStatus read offset=0x%x val=0x%08x\n", txRegOffset, ret));
+
+ return ret;
+}
+
+static uint16_t rtl8139_TSAD_read(RTL8139State *s)
+{
+ uint16_t ret = 0;
+
+ /* Simulate TSAD, it is read only anyway */
+
+ ret = ((s->TxStatus[3] & TxStatOK )?TSAD_TOK3:0)
+ |((s->TxStatus[2] & TxStatOK )?TSAD_TOK2:0)
+ |((s->TxStatus[1] & TxStatOK )?TSAD_TOK1:0)
+ |((s->TxStatus[0] & TxStatOK )?TSAD_TOK0:0)
+
+ |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0)
+ |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0)
+ |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0)
+ |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0)
+
+ |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0)
+ |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0)
+ |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0)
+ |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0)
+
+ |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0)
+ |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0)
+ |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0)
+ |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ;
+
+
+ DEBUG_PRINT(("RTL8139: TSAD read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static uint16_t rtl8139_CSCR_read(RTL8139State *s)
+{
+ uint16_t ret = s->CSCR;
+
+ DEBUG_PRINT(("RTL8139: CSCR read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val));
+
+ s->TxAddr[txAddrOffset/4] = val;
+}
+
+static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset)
+{
+ uint32_t ret = s->TxAddr[txAddrOffset/4];
+
+ DEBUG_PRINT(("RTL8139: TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret));
+
+ return ret;
+}
+
+static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: RxBufPtr write val=0x%04x\n", val));
+
+ /* this value is off by 16 */
+ s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize);
+
+ DEBUG_PRINT((" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n",
+ s->RxBufferSize, s->RxBufAddr, s->RxBufPtr));
+}
+
+static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s)
+{
+ /* this value is off by 16 */
+ uint32_t ret = s->RxBufPtr - 0x10;
+
+ DEBUG_PRINT(("RTL8139: RxBufPtr read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s)
+{
+ /* this value is NOT off by 16 */
+ uint32_t ret = s->RxBufAddr;
+
+ DEBUG_PRINT(("RTL8139: RxBufAddr read val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: RxBuf write val=0x%08x\n", val));
+
+ s->RxBuf = val;
+
+ /* may need to reset rxring here */
+}
+
+static uint32_t rtl8139_RxBuf_read(RTL8139State *s)
+{
+ uint32_t ret = s->RxBuf;
+
+ DEBUG_PRINT(("RTL8139: RxBuf read val=0x%08x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: IntrMask write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0x1e00, s->IntrMask);
+
+ s->IntrMask = val;
+
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+ rtl8139_update_irq(s);
+
+}
+
+static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
+{
+ uint32_t ret = s->IntrMask;
+
+ DEBUG_PRINT(("RTL8139: IntrMask read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: IntrStatus write(w) val=0x%04x\n", val));
+
+#if 0
+
+ /* writing to ISR has no effect */
+
+ return;
+
+#else
+ uint16_t newStatus = s->IntrStatus & ~val;
+
+ /* mask unwriteable bits */
+ newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);
+
+ /* writing 1 to interrupt status register bit clears it */
+ s->IntrStatus = 0;
+ rtl8139_update_irq(s);
+
+ s->IntrStatus = newStatus;
+ /*
+ * Computing if we miss an interrupt here is not that correct but
+ * considered that we should have had already an interrupt
+ * and probably emulated is slower is better to assume this resetting was
+ * done before testing on previous rtl8139_update_irq lead to IRQ loosing
+ */
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+ rtl8139_update_irq(s);
+
+#endif
+}
+
+static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
+{
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+
+ uint32_t ret = s->IntrStatus;
+
+ DEBUG_PRINT(("RTL8139: IntrStatus read(w) val=0x%04x\n", ret));
+
+#if 0
+
+ /* reading ISR clears all interrupts */
+ s->IntrStatus = 0;
+
+ rtl8139_update_irq(s);
+
+#endif
+
+ return ret;
+}
+
+static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val)
+{
+ DEBUG_PRINT(("RTL8139: MultiIntr write(w) val=0x%04x\n", val));
+
+ /* mask unwriteable bits */
+ val = SET_MASKED(val, 0xf000, s->MultiIntr);
+
+ s->MultiIntr = val;
+}
+
+static uint32_t rtl8139_MultiIntr_read(RTL8139State *s)
+{
+ uint32_t ret = s->MultiIntr;
+
+ DEBUG_PRINT(("RTL8139: MultiIntr read(w) val=0x%04x\n", ret));
+
+ return ret;
+}
+
+static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+ RTL8139State *s = opaque;
+
+ addr &= 0xff;
+
+ switch (addr)
+ {
+ case MAC0 ... MAC0+5:
+ s->phys[addr - MAC0] = val;
+ break;
+ case MAC0+6 ... MAC0+7:
+ /* reserved */
+ break;
+ case MAR0 ... MAR0+7:
+ s->mult[addr - MAR0] = val;
+ break;
+ case ChipCmd:
+ rtl8139_ChipCmd_write(s, val);
+ break;
+ case Cfg9346:
+ rtl8139_Cfg9346_write(s, val);
+ break;
+ case TxConfig: /* windows driver sometimes writes using byte-lenth call */
+ rtl8139_TxConfig_writeb(s, val);
+ break;
+ case Config0:
+ rtl8139_Config0_write(s, val);
+ break;
+ case Config1:
+ rtl8139_Config1_write(s, val);
+ break;
+ case Config3:
+ rtl8139_Config3_write(s, val);
+ break;
+ case Config4:
+ rtl8139_Config4_write(s, val);
+ break;
+ case Config5:
+ rtl8139_Config5_write(s, val);
+ break;
+ case MediaStatus:
+ /* ignore */
+ DEBUG_PRINT(("RTL8139: not implemented write(b) to MediaStatus val=0x%02x\n", val));
+ break;
+
+ case HltClk:
+ DEBUG_PRINT(("RTL8139: HltClk write val=0x%08x\n", val));
+ if (val == 'R')
+ {
+ s->clock_enabled = 1;
+ }
+ else if (val == 'H')
+ {
+ s->clock_enabled = 0;
+ }
+ break;
+
+ case TxThresh:
+ DEBUG_PRINT(("RTL8139C+ TxThresh write(b) val=0x%02x\n", val));
+ s->TxThresh = val;
+ break;
+
+ case TxPoll:
+ DEBUG_PRINT(("RTL8139C+ TxPoll write(b) val=0x%02x\n", val));
+ if (val & (1 << 7))
+ {
+ DEBUG_PRINT(("RTL8139C+ TxPoll high priority transmission (not implemented)\n"));
+ //rtl8139_cplus_transmit(s);
+ }
+ if (val & (1 << 6))
+ {
+ DEBUG_PRINT(("RTL8139C+ TxPoll normal priority transmission\n"));
+ rtl8139_cplus_transmit(s);
+ }
+
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: not implemented write(b) addr=0x%x val=0x%02x\n", addr, val));
+ break;
+ }
+}
+
+static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+ RTL8139State *s = opaque;
+
+ addr &= 0xfe;
+
+ switch (addr)
+ {
+ case IntrMask:
+ rtl8139_IntrMask_write(s, val);
+ break;
+
+ case IntrStatus:
+ rtl8139_IntrStatus_write(s, val);
+ break;
+
+ case MultiIntr:
+ rtl8139_MultiIntr_write(s, val);
+ break;
+
+ case RxBufPtr:
+ rtl8139_RxBufPtr_write(s, val);
+ break;
+
+ case BasicModeCtrl:
+ rtl8139_BasicModeCtrl_write(s, val);
+ break;
+ case BasicModeStatus:
+ rtl8139_BasicModeStatus_write(s, val);
+ break;
+ case NWayAdvert:
+ DEBUG_PRINT(("RTL8139: NWayAdvert write(w) val=0x%04x\n", val));
+ s->NWayAdvert = val;
+ break;
+ case NWayLPAR:
+ DEBUG_PRINT(("RTL8139: forbidden NWayLPAR write(w) val=0x%04x\n", val));
+ break;
+ case NWayExpansion:
+ DEBUG_PRINT(("RTL8139: NWayExpansion write(w) val=0x%04x\n", val));
+ s->NWayExpansion = val;
+ break;
+
+ case CpCmd:
+ rtl8139_CpCmd_write(s, val);
+ break;
+
+ case IntrMitigate:
+ rtl8139_IntrMitigate_write(s, val);
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport write(w) addr=0x%x val=0x%04x via write(b)\n", addr, val));
+
+ rtl8139_io_writeb(opaque, addr, val & 0xff);
+ rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ break;
+ }
+}
+
+static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time)
+{
+ int64_t pci_time, next_time;
+ uint32_t low_pci;
+
+ DEBUG_PRINT(("RTL8139: entered rtl8139_set_next_tctr_time\n"));
+
+ if (s->TimerExpire && current_time >= s->TimerExpire) {
+ s->IntrStatus |= PCSTimeout;
+ rtl8139_update_irq(s);
+ }
+
+ /* Set QEMU timer only if needed that is
+ * - TimerInt <> 0 (we have a timer)
+ * - mask = 1 (we want an interrupt timer)
+ * - irq = 0 (irq is not already active)
+ * If any of above change we need to compute timer again
+ * Also we must check if timer is passed without QEMU timer
+ */
+ s->TimerExpire = 0;
+ if (!s->TimerInt) {
+ return;
+ }
+
+ pci_time = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
+ get_ticks_per_sec());
+ low_pci = pci_time & 0xffffffff;
+ pci_time = pci_time - low_pci + s->TimerInt;
+ if (low_pci >= s->TimerInt) {
+ pci_time += 0x100000000LL;
+ }
+ next_time = s->TCTR_base + muldiv64(pci_time, get_ticks_per_sec(),
+ PCI_FREQUENCY);
+ s->TimerExpire = next_time;
+
+ if ((s->IntrMask & PCSTimeout) != 0 && (s->IntrStatus & PCSTimeout) == 0) {
+ qemu_mod_timer(s->timer, next_time);
+ }
+}
+
+static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+ RTL8139State *s = opaque;
+
+ addr &= 0xfc;
+
+ switch (addr)
+ {
+ case RxMissed:
+ DEBUG_PRINT(("RTL8139: RxMissed clearing on write\n"));
+ s->RxMissed = 0;
+ break;
+
+ case TxConfig:
+ rtl8139_TxConfig_write(s, val);
+ break;
+
+ case RxConfig:
+ rtl8139_RxConfig_write(s, val);
+ break;
+
+ case TxStatus0 ... TxStatus0+4*4-1:
+ rtl8139_TxStatus_write(s, addr-TxStatus0, val);
+ break;
+
+ case TxAddr0 ... TxAddr0+4*4-1:
+ rtl8139_TxAddr_write(s, addr-TxAddr0, val);
+ break;
+
+ case RxBuf:
+ rtl8139_RxBuf_write(s, val);
+ break;
+
+ case RxRingAddrLO:
+ DEBUG_PRINT(("RTL8139: C+ RxRing low bits write val=0x%08x\n", val));
+ s->RxRingAddrLO = val;
+ break;
+
+ case RxRingAddrHI:
+ DEBUG_PRINT(("RTL8139: C+ RxRing high bits write val=0x%08x\n", val));
+ s->RxRingAddrHI = val;
+ break;
+
+ case Timer:
+ DEBUG_PRINT(("RTL8139: TCTR Timer reset on write\n"));
+ s->TCTR_base = qemu_get_clock(vm_clock);
+ rtl8139_set_next_tctr_time(s, s->TCTR_base);
+ break;
+
+ case FlashReg:
+ DEBUG_PRINT(("RTL8139: FlashReg TimerInt write val=0x%08x\n", val));
+ if (s->TimerInt != val) {
+ s->TimerInt = val;
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+ }
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport write(l) addr=0x%x val=0x%08x via write(b)\n", addr, val));
+ rtl8139_io_writeb(opaque, addr, val & 0xff);
+ rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+ break;
+ }
+}
+
+static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
+{
+ RTL8139State *s = opaque;
+ int ret;
+
+ addr &= 0xff;
+
+ switch (addr)
+ {
+ case MAC0 ... MAC0+5:
+ ret = s->phys[addr - MAC0];
+ break;
+ case MAC0+6 ... MAC0+7:
+ ret = 0;
+ break;
+ case MAR0 ... MAR0+7:
+ ret = s->mult[addr - MAR0];
+ break;
+ case ChipCmd:
+ ret = rtl8139_ChipCmd_read(s);
+ break;
+ case Cfg9346:
+ ret = rtl8139_Cfg9346_read(s);
+ break;
+ case Config0:
+ ret = rtl8139_Config0_read(s);
+ break;
+ case Config1:
+ ret = rtl8139_Config1_read(s);
+ break;
+ case Config3:
+ ret = rtl8139_Config3_read(s);
+ break;
+ case Config4:
+ ret = rtl8139_Config4_read(s);
+ break;
+ case Config5:
+ ret = rtl8139_Config5_read(s);
+ break;
+
+ case MediaStatus:
+ ret = 0xd0;
+ DEBUG_PRINT(("RTL8139: MediaStatus read 0x%x\n", ret));
+ break;
+
+ case HltClk:
+ ret = s->clock_enabled;
+ DEBUG_PRINT(("RTL8139: HltClk read 0x%x\n", ret));
+ break;
+
+ case PCIRevisionID:
+ ret = RTL8139_PCI_REVID;
+ DEBUG_PRINT(("RTL8139: PCI Revision ID read 0x%x\n", ret));
+ break;
+
+ case TxThresh:
+ ret = s->TxThresh;
+ DEBUG_PRINT(("RTL8139C+ TxThresh read(b) val=0x%02x\n", ret));
+ break;
+
+ case 0x43: /* Part of TxConfig register. Windows driver tries to read it */
+ ret = s->TxConfig >> 24;
+ DEBUG_PRINT(("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret));
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: not implemented read(b) addr=0x%x\n", addr));
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
+{
+ RTL8139State *s = opaque;
+ uint32_t ret;
+
+ addr &= 0xfe; /* mask lower bit */
+
+ switch (addr)
+ {
+ case IntrMask:
+ ret = rtl8139_IntrMask_read(s);
+ break;
+
+ case IntrStatus:
+ ret = rtl8139_IntrStatus_read(s);
+ break;
+
+ case MultiIntr:
+ ret = rtl8139_MultiIntr_read(s);
+ break;
+
+ case RxBufPtr:
+ ret = rtl8139_RxBufPtr_read(s);
+ break;
+
+ case RxBufAddr:
+ ret = rtl8139_RxBufAddr_read(s);
+ break;
+
+ case BasicModeCtrl:
+ ret = rtl8139_BasicModeCtrl_read(s);
+ break;
+ case BasicModeStatus:
+ ret = rtl8139_BasicModeStatus_read(s);
+ break;
+ case NWayAdvert:
+ ret = s->NWayAdvert;
+ DEBUG_PRINT(("RTL8139: NWayAdvert read(w) val=0x%04x\n", ret));
+ break;
+ case NWayLPAR:
+ ret = s->NWayLPAR;
+ DEBUG_PRINT(("RTL8139: NWayLPAR read(w) val=0x%04x\n", ret));
+ break;
+ case NWayExpansion:
+ ret = s->NWayExpansion;
+ DEBUG_PRINT(("RTL8139: NWayExpansion read(w) val=0x%04x\n", ret));
+ break;
+
+ case CpCmd:
+ ret = rtl8139_CpCmd_read(s);
+ break;
+
+ case IntrMitigate:
+ ret = rtl8139_IntrMitigate_read(s);
+ break;
+
+ case TxSummary:
+ ret = rtl8139_TSAD_read(s);
+ break;
+
+ case CSCR:
+ ret = rtl8139_CSCR_read(s);
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport read(w) addr=0x%x via read(b)\n", addr));
+
+ ret = rtl8139_io_readb(opaque, addr);
+ ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
+
+ DEBUG_PRINT(("RTL8139: ioport read(w) addr=0x%x val=0x%04x\n", addr, ret));
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
+{
+ RTL8139State *s = opaque;
+ uint32_t ret;
+
+ addr &= 0xfc; /* also mask low 2 bits */
+
+ switch (addr)
+ {
+ case RxMissed:
+ ret = s->RxMissed;
+
+ DEBUG_PRINT(("RTL8139: RxMissed read val=0x%08x\n", ret));
+ break;
+
+ case TxConfig:
+ ret = rtl8139_TxConfig_read(s);
+ break;
+
+ case RxConfig:
+ ret = rtl8139_RxConfig_read(s);
+ break;
+
+ case TxStatus0 ... TxStatus0+4*4-1:
+ ret = rtl8139_TxStatus_read(s, addr-TxStatus0);
+ break;
+
+ case TxAddr0 ... TxAddr0+4*4-1:
+ ret = rtl8139_TxAddr_read(s, addr-TxAddr0);
+ break;
+
+ case RxBuf:
+ ret = rtl8139_RxBuf_read(s);
+ break;
+
+ case RxRingAddrLO:
+ ret = s->RxRingAddrLO;
+ DEBUG_PRINT(("RTL8139: C+ RxRing low bits read val=0x%08x\n", ret));
+ break;
+
+ case RxRingAddrHI:
+ ret = s->RxRingAddrHI;
+ DEBUG_PRINT(("RTL8139: C+ RxRing high bits read val=0x%08x\n", ret));
+ break;
+
+ case Timer:
+ ret = muldiv64(qemu_get_clock(vm_clock) - s->TCTR_base,
+ PCI_FREQUENCY, get_ticks_per_sec());
+ DEBUG_PRINT(("RTL8139: TCTR Timer read val=0x%08x\n", ret));
+ break;
+
+ case FlashReg:
+ ret = s->TimerInt;
+ DEBUG_PRINT(("RTL8139: FlashReg TimerInt read val=0x%08x\n", ret));
+ break;
+
+ default:
+ DEBUG_PRINT(("RTL8139: ioport read(l) addr=0x%x via read(b)\n", addr));
+
+ ret = rtl8139_io_readb(opaque, addr);
+ ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
+ ret |= rtl8139_io_readb(opaque, addr + 2) << 16;
+ ret |= rtl8139_io_readb(opaque, addr + 3) << 24;
+
+ DEBUG_PRINT(("RTL8139: read(l) addr=0x%x val=%08x\n", addr, ret));
+ break;
+ }
+
+ return ret;
+}
+
+/* */
+
+static void rtl8139_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ rtl8139_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ rtl8139_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ rtl8139_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t rtl8139_ioport_readb(void *opaque, uint32_t addr)
+{
+ return rtl8139_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_ioport_readw(void *opaque, uint32_t addr)
+{
+ return rtl8139_io_readw(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_ioport_readl(void *opaque, uint32_t addr)
+{
+ return rtl8139_io_readl(opaque, addr & 0xFF);
+}
+
+/* */
+
+static void rtl8139_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rtl8139_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rtl8139_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ rtl8139_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t rtl8139_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return rtl8139_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = rtl8139_io_readw(opaque, addr & 0xFF);
+ return val;
+}
+
+static uint32_t rtl8139_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = rtl8139_io_readl(opaque, addr & 0xFF);
+ return val;
+}
+
+static int rtl8139_post_load(void *opaque, int version_id)
+{
+ RTL8139State* s = opaque;
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+ if (version_id < 4) {
+ s->cplus_enabled = s->CpCmd != 0;
+ }
+
+ return 0;
+}
+
+static bool rtl8139_hotplug_ready_needed(void *opaque)
+{
+ return qdev_machine_modified();
+}
+
+static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
+ .name = "rtl8139/hotplug_ready",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void rtl8139_pre_save(void *opaque)
+{
+ RTL8139State* s = opaque;
+ int64_t current_time = qemu_get_clock(vm_clock);
+
+ /* set IntrStatus correctly */
+ rtl8139_set_next_tctr_time(s, current_time);
+ s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
+ get_ticks_per_sec());
+ s->rtl8139_mmio_io_addr_dummy = s->rtl8139_mmio_io_addr;
+}
+
+static const VMStateDescription vmstate_rtl8139 = {
+ .name = "rtl8139",
+ .version_id = 4,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .post_load = rtl8139_post_load,
+ .pre_save = rtl8139_pre_save,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, RTL8139State),
+ VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6),
+ VMSTATE_BUFFER(mult, RTL8139State),
+ VMSTATE_UINT32_ARRAY(TxStatus, RTL8139State, 4),
+ VMSTATE_UINT32_ARRAY(TxAddr, RTL8139State, 4),
+
+ VMSTATE_UINT32(RxBuf, RTL8139State),
+ VMSTATE_UINT32(RxBufferSize, RTL8139State),
+ VMSTATE_UINT32(RxBufPtr, RTL8139State),
+ VMSTATE_UINT32(RxBufAddr, RTL8139State),
+
+ VMSTATE_UINT16(IntrStatus, RTL8139State),
+ VMSTATE_UINT16(IntrMask, RTL8139State),
+
+ VMSTATE_UINT32(TxConfig, RTL8139State),
+ VMSTATE_UINT32(RxConfig, RTL8139State),
+ VMSTATE_UINT32(RxMissed, RTL8139State),
+ VMSTATE_UINT16(CSCR, RTL8139State),
+
+ VMSTATE_UINT8(Cfg9346, RTL8139State),
+ VMSTATE_UINT8(Config0, RTL8139State),
+ VMSTATE_UINT8(Config1, RTL8139State),
+ VMSTATE_UINT8(Config3, RTL8139State),
+ VMSTATE_UINT8(Config4, RTL8139State),
+ VMSTATE_UINT8(Config5, RTL8139State),
+
+ VMSTATE_UINT8(clock_enabled, RTL8139State),
+ VMSTATE_UINT8(bChipCmdState, RTL8139State),
+
+ VMSTATE_UINT16(MultiIntr, RTL8139State),
+
+ VMSTATE_UINT16(BasicModeCtrl, RTL8139State),
+ VMSTATE_UINT16(BasicModeStatus, RTL8139State),
+ VMSTATE_UINT16(NWayAdvert, RTL8139State),
+ VMSTATE_UINT16(NWayLPAR, RTL8139State),
+ VMSTATE_UINT16(NWayExpansion, RTL8139State),
+
+ VMSTATE_UINT16(CpCmd, RTL8139State),
+ VMSTATE_UINT8(TxThresh, RTL8139State),
+
+ VMSTATE_UNUSED(4),
+ VMSTATE_MACADDR(conf.macaddr, RTL8139State),
+ VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State),
+
+ VMSTATE_UINT32(currTxDesc, RTL8139State),
+ VMSTATE_UINT32(currCPlusRxDesc, RTL8139State),
+ VMSTATE_UINT32(currCPlusTxDesc, RTL8139State),
+ VMSTATE_UINT32(RxRingAddrLO, RTL8139State),
+ VMSTATE_UINT32(RxRingAddrHI, RTL8139State),
+
+ VMSTATE_UINT16_ARRAY(eeprom.contents, RTL8139State, EEPROM_9346_SIZE),
+ VMSTATE_INT32(eeprom.mode, RTL8139State),
+ VMSTATE_UINT32(eeprom.tick, RTL8139State),
+ VMSTATE_UINT8(eeprom.address, RTL8139State),
+ VMSTATE_UINT16(eeprom.input, RTL8139State),
+ VMSTATE_UINT16(eeprom.output, RTL8139State),
+
+ VMSTATE_UINT8(eeprom.eecs, RTL8139State),
+ VMSTATE_UINT8(eeprom.eesk, RTL8139State),
+ VMSTATE_UINT8(eeprom.eedi, RTL8139State),
+ VMSTATE_UINT8(eeprom.eedo, RTL8139State),
+
+ VMSTATE_UINT32(TCTR, RTL8139State),
+ VMSTATE_UINT32(TimerInt, RTL8139State),
+ VMSTATE_INT64(TCTR_base, RTL8139State),
+
+ VMSTATE_STRUCT(tally_counters, RTL8139State, 0,
+ vmstate_tally_counters, RTL8139TallyCounters),
+
+ VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_rtl8139_hotplug_ready,
+ .needed = rtl8139_hotplug_ready_needed,
+ }, {
+ /* empty */
+ }
+ }
+};
+
+/***********************************************************/
+/* PCI RTL8139 definitions */
+
+static void rtl8139_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ RTL8139State *s = DO_UPCAST(RTL8139State, dev, pci_dev);
+
+ cpu_register_physical_memory(addr + 0, 0x100, s->rtl8139_mmio_io_addr);
+}
+
+static void rtl8139_ioport_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ RTL8139State *s = DO_UPCAST(RTL8139State, dev, pci_dev);
+
+ register_ioport_write(addr, 0x100, 1, rtl8139_ioport_writeb, s);
+ register_ioport_read( addr, 0x100, 1, rtl8139_ioport_readb, s);
+
+ register_ioport_write(addr, 0x100, 2, rtl8139_ioport_writew, s);
+ register_ioport_read( addr, 0x100, 2, rtl8139_ioport_readw, s);
+
+ register_ioport_write(addr, 0x100, 4, rtl8139_ioport_writel, s);
+ register_ioport_read( addr, 0x100, 4, rtl8139_ioport_readl, s);
+}
+
+static CPUReadMemoryFunc * const rtl8139_mmio_read[3] = {
+ rtl8139_mmio_readb,
+ rtl8139_mmio_readw,
+ rtl8139_mmio_readl,
+};
+
+static CPUWriteMemoryFunc * const rtl8139_mmio_write[3] = {
+ rtl8139_mmio_writeb,
+ rtl8139_mmio_writew,
+ rtl8139_mmio_writel,
+};
+
+static void rtl8139_timer(void *opaque)
+{
+ RTL8139State *s = opaque;
+
+ if (!s->clock_enabled)
+ {
+ DEBUG_PRINT(("RTL8139: >>> timer: clock is not running\n"));
+ return;
+ }
+
+ s->IntrStatus |= PCSTimeout;
+ rtl8139_update_irq(s);
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+}
+
+static void rtl8139_cleanup(VLANClientState *nc)
+{
+ RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static int pci_rtl8139_uninit(PCIDevice *dev)
+{
+ RTL8139State *s = DO_UPCAST(RTL8139State, dev, dev);
+
+ cpu_unregister_io_memory(s->rtl8139_mmio_io_addr);
+ if (s->cplus_txbuffer) {
+ qemu_free(s->cplus_txbuffer);
+ s->cplus_txbuffer = NULL;
+ }
+ qemu_del_timer(s->timer);
+ qemu_free_timer(s->timer);
+ qemu_del_vlan_client(&s->nic->nc);
+ return 0;
+}
+
+static NetClientInfo net_rtl8139_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = rtl8139_can_receive,
+ .receive = rtl8139_receive,
+ .cleanup = rtl8139_cleanup,
+};
+
+static int pci_rtl8139_init(PCIDevice *dev)
+{
+ RTL8139State * s = DO_UPCAST(RTL8139State, dev, dev);
+ uint8_t *pci_conf;
+
+ pci_conf = s->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8139);
+ pci_conf[PCI_REVISION_ID] = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
+ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET);
+ pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */
+ /* TODO: start of capability list, but no capability
+ * list bit in status register, and offset 0xdc seems unused. */
+ pci_conf[PCI_CAPABILITY_LIST] = 0xdc;
+
+ /* I/O handler for memory-mapped I/O */
+ s->rtl8139_mmio_io_addr =
+ cpu_register_io_memory(rtl8139_mmio_read, rtl8139_mmio_write, s,
+ DEVICE_LITTLE_ENDIAN);
+
+ pci_register_bar(&s->dev, 0, 0x100,
+ PCI_BASE_ADDRESS_SPACE_IO, rtl8139_ioport_map);
+
+ pci_register_bar(&s->dev, 1, 0x100,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, rtl8139_mmio_map);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+ s->cplus_txbuffer = NULL;
+ s->cplus_txbuffer_len = 0;
+ s->cplus_txbuffer_offset = 0;
+
+ s->TimerExpire = 0;
+ s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+
+ add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet-phy@0");
+
+ return 0;
+}
+
+static PCIDeviceInfo rtl8139_info = {
+ .qdev.name = "rtl8139",
+ .qdev.size = sizeof(RTL8139State),
+ .qdev.reset = rtl8139_reset,
+ .qdev.vmsd = &vmstate_rtl8139,
+ .init = pci_rtl8139_init,
+ .exit = pci_rtl8139_uninit,
+ .romfile = "pxe-rtl8139.bin",
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(RTL8139State, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void rtl8139_register_devices(void)
+{
+ pci_qdev_register(&rtl8139_info);
+}
+
+device_init(rtl8139_register_devices)
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
new file mode 100644
index 0000000..784dc01
--- /dev/null
+++ b/hw/s390-virtio-bus.c
@@ -0,0 +1,409 @@
+/*
+ * QEMU S390 virtio target
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "block.h"
+#include "sysemu.h"
+#include "net.h"
+#include "boards.h"
+#include "monitor.h"
+#include "loader.h"
+#include "elf.h"
+#include "hw/virtio.h"
+#include "hw/virtio-serial.h"
+#include "hw/virtio-net.h"
+#include "hw/sysbus.h"
+#include "kvm.h"
+
+#include "hw/s390-virtio-bus.h"
+
+/* #define DEBUG_S390 */
+
+#ifdef DEBUG_S390
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+struct BusInfo s390_virtio_bus_info = {
+ .name = "s390-virtio",
+ .size = sizeof(VirtIOS390Bus),
+};
+
+typedef struct {
+ DeviceInfo qdev;
+ int (*init)(VirtIOS390Device *dev);
+} VirtIOS390DeviceInfo;
+
+
+static const VirtIOBindings virtio_s390_bindings;
+
+static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev);
+
+VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size)
+{
+ VirtIOS390Bus *bus;
+ BusState *_bus;
+ DeviceState *dev;
+
+ /* Create bridge device */
+ dev = qdev_create(NULL, "s390-virtio-bridge");
+ qdev_init_nofail(dev);
+
+ /* Create bus on bridge device */
+
+ _bus = qbus_create(&s390_virtio_bus_info, dev, "s390-virtio");
+ bus = DO_UPCAST(VirtIOS390Bus, bus, _bus);
+
+ bus->dev_page = *ram_size;
+ bus->dev_offs = bus->dev_page;
+ bus->next_ring = bus->dev_page + TARGET_PAGE_SIZE;
+
+ /* Allocate RAM for VirtIO device pages (descriptors, queues, rings) */
+ *ram_size += S390_DEVICE_PAGES * TARGET_PAGE_SIZE;
+
+ return bus;
+}
+
+static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev)
+{
+ VirtIOS390Bus *bus;
+ int dev_len;
+
+ bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
+ dev->vdev = vdev;
+ dev->dev_offs = bus->dev_offs;
+ dev->feat_len = sizeof(uint32_t); /* always keep 32 bits features */
+
+ dev_len = VIRTIO_DEV_OFFS_CONFIG;
+ dev_len += s390_virtio_device_num_vq(dev) * VIRTIO_VQCONFIG_LEN;
+ dev_len += dev->feat_len * 2;
+ dev_len += vdev->config_len;
+
+ bus->dev_offs += dev_len;
+
+ virtio_bind_device(vdev, &virtio_s390_bindings, dev);
+ dev->host_features = vdev->get_features(vdev, dev->host_features);
+ s390_virtio_device_sync(dev);
+
+ return 0;
+}
+
+static int s390_virtio_net_init(VirtIOS390Device *dev)
+{
+ VirtIODevice *vdev;
+
+ vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net);
+ if (!vdev) {
+ return -1;
+ }
+
+ return s390_virtio_device_init(dev, vdev);
+}
+
+static int s390_virtio_blk_init(VirtIOS390Device *dev)
+{
+ VirtIODevice *vdev;
+
+ vdev = virtio_blk_init((DeviceState *)dev, &dev->block);
+ if (!vdev) {
+ return -1;
+ }
+
+ return s390_virtio_device_init(dev, vdev);
+}
+
+static int s390_virtio_serial_init(VirtIOS390Device *dev)
+{
+ VirtIOS390Bus *bus;
+ VirtIODevice *vdev;
+ int r;
+
+ bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
+
+ vdev = virtio_serial_init((DeviceState *)dev, dev->max_virtserial_ports);
+ if (!vdev) {
+ return -1;
+ }
+
+ r = s390_virtio_device_init(dev, vdev);
+ if (!r) {
+ bus->console = dev;
+ }
+
+ return r;
+}
+
+static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
+{
+ ram_addr_t token_off;
+
+ token_off = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) +
+ (vq * VIRTIO_VQCONFIG_LEN) +
+ VIRTIO_VQCONFIG_OFFS_TOKEN;
+
+ return ldq_phys(token_off);
+}
+
+static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev)
+{
+ VirtIODevice *vdev = dev->vdev;
+ int num_vq;
+
+ for (num_vq = 0; num_vq < VIRTIO_PCI_QUEUE_MAX; num_vq++) {
+ if (!virtio_queue_get_num(vdev, num_vq)) {
+ break;
+ }
+ }
+
+ return num_vq;
+}
+
+static ram_addr_t s390_virtio_next_ring(VirtIOS390Bus *bus)
+{
+ ram_addr_t r = bus->next_ring;
+
+ bus->next_ring += VIRTIO_RING_LEN;
+ return r;
+}
+
+void s390_virtio_device_sync(VirtIOS390Device *dev)
+{
+ VirtIOS390Bus *bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus);
+ ram_addr_t cur_offs;
+ uint8_t num_vq;
+ int i;
+
+ virtio_reset(dev->vdev);
+
+ /* Sync dev space */
+ stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_TYPE, dev->vdev->device_id);
+
+ stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, s390_virtio_device_num_vq(dev));
+ stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_FEATURE_LEN, dev->feat_len);
+
+ stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG_LEN, dev->vdev->config_len);
+
+ num_vq = s390_virtio_device_num_vq(dev);
+ stb_phys(dev->dev_offs + VIRTIO_DEV_OFFS_NUM_VQ, num_vq);
+
+ /* Sync virtqueues */
+ for (i = 0; i < num_vq; i++) {
+ ram_addr_t vq = (dev->dev_offs + VIRTIO_DEV_OFFS_CONFIG) +
+ (i * VIRTIO_VQCONFIG_LEN);
+ ram_addr_t vring;
+
+ vring = s390_virtio_next_ring(bus);
+ virtio_queue_set_addr(dev->vdev, i, vring);
+ virtio_queue_set_vector(dev->vdev, i, i);
+ stq_phys(vq + VIRTIO_VQCONFIG_OFFS_ADDRESS, vring);
+ stw_phys(vq + VIRTIO_VQCONFIG_OFFS_NUM, virtio_queue_get_num(dev->vdev, i));
+ }
+
+ cur_offs = dev->dev_offs;
+ cur_offs += VIRTIO_DEV_OFFS_CONFIG;
+ cur_offs += num_vq * VIRTIO_VQCONFIG_LEN;
+
+ /* Sync feature bitmap */
+ stl_phys(cur_offs, dev->host_features);
+
+ dev->feat_offs = cur_offs + dev->feat_len;
+ cur_offs += dev->feat_len * 2;
+
+ /* Sync config space */
+ if (dev->vdev->get_config) {
+ dev->vdev->get_config(dev->vdev, dev->vdev->config);
+ }
+
+ cpu_physical_memory_rw(cur_offs, dev->vdev->config, dev->vdev->config_len, 1);
+ cur_offs += dev->vdev->config_len;
+}
+
+void s390_virtio_device_update_status(VirtIOS390Device *dev)
+{
+ VirtIODevice *vdev = dev->vdev;
+ uint32_t features;
+
+ virtio_set_status(vdev, ldub_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS));
+
+ /* Update guest supported feature bitmap */
+
+ features = ldl_phys(dev->feat_offs);
+ if (vdev->set_features) {
+ vdev->set_features(vdev, features);
+ }
+ vdev->guest_features = features;
+}
+
+VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus)
+{
+ return bus->console;
+}
+
+/* Find a device by vring address */
+VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
+ ram_addr_t mem,
+ int *vq_num)
+{
+ VirtIOS390Device *_dev;
+ DeviceState *dev;
+ int i;
+
+ QLIST_FOREACH(dev, &bus->bus.children, sibling) {
+ _dev = (VirtIOS390Device *)dev;
+ for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ if (!virtio_queue_get_addr(_dev->vdev, i))
+ break;
+ if (virtio_queue_get_addr(_dev->vdev, i) == mem) {
+ if (vq_num) {
+ *vq_num = i;
+ }
+ return _dev;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* Find a device by device descriptor location */
+VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem)
+{
+ VirtIOS390Device *_dev;
+ DeviceState *dev;
+
+ QLIST_FOREACH(dev, &bus->bus.children, sibling) {
+ _dev = (VirtIOS390Device *)dev;
+ if (_dev->dev_offs == mem) {
+ return _dev;
+ }
+ }
+
+ return NULL;
+}
+
+static void virtio_s390_notify(void *opaque, uint16_t vector)
+{
+ VirtIOS390Device *dev = (VirtIOS390Device*)opaque;
+ uint64_t token = s390_virtio_device_vq_token(dev, vector);
+
+ /* XXX kvm dependency! */
+ kvm_s390_virtio_irq(s390_cpu_addr2state(0), 0, token);
+}
+
+static unsigned virtio_s390_get_features(void *opaque)
+{
+ VirtIOS390Device *dev = (VirtIOS390Device*)opaque;
+ return dev->host_features;
+}
+
+/**************** S390 Virtio Bus Device Descriptions *******************/
+
+static const VirtIOBindings virtio_s390_bindings = {
+ .notify = virtio_s390_notify,
+ .get_features = virtio_s390_get_features,
+};
+
+static VirtIOS390DeviceInfo s390_virtio_net = {
+ .init = s390_virtio_net_init,
+ .qdev.name = "virtio-net-s390",
+ .qdev.size = sizeof(VirtIOS390Device),
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic),
+ DEFINE_PROP_UINT32("x-txtimer", VirtIOS390Device,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", VirtIOS390Device,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", VirtIOS390Device, net.tx),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static VirtIOS390DeviceInfo s390_virtio_blk = {
+ .init = s390_virtio_blk_init,
+ .qdev.name = "virtio-blk-s390",
+ .qdev.size = sizeof(VirtIOS390Device),
+ .qdev.props = (Property[]) {
+ DEFINE_BLOCK_PROPERTIES(VirtIOS390Device, block),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static VirtIOS390DeviceInfo s390_virtio_serial = {
+ .init = s390_virtio_serial_init,
+ .qdev.name = "virtio-serial-s390",
+ .qdev.alias = "virtio-serial",
+ .qdev.size = sizeof(VirtIOS390Device),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("max_ports", VirtIOS390Device, max_virtserial_ports,
+ 31),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static int s390_virtio_busdev_init(DeviceState *dev, DeviceInfo *info)
+{
+ VirtIOS390DeviceInfo *_info = (VirtIOS390DeviceInfo *)info;
+ VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
+
+ return _info->init(_dev);
+}
+
+static void s390_virtio_bus_register_withprop(VirtIOS390DeviceInfo *info)
+{
+ info->qdev.init = s390_virtio_busdev_init;
+ info->qdev.bus_info = &s390_virtio_bus_info;
+
+ assert(info->qdev.size >= sizeof(VirtIOS390Device));
+ qdev_register(&info->qdev);
+}
+
+static void s390_virtio_register(void)
+{
+ s390_virtio_bus_register_withprop(&s390_virtio_serial);
+ s390_virtio_bus_register_withprop(&s390_virtio_blk);
+ s390_virtio_bus_register_withprop(&s390_virtio_net);
+}
+device_init(s390_virtio_register);
+
+
+/***************** S390 Virtio Bus Bridge Device *******************/
+/* Only required to have the virtio bus as child in the system bus */
+
+static int s390_virtio_bridge_init(SysBusDevice *dev)
+{
+ /* nothing */
+ return 0;
+}
+
+static SysBusDeviceInfo s390_virtio_bridge_info = {
+ .init = s390_virtio_bridge_init,
+ .qdev.name = "s390-virtio-bridge",
+ .qdev.size = sizeof(SysBusDevice),
+ .qdev.no_user = 1,
+};
+
+static void s390_virtio_register_devices(void)
+{
+ sysbus_register_withprop(&s390_virtio_bridge_info);
+}
+
+device_init(s390_virtio_register_devices)
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
new file mode 100644
index 0000000..33379a3
--- /dev/null
+++ b/hw/s390-virtio-bus.h
@@ -0,0 +1,69 @@
+/*
+ * QEMU S390x VirtIO BUS definitions
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "virtio-net.h"
+
+#define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */
+#define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */
+#define VIRTIO_DEV_OFFS_FEATURE_LEN 2 /* 8 bits */
+#define VIRTIO_DEV_OFFS_CONFIG_LEN 3 /* 8 bits */
+#define VIRTIO_DEV_OFFS_STATUS 4 /* 8 bits */
+#define VIRTIO_DEV_OFFS_CONFIG 5 /* dynamic */
+
+#define VIRTIO_VQCONFIG_OFFS_TOKEN 0 /* 64 bits */
+#define VIRTIO_VQCONFIG_OFFS_ADDRESS 8 /* 64 bits */
+#define VIRTIO_VQCONFIG_OFFS_NUM 16 /* 16 bits */
+#define VIRTIO_VQCONFIG_LEN 24
+
+#define VIRTIO_RING_LEN (TARGET_PAGE_SIZE * 3)
+#define S390_DEVICE_PAGES 256
+
+typedef struct VirtIOS390Device {
+ DeviceState qdev;
+ ram_addr_t dev_offs;
+ ram_addr_t feat_offs;
+ uint8_t feat_len;
+ VirtIODevice *vdev;
+ BlockConf block;
+ NICConf nic;
+ uint32_t host_features;
+ /* Max. number of ports we can have for a the virtio-serial device */
+ uint32_t max_virtserial_ports;
+ virtio_net_conf net;
+} VirtIOS390Device;
+
+typedef struct VirtIOS390Bus {
+ BusState bus;
+
+ VirtIOS390Device *console;
+ ram_addr_t dev_page;
+ ram_addr_t dev_offs;
+ ram_addr_t next_ring;
+} VirtIOS390Bus;
+
+
+void s390_virtio_device_update_status(VirtIOS390Device *dev);
+
+VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus);
+VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size);
+
+VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
+ ram_addr_t mem, int *vq_num);
+VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem);
+void s390_virtio_device_sync(VirtIOS390Device *dev);
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
new file mode 100644
index 0000000..f29b624
--- /dev/null
+++ b/hw/s390-virtio.c
@@ -0,0 +1,289 @@
+/*
+ * QEMU S390 virtio target
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "block.h"
+#include "blockdev.h"
+#include "sysemu.h"
+#include "net.h"
+#include "boards.h"
+#include "monitor.h"
+#include "loader.h"
+#include "elf.h"
+#include "hw/virtio.h"
+#include "hw/sysbus.h"
+#include "kvm.h"
+
+#include "hw/s390-virtio-bus.h"
+
+//#define DEBUG_S390
+
+#ifdef DEBUG_S390
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define KVM_S390_VIRTIO_NOTIFY 0
+#define KVM_S390_VIRTIO_RESET 1
+#define KVM_S390_VIRTIO_SET_STATUS 2
+
+#define KERN_IMAGE_START 0x010000UL
+#define KERN_PARM_AREA 0x010480UL
+#define INITRD_START 0x800000UL
+#define INITRD_PARM_START 0x010408UL
+#define INITRD_PARM_SIZE 0x010410UL
+#define PARMFILE_START 0x001000UL
+
+#define ZIPL_START 0x009000UL
+#define ZIPL_LOAD_ADDR 0x009000UL
+#define ZIPL_FILENAME "s390-zipl.rom"
+
+#define MAX_BLK_DEVS 10
+
+static VirtIOS390Bus *s390_bus;
+static CPUState **ipi_states;
+
+void irq_info(Monitor *mon);
+void pic_info(Monitor *mon);
+
+void irq_info(Monitor *mon)
+{
+}
+
+void pic_info(Monitor *mon)
+{
+}
+
+CPUState *s390_cpu_addr2state(uint16_t cpu_addr)
+{
+ if (cpu_addr >= smp_cpus) {
+ return NULL;
+ }
+
+ return ipi_states[cpu_addr];
+}
+
+int s390_virtio_hypercall(CPUState *env)
+{
+ int r = 0, i;
+ target_ulong mem = env->regs[2];
+
+ dprintf("KVM hypercall: %ld\n", env->regs[1]);
+ switch (env->regs[1]) {
+ case KVM_S390_VIRTIO_NOTIFY:
+ if (mem > ram_size) {
+ VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
+ mem, &i);
+ if (dev) {
+ virtio_queue_notify(dev->vdev, i);
+ } else {
+ r = -EINVAL;
+ }
+ } else {
+ /* Early printk */
+ }
+ break;
+ case KVM_S390_VIRTIO_RESET:
+ {
+ VirtIOS390Device *dev;
+
+ dev = s390_virtio_bus_find_mem(s390_bus, mem);
+ virtio_reset(dev->vdev);
+ s390_virtio_device_sync(dev);
+ break;
+ }
+ case KVM_S390_VIRTIO_SET_STATUS:
+ {
+ VirtIOS390Device *dev;
+
+ dev = s390_virtio_bus_find_mem(s390_bus, mem);
+ if (dev) {
+ s390_virtio_device_update_status(dev);
+ } else {
+ r = -EINVAL;
+ }
+ break;
+ }
+ default:
+ r = -EINVAL;
+ break;
+ }
+
+ env->regs[2] = r;
+ return 0;
+}
+
+/* PC hardware initialisation */
+static void s390_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env = NULL;
+ ram_addr_t ram_addr;
+ ram_addr_t kernel_size = 0;
+ ram_addr_t initrd_offset;
+ ram_addr_t initrd_size = 0;
+ int i;
+
+ /* XXX we only work on KVM for now */
+
+ if (!kvm_enabled()) {
+ fprintf(stderr, "The S390 target only works with KVM enabled\n");
+ exit(1);
+ }
+
+ /* get a BUS */
+ s390_bus = s390_virtio_bus_init(&ram_size);
+
+ /* allocate RAM */
+ ram_addr = qemu_ram_alloc(NULL, "s390.ram", ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_addr);
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "host";
+ }
+
+ ipi_states = qemu_malloc(sizeof(CPUState *) * smp_cpus);
+
+ for (i = 0; i < smp_cpus; i++) {
+ CPUState *tmp_env;
+
+ tmp_env = cpu_init(cpu_model);
+ if (!env) {
+ env = tmp_env;
+ }
+ ipi_states[i] = tmp_env;
+ tmp_env->halted = 1;
+ tmp_env->exception_index = EXCP_HLT;
+ }
+
+ env->halted = 0;
+ env->exception_index = 0;
+
+ if (kernel_filename) {
+ kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0));
+
+ if (lduw_phys(KERN_IMAGE_START) != 0x0dd0) {
+ fprintf(stderr, "Specified image is not an s390 boot image\n");
+ exit(1);
+ }
+
+ env->psw.addr = KERN_IMAGE_START;
+ env->psw.mask = 0x0000000180000000ULL;
+ } else {
+ ram_addr_t bios_size = 0;
+ char *bios_filename;
+
+ /* Load zipl bootloader */
+ if (bios_name == NULL) {
+ bios_name = ZIPL_FILENAME;
+ }
+
+ bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ bios_size = load_image(bios_filename, qemu_get_ram_ptr(ZIPL_LOAD_ADDR));
+
+ if ((long)bios_size < 0) {
+ hw_error("could not load bootloader '%s'\n", bios_name);
+ }
+
+ if (bios_size > 4096) {
+ hw_error("stage1 bootloader is > 4k\n");
+ }
+
+ env->psw.addr = ZIPL_START;
+ env->psw.mask = 0x0000000180000000ULL;
+ }
+
+ if (initrd_filename) {
+ initrd_offset = INITRD_START;
+ while (kernel_size + 0x100000 > initrd_offset) {
+ initrd_offset += 0x100000;
+ }
+ initrd_size = load_image(initrd_filename, qemu_get_ram_ptr(initrd_offset));
+
+ stq_phys(INITRD_PARM_START, initrd_offset);
+ stq_phys(INITRD_PARM_SIZE, initrd_size);
+ }
+
+ if (kernel_cmdline) {
+ cpu_physical_memory_rw(KERN_PARM_AREA, (uint8_t *)kernel_cmdline,
+ strlen(kernel_cmdline), 1);
+ }
+
+ /* Create VirtIO network adapters */
+ for(i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+ DeviceState *dev;
+
+ if (!nd->model) {
+ nd->model = qemu_strdup("virtio");
+ }
+
+ if (strcmp(nd->model, "virtio")) {
+ fprintf(stderr, "S390 only supports VirtIO nics\n");
+ exit(1);
+ }
+
+ dev = qdev_create((BusState *)s390_bus, "virtio-net-s390");
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+ }
+
+ /* Create VirtIO disk drives */
+ for(i = 0; i < MAX_BLK_DEVS; i++) {
+ DriveInfo *dinfo;
+ DeviceState *dev;
+
+ dinfo = drive_get(IF_IDE, 0, i);
+ if (!dinfo) {
+ continue;
+ }
+
+ dev = qdev_create((BusState *)s390_bus, "virtio-blk-s390");
+ qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
+ qdev_init_nofail(dev);
+ }
+}
+
+static QEMUMachine s390_machine = {
+ .name = "s390-virtio",
+ .alias = "s390",
+ .desc = "VirtIO based S390 machine",
+ .init = s390_init,
+ .no_serial = 1,
+ .no_parallel = 1,
+ .use_virtcon = 1,
+ .no_vga = 1,
+ .max_cpus = 255,
+ .is_default = 1,
+};
+
+static void s390_machine_init(void)
+{
+ qemu_register_machine(&s390_machine);
+}
+
+machine_init(s390_machine_init);
diff --git a/hw/sb16.c b/hw/sb16.c
new file mode 100644
index 0000000..c9d37ad
--- /dev/null
+++ b/hw/sb16.c
@@ -0,0 +1,1420 @@
+/*
+ * QEMU Soundblaster 16 emulation
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "isa.h"
+#include "qdev.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+
+#define dolog(...) AUD_log ("sb16", __VA_ARGS__)
+
+/* #define DEBUG */
+/* #define DEBUG_SB16_MOST */
+
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#define IO_READ_PROTO(name) \
+ uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+ void name (void *opaque, uint32_t nport, uint32_t val)
+
+static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
+
+typedef struct SB16State {
+ ISADevice dev;
+ QEMUSoundCard card;
+ qemu_irq pic;
+ uint32_t irq;
+ uint32_t dma;
+ uint32_t hdma;
+ uint32_t port;
+ uint32_t ver;
+
+ int in_index;
+ int out_data_len;
+ int fmt_stereo;
+ int fmt_signed;
+ int fmt_bits;
+ audfmt_e fmt;
+ int dma_auto;
+ int block_size;
+ int fifo;
+ int freq;
+ int time_const;
+ int speaker;
+ int needed_bytes;
+ int cmd;
+ int use_hdma;
+ int highspeed;
+ int can_write;
+
+ int v2x6;
+
+ uint8_t csp_param;
+ uint8_t csp_value;
+ uint8_t csp_mode;
+ uint8_t csp_regs[256];
+ uint8_t csp_index;
+ uint8_t csp_reg83[4];
+ int csp_reg83r;
+ int csp_reg83w;
+
+ uint8_t in2_data[10];
+ uint8_t out_data[50];
+ uint8_t test_reg;
+ uint8_t last_read_byte;
+ int nzero;
+
+ int left_till_irq;
+
+ int dma_running;
+ int bytes_per_second;
+ int align;
+ int audio_free;
+ SWVoiceOut *voice;
+
+ QEMUTimer *aux_ts;
+ /* mixer state */
+ int mixer_nreg;
+ uint8_t mixer_regs[256];
+} SB16State;
+
+static void SB_audio_callback (void *opaque, int free);
+
+static int magic_of_irq (int irq)
+{
+ switch (irq) {
+ case 5:
+ return 2;
+ case 7:
+ return 4;
+ case 9:
+ return 1;
+ case 10:
+ return 8;
+ default:
+ dolog ("bad irq %d\n", irq);
+ return 2;
+ }
+}
+
+static int irq_of_magic (int magic)
+{
+ switch (magic) {
+ case 1:
+ return 9;
+ case 2:
+ return 5;
+ case 4:
+ return 7;
+ case 8:
+ return 10;
+ default:
+ dolog ("bad irq magic %d\n", magic);
+ return -1;
+ }
+}
+
+#if 0
+static void log_dsp (SB16State *dsp)
+{
+ ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
+ dsp->fmt_stereo ? "Stereo" : "Mono",
+ dsp->fmt_signed ? "Signed" : "Unsigned",
+ dsp->fmt_bits,
+ dsp->dma_auto ? "Auto" : "Single",
+ dsp->block_size,
+ dsp->freq,
+ dsp->time_const,
+ dsp->speaker);
+}
+#endif
+
+static void speaker (SB16State *s, int on)
+{
+ s->speaker = on;
+ /* AUD_enable (s->voice, on); */
+}
+
+static void control (SB16State *s, int hold)
+{
+ int dma = s->use_hdma ? s->hdma : s->dma;
+ s->dma_running = hold;
+
+ ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
+
+ if (hold) {
+ DMA_hold_DREQ (dma);
+ AUD_set_active_out (s->voice, 1);
+ }
+ else {
+ DMA_release_DREQ (dma);
+ AUD_set_active_out (s->voice, 0);
+ }
+}
+
+static void aux_timer (void *opaque)
+{
+ SB16State *s = opaque;
+ s->can_write = 1;
+ qemu_irq_raise (s->pic);
+}
+
+#define DMA8_AUTO 1
+#define DMA8_HIGH 2
+
+static void continue_dma8 (SB16State *s)
+{
+ if (s->freq > 0) {
+ struct audsettings as;
+
+ s->audio_free = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1 << s->fmt_stereo;
+ as.fmt = s->fmt;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+ }
+
+ control (s, 1);
+}
+
+static void dma_cmd8 (SB16State *s, int mask, int dma_len)
+{
+ s->fmt = AUD_FMT_U8;
+ s->use_hdma = 0;
+ s->fmt_bits = 8;
+ s->fmt_signed = 0;
+ s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
+ if (-1 == s->time_const) {
+ if (s->freq <= 0)
+ s->freq = 11025;
+ }
+ else {
+ int tmp = (256 - s->time_const);
+ s->freq = (1000000 + (tmp / 2)) / tmp;
+ }
+
+ if (dma_len != -1) {
+ s->block_size = dma_len << s->fmt_stereo;
+ }
+ else {
+ /* This is apparently the only way to make both Act1/PL
+ and SecondReality/FC work
+
+ Act1 sets block size via command 0x48 and it's an odd number
+ SR does the same with even number
+ Both use stereo, and Creatives own documentation states that
+ 0x48 sets block size in bytes less one.. go figure */
+ s->block_size &= ~s->fmt_stereo;
+ }
+
+ s->freq >>= s->fmt_stereo;
+ s->left_till_irq = s->block_size;
+ s->bytes_per_second = (s->freq << s->fmt_stereo);
+ /* s->highspeed = (mask & DMA8_HIGH) != 0; */
+ s->dma_auto = (mask & DMA8_AUTO) != 0;
+ s->align = (1 << s->fmt_stereo) - 1;
+
+ if (s->block_size & s->align) {
+ dolog ("warning: misaligned block size %d, alignment %d\n",
+ s->block_size, s->align + 1);
+ }
+
+ ldebug ("freq %d, stereo %d, sign %d, bits %d, "
+ "dma %d, auto %d, fifo %d, high %d\n",
+ s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
+ s->block_size, s->dma_auto, s->fifo, s->highspeed);
+
+ continue_dma8 (s);
+ speaker (s, 1);
+}
+
+static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
+{
+ s->use_hdma = cmd < 0xc0;
+ s->fifo = (cmd >> 1) & 1;
+ s->dma_auto = (cmd >> 2) & 1;
+ s->fmt_signed = (d0 >> 4) & 1;
+ s->fmt_stereo = (d0 >> 5) & 1;
+
+ switch (cmd >> 4) {
+ case 11:
+ s->fmt_bits = 16;
+ break;
+
+ case 12:
+ s->fmt_bits = 8;
+ break;
+ }
+
+ if (-1 != s->time_const) {
+#if 1
+ int tmp = 256 - s->time_const;
+ s->freq = (1000000 + (tmp / 2)) / tmp;
+#else
+ /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
+ s->freq = 1000000 / ((255 - s->time_const));
+#endif
+ s->time_const = -1;
+ }
+
+ s->block_size = dma_len + 1;
+ s->block_size <<= (s->fmt_bits == 16);
+ if (!s->dma_auto) {
+ /* It is clear that for DOOM and auto-init this value
+ shouldn't take stereo into account, while Miles Sound Systems
+ setsound.exe with single transfer mode wouldn't work without it
+ wonders of SB16 yet again */
+ s->block_size <<= s->fmt_stereo;
+ }
+
+ ldebug ("freq %d, stereo %d, sign %d, bits %d, "
+ "dma %d, auto %d, fifo %d, high %d\n",
+ s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
+ s->block_size, s->dma_auto, s->fifo, s->highspeed);
+
+ if (16 == s->fmt_bits) {
+ if (s->fmt_signed) {
+ s->fmt = AUD_FMT_S16;
+ }
+ else {
+ s->fmt = AUD_FMT_U16;
+ }
+ }
+ else {
+ if (s->fmt_signed) {
+ s->fmt = AUD_FMT_S8;
+ }
+ else {
+ s->fmt = AUD_FMT_U8;
+ }
+ }
+
+ s->left_till_irq = s->block_size;
+
+ s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
+ s->highspeed = 0;
+ s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
+ if (s->block_size & s->align) {
+ dolog ("warning: misaligned block size %d, alignment %d\n",
+ s->block_size, s->align + 1);
+ }
+
+ if (s->freq) {
+ struct audsettings as;
+
+ s->audio_free = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1 << s->fmt_stereo;
+ as.fmt = s->fmt;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+ }
+
+ control (s, 1);
+ speaker (s, 1);
+}
+
+static inline void dsp_out_data (SB16State *s, uint8_t val)
+{
+ ldebug ("outdata %#x\n", val);
+ if ((size_t) s->out_data_len < sizeof (s->out_data)) {
+ s->out_data[s->out_data_len++] = val;
+ }
+}
+
+static inline uint8_t dsp_get_data (SB16State *s)
+{
+ if (s->in_index) {
+ return s->in2_data[--s->in_index];
+ }
+ else {
+ dolog ("buffer underflow\n");
+ return 0;
+ }
+}
+
+static void command (SB16State *s, uint8_t cmd)
+{
+ ldebug ("command %#x\n", cmd);
+
+ if (cmd > 0xaf && cmd < 0xd0) {
+ if (cmd & 8) {
+ dolog ("ADC not yet supported (command %#x)\n", cmd);
+ }
+
+ switch (cmd >> 4) {
+ case 11:
+ case 12:
+ break;
+ default:
+ dolog ("%#x wrong bits\n", cmd);
+ }
+ s->needed_bytes = 3;
+ }
+ else {
+ s->needed_bytes = 0;
+
+ switch (cmd) {
+ case 0x03:
+ dsp_out_data (s, 0x10); /* s->csp_param); */
+ goto warn;
+
+ case 0x04:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0x05:
+ s->needed_bytes = 2;
+ goto warn;
+
+ case 0x08:
+ /* __asm__ ("int3"); */
+ goto warn;
+
+ case 0x0e:
+ s->needed_bytes = 2;
+ goto warn;
+
+ case 0x09:
+ dsp_out_data (s, 0xf8);
+ goto warn;
+
+ case 0x0f:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0x10:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0x14:
+ s->needed_bytes = 2;
+ s->block_size = 0;
+ break;
+
+ case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */
+ dma_cmd8 (s, DMA8_AUTO, -1);
+ break;
+
+ case 0x20: /* Direct ADC, Juice/PL */
+ dsp_out_data (s, 0xff);
+ goto warn;
+
+ case 0x35:
+ dolog ("0x35 - MIDI command not implemented\n");
+ break;
+
+ case 0x40:
+ s->freq = -1;
+ s->time_const = -1;
+ s->needed_bytes = 1;
+ break;
+
+ case 0x41:
+ s->freq = -1;
+ s->time_const = -1;
+ s->needed_bytes = 2;
+ break;
+
+ case 0x42:
+ s->freq = -1;
+ s->time_const = -1;
+ s->needed_bytes = 2;
+ goto warn;
+
+ case 0x45:
+ dsp_out_data (s, 0xaa);
+ goto warn;
+
+ case 0x47: /* Continue Auto-Initialize DMA 16bit */
+ break;
+
+ case 0x48:
+ s->needed_bytes = 2;
+ break;
+
+ case 0x74:
+ s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
+ dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
+ break;
+
+ case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
+ break;
+
+ case 0x76: /* DMA DAC, 2.6-bit ADPCM */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
+ break;
+
+ case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
+ s->needed_bytes = 2;
+ dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
+ break;
+
+ case 0x7d:
+ dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
+ dolog ("not implemented\n");
+ break;
+
+ case 0x7f:
+ dolog (
+ "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
+ );
+ dolog ("not implemented\n");
+ break;
+
+ case 0x80:
+ s->needed_bytes = 2;
+ break;
+
+ case 0x90:
+ case 0x91:
+ dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
+ break;
+
+ case 0xd0: /* halt DMA operation. 8bit */
+ control (s, 0);
+ break;
+
+ case 0xd1: /* speaker on */
+ speaker (s, 1);
+ break;
+
+ case 0xd3: /* speaker off */
+ speaker (s, 0);
+ break;
+
+ case 0xd4: /* continue DMA operation. 8bit */
+ /* KQ6 (or maybe Sierras audblst.drv in general) resets
+ the frequency between halt/continue */
+ continue_dma8 (s);
+ break;
+
+ case 0xd5: /* halt DMA operation. 16bit */
+ control (s, 0);
+ break;
+
+ case 0xd6: /* continue DMA operation. 16bit */
+ control (s, 1);
+ break;
+
+ case 0xd9: /* exit auto-init DMA after this block. 16bit */
+ s->dma_auto = 0;
+ break;
+
+ case 0xda: /* exit auto-init DMA after this block. 8bit */
+ s->dma_auto = 0;
+ break;
+
+ case 0xe0: /* DSP identification */
+ s->needed_bytes = 1;
+ break;
+
+ case 0xe1:
+ dsp_out_data (s, s->ver & 0xff);
+ dsp_out_data (s, s->ver >> 8);
+ break;
+
+ case 0xe2:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0xe3:
+ {
+ int i;
+ for (i = sizeof (e3) - 1; i >= 0; --i)
+ dsp_out_data (s, e3[i]);
+ }
+ break;
+
+ case 0xe4: /* write test reg */
+ s->needed_bytes = 1;
+ break;
+
+ case 0xe7:
+ dolog ("Attempt to probe for ESS (0xe7)?\n");
+ break;
+
+ case 0xe8: /* read test reg */
+ dsp_out_data (s, s->test_reg);
+ break;
+
+ case 0xf2:
+ case 0xf3:
+ dsp_out_data (s, 0xaa);
+ s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
+ qemu_irq_raise (s->pic);
+ break;
+
+ case 0xf9:
+ s->needed_bytes = 1;
+ goto warn;
+
+ case 0xfa:
+ dsp_out_data (s, 0);
+ goto warn;
+
+ case 0xfc: /* FIXME */
+ dsp_out_data (s, 0);
+ goto warn;
+
+ default:
+ dolog ("Unrecognized command %#x\n", cmd);
+ break;
+ }
+ }
+
+ if (!s->needed_bytes) {
+ ldebug ("\n");
+ }
+
+ exit:
+ if (!s->needed_bytes) {
+ s->cmd = -1;
+ }
+ else {
+ s->cmd = cmd;
+ }
+ return;
+
+ warn:
+ dolog ("warning: command %#x,%d is not truly understood yet\n",
+ cmd, s->needed_bytes);
+ goto exit;
+
+}
+
+static uint16_t dsp_get_lohi (SB16State *s)
+{
+ uint8_t hi = dsp_get_data (s);
+ uint8_t lo = dsp_get_data (s);
+ return (hi << 8) | lo;
+}
+
+static uint16_t dsp_get_hilo (SB16State *s)
+{
+ uint8_t lo = dsp_get_data (s);
+ uint8_t hi = dsp_get_data (s);
+ return (hi << 8) | lo;
+}
+
+static void complete (SB16State *s)
+{
+ int d0, d1, d2;
+ ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
+ s->cmd, s->in_index, s->needed_bytes);
+
+ if (s->cmd > 0xaf && s->cmd < 0xd0) {
+ d2 = dsp_get_data (s);
+ d1 = dsp_get_data (s);
+ d0 = dsp_get_data (s);
+
+ if (s->cmd & 8) {
+ dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
+ s->cmd, d0, d1, d2);
+ }
+ else {
+ ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
+ s->cmd, d0, d1, d2);
+ dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
+ }
+ }
+ else {
+ switch (s->cmd) {
+ case 0x04:
+ s->csp_mode = dsp_get_data (s);
+ s->csp_reg83r = 0;
+ s->csp_reg83w = 0;
+ ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
+ break;
+
+ case 0x05:
+ s->csp_param = dsp_get_data (s);
+ s->csp_value = dsp_get_data (s);
+ ldebug ("CSP command 0x05: param=%#x value=%#x\n",
+ s->csp_param,
+ s->csp_value);
+ break;
+
+ case 0x0e:
+ d0 = dsp_get_data (s);
+ d1 = dsp_get_data (s);
+ ldebug ("write CSP register %d <- %#x\n", d1, d0);
+ if (d1 == 0x83) {
+ ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
+ s->csp_reg83[s->csp_reg83r % 4] = d0;
+ s->csp_reg83r += 1;
+ }
+ else {
+ s->csp_regs[d1] = d0;
+ }
+ break;
+
+ case 0x0f:
+ d0 = dsp_get_data (s);
+ ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
+ d0, s->csp_regs[d0], s->csp_mode);
+ if (d0 == 0x83) {
+ ldebug ("0x83[%d] -> %#x\n",
+ s->csp_reg83w,
+ s->csp_reg83[s->csp_reg83w % 4]);
+ dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
+ s->csp_reg83w += 1;
+ }
+ else {
+ dsp_out_data (s, s->csp_regs[d0]);
+ }
+ break;
+
+ case 0x10:
+ d0 = dsp_get_data (s);
+ dolog ("cmd 0x10 d0=%#x\n", d0);
+ break;
+
+ case 0x14:
+ dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
+ break;
+
+ case 0x40:
+ s->time_const = dsp_get_data (s);
+ ldebug ("set time const %d\n", s->time_const);
+ break;
+
+ case 0x42: /* FT2 sets output freq with this, go figure */
+#if 0
+ dolog ("cmd 0x42 might not do what it think it should\n");
+#endif
+ case 0x41:
+ s->freq = dsp_get_hilo (s);
+ ldebug ("set freq %d\n", s->freq);
+ break;
+
+ case 0x48:
+ s->block_size = dsp_get_lohi (s) + 1;
+ ldebug ("set dma block len %d\n", s->block_size);
+ break;
+
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ /* ADPCM stuff, ignore */
+ break;
+
+ case 0x80:
+ {
+ int freq, samples, bytes;
+ int64_t ticks;
+
+ freq = s->freq > 0 ? s->freq : 11025;
+ samples = dsp_get_lohi (s) + 1;
+ bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
+ ticks = muldiv64 (bytes, get_ticks_per_sec (), freq);
+ if (ticks < get_ticks_per_sec () / 1024) {
+ qemu_irq_raise (s->pic);
+ }
+ else {
+ if (s->aux_ts) {
+ qemu_mod_timer (
+ s->aux_ts,
+ qemu_get_clock (vm_clock) + ticks
+ );
+ }
+ }
+ ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
+ }
+ break;
+
+ case 0xe0:
+ d0 = dsp_get_data (s);
+ s->out_data_len = 0;
+ ldebug ("E0 data = %#x\n", d0);
+ dsp_out_data (s, ~d0);
+ break;
+
+ case 0xe2:
+#ifdef DEBUG
+ d0 = dsp_get_data (s);
+ dolog ("E2 = %#x\n", d0);
+#endif
+ break;
+
+ case 0xe4:
+ s->test_reg = dsp_get_data (s);
+ break;
+
+ case 0xf9:
+ d0 = dsp_get_data (s);
+ ldebug ("command 0xf9 with %#x\n", d0);
+ switch (d0) {
+ case 0x0e:
+ dsp_out_data (s, 0xff);
+ break;
+
+ case 0x0f:
+ dsp_out_data (s, 0x07);
+ break;
+
+ case 0x37:
+ dsp_out_data (s, 0x38);
+ break;
+
+ default:
+ dsp_out_data (s, 0x00);
+ break;
+ }
+ break;
+
+ default:
+ dolog ("complete: unrecognized command %#x\n", s->cmd);
+ return;
+ }
+ }
+
+ ldebug ("\n");
+ s->cmd = -1;
+ return;
+}
+
+static void legacy_reset (SB16State *s)
+{
+ struct audsettings as;
+
+ s->freq = 11025;
+ s->fmt_signed = 0;
+ s->fmt_bits = 8;
+ s->fmt_stereo = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1;
+ as.fmt = AUD_FMT_U8;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+
+ /* Not sure about that... */
+ /* AUD_set_active_out (s->voice, 1); */
+}
+
+static void reset (SB16State *s)
+{
+ qemu_irq_lower (s->pic);
+ if (s->dma_auto) {
+ qemu_irq_raise (s->pic);
+ qemu_irq_lower (s->pic);
+ }
+
+ s->mixer_regs[0x82] = 0;
+ s->dma_auto = 0;
+ s->in_index = 0;
+ s->out_data_len = 0;
+ s->left_till_irq = 0;
+ s->needed_bytes = 0;
+ s->block_size = -1;
+ s->nzero = 0;
+ s->highspeed = 0;
+ s->v2x6 = 0;
+ s->cmd = -1;
+
+ dsp_out_data (s, 0xaa);
+ speaker (s, 0);
+ control (s, 0);
+ legacy_reset (s);
+}
+
+static IO_WRITE_PROTO (dsp_write)
+{
+ SB16State *s = opaque;
+ int iport;
+
+ iport = nport - s->port;
+
+ ldebug ("write %#x <- %#x\n", nport, val);
+ switch (iport) {
+ case 0x06:
+ switch (val) {
+ case 0x00:
+ if (s->v2x6 == 1) {
+ reset (s);
+ }
+ s->v2x6 = 0;
+ break;
+
+ case 0x01:
+ case 0x03: /* FreeBSD kludge */
+ s->v2x6 = 1;
+ break;
+
+ case 0xc6:
+ s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
+ break;
+
+ case 0xb8: /* Panic */
+ reset (s);
+ break;
+
+ case 0x39:
+ dsp_out_data (s, 0x38);
+ reset (s);
+ s->v2x6 = 0x39;
+ break;
+
+ default:
+ s->v2x6 = val;
+ break;
+ }
+ break;
+
+ case 0x0c: /* write data or command | write status */
+/* if (s->highspeed) */
+/* break; */
+
+ if (0 == s->needed_bytes) {
+ command (s, val);
+#if 0
+ if (0 == s->needed_bytes) {
+ log_dsp (s);
+ }
+#endif
+ }
+ else {
+ if (s->in_index == sizeof (s->in2_data)) {
+ dolog ("in data overrun\n");
+ }
+ else {
+ s->in2_data[s->in_index++] = val;
+ if (s->in_index == s->needed_bytes) {
+ s->needed_bytes = 0;
+ complete (s);
+#if 0
+ log_dsp (s);
+#endif
+ }
+ }
+ }
+ break;
+
+ default:
+ ldebug ("(nport=%#x, val=%#x)\n", nport, val);
+ break;
+ }
+}
+
+static IO_READ_PROTO (dsp_read)
+{
+ SB16State *s = opaque;
+ int iport, retval, ack = 0;
+
+ iport = nport - s->port;
+
+ switch (iport) {
+ case 0x06: /* reset */
+ retval = 0xff;
+ break;
+
+ case 0x0a: /* read data */
+ if (s->out_data_len) {
+ retval = s->out_data[--s->out_data_len];
+ s->last_read_byte = retval;
+ }
+ else {
+ if (s->cmd != -1) {
+ dolog ("empty output buffer for command %#x\n",
+ s->cmd);
+ }
+ retval = s->last_read_byte;
+ /* goto error; */
+ }
+ break;
+
+ case 0x0c: /* 0 can write */
+ retval = s->can_write ? 0 : 0x80;
+ break;
+
+ case 0x0d: /* timer interrupt clear */
+ /* dolog ("timer interrupt clear\n"); */
+ retval = 0;
+ break;
+
+ case 0x0e: /* data available status | irq 8 ack */
+ retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
+ if (s->mixer_regs[0x82] & 1) {
+ ack = 1;
+ s->mixer_regs[0x82] &= 1;
+ qemu_irq_lower (s->pic);
+ }
+ break;
+
+ case 0x0f: /* irq 16 ack */
+ retval = 0xff;
+ if (s->mixer_regs[0x82] & 2) {
+ ack = 1;
+ s->mixer_regs[0x82] &= 2;
+ qemu_irq_lower (s->pic);
+ }
+ break;
+
+ default:
+ goto error;
+ }
+
+ if (!ack) {
+ ldebug ("read %#x -> %#x\n", nport, retval);
+ }
+
+ return retval;
+
+ error:
+ dolog ("warning: dsp_read %#x error\n", nport);
+ return 0xff;
+}
+
+static void reset_mixer (SB16State *s)
+{
+ int i;
+
+ memset (s->mixer_regs, 0xff, 0x7f);
+ memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
+
+ s->mixer_regs[0x02] = 4; /* master volume 3bits */
+ s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
+ s->mixer_regs[0x08] = 0; /* CD volume 3bits */
+ s->mixer_regs[0x0a] = 0; /* voice volume 2bits */
+
+ /* d5=input filt, d3=lowpass filt, d1,d2=input source */
+ s->mixer_regs[0x0c] = 0;
+
+ /* d5=output filt, d1=stereo switch */
+ s->mixer_regs[0x0e] = 0;
+
+ /* voice volume L d5,d7, R d1,d3 */
+ s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
+ /* master ... */
+ s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
+ /* MIDI ... */
+ s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
+
+ for (i = 0x30; i < 0x48; i++) {
+ s->mixer_regs[i] = 0x20;
+ }
+}
+
+static IO_WRITE_PROTO (mixer_write_indexb)
+{
+ SB16State *s = opaque;
+ (void) nport;
+ s->mixer_nreg = val;
+}
+
+static IO_WRITE_PROTO (mixer_write_datab)
+{
+ SB16State *s = opaque;
+
+ (void) nport;
+ ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
+
+ switch (s->mixer_nreg) {
+ case 0x00:
+ reset_mixer (s);
+ break;
+
+ case 0x80:
+ {
+ int irq = irq_of_magic (val);
+ ldebug ("setting irq to %d (val=%#x)\n", irq, val);
+ if (irq > 0) {
+ s->irq = irq;
+ }
+ }
+ break;
+
+ case 0x81:
+ {
+ int dma, hdma;
+
+ dma = ctz32 (val & 0xf);
+ hdma = ctz32 (val & 0xf0);
+ if (dma != s->dma || hdma != s->hdma) {
+ dolog (
+ "attempt to change DMA "
+ "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
+ dma, s->dma, hdma, s->hdma, val);
+ }
+#if 0
+ s->dma = dma;
+ s->hdma = hdma;
+#endif
+ }
+ break;
+
+ case 0x82:
+ dolog ("attempt to write into IRQ status register (val=%#x)\n",
+ val);
+ return;
+
+ default:
+ if (s->mixer_nreg >= 0x80) {
+ ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
+ }
+ break;
+ }
+
+ s->mixer_regs[s->mixer_nreg] = val;
+}
+
+static IO_WRITE_PROTO (mixer_write_indexw)
+{
+ mixer_write_indexb (opaque, nport, val & 0xff);
+ mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
+}
+
+static IO_READ_PROTO (mixer_read)
+{
+ SB16State *s = opaque;
+
+ (void) nport;
+#ifndef DEBUG_SB16_MOST
+ if (s->mixer_nreg != 0x82) {
+ ldebug ("mixer_read[%#x] -> %#x\n",
+ s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+ }
+#else
+ ldebug ("mixer_read[%#x] -> %#x\n",
+ s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+#endif
+ return s->mixer_regs[s->mixer_nreg];
+}
+
+static int write_audio (SB16State *s, int nchan, int dma_pos,
+ int dma_len, int len)
+{
+ int temp, net;
+ uint8_t tmpbuf[4096];
+
+ temp = len;
+ net = 0;
+
+ while (temp) {
+ int left = dma_len - dma_pos;
+ int copied;
+ size_t to_copy;
+
+ to_copy = audio_MIN (temp, left);
+ if (to_copy > sizeof (tmpbuf)) {
+ to_copy = sizeof (tmpbuf);
+ }
+
+ copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+ copied = AUD_write (s->voice, tmpbuf, copied);
+
+ temp -= copied;
+ dma_pos = (dma_pos + copied) % dma_len;
+ net += copied;
+
+ if (!copied) {
+ break;
+ }
+ }
+
+ return net;
+}
+
+static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ SB16State *s = opaque;
+ int till, copy, written, free;
+
+ if (s->block_size <= 0) {
+ dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
+ s->block_size, nchan, dma_pos, dma_len);
+ return dma_pos;
+ }
+
+ if (s->left_till_irq < 0) {
+ s->left_till_irq = s->block_size;
+ }
+
+ if (s->voice) {
+ free = s->audio_free & ~s->align;
+ if ((free <= 0) || !dma_len) {
+ return dma_pos;
+ }
+ }
+ else {
+ free = dma_len;
+ }
+
+ copy = free;
+ till = s->left_till_irq;
+
+#ifdef DEBUG_SB16_MOST
+ dolog ("pos:%06d %d till:%d len:%d\n",
+ dma_pos, free, till, dma_len);
+#endif
+
+ if (till <= copy) {
+ if (0 == s->dma_auto) {
+ copy = till;
+ }
+ }
+
+ written = write_audio (s, nchan, dma_pos, dma_len, copy);
+ dma_pos = (dma_pos + written) % dma_len;
+ s->left_till_irq -= written;
+
+ if (s->left_till_irq <= 0) {
+ s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
+ qemu_irq_raise (s->pic);
+ if (0 == s->dma_auto) {
+ control (s, 0);
+ speaker (s, 0);
+ }
+ }
+
+#ifdef DEBUG_SB16_MOST
+ ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
+ dma_pos, free, dma_len, s->left_till_irq, copy, written,
+ s->block_size);
+#endif
+
+ while (s->left_till_irq <= 0) {
+ s->left_till_irq = s->block_size + s->left_till_irq;
+ }
+
+ return dma_pos;
+}
+
+static void SB_audio_callback (void *opaque, int free)
+{
+ SB16State *s = opaque;
+ s->audio_free = free;
+}
+
+static int sb16_post_load (void *opaque, int version_id)
+{
+ SB16State *s = opaque;
+
+ if (s->voice) {
+ AUD_close_out (&s->card, s->voice);
+ s->voice = NULL;
+ }
+
+ if (s->dma_running) {
+ if (s->freq) {
+ struct audsettings as;
+
+ s->audio_free = 0;
+
+ as.freq = s->freq;
+ as.nchannels = 1 << s->fmt_stereo;
+ as.fmt = s->fmt;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out (
+ &s->card,
+ s->voice,
+ "sb16",
+ s,
+ SB_audio_callback,
+ &as
+ );
+ }
+
+ control (s, 1);
+ speaker (s, s->speaker);
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_sb16 = {
+ .name = "sb16",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = sb16_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(irq, SB16State),
+ VMSTATE_UINT32(dma, SB16State),
+ VMSTATE_UINT32(hdma, SB16State),
+ VMSTATE_UINT32(port, SB16State),
+ VMSTATE_UINT32(ver, SB16State),
+ VMSTATE_INT32(in_index, SB16State),
+ VMSTATE_INT32(out_data_len, SB16State),
+ VMSTATE_INT32(fmt_stereo, SB16State),
+ VMSTATE_INT32(fmt_signed, SB16State),
+ VMSTATE_INT32(fmt_bits, SB16State),
+ VMSTATE_UINT32(fmt, SB16State),
+ VMSTATE_INT32(dma_auto, SB16State),
+ VMSTATE_INT32(block_size, SB16State),
+ VMSTATE_INT32(fifo, SB16State),
+ VMSTATE_INT32(freq, SB16State),
+ VMSTATE_INT32(time_const, SB16State),
+ VMSTATE_INT32(speaker, SB16State),
+ VMSTATE_INT32(needed_bytes, SB16State),
+ VMSTATE_INT32(cmd, SB16State),
+ VMSTATE_INT32(use_hdma, SB16State),
+ VMSTATE_INT32(highspeed, SB16State),
+ VMSTATE_INT32(can_write, SB16State),
+ VMSTATE_INT32(v2x6, SB16State),
+
+ VMSTATE_UINT8(csp_param, SB16State),
+ VMSTATE_UINT8(csp_value, SB16State),
+ VMSTATE_UINT8(csp_mode, SB16State),
+ VMSTATE_UINT8(csp_param, SB16State),
+ VMSTATE_BUFFER(csp_regs, SB16State),
+ VMSTATE_UINT8(csp_index, SB16State),
+ VMSTATE_BUFFER(csp_reg83, SB16State),
+ VMSTATE_INT32(csp_reg83r, SB16State),
+ VMSTATE_INT32(csp_reg83w, SB16State),
+
+ VMSTATE_BUFFER(in2_data, SB16State),
+ VMSTATE_BUFFER(out_data, SB16State),
+ VMSTATE_UINT8(test_reg, SB16State),
+ VMSTATE_UINT8(last_read_byte, SB16State),
+
+ VMSTATE_INT32(nzero, SB16State),
+ VMSTATE_INT32(left_till_irq, SB16State),
+ VMSTATE_INT32(dma_running, SB16State),
+ VMSTATE_INT32(bytes_per_second, SB16State),
+ VMSTATE_INT32(align, SB16State),
+
+ VMSTATE_INT32(mixer_nreg, SB16State),
+ VMSTATE_BUFFER(mixer_regs, SB16State),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int sb16_initfn (ISADevice *dev)
+{
+ static const uint8_t dsp_write_ports[] = {0x6, 0xc};
+ static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf};
+ SB16State *s;
+ int i;
+
+ s = DO_UPCAST (SB16State, dev, dev);
+
+ s->cmd = -1;
+ isa_init_irq (dev, &s->pic, s->irq);
+
+ s->mixer_regs[0x80] = magic_of_irq (s->irq);
+ s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
+ s->mixer_regs[0x82] = 2 << 5;
+
+ s->csp_regs[5] = 1;
+ s->csp_regs[9] = 0xf8;
+
+ reset_mixer (s);
+ s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s);
+ if (!s->aux_ts) {
+ dolog ("warning: Could not create auxiliary timer\n");
+ }
+
+ for (i = 0; i < ARRAY_SIZE (dsp_write_ports); i++) {
+ register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s);
+ isa_init_ioport(dev, s->port + dsp_write_ports[i]);
+ }
+
+ for (i = 0; i < ARRAY_SIZE (dsp_read_ports); i++) {
+ register_ioport_read (s->port + dsp_read_ports[i], 1, 1, dsp_read, s);
+ isa_init_ioport(dev, s->port + dsp_read_ports[i]);
+ }
+
+ register_ioport_write (s->port + 0x4, 1, 1, mixer_write_indexb, s);
+ register_ioport_write (s->port + 0x4, 1, 2, mixer_write_indexw, s);
+ isa_init_ioport(dev, s->port + 0x4);
+ register_ioport_read (s->port + 0x5, 1, 1, mixer_read, s);
+ register_ioport_write (s->port + 0x5, 1, 1, mixer_write_datab, s);
+ isa_init_ioport(dev, s->port + 0x5);
+
+ DMA_register_channel (s->hdma, SB_read_DMA, s);
+ DMA_register_channel (s->dma, SB_read_DMA, s);
+ s->can_write = 1;
+
+ AUD_register_card ("sb16", &s->card);
+ return 0;
+}
+
+int SB16_init (qemu_irq *pic)
+{
+ isa_create_simple ("sb16");
+ return 0;
+}
+
+static ISADeviceInfo sb16_info = {
+ .qdev.name = "sb16",
+ .qdev.desc = "Creative Sound Blaster 16",
+ .qdev.size = sizeof (SB16State),
+ .qdev.vmsd = &vmstate_sb16,
+ .init = sb16_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32 ("version", SB16State, ver, 0x0405), /* 4.5 */
+ DEFINE_PROP_HEX32 ("iobase", SB16State, port, 0x220),
+ DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5),
+ DEFINE_PROP_UINT32 ("dma", SB16State, dma, 1),
+ DEFINE_PROP_UINT32 ("dma16", SB16State, hdma, 5),
+ DEFINE_PROP_END_OF_LIST (),
+ },
+};
+
+static void sb16_register (void)
+{
+ isa_qdev_register (&sb16_info);
+}
+device_init (sb16_register)
diff --git a/hw/sbi.c b/hw/sbi.c
new file mode 100644
index 0000000..53f66f2
--- /dev/null
+++ b/hw/sbi.c
@@ -0,0 +1,148 @@
+/*
+ * QEMU Sparc SBI interrupt controller emulation
+ *
+ * Based on slavio_intctl, copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+
+//#define DEBUG_IRQ
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, ...) \
+ do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define MAX_CPUS 16
+
+#define SBI_NREGS 16
+
+typedef struct SBIState {
+ SysBusDevice busdev;
+ uint32_t regs[SBI_NREGS];
+ uint32_t intreg_pending[MAX_CPUS];
+ qemu_irq cpu_irqs[MAX_CPUS];
+ uint32_t pil_out[MAX_CPUS];
+} SBIState;
+
+#define SBI_SIZE (SBI_NREGS * 4)
+
+static void sbi_set_irq(void *opaque, int irq, int level)
+{
+}
+
+static uint32_t sbi_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SBIState *s = opaque;
+ uint32_t saddr, ret;
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ default:
+ ret = s->regs[saddr];
+ break;
+ }
+ DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
+
+ return ret;
+}
+
+static void sbi_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ SBIState *s = opaque;
+ uint32_t saddr;
+
+ saddr = addr >> 2;
+ DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
+ switch (saddr) {
+ default:
+ s->regs[saddr] = val;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const sbi_mem_read[3] = {
+ NULL,
+ NULL,
+ sbi_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const sbi_mem_write[3] = {
+ NULL,
+ NULL,
+ sbi_mem_writel,
+};
+
+static const VMStateDescription vmstate_sbi = {
+ .name ="sbi",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(intreg_pending, SBIState, MAX_CPUS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void sbi_reset(DeviceState *d)
+{
+ SBIState *s = container_of(d, SBIState, busdev.qdev);
+ unsigned int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ s->intreg_pending[i] = 0;
+ }
+}
+
+static int sbi_init1(SysBusDevice *dev)
+{
+ SBIState *s = FROM_SYSBUS(SBIState, dev);
+ int sbi_io_memory;
+ unsigned int i;
+
+ qdev_init_gpio_in(&dev->qdev, sbi_set_irq, 32 + MAX_CPUS);
+ for (i = 0; i < MAX_CPUS; i++) {
+ sysbus_init_irq(dev, &s->cpu_irqs[i]);
+ }
+
+ sbi_io_memory = cpu_register_io_memory(sbi_mem_read, sbi_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, SBI_SIZE, sbi_io_memory);
+
+ return 0;
+}
+
+static SysBusDeviceInfo sbi_info = {
+ .init = sbi_init1,
+ .qdev.name = "sbi",
+ .qdev.size = sizeof(SBIState),
+ .qdev.vmsd = &vmstate_sbi,
+ .qdev.reset = sbi_reset,
+};
+
+static void sbi_register_devices(void)
+{
+ sysbus_register_withprop(&sbi_info);
+}
+
+device_init(sbi_register_devices)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
new file mode 100644
index 0000000..ceeb4ec
--- /dev/null
+++ b/hw/scsi-bus.c
@@ -0,0 +1,547 @@
+#include "hw.h"
+#include "qemu-error.h"
+#include "scsi.h"
+#include "scsi-defs.h"
+#include "qdev.h"
+#include "blockdev.h"
+
+static char *scsibus_get_fw_dev_path(DeviceState *dev);
+
+static struct BusInfo scsi_bus_info = {
+ .name = "SCSI",
+ .size = sizeof(SCSIBus),
+ .get_fw_dev_path = scsibus_get_fw_dev_path,
+ .props = (Property[]) {
+ DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+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)
+{
+ 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->qbus.allow_hotplug = 1;
+}
+
+static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+ SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+ int rc = -1;
+
+ if (dev->id == -1) {
+ for (dev->id = 0; dev->id < bus->ndev; dev->id++) {
+ if (bus->devs[dev->id] == NULL)
+ break;
+ }
+ }
+ if (dev->id >= bus->ndev) {
+ error_report("bad scsi device id: %d", dev->id);
+ goto err;
+ }
+
+ if (bus->devs[dev->id]) {
+ qdev_free(&bus->devs[dev->id]->qdev);
+ }
+ bus->devs[dev->id] = dev;
+
+ dev->info = info;
+ QTAILQ_INIT(&dev->requests);
+ rc = dev->info->init(dev);
+ if (rc != 0) {
+ bus->devs[dev->id] = NULL;
+ }
+
+err:
+ return rc;
+}
+
+static int scsi_qdev_exit(DeviceState *qdev)
+{
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+ SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+
+ assert(bus->devs[dev->id] != NULL);
+ if (bus->devs[dev->id]->info->destroy) {
+ bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
+ }
+ bus->devs[dev->id] = NULL;
+ return 0;
+}
+
+void scsi_qdev_register(SCSIDeviceInfo *info)
+{
+ info->qdev.bus_info = &scsi_bus_info;
+ info->qdev.init = scsi_qdev_init;
+ info->qdev.unplug = qdev_simple_unplug_cb;
+ info->qdev.exit = scsi_qdev_exit;
+ qdev_register(&info->qdev);
+}
+
+/* handle legacy '-drive if=scsi,...' cmd line args */
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+ int unit, bool removable)
+{
+ const char *driver;
+ DeviceState *dev;
+
+ driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
+ dev = qdev_create(&bus->qbus, driver);
+ qdev_prop_set_uint32(dev, "scsi-id", unit);
+ if (qdev_prop_exists(dev, "removable")) {
+ qdev_prop_set_bit(dev, "removable", removable);
+ }
+ if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
+ qdev_free(dev);
+ return NULL;
+ }
+ if (qdev_init(dev) < 0)
+ return NULL;
+ return DO_UPCAST(SCSIDevice, qdev, dev);
+}
+
+int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
+{
+ Location loc;
+ DriveInfo *dinfo;
+ int res = 0, unit;
+
+ loc_push_none(&loc);
+ for (unit = 0; unit < bus->ndev; unit++) {
+ dinfo = drive_get(IF_SCSI, bus->busnr, unit);
+ if (dinfo == NULL) {
+ continue;
+ }
+ qemu_opts_loc_restore(dinfo->opts);
+ if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false)) {
+ res = -1;
+ break;
+ }
+ }
+ loc_pop(&loc);
+ return res;
+}
+
+SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun)
+{
+ SCSIRequest *req;
+
+ req = qemu_mallocz(size);
+ req->bus = scsi_bus_from_device(d);
+ req->dev = d;
+ req->tag = tag;
+ req->lun = lun;
+ req->status = -1;
+ req->enqueued = true;
+ QTAILQ_INSERT_TAIL(&d->requests, req, next);
+ return req;
+}
+
+SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
+{
+ SCSIRequest *req;
+
+ QTAILQ_FOREACH(req, &d->requests, next) {
+ if (req->tag == tag) {
+ return req;
+ }
+ }
+ return NULL;
+}
+
+static void scsi_req_dequeue(SCSIRequest *req)
+{
+ if (req->enqueued) {
+ QTAILQ_REMOVE(&req->dev->requests, req, next);
+ req->enqueued = false;
+ }
+}
+
+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) {
+ case 0:
+ req->cmd.xfer = cmd[4];
+ req->cmd.len = 6;
+ /* length 0 means 256 blocks */
+ if (req->cmd.xfer == 0)
+ req->cmd.xfer = 256;
+ break;
+ case 1:
+ case 2:
+ req->cmd.xfer = cmd[8] | (cmd[7] << 8);
+ req->cmd.len = 10;
+ break;
+ case 4:
+ req->cmd.xfer = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24);
+ req->cmd.len = 16;
+ break;
+ case 5:
+ req->cmd.xfer = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24);
+ req->cmd.len = 12;
+ break;
+ default:
+ return -1;
+ }
+
+ switch(cmd[0]) {
+ case TEST_UNIT_READY:
+ case REZERO_UNIT:
+ case START_STOP:
+ case SEEK_6:
+ case WRITE_FILEMARKS:
+ case SPACE:
+ case RESERVE:
+ case RELEASE:
+ case ERASE:
+ case ALLOW_MEDIUM_REMOVAL:
+ case VERIFY:
+ case SEEK_10:
+ case SYNCHRONIZE_CACHE:
+ case LOCK_UNLOCK_CACHE:
+ case LOAD_UNLOAD:
+ case SET_CD_SPEED:
+ case SET_LIMITS:
+ case WRITE_LONG:
+ case MOVE_MEDIUM:
+ case UPDATE_BLOCK:
+ req->cmd.xfer = 0;
+ break;
+ case MODE_SENSE:
+ break;
+ case WRITE_SAME:
+ req->cmd.xfer = 1;
+ break;
+ case READ_CAPACITY:
+ req->cmd.xfer = 8;
+ break;
+ case READ_BLOCK_LIMITS:
+ req->cmd.xfer = 6;
+ break;
+ case READ_POSITION:
+ req->cmd.xfer = 20;
+ break;
+ case SEND_VOLUME_TAG:
+ req->cmd.xfer *= 40;
+ break;
+ case MEDIUM_SCAN:
+ req->cmd.xfer *= 8;
+ break;
+ case WRITE_10:
+ case WRITE_VERIFY:
+ case WRITE_6:
+ case WRITE_12:
+ case WRITE_VERIFY_12:
+ case WRITE_16:
+ case WRITE_VERIFY_16:
+ req->cmd.xfer *= req->dev->blocksize;
+ break;
+ case READ_10:
+ case READ_6:
+ case READ_REVERSE:
+ case RECOVER_BUFFERED_DATA:
+ case READ_12:
+ case READ_16:
+ req->cmd.xfer *= req->dev->blocksize;
+ break;
+ case INQUIRY:
+ req->cmd.xfer = cmd[4] | (cmd[3] << 8);
+ break;
+ case MAINTENANCE_OUT:
+ case MAINTENANCE_IN:
+ if (req->dev->type == TYPE_ROM) {
+ /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */
+ req->cmd.xfer = cmd[9] | (cmd[8] << 8);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int scsi_req_stream_length(SCSIRequest *req, uint8_t *cmd)
+{
+ switch(cmd[0]) {
+ /* stream commands */
+ case READ_6:
+ case READ_REVERSE:
+ case RECOVER_BUFFERED_DATA:
+ case WRITE_6:
+ req->cmd.len = 6;
+ req->cmd.xfer = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16);
+ if (cmd[1] & 0x01) /* fixed */
+ req->cmd.xfer *= req->dev->blocksize;
+ break;
+ case REWIND:
+ case START_STOP:
+ req->cmd.len = 6;
+ req->cmd.xfer = 0;
+ break;
+ /* generic commands */
+ default:
+ return scsi_req_length(req, cmd);
+ }
+ return 0;
+}
+
+static void scsi_req_xfer_mode(SCSIRequest *req)
+{
+ switch (req->cmd.buf[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_VERIFY:
+ case WRITE_12:
+ case WRITE_VERIFY_12:
+ case WRITE_16:
+ case WRITE_VERIFY_16:
+ case COPY:
+ case COPY_VERIFY:
+ case COMPARE:
+ case CHANGE_DEFINITION:
+ case LOG_SELECT:
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case SEND_DIAGNOSTIC:
+ case WRITE_BUFFER:
+ case FORMAT_UNIT:
+ case REASSIGN_BLOCKS:
+ case SEARCH_EQUAL:
+ case SEARCH_HIGH:
+ case SEARCH_LOW:
+ case UPDATE_BLOCK:
+ case WRITE_LONG:
+ case WRITE_SAME:
+ case SEARCH_HIGH_12:
+ case SEARCH_EQUAL_12:
+ case SEARCH_LOW_12:
+ case SET_WINDOW:
+ case MEDIUM_SCAN:
+ case SEND_VOLUME_TAG:
+ case WRITE_LONG_2:
+ case PERSISTENT_RESERVE_OUT:
+ case MAINTENANCE_OUT:
+ req->cmd.mode = SCSI_XFER_TO_DEV;
+ break;
+ default:
+ if (req->cmd.xfer)
+ req->cmd.mode = SCSI_XFER_FROM_DEV;
+ else {
+ req->cmd.mode = SCSI_XFER_NONE;
+ }
+ break;
+ }
+}
+
+static uint64_t scsi_req_lba(SCSIRequest *req)
+{
+ uint8_t *buf = req->cmd.buf;
+ uint64_t lba;
+
+ switch (buf[0] >> 5) {
+ case 0:
+ lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) |
+ (((uint64_t) buf[1] & 0x1f) << 16);
+ break;
+ case 1:
+ case 2:
+ lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
+ ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
+ break;
+ case 4:
+ lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) |
+ ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) |
+ ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) |
+ ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56);
+ break;
+ case 5:
+ lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
+ ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
+ break;
+ default:
+ lba = -1;
+
+ }
+ return lba;
+}
+
+int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
+{
+ int rc;
+
+ if (req->dev->type == TYPE_TAPE) {
+ rc = scsi_req_stream_length(req, buf);
+ } else {
+ rc = scsi_req_length(req, buf);
+ }
+ if (rc != 0)
+ return rc;
+
+ memcpy(req->cmd.buf, buf, req->cmd.len);
+ scsi_req_xfer_mode(req);
+ req->cmd.lba = scsi_req_lba(req);
+ return 0;
+}
+
+static const char *scsi_command_name(uint8_t cmd)
+{
+ static const char *names[] = {
+ [ TEST_UNIT_READY ] = "TEST_UNIT_READY",
+ [ REZERO_UNIT ] = "REZERO_UNIT",
+ /* REWIND and REZERO_UNIT use the same operation code */
+ [ REQUEST_SENSE ] = "REQUEST_SENSE",
+ [ FORMAT_UNIT ] = "FORMAT_UNIT",
+ [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS",
+ [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS",
+ [ READ_6 ] = "READ_6",
+ [ WRITE_6 ] = "WRITE_6",
+ [ SEEK_6 ] = "SEEK_6",
+ [ READ_REVERSE ] = "READ_REVERSE",
+ [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS",
+ [ SPACE ] = "SPACE",
+ [ INQUIRY ] = "INQUIRY",
+ [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA",
+ [ MAINTENANCE_IN ] = "MAINTENANCE_IN",
+ [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT",
+ [ MODE_SELECT ] = "MODE_SELECT",
+ [ RESERVE ] = "RESERVE",
+ [ RELEASE ] = "RELEASE",
+ [ COPY ] = "COPY",
+ [ ERASE ] = "ERASE",
+ [ MODE_SENSE ] = "MODE_SENSE",
+ [ START_STOP ] = "START_STOP",
+ [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC",
+ [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC",
+ [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL",
+
+ [ SET_WINDOW ] = "SET_WINDOW",
+ [ READ_CAPACITY ] = "READ_CAPACITY",
+ [ READ_10 ] = "READ_10",
+ [ WRITE_10 ] = "WRITE_10",
+ [ SEEK_10 ] = "SEEK_10",
+ [ WRITE_VERIFY ] = "WRITE_VERIFY",
+ [ VERIFY ] = "VERIFY",
+ [ SEARCH_HIGH ] = "SEARCH_HIGH",
+ [ SEARCH_EQUAL ] = "SEARCH_EQUAL",
+ [ SEARCH_LOW ] = "SEARCH_LOW",
+ [ SET_LIMITS ] = "SET_LIMITS",
+ [ PRE_FETCH ] = "PRE_FETCH",
+ /* READ_POSITION and PRE_FETCH use the same operation code */
+ [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE",
+ [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE",
+ [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA",
+ [ MEDIUM_SCAN ] = "MEDIUM_SCAN",
+ [ COMPARE ] = "COMPARE",
+ [ COPY_VERIFY ] = "COPY_VERIFY",
+ [ WRITE_BUFFER ] = "WRITE_BUFFER",
+ [ READ_BUFFER ] = "READ_BUFFER",
+ [ UPDATE_BLOCK ] = "UPDATE_BLOCK",
+ [ READ_LONG ] = "READ_LONG",
+ [ WRITE_LONG ] = "WRITE_LONG",
+ [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION",
+ [ WRITE_SAME ] = "WRITE_SAME",
+ [ READ_TOC ] = "READ_TOC",
+ [ LOG_SELECT ] = "LOG_SELECT",
+ [ LOG_SENSE ] = "LOG_SENSE",
+ [ MODE_SELECT_10 ] = "MODE_SELECT_10",
+ [ RESERVE_10 ] = "RESERVE_10",
+ [ RELEASE_10 ] = "RELEASE_10",
+ [ MODE_SENSE_10 ] = "MODE_SENSE_10",
+ [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN",
+ [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT",
+ [ MOVE_MEDIUM ] = "MOVE_MEDIUM",
+ [ READ_12 ] = "READ_12",
+ [ WRITE_12 ] = "WRITE_12",
+ [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12",
+ [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12",
+ [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12",
+ [ SEARCH_LOW_12 ] = "SEARCH_LOW_12",
+ [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS",
+ [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG",
+ [ WRITE_LONG_2 ] = "WRITE_LONG_2",
+
+ [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
+ [ GET_CONFIGURATION ] = "GET_CONFIGURATION",
+ [ READ_16 ] = "READ_16",
+ [ WRITE_16 ] = "WRITE_16",
+ [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16",
+ [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN",
+ [ REPORT_LUNS ] = "REPORT_LUNS",
+ [ LOAD_UNLOAD ] = "LOAD_UNLOAD",
+ [ SET_CD_SPEED ] = "SET_CD_SPEED",
+ [ BLANK ] = "BLANK",
+ };
+
+ if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
+ return "*UNKNOWN*";
+ return names[cmd];
+}
+
+void scsi_req_print(SCSIRequest *req)
+{
+ FILE *fp = stderr;
+ int i;
+
+ fprintf(fp, "[%s id=%d] %s",
+ req->dev->qdev.parent_bus->name,
+ req->dev->id,
+ scsi_command_name(req->cmd.buf[0]));
+ for (i = 1; i < req->cmd.len; i++) {
+ fprintf(fp, " 0x%02x", req->cmd.buf[i]);
+ }
+ switch (req->cmd.mode) {
+ case SCSI_XFER_NONE:
+ fprintf(fp, " - none\n");
+ break;
+ case SCSI_XFER_FROM_DEV:
+ fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer);
+ break;
+ case SCSI_XFER_TO_DEV:
+ fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer);
+ break;
+ default:
+ fprintf(fp, " - Oops\n");
+ break;
+ }
+}
+
+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);
+}
+
+static char *scsibus_get_fw_dev_path(DeviceState *dev)
+{
+ SCSIDevice *d = (SCSIDevice*)dev;
+ SCSIBus *bus = scsi_bus_from_device(d);
+ char path[100];
+ int i;
+
+ for (i = 0; i < bus->ndev; i++) {
+ if (bus->devs[i] == d) {
+ break;
+ }
+ }
+
+ assert(i != bus->ndev);
+
+ snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i);
+
+ return strdup(path);
+}
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
new file mode 100644
index 0000000..413cce0
--- /dev/null
+++ b/hw/scsi-defs.h
@@ -0,0 +1,166 @@
+/* Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This header file contains public constants and structures used by
+ * the scsi code for linux.
+ */
+
+/*
+ * SCSI opcodes
+ */
+
+#define TEST_UNIT_READY 0x00
+#define REZERO_UNIT 0x01
+#define REQUEST_SENSE 0x03
+#define FORMAT_UNIT 0x04
+#define READ_BLOCK_LIMITS 0x05
+#define REASSIGN_BLOCKS 0x07
+#define READ_6 0x08
+#define WRITE_6 0x0a
+#define SEEK_6 0x0b
+#define READ_REVERSE 0x0f
+#define WRITE_FILEMARKS 0x10
+#define SPACE 0x11
+#define INQUIRY 0x12
+#define RECOVER_BUFFERED_DATA 0x14
+#define MODE_SELECT 0x15
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define COPY 0x18
+#define ERASE 0x19
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define RECEIVE_DIAGNOSTIC 0x1c
+#define SEND_DIAGNOSTIC 0x1d
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+
+#define SET_WINDOW 0x24
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define WRITE_10 0x2a
+#define SEEK_10 0x2b
+#define WRITE_VERIFY 0x2e
+#define VERIFY 0x2f
+#define SEARCH_HIGH 0x30
+#define SEARCH_EQUAL 0x31
+#define SEARCH_LOW 0x32
+#define SET_LIMITS 0x33
+#define PRE_FETCH 0x34
+#define READ_POSITION 0x34
+#define SYNCHRONIZE_CACHE 0x35
+#define LOCK_UNLOCK_CACHE 0x36
+#define READ_DEFECT_DATA 0x37
+#define MEDIUM_SCAN 0x38
+#define COMPARE 0x39
+#define COPY_VERIFY 0x3a
+#define WRITE_BUFFER 0x3b
+#define READ_BUFFER 0x3c
+#define UPDATE_BLOCK 0x3d
+#define READ_LONG 0x3e
+#define WRITE_LONG 0x3f
+#define CHANGE_DEFINITION 0x40
+#define WRITE_SAME 0x41
+#define READ_TOC 0x43
+#define LOG_SELECT 0x4c
+#define LOG_SENSE 0x4d
+#define MODE_SELECT_10 0x55
+#define RESERVE_10 0x56
+#define RELEASE_10 0x57
+#define MODE_SENSE_10 0x5a
+#define PERSISTENT_RESERVE_IN 0x5e
+#define PERSISTENT_RESERVE_OUT 0x5f
+#define WRITE_SAME_16 0x93
+#define MAINTENANCE_IN 0xa3
+#define MAINTENANCE_OUT 0xa4
+#define MOVE_MEDIUM 0xa5
+#define READ_12 0xa8
+#define WRITE_12 0xaa
+#define WRITE_VERIFY_12 0xae
+#define SEARCH_HIGH_12 0xb0
+#define SEARCH_EQUAL_12 0xb1
+#define SEARCH_LOW_12 0xb2
+#define READ_ELEMENT_STATUS 0xb8
+#define SEND_VOLUME_TAG 0xb6
+#define WRITE_LONG_2 0xea
+
+/* from hw/scsi-generic.c */
+#define REWIND 0x01
+#define REPORT_DENSITY_SUPPORT 0x44
+#define GET_CONFIGURATION 0x46
+#define READ_16 0x88
+#define WRITE_16 0x8a
+#define WRITE_VERIFY_16 0x8e
+#define SERVICE_ACTION_IN 0x9e
+#define REPORT_LUNS 0xa0
+#define LOAD_UNLOAD 0xa6
+#define SET_CD_SPEED 0xbb
+#define BLANK 0xa1
+
+/*
+ * SAM Status codes
+ */
+
+#define GOOD 0x00
+#define CHECK_CONDITION 0x02
+#define CONDITION_GOOD 0x04
+#define BUSY 0x08
+#define INTERMEDIATE_GOOD 0x10
+#define INTERMEDIATE_C_GOOD 0x14
+#define RESERVATION_CONFLICT 0x18
+#define COMMAND_TERMINATED 0x22
+#define TASK_SET_FULL 0x28
+#define ACA_ACTIVE 0x30
+#define TASK_ABORTED 0x40
+
+#define STATUS_MASK 0x3e
+
+/*
+ * SENSE KEYS
+ */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define BLANK_CHECK 0x08
+#define COPY_ABORTED 0x0a
+#define ABORTED_COMMAND 0x0b
+#define VOLUME_OVERFLOW 0x0d
+#define MISCOMPARE 0x0e
+
+
+/*
+ * DEVICE TYPES
+ */
+
+#define TYPE_DISK 0x00
+#define TYPE_TAPE 0x01
+#define TYPE_PROCESSOR 0x03 /* HP scanners use this */
+#define TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define TYPE_ROM 0x05
+#define TYPE_SCANNER 0x06
+#define TYPE_MOD 0x07 /* Magneto-optical disk -
+ * - treated as TYPE_DISK */
+#define TYPE_MEDIUM_CHANGER 0x08
+#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define TYPE_NO_LUN 0x7f
+
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
new file mode 100644
index 0000000..488eedd
--- /dev/null
+++ b/hw/scsi-disk.c
@@ -0,0 +1,1309 @@
+/*
+ * SCSI Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Paul Brook
+ * Modifications:
+ * 2009-Dec-12 Artyom Tarasenko : implemented stamdard inquiry for the case
+ * when the allocation length of CDB is smaller
+ * than 36.
+ * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the
+ * MODE SENSE response.
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Note that this file only handles the SCSI architecture model and device
+ * commands. Emulation of interface/link layer protocols is handled by
+ * the host adapter emulator.
+ */
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, ...) \
+do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "scsi.h"
+#include "scsi-defs.h"
+#include "sysemu.h"
+#include "blockdev.h"
+
+#define SCSI_DMA_BUF_SIZE 131072
+#define SCSI_MAX_INQUIRY_LEN 256
+
+#define SCSI_REQ_STATUS_RETRY 0x01
+#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06
+#define SCSI_REQ_STATUS_RETRY_READ 0x00
+#define SCSI_REQ_STATUS_RETRY_WRITE 0x02
+#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04
+
+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
+ 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;
+ struct iovec iov;
+ QEMUIOVector qiov;
+ uint32_t status;
+} SCSIDiskReq;
+
+struct SCSIDiskState
+{
+ SCSIDevice qdev;
+ BlockDriverState *bs;
+ /* The qemu block layer uses a fixed 512 byte sector size.
+ This is the number of 512 byte blocks in a single scsi sector. */
+ int cluster_size;
+ uint32_t removable;
+ uint64_t max_lba;
+ QEMUBH *bh;
+ char *version;
+ char *serial;
+ SCSISense sense;
+};
+
+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,
+ uint32_t lun)
+{
+ 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;
+}
+
+static void scsi_remove_request(SCSIDiskReq *r)
+{
+ qemu_vfree(r->iov.iov_base);
+ scsi_req_free(&r->req);
+}
+
+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));
+}
+
+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)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ r->req.status = status;
+ scsi_disk_set_sense(s, sense_code);
+}
+
+/* Helper function for command completion. */
+static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
+{
+ DPRINTF("Command complete tag=0x%x status=%d sense=%d\n",
+ 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. */
+static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+{
+ 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_remove_request(r);
+ }
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ int n;
+
+ r->req.aiocb = NULL;
+
+ if (ret) {
+ if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) {
+ return;
+ }
+ }
+
+ DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->iov.iov_len);
+
+ 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);
+}
+
+
+static void scsi_read_request(SCSIDiskReq *r)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ uint32_t n;
+
+ 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);
+ return;
+ }
+ DPRINTF("Read sector_count=%d\n", r->sector_count);
+ if (r->sector_count == 0) {
+ scsi_command_complete(r, GOOD, NO_SENSE);
+ return;
+ }
+
+ /* No data transfer may already be in progress */
+ assert(r->req.aiocb == NULL);
+
+ n = r->sector_count;
+ if (n > SCSI_DMA_BUF_SIZE / 512)
+ n = SCSI_DMA_BUF_SIZE / 512;
+
+ r->iov.iov_len = n * 512;
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+ r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n,
+ scsi_read_complete, r);
+ if (r->req.aiocb == NULL) {
+ scsi_read_complete(r, -EIO);
+ }
+}
+
+/* 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);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ BlockErrorAction action = bdrv_get_on_error(s->bs, is_read);
+
+ if (action == BLOCK_ERR_IGNORE) {
+ bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ return 0;
+ }
+
+ if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
+ || action == BLOCK_ERR_STOP_ANY) {
+
+ type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK;
+ r->status |= SCSI_REQ_STATUS_RETRY | type;
+
+ bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ vm_stop(0);
+ } else {
+ if (type == SCSI_REQ_STATUS_RETRY_READ) {
+ r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0);
+ }
+ scsi_command_complete(r, CHECK_CONDITION,
+ HARDWARE_ERROR);
+ bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ }
+
+ return 1;
+}
+
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ uint32_t len;
+ uint32_t n;
+
+ r->req.aiocb = NULL;
+
+ if (ret) {
+ if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) {
+ return;
+ }
+ }
+
+ n = r->iov.iov_len / 512;
+ r->sector += n;
+ r->sector_count -= n;
+ if (r->sector_count == 0) {
+ scsi_command_complete(r, GOOD, NO_SENSE);
+ } else {
+ len = r->sector_count * 512;
+ if (len > SCSI_DMA_BUF_SIZE) {
+ len = SCSI_DMA_BUF_SIZE;
+ }
+ 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);
+ }
+}
+
+static void scsi_write_request(SCSIDiskReq *r)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ uint32_t n;
+
+ /* No data transfer may already be in progress */
+ assert(r->req.aiocb == NULL);
+
+ n = r->iov.iov_len / 512;
+ if (n) {
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+ 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);
+ }
+ } else {
+ /* 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;
+}
+
+static void scsi_dma_restart_bh(void *opaque)
+{
+ SCSIDiskState *s = opaque;
+ SCSIRequest *req;
+ SCSIDiskReq *r;
+
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
+
+ QTAILQ_FOREACH(req, &s->qdev.requests, next) {
+ r = DO_UPCAST(SCSIDiskReq, req, req);
+ if (r->status & SCSI_REQ_STATUS_RETRY) {
+ int status = r->status;
+ int ret;
+
+ r->status &=
+ ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK);
+
+ switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
+ case SCSI_REQ_STATUS_RETRY_READ:
+ scsi_read_request(r);
+ break;
+ case SCSI_REQ_STATUS_RETRY_WRITE:
+ scsi_write_request(r);
+ break;
+ 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);
+ }
+ }
+ }
+ }
+}
+
+static void scsi_dma_restart_cb(void *opaque, int running, int reason)
+{
+ SCSIDiskState *s = opaque;
+
+ if (!running)
+ return;
+
+ if (!s->bh) {
+ s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
+/* Return a pointer to the data buffer. */
+static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+ SCSIDiskReq *r;
+
+ 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;
+}
+
+static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ int buflen = 0;
+
+ if (req->cmd.buf[1] & 0x2) {
+ /* Command support data - optional, not implemented */
+ BADF("optional INQUIRY command support request not implemented\n");
+ return -1;
+ }
+
+ if (req->cmd.buf[1] & 0x1) {
+ /* Vital product data */
+ uint8_t page_code = req->cmd.buf[2];
+ if (req->cmd.xfer < 4) {
+ BADF("Error: Inquiry (EVPD[%02X]) buffer size %zd is "
+ "less than 4\n", page_code, req->cmd.xfer);
+ return -1;
+ }
+
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ outbuf[buflen++] = 5;
+ } else {
+ outbuf[buflen++] = 0;
+ }
+ outbuf[buflen++] = page_code ; // this page
+ outbuf[buflen++] = 0x00;
+
+ switch (page_code) {
+ case 0x00: /* Supported page codes, mandatory */
+ {
+ int pages;
+ DPRINTF("Inquiry EVPD[Supported pages] "
+ "buffer size %zd\n", req->cmd.xfer);
+ pages = buflen++;
+ 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) {
+ outbuf[buflen++] = 0xb0; // block limits
+ outbuf[buflen++] = 0xb2; // thin provisioning
+ }
+ outbuf[pages] = buflen - pages - 1; // number of pages
+ break;
+ }
+ case 0x80: /* Device serial number, optional */
+ {
+ int l = strlen(s->serial);
+
+ if (l > req->cmd.xfer)
+ l = req->cmd.xfer;
+ if (l > 20)
+ l = 20;
+
+ DPRINTF("Inquiry EVPD[Serial number] "
+ "buffer size %zd\n", req->cmd.xfer);
+ outbuf[buflen++] = l;
+ memcpy(outbuf+buflen, s->serial, l);
+ buflen += l;
+ break;
+ }
+
+ case 0x83: /* Device identification page, mandatory */
+ {
+ int max_len = 255 - 8;
+ int id_len = strlen(bdrv_get_device_name(s->bs));
+
+ if (id_len > max_len)
+ id_len = max_len;
+ DPRINTF("Inquiry EVPD[Device identification] "
+ "buffer size %zd\n", req->cmd.xfer);
+
+ outbuf[buflen++] = 4 + id_len;
+ outbuf[buflen++] = 0x2; // ASCII
+ outbuf[buflen++] = 0; // not officially assigned
+ outbuf[buflen++] = 0; // reserved
+ outbuf[buflen++] = id_len; // length of data following
+
+ memcpy(outbuf+buflen, bdrv_get_device_name(s->bs), id_len);
+ buflen += id_len;
+ break;
+ }
+ case 0xb0: /* block limits */
+ {
+ unsigned int unmap_sectors =
+ s->qdev.conf.discard_granularity / s->qdev.blocksize;
+ unsigned int min_io_size =
+ s->qdev.conf.min_io_size / s->qdev.blocksize;
+ unsigned int opt_io_size =
+ s->qdev.conf.opt_io_size / s->qdev.blocksize;
+
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
+ page_code);
+ return -1;
+ }
+ /* required VPD size with unmap support */
+ outbuf[3] = buflen = 0x3c;
+
+ memset(outbuf + 4, 0, buflen - 4);
+
+ /* optimal transfer length granularity */
+ outbuf[6] = (min_io_size >> 8) & 0xff;
+ outbuf[7] = min_io_size & 0xff;
+
+ /* optimal transfer length */
+ outbuf[12] = (opt_io_size >> 24) & 0xff;
+ outbuf[13] = (opt_io_size >> 16) & 0xff;
+ outbuf[14] = (opt_io_size >> 8) & 0xff;
+ outbuf[15] = opt_io_size & 0xff;
+
+ /* optimal unmap granularity */
+ outbuf[28] = (unmap_sectors >> 24) & 0xff;
+ outbuf[29] = (unmap_sectors >> 16) & 0xff;
+ outbuf[30] = (unmap_sectors >> 8) & 0xff;
+ outbuf[31] = unmap_sectors & 0xff;
+ break;
+ }
+ case 0xb2: /* thin provisioning */
+ {
+ outbuf[3] = buflen = 8;
+ outbuf[4] = 0;
+ outbuf[5] = 0x40; /* write same with unmap supported */
+ outbuf[6] = 0;
+ outbuf[7] = 0;
+ break;
+ }
+ default:
+ BADF("Error: unsupported Inquiry (EVPD[%02X]) "
+ "buffer size %zd\n", page_code, req->cmd.xfer);
+ return -1;
+ }
+ /* done with EVPD */
+ return buflen;
+ }
+
+ /* Standard INQUIRY data */
+ if (req->cmd.buf[2] != 0) {
+ BADF("Error: Inquiry (STANDARD) page or code "
+ "is non-zero [%02X]\n", req->cmd.buf[2]);
+ return -1;
+ }
+
+ /* PAGE CODE == 0 */
+ if (req->cmd.xfer < 5) {
+ BADF("Error: Inquiry (STANDARD) buffer size %zd "
+ "is less than 5\n", req->cmd.xfer);
+ return -1;
+ }
+
+ buflen = req->cmd.xfer;
+ if (buflen > SCSI_MAX_INQUIRY_LEN)
+ buflen = SCSI_MAX_INQUIRY_LEN;
+
+ memset(outbuf, 0, buflen);
+
+ if (req->lun || req->cmd.buf[1] >> 5) {
+ outbuf[0] = 0x7f; /* LUN not supported */
+ return buflen;
+ }
+
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ outbuf[0] = 5;
+ outbuf[1] = 0x80;
+ memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
+ } else {
+ outbuf[0] = 0;
+ outbuf[1] = s->removable ? 0x80 : 0;
+ memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
+ }
+ memcpy(&outbuf[8], "QEMU ", 8);
+ memset(&outbuf[32], 0, 4);
+ memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version)));
+ /*
+ * We claim conformance to SPC-3, which is required for guests
+ * to ask for modern features like READ CAPACITY(16) or the
+ * block characteristics VPD page by default. Not all of SPC-3
+ * is actually implemented, but we're good enough.
+ */
+ outbuf[2] = 5;
+ outbuf[3] = 2; /* Format 2 */
+
+ if (buflen > 36) {
+ outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
+ } else {
+ /* If the allocation length of CDB is too small,
+ the additional length is not adjusted */
+ outbuf[4] = 36 - 5;
+ }
+
+ /* Sync data transfer and TCQ. */
+ outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0);
+ return buflen;
+}
+
+static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p,
+ int page_control)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ BlockDriverState *bdrv = s->bs;
+ int cylinders, heads, secs;
+
+ /*
+ * If Changeable Values are requested, a mask denoting those mode parameters
+ * that are changeable shall be returned. As we currently don't support
+ * parameter changes via MODE_SELECT all bits are returned set to zero.
+ * The buffer was already menset to zero by the caller of this function.
+ */
+ switch (page) {
+ case 4: /* Rigid disk device geometry page. */
+ p[0] = 4;
+ p[1] = 0x16;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
+ p[2] = (cylinders >> 16) & 0xff;
+ p[3] = (cylinders >> 8) & 0xff;
+ p[4] = cylinders & 0xff;
+ p[5] = heads & 0xff;
+ /* Write precomp start cylinder, disabled */
+ p[6] = (cylinders >> 16) & 0xff;
+ p[7] = (cylinders >> 8) & 0xff;
+ p[8] = cylinders & 0xff;
+ /* Reduced current start cylinder, disabled */
+ p[9] = (cylinders >> 16) & 0xff;
+ p[10] = (cylinders >> 8) & 0xff;
+ p[11] = cylinders & 0xff;
+ /* Device step rate [ns], 200ns */
+ p[12] = 0;
+ p[13] = 200;
+ /* Landing zone cylinder */
+ p[14] = 0xff;
+ p[15] = 0xff;
+ p[16] = 0xff;
+ /* Medium rotation rate [rpm], 5400 rpm */
+ p[20] = (5400 >> 8) & 0xff;
+ p[21] = 5400 & 0xff;
+ return p[1] + 2;
+
+ case 5: /* Flexible disk device geometry page. */
+ p[0] = 5;
+ p[1] = 0x1e;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
+ /* Transfer rate [kbit/s], 5Mbit/s */
+ p[2] = 5000 >> 8;
+ p[3] = 5000 & 0xff;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
+ p[4] = heads & 0xff;
+ p[5] = secs & 0xff;
+ p[6] = s->cluster_size * 2;
+ p[8] = (cylinders >> 8) & 0xff;
+ p[9] = cylinders & 0xff;
+ /* Write precomp start cylinder, disabled */
+ p[10] = (cylinders >> 8) & 0xff;
+ p[11] = cylinders & 0xff;
+ /* Reduced current start cylinder, disabled */
+ p[12] = (cylinders >> 8) & 0xff;
+ p[13] = cylinders & 0xff;
+ /* Device step rate [100us], 100us */
+ p[14] = 0;
+ p[15] = 1;
+ /* Device step pulse width [us], 1us */
+ p[16] = 1;
+ /* Device head settle delay [100us], 100us */
+ p[17] = 0;
+ p[18] = 1;
+ /* Motor on delay [0.1s], 0.1s */
+ p[19] = 1;
+ /* Motor off delay [0.1s], 0.1s */
+ p[20] = 1;
+ /* Medium rotation rate [rpm], 5400 rpm */
+ p[28] = (5400 >> 8) & 0xff;
+ p[29] = 5400 & 0xff;
+ return p[1] + 2;
+
+ case 8: /* Caching page. */
+ p[0] = 8;
+ p[1] = 0x12;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
+ if (bdrv_enable_write_cache(s->bs)) {
+ p[2] = 4; /* WCE */
+ }
+ return p[1] + 2;
+
+ case 0x2a: /* CD Capabilities and Mechanical Status page. */
+ if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM)
+ return 0;
+ p[0] = 0x2a;
+ p[1] = 0x14;
+ if (page_control == 1) { /* Changeable Values */
+ return p[1] + 2;
+ }
+ p[2] = 3; // CD-R & CD-RW read
+ p[3] = 0; // Writing not supported
+ p[4] = 0x7f; /* Audio, composite, digital out,
+ mode 2 form 1&2, multi session */
+ p[5] = 0xff; /* CD DA, DA accurate, RW supported,
+ RW corrected, C2 errors, ISRC,
+ UPC, Bar code */
+ p[6] = 0x2d | (bdrv_is_locked(s->bs)? 2 : 0);
+ /* Locking supported, jumper present, eject, tray */
+ p[7] = 0; /* no volume & mute control, no
+ changer */
+ p[8] = (50 * 176) >> 8; // 50x read speed
+ p[9] = (50 * 176) & 0xff;
+ p[10] = 0 >> 8; // No volume
+ p[11] = 0 & 0xff;
+ p[12] = 2048 >> 8; // 2M buffer
+ p[13] = 2048 & 0xff;
+ p[14] = (16 * 176) >> 8; // 16x read speed current
+ p[15] = (16 * 176) & 0xff;
+ p[18] = (16 * 176) >> 8; // 16x write speed
+ p[19] = (16 * 176) & 0xff;
+ p[20] = (16 * 176) >> 8; // 16x write speed current
+ p[21] = (16 * 176) & 0xff;
+ return p[1] + 2;
+
+ default:
+ return 0;
+ }
+}
+
+static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ uint64_t nb_sectors;
+ int page, dbd, buflen, page_control;
+ uint8_t *p;
+ uint8_t dev_specific_param;
+
+ dbd = req->cmd.buf[1] & 0x8;
+ page = req->cmd.buf[2] & 0x3f;
+ page_control = (req->cmd.buf[2] & 0xc0) >> 6;
+ DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
+ (req->cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, req->cmd.xfer, page_control);
+ memset(outbuf, 0, req->cmd.xfer);
+ p = outbuf;
+
+ if (bdrv_is_read_only(s->bs)) {
+ dev_specific_param = 0x80; /* Readonly. */
+ } else {
+ dev_specific_param = 0x00;
+ }
+
+ if (req->cmd.buf[0] == MODE_SENSE) {
+ p[1] = 0; /* Default media type. */
+ p[2] = dev_specific_param;
+ p[3] = 0; /* Block descriptor length. */
+ p += 4;
+ } else { /* MODE_SENSE_10 */
+ p[2] = 0; /* Default media type. */
+ p[3] = dev_specific_param;
+ p[6] = p[7] = 0; /* Block descriptor length. */
+ p += 8;
+ }
+
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ if (!dbd && nb_sectors) {
+ if (req->cmd.buf[0] == MODE_SENSE) {
+ outbuf[3] = 8; /* Block descriptor length */
+ } else { /* MODE_SENSE_10 */
+ outbuf[7] = 8; /* Block descriptor length */
+ }
+ nb_sectors /= s->cluster_size;
+ if (nb_sectors > 0xffffff)
+ nb_sectors = 0;
+ p[0] = 0; /* media density code */
+ p[1] = (nb_sectors >> 16) & 0xff;
+ p[2] = (nb_sectors >> 8) & 0xff;
+ p[3] = nb_sectors & 0xff;
+ p[4] = 0; /* reserved */
+ p[5] = 0; /* bytes 5-7 are the sector size in bytes */
+ p[6] = s->cluster_size * 2;
+ p[7] = 0;
+ p += 8;
+ }
+
+ if (page_control == 3) { /* Saved Values */
+ return -1; /* ILLEGAL_REQUEST */
+ }
+
+ switch (page) {
+ case 0x04:
+ case 0x05:
+ case 0x08:
+ case 0x2a:
+ p += mode_sense_page(req, page, p, page_control);
+ break;
+ case 0x3f:
+ p += mode_sense_page(req, 0x08, p, page_control);
+ p += mode_sense_page(req, 0x2a, p, page_control);
+ break;
+ default:
+ return -1; /* ILLEGAL_REQUEST */
+ }
+
+ buflen = p - outbuf;
+ /*
+ * The mode data length field specifies the length in bytes of the
+ * following data that is available to be transferred. The mode data
+ * length does not include itself.
+ */
+ if (req->cmd.buf[0] == MODE_SENSE) {
+ outbuf[0] = buflen - 1;
+ } else { /* MODE_SENSE_10 */
+ outbuf[0] = ((buflen - 2) >> 8) & 0xff;
+ outbuf[1] = (buflen - 2) & 0xff;
+ }
+ if (buflen > req->cmd.xfer)
+ buflen = req->cmd.xfer;
+ return buflen;
+}
+
+static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ int start_track, format, msf, toclen;
+ uint64_t nb_sectors;
+
+ msf = req->cmd.buf[1] & 2;
+ format = req->cmd.buf[2] & 0xf;
+ start_track = req->cmd.buf[6];
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+ nb_sectors /= s->cluster_size;
+ switch (format) {
+ case 0:
+ toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
+ break;
+ case 1:
+ /* multi session : only a single session defined */
+ toclen = 12;
+ memset(outbuf, 0, 12);
+ outbuf[1] = 0x0a;
+ outbuf[2] = 0x01;
+ outbuf[3] = 0x01;
+ break;
+ case 2:
+ toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
+ break;
+ default:
+ return -1;
+ }
+ if (toclen > req->cmd.xfer)
+ toclen = req->cmd.xfer;
+ return toclen;
+}
+
+static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
+{
+ SCSIRequest *req = &r->req;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+ uint64_t nb_sectors;
+ int buflen = 0;
+ int ret;
+
+ switch (req->cmd.buf[0]) {
+ case TEST_UNIT_READY:
+ if (!bdrv_is_inserted(s->bs))
+ goto not_ready;
+ break;
+ 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;
+ scsi_disk_clear_sense(s);
+ break;
+ case INQUIRY:
+ buflen = scsi_disk_emulate_inquiry(req, outbuf);
+ if (buflen < 0)
+ goto illegal_request;
+ break;
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ buflen = scsi_disk_emulate_mode_sense(req, outbuf);
+ if (buflen < 0)
+ goto illegal_request;
+ break;
+ case READ_TOC:
+ buflen = scsi_disk_emulate_read_toc(req, outbuf);
+ if (buflen < 0)
+ goto illegal_request;
+ break;
+ case RESERVE:
+ if (req->cmd.buf[1] & 1)
+ goto illegal_request;
+ break;
+ case RESERVE_10:
+ if (req->cmd.buf[1] & 3)
+ goto illegal_request;
+ break;
+ case RELEASE:
+ if (req->cmd.buf[1] & 1)
+ goto illegal_request;
+ break;
+ case RELEASE_10:
+ if (req->cmd.buf[1] & 3)
+ goto illegal_request;
+ break;
+ case START_STOP:
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) {
+ /* load/eject medium */
+ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1));
+ }
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ bdrv_set_locked(s->bs, req->cmd.buf[4] & 1);
+ break;
+ case READ_CAPACITY:
+ /* The normal LEN field for this command is zero. */
+ memset(outbuf, 0, 8);
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ if (!nb_sectors)
+ goto not_ready;
+ nb_sectors /= s->cluster_size;
+ /* Returned value is the address of the last sector. */
+ nb_sectors--;
+ /* Remember the new size for read/write sanity checking. */
+ s->max_lba = nb_sectors;
+ /* Clip to 2TB, instead of returning capacity modulo 2TB. */
+ if (nb_sectors > UINT32_MAX)
+ nb_sectors = UINT32_MAX;
+ outbuf[0] = (nb_sectors >> 24) & 0xff;
+ outbuf[1] = (nb_sectors >> 16) & 0xff;
+ outbuf[2] = (nb_sectors >> 8) & 0xff;
+ outbuf[3] = nb_sectors & 0xff;
+ outbuf[4] = 0;
+ outbuf[5] = 0;
+ outbuf[6] = s->cluster_size * 2;
+ outbuf[7] = 0;
+ buflen = 8;
+ break;
+ case SYNCHRONIZE_CACHE:
+ ret = bdrv_flush(s->bs);
+ if (ret < 0) {
+ if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) {
+ return -1;
+ }
+ }
+ break;
+ case GET_CONFIGURATION:
+ memset(outbuf, 0, 8);
+ /* ??? This should probably return much more information. For now
+ just return the basic header indicating the CD-ROM profile. */
+ outbuf[7] = 8; // CD-ROM
+ buflen = 8;
+ break;
+ case SERVICE_ACTION_IN:
+ /* Service Action In subcommands. */
+ if ((req->cmd.buf[1] & 31) == 0x10) {
+ DPRINTF("SAI READ CAPACITY(16)\n");
+ memset(outbuf, 0, req->cmd.xfer);
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ if (!nb_sectors)
+ goto not_ready;
+ nb_sectors /= s->cluster_size;
+ /* Returned value is the address of the last sector. */
+ nb_sectors--;
+ /* Remember the new size for read/write sanity checking. */
+ s->max_lba = nb_sectors;
+ outbuf[0] = (nb_sectors >> 56) & 0xff;
+ outbuf[1] = (nb_sectors >> 48) & 0xff;
+ outbuf[2] = (nb_sectors >> 40) & 0xff;
+ outbuf[3] = (nb_sectors >> 32) & 0xff;
+ outbuf[4] = (nb_sectors >> 24) & 0xff;
+ outbuf[5] = (nb_sectors >> 16) & 0xff;
+ outbuf[6] = (nb_sectors >> 8) & 0xff;
+ outbuf[7] = nb_sectors & 0xff;
+ outbuf[8] = 0;
+ outbuf[9] = 0;
+ outbuf[10] = s->cluster_size * 2;
+ outbuf[11] = 0;
+ outbuf[12] = 0;
+ outbuf[13] = get_physical_block_exp(&s->qdev.conf);
+
+ /* set TPE bit if the format supports discard */
+ if (s->qdev.conf.discard_granularity) {
+ outbuf[14] = 0x80;
+ }
+
+ /* Protection, exponent and lowest lba field left blank. */
+ buflen = req->cmd.xfer;
+ break;
+ }
+ DPRINTF("Unsupported Service Action In\n");
+ goto illegal_request;
+ case REPORT_LUNS:
+ if (req->cmd.xfer < 16)
+ goto illegal_request;
+ memset(outbuf, 0, 16);
+ outbuf[3] = 8;
+ buflen = 16;
+ break;
+ case VERIFY:
+ break;
+ case REZERO_UNIT:
+ DPRINTF("Rezero Unit\n");
+ if (!bdrv_is_inserted(s->bs)) {
+ goto not_ready;
+ }
+ break;
+ default:
+ goto illegal_request;
+ }
+ scsi_req_set_status(r, GOOD, NO_SENSE);
+ return buflen;
+
+not_ready:
+ scsi_command_complete(r, CHECK_CONDITION, NOT_READY);
+ return -1;
+
+illegal_request:
+ scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
+ return -1;
+}
+
+/* Execute a scsi command. Returns the length of the data expected by the
+ command. This will be Positive for data transfers from the device
+ (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)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+ uint32_t len;
+ int is_write;
+ uint8_t command;
+ uint8_t *outbuf;
+ SCSIDiskReq *r;
+ int rc;
+
+ 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]);
+
+ if (scsi_req_parse(&r->req, buf) != 0) {
+ BADF("Unsupported command length, command %x\n", command);
+ goto fail;
+ }
+#ifdef DEBUG_SCSI
+ {
+ int i;
+ for (i = 1; i < r->req.cmd.len; i++) {
+ printf(" 0x%02x", buf[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (lun || buf[1] >> 5) {
+ /* Only LUN 0 supported. */
+ DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
+ if (command != REQUEST_SENSE && command != INQUIRY)
+ goto fail;
+ }
+ switch (command) {
+ case TEST_UNIT_READY:
+ case REQUEST_SENSE:
+ case INQUIRY:
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ case RESERVE:
+ case RESERVE_10:
+ case RELEASE:
+ case RELEASE_10:
+ case START_STOP:
+ case ALLOW_MEDIUM_REMOVAL:
+ case READ_CAPACITY:
+ case SYNCHRONIZE_CACHE:
+ case READ_TOC:
+ case GET_CONFIGURATION:
+ case SERVICE_ACTION_IN:
+ case REPORT_LUNS:
+ case VERIFY:
+ case REZERO_UNIT:
+ rc = scsi_disk_emulate_command(r, outbuf);
+ if (rc < 0) {
+ return 0;
+ }
+
+ r->iov.iov_len = rc;
+ break;
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ len = r->req.cmd.xfer / d->blocksize;
+ DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
+ if (r->req.cmd.lba > s->max_lba)
+ goto illegal_lba;
+ r->sector = r->req.cmd.lba * s->cluster_size;
+ r->sector_count = len * s->cluster_size;
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case WRITE_VERIFY_16:
+ len = r->req.cmd.xfer / d->blocksize;
+ DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
+ (command & 0xe) == 0xe ? "And Verify " : "",
+ r->req.cmd.lba, len);
+ if (r->req.cmd.lba > s->max_lba)
+ 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);
+ /* We don't support mode parameter changes.
+ Allow the mode parameter header + block descriptors only. */
+ if (r->req.cmd.xfer > 12) {
+ goto fail;
+ }
+ break;
+ case MODE_SELECT_10:
+ DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer);
+ /* We don't support mode parameter changes.
+ Allow the mode parameter header + block descriptors only. */
+ if (r->req.cmd.xfer > 16) {
+ goto fail;
+ }
+ break;
+ case SEEK_6:
+ case SEEK_10:
+ DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10,
+ r->req.cmd.lba);
+ if (r->req.cmd.lba > s->max_lba) {
+ goto illegal_lba;
+ }
+ break;
+ case WRITE_SAME_16:
+ len = r->req.cmd.xfer / d->blocksize;
+
+ DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
+ r->req.cmd.lba, len);
+
+ if (r->req.cmd.lba > s->max_lba) {
+ goto illegal_lba;
+ }
+
+ /*
+ * We only support WRITE SAME with the unmap bit set for now.
+ */
+ if (!(buf[1] & 0x8)) {
+ goto fail;
+ }
+
+ rc = bdrv_discard(s->bs, r->req.cmd.lba * s->cluster_size,
+ len * s->cluster_size);
+ if (rc < 0) {
+ /* XXX: better error code ?*/
+ goto fail;
+ }
+
+ break;
+ default:
+ DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+ fail:
+ scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
+ return 0;
+ illegal_lba:
+ scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
+ return 0;
+ }
+ if (r->sector_count == 0 && r->iov.iov_len == 0) {
+ scsi_command_complete(r, GOOD, NO_SENSE);
+ }
+ len = r->sector_count * 512 + r->iov.iov_len;
+ if (is_write) {
+ return -len;
+ } else {
+ if (!r->sector_count)
+ r->sector_count = -1;
+ 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_remove_request(r);
+ }
+}
+
+static void scsi_disk_reset(DeviceState *dev)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
+ uint64_t nb_sectors;
+
+ scsi_disk_purge_requests(s);
+
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ nb_sectors /= s->cluster_size;
+ if (nb_sectors) {
+ nb_sectors--;
+ }
+ s->max_lba = nb_sectors;
+}
+
+static void scsi_destroy(SCSIDevice *dev)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+
+ scsi_disk_purge_requests(s);
+ blockdev_mark_auto_del(s->qdev.conf.bs);
+}
+
+static int scsi_disk_initfn(SCSIDevice *dev)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ int is_cd;
+ DriveInfo *dinfo;
+
+ if (!s->qdev.conf.bs) {
+ error_report("scsi-disk: drive property not set");
+ return -1;
+ }
+ s->bs = s->qdev.conf.bs;
+ is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM;
+
+ if (!is_cd && !bdrv_is_inserted(s->bs)) {
+ error_report("Device needs media, but drive is empty");
+ return -1;
+ }
+
+ if (!s->serial) {
+ /* try to fall back to value set with legacy -drive serial=... */
+ dinfo = drive_get_by_blockdev(s->bs);
+ s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0");
+ }
+
+ if (!s->version) {
+ s->version = qemu_strdup(QEMU_VERSION);
+ }
+
+ if (bdrv_is_sg(s->bs)) {
+ error_report("scsi-disk: unwanted /dev/sg*");
+ return -1;
+ }
+
+ if (is_cd) {
+ s->qdev.blocksize = 2048;
+ } else {
+ s->qdev.blocksize = s->qdev.conf.logical_block_size;
+ }
+ s->cluster_size = s->qdev.blocksize / 512;
+ s->bs->buffer_alignment = s->qdev.blocksize;
+
+ s->qdev.type = TYPE_DISK;
+ qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
+ bdrv_set_removable(s->bs, is_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 void scsi_disk_register_devices(void)
+{
+ scsi_qdev_register(&scsi_disk_info);
+}
+device_init(scsi_disk_register_devices)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
new file mode 100644
index 0000000..9be1cca
--- /dev/null
+++ b/hw/scsi-generic.c
@@ -0,0 +1,576 @@
+/*
+ * Generic SCSI Device support
+ *
+ * Copyright (c) 2007 Bull S.A.S.
+ * Based on code by Paul Brook
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ * This code is licenced under the LGPL.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "scsi.h"
+#include "blockdev.h"
+
+#ifdef __linux__
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, ...) \
+do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <scsi/sg.h>
+#include "scsi-defs.h"
+
+#define SCSI_SENSE_BUF_SIZE 96
+
+#define SG_ERR_DRIVER_TIMEOUT 0x06
+#define SG_ERR_DRIVER_SENSE 0x08
+
+#ifndef MAX_UINT
+#define MAX_UINT ((unsigned int)-1)
+#endif
+
+typedef struct SCSIGenericState SCSIGenericState;
+
+typedef struct SCSIGenericReq {
+ SCSIRequest req;
+ uint8_t *buf;
+ int buflen;
+ int len;
+ sg_io_hdr_t io_header;
+} SCSIGenericReq;
+
+struct SCSIGenericState
+{
+ SCSIDevice qdev;
+ BlockDriverState *bs;
+ int lun;
+ int driver_status;
+ uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
+ uint8_t senselen;
+};
+
+static SCSIGenericReq *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);
+}
+
+static void scsi_remove_request(SCSIGenericReq *r)
+{
+ qemu_free(r->buf);
+ scsi_req_free(&r->req);
+}
+
+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)
+{
+ SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
+
+ s->driver_status = r->io_header.driver_status;
+ 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 (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
+ r->req.status = BUSY;
+ BADF("Driver Timeout\n");
+ } else if (r->io_header.status)
+ r->req.status = r->io_header.status;
+ else if (s->driver_status & SG_ERR_DRIVER_SENSE)
+ r->req.status = CHECK_CONDITION;
+ else
+ r->req.status = GOOD;
+ }
+ DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
+ r, r->req.tag, r->req.status);
+
+ scsi_req_complete(&r->req);
+ scsi_remove_request(r);
+}
+
+/* Cancel a pending data transfer. */
+static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+{
+ 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_remove_request(r);
+ }
+}
+
+static int execute_command(BlockDriverState *bdrv,
+ SCSIGenericReq *r, int direction,
+ BlockDriverCompletionFunc *complete)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
+
+ r->io_header.interface_id = 'S';
+ r->io_header.dxfer_direction = direction;
+ r->io_header.dxferp = r->buf;
+ r->io_header.dxfer_len = r->buflen;
+ r->io_header.cmdp = r->req.cmd.buf;
+ r->io_header.cmd_len = r->req.cmd.len;
+ r->io_header.mx_sb_len = sizeof(s->sensebuf);
+ r->io_header.sbp = s->sensebuf;
+ r->io_header.timeout = MAX_UINT;
+ r->io_header.usr_ptr = r;
+ r->io_header.flags |= SG_FLAG_DIRECT_IO;
+
+ 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 0;
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+ SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+ int len;
+
+ if (ret) {
+ DPRINTF("IO error ret %d\n", ret);
+ scsi_command_complete(r, ret);
+ return;
+ }
+ len = r->io_header.dxfer_len - r->io_header.resid;
+ 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)
+ scsi_command_complete(r, 0);
+}
+
+/* Read more data from scsi device into buffer. */
+static void scsi_read_data(SCSIDevice *d, uint32_t tag)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
+ SCSIGenericReq *r;
+ 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;
+ }
+
+ if (r->len == -1) {
+ scsi_command_complete(r, 0);
+ return;
+ }
+
+ if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE)
+ {
+ s->senselen = MIN(r->len, s->senselen);
+ memcpy(r->buf, s->sensebuf, s->senselen);
+ r->io_header.driver_status = 0;
+ r->io_header.status = 0;
+ r->io_header.dxfer_len = s->senselen;
+ r->len = -1;
+ DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen);
+ 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);
+ return;
+ }
+
+ ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
+ if (ret == -1) {
+ scsi_command_complete(r, -EINVAL);
+ return;
+ }
+}
+
+static void scsi_write_complete(void * opaque, int ret)
+{
+ SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
+
+ DPRINTF("scsi_write_complete() ret = %d\n", ret);
+ if (ret) {
+ DPRINTF("IO error\n");
+ scsi_command_complete(r, ret);
+ return;
+ }
+
+ if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
+ s->qdev.type == TYPE_TAPE) {
+ s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
+ DPRINTF("block size %d\n", s->qdev.blocksize);
+ }
+
+ scsi_command_complete(r, 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)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
+ SCSIGenericReq *r;
+ 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;
+ }
+
+ if (r->len == 0) {
+ r->len = r->buflen;
+ r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len);
+ return 0;
+ }
+
+ ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
+ if (ret == -1) {
+ scsi_command_complete(r, -EINVAL);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return a pointer to the data buffer. */
+static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+{
+ 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;
+ }
+ return r->buf;
+}
+
+static void scsi_req_fixup(SCSIRequest *req)
+{
+ switch(req->cmd.buf[0]) {
+ case WRITE_10:
+ req->cmd.buf[1] &= ~0x08; /* disable FUA */
+ break;
+ case READ_10:
+ req->cmd.buf[1] &= ~0x08; /* disable FUA */
+ break;
+ case REWIND:
+ case START_STOP:
+ if (req->dev->type == TYPE_TAPE) {
+ /* force IMMED, otherwise qemu waits end of command */
+ req->cmd.buf[1] = 0x01;
+ }
+ break;
+ }
+}
+
+/* Execute a scsi command. Returns the length of the data expected by the
+ command. This will be Positive for data transfers from the device
+ (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)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
+ SCSIGenericReq *r;
+ SCSIBus *bus;
+ int ret;
+
+ if (cmd[0] != REQUEST_SENSE &&
+ (lun != s->lun || (cmd[1] >> 5) != s->lun)) {
+ DPRINTF("Unimplemented LUN %d\n", lun ? 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;
+ bus = scsi_bus_from_device(d);
+ bus->complete(bus, SCSI_REASON_DONE, tag, 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_remove_request(r);
+ return 0;
+ }
+ scsi_req_fixup(&r->req);
+
+ DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
+ r->req.cmd.xfer, cmd[0]);
+
+#ifdef DEBUG_SCSI
+ {
+ int i;
+ for (i = 1; i < r->req.cmd.len; i++) {
+ printf(" 0x%02x", cmd[i]);
+ }
+ printf("\n");
+ }
+#endif
+
+ if (r->req.cmd.xfer == 0) {
+ if (r->buf != NULL)
+ qemu_free(r->buf);
+ 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);
+ return 0;
+ }
+ return 0;
+ }
+
+ if (r->buflen != r->req.cmd.xfer) {
+ if (r->buf != NULL)
+ qemu_free(r->buf);
+ r->buf = qemu_malloc(r->req.cmd.xfer);
+ r->buflen = r->req.cmd.xfer;
+ }
+
+ memset(r->buf, 0, r->buflen);
+ r->len = r->req.cmd.xfer;
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ r->len = 0;
+ return -r->req.cmd.xfer;
+ }
+
+ return r->req.cmd.xfer;
+}
+
+static int get_blocksize(BlockDriverState *bdrv)
+{
+ uint8_t cmd[10];
+ uint8_t buf[8];
+ uint8_t sensebuf[8];
+ sg_io_hdr_t io_header;
+ int ret;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = READ_CAPACITY;
+
+ memset(&io_header, 0, sizeof(io_header));
+ io_header.interface_id = 'S';
+ io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_header.dxfer_len = sizeof(buf);
+ io_header.dxferp = buf;
+ io_header.cmdp = cmd;
+ io_header.cmd_len = sizeof(cmd);
+ io_header.mx_sb_len = sizeof(sensebuf);
+ io_header.sbp = sensebuf;
+ io_header.timeout = 6000; /* XXX */
+
+ ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+ if (ret < 0)
+ return -1;
+
+ return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+}
+
+static int get_stream_blocksize(BlockDriverState *bdrv)
+{
+ uint8_t cmd[6];
+ uint8_t buf[12];
+ uint8_t sensebuf[8];
+ sg_io_hdr_t io_header;
+ int ret;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = MODE_SENSE;
+ cmd[4] = sizeof(buf);
+
+ memset(&io_header, 0, sizeof(io_header));
+ io_header.interface_id = 'S';
+ io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_header.dxfer_len = sizeof(buf);
+ io_header.dxferp = buf;
+ io_header.cmdp = cmd;
+ io_header.cmd_len = sizeof(cmd);
+ io_header.mx_sb_len = sizeof(sensebuf);
+ io_header.sbp = sensebuf;
+ io_header.timeout = 6000; /* XXX */
+
+ ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+ if (ret < 0)
+ return -1;
+
+ 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_remove_request(r);
+ }
+}
+
+static void scsi_generic_reset(DeviceState *dev)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
+
+ scsi_generic_purge_requests(s);
+}
+
+static void scsi_destroy(SCSIDevice *d)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
+
+ scsi_generic_purge_requests(s);
+ blockdev_mark_auto_del(s->qdev.conf.bs);
+}
+
+static int scsi_generic_initfn(SCSIDevice *dev)
+{
+ SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
+ int sg_version;
+ struct sg_scsi_id scsiid;
+
+ if (!s->qdev.conf.bs) {
+ error_report("scsi-generic: drive property not set");
+ return -1;
+ }
+ s->bs = s->qdev.conf.bs;
+
+ /* check we are really using a /dev/sg* file */
+ if (!bdrv_is_sg(s->bs)) {
+ error_report("scsi-generic: not /dev/sg*");
+ return -1;
+ }
+
+ if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
+ error_report("Device doesn't support drive option werror");
+ return -1;
+ }
+ if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) {
+ error_report("Device doesn't support drive option rerror");
+ return -1;
+ }
+
+ /* check we are using a driver managing SG_IO (version 3 and after */
+ if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
+ sg_version < 30000) {
+ error_report("scsi-generic: scsi generic interface too old");
+ return -1;
+ }
+
+ /* get LUN of the /dev/sg? */
+ if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) {
+ error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed");
+ return -1;
+ }
+
+ /* define device state */
+ s->lun = scsiid.lun;
+ DPRINTF("LUN %d\n", s->lun);
+ s->qdev.type = scsiid.scsi_type;
+ DPRINTF("device type %d\n", s->qdev.type);
+ if (s->qdev.type == TYPE_TAPE) {
+ s->qdev.blocksize = get_stream_blocksize(s->bs);
+ if (s->qdev.blocksize == -1)
+ s->qdev.blocksize = 0;
+ } else {
+ s->qdev.blocksize = get_blocksize(s->bs);
+ /* removable media returns 0 if not present */
+ if (s->qdev.blocksize <= 0) {
+ if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM)
+ s->qdev.blocksize = 2048;
+ else
+ s->qdev.blocksize = 512;
+ }
+ }
+ DPRINTF("block size %d\n", s->qdev.blocksize);
+ s->driver_status = 0;
+ memset(s->sensebuf, 0, sizeof(s->sensebuf));
+ bdrv_set_removable(s->bs, 0);
+ return 0;
+}
+
+static SCSIDeviceInfo scsi_generic_info = {
+ .qdev.name = "scsi-generic",
+ .qdev.desc = "pass through generic scsi device (/dev/sg*)",
+ .qdev.size = sizeof(SCSIGenericState),
+ .qdev.reset = scsi_generic_reset,
+ .init = scsi_generic_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(SCSIGenericState, qdev.conf),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void scsi_generic_register_devices(void)
+{
+ scsi_qdev_register(&scsi_generic_info);
+}
+device_init(scsi_generic_register_devices)
+
+#endif /* __linux__ */
diff --git a/hw/scsi.h b/hw/scsi.h
new file mode 100644
index 0000000..d3b5d56
--- /dev/null
+++ b/hw/scsi.h
@@ -0,0 +1,110 @@
+#ifndef QEMU_HW_SCSI_H
+#define QEMU_HW_SCSI_H
+
+#include "qdev.h"
+#include "block.h"
+#include "block_int.h"
+
+#define MAX_SCSI_DEVS 255
+
+#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 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, ... */
+ SCSI_XFER_FROM_DEV, /* READ, INQUIRY, MODE_SENSE, ... */
+ SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
+};
+
+typedef struct SCSIRequest {
+ SCSIBus *bus;
+ SCSIDevice *dev;
+ uint32_t tag;
+ uint32_t lun;
+ uint32_t status;
+ struct {
+ uint8_t buf[SCSI_CMD_BUF_SIZE];
+ int len;
+ size_t xfer;
+ uint64_t lba;
+ enum SCSIXferMode mode;
+ } cmd;
+ BlockDriverAIOCB *aiocb;
+ bool enqueued;
+ QTAILQ_ENTRY(SCSIRequest) next;
+} SCSIRequest;
+
+struct SCSIDevice
+{
+ DeviceState qdev;
+ uint32_t id;
+ BlockConf conf;
+ SCSIDeviceInfo *info;
+ QTAILQ_HEAD(, SCSIRequest) requests;
+ int blocksize;
+ int type;
+};
+
+/* cdrom.c */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+
+/* scsi-bus.c */
+typedef int (*scsi_qdev_initfn)(SCSIDevice *dev);
+struct SCSIDeviceInfo {
+ DeviceInfo qdev;
+ scsi_qdev_initfn init;
+ void (*destroy)(SCSIDevice *s);
+ 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);
+};
+
+typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
+ int unit);
+struct SCSIBus {
+ BusState qbus;
+ int busnr;
+
+ int tcq, ndev;
+ scsi_completionfn complete;
+
+ SCSIDevice *devs[MAX_SCSI_DEVS];
+};
+
+void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
+ scsi_completionfn complete);
+void scsi_qdev_register(SCSIDeviceInfo *info);
+
+static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
+{
+ return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
+}
+
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+ int unit, bool removable);
+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);
+
+int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
+void scsi_req_print(SCSIRequest *req);
+void scsi_req_complete(SCSIRequest *req);
+
+#endif
diff --git a/hw/sd.c b/hw/sd.c
new file mode 100644
index 0000000..5e29752
--- /dev/null
+++ b/hw/sd.c
@@ -0,0 +1,1660 @@
+/*
+ * SD Memory Card emulation as defined in the "SD Memory Card Physical
+ * layer specification, Version 1.10."
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (c) 2007 CodeSourcery
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "hw.h"
+#include "block.h"
+#include "block_int.h"
+#include "sd.h"
+
+//#define DEBUG_SD 1
+
+#ifdef DEBUG_SD
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+typedef enum {
+ sd_r0 = 0, /* no response */
+ sd_r1, /* normal response command */
+ sd_r2_i, /* CID register */
+ sd_r2_s, /* CSD register */
+ sd_r3, /* OCR register */
+ sd_r6 = 6, /* Published RCA response */
+ sd_r7, /* Operating voltage */
+ sd_r1b = -1,
+} sd_rsp_type_t;
+
+struct SDState {
+ enum {
+ sd_inactive,
+ sd_card_identification_mode,
+ sd_data_transfer_mode,
+ } mode;
+ enum {
+ sd_inactive_state = -1,
+ sd_idle_state = 0,
+ sd_ready_state,
+ sd_identification_state,
+ sd_standby_state,
+ sd_transfer_state,
+ sd_sendingdata_state,
+ sd_receivingdata_state,
+ sd_programming_state,
+ sd_disconnect_state,
+ } state;
+ uint32_t ocr;
+ uint8_t scr[8];
+ uint8_t cid[16];
+ uint8_t csd[16];
+ uint16_t rca;
+ uint32_t card_status;
+ uint8_t sd_status[64];
+ uint32_t vhs;
+ int wp_switch;
+ int *wp_groups;
+ uint64_t size;
+ int blk_len;
+ uint32_t erase_start;
+ uint32_t erase_end;
+ uint8_t pwd[16];
+ int pwd_len;
+ int function_group[6];
+
+ int spi;
+ int current_cmd;
+ int blk_written;
+ uint64_t data_start;
+ uint32_t data_offset;
+ uint8_t data[512];
+ qemu_irq readonly_cb;
+ qemu_irq inserted_cb;
+ BlockDriverState *bdrv;
+ uint8_t *buf;
+
+ int enable;
+};
+
+static void sd_set_status(SDState *sd)
+{
+ switch (sd->state) {
+ case sd_inactive_state:
+ sd->mode = sd_inactive;
+ break;
+
+ case sd_idle_state:
+ case sd_ready_state:
+ case sd_identification_state:
+ sd->mode = sd_card_identification_mode;
+ break;
+
+ case sd_standby_state:
+ case sd_transfer_state:
+ case sd_sendingdata_state:
+ case sd_receivingdata_state:
+ case sd_programming_state:
+ case sd_disconnect_state:
+ sd->mode = sd_data_transfer_mode;
+ break;
+ }
+
+ sd->card_status &= ~CURRENT_STATE;
+ sd->card_status |= sd->state << 9;
+}
+
+static const sd_cmd_type_t sd_cmd_type[64] = {
+ sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac,
+ sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac,
+ sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none,
+ sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none,
+ sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
+ sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+static const sd_cmd_type_t sd_acmd_type[64] = {
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_ac,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_none, sd_bcr, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_none,
+ sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none, sd_none, sd_none,
+ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+static const int sd_cmd_class[64] = {
+ 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6,
+ 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7,
+ 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8,
+};
+
+static uint8_t sd_crc7(void *message, size_t width)
+{
+ int i, bit;
+ uint8_t shift_reg = 0x00;
+ uint8_t *msg = (uint8_t *) message;
+
+ for (i = 0; i < width; i ++, msg ++)
+ for (bit = 7; bit >= 0; bit --) {
+ shift_reg <<= 1;
+ if ((shift_reg >> 7) ^ ((*msg >> bit) & 1))
+ shift_reg ^= 0x89;
+ }
+
+ return shift_reg;
+}
+
+static uint16_t sd_crc16(void *message, size_t width)
+{
+ int i, bit;
+ uint16_t shift_reg = 0x0000;
+ uint16_t *msg = (uint16_t *) message;
+ width <<= 1;
+
+ for (i = 0; i < width; i ++, msg ++)
+ for (bit = 15; bit >= 0; bit --) {
+ shift_reg <<= 1;
+ if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
+ shift_reg ^= 0x1011;
+ }
+
+ return shift_reg;
+}
+
+static void sd_set_ocr(SDState *sd)
+{
+ /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
+ sd->ocr = 0x80ffff00;
+}
+
+static void sd_set_scr(SDState *sd)
+{
+ sd->scr[0] = 0x00; /* SCR Structure */
+ sd->scr[1] = 0x2f; /* SD Security Support */
+ sd->scr[2] = 0x00;
+ sd->scr[3] = 0x00;
+ sd->scr[4] = 0x00;
+ sd->scr[5] = 0x00;
+ sd->scr[6] = 0x00;
+ sd->scr[7] = 0x00;
+}
+
+#define MID 0xaa
+#define OID "XY"
+#define PNM "QEMU!"
+#define PRV 0x01
+#define MDT_YR 2006
+#define MDT_MON 2
+
+static void sd_set_cid(SDState *sd)
+{
+ sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */
+ sd->cid[1] = OID[0]; /* OEM/Application ID (OID) */
+ sd->cid[2] = OID[1];
+ sd->cid[3] = PNM[0]; /* Fake product name (PNM) */
+ sd->cid[4] = PNM[1];
+ sd->cid[5] = PNM[2];
+ sd->cid[6] = PNM[3];
+ sd->cid[7] = PNM[4];
+ sd->cid[8] = PRV; /* Fake product revision (PRV) */
+ sd->cid[9] = 0xde; /* Fake serial number (PSN) */
+ sd->cid[10] = 0xad;
+ sd->cid[11] = 0xbe;
+ sd->cid[12] = 0xef;
+ sd->cid[13] = 0x00 | /* Manufacture date (MDT) */
+ ((MDT_YR - 2000) / 10);
+ sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON;
+ sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
+}
+
+#define HWBLOCK_SHIFT 9 /* 512 bytes */
+#define SECTOR_SHIFT 5 /* 16 kilobytes */
+#define WPGROUP_SHIFT 7 /* 2 megs */
+#define CMULT_SHIFT 9 /* 512 times HWBLOCK_SIZE */
+#define WPGROUP_SIZE (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))
+
+static const uint8_t sd_csd_rw_mask[16] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe,
+};
+
+static void sd_set_csd(SDState *sd, uint64_t size)
+{
+ uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1;
+ uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
+ uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
+
+ if (size <= 0x40000000) { /* Standard Capacity SD */
+ sd->csd[0] = 0x00; /* CSD structure */
+ sd->csd[1] = 0x26; /* Data read access-time-1 */
+ sd->csd[2] = 0x00; /* Data read access-time-2 */
+ sd->csd[3] = 0x5a; /* Max. data transfer rate */
+ sd->csd[4] = 0x5f; /* Card Command Classes */
+ sd->csd[5] = 0x50 | /* Max. read data block length */
+ HWBLOCK_SHIFT;
+ sd->csd[6] = 0xe0 | /* Partial block for read allowed */
+ ((csize >> 10) & 0x03);
+ sd->csd[7] = 0x00 | /* Device size */
+ ((csize >> 2) & 0xff);
+ sd->csd[8] = 0x3f | /* Max. read current */
+ ((csize << 6) & 0xc0);
+ sd->csd[9] = 0xfc | /* Max. write current */
+ ((CMULT_SHIFT - 2) >> 1);
+ sd->csd[10] = 0x40 | /* Erase sector size */
+ (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1);
+ sd->csd[11] = 0x00 | /* Write protect group size */
+ ((sectsize << 7) & 0x80) | wpsize;
+ sd->csd[12] = 0x90 | /* Write speed factor */
+ (HWBLOCK_SHIFT >> 2);
+ sd->csd[13] = 0x20 | /* Max. write data block length */
+ ((HWBLOCK_SHIFT << 6) & 0xc0);
+ sd->csd[14] = 0x00; /* File format group */
+ sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
+ } else { /* SDHC */
+ size /= 512 * 1024;
+ size -= 1;
+ sd->csd[0] = 0x40;
+ sd->csd[1] = 0x0e;
+ sd->csd[2] = 0x00;
+ sd->csd[3] = 0x32;
+ sd->csd[4] = 0x5b;
+ sd->csd[5] = 0x59;
+ sd->csd[6] = 0x00;
+ sd->csd[7] = (size >> 16) & 0xff;
+ sd->csd[8] = (size >> 8) & 0xff;
+ sd->csd[9] = (size & 0xff);
+ sd->csd[10] = 0x7f;
+ sd->csd[11] = 0x80;
+ sd->csd[12] = 0x0a;
+ sd->csd[13] = 0x40;
+ sd->csd[14] = 0x00;
+ sd->csd[15] = 0x00;
+ sd->ocr |= 1 << 30; /* High Capacity SD Memort Card */
+ }
+}
+
+static void sd_set_rca(SDState *sd)
+{
+ sd->rca += 0x4567;
+}
+
+#define CARD_STATUS_A 0x02004100
+#define CARD_STATUS_B 0x00c01e00
+#define CARD_STATUS_C 0xfd39a028
+
+static void sd_set_cardstatus(SDState *sd)
+{
+ sd->card_status = 0x00000100;
+}
+
+static void sd_set_sdstatus(SDState *sd)
+{
+ memset(sd->sd_status, 0, 64);
+}
+
+static int sd_req_crc_validate(SDRequest *req)
+{
+ uint8_t buffer[5];
+ buffer[0] = 0x40 | req->cmd;
+ buffer[1] = (req->arg >> 24) & 0xff;
+ buffer[2] = (req->arg >> 16) & 0xff;
+ buffer[3] = (req->arg >> 8) & 0xff;
+ buffer[4] = (req->arg >> 0) & 0xff;
+ return 0;
+ return sd_crc7(buffer, 5) != req->crc; /* TODO */
+}
+
+static void sd_response_r1_make(SDState *sd,
+ uint8_t *response, uint32_t last_status)
+{
+ uint32_t mask = CARD_STATUS_B ^ ILLEGAL_COMMAND;
+ uint32_t status;
+
+ status = (sd->card_status & ~mask) | (last_status & mask);
+ sd->card_status &= ~CARD_STATUS_C | APP_CMD;
+
+ response[0] = (status >> 24) & 0xff;
+ response[1] = (status >> 16) & 0xff;
+ response[2] = (status >> 8) & 0xff;
+ response[3] = (status >> 0) & 0xff;
+}
+
+static void sd_response_r3_make(SDState *sd, uint8_t *response)
+{
+ response[0] = (sd->ocr >> 24) & 0xff;
+ response[1] = (sd->ocr >> 16) & 0xff;
+ response[2] = (sd->ocr >> 8) & 0xff;
+ response[3] = (sd->ocr >> 0) & 0xff;
+}
+
+static void sd_response_r6_make(SDState *sd, uint8_t *response)
+{
+ uint16_t arg;
+ uint16_t status;
+
+ arg = sd->rca;
+ status = ((sd->card_status >> 8) & 0xc000) |
+ ((sd->card_status >> 6) & 0x2000) |
+ (sd->card_status & 0x1fff);
+
+ response[0] = (arg >> 8) & 0xff;
+ response[1] = arg & 0xff;
+ response[2] = (status >> 8) & 0xff;
+ response[3] = status & 0xff;
+}
+
+static void sd_response_r7_make(SDState *sd, uint8_t *response)
+{
+ response[0] = (sd->vhs >> 24) & 0xff;
+ response[1] = (sd->vhs >> 16) & 0xff;
+ response[2] = (sd->vhs >> 8) & 0xff;
+ response[3] = (sd->vhs >> 0) & 0xff;
+}
+
+static void sd_reset(SDState *sd, BlockDriverState *bdrv)
+{
+ uint64_t size;
+ uint64_t sect;
+
+ if (bdrv) {
+ bdrv_get_geometry(bdrv, &sect);
+ } else {
+ sect = 0;
+ }
+ sect <<= 9;
+
+ size = sect + 1;
+
+ sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
+
+ sd->state = sd_idle_state;
+ sd->rca = 0x0000;
+ sd_set_ocr(sd);
+ sd_set_scr(sd);
+ sd_set_cid(sd);
+ sd_set_csd(sd, size);
+ sd_set_cardstatus(sd);
+ sd_set_sdstatus(sd);
+
+ sd->bdrv = bdrv;
+
+ if (sd->wp_groups)
+ qemu_free(sd->wp_groups);
+ sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : 0;
+ sd->wp_groups = (int *) qemu_mallocz(sizeof(int) * sect);
+ memset(sd->function_group, 0, sizeof(int) * 6);
+ sd->erase_start = 0;
+ sd->erase_end = 0;
+ sd->size = size;
+ sd->blk_len = 0x200;
+ sd->pwd_len = 0;
+}
+
+static void sd_cardchange(void *opaque, int reason)
+{
+ SDState *sd = opaque;
+
+ if (!(reason & CHANGE_MEDIA)) {
+ return;
+ }
+
+ qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv));
+ if (bdrv_is_inserted(sd->bdrv)) {
+ sd_reset(sd, sd->bdrv);
+ qemu_set_irq(sd->readonly_cb, sd->wp_switch);
+ }
+}
+
+/* We do not model the chip select pin, so allow the board to select
+ whether card should be in SSI or MMC/SD mode. It is also up to the
+ board to ensure that ssi transfers only occur when the chip select
+ is asserted. */
+SDState *sd_init(BlockDriverState *bs, int is_spi)
+{
+ SDState *sd;
+
+ sd = (SDState *) qemu_mallocz(sizeof(SDState));
+ sd->buf = qemu_blockalign(bs, 512);
+ sd->spi = is_spi;
+ sd->enable = 1;
+ sd_reset(sd, bs);
+ if (sd->bdrv) {
+ bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd);
+ }
+ return sd;
+}
+
+void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
+{
+ sd->readonly_cb = readonly;
+ sd->inserted_cb = insert;
+ qemu_set_irq(readonly, bdrv_is_read_only(sd->bdrv));
+ qemu_set_irq(insert, bdrv_is_inserted(sd->bdrv));
+}
+
+static void sd_erase(SDState *sd)
+{
+ int i, start, end;
+ if (!sd->erase_start || !sd->erase_end) {
+ sd->card_status |= ERASE_SEQ_ERROR;
+ return;
+ }
+
+ start = sd->erase_start >>
+ (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+ end = sd->erase_end >>
+ (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+ sd->erase_start = 0;
+ sd->erase_end = 0;
+ sd->csd[14] |= 0x40;
+
+ for (i = start; i <= end; i ++)
+ if (sd->wp_groups[i])
+ sd->card_status |= WP_ERASE_SKIP;
+}
+
+static uint32_t sd_wpbits(SDState *sd, uint64_t addr)
+{
+ uint32_t i, wpnum;
+ uint32_t ret = 0;
+
+ wpnum = addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+
+ for (i = 0; i < 32; i ++, wpnum ++, addr += WPGROUP_SIZE)
+ if (addr < sd->size && sd->wp_groups[wpnum])
+ ret |= (1 << i);
+
+ return ret;
+}
+
+static void sd_function_switch(SDState *sd, uint32_t arg)
+{
+ int i, mode, new_func, crc;
+ mode = !!(arg & 0x80000000);
+
+ sd->data[0] = 0x00; /* Maximum current consumption */
+ sd->data[1] = 0x01;
+ sd->data[2] = 0x80; /* Supported group 6 functions */
+ sd->data[3] = 0x01;
+ sd->data[4] = 0x80; /* Supported group 5 functions */
+ sd->data[5] = 0x01;
+ sd->data[6] = 0x80; /* Supported group 4 functions */
+ sd->data[7] = 0x01;
+ sd->data[8] = 0x80; /* Supported group 3 functions */
+ sd->data[9] = 0x01;
+ sd->data[10] = 0x80; /* Supported group 2 functions */
+ sd->data[11] = 0x43;
+ sd->data[12] = 0x80; /* Supported group 1 functions */
+ sd->data[13] = 0x03;
+ for (i = 0; i < 6; i ++) {
+ new_func = (arg >> (i * 4)) & 0x0f;
+ if (mode && new_func != 0x0f)
+ sd->function_group[i] = new_func;
+ sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4);
+ }
+ memset(&sd->data[17], 0, 47);
+ crc = sd_crc16(sd->data, 64);
+ sd->data[65] = crc >> 8;
+ sd->data[66] = crc & 0xff;
+}
+
+static inline int sd_wp_addr(SDState *sd, uint32_t addr)
+{
+ return sd->wp_groups[addr >>
+ (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)];
+}
+
+static void sd_lock_command(SDState *sd)
+{
+ int erase, lock, clr_pwd, set_pwd, pwd_len;
+ erase = !!(sd->data[0] & 0x08);
+ lock = sd->data[0] & 0x04;
+ clr_pwd = sd->data[0] & 0x02;
+ set_pwd = sd->data[0] & 0x01;
+
+ if (sd->blk_len > 1)
+ pwd_len = sd->data[1];
+ else
+ pwd_len = 0;
+
+ if (erase) {
+ if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 ||
+ set_pwd || clr_pwd || lock || sd->wp_switch ||
+ (sd->csd[14] & 0x20)) {
+ sd->card_status |= LOCK_UNLOCK_FAILED;
+ return;
+ }
+ memset(sd->wp_groups, 0, sizeof(int) * (sd->size >>
+ (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)));
+ sd->csd[14] &= ~0x10;
+ sd->card_status &= ~CARD_IS_LOCKED;
+ sd->pwd_len = 0;
+ /* Erasing the entire card here! */
+ fprintf(stderr, "SD: Card force-erased by CMD42\n");
+ return;
+ }
+
+ if (sd->blk_len < 2 + pwd_len ||
+ pwd_len <= sd->pwd_len ||
+ pwd_len > sd->pwd_len + 16) {
+ sd->card_status |= LOCK_UNLOCK_FAILED;
+ return;
+ }
+
+ if (sd->pwd_len && memcmp(sd->pwd, sd->data + 2, sd->pwd_len)) {
+ sd->card_status |= LOCK_UNLOCK_FAILED;
+ return;
+ }
+
+ pwd_len -= sd->pwd_len;
+ if ((pwd_len && !set_pwd) ||
+ (clr_pwd && (set_pwd || lock)) ||
+ (lock && !sd->pwd_len && !set_pwd) ||
+ (!set_pwd && !clr_pwd &&
+ (((sd->card_status & CARD_IS_LOCKED) && lock) ||
+ (!(sd->card_status & CARD_IS_LOCKED) && !lock)))) {
+ sd->card_status |= LOCK_UNLOCK_FAILED;
+ return;
+ }
+
+ if (set_pwd) {
+ memcpy(sd->pwd, sd->data + 2 + sd->pwd_len, pwd_len);
+ sd->pwd_len = pwd_len;
+ }
+
+ if (clr_pwd) {
+ sd->pwd_len = 0;
+ }
+
+ if (lock)
+ sd->card_status |= CARD_IS_LOCKED;
+ else
+ sd->card_status &= ~CARD_IS_LOCKED;
+}
+
+static sd_rsp_type_t sd_normal_command(SDState *sd,
+ SDRequest req)
+{
+ uint32_t rca = 0x0000;
+ uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
+
+ if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
+ rca = req.arg >> 16;
+
+ DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state);
+ switch (req.cmd) {
+ /* Basic commands (Class 0 and Class 1) */
+ case 0: /* CMD0: GO_IDLE_STATE */
+ switch (sd->state) {
+ case sd_inactive_state:
+ return sd->spi ? sd_r1 : sd_r0;
+
+ default:
+ sd->state = sd_idle_state;
+ sd_reset(sd, sd->bdrv);
+ return sd->spi ? sd_r1 : sd_r0;
+ }
+ break;
+
+ case 1: /* CMD1: SEND_OP_CMD */
+ if (!sd->spi)
+ goto bad_cmd;
+
+ sd->state = sd_transfer_state;
+ return sd_r1;
+
+ case 2: /* CMD2: ALL_SEND_CID */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_ready_state:
+ sd->state = sd_identification_state;
+ return sd_r2_i;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: /* CMD3: SEND_RELATIVE_ADDR */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_identification_state:
+ case sd_standby_state:
+ sd->state = sd_standby_state;
+ sd_set_rca(sd);
+ return sd_r6;
+
+ default:
+ break;
+ }
+ break;
+
+ case 4: /* CMD4: SEND_DSR */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_standby_state:
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 5: /* CMD5: reserved for SDIO cards */
+ sd->card_status |= ILLEGAL_COMMAND;
+ return sd_r0;
+
+ case 6: /* CMD6: SWITCH_FUNCTION */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->mode) {
+ case sd_data_transfer_mode:
+ sd_function_switch(sd, req.arg);
+ sd->state = sd_sendingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 7: /* CMD7: SELECT/DESELECT_CARD */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_standby_state:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ case sd_transfer_state:
+ case sd_sendingdata_state:
+ if (sd->rca == rca)
+ break;
+
+ sd->state = sd_standby_state;
+ return sd_r1b;
+
+ case sd_disconnect_state:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ sd->state = sd_programming_state;
+ return sd_r1b;
+
+ case sd_programming_state:
+ if (sd->rca == rca)
+ break;
+
+ sd->state = sd_disconnect_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ case 8: /* CMD8: SEND_IF_COND */
+ /* Physical Layer Specification Version 2.00 command */
+ switch (sd->state) {
+ case sd_idle_state:
+ sd->vhs = 0;
+
+ /* No response if not exactly one VHS bit is set. */
+ if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
+ return sd->spi ? sd_r7 : sd_r0;
+
+ /* Accept. */
+ sd->vhs = req.arg;
+ return sd_r7;
+
+ default:
+ break;
+ }
+ break;
+
+ case 9: /* CMD9: SEND_CSD */
+ switch (sd->state) {
+ case sd_standby_state:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ return sd_r2_s;
+
+ case sd_transfer_state:
+ if (!sd->spi)
+ break;
+ sd->state = sd_sendingdata_state;
+ memcpy(sd->data, sd->csd, 16);
+ sd->data_start = addr;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 10: /* CMD10: SEND_CID */
+ switch (sd->state) {
+ case sd_standby_state:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ return sd_r2_i;
+
+ case sd_transfer_state:
+ if (!sd->spi)
+ break;
+ sd->state = sd_sendingdata_state;
+ memcpy(sd->data, sd->cid, 16);
+ sd->data_start = addr;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 11: /* CMD11: READ_DAT_UNTIL_STOP */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_sendingdata_state;
+ sd->data_start = req.arg;
+ sd->data_offset = 0;
+
+ if (sd->data_start + sd->blk_len > sd->size)
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r0;
+
+ default:
+ break;
+ }
+ break;
+
+ case 12: /* CMD12: STOP_TRANSMISSION */
+ switch (sd->state) {
+ case sd_sendingdata_state:
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ case sd_receivingdata_state:
+ sd->state = sd_programming_state;
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ case 13: /* CMD13: SEND_STATUS */
+ switch (sd->mode) {
+ case sd_data_transfer_mode:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 15: /* CMD15: GO_INACTIVE_STATE */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->mode) {
+ case sd_data_transfer_mode:
+ if (sd->rca != rca)
+ return sd_r0;
+
+ sd->state = sd_inactive_state;
+ return sd_r0;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Block read commands (Classs 2) */
+ case 16: /* CMD16: SET_BLOCKLEN */
+ switch (sd->state) {
+ case sd_transfer_state:
+ if (req.arg > (1 << HWBLOCK_SHIFT))
+ sd->card_status |= BLOCK_LEN_ERROR;
+ else
+ sd->blk_len = req.arg;
+
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 17: /* CMD17: READ_SINGLE_BLOCK */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_sendingdata_state;
+ sd->data_start = addr;
+ sd->data_offset = 0;
+
+ if (sd->data_start + sd->blk_len > sd->size)
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 18: /* CMD18: READ_MULTIPLE_BLOCK */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_sendingdata_state;
+ sd->data_start = addr;
+ sd->data_offset = 0;
+
+ if (sd->data_start + sd->blk_len > sd->size)
+ sd->card_status |= ADDRESS_ERROR;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Block write commands (Class 4) */
+ case 24: /* CMD24: WRITE_SINGLE_BLOCK */
+ if (sd->spi)
+ goto unimplemented_cmd;
+ switch (sd->state) {
+ case sd_transfer_state:
+ /* Writing in SPI mode not implemented. */
+ if (sd->spi)
+ break;
+ sd->state = sd_receivingdata_state;
+ sd->data_start = addr;
+ sd->data_offset = 0;
+ sd->blk_written = 0;
+
+ if (sd->data_start + sd->blk_len > sd->size)
+ sd->card_status |= ADDRESS_ERROR;
+ if (sd_wp_addr(sd, sd->data_start))
+ sd->card_status |= WP_VIOLATION;
+ if (sd->csd[14] & 0x30)
+ sd->card_status |= WP_VIOLATION;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
+ if (sd->spi)
+ goto unimplemented_cmd;
+ switch (sd->state) {
+ case sd_transfer_state:
+ /* Writing in SPI mode not implemented. */
+ if (sd->spi)
+ break;
+ sd->state = sd_receivingdata_state;
+ sd->data_start = addr;
+ sd->data_offset = 0;
+ sd->blk_written = 0;
+
+ if (sd->data_start + sd->blk_len > sd->size)
+ sd->card_status |= ADDRESS_ERROR;
+ if (sd_wp_addr(sd, sd->data_start))
+ sd->card_status |= WP_VIOLATION;
+ if (sd->csd[14] & 0x30)
+ sd->card_status |= WP_VIOLATION;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 26: /* CMD26: PROGRAM_CID */
+ if (sd->spi)
+ goto bad_cmd;
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_receivingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 27: /* CMD27: PROGRAM_CSD */
+ if (sd->spi)
+ goto unimplemented_cmd;
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_receivingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Write protection (Class 6) */
+ case 28: /* CMD28: SET_WRITE_PROT */
+ switch (sd->state) {
+ case sd_transfer_state:
+ if (addr >= sd->size) {
+ sd->card_status = ADDRESS_ERROR;
+ return sd_r1b;
+ }
+
+ sd->state = sd_programming_state;
+ sd->wp_groups[addr >> (HWBLOCK_SHIFT +
+ SECTOR_SHIFT + WPGROUP_SHIFT)] = 1;
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ case 29: /* CMD29: CLR_WRITE_PROT */
+ switch (sd->state) {
+ case sd_transfer_state:
+ if (addr >= sd->size) {
+ sd->card_status = ADDRESS_ERROR;
+ return sd_r1b;
+ }
+
+ sd->state = sd_programming_state;
+ sd->wp_groups[addr >> (HWBLOCK_SHIFT +
+ SECTOR_SHIFT + WPGROUP_SHIFT)] = 0;
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ case 30: /* CMD30: SEND_WRITE_PROT */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_sendingdata_state;
+ *(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
+ sd->data_start = addr;
+ sd->data_offset = 0;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Erase commands (Class 5) */
+ case 32: /* CMD32: ERASE_WR_BLK_START */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->erase_start = req.arg;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 33: /* CMD33: ERASE_WR_BLK_END */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->erase_end = req.arg;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 38: /* CMD38: ERASE */
+ switch (sd->state) {
+ case sd_transfer_state:
+ if (sd->csd[14] & 0x30) {
+ sd->card_status |= WP_VIOLATION;
+ return sd_r1b;
+ }
+
+ sd->state = sd_programming_state;
+ sd_erase(sd);
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ return sd_r1b;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Lock card commands (Class 7) */
+ case 42: /* CMD42: LOCK_UNLOCK */
+ if (sd->spi)
+ goto unimplemented_cmd;
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_receivingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ /* Application specific commands (Class 8) */
+ case 55: /* CMD55: APP_CMD */
+ if (sd->rca != rca)
+ return sd_r0;
+
+ sd->card_status |= APP_CMD;
+ return sd_r1;
+
+ case 56: /* CMD56: GEN_CMD */
+ fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg);
+
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->data_offset = 0;
+ if (req.arg & 1)
+ sd->state = sd_sendingdata_state;
+ else
+ sd->state = sd_receivingdata_state;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ bad_cmd:
+ sd->card_status |= ILLEGAL_COMMAND;
+
+ fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd);
+ return sd_r0;
+
+ unimplemented_cmd:
+ /* Commands that are recognised but not yet implemented in SPI mode. */
+ sd->card_status |= ILLEGAL_COMMAND;
+ fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd);
+ return sd_r0;
+ }
+
+ sd->card_status |= ILLEGAL_COMMAND;
+ fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd);
+ return sd_r0;
+}
+
+static sd_rsp_type_t sd_app_command(SDState *sd,
+ SDRequest req)
+{
+ DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg);
+ switch (req.cmd) {
+ case 6: /* ACMD6: SET_BUS_WIDTH */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->sd_status[0] &= 0x3f;
+ sd->sd_status[0] |= (req.arg & 0x03) << 6;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 13: /* ACMD13: SD_STATUS */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_sendingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
+ switch (sd->state) {
+ case sd_transfer_state:
+ *(uint32_t *) sd->data = sd->blk_written;
+
+ sd->state = sd_sendingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */
+ switch (sd->state) {
+ case sd_transfer_state:
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 41: /* ACMD41: SD_APP_OP_COND */
+ if (sd->spi) {
+ /* SEND_OP_CMD */
+ sd->state = sd_transfer_state;
+ return sd_r1;
+ }
+ switch (sd->state) {
+ case sd_idle_state:
+ /* We accept any voltage. 10000 V is nothing. */
+ if (req.arg)
+ sd->state = sd_ready_state;
+
+ return sd_r3;
+
+ default:
+ break;
+ }
+ break;
+
+ case 42: /* ACMD42: SET_CLR_CARD_DETECT */
+ switch (sd->state) {
+ case sd_transfer_state:
+ /* Bringing in the 50KOhm pull-up resistor... Done. */
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ case 51: /* ACMD51: SEND_SCR */
+ switch (sd->state) {
+ case sd_transfer_state:
+ sd->state = sd_sendingdata_state;
+ sd->data_start = 0;
+ sd->data_offset = 0;
+ return sd_r1;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ /* Fall back to standard commands. */
+ sd->card_status &= ~APP_CMD;
+ return sd_normal_command(sd, req);
+ }
+
+ fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd);
+ return sd_r0;
+}
+
+int sd_do_command(SDState *sd, SDRequest *req,
+ uint8_t *response) {
+ uint32_t last_status = sd->card_status;
+ sd_rsp_type_t rtype;
+ int rsplen;
+
+ if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable) {
+ return 0;
+ }
+
+ if (sd_req_crc_validate(req)) {
+ sd->card_status &= ~COM_CRC_ERROR;
+ return 0;
+ }
+
+ sd->card_status &= ~CARD_STATUS_B;
+ sd_set_status(sd);
+
+ if (last_status & CARD_IS_LOCKED)
+ if (((last_status & APP_CMD) &&
+ req->cmd == 41) ||
+ (!(last_status & APP_CMD) &&
+ (sd_cmd_class[req->cmd] == 0 ||
+ sd_cmd_class[req->cmd] == 7 ||
+ req->cmd == 16 || req->cmd == 55))) {
+ sd->card_status |= ILLEGAL_COMMAND;
+ fprintf(stderr, "SD: Card is locked\n");
+ return 0;
+ }
+
+ if (last_status & APP_CMD) {
+ rtype = sd_app_command(sd, *req);
+ sd->card_status &= ~APP_CMD;
+ } else
+ rtype = sd_normal_command(sd, *req);
+
+ sd->current_cmd = req->cmd;
+
+ switch (rtype) {
+ case sd_r1:
+ case sd_r1b:
+ sd_response_r1_make(sd, response, last_status);
+ rsplen = 4;
+ break;
+
+ case sd_r2_i:
+ memcpy(response, sd->cid, sizeof(sd->cid));
+ rsplen = 16;
+ break;
+
+ case sd_r2_s:
+ memcpy(response, sd->csd, sizeof(sd->csd));
+ rsplen = 16;
+ break;
+
+ case sd_r3:
+ sd_response_r3_make(sd, response);
+ rsplen = 4;
+ break;
+
+ case sd_r6:
+ sd_response_r6_make(sd, response);
+ rsplen = 4;
+ break;
+
+ case sd_r7:
+ sd_response_r7_make(sd, response);
+ rsplen = 4;
+ break;
+
+ case sd_r0:
+ default:
+ rsplen = 0;
+ break;
+ }
+
+ if (sd->card_status & ILLEGAL_COMMAND)
+ rsplen = 0;
+
+#ifdef DEBUG_SD
+ if (rsplen) {
+ int i;
+ DPRINTF("Response:");
+ for (i = 0; i < rsplen; i++)
+ printf(" %02x", response[i]);
+ printf(" state %d\n", sd->state);
+ } else {
+ DPRINTF("No response %d\n", sd->state);
+ }
+#endif
+
+ return rsplen;
+}
+
+static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
+{
+ uint64_t end = addr + len;
+
+ DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n",
+ (unsigned long long) addr, len);
+ if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_read: read error on host side\n");
+ return;
+ }
+
+ if (end > (addr & ~511) + 512) {
+ memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511));
+
+ if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_read: read error on host side\n");
+ return;
+ }
+ memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511);
+ } else
+ memcpy(sd->data, sd->buf + (addr & 511), len);
+}
+
+static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
+{
+ uint64_t end = addr + len;
+
+ if ((addr & 511) || len < 512)
+ if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_write: read error on host side\n");
+ return;
+ }
+
+ if (end > (addr & ~511) + 512) {
+ memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
+ if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_write: write error on host side\n");
+ return;
+ }
+
+ if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_write: read error on host side\n");
+ return;
+ }
+ memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
+ if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) == -1)
+ fprintf(stderr, "sd_blk_write: write error on host side\n");
+ } else {
+ memcpy(sd->buf + (addr & 511), sd->data, len);
+ if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1)
+ fprintf(stderr, "sd_blk_write: write error on host side\n");
+ }
+}
+
+#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
+#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len)
+#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
+#define APP_WRITE_BLOCK(a, len)
+
+void sd_write_data(SDState *sd, uint8_t value)
+{
+ int i;
+
+ if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+ return;
+
+ if (sd->state != sd_receivingdata_state) {
+ fprintf(stderr, "sd_write_data: not in Receiving-Data state\n");
+ return;
+ }
+
+ if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
+ return;
+
+ switch (sd->current_cmd) {
+ case 24: /* CMD24: WRITE_SINGLE_BLOCK */
+ sd->data[sd->data_offset ++] = value;
+ if (sd->data_offset >= sd->blk_len) {
+ /* TODO: Check CRC before committing */
+ sd->state = sd_programming_state;
+ BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+ sd->blk_written ++;
+ sd->csd[14] |= 0x40;
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ }
+ break;
+
+ case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
+ sd->data[sd->data_offset ++] = value;
+ if (sd->data_offset >= sd->blk_len) {
+ /* TODO: Check CRC before committing */
+ sd->state = sd_programming_state;
+ BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+ sd->blk_written ++;
+ sd->data_start += sd->blk_len;
+ sd->data_offset = 0;
+ if (sd->data_start + sd->blk_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ break;
+ }
+ if (sd_wp_addr(sd, sd->data_start)) {
+ sd->card_status |= WP_VIOLATION;
+ break;
+ }
+ sd->csd[14] |= 0x40;
+
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_receivingdata_state;
+ }
+ break;
+
+ case 26: /* CMD26: PROGRAM_CID */
+ sd->data[sd->data_offset ++] = value;
+ if (sd->data_offset >= sizeof(sd->cid)) {
+ /* TODO: Check CRC before committing */
+ sd->state = sd_programming_state;
+ for (i = 0; i < sizeof(sd->cid); i ++)
+ if ((sd->cid[i] | 0x00) != sd->data[i])
+ sd->card_status |= CID_CSD_OVERWRITE;
+
+ if (!(sd->card_status & CID_CSD_OVERWRITE))
+ for (i = 0; i < sizeof(sd->cid); i ++) {
+ sd->cid[i] |= 0x00;
+ sd->cid[i] &= sd->data[i];
+ }
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ }
+ break;
+
+ case 27: /* CMD27: PROGRAM_CSD */
+ sd->data[sd->data_offset ++] = value;
+ if (sd->data_offset >= sizeof(sd->csd)) {
+ /* TODO: Check CRC before committing */
+ sd->state = sd_programming_state;
+ for (i = 0; i < sizeof(sd->csd); i ++)
+ if ((sd->csd[i] | sd_csd_rw_mask[i]) !=
+ (sd->data[i] | sd_csd_rw_mask[i]))
+ sd->card_status |= CID_CSD_OVERWRITE;
+
+ /* Copy flag (OTP) & Permanent write protect */
+ if (sd->csd[14] & ~sd->data[14] & 0x60)
+ sd->card_status |= CID_CSD_OVERWRITE;
+
+ if (!(sd->card_status & CID_CSD_OVERWRITE))
+ for (i = 0; i < sizeof(sd->csd); i ++) {
+ sd->csd[i] |= sd_csd_rw_mask[i];
+ sd->csd[i] &= sd->data[i];
+ }
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ }
+ break;
+
+ case 42: /* CMD42: LOCK_UNLOCK */
+ sd->data[sd->data_offset ++] = value;
+ if (sd->data_offset >= sd->blk_len) {
+ /* TODO: Check CRC before committing */
+ sd->state = sd_programming_state;
+ sd_lock_command(sd);
+ /* Bzzzzzzztt .... Operation complete. */
+ sd->state = sd_transfer_state;
+ }
+ break;
+
+ case 56: /* CMD56: GEN_CMD */
+ sd->data[sd->data_offset ++] = value;
+ if (sd->data_offset >= sd->blk_len) {
+ APP_WRITE_BLOCK(sd->data_start, sd->data_offset);
+ sd->state = sd_transfer_state;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "sd_write_data: unknown command\n");
+ break;
+ }
+}
+
+uint8_t sd_read_data(SDState *sd)
+{
+ /* TODO: Append CRCs */
+ uint8_t ret;
+ int io_len;
+
+ if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+ return 0x00;
+
+ if (sd->state != sd_sendingdata_state) {
+ fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
+ return 0x00;
+ }
+
+ if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
+ return 0x00;
+
+ io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
+
+ switch (sd->current_cmd) {
+ case 6: /* CMD6: SWITCH_FUNCTION */
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= 64)
+ sd->state = sd_transfer_state;
+ break;
+
+ case 9: /* CMD9: SEND_CSD */
+ case 10: /* CMD10: SEND_CID */
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= 16)
+ sd->state = sd_transfer_state;
+ break;
+
+ case 11: /* CMD11: READ_DAT_UNTIL_STOP */
+ if (sd->data_offset == 0)
+ BLK_READ_BLOCK(sd->data_start, io_len);
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= io_len) {
+ sd->data_start += io_len;
+ sd->data_offset = 0;
+ if (sd->data_start + io_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ break;
+ }
+ }
+ break;
+
+ case 13: /* ACMD13: SD_STATUS */
+ ret = sd->sd_status[sd->data_offset ++];
+
+ if (sd->data_offset >= sizeof(sd->sd_status))
+ sd->state = sd_transfer_state;
+ break;
+
+ case 17: /* CMD17: READ_SINGLE_BLOCK */
+ if (sd->data_offset == 0)
+ BLK_READ_BLOCK(sd->data_start, io_len);
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= io_len)
+ sd->state = sd_transfer_state;
+ break;
+
+ case 18: /* CMD18: READ_MULTIPLE_BLOCK */
+ if (sd->data_offset == 0)
+ BLK_READ_BLOCK(sd->data_start, io_len);
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= io_len) {
+ sd->data_start += io_len;
+ sd->data_offset = 0;
+ if (sd->data_start + io_len > sd->size) {
+ sd->card_status |= ADDRESS_ERROR;
+ break;
+ }
+ }
+ break;
+
+ case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= 4)
+ sd->state = sd_transfer_state;
+ break;
+
+ case 30: /* CMD30: SEND_WRITE_PROT */
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= 4)
+ sd->state = sd_transfer_state;
+ break;
+
+ case 51: /* ACMD51: SEND_SCR */
+ ret = sd->scr[sd->data_offset ++];
+
+ if (sd->data_offset >= sizeof(sd->scr))
+ sd->state = sd_transfer_state;
+ break;
+
+ case 56: /* CMD56: GEN_CMD */
+ if (sd->data_offset == 0)
+ APP_READ_BLOCK(sd->data_start, sd->blk_len);
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= sd->blk_len)
+ sd->state = sd_transfer_state;
+ break;
+
+ default:
+ fprintf(stderr, "sd_read_data: unknown command\n");
+ return 0x00;
+ }
+
+ return ret;
+}
+
+int sd_data_ready(SDState *sd)
+{
+ return sd->state == sd_sendingdata_state;
+}
+
+void sd_enable(SDState *sd, int enable)
+{
+ sd->enable = enable;
+}
diff --git a/hw/sd.h b/hw/sd.h
new file mode 100644
index 0000000..ac4b7c4
--- /dev/null
+++ b/hw/sd.h
@@ -0,0 +1,79 @@
+/*
+ * SD Memory Card emulation. Mostly correct for MMC too.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __hw_sd_h
+#define __hw_sd_h 1
+
+#define OUT_OF_RANGE (1 << 31)
+#define ADDRESS_ERROR (1 << 30)
+#define BLOCK_LEN_ERROR (1 << 29)
+#define ERASE_SEQ_ERROR (1 << 28)
+#define ERASE_PARAM (1 << 27)
+#define WP_VIOLATION (1 << 26)
+#define CARD_IS_LOCKED (1 << 25)
+#define LOCK_UNLOCK_FAILED (1 << 24)
+#define COM_CRC_ERROR (1 << 23)
+#define ILLEGAL_COMMAND (1 << 22)
+#define CARD_ECC_FAILED (1 << 21)
+#define CC_ERROR (1 << 20)
+#define SD_ERROR (1 << 19)
+#define CID_CSD_OVERWRITE (1 << 16)
+#define WP_ERASE_SKIP (1 << 15)
+#define CARD_ECC_DISABLED (1 << 14)
+#define ERASE_RESET (1 << 13)
+#define CURRENT_STATE (7 << 9)
+#define READY_FOR_DATA (1 << 8)
+#define APP_CMD (1 << 5)
+#define AKE_SEQ_ERROR (1 << 3)
+
+typedef enum {
+ sd_none = -1,
+ sd_bc = 0, /* broadcast -- no response */
+ sd_bcr, /* broadcast with response */
+ sd_ac, /* addressed -- no data transfer */
+ sd_adtc, /* addressed with data transfer */
+} sd_cmd_type_t;
+
+typedef struct {
+ uint8_t cmd;
+ uint32_t arg;
+ uint8_t crc;
+} SDRequest;
+
+typedef struct SDState SDState;
+
+SDState *sd_init(BlockDriverState *bs, int is_spi);
+int sd_do_command(SDState *sd, SDRequest *req,
+ uint8_t *response);
+void sd_write_data(SDState *sd, uint8_t value);
+uint8_t sd_read_data(SDState *sd);
+void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert);
+int sd_data_ready(SDState *sd);
+void sd_enable(SDState *sd, int enable);
+
+#endif /* __hw_sd_h */
diff --git a/hw/serial.c b/hw/serial.c
new file mode 100644
index 0000000..2c4af61
--- /dev/null
+++ b/hw/serial.c
@@ -0,0 +1,991 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "qemu-char.h"
+#include "isa.h"
+#include "pc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+//#define DEBUG_SERIAL
+
+#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
+
+#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */
+#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
+#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
+#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+
+#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
+#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI 0x00 /* Modem status interrupt */
+#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
+#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
+#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
+#define UART_IIR_CTI 0x0C /* Character Timeout Indication */
+
+#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functionning */
+#define UART_IIR_FE 0xC0 /* Fifo enabled */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
+#define UART_MCR_OUT2 0x08 /* Out2 complement */
+#define UART_MCR_OUT1 0x04 /* Out1 complement */
+#define UART_MCR_RTS 0x02 /* RTS complement */
+#define UART_MCR_DTR 0x01 /* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
+#define UART_MSR_RI 0x40 /* Ring Indicator */
+#define UART_MSR_DSR 0x20 /* Data Set Ready */
+#define UART_MSR_CTS 0x10 /* Clear to Send */
+#define UART_MSR_DDCD 0x08 /* Delta DCD */
+#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
+#define UART_MSR_DDSR 0x02 /* Delta DSR */
+#define UART_MSR_DCTS 0x01 /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
+
+#define UART_LSR_TEMT 0x40 /* Transmitter empty */
+#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
+#define UART_LSR_BI 0x10 /* Break interrupt indicator */
+#define UART_LSR_FE 0x08 /* Frame error indicator */
+#define UART_LSR_PE 0x04 /* Parity error indicator */
+#define UART_LSR_OE 0x02 /* Overrun error indicator */
+#define UART_LSR_DR 0x01 /* Receiver data ready */
+#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */
+
+/* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */
+
+#define UART_FCR_ITL_1 0x00 /* 1 byte ITL */
+#define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */
+#define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */
+#define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */
+
+#define UART_FCR_DMS 0x08 /* DMA Mode Select */
+#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */
+#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */
+#define UART_FCR_FE 0x01 /* FIFO Enable */
+
+#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */
+
+#define XMIT_FIFO 0
+#define RECV_FIFO 1
+#define MAX_XMIT_RETRY 4
+
+#ifdef DEBUG_SERIAL
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+typedef struct SerialFIFO {
+ uint8_t data[UART_FIFO_LENGTH];
+ uint8_t count;
+ uint8_t itl; /* Interrupt Trigger Level */
+ uint8_t tail;
+ uint8_t head;
+} SerialFIFO;
+
+struct SerialState {
+ uint16_t divider;
+ uint8_t rbr; /* receive register */
+ uint8_t thr; /* transmit holding register */
+ uint8_t tsr; /* transmit shift register */
+ uint8_t ier;
+ uint8_t iir; /* read only */
+ uint8_t lcr;
+ uint8_t mcr;
+ uint8_t lsr; /* read only */
+ uint8_t msr; /* read only */
+ uint8_t scr;
+ uint8_t fcr;
+ uint8_t fcr_vmstate; /* we can't write directly this value
+ it has side effects */
+ /* NOTE: this hidden state is necessary for tx irq generation as
+ it can be reset while reading iir */
+ int thr_ipending;
+ qemu_irq irq;
+ CharDriverState *chr;
+ int last_break_enable;
+ int it_shift;
+ int baudbase;
+ int tsr_retry;
+
+ uint64_t last_xmit_ts; /* Time when the last byte was successfully sent out of the tsr */
+ SerialFIFO recv_fifo;
+ SerialFIFO xmit_fifo;
+
+ struct QEMUTimer *fifo_timeout_timer;
+ int timeout_ipending; /* timeout interrupt pending state */
+ struct QEMUTimer *transmit_timer;
+
+
+ uint64_t char_transmit_time; /* time to transmit a char in ticks*/
+ int poll_msl;
+
+ struct QEMUTimer *modem_status_poll;
+};
+
+typedef struct ISASerialState {
+ ISADevice dev;
+ uint32_t index;
+ uint32_t iobase;
+ uint32_t isairq;
+ SerialState state;
+} ISASerialState;
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size);
+
+static void fifo_clear(SerialState *s, int fifo)
+{
+ SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+ memset(f->data, 0, UART_FIFO_LENGTH);
+ f->count = 0;
+ f->head = 0;
+ f->tail = 0;
+}
+
+static int fifo_put(SerialState *s, int fifo, uint8_t chr)
+{
+ SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+
+ /* Receive overruns do not overwrite FIFO contents. */
+ if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) {
+
+ f->data[f->head++] = chr;
+
+ if (f->head == UART_FIFO_LENGTH)
+ f->head = 0;
+ }
+
+ if (f->count < UART_FIFO_LENGTH)
+ f->count++;
+ else if (fifo == RECV_FIFO)
+ s->lsr |= UART_LSR_OE;
+
+ return 1;
+}
+
+static uint8_t fifo_get(SerialState *s, int fifo)
+{
+ SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+ uint8_t c;
+
+ if(f->count == 0)
+ return 0;
+
+ c = f->data[f->tail++];
+ if (f->tail == UART_FIFO_LENGTH)
+ f->tail = 0;
+ f->count--;
+
+ return c;
+}
+
+static void serial_update_irq(SerialState *s)
+{
+ uint8_t tmp_iir = UART_IIR_NO_INT;
+
+ if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) {
+ tmp_iir = UART_IIR_RLSI;
+ } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) {
+ /* Note that(s->ier & UART_IER_RDI) can mask this interrupt,
+ * this is not in the specification but is observed on existing
+ * hardware. */
+ tmp_iir = UART_IIR_CTI;
+ } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) &&
+ (!(s->fcr & UART_FCR_FE) ||
+ s->recv_fifo.count >= s->recv_fifo.itl)) {
+ tmp_iir = UART_IIR_RDI;
+ } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) {
+ tmp_iir = UART_IIR_THRI;
+ } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) {
+ tmp_iir = UART_IIR_MSI;
+ }
+
+ s->iir = tmp_iir | (s->iir & 0xF0);
+
+ if (tmp_iir != UART_IIR_NO_INT) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void serial_update_parameters(SerialState *s)
+{
+ int speed, parity, data_bits, stop_bits, frame_size;
+ QEMUSerialSetParams ssp;
+
+ if (s->divider == 0)
+ return;
+
+ /* Start bit. */
+ frame_size = 1;
+ if (s->lcr & 0x08) {
+ /* Parity bit. */
+ frame_size++;
+ if (s->lcr & 0x10)
+ parity = 'E';
+ else
+ parity = 'O';
+ } else {
+ parity = 'N';
+ }
+ if (s->lcr & 0x04)
+ stop_bits = 2;
+ else
+ stop_bits = 1;
+
+ data_bits = (s->lcr & 0x03) + 5;
+ frame_size += data_bits + stop_bits;
+ speed = s->baudbase / s->divider;
+ ssp.speed = speed;
+ ssp.parity = parity;
+ ssp.data_bits = data_bits;
+ ssp.stop_bits = stop_bits;
+ s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size;
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+
+ DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+}
+
+static void serial_update_msl(SerialState *s)
+{
+ uint8_t omsr;
+ int flags;
+
+ qemu_del_timer(s->modem_status_poll);
+
+ if (qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
+ s->poll_msl = -1;
+ return;
+ }
+
+ omsr = s->msr;
+
+ s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS;
+ s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR;
+ s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD;
+ s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI;
+
+ if (s->msr != omsr) {
+ /* Set delta bits */
+ s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4));
+ /* UART_MSR_TERI only if change was from 1 -> 0 */
+ if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI))
+ s->msr &= ~UART_MSR_TERI;
+ serial_update_irq(s);
+ }
+
+ /* The real 16550A apparently has a 250ns response latency to line status changes.
+ We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */
+
+ if (s->poll_msl)
+ qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + get_ticks_per_sec() / 100);
+}
+
+static void serial_xmit(void *opaque)
+{
+ SerialState *s = opaque;
+ uint64_t new_xmit_ts = qemu_get_clock(vm_clock);
+
+ if (s->tsr_retry <= 0) {
+ if (s->fcr & UART_FCR_FE) {
+ s->tsr = fifo_get(s,XMIT_FIFO);
+ if (!s->xmit_fifo.count)
+ s->lsr |= UART_LSR_THRE;
+ } else {
+ s->tsr = s->thr;
+ s->lsr |= UART_LSR_THRE;
+ }
+ }
+
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback mode, say that we just received a char */
+ serial_receive1(s, &s->tsr, 1);
+ } else if (qemu_chr_write(s->chr, &s->tsr, 1) != 1) {
+ if ((s->tsr_retry > 0) && (s->tsr_retry <= MAX_XMIT_RETRY)) {
+ s->tsr_retry++;
+ qemu_mod_timer(s->transmit_timer, new_xmit_ts + s->char_transmit_time);
+ return;
+ } else if (s->poll_msl < 0) {
+ /* If we exceed MAX_XMIT_RETRY and the backend is not a real serial port, then
+ drop any further failed writes instantly, until we get one that goes through.
+ This is to prevent guests that log to unconnected pipes or pty's from stalling. */
+ s->tsr_retry = -1;
+ }
+ }
+ else {
+ s->tsr_retry = 0;
+ }
+
+ s->last_xmit_ts = qemu_get_clock(vm_clock);
+ if (!(s->lsr & UART_LSR_THRE))
+ qemu_mod_timer(s->transmit_timer, s->last_xmit_ts + s->char_transmit_time);
+
+ if (s->lsr & UART_LSR_THRE) {
+ s->lsr |= UART_LSR_TEMT;
+ s->thr_ipending = 1;
+ serial_update_irq(s);
+ }
+}
+
+
+static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ SerialState *s = opaque;
+
+ addr &= 7;
+ DPRINTF("write addr=0x%02x val=0x%02x\n", addr, val);
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0xff00) | val;
+ serial_update_parameters(s);
+ } else {
+ s->thr = (uint8_t) val;
+ if(s->fcr & UART_FCR_FE) {
+ fifo_put(s, XMIT_FIFO, s->thr);
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_TEMT;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ } else {
+ s->thr_ipending = 0;
+ s->lsr &= ~UART_LSR_THRE;
+ serial_update_irq(s);
+ }
+ serial_xmit(s);
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ s->divider = (s->divider & 0x00ff) | (val << 8);
+ serial_update_parameters(s);
+ } else {
+ s->ier = val & 0x0f;
+ /* If the backend device is a real serial port, turn polling of the modem
+ status lines on physical port on or off depending on UART_IER_MSI state */
+ if (s->poll_msl >= 0) {
+ if (s->ier & UART_IER_MSI) {
+ s->poll_msl = 1;
+ serial_update_msl(s);
+ } else {
+ qemu_del_timer(s->modem_status_poll);
+ s->poll_msl = 0;
+ }
+ }
+ if (s->lsr & UART_LSR_THRE) {
+ s->thr_ipending = 1;
+ serial_update_irq(s);
+ }
+ }
+ break;
+ case 2:
+ val = val & 0xFF;
+
+ if (s->fcr == val)
+ break;
+
+ /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
+ if ((val ^ s->fcr) & UART_FCR_FE)
+ val |= UART_FCR_XFR | UART_FCR_RFR;
+
+ /* FIFO clear */
+
+ if (val & UART_FCR_RFR) {
+ qemu_del_timer(s->fifo_timeout_timer);
+ s->timeout_ipending=0;
+ fifo_clear(s,RECV_FIFO);
+ }
+
+ if (val & UART_FCR_XFR) {
+ fifo_clear(s,XMIT_FIFO);
+ }
+
+ if (val & UART_FCR_FE) {
+ s->iir |= UART_IIR_FE;
+ /* Set RECV_FIFO trigger Level */
+ switch (val & 0xC0) {
+ case UART_FCR_ITL_1:
+ s->recv_fifo.itl = 1;
+ break;
+ case UART_FCR_ITL_2:
+ s->recv_fifo.itl = 4;
+ break;
+ case UART_FCR_ITL_3:
+ s->recv_fifo.itl = 8;
+ break;
+ case UART_FCR_ITL_4:
+ s->recv_fifo.itl = 14;
+ break;
+ }
+ } else
+ s->iir &= ~UART_IIR_FE;
+
+ /* Set fcr - or at least the bits in it that are supposed to "stick" */
+ s->fcr = val & 0xC9;
+ serial_update_irq(s);
+ break;
+ case 3:
+ {
+ int break_enable;
+ s->lcr = val;
+ serial_update_parameters(s);
+ break_enable = (val >> 6) & 1;
+ if (break_enable != s->last_break_enable) {
+ s->last_break_enable = break_enable;
+ qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+ &break_enable);
+ }
+ }
+ break;
+ case 4:
+ {
+ int flags;
+ int old_mcr = s->mcr;
+ s->mcr = val & 0x1f;
+ if (val & UART_MCR_LOOP)
+ break;
+
+ if (s->poll_msl >= 0 && old_mcr != s->mcr) {
+
+ qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+
+ flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
+
+ if (val & UART_MCR_RTS)
+ flags |= CHR_TIOCM_RTS;
+ if (val & UART_MCR_DTR)
+ flags |= CHR_TIOCM_DTR;
+
+ qemu_chr_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+ /* Update the modem status after a one-character-send wait-time, since there may be a response
+ from the device/computer at the other end of the serial line */
+ qemu_mod_timer(s->modem_status_poll, qemu_get_clock(vm_clock) + s->char_transmit_time);
+ }
+ }
+ break;
+ case 5:
+ break;
+ case 6:
+ break;
+ case 7:
+ s->scr = val;
+ break;
+ }
+}
+
+static uint32_t serial_ioport_read(void *opaque, uint32_t addr)
+{
+ SerialState *s = opaque;
+ uint32_t ret;
+
+ addr &= 7;
+ switch(addr) {
+ default:
+ case 0:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = s->divider & 0xff;
+ } else {
+ if(s->fcr & UART_FCR_FE) {
+ ret = fifo_get(s,RECV_FIFO);
+ if (s->recv_fifo.count == 0)
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ else
+ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (vm_clock) + s->char_transmit_time * 4);
+ s->timeout_ipending = 0;
+ } else {
+ ret = s->rbr;
+ s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+ }
+ serial_update_irq(s);
+ if (!(s->mcr & UART_MCR_LOOP)) {
+ /* in loopback mode, don't receive any data */
+ qemu_chr_accept_input(s->chr);
+ }
+ }
+ break;
+ case 1:
+ if (s->lcr & UART_LCR_DLAB) {
+ ret = (s->divider >> 8) & 0xff;
+ } else {
+ ret = s->ier;
+ }
+ break;
+ case 2:
+ ret = s->iir;
+ if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
+ s->thr_ipending = 0;
+ serial_update_irq(s);
+ }
+ break;
+ case 3:
+ ret = s->lcr;
+ break;
+ case 4:
+ ret = s->mcr;
+ break;
+ case 5:
+ ret = s->lsr;
+ /* Clear break and overrun interrupts */
+ if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) {
+ s->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
+ serial_update_irq(s);
+ }
+ break;
+ case 6:
+ if (s->mcr & UART_MCR_LOOP) {
+ /* in loopback, the modem output pins are connected to the
+ inputs */
+ ret = (s->mcr & 0x0c) << 4;
+ ret |= (s->mcr & 0x02) << 3;
+ ret |= (s->mcr & 0x01) << 5;
+ } else {
+ if (s->poll_msl >= 0)
+ serial_update_msl(s);
+ ret = s->msr;
+ /* Clear delta bits & msr int after read, if they were set */
+ if (s->msr & UART_MSR_ANY_DELTA) {
+ s->msr &= 0xF0;
+ serial_update_irq(s);
+ }
+ }
+ break;
+ case 7:
+ ret = s->scr;
+ break;
+ }
+ DPRINTF("read addr=0x%02x val=0x%02x\n", addr, ret);
+ return ret;
+}
+
+static int serial_can_receive(SerialState *s)
+{
+ if(s->fcr & UART_FCR_FE) {
+ if(s->recv_fifo.count < UART_FIFO_LENGTH)
+ /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is
+ advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond,
+ effectively overriding the ITL that the guest has set. */
+ return (s->recv_fifo.count <= s->recv_fifo.itl) ? s->recv_fifo.itl - s->recv_fifo.count : 1;
+ else
+ return 0;
+ } else {
+ return !(s->lsr & UART_LSR_DR);
+ }
+}
+
+static void serial_receive_break(SerialState *s)
+{
+ s->rbr = 0;
+ /* When the LSR_DR is set a null byte is pushed into the fifo */
+ fifo_put(s, RECV_FIFO, '\0');
+ s->lsr |= UART_LSR_BI | UART_LSR_DR;
+ serial_update_irq(s);
+}
+
+/* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */
+static void fifo_timeout_int (void *opaque) {
+ SerialState *s = opaque;
+ if (s->recv_fifo.count) {
+ s->timeout_ipending = 1;
+ serial_update_irq(s);
+ }
+}
+
+static int serial_can_receive1(void *opaque)
+{
+ SerialState *s = opaque;
+ return serial_can_receive(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ SerialState *s = opaque;
+ if(s->fcr & UART_FCR_FE) {
+ int i;
+ for (i = 0; i < size; i++) {
+ fifo_put(s, RECV_FIFO, buf[i]);
+ }
+ s->lsr |= UART_LSR_DR;
+ /* call the timeout receive callback in 4 char transmit time */
+ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (vm_clock) + s->char_transmit_time * 4);
+ } else {
+ if (s->lsr & UART_LSR_DR)
+ s->lsr |= UART_LSR_OE;
+ s->rbr = buf[0];
+ s->lsr |= UART_LSR_DR;
+ }
+ serial_update_irq(s);
+}
+
+static void serial_event(void *opaque, int event)
+{
+ SerialState *s = opaque;
+ DPRINTF("event %x\n", event);
+ if (event == CHR_EVENT_BREAK)
+ serial_receive_break(s);
+}
+
+static void serial_pre_save(void *opaque)
+{
+ SerialState *s = opaque;
+ s->fcr_vmstate = s->fcr;
+}
+
+static int serial_post_load(void *opaque, int version_id)
+{
+ SerialState *s = opaque;
+
+ if (version_id < 3) {
+ s->fcr_vmstate = 0;
+ }
+ /* Initialize fcr via setter to perform essential side-effects */
+ serial_ioport_write(s, 0x02, s->fcr_vmstate);
+ serial_update_parameters(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_serial = {
+ .name = "serial",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .pre_save = serial_pre_save,
+ .post_load = serial_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16_V(divider, SerialState, 2),
+ VMSTATE_UINT8(rbr, SerialState),
+ VMSTATE_UINT8(ier, SerialState),
+ VMSTATE_UINT8(iir, SerialState),
+ VMSTATE_UINT8(lcr, SerialState),
+ VMSTATE_UINT8(mcr, SerialState),
+ VMSTATE_UINT8(lsr, SerialState),
+ VMSTATE_UINT8(msr, SerialState),
+ VMSTATE_UINT8(scr, SerialState),
+ VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void serial_reset(void *opaque)
+{
+ SerialState *s = opaque;
+
+ s->rbr = 0;
+ s->ier = 0;
+ s->iir = UART_IIR_NO_INT;
+ s->lcr = 0;
+ s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
+ s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+ /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */
+ s->divider = 0x0C;
+ s->mcr = UART_MCR_OUT2;
+ s->scr = 0;
+ s->tsr_retry = 0;
+ s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
+ s->poll_msl = 0;
+
+ fifo_clear(s,RECV_FIFO);
+ fifo_clear(s,XMIT_FIFO);
+
+ s->last_xmit_ts = qemu_get_clock(vm_clock);
+
+ s->thr_ipending = 0;
+ s->last_break_enable = 0;
+ qemu_irq_lower(s->irq);
+}
+
+static void serial_init_core(SerialState *s)
+{
+ if (!s->chr) {
+ fprintf(stderr, "Can't create serial device, empty char device\n");
+ exit(1);
+ }
+
+ s->modem_status_poll = qemu_new_timer(vm_clock, (QEMUTimerCB *) serial_update_msl, s);
+
+ s->fifo_timeout_timer = qemu_new_timer(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s);
+ s->transmit_timer = qemu_new_timer(vm_clock, (QEMUTimerCB *) serial_xmit, s);
+
+ qemu_register_reset(serial_reset, s);
+
+ qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
+ serial_event, s);
+}
+
+/* Change the main reference oscillator frequency. */
+void serial_set_frequency(SerialState *s, uint32_t frequency)
+{
+ s->baudbase = frequency;
+ serial_update_parameters(s);
+}
+
+static const int isa_serial_io[MAX_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+static const int isa_serial_irq[MAX_SERIAL_PORTS] = { 4, 3, 4, 3 };
+
+static int serial_isa_initfn(ISADevice *dev)
+{
+ static int index;
+ ISASerialState *isa = DO_UPCAST(ISASerialState, dev, dev);
+ SerialState *s = &isa->state;
+
+ if (isa->index == -1)
+ isa->index = index;
+ if (isa->index >= MAX_SERIAL_PORTS)
+ return -1;
+ if (isa->iobase == -1)
+ isa->iobase = isa_serial_io[isa->index];
+ if (isa->isairq == -1)
+ isa->isairq = isa_serial_irq[isa->index];
+ index++;
+
+ s->baudbase = 115200;
+ isa_init_irq(dev, &s->irq, isa->isairq);
+ serial_init_core(s);
+ qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3);
+
+ register_ioport_write(isa->iobase, 8, 1, serial_ioport_write, s);
+ register_ioport_read(isa->iobase, 8, 1, serial_ioport_read, s);
+ isa_init_ioport_range(dev, isa->iobase, 8);
+ return 0;
+}
+
+SerialState *serial_isa_init(int index, CharDriverState *chr)
+{
+ ISADevice *dev;
+
+ dev = isa_create("isa-serial");
+ qdev_prop_set_uint32(&dev->qdev, "index", index);
+ qdev_prop_set_chr(&dev->qdev, "chardev", chr);
+ if (qdev_init(&dev->qdev) < 0)
+ return NULL;
+ return &DO_UPCAST(ISASerialState, dev, dev)->state;
+}
+
+static const VMStateDescription vmstate_isa_serial = {
+ .name = "serial",
+ .version_id = 3,
+ .minimum_version_id = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+SerialState *serial_init(int base, qemu_irq irq, int baudbase,
+ CharDriverState *chr)
+{
+ SerialState *s;
+
+ s = qemu_mallocz(sizeof(SerialState));
+
+ s->irq = irq;
+ s->baudbase = baudbase;
+ s->chr = chr;
+ serial_init_core(s);
+
+ vmstate_register(NULL, base, &vmstate_serial, s);
+
+ register_ioport_write(base, 8, 1, serial_ioport_write, s);
+ register_ioport_read(base, 8, 1, serial_ioport_read, s);
+ return s;
+}
+
+/* Memory mapped interface */
+static uint32_t serial_mm_readb(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+
+ return serial_ioport_read(s, addr >> s->it_shift) & 0xFF;
+}
+
+static void serial_mm_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, addr >> s->it_shift, value & 0xFF);
+}
+
+static uint32_t serial_mm_readw_be(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+ uint32_t val;
+
+ val = serial_ioport_read(s, addr >> s->it_shift) & 0xFFFF;
+ val = bswap16(val);
+ return val;
+}
+
+static uint32_t serial_mm_readw_le(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+ uint32_t val;
+
+ val = serial_ioport_read(s, addr >> s->it_shift) & 0xFFFF;
+ return val;
+}
+
+static void serial_mm_writew_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ SerialState *s = opaque;
+
+ value = bswap16(value);
+ serial_ioport_write(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static void serial_mm_writew_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t serial_mm_readl_be(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+ uint32_t val;
+
+ val = serial_ioport_read(s, addr >> s->it_shift);
+ val = bswap32(val);
+ return val;
+}
+
+static uint32_t serial_mm_readl_le(void *opaque, target_phys_addr_t addr)
+{
+ SerialState *s = opaque;
+ uint32_t val;
+
+ val = serial_ioport_read(s, addr >> s->it_shift);
+ return val;
+}
+
+static void serial_mm_writel_be(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ SerialState *s = opaque;
+
+ value = bswap32(value);
+ serial_ioport_write(s, addr >> s->it_shift, value);
+}
+
+static void serial_mm_writel_le(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, addr >> s->it_shift, value);
+}
+
+static CPUReadMemoryFunc * const serial_mm_read_be[] = {
+ &serial_mm_readb,
+ &serial_mm_readw_be,
+ &serial_mm_readl_be,
+};
+
+static CPUWriteMemoryFunc * const serial_mm_write_be[] = {
+ &serial_mm_writeb,
+ &serial_mm_writew_be,
+ &serial_mm_writel_be,
+};
+
+static CPUReadMemoryFunc * const serial_mm_read_le[] = {
+ &serial_mm_readb,
+ &serial_mm_readw_le,
+ &serial_mm_readl_le,
+};
+
+static CPUWriteMemoryFunc * const serial_mm_write_le[] = {
+ &serial_mm_writeb,
+ &serial_mm_writew_le,
+ &serial_mm_writel_le,
+};
+
+SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
+ qemu_irq irq, int baudbase,
+ CharDriverState *chr, int ioregister,
+ int be)
+{
+ SerialState *s;
+ int s_io_memory;
+
+ s = qemu_mallocz(sizeof(SerialState));
+
+ s->it_shift = it_shift;
+ s->irq = irq;
+ s->baudbase = baudbase;
+ s->chr = chr;
+
+ serial_init_core(s);
+ vmstate_register(NULL, base, &vmstate_serial, s);
+
+ if (ioregister) {
+ if (be) {
+ s_io_memory = cpu_register_io_memory(serial_mm_read_be,
+ serial_mm_write_be, s,
+ DEVICE_NATIVE_ENDIAN);
+ } else {
+ s_io_memory = cpu_register_io_memory(serial_mm_read_le,
+ serial_mm_write_le, s,
+ DEVICE_NATIVE_ENDIAN);
+ }
+ cpu_register_physical_memory(base, 8 << it_shift, s_io_memory);
+ }
+ serial_update_msl(s);
+ return s;
+}
+
+static ISADeviceInfo serial_isa_info = {
+ .qdev.name = "isa-serial",
+ .qdev.size = sizeof(ISASerialState),
+ .qdev.vmsd = &vmstate_isa_serial,
+ .init = serial_isa_initfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("index", ISASerialState, index, -1),
+ DEFINE_PROP_HEX32("iobase", ISASerialState, iobase, -1),
+ DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1),
+ DEFINE_PROP_CHR("chardev", ISASerialState, state.chr),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void serial_register_devices(void)
+{
+ isa_qdev_register(&serial_isa_info);
+}
+
+device_init(serial_register_devices)
diff --git a/hw/sh.h b/hw/sh.h
new file mode 100644
index 0000000..d30e9f5
--- /dev/null
+++ b/hw/sh.h
@@ -0,0 +1,54 @@
+#ifndef QEMU_SH_H
+#define QEMU_SH_H
+/* Definitions for SH board emulation. */
+
+#include "sh_intc.h"
+
+#define A7ADDR(x) ((x) & 0x1fffffff)
+#define P4ADDR(x) ((x) | 0xe0000000)
+
+/* sh7750.c */
+struct SH7750State;
+
+struct SH7750State *sh7750_init(CPUState * cpu);
+
+typedef struct {
+ /* The callback will be triggered if any of the designated lines change */
+ uint16_t portamask_trigger;
+ uint16_t portbmask_trigger;
+ /* Return 0 if no action was taken */
+ int (*port_change_cb) (uint16_t porta, uint16_t portb,
+ uint16_t * periph_pdtra,
+ uint16_t * periph_portdira,
+ uint16_t * periph_pdtrb,
+ uint16_t * periph_portdirb);
+} sh7750_io_device;
+
+int sh7750_register_io_device(struct SH7750State *s,
+ sh7750_io_device * device);
+/* sh_timer.c */
+#define TMU012_FEAT_TOCR (1 << 0)
+#define TMU012_FEAT_3CHAN (1 << 1)
+#define TMU012_FEAT_EXTCLK (1 << 2)
+void tmu012_init(target_phys_addr_t base, int feat, uint32_t freq,
+ qemu_irq ch0_irq, qemu_irq ch1_irq,
+ qemu_irq ch2_irq0, qemu_irq ch2_irq1);
+
+
+/* sh_serial.c */
+#define SH_SERIAL_FEAT_SCIF (1 << 0)
+void sh_serial_init (target_phys_addr_t base, int feat,
+ uint32_t freq, CharDriverState *chr,
+ qemu_irq eri_source,
+ qemu_irq rxi_source,
+ qemu_irq txi_source,
+ qemu_irq tei_source,
+ qemu_irq bri_source);
+
+/* sh7750.c */
+qemu_irq sh7750_irl(struct SH7750State *s);
+
+/* tc58128.c */
+int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2);
+
+#endif
diff --git a/hw/sh7750.c b/hw/sh7750.c
new file mode 100644
index 0000000..19d5bf8
--- /dev/null
+++ b/hw/sh7750.c
@@ -0,0 +1,818 @@
+/*
+ * SH7750 device
+ *
+ * Copyright (c) 2007 Magnus Damm
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdio.h>
+#include "hw.h"
+#include "sh.h"
+#include "sysemu.h"
+#include "sh7750_regs.h"
+#include "sh7750_regnames.h"
+#include "sh_intc.h"
+#include "exec-all.h"
+#include "cpu.h"
+
+#define NB_DEVICES 4
+
+typedef struct SH7750State {
+ /* CPU */
+ CPUSH4State *cpu;
+ /* Peripheral frequency in Hz */
+ uint32_t periph_freq;
+ /* SDRAM controller */
+ uint32_t bcr1;
+ uint16_t bcr2;
+ uint16_t bcr3;
+ uint32_t bcr4;
+ uint16_t rfcr;
+ /* PCMCIA controller */
+ uint16_t pcr;
+ /* IO ports */
+ uint16_t gpioic;
+ uint32_t pctra;
+ uint32_t pctrb;
+ uint16_t portdira; /* Cached */
+ uint16_t portpullupa; /* Cached */
+ uint16_t portdirb; /* Cached */
+ uint16_t portpullupb; /* Cached */
+ uint16_t pdtra;
+ uint16_t pdtrb;
+ uint16_t periph_pdtra; /* Imposed by the peripherals */
+ uint16_t periph_portdira; /* Direction seen from the peripherals */
+ uint16_t periph_pdtrb; /* Imposed by the peripherals */
+ uint16_t periph_portdirb; /* Direction seen from the peripherals */
+ sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */
+
+ /* Cache */
+ uint32_t ccr;
+
+ struct intc_desc intc;
+} SH7750State;
+
+static inline int has_bcr3_and_bcr4(SH7750State * s)
+{
+ return (s->cpu->features & SH_FEATURE_BCR3_AND_BCR4);
+}
+/**********************************************************************
+ I/O ports
+**********************************************************************/
+
+int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device)
+{
+ int i;
+
+ for (i = 0; i < NB_DEVICES; i++) {
+ if (s->devices[i] == NULL) {
+ s->devices[i] = device;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static uint16_t portdir(uint32_t v)
+{
+#define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n))
+ return
+ EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) |
+ EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) |
+ EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) |
+ EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) |
+ EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) |
+ EVENPORTMASK(0);
+}
+
+static uint16_t portpullup(uint32_t v)
+{
+#define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n))
+ return
+ ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) |
+ ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) |
+ ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) |
+ ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) |
+ ODDPORTMASK(1) | ODDPORTMASK(0);
+}
+
+static uint16_t porta_lines(SH7750State * s)
+{
+ return (s->portdira & s->pdtra) | /* CPU */
+ (s->periph_portdira & s->periph_pdtra) | /* Peripherals */
+ (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */
+}
+
+static uint16_t portb_lines(SH7750State * s)
+{
+ return (s->portdirb & s->pdtrb) | /* CPU */
+ (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */
+ (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */
+}
+
+static void gen_port_interrupts(SH7750State * s)
+{
+ /* XXXXX interrupts not generated */
+}
+
+static void porta_changed(SH7750State * s, uint16_t prev)
+{
+ uint16_t currenta, changes;
+ int i, r = 0;
+
+#if 0
+ fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n",
+ prev, porta_lines(s));
+ fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra);
+#endif
+ currenta = porta_lines(s);
+ if (currenta == prev)
+ return;
+ changes = currenta ^ prev;
+
+ for (i = 0; i < NB_DEVICES; i++) {
+ if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) {
+ r |= s->devices[i]->port_change_cb(currenta, portb_lines(s),
+ &s->periph_pdtra,
+ &s->periph_portdira,
+ &s->periph_pdtrb,
+ &s->periph_portdirb);
+ }
+ }
+
+ if (r)
+ gen_port_interrupts(s);
+}
+
+static void portb_changed(SH7750State * s, uint16_t prev)
+{
+ uint16_t currentb, changes;
+ int i, r = 0;
+
+ currentb = portb_lines(s);
+ if (currentb == prev)
+ return;
+ changes = currentb ^ prev;
+
+ for (i = 0; i < NB_DEVICES; i++) {
+ if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) {
+ r |= s->devices[i]->port_change_cb(portb_lines(s), currentb,
+ &s->periph_pdtra,
+ &s->periph_portdira,
+ &s->periph_pdtrb,
+ &s->periph_portdirb);
+ }
+ }
+
+ if (r)
+ gen_port_interrupts(s);
+}
+
+/**********************************************************************
+ Memory
+**********************************************************************/
+
+static void error_access(const char *kind, target_phys_addr_t addr)
+{
+ fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") not supported\n",
+ kind, regname(addr), addr);
+}
+
+static void ignore_access(const char *kind, target_phys_addr_t addr)
+{
+ fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") ignored\n",
+ kind, regname(addr), addr);
+}
+
+static uint32_t sh7750_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ switch (addr) {
+ default:
+ error_access("byte read", addr);
+ abort();
+ }
+}
+
+static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ SH7750State *s = opaque;
+
+ switch (addr) {
+ case SH7750_BCR2_A7:
+ return s->bcr2;
+ case SH7750_BCR3_A7:
+ if(!has_bcr3_and_bcr4(s))
+ error_access("word read", addr);
+ return s->bcr3;
+ case SH7750_FRQCR_A7:
+ return 0;
+ case SH7750_PCR_A7:
+ return s->pcr;
+ case SH7750_RFCR_A7:
+ fprintf(stderr,
+ "Read access to refresh count register, incrementing\n");
+ return s->rfcr++;
+ case SH7750_PDTRA_A7:
+ return porta_lines(s);
+ case SH7750_PDTRB_A7:
+ return portb_lines(s);
+ case SH7750_RTCOR_A7:
+ case SH7750_RTCNT_A7:
+ case SH7750_RTCSR_A7:
+ ignore_access("word read", addr);
+ return 0;
+ default:
+ error_access("word read", addr);
+ abort();
+ }
+}
+
+static uint32_t sh7750_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SH7750State *s = opaque;
+
+ switch (addr) {
+ case SH7750_BCR1_A7:
+ return s->bcr1;
+ case SH7750_BCR4_A7:
+ if(!has_bcr3_and_bcr4(s))
+ error_access("long read", addr);
+ return s->bcr4;
+ case SH7750_WCR1_A7:
+ case SH7750_WCR2_A7:
+ case SH7750_WCR3_A7:
+ case SH7750_MCR_A7:
+ ignore_access("long read", addr);
+ return 0;
+ case SH7750_MMUCR_A7:
+ return s->cpu->mmucr;
+ case SH7750_PTEH_A7:
+ return s->cpu->pteh;
+ case SH7750_PTEL_A7:
+ return s->cpu->ptel;
+ case SH7750_TTB_A7:
+ return s->cpu->ttb;
+ case SH7750_TEA_A7:
+ return s->cpu->tea;
+ case SH7750_TRA_A7:
+ return s->cpu->tra;
+ case SH7750_EXPEVT_A7:
+ return s->cpu->expevt;
+ case SH7750_INTEVT_A7:
+ return s->cpu->intevt;
+ case SH7750_CCR_A7:
+ return s->ccr;
+ case 0x1f000030: /* Processor version */
+ return s->cpu->pvr;
+ case 0x1f000040: /* Cache version */
+ return s->cpu->cvr;
+ case 0x1f000044: /* Processor revision */
+ return s->cpu->prr;
+ default:
+ error_access("long read", addr);
+ abort();
+ }
+}
+
+#define is_in_sdrmx(a, x) (a >= SH7750_SDMR ## x ## _A7 \
+ && a <= (SH7750_SDMR ## x ## _A7 + SH7750_SDMR ## x ## _REGNB))
+static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+
+ if (is_in_sdrmx(addr, 2) || is_in_sdrmx(addr, 3)) {
+ ignore_access("byte write", addr);
+ return;
+ }
+
+ error_access("byte write", addr);
+ abort();
+}
+
+static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ SH7750State *s = opaque;
+ uint16_t temp;
+
+ switch (addr) {
+ /* SDRAM controller */
+ case SH7750_BCR2_A7:
+ s->bcr2 = mem_value;
+ return;
+ case SH7750_BCR3_A7:
+ if(!has_bcr3_and_bcr4(s))
+ error_access("word write", addr);
+ s->bcr3 = mem_value;
+ return;
+ case SH7750_PCR_A7:
+ s->pcr = mem_value;
+ return;
+ case SH7750_RTCNT_A7:
+ case SH7750_RTCOR_A7:
+ case SH7750_RTCSR_A7:
+ ignore_access("word write", addr);
+ return;
+ /* IO ports */
+ case SH7750_PDTRA_A7:
+ temp = porta_lines(s);
+ s->pdtra = mem_value;
+ porta_changed(s, temp);
+ return;
+ case SH7750_PDTRB_A7:
+ temp = portb_lines(s);
+ s->pdtrb = mem_value;
+ portb_changed(s, temp);
+ return;
+ case SH7750_RFCR_A7:
+ fprintf(stderr, "Write access to refresh count register\n");
+ s->rfcr = mem_value;
+ return;
+ case SH7750_GPIOIC_A7:
+ s->gpioic = mem_value;
+ if (mem_value != 0) {
+ fprintf(stderr, "I/O interrupts not implemented\n");
+ abort();
+ }
+ return;
+ default:
+ error_access("word write", addr);
+ abort();
+ }
+}
+
+static void sh7750_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ SH7750State *s = opaque;
+ uint16_t temp;
+
+ switch (addr) {
+ /* SDRAM controller */
+ case SH7750_BCR1_A7:
+ s->bcr1 = mem_value;
+ return;
+ case SH7750_BCR4_A7:
+ if(!has_bcr3_and_bcr4(s))
+ error_access("long write", addr);
+ s->bcr4 = mem_value;
+ return;
+ case SH7750_WCR1_A7:
+ case SH7750_WCR2_A7:
+ case SH7750_WCR3_A7:
+ case SH7750_MCR_A7:
+ ignore_access("long write", addr);
+ return;
+ /* IO ports */
+ case SH7750_PCTRA_A7:
+ temp = porta_lines(s);
+ s->pctra = mem_value;
+ s->portdira = portdir(mem_value);
+ s->portpullupa = portpullup(mem_value);
+ porta_changed(s, temp);
+ return;
+ case SH7750_PCTRB_A7:
+ temp = portb_lines(s);
+ s->pctrb = mem_value;
+ s->portdirb = portdir(mem_value);
+ s->portpullupb = portpullup(mem_value);
+ portb_changed(s, temp);
+ return;
+ case SH7750_MMUCR_A7:
+ if (mem_value & MMUCR_TI) {
+ cpu_sh4_invalidate_tlb(s->cpu);
+ }
+ s->cpu->mmucr = mem_value & ~MMUCR_TI;
+ return;
+ case SH7750_PTEH_A7:
+ /* If asid changes, clear all registered tlb entries. */
+ if ((s->cpu->pteh & 0xff) != (mem_value & 0xff))
+ tlb_flush(s->cpu, 1);
+ s->cpu->pteh = mem_value;
+ return;
+ case SH7750_PTEL_A7:
+ s->cpu->ptel = mem_value;
+ return;
+ case SH7750_PTEA_A7:
+ s->cpu->ptea = mem_value & 0x0000000f;
+ return;
+ case SH7750_TTB_A7:
+ s->cpu->ttb = mem_value;
+ return;
+ case SH7750_TEA_A7:
+ s->cpu->tea = mem_value;
+ return;
+ case SH7750_TRA_A7:
+ s->cpu->tra = mem_value & 0x000007ff;
+ return;
+ case SH7750_EXPEVT_A7:
+ s->cpu->expevt = mem_value & 0x000007ff;
+ return;
+ case SH7750_INTEVT_A7:
+ s->cpu->intevt = mem_value & 0x000007ff;
+ return;
+ case SH7750_CCR_A7:
+ s->ccr = mem_value;
+ return;
+ default:
+ error_access("long write", addr);
+ abort();
+ }
+}
+
+static CPUReadMemoryFunc * const sh7750_mem_read[] = {
+ sh7750_mem_readb,
+ sh7750_mem_readw,
+ sh7750_mem_readl
+};
+
+static CPUWriteMemoryFunc * const sh7750_mem_write[] = {
+ sh7750_mem_writeb,
+ sh7750_mem_writew,
+ sh7750_mem_writel
+};
+
+/* sh775x interrupt controller tables for sh_intc.c
+ * stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c
+ */
+
+enum {
+ UNUSED = 0,
+
+ /* interrupt sources */
+ IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, IRL_7,
+ IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E,
+ IRL0, IRL1, IRL2, IRL3,
+ HUDI, GPIOI,
+ DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3,
+ DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7,
+ DMAC_DMAE,
+ PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
+ PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3,
+ TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI,
+ RTC_ATI, RTC_PRI, RTC_CUI,
+ SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI,
+ SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI,
+ WDT,
+ REF_RCMI, REF_ROVI,
+
+ /* interrupt groups */
+ DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF,
+ /* irl bundle */
+ IRL,
+
+ NR_SOURCES,
+};
+
+static struct intc_vect vectors[] = {
+ INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620),
+ INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
+ INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460),
+ INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
+ INTC_VECT(RTC_CUI, 0x4c0),
+ INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500),
+ INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540),
+ INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720),
+ INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760),
+ INTC_VECT(WDT, 0x560),
+ INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0),
+};
+
+static struct intc_group groups[] = {
+ INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI),
+ INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
+ INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI),
+ INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI),
+ INTC_GROUP(REF, REF_RCMI, REF_ROVI),
+};
+
+static struct intc_prio_reg prio_registers[] = {
+ { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
+ { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } },
+ { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } },
+ { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } },
+ { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0,
+ TMU4, TMU3,
+ PCIC1, PCIC0_PCISERR } },
+};
+
+/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */
+
+static struct intc_vect vectors_dma4[] = {
+ INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
+ INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
+ INTC_VECT(DMAC_DMAE, 0x6c0),
+};
+
+static struct intc_group groups_dma4[] = {
+ INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
+ DMAC_DMTE3, DMAC_DMAE),
+};
+
+/* SH7750R and SH7751R both have 8-channel DMA controllers */
+
+static struct intc_vect vectors_dma8[] = {
+ INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
+ INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
+ INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0),
+ INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0),
+ INTC_VECT(DMAC_DMAE, 0x6c0),
+};
+
+static struct intc_group groups_dma8[] = {
+ INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
+ DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5,
+ DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE),
+};
+
+/* SH7750R, SH7751 and SH7751R all have two extra timer channels */
+
+static struct intc_vect vectors_tmu34[] = {
+ INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80),
+};
+
+static struct intc_mask_reg mask_registers[] = {
+ { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, TMU4, TMU3,
+ PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
+ PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2,
+ PCIC1_PCIDMA3, PCIC0_PCISERR } },
+};
+
+/* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */
+
+static struct intc_vect vectors_irlm[] = {
+ INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0),
+ INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360),
+};
+
+/* SH7751 and SH7751R both have PCI */
+
+static struct intc_vect vectors_pci[] = {
+ INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0),
+ INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0),
+ INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60),
+ INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20),
+};
+
+static struct intc_group groups_pci[] = {
+ INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
+ PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3),
+};
+
+static struct intc_vect vectors_irl[] = {
+ INTC_VECT(IRL_0, 0x200),
+ INTC_VECT(IRL_1, 0x220),
+ INTC_VECT(IRL_2, 0x240),
+ INTC_VECT(IRL_3, 0x260),
+ INTC_VECT(IRL_4, 0x280),
+ INTC_VECT(IRL_5, 0x2a0),
+ INTC_VECT(IRL_6, 0x2c0),
+ INTC_VECT(IRL_7, 0x2e0),
+ INTC_VECT(IRL_8, 0x300),
+ INTC_VECT(IRL_9, 0x320),
+ INTC_VECT(IRL_A, 0x340),
+ INTC_VECT(IRL_B, 0x360),
+ INTC_VECT(IRL_C, 0x380),
+ INTC_VECT(IRL_D, 0x3a0),
+ INTC_VECT(IRL_E, 0x3c0),
+};
+
+static struct intc_group groups_irl[] = {
+ INTC_GROUP(IRL, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6,
+ IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E),
+};
+
+/**********************************************************************
+ Memory mapped cache and TLB
+**********************************************************************/
+
+#define MM_REGION_MASK 0x07000000
+#define MM_ICACHE_ADDR (0)
+#define MM_ICACHE_DATA (1)
+#define MM_ITLB_ADDR (2)
+#define MM_ITLB_DATA (3)
+#define MM_OCACHE_ADDR (4)
+#define MM_OCACHE_DATA (5)
+#define MM_UTLB_ADDR (6)
+#define MM_UTLB_DATA (7)
+#define MM_REGION_TYPE(addr) ((addr & MM_REGION_MASK) >> 24)
+
+static uint32_t invalid_read(void *opaque, target_phys_addr_t addr)
+{
+ abort();
+
+ return 0;
+}
+
+static uint32_t sh7750_mmct_readl(void *opaque, target_phys_addr_t addr)
+{
+ SH7750State *s = opaque;
+ uint32_t ret = 0;
+
+ switch (MM_REGION_TYPE(addr)) {
+ case MM_ICACHE_ADDR:
+ case MM_ICACHE_DATA:
+ /* do nothing */
+ break;
+ case MM_ITLB_ADDR:
+ ret = cpu_sh4_read_mmaped_itlb_addr(s->cpu, addr);
+ break;
+ case MM_ITLB_DATA:
+ ret = cpu_sh4_read_mmaped_itlb_data(s->cpu, addr);
+ break;
+ case MM_OCACHE_ADDR:
+ case MM_OCACHE_DATA:
+ /* do nothing */
+ break;
+ case MM_UTLB_ADDR:
+ ret = cpu_sh4_read_mmaped_utlb_addr(s->cpu, addr);
+ break;
+ case MM_UTLB_DATA:
+ ret = cpu_sh4_read_mmaped_utlb_data(s->cpu, addr);
+ break;
+ default:
+ abort();
+ }
+
+ return ret;
+}
+
+static void invalid_write(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ abort();
+}
+
+static void sh7750_mmct_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ SH7750State *s = opaque;
+
+ switch (MM_REGION_TYPE(addr)) {
+ case MM_ICACHE_ADDR:
+ case MM_ICACHE_DATA:
+ /* do nothing */
+ break;
+ case MM_ITLB_ADDR:
+ cpu_sh4_write_mmaped_itlb_addr(s->cpu, addr, mem_value);
+ break;
+ case MM_ITLB_DATA:
+ cpu_sh4_write_mmaped_itlb_data(s->cpu, addr, mem_value);
+ abort();
+ break;
+ case MM_OCACHE_ADDR:
+ case MM_OCACHE_DATA:
+ /* do nothing */
+ break;
+ case MM_UTLB_ADDR:
+ cpu_sh4_write_mmaped_utlb_addr(s->cpu, addr, mem_value);
+ break;
+ case MM_UTLB_DATA:
+ cpu_sh4_write_mmaped_utlb_data(s->cpu, addr, mem_value);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const sh7750_mmct_read[] = {
+ invalid_read,
+ invalid_read,
+ sh7750_mmct_readl
+};
+
+static CPUWriteMemoryFunc * const sh7750_mmct_write[] = {
+ invalid_write,
+ invalid_write,
+ sh7750_mmct_writel
+};
+
+SH7750State *sh7750_init(CPUSH4State * cpu)
+{
+ SH7750State *s;
+ int sh7750_io_memory;
+ int sh7750_mm_cache_and_tlb; /* memory mapped cache and tlb */
+
+ s = qemu_mallocz(sizeof(SH7750State));
+ s->cpu = cpu;
+ s->periph_freq = 60000000; /* 60MHz */
+ sh7750_io_memory = cpu_register_io_memory(sh7750_mem_read,
+ sh7750_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory_offset(0x1f000000, 0x1000,
+ sh7750_io_memory, 0x1f000000);
+ cpu_register_physical_memory_offset(0xff000000, 0x1000,
+ sh7750_io_memory, 0x1f000000);
+ cpu_register_physical_memory_offset(0x1f800000, 0x1000,
+ sh7750_io_memory, 0x1f800000);
+ cpu_register_physical_memory_offset(0xff800000, 0x1000,
+ sh7750_io_memory, 0x1f800000);
+ cpu_register_physical_memory_offset(0x1fc00000, 0x1000,
+ sh7750_io_memory, 0x1fc00000);
+ cpu_register_physical_memory_offset(0xffc00000, 0x1000,
+ sh7750_io_memory, 0x1fc00000);
+
+ sh7750_mm_cache_and_tlb = cpu_register_io_memory(sh7750_mmct_read,
+ sh7750_mmct_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0xf0000000, 0x08000000,
+ sh7750_mm_cache_and_tlb);
+
+ sh_intc_init(&s->intc, NR_SOURCES,
+ _INTC_ARRAY(mask_registers),
+ _INTC_ARRAY(prio_registers));
+
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors),
+ _INTC_ARRAY(groups));
+
+ cpu->intc_handle = &s->intc;
+
+ sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0],
+ s->intc.irqs[SCI1_ERI],
+ s->intc.irqs[SCI1_RXI],
+ s->intc.irqs[SCI1_TXI],
+ s->intc.irqs[SCI1_TEI],
+ NULL);
+ sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF,
+ s->periph_freq, serial_hds[1],
+ s->intc.irqs[SCIF_ERI],
+ s->intc.irqs[SCIF_RXI],
+ s->intc.irqs[SCIF_TXI],
+ NULL,
+ s->intc.irqs[SCIF_BRI]);
+
+ tmu012_init(0x1fd80000,
+ TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK,
+ s->periph_freq,
+ s->intc.irqs[TMU0],
+ s->intc.irqs[TMU1],
+ s->intc.irqs[TMU2_TUNI],
+ s->intc.irqs[TMU2_TICPI]);
+
+ if (cpu->id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) {
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors_dma4),
+ _INTC_ARRAY(groups_dma4));
+ }
+
+ if (cpu->id & (SH_CPU_SH7750R | SH_CPU_SH7751R)) {
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors_dma8),
+ _INTC_ARRAY(groups_dma8));
+ }
+
+ if (cpu->id & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) {
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors_tmu34),
+ NULL, 0);
+ tmu012_init(0x1e100000, 0, s->periph_freq,
+ s->intc.irqs[TMU3],
+ s->intc.irqs[TMU4],
+ NULL, NULL);
+ }
+
+ if (cpu->id & (SH_CPU_SH7751_ALL)) {
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors_pci),
+ _INTC_ARRAY(groups_pci));
+ }
+
+ if (cpu->id & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) {
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors_irlm),
+ NULL, 0);
+ }
+
+ sh_intc_register_sources(&s->intc,
+ _INTC_ARRAY(vectors_irl),
+ _INTC_ARRAY(groups_irl));
+ return s;
+}
+
+qemu_irq sh7750_irl(SH7750State *s)
+{
+ sh_intc_toggle_source(sh_intc_source(&s->intc, IRL), 1, 0); /* enable */
+ return qemu_allocate_irqs(sh_intc_set_irl, sh_intc_source(&s->intc, IRL),
+ 1)[0];
+}
diff --git a/hw/sh7750_regnames.c b/hw/sh7750_regnames.c
new file mode 100644
index 0000000..5a5a2d8
--- /dev/null
+++ b/hw/sh7750_regnames.c
@@ -0,0 +1,97 @@
+#include "hw.h"
+#include "sh.h"
+#include "sh7750_regs.h"
+#include "sh7750_regnames.h"
+
+#define REGNAME(r) {r, #r},
+
+typedef struct {
+ uint32_t regaddr;
+ const char *regname;
+} regname_t;
+
+static regname_t regnames[] = {
+ REGNAME(SH7750_PTEH_A7)
+ REGNAME(SH7750_PTEL_A7)
+ REGNAME(SH7750_PTEA_A7)
+ REGNAME(SH7750_TTB_A7)
+ REGNAME(SH7750_TEA_A7)
+ REGNAME(SH7750_MMUCR_A7)
+ REGNAME(SH7750_CCR_A7)
+ REGNAME(SH7750_QACR0_A7)
+ REGNAME(SH7750_QACR1_A7)
+ REGNAME(SH7750_TRA_A7)
+ REGNAME(SH7750_EXPEVT_A7)
+ REGNAME(SH7750_INTEVT_A7)
+ REGNAME(SH7750_STBCR_A7)
+ REGNAME(SH7750_STBCR2_A7)
+ REGNAME(SH7750_FRQCR_A7)
+ REGNAME(SH7750_WTCNT_A7)
+ REGNAME(SH7750_WTCSR_A7)
+ REGNAME(SH7750_R64CNT_A7)
+ REGNAME(SH7750_RSECCNT_A7)
+ REGNAME(SH7750_RMINCNT_A7)
+ REGNAME(SH7750_RHRCNT_A7)
+ REGNAME(SH7750_RWKCNT_A7)
+ REGNAME(SH7750_RDAYCNT_A7)
+ REGNAME(SH7750_RMONCNT_A7)
+ REGNAME(SH7750_RYRCNT_A7)
+ REGNAME(SH7750_RSECAR_A7)
+ REGNAME(SH7750_RMINAR_A7)
+ REGNAME(SH7750_RHRAR_A7)
+ REGNAME(SH7750_RWKAR_A7)
+ REGNAME(SH7750_RDAYAR_A7)
+ REGNAME(SH7750_RMONAR_A7)
+ REGNAME(SH7750_RCR1_A7)
+ REGNAME(SH7750_RCR2_A7)
+ REGNAME(SH7750_BCR1_A7)
+ REGNAME(SH7750_BCR2_A7)
+ REGNAME(SH7750_WCR1_A7)
+ REGNAME(SH7750_WCR2_A7)
+ REGNAME(SH7750_WCR3_A7)
+ REGNAME(SH7750_MCR_A7)
+ REGNAME(SH7750_PCR_A7)
+ REGNAME(SH7750_RTCSR_A7)
+ REGNAME(SH7750_RTCNT_A7)
+ REGNAME(SH7750_RTCOR_A7)
+ REGNAME(SH7750_RFCR_A7)
+ REGNAME(SH7750_SAR0_A7)
+ REGNAME(SH7750_SAR1_A7)
+ REGNAME(SH7750_SAR2_A7)
+ REGNAME(SH7750_SAR3_A7)
+ REGNAME(SH7750_DAR0_A7)
+ REGNAME(SH7750_DAR1_A7)
+ REGNAME(SH7750_DAR2_A7)
+ REGNAME(SH7750_DAR3_A7)
+ REGNAME(SH7750_DMATCR0_A7)
+ REGNAME(SH7750_DMATCR1_A7)
+ REGNAME(SH7750_DMATCR2_A7)
+ REGNAME(SH7750_DMATCR3_A7)
+ REGNAME(SH7750_CHCR0_A7)
+ REGNAME(SH7750_CHCR1_A7)
+ REGNAME(SH7750_CHCR2_A7)
+ REGNAME(SH7750_CHCR3_A7)
+ REGNAME(SH7750_DMAOR_A7)
+ REGNAME(SH7750_PCTRA_A7)
+ REGNAME(SH7750_PDTRA_A7)
+ REGNAME(SH7750_PCTRB_A7)
+ REGNAME(SH7750_PDTRB_A7)
+ REGNAME(SH7750_GPIOIC_A7)
+ REGNAME(SH7750_ICR_A7)
+ REGNAME(SH7750_BCR3_A7)
+ REGNAME(SH7750_BCR4_A7)
+ REGNAME(SH7750_SDMR2_A7)
+ REGNAME(SH7750_SDMR3_A7) {(uint32_t) - 1, NULL}
+};
+
+const char *regname(uint32_t addr)
+{
+ unsigned int i;
+
+ for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) {
+ if (regnames[i].regaddr == addr)
+ return regnames[i].regname;
+ }
+
+ return "<unknown reg>";
+}
diff --git a/hw/sh7750_regnames.h b/hw/sh7750_regnames.h
new file mode 100644
index 0000000..7463709
--- /dev/null
+++ b/hw/sh7750_regnames.h
@@ -0,0 +1,6 @@
+#ifndef _SH7750_REGNAMES_H
+#define _SH7750_REGNAMES_H
+
+const char *regname(uint32_t addr);
+
+#endif /* _SH7750_REGNAMES_H */
diff --git a/hw/sh7750_regs.h b/hw/sh7750_regs.h
new file mode 100644
index 0000000..5a23a2c
--- /dev/null
+++ b/hw/sh7750_regs.h
@@ -0,0 +1,1277 @@
+/*
+ * SH-7750 memory-mapped registers
+ * This file based on information provided in the following document:
+ * "Hitachi SuperH (tm) RISC engine. SH7750 Series (SH7750, SH7750S)
+ * Hardware Manual"
+ * Document Number ADE-602-124C, Rev. 4.0, 4/21/00, Hitachi Ltd.
+ *
+ * Copyright (C) 2001 OKTET Ltd., St.-Petersburg, Russia
+ * Author: Alexandra Kossovsky <sasha@oktet.ru>
+ * Victor V. Vengerov <vvv@oktet.ru>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ *
+ * @(#) sh7750_regs.h,v 1.2.4.1 2003/09/04 18:46:00 joel Exp
+ */
+
+#ifndef __SH7750_REGS_H__
+#define __SH7750_REGS_H__
+
+/*
+ * All register has 2 addresses: in 0xff000000 - 0xffffffff (P4 address) and
+ * in 0x1f000000 - 0x1fffffff (area 7 address)
+ */
+#define SH7750_P4_BASE 0xff000000 /* Accessable only in
+ priveleged mode */
+#define SH7750_A7_BASE 0x1f000000 /* Accessable only using TLB */
+
+#define SH7750_P4_REG32(ofs) (SH7750_P4_BASE + (ofs))
+#define SH7750_A7_REG32(ofs) (SH7750_A7_BASE + (ofs))
+
+/*
+ * MMU Registers
+ */
+
+/* Page Table Entry High register - PTEH */
+#define SH7750_PTEH_REGOFS 0x000000 /* offset */
+#define SH7750_PTEH SH7750_P4_REG32(SH7750_PTEH_REGOFS)
+#define SH7750_PTEH_A7 SH7750_A7_REG32(SH7750_PTEH_REGOFS)
+#define SH7750_PTEH_VPN 0xfffffd00 /* Virtual page number */
+#define SH7750_PTEH_VPN_S 10
+#define SH7750_PTEH_ASID 0x000000ff /* Address space identifier */
+#define SH7750_PTEH_ASID_S 0
+
+/* Page Table Entry Low register - PTEL */
+#define SH7750_PTEL_REGOFS 0x000004 /* offset */
+#define SH7750_PTEL SH7750_P4_REG32(SH7750_PTEL_REGOFS)
+#define SH7750_PTEL_A7 SH7750_A7_REG32(SH7750_PTEL_REGOFS)
+#define SH7750_PTEL_PPN 0x1ffffc00 /* Physical page number */
+#define SH7750_PTEL_PPN_S 10
+#define SH7750_PTEL_V 0x00000100 /* Validity (0-entry is invalid) */
+#define SH7750_PTEL_SZ1 0x00000080 /* Page size bit 1 */
+#define SH7750_PTEL_SZ0 0x00000010 /* Page size bit 0 */
+#define SH7750_PTEL_SZ_1KB 0x00000000 /* 1-kbyte page */
+#define SH7750_PTEL_SZ_4KB 0x00000010 /* 4-kbyte page */
+#define SH7750_PTEL_SZ_64KB 0x00000080 /* 64-kbyte page */
+#define SH7750_PTEL_SZ_1MB 0x00000090 /* 1-Mbyte page */
+#define SH7750_PTEL_PR 0x00000060 /* Protection Key Data */
+#define SH7750_PTEL_PR_ROPO 0x00000000 /* read-only in priv mode */
+#define SH7750_PTEL_PR_RWPO 0x00000020 /* read-write in priv mode */
+#define SH7750_PTEL_PR_ROPU 0x00000040 /* read-only in priv or user mode */
+#define SH7750_PTEL_PR_RWPU 0x00000060 /* read-write in priv or user mode */
+#define SH7750_PTEL_C 0x00000008 /* Cacheability
+ (0 - page not cacheable) */
+#define SH7750_PTEL_D 0x00000004 /* Dirty bit (1 - write has been
+ performed to a page) */
+#define SH7750_PTEL_SH 0x00000002 /* Share Status bit (1 - page are
+ shared by processes) */
+#define SH7750_PTEL_WT 0x00000001 /* Write-through bit, specifies the
+ cache write mode:
+ 0 - Copy-back mode
+ 1 - Write-through mode */
+
+/* Page Table Entry Assistance register - PTEA */
+#define SH7750_PTEA_REGOFS 0x000034 /* offset */
+#define SH7750_PTEA SH7750_P4_REG32(SH7750_PTEA_REGOFS)
+#define SH7750_PTEA_A7 SH7750_A7_REG32(SH7750_PTEA_REGOFS)
+#define SH7750_PTEA_TC 0x00000008 /* Timing Control bit
+ 0 - use area 5 wait states
+ 1 - use area 6 wait states */
+#define SH7750_PTEA_SA 0x00000007 /* Space Attribute bits: */
+#define SH7750_PTEA_SA_UNDEF 0x00000000 /* 0 - undefined */
+#define SH7750_PTEA_SA_IOVAR 0x00000001 /* 1 - variable-size I/O space */
+#define SH7750_PTEA_SA_IO8 0x00000002 /* 2 - 8-bit I/O space */
+#define SH7750_PTEA_SA_IO16 0x00000003 /* 3 - 16-bit I/O space */
+#define SH7750_PTEA_SA_CMEM8 0x00000004 /* 4 - 8-bit common memory space */
+#define SH7750_PTEA_SA_CMEM16 0x00000005 /* 5 - 16-bit common memory space */
+#define SH7750_PTEA_SA_AMEM8 0x00000006 /* 6 - 8-bit attr memory space */
+#define SH7750_PTEA_SA_AMEM16 0x00000007 /* 7 - 16-bit attr memory space */
+
+
+/* Translation table base register */
+#define SH7750_TTB_REGOFS 0x000008 /* offset */
+#define SH7750_TTB SH7750_P4_REG32(SH7750_TTB_REGOFS)
+#define SH7750_TTB_A7 SH7750_A7_REG32(SH7750_TTB_REGOFS)
+
+/* TLB exeption address register - TEA */
+#define SH7750_TEA_REGOFS 0x00000c /* offset */
+#define SH7750_TEA SH7750_P4_REG32(SH7750_TEA_REGOFS)
+#define SH7750_TEA_A7 SH7750_A7_REG32(SH7750_TEA_REGOFS)
+
+/* MMU control register - MMUCR */
+#define SH7750_MMUCR_REGOFS 0x000010 /* offset */
+#define SH7750_MMUCR SH7750_P4_REG32(SH7750_MMUCR_REGOFS)
+#define SH7750_MMUCR_A7 SH7750_A7_REG32(SH7750_MMUCR_REGOFS)
+#define SH7750_MMUCR_AT 0x00000001 /* Address translation bit */
+#define SH7750_MMUCR_TI 0x00000004 /* TLB invalidate */
+#define SH7750_MMUCR_SV 0x00000100 /* Single Virtual Mode bit */
+#define SH7750_MMUCR_SQMD 0x00000200 /* Store Queue Mode bit */
+#define SH7750_MMUCR_URC 0x0000FC00 /* UTLB Replace Counter */
+#define SH7750_MMUCR_URC_S 10
+#define SH7750_MMUCR_URB 0x00FC0000 /* UTLB Replace Boundary */
+#define SH7750_MMUCR_URB_S 18
+#define SH7750_MMUCR_LRUI 0xFC000000 /* Least Recently Used ITLB */
+#define SH7750_MMUCR_LRUI_S 26
+
+
+
+
+/*
+ * Cache registers
+ * IC -- instructions cache
+ * OC -- operand cache
+ */
+
+/* Cache Control Register - CCR */
+#define SH7750_CCR_REGOFS 0x00001c /* offset */
+#define SH7750_CCR SH7750_P4_REG32(SH7750_CCR_REGOFS)
+#define SH7750_CCR_A7 SH7750_A7_REG32(SH7750_CCR_REGOFS)
+
+#define SH7750_CCR_IIX 0x00008000 /* IC index enable bit */
+#define SH7750_CCR_ICI 0x00000800 /* IC invalidation bit:
+ set it to clear IC */
+#define SH7750_CCR_ICE 0x00000100 /* IC enable bit */
+#define SH7750_CCR_OIX 0x00000080 /* OC index enable bit */
+#define SH7750_CCR_ORA 0x00000020 /* OC RAM enable bit
+ if you set OCE = 0,
+ you should set ORA = 0 */
+#define SH7750_CCR_OCI 0x00000008 /* OC invalidation bit */
+#define SH7750_CCR_CB 0x00000004 /* Copy-back bit for P1 area */
+#define SH7750_CCR_WT 0x00000002 /* Write-through bit for P0,U0,P3 area */
+#define SH7750_CCR_OCE 0x00000001 /* OC enable bit */
+
+/* Queue address control register 0 - QACR0 */
+#define SH7750_QACR0_REGOFS 0x000038 /* offset */
+#define SH7750_QACR0 SH7750_P4_REG32(SH7750_QACR0_REGOFS)
+#define SH7750_QACR0_A7 SH7750_A7_REG32(SH7750_QACR0_REGOFS)
+
+/* Queue address control register 1 - QACR1 */
+#define SH7750_QACR1_REGOFS 0x00003c /* offset */
+#define SH7750_QACR1 SH7750_P4_REG32(SH7750_QACR1_REGOFS)
+#define SH7750_QACR1_A7 SH7750_A7_REG32(SH7750_QACR1_REGOFS)
+
+
+/*
+ * Exeption-related registers
+ */
+
+/* Immediate data for TRAPA instuction - TRA */
+#define SH7750_TRA_REGOFS 0x000020 /* offset */
+#define SH7750_TRA SH7750_P4_REG32(SH7750_TRA_REGOFS)
+#define SH7750_TRA_A7 SH7750_A7_REG32(SH7750_TRA_REGOFS)
+
+#define SH7750_TRA_IMM 0x000003fd /* Immediate data operand */
+#define SH7750_TRA_IMM_S 2
+
+/* Exeption event register - EXPEVT */
+#define SH7750_EXPEVT_REGOFS 0x000024
+#define SH7750_EXPEVT SH7750_P4_REG32(SH7750_EXPEVT_REGOFS)
+#define SH7750_EXPEVT_A7 SH7750_A7_REG32(SH7750_EXPEVT_REGOFS)
+
+#define SH7750_EXPEVT_EX 0x00000fff /* Exeption code */
+#define SH7750_EXPEVT_EX_S 0
+
+/* Interrupt event register */
+#define SH7750_INTEVT_REGOFS 0x000028
+#define SH7750_INTEVT SH7750_P4_REG32(SH7750_INTEVT_REGOFS)
+#define SH7750_INTEVT_A7 SH7750_A7_REG32(SH7750_INTEVT_REGOFS)
+#define SH7750_INTEVT_EX 0x00000fff /* Exeption code */
+#define SH7750_INTEVT_EX_S 0
+
+/*
+ * Exception/interrupt codes
+ */
+#define SH7750_EVT_TO_NUM(evt) ((evt) >> 5)
+
+/* Reset exception category */
+#define SH7750_EVT_POWER_ON_RST 0x000 /* Power-on reset */
+#define SH7750_EVT_MANUAL_RST 0x020 /* Manual reset */
+#define SH7750_EVT_TLB_MULT_HIT 0x140 /* TLB multiple-hit exception */
+
+/* General exception category */
+#define SH7750_EVT_USER_BREAK 0x1E0 /* User break */
+#define SH7750_EVT_IADDR_ERR 0x0E0 /* Instruction address error */
+#define SH7750_EVT_TLB_READ_MISS 0x040 /* ITLB miss exception /
+ DTLB miss exception (read) */
+#define SH7750_EVT_TLB_READ_PROTV 0x0A0 /* ITLB protection violation /
+ DTLB protection violation (read) */
+#define SH7750_EVT_ILLEGAL_INSTR 0x180 /* General Illegal Instruction
+ exception */
+#define SH7750_EVT_SLOT_ILLEGAL_INSTR 0x1A0 /* Slot Illegal Instruction
+ exception */
+#define SH7750_EVT_FPU_DISABLE 0x800 /* General FPU disable exception */
+#define SH7750_EVT_SLOT_FPU_DISABLE 0x820 /* Slot FPU disable exception */
+#define SH7750_EVT_DATA_READ_ERR 0x0E0 /* Data address error (read) */
+#define SH7750_EVT_DATA_WRITE_ERR 0x100 /* Data address error (write) */
+#define SH7750_EVT_DTLB_WRITE_MISS 0x060 /* DTLB miss exception (write) */
+#define SH7750_EVT_DTLB_WRITE_PROTV 0x0C0 /* DTLB protection violation
+ exception (write) */
+#define SH7750_EVT_FPU_EXCEPTION 0x120 /* FPU exception */
+#define SH7750_EVT_INITIAL_PGWRITE 0x080 /* Initial Page Write exception */
+#define SH7750_EVT_TRAPA 0x160 /* Unconditional trap (TRAPA) */
+
+/* Interrupt exception category */
+#define SH7750_EVT_NMI 0x1C0 /* Non-maskable interrupt */
+#define SH7750_EVT_IRQ0 0x200 /* External Interrupt 0 */
+#define SH7750_EVT_IRQ1 0x220 /* External Interrupt 1 */
+#define SH7750_EVT_IRQ2 0x240 /* External Interrupt 2 */
+#define SH7750_EVT_IRQ3 0x260 /* External Interrupt 3 */
+#define SH7750_EVT_IRQ4 0x280 /* External Interrupt 4 */
+#define SH7750_EVT_IRQ5 0x2A0 /* External Interrupt 5 */
+#define SH7750_EVT_IRQ6 0x2C0 /* External Interrupt 6 */
+#define SH7750_EVT_IRQ7 0x2E0 /* External Interrupt 7 */
+#define SH7750_EVT_IRQ8 0x300 /* External Interrupt 8 */
+#define SH7750_EVT_IRQ9 0x320 /* External Interrupt 9 */
+#define SH7750_EVT_IRQA 0x340 /* External Interrupt A */
+#define SH7750_EVT_IRQB 0x360 /* External Interrupt B */
+#define SH7750_EVT_IRQC 0x380 /* External Interrupt C */
+#define SH7750_EVT_IRQD 0x3A0 /* External Interrupt D */
+#define SH7750_EVT_IRQE 0x3C0 /* External Interrupt E */
+
+/* Peripheral Module Interrupts - Timer Unit (TMU) */
+#define SH7750_EVT_TUNI0 0x400 /* TMU Underflow Interrupt 0 */
+#define SH7750_EVT_TUNI1 0x420 /* TMU Underflow Interrupt 1 */
+#define SH7750_EVT_TUNI2 0x440 /* TMU Underflow Interrupt 2 */
+#define SH7750_EVT_TICPI2 0x460 /* TMU Input Capture Interrupt 2 */
+
+/* Peripheral Module Interrupts - Real-Time Clock (RTC) */
+#define SH7750_EVT_RTC_ATI 0x480 /* Alarm Interrupt Request */
+#define SH7750_EVT_RTC_PRI 0x4A0 /* Periodic Interrupt Request */
+#define SH7750_EVT_RTC_CUI 0x4C0 /* Carry Interrupt Request */
+
+/* Peripheral Module Interrupts - Serial Communication Interface (SCI) */
+#define SH7750_EVT_SCI_ERI 0x4E0 /* Receive Error */
+#define SH7750_EVT_SCI_RXI 0x500 /* Receive Data Register Full */
+#define SH7750_EVT_SCI_TXI 0x520 /* Transmit Data Register Empty */
+#define SH7750_EVT_SCI_TEI 0x540 /* Transmit End */
+
+/* Peripheral Module Interrupts - Watchdog Timer (WDT) */
+#define SH7750_EVT_WDT_ITI 0x560 /* Interval Timer Interrupt
+ (used when WDT operates in
+ interval timer mode) */
+
+/* Peripheral Module Interrupts - Memory Refresh Unit (REF) */
+#define SH7750_EVT_REF_RCMI 0x580 /* Compare-match Interrupt */
+#define SH7750_EVT_REF_ROVI 0x5A0 /* Refresh Counter Overflow
+ interrupt */
+
+/* Peripheral Module Interrupts - Hitachi User Debug Interface (H-UDI) */
+#define SH7750_EVT_HUDI 0x600 /* UDI interrupt */
+
+/* Peripheral Module Interrupts - General-Purpose I/O (GPIO) */
+#define SH7750_EVT_GPIO 0x620 /* GPIO Interrupt */
+
+/* Peripheral Module Interrupts - DMA Controller (DMAC) */
+#define SH7750_EVT_DMAC_DMTE0 0x640 /* DMAC 0 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMTE1 0x660 /* DMAC 1 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMTE2 0x680 /* DMAC 2 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMTE3 0x6A0 /* DMAC 3 Transfer End Interrupt */
+#define SH7750_EVT_DMAC_DMAE 0x6C0 /* DMAC Address Error Interrupt */
+
+/* Peripheral Module Interrupts - Serial Communication Interface with FIFO */
+/* (SCIF) */
+#define SH7750_EVT_SCIF_ERI 0x700 /* Receive Error */
+#define SH7750_EVT_SCIF_RXI 0x720 /* Receive FIFO Data Full or
+ Receive Data ready interrupt */
+#define SH7750_EVT_SCIF_BRI 0x740 /* Break or overrun error */
+#define SH7750_EVT_SCIF_TXI 0x760 /* Transmit FIFO Data Empty */
+
+/*
+ * Power Management
+ */
+#define SH7750_STBCR_REGOFS 0xC00004 /* offset */
+#define SH7750_STBCR SH7750_P4_REG32(SH7750_STBCR_REGOFS)
+#define SH7750_STBCR_A7 SH7750_A7_REG32(SH7750_STBCR_REGOFS)
+
+#define SH7750_STBCR_STBY 0x80 /* Specifies a transition to standby mode:
+ 0 - Transition to SLEEP mode on SLEEP
+ 1 - Transition to STANDBY mode on SLEEP */
+#define SH7750_STBCR_PHZ 0x40 /* State of peripheral module pins in
+ standby mode:
+ 0 - normal state
+ 1 - high-impendance state */
+
+#define SH7750_STBCR_PPU 0x20 /* Peripheral module pins pull-up controls */
+#define SH7750_STBCR_MSTP4 0x10 /* Stopping the clock supply to DMAC */
+#define SH7750_STBCR_DMAC_STP SH7750_STBCR_MSTP4
+#define SH7750_STBCR_MSTP3 0x08 /* Stopping the clock supply to SCIF */
+#define SH7750_STBCR_SCIF_STP SH7750_STBCR_MSTP3
+#define SH7750_STBCR_MSTP2 0x04 /* Stopping the clock supply to TMU */
+#define SH7750_STBCR_TMU_STP SH7750_STBCR_MSTP2
+#define SH7750_STBCR_MSTP1 0x02 /* Stopping the clock supply to RTC */
+#define SH7750_STBCR_RTC_STP SH7750_STBCR_MSTP1
+#define SH7750_STBCR_MSPT0 0x01 /* Stopping the clock supply to SCI */
+#define SH7750_STBCR_SCI_STP SH7750_STBCR_MSTP0
+
+#define SH7750_STBCR_STBY 0x80
+
+
+#define SH7750_STBCR2_REGOFS 0xC00010 /* offset */
+#define SH7750_STBCR2 SH7750_P4_REG32(SH7750_STBCR2_REGOFS)
+#define SH7750_STBCR2_A7 SH7750_A7_REG32(SH7750_STBCR2_REGOFS)
+
+#define SH7750_STBCR2_DSLP 0x80 /* Specifies transition to deep sleep mode:
+ 0 - transition to sleep or standby mode
+ as it is specified in STBY bit
+ 1 - transition to deep sleep mode on
+ execution of SLEEP instruction */
+#define SH7750_STBCR2_MSTP6 0x02 /* Stopping the clock supply to Store Queue
+ in the cache controller */
+#define SH7750_STBCR2_SQ_STP SH7750_STBCR2_MSTP6
+#define SH7750_STBCR2_MSTP5 0x01 /* Stopping the clock supply to the User
+ Break Controller (UBC) */
+#define SH7750_STBCR2_UBC_STP SH7750_STBCR2_MSTP5
+
+/*
+ * Clock Pulse Generator (CPG)
+ */
+#define SH7750_FRQCR_REGOFS 0xC00000 /* offset */
+#define SH7750_FRQCR SH7750_P4_REG32(SH7750_FRQCR_REGOFS)
+#define SH7750_FRQCR_A7 SH7750_A7_REG32(SH7750_FRQCR_REGOFS)
+
+#define SH7750_FRQCR_CKOEN 0x0800 /* Clock Output Enable
+ 0 - CKIO pin goes to HiZ/pullup
+ 1 - Clock is output from CKIO */
+#define SH7750_FRQCR_PLL1EN 0x0400 /* PLL circuit 1 enable */
+#define SH7750_FRQCR_PLL2EN 0x0200 /* PLL circuit 2 enable */
+
+#define SH7750_FRQCR_IFC 0x01C0 /* CPU clock frequency division ratio: */
+#define SH7750_FRQCR_IFCDIV1 0x0000 /* 0 - * 1 */
+#define SH7750_FRQCR_IFCDIV2 0x0040 /* 1 - * 1/2 */
+#define SH7750_FRQCR_IFCDIV3 0x0080 /* 2 - * 1/3 */
+#define SH7750_FRQCR_IFCDIV4 0x00C0 /* 3 - * 1/4 */
+#define SH7750_FRQCR_IFCDIV6 0x0100 /* 4 - * 1/6 */
+#define SH7750_FRQCR_IFCDIV8 0x0140 /* 5 - * 1/8 */
+
+#define SH7750_FRQCR_BFC 0x0038 /* Bus clock frequency division ratio: */
+#define SH7750_FRQCR_BFCDIV1 0x0000 /* 0 - * 1 */
+#define SH7750_FRQCR_BFCDIV2 0x0008 /* 1 - * 1/2 */
+#define SH7750_FRQCR_BFCDIV3 0x0010 /* 2 - * 1/3 */
+#define SH7750_FRQCR_BFCDIV4 0x0018 /* 3 - * 1/4 */
+#define SH7750_FRQCR_BFCDIV6 0x0020 /* 4 - * 1/6 */
+#define SH7750_FRQCR_BFCDIV8 0x0028 /* 5 - * 1/8 */
+
+#define SH7750_FRQCR_PFC 0x0007 /* Peripheral module clock frequency
+ division ratio: */
+#define SH7750_FRQCR_PFCDIV2 0x0000 /* 0 - * 1/2 */
+#define SH7750_FRQCR_PFCDIV3 0x0001 /* 1 - * 1/3 */
+#define SH7750_FRQCR_PFCDIV4 0x0002 /* 2 - * 1/4 */
+#define SH7750_FRQCR_PFCDIV6 0x0003 /* 3 - * 1/6 */
+#define SH7750_FRQCR_PFCDIV8 0x0004 /* 4 - * 1/8 */
+
+/*
+ * Watchdog Timer (WDT)
+ */
+
+/* Watchdog Timer Counter register - WTCNT */
+#define SH7750_WTCNT_REGOFS 0xC00008 /* offset */
+#define SH7750_WTCNT SH7750_P4_REG32(SH7750_WTCNT_REGOFS)
+#define SH7750_WTCNT_A7 SH7750_A7_REG32(SH7750_WTCNT_REGOFS)
+#define SH7750_WTCNT_KEY 0x5A00 /* When WTCNT byte register written,
+ you have to set the upper byte to
+ 0x5A */
+
+/* Watchdog Timer Control/Status register - WTCSR */
+#define SH7750_WTCSR_REGOFS 0xC0000C /* offset */
+#define SH7750_WTCSR SH7750_P4_REG32(SH7750_WTCSR_REGOFS)
+#define SH7750_WTCSR_A7 SH7750_A7_REG32(SH7750_WTCSR_REGOFS)
+#define SH7750_WTCSR_KEY 0xA500 /* When WTCSR byte register written,
+ you have to set the upper byte to
+ 0xA5 */
+#define SH7750_WTCSR_TME 0x80 /* Timer enable (1-upcount start) */
+#define SH7750_WTCSR_MODE 0x40 /* Timer Mode Select: */
+#define SH7750_WTCSR_MODE_WT 0x40 /* Watchdog Timer Mode */
+#define SH7750_WTCSR_MODE_IT 0x00 /* Interval Timer Mode */
+#define SH7750_WTCSR_RSTS 0x20 /* Reset Select: */
+#define SH7750_WTCSR_RST_MAN 0x20 /* Manual Reset */
+#define SH7750_WTCSR_RST_PWR 0x00 /* Power-on Reset */
+#define SH7750_WTCSR_WOVF 0x10 /* Watchdog Timer Overflow Flag */
+#define SH7750_WTCSR_IOVF 0x08 /* Interval Timer Overflow Flag */
+#define SH7750_WTCSR_CKS 0x07 /* Clock Select: */
+#define SH7750_WTCSR_CKS_DIV32 0x00 /* 1/32 of frequency divider 2 input */
+#define SH7750_WTCSR_CKS_DIV64 0x01 /* 1/64 */
+#define SH7750_WTCSR_CKS_DIV128 0x02 /* 1/128 */
+#define SH7750_WTCSR_CKS_DIV256 0x03 /* 1/256 */
+#define SH7750_WTCSR_CKS_DIV512 0x04 /* 1/512 */
+#define SH7750_WTCSR_CKS_DIV1024 0x05 /* 1/1024 */
+#define SH7750_WTCSR_CKS_DIV2048 0x06 /* 1/2048 */
+#define SH7750_WTCSR_CKS_DIV4096 0x07 /* 1/4096 */
+
+/*
+ * Real-Time Clock (RTC)
+ */
+/* 64-Hz Counter Register (byte, read-only) - R64CNT */
+#define SH7750_R64CNT_REGOFS 0xC80000 /* offset */
+#define SH7750_R64CNT SH7750_P4_REG32(SH7750_R64CNT_REGOFS)
+#define SH7750_R64CNT_A7 SH7750_A7_REG32(SH7750_R64CNT_REGOFS)
+
+/* Second Counter Register (byte, BCD-coded) - RSECCNT */
+#define SH7750_RSECCNT_REGOFS 0xC80004 /* offset */
+#define SH7750_RSECCNT SH7750_P4_REG32(SH7750_RSECCNT_REGOFS)
+#define SH7750_RSECCNT_A7 SH7750_A7_REG32(SH7750_RSECCNT_REGOFS)
+
+/* Minute Counter Register (byte, BCD-coded) - RMINCNT */
+#define SH7750_RMINCNT_REGOFS 0xC80008 /* offset */
+#define SH7750_RMINCNT SH7750_P4_REG32(SH7750_RMINCNT_REGOFS)
+#define SH7750_RMINCNT_A7 SH7750_A7_REG32(SH7750_RMINCNT_REGOFS)
+
+/* Hour Counter Register (byte, BCD-coded) - RHRCNT */
+#define SH7750_RHRCNT_REGOFS 0xC8000C /* offset */
+#define SH7750_RHRCNT SH7750_P4_REG32(SH7750_RHRCNT_REGOFS)
+#define SH7750_RHRCNT_A7 SH7750_A7_REG32(SH7750_RHRCNT_REGOFS)
+
+/* Day-of-Week Counter Register (byte) - RWKCNT */
+#define SH7750_RWKCNT_REGOFS 0xC80010 /* offset */
+#define SH7750_RWKCNT SH7750_P4_REG32(SH7750_RWKCNT_REGOFS)
+#define SH7750_RWKCNT_A7 SH7750_A7_REG32(SH7750_RWKCNT_REGOFS)
+
+#define SH7750_RWKCNT_SUN 0 /* Sunday */
+#define SH7750_RWKCNT_MON 1 /* Monday */
+#define SH7750_RWKCNT_TUE 2 /* Tuesday */
+#define SH7750_RWKCNT_WED 3 /* Wednesday */
+#define SH7750_RWKCNT_THU 4 /* Thursday */
+#define SH7750_RWKCNT_FRI 5 /* Friday */
+#define SH7750_RWKCNT_SAT 6 /* Saturday */
+
+/* Day Counter Register (byte, BCD-coded) - RDAYCNT */
+#define SH7750_RDAYCNT_REGOFS 0xC80014 /* offset */
+#define SH7750_RDAYCNT SH7750_P4_REG32(SH7750_RDAYCNT_REGOFS)
+#define SH7750_RDAYCNT_A7 SH7750_A7_REG32(SH7750_RDAYCNT_REGOFS)
+
+/* Month Counter Register (byte, BCD-coded) - RMONCNT */
+#define SH7750_RMONCNT_REGOFS 0xC80018 /* offset */
+#define SH7750_RMONCNT SH7750_P4_REG32(SH7750_RMONCNT_REGOFS)
+#define SH7750_RMONCNT_A7 SH7750_A7_REG32(SH7750_RMONCNT_REGOFS)
+
+/* Year Counter Register (half, BCD-coded) - RYRCNT */
+#define SH7750_RYRCNT_REGOFS 0xC8001C /* offset */
+#define SH7750_RYRCNT SH7750_P4_REG32(SH7750_RYRCNT_REGOFS)
+#define SH7750_RYRCNT_A7 SH7750_A7_REG32(SH7750_RYRCNT_REGOFS)
+
+/* Second Alarm Register (byte, BCD-coded) - RSECAR */
+#define SH7750_RSECAR_REGOFS 0xC80020 /* offset */
+#define SH7750_RSECAR SH7750_P4_REG32(SH7750_RSECAR_REGOFS)
+#define SH7750_RSECAR_A7 SH7750_A7_REG32(SH7750_RSECAR_REGOFS)
+#define SH7750_RSECAR_ENB 0x80 /* Second Alarm Enable */
+
+/* Minute Alarm Register (byte, BCD-coded) - RMINAR */
+#define SH7750_RMINAR_REGOFS 0xC80024 /* offset */
+#define SH7750_RMINAR SH7750_P4_REG32(SH7750_RMINAR_REGOFS)
+#define SH7750_RMINAR_A7 SH7750_A7_REG32(SH7750_RMINAR_REGOFS)
+#define SH7750_RMINAR_ENB 0x80 /* Minute Alarm Enable */
+
+/* Hour Alarm Register (byte, BCD-coded) - RHRAR */
+#define SH7750_RHRAR_REGOFS 0xC80028 /* offset */
+#define SH7750_RHRAR SH7750_P4_REG32(SH7750_RHRAR_REGOFS)
+#define SH7750_RHRAR_A7 SH7750_A7_REG32(SH7750_RHRAR_REGOFS)
+#define SH7750_RHRAR_ENB 0x80 /* Hour Alarm Enable */
+
+/* Day-of-Week Alarm Register (byte) - RWKAR */
+#define SH7750_RWKAR_REGOFS 0xC8002C /* offset */
+#define SH7750_RWKAR SH7750_P4_REG32(SH7750_RWKAR_REGOFS)
+#define SH7750_RWKAR_A7 SH7750_A7_REG32(SH7750_RWKAR_REGOFS)
+#define SH7750_RWKAR_ENB 0x80 /* Day-of-week Alarm Enable */
+
+#define SH7750_RWKAR_SUN 0 /* Sunday */
+#define SH7750_RWKAR_MON 1 /* Monday */
+#define SH7750_RWKAR_TUE 2 /* Tuesday */
+#define SH7750_RWKAR_WED 3 /* Wednesday */
+#define SH7750_RWKAR_THU 4 /* Thursday */
+#define SH7750_RWKAR_FRI 5 /* Friday */
+#define SH7750_RWKAR_SAT 6 /* Saturday */
+
+/* Day Alarm Register (byte, BCD-coded) - RDAYAR */
+#define SH7750_RDAYAR_REGOFS 0xC80030 /* offset */
+#define SH7750_RDAYAR SH7750_P4_REG32(SH7750_RDAYAR_REGOFS)
+#define SH7750_RDAYAR_A7 SH7750_A7_REG32(SH7750_RDAYAR_REGOFS)
+#define SH7750_RDAYAR_ENB 0x80 /* Day Alarm Enable */
+
+/* Month Counter Register (byte, BCD-coded) - RMONAR */
+#define SH7750_RMONAR_REGOFS 0xC80034 /* offset */
+#define SH7750_RMONAR SH7750_P4_REG32(SH7750_RMONAR_REGOFS)
+#define SH7750_RMONAR_A7 SH7750_A7_REG32(SH7750_RMONAR_REGOFS)
+#define SH7750_RMONAR_ENB 0x80 /* Month Alarm Enable */
+
+/* RTC Control Register 1 (byte) - RCR1 */
+#define SH7750_RCR1_REGOFS 0xC80038 /* offset */
+#define SH7750_RCR1 SH7750_P4_REG32(SH7750_RCR1_REGOFS)
+#define SH7750_RCR1_A7 SH7750_A7_REG32(SH7750_RCR1_REGOFS)
+#define SH7750_RCR1_CF 0x80 /* Carry Flag */
+#define SH7750_RCR1_CIE 0x10 /* Carry Interrupt Enable */
+#define SH7750_RCR1_AIE 0x08 /* Alarm Interrupt Enable */
+#define SH7750_RCR1_AF 0x01 /* Alarm Flag */
+
+/* RTC Control Register 2 (byte) - RCR2 */
+#define SH7750_RCR2_REGOFS 0xC8003C /* offset */
+#define SH7750_RCR2 SH7750_P4_REG32(SH7750_RCR2_REGOFS)
+#define SH7750_RCR2_A7 SH7750_A7_REG32(SH7750_RCR2_REGOFS)
+#define SH7750_RCR2_PEF 0x80 /* Periodic Interrupt Flag */
+#define SH7750_RCR2_PES 0x70 /* Periodic Interrupt Enable: */
+#define SH7750_RCR2_PES_DIS 0x00 /* Periodic Interrupt Disabled */
+#define SH7750_RCR2_PES_DIV256 0x10 /* Generated at 1/256 sec interval */
+#define SH7750_RCR2_PES_DIV64 0x20 /* Generated at 1/64 sec interval */
+#define SH7750_RCR2_PES_DIV16 0x30 /* Generated at 1/16 sec interval */
+#define SH7750_RCR2_PES_DIV4 0x40 /* Generated at 1/4 sec interval */
+#define SH7750_RCR2_PES_DIV2 0x50 /* Generated at 1/2 sec interval */
+#define SH7750_RCR2_PES_x1 0x60 /* Generated at 1 sec interval */
+#define SH7750_RCR2_PES_x2 0x70 /* Generated at 2 sec interval */
+#define SH7750_RCR2_RTCEN 0x08 /* RTC Crystal Oscillator is Operated */
+#define SH7750_RCR2_ADJ 0x04 /* 30-Second Adjastment */
+#define SH7750_RCR2_RESET 0x02 /* Frequency divider circuits are reset */
+#define SH7750_RCR2_START 0x01 /* 0 - sec, min, hr, day-of-week, month,
+ year counters are stopped
+ 1 - sec, min, hr, day-of-week, month,
+ year counters operate normally */
+/*
+ * Bus State Controller - BSC
+ */
+/* Bus Control Register 1 - BCR1 */
+#define SH7750_BCR1_REGOFS 0x800000 /* offset */
+#define SH7750_BCR1 SH7750_P4_REG32(SH7750_BCR1_REGOFS)
+#define SH7750_BCR1_A7 SH7750_A7_REG32(SH7750_BCR1_REGOFS)
+#define SH7750_BCR1_ENDIAN 0x80000000 /* Endianness (1 - little endian) */
+#define SH7750_BCR1_MASTER 0x40000000 /* Master/Slave mode (1-master) */
+#define SH7750_BCR1_A0MPX 0x20000000 /* Area 0 Memory Type (0-SRAM,1-MPX) */
+#define SH7750_BCR1_IPUP 0x02000000 /* Input Pin Pull-up Control:
+ 0 - pull-up resistor is on for
+ control input pins
+ 1 - pull-up resistor is off */
+#define SH7750_BCR1_OPUP 0x01000000 /* Output Pin Pull-up Control:
+ 0 - pull-up resistor is on for
+ control output pins
+ 1 - pull-up resistor is off */
+#define SH7750_BCR1_A1MBC 0x00200000 /* Area 1 SRAM Byte Control Mode:
+ 0 - Area 1 SRAM is set to
+ normal mode
+ 1 - Area 1 SRAM is set to byte
+ control mode */
+#define SH7750_BCR1_A4MBC 0x00100000 /* Area 4 SRAM Byte Control Mode:
+ 0 - Area 4 SRAM is set to
+ normal mode
+ 1 - Area 4 SRAM is set to byte
+ control mode */
+#define SH7750_BCR1_BREQEN 0x00080000 /* BREQ Enable:
+ 0 - External requests are not
+ accepted
+ 1 - External requests are
+ accepted */
+#define SH7750_BCR1_PSHR 0x00040000 /* Partial Sharing Bit:
+ 0 - Master Mode
+ 1 - Partial-sharing Mode */
+#define SH7750_BCR1_MEMMPX 0x00020000 /* Area 1 to 6 MPX Interface:
+ 0 - SRAM/burst ROM interface
+ 1 - MPX interface */
+#define SH7750_BCR1_HIZMEM 0x00008000 /* High Impendance Control. Specifies
+ the state of A[25:0], BS\, CSn\,
+ RD/WR\, CE2A\, CE2B\ in standby
+ mode and when bus is released:
+ 0 - signals go to High-Z mode
+ 1 - signals driven */
+#define SH7750_BCR1_HIZCNT 0x00004000 /* High Impendance Control. Specifies
+ the state of the RAS\, RAS2\, WEn\,
+ CASn\, DQMn, RD\, CASS\, FRAME\,
+ RD2\ signals in standby mode and
+ when bus is released:
+ 0 - signals go to High-Z mode
+ 1 - signals driven */
+#define SH7750_BCR1_A0BST 0x00003800 /* Area 0 Burst ROM Control */
+#define SH7750_BCR1_A0BST_SRAM 0x0000 /* Area 0 accessed as SRAM i/f */
+#define SH7750_BCR1_A0BST_ROM4 0x0800 /* Area 0 accessed as burst ROM
+ interface, 4 cosequtive access */
+#define SH7750_BCR1_A0BST_ROM8 0x1000 /* Area 0 accessed as burst ROM
+ interface, 8 cosequtive access */
+#define SH7750_BCR1_A0BST_ROM16 0x1800 /* Area 0 accessed as burst ROM
+ interface, 16 cosequtive access */
+#define SH7750_BCR1_A0BST_ROM32 0x2000 /* Area 0 accessed as burst ROM
+ interface, 32 cosequtive access */
+
+#define SH7750_BCR1_A5BST 0x00000700 /* Area 5 Burst ROM Control */
+#define SH7750_BCR1_A5BST_SRAM 0x0000 /* Area 5 accessed as SRAM i/f */
+#define SH7750_BCR1_A5BST_ROM4 0x0100 /* Area 5 accessed as burst ROM
+ interface, 4 cosequtive access */
+#define SH7750_BCR1_A5BST_ROM8 0x0200 /* Area 5 accessed as burst ROM
+ interface, 8 cosequtive access */
+#define SH7750_BCR1_A5BST_ROM16 0x0300 /* Area 5 accessed as burst ROM
+ interface, 16 cosequtive access */
+#define SH7750_BCR1_A5BST_ROM32 0x0400 /* Area 5 accessed as burst ROM
+ interface, 32 cosequtive access */
+
+#define SH7750_BCR1_A6BST 0x000000E0 /* Area 6 Burst ROM Control */
+#define SH7750_BCR1_A6BST_SRAM 0x0000 /* Area 6 accessed as SRAM i/f */
+#define SH7750_BCR1_A6BST_ROM4 0x0020 /* Area 6 accessed as burst ROM
+ interface, 4 cosequtive access */
+#define SH7750_BCR1_A6BST_ROM8 0x0040 /* Area 6 accessed as burst ROM
+ interface, 8 cosequtive access */
+#define SH7750_BCR1_A6BST_ROM16 0x0060 /* Area 6 accessed as burst ROM
+ interface, 16 cosequtive access */
+#define SH7750_BCR1_A6BST_ROM32 0x0080 /* Area 6 accessed as burst ROM
+ interface, 32 cosequtive access */
+
+#define SH7750_BCR1_DRAMTP 0x001C /* Area 2 and 3 Memory Type */
+#define SH7750_BCR1_DRAMTP_2SRAM_3SRAM 0x0000 /* Area 2 and 3 are SRAM or MPX
+ interface. */
+#define SH7750_BCR1_DRAMTP_2SRAM_3SDRAM 0x0008 /* Area 2 - SRAM/MPX, Area 3 -
+ synchronous DRAM */
+#define SH7750_BCR1_DRAMTP_2SDRAM_3SDRAM 0x000C /* Area 2 and 3 are synchronous
+ DRAM interface */
+#define SH7750_BCR1_DRAMTP_2SRAM_3DRAM 0x0010 /* Area 2 - SRAM/MPX, Area 3 -
+ DRAM interface */
+#define SH7750_BCR1_DRAMTP_2DRAM_3DRAM 0x0014 /* Area 2 and 3 are DRAM
+ interface */
+
+#define SH7750_BCR1_A56PCM 0x00000001 /* Area 5 and 6 Bus Type:
+ 0 - SRAM interface
+ 1 - PCMCIA interface */
+
+/* Bus Control Register 2 (half) - BCR2 */
+#define SH7750_BCR2_REGOFS 0x800004 /* offset */
+#define SH7750_BCR2 SH7750_P4_REG32(SH7750_BCR2_REGOFS)
+#define SH7750_BCR2_A7 SH7750_A7_REG32(SH7750_BCR2_REGOFS)
+
+#define SH7750_BCR2_A0SZ 0xC000 /* Area 0 Bus Width */
+#define SH7750_BCR2_A0SZ_S 14
+#define SH7750_BCR2_A6SZ 0x3000 /* Area 6 Bus Width */
+#define SH7750_BCR2_A6SZ_S 12
+#define SH7750_BCR2_A5SZ 0x0C00 /* Area 5 Bus Width */
+#define SH7750_BCR2_A5SZ_S 10
+#define SH7750_BCR2_A4SZ 0x0300 /* Area 4 Bus Width */
+#define SH7750_BCR2_A4SZ_S 8
+#define SH7750_BCR2_A3SZ 0x00C0 /* Area 3 Bus Width */
+#define SH7750_BCR2_A3SZ_S 6
+#define SH7750_BCR2_A2SZ 0x0030 /* Area 2 Bus Width */
+#define SH7750_BCR2_A2SZ_S 4
+#define SH7750_BCR2_A1SZ 0x000C /* Area 1 Bus Width */
+#define SH7750_BCR2_A1SZ_S 2
+#define SH7750_BCR2_SZ_64 0 /* 64 bits */
+#define SH7750_BCR2_SZ_8 1 /* 8 bits */
+#define SH7750_BCR2_SZ_16 2 /* 16 bits */
+#define SH7750_BCR2_SZ_32 3 /* 32 bits */
+#define SH7750_BCR2_PORTEN 0x0001 /* Port Function Enable :
+ 0 - D51-D32 are not used as a port
+ 1 - D51-D32 are used as a port */
+
+/* Wait Control Register 1 - WCR1 */
+#define SH7750_WCR1_REGOFS 0x800008 /* offset */
+#define SH7750_WCR1 SH7750_P4_REG32(SH7750_WCR1_REGOFS)
+#define SH7750_WCR1_A7 SH7750_A7_REG32(SH7750_WCR1_REGOFS)
+#define SH7750_WCR1_DMAIW 0x70000000 /* DACK Device Inter-Cycle Idle
+ specification */
+#define SH7750_WCR1_DMAIW_S 28
+#define SH7750_WCR1_A6IW 0x07000000 /* Area 6 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A6IW_S 24
+#define SH7750_WCR1_A5IW 0x00700000 /* Area 5 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A5IW_S 20
+#define SH7750_WCR1_A4IW 0x00070000 /* Area 4 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A4IW_S 16
+#define SH7750_WCR1_A3IW 0x00007000 /* Area 3 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A3IW_S 12
+#define SH7750_WCR1_A2IW 0x00000700 /* Area 2 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A2IW_S 8
+#define SH7750_WCR1_A1IW 0x00000070 /* Area 1 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A1IW_S 4
+#define SH7750_WCR1_A0IW 0x00000007 /* Area 0 Inter-Cycle Idle spec. */
+#define SH7750_WCR1_A0IW_S 0
+
+/* Wait Control Register 2 - WCR2 */
+#define SH7750_WCR2_REGOFS 0x80000C /* offset */
+#define SH7750_WCR2 SH7750_P4_REG32(SH7750_WCR2_REGOFS)
+#define SH7750_WCR2_A7 SH7750_A7_REG32(SH7750_WCR2_REGOFS)
+
+#define SH7750_WCR2_A6W 0xE0000000 /* Area 6 Wait Control */
+#define SH7750_WCR2_A6W_S 29
+#define SH7750_WCR2_A6B 0x1C000000 /* Area 6 Burst Pitch */
+#define SH7750_WCR2_A6B_S 26
+#define SH7750_WCR2_A5W 0x03800000 /* Area 5 Wait Control */
+#define SH7750_WCR2_A5W_S 23
+#define SH7750_WCR2_A5B 0x00700000 /* Area 5 Burst Pitch */
+#define SH7750_WCR2_A5B_S 20
+#define SH7750_WCR2_A4W 0x000E0000 /* Area 4 Wait Control */
+#define SH7750_WCR2_A4W_S 17
+#define SH7750_WCR2_A3W 0x0000E000 /* Area 3 Wait Control */
+#define SH7750_WCR2_A3W_S 13
+#define SH7750_WCR2_A2W 0x00000E00 /* Area 2 Wait Control */
+#define SH7750_WCR2_A2W_S 9
+#define SH7750_WCR2_A1W 0x000001C0 /* Area 1 Wait Control */
+#define SH7750_WCR2_A1W_S 6
+#define SH7750_WCR2_A0W 0x00000038 /* Area 0 Wait Control */
+#define SH7750_WCR2_A0W_S 3
+#define SH7750_WCR2_A0B 0x00000007 /* Area 0 Burst Pitch */
+#define SH7750_WCR2_A0B_S 0
+
+#define SH7750_WCR2_WS0 0 /* 0 wait states inserted */
+#define SH7750_WCR2_WS1 1 /* 1 wait states inserted */
+#define SH7750_WCR2_WS2 2 /* 2 wait states inserted */
+#define SH7750_WCR2_WS3 3 /* 3 wait states inserted */
+#define SH7750_WCR2_WS6 4 /* 6 wait states inserted */
+#define SH7750_WCR2_WS9 5 /* 9 wait states inserted */
+#define SH7750_WCR2_WS12 6 /* 12 wait states inserted */
+#define SH7750_WCR2_WS15 7 /* 15 wait states inserted */
+
+#define SH7750_WCR2_BPWS0 0 /* 0 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS1 1 /* 1 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS2 2 /* 2 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS3 3 /* 3 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS4 4 /* 4 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS5 5 /* 5 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS6 6 /* 6 wait states inserted from 2nd access */
+#define SH7750_WCR2_BPWS7 7 /* 7 wait states inserted from 2nd access */
+
+/* DRAM CAS\ Assertion Delay (area 3,2) */
+#define SH7750_WCR2_DRAM_CAS_ASW1 0 /* 1 cycle */
+#define SH7750_WCR2_DRAM_CAS_ASW2 1 /* 2 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW3 2 /* 3 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW4 3 /* 4 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW7 4 /* 7 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW10 5 /* 10 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW13 6 /* 13 cycles */
+#define SH7750_WCR2_DRAM_CAS_ASW16 7 /* 16 cycles */
+
+/* SDRAM CAS\ Latency Cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT1 1 /* 1 cycle */
+#define SH7750_WCR2_SDRAM_CAS_LAT2 2 /* 2 cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT3 3 /* 3 cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT4 4 /* 4 cycles */
+#define SH7750_WCR2_SDRAM_CAS_LAT5 5 /* 5 cycles */
+
+/* Wait Control Register 3 - WCR3 */
+#define SH7750_WCR3_REGOFS 0x800010 /* offset */
+#define SH7750_WCR3 SH7750_P4_REG32(SH7750_WCR3_REGOFS)
+#define SH7750_WCR3_A7 SH7750_A7_REG32(SH7750_WCR3_REGOFS)
+
+#define SH7750_WCR3_A6S 0x04000000 /* Area 6 Write Strobe Setup time */
+#define SH7750_WCR3_A6H 0x03000000 /* Area 6 Data Hold Time */
+#define SH7750_WCR3_A6H_S 24
+#define SH7750_WCR3_A5S 0x00400000 /* Area 5 Write Strobe Setup time */
+#define SH7750_WCR3_A5H 0x00300000 /* Area 5 Data Hold Time */
+#define SH7750_WCR3_A5H_S 20
+#define SH7750_WCR3_A4S 0x00040000 /* Area 4 Write Strobe Setup time */
+#define SH7750_WCR3_A4H 0x00030000 /* Area 4 Data Hold Time */
+#define SH7750_WCR3_A4H_S 16
+#define SH7750_WCR3_A3S 0x00004000 /* Area 3 Write Strobe Setup time */
+#define SH7750_WCR3_A3H 0x00003000 /* Area 3 Data Hold Time */
+#define SH7750_WCR3_A3H_S 12
+#define SH7750_WCR3_A2S 0x00000400 /* Area 2 Write Strobe Setup time */
+#define SH7750_WCR3_A2H 0x00000300 /* Area 2 Data Hold Time */
+#define SH7750_WCR3_A2H_S 8
+#define SH7750_WCR3_A1S 0x00000040 /* Area 1 Write Strobe Setup time */
+#define SH7750_WCR3_A1H 0x00000030 /* Area 1 Data Hold Time */
+#define SH7750_WCR3_A1H_S 4
+#define SH7750_WCR3_A0S 0x00000004 /* Area 0 Write Strobe Setup time */
+#define SH7750_WCR3_A0H 0x00000003 /* Area 0 Data Hold Time */
+#define SH7750_WCR3_A0H_S 0
+
+#define SH7750_WCR3_DHWS_0 0 /* 0 wait states data hold time */
+#define SH7750_WCR3_DHWS_1 1 /* 1 wait states data hold time */
+#define SH7750_WCR3_DHWS_2 2 /* 2 wait states data hold time */
+#define SH7750_WCR3_DHWS_3 3 /* 3 wait states data hold time */
+
+#define SH7750_MCR_REGOFS 0x800014 /* offset */
+#define SH7750_MCR SH7750_P4_REG32(SH7750_MCR_REGOFS)
+#define SH7750_MCR_A7 SH7750_A7_REG32(SH7750_MCR_REGOFS)
+
+#define SH7750_MCR_RASD 0x80000000 /* RAS Down mode */
+#define SH7750_MCR_MRSET 0x40000000 /* SDRAM Mode Register Set */
+#define SH7750_MCR_PALL 0x00000000 /* SDRAM Precharge All cmd. Mode */
+#define SH7750_MCR_TRC 0x38000000 /* RAS Precharge Time at End of
+ Refresh: */
+#define SH7750_MCR_TRC_0 0x00000000 /* 0 */
+#define SH7750_MCR_TRC_3 0x08000000 /* 3 */
+#define SH7750_MCR_TRC_6 0x10000000 /* 6 */
+#define SH7750_MCR_TRC_9 0x18000000 /* 9 */
+#define SH7750_MCR_TRC_12 0x20000000 /* 12 */
+#define SH7750_MCR_TRC_15 0x28000000 /* 15 */
+#define SH7750_MCR_TRC_18 0x30000000 /* 18 */
+#define SH7750_MCR_TRC_21 0x38000000 /* 21 */
+
+#define SH7750_MCR_TCAS 0x00800000 /* CAS Negation Period */
+#define SH7750_MCR_TCAS_1 0x00000000 /* 1 */
+#define SH7750_MCR_TCAS_2 0x00800000 /* 2 */
+
+#define SH7750_MCR_TPC 0x00380000 /* DRAM: RAS Precharge Period
+ SDRAM: minimum number of cycles
+ until the next bank active cmd
+ is output after precharging */
+#define SH7750_MCR_TPC_S 19
+#define SH7750_MCR_TPC_SDRAM_1 0x00000000 /* 1 cycle */
+#define SH7750_MCR_TPC_SDRAM_2 0x00080000 /* 2 cycles */
+#define SH7750_MCR_TPC_SDRAM_3 0x00100000 /* 3 cycles */
+#define SH7750_MCR_TPC_SDRAM_4 0x00180000 /* 4 cycles */
+#define SH7750_MCR_TPC_SDRAM_5 0x00200000 /* 5 cycles */
+#define SH7750_MCR_TPC_SDRAM_6 0x00280000 /* 6 cycles */
+#define SH7750_MCR_TPC_SDRAM_7 0x00300000 /* 7 cycles */
+#define SH7750_MCR_TPC_SDRAM_8 0x00380000 /* 8 cycles */
+
+#define SH7750_MCR_RCD 0x00030000 /* DRAM: RAS-CAS Assertion Delay time
+ SDRAM: bank active-read/write cmd
+ delay time */
+#define SH7750_MCR_RCD_DRAM_2 0x00000000 /* DRAM delay 2 clocks */
+#define SH7750_MCR_RCD_DRAM_3 0x00010000 /* DRAM delay 3 clocks */
+#define SH7750_MCR_RCD_DRAM_4 0x00020000 /* DRAM delay 4 clocks */
+#define SH7750_MCR_RCD_DRAM_5 0x00030000 /* DRAM delay 5 clocks */
+#define SH7750_MCR_RCD_SDRAM_2 0x00010000 /* DRAM delay 2 clocks */
+#define SH7750_MCR_RCD_SDRAM_3 0x00020000 /* DRAM delay 3 clocks */
+#define SH7750_MCR_RCD_SDRAM_4 0x00030000 /* DRAM delay 4 clocks */
+
+#define SH7750_MCR_TRWL 0x0000E000 /* SDRAM Write Precharge Delay */
+#define SH7750_MCR_TRWL_1 0x00000000 /* 1 */
+#define SH7750_MCR_TRWL_2 0x00002000 /* 2 */
+#define SH7750_MCR_TRWL_3 0x00004000 /* 3 */
+#define SH7750_MCR_TRWL_4 0x00006000 /* 4 */
+#define SH7750_MCR_TRWL_5 0x00008000 /* 5 */
+
+#define SH7750_MCR_TRAS 0x00001C00 /* DRAM: CAS-Before-RAS Refresh RAS
+ asserting period
+ SDRAM: Command interval after
+ synchronous DRAM refresh */
+#define SH7750_MCR_TRAS_DRAM_2 0x00000000 /* 2 */
+#define SH7750_MCR_TRAS_DRAM_3 0x00000400 /* 3 */
+#define SH7750_MCR_TRAS_DRAM_4 0x00000800 /* 4 */
+#define SH7750_MCR_TRAS_DRAM_5 0x00000C00 /* 5 */
+#define SH7750_MCR_TRAS_DRAM_6 0x00001000 /* 6 */
+#define SH7750_MCR_TRAS_DRAM_7 0x00001400 /* 7 */
+#define SH7750_MCR_TRAS_DRAM_8 0x00001800 /* 8 */
+#define SH7750_MCR_TRAS_DRAM_9 0x00001C00 /* 9 */
+
+#define SH7750_MCR_TRAS_SDRAM_TRC_4 0x00000000 /* 4 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_5 0x00000400 /* 5 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_6 0x00000800 /* 6 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_7 0x00000C00 /* 7 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_8 0x00001000 /* 8 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_9 0x00001400 /* 9 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_10 0x00001800 /* 10 + TRC */
+#define SH7750_MCR_TRAS_SDRAM_TRC_11 0x00001C00 /* 11 + TRC */
+
+#define SH7750_MCR_BE 0x00000200 /* Burst Enable */
+#define SH7750_MCR_SZ 0x00000180 /* Memory Data Size */
+#define SH7750_MCR_SZ_64 0x00000000 /* 64 bits */
+#define SH7750_MCR_SZ_16 0x00000100 /* 16 bits */
+#define SH7750_MCR_SZ_32 0x00000180 /* 32 bits */
+
+#define SH7750_MCR_AMX 0x00000078 /* Address Multiplexing */
+#define SH7750_MCR_AMX_S 3
+#define SH7750_MCR_AMX_DRAM_8BIT_COL 0x00000000 /* 8-bit column addr */
+#define SH7750_MCR_AMX_DRAM_9BIT_COL 0x00000008 /* 9-bit column addr */
+#define SH7750_MCR_AMX_DRAM_10BIT_COL 0x00000010 /* 10-bit column addr */
+#define SH7750_MCR_AMX_DRAM_11BIT_COL 0x00000018 /* 11-bit column addr */
+#define SH7750_MCR_AMX_DRAM_12BIT_COL 0x00000020 /* 12-bit column addr */
+/* See SH7750 Hardware Manual for SDRAM address multiplexor selection */
+
+#define SH7750_MCR_RFSH 0x00000004 /* Refresh Control */
+#define SH7750_MCR_RMODE 0x00000002 /* Refresh Mode: */
+#define SH7750_MCR_RMODE_NORMAL 0x00000000 /* Normal Refresh Mode */
+#define SH7750_MCR_RMODE_SELF 0x00000002 /* Self-Refresh Mode */
+#define SH7750_MCR_RMODE_EDO 0x00000001 /* EDO Mode */
+
+/* SDRAM Mode Set address */
+#define SH7750_SDRAM_MODE_A2_BASE 0xFF900000
+#define SH7750_SDRAM_MODE_A3_BASE 0xFF940000
+#define SH7750_SDRAM_MODE_A2_32BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 2))
+#define SH7750_SDRAM_MODE_A3_32BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 2))
+#define SH7750_SDRAM_MODE_A2_64BIT(x) (SH7750_SDRAM_MODE_A2_BASE + ((x) << 3))
+#define SH7750_SDRAM_MODE_A3_64BIT(x) (SH7750_SDRAM_MODE_A3_BASE + ((x) << 3))
+
+
+/* PCMCIA Control Register (half) - PCR */
+#define SH7750_PCR_REGOFS 0x800018 /* offset */
+#define SH7750_PCR SH7750_P4_REG32(SH7750_PCR_REGOFS)
+#define SH7750_PCR_A7 SH7750_A7_REG32(SH7750_PCR_REGOFS)
+
+#define SH7750_PCR_A5PCW 0xC000 /* Area 5 PCMCIA Wait - Number of wait
+ states to be added to the number of
+ waits specified by WCR2 in a low-speed
+ PCMCIA wait cycle */
+#define SH7750_PCR_A5PCW_0 0x0000 /* 0 waits inserted */
+#define SH7750_PCR_A5PCW_15 0x4000 /* 15 waits inserted */
+#define SH7750_PCR_A5PCW_30 0x8000 /* 30 waits inserted */
+#define SH7750_PCR_A5PCW_50 0xC000 /* 50 waits inserted */
+
+#define SH7750_PCR_A6PCW 0x3000 /* Area 6 PCMCIA Wait - Number of wait
+ states to be added to the number of
+ waits specified by WCR2 in a low-speed
+ PCMCIA wait cycle */
+#define SH7750_PCR_A6PCW_0 0x0000 /* 0 waits inserted */
+#define SH7750_PCR_A6PCW_15 0x1000 /* 15 waits inserted */
+#define SH7750_PCR_A6PCW_30 0x2000 /* 30 waits inserted */
+#define SH7750_PCR_A6PCW_50 0x3000 /* 50 waits inserted */
+
+#define SH7750_PCR_A5TED 0x0E00 /* Area 5 Address-OE\/WE\ Assertion Delay,
+ delay time from address output to
+ OE\/WE\ assertion on the connected
+ PCMCIA interface */
+#define SH7750_PCR_A5TED_S 9
+#define SH7750_PCR_A6TED 0x01C0 /* Area 6 Address-OE\/WE\ Assertion Delay */
+#define SH7750_PCR_A6TED_S 6
+
+#define SH7750_PCR_TED_0WS 0 /* 0 Waits inserted */
+#define SH7750_PCR_TED_1WS 1 /* 1 Waits inserted */
+#define SH7750_PCR_TED_2WS 2 /* 2 Waits inserted */
+#define SH7750_PCR_TED_3WS 3 /* 3 Waits inserted */
+#define SH7750_PCR_TED_6WS 4 /* 6 Waits inserted */
+#define SH7750_PCR_TED_9WS 5 /* 9 Waits inserted */
+#define SH7750_PCR_TED_12WS 6 /* 12 Waits inserted */
+#define SH7750_PCR_TED_15WS 7 /* 15 Waits inserted */
+
+#define SH7750_PCR_A5TEH 0x0038 /* Area 5 OE\/WE\ Negation Address delay,
+ address hold delay time from OE\/WE\
+ negation in a write on the connected
+ PCMCIA interface */
+#define SH7750_PCR_A5TEH_S 3
+
+#define SH7750_PCR_A6TEH 0x0007 /* Area 6 OE\/WE\ Negation Address delay */
+#define SH7750_PCR_A6TEH_S 0
+
+#define SH7750_PCR_TEH_0WS 0 /* 0 Waits inserted */
+#define SH7750_PCR_TEH_1WS 1 /* 1 Waits inserted */
+#define SH7750_PCR_TEH_2WS 2 /* 2 Waits inserted */
+#define SH7750_PCR_TEH_3WS 3 /* 3 Waits inserted */
+#define SH7750_PCR_TEH_6WS 4 /* 6 Waits inserted */
+#define SH7750_PCR_TEH_9WS 5 /* 9 Waits inserted */
+#define SH7750_PCR_TEH_12WS 6 /* 12 Waits inserted */
+#define SH7750_PCR_TEH_15WS 7 /* 15 Waits inserted */
+
+/* Refresh Timer Control/Status Register (half) - RTSCR */
+#define SH7750_RTCSR_REGOFS 0x80001C /* offset */
+#define SH7750_RTCSR SH7750_P4_REG32(SH7750_RTCSR_REGOFS)
+#define SH7750_RTCSR_A7 SH7750_A7_REG32(SH7750_RTCSR_REGOFS)
+
+#define SH7750_RTCSR_KEY 0xA500 /* RTCSR write key */
+#define SH7750_RTCSR_CMF 0x0080 /* Compare-Match Flag (indicates a
+ match between the refresh timer
+ counter and refresh time constant) */
+#define SH7750_RTCSR_CMIE 0x0040 /* Compare-Match Interrupt Enable */
+#define SH7750_RTCSR_CKS 0x0038 /* Refresh Counter Clock Selects */
+#define SH7750_RTCSR_CKS_DIS 0x0000 /* Clock Input Disabled */
+#define SH7750_RTCSR_CKS_CKIO_DIV4 0x0008 /* Bus Clock / 4 */
+#define SH7750_RTCSR_CKS_CKIO_DIV16 0x0010 /* Bus Clock / 16 */
+#define SH7750_RTCSR_CKS_CKIO_DIV64 0x0018 /* Bus Clock / 64 */
+#define SH7750_RTCSR_CKS_CKIO_DIV256 0x0020 /* Bus Clock / 256 */
+#define SH7750_RTCSR_CKS_CKIO_DIV1024 0x0028 /* Bus Clock / 1024 */
+#define SH7750_RTCSR_CKS_CKIO_DIV2048 0x0030 /* Bus Clock / 2048 */
+#define SH7750_RTCSR_CKS_CKIO_DIV4096 0x0038 /* Bus Clock / 4096 */
+
+#define SH7750_RTCSR_OVF 0x0004 /* Refresh Count Overflow Flag */
+#define SH7750_RTCSR_OVIE 0x0002 /* Refresh Count Overflow Interrupt
+ Enable */
+#define SH7750_RTCSR_LMTS 0x0001 /* Refresh Count Overflow Limit Select */
+#define SH7750_RTCSR_LMTS_1024 0x0000 /* Count Limit is 1024 */
+#define SH7750_RTCSR_LMTS_512 0x0001 /* Count Limit is 512 */
+
+/* Refresh Timer Counter (half) - RTCNT */
+#define SH7750_RTCNT_REGOFS 0x800020 /* offset */
+#define SH7750_RTCNT SH7750_P4_REG32(SH7750_RTCNT_REGOFS)
+#define SH7750_RTCNT_A7 SH7750_A7_REG32(SH7750_RTCNT_REGOFS)
+
+#define SH7750_RTCNT_KEY 0xA500 /* RTCNT write key */
+
+/* Refresh Time Constant Register (half) - RTCOR */
+#define SH7750_RTCOR_REGOFS 0x800024 /* offset */
+#define SH7750_RTCOR SH7750_P4_REG32(SH7750_RTCOR_REGOFS)
+#define SH7750_RTCOR_A7 SH7750_A7_REG32(SH7750_RTCOR_REGOFS)
+
+#define SH7750_RTCOR_KEY 0xA500 /* RTCOR write key */
+
+/* Refresh Count Register (half) - RFCR */
+#define SH7750_RFCR_REGOFS 0x800028 /* offset */
+#define SH7750_RFCR SH7750_P4_REG32(SH7750_RFCR_REGOFS)
+#define SH7750_RFCR_A7 SH7750_A7_REG32(SH7750_RFCR_REGOFS)
+
+#define SH7750_RFCR_KEY 0xA400 /* RFCR write key */
+
+/* Synchronous DRAM mode registers - SDMR */
+#define SH7750_SDMR2_REGOFS 0x900000 /* base offset */
+#define SH7750_SDMR2_REGNB 0x0FFC /* nb of register */
+#define SH7750_SDMR2 SH7750_P4_REG32(SH7750_SDMR2_REGOFS)
+#define SH7750_SDMR2_A7 SH7750_A7_REG32(SH7750_SDMR2_REGOFS)
+
+#define SH7750_SDMR3_REGOFS 0x940000 /* offset */
+#define SH7750_SDMR3_REGNB 0x0FFC /* nb of register */
+#define SH7750_SDMR3 SH7750_P4_REG32(SH7750_SDMR3_REGOFS)
+#define SH7750_SDMR3_A7 SH7750_A7_REG32(SH7750_SDMR3_REGOFS)
+
+/*
+ * Direct Memory Access Controller (DMAC)
+ */
+
+/* DMA Source Address Register - SAR0, SAR1, SAR2, SAR3 */
+#define SH7750_SAR_REGOFS(n) (0xA00000 + ((n)*16)) /* offset */
+#define SH7750_SAR(n) SH7750_P4_REG32(SH7750_SAR_REGOFS(n))
+#define SH7750_SAR_A7(n) SH7750_A7_REG32(SH7750_SAR_REGOFS(n))
+#define SH7750_SAR0 SH7750_SAR(0)
+#define SH7750_SAR1 SH7750_SAR(1)
+#define SH7750_SAR2 SH7750_SAR(2)
+#define SH7750_SAR3 SH7750_SAR(3)
+#define SH7750_SAR0_A7 SH7750_SAR_A7(0)
+#define SH7750_SAR1_A7 SH7750_SAR_A7(1)
+#define SH7750_SAR2_A7 SH7750_SAR_A7(2)
+#define SH7750_SAR3_A7 SH7750_SAR_A7(3)
+
+/* DMA Destination Address Register - DAR0, DAR1, DAR2, DAR3 */
+#define SH7750_DAR_REGOFS(n) (0xA00004 + ((n)*16)) /* offset */
+#define SH7750_DAR(n) SH7750_P4_REG32(SH7750_DAR_REGOFS(n))
+#define SH7750_DAR_A7(n) SH7750_A7_REG32(SH7750_DAR_REGOFS(n))
+#define SH7750_DAR0 SH7750_DAR(0)
+#define SH7750_DAR1 SH7750_DAR(1)
+#define SH7750_DAR2 SH7750_DAR(2)
+#define SH7750_DAR3 SH7750_DAR(3)
+#define SH7750_DAR0_A7 SH7750_DAR_A7(0)
+#define SH7750_DAR1_A7 SH7750_DAR_A7(1)
+#define SH7750_DAR2_A7 SH7750_DAR_A7(2)
+#define SH7750_DAR3_A7 SH7750_DAR_A7(3)
+
+/* DMA Transfer Count Register - DMATCR0, DMATCR1, DMATCR2, DMATCR3 */
+#define SH7750_DMATCR_REGOFS(n) (0xA00008 + ((n)*16)) /* offset */
+#define SH7750_DMATCR(n) SH7750_P4_REG32(SH7750_DMATCR_REGOFS(n))
+#define SH7750_DMATCR_A7(n) SH7750_A7_REG32(SH7750_DMATCR_REGOFS(n))
+#define SH7750_DMATCR0_P4 SH7750_DMATCR(0)
+#define SH7750_DMATCR1_P4 SH7750_DMATCR(1)
+#define SH7750_DMATCR2_P4 SH7750_DMATCR(2)
+#define SH7750_DMATCR3_P4 SH7750_DMATCR(3)
+#define SH7750_DMATCR0_A7 SH7750_DMATCR_A7(0)
+#define SH7750_DMATCR1_A7 SH7750_DMATCR_A7(1)
+#define SH7750_DMATCR2_A7 SH7750_DMATCR_A7(2)
+#define SH7750_DMATCR3_A7 SH7750_DMATCR_A7(3)
+
+/* DMA Channel Control Register - CHCR0, CHCR1, CHCR2, CHCR3 */
+#define SH7750_CHCR_REGOFS(n) (0xA0000C + ((n)*16)) /* offset */
+#define SH7750_CHCR(n) SH7750_P4_REG32(SH7750_CHCR_REGOFS(n))
+#define SH7750_CHCR_A7(n) SH7750_A7_REG32(SH7750_CHCR_REGOFS(n))
+#define SH7750_CHCR0 SH7750_CHCR(0)
+#define SH7750_CHCR1 SH7750_CHCR(1)
+#define SH7750_CHCR2 SH7750_CHCR(2)
+#define SH7750_CHCR3 SH7750_CHCR(3)
+#define SH7750_CHCR0_A7 SH7750_CHCR_A7(0)
+#define SH7750_CHCR1_A7 SH7750_CHCR_A7(1)
+#define SH7750_CHCR2_A7 SH7750_CHCR_A7(2)
+#define SH7750_CHCR3_A7 SH7750_CHCR_A7(3)
+
+#define SH7750_CHCR_SSA 0xE0000000 /* Source Address Space Attribute */
+#define SH7750_CHCR_SSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */
+#define SH7750_CHCR_SSA_DYNBSZ 0x20000000 /* Dynamic Bus Sizing I/O space */
+#define SH7750_CHCR_SSA_IO8 0x40000000 /* 8-bit I/O space */
+#define SH7750_CHCR_SSA_IO16 0x60000000 /* 16-bit I/O space */
+#define SH7750_CHCR_SSA_CMEM8 0x80000000 /* 8-bit common memory space */
+#define SH7750_CHCR_SSA_CMEM16 0xA0000000 /* 16-bit common memory space */
+#define SH7750_CHCR_SSA_AMEM8 0xC0000000 /* 8-bit attribute memory space */
+#define SH7750_CHCR_SSA_AMEM16 0xE0000000 /* 16-bit attribute memory space */
+
+#define SH7750_CHCR_STC 0x10000000 /* Source Address Wait Control Select,
+ specifies CS5 or CS6 space wait
+ control for PCMCIA access */
+
+#define SH7750_CHCR_DSA 0x0E000000 /* Source Address Space Attribute */
+#define SH7750_CHCR_DSA_PCMCIA 0x00000000 /* Reserved in PCMCIA access */
+#define SH7750_CHCR_DSA_DYNBSZ 0x02000000 /* Dynamic Bus Sizing I/O space */
+#define SH7750_CHCR_DSA_IO8 0x04000000 /* 8-bit I/O space */
+#define SH7750_CHCR_DSA_IO16 0x06000000 /* 16-bit I/O space */
+#define SH7750_CHCR_DSA_CMEM8 0x08000000 /* 8-bit common memory space */
+#define SH7750_CHCR_DSA_CMEM16 0x0A000000 /* 16-bit common memory space */
+#define SH7750_CHCR_DSA_AMEM8 0x0C000000 /* 8-bit attribute memory space */
+#define SH7750_CHCR_DSA_AMEM16 0x0E000000 /* 16-bit attribute memory space */
+
+#define SH7750_CHCR_DTC 0x01000000 /* Destination Address Wait Control
+ Select, specifies CS5 or CS6
+ space wait control for PCMCIA
+ access */
+
+#define SH7750_CHCR_DS 0x00080000 /* DREQ\ Select : */
+#define SH7750_CHCR_DS_LOWLVL 0x00000000 /* Low Level Detection */
+#define SH7750_CHCR_DS_FALL 0x00080000 /* Falling Edge Detection */
+
+#define SH7750_CHCR_RL 0x00040000 /* Request Check Level: */
+#define SH7750_CHCR_RL_ACTH 0x00000000 /* DRAK is an active high out */
+#define SH7750_CHCR_RL_ACTL 0x00040000 /* DRAK is an active low out */
+
+#define SH7750_CHCR_AM 0x00020000 /* Acknowledge Mode: */
+#define SH7750_CHCR_AM_RD 0x00000000 /* DACK is output in read cycle */
+#define SH7750_CHCR_AM_WR 0x00020000 /* DACK is output in write cycle */
+
+#define SH7750_CHCR_AL 0x00010000 /* Acknowledge Level: */
+#define SH7750_CHCR_AL_ACTH 0x00000000 /* DACK is an active high out */
+#define SH7750_CHCR_AL_ACTL 0x00010000 /* DACK is an active low out */
+
+#define SH7750_CHCR_DM 0x0000C000 /* Destination Address Mode: */
+#define SH7750_CHCR_DM_FIX 0x00000000 /* Destination Addr Fixed */
+#define SH7750_CHCR_DM_INC 0x00004000 /* Destination Addr Incremented */
+#define SH7750_CHCR_DM_DEC 0x00008000 /* Destination Addr Decremented */
+
+#define SH7750_CHCR_SM 0x00003000 /* Source Address Mode: */
+#define SH7750_CHCR_SM_FIX 0x00000000 /* Source Addr Fixed */
+#define SH7750_CHCR_SM_INC 0x00001000 /* Source Addr Incremented */
+#define SH7750_CHCR_SM_DEC 0x00002000 /* Source Addr Decremented */
+
+#define SH7750_CHCR_RS 0x00000F00 /* Request Source Select: */
+#define SH7750_CHCR_RS_ER_DA_EA_TO_EA 0x000 /* External Request, Dual Address
+ Mode (External Addr Space->
+ External Addr Space) */
+#define SH7750_CHCR_RS_ER_SA_EA_TO_ED 0x200 /* External Request, Single
+ Address Mode (External Addr
+ Space -> External Device) */
+#define SH7750_CHCR_RS_ER_SA_ED_TO_EA 0x300 /* External Request, Single
+ Address Mode, (External
+ Device -> External Addr
+ Space) */
+#define SH7750_CHCR_RS_AR_EA_TO_EA 0x400 /* Auto-Request (External Addr
+ Space -> External Addr Space) */
+
+#define SH7750_CHCR_RS_AR_EA_TO_OCP 0x500 /* Auto-Request (External Addr
+ Space -> On-chip Peripheral
+ Module) */
+#define SH7750_CHCR_RS_AR_OCP_TO_EA 0x600 /* Auto-Request (On-chip
+ Peripheral Module ->
+ External Addr Space */
+#define SH7750_CHCR_RS_SCITX_EA_TO_SC 0x800 /* SCI Transmit-Data-Empty intr
+ transfer request (external
+ address space -> SCTDR1) */
+#define SH7750_CHCR_RS_SCIRX_SC_TO_EA 0x900 /* SCI Receive-Data-Full intr
+ transfer request (SCRDR1 ->
+ External Addr Space) */
+#define SH7750_CHCR_RS_SCIFTX_EA_TO_SC 0xA00 /* SCIF Transmit-Data-Empty intr
+ transfer request (external
+ address space -> SCFTDR1) */
+#define SH7750_CHCR_RS_SCIFRX_SC_TO_EA 0xB00 /* SCIF Receive-Data-Full intr
+ transfer request (SCFRDR2 ->
+ External Addr Space) */
+#define SH7750_CHCR_RS_TMU2_EA_TO_EA 0xC00 /* TMU Channel 2 (input capture
+ interrupt), (external address
+ space -> external address
+ space) */
+#define SH7750_CHCR_RS_TMU2_EA_TO_OCP 0xD00 /* TMU Channel 2 (input capture
+ interrupt), (external address
+ space -> on-chip peripheral
+ module) */
+#define SH7750_CHCR_RS_TMU2_OCP_TO_EA 0xE00 /* TMU Channel 2 (input capture
+ interrupt), (on-chip
+ peripheral module -> external
+ address space) */
+
+#define SH7750_CHCR_TM 0x00000080 /* Transmit mode: */
+#define SH7750_CHCR_TM_CSTEAL 0x00000000 /* Cycle Steal Mode */
+#define SH7750_CHCR_TM_BURST 0x00000080 /* Burst Mode */
+
+#define SH7750_CHCR_TS 0x00000070 /* Transmit Size: */
+#define SH7750_CHCR_TS_QUAD 0x00000000 /* Quadword Size (64 bits) */
+#define SH7750_CHCR_TS_BYTE 0x00000010 /* Byte Size (8 bit) */
+#define SH7750_CHCR_TS_WORD 0x00000020 /* Word Size (16 bit) */
+#define SH7750_CHCR_TS_LONG 0x00000030 /* Longword Size (32 bit) */
+#define SH7750_CHCR_TS_BLOCK 0x00000040 /* 32-byte block transfer */
+
+#define SH7750_CHCR_IE 0x00000004 /* Interrupt Enable */
+#define SH7750_CHCR_TE 0x00000002 /* Transfer End */
+#define SH7750_CHCR_DE 0x00000001 /* DMAC Enable */
+
+/* DMA Operation Register - DMAOR */
+#define SH7750_DMAOR_REGOFS 0xA00040 /* offset */
+#define SH7750_DMAOR SH7750_P4_REG32(SH7750_DMAOR_REGOFS)
+#define SH7750_DMAOR_A7 SH7750_A7_REG32(SH7750_DMAOR_REGOFS)
+
+#define SH7750_DMAOR_DDT 0x00008000 /* On-Demand Data Transfer Mode */
+
+#define SH7750_DMAOR_PR 0x00000300 /* Priority Mode: */
+#define SH7750_DMAOR_PR_0123 0x00000000 /* CH0 > CH1 > CH2 > CH3 */
+#define SH7750_DMAOR_PR_0231 0x00000100 /* CH0 > CH2 > CH3 > CH1 */
+#define SH7750_DMAOR_PR_2013 0x00000200 /* CH2 > CH0 > CH1 > CH3 */
+#define SH7750_DMAOR_PR_RR 0x00000300 /* Round-robin mode */
+
+#define SH7750_DMAOR_COD 0x00000010 /* Check Overrun for DREQ\ */
+#define SH7750_DMAOR_AE 0x00000004 /* Address Error flag */
+#define SH7750_DMAOR_NMIF 0x00000002 /* NMI Flag */
+#define SH7750_DMAOR_DME 0x00000001 /* DMAC Master Enable */
+
+/*
+ * I/O Ports
+ */
+/* Port Control Register A - PCTRA */
+#define SH7750_PCTRA_REGOFS 0x80002C /* offset */
+#define SH7750_PCTRA SH7750_P4_REG32(SH7750_PCTRA_REGOFS)
+#define SH7750_PCTRA_A7 SH7750_A7_REG32(SH7750_PCTRA_REGOFS)
+
+#define SH7750_PCTRA_PBPUP(n) 0 /* Bit n is pulled up */
+#define SH7750_PCTRA_PBNPUP(n) (1 << ((n)*2+1)) /* Bit n is not pulled up */
+#define SH7750_PCTRA_PBINP(n) 0 /* Bit n is an input */
+#define SH7750_PCTRA_PBOUT(n) (1 << ((n)*2)) /* Bit n is an output */
+
+/* Port Data Register A - PDTRA(half) */
+#define SH7750_PDTRA_REGOFS 0x800030 /* offset */
+#define SH7750_PDTRA SH7750_P4_REG32(SH7750_PDTRA_REGOFS)
+#define SH7750_PDTRA_A7 SH7750_A7_REG32(SH7750_PDTRA_REGOFS)
+
+#define SH7750_PDTRA_BIT(n) (1 << (n))
+
+/* Port Control Register B - PCTRB */
+#define SH7750_PCTRB_REGOFS 0x800040 /* offset */
+#define SH7750_PCTRB SH7750_P4_REG32(SH7750_PCTRB_REGOFS)
+#define SH7750_PCTRB_A7 SH7750_A7_REG32(SH7750_PCTRB_REGOFS)
+
+#define SH7750_PCTRB_PBPUP(n) 0 /* Bit n is pulled up */
+#define SH7750_PCTRB_PBNPUP(n) (1 << ((n-16)*2+1)) /* Bit n is not pulled up */
+#define SH7750_PCTRB_PBINP(n) 0 /* Bit n is an input */
+#define SH7750_PCTRB_PBOUT(n) (1 << ((n-16)*2)) /* Bit n is an output */
+
+/* Port Data Register B - PDTRB(half) */
+#define SH7750_PDTRB_REGOFS 0x800044 /* offset */
+#define SH7750_PDTRB SH7750_P4_REG32(SH7750_PDTRB_REGOFS)
+#define SH7750_PDTRB_A7 SH7750_A7_REG32(SH7750_PDTRB_REGOFS)
+
+#define SH7750_PDTRB_BIT(n) (1 << ((n)-16))
+
+/* GPIO Interrupt Control Register - GPIOIC(half) */
+#define SH7750_GPIOIC_REGOFS 0x800048 /* offset */
+#define SH7750_GPIOIC SH7750_P4_REG32(SH7750_GPIOIC_REGOFS)
+#define SH7750_GPIOIC_A7 SH7750_A7_REG32(SH7750_GPIOIC_REGOFS)
+
+#define SH7750_GPIOIC_PTIREN(n) (1 << (n)) /* Port n is used as a GPIO int */
+
+/*
+ * Interrupt Controller - INTC
+ */
+/* Interrupt Control Register - ICR (half) */
+#define SH7750_ICR_REGOFS 0xD00000 /* offset */
+#define SH7750_ICR SH7750_P4_REG32(SH7750_ICR_REGOFS)
+#define SH7750_ICR_A7 SH7750_A7_REG32(SH7750_ICR_REGOFS)
+
+#define SH7750_ICR_NMIL 0x8000 /* NMI Input Level */
+#define SH7750_ICR_MAI 0x4000 /* NMI Interrupt Mask */
+
+#define SH7750_ICR_NMIB 0x0200 /* NMI Block Mode: */
+#define SH7750_ICR_NMIB_BLK 0x0000 /* NMI requests held pending while
+ SR.BL bit is set to 1 */
+#define SH7750_ICR_NMIB_NBLK 0x0200 /* NMI requests detected when SR.BL bit
+ set to 1 */
+
+#define SH7750_ICR_NMIE 0x0100 /* NMI Edge Select: */
+#define SH7750_ICR_NMIE_FALL 0x0000 /* Interrupt request detected on falling
+ edge of NMI input */
+#define SH7750_ICR_NMIE_RISE 0x0100 /* Interrupt request detected on rising
+ edge of NMI input */
+
+#define SH7750_ICR_IRLM 0x0080 /* IRL Pin Mode: */
+#define SH7750_ICR_IRLM_ENC 0x0000 /* IRL\ pins used as a level-encoded
+ interrupt requests */
+#define SH7750_ICR_IRLM_RAW 0x0080 /* IRL\ pins used as a four independent
+ interrupt requests */
+
+/*
+ * User Break Controller registers
+ */
+#define SH7750_BARA 0x200000 /* Break address regiser A */
+#define SH7750_BAMRA 0x200004 /* Break address mask regiser A */
+#define SH7750_BBRA 0x200008 /* Break bus cycle regiser A */
+#define SH7750_BARB 0x20000c /* Break address regiser B */
+#define SH7750_BAMRB 0x200010 /* Break address mask regiser B */
+#define SH7750_BBRB 0x200014 /* Break bus cycle regiser B */
+#define SH7750_BASRB 0x000018 /* Break ASID regiser B */
+#define SH7750_BDRB 0x200018 /* Break data regiser B */
+#define SH7750_BDMRB 0x20001c /* Break data mask regiser B */
+#define SH7750_BRCR 0x200020 /* Break control register */
+
+#define SH7750_BRCR_UDBE 0x0001 /* User break debug enable bit */
+
+/*
+ * Missing in RTEMS, added for QEMU
+ */
+#define SH7750_BCR3_A7 0x1f800050
+#define SH7750_BCR4_A7 0x1e0a00f0
+
+#endif
diff --git a/hw/sh_intc.c b/hw/sh_intc.c
new file mode 100644
index 0000000..0734da9
--- /dev/null
+++ b/hw/sh_intc.c
@@ -0,0 +1,481 @@
+/*
+ * SuperH interrupt controller module
+ *
+ * Copyright (c) 2007 Magnus Damm
+ * Based on sh_timer.c and arm_timer.c by Paul Brook
+ * Copyright (c) 2005-2006 CodeSourcery.
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sh_intc.h"
+#include "hw.h"
+#include "sh.h"
+
+//#define DEBUG_INTC
+//#define DEBUG_INTC_SOURCES
+
+#define INTC_A7(x) ((x) & 0x1fffffff)
+
+void sh_intc_toggle_source(struct intc_source *source,
+ int enable_adj, int assert_adj)
+{
+ int enable_changed = 0;
+ int pending_changed = 0;
+ int old_pending;
+
+ if ((source->enable_count == source->enable_max) && (enable_adj == -1))
+ enable_changed = -1;
+
+ source->enable_count += enable_adj;
+
+ if (source->enable_count == source->enable_max)
+ enable_changed = 1;
+
+ source->asserted += assert_adj;
+
+ old_pending = source->pending;
+ source->pending = source->asserted &&
+ (source->enable_count == source->enable_max);
+
+ if (old_pending != source->pending)
+ pending_changed = 1;
+
+ if (pending_changed) {
+ if (source->pending) {
+ source->parent->pending++;
+ if (source->parent->pending == 1)
+ cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ }
+ else {
+ source->parent->pending--;
+ if (source->parent->pending == 0)
+ cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
+ }
+ }
+
+ if (enable_changed || assert_adj || pending_changed) {
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n",
+ source->parent->pending,
+ source->asserted,
+ source->enable_count,
+ source->enable_max,
+ source->vect,
+ source->asserted ? "asserted " :
+ assert_adj ? "deasserted" : "",
+ enable_changed == 1 ? "enabled " :
+ enable_changed == -1 ? "disabled " : "",
+ source->pending ? "pending" : "");
+#endif
+ }
+}
+
+static void sh_intc_set_irq (void *opaque, int n, int level)
+{
+ struct intc_desc *desc = opaque;
+ struct intc_source *source = &(desc->sources[n]);
+
+ if (level && !source->asserted)
+ sh_intc_toggle_source(source, 0, 1);
+ else if (!level && source->asserted)
+ sh_intc_toggle_source(source, 0, -1);
+}
+
+int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
+{
+ unsigned int i;
+
+ /* slow: use a linked lists of pending sources instead */
+ /* wrong: take interrupt priority into account (one list per priority) */
+
+ if (imask == 0x0f) {
+ return -1; /* FIXME, update code to include priority per source */
+ }
+
+ for (i = 0; i < desc->nr_sources; i++) {
+ struct intc_source *source = desc->sources + i;
+
+ if (source->pending) {
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: (%d) returning interrupt source 0x%x\n",
+ desc->pending, source->vect);
+#endif
+ return source->vect;
+ }
+ }
+
+ abort();
+}
+
+#define INTC_MODE_NONE 0
+#define INTC_MODE_DUAL_SET 1
+#define INTC_MODE_DUAL_CLR 2
+#define INTC_MODE_ENABLE_REG 3
+#define INTC_MODE_MASK_REG 4
+#define INTC_MODE_IS_PRIO 8
+
+static unsigned int sh_intc_mode(unsigned long address,
+ unsigned long set_reg, unsigned long clr_reg)
+{
+ if ((address != INTC_A7(set_reg)) &&
+ (address != INTC_A7(clr_reg)))
+ return INTC_MODE_NONE;
+
+ if (set_reg && clr_reg) {
+ if (address == INTC_A7(set_reg))
+ return INTC_MODE_DUAL_SET;
+ else
+ return INTC_MODE_DUAL_CLR;
+ }
+
+ if (set_reg)
+ return INTC_MODE_ENABLE_REG;
+ else
+ return INTC_MODE_MASK_REG;
+}
+
+static void sh_intc_locate(struct intc_desc *desc,
+ unsigned long address,
+ unsigned long **datap,
+ intc_enum **enums,
+ unsigned int *first,
+ unsigned int *width,
+ unsigned int *modep)
+{
+ unsigned int i, mode;
+
+ /* this is slow but works for now */
+
+ if (desc->mask_regs) {
+ for (i = 0; i < desc->nr_mask_regs; i++) {
+ struct intc_mask_reg *mr = desc->mask_regs + i;
+
+ mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg);
+ if (mode == INTC_MODE_NONE)
+ continue;
+
+ *modep = mode;
+ *datap = &mr->value;
+ *enums = mr->enum_ids;
+ *first = mr->reg_width - 1;
+ *width = 1;
+ return;
+ }
+ }
+
+ if (desc->prio_regs) {
+ for (i = 0; i < desc->nr_prio_regs; i++) {
+ struct intc_prio_reg *pr = desc->prio_regs + i;
+
+ mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg);
+ if (mode == INTC_MODE_NONE)
+ continue;
+
+ *modep = mode | INTC_MODE_IS_PRIO;
+ *datap = &pr->value;
+ *enums = pr->enum_ids;
+ *first = (pr->reg_width / pr->field_width) - 1;
+ *width = pr->field_width;
+ return;
+ }
+ }
+
+ abort();
+}
+
+static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id,
+ int enable, int is_group)
+{
+ struct intc_source *source = desc->sources + id;
+
+ if (!id)
+ return;
+
+ if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: reserved interrupt source %d modified\n", id);
+#endif
+ return;
+ }
+
+ if (source->vect)
+ sh_intc_toggle_source(source, enable ? 1 : -1, 0);
+
+#ifdef DEBUG_INTC
+ else {
+ printf("setting interrupt group %d to %d\n", id, !!enable);
+ }
+#endif
+
+ if ((is_group || !source->vect) && source->next_enum_id) {
+ sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1);
+ }
+
+#ifdef DEBUG_INTC
+ if (!source->vect) {
+ printf("setting interrupt group %d to %d - done\n", id, !!enable);
+ }
+#endif
+}
+
+static uint32_t sh_intc_read(void *opaque, target_phys_addr_t offset)
+{
+ struct intc_desc *desc = opaque;
+ intc_enum *enum_ids = NULL;
+ unsigned int first = 0;
+ unsigned int width = 0;
+ unsigned int mode = 0;
+ unsigned long *valuep;
+
+#ifdef DEBUG_INTC
+ printf("sh_intc_read 0x%lx\n", (unsigned long) offset);
+#endif
+
+ sh_intc_locate(desc, (unsigned long)offset, &valuep,
+ &enum_ids, &first, &width, &mode);
+ return *valuep;
+}
+
+static void sh_intc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ struct intc_desc *desc = opaque;
+ intc_enum *enum_ids = NULL;
+ unsigned int first = 0;
+ unsigned int width = 0;
+ unsigned int mode = 0;
+ unsigned int k;
+ unsigned long *valuep;
+ unsigned long mask;
+
+#ifdef DEBUG_INTC
+ printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
+#endif
+
+ sh_intc_locate(desc, (unsigned long)offset, &valuep,
+ &enum_ids, &first, &width, &mode);
+
+ switch (mode) {
+ case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break;
+ case INTC_MODE_DUAL_SET: value |= *valuep; break;
+ case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break;
+ default: abort();
+ }
+
+ for (k = 0; k <= first; k++) {
+ mask = ((1 << width) - 1) << ((first - k) * width);
+
+ if ((*valuep & mask) == (value & mask))
+ continue;
+#if 0
+ printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
+ k, first, enum_ids[k], (unsigned int)mask);
+#endif
+ sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
+ }
+
+ *valuep = value;
+
+#ifdef DEBUG_INTC
+ printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value);
+#endif
+}
+
+static CPUReadMemoryFunc * const sh_intc_readfn[] = {
+ sh_intc_read,
+ sh_intc_read,
+ sh_intc_read
+};
+
+static CPUWriteMemoryFunc * const sh_intc_writefn[] = {
+ sh_intc_write,
+ sh_intc_write,
+ sh_intc_write
+};
+
+struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id)
+{
+ if (id)
+ return desc->sources + id;
+
+ return NULL;
+}
+
+static void sh_intc_register(struct intc_desc *desc,
+ unsigned long address)
+{
+ if (address) {
+ cpu_register_physical_memory_offset(P4ADDR(address), 4,
+ desc->iomemtype, INTC_A7(address));
+ cpu_register_physical_memory_offset(A7ADDR(address), 4,
+ desc->iomemtype, INTC_A7(address));
+ }
+}
+
+static void sh_intc_register_source(struct intc_desc *desc,
+ intc_enum source,
+ struct intc_group *groups,
+ int nr_groups)
+{
+ unsigned int i, k;
+ struct intc_source *s;
+
+ if (desc->mask_regs) {
+ for (i = 0; i < desc->nr_mask_regs; i++) {
+ struct intc_mask_reg *mr = desc->mask_regs + i;
+
+ for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) {
+ if (mr->enum_ids[k] != source)
+ continue;
+
+ s = sh_intc_source(desc, mr->enum_ids[k]);
+ if (s)
+ s->enable_max++;
+ }
+ }
+ }
+
+ if (desc->prio_regs) {
+ for (i = 0; i < desc->nr_prio_regs; i++) {
+ struct intc_prio_reg *pr = desc->prio_regs + i;
+
+ for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) {
+ if (pr->enum_ids[k] != source)
+ continue;
+
+ s = sh_intc_source(desc, pr->enum_ids[k]);
+ if (s)
+ s->enable_max++;
+ }
+ }
+ }
+
+ if (groups) {
+ for (i = 0; i < nr_groups; i++) {
+ struct intc_group *gr = groups + i;
+
+ for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) {
+ if (gr->enum_ids[k] != source)
+ continue;
+
+ s = sh_intc_source(desc, gr->enum_ids[k]);
+ if (s)
+ s->enable_max++;
+ }
+ }
+ }
+
+}
+
+void sh_intc_register_sources(struct intc_desc *desc,
+ struct intc_vect *vectors,
+ int nr_vectors,
+ struct intc_group *groups,
+ int nr_groups)
+{
+ unsigned int i, k;
+ struct intc_source *s;
+
+ for (i = 0; i < nr_vectors; i++) {
+ struct intc_vect *vect = vectors + i;
+
+ sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
+ s = sh_intc_source(desc, vect->enum_id);
+ if (s)
+ s->vect = vect->vect;
+
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
+ vect->enum_id, s->vect, s->enable_count, s->enable_max);
+#endif
+ }
+
+ if (groups) {
+ for (i = 0; i < nr_groups; i++) {
+ struct intc_group *gr = groups + i;
+
+ s = sh_intc_source(desc, gr->enum_id);
+ s->next_enum_id = gr->enum_ids[0];
+
+ for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) {
+ if (!gr->enum_ids[k])
+ continue;
+
+ s = sh_intc_source(desc, gr->enum_ids[k - 1]);
+ s->next_enum_id = gr->enum_ids[k];
+ }
+
+#ifdef DEBUG_INTC_SOURCES
+ printf("sh_intc: registered group %d (%d/%d)\n",
+ gr->enum_id, s->enable_count, s->enable_max);
+#endif
+ }
+ }
+}
+
+int sh_intc_init(struct intc_desc *desc,
+ int nr_sources,
+ struct intc_mask_reg *mask_regs,
+ int nr_mask_regs,
+ struct intc_prio_reg *prio_regs,
+ int nr_prio_regs)
+{
+ unsigned int i;
+
+ desc->pending = 0;
+ desc->nr_sources = nr_sources;
+ desc->mask_regs = mask_regs;
+ desc->nr_mask_regs = nr_mask_regs;
+ desc->prio_regs = prio_regs;
+ desc->nr_prio_regs = nr_prio_regs;
+
+ i = sizeof(struct intc_source) * nr_sources;
+ desc->sources = qemu_mallocz(i);
+
+ for (i = 0; i < desc->nr_sources; i++) {
+ struct intc_source *source = desc->sources + i;
+
+ source->parent = desc;
+ }
+
+ desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources);
+
+ desc->iomemtype = cpu_register_io_memory(sh_intc_readfn,
+ sh_intc_writefn, desc,
+ DEVICE_NATIVE_ENDIAN);
+ if (desc->mask_regs) {
+ for (i = 0; i < desc->nr_mask_regs; i++) {
+ struct intc_mask_reg *mr = desc->mask_regs + i;
+
+ sh_intc_register(desc, mr->set_reg);
+ sh_intc_register(desc, mr->clr_reg);
+ }
+ }
+
+ if (desc->prio_regs) {
+ for (i = 0; i < desc->nr_prio_regs; i++) {
+ struct intc_prio_reg *pr = desc->prio_regs + i;
+
+ sh_intc_register(desc, pr->set_reg);
+ sh_intc_register(desc, pr->clr_reg);
+ }
+ }
+
+ return 0;
+}
+
+/* Assert level <n> IRL interrupt.
+ 0:deassert. 1:lowest priority,... 15:highest priority. */
+void sh_intc_set_irl(void *opaque, int n, int level)
+{
+ struct intc_source *s = opaque;
+ int i, irl = level ^ 15;
+ for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) {
+ if (i == irl)
+ sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1);
+ else
+ if (s->asserted)
+ sh_intc_toggle_source(s, 0, -1);
+ }
+}
diff --git a/hw/sh_intc.h b/hw/sh_intc.h
new file mode 100644
index 0000000..c117d6f
--- /dev/null
+++ b/hw/sh_intc.h
@@ -0,0 +1,80 @@
+#ifndef __SH_INTC_H__
+#define __SH_INTC_H__
+
+#include "qemu-common.h"
+#include "irq.h"
+
+typedef unsigned char intc_enum;
+
+struct intc_vect {
+ intc_enum enum_id;
+ unsigned short vect;
+};
+
+#define INTC_VECT(enum_id, vect) { enum_id, vect }
+
+struct intc_group {
+ intc_enum enum_id;
+ intc_enum enum_ids[32];
+};
+
+#define INTC_GROUP(enum_id, ...) { enum_id, { __VA_ARGS__ } }
+
+struct intc_mask_reg {
+ unsigned long set_reg, clr_reg, reg_width;
+ intc_enum enum_ids[32];
+ unsigned long value;
+};
+
+struct intc_prio_reg {
+ unsigned long set_reg, clr_reg, reg_width, field_width;
+ intc_enum enum_ids[16];
+ unsigned long value;
+};
+
+#define _INTC_ARRAY(a) a, ARRAY_SIZE(a)
+
+struct intc_source {
+ unsigned short vect;
+ intc_enum next_enum_id;
+
+ int asserted; /* emulates the interrupt signal line from device to intc */
+ int enable_count;
+ int enable_max;
+ int pending; /* emulates the result of signal and masking */
+ struct intc_desc *parent;
+};
+
+struct intc_desc {
+ qemu_irq *irqs;
+ struct intc_source *sources;
+ int nr_sources;
+ struct intc_mask_reg *mask_regs;
+ int nr_mask_regs;
+ struct intc_prio_reg *prio_regs;
+ int nr_prio_regs;
+ int iomemtype;
+ int pending; /* number of interrupt sources that has pending set */
+};
+
+int sh_intc_get_pending_vector(struct intc_desc *desc, int imask);
+struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id);
+void sh_intc_toggle_source(struct intc_source *source,
+ int enable_adj, int assert_adj);
+
+void sh_intc_register_sources(struct intc_desc *desc,
+ struct intc_vect *vectors,
+ int nr_vectors,
+ struct intc_group *groups,
+ int nr_groups);
+
+int sh_intc_init(struct intc_desc *desc,
+ int nr_sources,
+ struct intc_mask_reg *mask_regs,
+ int nr_mask_regs,
+ struct intc_prio_reg *prio_regs,
+ int nr_prio_regs);
+
+void sh_intc_set_irl(void *opaque, int n, int level);
+
+#endif /* __SH_INTC_H__ */
diff --git a/hw/sh_pci.c b/hw/sh_pci.c
new file mode 100644
index 0000000..e99d8db
--- /dev/null
+++ b/hw/sh_pci.c
@@ -0,0 +1,161 @@
+/*
+ * SuperH on-chip PCIC emulation.
+ *
+ * Copyright (c) 2008 Takashi YOSHII
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "sysbus.h"
+#include "sh.h"
+#include "pci.h"
+#include "pci_host.h"
+#include "bswap.h"
+
+typedef struct SHPCIState {
+ SysBusDevice busdev;
+ PCIBus *bus;
+ PCIDevice *dev;
+ qemu_irq irq[4];
+ int memconfig;
+ uint32_t par;
+ uint32_t mbr;
+ uint32_t iobr;
+} SHPCIState;
+
+static void sh_pci_reg_write (void *p, target_phys_addr_t addr, uint32_t val)
+{
+ SHPCIState *pcic = p;
+ switch(addr) {
+ case 0 ... 0xfc:
+ cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val);
+ break;
+ case 0x1c0:
+ pcic->par = val;
+ break;
+ case 0x1c4:
+ pcic->mbr = val & 0xff000001;
+ break;
+ case 0x1c8:
+ if ((val & 0xfffc0000) != (pcic->iobr & 0xfffc0000)) {
+ cpu_register_physical_memory(pcic->iobr & 0xfffc0000, 0x40000,
+ IO_MEM_UNASSIGNED);
+ pcic->iobr = val & 0xfffc0001;
+ isa_mmio_init(pcic->iobr & 0xfffc0000, 0x40000);
+ }
+ break;
+ case 0x220:
+ pci_data_write(pcic->bus, pcic->par, val, 4);
+ break;
+ }
+}
+
+static uint32_t sh_pci_reg_read (void *p, target_phys_addr_t addr)
+{
+ SHPCIState *pcic = p;
+ switch(addr) {
+ case 0 ... 0xfc:
+ return le32_to_cpup((uint32_t*)(pcic->dev->config + addr));
+ case 0x1c0:
+ return pcic->par;
+ case 0x1c4:
+ return pcic->mbr;
+ case 0x1c8:
+ return pcic->iobr;
+ case 0x220:
+ return pci_data_read(pcic->bus, pcic->par, 4);
+ }
+ return 0;
+}
+
+typedef struct {
+ CPUReadMemoryFunc * const r[3];
+ CPUWriteMemoryFunc * const w[3];
+} MemOp;
+
+static MemOp sh_pci_reg = {
+ { NULL, NULL, sh_pci_reg_read },
+ { NULL, NULL, sh_pci_reg_write },
+};
+
+static int sh_pci_map_irq(PCIDevice *d, int irq_num)
+{
+ return (d->devfn >> 3);
+}
+
+static void sh_pci_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+
+ qemu_set_irq(pic[irq_num], level);
+}
+
+static void sh_pci_map(SysBusDevice *dev, target_phys_addr_t base)
+{
+ SHPCIState *s = FROM_SYSBUS(SHPCIState, dev);
+
+ cpu_register_physical_memory(P4ADDR(base), 0x224, s->memconfig);
+ cpu_register_physical_memory(A7ADDR(base), 0x224, s->memconfig);
+
+ s->iobr = 0xfe240000;
+ isa_mmio_init(s->iobr, 0x40000);
+}
+
+static int sh_pci_init_device(SysBusDevice *dev)
+{
+ SHPCIState *s;
+ int i;
+
+ s = FROM_SYSBUS(SHPCIState, dev);
+ for (i = 0; i < 4; i++) {
+ sysbus_init_irq(dev, &s->irq[i]);
+ }
+ s->bus = pci_register_bus(&s->busdev.qdev, "pci",
+ sh_pci_set_irq, sh_pci_map_irq,
+ s->irq, PCI_DEVFN(0, 0), 4);
+ s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w,
+ s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio_cb(dev, 0x224, sh_pci_map);
+ s->dev = pci_create_simple(s->bus, PCI_DEVFN(0, 0), "sh_pci_host");
+ return 0;
+}
+
+static int sh_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_HITACHI);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_HITACHI_SH7751R);
+ pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT);
+ pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST |
+ PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
+ return 0;
+}
+
+static PCIDeviceInfo sh_pci_host_info = {
+ .qdev.name = "sh_pci_host",
+ .qdev.size = sizeof(PCIDevice),
+ .init = sh_pci_host_init,
+};
+
+static void sh_pci_register_devices(void)
+{
+ sysbus_register_dev("sh_pci", sizeof(SHPCIState),
+ sh_pci_init_device);
+ pci_qdev_register(&sh_pci_host_info);
+}
+
+device_init(sh_pci_register_devices)
diff --git a/hw/sh_serial.c b/hw/sh_serial.c
new file mode 100644
index 0000000..191f4a6
--- /dev/null
+++ b/hw/sh_serial.c
@@ -0,0 +1,401 @@
+/*
+ * QEMU SCI/SCIF serial port emulation
+ *
+ * Copyright (c) 2007 Magnus Damm
+ *
+ * Based on serial.c - QEMU 16450 UART emulation
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "sh.h"
+#include "qemu-char.h"
+
+//#define DEBUG_SERIAL
+
+#define SH_SERIAL_FLAG_TEND (1 << 0)
+#define SH_SERIAL_FLAG_TDE (1 << 1)
+#define SH_SERIAL_FLAG_RDF (1 << 2)
+#define SH_SERIAL_FLAG_BRK (1 << 3)
+#define SH_SERIAL_FLAG_DR (1 << 4)
+
+#define SH_RX_FIFO_LENGTH (16)
+
+typedef struct {
+ uint8_t smr;
+ uint8_t brr;
+ uint8_t scr;
+ uint8_t dr; /* ftdr / tdr */
+ uint8_t sr; /* fsr / ssr */
+ uint16_t fcr;
+ uint8_t sptr;
+
+ uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
+ uint8_t rx_cnt;
+ uint8_t rx_tail;
+ uint8_t rx_head;
+
+ int freq;
+ int feat;
+ int flags;
+ int rtrg;
+
+ CharDriverState *chr;
+
+ qemu_irq eri;
+ qemu_irq rxi;
+ qemu_irq txi;
+ qemu_irq tei;
+ qemu_irq bri;
+} sh_serial_state;
+
+static void sh_serial_clear_fifo(sh_serial_state * s)
+{
+ memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
+ s->rx_cnt = 0;
+ s->rx_head = 0;
+ s->rx_tail = 0;
+}
+
+static void sh_serial_write(void *opaque, uint32_t offs, uint32_t val)
+{
+ sh_serial_state *s = opaque;
+ unsigned char ch;
+
+#ifdef DEBUG_SERIAL
+ printf("sh_serial: write offs=0x%02x val=0x%02x\n",
+ offs, val);
+#endif
+ switch(offs) {
+ case 0x00: /* SMR */
+ s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
+ return;
+ case 0x04: /* BRR */
+ s->brr = val;
+ return;
+ case 0x08: /* SCR */
+ /* TODO : For SH7751, SCIF mask should be 0xfb. */
+ s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
+ if (!(val & (1 << 5)))
+ s->flags |= SH_SERIAL_FLAG_TEND;
+ if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
+ qemu_set_irq(s->txi, val & (1 << 7));
+ }
+ if (!(val & (1 << 6))) {
+ qemu_set_irq(s->rxi, 0);
+ }
+ return;
+ case 0x0c: /* FTDR / TDR */
+ if (s->chr) {
+ ch = val;
+ qemu_chr_write(s->chr, &ch, 1);
+ }
+ s->dr = val;
+ s->flags &= ~SH_SERIAL_FLAG_TDE;
+ return;
+#if 0
+ case 0x14: /* FRDR / RDR */
+ ret = 0;
+ break;
+#endif
+ }
+ if (s->feat & SH_SERIAL_FEAT_SCIF) {
+ switch(offs) {
+ case 0x10: /* FSR */
+ if (!(val & (1 << 6)))
+ s->flags &= ~SH_SERIAL_FLAG_TEND;
+ if (!(val & (1 << 5)))
+ s->flags &= ~SH_SERIAL_FLAG_TDE;
+ if (!(val & (1 << 4)))
+ s->flags &= ~SH_SERIAL_FLAG_BRK;
+ if (!(val & (1 << 1)))
+ s->flags &= ~SH_SERIAL_FLAG_RDF;
+ if (!(val & (1 << 0)))
+ s->flags &= ~SH_SERIAL_FLAG_DR;
+
+ if (!(val & (1 << 1)) || !(val & (1 << 0))) {
+ if (s->rxi) {
+ qemu_set_irq(s->rxi, 0);
+ }
+ }
+ return;
+ case 0x18: /* FCR */
+ s->fcr = val;
+ switch ((val >> 6) & 3) {
+ case 0:
+ s->rtrg = 1;
+ break;
+ case 1:
+ s->rtrg = 4;
+ break;
+ case 2:
+ s->rtrg = 8;
+ break;
+ case 3:
+ s->rtrg = 14;
+ break;
+ }
+ if (val & (1 << 1)) {
+ sh_serial_clear_fifo(s);
+ s->sr &= ~(1 << 1);
+ }
+
+ return;
+ case 0x20: /* SPTR */
+ s->sptr = val & 0xf3;
+ return;
+ case 0x24: /* LSR */
+ return;
+ }
+ }
+ else {
+ switch(offs) {
+#if 0
+ case 0x0c:
+ ret = s->dr;
+ break;
+ case 0x10:
+ ret = 0;
+ break;
+#endif
+ case 0x1c:
+ s->sptr = val & 0x8f;
+ return;
+ }
+ }
+
+ fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs);
+ abort();
+}
+
+static uint32_t sh_serial_read(void *opaque, uint32_t offs)
+{
+ sh_serial_state *s = opaque;
+ uint32_t ret = ~0;
+
+#if 0
+ switch(offs) {
+ case 0x00:
+ ret = s->smr;
+ break;
+ case 0x04:
+ ret = s->brr;
+ break;
+ case 0x08:
+ ret = s->scr;
+ break;
+ case 0x14:
+ ret = 0;
+ break;
+ }
+#endif
+ if (s->feat & SH_SERIAL_FEAT_SCIF) {
+ switch(offs) {
+ case 0x00: /* SMR */
+ ret = s->smr;
+ break;
+ case 0x08: /* SCR */
+ ret = s->scr;
+ break;
+ case 0x10: /* FSR */
+ ret = 0;
+ if (s->flags & SH_SERIAL_FLAG_TEND)
+ ret |= (1 << 6);
+ if (s->flags & SH_SERIAL_FLAG_TDE)
+ ret |= (1 << 5);
+ if (s->flags & SH_SERIAL_FLAG_BRK)
+ ret |= (1 << 4);
+ if (s->flags & SH_SERIAL_FLAG_RDF)
+ ret |= (1 << 1);
+ if (s->flags & SH_SERIAL_FLAG_DR)
+ ret |= (1 << 0);
+
+ if (s->scr & (1 << 5))
+ s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
+
+ break;
+ case 0x14:
+ if (s->rx_cnt > 0) {
+ ret = s->rx_fifo[s->rx_tail++];
+ s->rx_cnt--;
+ if (s->rx_tail == SH_RX_FIFO_LENGTH)
+ s->rx_tail = 0;
+ if (s->rx_cnt < s->rtrg)
+ s->flags &= ~SH_SERIAL_FLAG_RDF;
+ }
+ break;
+#if 0
+ case 0x18:
+ ret = s->fcr;
+ break;
+#endif
+ case 0x1c:
+ ret = s->rx_cnt;
+ break;
+ case 0x20:
+ ret = s->sptr;
+ break;
+ case 0x24:
+ ret = 0;
+ break;
+ }
+ }
+ else {
+ switch(offs) {
+#if 0
+ case 0x0c:
+ ret = s->dr;
+ break;
+ case 0x10:
+ ret = 0;
+ break;
+ case 0x14:
+ ret = s->rx_fifo[0];
+ break;
+#endif
+ case 0x1c:
+ ret = s->sptr;
+ break;
+ }
+ }
+#ifdef DEBUG_SERIAL
+ printf("sh_serial: read offs=0x%02x val=0x%x\n",
+ offs, ret);
+#endif
+
+ if (ret & ~((1 << 16) - 1)) {
+ fprintf(stderr, "sh_serial: unsupported read from 0x%02x\n", offs);
+ abort();
+ }
+
+ return ret;
+}
+
+static int sh_serial_can_receive(sh_serial_state *s)
+{
+ return s->scr & (1 << 4);
+}
+
+static void sh_serial_receive_break(sh_serial_state *s)
+{
+ if (s->feat & SH_SERIAL_FEAT_SCIF)
+ s->sr |= (1 << 4);
+}
+
+static int sh_serial_can_receive1(void *opaque)
+{
+ sh_serial_state *s = opaque;
+ return sh_serial_can_receive(s);
+}
+
+static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+ sh_serial_state *s = opaque;
+
+ if (s->feat & SH_SERIAL_FEAT_SCIF) {
+ int i;
+ for (i = 0; i < size; i++) {
+ if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
+ s->rx_fifo[s->rx_head++] = buf[i];
+ if (s->rx_head == SH_RX_FIFO_LENGTH) {
+ s->rx_head = 0;
+ }
+ s->rx_cnt++;
+ if (s->rx_cnt >= s->rtrg) {
+ s->flags |= SH_SERIAL_FLAG_RDF;
+ if (s->scr & (1 << 6) && s->rxi) {
+ qemu_set_irq(s->rxi, 1);
+ }
+ }
+ }
+ }
+ } else {
+ s->rx_fifo[0] = buf[0];
+ }
+}
+
+static void sh_serial_event(void *opaque, int event)
+{
+ sh_serial_state *s = opaque;
+ if (event == CHR_EVENT_BREAK)
+ sh_serial_receive_break(s);
+}
+
+static CPUReadMemoryFunc * const sh_serial_readfn[] = {
+ &sh_serial_read,
+ &sh_serial_read,
+ &sh_serial_read,
+};
+
+static CPUWriteMemoryFunc * const sh_serial_writefn[] = {
+ &sh_serial_write,
+ &sh_serial_write,
+ &sh_serial_write,
+};
+
+void sh_serial_init (target_phys_addr_t base, int feat,
+ uint32_t freq, CharDriverState *chr,
+ qemu_irq eri_source,
+ qemu_irq rxi_source,
+ qemu_irq txi_source,
+ qemu_irq tei_source,
+ qemu_irq bri_source)
+{
+ sh_serial_state *s;
+ int s_io_memory;
+
+ s = qemu_mallocz(sizeof(sh_serial_state));
+
+ s->feat = feat;
+ s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
+ s->rtrg = 1;
+
+ s->smr = 0;
+ s->brr = 0xff;
+ s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
+ s->sptr = 0;
+
+ if (feat & SH_SERIAL_FEAT_SCIF) {
+ s->fcr = 0;
+ }
+ else {
+ s->dr = 0xff;
+ }
+
+ sh_serial_clear_fifo(s);
+
+ s_io_memory = cpu_register_io_memory(sh_serial_readfn,
+ sh_serial_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(P4ADDR(base), 0x28, s_io_memory);
+ cpu_register_physical_memory(A7ADDR(base), 0x28, s_io_memory);
+
+ s->chr = chr;
+
+ if (chr)
+ qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
+ sh_serial_event, s);
+
+ s->eri = eri_source;
+ s->rxi = rxi_source;
+ s->txi = txi_source;
+ s->tei = tei_source;
+ s->bri = bri_source;
+}
diff --git a/hw/sh_timer.c b/hw/sh_timer.c
new file mode 100644
index 0000000..5eec6b7
--- /dev/null
+++ b/hw/sh_timer.c
@@ -0,0 +1,327 @@
+/*
+ * SuperH Timer modules.
+ *
+ * Copyright (c) 2007 Magnus Damm
+ * Based on arm_timer.c by Paul Brook
+ * Copyright (c) 2005-2006 CodeSourcery.
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "sh.h"
+#include "qemu-timer.h"
+
+//#define DEBUG_TIMER
+
+#define TIMER_TCR_TPSC (7 << 0)
+#define TIMER_TCR_CKEG (3 << 3)
+#define TIMER_TCR_UNIE (1 << 5)
+#define TIMER_TCR_ICPE (3 << 6)
+#define TIMER_TCR_UNF (1 << 8)
+#define TIMER_TCR_ICPF (1 << 9)
+#define TIMER_TCR_RESERVED (0x3f << 10)
+
+#define TIMER_FEAT_CAPT (1 << 0)
+#define TIMER_FEAT_EXTCLK (1 << 1)
+
+#define OFFSET_TCOR 0
+#define OFFSET_TCNT 1
+#define OFFSET_TCR 2
+#define OFFSET_TCPR 3
+
+typedef struct {
+ ptimer_state *timer;
+ uint32_t tcnt;
+ uint32_t tcor;
+ uint32_t tcr;
+ uint32_t tcpr;
+ int freq;
+ int int_level;
+ int old_level;
+ int feat;
+ int enabled;
+ qemu_irq irq;
+} sh_timer_state;
+
+/* Check all active timers, and schedule the next timer interrupt. */
+
+static void sh_timer_update(sh_timer_state *s)
+{
+ int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
+
+ if (new_level != s->old_level)
+ qemu_set_irq (s->irq, new_level);
+
+ s->old_level = s->int_level;
+ s->int_level = new_level;
+}
+
+static uint32_t sh_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+
+ switch (offset >> 2) {
+ case OFFSET_TCOR:
+ return s->tcor;
+ case OFFSET_TCNT:
+ return ptimer_get_count(s->timer);
+ case OFFSET_TCR:
+ return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
+ case OFFSET_TCPR:
+ if (s->feat & TIMER_FEAT_CAPT)
+ return s->tcpr;
+ default:
+ hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void sh_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+ int freq;
+
+ switch (offset >> 2) {
+ case OFFSET_TCOR:
+ s->tcor = value;
+ ptimer_set_limit(s->timer, s->tcor, 0);
+ break;
+ case OFFSET_TCNT:
+ s->tcnt = value;
+ ptimer_set_count(s->timer, s->tcnt);
+ break;
+ case OFFSET_TCR:
+ if (s->enabled) {
+ /* Pause the timer if it is running. This may cause some
+ inaccuracy dure to rounding, but avoids a whole lot of other
+ messyness. */
+ ptimer_stop(s->timer);
+ }
+ freq = s->freq;
+ /* ??? Need to recalculate expiry time after changing divisor. */
+ switch (value & TIMER_TCR_TPSC) {
+ case 0: freq >>= 2; break;
+ case 1: freq >>= 4; break;
+ case 2: freq >>= 6; break;
+ case 3: freq >>= 8; break;
+ case 4: freq >>= 10; break;
+ case 6:
+ case 7: if (s->feat & TIMER_FEAT_EXTCLK) break;
+ default: hw_error("sh_timer_write: Reserved TPSC value\n"); break;
+ }
+ switch ((value & TIMER_TCR_CKEG) >> 3) {
+ case 0: break;
+ case 1:
+ case 2:
+ case 3: if (s->feat & TIMER_FEAT_EXTCLK) break;
+ default: hw_error("sh_timer_write: Reserved CKEG value\n"); break;
+ }
+ switch ((value & TIMER_TCR_ICPE) >> 6) {
+ case 0: break;
+ case 2:
+ case 3: if (s->feat & TIMER_FEAT_CAPT) break;
+ default: hw_error("sh_timer_write: Reserved ICPE value\n"); break;
+ }
+ if ((value & TIMER_TCR_UNF) == 0)
+ s->int_level = 0;
+
+ value &= ~TIMER_TCR_UNF;
+
+ if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT)))
+ hw_error("sh_timer_write: Reserved ICPF value\n");
+
+ value &= ~TIMER_TCR_ICPF; /* capture not supported */
+
+ if (value & TIMER_TCR_RESERVED)
+ hw_error("sh_timer_write: Reserved TCR bits set\n");
+ s->tcr = value;
+ ptimer_set_limit(s->timer, s->tcor, 0);
+ ptimer_set_freq(s->timer, freq);
+ if (s->enabled) {
+ /* Restart the timer if still enabled. */
+ ptimer_run(s->timer, 0);
+ }
+ break;
+ case OFFSET_TCPR:
+ if (s->feat & TIMER_FEAT_CAPT) {
+ s->tcpr = value;
+ break;
+ }
+ default:
+ hw_error("sh_timer_write: Bad offset %x\n", (int)offset);
+ }
+ sh_timer_update(s);
+}
+
+static void sh_timer_start_stop(void *opaque, int enable)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+
+#ifdef DEBUG_TIMER
+ printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
+#endif
+
+ if (s->enabled && !enable) {
+ ptimer_stop(s->timer);
+ }
+ if (!s->enabled && enable) {
+ ptimer_run(s->timer, 0);
+ }
+ s->enabled = !!enable;
+
+#ifdef DEBUG_TIMER
+ printf("sh_timer_start_stop done %d\n", s->enabled);
+#endif
+}
+
+static void sh_timer_tick(void *opaque)
+{
+ sh_timer_state *s = (sh_timer_state *)opaque;
+ s->int_level = s->enabled;
+ sh_timer_update(s);
+}
+
+static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
+{
+ sh_timer_state *s;
+ QEMUBH *bh;
+
+ s = (sh_timer_state *)qemu_mallocz(sizeof(sh_timer_state));
+ s->freq = freq;
+ s->feat = feat;
+ s->tcor = 0xffffffff;
+ s->tcnt = 0xffffffff;
+ s->tcpr = 0xdeadbeef;
+ s->tcr = 0;
+ s->enabled = 0;
+ s->irq = irq;
+
+ bh = qemu_bh_new(sh_timer_tick, s);
+ s->timer = ptimer_init(bh);
+
+ sh_timer_write(s, OFFSET_TCOR >> 2, s->tcor);
+ sh_timer_write(s, OFFSET_TCNT >> 2, s->tcnt);
+ sh_timer_write(s, OFFSET_TCPR >> 2, s->tcpr);
+ sh_timer_write(s, OFFSET_TCR >> 2, s->tcpr);
+ /* ??? Save/restore. */
+ return s;
+}
+
+typedef struct {
+ void *timer[3];
+ int level[3];
+ uint32_t tocr;
+ uint32_t tstr;
+ int feat;
+} tmu012_state;
+
+static uint32_t tmu012_read(void *opaque, target_phys_addr_t offset)
+{
+ tmu012_state *s = (tmu012_state *)opaque;
+
+#ifdef DEBUG_TIMER
+ printf("tmu012_read 0x%lx\n", (unsigned long) offset);
+#endif
+
+ if (offset >= 0x20) {
+ if (!(s->feat & TMU012_FEAT_3CHAN))
+ hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
+ return sh_timer_read(s->timer[2], offset - 0x20);
+ }
+
+ if (offset >= 0x14)
+ return sh_timer_read(s->timer[1], offset - 0x14);
+
+ if (offset >= 0x08)
+ return sh_timer_read(s->timer[0], offset - 0x08);
+
+ if (offset == 4)
+ return s->tstr;
+
+ if ((s->feat & TMU012_FEAT_TOCR) && offset == 0)
+ return s->tocr;
+
+ hw_error("tmu012_write: Bad offset %x\n", (int)offset);
+ return 0;
+}
+
+static void tmu012_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ tmu012_state *s = (tmu012_state *)opaque;
+
+#ifdef DEBUG_TIMER
+ printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
+#endif
+
+ if (offset >= 0x20) {
+ if (!(s->feat & TMU012_FEAT_3CHAN))
+ hw_error("tmu012_write: Bad channel offset %x\n", (int)offset);
+ sh_timer_write(s->timer[2], offset - 0x20, value);
+ return;
+ }
+
+ if (offset >= 0x14) {
+ sh_timer_write(s->timer[1], offset - 0x14, value);
+ return;
+ }
+
+ if (offset >= 0x08) {
+ sh_timer_write(s->timer[0], offset - 0x08, value);
+ return;
+ }
+
+ if (offset == 4) {
+ sh_timer_start_stop(s->timer[0], value & (1 << 0));
+ sh_timer_start_stop(s->timer[1], value & (1 << 1));
+ if (s->feat & TMU012_FEAT_3CHAN)
+ sh_timer_start_stop(s->timer[2], value & (1 << 2));
+ else
+ if (value & (1 << 2))
+ hw_error("tmu012_write: Bad channel\n");
+
+ s->tstr = value;
+ return;
+ }
+
+ if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
+ s->tocr = value & (1 << 0);
+ }
+}
+
+static CPUReadMemoryFunc * const tmu012_readfn[] = {
+ tmu012_read,
+ tmu012_read,
+ tmu012_read
+};
+
+static CPUWriteMemoryFunc * const tmu012_writefn[] = {
+ tmu012_write,
+ tmu012_write,
+ tmu012_write
+};
+
+void tmu012_init(target_phys_addr_t base, int feat, uint32_t freq,
+ qemu_irq ch0_irq, qemu_irq ch1_irq,
+ qemu_irq ch2_irq0, qemu_irq ch2_irq1)
+{
+ int iomemtype;
+ tmu012_state *s;
+ int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
+
+ s = (tmu012_state *)qemu_mallocz(sizeof(tmu012_state));
+ s->feat = feat;
+ s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
+ s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
+ if (feat & TMU012_FEAT_3CHAN)
+ s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT,
+ ch2_irq0); /* ch2_irq1 not supported */
+ iomemtype = cpu_register_io_memory(tmu012_readfn,
+ tmu012_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(P4ADDR(base), 0x00001000, iomemtype);
+ cpu_register_physical_memory(A7ADDR(base), 0x00001000, iomemtype);
+ /* ??? Save/restore. */
+}
diff --git a/hw/sharpsl.h b/hw/sharpsl.h
new file mode 100644
index 0000000..0b3a774
--- /dev/null
+++ b/hw/sharpsl.h
@@ -0,0 +1,17 @@
+/*
+ * Common declarations for the Zaurii.
+ *
+ * This file is licensed under the GNU GPL.
+ */
+#ifndef QEMU_SHARPSL_H
+#define QEMU_SHARPSL_H
+
+#define zaurus_printf(format, ...) \
+ fprintf(stderr, "%s: " format, __FUNCTION__, ##__VA_ARGS__)
+
+/* zaurus.c */
+
+#define SL_PXA_PARAM_BASE 0xa0000a00
+void sl_bootparam_write(target_phys_addr_t ptr);
+
+#endif
diff --git a/hw/shix.c b/hw/shix.c
new file mode 100644
index 0000000..638bf16
--- /dev/null
+++ b/hw/shix.c
@@ -0,0 +1,104 @@
+/*
+ * SHIX 2.0 board description
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ Shix 2.0 board by Alexis Polti, described at
+ http://perso.enst.fr/~polti/realisations/shix20/
+
+ More information in target-sh4/README.sh4
+*/
+#include "hw.h"
+#include "pc.h"
+#include "sh.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "loader.h"
+
+#define BIOS_FILENAME "shix_bios.bin"
+#define BIOS_ADDRESS 0xA0000000
+
+void irq_info(Monitor *mon)
+{
+ /* XXXXX */
+}
+
+void pic_info(Monitor *mon)
+{
+ /* XXXXX */
+}
+
+static void shix_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ int ret;
+ CPUState *env;
+ struct SH7750State *s;
+
+ if (!cpu_model)
+ cpu_model = "any";
+
+ printf("Initializing CPU\n");
+ env = cpu_init(cpu_model);
+
+ /* Allocate memory space */
+ printf("Allocating ROM\n");
+ cpu_register_physical_memory(0x00000000, 0x00004000, IO_MEM_ROM);
+ printf("Allocating SDRAM 1\n");
+ cpu_register_physical_memory(0x08000000, 0x01000000, 0x00004000);
+ printf("Allocating SDRAM 2\n");
+ cpu_register_physical_memory(0x0c000000, 0x01000000, 0x01004000);
+
+ /* Load BIOS in 0 (and access it through P2, 0xA0000000) */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ printf("%s: load BIOS '%s'\n", __func__, bios_name);
+ ret = load_image_targphys(bios_name, 0, 0x4000);
+ if (ret < 0) { /* Check bios size */
+ fprintf(stderr, "ret=%d\n", ret);
+ fprintf(stderr, "qemu: could not load SHIX bios '%s'\n",
+ bios_name);
+ exit(1);
+ }
+
+ /* Register peripherals */
+ s = sh7750_init(env);
+ /* XXXXX Check success */
+ tc58128_init(s, "shix_linux_nand.bin", NULL);
+ fprintf(stderr, "initialization terminated\n");
+}
+
+static QEMUMachine shix_machine = {
+ .name = "shix",
+ .desc = "shix card",
+ .init = shix_init,
+ .is_default = 1,
+};
+
+static void shix_machine_init(void)
+{
+ qemu_register_machine(&shix_machine);
+}
+
+machine_init(shix_machine_init);
diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c
new file mode 100644
index 0000000..a83e5b8
--- /dev/null
+++ b/hw/slavio_intctl.c
@@ -0,0 +1,463 @@
+/*
+ * QEMU Sparc SLAVIO interrupt controller emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sun4m.h"
+#include "monitor.h"
+#include "sysbus.h"
+#include "trace.h"
+
+//#define DEBUG_IRQ_COUNT
+
+/*
+ * Registers of interrupt controller in sun4m.
+ *
+ * This is the interrupt controller part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * There is a system master controller and one for each cpu.
+ *
+ */
+
+#define MAX_CPUS 16
+#define MAX_PILS 16
+
+struct SLAVIO_INTCTLState;
+
+typedef struct SLAVIO_CPUINTCTLState {
+ uint32_t intreg_pending;
+ struct SLAVIO_INTCTLState *master;
+ uint32_t cpu;
+ uint32_t irl_out;
+} SLAVIO_CPUINTCTLState;
+
+typedef struct SLAVIO_INTCTLState {
+ SysBusDevice busdev;
+ uint32_t intregm_pending;
+ uint32_t intregm_disabled;
+ uint32_t target_cpu;
+#ifdef DEBUG_IRQ_COUNT
+ uint64_t irq_count[32];
+#endif
+ qemu_irq cpu_irqs[MAX_CPUS][MAX_PILS];
+ SLAVIO_CPUINTCTLState slaves[MAX_CPUS];
+} SLAVIO_INTCTLState;
+
+#define INTCTL_MAXADDR 0xf
+#define INTCTL_SIZE (INTCTL_MAXADDR + 1)
+#define INTCTLM_SIZE 0x14
+#define MASTER_IRQ_MASK ~0x0fa2007f
+#define MASTER_DISABLE 0x80000000
+#define CPU_SOFTIRQ_MASK 0xfffe0000
+#define CPU_IRQ_INT15_IN (1 << 15)
+#define CPU_IRQ_TIMER_IN (1 << 14)
+
+static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs);
+
+// per-cpu interrupt controller
+static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SLAVIO_CPUINTCTLState *s = opaque;
+ uint32_t saddr, ret;
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ case 0:
+ ret = s->intreg_pending;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ trace_slavio_intctl_mem_readl(s->cpu, addr, ret);
+
+ return ret;
+}
+
+static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SLAVIO_CPUINTCTLState *s = opaque;
+ uint32_t saddr;
+
+ saddr = addr >> 2;
+ trace_slavio_intctl_mem_writel(s->cpu, addr, val);
+ switch (saddr) {
+ case 1: // clear pending softints
+ val &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN;
+ s->intreg_pending &= ~val;
+ slavio_check_interrupts(s->master, 1);
+ trace_slavio_intctl_mem_writel_clear(s->cpu, val, s->intreg_pending);
+ break;
+ case 2: // set softint
+ val &= CPU_SOFTIRQ_MASK;
+ s->intreg_pending |= val;
+ slavio_check_interrupts(s->master, 1);
+ trace_slavio_intctl_mem_writel_set(s->cpu, val, s->intreg_pending);
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const slavio_intctl_mem_read[3] = {
+ NULL,
+ NULL,
+ slavio_intctl_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const slavio_intctl_mem_write[3] = {
+ NULL,
+ NULL,
+ slavio_intctl_mem_writel,
+};
+
+// master system interrupt controller
+static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t saddr, ret;
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ case 0:
+ ret = s->intregm_pending & ~MASTER_DISABLE;
+ break;
+ case 1:
+ ret = s->intregm_disabled & MASTER_IRQ_MASK;
+ break;
+ case 4:
+ ret = s->target_cpu;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ trace_slavio_intctlm_mem_readl(addr, ret);
+
+ return ret;
+}
+
+static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t saddr;
+
+ saddr = addr >> 2;
+ trace_slavio_intctlm_mem_writel(addr, val);
+ switch (saddr) {
+ case 2: // clear (enable)
+ // Force clear unused bits
+ val &= MASTER_IRQ_MASK;
+ s->intregm_disabled &= ~val;
+ trace_slavio_intctlm_mem_writel_enable(val, s->intregm_disabled);
+ slavio_check_interrupts(s, 1);
+ break;
+ case 3: // set (disable; doesn't affect pending)
+ // Force clear unused bits
+ val &= MASTER_IRQ_MASK;
+ s->intregm_disabled |= val;
+ slavio_check_interrupts(s, 1);
+ trace_slavio_intctlm_mem_writel_disable(val, s->intregm_disabled);
+ break;
+ case 4:
+ s->target_cpu = val & (MAX_CPUS - 1);
+ slavio_check_interrupts(s, 1);
+ trace_slavio_intctlm_mem_writel_target(s->target_cpu);
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const slavio_intctlm_mem_read[3] = {
+ NULL,
+ NULL,
+ slavio_intctlm_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const slavio_intctlm_mem_write[3] = {
+ NULL,
+ NULL,
+ slavio_intctlm_mem_writel,
+};
+
+void slavio_pic_info(Monitor *mon, DeviceState *dev)
+{
+ SysBusDevice *sd;
+ SLAVIO_INTCTLState *s;
+ int i;
+
+ sd = sysbus_from_qdev(dev);
+ s = FROM_SYSBUS(SLAVIO_INTCTLState, sd);
+ for (i = 0; i < MAX_CPUS; i++) {
+ monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i,
+ s->slaves[i].intreg_pending);
+ }
+ monitor_printf(mon, "master: pending 0x%08x, disabled 0x%08x\n",
+ s->intregm_pending, s->intregm_disabled);
+}
+
+void slavio_irq_info(Monitor *mon, DeviceState *dev)
+{
+#ifndef DEBUG_IRQ_COUNT
+ monitor_printf(mon, "irq statistic code not compiled.\n");
+#else
+ SysBusDevice *sd;
+ SLAVIO_INTCTLState *s;
+ int i;
+ int64_t count;
+
+ sd = sysbus_from_qdev(dev);
+ s = FROM_SYSBUS(SLAVIO_INTCTLState, sd);
+ monitor_printf(mon, "IRQ statistics:\n");
+ for (i = 0; i < 32; i++) {
+ count = s->irq_count[i];
+ if (count > 0)
+ monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
+ }
+#endif
+}
+
+static const uint32_t intbit_to_level[] = {
+ 2, 3, 5, 7, 9, 11, 13, 2, 3, 5, 7, 9, 11, 13, 12, 12,
+ 6, 13, 4, 10, 8, 9, 11, 0, 0, 0, 0, 15, 15, 15, 15, 0,
+};
+
+static void slavio_check_interrupts(SLAVIO_INTCTLState *s, int set_irqs)
+{
+ uint32_t pending = s->intregm_pending, pil_pending;
+ unsigned int i, j;
+
+ pending &= ~s->intregm_disabled;
+
+ trace_slavio_check_interrupts(pending, s->intregm_disabled);
+ for (i = 0; i < MAX_CPUS; i++) {
+ pil_pending = 0;
+
+ /* If we are the current interrupt target, get hard interrupts */
+ if (pending && !(s->intregm_disabled & MASTER_DISABLE) &&
+ (i == s->target_cpu)) {
+ for (j = 0; j < 32; j++) {
+ if ((pending & (1 << j)) && intbit_to_level[j]) {
+ pil_pending |= 1 << intbit_to_level[j];
+ }
+ }
+ }
+
+ /* Calculate current pending hard interrupts for display */
+ s->slaves[i].intreg_pending &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN |
+ CPU_IRQ_TIMER_IN;
+ if (i == s->target_cpu) {
+ for (j = 0; j < 32; j++) {
+ if ((s->intregm_pending & (1 << j)) && intbit_to_level[j]) {
+ s->slaves[i].intreg_pending |= 1 << intbit_to_level[j];
+ }
+ }
+ }
+
+ /* Level 15 and CPU timer interrupts are only masked when
+ the MASTER_DISABLE bit is set */
+ if (!(s->intregm_disabled & MASTER_DISABLE)) {
+ pil_pending |= s->slaves[i].intreg_pending &
+ (CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN);
+ }
+
+ /* Add soft interrupts */
+ pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16;
+
+ if (set_irqs) {
+ /* Since there is not really an interrupt 0 (and pil_pending
+ * and irl_out bit zero are thus always zero) there is no need
+ * to do anything with cpu_irqs[i][0] and it is OK not to do
+ * the j=0 iteration of this loop.
+ */
+ for (j = MAX_PILS-1; j > 0; j--) {
+ if (pil_pending & (1 << j)) {
+ if (!(s->slaves[i].irl_out & (1 << j))) {
+ qemu_irq_raise(s->cpu_irqs[i][j]);
+ }
+ } else {
+ if (s->slaves[i].irl_out & (1 << j)) {
+ qemu_irq_lower(s->cpu_irqs[i][j]);
+ }
+ }
+ }
+ }
+ s->slaves[i].irl_out = pil_pending;
+ }
+}
+
+/*
+ * "irq" here is the bit number in the system interrupt register to
+ * separate serial and keyboard interrupts sharing a level.
+ */
+static void slavio_set_irq(void *opaque, int irq, int level)
+{
+ SLAVIO_INTCTLState *s = opaque;
+ uint32_t mask = 1 << irq;
+ uint32_t pil = intbit_to_level[irq];
+ unsigned int i;
+
+ trace_slavio_set_irq(s->target_cpu, irq, pil, level);
+ if (pil > 0) {
+ if (level) {
+#ifdef DEBUG_IRQ_COUNT
+ s->irq_count[pil]++;
+#endif
+ s->intregm_pending |= mask;
+ if (pil == 15) {
+ for (i = 0; i < MAX_CPUS; i++) {
+ s->slaves[i].intreg_pending |= 1 << pil;
+ }
+ }
+ } else {
+ s->intregm_pending &= ~mask;
+ if (pil == 15) {
+ for (i = 0; i < MAX_CPUS; i++) {
+ s->slaves[i].intreg_pending &= ~(1 << pil);
+ }
+ }
+ }
+ slavio_check_interrupts(s, 1);
+ }
+}
+
+static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level)
+{
+ SLAVIO_INTCTLState *s = opaque;
+
+ trace_slavio_set_timer_irq_cpu(cpu, level);
+
+ if (level) {
+ s->slaves[cpu].intreg_pending |= CPU_IRQ_TIMER_IN;
+ } else {
+ s->slaves[cpu].intreg_pending &= ~CPU_IRQ_TIMER_IN;
+ }
+
+ slavio_check_interrupts(s, 1);
+}
+
+static void slavio_set_irq_all(void *opaque, int irq, int level)
+{
+ if (irq < 32) {
+ slavio_set_irq(opaque, irq, level);
+ } else {
+ slavio_set_timer_irq_cpu(opaque, irq - 32, level);
+ }
+}
+
+static int vmstate_intctl_post_load(void *opaque, int version_id)
+{
+ SLAVIO_INTCTLState *s = opaque;
+
+ slavio_check_interrupts(s, 0);
+ return 0;
+}
+
+static const VMStateDescription vmstate_intctl_cpu = {
+ .name ="slavio_intctl_cpu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(intreg_pending, SLAVIO_CPUINTCTLState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_intctl = {
+ .name ="slavio_intctl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = vmstate_intctl_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT_ARRAY(slaves, SLAVIO_INTCTLState, MAX_CPUS, 1,
+ vmstate_intctl_cpu, SLAVIO_CPUINTCTLState),
+ VMSTATE_UINT32(intregm_pending, SLAVIO_INTCTLState),
+ VMSTATE_UINT32(intregm_disabled, SLAVIO_INTCTLState),
+ VMSTATE_UINT32(target_cpu, SLAVIO_INTCTLState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void slavio_intctl_reset(DeviceState *d)
+{
+ SLAVIO_INTCTLState *s = container_of(d, SLAVIO_INTCTLState, busdev.qdev);
+ int i;
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ s->slaves[i].intreg_pending = 0;
+ s->slaves[i].irl_out = 0;
+ }
+ s->intregm_disabled = ~MASTER_IRQ_MASK;
+ s->intregm_pending = 0;
+ s->target_cpu = 0;
+ slavio_check_interrupts(s, 0);
+}
+
+static int slavio_intctl_init1(SysBusDevice *dev)
+{
+ SLAVIO_INTCTLState *s = FROM_SYSBUS(SLAVIO_INTCTLState, dev);
+ int io_memory;
+ unsigned int i, j;
+
+ qdev_init_gpio_in(&dev->qdev, slavio_set_irq_all, 32 + MAX_CPUS);
+ io_memory = cpu_register_io_memory(slavio_intctlm_mem_read,
+ slavio_intctlm_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, INTCTLM_SIZE, io_memory);
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ for (j = 0; j < MAX_PILS; j++) {
+ sysbus_init_irq(dev, &s->cpu_irqs[i][j]);
+ }
+ io_memory = cpu_register_io_memory(slavio_intctl_mem_read,
+ slavio_intctl_mem_write,
+ &s->slaves[i],
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, INTCTL_SIZE, io_memory);
+ s->slaves[i].cpu = i;
+ s->slaves[i].master = s;
+ }
+
+ return 0;
+}
+
+static SysBusDeviceInfo slavio_intctl_info = {
+ .init = slavio_intctl_init1,
+ .qdev.name = "slavio_intctl",
+ .qdev.size = sizeof(SLAVIO_INTCTLState),
+ .qdev.vmsd = &vmstate_intctl,
+ .qdev.reset = slavio_intctl_reset,
+};
+
+static void slavio_intctl_register_devices(void)
+{
+ sysbus_register_withprop(&slavio_intctl_info);
+}
+
+device_init(slavio_intctl_register_devices)
diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c
new file mode 100644
index 0000000..198360d
--- /dev/null
+++ b/hw/slavio_misc.c
@@ -0,0 +1,499 @@
+/*
+ * QEMU Sparc SLAVIO aux io port emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysemu.h"
+#include "sysbus.h"
+#include "trace.h"
+
+/*
+ * This is the auxio port, chip control and system control part of
+ * chip STP2001 (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * This also includes the PMC CPU idle controller.
+ */
+
+typedef struct MiscState {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t dummy;
+ uint8_t config;
+ uint8_t aux1, aux2;
+ uint8_t diag, mctrl;
+ uint8_t sysctrl;
+ uint16_t leds;
+ qemu_irq fdc_tc;
+} MiscState;
+
+typedef struct APCState {
+ SysBusDevice busdev;
+ qemu_irq cpu_halt;
+} APCState;
+
+#define MISC_SIZE 1
+#define SYSCTRL_SIZE 4
+
+#define AUX1_TC 0x02
+
+#define AUX2_PWROFF 0x01
+#define AUX2_PWRINTCLR 0x02
+#define AUX2_PWRFAIL 0x20
+
+#define CFG_PWRINTEN 0x08
+
+#define SYS_RESET 0x01
+#define SYS_RESETSTAT 0x02
+
+static void slavio_misc_update_irq(void *opaque)
+{
+ MiscState *s = opaque;
+
+ if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) {
+ trace_slavio_misc_update_irq_raise();
+ qemu_irq_raise(s->irq);
+ } else {
+ trace_slavio_misc_update_irq_lower();
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void slavio_misc_reset(DeviceState *d)
+{
+ MiscState *s = container_of(d, MiscState, busdev.qdev);
+
+ // Diagnostic and system control registers not cleared in reset
+ s->config = s->aux1 = s->aux2 = s->mctrl = 0;
+}
+
+static void slavio_set_power_fail(void *opaque, int irq, int power_failing)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_set_power_fail(power_failing, s->config);
+ if (power_failing && (s->config & CFG_PWRINTEN)) {
+ s->aux2 |= AUX2_PWRFAIL;
+ } else {
+ s->aux2 &= ~AUX2_PWRFAIL;
+ }
+ slavio_misc_update_irq(s);
+}
+
+static void slavio_cfg_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_cfg_mem_writeb(val & 0xff);
+ s->config = val & 0xff;
+ slavio_misc_update_irq(s);
+}
+
+static uint32_t slavio_cfg_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ ret = s->config;
+ trace_slavio_cfg_mem_readb(ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const slavio_cfg_mem_read[3] = {
+ slavio_cfg_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const slavio_cfg_mem_write[3] = {
+ slavio_cfg_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static void slavio_diag_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_diag_mem_writeb(val & 0xff);
+ s->diag = val & 0xff;
+}
+
+static uint32_t slavio_diag_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ ret = s->diag;
+ trace_slavio_diag_mem_readb(ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const slavio_diag_mem_read[3] = {
+ slavio_diag_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const slavio_diag_mem_write[3] = {
+ slavio_diag_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static void slavio_mdm_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_mdm_mem_writeb(val & 0xff);
+ s->mctrl = val & 0xff;
+}
+
+static uint32_t slavio_mdm_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ ret = s->mctrl;
+ trace_slavio_mdm_mem_readb(ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const slavio_mdm_mem_read[3] = {
+ slavio_mdm_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const slavio_mdm_mem_write[3] = {
+ slavio_mdm_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static void slavio_aux1_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_aux1_mem_writeb(val & 0xff);
+ if (val & AUX1_TC) {
+ // Send a pulse to floppy terminal count line
+ if (s->fdc_tc) {
+ qemu_irq_raise(s->fdc_tc);
+ qemu_irq_lower(s->fdc_tc);
+ }
+ val &= ~AUX1_TC;
+ }
+ s->aux1 = val & 0xff;
+}
+
+static uint32_t slavio_aux1_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ ret = s->aux1;
+ trace_slavio_aux1_mem_readb(ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const slavio_aux1_mem_read[3] = {
+ slavio_aux1_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const slavio_aux1_mem_write[3] = {
+ slavio_aux1_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static void slavio_aux2_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ val &= AUX2_PWRINTCLR | AUX2_PWROFF;
+ trace_slavio_aux2_mem_writeb(val & 0xff);
+ val |= s->aux2 & AUX2_PWRFAIL;
+ if (val & AUX2_PWRINTCLR) // Clear Power Fail int
+ val &= AUX2_PWROFF;
+ s->aux2 = val;
+ if (val & AUX2_PWROFF)
+ qemu_system_shutdown_request();
+ slavio_misc_update_irq(s);
+}
+
+static uint32_t slavio_aux2_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ ret = s->aux2;
+ trace_slavio_aux2_mem_readb(ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const slavio_aux2_mem_read[3] = {
+ slavio_aux2_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const slavio_aux2_mem_write[3] = {
+ slavio_aux2_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static void apc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ APCState *s = opaque;
+
+ trace_apc_mem_writeb(val & 0xff);
+ qemu_irq_raise(s->cpu_halt);
+}
+
+static uint32_t apc_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t ret = 0;
+
+ trace_apc_mem_readb(ret);
+ return ret;
+}
+
+static CPUReadMemoryFunc * const apc_mem_read[3] = {
+ apc_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const apc_mem_write[3] = {
+ apc_mem_writeb,
+ NULL,
+ NULL,
+};
+
+static uint32_t slavio_sysctrl_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (addr) {
+ case 0:
+ ret = s->sysctrl;
+ break;
+ default:
+ break;
+ }
+ trace_slavio_sysctrl_mem_readl(ret);
+ return ret;
+}
+
+static void slavio_sysctrl_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_sysctrl_mem_writel(val);
+ switch (addr) {
+ case 0:
+ if (val & SYS_RESET) {
+ s->sysctrl = SYS_RESETSTAT;
+ qemu_system_reset_request();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const slavio_sysctrl_mem_read[3] = {
+ NULL,
+ NULL,
+ slavio_sysctrl_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const slavio_sysctrl_mem_write[3] = {
+ NULL,
+ NULL,
+ slavio_sysctrl_mem_writel,
+};
+
+static uint32_t slavio_led_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ MiscState *s = opaque;
+ uint32_t ret = 0;
+
+ switch (addr) {
+ case 0:
+ ret = s->leds;
+ break;
+ default:
+ break;
+ }
+ trace_slavio_led_mem_readw(ret);
+ return ret;
+}
+
+static void slavio_led_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ MiscState *s = opaque;
+
+ trace_slavio_led_mem_readw(val & 0xffff);
+ switch (addr) {
+ case 0:
+ s->leds = val;
+ break;
+ default:
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const slavio_led_mem_read[3] = {
+ NULL,
+ slavio_led_mem_readw,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const slavio_led_mem_write[3] = {
+ NULL,
+ slavio_led_mem_writew,
+ NULL,
+};
+
+static const VMStateDescription vmstate_misc = {
+ .name ="slavio_misc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(dummy, MiscState),
+ VMSTATE_UINT8(config, MiscState),
+ VMSTATE_UINT8(aux1, MiscState),
+ VMSTATE_UINT8(aux2, MiscState),
+ VMSTATE_UINT8(diag, MiscState),
+ VMSTATE_UINT8(mctrl, MiscState),
+ VMSTATE_UINT8(sysctrl, MiscState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int apc_init1(SysBusDevice *dev)
+{
+ APCState *s = FROM_SYSBUS(APCState, dev);
+ int io;
+
+ sysbus_init_irq(dev, &s->cpu_halt);
+
+ /* Power management (APC) XXX: not a Slavio device */
+ io = cpu_register_io_memory(apc_mem_read, apc_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+ return 0;
+}
+
+static int slavio_misc_init1(SysBusDevice *dev)
+{
+ MiscState *s = FROM_SYSBUS(MiscState, dev);
+ int io;
+
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(dev, &s->fdc_tc);
+
+ /* 8 bit registers */
+ /* Slavio control */
+ io = cpu_register_io_memory(slavio_cfg_mem_read,
+ slavio_cfg_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+
+ /* Diagnostics */
+ io = cpu_register_io_memory(slavio_diag_mem_read,
+ slavio_diag_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+
+ /* Modem control */
+ io = cpu_register_io_memory(slavio_mdm_mem_read,
+ slavio_mdm_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+
+ /* 16 bit registers */
+ /* ss600mp diag LEDs */
+ io = cpu_register_io_memory(slavio_led_mem_read,
+ slavio_led_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+
+ /* 32 bit registers */
+ /* System control */
+ io = cpu_register_io_memory(slavio_sysctrl_mem_read,
+ slavio_sysctrl_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, SYSCTRL_SIZE, io);
+
+ /* AUX 1 (Misc System Functions) */
+ io = cpu_register_io_memory(slavio_aux1_mem_read,
+ slavio_aux1_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+
+ /* AUX 2 (Software Powerdown Control) */
+ io = cpu_register_io_memory(slavio_aux2_mem_read,
+ slavio_aux2_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, MISC_SIZE, io);
+
+ qdev_init_gpio_in(&dev->qdev, slavio_set_power_fail, 1);
+
+ return 0;
+}
+
+static SysBusDeviceInfo slavio_misc_info = {
+ .init = slavio_misc_init1,
+ .qdev.name = "slavio_misc",
+ .qdev.size = sizeof(MiscState),
+ .qdev.vmsd = &vmstate_misc,
+ .qdev.reset = slavio_misc_reset,
+};
+
+static SysBusDeviceInfo apc_info = {
+ .init = apc_init1,
+ .qdev.name = "apc",
+ .qdev.size = sizeof(MiscState),
+};
+
+static void slavio_misc_register_devices(void)
+{
+ sysbus_register_withprop(&slavio_misc_info);
+ sysbus_register_withprop(&apc_info);
+}
+
+device_init(slavio_misc_register_devices)
diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c
new file mode 100644
index 0000000..5511313
--- /dev/null
+++ b/hw/slavio_timer.c
@@ -0,0 +1,424 @@
+/*
+ * QEMU Sparc SLAVIO timer controller emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sun4m.h"
+#include "qemu-timer.h"
+#include "sysbus.h"
+#include "trace.h"
+
+/*
+ * Registers of hardware timer in sun4m.
+ *
+ * This is the timer/counter part of chip STP2001 (Slave I/O), also
+ * produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
+ * are zero. Bit 31 is 1 when count has been reached.
+ *
+ * Per-CPU timers interrupt local CPU, system timer uses normal
+ * interrupt routing.
+ *
+ */
+
+#define MAX_CPUS 16
+
+typedef struct CPUTimerState {
+ qemu_irq irq;
+ ptimer_state *timer;
+ uint32_t count, counthigh, reached;
+ uint64_t limit;
+ // processor only
+ uint32_t running;
+} CPUTimerState;
+
+typedef struct SLAVIO_TIMERState {
+ SysBusDevice busdev;
+ uint32_t num_cpus;
+ CPUTimerState cputimer[MAX_CPUS + 1];
+ uint32_t cputimer_mode;
+} SLAVIO_TIMERState;
+
+typedef struct TimerContext {
+ SLAVIO_TIMERState *s;
+ unsigned int timer_index; /* 0 for system, 1 ... MAX_CPUS for CPU timers */
+} TimerContext;
+
+#define SYS_TIMER_SIZE 0x14
+#define CPU_TIMER_SIZE 0x10
+
+#define TIMER_LIMIT 0
+#define TIMER_COUNTER 1
+#define TIMER_COUNTER_NORST 2
+#define TIMER_STATUS 3
+#define TIMER_MODE 4
+
+#define TIMER_COUNT_MASK32 0xfffffe00
+#define TIMER_LIMIT_MASK32 0x7fffffff
+#define TIMER_MAX_COUNT64 0x7ffffffffffffe00ULL
+#define TIMER_MAX_COUNT32 0x7ffffe00ULL
+#define TIMER_REACHED 0x80000000
+#define TIMER_PERIOD 500ULL // 500ns
+#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1)
+#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9)
+
+static int slavio_timer_is_user(TimerContext *tc)
+{
+ SLAVIO_TIMERState *s = tc->s;
+ unsigned int timer_index = tc->timer_index;
+
+ return timer_index != 0 && (s->cputimer_mode & (1 << (timer_index - 1)));
+}
+
+// Update count, set irq, update expire_time
+// Convert from ptimer countdown units
+static void slavio_timer_get_out(CPUTimerState *t)
+{
+ uint64_t count, limit;
+
+ if (t->limit == 0) { /* free-run system or processor counter */
+ limit = TIMER_MAX_COUNT32;
+ } else {
+ limit = t->limit;
+ }
+ count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer));
+
+ trace_slavio_timer_get_out(t->limit, t->counthigh, t->count);
+ t->count = count & TIMER_COUNT_MASK32;
+ t->counthigh = count >> 32;
+}
+
+// timer callback
+static void slavio_timer_irq(void *opaque)
+{
+ TimerContext *tc = opaque;
+ SLAVIO_TIMERState *s = tc->s;
+ CPUTimerState *t = &s->cputimer[tc->timer_index];
+
+ slavio_timer_get_out(t);
+ trace_slavio_timer_irq(t->counthigh, t->count);
+ /* if limit is 0 (free-run), there will be no match */
+ if (t->limit != 0) {
+ t->reached = TIMER_REACHED;
+ }
+ /* there is no interrupt if user timer or free-run */
+ if (!slavio_timer_is_user(tc) && t->limit != 0) {
+ qemu_irq_raise(t->irq);
+ }
+}
+
+static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ TimerContext *tc = opaque;
+ SLAVIO_TIMERState *s = tc->s;
+ uint32_t saddr, ret;
+ unsigned int timer_index = tc->timer_index;
+ CPUTimerState *t = &s->cputimer[timer_index];
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ case TIMER_LIMIT:
+ // read limit (system counter mode) or read most signifying
+ // part of counter (user mode)
+ if (slavio_timer_is_user(tc)) {
+ // read user timer MSW
+ slavio_timer_get_out(t);
+ ret = t->counthigh | t->reached;
+ } else {
+ // read limit
+ // clear irq
+ qemu_irq_lower(t->irq);
+ t->reached = 0;
+ ret = t->limit & TIMER_LIMIT_MASK32;
+ }
+ break;
+ case TIMER_COUNTER:
+ // read counter and reached bit (system mode) or read lsbits
+ // of counter (user mode)
+ slavio_timer_get_out(t);
+ if (slavio_timer_is_user(tc)) { // read user timer LSW
+ ret = t->count & TIMER_MAX_COUNT64;
+ } else { // read limit
+ ret = (t->count & TIMER_MAX_COUNT32) |
+ t->reached;
+ }
+ break;
+ case TIMER_STATUS:
+ // only available in processor counter/timer
+ // read start/stop status
+ if (timer_index > 0) {
+ ret = t->running;
+ } else {
+ ret = 0;
+ }
+ break;
+ case TIMER_MODE:
+ // only available in system counter
+ // read user/system mode
+ ret = s->cputimer_mode;
+ break;
+ default:
+ trace_slavio_timer_mem_readl_invalid(addr);
+ ret = 0;
+ break;
+ }
+ trace_slavio_timer_mem_readl(addr, ret);
+ return ret;
+}
+
+static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ TimerContext *tc = opaque;
+ SLAVIO_TIMERState *s = tc->s;
+ uint32_t saddr;
+ unsigned int timer_index = tc->timer_index;
+ CPUTimerState *t = &s->cputimer[timer_index];
+
+ trace_slavio_timer_mem_writel(addr, val);
+ saddr = addr >> 2;
+ switch (saddr) {
+ case TIMER_LIMIT:
+ if (slavio_timer_is_user(tc)) {
+ uint64_t count;
+
+ // set user counter MSW, reset counter
+ t->limit = TIMER_MAX_COUNT64;
+ t->counthigh = val & (TIMER_MAX_COUNT64 >> 32);
+ t->reached = 0;
+ count = ((uint64_t)t->counthigh << 32) | t->count;
+ trace_slavio_timer_mem_writel_limit(timer_index, count);
+ ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
+ } else {
+ // set limit, reset counter
+ qemu_irq_lower(t->irq);
+ t->limit = val & TIMER_MAX_COUNT32;
+ if (t->timer) {
+ if (t->limit == 0) { /* free-run */
+ ptimer_set_limit(t->timer,
+ LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
+ } else {
+ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1);
+ }
+ }
+ }
+ break;
+ case TIMER_COUNTER:
+ if (slavio_timer_is_user(tc)) {
+ uint64_t count;
+
+ // set user counter LSW, reset counter
+ t->limit = TIMER_MAX_COUNT64;
+ t->count = val & TIMER_MAX_COUNT64;
+ t->reached = 0;
+ count = ((uint64_t)t->counthigh) << 32 | t->count;
+ trace_slavio_timer_mem_writel_limit(timer_index, count);
+ ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count));
+ } else {
+ trace_slavio_timer_mem_writel_counter_invalid();
+ }
+ break;
+ case TIMER_COUNTER_NORST:
+ // set limit without resetting counter
+ t->limit = val & TIMER_MAX_COUNT32;
+ if (t->limit == 0) { /* free-run */
+ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0);
+ } else {
+ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0);
+ }
+ break;
+ case TIMER_STATUS:
+ if (slavio_timer_is_user(tc)) {
+ // start/stop user counter
+ if ((val & 1) && !t->running) {
+ trace_slavio_timer_mem_writel_status_start(timer_index);
+ ptimer_run(t->timer, 0);
+ t->running = 1;
+ } else if (!(val & 1) && t->running) {
+ trace_slavio_timer_mem_writel_status_stop(timer_index);
+ ptimer_stop(t->timer);
+ t->running = 0;
+ }
+ }
+ break;
+ case TIMER_MODE:
+ if (timer_index == 0) {
+ unsigned int i;
+
+ for (i = 0; i < s->num_cpus; i++) {
+ unsigned int processor = 1 << i;
+ CPUTimerState *curr_timer = &s->cputimer[i + 1];
+
+ // check for a change in timer mode for this processor
+ if ((val & processor) != (s->cputimer_mode & processor)) {
+ if (val & processor) { // counter -> user timer
+ qemu_irq_lower(curr_timer->irq);
+ // counters are always running
+ ptimer_stop(curr_timer->timer);
+ curr_timer->running = 0;
+ // user timer limit is always the same
+ curr_timer->limit = TIMER_MAX_COUNT64;
+ ptimer_set_limit(curr_timer->timer,
+ LIMIT_TO_PERIODS(curr_timer->limit),
+ 1);
+ // set this processors user timer bit in config
+ // register
+ s->cputimer_mode |= processor;
+ trace_slavio_timer_mem_writel_mode_user(timer_index);
+ } else { // user timer -> counter
+ // stop the user timer if it is running
+ if (curr_timer->running) {
+ ptimer_stop(curr_timer->timer);
+ }
+ // start the counter
+ ptimer_run(curr_timer->timer, 0);
+ curr_timer->running = 1;
+ // clear this processors user timer bit in config
+ // register
+ s->cputimer_mode &= ~processor;
+ trace_slavio_timer_mem_writel_mode_counter(timer_index);
+ }
+ }
+ }
+ } else {
+ trace_slavio_timer_mem_writel_mode_invalid();
+ }
+ break;
+ default:
+ trace_slavio_timer_mem_writel_invalid(addr);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const slavio_timer_mem_read[3] = {
+ NULL,
+ NULL,
+ slavio_timer_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const slavio_timer_mem_write[3] = {
+ NULL,
+ NULL,
+ slavio_timer_mem_writel,
+};
+
+static const VMStateDescription vmstate_timer = {
+ .name ="timer",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(limit, CPUTimerState),
+ VMSTATE_UINT32(count, CPUTimerState),
+ VMSTATE_UINT32(counthigh, CPUTimerState),
+ VMSTATE_UINT32(reached, CPUTimerState),
+ VMSTATE_UINT32(running, CPUTimerState),
+ VMSTATE_PTIMER(timer, CPUTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_slavio_timer = {
+ .name ="slavio_timer",
+ .version_id = 3,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .fields = (VMStateField []) {
+ VMSTATE_STRUCT_ARRAY(cputimer, SLAVIO_TIMERState, MAX_CPUS + 1, 3,
+ vmstate_timer, CPUTimerState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void slavio_timer_reset(DeviceState *d)
+{
+ SLAVIO_TIMERState *s = container_of(d, SLAVIO_TIMERState, busdev.qdev);
+ unsigned int i;
+ CPUTimerState *curr_timer;
+
+ for (i = 0; i <= MAX_CPUS; i++) {
+ curr_timer = &s->cputimer[i];
+ curr_timer->limit = 0;
+ curr_timer->count = 0;
+ curr_timer->reached = 0;
+ if (i <= s->num_cpus) {
+ ptimer_set_limit(curr_timer->timer,
+ LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1);
+ ptimer_run(curr_timer->timer, 0);
+ curr_timer->running = 1;
+ }
+ }
+ s->cputimer_mode = 0;
+}
+
+static int slavio_timer_init1(SysBusDevice *dev)
+{
+ int io;
+ SLAVIO_TIMERState *s = FROM_SYSBUS(SLAVIO_TIMERState, dev);
+ QEMUBH *bh;
+ unsigned int i;
+ TimerContext *tc;
+
+ for (i = 0; i <= MAX_CPUS; i++) {
+ tc = qemu_mallocz(sizeof(TimerContext));
+ tc->s = s;
+ tc->timer_index = i;
+
+ bh = qemu_bh_new(slavio_timer_irq, tc);
+ s->cputimer[i].timer = ptimer_init(bh);
+ ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD);
+
+ io = cpu_register_io_memory(slavio_timer_mem_read,
+ slavio_timer_mem_write, tc,
+ DEVICE_NATIVE_ENDIAN);
+ if (i == 0) {
+ sysbus_init_mmio(dev, SYS_TIMER_SIZE, io);
+ } else {
+ sysbus_init_mmio(dev, CPU_TIMER_SIZE, io);
+ }
+
+ sysbus_init_irq(dev, &s->cputimer[i].irq);
+ }
+
+ return 0;
+}
+
+static SysBusDeviceInfo slavio_timer_info = {
+ .init = slavio_timer_init1,
+ .qdev.name = "slavio_timer",
+ .qdev.size = sizeof(SLAVIO_TIMERState),
+ .qdev.vmsd = &vmstate_slavio_timer,
+ .qdev.reset = slavio_timer_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void slavio_timer_register_devices(void)
+{
+ sysbus_register_withprop(&slavio_timer_info);
+}
+
+device_init(slavio_timer_register_devices)
diff --git a/hw/sm501.c b/hw/sm501.c
new file mode 100644
index 0000000..0f0bf96
--- /dev/null
+++ b/hw/sm501.c
@@ -0,0 +1,1457 @@
+/*
+ * QEMU SM501 Device
+ *
+ * Copyright (c) 2008 Shin-ichiro KAWASAKI
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include "hw.h"
+#include "pc.h"
+#include "console.h"
+#include "devices.h"
+#include "sysbus.h"
+#include "qdev-addr.h"
+#include "range.h"
+
+/*
+ * Status: 2010/05/07
+ * - Minimum implementation for Linux console : mmio regs and CRT layer.
+ * - 2D grapihcs acceleration partially supported : only fill rectangle.
+ *
+ * TODO:
+ * - Panel support
+ * - Touch panel support
+ * - USB support
+ * - UART support
+ * - More 2D graphics engine support
+ * - Performance tuning
+ */
+
+//#define DEBUG_SM501
+//#define DEBUG_BITBLT
+
+#ifdef DEBUG_SM501
+#define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define SM501_DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+
+#define MMIO_BASE_OFFSET 0x3e00000
+
+/* SM501 register definitions taken from "linux/include/linux/sm501-regs.h" */
+
+/* System Configuration area */
+/* System config base */
+#define SM501_SYS_CONFIG (0x000000)
+
+/* config 1 */
+#define SM501_SYSTEM_CONTROL (0x000000)
+
+#define SM501_SYSCTRL_PANEL_TRISTATE (1<<0)
+#define SM501_SYSCTRL_MEM_TRISTATE (1<<1)
+#define SM501_SYSCTRL_CRT_TRISTATE (1<<2)
+
+#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3<<4)
+#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0<<4)
+#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1<<4)
+#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2<<4)
+#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3<<4)
+
+#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1<<6)
+#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1<<7)
+#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1<<11)
+#define SM501_SYSCTRL_PCI_BURST_READ_EN (1<<15)
+
+/* miscellaneous control */
+
+#define SM501_MISC_CONTROL (0x000004)
+
+#define SM501_MISC_BUS_SH (0x0)
+#define SM501_MISC_BUS_PCI (0x1)
+#define SM501_MISC_BUS_XSCALE (0x2)
+#define SM501_MISC_BUS_NEC (0x6)
+#define SM501_MISC_BUS_MASK (0x7)
+
+#define SM501_MISC_VR_62MB (1<<3)
+#define SM501_MISC_CDR_RESET (1<<7)
+#define SM501_MISC_USB_LB (1<<8)
+#define SM501_MISC_USB_SLAVE (1<<9)
+#define SM501_MISC_BL_1 (1<<10)
+#define SM501_MISC_MC (1<<11)
+#define SM501_MISC_DAC_POWER (1<<12)
+#define SM501_MISC_IRQ_INVERT (1<<16)
+#define SM501_MISC_SH (1<<17)
+
+#define SM501_MISC_HOLD_EMPTY (0<<18)
+#define SM501_MISC_HOLD_8 (1<<18)
+#define SM501_MISC_HOLD_16 (2<<18)
+#define SM501_MISC_HOLD_24 (3<<18)
+#define SM501_MISC_HOLD_32 (4<<18)
+#define SM501_MISC_HOLD_MASK (7<<18)
+
+#define SM501_MISC_FREQ_12 (1<<24)
+#define SM501_MISC_PNL_24BIT (1<<25)
+#define SM501_MISC_8051_LE (1<<26)
+
+
+
+#define SM501_GPIO31_0_CONTROL (0x000008)
+#define SM501_GPIO63_32_CONTROL (0x00000C)
+#define SM501_DRAM_CONTROL (0x000010)
+
+/* command list */
+#define SM501_ARBTRTN_CONTROL (0x000014)
+
+/* command list */
+#define SM501_COMMAND_LIST_STATUS (0x000024)
+
+/* interrupt debug */
+#define SM501_RAW_IRQ_STATUS (0x000028)
+#define SM501_RAW_IRQ_CLEAR (0x000028)
+#define SM501_IRQ_STATUS (0x00002C)
+#define SM501_IRQ_MASK (0x000030)
+#define SM501_DEBUG_CONTROL (0x000034)
+
+/* power management */
+#define SM501_POWERMODE_P2X_SRC (1<<29)
+#define SM501_POWERMODE_V2X_SRC (1<<20)
+#define SM501_POWERMODE_M_SRC (1<<12)
+#define SM501_POWERMODE_M1_SRC (1<<4)
+
+#define SM501_CURRENT_GATE (0x000038)
+#define SM501_CURRENT_CLOCK (0x00003C)
+#define SM501_POWER_MODE_0_GATE (0x000040)
+#define SM501_POWER_MODE_0_CLOCK (0x000044)
+#define SM501_POWER_MODE_1_GATE (0x000048)
+#define SM501_POWER_MODE_1_CLOCK (0x00004C)
+#define SM501_SLEEP_MODE_GATE (0x000050)
+#define SM501_POWER_MODE_CONTROL (0x000054)
+
+/* power gates for units within the 501 */
+#define SM501_GATE_HOST (0)
+#define SM501_GATE_MEMORY (1)
+#define SM501_GATE_DISPLAY (2)
+#define SM501_GATE_2D_ENGINE (3)
+#define SM501_GATE_CSC (4)
+#define SM501_GATE_ZVPORT (5)
+#define SM501_GATE_GPIO (6)
+#define SM501_GATE_UART0 (7)
+#define SM501_GATE_UART1 (8)
+#define SM501_GATE_SSP (10)
+#define SM501_GATE_USB_HOST (11)
+#define SM501_GATE_USB_GADGET (12)
+#define SM501_GATE_UCONTROLLER (17)
+#define SM501_GATE_AC97 (18)
+
+/* panel clock */
+#define SM501_CLOCK_P2XCLK (24)
+/* crt clock */
+#define SM501_CLOCK_V2XCLK (16)
+/* main clock */
+#define SM501_CLOCK_MCLK (8)
+/* SDRAM controller clock */
+#define SM501_CLOCK_M1XCLK (0)
+
+/* config 2 */
+#define SM501_PCI_MASTER_BASE (0x000058)
+#define SM501_ENDIAN_CONTROL (0x00005C)
+#define SM501_DEVICEID (0x000060)
+/* 0x050100A0 */
+
+#define SM501_DEVICEID_SM501 (0x05010000)
+#define SM501_DEVICEID_IDMASK (0xffff0000)
+#define SM501_DEVICEID_REVMASK (0x000000ff)
+
+#define SM501_PLLCLOCK_COUNT (0x000064)
+#define SM501_MISC_TIMING (0x000068)
+#define SM501_CURRENT_SDRAM_CLOCK (0x00006C)
+
+#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074)
+
+/* GPIO base */
+#define SM501_GPIO (0x010000)
+#define SM501_GPIO_DATA_LOW (0x00)
+#define SM501_GPIO_DATA_HIGH (0x04)
+#define SM501_GPIO_DDR_LOW (0x08)
+#define SM501_GPIO_DDR_HIGH (0x0C)
+#define SM501_GPIO_IRQ_SETUP (0x10)
+#define SM501_GPIO_IRQ_STATUS (0x14)
+#define SM501_GPIO_IRQ_RESET (0x14)
+
+/* I2C controller base */
+#define SM501_I2C (0x010040)
+#define SM501_I2C_BYTE_COUNT (0x00)
+#define SM501_I2C_CONTROL (0x01)
+#define SM501_I2C_STATUS (0x02)
+#define SM501_I2C_RESET (0x02)
+#define SM501_I2C_SLAVE_ADDRESS (0x03)
+#define SM501_I2C_DATA (0x04)
+
+/* SSP base */
+#define SM501_SSP (0x020000)
+
+/* Uart 0 base */
+#define SM501_UART0 (0x030000)
+
+/* Uart 1 base */
+#define SM501_UART1 (0x030020)
+
+/* USB host port base */
+#define SM501_USB_HOST (0x040000)
+
+/* USB slave/gadget base */
+#define SM501_USB_GADGET (0x060000)
+
+/* USB slave/gadget data port base */
+#define SM501_USB_GADGET_DATA (0x070000)
+
+/* Display controller/video engine base */
+#define SM501_DC (0x080000)
+
+/* common defines for the SM501 address registers */
+#define SM501_ADDR_FLIP (1<<31)
+#define SM501_ADDR_EXT (1<<27)
+#define SM501_ADDR_CS1 (1<<26)
+#define SM501_ADDR_MASK (0x3f << 26)
+
+#define SM501_FIFO_MASK (0x3 << 16)
+#define SM501_FIFO_1 (0x0 << 16)
+#define SM501_FIFO_3 (0x1 << 16)
+#define SM501_FIFO_7 (0x2 << 16)
+#define SM501_FIFO_11 (0x3 << 16)
+
+/* common registers for panel and the crt */
+#define SM501_OFF_DC_H_TOT (0x000)
+#define SM501_OFF_DC_V_TOT (0x008)
+#define SM501_OFF_DC_H_SYNC (0x004)
+#define SM501_OFF_DC_V_SYNC (0x00C)
+
+#define SM501_DC_PANEL_CONTROL (0x000)
+
+#define SM501_DC_PANEL_CONTROL_FPEN (1<<27)
+#define SM501_DC_PANEL_CONTROL_BIAS (1<<26)
+#define SM501_DC_PANEL_CONTROL_DATA (1<<25)
+#define SM501_DC_PANEL_CONTROL_VDD (1<<24)
+#define SM501_DC_PANEL_CONTROL_DP (1<<23)
+
+#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21)
+#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21)
+#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21)
+
+#define SM501_DC_PANEL_CONTROL_DE (1<<20)
+
+#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18)
+#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18)
+#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18)
+
+#define SM501_DC_PANEL_CONTROL_CP (1<<14)
+#define SM501_DC_PANEL_CONTROL_VSP (1<<13)
+#define SM501_DC_PANEL_CONTROL_HSP (1<<12)
+#define SM501_DC_PANEL_CONTROL_CK (1<<9)
+#define SM501_DC_PANEL_CONTROL_TE (1<<8)
+#define SM501_DC_PANEL_CONTROL_VPD (1<<7)
+#define SM501_DC_PANEL_CONTROL_VP (1<<6)
+#define SM501_DC_PANEL_CONTROL_HPD (1<<5)
+#define SM501_DC_PANEL_CONTROL_HP (1<<4)
+#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3)
+#define SM501_DC_PANEL_CONTROL_EN (1<<2)
+
+#define SM501_DC_PANEL_CONTROL_8BPP (0<<0)
+#define SM501_DC_PANEL_CONTROL_16BPP (1<<0)
+#define SM501_DC_PANEL_CONTROL_32BPP (2<<0)
+
+
+#define SM501_DC_PANEL_PANNING_CONTROL (0x004)
+#define SM501_DC_PANEL_COLOR_KEY (0x008)
+#define SM501_DC_PANEL_FB_ADDR (0x00C)
+#define SM501_DC_PANEL_FB_OFFSET (0x010)
+#define SM501_DC_PANEL_FB_WIDTH (0x014)
+#define SM501_DC_PANEL_FB_HEIGHT (0x018)
+#define SM501_DC_PANEL_TL_LOC (0x01C)
+#define SM501_DC_PANEL_BR_LOC (0x020)
+#define SM501_DC_PANEL_H_TOT (0x024)
+#define SM501_DC_PANEL_H_SYNC (0x028)
+#define SM501_DC_PANEL_V_TOT (0x02C)
+#define SM501_DC_PANEL_V_SYNC (0x030)
+#define SM501_DC_PANEL_CUR_LINE (0x034)
+
+#define SM501_DC_VIDEO_CONTROL (0x040)
+#define SM501_DC_VIDEO_FB0_ADDR (0x044)
+#define SM501_DC_VIDEO_FB_WIDTH (0x048)
+#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C)
+#define SM501_DC_VIDEO_TL_LOC (0x050)
+#define SM501_DC_VIDEO_BR_LOC (0x054)
+#define SM501_DC_VIDEO_SCALE (0x058)
+#define SM501_DC_VIDEO_INIT_SCALE (0x05C)
+#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060)
+#define SM501_DC_VIDEO_FB1_ADDR (0x064)
+#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068)
+
+#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080)
+#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084)
+#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088)
+#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C)
+#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090)
+#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094)
+#define SM501_DC_VIDEO_ALPHA_SCALE (0x098)
+#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C)
+#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0)
+#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4)
+
+#define SM501_DC_PANEL_HWC_BASE (0x0F0)
+#define SM501_DC_PANEL_HWC_ADDR (0x0F0)
+#define SM501_DC_PANEL_HWC_LOC (0x0F4)
+#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8)
+#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC)
+
+#define SM501_HWC_EN (1<<31)
+
+#define SM501_OFF_HWC_ADDR (0x00)
+#define SM501_OFF_HWC_LOC (0x04)
+#define SM501_OFF_HWC_COLOR_1_2 (0x08)
+#define SM501_OFF_HWC_COLOR_3 (0x0C)
+
+#define SM501_DC_ALPHA_CONTROL (0x100)
+#define SM501_DC_ALPHA_FB_ADDR (0x104)
+#define SM501_DC_ALPHA_FB_OFFSET (0x108)
+#define SM501_DC_ALPHA_TL_LOC (0x10C)
+#define SM501_DC_ALPHA_BR_LOC (0x110)
+#define SM501_DC_ALPHA_CHROMA_KEY (0x114)
+#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118)
+
+#define SM501_DC_CRT_CONTROL (0x200)
+
+#define SM501_DC_CRT_CONTROL_TVP (1<<15)
+#define SM501_DC_CRT_CONTROL_CP (1<<14)
+#define SM501_DC_CRT_CONTROL_VSP (1<<13)
+#define SM501_DC_CRT_CONTROL_HSP (1<<12)
+#define SM501_DC_CRT_CONTROL_VS (1<<11)
+#define SM501_DC_CRT_CONTROL_BLANK (1<<10)
+#define SM501_DC_CRT_CONTROL_SEL (1<<9)
+#define SM501_DC_CRT_CONTROL_TE (1<<8)
+#define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4)
+#define SM501_DC_CRT_CONTROL_GAMMA (1<<3)
+#define SM501_DC_CRT_CONTROL_ENABLE (1<<2)
+
+#define SM501_DC_CRT_CONTROL_8BPP (0<<0)
+#define SM501_DC_CRT_CONTROL_16BPP (1<<0)
+#define SM501_DC_CRT_CONTROL_32BPP (2<<0)
+
+#define SM501_DC_CRT_FB_ADDR (0x204)
+#define SM501_DC_CRT_FB_OFFSET (0x208)
+#define SM501_DC_CRT_H_TOT (0x20C)
+#define SM501_DC_CRT_H_SYNC (0x210)
+#define SM501_DC_CRT_V_TOT (0x214)
+#define SM501_DC_CRT_V_SYNC (0x218)
+#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C)
+#define SM501_DC_CRT_CUR_LINE (0x220)
+#define SM501_DC_CRT_MONITOR_DETECT (0x224)
+
+#define SM501_DC_CRT_HWC_BASE (0x230)
+#define SM501_DC_CRT_HWC_ADDR (0x230)
+#define SM501_DC_CRT_HWC_LOC (0x234)
+#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238)
+#define SM501_DC_CRT_HWC_COLOR_3 (0x23C)
+
+#define SM501_DC_PANEL_PALETTE (0x400)
+
+#define SM501_DC_VIDEO_PALETTE (0x800)
+
+#define SM501_DC_CRT_PALETTE (0xC00)
+
+/* Zoom Video port base */
+#define SM501_ZVPORT (0x090000)
+
+/* AC97/I2S base */
+#define SM501_AC97 (0x0A0000)
+
+/* 8051 micro controller base */
+#define SM501_UCONTROLLER (0x0B0000)
+
+/* 8051 micro controller SRAM base */
+#define SM501_UCONTROLLER_SRAM (0x0C0000)
+
+/* DMA base */
+#define SM501_DMA (0x0D0000)
+
+/* 2d engine base */
+#define SM501_2D_ENGINE (0x100000)
+#define SM501_2D_SOURCE (0x00)
+#define SM501_2D_DESTINATION (0x04)
+#define SM501_2D_DIMENSION (0x08)
+#define SM501_2D_CONTROL (0x0C)
+#define SM501_2D_PITCH (0x10)
+#define SM501_2D_FOREGROUND (0x14)
+#define SM501_2D_BACKGROUND (0x18)
+#define SM501_2D_STRETCH (0x1C)
+#define SM501_2D_COLOR_COMPARE (0x20)
+#define SM501_2D_COLOR_COMPARE_MASK (0x24)
+#define SM501_2D_MASK (0x28)
+#define SM501_2D_CLIP_TL (0x2C)
+#define SM501_2D_CLIP_BR (0x30)
+#define SM501_2D_MONO_PATTERN_LOW (0x34)
+#define SM501_2D_MONO_PATTERN_HIGH (0x38)
+#define SM501_2D_WINDOW_WIDTH (0x3C)
+#define SM501_2D_SOURCE_BASE (0x40)
+#define SM501_2D_DESTINATION_BASE (0x44)
+#define SM501_2D_ALPHA (0x48)
+#define SM501_2D_WRAP (0x4C)
+#define SM501_2D_STATUS (0x50)
+
+#define SM501_CSC_Y_SOURCE_BASE (0xC8)
+#define SM501_CSC_CONSTANTS (0xCC)
+#define SM501_CSC_Y_SOURCE_X (0xD0)
+#define SM501_CSC_Y_SOURCE_Y (0xD4)
+#define SM501_CSC_U_SOURCE_BASE (0xD8)
+#define SM501_CSC_V_SOURCE_BASE (0xDC)
+#define SM501_CSC_SOURCE_DIMENSION (0xE0)
+#define SM501_CSC_SOURCE_PITCH (0xE4)
+#define SM501_CSC_DESTINATION (0xE8)
+#define SM501_CSC_DESTINATION_DIMENSION (0xEC)
+#define SM501_CSC_DESTINATION_PITCH (0xF0)
+#define SM501_CSC_SCALE_FACTOR (0xF4)
+#define SM501_CSC_DESTINATION_BASE (0xF8)
+#define SM501_CSC_CONTROL (0xFC)
+
+/* 2d engine data port base */
+#define SM501_2D_ENGINE_DATA (0x110000)
+
+/* end of register definitions */
+
+#define SM501_HWC_WIDTH (64)
+#define SM501_HWC_HEIGHT (64)
+
+/* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */
+static const uint32_t sm501_mem_local_size[] = {
+ [0] = 4*1024*1024,
+ [1] = 8*1024*1024,
+ [2] = 16*1024*1024,
+ [3] = 32*1024*1024,
+ [4] = 64*1024*1024,
+ [5] = 2*1024*1024,
+};
+#define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index]
+
+typedef struct SM501State {
+ /* graphic console status */
+ DisplayState *ds;
+
+ /* status & internal resources */
+ target_phys_addr_t base;
+ uint32_t local_mem_size_index;
+ uint8_t * local_mem;
+ ram_addr_t local_mem_offset;
+ uint32_t last_width;
+ uint32_t last_height;
+
+ /* mmio registers */
+ uint32_t system_control;
+ uint32_t misc_control;
+ uint32_t gpio_31_0_control;
+ uint32_t gpio_63_32_control;
+ uint32_t dram_control;
+ uint32_t irq_mask;
+ uint32_t misc_timing;
+ uint32_t power_mode_control;
+
+ uint32_t uart0_ier;
+ uint32_t uart0_lcr;
+ uint32_t uart0_mcr;
+ uint32_t uart0_scr;
+
+ uint8_t dc_palette[0x400 * 3];
+
+ uint32_t dc_panel_control;
+ uint32_t dc_panel_panning_control;
+ uint32_t dc_panel_fb_addr;
+ uint32_t dc_panel_fb_offset;
+ uint32_t dc_panel_fb_width;
+ uint32_t dc_panel_fb_height;
+ uint32_t dc_panel_tl_location;
+ uint32_t dc_panel_br_location;
+ uint32_t dc_panel_h_total;
+ uint32_t dc_panel_h_sync;
+ uint32_t dc_panel_v_total;
+ uint32_t dc_panel_v_sync;
+
+ uint32_t dc_panel_hwc_addr;
+ uint32_t dc_panel_hwc_location;
+ uint32_t dc_panel_hwc_color_1_2;
+ uint32_t dc_panel_hwc_color_3;
+
+ uint32_t dc_crt_control;
+ uint32_t dc_crt_fb_addr;
+ uint32_t dc_crt_fb_offset;
+ uint32_t dc_crt_h_total;
+ uint32_t dc_crt_h_sync;
+ uint32_t dc_crt_v_total;
+ uint32_t dc_crt_v_sync;
+
+ uint32_t dc_crt_hwc_addr;
+ uint32_t dc_crt_hwc_location;
+ uint32_t dc_crt_hwc_color_1_2;
+ uint32_t dc_crt_hwc_color_3;
+
+ uint32_t twoD_source;
+ uint32_t twoD_destination;
+ uint32_t twoD_dimension;
+ uint32_t twoD_control;
+ uint32_t twoD_pitch;
+ uint32_t twoD_foreground;
+ uint32_t twoD_stretch;
+ uint32_t twoD_color_compare_mask;
+ uint32_t twoD_mask;
+ uint32_t twoD_window_width;
+ uint32_t twoD_source_base;
+ uint32_t twoD_destination_base;
+
+} SM501State;
+
+static uint32_t get_local_mem_size_index(uint32_t size)
+{
+ uint32_t norm_size = 0;
+ int i, index = 0;
+
+ for (i = 0; i < ARRAY_SIZE(sm501_mem_local_size); i++) {
+ uint32_t new_size = sm501_mem_local_size[i];
+ if (new_size >= size) {
+ if (norm_size == 0 || norm_size > new_size) {
+ norm_size = new_size;
+ index = i;
+ }
+ }
+ }
+
+ return index;
+}
+
+/**
+ * Check the availability of hardware cursor.
+ * @param crt 0 for PANEL, 1 for CRT.
+ */
+static inline int is_hwc_enabled(SM501State *state, int crt)
+{
+ uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr;
+ return addr & 0x80000000;
+}
+
+/**
+ * Get the address which holds cursor pattern data.
+ * @param crt 0 for PANEL, 1 for CRT.
+ */
+static inline uint32_t get_hwc_address(SM501State *state, int crt)
+{
+ uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr;
+ return (addr & 0x03FFFFF0)/* >> 4*/;
+}
+
+/**
+ * Get the cursor position in y coordinate.
+ * @param crt 0 for PANEL, 1 for CRT.
+ */
+static inline uint32_t get_hwc_y(SM501State *state, int crt)
+{
+ uint32_t location = crt ? state->dc_crt_hwc_location
+ : state->dc_panel_hwc_location;
+ return (location & 0x07FF0000) >> 16;
+}
+
+/**
+ * Get the cursor position in x coordinate.
+ * @param crt 0 for PANEL, 1 for CRT.
+ */
+static inline uint32_t get_hwc_x(SM501State *state, int crt)
+{
+ uint32_t location = crt ? state->dc_crt_hwc_location
+ : state->dc_panel_hwc_location;
+ return location & 0x000007FF;
+}
+
+/**
+ * Get the cursor position in x coordinate.
+ * @param crt 0 for PANEL, 1 for CRT.
+ * @param index 0, 1, 2 or 3 which specifies color of corsor dot.
+ */
+static inline uint16_t get_hwc_color(SM501State *state, int crt, int index)
+{
+ uint16_t color_reg = 0;
+ uint16_t color_565 = 0;
+
+ if (index == 0) {
+ return 0;
+ }
+
+ switch (index) {
+ case 1:
+ case 2:
+ color_reg = crt ? state->dc_crt_hwc_color_1_2
+ : state->dc_panel_hwc_color_1_2;
+ break;
+ case 3:
+ color_reg = crt ? state->dc_crt_hwc_color_3
+ : state->dc_panel_hwc_color_3;
+ break;
+ default:
+ printf("invalid hw cursor color.\n");
+ abort();
+ }
+
+ switch (index) {
+ case 1:
+ case 3:
+ color_565 = (uint16_t)(color_reg & 0xFFFF);
+ break;
+ case 2:
+ color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF);
+ break;
+ }
+ return color_565;
+}
+
+static int within_hwc_y_range(SM501State *state, int y, int crt)
+{
+ int hwc_y = get_hwc_y(state, crt);
+ return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT);
+}
+
+static void sm501_2d_operation(SM501State * s)
+{
+ /* obtain operation parameters */
+ int operation = (s->twoD_control >> 16) & 0x1f;
+ int rtl = s->twoD_control & 0x8000000;
+ int src_x = (s->twoD_source >> 16) & 0x01FFF;
+ int src_y = s->twoD_source & 0xFFFF;
+ int dst_x = (s->twoD_destination >> 16) & 0x01FFF;
+ int dst_y = s->twoD_destination & 0xFFFF;
+ int operation_width = (s->twoD_dimension >> 16) & 0x1FFF;
+ int operation_height = s->twoD_dimension & 0xFFFF;
+ uint32_t color = s->twoD_foreground;
+ int format_flags = (s->twoD_stretch >> 20) & 0x3;
+ int addressing = (s->twoD_stretch >> 16) & 0xF;
+
+ /* get frame buffer info */
+ uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF);
+ uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF);
+ int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1;
+ int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1;
+
+ if (addressing != 0x0) {
+ printf("%s: only XY addressing is supported.\n", __func__);
+ abort();
+ }
+
+ if ((s->twoD_source_base & 0x08000000) ||
+ (s->twoD_destination_base & 0x08000000)) {
+ printf("%s: only local memory is supported.\n", __func__);
+ abort();
+ }
+
+ switch (operation) {
+ case 0x00: /* copy area */
+#define COPY_AREA(_bpp, _pixel_type, rtl) { \
+ int y, x, index_d, index_s; \
+ for (y = 0; y < operation_height; y++) { \
+ for (x = 0; x < operation_width; x++) { \
+ if (rtl) { \
+ index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \
+ index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \
+ } else { \
+ index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \
+ index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \
+ } \
+ *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\
+ } \
+ } \
+ }
+ switch (format_flags) {
+ case 0:
+ COPY_AREA(1, uint8_t, rtl);
+ break;
+ case 1:
+ COPY_AREA(2, uint16_t, rtl);
+ break;
+ case 2:
+ COPY_AREA(4, uint32_t, rtl);
+ break;
+ }
+ break;
+
+ case 0x01: /* fill rectangle */
+#define FILL_RECT(_bpp, _pixel_type) { \
+ int y, x; \
+ for (y = 0; y < operation_height; y++) { \
+ for (x = 0; x < operation_width; x++) { \
+ int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \
+ *(_pixel_type*)&dst[index] = (_pixel_type)color; \
+ } \
+ } \
+ }
+
+ switch (format_flags) {
+ case 0:
+ FILL_RECT(1, uint8_t);
+ break;
+ case 1:
+ FILL_RECT(2, uint16_t);
+ break;
+ case 2:
+ FILL_RECT(4, uint32_t);
+ break;
+ }
+ break;
+
+ default:
+ printf("non-implemented SM501 2D operation. %d\n", operation);
+ abort();
+ break;
+ }
+}
+
+static uint32_t sm501_system_config_read(void *opaque, target_phys_addr_t addr)
+{
+ SM501State * s = (SM501State *)opaque;
+ uint32_t ret = 0;
+ SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr);
+
+ switch(addr) {
+ case SM501_SYSTEM_CONTROL:
+ ret = s->system_control;
+ break;
+ case SM501_MISC_CONTROL:
+ ret = s->misc_control;
+ break;
+ case SM501_GPIO31_0_CONTROL:
+ ret = s->gpio_31_0_control;
+ break;
+ case SM501_GPIO63_32_CONTROL:
+ ret = s->gpio_63_32_control;
+ break;
+ case SM501_DEVICEID:
+ ret = 0x050100A0;
+ break;
+ case SM501_DRAM_CONTROL:
+ ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13;
+ break;
+ case SM501_IRQ_MASK:
+ ret = s->irq_mask;
+ break;
+ case SM501_MISC_TIMING:
+ /* TODO : simulate gate control */
+ ret = s->misc_timing;
+ break;
+ case SM501_CURRENT_GATE:
+ /* TODO : simulate gate control */
+ ret = 0x00021807;
+ break;
+ case SM501_CURRENT_CLOCK:
+ ret = 0x2A1A0A09;
+ break;
+ case SM501_POWER_MODE_CONTROL:
+ ret = s->power_mode_control;
+ break;
+
+ default:
+ printf("sm501 system config : not implemented register read."
+ " addr=%x\n", (int)addr);
+ abort();
+ }
+
+ return ret;
+}
+
+static void sm501_system_config_write(void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ SM501State * s = (SM501State *)opaque;
+ SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n",
+ addr, value);
+
+ switch(addr) {
+ case SM501_SYSTEM_CONTROL:
+ s->system_control = value & 0xE300B8F7;
+ break;
+ case SM501_MISC_CONTROL:
+ s->misc_control = value & 0xFF7FFF20;
+ break;
+ case SM501_GPIO31_0_CONTROL:
+ s->gpio_31_0_control = value;
+ break;
+ case SM501_GPIO63_32_CONTROL:
+ s->gpio_63_32_control = value;
+ break;
+ case SM501_DRAM_CONTROL:
+ s->local_mem_size_index = (value >> 13) & 0x7;
+ /* rODO : check validity of size change */
+ s->dram_control |= value & 0x7FFFFFC3;
+ break;
+ case SM501_IRQ_MASK:
+ s->irq_mask = value;
+ break;
+ case SM501_MISC_TIMING:
+ s->misc_timing = value & 0xF31F1FFF;
+ break;
+ case SM501_POWER_MODE_0_GATE:
+ case SM501_POWER_MODE_1_GATE:
+ case SM501_POWER_MODE_0_CLOCK:
+ case SM501_POWER_MODE_1_CLOCK:
+ /* TODO : simulate gate & clock control */
+ break;
+ case SM501_POWER_MODE_CONTROL:
+ s->power_mode_control = value & 0x00000003;
+ break;
+
+ default:
+ printf("sm501 system config : not implemented register write."
+ " addr=%x, val=%x\n", (int)addr, value);
+ abort();
+ }
+}
+
+static CPUReadMemoryFunc * const sm501_system_config_readfn[] = {
+ NULL,
+ NULL,
+ &sm501_system_config_read,
+};
+
+static CPUWriteMemoryFunc * const sm501_system_config_writefn[] = {
+ NULL,
+ NULL,
+ &sm501_system_config_write,
+};
+
+static uint32_t sm501_palette_read(void *opaque, target_phys_addr_t addr)
+{
+ SM501State * s = (SM501State *)opaque;
+ SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr);
+
+ /* TODO : consider BYTE/WORD access */
+ /* TODO : consider endian */
+
+ assert(range_covers_byte(0, 0x400 * 3, addr));
+ return *(uint32_t*)&s->dc_palette[addr];
+}
+
+static void sm501_palette_write(void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ SM501State * s = (SM501State *)opaque;
+ SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n",
+ (int)addr, value);
+
+ /* TODO : consider BYTE/WORD access */
+ /* TODO : consider endian */
+
+ assert(range_covers_byte(0, 0x400 * 3, addr));
+ *(uint32_t*)&s->dc_palette[addr] = value;
+}
+
+static uint32_t sm501_disp_ctrl_read(void *opaque, target_phys_addr_t addr)
+{
+ SM501State * s = (SM501State *)opaque;
+ uint32_t ret = 0;
+ SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr);
+
+ switch(addr) {
+
+ case SM501_DC_PANEL_CONTROL:
+ ret = s->dc_panel_control;
+ break;
+ case SM501_DC_PANEL_PANNING_CONTROL:
+ ret = s->dc_panel_panning_control;
+ break;
+ case SM501_DC_PANEL_FB_ADDR:
+ ret = s->dc_panel_fb_addr;
+ break;
+ case SM501_DC_PANEL_FB_OFFSET:
+ ret = s->dc_panel_fb_offset;
+ break;
+ case SM501_DC_PANEL_FB_WIDTH:
+ ret = s->dc_panel_fb_width;
+ break;
+ case SM501_DC_PANEL_FB_HEIGHT:
+ ret = s->dc_panel_fb_height;
+ break;
+ case SM501_DC_PANEL_TL_LOC:
+ ret = s->dc_panel_tl_location;
+ break;
+ case SM501_DC_PANEL_BR_LOC:
+ ret = s->dc_panel_br_location;
+ break;
+
+ case SM501_DC_PANEL_H_TOT:
+ ret = s->dc_panel_h_total;
+ break;
+ case SM501_DC_PANEL_H_SYNC:
+ ret = s->dc_panel_h_sync;
+ break;
+ case SM501_DC_PANEL_V_TOT:
+ ret = s->dc_panel_v_total;
+ break;
+ case SM501_DC_PANEL_V_SYNC:
+ ret = s->dc_panel_v_sync;
+ break;
+
+ case SM501_DC_CRT_CONTROL:
+ ret = s->dc_crt_control;
+ break;
+ case SM501_DC_CRT_FB_ADDR:
+ ret = s->dc_crt_fb_addr;
+ break;
+ case SM501_DC_CRT_FB_OFFSET:
+ ret = s->dc_crt_fb_offset;
+ break;
+ case SM501_DC_CRT_H_TOT:
+ ret = s->dc_crt_h_total;
+ break;
+ case SM501_DC_CRT_H_SYNC:
+ ret = s->dc_crt_h_sync;
+ break;
+ case SM501_DC_CRT_V_TOT:
+ ret = s->dc_crt_v_total;
+ break;
+ case SM501_DC_CRT_V_SYNC:
+ ret = s->dc_crt_v_sync;
+ break;
+
+ case SM501_DC_CRT_HWC_ADDR:
+ ret = s->dc_crt_hwc_addr;
+ break;
+ case SM501_DC_CRT_HWC_LOC:
+ ret = s->dc_crt_hwc_location;
+ break;
+ case SM501_DC_CRT_HWC_COLOR_1_2:
+ ret = s->dc_crt_hwc_color_1_2;
+ break;
+ case SM501_DC_CRT_HWC_COLOR_3:
+ ret = s->dc_crt_hwc_color_3;
+ break;
+
+ case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4:
+ ret = sm501_palette_read(opaque, addr - SM501_DC_PANEL_PALETTE);
+ break;
+
+ default:
+ printf("sm501 disp ctrl : not implemented register read."
+ " addr=%x\n", (int)addr);
+ abort();
+ }
+
+ return ret;
+}
+
+static void sm501_disp_ctrl_write(void *opaque,
+ target_phys_addr_t addr,
+ uint32_t value)
+{
+ SM501State * s = (SM501State *)opaque;
+ SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n",
+ addr, value);
+
+ switch(addr) {
+ case SM501_DC_PANEL_CONTROL:
+ s->dc_panel_control = value & 0x0FFF73FF;
+ break;
+ case SM501_DC_PANEL_PANNING_CONTROL:
+ s->dc_panel_panning_control = value & 0xFF3FFF3F;
+ break;
+ case SM501_DC_PANEL_FB_ADDR:
+ s->dc_panel_fb_addr = value & 0x8FFFFFF0;
+ break;
+ case SM501_DC_PANEL_FB_OFFSET:
+ s->dc_panel_fb_offset = value & 0x3FF03FF0;
+ break;
+ case SM501_DC_PANEL_FB_WIDTH:
+ s->dc_panel_fb_width = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_PANEL_FB_HEIGHT:
+ s->dc_panel_fb_height = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_PANEL_TL_LOC:
+ s->dc_panel_tl_location = value & 0x07FF07FF;
+ break;
+ case SM501_DC_PANEL_BR_LOC:
+ s->dc_panel_br_location = value & 0x07FF07FF;
+ break;
+
+ case SM501_DC_PANEL_H_TOT:
+ s->dc_panel_h_total = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_PANEL_H_SYNC:
+ s->dc_panel_h_sync = value & 0x00FF0FFF;
+ break;
+ case SM501_DC_PANEL_V_TOT:
+ s->dc_panel_v_total = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_PANEL_V_SYNC:
+ s->dc_panel_v_sync = value & 0x003F0FFF;
+ break;
+
+ case SM501_DC_PANEL_HWC_ADDR:
+ s->dc_panel_hwc_addr = value & 0x8FFFFFF0;
+ break;
+ case SM501_DC_PANEL_HWC_LOC:
+ s->dc_panel_hwc_location = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_PANEL_HWC_COLOR_1_2:
+ s->dc_panel_hwc_color_1_2 = value;
+ break;
+ case SM501_DC_PANEL_HWC_COLOR_3:
+ s->dc_panel_hwc_color_3 = value & 0x0000FFFF;
+ break;
+
+ case SM501_DC_CRT_CONTROL:
+ s->dc_crt_control = value & 0x0003FFFF;
+ break;
+ case SM501_DC_CRT_FB_ADDR:
+ s->dc_crt_fb_addr = value & 0x8FFFFFF0;
+ break;
+ case SM501_DC_CRT_FB_OFFSET:
+ s->dc_crt_fb_offset = value & 0x3FF03FF0;
+ break;
+ case SM501_DC_CRT_H_TOT:
+ s->dc_crt_h_total = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_CRT_H_SYNC:
+ s->dc_crt_h_sync = value & 0x00FF0FFF;
+ break;
+ case SM501_DC_CRT_V_TOT:
+ s->dc_crt_v_total = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_CRT_V_SYNC:
+ s->dc_crt_v_sync = value & 0x003F0FFF;
+ break;
+
+ case SM501_DC_CRT_HWC_ADDR:
+ s->dc_crt_hwc_addr = value & 0x8FFFFFF0;
+ break;
+ case SM501_DC_CRT_HWC_LOC:
+ s->dc_crt_hwc_location = value & 0x0FFF0FFF;
+ break;
+ case SM501_DC_CRT_HWC_COLOR_1_2:
+ s->dc_crt_hwc_color_1_2 = value;
+ break;
+ case SM501_DC_CRT_HWC_COLOR_3:
+ s->dc_crt_hwc_color_3 = value & 0x0000FFFF;
+ break;
+
+ case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4:
+ sm501_palette_write(opaque, addr - SM501_DC_PANEL_PALETTE, value);
+ break;
+
+ default:
+ printf("sm501 disp ctrl : not implemented register write."
+ " addr=%x, val=%x\n", (int)addr, value);
+ abort();
+ }
+}
+
+static CPUReadMemoryFunc * const sm501_disp_ctrl_readfn[] = {
+ NULL,
+ NULL,
+ &sm501_disp_ctrl_read,
+};
+
+static CPUWriteMemoryFunc * const sm501_disp_ctrl_writefn[] = {
+ NULL,
+ NULL,
+ &sm501_disp_ctrl_write,
+};
+
+static uint32_t sm501_2d_engine_read(void *opaque, target_phys_addr_t addr)
+{
+ SM501State * s = (SM501State *)opaque;
+ uint32_t ret = 0;
+ SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr);
+
+ switch(addr) {
+ case SM501_2D_SOURCE_BASE:
+ ret = s->twoD_source_base;
+ break;
+ default:
+ printf("sm501 disp ctrl : not implemented register read."
+ " addr=%x\n", (int)addr);
+ abort();
+ }
+
+ return ret;
+}
+
+static void sm501_2d_engine_write(void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ SM501State * s = (SM501State *)opaque;
+ SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n",
+ addr, value);
+
+ switch(addr) {
+ case SM501_2D_SOURCE:
+ s->twoD_source = value;
+ break;
+ case SM501_2D_DESTINATION:
+ s->twoD_destination = value;
+ break;
+ case SM501_2D_DIMENSION:
+ s->twoD_dimension = value;
+ break;
+ case SM501_2D_CONTROL:
+ s->twoD_control = value;
+
+ /* do 2d operation if start flag is set. */
+ if (value & 0x80000000) {
+ sm501_2d_operation(s);
+ s->twoD_control &= ~0x80000000; /* start flag down */
+ }
+
+ break;
+ case SM501_2D_PITCH:
+ s->twoD_pitch = value;
+ break;
+ case SM501_2D_FOREGROUND:
+ s->twoD_foreground = value;
+ break;
+ case SM501_2D_STRETCH:
+ s->twoD_stretch = value;
+ break;
+ case SM501_2D_COLOR_COMPARE_MASK:
+ s->twoD_color_compare_mask = value;
+ break;
+ case SM501_2D_MASK:
+ s->twoD_mask = value;
+ break;
+ case SM501_2D_WINDOW_WIDTH:
+ s->twoD_window_width = value;
+ break;
+ case SM501_2D_SOURCE_BASE:
+ s->twoD_source_base = value;
+ break;
+ case SM501_2D_DESTINATION_BASE:
+ s->twoD_destination_base = value;
+ break;
+ default:
+ printf("sm501 2d engine : not implemented register write."
+ " addr=%x, val=%x\n", (int)addr, value);
+ abort();
+ }
+}
+
+static CPUReadMemoryFunc * const sm501_2d_engine_readfn[] = {
+ NULL,
+ NULL,
+ &sm501_2d_engine_read,
+};
+
+static CPUWriteMemoryFunc * const sm501_2d_engine_writefn[] = {
+ NULL,
+ NULL,
+ &sm501_2d_engine_write,
+};
+
+/* draw line functions for all console modes */
+
+#include "pixel_ops.h"
+
+typedef void draw_line_func(uint8_t *d, const uint8_t *s,
+ int width, const uint32_t *pal);
+
+typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette,
+ int c_y, uint8_t *d, int width);
+
+#define DEPTH 8
+#include "sm501_template.h"
+
+#define DEPTH 15
+#include "sm501_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 15
+#include "sm501_template.h"
+
+#define DEPTH 16
+#include "sm501_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 16
+#include "sm501_template.h"
+
+#define DEPTH 32
+#include "sm501_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 32
+#include "sm501_template.h"
+
+static draw_line_func * draw_line8_funcs[] = {
+ draw_line8_8,
+ draw_line8_15,
+ draw_line8_16,
+ draw_line8_32,
+ draw_line8_32bgr,
+ draw_line8_15bgr,
+ draw_line8_16bgr,
+};
+
+static draw_line_func * draw_line16_funcs[] = {
+ draw_line16_8,
+ draw_line16_15,
+ draw_line16_16,
+ draw_line16_32,
+ draw_line16_32bgr,
+ draw_line16_15bgr,
+ draw_line16_16bgr,
+};
+
+static draw_line_func * draw_line32_funcs[] = {
+ draw_line32_8,
+ draw_line32_15,
+ draw_line32_16,
+ draw_line32_32,
+ draw_line32_32bgr,
+ draw_line32_15bgr,
+ draw_line32_16bgr,
+};
+
+static draw_hwc_line_func * draw_hwc_line_funcs[] = {
+ draw_hwc_line_8,
+ draw_hwc_line_15,
+ draw_hwc_line_16,
+ draw_hwc_line_32,
+ draw_hwc_line_32bgr,
+ draw_hwc_line_15bgr,
+ draw_hwc_line_16bgr,
+};
+
+static inline int get_depth_index(DisplayState *s)
+{
+ switch(ds_get_bits_per_pixel(s)) {
+ default:
+ case 8:
+ return 0;
+ case 15:
+ return 1;
+ case 16:
+ return 2;
+ case 32:
+ if (is_surface_bgr(s->surface))
+ return 4;
+ else
+ return 3;
+ }
+}
+
+static void sm501_draw_crt(SM501State * s)
+{
+ int y;
+ int width = (s->dc_crt_h_total & 0x00000FFF) + 1;
+ int height = (s->dc_crt_v_total & 0x00000FFF) + 1;
+
+ uint8_t * src = s->local_mem;
+ int src_bpp = 0;
+ int dst_bpp = ds_get_bytes_per_pixel(s->ds) + (ds_get_bits_per_pixel(s->ds) % 8 ? 1 : 0);
+ uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE
+ - SM501_DC_PANEL_PALETTE];
+ uint8_t hwc_palette[3 * 3];
+ int ds_depth_index = get_depth_index(s->ds);
+ draw_line_func * draw_line = NULL;
+ draw_hwc_line_func * draw_hwc_line = NULL;
+ int full_update = 0;
+ int y_start = -1;
+ ram_addr_t page_min = ~0l;
+ ram_addr_t page_max = 0l;
+ ram_addr_t offset = s->local_mem_offset;
+
+ /* choose draw_line function */
+ switch (s->dc_crt_control & 3) {
+ case SM501_DC_CRT_CONTROL_8BPP:
+ src_bpp = 1;
+ draw_line = draw_line8_funcs[ds_depth_index];
+ break;
+ case SM501_DC_CRT_CONTROL_16BPP:
+ src_bpp = 2;
+ draw_line = draw_line16_funcs[ds_depth_index];
+ break;
+ case SM501_DC_CRT_CONTROL_32BPP:
+ src_bpp = 4;
+ draw_line = draw_line32_funcs[ds_depth_index];
+ break;
+ default:
+ printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n",
+ s->dc_crt_control);
+ abort();
+ break;
+ }
+
+ /* set up to draw hardware cursor */
+ if (is_hwc_enabled(s, 1)) {
+ int i;
+
+ /* get cursor palette */
+ for (i = 0; i < 3; i++) {
+ uint16_t rgb565 = get_hwc_color(s, 1, i + 1);
+ hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */
+ hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */
+ hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */
+ }
+
+ /* choose cursor draw line function */
+ draw_hwc_line = draw_hwc_line_funcs[ds_depth_index];
+ }
+
+ /* adjust console size */
+ if (s->last_width != width || s->last_height != height) {
+ qemu_console_resize(s->ds, width, height);
+ s->last_width = width;
+ s->last_height = height;
+ full_update = 1;
+ }
+
+ /* draw each line according to conditions */
+ for (y = 0; y < height; y++) {
+ int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0;
+ int update = full_update || update_hwc;
+ ram_addr_t page0 = offset & TARGET_PAGE_MASK;
+ ram_addr_t page1 = (offset + width * src_bpp - 1) & TARGET_PAGE_MASK;
+ ram_addr_t page;
+
+ /* check dirty flags for each line */
+ for (page = page0; page <= page1; page += TARGET_PAGE_SIZE)
+ if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG))
+ update = 1;
+
+ /* draw line and change status */
+ if (update) {
+ uint8_t * d = &(ds_get_data(s->ds)[y * width * dst_bpp]);
+
+ /* draw graphics layer */
+ draw_line(d, src, width, palette);
+
+ /* draw haredware cursor */
+ if (update_hwc) {
+ draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width);
+ }
+
+ if (y_start < 0)
+ y_start = y;
+ if (page0 < page_min)
+ page_min = page0;
+ if (page1 > page_max)
+ page_max = page1;
+ } else {
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(s->ds, 0, y_start, width, y - y_start);
+ y_start = -1;
+ }
+ }
+
+ src += width * src_bpp;
+ offset += width * src_bpp;
+ }
+
+ /* complete flush to display */
+ if (y_start >= 0)
+ dpy_update(s->ds, 0, y_start, width, y - y_start);
+
+ /* clear dirty flags */
+ if (page_min != ~0l) {
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+}
+
+static void sm501_update_display(void *opaque)
+{
+ SM501State * s = (SM501State *)opaque;
+
+ if (s->dc_crt_control & SM501_DC_CRT_CONTROL_ENABLE)
+ sm501_draw_crt(s);
+}
+
+void sm501_init(uint32_t base, uint32_t local_mem_bytes, qemu_irq irq,
+ CharDriverState *chr)
+{
+ SM501State * s;
+ DeviceState *dev;
+ int sm501_system_config_index;
+ int sm501_disp_ctrl_index;
+ int sm501_2d_engine_index;
+
+ /* allocate management data region */
+ s = (SM501State *)qemu_mallocz(sizeof(SM501State));
+ s->base = base;
+ s->local_mem_size_index
+ = get_local_mem_size_index(local_mem_bytes);
+ SM501_DPRINTF("local mem size=%x. index=%d\n", get_local_mem_size(s),
+ s->local_mem_size_index);
+ s->system_control = 0x00100000;
+ s->misc_control = 0x00001000; /* assumes SH, active=low */
+ s->dc_panel_control = 0x00010000;
+ s->dc_crt_control = 0x00010000;
+
+ /* allocate local memory */
+ s->local_mem_offset = qemu_ram_alloc(NULL, "sm501.local", local_mem_bytes);
+ s->local_mem = qemu_get_ram_ptr(s->local_mem_offset);
+ cpu_register_physical_memory(base, local_mem_bytes, s->local_mem_offset);
+
+ /* map mmio */
+ sm501_system_config_index
+ = cpu_register_io_memory(sm501_system_config_readfn,
+ sm501_system_config_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base + MMIO_BASE_OFFSET,
+ 0x6c, sm501_system_config_index);
+ sm501_disp_ctrl_index = cpu_register_io_memory(sm501_disp_ctrl_readfn,
+ sm501_disp_ctrl_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base + MMIO_BASE_OFFSET + SM501_DC,
+ 0x1000, sm501_disp_ctrl_index);
+ sm501_2d_engine_index = cpu_register_io_memory(sm501_2d_engine_readfn,
+ sm501_2d_engine_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base + MMIO_BASE_OFFSET + SM501_2D_ENGINE,
+ 0x54, sm501_2d_engine_index);
+
+ /* bridge to usb host emulation module */
+ dev = qdev_create(NULL, "sysbus-ohci");
+ qdev_prop_set_uint32(dev, "num-ports", 2);
+ qdev_prop_set_taddr(dev, "dma-offset", base);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0,
+ base + MMIO_BASE_OFFSET + SM501_USB_HOST);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+
+ /* bridge to serial emulation module */
+ if (chr) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ serial_mm_init(base + MMIO_BASE_OFFSET + SM501_UART0, 2,
+ NULL, /* TODO : chain irq to IRL */
+ 115200, chr, 1, 1);
+#else
+ serial_mm_init(base + MMIO_BASE_OFFSET + SM501_UART0, 2,
+ NULL, /* TODO : chain irq to IRL */
+ 115200, chr, 1, 0);
+#endif
+ }
+
+ /* create qemu graphic console */
+ s->ds = graphic_console_init(sm501_update_display, NULL,
+ NULL, NULL, s);
+}
diff --git a/hw/sm501_template.h b/hw/sm501_template.h
new file mode 100644
index 0000000..d1ceef9
--- /dev/null
+++ b/hw/sm501_template.h
@@ -0,0 +1,145 @@
+/*
+ * Pixel drawing function templates for QEMU SM501 Device
+ *
+ * Copyright (c) 2008 Shin-ichiro KAWASAKI
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if DEPTH == 8
+#define BPP 1
+#define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+#define BPP 2
+#define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+#define BPP 4
+#define PIXEL_TYPE uint32_t
+#else
+#error unsupport depth
+#endif
+
+#ifdef BGR_FORMAT
+#define PIXEL_NAME glue(DEPTH, bgr)
+#else
+#define PIXEL_NAME DEPTH
+#endif /* BGR_FORMAT */
+
+
+static void glue(draw_line8_, PIXEL_NAME)(
+ uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
+{
+ uint8_t v, r, g, b;
+ do {
+ v = ldub_raw(s);
+ r = (pal[v] >> 16) & 0xff;
+ g = (pal[v] >> 8) & 0xff;
+ b = (pal[v] >> 0) & 0xff;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s ++;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+static void glue(draw_line16_, PIXEL_NAME)(
+ uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
+{
+ uint16_t rgb565;
+ uint8_t r, g, b;
+
+ do {
+ rgb565 = lduw_raw(s);
+ r = ((rgb565 >> 11) & 0x1f) << 3;
+ g = ((rgb565 >> 5) & 0x3f) << 2;
+ b = ((rgb565 >> 0) & 0x1f) << 3;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+static void glue(draw_line32_, PIXEL_NAME)(
+ uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
+{
+ uint8_t r, g, b;
+
+ do {
+ ldub_raw(s);
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[1];
+ g = s[2];
+ b = s[3];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 4;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+/**
+ * Draw hardware cursor image on the given line.
+ */
+static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt,
+ uint8_t * palette, int c_y, uint8_t *d, int width)
+{
+ int x, i;
+ uint8_t bitset = 0;
+
+ /* get hardware cursor pattern */
+ uint32_t cursor_addr = get_hwc_address(s, crt);
+ assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
+ cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */
+ cursor_addr += s->base;
+
+ /* get cursor position */
+ x = get_hwc_x(s, crt);
+ d += x * BPP;
+
+ for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) {
+ uint8_t v;
+
+ /* get pixel value */
+ if (i % 4 == 0) {
+ cpu_physical_memory_rw(cursor_addr, &bitset, 1, 0);
+ cursor_addr++;
+ }
+ v = bitset & 3;
+ bitset >>= 2;
+
+ /* write pixel */
+ if (v) {
+ v--;
+ uint8_t r = palette[v * 3 + 0];
+ uint8_t g = palette[v * 3 + 1];
+ uint8_t b = palette[v * 3 + 2];
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ }
+ d += BPP;
+ }
+}
+
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
+#undef PIXEL_NAME
+#undef BGR_FORMAT
diff --git a/hw/smbios.c b/hw/smbios.c
new file mode 100644
index 0000000..a3ae1de
--- /dev/null
+++ b/hw/smbios.c
@@ -0,0 +1,239 @@
+/*
+ * SMBIOS Support
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@hp.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sysemu.h"
+#include "smbios.h"
+#include "loader.h"
+
+/*
+ * Structures shared with the BIOS
+ */
+struct smbios_header {
+ uint16_t length;
+ uint8_t type;
+} __attribute__((__packed__));
+
+struct smbios_field {
+ struct smbios_header header;
+ uint8_t type;
+ uint16_t offset;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+struct smbios_table {
+ struct smbios_header header;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+#define SMBIOS_FIELD_ENTRY 0
+#define SMBIOS_TABLE_ENTRY 1
+
+
+static uint8_t *smbios_entries;
+static size_t smbios_entries_len;
+static int smbios_type4_count = 0;
+
+static void smbios_validate_table(void)
+{
+ if (smbios_type4_count && smbios_type4_count != smp_cpus) {
+ fprintf(stderr,
+ "Number of SMBIOS Type 4 tables must match cpu count.\n");
+ exit(1);
+ }
+}
+
+uint8_t *smbios_get_table(size_t *length)
+{
+ smbios_validate_table();
+ *length = smbios_entries_len;
+ return smbios_entries;
+}
+
+/*
+ * To avoid unresolvable overlaps in data, don't allow both
+ * tables and fields for the same smbios type.
+ */
+static void smbios_check_collision(int type, int entry)
+{
+ uint16_t *num_entries = (uint16_t *)smbios_entries;
+ struct smbios_header *header;
+ char *p;
+ int i;
+
+ if (!num_entries)
+ return;
+
+ p = (char *)(num_entries + 1);
+
+ for (i = 0; i < *num_entries; i++) {
+ header = (struct smbios_header *)p;
+ if (entry == SMBIOS_TABLE_ENTRY && header->type == SMBIOS_FIELD_ENTRY) {
+ struct smbios_field *field = (void *)header;
+ if (type == field->type) {
+ fprintf(stderr, "SMBIOS type %d field already defined, "
+ "cannot add table\n", type);
+ exit(1);
+ }
+ } else if (entry == SMBIOS_FIELD_ENTRY &&
+ header->type == SMBIOS_TABLE_ENTRY) {
+ struct smbios_structure_header *table = (void *)(header + 1);
+ if (type == table->type) {
+ fprintf(stderr, "SMBIOS type %d table already defined, "
+ "cannot add field\n", type);
+ exit(1);
+ }
+ }
+ p += le16_to_cpu(header->length);
+ }
+}
+
+void smbios_add_field(int type, int offset, int len, void *data)
+{
+ struct smbios_field *field;
+
+ smbios_check_collision(type, SMBIOS_FIELD_ENTRY);
+
+ if (!smbios_entries) {
+ smbios_entries_len = sizeof(uint16_t);
+ smbios_entries = qemu_mallocz(smbios_entries_len);
+ }
+ smbios_entries = qemu_realloc(smbios_entries, smbios_entries_len +
+ sizeof(*field) + len);
+ field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
+ field->header.type = SMBIOS_FIELD_ENTRY;
+ field->header.length = cpu_to_le16(sizeof(*field) + len);
+
+ field->type = type;
+ field->offset = cpu_to_le16(offset);
+ memcpy(field->data, data, len);
+
+ smbios_entries_len += sizeof(*field) + len;
+ (*(uint16_t *)smbios_entries) =
+ cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+}
+
+static void smbios_build_type_0_fields(const char *t)
+{
+ char buf[1024];
+
+ if (get_param_value(buf, sizeof(buf), "vendor", t))
+ smbios_add_field(0, offsetof(struct smbios_type_0, vendor_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "version", t))
+ smbios_add_field(0, offsetof(struct smbios_type_0, bios_version_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "date", t))
+ smbios_add_field(0, offsetof(struct smbios_type_0,
+ bios_release_date_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "release", t)) {
+ int major, minor;
+ sscanf(buf, "%d.%d", &major, &minor);
+ smbios_add_field(0, offsetof(struct smbios_type_0,
+ system_bios_major_release), 1, &major);
+ smbios_add_field(0, offsetof(struct smbios_type_0,
+ system_bios_minor_release), 1, &minor);
+ }
+}
+
+static void smbios_build_type_1_fields(const char *t)
+{
+ char buf[1024];
+
+ if (get_param_value(buf, sizeof(buf), "manufacturer", t))
+ smbios_add_field(1, offsetof(struct smbios_type_1, manufacturer_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "product", t))
+ smbios_add_field(1, offsetof(struct smbios_type_1, product_name_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "version", t))
+ smbios_add_field(1, offsetof(struct smbios_type_1, version_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "serial", t))
+ smbios_add_field(1, offsetof(struct smbios_type_1, serial_number_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "uuid", t)) {
+ if (qemu_uuid_parse(buf, qemu_uuid) != 0) {
+ fprintf(stderr, "Invalid SMBIOS UUID string\n");
+ exit(1);
+ }
+ }
+ if (get_param_value(buf, sizeof(buf), "sku", t))
+ smbios_add_field(1, offsetof(struct smbios_type_1, sku_number_str),
+ strlen(buf) + 1, buf);
+ if (get_param_value(buf, sizeof(buf), "family", t))
+ smbios_add_field(1, offsetof(struct smbios_type_1, family_str),
+ strlen(buf) + 1, buf);
+}
+
+int smbios_entry_add(const char *t)
+{
+ char buf[1024];
+
+ if (get_param_value(buf, sizeof(buf), "file", t)) {
+ struct smbios_structure_header *header;
+ struct smbios_table *table;
+ int size = get_image_size(buf);
+
+ if (size == -1 || size < sizeof(struct smbios_structure_header)) {
+ fprintf(stderr, "Cannot read smbios file %s\n", buf);
+ exit(1);
+ }
+
+ if (!smbios_entries) {
+ smbios_entries_len = sizeof(uint16_t);
+ smbios_entries = qemu_mallocz(smbios_entries_len);
+ }
+
+ smbios_entries = qemu_realloc(smbios_entries, smbios_entries_len +
+ sizeof(*table) + size);
+ table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
+ table->header.type = SMBIOS_TABLE_ENTRY;
+ table->header.length = cpu_to_le16(sizeof(*table) + size);
+
+ if (load_image(buf, table->data) != size) {
+ fprintf(stderr, "Failed to load smbios file %s", buf);
+ exit(1);
+ }
+
+ header = (struct smbios_structure_header *)(table->data);
+ smbios_check_collision(header->type, SMBIOS_TABLE_ENTRY);
+ if (header->type == 4) {
+ smbios_type4_count++;
+ }
+
+ smbios_entries_len += sizeof(*table) + size;
+ (*(uint16_t *)smbios_entries) =
+ cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+ return 0;
+ }
+
+ if (get_param_value(buf, sizeof(buf), "type", t)) {
+ unsigned long type = strtoul(buf, NULL, 0);
+ switch (type) {
+ case 0:
+ smbios_build_type_0_fields(t);
+ return 0;
+ case 1:
+ smbios_build_type_1_fields(t);
+ return 0;
+ default:
+ fprintf(stderr, "Don't know how to build fields for SMBIOS type "
+ "%ld\n", type);
+ exit(1);
+ }
+ }
+
+ fprintf(stderr, "smbios: must specify type= or file=\n");
+ return -1;
+}
diff --git a/hw/smbios.h b/hw/smbios.h
new file mode 100644
index 0000000..3a5169d
--- /dev/null
+++ b/hw/smbios.h
@@ -0,0 +1,162 @@
+#ifndef QEMU_SMBIOS_H
+#define QEMU_SMBIOS_H
+/*
+ * SMBIOS Support
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@hp.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+int smbios_entry_add(const char *t);
+void smbios_add_field(int type, int offset, int len, void *data);
+uint8_t *smbios_get_table(size_t *length);
+
+/*
+ * SMBIOS spec defined tables
+ */
+
+/* This goes at the beginning of every SMBIOS structure. */
+struct smbios_structure_header {
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+} __attribute__((__packed__));
+
+/* SMBIOS type 0 - BIOS Information */
+struct smbios_type_0 {
+ struct smbios_structure_header header;
+ uint8_t vendor_str;
+ uint8_t bios_version_str;
+ uint16_t bios_starting_address_segment;
+ uint8_t bios_release_date_str;
+ uint8_t bios_rom_size;
+ uint8_t bios_characteristics[8];
+ uint8_t bios_characteristics_extension_bytes[2];
+ uint8_t system_bios_major_release;
+ uint8_t system_bios_minor_release;
+ uint8_t embedded_controller_major_release;
+ uint8_t embedded_controller_minor_release;
+} __attribute__((__packed__));
+
+/* SMBIOS type 1 - System Information */
+struct smbios_type_1 {
+ struct smbios_structure_header header;
+ uint8_t manufacturer_str;
+ uint8_t product_name_str;
+ uint8_t version_str;
+ uint8_t serial_number_str;
+ uint8_t uuid[16];
+ uint8_t wake_up_type;
+ uint8_t sku_number_str;
+ uint8_t family_str;
+} __attribute__((__packed__));
+
+/* SMBIOS type 3 - System Enclosure (v2.3) */
+struct smbios_type_3 {
+ struct smbios_structure_header header;
+ uint8_t manufacturer_str;
+ uint8_t type;
+ uint8_t version_str;
+ uint8_t serial_number_str;
+ uint8_t asset_tag_number_str;
+ uint8_t boot_up_state;
+ uint8_t power_supply_state;
+ uint8_t thermal_state;
+ uint8_t security_status;
+ uint32_t oem_defined;
+ uint8_t height;
+ uint8_t number_of_power_cords;
+ uint8_t contained_element_count;
+ // contained elements follow
+} __attribute__((__packed__));
+
+/* SMBIOS type 4 - Processor Information (v2.0) */
+struct smbios_type_4 {
+ struct smbios_structure_header header;
+ uint8_t socket_designation_str;
+ uint8_t processor_type;
+ uint8_t processor_family;
+ uint8_t processor_manufacturer_str;
+ uint32_t processor_id[2];
+ uint8_t processor_version_str;
+ uint8_t voltage;
+ uint16_t external_clock;
+ uint16_t max_speed;
+ uint16_t current_speed;
+ uint8_t status;
+ uint8_t processor_upgrade;
+ uint16_t l1_cache_handle;
+ uint16_t l2_cache_handle;
+ uint16_t l3_cache_handle;
+} __attribute__((__packed__));
+
+/* SMBIOS type 16 - Physical Memory Array
+ * Associated with one type 17 (Memory Device).
+ */
+struct smbios_type_16 {
+ struct smbios_structure_header header;
+ uint8_t location;
+ uint8_t use;
+ uint8_t error_correction;
+ uint32_t maximum_capacity;
+ uint16_t memory_error_information_handle;
+ uint16_t number_of_memory_devices;
+} __attribute__((__packed__));
+/* SMBIOS type 17 - Memory Device
+ * Associated with one type 19
+ */
+struct smbios_type_17 {
+ struct smbios_structure_header header;
+ uint16_t physical_memory_array_handle;
+ uint16_t memory_error_information_handle;
+ uint16_t total_width;
+ uint16_t data_width;
+ uint16_t size;
+ uint8_t form_factor;
+ uint8_t device_set;
+ uint8_t device_locator_str;
+ uint8_t bank_locator_str;
+ uint8_t memory_type;
+ uint16_t type_detail;
+} __attribute__((__packed__));
+
+/* SMBIOS type 19 - Memory Array Mapped Address */
+struct smbios_type_19 {
+ struct smbios_structure_header header;
+ uint32_t starting_address;
+ uint32_t ending_address;
+ uint16_t memory_array_handle;
+ uint8_t partition_width;
+} __attribute__((__packed__));
+
+/* SMBIOS type 20 - Memory Device Mapped Address */
+struct smbios_type_20 {
+ struct smbios_structure_header header;
+ uint32_t starting_address;
+ uint32_t ending_address;
+ uint16_t memory_device_handle;
+ uint16_t memory_array_mapped_address_handle;
+ uint8_t partition_row_position;
+ uint8_t interleave_position;
+ uint8_t interleaved_data_depth;
+} __attribute__((__packed__));
+
+/* SMBIOS type 32 - System Boot Information */
+struct smbios_type_32 {
+ struct smbios_structure_header header;
+ uint8_t reserved[6];
+ uint8_t boot_status;
+} __attribute__((__packed__));
+
+/* SMBIOS type 127 -- End-of-table */
+struct smbios_type_127 {
+ struct smbios_structure_header header;
+} __attribute__((__packed__));
+
+#endif /*QEMU_SMBIOS_H */
diff --git a/hw/smbus.c b/hw/smbus.c
new file mode 100644
index 0000000..e464539
--- /dev/null
+++ b/hw/smbus.c
@@ -0,0 +1,318 @@
+/*
+ * QEMU SMBus device emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+/* TODO: Implement PEC. */
+
+#include "hw.h"
+#include "i2c.h"
+#include "smbus.h"
+
+//#define DEBUG_SMBUS 1
+
+#ifdef DEBUG_SMBUS
+#define DPRINTF(fmt, ...) \
+do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+ SMBUS_IDLE,
+ SMBUS_WRITE_DATA,
+ SMBUS_RECV_BYTE,
+ SMBUS_READ_DATA,
+ SMBUS_DONE,
+ SMBUS_CONFUSED = -1
+};
+
+static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
+{
+ SMBusDeviceInfo *t = container_of(dev->i2c.info, SMBusDeviceInfo, i2c);
+
+ DPRINTF("Quick Command %d\n", recv);
+ if (t->quick_cmd)
+ t->quick_cmd(dev, recv);
+}
+
+static void smbus_do_write(SMBusDevice *dev)
+{
+ SMBusDeviceInfo *t = container_of(dev->i2c.info, SMBusDeviceInfo, i2c);
+
+ if (dev->data_len == 0) {
+ smbus_do_quick_cmd(dev, 0);
+ } else if (dev->data_len == 1) {
+ DPRINTF("Send Byte\n");
+ if (t->send_byte) {
+ t->send_byte(dev, dev->data_buf[0]);
+ }
+ } else {
+ dev->command = dev->data_buf[0];
+ DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
+ if (t->write_data) {
+ t->write_data(dev, dev->command, dev->data_buf + 1,
+ dev->data_len - 1);
+ }
+ }
+}
+
+static void smbus_i2c_event(i2c_slave *s, enum i2c_event event)
+{
+ SMBusDevice *dev = FROM_I2C_SLAVE(SMBusDevice, s);
+
+ switch (event) {
+ case I2C_START_SEND:
+ switch (dev->mode) {
+ case SMBUS_IDLE:
+ DPRINTF("Incoming data\n");
+ dev->mode = SMBUS_WRITE_DATA;
+ break;
+ default:
+ BADF("Unexpected send start condition in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ break;
+ }
+ break;
+
+ case I2C_START_RECV:
+ switch (dev->mode) {
+ case SMBUS_IDLE:
+ DPRINTF("Read mode\n");
+ dev->mode = SMBUS_RECV_BYTE;
+ break;
+ case SMBUS_WRITE_DATA:
+ if (dev->data_len == 0) {
+ BADF("Read after write with no data\n");
+ dev->mode = SMBUS_CONFUSED;
+ } else {
+ if (dev->data_len > 1) {
+ smbus_do_write(dev);
+ } else {
+ dev->command = dev->data_buf[0];
+ DPRINTF("%02x: Command %d\n", dev->i2c.address,
+ dev->command);
+ }
+ DPRINTF("Read mode\n");
+ dev->data_len = 0;
+ dev->mode = SMBUS_READ_DATA;
+ }
+ break;
+ default:
+ BADF("Unexpected recv start condition in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ break;
+ }
+ break;
+
+ case I2C_FINISH:
+ switch (dev->mode) {
+ case SMBUS_WRITE_DATA:
+ smbus_do_write(dev);
+ break;
+ case SMBUS_RECV_BYTE:
+ smbus_do_quick_cmd(dev, 1);
+ break;
+ case SMBUS_READ_DATA:
+ BADF("Unexpected stop during receive\n");
+ break;
+ default:
+ /* Nothing to do. */
+ break;
+ }
+ dev->mode = SMBUS_IDLE;
+ dev->data_len = 0;
+ break;
+
+ case I2C_NACK:
+ switch (dev->mode) {
+ case SMBUS_DONE:
+ /* Nothing to do. */
+ break;
+ case SMBUS_READ_DATA:
+ dev->mode = SMBUS_DONE;
+ break;
+ default:
+ BADF("Unexpected NACK in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ break;
+ }
+ }
+}
+
+static int smbus_i2c_recv(i2c_slave *s)
+{
+ SMBusDeviceInfo *t = container_of(s->info, SMBusDeviceInfo, i2c);
+ SMBusDevice *dev = FROM_I2C_SLAVE(SMBusDevice, s);
+ int ret;
+
+ switch (dev->mode) {
+ case SMBUS_RECV_BYTE:
+ if (t->receive_byte) {
+ ret = t->receive_byte(dev);
+ } else {
+ ret = 0;
+ }
+ DPRINTF("Receive Byte %02x\n", ret);
+ dev->mode = SMBUS_DONE;
+ break;
+ case SMBUS_READ_DATA:
+ if (t->read_data) {
+ ret = t->read_data(dev, dev->command, dev->data_len);
+ dev->data_len++;
+ } else {
+ ret = 0;
+ }
+ DPRINTF("Read data %02x\n", ret);
+ break;
+ default:
+ BADF("Unexpected read in state %d\n", dev->mode);
+ dev->mode = SMBUS_CONFUSED;
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int smbus_i2c_send(i2c_slave *s, uint8_t data)
+{
+ SMBusDevice *dev = FROM_I2C_SLAVE(SMBusDevice, s);
+
+ switch (dev->mode) {
+ case SMBUS_WRITE_DATA:
+ DPRINTF("Write data %02x\n", data);
+ dev->data_buf[dev->data_len++] = data;
+ break;
+ default:
+ BADF("Unexpected write in state %d\n", dev->mode);
+ break;
+ }
+ return 0;
+}
+
+static int smbus_device_init(i2c_slave *i2c)
+{
+ SMBusDeviceInfo *t = container_of(i2c->info, SMBusDeviceInfo, i2c);
+ SMBusDevice *dev = FROM_I2C_SLAVE(SMBusDevice, i2c);
+
+ return t->init(dev);
+}
+
+void smbus_register_device(SMBusDeviceInfo *info)
+{
+ assert(info->i2c.qdev.size >= sizeof(SMBusDevice));
+ info->i2c.init = smbus_device_init;
+ info->i2c.event = smbus_i2c_event;
+ info->i2c.recv = smbus_i2c_recv;
+ info->i2c.send = smbus_i2c_send;
+ i2c_register_slave(&info->i2c);
+}
+
+/* Master device commands. */
+void smbus_quick_command(i2c_bus *bus, uint8_t addr, int read)
+{
+ i2c_start_transfer(bus, addr, read);
+ i2c_end_transfer(bus);
+}
+
+uint8_t smbus_receive_byte(i2c_bus *bus, uint8_t addr)
+{
+ uint8_t data;
+
+ i2c_start_transfer(bus, addr, 1);
+ data = i2c_recv(bus);
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return data;
+}
+
+void smbus_send_byte(i2c_bus *bus, uint8_t addr, uint8_t data)
+{
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, data);
+ i2c_end_transfer(bus);
+}
+
+uint8_t smbus_read_byte(i2c_bus *bus, uint8_t addr, uint8_t command)
+{
+ uint8_t data;
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_start_transfer(bus, addr, 1);
+ data = i2c_recv(bus);
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return data;
+}
+
+void smbus_write_byte(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t data)
+{
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_send(bus, data);
+ i2c_end_transfer(bus);
+}
+
+uint16_t smbus_read_word(i2c_bus *bus, uint8_t addr, uint8_t command)
+{
+ uint16_t data;
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_start_transfer(bus, addr, 1);
+ data = i2c_recv(bus);
+ data |= i2c_recv(bus) << 8;
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return data;
+}
+
+void smbus_write_word(i2c_bus *bus, uint8_t addr, uint8_t command, uint16_t data)
+{
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_send(bus, data & 0xff);
+ i2c_send(bus, data >> 8);
+ i2c_end_transfer(bus);
+}
+
+int smbus_read_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data)
+{
+ int len;
+ int i;
+
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_start_transfer(bus, addr, 1);
+ len = i2c_recv(bus);
+ if (len > 32)
+ len = 0;
+ for (i = 0; i < len; i++)
+ data[i] = i2c_recv(bus);
+ i2c_nack(bus);
+ i2c_end_transfer(bus);
+ return len;
+}
+
+void smbus_write_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data,
+ int len)
+{
+ int i;
+
+ if (len > 32)
+ len = 32;
+
+ i2c_start_transfer(bus, addr, 0);
+ i2c_send(bus, command);
+ i2c_send(bus, len);
+ for (i = 0; i < len; i++)
+ i2c_send(bus, data[i]);
+ i2c_end_transfer(bus);
+}
diff --git a/hw/smbus.h b/hw/smbus.h
new file mode 100644
index 0000000..571c52d
--- /dev/null
+++ b/hw/smbus.h
@@ -0,0 +1,68 @@
+/*
+ * QEMU SMBus API
+ *
+ * Copyright (c) 2007 Arastra, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "i2c.h"
+
+struct SMBusDevice {
+ /* The SMBus protocol is implemented on top of I2C. */
+ i2c_slave i2c;
+
+ /* Remaining fields for internal use only. */
+ int mode;
+ int data_len;
+ uint8_t data_buf[34]; /* command + len + 32 bytes of data. */
+ uint8_t command;
+};
+
+typedef struct {
+ I2CSlaveInfo i2c;
+ int (*init)(SMBusDevice *dev);
+ void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
+ void (*send_byte)(SMBusDevice *dev, uint8_t val);
+ uint8_t (*receive_byte)(SMBusDevice *dev);
+ /* We can't distinguish between a word write and a block write with
+ length 1, so pass the whole data block including the length byte
+ (if present). The device is responsible figuring out what type of
+ command this is. */
+ void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len);
+ /* Likewise we can't distinguish between different reads, or even know
+ the length of the read until the read is complete, so read data a
+ byte at a time. The device is responsible for adding the length
+ byte on block reads. */
+ uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n);
+} SMBusDeviceInfo;
+
+void smbus_register_device(SMBusDeviceInfo *info);
+
+/* Master device commands. */
+void smbus_quick_command(i2c_bus *bus, uint8_t addr, int read);
+uint8_t smbus_receive_byte(i2c_bus *bus, uint8_t addr);
+void smbus_send_byte(i2c_bus *bus, uint8_t addr, uint8_t data);
+uint8_t smbus_read_byte(i2c_bus *bus, uint8_t addr, uint8_t command);
+void smbus_write_byte(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t data);
+uint16_t smbus_read_word(i2c_bus *bus, uint8_t addr, uint8_t command);
+void smbus_write_word(i2c_bus *bus, uint8_t addr, uint8_t command, uint16_t data);
+int smbus_read_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data);
+void smbus_write_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data,
+ int len);
diff --git a/hw/smbus_eeprom.c b/hw/smbus_eeprom.c
new file mode 100644
index 0000000..52463e0
--- /dev/null
+++ b/hw/smbus_eeprom.c
@@ -0,0 +1,127 @@
+/*
+ * QEMU SMBus EEPROM device
+ *
+ * Copyright (c) 2007 Arastra, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "i2c.h"
+#include "smbus.h"
+
+//#define DEBUG
+
+typedef struct SMBusEEPROMDevice {
+ SMBusDevice smbusdev;
+ void *data;
+ uint8_t offset;
+} SMBusEEPROMDevice;
+
+static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read)
+{
+#ifdef DEBUG
+ printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
+#endif
+}
+
+static void eeprom_send_byte(SMBusDevice *dev, uint8_t val)
+{
+ SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+#ifdef DEBUG
+ printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n",
+ dev->i2c.address, val);
+#endif
+ eeprom->offset = val;
+}
+
+static uint8_t eeprom_receive_byte(SMBusDevice *dev)
+{
+ SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+ uint8_t *data = eeprom->data;
+ uint8_t val = data[eeprom->offset++];
+#ifdef DEBUG
+ printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n",
+ dev->i2c.address, val);
+#endif
+ return val;
+}
+
+static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
+{
+ SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+ int n;
+#ifdef DEBUG
+ printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
+ dev->i2c.address, cmd, buf[0]);
+#endif
+ /* An page write operation is not a valid SMBus command.
+ It is a block write without a length byte. Fortunately we
+ get the full block anyway. */
+ /* TODO: Should this set the current location? */
+ if (cmd + len > 256)
+ n = 256 - cmd;
+ else
+ n = len;
+ memcpy(eeprom->data + cmd, buf, n);
+ len -= n;
+ if (len)
+ memcpy(eeprom->data, buf + n, len);
+}
+
+static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+{
+ SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+ /* If this is the first byte then set the current position. */
+ if (n == 0)
+ eeprom->offset = cmd;
+ /* As with writes, we implement block reads without the
+ SMBus length byte. */
+ return eeprom_receive_byte(dev);
+}
+
+static int smbus_eeprom_init(SMBusDevice *dev)
+{
+ SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev;
+
+ eeprom->offset = 0;
+ return 0;
+}
+
+static SMBusDeviceInfo smbus_eeprom_info = {
+ .i2c.qdev.name = "smbus-eeprom",
+ .i2c.qdev.size = sizeof(SMBusEEPROMDevice),
+ .i2c.qdev.props = (Property[]) {
+ DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ .init = smbus_eeprom_init,
+ .quick_cmd = eeprom_quick_cmd,
+ .send_byte = eeprom_send_byte,
+ .receive_byte = eeprom_receive_byte,
+ .write_data = eeprom_write_data,
+ .read_data = eeprom_read_data
+};
+
+static void smbus_eeprom_register_devices(void)
+{
+ smbus_register_device(&smbus_eeprom_info);
+}
+
+device_init(smbus_eeprom_register_devices)
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
new file mode 100644
index 0000000..dafea5c
--- /dev/null
+++ b/hw/smc91c111.c
@@ -0,0 +1,798 @@
+/*
+ * SMSC 91C111 Ethernet interface emulation
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "sysbus.h"
+#include "net.h"
+#include "devices.h"
+/* For crc32 */
+#include <zlib.h>
+
+/* Number of 2k memory pages available. */
+#define NUM_PACKETS 4
+
+typedef struct {
+ SysBusDevice busdev;
+ NICState *nic;
+ NICConf conf;
+ uint16_t tcr;
+ uint16_t rcr;
+ uint16_t cr;
+ uint16_t ctr;
+ uint16_t gpr;
+ uint16_t ptr;
+ uint16_t ercv;
+ qemu_irq irq;
+ int bank;
+ int packet_num;
+ int tx_alloc;
+ /* Bitmask of allocated packets. */
+ int allocated;
+ int tx_fifo_len;
+ int tx_fifo[NUM_PACKETS];
+ int rx_fifo_len;
+ int rx_fifo[NUM_PACKETS];
+ int tx_fifo_done_len;
+ int tx_fifo_done[NUM_PACKETS];
+ /* Packet buffer memory. */
+ uint8_t data[NUM_PACKETS][2048];
+ uint8_t int_level;
+ uint8_t int_mask;
+ int mmio_index;
+} smc91c111_state;
+
+static const VMStateDescription vmstate_smc91c111 = {
+ .name = "smc91c111",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(tcr, smc91c111_state),
+ VMSTATE_UINT16(rcr, smc91c111_state),
+ VMSTATE_UINT16(cr, smc91c111_state),
+ VMSTATE_UINT16(ctr, smc91c111_state),
+ VMSTATE_UINT16(gpr, smc91c111_state),
+ VMSTATE_UINT16(ptr, smc91c111_state),
+ VMSTATE_UINT16(ercv, smc91c111_state),
+ VMSTATE_INT32(bank, smc91c111_state),
+ VMSTATE_INT32(packet_num, smc91c111_state),
+ VMSTATE_INT32(tx_alloc, smc91c111_state),
+ VMSTATE_INT32(allocated, smc91c111_state),
+ VMSTATE_INT32(tx_fifo_len, smc91c111_state),
+ VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS),
+ VMSTATE_INT32(rx_fifo_len, smc91c111_state),
+ VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS),
+ VMSTATE_INT32(tx_fifo_done_len, smc91c111_state),
+ VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS),
+ VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048),
+ VMSTATE_UINT8(int_level, smc91c111_state),
+ VMSTATE_UINT8(int_mask, smc91c111_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define RCR_SOFT_RST 0x8000
+#define RCR_STRIP_CRC 0x0200
+#define RCR_RXEN 0x0100
+
+#define TCR_EPH_LOOP 0x2000
+#define TCR_NOCRC 0x0100
+#define TCR_PAD_EN 0x0080
+#define TCR_FORCOL 0x0004
+#define TCR_LOOP 0x0002
+#define TCR_TXEN 0x0001
+
+#define INT_MD 0x80
+#define INT_ERCV 0x40
+#define INT_EPH 0x20
+#define INT_RX_OVRN 0x10
+#define INT_ALLOC 0x08
+#define INT_TX_EMPTY 0x04
+#define INT_TX 0x02
+#define INT_RCV 0x01
+
+#define CTR_AUTO_RELEASE 0x0800
+#define CTR_RELOAD 0x0002
+#define CTR_STORE 0x0001
+
+#define RS_ALGNERR 0x8000
+#define RS_BRODCAST 0x4000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+
+/* Update interrupt status. */
+static void smc91c111_update(smc91c111_state *s)
+{
+ int level;
+
+ if (s->tx_fifo_len == 0)
+ s->int_level |= INT_TX_EMPTY;
+ if (s->tx_fifo_done_len != 0)
+ s->int_level |= INT_TX;
+ level = (s->int_level & s->int_mask) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+/* Try to allocate a packet. Returns 0x80 on failure. */
+static int smc91c111_allocate_packet(smc91c111_state *s)
+{
+ int i;
+ if (s->allocated == (1 << NUM_PACKETS) - 1) {
+ return 0x80;
+ }
+
+ for (i = 0; i < NUM_PACKETS; i++) {
+ if ((s->allocated & (1 << i)) == 0)
+ break;
+ }
+ s->allocated |= 1 << i;
+ return i;
+}
+
+
+/* Process a pending TX allocate. */
+static void smc91c111_tx_alloc(smc91c111_state *s)
+{
+ s->tx_alloc = smc91c111_allocate_packet(s);
+ if (s->tx_alloc == 0x80)
+ return;
+ s->int_level |= INT_ALLOC;
+ smc91c111_update(s);
+}
+
+/* Remove and item from the RX FIFO. */
+static void smc91c111_pop_rx_fifo(smc91c111_state *s)
+{
+ int i;
+
+ s->rx_fifo_len--;
+ if (s->rx_fifo_len) {
+ for (i = 0; i < s->rx_fifo_len; i++)
+ s->rx_fifo[i] = s->rx_fifo[i + 1];
+ s->int_level |= INT_RCV;
+ } else {
+ s->int_level &= ~INT_RCV;
+ }
+ smc91c111_update(s);
+}
+
+/* Remove an item from the TX completion FIFO. */
+static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
+{
+ int i;
+
+ if (s->tx_fifo_done_len == 0)
+ return;
+ s->tx_fifo_done_len--;
+ for (i = 0; i < s->tx_fifo_done_len; i++)
+ s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
+}
+
+/* Release the memory allocated to a packet. */
+static void smc91c111_release_packet(smc91c111_state *s, int packet)
+{
+ s->allocated &= ~(1 << packet);
+ if (s->tx_alloc == 0x80)
+ smc91c111_tx_alloc(s);
+}
+
+/* Flush the TX FIFO. */
+static void smc91c111_do_tx(smc91c111_state *s)
+{
+ int i;
+ int len;
+ int control;
+ int packetnum;
+ uint8_t *p;
+
+ if ((s->tcr & TCR_TXEN) == 0)
+ return;
+ if (s->tx_fifo_len == 0)
+ return;
+ for (i = 0; i < s->tx_fifo_len; i++) {
+ packetnum = s->tx_fifo[i];
+ p = &s->data[packetnum][0];
+ /* Set status word. */
+ *(p++) = 0x01;
+ *(p++) = 0x40;
+ len = *(p++);
+ len |= ((int)*(p++)) << 8;
+ len -= 6;
+ control = p[len + 1];
+ if (control & 0x20)
+ len++;
+ /* ??? This overwrites the data following the buffer.
+ Don't know what real hardware does. */
+ if (len < 64 && (s->tcr & TCR_PAD_EN)) {
+ memset(p + len, 0, 64 - len);
+ len = 64;
+ }
+#if 0
+ {
+ int add_crc;
+
+ /* The card is supposed to append the CRC to the frame.
+ However none of the other network traffic has the CRC
+ appended. Suspect this is low level ethernet detail we
+ don't need to worry about. */
+ add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
+ if (add_crc) {
+ uint32_t crc;
+
+ crc = crc32(~0, p, len);
+ memcpy(p + len, &crc, 4);
+ len += 4;
+ }
+ }
+#endif
+ if (s->ctr & CTR_AUTO_RELEASE)
+ /* Race? */
+ smc91c111_release_packet(s, packetnum);
+ else if (s->tx_fifo_done_len < NUM_PACKETS)
+ s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
+ qemu_send_packet(&s->nic->nc, p, len);
+ }
+ s->tx_fifo_len = 0;
+ smc91c111_update(s);
+}
+
+/* Add a packet to the TX FIFO. */
+static void smc91c111_queue_tx(smc91c111_state *s, int packet)
+{
+ if (s->tx_fifo_len == NUM_PACKETS)
+ return;
+ s->tx_fifo[s->tx_fifo_len++] = packet;
+ smc91c111_do_tx(s);
+}
+
+static void smc91c111_reset(smc91c111_state *s)
+{
+ s->bank = 0;
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ s->rx_fifo_len = 0;
+ s->allocated = 0;
+ s->packet_num = 0;
+ s->tx_alloc = 0;
+ s->tcr = 0;
+ s->rcr = 0;
+ s->cr = 0xa0b1;
+ s->ctr = 0x1210;
+ s->ptr = 0;
+ s->ercv = 0x1f;
+ s->int_level = INT_TX_EMPTY;
+ s->int_mask = 0;
+ smc91c111_update(s);
+}
+
+#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
+#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
+
+static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ offset = offset & 0xf;
+ if (offset == 14) {
+ s->bank = value;
+ return;
+ }
+ if (offset == 15)
+ return;
+ switch (s->bank) {
+ case 0:
+ switch (offset) {
+ case 0: /* TCR */
+ SET_LOW(tcr, value);
+ return;
+ case 1:
+ SET_HIGH(tcr, value);
+ return;
+ case 4: /* RCR */
+ SET_LOW(rcr, value);
+ return;
+ case 5:
+ SET_HIGH(rcr, value);
+ if (s->rcr & RCR_SOFT_RST)
+ smc91c111_reset(s);
+ return;
+ case 10: case 11: /* RPCR */
+ /* Ignored */
+ return;
+ case 12: case 13: /* Reserved */
+ return;
+ }
+ break;
+
+ case 1:
+ switch (offset) {
+ case 0: /* CONFIG */
+ SET_LOW(cr, value);
+ return;
+ case 1:
+ SET_HIGH(cr,value);
+ return;
+ case 2: case 3: /* BASE */
+ case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+ /* Not implemented. */
+ return;
+ case 10: /* Genral Purpose */
+ SET_LOW(gpr, value);
+ return;
+ case 11:
+ SET_HIGH(gpr, value);
+ return;
+ case 12: /* Control */
+ if (value & 1)
+ fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
+ if (value & 2)
+ fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
+ value &= ~3;
+ SET_LOW(ctr, value);
+ return;
+ case 13:
+ SET_HIGH(ctr, value);
+ return;
+ }
+ break;
+
+ case 2:
+ switch (offset) {
+ case 0: /* MMU Command */
+ switch (value >> 5) {
+ case 0: /* no-op */
+ break;
+ case 1: /* Allocate for TX. */
+ s->tx_alloc = 0x80;
+ s->int_level &= ~INT_ALLOC;
+ smc91c111_update(s);
+ smc91c111_tx_alloc(s);
+ break;
+ case 2: /* Reset MMU. */
+ s->allocated = 0;
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ s->rx_fifo_len = 0;
+ s->tx_alloc = 0;
+ break;
+ case 3: /* Remove from RX FIFO. */
+ smc91c111_pop_rx_fifo(s);
+ break;
+ case 4: /* Remove from RX FIFO and release. */
+ if (s->rx_fifo_len > 0) {
+ smc91c111_release_packet(s, s->rx_fifo[0]);
+ }
+ smc91c111_pop_rx_fifo(s);
+ break;
+ case 5: /* Release. */
+ smc91c111_release_packet(s, s->packet_num);
+ break;
+ case 6: /* Add to TX FIFO. */
+ smc91c111_queue_tx(s, s->packet_num);
+ break;
+ case 7: /* Reset TX FIFO. */
+ s->tx_fifo_len = 0;
+ s->tx_fifo_done_len = 0;
+ break;
+ }
+ return;
+ case 1:
+ /* Ignore. */
+ return;
+ case 2: /* Packet Number Register */
+ s->packet_num = value;
+ return;
+ case 3: case 4: case 5:
+ /* Should be readonly, but linux writes to them anyway. Ignore. */
+ return;
+ case 6: /* Pointer */
+ SET_LOW(ptr, value);
+ return;
+ case 7:
+ SET_HIGH(ptr, value);
+ return;
+ case 8: case 9: case 10: case 11: /* Data */
+ {
+ int p;
+ int n;
+
+ if (s->ptr & 0x8000)
+ n = s->rx_fifo[0];
+ else
+ n = s->packet_num;
+ p = s->ptr & 0x07ff;
+ if (s->ptr & 0x4000) {
+ s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
+ } else {
+ p += (offset & 3);
+ }
+ s->data[n][p] = value;
+ }
+ return;
+ case 12: /* Interrupt ACK. */
+ s->int_level &= ~(value & 0xd6);
+ if (value & INT_TX)
+ smc91c111_pop_tx_fifo_done(s);
+ smc91c111_update(s);
+ return;
+ case 13: /* Interrupt mask. */
+ s->int_mask = value;
+ smc91c111_update(s);
+ return;
+ }
+ break;;
+
+ case 3:
+ switch (offset) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* Multicast table. */
+ /* Not implemented. */
+ return;
+ case 8: case 9: /* Management Interface. */
+ /* Not implemented. */
+ return;
+ case 12: /* Early receive. */
+ s->ercv = value & 0x1f;
+ case 13:
+ /* Ignore. */
+ return;
+ }
+ break;
+ }
+ hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
+}
+
+static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
+{
+ smc91c111_state *s = (smc91c111_state *)opaque;
+
+ offset = offset & 0xf;
+ if (offset == 14) {
+ return s->bank;
+ }
+ if (offset == 15)
+ return 0x33;
+ switch (s->bank) {
+ case 0:
+ switch (offset) {
+ case 0: /* TCR */
+ return s->tcr & 0xff;
+ case 1:
+ return s->tcr >> 8;
+ case 2: /* EPH Status */
+ return 0;
+ case 3:
+ return 0x40;
+ case 4: /* RCR */
+ return s->rcr & 0xff;
+ case 5:
+ return s->rcr >> 8;
+ case 6: /* Counter */
+ case 7:
+ /* Not implemented. */
+ return 0;
+ case 8: /* Memory size. */
+ return NUM_PACKETS;
+ case 9: /* Free memory available. */
+ {
+ int i;
+ int n;
+ n = 0;
+ for (i = 0; i < NUM_PACKETS; i++) {
+ if (s->allocated & (1 << i))
+ n++;
+ }
+ return n;
+ }
+ case 10: case 11: /* RPCR */
+ /* Not implemented. */
+ return 0;
+ case 12: case 13: /* Reserved */
+ return 0;
+ }
+ break;
+
+ case 1:
+ switch (offset) {
+ case 0: /* CONFIG */
+ return s->cr & 0xff;
+ case 1:
+ return s->cr >> 8;
+ case 2: case 3: /* BASE */
+ /* Not implemented. */
+ return 0;
+ case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+ return s->conf.macaddr.a[offset - 4];
+ case 10: /* General Purpose */
+ return s->gpr & 0xff;
+ case 11:
+ return s->gpr >> 8;
+ case 12: /* Control */
+ return s->ctr & 0xff;
+ case 13:
+ return s->ctr >> 8;
+ }
+ break;
+
+ case 2:
+ switch (offset) {
+ case 0: case 1: /* MMUCR Busy bit. */
+ return 0;
+ case 2: /* Packet Number. */
+ return s->packet_num;
+ case 3: /* Allocation Result. */
+ return s->tx_alloc;
+ case 4: /* TX FIFO */
+ if (s->tx_fifo_done_len == 0)
+ return 0x80;
+ else
+ return s->tx_fifo_done[0];
+ case 5: /* RX FIFO */
+ if (s->rx_fifo_len == 0)
+ return 0x80;
+ else
+ return s->rx_fifo[0];
+ case 6: /* Pointer */
+ return s->ptr & 0xff;
+ case 7:
+ return (s->ptr >> 8) & 0xf7;
+ case 8: case 9: case 10: case 11: /* Data */
+ {
+ int p;
+ int n;
+
+ if (s->ptr & 0x8000)
+ n = s->rx_fifo[0];
+ else
+ n = s->packet_num;
+ p = s->ptr & 0x07ff;
+ if (s->ptr & 0x4000) {
+ s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
+ } else {
+ p += (offset & 3);
+ }
+ return s->data[n][p];
+ }
+ case 12: /* Interrupt status. */
+ return s->int_level;
+ case 13: /* Interrupt mask. */
+ return s->int_mask;
+ }
+ break;
+
+ case 3:
+ switch (offset) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ /* Multicast table. */
+ /* Not implemented. */
+ return 0;
+ case 8: /* Management Interface. */
+ /* Not implemented. */
+ return 0x30;
+ case 9:
+ return 0x33;
+ case 10: /* Revision. */
+ return 0x91;
+ case 11:
+ return 0x33;
+ case 12:
+ return s->ercv;
+ case 13:
+ return 0;
+ }
+ break;
+ }
+ hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset);
+ return 0;
+}
+
+static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ smc91c111_writeb(opaque, offset, value & 0xff);
+ smc91c111_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ /* 32-bit writes to offset 0xc only actually write to the bank select
+ register (offset 0xe) */
+ if (offset != 0xc)
+ smc91c111_writew(opaque, offset, value & 0xffff);
+ smc91c111_writew(opaque, offset + 2, value >> 16);
+}
+
+static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = smc91c111_readb(opaque, offset);
+ val |= smc91c111_readb(opaque, offset + 1) << 8;
+ return val;
+}
+
+static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t val;
+ val = smc91c111_readw(opaque, offset);
+ val |= smc91c111_readw(opaque, offset + 2) << 16;
+ return val;
+}
+
+static int smc91c111_can_receive(VLANClientState *nc)
+{
+ smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+ return 1;
+ if (s->allocated == (1 << NUM_PACKETS) - 1)
+ return 0;
+ return 1;
+}
+
+static ssize_t smc91c111_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int status;
+ int packetsize;
+ uint32_t crc;
+ int packetnum;
+ uint8_t *p;
+
+ if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+ return -1;
+ /* Short packets are padded with zeros. Receiving a packet
+ < 64 bytes long is considered an error condition. */
+ if (size < 64)
+ packetsize = 64;
+ else
+ packetsize = (size & ~1);
+ packetsize += 6;
+ crc = (s->rcr & RCR_STRIP_CRC) == 0;
+ if (crc)
+ packetsize += 4;
+ /* TODO: Flag overrun and receive errors. */
+ if (packetsize > 2048)
+ return -1;
+ packetnum = smc91c111_allocate_packet(s);
+ if (packetnum == 0x80)
+ return -1;
+ s->rx_fifo[s->rx_fifo_len++] = packetnum;
+
+ p = &s->data[packetnum][0];
+ /* ??? Multicast packets? */
+ status = 0;
+ if (size > 1518)
+ status |= RS_TOOLONG;
+ if (size & 1)
+ status |= RS_ODDFRAME;
+ *(p++) = status & 0xff;
+ *(p++) = status >> 8;
+ *(p++) = packetsize & 0xff;
+ *(p++) = packetsize >> 8;
+ memcpy(p, buf, size & ~1);
+ p += (size & ~1);
+ /* Pad short packets. */
+ if (size < 64) {
+ int pad;
+
+ if (size & 1)
+ *(p++) = buf[size - 1];
+ pad = 64 - size;
+ memset(p, 0, pad);
+ p += pad;
+ size = 64;
+ }
+ /* It's not clear if the CRC should go before or after the last byte in
+ odd sized packets. Linux disables the CRC, so that's no help.
+ The pictures in the documentation show the CRC aligned on a 16-bit
+ boundary before the last odd byte, so that's what we do. */
+ if (crc) {
+ crc = crc32(~0, buf, size);
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff; crc >>= 8;
+ *(p++) = crc & 0xff;
+ }
+ if (size & 1) {
+ *(p++) = buf[size - 1];
+ *p = 0x60;
+ } else {
+ *(p++) = 0;
+ *p = 0x40;
+ }
+ /* TODO: Raise early RX interrupt? */
+ s->int_level |= INT_RCV;
+ smc91c111_update(s);
+
+ return size;
+}
+
+static CPUReadMemoryFunc * const smc91c111_readfn[] = {
+ smc91c111_readb,
+ smc91c111_readw,
+ smc91c111_readl
+};
+
+static CPUWriteMemoryFunc * const smc91c111_writefn[] = {
+ smc91c111_writeb,
+ smc91c111_writew,
+ smc91c111_writel
+};
+
+static void smc91c111_cleanup(VLANClientState *nc)
+{
+ smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_smc91c111_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = smc91c111_can_receive,
+ .receive = smc91c111_receive,
+ .cleanup = smc91c111_cleanup,
+};
+
+static int smc91c111_init1(SysBusDevice *dev)
+{
+ smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev);
+
+ s->mmio_index = cpu_register_io_memory(smc91c111_readfn,
+ smc91c111_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 16, s->mmio_index);
+ sysbus_init_irq(dev, &s->irq);
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ smc91c111_reset(s);
+
+ s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ /* ??? Save/restore. */
+ return 0;
+}
+
+static SysBusDeviceInfo smc91c111_info = {
+ .init = smc91c111_init1,
+ .qdev.name = "smc91c111",
+ .qdev.size = sizeof(smc91c111_state),
+ .qdev.vmsd = &vmstate_smc91c111,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(smc91c111_state, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void smc91c111_register_devices(void)
+{
+ sysbus_register_withprop(&smc91c111_info);
+}
+
+/* Legacy helper function. Should go away when machine config files are
+ implemented. */
+void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(nd, "smc91c111");
+ dev = qdev_create(NULL, "smc91c111");
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, base);
+ sysbus_connect_irq(s, 0, irq);
+}
+
+device_init(smc91c111_register_devices)
diff --git a/hw/soc_dma.c b/hw/soc_dma.c
new file mode 100644
index 0000000..23ec516
--- /dev/null
+++ b/hw/soc_dma.c
@@ -0,0 +1,366 @@
+/*
+ * On-chip DMA controller framework.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "soc_dma.h"
+
+static void transfer_mem2mem(struct soc_dma_ch_s *ch)
+{
+ memcpy(ch->paddr[0], ch->paddr[1], ch->bytes);
+ ch->paddr[0] += ch->bytes;
+ ch->paddr[1] += ch->bytes;
+}
+
+static void transfer_mem2fifo(struct soc_dma_ch_s *ch)
+{
+ ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes);
+ ch->paddr[0] += ch->bytes;
+}
+
+static void transfer_fifo2mem(struct soc_dma_ch_s *ch)
+{
+ ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes);
+ ch->paddr[1] += ch->bytes;
+}
+
+/* This is further optimisable but isn't very important because often
+ * DMA peripherals forbid this kind of transfers and even when they don't,
+ * oprating systems may not need to use them. */
+static void *fifo_buf;
+static int fifo_size;
+static void transfer_fifo2fifo(struct soc_dma_ch_s *ch)
+{
+ if (ch->bytes > fifo_size)
+ fifo_buf = qemu_realloc(fifo_buf, fifo_size = ch->bytes);
+
+ /* Implement as transfer_fifo2linear + transfer_linear2fifo. */
+ ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes);
+ ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes);
+}
+
+struct dma_s {
+ struct soc_dma_s soc;
+ int chnum;
+ uint64_t ch_enable_mask;
+ int64_t channel_freq;
+ int enabled_count;
+
+ struct memmap_entry_s {
+ enum soc_dma_port_type type;
+ target_phys_addr_t addr;
+ union {
+ struct {
+ void *opaque;
+ soc_dma_io_t fn;
+ int out;
+ } fifo;
+ struct {
+ void *base;
+ size_t size;
+ } mem;
+ } u;
+ } *memmap;
+ int memmap_size;
+
+ struct soc_dma_ch_s ch[0];
+};
+
+static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes)
+{
+ int64_t now = qemu_get_clock(vm_clock);
+ struct dma_s *dma = (struct dma_s *) ch->dma;
+
+ qemu_mod_timer(ch->timer, now + delay_bytes / dma->channel_freq);
+}
+
+static void soc_dma_ch_run(void *opaque)
+{
+ struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque;
+
+ ch->running = 1;
+ ch->dma->setup_fn(ch);
+ ch->transfer_fn(ch);
+ ch->running = 0;
+
+ if (ch->enable)
+ soc_dma_ch_schedule(ch, ch->bytes);
+ ch->bytes = 0;
+}
+
+static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma,
+ target_phys_addr_t addr)
+{
+ struct memmap_entry_s *lo;
+ int hi;
+
+ lo = dma->memmap;
+ hi = dma->memmap_size;
+
+ while (hi > 1) {
+ hi /= 2;
+ if (lo[hi].addr <= addr)
+ lo += hi;
+ }
+
+ return lo;
+}
+
+static inline enum soc_dma_port_type soc_dma_ch_update_type(
+ struct soc_dma_ch_s *ch, int port)
+{
+ struct dma_s *dma = (struct dma_s *) ch->dma;
+ struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]);
+
+ if (entry->type == soc_dma_port_fifo) {
+ while (entry < dma->memmap + dma->memmap_size &&
+ entry->u.fifo.out != port)
+ entry ++;
+ if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port)
+ return soc_dma_port_other;
+
+ if (ch->type[port] != soc_dma_access_const)
+ return soc_dma_port_other;
+
+ ch->io_fn[port] = entry->u.fifo.fn;
+ ch->io_opaque[port] = entry->u.fifo.opaque;
+ return soc_dma_port_fifo;
+ } else if (entry->type == soc_dma_port_mem) {
+ if (entry->addr > ch->vaddr[port] ||
+ entry->addr + entry->u.mem.size <= ch->vaddr[port])
+ return soc_dma_port_other;
+
+ /* TODO: support constant memory address for source port as used for
+ * drawing solid rectangles by PalmOS(R). */
+ if (ch->type[port] != soc_dma_access_const)
+ return soc_dma_port_other;
+
+ ch->paddr[port] = (uint8_t *) entry->u.mem.base +
+ (ch->vaddr[port] - entry->addr);
+ /* TODO: save bytes left to the end of the mapping somewhere so we
+ * can check we're not reading beyond it. */
+ return soc_dma_port_mem;
+ } else
+ return soc_dma_port_other;
+}
+
+void soc_dma_ch_update(struct soc_dma_ch_s *ch)
+{
+ enum soc_dma_port_type src, dst;
+
+ src = soc_dma_ch_update_type(ch, 0);
+ if (src == soc_dma_port_other) {
+ ch->update = 0;
+ ch->transfer_fn = ch->dma->transfer_fn;
+ return;
+ }
+ dst = soc_dma_ch_update_type(ch, 1);
+
+ /* TODO: use src and dst as array indices. */
+ if (src == soc_dma_port_mem && dst == soc_dma_port_mem)
+ ch->transfer_fn = transfer_mem2mem;
+ else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo)
+ ch->transfer_fn = transfer_mem2fifo;
+ else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem)
+ ch->transfer_fn = transfer_fifo2mem;
+ else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo)
+ ch->transfer_fn = transfer_fifo2fifo;
+ else
+ ch->transfer_fn = ch->dma->transfer_fn;
+
+ ch->update = (dst != soc_dma_port_other);
+}
+
+static void soc_dma_ch_freq_update(struct dma_s *s)
+{
+ if (s->enabled_count)
+ /* We completely ignore channel priorities and stuff */
+ s->channel_freq = s->soc.freq / s->enabled_count;
+ else {
+ /* TODO: Signal that we want to disable the functional clock and let
+ * the platform code decide what to do with it, i.e. check that
+ * auto-idle is enabled in the clock controller and if we are stopping
+ * the clock, do the same with any parent clocks that had only one
+ * user keeping them on and auto-idle enabled. */
+ }
+}
+
+void soc_dma_set_request(struct soc_dma_ch_s *ch, int level)
+{
+ struct dma_s *dma = (struct dma_s *) ch->dma;
+
+ dma->enabled_count += level - ch->enable;
+
+ if (level)
+ dma->ch_enable_mask |= 1 << ch->num;
+ else
+ dma->ch_enable_mask &= ~(1 << ch->num);
+
+ if (level != ch->enable) {
+ soc_dma_ch_freq_update(dma);
+ ch->enable = level;
+
+ if (!ch->enable)
+ qemu_del_timer(ch->timer);
+ else if (!ch->running)
+ soc_dma_ch_run(ch);
+ else
+ soc_dma_ch_schedule(ch, 1);
+ }
+}
+
+void soc_dma_reset(struct soc_dma_s *soc)
+{
+ struct dma_s *s = (struct dma_s *) soc;
+
+ s->soc.drqbmp = 0;
+ s->ch_enable_mask = 0;
+ s->enabled_count = 0;
+ soc_dma_ch_freq_update(s);
+}
+
+/* TODO: take a functional-clock argument */
+struct soc_dma_s *soc_dma_init(int n)
+{
+ int i;
+ struct dma_s *s = qemu_mallocz(sizeof(*s) + n * sizeof(*s->ch));
+
+ s->chnum = n;
+ s->soc.ch = s->ch;
+ for (i = 0; i < n; i ++) {
+ s->ch[i].dma = &s->soc;
+ s->ch[i].num = i;
+ s->ch[i].timer = qemu_new_timer(vm_clock, soc_dma_ch_run, &s->ch[i]);
+ }
+
+ soc_dma_reset(&s->soc);
+ fifo_size = 0;
+
+ return &s->soc;
+}
+
+void soc_dma_port_add_fifo(struct soc_dma_s *soc, target_phys_addr_t virt_base,
+ soc_dma_io_t fn, void *opaque, int out)
+{
+ struct memmap_entry_s *entry;
+ struct dma_s *dma = (struct dma_s *) soc;
+
+ dma->memmap = qemu_realloc(dma->memmap, sizeof(*entry) *
+ (dma->memmap_size + 1));
+ entry = soc_dma_lookup(dma, virt_base);
+
+ if (dma->memmap_size) {
+ if (entry->type == soc_dma_port_mem) {
+ if (entry->addr <= virt_base &&
+ entry->addr + entry->u.mem.size > virt_base) {
+ fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
+ " collides with RAM region at " TARGET_FMT_lx
+ "-" TARGET_FMT_lx "\n", __FUNCTION__,
+ (target_ulong) virt_base,
+ (target_ulong) entry->addr, (target_ulong)
+ (entry->addr + entry->u.mem.size));
+ exit(-1);
+ }
+
+ if (entry->addr <= virt_base)
+ entry ++;
+ } else
+ while (entry < dma->memmap + dma->memmap_size &&
+ entry->addr <= virt_base) {
+ if (entry->addr == virt_base && entry->u.fifo.out == out) {
+ fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
+ " collides FIFO at " TARGET_FMT_lx "\n",
+ __FUNCTION__, (target_ulong) virt_base,
+ (target_ulong) entry->addr);
+ exit(-1);
+ }
+
+ entry ++;
+ }
+
+ memmove(entry + 1, entry,
+ (uint8_t *) (dma->memmap + dma->memmap_size ++) -
+ (uint8_t *) entry);
+ } else
+ dma->memmap_size ++;
+
+ entry->addr = virt_base;
+ entry->type = soc_dma_port_fifo;
+ entry->u.fifo.fn = fn;
+ entry->u.fifo.opaque = opaque;
+ entry->u.fifo.out = out;
+}
+
+void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
+ target_phys_addr_t virt_base, size_t size)
+{
+ struct memmap_entry_s *entry;
+ struct dma_s *dma = (struct dma_s *) soc;
+
+ dma->memmap = qemu_realloc(dma->memmap, sizeof(*entry) *
+ (dma->memmap_size + 1));
+ entry = soc_dma_lookup(dma, virt_base);
+
+ if (dma->memmap_size) {
+ if (entry->type == soc_dma_port_mem) {
+ if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
+ (entry->addr <= virt_base &&
+ entry->addr + entry->u.mem.size > virt_base)) {
+ fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
+ " collides with RAM region at " TARGET_FMT_lx
+ "-" TARGET_FMT_lx "\n", __FUNCTION__,
+ (target_ulong) virt_base,
+ (target_ulong) (virt_base + size),
+ (target_ulong) entry->addr, (target_ulong)
+ (entry->addr + entry->u.mem.size));
+ exit(-1);
+ }
+
+ if (entry->addr <= virt_base)
+ entry ++;
+ } else {
+ if (entry->addr >= virt_base &&
+ entry->addr < virt_base + size) {
+ fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
+ " collides with FIFO at " TARGET_FMT_lx
+ "\n", __FUNCTION__,
+ (target_ulong) virt_base,
+ (target_ulong) (virt_base + size),
+ (target_ulong) entry->addr);
+ exit(-1);
+ }
+
+ while (entry < dma->memmap + dma->memmap_size &&
+ entry->addr <= virt_base)
+ entry ++;
+ }
+
+ memmove(entry + 1, entry,
+ (uint8_t *) (dma->memmap + dma->memmap_size ++) -
+ (uint8_t *) entry);
+ } else
+ dma->memmap_size ++;
+
+ entry->addr = virt_base;
+ entry->type = soc_dma_port_mem;
+ entry->u.mem.base = phys_base;
+ entry->u.mem.size = size;
+}
+
+/* TODO: port removal for ports like PCMCIA memory */
diff --git a/hw/soc_dma.h b/hw/soc_dma.h
new file mode 100644
index 0000000..c0ebb8d
--- /dev/null
+++ b/hw/soc_dma.h
@@ -0,0 +1,113 @@
+/*
+ * On-chip DMA controller framework.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+struct soc_dma_s;
+struct soc_dma_ch_s;
+typedef void (*soc_dma_io_t)(void *opaque, uint8_t *buf, int len);
+typedef void (*soc_dma_transfer_t)(struct soc_dma_ch_s *ch);
+
+enum soc_dma_port_type {
+ soc_dma_port_mem,
+ soc_dma_port_fifo,
+ soc_dma_port_other,
+};
+
+enum soc_dma_access_type {
+ soc_dma_access_const,
+ soc_dma_access_linear,
+ soc_dma_access_other,
+};
+
+struct soc_dma_ch_s {
+ /* Private */
+ struct soc_dma_s *dma;
+ int num;
+ QEMUTimer *timer;
+
+ /* Set by soc_dma.c */
+ int enable;
+ int update;
+
+ /* This should be set by dma->setup_fn(). */
+ int bytes;
+ /* Initialised by the DMA module, call soc_dma_ch_update after writing. */
+ enum soc_dma_access_type type[2];
+ target_phys_addr_t vaddr[2]; /* Updated by .transfer_fn(). */
+ /* Private */
+ void *paddr[2];
+ soc_dma_io_t io_fn[2];
+ void *io_opaque[2];
+
+ int running;
+ soc_dma_transfer_t transfer_fn;
+
+ /* Set and used by the DMA module. */
+ void *opaque;
+};
+
+struct soc_dma_s {
+ /* Following fields are set by the SoC DMA module and can be used
+ * by anybody. */
+ uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */
+ qemu_irq *drq;
+ void *opaque;
+ int64_t freq;
+ soc_dma_transfer_t transfer_fn;
+ soc_dma_transfer_t setup_fn;
+ /* Set by soc_dma_init() for use by the DMA module. */
+ struct soc_dma_ch_s *ch;
+};
+
+/* Call to activate or stop a DMA channel. */
+void soc_dma_set_request(struct soc_dma_ch_s *ch, int level);
+/* Call after every write to one of the following fields and before
+ * calling soc_dma_set_request(ch, 1):
+ * ch->type[0...1],
+ * ch->vaddr[0...1],
+ * ch->paddr[0...1],
+ * or after a soc_dma_port_add_fifo() or soc_dma_port_add_mem(). */
+void soc_dma_ch_update(struct soc_dma_ch_s *ch);
+
+/* The SoC should call this when the DMA module is being reset. */
+void soc_dma_reset(struct soc_dma_s *s);
+struct soc_dma_s *soc_dma_init(int n);
+
+void soc_dma_port_add_fifo(struct soc_dma_s *dma, target_phys_addr_t virt_base,
+ soc_dma_io_t fn, void *opaque, int out);
+void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base,
+ target_phys_addr_t virt_base, size_t size);
+
+static inline void soc_dma_port_add_fifo_in(struct soc_dma_s *dma,
+ target_phys_addr_t virt_base, soc_dma_io_t fn, void *opaque)
+{
+ return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 0);
+}
+
+static inline void soc_dma_port_add_fifo_out(struct soc_dma_s *dma,
+ target_phys_addr_t virt_base, soc_dma_io_t fn, void *opaque)
+{
+ return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 1);
+}
+
+static inline void soc_dma_port_add_mem_ram(struct soc_dma_s *dma,
+ ram_addr_t offset, target_phys_addr_t virt_base, size_t size)
+{
+ return soc_dma_port_add_mem(dma, qemu_get_ram_ptr(offset), virt_base, size);
+}
diff --git a/hw/sparc32_dma.c b/hw/sparc32_dma.c
new file mode 100644
index 0000000..e75694b
--- /dev/null
+++ b/hw/sparc32_dma.c
@@ -0,0 +1,305 @@
+/*
+ * QEMU Sparc32 DMA controller emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Modifications:
+ * 2010-Feb-14 Artyom Tarasenko : reworked irq generation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "sparc32_dma.h"
+#include "sun4m.h"
+#include "sysbus.h"
+#include "trace.h"
+
+/*
+ * This is the DMA controller part of chip STP2000 (Master I/O), also
+ * produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/DMA2.txt
+ */
+
+#define DMA_REGS 4
+#define DMA_SIZE (4 * sizeof(uint32_t))
+/* We need the mask, because one instance of the device is not page
+ aligned (ledma, start address 0x0010) */
+#define DMA_MASK (DMA_SIZE - 1)
+/* OBP says 0x20 bytes for ledma, the extras are aliased to espdma */
+#define DMA_ETH_SIZE (8 * sizeof(uint32_t))
+#define DMA_MAX_REG_OFFSET (2 * DMA_SIZE - 1)
+
+#define DMA_VER 0xa0000000
+#define DMA_INTR 1
+#define DMA_INTREN 0x10
+#define DMA_WRITE_MEM 0x100
+#define DMA_EN 0x200
+#define DMA_LOADED 0x04000000
+#define DMA_DRAIN_FIFO 0x40
+#define DMA_RESET 0x80
+
+/* XXX SCSI and ethernet should have different read-only bit masks */
+#define DMA_CSR_RO_MASK 0xfe000007
+
+typedef struct DMAState DMAState;
+
+struct DMAState {
+ SysBusDevice busdev;
+ uint32_t dmaregs[DMA_REGS];
+ qemu_irq irq;
+ void *iommu;
+ qemu_irq gpio[2];
+ uint32_t is_ledma;
+};
+
+enum {
+ GPIO_RESET = 0,
+ GPIO_DMA,
+};
+
+/* Note: on sparc, the lance 16 bit bus is swapped */
+void ledma_memory_read(void *opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap)
+{
+ DMAState *s = opaque;
+ int i;
+
+ addr |= s->dmaregs[3];
+ trace_ledma_memory_read(addr);
+ if (do_bswap) {
+ sparc_iommu_memory_read(s->iommu, addr, buf, len);
+ } else {
+ addr &= ~1;
+ len &= ~1;
+ sparc_iommu_memory_read(s->iommu, addr, buf, len);
+ for(i = 0; i < len; i += 2) {
+ bswap16s((uint16_t *)(buf + i));
+ }
+ }
+}
+
+void ledma_memory_write(void *opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap)
+{
+ DMAState *s = opaque;
+ int l, i;
+ uint16_t tmp_buf[32];
+
+ addr |= s->dmaregs[3];
+ trace_ledma_memory_write(addr);
+ if (do_bswap) {
+ sparc_iommu_memory_write(s->iommu, addr, buf, len);
+ } else {
+ addr &= ~1;
+ len &= ~1;
+ while (len > 0) {
+ l = len;
+ if (l > sizeof(tmp_buf))
+ l = sizeof(tmp_buf);
+ for(i = 0; i < l; i += 2) {
+ tmp_buf[i >> 1] = bswap16(*(uint16_t *)(buf + i));
+ }
+ sparc_iommu_memory_write(s->iommu, addr, (uint8_t *)tmp_buf, l);
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+ }
+}
+
+static void dma_set_irq(void *opaque, int irq, int level)
+{
+ DMAState *s = opaque;
+ if (level) {
+ s->dmaregs[0] |= DMA_INTR;
+ if (s->dmaregs[0] & DMA_INTREN) {
+ trace_sparc32_dma_set_irq_raise();
+ qemu_irq_raise(s->irq);
+ }
+ } else {
+ if (s->dmaregs[0] & DMA_INTR) {
+ s->dmaregs[0] &= ~DMA_INTR;
+ if (s->dmaregs[0] & DMA_INTREN) {
+ trace_sparc32_dma_set_irq_lower();
+ qemu_irq_lower(s->irq);
+ }
+ }
+ }
+}
+
+void espdma_memory_read(void *opaque, uint8_t *buf, int len)
+{
+ DMAState *s = opaque;
+
+ trace_espdma_memory_read(s->dmaregs[1]);
+ sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len);
+ s->dmaregs[1] += len;
+}
+
+void espdma_memory_write(void *opaque, uint8_t *buf, int len)
+{
+ DMAState *s = opaque;
+
+ trace_espdma_memory_write(s->dmaregs[1]);
+ sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len);
+ s->dmaregs[1] += len;
+}
+
+static uint32_t dma_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ DMAState *s = opaque;
+ uint32_t saddr;
+
+ if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) {
+ /* aliased to espdma, but we can't get there from here */
+ /* buggy driver if using undocumented behavior, just return 0 */
+ trace_sparc32_dma_mem_readl(addr, 0);
+ return 0;
+ }
+ saddr = (addr & DMA_MASK) >> 2;
+ trace_sparc32_dma_mem_readl(addr, s->dmaregs[saddr]);
+ return s->dmaregs[saddr];
+}
+
+static void dma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ DMAState *s = opaque;
+ uint32_t saddr;
+
+ if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) {
+ /* aliased to espdma, but we can't get there from here */
+ trace_sparc32_dma_mem_writel(addr, 0, val);
+ return;
+ }
+ saddr = (addr & DMA_MASK) >> 2;
+ trace_sparc32_dma_mem_writel(addr, s->dmaregs[saddr], val);
+ switch (saddr) {
+ case 0:
+ if (val & DMA_INTREN) {
+ if (s->dmaregs[0] & DMA_INTR) {
+ trace_sparc32_dma_set_irq_raise();
+ qemu_irq_raise(s->irq);
+ }
+ } else {
+ if (s->dmaregs[0] & (DMA_INTR | DMA_INTREN)) {
+ trace_sparc32_dma_set_irq_lower();
+ qemu_irq_lower(s->irq);
+ }
+ }
+ if (val & DMA_RESET) {
+ qemu_irq_raise(s->gpio[GPIO_RESET]);
+ qemu_irq_lower(s->gpio[GPIO_RESET]);
+ } else if (val & DMA_DRAIN_FIFO) {
+ val &= ~DMA_DRAIN_FIFO;
+ } else if (val == 0)
+ val = DMA_DRAIN_FIFO;
+
+ if (val & DMA_EN && !(s->dmaregs[0] & DMA_EN)) {
+ trace_sparc32_dma_enable_raise();
+ qemu_irq_raise(s->gpio[GPIO_DMA]);
+ } else if (!(val & DMA_EN) && !!(s->dmaregs[0] & DMA_EN)) {
+ trace_sparc32_dma_enable_lower();
+ qemu_irq_lower(s->gpio[GPIO_DMA]);
+ }
+
+ val &= ~DMA_CSR_RO_MASK;
+ val |= DMA_VER;
+ s->dmaregs[0] = (s->dmaregs[0] & DMA_CSR_RO_MASK) | val;
+ break;
+ case 1:
+ s->dmaregs[0] |= DMA_LOADED;
+ /* fall through */
+ default:
+ s->dmaregs[saddr] = val;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const dma_mem_read[3] = {
+ NULL,
+ NULL,
+ dma_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const dma_mem_write[3] = {
+ NULL,
+ NULL,
+ dma_mem_writel,
+};
+
+static void dma_reset(DeviceState *d)
+{
+ DMAState *s = container_of(d, DMAState, busdev.qdev);
+
+ memset(s->dmaregs, 0, DMA_SIZE);
+ s->dmaregs[0] = DMA_VER;
+}
+
+static const VMStateDescription vmstate_dma = {
+ .name ="sparc32_dma",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(dmaregs, DMAState, DMA_REGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int sparc32_dma_init1(SysBusDevice *dev)
+{
+ DMAState *s = FROM_SYSBUS(DMAState, dev);
+ int dma_io_memory;
+ int reg_size;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ dma_io_memory = cpu_register_io_memory(dma_mem_read, dma_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE;
+ sysbus_init_mmio(dev, reg_size, dma_io_memory);
+
+ qdev_init_gpio_in(&dev->qdev, dma_set_irq, 1);
+ qdev_init_gpio_out(&dev->qdev, s->gpio, 2);
+
+ return 0;
+}
+
+static SysBusDeviceInfo sparc32_dma_info = {
+ .init = sparc32_dma_init1,
+ .qdev.name = "sparc32_dma",
+ .qdev.size = sizeof(DMAState),
+ .qdev.vmsd = &vmstate_dma,
+ .qdev.reset = dma_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_PTR("iommu_opaque", DMAState, iommu),
+ DEFINE_PROP_UINT32("is_ledma", DMAState, is_ledma, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void sparc32_dma_register_devices(void)
+{
+ sysbus_register_withprop(&sparc32_dma_info);
+}
+
+device_init(sparc32_dma_register_devices)
diff --git a/hw/sparc32_dma.h b/hw/sparc32_dma.h
new file mode 100644
index 0000000..8b72c37
--- /dev/null
+++ b/hw/sparc32_dma.h
@@ -0,0 +1,12 @@
+#ifndef SPARC32_DMA_H
+#define SPARC32_DMA_H
+
+/* sparc32_dma.c */
+void ledma_memory_read(void *opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap);
+void ledma_memory_write(void *opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int do_bswap);
+void espdma_memory_read(void *opaque, uint8_t *buf, int len);
+void espdma_memory_write(void *opaque, uint8_t *buf, int len);
+
+#endif
diff --git a/hw/spitz.c b/hw/spitz.c
new file mode 100644
index 0000000..5b1e42d
--- /dev/null
+++ b/hw/spitz.c
@@ -0,0 +1,1117 @@
+/*
+ * PXA270-based Clamshell PDA platforms.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+#include "pcmcia.h"
+#include "i2c.h"
+#include "ssi.h"
+#include "flash.h"
+#include "qemu-timer.h"
+#include "devices.h"
+#include "sharpsl.h"
+#include "console.h"
+#include "block.h"
+#include "audio/audio.h"
+#include "boards.h"
+#include "blockdev.h"
+#include "sysbus.h"
+
+#undef REG_FMT
+#define REG_FMT "0x%02lx"
+
+/* Spitz Flash */
+#define FLASH_BASE 0x0c000000
+#define FLASH_ECCLPLB 0x00 /* Line parity 7 - 0 bit */
+#define FLASH_ECCLPUB 0x04 /* Line parity 15 - 8 bit */
+#define FLASH_ECCCP 0x08 /* Column parity 5 - 0 bit */
+#define FLASH_ECCCNTR 0x0c /* ECC byte counter */
+#define FLASH_ECCCLRR 0x10 /* Clear ECC */
+#define FLASH_FLASHIO 0x14 /* Flash I/O */
+#define FLASH_FLASHCTL 0x18 /* Flash Control */
+
+#define FLASHCTL_CE0 (1 << 0)
+#define FLASHCTL_CLE (1 << 1)
+#define FLASHCTL_ALE (1 << 2)
+#define FLASHCTL_WP (1 << 3)
+#define FLASHCTL_CE1 (1 << 4)
+#define FLASHCTL_RYBY (1 << 5)
+#define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1)
+
+typedef struct {
+ SysBusDevice busdev;
+ NANDFlashState *nand;
+ uint8_t ctl;
+ uint8_t manf_id;
+ uint8_t chip_id;
+ ECCState ecc;
+} SLNANDState;
+
+static uint32_t sl_readb(void *opaque, target_phys_addr_t addr)
+{
+ SLNANDState *s = (SLNANDState *) opaque;
+ int ryby;
+
+ switch (addr) {
+#define BSHR(byte, from, to) ((s->ecc.lp[byte] >> (from - to)) & (1 << to))
+ case FLASH_ECCLPLB:
+ return BSHR(0, 4, 0) | BSHR(0, 5, 2) | BSHR(0, 6, 4) | BSHR(0, 7, 6) |
+ BSHR(1, 4, 1) | BSHR(1, 5, 3) | BSHR(1, 6, 5) | BSHR(1, 7, 7);
+
+#define BSHL(byte, from, to) ((s->ecc.lp[byte] << (to - from)) & (1 << to))
+ case FLASH_ECCLPUB:
+ return BSHL(0, 0, 0) | BSHL(0, 1, 2) | BSHL(0, 2, 4) | BSHL(0, 3, 6) |
+ BSHL(1, 0, 1) | BSHL(1, 1, 3) | BSHL(1, 2, 5) | BSHL(1, 3, 7);
+
+ case FLASH_ECCCP:
+ return s->ecc.cp;
+
+ case FLASH_ECCCNTR:
+ return s->ecc.count & 0xff;
+
+ case FLASH_FLASHCTL:
+ nand_getpins(s->nand, &ryby);
+ if (ryby)
+ return s->ctl | FLASHCTL_RYBY;
+ else
+ return s->ctl;
+
+ case FLASH_FLASHIO:
+ return ecc_digest(&s->ecc, nand_getio(s->nand));
+
+ default:
+ zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr);
+ }
+ return 0;
+}
+
+static uint32_t sl_readl(void *opaque, target_phys_addr_t addr)
+{
+ SLNANDState *s = (SLNANDState *) opaque;
+
+ if (addr == FLASH_FLASHIO)
+ return ecc_digest(&s->ecc, nand_getio(s->nand)) |
+ (ecc_digest(&s->ecc, nand_getio(s->nand)) << 16);
+
+ return sl_readb(opaque, addr);
+}
+
+static void sl_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ SLNANDState *s = (SLNANDState *) opaque;
+
+ switch (addr) {
+ case FLASH_ECCCLRR:
+ /* Value is ignored. */
+ ecc_reset(&s->ecc);
+ break;
+
+ case FLASH_FLASHCTL:
+ s->ctl = value & 0xff & ~FLASHCTL_RYBY;
+ nand_setpins(s->nand,
+ s->ctl & FLASHCTL_CLE,
+ s->ctl & FLASHCTL_ALE,
+ s->ctl & FLASHCTL_NCE,
+ s->ctl & FLASHCTL_WP,
+ 0);
+ break;
+
+ case FLASH_FLASHIO:
+ nand_setio(s->nand, ecc_digest(&s->ecc, value & 0xff));
+ break;
+
+ default:
+ zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr);
+ }
+}
+
+enum {
+ FLASH_128M,
+ FLASH_1024M,
+};
+
+static CPUReadMemoryFunc * const sl_readfn[] = {
+ sl_readb,
+ sl_readb,
+ sl_readl,
+};
+static CPUWriteMemoryFunc * const sl_writefn[] = {
+ sl_writeb,
+ sl_writeb,
+ sl_writeb,
+};
+
+static void sl_flash_register(PXA2xxState *cpu, int size)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "sl-nand");
+
+ qdev_prop_set_uint8(dev, "manf_id", NAND_MFR_SAMSUNG);
+ if (size == FLASH_128M)
+ qdev_prop_set_uint8(dev, "chip_id", 0x73);
+ else if (size == FLASH_1024M)
+ qdev_prop_set_uint8(dev, "chip_id", 0xf1);
+
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, FLASH_BASE);
+}
+
+static int sl_nand_init(SysBusDevice *dev) {
+ int iomemtype;
+ SLNANDState *s;
+
+ s = FROM_SYSBUS(SLNANDState, dev);
+
+ s->ctl = 0;
+ s->nand = nand_init(s->manf_id, s->chip_id);
+
+ iomemtype = cpu_register_io_memory(sl_readfn,
+ sl_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x40, iomemtype);
+
+ return 0;
+}
+
+/* Spitz Keyboard */
+
+#define SPITZ_KEY_STROBE_NUM 11
+#define SPITZ_KEY_SENSE_NUM 7
+
+static const int spitz_gpio_key_sense[SPITZ_KEY_SENSE_NUM] = {
+ 12, 17, 91, 34, 36, 38, 39
+};
+
+static const int spitz_gpio_key_strobe[SPITZ_KEY_STROBE_NUM] = {
+ 88, 23, 24, 25, 26, 27, 52, 103, 107, 108, 114
+};
+
+/* Eighth additional row maps the special keys */
+static int spitz_keymap[SPITZ_KEY_SENSE_NUM + 1][SPITZ_KEY_STROBE_NUM] = {
+ { 0x1d, 0x02, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0e, 0x3f, 0x40 },
+ { -1 , 0x03, 0x05, 0x13, 0x15, 0x09, 0x17, 0x18, 0x19, 0x41, 0x42 },
+ { 0x0f, 0x10, 0x12, 0x14, 0x22, 0x16, 0x24, 0x25, -1 , -1 , -1 },
+ { 0x3c, 0x11, 0x1f, 0x21, 0x2f, 0x23, 0x32, 0x26, -1 , 0x36, -1 },
+ { 0x3b, 0x1e, 0x20, 0x2e, 0x30, 0x31, 0x34, -1 , 0x1c, 0x2a, -1 },
+ { 0x44, 0x2c, 0x2d, 0x0c, 0x39, 0x33, -1 , 0x48, -1 , -1 , 0x38 },
+ { 0x37, 0x3d, -1 , 0x45, 0x57, 0x58, 0x4b, 0x50, 0x4d, -1 , -1 },
+ { 0x52, 0x43, 0x01, 0x47, 0x49, -1 , -1 , -1 , -1 , -1 , -1 },
+};
+
+#define SPITZ_GPIO_AK_INT 13 /* Remote control */
+#define SPITZ_GPIO_SYNC 16 /* Sync button */
+#define SPITZ_GPIO_ON_KEY 95 /* Power button */
+#define SPITZ_GPIO_SWA 97 /* Lid */
+#define SPITZ_GPIO_SWB 96 /* Tablet mode */
+
+/* The special buttons are mapped to unused keys */
+static const int spitz_gpiomap[5] = {
+ SPITZ_GPIO_AK_INT, SPITZ_GPIO_SYNC, SPITZ_GPIO_ON_KEY,
+ SPITZ_GPIO_SWA, SPITZ_GPIO_SWB,
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq sense[SPITZ_KEY_SENSE_NUM];
+ qemu_irq gpiomap[5];
+ int keymap[0x80];
+ uint16_t keyrow[SPITZ_KEY_SENSE_NUM];
+ uint16_t strobe_state;
+ uint16_t sense_state;
+
+ uint16_t pre_map[0x100];
+ uint16_t modifiers;
+ uint16_t imodifiers;
+ uint8_t fifo[16];
+ int fifopos, fifolen;
+ QEMUTimer *kbdtimer;
+} SpitzKeyboardState;
+
+static void spitz_keyboard_sense_update(SpitzKeyboardState *s)
+{
+ int i;
+ uint16_t strobe, sense = 0;
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) {
+ strobe = s->keyrow[i] & s->strobe_state;
+ if (strobe) {
+ sense |= 1 << i;
+ if (!(s->sense_state & (1 << i)))
+ qemu_irq_raise(s->sense[i]);
+ } else if (s->sense_state & (1 << i))
+ qemu_irq_lower(s->sense[i]);
+ }
+
+ s->sense_state = sense;
+}
+
+static void spitz_keyboard_strobe(void *opaque, int line, int level)
+{
+ SpitzKeyboardState *s = (SpitzKeyboardState *) opaque;
+
+ if (level)
+ s->strobe_state |= 1 << line;
+ else
+ s->strobe_state &= ~(1 << line);
+ spitz_keyboard_sense_update(s);
+}
+
+static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode)
+{
+ int spitz_keycode = s->keymap[keycode & 0x7f];
+ if (spitz_keycode == -1)
+ return;
+
+ /* Handle the additional keys */
+ if ((spitz_keycode >> 4) == SPITZ_KEY_SENSE_NUM) {
+ qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80));
+ return;
+ }
+
+ if (keycode & 0x80)
+ s->keyrow[spitz_keycode >> 4] &= ~(1 << (spitz_keycode & 0xf));
+ else
+ s->keyrow[spitz_keycode >> 4] |= 1 << (spitz_keycode & 0xf);
+
+ spitz_keyboard_sense_update(s);
+}
+
+#define SHIFT (1 << 7)
+#define CTRL (1 << 8)
+#define FN (1 << 9)
+
+#define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c
+
+static void spitz_keyboard_handler(void *opaque, int keycode)
+{
+ SpitzKeyboardState *s = opaque;
+ uint16_t code;
+ int mapcode;
+ switch (keycode) {
+ case 0x2a: /* Left Shift */
+ s->modifiers |= 1;
+ break;
+ case 0xaa:
+ s->modifiers &= ~1;
+ break;
+ case 0x36: /* Right Shift */
+ s->modifiers |= 2;
+ break;
+ case 0xb6:
+ s->modifiers &= ~2;
+ break;
+ case 0x1d: /* Control */
+ s->modifiers |= 4;
+ break;
+ case 0x9d:
+ s->modifiers &= ~4;
+ break;
+ case 0x38: /* Alt */
+ s->modifiers |= 8;
+ break;
+ case 0xb8:
+ s->modifiers &= ~8;
+ break;
+ }
+
+ code = s->pre_map[mapcode = ((s->modifiers & 3) ?
+ (keycode | SHIFT) :
+ (keycode & ~SHIFT))];
+
+ if (code != mapcode) {
+#if 0
+ if ((code & SHIFT) && !(s->modifiers & 1))
+ QUEUE_KEY(0x2a | (keycode & 0x80));
+ if ((code & CTRL ) && !(s->modifiers & 4))
+ QUEUE_KEY(0x1d | (keycode & 0x80));
+ if ((code & FN ) && !(s->modifiers & 8))
+ QUEUE_KEY(0x38 | (keycode & 0x80));
+ if ((code & FN ) && (s->modifiers & 1))
+ QUEUE_KEY(0x2a | (~keycode & 0x80));
+ if ((code & FN ) && (s->modifiers & 2))
+ QUEUE_KEY(0x36 | (~keycode & 0x80));
+#else
+ if (keycode & 0x80) {
+ if ((s->imodifiers & 1 ) && !(s->modifiers & 1))
+ QUEUE_KEY(0x2a | 0x80);
+ if ((s->imodifiers & 4 ) && !(s->modifiers & 4))
+ QUEUE_KEY(0x1d | 0x80);
+ if ((s->imodifiers & 8 ) && !(s->modifiers & 8))
+ QUEUE_KEY(0x38 | 0x80);
+ if ((s->imodifiers & 0x10) && (s->modifiers & 1))
+ QUEUE_KEY(0x2a);
+ if ((s->imodifiers & 0x20) && (s->modifiers & 2))
+ QUEUE_KEY(0x36);
+ s->imodifiers = 0;
+ } else {
+ if ((code & SHIFT) && !((s->modifiers | s->imodifiers) & 1)) {
+ QUEUE_KEY(0x2a);
+ s->imodifiers |= 1;
+ }
+ if ((code & CTRL ) && !((s->modifiers | s->imodifiers) & 4)) {
+ QUEUE_KEY(0x1d);
+ s->imodifiers |= 4;
+ }
+ if ((code & FN ) && !((s->modifiers | s->imodifiers) & 8)) {
+ QUEUE_KEY(0x38);
+ s->imodifiers |= 8;
+ }
+ if ((code & FN ) && (s->modifiers & 1) &&
+ !(s->imodifiers & 0x10)) {
+ QUEUE_KEY(0x2a | 0x80);
+ s->imodifiers |= 0x10;
+ }
+ if ((code & FN ) && (s->modifiers & 2) &&
+ !(s->imodifiers & 0x20)) {
+ QUEUE_KEY(0x36 | 0x80);
+ s->imodifiers |= 0x20;
+ }
+ }
+#endif
+ }
+
+ QUEUE_KEY((code & 0x7f) | (keycode & 0x80));
+}
+
+static void spitz_keyboard_tick(void *opaque)
+{
+ SpitzKeyboardState *s = (SpitzKeyboardState *) opaque;
+
+ if (s->fifolen) {
+ spitz_keyboard_keydown(s, s->fifo[s->fifopos ++]);
+ s->fifolen --;
+ if (s->fifopos >= 16)
+ s->fifopos = 0;
+ }
+
+ qemu_mod_timer(s->kbdtimer, qemu_get_clock(vm_clock) +
+ get_ticks_per_sec() / 32);
+}
+
+static void spitz_keyboard_pre_map(SpitzKeyboardState *s)
+{
+ int i;
+ for (i = 0; i < 0x100; i ++)
+ s->pre_map[i] = i;
+ s->pre_map[0x02 | SHIFT ] = 0x02 | SHIFT; /* exclam */
+ s->pre_map[0x28 | SHIFT ] = 0x03 | SHIFT; /* quotedbl */
+ s->pre_map[0x04 | SHIFT ] = 0x04 | SHIFT; /* numbersign */
+ s->pre_map[0x05 | SHIFT ] = 0x05 | SHIFT; /* dollar */
+ s->pre_map[0x06 | SHIFT ] = 0x06 | SHIFT; /* percent */
+ s->pre_map[0x08 | SHIFT ] = 0x07 | SHIFT; /* ampersand */
+ s->pre_map[0x28 ] = 0x08 | SHIFT; /* apostrophe */
+ s->pre_map[0x0a | SHIFT ] = 0x09 | SHIFT; /* parenleft */
+ s->pre_map[0x0b | SHIFT ] = 0x0a | SHIFT; /* parenright */
+ s->pre_map[0x29 | SHIFT ] = 0x0b | SHIFT; /* asciitilde */
+ s->pre_map[0x03 | SHIFT ] = 0x0c | SHIFT; /* at */
+ s->pre_map[0xd3 ] = 0x0e | FN; /* Delete */
+ s->pre_map[0x3a ] = 0x0f | FN; /* Caps_Lock */
+ s->pre_map[0x07 | SHIFT ] = 0x11 | FN; /* asciicircum */
+ s->pre_map[0x0d ] = 0x12 | FN; /* equal */
+ s->pre_map[0x0d | SHIFT ] = 0x13 | FN; /* plus */
+ s->pre_map[0x1a ] = 0x14 | FN; /* bracketleft */
+ s->pre_map[0x1b ] = 0x15 | FN; /* bracketright */
+ s->pre_map[0x1a | SHIFT ] = 0x16 | FN; /* braceleft */
+ s->pre_map[0x1b | SHIFT ] = 0x17 | FN; /* braceright */
+ s->pre_map[0x27 ] = 0x22 | FN; /* semicolon */
+ s->pre_map[0x27 | SHIFT ] = 0x23 | FN; /* colon */
+ s->pre_map[0x09 | SHIFT ] = 0x24 | FN; /* asterisk */
+ s->pre_map[0x2b ] = 0x25 | FN; /* backslash */
+ s->pre_map[0x2b | SHIFT ] = 0x26 | FN; /* bar */
+ s->pre_map[0x0c | SHIFT ] = 0x30 | FN; /* underscore */
+ s->pre_map[0x33 | SHIFT ] = 0x33 | FN; /* less */
+ s->pre_map[0x35 ] = 0x33 | SHIFT; /* slash */
+ s->pre_map[0x34 | SHIFT ] = 0x34 | FN; /* greater */
+ s->pre_map[0x35 | SHIFT ] = 0x34 | SHIFT; /* question */
+ s->pre_map[0x49 ] = 0x48 | FN; /* Page_Up */
+ s->pre_map[0x51 ] = 0x50 | FN; /* Page_Down */
+
+ s->modifiers = 0;
+ s->imodifiers = 0;
+ s->fifopos = 0;
+ s->fifolen = 0;
+}
+
+#undef SHIFT
+#undef CTRL
+#undef FN
+
+static int spitz_keyboard_post_load(void *opaque, int version_id)
+{
+ SpitzKeyboardState *s = (SpitzKeyboardState *) opaque;
+
+ /* Release all pressed keys */
+ memset(s->keyrow, 0, sizeof(s->keyrow));
+ spitz_keyboard_sense_update(s);
+ s->modifiers = 0;
+ s->imodifiers = 0;
+ s->fifopos = 0;
+ s->fifolen = 0;
+
+ return 0;
+}
+
+static void spitz_keyboard_register(PXA2xxState *cpu)
+{
+ int i;
+ DeviceState *dev;
+ SpitzKeyboardState *s;
+
+ dev = sysbus_create_simple("spitz-keyboard", -1, NULL);
+ s = FROM_SYSBUS(SpitzKeyboardState, sysbus_from_qdev(dev));
+
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++)
+ qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i]));
+
+ for (i = 0; i < 5; i ++)
+ s->gpiomap[i] = qdev_get_gpio_in(cpu->gpio, spitz_gpiomap[i]);
+
+ if (!graphic_rotate)
+ s->gpiomap[4] = qemu_irq_invert(s->gpiomap[4]);
+
+ for (i = 0; i < 5; i++)
+ qemu_set_irq(s->gpiomap[i], 0);
+
+ for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++)
+ qdev_connect_gpio_out(cpu->gpio, spitz_gpio_key_strobe[i],
+ qdev_get_gpio_in(dev, i));
+
+ qemu_mod_timer(s->kbdtimer, qemu_get_clock(vm_clock));
+
+ qemu_add_kbd_event_handler(spitz_keyboard_handler, s);
+}
+
+static int spitz_keyboard_init(SysBusDevice *dev)
+{
+ SpitzKeyboardState *s;
+ int i, j;
+
+ s = FROM_SYSBUS(SpitzKeyboardState, dev);
+
+ for (i = 0; i < 0x80; i ++)
+ s->keymap[i] = -1;
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM + 1; i ++)
+ for (j = 0; j < SPITZ_KEY_STROBE_NUM; j ++)
+ if (spitz_keymap[i][j] != -1)
+ s->keymap[spitz_keymap[i][j]] = (i << 4) | j;
+
+ spitz_keyboard_pre_map(s);
+
+ s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s);
+ qdev_init_gpio_in(&dev->qdev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM);
+ qdev_init_gpio_out(&dev->qdev, s->sense, SPITZ_KEY_SENSE_NUM);
+
+ return 0;
+}
+
+/* LCD backlight controller */
+
+#define LCDTG_RESCTL 0x00
+#define LCDTG_PHACTRL 0x01
+#define LCDTG_DUTYCTRL 0x02
+#define LCDTG_POWERREG0 0x03
+#define LCDTG_POWERREG1 0x04
+#define LCDTG_GPOR3 0x05
+#define LCDTG_PICTRL 0x06
+#define LCDTG_POLCTRL 0x07
+
+typedef struct {
+ SSISlave ssidev;
+ uint32_t bl_intensity;
+ uint32_t bl_power;
+} SpitzLCDTG;
+
+static void spitz_bl_update(SpitzLCDTG *s)
+{
+ if (s->bl_power && s->bl_intensity)
+ zaurus_printf("LCD Backlight now at %i/63\n", s->bl_intensity);
+ else
+ zaurus_printf("LCD Backlight now off\n");
+}
+
+/* FIXME: Implement GPIO properly and remove this hack. */
+static SpitzLCDTG *spitz_lcdtg;
+
+static inline void spitz_bl_bit5(void *opaque, int line, int level)
+{
+ SpitzLCDTG *s = spitz_lcdtg;
+ int prev = s->bl_intensity;
+
+ if (level)
+ s->bl_intensity &= ~0x20;
+ else
+ s->bl_intensity |= 0x20;
+
+ if (s->bl_power && prev != s->bl_intensity)
+ spitz_bl_update(s);
+}
+
+static inline void spitz_bl_power(void *opaque, int line, int level)
+{
+ SpitzLCDTG *s = spitz_lcdtg;
+ s->bl_power = !!level;
+ spitz_bl_update(s);
+}
+
+static uint32_t spitz_lcdtg_transfer(SSISlave *dev, uint32_t value)
+{
+ SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev);
+ int addr;
+ addr = value >> 5;
+ value &= 0x1f;
+
+ switch (addr) {
+ case LCDTG_RESCTL:
+ if (value)
+ zaurus_printf("LCD in QVGA mode\n");
+ else
+ zaurus_printf("LCD in VGA mode\n");
+ break;
+
+ case LCDTG_DUTYCTRL:
+ s->bl_intensity &= ~0x1f;
+ s->bl_intensity |= value;
+ if (s->bl_power)
+ spitz_bl_update(s);
+ break;
+
+ case LCDTG_POWERREG0:
+ /* Set common voltage to M62332FP */
+ break;
+ }
+ return 0;
+}
+
+static int spitz_lcdtg_init(SSISlave *dev)
+{
+ SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev);
+
+ spitz_lcdtg = s;
+ s->bl_power = 0;
+ s->bl_intensity = 0x20;
+
+ return 0;
+}
+
+/* SSP devices */
+
+#define CORGI_SSP_PORT 2
+
+#define SPITZ_GPIO_LCDCON_CS 53
+#define SPITZ_GPIO_ADS7846_CS 14
+#define SPITZ_GPIO_MAX1111_CS 20
+#define SPITZ_GPIO_TP_INT 11
+
+static DeviceState *max1111;
+
+/* "Demux" the signal based on current chipselect */
+typedef struct {
+ SSISlave ssidev;
+ SSIBus *bus[3];
+ uint32_t enable[3];
+} CorgiSSPState;
+
+static uint32_t corgi_ssp_transfer(SSISlave *dev, uint32_t value)
+{
+ CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev);
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (s->enable[i]) {
+ return ssi_transfer(s->bus[i], value);
+ }
+ }
+ return 0;
+}
+
+static void corgi_ssp_gpio_cs(void *opaque, int line, int level)
+{
+ CorgiSSPState *s = (CorgiSSPState *)opaque;
+ assert(line >= 0 && line < 3);
+ s->enable[line] = !level;
+}
+
+#define MAX1111_BATT_VOLT 1
+#define MAX1111_BATT_TEMP 2
+#define MAX1111_ACIN_VOLT 3
+
+#define SPITZ_BATTERY_TEMP 0xe0 /* About 2.9V */
+#define SPITZ_BATTERY_VOLT 0xd0 /* About 4.0V */
+#define SPITZ_CHARGEON_ACIN 0x80 /* About 5.0V */
+
+static void spitz_adc_temp_on(void *opaque, int line, int level)
+{
+ if (!max1111)
+ return;
+
+ if (level)
+ max111x_set_input(max1111, MAX1111_BATT_TEMP, SPITZ_BATTERY_TEMP);
+ else
+ max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
+}
+
+static int corgi_ssp_init(SSISlave *dev)
+{
+ CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev);
+
+ qdev_init_gpio_in(&dev->qdev, corgi_ssp_gpio_cs, 3);
+ s->bus[0] = ssi_create_bus(&dev->qdev, "ssi0");
+ s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1");
+ s->bus[2] = ssi_create_bus(&dev->qdev, "ssi2");
+
+ return 0;
+}
+
+static void spitz_ssp_attach(PXA2xxState *cpu)
+{
+ DeviceState *mux;
+ DeviceState *dev;
+ void *bus;
+
+ mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp");
+
+ bus = qdev_get_child_bus(mux, "ssi0");
+ ssi_create_slave(bus, "spitz-lcdtg");
+
+ bus = qdev_get_child_bus(mux, "ssi1");
+ dev = ssi_create_slave(bus, "ads7846");
+ qdev_connect_gpio_out(dev, 0,
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT));
+
+ bus = qdev_get_child_bus(mux, "ssi2");
+ max1111 = ssi_create_slave(bus, "max1111");
+ max111x_set_input(max1111, MAX1111_BATT_VOLT, SPITZ_BATTERY_VOLT);
+ max111x_set_input(max1111, MAX1111_BATT_TEMP, 0);
+ max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN);
+
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_LCDCON_CS,
+ qdev_get_gpio_in(mux, 0));
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ADS7846_CS,
+ qdev_get_gpio_in(mux, 1));
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_MAX1111_CS,
+ qdev_get_gpio_in(mux, 2));
+}
+
+/* CF Microdrive */
+
+static void spitz_microdrive_attach(PXA2xxState *cpu, int slot)
+{
+ PCMCIACardState *md;
+ BlockDriverState *bs;
+ DriveInfo *dinfo;
+
+ dinfo = drive_get(IF_IDE, 0, 0);
+ if (!dinfo)
+ return;
+ bs = dinfo->bdrv;
+ if (bdrv_is_inserted(bs) && !bdrv_is_removable(bs)) {
+ md = dscm1xxxx_init(dinfo);
+ pxa2xx_pcmcia_attach(cpu->pcmcia[slot], md);
+ }
+}
+
+/* Wm8750 and Max7310 on I2C */
+
+#define AKITA_MAX_ADDR 0x18
+#define SPITZ_WM_ADDRL 0x1b
+#define SPITZ_WM_ADDRH 0x1a
+
+#define SPITZ_GPIO_WM 5
+
+static void spitz_wm8750_addr(void *opaque, int line, int level)
+{
+ i2c_slave *wm = (i2c_slave *) opaque;
+ if (level)
+ i2c_set_slave_address(wm, SPITZ_WM_ADDRH);
+ else
+ i2c_set_slave_address(wm, SPITZ_WM_ADDRL);
+}
+
+static void spitz_i2c_setup(PXA2xxState *cpu)
+{
+ /* Attach the CPU on one end of our I2C bus. */
+ i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
+
+ DeviceState *wm;
+
+ /* Attach a WM8750 to the bus */
+ wm = i2c_create_slave(bus, "wm8750", 0);
+
+ spitz_wm8750_addr(wm, 0, 0);
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM,
+ qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]);
+ /* .. and to the sound interface. */
+ cpu->i2s->opaque = wm;
+ cpu->i2s->codec_out = wm8750_dac_dat;
+ cpu->i2s->codec_in = wm8750_adc_dat;
+ wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s);
+}
+
+static void spitz_akita_i2c_setup(PXA2xxState *cpu)
+{
+ /* Attach a Max7310 to Akita I2C bus. */
+ i2c_create_slave(pxa2xx_i2c_bus(cpu->i2c[0]), "max7310",
+ AKITA_MAX_ADDR);
+}
+
+/* Other peripherals */
+
+static void spitz_out_switch(void *opaque, int line, int level)
+{
+ switch (line) {
+ case 0:
+ zaurus_printf("Charging %s.\n", level ? "off" : "on");
+ break;
+ case 1:
+ zaurus_printf("Discharging %s.\n", level ? "on" : "off");
+ break;
+ case 2:
+ zaurus_printf("Green LED %s.\n", level ? "on" : "off");
+ break;
+ case 3:
+ zaurus_printf("Orange LED %s.\n", level ? "on" : "off");
+ break;
+ case 4:
+ spitz_bl_bit5(opaque, line, level);
+ break;
+ case 5:
+ spitz_bl_power(opaque, line, level);
+ break;
+ case 6:
+ spitz_adc_temp_on(opaque, line, level);
+ break;
+ }
+}
+
+#define SPITZ_SCP_LED_GREEN 1
+#define SPITZ_SCP_JK_B 2
+#define SPITZ_SCP_CHRG_ON 3
+#define SPITZ_SCP_MUTE_L 4
+#define SPITZ_SCP_MUTE_R 5
+#define SPITZ_SCP_CF_POWER 6
+#define SPITZ_SCP_LED_ORANGE 7
+#define SPITZ_SCP_JK_A 8
+#define SPITZ_SCP_ADC_TEMP_ON 9
+#define SPITZ_SCP2_IR_ON 1
+#define SPITZ_SCP2_AKIN_PULLUP 2
+#define SPITZ_SCP2_BACKLIGHT_CONT 7
+#define SPITZ_SCP2_BACKLIGHT_ON 8
+#define SPITZ_SCP2_MIC_BIAS 9
+
+static void spitz_scoop_gpio_setup(PXA2xxState *cpu,
+ DeviceState *scp0, DeviceState *scp1)
+{
+ qemu_irq *outsignals = qemu_allocate_irqs(spitz_out_switch, cpu, 8);
+
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]);
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_JK_B, outsignals[1]);
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]);
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]);
+
+ if (scp1) {
+ qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]);
+ qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]);
+ }
+
+ qdev_connect_gpio_out(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]);
+}
+
+#define SPITZ_GPIO_HSYNC 22
+#define SPITZ_GPIO_SD_DETECT 9
+#define SPITZ_GPIO_SD_WP 81
+#define SPITZ_GPIO_ON_RESET 89
+#define SPITZ_GPIO_BAT_COVER 90
+#define SPITZ_GPIO_CF1_IRQ 105
+#define SPITZ_GPIO_CF1_CD 94
+#define SPITZ_GPIO_CF2_IRQ 106
+#define SPITZ_GPIO_CF2_CD 93
+
+static int spitz_hsync;
+
+static void spitz_lcd_hsync_handler(void *opaque, int line, int level)
+{
+ PXA2xxState *cpu = (PXA2xxState *) opaque;
+ qemu_set_irq(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_HSYNC), spitz_hsync);
+ spitz_hsync ^= 1;
+}
+
+static void spitz_gpio_setup(PXA2xxState *cpu, int slots)
+{
+ qemu_irq lcd_hsync;
+ /*
+ * Bad hack: We toggle the LCD hsync GPIO on every GPIO status
+ * read to satisfy broken guests that poll-wait for hsync.
+ * Simulating a real hsync event would be less practical and
+ * wouldn't guarantee that a guest ever exits the loop.
+ */
+ spitz_hsync = 0;
+ lcd_hsync = qemu_allocate_irqs(spitz_lcd_hsync_handler, cpu, 1)[0];
+ pxa2xx_gpio_read_notifier(cpu->gpio, lcd_hsync);
+ pxa2xx_lcd_vsync_notifier(cpu->lcd, lcd_hsync);
+
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(cpu->mmc,
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_WP),
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_DETECT));
+
+ /* Battery lock always closed */
+ qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER));
+
+ /* Handle reset */
+ qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset);
+
+ /* PCMCIA signals: card's IRQ and Card-Detect */
+ if (slots >= 1)
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0],
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_IRQ),
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_CD));
+ if (slots >= 2)
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1],
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_IRQ),
+ qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_CD));
+}
+
+/* Board init. */
+enum spitz_model_e { spitz, akita, borzoi, terrier };
+
+#define SPITZ_RAM 0x04000000
+#define SPITZ_ROM 0x00800000
+
+static struct arm_boot_info spitz_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = 0x04000000,
+};
+
+static void spitz_common_init(ram_addr_t ram_size,
+ const char *kernel_filename,
+ const char *kernel_cmdline, const char *initrd_filename,
+ const char *cpu_model, enum spitz_model_e model, int arm_id)
+{
+ PXA2xxState *cpu;
+ DeviceState *scp0, *scp1 = NULL;
+
+ if (!cpu_model)
+ cpu_model = (model == terrier) ? "pxa270-c5" : "pxa270-c0";
+
+ /* Setup CPU & memory */
+ cpu = pxa270_init(spitz_binfo.ram_size, cpu_model);
+
+ sl_flash_register(cpu, (model == spitz) ? FLASH_128M : FLASH_1024M);
+
+ cpu_register_physical_memory(0, SPITZ_ROM,
+ qemu_ram_alloc(NULL, "spitz.rom", SPITZ_ROM) | IO_MEM_ROM);
+
+ /* Setup peripherals */
+ spitz_keyboard_register(cpu);
+
+ spitz_ssp_attach(cpu);
+
+ scp0 = sysbus_create_simple("scoop", 0x10800000, NULL);
+ if (model != akita) {
+ scp1 = sysbus_create_simple("scoop", 0x08800040, NULL);
+ }
+
+ spitz_scoop_gpio_setup(cpu, scp0, scp1);
+
+ spitz_gpio_setup(cpu, (model == akita) ? 1 : 2);
+
+ spitz_i2c_setup(cpu);
+
+ if (model == akita)
+ spitz_akita_i2c_setup(cpu);
+
+ if (model == terrier)
+ /* A 6.0 GB microdrive is permanently sitting in CF slot 1. */
+ spitz_microdrive_attach(cpu, 1);
+ else if (model != akita)
+ /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */
+ spitz_microdrive_attach(cpu, 0);
+
+ spitz_binfo.kernel_filename = kernel_filename;
+ spitz_binfo.kernel_cmdline = kernel_cmdline;
+ spitz_binfo.initrd_filename = initrd_filename;
+ spitz_binfo.board_id = arm_id;
+ arm_load_kernel(cpu->env, &spitz_binfo);
+ sl_bootparam_write(SL_PXA_PARAM_BASE);
+}
+
+static void spitz_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ spitz_common_init(ram_size, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, spitz, 0x2c9);
+}
+
+static void borzoi_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ spitz_common_init(ram_size, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, borzoi, 0x33f);
+}
+
+static void akita_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ spitz_common_init(ram_size, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, akita, 0x2e8);
+}
+
+static void terrier_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ spitz_common_init(ram_size, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, terrier, 0x33f);
+}
+
+static QEMUMachine akitapda_machine = {
+ .name = "akita",
+ .desc = "Akita PDA (PXA270)",
+ .init = akita_init,
+};
+
+static QEMUMachine spitzpda_machine = {
+ .name = "spitz",
+ .desc = "Spitz PDA (PXA270)",
+ .init = spitz_init,
+};
+
+static QEMUMachine borzoipda_machine = {
+ .name = "borzoi",
+ .desc = "Borzoi PDA (PXA270)",
+ .init = borzoi_init,
+};
+
+static QEMUMachine terrierpda_machine = {
+ .name = "terrier",
+ .desc = "Terrier PDA (PXA270)",
+ .init = terrier_init,
+};
+
+static void spitz_machine_init(void)
+{
+ qemu_register_machine(&akitapda_machine);
+ qemu_register_machine(&spitzpda_machine);
+ qemu_register_machine(&borzoipda_machine);
+ qemu_register_machine(&terrierpda_machine);
+}
+
+machine_init(spitz_machine_init);
+
+static bool is_version_0(void *opaque, int version_id)
+{
+ return version_id == 0;
+}
+
+static VMStateDescription vmstate_sl_nand_info = {
+ .name = "sl-nand",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(ctl, SLNANDState),
+ VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo sl_nand_info = {
+ .init = sl_nand_init,
+ .qdev.name = "sl-nand",
+ .qdev.size = sizeof(SLNANDState),
+ .qdev.vmsd = &vmstate_sl_nand_info,
+ .qdev.props = (Property []) {
+ DEFINE_PROP_UINT8("manf_id", SLNANDState, manf_id, NAND_MFR_SAMSUNG),
+ DEFINE_PROP_UINT8("chip_id", SLNANDState, chip_id, 0xf1),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static VMStateDescription vmstate_spitz_kbd = {
+ .name = "spitz-keyboard",
+ .version_id = 1,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = spitz_keyboard_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(sense_state, SpitzKeyboardState),
+ VMSTATE_UINT16(strobe_state, SpitzKeyboardState),
+ VMSTATE_UNUSED_TEST(is_version_0, 5),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo spitz_keyboard_info = {
+ .init = spitz_keyboard_init,
+ .qdev.name = "spitz-keyboard",
+ .qdev.size = sizeof(SpitzKeyboardState),
+ .qdev.vmsd = &vmstate_spitz_kbd,
+ .qdev.props = (Property []) {
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static const VMStateDescription vmstate_corgi_ssp_regs = {
+ .name = "corgi-ssp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static SSISlaveInfo corgi_ssp_info = {
+ .qdev.name = "corgi-ssp",
+ .qdev.size = sizeof(CorgiSSPState),
+ .qdev.vmsd = &vmstate_corgi_ssp_regs,
+ .init = corgi_ssp_init,
+ .transfer = corgi_ssp_transfer
+};
+
+static const VMStateDescription vmstate_spitz_lcdtg_regs = {
+ .name = "spitz-lcdtg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(bl_intensity, SpitzLCDTG),
+ VMSTATE_UINT32(bl_power, SpitzLCDTG),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static SSISlaveInfo spitz_lcdtg_info = {
+ .qdev.name = "spitz-lcdtg",
+ .qdev.size = sizeof(SpitzLCDTG),
+ .qdev.vmsd = &vmstate_spitz_lcdtg_regs,
+ .init = spitz_lcdtg_init,
+ .transfer = spitz_lcdtg_transfer
+};
+
+static void spitz_register_devices(void)
+{
+ ssi_register_slave(&corgi_ssp_info);
+ ssi_register_slave(&spitz_lcdtg_info);
+ sysbus_register_withprop(&spitz_keyboard_info);
+ sysbus_register_withprop(&sl_nand_info);
+}
+
+device_init(spitz_register_devices)
diff --git a/hw/ssd0303.c b/hw/ssd0303.c
new file mode 100644
index 0000000..108c068
--- /dev/null
+++ b/hw/ssd0303.c
@@ -0,0 +1,312 @@
+/*
+ * SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+ implement one. Most of the commends relating to brightness and geometry
+ setup are ignored. */
+#include "i2c.h"
+#include "console.h"
+
+//#define DEBUG_SSD0303 1
+
+#ifdef DEBUG_SSD0303
+#define DPRINTF(fmt, ...) \
+do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Scaling factor for pixels. */
+#define MAGNIFY 4
+
+enum ssd0303_mode
+{
+ SSD0303_IDLE,
+ SSD0303_DATA,
+ SSD0303_CMD
+};
+
+enum ssd0303_cmd {
+ SSD0303_CMD_NONE,
+ SSD0303_CMD_SKIP1
+};
+
+typedef struct {
+ i2c_slave i2c;
+ DisplayState *ds;
+ int row;
+ int col;
+ int start_line;
+ int mirror;
+ int flash;
+ int enabled;
+ int inverse;
+ int redraw;
+ enum ssd0303_mode mode;
+ enum ssd0303_cmd cmd_state;
+ uint8_t framebuffer[132*8];
+} ssd0303_state;
+
+static int ssd0303_recv(i2c_slave *i2c)
+{
+ BADF("Reads not implemented\n");
+ return -1;
+}
+
+static int ssd0303_send(i2c_slave *i2c, uint8_t data)
+{
+ ssd0303_state *s = (ssd0303_state *)i2c;
+ enum ssd0303_cmd old_cmd_state;
+ switch (s->mode) {
+ case SSD0303_IDLE:
+ DPRINTF("byte 0x%02x\n", data);
+ if (data == 0x80)
+ s->mode = SSD0303_CMD;
+ else if (data == 0x40)
+ s->mode = SSD0303_DATA;
+ else
+ BADF("Unexpected byte 0x%x\n", data);
+ break;
+ case SSD0303_DATA:
+ DPRINTF("data 0x%02x\n", data);
+ if (s->col < 132) {
+ s->framebuffer[s->col + s->row * 132] = data;
+ s->col++;
+ s->redraw = 1;
+ }
+ break;
+ case SSD0303_CMD:
+ old_cmd_state = s->cmd_state;
+ s->cmd_state = SSD0303_CMD_NONE;
+ switch (old_cmd_state) {
+ case SSD0303_CMD_NONE:
+ DPRINTF("cmd 0x%02x\n", data);
+ s->mode = SSD0303_IDLE;
+ switch (data) {
+ case 0x00 ... 0x0f: /* Set lower colum address. */
+ s->col = (s->col & 0xf0) | (data & 0xf);
+ break;
+ case 0x10 ... 0x20: /* Set higher column address. */
+ s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
+ break;
+ case 0x40 ... 0x7f: /* Set start line. */
+ s->start_line = 0;
+ break;
+ case 0x81: /* Set contrast (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xa0: /* Mirror off. */
+ s->mirror = 0;
+ break;
+ case 0xa1: /* Mirror off. */
+ s->mirror = 1;
+ break;
+ case 0xa4: /* Entire display off. */
+ s->flash = 0;
+ break;
+ case 0xa5: /* Entire display on. */
+ s->flash = 1;
+ break;
+ case 0xa6: /* Inverse off. */
+ s->inverse = 0;
+ break;
+ case 0xa7: /* Inverse on. */
+ s->inverse = 1;
+ break;
+ case 0xa8: /* Set multipled ratio (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xad: /* DC-DC power control. */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xae: /* Display off. */
+ s->enabled = 0;
+ break;
+ case 0xaf: /* Display on. */
+ s->enabled = 1;
+ break;
+ case 0xb0 ... 0xbf: /* Set Page address. */
+ s->row = data & 7;
+ break;
+ case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */
+ break;
+ case 0xd3: /* Set display offset (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd5: /* Set display clock (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd8: /* Set color and power mode (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd9: /* Set pre-charge period (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xda: /* Set COM pin configuration (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xdb: /* Set VCOM dselect level (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xe3: /* no-op. */
+ break;
+ default:
+ BADF("Unknown command: 0x%x\n", data);
+ }
+ break;
+ case SSD0303_CMD_SKIP1:
+ DPRINTF("skip 0x%02x\n", data);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
+{
+ ssd0303_state *s = (ssd0303_state *)i2c;
+ switch (event) {
+ case I2C_FINISH:
+ s->mode = SSD0303_IDLE;
+ break;
+ case I2C_START_RECV:
+ case I2C_START_SEND:
+ case I2C_NACK:
+ /* Nothing to do. */
+ break;
+ }
+}
+
+static void ssd0303_update_display(void *opaque)
+{
+ ssd0303_state *s = (ssd0303_state *)opaque;
+ uint8_t *dest;
+ uint8_t *src;
+ int x;
+ int y;
+ int line;
+ char *colors[2];
+ char colortab[MAGNIFY * 8];
+ int dest_width;
+ uint8_t mask;
+
+ if (!s->redraw)
+ return;
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ return;
+ case 15:
+ dest_width = 2;
+ break;
+ case 16:
+ dest_width = 2;
+ break;
+ case 24:
+ dest_width = 3;
+ break;
+ case 32:
+ dest_width = 4;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ dest_width *= MAGNIFY;
+ memset(colortab, 0xff, dest_width);
+ memset(colortab + dest_width, 0, dest_width);
+ if (s->flash) {
+ colors[0] = colortab;
+ colors[1] = colortab;
+ } else if (s->inverse) {
+ colors[0] = colortab;
+ colors[1] = colortab + dest_width;
+ } else {
+ colors[0] = colortab + dest_width;
+ colors[1] = colortab;
+ }
+ dest = ds_get_data(s->ds);
+ for (y = 0; y < 16; y++) {
+ line = (y + s->start_line) & 63;
+ src = s->framebuffer + 132 * (line >> 3) + 36;
+ mask = 1 << (line & 7);
+ for (x = 0; x < 96; x++) {
+ memcpy(dest, colors[(*src & mask) != 0], dest_width);
+ dest += dest_width;
+ src++;
+ }
+ for (x = 1; x < MAGNIFY; x++) {
+ memcpy(dest, dest - dest_width * 96, dest_width * 96);
+ dest += dest_width * 96;
+ }
+ }
+ s->redraw = 0;
+ dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+}
+
+static void ssd0303_invalidate_display(void * opaque)
+{
+ ssd0303_state *s = (ssd0303_state *)opaque;
+ s->redraw = 1;
+}
+
+static const VMStateDescription vmstate_ssd0303 = {
+ .name = "ssd0303_oled",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(row, ssd0303_state),
+ VMSTATE_INT32(col, ssd0303_state),
+ VMSTATE_INT32(start_line, ssd0303_state),
+ VMSTATE_INT32(mirror, ssd0303_state),
+ VMSTATE_INT32(flash, ssd0303_state),
+ VMSTATE_INT32(enabled, ssd0303_state),
+ VMSTATE_INT32(inverse, ssd0303_state),
+ VMSTATE_INT32(redraw, ssd0303_state),
+ VMSTATE_UINT32(mode, ssd0303_state),
+ VMSTATE_UINT32(cmd_state, ssd0303_state),
+ VMSTATE_BUFFER(framebuffer, ssd0303_state),
+ VMSTATE_I2C_SLAVE(i2c, ssd0303_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ssd0303_init(i2c_slave *i2c)
+{
+ ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
+
+ s->ds = graphic_console_init(ssd0303_update_display,
+ ssd0303_invalidate_display,
+ NULL, NULL, s);
+ qemu_console_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
+ return 0;
+}
+
+static I2CSlaveInfo ssd0303_info = {
+ .qdev.name = "ssd0303",
+ .qdev.size = sizeof(ssd0303_state),
+ .qdev.vmsd = &vmstate_ssd0303,
+ .init = ssd0303_init,
+ .event = ssd0303_event,
+ .recv = ssd0303_recv,
+ .send = ssd0303_send
+};
+
+static void ssd0303_register_devices(void)
+{
+ i2c_register_slave(&ssd0303_info);
+}
+
+device_init(ssd0303_register_devices)
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
new file mode 100644
index 0000000..8643961
--- /dev/null
+++ b/hw/ssd0323.c
@@ -0,0 +1,355 @@
+/*
+ * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+ implement one. Most of the commends relating to brightness and geometry
+ setup are ignored. */
+#include "ssi.h"
+#include "console.h"
+
+//#define DEBUG_SSD0323 1
+
+#ifdef DEBUG_SSD0323
+#define DPRINTF(fmt, ...) \
+do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Scaling factor for pixels. */
+#define MAGNIFY 4
+
+#define REMAP_SWAP_COLUMN 0x01
+#define REMAP_SWAP_NYBBLE 0x02
+#define REMAP_VERTICAL 0x04
+#define REMAP_SWAP_COM 0x10
+#define REMAP_SPLIT_COM 0x40
+
+enum ssd0323_mode
+{
+ SSD0323_CMD,
+ SSD0323_DATA
+};
+
+typedef struct {
+ SSISlave ssidev;
+ DisplayState *ds;
+
+ int cmd_len;
+ int cmd;
+ int cmd_data[8];
+ int row;
+ int row_start;
+ int row_end;
+ int col;
+ int col_start;
+ int col_end;
+ int redraw;
+ int remap;
+ enum ssd0323_mode mode;
+ uint8_t framebuffer[128 * 80 / 2];
+} ssd0323_state;
+
+static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
+{
+ ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
+
+ switch (s->mode) {
+ case SSD0323_DATA:
+ DPRINTF("data 0x%02x\n", data);
+ s->framebuffer[s->col + s->row * 64] = data;
+ if (s->remap & REMAP_VERTICAL) {
+ s->row++;
+ if (s->row > s->row_end) {
+ s->row = s->row_start;
+ s->col++;
+ }
+ if (s->col > s->col_end) {
+ s->col = s->col_start;
+ }
+ } else {
+ s->col++;
+ if (s->col > s->col_end) {
+ s->row++;
+ s->col = s->col_start;
+ }
+ if (s->row > s->row_end) {
+ s->row = s->row_start;
+ }
+ }
+ s->redraw = 1;
+ break;
+ case SSD0323_CMD:
+ DPRINTF("cmd 0x%02x\n", data);
+ if (s->cmd_len == 0) {
+ s->cmd = data;
+ } else {
+ s->cmd_data[s->cmd_len - 1] = data;
+ }
+ s->cmd_len++;
+ switch (s->cmd) {
+#define DATA(x) if (s->cmd_len <= (x)) return 0
+ case 0x15: /* Set column. */
+ DATA(2);
+ s->col = s->col_start = s->cmd_data[0] % 64;
+ s->col_end = s->cmd_data[1] % 64;
+ break;
+ case 0x75: /* Set row. */
+ DATA(2);
+ s->row = s->row_start = s->cmd_data[0] % 80;
+ s->row_end = s->cmd_data[1] % 80;
+ break;
+ case 0x81: /* Set contrast */
+ DATA(1);
+ break;
+ case 0x84: case 0x85: case 0x86: /* Max current. */
+ DATA(0);
+ break;
+ case 0xa0: /* Set remapping. */
+ /* FIXME: Implement this. */
+ DATA(1);
+ s->remap = s->cmd_data[0];
+ break;
+ case 0xa1: /* Set display start line. */
+ case 0xa2: /* Set display offset. */
+ /* FIXME: Implement these. */
+ DATA(1);
+ break;
+ case 0xa4: /* Normal mode. */
+ case 0xa5: /* All on. */
+ case 0xa6: /* All off. */
+ case 0xa7: /* Inverse. */
+ /* FIXME: Implement these. */
+ DATA(0);
+ break;
+ case 0xa8: /* Set multiplex ratio. */
+ case 0xad: /* Set DC-DC converter. */
+ DATA(1);
+ /* Ignored. Don't care. */
+ break;
+ case 0xae: /* Display off. */
+ case 0xaf: /* Display on. */
+ DATA(0);
+ /* TODO: Implement power control. */
+ break;
+ case 0xb1: /* Set phase length. */
+ case 0xb2: /* Set row period. */
+ case 0xb3: /* Set clock rate. */
+ case 0xbc: /* Set precharge. */
+ case 0xbe: /* Set VCOMH. */
+ case 0xbf: /* Set segment low. */
+ DATA(1);
+ /* Ignored. Don't care. */
+ break;
+ case 0xb8: /* Set grey scale table. */
+ /* FIXME: Implement this. */
+ DATA(8);
+ break;
+ case 0xe3: /* NOP. */
+ DATA(0);
+ break;
+ case 0xff: /* Nasty hack because we don't handle chip selects
+ properly. */
+ break;
+ default:
+ BADF("Unknown command: 0x%x\n", data);
+ }
+ s->cmd_len = 0;
+ return 0;
+ }
+ return 0;
+}
+
+static void ssd0323_update_display(void *opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ uint8_t *dest;
+ uint8_t *src;
+ int x;
+ int y;
+ int i;
+ int line;
+ char *colors[16];
+ char colortab[MAGNIFY * 64];
+ char *p;
+ int dest_width;
+
+ if (!s->redraw)
+ return;
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ return;
+ case 15:
+ dest_width = 2;
+ break;
+ case 16:
+ dest_width = 2;
+ break;
+ case 24:
+ dest_width = 3;
+ break;
+ case 32:
+ dest_width = 4;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ p = colortab;
+ for (i = 0; i < 16; i++) {
+ int n;
+ colors[i] = p;
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 15:
+ n = i * 2 + (i >> 3);
+ p[0] = n | (n << 5);
+ p[1] = (n << 2) | (n >> 3);
+ break;
+ case 16:
+ n = i * 2 + (i >> 3);
+ p[0] = n | (n << 6) | ((n << 1) & 0x20);
+ p[1] = (n << 3) | (n >> 2);
+ break;
+ case 24:
+ case 32:
+ n = (i << 4) | i;
+ p[0] = p[1] = p[2] = n;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ p += dest_width;
+ }
+ /* TODO: Implement row/column remapping. */
+ dest = ds_get_data(s->ds);
+ for (y = 0; y < 64; y++) {
+ line = y;
+ src = s->framebuffer + 64 * line;
+ for (x = 0; x < 64; x++) {
+ int val;
+ val = *src >> 4;
+ for (i = 0; i < MAGNIFY; i++) {
+ memcpy(dest, colors[val], dest_width);
+ dest += dest_width;
+ }
+ val = *src & 0xf;
+ for (i = 0; i < MAGNIFY; i++) {
+ memcpy(dest, colors[val], dest_width);
+ dest += dest_width;
+ }
+ src++;
+ }
+ for (i = 1; i < MAGNIFY; i++) {
+ memcpy(dest, dest - dest_width * MAGNIFY * 128,
+ dest_width * 128 * MAGNIFY);
+ dest += dest_width * 128 * MAGNIFY;
+ }
+ }
+ s->redraw = 0;
+ dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+}
+
+static void ssd0323_invalidate_display(void * opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ s->redraw = 1;
+}
+
+/* Command/data input. */
+static void ssd0323_cd(void *opaque, int n, int level)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ DPRINTF("%s mode\n", level ? "Data" : "Command");
+ s->mode = level ? SSD0323_DATA : SSD0323_CMD;
+}
+
+static void ssd0323_save(QEMUFile *f, void *opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->cmd_len);
+ qemu_put_be32(f, s->cmd);
+ for (i = 0; i < 8; i++)
+ qemu_put_be32(f, s->cmd_data[i]);
+ qemu_put_be32(f, s->row);
+ qemu_put_be32(f, s->row_start);
+ qemu_put_be32(f, s->row_end);
+ qemu_put_be32(f, s->col);
+ qemu_put_be32(f, s->col_start);
+ qemu_put_be32(f, s->col_end);
+ qemu_put_be32(f, s->redraw);
+ qemu_put_be32(f, s->remap);
+ qemu_put_be32(f, s->mode);
+ qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+}
+
+static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->cmd_len = qemu_get_be32(f);
+ s->cmd = qemu_get_be32(f);
+ for (i = 0; i < 8; i++)
+ s->cmd_data[i] = qemu_get_be32(f);
+ s->row = qemu_get_be32(f);
+ s->row_start = qemu_get_be32(f);
+ s->row_end = qemu_get_be32(f);
+ s->col = qemu_get_be32(f);
+ s->col_start = qemu_get_be32(f);
+ s->col_end = qemu_get_be32(f);
+ s->redraw = qemu_get_be32(f);
+ s->remap = qemu_get_be32(f);
+ s->mode = qemu_get_be32(f);
+ qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+ return 0;
+}
+
+static int ssd0323_init(SSISlave *dev)
+{
+ ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
+
+ s->col_end = 63;
+ s->row_end = 79;
+ s->ds = graphic_console_init(ssd0323_update_display,
+ ssd0323_invalidate_display,
+ NULL, NULL, s);
+ qemu_console_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
+
+ qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
+
+ register_savevm(&dev->qdev, "ssd0323_oled", -1, 1,
+ ssd0323_save, ssd0323_load, s);
+ return 0;
+}
+
+static SSISlaveInfo ssd0323_info = {
+ .qdev.name = "ssd0323",
+ .qdev.size = sizeof(ssd0323_state),
+ .init = ssd0323_init,
+ .transfer = ssd0323_transfer
+};
+
+static void ssd03232_register_devices(void)
+{
+ ssi_register_slave(&ssd0323_info);
+}
+
+device_init(ssd03232_register_devices)
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
new file mode 100644
index 0000000..fb4b649
--- /dev/null
+++ b/hw/ssi-sd.c
@@ -0,0 +1,256 @@
+/*
+ * SSI to SD card adapter.
+ *
+ * Copyright (c) 2007-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+
+#include "blockdev.h"
+#include "ssi.h"
+#include "sd.h"
+
+//#define DEBUG_SSI_SD 1
+
+#ifdef DEBUG_SSI_SD
+#define DPRINTF(fmt, ...) \
+do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+typedef enum {
+ SSI_SD_CMD,
+ SSI_SD_CMDARG,
+ SSI_SD_RESPONSE,
+ SSI_SD_DATA_START,
+ SSI_SD_DATA_READ,
+} ssi_sd_mode;
+
+typedef struct {
+ SSISlave ssidev;
+ ssi_sd_mode mode;
+ int cmd;
+ uint8_t cmdarg[4];
+ uint8_t response[5];
+ int arglen;
+ int response_pos;
+ int stopping;
+ SDState *sd;
+} ssi_sd_state;
+
+/* State word bits. */
+#define SSI_SDR_LOCKED 0x0001
+#define SSI_SDR_WP_ERASE 0x0002
+#define SSI_SDR_ERROR 0x0004
+#define SSI_SDR_CC_ERROR 0x0008
+#define SSI_SDR_ECC_FAILED 0x0010
+#define SSI_SDR_WP_VIOLATION 0x0020
+#define SSI_SDR_ERASE_PARAM 0x0040
+#define SSI_SDR_OUT_OF_RANGE 0x0080
+#define SSI_SDR_IDLE 0x0100
+#define SSI_SDR_ERASE_RESET 0x0200
+#define SSI_SDR_ILLEGAL_COMMAND 0x0400
+#define SSI_SDR_COM_CRC_ERROR 0x0800
+#define SSI_SDR_ERASE_SEQ_ERROR 0x1000
+#define SSI_SDR_ADDRESS_ERROR 0x2000
+#define SSI_SDR_PARAMETER_ERROR 0x4000
+
+static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
+{
+ ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
+
+ /* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */
+ if (s->mode == SSI_SD_DATA_READ && val == 0x4d) {
+ s->mode = SSI_SD_CMD;
+ /* There must be at least one byte delay before the card responds. */
+ s->stopping = 1;
+ }
+
+ switch (s->mode) {
+ case SSI_SD_CMD:
+ if (val == 0xff) {
+ DPRINTF("NULL command\n");
+ return 0xff;
+ }
+ s->cmd = val & 0x3f;
+ s->mode = SSI_SD_CMDARG;
+ s->arglen = 0;
+ return 0xff;
+ case SSI_SD_CMDARG:
+ if (s->arglen == 4) {
+ SDRequest request;
+ uint8_t longresp[16];
+ /* FIXME: Check CRC. */
+ request.cmd = s->cmd;
+ request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16)
+ | (s->cmdarg[2] << 8) | s->cmdarg[3];
+ DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg);
+ s->arglen = sd_do_command(s->sd, &request, longresp);
+ if (s->arglen <= 0) {
+ s->arglen = 1;
+ s->response[0] = 4;
+ DPRINTF("SD command failed\n");
+ } else if (s->cmd == 58) {
+ /* CMD58 returns R3 response (OCR) */
+ DPRINTF("Returned OCR\n");
+ s->arglen = 5;
+ s->response[0] = 1;
+ memcpy(&s->response[1], longresp, 4);
+ } else if (s->arglen != 4) {
+ BADF("Unexpected response to cmd %d\n", s->cmd);
+ /* Illegal command is about as near as we can get. */
+ s->arglen = 1;
+ s->response[0] = 4;
+ } else {
+ /* All other commands return status. */
+ uint32_t cardstatus;
+ uint16_t status;
+ /* CMD13 returns a 2-byte statuse work. Other commands
+ only return the first byte. */
+ s->arglen = (s->cmd == 13) ? 2 : 1;
+ cardstatus = (longresp[0] << 24) | (longresp[1] << 16)
+ | (longresp[2] << 8) | longresp[3];
+ status = 0;
+ if (((cardstatus >> 9) & 0xf) < 4)
+ status |= SSI_SDR_IDLE;
+ if (cardstatus & ERASE_RESET)
+ status |= SSI_SDR_ERASE_RESET;
+ if (cardstatus & ILLEGAL_COMMAND)
+ status |= SSI_SDR_ILLEGAL_COMMAND;
+ if (cardstatus & COM_CRC_ERROR)
+ status |= SSI_SDR_COM_CRC_ERROR;
+ if (cardstatus & ERASE_SEQ_ERROR)
+ status |= SSI_SDR_ERASE_SEQ_ERROR;
+ if (cardstatus & ADDRESS_ERROR)
+ status |= SSI_SDR_ADDRESS_ERROR;
+ if (cardstatus & CARD_IS_LOCKED)
+ status |= SSI_SDR_LOCKED;
+ if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP))
+ status |= SSI_SDR_WP_ERASE;
+ if (cardstatus & SD_ERROR)
+ status |= SSI_SDR_ERROR;
+ if (cardstatus & CC_ERROR)
+ status |= SSI_SDR_CC_ERROR;
+ if (cardstatus & CARD_ECC_FAILED)
+ status |= SSI_SDR_ECC_FAILED;
+ if (cardstatus & WP_VIOLATION)
+ status |= SSI_SDR_WP_VIOLATION;
+ if (cardstatus & ERASE_PARAM)
+ status |= SSI_SDR_ERASE_PARAM;
+ if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE))
+ status |= SSI_SDR_OUT_OF_RANGE;
+ /* ??? Don't know what Parameter Error really means, so
+ assume it's set if the second byte is nonzero. */
+ if (status & 0xff)
+ status |= SSI_SDR_PARAMETER_ERROR;
+ s->response[0] = status >> 8;
+ s->response[1] = status;
+ DPRINTF("Card status 0x%02x\n", status);
+ }
+ s->mode = SSI_SD_RESPONSE;
+ s->response_pos = 0;
+ } else {
+ s->cmdarg[s->arglen++] = val;
+ }
+ return 0xff;
+ case SSI_SD_RESPONSE:
+ if (s->stopping) {
+ s->stopping = 0;
+ return 0xff;
+ }
+ if (s->response_pos < s->arglen) {
+ DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
+ return s->response[s->response_pos++];
+ }
+ if (sd_data_ready(s->sd)) {
+ DPRINTF("Data read\n");
+ s->mode = SSI_SD_DATA_START;
+ } else {
+ DPRINTF("End of command\n");
+ s->mode = SSI_SD_CMD;
+ }
+ return 0xff;
+ case SSI_SD_DATA_START:
+ DPRINTF("Start read block\n");
+ s->mode = SSI_SD_DATA_READ;
+ return 0xfe;
+ case SSI_SD_DATA_READ:
+ val = sd_read_data(s->sd);
+ if (!sd_data_ready(s->sd)) {
+ DPRINTF("Data read end\n");
+ s->mode = SSI_SD_CMD;
+ }
+ return val;
+ }
+ /* Should never happen. */
+ return 0xff;
+}
+
+static void ssi_sd_save(QEMUFile *f, void *opaque)
+{
+ ssi_sd_state *s = (ssi_sd_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->mode);
+ qemu_put_be32(f, s->cmd);
+ for (i = 0; i < 4; i++)
+ qemu_put_be32(f, s->cmdarg[i]);
+ for (i = 0; i < 5; i++)
+ qemu_put_be32(f, s->response[i]);
+ qemu_put_be32(f, s->arglen);
+ qemu_put_be32(f, s->response_pos);
+ qemu_put_be32(f, s->stopping);
+}
+
+static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
+{
+ ssi_sd_state *s = (ssi_sd_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->mode = qemu_get_be32(f);
+ s->cmd = qemu_get_be32(f);
+ for (i = 0; i < 4; i++)
+ s->cmdarg[i] = qemu_get_be32(f);
+ for (i = 0; i < 5; i++)
+ s->response[i] = qemu_get_be32(f);
+ s->arglen = qemu_get_be32(f);
+ s->response_pos = qemu_get_be32(f);
+ s->stopping = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int ssi_sd_init(SSISlave *dev)
+{
+ ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
+ DriveInfo *dinfo;
+
+ s->mode = SSI_SD_CMD;
+ dinfo = drive_get_next(IF_SD);
+ s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 1);
+ register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
+ return 0;
+}
+
+static SSISlaveInfo ssi_sd_info = {
+ .qdev.name = "ssi-sd",
+ .qdev.size = sizeof(ssi_sd_state),
+ .init = ssi_sd_init,
+ .transfer = ssi_sd_transfer
+};
+
+static void ssi_sd_register_devices(void)
+{
+ ssi_register_slave(&ssi_sd_info);
+}
+
+device_init(ssi_sd_register_devices)
diff --git a/hw/ssi.c b/hw/ssi.c
new file mode 100644
index 0000000..cfe7c07
--- /dev/null
+++ b/hw/ssi.c
@@ -0,0 +1,70 @@
+/*
+ * QEMU Synchronous Serial Interface support
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+
+#include "ssi.h"
+
+struct SSIBus {
+ BusState qbus;
+};
+
+static struct BusInfo ssi_bus_info = {
+ .name = "SSI",
+ .size = sizeof(SSIBus),
+};
+
+static int ssi_slave_init(DeviceState *dev, DeviceInfo *base_info)
+{
+ SSISlaveInfo *info = container_of(base_info, SSISlaveInfo, qdev);
+ SSISlave *s = SSI_SLAVE_FROM_QDEV(dev);
+ SSIBus *bus;
+
+ bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
+ if (QLIST_FIRST(&bus->qbus.children) != dev
+ || QLIST_NEXT(dev, sibling) != NULL) {
+ hw_error("Too many devices on SSI bus");
+ }
+
+ s->info = info;
+ return info->init(s);
+}
+
+void ssi_register_slave(SSISlaveInfo *info)
+{
+ assert(info->qdev.size >= sizeof(SSISlave));
+ info->qdev.init = ssi_slave_init;
+ info->qdev.bus_info = &ssi_bus_info;
+ qdev_register(&info->qdev);
+}
+
+DeviceState *ssi_create_slave(SSIBus *bus, const char *name)
+{
+ DeviceState *dev;
+ dev = qdev_create(&bus->qbus, name);
+ qdev_init_nofail(dev);
+ return dev;
+}
+
+SSIBus *ssi_create_bus(DeviceState *parent, const char *name)
+{
+ BusState *bus;
+ bus = qbus_create(&ssi_bus_info, parent, name);
+ return FROM_QBUS(SSIBus, bus);
+}
+
+uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
+{
+ DeviceState *dev;
+ SSISlave *slave;
+ dev = QLIST_FIRST(&bus->qbus.children);
+ if (!dev) {
+ return 0;
+ }
+ slave = SSI_SLAVE_FROM_QDEV(dev);
+ return slave->info->transfer(slave, val);
+}
diff --git a/hw/ssi.h b/hw/ssi.h
new file mode 100644
index 0000000..24610a8
--- /dev/null
+++ b/hw/ssi.h
@@ -0,0 +1,45 @@
+/* QEMU Synchronous Serial Interface support. */
+
+/* In principle SSI is a point-point interface. As such the qemu
+ implementation has a single slave device on a "bus".
+ However it is fairly common for boards to have multiple slaves
+ connected to a single master, and select devices with an external
+ chip select. This is implemented in qemu by having an explicit mux device.
+ It is assumed that master and slave are both using the same transfer width.
+ */
+
+#ifndef QEMU_SSI_H
+#define QEMU_SSI_H
+
+#include "qdev.h"
+
+typedef struct SSISlave SSISlave;
+
+/* Slave devices. */
+typedef struct {
+ DeviceInfo qdev;
+ int (*init)(SSISlave *dev);
+ uint32_t (*transfer)(SSISlave *dev, uint32_t val);
+} SSISlaveInfo;
+
+struct SSISlave {
+ DeviceState qdev;
+ SSISlaveInfo *info;
+};
+
+#define SSI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SSISlave, qdev, dev)
+#define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev)
+
+void ssi_register_slave(SSISlaveInfo *info);
+
+DeviceState *ssi_create_slave(SSIBus *bus, const char *name);
+
+/* Master interface. */
+SSIBus *ssi_create_bus(DeviceState *parent, const char *name);
+
+uint32_t ssi_transfer(SSIBus *bus, uint32_t val);
+
+/* max111x.c */
+void max111x_set_input(DeviceState *dev, int line, uint8_t value);
+
+#endif
diff --git a/hw/stellaris.c b/hw/stellaris.c
new file mode 100644
index 0000000..b903273
--- /dev/null
+++ b/hw/stellaris.c
@@ -0,0 +1,1474 @@
+/*
+ * Luminary Micro Stellaris peripherals
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "ssi.h"
+#include "arm-misc.h"
+#include "devices.h"
+#include "qemu-timer.h"
+#include "i2c.h"
+#include "net.h"
+#include "sysemu.h"
+#include "boards.h"
+
+#define GPIO_A 0
+#define GPIO_B 1
+#define GPIO_C 2
+#define GPIO_D 3
+#define GPIO_E 4
+#define GPIO_F 5
+#define GPIO_G 6
+
+#define BP_OLED_I2C 0x01
+#define BP_OLED_SSI 0x02
+#define BP_GAMEPAD 0x04
+
+typedef const struct {
+ const char *name;
+ uint32_t did0;
+ uint32_t did1;
+ uint32_t dc0;
+ uint32_t dc1;
+ uint32_t dc2;
+ uint32_t dc3;
+ uint32_t dc4;
+ uint32_t peripherals;
+} stellaris_board_info;
+
+/* General purpose timer module. */
+
+typedef struct gptm_state {
+ SysBusDevice busdev;
+ uint32_t config;
+ uint32_t mode[2];
+ uint32_t control;
+ uint32_t state;
+ uint32_t mask;
+ uint32_t load[2];
+ uint32_t match[2];
+ uint32_t prescale[2];
+ uint32_t match_prescale[2];
+ uint32_t rtc;
+ int64_t tick[2];
+ struct gptm_state *opaque[2];
+ QEMUTimer *timer[2];
+ /* The timers have an alternate output used to trigger the ADC. */
+ qemu_irq trigger;
+ qemu_irq irq;
+} gptm_state;
+
+static void gptm_update_irq(gptm_state *s)
+{
+ int level;
+ level = (s->state & s->mask) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void gptm_stop(gptm_state *s, int n)
+{
+ qemu_del_timer(s->timer[n]);
+}
+
+static void gptm_reload(gptm_state *s, int n, int reset)
+{
+ int64_t tick;
+ if (reset)
+ tick = qemu_get_clock(vm_clock);
+ else
+ tick = s->tick[n];
+
+ if (s->config == 0) {
+ /* 32-bit CountDown. */
+ uint32_t count;
+ count = s->load[0] | (s->load[1] << 16);
+ tick += (int64_t)count * system_clock_scale;
+ } else if (s->config == 1) {
+ /* 32-bit RTC. 1Hz tick. */
+ tick += get_ticks_per_sec();
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]);
+ }
+ s->tick[n] = tick;
+ qemu_mod_timer(s->timer[n], tick);
+}
+
+static void gptm_tick(void *opaque)
+{
+ gptm_state **p = (gptm_state **)opaque;
+ gptm_state *s;
+ int n;
+
+ s = *p;
+ n = p - s->opaque;
+ if (s->config == 0) {
+ s->state |= 1;
+ if ((s->control & 0x20)) {
+ /* Output trigger. */
+ qemu_irq_pulse(s->trigger);
+ }
+ if (s->mode[0] & 1) {
+ /* One-shot. */
+ s->control &= ~1;
+ } else {
+ /* Periodic. */
+ gptm_reload(s, 0, 0);
+ }
+ } else if (s->config == 1) {
+ /* RTC. */
+ uint32_t match;
+ s->rtc++;
+ match = s->match[0] | (s->match[1] << 16);
+ if (s->rtc > match)
+ s->rtc = 0;
+ if (s->rtc == 0) {
+ s->state |= 8;
+ }
+ gptm_reload(s, 0, 0);
+ } else if (s->mode[n] == 0xa) {
+ /* PWM mode. Not implemented. */
+ } else {
+ hw_error("TODO: 16-bit timer mode 0x%x\n", s->mode[n]);
+ }
+ gptm_update_irq(s);
+}
+
+static uint32_t gptm_read(void *opaque, target_phys_addr_t offset)
+{
+ gptm_state *s = (gptm_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* CFG */
+ return s->config;
+ case 0x04: /* TAMR */
+ return s->mode[0];
+ case 0x08: /* TBMR */
+ return s->mode[1];
+ case 0x0c: /* CTL */
+ return s->control;
+ case 0x18: /* IMR */
+ return s->mask;
+ case 0x1c: /* RIS */
+ return s->state;
+ case 0x20: /* MIS */
+ return s->state & s->mask;
+ case 0x24: /* CR */
+ return 0;
+ case 0x28: /* TAILR */
+ return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
+ case 0x2c: /* TBILR */
+ return s->load[1];
+ case 0x30: /* TAMARCHR */
+ return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
+ case 0x34: /* TBMATCHR */
+ return s->match[1];
+ case 0x38: /* TAPR */
+ return s->prescale[0];
+ case 0x3c: /* TBPR */
+ return s->prescale[1];
+ case 0x40: /* TAPMR */
+ return s->match_prescale[0];
+ case 0x44: /* TBPMR */
+ return s->match_prescale[1];
+ case 0x48: /* TAR */
+ if (s->control == 1)
+ return s->rtc;
+ case 0x4c: /* TBR */
+ hw_error("TODO: Timer value read\n");
+ default:
+ hw_error("gptm_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void gptm_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ gptm_state *s = (gptm_state *)opaque;
+ uint32_t oldval;
+
+ /* The timers should be disabled before changing the configuration.
+ We take advantage of this and defer everything until the timer
+ is enabled. */
+ switch (offset) {
+ case 0x00: /* CFG */
+ s->config = value;
+ break;
+ case 0x04: /* TAMR */
+ s->mode[0] = value;
+ break;
+ case 0x08: /* TBMR */
+ s->mode[1] = value;
+ break;
+ case 0x0c: /* CTL */
+ oldval = s->control;
+ s->control = value;
+ /* TODO: Implement pause. */
+ if ((oldval ^ value) & 1) {
+ if (value & 1) {
+ gptm_reload(s, 0, 1);
+ } else {
+ gptm_stop(s, 0);
+ }
+ }
+ if (((oldval ^ value) & 0x100) && s->config >= 4) {
+ if (value & 0x100) {
+ gptm_reload(s, 1, 1);
+ } else {
+ gptm_stop(s, 1);
+ }
+ }
+ break;
+ case 0x18: /* IMR */
+ s->mask = value & 0x77;
+ gptm_update_irq(s);
+ break;
+ case 0x24: /* CR */
+ s->state &= ~value;
+ break;
+ case 0x28: /* TAILR */
+ s->load[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->load[1] = value >> 16;
+ }
+ break;
+ case 0x2c: /* TBILR */
+ s->load[1] = value & 0xffff;
+ break;
+ case 0x30: /* TAMARCHR */
+ s->match[0] = value & 0xffff;
+ if (s->config < 4) {
+ s->match[1] = value >> 16;
+ }
+ break;
+ case 0x34: /* TBMATCHR */
+ s->match[1] = value >> 16;
+ break;
+ case 0x38: /* TAPR */
+ s->prescale[0] = value;
+ break;
+ case 0x3c: /* TBPR */
+ s->prescale[1] = value;
+ break;
+ case 0x40: /* TAPMR */
+ s->match_prescale[0] = value;
+ break;
+ case 0x44: /* TBPMR */
+ s->match_prescale[0] = value;
+ break;
+ default:
+ hw_error("gptm_write: Bad offset 0x%x\n", (int)offset);
+ }
+ gptm_update_irq(s);
+}
+
+static CPUReadMemoryFunc * const gptm_readfn[] = {
+ gptm_read,
+ gptm_read,
+ gptm_read
+};
+
+static CPUWriteMemoryFunc * const gptm_writefn[] = {
+ gptm_write,
+ gptm_write,
+ gptm_write
+};
+
+static void gptm_save(QEMUFile *f, void *opaque)
+{
+ gptm_state *s = (gptm_state *)opaque;
+
+ qemu_put_be32(f, s->config);
+ qemu_put_be32(f, s->mode[0]);
+ qemu_put_be32(f, s->mode[1]);
+ qemu_put_be32(f, s->control);
+ qemu_put_be32(f, s->state);
+ qemu_put_be32(f, s->mask);
+ qemu_put_be32(f, s->mode[0]);
+ qemu_put_be32(f, s->mode[0]);
+ qemu_put_be32(f, s->load[0]);
+ qemu_put_be32(f, s->load[1]);
+ qemu_put_be32(f, s->match[0]);
+ qemu_put_be32(f, s->match[1]);
+ qemu_put_be32(f, s->prescale[0]);
+ qemu_put_be32(f, s->prescale[1]);
+ qemu_put_be32(f, s->match_prescale[0]);
+ qemu_put_be32(f, s->match_prescale[1]);
+ qemu_put_be32(f, s->rtc);
+ qemu_put_be64(f, s->tick[0]);
+ qemu_put_be64(f, s->tick[1]);
+ qemu_put_timer(f, s->timer[0]);
+ qemu_put_timer(f, s->timer[1]);
+}
+
+static int gptm_load(QEMUFile *f, void *opaque, int version_id)
+{
+ gptm_state *s = (gptm_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->config = qemu_get_be32(f);
+ s->mode[0] = qemu_get_be32(f);
+ s->mode[1] = qemu_get_be32(f);
+ s->control = qemu_get_be32(f);
+ s->state = qemu_get_be32(f);
+ s->mask = qemu_get_be32(f);
+ s->mode[0] = qemu_get_be32(f);
+ s->mode[0] = qemu_get_be32(f);
+ s->load[0] = qemu_get_be32(f);
+ s->load[1] = qemu_get_be32(f);
+ s->match[0] = qemu_get_be32(f);
+ s->match[1] = qemu_get_be32(f);
+ s->prescale[0] = qemu_get_be32(f);
+ s->prescale[1] = qemu_get_be32(f);
+ s->match_prescale[0] = qemu_get_be32(f);
+ s->match_prescale[1] = qemu_get_be32(f);
+ s->rtc = qemu_get_be32(f);
+ s->tick[0] = qemu_get_be64(f);
+ s->tick[1] = qemu_get_be64(f);
+ qemu_get_timer(f, s->timer[0]);
+ qemu_get_timer(f, s->timer[1]);
+
+ return 0;
+}
+
+static int stellaris_gptm_init(SysBusDevice *dev)
+{
+ int iomemtype;
+ gptm_state *s = FROM_SYSBUS(gptm_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+ qdev_init_gpio_out(&dev->qdev, &s->trigger, 1);
+
+ iomemtype = cpu_register_io_memory(gptm_readfn,
+ gptm_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ s->opaque[0] = s->opaque[1] = s;
+ s->timer[0] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[0]);
+ s->timer[1] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[1]);
+ register_savevm(&dev->qdev, "stellaris_gptm", -1, 1,
+ gptm_save, gptm_load, s);
+ return 0;
+}
+
+
+/* System controller. */
+
+typedef struct {
+ uint32_t pborctl;
+ uint32_t ldopctl;
+ uint32_t int_status;
+ uint32_t int_mask;
+ uint32_t resc;
+ uint32_t rcc;
+ uint32_t rcgc[3];
+ uint32_t scgc[3];
+ uint32_t dcgc[3];
+ uint32_t clkvclr;
+ uint32_t ldoarst;
+ uint32_t user0;
+ uint32_t user1;
+ qemu_irq irq;
+ stellaris_board_info *board;
+} ssys_state;
+
+static void ssys_update(ssys_state *s)
+{
+ qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0);
+}
+
+static uint32_t pllcfg_sandstorm[16] = {
+ 0x31c0, /* 1 Mhz */
+ 0x1ae0, /* 1.8432 Mhz */
+ 0x18c0, /* 2 Mhz */
+ 0xd573, /* 2.4576 Mhz */
+ 0x37a6, /* 3.57954 Mhz */
+ 0x1ae2, /* 3.6864 Mhz */
+ 0x0c40, /* 4 Mhz */
+ 0x98bc, /* 4.906 Mhz */
+ 0x935b, /* 4.9152 Mhz */
+ 0x09c0, /* 5 Mhz */
+ 0x4dee, /* 5.12 Mhz */
+ 0x0c41, /* 6 Mhz */
+ 0x75db, /* 6.144 Mhz */
+ 0x1ae6, /* 7.3728 Mhz */
+ 0x0600, /* 8 Mhz */
+ 0x585b /* 8.192 Mhz */
+};
+
+static uint32_t pllcfg_fury[16] = {
+ 0x3200, /* 1 Mhz */
+ 0x1b20, /* 1.8432 Mhz */
+ 0x1900, /* 2 Mhz */
+ 0xf42b, /* 2.4576 Mhz */
+ 0x37e3, /* 3.57954 Mhz */
+ 0x1b21, /* 3.6864 Mhz */
+ 0x0c80, /* 4 Mhz */
+ 0x98ee, /* 4.906 Mhz */
+ 0xd5b4, /* 4.9152 Mhz */
+ 0x0a00, /* 5 Mhz */
+ 0x4e27, /* 5.12 Mhz */
+ 0x1902, /* 6 Mhz */
+ 0xec1c, /* 6.144 Mhz */
+ 0x1b23, /* 7.3728 Mhz */
+ 0x0640, /* 8 Mhz */
+ 0xb11c /* 8.192 Mhz */
+};
+
+static uint32_t ssys_read(void *opaque, target_phys_addr_t offset)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ switch (offset) {
+ case 0x000: /* DID0 */
+ return s->board->did0;
+ case 0x004: /* DID1 */
+ return s->board->did1;
+ case 0x008: /* DC0 */
+ return s->board->dc0;
+ case 0x010: /* DC1 */
+ return s->board->dc1;
+ case 0x014: /* DC2 */
+ return s->board->dc2;
+ case 0x018: /* DC3 */
+ return s->board->dc3;
+ case 0x01c: /* DC4 */
+ return s->board->dc4;
+ case 0x030: /* PBORCTL */
+ return s->pborctl;
+ case 0x034: /* LDOPCTL */
+ return s->ldopctl;
+ case 0x040: /* SRCR0 */
+ return 0;
+ case 0x044: /* SRCR1 */
+ return 0;
+ case 0x048: /* SRCR2 */
+ return 0;
+ case 0x050: /* RIS */
+ return s->int_status;
+ case 0x054: /* IMC */
+ return s->int_mask;
+ case 0x058: /* MISC */
+ return s->int_status & s->int_mask;
+ case 0x05c: /* RESC */
+ return s->resc;
+ case 0x060: /* RCC */
+ return s->rcc;
+ case 0x064: /* PLLCFG */
+ {
+ int xtal;
+ xtal = (s->rcc >> 6) & 0xf;
+ if (s->board->did0 & (1 << 16)) {
+ return pllcfg_fury[xtal];
+ } else {
+ return pllcfg_sandstorm[xtal];
+ }
+ }
+ case 0x100: /* RCGC0 */
+ return s->rcgc[0];
+ case 0x104: /* RCGC1 */
+ return s->rcgc[1];
+ case 0x108: /* RCGC2 */
+ return s->rcgc[2];
+ case 0x110: /* SCGC0 */
+ return s->scgc[0];
+ case 0x114: /* SCGC1 */
+ return s->scgc[1];
+ case 0x118: /* SCGC2 */
+ return s->scgc[2];
+ case 0x120: /* DCGC0 */
+ return s->dcgc[0];
+ case 0x124: /* DCGC1 */
+ return s->dcgc[1];
+ case 0x128: /* DCGC2 */
+ return s->dcgc[2];
+ case 0x150: /* CLKVCLR */
+ return s->clkvclr;
+ case 0x160: /* LDOARST */
+ return s->ldoarst;
+ case 0x1e0: /* USER0 */
+ return s->user0;
+ case 0x1e4: /* USER1 */
+ return s->user1;
+ default:
+ hw_error("ssys_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void ssys_calculate_system_clock(ssys_state *s)
+{
+ system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
+}
+
+static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ switch (offset) {
+ case 0x030: /* PBORCTL */
+ s->pborctl = value & 0xffff;
+ break;
+ case 0x034: /* LDOPCTL */
+ s->ldopctl = value & 0x1f;
+ break;
+ case 0x040: /* SRCR0 */
+ case 0x044: /* SRCR1 */
+ case 0x048: /* SRCR2 */
+ fprintf(stderr, "Peripheral reset not implemented\n");
+ break;
+ case 0x054: /* IMC */
+ s->int_mask = value & 0x7f;
+ break;
+ case 0x058: /* MISC */
+ s->int_status &= ~value;
+ break;
+ case 0x05c: /* RESC */
+ s->resc = value & 0x3f;
+ break;
+ case 0x060: /* RCC */
+ if ((s->rcc & (1 << 13)) != 0 && (value & (1 << 13)) == 0) {
+ /* PLL enable. */
+ s->int_status |= (1 << 6);
+ }
+ s->rcc = value;
+ ssys_calculate_system_clock(s);
+ break;
+ case 0x100: /* RCGC0 */
+ s->rcgc[0] = value;
+ break;
+ case 0x104: /* RCGC1 */
+ s->rcgc[1] = value;
+ break;
+ case 0x108: /* RCGC2 */
+ s->rcgc[2] = value;
+ break;
+ case 0x110: /* SCGC0 */
+ s->scgc[0] = value;
+ break;
+ case 0x114: /* SCGC1 */
+ s->scgc[1] = value;
+ break;
+ case 0x118: /* SCGC2 */
+ s->scgc[2] = value;
+ break;
+ case 0x120: /* DCGC0 */
+ s->dcgc[0] = value;
+ break;
+ case 0x124: /* DCGC1 */
+ s->dcgc[1] = value;
+ break;
+ case 0x128: /* DCGC2 */
+ s->dcgc[2] = value;
+ break;
+ case 0x150: /* CLKVCLR */
+ s->clkvclr = value;
+ break;
+ case 0x160: /* LDOARST */
+ s->ldoarst = value;
+ break;
+ default:
+ hw_error("ssys_write: Bad offset 0x%x\n", (int)offset);
+ }
+ ssys_update(s);
+}
+
+static CPUReadMemoryFunc * const ssys_readfn[] = {
+ ssys_read,
+ ssys_read,
+ ssys_read
+};
+
+static CPUWriteMemoryFunc * const ssys_writefn[] = {
+ ssys_write,
+ ssys_write,
+ ssys_write
+};
+
+static void ssys_reset(void *opaque)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ s->pborctl = 0x7ffd;
+ s->rcc = 0x078e3ac0;
+ s->rcgc[0] = 1;
+ s->scgc[0] = 1;
+ s->dcgc[0] = 1;
+}
+
+static void ssys_save(QEMUFile *f, void *opaque)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ qemu_put_be32(f, s->pborctl);
+ qemu_put_be32(f, s->ldopctl);
+ qemu_put_be32(f, s->int_mask);
+ qemu_put_be32(f, s->int_status);
+ qemu_put_be32(f, s->resc);
+ qemu_put_be32(f, s->rcc);
+ qemu_put_be32(f, s->rcgc[0]);
+ qemu_put_be32(f, s->rcgc[1]);
+ qemu_put_be32(f, s->rcgc[2]);
+ qemu_put_be32(f, s->scgc[0]);
+ qemu_put_be32(f, s->scgc[1]);
+ qemu_put_be32(f, s->scgc[2]);
+ qemu_put_be32(f, s->dcgc[0]);
+ qemu_put_be32(f, s->dcgc[1]);
+ qemu_put_be32(f, s->dcgc[2]);
+ qemu_put_be32(f, s->clkvclr);
+ qemu_put_be32(f, s->ldoarst);
+}
+
+static int ssys_load(QEMUFile *f, void *opaque, int version_id)
+{
+ ssys_state *s = (ssys_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->pborctl = qemu_get_be32(f);
+ s->ldopctl = qemu_get_be32(f);
+ s->int_mask = qemu_get_be32(f);
+ s->int_status = qemu_get_be32(f);
+ s->resc = qemu_get_be32(f);
+ s->rcc = qemu_get_be32(f);
+ s->rcgc[0] = qemu_get_be32(f);
+ s->rcgc[1] = qemu_get_be32(f);
+ s->rcgc[2] = qemu_get_be32(f);
+ s->scgc[0] = qemu_get_be32(f);
+ s->scgc[1] = qemu_get_be32(f);
+ s->scgc[2] = qemu_get_be32(f);
+ s->dcgc[0] = qemu_get_be32(f);
+ s->dcgc[1] = qemu_get_be32(f);
+ s->dcgc[2] = qemu_get_be32(f);
+ s->clkvclr = qemu_get_be32(f);
+ s->ldoarst = qemu_get_be32(f);
+ ssys_calculate_system_clock(s);
+
+ return 0;
+}
+
+static int stellaris_sys_init(uint32_t base, qemu_irq irq,
+ stellaris_board_info * board,
+ uint8_t *macaddr)
+{
+ int iomemtype;
+ ssys_state *s;
+
+ s = (ssys_state *)qemu_mallocz(sizeof(ssys_state));
+ s->irq = irq;
+ s->board = board;
+ /* Most devices come preprogrammed with a MAC address in the user data. */
+ s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16);
+ s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16);
+
+ iomemtype = cpu_register_io_memory(ssys_readfn,
+ ssys_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x00001000, iomemtype);
+ ssys_reset(s);
+ register_savevm(NULL, "stellaris_sys", -1, 1, ssys_save, ssys_load, s);
+ return 0;
+}
+
+
+/* I2C controller. */
+
+typedef struct {
+ SysBusDevice busdev;
+ i2c_bus *bus;
+ qemu_irq irq;
+ uint32_t msa;
+ uint32_t mcs;
+ uint32_t mdr;
+ uint32_t mtpr;
+ uint32_t mimr;
+ uint32_t mris;
+ uint32_t mcr;
+} stellaris_i2c_state;
+
+#define STELLARIS_I2C_MCS_BUSY 0x01
+#define STELLARIS_I2C_MCS_ERROR 0x02
+#define STELLARIS_I2C_MCS_ADRACK 0x04
+#define STELLARIS_I2C_MCS_DATACK 0x08
+#define STELLARIS_I2C_MCS_ARBLST 0x10
+#define STELLARIS_I2C_MCS_IDLE 0x20
+#define STELLARIS_I2C_MCS_BUSBSY 0x40
+
+static uint32_t stellaris_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* MSA */
+ return s->msa;
+ case 0x04: /* MCS */
+ /* We don't emulate timing, so the controller is never busy. */
+ return s->mcs | STELLARIS_I2C_MCS_IDLE;
+ case 0x08: /* MDR */
+ return s->mdr;
+ case 0x0c: /* MTPR */
+ return s->mtpr;
+ case 0x10: /* MIMR */
+ return s->mimr;
+ case 0x14: /* MRIS */
+ return s->mris;
+ case 0x18: /* MMIS */
+ return s->mris & s->mimr;
+ case 0x20: /* MCR */
+ return s->mcr;
+ default:
+ hw_error("strllaris_i2c_read: Bad offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_i2c_update(stellaris_i2c_state *s)
+{
+ int level;
+
+ level = (s->mris & s->mimr) != 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void stellaris_i2c_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* MSA */
+ s->msa = value & 0xff;
+ break;
+ case 0x04: /* MCS */
+ if ((s->mcr & 0x10) == 0) {
+ /* Disabled. Do nothing. */
+ break;
+ }
+ /* Grab the bus if this is starting a transfer. */
+ if ((value & 2) && (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+ if (i2c_start_transfer(s->bus, s->msa >> 1, s->msa & 1)) {
+ s->mcs |= STELLARIS_I2C_MCS_ARBLST;
+ } else {
+ s->mcs &= ~STELLARIS_I2C_MCS_ARBLST;
+ s->mcs |= STELLARIS_I2C_MCS_BUSBSY;
+ }
+ }
+ /* If we don't have the bus then indicate an error. */
+ if (!i2c_bus_busy(s->bus)
+ || (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+ s->mcs |= STELLARIS_I2C_MCS_ERROR;
+ break;
+ }
+ s->mcs &= ~STELLARIS_I2C_MCS_ERROR;
+ if (value & 1) {
+ /* Transfer a byte. */
+ /* TODO: Handle errors. */
+ if (s->msa & 1) {
+ /* Recv */
+ s->mdr = i2c_recv(s->bus) & 0xff;
+ } else {
+ /* Send */
+ i2c_send(s->bus, s->mdr);
+ }
+ /* Raise an interrupt. */
+ s->mris |= 1;
+ }
+ if (value & 4) {
+ /* Finish transfer. */
+ i2c_end_transfer(s->bus);
+ s->mcs &= ~STELLARIS_I2C_MCS_BUSBSY;
+ }
+ break;
+ case 0x08: /* MDR */
+ s->mdr = value & 0xff;
+ break;
+ case 0x0c: /* MTPR */
+ s->mtpr = value & 0xff;
+ break;
+ case 0x10: /* MIMR */
+ s->mimr = 1;
+ break;
+ case 0x1c: /* MICR */
+ s->mris &= ~value;
+ break;
+ case 0x20: /* MCR */
+ if (value & 1)
+ hw_error(
+ "stellaris_i2c_write: Loopback not implemented\n");
+ if (value & 0x20)
+ hw_error(
+ "stellaris_i2c_write: Slave mode not implemented\n");
+ s->mcr = value & 0x31;
+ break;
+ default:
+ hw_error("stellaris_i2c_write: Bad offset 0x%x\n",
+ (int)offset);
+ }
+ stellaris_i2c_update(s);
+}
+
+static void stellaris_i2c_reset(stellaris_i2c_state *s)
+{
+ if (s->mcs & STELLARIS_I2C_MCS_BUSBSY)
+ i2c_end_transfer(s->bus);
+
+ s->msa = 0;
+ s->mcs = 0;
+ s->mdr = 0;
+ s->mtpr = 1;
+ s->mimr = 0;
+ s->mris = 0;
+ s->mcr = 0;
+ stellaris_i2c_update(s);
+}
+
+static CPUReadMemoryFunc * const stellaris_i2c_readfn[] = {
+ stellaris_i2c_read,
+ stellaris_i2c_read,
+ stellaris_i2c_read
+};
+
+static CPUWriteMemoryFunc * const stellaris_i2c_writefn[] = {
+ stellaris_i2c_write,
+ stellaris_i2c_write,
+ stellaris_i2c_write
+};
+
+static void stellaris_i2c_save(QEMUFile *f, void *opaque)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ qemu_put_be32(f, s->msa);
+ qemu_put_be32(f, s->mcs);
+ qemu_put_be32(f, s->mdr);
+ qemu_put_be32(f, s->mtpr);
+ qemu_put_be32(f, s->mimr);
+ qemu_put_be32(f, s->mris);
+ qemu_put_be32(f, s->mcr);
+}
+
+static int stellaris_i2c_load(QEMUFile *f, void *opaque, int version_id)
+{
+ stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->msa = qemu_get_be32(f);
+ s->mcs = qemu_get_be32(f);
+ s->mdr = qemu_get_be32(f);
+ s->mtpr = qemu_get_be32(f);
+ s->mimr = qemu_get_be32(f);
+ s->mris = qemu_get_be32(f);
+ s->mcr = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int stellaris_i2c_init(SysBusDevice * dev)
+{
+ stellaris_i2c_state *s = FROM_SYSBUS(stellaris_i2c_state, dev);
+ i2c_bus *bus;
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ bus = i2c_init_bus(&dev->qdev, "i2c");
+ s->bus = bus;
+
+ iomemtype = cpu_register_io_memory(stellaris_i2c_readfn,
+ stellaris_i2c_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ /* ??? For now we only implement the master interface. */
+ stellaris_i2c_reset(s);
+ register_savevm(&dev->qdev, "stellaris_i2c", -1, 1,
+ stellaris_i2c_save, stellaris_i2c_load, s);
+ return 0;
+}
+
+/* Analogue to Digital Converter. This is only partially implemented,
+ enough for applications that use a combined ADC and timer tick. */
+
+#define STELLARIS_ADC_EM_CONTROLLER 0
+#define STELLARIS_ADC_EM_COMP 1
+#define STELLARIS_ADC_EM_EXTERNAL 4
+#define STELLARIS_ADC_EM_TIMER 5
+#define STELLARIS_ADC_EM_PWM0 6
+#define STELLARIS_ADC_EM_PWM1 7
+#define STELLARIS_ADC_EM_PWM2 8
+
+#define STELLARIS_ADC_FIFO_EMPTY 0x0100
+#define STELLARIS_ADC_FIFO_FULL 0x1000
+
+typedef struct
+{
+ SysBusDevice busdev;
+ uint32_t actss;
+ uint32_t ris;
+ uint32_t im;
+ uint32_t emux;
+ uint32_t ostat;
+ uint32_t ustat;
+ uint32_t sspri;
+ uint32_t sac;
+ struct {
+ uint32_t state;
+ uint32_t data[16];
+ } fifo[4];
+ uint32_t ssmux[4];
+ uint32_t ssctl[4];
+ uint32_t noise;
+ qemu_irq irq[4];
+} stellaris_adc_state;
+
+static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n)
+{
+ int tail;
+
+ tail = s->fifo[n].state & 0xf;
+ if (s->fifo[n].state & STELLARIS_ADC_FIFO_EMPTY) {
+ s->ustat |= 1 << n;
+ } else {
+ s->fifo[n].state = (s->fifo[n].state & ~0xf) | ((tail + 1) & 0xf);
+ s->fifo[n].state &= ~STELLARIS_ADC_FIFO_FULL;
+ if (tail + 1 == ((s->fifo[n].state >> 4) & 0xf))
+ s->fifo[n].state |= STELLARIS_ADC_FIFO_EMPTY;
+ }
+ return s->fifo[n].data[tail];
+}
+
+static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n,
+ uint32_t value)
+{
+ int head;
+
+ /* TODO: Real hardware has limited size FIFOs. We have a full 16 entry
+ FIFO fir each sequencer. */
+ head = (s->fifo[n].state >> 4) & 0xf;
+ if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) {
+ s->ostat |= 1 << n;
+ return;
+ }
+ s->fifo[n].data[head] = value;
+ head = (head + 1) & 0xf;
+ s->fifo[n].state &= ~STELLARIS_ADC_FIFO_EMPTY;
+ s->fifo[n].state = (s->fifo[n].state & ~0xf0) | (head << 4);
+ if ((s->fifo[n].state & 0xf) == head)
+ s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL;
+}
+
+static void stellaris_adc_update(stellaris_adc_state *s)
+{
+ int level;
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ level = (s->ris & s->im & (1 << n)) != 0;
+ qemu_set_irq(s->irq[n], level);
+ }
+}
+
+static void stellaris_adc_trigger(void *opaque, int irq, int level)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ if ((s->actss & (1 << n)) == 0) {
+ continue;
+ }
+
+ if (((s->emux >> (n * 4)) & 0xff) != 5) {
+ continue;
+ }
+
+ /* Some applications use the ADC as a random number source, so introduce
+ some variation into the signal. */
+ s->noise = s->noise * 314159 + 1;
+ /* ??? actual inputs not implemented. Return an arbitrary value. */
+ stellaris_adc_fifo_write(s, n, 0x200 + ((s->noise >> 16) & 7));
+ s->ris |= (1 << n);
+ stellaris_adc_update(s);
+ }
+}
+
+static void stellaris_adc_reset(stellaris_adc_state *s)
+{
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ s->ssmux[n] = 0;
+ s->ssctl[n] = 0;
+ s->fifo[n].state = STELLARIS_ADC_FIFO_EMPTY;
+ }
+}
+
+static uint32_t stellaris_adc_read(void *opaque, target_phys_addr_t offset)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+ /* TODO: Implement this. */
+ if (offset >= 0x40 && offset < 0xc0) {
+ int n;
+ n = (offset - 0x40) >> 5;
+ switch (offset & 0x1f) {
+ case 0x00: /* SSMUX */
+ return s->ssmux[n];
+ case 0x04: /* SSCTL */
+ return s->ssctl[n];
+ case 0x08: /* SSFIFO */
+ return stellaris_adc_fifo_read(s, n);
+ case 0x0c: /* SSFSTAT */
+ return s->fifo[n].state;
+ default:
+ break;
+ }
+ }
+ switch (offset) {
+ case 0x00: /* ACTSS */
+ return s->actss;
+ case 0x04: /* RIS */
+ return s->ris;
+ case 0x08: /* IM */
+ return s->im;
+ case 0x0c: /* ISC */
+ return s->ris & s->im;
+ case 0x10: /* OSTAT */
+ return s->ostat;
+ case 0x14: /* EMUX */
+ return s->emux;
+ case 0x18: /* USTAT */
+ return s->ustat;
+ case 0x20: /* SSPRI */
+ return s->sspri;
+ case 0x30: /* SAC */
+ return s->sac;
+ default:
+ hw_error("strllaris_adc_read: Bad offset 0x%x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_adc_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+ /* TODO: Implement this. */
+ if (offset >= 0x40 && offset < 0xc0) {
+ int n;
+ n = (offset - 0x40) >> 5;
+ switch (offset & 0x1f) {
+ case 0x00: /* SSMUX */
+ s->ssmux[n] = value & 0x33333333;
+ return;
+ case 0x04: /* SSCTL */
+ if (value != 6) {
+ hw_error("ADC: Unimplemented sequence %x\n",
+ value);
+ }
+ s->ssctl[n] = value;
+ return;
+ default:
+ break;
+ }
+ }
+ switch (offset) {
+ case 0x00: /* ACTSS */
+ s->actss = value & 0xf;
+ break;
+ case 0x08: /* IM */
+ s->im = value;
+ break;
+ case 0x0c: /* ISC */
+ s->ris &= ~value;
+ break;
+ case 0x10: /* OSTAT */
+ s->ostat &= ~value;
+ break;
+ case 0x14: /* EMUX */
+ s->emux = value;
+ break;
+ case 0x18: /* USTAT */
+ s->ustat &= ~value;
+ break;
+ case 0x20: /* SSPRI */
+ s->sspri = value;
+ break;
+ case 0x28: /* PSSI */
+ hw_error("Not implemented: ADC sample initiate\n");
+ break;
+ case 0x30: /* SAC */
+ s->sac = value;
+ break;
+ default:
+ hw_error("stellaris_adc_write: Bad offset 0x%x\n", (int)offset);
+ }
+ stellaris_adc_update(s);
+}
+
+static CPUReadMemoryFunc * const stellaris_adc_readfn[] = {
+ stellaris_adc_read,
+ stellaris_adc_read,
+ stellaris_adc_read
+};
+
+static CPUWriteMemoryFunc * const stellaris_adc_writefn[] = {
+ stellaris_adc_write,
+ stellaris_adc_write,
+ stellaris_adc_write
+};
+
+static void stellaris_adc_save(QEMUFile *f, void *opaque)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+ int i;
+ int j;
+
+ qemu_put_be32(f, s->actss);
+ qemu_put_be32(f, s->ris);
+ qemu_put_be32(f, s->im);
+ qemu_put_be32(f, s->emux);
+ qemu_put_be32(f, s->ostat);
+ qemu_put_be32(f, s->ustat);
+ qemu_put_be32(f, s->sspri);
+ qemu_put_be32(f, s->sac);
+ for (i = 0; i < 4; i++) {
+ qemu_put_be32(f, s->fifo[i].state);
+ for (j = 0; j < 16; j++) {
+ qemu_put_be32(f, s->fifo[i].data[j]);
+ }
+ qemu_put_be32(f, s->ssmux[i]);
+ qemu_put_be32(f, s->ssctl[i]);
+ }
+ qemu_put_be32(f, s->noise);
+}
+
+static int stellaris_adc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+ int i;
+ int j;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->actss = qemu_get_be32(f);
+ s->ris = qemu_get_be32(f);
+ s->im = qemu_get_be32(f);
+ s->emux = qemu_get_be32(f);
+ s->ostat = qemu_get_be32(f);
+ s->ustat = qemu_get_be32(f);
+ s->sspri = qemu_get_be32(f);
+ s->sac = qemu_get_be32(f);
+ for (i = 0; i < 4; i++) {
+ s->fifo[i].state = qemu_get_be32(f);
+ for (j = 0; j < 16; j++) {
+ s->fifo[i].data[j] = qemu_get_be32(f);
+ }
+ s->ssmux[i] = qemu_get_be32(f);
+ s->ssctl[i] = qemu_get_be32(f);
+ }
+ s->noise = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int stellaris_adc_init(SysBusDevice *dev)
+{
+ stellaris_adc_state *s = FROM_SYSBUS(stellaris_adc_state, dev);
+ int iomemtype;
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ sysbus_init_irq(dev, &s->irq[n]);
+ }
+
+ iomemtype = cpu_register_io_memory(stellaris_adc_readfn,
+ stellaris_adc_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ stellaris_adc_reset(s);
+ qdev_init_gpio_in(&dev->qdev, stellaris_adc_trigger, 1);
+ register_savevm(&dev->qdev, "stellaris_adc", -1, 1,
+ stellaris_adc_save, stellaris_adc_load, s);
+ return 0;
+}
+
+/* Some boards have both an OLED controller and SD card connected to
+ the same SSI port, with the SD card chip select connected to a
+ GPIO pin. Technically the OLED chip select is connected to the SSI
+ Fss pin. We do not bother emulating that as both devices should
+ never be selected simultaneously, and our OLED controller ignores stray
+ 0xff commands that occur when deselecting the SD card. */
+
+typedef struct {
+ SSISlave ssidev;
+ qemu_irq irq;
+ int current_dev;
+ SSIBus *bus[2];
+} stellaris_ssi_bus_state;
+
+static void stellaris_ssi_bus_select(void *opaque, int irq, int level)
+{
+ stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
+
+ s->current_dev = level;
+}
+
+static uint32_t stellaris_ssi_bus_transfer(SSISlave *dev, uint32_t val)
+{
+ stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev);
+
+ return ssi_transfer(s->bus[s->current_dev], val);
+}
+
+static void stellaris_ssi_bus_save(QEMUFile *f, void *opaque)
+{
+ stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
+
+ qemu_put_be32(f, s->current_dev);
+}
+
+static int stellaris_ssi_bus_load(QEMUFile *f, void *opaque, int version_id)
+{
+ stellaris_ssi_bus_state *s = (stellaris_ssi_bus_state *)opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->current_dev = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int stellaris_ssi_bus_init(SSISlave *dev)
+{
+ stellaris_ssi_bus_state *s = FROM_SSI_SLAVE(stellaris_ssi_bus_state, dev);
+
+ s->bus[0] = ssi_create_bus(&dev->qdev, "ssi0");
+ s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1");
+ qdev_init_gpio_in(&dev->qdev, stellaris_ssi_bus_select, 1);
+
+ register_savevm(&dev->qdev, "stellaris_ssi_bus", -1, 1,
+ stellaris_ssi_bus_save, stellaris_ssi_bus_load, s);
+ return 0;
+}
+
+/* Board init. */
+static stellaris_board_info stellaris_boards[] = {
+ { "LM3S811EVB",
+ 0,
+ 0x0032000e,
+ 0x001f001f, /* dc0 */
+ 0x001132bf,
+ 0x01071013,
+ 0x3f0f01ff,
+ 0x0000001f,
+ BP_OLED_I2C
+ },
+ { "LM3S6965EVB",
+ 0x10010002,
+ 0x1073402e,
+ 0x00ff007f, /* dc0 */
+ 0x001133ff,
+ 0x030f5317,
+ 0x0f0f87ff,
+ 0x5000007f,
+ BP_OLED_SSI | BP_GAMEPAD
+ }
+};
+
+static void stellaris_init(const char *kernel_filename, const char *cpu_model,
+ stellaris_board_info *board)
+{
+ static const int uart_irq[] = {5, 6, 33, 34};
+ static const int timer_irq[] = {19, 21, 23, 35};
+ static const uint32_t gpio_addr[7] =
+ { 0x40004000, 0x40005000, 0x40006000, 0x40007000,
+ 0x40024000, 0x40025000, 0x40026000};
+ static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
+
+ qemu_irq *pic;
+ DeviceState *gpio_dev[7];
+ qemu_irq gpio_in[7][8];
+ qemu_irq gpio_out[7][8];
+ qemu_irq adc;
+ int sram_size;
+ int flash_size;
+ i2c_bus *i2c;
+ DeviceState *dev;
+ int i;
+ int j;
+
+ flash_size = ((board->dc0 & 0xffff) + 1) << 1;
+ sram_size = (board->dc0 >> 18) + 1;
+ pic = armv7m_init(flash_size, sram_size, kernel_filename, cpu_model);
+
+ if (board->dc1 & (1 << 16)) {
+ dev = sysbus_create_varargs("stellaris-adc", 0x40038000,
+ pic[14], pic[15], pic[16], pic[17], NULL);
+ adc = qdev_get_gpio_in(dev, 0);
+ } else {
+ adc = NULL;
+ }
+ for (i = 0; i < 4; i++) {
+ if (board->dc2 & (0x10000 << i)) {
+ dev = sysbus_create_simple("stellaris-gptm",
+ 0x40030000 + i * 0x1000,
+ pic[timer_irq[i]]);
+ /* TODO: This is incorrect, but we get away with it because
+ the ADC output is only ever pulsed. */
+ qdev_connect_gpio_out(dev, 0, adc);
+ }
+ }
+
+ stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr);
+
+ for (i = 0; i < 7; i++) {
+ if (board->dc4 & (1 << i)) {
+ gpio_dev[i] = sysbus_create_simple("pl061", gpio_addr[i],
+ pic[gpio_irq[i]]);
+ for (j = 0; j < 8; j++) {
+ gpio_in[i][j] = qdev_get_gpio_in(gpio_dev[i], j);
+ gpio_out[i][j] = NULL;
+ }
+ }
+ }
+
+ if (board->dc2 & (1 << 12)) {
+ dev = sysbus_create_simple("stellaris-i2c", 0x40020000, pic[8]);
+ i2c = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ if (board->peripherals & BP_OLED_I2C) {
+ i2c_create_slave(i2c, "ssd0303", 0x3d);
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (board->dc2 & (1 << i)) {
+ sysbus_create_simple("pl011_luminary", 0x4000c000 + i * 0x1000,
+ pic[uart_irq[i]]);
+ }
+ }
+ if (board->dc2 & (1 << 4)) {
+ dev = sysbus_create_simple("pl022", 0x40008000, pic[7]);
+ if (board->peripherals & BP_OLED_SSI) {
+ DeviceState *mux;
+ void *bus;
+
+ bus = qdev_get_child_bus(dev, "ssi");
+ mux = ssi_create_slave(bus, "evb6965-ssi");
+ gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0);
+
+ bus = qdev_get_child_bus(mux, "ssi0");
+ ssi_create_slave(bus, "ssi-sd");
+
+ bus = qdev_get_child_bus(mux, "ssi1");
+ dev = ssi_create_slave(bus, "ssd0323");
+ gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0);
+
+ /* Make sure the select pin is high. */
+ qemu_irq_raise(gpio_out[GPIO_D][0]);
+ }
+ }
+ if (board->dc4 & (1 << 28)) {
+ DeviceState *enet;
+
+ qemu_check_nic_model(&nd_table[0], "stellaris");
+
+ enet = qdev_create(NULL, "stellaris_enet");
+ qdev_set_nic_properties(enet, &nd_table[0]);
+ qdev_init_nofail(enet);
+ sysbus_mmio_map(sysbus_from_qdev(enet), 0, 0x40048000);
+ sysbus_connect_irq(sysbus_from_qdev(enet), 0, pic[42]);
+ }
+ if (board->peripherals & BP_GAMEPAD) {
+ qemu_irq gpad_irq[5];
+ static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d };
+
+ gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */
+ gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */
+ gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */
+ gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */
+ gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */
+
+ stellaris_gamepad_init(5, gpad_irq, gpad_keycode);
+ }
+ for (i = 0; i < 7; i++) {
+ if (board->dc4 & (1 << i)) {
+ for (j = 0; j < 8; j++) {
+ if (gpio_out[i][j]) {
+ qdev_connect_gpio_out(gpio_dev[i], j, gpio_out[i][j]);
+ }
+ }
+ }
+ }
+}
+
+/* FIXME: Figure out how to generate these from stellaris_boards. */
+static void lm3s811evb_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ stellaris_init(kernel_filename, cpu_model, &stellaris_boards[0]);
+}
+
+static void lm3s6965evb_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ stellaris_init(kernel_filename, cpu_model, &stellaris_boards[1]);
+}
+
+static QEMUMachine lm3s811evb_machine = {
+ .name = "lm3s811evb",
+ .desc = "Stellaris LM3S811EVB",
+ .init = lm3s811evb_init,
+};
+
+static QEMUMachine lm3s6965evb_machine = {
+ .name = "lm3s6965evb",
+ .desc = "Stellaris LM3S6965EVB",
+ .init = lm3s6965evb_init,
+};
+
+static void stellaris_machine_init(void)
+{
+ qemu_register_machine(&lm3s811evb_machine);
+ qemu_register_machine(&lm3s6965evb_machine);
+}
+
+machine_init(stellaris_machine_init);
+
+static SSISlaveInfo stellaris_ssi_bus_info = {
+ .qdev.name = "evb6965-ssi",
+ .qdev.size = sizeof(stellaris_ssi_bus_state),
+ .init = stellaris_ssi_bus_init,
+ .transfer = stellaris_ssi_bus_transfer
+};
+
+static void stellaris_register_devices(void)
+{
+ sysbus_register_dev("stellaris-i2c", sizeof(stellaris_i2c_state),
+ stellaris_i2c_init);
+ sysbus_register_dev("stellaris-gptm", sizeof(gptm_state),
+ stellaris_gptm_init);
+ sysbus_register_dev("stellaris-adc", sizeof(stellaris_adc_state),
+ stellaris_adc_init);
+ ssi_register_slave(&stellaris_ssi_bus_info);
+}
+
+device_init(stellaris_register_devices)
diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c
new file mode 100644
index 0000000..6a0583a
--- /dev/null
+++ b/hw/stellaris_enet.c
@@ -0,0 +1,443 @@
+/*
+ * Luminary Micro Stellaris Ethernet Controller
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+#include "sysbus.h"
+#include "net.h"
+#include <zlib.h>
+
+//#define DEBUG_STELLARIS_ENET 1
+
+#ifdef DEBUG_STELLARIS_ENET
+#define DPRINTF(fmt, ...) \
+do { printf("stellaris_enet: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "stellaris_enet: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define SE_INT_RX 0x01
+#define SE_INT_TXER 0x02
+#define SE_INT_TXEMP 0x04
+#define SE_INT_FOV 0x08
+#define SE_INT_RXER 0x10
+#define SE_INT_MD 0x20
+#define SE_INT_PHY 0x40
+
+#define SE_RCTL_RXEN 0x01
+#define SE_RCTL_AMUL 0x02
+#define SE_RCTL_PRMS 0x04
+#define SE_RCTL_BADCRC 0x08
+#define SE_RCTL_RSTFIFO 0x10
+
+#define SE_TCTL_TXEN 0x01
+#define SE_TCTL_PADEN 0x02
+#define SE_TCTL_CRC 0x04
+#define SE_TCTL_DUPLEX 0x08
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t ris;
+ uint32_t im;
+ uint32_t rctl;
+ uint32_t tctl;
+ uint32_t thr;
+ uint32_t mctl;
+ uint32_t mdv;
+ uint32_t mtxd;
+ uint32_t mrxd;
+ uint32_t np;
+ int tx_frame_len;
+ int tx_fifo_len;
+ uint8_t tx_fifo[2048];
+ /* Real hardware has a 2k fifo, which works out to be at most 31 packets.
+ We implement a full 31 packet fifo. */
+ struct {
+ uint8_t data[2048];
+ int len;
+ } rx[31];
+ uint8_t *rx_fifo;
+ int rx_fifo_len;
+ int next_packet;
+ NICState *nic;
+ NICConf conf;
+ qemu_irq irq;
+ int mmio_index;
+} stellaris_enet_state;
+
+static void stellaris_enet_update(stellaris_enet_state *s)
+{
+ qemu_set_irq(s->irq, (s->ris & s->im) != 0);
+}
+
+/* TODO: Implement MAC address filtering. */
+static ssize_t stellaris_enet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int n;
+ uint8_t *p;
+ uint32_t crc;
+
+ if ((s->rctl & SE_RCTL_RXEN) == 0)
+ return -1;
+ if (s->np >= 31) {
+ DPRINTF("Packet dropped\n");
+ return -1;
+ }
+
+ DPRINTF("Received packet len=%d\n", size);
+ n = s->next_packet + s->np;
+ if (n >= 31)
+ n -= 31;
+ s->np++;
+
+ s->rx[n].len = size + 6;
+ p = s->rx[n].data;
+ *(p++) = (size + 6);
+ *(p++) = (size + 6) >> 8;
+ memcpy (p, buf, size);
+ p += size;
+ crc = crc32(~0, buf, size);
+ *(p++) = crc;
+ *(p++) = crc >> 8;
+ *(p++) = crc >> 16;
+ *(p++) = crc >> 24;
+ /* Clear the remaining bytes in the last word. */
+ if ((size & 3) != 2) {
+ memset(p, 0, (6 - size) & 3);
+ }
+
+ s->ris |= SE_INT_RX;
+ stellaris_enet_update(s);
+
+ return size;
+}
+
+static int stellaris_enet_can_receive(VLANClientState *nc)
+{
+ stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if ((s->rctl & SE_RCTL_RXEN) == 0)
+ return 1;
+
+ return (s->np < 31);
+}
+
+static uint32_t stellaris_enet_read(void *opaque, target_phys_addr_t offset)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+ uint32_t val;
+
+ switch (offset) {
+ case 0x00: /* RIS */
+ DPRINTF("IRQ status %02x\n", s->ris);
+ return s->ris;
+ case 0x04: /* IM */
+ return s->im;
+ case 0x08: /* RCTL */
+ return s->rctl;
+ case 0x0c: /* TCTL */
+ return s->tctl;
+ case 0x10: /* DATA */
+ if (s->rx_fifo_len == 0) {
+ if (s->np == 0) {
+ BADF("RX underflow\n");
+ return 0;
+ }
+ s->rx_fifo_len = s->rx[s->next_packet].len;
+ s->rx_fifo = s->rx[s->next_packet].data;
+ DPRINTF("RX FIFO start packet len=%d\n", s->rx_fifo_len);
+ }
+ val = s->rx_fifo[0] | (s->rx_fifo[1] << 8) | (s->rx_fifo[2] << 16)
+ | (s->rx_fifo[3] << 24);
+ s->rx_fifo += 4;
+ s->rx_fifo_len -= 4;
+ if (s->rx_fifo_len <= 0) {
+ s->rx_fifo_len = 0;
+ s->next_packet++;
+ if (s->next_packet >= 31)
+ s->next_packet = 0;
+ s->np--;
+ DPRINTF("RX done np=%d\n", s->np);
+ }
+ return val;
+ case 0x14: /* IA0 */
+ return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
+ | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
+ case 0x18: /* IA1 */
+ return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
+ case 0x1c: /* THR */
+ return s->thr;
+ case 0x20: /* MCTL */
+ return s->mctl;
+ case 0x24: /* MDV */
+ return s->mdv;
+ case 0x28: /* MADD */
+ return 0;
+ case 0x2c: /* MTXD */
+ return s->mtxd;
+ case 0x30: /* MRXD */
+ return s->mrxd;
+ case 0x34: /* NP */
+ return s->np;
+ case 0x38: /* TR */
+ return 0;
+ case 0x3c: /* Undocuented: Timestamp? */
+ return 0;
+ default:
+ hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void stellaris_enet_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+
+ switch (offset) {
+ case 0x00: /* IACK */
+ s->ris &= ~value;
+ DPRINTF("IRQ ack %02x/%02x\n", value, s->ris);
+ stellaris_enet_update(s);
+ /* Clearing TXER also resets the TX fifo. */
+ if (value & SE_INT_TXER)
+ s->tx_frame_len = -1;
+ break;
+ case 0x04: /* IM */
+ DPRINTF("IRQ mask %02x/%02x\n", value, s->ris);
+ s->im = value;
+ stellaris_enet_update(s);
+ break;
+ case 0x08: /* RCTL */
+ s->rctl = value;
+ if (value & SE_RCTL_RSTFIFO) {
+ s->rx_fifo_len = 0;
+ s->np = 0;
+ stellaris_enet_update(s);
+ }
+ break;
+ case 0x0c: /* TCTL */
+ s->tctl = value;
+ break;
+ case 0x10: /* DATA */
+ if (s->tx_frame_len == -1) {
+ s->tx_frame_len = value & 0xffff;
+ if (s->tx_frame_len > 2032) {
+ DPRINTF("TX frame too long (%d)\n", s->tx_frame_len);
+ s->tx_frame_len = 0;
+ s->ris |= SE_INT_TXER;
+ stellaris_enet_update(s);
+ } else {
+ DPRINTF("Start TX frame len=%d\n", s->tx_frame_len);
+ /* The value written does not include the ethernet header. */
+ s->tx_frame_len += 14;
+ if ((s->tctl & SE_TCTL_CRC) == 0)
+ s->tx_frame_len += 4;
+ s->tx_fifo_len = 0;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 16;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 24;
+ }
+ } else {
+ s->tx_fifo[s->tx_fifo_len++] = value;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 8;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 16;
+ s->tx_fifo[s->tx_fifo_len++] = value >> 24;
+ if (s->tx_fifo_len >= s->tx_frame_len) {
+ /* We don't implement explicit CRC, so just chop it off. */
+ if ((s->tctl & SE_TCTL_CRC) == 0)
+ s->tx_frame_len -= 4;
+ if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) {
+ memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len);
+ s->tx_fifo_len = 60;
+ }
+ qemu_send_packet(&s->nic->nc, s->tx_fifo, s->tx_frame_len);
+ s->tx_frame_len = -1;
+ s->ris |= SE_INT_TXEMP;
+ stellaris_enet_update(s);
+ DPRINTF("Done TX\n");
+ }
+ }
+ break;
+ case 0x14: /* IA0 */
+ s->conf.macaddr.a[0] = value;
+ s->conf.macaddr.a[1] = value >> 8;
+ s->conf.macaddr.a[2] = value >> 16;
+ s->conf.macaddr.a[3] = value >> 24;
+ break;
+ case 0x18: /* IA1 */
+ s->conf.macaddr.a[4] = value;
+ s->conf.macaddr.a[5] = value >> 8;
+ break;
+ case 0x1c: /* THR */
+ s->thr = value;
+ break;
+ case 0x20: /* MCTL */
+ s->mctl = value;
+ break;
+ case 0x24: /* MDV */
+ s->mdv = value;
+ break;
+ case 0x28: /* MADD */
+ /* ignored. */
+ break;
+ case 0x2c: /* MTXD */
+ s->mtxd = value & 0xff;
+ break;
+ case 0x30: /* MRXD */
+ case 0x34: /* NP */
+ case 0x38: /* TR */
+ /* Ignored. */
+ case 0x3c: /* Undocuented: Timestamp? */
+ /* Ignored. */
+ break;
+ default:
+ hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static CPUReadMemoryFunc * const stellaris_enet_readfn[] = {
+ stellaris_enet_read,
+ stellaris_enet_read,
+ stellaris_enet_read
+};
+
+static CPUWriteMemoryFunc * const stellaris_enet_writefn[] = {
+ stellaris_enet_write,
+ stellaris_enet_write,
+ stellaris_enet_write
+};
+static void stellaris_enet_reset(stellaris_enet_state *s)
+{
+ s->mdv = 0x80;
+ s->rctl = SE_RCTL_BADCRC;
+ s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP
+ | SE_INT_TXER | SE_INT_RX;
+ s->thr = 0x3f;
+ s->tx_frame_len = -1;
+}
+
+static void stellaris_enet_save(QEMUFile *f, void *opaque)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->ris);
+ qemu_put_be32(f, s->im);
+ qemu_put_be32(f, s->rctl);
+ qemu_put_be32(f, s->tctl);
+ qemu_put_be32(f, s->thr);
+ qemu_put_be32(f, s->mctl);
+ qemu_put_be32(f, s->mdv);
+ qemu_put_be32(f, s->mtxd);
+ qemu_put_be32(f, s->mrxd);
+ qemu_put_be32(f, s->np);
+ qemu_put_be32(f, s->tx_frame_len);
+ qemu_put_be32(f, s->tx_fifo_len);
+ qemu_put_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
+ for (i = 0; i < 31; i++) {
+ qemu_put_be32(f, s->rx[i].len);
+ qemu_put_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
+
+ }
+ qemu_put_be32(f, s->next_packet);
+ qemu_put_be32(f, s->rx_fifo - s->rx[s->next_packet].data);
+ qemu_put_be32(f, s->rx_fifo_len);
+}
+
+static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id)
+{
+ stellaris_enet_state *s = (stellaris_enet_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->ris = qemu_get_be32(f);
+ s->im = qemu_get_be32(f);
+ s->rctl = qemu_get_be32(f);
+ s->tctl = qemu_get_be32(f);
+ s->thr = qemu_get_be32(f);
+ s->mctl = qemu_get_be32(f);
+ s->mdv = qemu_get_be32(f);
+ s->mtxd = qemu_get_be32(f);
+ s->mrxd = qemu_get_be32(f);
+ s->np = qemu_get_be32(f);
+ s->tx_frame_len = qemu_get_be32(f);
+ s->tx_fifo_len = qemu_get_be32(f);
+ qemu_get_buffer(f, s->tx_fifo, sizeof(s->tx_fifo));
+ for (i = 0; i < 31; i++) {
+ s->rx[i].len = qemu_get_be32(f);
+ qemu_get_buffer(f, s->rx[i].data, sizeof(s->rx[i].data));
+
+ }
+ s->next_packet = qemu_get_be32(f);
+ s->rx_fifo = s->rx[s->next_packet].data + qemu_get_be32(f);
+ s->rx_fifo_len = qemu_get_be32(f);
+
+ return 0;
+}
+
+static void stellaris_enet_cleanup(VLANClientState *nc)
+{
+ stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ unregister_savevm(&s->busdev.qdev, "stellaris_enet", s);
+
+ cpu_unregister_io_memory(s->mmio_index);
+
+ qemu_free(s);
+}
+
+static NetClientInfo net_stellaris_enet_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = stellaris_enet_can_receive,
+ .receive = stellaris_enet_receive,
+ .cleanup = stellaris_enet_cleanup,
+};
+
+static int stellaris_enet_init(SysBusDevice *dev)
+{
+ stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev);
+
+ s->mmio_index = cpu_register_io_memory(stellaris_enet_readfn,
+ stellaris_enet_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, s->mmio_index);
+ sysbus_init_irq(dev, &s->irq);
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+ s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+
+ stellaris_enet_reset(s);
+ register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1,
+ stellaris_enet_save, stellaris_enet_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo stellaris_enet_info = {
+ .init = stellaris_enet_init,
+ .qdev.name = "stellaris_enet",
+ .qdev.size = sizeof(stellaris_enet_state),
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void stellaris_enet_register_devices(void)
+{
+ sysbus_register_withprop(&stellaris_enet_info);
+}
+
+device_init(stellaris_enet_register_devices)
diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c
new file mode 100644
index 0000000..16aae96
--- /dev/null
+++ b/hw/stellaris_input.c
@@ -0,0 +1,91 @@
+/*
+ * Gamepad style buttons connected to IRQ/GPIO lines
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+#include "hw.h"
+#include "devices.h"
+#include "console.h"
+
+typedef struct {
+ qemu_irq irq;
+ int keycode;
+ int pressed;
+} gamepad_button;
+
+typedef struct {
+ gamepad_button *buttons;
+ int num_buttons;
+ int extension;
+} gamepad_state;
+
+static void stellaris_gamepad_put_key(void * opaque, int keycode)
+{
+ gamepad_state *s = (gamepad_state *)opaque;
+ int i;
+ int down;
+
+ if (keycode == 0xe0 && !s->extension) {
+ s->extension = 0x80;
+ return;
+ }
+
+ down = (keycode & 0x80) == 0;
+ keycode = (keycode & 0x7f) | s->extension;
+
+ for (i = 0; i < s->num_buttons; i++) {
+ if (s->buttons[i].keycode == keycode
+ && s->buttons[i].pressed != down) {
+ s->buttons[i].pressed = down;
+ qemu_set_irq(s->buttons[i].irq, down);
+ }
+ }
+
+ s->extension = 0;
+}
+
+static void stellaris_gamepad_save(QEMUFile *f, void *opaque)
+{
+ gamepad_state *s = (gamepad_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->extension);
+ for (i = 0; i < s->num_buttons; i++)
+ qemu_put_byte(f, s->buttons[i].pressed);
+}
+
+static int stellaris_gamepad_load(QEMUFile *f, void *opaque, int version_id)
+{
+ gamepad_state *s = (gamepad_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->extension = qemu_get_be32(f);
+ for (i = 0; i < s->num_buttons; i++)
+ s->buttons[i].pressed = qemu_get_byte(f);
+
+ return 0;
+}
+
+/* Returns an array 5 ouput slots. */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
+{
+ gamepad_state *s;
+ int i;
+
+ s = (gamepad_state *)qemu_mallocz(sizeof (gamepad_state));
+ s->buttons = (gamepad_button *)qemu_mallocz(n * sizeof (gamepad_button));
+ for (i = 0; i < n; i++) {
+ s->buttons[i].irq = irq[i];
+ s->buttons[i].keycode = keycode[i];
+ }
+ s->num_buttons = n;
+ qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
+ register_savevm(NULL, "stellaris_gamepad", -1, 1,
+ stellaris_gamepad_save, stellaris_gamepad_load, s);
+}
diff --git a/hw/sun4c_intctl.c b/hw/sun4c_intctl.c
new file mode 100644
index 0000000..5c7fdef
--- /dev/null
+++ b/hw/sun4c_intctl.c
@@ -0,0 +1,224 @@
+/*
+ * QEMU Sparc Sun4c interrupt controller emulation
+ *
+ * Based on slavio_intctl, copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "sun4m.h"
+#include "monitor.h"
+#include "sysbus.h"
+
+//#define DEBUG_IRQ_COUNT
+//#define DEBUG_IRQ
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, ...) \
+ do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/*
+ * Registers of interrupt controller in sun4c.
+ *
+ */
+
+#define MAX_PILS 16
+
+typedef struct Sun4c_INTCTLState {
+ SysBusDevice busdev;
+#ifdef DEBUG_IRQ_COUNT
+ uint64_t irq_count;
+#endif
+ qemu_irq cpu_irqs[MAX_PILS];
+ const uint32_t *intbit_to_level;
+ uint32_t pil_out;
+ uint8_t reg;
+ uint8_t pending;
+} Sun4c_INTCTLState;
+
+#define INTCTL_SIZE 1
+
+static void sun4c_check_interrupts(void *opaque);
+
+static uint32_t sun4c_intctl_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ Sun4c_INTCTLState *s = opaque;
+ uint32_t ret;
+
+ ret = s->reg;
+ DPRINTF("read reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
+
+ return ret;
+}
+
+static void sun4c_intctl_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ Sun4c_INTCTLState *s = opaque;
+
+ DPRINTF("write reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
+ val &= 0xbf;
+ s->reg = val;
+ sun4c_check_interrupts(s);
+}
+
+static CPUReadMemoryFunc * const sun4c_intctl_mem_read[3] = {
+ sun4c_intctl_mem_readb,
+ NULL,
+ NULL,
+};
+
+static CPUWriteMemoryFunc * const sun4c_intctl_mem_write[3] = {
+ sun4c_intctl_mem_writeb,
+ NULL,
+ NULL,
+};
+
+void sun4c_pic_info(Monitor *mon, void *opaque)
+{
+ Sun4c_INTCTLState *s = opaque;
+
+ monitor_printf(mon, "master: pending 0x%2.2x, enabled 0x%2.2x\n",
+ s->pending, s->reg);
+}
+
+void sun4c_irq_info(Monitor *mon, void *opaque)
+{
+#ifndef DEBUG_IRQ_COUNT
+ monitor_printf(mon, "irq statistic code not compiled.\n");
+#else
+ Sun4c_INTCTLState *s = opaque;
+ int64_t count;
+
+ monitor_printf(mon, "IRQ statistics:\n");
+ count = s->irq_count;
+ if (count > 0)
+ monitor_printf(mon, " %" PRId64 "\n", count);
+#endif
+}
+
+static const uint32_t intbit_to_level[] = { 0, 1, 4, 6, 8, 10, 0, 14, };
+
+static void sun4c_check_interrupts(void *opaque)
+{
+ Sun4c_INTCTLState *s = opaque;
+ uint32_t pil_pending;
+ unsigned int i;
+
+ pil_pending = 0;
+ if (s->pending && !(s->reg & 0x80000000)) {
+ for (i = 0; i < 8; i++) {
+ if (s->pending & (1 << i))
+ pil_pending |= 1 << intbit_to_level[i];
+ }
+ }
+
+ for (i = 0; i < MAX_PILS; i++) {
+ if (pil_pending & (1 << i)) {
+ if (!(s->pil_out & (1 << i)))
+ qemu_irq_raise(s->cpu_irqs[i]);
+ } else {
+ if (s->pil_out & (1 << i))
+ qemu_irq_lower(s->cpu_irqs[i]);
+ }
+ }
+ s->pil_out = pil_pending;
+}
+
+/*
+ * "irq" here is the bit number in the system interrupt register
+ */
+static void sun4c_set_irq(void *opaque, int irq, int level)
+{
+ Sun4c_INTCTLState *s = opaque;
+ uint32_t mask = 1 << irq;
+ uint32_t pil = intbit_to_level[irq];
+
+ DPRINTF("Set irq %d -> pil %d level %d\n", irq, pil,
+ level);
+ if (pil > 0) {
+ if (level) {
+#ifdef DEBUG_IRQ_COUNT
+ s->irq_count++;
+#endif
+ s->pending |= mask;
+ } else {
+ s->pending &= ~mask;
+ }
+ sun4c_check_interrupts(s);
+ }
+}
+
+static const VMStateDescription vmstate_sun4c_intctl = {
+ .name ="sun4c_intctl",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(reg, Sun4c_INTCTLState),
+ VMSTATE_UINT8(pending, Sun4c_INTCTLState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void sun4c_intctl_reset(DeviceState *d)
+{
+ Sun4c_INTCTLState *s = container_of(d, Sun4c_INTCTLState, busdev.qdev);
+
+ s->reg = 1;
+ s->pending = 0;
+}
+
+static int sun4c_intctl_init1(SysBusDevice *dev)
+{
+ Sun4c_INTCTLState *s = FROM_SYSBUS(Sun4c_INTCTLState, dev);
+ int io_memory;
+ unsigned int i;
+
+ io_memory = cpu_register_io_memory(sun4c_intctl_mem_read,
+ sun4c_intctl_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, INTCTL_SIZE, io_memory);
+ qdev_init_gpio_in(&dev->qdev, sun4c_set_irq, 8);
+
+ for (i = 0; i < MAX_PILS; i++) {
+ sysbus_init_irq(dev, &s->cpu_irqs[i]);
+ }
+
+ return 0;
+}
+
+static SysBusDeviceInfo sun4c_intctl_info = {
+ .init = sun4c_intctl_init1,
+ .qdev.name = "sun4c_intctl",
+ .qdev.size = sizeof(Sun4c_INTCTLState),
+ .qdev.vmsd = &vmstate_sun4c_intctl,
+ .qdev.reset = sun4c_intctl_reset,
+};
+
+static void sun4c_intctl_register_devices(void)
+{
+ sysbus_register_withprop(&sun4c_intctl_info);
+}
+
+device_init(sun4c_intctl_register_devices)
diff --git a/hw/sun4m.c b/hw/sun4m.c
new file mode 100644
index 0000000..30e8a21
--- /dev/null
+++ b/hw/sun4m.c
@@ -0,0 +1,1821 @@
+/*
+ * QEMU Sun4m & Sun4d & Sun4c System Emulator
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "sun4m.h"
+#include "nvram.h"
+#include "sparc32_dma.h"
+#include "fdc.h"
+#include "sysemu.h"
+#include "net.h"
+#include "boards.h"
+#include "firmware_abi.h"
+#include "esp.h"
+#include "pc.h"
+#include "isa.h"
+#include "fw_cfg.h"
+#include "escc.h"
+#include "empty_slot.h"
+#include "qdev-addr.h"
+#include "loader.h"
+#include "elf.h"
+#include "blockdev.h"
+#include "trace.h"
+
+/*
+ * Sun4m architecture was used in the following machines:
+ *
+ * SPARCserver 6xxMP/xx
+ * SPARCclassic (SPARCclassic Server)(SPARCstation LC) (4/15),
+ * SPARCclassic X (4/10)
+ * SPARCstation LX/ZX (4/30)
+ * SPARCstation Voyager
+ * SPARCstation 10/xx, SPARCserver 10/xx
+ * SPARCstation 5, SPARCserver 5
+ * SPARCstation 20/xx, SPARCserver 20
+ * SPARCstation 4
+ *
+ * Sun4d architecture was used in the following machines:
+ *
+ * SPARCcenter 2000
+ * SPARCserver 1000
+ *
+ * Sun4c architecture was used in the following machines:
+ * SPARCstation 1/1+, SPARCserver 1/1+
+ * SPARCstation SLC
+ * SPARCstation IPC
+ * SPARCstation ELC
+ * SPARCstation IPX
+ *
+ * See for example: http://www.sunhelp.org/faq/sunref1.html
+ */
+
+#define KERNEL_LOAD_ADDR 0x00004000
+#define CMDLINE_ADDR 0x007ff000
+#define INITRD_LOAD_ADDR 0x00800000
+#define PROM_SIZE_MAX (1024 * 1024)
+#define PROM_VADDR 0xffd00000
+#define PROM_FILENAME "openbios-sparc32"
+#define CFG_ADDR 0xd00000510ULL
+#define FW_CFG_SUN4M_DEPTH (FW_CFG_ARCH_LOCAL + 0x00)
+
+#define MAX_CPUS 16
+#define MAX_PILS 16
+#define MAX_VSIMMS 4
+
+#define ESCC_CLOCK 4915200
+
+struct sun4m_hwdef {
+ target_phys_addr_t iommu_base, iommu_pad_base, iommu_pad_len, slavio_base;
+ target_phys_addr_t intctl_base, counter_base, nvram_base, ms_kb_base;
+ target_phys_addr_t serial_base, fd_base;
+ target_phys_addr_t afx_base, idreg_base, dma_base, esp_base, le_base;
+ target_phys_addr_t tcx_base, cs_base, apc_base, aux1_base, aux2_base;
+ target_phys_addr_t bpp_base, dbri_base, sx_base;
+ struct {
+ target_phys_addr_t reg_base, vram_base;
+ } vsimm[MAX_VSIMMS];
+ target_phys_addr_t ecc_base;
+ uint32_t ecc_version;
+ uint8_t nvram_machine_id;
+ uint16_t machine_id;
+ uint32_t iommu_version;
+ uint64_t max_mem;
+ const char * const default_cpu_model;
+};
+
+#define MAX_IOUNITS 5
+
+struct sun4d_hwdef {
+ target_phys_addr_t iounit_bases[MAX_IOUNITS], slavio_base;
+ target_phys_addr_t counter_base, nvram_base, ms_kb_base;
+ target_phys_addr_t serial_base;
+ target_phys_addr_t espdma_base, esp_base;
+ target_phys_addr_t ledma_base, le_base;
+ target_phys_addr_t tcx_base;
+ target_phys_addr_t sbi_base;
+ uint8_t nvram_machine_id;
+ uint16_t machine_id;
+ uint32_t iounit_version;
+ uint64_t max_mem;
+ const char * const default_cpu_model;
+};
+
+struct sun4c_hwdef {
+ target_phys_addr_t iommu_base, slavio_base;
+ target_phys_addr_t intctl_base, counter_base, nvram_base, ms_kb_base;
+ target_phys_addr_t serial_base, fd_base;
+ target_phys_addr_t idreg_base, dma_base, esp_base, le_base;
+ target_phys_addr_t tcx_base, aux1_base;
+ uint8_t nvram_machine_id;
+ uint16_t machine_id;
+ uint32_t iommu_version;
+ uint64_t max_mem;
+ const char * const default_cpu_model;
+};
+
+int DMA_get_channel_mode (int nchan)
+{
+ return 0;
+}
+int DMA_read_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+int DMA_write_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+void DMA_hold_DREQ (int nchan) {}
+void DMA_release_DREQ (int nchan) {}
+void DMA_schedule(int nchan) {}
+
+void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+{
+}
+
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+}
+
+static int fw_cfg_boot_set(void *opaque, const char *boot_device)
+{
+ fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ return 0;
+}
+
+static void nvram_init(M48t59State *nvram, uint8_t *macaddr,
+ const char *cmdline, const char *boot_devices,
+ ram_addr_t RAM_size, uint32_t kernel_size,
+ int width, int height, int depth,
+ int nvram_machine_id, const char *arch)
+{
+ unsigned int i;
+ uint32_t start, end;
+ uint8_t image[0x1ff0];
+ struct OpenBIOS_nvpart_v1 *part_header;
+
+ memset(image, '\0', sizeof(image));
+
+ start = 0;
+
+ // OpenBIOS nvram variables
+ // Variable partition
+ part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
+ part_header->signature = OPENBIOS_PART_SYSTEM;
+ pstrcpy(part_header->name, sizeof(part_header->name), "system");
+
+ end = start + sizeof(struct OpenBIOS_nvpart_v1);
+ for (i = 0; i < nb_prom_envs; i++)
+ end = OpenBIOS_set_var(image, end, prom_envs[i]);
+
+ // End marker
+ image[end++] = '\0';
+
+ end = start + ((end - start + 15) & ~15);
+ OpenBIOS_finish_partition(part_header, end - start);
+
+ // free partition
+ start = end;
+ part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
+ part_header->signature = OPENBIOS_PART_FREE;
+ pstrcpy(part_header->name, sizeof(part_header->name), "free");
+
+ end = 0x1fd0;
+ OpenBIOS_finish_partition(part_header, end - start);
+
+ Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr,
+ nvram_machine_id);
+
+ for (i = 0; i < sizeof(image); i++)
+ m48t59_write(nvram, i, image[i]);
+}
+
+static DeviceState *slavio_intctl;
+
+void pic_info(Monitor *mon)
+{
+ if (slavio_intctl)
+ slavio_pic_info(mon, slavio_intctl);
+}
+
+void irq_info(Monitor *mon)
+{
+ if (slavio_intctl)
+ slavio_irq_info(mon, slavio_intctl);
+}
+
+void cpu_check_irqs(CPUState *env)
+{
+ if (env->pil_in && (env->interrupt_index == 0 ||
+ (env->interrupt_index & ~15) == TT_EXTINT)) {
+ unsigned int i;
+
+ for (i = 15; i > 0; i--) {
+ if (env->pil_in & (1 << i)) {
+ int old_interrupt = env->interrupt_index;
+
+ env->interrupt_index = TT_EXTINT | i;
+ if (old_interrupt != env->interrupt_index) {
+ trace_sun4m_cpu_interrupt(i);
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ break;
+ }
+ }
+ } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) {
+ trace_sun4m_cpu_reset_interrupt(env->interrupt_index & 15);
+ env->interrupt_index = 0;
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+}
+
+static void cpu_set_irq(void *opaque, int irq, int level)
+{
+ CPUState *env = opaque;
+
+ if (level) {
+ trace_sun4m_cpu_set_irq_raise(irq);
+ env->halted = 0;
+ env->pil_in |= 1 << irq;
+ cpu_check_irqs(env);
+ } else {
+ trace_sun4m_cpu_set_irq_lower(irq);
+ env->pil_in &= ~(1 << irq);
+ cpu_check_irqs(env);
+ }
+}
+
+static void dummy_cpu_set_irq(void *opaque, int irq, int level)
+{
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ env->halted = 0;
+}
+
+static void secondary_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+ env->halted = 1;
+}
+
+static void cpu_halt_signal(void *opaque, int irq, int level)
+{
+ if (level && cpu_single_env)
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return addr - 0xf0000000ULL;
+}
+
+static unsigned long sun4m_load_kernel(const char *kernel_filename,
+ const char *initrd_filename,
+ ram_addr_t RAM_size)
+{
+ int linux_boot;
+ unsigned int i;
+ long initrd_size, kernel_size;
+ uint8_t *ptr;
+
+ linux_boot = (kernel_filename != NULL);
+
+ kernel_size = 0;
+ if (linux_boot) {
+ int bswap_needed;
+
+#ifdef BSWAP_NEEDED
+ bswap_needed = 1;
+#else
+ bswap_needed = 0;
+#endif
+ kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+ NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+ if (kernel_size < 0)
+ kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR,
+ RAM_size - KERNEL_LOAD_ADDR, bswap_needed,
+ TARGET_PAGE_SIZE);
+ if (kernel_size < 0)
+ kernel_size = load_image_targphys(kernel_filename,
+ KERNEL_LOAD_ADDR,
+ RAM_size - KERNEL_LOAD_ADDR);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ if (initrd_filename) {
+ initrd_size = load_image_targphys(initrd_filename,
+ INITRD_LOAD_ADDR,
+ RAM_size - INITRD_LOAD_ADDR);
+ if (initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+ if (initrd_size > 0) {
+ for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) {
+ ptr = rom_ptr(KERNEL_LOAD_ADDR + i);
+ if (ldl_p(ptr) == 0x48647253) { // HdrS
+ stl_p(ptr + 16, INITRD_LOAD_ADDR);
+ stl_p(ptr + 20, initrd_size);
+ break;
+ }
+ }
+ }
+ }
+ return kernel_size;
+}
+
+static void *iommu_init(target_phys_addr_t addr, uint32_t version, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "iommu");
+ qdev_prop_set_uint32(dev, "version", version);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irq);
+ sysbus_mmio_map(s, 0, addr);
+
+ return s;
+}
+
+static void *sparc32_dma_init(target_phys_addr_t daddr, qemu_irq parent_irq,
+ void *iommu, qemu_irq *dev_irq, int is_ledma)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "sparc32_dma");
+ qdev_prop_set_ptr(dev, "iommu_opaque", iommu);
+ qdev_prop_set_uint32(dev, "is_ledma", is_ledma);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, parent_irq);
+ *dev_irq = qdev_get_gpio_in(dev, 0);
+ sysbus_mmio_map(s, 0, daddr);
+
+ return s;
+}
+
+static void lance_init(NICInfo *nd, target_phys_addr_t leaddr,
+ void *dma_opaque, qemu_irq irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ qemu_irq reset;
+
+ qemu_check_nic_model(&nd_table[0], "lance");
+
+ dev = qdev_create(NULL, "lance");
+ qdev_set_nic_properties(dev, nd);
+ qdev_prop_set_ptr(dev, "dma", dma_opaque);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, leaddr);
+ sysbus_connect_irq(s, 0, irq);
+ reset = qdev_get_gpio_in(dev, 0);
+ qdev_connect_gpio_out(dma_opaque, 0, reset);
+}
+
+static DeviceState *slavio_intctl_init(target_phys_addr_t addr,
+ target_phys_addr_t addrg,
+ qemu_irq **parent_irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ unsigned int i, j;
+
+ dev = qdev_create(NULL, "slavio_intctl");
+ qdev_init_nofail(dev);
+
+ s = sysbus_from_qdev(dev);
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ for (j = 0; j < MAX_PILS; j++) {
+ sysbus_connect_irq(s, i * MAX_PILS + j, parent_irq[i][j]);
+ }
+ }
+ sysbus_mmio_map(s, 0, addrg);
+ for (i = 0; i < MAX_CPUS; i++) {
+ sysbus_mmio_map(s, i + 1, addr + i * TARGET_PAGE_SIZE);
+ }
+
+ return dev;
+}
+
+#define SYS_TIMER_OFFSET 0x10000ULL
+#define CPU_TIMER_OFFSET(cpu) (0x1000ULL * cpu)
+
+static void slavio_timer_init_all(target_phys_addr_t addr, qemu_irq master_irq,
+ qemu_irq *cpu_irqs, unsigned int num_cpus)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ unsigned int i;
+
+ dev = qdev_create(NULL, "slavio_timer");
+ qdev_prop_set_uint32(dev, "num_cpus", num_cpus);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, master_irq);
+ sysbus_mmio_map(s, 0, addr + SYS_TIMER_OFFSET);
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ sysbus_mmio_map(s, i + 1, addr + (target_phys_addr_t)CPU_TIMER_OFFSET(i));
+ sysbus_connect_irq(s, i + 1, cpu_irqs[i]);
+ }
+}
+
+#define MISC_LEDS 0x01600000
+#define MISC_CFG 0x01800000
+#define MISC_DIAG 0x01a00000
+#define MISC_MDM 0x01b00000
+#define MISC_SYS 0x01f00000
+
+static void slavio_misc_init(target_phys_addr_t base,
+ target_phys_addr_t aux1_base,
+ target_phys_addr_t aux2_base, qemu_irq irq,
+ qemu_irq fdc_tc)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "slavio_misc");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ if (base) {
+ /* 8 bit registers */
+ /* Slavio control */
+ sysbus_mmio_map(s, 0, base + MISC_CFG);
+ /* Diagnostics */
+ sysbus_mmio_map(s, 1, base + MISC_DIAG);
+ /* Modem control */
+ sysbus_mmio_map(s, 2, base + MISC_MDM);
+ /* 16 bit registers */
+ /* ss600mp diag LEDs */
+ sysbus_mmio_map(s, 3, base + MISC_LEDS);
+ /* 32 bit registers */
+ /* System control */
+ sysbus_mmio_map(s, 4, base + MISC_SYS);
+ }
+ if (aux1_base) {
+ /* AUX 1 (Misc System Functions) */
+ sysbus_mmio_map(s, 5, aux1_base);
+ }
+ if (aux2_base) {
+ /* AUX 2 (Software Powerdown Control) */
+ sysbus_mmio_map(s, 6, aux2_base);
+ }
+ sysbus_connect_irq(s, 0, irq);
+ sysbus_connect_irq(s, 1, fdc_tc);
+ qemu_system_powerdown = qdev_get_gpio_in(dev, 0);
+}
+
+static void ecc_init(target_phys_addr_t base, qemu_irq irq, uint32_t version)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "eccmemctl");
+ qdev_prop_set_uint32(dev, "version", version);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, irq);
+ sysbus_mmio_map(s, 0, base);
+ if (version == 0) { // SS-600MP only
+ sysbus_mmio_map(s, 1, base + 0x1000);
+ }
+}
+
+static void apc_init(target_phys_addr_t power_base, qemu_irq cpu_halt)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "apc");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ /* Power management (APC) XXX: not a Slavio device */
+ sysbus_mmio_map(s, 0, power_base);
+ sysbus_connect_irq(s, 0, cpu_halt);
+}
+
+static void tcx_init(target_phys_addr_t addr, int vram_size, int width,
+ int height, int depth)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "SUNW,tcx");
+ qdev_prop_set_taddr(dev, "addr", addr);
+ qdev_prop_set_uint32(dev, "vram_size", vram_size);
+ qdev_prop_set_uint16(dev, "width", width);
+ qdev_prop_set_uint16(dev, "height", height);
+ qdev_prop_set_uint16(dev, "depth", depth);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ /* 8-bit plane */
+ sysbus_mmio_map(s, 0, addr + 0x00800000ULL);
+ /* DAC */
+ sysbus_mmio_map(s, 1, addr + 0x00200000ULL);
+ /* TEC (dummy) */
+ sysbus_mmio_map(s, 2, addr + 0x00700000ULL);
+ /* THC 24 bit: NetBSD writes here even with 8-bit display: dummy */
+ sysbus_mmio_map(s, 3, addr + 0x00301000ULL);
+ if (depth == 24) {
+ /* 24-bit plane */
+ sysbus_mmio_map(s, 4, addr + 0x02000000ULL);
+ /* Control plane */
+ sysbus_mmio_map(s, 5, addr + 0x0a000000ULL);
+ } else {
+ /* THC 8 bit (dummy) */
+ sysbus_mmio_map(s, 4, addr + 0x00300000ULL);
+ }
+}
+
+/* NCR89C100/MACIO Internal ID register */
+static const uint8_t idreg_data[] = { 0xfe, 0x81, 0x01, 0x03 };
+
+static void idreg_init(target_phys_addr_t addr)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "macio_idreg");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+ cpu_physical_memory_write_rom(addr, idreg_data, sizeof(idreg_data));
+}
+
+static int idreg_init1(SysBusDevice *dev)
+{
+ ram_addr_t idreg_offset;
+
+ idreg_offset = qemu_ram_alloc(NULL, "sun4m.idreg", sizeof(idreg_data));
+ sysbus_init_mmio(dev, sizeof(idreg_data), idreg_offset | IO_MEM_ROM);
+ return 0;
+}
+
+static SysBusDeviceInfo idreg_info = {
+ .init = idreg_init1,
+ .qdev.name = "macio_idreg",
+ .qdev.size = sizeof(SysBusDevice),
+};
+
+static void idreg_register_devices(void)
+{
+ sysbus_register_withprop(&idreg_info);
+}
+
+device_init(idreg_register_devices);
+
+/* SS-5 TCX AFX register */
+static void afx_init(target_phys_addr_t addr)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, "tcx_afx");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+}
+
+static int afx_init1(SysBusDevice *dev)
+{
+ ram_addr_t afx_offset;
+
+ afx_offset = qemu_ram_alloc(NULL, "sun4m.afx", 4);
+ sysbus_init_mmio(dev, 4, afx_offset | IO_MEM_RAM);
+ return 0;
+}
+
+static SysBusDeviceInfo afx_info = {
+ .init = afx_init1,
+ .qdev.name = "tcx_afx",
+ .qdev.size = sizeof(SysBusDevice),
+};
+
+static void afx_register_devices(void)
+{
+ sysbus_register_withprop(&afx_info);
+}
+
+device_init(afx_register_devices);
+
+/* Boot PROM (OpenBIOS) */
+static uint64_t translate_prom_address(void *opaque, uint64_t addr)
+{
+ target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque;
+ return addr + *base_addr - PROM_VADDR;
+}
+
+static void prom_init(target_phys_addr_t addr, const char *bios_name)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ char *filename;
+ int ret;
+
+ dev = qdev_create(NULL, "openprom");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+
+ /* load boot prom */
+ if (bios_name == NULL) {
+ bios_name = PROM_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ ret = load_elf(filename, translate_prom_address, &addr, NULL,
+ NULL, NULL, 1, ELF_MACHINE, 0);
+ if (ret < 0 || ret > PROM_SIZE_MAX) {
+ ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
+ }
+ qemu_free(filename);
+ } else {
+ ret = -1;
+ }
+ if (ret < 0 || ret > PROM_SIZE_MAX) {
+ fprintf(stderr, "qemu: could not load prom '%s'\n", bios_name);
+ exit(1);
+ }
+}
+
+static int prom_init1(SysBusDevice *dev)
+{
+ ram_addr_t prom_offset;
+
+ prom_offset = qemu_ram_alloc(NULL, "sun4m.prom", PROM_SIZE_MAX);
+ sysbus_init_mmio(dev, PROM_SIZE_MAX, prom_offset | IO_MEM_ROM);
+ return 0;
+}
+
+static SysBusDeviceInfo prom_info = {
+ .init = prom_init1,
+ .qdev.name = "openprom",
+ .qdev.size = sizeof(SysBusDevice),
+ .qdev.props = (Property[]) {
+ {/* end of property list */}
+ }
+};
+
+static void prom_register_devices(void)
+{
+ sysbus_register_withprop(&prom_info);
+}
+
+device_init(prom_register_devices);
+
+typedef struct RamDevice
+{
+ SysBusDevice busdev;
+ uint64_t size;
+} RamDevice;
+
+/* System RAM */
+static int ram_init1(SysBusDevice *dev)
+{
+ ram_addr_t RAM_size, ram_offset;
+ RamDevice *d = FROM_SYSBUS(RamDevice, dev);
+
+ RAM_size = d->size;
+
+ ram_offset = qemu_ram_alloc(NULL, "sun4m.ram", RAM_size);
+ sysbus_init_mmio(dev, RAM_size, ram_offset);
+ return 0;
+}
+
+static void ram_init(target_phys_addr_t addr, ram_addr_t RAM_size,
+ uint64_t max_mem)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ RamDevice *d;
+
+ /* allocate RAM */
+ if ((uint64_t)RAM_size > max_mem) {
+ fprintf(stderr,
+ "qemu: Too much memory for this machine: %d, maximum %d\n",
+ (unsigned int)(RAM_size / (1024 * 1024)),
+ (unsigned int)(max_mem / (1024 * 1024)));
+ exit(1);
+ }
+ dev = qdev_create(NULL, "memory");
+ s = sysbus_from_qdev(dev);
+
+ d = FROM_SYSBUS(RamDevice, s);
+ d->size = RAM_size;
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+}
+
+static SysBusDeviceInfo ram_info = {
+ .init = ram_init1,
+ .qdev.name = "memory",
+ .qdev.size = sizeof(RamDevice),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT64("size", RamDevice, size, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ram_register_devices(void)
+{
+ sysbus_register_withprop(&ram_info);
+}
+
+device_init(ram_register_devices);
+
+static void cpu_devinit(const char *cpu_model, unsigned int id,
+ uint64_t prom_addr, qemu_irq **cpu_irqs)
+{
+ CPUState *env;
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n");
+ exit(1);
+ }
+
+ cpu_sparc_set_id(env, id);
+ if (id == 0) {
+ qemu_register_reset(main_cpu_reset, env);
+ } else {
+ qemu_register_reset(secondary_cpu_reset, env);
+ env->halted = 1;
+ }
+ *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
+ env->prom_addr = prom_addr;
+}
+
+static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ unsigned int i;
+ void *iommu, *espdma, *ledma, *nvram;
+ qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS],
+ espdma_irq, ledma_irq;
+ qemu_irq esp_reset, dma_enable;
+ qemu_irq fdc_tc;
+ qemu_irq *cpu_halt;
+ unsigned long kernel_size;
+ DriveInfo *fd[MAX_FD];
+ void *fw_cfg;
+ unsigned int num_vsimms;
+
+ /* init CPUs */
+ if (!cpu_model)
+ cpu_model = hwdef->default_cpu_model;
+
+ for(i = 0; i < smp_cpus; i++) {
+ cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]);
+ }
+
+ for (i = smp_cpus; i < MAX_CPUS; i++)
+ cpu_irqs[i] = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, MAX_PILS);
+
+
+ /* set up devices */
+ ram_init(0, RAM_size, hwdef->max_mem);
+ /* models without ECC don't trap when missing ram is accessed */
+ if (!hwdef->ecc_base) {
+ empty_slot_init(RAM_size, hwdef->max_mem - RAM_size);
+ }
+
+ prom_init(hwdef->slavio_base, bios_name);
+
+ slavio_intctl = slavio_intctl_init(hwdef->intctl_base,
+ hwdef->intctl_base + 0x10000ULL,
+ cpu_irqs);
+
+ for (i = 0; i < 32; i++) {
+ slavio_irq[i] = qdev_get_gpio_in(slavio_intctl, i);
+ }
+ for (i = 0; i < MAX_CPUS; i++) {
+ slavio_cpu_irq[i] = qdev_get_gpio_in(slavio_intctl, 32 + i);
+ }
+
+ if (hwdef->idreg_base) {
+ idreg_init(hwdef->idreg_base);
+ }
+
+ if (hwdef->afx_base) {
+ afx_init(hwdef->afx_base);
+ }
+
+ iommu = iommu_init(hwdef->iommu_base, hwdef->iommu_version,
+ slavio_irq[30]);
+
+ if (hwdef->iommu_pad_base) {
+ /* On the real hardware (SS-5, LX) the MMU is not padded, but aliased.
+ Software shouldn't use aliased addresses, neither should it crash
+ when does. Using empty_slot instead of aliasing can help with
+ debugging such accesses */
+ empty_slot_init(hwdef->iommu_pad_base,hwdef->iommu_pad_len);
+ }
+
+ espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[18],
+ iommu, &espdma_irq, 0);
+
+ ledma = sparc32_dma_init(hwdef->dma_base + 16ULL,
+ slavio_irq[16], iommu, &ledma_irq, 1);
+
+ if (graphic_depth != 8 && graphic_depth != 24) {
+ fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
+ exit (1);
+ }
+ num_vsimms = 0;
+ if (num_vsimms == 0) {
+ tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
+ graphic_depth);
+ }
+
+ for (i = num_vsimms; i < MAX_VSIMMS; i++) {
+ /* vsimm registers probed by OBP */
+ if (hwdef->vsimm[i].reg_base) {
+ empty_slot_init(hwdef->vsimm[i].reg_base, 0x2000);
+ }
+ }
+
+ if (hwdef->sx_base) {
+ empty_slot_init(hwdef->sx_base, 0x2000);
+ }
+
+ lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq);
+
+ nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x2000, 8);
+
+ slavio_timer_init_all(hwdef->counter_base, slavio_irq[19], slavio_cpu_irq, smp_cpus);
+
+ slavio_serial_ms_kbd_init(hwdef->ms_kb_base, slavio_irq[14],
+ display_type == DT_NOGRAPHIC, ESCC_CLOCK, 1);
+ // Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device
+ // Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device
+ escc_init(hwdef->serial_base, slavio_irq[15], slavio_irq[15],
+ serial_hds[0], serial_hds[1], ESCC_CLOCK, 1);
+
+ cpu_halt = qemu_allocate_irqs(cpu_halt_signal, NULL, 1);
+ slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base,
+ slavio_irq[30], fdc_tc);
+
+ if (hwdef->apc_base) {
+ apc_init(hwdef->apc_base, cpu_halt[0]);
+ }
+
+ if (hwdef->fd_base) {
+ /* there is zero or one floppy drive */
+ memset(fd, 0, sizeof(fd));
+ fd[0] = drive_get(IF_FLOPPY, 0, 0);
+ sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd,
+ &fdc_tc);
+ }
+
+ if (drive_get_max_bus(IF_SCSI) > 0) {
+ fprintf(stderr, "qemu: too many SCSI bus\n");
+ exit(1);
+ }
+
+ esp_init(hwdef->esp_base, 2,
+ espdma_memory_read, espdma_memory_write,
+ espdma, espdma_irq, &esp_reset, &dma_enable);
+
+ qdev_connect_gpio_out(espdma, 0, esp_reset);
+ qdev_connect_gpio_out(espdma, 1, dma_enable);
+
+ if (hwdef->cs_base) {
+ sysbus_create_simple("SUNW,CS4231", hwdef->cs_base,
+ slavio_irq[5]);
+ }
+
+ if (hwdef->dbri_base) {
+ /* ISDN chip with attached CS4215 audio codec */
+ /* prom space */
+ empty_slot_init(hwdef->dbri_base+0x1000, 0x30);
+ /* reg space */
+ empty_slot_init(hwdef->dbri_base+0x10000, 0x100);
+ }
+
+ if (hwdef->bpp_base) {
+ /* parallel port */
+ empty_slot_init(hwdef->bpp_base, 0x20);
+ }
+
+ kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
+ RAM_size);
+
+ nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline,
+ boot_device, RAM_size, kernel_size, graphic_width,
+ graphic_height, graphic_depth, hwdef->nvram_machine_id,
+ "Sun4m");
+
+ if (hwdef->ecc_base)
+ ecc_init(hwdef->ecc_base, slavio_irq[28],
+ hwdef->ecc_version);
+
+ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_DEPTH, graphic_depth);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ if (kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
+ (uint8_t*)strdup(kernel_cmdline),
+ strlen(kernel_cmdline) + 1);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(kernel_cmdline) + 1);
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+enum {
+ ss2_id = 0,
+ ss5_id = 32,
+ vger_id,
+ lx_id,
+ ss4_id,
+ scls_id,
+ sbook_id,
+ ss10_id = 64,
+ ss20_id,
+ ss600mp_id,
+ ss1000_id = 96,
+ ss2000_id,
+};
+
+static const struct sun4m_hwdef sun4m_hwdefs[] = {
+ /* SS-5 */
+ {
+ .iommu_base = 0x10000000,
+ .iommu_pad_base = 0x10004000,
+ .iommu_pad_len = 0x0fffb000,
+ .tcx_base = 0x50000000,
+ .cs_base = 0x6c000000,
+ .slavio_base = 0x70000000,
+ .ms_kb_base = 0x71000000,
+ .serial_base = 0x71100000,
+ .nvram_base = 0x71200000,
+ .fd_base = 0x71400000,
+ .counter_base = 0x71d00000,
+ .intctl_base = 0x71e00000,
+ .idreg_base = 0x78000000,
+ .dma_base = 0x78400000,
+ .esp_base = 0x78800000,
+ .le_base = 0x78c00000,
+ .apc_base = 0x6a000000,
+ .afx_base = 0x6e000000,
+ .aux1_base = 0x71900000,
+ .aux2_base = 0x71910000,
+ .nvram_machine_id = 0x80,
+ .machine_id = ss5_id,
+ .iommu_version = 0x05000000,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "Fujitsu MB86904",
+ },
+ /* SS-10 */
+ {
+ .iommu_base = 0xfe0000000ULL,
+ .tcx_base = 0xe20000000ULL,
+ .slavio_base = 0xff0000000ULL,
+ .ms_kb_base = 0xff1000000ULL,
+ .serial_base = 0xff1100000ULL,
+ .nvram_base = 0xff1200000ULL,
+ .fd_base = 0xff1700000ULL,
+ .counter_base = 0xff1300000ULL,
+ .intctl_base = 0xff1400000ULL,
+ .idreg_base = 0xef0000000ULL,
+ .dma_base = 0xef0400000ULL,
+ .esp_base = 0xef0800000ULL,
+ .le_base = 0xef0c00000ULL,
+ .apc_base = 0xefa000000ULL, // XXX should not exist
+ .aux1_base = 0xff1800000ULL,
+ .aux2_base = 0xff1a01000ULL,
+ .ecc_base = 0xf00000000ULL,
+ .ecc_version = 0x10000000, // version 0, implementation 1
+ .nvram_machine_id = 0x72,
+ .machine_id = ss10_id,
+ .iommu_version = 0x03000000,
+ .max_mem = 0xf00000000ULL,
+ .default_cpu_model = "TI SuperSparc II",
+ },
+ /* SS-600MP */
+ {
+ .iommu_base = 0xfe0000000ULL,
+ .tcx_base = 0xe20000000ULL,
+ .slavio_base = 0xff0000000ULL,
+ .ms_kb_base = 0xff1000000ULL,
+ .serial_base = 0xff1100000ULL,
+ .nvram_base = 0xff1200000ULL,
+ .counter_base = 0xff1300000ULL,
+ .intctl_base = 0xff1400000ULL,
+ .dma_base = 0xef0081000ULL,
+ .esp_base = 0xef0080000ULL,
+ .le_base = 0xef0060000ULL,
+ .apc_base = 0xefa000000ULL, // XXX should not exist
+ .aux1_base = 0xff1800000ULL,
+ .aux2_base = 0xff1a01000ULL, // XXX should not exist
+ .ecc_base = 0xf00000000ULL,
+ .ecc_version = 0x00000000, // version 0, implementation 0
+ .nvram_machine_id = 0x71,
+ .machine_id = ss600mp_id,
+ .iommu_version = 0x01000000,
+ .max_mem = 0xf00000000ULL,
+ .default_cpu_model = "TI SuperSparc II",
+ },
+ /* SS-20 */
+ {
+ .iommu_base = 0xfe0000000ULL,
+ .tcx_base = 0xe20000000ULL,
+ .slavio_base = 0xff0000000ULL,
+ .ms_kb_base = 0xff1000000ULL,
+ .serial_base = 0xff1100000ULL,
+ .nvram_base = 0xff1200000ULL,
+ .fd_base = 0xff1700000ULL,
+ .counter_base = 0xff1300000ULL,
+ .intctl_base = 0xff1400000ULL,
+ .idreg_base = 0xef0000000ULL,
+ .dma_base = 0xef0400000ULL,
+ .esp_base = 0xef0800000ULL,
+ .le_base = 0xef0c00000ULL,
+ .bpp_base = 0xef4800000ULL,
+ .apc_base = 0xefa000000ULL, // XXX should not exist
+ .aux1_base = 0xff1800000ULL,
+ .aux2_base = 0xff1a01000ULL,
+ .dbri_base = 0xee0000000ULL,
+ .sx_base = 0xf80000000ULL,
+ .vsimm = {
+ {
+ .reg_base = 0x9c000000ULL,
+ .vram_base = 0xfc000000ULL
+ }, {
+ .reg_base = 0x90000000ULL,
+ .vram_base = 0xf0000000ULL
+ }, {
+ .reg_base = 0x94000000ULL
+ }, {
+ .reg_base = 0x98000000ULL
+ }
+ },
+ .ecc_base = 0xf00000000ULL,
+ .ecc_version = 0x20000000, // version 0, implementation 2
+ .nvram_machine_id = 0x72,
+ .machine_id = ss20_id,
+ .iommu_version = 0x13000000,
+ .max_mem = 0xf00000000ULL,
+ .default_cpu_model = "TI SuperSparc II",
+ },
+ /* Voyager */
+ {
+ .iommu_base = 0x10000000,
+ .tcx_base = 0x50000000,
+ .slavio_base = 0x70000000,
+ .ms_kb_base = 0x71000000,
+ .serial_base = 0x71100000,
+ .nvram_base = 0x71200000,
+ .fd_base = 0x71400000,
+ .counter_base = 0x71d00000,
+ .intctl_base = 0x71e00000,
+ .idreg_base = 0x78000000,
+ .dma_base = 0x78400000,
+ .esp_base = 0x78800000,
+ .le_base = 0x78c00000,
+ .apc_base = 0x71300000, // pmc
+ .aux1_base = 0x71900000,
+ .aux2_base = 0x71910000,
+ .nvram_machine_id = 0x80,
+ .machine_id = vger_id,
+ .iommu_version = 0x05000000,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "Fujitsu MB86904",
+ },
+ /* LX */
+ {
+ .iommu_base = 0x10000000,
+ .iommu_pad_base = 0x10004000,
+ .iommu_pad_len = 0x0fffb000,
+ .tcx_base = 0x50000000,
+ .slavio_base = 0x70000000,
+ .ms_kb_base = 0x71000000,
+ .serial_base = 0x71100000,
+ .nvram_base = 0x71200000,
+ .fd_base = 0x71400000,
+ .counter_base = 0x71d00000,
+ .intctl_base = 0x71e00000,
+ .idreg_base = 0x78000000,
+ .dma_base = 0x78400000,
+ .esp_base = 0x78800000,
+ .le_base = 0x78c00000,
+ .aux1_base = 0x71900000,
+ .aux2_base = 0x71910000,
+ .nvram_machine_id = 0x80,
+ .machine_id = lx_id,
+ .iommu_version = 0x04000000,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "TI MicroSparc I",
+ },
+ /* SS-4 */
+ {
+ .iommu_base = 0x10000000,
+ .tcx_base = 0x50000000,
+ .cs_base = 0x6c000000,
+ .slavio_base = 0x70000000,
+ .ms_kb_base = 0x71000000,
+ .serial_base = 0x71100000,
+ .nvram_base = 0x71200000,
+ .fd_base = 0x71400000,
+ .counter_base = 0x71d00000,
+ .intctl_base = 0x71e00000,
+ .idreg_base = 0x78000000,
+ .dma_base = 0x78400000,
+ .esp_base = 0x78800000,
+ .le_base = 0x78c00000,
+ .apc_base = 0x6a000000,
+ .aux1_base = 0x71900000,
+ .aux2_base = 0x71910000,
+ .nvram_machine_id = 0x80,
+ .machine_id = ss4_id,
+ .iommu_version = 0x05000000,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "Fujitsu MB86904",
+ },
+ /* SPARCClassic */
+ {
+ .iommu_base = 0x10000000,
+ .tcx_base = 0x50000000,
+ .slavio_base = 0x70000000,
+ .ms_kb_base = 0x71000000,
+ .serial_base = 0x71100000,
+ .nvram_base = 0x71200000,
+ .fd_base = 0x71400000,
+ .counter_base = 0x71d00000,
+ .intctl_base = 0x71e00000,
+ .idreg_base = 0x78000000,
+ .dma_base = 0x78400000,
+ .esp_base = 0x78800000,
+ .le_base = 0x78c00000,
+ .apc_base = 0x6a000000,
+ .aux1_base = 0x71900000,
+ .aux2_base = 0x71910000,
+ .nvram_machine_id = 0x80,
+ .machine_id = scls_id,
+ .iommu_version = 0x05000000,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "TI MicroSparc I",
+ },
+ /* SPARCbook */
+ {
+ .iommu_base = 0x10000000,
+ .tcx_base = 0x50000000, // XXX
+ .slavio_base = 0x70000000,
+ .ms_kb_base = 0x71000000,
+ .serial_base = 0x71100000,
+ .nvram_base = 0x71200000,
+ .fd_base = 0x71400000,
+ .counter_base = 0x71d00000,
+ .intctl_base = 0x71e00000,
+ .idreg_base = 0x78000000,
+ .dma_base = 0x78400000,
+ .esp_base = 0x78800000,
+ .le_base = 0x78c00000,
+ .apc_base = 0x6a000000,
+ .aux1_base = 0x71900000,
+ .aux2_base = 0x71910000,
+ .nvram_machine_id = 0x80,
+ .machine_id = sbook_id,
+ .iommu_version = 0x05000000,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "TI MicroSparc I",
+ },
+};
+
+/* SPARCstation 5 hardware initialisation */
+static void ss5_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[0], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCstation 10 hardware initialisation */
+static void ss10_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[1], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCserver 600MP hardware initialisation */
+static void ss600mp_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[2], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCstation 20 hardware initialisation */
+static void ss20_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[3], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCstation Voyager hardware initialisation */
+static void vger_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[4], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCstation LX hardware initialisation */
+static void ss_lx_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[5], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCstation 4 hardware initialisation */
+static void ss4_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[6], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCClassic hardware initialisation */
+static void scls_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[7], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCbook hardware initialisation */
+static void sbook_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4m_hw_init(&sun4m_hwdefs[8], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+static QEMUMachine ss5_machine = {
+ .name = "SS-5",
+ .desc = "Sun4m platform, SPARCstation 5",
+ .init = ss5_init,
+ .use_scsi = 1,
+ .is_default = 1,
+};
+
+static QEMUMachine ss10_machine = {
+ .name = "SS-10",
+ .desc = "Sun4m platform, SPARCstation 10",
+ .init = ss10_init,
+ .use_scsi = 1,
+ .max_cpus = 4,
+};
+
+static QEMUMachine ss600mp_machine = {
+ .name = "SS-600MP",
+ .desc = "Sun4m platform, SPARCserver 600MP",
+ .init = ss600mp_init,
+ .use_scsi = 1,
+ .max_cpus = 4,
+};
+
+static QEMUMachine ss20_machine = {
+ .name = "SS-20",
+ .desc = "Sun4m platform, SPARCstation 20",
+ .init = ss20_init,
+ .use_scsi = 1,
+ .max_cpus = 4,
+};
+
+static QEMUMachine voyager_machine = {
+ .name = "Voyager",
+ .desc = "Sun4m platform, SPARCstation Voyager",
+ .init = vger_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine ss_lx_machine = {
+ .name = "LX",
+ .desc = "Sun4m platform, SPARCstation LX",
+ .init = ss_lx_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine ss4_machine = {
+ .name = "SS-4",
+ .desc = "Sun4m platform, SPARCstation 4",
+ .init = ss4_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine scls_machine = {
+ .name = "SPARCClassic",
+ .desc = "Sun4m platform, SPARCClassic",
+ .init = scls_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine sbook_machine = {
+ .name = "SPARCbook",
+ .desc = "Sun4m platform, SPARCbook",
+ .init = sbook_init,
+ .use_scsi = 1,
+};
+
+static const struct sun4d_hwdef sun4d_hwdefs[] = {
+ /* SS-1000 */
+ {
+ .iounit_bases = {
+ 0xfe0200000ULL,
+ 0xfe1200000ULL,
+ 0xfe2200000ULL,
+ 0xfe3200000ULL,
+ -1,
+ },
+ .tcx_base = 0x820000000ULL,
+ .slavio_base = 0xf00000000ULL,
+ .ms_kb_base = 0xf00240000ULL,
+ .serial_base = 0xf00200000ULL,
+ .nvram_base = 0xf00280000ULL,
+ .counter_base = 0xf00300000ULL,
+ .espdma_base = 0x800081000ULL,
+ .esp_base = 0x800080000ULL,
+ .ledma_base = 0x800040000ULL,
+ .le_base = 0x800060000ULL,
+ .sbi_base = 0xf02800000ULL,
+ .nvram_machine_id = 0x80,
+ .machine_id = ss1000_id,
+ .iounit_version = 0x03000000,
+ .max_mem = 0xf00000000ULL,
+ .default_cpu_model = "TI SuperSparc II",
+ },
+ /* SS-2000 */
+ {
+ .iounit_bases = {
+ 0xfe0200000ULL,
+ 0xfe1200000ULL,
+ 0xfe2200000ULL,
+ 0xfe3200000ULL,
+ 0xfe4200000ULL,
+ },
+ .tcx_base = 0x820000000ULL,
+ .slavio_base = 0xf00000000ULL,
+ .ms_kb_base = 0xf00240000ULL,
+ .serial_base = 0xf00200000ULL,
+ .nvram_base = 0xf00280000ULL,
+ .counter_base = 0xf00300000ULL,
+ .espdma_base = 0x800081000ULL,
+ .esp_base = 0x800080000ULL,
+ .ledma_base = 0x800040000ULL,
+ .le_base = 0x800060000ULL,
+ .sbi_base = 0xf02800000ULL,
+ .nvram_machine_id = 0x80,
+ .machine_id = ss2000_id,
+ .iounit_version = 0x03000000,
+ .max_mem = 0xf00000000ULL,
+ .default_cpu_model = "TI SuperSparc II",
+ },
+};
+
+static DeviceState *sbi_init(target_phys_addr_t addr, qemu_irq **parent_irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ unsigned int i;
+
+ dev = qdev_create(NULL, "sbi");
+ qdev_init_nofail(dev);
+
+ s = sysbus_from_qdev(dev);
+
+ for (i = 0; i < MAX_CPUS; i++) {
+ sysbus_connect_irq(s, i, *parent_irq[i]);
+ }
+
+ sysbus_mmio_map(s, 0, addr);
+
+ return dev;
+}
+
+static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ unsigned int i;
+ void *iounits[MAX_IOUNITS], *espdma, *ledma, *nvram;
+ qemu_irq *cpu_irqs[MAX_CPUS], sbi_irq[32], sbi_cpu_irq[MAX_CPUS],
+ espdma_irq, ledma_irq;
+ qemu_irq esp_reset, dma_enable;
+ unsigned long kernel_size;
+ void *fw_cfg;
+ DeviceState *dev;
+
+ /* init CPUs */
+ if (!cpu_model)
+ cpu_model = hwdef->default_cpu_model;
+
+ for(i = 0; i < smp_cpus; i++) {
+ cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]);
+ }
+
+ for (i = smp_cpus; i < MAX_CPUS; i++)
+ cpu_irqs[i] = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, MAX_PILS);
+
+ /* set up devices */
+ ram_init(0, RAM_size, hwdef->max_mem);
+
+ prom_init(hwdef->slavio_base, bios_name);
+
+ dev = sbi_init(hwdef->sbi_base, cpu_irqs);
+
+ for (i = 0; i < 32; i++) {
+ sbi_irq[i] = qdev_get_gpio_in(dev, i);
+ }
+ for (i = 0; i < MAX_CPUS; i++) {
+ sbi_cpu_irq[i] = qdev_get_gpio_in(dev, 32 + i);
+ }
+
+ for (i = 0; i < MAX_IOUNITS; i++)
+ if (hwdef->iounit_bases[i] != (target_phys_addr_t)-1)
+ iounits[i] = iommu_init(hwdef->iounit_bases[i],
+ hwdef->iounit_version,
+ sbi_irq[0]);
+
+ espdma = sparc32_dma_init(hwdef->espdma_base, sbi_irq[3],
+ iounits[0], &espdma_irq, 0);
+
+ /* should be lebuffer instead */
+ ledma = sparc32_dma_init(hwdef->ledma_base, sbi_irq[4],
+ iounits[0], &ledma_irq, 0);
+
+ if (graphic_depth != 8 && graphic_depth != 24) {
+ fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
+ exit (1);
+ }
+ tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
+ graphic_depth);
+
+ lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq);
+
+ nvram = m48t59_init(sbi_irq[0], hwdef->nvram_base, 0, 0x2000, 8);
+
+ slavio_timer_init_all(hwdef->counter_base, sbi_irq[10], sbi_cpu_irq, smp_cpus);
+
+ slavio_serial_ms_kbd_init(hwdef->ms_kb_base, sbi_irq[12],
+ display_type == DT_NOGRAPHIC, ESCC_CLOCK, 1);
+ // Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device
+ // Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device
+ escc_init(hwdef->serial_base, sbi_irq[12], sbi_irq[12],
+ serial_hds[0], serial_hds[1], ESCC_CLOCK, 1);
+
+ if (drive_get_max_bus(IF_SCSI) > 0) {
+ fprintf(stderr, "qemu: too many SCSI bus\n");
+ exit(1);
+ }
+
+ esp_init(hwdef->esp_base, 2,
+ espdma_memory_read, espdma_memory_write,
+ espdma, espdma_irq, &esp_reset, &dma_enable);
+
+ qdev_connect_gpio_out(espdma, 0, esp_reset);
+ qdev_connect_gpio_out(espdma, 1, dma_enable);
+
+ kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
+ RAM_size);
+
+ nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline,
+ boot_device, RAM_size, kernel_size, graphic_width,
+ graphic_height, graphic_depth, hwdef->nvram_machine_id,
+ "Sun4d");
+
+ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_DEPTH, graphic_depth);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ if (kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
+ (uint8_t*)strdup(kernel_cmdline),
+ strlen(kernel_cmdline) + 1);
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+/* SPARCserver 1000 hardware initialisation */
+static void ss1000_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4d_hw_init(&sun4d_hwdefs[0], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+/* SPARCcenter 2000 hardware initialisation */
+static void ss2000_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4d_hw_init(&sun4d_hwdefs[1], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+static QEMUMachine ss1000_machine = {
+ .name = "SS-1000",
+ .desc = "Sun4d platform, SPARCserver 1000",
+ .init = ss1000_init,
+ .use_scsi = 1,
+ .max_cpus = 8,
+};
+
+static QEMUMachine ss2000_machine = {
+ .name = "SS-2000",
+ .desc = "Sun4d platform, SPARCcenter 2000",
+ .init = ss2000_init,
+ .use_scsi = 1,
+ .max_cpus = 20,
+};
+
+static const struct sun4c_hwdef sun4c_hwdefs[] = {
+ /* SS-2 */
+ {
+ .iommu_base = 0xf8000000,
+ .tcx_base = 0xfe000000,
+ .slavio_base = 0xf6000000,
+ .intctl_base = 0xf5000000,
+ .counter_base = 0xf3000000,
+ .ms_kb_base = 0xf0000000,
+ .serial_base = 0xf1000000,
+ .nvram_base = 0xf2000000,
+ .fd_base = 0xf7200000,
+ .dma_base = 0xf8400000,
+ .esp_base = 0xf8800000,
+ .le_base = 0xf8c00000,
+ .aux1_base = 0xf7400003,
+ .nvram_machine_id = 0x55,
+ .machine_id = ss2_id,
+ .max_mem = 0x10000000,
+ .default_cpu_model = "Cypress CY7C601",
+ },
+};
+
+static DeviceState *sun4c_intctl_init(target_phys_addr_t addr,
+ qemu_irq *parent_irq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ unsigned int i;
+
+ dev = qdev_create(NULL, "sun4c_intctl");
+ qdev_init_nofail(dev);
+
+ s = sysbus_from_qdev(dev);
+
+ for (i = 0; i < MAX_PILS; i++) {
+ sysbus_connect_irq(s, i, parent_irq[i]);
+ }
+ sysbus_mmio_map(s, 0, addr);
+
+ return dev;
+}
+
+static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ void *iommu, *espdma, *ledma, *nvram;
+ qemu_irq *cpu_irqs, slavio_irq[8], espdma_irq, ledma_irq;
+ qemu_irq esp_reset, dma_enable;
+ qemu_irq fdc_tc;
+ unsigned long kernel_size;
+ DriveInfo *fd[MAX_FD];
+ void *fw_cfg;
+ DeviceState *dev;
+ unsigned int i;
+
+ /* init CPU */
+ if (!cpu_model)
+ cpu_model = hwdef->default_cpu_model;
+
+ cpu_devinit(cpu_model, 0, hwdef->slavio_base, &cpu_irqs);
+
+ /* set up devices */
+ ram_init(0, RAM_size, hwdef->max_mem);
+
+ prom_init(hwdef->slavio_base, bios_name);
+
+ dev = sun4c_intctl_init(hwdef->intctl_base, cpu_irqs);
+
+ for (i = 0; i < 8; i++) {
+ slavio_irq[i] = qdev_get_gpio_in(dev, i);
+ }
+
+ iommu = iommu_init(hwdef->iommu_base, hwdef->iommu_version,
+ slavio_irq[1]);
+
+ espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[2],
+ iommu, &espdma_irq, 0);
+
+ ledma = sparc32_dma_init(hwdef->dma_base + 16ULL,
+ slavio_irq[3], iommu, &ledma_irq, 1);
+
+ if (graphic_depth != 8 && graphic_depth != 24) {
+ fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
+ exit (1);
+ }
+ tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
+ graphic_depth);
+
+ lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq);
+
+ nvram = m48t59_init(slavio_irq[0], hwdef->nvram_base, 0, 0x800, 2);
+
+ slavio_serial_ms_kbd_init(hwdef->ms_kb_base, slavio_irq[1],
+ display_type == DT_NOGRAPHIC, ESCC_CLOCK, 1);
+ // Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device
+ // Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device
+ escc_init(hwdef->serial_base, slavio_irq[1],
+ slavio_irq[1], serial_hds[0], serial_hds[1],
+ ESCC_CLOCK, 1);
+
+ slavio_misc_init(0, hwdef->aux1_base, 0, slavio_irq[1], fdc_tc);
+
+ if (hwdef->fd_base != (target_phys_addr_t)-1) {
+ /* there is zero or one floppy drive */
+ memset(fd, 0, sizeof(fd));
+ fd[0] = drive_get(IF_FLOPPY, 0, 0);
+ sun4m_fdctrl_init(slavio_irq[1], hwdef->fd_base, fd,
+ &fdc_tc);
+ }
+
+ if (drive_get_max_bus(IF_SCSI) > 0) {
+ fprintf(stderr, "qemu: too many SCSI bus\n");
+ exit(1);
+ }
+
+ esp_init(hwdef->esp_base, 2,
+ espdma_memory_read, espdma_memory_write,
+ espdma, espdma_irq, &esp_reset, &dma_enable);
+
+ qdev_connect_gpio_out(espdma, 0, esp_reset);
+ qdev_connect_gpio_out(espdma, 1, dma_enable);
+
+ kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename,
+ RAM_size);
+
+ nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline,
+ boot_device, RAM_size, kernel_size, graphic_width,
+ graphic_height, graphic_depth, hwdef->nvram_machine_id,
+ "Sun4c");
+
+ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_SUN4M_DEPTH, graphic_depth);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ if (kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
+ (uint8_t*)strdup(kernel_cmdline),
+ strlen(kernel_cmdline) + 1);
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+/* SPARCstation 2 hardware initialisation */
+static void ss2_init(ram_addr_t RAM_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4c_hw_init(&sun4c_hwdefs[0], RAM_size, boot_device, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model);
+}
+
+static QEMUMachine ss2_machine = {
+ .name = "SS-2",
+ .desc = "Sun4c platform, SPARCstation 2",
+ .init = ss2_init,
+ .use_scsi = 1,
+};
+
+static void ss2_machine_init(void)
+{
+ qemu_register_machine(&ss5_machine);
+ qemu_register_machine(&ss10_machine);
+ qemu_register_machine(&ss600mp_machine);
+ qemu_register_machine(&ss20_machine);
+ qemu_register_machine(&voyager_machine);
+ qemu_register_machine(&ss_lx_machine);
+ qemu_register_machine(&ss4_machine);
+ qemu_register_machine(&scls_machine);
+ qemu_register_machine(&sbook_machine);
+ qemu_register_machine(&ss1000_machine);
+ qemu_register_machine(&ss2000_machine);
+ qemu_register_machine(&ss2_machine);
+}
+
+machine_init(ss2_machine_init);
diff --git a/hw/sun4m.h b/hw/sun4m.h
new file mode 100644
index 0000000..ce97ee5
--- /dev/null
+++ b/hw/sun4m.h
@@ -0,0 +1,36 @@
+#ifndef SUN4M_H
+#define SUN4M_H
+
+#include "qemu-common.h"
+
+/* Devices used by sparc32 system. */
+
+/* iommu.c */
+void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int is_write);
+static inline void sparc_iommu_memory_read(void *opaque,
+ target_phys_addr_t addr,
+ uint8_t *buf, int len)
+{
+ sparc_iommu_memory_rw(opaque, addr, buf, len, 0);
+}
+
+static inline void sparc_iommu_memory_write(void *opaque,
+ target_phys_addr_t addr,
+ uint8_t *buf, int len)
+{
+ sparc_iommu_memory_rw(opaque, addr, buf, len, 1);
+}
+
+/* slavio_intctl.c */
+void slavio_pic_info(Monitor *mon, DeviceState *dev);
+void slavio_irq_info(Monitor *mon, DeviceState *dev);
+
+/* sun4c_intctl.c */
+void sun4c_pic_info(Monitor *mon, void *opaque);
+void sun4c_irq_info(Monitor *mon, void *opaque);
+
+/* sparc32_dma.c */
+#include "sparc32_dma.h"
+
+#endif
diff --git a/hw/sun4m_iommu.c b/hw/sun4m_iommu.c
new file mode 100644
index 0000000..bba69ee
--- /dev/null
+++ b/hw/sun4m_iommu.c
@@ -0,0 +1,378 @@
+/*
+ * QEMU Sun4m iommu emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sun4m.h"
+#include "sysbus.h"
+#include "trace.h"
+
+/*
+ * I/O MMU used by Sun4m systems
+ *
+ * Chipset docs:
+ * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01,
+ * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf
+ */
+
+#define IOMMU_NREGS (4*4096/4)
+#define IOMMU_CTRL (0x0000 >> 2)
+#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */
+#define IOMMU_CTRL_VERS 0x0f000000 /* Version */
+#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */
+#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */
+#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */
+#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */
+#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */
+#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */
+#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */
+#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */
+#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */
+#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */
+#define IOMMU_CTRL_MASK 0x0000001d
+
+#define IOMMU_BASE (0x0004 >> 2)
+#define IOMMU_BASE_MASK 0x07fffc00
+
+#define IOMMU_TLBFLUSH (0x0014 >> 2)
+#define IOMMU_TLBFLUSH_MASK 0xffffffff
+
+#define IOMMU_PGFLUSH (0x0018 >> 2)
+#define IOMMU_PGFLUSH_MASK 0xffffffff
+
+#define IOMMU_AFSR (0x1000 >> 2)
+#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */
+#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after
+ transaction */
+#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than
+ 12.8 us. */
+#define IOMMU_AFSR_BE 0x10000000 /* Write access received error
+ acknowledge */
+#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */
+#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */
+#define IOMMU_AFSR_RESV 0x00800000 /* Reserved, forced to 0x8 by
+ hardware */
+#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */
+#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */
+#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */
+#define IOMMU_AFSR_MASK 0xff0fffff
+
+#define IOMMU_AFAR (0x1004 >> 2)
+
+#define IOMMU_AER (0x1008 >> 2) /* Arbiter Enable Register */
+#define IOMMU_AER_EN_P0_ARB 0x00000001 /* MBus master 0x8 (Always 1) */
+#define IOMMU_AER_EN_P1_ARB 0x00000002 /* MBus master 0x9 */
+#define IOMMU_AER_EN_P2_ARB 0x00000004 /* MBus master 0xa */
+#define IOMMU_AER_EN_P3_ARB 0x00000008 /* MBus master 0xb */
+#define IOMMU_AER_EN_0 0x00010000 /* SBus slot 0 */
+#define IOMMU_AER_EN_1 0x00020000 /* SBus slot 1 */
+#define IOMMU_AER_EN_2 0x00040000 /* SBus slot 2 */
+#define IOMMU_AER_EN_3 0x00080000 /* SBus slot 3 */
+#define IOMMU_AER_EN_F 0x00100000 /* SBus on-board */
+#define IOMMU_AER_SBW 0x80000000 /* S-to-M asynchronous writes */
+#define IOMMU_AER_MASK 0x801f000f
+
+#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */
+#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when
+ bypass enabled */
+#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */
+#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */
+#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses
+ produced by this device as pure
+ physical. */
+#define IOMMU_SBCFG_MASK 0x00010003
+
+#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */
+#define IOMMU_ARBEN_MASK 0x001f0000
+#define IOMMU_MID 0x00000008
+
+#define IOMMU_MASK_ID (0x3018 >> 2) /* Mask ID */
+#define IOMMU_MASK_ID_MASK 0x00ffffff
+
+#define IOMMU_MSII_MASK 0x26000000 /* microSPARC II mask number */
+#define IOMMU_TS_MASK 0x23000000 /* turboSPARC mask number */
+
+/* The format of an iopte in the page tables */
+#define IOPTE_PAGE 0xffffff00 /* Physical page number (PA[35:12]) */
+#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or
+ Viking/MXCC) */
+#define IOPTE_WRITE 0x00000004 /* Writeable */
+#define IOPTE_VALID 0x00000002 /* IOPTE is valid */
+#define IOPTE_WAZ 0x00000001 /* Write as zeros */
+
+#define IOMMU_PAGE_SHIFT 12
+#define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT)
+#define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1)
+
+typedef struct IOMMUState {
+ SysBusDevice busdev;
+ uint32_t regs[IOMMU_NREGS];
+ target_phys_addr_t iostart;
+ uint32_t version;
+ qemu_irq irq;
+} IOMMUState;
+
+static uint32_t iommu_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ IOMMUState *s = opaque;
+ target_phys_addr_t saddr;
+ uint32_t ret;
+
+ saddr = addr >> 2;
+ switch (saddr) {
+ default:
+ ret = s->regs[saddr];
+ break;
+ case IOMMU_AFAR:
+ case IOMMU_AFSR:
+ ret = s->regs[saddr];
+ qemu_irq_lower(s->irq);
+ break;
+ }
+ trace_sun4m_iommu_mem_readl(saddr, ret);
+ return ret;
+}
+
+static void iommu_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ IOMMUState *s = opaque;
+ target_phys_addr_t saddr;
+
+ saddr = addr >> 2;
+ trace_sun4m_iommu_mem_writel(saddr, val);
+ switch (saddr) {
+ case IOMMU_CTRL:
+ switch (val & IOMMU_CTRL_RNGE) {
+ case IOMMU_RNGE_16MB:
+ s->iostart = 0xffffffffff000000ULL;
+ break;
+ case IOMMU_RNGE_32MB:
+ s->iostart = 0xfffffffffe000000ULL;
+ break;
+ case IOMMU_RNGE_64MB:
+ s->iostart = 0xfffffffffc000000ULL;
+ break;
+ case IOMMU_RNGE_128MB:
+ s->iostart = 0xfffffffff8000000ULL;
+ break;
+ case IOMMU_RNGE_256MB:
+ s->iostart = 0xfffffffff0000000ULL;
+ break;
+ case IOMMU_RNGE_512MB:
+ s->iostart = 0xffffffffe0000000ULL;
+ break;
+ case IOMMU_RNGE_1GB:
+ s->iostart = 0xffffffffc0000000ULL;
+ break;
+ default:
+ case IOMMU_RNGE_2GB:
+ s->iostart = 0xffffffff80000000ULL;
+ break;
+ }
+ trace_sun4m_iommu_mem_writel_ctrl(s->iostart);
+ s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version);
+ break;
+ case IOMMU_BASE:
+ s->regs[saddr] = val & IOMMU_BASE_MASK;
+ break;
+ case IOMMU_TLBFLUSH:
+ trace_sun4m_iommu_mem_writel_tlbflush(val);
+ s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK;
+ break;
+ case IOMMU_PGFLUSH:
+ trace_sun4m_iommu_mem_writel_pgflush(val);
+ s->regs[saddr] = val & IOMMU_PGFLUSH_MASK;
+ break;
+ case IOMMU_AFAR:
+ s->regs[saddr] = val;
+ qemu_irq_lower(s->irq);
+ break;
+ case IOMMU_AER:
+ s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB;
+ break;
+ case IOMMU_AFSR:
+ s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV;
+ qemu_irq_lower(s->irq);
+ break;
+ case IOMMU_SBCFG0:
+ case IOMMU_SBCFG1:
+ case IOMMU_SBCFG2:
+ case IOMMU_SBCFG3:
+ s->regs[saddr] = val & IOMMU_SBCFG_MASK;
+ break;
+ case IOMMU_ARBEN:
+ // XXX implement SBus probing: fault when reading unmapped
+ // addresses, fault cause and address stored to MMU/IOMMU
+ s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID;
+ break;
+ case IOMMU_MASK_ID:
+ s->regs[saddr] |= val & IOMMU_MASK_ID_MASK;
+ break;
+ default:
+ s->regs[saddr] = val;
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const iommu_mem_read[3] = {
+ NULL,
+ NULL,
+ iommu_mem_readl,
+};
+
+static CPUWriteMemoryFunc * const iommu_mem_write[3] = {
+ NULL,
+ NULL,
+ iommu_mem_writel,
+};
+
+static uint32_t iommu_page_get_flags(IOMMUState *s, target_phys_addr_t addr)
+{
+ uint32_t ret;
+ target_phys_addr_t iopte;
+ target_phys_addr_t pa = addr;
+
+ iopte = s->regs[IOMMU_BASE] << 4;
+ addr &= ~s->iostart;
+ iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3;
+ cpu_physical_memory_read(iopte, (uint8_t *)&ret, 4);
+ tswap32s(&ret);
+ trace_sun4m_iommu_page_get_flags(pa, iopte, ret);
+ return ret;
+}
+
+static target_phys_addr_t iommu_translate_pa(target_phys_addr_t addr,
+ uint32_t pte)
+{
+ target_phys_addr_t pa;
+
+ pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK);
+ trace_sun4m_iommu_translate_pa(addr, pa, pte);
+ return pa;
+}
+
+static void iommu_bad_addr(IOMMUState *s, target_phys_addr_t addr,
+ int is_write)
+{
+ trace_sun4m_iommu_bad_addr(addr);
+ s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV |
+ IOMMU_AFSR_FAV;
+ if (!is_write)
+ s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD;
+ s->regs[IOMMU_AFAR] = addr;
+ qemu_irq_raise(s->irq);
+}
+
+void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr,
+ uint8_t *buf, int len, int is_write)
+{
+ int l;
+ uint32_t flags;
+ target_phys_addr_t page, phys_addr;
+
+ while (len > 0) {
+ page = addr & IOMMU_PAGE_MASK;
+ l = (page + IOMMU_PAGE_SIZE) - addr;
+ if (l > len)
+ l = len;
+ flags = iommu_page_get_flags(opaque, page);
+ if (!(flags & IOPTE_VALID)) {
+ iommu_bad_addr(opaque, page, is_write);
+ return;
+ }
+ phys_addr = iommu_translate_pa(addr, flags);
+ if (is_write) {
+ if (!(flags & IOPTE_WRITE)) {
+ iommu_bad_addr(opaque, page, is_write);
+ return;
+ }
+ cpu_physical_memory_write(phys_addr, buf, l);
+ } else {
+ cpu_physical_memory_read(phys_addr, buf, l);
+ }
+ len -= l;
+ buf += l;
+ addr += l;
+ }
+}
+
+static const VMStateDescription vmstate_iommu = {
+ .name ="iommu",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS),
+ VMSTATE_UINT64(iostart, IOMMUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void iommu_reset(DeviceState *d)
+{
+ IOMMUState *s = container_of(d, IOMMUState, busdev.qdev);
+
+ memset(s->regs, 0, IOMMU_NREGS * 4);
+ s->iostart = 0;
+ s->regs[IOMMU_CTRL] = s->version;
+ s->regs[IOMMU_ARBEN] = IOMMU_MID;
+ s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV;
+ s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB;
+ s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK;
+}
+
+static int iommu_init1(SysBusDevice *dev)
+{
+ IOMMUState *s = FROM_SYSBUS(IOMMUState, dev);
+ int io;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ io = cpu_register_io_memory(iommu_mem_read, iommu_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, IOMMU_NREGS * sizeof(uint32_t), io);
+
+ return 0;
+}
+
+static SysBusDeviceInfo iommu_info = {
+ .init = iommu_init1,
+ .qdev.name = "iommu",
+ .qdev.size = sizeof(IOMMUState),
+ .qdev.vmsd = &vmstate_iommu,
+ .qdev.reset = iommu_reset,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("version", IOMMUState, version, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void iommu_register_devices(void)
+{
+ sysbus_register_withprop(&iommu_info);
+}
+
+device_init(iommu_register_devices)
diff --git a/hw/sun4u.c b/hw/sun4u.c
new file mode 100644
index 0000000..90b1ce2
--- /dev/null
+++ b/hw/sun4u.c
@@ -0,0 +1,942 @@
+/*
+ * QEMU Sun4u/Sun4v System Emulator
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pci.h"
+#include "apb_pci.h"
+#include "pc.h"
+#include "nvram.h"
+#include "fdc.h"
+#include "net.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "firmware_abi.h"
+#include "fw_cfg.h"
+#include "sysbus.h"
+#include "ide.h"
+#include "loader.h"
+#include "elf.h"
+#include "blockdev.h"
+
+//#define DEBUG_IRQ
+//#define DEBUG_EBUS
+//#define DEBUG_TIMER
+
+#ifdef DEBUG_IRQ
+#define CPUIRQ_DPRINTF(fmt, ...) \
+ do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define CPUIRQ_DPRINTF(fmt, ...)
+#endif
+
+#ifdef DEBUG_EBUS
+#define EBUS_DPRINTF(fmt, ...) \
+ do { printf("EBUS: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define EBUS_DPRINTF(fmt, ...)
+#endif
+
+#ifdef DEBUG_TIMER
+#define TIMER_DPRINTF(fmt, ...) \
+ do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define TIMER_DPRINTF(fmt, ...)
+#endif
+
+#define KERNEL_LOAD_ADDR 0x00404000
+#define CMDLINE_ADDR 0x003ff000
+#define INITRD_LOAD_ADDR 0x00300000
+#define PROM_SIZE_MAX (4 * 1024 * 1024)
+#define PROM_VADDR 0x000ffd00000ULL
+#define APB_SPECIAL_BASE 0x1fe00000000ULL
+#define APB_MEM_BASE 0x1ff00000000ULL
+#define APB_PCI_IO_BASE (APB_SPECIAL_BASE + 0x02000000ULL)
+#define PROM_FILENAME "openbios-sparc64"
+#define NVRAM_SIZE 0x2000
+#define MAX_IDE_BUS 2
+#define BIOS_CFG_IOPORT 0x510
+#define FW_CFG_SPARC64_WIDTH (FW_CFG_ARCH_LOCAL + 0x00)
+#define FW_CFG_SPARC64_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01)
+#define FW_CFG_SPARC64_DEPTH (FW_CFG_ARCH_LOCAL + 0x02)
+
+#define MAX_PILS 16
+
+#define TICK_MAX 0x7fffffffffffffffULL
+
+struct hwdef {
+ const char * const default_cpu_model;
+ uint16_t machine_id;
+ uint64_t prom_addr;
+ uint64_t console_serial_base;
+};
+
+int DMA_get_channel_mode (int nchan)
+{
+ return 0;
+}
+int DMA_read_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+int DMA_write_memory (int nchan, void *buf, int pos, int size)
+{
+ return 0;
+}
+void DMA_hold_DREQ (int nchan) {}
+void DMA_release_DREQ (int nchan) {}
+void DMA_schedule(int nchan) {}
+
+void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+{
+}
+
+void DMA_register_channel (int nchan,
+ DMA_transfer_handler transfer_handler,
+ void *opaque)
+{
+}
+
+static int fw_cfg_boot_set(void *opaque, const char *boot_device)
+{
+ fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+ return 0;
+}
+
+static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size,
+ const char *arch, ram_addr_t RAM_size,
+ const char *boot_devices,
+ uint32_t kernel_image, uint32_t kernel_size,
+ const char *cmdline,
+ uint32_t initrd_image, uint32_t initrd_size,
+ uint32_t NVRAM_image,
+ int width, int height, int depth,
+ const uint8_t *macaddr)
+{
+ unsigned int i;
+ uint32_t start, end;
+ uint8_t image[0x1ff0];
+ struct OpenBIOS_nvpart_v1 *part_header;
+
+ memset(image, '\0', sizeof(image));
+
+ start = 0;
+
+ // OpenBIOS nvram variables
+ // Variable partition
+ part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
+ part_header->signature = OPENBIOS_PART_SYSTEM;
+ pstrcpy(part_header->name, sizeof(part_header->name), "system");
+
+ end = start + sizeof(struct OpenBIOS_nvpart_v1);
+ for (i = 0; i < nb_prom_envs; i++)
+ end = OpenBIOS_set_var(image, end, prom_envs[i]);
+
+ // End marker
+ image[end++] = '\0';
+
+ end = start + ((end - start + 15) & ~15);
+ OpenBIOS_finish_partition(part_header, end - start);
+
+ // free partition
+ start = end;
+ part_header = (struct OpenBIOS_nvpart_v1 *)&image[start];
+ part_header->signature = OPENBIOS_PART_FREE;
+ pstrcpy(part_header->name, sizeof(part_header->name), "free");
+
+ end = 0x1fd0;
+ OpenBIOS_finish_partition(part_header, end - start);
+
+ Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80);
+
+ for (i = 0; i < sizeof(image); i++)
+ m48t59_write(nvram, i, image[i]);
+
+ return 0;
+}
+static unsigned long sun4u_load_kernel(const char *kernel_filename,
+ const char *initrd_filename,
+ ram_addr_t RAM_size, long *initrd_size)
+{
+ int linux_boot;
+ unsigned int i;
+ long kernel_size;
+ uint8_t *ptr;
+
+ linux_boot = (kernel_filename != NULL);
+
+ kernel_size = 0;
+ if (linux_boot) {
+ int bswap_needed;
+
+#ifdef BSWAP_NEEDED
+ bswap_needed = 1;
+#else
+ bswap_needed = 0;
+#endif
+ kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
+ NULL, NULL, 1, ELF_MACHINE, 0);
+ if (kernel_size < 0)
+ kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR,
+ RAM_size - KERNEL_LOAD_ADDR, bswap_needed,
+ TARGET_PAGE_SIZE);
+ if (kernel_size < 0)
+ kernel_size = load_image_targphys(kernel_filename,
+ KERNEL_LOAD_ADDR,
+ RAM_size - KERNEL_LOAD_ADDR);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ *initrd_size = 0;
+ if (initrd_filename) {
+ *initrd_size = load_image_targphys(initrd_filename,
+ INITRD_LOAD_ADDR,
+ RAM_size - INITRD_LOAD_ADDR);
+ if (*initrd_size < 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+ }
+ if (*initrd_size > 0) {
+ for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) {
+ ptr = rom_ptr(KERNEL_LOAD_ADDR + i);
+ if (ldl_p(ptr + 8) == 0x48647253) { /* HdrS */
+ stl_p(ptr + 24, INITRD_LOAD_ADDR + KERNEL_LOAD_ADDR - 0x4000);
+ stl_p(ptr + 28, *initrd_size);
+ break;
+ }
+ }
+ }
+ }
+ return kernel_size;
+}
+
+void pic_info(Monitor *mon)
+{
+}
+
+void irq_info(Monitor *mon)
+{
+}
+
+void cpu_check_irqs(CPUState *env)
+{
+ uint32_t pil = env->pil_in |
+ (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER));
+
+ /* check if TM or SM in SOFTINT are set
+ setting these also causes interrupt 14 */
+ if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) {
+ pil |= 1 << 14;
+ }
+
+ if (!pil) {
+ if (env->interrupt_request & CPU_INTERRUPT_HARD) {
+ CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n",
+ env->interrupt_index);
+ env->interrupt_index = 0;
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ return;
+ }
+
+ if (cpu_interrupts_enabled(env)) {
+
+ unsigned int i;
+
+ for (i = 15; i > env->psrpil; i--) {
+ if (pil & (1 << i)) {
+ int old_interrupt = env->interrupt_index;
+ int new_interrupt = TT_EXTINT | i;
+
+ if (env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt) {
+ CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d "
+ "current %x >= pending %x\n",
+ env->tl, cpu_tsptr(env)->tt, new_interrupt);
+ } else if (old_interrupt != new_interrupt) {
+ env->interrupt_index = new_interrupt;
+ CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i,
+ old_interrupt, new_interrupt);
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ }
+ break;
+ }
+ }
+ } else {
+ CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x "
+ "current interrupt %x\n",
+ pil, env->pil_in, env->softint, env->interrupt_index);
+ }
+}
+
+static void cpu_kick_irq(CPUState *env)
+{
+ env->halted = 0;
+ cpu_check_irqs(env);
+}
+
+static void cpu_set_irq(void *opaque, int irq, int level)
+{
+ CPUState *env = opaque;
+
+ if (level) {
+ CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq);
+ env->halted = 0;
+ env->pil_in |= 1 << irq;
+ cpu_check_irqs(env);
+ } else {
+ CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq);
+ env->pil_in &= ~(1 << irq);
+ cpu_check_irqs(env);
+ }
+}
+
+typedef struct ResetData {
+ CPUState *env;
+ uint64_t prom_addr;
+} ResetData;
+
+void cpu_put_timer(QEMUFile *f, CPUTimer *s)
+{
+ qemu_put_be32s(f, &s->frequency);
+ qemu_put_be32s(f, &s->disabled);
+ qemu_put_be64s(f, &s->disabled_mask);
+ qemu_put_sbe64s(f, &s->clock_offset);
+
+ qemu_put_timer(f, s->qtimer);
+}
+
+void cpu_get_timer(QEMUFile *f, CPUTimer *s)
+{
+ qemu_get_be32s(f, &s->frequency);
+ qemu_get_be32s(f, &s->disabled);
+ qemu_get_be64s(f, &s->disabled_mask);
+ qemu_get_sbe64s(f, &s->clock_offset);
+
+ qemu_get_timer(f, s->qtimer);
+}
+
+static CPUTimer* cpu_timer_create(const char* name, CPUState *env,
+ QEMUBHFunc *cb, uint32_t frequency,
+ uint64_t disabled_mask)
+{
+ CPUTimer *timer = qemu_mallocz(sizeof (CPUTimer));
+
+ timer->name = name;
+ timer->frequency = frequency;
+ timer->disabled_mask = disabled_mask;
+
+ timer->disabled = 1;
+ timer->clock_offset = qemu_get_clock(vm_clock);
+
+ timer->qtimer = qemu_new_timer(vm_clock, cb, env);
+
+ return timer;
+}
+
+static void cpu_timer_reset(CPUTimer *timer)
+{
+ timer->disabled = 1;
+ timer->clock_offset = qemu_get_clock(vm_clock);
+
+ qemu_del_timer(timer->qtimer);
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPUState *env = s->env;
+ static unsigned int nr_resets;
+
+ cpu_reset(env);
+
+ cpu_timer_reset(env->tick);
+ cpu_timer_reset(env->stick);
+ cpu_timer_reset(env->hstick);
+
+ env->gregs[1] = 0; // Memory start
+ env->gregs[2] = ram_size; // Memory size
+ env->gregs[3] = 0; // Machine description XXX
+ if (nr_resets++ == 0) {
+ /* Power on reset */
+ env->pc = s->prom_addr + 0x20ULL;
+ } else {
+ env->pc = s->prom_addr + 0x40ULL;
+ }
+ env->npc = env->pc + 4;
+}
+
+static void tick_irq(void *opaque)
+{
+ CPUState *env = opaque;
+
+ CPUTimer* timer = env->tick;
+
+ if (timer->disabled) {
+ CPUIRQ_DPRINTF("tick_irq: softint disabled\n");
+ return;
+ } else {
+ CPUIRQ_DPRINTF("tick: fire\n");
+ }
+
+ env->softint |= SOFTINT_TIMER;
+ cpu_kick_irq(env);
+}
+
+static void stick_irq(void *opaque)
+{
+ CPUState *env = opaque;
+
+ CPUTimer* timer = env->stick;
+
+ if (timer->disabled) {
+ CPUIRQ_DPRINTF("stick_irq: softint disabled\n");
+ return;
+ } else {
+ CPUIRQ_DPRINTF("stick: fire\n");
+ }
+
+ env->softint |= SOFTINT_STIMER;
+ cpu_kick_irq(env);
+}
+
+static void hstick_irq(void *opaque)
+{
+ CPUState *env = opaque;
+
+ CPUTimer* timer = env->hstick;
+
+ if (timer->disabled) {
+ CPUIRQ_DPRINTF("hstick_irq: softint disabled\n");
+ return;
+ } else {
+ CPUIRQ_DPRINTF("hstick: fire\n");
+ }
+
+ env->softint |= SOFTINT_STIMER;
+ cpu_kick_irq(env);
+}
+
+static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency)
+{
+ return muldiv64(cpu_ticks, get_ticks_per_sec(), frequency);
+}
+
+static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
+{
+ return muldiv64(timer_ticks, frequency, get_ticks_per_sec());
+}
+
+void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
+{
+ uint64_t real_count = count & ~timer->disabled_mask;
+ uint64_t disabled_bit = count & timer->disabled_mask;
+
+ int64_t vm_clock_offset = qemu_get_clock(vm_clock) -
+ cpu_to_timer_ticks(real_count, timer->frequency);
+
+ TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n",
+ timer->name, real_count,
+ timer->disabled?"disabled":"enabled", timer);
+
+ timer->disabled = disabled_bit ? 1 : 0;
+ timer->clock_offset = vm_clock_offset;
+}
+
+uint64_t cpu_tick_get_count(CPUTimer *timer)
+{
+ uint64_t real_count = timer_to_cpu_ticks(
+ qemu_get_clock(vm_clock) - timer->clock_offset,
+ timer->frequency);
+
+ TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n",
+ timer->name, real_count,
+ timer->disabled?"disabled":"enabled", timer);
+
+ if (timer->disabled)
+ real_count |= timer->disabled_mask;
+
+ return real_count;
+}
+
+void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit)
+{
+ int64_t now = qemu_get_clock(vm_clock);
+
+ uint64_t real_limit = limit & ~timer->disabled_mask;
+ timer->disabled = (limit & timer->disabled_mask) ? 1 : 0;
+
+ int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) +
+ timer->clock_offset;
+
+ if (expires < now) {
+ expires = now + 1;
+ }
+
+ TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p "
+ "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n",
+ timer->name, real_limit,
+ timer->disabled?"disabled":"enabled",
+ timer, limit,
+ timer_to_cpu_ticks(now - timer->clock_offset,
+ timer->frequency),
+ timer_to_cpu_ticks(expires - now, timer->frequency));
+
+ if (!real_limit) {
+ TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n",
+ timer->name);
+ qemu_del_timer(timer->qtimer);
+ } else if (timer->disabled) {
+ qemu_del_timer(timer->qtimer);
+ } else {
+ qemu_mod_timer(timer->qtimer, expires);
+ }
+}
+
+static void ebus_mmio_mapfunc(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ EBUS_DPRINTF("Mapping region %d registers at %" FMT_PCIBUS "\n",
+ region_num, addr);
+ switch (region_num) {
+ case 0:
+ isa_mmio_init(addr, 0x1000000);
+ break;
+ case 1:
+ isa_mmio_init(addr, 0x800000);
+ break;
+ }
+}
+
+static void dummy_isa_irq_handler(void *opaque, int n, int level)
+{
+}
+
+/* EBUS (Eight bit bus) bridge */
+static void
+pci_ebus_init(PCIBus *bus, int devfn)
+{
+ qemu_irq *isa_irq;
+
+ pci_create_simple(bus, devfn, "ebus");
+ isa_irq = qemu_allocate_irqs(dummy_isa_irq_handler, NULL, 16);
+ isa_bus_irqs(isa_irq);
+}
+
+static int
+pci_ebus_init1(PCIDevice *s)
+{
+ isa_bus_new(&s->qdev);
+
+ pci_config_set_vendor_id(s->config, PCI_VENDOR_ID_SUN);
+ pci_config_set_device_id(s->config, PCI_DEVICE_ID_SUN_EBUS);
+ s->config[0x04] = 0x06; // command = bus master, pci mem
+ s->config[0x05] = 0x00;
+ s->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
+ s->config[0x07] = 0x03; // status = medium devsel
+ s->config[0x08] = 0x01; // revision
+ s->config[0x09] = 0x00; // programming i/f
+ pci_config_set_class(s->config, PCI_CLASS_BRIDGE_OTHER);
+ s->config[0x0D] = 0x0a; // latency_timer
+
+ pci_register_bar(s, 0, 0x1000000, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ ebus_mmio_mapfunc);
+ pci_register_bar(s, 1, 0x800000, PCI_BASE_ADDRESS_SPACE_MEMORY,
+ ebus_mmio_mapfunc);
+ return 0;
+}
+
+static PCIDeviceInfo ebus_info = {
+ .qdev.name = "ebus",
+ .qdev.size = sizeof(PCIDevice),
+ .init = pci_ebus_init1,
+};
+
+static void pci_ebus_register(void)
+{
+ pci_qdev_register(&ebus_info);
+}
+
+device_init(pci_ebus_register);
+
+static uint64_t translate_prom_address(void *opaque, uint64_t addr)
+{
+ target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque;
+ return addr + *base_addr - PROM_VADDR;
+}
+
+/* Boot PROM (OpenBIOS) */
+static void prom_init(target_phys_addr_t addr, const char *bios_name)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ char *filename;
+ int ret;
+
+ dev = qdev_create(NULL, "openprom");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+
+ /* load boot prom */
+ if (bios_name == NULL) {
+ bios_name = PROM_FILENAME;
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (filename) {
+ ret = load_elf(filename, translate_prom_address, &addr,
+ NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+ if (ret < 0 || ret > PROM_SIZE_MAX) {
+ ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
+ }
+ qemu_free(filename);
+ } else {
+ ret = -1;
+ }
+ if (ret < 0 || ret > PROM_SIZE_MAX) {
+ fprintf(stderr, "qemu: could not load prom '%s'\n", bios_name);
+ exit(1);
+ }
+}
+
+static int prom_init1(SysBusDevice *dev)
+{
+ ram_addr_t prom_offset;
+
+ prom_offset = qemu_ram_alloc(NULL, "sun4u.prom", PROM_SIZE_MAX);
+ sysbus_init_mmio(dev, PROM_SIZE_MAX, prom_offset | IO_MEM_ROM);
+ return 0;
+}
+
+static SysBusDeviceInfo prom_info = {
+ .init = prom_init1,
+ .qdev.name = "openprom",
+ .qdev.size = sizeof(SysBusDevice),
+ .qdev.props = (Property[]) {
+ {/* end of property list */}
+ }
+};
+
+static void prom_register_devices(void)
+{
+ sysbus_register_withprop(&prom_info);
+}
+
+device_init(prom_register_devices);
+
+
+typedef struct RamDevice
+{
+ SysBusDevice busdev;
+ uint64_t size;
+} RamDevice;
+
+/* System RAM */
+static int ram_init1(SysBusDevice *dev)
+{
+ ram_addr_t RAM_size, ram_offset;
+ RamDevice *d = FROM_SYSBUS(RamDevice, dev);
+
+ RAM_size = d->size;
+
+ ram_offset = qemu_ram_alloc(NULL, "sun4u.ram", RAM_size);
+ sysbus_init_mmio(dev, RAM_size, ram_offset);
+ return 0;
+}
+
+static void ram_init(target_phys_addr_t addr, ram_addr_t RAM_size)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ RamDevice *d;
+
+ /* allocate RAM */
+ dev = qdev_create(NULL, "memory");
+ s = sysbus_from_qdev(dev);
+
+ d = FROM_SYSBUS(RamDevice, s);
+ d->size = RAM_size;
+ qdev_init_nofail(dev);
+
+ sysbus_mmio_map(s, 0, addr);
+}
+
+static SysBusDeviceInfo ram_info = {
+ .init = ram_init1,
+ .qdev.name = "memory",
+ .qdev.size = sizeof(RamDevice),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT64("size", RamDevice, size, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ram_register_devices(void)
+{
+ sysbus_register_withprop(&ram_info);
+}
+
+device_init(ram_register_devices);
+
+static CPUState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
+{
+ CPUState *env;
+ ResetData *reset_info;
+
+ uint32_t tick_frequency = 100*1000000;
+ uint32_t stick_frequency = 100*1000000;
+ uint32_t hstick_frequency = 100*1000000;
+
+ if (!cpu_model)
+ cpu_model = hwdef->default_cpu_model;
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find Sparc CPU definition\n");
+ exit(1);
+ }
+
+ env->tick = cpu_timer_create("tick", env, tick_irq,
+ tick_frequency, TICK_NPT_MASK);
+
+ env->stick = cpu_timer_create("stick", env, stick_irq,
+ stick_frequency, TICK_INT_DIS);
+
+ env->hstick = cpu_timer_create("hstick", env, hstick_irq,
+ hstick_frequency, TICK_INT_DIS);
+
+ reset_info = qemu_mallocz(sizeof(ResetData));
+ reset_info->env = env;
+ reset_info->prom_addr = hwdef->prom_addr;
+ qemu_register_reset(main_cpu_reset, reset_info);
+
+ return env;
+}
+
+static void sun4uv_init(ram_addr_t RAM_size,
+ const char *boot_devices,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model,
+ const struct hwdef *hwdef)
+{
+ CPUState *env;
+ M48t59State *nvram;
+ unsigned int i;
+ long initrd_size, kernel_size;
+ PCIBus *pci_bus, *pci_bus2, *pci_bus3;
+ qemu_irq *irq;
+ DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
+ DriveInfo *fd[MAX_FD];
+ void *fw_cfg;
+
+ /* init CPUs */
+ env = cpu_devinit(cpu_model, hwdef);
+
+ /* set up devices */
+ ram_init(0, RAM_size);
+
+ prom_init(hwdef->prom_addr, bios_name);
+
+
+ irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS);
+ pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2,
+ &pci_bus3);
+ isa_mem_base = APB_PCI_IO_BASE;
+ pci_vga_init(pci_bus);
+
+ // XXX Should be pci_bus3
+ pci_ebus_init(pci_bus, -1);
+
+ i = 0;
+ if (hwdef->console_serial_base) {
+ serial_mm_init(hwdef->console_serial_base, 0, NULL, 115200,
+ serial_hds[i], 1, 1);
+ i++;
+ }
+ for(; i < MAX_SERIAL_PORTS; i++) {
+ if (serial_hds[i]) {
+ serial_isa_init(i, serial_hds[i]);
+ }
+ }
+
+ for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+ if (parallel_hds[i]) {
+ parallel_init(i, parallel_hds[i]);
+ }
+ }
+
+ for(i = 0; i < nb_nics; i++)
+ pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
+
+ if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
+ fprintf(stderr, "qemu: too many IDE bus\n");
+ exit(1);
+ }
+ for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
+ hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS,
+ i % MAX_IDE_DEVS);
+ }
+
+ pci_cmd646_ide_init(pci_bus, hd, 1);
+
+ isa_create_simple("i8042");
+ for(i = 0; i < MAX_FD; i++) {
+ fd[i] = drive_get(IF_FLOPPY, 0, i);
+ }
+ fdctrl_init_isa(fd);
+ nvram = m48t59_init_isa(0x0074, NVRAM_SIZE, 59);
+
+ initrd_size = 0;
+ kernel_size = sun4u_load_kernel(kernel_filename, initrd_filename,
+ ram_size, &initrd_size);
+
+ sun4u_NVRAM_set_params(nvram, NVRAM_SIZE, "Sun4u", RAM_size, boot_devices,
+ KERNEL_LOAD_ADDR, kernel_size,
+ kernel_cmdline,
+ INITRD_LOAD_ADDR, initrd_size,
+ /* XXX: need an option to load a NVRAM image */
+ 0,
+ graphic_width, graphic_height, graphic_depth,
+ (uint8_t *)&nd_table[0].macaddr);
+
+ fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, hwdef->machine_id);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ if (kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(kernel_cmdline) + 1);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
+ (uint8_t*)strdup(kernel_cmdline),
+ strlen(kernel_cmdline) + 1);
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_devices[0]);
+
+ fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_WIDTH, graphic_width);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_HEIGHT, graphic_height);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_DEPTH, graphic_depth);
+
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+}
+
+enum {
+ sun4u_id = 0,
+ sun4v_id = 64,
+ niagara_id,
+};
+
+static const struct hwdef hwdefs[] = {
+ /* Sun4u generic PC-like machine */
+ {
+ .default_cpu_model = "TI UltraSparc IIi",
+ .machine_id = sun4u_id,
+ .prom_addr = 0x1fff0000000ULL,
+ .console_serial_base = 0,
+ },
+ /* Sun4v generic PC-like machine */
+ {
+ .default_cpu_model = "Sun UltraSparc T1",
+ .machine_id = sun4v_id,
+ .prom_addr = 0x1fff0000000ULL,
+ .console_serial_base = 0,
+ },
+ /* Sun4v generic Niagara machine */
+ {
+ .default_cpu_model = "Sun UltraSparc T1",
+ .machine_id = niagara_id,
+ .prom_addr = 0xfff0000000ULL,
+ .console_serial_base = 0xfff0c2c000ULL,
+ },
+};
+
+/* Sun4u hardware initialisation */
+static void sun4u_init(ram_addr_t RAM_size,
+ const char *boot_devices,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4uv_init(RAM_size, boot_devices, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, &hwdefs[0]);
+}
+
+/* Sun4v hardware initialisation */
+static void sun4v_init(ram_addr_t RAM_size,
+ const char *boot_devices,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4uv_init(RAM_size, boot_devices, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, &hwdefs[1]);
+}
+
+/* Niagara hardware initialisation */
+static void niagara_init(ram_addr_t RAM_size,
+ const char *boot_devices,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ sun4uv_init(RAM_size, boot_devices, kernel_filename,
+ kernel_cmdline, initrd_filename, cpu_model, &hwdefs[2]);
+}
+
+static QEMUMachine sun4u_machine = {
+ .name = "sun4u",
+ .desc = "Sun4u platform",
+ .init = sun4u_init,
+ .max_cpus = 1, // XXX for now
+ .is_default = 1,
+};
+
+static QEMUMachine sun4v_machine = {
+ .name = "sun4v",
+ .desc = "Sun4v platform",
+ .init = sun4v_init,
+ .max_cpus = 1, // XXX for now
+};
+
+static QEMUMachine niagara_machine = {
+ .name = "Niagara",
+ .desc = "Sun4v platform, Niagara",
+ .init = niagara_init,
+ .max_cpus = 1, // XXX for now
+};
+
+static void sun4u_machine_init(void)
+{
+ qemu_register_machine(&sun4u_machine);
+ qemu_register_machine(&sun4v_machine);
+ qemu_register_machine(&niagara_machine);
+}
+
+machine_init(sun4u_machine_init);
diff --git a/hw/syborg.c b/hw/syborg.c
new file mode 100644
index 0000000..758c69a
--- /dev/null
+++ b/hw/syborg.c
@@ -0,0 +1,112 @@
+/*
+ * Syborg (Symbian Virtual Platform) reference board
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "boards.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+#include "net.h"
+
+static struct arm_boot_info syborg_binfo;
+
+static void syborg_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ CPUState *env;
+ qemu_irq *cpu_pic;
+ qemu_irq pic[64];
+ ram_addr_t ram_addr;
+ DeviceState *dev;
+ int i;
+
+ if (!cpu_model)
+ cpu_model = "cortex-a8";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ /* RAM at address zero. */
+ ram_addr = qemu_ram_alloc(NULL, "syborg.ram", ram_size);
+ cpu_register_physical_memory(0, ram_size, ram_addr | IO_MEM_RAM);
+
+ cpu_pic = arm_pic_init_cpu(env);
+ dev = sysbus_create_simple("syborg,interrupt", 0xC0000000,
+ cpu_pic[ARM_PIC_CPU_IRQ]);
+ for (i = 0; i < 64; i++) {
+ pic[i] = qdev_get_gpio_in(dev, i);
+ }
+
+ sysbus_create_simple("syborg,rtc", 0xC0001000, NULL);
+
+ dev = qdev_create(NULL, "syborg,timer");
+ qdev_prop_set_uint32(dev, "frequency", 1000000);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xC0002000);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[1]);
+
+ sysbus_create_simple("syborg,keyboard", 0xC0003000, pic[2]);
+ sysbus_create_simple("syborg,pointer", 0xC0004000, pic[3]);
+ sysbus_create_simple("syborg,framebuffer", 0xC0005000, pic[4]);
+ sysbus_create_simple("syborg,serial", 0xC0006000, pic[5]);
+ sysbus_create_simple("syborg,serial", 0xC0007000, pic[6]);
+ sysbus_create_simple("syborg,serial", 0xC0008000, pic[7]);
+ sysbus_create_simple("syborg,serial", 0xC0009000, pic[8]);
+
+ if (nd_table[0].vlan || nd_table[0].netdev) {
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(&nd_table[0], "virtio");
+ dev = qdev_create(NULL, "syborg,virtio-net");
+ qdev_set_nic_properties(dev, &nd_table[0]);
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, 0xc000c000);
+ sysbus_connect_irq(s, 0, pic[9]);
+ }
+
+ syborg_binfo.ram_size = ram_size;
+ syborg_binfo.kernel_filename = kernel_filename;
+ syborg_binfo.kernel_cmdline = kernel_cmdline;
+ syborg_binfo.initrd_filename = initrd_filename;
+ syborg_binfo.board_id = 0;
+ arm_load_kernel(env, &syborg_binfo);
+}
+
+static QEMUMachine syborg_machine = {
+ .name = "syborg",
+ .desc = "Syborg (Symbian Virtual Platform)",
+ .init = syborg_init,
+};
+
+static void syborg_machine_init(void)
+{
+ qemu_register_machine(&syborg_machine);
+}
+
+machine_init(syborg_machine_init);
diff --git a/hw/syborg.h b/hw/syborg.h
new file mode 100644
index 0000000..b82ce4a
--- /dev/null
+++ b/hw/syborg.h
@@ -0,0 +1,18 @@
+#ifndef _SYBORG_H
+#define _SYBORG_H
+
+#define SYBORG_ID_PLATFORM 0xc51d1000
+#define SYBORG_ID_INT 0xc51d0000
+#define SYBORG_ID_SERIAL 0xc51d0001
+#define SYBORG_ID_KEYBOARD 0xc51d0002
+#define SYBORG_ID_TIMER 0xc51d0003
+#define SYBORG_ID_RTC 0xc51d0004
+#define SYBORG_ID_MOUSE 0xc51d0005
+#define SYBORG_ID_TOUCHSCREEN 0xc51d0006
+#define SYBORG_ID_FRAMEBUFFER 0xc51d0007
+#define SYBORG_ID_HOSTFS 0xc51d0008
+#define SYBORG_ID_SNAPSHOT 0xc51d0009
+#define SYBORG_ID_VIRTIO 0xc51d000a
+#define SYBORG_ID_NAND 0xc51d000b
+
+#endif
diff --git a/hw/syborg_fb.c b/hw/syborg_fb.c
new file mode 100644
index 0000000..7e37364
--- /dev/null
+++ b/hw/syborg_fb.c
@@ -0,0 +1,551 @@
+/*
+ * Syborg Framebuffer
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "console.h"
+#include "syborg.h"
+#include "framebuffer.h"
+
+//#define DEBUG_SYBORG_FB
+
+#ifdef DEBUG_SYBORG_FB
+#define DPRINTF(fmt, ...) \
+do { printf("syborg_fb: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__); \
+ exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+ FB_ID = 0,
+ FB_BASE = 1,
+ FB_HEIGHT = 2,
+ FB_WIDTH = 3,
+ FB_ORIENTATION = 4,
+ FB_BLANK = 5,
+ FB_INT_MASK = 6,
+ FB_INTERRUPT_CAUSE = 7,
+ FB_BPP = 8,
+ FB_COLOR_ORDER = 9,
+ FB_BYTE_ORDER = 10,
+ FB_PIXEL_ORDER = 11,
+ FB_ROW_PITCH = 12,
+ FB_ENABLED = 13,
+ FB_PALETTE_START = 0x400 >> 2,
+ FB_PALETTE_END = FB_PALETTE_START+256-1,
+};
+
+#define FB_INT_VSYNC (1U << 0)
+#define FB_INT_BASE_UPDATE_DONE (1U << 1)
+
+typedef struct {
+ SysBusDevice busdev;
+ DisplayState *ds;
+ /*QEMUConsole *console;*/
+ uint32_t need_update : 1;
+ uint32_t need_int : 1;
+ uint32_t enabled : 1;
+ uint32_t int_status;
+ uint32_t int_enable;
+ qemu_irq irq;
+
+ uint32_t base;
+ uint32_t pitch;
+ uint32_t rows;
+ uint32_t cols;
+ int blank;
+ int bpp;
+ int rgb; /* 0 = BGR, 1 = RGB */
+ int endian; /* 0 = Little, 1 = Big */
+ uint32_t raw_palette[256];
+ uint32_t palette[256];
+} SyborgFBState;
+
+enum {
+ BPP_SRC_1,
+ BPP_SRC_2,
+ BPP_SRC_4,
+ BPP_SRC_8,
+ BPP_SRC_16,
+ BPP_SRC_32,
+ /* TODO: Implement these. */
+ BPP_SRC_15 = -1,
+ BPP_SRC_24 = -2
+};
+
+#include "pixel_ops.h"
+
+#define BITS 8
+#include "pl110_template.h"
+#define BITS 15
+#include "pl110_template.h"
+#define BITS 16
+#include "pl110_template.h"
+#define BITS 24
+#include "pl110_template.h"
+#define BITS 32
+#include "pl110_template.h"
+
+/* Update interrupts. */
+static void syborg_fb_update(SyborgFBState *s)
+{
+ if ((s->int_status & s->int_enable) != 0) {
+ DPRINTF("Raise IRQ\n");
+ qemu_irq_raise(s->irq);
+ } else {
+ DPRINTF("Lower IRQ\n");
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static int syborg_fb_enabled(const SyborgFBState *s)
+{
+ return s->enabled;
+}
+
+static void syborg_fb_update_palette(SyborgFBState *s)
+{
+ int n, i;
+ uint32_t raw;
+ unsigned int r, g, b;
+
+ switch (s->bpp) {
+ case BPP_SRC_1: n = 2; break;
+ case BPP_SRC_2: n = 4; break;
+ case BPP_SRC_4: n = 16; break;
+ case BPP_SRC_8: n = 256; break;
+ default: return;
+ }
+
+ for (i = 0; i < n; i++) {
+ raw = s->raw_palette[i];
+ r = (raw >> 16) & 0xff;
+ g = (raw >> 8) & 0xff;
+ b = raw & 0xff;
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 8:
+ s->palette[i] = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ s->palette[i] = rgb_to_pixel15(r, g, b);
+ break;
+ case 16:
+ s->palette[i] = rgb_to_pixel16(r, g, b);
+ break;
+ case 24:
+ case 32:
+ s->palette[i] = rgb_to_pixel32(r, g, b);
+ break;
+ default:
+ abort();
+ }
+ }
+
+}
+
+static void syborg_fb_update_display(void *opaque)
+{
+ SyborgFBState *s = (SyborgFBState *)opaque;
+ drawfn* fntable;
+ drawfn fn;
+ int dest_width;
+ int src_width;
+ int bpp_offset;
+ int first;
+ int last;
+
+ if (!syborg_fb_enabled(s))
+ return;
+
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 0:
+ return;
+ case 8:
+ fntable = pl110_draw_fn_8;
+ dest_width = 1;
+ break;
+ case 15:
+ fntable = pl110_draw_fn_15;
+ dest_width = 2;
+ break;
+ case 16:
+ fntable = pl110_draw_fn_16;
+ dest_width = 2;
+ break;
+ case 24:
+ fntable = pl110_draw_fn_24;
+ dest_width = 3;
+ break;
+ case 32:
+ fntable = pl110_draw_fn_32;
+ dest_width = 4;
+ break;
+ default:
+ fprintf(stderr, "syborg_fb: Bad color depth\n");
+ exit(1);
+ }
+
+ if (s->need_int) {
+ s->int_status |= FB_INT_BASE_UPDATE_DONE;
+ syborg_fb_update(s);
+ s->need_int = 0;
+ }
+
+ if (s->rgb) {
+ bpp_offset = 18;
+ } else {
+ bpp_offset = 0;
+ }
+ if (s->endian) {
+ bpp_offset += 6;
+ }
+
+ fn = fntable[s->bpp + bpp_offset];
+
+ if (s->pitch) {
+ src_width = s->pitch;
+ } else {
+ src_width = s->cols;
+ switch (s->bpp) {
+ case BPP_SRC_1:
+ src_width >>= 3;
+ break;
+ case BPP_SRC_2:
+ src_width >>= 2;
+ break;
+ case BPP_SRC_4:
+ src_width >>= 1;
+ break;
+ case BPP_SRC_8:
+ break;
+ case BPP_SRC_15:
+ case BPP_SRC_16:
+ src_width <<= 1;
+ break;
+ case BPP_SRC_24:
+ src_width *= 3;
+ break;
+ case BPP_SRC_32:
+ src_width <<= 2;
+ break;
+ }
+ }
+ dest_width *= s->cols;
+ first = 0;
+ /* TODO: Implement blanking. */
+ if (!s->blank) {
+ if (s->need_update && s->bpp <= BPP_SRC_8) {
+ syborg_fb_update_palette(s);
+ }
+ framebuffer_update_display(s->ds,
+ s->base, s->cols, s->rows,
+ src_width, dest_width, 0,
+ s->need_update,
+ fn, s->palette,
+ &first, &last);
+ if (first >= 0) {
+ dpy_update(s->ds, 0, first, s->cols, last - first + 1);
+ }
+
+ s->int_status |= FB_INT_VSYNC;
+ syborg_fb_update(s);
+ }
+
+ s->need_update = 0;
+}
+
+static void syborg_fb_invalidate_display(void * opaque)
+{
+ SyborgFBState *s = (SyborgFBState *)opaque;
+ s->need_update = 1;
+}
+
+static uint32_t syborg_fb_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgFBState *s = opaque;
+
+ DPRINTF("read reg %d\n", (int)offset);
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case FB_ID:
+ return SYBORG_ID_FRAMEBUFFER;
+
+ case FB_BASE:
+ return s->base;
+
+ case FB_HEIGHT:
+ return s->rows;
+
+ case FB_WIDTH:
+ return s->cols;
+
+ case FB_ORIENTATION:
+ return 0;
+
+ case FB_BLANK:
+ return s->blank;
+
+ case FB_INT_MASK:
+ return s->int_enable;
+
+ case FB_INTERRUPT_CAUSE:
+ return s->int_status;
+
+ case FB_BPP:
+ switch (s->bpp) {
+ case BPP_SRC_1: return 1;
+ case BPP_SRC_2: return 2;
+ case BPP_SRC_4: return 4;
+ case BPP_SRC_8: return 8;
+ case BPP_SRC_15: return 15;
+ case BPP_SRC_16: return 16;
+ case BPP_SRC_24: return 24;
+ case BPP_SRC_32: return 32;
+ default: return 0;
+ }
+
+ case FB_COLOR_ORDER:
+ return s->rgb;
+
+ case FB_BYTE_ORDER:
+ return s->endian;
+
+ case FB_PIXEL_ORDER:
+ return 0;
+
+ case FB_ROW_PITCH:
+ return s->pitch;
+
+ case FB_ENABLED:
+ return s->enabled;
+
+ default:
+ if ((offset >> 2) >= FB_PALETTE_START
+ && (offset >> 2) <= FB_PALETTE_END) {
+ return s->raw_palette[(offset >> 2) - FB_PALETTE_START];
+ } else {
+ cpu_abort (cpu_single_env, "syborg_fb_read: Bad offset %x\n",
+ (int)offset);
+ }
+ return 0;
+ }
+}
+
+static void syborg_fb_write(void *opaque, target_phys_addr_t offset,
+ uint32_t val)
+{
+ SyborgFBState *s = opaque;
+
+ DPRINTF("write reg %d = %d\n", (int)offset, val);
+ s->need_update = 1;
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case FB_BASE:
+ s->base = val;
+ s->need_int = 1;
+ s->need_update = 1;
+ syborg_fb_update(s);
+ break;
+
+ case FB_HEIGHT:
+ s->rows = val;
+ break;
+
+ case FB_WIDTH:
+ s->cols = val;
+ break;
+
+ case FB_ORIENTATION:
+ /* TODO: Implement rotation. */
+ break;
+
+ case FB_BLANK:
+ s->blank = val & 1;
+ break;
+
+ case FB_INT_MASK:
+ s->int_enable = val;
+ syborg_fb_update(s);
+ break;
+
+ case FB_INTERRUPT_CAUSE:
+ s->int_status &= ~val;
+ syborg_fb_update(s);
+ break;
+
+ case FB_BPP:
+ switch (val) {
+ case 1: val = BPP_SRC_1; break;
+ case 2: val = BPP_SRC_2; break;
+ case 4: val = BPP_SRC_4; break;
+ case 8: val = BPP_SRC_8; break;
+ /* case 15: val = BPP_SRC_15; break; */
+ case 16: val = BPP_SRC_16; break;
+ /* case 24: val = BPP_SRC_24; break; */
+ case 32: val = BPP_SRC_32; break;
+ default: val = s->bpp; break;
+ }
+ s->bpp = val;
+ break;
+
+ case FB_COLOR_ORDER:
+ s->rgb = (val != 0);
+ break;
+
+ case FB_BYTE_ORDER:
+ s->endian = (val != 0);
+ break;
+
+ case FB_PIXEL_ORDER:
+ /* TODO: Implement this. */
+ break;
+
+ case FB_ROW_PITCH:
+ s->pitch = val;
+ break;
+
+ case FB_ENABLED:
+ s->enabled = val;
+ break;
+
+ default:
+ if ((offset >> 2) >= FB_PALETTE_START
+ && (offset >> 2) <= FB_PALETTE_END) {
+ s->raw_palette[(offset >> 2) - FB_PALETTE_START] = val;
+ } else {
+ cpu_abort (cpu_single_env, "syborg_fb_write: Bad offset %x\n",
+ (int)offset);
+ }
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const syborg_fb_readfn[] = {
+ syborg_fb_read,
+ syborg_fb_read,
+ syborg_fb_read
+};
+
+static CPUWriteMemoryFunc * const syborg_fb_writefn[] = {
+ syborg_fb_write,
+ syborg_fb_write,
+ syborg_fb_write
+};
+
+static void syborg_fb_save(QEMUFile *f, void *opaque)
+{
+ SyborgFBState *s = opaque;
+ int i;
+
+ qemu_put_be32(f, s->need_int);
+ qemu_put_be32(f, s->int_status);
+ qemu_put_be32(f, s->int_enable);
+ qemu_put_be32(f, s->enabled);
+ qemu_put_be32(f, s->base);
+ qemu_put_be32(f, s->pitch);
+ qemu_put_be32(f, s->rows);
+ qemu_put_be32(f, s->cols);
+ qemu_put_be32(f, s->bpp);
+ qemu_put_be32(f, s->rgb);
+ for (i = 0; i < 256; i++) {
+ qemu_put_be32(f, s->raw_palette[i]);
+ }
+}
+
+static int syborg_fb_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgFBState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->need_int = qemu_get_be32(f);
+ s->int_status = qemu_get_be32(f);
+ s->int_enable = qemu_get_be32(f);
+ s->enabled = qemu_get_be32(f);
+ s->base = qemu_get_be32(f);
+ s->pitch = qemu_get_be32(f);
+ s->rows = qemu_get_be32(f);
+ s->cols = qemu_get_be32(f);
+ s->bpp = qemu_get_be32(f);
+ s->rgb = qemu_get_be32(f);
+ for (i = 0; i < 256; i++) {
+ s->raw_palette[i] = qemu_get_be32(f);
+ }
+ s->need_update = 1;
+
+ return 0;
+}
+
+static int syborg_fb_init(SysBusDevice *dev)
+{
+ SyborgFBState *s = FROM_SYSBUS(SyborgFBState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype = cpu_register_io_memory(syborg_fb_readfn,
+ syborg_fb_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ s->ds = graphic_console_init(syborg_fb_update_display,
+ syborg_fb_invalidate_display,
+ NULL, NULL, s);
+
+ if (s->cols != 0 && s->rows != 0) {
+ qemu_console_resize(s->ds, s->cols, s->rows);
+ }
+
+ if (!s->cols)
+ s->cols = ds_get_width(s->ds);
+ if (!s->rows)
+ s->rows = ds_get_height(s->ds);
+
+ register_savevm(&dev->qdev, "syborg_framebuffer", -1, 1,
+ syborg_fb_save, syborg_fb_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo syborg_fb_info = {
+ .init = syborg_fb_init,
+ .qdev.name = "syborg,framebuffer",
+ .qdev.size = sizeof(SyborgFBState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("width", SyborgFBState, cols, 0),
+ DEFINE_PROP_UINT32("height", SyborgFBState, rows, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_fb_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_fb_info);
+}
+
+device_init(syborg_fb_register_devices)
diff --git a/hw/syborg_interrupt.c b/hw/syborg_interrupt.c
new file mode 100644
index 0000000..5217983
--- /dev/null
+++ b/hw/syborg_interrupt.c
@@ -0,0 +1,238 @@
+/*
+ * Syborg interrupt controller.
+ *
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "syborg.h"
+
+//#define DEBUG_SYBORG_INT
+
+#ifdef DEBUG_SYBORG_INT
+#define DPRINTF(fmt, ...) \
+do { printf("syborg_int: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__); \
+ exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+enum {
+ INT_ID = 0,
+ INT_STATUS = 1, /* number of pending interrupts */
+ INT_CURRENT = 2, /* next interrupt to be serviced */
+ INT_DISABLE_ALL = 3,
+ INT_DISABLE = 4,
+ INT_ENABLE = 5,
+ INT_TOTAL = 6
+};
+
+typedef struct {
+ unsigned level:1;
+ unsigned enabled:1;
+} syborg_int_flags;
+
+typedef struct {
+ SysBusDevice busdev;
+ int pending_count;
+ uint32_t num_irqs;
+ syborg_int_flags *flags;
+ qemu_irq parent_irq;
+} SyborgIntState;
+
+static void syborg_int_update(SyborgIntState *s)
+{
+ DPRINTF("pending %d\n", s->pending_count);
+ qemu_set_irq(s->parent_irq, s->pending_count > 0);
+}
+
+static void syborg_int_set_irq(void *opaque, int irq, int level)
+{
+ SyborgIntState *s = (SyborgIntState *)opaque;
+
+ if (s->flags[irq].level == level)
+ return;
+
+ s->flags[irq].level = level;
+ if (s->flags[irq].enabled) {
+ if (level)
+ s->pending_count++;
+ else
+ s->pending_count--;
+ syborg_int_update(s);
+ }
+}
+
+static uint32_t syborg_int_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgIntState *s = (SyborgIntState *)opaque;
+ int i;
+
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case INT_ID:
+ return SYBORG_ID_INT;
+ case INT_STATUS:
+ DPRINTF("read status=%d\n", s->pending_count);
+ return s->pending_count;
+
+ case INT_CURRENT:
+ for (i = 0; i < s->num_irqs; i++) {
+ if (s->flags[i].level & s->flags[i].enabled) {
+ DPRINTF("read current=%d\n", i);
+ return i;
+ }
+ }
+ DPRINTF("read current=none\n");
+ return 0xffffffffu;
+
+ default:
+ cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void syborg_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ SyborgIntState *s = (SyborgIntState *)opaque;
+ int i;
+ offset &= 0xfff;
+
+ DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value);
+ switch (offset >> 2) {
+ case INT_DISABLE_ALL:
+ s->pending_count = 0;
+ for (i = 0; i < s->num_irqs; i++)
+ s->flags[i].enabled = 0;
+ break;
+
+ case INT_DISABLE:
+ if (value >= s->num_irqs)
+ break;
+ if (s->flags[value].enabled) {
+ if (s->flags[value].enabled)
+ s->pending_count--;
+ s->flags[value].enabled = 0;
+ }
+ break;
+
+ case INT_ENABLE:
+ if (value >= s->num_irqs)
+ break;
+ if (!(s->flags[value].enabled)) {
+ if(s->flags[value].level)
+ s->pending_count++;
+ s->flags[value].enabled = 1;
+ }
+ break;
+
+ default:
+ cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n",
+ (int)offset);
+ return;
+ }
+ syborg_int_update(s);
+}
+
+static CPUReadMemoryFunc * const syborg_int_readfn[] = {
+ syborg_int_read,
+ syborg_int_read,
+ syborg_int_read
+};
+
+static CPUWriteMemoryFunc * const syborg_int_writefn[] = {
+ syborg_int_write,
+ syborg_int_write,
+ syborg_int_write
+};
+
+static void syborg_int_save(QEMUFile *f, void *opaque)
+{
+ SyborgIntState *s = (SyborgIntState *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->num_irqs);
+ qemu_put_be32(f, s->pending_count);
+ for (i = 0; i < s->num_irqs; i++) {
+ qemu_put_be32(f, s->flags[i].enabled
+ | ((unsigned)s->flags[i].level << 1));
+ }
+}
+
+static int syborg_int_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgIntState *s = (SyborgIntState *)opaque;
+ uint32_t val;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ val = qemu_get_be32(f);
+ if (val != s->num_irqs)
+ return -EINVAL;
+ s->pending_count = qemu_get_be32(f);
+ for (i = 0; i < s->num_irqs; i++) {
+ val = qemu_get_be32(f);
+ s->flags[i].enabled = val & 1;
+ s->flags[i].level = (val >> 1) & 1;
+ }
+ return 0;
+}
+
+static int syborg_int_init(SysBusDevice *dev)
+{
+ SyborgIntState *s = FROM_SYSBUS(SyborgIntState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->parent_irq);
+ qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs);
+ iomemtype = cpu_register_io_memory(syborg_int_readfn,
+ syborg_int_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ s->flags = qemu_mallocz(s->num_irqs * sizeof(syborg_int_flags));
+
+ register_savevm(&dev->qdev, "syborg_int", -1, 1, syborg_int_save,
+ syborg_int_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo syborg_int_info = {
+ .init = syborg_int_init,
+ .qdev.name = "syborg,interrupt",
+ .qdev.size = sizeof(SyborgIntState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-interrupts", SyborgIntState, num_irqs, 64),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_interrupt_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_int_info);
+}
+
+device_init(syborg_interrupt_register_devices)
diff --git a/hw/syborg_keyboard.c b/hw/syborg_keyboard.c
new file mode 100644
index 0000000..d295e99
--- /dev/null
+++ b/hw/syborg_keyboard.c
@@ -0,0 +1,244 @@
+/*
+ * Syborg keyboard controller.
+ *
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "console.h"
+#include "syborg.h"
+
+//#define DEBUG_SYBORG_KEYBOARD
+
+#ifdef DEBUG_SYBORG_KEYBOARD
+#define DPRINTF(fmt, ...) \
+do { printf("syborg_keyboard: " fmt , ##args); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \
+ exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \
+} while (0)
+#endif
+
+enum {
+ KBD_ID = 0,
+ KBD_DATA = 1,
+ KBD_FIFO_COUNT = 2,
+ KBD_INT_ENABLE = 3,
+ KBD_FIFO_SIZE = 4
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ int int_enabled;
+ int extension_bit;
+ uint32_t fifo_size;
+ uint32_t *key_fifo;
+ int read_pos, read_count;
+ qemu_irq irq;
+} SyborgKeyboardState;
+
+static void syborg_keyboard_update(SyborgKeyboardState *s)
+{
+ int level = s->read_count && s->int_enabled;
+ DPRINTF("Update IRQ %d\n", level);
+ qemu_set_irq(s->irq, level);
+}
+
+static uint32_t syborg_keyboard_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
+ int c;
+
+ DPRINTF("reg read %d\n", (int)offset);
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case KBD_ID:
+ return SYBORG_ID_KEYBOARD;
+ case KBD_FIFO_COUNT:
+ return s->read_count;
+ case KBD_DATA:
+ if (s->read_count == 0) {
+ c = -1;
+ DPRINTF("FIFO underflow\n");
+ } else {
+ c = s->key_fifo[s->read_pos];
+ DPRINTF("FIFO read 0x%x\n", c);
+ s->read_count--;
+ s->read_pos++;
+ if (s->read_pos == s->fifo_size)
+ s->read_pos = 0;
+ }
+ syborg_keyboard_update(s);
+ return c;
+ case KBD_INT_ENABLE:
+ return s->int_enabled;
+ case KBD_FIFO_SIZE:
+ return s->fifo_size;
+ default:
+ cpu_abort(cpu_single_env, "syborg_keyboard_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void syborg_keyboard_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
+
+ DPRINTF("reg write %d\n", (int)offset);
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case KBD_INT_ENABLE:
+ s->int_enabled = value;
+ syborg_keyboard_update(s);
+ break;
+ default:
+ cpu_abort(cpu_single_env, "syborg_keyboard_write: Bad offset %x\n",
+ (int)offset);
+ }
+}
+
+static CPUReadMemoryFunc * const syborg_keyboard_readfn[] = {
+ syborg_keyboard_read,
+ syborg_keyboard_read,
+ syborg_keyboard_read
+};
+
+static CPUWriteMemoryFunc * const syborg_keyboard_writefn[] = {
+ syborg_keyboard_write,
+ syborg_keyboard_write,
+ syborg_keyboard_write
+};
+
+static void syborg_keyboard_event(void *opaque, int keycode)
+{
+ SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
+ int slot;
+ uint32_t val;
+
+ /* Strip off 0xe0 prefixes and reconstruct the full scancode. */
+ if (keycode == 0xe0 && !s->extension_bit) {
+ DPRINTF("Extension bit\n");
+ s->extension_bit = 0x80;
+ return;
+ }
+ val = (keycode & 0x7f) | s->extension_bit;
+ if (keycode & 0x80)
+ val |= 0x80000000u;
+ s->extension_bit = 0;
+
+ DPRINTF("FIFO push 0x%x\n", val);
+ slot = s->read_pos + s->read_count;
+ if (slot >= s->fifo_size)
+ slot -= s->fifo_size;
+
+ if (s->read_count < s->fifo_size) {
+ s->read_count++;
+ s->key_fifo[slot] = val;
+ } else {
+ fprintf(stderr, "syborg_keyboard error! FIFO overflow\n");
+ }
+
+ syborg_keyboard_update(s);
+}
+
+static void syborg_keyboard_save(QEMUFile *f, void *opaque)
+{
+ SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->fifo_size);
+ qemu_put_be32(f, s->int_enabled);
+ qemu_put_be32(f, s->extension_bit);
+ qemu_put_be32(f, s->read_pos);
+ qemu_put_be32(f, s->read_count);
+ for (i = 0; i < s->fifo_size; i++) {
+ qemu_put_be32(f, s->key_fifo[i]);
+ }
+}
+
+static int syborg_keyboard_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgKeyboardState *s = (SyborgKeyboardState *)opaque;
+ uint32_t val;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ val = qemu_get_be32(f);
+ if (val != s->fifo_size)
+ return -EINVAL;
+
+ s->int_enabled = qemu_get_be32(f);
+ s->extension_bit = qemu_get_be32(f);
+ s->read_pos = qemu_get_be32(f);
+ s->read_count = qemu_get_be32(f);
+ for (i = 0; i < s->fifo_size; i++) {
+ s->key_fifo[i] = qemu_get_be32(f);
+ }
+ return 0;
+}
+
+static int syborg_keyboard_init(SysBusDevice *dev)
+{
+ SyborgKeyboardState *s = FROM_SYSBUS(SyborgKeyboardState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype = cpu_register_io_memory(syborg_keyboard_readfn,
+ syborg_keyboard_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ if (s->fifo_size <= 0) {
+ fprintf(stderr, "syborg_keyboard: fifo too small\n");
+ s->fifo_size = 16;
+ }
+ s->key_fifo = qemu_mallocz(s->fifo_size * sizeof(s->key_fifo[0]));
+
+ qemu_add_kbd_event_handler(syborg_keyboard_event, s);
+
+ register_savevm(&dev->qdev, "syborg_keyboard", -1, 1,
+ syborg_keyboard_save, syborg_keyboard_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo syborg_keyboard_info = {
+ .init = syborg_keyboard_init,
+ .qdev.name = "syborg,keyboard",
+ .qdev.size = sizeof(SyborgKeyboardState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("fifo-size", SyborgKeyboardState, fifo_size, 16),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_keyboard_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_keyboard_info);
+}
+
+device_init(syborg_keyboard_register_devices)
diff --git a/hw/syborg_pointer.c b/hw/syborg_pointer.c
new file mode 100644
index 0000000..a886888
--- /dev/null
+++ b/hw/syborg_pointer.c
@@ -0,0 +1,243 @@
+/*
+ * Syborg pointing device (mouse/touchscreen)
+ *
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "console.h"
+#include "syborg.h"
+
+enum {
+ POINTER_ID = 0,
+ POINTER_LATCH = 1,
+ POINTER_FIFO_COUNT = 2,
+ POINTER_X = 3,
+ POINTER_Y = 4,
+ POINTER_Z = 5,
+ POINTER_BUTTONS = 6,
+ POINTER_INT_ENABLE = 7,
+ POINTER_FIFO_SIZE = 8
+};
+
+typedef struct {
+ int x, y, z, pointer_buttons;
+} event_data;
+
+typedef struct {
+ SysBusDevice busdev;
+ int int_enabled;
+ uint32_t fifo_size;
+ event_data *event_fifo;
+ int read_pos, read_count;
+ qemu_irq irq;
+ uint32_t absolute;
+} SyborgPointerState;
+
+static void syborg_pointer_update(SyborgPointerState *s)
+{
+ qemu_set_irq(s->irq, s->read_count && s->int_enabled);
+}
+
+static uint32_t syborg_pointer_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgPointerState *s = (SyborgPointerState *)opaque;
+
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case POINTER_ID:
+ return s->absolute ? SYBORG_ID_TOUCHSCREEN : SYBORG_ID_MOUSE;
+ case POINTER_FIFO_COUNT:
+ return s->read_count;
+ case POINTER_X:
+ return s->event_fifo[s->read_pos].x;
+ case POINTER_Y:
+ return s->event_fifo[s->read_pos].y;
+ case POINTER_Z:
+ return s->event_fifo[s->read_pos].z;
+ case POINTER_BUTTONS:
+ return s->event_fifo[s->read_pos].pointer_buttons;
+ case POINTER_INT_ENABLE:
+ return s->int_enabled;
+ case POINTER_FIFO_SIZE:
+ return s->fifo_size;
+ default:
+ cpu_abort(cpu_single_env, "syborg_pointer_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void syborg_pointer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgPointerState *s = (SyborgPointerState *)opaque;
+
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case POINTER_LATCH:
+ if (s->read_count > 0) {
+ s->read_count--;
+ if (++s->read_pos == s->fifo_size)
+ s->read_pos = 0;
+ }
+ break;
+ case POINTER_INT_ENABLE:
+ s->int_enabled = value;
+ break;
+ default:
+ cpu_abort(cpu_single_env, "syborg_pointer_write: Bad offset %x\n",
+ (int)offset);
+ }
+ syborg_pointer_update(s);
+}
+
+static CPUReadMemoryFunc * const syborg_pointer_readfn[] = {
+ syborg_pointer_read,
+ syborg_pointer_read,
+ syborg_pointer_read
+};
+
+static CPUWriteMemoryFunc * const syborg_pointer_writefn[] = {
+ syborg_pointer_write,
+ syborg_pointer_write,
+ syborg_pointer_write
+};
+
+static void syborg_pointer_event(void *opaque, int dx, int dy, int dz,
+ int buttons_state)
+{
+ SyborgPointerState *s = (SyborgPointerState *)opaque;
+ int slot = s->read_pos + s->read_count;
+
+ /* This first FIFO entry is used to store current register state. */
+ if (s->read_count < s->fifo_size - 1) {
+ s->read_count++;
+ slot++;
+ }
+
+ if (slot >= s->fifo_size)
+ slot -= s->fifo_size;
+
+ if (s->read_count == s->fifo_size && !s->absolute) {
+ /* Merge existing entries. */
+ s->event_fifo[slot].x += dx;
+ s->event_fifo[slot].y += dy;
+ s->event_fifo[slot].z += dz;
+ } else {
+ s->event_fifo[slot].x = dx;
+ s->event_fifo[slot].y = dy;
+ s->event_fifo[slot].z = dz;
+ }
+ s->event_fifo[slot].pointer_buttons = buttons_state;
+
+ syborg_pointer_update(s);
+}
+
+static void syborg_pointer_save(QEMUFile *f, void *opaque)
+{
+ SyborgPointerState *s = (SyborgPointerState *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->fifo_size);
+ qemu_put_be32(f, s->absolute);
+ qemu_put_be32(f, s->int_enabled);
+ qemu_put_be32(f, s->read_pos);
+ qemu_put_be32(f, s->read_count);
+ for (i = 0; i < s->fifo_size; i++) {
+ qemu_put_be32(f, s->event_fifo[i].x);
+ qemu_put_be32(f, s->event_fifo[i].y);
+ qemu_put_be32(f, s->event_fifo[i].z);
+ qemu_put_be32(f, s->event_fifo[i].pointer_buttons);
+ }
+}
+
+static int syborg_pointer_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgPointerState *s = (SyborgPointerState *)opaque;
+ uint32_t val;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ val = qemu_get_be32(f);
+ if (val != s->fifo_size)
+ return -EINVAL;
+
+ val = qemu_get_be32(f);
+ if (val != s->absolute)
+ return -EINVAL;
+
+ s->int_enabled = qemu_get_be32(f);
+ s->read_pos = qemu_get_be32(f);
+ s->read_count = qemu_get_be32(f);
+ for (i = 0; i < s->fifo_size; i++) {
+ s->event_fifo[i].x = qemu_get_be32(f);
+ s->event_fifo[i].y = qemu_get_be32(f);
+ s->event_fifo[i].z = qemu_get_be32(f);
+ s->event_fifo[i].pointer_buttons = qemu_get_be32(f);
+ }
+ return 0;
+}
+
+static int syborg_pointer_init(SysBusDevice *dev)
+{
+ SyborgPointerState *s = FROM_SYSBUS(SyborgPointerState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype = cpu_register_io_memory(syborg_pointer_readfn,
+ syborg_pointer_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ if (s->fifo_size <= 0) {
+ fprintf(stderr, "syborg_pointer: fifo too small\n");
+ s->fifo_size = 16;
+ }
+ s->event_fifo = qemu_mallocz(s->fifo_size * sizeof(s->event_fifo[0]));
+
+ qemu_add_mouse_event_handler(syborg_pointer_event, s, s->absolute,
+ "Syborg Pointer");
+
+ register_savevm(&dev->qdev, "syborg_pointer", -1, 1,
+ syborg_pointer_save, syborg_pointer_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo syborg_pointer_info = {
+ .init = syborg_pointer_init,
+ .qdev.name = "syborg,pointer",
+ .qdev.size = sizeof(SyborgPointerState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("fifo-size", SyborgPointerState, fifo_size, 16),
+ DEFINE_PROP_UINT32("absolute", SyborgPointerState, absolute, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_pointer_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_pointer_info);
+}
+
+device_init(syborg_pointer_register_devices)
diff --git a/hw/syborg_rtc.c b/hw/syborg_rtc.c
new file mode 100644
index 0000000..329aa42
--- /dev/null
+++ b/hw/syborg_rtc.c
@@ -0,0 +1,150 @@
+/*
+ * Syborg RTC
+ *
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "syborg.h"
+
+enum {
+ RTC_ID = 0,
+ RTC_LATCH = 1,
+ RTC_DATA_LOW = 2,
+ RTC_DATA_HIGH = 3
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ int64_t offset;
+ int64_t data;
+ qemu_irq irq;
+} SyborgRTCState;
+
+static uint32_t syborg_rtc_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgRTCState *s = (SyborgRTCState *)opaque;
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case RTC_ID:
+ return SYBORG_ID_RTC;
+ case RTC_DATA_LOW:
+ return (uint32_t)s->data;
+ case RTC_DATA_HIGH:
+ return (uint32_t)(s->data >> 32);
+ default:
+ cpu_abort(cpu_single_env, "syborg_rtc_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void syborg_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+ SyborgRTCState *s = (SyborgRTCState *)opaque;
+ uint64_t now;
+
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case RTC_LATCH:
+ now = qemu_get_clock(vm_clock);
+ if (value >= 4) {
+ s->offset = s->data - now;
+ } else {
+ s->data = now + s->offset;
+ while (value) {
+ s->data /= 1000;
+ value--;
+ }
+ }
+ break;
+ case RTC_DATA_LOW:
+ s->data = (s->data & ~(uint64_t)0xffffffffu) | value;
+ break;
+ case RTC_DATA_HIGH:
+ s->data = (s->data & 0xffffffffu) | ((uint64_t)value << 32);
+ break;
+ default:
+ cpu_abort(cpu_single_env, "syborg_rtc_write: Bad offset %x\n",
+ (int)offset);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const syborg_rtc_readfn[] = {
+ syborg_rtc_read,
+ syborg_rtc_read,
+ syborg_rtc_read
+};
+
+static CPUWriteMemoryFunc * const syborg_rtc_writefn[] = {
+ syborg_rtc_write,
+ syborg_rtc_write,
+ syborg_rtc_write
+};
+
+static void syborg_rtc_save(QEMUFile *f, void *opaque)
+{
+ SyborgRTCState *s = opaque;
+
+ qemu_put_be64(f, s->offset);
+ qemu_put_be64(f, s->data);
+}
+
+static int syborg_rtc_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgRTCState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->offset = qemu_get_be64(f);
+ s->data = qemu_get_be64(f);
+
+ return 0;
+}
+
+static int syborg_rtc_init(SysBusDevice *dev)
+{
+ SyborgRTCState *s = FROM_SYSBUS(SyborgRTCState, dev);
+ struct tm tm;
+ int iomemtype;
+
+ iomemtype = cpu_register_io_memory(syborg_rtc_readfn,
+ syborg_rtc_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ qemu_get_timedate(&tm, 0);
+ s->offset = (uint64_t)mktime(&tm) * 1000000000;
+
+ register_savevm(&dev->qdev, "syborg_rtc", -1, 1,
+ syborg_rtc_save, syborg_rtc_load, s);
+ return 0;
+}
+
+static void syborg_rtc_register_devices(void)
+{
+ sysbus_register_dev("syborg,rtc", sizeof(SyborgRTCState), syborg_rtc_init);
+}
+
+device_init(syborg_rtc_register_devices)
diff --git a/hw/syborg_serial.c b/hw/syborg_serial.c
new file mode 100644
index 0000000..34ce076
--- /dev/null
+++ b/hw/syborg_serial.c
@@ -0,0 +1,359 @@
+/*
+ * Syborg serial port
+ *
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+#include "syborg.h"
+
+//#define DEBUG_SYBORG_SERIAL
+
+#ifdef DEBUG_SYBORG_SERIAL
+#define DPRINTF(fmt, ...) \
+do { printf("syborg_serial: " fmt , ##args); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__); \
+ exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+ SERIAL_ID = 0,
+ SERIAL_DATA = 1,
+ SERIAL_FIFO_COUNT = 2,
+ SERIAL_INT_ENABLE = 3,
+ SERIAL_DMA_TX_ADDR = 4,
+ SERIAL_DMA_TX_COUNT = 5, /* triggers dma */
+ SERIAL_DMA_RX_ADDR = 6,
+ SERIAL_DMA_RX_COUNT = 7, /* triggers dma */
+ SERIAL_FIFO_SIZE = 8
+};
+
+#define SERIAL_INT_FIFO (1u << 0)
+#define SERIAL_INT_DMA_TX (1u << 1)
+#define SERIAL_INT_DMA_RX (1u << 2)
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t int_enable;
+ uint32_t fifo_size;
+ uint32_t *read_fifo;
+ int read_pos;
+ int read_count;
+ CharDriverState *chr;
+ qemu_irq irq;
+ uint32_t dma_tx_ptr;
+ uint32_t dma_rx_ptr;
+ uint32_t dma_rx_size;
+} SyborgSerialState;
+
+static void syborg_serial_update(SyborgSerialState *s)
+{
+ int level;
+ level = 0;
+ if ((s->int_enable & SERIAL_INT_FIFO) && s->read_count)
+ level = 1;
+ if (s->int_enable & SERIAL_INT_DMA_TX)
+ level = 1;
+ if ((s->int_enable & SERIAL_INT_DMA_RX) && s->dma_rx_size == 0)
+ level = 1;
+
+ qemu_set_irq(s->irq, level);
+}
+
+static uint32_t fifo_pop(SyborgSerialState *s)
+{
+ const uint32_t c = s->read_fifo[s->read_pos];
+ s->read_count--;
+ s->read_pos++;
+ if (s->read_pos == s->fifo_size)
+ s->read_pos = 0;
+
+ DPRINTF("FIFO pop %x (%d)\n", c, s->read_count);
+ return c;
+}
+
+static void fifo_push(SyborgSerialState *s, uint32_t new_value)
+{
+ int slot;
+
+ DPRINTF("FIFO push %x (%d)\n", new_value, s->read_count);
+ slot = s->read_pos + s->read_count;
+ if (slot >= s->fifo_size)
+ slot -= s->fifo_size;
+ s->read_fifo[slot] = new_value;
+ s->read_count++;
+}
+
+static void do_dma_tx(SyborgSerialState *s, uint32_t count)
+{
+ unsigned char ch;
+
+ if (count == 0)
+ return;
+
+ if (s->chr != NULL) {
+ /* optimize later. Now, 1 byte per iteration */
+ while (count--) {
+ cpu_physical_memory_read(s->dma_tx_ptr, &ch, 1);
+ qemu_chr_write(s->chr, &ch, 1);
+ s->dma_tx_ptr++;
+ }
+ } else {
+ s->dma_tx_ptr += count;
+ }
+ /* QEMU char backends do not have a nonblocking mode, so we transmit all
+ the data imediately and the interrupt status will be unchanged. */
+}
+
+/* Initiate RX DMA, and transfer data from the FIFO. */
+static void dma_rx_start(SyborgSerialState *s, uint32_t len)
+{
+ uint32_t dest;
+ unsigned char ch;
+
+ dest = s->dma_rx_ptr;
+ if (s->read_count < len) {
+ s->dma_rx_size = len - s->read_count;
+ len = s->read_count;
+ } else {
+ s->dma_rx_size = 0;
+ }
+
+ while (len--) {
+ ch = fifo_pop(s);
+ cpu_physical_memory_write(dest, &ch, 1);
+ dest++;
+ }
+ s->dma_rx_ptr = dest;
+ syborg_serial_update(s);
+}
+
+static uint32_t syborg_serial_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgSerialState *s = (SyborgSerialState *)opaque;
+ uint32_t c;
+
+ offset &= 0xfff;
+ DPRINTF("read 0x%x\n", (int)offset);
+ switch(offset >> 2) {
+ case SERIAL_ID:
+ return SYBORG_ID_SERIAL;
+ case SERIAL_DATA:
+ if (s->read_count > 0)
+ c = fifo_pop(s);
+ else
+ c = -1;
+ syborg_serial_update(s);
+ return c;
+ case SERIAL_FIFO_COUNT:
+ return s->read_count;
+ case SERIAL_INT_ENABLE:
+ return s->int_enable;
+ case SERIAL_DMA_TX_ADDR:
+ return s->dma_tx_ptr;
+ case SERIAL_DMA_TX_COUNT:
+ return 0;
+ case SERIAL_DMA_RX_ADDR:
+ return s->dma_rx_ptr;
+ case SERIAL_DMA_RX_COUNT:
+ return s->dma_rx_size;
+ case SERIAL_FIFO_SIZE:
+ return s->fifo_size;
+
+ default:
+ cpu_abort(cpu_single_env, "syborg_serial_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void syborg_serial_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgSerialState *s = (SyborgSerialState *)opaque;
+ unsigned char ch;
+
+ offset &= 0xfff;
+ DPRINTF("Write 0x%x=0x%x\n", (int)offset, value);
+ switch (offset >> 2) {
+ case SERIAL_DATA:
+ ch = value;
+ if (s->chr)
+ qemu_chr_write(s->chr, &ch, 1);
+ break;
+ case SERIAL_INT_ENABLE:
+ s->int_enable = value;
+ syborg_serial_update(s);
+ break;
+ case SERIAL_DMA_TX_ADDR:
+ s->dma_tx_ptr = value;
+ break;
+ case SERIAL_DMA_TX_COUNT:
+ do_dma_tx(s, value);
+ break;
+ case SERIAL_DMA_RX_ADDR:
+ /* For safety, writes to this register cancel any pending DMA. */
+ s->dma_rx_size = 0;
+ s->dma_rx_ptr = value;
+ break;
+ case SERIAL_DMA_RX_COUNT:
+ dma_rx_start(s, value);
+ break;
+ default:
+ cpu_abort(cpu_single_env, "syborg_serial_write: Bad offset %x\n",
+ (int)offset);
+ break;
+ }
+}
+
+static int syborg_serial_can_receive(void *opaque)
+{
+ SyborgSerialState *s = (SyborgSerialState *)opaque;
+
+ if (s->dma_rx_size)
+ return s->dma_rx_size;
+ return s->fifo_size - s->read_count;
+}
+
+static void syborg_serial_receive(void *opaque, const uint8_t *buf, int size)
+{
+ SyborgSerialState *s = (SyborgSerialState *)opaque;
+
+ if (s->dma_rx_size) {
+ /* Place it in the DMA buffer. */
+ cpu_physical_memory_write(s->dma_rx_ptr, buf, size);
+ s->dma_rx_size -= size;
+ s->dma_rx_ptr += size;
+ } else {
+ while (size--)
+ fifo_push(s, *buf);
+ }
+
+ syborg_serial_update(s);
+}
+
+static void syborg_serial_event(void *opaque, int event)
+{
+ /* TODO: Report BREAK events? */
+}
+
+static CPUReadMemoryFunc * const syborg_serial_readfn[] = {
+ syborg_serial_read,
+ syborg_serial_read,
+ syborg_serial_read
+};
+
+static CPUWriteMemoryFunc * const syborg_serial_writefn[] = {
+ syborg_serial_write,
+ syborg_serial_write,
+ syborg_serial_write
+};
+
+static void syborg_serial_save(QEMUFile *f, void *opaque)
+{
+ SyborgSerialState *s = opaque;
+ int i;
+
+ qemu_put_be32(f, s->fifo_size);
+ qemu_put_be32(f, s->int_enable);
+ qemu_put_be32(f, s->read_pos);
+ qemu_put_be32(f, s->read_count);
+ qemu_put_be32(f, s->dma_tx_ptr);
+ qemu_put_be32(f, s->dma_rx_ptr);
+ qemu_put_be32(f, s->dma_rx_size);
+ for (i = 0; i < s->fifo_size; i++) {
+ qemu_put_be32(f, s->read_fifo[i]);
+ }
+}
+
+static int syborg_serial_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgSerialState *s = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ i = qemu_get_be32(f);
+ if (s->fifo_size != i)
+ return -EINVAL;
+
+ s->int_enable = qemu_get_be32(f);
+ s->read_pos = qemu_get_be32(f);
+ s->read_count = qemu_get_be32(f);
+ s->dma_tx_ptr = qemu_get_be32(f);
+ s->dma_rx_ptr = qemu_get_be32(f);
+ s->dma_rx_size = qemu_get_be32(f);
+ for (i = 0; i < s->fifo_size; i++) {
+ s->read_fifo[i] = qemu_get_be32(f);
+ }
+
+ return 0;
+}
+
+static int syborg_serial_init(SysBusDevice *dev)
+{
+ SyborgSerialState *s = FROM_SYSBUS(SyborgSerialState, dev);
+ int iomemtype;
+
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype = cpu_register_io_memory(syborg_serial_readfn,
+ syborg_serial_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ s->chr = qdev_init_chardev(&dev->qdev);
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, syborg_serial_can_receive,
+ syborg_serial_receive, syborg_serial_event, s);
+ }
+ if (s->fifo_size <= 0) {
+ fprintf(stderr, "syborg_serial: fifo too small\n");
+ s->fifo_size = 16;
+ }
+ s->read_fifo = qemu_mallocz(s->fifo_size * sizeof(s->read_fifo[0]));
+
+ register_savevm(&dev->qdev, "syborg_serial", -1, 1,
+ syborg_serial_save, syborg_serial_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo syborg_serial_info = {
+ .init = syborg_serial_init,
+ .qdev.name = "syborg,serial",
+ .qdev.size = sizeof(SyborgSerialState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("fifo-size", SyborgSerialState, fifo_size, 16),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_serial_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_serial_info);
+}
+
+device_init(syborg_serial_register_devices)
diff --git a/hw/syborg_timer.c b/hw/syborg_timer.c
new file mode 100644
index 0000000..cedcd8e
--- /dev/null
+++ b/hw/syborg_timer.c
@@ -0,0 +1,245 @@
+/*
+ * Syborg Interval Timer.
+ *
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "syborg.h"
+
+//#define DEBUG_SYBORG_TIMER
+
+#ifdef DEBUG_SYBORG_TIMER
+#define DPRINTF(fmt, ...) \
+do { printf("syborg_timer: " fmt , ##args); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__); \
+ exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+ TIMER_ID = 0,
+ TIMER_RUNNING = 1,
+ TIMER_ONESHOT = 2,
+ TIMER_LIMIT = 3,
+ TIMER_VALUE = 4,
+ TIMER_INT_ENABLE = 5,
+ TIMER_INT_STATUS = 6,
+ TIMER_FREQ = 7
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ ptimer_state *timer;
+ int running;
+ int oneshot;
+ uint32_t limit;
+ uint32_t freq;
+ uint32_t int_level;
+ uint32_t int_enabled;
+ qemu_irq irq;
+} SyborgTimerState;
+
+static void syborg_timer_update(SyborgTimerState *s)
+{
+ /* Update interrupt. */
+ if (s->int_level && s->int_enabled) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void syborg_timer_tick(void *opaque)
+{
+ SyborgTimerState *s = (SyborgTimerState *)opaque;
+ //DPRINTF("Timer Tick\n");
+ s->int_level = 1;
+ if (s->oneshot)
+ s->running = 0;
+ syborg_timer_update(s);
+}
+
+static uint32_t syborg_timer_read(void *opaque, target_phys_addr_t offset)
+{
+ SyborgTimerState *s = (SyborgTimerState *)opaque;
+
+ DPRINTF("Reg read %d\n", (int)offset);
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case TIMER_ID:
+ return SYBORG_ID_TIMER;
+ case TIMER_RUNNING:
+ return s->running;
+ case TIMER_ONESHOT:
+ return s->oneshot;
+ case TIMER_LIMIT:
+ return s->limit;
+ case TIMER_VALUE:
+ return ptimer_get_count(s->timer);
+ case TIMER_INT_ENABLE:
+ return s->int_enabled;
+ case TIMER_INT_STATUS:
+ return s->int_level;
+ case TIMER_FREQ:
+ return s->freq;
+ default:
+ cpu_abort(cpu_single_env, "syborg_timer_read: Bad offset %x\n",
+ (int)offset);
+ return 0;
+ }
+}
+
+static void syborg_timer_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgTimerState *s = (SyborgTimerState *)opaque;
+
+ DPRINTF("Reg write %d\n", (int)offset);
+ offset &= 0xfff;
+ switch (offset >> 2) {
+ case TIMER_RUNNING:
+ if (value == s->running)
+ break;
+ s->running = value;
+ if (value) {
+ ptimer_run(s->timer, s->oneshot);
+ } else {
+ ptimer_stop(s->timer);
+ }
+ break;
+ case TIMER_ONESHOT:
+ if (s->running) {
+ ptimer_stop(s->timer);
+ }
+ s->oneshot = value;
+ if (s->running) {
+ ptimer_run(s->timer, s->oneshot);
+ }
+ break;
+ case TIMER_LIMIT:
+ s->limit = value;
+ ptimer_set_limit(s->timer, value, 1);
+ break;
+ case TIMER_VALUE:
+ ptimer_set_count(s->timer, value);
+ break;
+ case TIMER_INT_ENABLE:
+ s->int_enabled = value;
+ syborg_timer_update(s);
+ break;
+ case TIMER_INT_STATUS:
+ s->int_level &= ~value;
+ syborg_timer_update(s);
+ break;
+ default:
+ cpu_abort(cpu_single_env, "syborg_timer_write: Bad offset %x\n",
+ (int)offset);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const syborg_timer_readfn[] = {
+ syborg_timer_read,
+ syborg_timer_read,
+ syborg_timer_read
+};
+
+static CPUWriteMemoryFunc * const syborg_timer_writefn[] = {
+ syborg_timer_write,
+ syborg_timer_write,
+ syborg_timer_write
+};
+
+static void syborg_timer_save(QEMUFile *f, void *opaque)
+{
+ SyborgTimerState *s = opaque;
+
+ qemu_put_be32(f, s->running);
+ qemu_put_be32(f, s->oneshot);
+ qemu_put_be32(f, s->limit);
+ qemu_put_be32(f, s->int_level);
+ qemu_put_be32(f, s->int_enabled);
+ qemu_put_ptimer(f, s->timer);
+}
+
+static int syborg_timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SyborgTimerState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->running = qemu_get_be32(f);
+ s->oneshot = qemu_get_be32(f);
+ s->limit = qemu_get_be32(f);
+ s->int_level = qemu_get_be32(f);
+ s->int_enabled = qemu_get_be32(f);
+ qemu_get_ptimer(f, s->timer);
+
+ return 0;
+}
+
+static int syborg_timer_init(SysBusDevice *dev)
+{
+ SyborgTimerState *s = FROM_SYSBUS(SyborgTimerState, dev);
+ QEMUBH *bh;
+ int iomemtype;
+
+ if (s->freq == 0) {
+ fprintf(stderr, "syborg_timer: Zero/unset frequency\n");
+ exit(1);
+ }
+ sysbus_init_irq(dev, &s->irq);
+ iomemtype = cpu_register_io_memory(syborg_timer_readfn,
+ syborg_timer_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ bh = qemu_bh_new(syborg_timer_tick, s);
+ s->timer = ptimer_init(bh);
+ ptimer_set_freq(s->timer, s->freq);
+ register_savevm(&dev->qdev, "syborg_timer", -1, 1,
+ syborg_timer_save, syborg_timer_load, s);
+ return 0;
+}
+
+static SysBusDeviceInfo syborg_timer_info = {
+ .init = syborg_timer_init,
+ .qdev.name = "syborg,timer",
+ .qdev.size = sizeof(SyborgTimerState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("frequency",SyborgTimerState, freq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_timer_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_timer_info);
+}
+
+device_init(syborg_timer_register_devices)
diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c
new file mode 100644
index 0000000..ee08c49
--- /dev/null
+++ b/hw/syborg_virtio.c
@@ -0,0 +1,314 @@
+/*
+ * Virtio Syborg bindings
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "syborg.h"
+#include "sysbus.h"
+#include "virtio.h"
+#include "virtio-net.h"
+#include "sysemu.h"
+
+//#define DEBUG_SYBORG_VIRTIO
+
+#ifdef DEBUG_SYBORG_VIRTIO
+#define DPRINTF(fmt, ...) \
+do { printf("syborg_virtio: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_virtio: error: " fmt , ## __VA_ARGS__); \
+ exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "syborg_virtio: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+ SYBORG_VIRTIO_ID = 0,
+ SYBORG_VIRTIO_DEVTYPE = 1,
+ SYBORG_VIRTIO_HOST_FEATURES = 2,
+ SYBORG_VIRTIO_GUEST_FEATURES = 3,
+ SYBORG_VIRTIO_QUEUE_BASE = 4,
+ SYBORG_VIRTIO_QUEUE_NUM = 5,
+ SYBORG_VIRTIO_QUEUE_SEL = 6,
+ SYBORG_VIRTIO_QUEUE_NOTIFY = 7,
+ SYBORG_VIRTIO_STATUS = 8,
+ SYBORG_VIRTIO_INT_ENABLE = 9,
+ SYBORG_VIRTIO_INT_STATUS = 10
+};
+
+#define SYBORG_VIRTIO_CONFIG 0x100
+
+/* Device independent interface. */
+
+typedef struct {
+ SysBusDevice busdev;
+ VirtIODevice *vdev;
+ qemu_irq irq;
+ uint32_t int_enable;
+ uint32_t id;
+ NICConf nic;
+ uint32_t host_features;
+ virtio_net_conf net;
+} SyborgVirtIOProxy;
+
+static uint32_t syborg_virtio_readl(void *opaque, target_phys_addr_t offset)
+{
+ SyborgVirtIOProxy *s = opaque;
+ VirtIODevice *vdev = s->vdev;
+ uint32_t ret;
+
+ DPRINTF("readl 0x%x\n", (int)offset);
+ if (offset >= SYBORG_VIRTIO_CONFIG) {
+ return virtio_config_readl(vdev, offset - SYBORG_VIRTIO_CONFIG);
+ }
+ switch(offset >> 2) {
+ case SYBORG_VIRTIO_ID:
+ ret = SYBORG_ID_VIRTIO;
+ break;
+ case SYBORG_VIRTIO_DEVTYPE:
+ ret = s->id;
+ break;
+ case SYBORG_VIRTIO_HOST_FEATURES:
+ ret = s->host_features;
+ break;
+ case SYBORG_VIRTIO_GUEST_FEATURES:
+ ret = vdev->guest_features;
+ break;
+ case SYBORG_VIRTIO_QUEUE_BASE:
+ ret = virtio_queue_get_addr(vdev, vdev->queue_sel);
+ break;
+ case SYBORG_VIRTIO_QUEUE_NUM:
+ ret = virtio_queue_get_num(vdev, vdev->queue_sel);
+ break;
+ case SYBORG_VIRTIO_QUEUE_SEL:
+ ret = vdev->queue_sel;
+ break;
+ case SYBORG_VIRTIO_STATUS:
+ ret = vdev->status;
+ break;
+ case SYBORG_VIRTIO_INT_ENABLE:
+ ret = s->int_enable;
+ break;
+ case SYBORG_VIRTIO_INT_STATUS:
+ ret = vdev->isr;
+ break;
+ default:
+ BADF("Bad read offset 0x%x\n", (int)offset);
+ return 0;
+ }
+ return ret;
+}
+
+static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgVirtIOProxy *s = opaque;
+ VirtIODevice *vdev = s->vdev;
+
+ DPRINTF("writel 0x%x = 0x%x\n", (int)offset, value);
+ if (offset >= SYBORG_VIRTIO_CONFIG) {
+ return virtio_config_writel(vdev, offset - SYBORG_VIRTIO_CONFIG,
+ value);
+ }
+ switch (offset >> 2) {
+ case SYBORG_VIRTIO_GUEST_FEATURES:
+ if (vdev->set_features)
+ vdev->set_features(vdev, value);
+ vdev->guest_features = value;
+ break;
+ case SYBORG_VIRTIO_QUEUE_BASE:
+ if (value == 0)
+ virtio_reset(vdev);
+ else
+ virtio_queue_set_addr(vdev, vdev->queue_sel, value);
+ break;
+ case SYBORG_VIRTIO_QUEUE_SEL:
+ if (value < VIRTIO_PCI_QUEUE_MAX)
+ vdev->queue_sel = value;
+ break;
+ case SYBORG_VIRTIO_QUEUE_NOTIFY:
+ virtio_queue_notify(vdev, value);
+ break;
+ case SYBORG_VIRTIO_STATUS:
+ virtio_set_status(vdev, value & 0xFF);
+ if (vdev->status == 0)
+ virtio_reset(vdev);
+ break;
+ case SYBORG_VIRTIO_INT_ENABLE:
+ s->int_enable = value;
+ virtio_update_irq(vdev);
+ break;
+ case SYBORG_VIRTIO_INT_STATUS:
+ vdev->isr &= ~value;
+ virtio_update_irq(vdev);
+ break;
+ default:
+ BADF("Bad write offset 0x%x\n", (int)offset);
+ break;
+ }
+}
+
+static uint32_t syborg_virtio_readw(void *opaque, target_phys_addr_t offset)
+{
+ SyborgVirtIOProxy *s = opaque;
+ VirtIODevice *vdev = s->vdev;
+
+ DPRINTF("readw 0x%x\n", (int)offset);
+ if (offset >= SYBORG_VIRTIO_CONFIG) {
+ return virtio_config_readw(vdev, offset - SYBORG_VIRTIO_CONFIG);
+ }
+ BADF("Bad halfword read offset 0x%x\n", (int)offset);
+ return -1;
+}
+
+static void syborg_virtio_writew(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgVirtIOProxy *s = opaque;
+ VirtIODevice *vdev = s->vdev;
+
+ DPRINTF("writew 0x%x = 0x%x\n", (int)offset, value);
+ if (offset >= SYBORG_VIRTIO_CONFIG) {
+ return virtio_config_writew(vdev, offset - SYBORG_VIRTIO_CONFIG,
+ value);
+ }
+ BADF("Bad halfword write offset 0x%x\n", (int)offset);
+}
+
+static uint32_t syborg_virtio_readb(void *opaque, target_phys_addr_t offset)
+{
+ SyborgVirtIOProxy *s = opaque;
+ VirtIODevice *vdev = s->vdev;
+
+ DPRINTF("readb 0x%x\n", (int)offset);
+ if (offset >= SYBORG_VIRTIO_CONFIG) {
+ return virtio_config_readb(vdev, offset - SYBORG_VIRTIO_CONFIG);
+ }
+ BADF("Bad byte read offset 0x%x\n", (int)offset);
+ return -1;
+}
+
+static void syborg_virtio_writeb(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ SyborgVirtIOProxy *s = opaque;
+ VirtIODevice *vdev = s->vdev;
+
+ DPRINTF("writeb 0x%x = 0x%x\n", (int)offset, value);
+ if (offset >= SYBORG_VIRTIO_CONFIG) {
+ return virtio_config_writeb(vdev, offset - SYBORG_VIRTIO_CONFIG,
+ value);
+ }
+ BADF("Bad byte write offset 0x%x\n", (int)offset);
+}
+
+static CPUReadMemoryFunc * const syborg_virtio_readfn[] = {
+ syborg_virtio_readb,
+ syborg_virtio_readw,
+ syborg_virtio_readl
+};
+
+static CPUWriteMemoryFunc * const syborg_virtio_writefn[] = {
+ syborg_virtio_writeb,
+ syborg_virtio_writew,
+ syborg_virtio_writel
+};
+
+static void syborg_virtio_update_irq(void *opaque, uint16_t vector)
+{
+ SyborgVirtIOProxy *proxy = opaque;
+ int level;
+
+ level = proxy->int_enable & proxy->vdev->isr;
+ DPRINTF("IRQ %d\n", level);
+ qemu_set_irq(proxy->irq, level != 0);
+}
+
+static unsigned syborg_virtio_get_features(void *opaque)
+{
+ SyborgVirtIOProxy *proxy = opaque;
+ return proxy->host_features;
+}
+
+static VirtIOBindings syborg_virtio_bindings = {
+ .notify = syborg_virtio_update_irq,
+ .get_features = syborg_virtio_get_features,
+};
+
+static int syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev)
+{
+ int iomemtype;
+
+ proxy->vdev = vdev;
+
+ /* Don't support multiple vectors */
+ proxy->vdev->nvectors = 0;
+ sysbus_init_irq(&proxy->busdev, &proxy->irq);
+ iomemtype = cpu_register_io_memory(syborg_virtio_readfn,
+ syborg_virtio_writefn, proxy,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(&proxy->busdev, 0x1000, iomemtype);
+
+ proxy->id = ((uint32_t)0x1af4 << 16) | vdev->device_id;
+
+ qemu_register_reset(virtio_reset, vdev);
+
+ virtio_bind_device(vdev, &syborg_virtio_bindings, proxy);
+ proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY);
+ proxy->host_features = vdev->get_features(vdev, proxy->host_features);
+ return 0;
+}
+
+/* Device specific bindings. */
+
+static int syborg_virtio_net_init(SysBusDevice *dev)
+{
+ VirtIODevice *vdev;
+ SyborgVirtIOProxy *proxy = FROM_SYSBUS(SyborgVirtIOProxy, dev);
+
+ vdev = virtio_net_init(&dev->qdev, &proxy->nic, &proxy->net);
+ return syborg_virtio_init(proxy, vdev);
+}
+
+static SysBusDeviceInfo syborg_virtio_net_info = {
+ .init = syborg_virtio_net_init,
+ .qdev.name = "syborg,virtio-net",
+ .qdev.size = sizeof(SyborgVirtIOProxy),
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(SyborgVirtIOProxy, nic),
+ DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features),
+ DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void syborg_virtio_register_devices(void)
+{
+ sysbus_register_withprop(&syborg_virtio_net_info);
+}
+
+device_init(syborg_virtio_register_devices)
diff --git a/hw/sysbus.c b/hw/sysbus.c
new file mode 100644
index 0000000..1583bd8
--- /dev/null
+++ b/hw/sysbus.c
@@ -0,0 +1,203 @@
+/*
+ * System (CPU) Bus device support code
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * 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 "sysbus.h"
+#include "sysemu.h"
+#include "monitor.h"
+
+static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *sysbus_get_fw_dev_path(DeviceState *dev);
+
+struct BusInfo system_bus_info = {
+ .name = "System",
+ .size = sizeof(BusState),
+ .print_dev = sysbus_dev_print,
+ .get_fw_dev_path = sysbus_get_fw_dev_path,
+};
+
+void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
+{
+ assert(n >= 0 && n < dev->num_irq);
+ dev->irqs[n] = NULL;
+ if (dev->irqp[n]) {
+ *dev->irqp[n] = irq;
+ }
+}
+
+void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr)
+{
+ assert(n >= 0 && n < dev->num_mmio);
+
+ if (dev->mmio[n].addr == addr) {
+ /* ??? region already mapped here. */
+ return;
+ }
+ if (dev->mmio[n].addr != (target_phys_addr_t)-1) {
+ /* Unregister previous mapping. */
+ cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size,
+ IO_MEM_UNASSIGNED);
+ }
+ dev->mmio[n].addr = addr;
+ if (dev->mmio[n].cb) {
+ dev->mmio[n].cb(dev, addr);
+ } else {
+ cpu_register_physical_memory(addr, dev->mmio[n].size,
+ dev->mmio[n].iofunc);
+ }
+}
+
+
+/* Request an IRQ source. The actual IRQ object may be populated later. */
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+{
+ int n;
+
+ assert(dev->num_irq < QDEV_MAX_IRQ);
+ n = dev->num_irq++;
+ dev->irqp[n] = p;
+}
+
+/* Pass IRQs from a target device. */
+void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target)
+{
+ int i;
+ assert(dev->num_irq == 0);
+ dev->num_irq = target->num_irq;
+ for (i = 0; i < dev->num_irq; i++) {
+ dev->irqp[i] = target->irqp[i];
+ }
+}
+
+void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size,
+ ram_addr_t iofunc)
+{
+ int n;
+
+ assert(dev->num_mmio < QDEV_MAX_MMIO);
+ n = dev->num_mmio++;
+ dev->mmio[n].addr = -1;
+ dev->mmio[n].size = size;
+ dev->mmio[n].iofunc = iofunc;
+}
+
+void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size,
+ mmio_mapfunc cb)
+{
+ int n;
+
+ assert(dev->num_mmio < QDEV_MAX_MMIO);
+ n = dev->num_mmio++;
+ dev->mmio[n].addr = -1;
+ dev->mmio[n].size = size;
+ dev->mmio[n].cb = cb;
+}
+
+void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size)
+{
+ pio_addr_t i;
+
+ for (i = 0; i < size; i++) {
+ assert(dev->num_pio < QDEV_MAX_PIO);
+ dev->pio[dev->num_pio++] = ioport++;
+ }
+}
+
+static int sysbus_device_init(DeviceState *dev, DeviceInfo *base)
+{
+ SysBusDeviceInfo *info = container_of(base, SysBusDeviceInfo, qdev);
+
+ return info->init(sysbus_from_qdev(dev));
+}
+
+void sysbus_register_withprop(SysBusDeviceInfo *info)
+{
+ info->qdev.init = sysbus_device_init;
+ info->qdev.bus_info = &system_bus_info;
+
+ assert(info->qdev.size >= sizeof(SysBusDevice));
+ qdev_register(&info->qdev);
+}
+
+void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init)
+{
+ SysBusDeviceInfo *info;
+
+ info = qemu_mallocz(sizeof(*info));
+ info->qdev.name = qemu_strdup(name);
+ info->qdev.size = size;
+ info->init = init;
+ sysbus_register_withprop(info);
+}
+
+DeviceState *sysbus_create_varargs(const char *name,
+ target_phys_addr_t addr, ...)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ va_list va;
+ qemu_irq irq;
+ int n;
+
+ dev = qdev_create(NULL, name);
+ s = sysbus_from_qdev(dev);
+ qdev_init_nofail(dev);
+ if (addr != (target_phys_addr_t)-1) {
+ sysbus_mmio_map(s, 0, addr);
+ }
+ va_start(va, addr);
+ n = 0;
+ while (1) {
+ irq = va_arg(va, qemu_irq);
+ if (!irq) {
+ break;
+ }
+ sysbus_connect_irq(s, n, irq);
+ n++;
+ }
+ return dev;
+}
+
+static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ SysBusDevice *s = sysbus_from_qdev(dev);
+ int i;
+
+ for (i = 0; i < s->num_mmio; i++) {
+ monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
+ indent, "", s->mmio[i].addr, s->mmio[i].size);
+ }
+}
+
+static char *sysbus_get_fw_dev_path(DeviceState *dev)
+{
+ SysBusDevice *s = sysbus_from_qdev(dev);
+ char path[40];
+ int off;
+
+ off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
+
+ if (s->num_mmio) {
+ snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx,
+ s->mmio[0].addr);
+ } else if (s->num_pio) {
+ snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
+ }
+
+ return strdup(path);
+}
diff --git a/hw/sysbus.h b/hw/sysbus.h
new file mode 100644
index 0000000..e9eb618
--- /dev/null
+++ b/hw/sysbus.h
@@ -0,0 +1,67 @@
+#ifndef HW_SYSBUS_H
+#define HW_SYSBUS_H 1
+
+/* Devices attached directly to the main system bus. */
+
+#include "qdev.h"
+
+#define QDEV_MAX_MMIO 32
+#define QDEV_MAX_PIO 32
+#define QDEV_MAX_IRQ 256
+
+typedef struct SysBusDevice SysBusDevice;
+typedef void (*mmio_mapfunc)(SysBusDevice *dev, target_phys_addr_t addr);
+
+struct SysBusDevice {
+ DeviceState qdev;
+ int num_irq;
+ qemu_irq irqs[QDEV_MAX_IRQ];
+ qemu_irq *irqp[QDEV_MAX_IRQ];
+ int num_mmio;
+ struct {
+ target_phys_addr_t addr;
+ target_phys_addr_t size;
+ mmio_mapfunc cb;
+ ram_addr_t iofunc;
+ } mmio[QDEV_MAX_MMIO];
+ int num_pio;
+ pio_addr_t pio[QDEV_MAX_PIO];
+};
+
+typedef int (*sysbus_initfn)(SysBusDevice *dev);
+
+/* Macros to compensate for lack of type inheritance in C. */
+#define sysbus_from_qdev(dev) ((SysBusDevice *)(dev))
+#define FROM_SYSBUS(type, dev) DO_UPCAST(type, busdev, dev)
+
+typedef struct {
+ DeviceInfo qdev;
+ sysbus_initfn init;
+} SysBusDeviceInfo;
+
+void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init);
+void sysbus_register_withprop(SysBusDeviceInfo *info);
+void *sysbus_new(void);
+void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size,
+ ram_addr_t iofunc);
+void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size,
+ mmio_mapfunc cb);
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p);
+void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target);
+void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size);
+
+
+void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq);
+void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr);
+
+/* Legacy helper function for creating devices. */
+DeviceState *sysbus_create_varargs(const char *name,
+ target_phys_addr_t addr, ...);
+static inline DeviceState *sysbus_create_simple(const char *name,
+ target_phys_addr_t addr,
+ qemu_irq irq)
+{
+ return sysbus_create_varargs(name, addr, irq, NULL);
+}
+
+#endif /* !HW_SYSBUS_H */
diff --git a/hw/tc58128.c b/hw/tc58128.c
new file mode 100644
index 0000000..672a01c
--- /dev/null
+++ b/hw/tc58128.c
@@ -0,0 +1,183 @@
+#include "hw.h"
+#include "sh.h"
+#include "sysemu.h"
+#include "loader.h"
+
+#define CE1 0x0100
+#define CE2 0x0200
+#define RE 0x0400
+#define WE 0x0800
+#define ALE 0x1000
+#define CLE 0x2000
+#define RDY1 0x4000
+#define RDY2 0x8000
+#define RDY(n) ((n) == 0 ? RDY1 : RDY2)
+
+typedef enum { WAIT, READ1, READ2, READ3 } state_t;
+
+typedef struct {
+ uint8_t *flash_contents;
+ state_t state;
+ uint32_t address;
+ uint8_t address_cycle;
+} tc58128_dev;
+
+static tc58128_dev tc58128_devs[2];
+
+#define FLASH_SIZE (16*1024*1024)
+
+static void init_dev(tc58128_dev * dev, const char *filename)
+{
+ int ret, blocks;
+
+ dev->state = WAIT;
+ dev->flash_contents = qemu_mallocz(FLASH_SIZE);
+ memset(dev->flash_contents, 0xff, FLASH_SIZE);
+ if (!dev->flash_contents) {
+ fprintf(stderr, "could not alloc memory for flash\n");
+ exit(1);
+ }
+ if (filename) {
+ /* Load flash image skipping the first block */
+ ret = load_image(filename, dev->flash_contents + 528 * 32);
+ if (ret < 0) {
+ fprintf(stderr, "ret=%d\n", ret);
+ fprintf(stderr, "qemu: could not load flash image %s\n",
+ filename);
+ exit(1);
+ } else {
+ /* Build first block with number of blocks */
+ blocks = (ret + 528 * 32 - 1) / (528 * 32);
+ dev->flash_contents[0] = blocks & 0xff;
+ dev->flash_contents[1] = (blocks >> 8) & 0xff;
+ dev->flash_contents[2] = (blocks >> 16) & 0xff;
+ dev->flash_contents[3] = (blocks >> 24) & 0xff;
+ fprintf(stderr, "loaded %d bytes for %s into flash\n", ret,
+ filename);
+ }
+ }
+}
+
+static void handle_command(tc58128_dev * dev, uint8_t command)
+{
+ switch (command) {
+ case 0xff:
+ fprintf(stderr, "reset flash device\n");
+ dev->state = WAIT;
+ break;
+ case 0x00:
+ fprintf(stderr, "read mode 1\n");
+ dev->state = READ1;
+ dev->address_cycle = 0;
+ break;
+ case 0x01:
+ fprintf(stderr, "read mode 2\n");
+ dev->state = READ2;
+ dev->address_cycle = 0;
+ break;
+ case 0x50:
+ fprintf(stderr, "read mode 3\n");
+ dev->state = READ3;
+ dev->address_cycle = 0;
+ break;
+ default:
+ fprintf(stderr, "unknown flash command 0x%02x\n", command);
+ abort();
+ }
+}
+
+static void handle_address(tc58128_dev * dev, uint8_t data)
+{
+ switch (dev->state) {
+ case READ1:
+ case READ2:
+ case READ3:
+ switch (dev->address_cycle) {
+ case 0:
+ dev->address = data;
+ if (dev->state == READ2)
+ dev->address |= 0x100;
+ else if (dev->state == READ3)
+ dev->address |= 0x200;
+ break;
+ case 1:
+ dev->address += data * 528 * 0x100;
+ break;
+ case 2:
+ dev->address += data * 528;
+ fprintf(stderr, "address pointer in flash: 0x%08x\n",
+ dev->address);
+ break;
+ default:
+ /* Invalid data */
+ abort();
+ }
+ dev->address_cycle++;
+ break;
+ default:
+ abort();
+ }
+}
+
+static uint8_t handle_read(tc58128_dev * dev)
+{
+#if 0
+ if (dev->address % 0x100000 == 0)
+ fprintf(stderr, "reading flash at address 0x%08x\n", dev->address);
+#endif
+ return dev->flash_contents[dev->address++];
+}
+
+/* We never mark the device as busy, so interrupts cannot be triggered
+ XXXXX */
+
+static int tc58128_cb(uint16_t porta, uint16_t portb,
+ uint16_t * periph_pdtra, uint16_t * periph_portadir,
+ uint16_t * periph_pdtrb, uint16_t * periph_portbdir)
+{
+ int dev;
+
+ if ((porta & CE1) == 0)
+ dev = 0;
+ else if ((porta & CE2) == 0)
+ dev = 1;
+ else
+ return 0; /* No device selected */
+
+ if ((porta & RE) && (porta & WE)) {
+ /* Nothing to do, assert ready and return to input state */
+ *periph_portadir &= 0xff00;
+ *periph_portadir |= RDY(dev);
+ *periph_pdtra |= RDY(dev);
+ return 1;
+ }
+
+ if (porta & CLE) {
+ /* Command */
+ assert((porta & WE) == 0);
+ handle_command(&tc58128_devs[dev], porta & 0x00ff);
+ } else if (porta & ALE) {
+ assert((porta & WE) == 0);
+ handle_address(&tc58128_devs[dev], porta & 0x00ff);
+ } else if ((porta & RE) == 0) {
+ *periph_portadir |= 0x00ff;
+ *periph_pdtra &= 0xff00;
+ *periph_pdtra |= handle_read(&tc58128_devs[dev]);
+ } else {
+ abort();
+ }
+ return 1;
+}
+
+static sh7750_io_device tc58128 = {
+ RE | WE, /* Port A triggers */
+ 0, /* Port B triggers */
+ tc58128_cb /* Callback */
+};
+
+int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2)
+{
+ init_dev(&tc58128_devs[0], zone1);
+ init_dev(&tc58128_devs[1], zone2);
+ return sh7750_register_io_device(s, &tc58128);
+}
diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c
new file mode 100644
index 0000000..c3fbe4e
--- /dev/null
+++ b/hw/tc6393xb.c
@@ -0,0 +1,608 @@
+/*
+ * Toshiba TC6393XB I/O Controller.
+ * Found in Sharp Zaurus SL-6000 (tosa) or some
+ * Toshiba e-Series PDAs.
+ *
+ * Most features are currently unsupported!!!
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#include "hw.h"
+#include "pxa.h"
+#include "devices.h"
+#include "flash.h"
+#include "console.h"
+#include "pixel_ops.h"
+
+#define IRQ_TC6393_NAND 0
+#define IRQ_TC6393_MMC 1
+#define IRQ_TC6393_OHCI 2
+#define IRQ_TC6393_SERIAL 3
+#define IRQ_TC6393_FB 4
+
+#define TC6393XB_NR_IRQS 8
+
+#define TC6393XB_GPIOS 16
+
+#define SCR_REVID 0x08 /* b Revision ID */
+#define SCR_ISR 0x50 /* b Interrupt Status */
+#define SCR_IMR 0x52 /* b Interrupt Mask */
+#define SCR_IRR 0x54 /* b Interrupt Routing */
+#define SCR_GPER 0x60 /* w GP Enable */
+#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */
+#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */
+#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */
+#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */
+#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */
+#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */
+#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */
+#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */
+#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */
+#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */
+#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */
+#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */
+#define SCR_CCR 0x98 /* w Clock Control */
+#define SCR_PLL2CR 0x9a /* w PLL2 Control */
+#define SCR_PLL1CR 0x9c /* l PLL1 Control */
+#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */
+#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */
+#define SCR_FER 0xe0 /* b Function Enable */
+#define SCR_MCR 0xe4 /* w Mode Control */
+#define SCR_CONFIG 0xfc /* b Configuration Control */
+#define SCR_DEBUG 0xff /* b Debug */
+
+#define NAND_CFG_COMMAND 0x04 /* w Command */
+#define NAND_CFG_BASE 0x10 /* l Control Base Address */
+#define NAND_CFG_INTP 0x3d /* b Interrupt Pin */
+#define NAND_CFG_INTE 0x48 /* b Int Enable */
+#define NAND_CFG_EC 0x4a /* b Event Control */
+#define NAND_CFG_ICC 0x4c /* b Internal Clock Control */
+#define NAND_CFG_ECCC 0x5b /* b ECC Control */
+#define NAND_CFG_NFTC 0x60 /* b NAND Flash Transaction Control */
+#define NAND_CFG_NFM 0x61 /* b NAND Flash Monitor */
+#define NAND_CFG_NFPSC 0x62 /* b NAND Flash Power Supply Control */
+#define NAND_CFG_NFDC 0x63 /* b NAND Flash Detect Control */
+
+#define NAND_DATA 0x00 /* l Data */
+#define NAND_MODE 0x04 /* b Mode */
+#define NAND_STATUS 0x05 /* b Status */
+#define NAND_ISR 0x06 /* b Interrupt Status */
+#define NAND_IMR 0x07 /* b Interrupt Mask */
+
+#define NAND_MODE_WP 0x80
+#define NAND_MODE_CE 0x10
+#define NAND_MODE_ALE 0x02
+#define NAND_MODE_CLE 0x01
+#define NAND_MODE_ECC_MASK 0x60
+#define NAND_MODE_ECC_EN 0x20
+#define NAND_MODE_ECC_READ 0x40
+#define NAND_MODE_ECC_RST 0x60
+
+struct TC6393xbState {
+ qemu_irq irq;
+ qemu_irq *sub_irqs;
+ struct {
+ uint8_t ISR;
+ uint8_t IMR;
+ uint8_t IRR;
+ uint16_t GPER;
+ uint8_t GPI_SR[3];
+ uint8_t GPI_IMR[3];
+ uint8_t GPI_EDER[3];
+ uint8_t GPI_LIR[3];
+ uint8_t GP_IARCR[3];
+ uint8_t GP_IARLCR[3];
+ uint8_t GPI_BCR[3];
+ uint16_t GPA_IARCR;
+ uint16_t GPA_IARLCR;
+ uint16_t CCR;
+ uint16_t PLL2CR;
+ uint32_t PLL1CR;
+ uint8_t DIARCR;
+ uint8_t DBOCR;
+ uint8_t FER;
+ uint16_t MCR;
+ uint8_t CONFIG;
+ uint8_t DEBUG;
+ } scr;
+ uint32_t gpio_dir;
+ uint32_t gpio_level;
+ uint32_t prev_level;
+ qemu_irq handler[TC6393XB_GPIOS];
+ qemu_irq *gpio_in;
+
+ struct {
+ uint8_t mode;
+ uint8_t isr;
+ uint8_t imr;
+ } nand;
+ int nand_enable;
+ uint32_t nand_phys;
+ NANDFlashState *flash;
+ ECCState ecc;
+
+ DisplayState *ds;
+ ram_addr_t vram_addr;
+ uint16_t *vram_ptr;
+ uint32_t scr_width, scr_height; /* in pixels */
+ qemu_irq l3v;
+ unsigned blank : 1,
+ blanked : 1;
+};
+
+qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s)
+{
+ return s->gpio_in;
+}
+
+static void tc6393xb_gpio_set(void *opaque, int line, int level)
+{
+// TC6393xbState *s = opaque;
+
+ if (line > TC6393XB_GPIOS) {
+ printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
+ return;
+ }
+
+ // FIXME: how does the chip reflect the GPIO input level change?
+}
+
+void tc6393xb_gpio_out_set(TC6393xbState *s, int line,
+ qemu_irq handler)
+{
+ if (line >= TC6393XB_GPIOS) {
+ fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line);
+ return;
+ }
+
+ s->handler[line] = handler;
+}
+
+static void tc6393xb_gpio_handler_update(TC6393xbState *s)
+{
+ uint32_t level, diff;
+ int bit;
+
+ level = s->gpio_level & s->gpio_dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+qemu_irq tc6393xb_l3v_get(TC6393xbState *s)
+{
+ return s->l3v;
+}
+
+static void tc6393xb_l3v(void *opaque, int line, int level)
+{
+ TC6393xbState *s = opaque;
+ s->blank = !level;
+ fprintf(stderr, "L3V: %d\n", level);
+}
+
+static void tc6393xb_sub_irq(void *opaque, int line, int level) {
+ TC6393xbState *s = opaque;
+ uint8_t isr = s->scr.ISR;
+ if (level)
+ isr |= 1 << line;
+ else
+ isr &= ~(1 << line);
+ s->scr.ISR = isr;
+ qemu_set_irq(s->irq, isr & s->scr.IMR);
+}
+
+#define SCR_REG_B(N) \
+ case SCR_ ##N: return s->scr.N
+#define SCR_REG_W(N) \
+ case SCR_ ##N: return s->scr.N; \
+ case SCR_ ##N + 1: return s->scr.N >> 8;
+#define SCR_REG_L(N) \
+ case SCR_ ##N: return s->scr.N; \
+ case SCR_ ##N + 1: return s->scr.N >> 8; \
+ case SCR_ ##N + 2: return s->scr.N >> 16; \
+ case SCR_ ##N + 3: return s->scr.N >> 24;
+#define SCR_REG_A(N) \
+ case SCR_ ##N(0): return s->scr.N[0]; \
+ case SCR_ ##N(1): return s->scr.N[1]; \
+ case SCR_ ##N(2): return s->scr.N[2]
+
+static uint32_t tc6393xb_scr_readb(TC6393xbState *s, target_phys_addr_t addr)
+{
+ switch (addr) {
+ case SCR_REVID:
+ return 3;
+ case SCR_REVID+1:
+ return 0;
+ SCR_REG_B(ISR);
+ SCR_REG_B(IMR);
+ SCR_REG_B(IRR);
+ SCR_REG_W(GPER);
+ SCR_REG_A(GPI_SR);
+ SCR_REG_A(GPI_IMR);
+ SCR_REG_A(GPI_EDER);
+ SCR_REG_A(GPI_LIR);
+ case SCR_GPO_DSR(0):
+ case SCR_GPO_DSR(1):
+ case SCR_GPO_DSR(2):
+ return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff;
+ case SCR_GPO_DOECR(0):
+ case SCR_GPO_DOECR(1):
+ case SCR_GPO_DOECR(2):
+ return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff;
+ SCR_REG_A(GP_IARCR);
+ SCR_REG_A(GP_IARLCR);
+ SCR_REG_A(GPI_BCR);
+ SCR_REG_W(GPA_IARCR);
+ SCR_REG_W(GPA_IARLCR);
+ SCR_REG_W(CCR);
+ SCR_REG_W(PLL2CR);
+ SCR_REG_L(PLL1CR);
+ SCR_REG_B(DIARCR);
+ SCR_REG_B(DBOCR);
+ SCR_REG_B(FER);
+ SCR_REG_W(MCR);
+ SCR_REG_B(CONFIG);
+ SCR_REG_B(DEBUG);
+ }
+ fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr);
+ return 0;
+}
+#undef SCR_REG_B
+#undef SCR_REG_W
+#undef SCR_REG_L
+#undef SCR_REG_A
+
+#define SCR_REG_B(N) \
+ case SCR_ ##N: s->scr.N = value; return;
+#define SCR_REG_W(N) \
+ case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \
+ case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return
+#define SCR_REG_L(N) \
+ case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \
+ case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return; \
+ case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return; \
+ case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return;
+#define SCR_REG_A(N) \
+ case SCR_ ##N(0): s->scr.N[0] = value; return; \
+ case SCR_ ##N(1): s->scr.N[1] = value; return; \
+ case SCR_ ##N(2): s->scr.N[2] = value; return
+
+static void tc6393xb_scr_writeb(TC6393xbState *s, target_phys_addr_t addr, uint32_t value)
+{
+ switch (addr) {
+ SCR_REG_B(ISR);
+ SCR_REG_B(IMR);
+ SCR_REG_B(IRR);
+ SCR_REG_W(GPER);
+ SCR_REG_A(GPI_SR);
+ SCR_REG_A(GPI_IMR);
+ SCR_REG_A(GPI_EDER);
+ SCR_REG_A(GPI_LIR);
+ case SCR_GPO_DSR(0):
+ case SCR_GPO_DSR(1):
+ case SCR_GPO_DSR(2):
+ s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8));
+ tc6393xb_gpio_handler_update(s);
+ return;
+ case SCR_GPO_DOECR(0):
+ case SCR_GPO_DOECR(1):
+ case SCR_GPO_DOECR(2):
+ s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8));
+ tc6393xb_gpio_handler_update(s);
+ return;
+ SCR_REG_A(GP_IARCR);
+ SCR_REG_A(GP_IARLCR);
+ SCR_REG_A(GPI_BCR);
+ SCR_REG_W(GPA_IARCR);
+ SCR_REG_W(GPA_IARLCR);
+ SCR_REG_W(CCR);
+ SCR_REG_W(PLL2CR);
+ SCR_REG_L(PLL1CR);
+ SCR_REG_B(DIARCR);
+ SCR_REG_B(DBOCR);
+ SCR_REG_B(FER);
+ SCR_REG_W(MCR);
+ SCR_REG_B(CONFIG);
+ SCR_REG_B(DEBUG);
+ }
+ fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n",
+ (uint32_t) addr, value & 0xff);
+}
+#undef SCR_REG_B
+#undef SCR_REG_W
+#undef SCR_REG_L
+#undef SCR_REG_A
+
+static void tc6393xb_nand_irq(TC6393xbState *s) {
+ qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND],
+ (s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr));
+}
+
+static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, target_phys_addr_t addr) {
+ switch (addr) {
+ case NAND_CFG_COMMAND:
+ return s->nand_enable ? 2 : 0;
+ case NAND_CFG_BASE:
+ case NAND_CFG_BASE + 1:
+ case NAND_CFG_BASE + 2:
+ case NAND_CFG_BASE + 3:
+ return s->nand_phys >> (addr - NAND_CFG_BASE);
+ }
+ fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr);
+ return 0;
+}
+static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, target_phys_addr_t addr, uint32_t value) {
+ switch (addr) {
+ case NAND_CFG_COMMAND:
+ s->nand_enable = (value & 0x2);
+ return;
+ case NAND_CFG_BASE:
+ case NAND_CFG_BASE + 1:
+ case NAND_CFG_BASE + 2:
+ case NAND_CFG_BASE + 3:
+ s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8));
+ s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8);
+ return;
+ }
+ fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n",
+ (uint32_t) addr, value & 0xff);
+}
+
+static uint32_t tc6393xb_nand_readb(TC6393xbState *s, target_phys_addr_t addr) {
+ switch (addr) {
+ case NAND_DATA + 0:
+ case NAND_DATA + 1:
+ case NAND_DATA + 2:
+ case NAND_DATA + 3:
+ return nand_getio(s->flash);
+ case NAND_MODE:
+ return s->nand.mode;
+ case NAND_STATUS:
+ return 0x14;
+ case NAND_ISR:
+ return s->nand.isr;
+ case NAND_IMR:
+ return s->nand.imr;
+ }
+ fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr);
+ return 0;
+}
+static void tc6393xb_nand_writeb(TC6393xbState *s, target_phys_addr_t addr, uint32_t value) {
+// fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n",
+// (uint32_t) addr, value & 0xff);
+ switch (addr) {
+ case NAND_DATA + 0:
+ case NAND_DATA + 1:
+ case NAND_DATA + 2:
+ case NAND_DATA + 3:
+ nand_setio(s->flash, value);
+ s->nand.isr &= 1;
+ tc6393xb_nand_irq(s);
+ return;
+ case NAND_MODE:
+ s->nand.mode = value;
+ nand_setpins(s->flash,
+ value & NAND_MODE_CLE,
+ value & NAND_MODE_ALE,
+ !(value & NAND_MODE_CE),
+ value & NAND_MODE_WP,
+ 0); // FIXME: gnd
+ switch (value & NAND_MODE_ECC_MASK) {
+ case NAND_MODE_ECC_RST:
+ ecc_reset(&s->ecc);
+ break;
+ case NAND_MODE_ECC_READ:
+ // FIXME
+ break;
+ case NAND_MODE_ECC_EN:
+ ecc_reset(&s->ecc);
+ }
+ return;
+ case NAND_ISR:
+ s->nand.isr = value;
+ tc6393xb_nand_irq(s);
+ return;
+ case NAND_IMR:
+ s->nand.imr = value;
+ tc6393xb_nand_irq(s);
+ return;
+ }
+ fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n",
+ (uint32_t) addr, value & 0xff);
+}
+
+#define BITS 8
+#include "tc6393xb_template.h"
+#define BITS 15
+#include "tc6393xb_template.h"
+#define BITS 16
+#include "tc6393xb_template.h"
+#define BITS 24
+#include "tc6393xb_template.h"
+#define BITS 32
+#include "tc6393xb_template.h"
+
+static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update)
+{
+ switch (ds_get_bits_per_pixel(s->ds)) {
+ case 8:
+ tc6393xb_draw_graphic8(s);
+ break;
+ case 15:
+ tc6393xb_draw_graphic15(s);
+ break;
+ case 16:
+ tc6393xb_draw_graphic16(s);
+ break;
+ case 24:
+ tc6393xb_draw_graphic24(s);
+ break;
+ case 32:
+ tc6393xb_draw_graphic32(s);
+ break;
+ default:
+ printf("tc6393xb: unknown depth %d\n", ds_get_bits_per_pixel(s->ds));
+ return;
+ }
+
+ dpy_update(s->ds, 0, 0, s->scr_width, s->scr_height);
+}
+
+static void tc6393xb_draw_blank(TC6393xbState *s, int full_update)
+{
+ int i, w;
+ uint8_t *d;
+
+ if (!full_update)
+ return;
+
+ w = s->scr_width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
+ d = ds_get_data(s->ds);
+ for(i = 0; i < s->scr_height; i++) {
+ memset(d, 0, w);
+ d += ds_get_linesize(s->ds);
+ }
+
+ dpy_update(s->ds, 0, 0, s->scr_width, s->scr_height);
+}
+
+static void tc6393xb_update_display(void *opaque)
+{
+ TC6393xbState *s = opaque;
+ int full_update;
+
+ if (s->scr_width == 0 || s->scr_height == 0)
+ return;
+
+ full_update = 0;
+ if (s->blanked != s->blank) {
+ s->blanked = s->blank;
+ full_update = 1;
+ }
+ if (s->scr_width != ds_get_width(s->ds) || s->scr_height != ds_get_height(s->ds)) {
+ qemu_console_resize(s->ds, s->scr_width, s->scr_height);
+ full_update = 1;
+ }
+ if (s->blanked)
+ tc6393xb_draw_blank(s, full_update);
+ else
+ tc6393xb_draw_graphic(s, full_update);
+}
+
+
+static uint32_t tc6393xb_readb(void *opaque, target_phys_addr_t addr) {
+ TC6393xbState *s = opaque;
+
+ switch (addr >> 8) {
+ case 0:
+ return tc6393xb_scr_readb(s, addr & 0xff);
+ case 1:
+ return tc6393xb_nand_cfg_readb(s, addr & 0xff);
+ };
+
+ if ((addr &~0xff) == s->nand_phys && s->nand_enable) {
+// return tc6393xb_nand_readb(s, addr & 0xff);
+ uint8_t d = tc6393xb_nand_readb(s, addr & 0xff);
+// fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d);
+ return d;
+ }
+
+// fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr);
+ return 0;
+}
+
+static void tc6393xb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) {
+ TC6393xbState *s = opaque;
+
+ switch (addr >> 8) {
+ case 0:
+ tc6393xb_scr_writeb(s, addr & 0xff, value);
+ return;
+ case 1:
+ tc6393xb_nand_cfg_writeb(s, addr & 0xff, value);
+ return;
+ };
+
+ if ((addr &~0xff) == s->nand_phys && s->nand_enable)
+ tc6393xb_nand_writeb(s, addr & 0xff, value);
+ else
+ fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n",
+ (uint32_t) addr, value & 0xff);
+}
+
+static uint32_t tc6393xb_readw(void *opaque, target_phys_addr_t addr)
+{
+ return (tc6393xb_readb(opaque, addr) & 0xff) |
+ (tc6393xb_readb(opaque, addr + 1) << 8);
+}
+
+static uint32_t tc6393xb_readl(void *opaque, target_phys_addr_t addr)
+{
+ return (tc6393xb_readb(opaque, addr) & 0xff) |
+ ((tc6393xb_readb(opaque, addr + 1) & 0xff) << 8) |
+ ((tc6393xb_readb(opaque, addr + 2) & 0xff) << 16) |
+ ((tc6393xb_readb(opaque, addr + 3) & 0xff) << 24);
+}
+
+static void tc6393xb_writew(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ tc6393xb_writeb(opaque, addr, value);
+ tc6393xb_writeb(opaque, addr + 1, value >> 8);
+}
+
+static void tc6393xb_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ tc6393xb_writeb(opaque, addr, value);
+ tc6393xb_writeb(opaque, addr + 1, value >> 8);
+ tc6393xb_writeb(opaque, addr + 2, value >> 16);
+ tc6393xb_writeb(opaque, addr + 3, value >> 24);
+}
+
+TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq)
+{
+ int iomemtype;
+ TC6393xbState *s;
+ CPUReadMemoryFunc * const tc6393xb_readfn[] = {
+ tc6393xb_readb,
+ tc6393xb_readw,
+ tc6393xb_readl,
+ };
+ CPUWriteMemoryFunc * const tc6393xb_writefn[] = {
+ tc6393xb_writeb,
+ tc6393xb_writew,
+ tc6393xb_writel,
+ };
+
+ s = (TC6393xbState *) qemu_mallocz(sizeof(TC6393xbState));
+ s->irq = irq;
+ s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS);
+
+ s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1);
+ s->blanked = 1;
+
+ s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);
+
+ s->flash = nand_init(NAND_MFR_TOSHIBA, 0x76);
+
+ iomemtype = cpu_register_io_memory(tc6393xb_readfn,
+ tc6393xb_writefn, s, DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(base, 0x10000, iomemtype);
+
+ s->vram_addr = qemu_ram_alloc(NULL, "tc6393xb.vram", 0x100000);
+ s->vram_ptr = qemu_get_ram_ptr(s->vram_addr);
+ cpu_register_physical_memory(base + 0x100000, 0x100000, s->vram_addr);
+ s->scr_width = 480;
+ s->scr_height = 640;
+ s->ds = graphic_console_init(tc6393xb_update_display,
+ NULL, /* invalidate */
+ NULL, /* screen_dump */
+ NULL, /* text_update */
+ s);
+
+ return s;
+}
diff --git a/hw/tc6393xb_template.h b/hw/tc6393xb_template.h
new file mode 100644
index 0000000..1ccf6e8
--- /dev/null
+++ b/hw/tc6393xb_template.h
@@ -0,0 +1,67 @@
+/*
+ * Toshiba TC6393XB I/O Controller.
+ * Found in Sharp Zaurus SL-6000 (tosa) or some
+ * Toshiba e-Series PDAs.
+ *
+ * FB support code. Based on G364 fb emulator
+ *
+ * Copyright (c) 2007 Hervé Poussineau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#if BITS == 8
+# define SET_PIXEL(addr, color) *(uint8_t*)addr = color;
+#elif BITS == 15 || BITS == 16
+# define SET_PIXEL(addr, color) *(uint16_t*)addr = color;
+#elif BITS == 24
+# define SET_PIXEL(addr, color) \
+ addr[0] = color; addr[1] = (color) >> 8; addr[2] = (color) >> 16;
+#elif BITS == 32
+# define SET_PIXEL(addr, color) *(uint32_t*)addr = color;
+#else
+# error unknown bit depth
+#endif
+
+
+static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s)
+{
+ int i;
+ uint16_t *data_buffer;
+ uint8_t *data_display;
+
+ data_buffer = s->vram_ptr;
+ data_display = ds_get_data(s->ds);
+ for(i = 0; i < s->scr_height; i++) {
+#if (BITS == 16)
+ memcpy(data_display, data_buffer, s->scr_width * 2);
+ data_buffer += s->scr_width;
+ data_display += ds_get_linesize(s->ds);
+#else
+ int j;
+ for (j = 0; j < s->scr_width; j++, data_display += BITS / 8, data_buffer++) {
+ uint16_t color = *data_buffer;
+ uint32_t dest_color = glue(rgb_to_pixel, BITS)(
+ ((color & 0xf800) * 0x108) >> 11,
+ ((color & 0x7e0) * 0x41) >> 9,
+ ((color & 0x1f) * 0x21) >> 2
+ );
+ SET_PIXEL(data_display, dest_color);
+ }
+#endif
+ }
+}
+
+#undef BITS
+#undef SET_PIXEL
diff --git a/hw/tcx.c b/hw/tcx.c
new file mode 100644
index 0000000..0e32830
--- /dev/null
+++ b/hw/tcx.c
@@ -0,0 +1,651 @@
+/*
+ * QEMU TCX Frame buffer
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "console.h"
+#include "pixel_ops.h"
+#include "sysbus.h"
+#include "qdev-addr.h"
+
+#define MAXX 1024
+#define MAXY 768
+#define TCX_DAC_NREGS 16
+#define TCX_THC_NREGS_8 0x081c
+#define TCX_THC_NREGS_24 0x1000
+#define TCX_TEC_NREGS 0x1000
+
+typedef struct TCXState {
+ SysBusDevice busdev;
+ target_phys_addr_t addr;
+ DisplayState *ds;
+ uint8_t *vram;
+ uint32_t *vram24, *cplane;
+ ram_addr_t vram_offset, vram24_offset, cplane_offset;
+ uint32_t vram_size;
+ uint16_t width, height, depth;
+ uint8_t r[256], g[256], b[256];
+ uint32_t palette[256];
+ uint8_t dac_index, dac_state;
+} TCXState;
+
+static void tcx_screen_dump(void *opaque, const char *filename);
+static void tcx24_screen_dump(void *opaque, const char *filename);
+
+static void tcx_set_dirty(TCXState *s)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) {
+ cpu_physical_memory_set_dirty(s->vram_offset + i);
+ }
+}
+
+static void tcx24_set_dirty(TCXState *s)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) {
+ cpu_physical_memory_set_dirty(s->vram24_offset + i);
+ cpu_physical_memory_set_dirty(s->cplane_offset + i);
+ }
+}
+
+static void update_palette_entries(TCXState *s, int start, int end)
+{
+ int i;
+ for(i = start; i < end; i++) {
+ switch(ds_get_bits_per_pixel(s->ds)) {
+ default:
+ case 8:
+ s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
+ break;
+ case 15:
+ s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
+ break;
+ case 16:
+ s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
+ break;
+ case 32:
+ if (is_surface_bgr(s->ds->surface))
+ s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
+ else
+ s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
+ break;
+ }
+ }
+ if (s->depth == 24) {
+ tcx24_set_dirty(s);
+ } else {
+ tcx_set_dirty(s);
+ }
+}
+
+static void tcx_draw_line32(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int x;
+ uint8_t val;
+ uint32_t *p = (uint32_t *)d;
+
+ for(x = 0; x < width; x++) {
+ val = *s++;
+ *p++ = s1->palette[val];
+ }
+}
+
+static void tcx_draw_line16(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int x;
+ uint8_t val;
+ uint16_t *p = (uint16_t *)d;
+
+ for(x = 0; x < width; x++) {
+ val = *s++;
+ *p++ = s1->palette[val];
+ }
+}
+
+static void tcx_draw_line8(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int x;
+ uint8_t val;
+
+ for(x = 0; x < width; x++) {
+ val = *s++;
+ *d++ = s1->palette[val];
+ }
+}
+
+/*
+ XXX Could be much more optimal:
+ * detect if line/page/whole screen is in 24 bit mode
+ * if destination is also BGR, use memcpy
+ */
+static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
+ const uint8_t *s, int width,
+ const uint32_t *cplane,
+ const uint32_t *s24)
+{
+ int x, bgr, r, g, b;
+ uint8_t val, *p8;
+ uint32_t *p = (uint32_t *)d;
+ uint32_t dval;
+
+ bgr = is_surface_bgr(s1->ds->surface);
+ for(x = 0; x < width; x++, s++, s24++) {
+ if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
+ // 24-bit direct, BGR order
+ p8 = (uint8_t *)s24;
+ p8++;
+ b = *p8++;
+ g = *p8++;
+ r = *p8;
+ if (bgr)
+ dval = rgb_to_pixel32bgr(r, g, b);
+ else
+ dval = rgb_to_pixel32(r, g, b);
+ } else {
+ val = *s;
+ dval = s1->palette[val];
+ }
+ *p++ = dval;
+ }
+}
+
+static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
+ ram_addr_t cpage)
+{
+ int ret;
+ unsigned int off;
+
+ ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
+ for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
+ ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
+ ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
+ }
+ return ret;
+}
+
+static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
+ ram_addr_t page_max, ram_addr_t page24,
+ ram_addr_t cpage)
+{
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ page_min -= ts->vram_offset;
+ page_max -= ts->vram_offset;
+ cpu_physical_memory_reset_dirty(page24 + page_min * 4,
+ page24 + page_max * 4 + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ cpu_physical_memory_reset_dirty(cpage + page_min * 4,
+ cpage + page_max * 4 + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+}
+
+/* Fixed line length 1024 allows us to do nice tricks not possible on
+ VGA... */
+static void tcx_update_display(void *opaque)
+{
+ TCXState *ts = opaque;
+ ram_addr_t page, page_min, page_max;
+ int y, y_start, dd, ds;
+ uint8_t *d, *s;
+ void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
+
+ if (ds_get_bits_per_pixel(ts->ds) == 0)
+ return;
+ page = ts->vram_offset;
+ y_start = -1;
+ page_min = -1;
+ page_max = 0;
+ d = ds_get_data(ts->ds);
+ s = ts->vram;
+ dd = ds_get_linesize(ts->ds);
+ ds = 1024;
+
+ switch (ds_get_bits_per_pixel(ts->ds)) {
+ case 32:
+ f = tcx_draw_line32;
+ break;
+ case 15:
+ case 16:
+ f = tcx_draw_line16;
+ break;
+ default:
+ case 8:
+ f = tcx_draw_line8;
+ break;
+ case 0:
+ return;
+ }
+
+ for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
+ if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
+ if (y_start < 0)
+ y_start = y;
+ if (page < page_min)
+ page_min = page;
+ if (page > page_max)
+ page_max = page;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ f(ts, d, s, ts->width);
+ d += dd;
+ s += ds;
+ } else {
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(ts->ds, 0, y_start,
+ ts->width, y - y_start);
+ y_start = -1;
+ }
+ d += dd * 4;
+ s += ds * 4;
+ }
+ }
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(ts->ds, 0, y_start,
+ ts->width, y - y_start);
+ }
+ /* reset modified pages */
+ if (page_max >= page_min) {
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+}
+
+static void tcx24_update_display(void *opaque)
+{
+ TCXState *ts = opaque;
+ ram_addr_t page, page_min, page_max, cpage, page24;
+ int y, y_start, dd, ds;
+ uint8_t *d, *s;
+ uint32_t *cptr, *s24;
+
+ if (ds_get_bits_per_pixel(ts->ds) != 32)
+ return;
+ page = ts->vram_offset;
+ page24 = ts->vram24_offset;
+ cpage = ts->cplane_offset;
+ y_start = -1;
+ page_min = -1;
+ page_max = 0;
+ d = ds_get_data(ts->ds);
+ s = ts->vram;
+ s24 = ts->vram24;
+ cptr = ts->cplane;
+ dd = ds_get_linesize(ts->ds);
+ ds = 1024;
+
+ for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
+ page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
+ if (check_dirty(page, page24, cpage)) {
+ if (y_start < 0)
+ y_start = y;
+ if (page < page_min)
+ page_min = page;
+ if (page > page_max)
+ page_max = page;
+ tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
+ d += dd;
+ s += ds;
+ cptr += ds;
+ s24 += ds;
+ tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
+ d += dd;
+ s += ds;
+ cptr += ds;
+ s24 += ds;
+ tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
+ d += dd;
+ s += ds;
+ cptr += ds;
+ s24 += ds;
+ tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
+ d += dd;
+ s += ds;
+ cptr += ds;
+ s24 += ds;
+ } else {
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(ts->ds, 0, y_start,
+ ts->width, y - y_start);
+ y_start = -1;
+ }
+ d += dd * 4;
+ s += ds * 4;
+ cptr += ds * 4;
+ s24 += ds * 4;
+ }
+ }
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(ts->ds, 0, y_start,
+ ts->width, y - y_start);
+ }
+ /* reset modified pages */
+ if (page_max >= page_min) {
+ reset_dirty(ts, page_min, page_max, page24, cpage);
+ }
+}
+
+static void tcx_invalidate_display(void *opaque)
+{
+ TCXState *s = opaque;
+
+ tcx_set_dirty(s);
+ qemu_console_resize(s->ds, s->width, s->height);
+}
+
+static void tcx24_invalidate_display(void *opaque)
+{
+ TCXState *s = opaque;
+
+ tcx_set_dirty(s);
+ tcx24_set_dirty(s);
+ qemu_console_resize(s->ds, s->width, s->height);
+}
+
+static int vmstate_tcx_post_load(void *opaque, int version_id)
+{
+ TCXState *s = opaque;
+
+ update_palette_entries(s, 0, 256);
+ if (s->depth == 24) {
+ tcx24_set_dirty(s);
+ } else {
+ tcx_set_dirty(s);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_tcx = {
+ .name ="tcx",
+ .version_id = 4,
+ .minimum_version_id = 4,
+ .minimum_version_id_old = 4,
+ .post_load = vmstate_tcx_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(height, TCXState),
+ VMSTATE_UINT16(width, TCXState),
+ VMSTATE_UINT16(depth, TCXState),
+ VMSTATE_BUFFER(r, TCXState),
+ VMSTATE_BUFFER(g, TCXState),
+ VMSTATE_BUFFER(b, TCXState),
+ VMSTATE_UINT8(dac_index, TCXState),
+ VMSTATE_UINT8(dac_state, TCXState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void tcx_reset(DeviceState *d)
+{
+ TCXState *s = container_of(d, TCXState, busdev.qdev);
+
+ /* Initialize palette */
+ memset(s->r, 0, 256);
+ memset(s->g, 0, 256);
+ memset(s->b, 0, 256);
+ s->r[255] = s->g[255] = s->b[255] = 255;
+ update_palette_entries(s, 0, 256);
+ memset(s->vram, 0, MAXX*MAXY);
+ cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
+ MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
+ s->dac_index = 0;
+ s->dac_state = 0;
+}
+
+static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ TCXState *s = opaque;
+
+ switch (addr) {
+ case 0:
+ s->dac_index = val >> 24;
+ s->dac_state = 0;
+ break;
+ case 4:
+ switch (s->dac_state) {
+ case 0:
+ s->r[s->dac_index] = val >> 24;
+ update_palette_entries(s, s->dac_index, s->dac_index + 1);
+ s->dac_state++;
+ break;
+ case 1:
+ s->g[s->dac_index] = val >> 24;
+ update_palette_entries(s, s->dac_index, s->dac_index + 1);
+ s->dac_state++;
+ break;
+ case 2:
+ s->b[s->dac_index] = val >> 24;
+ update_palette_entries(s, s->dac_index, s->dac_index + 1);
+ s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
+ default:
+ s->dac_state = 0;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static CPUReadMemoryFunc * const tcx_dac_read[3] = {
+ NULL,
+ NULL,
+ tcx_dac_readl,
+};
+
+static CPUWriteMemoryFunc * const tcx_dac_write[3] = {
+ NULL,
+ NULL,
+ tcx_dac_writel,
+};
+
+static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
+{
+ return 0;
+}
+
+static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+}
+
+static CPUReadMemoryFunc * const tcx_dummy_read[3] = {
+ NULL,
+ NULL,
+ tcx_dummy_readl,
+};
+
+static CPUWriteMemoryFunc * const tcx_dummy_write[3] = {
+ NULL,
+ NULL,
+ tcx_dummy_writel,
+};
+
+static int tcx_init1(SysBusDevice *dev)
+{
+ TCXState *s = FROM_SYSBUS(TCXState, dev);
+ int io_memory, dummy_memory;
+ ram_addr_t vram_offset;
+ int size;
+ uint8_t *vram_base;
+
+ vram_offset = qemu_ram_alloc(NULL, "tcx.vram", s->vram_size * (1 + 4 + 4));
+ vram_base = qemu_get_ram_ptr(vram_offset);
+ s->vram_offset = vram_offset;
+
+ /* 8-bit plane */
+ s->vram = vram_base;
+ size = s->vram_size;
+ sysbus_init_mmio(dev, size, s->vram_offset);
+ vram_offset += size;
+ vram_base += size;
+
+ /* DAC */
+ io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
+
+ /* TEC (dummy) */
+ dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
+ s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
+ /* THC: NetBSD writes here even with 8-bit display: dummy */
+ sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
+
+ if (s->depth == 24) {
+ /* 24-bit plane */
+ size = s->vram_size * 4;
+ s->vram24 = (uint32_t *)vram_base;
+ s->vram24_offset = vram_offset;
+ sysbus_init_mmio(dev, size, vram_offset);
+ vram_offset += size;
+ vram_base += size;
+
+ /* Control plane */
+ size = s->vram_size * 4;
+ s->cplane = (uint32_t *)vram_base;
+ s->cplane_offset = vram_offset;
+ sysbus_init_mmio(dev, size, vram_offset);
+
+ s->ds = graphic_console_init(tcx24_update_display,
+ tcx24_invalidate_display,
+ tcx24_screen_dump, NULL, s);
+ } else {
+ /* THC 8 bit (dummy) */
+ sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
+
+ s->ds = graphic_console_init(tcx_update_display,
+ tcx_invalidate_display,
+ tcx_screen_dump, NULL, s);
+ }
+
+ qemu_console_resize(s->ds, s->width, s->height);
+ return 0;
+}
+
+static void tcx_screen_dump(void *opaque, const char *filename)
+{
+ TCXState *s = opaque;
+ FILE *f;
+ uint8_t *d, *d1, v;
+ int y, x;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return;
+ fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
+ d1 = s->vram;
+ for(y = 0; y < s->height; y++) {
+ d = d1;
+ for(x = 0; x < s->width; x++) {
+ v = *d;
+ fputc(s->r[v], f);
+ fputc(s->g[v], f);
+ fputc(s->b[v], f);
+ d++;
+ }
+ d1 += MAXX;
+ }
+ fclose(f);
+ return;
+}
+
+static void tcx24_screen_dump(void *opaque, const char *filename)
+{
+ TCXState *s = opaque;
+ FILE *f;
+ uint8_t *d, *d1, v;
+ uint32_t *s24, *cptr, dval;
+ int y, x;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return;
+ fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
+ d1 = s->vram;
+ s24 = s->vram24;
+ cptr = s->cplane;
+ for(y = 0; y < s->height; y++) {
+ d = d1;
+ for(x = 0; x < s->width; x++, d++, s24++) {
+ if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
+ dval = *s24 & 0x00ffffff;
+ fputc((dval >> 16) & 0xff, f);
+ fputc((dval >> 8) & 0xff, f);
+ fputc(dval & 0xff, f);
+ } else {
+ v = *d;
+ fputc(s->r[v], f);
+ fputc(s->g[v], f);
+ fputc(s->b[v], f);
+ }
+ }
+ d1 += MAXX;
+ }
+ fclose(f);
+ return;
+}
+
+static SysBusDeviceInfo tcx_info = {
+ .init = tcx_init1,
+ .qdev.name = "SUNW,tcx",
+ .qdev.size = sizeof(TCXState),
+ .qdev.reset = tcx_reset,
+ .qdev.vmsd = &vmstate_tcx,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_TADDR("addr", TCXState, addr, -1),
+ DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
+ DEFINE_PROP_UINT16("width", TCXState, width, -1),
+ DEFINE_PROP_UINT16("height", TCXState, height, -1),
+ DEFINE_PROP_UINT16("depth", TCXState, depth, -1),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void tcx_register_devices(void)
+{
+ sysbus_register_withprop(&tcx_info);
+}
+
+device_init(tcx_register_devices)
diff --git a/hw/testdev.c b/hw/testdev.c
new file mode 100644
index 0000000..f0355c8
--- /dev/null
+++ b/hw/testdev.c
@@ -0,0 +1,142 @@
+#include <sys/mman.h>
+#include "hw.h"
+#include "qdev.h"
+#include "isa.h"
+
+struct testdev {
+ ISADevice dev;
+ CharDriverState *chr;
+};
+
+static void test_device_serial_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ struct testdev *dev = opaque;
+ uint8_t buf[1] = { data };
+
+ if (dev->chr) {
+ qemu_chr_write(dev->chr, buf, 1);
+ }
+}
+
+static void test_device_exit(void *opaque, uint32_t addr, uint32_t data)
+{
+ exit(data);
+}
+
+static uint32_t test_device_memsize_read(void *opaque, uint32_t addr)
+{
+ return ram_size;
+}
+
+extern qemu_irq *ioapic_irq_hack;
+
+static void test_device_irq_line(void *opaque, uint32_t addr, uint32_t data)
+{
+ qemu_set_irq(ioapic_irq_hack[addr - 0x2000], !!data);
+}
+
+static uint32 test_device_ioport_data;
+
+static void test_device_ioport_write(void *opaque, uint32_t addr, uint32_t data)
+{
+ test_device_ioport_data = data;
+}
+
+static uint32_t test_device_ioport_read(void *opaque, uint32_t addr)
+{
+ return test_device_ioport_data;
+}
+
+static void test_device_flush_page(void *opaque, uint32_t addr, uint32_t data)
+{
+ target_phys_addr_t len = 4096;
+ void *a = cpu_physical_memory_map(data & ~0xffful, &len, 0);
+
+ mprotect(a, 4096, PROT_NONE);
+ mprotect(a, 4096, PROT_READ|PROT_WRITE);
+ cpu_physical_memory_unmap(a, len, 0, 0);
+}
+
+static char *iomem_buf;
+
+static uint32_t test_iomem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return iomem_buf[addr];
+}
+
+static uint32_t test_iomem_readw(void *opaque, target_phys_addr_t addr)
+{
+ return *(uint16_t*)(iomem_buf + addr);
+}
+
+static uint32_t test_iomem_readl(void *opaque, target_phys_addr_t addr)
+{
+ return *(uint32_t*)(iomem_buf + addr);
+}
+
+static void test_iomem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ iomem_buf[addr] = val;
+}
+
+static void test_iomem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ *(uint16_t*)(iomem_buf + addr) = val;
+}
+
+static void test_iomem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ *(uint32_t*)(iomem_buf + addr) = val;
+}
+
+static CPUReadMemoryFunc * const test_iomem_read[3] = {
+ test_iomem_readb,
+ test_iomem_readw,
+ test_iomem_readl,
+};
+
+static CPUWriteMemoryFunc * const test_iomem_write[3] = {
+ test_iomem_writeb,
+ test_iomem_writew,
+ test_iomem_writel,
+};
+
+static int init_test_device(ISADevice *isa)
+{
+ struct testdev *dev = DO_UPCAST(struct testdev, dev, isa);
+ int iomem;
+
+ register_ioport_write(0xf1, 1, 1, test_device_serial_write, dev);
+ register_ioport_write(0xf4, 1, 4, test_device_exit, dev);
+ register_ioport_read(0xd1, 1, 4, test_device_memsize_read, dev);
+ register_ioport_read(0xe0, 1, 1, test_device_ioport_read, dev);
+ register_ioport_write(0xe0, 1, 1, test_device_ioport_write, dev);
+ register_ioport_read(0xe0, 1, 2, test_device_ioport_read, dev);
+ register_ioport_write(0xe0, 1, 2, test_device_ioport_write, dev);
+ register_ioport_read(0xe0, 1, 4, test_device_ioport_read, dev);
+ register_ioport_write(0xe0, 1, 4, test_device_ioport_write, dev);
+ register_ioport_write(0xe4, 1, 4, test_device_flush_page, dev);
+ register_ioport_write(0x2000, 24, 1, test_device_irq_line, NULL);
+ iomem_buf = qemu_mallocz(0x10000);
+ iomem = cpu_register_io_memory(test_iomem_read, test_iomem_write, NULL,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory(0xff000000, 0x10000, iomem);
+ return 0;
+}
+
+static ISADeviceInfo testdev_info = {
+ .qdev.name = "testdev",
+ .qdev.size = sizeof(struct testdev),
+ .init = init_test_device,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", struct testdev, chr),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void testdev_register_devices(void)
+{
+ isa_qdev_register(&testdev_info);
+}
+
+device_init(testdev_register_devices)
diff --git a/hw/tmp105.c b/hw/tmp105.c
new file mode 100644
index 0000000..f7e6f2b
--- /dev/null
+++ b/hw/tmp105.c
@@ -0,0 +1,244 @@
+/*
+ * Texas Instruments TMP105 temperature sensor.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "i2c.h"
+
+typedef struct {
+ i2c_slave i2c;
+ uint8_t len;
+ uint8_t buf[2];
+ qemu_irq pin;
+
+ uint8_t pointer;
+ uint8_t config;
+ int16_t temperature;
+ int16_t limit[2];
+ int faults;
+ uint8_t alarm;
+} TMP105State;
+
+static void tmp105_interrupt_update(TMP105State *s)
+{
+ qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */
+}
+
+static void tmp105_alarm_update(TMP105State *s)
+{
+ if ((s->config >> 0) & 1) { /* SD */
+ if ((s->config >> 7) & 1) /* OS */
+ s->config &= ~(1 << 7); /* OS */
+ else
+ return;
+ }
+
+ if ((s->config >> 1) & 1) { /* TM */
+ if (s->temperature >= s->limit[1])
+ s->alarm = 1;
+ else if (s->temperature < s->limit[0])
+ s->alarm = 1;
+ } else {
+ if (s->temperature >= s->limit[1])
+ s->alarm = 1;
+ else if (s->temperature < s->limit[0])
+ s->alarm = 0;
+ }
+
+ tmp105_interrupt_update(s);
+}
+
+/* Units are 0.001 centigrades relative to 0 C. */
+void tmp105_set(i2c_slave *i2c, int temp)
+{
+ TMP105State *s = (TMP105State *) i2c;
+
+ if (temp >= 128000 || temp < -128000) {
+ fprintf(stderr, "%s: values is out of range (%i.%03i C)\n",
+ __FUNCTION__, temp / 1000, temp % 1000);
+ exit(-1);
+ }
+
+ s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
+
+ tmp105_alarm_update(s);
+}
+
+static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
+
+static void tmp105_read(TMP105State *s)
+{
+ s->len = 0;
+
+ if ((s->config >> 1) & 1) { /* TM */
+ s->alarm = 0;
+ tmp105_interrupt_update(s);
+ }
+
+ switch (s->pointer & 3) {
+ case 0: /* Temperature */
+ s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
+ s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
+ (0xf0 << ((~s->config >> 5) & 3)); /* R */
+ break;
+
+ case 1: /* Configuration */
+ s->buf[s->len ++] = s->config;
+ break;
+
+ case 2: /* T_LOW */
+ s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
+ s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
+ break;
+
+ case 3: /* T_HIGH */
+ s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
+ s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
+ break;
+ }
+}
+
+static void tmp105_write(TMP105State *s)
+{
+ switch (s->pointer & 3) {
+ case 0: /* Temperature */
+ break;
+
+ case 1: /* Configuration */
+ if (s->buf[0] & ~s->config & (1 << 0)) /* SD */
+ printf("%s: TMP105 shutdown\n", __FUNCTION__);
+ s->config = s->buf[0];
+ s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */
+ tmp105_alarm_update(s);
+ break;
+
+ case 2: /* T_LOW */
+ case 3: /* T_HIGH */
+ if (s->len >= 3)
+ s->limit[s->pointer & 1] = (int16_t)
+ ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
+ tmp105_alarm_update(s);
+ break;
+ }
+}
+
+static int tmp105_rx(i2c_slave *i2c)
+{
+ TMP105State *s = (TMP105State *) i2c;
+
+ if (s->len < 2)
+ return s->buf[s->len ++];
+ else
+ return 0xff;
+}
+
+static int tmp105_tx(i2c_slave *i2c, uint8_t data)
+{
+ TMP105State *s = (TMP105State *) i2c;
+
+ if (!s->len ++)
+ s->pointer = data;
+ else {
+ if (s->len <= 2)
+ s->buf[s->len - 1] = data;
+ tmp105_write(s);
+ }
+
+ return 0;
+}
+
+static void tmp105_event(i2c_slave *i2c, enum i2c_event event)
+{
+ TMP105State *s = (TMP105State *) i2c;
+
+ if (event == I2C_START_RECV)
+ tmp105_read(s);
+
+ s->len = 0;
+}
+
+static int tmp105_post_load(void *opaque, int version_id)
+{
+ TMP105State *s = opaque;
+
+ s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */
+
+ tmp105_interrupt_update(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_tmp105 = {
+ .name = "TMP105",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = tmp105_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(len, TMP105State),
+ VMSTATE_UINT8_ARRAY(buf, TMP105State, 2),
+ VMSTATE_UINT8(pointer, TMP105State),
+ VMSTATE_UINT8(config, TMP105State),
+ VMSTATE_INT16(temperature, TMP105State),
+ VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
+ VMSTATE_UINT8(alarm, TMP105State),
+ VMSTATE_I2C_SLAVE(i2c, TMP105State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void tmp105_reset(i2c_slave *i2c)
+{
+ TMP105State *s = (TMP105State *) i2c;
+
+ s->temperature = 0;
+ s->pointer = 0;
+ s->config = 0;
+ s->faults = tmp105_faultq[(s->config >> 3) & 3];
+ s->alarm = 0;
+
+ tmp105_interrupt_update(s);
+}
+
+static int tmp105_init(i2c_slave *i2c)
+{
+ TMP105State *s = FROM_I2C_SLAVE(TMP105State, i2c);
+
+ qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
+
+ tmp105_reset(&s->i2c);
+
+ return 0;
+}
+
+static I2CSlaveInfo tmp105_info = {
+ .qdev.name = "tmp105",
+ .qdev.size = sizeof(TMP105State),
+ .qdev.vmsd = &vmstate_tmp105,
+ .init = tmp105_init,
+ .event = tmp105_event,
+ .recv = tmp105_rx,
+ .send = tmp105_tx
+};
+
+static void tmp105_register_devices(void)
+{
+ i2c_register_slave(&tmp105_info);
+}
+
+device_init(tmp105_register_devices)
diff --git a/hw/tosa.c b/hw/tosa.c
new file mode 100644
index 0000000..0bfab16
--- /dev/null
+++ b/hw/tosa.c
@@ -0,0 +1,277 @@
+/* vim:set shiftwidth=4 ts=4 et: */
+/*
+ * PXA255 Sharp Zaurus SL-6000 PDA platform
+ *
+ * Copyright (c) 2008 Dmitry Baryshkov
+ *
+ * Code based on spitz platform by Andrzej Zaborowski <balrog@zabor.org>
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "pxa.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "sharpsl.h"
+#include "pcmcia.h"
+#include "block.h"
+#include "boards.h"
+#include "i2c.h"
+#include "ssi.h"
+#include "blockdev.h"
+#include "sysbus.h"
+
+#define TOSA_RAM 0x04000000
+#define TOSA_ROM 0x00800000
+
+#define TOSA_GPIO_nSD_DETECT (9)
+#define TOSA_GPIO_ON_RESET (19)
+#define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */
+#define TOSA_GPIO_CF_CD (13)
+#define TOSA_GPIO_TC6393XB_INT (15)
+#define TOSA_GPIO_JC_CF_IRQ (36) /* CF slot1 Ready */
+
+#define TOSA_SCOOP_GPIO_BASE 1
+#define TOSA_GPIO_IR_POWERDWN (TOSA_SCOOP_GPIO_BASE + 2)
+#define TOSA_GPIO_SD_WP (TOSA_SCOOP_GPIO_BASE + 3)
+#define TOSA_GPIO_PWR_ON (TOSA_SCOOP_GPIO_BASE + 4)
+
+#define TOSA_SCOOP_JC_GPIO_BASE 1
+#define TOSA_GPIO_BT_LED (TOSA_SCOOP_JC_GPIO_BASE + 0)
+#define TOSA_GPIO_NOTE_LED (TOSA_SCOOP_JC_GPIO_BASE + 1)
+#define TOSA_GPIO_CHRG_ERR_LED (TOSA_SCOOP_JC_GPIO_BASE + 2)
+#define TOSA_GPIO_TC6393XB_L3V_ON (TOSA_SCOOP_JC_GPIO_BASE + 5)
+#define TOSA_GPIO_WLAN_LED (TOSA_SCOOP_JC_GPIO_BASE + 7)
+
+#define DAC_BASE 0x4e
+#define DAC_CH1 0
+#define DAC_CH2 1
+
+static void tosa_microdrive_attach(PXA2xxState *cpu)
+{
+ PCMCIACardState *md;
+ BlockDriverState *bs;
+ DriveInfo *dinfo;
+
+ dinfo = drive_get(IF_IDE, 0, 0);
+ if (!dinfo)
+ return;
+ bs = dinfo->bdrv;
+ if (bdrv_is_inserted(bs) && !bdrv_is_removable(bs)) {
+ md = dscm1xxxx_init(dinfo);
+ pxa2xx_pcmcia_attach(cpu->pcmcia[0], md);
+ }
+}
+
+static void tosa_out_switch(void *opaque, int line, int level)
+{
+ switch (line) {
+ case 0:
+ fprintf(stderr, "blue LED %s.\n", level ? "on" : "off");
+ break;
+ case 1:
+ fprintf(stderr, "green LED %s.\n", level ? "on" : "off");
+ break;
+ case 2:
+ fprintf(stderr, "amber LED %s.\n", level ? "on" : "off");
+ break;
+ case 3:
+ fprintf(stderr, "wlan LED %s.\n", level ? "on" : "off");
+ break;
+ default:
+ fprintf(stderr, "Uhandled out event: %d = %d\n", line, level);
+ break;
+ }
+}
+
+
+static void tosa_gpio_setup(PXA2xxState *cpu,
+ DeviceState *scp0,
+ DeviceState *scp1,
+ TC6393xbState *tmio)
+{
+ qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4);
+ /* MMC/SD host */
+ pxa2xx_mmci_handlers(cpu->mmc,
+ qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP),
+ qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT)));
+
+ /* Handle reset */
+ qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset);
+
+ /* PCMCIA signals: card's IRQ and Card-Detect */
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0],
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_IRQ),
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_CD));
+
+ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1],
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ),
+ NULL);
+
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, outsignals[0]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]);
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]);
+
+ qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio));
+}
+
+static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value)
+{
+ fprintf(stderr, "TG: %d %02x\n", value >> 5, value & 0x1f);
+ return 0;
+}
+
+static int tosa_ssp_init(SSISlave *dev)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+typedef struct {
+ i2c_slave i2c;
+ int len;
+ char buf[3];
+} TosaDACState;
+
+static int tosa_dac_send(i2c_slave *i2c, uint8_t data)
+{
+ TosaDACState *s = FROM_I2C_SLAVE(TosaDACState, i2c);
+ s->buf[s->len] = data;
+ if (s->len ++ > 2) {
+#ifdef VERBOSE
+ fprintf(stderr, "%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
+#endif
+ return 1;
+ }
+
+ if (s->len == 2) {
+ fprintf(stderr, "dac: channel %d value 0x%02x\n",
+ s->buf[0], s->buf[1]);
+ }
+
+ return 0;
+}
+
+static void tosa_dac_event(i2c_slave *i2c, enum i2c_event event)
+{
+ TosaDACState *s = FROM_I2C_SLAVE(TosaDACState, i2c);
+ s->len = 0;
+ switch (event) {
+ case I2C_START_SEND:
+ break;
+ case I2C_START_RECV:
+ printf("%s: recv not supported!!!\n", __FUNCTION__);
+ break;
+ case I2C_FINISH:
+#ifdef VERBOSE
+ if (s->len < 2)
+ printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
+ if (s->len > 2)
+ printf("%s: message too long\n", __FUNCTION__);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+static int tosa_dac_recv(i2c_slave *s)
+{
+ printf("%s: recv not supported!!!\n", __FUNCTION__);
+ return -1;
+}
+
+static int tosa_dac_init(i2c_slave *i2c)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+static void tosa_tg_init(PXA2xxState *cpu)
+{
+ i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]);
+ i2c_create_slave(bus, "tosa_dac", DAC_BASE);
+ ssi_create_slave(cpu->ssp[1], "tosa-ssp");
+}
+
+
+static struct arm_boot_info tosa_binfo = {
+ .loader_start = PXA2XX_SDRAM_BASE,
+ .ram_size = 0x04000000,
+};
+
+static void tosa_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ PXA2xxState *cpu;
+ TC6393xbState *tmio;
+ DeviceState *scp0, *scp1;
+
+ if (!cpu_model)
+ cpu_model = "pxa255";
+
+ cpu = pxa255_init(tosa_binfo.ram_size);
+
+ cpu_register_physical_memory(0, TOSA_ROM,
+ qemu_ram_alloc(NULL, "tosa.rom", TOSA_ROM) | IO_MEM_ROM);
+
+ tmio = tc6393xb_init(0x10000000,
+ qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_TC6393XB_INT));
+
+ scp0 = sysbus_create_simple("scoop", 0x08800000, NULL);
+ scp1 = sysbus_create_simple("scoop", 0x14800040, NULL);
+
+ tosa_gpio_setup(cpu, scp0, scp1, tmio);
+
+ tosa_microdrive_attach(cpu);
+
+ tosa_tg_init(cpu);
+
+ tosa_binfo.kernel_filename = kernel_filename;
+ tosa_binfo.kernel_cmdline = kernel_cmdline;
+ tosa_binfo.initrd_filename = initrd_filename;
+ tosa_binfo.board_id = 0x208;
+ arm_load_kernel(cpu->env, &tosa_binfo);
+ sl_bootparam_write(SL_PXA_PARAM_BASE);
+}
+
+static QEMUMachine tosapda_machine = {
+ .name = "tosa",
+ .desc = "Tosa PDA (PXA255)",
+ .init = tosa_init,
+};
+
+static void tosapda_machine_init(void)
+{
+ qemu_register_machine(&tosapda_machine);
+}
+
+machine_init(tosapda_machine_init);
+
+static I2CSlaveInfo tosa_dac_info = {
+ .qdev.name = "tosa_dac",
+ .qdev.size = sizeof(TosaDACState),
+ .init = tosa_dac_init,
+ .event = tosa_dac_event,
+ .recv = tosa_dac_recv,
+ .send = tosa_dac_send
+};
+
+static SSISlaveInfo tosa_ssp_info = {
+ .qdev.name = "tosa-ssp",
+ .qdev.size = sizeof(SSISlave),
+ .init = tosa_ssp_init,
+ .transfer = tosa_ssp_tansfer
+};
+
+static void tosa_register_devices(void)
+{
+ i2c_register_slave(&tosa_dac_info);
+ ssi_register_slave(&tosa_ssp_info);
+}
+
+device_init(tosa_register_devices)
diff --git a/hw/tsc2005.c b/hw/tsc2005.c
new file mode 100644
index 0000000..a55853c
--- /dev/null
+++ b/hw/tsc2005.c
@@ -0,0 +1,593 @@
+/*
+ * TI TSC2005 emulator.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "console.h"
+#include "devices.h"
+
+#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10)))
+
+typedef struct {
+ qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */
+ QEMUTimer *timer;
+ uint16_t model;
+
+ int x, y;
+ int pressure;
+
+ int state, reg, irq, command;
+ uint16_t data, dav;
+
+ int busy;
+ int enabled;
+ int host_mode;
+ int function;
+ int nextfunction;
+ int precision;
+ int nextprecision;
+ int filter;
+ int pin_func;
+ int timing[2];
+ int noise;
+ int reset;
+ int pdst;
+ int pnd0;
+ uint16_t temp_thr[2];
+ uint16_t aux_thr[2];
+
+ int tr[8];
+} TSC2005State;
+
+enum {
+ TSC_MODE_XYZ_SCAN = 0x0,
+ TSC_MODE_XY_SCAN,
+ TSC_MODE_X,
+ TSC_MODE_Y,
+ TSC_MODE_Z,
+ TSC_MODE_AUX,
+ TSC_MODE_TEMP1,
+ TSC_MODE_TEMP2,
+ TSC_MODE_AUX_SCAN,
+ TSC_MODE_X_TEST,
+ TSC_MODE_Y_TEST,
+ TSC_MODE_TS_TEST,
+ TSC_MODE_RESERVED,
+ TSC_MODE_XX_DRV,
+ TSC_MODE_YY_DRV,
+ TSC_MODE_YX_DRV,
+};
+
+static const uint16_t mode_regs[16] = {
+ 0xf000, /* X, Y, Z scan */
+ 0xc000, /* X, Y scan */
+ 0x8000, /* X */
+ 0x4000, /* Y */
+ 0x3000, /* Z */
+ 0x0800, /* AUX */
+ 0x0400, /* TEMP1 */
+ 0x0200, /* TEMP2 */
+ 0x0800, /* AUX scan */
+ 0x0040, /* X test */
+ 0x0020, /* Y test */
+ 0x0080, /* Short-circuit test */
+ 0x0000, /* Reserved */
+ 0x0000, /* X+, X- drivers */
+ 0x0000, /* Y+, Y- drivers */
+ 0x0000, /* Y+, X- drivers */
+};
+
+#define X_TRANSFORM(s) \
+ ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
+#define Y_TRANSFORM(s) \
+ ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
+#define Z1_TRANSFORM(s) \
+ ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
+#define Z2_TRANSFORM(s) \
+ ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
+
+#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */
+#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */
+#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */
+
+static uint16_t tsc2005_read(TSC2005State *s, int reg)
+{
+ uint16_t ret;
+
+ switch (reg) {
+ case 0x0: /* X */
+ s->dav &= ~mode_regs[TSC_MODE_X];
+ return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
+ (s->noise & 3);
+ case 0x1: /* Y */
+ s->dav &= ~mode_regs[TSC_MODE_Y];
+ s->noise ++;
+ return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
+ (s->noise & 3);
+ case 0x2: /* Z1 */
+ s->dav &= 0xdfff;
+ return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
+ (s->noise & 3);
+ case 0x3: /* Z2 */
+ s->dav &= 0xefff;
+ return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
+ (s->noise & 3);
+
+ case 0x4: /* AUX */
+ s->dav &= ~mode_regs[TSC_MODE_AUX];
+ return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
+
+ case 0x5: /* TEMP1 */
+ s->dav &= ~mode_regs[TSC_MODE_TEMP1];
+ return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
+ (s->noise & 5);
+ case 0x6: /* TEMP2 */
+ s->dav &= 0xdfff;
+ s->dav &= ~mode_regs[TSC_MODE_TEMP2];
+ return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
+ (s->noise & 3);
+
+ case 0x7: /* Status */
+ ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
+ s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
+ mode_regs[TSC_MODE_TS_TEST]);
+ s->reset = 1;
+ return ret;
+
+ case 0x8: /* AUX high treshold */
+ return s->aux_thr[1];
+ case 0x9: /* AUX low treshold */
+ return s->aux_thr[0];
+
+ case 0xa: /* TEMP high treshold */
+ return s->temp_thr[1];
+ case 0xb: /* TEMP low treshold */
+ return s->temp_thr[0];
+
+ case 0xc: /* CFR0 */
+ return (s->pressure << 15) | ((!s->busy) << 14) |
+ (s->nextprecision << 13) | s->timing[0];
+ case 0xd: /* CFR1 */
+ return s->timing[1];
+ case 0xe: /* CFR2 */
+ return (s->pin_func << 14) | s->filter;
+
+ case 0xf: /* Function select status */
+ return s->function >= 0 ? 1 << s->function : 0;
+ }
+
+ /* Never gets here */
+ return 0xffff;
+}
+
+static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
+{
+ switch (reg) {
+ case 0x8: /* AUX high treshold */
+ s->aux_thr[1] = data;
+ break;
+ case 0x9: /* AUX low treshold */
+ s->aux_thr[0] = data;
+ break;
+
+ case 0xa: /* TEMP high treshold */
+ s->temp_thr[1] = data;
+ break;
+ case 0xb: /* TEMP low treshold */
+ s->temp_thr[0] = data;
+ break;
+
+ case 0xc: /* CFR0 */
+ s->host_mode = data >> 15;
+ if (s->enabled != !(data & 0x4000)) {
+ s->enabled = !(data & 0x4000);
+ fprintf(stderr, "%s: touchscreen sense %sabled\n",
+ __FUNCTION__, s->enabled ? "en" : "dis");
+ if (s->busy && !s->enabled)
+ qemu_del_timer(s->timer);
+ s->busy &= s->enabled;
+ }
+ s->nextprecision = (data >> 13) & 1;
+ s->timing[0] = data & 0x1fff;
+ if ((s->timing[0] >> 11) == 3)
+ fprintf(stderr, "%s: illegal conversion clock setting\n",
+ __FUNCTION__);
+ break;
+ case 0xd: /* CFR1 */
+ s->timing[1] = data & 0xf07;
+ break;
+ case 0xe: /* CFR2 */
+ s->pin_func = (data >> 14) & 3;
+ s->filter = data & 0x3fff;
+ break;
+
+ default:
+ fprintf(stderr, "%s: write into read-only register %x\n",
+ __FUNCTION__, reg);
+ }
+}
+
+/* This handles most of the chip's logic. */
+static void tsc2005_pin_update(TSC2005State *s)
+{
+ int64_t expires;
+ int pin_state;
+
+ switch (s->pin_func) {
+ case 0:
+ pin_state = !s->pressure && !!s->dav;
+ break;
+ case 1:
+ case 3:
+ default:
+ pin_state = !s->dav;
+ break;
+ case 2:
+ pin_state = !s->pressure;
+ }
+
+ if (pin_state != s->irq) {
+ s->irq = pin_state;
+ qemu_set_irq(s->pint, s->irq);
+ }
+
+ switch (s->nextfunction) {
+ case TSC_MODE_XYZ_SCAN:
+ case TSC_MODE_XY_SCAN:
+ if (!s->host_mode && s->dav)
+ s->enabled = 0;
+ if (!s->pressure)
+ return;
+ /* Fall through */
+ case TSC_MODE_AUX_SCAN:
+ break;
+
+ case TSC_MODE_X:
+ case TSC_MODE_Y:
+ case TSC_MODE_Z:
+ if (!s->pressure)
+ return;
+ /* Fall through */
+ case TSC_MODE_AUX:
+ case TSC_MODE_TEMP1:
+ case TSC_MODE_TEMP2:
+ case TSC_MODE_X_TEST:
+ case TSC_MODE_Y_TEST:
+ case TSC_MODE_TS_TEST:
+ if (s->dav)
+ s->enabled = 0;
+ break;
+
+ case TSC_MODE_RESERVED:
+ case TSC_MODE_XX_DRV:
+ case TSC_MODE_YY_DRV:
+ case TSC_MODE_YX_DRV:
+ default:
+ return;
+ }
+
+ if (!s->enabled || s->busy)
+ return;
+
+ s->busy = 1;
+ s->precision = s->nextprecision;
+ s->function = s->nextfunction;
+ s->pdst = !s->pnd0; /* Synchronised on internal clock */
+ expires = qemu_get_clock(vm_clock) + (get_ticks_per_sec() >> 7);
+ qemu_mod_timer(s->timer, expires);
+}
+
+static void tsc2005_reset(TSC2005State *s)
+{
+ s->state = 0;
+ s->pin_func = 0;
+ s->enabled = 0;
+ s->busy = 0;
+ s->nextprecision = 0;
+ s->nextfunction = 0;
+ s->timing[0] = 0;
+ s->timing[1] = 0;
+ s->irq = 0;
+ s->dav = 0;
+ s->reset = 0;
+ s->pdst = 1;
+ s->pnd0 = 0;
+ s->function = -1;
+ s->temp_thr[0] = 0x000;
+ s->temp_thr[1] = 0xfff;
+ s->aux_thr[0] = 0x000;
+ s->aux_thr[1] = 0xfff;
+
+ tsc2005_pin_update(s);
+}
+
+static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
+{
+ TSC2005State *s = opaque;
+ uint32_t ret = 0;
+
+ switch (s->state ++) {
+ case 0:
+ if (value & 0x80) {
+ /* Command */
+ if (value & (1 << 1))
+ tsc2005_reset(s);
+ else {
+ s->nextfunction = (value >> 3) & 0xf;
+ s->nextprecision = (value >> 2) & 1;
+ if (s->enabled != !(value & 1)) {
+ s->enabled = !(value & 1);
+ fprintf(stderr, "%s: touchscreen sense %sabled\n",
+ __FUNCTION__, s->enabled ? "en" : "dis");
+ if (s->busy && !s->enabled)
+ qemu_del_timer(s->timer);
+ s->busy &= s->enabled;
+ }
+ tsc2005_pin_update(s);
+ }
+
+ s->state = 0;
+ } else if (value) {
+ /* Data transfer */
+ s->reg = (value >> 3) & 0xf;
+ s->pnd0 = (value >> 1) & 1;
+ s->command = value & 1;
+
+ if (s->command) {
+ /* Read */
+ s->data = tsc2005_read(s, s->reg);
+ tsc2005_pin_update(s);
+ } else
+ s->data = 0;
+ } else
+ s->state = 0;
+ break;
+
+ case 1:
+ if (s->command)
+ ret = (s->data >> 8) & 0xff;
+ else
+ s->data |= value << 8;
+ break;
+
+ case 2:
+ if (s->command)
+ ret = s->data & 0xff;
+ else {
+ s->data |= value;
+ tsc2005_write(s, s->reg, s->data);
+ tsc2005_pin_update(s);
+ }
+
+ s->state = 0;
+ break;
+ }
+
+ return ret;
+}
+
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
+{
+ uint32_t ret = 0;
+
+ len &= ~7;
+ while (len > 0) {
+ len -= 8;
+ ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len;
+ }
+
+ return ret;
+}
+
+static void tsc2005_timer_tick(void *opaque)
+{
+ TSC2005State *s = opaque;
+
+ /* Timer ticked -- a set of conversions has been finished. */
+
+ if (!s->busy)
+ return;
+
+ s->busy = 0;
+ s->dav |= mode_regs[s->function];
+ s->function = -1;
+ tsc2005_pin_update(s);
+}
+
+static void tsc2005_touchscreen_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ TSC2005State *s = opaque;
+ int p = s->pressure;
+
+ if (buttons_state) {
+ s->x = x;
+ s->y = y;
+ }
+ s->pressure = !!buttons_state;
+
+ /*
+ * Note: We would get better responsiveness in the guest by
+ * signaling TS events immediately, but for now we simulate
+ * the first conversion delay for sake of correctness.
+ */
+ if (p != s->pressure)
+ tsc2005_pin_update(s);
+}
+
+static void tsc2005_save(QEMUFile *f, void *opaque)
+{
+ TSC2005State *s = (TSC2005State *) opaque;
+ int i;
+
+ qemu_put_be16(f, s->x);
+ qemu_put_be16(f, s->y);
+ qemu_put_byte(f, s->pressure);
+
+ qemu_put_byte(f, s->state);
+ qemu_put_byte(f, s->reg);
+ qemu_put_byte(f, s->command);
+
+ qemu_put_byte(f, s->irq);
+ qemu_put_be16s(f, &s->dav);
+ qemu_put_be16s(f, &s->data);
+
+ qemu_put_timer(f, s->timer);
+ qemu_put_byte(f, s->enabled);
+ qemu_put_byte(f, s->host_mode);
+ qemu_put_byte(f, s->function);
+ qemu_put_byte(f, s->nextfunction);
+ qemu_put_byte(f, s->precision);
+ qemu_put_byte(f, s->nextprecision);
+ qemu_put_be16(f, s->filter);
+ qemu_put_byte(f, s->pin_func);
+ qemu_put_be16(f, s->timing[0]);
+ qemu_put_be16(f, s->timing[1]);
+ qemu_put_be16s(f, &s->temp_thr[0]);
+ qemu_put_be16s(f, &s->temp_thr[1]);
+ qemu_put_be16s(f, &s->aux_thr[0]);
+ qemu_put_be16s(f, &s->aux_thr[1]);
+ qemu_put_be32(f, s->noise);
+ qemu_put_byte(f, s->reset);
+ qemu_put_byte(f, s->pdst);
+ qemu_put_byte(f, s->pnd0);
+
+ for (i = 0; i < 8; i ++)
+ qemu_put_be32(f, s->tr[i]);
+}
+
+static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
+{
+ TSC2005State *s = (TSC2005State *) opaque;
+ int i;
+
+ s->x = qemu_get_be16(f);
+ s->y = qemu_get_be16(f);
+ s->pressure = qemu_get_byte(f);
+
+ s->state = qemu_get_byte(f);
+ s->reg = qemu_get_byte(f);
+ s->command = qemu_get_byte(f);
+
+ s->irq = qemu_get_byte(f);
+ qemu_get_be16s(f, &s->dav);
+ qemu_get_be16s(f, &s->data);
+
+ qemu_get_timer(f, s->timer);
+ s->enabled = qemu_get_byte(f);
+ s->host_mode = qemu_get_byte(f);
+ s->function = qemu_get_byte(f);
+ s->nextfunction = qemu_get_byte(f);
+ s->precision = qemu_get_byte(f);
+ s->nextprecision = qemu_get_byte(f);
+ s->filter = qemu_get_be16(f);
+ s->pin_func = qemu_get_byte(f);
+ s->timing[0] = qemu_get_be16(f);
+ s->timing[1] = qemu_get_be16(f);
+ qemu_get_be16s(f, &s->temp_thr[0]);
+ qemu_get_be16s(f, &s->temp_thr[1]);
+ qemu_get_be16s(f, &s->aux_thr[0]);
+ qemu_get_be16s(f, &s->aux_thr[1]);
+ s->noise = qemu_get_be32(f);
+ s->reset = qemu_get_byte(f);
+ s->pdst = qemu_get_byte(f);
+ s->pnd0 = qemu_get_byte(f);
+
+ for (i = 0; i < 8; i ++)
+ s->tr[i] = qemu_get_be32(f);
+
+ s->busy = qemu_timer_pending(s->timer);
+ tsc2005_pin_update(s);
+
+ return 0;
+}
+
+void *tsc2005_init(qemu_irq pintdav)
+{
+ TSC2005State *s;
+
+ s = (TSC2005State *)
+ qemu_mallocz(sizeof(TSC2005State));
+ s->x = 400;
+ s->y = 240;
+ s->pressure = 0;
+ s->precision = s->nextprecision = 0;
+ s->timer = qemu_new_timer(vm_clock, tsc2005_timer_tick, s);
+ s->pint = pintdav;
+ s->model = 0x2005;
+
+ s->tr[0] = 0;
+ s->tr[1] = 1;
+ s->tr[2] = 1;
+ s->tr[3] = 0;
+ s->tr[4] = 1;
+ s->tr[5] = 0;
+ s->tr[6] = 1;
+ s->tr[7] = 0;
+
+ tsc2005_reset(s);
+
+ qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1,
+ "QEMU TSC2005-driven Touchscreen");
+
+ qemu_register_reset((void *) tsc2005_reset, s);
+ register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
+
+ return s;
+}
+
+/*
+ * Use tslib generated calibration data to generate ADC input values
+ * from the touchscreen. Assuming 12-bit precision was used during
+ * tslib calibration.
+ */
+void tsc2005_set_transform(void *opaque, MouseTransformInfo *info)
+{
+ TSC2005State *s = (TSC2005State *) opaque;
+
+ /* This version assumes touchscreen X & Y axis are parallel or
+ * perpendicular to LCD's X & Y axis in some way. */
+ if (abs(info->a[0]) > abs(info->a[1])) {
+ s->tr[0] = 0;
+ s->tr[1] = -info->a[6] * info->x;
+ s->tr[2] = info->a[0];
+ s->tr[3] = -info->a[2] / info->a[0];
+ s->tr[4] = info->a[6] * info->y;
+ s->tr[5] = 0;
+ s->tr[6] = info->a[4];
+ s->tr[7] = -info->a[5] / info->a[4];
+ } else {
+ s->tr[0] = info->a[6] * info->y;
+ s->tr[1] = 0;
+ s->tr[2] = info->a[1];
+ s->tr[3] = -info->a[2] / info->a[1];
+ s->tr[4] = 0;
+ s->tr[5] = -info->a[6] * info->x;
+ s->tr[6] = info->a[3];
+ s->tr[7] = -info->a[5] / info->a[3];
+ }
+
+ s->tr[0] >>= 11;
+ s->tr[1] >>= 11;
+ s->tr[3] <<= 4;
+ s->tr[4] >>= 11;
+ s->tr[5] >>= 11;
+ s->tr[7] <<= 4;
+}
diff --git a/hw/tsc210x.c b/hw/tsc210x.c
new file mode 100644
index 0000000..fca73f1
--- /dev/null
+++ b/hw/tsc210x.c
@@ -0,0 +1,1293 @@
+/*
+ * TI TSC2102 (touchscreen/sensors/audio controller) emulator.
+ * TI TSC2301 (touchscreen/sensors/keypad).
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "audio/audio.h"
+#include "qemu-timer.h"
+#include "console.h"
+#include "omap.h" /* For I2SCodec and uWireSlave */
+#include "devices.h"
+
+#define TSC_DATA_REGISTERS_PAGE 0x0
+#define TSC_CONTROL_REGISTERS_PAGE 0x1
+#define TSC_AUDIO_REGISTERS_PAGE 0x2
+
+#define TSC_VERBOSE
+
+#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p]))
+
+typedef struct {
+ qemu_irq pint;
+ qemu_irq kbint;
+ qemu_irq davint;
+ QEMUTimer *timer;
+ QEMUSoundCard card;
+ uWireSlave chip;
+ I2SCodec codec;
+ uint8_t in_fifo[16384];
+ uint8_t out_fifo[16384];
+ uint16_t model;
+
+ int x, y;
+ int pressure;
+
+ int state, page, offset, irq;
+ uint16_t command, dav;
+
+ int busy;
+ int enabled;
+ int host_mode;
+ int function;
+ int nextfunction;
+ int precision;
+ int nextprecision;
+ int filter;
+ int pin_func;
+ int ref;
+ int timing;
+ int noise;
+
+ uint16_t audio_ctrl1;
+ uint16_t audio_ctrl2;
+ uint16_t audio_ctrl3;
+ uint16_t pll[3];
+ uint16_t volume;
+ int64_t volume_change;
+ int softstep;
+ uint16_t dac_power;
+ int64_t powerdown;
+ uint16_t filter_data[0x14];
+
+ const char *name;
+ SWVoiceIn *adc_voice[1];
+ SWVoiceOut *dac_voice[1];
+ int i2s_rx_rate;
+ int i2s_tx_rate;
+
+ int tr[8];
+
+ struct {
+ uint16_t down;
+ uint16_t mask;
+ int scan;
+ int debounce;
+ int mode;
+ int intr;
+ } kb;
+} TSC210xState;
+
+static const int resolution[4] = { 12, 8, 10, 12 };
+
+#define TSC_MODE_NO_SCAN 0x0
+#define TSC_MODE_XY_SCAN 0x1
+#define TSC_MODE_XYZ_SCAN 0x2
+#define TSC_MODE_X 0x3
+#define TSC_MODE_Y 0x4
+#define TSC_MODE_Z 0x5
+#define TSC_MODE_BAT1 0x6
+#define TSC_MODE_BAT2 0x7
+#define TSC_MODE_AUX 0x8
+#define TSC_MODE_AUX_SCAN 0x9
+#define TSC_MODE_TEMP1 0xa
+#define TSC_MODE_PORT_SCAN 0xb
+#define TSC_MODE_TEMP2 0xc
+#define TSC_MODE_XX_DRV 0xd
+#define TSC_MODE_YY_DRV 0xe
+#define TSC_MODE_YX_DRV 0xf
+
+static const uint16_t mode_regs[16] = {
+ 0x0000, /* No scan */
+ 0x0600, /* X, Y scan */
+ 0x0780, /* X, Y, Z scan */
+ 0x0400, /* X */
+ 0x0200, /* Y */
+ 0x0180, /* Z */
+ 0x0040, /* BAT1 */
+ 0x0030, /* BAT2 */
+ 0x0010, /* AUX */
+ 0x0010, /* AUX scan */
+ 0x0004, /* TEMP1 */
+ 0x0070, /* Port scan */
+ 0x0002, /* TEMP2 */
+ 0x0000, /* X+, X- drivers */
+ 0x0000, /* Y+, Y- drivers */
+ 0x0000, /* Y+, X- drivers */
+};
+
+#define X_TRANSFORM(s) \
+ ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
+#define Y_TRANSFORM(s) \
+ ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
+#define Z1_TRANSFORM(s) \
+ ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
+#define Z2_TRANSFORM(s) \
+ ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
+
+#define BAT1_VAL 0x8660
+#define BAT2_VAL 0x0000
+#define AUX1_VAL 0x35c0
+#define AUX2_VAL 0xffff
+#define TEMP1_VAL 0x8c70
+#define TEMP2_VAL 0xa5b0
+
+#define TSC_POWEROFF_DELAY 50
+#define TSC_SOFTSTEP_DELAY 50
+
+static void tsc210x_reset(TSC210xState *s)
+{
+ s->state = 0;
+ s->pin_func = 2;
+ s->enabled = 0;
+ s->busy = 0;
+ s->nextfunction = 0;
+ s->ref = 0;
+ s->timing = 0;
+ s->irq = 0;
+ s->dav = 0;
+
+ s->audio_ctrl1 = 0x0000;
+ s->audio_ctrl2 = 0x4410;
+ s->audio_ctrl3 = 0x0000;
+ s->pll[0] = 0x1004;
+ s->pll[1] = 0x0000;
+ s->pll[2] = 0x1fff;
+ s->volume = 0xffff;
+ s->dac_power = 0x8540;
+ s->softstep = 1;
+ s->volume_change = 0;
+ s->powerdown = 0;
+ s->filter_data[0x00] = 0x6be3;
+ s->filter_data[0x01] = 0x9666;
+ s->filter_data[0x02] = 0x675d;
+ s->filter_data[0x03] = 0x6be3;
+ s->filter_data[0x04] = 0x9666;
+ s->filter_data[0x05] = 0x675d;
+ s->filter_data[0x06] = 0x7d83;
+ s->filter_data[0x07] = 0x84ee;
+ s->filter_data[0x08] = 0x7d83;
+ s->filter_data[0x09] = 0x84ee;
+ s->filter_data[0x0a] = 0x6be3;
+ s->filter_data[0x0b] = 0x9666;
+ s->filter_data[0x0c] = 0x675d;
+ s->filter_data[0x0d] = 0x6be3;
+ s->filter_data[0x0e] = 0x9666;
+ s->filter_data[0x0f] = 0x675d;
+ s->filter_data[0x10] = 0x7d83;
+ s->filter_data[0x11] = 0x84ee;
+ s->filter_data[0x12] = 0x7d83;
+ s->filter_data[0x13] = 0x84ee;
+
+ s->i2s_tx_rate = 0;
+ s->i2s_rx_rate = 0;
+
+ s->kb.scan = 1;
+ s->kb.debounce = 0;
+ s->kb.mask = 0x0000;
+ s->kb.mode = 3;
+ s->kb.intr = 0;
+
+ qemu_set_irq(s->pint, !s->irq);
+ qemu_set_irq(s->davint, !s->dav);
+ qemu_irq_raise(s->kbint);
+}
+
+typedef struct {
+ int rate;
+ int dsor;
+ int fsref;
+} TSC210xRateInfo;
+
+/* { rate, dsor, fsref } */
+static const TSC210xRateInfo tsc2101_rates[] = {
+ /* Fsref / 6.0 */
+ { 7350, 7, 1 },
+ { 8000, 7, 0 },
+ /* Fsref / 5.5 */
+ { 8018, 6, 1 },
+ { 8727, 6, 0 },
+ /* Fsref / 5.0 */
+ { 8820, 5, 1 },
+ { 9600, 5, 0 },
+ /* Fsref / 4.0 */
+ { 11025, 4, 1 },
+ { 12000, 4, 0 },
+ /* Fsref / 3.0 */
+ { 14700, 3, 1 },
+ { 16000, 3, 0 },
+ /* Fsref / 2.0 */
+ { 22050, 2, 1 },
+ { 24000, 2, 0 },
+ /* Fsref / 1.5 */
+ { 29400, 1, 1 },
+ { 32000, 1, 0 },
+ /* Fsref */
+ { 44100, 0, 1 },
+ { 48000, 0, 0 },
+
+ { 0, 0, 0 },
+};
+
+/* { rate, dsor, fsref } */
+static const TSC210xRateInfo tsc2102_rates[] = {
+ /* Fsref / 6.0 */
+ { 7350, 63, 1 },
+ { 8000, 63, 0 },
+ /* Fsref / 6.0 */
+ { 7350, 54, 1 },
+ { 8000, 54, 0 },
+ /* Fsref / 5.0 */
+ { 8820, 45, 1 },
+ { 9600, 45, 0 },
+ /* Fsref / 4.0 */
+ { 11025, 36, 1 },
+ { 12000, 36, 0 },
+ /* Fsref / 3.0 */
+ { 14700, 27, 1 },
+ { 16000, 27, 0 },
+ /* Fsref / 2.0 */
+ { 22050, 18, 1 },
+ { 24000, 18, 0 },
+ /* Fsref / 1.5 */
+ { 29400, 9, 1 },
+ { 32000, 9, 0 },
+ /* Fsref */
+ { 44100, 0, 1 },
+ { 48000, 0, 0 },
+
+ { 0, 0, 0 },
+};
+
+static inline void tsc210x_out_flush(TSC210xState *s, int len)
+{
+ uint8_t *data = s->codec.out.fifo + s->codec.out.start;
+ uint8_t *end = data + len;
+
+ while (data < end)
+ data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data);
+
+ s->codec.out.len -= len;
+ if (s->codec.out.len)
+ memmove(s->codec.out.fifo, end, s->codec.out.len);
+ s->codec.out.start = 0;
+}
+
+static void tsc210x_audio_out_cb(TSC210xState *s, int free_b)
+{
+ if (s->codec.out.len >= free_b) {
+ tsc210x_out_flush(s, free_b);
+ return;
+ }
+
+ s->codec.out.size = MIN(free_b, 16384);
+ qemu_irq_raise(s->codec.tx_start);
+}
+
+static void tsc2102_audio_rate_update(TSC210xState *s)
+{
+ const TSC210xRateInfo *rate;
+
+ s->codec.tx_rate = 0;
+ s->codec.rx_rate = 0;
+ if (s->dac_power & (1 << 15)) /* PWDNC */
+ return;
+
+ for (rate = tsc2102_rates; rate->rate; rate ++)
+ if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */
+ rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */
+ break;
+ if (!rate->rate) {
+ printf("%s: unknown sampling rate configured\n", __FUNCTION__);
+ return;
+ }
+
+ s->codec.tx_rate = rate->rate;
+}
+
+static void tsc2102_audio_output_update(TSC210xState *s)
+{
+ int enable;
+ struct audsettings fmt;
+
+ if (s->dac_voice[0]) {
+ tsc210x_out_flush(s, s->codec.out.len);
+ s->codec.out.size = 0;
+ AUD_set_active_out(s->dac_voice[0], 0);
+ AUD_close_out(&s->card, s->dac_voice[0]);
+ s->dac_voice[0] = NULL;
+ }
+ s->codec.cts = 0;
+
+ enable =
+ (~s->dac_power & (1 << 15)) && /* PWDNC */
+ (~s->dac_power & (1 << 10)); /* DAPWDN */
+ if (!enable || !s->codec.tx_rate)
+ return;
+
+ /* Force our own sampling rate even in slave DAC mode */
+ fmt.endianness = 0;
+ fmt.nchannels = 2;
+ fmt.freq = s->codec.tx_rate;
+ fmt.fmt = AUD_FMT_S16;
+
+ s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
+ "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
+ if (s->dac_voice[0]) {
+ s->codec.cts = 1;
+ AUD_set_active_out(s->dac_voice[0], 1);
+ }
+}
+
+static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg)
+{
+ switch (reg) {
+ case 0x00: /* X */
+ s->dav &= 0xfbff;
+ return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
+ (s->noise & 3);
+
+ case 0x01: /* Y */
+ s->noise ++;
+ s->dav &= 0xfdff;
+ return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
+ (s->noise & 3);
+
+ case 0x02: /* Z1 */
+ s->dav &= 0xfeff;
+ return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
+ (s->noise & 3);
+
+ case 0x03: /* Z2 */
+ s->dav &= 0xff7f;
+ return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
+ (s->noise & 3);
+
+ case 0x04: /* KPData */
+ if ((s->model & 0xff00) == 0x2300) {
+ if (s->kb.intr && (s->kb.mode & 2)) {
+ s->kb.intr = 0;
+ qemu_irq_raise(s->kbint);
+ }
+ return s->kb.down;
+ }
+
+ return 0xffff;
+
+ case 0x05: /* BAT1 */
+ s->dav &= 0xffbf;
+ return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) +
+ (s->noise & 6);
+
+ case 0x06: /* BAT2 */
+ s->dav &= 0xffdf;
+ return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision);
+
+ case 0x07: /* AUX1 */
+ s->dav &= 0xffef;
+ return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision);
+
+ case 0x08: /* AUX2 */
+ s->dav &= 0xfff7;
+ return 0xffff;
+
+ case 0x09: /* TEMP1 */
+ s->dav &= 0xfffb;
+ return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
+ (s->noise & 5);
+
+ case 0x0a: /* TEMP2 */
+ s->dav &= 0xfffd;
+ return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
+ (s->noise & 3);
+
+ case 0x0b: /* DAC */
+ s->dav &= 0xfffe;
+ return 0xffff;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_data_register_read: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ return 0xffff;
+ }
+}
+
+static uint16_t tsc2102_control_register_read(
+ TSC210xState *s, int reg)
+{
+ switch (reg) {
+ case 0x00: /* TSC ADC */
+ return (s->pressure << 15) | ((!s->busy) << 14) |
+ (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter;
+
+ case 0x01: /* Status / Keypad Control */
+ if ((s->model & 0xff00) == 0x2100)
+ return (s->pin_func << 14) | ((!s->enabled) << 13) |
+ (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav;
+ else
+ return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) |
+ (s->kb.debounce << 11);
+
+ case 0x02: /* DAC Control */
+ if ((s->model & 0xff00) == 0x2300)
+ return s->dac_power & 0x8000;
+ else
+ goto bad_reg;
+
+ case 0x03: /* Reference */
+ return s->ref;
+
+ case 0x04: /* Reset */
+ return 0xffff;
+
+ case 0x05: /* Configuration */
+ return s->timing;
+
+ case 0x06: /* Secondary configuration */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2];
+
+ case 0x10: /* Keypad Mask */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ return s->kb.mask;
+
+ default:
+ bad_reg:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_control_register_read: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ return 0xffff;
+ }
+}
+
+static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg)
+{
+ int l_ch, r_ch;
+ uint16_t val;
+
+ switch (reg) {
+ case 0x00: /* Audio Control 1 */
+ return s->audio_ctrl1;
+
+ case 0x01:
+ return 0xff00;
+
+ case 0x02: /* DAC Volume Control */
+ return s->volume;
+
+ case 0x03:
+ return 0x8b00;
+
+ case 0x04: /* Audio Control 2 */
+ l_ch = 1;
+ r_ch = 1;
+ if (s->softstep && !(s->dac_power & (1 << 10))) {
+ l_ch = (qemu_get_clock(vm_clock) >
+ s->volume_change + TSC_SOFTSTEP_DELAY);
+ r_ch = (qemu_get_clock(vm_clock) >
+ s->volume_change + TSC_SOFTSTEP_DELAY);
+ }
+
+ return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2);
+
+ case 0x05: /* Stereo DAC Power Control */
+ return 0x2aa0 | s->dac_power |
+ (((s->dac_power & (1 << 10)) &&
+ (qemu_get_clock(vm_clock) >
+ s->powerdown + TSC_POWEROFF_DELAY)) << 6);
+
+ case 0x06: /* Audio Control 3 */
+ val = s->audio_ctrl3 | 0x0001;
+ s->audio_ctrl3 &= 0xff3f;
+ return val;
+
+ case 0x07: /* LCH_BASS_BOOST_N0 */
+ case 0x08: /* LCH_BASS_BOOST_N1 */
+ case 0x09: /* LCH_BASS_BOOST_N2 */
+ case 0x0a: /* LCH_BASS_BOOST_N3 */
+ case 0x0b: /* LCH_BASS_BOOST_N4 */
+ case 0x0c: /* LCH_BASS_BOOST_N5 */
+ case 0x0d: /* LCH_BASS_BOOST_D1 */
+ case 0x0e: /* LCH_BASS_BOOST_D2 */
+ case 0x0f: /* LCH_BASS_BOOST_D4 */
+ case 0x10: /* LCH_BASS_BOOST_D5 */
+ case 0x11: /* RCH_BASS_BOOST_N0 */
+ case 0x12: /* RCH_BASS_BOOST_N1 */
+ case 0x13: /* RCH_BASS_BOOST_N2 */
+ case 0x14: /* RCH_BASS_BOOST_N3 */
+ case 0x15: /* RCH_BASS_BOOST_N4 */
+ case 0x16: /* RCH_BASS_BOOST_N5 */
+ case 0x17: /* RCH_BASS_BOOST_D1 */
+ case 0x18: /* RCH_BASS_BOOST_D2 */
+ case 0x19: /* RCH_BASS_BOOST_D4 */
+ case 0x1a: /* RCH_BASS_BOOST_D5 */
+ return s->filter_data[reg - 0x07];
+
+ case 0x1b: /* PLL Programmability 1 */
+ return s->pll[0];
+
+ case 0x1c: /* PLL Programmability 2 */
+ return s->pll[1];
+
+ case 0x1d: /* Audio Control 4 */
+ return (!s->softstep) << 14;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_audio_register_read: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ return 0xffff;
+ }
+}
+
+static void tsc2102_data_register_write(
+ TSC210xState *s, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* X */
+ case 0x01: /* Y */
+ case 0x02: /* Z1 */
+ case 0x03: /* Z2 */
+ case 0x05: /* BAT1 */
+ case 0x06: /* BAT2 */
+ case 0x07: /* AUX1 */
+ case 0x08: /* AUX2 */
+ case 0x09: /* TEMP1 */
+ case 0x0a: /* TEMP2 */
+ return;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_data_register_write: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ }
+}
+
+static void tsc2102_control_register_write(
+ TSC210xState *s, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* TSC ADC */
+ s->host_mode = value >> 15;
+ s->enabled = !(value & 0x4000);
+ if (s->busy && !s->enabled)
+ qemu_del_timer(s->timer);
+ s->busy &= s->enabled;
+ s->nextfunction = (value >> 10) & 0xf;
+ s->nextprecision = (value >> 8) & 3;
+ s->filter = value & 0xff;
+ return;
+
+ case 0x01: /* Status / Keypad Control */
+ if ((s->model & 0xff00) == 0x2100)
+ s->pin_func = value >> 14;
+ else {
+ s->kb.scan = (value >> 14) & 1;
+ s->kb.debounce = (value >> 11) & 7;
+ if (s->kb.intr && s->kb.scan) {
+ s->kb.intr = 0;
+ qemu_irq_raise(s->kbint);
+ }
+ }
+ return;
+
+ case 0x02: /* DAC Control */
+ if ((s->model & 0xff00) == 0x2300) {
+ s->dac_power &= 0x7fff;
+ s->dac_power |= 0x8000 & value;
+ } else
+ goto bad_reg;
+ break;
+
+ case 0x03: /* Reference */
+ s->ref = value & 0x1f;
+ return;
+
+ case 0x04: /* Reset */
+ if (value == 0xbb00) {
+ if (s->busy)
+ qemu_del_timer(s->timer);
+ tsc210x_reset(s);
+#ifdef TSC_VERBOSE
+ } else {
+ fprintf(stderr, "tsc2102_control_register_write: "
+ "wrong value written into RESET\n");
+#endif
+ }
+ return;
+
+ case 0x05: /* Configuration */
+ s->timing = value & 0x3f;
+#ifdef TSC_VERBOSE
+ if (value & ~0x3f)
+ fprintf(stderr, "tsc2102_control_register_write: "
+ "wrong value written into CONFIG\n");
+#endif
+ return;
+
+ case 0x06: /* Secondary configuration */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ s->kb.mode = value >> 14;
+ s->pll[2] = value & 0x3ffff;
+ return;
+
+ case 0x10: /* Keypad Mask */
+ if ((s->model & 0xff00) == 0x2100)
+ goto bad_reg;
+ s->kb.mask = value;
+ return;
+
+ default:
+ bad_reg:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_control_register_write: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ }
+}
+
+static void tsc2102_audio_register_write(
+ TSC210xState *s, int reg, uint16_t value)
+{
+ switch (reg) {
+ case 0x00: /* Audio Control 1 */
+ s->audio_ctrl1 = value & 0x0f3f;
+#ifdef TSC_VERBOSE
+ if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7)))
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 1\n");
+#endif
+ tsc2102_audio_rate_update(s);
+ tsc2102_audio_output_update(s);
+ return;
+
+ case 0x01:
+#ifdef TSC_VERBOSE
+ if (value != 0xff00)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into reg 0x01\n");
+#endif
+ return;
+
+ case 0x02: /* DAC Volume Control */
+ s->volume = value;
+ s->volume_change = qemu_get_clock(vm_clock);
+ return;
+
+ case 0x03:
+#ifdef TSC_VERBOSE
+ if (value != 0x8b00)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into reg 0x03\n");
+#endif
+ return;
+
+ case 0x04: /* Audio Control 2 */
+ s->audio_ctrl2 = value & 0xf7f2;
+#ifdef TSC_VERBOSE
+ if (value & ~0xf7fd)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 2\n");
+#endif
+ return;
+
+ case 0x05: /* Stereo DAC Power Control */
+ if ((value & ~s->dac_power) & (1 << 10))
+ s->powerdown = qemu_get_clock(vm_clock);
+
+ s->dac_power = value & 0x9543;
+#ifdef TSC_VERBOSE
+ if ((value & ~0x9543) != 0x2aa0)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Power\n");
+#endif
+ tsc2102_audio_rate_update(s);
+ tsc2102_audio_output_update(s);
+ return;
+
+ case 0x06: /* Audio Control 3 */
+ s->audio_ctrl3 &= 0x00c0;
+ s->audio_ctrl3 |= value & 0xf800;
+#ifdef TSC_VERBOSE
+ if (value & ~0xf8c7)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 3\n");
+#endif
+ tsc2102_audio_output_update(s);
+ return;
+
+ case 0x07: /* LCH_BASS_BOOST_N0 */
+ case 0x08: /* LCH_BASS_BOOST_N1 */
+ case 0x09: /* LCH_BASS_BOOST_N2 */
+ case 0x0a: /* LCH_BASS_BOOST_N3 */
+ case 0x0b: /* LCH_BASS_BOOST_N4 */
+ case 0x0c: /* LCH_BASS_BOOST_N5 */
+ case 0x0d: /* LCH_BASS_BOOST_D1 */
+ case 0x0e: /* LCH_BASS_BOOST_D2 */
+ case 0x0f: /* LCH_BASS_BOOST_D4 */
+ case 0x10: /* LCH_BASS_BOOST_D5 */
+ case 0x11: /* RCH_BASS_BOOST_N0 */
+ case 0x12: /* RCH_BASS_BOOST_N1 */
+ case 0x13: /* RCH_BASS_BOOST_N2 */
+ case 0x14: /* RCH_BASS_BOOST_N3 */
+ case 0x15: /* RCH_BASS_BOOST_N4 */
+ case 0x16: /* RCH_BASS_BOOST_N5 */
+ case 0x17: /* RCH_BASS_BOOST_D1 */
+ case 0x18: /* RCH_BASS_BOOST_D2 */
+ case 0x19: /* RCH_BASS_BOOST_D4 */
+ case 0x1a: /* RCH_BASS_BOOST_D5 */
+ s->filter_data[reg - 0x07] = value;
+ return;
+
+ case 0x1b: /* PLL Programmability 1 */
+ s->pll[0] = value & 0xfffc;
+#ifdef TSC_VERBOSE
+ if (value & ~0xfffc)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into PLL 1\n");
+#endif
+ return;
+
+ case 0x1c: /* PLL Programmability 2 */
+ s->pll[1] = value & 0xfffc;
+#ifdef TSC_VERBOSE
+ if (value & ~0xfffc)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into PLL 2\n");
+#endif
+ return;
+
+ case 0x1d: /* Audio Control 4 */
+ s->softstep = !(value & 0x4000);
+#ifdef TSC_VERBOSE
+ if (value & ~0x4000)
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "wrong value written into Audio 4\n");
+#endif
+ return;
+
+ default:
+#ifdef TSC_VERBOSE
+ fprintf(stderr, "tsc2102_audio_register_write: "
+ "no such register: 0x%02x\n", reg);
+#endif
+ }
+}
+
+/* This handles most of the chip logic. */
+static void tsc210x_pin_update(TSC210xState *s)
+{
+ int64_t expires;
+ int pin_state;
+
+ switch (s->pin_func) {
+ case 0:
+ pin_state = s->pressure;
+ break;
+ case 1:
+ pin_state = !!s->dav;
+ break;
+ case 2:
+ default:
+ pin_state = s->pressure && !s->dav;
+ }
+
+ if (!s->enabled)
+ pin_state = 0;
+
+ if (pin_state != s->irq) {
+ s->irq = pin_state;
+ qemu_set_irq(s->pint, !s->irq);
+ }
+
+ switch (s->nextfunction) {
+ case TSC_MODE_XY_SCAN:
+ case TSC_MODE_XYZ_SCAN:
+ if (!s->pressure)
+ return;
+ break;
+
+ case TSC_MODE_X:
+ case TSC_MODE_Y:
+ case TSC_MODE_Z:
+ if (!s->pressure)
+ return;
+ /* Fall through */
+ case TSC_MODE_BAT1:
+ case TSC_MODE_BAT2:
+ case TSC_MODE_AUX:
+ case TSC_MODE_TEMP1:
+ case TSC_MODE_TEMP2:
+ if (s->dav)
+ s->enabled = 0;
+ break;
+
+ case TSC_MODE_AUX_SCAN:
+ case TSC_MODE_PORT_SCAN:
+ break;
+
+ case TSC_MODE_NO_SCAN:
+ case TSC_MODE_XX_DRV:
+ case TSC_MODE_YY_DRV:
+ case TSC_MODE_YX_DRV:
+ default:
+ return;
+ }
+
+ if (!s->enabled || s->busy || s->dav)
+ return;
+
+ s->busy = 1;
+ s->precision = s->nextprecision;
+ s->function = s->nextfunction;
+ expires = qemu_get_clock(vm_clock) + (get_ticks_per_sec() >> 10);
+ qemu_mod_timer(s->timer, expires);
+}
+
+static uint16_t tsc210x_read(TSC210xState *s)
+{
+ uint16_t ret = 0x0000;
+
+ if (!s->command)
+ fprintf(stderr, "tsc210x_read: SPI underrun!\n");
+
+ switch (s->page) {
+ case TSC_DATA_REGISTERS_PAGE:
+ ret = tsc2102_data_register_read(s, s->offset);
+ if (!s->dav)
+ qemu_irq_raise(s->davint);
+ break;
+ case TSC_CONTROL_REGISTERS_PAGE:
+ ret = tsc2102_control_register_read(s, s->offset);
+ break;
+ case TSC_AUDIO_REGISTERS_PAGE:
+ ret = tsc2102_audio_register_read(s, s->offset);
+ break;
+ default:
+ hw_error("tsc210x_read: wrong memory page\n");
+ }
+
+ tsc210x_pin_update(s);
+
+ /* Allow sequential reads. */
+ s->offset ++;
+ s->state = 0;
+ return ret;
+}
+
+static void tsc210x_write(TSC210xState *s, uint16_t value)
+{
+ /*
+ * This is a two-state state machine for reading
+ * command and data every second time.
+ */
+ if (!s->state) {
+ s->command = value >> 15;
+ s->page = (value >> 11) & 0x0f;
+ s->offset = (value >> 5) & 0x3f;
+ s->state = 1;
+ } else {
+ if (s->command)
+ fprintf(stderr, "tsc210x_write: SPI overrun!\n");
+ else
+ switch (s->page) {
+ case TSC_DATA_REGISTERS_PAGE:
+ tsc2102_data_register_write(s, s->offset, value);
+ break;
+ case TSC_CONTROL_REGISTERS_PAGE:
+ tsc2102_control_register_write(s, s->offset, value);
+ break;
+ case TSC_AUDIO_REGISTERS_PAGE:
+ tsc2102_audio_register_write(s, s->offset, value);
+ break;
+ default:
+ hw_error("tsc210x_write: wrong memory page\n");
+ }
+
+ tsc210x_pin_update(s);
+ s->state = 0;
+ }
+}
+
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len)
+{
+ TSC210xState *s = opaque;
+ uint32_t ret = 0;
+
+ if (len != 16)
+ hw_error("%s: FIXME: bad SPI word width %i\n", __FUNCTION__, len);
+
+ /* TODO: sequential reads etc - how do we make sure the host doesn't
+ * unintentionally read out a conversion result from a register while
+ * transmitting the command word of the next command? */
+ if (!value || (s->state && s->command))
+ ret = tsc210x_read(s);
+ if (value || (s->state && !s->command))
+ tsc210x_write(s, value);
+
+ return ret;
+}
+
+static void tsc210x_timer_tick(void *opaque)
+{
+ TSC210xState *s = opaque;
+
+ /* Timer ticked -- a set of conversions has been finished. */
+
+ if (!s->busy)
+ return;
+
+ s->busy = 0;
+ s->dav |= mode_regs[s->function];
+ tsc210x_pin_update(s);
+ qemu_irq_lower(s->davint);
+}
+
+static void tsc210x_touchscreen_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ TSC210xState *s = opaque;
+ int p = s->pressure;
+
+ if (buttons_state) {
+ s->x = x;
+ s->y = y;
+ }
+ s->pressure = !!buttons_state;
+
+ /*
+ * Note: We would get better responsiveness in the guest by
+ * signaling TS events immediately, but for now we simulate
+ * the first conversion delay for sake of correctness.
+ */
+ if (p != s->pressure)
+ tsc210x_pin_update(s);
+}
+
+static void tsc210x_i2s_swallow(TSC210xState *s)
+{
+ if (s->dac_voice[0])
+ tsc210x_out_flush(s, s->codec.out.len);
+ else
+ s->codec.out.len = 0;
+}
+
+static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out)
+{
+ s->i2s_tx_rate = out;
+ s->i2s_rx_rate = in;
+}
+
+static void tsc210x_save(QEMUFile *f, void *opaque)
+{
+ TSC210xState *s = (TSC210xState *) opaque;
+ int64_t now = qemu_get_clock(vm_clock);
+ int i;
+
+ qemu_put_be16(f, s->x);
+ qemu_put_be16(f, s->y);
+ qemu_put_byte(f, s->pressure);
+
+ qemu_put_byte(f, s->state);
+ qemu_put_byte(f, s->page);
+ qemu_put_byte(f, s->offset);
+ qemu_put_byte(f, s->command);
+
+ qemu_put_byte(f, s->irq);
+ qemu_put_be16s(f, &s->dav);
+
+ qemu_put_timer(f, s->timer);
+ qemu_put_byte(f, s->enabled);
+ qemu_put_byte(f, s->host_mode);
+ qemu_put_byte(f, s->function);
+ qemu_put_byte(f, s->nextfunction);
+ qemu_put_byte(f, s->precision);
+ qemu_put_byte(f, s->nextprecision);
+ qemu_put_byte(f, s->filter);
+ qemu_put_byte(f, s->pin_func);
+ qemu_put_byte(f, s->ref);
+ qemu_put_byte(f, s->timing);
+ qemu_put_be32(f, s->noise);
+
+ qemu_put_be16s(f, &s->audio_ctrl1);
+ qemu_put_be16s(f, &s->audio_ctrl2);
+ qemu_put_be16s(f, &s->audio_ctrl3);
+ qemu_put_be16s(f, &s->pll[0]);
+ qemu_put_be16s(f, &s->pll[1]);
+ qemu_put_be16s(f, &s->volume);
+ qemu_put_sbe64(f, (s->volume_change - now));
+ qemu_put_sbe64(f, (s->powerdown - now));
+ qemu_put_byte(f, s->softstep);
+ qemu_put_be16s(f, &s->dac_power);
+
+ for (i = 0; i < 0x14; i ++)
+ qemu_put_be16s(f, &s->filter_data[i]);
+}
+
+static int tsc210x_load(QEMUFile *f, void *opaque, int version_id)
+{
+ TSC210xState *s = (TSC210xState *) opaque;
+ int64_t now = qemu_get_clock(vm_clock);
+ int i;
+
+ s->x = qemu_get_be16(f);
+ s->y = qemu_get_be16(f);
+ s->pressure = qemu_get_byte(f);
+
+ s->state = qemu_get_byte(f);
+ s->page = qemu_get_byte(f);
+ s->offset = qemu_get_byte(f);
+ s->command = qemu_get_byte(f);
+
+ s->irq = qemu_get_byte(f);
+ qemu_get_be16s(f, &s->dav);
+
+ qemu_get_timer(f, s->timer);
+ s->enabled = qemu_get_byte(f);
+ s->host_mode = qemu_get_byte(f);
+ s->function = qemu_get_byte(f);
+ s->nextfunction = qemu_get_byte(f);
+ s->precision = qemu_get_byte(f);
+ s->nextprecision = qemu_get_byte(f);
+ s->filter = qemu_get_byte(f);
+ s->pin_func = qemu_get_byte(f);
+ s->ref = qemu_get_byte(f);
+ s->timing = qemu_get_byte(f);
+ s->noise = qemu_get_be32(f);
+
+ qemu_get_be16s(f, &s->audio_ctrl1);
+ qemu_get_be16s(f, &s->audio_ctrl2);
+ qemu_get_be16s(f, &s->audio_ctrl3);
+ qemu_get_be16s(f, &s->pll[0]);
+ qemu_get_be16s(f, &s->pll[1]);
+ qemu_get_be16s(f, &s->volume);
+ s->volume_change = qemu_get_sbe64(f) + now;
+ s->powerdown = qemu_get_sbe64(f) + now;
+ s->softstep = qemu_get_byte(f);
+ qemu_get_be16s(f, &s->dac_power);
+
+ for (i = 0; i < 0x14; i ++)
+ qemu_get_be16s(f, &s->filter_data[i]);
+
+ s->busy = qemu_timer_pending(s->timer);
+ qemu_set_irq(s->pint, !s->irq);
+ qemu_set_irq(s->davint, !s->dav);
+
+ return 0;
+}
+
+uWireSlave *tsc2102_init(qemu_irq pint)
+{
+ TSC210xState *s;
+
+ s = (TSC210xState *)
+ qemu_mallocz(sizeof(TSC210xState));
+ memset(s, 0, sizeof(TSC210xState));
+ s->x = 160;
+ s->y = 160;
+ s->pressure = 0;
+ s->precision = s->nextprecision = 0;
+ s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s);
+ s->pint = pint;
+ s->model = 0x2102;
+ s->name = "tsc2102";
+
+ s->tr[0] = 0;
+ s->tr[1] = 1;
+ s->tr[2] = 1;
+ s->tr[3] = 0;
+ s->tr[4] = 1;
+ s->tr[5] = 0;
+ s->tr[6] = 1;
+ s->tr[7] = 0;
+
+ s->chip.opaque = s;
+ s->chip.send = (void *) tsc210x_write;
+ s->chip.receive = (void *) tsc210x_read;
+
+ s->codec.opaque = s;
+ s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
+ s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
+ s->codec.in.fifo = s->in_fifo;
+ s->codec.out.fifo = s->out_fifo;
+
+ tsc210x_reset(s);
+
+ qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
+ "QEMU TSC2102-driven Touchscreen");
+
+ AUD_register_card(s->name, &s->card);
+
+ qemu_register_reset((void *) tsc210x_reset, s);
+ register_savevm(NULL, s->name, -1, 0,
+ tsc210x_save, tsc210x_load, s);
+
+ return &s->chip;
+}
+
+uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav)
+{
+ TSC210xState *s;
+
+ s = (TSC210xState *)
+ qemu_mallocz(sizeof(TSC210xState));
+ memset(s, 0, sizeof(TSC210xState));
+ s->x = 400;
+ s->y = 240;
+ s->pressure = 0;
+ s->precision = s->nextprecision = 0;
+ s->timer = qemu_new_timer(vm_clock, tsc210x_timer_tick, s);
+ s->pint = penirq;
+ s->kbint = kbirq;
+ s->davint = dav;
+ s->model = 0x2301;
+ s->name = "tsc2301";
+
+ s->tr[0] = 0;
+ s->tr[1] = 1;
+ s->tr[2] = 1;
+ s->tr[3] = 0;
+ s->tr[4] = 1;
+ s->tr[5] = 0;
+ s->tr[6] = 1;
+ s->tr[7] = 0;
+
+ s->chip.opaque = s;
+ s->chip.send = (void *) tsc210x_write;
+ s->chip.receive = (void *) tsc210x_read;
+
+ s->codec.opaque = s;
+ s->codec.tx_swallow = (void *) tsc210x_i2s_swallow;
+ s->codec.set_rate = (void *) tsc210x_i2s_set_rate;
+ s->codec.in.fifo = s->in_fifo;
+ s->codec.out.fifo = s->out_fifo;
+
+ tsc210x_reset(s);
+
+ qemu_add_mouse_event_handler(tsc210x_touchscreen_event, s, 1,
+ "QEMU TSC2301-driven Touchscreen");
+
+ AUD_register_card(s->name, &s->card);
+
+ qemu_register_reset((void *) tsc210x_reset, s);
+ register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s);
+
+ return &s->chip;
+}
+
+I2SCodec *tsc210x_codec(uWireSlave *chip)
+{
+ TSC210xState *s = (TSC210xState *) chip->opaque;
+
+ return &s->codec;
+}
+
+/*
+ * Use tslib generated calibration data to generate ADC input values
+ * from the touchscreen. Assuming 12-bit precision was used during
+ * tslib calibration.
+ */
+void tsc210x_set_transform(uWireSlave *chip,
+ MouseTransformInfo *info)
+{
+ TSC210xState *s = (TSC210xState *) chip->opaque;
+#if 0
+ int64_t ltr[8];
+
+ ltr[0] = (int64_t) info->a[1] * info->y;
+ ltr[1] = (int64_t) info->a[4] * info->x;
+ ltr[2] = (int64_t) info->a[1] * info->a[3] -
+ (int64_t) info->a[4] * info->a[0];
+ ltr[3] = (int64_t) info->a[2] * info->a[4] -
+ (int64_t) info->a[5] * info->a[1];
+ ltr[4] = (int64_t) info->a[0] * info->y;
+ ltr[5] = (int64_t) info->a[3] * info->x;
+ ltr[6] = (int64_t) info->a[4] * info->a[0] -
+ (int64_t) info->a[1] * info->a[3];
+ ltr[7] = (int64_t) info->a[2] * info->a[3] -
+ (int64_t) info->a[5] * info->a[0];
+
+ /* Avoid integer overflow */
+ s->tr[0] = ltr[0] >> 11;
+ s->tr[1] = ltr[1] >> 11;
+ s->tr[2] = muldiv64(ltr[2], 1, info->a[6]);
+ s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]);
+ s->tr[4] = ltr[4] >> 11;
+ s->tr[5] = ltr[5] >> 11;
+ s->tr[6] = muldiv64(ltr[6], 1, info->a[6]);
+ s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]);
+#else
+
+ /* This version assumes touchscreen X & Y axis are parallel or
+ * perpendicular to LCD's X & Y axis in some way. */
+ if (abs(info->a[0]) > abs(info->a[1])) {
+ s->tr[0] = 0;
+ s->tr[1] = -info->a[6] * info->x;
+ s->tr[2] = info->a[0];
+ s->tr[3] = -info->a[2] / info->a[0];
+ s->tr[4] = info->a[6] * info->y;
+ s->tr[5] = 0;
+ s->tr[6] = info->a[4];
+ s->tr[7] = -info->a[5] / info->a[4];
+ } else {
+ s->tr[0] = info->a[6] * info->y;
+ s->tr[1] = 0;
+ s->tr[2] = info->a[1];
+ s->tr[3] = -info->a[2] / info->a[1];
+ s->tr[4] = 0;
+ s->tr[5] = -info->a[6] * info->x;
+ s->tr[6] = info->a[3];
+ s->tr[7] = -info->a[5] / info->a[3];
+ }
+
+ s->tr[0] >>= 11;
+ s->tr[1] >>= 11;
+ s->tr[3] <<= 4;
+ s->tr[4] >>= 11;
+ s->tr[5] >>= 11;
+ s->tr[7] <<= 4;
+#endif
+}
+
+void tsc210x_key_event(uWireSlave *chip, int key, int down)
+{
+ TSC210xState *s = (TSC210xState *) chip->opaque;
+
+ if (down)
+ s->kb.down |= 1 << key;
+ else
+ s->kb.down &= ~(1 << key);
+
+ if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) {
+ s->kb.intr = 1;
+ qemu_irq_lower(s->kbint);
+ } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) &&
+ !(s->kb.mode & 1)) {
+ s->kb.intr = 0;
+ qemu_irq_raise(s->kbint);
+ }
+}
diff --git a/hw/tusb6010.c b/hw/tusb6010.c
new file mode 100644
index 0000000..0005e1c
--- /dev/null
+++ b/hw/tusb6010.c
@@ -0,0 +1,766 @@
+/*
+ * Texas Instruments TUSB6010 emulation.
+ * Based on reverse-engineering of a linux driver.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "omap.h"
+#include "irq.h"
+#include "devices.h"
+
+struct TUSBState {
+ int iomemtype[2];
+ qemu_irq irq;
+ MUSBState *musb;
+ QEMUTimer *otg_timer;
+ QEMUTimer *pwr_timer;
+
+ int power;
+ uint32_t scratch;
+ uint16_t test_reset;
+ uint32_t prcm_config;
+ uint32_t prcm_mngmt;
+ uint16_t otg_status;
+ uint32_t dev_config;
+ int host_mode;
+ uint32_t intr;
+ uint32_t intr_ok;
+ uint32_t mask;
+ uint32_t usbip_intr;
+ uint32_t usbip_mask;
+ uint32_t gpio_intr;
+ uint32_t gpio_mask;
+ uint32_t gpio_config;
+ uint32_t dma_intr;
+ uint32_t dma_mask;
+ uint32_t dma_map;
+ uint32_t dma_config;
+ uint32_t ep0_config;
+ uint32_t rx_config[15];
+ uint32_t tx_config[15];
+ uint32_t wkup_mask;
+ uint32_t pullup[2];
+ uint32_t control_config;
+ uint32_t otg_timer_val;
+};
+
+#define TUSB_DEVCLOCK 60000000 /* 60 MHz */
+
+#define TUSB_VLYNQ_CTRL 0x004
+
+/* Mentor Graphics OTG core registers. */
+#define TUSB_BASE_OFFSET 0x400
+
+/* FIFO registers, 32-bit. */
+#define TUSB_FIFO_BASE 0x600
+
+/* Device System & Control registers, 32-bit. */
+#define TUSB_SYS_REG_BASE 0x800
+
+#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000)
+#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16)
+#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15)
+#define TUSB_DEV_CONF_SOFT_ID (1 << 1)
+#define TUSB_DEV_CONF_ID_SEL (1 << 0)
+
+#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004)
+#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008)
+#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24)
+#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23)
+#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19)
+#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18)
+#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17)
+#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16)
+#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15)
+#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14)
+#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13)
+#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12)
+#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11)
+#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10)
+#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9)
+#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7)
+#define TUSB_PHY_OTG_CTRL_PD (1 << 6)
+#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5)
+#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4)
+#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3)
+#define TUSB_PHY_OTG_CTRL_RESET (1 << 2)
+#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1)
+#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0)
+
+/* OTG status register */
+#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c)
+#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8)
+#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7)
+#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6)
+#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5)
+#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4)
+#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3)
+#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2)
+#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0)
+#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1)
+#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0)
+
+#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010)
+#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31)
+#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff)
+#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014)
+
+/* PRCM configuration register */
+#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018)
+#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24)
+#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16)
+
+/* PRCM management register */
+#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c)
+#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25)
+#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24)
+#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20)
+#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19)
+#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18)
+#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17)
+#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10)
+#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9)
+#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8)
+#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4)
+#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3)
+#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2)
+#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1)
+#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0)
+
+/* Wake-up source clear and mask registers */
+#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020)
+#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028)
+#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c)
+#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13)
+#define TUSB_PRCM_WGPIO_7 (1 << 12)
+#define TUSB_PRCM_WGPIO_6 (1 << 11)
+#define TUSB_PRCM_WGPIO_5 (1 << 10)
+#define TUSB_PRCM_WGPIO_4 (1 << 9)
+#define TUSB_PRCM_WGPIO_3 (1 << 8)
+#define TUSB_PRCM_WGPIO_2 (1 << 7)
+#define TUSB_PRCM_WGPIO_1 (1 << 6)
+#define TUSB_PRCM_WGPIO_0 (1 << 5)
+#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */
+#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */
+#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */
+#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */
+#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */
+
+#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030)
+#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034)
+#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038)
+#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c)
+#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040)
+#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044)
+#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048)
+#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c)
+#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050)
+#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054)
+#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058)
+#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c)
+#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060)
+#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064)
+#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068)
+#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c)
+
+/* NOR flash interrupt source registers */
+#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070)
+#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074)
+#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078)
+#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c)
+#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24)
+#define TUSB_INT_SRC_USB_IP_CORE (1 << 17)
+#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16)
+#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15)
+#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14)
+#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13)
+#define TUSB_INT_SRC_DEV_READY (1 << 12)
+#define TUSB_INT_SRC_USB_IP_TX (1 << 9)
+#define TUSB_INT_SRC_USB_IP_RX (1 << 8)
+#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7)
+#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6)
+#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5)
+#define TUSB_INT_SRC_USB_IP_CONN (1 << 4)
+#define TUSB_INT_SRC_USB_IP_SOF (1 << 3)
+#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2)
+#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1)
+#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0)
+
+#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080)
+#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084)
+#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100)
+#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104)
+#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108)
+#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c)
+#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148)
+#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c)
+#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188)
+#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4)
+#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8)
+#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8)
+
+#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8)
+#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc)
+
+/* Device System & Control register bitfields */
+#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18)
+#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17)
+#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16)
+#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24)
+#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26)
+#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20)
+#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16)
+#define TUSB_EP0_CONFIG_SW_EN (1 << 8)
+#define TUSB_EP0_CONFIG_DIR_TX (1 << 7)
+#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f)
+#define TUSB_EP_CONFIG_SW_EN (1 << 31)
+#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff)
+#define TUSB_PROD_TEST_RESET_VAL 0xa596
+
+int tusb6010_sync_io(TUSBState *s)
+{
+ return s->iomemtype[0];
+}
+
+int tusb6010_async_io(TUSBState *s)
+{
+ return s->iomemtype[1];
+}
+
+static void tusb_intr_update(TUSBState *s)
+{
+ if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY)
+ qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok);
+ else
+ qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok);
+}
+
+static void tusb_usbip_intr_update(TUSBState *s)
+{
+ /* TX interrupt in the MUSB */
+ if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask)
+ s->intr |= TUSB_INT_SRC_USB_IP_TX;
+ else
+ s->intr &= ~TUSB_INT_SRC_USB_IP_TX;
+
+ /* RX interrupt in the MUSB */
+ if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask)
+ s->intr |= TUSB_INT_SRC_USB_IP_RX;
+ else
+ s->intr &= ~TUSB_INT_SRC_USB_IP_RX;
+
+ /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */
+
+ tusb_intr_update(s);
+}
+
+static void tusb_dma_intr_update(TUSBState *s)
+{
+ if (s->dma_intr & ~s->dma_mask)
+ s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE;
+ else
+ s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE;
+
+ tusb_intr_update(s);
+}
+
+static void tusb_gpio_intr_update(TUSBState *s)
+{
+ /* TODO: How is this signalled? */
+}
+
+extern CPUReadMemoryFunc * const musb_read[];
+extern CPUWriteMemoryFunc * const musb_write[];
+
+static uint32_t tusb_async_readb(void *opaque, target_phys_addr_t addr)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ return musb_read[0](s->musb, addr & 0x1ff);
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c));
+ }
+
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return 0;
+}
+
+static uint32_t tusb_async_readh(void *opaque, target_phys_addr_t addr)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ return musb_read[1](s->musb, addr & 0x1ff);
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c));
+ }
+
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return 0;
+}
+
+static uint32_t tusb_async_readw(void *opaque, target_phys_addr_t addr)
+{
+ TUSBState *s = (TUSBState *) opaque;
+ int offset = addr & 0xfff;
+ int epnum;
+ uint32_t ret;
+
+ switch (offset) {
+ case TUSB_DEV_CONF:
+ return s->dev_config;
+
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ return musb_read[2](s->musb, offset & 0x1ff);
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c));
+
+ case TUSB_PHY_OTG_CTRL_ENABLE:
+ case TUSB_PHY_OTG_CTRL:
+ return 0x00; /* TODO */
+
+ case TUSB_DEV_OTG_STAT:
+ ret = s->otg_status;
+#if 0
+ if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN))
+ ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID;
+#endif
+ return ret;
+ case TUSB_DEV_OTG_TIMER:
+ return s->otg_timer_val;
+
+ case TUSB_PRCM_REV:
+ return 0x20;
+ case TUSB_PRCM_CONF:
+ return s->prcm_config;
+ case TUSB_PRCM_MNGMT:
+ return s->prcm_mngmt;
+ case TUSB_PRCM_WAKEUP_SOURCE:
+ case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */
+ return 0x00000000;
+ case TUSB_PRCM_WAKEUP_MASK:
+ return s->wkup_mask;
+
+ case TUSB_PULLUP_1_CTRL:
+ return s->pullup[0];
+ case TUSB_PULLUP_2_CTRL:
+ return s->pullup[1];
+
+ case TUSB_INT_CTRL_REV:
+ return 0x20;
+ case TUSB_INT_CTRL_CONF:
+ return s->control_config;
+
+ case TUSB_USBIP_INT_SRC:
+ case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */
+ case TUSB_USBIP_INT_CLEAR:
+ return s->usbip_intr;
+ case TUSB_USBIP_INT_MASK:
+ return s->usbip_mask;
+
+ case TUSB_DMA_INT_SRC:
+ case TUSB_DMA_INT_SET: /* TODO: What do these two return? */
+ case TUSB_DMA_INT_CLEAR:
+ return s->dma_intr;
+ case TUSB_DMA_INT_MASK:
+ return s->dma_mask;
+
+ case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */
+ case TUSB_GPIO_INT_SET:
+ case TUSB_GPIO_INT_CLEAR:
+ return s->gpio_intr;
+ case TUSB_GPIO_INT_MASK:
+ return s->gpio_mask;
+
+ case TUSB_INT_SRC:
+ case TUSB_INT_SRC_SET: /* TODO: What do these two return? */
+ case TUSB_INT_SRC_CLEAR:
+ return s->intr;
+ case TUSB_INT_MASK:
+ return s->mask;
+
+ case TUSB_GPIO_REV:
+ return 0x30;
+ case TUSB_GPIO_CONF:
+ return s->gpio_config;
+
+ case TUSB_DMA_CTRL_REV:
+ return 0x30;
+ case TUSB_DMA_REQ_CONF:
+ return s->dma_config;
+ case TUSB_EP0_CONF:
+ return s->ep0_config;
+ case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_IN_SIZE) >> 2;
+ return s->tx_config[epnum];
+ case TUSB_DMA_EP_MAP:
+ return s->dma_map;
+ case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_OUT_SIZE) >> 2;
+ return s->rx_config[epnum];
+ case TUSB_EP_MAX_PACKET_SIZE_OFFSET ...
+ (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b):
+ return 0x00000000; /* TODO */
+ case TUSB_WAIT_COUNT:
+ return 0x00; /* TODO */
+
+ case TUSB_SCRATCH_PAD:
+ return s->scratch;
+
+ case TUSB_PROD_TEST_RESET:
+ return s->test_reset;
+
+ /* DIE IDs */
+ case TUSB_DIDR1_LO:
+ return 0xa9453c59;
+ case TUSB_DIDR1_HI:
+ return 0x54059adf;
+ }
+
+ printf("%s: unknown register at %03x\n", __FUNCTION__, offset);
+ return 0;
+}
+
+static void tusb_async_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ musb_write[0](s->musb, addr & 0x1ff, value);
+ break;
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
+ break;
+
+ default:
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return;
+ }
+}
+
+static void tusb_async_writeh(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ switch (addr & 0xfff) {
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ musb_write[1](s->musb, addr & 0x1ff, value);
+ break;
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
+ break;
+
+ default:
+ printf("%s: unknown register at %03x\n",
+ __FUNCTION__, (int) (addr & 0xfff));
+ return;
+ }
+}
+
+static void tusb_async_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ TUSBState *s = (TUSBState *) opaque;
+ int offset = addr & 0xfff;
+ int epnum;
+
+ switch (offset) {
+ case TUSB_VLYNQ_CTRL:
+ break;
+
+ case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff):
+ musb_write[2](s->musb, offset & 0x1ff, value);
+ break;
+
+ case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff):
+ musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
+ break;
+
+ case TUSB_DEV_CONF:
+ s->dev_config = value;
+ s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE);
+ if (value & TUSB_DEV_CONF_PROD_TEST_MODE)
+ hw_error("%s: Product Test mode not allowed\n", __FUNCTION__);
+ break;
+
+ case TUSB_PHY_OTG_CTRL_ENABLE:
+ case TUSB_PHY_OTG_CTRL:
+ return; /* TODO */
+ case TUSB_DEV_OTG_TIMER:
+ s->otg_timer_val = value;
+ if (value & TUSB_DEV_OTG_TIMER_ENABLE)
+ qemu_mod_timer(s->otg_timer, qemu_get_clock(vm_clock) +
+ muldiv64(TUSB_DEV_OTG_TIMER_VAL(value),
+ get_ticks_per_sec(), TUSB_DEVCLOCK));
+ else
+ qemu_del_timer(s->otg_timer);
+ break;
+
+ case TUSB_PRCM_CONF:
+ s->prcm_config = value;
+ break;
+ case TUSB_PRCM_MNGMT:
+ s->prcm_mngmt = value;
+ break;
+ case TUSB_PRCM_WAKEUP_CLEAR:
+ break;
+ case TUSB_PRCM_WAKEUP_MASK:
+ s->wkup_mask = value;
+ break;
+
+ case TUSB_PULLUP_1_CTRL:
+ s->pullup[0] = value;
+ break;
+ case TUSB_PULLUP_2_CTRL:
+ s->pullup[1] = value;
+ break;
+ case TUSB_INT_CTRL_CONF:
+ s->control_config = value;
+ tusb_intr_update(s);
+ break;
+
+ case TUSB_USBIP_INT_SET:
+ s->usbip_intr |= value;
+ tusb_usbip_intr_update(s);
+ break;
+ case TUSB_USBIP_INT_CLEAR:
+ s->usbip_intr &= ~value;
+ tusb_usbip_intr_update(s);
+ musb_core_intr_clear(s->musb, ~value);
+ break;
+ case TUSB_USBIP_INT_MASK:
+ s->usbip_mask = value;
+ tusb_usbip_intr_update(s);
+ break;
+
+ case TUSB_DMA_INT_SET:
+ s->dma_intr |= value;
+ tusb_dma_intr_update(s);
+ break;
+ case TUSB_DMA_INT_CLEAR:
+ s->dma_intr &= ~value;
+ tusb_dma_intr_update(s);
+ break;
+ case TUSB_DMA_INT_MASK:
+ s->dma_mask = value;
+ tusb_dma_intr_update(s);
+ break;
+
+ case TUSB_GPIO_INT_SET:
+ s->gpio_intr |= value;
+ tusb_gpio_intr_update(s);
+ break;
+ case TUSB_GPIO_INT_CLEAR:
+ s->gpio_intr &= ~value;
+ tusb_gpio_intr_update(s);
+ break;
+ case TUSB_GPIO_INT_MASK:
+ s->gpio_mask = value;
+ tusb_gpio_intr_update(s);
+ break;
+
+ case TUSB_INT_SRC_SET:
+ s->intr |= value;
+ tusb_intr_update(s);
+ break;
+ case TUSB_INT_SRC_CLEAR:
+ s->intr &= ~value;
+ tusb_intr_update(s);
+ break;
+ case TUSB_INT_MASK:
+ s->mask = value;
+ tusb_intr_update(s);
+ break;
+
+ case TUSB_GPIO_CONF:
+ s->gpio_config = value;
+ break;
+ case TUSB_DMA_REQ_CONF:
+ s->dma_config = value;
+ break;
+ case TUSB_EP0_CONF:
+ s->ep0_config = value & 0x1ff;
+ musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value),
+ value & TUSB_EP0_CONFIG_DIR_TX);
+ break;
+ case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_IN_SIZE) >> 2;
+ s->tx_config[epnum] = value;
+ musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1);
+ break;
+ case TUSB_DMA_EP_MAP:
+ s->dma_map = value;
+ break;
+ case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b):
+ epnum = (offset - TUSB_EP_OUT_SIZE) >> 2;
+ s->rx_config[epnum] = value;
+ musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0);
+ break;
+ case TUSB_EP_MAX_PACKET_SIZE_OFFSET ...
+ (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b):
+ return; /* TODO */
+ case TUSB_WAIT_COUNT:
+ return; /* TODO */
+
+ case TUSB_SCRATCH_PAD:
+ s->scratch = value;
+ break;
+
+ case TUSB_PROD_TEST_RESET:
+ s->test_reset = value;
+ break;
+
+ default:
+ printf("%s: unknown register at %03x\n", __FUNCTION__, offset);
+ return;
+ }
+}
+
+static CPUReadMemoryFunc * const tusb_async_readfn[] = {
+ tusb_async_readb,
+ tusb_async_readh,
+ tusb_async_readw,
+};
+
+static CPUWriteMemoryFunc * const tusb_async_writefn[] = {
+ tusb_async_writeb,
+ tusb_async_writeh,
+ tusb_async_writew,
+};
+
+static void tusb_otg_tick(void *opaque)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ s->otg_timer_val = 0;
+ s->intr |= TUSB_INT_SRC_OTG_TIMEOUT;
+ tusb_intr_update(s);
+}
+
+static void tusb_power_tick(void *opaque)
+{
+ TUSBState *s = (TUSBState *) opaque;
+
+ if (s->power) {
+ s->intr_ok = ~0;
+ tusb_intr_update(s);
+ }
+}
+
+static void tusb_musb_core_intr(void *opaque, int source, int level)
+{
+ TUSBState *s = (TUSBState *) opaque;
+ uint16_t otg_status = s->otg_status;
+
+ switch (source) {
+ case musb_set_vbus:
+ if (level)
+ otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID;
+ else
+ otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID;
+
+ /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */
+ /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */
+ if (s->otg_status != otg_status) {
+ s->otg_status = otg_status;
+ s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG;
+ tusb_intr_update(s);
+ }
+ break;
+
+ case musb_set_session:
+ /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */
+ /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */
+ if (level) {
+ s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID;
+ s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END;
+ } else {
+ s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID;
+ s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END;
+ }
+
+ /* XXX: some IRQ or anything? */
+ break;
+
+ case musb_irq_tx:
+ case musb_irq_rx:
+ s->usbip_intr = musb_core_intr_get(s->musb);
+ /* Fall through. */
+ default:
+ if (level)
+ s->intr |= 1 << source;
+ else
+ s->intr &= ~(1 << source);
+ tusb_intr_update(s);
+ break;
+ }
+}
+
+TUSBState *tusb6010_init(qemu_irq intr)
+{
+ TUSBState *s = qemu_mallocz(sizeof(*s));
+
+ s->test_reset = TUSB_PROD_TEST_RESET_VAL;
+ s->host_mode = 0;
+ s->dev_config = 0;
+ s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */
+ s->power = 0;
+ s->mask = 0xffffffff;
+ s->intr = 0x00000000;
+ s->otg_timer_val = 0;
+ s->iomemtype[1] = cpu_register_io_memory(tusb_async_readfn,
+ tusb_async_writefn, s, DEVICE_NATIVE_ENDIAN);
+ s->irq = intr;
+ s->otg_timer = qemu_new_timer(vm_clock, tusb_otg_tick, s);
+ s->pwr_timer = qemu_new_timer(vm_clock, tusb_power_tick, s);
+ s->musb = musb_init(qemu_allocate_irqs(tusb_musb_core_intr, s,
+ __musb_irq_max));
+
+ return s;
+}
+
+void tusb6010_power(TUSBState *s, int on)
+{
+ if (!on)
+ s->power = 0;
+ else if (!s->power && on) {
+ s->power = 1;
+
+ /* Pull the interrupt down after TUSB6010 comes up. */
+ s->intr_ok = 0;
+ tusb_intr_update(s);
+ qemu_mod_timer(s->pwr_timer,
+ qemu_get_clock(vm_clock) + get_ticks_per_sec() / 2);
+ }
+}
diff --git a/hw/twl92230.c b/hw/twl92230.c
new file mode 100644
index 0000000..e61f17f
--- /dev/null
+++ b/hw/twl92230.c
@@ -0,0 +1,876 @@
+/*
+ * TI TWL92230C energy-management companion device for the OMAP24xx.
+ * Aka. Menelaus (N4200 MENELAUS1_V2.2)
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "i2c.h"
+#include "sysemu.h"
+#include "console.h"
+
+#define VERBOSE 1
+
+typedef struct {
+ i2c_slave i2c;
+
+ int firstbyte;
+ uint8_t reg;
+
+ uint8_t vcore[5];
+ uint8_t dcdc[3];
+ uint8_t ldo[8];
+ uint8_t sleep[2];
+ uint8_t osc;
+ uint8_t detect;
+ uint16_t mask;
+ uint16_t status;
+ uint8_t dir;
+ uint8_t inputs;
+ uint8_t outputs;
+ uint8_t bbsms;
+ uint8_t pull[4];
+ uint8_t mmc_ctrl[3];
+ uint8_t mmc_debounce;
+ struct {
+ uint8_t ctrl;
+ uint16_t comp;
+ QEMUTimer *hz_tm;
+ int64_t next;
+ struct tm tm;
+ struct tm new;
+ struct tm alm;
+ int sec_offset;
+ int alm_sec;
+ int next_comp;
+ } rtc;
+ uint16_t rtc_next_vmstate;
+ qemu_irq out[4];
+ qemu_irq *in;
+ uint8_t pwrbtn_state;
+ qemu_irq pwrbtn;
+} MenelausState;
+
+static inline void menelaus_update(MenelausState *s)
+{
+ qemu_set_irq(s->out[3], s->status & ~s->mask);
+}
+
+static inline void menelaus_rtc_start(MenelausState *s)
+{
+ s->rtc.next += qemu_get_clock(rt_clock);
+ qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
+}
+
+static inline void menelaus_rtc_stop(MenelausState *s)
+{
+ qemu_del_timer(s->rtc.hz_tm);
+ s->rtc.next -= qemu_get_clock(rt_clock);
+ if (s->rtc.next < 1)
+ s->rtc.next = 1;
+}
+
+static void menelaus_rtc_update(MenelausState *s)
+{
+ qemu_get_timedate(&s->rtc.tm, s->rtc.sec_offset);
+}
+
+static void menelaus_alm_update(MenelausState *s)
+{
+ if ((s->rtc.ctrl & 3) == 3)
+ s->rtc.alm_sec = qemu_timedate_diff(&s->rtc.alm) - s->rtc.sec_offset;
+}
+
+static void menelaus_rtc_hz(void *opaque)
+{
+ MenelausState *s = (MenelausState *) opaque;
+
+ s->rtc.next_comp --;
+ s->rtc.alm_sec --;
+ s->rtc.next += 1000;
+ qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
+ if ((s->rtc.ctrl >> 3) & 3) { /* EVERY */
+ menelaus_rtc_update(s);
+ if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
+ s->status |= 1 << 8; /* RTCTMR */
+ else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
+ s->status |= 1 << 8; /* RTCTMR */
+ else if (!s->rtc.tm.tm_hour)
+ s->status |= 1 << 8; /* RTCTMR */
+ } else
+ s->status |= 1 << 8; /* RTCTMR */
+ if ((s->rtc.ctrl >> 1) & 1) { /* RTC_AL_EN */
+ if (s->rtc.alm_sec == 0)
+ s->status |= 1 << 9; /* RTCALM */
+ /* TODO: wake-up */
+ }
+ if (s->rtc.next_comp <= 0) {
+ s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
+ s->rtc.next_comp = 3600;
+ }
+ menelaus_update(s);
+}
+
+static void menelaus_reset(i2c_slave *i2c)
+{
+ MenelausState *s = (MenelausState *) i2c;
+ s->reg = 0x00;
+
+ s->vcore[0] = 0x0c; /* XXX: X-loader needs 0x8c? check! */
+ s->vcore[1] = 0x05;
+ s->vcore[2] = 0x02;
+ s->vcore[3] = 0x0c;
+ s->vcore[4] = 0x03;
+ s->dcdc[0] = 0x33; /* Depends on wiring */
+ s->dcdc[1] = 0x03;
+ s->dcdc[2] = 0x00;
+ s->ldo[0] = 0x95;
+ s->ldo[1] = 0x7e;
+ s->ldo[2] = 0x00;
+ s->ldo[3] = 0x00; /* Depends on wiring */
+ s->ldo[4] = 0x03; /* Depends on wiring */
+ s->ldo[5] = 0x00;
+ s->ldo[6] = 0x00;
+ s->ldo[7] = 0x00;
+ s->sleep[0] = 0x00;
+ s->sleep[1] = 0x00;
+ s->osc = 0x01;
+ s->detect = 0x09;
+ s->mask = 0x0fff;
+ s->status = 0;
+ s->dir = 0x07;
+ s->outputs = 0x00;
+ s->bbsms = 0x00;
+ s->pull[0] = 0x00;
+ s->pull[1] = 0x00;
+ s->pull[2] = 0x00;
+ s->pull[3] = 0x00;
+ s->mmc_ctrl[0] = 0x03;
+ s->mmc_ctrl[1] = 0xc0;
+ s->mmc_ctrl[2] = 0x00;
+ s->mmc_debounce = 0x05;
+
+ if (s->rtc.ctrl & 1)
+ menelaus_rtc_stop(s);
+ s->rtc.ctrl = 0x00;
+ s->rtc.comp = 0x0000;
+ s->rtc.next = 1000;
+ s->rtc.sec_offset = 0;
+ s->rtc.next_comp = 1800;
+ s->rtc.alm_sec = 1800;
+ s->rtc.alm.tm_sec = 0x00;
+ s->rtc.alm.tm_min = 0x00;
+ s->rtc.alm.tm_hour = 0x00;
+ s->rtc.alm.tm_mday = 0x01;
+ s->rtc.alm.tm_mon = 0x00;
+ s->rtc.alm.tm_year = 2004;
+ menelaus_update(s);
+}
+
+static void menelaus_gpio_set(void *opaque, int line, int level)
+{
+ MenelausState *s = (MenelausState *) opaque;
+
+ /* No interrupt generated */
+ s->inputs &= ~(1 << line);
+ s->inputs |= level << line;
+}
+
+static void menelaus_pwrbtn_set(void *opaque, int line, int level)
+{
+ MenelausState *s = (MenelausState *) opaque;
+
+ if (!s->pwrbtn_state && level) {
+ s->status |= 1 << 11; /* PSHBTN */
+ menelaus_update(s);
+ }
+ s->pwrbtn_state = level;
+}
+
+#define MENELAUS_REV 0x01
+#define MENELAUS_VCORE_CTRL1 0x02
+#define MENELAUS_VCORE_CTRL2 0x03
+#define MENELAUS_VCORE_CTRL3 0x04
+#define MENELAUS_VCORE_CTRL4 0x05
+#define MENELAUS_VCORE_CTRL5 0x06
+#define MENELAUS_DCDC_CTRL1 0x07
+#define MENELAUS_DCDC_CTRL2 0x08
+#define MENELAUS_DCDC_CTRL3 0x09
+#define MENELAUS_LDO_CTRL1 0x0a
+#define MENELAUS_LDO_CTRL2 0x0b
+#define MENELAUS_LDO_CTRL3 0x0c
+#define MENELAUS_LDO_CTRL4 0x0d
+#define MENELAUS_LDO_CTRL5 0x0e
+#define MENELAUS_LDO_CTRL6 0x0f
+#define MENELAUS_LDO_CTRL7 0x10
+#define MENELAUS_LDO_CTRL8 0x11
+#define MENELAUS_SLEEP_CTRL1 0x12
+#define MENELAUS_SLEEP_CTRL2 0x13
+#define MENELAUS_DEVICE_OFF 0x14
+#define MENELAUS_OSC_CTRL 0x15
+#define MENELAUS_DETECT_CTRL 0x16
+#define MENELAUS_INT_MASK1 0x17
+#define MENELAUS_INT_MASK2 0x18
+#define MENELAUS_INT_STATUS1 0x19
+#define MENELAUS_INT_STATUS2 0x1a
+#define MENELAUS_INT_ACK1 0x1b
+#define MENELAUS_INT_ACK2 0x1c
+#define MENELAUS_GPIO_CTRL 0x1d
+#define MENELAUS_GPIO_IN 0x1e
+#define MENELAUS_GPIO_OUT 0x1f
+#define MENELAUS_BBSMS 0x20
+#define MENELAUS_RTC_CTRL 0x21
+#define MENELAUS_RTC_UPDATE 0x22
+#define MENELAUS_RTC_SEC 0x23
+#define MENELAUS_RTC_MIN 0x24
+#define MENELAUS_RTC_HR 0x25
+#define MENELAUS_RTC_DAY 0x26
+#define MENELAUS_RTC_MON 0x27
+#define MENELAUS_RTC_YR 0x28
+#define MENELAUS_RTC_WKDAY 0x29
+#define MENELAUS_RTC_AL_SEC 0x2a
+#define MENELAUS_RTC_AL_MIN 0x2b
+#define MENELAUS_RTC_AL_HR 0x2c
+#define MENELAUS_RTC_AL_DAY 0x2d
+#define MENELAUS_RTC_AL_MON 0x2e
+#define MENELAUS_RTC_AL_YR 0x2f
+#define MENELAUS_RTC_COMP_MSB 0x30
+#define MENELAUS_RTC_COMP_LSB 0x31
+#define MENELAUS_S1_PULL_EN 0x32
+#define MENELAUS_S1_PULL_DIR 0x33
+#define MENELAUS_S2_PULL_EN 0x34
+#define MENELAUS_S2_PULL_DIR 0x35
+#define MENELAUS_MCT_CTRL1 0x36
+#define MENELAUS_MCT_CTRL2 0x37
+#define MENELAUS_MCT_CTRL3 0x38
+#define MENELAUS_MCT_PIN_ST 0x39
+#define MENELAUS_DEBOUNCE1 0x3a
+
+static uint8_t menelaus_read(void *opaque, uint8_t addr)
+{
+ MenelausState *s = (MenelausState *) opaque;
+ int reg = 0;
+
+ switch (addr) {
+ case MENELAUS_REV:
+ return 0x22;
+
+ case MENELAUS_VCORE_CTRL5: reg ++;
+ case MENELAUS_VCORE_CTRL4: reg ++;
+ case MENELAUS_VCORE_CTRL3: reg ++;
+ case MENELAUS_VCORE_CTRL2: reg ++;
+ case MENELAUS_VCORE_CTRL1:
+ return s->vcore[reg];
+
+ case MENELAUS_DCDC_CTRL3: reg ++;
+ case MENELAUS_DCDC_CTRL2: reg ++;
+ case MENELAUS_DCDC_CTRL1:
+ return s->dcdc[reg];
+
+ case MENELAUS_LDO_CTRL8: reg ++;
+ case MENELAUS_LDO_CTRL7: reg ++;
+ case MENELAUS_LDO_CTRL6: reg ++;
+ case MENELAUS_LDO_CTRL5: reg ++;
+ case MENELAUS_LDO_CTRL4: reg ++;
+ case MENELAUS_LDO_CTRL3: reg ++;
+ case MENELAUS_LDO_CTRL2: reg ++;
+ case MENELAUS_LDO_CTRL1:
+ return s->ldo[reg];
+
+ case MENELAUS_SLEEP_CTRL2: reg ++;
+ case MENELAUS_SLEEP_CTRL1:
+ return s->sleep[reg];
+
+ case MENELAUS_DEVICE_OFF:
+ return 0;
+
+ case MENELAUS_OSC_CTRL:
+ return s->osc | (1 << 7); /* CLK32K_GOOD */
+
+ case MENELAUS_DETECT_CTRL:
+ return s->detect;
+
+ case MENELAUS_INT_MASK1:
+ return (s->mask >> 0) & 0xff;
+ case MENELAUS_INT_MASK2:
+ return (s->mask >> 8) & 0xff;
+
+ case MENELAUS_INT_STATUS1:
+ return (s->status >> 0) & 0xff;
+ case MENELAUS_INT_STATUS2:
+ return (s->status >> 8) & 0xff;
+
+ case MENELAUS_INT_ACK1:
+ case MENELAUS_INT_ACK2:
+ return 0;
+
+ case MENELAUS_GPIO_CTRL:
+ return s->dir;
+ case MENELAUS_GPIO_IN:
+ return s->inputs | (~s->dir & s->outputs);
+ case MENELAUS_GPIO_OUT:
+ return s->outputs;
+
+ case MENELAUS_BBSMS:
+ return s->bbsms;
+
+ case MENELAUS_RTC_CTRL:
+ return s->rtc.ctrl;
+ case MENELAUS_RTC_UPDATE:
+ return 0x00;
+ case MENELAUS_RTC_SEC:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_sec);
+ case MENELAUS_RTC_MIN:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_min);
+ case MENELAUS_RTC_HR:
+ menelaus_rtc_update(s);
+ if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
+ return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
+ (!!(s->rtc.tm.tm_hour >= 12) << 7); /* PM_nAM */
+ else
+ return to_bcd(s->rtc.tm.tm_hour);
+ case MENELAUS_RTC_DAY:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_mday);
+ case MENELAUS_RTC_MON:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_mon + 1);
+ case MENELAUS_RTC_YR:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_year - 2000);
+ case MENELAUS_RTC_WKDAY:
+ menelaus_rtc_update(s);
+ return to_bcd(s->rtc.tm.tm_wday);
+ case MENELAUS_RTC_AL_SEC:
+ return to_bcd(s->rtc.alm.tm_sec);
+ case MENELAUS_RTC_AL_MIN:
+ return to_bcd(s->rtc.alm.tm_min);
+ case MENELAUS_RTC_AL_HR:
+ if ((s->rtc.ctrl >> 2) & 1) /* MODE12_n24 */
+ return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
+ (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
+ else
+ return to_bcd(s->rtc.alm.tm_hour);
+ case MENELAUS_RTC_AL_DAY:
+ return to_bcd(s->rtc.alm.tm_mday);
+ case MENELAUS_RTC_AL_MON:
+ return to_bcd(s->rtc.alm.tm_mon + 1);
+ case MENELAUS_RTC_AL_YR:
+ return to_bcd(s->rtc.alm.tm_year - 2000);
+ case MENELAUS_RTC_COMP_MSB:
+ return (s->rtc.comp >> 8) & 0xff;
+ case MENELAUS_RTC_COMP_LSB:
+ return (s->rtc.comp >> 0) & 0xff;
+
+ case MENELAUS_S1_PULL_EN:
+ return s->pull[0];
+ case MENELAUS_S1_PULL_DIR:
+ return s->pull[1];
+ case MENELAUS_S2_PULL_EN:
+ return s->pull[2];
+ case MENELAUS_S2_PULL_DIR:
+ return s->pull[3];
+
+ case MENELAUS_MCT_CTRL3: reg ++;
+ case MENELAUS_MCT_CTRL2: reg ++;
+ case MENELAUS_MCT_CTRL1:
+ return s->mmc_ctrl[reg];
+ case MENELAUS_MCT_PIN_ST:
+ /* TODO: return the real Card Detect */
+ return 0;
+ case MENELAUS_DEBOUNCE1:
+ return s->mmc_debounce;
+
+ default:
+#ifdef VERBOSE
+ printf("%s: unknown register %02x\n", __FUNCTION__, addr);
+#endif
+ break;
+ }
+ return 0;
+}
+
+static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
+{
+ MenelausState *s = (MenelausState *) opaque;
+ int line;
+ int reg = 0;
+ struct tm tm;
+
+ switch (addr) {
+ case MENELAUS_VCORE_CTRL1:
+ s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
+ break;
+ case MENELAUS_VCORE_CTRL2:
+ s->vcore[1] = value;
+ break;
+ case MENELAUS_VCORE_CTRL3:
+ s->vcore[2] = MIN(value & 0x1f, 0x12);
+ break;
+ case MENELAUS_VCORE_CTRL4:
+ s->vcore[3] = MIN(value & 0x1f, 0x12);
+ break;
+ case MENELAUS_VCORE_CTRL5:
+ s->vcore[4] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+
+ case MENELAUS_DCDC_CTRL1:
+ s->dcdc[0] = value & 0x3f;
+ break;
+ case MENELAUS_DCDC_CTRL2:
+ s->dcdc[1] = value & 0x07;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_DCDC_CTRL3:
+ s->dcdc[2] = value & 0x07;
+ break;
+
+ case MENELAUS_LDO_CTRL1:
+ s->ldo[0] = value;
+ break;
+ case MENELAUS_LDO_CTRL2:
+ s->ldo[1] = value & 0x7f;
+ /* XXX
+ * auto set to 0x7e on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL3:
+ s->ldo[2] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL4:
+ s->ldo[3] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL5:
+ s->ldo[4] = value & 3;
+ /* XXX
+ * auto set to 3 on M_Active, nRESWARM
+ * auto set to 0 on M_WaitOn, M_Backup
+ */
+ break;
+ case MENELAUS_LDO_CTRL6:
+ s->ldo[5] = value & 3;
+ break;
+ case MENELAUS_LDO_CTRL7:
+ s->ldo[6] = value & 3;
+ break;
+ case MENELAUS_LDO_CTRL8:
+ s->ldo[7] = value & 3;
+ break;
+
+ case MENELAUS_SLEEP_CTRL2: reg ++;
+ case MENELAUS_SLEEP_CTRL1:
+ s->sleep[reg] = value;
+ break;
+
+ case MENELAUS_DEVICE_OFF:
+ if (value & 1)
+ menelaus_reset(&s->i2c);
+ break;
+
+ case MENELAUS_OSC_CTRL:
+ s->osc = value & 7;
+ break;
+
+ case MENELAUS_DETECT_CTRL:
+ s->detect = value & 0x7f;
+ break;
+
+ case MENELAUS_INT_MASK1:
+ s->mask &= 0xf00;
+ s->mask |= value << 0;
+ menelaus_update(s);
+ break;
+ case MENELAUS_INT_MASK2:
+ s->mask &= 0x0ff;
+ s->mask |= value << 8;
+ menelaus_update(s);
+ break;
+
+ case MENELAUS_INT_ACK1:
+ s->status &= ~(((uint16_t) value) << 0);
+ menelaus_update(s);
+ break;
+ case MENELAUS_INT_ACK2:
+ s->status &= ~(((uint16_t) value) << 8);
+ menelaus_update(s);
+ break;
+
+ case MENELAUS_GPIO_CTRL:
+ for (line = 0; line < 3; line ++) {
+ if (((s->dir ^ value) >> line) & 1) {
+ qemu_set_irq(s->out[line],
+ ((s->outputs & ~s->dir) >> line) & 1);
+ }
+ }
+ s->dir = value & 0x67;
+ break;
+ case MENELAUS_GPIO_OUT:
+ for (line = 0; line < 3; line ++) {
+ if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) {
+ qemu_set_irq(s->out[line], (s->outputs >> line) & 1);
+ }
+ }
+ s->outputs = value & 0x07;
+ break;
+
+ case MENELAUS_BBSMS:
+ s->bbsms = 0x0d;
+ break;
+
+ case MENELAUS_RTC_CTRL:
+ if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */
+ if (value & 1)
+ menelaus_rtc_start(s);
+ else
+ menelaus_rtc_stop(s);
+ }
+ s->rtc.ctrl = value & 0x1f;
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_UPDATE:
+ menelaus_rtc_update(s);
+ memcpy(&tm, &s->rtc.tm, sizeof(tm));
+ switch (value & 0xf) {
+ case 0:
+ break;
+ case 1:
+ tm.tm_sec = s->rtc.new.tm_sec;
+ break;
+ case 2:
+ tm.tm_min = s->rtc.new.tm_min;
+ break;
+ case 3:
+ if (s->rtc.new.tm_hour > 23)
+ goto rtc_badness;
+ tm.tm_hour = s->rtc.new.tm_hour;
+ break;
+ case 4:
+ if (s->rtc.new.tm_mday < 1)
+ goto rtc_badness;
+ /* TODO check range */
+ tm.tm_mday = s->rtc.new.tm_mday;
+ break;
+ case 5:
+ if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
+ goto rtc_badness;
+ tm.tm_mon = s->rtc.new.tm_mon;
+ break;
+ case 6:
+ tm.tm_year = s->rtc.new.tm_year;
+ break;
+ case 7:
+ /* TODO set .tm_mday instead */
+ tm.tm_wday = s->rtc.new.tm_wday;
+ break;
+ case 8:
+ if (s->rtc.new.tm_hour > 23)
+ goto rtc_badness;
+ if (s->rtc.new.tm_mday < 1)
+ goto rtc_badness;
+ if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
+ goto rtc_badness;
+ tm.tm_sec = s->rtc.new.tm_sec;
+ tm.tm_min = s->rtc.new.tm_min;
+ tm.tm_hour = s->rtc.new.tm_hour;
+ tm.tm_mday = s->rtc.new.tm_mday;
+ tm.tm_mon = s->rtc.new.tm_mon;
+ tm.tm_year = s->rtc.new.tm_year;
+ break;
+ rtc_badness:
+ default:
+ fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
+ __FUNCTION__, value);
+ s->status |= 1 << 10; /* RTCERR */
+ menelaus_update(s);
+ }
+ s->rtc.sec_offset = qemu_timedate_diff(&tm);
+ break;
+ case MENELAUS_RTC_SEC:
+ s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
+ break;
+ case MENELAUS_RTC_MIN:
+ s->rtc.tm.tm_min = from_bcd(value & 0x7f);
+ break;
+ case MENELAUS_RTC_HR:
+ s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
+ MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
+ from_bcd(value & 0x3f);
+ break;
+ case MENELAUS_RTC_DAY:
+ s->rtc.tm.tm_mday = from_bcd(value);
+ break;
+ case MENELAUS_RTC_MON:
+ s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
+ break;
+ case MENELAUS_RTC_YR:
+ s->rtc.tm.tm_year = 2000 + from_bcd(value);
+ break;
+ case MENELAUS_RTC_WKDAY:
+ s->rtc.tm.tm_mday = from_bcd(value);
+ break;
+ case MENELAUS_RTC_AL_SEC:
+ s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_MIN:
+ s->rtc.alm.tm_min = from_bcd(value & 0x7f);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_HR:
+ s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
+ MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
+ from_bcd(value & 0x3f);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_DAY:
+ s->rtc.alm.tm_mday = from_bcd(value);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_MON:
+ s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_AL_YR:
+ s->rtc.alm.tm_year = 2000 + from_bcd(value);
+ menelaus_alm_update(s);
+ break;
+ case MENELAUS_RTC_COMP_MSB:
+ s->rtc.comp &= 0xff;
+ s->rtc.comp |= value << 8;
+ break;
+ case MENELAUS_RTC_COMP_LSB:
+ s->rtc.comp &= 0xff << 8;
+ s->rtc.comp |= value;
+ break;
+
+ case MENELAUS_S1_PULL_EN:
+ s->pull[0] = value;
+ break;
+ case MENELAUS_S1_PULL_DIR:
+ s->pull[1] = value & 0x1f;
+ break;
+ case MENELAUS_S2_PULL_EN:
+ s->pull[2] = value;
+ break;
+ case MENELAUS_S2_PULL_DIR:
+ s->pull[3] = value & 0x1f;
+ break;
+
+ case MENELAUS_MCT_CTRL1:
+ s->mmc_ctrl[0] = value & 0x7f;
+ break;
+ case MENELAUS_MCT_CTRL2:
+ s->mmc_ctrl[1] = value;
+ /* TODO update Card Detect interrupts */
+ break;
+ case MENELAUS_MCT_CTRL3:
+ s->mmc_ctrl[2] = value & 0xf;
+ break;
+ case MENELAUS_DEBOUNCE1:
+ s->mmc_debounce = value & 0x3f;
+ break;
+
+ default:
+#ifdef VERBOSE
+ printf("%s: unknown register %02x\n", __FUNCTION__, addr);
+#endif
+ }
+}
+
+static void menelaus_event(i2c_slave *i2c, enum i2c_event event)
+{
+ MenelausState *s = (MenelausState *) i2c;
+
+ if (event == I2C_START_SEND)
+ s->firstbyte = 1;
+}
+
+static int menelaus_tx(i2c_slave *i2c, uint8_t data)
+{
+ MenelausState *s = (MenelausState *) i2c;
+ /* Interpret register address byte */
+ if (s->firstbyte) {
+ s->reg = data;
+ s->firstbyte = 0;
+ } else
+ menelaus_write(s, s->reg ++, data);
+
+ return 0;
+}
+
+static int menelaus_rx(i2c_slave *i2c)
+{
+ MenelausState *s = (MenelausState *) i2c;
+
+ return menelaus_read(s, s->reg ++);
+}
+
+/* Save restore 32 bit int as uint16_t
+ This is a Big hack, but it is how the old state did it.
+ Or we broke compatibility in the state, or we can't use struct tm
+ */
+
+static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ int *v = pv;
+ *v = qemu_get_be16(f);
+ return 0;
+}
+
+static void put_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ int *v = pv;
+ qemu_put_be16(f, *v);
+}
+
+static const VMStateInfo vmstate_hack_int32_as_uint16 = {
+ .name = "int32_as_uint16",
+ .get = get_int32_as_uint16,
+ .put = put_int32_as_uint16,
+};
+
+#define VMSTATE_UINT16_HACK(_f, _s) \
+ VMSTATE_SINGLE(_f, _s, 0, vmstate_hack_int32_as_uint16, int32_t)
+
+
+static const VMStateDescription vmstate_menelaus_tm = {
+ .name = "menelaus_tm",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16_HACK(tm_sec, struct tm),
+ VMSTATE_UINT16_HACK(tm_min, struct tm),
+ VMSTATE_UINT16_HACK(tm_hour, struct tm),
+ VMSTATE_UINT16_HACK(tm_mday, struct tm),
+ VMSTATE_UINT16_HACK(tm_min, struct tm),
+ VMSTATE_UINT16_HACK(tm_year, struct tm),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void menelaus_pre_save(void *opaque)
+{
+ MenelausState *s = opaque;
+ /* Should be <= 1000 */
+ s->rtc_next_vmstate = s->rtc.next - qemu_get_clock(rt_clock);
+}
+
+static int menelaus_post_load(void *opaque, int version_id)
+{
+ MenelausState *s = opaque;
+
+ if (s->rtc.ctrl & 1) /* RTC_EN */
+ menelaus_rtc_stop(s);
+
+ s->rtc.next = s->rtc_next_vmstate;
+
+ menelaus_alm_update(s);
+ menelaus_update(s);
+ if (s->rtc.ctrl & 1) /* RTC_EN */
+ menelaus_rtc_start(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_menelaus = {
+ .name = "menelaus",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = menelaus_pre_save,
+ .post_load = menelaus_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(firstbyte, MenelausState),
+ VMSTATE_UINT8(reg, MenelausState),
+ VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5),
+ VMSTATE_UINT8_ARRAY(dcdc, MenelausState, 3),
+ VMSTATE_UINT8_ARRAY(ldo, MenelausState, 8),
+ VMSTATE_UINT8_ARRAY(sleep, MenelausState, 2),
+ VMSTATE_UINT8(osc, MenelausState),
+ VMSTATE_UINT8(detect, MenelausState),
+ VMSTATE_UINT16(mask, MenelausState),
+ VMSTATE_UINT16(status, MenelausState),
+ VMSTATE_UINT8(dir, MenelausState),
+ VMSTATE_UINT8(inputs, MenelausState),
+ VMSTATE_UINT8(outputs, MenelausState),
+ VMSTATE_UINT8(bbsms, MenelausState),
+ VMSTATE_UINT8_ARRAY(pull, MenelausState, 4),
+ VMSTATE_UINT8_ARRAY(mmc_ctrl, MenelausState, 3),
+ VMSTATE_UINT8(mmc_debounce, MenelausState),
+ VMSTATE_UINT8(rtc.ctrl, MenelausState),
+ VMSTATE_UINT16(rtc.comp, MenelausState),
+ VMSTATE_UINT16(rtc_next_vmstate, MenelausState),
+ VMSTATE_STRUCT(rtc.new, MenelausState, 0, vmstate_menelaus_tm,
+ struct tm),
+ VMSTATE_STRUCT(rtc.alm, MenelausState, 0, vmstate_menelaus_tm,
+ struct tm),
+ VMSTATE_UINT8(pwrbtn_state, MenelausState),
+ VMSTATE_I2C_SLAVE(i2c, MenelausState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int twl92230_init(i2c_slave *i2c)
+{
+ MenelausState *s = FROM_I2C_SLAVE(MenelausState, i2c);
+
+ s->rtc.hz_tm = qemu_new_timer(rt_clock, menelaus_rtc_hz, s);
+ /* Three output pins plus one interrupt pin. */
+ qdev_init_gpio_out(&i2c->qdev, s->out, 4);
+ qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 3);
+ s->pwrbtn = qemu_allocate_irqs(menelaus_pwrbtn_set, s, 1)[0];
+
+ menelaus_reset(&s->i2c);
+
+ return 0;
+}
+
+static I2CSlaveInfo twl92230_info = {
+ .qdev.name ="twl92230",
+ .qdev.size = sizeof(MenelausState),
+ .qdev.vmsd = &vmstate_menelaus,
+ .init = twl92230_init,
+ .event = menelaus_event,
+ .recv = menelaus_rx,
+ .send = menelaus_tx
+};
+
+static void twl92230_register_devices(void)
+{
+ i2c_register_slave(&twl92230_info);
+}
+
+device_init(twl92230_register_devices)
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
new file mode 100644
index 0000000..5f15058
--- /dev/null
+++ b/hw/unin_pci.c
@@ -0,0 +1,391 @@
+/*
+ * QEMU Uninorth PCI host (for all Mac99 and newer machines)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc_mac.h"
+#include "pci.h"
+#include "pci_host.h"
+
+/* debug UniNorth */
+//#define DEBUG_UNIN
+
+#ifdef DEBUG_UNIN
+#define UNIN_DPRINTF(fmt, ...) \
+ do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define UNIN_DPRINTF(fmt, ...)
+#endif
+
+static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e };
+
+typedef struct UNINState {
+ SysBusDevice busdev;
+ PCIHostState host_state;
+ ReadWriteHandler data_handler;
+} UNINState;
+
+static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int retval;
+ int devfn = pci_dev->devfn & 0x00FFFFFF;
+
+ retval = (((devfn >> 11) & 0x1F) + irq_num) & 3;
+
+ return retval;
+}
+
+static void pci_unin_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+
+ UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__,
+ unin_irq_line[irq_num], level);
+ qemu_set_irq(pic[unin_irq_line[irq_num]], level);
+}
+
+static void pci_unin_save(QEMUFile* f, void *opaque)
+{
+ PCIDevice *d = opaque;
+
+ pci_device_save(d, f);
+}
+
+static int pci_unin_load(QEMUFile* f, void *opaque, int version_id)
+{
+ PCIDevice *d = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ return pci_device_load(d, f);
+}
+
+static void pci_unin_reset(void *opaque)
+{
+}
+
+static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
+{
+ uint32_t retval;
+
+ if (reg & (1u << 31)) {
+ /* XXX OpenBIOS compatibility hack */
+ retval = reg | (addr & 3);
+ } else if (reg & 1) {
+ /* CFA1 style */
+ retval = (reg & ~7u) | (addr & 7);
+ } else {
+ uint32_t slot, func;
+
+ /* Grab CFA0 style values */
+ slot = ffs(reg & 0xfffff800) - 1;
+ func = (reg >> 8) & 7;
+
+ /* ... and then convert them to x86 format */
+ /* config pointer */
+ retval = (reg & (0xff - 7)) | (addr & 7);
+ /* slot */
+ retval |= slot << 11;
+ /* fn */
+ retval |= func << 8;
+ }
+
+
+ UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n",
+ reg, addr, retval);
+
+ return retval;
+}
+
+static void unin_data_write(ReadWriteHandler *handler,
+ pcibus_t addr, uint32_t val, int len)
+{
+ UNINState *s = container_of(handler, UNINState, data_handler);
+ UNIN_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
+ pci_data_write(s->host_state.bus,
+ unin_get_config_reg(s->host_state.config_reg, addr),
+ val, len);
+}
+
+static uint32_t unin_data_read(ReadWriteHandler *handler,
+ pcibus_t addr, int len)
+{
+ UNINState *s = container_of(handler, UNINState, data_handler);
+ uint32_t val;
+
+ val = pci_data_read(s->host_state.bus,
+ unin_get_config_reg(s->host_state.config_reg, addr),
+ len);
+ UNIN_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
+ return val;
+}
+
+static int pci_unin_main_init_device(SysBusDevice *dev)
+{
+ UNINState *s;
+ int pci_mem_config, pci_mem_data;
+
+ /* Use values found on a real PowerMac */
+ /* Uninorth main bus */
+ s = FROM_SYSBUS(UNINState, dev);
+
+ pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ s->data_handler.read = unin_data_read;
+ s->data_handler.write = unin_data_write;
+ pci_mem_data = cpu_register_io_memory_simple(&s->data_handler,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+
+ register_savevm(&dev->qdev, "uninorth", 0, 1,
+ pci_unin_save, pci_unin_load, &s->host_state);
+ qemu_register_reset(pci_unin_reset, &s->host_state);
+ return 0;
+}
+
+static int pci_u3_agp_init_device(SysBusDevice *dev)
+{
+ UNINState *s;
+ int pci_mem_config, pci_mem_data;
+
+ /* Uninorth U3 AGP bus */
+ s = FROM_SYSBUS(UNINState, dev);
+
+ pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ s->data_handler.read = unin_data_read;
+ s->data_handler.write = unin_data_write;
+ pci_mem_data = cpu_register_io_memory_simple(&s->data_handler,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+
+ register_savevm(&dev->qdev, "uninorth", 0, 1,
+ pci_unin_save, pci_unin_load, &s->host_state);
+ qemu_register_reset(pci_unin_reset, &s->host_state);
+
+ return 0;
+}
+
+static int pci_unin_agp_init_device(SysBusDevice *dev)
+{
+ UNINState *s;
+ int pci_mem_config, pci_mem_data;
+
+ /* Uninorth AGP bus */
+ s = FROM_SYSBUS(UNINState, dev);
+
+ pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ pci_mem_data = pci_host_data_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+ return 0;
+}
+
+static int pci_unin_internal_init_device(SysBusDevice *dev)
+{
+ UNINState *s;
+ int pci_mem_config, pci_mem_data;
+
+ /* Uninorth internal bus */
+ s = FROM_SYSBUS(UNINState, dev);
+
+ pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ pci_mem_data = pci_host_data_register_mmio(&s->host_state,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_config);
+ sysbus_init_mmio(dev, 0x1000, pci_mem_data);
+ return 0;
+}
+
+PCIBus *pci_pmac_init(qemu_irq *pic)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ UNINState *d;
+
+ /* Use values found on a real PowerMac */
+ /* Uninorth main bus */
+ dev = qdev_create(NULL, "uni-north");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ d = FROM_SYSBUS(UNINState, s);
+ d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
+ pci_unin_set_irq, pci_unin_map_irq,
+ pic, PCI_DEVFN(11, 0), 4);
+
+#if 0
+ pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north");
+#endif
+
+ sysbus_mmio_map(s, 0, 0xf2800000);
+ sysbus_mmio_map(s, 1, 0xf2c00000);
+
+ /* DEC 21154 bridge */
+#if 0
+ /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */
+ pci_create_simple(d->host_state.bus, PCI_DEVFN(12, 0), "dec-21154");
+#endif
+
+ /* Uninorth AGP bus */
+ pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north-agp");
+ dev = qdev_create(NULL, "uni-north-agp");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, 0xf0800000);
+ sysbus_mmio_map(s, 1, 0xf0c00000);
+
+ /* Uninorth internal bus */
+#if 0
+ /* XXX: not needed for now */
+ pci_create_simple(d->host_state.bus, PCI_DEVFN(14, 0), "uni-north-pci");
+ dev = qdev_create(NULL, "uni-north-pci");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ sysbus_mmio_map(s, 0, 0xf4800000);
+ sysbus_mmio_map(s, 1, 0xf4c00000);
+#endif
+
+ return d->host_state.bus;
+}
+
+PCIBus *pci_pmac_u3_init(qemu_irq *pic)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+ UNINState *d;
+
+ /* Uninorth AGP bus */
+
+ dev = qdev_create(NULL, "u3-agp");
+ qdev_init_nofail(dev);
+ s = sysbus_from_qdev(dev);
+ d = FROM_SYSBUS(UNINState, s);
+
+ d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
+ pci_unin_set_irq, pci_unin_map_irq,
+ pic, PCI_DEVFN(11, 0), 4);
+
+ sysbus_mmio_map(s, 0, 0xf0800000);
+ sysbus_mmio_map(s, 1, 0xf0c00000);
+
+ pci_create_simple(d->host_state.bus, 11 << 3, "u3-agp");
+
+ return d->host_state.bus;
+}
+
+static int unin_main_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_PCI);
+ d->config[0x08] = 0x00; // revision
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x34] = 0x00; // capabilities_pointer
+ return 0;
+}
+
+static int unin_agp_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_AGP);
+ d->config[0x08] = 0x00; // revision
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ // d->config[0x34] = 0x80; // capabilities_pointer
+ return 0;
+}
+
+static int u3_agp_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_U3_AGP);
+ /* revision */
+ d->config[0x08] = 0x00;
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ /* cache line size */
+ d->config[0x0C] = 0x08;
+ /* latency timer */
+ d->config[0x0D] = 0x10;
+ return 0;
+}
+
+static int unin_internal_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_I_PCI);
+ d->config[0x08] = 0x00; // revision
+ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
+ d->config[0x0C] = 0x08; // cache_line_size
+ d->config[0x0D] = 0x10; // latency_timer
+ d->config[0x34] = 0x00; // capabilities_pointer
+ return 0;
+}
+
+static PCIDeviceInfo unin_main_pci_host_info = {
+ .qdev.name = "uni-north",
+ .qdev.size = sizeof(PCIDevice),
+ .init = unin_main_pci_host_init,
+};
+
+static PCIDeviceInfo u3_agp_pci_host_info = {
+ .qdev.name = "u3-agp",
+ .qdev.size = sizeof(PCIDevice),
+ .init = u3_agp_pci_host_init,
+};
+
+static PCIDeviceInfo unin_agp_pci_host_info = {
+ .qdev.name = "uni-north-agp",
+ .qdev.size = sizeof(PCIDevice),
+ .init = unin_agp_pci_host_init,
+};
+
+static PCIDeviceInfo unin_internal_pci_host_info = {
+ .qdev.name = "uni-north-pci",
+ .qdev.size = sizeof(PCIDevice),
+ .init = unin_internal_pci_host_init,
+};
+
+static void unin_register_devices(void)
+{
+ sysbus_register_dev("uni-north", sizeof(UNINState),
+ pci_unin_main_init_device);
+ pci_qdev_register(&unin_main_pci_host_info);
+ sysbus_register_dev("u3-agp", sizeof(UNINState),
+ pci_u3_agp_init_device);
+ pci_qdev_register(&u3_agp_pci_host_info);
+ sysbus_register_dev("uni-north-agp", sizeof(UNINState),
+ pci_unin_agp_init_device);
+ pci_qdev_register(&unin_agp_pci_host_info);
+ sysbus_register_dev("uni-north-pci", sizeof(UNINState),
+ pci_unin_internal_init_device);
+ pci_qdev_register(&unin_internal_pci_host_info);
+}
+
+device_init(unin_register_devices)
diff --git a/hw/usb-bt.c b/hw/usb-bt.c
new file mode 100644
index 0000000..22e6845
--- /dev/null
+++ b/hw/usb-bt.c
@@ -0,0 +1,568 @@
+/*
+ * QEMU Bluetooth HCI USB Transport Layer v1.0
+ *
+ * Copyright (C) 2007 OpenMoko, Inc.
+ * Copyright (C) 2008 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "usb-desc.h"
+#include "net.h"
+#include "bt.h"
+
+struct USBBtState {
+ USBDevice dev;
+ struct HCIInfo *hci;
+
+ int altsetting;
+ int config;
+
+#define CFIFO_LEN_MASK 255
+#define DFIFO_LEN_MASK 4095
+ struct usb_hci_in_fifo_s {
+ uint8_t data[(DFIFO_LEN_MASK + 1) * 2];
+ struct {
+ uint8_t *data;
+ int len;
+ } fifo[CFIFO_LEN_MASK + 1];
+ int dstart, dlen, dsize, start, len;
+ } evt, acl, sco;
+
+ struct usb_hci_out_fifo_s {
+ uint8_t data[4096];
+ int len;
+ } outcmd, outacl, outsco;
+};
+
+#define USB_EVT_EP 1
+#define USB_ACL_EP 2
+#define USB_SCO_EP 3
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+ [STR_SERIALNUMBER] = "1",
+};
+
+static const USBDescIface desc_iface_bluetooth[] = {
+ {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 3,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_EVT_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x10,
+ .bInterval = 0x02,
+ },
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_ACL_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 0x40,
+ .bInterval = 0x0a,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_ACL_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 0x40,
+ .bInterval = 0x0a,
+ },
+ },
+ },{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0,
+ .bInterval = 0x01,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0,
+ .bInterval = 0x01,
+ },
+ },
+ },{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x09,
+ .bInterval = 0x01,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x09,
+ .bInterval = 0x01,
+ },
+ },
+ },{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 2,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x11,
+ .bInterval = 0x01,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x11,
+ .bInterval = 0x01,
+ },
+ },
+ },{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 3,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x19,
+ .bInterval = 0x01,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x19,
+ .bInterval = 0x01,
+ },
+ },
+ },{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 4,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x21,
+ .bInterval = 0x01,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x21,
+ .bInterval = 0x01,
+ },
+ },
+ },{
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 5,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xe0, /* Wireless */
+ .bInterfaceSubClass = 0x01, /* Radio Frequency */
+ .bInterfaceProtocol = 0x01, /* Bluetooth */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x31,
+ .bInterval = 0x01,
+ },
+ {
+ .bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 0x31,
+ .bInterval = 0x01,
+ },
+ },
+ }
+};
+
+static const USBDescDevice desc_device_bluetooth = {
+ .bcdUSB = 0x0110,
+ .bDeviceClass = 0xe0, /* Wireless */
+ .bDeviceSubClass = 0x01, /* Radio Frequency */
+ .bDeviceProtocol = 0x01, /* Bluetooth */
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 2,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0xc0,
+ .bMaxPower = 0,
+ .nif = ARRAY_SIZE(desc_iface_bluetooth),
+ .ifs = desc_iface_bluetooth,
+ },
+ },
+};
+
+static const USBDesc desc_bluetooth = {
+ .id = {
+ .idVendor = 0x0a12,
+ .idProduct = 0x0001,
+ .bcdDevice = 0x1958,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = 0,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_bluetooth,
+ .str = desc_strings,
+};
+
+static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo)
+{
+ fifo->dstart = 0;
+ fifo->dlen = 0;
+ fifo->dsize = DFIFO_LEN_MASK + 1;
+ fifo->start = 0;
+ fifo->len = 0;
+}
+
+static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
+ const uint8_t *data, int len)
+{
+ int off = fifo->dstart + fifo->dlen;
+ uint8_t *buf;
+
+ fifo->dlen += len;
+ if (off <= DFIFO_LEN_MASK) {
+ if (off + len > DFIFO_LEN_MASK + 1 &&
+ (fifo->dsize = off + len) > (DFIFO_LEN_MASK + 1) * 2) {
+ fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+ exit(-1);
+ }
+ buf = fifo->data + off;
+ } else {
+ if (fifo->dlen > fifo->dsize) {
+ fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+ exit(-1);
+ }
+ buf = fifo->data + off - fifo->dsize;
+ }
+
+ off = (fifo->start + fifo->len ++) & CFIFO_LEN_MASK;
+ fifo->fifo[off].data = memcpy(buf, data, len);
+ fifo->fifo[off].len = len;
+}
+
+static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
+ USBPacket *p)
+{
+ int len;
+
+ if (likely(!fifo->len))
+ return USB_RET_STALL;
+
+ len = MIN(p->len, fifo->fifo[fifo->start].len);
+ memcpy(p->data, fifo->fifo[fifo->start].data, len);
+ if (len == p->len) {
+ fifo->fifo[fifo->start].len -= len;
+ fifo->fifo[fifo->start].data += len;
+ } else {
+ fifo->start ++;
+ fifo->start &= CFIFO_LEN_MASK;
+ fifo->len --;
+ }
+
+ fifo->dstart += len;
+ fifo->dlen -= len;
+ if (fifo->dstart >= fifo->dsize) {
+ fifo->dstart = 0;
+ fifo->dsize = DFIFO_LEN_MASK + 1;
+ }
+
+ return len;
+}
+
+static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
+ struct usb_hci_out_fifo_s *fifo,
+ void (*send)(struct HCIInfo *, const uint8_t *, int),
+ int (*complete)(const uint8_t *, int),
+ const uint8_t *data, int len)
+{
+ if (fifo->len) {
+ memcpy(fifo->data + fifo->len, data, len);
+ fifo->len += len;
+ if (complete(fifo->data, fifo->len)) {
+ send(s->hci, fifo->data, fifo->len);
+ fifo->len = 0;
+ }
+ } else if (complete(data, len))
+ send(s->hci, data, len);
+ else {
+ memcpy(fifo->data, data, len);
+ fifo->len = len;
+ }
+
+ /* TODO: do we need to loop? */
+}
+
+static int usb_bt_hci_cmd_complete(const uint8_t *data, int len)
+{
+ len -= HCI_COMMAND_HDR_SIZE;
+ return len >= 0 &&
+ len >= ((struct hci_command_hdr *) data)->plen;
+}
+
+static int usb_bt_hci_acl_complete(const uint8_t *data, int len)
+{
+ len -= HCI_ACL_HDR_SIZE;
+ return len >= 0 &&
+ len >= le16_to_cpu(((struct hci_acl_hdr *) data)->dlen);
+}
+
+static int usb_bt_hci_sco_complete(const uint8_t *data, int len)
+{
+ len -= HCI_SCO_HDR_SIZE;
+ return len >= 0 &&
+ len >= ((struct hci_sco_hdr *) data)->dlen;
+}
+
+static void usb_bt_handle_reset(USBDevice *dev)
+{
+ struct USBBtState *s = (struct USBBtState *) dev->opaque;
+
+ usb_bt_fifo_reset(&s->evt);
+ usb_bt_fifo_reset(&s->acl);
+ usb_bt_fifo_reset(&s->sco);
+ s->outcmd.len = 0;
+ s->outacl.len = 0;
+ s->outsco.len = 0;
+ s->altsetting = 0;
+}
+
+static int usb_bt_handle_control(USBDevice *dev, 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);
+ if (ret >= 0) {
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ s->config = 0;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ s->config = 1;
+ usb_bt_fifo_reset(&s->evt);
+ usb_bt_fifo_reset(&s->acl);
+ usb_bt_fifo_reset(&s->sco);
+ break;
+ }
+ return ret;
+ }
+
+ ret = 0;
+ switch (request) {
+ case InterfaceRequest | USB_REQ_GET_STATUS:
+ case EndpointRequest | USB_REQ_GET_STATUS:
+ data[0] = 0x00;
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ goto fail;
+ case InterfaceOutRequest | USB_REQ_SET_FEATURE:
+ case EndpointOutRequest | USB_REQ_SET_FEATURE:
+ goto fail;
+ break;
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ if (value != 0 || (index & ~1) || length != 1)
+ goto fail;
+ if (index == 1)
+ data[0] = s->altsetting;
+ else
+ data[0] = 0;
+ ret = 1;
+ break;
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ if ((index & ~1) || length != 0 ||
+ (index == 1 && (value < 0 || value > 4)) ||
+ (index == 0 && value != 0)) {
+ printf("%s: Wrong SET_INTERFACE request (%i, %i)\n",
+ __FUNCTION__, index, value);
+ goto fail;
+ }
+ s->altsetting = value;
+ ret = 0;
+ break;
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8):
+ if (s->config)
+ usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send,
+ usb_bt_hci_cmd_complete, data, length);
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
+{
+ struct USBBtState *s = (struct USBBtState *) dev->opaque;
+ int ret = 0;
+
+ if (!s->config)
+ goto fail;
+
+ switch (p->pid) {
+ case USB_TOKEN_IN:
+ switch (p->devep & 0xf) {
+ case USB_EVT_EP:
+ ret = usb_bt_fifo_dequeue(&s->evt, p);
+ break;
+
+ case USB_ACL_EP:
+ ret = usb_bt_fifo_dequeue(&s->acl, p);
+ break;
+
+ case USB_SCO_EP:
+ ret = usb_bt_fifo_dequeue(&s->sco, p);
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+
+ case USB_TOKEN_OUT:
+ switch (p->devep & 0xf) {
+ case USB_ACL_EP:
+ usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send,
+ usb_bt_hci_acl_complete, p->data, p->len);
+ break;
+
+ case USB_SCO_EP:
+ usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send,
+ usb_bt_hci_sco_complete, p->data, p->len);
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_bt_out_hci_packet_event(void *opaque,
+ const uint8_t *data, int len)
+{
+ struct USBBtState *s = (struct USBBtState *) opaque;
+
+ usb_bt_fifo_enqueue(&s->evt, data, len);
+}
+
+static void usb_bt_out_hci_packet_acl(void *opaque,
+ const uint8_t *data, int len)
+{
+ struct USBBtState *s = (struct USBBtState *) opaque;
+
+ usb_bt_fifo_enqueue(&s->acl, data, len);
+}
+
+static void usb_bt_handle_destroy(USBDevice *dev)
+{
+ struct USBBtState *s = (struct USBBtState *) dev->opaque;
+
+ s->hci->opaque = NULL;
+ s->hci->evt_recv = NULL;
+ s->hci->acl_recv = NULL;
+}
+
+static int usb_bt_initfn(USBDevice *dev)
+{
+ usb_desc_init(dev);
+ return 0;
+}
+
+USBDevice *usb_bt_init(HCIInfo *hci)
+{
+ USBDevice *dev;
+ struct USBBtState *s;
+
+ if (!hci)
+ return NULL;
+ dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle");
+ s = DO_UPCAST(struct USBBtState, dev, dev);
+ s->dev.opaque = s;
+
+ s->hci = hci;
+ s->hci->opaque = s;
+ s->hci->evt_recv = usb_bt_out_hci_packet_event;
+ s->hci->acl_recv = usb_bt_out_hci_packet_acl;
+
+ usb_bt_handle_reset(&s->dev);
+
+ return dev;
+}
+
+static struct USBDeviceInfo bt_info = {
+ .product_desc = "QEMU BT dongle",
+ .qdev.name = "usb-bt-dongle",
+ .qdev.size = sizeof(struct USBBtState),
+ .usb_desc = &desc_bluetooth,
+ .init = usb_bt_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_bt_handle_reset,
+ .handle_control = usb_bt_handle_control,
+ .handle_data = usb_bt_handle_data,
+ .handle_destroy = usb_bt_handle_destroy,
+};
+
+static void usb_bt_register_devices(void)
+{
+ usb_qdev_register(&bt_info);
+}
+device_init(usb_bt_register_devices)
diff --git a/hw/usb-bus.c b/hw/usb-bus.c
new file mode 100644
index 0000000..abc7e61
--- /dev/null
+++ b/hw/usb-bus.c
@@ -0,0 +1,394 @@
+#include "hw.h"
+#include "usb.h"
+#include "qdev.h"
+#include "sysemu.h"
+#include "monitor.h"
+
+static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
+
+static char *usb_get_dev_path(DeviceState *dev);
+static char *usb_get_fw_dev_path(DeviceState *qdev);
+
+static struct BusInfo usb_bus_info = {
+ .name = "USB",
+ .size = sizeof(USBBus),
+ .print_dev = usb_bus_dev_print,
+ .get_dev_path = usb_get_dev_path,
+ .get_fw_dev_path = usb_get_fw_dev_path,
+ .props = (Property[]) {
+ DEFINE_PROP_STRING("port", USBDevice, port_path),
+ DEFINE_PROP_END_OF_LIST()
+ },
+};
+static int next_usb_bus = 0;
+static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
+
+const VMStateDescription vmstate_usb_device = {
+ .name = "USBDevice",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8(addr, USBDevice),
+ VMSTATE_INT32(state, USBDevice),
+ VMSTATE_INT32(remote_wakeup, USBDevice),
+ VMSTATE_INT32(setup_state, USBDevice),
+ VMSTATE_INT32(setup_len, USBDevice),
+ VMSTATE_INT32(setup_index, USBDevice),
+ VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+void usb_bus_new(USBBus *bus, DeviceState *host)
+{
+ qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL);
+ bus->busnr = next_usb_bus++;
+ bus->qbus.allow_hotplug = 1; /* Yes, we can */
+ QTAILQ_INIT(&bus->free);
+ QTAILQ_INIT(&bus->used);
+ QTAILQ_INSERT_TAIL(&busses, bus, next);
+}
+
+USBBus *usb_bus_find(int busnr)
+{
+ USBBus *bus;
+
+ if (-1 == busnr)
+ return QTAILQ_FIRST(&busses);
+ QTAILQ_FOREACH(bus, &busses, next) {
+ if (bus->busnr == busnr)
+ return bus;
+ }
+ return NULL;
+}
+
+static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+ USBDeviceInfo *info = DO_UPCAST(USBDeviceInfo, qdev, base);
+ int rc;
+
+ pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc);
+ dev->info = info;
+ dev->auto_attach = 1;
+ QLIST_INIT(&dev->strings);
+ rc = dev->info->init(dev);
+ if (rc == 0 && dev->auto_attach)
+ usb_device_attach(dev);
+ return rc;
+}
+
+static int usb_qdev_exit(DeviceState *qdev)
+{
+ USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+
+ usb_device_detach(dev);
+ if (dev->info->handle_destroy) {
+ dev->info->handle_destroy(dev);
+ }
+ return 0;
+}
+
+void usb_qdev_register(USBDeviceInfo *info)
+{
+ info->qdev.bus_info = &usb_bus_info;
+ info->qdev.init = usb_qdev_init;
+ info->qdev.unplug = qdev_simple_unplug_cb;
+ info->qdev.exit = usb_qdev_exit;
+ qdev_register(&info->qdev);
+}
+
+void usb_qdev_register_many(USBDeviceInfo *info)
+{
+ while (info->qdev.name) {
+ usb_qdev_register(info);
+ info++;
+ }
+}
+
+USBDevice *usb_create(USBBus *bus, const char *name)
+{
+ DeviceState *dev;
+
+#if 1
+ /* temporary stopgap until all usb is properly qdev-ified */
+ if (!bus) {
+ bus = usb_bus_find(-1);
+ if (!bus)
+ return NULL;
+ fprintf(stderr, "%s: no bus specified, using \"%s\" for \"%s\"\n",
+ __FUNCTION__, bus->qbus.name, name);
+ }
+#endif
+
+ dev = qdev_create(&bus->qbus, name);
+ return DO_UPCAST(USBDevice, qdev, dev);
+}
+
+USBDevice *usb_create_simple(USBBus *bus, const char *name)
+{
+ USBDevice *dev = usb_create(bus, name);
+ if (!dev) {
+ hw_error("Failed to create USB device '%s'\n", name);
+ }
+ qdev_init_nofail(&dev->qdev);
+ return dev;
+}
+
+void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
+ USBPortOps *ops, int speedmask)
+{
+ port->opaque = opaque;
+ port->index = index;
+ port->opaque = opaque;
+ port->index = index;
+ port->ops = ops;
+ port->speedmask = speedmask;
+ QTAILQ_INSERT_TAIL(&bus->free, port, next);
+ bus->nfree++;
+}
+
+void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
+{
+ if (upstream) {
+ snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
+ upstream->path, portnr);
+ } else {
+ snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
+ }
+}
+
+void usb_unregister_port(USBBus *bus, USBPort *port)
+{
+ if (port->dev)
+ qdev_free(&port->dev->qdev);
+ QTAILQ_REMOVE(&bus->free, port, next);
+ bus->nfree--;
+}
+
+static void do_attach(USBDevice *dev)
+{
+ USBBus *bus = usb_bus_from_device(dev);
+ USBPort *port;
+
+ if (dev->attached) {
+ fprintf(stderr, "Warning: tried to attach usb device %s twice\n",
+ dev->product_desc);
+ return;
+ }
+ if (dev->port_path) {
+ QTAILQ_FOREACH(port, &bus->free, next) {
+ if (strcmp(port->path, dev->port_path) == 0) {
+ break;
+ }
+ }
+ if (port == NULL) {
+ fprintf(stderr, "Warning: usb port %s (bus %s) not found\n",
+ dev->port_path, bus->qbus.name);
+ return;
+ }
+ } else {
+ port = QTAILQ_FIRST(&bus->free);
+ }
+
+ dev->attached++;
+ QTAILQ_REMOVE(&bus->free, port, next);
+ bus->nfree--;
+
+ usb_attach(port, dev);
+
+ QTAILQ_INSERT_TAIL(&bus->used, port, next);
+ bus->nused++;
+}
+
+int usb_device_attach(USBDevice *dev)
+{
+ USBBus *bus = usb_bus_from_device(dev);
+
+ if (bus->nfree == 1 && dev->port_path == NULL) {
+ /* Create a new hub and chain it on
+ (unless a physical port location is specified). */
+ usb_create_simple(bus, "usb-hub");
+ }
+ do_attach(dev);
+ return 0;
+}
+
+int usb_device_detach(USBDevice *dev)
+{
+ USBBus *bus = usb_bus_from_device(dev);
+ USBPort *port;
+
+ if (!dev->attached) {
+ fprintf(stderr, "Warning: tried to detach unattached usb device %s\n",
+ dev->product_desc);
+ return -1;
+ }
+ dev->attached--;
+
+ QTAILQ_FOREACH(port, &bus->used, next) {
+ if (port->dev == dev)
+ break;
+ }
+ assert(port != NULL);
+
+ QTAILQ_REMOVE(&bus->used, port, next);
+ bus->nused--;
+
+ usb_attach(port, NULL);
+
+ QTAILQ_INSERT_TAIL(&bus->free, port, next);
+ bus->nfree++;
+ return 0;
+}
+
+int usb_device_delete_addr(int busnr, int addr)
+{
+ USBBus *bus;
+ USBPort *port;
+ USBDevice *dev;
+
+ bus = usb_bus_find(busnr);
+ if (!bus)
+ return -1;
+
+ QTAILQ_FOREACH(port, &bus->used, next) {
+ if (port->dev->addr == addr)
+ break;
+ }
+ if (!port)
+ return -1;
+ dev = port->dev;
+
+ qdev_free(&dev->qdev);
+ return 0;
+}
+
+static const char *usb_speed(unsigned int speed)
+{
+ static const char *txt[] = {
+ [ USB_SPEED_LOW ] = "1.5",
+ [ USB_SPEED_FULL ] = "12",
+ [ USB_SPEED_HIGH ] = "480",
+ };
+ if (speed >= ARRAY_SIZE(txt))
+ return "?";
+ return txt[speed];
+}
+
+static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
+{
+ USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+ USBBus *bus = usb_bus_from_device(dev);
+
+ monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
+ indent, "", bus->busnr, dev->addr,
+ dev->port ? dev->port->path : "-",
+ usb_speed(dev->speed), dev->product_desc,
+ dev->attached ? ", attached" : "");
+}
+
+static char *usb_get_dev_path(DeviceState *qdev)
+{
+ USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+ return qemu_strdup(dev->port->path);
+}
+
+static char *usb_get_fw_dev_path(DeviceState *qdev)
+{
+ USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
+ char *fw_path, *in;
+ ssize_t pos = 0, fw_len;
+ long nr;
+
+ fw_len = 32 + strlen(dev->port->path) * 6;
+ fw_path = qemu_malloc(fw_len);
+ in = dev->port->path;
+ while (fw_len - pos > 0) {
+ nr = strtol(in, &in, 10);
+ if (in[0] == '.') {
+ /* some hub between root port and device */
+ pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr);
+ in++;
+ } else {
+ /* the device itself */
+ pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld",
+ qdev_fw_name(qdev), nr);
+ break;
+ }
+ }
+ return fw_path;
+}
+
+void usb_info(Monitor *mon)
+{
+ USBBus *bus;
+ USBDevice *dev;
+ USBPort *port;
+
+ if (QTAILQ_EMPTY(&busses)) {
+ monitor_printf(mon, "USB support not enabled\n");
+ return;
+ }
+
+ QTAILQ_FOREACH(bus, &busses, next) {
+ QTAILQ_FOREACH(port, &bus->used, next) {
+ dev = port->dev;
+ if (!dev)
+ continue;
+ monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
+ bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
+ dev->product_desc);
+ }
+ }
+}
+
+/* handle legacy -usbdevice cmd line option */
+USBDevice *usbdevice_create(const char *cmdline)
+{
+ USBBus *bus = usb_bus_find(-1 /* any */);
+ DeviceInfo *info;
+ USBDeviceInfo *usb;
+ char driver[32];
+ const char *params;
+ int len;
+
+ params = strchr(cmdline,':');
+ if (params) {
+ params++;
+ len = params - cmdline;
+ if (len > sizeof(driver))
+ len = sizeof(driver);
+ pstrcpy(driver, len, cmdline);
+ } else {
+ params = "";
+ pstrcpy(driver, sizeof(driver), cmdline);
+ }
+
+ for (info = device_info_list; info != NULL; info = info->next) {
+ if (info->bus_info != &usb_bus_info)
+ continue;
+ usb = DO_UPCAST(USBDeviceInfo, qdev, info);
+ if (usb->usbdevice_name == NULL)
+ continue;
+ if (strcmp(usb->usbdevice_name, driver) != 0)
+ continue;
+ break;
+ }
+ if (info == NULL) {
+#if 0
+ /* no error because some drivers are not converted (yet) */
+ error_report("usbdevice %s not found", driver);
+#endif
+ return NULL;
+ }
+
+ if (!usb->usbdevice_init) {
+ if (*params) {
+ error_report("usbdevice %s accepts no params", driver);
+ return NULL;
+ }
+ return usb_create_simple(bus, usb->qdev.name);
+ }
+ return usb->usbdevice_init(params);
+}
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
new file mode 100644
index 0000000..62591f2
--- /dev/null
+++ b/hw/usb-desc.c
@@ -0,0 +1,406 @@
+#include "usb.h"
+#include "usb-desc.h"
+#include "trace.h"
+
+/* ------------------------------------------------------------------ */
+
+static uint8_t usb_lo(uint16_t val)
+{
+ return val & 0xff;
+}
+
+static uint8_t usb_hi(uint16_t val)
+{
+ return (val >> 8) & 0xff;
+}
+
+int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
+ uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x12;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ dest[0x00] = bLength;
+ dest[0x01] = USB_DT_DEVICE;
+
+ dest[0x02] = usb_lo(dev->bcdUSB);
+ dest[0x03] = usb_hi(dev->bcdUSB);
+ dest[0x04] = dev->bDeviceClass;
+ dest[0x05] = dev->bDeviceSubClass;
+ dest[0x06] = dev->bDeviceProtocol;
+ dest[0x07] = dev->bMaxPacketSize0;
+
+ dest[0x08] = usb_lo(id->idVendor);
+ dest[0x09] = usb_hi(id->idVendor);
+ dest[0x0a] = usb_lo(id->idProduct);
+ dest[0x0b] = usb_hi(id->idProduct);
+ dest[0x0c] = usb_lo(id->bcdDevice);
+ dest[0x0d] = usb_hi(id->bcdDevice);
+ dest[0x0e] = id->iManufacturer;
+ dest[0x0f] = id->iProduct;
+ dest[0x10] = id->iSerialNumber;
+
+ dest[0x11] = dev->bNumConfigurations;
+
+ return bLength;
+}
+
+int usb_desc_device_qualifier(const USBDescDevice *dev,
+ uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x0a;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ dest[0x00] = bLength;
+ dest[0x01] = USB_DT_DEVICE_QUALIFIER;
+
+ dest[0x02] = usb_lo(dev->bcdUSB);
+ dest[0x03] = usb_hi(dev->bcdUSB);
+ dest[0x04] = dev->bDeviceClass;
+ dest[0x05] = dev->bDeviceSubClass;
+ dest[0x06] = dev->bDeviceProtocol;
+ dest[0x07] = dev->bMaxPacketSize0;
+ dest[0x08] = dev->bNumConfigurations;
+ dest[0x09] = 0; /* reserved */
+
+ return bLength;
+}
+
+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;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ dest[0x00] = bLength;
+ dest[0x01] = USB_DT_CONFIG;
+ dest[0x04] = conf->bNumInterfaces;
+ dest[0x05] = conf->bConfigurationValue;
+ dest[0x06] = conf->iConfiguration;
+ dest[0x07] = conf->bmAttributes;
+ dest[0x08] = conf->bMaxPower;
+ wTotalLength += bLength;
+
+ count = conf->nif ? conf->nif : conf->bNumInterfaces;
+ for (i = 0; i < count; i++) {
+ rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
+ if (rc < 0) {
+ return rc;
+ }
+ wTotalLength += rc;
+ }
+
+ dest[0x02] = usb_lo(wTotalLength);
+ dest[0x03] = usb_hi(wTotalLength);
+ return wTotalLength;
+}
+
+int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x09;
+ int i, rc, pos = 0;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ dest[0x00] = bLength;
+ dest[0x01] = USB_DT_INTERFACE;
+ dest[0x02] = iface->bInterfaceNumber;
+ dest[0x03] = iface->bAlternateSetting;
+ dest[0x04] = iface->bNumEndpoints;
+ dest[0x05] = iface->bInterfaceClass;
+ dest[0x06] = iface->bInterfaceSubClass;
+ dest[0x07] = iface->bInterfaceProtocol;
+ dest[0x08] = iface->iInterface;
+ pos += bLength;
+
+ for (i = 0; i < iface->ndesc; i++) {
+ rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
+ if (rc < 0) {
+ return rc;
+ }
+ pos += rc;
+ }
+
+ for (i = 0; i < iface->bNumEndpoints; i++) {
+ rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
+ if (rc < 0) {
+ return rc;
+ }
+ pos += rc;
+ }
+
+ return pos;
+}
+
+int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
+{
+ uint8_t bLength = 0x07;
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ dest[0x00] = bLength;
+ dest[0x01] = USB_DT_ENDPOINT;
+ dest[0x02] = ep->bEndpointAddress;
+ dest[0x03] = ep->bmAttributes;
+ dest[0x04] = usb_lo(ep->wMaxPacketSize);
+ dest[0x05] = usb_hi(ep->wMaxPacketSize);
+ dest[0x06] = ep->bInterval;
+
+ return bLength;
+}
+
+int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
+{
+ int bLength = desc->length ? desc->length : desc->data[0];
+
+ if (len < bLength) {
+ return -1;
+ }
+
+ memcpy(dest, desc->data, bLength);
+ return bLength;
+}
+
+/* ------------------------------------------------------------------ */
+
+static void usb_desc_setdefaults(USBDevice *dev)
+{
+ const USBDesc *desc = dev->info->usb_desc;
+
+ assert(desc != NULL);
+ switch (dev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ dev->device = desc->full;
+ break;
+ case USB_SPEED_HIGH:
+ dev->device = desc->high;
+ break;
+ }
+ dev->config = dev->device->confs;
+}
+
+void usb_desc_init(USBDevice *dev)
+{
+ dev->speed = USB_SPEED_FULL;
+ usb_desc_setdefaults(dev);
+}
+
+void usb_desc_attach(USBDevice *dev)
+{
+ const USBDesc *desc = dev->info->usb_desc;
+
+ assert(desc != NULL);
+ if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
+ dev->speed = USB_SPEED_HIGH;
+ } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
+ dev->speed = USB_SPEED_FULL;
+ } else {
+ fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n",
+ dev->info->product_desc);
+ return;
+ }
+ usb_desc_setdefaults(dev);
+}
+
+void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str)
+{
+ USBDescString *s;
+
+ QLIST_FOREACH(s, &dev->strings, next) {
+ if (s->index == index) {
+ break;
+ }
+ }
+ if (s == NULL) {
+ s = qemu_mallocz(sizeof(*s));
+ s->index = index;
+ QLIST_INSERT_HEAD(&dev->strings, s, next);
+ }
+ qemu_free(s->str);
+ s->str = qemu_strdup(str);
+}
+
+const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
+{
+ USBDescString *s;
+
+ QLIST_FOREACH(s, &dev->strings, next) {
+ if (s->index == index) {
+ return s->str;
+ }
+ }
+ return NULL;
+}
+
+int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
+{
+ uint8_t bLength, pos, i;
+ const char *str;
+
+ if (len < 4) {
+ return -1;
+ }
+
+ if (index == 0) {
+ /* language ids */
+ dest[0] = 4;
+ dest[1] = USB_DT_STRING;
+ dest[2] = 0x09;
+ dest[3] = 0x04;
+ return 4;
+ }
+
+ str = usb_desc_get_string(dev, index);
+ if (str == NULL) {
+ str = dev->info->usb_desc->str[index];
+ if (str == NULL) {
+ return 0;
+ }
+ }
+
+ bLength = strlen(str) * 2 + 2;
+ dest[0] = bLength;
+ dest[1] = USB_DT_STRING;
+ i = 0; pos = 2;
+ while (pos+1 < bLength && pos+1 < len) {
+ dest[pos++] = str[i++];
+ dest[pos++] = 0;
+ }
+ return pos;
+}
+
+int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
+{
+ const USBDesc *desc = dev->info->usb_desc;
+ const USBDescDevice *other_dev;
+ uint8_t buf[256];
+ uint8_t type = value >> 8;
+ uint8_t index = value & 0xff;
+ int ret = -1;
+
+ if (dev->speed == USB_SPEED_HIGH) {
+ other_dev = dev->info->usb_desc->full;
+ } else {
+ other_dev = dev->info->usb_desc->high;
+ }
+
+ switch(type) {
+ case USB_DT_DEVICE:
+ ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
+ trace_usb_desc_device(dev->addr, len, ret);
+ break;
+ case USB_DT_CONFIG:
+ if (index < dev->device->bNumConfigurations) {
+ ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
+ }
+ trace_usb_desc_config(dev->addr, index, len, ret);
+ break;
+ case USB_DT_STRING:
+ ret = usb_desc_string(dev, index, buf, sizeof(buf));
+ trace_usb_desc_string(dev->addr, index, len, ret);
+ break;
+
+ case USB_DT_DEVICE_QUALIFIER:
+ if (other_dev != NULL) {
+ ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
+ }
+ trace_usb_desc_device_qualifier(dev->addr, len, ret);
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (other_dev != NULL && index < other_dev->bNumConfigurations) {
+ ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
+ buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
+ }
+ trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
+ break;
+
+ default:
+ fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
+ dev->addr, type, len);
+ break;
+ }
+
+ if (ret > 0) {
+ if (ret > len) {
+ ret = len;
+ }
+ memcpy(dest, buf, ret);
+ }
+ return ret;
+}
+
+int usb_desc_handle_control(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data)
+{
+ const USBDesc *desc = dev->info->usb_desc;
+ int i, ret = -1;
+
+ assert(desc != NULL);
+ switch(request) {
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ dev->addr = value;
+ trace_usb_set_addr(dev->addr);
+ ret = 0;
+ break;
+
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ ret = usb_desc_get_descriptor(dev, value, data, length);
+ break;
+
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ data[0] = dev->config->bConfigurationValue;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ for (i = 0; i < dev->device->bNumConfigurations; i++) {
+ if (dev->device->confs[i].bConfigurationValue == value) {
+ dev->config = dev->device->confs + i;
+ ret = 0;
+ }
+ }
+ trace_usb_set_config(dev->addr, value, ret);
+ break;
+
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ data[0] = 0;
+ if (dev->config->bmAttributes & 0x40) {
+ data[0] |= 1 << USB_DEVICE_SELF_POWERED;
+ }
+ if (dev->remote_wakeup) {
+ data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
+ }
+ data[1] = 0x00;
+ ret = 2;
+ break;
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 0;
+ ret = 0;
+ }
+ trace_usb_clear_device_feature(dev->addr, value, ret);
+ break;
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ if (value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->remote_wakeup = 1;
+ ret = 0;
+ }
+ trace_usb_set_device_feature(dev->addr, value, ret);
+ break;
+ }
+ return ret;
+}
diff --git a/hw/usb-desc.h b/hw/usb-desc.h
new file mode 100644
index 0000000..ac734ab
--- /dev/null
+++ b/hw/usb-desc.h
@@ -0,0 +1,92 @@
+#ifndef QEMU_HW_USB_DESC_H
+#define QEMU_HW_USB_DESC_H
+
+#include <inttypes.h>
+
+struct USBDescID {
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+};
+
+struct USBDescDevice {
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint8_t bNumConfigurations;
+
+ const USBDescConfig *confs;
+};
+
+struct USBDescConfig {
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+
+ uint8_t nif;
+ const USBDescIface *ifs;
+};
+
+struct USBDescIface {
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+
+ uint8_t ndesc;
+ USBDescOther *descs;
+ USBDescEndpoint *eps;
+};
+
+struct USBDescEndpoint {
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+};
+
+struct USBDescOther {
+ uint8_t length;
+ uint8_t *data;
+};
+
+typedef const char *USBDescStrings[256];
+
+struct USBDesc {
+ USBDescID id;
+ const USBDescDevice *full;
+ const USBDescDevice *high;
+ const char* const *str;
+};
+
+/* generate usb packages from structs */
+int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
+ uint8_t *dest, size_t len);
+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(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);
+
+/* control message emulation helpers */
+void usb_desc_init(USBDevice *dev);
+void usb_desc_attach(USBDevice *dev);
+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);
+
+#endif /* QEMU_HW_USB_DESC_H */
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
new file mode 100644
index 0000000..79b20df
--- /dev/null
+++ b/hw/usb-hid.c
@@ -0,0 +1,1006 @@
+/*
+ * QEMU USB HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "usb.h"
+#include "usb-desc.h"
+#include "sysemu.h"
+
+/* HID interface requests */
+#define GET_REPORT 0xa101
+#define GET_IDLE 0xa102
+#define GET_PROTOCOL 0xa103
+#define SET_REPORT 0x2109
+#define SET_IDLE 0x210a
+#define SET_PROTOCOL 0x210b
+
+/* HID descriptor types */
+#define USB_DT_HID 0x21
+#define USB_DT_REPORT 0x22
+#define USB_DT_PHY 0x23
+
+#define USB_MOUSE 1
+#define USB_TABLET 2
+#define USB_KEYBOARD 3
+
+typedef struct USBPointerEvent {
+ int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
+ int32_t dz, buttons_state;
+} USBPointerEvent;
+
+#define QUEUE_LENGTH 16 /* should be enough for a triple-click */
+#define QUEUE_MASK (QUEUE_LENGTH-1u)
+#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK)
+
+typedef struct USBMouseState {
+ USBPointerEvent queue[QUEUE_LENGTH];
+ int mouse_grabbed;
+ QEMUPutMouseEntry *eh_entry;
+} USBMouseState;
+
+typedef struct USBKeyboardState {
+ uint32_t keycodes[QUEUE_LENGTH];
+ uint16_t modifiers;
+ uint8_t leds;
+ uint8_t key[16];
+ int32_t keys;
+} USBKeyboardState;
+
+typedef struct USBHIDState {
+ USBDevice dev;
+ union {
+ USBMouseState ptr;
+ USBKeyboardState kbd;
+ };
+ uint32_t head; /* index into circular queue */
+ uint32_t n;
+ int kind;
+ int32_t protocol;
+ uint8_t idle;
+ int64_t next_idle_clock;
+ int changed;
+ void *datain_opaque;
+ void (*datain)(void *);
+} USBHIDState;
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT_MOUSE,
+ STR_PRODUCT_TABLET,
+ STR_PRODUCT_KEYBOARD,
+ STR_SERIALNUMBER,
+ STR_CONFIG_MOUSE,
+ STR_CONFIG_TABLET,
+ STR_CONFIG_KEYBOARD,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+ [STR_PRODUCT_MOUSE] = "QEMU USB Mouse",
+ [STR_PRODUCT_TABLET] = "QEMU USB Tablet",
+ [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard",
+ [STR_SERIALNUMBER] = "42", /* == remote wakeup works */
+ [STR_CONFIG_MOUSE] = "HID Mouse",
+ [STR_CONFIG_TABLET] = "HID Tablet",
+ [STR_CONFIG_KEYBOARD] = "HID Keyboard",
+};
+
+static const USBDescIface desc_iface_mouse = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0x01, /* boot */
+ .bInterfaceProtocol = 0x02,
+ .ndesc = 1,
+ .descs = (USBDescOther[]) {
+ {
+ /* HID descriptor */
+ .data = (uint8_t[]) {
+ 0x09, /* u8 bLength */
+ USB_DT_HID, /* u8 bDescriptorType */
+ 0x01, 0x00, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ USB_DT_REPORT, /* u8 type: Report */
+ 52, 0, /* u16 len */
+ },
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 4,
+ .bInterval = 0x0a,
+ },
+ },
+};
+
+static const USBDescIface desc_iface_tablet = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0x01, /* boot */
+ .bInterfaceProtocol = 0x02,
+ .ndesc = 1,
+ .descs = (USBDescOther[]) {
+ {
+ /* HID descriptor */
+ .data = (uint8_t[]) {
+ 0x09, /* u8 bLength */
+ USB_DT_HID, /* u8 bDescriptorType */
+ 0x01, 0x00, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ USB_DT_REPORT, /* u8 type: Report */
+ 74, 0, /* u16 len */
+ },
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 8,
+ .bInterval = 0x0a,
+ },
+ },
+};
+
+static const USBDescIface desc_iface_keyboard = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0x01, /* boot */
+ .bInterfaceProtocol = 0x01, /* keyboard */
+ .ndesc = 1,
+ .descs = (USBDescOther[]) {
+ {
+ /* HID descriptor */
+ .data = (uint8_t[]) {
+ 0x09, /* u8 bLength */
+ USB_DT_HID, /* u8 bDescriptorType */
+ 0x11, 0x01, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ USB_DT_REPORT, /* u8 type: Report */
+ 0x3f, 0, /* u16 len */
+ },
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 8,
+ .bInterval = 0x0a,
+ },
+ },
+};
+
+static const USBDescDevice desc_device_mouse = {
+ .bcdUSB = 0x0100,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_MOUSE,
+ .bmAttributes = 0xa0,
+ .bMaxPower = 50,
+ .ifs = &desc_iface_mouse,
+ },
+ },
+};
+
+static const USBDescDevice desc_device_tablet = {
+ .bcdUSB = 0x0100,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_TABLET,
+ .bmAttributes = 0xa0,
+ .bMaxPower = 50,
+ .ifs = &desc_iface_tablet,
+ },
+ },
+};
+
+static const USBDescDevice desc_device_keyboard = {
+ .bcdUSB = 0x0100,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_KEYBOARD,
+ .bmAttributes = 0xa0,
+ .bMaxPower = 50,
+ .ifs = &desc_iface_keyboard,
+ },
+ },
+};
+
+static const USBDesc desc_mouse = {
+ .id = {
+ .idVendor = 0x0627,
+ .idProduct = 0x0001,
+ .bcdDevice = 0,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT_MOUSE,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_mouse,
+ .str = desc_strings,
+};
+
+static const USBDesc desc_tablet = {
+ .id = {
+ .idVendor = 0x0627,
+ .idProduct = 0x0001,
+ .bcdDevice = 0,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT_TABLET,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_tablet,
+ .str = desc_strings,
+};
+
+static const USBDesc desc_keyboard = {
+ .id = {
+ .idVendor = 0x0627,
+ .idProduct = 0x0001,
+ .bcdDevice = 0,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT_KEYBOARD,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_keyboard,
+ .str = desc_strings,
+};
+
+static const uint8_t qemu_mouse_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
+};
+
+static const uint8_t qemu_tablet_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x03, /* Usage Maximum (3) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
+ 0x35, 0x00, /* Physical Minimum (0) */
+ 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
+ 0x35, 0x00, /* Physical Minimum (same as logical) */
+ 0x45, 0x00, /* Physical Maximum (same as logical) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
+};
+
+static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x06, /* Usage (Keyboard) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0xe0, /* Usage Minimum (224) */
+ 0x29, 0xe7, /* Usage Maximum (231) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x05, 0x08, /* Usage Page (LEDs) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x05, /* Usage Maximum (5) */
+ 0x91, 0x02, /* Output (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x03, /* Report Size (3) */
+ 0x91, 0x01, /* Output (Constant) */
+ 0x95, 0x06, /* Report Count (6) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0xff, /* Logical Maximum (255) */
+ 0x05, 0x07, /* Usage Page (Key Codes) */
+ 0x19, 0x00, /* Usage Minimum (0) */
+ 0x29, 0xff, /* Usage Maximum (255) */
+ 0x81, 0x00, /* Input (Data, Array) */
+ 0xc0, /* End Collection */
+};
+
+#define USB_HID_USAGE_ERROR_ROLLOVER 0x01
+#define USB_HID_USAGE_POSTFAIL 0x02
+#define USB_HID_USAGE_ERROR_UNDEFINED 0x03
+
+/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
+ * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
+static const uint8_t usb_hid_usage_keys[0x100] = {
+ 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
+ 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
+ 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
+ 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
+ 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
+ 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
+ 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+ 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
+ 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
+ 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+ 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+ 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
+ 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
+ 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
+ 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void usb_hid_changed(USBHIDState *hs)
+{
+ hs->changed = 1;
+
+ if (hs->datain)
+ hs->datain(hs->datain_opaque);
+
+ usb_wakeup(&hs->dev);
+}
+
+static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) {
+ e->xdx = e->ydy = e->dz = 0;
+ e->buttons_state = buttons;
+}
+
+static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel,
+ int x1, int y1, int z1) {
+ if (xyrel) {
+ e->xdx += x1;
+ e->ydy += y1;
+ } else {
+ e->xdx = x1;
+ e->ydy = y1;
+ }
+ e->dz += z1;
+}
+
+static void usb_pointer_event(void *opaque,
+ int x1, int y1, int z1, int buttons_state)
+{
+ USBHIDState *hs = opaque;
+ USBMouseState *s = &hs->ptr;
+ unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
+ unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
+
+ /* We combine events where feasible to keep the queue small. We shouldn't
+ * combine anything with the first event of a particular button state, as
+ * that would change the location of the button state change. When the
+ * queue is empty, a second event is needed because we don't know if
+ * the first event changed the button state. */
+ if (hs->n == QUEUE_LENGTH) {
+ /* Queue full. Discard old button state, combine motion normally. */
+ s->queue[use_slot].buttons_state = buttons_state;
+ } else if (hs->n < 2 ||
+ s->queue[use_slot].buttons_state != buttons_state ||
+ s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) {
+ /* Cannot or should not combine, so add an empty item to the queue. */
+ QUEUE_INCR(use_slot);
+ hs->n++;
+ usb_pointer_event_clear(&s->queue[use_slot], buttons_state);
+ }
+ usb_pointer_event_combine(&s->queue[use_slot],
+ hs->kind == USB_MOUSE,
+ x1, y1, z1);
+ usb_hid_changed(hs);
+}
+
+static void usb_keyboard_event(void *opaque, int keycode)
+{
+ USBHIDState *hs = opaque;
+ USBKeyboardState *s = &hs->kbd;
+ int slot;
+
+ if (hs->n == QUEUE_LENGTH) {
+ fprintf(stderr, "usb-kbd: warning: key event queue full\n");
+ return;
+ }
+ slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
+ s->keycodes[slot] = keycode;
+ usb_hid_changed(hs);
+}
+
+static void usb_keyboard_process_keycode(USBHIDState *hs)
+{
+ USBKeyboardState *s = &hs->kbd;
+ uint8_t hid_code, key;
+ int i, keycode, slot;
+
+ if (hs->n == 0) {
+ return;
+ }
+ slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
+ keycode = s->keycodes[slot];
+
+ key = keycode & 0x7f;
+ hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
+ s->modifiers &= ~(1 << 8);
+
+ switch (hid_code) {
+ case 0x00:
+ return;
+
+ case 0xe0:
+ if (s->modifiers & (1 << 9)) {
+ s->modifiers ^= 3 << 8;
+ usb_hid_changed(hs);
+ return;
+ }
+ case 0xe1 ... 0xe7:
+ if (keycode & (1 << 7)) {
+ s->modifiers &= ~(1 << (hid_code & 0x0f));
+ usb_hid_changed(hs);
+ return;
+ }
+ case 0xe8 ... 0xef:
+ s->modifiers |= 1 << (hid_code & 0x0f);
+ usb_hid_changed(hs);
+ return;
+ }
+
+ if (keycode & (1 << 7)) {
+ for (i = s->keys - 1; i >= 0; i --)
+ if (s->key[i] == hid_code) {
+ s->key[i] = s->key[-- s->keys];
+ s->key[s->keys] = 0x00;
+ break;
+ }
+ if (i < 0)
+ return;
+ } else {
+ for (i = s->keys - 1; i >= 0; i --)
+ if (s->key[i] == hid_code)
+ break;
+ if (i < 0) {
+ if (s->keys < sizeof(s->key))
+ s->key[s->keys ++] = hid_code;
+ } else
+ return;
+ }
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+ if (val < vmin)
+ return vmin;
+ else if (val > vmax)
+ return vmax;
+ else
+ return val;
+}
+
+static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+ int dx, dy, dz, b, l;
+ int index;
+ USBMouseState *s = &hs->ptr;
+ USBPointerEvent *e;
+
+ if (!s->mouse_grabbed) {
+ qemu_activate_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 1;
+ }
+
+ /* When the buffer is empty, return the last event. Relative
+ movements will all be zero. */
+ index = (hs->n ? hs->head : hs->head - 1);
+ e = &s->queue[index & QUEUE_MASK];
+
+ if (hs->kind == USB_MOUSE) {
+ dx = int_clamp(e->xdx, -127, 127);
+ dy = int_clamp(e->ydy, -127, 127);
+ e->xdx -= dx;
+ e->ydy -= dy;
+ } else {
+ dx = e->xdx;
+ dy = e->ydy;
+ }
+ dz = int_clamp(e->dz, -127, 127);
+ e->dz -= dz;
+
+ b = 0;
+ if (e->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (e->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (e->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ if (hs->n &&
+ !e->dz &&
+ (hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) {
+ /* that deals with this event */
+ QUEUE_INCR(hs->head);
+ hs->n--;
+ }
+
+ /* Appears we have to invert the wheel direction */
+ dz = 0 - dz;
+ l = 0;
+ switch (hs->kind) {
+ case USB_MOUSE:
+ if (len > l)
+ buf[l++] = b;
+ if (len > l)
+ buf[l++] = dx;
+ if (len > l)
+ buf[l++] = dy;
+ if (len > l)
+ buf[l++] = dz;
+ break;
+
+ case USB_TABLET:
+ if (len > l)
+ buf[l++] = b;
+ if (len > l)
+ buf[l++] = dx & 0xff;
+ if (len > l)
+ buf[l++] = dx >> 8;
+ if (len > l)
+ buf[l++] = dy & 0xff;
+ if (len > l)
+ buf[l++] = dy >> 8;
+ if (len > l)
+ buf[l++] = dz;
+ break;
+
+ default:
+ abort();
+ }
+
+ return l;
+}
+
+static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+ USBKeyboardState *s = &hs->kbd;
+ if (len < 2)
+ return 0;
+
+ usb_keyboard_process_keycode(hs);
+
+ buf[0] = s->modifiers & 0xff;
+ buf[1] = 0;
+ if (s->keys > 6)
+ memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
+ else
+ memcpy(buf + 2, s->key, MIN(8, len) - 2);
+
+ return MIN(8, len);
+}
+
+static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
+{
+ if (len > 0) {
+ int ledstate = 0;
+ /* 0x01: Num Lock LED
+ * 0x02: Caps Lock LED
+ * 0x04: Scroll Lock LED
+ * 0x08: Compose LED
+ * 0x10: Kana LED */
+ s->leds = buf[0];
+ if (s->leds & 0x04)
+ ledstate |= QEMU_SCROLL_LOCK_LED;
+ if (s->leds & 0x01)
+ ledstate |= QEMU_NUM_LOCK_LED;
+ if (s->leds & 0x02)
+ ledstate |= QEMU_CAPS_LOCK_LED;
+ kbd_put_ledstate(ledstate);
+ }
+ return 0;
+}
+
+static void usb_mouse_handle_reset(USBDevice *dev)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ memset(s->ptr.queue, 0, sizeof (s->ptr.queue));
+ s->head = 0;
+ s->n = 0;
+ s->protocol = 1;
+}
+
+static void usb_keyboard_handle_reset(USBDevice *dev)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ qemu_add_kbd_event_handler(usb_keyboard_event, s);
+ memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes));
+ s->head = 0;
+ s->n = 0;
+ memset(s->kbd.key, 0, sizeof (s->kbd.key));
+ s->kbd.keys = 0;
+ s->protocol = 1;
+}
+
+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)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+ int ret;
+
+ ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ if (ret >= 0) {
+ return ret;
+ }
+
+ ret = 0;
+ switch(request) {
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* hid specific requests */
+ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch(value >> 8) {
+ case 0x22:
+ if (s->kind == USB_MOUSE) {
+ memcpy(data, qemu_mouse_hid_report_descriptor,
+ sizeof(qemu_mouse_hid_report_descriptor));
+ ret = sizeof(qemu_mouse_hid_report_descriptor);
+ } else if (s->kind == USB_TABLET) {
+ memcpy(data, qemu_tablet_hid_report_descriptor,
+ sizeof(qemu_tablet_hid_report_descriptor));
+ ret = sizeof(qemu_tablet_hid_report_descriptor);
+ } else if (s->kind == USB_KEYBOARD) {
+ memcpy(data, qemu_keyboard_hid_report_descriptor,
+ sizeof(qemu_keyboard_hid_report_descriptor));
+ ret = sizeof(qemu_keyboard_hid_report_descriptor);
+ }
+ break;
+ default:
+ goto fail;
+ }
+ break;
+ case GET_REPORT:
+ if (s->kind == USB_MOUSE || s->kind == USB_TABLET)
+ ret = usb_pointer_poll(s, data, length);
+ else if (s->kind == USB_KEYBOARD)
+ ret = usb_keyboard_poll(s, data, length);
+ break;
+ case SET_REPORT:
+ if (s->kind == USB_KEYBOARD)
+ ret = usb_keyboard_write(&s->kbd, data, length);
+ else
+ goto fail;
+ break;
+ case GET_PROTOCOL:
+ if (s->kind != USB_KEYBOARD)
+ goto fail;
+ ret = 1;
+ data[0] = s->protocol;
+ break;
+ case SET_PROTOCOL:
+ if (s->kind != USB_KEYBOARD)
+ goto fail;
+ ret = 0;
+ s->protocol = value;
+ break;
+ case GET_IDLE:
+ ret = 1;
+ data[0] = s->idle;
+ break;
+ case SET_IDLE:
+ s->idle = (uint8_t) (value >> 8);
+ usb_hid_set_next_idle(s, qemu_get_clock(vm_clock));
+ ret = 0;
+ break;
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+ int ret = 0;
+
+ switch(p->pid) {
+ case USB_TOKEN_IN:
+ if (p->devep == 1) {
+ int64_t curtime = qemu_get_clock(vm_clock);
+ if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0))
+ return USB_RET_NAK;
+ usb_hid_set_next_idle(s, curtime);
+ if (s->kind == USB_MOUSE || s->kind == USB_TABLET) {
+ ret = usb_pointer_poll(s, p->data, p->len);
+ }
+ else if (s->kind == USB_KEYBOARD) {
+ ret = usb_keyboard_poll(s, p->data, p->len);
+ }
+ s->changed = s->n > 0;
+ } else {
+ goto fail;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_hid_handle_destroy(USBDevice *dev)
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ switch(s->kind) {
+ case USB_KEYBOARD:
+ qemu_remove_kbd_event_handler();
+ break;
+ default:
+ qemu_remove_mouse_event_handler(s->ptr.eh_entry);
+ }
+}
+
+static int usb_hid_initfn(USBDevice *dev, int kind)
+{
+ USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
+
+ usb_desc_init(dev);
+ s->kind = kind;
+
+ if (s->kind == USB_MOUSE) {
+ s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s,
+ 0, "QEMU USB Mouse");
+ } else if (s->kind == USB_TABLET) {
+ s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s,
+ 1, "QEMU USB Tablet");
+ }
+
+ /* Force poll routine to be run and grab input the first time. */
+ s->changed = 1;
+ return 0;
+}
+
+static int usb_tablet_initfn(USBDevice *dev)
+{
+ return usb_hid_initfn(dev, USB_TABLET);
+}
+
+static int usb_mouse_initfn(USBDevice *dev)
+{
+ return usb_hid_initfn(dev, USB_MOUSE);
+}
+
+static int usb_keyboard_initfn(USBDevice *dev)
+{
+ return usb_hid_initfn(dev, USB_KEYBOARD);
+}
+
+void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *))
+{
+ USBHIDState *s = (USBHIDState *)dev;
+
+ s->datain_opaque = opaque;
+ s->datain = datain;
+}
+
+static int usb_hid_post_load(void *opaque, int version_id)
+{
+ USBHIDState *s = opaque;
+
+ if (s->idle) {
+ usb_hid_set_next_idle(s, qemu_get_clock(vm_clock));
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_usb_ptr_queue = {
+ .name = "usb-ptr-queue",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(xdx, USBPointerEvent),
+ VMSTATE_INT32(ydy, USBPointerEvent),
+ VMSTATE_INT32(dz, USBPointerEvent),
+ VMSTATE_INT32(buttons_state, USBPointerEvent),
+ VMSTATE_END_OF_LIST()
+ }
+};
+static const VMStateDescription vmstate_usb_ptr = {
+ .name = "usb-ptr",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = usb_hid_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_USB_DEVICE(dev, USBHIDState),
+ VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0,
+ vmstate_usb_ptr_queue, USBPointerEvent),
+ VMSTATE_UINT32(head, USBHIDState),
+ VMSTATE_UINT32(n, USBHIDState),
+ VMSTATE_INT32(protocol, USBHIDState),
+ VMSTATE_UINT8(idle, USBHIDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_usb_kbd = {
+ .name = "usb-kbd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = usb_hid_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_USB_DEVICE(dev, USBHIDState),
+ VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH),
+ VMSTATE_UINT32(head, USBHIDState),
+ VMSTATE_UINT32(n, USBHIDState),
+ VMSTATE_UINT16(kbd.modifiers, USBHIDState),
+ VMSTATE_UINT8(kbd.leds, USBHIDState),
+ VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16),
+ VMSTATE_INT32(kbd.keys, USBHIDState),
+ VMSTATE_INT32(protocol, USBHIDState),
+ VMSTATE_UINT8(idle, USBHIDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static struct USBDeviceInfo hid_info[] = {
+ {
+ .product_desc = "QEMU USB Tablet",
+ .qdev.name = "usb-tablet",
+ .usbdevice_name = "tablet",
+ .qdev.size = sizeof(USBHIDState),
+ .qdev.vmsd = &vmstate_usb_ptr,
+ .usb_desc = &desc_tablet,
+ .init = usb_tablet_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_mouse_handle_reset,
+ .handle_control = usb_hid_handle_control,
+ .handle_data = usb_hid_handle_data,
+ .handle_destroy = usb_hid_handle_destroy,
+ },{
+ .product_desc = "QEMU USB Mouse",
+ .qdev.name = "usb-mouse",
+ .usbdevice_name = "mouse",
+ .qdev.size = sizeof(USBHIDState),
+ .qdev.vmsd = &vmstate_usb_ptr,
+ .usb_desc = &desc_mouse,
+ .init = usb_mouse_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_mouse_handle_reset,
+ .handle_control = usb_hid_handle_control,
+ .handle_data = usb_hid_handle_data,
+ .handle_destroy = usb_hid_handle_destroy,
+ },{
+ .product_desc = "QEMU USB Keyboard",
+ .qdev.name = "usb-kbd",
+ .usbdevice_name = "keyboard",
+ .qdev.size = sizeof(USBHIDState),
+ .qdev.vmsd = &vmstate_usb_kbd,
+ .usb_desc = &desc_keyboard,
+ .init = usb_keyboard_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_keyboard_handle_reset,
+ .handle_control = usb_hid_handle_control,
+ .handle_data = usb_hid_handle_data,
+ .handle_destroy = usb_hid_handle_destroy,
+ },{
+ /* end of list */
+ }
+};
+
+static void usb_hid_register_devices(void)
+{
+ usb_qdev_register_many(hid_info);
+}
+device_init(usb_hid_register_devices)
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
new file mode 100644
index 0000000..3dd31ba
--- /dev/null
+++ b/hw/usb-hub.c
@@ -0,0 +1,590 @@
+/*
+ * QEMU USB HUB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "usb.h"
+#include "usb-desc.h"
+
+//#define DEBUG
+
+#define NUM_PORTS 8
+
+typedef struct USBHubPort {
+ USBPort port;
+ uint16_t wPortStatus;
+ uint16_t wPortChange;
+} USBHubPort;
+
+typedef struct USBHubState {
+ USBDevice dev;
+ USBHubPort ports[NUM_PORTS];
+} USBHubState;
+
+#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+
+#define PORT_STAT_CONNECTION 0x0001
+#define PORT_STAT_ENABLE 0x0002
+#define PORT_STAT_SUSPEND 0x0004
+#define PORT_STAT_OVERCURRENT 0x0008
+#define PORT_STAT_RESET 0x0010
+#define PORT_STAT_POWER 0x0100
+#define PORT_STAT_LOW_SPEED 0x0200
+#define PORT_STAT_HIGH_SPEED 0x0400
+#define PORT_STAT_TEST 0x0800
+#define PORT_STAT_INDICATOR 0x1000
+
+#define PORT_STAT_C_CONNECTION 0x0001
+#define PORT_STAT_C_ENABLE 0x0002
+#define PORT_STAT_C_SUSPEND 0x0004
+#define PORT_STAT_C_OVERCURRENT 0x0008
+#define PORT_STAT_C_RESET 0x0010
+
+#define PORT_CONNECTION 0
+#define PORT_ENABLE 1
+#define PORT_SUSPEND 2
+#define PORT_OVERCURRENT 3
+#define PORT_RESET 4
+#define PORT_POWER 8
+#define PORT_LOWSPEED 9
+#define PORT_HIGHSPEED 10
+#define PORT_C_CONNECTION 16
+#define PORT_C_ENABLE 17
+#define PORT_C_SUSPEND 18
+#define PORT_C_OVERCURRENT 19
+#define PORT_C_RESET 20
+#define PORT_TEST 21
+#define PORT_INDICATOR 22
+
+/* same as Linux kernel root hubs */
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT,
+ STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+ [STR_PRODUCT] = "QEMU USB Hub",
+ [STR_SERIALNUMBER] = "314159",
+};
+
+static const USBDescIface desc_iface_hub = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HUB,
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8,
+ .bInterval = 0xff,
+ },
+ }
+};
+
+static const USBDescDevice desc_device_hub = {
+ .bcdUSB = 0x0110,
+ .bDeviceClass = USB_CLASS_HUB,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0xe0,
+ .ifs = &desc_iface_hub,
+ },
+ },
+};
+
+static const USBDesc desc_hub = {
+ .id = {
+ .idVendor = 0,
+ .idProduct = 0,
+ .bcdDevice = 0x0101,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_hub,
+ .str = desc_strings,
+};
+
+static const uint8_t qemu_hub_dev_descriptor[] = {
+ 0x12, /* u8 bLength; */
+ 0x01, /* u8 bDescriptorType; Device */
+ 0x10, 0x01, /* u16 bcdUSB; v1.1 */
+
+ 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* u8 bDeviceSubClass; */
+ 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* u16 idVendor; */
+ 0x00, 0x00, /* u16 idProduct; */
+ 0x01, 0x01, /* u16 bcdDevice */
+
+ 0x03, /* u8 iManufacturer; */
+ 0x02, /* u8 iProduct; */
+ 0x01, /* u8 iSerialNumber; */
+ 0x01 /* u8 bNumConfigurations; */
+};
+
+/* XXX: patch interrupt size */
+static const uint8_t qemu_hub_config_descriptor[] = {
+
+ /* one configuration */
+ 0x09, /* u8 bLength; */
+ 0x02, /* u8 bDescriptorType; Configuration */
+ 0x19, 0x00, /* u16 wTotalLength; */
+ 0x01, /* u8 bNumInterfaces; (1) */
+ 0x01, /* u8 bConfigurationValue; */
+ 0x00, /* u8 iConfiguration; */
+ 0xe0, /* u8 bmAttributes;
+ Bit 7: must be set,
+ 6: Self-powered,
+ 5: Remote wakeup,
+ 4..0: resvd */
+ 0x00, /* u8 MaxPower; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one interface */
+ 0x09, /* u8 if_bLength; */
+ 0x04, /* u8 if_bDescriptorType; Interface */
+ 0x00, /* u8 if_bInterfaceNumber; */
+ 0x00, /* u8 if_bAlternateSetting; */
+ 0x01, /* u8 if_bNumEndpoints; */
+ 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* u8 if_bInterfaceSubClass; */
+ 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
+ 0x00, /* u8 if_iInterface; */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* u8 ep_bLength; */
+ 0x05, /* u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_hub_hub_descriptor[] =
+{
+ 0x00, /* u8 bLength; patched in later */
+ 0x29, /* u8 bDescriptorType; Hub-descriptor */
+ 0x00, /* u8 bNbrPorts; (patched later) */
+ 0x0a, /* u16 wHubCharacteristics; */
+ 0x00, /* (per-port OC, no power switching) */
+ 0x01, /* u8 bPwrOn2pwrGood; 2ms */
+ 0x00 /* u8 bHubContrCurrent; 0 mA */
+
+ /* DeviceRemovable and PortPwrCtrlMask patched in later */
+};
+
+static void usb_hub_attach(USBPort *port1)
+{
+ USBHubState *s = port1->opaque;
+ USBHubPort *port = &s->ports[port1->index];
+
+ port->wPortStatus |= PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (port->port.dev->speed == USB_SPEED_LOW) {
+ port->wPortStatus |= PORT_STAT_LOW_SPEED;
+ } else {
+ port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
+ }
+}
+
+static void usb_hub_detach(USBPort *port1)
+{
+ USBHubState *s = port1->opaque;
+ USBHubPort *port = &s->ports[port1->index];
+
+ port->wPortStatus &= ~PORT_STAT_CONNECTION;
+ port->wPortChange |= PORT_STAT_C_CONNECTION;
+ if (port->wPortStatus & PORT_STAT_ENABLE) {
+ port->wPortStatus &= ~PORT_STAT_ENABLE;
+ port->wPortChange |= PORT_STAT_C_ENABLE;
+ }
+}
+
+static void usb_hub_wakeup(USBDevice *dev)
+{
+ USBHubState *s = dev->port->opaque;
+ USBHubPort *port = &s->ports[dev->port->index];
+
+ if (port->wPortStatus & PORT_STAT_SUSPEND) {
+ port->wPortChange |= PORT_STAT_C_SUSPEND;
+ usb_wakeup(&s->dev);
+ }
+}
+
+static void usb_hub_handle_attach(USBDevice *dev)
+{
+ USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ int i;
+
+ for (i = 0; i < NUM_PORTS; i++) {
+ usb_port_location(&s->ports[i].port, dev->port, i+1);
+ }
+}
+
+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)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int ret;
+
+ ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ if (ret >= 0) {
+ return ret;
+ }
+
+ switch(request) {
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ if (value == 0 && index != 0x81) { /* clear ep halt */
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* usb specific requests */
+ case GetHubStatus:
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ ret = 4;
+ break;
+ case GetPortStatus:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ if (n >= NUM_PORTS) {
+ goto fail;
+ }
+ port = &s->ports[n];
+ data[0] = port->wPortStatus;
+ data[1] = port->wPortStatus >> 8;
+ data[2] = port->wPortChange;
+ data[3] = port->wPortChange >> 8;
+ ret = 4;
+ }
+ break;
+ case SetHubFeature:
+ case ClearHubFeature:
+ if (value == 0 || value == 1) {
+ } else {
+ goto fail;
+ }
+ ret = 0;
+ break;
+ case SetPortFeature:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+ USBDevice *dev;
+ if (n >= NUM_PORTS) {
+ goto fail;
+ }
+ port = &s->ports[n];
+ dev = port->port.dev;
+ switch(value) {
+ case PORT_SUSPEND:
+ port->wPortStatus |= PORT_STAT_SUSPEND;
+ break;
+ case PORT_RESET:
+ if (dev) {
+ usb_send_msg(dev, USB_MSG_RESET);
+ port->wPortChange |= PORT_STAT_C_RESET;
+ /* set enable bit */
+ port->wPortStatus |= PORT_STAT_ENABLE;
+ }
+ break;
+ case PORT_POWER:
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ }
+ break;
+ case ClearPortFeature:
+ {
+ unsigned int n = index - 1;
+ USBHubPort *port;
+
+ if (n >= NUM_PORTS) {
+ goto fail;
+ }
+ port = &s->ports[n];
+ switch(value) {
+ case PORT_ENABLE:
+ port->wPortStatus &= ~PORT_STAT_ENABLE;
+ break;
+ case PORT_C_ENABLE:
+ port->wPortChange &= ~PORT_STAT_C_ENABLE;
+ break;
+ case PORT_SUSPEND:
+ port->wPortStatus &= ~PORT_STAT_SUSPEND;
+ break;
+ case PORT_C_SUSPEND:
+ port->wPortChange &= ~PORT_STAT_C_SUSPEND;
+ break;
+ case PORT_C_CONNECTION:
+ port->wPortChange &= ~PORT_STAT_C_CONNECTION;
+ break;
+ case PORT_C_OVERCURRENT:
+ port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
+ break;
+ case PORT_C_RESET:
+ port->wPortChange &= ~PORT_STAT_C_RESET;
+ break;
+ default:
+ goto fail;
+ }
+ ret = 0;
+ }
+ break;
+ case GetHubDescriptor:
+ {
+ unsigned int n, limit, var_hub_size = 0;
+ memcpy(data, qemu_hub_hub_descriptor,
+ sizeof(qemu_hub_hub_descriptor));
+ data[2] = NUM_PORTS;
+
+ /* fill DeviceRemovable bits */
+ limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
+ for (n = 7; n < limit; n++) {
+ data[n] = 0x00;
+ var_hub_size++;
+ }
+
+ /* fill PortPwrCtrlMask bits */
+ limit = limit + ((NUM_PORTS + 7) / 8);
+ for (;n < limit; n++) {
+ data[n] = 0xff;
+ var_hub_size++;
+ }
+
+ ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
+ data[0] = ret;
+ break;
+ }
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int ret;
+
+ switch(p->pid) {
+ case USB_TOKEN_IN:
+ if (p->devep == 1) {
+ USBHubPort *port;
+ unsigned int status;
+ int i, n;
+ n = (NUM_PORTS + 1 + 7) / 8;
+ if (p->len == 1) { /* FreeBSD workaround */
+ n = 1;
+ } else if (n > p->len) {
+ return USB_RET_BABBLE;
+ }
+ status = 0;
+ for(i = 0; i < NUM_PORTS; i++) {
+ port = &s->ports[i];
+ if (port->wPortChange)
+ status |= (1 << (i + 1));
+ }
+ if (status != 0) {
+ for(i = 0; i < n; i++) {
+ p->data[i] = status >> (8 * i);
+ }
+ ret = n;
+ } else {
+ ret = USB_RET_NAK; /* usb11 11.13.1 */
+ }
+ } else {
+ goto fail;
+ }
+ break;
+ case USB_TOKEN_OUT:
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
+{
+ USBHubPort *port;
+ USBDevice *dev;
+ int i, ret;
+
+ for(i = 0; i < NUM_PORTS; i++) {
+ port = &s->ports[i];
+ dev = port->port.dev;
+ if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
+ ret = dev->info->handle_packet(dev, p);
+ if (ret != USB_RET_NODEV) {
+ return ret;
+ }
+ }
+ }
+ return USB_RET_NODEV;
+}
+
+static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p)
+{
+ USBHubState *s = (USBHubState *)dev;
+
+#if defined(DEBUG) && 0
+ printf("usb_hub: pid=0x%x\n", pid);
+#endif
+ if (dev->state == USB_STATE_DEFAULT &&
+ dev->addr != 0 &&
+ p->devaddr != dev->addr &&
+ (p->pid == USB_TOKEN_SETUP ||
+ p->pid == USB_TOKEN_OUT ||
+ p->pid == USB_TOKEN_IN)) {
+ /* broadcast the packet to the devices */
+ return usb_hub_broadcast_packet(s, p);
+ }
+ return usb_generic_handle_packet(dev, p);
+}
+
+static void usb_hub_handle_destroy(USBDevice *dev)
+{
+ USBHubState *s = (USBHubState *)dev;
+ int i;
+
+ for (i = 0; i < NUM_PORTS; i++) {
+ usb_unregister_port(usb_bus_from_device(dev),
+ &s->ports[i].port);
+ }
+}
+
+static USBPortOps usb_hub_port_ops = {
+ .attach = usb_hub_attach,
+ .detach = usb_hub_detach,
+ .wakeup = usb_hub_wakeup,
+};
+
+static int usb_hub_initfn(USBDevice *dev)
+{
+ USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
+ USBHubPort *port;
+ int i;
+
+ usb_desc_init(dev);
+ for (i = 0; i < NUM_PORTS; i++) {
+ port = &s->ports[i];
+ usb_register_port(usb_bus_from_device(dev),
+ &port->port, s, i, &usb_hub_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+ port->wPortStatus = PORT_STAT_POWER;
+ port->wPortChange = 0;
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_usb_hub_port = {
+ .name = "usb-hub-port",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(wPortStatus, USBHubPort),
+ VMSTATE_UINT16(wPortChange, USBHubPort),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_usb_hub = {
+ .name = "usb-hub",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_USB_DEVICE(dev, USBHubState),
+ VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0,
+ vmstate_usb_hub_port, USBHubPort),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static struct USBDeviceInfo hub_info = {
+ .product_desc = "QEMU USB Hub",
+ .qdev.name = "usb-hub",
+ .qdev.fw_name = "hub",
+ .qdev.size = sizeof(USBHubState),
+ .qdev.vmsd = &vmstate_usb_hub,
+ .usb_desc = &desc_hub,
+ .init = usb_hub_initfn,
+ .handle_packet = usb_hub_handle_packet,
+ .handle_attach = usb_hub_handle_attach,
+ .handle_reset = usb_hub_handle_reset,
+ .handle_control = usb_hub_handle_control,
+ .handle_data = usb_hub_handle_data,
+ .handle_destroy = usb_hub_handle_destroy,
+};
+
+static void usb_hub_register_devices(void)
+{
+ usb_qdev_register(&hub_info);
+}
+device_init(usb_hub_register_devices)
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
new file mode 100644
index 0000000..76f5b02
--- /dev/null
+++ b/hw/usb-msd.c
@@ -0,0 +1,621 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "usb.h"
+#include "usb-desc.h"
+#include "scsi.h"
+#include "console.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "blockdev.h"
+
+//#define DEBUG_MSD
+
+#ifdef DEBUG_MSD
+#define DPRINTF(fmt, ...) \
+do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+/* USB requests. */
+#define MassStorageReset 0xff
+#define GetMaxLun 0xfe
+
+enum USBMSDMode {
+ USB_MSDM_CBW, /* Command Block. */
+ USB_MSDM_DATAOUT, /* Tranfer data to device. */
+ USB_MSDM_DATAIN, /* Transfer data from device. */
+ USB_MSDM_CSW /* Command Status. */
+};
+
+typedef struct {
+ USBDevice dev;
+ enum USBMSDMode mode;
+ uint32_t scsi_len;
+ uint8_t *scsi_buf;
+ uint32_t usb_len;
+ uint8_t *usb_buf;
+ uint32_t data_len;
+ uint32_t residue;
+ uint32_t tag;
+ SCSIBus bus;
+ BlockConf conf;
+ SCSIDevice *scsi_dev;
+ uint32_t removable;
+ int result;
+ /* For async completion. */
+ USBPacket *packet;
+} MSDState;
+
+struct usb_msd_cbw {
+ uint32_t sig;
+ uint32_t tag;
+ uint32_t data_len;
+ uint8_t flags;
+ uint8_t lun;
+ uint8_t cmd_len;
+ uint8_t cmd[16];
+};
+
+struct usb_msd_csw {
+ uint32_t sig;
+ uint32_t tag;
+ uint32_t residue;
+ uint8_t status;
+};
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT,
+ STR_SERIALNUMBER,
+ STR_CONFIG_FULL,
+ STR_CONFIG_HIGH,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+ [STR_PRODUCT] = "QEMU USB HARDDRIVE",
+ [STR_SERIALNUMBER] = "1",
+ [STR_CONFIG_FULL] = "Full speed config (usb 1.1)",
+ [STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
+};
+
+static const USBDescIface desc_iface_full = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x50, /* Bulk */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ },{
+ .bEndpointAddress = USB_DIR_OUT | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ },
+ }
+};
+
+static const USBDescDevice desc_device_full = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_FULL,
+ .bmAttributes = 0xc0,
+ .ifs = &desc_iface_full,
+ },
+ },
+};
+
+static const USBDescIface desc_iface_high = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x50, /* Bulk */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ },{
+ .bEndpointAddress = USB_DIR_OUT | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 512,
+ },
+ }
+};
+
+static const USBDescDevice desc_device_high = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_HIGH,
+ .bmAttributes = 0xc0,
+ .ifs = &desc_iface_high,
+ },
+ },
+};
+
+static const USBDesc desc = {
+ .id = {
+ .idVendor = 0,
+ .idProduct = 0,
+ .bcdDevice = 0,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_full,
+ .high = &desc_device_high,
+ .str = desc_strings,
+};
+
+static void usb_msd_copy_data(MSDState *s)
+{
+ uint32_t len;
+ len = s->usb_len;
+ if (len > s->scsi_len)
+ len = s->scsi_len;
+ if (s->mode == USB_MSDM_DATAIN) {
+ memcpy(s->usb_buf, s->scsi_buf, len);
+ } else {
+ memcpy(s->scsi_buf, s->usb_buf, len);
+ }
+ s->usb_len -= len;
+ s->scsi_len -= len;
+ s->usb_buf += len;
+ 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->scsi_dev, s->tag);
+ } else if (s->mode == USB_MSDM_DATAOUT) {
+ s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
+ }
+ }
+}
+
+static void usb_msd_send_status(MSDState *s, USBPacket *p)
+{
+ struct usb_msd_csw csw;
+ int len;
+
+ csw.sig = cpu_to_le32(0x53425355);
+ csw.tag = cpu_to_le32(s->tag);
+ csw.residue = s->residue;
+ csw.status = s->result;
+
+ len = MIN(sizeof(csw), p->len);
+ memcpy(p->data, &csw, len);
+}
+
+static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
+ uint32_t arg)
+{
+ MSDState *s = DO_UPCAST(MSDState, dev.qdev, 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(p);
+ } else if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ }
+ return;
+ }
+ s->scsi_len = arg;
+ 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) {
+ /* Set s->packet to NULL before calling usb_packet_complete
+ because annother request may be issued before
+ usb_packet_complete returns. */
+ DPRINTF("Packet complete %p\n", p);
+ s->packet = NULL;
+ usb_packet_complete(p);
+ }
+ }
+}
+
+static void usb_msd_handle_reset(USBDevice *dev)
+{
+ MSDState *s = (MSDState *)dev;
+
+ DPRINTF("Reset\n");
+ s->mode = USB_MSDM_CBW;
+}
+
+static int usb_msd_handle_control(USBDevice *dev, 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);
+ if (ret >= 0) {
+ return ret;
+ }
+
+ ret = 0;
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ ret = 0;
+ break;
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ /* Class specific requests. */
+ case ClassInterfaceOutRequest | MassStorageReset:
+ /* Reset state ready for the next CBW. */
+ s->mode = USB_MSDM_CBW;
+ ret = 0;
+ break;
+ case ClassInterfaceRequest | GetMaxLun:
+ data[0] = 0;
+ ret = 1;
+ break;
+ default:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+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->packet = NULL;
+ s->scsi_len = 0;
+}
+
+static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
+{
+ MSDState *s = (MSDState *)dev;
+ int ret = 0;
+ struct usb_msd_cbw cbw;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+
+ switch (s->mode) {
+ case USB_MSDM_CBW:
+ if (len != 31) {
+ fprintf(stderr, "usb-msd: Bad CBW size");
+ goto fail;
+ }
+ memcpy(&cbw, data, 31);
+ if (le32_to_cpu(cbw.sig) != 0x43425355) {
+ fprintf(stderr, "usb-msd: Bad signature %08x\n",
+ le32_to_cpu(cbw.sig));
+ goto fail;
+ }
+ DPRINTF("Command on LUN %d\n", cbw.lun);
+ if (cbw.lun != 0) {
+ fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
+ goto fail;
+ }
+ s->tag = le32_to_cpu(cbw.tag);
+ s->data_len = le32_to_cpu(cbw.data_len);
+ if (s->data_len == 0) {
+ s->mode = USB_MSDM_CSW;
+ } else if (cbw.flags & 0x80) {
+ s->mode = USB_MSDM_DATAIN;
+ } else {
+ s->mode = USB_MSDM_DATAOUT;
+ }
+ DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
+ s->tag, cbw.flags, cbw.cmd_len, s->data_len);
+ s->residue = 0;
+ s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+ /* ??? 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);
+ } else if (s->mode == USB_MSDM_DATAOUT) {
+ s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
+ }
+ }
+ ret = len;
+ break;
+
+ case USB_MSDM_DATAOUT:
+ DPRINTF("Data out %d/%d\n", len, s->data_len);
+ if (len > s->data_len)
+ goto fail;
+
+ s->usb_buf = data;
+ s->usb_len = len;
+ if (s->scsi_len) {
+ usb_msd_copy_data(s);
+ }
+ if (s->residue && s->usb_len) {
+ s->data_len -= s->usb_len;
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ s->usb_len = 0;
+ }
+ 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 {
+ ret = len;
+ }
+ break;
+
+ default:
+ DPRINTF("Unexpected write (len %d)\n", len);
+ goto fail;
+ }
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+
+ switch (s->mode) {
+ case USB_MSDM_DATAOUT:
+ 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;
+
+ case USB_MSDM_CSW:
+ DPRINTF("Command status %d tag 0x%x, len %d\n",
+ s->result, s->tag, len);
+ if (len < 13)
+ goto fail;
+
+ usb_msd_send_status(s, p);
+ s->mode = USB_MSDM_CBW;
+ ret = 13;
+ break;
+
+ case USB_MSDM_DATAIN:
+ DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len);
+ if (len > s->data_len)
+ len = s->data_len;
+ s->usb_buf = data;
+ s->usb_len = len;
+ if (s->scsi_len) {
+ usb_msd_copy_data(s);
+ }
+ if (s->residue && s->usb_len) {
+ s->data_len -= s->usb_len;
+ memset(s->usb_buf, 0, s->usb_len);
+ if (s->data_len == 0)
+ s->mode = USB_MSDM_CSW;
+ s->usb_len = 0;
+ }
+ 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 {
+ ret = len;
+ }
+ break;
+
+ default:
+ DPRINTF("Unexpected read (len %d)\n", len);
+ goto fail;
+ }
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_msd_password_cb(void *opaque, int err)
+{
+ MSDState *s = opaque;
+
+ if (!err)
+ usb_device_attach(&s->dev);
+ else
+ qdev_unplug(&s->dev.qdev);
+}
+
+static int usb_msd_initfn(USBDevice *dev)
+{
+ MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ BlockDriverState *bs = s->conf.bs;
+ DriveInfo *dinfo;
+
+ if (!bs) {
+ error_report("usb-msd: drive property not set");
+ return -1;
+ }
+
+ /*
+ * Hack alert: this pretends to be a block device, but it's really
+ * a SCSI bus that can serve only a single device, which it
+ * creates automatically. But first it needs to detach from its
+ * blockdev, or else scsi_bus_legacy_add_drive() dies when it
+ * attaches again.
+ *
+ * The hack is probably a bad idea.
+ */
+ bdrv_detach(bs, &s->dev.qdev);
+ s->conf.bs = NULL;
+
+ dinfo = drive_get_by_blockdev(bs);
+ if (dinfo && dinfo->serial) {
+ usb_desc_set_string(dev, STR_SERIALNUMBER, dinfo->serial);
+ }
+
+ usb_desc_init(dev);
+ scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
+ s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
+ if (!s->scsi_dev) {
+ return -1;
+ }
+ s->bus.qbus.allow_hotplug = 0;
+ usb_msd_handle_reset(dev);
+
+ if (bdrv_key_required(bs)) {
+ if (cur_mon) {
+ monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
+ s->dev.auto_attach = 0;
+ } else {
+ autostart = 0;
+ }
+ }
+
+ add_boot_device_path(s->conf.bootindex, &dev->qdev, "/disk@0,0");
+ return 0;
+}
+
+static USBDevice *usb_msd_init(const char *filename)
+{
+ static int nr=0;
+ char id[8];
+ QemuOpts *opts;
+ DriveInfo *dinfo;
+ USBDevice *dev;
+ const char *p1;
+ char fmt[32];
+
+ /* parse -usbdevice disk: syntax into drive opts */
+ snprintf(id, sizeof(id), "usb%d", nr++);
+ opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
+
+ p1 = strchr(filename, ':');
+ if (p1++) {
+ const char *p2;
+
+ if (strstart(filename, "format=", &p2)) {
+ int len = MIN(p1 - p2, sizeof(fmt));
+ pstrcpy(fmt, len, p2);
+ qemu_opt_set(opts, "format", fmt);
+ } else if (*filename != ':') {
+ printf("unrecognized USB mass-storage option %s\n", filename);
+ return NULL;
+ }
+ filename = p1;
+ }
+ if (!*filename) {
+ printf("block device specification needed\n");
+ return NULL;
+ }
+ qemu_opt_set(opts, "file", filename);
+ qemu_opt_set(opts, "if", "none");
+
+ /* create host drive */
+ dinfo = drive_init(opts, 0);
+ if (!dinfo) {
+ qemu_opts_del(opts);
+ return NULL;
+ }
+
+ /* create guest device */
+ dev = usb_create(NULL /* FIXME */, "usb-storage");
+ if (!dev) {
+ return NULL;
+ }
+ if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
+ qdev_free(&dev->qdev);
+ return NULL;
+ }
+ if (qdev_init(&dev->qdev) < 0)
+ return NULL;
+
+ return dev;
+}
+
+static struct USBDeviceInfo msd_info = {
+ .product_desc = "QEMU USB MSD",
+ .qdev.name = "usb-storage",
+ .qdev.fw_name = "storage",
+ .qdev.size = sizeof(MSDState),
+ .usb_desc = &desc,
+ .init = usb_msd_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_attach = usb_desc_attach,
+ .handle_reset = usb_msd_handle_reset,
+ .handle_control = usb_msd_handle_control,
+ .handle_data = usb_msd_handle_data,
+ .usbdevice_name = "disk",
+ .usbdevice_init = usb_msd_init,
+ .qdev.props = (Property[]) {
+ DEFINE_BLOCK_PROPERTIES(MSDState, conf),
+ DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void usb_msd_register_devices(void)
+{
+ usb_qdev_register(&msd_info);
+}
+device_init(usb_msd_register_devices)
diff --git a/hw/usb-musb.c b/hw/usb-musb.c
new file mode 100644
index 0000000..782cfa2
--- /dev/null
+++ b/hw/usb-musb.c
@@ -0,0 +1,1495 @@
+/*
+ * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics,
+ * USB2.0 OTG compliant core used in various chips.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ *
+ * Only host-mode and non-DMA accesses are currently supported.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "irq.h"
+#include "hw.h"
+
+/* Common USB registers */
+#define MUSB_HDRC_FADDR 0x00 /* 8-bit */
+#define MUSB_HDRC_POWER 0x01 /* 8-bit */
+
+#define MUSB_HDRC_INTRTX 0x02 /* 16-bit */
+#define MUSB_HDRC_INTRRX 0x04
+#define MUSB_HDRC_INTRTXE 0x06
+#define MUSB_HDRC_INTRRXE 0x08
+#define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */
+#define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */
+#define MUSB_HDRC_FRAME 0x0c /* 16-bit */
+#define MUSB_HDRC_INDEX 0x0e /* 8 bit */
+#define MUSB_HDRC_TESTMODE 0x0f /* 8 bit */
+
+/* Per-EP registers in indexed mode */
+#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */
+
+/* EP FIFOs */
+#define MUSB_HDRC_FIFO 0x20
+
+/* Additional Control Registers */
+#define MUSB_HDRC_DEVCTL 0x60 /* 8 bit */
+
+/* These are indexed */
+#define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */
+#define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */
+#define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */
+#define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 */
+
+/* Some more registers */
+#define MUSB_HDRC_VCTRL 0x68 /* 8 bit */
+#define MUSB_HDRC_HWVERS 0x6c /* 8 bit */
+
+/* Added in HDRC 1.9(?) & MHDRC 1.4 */
+/* ULPI pass-through */
+#define MUSB_HDRC_ULPI_VBUSCTL 0x70
+#define MUSB_HDRC_ULPI_REGDATA 0x74
+#define MUSB_HDRC_ULPI_REGADDR 0x75
+#define MUSB_HDRC_ULPI_REGCTL 0x76
+
+/* Extended config & PHY control */
+#define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */
+#define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */
+#define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */
+#define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */
+#define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */
+#define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */
+#define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us */
+
+/* Per-EP BUSCTL registers */
+#define MUSB_HDRC_BUSCTL 0x80
+
+/* Per-EP registers in flat mode */
+#define MUSB_HDRC_EP 0x100
+
+/* offsets to registers in flat model */
+#define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */
+#define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */
+#define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */
+#define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */
+#define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */
+#define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */
+#define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */
+#define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */
+#define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */
+#define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */
+#define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */
+#define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */
+#define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */
+#define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */
+#define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 */
+
+/* "Bus control" registers */
+#define MUSB_HDRC_TXFUNCADDR 0x00
+#define MUSB_HDRC_TXHUBADDR 0x02
+#define MUSB_HDRC_TXHUBPORT 0x03
+
+#define MUSB_HDRC_RXFUNCADDR 0x04
+#define MUSB_HDRC_RXHUBADDR 0x06
+#define MUSB_HDRC_RXHUBPORT 0x07
+
+/*
+ * MUSBHDRC Register bit masks
+ */
+
+/* POWER */
+#define MGC_M_POWER_ISOUPDATE 0x80
+#define MGC_M_POWER_SOFTCONN 0x40
+#define MGC_M_POWER_HSENAB 0x20
+#define MGC_M_POWER_HSMODE 0x10
+#define MGC_M_POWER_RESET 0x08
+#define MGC_M_POWER_RESUME 0x04
+#define MGC_M_POWER_SUSPENDM 0x02
+#define MGC_M_POWER_ENSUSPEND 0x01
+
+/* INTRUSB */
+#define MGC_M_INTR_SUSPEND 0x01
+#define MGC_M_INTR_RESUME 0x02
+#define MGC_M_INTR_RESET 0x04
+#define MGC_M_INTR_BABBLE 0x04
+#define MGC_M_INTR_SOF 0x08
+#define MGC_M_INTR_CONNECT 0x10
+#define MGC_M_INTR_DISCONNECT 0x20
+#define MGC_M_INTR_SESSREQ 0x40
+#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */
+#define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT */
+
+/* DEVCTL */
+#define MGC_M_DEVCTL_BDEVICE 0x80
+#define MGC_M_DEVCTL_FSDEV 0x40
+#define MGC_M_DEVCTL_LSDEV 0x20
+#define MGC_M_DEVCTL_VBUS 0x18
+#define MGC_S_DEVCTL_VBUS 3
+#define MGC_M_DEVCTL_HM 0x04
+#define MGC_M_DEVCTL_HR 0x02
+#define MGC_M_DEVCTL_SESSION 0x01
+
+/* TESTMODE */
+#define MGC_M_TEST_FORCE_HOST 0x80
+#define MGC_M_TEST_FIFO_ACCESS 0x40
+#define MGC_M_TEST_FORCE_FS 0x20
+#define MGC_M_TEST_FORCE_HS 0x10
+#define MGC_M_TEST_PACKET 0x08
+#define MGC_M_TEST_K 0x04
+#define MGC_M_TEST_J 0x02
+#define MGC_M_TEST_SE0_NAK 0x01
+
+/* CSR0 */
+#define MGC_M_CSR0_FLUSHFIFO 0x0100
+#define MGC_M_CSR0_TXPKTRDY 0x0002
+#define MGC_M_CSR0_RXPKTRDY 0x0001
+
+/* CSR0 in Peripheral mode */
+#define MGC_M_CSR0_P_SVDSETUPEND 0x0080
+#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040
+#define MGC_M_CSR0_P_SENDSTALL 0x0020
+#define MGC_M_CSR0_P_SETUPEND 0x0010
+#define MGC_M_CSR0_P_DATAEND 0x0008
+#define MGC_M_CSR0_P_SENTSTALL 0x0004
+
+/* CSR0 in Host mode */
+#define MGC_M_CSR0_H_NO_PING 0x0800
+#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */
+#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */
+#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080
+#define MGC_M_CSR0_H_STATUSPKT 0x0040
+#define MGC_M_CSR0_H_REQPKT 0x0020
+#define MGC_M_CSR0_H_ERROR 0x0010
+#define MGC_M_CSR0_H_SETUPPKT 0x0008
+#define MGC_M_CSR0_H_RXSTALL 0x0004
+
+/* CONFIGDATA */
+#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */
+#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */
+#define MGC_M_CONFIGDATA_BIGENDIAN 0x20
+#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */
+#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */
+#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */
+#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */
+#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b */
+
+/* TXCSR in Peripheral and Host mode */
+#define MGC_M_TXCSR_AUTOSET 0x8000
+#define MGC_M_TXCSR_ISO 0x4000
+#define MGC_M_TXCSR_MODE 0x2000
+#define MGC_M_TXCSR_DMAENAB 0x1000
+#define MGC_M_TXCSR_FRCDATATOG 0x0800
+#define MGC_M_TXCSR_DMAMODE 0x0400
+#define MGC_M_TXCSR_CLRDATATOG 0x0040
+#define MGC_M_TXCSR_FLUSHFIFO 0x0008
+#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002
+#define MGC_M_TXCSR_TXPKTRDY 0x0001
+
+/* TXCSR in Peripheral mode */
+#define MGC_M_TXCSR_P_INCOMPTX 0x0080
+#define MGC_M_TXCSR_P_SENTSTALL 0x0020
+#define MGC_M_TXCSR_P_SENDSTALL 0x0010
+#define MGC_M_TXCSR_P_UNDERRUN 0x0004
+
+/* TXCSR in Host mode */
+#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200
+#define MGC_M_TXCSR_H_DATATOGGLE 0x0100
+#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080
+#define MGC_M_TXCSR_H_RXSTALL 0x0020
+#define MGC_M_TXCSR_H_ERROR 0x0004
+
+/* RXCSR in Peripheral and Host mode */
+#define MGC_M_RXCSR_AUTOCLEAR 0x8000
+#define MGC_M_RXCSR_DMAENAB 0x2000
+#define MGC_M_RXCSR_DISNYET 0x1000
+#define MGC_M_RXCSR_DMAMODE 0x0800
+#define MGC_M_RXCSR_INCOMPRX 0x0100
+#define MGC_M_RXCSR_CLRDATATOG 0x0080
+#define MGC_M_RXCSR_FLUSHFIFO 0x0010
+#define MGC_M_RXCSR_DATAERROR 0x0008
+#define MGC_M_RXCSR_FIFOFULL 0x0002
+#define MGC_M_RXCSR_RXPKTRDY 0x0001
+
+/* RXCSR in Peripheral mode */
+#define MGC_M_RXCSR_P_ISO 0x4000
+#define MGC_M_RXCSR_P_SENTSTALL 0x0040
+#define MGC_M_RXCSR_P_SENDSTALL 0x0020
+#define MGC_M_RXCSR_P_OVERRUN 0x0004
+
+/* RXCSR in Host mode */
+#define MGC_M_RXCSR_H_AUTOREQ 0x4000
+#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400
+#define MGC_M_RXCSR_H_DATATOGGLE 0x0200
+#define MGC_M_RXCSR_H_RXSTALL 0x0040
+#define MGC_M_RXCSR_H_REQPKT 0x0020
+#define MGC_M_RXCSR_H_ERROR 0x0004
+
+/* HUBADDR */
+#define MGC_M_HUBADDR_MULTI_TT 0x80
+
+/* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */
+#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02
+#define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01
+#define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08
+#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04
+#define MGC_M_ULPI_REGCTL_COMPLETE 0x02
+#define MGC_M_ULPI_REGCTL_REG 0x01
+
+/* #define MUSB_DEBUG */
+
+#ifdef MUSB_DEBUG
+#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \
+ __LINE__, ##__VA_ARGS__)
+#else
+#define TRACE(...)
+#endif
+
+
+static void musb_attach(USBPort *port);
+static void musb_detach(USBPort *port);
+
+static USBPortOps musb_port_ops = {
+ .attach = musb_attach,
+ .detach = musb_detach,
+};
+
+typedef struct {
+ uint16_t faddr[2];
+ uint8_t haddr[2];
+ uint8_t hport[2];
+ uint16_t csr[2];
+ uint16_t maxp[2];
+ uint16_t rxcount;
+ uint8_t type[2];
+ uint8_t interval[2];
+ uint8_t config;
+ uint8_t fifosize;
+ int timeout[2]; /* Always in microframes */
+
+ uint8_t *buf[2];
+ int fifolen[2];
+ int fifostart[2];
+ int fifoaddr[2];
+ USBPacket packey[2];
+ int status[2];
+ int ext_size[2];
+
+ /* For callbacks' use */
+ int epnum;
+ int interrupt[2];
+ MUSBState *musb;
+ USBCallback *delayed_cb[2];
+ QEMUTimer *intv_timer[2];
+} MUSBEndPoint;
+
+struct MUSBState {
+ qemu_irq *irqs;
+ USBBus bus;
+ USBPort port;
+
+ int idx;
+ uint8_t devctl;
+ uint8_t power;
+ uint8_t faddr;
+
+ uint8_t intr;
+ uint8_t mask;
+ uint16_t tx_intr;
+ uint16_t tx_mask;
+ uint16_t rx_intr;
+ uint16_t rx_mask;
+
+ int setup_len;
+ int session;
+
+ uint8_t buf[0x8000];
+
+ /* Duplicating the world since 2008!... probably we should have 32
+ * logical, single endpoints instead. */
+ MUSBEndPoint ep[16];
+} *musb_init(qemu_irq *irqs)
+{
+ MUSBState *s = qemu_mallocz(sizeof(*s));
+ int i;
+
+ s->irqs = irqs;
+
+ s->faddr = 0x00;
+ s->power = MGC_M_POWER_HSENAB;
+ s->tx_intr = 0x0000;
+ s->rx_intr = 0x0000;
+ s->tx_mask = 0xffff;
+ s->rx_mask = 0xffff;
+ s->intr = 0x00;
+ s->mask = 0x06;
+ s->idx = 0;
+
+ /* TODO: _DW */
+ s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO;
+ for (i = 0; i < 16; i ++) {
+ s->ep[i].fifosize = 64;
+ s->ep[i].maxp[0] = 0x40;
+ s->ep[i].maxp[1] = 0x40;
+ s->ep[i].musb = s;
+ s->ep[i].epnum = i;
+ }
+
+ usb_bus_new(&s->bus, NULL /* FIXME */);
+ usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+ usb_port_location(&s->port, NULL, 1);
+
+ return s;
+}
+
+static void musb_vbus_set(MUSBState *s, int level)
+{
+ if (level)
+ s->devctl |= 3 << MGC_S_DEVCTL_VBUS;
+ else
+ s->devctl &= ~MGC_M_DEVCTL_VBUS;
+
+ qemu_set_irq(s->irqs[musb_set_vbus], level);
+}
+
+static void musb_intr_set(MUSBState *s, int line, int level)
+{
+ if (!level) {
+ s->intr &= ~(1 << line);
+ qemu_irq_lower(s->irqs[line]);
+ } else if (s->mask & (1 << line)) {
+ s->intr |= 1 << line;
+ qemu_irq_raise(s->irqs[line]);
+ }
+}
+
+static void musb_tx_intr_set(MUSBState *s, int line, int level)
+{
+ if (!level) {
+ s->tx_intr &= ~(1 << line);
+ if (!s->tx_intr)
+ qemu_irq_lower(s->irqs[musb_irq_tx]);
+ } else if (s->tx_mask & (1 << line)) {
+ s->tx_intr |= 1 << line;
+ qemu_irq_raise(s->irqs[musb_irq_tx]);
+ }
+}
+
+static void musb_rx_intr_set(MUSBState *s, int line, int level)
+{
+ if (line) {
+ if (!level) {
+ s->rx_intr &= ~(1 << line);
+ if (!s->rx_intr)
+ qemu_irq_lower(s->irqs[musb_irq_rx]);
+ } else if (s->rx_mask & (1 << line)) {
+ s->rx_intr |= 1 << line;
+ qemu_irq_raise(s->irqs[musb_irq_rx]);
+ }
+ } else
+ musb_tx_intr_set(s, line, level);
+}
+
+uint32_t musb_core_intr_get(MUSBState *s)
+{
+ return (s->rx_intr << 15) | s->tx_intr;
+}
+
+void musb_core_intr_clear(MUSBState *s, uint32_t mask)
+{
+ if (s->rx_intr) {
+ s->rx_intr &= mask >> 15;
+ if (!s->rx_intr)
+ qemu_irq_lower(s->irqs[musb_irq_rx]);
+ }
+
+ if (s->tx_intr) {
+ s->tx_intr &= mask & 0xffff;
+ if (!s->tx_intr)
+ qemu_irq_lower(s->irqs[musb_irq_tx]);
+ }
+}
+
+void musb_set_size(MUSBState *s, int epnum, int size, int is_tx)
+{
+ s->ep[epnum].ext_size[!is_tx] = size;
+ s->ep[epnum].fifostart[0] = 0;
+ s->ep[epnum].fifostart[1] = 0;
+ s->ep[epnum].fifolen[0] = 0;
+ s->ep[epnum].fifolen[1] = 0;
+}
+
+static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess)
+{
+ int detect_prev = prev_dev && prev_sess;
+ int detect = !!s->port.dev && s->session;
+
+ if (detect && !detect_prev) {
+ /* Let's skip the ID pin sense and VBUS sense formalities and
+ * and signal a successful SRP directly. This should work at least
+ * for the Linux driver stack. */
+ musb_intr_set(s, musb_irq_connect, 1);
+
+ if (s->port.dev->speed == USB_SPEED_LOW) {
+ s->devctl &= ~MGC_M_DEVCTL_FSDEV;
+ s->devctl |= MGC_M_DEVCTL_LSDEV;
+ } else {
+ s->devctl |= MGC_M_DEVCTL_FSDEV;
+ s->devctl &= ~MGC_M_DEVCTL_LSDEV;
+ }
+
+ /* A-mode? */
+ s->devctl &= ~MGC_M_DEVCTL_BDEVICE;
+
+ /* Host-mode bit? */
+ s->devctl |= MGC_M_DEVCTL_HM;
+#if 1
+ musb_vbus_set(s, 1);
+#endif
+ } else if (!detect && detect_prev) {
+#if 1
+ musb_vbus_set(s, 0);
+#endif
+ }
+}
+
+/* Attach or detach a device on our only port. */
+static void musb_attach(USBPort *port)
+{
+ MUSBState *s = (MUSBState *) port->opaque;
+
+ musb_intr_set(s, musb_irq_vbus_request, 1);
+ musb_session_update(s, 0, s->session);
+}
+
+static void musb_detach(USBPort *port)
+{
+ MUSBState *s = (MUSBState *) port->opaque;
+
+ musb_intr_set(s, musb_irq_disconnect, 1);
+ musb_session_update(s, 1, s->session);
+}
+
+static inline void musb_cb_tick0(void *opaque)
+{
+ MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+
+ ep->delayed_cb[0](&ep->packey[0], opaque);
+}
+
+static inline void musb_cb_tick1(void *opaque)
+{
+ MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+
+ ep->delayed_cb[1](&ep->packey[1], opaque);
+}
+
+#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0)
+
+static inline void musb_schedule_cb(USBPacket *packey, void *opaque, int dir)
+{
+ MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+ int timeout = 0;
+
+ if (ep->status[dir] == USB_RET_NAK)
+ timeout = ep->timeout[dir];
+ else if (ep->interrupt[dir])
+ timeout = 8;
+ else
+ return musb_cb_tick(opaque);
+
+ if (!ep->intv_timer[dir])
+ ep->intv_timer[dir] = qemu_new_timer(vm_clock, musb_cb_tick, opaque);
+
+ qemu_mod_timer(ep->intv_timer[dir], qemu_get_clock(vm_clock) +
+ muldiv64(timeout, get_ticks_per_sec(), 8000));
+}
+
+static void musb_schedule0_cb(USBPacket *packey, void *opaque)
+{
+ return musb_schedule_cb(packey, opaque, 0);
+}
+
+static void musb_schedule1_cb(USBPacket *packey, void *opaque)
+{
+ return musb_schedule_cb(packey, opaque, 1);
+}
+
+static int musb_timeout(int ttype, int speed, int val)
+{
+#if 1
+ return val << 3;
+#endif
+
+ switch (ttype) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ if (val < 2)
+ return 0;
+ else if (speed == USB_SPEED_HIGH)
+ return 1 << (val - 1);
+ else
+ return 8 << (val - 1);
+
+ case USB_ENDPOINT_XFER_INT:
+ if (speed == USB_SPEED_HIGH)
+ if (val < 2)
+ return 0;
+ else
+ return 1 << (val - 1);
+ else
+ return val << 3;
+
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_ISOC:
+ if (val < 2)
+ return 0;
+ else if (speed == USB_SPEED_HIGH)
+ return 1 << (val - 1);
+ else
+ return 8 << (val - 1);
+ /* TODO: what with low-speed Bulk and Isochronous? */
+ }
+
+ hw_error("bad interval\n");
+}
+
+static inline void musb_packet(MUSBState *s, MUSBEndPoint *ep,
+ int epnum, int pid, int len, USBCallback cb, int dir)
+{
+ int ret;
+ int idx = epnum && dir;
+ int ttype;
+
+ /* ep->type[0,1] contains:
+ * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow)
+ * in bits 5:4 the transfer type (BULK / INT)
+ * in bits 3:0 the EP num
+ */
+ ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0;
+
+ ep->timeout[dir] = musb_timeout(ttype,
+ ep->type[idx] >> 6, ep->interval[idx]);
+ ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT;
+ ep->delayed_cb[dir] = cb;
+ cb = dir ? musb_schedule1_cb : musb_schedule0_cb;
+
+ ep->packey[dir].pid = pid;
+ /* A wild guess on the FADDR semantics... */
+ ep->packey[dir].devaddr = ep->faddr[idx];
+ ep->packey[dir].devep = ep->type[idx] & 0xf;
+ ep->packey[dir].data = (void *) ep->buf[idx];
+ ep->packey[dir].len = len;
+ ep->packey[dir].complete_cb = cb;
+ ep->packey[dir].complete_opaque = ep;
+
+ if (s->port.dev)
+ ret = s->port.dev->info->handle_packet(s->port.dev, &ep->packey[dir]);
+ else
+ ret = USB_RET_NODEV;
+
+ if (ret == USB_RET_ASYNC) {
+ ep->status[dir] = len;
+ return;
+ }
+
+ ep->status[dir] = ret;
+ usb_packet_complete(&ep->packey[dir]);
+}
+
+static void musb_tx_packet_complete(USBPacket *packey, void *opaque)
+{
+ /* Unfortunately we can't use packey->devep because that's the remote
+ * endpoint number and may be different than our local. */
+ MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+ int epnum = ep->epnum;
+ MUSBState *s = ep->musb;
+
+ ep->fifostart[0] = 0;
+ ep->fifolen[0] = 0;
+#ifdef CLEAR_NAK
+ if (ep->status[0] != USB_RET_NAK) {
+#endif
+ if (epnum)
+ ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY);
+ else
+ ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY;
+#ifdef CLEAR_NAK
+ }
+#endif
+
+ /* Clear all of the error bits first */
+ if (epnum)
+ ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL |
+ MGC_M_TXCSR_H_NAKTIMEOUT);
+ else
+ ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
+ MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
+
+ if (ep->status[0] == USB_RET_STALL) {
+ /* Command not supported by target! */
+ ep->status[0] = 0;
+
+ if (epnum)
+ ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL;
+ else
+ ep->csr[0] |= MGC_M_CSR0_H_RXSTALL;
+ }
+
+ if (ep->status[0] == USB_RET_NAK) {
+ ep->status[0] = 0;
+
+ /* NAK timeouts are only generated in Bulk transfers and
+ * Data-errors in Isochronous. */
+ if (ep->interrupt[0]) {
+ return;
+ }
+
+ if (epnum)
+ ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT;
+ else
+ ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT;
+ }
+
+ if (ep->status[0] < 0) {
+ if (ep->status[0] == USB_RET_BABBLE)
+ musb_intr_set(s, musb_irq_rst_babble, 1);
+
+ /* Pretend we've tried three times already and failed (in
+ * case of USB_TOKEN_SETUP). */
+ if (epnum)
+ ep->csr[0] |= MGC_M_TXCSR_H_ERROR;
+ else
+ ep->csr[0] |= MGC_M_CSR0_H_ERROR;
+
+ musb_tx_intr_set(s, epnum, 1);
+ return;
+ }
+ /* TODO: check len for over/underruns of an OUT packet? */
+
+#ifdef SETUPLEN_HACK
+ if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP)
+ s->setup_len = ep->packey[0].data[6];
+#endif
+
+ /* In DMA mode: if no error, assert DMA request for this EP,
+ * and skip the interrupt. */
+ musb_tx_intr_set(s, epnum, 1);
+}
+
+static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
+{
+ /* Unfortunately we can't use packey->devep because that's the remote
+ * endpoint number and may be different than our local. */
+ MUSBEndPoint *ep = (MUSBEndPoint *) opaque;
+ int epnum = ep->epnum;
+ MUSBState *s = ep->musb;
+
+ ep->fifostart[1] = 0;
+ ep->fifolen[1] = 0;
+
+#ifdef CLEAR_NAK
+ if (ep->status[1] != USB_RET_NAK) {
+#endif
+ ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
+ if (!epnum)
+ ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;
+#ifdef CLEAR_NAK
+ }
+#endif
+
+ /* Clear all of the imaginable error bits first */
+ ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL |
+ MGC_M_RXCSR_DATAERROR);
+ if (!epnum)
+ ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
+ MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
+
+ if (ep->status[1] == USB_RET_STALL) {
+ ep->status[1] = 0;
+ packey->len = 0;
+
+ ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
+ if (!epnum)
+ ep->csr[0] |= MGC_M_CSR0_H_RXSTALL;
+ }
+
+ if (ep->status[1] == USB_RET_NAK) {
+ ep->status[1] = 0;
+
+ /* NAK timeouts are only generated in Bulk transfers and
+ * Data-errors in Isochronous. */
+ if (ep->interrupt[1])
+ return musb_packet(s, ep, epnum, USB_TOKEN_IN,
+ packey->len, musb_rx_packet_complete, 1);
+
+ ep->csr[1] |= MGC_M_RXCSR_DATAERROR;
+ if (!epnum)
+ ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT;
+ }
+
+ if (ep->status[1] < 0) {
+ if (ep->status[1] == USB_RET_BABBLE) {
+ musb_intr_set(s, musb_irq_rst_babble, 1);
+ return;
+ }
+
+ /* Pretend we've tried three times already and failed (in
+ * case of a control transfer). */
+ ep->csr[1] |= MGC_M_RXCSR_H_ERROR;
+ if (!epnum)
+ ep->csr[0] |= MGC_M_CSR0_H_ERROR;
+
+ musb_rx_intr_set(s, epnum, 1);
+ return;
+ }
+ /* TODO: check len for over/underruns of an OUT packet? */
+ /* TODO: perhaps make use of e->ext_size[1] here. */
+
+ packey->len = ep->status[1];
+
+ if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
+ ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
+ if (!epnum)
+ ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
+
+ ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */
+ /* In DMA mode: assert DMA request for this EP */
+ }
+
+ /* Only if DMA has not been asserted */
+ musb_rx_intr_set(s, epnum, 1);
+}
+
+static void musb_tx_rdy(MUSBState *s, int epnum)
+{
+ MUSBEndPoint *ep = s->ep + epnum;
+ int pid;
+ int total, valid = 0;
+ TRACE("start %d, len %d", ep->fifostart[0], ep->fifolen[0] );
+ ep->fifostart[0] += ep->fifolen[0];
+ ep->fifolen[0] = 0;
+
+ /* XXX: how's the total size of the packet retrieved exactly in
+ * the generic case? */
+ total = ep->maxp[0] & 0x3ff;
+
+ if (ep->ext_size[0]) {
+ total = ep->ext_size[0];
+ ep->ext_size[0] = 0;
+ valid = 1;
+ }
+
+ /* If the packet is not fully ready yet, wait for a next segment. */
+ if (epnum && (ep->fifostart[0]) < total)
+ return;
+
+ if (!valid)
+ total = ep->fifostart[0];
+
+ pid = USB_TOKEN_OUT;
+ if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) {
+ pid = USB_TOKEN_SETUP;
+ if (total != 8) {
+ TRACE("illegal SETUPPKT length of %i bytes", total);
+ }
+ /* Controller should retry SETUP packets three times on errors
+ * but it doesn't make sense for us to do that. */
+ }
+
+ return musb_packet(s, ep, epnum, pid,
+ total, musb_tx_packet_complete, 0);
+}
+
+static void musb_rx_req(MUSBState *s, int epnum)
+{
+ MUSBEndPoint *ep = s->ep + epnum;
+ int total;
+
+ /* If we already have a packet, which didn't fit into the
+ * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */
+ if (ep->packey[1].pid == USB_TOKEN_IN && ep->status[1] >= 0 &&
+ (ep->fifostart[1]) + ep->rxcount <
+ ep->packey[1].len) {
+ TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount );
+ ep->fifostart[1] += ep->rxcount;
+ ep->fifolen[1] = 0;
+
+ ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1]),
+ ep->maxp[1]);
+
+ ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT;
+ if (!epnum)
+ ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT;
+
+ /* Clear all of the error bits first */
+ ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL |
+ MGC_M_RXCSR_DATAERROR);
+ if (!epnum)
+ ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL |
+ MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING);
+
+ ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
+ if (!epnum)
+ ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
+ musb_rx_intr_set(s, epnum, 1);
+ return;
+ }
+
+ /* The driver sets maxp[1] to 64 or less because it knows the hardware
+ * FIFO is this deep. Bigger packets get split in
+ * usb_generic_handle_packet but we can also do the splitting locally
+ * for performance. It turns out we can also have a bigger FIFO and
+ * ignore the limit set in ep->maxp[1]. The Linux MUSB driver deals
+ * OK with single packets of even 32KB and we avoid splitting, however
+ * usb_msd.c sometimes sends a packet bigger than what Linux expects
+ * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN. Splitting
+ * hides this overrun from Linux. Up to 4096 everything is fine
+ * though. Currently this is disabled.
+ *
+ * XXX: mind ep->fifosize. */
+ total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf));
+
+#ifdef SETUPLEN_HACK
+ /* Why should *we* do that instead of Linux? */
+ if (!epnum) {
+ if (ep->packey[0].devaddr == 2)
+ total = MIN(s->setup_len, 8);
+ else
+ total = MIN(s->setup_len, 64);
+ s->setup_len -= total;
+ }
+#endif
+
+ return musb_packet(s, ep, epnum, USB_TOKEN_IN,
+ total, musb_rx_packet_complete, 1);
+}
+
+static uint8_t musb_read_fifo(MUSBEndPoint *ep)
+{
+ uint8_t value;
+ if (ep->fifolen[1] >= 64) {
+ /* We have a FIFO underrun */
+ TRACE("EP%d FIFO is now empty, stop reading", ep->epnum);
+ return 0x00000000;
+ }
+ /* In DMA mode clear RXPKTRDY and set REQPKT automatically
+ * (if AUTOREQ is set) */
+
+ ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL;
+ value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++];
+ TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] );
+ return value;
+}
+
+static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value)
+{
+ TRACE("EP%d = %02x", ep->epnum, value);
+ if (ep->fifolen[0] >= 64) {
+ /* We have a FIFO overrun */
+ TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum);
+ return;
+ }
+
+ ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value;
+ ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY;
+}
+
+static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir)
+{
+ if (ep->intv_timer[dir])
+ qemu_del_timer(ep->intv_timer[dir]);
+}
+
+/* Bus control */
+static uint8_t musb_busctl_readb(void *opaque, int ep, int addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ /* For USB2.0 HS hubs only */
+ case MUSB_HDRC_TXHUBADDR:
+ return s->ep[ep].haddr[0];
+ case MUSB_HDRC_TXHUBPORT:
+ return s->ep[ep].hport[0];
+ case MUSB_HDRC_RXHUBADDR:
+ return s->ep[ep].haddr[1];
+ case MUSB_HDRC_RXHUBPORT:
+ return s->ep[ep].hport[1];
+
+ default:
+ TRACE("unknown register 0x%02x", addr);
+ return 0x00;
+ };
+}
+
+static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ case MUSB_HDRC_TXFUNCADDR:
+ s->ep[ep].faddr[0] = value;
+ break;
+ case MUSB_HDRC_RXFUNCADDR:
+ s->ep[ep].faddr[1] = value;
+ break;
+ case MUSB_HDRC_TXHUBADDR:
+ s->ep[ep].haddr[0] = value;
+ break;
+ case MUSB_HDRC_TXHUBPORT:
+ s->ep[ep].hport[0] = value;
+ break;
+ case MUSB_HDRC_RXHUBADDR:
+ s->ep[ep].haddr[1] = value;
+ break;
+ case MUSB_HDRC_RXHUBPORT:
+ s->ep[ep].hport[1] = value;
+ break;
+
+ default:
+ TRACE("unknown register 0x%02x", addr);
+ break;
+ };
+}
+
+static uint16_t musb_busctl_readh(void *opaque, int ep, int addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ case MUSB_HDRC_TXFUNCADDR:
+ return s->ep[ep].faddr[0];
+ case MUSB_HDRC_RXFUNCADDR:
+ return s->ep[ep].faddr[1];
+
+ default:
+ return musb_busctl_readb(s, ep, addr) |
+ (musb_busctl_readb(s, ep, addr | 1) << 8);
+ };
+}
+
+static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ case MUSB_HDRC_TXFUNCADDR:
+ s->ep[ep].faddr[0] = value;
+ break;
+ case MUSB_HDRC_RXFUNCADDR:
+ s->ep[ep].faddr[1] = value;
+ break;
+
+ default:
+ musb_busctl_writeb(s, ep, addr, value & 0xff);
+ musb_busctl_writeb(s, ep, addr | 1, value >> 8);
+ };
+}
+
+/* Endpoint control */
+static uint8_t musb_ep_readb(void *opaque, int ep, int addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ case MUSB_HDRC_TXTYPE:
+ return s->ep[ep].type[0];
+ case MUSB_HDRC_TXINTERVAL:
+ return s->ep[ep].interval[0];
+ case MUSB_HDRC_RXTYPE:
+ return s->ep[ep].type[1];
+ case MUSB_HDRC_RXINTERVAL:
+ return s->ep[ep].interval[1];
+ case (MUSB_HDRC_FIFOSIZE & ~1):
+ return 0x00;
+ case MUSB_HDRC_FIFOSIZE:
+ return ep ? s->ep[ep].fifosize : s->ep[ep].config;
+ case MUSB_HDRC_RXCOUNT:
+ return s->ep[ep].rxcount;
+
+ default:
+ TRACE("unknown register 0x%02x", addr);
+ return 0x00;
+ };
+}
+
+static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ case MUSB_HDRC_TXTYPE:
+ s->ep[ep].type[0] = value;
+ break;
+ case MUSB_HDRC_TXINTERVAL:
+ s->ep[ep].interval[0] = value;
+ musb_ep_frame_cancel(&s->ep[ep], 0);
+ break;
+ case MUSB_HDRC_RXTYPE:
+ s->ep[ep].type[1] = value;
+ break;
+ case MUSB_HDRC_RXINTERVAL:
+ s->ep[ep].interval[1] = value;
+ musb_ep_frame_cancel(&s->ep[ep], 1);
+ break;
+ case (MUSB_HDRC_FIFOSIZE & ~1):
+ break;
+ case MUSB_HDRC_FIFOSIZE:
+ TRACE("somebody messes with fifosize (now %i bytes)", value);
+ s->ep[ep].fifosize = value;
+ break;
+ default:
+ TRACE("unknown register 0x%02x", addr);
+ break;
+ };
+}
+
+static uint16_t musb_ep_readh(void *opaque, int ep, int addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ uint16_t ret;
+
+ switch (addr) {
+ case MUSB_HDRC_TXMAXP:
+ return s->ep[ep].maxp[0];
+ case MUSB_HDRC_TXCSR:
+ return s->ep[ep].csr[0];
+ case MUSB_HDRC_RXMAXP:
+ return s->ep[ep].maxp[1];
+ case MUSB_HDRC_RXCSR:
+ ret = s->ep[ep].csr[1];
+
+ /* TODO: This and other bits probably depend on
+ * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR. */
+ if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR)
+ s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY;
+
+ return ret;
+ case MUSB_HDRC_RXCOUNT:
+ return s->ep[ep].rxcount;
+
+ default:
+ return musb_ep_readb(s, ep, addr) |
+ (musb_ep_readb(s, ep, addr | 1) << 8);
+ };
+}
+
+static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+
+ switch (addr) {
+ case MUSB_HDRC_TXMAXP:
+ s->ep[ep].maxp[0] = value;
+ break;
+ case MUSB_HDRC_TXCSR:
+ if (ep) {
+ s->ep[ep].csr[0] &= value & 0xa6;
+ s->ep[ep].csr[0] |= value & 0xff59;
+ } else {
+ s->ep[ep].csr[0] &= value & 0x85;
+ s->ep[ep].csr[0] |= value & 0xf7a;
+ }
+
+ musb_ep_frame_cancel(&s->ep[ep], 0);
+
+ if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) ||
+ (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) {
+ s->ep[ep].fifolen[0] = 0;
+ s->ep[ep].fifostart[0] = 0;
+ if (ep)
+ s->ep[ep].csr[0] &=
+ ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY);
+ else
+ s->ep[ep].csr[0] &=
+ ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY);
+ }
+ if (
+ (ep &&
+#ifdef CLEAR_NAK
+ (value & MGC_M_TXCSR_TXPKTRDY) &&
+ !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) ||
+#else
+ (value & MGC_M_TXCSR_TXPKTRDY)) ||
+#endif
+ (!ep &&
+#ifdef CLEAR_NAK
+ (value & MGC_M_CSR0_TXPKTRDY) &&
+ !(value & MGC_M_CSR0_H_NAKTIMEOUT)))
+#else
+ (value & MGC_M_CSR0_TXPKTRDY)))
+#endif
+ musb_tx_rdy(s, ep);
+ if (!ep &&
+ (value & MGC_M_CSR0_H_REQPKT) &&
+#ifdef CLEAR_NAK
+ !(value & (MGC_M_CSR0_H_NAKTIMEOUT |
+ MGC_M_CSR0_RXPKTRDY)))
+#else
+ !(value & MGC_M_CSR0_RXPKTRDY))
+#endif
+ musb_rx_req(s, ep);
+ break;
+
+ case MUSB_HDRC_RXMAXP:
+ s->ep[ep].maxp[1] = value;
+ break;
+ case MUSB_HDRC_RXCSR:
+ /* (DMA mode only) */
+ if (
+ (value & MGC_M_RXCSR_H_AUTOREQ) &&
+ !(value & MGC_M_RXCSR_RXPKTRDY) &&
+ (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY))
+ value |= MGC_M_RXCSR_H_REQPKT;
+
+ s->ep[ep].csr[1] &= 0x102 | (value & 0x4d);
+ s->ep[ep].csr[1] |= value & 0xfeb0;
+
+ musb_ep_frame_cancel(&s->ep[ep], 1);
+
+ if (value & MGC_M_RXCSR_FLUSHFIFO) {
+ s->ep[ep].fifolen[1] = 0;
+ s->ep[ep].fifostart[1] = 0;
+ s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY);
+ /* If double buffering and we have two packets ready, flush
+ * only the first one and set up the fifo at the second packet. */
+ }
+#ifdef CLEAR_NAK
+ if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR))
+#else
+ if (value & MGC_M_RXCSR_H_REQPKT)
+#endif
+ musb_rx_req(s, ep);
+ break;
+ case MUSB_HDRC_RXCOUNT:
+ s->ep[ep].rxcount = value;
+ break;
+
+ default:
+ musb_ep_writeb(s, ep, addr, value & 0xff);
+ musb_ep_writeb(s, ep, addr | 1, value >> 8);
+ };
+}
+
+/* Generic control */
+static uint32_t musb_readb(void *opaque, target_phys_addr_t addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ int ep, i;
+ uint8_t ret;
+
+ switch (addr) {
+ case MUSB_HDRC_FADDR:
+ return s->faddr;
+ case MUSB_HDRC_POWER:
+ return s->power;
+ case MUSB_HDRC_INTRUSB:
+ ret = s->intr;
+ for (i = 0; i < sizeof(ret) * 8; i ++)
+ if (ret & (1 << i))
+ musb_intr_set(s, i, 0);
+ return ret;
+ case MUSB_HDRC_INTRUSBE:
+ return s->mask;
+ case MUSB_HDRC_INDEX:
+ return s->idx;
+ case MUSB_HDRC_TESTMODE:
+ return 0x00;
+
+ case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+ return musb_ep_readb(s, s->idx, addr & 0xf);
+
+ case MUSB_HDRC_DEVCTL:
+ return s->devctl;
+
+ case MUSB_HDRC_TXFIFOSZ:
+ case MUSB_HDRC_RXFIFOSZ:
+ case MUSB_HDRC_VCTRL:
+ /* TODO */
+ return 0x00;
+
+ case MUSB_HDRC_HWVERS:
+ return (1 << 10) | 400;
+
+ case (MUSB_HDRC_VCTRL | 1):
+ case (MUSB_HDRC_HWVERS | 1):
+ case (MUSB_HDRC_DEVCTL | 1):
+ return 0x00;
+
+ case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+ ep = (addr >> 3) & 0xf;
+ return musb_busctl_readb(s, ep, addr & 0x7);
+
+ case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+ ep = (addr >> 4) & 0xf;
+ return musb_ep_readb(s, ep, addr & 0xf);
+
+ case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+ ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+ return musb_read_fifo(s->ep + ep);
+
+ default:
+ TRACE("unknown register 0x%02x", (int) addr);
+ return 0x00;
+ };
+}
+
+static void musb_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ int ep;
+
+ switch (addr) {
+ case MUSB_HDRC_FADDR:
+ s->faddr = value & 0x7f;
+ break;
+ case MUSB_HDRC_POWER:
+ s->power = (value & 0xef) | (s->power & 0x10);
+ /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */
+ if ((value & MGC_M_POWER_RESET) && s->port.dev) {
+ usb_send_msg(s->port.dev, USB_MSG_RESET);
+ /* Negotiate high-speed operation if MGC_M_POWER_HSENAB is set. */
+ if ((value & MGC_M_POWER_HSENAB) &&
+ s->port.dev->speed == USB_SPEED_HIGH)
+ s->power |= MGC_M_POWER_HSMODE; /* Success */
+ /* Restart frame counting. */
+ }
+ if (value & MGC_M_POWER_SUSPENDM) {
+ /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND
+ * is set, also go into low power mode. Frame counting stops. */
+ /* XXX: Cleared when the interrupt register is read */
+ }
+ if (value & MGC_M_POWER_RESUME) {
+ /* Wait 20ms and signal resuming on the bus. Frame counting
+ * restarts. */
+ }
+ break;
+ case MUSB_HDRC_INTRUSB:
+ break;
+ case MUSB_HDRC_INTRUSBE:
+ s->mask = value & 0xff;
+ break;
+ case MUSB_HDRC_INDEX:
+ s->idx = value & 0xf;
+ break;
+ case MUSB_HDRC_TESTMODE:
+ break;
+
+ case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+ musb_ep_writeb(s, s->idx, addr & 0xf, value);
+ break;
+
+ case MUSB_HDRC_DEVCTL:
+ s->session = !!(value & MGC_M_DEVCTL_SESSION);
+ musb_session_update(s,
+ !!s->port.dev,
+ !!(s->devctl & MGC_M_DEVCTL_SESSION));
+
+ /* It seems this is the only R/W bit in this register? */
+ s->devctl &= ~MGC_M_DEVCTL_SESSION;
+ s->devctl |= value & MGC_M_DEVCTL_SESSION;
+ break;
+
+ case MUSB_HDRC_TXFIFOSZ:
+ case MUSB_HDRC_RXFIFOSZ:
+ case MUSB_HDRC_VCTRL:
+ /* TODO */
+ break;
+
+ case (MUSB_HDRC_VCTRL | 1):
+ case (MUSB_HDRC_DEVCTL | 1):
+ break;
+
+ case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+ ep = (addr >> 3) & 0xf;
+ musb_busctl_writeb(s, ep, addr & 0x7, value);
+ break;
+
+ case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+ ep = (addr >> 4) & 0xf;
+ musb_ep_writeb(s, ep, addr & 0xf, value);
+ break;
+
+ case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+ ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+ musb_write_fifo(s->ep + ep, value & 0xff);
+ break;
+
+ default:
+ TRACE("unknown register 0x%02x", (int) addr);
+ break;
+ };
+}
+
+static uint32_t musb_readh(void *opaque, target_phys_addr_t addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ int ep, i;
+ uint16_t ret;
+
+ switch (addr) {
+ case MUSB_HDRC_INTRTX:
+ ret = s->tx_intr;
+ /* Auto clear */
+ for (i = 0; i < sizeof(ret) * 8; i ++)
+ if (ret & (1 << i))
+ musb_tx_intr_set(s, i, 0);
+ return ret;
+ case MUSB_HDRC_INTRRX:
+ ret = s->rx_intr;
+ /* Auto clear */
+ for (i = 0; i < sizeof(ret) * 8; i ++)
+ if (ret & (1 << i))
+ musb_rx_intr_set(s, i, 0);
+ return ret;
+ case MUSB_HDRC_INTRTXE:
+ return s->tx_mask;
+ case MUSB_HDRC_INTRRXE:
+ return s->rx_mask;
+
+ case MUSB_HDRC_FRAME:
+ /* TODO */
+ return 0x0000;
+ case MUSB_HDRC_TXFIFOADDR:
+ return s->ep[s->idx].fifoaddr[0];
+ case MUSB_HDRC_RXFIFOADDR:
+ return s->ep[s->idx].fifoaddr[1];
+
+ case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+ return musb_ep_readh(s, s->idx, addr & 0xf);
+
+ case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+ ep = (addr >> 3) & 0xf;
+ return musb_busctl_readh(s, ep, addr & 0x7);
+
+ case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+ ep = (addr >> 4) & 0xf;
+ return musb_ep_readh(s, ep, addr & 0xf);
+
+ case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+ ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+ return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8);
+
+ default:
+ return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8);
+ };
+}
+
+static void musb_writeh(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ int ep;
+
+ switch (addr) {
+ case MUSB_HDRC_INTRTXE:
+ s->tx_mask = value;
+ /* XXX: the masks seem to apply on the raising edge like with
+ * edge-triggered interrupts, thus no need to update. I may be
+ * wrong though. */
+ break;
+ case MUSB_HDRC_INTRRXE:
+ s->rx_mask = value;
+ break;
+
+ case MUSB_HDRC_FRAME:
+ /* TODO */
+ break;
+ case MUSB_HDRC_TXFIFOADDR:
+ s->ep[s->idx].fifoaddr[0] = value;
+ s->ep[s->idx].buf[0] =
+ s->buf + ((value << 3) & 0x7ff );
+ break;
+ case MUSB_HDRC_RXFIFOADDR:
+ s->ep[s->idx].fifoaddr[1] = value;
+ s->ep[s->idx].buf[1] =
+ s->buf + ((value << 3) & 0x7ff);
+ break;
+
+ case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf):
+ musb_ep_writeh(s, s->idx, addr & 0xf, value);
+ break;
+
+ case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f):
+ ep = (addr >> 3) & 0xf;
+ musb_busctl_writeh(s, ep, addr & 0x7, value);
+ break;
+
+ case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff):
+ ep = (addr >> 4) & 0xf;
+ musb_ep_writeh(s, ep, addr & 0xf, value);
+ break;
+
+ case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+ ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+ musb_write_fifo(s->ep + ep, value & 0xff);
+ musb_write_fifo(s->ep + ep, (value >> 8) & 0xff);
+ break;
+
+ default:
+ musb_writeb(s, addr, value & 0xff);
+ musb_writeb(s, addr | 1, value >> 8);
+ };
+}
+
+static uint32_t musb_readw(void *opaque, target_phys_addr_t addr)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ int ep;
+
+ switch (addr) {
+ case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+ ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+ return ( musb_read_fifo(s->ep + ep) |
+ musb_read_fifo(s->ep + ep) << 8 |
+ musb_read_fifo(s->ep + ep) << 16 |
+ musb_read_fifo(s->ep + ep) << 24 );
+ default:
+ TRACE("unknown register 0x%02x", (int) addr);
+ return 0x00000000;
+ };
+}
+
+static void musb_writew(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ MUSBState *s = (MUSBState *) opaque;
+ int ep;
+
+ switch (addr) {
+ case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f):
+ ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf;
+ musb_write_fifo(s->ep + ep, value & 0xff);
+ musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff);
+ musb_write_fifo(s->ep + ep, (value >> 16) & 0xff);
+ musb_write_fifo(s->ep + ep, (value >> 24) & 0xff);
+ break;
+ default:
+ TRACE("unknown register 0x%02x", (int) addr);
+ break;
+ };
+}
+
+CPUReadMemoryFunc * const musb_read[] = {
+ musb_readb,
+ musb_readh,
+ musb_readw,
+};
+
+CPUWriteMemoryFunc * const musb_write[] = {
+ musb_writeb,
+ musb_writeh,
+ musb_writew,
+};
diff --git a/hw/usb-net.c b/hw/usb-net.c
new file mode 100644
index 0000000..bf51bb3
--- /dev/null
+++ b/hw/usb-net.c
@@ -0,0 +1,1441 @@
+/*
+ * QEMU USB Net devices
+ *
+ * Copyright (c) 2006 Thomas Sailer
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "usb-desc.h"
+#include "net.h"
+#include "qemu-queue.h"
+#include "sysemu.h"
+
+/*#define TRAFFIC_DEBUG*/
+/* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
+/* For hardware that can talk RNDIS and either of the above protocols,
+ * use this ID ... the windows INF files will know it.
+ */
+#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
+#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
+
+enum usbstring_idx {
+ STRING_MANUFACTURER = 1,
+ STRING_PRODUCT,
+ STRING_ETHADDR,
+ STRING_DATA,
+ STRING_CONTROL,
+ STRING_RNDIS_CONTROL,
+ STRING_CDC,
+ STRING_SUBSET,
+ STRING_RNDIS,
+ STRING_SERIALNUMBER,
+};
+
+#define DEV_CONFIG_VALUE 1 /* CDC or a subset */
+#define DEV_RNDIS_CONFIG_VALUE 2 /* RNDIS; optional */
+
+#define USB_CDC_SUBCLASS_ACM 0x02
+#define USB_CDC_SUBCLASS_ETHERNET 0x06
+
+#define USB_CDC_PROTO_NONE 0
+#define USB_CDC_ACM_PROTO_VENDOR 0xff
+
+#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */
+#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */
+#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */
+#define USB_CDC_UNION_TYPE 0x06 /* union_desc */
+#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */
+
+#define USB_DT_CS_INTERFACE 0x24
+#define USB_DT_CS_ENDPOINT 0x25
+
+#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00
+#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01
+#define USB_CDC_REQ_SET_LINE_CODING 0x20
+#define USB_CDC_REQ_GET_LINE_CODING 0x21
+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
+#define USB_CDC_REQ_SEND_BREAK 0x23
+#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40
+#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41
+#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42
+#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43
+#define USB_CDC_GET_ETHERNET_STATISTIC 0x44
+
+#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
+
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+
+static const USBDescStrings usb_net_stringtable = {
+ [STRING_MANUFACTURER] = "QEMU",
+ [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device",
+ [STRING_ETHADDR] = "400102030405",
+ [STRING_DATA] = "QEMU USB Net Data Interface",
+ [STRING_CONTROL] = "QEMU USB Net Control Interface",
+ [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface",
+ [STRING_CDC] = "QEMU USB Net CDC",
+ [STRING_SUBSET] = "QEMU USB Net Subset",
+ [STRING_RNDIS] = "QEMU USB Net RNDIS",
+ [STRING_SERIALNUMBER] = "1",
+};
+
+static const USBDescIface desc_iface_rndis[] = {
+ {
+ /* RNDIS Control Interface */
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ .iInterface = STRING_RNDIS_CONTROL,
+ .ndesc = 4,
+ .descs = (USBDescOther[]) {
+ {
+ /* Header Descriptor */
+ .data = (uint8_t[]) {
+ 0x05, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
+ 0x10, 0x01, /* le16 bcdCDC */
+ },
+ },{
+ /* Call Management Descriptor */
+ .data = (uint8_t[]) {
+ 0x05, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */
+ 0x00, /* u8 bmCapabilities */
+ 0x01, /* u8 bDataInterface */
+ },
+ },{
+ /* ACM Descriptor */
+ .data = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */
+ 0x00, /* u8 bmCapabilities */
+ },
+ },{
+ /* Union Descriptor */
+ .data = (uint8_t[]) {
+ 0x05, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
+ 0x00, /* u8 bMasterInterface0 */
+ 0x01, /* u8 bSlaveInterface0 */
+ },
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = STATUS_BYTECOUNT,
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+ },
+ }
+ },{
+ /* RNDIS Data Interface */
+ .bInterfaceNumber = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .iInterface = STRING_DATA,
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 0x40,
+ },{
+ .bEndpointAddress = USB_DIR_OUT | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 0x40,
+ }
+ }
+ }
+};
+
+static const USBDescIface desc_iface_cdc[] = {
+ {
+ /* CDC Control Interface */
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ .iInterface = STRING_CONTROL,
+ .ndesc = 3,
+ .descs = (USBDescOther[]) {
+ {
+ /* Header Descriptor */
+ .data = (uint8_t[]) {
+ 0x05, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
+ 0x10, 0x01, /* le16 bcdCDC */
+ },
+ },{
+ /* Union Descriptor */
+ .data = (uint8_t[]) {
+ 0x05, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
+ 0x00, /* u8 bMasterInterface0 */
+ 0x01, /* u8 bSlaveInterface0 */
+ },
+ },{
+ /* Ethernet Descriptor */
+ .data = (uint8_t[]) {
+ 0x0d, /* u8 bLength */
+ USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
+ USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */
+ STRING_ETHADDR, /* u8 iMACAddress */
+ 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */
+ ETH_FRAME_LEN & 0xff,
+ ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */
+ 0x00, 0x00, /* le16 wNumberMCFilters */
+ 0x00, /* u8 bNumberPowerFilters */
+ },
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = STATUS_BYTECOUNT,
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+ },
+ }
+ },{
+ /* CDC Data Interface (off) */
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ },{
+ /* CDC Data Interface */
+ .bInterfaceNumber = 1,
+ .bAlternateSetting = 1,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .iInterface = STRING_DATA,
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 0x40,
+ },{
+ .bEndpointAddress = USB_DIR_OUT | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 0x40,
+ }
+ }
+ }
+};
+
+static const USBDescDevice desc_device_net = {
+ .bcdUSB = 0x0200,
+ .bDeviceClass = USB_CLASS_COMM,
+ .bMaxPacketSize0 = 0x40,
+ .bNumConfigurations = 2,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 2,
+ .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
+ .iConfiguration = STRING_RNDIS,
+ .bmAttributes = 0xc0,
+ .bMaxPower = 0x32,
+ .nif = ARRAY_SIZE(desc_iface_rndis),
+ .ifs = desc_iface_rndis,
+ },{
+ .bNumInterfaces = 2,
+ .bConfigurationValue = DEV_CONFIG_VALUE,
+ .iConfiguration = STRING_CDC,
+ .bmAttributes = 0xc0,
+ .bMaxPower = 0x32,
+ .nif = ARRAY_SIZE(desc_iface_cdc),
+ .ifs = desc_iface_cdc,
+ }
+ },
+};
+
+static const USBDesc desc_net = {
+ .id = {
+ .idVendor = RNDIS_VENDOR_NUM,
+ .idProduct = RNDIS_PRODUCT_NUM,
+ .bcdDevice = 0,
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .iSerialNumber = STRING_SERIALNUMBER,
+ },
+ .full = &desc_device_net,
+ .str = usb_net_stringtable,
+};
+
+/*
+ * RNDIS Definitions - in theory not specific to USB.
+ */
+#define RNDIS_MAXIMUM_FRAME_SIZE 1518
+#define RNDIS_MAX_TOTAL_SIZE 1558
+
+/* Remote NDIS Versions */
+#define RNDIS_MAJOR_VERSION 1
+#define RNDIS_MINOR_VERSION 0
+
+/* Status Values */
+#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */
+#define RNDIS_STATUS_FAILURE 0xc0000001U /* Unspecified error */
+#define RNDIS_STATUS_INVALID_DATA 0xc0010015U /* Invalid data */
+#define RNDIS_STATUS_NOT_SUPPORTED 0xc00000bbU /* Unsupported request */
+#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000bU /* Device connected */
+#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000cU /* Device disconnected */
+
+/* Message Set for Connectionless (802.3) Devices */
+enum {
+ RNDIS_PACKET_MSG = 1,
+ RNDIS_INITIALIZE_MSG = 2, /* Initialize device */
+ RNDIS_HALT_MSG = 3,
+ RNDIS_QUERY_MSG = 4,
+ RNDIS_SET_MSG = 5,
+ RNDIS_RESET_MSG = 6,
+ RNDIS_INDICATE_STATUS_MSG = 7,
+ RNDIS_KEEPALIVE_MSG = 8,
+};
+
+/* Message completion */
+enum {
+ RNDIS_INITIALIZE_CMPLT = 0x80000002U,
+ RNDIS_QUERY_CMPLT = 0x80000004U,
+ RNDIS_SET_CMPLT = 0x80000005U,
+ RNDIS_RESET_CMPLT = 0x80000006U,
+ RNDIS_KEEPALIVE_CMPLT = 0x80000008U,
+};
+
+/* Device Flags */
+enum {
+ RNDIS_DF_CONNECTIONLESS = 1,
+ RNDIS_DF_CONNECTIONORIENTED = 2,
+};
+
+#define RNDIS_MEDIUM_802_3 0x00000000U
+
+/* from drivers/net/sk98lin/h/skgepnmi.h */
+#define OID_PNP_CAPABILITIES 0xfd010100
+#define OID_PNP_SET_POWER 0xfd010101
+#define OID_PNP_QUERY_POWER 0xfd010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN 0xfd010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xfd010104
+#define OID_PNP_ENABLE_WAKE_UP 0xfd010106
+
+typedef uint32_t le32;
+
+typedef struct rndis_init_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 MajorVersion;
+ le32 MinorVersion;
+ le32 MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 Status;
+ le32 MajorVersion;
+ le32 MinorVersion;
+ le32 DeviceFlags;
+ le32 Medium;
+ le32 MaxPacketsPerTransfer;
+ le32 MaxTransferSize;
+ le32 PacketAlignmentFactor;
+ le32 AFListOffset;
+ le32 AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 OID;
+ le32 InformationBufferLength;
+ le32 InformationBufferOffset;
+ le32 DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 Status;
+ le32 InformationBufferLength;
+ le32 InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 OID;
+ le32 InformationBufferLength;
+ le32 InformationBufferOffset;
+ le32 DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 Status;
+ le32 AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 Status;
+ le32 StatusBufferLength;
+ le32 StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 RequestID;
+ le32 Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type {
+ le32 MessageType;
+ le32 MessageLength;
+ le32 DataOffset;
+ le32 DataLength;
+ le32 OOBDataOffset;
+ le32 OOBDataLength;
+ le32 NumOOBDataElements;
+ le32 PerPacketInfoOffset;
+ le32 PerPacketInfoLength;
+ le32 VcHandle;
+ le32 Reserved;
+};
+
+struct rndis_config_parameter {
+ le32 ParameterNameOffset;
+ le32 ParameterNameLength;
+ le32 ParameterType;
+ le32 ParameterValueOffset;
+ le32 ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state
+{
+ RNDIS_UNINITIALIZED,
+ RNDIS_INITIALIZED,
+ RNDIS_DATA_INITIALIZED,
+};
+
+/* from ndis.h */
+enum ndis_oid {
+ /* Required Object IDs (OIDs) */
+ OID_GEN_SUPPORTED_LIST = 0x00010101,
+ OID_GEN_HARDWARE_STATUS = 0x00010102,
+ OID_GEN_MEDIA_SUPPORTED = 0x00010103,
+ OID_GEN_MEDIA_IN_USE = 0x00010104,
+ OID_GEN_MAXIMUM_LOOKAHEAD = 0x00010105,
+ OID_GEN_MAXIMUM_FRAME_SIZE = 0x00010106,
+ OID_GEN_LINK_SPEED = 0x00010107,
+ OID_GEN_TRANSMIT_BUFFER_SPACE = 0x00010108,
+ OID_GEN_RECEIVE_BUFFER_SPACE = 0x00010109,
+ OID_GEN_TRANSMIT_BLOCK_SIZE = 0x0001010a,
+ OID_GEN_RECEIVE_BLOCK_SIZE = 0x0001010b,
+ OID_GEN_VENDOR_ID = 0x0001010c,
+ OID_GEN_VENDOR_DESCRIPTION = 0x0001010d,
+ OID_GEN_CURRENT_PACKET_FILTER = 0x0001010e,
+ OID_GEN_CURRENT_LOOKAHEAD = 0x0001010f,
+ OID_GEN_DRIVER_VERSION = 0x00010110,
+ OID_GEN_MAXIMUM_TOTAL_SIZE = 0x00010111,
+ OID_GEN_PROTOCOL_OPTIONS = 0x00010112,
+ OID_GEN_MAC_OPTIONS = 0x00010113,
+ OID_GEN_MEDIA_CONNECT_STATUS = 0x00010114,
+ OID_GEN_MAXIMUM_SEND_PACKETS = 0x00010115,
+ OID_GEN_VENDOR_DRIVER_VERSION = 0x00010116,
+ OID_GEN_SUPPORTED_GUIDS = 0x00010117,
+ OID_GEN_NETWORK_LAYER_ADDRESSES = 0x00010118,
+ OID_GEN_TRANSPORT_HEADER_OFFSET = 0x00010119,
+ OID_GEN_MACHINE_NAME = 0x0001021a,
+ OID_GEN_RNDIS_CONFIG_PARAMETER = 0x0001021b,
+ OID_GEN_VLAN_ID = 0x0001021c,
+
+ /* Optional OIDs */
+ OID_GEN_MEDIA_CAPABILITIES = 0x00010201,
+ OID_GEN_PHYSICAL_MEDIUM = 0x00010202,
+
+ /* Required statistics OIDs */
+ OID_GEN_XMIT_OK = 0x00020101,
+ OID_GEN_RCV_OK = 0x00020102,
+ OID_GEN_XMIT_ERROR = 0x00020103,
+ OID_GEN_RCV_ERROR = 0x00020104,
+ OID_GEN_RCV_NO_BUFFER = 0x00020105,
+
+ /* Optional statistics OIDs */
+ OID_GEN_DIRECTED_BYTES_XMIT = 0x00020201,
+ OID_GEN_DIRECTED_FRAMES_XMIT = 0x00020202,
+ OID_GEN_MULTICAST_BYTES_XMIT = 0x00020203,
+ OID_GEN_MULTICAST_FRAMES_XMIT = 0x00020204,
+ OID_GEN_BROADCAST_BYTES_XMIT = 0x00020205,
+ OID_GEN_BROADCAST_FRAMES_XMIT = 0x00020206,
+ OID_GEN_DIRECTED_BYTES_RCV = 0x00020207,
+ OID_GEN_DIRECTED_FRAMES_RCV = 0x00020208,
+ OID_GEN_MULTICAST_BYTES_RCV = 0x00020209,
+ OID_GEN_MULTICAST_FRAMES_RCV = 0x0002020a,
+ OID_GEN_BROADCAST_BYTES_RCV = 0x0002020b,
+ OID_GEN_BROADCAST_FRAMES_RCV = 0x0002020c,
+ OID_GEN_RCV_CRC_ERROR = 0x0002020d,
+ OID_GEN_TRANSMIT_QUEUE_LENGTH = 0x0002020e,
+ OID_GEN_GET_TIME_CAPS = 0x0002020f,
+ OID_GEN_GET_NETCARD_TIME = 0x00020210,
+ OID_GEN_NETCARD_LOAD = 0x00020211,
+ OID_GEN_DEVICE_PROFILE = 0x00020212,
+ OID_GEN_INIT_TIME_MS = 0x00020213,
+ OID_GEN_RESET_COUNTS = 0x00020214,
+ OID_GEN_MEDIA_SENSE_COUNTS = 0x00020215,
+ OID_GEN_FRIENDLY_NAME = 0x00020216,
+ OID_GEN_MINIPORT_INFO = 0x00020217,
+ OID_GEN_RESET_VERIFY_PARAMETERS = 0x00020218,
+
+ /* IEEE 802.3 (Ethernet) OIDs */
+ OID_802_3_PERMANENT_ADDRESS = 0x01010101,
+ OID_802_3_CURRENT_ADDRESS = 0x01010102,
+ OID_802_3_MULTICAST_LIST = 0x01010103,
+ OID_802_3_MAXIMUM_LIST_SIZE = 0x01010104,
+ OID_802_3_MAC_OPTIONS = 0x01010105,
+ OID_802_3_RCV_ERROR_ALIGNMENT = 0x01020101,
+ OID_802_3_XMIT_ONE_COLLISION = 0x01020102,
+ OID_802_3_XMIT_MORE_COLLISIONS = 0x01020103,
+ OID_802_3_XMIT_DEFERRED = 0x01020201,
+ OID_802_3_XMIT_MAX_COLLISIONS = 0x01020202,
+ OID_802_3_RCV_OVERRUN = 0x01020203,
+ OID_802_3_XMIT_UNDERRUN = 0x01020204,
+ OID_802_3_XMIT_HEARTBEAT_FAILURE = 0x01020205,
+ OID_802_3_XMIT_TIMES_CRS_LOST = 0x01020206,
+ OID_802_3_XMIT_LATE_COLLISIONS = 0x01020207,
+};
+
+static const uint32_t oid_supported_list[] =
+{
+ /* the general stuff */
+ OID_GEN_SUPPORTED_LIST,
+ OID_GEN_HARDWARE_STATUS,
+ OID_GEN_MEDIA_SUPPORTED,
+ OID_GEN_MEDIA_IN_USE,
+ OID_GEN_MAXIMUM_FRAME_SIZE,
+ OID_GEN_LINK_SPEED,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+ OID_GEN_PHYSICAL_MEDIUM,
+
+ /* the statistical stuff */
+ OID_GEN_XMIT_OK,
+ OID_GEN_RCV_OK,
+ OID_GEN_XMIT_ERROR,
+ OID_GEN_RCV_ERROR,
+ OID_GEN_RCV_NO_BUFFER,
+
+ /* IEEE 802.3 */
+ /* the general stuff */
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAC_OPTIONS,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+
+ /* the statistical stuff */
+ OID_802_3_RCV_ERROR_ALIGNMENT,
+ OID_802_3_XMIT_ONE_COLLISION,
+ OID_802_3_XMIT_MORE_COLLISIONS,
+};
+
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA (1 << 0)
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED (1 << 1)
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND (1 << 2)
+#define NDIS_MAC_OPTION_NO_LOOPBACK (1 << 3)
+#define NDIS_MAC_OPTION_FULL_DUPLEX (1 << 4)
+#define NDIS_MAC_OPTION_EOTX_INDICATION (1 << 5)
+#define NDIS_MAC_OPTION_8021P_PRIORITY (1 << 6)
+
+struct rndis_response {
+ QTAILQ_ENTRY(rndis_response) entries;
+ uint32_t length;
+ uint8_t buf[0];
+};
+
+typedef struct USBNetState {
+ USBDevice dev;
+
+ enum rndis_state rndis_state;
+ uint32_t medium;
+ uint32_t speed;
+ uint32_t media_state;
+ uint16_t filter;
+ uint32_t vendorid;
+
+ unsigned int out_ptr;
+ uint8_t out_buf[2048];
+
+ USBPacket *inpkt;
+ unsigned int in_ptr, in_len;
+ uint8_t in_buf[2048];
+
+ char usbstring_mac[13];
+ NICState *nic;
+ NICConf conf;
+ QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
+} USBNetState;
+
+static int is_rndis(USBNetState *s)
+{
+ return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
+}
+
+static int ndis_query(USBNetState *s, uint32_t oid,
+ uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf,
+ size_t outlen)
+{
+ unsigned int i;
+
+ switch (oid) {
+ /* general oids (table 4-1) */
+ /* mandatory */
+ case OID_GEN_SUPPORTED_LIST:
+ for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++)
+ ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]);
+ return sizeof(oid_supported_list);
+
+ /* mandatory */
+ case OID_GEN_HARDWARE_STATUS:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_MEDIA_SUPPORTED:
+ *((le32 *) outbuf) = cpu_to_le32(s->medium);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_MEDIA_IN_USE:
+ *((le32 *) outbuf) = cpu_to_le32(s->medium);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_LINK_SPEED:
+ *((le32 *) outbuf) = cpu_to_le32(s->speed);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_VENDOR_ID:
+ *((le32 *) outbuf) = cpu_to_le32(s->vendorid);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_VENDOR_DESCRIPTION:
+ pstrcpy((char *)outbuf, outlen, "QEMU USB RNDIS Net");
+ return strlen((char *)outbuf) + 1;
+
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ *((le32 *) outbuf) = cpu_to_le32(1);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ *((le32 *) outbuf) = cpu_to_le32(s->filter);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+ *((le32 *) outbuf) = cpu_to_le32(s->media_state);
+ return sizeof(le32);
+
+ case OID_GEN_PHYSICAL_MEDIUM:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ case OID_GEN_MAC_OPTIONS:
+ *((le32 *) outbuf) = cpu_to_le32(
+ NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
+ NDIS_MAC_OPTION_FULL_DUPLEX);
+ return sizeof(le32);
+
+ /* statistics OIDs (table 4-2) */
+ /* mandatory */
+ case OID_GEN_XMIT_OK:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_RCV_OK:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_XMIT_ERROR:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_RCV_ERROR:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_GEN_RCV_NO_BUFFER:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* ieee802.3 OIDs (table 4-3) */
+ /* mandatory */
+ case OID_802_3_PERMANENT_ADDRESS:
+ memcpy(outbuf, s->conf.macaddr.a, 6);
+ return 6;
+
+ /* mandatory */
+ case OID_802_3_CURRENT_ADDRESS:
+ memcpy(outbuf, s->conf.macaddr.a, 6);
+ return 6;
+
+ /* mandatory */
+ case OID_802_3_MULTICAST_LIST:
+ *((le32 *) outbuf) = cpu_to_le32(0xe0000000);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ *((le32 *) outbuf) = cpu_to_le32(1);
+ return sizeof(le32);
+
+ case OID_802_3_MAC_OPTIONS:
+ return 0;
+
+ /* ieee802.3 statistics OIDs (table 4-4) */
+ /* mandatory */
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_802_3_XMIT_ONE_COLLISION:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ /* mandatory */
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ *((le32 *) outbuf) = cpu_to_le32(0);
+ return sizeof(le32);
+
+ default:
+ fprintf(stderr, "usbnet: unknown OID 0x%08x\n", oid);
+ return 0;
+ }
+ return -1;
+}
+
+static int ndis_set(USBNetState *s, uint32_t oid,
+ uint8_t *inbuf, unsigned int inlen)
+{
+ switch (oid) {
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ s->filter = le32_to_cpup((le32 *) inbuf);
+ if (s->filter) {
+ s->rndis_state = RNDIS_DATA_INITIALIZED;
+ } else {
+ s->rndis_state = RNDIS_INITIALIZED;
+ }
+ return 0;
+
+ case OID_802_3_MULTICAST_LIST:
+ return 0;
+ }
+ return -1;
+}
+
+static int rndis_get_response(USBNetState *s, uint8_t *buf)
+{
+ int ret = 0;
+ struct rndis_response *r = s->rndis_resp.tqh_first;
+
+ if (!r)
+ return ret;
+
+ QTAILQ_REMOVE(&s->rndis_resp, r, entries);
+ ret = r->length;
+ memcpy(buf, r->buf, r->length);
+ qemu_free(r);
+
+ return ret;
+}
+
+static void *rndis_queue_response(USBNetState *s, unsigned int length)
+{
+ struct rndis_response *r =
+ qemu_mallocz(sizeof(struct rndis_response) + length);
+
+ QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries);
+ r->length = length;
+
+ return &r->buf[0];
+}
+
+static void rndis_clear_responsequeue(USBNetState *s)
+{
+ struct rndis_response *r;
+
+ while ((r = s->rndis_resp.tqh_first)) {
+ QTAILQ_REMOVE(&s->rndis_resp, r, entries);
+ qemu_free(r);
+ }
+}
+
+static int rndis_init_response(USBNetState *s, rndis_init_msg_type *buf)
+{
+ rndis_init_cmplt_type *resp =
+ rndis_queue_response(s, sizeof(rndis_init_cmplt_type));
+
+ if (!resp)
+ return USB_RET_STALL;
+
+ resp->MessageType = cpu_to_le32(RNDIS_INITIALIZE_CMPLT);
+ resp->MessageLength = cpu_to_le32(sizeof(rndis_init_cmplt_type));
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
+ resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
+ resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+ resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
+ resp->MaxPacketsPerTransfer = cpu_to_le32(1);
+ resp->MaxTransferSize = cpu_to_le32(ETH_FRAME_LEN +
+ sizeof(struct rndis_packet_msg_type) + 22);
+ resp->PacketAlignmentFactor = cpu_to_le32(0);
+ resp->AFListOffset = cpu_to_le32(0);
+ resp->AFListSize = cpu_to_le32(0);
+ return 0;
+}
+
+static int rndis_query_response(USBNetState *s,
+ rndis_query_msg_type *buf, unsigned int length)
+{
+ rndis_query_cmplt_type *resp;
+ /* oid_supported_list is the largest data reply */
+ uint8_t infobuf[sizeof(oid_supported_list)];
+ uint32_t bufoffs, buflen;
+ int infobuflen;
+ unsigned int resplen;
+
+ bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
+ buflen = le32_to_cpu(buf->InformationBufferLength);
+ if (bufoffs + buflen > length)
+ return USB_RET_STALL;
+
+ infobuflen = ndis_query(s, le32_to_cpu(buf->OID),
+ bufoffs + (uint8_t *) buf, buflen, infobuf,
+ sizeof(infobuf));
+ resplen = sizeof(rndis_query_cmplt_type) +
+ ((infobuflen < 0) ? 0 : infobuflen);
+ resp = rndis_queue_response(s, resplen);
+ if (!resp)
+ return USB_RET_STALL;
+
+ resp->MessageType = cpu_to_le32(RNDIS_QUERY_CMPLT);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->MessageLength = cpu_to_le32(resplen);
+
+ if (infobuflen < 0) {
+ /* OID not supported */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+ resp->InformationBufferLength = cpu_to_le32(0);
+ resp->InformationBufferOffset = cpu_to_le32(0);
+ return 0;
+ }
+
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ resp->InformationBufferOffset =
+ cpu_to_le32(infobuflen ? sizeof(rndis_query_cmplt_type) - 8 : 0);
+ resp->InformationBufferLength = cpu_to_le32(infobuflen);
+ memcpy(resp + 1, infobuf, infobuflen);
+
+ return 0;
+}
+
+static int rndis_set_response(USBNetState *s,
+ rndis_set_msg_type *buf, unsigned int length)
+{
+ rndis_set_cmplt_type *resp =
+ rndis_queue_response(s, sizeof(rndis_set_cmplt_type));
+ uint32_t bufoffs, buflen;
+ int ret;
+
+ if (!resp)
+ return USB_RET_STALL;
+
+ bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8;
+ buflen = le32_to_cpu(buf->InformationBufferLength);
+ if (bufoffs + buflen > length)
+ return USB_RET_STALL;
+
+ ret = ndis_set(s, le32_to_cpu(buf->OID),
+ bufoffs + (uint8_t *) buf, buflen);
+ resp->MessageType = cpu_to_le32(RNDIS_SET_CMPLT);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->MessageLength = cpu_to_le32(sizeof(rndis_set_cmplt_type));
+ if (ret < 0) {
+ /* OID not supported */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+ return 0;
+ }
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ return 0;
+}
+
+static int rndis_reset_response(USBNetState *s, rndis_reset_msg_type *buf)
+{
+ rndis_reset_cmplt_type *resp =
+ rndis_queue_response(s, sizeof(rndis_reset_cmplt_type));
+
+ if (!resp)
+ return USB_RET_STALL;
+
+ resp->MessageType = cpu_to_le32(RNDIS_RESET_CMPLT);
+ resp->MessageLength = cpu_to_le32(sizeof(rndis_reset_cmplt_type));
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ resp->AddressingReset = cpu_to_le32(1); /* reset information */
+
+ return 0;
+}
+
+static int rndis_keepalive_response(USBNetState *s,
+ rndis_keepalive_msg_type *buf)
+{
+ rndis_keepalive_cmplt_type *resp =
+ rndis_queue_response(s, sizeof(rndis_keepalive_cmplt_type));
+
+ if (!resp)
+ return USB_RET_STALL;
+
+ resp->MessageType = cpu_to_le32(RNDIS_KEEPALIVE_CMPLT);
+ resp->MessageLength = cpu_to_le32(sizeof(rndis_keepalive_cmplt_type));
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ return 0;
+}
+
+static int rndis_parse(USBNetState *s, uint8_t *data, int length)
+{
+ uint32_t msg_type;
+ le32 *tmp = (le32 *) data;
+
+ msg_type = le32_to_cpup(tmp);
+
+ switch (msg_type) {
+ case RNDIS_INITIALIZE_MSG:
+ s->rndis_state = RNDIS_INITIALIZED;
+ return rndis_init_response(s, (rndis_init_msg_type *) data);
+
+ case RNDIS_HALT_MSG:
+ s->rndis_state = RNDIS_UNINITIALIZED;
+ return 0;
+
+ case RNDIS_QUERY_MSG:
+ return rndis_query_response(s, (rndis_query_msg_type *) data, length);
+
+ case RNDIS_SET_MSG:
+ return rndis_set_response(s, (rndis_set_msg_type *) data, length);
+
+ case RNDIS_RESET_MSG:
+ rndis_clear_responsequeue(s);
+ s->out_ptr = s->in_ptr = s->in_len = 0;
+ return rndis_reset_response(s, (rndis_reset_msg_type *) data);
+
+ case RNDIS_KEEPALIVE_MSG:
+ /* For USB: host does this every 5 seconds */
+ return rndis_keepalive_response(s, (rndis_keepalive_msg_type *) data);
+ }
+
+ return USB_RET_STALL;
+}
+
+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)
+{
+ USBNetState *s = (USBNetState *) dev;
+ int ret;
+
+ ret = usb_desc_handle_control(dev, request, value, index, length, data);
+ if (ret >= 0) {
+ return ret;
+ }
+
+ ret = 0;
+ switch(request) {
+ case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
+ if (!is_rndis(s) || value || index != 0) {
+ goto fail;
+ }
+#ifdef TRAFFIC_DEBUG
+ {
+ unsigned int i;
+ fprintf(stderr, "SEND_ENCAPSULATED_COMMAND:");
+ for (i = 0; i < length; i++) {
+ if (!(i & 15))
+ fprintf(stderr, "\n%04x:", i);
+ fprintf(stderr, " %02x", data[i]);
+ }
+ fprintf(stderr, "\n\n");
+ }
+#endif
+ ret = rndis_parse(s, data, length);
+ break;
+
+ case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
+ if (!is_rndis(s) || value || index != 0) {
+ goto fail;
+ }
+ ret = rndis_get_response(s, data);
+ if (!ret) {
+ data[0] = 0;
+ ret = 1;
+ }
+#ifdef TRAFFIC_DEBUG
+ {
+ unsigned int i;
+ fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:");
+ for (i = 0; i < ret; i++) {
+ if (!(i & 15))
+ fprintf(stderr, "\n%04x:", i);
+ fprintf(stderr, " %02x", data[i]);
+ }
+ fprintf(stderr, "\n\n");
+ }
+#endif
+ break;
+
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+
+ default:
+ fail:
+ fprintf(stderr, "usbnet: failed control transaction: "
+ "request 0x%x value 0x%x index 0x%x length 0x%x\n",
+ request, value, index, length);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
+{
+ int ret = 8;
+
+ if (p->len < 8)
+ return USB_RET_STALL;
+
+ ((le32 *) p->data)[0] = cpu_to_le32(1);
+ ((le32 *) p->data)[1] = cpu_to_le32(0);
+ if (!s->rndis_resp.tqh_first)
+ ret = USB_RET_NAK;
+
+#ifdef TRAFFIC_DEBUG
+ fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret);
+ {
+ int i;
+ fprintf(stderr, ":");
+ for (i = 0; i < ret; i++) {
+ if (!(i & 15))
+ fprintf(stderr, "\n%04x:", i);
+ fprintf(stderr, " %02x", p->data[i]);
+ }
+ fprintf(stderr, "\n\n");
+ }
+#endif
+
+ return ret;
+}
+
+static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
+{
+ int ret = USB_RET_NAK;
+
+ if (s->in_ptr > s->in_len) {
+ s->in_ptr = s->in_len = 0;
+ ret = USB_RET_NAK;
+ return ret;
+ }
+ if (!s->in_len) {
+ ret = USB_RET_NAK;
+ return ret;
+ }
+ ret = s->in_len - s->in_ptr;
+ if (ret > p->len)
+ ret = p->len;
+ memcpy(p->data, &s->in_buf[s->in_ptr], ret);
+ s->in_ptr += ret;
+ if (s->in_ptr >= s->in_len &&
+ (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
+ /* no short packet necessary */
+ s->in_ptr = s->in_len = 0;
+ }
+
+#ifdef TRAFFIC_DEBUG
+ fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret);
+ {
+ int i;
+ fprintf(stderr, ":");
+ for (i = 0; i < ret; i++) {
+ if (!(i & 15))
+ fprintf(stderr, "\n%04x:", i);
+ fprintf(stderr, " %02x", p->data[i]);
+ }
+ fprintf(stderr, "\n\n");
+ }
+#endif
+
+ return ret;
+}
+
+static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
+{
+ int ret = p->len;
+ int sz = sizeof(s->out_buf) - s->out_ptr;
+ struct rndis_packet_msg_type *msg =
+ (struct rndis_packet_msg_type *) s->out_buf;
+ uint32_t len;
+
+#ifdef TRAFFIC_DEBUG
+ fprintf(stderr, "usbnet: data out len %u\n", p->len);
+ {
+ int i;
+ fprintf(stderr, ":");
+ for (i = 0; i < p->len; i++) {
+ if (!(i & 15))
+ fprintf(stderr, "\n%04x:", i);
+ fprintf(stderr, " %02x", p->data[i]);
+ }
+ fprintf(stderr, "\n\n");
+ }
+#endif
+
+ if (sz > ret)
+ sz = ret;
+ memcpy(&s->out_buf[s->out_ptr], p->data, sz);
+ s->out_ptr += sz;
+
+ if (!is_rndis(s)) {
+ if (ret < 64) {
+ qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
+ s->out_ptr = 0;
+ }
+ return ret;
+ }
+ len = le32_to_cpu(msg->MessageLength);
+ if (s->out_ptr < 8 || s->out_ptr < len)
+ return ret;
+ if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
+ uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
+ uint32_t size = le32_to_cpu(msg->DataLength);
+ if (offs + size <= len)
+ qemu_send_packet(&s->nic->nc, s->out_buf + offs, size);
+ }
+ s->out_ptr -= len;
+ memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
+
+ return ret;
+}
+
+static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBNetState *s = (USBNetState *) dev;
+ int ret = 0;
+
+ switch(p->pid) {
+ case USB_TOKEN_IN:
+ switch (p->devep) {
+ case 1:
+ ret = usb_net_handle_statusin(s, p);
+ break;
+
+ case 2:
+ ret = usb_net_handle_datain(s, p);
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+
+ case USB_TOKEN_OUT:
+ switch (p->devep) {
+ case 2:
+ ret = usb_net_handle_dataout(s, p);
+ break;
+
+ default:
+ goto fail;
+ }
+ break;
+
+ default:
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+ if (ret == USB_RET_STALL)
+ fprintf(stderr, "usbnet: failed data transaction: "
+ "pid 0x%x ep 0x%x len 0x%x\n",
+ p->pid, p->devep, p->len);
+ return ret;
+}
+
+static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct rndis_packet_msg_type *msg;
+
+ if (is_rndis(s)) {
+ msg = (struct rndis_packet_msg_type *) s->in_buf;
+ if (!s->rndis_state == RNDIS_DATA_INITIALIZED)
+ return -1;
+ if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf))
+ return -1;
+
+ memset(msg, 0, sizeof(struct rndis_packet_msg_type));
+ msg->MessageType = cpu_to_le32(RNDIS_PACKET_MSG);
+ msg->MessageLength = cpu_to_le32(size + sizeof(struct rndis_packet_msg_type));
+ msg->DataOffset = cpu_to_le32(sizeof(struct rndis_packet_msg_type) - 8);
+ msg->DataLength = cpu_to_le32(size);
+ /* msg->OOBDataOffset;
+ * msg->OOBDataLength;
+ * msg->NumOOBDataElements;
+ * msg->PerPacketInfoOffset;
+ * msg->PerPacketInfoLength;
+ * msg->VcHandle;
+ * msg->Reserved;
+ */
+ memcpy(msg + 1, buf, size);
+ s->in_len = size + sizeof(struct rndis_packet_msg_type);
+ } else {
+ if (size > sizeof(s->in_buf))
+ return -1;
+ memcpy(s->in_buf, buf, size);
+ s->in_len = size;
+ }
+ s->in_ptr = 0;
+ return size;
+}
+
+static int usbnet_can_receive(VLANClientState *nc)
+{
+ USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ if (is_rndis(s) && !s->rndis_state == RNDIS_DATA_INITIALIZED) {
+ return 1;
+ }
+
+ return !s->in_len;
+}
+
+static void usbnet_cleanup(VLANClientState *nc)
+{
+ USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static void usb_net_handle_destroy(USBDevice *dev)
+{
+ USBNetState *s = (USBNetState *) dev;
+
+ /* TODO: remove the nd_table[] entry */
+ rndis_clear_responsequeue(s);
+ qemu_del_vlan_client(&s->nic->nc);
+}
+
+static NetClientInfo net_usbnet_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = usbnet_can_receive,
+ .receive = usbnet_receive,
+ .cleanup = usbnet_cleanup,
+};
+
+static int usb_net_initfn(USBDevice *dev)
+{
+ USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
+
+ usb_desc_init(dev);
+
+ s->rndis_state = RNDIS_UNINITIALIZED;
+ QTAILQ_INIT(&s->rndis_resp);
+
+ s->medium = 0; /* NDIS_MEDIUM_802_3 */
+ s->speed = 1000000; /* 100MBps, in 100Bps units */
+ s->media_state = 0; /* NDIS_MEDIA_STATE_CONNECTED */;
+ s->filter = 0;
+ s->vendorid = 0x1234;
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_usbnet_info, &s->conf,
+ s->dev.qdev.info->name, s->dev.qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
+ "%02x%02x%02x%02x%02x%02x",
+ 0x40,
+ s->conf.macaddr.a[1],
+ s->conf.macaddr.a[2],
+ s->conf.macaddr.a[3],
+ s->conf.macaddr.a[4],
+ s->conf.macaddr.a[5]);
+ usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac);
+
+ add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0");
+ return 0;
+}
+
+static USBDevice *usb_net_init(const char *cmdline)
+{
+ USBDevice *dev;
+ QemuOpts *opts;
+ int idx;
+
+ opts = qemu_opts_parse(qemu_find_opts("net"), cmdline, 0);
+ if (!opts) {
+ return NULL;
+ }
+ qemu_opt_set(opts, "type", "nic");
+ qemu_opt_set(opts, "model", "usb");
+
+ idx = net_client_init(NULL, opts, 0);
+ if (idx == -1) {
+ return NULL;
+ }
+
+ dev = usb_create(NULL /* FIXME */, "usb-net");
+ if (!dev) {
+ return NULL;
+ }
+ qdev_set_nic_properties(&dev->qdev, &nd_table[idx]);
+ qdev_init_nofail(&dev->qdev);
+ return dev;
+}
+
+static struct USBDeviceInfo net_info = {
+ .product_desc = "QEMU USB Network Interface",
+ .qdev.name = "usb-net",
+ .qdev.fw_name = "network",
+ .qdev.size = sizeof(USBNetState),
+ .usb_desc = &desc_net,
+ .init = usb_net_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_net_handle_reset,
+ .handle_control = usb_net_handle_control,
+ .handle_data = usb_net_handle_data,
+ .handle_destroy = usb_net_handle_destroy,
+ .usbdevice_name = "net",
+ .usbdevice_init = usb_net_init,
+ .qdev.props = (Property[]) {
+ DEFINE_NIC_PROPERTIES(USBNetState, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void usb_net_register_devices(void)
+{
+ usb_qdev_register(&net_info);
+}
+device_init(usb_net_register_devices)
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
new file mode 100644
index 0000000..09ea0b6
--- /dev/null
+++ b/hw/usb-ohci.c
@@ -0,0 +1,1792 @@
+/*
+ * QEMU USB OHCI Emulation
+ * Copyright (c) 2004 Gianni Tedesco
+ * Copyright (c) 2006 CodeSourcery
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * 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/>.
+ *
+ * TODO:
+ * o Isochronous transfers
+ * o Allocate bandwidth in frames properly
+ * o Disable timers when nothing needs to be done, or remove timer usage
+ * all together.
+ * o Handle unrecoverable errors properly
+ * o BIOS work to boot from USB storage
+*/
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "usb-ohci.h"
+#include "sysbus.h"
+#include "qdev-addr.h"
+
+//#define DEBUG_OHCI
+/* Dump packet contents. */
+//#define DEBUG_PACKET
+//#define DEBUG_ISOCH
+/* This causes frames to occur 1000x slower */
+//#define OHCI_TIME_WARP 1
+
+#ifdef DEBUG_OHCI
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+/* Number of Downstream Ports on the root hub. */
+
+#define OHCI_MAX_PORTS 15
+
+static int64_t usb_frame_time;
+static int64_t usb_bit_time;
+
+typedef struct OHCIPort {
+ USBPort port;
+ uint32_t ctrl;
+} OHCIPort;
+
+typedef struct {
+ USBBus bus;
+ qemu_irq irq;
+ int mem;
+ int num_ports;
+ const char *name;
+
+ QEMUTimer *eof_timer;
+ int64_t sof_time;
+
+ /* OHCI state */
+ /* Control partition */
+ uint32_t ctl, status;
+ uint32_t intr_status;
+ uint32_t intr;
+
+ /* memory pointer partition */
+ uint32_t hcca;
+ uint32_t ctrl_head, ctrl_cur;
+ uint32_t bulk_head, bulk_cur;
+ uint32_t per_cur;
+ uint32_t done;
+ int done_count;
+
+ /* Frame counter partition */
+ uint32_t fsmps:15;
+ uint32_t fit:1;
+ uint32_t fi:14;
+ uint32_t frt:1;
+ uint16_t frame_number;
+ uint16_t padding;
+ uint32_t pstart;
+ uint32_t lst;
+
+ /* Root Hub partition */
+ uint32_t rhdesc_a, rhdesc_b;
+ uint32_t rhstatus;
+ OHCIPort rhport[OHCI_MAX_PORTS];
+
+ /* PXA27x Non-OHCI events */
+ uint32_t hstatus;
+ uint32_t hmask;
+ uint32_t hreset;
+ uint32_t htest;
+
+ /* SM501 local memory offset */
+ target_phys_addr_t localmem_base;
+
+ /* Active packets. */
+ uint32_t old_ctl;
+ USBPacket usb_packet;
+ uint8_t usb_buf[8192];
+ uint32_t async_td;
+ int async_complete;
+
+} OHCIState;
+
+/* Host Controller Communications Area */
+struct ohci_hcca {
+ uint32_t intr[32];
+ uint16_t frame, pad;
+ uint32_t done;
+};
+
+static void ohci_bus_stop(OHCIState *ohci);
+
+/* Bitfields for the first word of an Endpoint Desciptor. */
+#define OHCI_ED_FA_SHIFT 0
+#define OHCI_ED_FA_MASK (0x7f<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_EN_SHIFT 7
+#define OHCI_ED_EN_MASK (0xf<<OHCI_ED_EN_SHIFT)
+#define OHCI_ED_D_SHIFT 11
+#define OHCI_ED_D_MASK (3<<OHCI_ED_D_SHIFT)
+#define OHCI_ED_S (1<<13)
+#define OHCI_ED_K (1<<14)
+#define OHCI_ED_F (1<<15)
+#define OHCI_ED_MPS_SHIFT 16
+#define OHCI_ED_MPS_MASK (0x7ff<<OHCI_ED_MPS_SHIFT)
+
+/* Flags in the head field of an Endpoint Desciptor. */
+#define OHCI_ED_H 1
+#define OHCI_ED_C 2
+
+/* Bitfields for the first word of a Transfer Desciptor. */
+#define OHCI_TD_R (1<<18)
+#define OHCI_TD_DP_SHIFT 19
+#define OHCI_TD_DP_MASK (3<<OHCI_TD_DP_SHIFT)
+#define OHCI_TD_DI_SHIFT 21
+#define OHCI_TD_DI_MASK (7<<OHCI_TD_DI_SHIFT)
+#define OHCI_TD_T0 (1<<24)
+#define OHCI_TD_T1 (1<<24)
+#define OHCI_TD_EC_SHIFT 26
+#define OHCI_TD_EC_MASK (3<<OHCI_TD_EC_SHIFT)
+#define OHCI_TD_CC_SHIFT 28
+#define OHCI_TD_CC_MASK (0xf<<OHCI_TD_CC_SHIFT)
+
+/* Bitfields for the first word of an Isochronous Transfer Desciptor. */
+/* CC & DI - same as in the General Transfer Desciptor */
+#define OHCI_TD_SF_SHIFT 0
+#define OHCI_TD_SF_MASK (0xffff<<OHCI_TD_SF_SHIFT)
+#define OHCI_TD_FC_SHIFT 24
+#define OHCI_TD_FC_MASK (7<<OHCI_TD_FC_SHIFT)
+
+/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
+#define OHCI_TD_PSW_CC_SHIFT 12
+#define OHCI_TD_PSW_CC_MASK (0xf<<OHCI_TD_PSW_CC_SHIFT)
+#define OHCI_TD_PSW_SIZE_SHIFT 0
+#define OHCI_TD_PSW_SIZE_MASK (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
+
+#define OHCI_PAGE_MASK 0xfffff000
+#define OHCI_OFFSET_MASK 0xfff
+
+#define OHCI_DPTR_MASK 0xfffffff0
+
+#define OHCI_BM(val, field) \
+ (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)
+
+#define OHCI_SET_BM(val, field, newval) do { \
+ val &= ~OHCI_##field##_MASK; \
+ val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \
+ } while(0)
+
+/* endpoint descriptor */
+struct ohci_ed {
+ uint32_t flags;
+ uint32_t tail;
+ uint32_t head;
+ uint32_t next;
+};
+
+/* General transfer descriptor */
+struct ohci_td {
+ uint32_t flags;
+ uint32_t cbp;
+ uint32_t next;
+ uint32_t be;
+};
+
+/* Isochronous transfer descriptor */
+struct ohci_iso_td {
+ uint32_t flags;
+ uint32_t bp;
+ uint32_t next;
+ uint32_t be;
+ uint16_t offset[8];
+};
+
+#define USB_HZ 12000000
+
+/* OHCI Local stuff */
+#define OHCI_CTL_CBSR ((1<<0)|(1<<1))
+#define OHCI_CTL_PLE (1<<2)
+#define OHCI_CTL_IE (1<<3)
+#define OHCI_CTL_CLE (1<<4)
+#define OHCI_CTL_BLE (1<<5)
+#define OHCI_CTL_HCFS ((1<<6)|(1<<7))
+#define OHCI_USB_RESET 0x00
+#define OHCI_USB_RESUME 0x40
+#define OHCI_USB_OPERATIONAL 0x80
+#define OHCI_USB_SUSPEND 0xc0
+#define OHCI_CTL_IR (1<<8)
+#define OHCI_CTL_RWC (1<<9)
+#define OHCI_CTL_RWE (1<<10)
+
+#define OHCI_STATUS_HCR (1<<0)
+#define OHCI_STATUS_CLF (1<<1)
+#define OHCI_STATUS_BLF (1<<2)
+#define OHCI_STATUS_OCR (1<<3)
+#define OHCI_STATUS_SOC ((1<<6)|(1<<7))
+
+#define OHCI_INTR_SO (1<<0) /* Scheduling overrun */
+#define OHCI_INTR_WD (1<<1) /* HcDoneHead writeback */
+#define OHCI_INTR_SF (1<<2) /* Start of frame */
+#define OHCI_INTR_RD (1<<3) /* Resume detect */
+#define OHCI_INTR_UE (1<<4) /* Unrecoverable error */
+#define OHCI_INTR_FNO (1<<5) /* Frame number overflow */
+#define OHCI_INTR_RHSC (1<<6) /* Root hub status change */
+#define OHCI_INTR_OC (1<<30) /* Ownership change */
+#define OHCI_INTR_MIE (1<<31) /* Master Interrupt Enable */
+
+#define OHCI_HCCA_SIZE 0x100
+#define OHCI_HCCA_MASK 0xffffff00
+
+#define OHCI_EDPTR_MASK 0xfffffff0
+
+#define OHCI_FMI_FI 0x00003fff
+#define OHCI_FMI_FSMPS 0xffff0000
+#define OHCI_FMI_FIT 0x80000000
+
+#define OHCI_FR_RT (1<<31)
+
+#define OHCI_LS_THRESH 0x628
+
+#define OHCI_RHA_RW_MASK 0x00000000 /* Mask of supported features. */
+#define OHCI_RHA_PSM (1<<8)
+#define OHCI_RHA_NPS (1<<9)
+#define OHCI_RHA_DT (1<<10)
+#define OHCI_RHA_OCPM (1<<11)
+#define OHCI_RHA_NOCP (1<<12)
+#define OHCI_RHA_POTPGT_MASK 0xff000000
+
+#define OHCI_RHS_LPS (1<<0)
+#define OHCI_RHS_OCI (1<<1)
+#define OHCI_RHS_DRWE (1<<15)
+#define OHCI_RHS_LPSC (1<<16)
+#define OHCI_RHS_OCIC (1<<17)
+#define OHCI_RHS_CRWE (1<<31)
+
+#define OHCI_PORT_CCS (1<<0)
+#define OHCI_PORT_PES (1<<1)
+#define OHCI_PORT_PSS (1<<2)
+#define OHCI_PORT_POCI (1<<3)
+#define OHCI_PORT_PRS (1<<4)
+#define OHCI_PORT_PPS (1<<8)
+#define OHCI_PORT_LSDA (1<<9)
+#define OHCI_PORT_CSC (1<<16)
+#define OHCI_PORT_PESC (1<<17)
+#define OHCI_PORT_PSSC (1<<18)
+#define OHCI_PORT_OCIC (1<<19)
+#define OHCI_PORT_PRSC (1<<20)
+#define OHCI_PORT_WTC (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
+ |OHCI_PORT_OCIC|OHCI_PORT_PRSC)
+
+#define OHCI_TD_DIR_SETUP 0x0
+#define OHCI_TD_DIR_OUT 0x1
+#define OHCI_TD_DIR_IN 0x2
+#define OHCI_TD_DIR_RESERVED 0x3
+
+#define OHCI_CC_NOERROR 0x0
+#define OHCI_CC_CRC 0x1
+#define OHCI_CC_BITSTUFFING 0x2
+#define OHCI_CC_DATATOGGLEMISMATCH 0x3
+#define OHCI_CC_STALL 0x4
+#define OHCI_CC_DEVICENOTRESPONDING 0x5
+#define OHCI_CC_PIDCHECKFAILURE 0x6
+#define OHCI_CC_UNDEXPETEDPID 0x7
+#define OHCI_CC_DATAOVERRUN 0x8
+#define OHCI_CC_DATAUNDERRUN 0x9
+#define OHCI_CC_BUFFEROVERRUN 0xc
+#define OHCI_CC_BUFFERUNDERRUN 0xd
+
+#define OHCI_HRESET_FSBIR (1 << 0)
+
+/* Update IRQ levels */
+static inline void ohci_intr_update(OHCIState *ohci)
+{
+ int level = 0;
+
+ if ((ohci->intr & OHCI_INTR_MIE) &&
+ (ohci->intr_status & ohci->intr))
+ level = 1;
+
+ qemu_set_irq(ohci->irq, level);
+}
+
+/* Set an interrupt */
+static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
+{
+ ohci->intr_status |= intr;
+ ohci_intr_update(ohci);
+}
+
+/* Attach or detach a device on a root hub port. */
+static void ohci_attach(USBPort *port1)
+{
+ OHCIState *s = port1->opaque;
+ OHCIPort *port = &s->rhport[port1->index];
+
+ /* set connect status */
+ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
+
+ /* update speed */
+ if (port->port.dev->speed == USB_SPEED_LOW) {
+ port->ctrl |= OHCI_PORT_LSDA;
+ } else {
+ port->ctrl &= ~OHCI_PORT_LSDA;
+ }
+
+ /* notify of remote-wakeup */
+ if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
+ ohci_set_interrupt(s, OHCI_INTR_RD);
+ }
+
+ DPRINTF("usb-ohci: Attached port %d\n", port1->index);
+}
+
+static void ohci_detach(USBPort *port1)
+{
+ OHCIState *s = port1->opaque;
+ OHCIPort *port = &s->rhport[port1->index];
+ uint32_t old_state = port->ctrl;
+
+ /* set connect status */
+ if (port->ctrl & OHCI_PORT_CCS) {
+ port->ctrl &= ~OHCI_PORT_CCS;
+ port->ctrl |= OHCI_PORT_CSC;
+ }
+ /* disable port */
+ if (port->ctrl & OHCI_PORT_PES) {
+ port->ctrl &= ~OHCI_PORT_PES;
+ port->ctrl |= OHCI_PORT_PESC;
+ }
+ DPRINTF("usb-ohci: Detached port %d\n", port1->index);
+
+ if (old_state != port->ctrl)
+ ohci_set_interrupt(s, OHCI_INTR_RHSC);
+}
+
+/* Reset the controller */
+static void ohci_reset(void *opaque)
+{
+ OHCIState *ohci = opaque;
+ OHCIPort *port;
+ int i;
+
+ ohci_bus_stop(ohci);
+ ohci->ctl = 0;
+ ohci->old_ctl = 0;
+ ohci->status = 0;
+ ohci->intr_status = 0;
+ ohci->intr = OHCI_INTR_MIE;
+
+ ohci->hcca = 0;
+ ohci->ctrl_head = ohci->ctrl_cur = 0;
+ ohci->bulk_head = ohci->bulk_cur = 0;
+ ohci->per_cur = 0;
+ ohci->done = 0;
+ ohci->done_count = 7;
+
+ /* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
+ * I took the value linux sets ...
+ */
+ ohci->fsmps = 0x2778;
+ ohci->fi = 0x2edf;
+ ohci->fit = 0;
+ ohci->frt = 0;
+ ohci->frame_number = 0;
+ ohci->pstart = 0;
+ ohci->lst = OHCI_LS_THRESH;
+
+ ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+ ohci->rhdesc_b = 0x0; /* Impl. specific */
+ ohci->rhstatus = 0;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ {
+ port = &ohci->rhport[i];
+ port->ctrl = 0;
+ if (port->port.dev) {
+ usb_attach(&port->port, port->port.dev);
+ }
+ }
+ if (ohci->async_td) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ DPRINTF("usb-ohci: Reset %s\n", ohci->name);
+}
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(OHCIState *ohci,
+ uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ addr += ohci->localmem_base;
+
+ 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(OHCIState *ohci,
+ uint32_t addr, uint32_t *buf, int num)
+{
+ int i;
+
+ addr += ohci->localmem_base;
+
+ 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;
+}
+
+/* Get an array of words from main memory */
+static inline int get_words(OHCIState *ohci,
+ uint32_t addr, uint16_t *buf, int num)
+{
+ int i;
+
+ addr += ohci->localmem_base;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+ *buf = le16_to_cpu(*buf);
+ }
+
+ return 1;
+}
+
+/* Put an array of words in to main memory */
+static inline int put_words(OHCIState *ohci,
+ uint32_t addr, uint16_t *buf, int num)
+{
+ int i;
+
+ addr += ohci->localmem_base;
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ uint16_t tmp = cpu_to_le16(*buf);
+ cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+ }
+
+ return 1;
+}
+
+static inline int ohci_read_ed(OHCIState *ohci,
+ uint32_t addr, struct ohci_ed *ed)
+{
+ return get_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_read_td(OHCIState *ohci,
+ uint32_t addr, struct ohci_td *td)
+{
+ return get_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_read_iso_td(OHCIState *ohci,
+ uint32_t addr, struct ohci_iso_td *td)
+{
+ return (get_dwords(ohci, addr, (uint32_t *)td, 4) &&
+ get_words(ohci, addr + 16, td->offset, 8));
+}
+
+static inline int ohci_read_hcca(OHCIState *ohci,
+ uint32_t addr, struct ohci_hcca *hcca)
+{
+ cpu_physical_memory_rw(addr + ohci->localmem_base,
+ (uint8_t *)hcca, sizeof(*hcca), 0);
+ return 1;
+}
+
+static inline int ohci_put_ed(OHCIState *ohci,
+ uint32_t addr, struct ohci_ed *ed)
+{
+ return put_dwords(ohci, addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_put_td(OHCIState *ohci,
+ uint32_t addr, struct ohci_td *td)
+{
+ return put_dwords(ohci, addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_put_iso_td(OHCIState *ohci,
+ uint32_t addr, struct ohci_iso_td *td)
+{
+ return (put_dwords(ohci, addr, (uint32_t *)td, 4) &&
+ put_words(ohci, addr + 16, td->offset, 8));
+}
+
+static inline int ohci_put_hcca(OHCIState *ohci,
+ uint32_t addr, struct ohci_hcca *hcca)
+{
+ cpu_physical_memory_rw(addr + ohci->localmem_base,
+ (uint8_t *)hcca, sizeof(*hcca), 1);
+ return 1;
+}
+
+/* Read/Write the contents of a TD from/to main memory. */
+static void ohci_copy_td(OHCIState *ohci, struct ohci_td *td,
+ uint8_t *buf, int len, int write)
+{
+ uint32_t ptr;
+ uint32_t n;
+
+ ptr = td->cbp;
+ n = 0x1000 - (ptr & 0xfff);
+ if (n > len)
+ n = len;
+ cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
+ if (n == len)
+ return;
+ ptr = td->be & ~0xfffu;
+ buf += n;
+ cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
+}
+
+/* Read/Write the contents of an ISO TD from/to main memory. */
+static void ohci_copy_iso_td(OHCIState *ohci,
+ uint32_t start_addr, uint32_t end_addr,
+ uint8_t *buf, int len, int write)
+{
+ uint32_t ptr;
+ uint32_t n;
+
+ ptr = start_addr;
+ n = 0x1000 - (ptr & 0xfff);
+ if (n > len)
+ n = len;
+ cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, n, write);
+ if (n == len)
+ return;
+ ptr = end_addr & ~0xfffu;
+ buf += n;
+ cpu_physical_memory_rw(ptr + ohci->localmem_base, buf, len - n, write);
+}
+
+static void ohci_process_lists(OHCIState *ohci, int completion);
+
+static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
+{
+ OHCIState *ohci = opaque;
+#ifdef DEBUG_PACKET
+ DPRINTF("Async packet complete\n");
+#endif
+ ohci->async_complete = 1;
+ ohci_process_lists(ohci, 1);
+}
+
+#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
+
+static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
+ int completion)
+{
+ int dir;
+ size_t len = 0;
+#ifdef DEBUG_ISOCH
+ const char *str = NULL;
+#endif
+ int pid;
+ int ret;
+ int i;
+ USBDevice *dev;
+ struct ohci_iso_td iso_td;
+ uint32_t addr;
+ uint16_t starting_frame;
+ int16_t relative_frame_number;
+ int frame_count;
+ uint32_t start_offset, next_offset, end_offset = 0;
+ uint32_t start_addr, end_addr;
+
+ addr = ed->head & OHCI_DPTR_MASK;
+
+ if (!ohci_read_iso_td(ohci, addr, &iso_td)) {
+ printf("usb-ohci: ISO_TD read error at %x\n", addr);
+ return 0;
+ }
+
+ starting_frame = OHCI_BM(iso_td.flags, TD_SF);
+ frame_count = OHCI_BM(iso_td.flags, TD_FC);
+ relative_frame_number = USUB(ohci->frame_number, starting_frame);
+
+#ifdef DEBUG_ISOCH
+ printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
+ "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+ "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+ "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+ "frame_number 0x%.8x starting_frame 0x%.8x\n"
+ "frame_count 0x%.8x relative %d\n"
+ "di 0x%.8x cc 0x%.8x\n",
+ ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
+ iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
+ iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
+ iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
+ ohci->frame_number, starting_frame,
+ frame_count, relative_frame_number,
+ OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
+#endif
+
+ if (relative_frame_number < 0) {
+ DPRINTF("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+ return 1;
+ } else if (relative_frame_number > frame_count) {
+ /* ISO TD expired - retire the TD to the Done Queue and continue with
+ the next ISO TD of the same ED */
+ DPRINTF("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number,
+ frame_count);
+ OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+ iso_td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(iso_td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ ohci_put_iso_td(ohci, addr, &iso_td);
+ return 0;
+ }
+
+ dir = OHCI_BM(ed->flags, ED_D);
+ switch (dir) {
+ case OHCI_TD_DIR_IN:
+#ifdef DEBUG_ISOCH
+ str = "in";
+#endif
+ pid = USB_TOKEN_IN;
+ break;
+ case OHCI_TD_DIR_OUT:
+#ifdef DEBUG_ISOCH
+ str = "out";
+#endif
+ pid = USB_TOKEN_OUT;
+ break;
+ case OHCI_TD_DIR_SETUP:
+#ifdef DEBUG_ISOCH
+ str = "setup";
+#endif
+ pid = USB_TOKEN_SETUP;
+ break;
+ default:
+ printf("usb-ohci: Bad direction %d\n", dir);
+ return 1;
+ }
+
+ if (!iso_td.bp || !iso_td.be) {
+ printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
+ return 1;
+ }
+
+ start_offset = iso_td.offset[relative_frame_number];
+ next_offset = iso_td.offset[relative_frame_number + 1];
+
+ if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) ||
+ ((relative_frame_number < frame_count) &&
+ !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
+ printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
+ start_offset, next_offset);
+ return 1;
+ }
+
+ if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
+ printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
+ start_offset, next_offset);
+ return 1;
+ }
+
+ if ((start_offset & 0x1000) == 0) {
+ start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+ (start_offset & OHCI_OFFSET_MASK);
+ } else {
+ start_addr = (iso_td.be & OHCI_PAGE_MASK) |
+ (start_offset & OHCI_OFFSET_MASK);
+ }
+
+ if (relative_frame_number < frame_count) {
+ end_offset = next_offset - 1;
+ if ((end_offset & 0x1000) == 0) {
+ end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+ (end_offset & OHCI_OFFSET_MASK);
+ } else {
+ end_addr = (iso_td.be & OHCI_PAGE_MASK) |
+ (end_offset & OHCI_OFFSET_MASK);
+ }
+ } else {
+ /* Last packet in the ISO TD */
+ end_addr = iso_td.be;
+ }
+
+ if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
+ len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
+ - (start_addr & OHCI_OFFSET_MASK);
+ } else {
+ len = end_addr - start_addr + 1;
+ }
+
+ if (len && dir != OHCI_TD_DIR_IN) {
+ ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, len, 0);
+ }
+
+ if (completion) {
+ ret = ohci->usb_packet.len;
+ } else {
+ ret = USB_RET_NODEV;
+ for (i = 0; i < ohci->num_ports; i++) {
+ dev = ohci->rhport[i].port.dev;
+ if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+ continue;
+ ohci->usb_packet.pid = pid;
+ ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+ ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+ ohci->usb_packet.data = ohci->usb_buf;
+ ohci->usb_packet.len = len;
+ ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+ ohci->usb_packet.complete_opaque = ohci;
+ ret = dev->info->handle_packet(dev, &ohci->usb_packet);
+ if (ret != USB_RET_NODEV)
+ break;
+ }
+
+ if (ret == USB_RET_ASYNC) {
+ return 1;
+ }
+ }
+
+#ifdef DEBUG_ISOCH
+ printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
+ start_offset, end_offset, start_addr, end_addr, str, len, ret);
+#endif
+
+ /* Writeback */
+ if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
+ /* IN transfer succeeded */
+ ohci_copy_iso_td(ohci, start_addr, end_addr, ohci->usb_buf, ret, 1);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_NOERROR);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
+ } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
+ /* OUT transfer succeeded */
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_NOERROR);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
+ } else {
+ if (ret > (ssize_t) len) {
+ printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_DATAOVERRUN);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+ len);
+ } else if (ret >= 0) {
+ printf("usb-ohci: DataUnderrun %d\n", ret);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_DATAUNDERRUN);
+ } else {
+ switch (ret) {
+ case USB_RET_NODEV:
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_DEVICENOTRESPONDING);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+ 0);
+ break;
+ case USB_RET_NAK:
+ case USB_RET_STALL:
+ printf("usb-ohci: got NAK/STALL %d\n", ret);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_STALL);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+ 0);
+ break;
+ default:
+ printf("usb-ohci: Bad device response %d\n", ret);
+ OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+ OHCI_CC_UNDEXPETEDPID);
+ break;
+ }
+ }
+ }
+
+ if (relative_frame_number == frame_count) {
+ /* Last data packet of ISO TD - retire the TD to the Done Queue */
+ OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+ iso_td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(iso_td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ }
+ ohci_put_iso_td(ohci, addr, &iso_td);
+ return 1;
+}
+
+/* Service a transport descriptor.
+ Returns nonzero to terminate processing of this endpoint. */
+
+static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
+{
+ int dir;
+ size_t len = 0;
+#ifdef DEBUG_PACKET
+ const char *str = NULL;
+#endif
+ int pid;
+ int ret;
+ int i;
+ USBDevice *dev;
+ struct ohci_td td;
+ uint32_t addr;
+ int flag_r;
+ int completion;
+
+ addr = ed->head & OHCI_DPTR_MASK;
+ /* See if this TD has already been submitted to the device. */
+ completion = (addr == ohci->async_td);
+ if (completion && !ohci->async_complete) {
+#ifdef DEBUG_PACKET
+ DPRINTF("Skipping async TD\n");
+#endif
+ return 1;
+ }
+ if (!ohci_read_td(ohci, addr, &td)) {
+ fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
+ return 0;
+ }
+
+ dir = OHCI_BM(ed->flags, ED_D);
+ switch (dir) {
+ case OHCI_TD_DIR_OUT:
+ case OHCI_TD_DIR_IN:
+ /* Same value. */
+ break;
+ default:
+ dir = OHCI_BM(td.flags, TD_DP);
+ break;
+ }
+
+ switch (dir) {
+ case OHCI_TD_DIR_IN:
+#ifdef DEBUG_PACKET
+ str = "in";
+#endif
+ pid = USB_TOKEN_IN;
+ break;
+ case OHCI_TD_DIR_OUT:
+#ifdef DEBUG_PACKET
+ str = "out";
+#endif
+ pid = USB_TOKEN_OUT;
+ break;
+ case OHCI_TD_DIR_SETUP:
+#ifdef DEBUG_PACKET
+ str = "setup";
+#endif
+ pid = USB_TOKEN_SETUP;
+ break;
+ default:
+ fprintf(stderr, "usb-ohci: Bad direction\n");
+ return 1;
+ }
+ if (td.cbp && td.be) {
+ if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) {
+ len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff);
+ } else {
+ len = (td.be - td.cbp) + 1;
+ }
+
+ if (len && dir != OHCI_TD_DIR_IN && !completion) {
+ ohci_copy_td(ohci, &td, ohci->usb_buf, len, 0);
+ }
+ }
+
+ flag_r = (td.flags & OHCI_TD_R) != 0;
+#ifdef DEBUG_PACKET
+ DPRINTF(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
+ addr, (int64_t)len, str, flag_r, td.cbp, td.be);
+
+ if (len > 0 && dir != OHCI_TD_DIR_IN) {
+ DPRINTF(" data:");
+ for (i = 0; i < len; i++)
+ printf(" %.2x", ohci->usb_buf[i]);
+ DPRINTF("\n");
+ }
+#endif
+ if (completion) {
+ ret = ohci->usb_packet.len;
+ ohci->async_td = 0;
+ ohci->async_complete = 0;
+ } else {
+ ret = USB_RET_NODEV;
+ for (i = 0; i < ohci->num_ports; i++) {
+ dev = ohci->rhport[i].port.dev;
+ if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+ continue;
+
+ if (ohci->async_td) {
+ /* ??? The hardware should allow one active packet per
+ endpoint. We only allow one active packet per controller.
+ This should be sufficient as long as devices respond in a
+ timely manner.
+ */
+#ifdef DEBUG_PACKET
+ DPRINTF("Too many pending packets\n");
+#endif
+ return 1;
+ }
+ ohci->usb_packet.pid = pid;
+ ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+ ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+ ohci->usb_packet.data = ohci->usb_buf;
+ ohci->usb_packet.len = len;
+ ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+ ohci->usb_packet.complete_opaque = ohci;
+ ret = dev->info->handle_packet(dev, &ohci->usb_packet);
+ if (ret != USB_RET_NODEV)
+ break;
+ }
+#ifdef DEBUG_PACKET
+ DPRINTF("ret=%d\n", ret);
+#endif
+ if (ret == USB_RET_ASYNC) {
+ ohci->async_td = addr;
+ return 1;
+ }
+ }
+ if (ret >= 0) {
+ if (dir == OHCI_TD_DIR_IN) {
+ ohci_copy_td(ohci, &td, ohci->usb_buf, ret, 1);
+#ifdef DEBUG_PACKET
+ DPRINTF(" data:");
+ for (i = 0; i < ret; i++)
+ printf(" %.2x", ohci->usb_buf[i]);
+ DPRINTF("\n");
+#endif
+ } else {
+ ret = len;
+ }
+ }
+
+ /* Writeback */
+ if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
+ /* Transmission succeeded. */
+ if (ret == len) {
+ td.cbp = 0;
+ } else {
+ td.cbp += ret;
+ if ((td.cbp & 0xfff) + ret > 0xfff) {
+ td.cbp &= 0xfff;
+ td.cbp |= td.be & ~0xfff;
+ }
+ }
+ td.flags |= OHCI_TD_T1;
+ td.flags ^= OHCI_TD_T0;
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
+ OHCI_SET_BM(td.flags, TD_EC, 0);
+
+ ed->head &= ~OHCI_ED_C;
+ if (td.flags & OHCI_TD_T0)
+ ed->head |= OHCI_ED_C;
+ } else {
+ if (ret >= 0) {
+ DPRINTF("usb-ohci: Underrun\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
+ } else {
+ switch (ret) {
+ case USB_RET_NODEV:
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
+ case USB_RET_NAK:
+ DPRINTF("usb-ohci: got NAK\n");
+ return 1;
+ case USB_RET_STALL:
+ DPRINTF("usb-ohci: got STALL\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
+ break;
+ case USB_RET_BABBLE:
+ DPRINTF("usb-ohci: got BABBLE\n");
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+ break;
+ default:
+ fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
+ OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
+ OHCI_SET_BM(td.flags, TD_EC, 3);
+ break;
+ }
+ }
+ ed->head |= OHCI_ED_H;
+ }
+
+ /* Retire this TD */
+ ed->head &= ~OHCI_DPTR_MASK;
+ ed->head |= td.next & OHCI_DPTR_MASK;
+ td.next = ohci->done;
+ ohci->done = addr;
+ i = OHCI_BM(td.flags, TD_DI);
+ if (i < ohci->done_count)
+ ohci->done_count = i;
+ ohci_put_td(ohci, addr, &td);
+ return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
+}
+
+/* Service an endpoint list. Returns nonzero if active TD were found. */
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
+{
+ struct ohci_ed ed;
+ uint32_t next_ed;
+ uint32_t cur;
+ int active;
+
+ active = 0;
+
+ if (head == 0)
+ return 0;
+
+ for (cur = head; cur; cur = next_ed) {
+ if (!ohci_read_ed(ohci, cur, &ed)) {
+ fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
+ return 0;
+ }
+
+ next_ed = ed.next & OHCI_DPTR_MASK;
+
+ if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
+ uint32_t addr;
+ /* Cancel pending packets for ED that have been paused. */
+ addr = ed.head & OHCI_DPTR_MASK;
+ if (ohci->async_td && addr == ohci->async_td) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ continue;
+ }
+
+ while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
+#ifdef DEBUG_PACKET
+ DPRINTF("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
+ "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
+ OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
+ OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
+ (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
+ OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
+ (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
+ ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
+#endif
+ active = 1;
+
+ if ((ed.flags & OHCI_ED_F) == 0) {
+ if (ohci_service_td(ohci, &ed))
+ break;
+ } else {
+ /* Handle isochronous endpoints */
+ if (ohci_service_iso_td(ohci, &ed, completion))
+ break;
+ }
+ }
+
+ ohci_put_ed(ohci, cur, &ed);
+ }
+
+ return active;
+}
+
+/* Generate a SOF event, and set a timer for EOF */
+static void ohci_sof(OHCIState *ohci)
+{
+ ohci->sof_time = qemu_get_clock(vm_clock);
+ qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+ ohci_set_interrupt(ohci, OHCI_INTR_SF);
+}
+
+/* Process Control and Bulk lists. */
+static void ohci_process_lists(OHCIState *ohci, int completion)
+{
+ if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
+ if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
+ DPRINTF("usb-ohci: head %x, cur %x\n",
+ ohci->ctrl_head, ohci->ctrl_cur);
+ }
+ if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
+ ohci->ctrl_cur = 0;
+ ohci->status &= ~OHCI_STATUS_CLF;
+ }
+ }
+
+ if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
+ if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
+ ohci->bulk_cur = 0;
+ ohci->status &= ~OHCI_STATUS_BLF;
+ }
+ }
+}
+
+/* Do frame processing on frame boundary */
+static void ohci_frame_boundary(void *opaque)
+{
+ OHCIState *ohci = opaque;
+ struct ohci_hcca hcca;
+
+ ohci_read_hcca(ohci, ohci->hcca, &hcca);
+
+ /* Process all the lists at the end of the frame */
+ if (ohci->ctl & OHCI_CTL_PLE) {
+ int n;
+
+ n = ohci->frame_number & 0x1f;
+ ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
+ }
+
+ /* Cancel all pending packets if either of the lists has been disabled. */
+ if (ohci->async_td &&
+ ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
+ usb_cancel_packet(&ohci->usb_packet);
+ ohci->async_td = 0;
+ }
+ ohci->old_ctl = ohci->ctl;
+ ohci_process_lists(ohci, 0);
+
+ /* Frame boundary, so do EOF stuf here */
+ ohci->frt = ohci->fit;
+
+ /* Increment frame number and take care of endianness. */
+ ohci->frame_number = (ohci->frame_number + 1) & 0xffff;
+ hcca.frame = cpu_to_le16(ohci->frame_number);
+
+ if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
+ if (!ohci->done)
+ abort();
+ if (ohci->intr & ohci->intr_status)
+ ohci->done |= 1;
+ hcca.done = cpu_to_le32(ohci->done);
+ ohci->done = 0;
+ ohci->done_count = 7;
+ ohci_set_interrupt(ohci, OHCI_INTR_WD);
+ }
+
+ if (ohci->done_count != 7 && ohci->done_count != 0)
+ ohci->done_count--;
+
+ /* Do SOF stuff here */
+ ohci_sof(ohci);
+
+ /* Writeback HCCA */
+ ohci_put_hcca(ohci, ohci->hcca, &hcca);
+}
+
+/* Start sending SOF tokens across the USB bus, lists are processed in
+ * next frame
+ */
+static int ohci_bus_start(OHCIState *ohci)
+{
+ ohci->eof_timer = qemu_new_timer(vm_clock,
+ ohci_frame_boundary,
+ ohci);
+
+ if (ohci->eof_timer == NULL) {
+ fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", ohci->name);
+ /* TODO: Signal unrecoverable error */
+ return 0;
+ }
+
+ DPRINTF("usb-ohci: %s: USB Operational\n", ohci->name);
+
+ ohci_sof(ohci);
+
+ return 1;
+}
+
+/* Stop sending SOF tokens on the bus */
+static void ohci_bus_stop(OHCIState *ohci)
+{
+ if (ohci->eof_timer)
+ qemu_del_timer(ohci->eof_timer);
+ ohci->eof_timer = NULL;
+}
+
+/* Sets a flag in a port status register but only set it if the port is
+ * connected, if not set ConnectStatusChange flag. If flag is enabled
+ * return 1.
+ */
+static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val)
+{
+ int ret = 1;
+
+ /* writing a 0 has no effect */
+ if (val == 0)
+ return 0;
+
+ /* If CurrentConnectStatus is cleared we set
+ * ConnectStatusChange
+ */
+ if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
+ ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
+ if (ohci->rhstatus & OHCI_RHS_DRWE) {
+ /* TODO: CSC is a wakeup event */
+ }
+ return 0;
+ }
+
+ if (ohci->rhport[i].ctrl & val)
+ ret = 0;
+
+ /* set the bit */
+ ohci->rhport[i].ctrl |= val;
+
+ return ret;
+}
+
+/* Set the frame interval - frame interval toggle is manipulated by the hcd only */
+static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
+{
+ val &= OHCI_FMI_FI;
+
+ if (val != ohci->fi) {
+ DPRINTF("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
+ ohci->name, ohci->fi, ohci->fi);
+ }
+
+ ohci->fi = val;
+}
+
+static void ohci_port_power(OHCIState *ohci, int i, int p)
+{
+ if (p) {
+ ohci->rhport[i].ctrl |= OHCI_PORT_PPS;
+ } else {
+ ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS|
+ OHCI_PORT_CCS|
+ OHCI_PORT_PSS|
+ OHCI_PORT_PRS);
+ }
+}
+
+/* Set HcControlRegister */
+static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
+{
+ uint32_t old_state;
+ uint32_t new_state;
+
+ old_state = ohci->ctl & OHCI_CTL_HCFS;
+ ohci->ctl = val;
+ new_state = ohci->ctl & OHCI_CTL_HCFS;
+
+ /* no state change */
+ if (old_state == new_state)
+ return;
+
+ switch (new_state) {
+ case OHCI_USB_OPERATIONAL:
+ ohci_bus_start(ohci);
+ break;
+ case OHCI_USB_SUSPEND:
+ ohci_bus_stop(ohci);
+ DPRINTF("usb-ohci: %s: USB Suspended\n", ohci->name);
+ break;
+ case OHCI_USB_RESUME:
+ DPRINTF("usb-ohci: %s: USB Resume\n", ohci->name);
+ break;
+ case OHCI_USB_RESET:
+ ohci_reset(ohci);
+ DPRINTF("usb-ohci: %s: USB Reset\n", ohci->name);
+ break;
+ }
+}
+
+static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
+{
+ uint16_t fr;
+ int64_t tks;
+
+ if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL)
+ return (ohci->frt << 31);
+
+ /* Being in USB operational state guarnatees sof_time was
+ * set already.
+ */
+ tks = qemu_get_clock(vm_clock) - ohci->sof_time;
+
+ /* avoid muldiv if possible */
+ if (tks >= usb_frame_time)
+ return (ohci->frt << 31);
+
+ tks = muldiv64(1, tks, usb_bit_time);
+ fr = (uint16_t)(ohci->fi - tks);
+
+ return (ohci->frt << 31) | fr;
+}
+
+
+/* Set root hub status */
+static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
+{
+ uint32_t old_state;
+
+ old_state = ohci->rhstatus;
+
+ /* write 1 to clear OCIC */
+ if (val & OHCI_RHS_OCIC)
+ ohci->rhstatus &= ~OHCI_RHS_OCIC;
+
+ if (val & OHCI_RHS_LPS) {
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ ohci_port_power(ohci, i, 0);
+ DPRINTF("usb-ohci: powered down all ports\n");
+ }
+
+ if (val & OHCI_RHS_LPSC) {
+ int i;
+
+ for (i = 0; i < ohci->num_ports; i++)
+ ohci_port_power(ohci, i, 1);
+ DPRINTF("usb-ohci: powered up all ports\n");
+ }
+
+ if (val & OHCI_RHS_DRWE)
+ ohci->rhstatus |= OHCI_RHS_DRWE;
+
+ if (val & OHCI_RHS_CRWE)
+ ohci->rhstatus &= ~OHCI_RHS_DRWE;
+
+ if (old_state != ohci->rhstatus)
+ ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+}
+
+/* Set root hub port status */
+static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
+{
+ uint32_t old_state;
+ OHCIPort *port;
+
+ port = &ohci->rhport[portnum];
+ old_state = port->ctrl;
+
+ /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */
+ if (val & OHCI_PORT_WTC)
+ port->ctrl &= ~(val & OHCI_PORT_WTC);
+
+ if (val & OHCI_PORT_CCS)
+ port->ctrl &= ~OHCI_PORT_PES;
+
+ ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
+
+ if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) {
+ DPRINTF("usb-ohci: port %d: SUSPEND\n", portnum);
+ }
+
+ if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
+ DPRINTF("usb-ohci: port %d: RESET\n", portnum);
+ usb_send_msg(port->port.dev, USB_MSG_RESET);
+ port->ctrl &= ~OHCI_PORT_PRS;
+ /* ??? Should this also set OHCI_PORT_PESC. */
+ port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
+ }
+
+ /* Invert order here to ensure in ambiguous case, device is
+ * powered up...
+ */
+ if (val & OHCI_PORT_LSDA)
+ ohci_port_power(ohci, portnum, 0);
+ if (val & OHCI_PORT_PPS)
+ ohci_port_power(ohci, portnum, 1);
+
+ if (old_state != port->ctrl)
+ ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+
+ return;
+}
+
+static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
+{
+ OHCIState *ohci = ptr;
+ uint32_t retval;
+
+ addr &= 0xff;
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+ return 0xffffffff;
+ } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+ /* HcRhPortStatus */
+ retval = ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS;
+ } else {
+ switch (addr >> 2) {
+ case 0: /* HcRevision */
+ retval = 0x10;
+ break;
+
+ case 1: /* HcControl */
+ retval = ohci->ctl;
+ break;
+
+ case 2: /* HcCommandStatus */
+ retval = ohci->status;
+ break;
+
+ case 3: /* HcInterruptStatus */
+ retval = ohci->intr_status;
+ break;
+
+ case 4: /* HcInterruptEnable */
+ case 5: /* HcInterruptDisable */
+ retval = ohci->intr;
+ break;
+
+ case 6: /* HcHCCA */
+ retval = ohci->hcca;
+ break;
+
+ case 7: /* HcPeriodCurrentED */
+ retval = ohci->per_cur;
+ break;
+
+ case 8: /* HcControlHeadED */
+ retval = ohci->ctrl_head;
+ break;
+
+ case 9: /* HcControlCurrentED */
+ retval = ohci->ctrl_cur;
+ break;
+
+ case 10: /* HcBulkHeadED */
+ retval = ohci->bulk_head;
+ break;
+
+ case 11: /* HcBulkCurrentED */
+ retval = ohci->bulk_cur;
+ break;
+
+ case 12: /* HcDoneHead */
+ retval = ohci->done;
+ break;
+
+ case 13: /* HcFmInterretval */
+ retval = (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi);
+ break;
+
+ case 14: /* HcFmRemaining */
+ retval = ohci_get_frame_remaining(ohci);
+ break;
+
+ case 15: /* HcFmNumber */
+ retval = ohci->frame_number;
+ break;
+
+ case 16: /* HcPeriodicStart */
+ retval = ohci->pstart;
+ break;
+
+ case 17: /* HcLSThreshold */
+ retval = ohci->lst;
+ break;
+
+ case 18: /* HcRhDescriptorA */
+ retval = ohci->rhdesc_a;
+ break;
+
+ case 19: /* HcRhDescriptorB */
+ retval = ohci->rhdesc_b;
+ break;
+
+ case 20: /* HcRhStatus */
+ retval = ohci->rhstatus;
+ break;
+
+ /* PXA27x specific registers */
+ case 24: /* HcStatus */
+ retval = ohci->hstatus & ohci->hmask;
+ break;
+
+ case 25: /* HcHReset */
+ retval = ohci->hreset;
+ break;
+
+ case 26: /* HcHInterruptEnable */
+ retval = ohci->hmask;
+ break;
+
+ case 27: /* HcHInterruptTest */
+ retval = ohci->htest;
+ break;
+
+ default:
+ fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
+ retval = 0xffffffff;
+ }
+ }
+
+ return retval;
+}
+
+static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ OHCIState *ohci = ptr;
+
+ addr &= 0xff;
+
+ /* Only aligned reads are allowed on OHCI */
+ if (addr & 3) {
+ fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+ return;
+ }
+
+ if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+ /* HcRhPortStatus */
+ ohci_port_set_status(ohci, (addr - 0x54) >> 2, val);
+ return;
+ }
+
+ switch (addr >> 2) {
+ case 1: /* HcControl */
+ ohci_set_ctl(ohci, val);
+ break;
+
+ case 2: /* HcCommandStatus */
+ /* SOC is read-only */
+ val = (val & ~OHCI_STATUS_SOC);
+
+ /* Bits written as '0' remain unchanged in the register */
+ ohci->status |= val;
+
+ if (ohci->status & OHCI_STATUS_HCR)
+ ohci_reset(ohci);
+ break;
+
+ case 3: /* HcInterruptStatus */
+ ohci->intr_status &= ~val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 4: /* HcInterruptEnable */
+ ohci->intr |= val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 5: /* HcInterruptDisable */
+ ohci->intr &= ~val;
+ ohci_intr_update(ohci);
+ break;
+
+ case 6: /* HcHCCA */
+ ohci->hcca = val & OHCI_HCCA_MASK;
+ break;
+
+ case 8: /* HcControlHeadED */
+ ohci->ctrl_head = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 9: /* HcControlCurrentED */
+ ohci->ctrl_cur = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 10: /* HcBulkHeadED */
+ ohci->bulk_head = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 11: /* HcBulkCurrentED */
+ ohci->bulk_cur = val & OHCI_EDPTR_MASK;
+ break;
+
+ case 13: /* HcFmInterval */
+ ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16;
+ ohci->fit = (val & OHCI_FMI_FIT) >> 31;
+ ohci_set_frame_interval(ohci, val);
+ break;
+
+ case 15: /* HcFmNumber */
+ break;
+
+ case 16: /* HcPeriodicStart */
+ ohci->pstart = val & 0xffff;
+ break;
+
+ case 17: /* HcLSThreshold */
+ ohci->lst = val & 0xffff;
+ break;
+
+ case 18: /* HcRhDescriptorA */
+ ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK;
+ ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK;
+ break;
+
+ case 19: /* HcRhDescriptorB */
+ break;
+
+ case 20: /* HcRhStatus */
+ ohci_set_hub_status(ohci, val);
+ break;
+
+ /* PXA27x specific registers */
+ case 24: /* HcStatus */
+ ohci->hstatus &= ~(val & ohci->hmask);
+
+ case 25: /* HcHReset */
+ ohci->hreset = val & ~OHCI_HRESET_FSBIR;
+ if (val & OHCI_HRESET_FSBIR)
+ ohci_reset(ohci);
+ break;
+
+ case 26: /* HcHInterruptEnable */
+ ohci->hmask = val;
+ break;
+
+ case 27: /* HcHInterruptTest */
+ ohci->htest = val;
+ break;
+
+ default:
+ fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
+ break;
+ }
+}
+
+/* Only dword reads are defined on OHCI register space */
+static CPUReadMemoryFunc * const ohci_readfn[3]={
+ ohci_mem_read,
+ ohci_mem_read,
+ ohci_mem_read
+};
+
+/* Only dword writes are defined on OHCI register space */
+static CPUWriteMemoryFunc * const ohci_writefn[3]={
+ ohci_mem_write,
+ ohci_mem_write,
+ ohci_mem_write
+};
+
+static USBPortOps ohci_port_ops = {
+ .attach = ohci_attach,
+ .detach = ohci_detach,
+};
+
+static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
+ int num_ports, uint32_t localmem_base)
+{
+ int i;
+
+ if (usb_frame_time == 0) {
+#ifdef OHCI_TIME_WARP
+ usb_frame_time = get_ticks_per_sec();
+ usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ/1000);
+#else
+ usb_frame_time = muldiv64(1, get_ticks_per_sec(), 1000);
+ if (get_ticks_per_sec() >= USB_HZ) {
+ usb_bit_time = muldiv64(1, get_ticks_per_sec(), USB_HZ);
+ } else {
+ usb_bit_time = 1;
+ }
+#endif
+ DPRINTF("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
+ usb_frame_time, usb_bit_time);
+ }
+
+ ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci,
+ DEVICE_LITTLE_ENDIAN);
+ ohci->localmem_base = localmem_base;
+
+ ohci->name = dev->info->name;
+
+ usb_bus_new(&ohci->bus, dev);
+ ohci->num_ports = num_ports;
+ for (i = 0; i < num_ports; i++) {
+ usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+ usb_port_location(&ohci->rhport[i].port, NULL, i+1);
+ }
+
+ ohci->async_td = 0;
+ qemu_register_reset(ohci_reset, ohci);
+}
+
+typedef struct {
+ PCIDevice pci_dev;
+ OHCIState state;
+} OHCIPCIState;
+
+static void ohci_mapfunc(PCIDevice *pci_dev, int i,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, pci_dev);
+ cpu_register_physical_memory(addr, size, ohci->state.mem);
+}
+
+static int usb_ohci_initfn_pci(struct PCIDevice *dev)
+{
+ OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev);
+ int num_ports = 3;
+
+ pci_config_set_vendor_id(ohci->pci_dev.config, PCI_VENDOR_ID_APPLE);
+ pci_config_set_device_id(ohci->pci_dev.config,
+ PCI_DEVICE_ID_APPLE_IPID_USB);
+ ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */
+ pci_config_set_class(ohci->pci_dev.config, PCI_CLASS_SERIAL_USB);
+ /* TODO: RST# value should be 0. */
+ ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */
+
+ usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0);
+ ohci->state.irq = ohci->pci_dev.irq[0];
+
+ /* TODO: avoid cast below by using dev */
+ pci_register_bar(&ohci->pci_dev, 0, 256,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ohci_mapfunc);
+ return 0;
+}
+
+void usb_ohci_init_pci(struct PCIBus *bus, int devfn)
+{
+ pci_create_simple(bus, devfn, "pci-ohci");
+}
+
+typedef struct {
+ SysBusDevice busdev;
+ OHCIState ohci;
+ uint32_t num_ports;
+ target_phys_addr_t dma_offset;
+} OHCISysBusState;
+
+static int ohci_init_pxa(SysBusDevice *dev)
+{
+ OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev);
+
+ usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset);
+ sysbus_init_irq(dev, &s->ohci.irq);
+ sysbus_init_mmio(dev, 0x1000, s->ohci.mem);
+
+ return 0;
+}
+
+static PCIDeviceInfo ohci_pci_info = {
+ .qdev.name = "pci-ohci",
+ .qdev.desc = "Apple USB Controller",
+ .qdev.size = sizeof(OHCIPCIState),
+ .init = usb_ohci_initfn_pci,
+};
+
+static SysBusDeviceInfo ohci_sysbus_info = {
+ .init = ohci_init_pxa,
+ .qdev.name = "sysbus-ohci",
+ .qdev.desc = "OHCI USB Controller",
+ .qdev.size = sizeof(OHCISysBusState),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3),
+ DEFINE_PROP_TADDR("dma-offset", OHCISysBusState, dma_offset, 3),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void ohci_register(void)
+{
+ pci_qdev_register(&ohci_pci_info);
+ sysbus_register_withprop(&ohci_sysbus_info);
+}
+device_init(ohci_register);
diff --git a/hw/usb-ohci.h b/hw/usb-ohci.h
new file mode 100644
index 0000000..eefcef3
--- /dev/null
+++ b/hw/usb-ohci.h
@@ -0,0 +1,9 @@
+#ifndef QEMU_USB_OHCI_H
+#define QEMU_USB_OHCI_H
+
+#include "qemu-common.h"
+
+void usb_ohci_init_pci(struct PCIBus *bus, int devfn);
+
+#endif
+
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
new file mode 100644
index 0000000..6763d52
--- /dev/null
+++ b/hw/usb-serial.c
@@ -0,0 +1,611 @@
+/*
+ * FTDI FT232BM Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "usb.h"
+#include "usb-desc.h"
+#include "qemu-char.h"
+
+//#define DEBUG_Serial
+
+#ifdef DEBUG_Serial
+#define DPRINTF(fmt, ...) \
+do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+
+/* Commands */
+#define FTDI_RESET 0
+#define FTDI_SET_MDM_CTRL 1
+#define FTDI_SET_FLOW_CTRL 2
+#define FTDI_SET_BAUD 3
+#define FTDI_SET_DATA 4
+#define FTDI_GET_MDM_ST 5
+#define FTDI_SET_EVENT_CHR 6
+#define FTDI_SET_ERROR_CHR 7
+#define FTDI_SET_LATENCY 9
+#define FTDI_GET_LATENCY 10
+
+#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+#define DeviceInVendor ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+/* RESET */
+
+#define FTDI_RESET_SIO 0
+#define FTDI_RESET_RX 1
+#define FTDI_RESET_TX 2
+
+/* SET_MDM_CTRL */
+
+#define FTDI_DTR 1
+#define FTDI_SET_DTR (FTDI_DTR << 8)
+#define FTDI_RTS 2
+#define FTDI_SET_RTS (FTDI_RTS << 8)
+
+/* SET_FLOW_CTRL */
+
+#define FTDI_RTS_CTS_HS 1
+#define FTDI_DTR_DSR_HS 2
+#define FTDI_XON_XOFF_HS 4
+
+/* SET_DATA */
+
+#define FTDI_PARITY (0x7 << 8)
+#define FTDI_ODD (0x1 << 8)
+#define FTDI_EVEN (0x2 << 8)
+#define FTDI_MARK (0x3 << 8)
+#define FTDI_SPACE (0x4 << 8)
+
+#define FTDI_STOP (0x3 << 11)
+#define FTDI_STOP1 (0x0 << 11)
+#define FTDI_STOP15 (0x1 << 11)
+#define FTDI_STOP2 (0x2 << 11)
+
+/* GET_MDM_ST */
+/* TODO: should be sent every 40ms */
+#define FTDI_CTS (1<<4) // CTS line status
+#define FTDI_DSR (1<<5) // DSR line status
+#define FTDI_RI (1<<6) // RI line status
+#define FTDI_RLSD (1<<7) // Receive Line Signal Detect
+
+/* Status */
+
+#define FTDI_DR (1<<0) // Data Ready
+#define FTDI_OE (1<<1) // Overrun Err
+#define FTDI_PE (1<<2) // Parity Err
+#define FTDI_FE (1<<3) // Framing Err
+#define FTDI_BI (1<<4) // Break Interrupt
+#define FTDI_THRE (1<<5) // Transmitter Holding Register
+#define FTDI_TEMT (1<<6) // Transmitter Empty
+#define FTDI_FIFO (1<<7) // Error in FIFO
+
+typedef struct {
+ USBDevice dev;
+ uint8_t recv_buf[RECV_BUF];
+ uint16_t recv_ptr;
+ uint16_t recv_used;
+ uint8_t event_chr;
+ uint8_t error_chr;
+ uint8_t event_trigger;
+ QEMUSerialSetParams params;
+ int latency; /* ms */
+ CharDriverState *cs;
+} USBSerialState;
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT_SERIAL,
+ STR_PRODUCT_BRAILLE,
+ STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+ [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
+ [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
+ [STR_SERIALNUMBER] = "1",
+};
+
+static const USBDescIface desc_iface0 = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ },{
+ .bEndpointAddress = USB_DIR_OUT | 0x02,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 64,
+ },
+ }
+};
+
+static const USBDescDevice desc_device = {
+ .bcdUSB = 0x0200,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0x80,
+ .bMaxPower = 50,
+ .ifs = &desc_iface0,
+ },
+ },
+};
+
+static const USBDesc desc_serial = {
+ .id = {
+ .idVendor = 0x0403,
+ .idProduct = 0x6001,
+ .bcdDevice = 0x0400,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT_SERIAL,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device,
+ .str = desc_strings,
+};
+
+static const USBDesc desc_braille = {
+ .id = {
+ .idVendor = 0x0403,
+ .idProduct = 0xfe72,
+ .bcdDevice = 0x0400,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT_BRAILLE,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device,
+ .str = desc_strings,
+};
+
+static void usb_serial_reset(USBSerialState *s)
+{
+ /* TODO: Set flow control to none */
+ s->event_chr = 0x0d;
+ s->event_trigger = 0;
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge in char driver */
+}
+
+static void usb_serial_handle_reset(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ DPRINTF("Reset\n");
+
+ usb_serial_reset(s);
+ /* TODO: Reset char device, send BREAK? */
+}
+
+static uint8_t usb_get_modem_lines(USBSerialState *s)
+{
+ int flags;
+ uint8_t ret;
+
+ if (qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP)
+ return FTDI_CTS|FTDI_DSR|FTDI_RLSD;
+
+ ret = 0;
+ if (flags & CHR_TIOCM_CTS)
+ ret |= FTDI_CTS;
+ if (flags & CHR_TIOCM_DSR)
+ ret |= FTDI_DSR;
+ if (flags & CHR_TIOCM_RI)
+ ret |= FTDI_RI;
+ if (flags & CHR_TIOCM_CAR)
+ ret |= FTDI_RLSD;
+
+ return ret;
+}
+
+static int usb_serial_handle_control(USBDevice *dev, 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);
+ if (ret >= 0) {
+ return ret;
+ }
+
+ ret = 0;
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ ret = 0;
+ break;
+
+ /* Class specific requests. */
+ case DeviceOutVendor | FTDI_RESET:
+ switch (value) {
+ case FTDI_RESET_SIO:
+ usb_serial_reset(s);
+ break;
+ case FTDI_RESET_RX:
+ s->recv_ptr = 0;
+ s->recv_used = 0;
+ /* TODO: purge from char device */
+ break;
+ case FTDI_RESET_TX:
+ /* TODO: purge from char device */
+ break;
+ }
+ break;
+ case DeviceOutVendor | FTDI_SET_MDM_CTRL:
+ {
+ static int flags;
+ qemu_chr_ioctl(s->cs,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+ if (value & FTDI_SET_RTS) {
+ if (value & FTDI_RTS)
+ flags |= CHR_TIOCM_RTS;
+ else
+ flags &= ~CHR_TIOCM_RTS;
+ }
+ if (value & FTDI_SET_DTR) {
+ if (value & FTDI_DTR)
+ flags |= CHR_TIOCM_DTR;
+ else
+ flags &= ~CHR_TIOCM_DTR;
+ }
+ qemu_chr_ioctl(s->cs,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+ break;
+ }
+ case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
+ /* TODO: ioctl */
+ break;
+ case DeviceOutVendor | FTDI_SET_BAUD: {
+ static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
+ int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
+ | ((index & 1) << 2)];
+ int divisor = value & 0x3fff;
+
+ /* chip special cases */
+ if (divisor == 1 && subdivisor8 == 0)
+ subdivisor8 = 4;
+ if (divisor == 0 && subdivisor8 == 0)
+ divisor = 1;
+
+ s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ break;
+ }
+ case DeviceOutVendor | FTDI_SET_DATA:
+ switch (value & FTDI_PARITY) {
+ case 0:
+ s->params.parity = 'N';
+ break;
+ case FTDI_ODD:
+ s->params.parity = 'O';
+ break;
+ case FTDI_EVEN:
+ s->params.parity = 'E';
+ break;
+ default:
+ DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
+ goto fail;
+ }
+ switch (value & FTDI_STOP) {
+ case FTDI_STOP1:
+ s->params.stop_bits = 1;
+ break;
+ case FTDI_STOP2:
+ s->params.stop_bits = 2;
+ break;
+ default:
+ DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
+ goto fail;
+ }
+ qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+ /* TODO: TX ON/OFF */
+ break;
+ case DeviceInVendor | FTDI_GET_MDM_ST:
+ data[0] = usb_get_modem_lines(s) | 1;
+ data[1] = 0;
+ ret = 2;
+ break;
+ case DeviceOutVendor | FTDI_SET_EVENT_CHR:
+ /* TODO: handle it */
+ s->event_chr = value;
+ break;
+ case DeviceOutVendor | FTDI_SET_ERROR_CHR:
+ /* TODO: handle it */
+ s->error_chr = value;
+ break;
+ case DeviceOutVendor | FTDI_SET_LATENCY:
+ s->latency = value;
+ break;
+ case DeviceInVendor | FTDI_GET_LATENCY:
+ data[0] = s->latency;
+ ret = 1;
+ break;
+ default:
+ fail:
+ DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+ int ret = 0;
+ uint8_t devep = p->devep;
+ uint8_t *data = p->data;
+ int len = p->len;
+ int first_len;
+
+ switch (p->pid) {
+ case USB_TOKEN_OUT:
+ if (devep != 2)
+ goto fail;
+ qemu_chr_write(s->cs, data, len);
+ break;
+
+ case USB_TOKEN_IN:
+ if (devep != 1)
+ goto fail;
+ first_len = RECV_BUF - s->recv_ptr;
+ if (len <= 2) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ *data++ = usb_get_modem_lines(s) | 1;
+ /* We do not have the uart details */
+ /* handle serial break */
+ if (s->event_trigger && s->event_trigger & FTDI_BI) {
+ s->event_trigger &= ~FTDI_BI;
+ *data = FTDI_BI;
+ ret = 2;
+ break;
+ } else {
+ *data++ = 0;
+ }
+ len -= 2;
+ if (len > s->recv_used)
+ len = s->recv_used;
+ if (!len) {
+ ret = USB_RET_NAK;
+ break;
+ }
+ if (first_len > len)
+ first_len = len;
+ memcpy(data, s->recv_buf + s->recv_ptr, first_len);
+ if (len > first_len)
+ memcpy(data + first_len, s->recv_buf, len - first_len);
+ s->recv_used -= len;
+ s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
+ ret = len + 2;
+ break;
+
+ default:
+ DPRINTF("Bad token\n");
+ fail:
+ ret = USB_RET_STALL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usb_serial_handle_destroy(USBDevice *dev)
+{
+ USBSerialState *s = (USBSerialState *)dev;
+
+ qemu_chr_close(s->cs);
+}
+
+static int usb_serial_can_read(void *opaque)
+{
+ USBSerialState *s = opaque;
+ return RECV_BUF - s->recv_used;
+}
+
+static void usb_serial_read(void *opaque, const uint8_t *buf, int size)
+{
+ USBSerialState *s = opaque;
+ int first_size, start;
+
+ /* room in the buffer? */
+ if (size > (RECV_BUF - s->recv_used))
+ size = RECV_BUF - s->recv_used;
+
+ start = s->recv_ptr + s->recv_used;
+ if (start < RECV_BUF) {
+ /* copy data to end of buffer */
+ first_size = RECV_BUF - start;
+ if (first_size > size)
+ first_size = size;
+
+ memcpy(s->recv_buf + start, buf, first_size);
+
+ /* wrap around to front if needed */
+ if (size > first_size)
+ memcpy(s->recv_buf, buf + first_size, size - first_size);
+ } else {
+ start -= RECV_BUF;
+ memcpy(s->recv_buf + start, buf, size);
+ }
+ s->recv_used += size;
+}
+
+static void usb_serial_event(void *opaque, int event)
+{
+ USBSerialState *s = opaque;
+
+ switch (event) {
+ case CHR_EVENT_BREAK:
+ s->event_trigger |= FTDI_BI;
+ break;
+ case CHR_EVENT_FOCUS:
+ break;
+ case CHR_EVENT_OPENED:
+ usb_serial_reset(s);
+ /* TODO: Reset USB port */
+ break;
+ }
+}
+
+static int usb_serial_initfn(USBDevice *dev)
+{
+ USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
+
+ usb_desc_init(dev);
+
+ if (!s->cs) {
+ error_report("Property chardev is required");
+ return -1;
+ }
+
+ qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read,
+ usb_serial_event, s);
+ usb_serial_handle_reset(dev);
+ return 0;
+}
+
+static USBDevice *usb_serial_init(const char *filename)
+{
+ USBDevice *dev;
+ CharDriverState *cdrv;
+ uint32_t vendorid = 0, productid = 0;
+ char label[32];
+ static int index;
+
+ while (*filename && *filename != ':') {
+ const char *p;
+ char *e;
+ if (strstart(filename, "vendorid=", &p)) {
+ vendorid = strtol(p, &e, 16);
+ if (e == p || (*e && *e != ',' && *e != ':')) {
+ error_report("bogus vendor ID %s", p);
+ return NULL;
+ }
+ filename = e;
+ } else if (strstart(filename, "productid=", &p)) {
+ productid = strtol(p, &e, 16);
+ if (e == p || (*e && *e != ',' && *e != ':')) {
+ error_report("bogus product ID %s", p);
+ return NULL;
+ }
+ filename = e;
+ } else {
+ error_report("unrecognized serial USB option %s", filename);
+ return NULL;
+ }
+ while(*filename == ',')
+ filename++;
+ }
+ if (!*filename) {
+ error_report("character device specification needed");
+ return NULL;
+ }
+ filename++;
+
+ snprintf(label, sizeof(label), "usbserial%d", index++);
+ cdrv = qemu_chr_open(label, filename, NULL);
+ if (!cdrv)
+ return NULL;
+
+ dev = usb_create(NULL /* FIXME */, "usb-serial");
+ if (!dev) {
+ return NULL;
+ }
+ qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
+ if (vendorid)
+ qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid);
+ if (productid)
+ qdev_prop_set_uint16(&dev->qdev, "productid", productid);
+ qdev_init_nofail(&dev->qdev);
+
+ return dev;
+}
+
+static USBDevice *usb_braille_init(const char *unused)
+{
+ USBDevice *dev;
+ CharDriverState *cdrv;
+
+ cdrv = qemu_chr_open("braille", "braille", NULL);
+ if (!cdrv)
+ return NULL;
+
+ dev = usb_create(NULL /* FIXME */, "usb-braille");
+ qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
+ qdev_init_nofail(&dev->qdev);
+
+ return dev;
+}
+
+static struct USBDeviceInfo serial_info = {
+ .product_desc = "QEMU USB Serial",
+ .qdev.name = "usb-serial",
+ .qdev.size = sizeof(USBSerialState),
+ .usb_desc = &desc_serial,
+ .init = usb_serial_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_serial_handle_reset,
+ .handle_control = usb_serial_handle_control,
+ .handle_data = usb_serial_handle_data,
+ .handle_destroy = usb_serial_handle_destroy,
+ .usbdevice_name = "serial",
+ .usbdevice_init = usb_serial_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", USBSerialState, cs),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static struct USBDeviceInfo braille_info = {
+ .product_desc = "QEMU USB Braille",
+ .qdev.name = "usb-braille",
+ .qdev.size = sizeof(USBSerialState),
+ .usb_desc = &desc_braille,
+ .init = usb_serial_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_serial_handle_reset,
+ .handle_control = usb_serial_handle_control,
+ .handle_data = usb_serial_handle_data,
+ .handle_destroy = usb_serial_handle_destroy,
+ .usbdevice_name = "braille",
+ .usbdevice_init = usb_braille_init,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_CHR("chardev", USBSerialState, cs),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void usb_serial_register_devices(void)
+{
+ usb_qdev_register(&serial_info);
+ usb_qdev_register(&braille_info);
+}
+device_init(usb_serial_register_devices)
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c
new file mode 100644
index 0000000..b384e1d
--- /dev/null
+++ b/hw/usb-uhci.c
@@ -0,0 +1,1229 @@
+/*
+ * USB UHCI controller emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ * Magor rewrite of the UHCI data structures parser and frame processor
+ * Support for fully async operation and multiple outstanding transactions
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "usb.h"
+#include "pci.h"
+#include "qemu-timer.h"
+#include "usb-uhci.h"
+
+//#define DEBUG
+//#define DEBUG_DUMP_DATA
+
+#define UHCI_CMD_FGR (1 << 4)
+#define UHCI_CMD_EGSM (1 << 3)
+#define UHCI_CMD_GRESET (1 << 2)
+#define UHCI_CMD_HCRESET (1 << 1)
+#define UHCI_CMD_RS (1 << 0)
+
+#define UHCI_STS_HCHALTED (1 << 5)
+#define UHCI_STS_HCPERR (1 << 4)
+#define UHCI_STS_HSERR (1 << 3)
+#define UHCI_STS_RD (1 << 2)
+#define UHCI_STS_USBERR (1 << 1)
+#define UHCI_STS_USBINT (1 << 0)
+
+#define TD_CTRL_SPD (1 << 29)
+#define TD_CTRL_ERROR_SHIFT 27
+#define TD_CTRL_IOS (1 << 25)
+#define TD_CTRL_IOC (1 << 24)
+#define TD_CTRL_ACTIVE (1 << 23)
+#define TD_CTRL_STALL (1 << 22)
+#define TD_CTRL_BABBLE (1 << 20)
+#define TD_CTRL_NAK (1 << 19)
+#define TD_CTRL_TIMEOUT (1 << 18)
+
+#define UHCI_PORT_SUSPEND (1 << 12)
+#define UHCI_PORT_RESET (1 << 9)
+#define UHCI_PORT_LSDA (1 << 8)
+#define UHCI_PORT_RD (1 << 6)
+#define UHCI_PORT_ENC (1 << 3)
+#define UHCI_PORT_EN (1 << 2)
+#define UHCI_PORT_CSC (1 << 1)
+#define UHCI_PORT_CCS (1 << 0)
+
+#define UHCI_PORT_READ_ONLY (0x1bb)
+#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC)
+
+#define FRAME_TIMER_FREQ 1000
+
+#define FRAME_MAX_LOOPS 100
+
+#define NB_PORTS 2
+
+#ifdef DEBUG
+#define DPRINTF printf
+
+static const char *pid2str(int pid)
+{
+ switch (pid) {
+ case USB_TOKEN_SETUP: return "SETUP";
+ case USB_TOKEN_IN: return "IN";
+ case USB_TOKEN_OUT: return "OUT";
+ }
+ return "?";
+}
+
+#else
+#define DPRINTF(...)
+#endif
+
+#ifdef DEBUG_DUMP_DATA
+static void dump_data(const uint8_t *data, int len)
+{
+ int i;
+
+ printf("uhci: data: ");
+ for(i = 0; i < len; i++)
+ printf(" %02x", data[i]);
+ printf("\n");
+}
+#else
+static void dump_data(const uint8_t *data, int len) {}
+#endif
+
+/*
+ * Pending async transaction.
+ * 'packet' must be the first field because completion
+ * handler does "(UHCIAsync *) pkt" cast.
+ */
+typedef struct UHCIAsync {
+ USBPacket packet;
+ struct UHCIAsync *next;
+ uint32_t td;
+ uint32_t token;
+ int8_t valid;
+ uint8_t isoc;
+ uint8_t done;
+ uint8_t buffer[2048];
+} UHCIAsync;
+
+typedef struct UHCIPort {
+ USBPort port;
+ uint16_t ctrl;
+} UHCIPort;
+
+typedef struct UHCIState {
+ PCIDevice dev;
+ USBBus bus;
+ uint16_t cmd; /* cmd register */
+ uint16_t status;
+ uint16_t intr; /* interrupt enable register */
+ uint16_t frnum; /* frame number */
+ uint32_t fl_base_addr; /* frame list base address */
+ uint8_t sof_timing;
+ uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
+ int64_t expire_time;
+ QEMUTimer *frame_timer;
+ UHCIPort ports[NB_PORTS];
+
+ /* Interrupts that should be raised at the end of the current frame. */
+ uint32_t pending_int_mask;
+
+ /* Active packets */
+ UHCIAsync *async_pending;
+ UHCIAsync *async_pool;
+ uint8_t num_ports_vmstate;
+} UHCIState;
+
+typedef struct UHCI_TD {
+ uint32_t link;
+ uint32_t ctrl; /* see TD_CTRL_xxx */
+ uint32_t token;
+ uint32_t buffer;
+} UHCI_TD;
+
+typedef struct UHCI_QH {
+ uint32_t link;
+ uint32_t el_link;
+} UHCI_QH;
+
+static UHCIAsync *uhci_async_alloc(UHCIState *s)
+{
+ UHCIAsync *async = qemu_malloc(sizeof(UHCIAsync));
+
+ memset(&async->packet, 0, sizeof(async->packet));
+ async->valid = 0;
+ async->td = 0;
+ async->token = 0;
+ async->done = 0;
+ async->isoc = 0;
+ async->next = NULL;
+
+ return async;
+}
+
+static void uhci_async_free(UHCIState *s, UHCIAsync *async)
+{
+ qemu_free(async);
+}
+
+static void uhci_async_link(UHCIState *s, UHCIAsync *async)
+{
+ async->next = s->async_pending;
+ s->async_pending = async;
+}
+
+static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
+{
+ UHCIAsync *curr = s->async_pending;
+ UHCIAsync **prev = &s->async_pending;
+
+ while (curr) {
+ if (curr == async) {
+ *prev = curr->next;
+ return;
+ }
+
+ prev = &curr->next;
+ curr = curr->next;
+ }
+}
+
+static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
+{
+ DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
+ async->td, async->token, async->done);
+
+ if (!async->done)
+ usb_cancel_packet(&async->packet);
+ uhci_async_free(s, async);
+}
+
+/*
+ * Mark all outstanding async packets as invalid.
+ * This is used for canceling them when TDs are removed by the HCD.
+ */
+static UHCIAsync *uhci_async_validate_begin(UHCIState *s)
+{
+ UHCIAsync *async = s->async_pending;
+
+ while (async) {
+ async->valid--;
+ async = async->next;
+ }
+ return NULL;
+}
+
+/*
+ * Cancel async packets that are no longer valid
+ */
+static void uhci_async_validate_end(UHCIState *s)
+{
+ UHCIAsync *curr = s->async_pending;
+ UHCIAsync **prev = &s->async_pending;
+ UHCIAsync *next;
+
+ while (curr) {
+ if (curr->valid > 0) {
+ prev = &curr->next;
+ curr = curr->next;
+ continue;
+ }
+
+ next = curr->next;
+
+ /* Unlink */
+ *prev = next;
+
+ uhci_async_cancel(s, curr);
+
+ curr = next;
+ }
+}
+
+static void uhci_async_cancel_all(UHCIState *s)
+{
+ UHCIAsync *curr = s->async_pending;
+ UHCIAsync *next;
+
+ while (curr) {
+ next = curr->next;
+
+ uhci_async_cancel(s, curr);
+
+ curr = next;
+ }
+
+ s->async_pending = NULL;
+}
+
+static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token)
+{
+ UHCIAsync *async = s->async_pending;
+ UHCIAsync *match = NULL;
+ int count = 0;
+
+ /*
+ * We're looking for the best match here. ie both td addr and token.
+ * Otherwise we return last good match. ie just token.
+ * It's ok to match just token because it identifies the transaction
+ * rather well, token includes: device addr, endpoint, size, etc.
+ *
+ * Also since we queue async transactions in reverse order by returning
+ * last good match we restores the order.
+ *
+ * It's expected that we wont have a ton of outstanding transactions.
+ * If we ever do we'd want to optimize this algorithm.
+ */
+
+ while (async) {
+ if (async->token == token) {
+ /* Good match */
+ match = async;
+
+ if (async->td == addr) {
+ /* Best match */
+ break;
+ }
+ }
+
+ async = async->next;
+ count++;
+ }
+
+ if (count > 64)
+ fprintf(stderr, "uhci: warning lots of async transactions\n");
+
+ return match;
+}
+
+static void uhci_update_irq(UHCIState *s)
+{
+ int level;
+ if (((s->status2 & 1) && (s->intr & (1 << 2))) ||
+ ((s->status2 & 2) && (s->intr & (1 << 3))) ||
+ ((s->status & UHCI_STS_USBERR) && (s->intr & (1 << 0))) ||
+ ((s->status & UHCI_STS_RD) && (s->intr & (1 << 1))) ||
+ (s->status & UHCI_STS_HSERR) ||
+ (s->status & UHCI_STS_HCPERR)) {
+ level = 1;
+ } else {
+ level = 0;
+ }
+ qemu_set_irq(s->dev.irq[3], level);
+}
+
+static void uhci_reset(void *opaque)
+{
+ UHCIState *s = opaque;
+ uint8_t *pci_conf;
+ int i;
+ UHCIPort *port;
+
+ DPRINTF("uhci: full reset\n");
+
+ pci_conf = s->dev.config;
+
+ pci_conf[0x6a] = 0x01; /* usb clock */
+ pci_conf[0x6b] = 0x00;
+ s->cmd = 0;
+ s->status = 0;
+ s->status2 = 0;
+ s->intr = 0;
+ s->fl_base_addr = 0;
+ s->sof_timing = 64;
+
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &s->ports[i];
+ port->ctrl = 0x0080;
+ if (port->port.dev) {
+ usb_attach(&port->port, port->port.dev);
+ }
+ }
+
+ uhci_async_cancel_all(s);
+}
+
+static void uhci_pre_save(void *opaque)
+{
+ UHCIState *s = opaque;
+
+ uhci_async_cancel_all(s);
+}
+
+static const VMStateDescription vmstate_uhci_port = {
+ .name = "uhci port",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(ctrl, UHCIPort),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_uhci = {
+ .name = "uhci",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = uhci_pre_save,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, UHCIState),
+ VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState),
+ VMSTATE_STRUCT_ARRAY(ports, UHCIState, NB_PORTS, 1,
+ vmstate_uhci_port, UHCIPort),
+ VMSTATE_UINT16(cmd, UHCIState),
+ VMSTATE_UINT16(status, UHCIState),
+ VMSTATE_UINT16(intr, UHCIState),
+ VMSTATE_UINT16(frnum, UHCIState),
+ VMSTATE_UINT32(fl_base_addr, UHCIState),
+ VMSTATE_UINT8(sof_timing, UHCIState),
+ VMSTATE_UINT8(status2, UHCIState),
+ VMSTATE_TIMER(frame_timer, UHCIState),
+ VMSTATE_INT64_V(expire_time, UHCIState, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ UHCIState *s = opaque;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x0c:
+ s->sof_timing = val;
+ break;
+ }
+}
+
+static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr)
+{
+ UHCIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x0c:
+ val = s->sof_timing;
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+ return val;
+}
+
+static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ UHCIState *s = opaque;
+
+ addr &= 0x1f;
+ DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
+
+ switch(addr) {
+ case 0x00:
+ if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) {
+ /* start frame processing */
+ qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock));
+ s->status &= ~UHCI_STS_HCHALTED;
+ } else if (!(val & UHCI_CMD_RS)) {
+ s->status |= UHCI_STS_HCHALTED;
+ }
+ if (val & UHCI_CMD_GRESET) {
+ UHCIPort *port;
+ USBDevice *dev;
+ int i;
+
+ /* send reset on the USB bus */
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &s->ports[i];
+ dev = port->port.dev;
+ if (dev) {
+ usb_send_msg(dev, USB_MSG_RESET);
+ }
+ }
+ uhci_reset(s);
+ return;
+ }
+ if (val & UHCI_CMD_HCRESET) {
+ uhci_reset(s);
+ return;
+ }
+ s->cmd = val;
+ break;
+ case 0x02:
+ s->status &= ~val;
+ /* XXX: the chip spec is not coherent, so we add a hidden
+ register to distinguish between IOC and SPD */
+ if (val & UHCI_STS_USBINT)
+ s->status2 = 0;
+ uhci_update_irq(s);
+ break;
+ case 0x04:
+ s->intr = val;
+ uhci_update_irq(s);
+ break;
+ case 0x06:
+ if (s->status & UHCI_STS_HCHALTED)
+ s->frnum = val & 0x7ff;
+ break;
+ case 0x10 ... 0x1f:
+ {
+ UHCIPort *port;
+ USBDevice *dev;
+ int n;
+
+ n = (addr >> 1) & 7;
+ if (n >= NB_PORTS)
+ return;
+ port = &s->ports[n];
+ dev = port->port.dev;
+ if (dev) {
+ /* port reset */
+ if ( (val & UHCI_PORT_RESET) &&
+ !(port->ctrl & UHCI_PORT_RESET) ) {
+ usb_send_msg(dev, USB_MSG_RESET);
+ }
+ }
+ port->ctrl &= UHCI_PORT_READ_ONLY;
+ port->ctrl |= (val & ~UHCI_PORT_READ_ONLY);
+ /* some bits are reset when a '1' is written to them */
+ port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR);
+ }
+ break;
+ }
+}
+
+static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr)
+{
+ UHCIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x00:
+ val = s->cmd;
+ break;
+ case 0x02:
+ val = s->status;
+ break;
+ case 0x04:
+ val = s->intr;
+ break;
+ case 0x06:
+ val = s->frnum;
+ break;
+ case 0x10 ... 0x1f:
+ {
+ UHCIPort *port;
+ int n;
+ n = (addr >> 1) & 7;
+ if (n >= NB_PORTS)
+ goto read_default;
+ port = &s->ports[n];
+ val = port->ctrl;
+ }
+ break;
+ default:
+ read_default:
+ val = 0xff7f; /* disabled port */
+ break;
+ }
+
+ DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
+
+ return val;
+}
+
+static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ UHCIState *s = opaque;
+
+ addr &= 0x1f;
+ DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
+
+ switch(addr) {
+ case 0x08:
+ s->fl_base_addr = val & ~0xfff;
+ break;
+ }
+}
+
+static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr)
+{
+ UHCIState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x1f;
+ switch(addr) {
+ case 0x08:
+ val = s->fl_base_addr;
+ break;
+ default:
+ val = 0xffffffff;
+ break;
+ }
+ return val;
+}
+
+/* signal resume if controller suspended */
+static void uhci_resume (void *opaque)
+{
+ UHCIState *s = (UHCIState *)opaque;
+
+ if (!s)
+ return;
+
+ if (s->cmd & UHCI_CMD_EGSM) {
+ s->cmd |= UHCI_CMD_FGR;
+ s->status |= UHCI_STS_RD;
+ uhci_update_irq(s);
+ }
+}
+
+static void uhci_attach(USBPort *port1)
+{
+ UHCIState *s = port1->opaque;
+ UHCIPort *port = &s->ports[port1->index];
+
+ /* set connect status */
+ port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
+
+ /* update speed */
+ if (port->port.dev->speed == USB_SPEED_LOW) {
+ port->ctrl |= UHCI_PORT_LSDA;
+ } else {
+ port->ctrl &= ~UHCI_PORT_LSDA;
+ }
+
+ uhci_resume(s);
+}
+
+static void uhci_detach(USBPort *port1)
+{
+ UHCIState *s = port1->opaque;
+ UHCIPort *port = &s->ports[port1->index];
+
+ /* set connect status */
+ if (port->ctrl & UHCI_PORT_CCS) {
+ port->ctrl &= ~UHCI_PORT_CCS;
+ port->ctrl |= UHCI_PORT_CSC;
+ }
+ /* disable port */
+ if (port->ctrl & UHCI_PORT_EN) {
+ port->ctrl &= ~UHCI_PORT_EN;
+ port->ctrl |= UHCI_PORT_ENC;
+ }
+
+ uhci_resume(s);
+}
+
+static void uhci_wakeup(USBDevice *dev)
+{
+ USBBus *bus = usb_bus_from_device(dev);
+ UHCIState *s = container_of(bus, UHCIState, bus);
+ UHCIPort *port = s->ports + dev->port->index;
+
+ if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
+ port->ctrl |= UHCI_PORT_RD;
+ uhci_resume(s);
+ }
+}
+
+static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
+{
+ int i, ret;
+
+ DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n",
+ pid2str(p->pid), p->devaddr, p->devep, p->len);
+ if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
+ dump_data(p->data, p->len);
+
+ ret = USB_RET_NODEV;
+ for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) {
+ UHCIPort *port = &s->ports[i];
+ USBDevice *dev = port->port.dev;
+
+ if (dev && (port->ctrl & UHCI_PORT_EN))
+ ret = dev->info->handle_packet(dev, p);
+ }
+
+ DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
+ if (p->pid == USB_TOKEN_IN && ret > 0)
+ dump_data(p->data, ret);
+
+ return ret;
+}
+
+static void uhci_async_complete(USBPacket * packet, void *opaque);
+static void uhci_process_frame(UHCIState *s);
+
+/* return -1 if fatal error (frame must be stopped)
+ 0 if TD successful
+ 1 if TD unsuccessful or inactive
+*/
+static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
+{
+ int len = 0, max_len, err, ret;
+ uint8_t pid;
+
+ max_len = ((td->token >> 21) + 1) & 0x7ff;
+ pid = td->token & 0xff;
+
+ ret = async->packet.len;
+
+ if (td->ctrl & TD_CTRL_IOS)
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+
+ if (ret < 0)
+ goto out;
+
+ len = async->packet.len;
+ td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
+
+ /* The NAK bit may have been set by a previous frame, so clear it
+ here. The docs are somewhat unclear, but win2k relies on this
+ behavior. */
+ td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK);
+ if (td->ctrl & TD_CTRL_IOC)
+ *int_mask |= 0x01;
+
+ if (pid == USB_TOKEN_IN) {
+ if (len > max_len) {
+ ret = USB_RET_BABBLE;
+ goto out;
+ }
+
+ if (len > 0) {
+ /* write the data back */
+ cpu_physical_memory_write(td->buffer, async->buffer, len);
+ }
+
+ if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
+ *int_mask |= 0x02;
+ /* short packet: do not update QH */
+ DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
+ return 1;
+ }
+ }
+
+ /* success */
+ return 0;
+
+out:
+ switch(ret) {
+ case USB_RET_STALL:
+ td->ctrl |= TD_CTRL_STALL;
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ return 1;
+
+ case USB_RET_BABBLE:
+ td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ /* frame interrupted */
+ return -1;
+
+ case USB_RET_NAK:
+ td->ctrl |= TD_CTRL_NAK;
+ if (pid == USB_TOKEN_SETUP)
+ break;
+ return 1;
+
+ case USB_RET_NODEV:
+ default:
+ break;
+ }
+
+ /* Retry the TD if error count is not zero */
+
+ td->ctrl |= TD_CTRL_TIMEOUT;
+ err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
+ if (err != 0) {
+ err--;
+ if (err == 0) {
+ td->ctrl &= ~TD_CTRL_ACTIVE;
+ s->status |= UHCI_STS_USBERR;
+ if (td->ctrl & TD_CTRL_IOC)
+ *int_mask |= 0x01;
+ uhci_update_irq(s);
+ }
+ }
+ td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
+ (err << TD_CTRL_ERROR_SHIFT);
+ return 1;
+}
+
+static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
+{
+ UHCIAsync *async;
+ int len = 0, max_len;
+ uint8_t pid, isoc;
+ uint32_t token;
+
+ /* Is active ? */
+ if (!(td->ctrl & TD_CTRL_ACTIVE))
+ return 1;
+
+ /* token field is not unique for isochronous requests,
+ * so use the destination buffer
+ */
+ if (td->ctrl & TD_CTRL_IOS) {
+ token = td->buffer;
+ isoc = 1;
+ } else {
+ token = td->token;
+ isoc = 0;
+ }
+
+ async = uhci_async_find_td(s, addr, token);
+ if (async) {
+ /* Already submitted */
+ async->valid = 32;
+
+ if (!async->done)
+ return 1;
+
+ uhci_async_unlink(s, async);
+ goto done;
+ }
+
+ /* Allocate new packet */
+ async = uhci_async_alloc(s);
+ if (!async)
+ return 1;
+
+ /* valid needs to be large enough to handle 10 frame delay
+ * for initial isochronous requests
+ */
+ async->valid = 32;
+ async->td = addr;
+ async->token = token;
+ async->isoc = isoc;
+
+ max_len = ((td->token >> 21) + 1) & 0x7ff;
+ pid = td->token & 0xff;
+
+ async->packet.pid = pid;
+ async->packet.devaddr = (td->token >> 8) & 0x7f;
+ async->packet.devep = (td->token >> 15) & 0xf;
+ async->packet.data = async->buffer;
+ async->packet.len = max_len;
+ async->packet.complete_cb = uhci_async_complete;
+ async->packet.complete_opaque = s;
+
+ switch(pid) {
+ case USB_TOKEN_OUT:
+ case USB_TOKEN_SETUP:
+ cpu_physical_memory_read(td->buffer, async->buffer, max_len);
+ len = uhci_broadcast_packet(s, &async->packet);
+ if (len >= 0)
+ len = max_len;
+ break;
+
+ case USB_TOKEN_IN:
+ len = uhci_broadcast_packet(s, &async->packet);
+ break;
+
+ default:
+ /* invalid pid : frame interrupted */
+ uhci_async_free(s, async);
+ s->status |= UHCI_STS_HCPERR;
+ uhci_update_irq(s);
+ return -1;
+ }
+
+ if (len == USB_RET_ASYNC) {
+ uhci_async_link(s, async);
+ return 2;
+ }
+
+ async->packet.len = len;
+
+done:
+ len = uhci_complete_td(s, td, async, int_mask);
+ uhci_async_free(s, async);
+ return len;
+}
+
+static void uhci_async_complete(USBPacket *packet, void *opaque)
+{
+ UHCIState *s = opaque;
+ UHCIAsync *async = (UHCIAsync *) packet;
+
+ DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
+
+ if (async->isoc) {
+ UHCI_TD td;
+ uint32_t link = async->td;
+ uint32_t int_mask = 0, val;
+
+ cpu_physical_memory_read(link & ~0xf, (uint8_t *) &td, sizeof(td));
+ le32_to_cpus(&td.link);
+ le32_to_cpus(&td.ctrl);
+ le32_to_cpus(&td.token);
+ le32_to_cpus(&td.buffer);
+
+ uhci_async_unlink(s, async);
+ uhci_complete_td(s, &td, async, &int_mask);
+ s->pending_int_mask |= int_mask;
+
+ /* update the status bits of the TD */
+ val = cpu_to_le32(td.ctrl);
+ cpu_physical_memory_write((link & ~0xf) + 4,
+ (const uint8_t *)&val, sizeof(val));
+ uhci_async_free(s, async);
+ } else {
+ async->done = 1;
+ uhci_process_frame(s);
+ }
+}
+
+static int is_valid(uint32_t link)
+{
+ return (link & 1) == 0;
+}
+
+static int is_qh(uint32_t link)
+{
+ return (link & 2) != 0;
+}
+
+static int depth_first(uint32_t link)
+{
+ return (link & 4) != 0;
+}
+
+/* QH DB used for detecting QH loops */
+#define UHCI_MAX_QUEUES 128
+typedef struct {
+ uint32_t addr[UHCI_MAX_QUEUES];
+ int count;
+} QhDb;
+
+static void qhdb_reset(QhDb *db)
+{
+ db->count = 0;
+}
+
+/* Add QH to DB. Returns 1 if already present or DB is full. */
+static int qhdb_insert(QhDb *db, uint32_t addr)
+{
+ int i;
+ for (i = 0; i < db->count; i++)
+ if (db->addr[i] == addr)
+ return 1;
+
+ if (db->count >= UHCI_MAX_QUEUES)
+ return 1;
+
+ db->addr[db->count++] = addr;
+ return 0;
+}
+
+static void uhci_process_frame(UHCIState *s)
+{
+ uint32_t frame_addr, link, old_td_ctrl, val, int_mask;
+ uint32_t curr_qh;
+ int cnt, ret;
+ UHCI_TD td;
+ UHCI_QH qh;
+ QhDb qhdb;
+
+ frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
+
+ DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
+
+ cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4);
+ le32_to_cpus(&link);
+
+ int_mask = 0;
+ curr_qh = 0;
+
+ qhdb_reset(&qhdb);
+
+ for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) {
+ if (is_qh(link)) {
+ /* QH */
+
+ if (qhdb_insert(&qhdb, link)) {
+ /*
+ * We're going in circles. Which is not a bug because
+ * HCD is allowed to do that as part of the BW management.
+ * In our case though it makes no sense to spin here. Sync transations
+ * are already done, and async completion handler will re-process
+ * the frame when something is ready.
+ */
+ DPRINTF("uhci: detected loop. qh 0x%x\n", link);
+ break;
+ }
+
+ cpu_physical_memory_read(link & ~0xf, (uint8_t *) &qh, sizeof(qh));
+ le32_to_cpus(&qh.link);
+ le32_to_cpus(&qh.el_link);
+
+ DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
+ link, qh.link, qh.el_link);
+
+ if (!is_valid(qh.el_link)) {
+ /* QH w/o elements */
+ curr_qh = 0;
+ link = qh.link;
+ } else {
+ /* QH with elements */
+ curr_qh = link;
+ link = qh.el_link;
+ }
+ continue;
+ }
+
+ /* TD */
+ cpu_physical_memory_read(link & ~0xf, (uint8_t *) &td, sizeof(td));
+ le32_to_cpus(&td.link);
+ le32_to_cpus(&td.ctrl);
+ le32_to_cpus(&td.token);
+ le32_to_cpus(&td.buffer);
+
+ DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+ link, td.link, td.ctrl, td.token, curr_qh);
+
+ old_td_ctrl = td.ctrl;
+ ret = uhci_handle_td(s, link, &td, &int_mask);
+ if (old_td_ctrl != td.ctrl) {
+ /* update the status bits of the TD */
+ val = cpu_to_le32(td.ctrl);
+ cpu_physical_memory_write((link & ~0xf) + 4,
+ (const uint8_t *)&val, sizeof(val));
+ }
+
+ if (ret < 0) {
+ /* interrupted frame */
+ break;
+ }
+
+ if (ret == 2 || ret == 1) {
+ DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+ link, ret == 2 ? "pend" : "skip",
+ td.link, td.ctrl, td.token, curr_qh);
+
+ link = curr_qh ? qh.link : td.link;
+ continue;
+ }
+
+ /* completed TD */
+
+ DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+ link, td.link, td.ctrl, td.token, curr_qh);
+
+ link = td.link;
+
+ if (curr_qh) {
+ /* update QH element link */
+ qh.el_link = link;
+ val = cpu_to_le32(qh.el_link);
+ cpu_physical_memory_write((curr_qh & ~0xf) + 4,
+ (const uint8_t *)&val, sizeof(val));
+
+ if (!depth_first(link)) {
+ /* done with this QH */
+
+ DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
+ curr_qh, qh.link, qh.el_link);
+
+ curr_qh = 0;
+ link = qh.link;
+ }
+ }
+
+ /* go to the next entry */
+ }
+
+ s->pending_int_mask |= int_mask;
+}
+
+static void uhci_frame_timer(void *opaque)
+{
+ UHCIState *s = opaque;
+
+ /* prepare the timer for the next frame */
+ s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+
+ if (!(s->cmd & UHCI_CMD_RS)) {
+ /* Full stop */
+ qemu_del_timer(s->frame_timer);
+ /* set hchalted bit in status - UHCI11D 2.1.2 */
+ s->status |= UHCI_STS_HCHALTED;
+
+ DPRINTF("uhci: halted\n");
+ return;
+ }
+
+ /* Complete the previous frame */
+ if (s->pending_int_mask) {
+ s->status2 |= s->pending_int_mask;
+ s->status |= UHCI_STS_USBINT;
+ uhci_update_irq(s);
+ }
+ s->pending_int_mask = 0;
+
+ /* Start new frame */
+ s->frnum = (s->frnum + 1) & 0x7ff;
+
+ DPRINTF("uhci: new frame #%u\n" , s->frnum);
+
+ uhci_async_validate_begin(s);
+
+ uhci_process_frame(s);
+
+ uhci_async_validate_end(s);
+
+ qemu_mod_timer(s->frame_timer, s->expire_time);
+}
+
+static void uhci_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ UHCIState *s = (UHCIState *)pci_dev;
+
+ register_ioport_write(addr, 32, 2, uhci_ioport_writew, s);
+ register_ioport_read(addr, 32, 2, uhci_ioport_readw, s);
+ register_ioport_write(addr, 32, 4, uhci_ioport_writel, s);
+ register_ioport_read(addr, 32, 4, uhci_ioport_readl, s);
+ register_ioport_write(addr, 32, 1, uhci_ioport_writeb, s);
+ register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
+}
+
+static USBPortOps uhci_port_ops = {
+ .attach = uhci_attach,
+ .detach = uhci_detach,
+ .wakeup = uhci_wakeup,
+};
+
+static int usb_uhci_common_initfn(UHCIState *s)
+{
+ uint8_t *pci_conf = s->dev.config;
+ int i;
+
+ pci_conf[PCI_REVISION_ID] = 0x01; // revision number
+ pci_conf[PCI_CLASS_PROG] = 0x00;
+ pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
+ /* TODO: reset value should be 0. */
+ pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
+ pci_conf[0x60] = 0x10; // release number
+
+ usb_bus_new(&s->bus, &s->dev.qdev);
+ for(i = 0; i < NB_PORTS; i++) {
+ usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
+ usb_port_location(&s->ports[i].port, NULL, i+1);
+ }
+ s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s);
+ s->expire_time = qemu_get_clock(vm_clock) +
+ (get_ticks_per_sec() / FRAME_TIMER_FREQ);
+ s->num_ports_vmstate = NB_PORTS;
+
+ qemu_register_reset(uhci_reset, s);
+
+ /* Use region 4 for consistency with real hardware. BSD guests seem
+ to rely on this. */
+ pci_register_bar(&s->dev, 4, 0x20,
+ PCI_BASE_ADDRESS_SPACE_IO, uhci_map);
+
+ return 0;
+}
+
+static int usb_uhci_piix3_initfn(PCIDevice *dev)
+{
+ UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+ uint8_t *pci_conf = s->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_2);
+ return usb_uhci_common_initfn(s);
+}
+
+static int usb_uhci_piix4_initfn(PCIDevice *dev)
+{
+ UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+ uint8_t *pci_conf = s->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_2);
+ return usb_uhci_common_initfn(s);
+}
+
+static int usb_uhci_vt82c686b_initfn(PCIDevice *dev)
+{
+ UHCIState *s = DO_UPCAST(UHCIState, dev, dev);
+ uint8_t *pci_conf = s->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_UHCI);
+
+ /* USB misc control 1/2 */
+ pci_set_long(pci_conf + 0x40,0x00001000);
+ /* PM capability */
+ pci_set_long(pci_conf + 0x80,0x00020001);
+ /* USB legacy support */
+ pci_set_long(pci_conf + 0xc0,0x00002000);
+
+ return usb_uhci_common_initfn(s);
+}
+
+static PCIDeviceInfo uhci_info[] = {
+ {
+ .qdev.name = "piix3-usb-uhci",
+ .qdev.size = sizeof(UHCIState),
+ .qdev.vmsd = &vmstate_uhci,
+ .init = usb_uhci_piix3_initfn,
+ },{
+ .qdev.name = "piix4-usb-uhci",
+ .qdev.size = sizeof(UHCIState),
+ .qdev.vmsd = &vmstate_uhci,
+ .init = usb_uhci_piix4_initfn,
+ },{
+ .qdev.name = "vt82c686b-usb-uhci",
+ .qdev.size = sizeof(UHCIState),
+ .qdev.vmsd = &vmstate_uhci,
+ .init = usb_uhci_vt82c686b_initfn,
+ },{
+ /* end of list */
+ }
+};
+
+static void uhci_register(void)
+{
+ pci_qdev_register_many(uhci_info);
+}
+device_init(uhci_register);
+
+void usb_uhci_piix3_init(PCIBus *bus, int devfn)
+{
+ pci_create_simple(bus, devfn, "piix3-usb-uhci");
+}
+
+void usb_uhci_piix4_init(PCIBus *bus, int devfn)
+{
+ pci_create_simple(bus, devfn, "piix4-usb-uhci");
+}
+
+void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn)
+{
+ pci_create_simple(bus, devfn, "vt82c686b-usb-uhci");
+}
diff --git a/hw/usb-uhci.h b/hw/usb-uhci.h
new file mode 100644
index 0000000..3e4d377
--- /dev/null
+++ b/hw/usb-uhci.h
@@ -0,0 +1,10 @@
+#ifndef QEMU_USB_UHCI_H
+#define QEMU_USB_UHCI_H
+
+#include "qemu-common.h"
+
+void usb_uhci_piix3_init(PCIBus *bus, int devfn);
+void usb_uhci_piix4_init(PCIBus *bus, int devfn);
+void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn);
+
+#endif
diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c
new file mode 100644
index 0000000..16be7a2
--- /dev/null
+++ b/hw/usb-wacom.c
@@ -0,0 +1,370 @@
+/*
+ * Wacom PenPartner USB tablet emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Author: Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Based on hw/usb-hid.c:
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "usb.h"
+#include "usb-desc.h"
+
+/* Interface requests */
+#define WACOM_GET_REPORT 0x2101
+#define WACOM_SET_REPORT 0x2109
+
+/* HID interface requests */
+#define HID_GET_REPORT 0xa101
+#define HID_GET_IDLE 0xa102
+#define HID_GET_PROTOCOL 0xa103
+#define HID_SET_IDLE 0x210a
+#define HID_SET_PROTOCOL 0x210b
+
+typedef struct USBWacomState {
+ USBDevice dev;
+ QEMUPutMouseEntry *eh_entry;
+ int dx, dy, dz, buttons_state;
+ int x, y;
+ int mouse_grabbed;
+ enum {
+ WACOM_MODE_HID = 1,
+ WACOM_MODE_WACOM = 2,
+ } mode;
+ uint8_t idle;
+ int changed;
+} USBWacomState;
+
+enum {
+ STR_MANUFACTURER = 1,
+ STR_PRODUCT,
+ STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+ [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
+ [STR_PRODUCT] = "Wacom PenPartner",
+ [STR_SERIALNUMBER] = "1",
+};
+
+static const USBDescIface desc_iface_wacom = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0x01, /* boot */
+ .bInterfaceProtocol = 0x02,
+ .ndesc = 1,
+ .descs = (USBDescOther[]) {
+ {
+ /* HID descriptor */
+ .data = (uint8_t[]) {
+ 0x09, /* u8 bLength */
+ 0x21, /* u8 bDescriptorType */
+ 0x01, 0x10, /* u16 HID_class */
+ 0x00, /* u8 country_code */
+ 0x01, /* u8 num_descriptors */
+ 0x22, /* u8 type: Report */
+ 0x6e, 0, /* u16 len */
+ },
+ },
+ },
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_IN | 0x01,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 8,
+ .bInterval = 0x0a,
+ },
+ },
+};
+
+static const USBDescDevice desc_device_wacom = {
+ .bcdUSB = 0x0110,
+ .bMaxPacketSize0 = 8,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .bmAttributes = 0x80,
+ .bMaxPower = 40,
+ .ifs = &desc_iface_wacom,
+ },
+ },
+};
+
+static const USBDesc desc_wacom = {
+ .id = {
+ .idVendor = 0x056a,
+ .idProduct = 0x0000,
+ .bcdDevice = 0x4210,
+ .iManufacturer = STR_MANUFACTURER,
+ .iProduct = STR_PRODUCT,
+ .iSerialNumber = STR_SERIALNUMBER,
+ },
+ .full = &desc_device_wacom,
+ .str = desc_strings,
+};
+
+static void usb_mouse_event(void *opaque,
+ int dx1, int dy1, int dz1, int buttons_state)
+{
+ USBWacomState *s = opaque;
+
+ s->dx += dx1;
+ s->dy += dy1;
+ s->dz += dz1;
+ s->buttons_state = buttons_state;
+ s->changed = 1;
+}
+
+static void usb_wacom_event(void *opaque,
+ int x, int y, int dz, int buttons_state)
+{
+ USBWacomState *s = opaque;
+
+ /* scale to Penpartner resolution */
+ s->x = (x * 5040 / 0x7FFF);
+ s->y = (y * 3780 / 0x7FFF);
+ s->dz += dz;
+ s->buttons_state = buttons_state;
+ s->changed = 1;
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+ if (val < vmin)
+ return vmin;
+ else if (val > vmax)
+ return vmax;
+ else
+ return val;
+}
+
+static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
+{
+ int dx, dy, dz, b, l;
+
+ if (!s->mouse_grabbed) {
+ s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
+ "QEMU PenPartner tablet");
+ qemu_activate_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 1;
+ }
+
+ dx = int_clamp(s->dx, -128, 127);
+ dy = int_clamp(s->dy, -128, 127);
+ dz = int_clamp(s->dz, -128, 127);
+
+ s->dx -= dx;
+ s->dy -= dy;
+ s->dz -= dz;
+
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x02;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x04;
+
+ buf[0] = b;
+ buf[1] = dx;
+ buf[2] = dy;
+ l = 3;
+ if (len >= 4) {
+ buf[3] = dz;
+ l = 4;
+ }
+ return l;
+}
+
+static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
+{
+ int b;
+
+ if (!s->mouse_grabbed) {
+ s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
+ "QEMU PenPartner tablet");
+ qemu_activate_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 1;
+ }
+
+ b = 0;
+ if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+ b |= 0x01;
+ if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+ b |= 0x40;
+ if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+ b |= 0x20; /* eraser */
+
+ if (len < 7)
+ return 0;
+
+ buf[0] = s->mode;
+ buf[5] = 0x00 | (b & 0xf0);
+ buf[1] = s->x & 0xff;
+ buf[2] = s->x >> 8;
+ buf[3] = s->y & 0xff;
+ buf[4] = s->y >> 8;
+ if (b & 0x3f) {
+ buf[6] = 0;
+ } else {
+ buf[6] = (unsigned char) -127;
+ }
+
+ return 7;
+}
+
+static void usb_wacom_handle_reset(USBDevice *dev)
+{
+ USBWacomState *s = (USBWacomState *) dev;
+
+ s->dx = 0;
+ s->dy = 0;
+ s->dz = 0;
+ s->x = 0;
+ s->y = 0;
+ s->buttons_state = 0;
+ s->mode = WACOM_MODE_HID;
+}
+
+static int usb_wacom_handle_control(USBDevice *dev, 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);
+ if (ret >= 0) {
+ return ret;
+ }
+
+ ret = 0;
+ switch (request) {
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ data[0] = 0;
+ ret = 1;
+ break;
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = 0;
+ break;
+ case WACOM_SET_REPORT:
+ if (s->mouse_grabbed) {
+ qemu_remove_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 0;
+ }
+ s->mode = data[0];
+ ret = 0;
+ break;
+ case WACOM_GET_REPORT:
+ data[0] = 0;
+ data[1] = s->mode;
+ ret = 2;
+ break;
+ /* USB HID requests */
+ case HID_GET_REPORT:
+ if (s->mode == WACOM_MODE_HID)
+ ret = usb_mouse_poll(s, data, length);
+ else if (s->mode == WACOM_MODE_WACOM)
+ ret = usb_wacom_poll(s, data, length);
+ break;
+ case HID_GET_IDLE:
+ ret = 1;
+ data[0] = s->idle;
+ break;
+ case HID_SET_IDLE:
+ s->idle = (uint8_t) (value >> 8);
+ ret = 0;
+ break;
+ default:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBWacomState *s = (USBWacomState *) dev;
+ int ret = 0;
+
+ switch (p->pid) {
+ case USB_TOKEN_IN:
+ if (p->devep == 1) {
+ if (!(s->changed || s->idle))
+ return USB_RET_NAK;
+ s->changed = 0;
+ if (s->mode == WACOM_MODE_HID)
+ ret = usb_mouse_poll(s, p->data, p->len);
+ else if (s->mode == WACOM_MODE_WACOM)
+ ret = usb_wacom_poll(s, p->data, p->len);
+ break;
+ }
+ /* Fall through. */
+ case USB_TOKEN_OUT:
+ default:
+ ret = USB_RET_STALL;
+ break;
+ }
+ return ret;
+}
+
+static void usb_wacom_handle_destroy(USBDevice *dev)
+{
+ USBWacomState *s = (USBWacomState *) dev;
+
+ if (s->mouse_grabbed) {
+ qemu_remove_mouse_event_handler(s->eh_entry);
+ s->mouse_grabbed = 0;
+ }
+}
+
+static int usb_wacom_initfn(USBDevice *dev)
+{
+ USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
+ usb_desc_init(dev);
+ s->changed = 1;
+ return 0;
+}
+
+static struct USBDeviceInfo wacom_info = {
+ .product_desc = "QEMU PenPartner Tablet",
+ .qdev.name = "usb-wacom-tablet",
+ .qdev.desc = "QEMU PenPartner Tablet",
+ .usbdevice_name = "wacom-tablet",
+ .usb_desc = &desc_wacom,
+ .qdev.size = sizeof(USBWacomState),
+ .init = usb_wacom_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_wacom_handle_reset,
+ .handle_control = usb_wacom_handle_control,
+ .handle_data = usb_wacom_handle_data,
+ .handle_destroy = usb_wacom_handle_destroy,
+};
+
+static void usb_wacom_register_devices(void)
+{
+ usb_qdev_register(&wacom_info);
+}
+device_init(usb_wacom_register_devices)
diff --git a/hw/usb.c b/hw/usb.c
new file mode 100644
index 0000000..82a6217
--- /dev/null
+++ b/hw/usb.c
@@ -0,0 +1,261 @@
+/*
+ * QEMU USB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 2008 Generic packet handler rewrite by Max Krasnyansky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "usb.h"
+
+void usb_attach(USBPort *port, USBDevice *dev)
+{
+ if (dev != NULL) {
+ /* attach */
+ if (port->dev) {
+ usb_attach(port, NULL);
+ }
+ dev->port = port;
+ port->dev = dev;
+ port->ops->attach(port);
+ usb_send_msg(dev, USB_MSG_ATTACH);
+ } else {
+ /* detach */
+ dev = port->dev;
+ port->ops->detach(port);
+ if (dev) {
+ usb_send_msg(dev, USB_MSG_DETACH);
+ dev->port = NULL;
+ port->dev = NULL;
+ }
+ }
+}
+
+void usb_wakeup(USBDevice *dev)
+{
+ if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
+ dev->port->ops->wakeup(dev);
+ }
+}
+
+/**********************/
+
+/* generic USB device helpers (you are not forced to use them when
+ writing your USB device driver, but they help handling the
+ protocol)
+*/
+
+#define SETUP_STATE_IDLE 0
+#define SETUP_STATE_DATA 1
+#define SETUP_STATE_ACK 2
+
+static int do_token_setup(USBDevice *s, USBPacket *p)
+{
+ int request, value, index;
+ int ret = 0;
+
+ if (p->len != 8)
+ return USB_RET_STALL;
+
+ memcpy(s->setup_buf, p->data, 8);
+ s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
+ s->setup_index = 0;
+
+ 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,
+ s->setup_len, s->data_buf);
+ if (ret < 0)
+ return ret;
+
+ if (ret < s->setup_len)
+ s->setup_len = ret;
+ s->setup_state = SETUP_STATE_DATA;
+ } else {
+ if (s->setup_len == 0)
+ s->setup_state = SETUP_STATE_ACK;
+ else
+ s->setup_state = SETUP_STATE_DATA;
+ }
+
+ return ret;
+}
+
+static int do_token_in(USBDevice *s, USBPacket *p)
+{
+ int request, value, index;
+ int ret = 0;
+
+ if (p->devep != 0)
+ return s->info->handle_data(s, 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];
+
+ 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,
+ s->setup_len, s->data_buf);
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ /* return 0 byte */
+ return 0;
+
+ case SETUP_STATE_DATA:
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ int len = s->setup_len - s->setup_index;
+ if (len > p->len)
+ len = p->len;
+ memcpy(p->data, s->data_buf + s->setup_index, len);
+ s->setup_index += len;
+ if (s->setup_index >= s->setup_len)
+ s->setup_state = SETUP_STATE_ACK;
+ return len;
+ }
+
+ s->setup_state = SETUP_STATE_IDLE;
+ return USB_RET_STALL;
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+static int do_token_out(USBDevice *s, USBPacket *p)
+{
+ if (p->devep != 0)
+ return s->info->handle_data(s, p);
+
+ switch(s->setup_state) {
+ case SETUP_STATE_ACK:
+ if (s->setup_buf[0] & USB_DIR_IN) {
+ s->setup_state = SETUP_STATE_IDLE;
+ /* transfer OK */
+ } else {
+ /* ignore additional output */
+ }
+ return 0;
+
+ case SETUP_STATE_DATA:
+ if (!(s->setup_buf[0] & USB_DIR_IN)) {
+ int len = s->setup_len - s->setup_index;
+ if (len > p->len)
+ len = p->len;
+ memcpy(s->data_buf + s->setup_index, p->data, len);
+ s->setup_index += len;
+ if (s->setup_index >= s->setup_len)
+ s->setup_state = SETUP_STATE_ACK;
+ return len;
+ }
+
+ s->setup_state = SETUP_STATE_IDLE;
+ return USB_RET_STALL;
+
+ default:
+ return USB_RET_STALL;
+ }
+}
+
+/*
+ * Generic packet handler.
+ * Called by the HC (host controller).
+ *
+ * Returns length of the transaction or one of the USB_RET_XXX codes.
+ */
+int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
+{
+ switch(p->pid) {
+ case USB_MSG_ATTACH:
+ s->state = USB_STATE_ATTACHED;
+ if (s->info->handle_attach) {
+ s->info->handle_attach(s);
+ }
+ 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;
+ if (s->info->handle_reset) {
+ 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;
+ }
+}
+
+/* XXX: fix overflow */
+int set_usb_string(uint8_t *buf, const char *str)
+{
+ int len, i;
+ uint8_t *q;
+
+ q = buf;
+ len = strlen(str);
+ *q++ = 2 * len + 2;
+ *q++ = 3;
+ for(i = 0; i < len; i++) {
+ *q++ = str[i];
+ *q++ = 0;
+ }
+ return q - buf;
+}
+
+/* Send an internal message to a USB device. */
+void usb_send_msg(USBDevice *dev, int msg)
+{
+ USBPacket p;
+ memset(&p, 0, sizeof(p));
+ p.pid = msg;
+ dev->info->handle_packet(dev, &p);
+
+ /* This _must_ be synchronous */
+}
diff --git a/hw/usb.h b/hw/usb.h
new file mode 100644
index 0000000..d3d755d
--- /dev/null
+++ b/hw/usb.h
@@ -0,0 +1,367 @@
+/*
+ * QEMU USB API
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "block.h"
+#include "qdev.h"
+#include "qemu-queue.h"
+
+#define USB_TOKEN_SETUP 0x2d
+#define USB_TOKEN_IN 0x69 /* device -> host */
+#define USB_TOKEN_OUT 0xe1 /* host -> device */
+
+/* specific usb messages, also sent in the 'pid' parameter */
+#define USB_MSG_ATTACH 0x100
+#define USB_MSG_DETACH 0x101
+#define USB_MSG_RESET 0x102
+
+#define USB_RET_NODEV (-1)
+#define USB_RET_NAK (-2)
+#define USB_RET_STALL (-3)
+#define USB_RET_BABBLE (-4)
+#define USB_RET_ASYNC (-5)
+
+#define USB_SPEED_LOW 0
+#define USB_SPEED_FULL 1
+#define USB_SPEED_HIGH 2
+#define USB_SPEED_SUPER 3
+
+#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW)
+#define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL)
+#define USB_SPEED_MASK_HIGH (1 << USB_SPEED_HIGH)
+#define USB_SPEED_MASK_SUPER (1 << USB_SPEED_SUPER)
+
+#define USB_STATE_NOTATTACHED 0
+#define USB_STATE_ATTACHED 1
+//#define USB_STATE_POWERED 2
+#define USB_STATE_DEFAULT 3
+//#define USB_STATE_ADDRESS 4
+//#define USB_STATE_CONFIGURED 5
+#define USB_STATE_SUSPENDED 6
+
+#define USB_CLASS_AUDIO 1
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_PHYSICAL 5
+#define USB_CLASS_STILL_IMAGE 6
+#define USB_CLASS_PRINTER 7
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_CDC_DATA 0x0a
+#define USB_CLASS_CSCID 0x0b
+#define USB_CLASS_CONTENT_SEC 0x0d
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+#define USB_DIR_OUT 0
+#define USB_DIR_IN 0x80
+
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define InterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define InterfaceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+#define EndpointOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+#define ClassInterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
+#define ClassInterfaceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
+
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+#define USB_DEVICE_SELF_POWERED 0
+#define USB_DEVICE_REMOTE_WAKEUP 1
+
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIG 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+#define USB_DT_DEVICE_QUALIFIER 0x06
+#define USB_DT_OTHER_SPEED_CONFIG 0x07
+
+#define USB_ENDPOINT_XFER_CONTROL 0
+#define USB_ENDPOINT_XFER_ISOC 1
+#define USB_ENDPOINT_XFER_BULK 2
+#define USB_ENDPOINT_XFER_INT 3
+
+typedef struct USBBus USBBus;
+typedef struct USBPort USBPort;
+typedef struct USBDevice USBDevice;
+typedef struct USBDeviceInfo USBDeviceInfo;
+typedef struct USBPacket USBPacket;
+
+typedef struct USBDesc USBDesc;
+typedef struct USBDescID USBDescID;
+typedef struct USBDescDevice USBDescDevice;
+typedef struct USBDescConfig USBDescConfig;
+typedef struct USBDescIface USBDescIface;
+typedef struct USBDescEndpoint USBDescEndpoint;
+typedef struct USBDescOther USBDescOther;
+typedef struct USBDescString USBDescString;
+
+struct USBDescString {
+ uint8_t index;
+ char *str;
+ QLIST_ENTRY(USBDescString) next;
+};
+
+/* definition of a USB device */
+struct USBDevice {
+ DeviceState qdev;
+ USBDeviceInfo *info;
+ USBPort *port;
+ char *port_path;
+ void *opaque;
+
+ int speed;
+ uint8_t addr;
+ char product_desc[32];
+ int auto_attach;
+ int attached;
+
+ int32_t state;
+ uint8_t setup_buf[8];
+ uint8_t data_buf[1024];
+ int32_t remote_wakeup;
+ int32_t setup_state;
+ int32_t setup_len;
+ int32_t setup_index;
+
+ QLIST_HEAD(, USBDescString) strings;
+ const USBDescDevice *device;
+ const USBDescConfig *config;
+};
+
+struct USBDeviceInfo {
+ DeviceInfo qdev;
+ int (*init)(USBDevice *dev);
+
+ /*
+ * Process USB packet.
+ * Called by the HC (Host Controller).
+ *
+ * Returns length of the transaction
+ * or one of the USB_RET_XXX codes.
+ */
+ int (*handle_packet)(USBDevice *dev, USBPacket *p);
+
+ /*
+ * Called when device is destroyed.
+ */
+ void (*handle_destroy)(USBDevice *dev);
+
+ /*
+ * Attach the device
+ */
+ void (*handle_attach)(USBDevice *dev);
+
+ /*
+ * Reset the device
+ */
+ void (*handle_reset)(USBDevice *dev);
+
+ /*
+ * Process control request.
+ * Called from handle_packet().
+ *
+ * Returns length or one of the USB_RET_ codes.
+ */
+ int (*handle_control)(USBDevice *dev, int request, int value,
+ int index, int length, uint8_t *data);
+
+ /*
+ * Process data transfers (both BULK and ISOC).
+ * Called from handle_packet().
+ *
+ * Returns length or one of the USB_RET_ codes.
+ */
+ int (*handle_data)(USBDevice *dev, USBPacket *p);
+
+ const char *product_desc;
+ const USBDesc *usb_desc;
+
+ /* handle legacy -usbdevice command line options */
+ const char *usbdevice_name;
+ USBDevice *(*usbdevice_init)(const char *params);
+};
+
+typedef struct USBPortOps {
+ void (*attach)(USBPort *port);
+ void (*detach)(USBPort *port);
+ void (*wakeup)(USBDevice *dev);
+} USBPortOps;
+
+/* USB port on which a device can be connected */
+struct USBPort {
+ USBDevice *dev;
+ int speedmask;
+ char path[16];
+ USBPortOps *ops;
+ void *opaque;
+ int index; /* internal port index, may be used with the opaque */
+ QTAILQ_ENTRY(USBPort) next;
+};
+
+typedef void USBCallback(USBPacket * packet, void *opaque);
+
+/* Structure used to hold information about an active USB packet. */
+struct USBPacket {
+ /* Data fields for use by the driver. */
+ int pid;
+ uint8_t devaddr;
+ uint8_t devep;
+ uint8_t *data;
+ int len;
+ /* Internal use by the USB layer. */
+ USBCallback *complete_cb;
+ void *complete_opaque;
+ USBCallback *cancel_cb;
+ void *cancel_opaque;
+};
+
+/* 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;
+}
+
+/* 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(USBPacket *p)
+{
+ p->complete_cb(p, p->complete_opaque);
+}
+
+/* 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);
+int set_usb_string(uint8_t *buf, const char *str);
+void usb_send_msg(USBDevice *dev, int msg);
+
+/* usb-linux.c */
+USBDevice *usb_host_device_open(const char *devname);
+int usb_host_device_close(const char *devname);
+void usb_host_info(Monitor *mon);
+
+/* usb-hid.c */
+void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *));
+
+/* usb-bt.c */
+USBDevice *usb_bt_init(HCIInfo *hci);
+
+/* usb ports of the VM */
+
+#define VM_USB_HUB_SIZE 8
+
+/* usb-musb.c */
+enum musb_irq_source_e {
+ musb_irq_suspend = 0,
+ musb_irq_resume,
+ musb_irq_rst_babble,
+ musb_irq_sof,
+ musb_irq_connect,
+ musb_irq_disconnect,
+ musb_irq_vbus_request,
+ musb_irq_vbus_error,
+ musb_irq_rx,
+ musb_irq_tx,
+ musb_set_vbus,
+ musb_set_session,
+ __musb_irq_max,
+};
+
+typedef struct MUSBState MUSBState;
+MUSBState *musb_init(qemu_irq *irqs);
+uint32_t musb_core_intr_get(MUSBState *s);
+void musb_core_intr_clear(MUSBState *s, uint32_t mask);
+void musb_set_size(MUSBState *s, int epnum, int size, int is_tx);
+
+/* usb-bus.c */
+
+struct USBBus {
+ BusState qbus;
+ int busnr;
+ int nfree;
+ int nused;
+ QTAILQ_HEAD(, USBPort) free;
+ QTAILQ_HEAD(, USBPort) used;
+ QTAILQ_ENTRY(USBBus) next;
+};
+
+void usb_bus_new(USBBus *bus, DeviceState *host);
+USBBus *usb_bus_find(int busnr);
+void usb_qdev_register(USBDeviceInfo *info);
+void usb_qdev_register_many(USBDeviceInfo *info);
+USBDevice *usb_create(USBBus *bus, const char *name);
+USBDevice *usb_create_simple(USBBus *bus, const char *name);
+USBDevice *usbdevice_create(const char *cmdline);
+void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
+ USBPortOps *ops, int speedmask);
+void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr);
+void usb_unregister_port(USBBus *bus, USBPort *port);
+int usb_device_attach(USBDevice *dev);
+int usb_device_detach(USBDevice *dev);
+int usb_device_delete_addr(int busnr, int addr);
+
+static inline USBBus *usb_bus_from_device(USBDevice *d)
+{
+ return DO_UPCAST(USBBus, qbus, d->qdev.parent_bus);
+}
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
new file mode 100644
index 0000000..2fed8a0
--- /dev/null
+++ b/hw/versatile_pci.c
@@ -0,0 +1,160 @@
+/*
+ * ARM Versatile/PB PCI host controller
+ *
+ * Copyright (c) 2006-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "sysbus.h"
+#include "pci.h"
+#include "pci_host.h"
+
+typedef struct {
+ SysBusDevice busdev;
+ qemu_irq irq[4];
+ int realview;
+ int mem_config;
+} PCIVPBState;
+
+static inline uint32_t vpb_pci_config_addr(target_phys_addr_t addr)
+{
+ return addr & 0xffffff;
+}
+
+static void pci_vpb_config_writeb (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ pci_data_write(opaque, vpb_pci_config_addr (addr), val, 1);
+}
+
+static void pci_vpb_config_writew (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ pci_data_write(opaque, vpb_pci_config_addr (addr), val, 2);
+}
+
+static void pci_vpb_config_writel (void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ pci_data_write(opaque, vpb_pci_config_addr (addr), val, 4);
+}
+
+static uint32_t pci_vpb_config_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ val = pci_data_read(opaque, vpb_pci_config_addr (addr), 1);
+ return val;
+}
+
+static uint32_t pci_vpb_config_readw (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ val = pci_data_read(opaque, vpb_pci_config_addr (addr), 2);
+ return val;
+}
+
+static uint32_t pci_vpb_config_readl (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ val = pci_data_read(opaque, vpb_pci_config_addr (addr), 4);
+ return val;
+}
+
+static CPUWriteMemoryFunc * const pci_vpb_config_write[] = {
+ &pci_vpb_config_writeb,
+ &pci_vpb_config_writew,
+ &pci_vpb_config_writel,
+};
+
+static CPUReadMemoryFunc * const pci_vpb_config_read[] = {
+ &pci_vpb_config_readb,
+ &pci_vpb_config_readw,
+ &pci_vpb_config_readl,
+};
+
+static int pci_vpb_map_irq(PCIDevice *d, int irq_num)
+{
+ return irq_num;
+}
+
+static void pci_vpb_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+
+ qemu_set_irq(pic[irq_num], level);
+}
+
+static void pci_vpb_map(SysBusDevice *dev, target_phys_addr_t base)
+{
+ PCIVPBState *s = (PCIVPBState *)dev;
+ /* Selfconfig area. */
+ cpu_register_physical_memory(base + 0x01000000, 0x1000000, s->mem_config);
+ /* Normal config area. */
+ cpu_register_physical_memory(base + 0x02000000, 0x1000000, s->mem_config);
+
+ if (s->realview) {
+ /* IO memory area. */
+ isa_mmio_init(base + 0x03000000, 0x00100000);
+ }
+}
+
+static int pci_vpb_init(SysBusDevice *dev)
+{
+ PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev);
+ PCIBus *bus;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ sysbus_init_irq(dev, &s->irq[i]);
+ }
+ bus = pci_register_bus(&dev->qdev, "pci",
+ pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
+ PCI_DEVFN(11, 0), 4);
+
+ /* ??? Register memory space. */
+
+ s->mem_config = cpu_register_io_memory(pci_vpb_config_read,
+ pci_vpb_config_write, bus,
+ DEVICE_LITTLE_ENDIAN);
+ sysbus_init_mmio_cb(dev, 0x04000000, pci_vpb_map);
+
+ pci_create_simple(bus, -1, "versatile_pci_host");
+ return 0;
+}
+
+static int pci_realview_init(SysBusDevice *dev)
+{
+ PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev);
+ s->realview = 1;
+ return pci_vpb_init(dev);
+}
+
+static int versatile_pci_host_init(PCIDevice *d)
+{
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_XILINX);
+ /* Both boards have the same device ID. Oh well. */
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_XILINX_XC2VP30);
+ pci_set_word(d->config + PCI_STATUS,
+ PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM);
+ pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_CO);
+ pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10);
+ return 0;
+}
+
+static PCIDeviceInfo versatile_pci_host_info = {
+ .qdev.name = "versatile_pci_host",
+ .qdev.size = sizeof(PCIDevice),
+ .init = versatile_pci_host_init,
+};
+
+static void versatile_pci_register_devices(void)
+{
+ sysbus_register_dev("versatile_pci", sizeof(PCIVPBState), pci_vpb_init);
+ sysbus_register_dev("realview_pci", sizeof(PCIVPBState),
+ pci_realview_init);
+ pci_qdev_register(&versatile_pci_host_info);
+}
+
+device_init(versatile_pci_register_devices)
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
new file mode 100644
index 0000000..9f1bfcf
--- /dev/null
+++ b/hw/versatilepb.c
@@ -0,0 +1,362 @@
+/*
+ * ARM Versatile Platform/Application Baseboard System emulation.
+ *
+ * Copyright (c) 2005-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "sysbus.h"
+#include "arm-misc.h"
+#include "primecell.h"
+#include "devices.h"
+#include "net.h"
+#include "sysemu.h"
+#include "pci.h"
+#include "usb-ohci.h"
+#include "boards.h"
+#include "blockdev.h"
+
+/* Primary interrupt controller. */
+
+typedef struct vpb_sic_state
+{
+ SysBusDevice busdev;
+ uint32_t level;
+ uint32_t mask;
+ uint32_t pic_enable;
+ qemu_irq parent[32];
+ int irq;
+} vpb_sic_state;
+
+static const VMStateDescription vmstate_vpb_sic = {
+ .name = "versatilepb_sic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, vpb_sic_state),
+ VMSTATE_UINT32(mask, vpb_sic_state),
+ VMSTATE_UINT32(pic_enable, vpb_sic_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void vpb_sic_update(vpb_sic_state *s)
+{
+ uint32_t flags;
+
+ flags = s->level & s->mask;
+ qemu_set_irq(s->parent[s->irq], flags != 0);
+}
+
+static void vpb_sic_update_pic(vpb_sic_state *s)
+{
+ int i;
+ uint32_t mask;
+
+ for (i = 21; i <= 30; i++) {
+ mask = 1u << i;
+ if (!(s->pic_enable & mask))
+ continue;
+ qemu_set_irq(s->parent[i], (s->level & mask) != 0);
+ }
+}
+
+static void vpb_sic_set_irq(void *opaque, int irq, int level)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+ if (level)
+ s->level |= 1u << irq;
+ else
+ s->level &= ~(1u << irq);
+ if (s->pic_enable & (1u << irq))
+ qemu_set_irq(s->parent[irq], level);
+ vpb_sic_update(s);
+}
+
+static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 0: /* STATUS */
+ return s->level & s->mask;
+ case 1: /* RAWSTAT */
+ return s->level;
+ case 2: /* ENABLE */
+ return s->mask;
+ case 4: /* SOFTINT */
+ return s->level & 1;
+ case 8: /* PICENABLE */
+ return s->pic_enable;
+ default:
+ printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void vpb_sic_write(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ vpb_sic_state *s = (vpb_sic_state *)opaque;
+
+ switch (offset >> 2) {
+ case 2: /* ENSET */
+ s->mask |= value;
+ break;
+ case 3: /* ENCLR */
+ s->mask &= ~value;
+ break;
+ case 4: /* SOFTINTSET */
+ if (value)
+ s->mask |= 1;
+ break;
+ case 5: /* SOFTINTCLR */
+ if (value)
+ s->mask &= ~1u;
+ break;
+ case 8: /* PICENSET */
+ s->pic_enable |= (value & 0x7fe00000);
+ vpb_sic_update_pic(s);
+ break;
+ case 9: /* PICENCLR */
+ s->pic_enable &= ~value;
+ vpb_sic_update_pic(s);
+ break;
+ default:
+ printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset);
+ return;
+ }
+ vpb_sic_update(s);
+}
+
+static CPUReadMemoryFunc * const vpb_sic_readfn[] = {
+ vpb_sic_read,
+ vpb_sic_read,
+ vpb_sic_read
+};
+
+static CPUWriteMemoryFunc * const vpb_sic_writefn[] = {
+ vpb_sic_write,
+ vpb_sic_write,
+ vpb_sic_write
+};
+
+static int vpb_sic_init(SysBusDevice *dev)
+{
+ vpb_sic_state *s = FROM_SYSBUS(vpb_sic_state, dev);
+ int iomemtype;
+ int i;
+
+ qdev_init_gpio_in(&dev->qdev, vpb_sic_set_irq, 32);
+ for (i = 0; i < 32; i++) {
+ sysbus_init_irq(dev, &s->parent[i]);
+ }
+ s->irq = 31;
+ iomemtype = cpu_register_io_memory(vpb_sic_readfn,
+ vpb_sic_writefn, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+ return 0;
+}
+
+/* Board init. */
+
+/* The AB and PB boards both use the same core, just with different
+ peripherans and expansion busses. For now we emulate a subset of the
+ PB peripherals and just change the board ID. */
+
+static struct arm_boot_info versatile_binfo;
+
+static void versatile_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model,
+ int board_id)
+{
+ CPUState *env;
+ ram_addr_t ram_offset;
+ qemu_irq *cpu_pic;
+ qemu_irq pic[32];
+ qemu_irq sic[32];
+ DeviceState *dev;
+ PCIBus *pci_bus;
+ NICInfo *nd;
+ int n;
+ int done_smc = 0;
+
+ if (!cpu_model)
+ cpu_model = "arm926";
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+ ram_offset = qemu_ram_alloc(NULL, "versatile.ram", ram_size);
+ /* ??? RAM should repeat to fill physical memory space. */
+ /* SDRAM at address zero. */
+ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM);
+
+ arm_sysctl_init(0x10000000, 0x41007004, 0x02000000);
+ cpu_pic = arm_pic_init_cpu(env);
+ dev = sysbus_create_varargs("pl190", 0x10140000,
+ cpu_pic[0], cpu_pic[1], NULL);
+ for (n = 0; n < 32; n++) {
+ pic[n] = qdev_get_gpio_in(dev, n);
+ }
+ dev = sysbus_create_simple("versatilepb_sic", 0x10003000, NULL);
+ for (n = 0; n < 32; n++) {
+ sysbus_connect_irq(sysbus_from_qdev(dev), n, pic[n]);
+ sic[n] = qdev_get_gpio_in(dev, n);
+ }
+
+ sysbus_create_simple("pl050_keyboard", 0x10006000, sic[3]);
+ sysbus_create_simple("pl050_mouse", 0x10007000, sic[4]);
+
+ dev = sysbus_create_varargs("versatile_pci", 0x40000000,
+ sic[27], sic[28], sic[29], sic[30], NULL);
+ pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci");
+
+ /* The Versatile PCI bridge does not provide access to PCI IO space,
+ so many of the qemu PCI devices are not useable. */
+ for(n = 0; n < nb_nics; n++) {
+ nd = &nd_table[n];
+
+ if ((!nd->model && !done_smc) || strcmp(nd->model, "smc91c111") == 0) {
+ smc91c111_init(nd, 0x10010000, sic[25]);
+ done_smc = 1;
+ } else {
+ pci_nic_init_nofail(nd, "rtl8139", NULL);
+ }
+ }
+ if (usb_enabled) {
+ usb_ohci_init_pci(pci_bus, -1);
+ }
+ n = drive_get_max_bus(IF_SCSI);
+ while (n >= 0) {
+ pci_create_simple(pci_bus, -1, "lsi53c895a");
+ n--;
+ }
+
+ sysbus_create_simple("pl011", 0x101f1000, pic[12]);
+ sysbus_create_simple("pl011", 0x101f2000, pic[13]);
+ sysbus_create_simple("pl011", 0x101f3000, pic[14]);
+ sysbus_create_simple("pl011", 0x10009000, sic[6]);
+
+ sysbus_create_simple("pl080", 0x10130000, pic[17]);
+ sysbus_create_simple("sp804", 0x101e2000, pic[4]);
+ sysbus_create_simple("sp804", 0x101e3000, pic[5]);
+
+ /* The versatile/PB actually has a modified Color LCD controller
+ that includes hardware cursor support from the PL111. */
+ sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]);
+
+ sysbus_create_varargs("pl181", 0x10005000, sic[22], sic[1], NULL);
+ sysbus_create_varargs("pl181", 0x1000b000, sic[23], sic[2], NULL);
+
+ /* Add PL031 Real Time Clock. */
+ sysbus_create_simple("pl031", 0x101e8000, pic[10]);
+
+ /* Memory map for Versatile/PB: */
+ /* 0x10000000 System registers. */
+ /* 0x10001000 PCI controller config registers. */
+ /* 0x10002000 Serial bus interface. */
+ /* 0x10003000 Secondary interrupt controller. */
+ /* 0x10004000 AACI (audio). */
+ /* 0x10005000 MMCI0. */
+ /* 0x10006000 KMI0 (keyboard). */
+ /* 0x10007000 KMI1 (mouse). */
+ /* 0x10008000 Character LCD Interface. */
+ /* 0x10009000 UART3. */
+ /* 0x1000a000 Smart card 1. */
+ /* 0x1000b000 MMCI1. */
+ /* 0x10010000 Ethernet. */
+ /* 0x10020000 USB. */
+ /* 0x10100000 SSMC. */
+ /* 0x10110000 MPMC. */
+ /* 0x10120000 CLCD Controller. */
+ /* 0x10130000 DMA Controller. */
+ /* 0x10140000 Vectored interrupt controller. */
+ /* 0x101d0000 AHB Monitor Interface. */
+ /* 0x101e0000 System Controller. */
+ /* 0x101e1000 Watchdog Interface. */
+ /* 0x101e2000 Timer 0/1. */
+ /* 0x101e3000 Timer 2/3. */
+ /* 0x101e4000 GPIO port 0. */
+ /* 0x101e5000 GPIO port 1. */
+ /* 0x101e6000 GPIO port 2. */
+ /* 0x101e7000 GPIO port 3. */
+ /* 0x101e8000 RTC. */
+ /* 0x101f0000 Smart card 0. */
+ /* 0x101f1000 UART0. */
+ /* 0x101f2000 UART1. */
+ /* 0x101f3000 UART2. */
+ /* 0x101f4000 SSPI. */
+
+ versatile_binfo.ram_size = ram_size;
+ versatile_binfo.kernel_filename = kernel_filename;
+ versatile_binfo.kernel_cmdline = kernel_cmdline;
+ versatile_binfo.initrd_filename = initrd_filename;
+ versatile_binfo.board_id = board_id;
+ arm_load_kernel(env, &versatile_binfo);
+}
+
+static void vpb_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ versatile_init(ram_size,
+ boot_device,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, 0x183);
+}
+
+static void vab_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename, const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ versatile_init(ram_size,
+ boot_device,
+ kernel_filename, kernel_cmdline,
+ initrd_filename, cpu_model, 0x25e);
+}
+
+static QEMUMachine versatilepb_machine = {
+ .name = "versatilepb",
+ .desc = "ARM Versatile/PB (ARM926EJ-S)",
+ .init = vpb_init,
+ .use_scsi = 1,
+};
+
+static QEMUMachine versatileab_machine = {
+ .name = "versatileab",
+ .desc = "ARM Versatile/AB (ARM926EJ-S)",
+ .init = vab_init,
+ .use_scsi = 1,
+};
+
+static void versatile_machine_init(void)
+{
+ qemu_register_machine(&versatilepb_machine);
+ qemu_register_machine(&versatileab_machine);
+}
+
+machine_init(versatile_machine_init);
+
+static SysBusDeviceInfo vpb_sic_info = {
+ .init = vpb_sic_init,
+ .qdev.name = "versatilepb_sic",
+ .qdev.size = sizeof(vpb_sic_state),
+ .qdev.vmsd = &vmstate_vpb_sic,
+ .qdev.no_user = 1,
+};
+
+static void versatilepb_register_devices(void)
+{
+ sysbus_register_withprop(&vpb_sic_info);
+}
+
+device_init(versatilepb_register_devices)
diff --git a/hw/vga-isa-mm.c b/hw/vga-isa-mm.c
new file mode 100644
index 0000000..4954bb1
--- /dev/null
+++ b/hw/vga-isa-mm.c
@@ -0,0 +1,128 @@
+/*
+ * QEMU ISA MM VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "pc.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+#include "qemu-timer.h"
+
+typedef struct ISAVGAMMState {
+ VGACommonState vga;
+ int it_shift;
+} ISAVGAMMState;
+
+/* Memory mapped interface */
+static uint32_t vga_mm_readb (void *opaque, target_phys_addr_t addr)
+{
+ ISAVGAMMState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xff;
+}
+
+static void vga_mm_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ISAVGAMMState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xff);
+}
+
+static uint32_t vga_mm_readw (void *opaque, target_phys_addr_t addr)
+{
+ ISAVGAMMState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xffff;
+}
+
+static void vga_mm_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ISAVGAMMState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xffff);
+}
+
+static uint32_t vga_mm_readl (void *opaque, target_phys_addr_t addr)
+{
+ ISAVGAMMState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift);
+}
+
+static void vga_mm_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t value)
+{
+ ISAVGAMMState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift, value);
+}
+
+static CPUReadMemoryFunc * const vga_mm_read_ctrl[] = {
+ &vga_mm_readb,
+ &vga_mm_readw,
+ &vga_mm_readl,
+};
+
+static CPUWriteMemoryFunc * const vga_mm_write_ctrl[] = {
+ &vga_mm_writeb,
+ &vga_mm_writew,
+ &vga_mm_writel,
+};
+
+static void vga_mm_init(ISAVGAMMState *s, target_phys_addr_t vram_base,
+ target_phys_addr_t ctrl_base, int it_shift)
+{
+ int s_ioport_ctrl, vga_io_memory;
+
+ s->it_shift = it_shift;
+ s_ioport_ctrl = cpu_register_io_memory(vga_mm_read_ctrl, vga_mm_write_ctrl, s,
+ DEVICE_NATIVE_ENDIAN);
+ vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s,
+ DEVICE_NATIVE_ENDIAN);
+
+ vmstate_register(NULL, 0, &vmstate_vga_common, s);
+
+ cpu_register_physical_memory(ctrl_base, 0x100000, s_ioport_ctrl);
+ s->vga.bank_offset = 0;
+ cpu_register_physical_memory(vram_base + 0x000a0000, 0x20000, vga_io_memory);
+ qemu_register_coalesced_mmio(vram_base + 0x000a0000, 0x20000);
+}
+
+int isa_vga_mm_init(target_phys_addr_t vram_base,
+ target_phys_addr_t ctrl_base, int it_shift)
+{
+ ISAVGAMMState *s;
+
+ s = qemu_mallocz(sizeof(*s));
+
+ vga_common_init(&s->vga, VGA_RAM_SIZE);
+ vga_mm_init(s, vram_base, ctrl_base, it_shift);
+
+ s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate,
+ s->vga.screen_dump, s->vga.text_update, s);
+
+ vga_init_vbe(&s->vga);
+ return 0;
+}
diff --git a/hw/vga-isa.c b/hw/vga-isa.c
new file mode 100644
index 0000000..3046054
--- /dev/null
+++ b/hw/vga-isa.c
@@ -0,0 +1,49 @@
+/*
+ * QEMU ISA VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "pc.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+#include "qemu-timer.h"
+#include "loader.h"
+
+int isa_vga_init(void)
+{
+ VGACommonState *s;
+
+ s = qemu_mallocz(sizeof(*s));
+
+ vga_common_init(s, VGA_RAM_SIZE);
+ vga_init(s);
+ vmstate_register(NULL, 0, &vmstate_vga_common, s);
+
+ s->ds = graphic_console_init(s->update, s->invalidate,
+ s->screen_dump, s->text_update, s);
+
+ vga_init_vbe(s);
+ /* ROM BIOS */
+ rom_add_vga(VGABIOS_FILENAME);
+ return 0;
+}
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
new file mode 100644
index 0000000..1ef2d96
--- /dev/null
+++ b/hw/vga-pci.c
@@ -0,0 +1,125 @@
+/*
+ * QEMU PCI VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "pc.h"
+#include "pci.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+#include "qemu-timer.h"
+#include "loader.h"
+
+typedef struct PCIVGAState {
+ PCIDevice dev;
+ VGACommonState vga;
+} PCIVGAState;
+
+static const VMStateDescription vmstate_vga_pci = {
+ .name = "vga",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCIVGAState),
+ VMSTATE_STRUCT(vga, PCIVGAState, 0, vmstate_vga_common, VGACommonState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void vga_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCIVGAState *d = (PCIVGAState *)pci_dev;
+ VGACommonState *s = &d->vga;
+
+ cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
+ s->map_addr = addr;
+ s->map_end = addr + s->vram_size;
+ vga_dirty_log_start(s);
+}
+
+static void pci_vga_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ PCIVGAState *pvs = container_of(d, PCIVGAState, dev);
+ VGACommonState *s = &pvs->vga;
+
+ vga_dirty_log_stop(s);
+ pci_default_write_config(d, address, val, len);
+ if (s->map_addr && pvs->dev.io_regions[0].addr == -1)
+ s->map_addr = 0;
+ vga_dirty_log_start(s);
+}
+
+static int pci_vga_initfn(PCIDevice *dev)
+{
+ PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
+ VGACommonState *s = &d->vga;
+ uint8_t *pci_conf = d->dev.config;
+
+ // vga + console init
+ vga_common_init(s, VGA_RAM_SIZE);
+ vga_init(s);
+
+ s->ds = graphic_console_init(s->update, s->invalidate,
+ s->screen_dump, s->text_update, s);
+
+ // dummy VGA (same as Bochs ID)
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_QEMU);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_QEMU_VGA);
+ pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA);
+
+ /* XXX: VGA_RAM_SIZE must be a power of two */
+ pci_register_bar(&d->dev, 0, VGA_RAM_SIZE,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, vga_map);
+
+ if (!dev->rom_bar) {
+ /* compatibility with pc-0.13 and older */
+ vga_init_vbe(s);
+ }
+
+ return 0;
+}
+
+int pci_vga_init(PCIBus *bus)
+{
+ pci_create_simple(bus, -1, "VGA");
+ return 0;
+}
+
+static PCIDeviceInfo vga_info = {
+ .qdev.name = "VGA",
+ .qdev.size = sizeof(PCIVGAState),
+ .qdev.vmsd = &vmstate_vga_pci,
+ .no_hotplug = 1,
+ .init = pci_vga_initfn,
+ .config_write = pci_vga_write_config,
+ .romfile = "vgabios-stdvga.bin",
+};
+
+static void vga_register(void)
+{
+ pci_qdev_register(&vga_info);
+}
+device_init(vga_register);
diff --git a/hw/vga.c b/hw/vga.c
new file mode 100644
index 0000000..3d15c15
--- /dev/null
+++ b/hw/vga.c
@@ -0,0 +1,2429 @@
+/*
+ * QEMU VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "pc.h"
+#include "pci.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+#include "qemu-timer.h"
+#include "kvm.h"
+
+//#define DEBUG_VGA
+//#define DEBUG_VGA_MEM
+//#define DEBUG_VGA_REG
+
+//#define DEBUG_BOCHS_VBE
+
+/* force some bits to zero */
+const uint8_t sr_mask[8] = {
+ 0x03,
+ 0x3d,
+ 0x0f,
+ 0x3f,
+ 0x0e,
+ 0x00,
+ 0x00,
+ 0xff,
+};
+
+const uint8_t gr_mask[16] = {
+ 0x0f, /* 0x00 */
+ 0x0f, /* 0x01 */
+ 0x0f, /* 0x02 */
+ 0x1f, /* 0x03 */
+ 0x03, /* 0x04 */
+ 0x7b, /* 0x05 */
+ 0x0f, /* 0x06 */
+ 0x0f, /* 0x07 */
+ 0xff, /* 0x08 */
+ 0x00, /* 0x09 */
+ 0x00, /* 0x0a */
+ 0x00, /* 0x0b */
+ 0x00, /* 0x0c */
+ 0x00, /* 0x0d */
+ 0x00, /* 0x0e */
+ 0x00, /* 0x0f */
+};
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
+ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
+ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define PAT(x) cbswap_32(x)
+#else
+#define PAT(x) (x)
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define BIG 1
+#else
+#define BIG 0
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
+#else
+#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
+#endif
+
+static const uint32_t mask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+#undef PAT
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define PAT(x) (x)
+#else
+#define PAT(x) cbswap_32(x)
+#endif
+
+static const uint32_t dmask16[16] = {
+ PAT(0x00000000),
+ PAT(0x000000ff),
+ PAT(0x0000ff00),
+ PAT(0x0000ffff),
+ PAT(0x00ff0000),
+ PAT(0x00ff00ff),
+ PAT(0x00ffff00),
+ PAT(0x00ffffff),
+ PAT(0xff000000),
+ PAT(0xff0000ff),
+ PAT(0xff00ff00),
+ PAT(0xff00ffff),
+ PAT(0xffff0000),
+ PAT(0xffff00ff),
+ PAT(0xffffff00),
+ PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+ PAT(0x00000000),
+ PAT(0x0000ffff),
+ PAT(0xffff0000),
+ PAT(0xffffffff),
+};
+
+static uint32_t expand4[256];
+static uint16_t expand2[256];
+static uint8_t expand4to8[16];
+
+static void vga_screen_dump(void *opaque, const char *filename);
+static char *screen_dump_filename;
+static DisplayChangeListener *screen_dump_dcl;
+
+static void vga_dumb_update_retrace_info(VGACommonState *s)
+{
+ (void) s;
+}
+
+static void vga_precise_update_retrace_info(VGACommonState *s)
+{
+ int htotal_chars;
+ int hretr_start_char;
+ int hretr_skew_chars;
+ int hretr_end_char;
+
+ int vtotal_lines;
+ int vretr_start_line;
+ int vretr_end_line;
+
+ int dots;
+#if 0
+ int div2, sldiv2;
+#endif
+ int clocking_mode;
+ int clock_sel;
+ const int clk_hz[] = {25175000, 28322000, 25175000, 25175000};
+ int64_t chars_per_sec;
+ struct vga_precise_retrace *r = &s->retrace_info.precise;
+
+ htotal_chars = s->cr[0x00] + 5;
+ hretr_start_char = s->cr[0x04];
+ hretr_skew_chars = (s->cr[0x05] >> 5) & 3;
+ hretr_end_char = s->cr[0x05] & 0x1f;
+
+ vtotal_lines = (s->cr[0x06]
+ | (((s->cr[0x07] & 1) | ((s->cr[0x07] >> 4) & 2)) << 8)) + 2
+ ;
+ vretr_start_line = s->cr[0x10]
+ | ((((s->cr[0x07] >> 2) & 1) | ((s->cr[0x07] >> 6) & 2)) << 8)
+ ;
+ vretr_end_line = s->cr[0x11] & 0xf;
+
+
+
+ clocking_mode = (s->sr[0x01] >> 3) & 1;
+ clock_sel = (s->msr >> 2) & 3;
+ dots = (s->msr & 1) ? 8 : 9;
+
+ chars_per_sec = clk_hz[clock_sel] / dots;
+
+ htotal_chars <<= clocking_mode;
+
+ r->total_chars = vtotal_lines * htotal_chars;
+ if (r->freq) {
+ r->ticks_per_char = get_ticks_per_sec() / (r->total_chars * r->freq);
+ } else {
+ r->ticks_per_char = get_ticks_per_sec() / chars_per_sec;
+ }
+
+ r->vstart = vretr_start_line;
+ r->vend = r->vstart + vretr_end_line + 1;
+
+ r->hstart = hretr_start_char + hretr_skew_chars;
+ r->hend = r->hstart + hretr_end_char + 1;
+ r->htotal = htotal_chars;
+
+#if 0
+ div2 = (s->cr[0x17] >> 2) & 1;
+ sldiv2 = (s->cr[0x17] >> 3) & 1;
+ printf (
+ "hz=%f\n"
+ "htotal = %d\n"
+ "hretr_start = %d\n"
+ "hretr_skew = %d\n"
+ "hretr_end = %d\n"
+ "vtotal = %d\n"
+ "vretr_start = %d\n"
+ "vretr_end = %d\n"
+ "div2 = %d sldiv2 = %d\n"
+ "clocking_mode = %d\n"
+ "clock_sel = %d %d\n"
+ "dots = %d\n"
+ "ticks/char = %" PRId64 "\n"
+ "\n",
+ (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars),
+ htotal_chars,
+ hretr_start_char,
+ hretr_skew_chars,
+ hretr_end_char,
+ vtotal_lines,
+ vretr_start_line,
+ vretr_end_line,
+ div2, sldiv2,
+ clocking_mode,
+ clock_sel,
+ clk_hz[clock_sel],
+ dots,
+ r->ticks_per_char
+ );
+#endif
+}
+
+static uint8_t vga_precise_retrace(VGACommonState *s)
+{
+ struct vga_precise_retrace *r = &s->retrace_info.precise;
+ uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE);
+
+ if (r->total_chars) {
+ int cur_line, cur_line_char, cur_char;
+ int64_t cur_tick;
+
+ cur_tick = qemu_get_clock(vm_clock);
+
+ cur_char = (cur_tick / r->ticks_per_char) % r->total_chars;
+ cur_line = cur_char / r->htotal;
+
+ if (cur_line >= r->vstart && cur_line <= r->vend) {
+ val |= ST01_V_RETRACE | ST01_DISP_ENABLE;
+ } else {
+ cur_line_char = cur_char % r->htotal;
+ if (cur_line_char >= r->hstart && cur_line_char <= r->hend) {
+ val |= ST01_DISP_ENABLE;
+ }
+ }
+
+ return val;
+ } else {
+ return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE);
+ }
+}
+
+static uint8_t vga_dumb_retrace(VGACommonState *s)
+{
+ return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE);
+}
+
+int vga_ioport_invalid(VGACommonState *s, uint32_t addr)
+{
+ if (s->msr & MSR_COLOR_EMULATION) {
+ /* Color */
+ return (addr >= 0x3b0 && addr <= 0x3bf);
+ } else {
+ /* Monochrome */
+ return (addr >= 0x3d0 && addr <= 0x3df);
+ }
+}
+
+uint32_t vga_ioport_read(void *opaque, uint32_t addr)
+{
+ VGACommonState *s = opaque;
+ int val, index;
+
+ if (vga_ioport_invalid(s, addr)) {
+ val = 0xff;
+ } else {
+ switch(addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val = s->ar_index;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x3c1:
+ index = s->ar_index & 0x1f;
+ if (index < 21)
+ val = s->ar[index];
+ else
+ val = 0;
+ break;
+ case 0x3c2:
+ val = s->st00;
+ break;
+ case 0x3c4:
+ val = s->sr_index;
+ break;
+ case 0x3c5:
+ val = s->sr[s->sr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ break;
+ case 0x3c7:
+ val = s->dac_state;
+ break;
+ case 0x3c8:
+ val = s->dac_write_index;
+ break;
+ case 0x3c9:
+ val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
+ if (++s->dac_sub_index == 3) {
+ s->dac_sub_index = 0;
+ s->dac_read_index++;
+ }
+ break;
+ case 0x3ca:
+ val = s->fcr;
+ break;
+ case 0x3cc:
+ val = s->msr;
+ break;
+ case 0x3ce:
+ val = s->gr_index;
+ break;
+ case 0x3cf:
+ val = s->gr[s->gr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ val = s->cr_index;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ val = s->cr[s->cr_index];
+#ifdef DEBUG_VGA_REG
+ printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ break;
+ case 0x3ba:
+ case 0x3da:
+ /* just toggle to fool polling */
+ val = s->st01 = s->retrace(s);
+ s->ar_flip_flop = 0;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ }
+#if defined(DEBUG_VGA)
+ printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+ return val;
+}
+
+void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGACommonState *s = opaque;
+ int index;
+
+ /* check port range access depending on color/monochrome mode */
+ if (vga_ioport_invalid(s, addr)) {
+ return;
+ }
+#ifdef DEBUG_VGA
+ printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+
+ switch(addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val &= 0x3f;
+ s->ar_index = val;
+ } else {
+ index = s->ar_index & 0x1f;
+ switch(index) {
+ case 0x00 ... 0x0f:
+ s->ar[index] = val & 0x3f;
+ break;
+ case 0x10:
+ s->ar[index] = val & ~0x10;
+ break;
+ case 0x11:
+ s->ar[index] = val;
+ break;
+ case 0x12:
+ s->ar[index] = val & ~0xc0;
+ break;
+ case 0x13:
+ s->ar[index] = val & ~0xf0;
+ break;
+ case 0x14:
+ s->ar[index] = val & ~0xf0;
+ break;
+ default:
+ break;
+ }
+ }
+ s->ar_flip_flop ^= 1;
+ break;
+ case 0x3c2:
+ s->msr = val & ~0x10;
+ s->update_retrace_info(s);
+ break;
+ case 0x3c4:
+ s->sr_index = val & 7;
+ break;
+ case 0x3c5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+ if (s->sr_index == 1) s->update_retrace_info(s);
+ break;
+ case 0x3c7:
+ s->dac_read_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 3;
+ break;
+ case 0x3c8:
+ s->dac_write_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 0;
+ break;
+ case 0x3c9:
+ s->dac_cache[s->dac_sub_index] = val;
+ if (++s->dac_sub_index == 3) {
+ memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
+ s->dac_sub_index = 0;
+ s->dac_write_index++;
+ }
+ break;
+ case 0x3ce:
+ s->gr_index = val & 0x0f;
+ break;
+ case 0x3cf:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ s->cr_index = val;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ /* handle CR0-7 protection */
+ if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
+ /* can always write bit 4 of CR7 */
+ if (s->cr_index == 7)
+ s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
+ return;
+ }
+ s->cr[s->cr_index] = val;
+
+ switch(s->cr_index) {
+ case 0x00:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x11:
+ case 0x17:
+ s->update_retrace_info(s);
+ break;
+ }
+ break;
+ case 0x3ba:
+ case 0x3da:
+ s->fcr = val & 0x10;
+ break;
+ }
+}
+
+#ifdef CONFIG_BOCHS_VBE
+static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
+{
+ VGACommonState *s = opaque;
+ uint32_t val;
+ val = s->vbe_index;
+ return val;
+}
+
+static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
+{
+ VGACommonState *s = opaque;
+ uint32_t val;
+
+ if (s->vbe_index < VBE_DISPI_INDEX_NB) {
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
+ switch(s->vbe_index) {
+ /* XXX: do not hardcode ? */
+ case VBE_DISPI_INDEX_XRES:
+ val = VBE_DISPI_MAX_XRES;
+ break;
+ case VBE_DISPI_INDEX_YRES:
+ val = VBE_DISPI_MAX_YRES;
+ break;
+ case VBE_DISPI_INDEX_BPP:
+ val = VBE_DISPI_MAX_BPP;
+ break;
+ default:
+ val = s->vbe_regs[s->vbe_index];
+ break;
+ }
+ } else {
+ val = s->vbe_regs[s->vbe_index];
+ }
+ } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) {
+ val = s->vram_size / (64 * 1024);
+ } else {
+ val = 0;
+ }
+#ifdef DEBUG_BOCHS_VBE
+ printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val);
+#endif
+ return val;
+}
+
+static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGACommonState *s = opaque;
+ s->vbe_index = val;
+}
+
+static void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+ VGACommonState *s = opaque;
+
+ if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
+#ifdef DEBUG_BOCHS_VBE
+ printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val);
+#endif
+ switch(s->vbe_index) {
+ case VBE_DISPI_INDEX_ID:
+ if (val == VBE_DISPI_ID0 ||
+ val == VBE_DISPI_ID1 ||
+ val == VBE_DISPI_ID2 ||
+ val == VBE_DISPI_ID3 ||
+ val == VBE_DISPI_ID4) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_XRES:
+ if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_YRES:
+ if (val <= VBE_DISPI_MAX_YRES) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_BPP:
+ if (val == 0)
+ val = 8;
+ if (val == 4 || val == 8 || val == 15 ||
+ val == 16 || val == 24 || val == 32) {
+ s->vbe_regs[s->vbe_index] = val;
+ }
+ break;
+ case VBE_DISPI_INDEX_BANK:
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
+ val &= (s->vbe_bank_mask >> 2);
+ } else {
+ val &= s->vbe_bank_mask;
+ }
+ s->vbe_regs[s->vbe_index] = val;
+ s->bank_offset = (val << 16);
+ break;
+ case VBE_DISPI_INDEX_ENABLE:
+ if ((val & VBE_DISPI_ENABLED) &&
+ !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
+ int h, shift_control;
+
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
+ s->vbe_regs[VBE_DISPI_INDEX_XRES];
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
+ s->vbe_regs[VBE_DISPI_INDEX_YRES];
+ s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
+ s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
+
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+ s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
+ else
+ s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] *
+ ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+ s->vbe_start_addr = 0;
+
+ /* clear the screen (should be done in BIOS) */
+ if (!(val & VBE_DISPI_NOCLEARMEM)) {
+ memset(s->vram_ptr, 0,
+ s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
+ }
+
+ /* we initialize the VGA graphic mode (should be done
+ in BIOS) */
+ s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
+ s->cr[0x17] |= 3; /* no CGA modes */
+ s->cr[0x13] = s->vbe_line_offset >> 3;
+ /* width */
+ s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
+ /* height (only meaningful if < 1024) */
+ h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
+ s->cr[0x12] = h;
+ s->cr[0x07] = (s->cr[0x07] & ~0x42) |
+ ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
+ /* line compare to 1023 */
+ s->cr[0x18] = 0xff;
+ s->cr[0x07] |= 0x10;
+ s->cr[0x09] |= 0x40;
+
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
+ shift_control = 0;
+ s->sr[0x01] &= ~8; /* no double line */
+ } else {
+ shift_control = 2;
+ s->sr[4] |= 0x08; /* set chain 4 mode */
+ s->sr[2] |= 0x0f; /* activate all planes */
+ }
+ s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
+ s->cr[0x09] &= ~0x9f; /* no double scan */
+ } else {
+ /* XXX: the bios should do that */
+ s->bank_offset = 0;
+ }
+ s->dac_8bit = (val & VBE_DISPI_8BIT_DAC) > 0;
+ s->vbe_regs[s->vbe_index] = val;
+ break;
+ case VBE_DISPI_INDEX_VIRT_WIDTH:
+ {
+ int w, h, line_offset;
+
+ if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
+ return;
+ w = val;
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+ line_offset = w >> 1;
+ else
+ line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+ h = s->vram_size / line_offset;
+ /* XXX: support weird bochs semantics ? */
+ if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
+ return;
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
+ s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
+ s->vbe_line_offset = line_offset;
+ }
+ break;
+ case VBE_DISPI_INDEX_X_OFFSET:
+ case VBE_DISPI_INDEX_Y_OFFSET:
+ {
+ int x;
+ s->vbe_regs[s->vbe_index] = val;
+ s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
+ x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
+ if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+ s->vbe_start_addr += x >> 1;
+ else
+ s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+ s->vbe_start_addr >>= 2;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+#endif
+
+/* called for accesses between 0xa0000 and 0xc0000 */
+uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ VGACommonState *s = opaque;
+ int memory_map_mode, plane;
+ uint32_t ret;
+
+ /* convert to VGA memory offset */
+ memory_map_mode = (s->gr[6] >> 2) & 3;
+ addr &= 0x1ffff;
+ switch(memory_map_mode) {
+ case 0:
+ break;
+ case 1:
+ if (addr >= 0x10000)
+ return 0xff;
+ addr += s->bank_offset;
+ break;
+ case 2:
+ addr -= 0x10000;
+ if (addr >= 0x8000)
+ return 0xff;
+ break;
+ default:
+ case 3:
+ addr -= 0x18000;
+ if (addr >= 0x8000)
+ return 0xff;
+ break;
+ }
+
+ if (s->sr[4] & 0x08) {
+ /* chain 4 mode : simplest access */
+ ret = s->vram_ptr[addr];
+ } else if (s->gr[5] & 0x10) {
+ /* odd/even mode (aka text mode mapping) */
+ plane = (s->gr[4] & 2) | (addr & 1);
+ ret = s->vram_ptr[((addr & ~1) << 1) | plane];
+ } else {
+ /* standard VGA latched access */
+ s->latch = ((uint32_t *)s->vram_ptr)[addr];
+
+ if (!(s->gr[5] & 0x08)) {
+ /* read mode 0 */
+ plane = s->gr[4];
+ ret = GET_PLANE(s->latch, plane);
+ } else {
+ /* read mode 1 */
+ ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
+ ret |= ret >> 16;
+ ret |= ret >> 8;
+ ret = (~ret) & 0xff;
+ }
+ }
+ return ret;
+}
+
+static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = vga_mem_readb(opaque, addr);
+ v |= vga_mem_readb(opaque, addr + 1) << 8;
+ return v;
+}
+
+static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t v;
+ v = vga_mem_readb(opaque, addr);
+ v |= vga_mem_readb(opaque, addr + 1) << 8;
+ v |= vga_mem_readb(opaque, addr + 2) << 16;
+ v |= vga_mem_readb(opaque, addr + 3) << 24;
+ return v;
+}
+
+/* called for accesses between 0xa0000 and 0xc0000 */
+void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ VGACommonState *s = opaque;
+ int memory_map_mode, plane, write_mode, b, func_select, mask;
+ uint32_t write_mask, bit_mask, set_mask;
+
+#ifdef DEBUG_VGA_MEM
+ printf("vga: [0x" TARGET_FMT_plx "] = 0x%02x\n", addr, val);
+#endif
+ /* convert to VGA memory offset */
+ memory_map_mode = (s->gr[6] >> 2) & 3;
+ addr &= 0x1ffff;
+ switch(memory_map_mode) {
+ case 0:
+ break;
+ case 1:
+ if (addr >= 0x10000)
+ return;
+ addr += s->bank_offset;
+ break;
+ case 2:
+ addr -= 0x10000;
+ if (addr >= 0x8000)
+ return;
+ break;
+ default:
+ case 3:
+ addr -= 0x18000;
+ if (addr >= 0x8000)
+ return;
+ break;
+ }
+
+ if (s->sr[4] & 0x08) {
+ /* chain 4 mode : simplest access */
+ plane = addr & 3;
+ mask = (1 << plane);
+ if (s->sr[2] & mask) {
+ s->vram_ptr[addr] = val;
+#ifdef DEBUG_VGA_MEM
+ printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr);
+#endif
+ s->plane_updated |= mask; /* only used to detect font change */
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+ }
+ } else if (s->gr[5] & 0x10) {
+ /* odd/even mode (aka text mode mapping) */
+ plane = (s->gr[4] & 2) | (addr & 1);
+ mask = (1 << plane);
+ if (s->sr[2] & mask) {
+ addr = ((addr & ~1) << 1) | plane;
+ s->vram_ptr[addr] = val;
+#ifdef DEBUG_VGA_MEM
+ printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr);
+#endif
+ s->plane_updated |= mask; /* only used to detect font change */
+ cpu_physical_memory_set_dirty(s->vram_offset + addr);
+ }
+ } else {
+ /* standard VGA latched access */
+ write_mode = s->gr[5] & 3;
+ switch(write_mode) {
+ default:
+ case 0:
+ /* rotate */
+ b = s->gr[3] & 7;
+ val = ((val >> b) | (val << (8 - b))) & 0xff;
+ val |= val << 8;
+ val |= val << 16;
+
+ /* apply set/reset mask */
+ set_mask = mask16[s->gr[1]];
+ val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
+ bit_mask = s->gr[8];
+ break;
+ case 1:
+ val = s->latch;
+ goto do_write;
+ case 2:
+ val = mask16[val & 0x0f];
+ bit_mask = s->gr[8];
+ break;
+ case 3:
+ /* rotate */
+ b = s->gr[3] & 7;
+ val = (val >> b) | (val << (8 - b));
+
+ bit_mask = s->gr[8] & val;
+ val = mask16[s->gr[0]];
+ break;
+ }
+
+ /* apply logical operation */
+ func_select = s->gr[3] >> 3;
+ switch(func_select) {
+ case 0:
+ default:
+ /* nothing to do */
+ break;
+ case 1:
+ /* and */
+ val &= s->latch;
+ break;
+ case 2:
+ /* or */
+ val |= s->latch;
+ break;
+ case 3:
+ /* xor */
+ val ^= s->latch;
+ break;
+ }
+
+ /* apply bit mask */
+ bit_mask |= bit_mask << 8;
+ bit_mask |= bit_mask << 16;
+ val = (val & bit_mask) | (s->latch & ~bit_mask);
+
+ do_write:
+ /* mask data according to sr[2] */
+ mask = s->sr[2];
+ s->plane_updated |= mask; /* only used to detect font change */
+ write_mask = mask16[mask];
+ ((uint32_t *)s->vram_ptr)[addr] =
+ (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
+ (val & write_mask);
+#ifdef DEBUG_VGA_MEM
+ printf("vga: latch: [0x" TARGET_FMT_plx "] mask=0x%08x val=0x%08x\n",
+ addr * 4, write_mask, val);
+#endif
+ cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2));
+ }
+}
+
+static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ vga_mem_writeb(opaque, addr, val & 0xff);
+ vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+}
+
+static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+ vga_mem_writeb(opaque, addr, val & 0xff);
+ vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+}
+
+typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol);
+typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol, int dup9);
+typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width);
+
+#define DEPTH 8
+#include "vga_template.h"
+
+#define DEPTH 15
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 15
+#include "vga_template.h"
+
+#define DEPTH 16
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 16
+#include "vga_template.h"
+
+#define DEPTH 32
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 32
+#include "vga_template.h"
+
+static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel8(r, g, b);
+ col |= col << 8;
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel15(r, g, b);
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel15bgr_dup(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ unsigned int col;
+ col = rgb_to_pixel15bgr(r, g, b);
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel16(r, g, b);
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel16bgr_dup(unsigned int r, unsigned int g,
+ unsigned int b)
+{
+ unsigned int col;
+ col = rgb_to_pixel16bgr(r, g, b);
+ col |= col << 16;
+ return col;
+}
+
+static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel32(r, g, b);
+ return col;
+}
+
+static unsigned int rgb_to_pixel32bgr_dup(unsigned int r, unsigned int g, unsigned b)
+{
+ unsigned int col;
+ col = rgb_to_pixel32bgr(r, g, b);
+ return col;
+}
+
+/* return true if the palette was modified */
+static int update_palette16(VGACommonState *s)
+{
+ int full_update, i;
+ uint32_t v, col, *palette;
+
+ full_update = 0;
+ palette = s->last_palette;
+ for(i = 0; i < 16; i++) {
+ v = s->ar[i];
+ if (s->ar[0x10] & 0x80)
+ v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
+ else
+ v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
+ v = v * 3;
+ col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
+ c6_to_8(s->palette[v + 1]),
+ c6_to_8(s->palette[v + 2]));
+ if (col != palette[i]) {
+ full_update = 1;
+ palette[i] = col;
+ }
+ }
+ return full_update;
+}
+
+/* return true if the palette was modified */
+static int update_palette256(VGACommonState *s)
+{
+ int full_update, i;
+ uint32_t v, col, *palette;
+
+ full_update = 0;
+ palette = s->last_palette;
+ v = 0;
+ for(i = 0; i < 256; i++) {
+ if (s->dac_8bit) {
+ col = s->rgb_to_pixel(s->palette[v],
+ s->palette[v + 1],
+ s->palette[v + 2]);
+ } else {
+ col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
+ c6_to_8(s->palette[v + 1]),
+ c6_to_8(s->palette[v + 2]));
+ }
+ if (col != palette[i]) {
+ full_update = 1;
+ palette[i] = col;
+ }
+ v += 3;
+ }
+ return full_update;
+}
+
+static void vga_get_offsets(VGACommonState *s,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr,
+ uint32_t *pline_compare)
+{
+ uint32_t start_addr, line_offset, line_compare;
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ line_offset = s->vbe_line_offset;
+ start_addr = s->vbe_start_addr;
+ line_compare = 65535;
+ } else
+#endif
+ {
+ /* compute line_offset in bytes */
+ line_offset = s->cr[0x13];
+ line_offset <<= 3;
+
+ /* starting address */
+ start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
+
+ /* line compare */
+ line_compare = s->cr[0x18] |
+ ((s->cr[0x07] & 0x10) << 4) |
+ ((s->cr[0x09] & 0x40) << 3);
+ }
+ *pline_offset = line_offset;
+ *pstart_addr = start_addr;
+ *pline_compare = line_compare;
+}
+
+/* update start_addr and line_offset. Return TRUE if modified */
+static int update_basic_params(VGACommonState *s)
+{
+ int full_update;
+ uint32_t start_addr, line_offset, line_compare;
+
+ full_update = 0;
+
+ s->get_offsets(s, &line_offset, &start_addr, &line_compare);
+
+ if (line_offset != s->line_offset ||
+ start_addr != s->start_addr ||
+ line_compare != s->line_compare) {
+ s->line_offset = line_offset;
+ s->start_addr = start_addr;
+ s->line_compare = line_compare;
+ full_update = 1;
+ }
+ return full_update;
+}
+
+#define NB_DEPTHS 7
+
+static inline int get_depth_index(DisplayState *s)
+{
+ switch(ds_get_bits_per_pixel(s)) {
+ default:
+ case 8:
+ return 0;
+ case 15:
+ return 1;
+ case 16:
+ return 2;
+ case 32:
+ if (is_surface_bgr(s->surface))
+ return 4;
+ else
+ return 3;
+ }
+}
+
+static vga_draw_glyph8_func * const vga_draw_glyph8_table[NB_DEPTHS] = {
+ vga_draw_glyph8_8,
+ vga_draw_glyph8_16,
+ vga_draw_glyph8_16,
+ vga_draw_glyph8_32,
+ vga_draw_glyph8_32,
+ vga_draw_glyph8_16,
+ vga_draw_glyph8_16,
+};
+
+static vga_draw_glyph8_func * const vga_draw_glyph16_table[NB_DEPTHS] = {
+ vga_draw_glyph16_8,
+ vga_draw_glyph16_16,
+ vga_draw_glyph16_16,
+ vga_draw_glyph16_32,
+ vga_draw_glyph16_32,
+ vga_draw_glyph16_16,
+ vga_draw_glyph16_16,
+};
+
+static vga_draw_glyph9_func * const vga_draw_glyph9_table[NB_DEPTHS] = {
+ vga_draw_glyph9_8,
+ vga_draw_glyph9_16,
+ vga_draw_glyph9_16,
+ vga_draw_glyph9_32,
+ vga_draw_glyph9_32,
+ vga_draw_glyph9_16,
+ vga_draw_glyph9_16,
+};
+
+static const uint8_t cursor_glyph[32 * 4] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight,
+ int *pcwidth, int *pcheight)
+{
+ int width, cwidth, height, cheight;
+
+ /* total width & height */
+ cheight = (s->cr[9] & 0x1f) + 1;
+ cwidth = 8;
+ if (!(s->sr[1] & 0x01))
+ cwidth = 9;
+ if (s->sr[1] & 0x08)
+ cwidth = 16; /* NOTE: no 18 pixel wide */
+ width = (s->cr[0x01] + 1);
+ if (s->cr[0x06] == 100) {
+ /* ugly hack for CGA 160x100x16 - explain me the logic */
+ height = 100;
+ } else {
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1) / cheight;
+ }
+
+ *pwidth = width;
+ *pheight = height;
+ *pcwidth = cwidth;
+ *pcheight = cheight;
+}
+
+typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, unsigned int g, unsigned b);
+
+static rgb_to_pixel_dup_func * const rgb_to_pixel_dup_table[NB_DEPTHS] = {
+ rgb_to_pixel8_dup,
+ rgb_to_pixel15_dup,
+ rgb_to_pixel16_dup,
+ rgb_to_pixel32_dup,
+ rgb_to_pixel32bgr_dup,
+ rgb_to_pixel15bgr_dup,
+ rgb_to_pixel16bgr_dup,
+};
+
+/*
+ * Text mode update
+ * Missing:
+ * - double scan
+ * - double width
+ * - underline
+ * - flashing
+ */
+static void vga_draw_text(VGACommonState *s, int full_update)
+{
+ int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
+ int cx_min, cx_max, linesize, x_incr, line, line1;
+ uint32_t offset, fgcol, bgcol, v, cursor_offset;
+ uint8_t *d1, *d, *src, *dest, *cursor_ptr;
+ const uint8_t *font_ptr, *font_base[2];
+ int dup9, line_offset, depth_index;
+ uint32_t *palette;
+ uint32_t *ch_attr_ptr;
+ vga_draw_glyph8_func *vga_draw_glyph8;
+ vga_draw_glyph9_func *vga_draw_glyph9;
+
+ vga_dirty_log_stop(s);
+
+ /* compute font data address (in plane 2) */
+ v = s->sr[3];
+ offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
+ if (offset != s->font_offsets[0]) {
+ s->font_offsets[0] = offset;
+ full_update = 1;
+ }
+ font_base[0] = s->vram_ptr + offset;
+
+ offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
+ font_base[1] = s->vram_ptr + offset;
+ if (offset != s->font_offsets[1]) {
+ s->font_offsets[1] = offset;
+ full_update = 1;
+ }
+ if (s->plane_updated & (1 << 2)) {
+ /* if the plane 2 was modified since the last display, it
+ indicates the font may have been modified */
+ s->plane_updated = 0;
+ full_update = 1;
+ }
+ full_update |= update_basic_params(s);
+
+ line_offset = s->line_offset;
+
+ vga_get_text_resolution(s, &width, &height, &cw, &cheight);
+ if ((height * width) > CH_ATTR_SIZE) {
+ /* better than nothing: exit if transient size is too big */
+ return;
+ }
+
+ if (width != s->last_width || height != s->last_height ||
+ cw != s->last_cw || cheight != s->last_ch || s->last_depth) {
+ s->last_scr_width = width * cw;
+ s->last_scr_height = height * cheight;
+ qemu_console_resize(s->ds, s->last_scr_width, s->last_scr_height);
+ s->last_depth = 0;
+ s->last_width = width;
+ s->last_height = height;
+ s->last_ch = cheight;
+ s->last_cw = cw;
+ full_update = 1;
+ }
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
+ full_update |= update_palette16(s);
+ palette = s->last_palette;
+ x_incr = cw * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
+
+ cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
+ if (cursor_offset != s->cursor_offset ||
+ s->cr[0xa] != s->cursor_start ||
+ s->cr[0xb] != s->cursor_end) {
+ /* if the cursor position changed, we update the old and new
+ chars */
+ if (s->cursor_offset < CH_ATTR_SIZE)
+ s->last_ch_attr[s->cursor_offset] = -1;
+ if (cursor_offset < CH_ATTR_SIZE)
+ s->last_ch_attr[cursor_offset] = -1;
+ s->cursor_offset = cursor_offset;
+ s->cursor_start = s->cr[0xa];
+ s->cursor_end = s->cr[0xb];
+ }
+ cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
+
+ depth_index = get_depth_index(s->ds);
+ if (cw == 16)
+ vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
+ else
+ vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
+ vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
+
+ dest = ds_get_data(s->ds);
+ linesize = ds_get_linesize(s->ds);
+ ch_attr_ptr = s->last_ch_attr;
+ line = 0;
+ offset = s->start_addr * 4;
+ for(cy = 0; cy < height; cy++) {
+ d1 = dest;
+ src = s->vram_ptr + offset;
+ cx_min = width;
+ cx_max = -1;
+ for(cx = 0; cx < width; cx++) {
+ ch_attr = *(uint16_t *)src;
+ if (full_update || ch_attr != *ch_attr_ptr) {
+ if (cx < cx_min)
+ cx_min = cx;
+ if (cx > cx_max)
+ cx_max = cx;
+ *ch_attr_ptr = ch_attr;
+#ifdef HOST_WORDS_BIGENDIAN
+ ch = ch_attr >> 8;
+ cattr = ch_attr & 0xff;
+#else
+ ch = ch_attr & 0xff;
+ cattr = ch_attr >> 8;
+#endif
+ font_ptr = font_base[(cattr >> 3) & 1];
+ font_ptr += 32 * 4 * ch;
+ bgcol = palette[cattr >> 4];
+ fgcol = palette[cattr & 0x0f];
+ if (cw != 9) {
+ vga_draw_glyph8(d1, linesize,
+ font_ptr, cheight, fgcol, bgcol);
+ } else {
+ dup9 = 0;
+ if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
+ dup9 = 1;
+ vga_draw_glyph9(d1, linesize,
+ font_ptr, cheight, fgcol, bgcol, dup9);
+ }
+ if (src == cursor_ptr &&
+ !(s->cr[0x0a] & 0x20)) {
+ int line_start, line_last, h;
+ /* draw the cursor */
+ line_start = s->cr[0x0a] & 0x1f;
+ line_last = s->cr[0x0b] & 0x1f;
+ /* XXX: check that */
+ if (line_last > cheight - 1)
+ line_last = cheight - 1;
+ if (line_last >= line_start && line_start < cheight) {
+ h = line_last - line_start + 1;
+ d = d1 + linesize * line_start;
+ if (cw != 9) {
+ vga_draw_glyph8(d, linesize,
+ cursor_glyph, h, fgcol, bgcol);
+ } else {
+ vga_draw_glyph9(d, linesize,
+ cursor_glyph, h, fgcol, bgcol, 1);
+ }
+ }
+ }
+ }
+ d1 += x_incr;
+ src += 4;
+ ch_attr_ptr++;
+ }
+ if (cx_max != -1) {
+ dpy_update(s->ds, cx_min * cw, cy * cheight,
+ (cx_max - cx_min + 1) * cw, cheight);
+ }
+ dest += linesize * cheight;
+ line1 = line + cheight;
+ offset += line_offset;
+ if (line < s->line_compare && line1 >= s->line_compare) {
+ offset = 0;
+ }
+ line = line1;
+ }
+}
+
+enum {
+ VGA_DRAW_LINE2,
+ VGA_DRAW_LINE2D2,
+ VGA_DRAW_LINE4,
+ VGA_DRAW_LINE4D2,
+ VGA_DRAW_LINE8D2,
+ VGA_DRAW_LINE8,
+ VGA_DRAW_LINE15,
+ VGA_DRAW_LINE16,
+ VGA_DRAW_LINE24,
+ VGA_DRAW_LINE32,
+ VGA_DRAW_LINE_NB,
+};
+
+static vga_draw_line_func * const vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = {
+ vga_draw_line2_8,
+ vga_draw_line2_16,
+ vga_draw_line2_16,
+ vga_draw_line2_32,
+ vga_draw_line2_32,
+ vga_draw_line2_16,
+ vga_draw_line2_16,
+
+ vga_draw_line2d2_8,
+ vga_draw_line2d2_16,
+ vga_draw_line2d2_16,
+ vga_draw_line2d2_32,
+ vga_draw_line2d2_32,
+ vga_draw_line2d2_16,
+ vga_draw_line2d2_16,
+
+ vga_draw_line4_8,
+ vga_draw_line4_16,
+ vga_draw_line4_16,
+ vga_draw_line4_32,
+ vga_draw_line4_32,
+ vga_draw_line4_16,
+ vga_draw_line4_16,
+
+ vga_draw_line4d2_8,
+ vga_draw_line4d2_16,
+ vga_draw_line4d2_16,
+ vga_draw_line4d2_32,
+ vga_draw_line4d2_32,
+ vga_draw_line4d2_16,
+ vga_draw_line4d2_16,
+
+ vga_draw_line8d2_8,
+ vga_draw_line8d2_16,
+ vga_draw_line8d2_16,
+ vga_draw_line8d2_32,
+ vga_draw_line8d2_32,
+ vga_draw_line8d2_16,
+ vga_draw_line8d2_16,
+
+ vga_draw_line8_8,
+ vga_draw_line8_16,
+ vga_draw_line8_16,
+ vga_draw_line8_32,
+ vga_draw_line8_32,
+ vga_draw_line8_16,
+ vga_draw_line8_16,
+
+ vga_draw_line15_8,
+ vga_draw_line15_15,
+ vga_draw_line15_16,
+ vga_draw_line15_32,
+ vga_draw_line15_32bgr,
+ vga_draw_line15_15bgr,
+ vga_draw_line15_16bgr,
+
+ vga_draw_line16_8,
+ vga_draw_line16_15,
+ vga_draw_line16_16,
+ vga_draw_line16_32,
+ vga_draw_line16_32bgr,
+ vga_draw_line16_15bgr,
+ vga_draw_line16_16bgr,
+
+ vga_draw_line24_8,
+ vga_draw_line24_15,
+ vga_draw_line24_16,
+ vga_draw_line24_32,
+ vga_draw_line24_32bgr,
+ vga_draw_line24_15bgr,
+ vga_draw_line24_16bgr,
+
+ vga_draw_line32_8,
+ vga_draw_line32_15,
+ vga_draw_line32_16,
+ vga_draw_line32_32,
+ vga_draw_line32_32bgr,
+ vga_draw_line32_15bgr,
+ vga_draw_line32_16bgr,
+};
+
+static int vga_get_bpp(VGACommonState *s)
+{
+ int ret;
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
+ } else
+#endif
+ {
+ ret = 0;
+ }
+ return ret;
+}
+
+static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
+{
+ int width, height;
+
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+ width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
+ height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
+ } else
+#endif
+ {
+ width = (s->cr[0x01] + 1) * 8;
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1);
+ }
+ *pwidth = width;
+ *pheight = height;
+}
+
+void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2)
+{
+ int y;
+ if (y1 >= VGA_MAX_HEIGHT)
+ return;
+ if (y2 >= VGA_MAX_HEIGHT)
+ y2 = VGA_MAX_HEIGHT;
+ for(y = y1; y < y2; y++) {
+ s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f);
+ }
+}
+
+static void vga_sync_dirty_bitmap(VGACommonState *s)
+{
+ if (s->map_addr)
+ cpu_physical_sync_dirty_bitmap(s->map_addr, s->map_end);
+
+ if (s->lfb_vram_mapped) {
+ cpu_physical_sync_dirty_bitmap(isa_mem_base + 0xa0000, 0xa8000);
+ cpu_physical_sync_dirty_bitmap(isa_mem_base + 0xa8000, 0xb0000);
+ }
+
+#ifdef CONFIG_BOCHS_VBE
+ if (s->vbe_mapped) {
+ cpu_physical_sync_dirty_bitmap(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
+ VBE_DISPI_LFB_PHYSICAL_ADDRESS + s->vram_size);
+ }
+#endif
+
+ vga_dirty_log_start(s);
+}
+
+static int s1, s2, s3;
+
+static void mark_dirty(target_phys_addr_t start, target_phys_addr_t len)
+{
+ target_phys_addr_t end = start + len;
+
+ while (start < end) {
+ cpu_physical_memory_set_dirty(cpu_get_physical_page_desc(start));
+ start += TARGET_PAGE_SIZE;
+ }
+}
+
+void vga_dirty_log_start(VGACommonState *s)
+{
+ if (kvm_enabled() && s->map_addr)
+ if (!s1) {
+ kvm_log_start(s->map_addr, s->map_end - s->map_addr);
+ mark_dirty(s->map_addr, s->map_end - s->map_addr);
+ s1 = 1;
+ }
+ if (kvm_enabled() && s->lfb_vram_mapped) {
+ if (!s2) {
+ kvm_log_start(isa_mem_base + 0xa0000, 0x8000);
+ kvm_log_start(isa_mem_base + 0xa8000, 0x8000);
+ mark_dirty(isa_mem_base + 0xa0000, 0x10000);
+ }
+ s2 = 1;
+ }
+
+#ifdef CONFIG_BOCHS_VBE
+ if (kvm_enabled() && s->vbe_mapped) {
+ if (!s3) {
+ kvm_log_start(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size);
+ }
+ s3 = 1;
+ }
+#endif
+}
+
+void vga_dirty_log_stop(VGACommonState *s)
+{
+ if (kvm_enabled() && s->map_addr && s1)
+ kvm_log_stop(s->map_addr, s->map_end - s->map_addr);
+
+ if (kvm_enabled() && s->lfb_vram_mapped && s1) {
+ kvm_log_stop(isa_mem_base + 0xa0000, 0x8000);
+ kvm_log_stop(isa_mem_base + 0xa8000, 0x8000);
+ }
+
+#ifdef CONFIG_BOCHS_VBE
+ if (kvm_enabled() && s->vbe_mapped && s3) {
+ kvm_log_stop(VBE_DISPI_LFB_PHYSICAL_ADDRESS, s->vram_size);
+ }
+#endif
+
+ s1 = s2 = s3 = 0;
+}
+
+void vga_dirty_log_restart(VGACommonState *s)
+{
+ vga_dirty_log_stop(s);
+ vga_dirty_log_start(s);
+}
+
+/*
+ * graphic modes
+ */
+static void vga_draw_graphic(VGACommonState *s, int full_update)
+{
+ int y1, y, update, linesize, y_start, double_scan, mask, depth;
+ int width, height, shift_control, line_offset, bwidth, bits;
+ ram_addr_t page0, page1, page_min, page_max;
+ int disp_width, multi_scan, multi_run;
+ uint8_t *d;
+ uint32_t v, addr1, addr;
+ vga_draw_line_func *vga_draw_line;
+
+ full_update |= update_basic_params(s);
+
+ if (!full_update)
+ vga_sync_dirty_bitmap(s);
+
+ s->get_resolution(s, &width, &height);
+ disp_width = width;
+
+ shift_control = (s->gr[0x05] >> 5) & 3;
+ double_scan = (s->cr[0x09] >> 7);
+ if (shift_control != 1) {
+ multi_scan = (((s->cr[0x09] & 0x1f) + 1) << double_scan) - 1;
+ } else {
+ /* in CGA modes, multi_scan is ignored */
+ /* XXX: is it correct ? */
+ multi_scan = double_scan;
+ }
+ multi_run = multi_scan;
+ if (shift_control != s->shift_control ||
+ double_scan != s->double_scan) {
+ full_update = 1;
+ s->shift_control = shift_control;
+ s->double_scan = double_scan;
+ }
+
+ if (shift_control == 0) {
+ if (s->sr[0x01] & 8) {
+ disp_width <<= 1;
+ }
+ } else if (shift_control == 1) {
+ if (s->sr[0x01] & 8) {
+ disp_width <<= 1;
+ }
+ }
+
+ depth = s->get_bpp(s);
+ if (s->line_offset != s->last_line_offset ||
+ disp_width != s->last_width ||
+ height != s->last_height ||
+ s->last_depth != depth) {
+#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ if (depth == 16 || depth == 32) {
+#else
+ if (depth == 32) {
+#endif
+ qemu_free_displaysurface(s->ds);
+ s->ds->surface = qemu_create_displaysurface_from(disp_width, height, depth,
+ s->line_offset,
+ s->vram_ptr + (s->start_addr * 4));
+#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+ s->ds->surface->pf = qemu_different_endianness_pixelformat(depth);
+#endif
+ dpy_resize(s->ds);
+ } else {
+ qemu_console_resize(s->ds, disp_width, height);
+ }
+ s->last_scr_width = disp_width;
+ s->last_scr_height = height;
+ s->last_width = disp_width;
+ s->last_height = height;
+ s->last_line_offset = s->line_offset;
+ s->last_depth = depth;
+ full_update = 1;
+ } else if (is_buffer_shared(s->ds->surface) &&
+ (full_update || s->ds->surface->data != s->vram_ptr + (s->start_addr * 4))) {
+ s->ds->surface->data = s->vram_ptr + (s->start_addr * 4);
+ dpy_setdata(s->ds);
+ }
+
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
+
+ if (shift_control == 0) {
+ full_update |= update_palette16(s);
+ if (s->sr[0x01] & 8) {
+ v = VGA_DRAW_LINE4D2;
+ } else {
+ v = VGA_DRAW_LINE4;
+ }
+ bits = 4;
+ } else if (shift_control == 1) {
+ full_update |= update_palette16(s);
+ if (s->sr[0x01] & 8) {
+ v = VGA_DRAW_LINE2D2;
+ } else {
+ v = VGA_DRAW_LINE2;
+ }
+ bits = 4;
+ } else {
+ switch(s->get_bpp(s)) {
+ default:
+ case 0:
+ full_update |= update_palette256(s);
+ v = VGA_DRAW_LINE8D2;
+ bits = 4;
+ break;
+ case 8:
+ full_update |= update_palette256(s);
+ v = VGA_DRAW_LINE8;
+ bits = 8;
+ break;
+ case 15:
+ v = VGA_DRAW_LINE15;
+ bits = 16;
+ break;
+ case 16:
+ v = VGA_DRAW_LINE16;
+ bits = 16;
+ break;
+ case 24:
+ v = VGA_DRAW_LINE24;
+ bits = 24;
+ break;
+ case 32:
+ v = VGA_DRAW_LINE32;
+ bits = 32;
+ break;
+ }
+ }
+ vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + get_depth_index(s->ds)];
+
+ if (!is_buffer_shared(s->ds->surface) && s->cursor_invalidate)
+ s->cursor_invalidate(s);
+
+ line_offset = s->line_offset;
+#if 0
+ printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
+ width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]);
+#endif
+ addr1 = (s->start_addr * 4);
+ bwidth = (width * bits + 7) / 8;
+ y_start = -1;
+ page_min = -1;
+ page_max = 0;
+ d = ds_get_data(s->ds);
+ linesize = ds_get_linesize(s->ds);
+ y1 = 0;
+ for(y = 0; y < height; y++) {
+ addr = addr1;
+ if (!(s->cr[0x17] & 1)) {
+ int shift;
+ /* CGA compatibility handling */
+ shift = 14 + ((s->cr[0x17] >> 6) & 1);
+ addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift);
+ }
+ if (!(s->cr[0x17] & 2)) {
+ addr = (addr & ~0x8000) | ((y1 & 2) << 14);
+ }
+ page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
+ page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
+ update = full_update |
+ cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
+ cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
+ if ((page1 - page0) > TARGET_PAGE_SIZE) {
+ /* if wide line, can use another page */
+ update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+ /* explicit invalidation for the hardware cursor */
+ update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
+ if (update) {
+ if (y_start < 0)
+ y_start = y;
+ if (page0 < page_min)
+ page_min = page0;
+ if (page1 > page_max)
+ page_max = page1;
+ if (!(is_buffer_shared(s->ds->surface))) {
+ vga_draw_line(s, d, s->vram_ptr + addr, width);
+ if (s->cursor_draw_line)
+ s->cursor_draw_line(s, d, y);
+ }
+ } else {
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(s->ds, 0, y_start,
+ disp_width, y - y_start);
+ y_start = -1;
+ }
+ }
+ if (!multi_run) {
+ mask = (s->cr[0x17] & 3) ^ 3;
+ if ((y1 & mask) == mask)
+ addr1 += line_offset;
+ y1++;
+ multi_run = multi_scan;
+ } else {
+ multi_run--;
+ }
+ /* line compare acts on the displayed lines */
+ if (y == s->line_compare)
+ addr1 = 0;
+ d += linesize;
+ }
+ if (y_start >= 0) {
+ /* flush to display */
+ dpy_update(s->ds, 0, y_start,
+ disp_width, y - y_start);
+ }
+ /* reset modified pages */
+ if (page_max >= page_min) {
+ cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
+ VGA_DIRTY_FLAG);
+ }
+ memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
+}
+
+static void vga_draw_blank(VGACommonState *s, int full_update)
+{
+ int i, w, val;
+ uint8_t *d;
+
+ if (!full_update)
+ return;
+ if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
+ return;
+ vga_dirty_log_stop(s);
+
+ s->rgb_to_pixel =
+ rgb_to_pixel_dup_table[get_depth_index(s->ds)];
+ if (ds_get_bits_per_pixel(s->ds) == 8)
+ val = s->rgb_to_pixel(0, 0, 0);
+ else
+ val = 0;
+ w = s->last_scr_width * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3);
+ d = ds_get_data(s->ds);
+ for(i = 0; i < s->last_scr_height; i++) {
+ memset(d, val, w);
+ d += ds_get_linesize(s->ds);
+ }
+ dpy_update(s->ds, 0, 0,
+ s->last_scr_width, s->last_scr_height);
+}
+
+#define GMODE_TEXT 0
+#define GMODE_GRAPH 1
+#define GMODE_BLANK 2
+
+static void vga_update_display(void *opaque)
+{
+ VGACommonState *s = opaque;
+ int full_update, graphic_mode;
+
+ if (ds_get_bits_per_pixel(s->ds) == 0) {
+ /* nothing to do */
+ } else {
+ full_update = 0;
+ if (!(s->ar_index & 0x20)) {
+ graphic_mode = GMODE_BLANK;
+ } else {
+ graphic_mode = s->gr[6] & 1;
+ }
+ if (graphic_mode != s->graphic_mode) {
+ s->graphic_mode = graphic_mode;
+ full_update = 1;
+ }
+ switch(graphic_mode) {
+ case GMODE_TEXT:
+ vga_draw_text(s, full_update);
+ break;
+ case GMODE_GRAPH:
+#ifdef TARGET_IA64
+ full_update = 1;
+#endif
+ vga_draw_graphic(s, full_update);
+ break;
+ case GMODE_BLANK:
+ default:
+ vga_draw_blank(s, full_update);
+ break;
+ }
+ }
+}
+
+/* force a full display refresh */
+static void vga_invalidate_display(void *opaque)
+{
+ VGACommonState *s = opaque;
+
+ s->last_width = -1;
+ s->last_height = -1;
+}
+
+void vga_common_reset(VGACommonState *s)
+{
+ s->lfb_addr = 0;
+ s->lfb_end = 0;
+ s->map_addr = 0;
+ s->map_end = 0;
+ s->lfb_vram_mapped = 0;
+ s->sr_index = 0;
+ memset(s->sr, '\0', sizeof(s->sr));
+ s->gr_index = 0;
+ memset(s->gr, '\0', sizeof(s->gr));
+ s->ar_index = 0;
+ memset(s->ar, '\0', sizeof(s->ar));
+ s->ar_flip_flop = 0;
+ s->cr_index = 0;
+ memset(s->cr, '\0', sizeof(s->cr));
+ s->msr = 0;
+ s->fcr = 0;
+ s->st00 = 0;
+ s->st01 = 0;
+ s->dac_state = 0;
+ s->dac_sub_index = 0;
+ s->dac_read_index = 0;
+ s->dac_write_index = 0;
+ memset(s->dac_cache, '\0', sizeof(s->dac_cache));
+ s->dac_8bit = 0;
+ memset(s->palette, '\0', sizeof(s->palette));
+ s->bank_offset = 0;
+#ifdef CONFIG_BOCHS_VBE
+ s->vbe_index = 0;
+ memset(s->vbe_regs, '\0', sizeof(s->vbe_regs));
+ s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5;
+ s->vbe_start_addr = 0;
+ s->vbe_line_offset = 0;
+ s->vbe_bank_mask = (s->vram_size >> 16) - 1;
+#endif
+ memset(s->font_offsets, '\0', sizeof(s->font_offsets));
+ s->graphic_mode = -1; /* force full update */
+ s->shift_control = 0;
+ s->double_scan = 0;
+ s->line_offset = 0;
+ s->line_compare = 0;
+ s->start_addr = 0;
+ s->plane_updated = 0;
+ s->last_cw = 0;
+ s->last_ch = 0;
+ s->last_width = 0;
+ s->last_height = 0;
+ s->last_scr_width = 0;
+ s->last_scr_height = 0;
+ s->cursor_start = 0;
+ s->cursor_end = 0;
+ s->cursor_offset = 0;
+ memset(s->invalidated_y_table, '\0', sizeof(s->invalidated_y_table));
+ memset(s->last_palette, '\0', sizeof(s->last_palette));
+ memset(s->last_ch_attr, '\0', sizeof(s->last_ch_attr));
+ switch (vga_retrace_method) {
+ case VGA_RETRACE_DUMB:
+ break;
+ case VGA_RETRACE_PRECISE:
+ memset(&s->retrace_info, 0, sizeof (s->retrace_info));
+ break;
+ }
+}
+
+static void vga_reset(void *opaque)
+{
+ VGACommonState *s = opaque;
+ vga_common_reset(s);
+}
+
+#define TEXTMODE_X(x) ((x) % width)
+#define TEXTMODE_Y(x) ((x) / width)
+#define VMEM2CHTYPE(v) ((v & 0xff0007ff) | \
+ ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1))
+/* relay text rendering to the display driver
+ * instead of doing a full vga_update_display() */
+static void vga_update_text(void *opaque, console_ch_t *chardata)
+{
+ VGACommonState *s = opaque;
+ int graphic_mode, i, cursor_offset, cursor_visible;
+ int cw, cheight, width, height, size, c_min, c_max;
+ uint32_t *src;
+ console_ch_t *dst, val;
+ char msg_buffer[80];
+ int full_update = 0;
+
+ if (!(s->ar_index & 0x20)) {
+ graphic_mode = GMODE_BLANK;
+ } else {
+ graphic_mode = s->gr[6] & 1;
+ }
+ if (graphic_mode != s->graphic_mode) {
+ s->graphic_mode = graphic_mode;
+ full_update = 1;
+ }
+ if (s->last_width == -1) {
+ s->last_width = 0;
+ full_update = 1;
+ }
+
+ switch (graphic_mode) {
+ case GMODE_TEXT:
+ /* TODO: update palette */
+ full_update |= update_basic_params(s);
+
+ /* total width & height */
+ cheight = (s->cr[9] & 0x1f) + 1;
+ cw = 8;
+ if (!(s->sr[1] & 0x01))
+ cw = 9;
+ if (s->sr[1] & 0x08)
+ cw = 16; /* NOTE: no 18 pixel wide */
+ width = (s->cr[0x01] + 1);
+ if (s->cr[0x06] == 100) {
+ /* ugly hack for CGA 160x100x16 - explain me the logic */
+ height = 100;
+ } else {
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1) / cheight;
+ }
+
+ size = (height * width);
+ if (size > CH_ATTR_SIZE) {
+ if (!full_update)
+ return;
+
+ snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Text mode",
+ width, height);
+ break;
+ }
+
+ if (width != s->last_width || height != s->last_height ||
+ cw != s->last_cw || cheight != s->last_ch) {
+ s->last_scr_width = width * cw;
+ s->last_scr_height = height * cheight;
+ s->ds->surface->width = width;
+ s->ds->surface->height = height;
+ dpy_resize(s->ds);
+ s->last_width = width;
+ s->last_height = height;
+ s->last_ch = cheight;
+ s->last_cw = cw;
+ full_update = 1;
+ }
+
+ /* Update "hardware" cursor */
+ cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
+ if (cursor_offset != s->cursor_offset ||
+ s->cr[0xa] != s->cursor_start ||
+ s->cr[0xb] != s->cursor_end || full_update) {
+ cursor_visible = !(s->cr[0xa] & 0x20);
+ if (cursor_visible && cursor_offset < size && cursor_offset >= 0)
+ dpy_cursor(s->ds,
+ TEXTMODE_X(cursor_offset),
+ TEXTMODE_Y(cursor_offset));
+ else
+ dpy_cursor(s->ds, -1, -1);
+ s->cursor_offset = cursor_offset;
+ s->cursor_start = s->cr[0xa];
+ s->cursor_end = s->cr[0xb];
+ }
+
+ src = (uint32_t *) s->vram_ptr + s->start_addr;
+ dst = chardata;
+
+ if (full_update) {
+ for (i = 0; i < size; src ++, dst ++, i ++)
+ console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src)));
+
+ dpy_update(s->ds, 0, 0, width, height);
+ } else {
+ c_max = 0;
+
+ for (i = 0; i < size; src ++, dst ++, i ++) {
+ console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src)));
+ if (*dst != val) {
+ *dst = val;
+ c_max = i;
+ break;
+ }
+ }
+ c_min = i;
+ for (; i < size; src ++, dst ++, i ++) {
+ console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src)));
+ if (*dst != val) {
+ *dst = val;
+ c_max = i;
+ }
+ }
+
+ if (c_min <= c_max) {
+ i = TEXTMODE_Y(c_min);
+ dpy_update(s->ds, 0, i, width, TEXTMODE_Y(c_max) - i + 1);
+ }
+ }
+
+ return;
+ case GMODE_GRAPH:
+ if (!full_update)
+ return;
+
+ s->get_resolution(s, &width, &height);
+ snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Graphic mode",
+ width, height);
+ break;
+ case GMODE_BLANK:
+ default:
+ if (!full_update)
+ return;
+
+ snprintf(msg_buffer, sizeof(msg_buffer), "VGA Blank mode");
+ break;
+ }
+
+ /* Display a message */
+ s->last_width = 60;
+ s->last_height = height = 3;
+ dpy_cursor(s->ds, -1, -1);
+ s->ds->surface->width = s->last_width;
+ s->ds->surface->height = height;
+ dpy_resize(s->ds);
+
+ for (dst = chardata, i = 0; i < s->last_width * height; i ++)
+ console_write_ch(dst ++, ' ');
+
+ size = strlen(msg_buffer);
+ width = (s->last_width - size) / 2;
+ dst = chardata + s->last_width + width;
+ for (i = 0; i < size; i ++)
+ console_write_ch(dst ++, 0x00200100 | msg_buffer[i]);
+
+ dpy_update(s->ds, 0, 0, s->last_width, height);
+}
+
+CPUReadMemoryFunc * const vga_mem_read[3] = {
+ vga_mem_readb,
+ vga_mem_readw,
+ vga_mem_readl,
+};
+
+CPUWriteMemoryFunc * const vga_mem_write[3] = {
+ vga_mem_writeb,
+ vga_mem_writew,
+ vga_mem_writel,
+};
+
+static int vga_common_post_load(void *opaque, int version_id)
+{
+ VGACommonState *s = opaque;
+
+ /* force refresh */
+ s->graphic_mode = -1;
+ return 0;
+}
+
+const VMStateDescription vmstate_vga_common = {
+ .name = "vga",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .post_load = vga_common_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(latch, VGACommonState),
+ VMSTATE_UINT8(sr_index, VGACommonState),
+ VMSTATE_PARTIAL_BUFFER(sr, VGACommonState, 8),
+ VMSTATE_UINT8(gr_index, VGACommonState),
+ VMSTATE_PARTIAL_BUFFER(gr, VGACommonState, 16),
+ VMSTATE_UINT8(ar_index, VGACommonState),
+ VMSTATE_BUFFER(ar, VGACommonState),
+ VMSTATE_INT32(ar_flip_flop, VGACommonState),
+ VMSTATE_UINT8(cr_index, VGACommonState),
+ VMSTATE_BUFFER(cr, VGACommonState),
+ VMSTATE_UINT8(msr, VGACommonState),
+ VMSTATE_UINT8(fcr, VGACommonState),
+ VMSTATE_UINT8(st00, VGACommonState),
+ VMSTATE_UINT8(st01, VGACommonState),
+
+ VMSTATE_UINT8(dac_state, VGACommonState),
+ VMSTATE_UINT8(dac_sub_index, VGACommonState),
+ VMSTATE_UINT8(dac_read_index, VGACommonState),
+ VMSTATE_UINT8(dac_write_index, VGACommonState),
+ VMSTATE_BUFFER(dac_cache, VGACommonState),
+ VMSTATE_BUFFER(palette, VGACommonState),
+
+ VMSTATE_INT32(bank_offset, VGACommonState),
+ VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState),
+#ifdef CONFIG_BOCHS_VBE
+ VMSTATE_UINT16(vbe_index, VGACommonState),
+ VMSTATE_UINT16_ARRAY(vbe_regs, VGACommonState, VBE_DISPI_INDEX_NB),
+ VMSTATE_UINT32(vbe_start_addr, VGACommonState),
+ VMSTATE_UINT32(vbe_line_offset, VGACommonState),
+ VMSTATE_UINT32(vbe_bank_mask, VGACommonState),
+#endif
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void vga_common_init(VGACommonState *s, int vga_ram_size)
+{
+ int i, j, v, b;
+
+ for(i = 0;i < 256; i++) {
+ v = 0;
+ for(j = 0; j < 8; j++) {
+ v |= ((i >> j) & 1) << (j * 4);
+ }
+ expand4[i] = v;
+
+ v = 0;
+ for(j = 0; j < 4; j++) {
+ v |= ((i >> (2 * j)) & 3) << (j * 4);
+ }
+ expand2[i] = v;
+ }
+ for(i = 0; i < 16; i++) {
+ v = 0;
+ for(j = 0; j < 4; j++) {
+ b = ((i >> j) & 1);
+ v |= b << (2 * j);
+ v |= b << (2 * j + 1);
+ }
+ expand4to8[i] = v;
+ }
+
+#ifdef CONFIG_BOCHS_VBE
+ s->is_vbe_vmstate = 1;
+#else
+ s->is_vbe_vmstate = 0;
+#endif
+ s->vram_offset = qemu_ram_alloc(NULL, "vga.vram", vga_ram_size);
+ s->vram_ptr = qemu_get_ram_ptr(s->vram_offset);
+ s->vram_size = vga_ram_size;
+ s->get_bpp = vga_get_bpp;
+ s->get_offsets = vga_get_offsets;
+ s->get_resolution = vga_get_resolution;
+ s->update = vga_update_display;
+ s->invalidate = vga_invalidate_display;
+ s->screen_dump = vga_screen_dump;
+ s->text_update = vga_update_text;
+ switch (vga_retrace_method) {
+ case VGA_RETRACE_DUMB:
+ s->retrace = vga_dumb_retrace;
+ s->update_retrace_info = vga_dumb_update_retrace_info;
+ break;
+
+ case VGA_RETRACE_PRECISE:
+ s->retrace = vga_precise_retrace;
+ s->update_retrace_info = vga_precise_update_retrace_info;
+ break;
+ }
+}
+
+/* used by both ISA and PCI */
+void vga_init(VGACommonState *s)
+{
+ int vga_io_memory;
+
+ qemu_register_reset(vga_reset, s);
+
+ register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
+
+ register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
+ register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
+ register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
+
+ register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
+
+ register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
+ register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
+ register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
+ s->bank_offset = 0;
+
+#ifdef CONFIG_BOCHS_VBE
+#if defined (TARGET_I386)
+ register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
+ register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
+
+ register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
+ register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
+#else
+ register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
+ register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
+
+ register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
+ register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
+#endif
+#endif /* CONFIG_BOCHS_VBE */
+
+ vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s,
+ DEVICE_LITTLE_ENDIAN);
+ cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
+ vga_io_memory);
+ qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000);
+}
+
+void vga_init_vbe(VGACommonState *s)
+{
+#ifdef CONFIG_BOCHS_VBE
+ /* XXX: use optimized standard vga accesses */
+ cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
+ VGA_RAM_SIZE, s->vram_offset);
+ s->vbe_mapped = 1;
+#endif
+}
+/********************************************************/
+/* vga screen dump */
+
+static void vga_save_dpy_update(DisplayState *ds,
+ int x, int y, int w, int h)
+{
+ if (screen_dump_filename) {
+ ppm_save(screen_dump_filename, ds->surface);
+ screen_dump_filename = NULL;
+ }
+}
+
+static void vga_save_dpy_resize(DisplayState *s)
+{
+}
+
+static void vga_save_dpy_refresh(DisplayState *s)
+{
+}
+
+int ppm_save(const char *filename, struct DisplaySurface *ds)
+{
+ FILE *f;
+ uint8_t *d, *d1;
+ uint32_t v;
+ int y, x;
+ uint8_t r, g, b;
+
+ f = fopen(filename, "wb");
+ if (!f)
+ return -1;
+ fprintf(f, "P6\n%d %d\n%d\n",
+ ds->width, ds->height, 255);
+ d1 = ds->data;
+ for(y = 0; y < ds->height; y++) {
+ d = d1;
+ for(x = 0; x < ds->width; x++) {
+ if (ds->pf.bits_per_pixel == 32)
+ v = *(uint32_t *)d;
+ else
+ v = (uint32_t) (*(uint16_t *)d);
+ r = ((v >> ds->pf.rshift) & ds->pf.rmax) * 256 /
+ (ds->pf.rmax + 1);
+ g = ((v >> ds->pf.gshift) & ds->pf.gmax) * 256 /
+ (ds->pf.gmax + 1);
+ b = ((v >> ds->pf.bshift) & ds->pf.bmax) * 256 /
+ (ds->pf.bmax + 1);
+ fputc(r, f);
+ fputc(g, f);
+ fputc(b, f);
+ d += ds->pf.bytes_per_pixel;
+ }
+ d1 += ds->linesize;
+ }
+ fclose(f);
+ return 0;
+}
+
+static DisplayChangeListener* vga_screen_dump_init(DisplayState *ds)
+{
+ DisplayChangeListener *dcl;
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = vga_save_dpy_update;
+ dcl->dpy_resize = vga_save_dpy_resize;
+ dcl->dpy_refresh = vga_save_dpy_refresh;
+ register_displaychangelistener(ds, dcl);
+ return dcl;
+}
+
+/* save the vga display in a PPM image even if no display is
+ available */
+static void vga_screen_dump(void *opaque, const char *filename)
+{
+ VGACommonState *s = opaque;
+
+ if (!screen_dump_dcl)
+ screen_dump_dcl = vga_screen_dump_init(s->ds);
+
+ screen_dump_filename = (char *)filename;
+ vga_invalidate_display(s);
+ vga_hw_update();
+}
+
diff --git a/hw/vga_int.h b/hw/vga_int.h
new file mode 100644
index 0000000..4790053
--- /dev/null
+++ b/hw/vga_int.h
@@ -0,0 +1,232 @@
+/*
+ * QEMU internal VGA defines.
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <hw/hw.h>
+
+#define MSR_COLOR_EMULATION 0x01
+#define MSR_PAGE_SELECT 0x20
+
+#define ST01_V_RETRACE 0x08
+#define ST01_DISP_ENABLE 0x01
+
+/* bochs VBE support */
+#define CONFIG_BOCHS_VBE
+
+#define VBE_DISPI_MAX_XRES 2560
+#define VBE_DISPI_MAX_YRES 1600
+#define VBE_DISPI_MAX_BPP 32
+
+#define VBE_DISPI_INDEX_ID 0x0
+#define VBE_DISPI_INDEX_XRES 0x1
+#define VBE_DISPI_INDEX_YRES 0x2
+#define VBE_DISPI_INDEX_BPP 0x3
+#define VBE_DISPI_INDEX_ENABLE 0x4
+#define VBE_DISPI_INDEX_BANK 0x5
+#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+#define VBE_DISPI_INDEX_X_OFFSET 0x8
+#define VBE_DISPI_INDEX_Y_OFFSET 0x9
+#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */
+#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */
+
+#define VBE_DISPI_ID0 0xB0C0
+#define VBE_DISPI_ID1 0xB0C1
+#define VBE_DISPI_ID2 0xB0C2
+#define VBE_DISPI_ID3 0xB0C3
+#define VBE_DISPI_ID4 0xB0C4
+#define VBE_DISPI_ID5 0xB0C5
+
+#define VBE_DISPI_DISABLED 0x00
+#define VBE_DISPI_ENABLED 0x01
+#define VBE_DISPI_GETCAPS 0x02
+#define VBE_DISPI_8BIT_DAC 0x20
+#define VBE_DISPI_LFB_ENABLED 0x40
+#define VBE_DISPI_NOCLEARMEM 0x80
+
+#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+#ifdef CONFIG_BOCHS_VBE
+
+#define VGA_STATE_COMMON_BOCHS_VBE \
+ uint16_t vbe_index; \
+ uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; \
+ uint32_t vbe_start_addr; \
+ uint32_t vbe_line_offset; \
+ uint32_t vbe_bank_mask; \
+ int vbe_mapped;
+#else
+
+#define VGA_STATE_COMMON_BOCHS_VBE
+
+#endif /* !CONFIG_BOCHS_VBE */
+
+#define CH_ATTR_SIZE (160 * 100)
+#define VGA_MAX_HEIGHT 2048
+
+struct vga_precise_retrace {
+ int64_t ticks_per_char;
+ int64_t total_chars;
+ int htotal;
+ int hstart;
+ int hend;
+ int vstart;
+ int vend;
+ int freq;
+};
+
+union vga_retrace {
+ struct vga_precise_retrace precise;
+};
+
+struct VGACommonState;
+typedef uint8_t (* vga_retrace_fn)(struct VGACommonState *s);
+typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
+
+typedef struct VGACommonState {
+ uint8_t *vram_ptr;
+ ram_addr_t vram_offset;
+ uint32_t vram_size;
+ uint32_t lfb_addr;
+ uint32_t lfb_end;
+ uint32_t map_addr;
+ uint32_t map_end;
+ uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */
+ uint32_t latch;
+ uint8_t sr_index;
+ uint8_t sr[256];
+ uint8_t gr_index;
+ uint8_t gr[256];
+ uint8_t ar_index;
+ uint8_t ar[21];
+ int ar_flip_flop;
+ uint8_t cr_index;
+ uint8_t cr[256]; /* CRT registers */
+ uint8_t msr; /* Misc Output Register */
+ uint8_t fcr; /* Feature Control Register */
+ uint8_t st00; /* status 0 */
+ uint8_t st01; /* status 1 */
+ uint8_t dac_state;
+ uint8_t dac_sub_index;
+ uint8_t dac_read_index;
+ uint8_t dac_write_index;
+ uint8_t dac_cache[3]; /* used when writing */
+ int dac_8bit;
+ uint8_t palette[768];
+ int32_t bank_offset;
+ int vga_io_memory;
+ int (*get_bpp)(struct VGACommonState *s);
+ void (*get_offsets)(struct VGACommonState *s,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr,
+ uint32_t *pline_compare);
+ void (*get_resolution)(struct VGACommonState *s,
+ int *pwidth,
+ int *pheight);
+ VGA_STATE_COMMON_BOCHS_VBE
+ /* display refresh support */
+ DisplayState *ds;
+ uint32_t font_offsets[2];
+ int graphic_mode;
+ uint8_t shift_control;
+ uint8_t double_scan;
+ uint32_t line_offset;
+ uint32_t line_compare;
+ uint32_t start_addr;
+ uint32_t plane_updated;
+ uint32_t last_line_offset;
+ uint8_t last_cw, last_ch;
+ uint32_t last_width, last_height; /* in chars or pixels */
+ uint32_t last_scr_width, last_scr_height; /* in pixels */
+ uint32_t last_depth; /* in bits */
+ uint8_t cursor_start, cursor_end;
+ uint32_t cursor_offset;
+ unsigned int (*rgb_to_pixel)(unsigned int r,
+ unsigned int g, unsigned b);
+ vga_hw_update_ptr update;
+ vga_hw_invalidate_ptr invalidate;
+ vga_hw_screen_dump_ptr screen_dump;
+ vga_hw_text_update_ptr text_update;
+ /* hardware mouse cursor support */
+ uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32];
+ void (*cursor_invalidate)(struct VGACommonState *s);
+ void (*cursor_draw_line)(struct VGACommonState *s, uint8_t *d, int y);
+ /* tell for each page if it has been updated since the last time */
+ uint32_t last_palette[256];
+ uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
+ /* retrace */
+ vga_retrace_fn retrace;
+ vga_update_retrace_info_fn update_retrace_info;
+ union vga_retrace retrace_info;
+ uint8_t is_vbe_vmstate;
+} VGACommonState;
+
+static inline int c6_to_8(int v)
+{
+ int b;
+ v &= 0x3f;
+ b = v & 1;
+ return (v << 2) | (b << 1) | b;
+}
+
+void vga_common_init(VGACommonState *s, int vga_ram_size);
+void vga_init(VGACommonState *s);
+void vga_common_reset(VGACommonState *s);
+
+void vga_dirty_log_start(VGACommonState *s);
+void vga_dirty_log_stop(VGACommonState *s);
+void vga_dirty_log_restart(VGACommonState *s);
+
+extern const VMStateDescription vmstate_vga_common;
+uint32_t vga_ioport_read(void *opaque, uint32_t addr);
+void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr);
+void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val);
+void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2);
+int ppm_save(const char *filename, struct DisplaySurface *ds);
+
+void vga_draw_cursor_line_8(uint8_t *d1, const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0, unsigned int color1,
+ unsigned int color_xor);
+void vga_draw_cursor_line_16(uint8_t *d1, const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0, unsigned int color1,
+ unsigned int color_xor);
+void vga_draw_cursor_line_32(uint8_t *d1, const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0, unsigned int color1,
+ unsigned int color_xor);
+
+int vga_ioport_invalid(VGACommonState *s, uint32_t addr);
+void vga_init_vbe(VGACommonState *s);
+
+extern const uint8_t sr_mask[8];
+extern const uint8_t gr_mask[16];
+
+#define VGA_RAM_SIZE (16 * 1024 * 1024)
+#define VGABIOS_FILENAME "vgabios.bin"
+#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+
+extern CPUReadMemoryFunc * const vga_mem_read[3];
+extern CPUWriteMemoryFunc * const vga_mem_write[3];
diff --git a/hw/vga_template.h b/hw/vga_template.h
new file mode 100644
index 0000000..681425f
--- /dev/null
+++ b/hw/vga_template.h
@@ -0,0 +1,525 @@
+/*
+ * QEMU VGA Emulator templates
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if DEPTH == 8
+#define BPP 1
+#define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+#define BPP 2
+#define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+#define BPP 4
+#define PIXEL_TYPE uint32_t
+#else
+#error unsupport depth
+#endif
+
+#ifdef BGR_FORMAT
+#define PIXEL_NAME glue(DEPTH, bgr)
+#else
+#define PIXEL_NAME DEPTH
+#endif /* BGR_FORMAT */
+
+#if DEPTH != 15 && !defined(BGR_FORMAT)
+
+static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d,
+ uint32_t font_data,
+ uint32_t xorcol,
+ uint32_t bgcol)
+{
+#if BPP == 1
+ ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+#elif BPP == 2
+ ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+#else
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+#endif
+}
+
+static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol);
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ glue(vga_draw_glyph_line_, DEPTH)(d,
+ expand4to8[font_data >> 4],
+ xorcol, bgcol);
+ glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP,
+ expand4to8[font_data & 0x0f],
+ xorcol, bgcol);
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol, int dup9)
+{
+ uint32_t font_data, xorcol, v;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+#if BPP == 1
+ cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol);
+ v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+ cpu_to_32wu(((uint32_t *)d)+1, v);
+ if (dup9)
+ ((uint8_t *)d)[8] = v >> (24 * (1 - BIG));
+ else
+ ((uint8_t *)d)[8] = bgcol;
+
+#elif BPP == 2
+ cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol);
+ cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol);
+ cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol);
+ v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+ cpu_to_32wu(((uint32_t *)d)+3, v);
+ if (dup9)
+ ((uint16_t *)d)[8] = v >> (16 * (1 - BIG));
+ else
+ ((uint16_t *)d)[8] = bgcol;
+#else
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = v;
+ if (dup9)
+ ((uint32_t *)d)[8] = v;
+ else
+ ((uint32_t *)d)[8] = bgcol;
+#endif
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+/*
+ * 4 color mode
+ */
+static void glue(vga_draw_line2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, *palette, data, v;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand2[GET_PLANE(data, 0)];
+ v |= expand2[GET_PLANE(data, 2)] << 2;
+ ((PIXEL_TYPE *)d)[0] = palette[v >> 12];
+ ((PIXEL_TYPE *)d)[1] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[2] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[3] = palette[(v >> 0) & 0xf];
+
+ v = expand2[GET_PLANE(data, 1)];
+ v |= expand2[GET_PLANE(data, 3)] << 2;
+ ((PIXEL_TYPE *)d)[4] = palette[v >> 12];
+ ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+#if BPP == 1
+#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v)
+#elif BPP == 2
+#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v)
+#else
+#define PUT_PIXEL2(d, n, v) \
+((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v)
+#endif
+
+/*
+ * 4 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line2d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, *palette, data, v;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand2[GET_PLANE(data, 0)];
+ v |= expand2[GET_PLANE(data, 2)] << 2;
+ PUT_PIXEL2(d, 0, palette[v >> 12]);
+ PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]);
+
+ v = expand2[GET_PLANE(data, 1)];
+ v |= expand2[GET_PLANE(data, 3)] << 2;
+ PUT_PIXEL2(d, 4, palette[v >> 12]);
+ PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+ d += BPP * 16;
+ s += 4;
+ }
+}
+
+/*
+ * 16 color mode
+ */
+static void glue(vga_draw_line4_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, data, v, *palette;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand4[GET_PLANE(data, 0)];
+ v |= expand4[GET_PLANE(data, 1)] << 1;
+ v |= expand4[GET_PLANE(data, 2)] << 2;
+ v |= expand4[GET_PLANE(data, 3)] << 3;
+ ((PIXEL_TYPE *)d)[0] = palette[v >> 28];
+ ((PIXEL_TYPE *)d)[1] = palette[(v >> 24) & 0xf];
+ ((PIXEL_TYPE *)d)[2] = palette[(v >> 20) & 0xf];
+ ((PIXEL_TYPE *)d)[3] = palette[(v >> 16) & 0xf];
+ ((PIXEL_TYPE *)d)[4] = palette[(v >> 12) & 0xf];
+ ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+/*
+ * 16 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line4d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, data, v, *palette;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[0x12] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand4[GET_PLANE(data, 0)];
+ v |= expand4[GET_PLANE(data, 1)] << 1;
+ v |= expand4[GET_PLANE(data, 2)] << 2;
+ v |= expand4[GET_PLANE(data, 3)] << 3;
+ PUT_PIXEL2(d, 0, palette[v >> 28]);
+ PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]);
+ PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]);
+ PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]);
+ PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]);
+ PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+ d += BPP * 16;
+ s += 4;
+ }
+}
+
+/*
+ * 256 color mode, double pixels
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t *palette;
+ int x;
+
+ palette = s1->last_palette;
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ PUT_PIXEL2(d, 0, palette[s[0]]);
+ PUT_PIXEL2(d, 1, palette[s[1]]);
+ PUT_PIXEL2(d, 2, palette[s[2]]);
+ PUT_PIXEL2(d, 3, palette[s[3]]);
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+/*
+ * standard 256 color mode
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t *palette;
+ int x;
+
+ palette = s1->last_palette;
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ ((PIXEL_TYPE *)d)[0] = palette[s[0]];
+ ((PIXEL_TYPE *)d)[1] = palette[s[1]];
+ ((PIXEL_TYPE *)d)[2] = palette[s[2]];
+ ((PIXEL_TYPE *)d)[3] = palette[s[3]];
+ ((PIXEL_TYPE *)d)[4] = palette[s[4]];
+ ((PIXEL_TYPE *)d)[5] = palette[s[5]];
+ ((PIXEL_TYPE *)d)[6] = palette[s[6]];
+ ((PIXEL_TYPE *)d)[7] = palette[s[7]];
+ d += BPP * 8;
+ s += 8;
+ }
+}
+
+void glue(vga_draw_cursor_line_, DEPTH)(uint8_t *d1,
+ const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0,
+ unsigned int color1,
+ unsigned int color_xor)
+{
+ const uint8_t *plane0, *plane1;
+ int x, b0, b1;
+ uint8_t *d;
+
+ d = d1;
+ plane0 = src1;
+ plane1 = src1 + poffset;
+ for(x = 0; x < w; x++) {
+ b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1;
+ b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1;
+#if DEPTH == 8
+ switch(b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ d[0] ^= color_xor;
+ break;
+ case 2:
+ d[0] = color0;
+ break;
+ case 3:
+ d[0] = color1;
+ break;
+ }
+#elif DEPTH == 16
+ switch(b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ ((uint16_t *)d)[0] ^= color_xor;
+ break;
+ case 2:
+ ((uint16_t *)d)[0] = color0;
+ break;
+ case 3:
+ ((uint16_t *)d)[0] = color1;
+ break;
+ }
+#elif DEPTH == 32
+ switch(b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ ((uint32_t *)d)[0] ^= color_xor;
+ break;
+ case 2:
+ ((uint32_t *)d)[0] = color0;
+ break;
+ case 3:
+ ((uint32_t *)d)[0] = color1;
+ break;
+ }
+#else
+#error unsupported depth
+#endif
+ d += BPP;
+ }
+}
+
+#endif /* DEPTH != 15 */
+
+
+/* XXX: optimize */
+
+/*
+ * 15 bit color
+ */
+static void glue(vga_draw_line15_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 15 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ int w;
+ uint32_t v, r, g, b;
+
+ w = width;
+ do {
+ v = lduw_raw((void *)s);
+ r = (v >> 7) & 0xf8;
+ g = (v >> 2) & 0xf8;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+/*
+ * 16 bit color
+ */
+static void glue(vga_draw_line16_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ int w;
+ uint32_t v, r, g, b;
+
+ w = width;
+ do {
+ v = lduw_raw((void *)s);
+ r = (v >> 8) & 0xf8;
+ g = (v >> 3) & 0xfc;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+/*
+ * 24 bit color
+ */
+static void glue(vga_draw_line24_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int w;
+ uint32_t r, g, b;
+
+ w = width;
+ do {
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[0];
+ g = s[1];
+ b = s[2];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 3;
+ d += BPP;
+ } while (--w != 0);
+}
+
+/*
+ * 32 bit color
+ */
+static void glue(vga_draw_line32_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 32 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) && !defined(BGR_FORMAT)
+ memcpy(d, s, width * 4);
+#else
+ int w;
+ uint32_t r, g, b;
+
+ w = width;
+ do {
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[1];
+ g = s[2];
+ b = s[3];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 4;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+#undef PUT_PIXEL2
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
+#undef PIXEL_NAME
+#undef BGR_FORMAT
diff --git a/hw/vhost.c b/hw/vhost.c
new file mode 100644
index 0000000..6c194f0
--- /dev/null
+++ b/hw/vhost.c
@@ -0,0 +1,727 @@
+/*
+ * vhost support
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Authors:
+ * Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <sys/ioctl.h>
+#include "vhost.h"
+#include "hw/hw.h"
+#include "range.h"
+#include <linux/vhost.h>
+
+static void vhost_dev_sync_region(struct vhost_dev *dev,
+ uint64_t mfirst, uint64_t mlast,
+ uint64_t rfirst, uint64_t rlast)
+{
+ uint64_t start = MAX(mfirst, rfirst);
+ uint64_t end = MIN(mlast, rlast);
+ vhost_log_chunk_t *from = dev->log + start / VHOST_LOG_CHUNK;
+ vhost_log_chunk_t *to = dev->log + end / VHOST_LOG_CHUNK + 1;
+ uint64_t addr = (start / VHOST_LOG_CHUNK) * VHOST_LOG_CHUNK;
+
+ assert(end / VHOST_LOG_CHUNK < dev->log_size);
+ assert(start / VHOST_LOG_CHUNK < dev->log_size);
+ if (end < start) {
+ return;
+ }
+ for (;from < to; ++from) {
+ vhost_log_chunk_t log;
+ int bit;
+ /* We first check with non-atomic: much cheaper,
+ * and we expect non-dirty to be the common case. */
+ if (!*from) {
+ addr += VHOST_LOG_CHUNK;
+ continue;
+ }
+ /* Data must be read atomically. We don't really
+ * need the barrier semantics of __sync
+ * builtins, but it's easier to use them than
+ * roll our own. */
+ log = __sync_fetch_and_and(from, 0);
+ while ((bit = sizeof(log) > sizeof(int) ?
+ ffsll(log) : ffs(log))) {
+ ram_addr_t ram_addr;
+ bit -= 1;
+ ram_addr = cpu_get_physical_page_desc(addr + bit * VHOST_LOG_PAGE);
+ cpu_physical_memory_set_dirty(ram_addr);
+ log &= ~(0x1ull << bit);
+ }
+ addr += VHOST_LOG_CHUNK;
+ }
+}
+
+static int vhost_client_sync_dirty_bitmap(CPUPhysMemoryClient *client,
+ target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
+{
+ struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
+ int i;
+ if (!dev->log_enabled || !dev->started) {
+ return 0;
+ }
+ for (i = 0; i < dev->mem->nregions; ++i) {
+ struct vhost_memory_region *reg = dev->mem->regions + i;
+ vhost_dev_sync_region(dev, start_addr, end_addr,
+ reg->guest_phys_addr,
+ range_get_last(reg->guest_phys_addr,
+ reg->memory_size));
+ }
+ for (i = 0; i < dev->nvqs; ++i) {
+ struct vhost_virtqueue *vq = dev->vqs + i;
+ vhost_dev_sync_region(dev, start_addr, end_addr, vq->used_phys,
+ range_get_last(vq->used_phys, vq->used_size));
+ }
+ return 0;
+}
+
+/* Assign/unassign. Keep an unsorted array of non-overlapping
+ * memory regions in dev->mem. */
+static void vhost_dev_unassign_memory(struct vhost_dev *dev,
+ uint64_t start_addr,
+ uint64_t size)
+{
+ int from, to, n = dev->mem->nregions;
+ /* Track overlapping/split regions for sanity checking. */
+ int overlap_start = 0, overlap_end = 0, overlap_middle = 0, split = 0;
+
+ for (from = 0, to = 0; from < n; ++from, ++to) {
+ struct vhost_memory_region *reg = dev->mem->regions + to;
+ uint64_t reglast;
+ uint64_t memlast;
+ uint64_t change;
+
+ /* clone old region */
+ if (to != from) {
+ memcpy(reg, dev->mem->regions + from, sizeof *reg);
+ }
+
+ /* No overlap is simple */
+ if (!ranges_overlap(reg->guest_phys_addr, reg->memory_size,
+ start_addr, size)) {
+ continue;
+ }
+
+ /* Split only happens if supplied region
+ * is in the middle of an existing one. Thus it can not
+ * overlap with any other existing region. */
+ assert(!split);
+
+ reglast = range_get_last(reg->guest_phys_addr, reg->memory_size);
+ memlast = range_get_last(start_addr, size);
+
+ /* Remove whole region */
+ if (start_addr <= reg->guest_phys_addr && memlast >= reglast) {
+ --dev->mem->nregions;
+ --to;
+ assert(to >= 0);
+ ++overlap_middle;
+ continue;
+ }
+
+ /* Shrink region */
+ if (memlast >= reglast) {
+ reg->memory_size = start_addr - reg->guest_phys_addr;
+ assert(reg->memory_size);
+ assert(!overlap_end);
+ ++overlap_end;
+ continue;
+ }
+
+ /* Shift region */
+ if (start_addr <= reg->guest_phys_addr) {
+ change = memlast + 1 - reg->guest_phys_addr;
+ reg->memory_size -= change;
+ reg->guest_phys_addr += change;
+ reg->userspace_addr += change;
+ assert(reg->memory_size);
+ assert(!overlap_start);
+ ++overlap_start;
+ continue;
+ }
+
+ /* This only happens if supplied region
+ * is in the middle of an existing one. Thus it can not
+ * overlap with any other existing region. */
+ assert(!overlap_start);
+ assert(!overlap_end);
+ assert(!overlap_middle);
+ /* Split region: shrink first part, shift second part. */
+ memcpy(dev->mem->regions + n, reg, sizeof *reg);
+ reg->memory_size = start_addr - reg->guest_phys_addr;
+ assert(reg->memory_size);
+ change = memlast + 1 - reg->guest_phys_addr;
+ reg = dev->mem->regions + n;
+ reg->memory_size -= change;
+ assert(reg->memory_size);
+ reg->guest_phys_addr += change;
+ reg->userspace_addr += change;
+ /* Never add more than 1 region */
+ assert(dev->mem->nregions == n);
+ ++dev->mem->nregions;
+ ++split;
+ }
+}
+
+/* Called after unassign, so no regions overlap the given range. */
+static void vhost_dev_assign_memory(struct vhost_dev *dev,
+ uint64_t start_addr,
+ uint64_t size,
+ uint64_t uaddr)
+{
+ int from, to;
+ struct vhost_memory_region *merged = NULL;
+ for (from = 0, to = 0; from < dev->mem->nregions; ++from, ++to) {
+ struct vhost_memory_region *reg = dev->mem->regions + to;
+ uint64_t prlast, urlast;
+ uint64_t pmlast, umlast;
+ uint64_t s, e, u;
+
+ /* clone old region */
+ if (to != from) {
+ memcpy(reg, dev->mem->regions + from, sizeof *reg);
+ }
+ prlast = range_get_last(reg->guest_phys_addr, reg->memory_size);
+ pmlast = range_get_last(start_addr, size);
+ urlast = range_get_last(reg->userspace_addr, reg->memory_size);
+ umlast = range_get_last(uaddr, size);
+
+ /* check for overlapping regions: should never happen. */
+ assert(prlast < start_addr || pmlast < reg->guest_phys_addr);
+ /* Not an adjacent or overlapping region - do not merge. */
+ if ((prlast + 1 != start_addr || urlast + 1 != uaddr) &&
+ (pmlast + 1 != reg->guest_phys_addr ||
+ umlast + 1 != reg->userspace_addr)) {
+ continue;
+ }
+
+ if (merged) {
+ --to;
+ assert(to >= 0);
+ } else {
+ merged = reg;
+ }
+ u = MIN(uaddr, reg->userspace_addr);
+ s = MIN(start_addr, reg->guest_phys_addr);
+ e = MAX(pmlast, prlast);
+ uaddr = merged->userspace_addr = u;
+ start_addr = merged->guest_phys_addr = s;
+ size = merged->memory_size = e - s + 1;
+ assert(merged->memory_size);
+ }
+
+ if (!merged) {
+ struct vhost_memory_region *reg = dev->mem->regions + to;
+ memset(reg, 0, sizeof *reg);
+ reg->memory_size = size;
+ assert(reg->memory_size);
+ reg->guest_phys_addr = start_addr;
+ reg->userspace_addr = uaddr;
+ ++to;
+ }
+ assert(to <= dev->mem->nregions + 1);
+ dev->mem->nregions = to;
+}
+
+static uint64_t vhost_get_log_size(struct vhost_dev *dev)
+{
+ uint64_t log_size = 0;
+ int i;
+ for (i = 0; i < dev->mem->nregions; ++i) {
+ struct vhost_memory_region *reg = dev->mem->regions + i;
+ uint64_t last = range_get_last(reg->guest_phys_addr,
+ reg->memory_size);
+ log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1);
+ }
+ for (i = 0; i < dev->nvqs; ++i) {
+ struct vhost_virtqueue *vq = dev->vqs + i;
+ uint64_t last = vq->used_phys + vq->used_size - 1;
+ log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1);
+ }
+ return log_size;
+}
+
+static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size)
+{
+ vhost_log_chunk_t *log;
+ uint64_t log_base;
+ int r;
+ if (size) {
+ log = qemu_mallocz(size * sizeof *log);
+ } else {
+ log = NULL;
+ }
+ log_base = (uint64_t)(unsigned long)log;
+ r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base);
+ assert(r >= 0);
+ vhost_client_sync_dirty_bitmap(&dev->client, 0,
+ (target_phys_addr_t)~0x0ull);
+ if (dev->log) {
+ qemu_free(dev->log);
+ }
+ dev->log = log;
+ dev->log_size = size;
+}
+
+static int vhost_verify_ring_mappings(struct vhost_dev *dev,
+ uint64_t start_addr,
+ uint64_t size)
+{
+ int i;
+ for (i = 0; i < dev->nvqs; ++i) {
+ struct vhost_virtqueue *vq = dev->vqs + i;
+ target_phys_addr_t l;
+ void *p;
+
+ if (!ranges_overlap(start_addr, size, vq->ring_phys, vq->ring_size)) {
+ continue;
+ }
+ l = vq->ring_size;
+ p = cpu_physical_memory_map(vq->ring_phys, &l, 1);
+ if (!p || l != vq->ring_size) {
+ fprintf(stderr, "Unable to map ring buffer for ring %d\n", i);
+ return -ENOMEM;
+ }
+ if (p != vq->ring) {
+ fprintf(stderr, "Ring buffer relocated for ring %d\n", i);
+ return -EBUSY;
+ }
+ cpu_physical_memory_unmap(p, l, 0, 0);
+ }
+ return 0;
+}
+
+static void vhost_client_set_memory(CPUPhysMemoryClient *client,
+ target_phys_addr_t start_addr,
+ ram_addr_t size,
+ ram_addr_t phys_offset)
+{
+ struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
+ ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
+ int s = offsetof(struct vhost_memory, regions) +
+ (dev->mem->nregions + 1) * sizeof dev->mem->regions[0];
+ uint64_t log_size;
+ int r;
+ dev->mem = qemu_realloc(dev->mem, s);
+
+ assert(size);
+
+ vhost_dev_unassign_memory(dev, start_addr, size);
+ if (flags == IO_MEM_RAM) {
+ /* Add given mapping, merging adjacent regions if any */
+ vhost_dev_assign_memory(dev, start_addr, size,
+ (uintptr_t)qemu_get_ram_ptr(phys_offset));
+ } else {
+ /* Remove old mapping for this memory, if any. */
+ vhost_dev_unassign_memory(dev, start_addr, size);
+ }
+
+ if (!dev->started) {
+ return;
+ }
+
+ if (dev->started) {
+ r = vhost_verify_ring_mappings(dev, start_addr, size);
+ assert(r >= 0);
+ }
+
+ if (!dev->log_enabled) {
+ r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
+ assert(r >= 0);
+ return;
+ }
+ log_size = vhost_get_log_size(dev);
+ /* We allocate an extra 4K bytes to log,
+ * to reduce the * number of reallocations. */
+#define VHOST_LOG_BUFFER (0x1000 / sizeof *dev->log)
+ /* To log more, must increase log size before table update. */
+ if (dev->log_size < log_size) {
+ vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER);
+ }
+ r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
+ assert(r >= 0);
+ /* To log less, can only decrease log size after table update. */
+ if (dev->log_size > log_size + VHOST_LOG_BUFFER) {
+ vhost_dev_log_resize(dev, log_size);
+ }
+}
+
+static int vhost_virtqueue_set_addr(struct vhost_dev *dev,
+ struct vhost_virtqueue *vq,
+ unsigned idx, bool enable_log)
+{
+ struct vhost_vring_addr addr = {
+ .index = idx,
+ .desc_user_addr = (uint64_t)(unsigned long)vq->desc,
+ .avail_user_addr = (uint64_t)(unsigned long)vq->avail,
+ .used_user_addr = (uint64_t)(unsigned long)vq->used,
+ .log_guest_addr = vq->used_phys,
+ .flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0,
+ };
+ int r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
+ if (r < 0) {
+ return -errno;
+ }
+ return 0;
+}
+
+static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log)
+{
+ uint64_t features = dev->acked_features;
+ int r;
+ if (enable_log) {
+ features |= 0x1 << VHOST_F_LOG_ALL;
+ }
+ r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
+ return r < 0 ? -errno : 0;
+}
+
+static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log)
+{
+ int r, t, i;
+ r = vhost_dev_set_features(dev, enable_log);
+ if (r < 0) {
+ goto err_features;
+ }
+ for (i = 0; i < dev->nvqs; ++i) {
+ r = vhost_virtqueue_set_addr(dev, dev->vqs + i, i,
+ enable_log);
+ if (r < 0) {
+ goto err_vq;
+ }
+ }
+ return 0;
+err_vq:
+ for (; i >= 0; --i) {
+ t = vhost_virtqueue_set_addr(dev, dev->vqs + i, i,
+ dev->log_enabled);
+ assert(t >= 0);
+ }
+ t = vhost_dev_set_features(dev, dev->log_enabled);
+ assert(t >= 0);
+err_features:
+ return r;
+}
+
+static int vhost_client_migration_log(CPUPhysMemoryClient *client,
+ int enable)
+{
+ struct vhost_dev *dev = container_of(client, struct vhost_dev, client);
+ int r;
+ if (!!enable == dev->log_enabled) {
+ return 0;
+ }
+ if (!dev->started) {
+ dev->log_enabled = enable;
+ return 0;
+ }
+ if (!enable) {
+ r = vhost_dev_set_log(dev, false);
+ if (r < 0) {
+ return r;
+ }
+ if (dev->log) {
+ qemu_free(dev->log);
+ }
+ dev->log = NULL;
+ dev->log_size = 0;
+ } else {
+ vhost_dev_log_resize(dev, vhost_get_log_size(dev));
+ r = vhost_dev_set_log(dev, true);
+ if (r < 0) {
+ return r;
+ }
+ }
+ dev->log_enabled = enable;
+ return 0;
+}
+
+static int vhost_virtqueue_init(struct vhost_dev *dev,
+ struct VirtIODevice *vdev,
+ struct vhost_virtqueue *vq,
+ unsigned idx)
+{
+ target_phys_addr_t s, l, a;
+ int r;
+ struct vhost_vring_file file = {
+ .index = idx,
+ };
+ struct vhost_vring_state state = {
+ .index = idx,
+ };
+ struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
+
+ if (!vdev->binding->set_host_notifier) {
+ fprintf(stderr, "binding does not support host notifiers\n");
+ return -ENOSYS;
+ }
+
+ vq->num = state.num = virtio_queue_get_num(vdev, idx);
+ r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
+ if (r) {
+ return -errno;
+ }
+
+ state.num = virtio_queue_get_last_avail_idx(vdev, idx);
+ r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
+ if (r) {
+ return -errno;
+ }
+
+ s = l = virtio_queue_get_desc_size(vdev, idx);
+ a = virtio_queue_get_desc_addr(vdev, idx);
+ vq->desc = cpu_physical_memory_map(a, &l, 0);
+ if (!vq->desc || l != s) {
+ r = -ENOMEM;
+ goto fail_alloc_desc;
+ }
+ s = l = virtio_queue_get_avail_size(vdev, idx);
+ a = virtio_queue_get_avail_addr(vdev, idx);
+ vq->avail = cpu_physical_memory_map(a, &l, 0);
+ if (!vq->avail || l != s) {
+ r = -ENOMEM;
+ goto fail_alloc_avail;
+ }
+ vq->used_size = s = l = virtio_queue_get_used_size(vdev, idx);
+ vq->used_phys = a = virtio_queue_get_used_addr(vdev, idx);
+ vq->used = cpu_physical_memory_map(a, &l, 1);
+ if (!vq->used || l != s) {
+ r = -ENOMEM;
+ goto fail_alloc_used;
+ }
+
+ vq->ring_size = s = l = virtio_queue_get_ring_size(vdev, idx);
+ vq->ring_phys = a = virtio_queue_get_ring_addr(vdev, idx);
+ vq->ring = cpu_physical_memory_map(a, &l, 1);
+ if (!vq->ring || l != s) {
+ r = -ENOMEM;
+ goto fail_alloc_ring;
+ }
+
+ r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled);
+ if (r < 0) {
+ r = -errno;
+ goto fail_alloc;
+ }
+ r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, true);
+ if (r < 0) {
+ fprintf(stderr, "Error binding host notifier: %d\n", -r);
+ goto fail_host_notifier;
+ }
+
+ file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
+ r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
+ if (r) {
+ r = -errno;
+ goto fail_kick;
+ }
+
+ file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq));
+ r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file);
+ if (r) {
+ r = -errno;
+ goto fail_call;
+ }
+
+ return 0;
+
+fail_call:
+fail_kick:
+ vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
+fail_host_notifier:
+fail_alloc:
+ cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
+ 0, 0);
+fail_alloc_ring:
+ cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx),
+ 0, 0);
+fail_alloc_used:
+ cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx),
+ 0, 0);
+fail_alloc_avail:
+ cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx),
+ 0, 0);
+fail_alloc_desc:
+ return r;
+}
+
+static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
+ struct VirtIODevice *vdev,
+ struct vhost_virtqueue *vq,
+ unsigned idx)
+{
+ struct vhost_vring_state state = {
+ .index = idx,
+ };
+ int r;
+ r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
+ if (r < 0) {
+ fprintf(stderr, "vhost VQ %d host cleanup failed: %d\n", idx, r);
+ fflush(stderr);
+ }
+ assert (r >= 0);
+ r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state);
+ if (r < 0) {
+ fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
+ fflush(stderr);
+ }
+ virtio_queue_set_last_avail_idx(vdev, idx, state.num);
+ assert (r >= 0);
+ cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
+ 0, virtio_queue_get_ring_size(vdev, idx));
+ cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx),
+ 1, virtio_queue_get_used_size(vdev, idx));
+ cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx),
+ 0, virtio_queue_get_avail_size(vdev, idx));
+ cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx),
+ 0, virtio_queue_get_desc_size(vdev, idx));
+}
+
+int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force)
+{
+ uint64_t features;
+ int r;
+ if (devfd >= 0) {
+ hdev->control = devfd;
+ } else {
+ hdev->control = open("/dev/vhost-net", O_RDWR);
+ if (hdev->control < 0) {
+ return -errno;
+ }
+ }
+ r = ioctl(hdev->control, VHOST_SET_OWNER, NULL);
+ if (r < 0) {
+ goto fail;
+ }
+
+ r = ioctl(hdev->control, VHOST_GET_FEATURES, &features);
+ if (r < 0) {
+ goto fail;
+ }
+ hdev->features = features;
+
+ hdev->client.set_memory = vhost_client_set_memory;
+ hdev->client.sync_dirty_bitmap = vhost_client_sync_dirty_bitmap;
+ hdev->client.migration_log = vhost_client_migration_log;
+ hdev->mem = qemu_mallocz(offsetof(struct vhost_memory, regions));
+ hdev->log = NULL;
+ hdev->log_size = 0;
+ hdev->log_enabled = false;
+ hdev->started = false;
+ cpu_register_phys_memory_client(&hdev->client);
+ hdev->force = force;
+ return 0;
+fail:
+ r = -errno;
+ close(hdev->control);
+ return r;
+}
+
+void vhost_dev_cleanup(struct vhost_dev *hdev)
+{
+ cpu_unregister_phys_memory_client(&hdev->client);
+ qemu_free(hdev->mem);
+ close(hdev->control);
+}
+
+bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev)
+{
+ return !vdev->binding->query_guest_notifiers ||
+ vdev->binding->query_guest_notifiers(vdev->binding_opaque) ||
+ hdev->force;
+}
+
+int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
+{
+ int i, r;
+ if (!vdev->binding->set_guest_notifiers) {
+ fprintf(stderr, "binding does not support guest notifiers\n");
+ r = -ENOSYS;
+ goto fail;
+ }
+
+ r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true);
+ if (r < 0) {
+ fprintf(stderr, "Error binding guest notifier: %d\n", -r);
+ goto fail_notifiers;
+ }
+
+ r = vhost_dev_set_features(hdev, hdev->log_enabled);
+ if (r < 0) {
+ goto fail_features;
+ }
+ r = ioctl(hdev->control, VHOST_SET_MEM_TABLE, hdev->mem);
+ if (r < 0) {
+ r = -errno;
+ goto fail_mem;
+ }
+ for (i = 0; i < hdev->nvqs; ++i) {
+ r = vhost_virtqueue_init(hdev,
+ vdev,
+ hdev->vqs + i,
+ i);
+ if (r < 0) {
+ goto fail_vq;
+ }
+ }
+
+ if (hdev->log_enabled) {
+ hdev->log_size = vhost_get_log_size(hdev);
+ hdev->log = hdev->log_size ?
+ qemu_mallocz(hdev->log_size * sizeof *hdev->log) : NULL;
+ r = ioctl(hdev->control, VHOST_SET_LOG_BASE,
+ (uint64_t)(unsigned long)hdev->log);
+ if (r < 0) {
+ r = -errno;
+ goto fail_log;
+ }
+ }
+
+ hdev->started = true;
+
+ return 0;
+fail_log:
+fail_vq:
+ while (--i >= 0) {
+ vhost_virtqueue_cleanup(hdev,
+ vdev,
+ hdev->vqs + i,
+ i);
+ }
+fail_mem:
+fail_features:
+ vdev->binding->set_guest_notifiers(vdev->binding_opaque, false);
+fail_notifiers:
+fail:
+ return r;
+}
+
+void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
+{
+ int i, r;
+
+ for (i = 0; i < hdev->nvqs; ++i) {
+ vhost_virtqueue_cleanup(hdev,
+ vdev,
+ hdev->vqs + i,
+ i);
+ }
+ vhost_client_sync_dirty_bitmap(&hdev->client, 0,
+ (target_phys_addr_t)~0x0ull);
+ r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false);
+ if (r < 0) {
+ fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r);
+ fflush(stderr);
+ }
+ assert (r >= 0);
+
+ hdev->started = false;
+ qemu_free(hdev->log);
+ hdev->log_size = 0;
+}
diff --git a/hw/vhost.h b/hw/vhost.h
new file mode 100644
index 0000000..c8c595a
--- /dev/null
+++ b/hw/vhost.h
@@ -0,0 +1,50 @@
+#ifndef VHOST_H
+#define VHOST_H
+
+#include "hw/hw.h"
+#include "hw/virtio.h"
+
+/* Generic structures common for any vhost based device. */
+struct vhost_virtqueue {
+ int kick;
+ int call;
+ void *desc;
+ void *avail;
+ void *used;
+ int num;
+ unsigned long long used_phys;
+ unsigned used_size;
+ void *ring;
+ unsigned long long ring_phys;
+ unsigned ring_size;
+};
+
+typedef unsigned long vhost_log_chunk_t;
+#define VHOST_LOG_PAGE 0x1000
+#define VHOST_LOG_BITS (8 * sizeof(vhost_log_chunk_t))
+#define VHOST_LOG_CHUNK (VHOST_LOG_PAGE * VHOST_LOG_BITS)
+
+struct vhost_memory;
+struct vhost_dev {
+ CPUPhysMemoryClient client;
+ int control;
+ struct vhost_memory *mem;
+ struct vhost_virtqueue *vqs;
+ int nvqs;
+ unsigned long long features;
+ unsigned long long acked_features;
+ unsigned long long backend_features;
+ bool started;
+ bool log_enabled;
+ vhost_log_chunk_t *log;
+ unsigned long long log_size;
+ bool force;
+};
+
+int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force);
+void vhost_dev_cleanup(struct vhost_dev *hdev);
+bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev);
+int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev);
+void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev);
+
+#endif
diff --git a/hw/vhost_net.c b/hw/vhost_net.c
new file mode 100644
index 0000000..420e05f
--- /dev/null
+++ b/hw/vhost_net.c
@@ -0,0 +1,229 @@
+/*
+ * vhost-net support
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Authors:
+ * Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "net.h"
+#include "net/tap.h"
+
+#include "virtio-net.h"
+#include "vhost_net.h"
+
+#include "config.h"
+
+#ifdef CONFIG_VHOST_NET
+#include <linux/vhost.h>
+#include <sys/socket.h>
+#include <linux/kvm.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/virtio_ring.h>
+#include <netpacket/packet.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#include "vhost.h"
+
+struct vhost_net {
+ struct vhost_dev dev;
+ struct vhost_virtqueue vqs[2];
+ int backend;
+ VLANClientState *vc;
+};
+
+unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+{
+ /* Clear features not supported by host kernel. */
+ if (!(net->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) {
+ features &= ~(1 << VIRTIO_F_NOTIFY_ON_EMPTY);
+ }
+ if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) {
+ features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC);
+ }
+ if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) {
+ features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
+ return features;
+}
+
+void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+{
+ net->dev.acked_features = net->dev.backend_features;
+ if (features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) {
+ net->dev.acked_features |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY);
+ }
+ if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) {
+ net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC);
+ }
+ if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
+}
+
+static int vhost_net_get_fd(VLANClientState *backend)
+{
+ switch (backend->info->type) {
+ case NET_CLIENT_TYPE_TAP:
+ return tap_get_fd(backend);
+ default:
+ fprintf(stderr, "vhost-net requires tap backend\n");
+ return -EBADFD;
+ }
+}
+
+struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd,
+ bool force)
+{
+ int r;
+ struct vhost_net *net = qemu_malloc(sizeof *net);
+ if (!backend) {
+ fprintf(stderr, "vhost-net requires backend to be setup\n");
+ goto fail;
+ }
+ r = vhost_net_get_fd(backend);
+ if (r < 0) {
+ goto fail;
+ }
+ net->vc = backend;
+ net->dev.backend_features = tap_has_vnet_hdr(backend) ? 0 :
+ (1 << VHOST_NET_F_VIRTIO_NET_HDR);
+ net->backend = r;
+
+ r = vhost_dev_init(&net->dev, devfd, force);
+ if (r < 0) {
+ goto fail;
+ }
+ if (!tap_has_vnet_hdr_len(backend,
+ sizeof(struct virtio_net_hdr_mrg_rxbuf))) {
+ net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF);
+ }
+ if (~net->dev.features & net->dev.backend_features) {
+ fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n",
+ (uint64_t)(~net->dev.features & net->dev.backend_features));
+ vhost_dev_cleanup(&net->dev);
+ goto fail;
+ }
+
+ /* Set sane init value. Override when guest acks. */
+ vhost_net_ack_features(net, 0);
+ return net;
+fail:
+ qemu_free(net);
+ return NULL;
+}
+
+bool vhost_net_query(VHostNetState *net, VirtIODevice *dev)
+{
+ return vhost_dev_query(&net->dev, dev);
+}
+
+int vhost_net_start(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+ struct vhost_vring_file file = { };
+ int r;
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc,
+ sizeof(struct virtio_net_hdr_mrg_rxbuf));
+ }
+
+ net->dev.nvqs = 2;
+ net->dev.vqs = net->vqs;
+ r = vhost_dev_start(&net->dev, dev);
+ if (r < 0) {
+ return r;
+ }
+
+ net->vc->info->poll(net->vc, false);
+ qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
+ file.fd = net->backend;
+ for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
+ r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ file.fd = -1;
+ while (file.index-- > 0) {
+ int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
+ assert(r >= 0);
+ }
+ net->vc->info->poll(net->vc, true);
+ vhost_dev_stop(&net->dev, dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
+ return r;
+}
+
+void vhost_net_stop(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+ struct vhost_vring_file file = { .fd = -1 };
+
+ for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
+ int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file);
+ assert(r >= 0);
+ }
+ net->vc->info->poll(net->vc, true);
+ vhost_dev_stop(&net->dev, dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
+}
+
+void vhost_net_cleanup(struct vhost_net *net)
+{
+ vhost_dev_cleanup(&net->dev);
+ if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
+ tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
+ }
+ qemu_free(net);
+}
+#else
+struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd,
+ bool force)
+{
+ return NULL;
+}
+
+bool vhost_net_query(VHostNetState *net, VirtIODevice *dev)
+{
+ return false;
+}
+
+int vhost_net_start(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+ return -ENOSYS;
+}
+void vhost_net_stop(struct vhost_net *net,
+ VirtIODevice *dev)
+{
+}
+
+void vhost_net_cleanup(struct vhost_net *net)
+{
+}
+
+unsigned vhost_net_get_features(struct vhost_net *net, unsigned features)
+{
+ return features;
+}
+void vhost_net_ack_features(struct vhost_net *net, unsigned features)
+{
+}
+#endif
diff --git a/hw/vhost_net.h b/hw/vhost_net.h
new file mode 100644
index 0000000..91e40b1
--- /dev/null
+++ b/hw/vhost_net.h
@@ -0,0 +1,20 @@
+#ifndef VHOST_NET_H
+#define VHOST_NET_H
+
+#include "net.h"
+
+struct vhost_net;
+typedef struct vhost_net VHostNetState;
+
+VHostNetState *vhost_net_init(VLANClientState *backend, int devfd, bool force);
+
+bool vhost_net_query(VHostNetState *net, VirtIODevice *dev);
+int vhost_net_start(VHostNetState *net, VirtIODevice *dev);
+void vhost_net_stop(VHostNetState *net, VirtIODevice *dev);
+
+void vhost_net_cleanup(VHostNetState *net);
+
+unsigned vhost_net_get_features(VHostNetState *net, unsigned features);
+void vhost_net_ack_features(VHostNetState *net, unsigned features);
+
+#endif
diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c
new file mode 100644
index 0000000..fa60515
--- /dev/null
+++ b/hw/virtex_ml507.c
@@ -0,0 +1,276 @@
+/*
+ * Model of Xilinx Virtex5 ML507 PPC-440 refdesign.
+ *
+ * Copyright (c) 2010 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "pc.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "loader.h"
+#include "elf.h"
+#include "qemu-log.h"
+
+#include "ppc.h"
+#include "ppc4xx.h"
+#include "ppc440.h"
+#include "ppc405.h"
+
+#include "blockdev.h"
+#include "xilinx.h"
+
+#define EPAPR_MAGIC (0x45504150)
+#define FLASH_SIZE (16 * 1024 * 1024)
+
+static struct boot_info
+{
+ uint32_t bootstrap_pc;
+ uint32_t cmdline;
+ uint32_t fdt;
+ uint32_t ima_size;
+ void *vfdt;
+} boot_info;
+
+/* Create reset TLB entries for BookE, spanning the 32bit addr space. */
+static void mmubooke_create_initial_mapping(CPUState *env,
+ target_ulong va,
+ target_phys_addr_t pa)
+{
+ ppcemb_tlb_t *tlb = &env->tlb[0].tlbe;
+
+ tlb->attr = 0;
+ tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
+ tlb->size = 1 << 31; /* up to 0x80000000 */
+ tlb->EPN = va & TARGET_PAGE_MASK;
+ tlb->RPN = pa & TARGET_PAGE_MASK;
+ tlb->PID = 0;
+
+ tlb = &env->tlb[1].tlbe;
+ tlb->attr = 0;
+ tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
+ tlb->size = 1 << 31; /* up to 0xffffffff */
+ tlb->EPN = 0x80000000 & TARGET_PAGE_MASK;
+ tlb->RPN = 0x80000000 & TARGET_PAGE_MASK;
+ tlb->PID = 0;
+}
+
+static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
+ int do_init,
+ const char *cpu_model,
+ clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+ uint32_t sysclk)
+{
+ CPUState *env;
+ qemu_irq *irqs;
+
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to initialize CPU!\n");
+ exit(1);
+ }
+
+ cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
+ cpu_clk->opaque = env;
+ /* Set time-base frequency to sysclk */
+ tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR);
+ tb_clk->opaque = env;
+
+ ppc_dcr_init(env, NULL, NULL);
+
+ /* interrupt controller */
+ irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+ irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
+ irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
+ ppcuic_init(env, irqs, 0x0C0, 0, 1);
+ return env;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ struct boot_info *bi = env->load_info;
+
+ cpu_reset(env);
+ /* Linux Kernel Parameters (passing device tree):
+ * r3: pointer to the fdt
+ * r4: 0
+ * r5: 0
+ * r6: epapr magic
+ * r7: size of IMA in bytes
+ * r8: 0
+ * r9: 0
+ */
+ env->gpr[1] = (16<<20) - 8;
+ /* Provide a device-tree. */
+ env->gpr[3] = bi->fdt;
+ env->nip = bi->bootstrap_pc;
+
+ /* Create a mapping for the kernel. */
+ mmubooke_create_initial_mapping(env, 0, 0);
+ env->gpr[6] = tswap32(EPAPR_MAGIC);
+ env->gpr[7] = bi->ima_size;
+}
+
+#define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb"
+static int xilinx_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ char *path;
+ int fdt_size;
+#ifdef CONFIG_FDT
+ void *fdt;
+ int r;
+
+ /* Try the local "ppc.dtb" override. */
+ fdt = load_device_tree("ppc.dtb", &fdt_size);
+ if (!fdt) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt = load_device_tree(path, &fdt_size);
+ qemu_free(path);
+ }
+ if (!fdt) {
+ return 0;
+ }
+ }
+
+ r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
+ if (r < 0)
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
+#else
+ /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
+ to the kernel. */
+ fdt_size = load_image_targphys("ppc.dtb", addr, 0x10000);
+ if (fdt_size < 0) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt_size = load_image_targphys(path, addr, 0x10000);
+ qemu_free(path);
+ }
+ }
+
+ if (kernel_cmdline) {
+ fprintf(stderr,
+ "Warning: missing libfdt, cannot pass cmdline to kernel!\n");
+ }
+#endif
+ return fdt_size;
+}
+
+static void virtex_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename, const char *cpu_model)
+{
+ DeviceState *dev;
+ CPUState *env;
+ target_phys_addr_t ram_base = 0;
+ DriveInfo *dinfo;
+ ram_addr_t phys_ram;
+ ram_addr_t phys_flash;
+ qemu_irq irq[32], *cpu_irq;
+ clk_setup_t clk_setup[7];
+ int kernel_size;
+ int i;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "440-Xilinx";
+ }
+
+ memset(clk_setup, 0, sizeof(clk_setup));
+ env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0],
+ &clk_setup[1], 400000000);
+ qemu_register_reset(main_cpu_reset, env);
+
+ phys_ram = qemu_ram_alloc(NULL, "ram", ram_size);
+ cpu_register_physical_memory(ram_base, ram_size, phys_ram | IO_MEM_RAM);
+
+ phys_flash = qemu_ram_alloc(NULL, "virtex.flash", FLASH_SIZE);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(0xfc000000, phys_flash,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ FLASH_SIZE >> 16,
+ 1, 0x89, 0x18, 0x0000, 0x0, 1);
+
+ cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT];
+ dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0);
+ for (i = 0; i < 32; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+
+ serial_mm_init(0x83e01003ULL, 2, irq[9], 115200, serial_hds[0], 1, 0);
+
+ /* 2 timers at irq 2 @ 62 Mhz. */
+ xilinx_timer_create(0x83c00000, irq[3], 2, 62 * 1000000);
+
+ if (kernel_filename) {
+ uint64_t entry, low, high;
+ target_phys_addr_t boot_offset;
+
+ /* Boots a kernel elf binary. */
+ kernel_size = load_elf(kernel_filename, NULL, NULL,
+ &entry, &low, &high, 1, ELF_MACHINE, 0);
+ boot_info.bootstrap_pc = entry & 0x00ffffff;
+
+ if (kernel_size < 0) {
+ boot_offset = 0x1200000;
+ /* If we failed loading ELF's try a raw image. */
+ kernel_size = load_image_targphys(kernel_filename,
+ boot_offset,
+ ram_size);
+ boot_info.bootstrap_pc = boot_offset;
+ high = boot_info.bootstrap_pc + kernel_size + 8192;
+ }
+
+ boot_info.ima_size = kernel_size;
+
+ /* Provide a device-tree. */
+ boot_info.fdt = high + (8192 * 2);
+ boot_info.fdt &= ~8191;
+ xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline);
+ }
+ env->load_info = &boot_info;
+}
+
+static QEMUMachine virtex_machine = {
+ .name = "virtex-ml507",
+ .desc = "Xilinx Virtex ML507 reference design",
+ .init = virtex_init,
+};
+
+static void virtex_machine_init(void)
+{
+ qemu_register_machine(&virtex_machine);
+}
+
+machine_init(virtex_machine_init);
diff --git a/hw/virtio-9p-debug.c b/hw/virtio-9p-debug.c
new file mode 100644
index 0000000..6b18842
--- /dev/null
+++ b/hw/virtio-9p-debug.c
@@ -0,0 +1,645 @@
+/*
+ * Virtio 9p PDU debug
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "virtio.h"
+#include "pc.h"
+#include "virtio-9p.h"
+#include "virtio-9p-debug.h"
+
+#define BUG_ON(cond) assert(!(cond))
+
+static FILE *llogfile;
+
+static struct iovec *get_sg(V9fsPDU *pdu, int rx)
+{
+ if (rx) {
+ return pdu->elem.in_sg;
+ }
+ return pdu->elem.out_sg;
+}
+
+static int get_sg_count(V9fsPDU *pdu, int rx)
+{
+ if (rx) {
+ return pdu->elem.in_num;
+ }
+ return pdu->elem.out_num;
+
+}
+
+static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ size_t copied;
+ int count = get_sg_count(pdu, rx);
+ size_t offset = *offsetp;
+ struct iovec *sg = get_sg(pdu, rx);
+ int8_t value;
+
+ copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+ BUG_ON(copied != sizeof(value));
+ offset += sizeof(value);
+ fprintf(llogfile, "%s=0x%x", name, value);
+ *offsetp = offset;
+}
+
+static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ size_t copied;
+ int count = get_sg_count(pdu, rx);
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ int16_t value;
+
+
+ copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+ BUG_ON(copied != sizeof(value));
+ offset += sizeof(value);
+ fprintf(llogfile, "%s=0x%x", name, value);
+ *offsetp = offset;
+}
+
+static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ size_t copied;
+ int count = get_sg_count(pdu, rx);
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ int32_t value;
+
+
+ copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+ BUG_ON(copied != sizeof(value));
+ offset += sizeof(value);
+ fprintf(llogfile, "%s=0x%x", name, value);
+ *offsetp = offset;
+}
+
+static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ size_t copied;
+ int count = get_sg_count(pdu, rx);
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ int64_t value;
+
+
+ copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
+
+ BUG_ON(copied != sizeof(value));
+ offset += sizeof(value);
+ fprintf(llogfile, "%s=0x%" PRIx64, name, value);
+ *offsetp = offset;
+}
+
+static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ int sg_count = get_sg_count(pdu, rx);
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ uint16_t tmp_size, size;
+ size_t result;
+ size_t copied = 0;
+ int i = 0;
+
+ /* get the size */
+ copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size));
+ BUG_ON(copied != sizeof(tmp_size));
+ size = le16_to_cpupu(&tmp_size);
+ offset += copied;
+
+ fprintf(llogfile, "%s=", name);
+ for (i = 0; size && i < sg_count; i++) {
+ size_t len;
+ if (offset >= sg[i].iov_len) {
+ /* skip this sg */
+ offset -= sg[i].iov_len;
+ continue;
+ } else {
+ len = MIN(sg[i].iov_len - offset, size);
+ result = fwrite(sg[i].iov_base + offset, 1, len, llogfile);
+ BUG_ON(result != len);
+ size -= len;
+ copied += len;
+ if (size) {
+ offset = 0;
+ continue;
+ }
+ }
+ }
+ *offsetp += copied;
+}
+
+static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ fprintf(llogfile, "%s={", name);
+ pprint_int8(pdu, rx, offsetp, "type");
+ pprint_int32(pdu, rx, offsetp, ", version");
+ pprint_int64(pdu, rx, offsetp, ", path");
+ fprintf(llogfile, "}");
+}
+
+static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ fprintf(llogfile, "%s={", name);
+ pprint_int16(pdu, rx, offsetp, "size");
+ pprint_int16(pdu, rx, offsetp, ", type");
+ pprint_int32(pdu, rx, offsetp, ", dev");
+ pprint_qid(pdu, rx, offsetp, ", qid");
+ pprint_int32(pdu, rx, offsetp, ", mode");
+ pprint_int32(pdu, rx, offsetp, ", atime");
+ pprint_int32(pdu, rx, offsetp, ", mtime");
+ pprint_int64(pdu, rx, offsetp, ", length");
+ pprint_str(pdu, rx, offsetp, ", name");
+ pprint_str(pdu, rx, offsetp, ", uid");
+ pprint_str(pdu, rx, offsetp, ", gid");
+ pprint_str(pdu, rx, offsetp, ", muid");
+ pprint_str(pdu, rx, offsetp, ", extension");
+ pprint_int32(pdu, rx, offsetp, ", uid");
+ pprint_int32(pdu, rx, offsetp, ", gid");
+ pprint_int32(pdu, rx, offsetp, ", muid");
+ fprintf(llogfile, "}");
+}
+
+static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp,
+ const char *name)
+{
+ fprintf(llogfile, "%s={", name);
+ pprint_qid(pdu, rx, offsetp, "qid");
+ pprint_int32(pdu, rx, offsetp, ", st_mode");
+ pprint_int64(pdu, rx, offsetp, ", st_nlink");
+ pprint_int32(pdu, rx, offsetp, ", st_uid");
+ pprint_int32(pdu, rx, offsetp, ", st_gid");
+ pprint_int64(pdu, rx, offsetp, ", st_rdev");
+ pprint_int64(pdu, rx, offsetp, ", st_size");
+ pprint_int64(pdu, rx, offsetp, ", st_blksize");
+ pprint_int64(pdu, rx, offsetp, ", st_blocks");
+ pprint_int64(pdu, rx, offsetp, ", atime");
+ pprint_int64(pdu, rx, offsetp, ", atime_nsec");
+ pprint_int64(pdu, rx, offsetp, ", mtime");
+ pprint_int64(pdu, rx, offsetp, ", mtime_nsec");
+ pprint_int64(pdu, rx, offsetp, ", ctime");
+ pprint_int64(pdu, rx, offsetp, ", ctime_nsec");
+ fprintf(llogfile, "}");
+}
+
+
+
+static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ int sg_count = get_sg_count(pdu, rx);
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ uint16_t tmp_count, count, i;
+ size_t copied = 0;
+
+ fprintf(llogfile, "%s={", name);
+
+ /* Get the count */
+ copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
+ BUG_ON(copied != sizeof(tmp_count));
+ count = le16_to_cpupu(&tmp_count);
+ offset += copied;
+
+ for (i = 0; i < count; i++) {
+ char str[512];
+ if (i) {
+ fprintf(llogfile, ", ");
+ }
+ snprintf(str, sizeof(str), "[%d]", i);
+ pprint_str(pdu, rx, &offset, str);
+ }
+
+ fprintf(llogfile, "}");
+
+ *offsetp = offset;
+}
+
+static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ int sg_count = get_sg_count(pdu, rx);
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ uint16_t tmp_count, count, i;
+ size_t copied = 0;
+
+ fprintf(llogfile, "%s={", name);
+
+ copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
+ BUG_ON(copied != sizeof(tmp_count));
+ count = le16_to_cpupu(&tmp_count);
+ offset += copied;
+
+ for (i = 0; i < count; i++) {
+ char str[512];
+ if (i) {
+ fprintf(llogfile, ", ");
+ }
+ snprintf(str, sizeof(str), "[%d]", i);
+ pprint_qid(pdu, rx, &offset, str);
+ }
+
+ fprintf(llogfile, "}");
+
+ *offsetp = offset;
+}
+
+static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ struct iovec *sg = get_sg(pdu, rx);
+ unsigned int count;
+ int i;
+
+ if (rx) {
+ count = pdu->elem.in_num;
+ } else {
+ count = pdu->elem.out_num;
+ }
+
+ fprintf(llogfile, "%s={", name);
+ for (i = 0; i < count; i++) {
+ if (i) {
+ fprintf(llogfile, ", ");
+ }
+ fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len);
+ }
+ fprintf(llogfile, "}");
+}
+
+/* FIXME: read from a directory fid returns serialized stat_t's */
+#ifdef DEBUG_DATA
+static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
+{
+ struct iovec *sg = get_sg(pdu, rx);
+ size_t offset = *offsetp;
+ unsigned int count;
+ int32_t size;
+ int total, i, j;
+ ssize_t len;
+
+ if (rx) {
+ count = pdu->elem.in_num;
+ } else
+ count = pdu->elem.out_num;
+ }
+
+ BUG_ON((offset + sizeof(size)) > sg[0].iov_len);
+
+ memcpy(&size, sg[0].iov_base + offset, sizeof(size));
+ offset += sizeof(size);
+
+ fprintf(llogfile, "size: %x\n", size);
+
+ sg[0].iov_base += 11; /* skip header */
+ sg[0].iov_len -= 11;
+
+ total = 0;
+ for (i = 0; i < count; i++) {
+ total += sg[i].iov_len;
+ if (total >= size) {
+ /* trim sg list so writev does the right thing */
+ sg[i].iov_len -= (total - size);
+ i++;
+ break;
+ }
+ }
+
+ fprintf(llogfile, "%s={\"", name);
+ fflush(llogfile);
+ for (j = 0; j < i; j++) {
+ if (j) {
+ fprintf(llogfile, "\", \"");
+ fflush(llogfile);
+ }
+
+ do {
+ len = writev(fileno(llogfile), &sg[j], 1);
+ } while (len == -1 && errno == EINTR);
+ fprintf(llogfile, "len == %ld: %m\n", len);
+ BUG_ON(len != sg[j].iov_len);
+ }
+ fprintf(llogfile, "\"}");
+
+ sg[0].iov_base -= 11;
+ sg[0].iov_len += 11;
+
+}
+#endif
+
+void pprint_pdu(V9fsPDU *pdu)
+{
+ size_t offset = 7;
+
+ if (llogfile == NULL) {
+ llogfile = fopen("/tmp/pdu.log", "w");
+ }
+
+ BUG_ON(!llogfile);
+
+ switch (pdu->id) {
+ case P9_TREADDIR:
+ fprintf(llogfile, "TREADDIR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int64(pdu, 0, &offset, ", initial offset");
+ pprint_int32(pdu, 0, &offset, ", max count");
+ break;
+ case P9_RREADDIR:
+ fprintf(llogfile, "RREADDIR: (");
+ pprint_int32(pdu, 1, &offset, "count");
+#ifdef DEBUG_DATA
+ pprint_data(pdu, 1, &offset, ", data");
+#endif
+ break;
+ case P9_TMKDIR:
+ fprintf(llogfile, "TMKDIR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, "name");
+ pprint_int32(pdu, 0, &offset, "mode");
+ pprint_int32(pdu, 0, &offset, "gid");
+ break;
+ case P9_RMKDIR:
+ fprintf(llogfile, "RMKDIR: (");
+ pprint_qid(pdu, 0, &offset, "qid");
+ break;
+ case P9_TVERSION:
+ fprintf(llogfile, "TVERSION: (");
+ pprint_int32(pdu, 0, &offset, "msize");
+ pprint_str(pdu, 0, &offset, ", version");
+ break;
+ case P9_RVERSION:
+ fprintf(llogfile, "RVERSION: (");
+ pprint_int32(pdu, 1, &offset, "msize");
+ pprint_str(pdu, 1, &offset, ", version");
+ break;
+ case P9_TGETATTR:
+ fprintf(llogfile, "TGETATTR: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RGETATTR:
+ fprintf(llogfile, "RGETATTR: (");
+ pprint_stat_dotl(pdu, 1, &offset, "getattr");
+ break;
+ case P9_TAUTH:
+ fprintf(llogfile, "TAUTH: (");
+ pprint_int32(pdu, 0, &offset, "afid");
+ pprint_str(pdu, 0, &offset, ", uname");
+ pprint_str(pdu, 0, &offset, ", aname");
+ pprint_int32(pdu, 0, &offset, ", n_uname");
+ break;
+ case P9_RAUTH:
+ fprintf(llogfile, "RAUTH: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ break;
+ case P9_TATTACH:
+ fprintf(llogfile, "TATTACH: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int32(pdu, 0, &offset, ", afid");
+ pprint_str(pdu, 0, &offset, ", uname");
+ pprint_str(pdu, 0, &offset, ", aname");
+ pprint_int32(pdu, 0, &offset, ", n_uname");
+ break;
+ case P9_RATTACH:
+ fprintf(llogfile, "RATTACH: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ break;
+ case P9_TERROR:
+ fprintf(llogfile, "TERROR: (");
+ break;
+ case P9_RERROR:
+ fprintf(llogfile, "RERROR: (");
+ pprint_str(pdu, 1, &offset, "ename");
+ pprint_int32(pdu, 1, &offset, ", ecode");
+ break;
+ case P9_TFLUSH:
+ fprintf(llogfile, "TFLUSH: (");
+ pprint_int16(pdu, 0, &offset, "oldtag");
+ break;
+ case P9_RFLUSH:
+ fprintf(llogfile, "RFLUSH: (");
+ break;
+ case P9_TWALK:
+ fprintf(llogfile, "TWALK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int32(pdu, 0, &offset, ", newfid");
+ pprint_strs(pdu, 0, &offset, ", wnames");
+ break;
+ case P9_RWALK:
+ fprintf(llogfile, "RWALK: (");
+ pprint_qids(pdu, 1, &offset, "wqids");
+ break;
+ case P9_TOPEN:
+ fprintf(llogfile, "TOPEN: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int8(pdu, 0, &offset, ", mode");
+ break;
+ case P9_ROPEN:
+ fprintf(llogfile, "ROPEN: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ pprint_int32(pdu, 1, &offset, ", iounit");
+ break;
+ case P9_TCREATE:
+ fprintf(llogfile, "TCREATE: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int32(pdu, 0, &offset, ", perm");
+ pprint_int8(pdu, 0, &offset, ", mode");
+ pprint_str(pdu, 0, &offset, ", extension");
+ break;
+ case P9_RCREATE:
+ fprintf(llogfile, "RCREATE: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ pprint_int32(pdu, 1, &offset, ", iounit");
+ break;
+ case P9_TSYMLINK:
+ fprintf(llogfile, "TSYMLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_str(pdu, 0, &offset, ", symname");
+ pprint_int32(pdu, 0, &offset, ", gid");
+ break;
+ case P9_RSYMLINK:
+ fprintf(llogfile, "RSYMLINK: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ break;
+ case P9_TLCREATE:
+ fprintf(llogfile, "TLCREATE: (");
+ pprint_int32(pdu, 0, &offset, "dfid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ pprint_int32(pdu, 0, &offset, ", mode");
+ pprint_int32(pdu, 0, &offset, ", gid");
+ break;
+ case P9_RLCREATE:
+ fprintf(llogfile, "RLCREATE: (");
+ pprint_qid(pdu, 1, &offset, "qid");
+ pprint_int32(pdu, 1, &offset, ", iounit");
+ break;
+ case P9_TMKNOD:
+ fprintf(llogfile, "TMKNOD: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, "name");
+ pprint_int32(pdu, 0, &offset, "mode");
+ pprint_int32(pdu, 0, &offset, "major");
+ pprint_int32(pdu, 0, &offset, "minor");
+ pprint_int32(pdu, 0, &offset, "gid");
+ break;
+ case P9_RMKNOD:
+ fprintf(llogfile, "RMKNOD: )");
+ pprint_qid(pdu, 0, &offset, "qid");
+ break;
+ case P9_TREADLINK:
+ fprintf(llogfile, "TREADLINK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RREADLINK:
+ fprintf(llogfile, "RREADLINK: (");
+ pprint_str(pdu, 0, &offset, "target");
+ break;
+ case P9_TREAD:
+ fprintf(llogfile, "TREAD: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int64(pdu, 0, &offset, ", offset");
+ pprint_int32(pdu, 0, &offset, ", count");
+ pprint_sg(pdu, 0, &offset, ", sg");
+ break;
+ case P9_RREAD:
+ fprintf(llogfile, "RREAD: (");
+ pprint_int32(pdu, 1, &offset, "count");
+ pprint_sg(pdu, 1, &offset, ", sg");
+ offset = 7;
+#ifdef DEBUG_DATA
+ pprint_data(pdu, 1, &offset, ", data");
+#endif
+ break;
+ case P9_TWRITE:
+ fprintf(llogfile, "TWRITE: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int64(pdu, 0, &offset, ", offset");
+ pprint_int32(pdu, 0, &offset, ", count");
+ break;
+ case P9_RWRITE:
+ fprintf(llogfile, "RWRITE: (");
+ pprint_int32(pdu, 1, &offset, "count");
+ break;
+ case P9_TCLUNK:
+ fprintf(llogfile, "TCLUNK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RCLUNK:
+ fprintf(llogfile, "RCLUNK: (");
+ break;
+ case P9_TFSYNC:
+ fprintf(llogfile, "TFSYNC: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RFSYNC:
+ fprintf(llogfile, "RFSYNC: (");
+ break;
+ case P9_TLINK:
+ fprintf(llogfile, "TLINK: (");
+ pprint_int32(pdu, 0, &offset, "dfid");
+ pprint_int32(pdu, 0, &offset, ", fid");
+ pprint_str(pdu, 0, &offset, ", newpath");
+ break;
+ case P9_RLINK:
+ fprintf(llogfile, "RLINK: (");
+ break;
+ case P9_TREMOVE:
+ fprintf(llogfile, "TREMOVE: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RREMOVE:
+ fprintf(llogfile, "RREMOVE: (");
+ break;
+ case P9_TSTAT:
+ fprintf(llogfile, "TSTAT: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ break;
+ case P9_RSTAT:
+ fprintf(llogfile, "RSTAT: (");
+ offset += 2; /* ignored */
+ pprint_stat(pdu, 1, &offset, "stat");
+ break;
+ case P9_TWSTAT:
+ fprintf(llogfile, "TWSTAT: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ offset += 2; /* ignored */
+ pprint_stat(pdu, 0, &offset, ", stat");
+ break;
+ case P9_RWSTAT:
+ fprintf(llogfile, "RWSTAT: (");
+ break;
+ case P9_TXATTRWALK:
+ fprintf(llogfile, "TXATTRWALK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int32(pdu, 0, &offset, ", newfid");
+ pprint_str(pdu, 0, &offset, ", xattr name");
+ break;
+ case P9_RXATTRWALK:
+ fprintf(llogfile, "RXATTRWALK: (");
+ pprint_int64(pdu, 1, &offset, "xattrsize");
+ case P9_TXATTRCREATE:
+ fprintf(llogfile, "TXATTRCREATE: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_str(pdu, 0, &offset, ", name");
+ pprint_int64(pdu, 0, &offset, ", xattrsize");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ break;
+ case P9_RXATTRCREATE:
+ fprintf(llogfile, "RXATTRCREATE: (");
+ break;
+ case P9_TLOCK:
+ fprintf(llogfile, "TLOCK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int8(pdu, 0, &offset, ", type");
+ pprint_int32(pdu, 0, &offset, ", flags");
+ pprint_int64(pdu, 0, &offset, ", start");
+ pprint_int64(pdu, 0, &offset, ", length");
+ pprint_int32(pdu, 0, &offset, ", proc_id");
+ pprint_str(pdu, 0, &offset, ", client_id");
+ break;
+ case P9_RLOCK:
+ fprintf(llogfile, "RLOCK: (");
+ pprint_int8(pdu, 0, &offset, "status");
+ break;
+ case P9_TGETLOCK:
+ fprintf(llogfile, "TGETLOCK: (");
+ pprint_int32(pdu, 0, &offset, "fid");
+ pprint_int8(pdu, 0, &offset, ", type");
+ pprint_int64(pdu, 0, &offset, ", start");
+ pprint_int64(pdu, 0, &offset, ", length");
+ pprint_int32(pdu, 0, &offset, ", proc_id");
+ pprint_str(pdu, 0, &offset, ", client_id");
+ break;
+ case P9_RGETLOCK:
+ fprintf(llogfile, "RGETLOCK: (");
+ pprint_int8(pdu, 0, &offset, "type");
+ pprint_int64(pdu, 0, &offset, ", start");
+ pprint_int64(pdu, 0, &offset, ", length");
+ pprint_int32(pdu, 0, &offset, ", proc_id");
+ pprint_str(pdu, 0, &offset, ", client_id");
+ break;
+ default:
+ fprintf(llogfile, "unknown(%d): (", pdu->id);
+ break;
+ }
+
+ fprintf(llogfile, ")\n");
+ /* Flush the log message out */
+ fflush(llogfile);
+}
diff --git a/hw/virtio-9p-debug.h b/hw/virtio-9p-debug.h
new file mode 100644
index 0000000..d9a2491
--- /dev/null
+++ b/hw/virtio-9p-debug.h
@@ -0,0 +1,6 @@
+#ifndef _QEMU_VIRTIO_9P_DEBUG_H
+#define _QEMU_VIRTIO_9P_DEBUG_H
+
+void pprint_pdu(V9fsPDU *pdu);
+
+#endif
diff --git a/hw/virtio-9p-local.c b/hw/virtio-9p-local.c
new file mode 100644
index 0000000..a8e7525
--- /dev/null
+++ b/hw/virtio-9p-local.c
@@ -0,0 +1,563 @@
+/*
+ * Virtio 9p Posix callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "virtio-9p-xattr.h"
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <attr/xattr.h>
+
+
+static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
+{
+ int err;
+ err = lstat(rpath(fs_ctx, path), stbuf);
+ if (err) {
+ return err;
+ }
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ /* Actual credentials are part of extended attrs */
+ uid_t tmp_uid;
+ gid_t tmp_gid;
+ mode_t tmp_mode;
+ dev_t tmp_dev;
+ if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
+ sizeof(uid_t)) > 0) {
+ stbuf->st_uid = tmp_uid;
+ }
+ if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
+ sizeof(gid_t)) > 0) {
+ stbuf->st_gid = tmp_gid;
+ }
+ if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
+ sizeof(mode_t)) > 0) {
+ stbuf->st_mode = tmp_mode;
+ }
+ if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
+ sizeof(dev_t)) > 0) {
+ stbuf->st_rdev = tmp_dev;
+ }
+ }
+ return err;
+}
+
+static int local_set_xattr(const char *path, FsCred *credp)
+{
+ int err;
+ if (credp->fc_uid != -1) {
+ err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
+ 0);
+ if (err) {
+ return err;
+ }
+ }
+ if (credp->fc_gid != -1) {
+ err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
+ 0);
+ if (err) {
+ return err;
+ }
+ }
+ if (credp->fc_mode != -1) {
+ err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
+ sizeof(mode_t), 0);
+ if (err) {
+ return err;
+ }
+ }
+ if (credp->fc_rdev != -1) {
+ err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
+ sizeof(dev_t), 0);
+ if (err) {
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
+ FsCred *credp)
+{
+ if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
+ return -1;
+ }
+ if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
+ /*
+ * If we fail to change ownership and if we are
+ * using security model none. Ignore the error
+ */
+ if (fs_ctx->fs_sm != SM_NONE) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
+ char *buf, size_t bufsz)
+{
+ ssize_t tsize = -1;
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ int fd;
+ fd = open(rpath(fs_ctx, path), O_RDONLY);
+ if (fd == -1) {
+ return -1;
+ }
+ do {
+ tsize = read(fd, (void *)buf, bufsz);
+ } while (tsize == -1 && errno == EINTR);
+ close(fd);
+ return tsize;
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
+ }
+ return tsize;
+}
+
+static int local_close(FsContext *ctx, int fd)
+{
+ return close(fd);
+}
+
+static int local_closedir(FsContext *ctx, DIR *dir)
+{
+ return closedir(dir);
+}
+
+static int local_open(FsContext *ctx, const char *path, int flags)
+{
+ return open(rpath(ctx, path), flags);
+}
+
+static DIR *local_opendir(FsContext *ctx, const char *path)
+{
+ return opendir(rpath(ctx, path));
+}
+
+static void local_rewinddir(FsContext *ctx, DIR *dir)
+{
+ return rewinddir(dir);
+}
+
+static off_t local_telldir(FsContext *ctx, DIR *dir)
+{
+ return telldir(dir);
+}
+
+static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
+{
+ return readdir(dir);
+}
+
+static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
+{
+ return seekdir(dir, off);
+}
+
+static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+ int iovcnt, off_t offset)
+{
+#ifdef CONFIG_PREADV
+ return preadv(fd, iov, iovcnt, offset);
+#else
+ int err = lseek(fd, offset, SEEK_SET);
+ if (err == -1) {
+ return err;
+ } else {
+ return readv(fd, iov, iovcnt);
+ }
+#endif
+}
+
+static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+ int iovcnt, off_t offset)
+{
+#ifdef CONFIG_PREADV
+ return pwritev(fd, iov, iovcnt, offset);
+#else
+ int err = lseek(fd, offset, SEEK_SET);
+ if (err == -1) {
+ return err;
+ } else {
+ return writev(fd, iov, iovcnt);
+ }
+#endif
+}
+
+static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ return local_set_xattr(rpath(fs_ctx, path), credp);
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ return chmod(rpath(fs_ctx, path), credp->fc_mode);
+ }
+ return -1;
+}
+
+static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+ int err = -1;
+ int serrno = 0;
+
+ /* Determine the security model */
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0);
+ if (err == -1) {
+ return err;
+ }
+ local_set_xattr(rpath(fs_ctx, path), credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev);
+ if (err == -1) {
+ return err;
+ }
+ err = local_post_create_passthrough(fs_ctx, path, credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ }
+ return err;
+
+err_end:
+ remove(rpath(fs_ctx, path));
+ errno = serrno;
+ return err;
+}
+
+static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+ int err = -1;
+ int serrno = 0;
+
+ /* Determine the security model */
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS);
+ if (err == -1) {
+ return err;
+ }
+ credp->fc_mode = credp->fc_mode|S_IFDIR;
+ err = local_set_xattr(rpath(fs_ctx, path), credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
+ if (err == -1) {
+ return err;
+ }
+ err = local_post_create_passthrough(fs_ctx, path, credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ }
+ return err;
+
+err_end:
+ remove(rpath(fs_ctx, path));
+ errno = serrno;
+ return err;
+}
+
+static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
+{
+ int err;
+ err = fstat(fd, stbuf);
+ if (err) {
+ return err;
+ }
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ /* Actual credentials are part of extended attrs */
+ uid_t tmp_uid;
+ gid_t tmp_gid;
+ mode_t tmp_mode;
+ dev_t tmp_dev;
+
+ if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
+ stbuf->st_uid = tmp_uid;
+ }
+ if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
+ stbuf->st_gid = tmp_gid;
+ }
+ if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
+ stbuf->st_mode = tmp_mode;
+ }
+ if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
+ stbuf->st_rdev = tmp_dev;
+ }
+ }
+ return err;
+}
+
+static int local_open2(FsContext *fs_ctx, const char *path, int flags,
+ FsCred *credp)
+{
+ int fd = -1;
+ int err = -1;
+ int serrno = 0;
+
+ /* Determine the security model */
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
+ if (fd == -1) {
+ return fd;
+ }
+ credp->fc_mode = credp->fc_mode|S_IFREG;
+ /* Set cleint credentials in xattr */
+ err = local_set_xattr(rpath(fs_ctx, path), credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
+ if (fd == -1) {
+ return fd;
+ }
+ err = local_post_create_passthrough(fs_ctx, path, credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ }
+ return fd;
+
+err_end:
+ close(fd);
+ remove(rpath(fs_ctx, path));
+ errno = serrno;
+ return err;
+}
+
+
+static int local_symlink(FsContext *fs_ctx, const char *oldpath,
+ const char *newpath, FsCred *credp)
+{
+ int err = -1;
+ int serrno = 0;
+
+ /* Determine the security model */
+ if (fs_ctx->fs_sm == SM_MAPPED) {
+ int fd;
+ ssize_t oldpath_size, write_size;
+ fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR,
+ SM_LOCAL_MODE_BITS);
+ if (fd == -1) {
+ return fd;
+ }
+ /* Write the oldpath (target) to the file. */
+ oldpath_size = strlen(oldpath) + 1;
+ do {
+ write_size = write(fd, (void *)oldpath, oldpath_size);
+ } while (write_size == -1 && errno == EINTR);
+
+ if (write_size != oldpath_size) {
+ serrno = errno;
+ close(fd);
+ err = -1;
+ goto err_end;
+ }
+ close(fd);
+ /* Set cleint credentials in symlink's xattr */
+ credp->fc_mode = credp->fc_mode|S_IFLNK;
+ err = local_set_xattr(rpath(fs_ctx, newpath), credp);
+ if (err == -1) {
+ serrno = errno;
+ goto err_end;
+ }
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ err = symlink(oldpath, rpath(fs_ctx, newpath));
+ if (err) {
+ return err;
+ }
+ err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
+ if (err == -1) {
+ /*
+ * If we fail to change ownership and if we are
+ * using security model none. Ignore the error
+ */
+ if (fs_ctx->fs_sm != SM_NONE) {
+ serrno = errno;
+ goto err_end;
+ } else
+ err = 0;
+ }
+ }
+ return err;
+
+err_end:
+ remove(rpath(fs_ctx, newpath));
+ errno = serrno;
+ return err;
+}
+
+static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
+{
+ char *tmp = qemu_strdup(rpath(ctx, oldpath));
+ int err, serrno = 0;
+
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ err = link(tmp, rpath(ctx, newpath));
+ if (err == -1) {
+ serrno = errno;
+ }
+
+ qemu_free(tmp);
+
+ if (err == -1) {
+ errno = serrno;
+ }
+
+ return err;
+}
+
+static int local_truncate(FsContext *ctx, const char *path, off_t size)
+{
+ return truncate(rpath(ctx, path), size);
+}
+
+static int local_rename(FsContext *ctx, const char *oldpath,
+ const char *newpath)
+{
+ char *tmp;
+ int err;
+
+ tmp = qemu_strdup(rpath(ctx, oldpath));
+
+ err = rename(tmp, rpath(ctx, newpath));
+ if (err == -1) {
+ int serrno = errno;
+ qemu_free(tmp);
+ errno = serrno;
+ } else {
+ qemu_free(tmp);
+ }
+
+ return err;
+
+}
+
+static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
+{
+ if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+ (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
+ return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+ } else if (fs_ctx->fs_sm == SM_MAPPED) {
+ return local_set_xattr(rpath(fs_ctx, path), credp);
+ } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
+ (fs_ctx->fs_sm == SM_NONE)) {
+ return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
+ }
+ return -1;
+}
+
+static int local_utimensat(FsContext *s, const char *path,
+ const struct timespec *buf)
+{
+ return qemu_utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW);
+}
+
+static int local_remove(FsContext *ctx, const char *path)
+{
+ return remove(rpath(ctx, path));
+}
+
+static int local_fsync(FsContext *ctx, int fd, int datasync)
+{
+ if (datasync) {
+ return qemu_fdatasync(fd);
+ } else {
+ return fsync(fd);
+ }
+}
+
+static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf)
+{
+ return statfs(rpath(s, path), stbuf);
+}
+
+static ssize_t local_lgetxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return v9fs_get_xattr(ctx, path, name, value, size);
+}
+
+static ssize_t local_llistxattr(FsContext *ctx, const char *path,
+ void *value, size_t size)
+{
+ return v9fs_list_xattr(ctx, path, value, size);
+}
+
+static int local_lsetxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ return v9fs_set_xattr(ctx, path, name, value, size, flags);
+}
+
+static int local_lremovexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ return v9fs_remove_xattr(ctx, path, name);
+}
+
+
+FileOperations local_ops = {
+ .lstat = local_lstat,
+ .readlink = local_readlink,
+ .close = local_close,
+ .closedir = local_closedir,
+ .open = local_open,
+ .opendir = local_opendir,
+ .rewinddir = local_rewinddir,
+ .telldir = local_telldir,
+ .readdir = local_readdir,
+ .seekdir = local_seekdir,
+ .preadv = local_preadv,
+ .pwritev = local_pwritev,
+ .chmod = local_chmod,
+ .mknod = local_mknod,
+ .mkdir = local_mkdir,
+ .fstat = local_fstat,
+ .open2 = local_open2,
+ .symlink = local_symlink,
+ .link = local_link,
+ .truncate = local_truncate,
+ .rename = local_rename,
+ .chown = local_chown,
+ .utimensat = local_utimensat,
+ .remove = local_remove,
+ .fsync = local_fsync,
+ .statfs = local_statfs,
+ .lgetxattr = local_lgetxattr,
+ .llistxattr = local_llistxattr,
+ .lsetxattr = local_lsetxattr,
+ .lremovexattr = local_lremovexattr,
+};
diff --git a/hw/virtio-9p-posix-acl.c b/hw/virtio-9p-posix-acl.c
new file mode 100644
index 0000000..3978d0c
--- /dev/null
+++ b/hw/virtio-9p-posix-acl.c
@@ -0,0 +1,140 @@
+/*
+ * Virtio 9p system.posix* xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
+#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
+#define ACL_ACCESS "system.posix_acl_access"
+#define ACL_DEFAULT "system.posix_acl_default"
+
+static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return lgetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size);
+}
+
+static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t osize)
+{
+ ssize_t len = sizeof(ACL_ACCESS);
+
+ if (!value) {
+ return len;
+ }
+
+ if (osize < len) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, ACL_ACCESS, len);
+ return 0;
+}
+
+static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ return lsetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size, flags);
+}
+
+static int mp_pacl_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ int ret;
+ ret = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS);
+ if (ret == -1 && errno == ENODATA) {
+ /*
+ * We don't get ENODATA error when trying to remote a
+ * posix acl that is not present. So don't throw the error
+ * even in case of mapped security model
+ */
+ errno = 0;
+ ret = 0;
+ }
+ return ret;
+}
+
+static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return lgetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size);
+}
+
+static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t osize)
+{
+ ssize_t len = sizeof(ACL_DEFAULT);
+
+ if (!value) {
+ return len;
+ }
+
+ if (osize < len) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, ACL_DEFAULT, len);
+ return 0;
+}
+
+static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ return lsetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size, flags);
+}
+
+static int mp_dacl_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ return lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT);
+}
+
+
+XattrOperations mapped_pacl_xattr = {
+ .name = "system.posix_acl_access",
+ .getxattr = mp_pacl_getxattr,
+ .setxattr = mp_pacl_setxattr,
+ .listxattr = mp_pacl_listxattr,
+ .removexattr = mp_pacl_removexattr,
+};
+
+XattrOperations mapped_dacl_xattr = {
+ .name = "system.posix_acl_default",
+ .getxattr = mp_dacl_getxattr,
+ .setxattr = mp_dacl_setxattr,
+ .listxattr = mp_dacl_listxattr,
+ .removexattr = mp_dacl_removexattr,
+};
+
+XattrOperations passthrough_acl_xattr = {
+ .name = "system.posix_acl_",
+ .getxattr = pt_getxattr,
+ .setxattr = pt_setxattr,
+ .listxattr = pt_listxattr,
+ .removexattr = pt_removexattr,
+};
+
+XattrOperations none_acl_xattr = {
+ .name = "system.posix_acl_",
+ .getxattr = notsup_getxattr,
+ .setxattr = notsup_setxattr,
+ .listxattr = notsup_listxattr,
+ .removexattr = notsup_removexattr,
+};
diff --git a/hw/virtio-9p-xattr-user.c b/hw/virtio-9p-xattr-user.c
new file mode 100644
index 0000000..faa02a1
--- /dev/null
+++ b/hw/virtio-9p-xattr-user.c
@@ -0,0 +1,109 @@
+/*
+ * Virtio 9p user. xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+
+static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = ENOATTR;
+ return -1;
+ }
+ return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size)
+{
+ int name_size = strlen(name) + 1;
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+
+ /* check if it is a mapped posix acl */
+ if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) {
+ /* adjust the name and size */
+ name += 12;
+ name_size -= 12;
+ } else {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ return 0;
+ }
+ }
+ if (!value) {
+ return name_size;
+ }
+
+ if (size < name_size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, name, name_size);
+ return name_size;
+}
+
+static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = EACCES;
+ return -1;
+ }
+ return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static int mp_user_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ if (strncmp(name, "user.virtfs.", 12) == 0) {
+ /*
+ * Don't allow fetch of user.virtfs namesapce
+ * in case of mapped security
+ */
+ errno = EACCES;
+ return -1;
+ }
+ return lremovexattr(rpath(ctx, path), name);
+}
+
+XattrOperations mapped_user_xattr = {
+ .name = "user.",
+ .getxattr = mp_user_getxattr,
+ .setxattr = mp_user_setxattr,
+ .listxattr = mp_user_listxattr,
+ .removexattr = mp_user_removexattr,
+};
+
+XattrOperations passthrough_user_xattr = {
+ .name = "user.",
+ .getxattr = pt_getxattr,
+ .setxattr = pt_setxattr,
+ .listxattr = pt_listxattr,
+ .removexattr = pt_removexattr,
+};
diff --git a/hw/virtio-9p-xattr.c b/hw/virtio-9p-xattr.c
new file mode 100644
index 0000000..1aab081
--- /dev/null
+++ b/hw/virtio-9p-xattr.c
@@ -0,0 +1,159 @@
+/*
+ * Virtio 9p xattr callback
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio.h"
+#include "virtio-9p.h"
+#include "file-op-9p.h"
+#include "virtio-9p-xattr.h"
+
+
+static XattrOperations *get_xattr_operations(XattrOperations **h,
+ const char *name)
+{
+ XattrOperations *xops;
+ for (xops = *(h)++; xops != NULL; xops = *(h)++) {
+ if (!strncmp(name, xops->name, strlen(xops->name))) {
+ return xops;
+ }
+ }
+ return NULL;
+}
+
+ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+ if (xops) {
+ return xops->getxattr(ctx, path, name, value, size);
+ }
+ errno = -EOPNOTSUPP;
+ return -1;
+}
+
+ssize_t pt_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size)
+{
+ int name_size = strlen(name) + 1;
+ if (!value) {
+ return name_size;
+ }
+
+ if (size < name_size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ strncpy(value, name, name_size);
+ return name_size;
+}
+
+
+/*
+ * Get the list and pass to each layer to find out whether
+ * to send the data or not
+ */
+ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+ void *value, size_t vsize)
+{
+ ssize_t size = 0;
+ void *ovalue = value;
+ XattrOperations *xops;
+ char *orig_value, *orig_value_start;
+ ssize_t xattr_len, parsed_len = 0, attr_len;
+
+ /* Get the actual len */
+ xattr_len = llistxattr(rpath(ctx, path), value, 0);
+ if (xattr_len <= 0) {
+ return xattr_len;
+ }
+
+ /* Now fetch the xattr and find the actual size */
+ orig_value = qemu_malloc(xattr_len);
+ xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len);
+
+ /* store the orig pointer */
+ orig_value_start = orig_value;
+ while (xattr_len > parsed_len) {
+ xops = get_xattr_operations(ctx->xops, orig_value);
+ if (!xops) {
+ goto next_entry;
+ }
+
+ if (!value) {
+ size += xops->listxattr(ctx, path, orig_value, value, vsize);
+ } else {
+ size = xops->listxattr(ctx, path, orig_value, value, vsize);
+ if (size < 0) {
+ goto err_out;
+ }
+ value += size;
+ vsize -= size;
+ }
+next_entry:
+ /* Got the next entry */
+ attr_len = strlen(orig_value) + 1;
+ parsed_len += attr_len;
+ orig_value += attr_len;
+ }
+ if (value) {
+ size = value - ovalue;
+ }
+
+err_out:
+ qemu_free(orig_value_start);
+ return size;
+}
+
+int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags)
+{
+ XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+ if (xops) {
+ return xops->setxattr(ctx, path, name, value, size, flags);
+ }
+ errno = -EOPNOTSUPP;
+ return -1;
+
+}
+
+int v9fs_remove_xattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+ if (xops) {
+ return xops->removexattr(ctx, path, name);
+ }
+ errno = -EOPNOTSUPP;
+ return -1;
+
+}
+
+XattrOperations *mapped_xattr_ops[] = {
+ &mapped_user_xattr,
+ &mapped_pacl_xattr,
+ &mapped_dacl_xattr,
+ NULL,
+};
+
+XattrOperations *passthrough_xattr_ops[] = {
+ &passthrough_user_xattr,
+ &passthrough_acl_xattr,
+ NULL,
+};
+
+/* for .user none model should be same as passthrough */
+XattrOperations *none_xattr_ops[] = {
+ &passthrough_user_xattr,
+ &none_acl_xattr,
+ NULL,
+};
diff --git a/hw/virtio-9p-xattr.h b/hw/virtio-9p-xattr.h
new file mode 100644
index 0000000..2bbae2d
--- /dev/null
+++ b/hw/virtio-9p-xattr.h
@@ -0,0 +1,102 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef _QEMU_VIRTIO_9P_XATTR_H
+#define _QEMU_VIRTIO_9P_XATTR_H
+
+#include <attr/xattr.h>
+
+typedef struct xattr_operations
+{
+ const char *name;
+ ssize_t (*getxattr)(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size);
+ ssize_t (*listxattr)(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size);
+ int (*setxattr)(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags);
+ int (*removexattr)(FsContext *ctx,
+ const char *path, const char *name);
+} XattrOperations;
+
+
+extern XattrOperations mapped_user_xattr;
+extern XattrOperations passthrough_user_xattr;
+
+extern XattrOperations mapped_pacl_xattr;
+extern XattrOperations mapped_dacl_xattr;
+extern XattrOperations passthrough_acl_xattr;
+extern XattrOperations none_acl_xattr;
+
+extern XattrOperations *mapped_xattr_ops[];
+extern XattrOperations *passthrough_xattr_ops[];
+extern XattrOperations *none_xattr_ops[];
+
+ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size);
+ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value,
+ size_t vsize);
+int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+ void *value, size_t size, int flags);
+int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name);
+ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value,
+ size_t size);
+
+static inline ssize_t pt_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value, size_t size)
+{
+ return lgetxattr(rpath(ctx, path), name, value, size);
+}
+
+static inline int pt_setxattr(FsContext *ctx, const char *path,
+ const char *name, void *value,
+ size_t size, int flags)
+{
+ return lsetxattr(rpath(ctx, path), name, value, size, flags);
+}
+
+static inline int pt_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ return lremovexattr(rpath(ctx, path), name);
+}
+
+static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path,
+ const char *name, void *value,
+ size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static inline int notsup_setxattr(FsContext *ctx, const char *path,
+ const char *name, void *value,
+ size_t size, int flags)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path,
+ char *name, void *value, size_t size)
+{
+ return 0;
+}
+
+static inline int notsup_removexattr(FsContext *ctx,
+ const char *path, const char *name)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+#endif
diff --git a/hw/virtio-9p.c b/hw/virtio-9p.c
new file mode 100644
index 0000000..7c59988
--- /dev/null
+++ b/hw/virtio-9p.c
@@ -0,0 +1,3744 @@
+/*
+ * Virtio 9p backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "virtio.h"
+#include "pc.h"
+#include "qemu_socket.h"
+#include "virtio-9p.h"
+#include "fsdev/qemu-fsdev.h"
+#include "virtio-9p-debug.h"
+#include "virtio-9p-xattr.h"
+
+int debug_9p_pdu;
+
+enum {
+ Oread = 0x00,
+ Owrite = 0x01,
+ Ordwr = 0x02,
+ Oexec = 0x03,
+ Oexcl = 0x04,
+ Otrunc = 0x10,
+ Orexec = 0x20,
+ Orclose = 0x40,
+ Oappend = 0x80,
+};
+
+static int omode_to_uflags(int8_t mode)
+{
+ int ret = 0;
+
+ switch (mode & 3) {
+ case Oread:
+ ret = O_RDONLY;
+ break;
+ case Ordwr:
+ ret = O_RDWR;
+ break;
+ case Owrite:
+ ret = O_WRONLY;
+ break;
+ case Oexec:
+ ret = O_RDONLY;
+ break;
+ }
+
+ if (mode & Otrunc) {
+ ret |= O_TRUNC;
+ }
+
+ if (mode & Oappend) {
+ ret |= O_APPEND;
+ }
+
+ if (mode & Oexcl) {
+ ret |= O_EXCL;
+ }
+
+ return ret;
+}
+
+void cred_init(FsCred *credp)
+{
+ credp->fc_uid = -1;
+ credp->fc_gid = -1;
+ credp->fc_mode = -1;
+ credp->fc_rdev = -1;
+}
+
+static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
+{
+ return s->ops->lstat(&s->ctx, path->data, stbuf);
+}
+
+static ssize_t v9fs_do_readlink(V9fsState *s, V9fsString *path, V9fsString *buf)
+{
+ ssize_t len;
+
+ buf->data = qemu_malloc(1024);
+
+ len = s->ops->readlink(&s->ctx, path->data, buf->data, 1024 - 1);
+ if (len > -1) {
+ buf->size = len;
+ buf->data[len] = 0;
+ }
+
+ return len;
+}
+
+static int v9fs_do_close(V9fsState *s, int fd)
+{
+ return s->ops->close(&s->ctx, fd);
+}
+
+static int v9fs_do_closedir(V9fsState *s, DIR *dir)
+{
+ return s->ops->closedir(&s->ctx, dir);
+}
+
+static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags)
+{
+ return s->ops->open(&s->ctx, path->data, flags);
+}
+
+static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path)
+{
+ return s->ops->opendir(&s->ctx, path->data);
+}
+
+static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
+{
+ return s->ops->rewinddir(&s->ctx, dir);
+}
+
+static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
+{
+ return s->ops->telldir(&s->ctx, dir);
+}
+
+static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir)
+{
+ return s->ops->readdir(&s->ctx, dir);
+}
+
+static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
+{
+ return s->ops->seekdir(&s->ctx, dir, off);
+}
+
+static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov,
+ int iovcnt, int64_t offset)
+{
+ return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
+}
+
+static int v9fs_do_pwritev(V9fsState *s, int fd, const struct iovec *iov,
+ int iovcnt, int64_t offset)
+{
+ return s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
+}
+
+static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
+{
+ FsCred cred;
+ cred_init(&cred);
+ cred.fc_mode = mode;
+ return s->ops->chmod(&s->ctx, path->data, &cred);
+}
+
+static int v9fs_do_mknod(V9fsState *s, char *name,
+ mode_t mode, dev_t dev, uid_t uid, gid_t gid)
+{
+ FsCred cred;
+ cred_init(&cred);
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode;
+ cred.fc_rdev = dev;
+ return s->ops->mknod(&s->ctx, name, &cred);
+}
+
+static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
+ uid_t uid, gid_t gid)
+{
+ FsCred cred;
+
+ cred_init(&cred);
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode;
+
+ return s->ops->mkdir(&s->ctx, name, &cred);
+}
+
+static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
+{
+ return s->ops->fstat(&s->ctx, fd, stbuf);
+}
+
+static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
+ int flags, int mode)
+{
+ FsCred cred;
+
+ cred_init(&cred);
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = mode & 07777;
+ flags = flags;
+
+ return s->ops->open2(&s->ctx, fullname, flags, &cred);
+}
+
+static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
+ const char *oldpath, const char *newpath, gid_t gid)
+{
+ FsCred cred;
+ cred_init(&cred);
+ cred.fc_uid = fidp->uid;
+ cred.fc_gid = gid;
+ cred.fc_mode = 0777;
+
+ return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
+}
+
+static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
+{
+ return s->ops->link(&s->ctx, oldpath->data, newpath->data);
+}
+
+static int v9fs_do_truncate(V9fsState *s, V9fsString *path, off_t size)
+{
+ return s->ops->truncate(&s->ctx, path->data, size);
+}
+
+static int v9fs_do_rename(V9fsState *s, V9fsString *oldpath,
+ V9fsString *newpath)
+{
+ return s->ops->rename(&s->ctx, oldpath->data, newpath->data);
+}
+
+static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
+{
+ FsCred cred;
+ cred_init(&cred);
+ cred.fc_uid = uid;
+ cred.fc_gid = gid;
+
+ return s->ops->chown(&s->ctx, path->data, &cred);
+}
+
+static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
+ const struct timespec times[2])
+{
+ return s->ops->utimensat(&s->ctx, path->data, times);
+}
+
+static int v9fs_do_remove(V9fsState *s, V9fsString *path)
+{
+ return s->ops->remove(&s->ctx, path->data);
+}
+
+static int v9fs_do_fsync(V9fsState *s, int fd, int datasync)
+{
+ return s->ops->fsync(&s->ctx, fd, datasync);
+}
+
+static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
+{
+ return s->ops->statfs(&s->ctx, path->data, stbuf);
+}
+
+static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name,
+ void *value, size_t size)
+{
+ return s->ops->lgetxattr(&s->ctx, path->data,
+ xattr_name->data, value, size);
+}
+
+static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
+ void *value, size_t size)
+{
+ return s->ops->llistxattr(&s->ctx, path->data,
+ value, size);
+}
+
+static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name,
+ void *value, size_t size, int flags)
+{
+ return s->ops->lsetxattr(&s->ctx, path->data,
+ xattr_name->data, value, size, flags);
+}
+
+static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path,
+ V9fsString *xattr_name)
+{
+ return s->ops->lremovexattr(&s->ctx, path->data,
+ xattr_name->data);
+}
+
+
+static void v9fs_string_init(V9fsString *str)
+{
+ str->data = NULL;
+ str->size = 0;
+}
+
+static void v9fs_string_free(V9fsString *str)
+{
+ qemu_free(str->data);
+ str->data = NULL;
+ str->size = 0;
+}
+
+static void v9fs_string_null(V9fsString *str)
+{
+ v9fs_string_free(str);
+}
+
+static int number_to_string(void *arg, char type)
+{
+ unsigned int ret = 0;
+
+ switch (type) {
+ case 'u': {
+ unsigned int num = *(unsigned int *)arg;
+
+ do {
+ ret++;
+ num = num/10;
+ } while (num);
+ break;
+ }
+ case 'U': {
+ unsigned long num = *(unsigned long *)arg;
+ do {
+ ret++;
+ num = num/10;
+ } while (num);
+ break;
+ }
+ default:
+ printf("Number_to_string: Unknown number format\n");
+ return -1;
+ }
+
+ return ret;
+}
+
+static int GCC_FMT_ATTR(2, 0)
+v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
+{
+ va_list ap2;
+ char *iter = (char *)fmt;
+ int len = 0;
+ int nr_args = 0;
+ char *arg_char_ptr;
+ unsigned int arg_uint;
+ unsigned long arg_ulong;
+
+ /* Find the number of %'s that denotes an argument */
+ for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
+ nr_args++;
+ iter++;
+ }
+
+ len = strlen(fmt) - 2*nr_args;
+
+ if (!nr_args) {
+ goto alloc_print;
+ }
+
+ va_copy(ap2, ap);
+
+ iter = (char *)fmt;
+
+ /* Now parse the format string */
+ for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
+ iter++;
+ switch (*iter) {
+ case 'u':
+ arg_uint = va_arg(ap2, unsigned int);
+ len += number_to_string((void *)&arg_uint, 'u');
+ break;
+ case 'l':
+ if (*++iter == 'u') {
+ arg_ulong = va_arg(ap2, unsigned long);
+ len += number_to_string((void *)&arg_ulong, 'U');
+ } else {
+ return -1;
+ }
+ break;
+ case 's':
+ arg_char_ptr = va_arg(ap2, char *);
+ len += strlen(arg_char_ptr);
+ break;
+ case 'c':
+ len += 1;
+ break;
+ default:
+ fprintf(stderr,
+ "v9fs_string_alloc_printf:Incorrect format %c", *iter);
+ return -1;
+ }
+ iter++;
+ }
+
+alloc_print:
+ *strp = qemu_malloc((len + 1) * sizeof(**strp));
+
+ return vsprintf(*strp, fmt, ap);
+}
+
+static void GCC_FMT_ATTR(2, 3)
+v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ v9fs_string_free(str);
+
+ va_start(ap, fmt);
+ err = v9fs_string_alloc_printf(&str->data, fmt, ap);
+ BUG_ON(err == -1);
+ va_end(ap);
+
+ str->size = err;
+}
+
+static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
+{
+ v9fs_string_free(lhs);
+ v9fs_string_sprintf(lhs, "%s", rhs->data);
+}
+
+static size_t v9fs_string_size(V9fsString *str)
+{
+ return str->size;
+}
+
+static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
+{
+ V9fsFidState *f;
+
+ for (f = s->fid_list; f; f = f->next) {
+ if (f->fid == fid) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
+{
+ V9fsFidState *f;
+
+ f = lookup_fid(s, fid);
+ if (f) {
+ return NULL;
+ }
+
+ f = qemu_mallocz(sizeof(V9fsFidState));
+
+ f->fid = fid;
+ f->fid_type = P9_FID_NONE;
+
+ f->next = s->fid_list;
+ s->fid_list = f;
+
+ return f;
+}
+
+static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
+{
+ int retval = 0;
+
+ if (fidp->fs.xattr.copied_len == -1) {
+ /* getxattr/listxattr fid */
+ goto free_value;
+ }
+ /*
+ * if this is fid for setxattr. clunk should
+ * result in setxattr localcall
+ */
+ if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
+ /* clunk after partial write */
+ retval = -EINVAL;
+ goto free_out;
+ }
+ if (fidp->fs.xattr.len) {
+ retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
+ fidp->fs.xattr.value,
+ fidp->fs.xattr.len,
+ fidp->fs.xattr.flags);
+ } else {
+ retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
+ }
+free_out:
+ v9fs_string_free(&fidp->fs.xattr.name);
+free_value:
+ if (fidp->fs.xattr.value) {
+ qemu_free(fidp->fs.xattr.value);
+ }
+ return retval;
+}
+
+static int free_fid(V9fsState *s, int32_t fid)
+{
+ int retval = 0;
+ V9fsFidState **fidpp, *fidp;
+
+ for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
+ if ((*fidpp)->fid == fid) {
+ break;
+ }
+ }
+
+ if (*fidpp == NULL) {
+ return -ENOENT;
+ }
+
+ fidp = *fidpp;
+ *fidpp = fidp->next;
+
+ if (fidp->fid_type == P9_FID_FILE) {
+ v9fs_do_close(s, fidp->fs.fd);
+ } else if (fidp->fid_type == P9_FID_DIR) {
+ v9fs_do_closedir(s, fidp->fs.dir);
+ } else if (fidp->fid_type == P9_FID_XATTR) {
+ retval = v9fs_xattr_fid_clunk(s, fidp);
+ }
+ v9fs_string_free(&fidp->path);
+ qemu_free(fidp);
+
+ return retval;
+}
+
+#define P9_QID_TYPE_DIR 0x80
+#define P9_QID_TYPE_SYMLINK 0x02
+
+#define P9_STAT_MODE_DIR 0x80000000
+#define P9_STAT_MODE_APPEND 0x40000000
+#define P9_STAT_MODE_EXCL 0x20000000
+#define P9_STAT_MODE_MOUNT 0x10000000
+#define P9_STAT_MODE_AUTH 0x08000000
+#define P9_STAT_MODE_TMP 0x04000000
+#define P9_STAT_MODE_SYMLINK 0x02000000
+#define P9_STAT_MODE_LINK 0x01000000
+#define P9_STAT_MODE_DEVICE 0x00800000
+#define P9_STAT_MODE_NAMED_PIPE 0x00200000
+#define P9_STAT_MODE_SOCKET 0x00100000
+#define P9_STAT_MODE_SETUID 0x00080000
+#define P9_STAT_MODE_SETGID 0x00040000
+#define P9_STAT_MODE_SETVTX 0x00010000
+
+#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \
+ P9_STAT_MODE_SYMLINK | \
+ P9_STAT_MODE_LINK | \
+ P9_STAT_MODE_DEVICE | \
+ P9_STAT_MODE_NAMED_PIPE | \
+ P9_STAT_MODE_SOCKET)
+
+/* This is the algorithm from ufs in spfs */
+static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
+{
+ size_t size;
+
+ size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
+ memcpy(&qidp->path, &stbuf->st_ino, size);
+ qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
+ qidp->type = 0;
+ if (S_ISDIR(stbuf->st_mode)) {
+ qidp->type |= P9_QID_TYPE_DIR;
+ }
+ if (S_ISLNK(stbuf->st_mode)) {
+ qidp->type |= P9_QID_TYPE_SYMLINK;
+ }
+}
+
+static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
+{
+ struct stat stbuf;
+ int err;
+
+ err = v9fs_do_lstat(s, &fidp->path, &stbuf);
+ if (err) {
+ return err;
+ }
+
+ stat_to_qid(&stbuf, qidp);
+ return 0;
+}
+
+static V9fsPDU *alloc_pdu(V9fsState *s)
+{
+ V9fsPDU *pdu = NULL;
+
+ if (!QLIST_EMPTY(&s->free_list)) {
+ pdu = QLIST_FIRST(&s->free_list);
+ QLIST_REMOVE(pdu, next);
+ }
+ return pdu;
+}
+
+static void free_pdu(V9fsState *s, V9fsPDU *pdu)
+{
+ if (pdu) {
+ QLIST_INSERT_HEAD(&s->free_list, pdu, next);
+ }
+}
+
+size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
+ size_t offset, size_t size, int pack)
+{
+ int i = 0;
+ size_t copied = 0;
+
+ for (i = 0; size && i < sg_count; i++) {
+ size_t len;
+ if (offset >= sg[i].iov_len) {
+ /* skip this sg */
+ offset -= sg[i].iov_len;
+ continue;
+ } else {
+ len = MIN(sg[i].iov_len - offset, size);
+ if (pack) {
+ memcpy(sg[i].iov_base + offset, addr, len);
+ } else {
+ memcpy(addr, sg[i].iov_base + offset, len);
+ }
+ size -= len;
+ copied += len;
+ addr += len;
+ if (size) {
+ offset = 0;
+ continue;
+ }
+ }
+ }
+
+ return copied;
+}
+
+static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
+{
+ return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
+ offset, size, 0);
+}
+
+static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
+ size_t size)
+{
+ return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
+ offset, size, 1);
+}
+
+static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
+{
+ size_t pos = 0;
+ int i, j;
+ struct iovec *src_sg;
+ unsigned int num;
+
+ if (rx) {
+ src_sg = pdu->elem.in_sg;
+ num = pdu->elem.in_num;
+ } else {
+ src_sg = pdu->elem.out_sg;
+ num = pdu->elem.out_num;
+ }
+
+ j = 0;
+ for (i = 0; i < num; i++) {
+ if (offset <= pos) {
+ sg[j].iov_base = src_sg[i].iov_base;
+ sg[j].iov_len = src_sg[i].iov_len;
+ j++;
+ } else if (offset < (src_sg[i].iov_len + pos)) {
+ sg[j].iov_base = src_sg[i].iov_base;
+ sg[j].iov_len = src_sg[i].iov_len;
+ sg[j].iov_base += (offset - pos);
+ sg[j].iov_len -= (offset - pos);
+ j++;
+ }
+ pos += src_sg[i].iov_len;
+ }
+
+ return j;
+}
+
+static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
+{
+ size_t old_offset = offset;
+ va_list ap;
+ int i;
+
+ va_start(ap, fmt);
+ for (i = 0; fmt[i]; i++) {
+ switch (fmt[i]) {
+ case 'b': {
+ uint8_t *valp = va_arg(ap, uint8_t *);
+ offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
+ break;
+ }
+ case 'w': {
+ uint16_t val, *valp;
+ valp = va_arg(ap, uint16_t *);
+ val = le16_to_cpupu(valp);
+ offset += pdu_unpack(&val, pdu, offset, sizeof(val));
+ *valp = val;
+ break;
+ }
+ case 'd': {
+ uint32_t val, *valp;
+ valp = va_arg(ap, uint32_t *);
+ val = le32_to_cpupu(valp);
+ offset += pdu_unpack(&val, pdu, offset, sizeof(val));
+ *valp = val;
+ break;
+ }
+ case 'q': {
+ uint64_t val, *valp;
+ valp = va_arg(ap, uint64_t *);
+ val = le64_to_cpup(valp);
+ offset += pdu_unpack(&val, pdu, offset, sizeof(val));
+ *valp = val;
+ break;
+ }
+ case 'v': {
+ struct iovec *iov = va_arg(ap, struct iovec *);
+ int *iovcnt = va_arg(ap, int *);
+ *iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
+ break;
+ }
+ case 's': {
+ V9fsString *str = va_arg(ap, V9fsString *);
+ offset += pdu_unmarshal(pdu, offset, "w", &str->size);
+ /* FIXME: sanity check str->size */
+ str->data = qemu_malloc(str->size + 1);
+ offset += pdu_unpack(str->data, pdu, offset, str->size);
+ str->data[str->size] = 0;
+ break;
+ }
+ case 'Q': {
+ V9fsQID *qidp = va_arg(ap, V9fsQID *);
+ offset += pdu_unmarshal(pdu, offset, "bdq",
+ &qidp->type, &qidp->version, &qidp->path);
+ break;
+ }
+ case 'S': {
+ V9fsStat *statp = va_arg(ap, V9fsStat *);
+ offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
+ &statp->size, &statp->type, &statp->dev,
+ &statp->qid, &statp->mode, &statp->atime,
+ &statp->mtime, &statp->length,
+ &statp->name, &statp->uid, &statp->gid,
+ &statp->muid, &statp->extension,
+ &statp->n_uid, &statp->n_gid,
+ &statp->n_muid);
+ break;
+ }
+ case 'I': {
+ V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
+ offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
+ &iattr->valid, &iattr->mode,
+ &iattr->uid, &iattr->gid, &iattr->size,
+ &iattr->atime_sec, &iattr->atime_nsec,
+ &iattr->mtime_sec, &iattr->mtime_nsec);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ va_end(ap);
+
+ return offset - old_offset;
+}
+
+static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
+{
+ size_t old_offset = offset;
+ va_list ap;
+ int i;
+
+ va_start(ap, fmt);
+ for (i = 0; fmt[i]; i++) {
+ switch (fmt[i]) {
+ case 'b': {
+ uint8_t val = va_arg(ap, int);
+ offset += pdu_pack(pdu, offset, &val, sizeof(val));
+ break;
+ }
+ case 'w': {
+ uint16_t val;
+ cpu_to_le16w(&val, va_arg(ap, int));
+ offset += pdu_pack(pdu, offset, &val, sizeof(val));
+ break;
+ }
+ case 'd': {
+ uint32_t val;
+ cpu_to_le32w(&val, va_arg(ap, uint32_t));
+ offset += pdu_pack(pdu, offset, &val, sizeof(val));
+ break;
+ }
+ case 'q': {
+ uint64_t val;
+ cpu_to_le64w(&val, va_arg(ap, uint64_t));
+ offset += pdu_pack(pdu, offset, &val, sizeof(val));
+ break;
+ }
+ case 'v': {
+ struct iovec *iov = va_arg(ap, struct iovec *);
+ int *iovcnt = va_arg(ap, int *);
+ *iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
+ break;
+ }
+ case 's': {
+ V9fsString *str = va_arg(ap, V9fsString *);
+ offset += pdu_marshal(pdu, offset, "w", str->size);
+ offset += pdu_pack(pdu, offset, str->data, str->size);
+ break;
+ }
+ case 'Q': {
+ V9fsQID *qidp = va_arg(ap, V9fsQID *);
+ offset += pdu_marshal(pdu, offset, "bdq",
+ qidp->type, qidp->version, qidp->path);
+ break;
+ }
+ case 'S': {
+ V9fsStat *statp = va_arg(ap, V9fsStat *);
+ offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
+ statp->size, statp->type, statp->dev,
+ &statp->qid, statp->mode, statp->atime,
+ statp->mtime, statp->length, &statp->name,
+ &statp->uid, &statp->gid, &statp->muid,
+ &statp->extension, statp->n_uid,
+ statp->n_gid, statp->n_muid);
+ break;
+ }
+ case 'A': {
+ V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
+ offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
+ statp->st_result_mask,
+ &statp->qid, statp->st_mode,
+ statp->st_uid, statp->st_gid,
+ statp->st_nlink, statp->st_rdev,
+ statp->st_size, statp->st_blksize, statp->st_blocks,
+ statp->st_atime_sec, statp->st_atime_nsec,
+ statp->st_mtime_sec, statp->st_mtime_nsec,
+ statp->st_ctime_sec, statp->st_ctime_nsec,
+ statp->st_btime_sec, statp->st_btime_nsec,
+ statp->st_gen, statp->st_data_version);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ va_end(ap);
+
+ return offset - old_offset;
+}
+
+static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
+{
+ int8_t id = pdu->id + 1; /* Response */
+
+ if (len < 0) {
+ int err = -len;
+ len = 7;
+
+ if (s->proto_version != V9FS_PROTO_2000L) {
+ V9fsString str;
+
+ str.data = strerror(err);
+ str.size = strlen(str.data);
+
+ len += pdu_marshal(pdu, len, "s", &str);
+ id = P9_RERROR;
+ }
+
+ len += pdu_marshal(pdu, len, "d", err);
+
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ id = P9_RLERROR;
+ }
+ }
+
+ /* fill out the header */
+ pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
+
+ /* keep these in sync */
+ pdu->size = len;
+ pdu->id = id;
+
+ /* push onto queue and notify */
+ virtqueue_push(s->vq, &pdu->elem, len);
+
+ /* FIXME: we should batch these completions */
+ virtio_notify(&s->vdev, s->vq);
+
+ free_pdu(s, pdu);
+}
+
+static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
+{
+ mode_t ret;
+
+ ret = mode & 0777;
+ if (mode & P9_STAT_MODE_DIR) {
+ ret |= S_IFDIR;
+ }
+
+ if (mode & P9_STAT_MODE_SYMLINK) {
+ ret |= S_IFLNK;
+ }
+ if (mode & P9_STAT_MODE_SOCKET) {
+ ret |= S_IFSOCK;
+ }
+ if (mode & P9_STAT_MODE_NAMED_PIPE) {
+ ret |= S_IFIFO;
+ }
+ if (mode & P9_STAT_MODE_DEVICE) {
+ if (extension && extension->data[0] == 'c') {
+ ret |= S_IFCHR;
+ } else {
+ ret |= S_IFBLK;
+ }
+ }
+
+ if (!(ret&~0777)) {
+ ret |= S_IFREG;
+ }
+
+ if (mode & P9_STAT_MODE_SETUID) {
+ ret |= S_ISUID;
+ }
+ if (mode & P9_STAT_MODE_SETGID) {
+ ret |= S_ISGID;
+ }
+ if (mode & P9_STAT_MODE_SETVTX) {
+ ret |= S_ISVTX;
+ }
+
+ return ret;
+}
+
+static int donttouch_stat(V9fsStat *stat)
+{
+ if (stat->type == -1 &&
+ stat->dev == -1 &&
+ stat->qid.type == -1 &&
+ stat->qid.version == -1 &&
+ stat->qid.path == -1 &&
+ stat->mode == -1 &&
+ stat->atime == -1 &&
+ stat->mtime == -1 &&
+ stat->length == -1 &&
+ !stat->name.size &&
+ !stat->uid.size &&
+ !stat->gid.size &&
+ !stat->muid.size &&
+ stat->n_uid == -1 &&
+ stat->n_gid == -1 &&
+ stat->n_muid == -1) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void v9fs_stat_free(V9fsStat *stat)
+{
+ v9fs_string_free(&stat->name);
+ v9fs_string_free(&stat->uid);
+ v9fs_string_free(&stat->gid);
+ v9fs_string_free(&stat->muid);
+ v9fs_string_free(&stat->extension);
+}
+
+static uint32_t stat_to_v9mode(const struct stat *stbuf)
+{
+ uint32_t mode;
+
+ mode = stbuf->st_mode & 0777;
+ if (S_ISDIR(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_DIR;
+ }
+
+ if (S_ISLNK(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_SYMLINK;
+ }
+
+ if (S_ISSOCK(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_SOCKET;
+ }
+
+ if (S_ISFIFO(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_NAMED_PIPE;
+ }
+
+ if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
+ mode |= P9_STAT_MODE_DEVICE;
+ }
+
+ if (stbuf->st_mode & S_ISUID) {
+ mode |= P9_STAT_MODE_SETUID;
+ }
+
+ if (stbuf->st_mode & S_ISGID) {
+ mode |= P9_STAT_MODE_SETGID;
+ }
+
+ if (stbuf->st_mode & S_ISVTX) {
+ mode |= P9_STAT_MODE_SETVTX;
+ }
+
+ return mode;
+}
+
+static int stat_to_v9stat(V9fsState *s, V9fsString *name,
+ const struct stat *stbuf,
+ V9fsStat *v9stat)
+{
+ int err;
+ const char *str;
+
+ memset(v9stat, 0, sizeof(*v9stat));
+
+ stat_to_qid(stbuf, &v9stat->qid);
+ v9stat->mode = stat_to_v9mode(stbuf);
+ v9stat->atime = stbuf->st_atime;
+ v9stat->mtime = stbuf->st_mtime;
+ v9stat->length = stbuf->st_size;
+
+ v9fs_string_null(&v9stat->uid);
+ v9fs_string_null(&v9stat->gid);
+ v9fs_string_null(&v9stat->muid);
+
+ v9stat->n_uid = stbuf->st_uid;
+ v9stat->n_gid = stbuf->st_gid;
+ v9stat->n_muid = 0;
+
+ v9fs_string_null(&v9stat->extension);
+
+ if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
+ err = v9fs_do_readlink(s, name, &v9stat->extension);
+ if (err == -1) {
+ err = -errno;
+ return err;
+ }
+ v9stat->extension.data[err] = 0;
+ v9stat->extension.size = err;
+ } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
+ v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
+ S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
+ major(stbuf->st_rdev), minor(stbuf->st_rdev));
+ } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
+ v9fs_string_sprintf(&v9stat->extension, "%s %lu",
+ "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
+ }
+
+ str = strrchr(name->data, '/');
+ if (str) {
+ str += 1;
+ } else {
+ str = name->data;
+ }
+
+ v9fs_string_sprintf(&v9stat->name, "%s", str);
+
+ v9stat->size = 61 +
+ v9fs_string_size(&v9stat->name) +
+ v9fs_string_size(&v9stat->uid) +
+ v9fs_string_size(&v9stat->gid) +
+ v9fs_string_size(&v9stat->muid) +
+ v9fs_string_size(&v9stat->extension);
+ return 0;
+}
+
+#define P9_STATS_MODE 0x00000001ULL
+#define P9_STATS_NLINK 0x00000002ULL
+#define P9_STATS_UID 0x00000004ULL
+#define P9_STATS_GID 0x00000008ULL
+#define P9_STATS_RDEV 0x00000010ULL
+#define P9_STATS_ATIME 0x00000020ULL
+#define P9_STATS_MTIME 0x00000040ULL
+#define P9_STATS_CTIME 0x00000080ULL
+#define P9_STATS_INO 0x00000100ULL
+#define P9_STATS_SIZE 0x00000200ULL
+#define P9_STATS_BLOCKS 0x00000400ULL
+
+#define P9_STATS_BTIME 0x00000800ULL
+#define P9_STATS_GEN 0x00001000ULL
+#define P9_STATS_DATA_VERSION 0x00002000ULL
+
+#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
+#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
+
+
+static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
+ V9fsStatDotl *v9lstat)
+{
+ memset(v9lstat, 0, sizeof(*v9lstat));
+
+ v9lstat->st_mode = stbuf->st_mode;
+ v9lstat->st_nlink = stbuf->st_nlink;
+ v9lstat->st_uid = stbuf->st_uid;
+ v9lstat->st_gid = stbuf->st_gid;
+ v9lstat->st_rdev = stbuf->st_rdev;
+ v9lstat->st_size = stbuf->st_size;
+ v9lstat->st_blksize = stbuf->st_blksize;
+ v9lstat->st_blocks = stbuf->st_blocks;
+ v9lstat->st_atime_sec = stbuf->st_atime;
+ v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
+ v9lstat->st_mtime_sec = stbuf->st_mtime;
+ v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
+ v9lstat->st_ctime_sec = stbuf->st_ctime;
+ v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
+ /* Currently we only support BASIC fields in stat */
+ v9lstat->st_result_mask = P9_STATS_BASIC;
+
+ stat_to_qid(stbuf, &v9lstat->qid);
+}
+
+static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
+{
+ while (len && *iovcnt) {
+ if (len < sg->iov_len) {
+ sg->iov_len -= len;
+ sg->iov_base += len;
+ len = 0;
+ } else {
+ len -= sg->iov_len;
+ sg++;
+ *iovcnt -= 1;
+ }
+ }
+
+ return sg;
+}
+
+static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
+{
+ int i;
+ int total = 0;
+
+ for (i = 0; i < *cnt; i++) {
+ if ((total + sg[i].iov_len) > cap) {
+ sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
+ i++;
+ break;
+ }
+ total += sg[i].iov_len;
+ }
+
+ *cnt = i;
+
+ return sg;
+}
+
+static void print_sg(struct iovec *sg, int cnt)
+{
+ int i;
+
+ printf("sg[%d]: {", cnt);
+ for (i = 0; i < cnt; i++) {
+ if (i) {
+ printf(", ");
+ }
+ printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
+ }
+ printf("}\n");
+}
+
+static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
+{
+ V9fsString str;
+ v9fs_string_init(&str);
+ v9fs_string_copy(&str, dst);
+ v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len);
+ v9fs_string_free(&str);
+}
+
+static void v9fs_version(V9fsState *s, V9fsPDU *pdu)
+{
+ V9fsString version;
+ size_t offset = 7;
+
+ pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
+
+ if (!strcmp(version.data, "9P2000.u")) {
+ s->proto_version = V9FS_PROTO_2000U;
+ } else if (!strcmp(version.data, "9P2000.L")) {
+ s->proto_version = V9FS_PROTO_2000L;
+ } else {
+ v9fs_string_sprintf(&version, "unknown");
+ }
+
+ offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
+ complete_pdu(s, pdu, offset);
+
+ v9fs_string_free(&version);
+}
+
+static void v9fs_attach(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid, afid, n_uname;
+ V9fsString uname, aname;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ size_t offset = 7;
+ ssize_t err;
+
+ pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
+
+ fidp = alloc_fid(s, fid);
+ if (fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ fidp->uid = n_uname;
+
+ v9fs_string_sprintf(&fidp->path, "%s", "/");
+ err = fid_to_qid(s, fidp, &qid);
+ if (err) {
+ err = -EINVAL;
+ free_fid(s, fid);
+ goto out;
+ }
+
+ offset += pdu_marshal(pdu, offset, "Q", &qid);
+
+ err = offset;
+out:
+ complete_pdu(s, pdu, err);
+ v9fs_string_free(&uname);
+ v9fs_string_free(&aname);
+}
+
+static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat);
+ if (err) {
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat);
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_stat_free(&vs->v9stat);
+ qemu_free(vs);
+}
+
+static void v9fs_stat(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsStatState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9stat, 0, sizeof(vs->v9stat));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_stat_post_lstat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_stat_free(&vs->v9stat);
+ qemu_free(vs);
+}
+
+static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsStatStateDotl *vs;
+ ssize_t err = 0;
+ V9fsFidState *fidp;
+ uint64_t request_mask;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ /* Currently we only support BASIC fields in stat, so there is no
+ * need to look at request_mask.
+ */
+ err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
+ v9fs_getattr_post_lstat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+/* From Linux kernel code */
+#define ATTR_MODE (1 << 0)
+#define ATTR_UID (1 << 1)
+#define ATTR_GID (1 << 2)
+#define ATTR_SIZE (1 << 3)
+#define ATTR_ATIME (1 << 4)
+#define ATTR_MTIME (1 << 5)
+#define ATTR_CTIME (1 << 6)
+#define ATTR_MASK 127
+#define ATTR_ATIME_SET (1 << 7)
+#define ATTR_MTIME_SET (1 << 8)
+
+static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_SIZE)) {
+ err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
+ }
+ v9fs_setattr_post_truncate(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
+ int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ /* If the only valid entry in iattr is ctime we can call
+ * chown(-1,-1) to update the ctime of the file
+ */
+ if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
+ ((vs->v9iattr.valid & ATTR_CTIME)
+ && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
+ if (!(vs->v9iattr.valid & ATTR_UID)) {
+ vs->v9iattr.uid = -1;
+ }
+ if (!(vs->v9iattr.valid & ATTR_GID)) {
+ vs->v9iattr.gid = -1;
+ }
+ err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
+ vs->v9iattr.gid);
+ }
+ v9fs_setattr_post_chown(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
+ struct timespec times[2];
+ if (vs->v9iattr.valid & ATTR_ATIME) {
+ if (vs->v9iattr.valid & ATTR_ATIME_SET) {
+ times[0].tv_sec = vs->v9iattr.atime_sec;
+ times[0].tv_nsec = vs->v9iattr.atime_nsec;
+ } else {
+ times[0].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MTIME) {
+ if (vs->v9iattr.valid & ATTR_MTIME_SET) {
+ times[1].tv_sec = vs->v9iattr.mtime_sec;
+ times[1].tv_nsec = vs->v9iattr.mtime_nsec;
+ } else {
+ times[1].tv_nsec = UTIME_NOW;
+ }
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+ err = v9fs_do_utimensat(s, &vs->fidp->path, times);
+ }
+ v9fs_setattr_post_utimensat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsSetattrState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->v9iattr.valid & ATTR_MODE) {
+ err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
+ }
+
+ v9fs_setattr_post_chmod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
+{
+ complete_pdu(s, vs->pdu, err);
+
+ if (vs->nwnames) {
+ for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
+ v9fs_string_free(&vs->wnames[vs->name_idx]);
+ }
+
+ qemu_free(vs->wnames);
+ qemu_free(vs->qids);
+ }
+}
+
+static void v9fs_walk_marshal(V9fsWalkState *vs)
+{
+ int i;
+ vs->offset = 7;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames);
+
+ for (i = 0; i < vs->nwnames; i++) {
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]);
+ }
+}
+
+static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs,
+ int err)
+{
+ if (err == -1) {
+ free_fid(s, vs->newfidp->fid);
+ v9fs_string_free(&vs->path);
+ err = -ENOENT;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
+
+ vs->name_idx++;
+ if (vs->name_idx < vs->nwnames) {
+ v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
+ vs->wnames[vs->name_idx].data);
+ v9fs_string_copy(&vs->newfidp->path, &vs->path);
+
+ err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
+ v9fs_walk_post_newfid_lstat(s, vs, err);
+ return;
+ }
+
+ v9fs_string_free(&vs->path);
+ v9fs_walk_marshal(vs);
+ err = vs->offset;
+out:
+ v9fs_walk_complete(s, vs, err);
+}
+
+static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs,
+ int err)
+{
+ if (err == -1) {
+ v9fs_string_free(&vs->path);
+ err = -ENOENT;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
+ vs->name_idx++;
+ if (vs->name_idx < vs->nwnames) {
+
+ v9fs_string_sprintf(&vs->path, "%s/%s",
+ vs->fidp->path.data, vs->wnames[vs->name_idx].data);
+ v9fs_string_copy(&vs->fidp->path, &vs->path);
+
+ err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_walk_post_oldfid_lstat(s, vs, err);
+ return;
+ }
+
+ v9fs_string_free(&vs->path);
+ v9fs_walk_marshal(vs);
+ err = vs->offset;
+out:
+ v9fs_walk_complete(s, vs, err);
+}
+
+static void v9fs_walk(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid, newfid;
+ V9fsWalkState *vs;
+ int err = 0;
+ int i;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->wnames = NULL;
+ vs->qids = NULL;
+ vs->offset = 7;
+
+ vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
+ &newfid, &vs->nwnames);
+
+ if (vs->nwnames) {
+ vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
+
+ vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
+
+ for (i = 0; i < vs->nwnames; i++) {
+ vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
+ &vs->wnames[i]);
+ }
+ }
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ /* FIXME: is this really valid? */
+ if (fid == newfid) {
+
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+ v9fs_string_init(&vs->path);
+ vs->name_idx = 0;
+
+ if (vs->name_idx < vs->nwnames) {
+ v9fs_string_sprintf(&vs->path, "%s/%s",
+ vs->fidp->path.data, vs->wnames[vs->name_idx].data);
+ v9fs_string_copy(&vs->fidp->path, &vs->path);
+
+ err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_walk_post_oldfid_lstat(s, vs, err);
+ return;
+ }
+ } else {
+ vs->newfidp = alloc_fid(s, newfid);
+ if (vs->newfidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ vs->newfidp->uid = vs->fidp->uid;
+ v9fs_string_init(&vs->path);
+ vs->name_idx = 0;
+ v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path);
+
+ if (vs->name_idx < vs->nwnames) {
+ v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
+ vs->wnames[vs->name_idx].data);
+ v9fs_string_copy(&vs->newfidp->path, &vs->path);
+
+ err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
+ v9fs_walk_post_newfid_lstat(s, vs, err);
+ return;
+ }
+ }
+
+ v9fs_walk_marshal(vs);
+ err = vs->offset;
+out:
+ v9fs_walk_complete(s, vs, err);
+}
+
+static int32_t get_iounit(V9fsState *s, V9fsString *name)
+{
+ struct statfs stbuf;
+ int32_t iounit = 0;
+
+ /*
+ * iounit should be multiples of f_bsize (host filesystem block size
+ * and as well as less than (client msize - P9_IOHDRSZ))
+ */
+ if (!v9fs_do_statfs(s, name, &stbuf)) {
+ iounit = stbuf.f_bsize;
+ iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
+ }
+
+ if (!iounit) {
+ iounit = s->msize - P9_IOHDRSZ;
+ }
+ return iounit;
+}
+
+static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
+{
+ if (vs->fidp->fs.dir == NULL) {
+ err = -errno;
+ goto out;
+ }
+ vs->fidp->fid_type = P9_FID_DIR;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+
+}
+
+static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
+{
+ int err;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+ err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
+{
+ if (vs->fidp->fs.fd == -1) {
+ err = -errno;
+ goto out;
+ }
+ vs->fidp->fid_type = P9_FID_FILE;
+ vs->iounit = get_iounit(s, &vs->fidp->path);
+ v9fs_open_post_getiounit(s, vs);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
+{
+ int flags;
+
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+
+ if (S_ISDIR(vs->stbuf.st_mode)) {
+ vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
+ v9fs_open_post_opendir(s, vs, err);
+ } else {
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ flags = vs->mode;
+ flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
+ /* Ignore direct disk access hint until the server supports it. */
+ flags &= ~O_DIRECT;
+ } else {
+ flags = omode_to_uflags(vs->mode);
+ }
+ vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
+ v9fs_open_post_open(s, vs, err);
+ }
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_open(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsOpenState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+ vs->mode = 0;
+
+ if (s->proto_version == V9FS_PROTO_2000L) {
+ pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
+ } else {
+ pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
+ }
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+
+ err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+
+ v9fs_open_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
+{
+ if (err == 0) {
+ v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
+ &vs->iounit);
+ err = vs->offset;
+ } else {
+ vs->fidp->fid_type = P9_FID_NONE;
+ err = -errno;
+ if (vs->fidp->fs.fd > 0) {
+ close(vs->fidp->fs.fd);
+ }
+ }
+
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
+ int err)
+{
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+
+out:
+ v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
+ int err)
+{
+ if (vs->fidp->fs.fd == -1) {
+ err = -errno;
+ goto out;
+ }
+ vs->fidp->fid_type = P9_FID_FILE;
+ vs->iounit = get_iounit(s, &vs->fullname);
+ v9fs_lcreate_post_get_iounit(s, vs, err);
+ return;
+
+out:
+ v9fs_post_lcreate(s, vs, err);
+}
+
+static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid, flags, mode;
+ gid_t gid;
+ V9fsLcreateState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
+ &mode, &gid);
+
+ vs->fidp = lookup_fid(s, dfid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
+ vs->name.data);
+
+ /* Ignore direct disk access hint until the server supports it. */
+ flags &= ~O_DIRECT;
+
+ vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+ gid, flags, mode);
+ v9fs_lcreate_post_do_open2(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_do_fsync(V9fsState *s, V9fsPDU *pdu, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ }
+ complete_pdu(s, pdu, err);
+}
+
+static void v9fs_fsync(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ size_t offset = 7;
+ V9fsFidState *fidp;
+ int datasync;
+ int err;
+
+ pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ v9fs_post_do_fsync(s, pdu, err);
+ return;
+ }
+ err = v9fs_do_fsync(s, fidp->fs.fd, datasync);
+ v9fs_post_do_fsync(s, pdu, err);
+}
+
+static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ size_t offset = 7;
+ int err;
+
+ pdu_unmarshal(pdu, offset, "d", &fid);
+
+ err = free_fid(s, fid);
+ if (err < 0) {
+ goto out;
+ }
+
+ offset = 7;
+ err = offset;
+out:
+ complete_pdu(s, pdu, err);
+}
+
+static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
+
+static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+ if (err) {
+ goto out;
+ }
+ v9fs_stat_free(&vs->v9stat);
+ v9fs_string_free(&vs->name);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
+ ssize_t err)
+{
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+ err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
+ if (err) {
+ goto out;
+ }
+
+ vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
+ &vs->v9stat);
+ if ((vs->len != (vs->v9stat.size + 2)) ||
+ ((vs->count + vs->len) > vs->max_count)) {
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
+ v9fs_read_post_seekdir(s, vs, err);
+ return;
+ }
+ vs->count += vs->len;
+ v9fs_stat_free(&vs->v9stat);
+ v9fs_string_free(&vs->name);
+ vs->dir_pos = vs->dent->d_off;
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_read_post_readdir(s, vs, err);
+ return;
+out:
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
+ v9fs_read_post_seekdir(s, vs, err);
+ return;
+
+}
+
+static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+ if (vs->dent) {
+ memset(&vs->v9stat, 0, sizeof(vs->v9stat));
+ v9fs_string_init(&vs->name);
+ v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
+ vs->dent->d_name);
+ err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
+ v9fs_read_post_dir_lstat(s, vs, err);
+ return;
+ }
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_read_post_readdir(s, vs, err);
+ return;
+}
+
+static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
+ ssize_t err)
+{
+ vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
+ v9fs_read_post_telldir(s, vs, err);
+ return;
+}
+
+static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err)
+{
+ if (err < 0) {
+ /* IO error return the error */
+ err = -errno;
+ goto out;
+ }
+ vs->total += vs->len;
+ vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
+ if (vs->total < vs->count && vs->len > 0) {
+ do {
+ if (0) {
+ print_sg(vs->sg, vs->cnt);
+ }
+ vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+ vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
+ } while (vs->len == -1 && errno == EINTR);
+ if (vs->len == -1) {
+ err = -errno;
+ }
+ v9fs_read_post_preadv(s, vs, err);
+ return;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
+ vs->offset += vs->count;
+ err = vs->offset;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
+{
+ ssize_t err = 0;
+ int read_count;
+ int64_t xattr_len;
+
+ xattr_len = vs->fidp->fs.xattr.len;
+ read_count = xattr_len - vs->off;
+ if (read_count > vs->count) {
+ read_count = vs->count;
+ } else if (read_count < 0) {
+ /*
+ * read beyond XATTR value
+ */
+ read_count = 0;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
+ vs->offset += pdu_pack(vs->pdu, vs->offset,
+ ((char *)vs->fidp->fs.xattr.value) + vs->off,
+ read_count);
+ err = vs->offset;
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_read(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsReadState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+ vs->total = 0;
+ vs->len = 0;
+ vs->count = 0;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->fidp->fid_type == P9_FID_DIR) {
+ vs->max_count = vs->count;
+ vs->count = 0;
+ if (vs->off == 0) {
+ v9fs_do_rewinddir(s, vs->fidp->fs.dir);
+ }
+ v9fs_read_post_rewinddir(s, vs, err);
+ return;
+ } else if (vs->fidp->fid_type == P9_FID_FILE) {
+ vs->sg = vs->iov;
+ pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
+ vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
+ if (vs->total <= vs->count) {
+ vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+ vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
+ err = vs->len;
+ v9fs_read_post_preadv(s, vs, err);
+ }
+ return;
+ } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+ v9fs_xattr_read(s, vs);
+ return;
+ } else {
+ err = -EINVAL;
+ }
+out:
+ complete_pdu(s, pdu, err);
+ qemu_free(vs);
+}
+
+typedef struct V9fsReadDirState {
+ V9fsPDU *pdu;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ off_t saved_dir_pos;
+ struct dirent *dent;
+ int32_t count;
+ int32_t max_count;
+ size_t offset;
+ int64_t initial_offset;
+ V9fsString name;
+} V9fsReadDirState;
+
+static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+ return;
+}
+
+/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
+ * size of type (1) + size of name.size (2) + strlen(name.data)
+ */
+#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
+
+static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ int len;
+ size_t size;
+
+ if (vs->dent) {
+ v9fs_string_init(&vs->name);
+ v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
+
+ if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
+ /* Ran out of buffer. Set dir back to old position and return */
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
+ v9fs_readdir_post_seekdir(s, vs);
+ return;
+ }
+
+ /* Fill up just the path field of qid because the client uses
+ * only that. To fill the entire qid structure we will have
+ * to stat each dirent found, which is expensive
+ */
+ size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
+ memcpy(&vs->qid.path, &vs->dent->d_ino, size);
+ /* Fill the other fields with dummy values */
+ vs->qid.type = 0;
+ vs->qid.version = 0;
+
+ len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
+ &vs->qid, vs->dent->d_off,
+ vs->dent->d_type, &vs->name);
+ vs->count += len;
+ v9fs_string_free(&vs->name);
+ vs->saved_dir_pos = vs->dent->d_off;
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_readdir(s, vs);
+ return;
+ }
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
+ vs->offset += vs->count;
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_readdir(s, vs);
+ return;
+}
+
+static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
+{
+ vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
+ v9fs_readdir_post_telldir(s, vs);
+ return;
+}
+
+static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsReadDirState *vs;
+ ssize_t err = 0;
+ size_t offset = 7;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+ vs->count = 0;
+
+ pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
+ &vs->max_count);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->initial_offset == 0) {
+ v9fs_do_rewinddir(s, vs->fidp->fs.dir);
+ } else {
+ v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
+ }
+
+ v9fs_readdir_post_setdir(s, vs);
+ return;
+
+out:
+ complete_pdu(s, pdu, err);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_write_post_pwritev(V9fsState *s, V9fsWriteState *vs,
+ ssize_t err)
+{
+ if (err < 0) {
+ /* IO error return the error */
+ err = -errno;
+ goto out;
+ }
+ vs->total += vs->len;
+ vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
+ if (vs->total < vs->count && vs->len > 0) {
+ do {
+ if (0) {
+ print_sg(vs->sg, vs->cnt);
+ }
+ vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
+ vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
+ } while (vs->len == -1 && errno == EINTR);
+ if (vs->len == -1) {
+ err = -errno;
+ }
+ v9fs_write_post_pwritev(s, vs, err);
+ return;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs)
+{
+ int i, to_copy;
+ ssize_t err = 0;
+ int write_count;
+ int64_t xattr_len;
+
+ xattr_len = vs->fidp->fs.xattr.len;
+ write_count = xattr_len - vs->off;
+ if (write_count > vs->count) {
+ write_count = vs->count;
+ } else if (write_count < 0) {
+ /*
+ * write beyond XATTR value len specified in
+ * xattrcreate
+ */
+ err = -ENOSPC;
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count);
+ err = vs->offset;
+ vs->fidp->fs.xattr.copied_len += write_count;
+ /*
+ * Now copy the content from sg list
+ */
+ for (i = 0; i < vs->cnt; i++) {
+ if (write_count > vs->sg[i].iov_len) {
+ to_copy = vs->sg[i].iov_len;
+ } else {
+ to_copy = write_count;
+ }
+ memcpy((char *)vs->fidp->fs.xattr.value + vs->off,
+ vs->sg[i].iov_base, to_copy);
+ /* updating vs->off since we are not using below */
+ vs->off += to_copy;
+ write_count -= to_copy;
+ }
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_write(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsWriteState *vs;
+ ssize_t err;
+
+ vs = qemu_malloc(sizeof(*vs));
+
+ vs->pdu = pdu;
+ vs->offset = 7;
+ vs->sg = vs->iov;
+ vs->total = 0;
+ vs->len = 0;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
+ vs->sg, &vs->cnt);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (vs->fidp->fid_type == P9_FID_FILE) {
+ if (vs->fidp->fs.fd == -1) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else if (vs->fidp->fid_type == P9_FID_XATTR) {
+ /*
+ * setxattr operation
+ */
+ v9fs_xattr_write(s, vs);
+ return;
+ } else {
+ err = -EINVAL;
+ goto out;
+ }
+ vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
+ if (vs->total <= vs->count) {
+ vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, vs->off);
+ if (vs->len > 0) {
+ vs->off += vs->len;
+ }
+ err = vs->len;
+ v9fs_write_post_pwritev(s, vs, err);
+ }
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
+{
+ int err;
+ v9fs_string_copy(&vs->fidp->path, &vs->fullname);
+ stat_to_qid(&vs->stbuf, &vs->qid);
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
+ err = vs->offset;
+
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->extension);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
+{
+ if (err == 0) {
+ vs->iounit = get_iounit(s, &vs->fidp->path);
+ v9fs_create_post_getiounit(s, vs);
+ return;
+ }
+
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->extension);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
+{
+ if (err) {
+ err = -errno;
+ }
+ v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
+ int err)
+{
+ if (!vs->fidp->fs.dir) {
+ err = -errno;
+ }
+ vs->fidp->fid_type = P9_FID_DIR;
+ v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
+ int err)
+{
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
+ v9fs_create_post_opendir(s, vs, err);
+ return;
+
+out:
+ v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_mkdir(V9fsState *s, V9fsCreateState *vs, int err)
+{
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_create_post_dir_lstat(s, vs, err);
+ return;
+
+out:
+ v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
+{
+ if (err) {
+ vs->fidp->fid_type = P9_FID_NONE;
+ close(vs->fidp->fs.fd);
+ err = -errno;
+ }
+ v9fs_post_create(s, vs, err);
+ return;
+}
+
+static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
+{
+ if (vs->fidp->fs.fd == -1) {
+ err = -errno;
+ goto out;
+ }
+ vs->fidp->fid_type = P9_FID_FILE;
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+ v9fs_create_post_fstat(s, vs, err);
+
+ return;
+
+out:
+ v9fs_post_create(s, vs, err);
+
+}
+
+static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
+{
+
+ if (err == 0 || errno != ENOENT) {
+ err = -errno;
+ goto out;
+ }
+
+ if (vs->perm & P9_STAT_MODE_DIR) {
+ err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
+ vs->fidp->uid, -1);
+ v9fs_create_post_mkdir(s, vs, err);
+ } else if (vs->perm & P9_STAT_MODE_SYMLINK) {
+ err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
+ vs->fullname.data, -1);
+ v9fs_create_post_perms(s, vs, err);
+ } else if (vs->perm & P9_STAT_MODE_LINK) {
+ int32_t nfid = atoi(vs->extension.data);
+ V9fsFidState *nfidp = lookup_fid(s, nfid);
+ if (nfidp == NULL) {
+ err = -errno;
+ v9fs_post_create(s, vs, err);
+ }
+ err = v9fs_do_link(s, &nfidp->path, &vs->fullname);
+ v9fs_create_post_perms(s, vs, err);
+ } else if (vs->perm & P9_STAT_MODE_DEVICE) {
+ char ctype;
+ uint32_t major, minor;
+ mode_t nmode = 0;
+
+ if (sscanf(vs->extension.data, "%c %u %u", &ctype, &major,
+ &minor) != 3) {
+ err = -errno;
+ v9fs_post_create(s, vs, err);
+ }
+
+ switch (ctype) {
+ case 'c':
+ nmode = S_IFCHR;
+ break;
+ case 'b':
+ nmode = S_IFBLK;
+ break;
+ default:
+ err = -EIO;
+ v9fs_post_create(s, vs, err);
+ }
+
+ nmode |= vs->perm & 0777;
+ err = v9fs_do_mknod(s, vs->fullname.data, nmode,
+ makedev(major, minor), vs->fidp->uid, -1);
+ v9fs_create_post_perms(s, vs, err);
+ } else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
+ err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
+ 0, vs->fidp->uid, -1);
+ v9fs_post_create(s, vs, err);
+ } else if (vs->perm & P9_STAT_MODE_SOCKET) {
+ err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
+ 0, vs->fidp->uid, -1);
+ v9fs_post_create(s, vs, err);
+ } else {
+ vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
+ -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
+
+ v9fs_create_post_open2(s, vs, err);
+ }
+
+ return;
+
+out:
+ v9fs_post_create(s, vs, err);
+}
+
+static void v9fs_create(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsCreateState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdbs", &fid, &vs->name,
+ &vs->perm, &vs->mode, &vs->extension);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
+ vs->name.data);
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_create_post_lstat(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->extension);
+ qemu_free(vs);
+}
+
+static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
+{
+ if (err == 0) {
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+ } else {
+ err = -errno;
+ }
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->symname);
+ v9fs_string_free(&vs->fullname);
+ qemu_free(vs);
+}
+
+static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
+ int err)
+{
+ if (err) {
+ goto out;
+ }
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+out:
+ v9fs_post_symlink(s, vs, err);
+}
+
+static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid;
+ V9fsSymlinkState *vs;
+ int err = 0;
+ gid_t gid;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
+ &vs->symname, &gid);
+
+ vs->dfidp = lookup_fid(s, dfid);
+ if (vs->dfidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
+ vs->name.data);
+ err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
+ vs->fullname.data, gid);
+ v9fs_symlink_post_do_symlink(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ v9fs_string_free(&vs->symname);
+ qemu_free(vs);
+}
+
+static void v9fs_flush(V9fsState *s, V9fsPDU *pdu)
+{
+ /* A nop call with no return */
+ complete_pdu(s, pdu, 7);
+}
+
+static void v9fs_link(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t dfid, oldfid;
+ V9fsFidState *dfidp, *oldfidp;
+ V9fsString name, fullname;
+ size_t offset = 7;
+ int err = 0;
+
+ v9fs_string_init(&fullname);
+
+ pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
+
+ dfidp = lookup_fid(s, dfid);
+ if (dfidp == NULL) {
+ err = -errno;
+ goto out;
+ }
+
+ oldfidp = lookup_fid(s, oldfid);
+ if (oldfidp == NULL) {
+ err = -errno;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
+ err = offset;
+ err = v9fs_do_link(s, &oldfidp->path, &fullname);
+ if (err) {
+ err = -errno;
+ }
+ v9fs_string_free(&fullname);
+
+out:
+ v9fs_string_free(&name);
+ complete_pdu(s, pdu, err);
+}
+
+static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
+ int err)
+{
+ if (err < 0) {
+ err = -errno;
+ } else {
+ err = vs->offset;
+ }
+
+ /* For TREMOVE we need to clunk the fid even on failed remove */
+ free_fid(s, vs->fidp->fid);
+
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_remove(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsRemoveState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = v9fs_do_remove(s, &vs->fidp->path);
+ v9fs_remove_post_remove(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_truncate(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+
+ err = vs->offset;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+ if (vs->v9stat.length != -1) {
+ if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
+ err = -errno;
+ }
+ }
+ v9fs_wstat_post_truncate(s, vs, err);
+ return;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
+{
+ int err = 0;
+ char *old_name, *new_name;
+ char *end;
+
+ if (vs->newdirfid != -1) {
+ V9fsFidState *dirfidp;
+ dirfidp = lookup_fid(s, vs->newdirfid);
+
+ if (dirfidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(dirfidp->fid_type != P9_FID_NONE);
+
+ new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
+
+ strcpy(new_name, dirfidp->path.data);
+ strcat(new_name, "/");
+ strcat(new_name + dirfidp->path.size, vs->name.data);
+ } else {
+ old_name = vs->fidp->path.data;
+ end = strrchr(old_name, '/');
+ if (end) {
+ end++;
+ } else {
+ end = old_name;
+ }
+ new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
+
+ strncat(new_name, old_name, end - old_name);
+ strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
+ }
+
+ v9fs_string_free(&vs->name);
+ vs->name.data = qemu_strdup(new_name);
+ vs->name.size = strlen(new_name);
+
+ if (strcmp(new_name, vs->fidp->path.data) != 0) {
+ if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
+ err = -errno;
+ } else {
+ V9fsFidState *fidp;
+ /*
+ * Fixup fid's pointing to the old name to
+ * start pointing to the new name
+ */
+ for (fidp = s->fid_list; fidp; fidp = fidp->next) {
+ if (vs->fidp == fidp) {
+ /*
+ * we replace name of this fid towards the end
+ * so that our below strcmp will work
+ */
+ continue;
+ }
+ if (!strncmp(vs->fidp->path.data, fidp->path.data,
+ strlen(vs->fidp->path.data))) {
+ /* replace the name */
+ v9fs_fix_path(&fidp->path, &vs->name,
+ strlen(vs->fidp->path.data));
+ }
+ }
+ v9fs_string_copy(&vs->fidp->path, &vs->name);
+ }
+ }
+out:
+ v9fs_string_free(&vs->name);
+ return err;
+}
+
+static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
+{
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+
+ if (vs->v9stat.name.size != 0) {
+ V9fsRenameState *vr;
+
+ vr = qemu_mallocz(sizeof(V9fsRenameState));
+ vr->newdirfid = -1;
+ vr->pdu = vs->pdu;
+ vr->fidp = vs->fidp;
+ vr->offset = vs->offset;
+ vr->name.size = vs->v9stat.name.size;
+ vr->name.data = qemu_strdup(vs->v9stat.name.data);
+
+ err = v9fs_complete_rename(s, vr);
+ qemu_free(vr);
+ }
+ v9fs_wstat_post_rename(s, vs, err);
+ return;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_rename(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsRenameState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
+
+ err = v9fs_complete_rename(s, vs);
+ v9fs_rename_post_rename(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+
+ if (vs->v9stat.n_gid != -1 || vs->v9stat.n_uid != -1) {
+ if (v9fs_do_chown(s, &vs->fidp->path, vs->v9stat.n_uid,
+ vs->v9stat.n_gid)) {
+ err = -errno;
+ }
+ }
+ v9fs_wstat_post_chown(s, vs, err);
+ return;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err < 0) {
+ goto out;
+ }
+
+ if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
+ struct timespec times[2];
+ if (vs->v9stat.atime != -1) {
+ times[0].tv_sec = vs->v9stat.atime;
+ times[0].tv_nsec = 0;
+ } else {
+ times[0].tv_nsec = UTIME_OMIT;
+ }
+ if (vs->v9stat.mtime != -1) {
+ times[1].tv_sec = vs->v9stat.mtime;
+ times[1].tv_nsec = 0;
+ } else {
+ times[1].tv_nsec = UTIME_OMIT;
+ }
+
+ if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
+ err = -errno;
+ }
+ }
+
+ v9fs_wstat_post_utime(s, vs, err);
+ return;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_fsync(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ }
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat_post_lstat(V9fsState *s, V9fsWstatState *vs, int err)
+{
+ uint32_t v9_mode;
+
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ v9_mode = stat_to_v9mode(&vs->stbuf);
+
+ if ((vs->v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
+ (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
+ /* Attempting to change the type */
+ err = -EIO;
+ goto out;
+ }
+
+ if (v9fs_do_chmod(s, &vs->fidp->path, v9mode_to_mode(vs->v9stat.mode,
+ &vs->v9stat.extension))) {
+ err = -errno;
+ }
+ v9fs_wstat_post_chmod(s, vs, err);
+ return;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsWstatState *vs;
+ int err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(pdu, vs->offset, "dwS", &fid, &vs->unused, &vs->v9stat);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* do we need to sync the file? */
+ if (donttouch_stat(&vs->v9stat)) {
+ err = v9fs_do_fsync(s, vs->fidp->fs.fd, 0);
+ v9fs_wstat_post_fsync(s, vs, err);
+ return;
+ }
+
+ if (vs->v9stat.mode != -1) {
+ err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_wstat_post_lstat(s, vs, err);
+ return;
+ }
+
+ v9fs_wstat_post_chmod(s, vs, err);
+ return;
+
+out:
+ v9fs_stat_free(&vs->v9stat);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
+{
+ int32_t bsize_factor;
+
+ if (err) {
+ err = -errno;
+ goto out;
+ }
+
+ /*
+ * compute bsize factor based on host file system block size
+ * and client msize
+ */
+ bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
+ if (!bsize_factor) {
+ bsize_factor = 1;
+ }
+ vs->v9statfs.f_type = vs->stbuf.f_type;
+ vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
+ vs->v9statfs.f_bsize *= bsize_factor;
+ /*
+ * f_bsize is adjusted(multiplied) by bsize factor, so we need to
+ * adjust(divide) the number of blocks, free blocks and available
+ * blocks by bsize factor
+ */
+ vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
+ vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
+ vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
+ vs->v9statfs.f_files = vs->stbuf.f_files;
+ vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
+ vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
+ (unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
+ vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
+
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
+ vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
+ vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
+ vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
+ vs->v9statfs.f_namelen);
+
+out:
+ complete_pdu(s, vs->pdu, vs->offset);
+ qemu_free(vs);
+}
+
+static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu)
+{
+ V9fsStatfsState *vs;
+ ssize_t err = 0;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
+
+ vs->fidp = lookup_fid(s, vs->fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
+ v9fs_statfs_post_statfs(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_mknod_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsMkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+ gid_t gid;
+ int mode;
+ int major, minor;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
+ &major, &minor, &gid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+ err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
+ fidp->uid, gid);
+ v9fs_mknod_post_mknod(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+/*
+ * Implement posix byte range locking code
+ * Server side handling of locking code is very simple, because 9p server in
+ * QEMU can handle only one client. And most of the lock handling
+ * (like conflict, merging) etc is done by the VFS layer itself, so no need to
+ * do any thing in * qemu 9p server side lock code path.
+ * So when a TLOCK request comes, always return success
+ */
+
+static void v9fs_lock(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid, err = 0;
+ V9fsLockState *vs;
+
+ vs = qemu_mallocz(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ vs->flock = qemu_malloc(sizeof(*vs->flock));
+ pdu_unmarshal(vs->pdu, vs->offset, "dbdqqds", &fid, &vs->flock->type,
+ &vs->flock->flags, &vs->flock->start, &vs->flock->length,
+ &vs->flock->proc_id, &vs->flock->client_id);
+
+ vs->status = P9_LOCK_ERROR;
+
+ /* We support only block flag now (that too ignored currently) */
+ if (vs->flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
+ err = -EINVAL;
+ goto out;
+ }
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+ if (err < 0) {
+ err = -errno;
+ goto out;
+ }
+ vs->status = P9_LOCK_SUCCESS;
+out:
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "b", vs->status);
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs->flock);
+ qemu_free(vs);
+}
+
+/*
+ * When a TGETLOCK request comes, always return success because all lock
+ * handling is done by client's VFS layer.
+ */
+
+static void v9fs_getlock(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid, err = 0;
+ V9fsGetlockState *vs;
+
+ vs = qemu_mallocz(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ vs->glock = qemu_malloc(sizeof(*vs->glock));
+ pdu_unmarshal(vs->pdu, vs->offset, "dbqqds", &fid, &vs->glock->type,
+ &vs->glock->start, &vs->glock->length, &vs->glock->proc_id,
+ &vs->glock->client_id);
+
+ vs->fidp = lookup_fid(s, fid);
+ if (vs->fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
+ if (err < 0) {
+ err = -errno;
+ goto out;
+ }
+ vs->glock->type = F_UNLCK;
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "bqqds", vs->glock->type,
+ vs->glock->start, vs->glock->length, vs->glock->proc_id,
+ &vs->glock->client_id);
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs->glock);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ stat_to_qid(&vs->stbuf, &vs->qid);
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
+{
+ if (err == -1) {
+ err = -errno;
+ goto out;
+ }
+
+ err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
+ v9fs_mkdir_post_lstat(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsMkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+ gid_t gid;
+ int mode;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ v9fs_string_init(&vs->fullname);
+ pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
+ &gid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
+ err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
+ v9fs_mkdir_post_mkdir(s, vs, err);
+ return;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->fullname);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
+{
+
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ /*
+ * Read the xattr value
+ */
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = -1;
+ if (vs->size) {
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+ &vs->name, vs->xattr_fidp->fs.xattr.value,
+ vs->xattr_fidp->fs.xattr.len);
+ }
+ v9fs_post_xattr_getvalue(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_post_lxattr_getvalue(V9fsState *s,
+ V9fsXattrState *vs, int err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+ return;
+}
+
+static void v9fs_post_lxattr_check(V9fsState *s,
+ V9fsXattrState *vs, ssize_t err)
+{
+ if (err < 0) {
+ err = -errno;
+ free_fid(s, vs->xattr_fidp->fid);
+ goto out;
+ }
+ /*
+ * Read the xattr value
+ */
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = -1;
+ if (vs->size) {
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+ vs->xattr_fidp->fs.xattr.value,
+ vs->xattr_fidp->fs.xattr.len);
+ }
+ v9fs_post_lxattr_getvalue(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu)
+{
+ ssize_t err = 0;
+ V9fsXattrState *vs;
+ int32_t fid, newfid;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
+ vs->file_fidp = lookup_fid(s, fid);
+ if (vs->file_fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ vs->xattr_fidp = alloc_fid(s, newfid);
+ if (vs->xattr_fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
+ if (vs->name.data[0] == 0) {
+ /*
+ * listxattr request. Get the size first
+ */
+ vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
+ NULL, 0);
+ if (vs->size < 0) {
+ err = vs->size;
+ }
+ v9fs_post_lxattr_check(s, vs, err);
+ return;
+ } else {
+ /*
+ * specific xattr fid. We check for xattr
+ * presence also collect the xattr size
+ */
+ vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
+ &vs->name, NULL, 0);
+ if (vs->size < 0) {
+ err = vs->size;
+ }
+ v9fs_post_xattr_check(s, vs, err);
+ return;
+ }
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu)
+{
+ int flags;
+ int32_t fid;
+ ssize_t err = 0;
+ V9fsXattrState *vs;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "dsqd",
+ &fid, &vs->name, &vs->size, &flags);
+
+ vs->file_fidp = lookup_fid(s, fid);
+ if (vs->file_fidp == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ /* Make the file fid point to xattr */
+ vs->xattr_fidp = vs->file_fidp;
+ vs->xattr_fidp->fid_type = P9_FID_XATTR;
+ vs->xattr_fidp->fs.xattr.copied_len = 0;
+ vs->xattr_fidp->fs.xattr.len = vs->size;
+ vs->xattr_fidp->fs.xattr.flags = flags;
+ v9fs_string_init(&vs->xattr_fidp->fs.xattr.name);
+ v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name);
+ if (vs->size)
+ vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
+ else
+ vs->xattr_fidp->fs.xattr.value = NULL;
+
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->name);
+ qemu_free(vs);
+}
+
+static void v9fs_readlink_post_readlink(V9fsState *s, V9fsReadLinkState *vs,
+ int err)
+{
+ if (err < 0) {
+ err = -errno;
+ goto out;
+ }
+ vs->offset += pdu_marshal(vs->pdu, vs->offset, "s", &vs->target);
+ err = vs->offset;
+out:
+ complete_pdu(s, vs->pdu, err);
+ v9fs_string_free(&vs->target);
+ qemu_free(vs);
+}
+
+static void v9fs_readlink(V9fsState *s, V9fsPDU *pdu)
+{
+ int32_t fid;
+ V9fsReadLinkState *vs;
+ int err = 0;
+ V9fsFidState *fidp;
+
+ vs = qemu_malloc(sizeof(*vs));
+ vs->pdu = pdu;
+ vs->offset = 7;
+
+ pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
+
+ fidp = lookup_fid(s, fid);
+ if (fidp == NULL) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ v9fs_string_init(&vs->target);
+ err = v9fs_do_readlink(s, &fidp->path, &vs->target);
+ v9fs_readlink_post_readlink(s, vs, err);
+ return;
+out:
+ complete_pdu(s, vs->pdu, err);
+ qemu_free(vs);
+}
+
+typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu);
+
+static pdu_handler_t *pdu_handlers[] = {
+ [P9_TREADDIR] = v9fs_readdir,
+ [P9_TSTATFS] = v9fs_statfs,
+ [P9_TGETATTR] = v9fs_getattr,
+ [P9_TSETATTR] = v9fs_setattr,
+ [P9_TXATTRWALK] = v9fs_xattrwalk,
+ [P9_TXATTRCREATE] = v9fs_xattrcreate,
+ [P9_TMKNOD] = v9fs_mknod,
+ [P9_TRENAME] = v9fs_rename,
+ [P9_TLOCK] = v9fs_lock,
+ [P9_TGETLOCK] = v9fs_getlock,
+ [P9_TREADLINK] = v9fs_readlink,
+ [P9_TMKDIR] = v9fs_mkdir,
+ [P9_TVERSION] = v9fs_version,
+ [P9_TLOPEN] = v9fs_open,
+ [P9_TATTACH] = v9fs_attach,
+ [P9_TSTAT] = v9fs_stat,
+ [P9_TWALK] = v9fs_walk,
+ [P9_TCLUNK] = v9fs_clunk,
+ [P9_TFSYNC] = v9fs_fsync,
+ [P9_TOPEN] = v9fs_open,
+ [P9_TREAD] = v9fs_read,
+#if 0
+ [P9_TAUTH] = v9fs_auth,
+#endif
+ [P9_TFLUSH] = v9fs_flush,
+ [P9_TLINK] = v9fs_link,
+ [P9_TSYMLINK] = v9fs_symlink,
+ [P9_TCREATE] = v9fs_create,
+ [P9_TLCREATE] = v9fs_lcreate,
+ [P9_TWRITE] = v9fs_write,
+ [P9_TWSTAT] = v9fs_wstat,
+ [P9_TREMOVE] = v9fs_remove,
+};
+
+static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
+{
+ pdu_handler_t *handler;
+
+ if (debug_9p_pdu) {
+ pprint_pdu(pdu);
+ }
+
+ BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers));
+
+ handler = pdu_handlers[pdu->id];
+ BUG_ON(handler == NULL);
+
+ handler(s, pdu);
+}
+
+static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ V9fsState *s = (V9fsState *)vdev;
+ V9fsPDU *pdu;
+ ssize_t len;
+
+ while ((pdu = alloc_pdu(s)) &&
+ (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
+ uint8_t *ptr;
+
+ BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
+ BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
+
+ ptr = pdu->elem.out_sg[0].iov_base;
+
+ memcpy(&pdu->size, ptr, 4);
+ pdu->id = ptr[4];
+ memcpy(&pdu->tag, ptr + 5, 2);
+
+ submit_pdu(s, pdu);
+ }
+
+ free_pdu(s, pdu);
+}
+
+static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
+{
+ features |= 1 << VIRTIO_9P_MOUNT_TAG;
+ return features;
+}
+
+static V9fsState *to_virtio_9p(VirtIODevice *vdev)
+{
+ return (V9fsState *)vdev;
+}
+
+static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ struct virtio_9p_config *cfg;
+ V9fsState *s = to_virtio_9p(vdev);
+
+ cfg = qemu_mallocz(sizeof(struct virtio_9p_config) +
+ s->tag_len);
+ stw_raw(&cfg->tag_len, s->tag_len);
+ memcpy(cfg->tag, s->tag, s->tag_len);
+ memcpy(config, cfg, s->config_size);
+ qemu_free(cfg);
+}
+
+VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
+ {
+ V9fsState *s;
+ int i, len;
+ struct stat stat;
+ FsTypeEntry *fse;
+
+
+ s = (V9fsState *)virtio_common_init("virtio-9p",
+ VIRTIO_ID_9P,
+ sizeof(struct virtio_9p_config)+
+ MAX_TAG_LEN,
+ sizeof(V9fsState));
+
+ /* initialize pdu allocator */
+ QLIST_INIT(&s->free_list);
+ for (i = 0; i < (MAX_REQ - 1); i++) {
+ QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next);
+ }
+
+ s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output);
+
+ fse = get_fsdev_fsentry(conf->fsdev_id);
+
+ if (!fse) {
+ /* We don't have a fsdev identified by fsdev_id */
+ fprintf(stderr, "Virtio-9p device couldn't find fsdev with the "
+ "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL");
+ exit(1);
+ }
+
+ if (!fse->path || !conf->tag) {
+ /* we haven't specified a mount_tag or the path */
+ fprintf(stderr, "fsdev with id %s needs path "
+ "and Virtio-9p device needs mount_tag arguments\n",
+ conf->fsdev_id);
+ exit(1);
+ }
+
+ if (!strcmp(fse->security_model, "passthrough")) {
+ /* Files on the Fileserver set to client user credentials */
+ s->ctx.fs_sm = SM_PASSTHROUGH;
+ s->ctx.xops = passthrough_xattr_ops;
+ } else if (!strcmp(fse->security_model, "mapped")) {
+ /* Files on the fileserver are set to QEMU credentials.
+ * Client user credentials are saved in extended attributes.
+ */
+ s->ctx.fs_sm = SM_MAPPED;
+ s->ctx.xops = mapped_xattr_ops;
+ } else if (!strcmp(fse->security_model, "none")) {
+ /*
+ * Files on the fileserver are set to QEMU credentials.
+ */
+ s->ctx.fs_sm = SM_NONE;
+ s->ctx.xops = none_xattr_ops;
+ } else {
+ fprintf(stderr, "Default to security_model=none. You may want"
+ " enable advanced security model using "
+ "security option:\n\t security_model=passthrough \n\t "
+ "security_model=mapped\n");
+ s->ctx.fs_sm = SM_NONE;
+ s->ctx.xops = none_xattr_ops;
+ }
+
+ if (lstat(fse->path, &stat)) {
+ fprintf(stderr, "share path %s does not exist\n", fse->path);
+ exit(1);
+ } else if (!S_ISDIR(stat.st_mode)) {
+ fprintf(stderr, "share path %s is not a directory \n", fse->path);
+ exit(1);
+ }
+
+ s->ctx.fs_root = qemu_strdup(fse->path);
+ len = strlen(conf->tag);
+ if (len > MAX_TAG_LEN) {
+ len = MAX_TAG_LEN;
+ }
+ /* s->tag is non-NULL terminated string */
+ s->tag = qemu_malloc(len);
+ memcpy(s->tag, conf->tag, len);
+ s->tag_len = len;
+ s->ctx.uid = -1;
+
+ s->ops = fse->ops;
+ s->vdev.get_features = virtio_9p_get_features;
+ s->config_size = sizeof(struct virtio_9p_config) +
+ s->tag_len;
+ s->vdev.get_config = virtio_9p_get_config;
+
+ return &s->vdev;
+}
diff --git a/hw/virtio-9p.h b/hw/virtio-9p.h
new file mode 100644
index 0000000..2ae4ce7
--- /dev/null
+++ b/hw/virtio-9p.h
@@ -0,0 +1,507 @@
+#ifndef _QEMU_VIRTIO_9P_H
+#define _QEMU_VIRTIO_9P_H
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <utime.h>
+
+#include "file-op-9p.h"
+
+/* The feature bitmap for virtio 9P */
+/* The mount point is specified in a config variable */
+#define VIRTIO_9P_MOUNT_TAG 0
+
+enum {
+ P9_TLERROR = 6,
+ P9_RLERROR,
+ P9_TSTATFS = 8,
+ P9_RSTATFS,
+ P9_TLOPEN = 12,
+ P9_RLOPEN,
+ P9_TLCREATE = 14,
+ P9_RLCREATE,
+ P9_TSYMLINK = 16,
+ P9_RSYMLINK,
+ P9_TMKNOD = 18,
+ P9_RMKNOD,
+ P9_TRENAME = 20,
+ P9_RRENAME,
+ P9_TREADLINK = 22,
+ P9_RREADLINK,
+ P9_TGETATTR = 24,
+ P9_RGETATTR,
+ P9_TSETATTR = 26,
+ P9_RSETATTR,
+ P9_TXATTRWALK = 30,
+ P9_RXATTRWALK,
+ P9_TXATTRCREATE = 32,
+ P9_RXATTRCREATE,
+ P9_TREADDIR = 40,
+ P9_RREADDIR,
+ P9_TFSYNC = 50,
+ P9_RFSYNC,
+ P9_TLOCK = 52,
+ P9_RLOCK,
+ P9_TGETLOCK = 54,
+ P9_RGETLOCK,
+ P9_TLINK = 70,
+ P9_RLINK,
+ P9_TMKDIR = 72,
+ P9_RMKDIR,
+ P9_TVERSION = 100,
+ P9_RVERSION,
+ P9_TAUTH = 102,
+ P9_RAUTH,
+ P9_TATTACH = 104,
+ P9_RATTACH,
+ P9_TERROR = 106,
+ P9_RERROR,
+ P9_TFLUSH = 108,
+ P9_RFLUSH,
+ P9_TWALK = 110,
+ P9_RWALK,
+ P9_TOPEN = 112,
+ P9_ROPEN,
+ P9_TCREATE = 114,
+ P9_RCREATE,
+ P9_TREAD = 116,
+ P9_RREAD,
+ P9_TWRITE = 118,
+ P9_RWRITE,
+ P9_TCLUNK = 120,
+ P9_RCLUNK,
+ P9_TREMOVE = 122,
+ P9_RREMOVE,
+ P9_TSTAT = 124,
+ P9_RSTAT,
+ P9_TWSTAT = 126,
+ P9_RWSTAT,
+};
+
+
+/* qid.types */
+enum {
+ P9_QTDIR = 0x80,
+ P9_QTAPPEND = 0x40,
+ P9_QTEXCL = 0x20,
+ P9_QTMOUNT = 0x10,
+ P9_QTAUTH = 0x08,
+ P9_QTTMP = 0x04,
+ P9_QTSYMLINK = 0x02,
+ P9_QTLINK = 0x01,
+ P9_QTFILE = 0x00,
+};
+
+enum p9_proto_version {
+ V9FS_PROTO_2000U = 0x01,
+ V9FS_PROTO_2000L = 0x02,
+};
+
+#define P9_NOTAG (u16)(~0)
+#define P9_NOFID (u32)(~0)
+#define P9_MAXWELEM 16
+
+/*
+ * ample room for Twrite/Rread header
+ * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4]
+ */
+#define P9_IOHDRSZ 24
+
+typedef struct V9fsPDU V9fsPDU;
+
+struct V9fsPDU
+{
+ uint32_t size;
+ uint16_t tag;
+ uint8_t id;
+ VirtQueueElement elem;
+ QLIST_ENTRY(V9fsPDU) next;
+};
+
+
+/* FIXME
+ * 1) change user needs to set groups and stuff
+ */
+
+/* from Linux's linux/virtio_9p.h */
+
+/* The ID for virtio console */
+#define VIRTIO_ID_9P 9
+#define MAX_REQ 128
+#define MAX_TAG_LEN 32
+
+#define BUG_ON(cond) assert(!(cond))
+
+typedef struct V9fsFidState V9fsFidState;
+
+typedef struct V9fsString
+{
+ int16_t size;
+ char *data;
+} V9fsString;
+
+typedef struct V9fsQID
+{
+ int8_t type;
+ int32_t version;
+ int64_t path;
+} V9fsQID;
+
+typedef struct V9fsStat
+{
+ int16_t size;
+ int16_t type;
+ int32_t dev;
+ V9fsQID qid;
+ int32_t mode;
+ int32_t atime;
+ int32_t mtime;
+ int64_t length;
+ V9fsString name;
+ V9fsString uid;
+ V9fsString gid;
+ V9fsString muid;
+ /* 9p2000.u */
+ V9fsString extension;
+ int32_t n_uid;
+ int32_t n_gid;
+ int32_t n_muid;
+} V9fsStat;
+
+enum {
+ P9_FID_NONE = 0,
+ P9_FID_FILE,
+ P9_FID_DIR,
+ P9_FID_XATTR,
+};
+
+typedef struct V9fsXattr
+{
+ int64_t copied_len;
+ int64_t len;
+ void *value;
+ V9fsString name;
+ int flags;
+} V9fsXattr;
+
+struct V9fsFidState
+{
+ int fid_type;
+ int32_t fid;
+ V9fsString path;
+ union {
+ int fd;
+ DIR *dir;
+ V9fsXattr xattr;
+ } fs;
+ uid_t uid;
+ V9fsFidState *next;
+};
+
+typedef struct V9fsState
+{
+ VirtIODevice vdev;
+ VirtQueue *vq;
+ V9fsPDU pdus[MAX_REQ];
+ QLIST_HEAD(, V9fsPDU) free_list;
+ V9fsFidState *fid_list;
+ FileOperations *ops;
+ FsContext ctx;
+ uint16_t tag_len;
+ uint8_t *tag;
+ size_t config_size;
+ enum p9_proto_version proto_version;
+ int32_t msize;
+} V9fsState;
+
+typedef struct V9fsCreateState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ int32_t perm;
+ int8_t mode;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString extension;
+ V9fsString fullname;
+ int iounit;
+} V9fsCreateState;
+
+typedef struct V9fsLcreateState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ int32_t iounit;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString fullname;
+} V9fsLcreateState;
+
+typedef struct V9fsStatState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsStat v9stat;
+ V9fsFidState *fidp;
+ struct stat stbuf;
+} V9fsStatState;
+
+typedef struct V9fsStatDotl {
+ uint64_t st_result_mask;
+ V9fsQID qid;
+ uint32_t st_mode;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_nlink;
+ uint64_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint64_t st_atime_sec;
+ uint64_t st_atime_nsec;
+ uint64_t st_mtime_sec;
+ uint64_t st_mtime_nsec;
+ uint64_t st_ctime_sec;
+ uint64_t st_ctime_nsec;
+ uint64_t st_btime_sec;
+ uint64_t st_btime_nsec;
+ uint64_t st_gen;
+ uint64_t st_data_version;
+} V9fsStatDotl;
+
+typedef struct V9fsStatStateDotl {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsStatDotl v9stat_dotl;
+ struct stat stbuf;
+} V9fsStatStateDotl;
+
+
+typedef struct V9fsWalkState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int16_t nwnames;
+ int name_idx;
+ V9fsQID *qids;
+ V9fsFidState *fidp;
+ V9fsFidState *newfidp;
+ V9fsString path;
+ V9fsString *wnames;
+ struct stat stbuf;
+} V9fsWalkState;
+
+typedef struct V9fsOpenState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int32_t mode;
+ V9fsFidState *fidp;
+ V9fsQID qid;
+ struct stat stbuf;
+ int iounit;
+} V9fsOpenState;
+
+typedef struct V9fsReadState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int32_t count;
+ int32_t total;
+ int64_t off;
+ V9fsFidState *fidp;
+ struct iovec iov[128]; /* FIXME: bad, bad, bad */
+ struct iovec *sg;
+ off_t dir_pos;
+ struct dirent *dent;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsStat v9stat;
+ int32_t len;
+ int32_t cnt;
+ int32_t max_count;
+} V9fsReadState;
+
+typedef struct V9fsWriteState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int32_t len;
+ int32_t count;
+ int32_t total;
+ int64_t off;
+ V9fsFidState *fidp;
+ struct iovec iov[128]; /* FIXME: bad, bad, bad */
+ struct iovec *sg;
+ int cnt;
+} V9fsWriteState;
+
+typedef struct V9fsRemoveState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *fidp;
+} V9fsRemoveState;
+
+typedef struct V9fsWstatState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ int16_t unused;
+ V9fsStat v9stat;
+ V9fsFidState *fidp;
+ struct stat stbuf;
+} V9fsWstatState;
+
+typedef struct V9fsSymlinkState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsString name;
+ V9fsString symname;
+ V9fsString fullname;
+ V9fsFidState *dfidp;
+ V9fsQID qid;
+ struct stat stbuf;
+} V9fsSymlinkState;
+
+typedef struct V9fsIattr
+{
+ int32_t valid;
+ int32_t mode;
+ int32_t uid;
+ int32_t gid;
+ int64_t size;
+ int64_t atime_sec;
+ int64_t atime_nsec;
+ int64_t mtime_sec;
+ int64_t mtime_nsec;
+} V9fsIattr;
+
+typedef struct V9fsSetattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsIattr v9iattr;
+ V9fsFidState *fidp;
+} V9fsSetattrState;
+
+struct virtio_9p_config
+{
+ /* number of characters in tag */
+ uint16_t tag_len;
+ /* Variable size tag name */
+ uint8_t tag[0];
+} __attribute__((packed));
+
+typedef struct V9fsStatfs
+{
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_bavail;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ uint64_t fsid_val;
+ uint32_t f_namelen;
+} V9fsStatfs;
+
+typedef struct V9fsStatfsState {
+ V9fsPDU *pdu;
+ size_t offset;
+ int32_t fid;
+ V9fsStatfs v9statfs;
+ V9fsFidState *fidp;
+ struct statfs stbuf;
+} V9fsStatfsState;
+
+typedef struct V9fsMkState {
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsQID qid;
+ struct stat stbuf;
+ V9fsString name;
+ V9fsString fullname;
+} V9fsMkState;
+
+typedef struct V9fsRenameState {
+ V9fsPDU *pdu;
+ V9fsFidState *fidp;
+ size_t offset;
+ int32_t newdirfid;
+ V9fsString name;
+} V9fsRenameState;
+
+typedef struct V9fsXattrState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsFidState *file_fidp;
+ V9fsFidState *xattr_fidp;
+ V9fsString name;
+ int64_t size;
+ int flags;
+ void *value;
+} V9fsXattrState;
+
+#define P9_LOCK_SUCCESS 0
+#define P9_LOCK_BLOCKED 1
+#define P9_LOCK_ERROR 2
+#define P9_LOCK_GRACE 3
+
+#define P9_LOCK_FLAGS_BLOCK 1
+#define P9_LOCK_FLAGS_RECLAIM 2
+
+typedef struct V9fsFlock
+{
+ uint8_t type;
+ uint32_t flags;
+ uint64_t start; /* absolute offset */
+ uint64_t length;
+ uint32_t proc_id;
+ V9fsString client_id;
+} V9fsFlock;
+
+typedef struct V9fsLockState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ int8_t status;
+ struct stat stbuf;
+ V9fsFidState *fidp;
+ V9fsFlock *flock;
+} V9fsLockState;
+
+typedef struct V9fsGetlock
+{
+ uint8_t type;
+ uint64_t start; /* absolute offset */
+ uint64_t length;
+ uint32_t proc_id;
+ V9fsString client_id;
+} V9fsGetlock;
+
+typedef struct V9fsGetlockState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ struct stat stbuf;
+ V9fsFidState *fidp;
+ V9fsGetlock *glock;
+} V9fsGetlockState;
+
+typedef struct V9fsReadLinkState
+{
+ V9fsPDU *pdu;
+ size_t offset;
+ V9fsString target;
+} V9fsReadLinkState;
+
+size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
+ size_t offset, size_t size, int pack);
+
+static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
+ size_t offset, size_t size)
+{
+ return pdu_packunpack(dst, sg, sg_count, offset, size, 0);
+}
+
+#endif
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
new file mode 100644
index 0000000..1e6be18
--- /dev/null
+++ b/hw/virtio-balloon.c
@@ -0,0 +1,284 @@
+/*
+ * Virtio Block Device
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "iov.h"
+#include "qemu-common.h"
+#include "virtio.h"
+#include "pc.h"
+#include "sysemu.h"
+#include "cpu.h"
+#include "monitor.h"
+#include "balloon.h"
+#include "virtio-balloon.h"
+#include "kvm.h"
+#include "qemu-kvm.h"
+#include "qlist.h"
+#include "qint.h"
+#include "qstring.h"
+
+#if defined(__linux__)
+#include <sys/mman.h>
+#endif
+
+/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */
+#define ENABLE_GUEST_STATS 0
+
+
+typedef struct VirtIOBalloon
+{
+ VirtIODevice vdev;
+ VirtQueue *ivq, *dvq, *svq;
+ uint32_t num_pages;
+ uint32_t actual;
+ uint64_t stats[VIRTIO_BALLOON_S_NR];
+ VirtQueueElement stats_vq_elem;
+ size_t stats_vq_offset;
+ MonitorCompletion *stats_callback;
+ void *stats_opaque_callback_data;
+} VirtIOBalloon;
+
+static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev)
+{
+ return (VirtIOBalloon *)vdev;
+}
+
+static void balloon_page(void *addr, int deflate)
+{
+#if defined(__linux__)
+ if (!kvm_enabled() || kvm_has_sync_mmu())
+ qemu_madvise(addr, TARGET_PAGE_SIZE,
+ deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
+#endif
+}
+
+/*
+ * reset_stats - Mark all items in the stats array as unset
+ *
+ * This function needs to be called at device intialization and before
+ * before updating to a set of newly-generated stats. This will ensure that no
+ * stale values stick around in case the guest reports a subset of the supported
+ * statistics.
+ */
+static inline void reset_stats(VirtIOBalloon *dev)
+{
+ int i;
+ for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1);
+}
+
+static void stat_put(QDict *dict, const char *label, uint64_t val)
+{
+ if (val != -1)
+ qdict_put(dict, label, qint_from_int(val));
+}
+
+static QObject *get_stats_qobject(VirtIOBalloon *dev)
+{
+ QDict *dict = qdict_new();
+ uint64_t actual = ram_size - ((uint64_t) dev->actual <<
+ VIRTIO_BALLOON_PFN_SHIFT);
+
+ stat_put(dict, "actual", actual);
+#if ENABLE_GUEST_STATS
+ stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]);
+ stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]);
+ stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]);
+ stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]);
+ stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]);
+ stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]);
+#endif
+
+ return QOBJECT(dict);
+}
+
+static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOBalloon *s = to_virtio_balloon(vdev);
+ VirtQueueElement elem;
+
+ while (virtqueue_pop(vq, &elem)) {
+ size_t offset = 0;
+ uint32_t pfn;
+
+ while (iov_to_buf(elem.out_sg, elem.out_num, &pfn, offset, 4) == 4) {
+ ram_addr_t pa;
+ ram_addr_t addr;
+
+ pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT;
+ offset += 4;
+
+ addr = cpu_get_physical_page_desc(pa);
+ if ((addr & ~TARGET_PAGE_MASK) != IO_MEM_RAM)
+ continue;
+
+ /* Using qemu_get_ram_ptr is bending the rules a bit, but
+ should be OK because we only want a single page. */
+ balloon_page(qemu_get_ram_ptr(addr), !!(vq == s->dvq));
+ }
+
+ virtqueue_push(vq, &elem, offset);
+ virtio_notify(vdev, vq);
+ }
+}
+
+static void complete_stats_request(VirtIOBalloon *vb)
+{
+ QObject *stats;
+
+ if (!vb->stats_opaque_callback_data)
+ return;
+
+ stats = get_stats_qobject(vb);
+ vb->stats_callback(vb->stats_opaque_callback_data, stats);
+ qobject_decref(stats);
+ vb->stats_opaque_callback_data = NULL;
+ vb->stats_callback = NULL;
+}
+
+static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
+ VirtQueueElement *elem = &s->stats_vq_elem;
+ VirtIOBalloonStat stat;
+ size_t offset = 0;
+
+ if (!virtqueue_pop(vq, elem)) {
+ return;
+ }
+
+ /* Initialize the stats to get rid of any stale values. This is only
+ * needed to handle the case where a guest supports fewer stats than it
+ * used to (ie. it has booted into an old kernel).
+ */
+ reset_stats(s);
+
+ while (iov_to_buf(elem->out_sg, elem->out_num, &stat, offset, sizeof(stat))
+ == sizeof(stat)) {
+ uint16_t tag = tswap16(stat.tag);
+ uint64_t val = tswap64(stat.val);
+
+ offset += sizeof(stat);
+ if (tag < VIRTIO_BALLOON_S_NR)
+ s->stats[tag] = val;
+ }
+ s->stats_vq_offset = offset;
+
+ complete_stats_request(s);
+}
+
+static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
+{
+ VirtIOBalloon *dev = to_virtio_balloon(vdev);
+ struct virtio_balloon_config config;
+
+ config.num_pages = cpu_to_le32(dev->num_pages);
+ config.actual = cpu_to_le32(dev->actual);
+
+ memcpy(config_data, &config, 8);
+}
+
+static void virtio_balloon_set_config(VirtIODevice *vdev,
+ const uint8_t *config_data)
+{
+ VirtIOBalloon *dev = to_virtio_balloon(vdev);
+ struct virtio_balloon_config config;
+ memcpy(&config, config_data, 8);
+ dev->actual = config.actual;
+}
+
+static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
+{
+ f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
+ return f;
+}
+
+static void virtio_balloon_to_target(void *opaque, ram_addr_t target,
+ MonitorCompletion cb, void *cb_data)
+{
+ VirtIOBalloon *dev = opaque;
+
+ if (target > ram_size)
+ target = ram_size;
+
+ if (target) {
+ dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT;
+ virtio_notify_config(&dev->vdev);
+ } else {
+ /* For now, only allow one request at a time. This restriction can be
+ * removed later by queueing callback and data pairs.
+ */
+ if (dev->stats_callback != NULL) {
+ return;
+ }
+ dev->stats_callback = cb;
+ dev->stats_opaque_callback_data = cb_data;
+ if (ENABLE_GUEST_STATS && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) {
+ virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset);
+ virtio_notify(&dev->vdev, dev->svq);
+ } else {
+ /* Stats are not supported. Clear out any stale values that might
+ * have been set by a more featureful guest kernel.
+ */
+ reset_stats(dev);
+ complete_stats_request(dev);
+ }
+ }
+}
+
+static void virtio_balloon_save(QEMUFile *f, void *opaque)
+{
+ VirtIOBalloon *s = opaque;
+
+ virtio_save(&s->vdev, f);
+
+ qemu_put_be32(f, s->num_pages);
+ qemu_put_be32(f, s->actual);
+}
+
+static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIOBalloon *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ virtio_load(&s->vdev, f);
+
+ s->num_pages = qemu_get_be32(f);
+ s->actual = qemu_get_be32(f);
+ return 0;
+}
+
+VirtIODevice *virtio_balloon_init(DeviceState *dev)
+{
+ VirtIOBalloon *s;
+
+ s = (VirtIOBalloon *)virtio_common_init("virtio-balloon",
+ VIRTIO_ID_BALLOON,
+ 8, sizeof(VirtIOBalloon));
+
+ s->vdev.get_config = virtio_balloon_get_config;
+ s->vdev.set_config = virtio_balloon_set_config;
+ s->vdev.get_features = virtio_balloon_get_features;
+
+ s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
+ s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output);
+ s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats);
+
+ reset_stats(s);
+ qemu_add_balloon_handler(virtio_balloon_to_target, s);
+
+ register_savevm(dev, "virtio-balloon", -1, 1,
+ virtio_balloon_save, virtio_balloon_load, s);
+
+ return &s->vdev;
+}
diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h
new file mode 100644
index 0000000..e20cf6b
--- /dev/null
+++ b/hw/virtio-balloon.h
@@ -0,0 +1,55 @@
+/*
+ * Virtio Support
+ *
+ * Copyright IBM, Corp. 2007-2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Rusty Russell <rusty@rustcorp.com.au>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VIRTIO_BALLOON_H
+#define _QEMU_VIRTIO_BALLOON_H
+
+#include "virtio.h"
+#include "pci.h"
+
+/* from Linux's linux/virtio_balloon.h */
+
+/* The ID for virtio_balloon */
+#define VIRTIO_ID_BALLOON 5
+
+/* The feature bitmap for virtio balloon */
+#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
+#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory stats virtqueue */
+
+/* Size of a PFN in the balloon interface. */
+#define VIRTIO_BALLOON_PFN_SHIFT 12
+
+struct virtio_balloon_config
+{
+ /* Number of pages host wants Guest to give up. */
+ uint32_t num_pages;
+ /* Number of pages we've actually got in balloon. */
+ uint32_t actual;
+};
+
+/* Memory Statistics */
+#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */
+#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */
+#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */
+#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */
+#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */
+#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */
+#define VIRTIO_BALLOON_S_NR 6
+
+typedef struct VirtIOBalloonStat {
+ uint16_t tag;
+ uint64_t val;
+} __attribute__((packed)) VirtIOBalloonStat;
+
+#endif
diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c
new file mode 100644
index 0000000..114c638
--- /dev/null
+++ b/hw/virtio-blk.c
@@ -0,0 +1,588 @@
+/*
+ * Virtio Block Device
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <qemu-common.h>
+#include "qemu-error.h"
+#include "trace.h"
+#include "blockdev.h"
+#include "virtio-blk.h"
+#ifdef __linux__
+# include <scsi/sg.h>
+#endif
+
+typedef struct VirtIOBlock
+{
+ VirtIODevice vdev;
+ BlockDriverState *bs;
+ VirtQueue *vq;
+ void *rq;
+ QEMUBH *bh;
+ BlockConf *conf;
+ unsigned short sector_mask;
+ char sn[BLOCK_SERIAL_STRLEN];
+ DeviceState *qdev;
+} VirtIOBlock;
+
+static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev)
+{
+ return (VirtIOBlock *)vdev;
+}
+
+typedef struct VirtIOBlockReq
+{
+ VirtIOBlock *dev;
+ VirtQueueElement elem;
+ struct virtio_blk_inhdr *in;
+ struct virtio_blk_outhdr *out;
+ struct virtio_scsi_inhdr *scsi;
+ QEMUIOVector qiov;
+ struct VirtIOBlockReq *next;
+} VirtIOBlockReq;
+
+static void virtio_blk_req_complete(VirtIOBlockReq *req, int status)
+{
+ VirtIOBlock *s = req->dev;
+
+ trace_virtio_blk_req_complete(req, status);
+
+ stb_p(&req->in->status, status);
+ virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in));
+ virtio_notify(&s->vdev, s->vq);
+
+ qemu_free(req);
+}
+
+static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
+ int is_read)
+{
+ BlockErrorAction action = bdrv_get_on_error(req->dev->bs, is_read);
+ VirtIOBlock *s = req->dev;
+
+ if (action == BLOCK_ERR_IGNORE) {
+ bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ return 0;
+ }
+
+ if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
+ || action == BLOCK_ERR_STOP_ANY) {
+ req->next = s->rq;
+ s->rq = req;
+ bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ vm_stop(0);
+ } else {
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ }
+
+ return 1;
+}
+
+static void virtio_blk_rw_complete(void *opaque, int ret)
+{
+ VirtIOBlockReq *req = opaque;
+
+ trace_virtio_blk_rw_complete(req, ret);
+
+ if (ret) {
+ int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT);
+ if (virtio_blk_handle_rw_error(req, -ret, is_read))
+ return;
+ }
+
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
+}
+
+static void virtio_blk_flush_complete(void *opaque, int ret)
+{
+ VirtIOBlockReq *req = opaque;
+
+ if (ret) {
+ if (virtio_blk_handle_rw_error(req, -ret, 0)) {
+ return;
+ }
+ }
+
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
+}
+
+static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s)
+{
+ VirtIOBlockReq *req = qemu_malloc(sizeof(*req));
+ req->dev = s;
+ req->qiov.size = 0;
+ req->next = NULL;
+ return req;
+}
+
+static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s)
+{
+ VirtIOBlockReq *req = virtio_blk_alloc_request(s);
+
+ if (req != NULL) {
+ if (!virtqueue_pop(s->vq, &req->elem)) {
+ qemu_free(req);
+ return NULL;
+ }
+ }
+
+ return req;
+}
+
+#ifdef __linux__
+static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
+{
+ struct sg_io_hdr hdr;
+ int ret;
+ int status;
+ int i;
+
+ /*
+ * We require at least one output segment each for the virtio_blk_outhdr
+ * and the SCSI command block.
+ *
+ * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr
+ * and the sense buffer pointer in the input segments.
+ */
+ if (req->elem.out_num < 2 || req->elem.in_num < 3) {
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR);
+ return;
+ }
+
+ /*
+ * No support for bidirection commands yet.
+ */
+ if (req->elem.out_num > 2 && req->elem.in_num > 3) {
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
+ return;
+ }
+
+ /*
+ * The scsi inhdr is placed in the second-to-last input segment, just
+ * before the regular inhdr.
+ */
+ req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base;
+
+ memset(&hdr, 0, sizeof(struct sg_io_hdr));
+ hdr.interface_id = 'S';
+ hdr.cmd_len = req->elem.out_sg[1].iov_len;
+ hdr.cmdp = req->elem.out_sg[1].iov_base;
+ hdr.dxfer_len = 0;
+
+ if (req->elem.out_num > 2) {
+ /*
+ * If there are more than the minimally required 2 output segments
+ * there is write payload starting from the third iovec.
+ */
+ hdr.dxfer_direction = SG_DXFER_TO_DEV;
+ hdr.iovec_count = req->elem.out_num - 2;
+
+ for (i = 0; i < hdr.iovec_count; i++)
+ hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len;
+
+ hdr.dxferp = req->elem.out_sg + 2;
+
+ } else if (req->elem.in_num > 3) {
+ /*
+ * If we have more than 3 input segments the guest wants to actually
+ * read data.
+ */
+ hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ hdr.iovec_count = req->elem.in_num - 3;
+ for (i = 0; i < hdr.iovec_count; i++)
+ hdr.dxfer_len += req->elem.in_sg[i].iov_len;
+
+ hdr.dxferp = req->elem.in_sg;
+ } else {
+ /*
+ * Some SCSI commands don't actually transfer any data.
+ */
+ hdr.dxfer_direction = SG_DXFER_NONE;
+ }
+
+ hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base;
+ hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len;
+
+ ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr);
+ if (ret) {
+ status = VIRTIO_BLK_S_UNSUPP;
+ hdr.status = ret;
+ hdr.resid = hdr.dxfer_len;
+ } else if (hdr.status) {
+ status = VIRTIO_BLK_S_IOERR;
+ } else {
+ status = VIRTIO_BLK_S_OK;
+ }
+
+ stl_p(&req->scsi->errors, hdr.status);
+ stl_p(&req->scsi->residual, hdr.resid);
+ stl_p(&req->scsi->sense_len, hdr.sb_len_wr);
+ stl_p(&req->scsi->data_len, hdr.dxfer_len);
+
+ virtio_blk_req_complete(req, status);
+}
+#else
+static void virtio_blk_handle_scsi(VirtIOBlockReq *req)
+{
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
+}
+#endif /* __linux__ */
+
+typedef struct MultiReqBuffer {
+ BlockRequest blkreq[32];
+ unsigned int num_writes;
+} MultiReqBuffer;
+
+static void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb)
+{
+ int i, ret;
+
+ if (!mrb->num_writes) {
+ return;
+ }
+
+ ret = bdrv_aio_multiwrite(bs, mrb->blkreq, mrb->num_writes);
+ if (ret != 0) {
+ for (i = 0; i < mrb->num_writes; i++) {
+ if (mrb->blkreq[i].error) {
+ virtio_blk_rw_complete(mrb->blkreq[i].opaque, -EIO);
+ }
+ }
+ }
+
+ mrb->num_writes = 0;
+}
+
+static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb)
+{
+ BlockDriverAIOCB *acb;
+
+ /*
+ * Make sure all outstanding writes are posted to the backing device.
+ */
+ virtio_submit_multiwrite(req->dev->bs, mrb);
+
+ acb = bdrv_aio_flush(req->dev->bs, virtio_blk_flush_complete, req);
+ if (!acb) {
+ virtio_blk_flush_complete(req, -EIO);
+ }
+}
+
+static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb)
+{
+ BlockRequest *blkreq;
+ uint64_t sector;
+
+ sector = ldq_p(&req->out->sector);
+
+ trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512);
+
+ if (sector & req->dev->sector_mask) {
+ virtio_blk_rw_complete(req, -EIO);
+ return;
+ }
+ if (req->qiov.size % req->dev->conf->logical_block_size) {
+ virtio_blk_rw_complete(req, -EIO);
+ return;
+ }
+
+ if (mrb->num_writes == 32) {
+ virtio_submit_multiwrite(req->dev->bs, mrb);
+ }
+
+ blkreq = &mrb->blkreq[mrb->num_writes];
+ blkreq->sector = sector;
+ blkreq->nb_sectors = req->qiov.size / BDRV_SECTOR_SIZE;
+ blkreq->qiov = &req->qiov;
+ blkreq->cb = virtio_blk_rw_complete;
+ blkreq->opaque = req;
+ blkreq->error = 0;
+
+ mrb->num_writes++;
+}
+
+static void virtio_blk_handle_read(VirtIOBlockReq *req)
+{
+ BlockDriverAIOCB *acb;
+ uint64_t sector;
+
+ sector = ldq_p(&req->out->sector);
+
+ if (sector & req->dev->sector_mask) {
+ virtio_blk_rw_complete(req, -EIO);
+ return;
+ }
+ if (req->qiov.size % req->dev->conf->logical_block_size) {
+ virtio_blk_rw_complete(req, -EIO);
+ return;
+ }
+
+ acb = bdrv_aio_readv(req->dev->bs, sector, &req->qiov,
+ req->qiov.size / BDRV_SECTOR_SIZE,
+ virtio_blk_rw_complete, req);
+ if (!acb) {
+ virtio_blk_rw_complete(req, -EIO);
+ }
+}
+
+static void virtio_blk_handle_request(VirtIOBlockReq *req,
+ MultiReqBuffer *mrb)
+{
+ uint32_t type;
+
+ if (req->elem.out_num < 1 || req->elem.in_num < 1) {
+ error_report("virtio-blk missing headers");
+ exit(1);
+ }
+
+ if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
+ req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
+ error_report("virtio-blk header not in correct element");
+ exit(1);
+ }
+
+ req->out = (void *)req->elem.out_sg[0].iov_base;
+ req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;
+
+ type = ldl_p(&req->out->type);
+
+ if (type & VIRTIO_BLK_T_FLUSH) {
+ virtio_blk_handle_flush(req, mrb);
+ } else if (type & VIRTIO_BLK_T_SCSI_CMD) {
+ virtio_blk_handle_scsi(req);
+ } else if (type & VIRTIO_BLK_T_GET_ID) {
+ VirtIOBlock *s = req->dev;
+
+ memcpy(req->elem.in_sg[0].iov_base, s->sn,
+ MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn)));
+ virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
+ } else if (type & VIRTIO_BLK_T_OUT) {
+ qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
+ req->elem.out_num - 1);
+ virtio_blk_handle_write(req, mrb);
+ } else {
+ qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
+ req->elem.in_num - 1);
+ virtio_blk_handle_read(req);
+ }
+}
+
+static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+ VirtIOBlockReq *req;
+ MultiReqBuffer mrb = {
+ .num_writes = 0,
+ };
+
+ while ((req = virtio_blk_get_request(s))) {
+ virtio_blk_handle_request(req, &mrb);
+ }
+
+ virtio_submit_multiwrite(s->bs, &mrb);
+
+ /*
+ * FIXME: Want to check for completions before returning to guest mode,
+ * so cached reads and writes are reported as quickly as possible. But
+ * that should be done in the generic block layer.
+ */
+}
+
+static void virtio_blk_dma_restart_bh(void *opaque)
+{
+ VirtIOBlock *s = opaque;
+ VirtIOBlockReq *req = s->rq;
+ MultiReqBuffer mrb = {
+ .num_writes = 0,
+ };
+
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
+
+ s->rq = NULL;
+
+ while (req) {
+ virtio_blk_handle_request(req, &mrb);
+ req = req->next;
+ }
+
+ virtio_submit_multiwrite(s->bs, &mrb);
+}
+
+static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason)
+{
+ VirtIOBlock *s = opaque;
+
+ if (!running)
+ return;
+
+ if (!s->bh) {
+ s->bh = qemu_bh_new(virtio_blk_dma_restart_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
+static void virtio_blk_reset(VirtIODevice *vdev)
+{
+ /*
+ * This should cancel pending requests, but can't do nicely until there
+ * are per-device request lists.
+ */
+ qemu_aio_flush();
+}
+
+/* coalesce internal state, copy to pci i/o region 0
+ */
+static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+ struct virtio_blk_config blkcfg;
+ uint64_t capacity;
+ int cylinders, heads, secs;
+
+ bdrv_get_geometry(s->bs, &capacity);
+ bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
+ memset(&blkcfg, 0, sizeof(blkcfg));
+ stq_raw(&blkcfg.capacity, capacity);
+ stl_raw(&blkcfg.seg_max, 128 - 2);
+ stw_raw(&blkcfg.cylinders, cylinders);
+ blkcfg.heads = heads;
+ blkcfg.sectors = secs & ~s->sector_mask;
+ blkcfg.blk_size = s->conf->logical_block_size;
+ blkcfg.size_max = 0;
+ blkcfg.physical_block_exp = get_physical_block_exp(s->conf);
+ blkcfg.alignment_offset = 0;
+ blkcfg.min_io_size = s->conf->min_io_size / blkcfg.blk_size;
+ blkcfg.opt_io_size = s->conf->opt_io_size / blkcfg.blk_size;
+ memcpy(config, &blkcfg, sizeof(struct virtio_blk_config));
+}
+
+static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+
+ features |= (1 << VIRTIO_BLK_F_SEG_MAX);
+ features |= (1 << VIRTIO_BLK_F_GEOMETRY);
+ features |= (1 << VIRTIO_BLK_F_TOPOLOGY);
+ features |= (1 << VIRTIO_BLK_F_BLK_SIZE);
+
+ if (bdrv_enable_write_cache(s->bs))
+ features |= (1 << VIRTIO_BLK_F_WCACHE);
+
+ if (bdrv_is_read_only(s->bs))
+ features |= 1 << VIRTIO_BLK_F_RO;
+
+ return features;
+}
+
+static void virtio_blk_save(QEMUFile *f, void *opaque)
+{
+ VirtIOBlock *s = opaque;
+ VirtIOBlockReq *req = s->rq;
+
+ virtio_save(&s->vdev, f);
+
+ while (req) {
+ qemu_put_sbyte(f, 1);
+ qemu_put_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
+ req = req->next;
+ }
+ qemu_put_sbyte(f, 0);
+}
+
+static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIOBlock *s = opaque;
+
+ if (version_id != 2)
+ return -EINVAL;
+
+ virtio_load(&s->vdev, f);
+ while (qemu_get_sbyte(f)) {
+ VirtIOBlockReq *req = virtio_blk_alloc_request(s);
+ qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem));
+ req->next = s->rq;
+ s->rq = req;
+
+ virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr,
+ req->elem.in_num, 1);
+ virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr,
+ req->elem.out_num, 0);
+ }
+
+ return 0;
+}
+
+static void virtio_blk_change_cb(void *opaque, int reason)
+{
+ VirtIOBlock *s = opaque;
+
+ if (reason & CHANGE_SIZE) {
+ virtio_notify_config(&s->vdev);
+ }
+}
+
+VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf)
+{
+ VirtIOBlock *s;
+ int cylinders, heads, secs;
+ static int virtio_blk_id;
+ DriveInfo *dinfo;
+
+ if (!conf->bs) {
+ error_report("virtio-blk-pci: drive property not set");
+ return NULL;
+ }
+ if (!bdrv_is_inserted(conf->bs)) {
+ error_report("Device needs media, but drive is empty");
+ return NULL;
+ }
+
+ s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK,
+ sizeof(struct virtio_blk_config),
+ sizeof(VirtIOBlock));
+
+ s->vdev.get_config = virtio_blk_update_config;
+ s->vdev.get_features = virtio_blk_get_features;
+ s->vdev.reset = virtio_blk_reset;
+ s->bs = conf->bs;
+ s->conf = conf;
+ s->rq = NULL;
+ s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1;
+ bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs);
+
+ /* NB: per existing s/n string convention the string is terminated
+ * by '\0' only when less than sizeof (s->sn)
+ */
+ dinfo = drive_get_by_blockdev(s->bs);
+ strncpy(s->sn, dinfo->serial, sizeof (s->sn));
+
+ s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output);
+
+ qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
+ s->qdev = dev;
+ register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
+ virtio_blk_save, virtio_blk_load, s);
+ bdrv_set_removable(s->bs, 0);
+ bdrv_set_change_cb(s->bs, virtio_blk_change_cb, s);
+ s->bs->buffer_alignment = conf->logical_block_size;
+
+ add_boot_device_path(conf->bootindex, dev, "/disk@0,0");
+
+ return &s->vdev;
+}
+
+void virtio_blk_exit(VirtIODevice *vdev)
+{
+ VirtIOBlock *s = to_virtio_blk(vdev);
+ unregister_savevm(s->qdev, "virtio-blk", s);
+}
diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h
new file mode 100644
index 0000000..fff46da
--- /dev/null
+++ b/hw/virtio-blk.h
@@ -0,0 +1,106 @@
+/*
+ * Virtio Block Device
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VIRTIO_BLK_H
+#define _QEMU_VIRTIO_BLK_H
+
+#include "virtio.h"
+#include "block.h"
+
+/* from Linux's linux/virtio_blk.h */
+
+/* The ID for virtio_block */
+#define VIRTIO_ID_BLOCK 2
+
+/* Feature bits */
+#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */
+#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */
+#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */
+#define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */
+#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
+#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/
+#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
+/* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */
+#define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */
+#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */
+
+struct virtio_blk_config
+{
+ uint64_t capacity;
+ uint32_t size_max;
+ uint32_t seg_max;
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ uint32_t blk_size;
+ uint8_t physical_block_exp;
+ uint8_t alignment_offset;
+ uint16_t min_io_size;
+ uint32_t opt_io_size;
+} __attribute__((packed));
+
+/* These two define direction. */
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+
+/* This bit says it's a scsi command, not an actual read or write. */
+#define VIRTIO_BLK_T_SCSI_CMD 2
+
+/* Flush the volatile write cache */
+#define VIRTIO_BLK_T_FLUSH 4
+
+/* return the device ID string */
+#define VIRTIO_BLK_T_GET_ID 8
+
+/* Barrier before this op. */
+#define VIRTIO_BLK_T_BARRIER 0x80000000
+
+/* This is the first element of the read scatter-gather list. */
+struct virtio_blk_outhdr
+{
+ /* VIRTIO_BLK_T* */
+ uint32_t type;
+ /* io priority. */
+ uint32_t ioprio;
+ /* Sector (ie. 512 byte offset) */
+ uint64_t sector;
+};
+
+#define VIRTIO_BLK_S_OK 0
+#define VIRTIO_BLK_S_IOERR 1
+#define VIRTIO_BLK_S_UNSUPP 2
+
+/* This is the last element of the write scatter-gather list */
+struct virtio_blk_inhdr
+{
+ unsigned char status;
+};
+
+/* SCSI pass-through header */
+struct virtio_scsi_inhdr
+{
+ uint32_t errors;
+ uint32_t data_len;
+ uint32_t sense_len;
+ uint32_t residual;
+};
+
+#ifdef __linux__
+#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
+ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
+ DEFINE_PROP_BIT("scsi", _state, _field, VIRTIO_BLK_F_SCSI, true)
+#else
+#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \
+ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field)
+#endif
+#endif
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
new file mode 100644
index 0000000..62624ec
--- /dev/null
+++ b/hw/virtio-console.c
@@ -0,0 +1,141 @@
+/*
+ * Virtio Console and Generic Serial Port Devices
+ *
+ * Copyright Red Hat, Inc. 2009, 2010
+ *
+ * Authors:
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu-char.h"
+#include "virtio-serial.h"
+
+typedef struct VirtConsole {
+ VirtIOSerialPort port;
+ CharDriverState *chr;
+} VirtConsole;
+
+
+/* Callback function that's called when the guest sends us data */
+static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
+{
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+ return qemu_chr_write(vcon->chr, buf, len);
+}
+
+/* Readiness of the guest to accept data on a port */
+static int chr_can_read(void *opaque)
+{
+ VirtConsole *vcon = opaque;
+
+ return virtio_serial_guest_ready(&vcon->port);
+}
+
+/* Send data from a char device over to the guest */
+static void chr_read(void *opaque, const uint8_t *buf, int size)
+{
+ VirtConsole *vcon = opaque;
+
+ virtio_serial_write(&vcon->port, buf, size);
+}
+
+static void chr_event(void *opaque, int event)
+{
+ VirtConsole *vcon = opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ virtio_serial_open(&vcon->port);
+ break;
+ case CHR_EVENT_CLOSED:
+ virtio_serial_close(&vcon->port);
+ break;
+ }
+}
+
+static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev)
+{
+ vcon->port.info = 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;
+ }
+ return 0;
+}
+
+/* Virtio Console Ports */
+static int virtconsole_initfn(VirtIOSerialDevice *dev)
+{
+ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+ port->is_console = true;
+ return generic_port_init(vcon, dev);
+}
+
+static int virtconsole_exitfn(VirtIOSerialDevice *dev)
+{
+ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+ if (vcon->chr) {
+ port->info->have_data = NULL;
+ qemu_chr_close(vcon->chr);
+ }
+
+ return 0;
+}
+
+static VirtIOSerialPortInfo virtconsole_info = {
+ .qdev.name = "virtconsole",
+ .qdev.size = sizeof(VirtConsole),
+ .init = virtconsole_initfn,
+ .exit = virtconsole_exitfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console, 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),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void virtconsole_register(void)
+{
+ virtio_serial_port_qdev_register(&virtconsole_info);
+}
+device_init(virtconsole_register)
+
+/* Generic Virtio Serial Ports */
+static int virtserialport_initfn(VirtIOSerialDevice *dev)
+{
+ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+ VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+ return generic_port_init(vcon, dev);
+}
+
+static VirtIOSerialPortInfo virtserialport_info = {
+ .qdev.name = "virtserialport",
+ .qdev.size = sizeof(VirtConsole),
+ .init = virtserialport_initfn,
+ .exit = virtconsole_exitfn,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID),
+ DEFINE_PROP_CHR("chardev", VirtConsole, chr),
+ DEFINE_PROP_STRING("name", VirtConsole, port.name),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void virtserialport_register(void)
+{
+ virtio_serial_port_qdev_register(&virtserialport_info);
+}
+device_init(virtserialport_register)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
new file mode 100644
index 0000000..e9775a6
--- /dev/null
+++ b/hw/virtio-net.c
@@ -0,0 +1,1077 @@
+/*
+ * Virtio Network Device
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "iov.h"
+#include "virtio.h"
+#include "net.h"
+#include "net/checksum.h"
+#include "net/tap.h"
+#include "qemu-error.h"
+#include "qemu-timer.h"
+#include "virtio-net.h"
+#include "vhost_net.h"
+
+#define VIRTIO_NET_VM_VERSION 11
+
+#define MAC_TABLE_ENTRIES 64
+#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */
+
+typedef struct VirtIONet
+{
+ VirtIODevice vdev;
+ uint8_t mac[ETH_ALEN];
+ uint16_t status;
+ VirtQueue *rx_vq;
+ VirtQueue *tx_vq;
+ VirtQueue *ctrl_vq;
+ NICState *nic;
+ QEMUTimer *tx_timer;
+ QEMUBH *tx_bh;
+ uint32_t tx_timeout;
+ int32_t tx_burst;
+ int tx_waiting;
+ uint32_t has_vnet_hdr;
+ uint8_t has_ufo;
+ struct {
+ VirtQueueElement elem;
+ ssize_t len;
+ } async_tx;
+ int mergeable_rx_bufs;
+ uint8_t promisc;
+ uint8_t allmulti;
+ uint8_t alluni;
+ uint8_t nomulti;
+ uint8_t nouni;
+ uint8_t nobcast;
+ uint8_t vhost_started;
+ struct {
+ int in_use;
+ int first_multi;
+ uint8_t multi_overflow;
+ uint8_t uni_overflow;
+ uint8_t *macs;
+ } mac_table;
+ uint32_t *vlans;
+ DeviceState *qdev;
+} VirtIONet;
+
+/* TODO
+ * - we could suppress RX interrupt if we were so inclined.
+ */
+
+static VirtIONet *to_virtio_net(VirtIODevice *vdev)
+{
+ return (VirtIONet *)vdev;
+}
+
+static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+ struct virtio_net_config netcfg;
+
+ stw_p(&netcfg.status, n->status);
+ memcpy(netcfg.mac, n->mac, ETH_ALEN);
+ memcpy(config, &netcfg, sizeof(netcfg));
+}
+
+static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+ struct virtio_net_config netcfg;
+
+ memcpy(&netcfg, config, sizeof(netcfg));
+
+ if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
+ memcpy(n->mac, netcfg.mac, ETH_ALEN);
+ qemu_format_nic_info_str(&n->nic->nc, n->mac);
+ }
+}
+
+static bool virtio_net_started(VirtIONet *n, uint8_t status)
+{
+ return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running;
+}
+
+static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
+{
+ if (!n->nic->nc.peer) {
+ return;
+ }
+ if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ return;
+ }
+
+ if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ return;
+ }
+ if (!!n->vhost_started == virtio_net_started(n, status)) {
+ return;
+ }
+ if (!n->vhost_started) {
+ int r;
+ if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) {
+ return;
+ }
+ r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ if (r < 0) {
+ error_report("unable to start vhost net: %d: "
+ "falling back on userspace virtio", -r);
+ } else {
+ n->vhost_started = 1;
+ }
+ } else {
+ vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ n->vhost_started = 0;
+ }
+}
+
+static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ virtio_net_vhost_status(n, status);
+
+ if (!n->tx_waiting) {
+ return;
+ }
+
+ if (virtio_net_started(n, status) && !n->vhost_started) {
+ if (n->tx_timer) {
+ qemu_mod_timer(n->tx_timer,
+ qemu_get_clock(vm_clock) + n->tx_timeout);
+ } else {
+ qemu_bh_schedule(n->tx_bh);
+ }
+ } else {
+ if (n->tx_timer) {
+ qemu_del_timer(n->tx_timer);
+ } else {
+ qemu_bh_cancel(n->tx_bh);
+ }
+ }
+}
+
+static void virtio_net_set_link_status(VLANClientState *nc)
+{
+ VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ uint16_t old_status = n->status;
+
+ if (nc->link_down)
+ n->status &= ~VIRTIO_NET_S_LINK_UP;
+ else
+ n->status |= VIRTIO_NET_S_LINK_UP;
+
+ if (n->status != old_status)
+ virtio_notify_config(&n->vdev);
+
+ virtio_net_set_status(&n->vdev, n->vdev.status);
+}
+
+static void virtio_net_reset(VirtIODevice *vdev)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ /* Reset back to compatibility mode */
+ n->promisc = 1;
+ n->allmulti = 0;
+ n->alluni = 0;
+ n->nomulti = 0;
+ n->nouni = 0;
+ n->nobcast = 0;
+
+ /* Flush any MAC and VLAN filter table state */
+ n->mac_table.in_use = 0;
+ n->mac_table.first_multi = 0;
+ n->mac_table.multi_overflow = 0;
+ n->mac_table.uni_overflow = 0;
+ memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
+ memset(n->vlans, 0, MAX_VLAN >> 3);
+}
+
+static int peer_has_vnet_hdr(VirtIONet *n)
+{
+ if (!n->nic->nc.peer)
+ return 0;
+
+ if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP)
+ return 0;
+
+ n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer);
+
+ return n->has_vnet_hdr;
+}
+
+static int peer_has_ufo(VirtIONet *n)
+{
+ if (!peer_has_vnet_hdr(n))
+ return 0;
+
+ n->has_ufo = tap_has_ufo(n->nic->nc.peer);
+
+ return n->has_ufo;
+}
+
+static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ features |= (1 << VIRTIO_NET_F_MAC);
+
+ if (peer_has_vnet_hdr(n)) {
+ tap_using_vnet_hdr(n->nic->nc.peer, 1);
+ } else {
+ features &= ~(0x1 << VIRTIO_NET_F_CSUM);
+ features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4);
+ features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO6);
+ features &= ~(0x1 << VIRTIO_NET_F_HOST_ECN);
+
+ features &= ~(0x1 << VIRTIO_NET_F_GUEST_CSUM);
+ features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO4);
+ features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO6);
+ features &= ~(0x1 << VIRTIO_NET_F_GUEST_ECN);
+ }
+
+ if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) {
+ features &= ~(0x1 << VIRTIO_NET_F_GUEST_UFO);
+ features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO);
+ }
+
+ if (!n->nic->nc.peer ||
+ n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ return features;
+ }
+ if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ return features;
+ }
+ return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features);
+}
+
+static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
+{
+ uint32_t features = 0;
+
+ /* Linux kernel 2.6.25. It understood MAC (as everyone must),
+ * but also these: */
+ features |= (1 << VIRTIO_NET_F_MAC);
+ features |= (1 << VIRTIO_NET_F_CSUM);
+ features |= (1 << VIRTIO_NET_F_HOST_TSO4);
+ features |= (1 << VIRTIO_NET_F_HOST_TSO6);
+ features |= (1 << VIRTIO_NET_F_HOST_ECN);
+
+ return features;
+}
+
+static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
+
+ if (n->has_vnet_hdr) {
+ tap_set_offload(n->nic->nc.peer,
+ (features >> VIRTIO_NET_F_GUEST_CSUM) & 1,
+ (features >> VIRTIO_NET_F_GUEST_TSO4) & 1,
+ (features >> VIRTIO_NET_F_GUEST_TSO6) & 1,
+ (features >> VIRTIO_NET_F_GUEST_ECN) & 1,
+ (features >> VIRTIO_NET_F_GUEST_UFO) & 1);
+ }
+ if (!n->nic->nc.peer ||
+ n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ return;
+ }
+ if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ return;
+ }
+ vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features);
+}
+
+static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ uint8_t on;
+
+ if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) {
+ error_report("virtio-net ctrl invalid rx mode command");
+ exit(1);
+ }
+
+ on = ldub_p(elem->out_sg[1].iov_base);
+
+ if (cmd == VIRTIO_NET_CTRL_RX_MODE_PROMISC)
+ n->promisc = on;
+ else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI)
+ n->allmulti = on;
+ else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLUNI)
+ n->alluni = on;
+ else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOMULTI)
+ n->nomulti = on;
+ else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOUNI)
+ n->nouni = on;
+ else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOBCAST)
+ n->nobcast = on;
+ else
+ return VIRTIO_NET_ERR;
+
+ return VIRTIO_NET_OK;
+}
+
+static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ struct virtio_net_ctrl_mac mac_data;
+
+ if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 ||
+ elem->out_sg[1].iov_len < sizeof(mac_data) ||
+ elem->out_sg[2].iov_len < sizeof(mac_data))
+ return VIRTIO_NET_ERR;
+
+ n->mac_table.in_use = 0;
+ n->mac_table.first_multi = 0;
+ n->mac_table.uni_overflow = 0;
+ n->mac_table.multi_overflow = 0;
+ memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
+
+ mac_data.entries = ldl_p(elem->out_sg[1].iov_base);
+
+ if (sizeof(mac_data.entries) +
+ (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len)
+ return VIRTIO_NET_ERR;
+
+ if (mac_data.entries <= MAC_TABLE_ENTRIES) {
+ memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data),
+ mac_data.entries * ETH_ALEN);
+ n->mac_table.in_use += mac_data.entries;
+ } else {
+ n->mac_table.uni_overflow = 1;
+ }
+
+ n->mac_table.first_multi = n->mac_table.in_use;
+
+ mac_data.entries = ldl_p(elem->out_sg[2].iov_base);
+
+ if (sizeof(mac_data.entries) +
+ (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len)
+ return VIRTIO_NET_ERR;
+
+ if (mac_data.entries) {
+ if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
+ memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN),
+ elem->out_sg[2].iov_base + sizeof(mac_data),
+ mac_data.entries * ETH_ALEN);
+ n->mac_table.in_use += mac_data.entries;
+ } else {
+ n->mac_table.multi_overflow = 1;
+ }
+ }
+
+ return VIRTIO_NET_OK;
+}
+
+static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ uint16_t vid;
+
+ if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) {
+ error_report("virtio-net ctrl invalid vlan command");
+ return VIRTIO_NET_ERR;
+ }
+
+ vid = lduw_p(elem->out_sg[1].iov_base);
+
+ if (vid >= MAX_VLAN)
+ return VIRTIO_NET_ERR;
+
+ if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
+ n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
+ else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
+ n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
+ else
+ return VIRTIO_NET_ERR;
+
+ return VIRTIO_NET_OK;
+}
+
+static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+ struct virtio_net_ctrl_hdr ctrl;
+ virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
+ VirtQueueElement elem;
+
+ while (virtqueue_pop(vq, &elem)) {
+ if ((elem.in_num < 1) || (elem.out_num < 1)) {
+ error_report("virtio-net ctrl missing headers");
+ exit(1);
+ }
+
+ if (elem.out_sg[0].iov_len < sizeof(ctrl) ||
+ elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) {
+ error_report("virtio-net ctrl header not in correct element");
+ exit(1);
+ }
+
+ ctrl.class = ldub_p(elem.out_sg[0].iov_base);
+ ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class));
+
+ if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE)
+ status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem);
+ else if (ctrl.class == VIRTIO_NET_CTRL_MAC)
+ status = virtio_net_handle_mac(n, ctrl.cmd, &elem);
+ else if (ctrl.class == VIRTIO_NET_CTRL_VLAN)
+ status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem);
+
+ stb_p(elem.in_sg[elem.in_num - 1].iov_base, status);
+
+ virtqueue_push(vq, &elem, sizeof(status));
+ virtio_notify(vdev, vq);
+ }
+}
+
+/* RX */
+
+static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ qemu_flush_queued_packets(&n->nic->nc);
+
+ /* We now have RX buffers, signal to the IO thread to break out of the
+ * select to re-poll the tap file descriptor */
+ qemu_notify_event();
+}
+
+static int virtio_net_can_receive(VLANClientState *nc)
+{
+ VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ if (!n->vdev.vm_running) {
+ return 0;
+ }
+
+ if (!virtio_queue_ready(n->rx_vq) ||
+ !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
+ return 0;
+
+ return 1;
+}
+
+static int virtio_net_has_buffers(VirtIONet *n, int bufsize)
+{
+ if (virtio_queue_empty(n->rx_vq) ||
+ (n->mergeable_rx_bufs &&
+ !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) {
+ virtio_queue_set_notification(n->rx_vq, 1);
+
+ /* To avoid a race condition where the guest has made some buffers
+ * available after the above check but before notification was
+ * enabled, check for available buffers again.
+ */
+ if (virtio_queue_empty(n->rx_vq) ||
+ (n->mergeable_rx_bufs &&
+ !virtqueue_avail_bytes(n->rx_vq, bufsize, 0)))
+ return 0;
+ }
+
+ virtio_queue_set_notification(n->rx_vq, 0);
+ return 1;
+}
+
+/* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so
+ * it never finds out that the packets don't have valid checksums. This
+ * causes dhclient to get upset. Fedora's carried a patch for ages to
+ * fix this with Xen but it hasn't appeared in an upstream release of
+ * dhclient yet.
+ *
+ * To avoid breaking existing guests, we catch udp packets and add
+ * checksums. This is terrible but it's better than hacking the guest
+ * kernels.
+ *
+ * N.B. if we introduce a zero-copy API, this operation is no longer free so
+ * we should provide a mechanism to disable it to avoid polluting the host
+ * cache.
+ */
+static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
+ const uint8_t *buf, size_t size)
+{
+ if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
+ (size > 27 && size < 1500) && /* normal sized MTU */
+ (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
+ (buf[23] == 17) && /* ip.protocol == UDP */
+ (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
+ /* FIXME this cast is evil */
+ net_checksum_calculate((uint8_t *)buf, size);
+ hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
+ }
+}
+
+static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
+ const void *buf, size_t size, size_t hdr_len)
+{
+ struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)iov[0].iov_base;
+ int offset = 0;
+
+ hdr->flags = 0;
+ hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
+
+ if (n->has_vnet_hdr) {
+ memcpy(hdr, buf, sizeof(*hdr));
+ offset = sizeof(*hdr);
+ work_around_broken_dhclient(hdr, buf + offset, size - offset);
+ }
+
+ /* We only ever receive a struct virtio_net_hdr from the tapfd,
+ * but we may be passing along a larger header to the guest.
+ */
+ iov[0].iov_base += hdr_len;
+ iov[0].iov_len -= hdr_len;
+
+ return offset;
+}
+
+static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
+{
+ static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const uint8_t vlan[] = {0x81, 0x00};
+ uint8_t *ptr = (uint8_t *)buf;
+ int i;
+
+ if (n->promisc)
+ return 1;
+
+ if (n->has_vnet_hdr) {
+ ptr += sizeof(struct virtio_net_hdr);
+ }
+
+ if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
+ int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff;
+ if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
+ return 0;
+ }
+
+ if (ptr[0] & 1) { // multicast
+ if (!memcmp(ptr, bcast, sizeof(bcast))) {
+ return !n->nobcast;
+ } else if (n->nomulti) {
+ return 0;
+ } else if (n->allmulti || n->mac_table.multi_overflow) {
+ return 1;
+ }
+
+ for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
+ if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
+ return 1;
+ }
+ }
+ } else { // unicast
+ if (n->nouni) {
+ return 0;
+ } else if (n->alluni || n->mac_table.uni_overflow) {
+ return 1;
+ } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
+ return 1;
+ }
+
+ for (i = 0; i < n->mac_table.first_multi; i++) {
+ if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
+ size_t guest_hdr_len, offset, i, host_hdr_len;
+
+ if (!virtio_net_can_receive(&n->nic->nc))
+ return -1;
+
+ /* hdr_len refers to the header we supply to the guest */
+ guest_hdr_len = n->mergeable_rx_bufs ?
+ sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
+
+
+ host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
+ if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len))
+ return 0;
+
+ if (!receive_filter(n, buf, size))
+ return size;
+
+ offset = i = 0;
+
+ while (offset < size) {
+ VirtQueueElement elem;
+ int len, total;
+ struct iovec sg[VIRTQUEUE_MAX_SIZE];
+
+ total = 0;
+
+ if (virtqueue_pop(n->rx_vq, &elem) == 0) {
+ if (i == 0)
+ return -1;
+ error_report("virtio-net unexpected empty queue: "
+ "i %zd mergeable %d offset %zd, size %zd, "
+ "guest hdr len %zd, host hdr len %zd guest features 0x%x",
+ i, n->mergeable_rx_bufs, offset, size,
+ guest_hdr_len, host_hdr_len, n->vdev.guest_features);
+ exit(1);
+ }
+
+ if (elem.in_num < 1) {
+ error_report("virtio-net receive queue contains no in buffers");
+ exit(1);
+ }
+
+ if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != guest_hdr_len) {
+ error_report("virtio-net header not in first element");
+ exit(1);
+ }
+
+ memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num);
+
+ if (i == 0) {
+ if (n->mergeable_rx_bufs)
+ mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
+
+ offset += receive_header(n, sg, elem.in_num,
+ buf + offset, size - offset, guest_hdr_len);
+ total += guest_hdr_len;
+ }
+
+ /* copy in packet. ugh */
+ len = iov_from_buf(sg, elem.in_num,
+ buf + offset, size - offset);
+ total += len;
+ offset += len;
+ /* If buffers can't be merged, at this point we
+ * must have consumed the complete packet.
+ * Otherwise, drop it. */
+ if (!n->mergeable_rx_bufs && offset < size) {
+#if 0
+ error_report("virtio-net truncated non-mergeable packet: "
+ "i %zd mergeable %d offset %zd, size %zd, "
+ "guest hdr len %zd, host hdr len %zd",
+ i, n->mergeable_rx_bufs,
+ offset, size, guest_hdr_len, host_hdr_len);
+#endif
+ return size;
+ }
+
+ /* signal other side */
+ virtqueue_fill(n->rx_vq, &elem, total, i++);
+ }
+
+ if (mhdr) {
+ stw_p(&mhdr->num_buffers, i);
+ }
+
+ virtqueue_flush(n->rx_vq, i);
+ virtio_notify(&n->vdev, n->rx_vq);
+
+ return size;
+}
+
+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
+
+static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len)
+{
+ VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len);
+ virtio_notify(&n->vdev, n->tx_vq);
+
+ n->async_tx.elem.out_num = n->async_tx.len = 0;
+
+ virtio_queue_set_notification(n->tx_vq, 1);
+ virtio_net_flush_tx(n, n->tx_vq);
+}
+
+/* TX */
+static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)
+{
+ VirtQueueElement elem;
+ int32_t num_packets = 0;
+ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ return num_packets;
+ }
+
+ assert(n->vdev.vm_running);
+
+ if (n->async_tx.elem.out_num) {
+ virtio_queue_set_notification(n->tx_vq, 0);
+ return num_packets;
+ }
+
+ while (virtqueue_pop(vq, &elem)) {
+ ssize_t ret, len = 0;
+ unsigned int out_num = elem.out_num;
+ struct iovec *out_sg = &elem.out_sg[0];
+ unsigned hdr_len;
+
+ /* hdr_len refers to the header received from the guest */
+ hdr_len = n->mergeable_rx_bufs ?
+ sizeof(struct virtio_net_hdr_mrg_rxbuf) :
+ sizeof(struct virtio_net_hdr);
+
+ if (out_num < 1 || out_sg->iov_len != hdr_len) {
+ error_report("virtio-net header not in first element");
+ exit(1);
+ }
+
+ /* ignore the header if GSO is not supported */
+ if (!n->has_vnet_hdr) {
+ out_num--;
+ out_sg++;
+ len += hdr_len;
+ } else if (n->mergeable_rx_bufs) {
+ /* tapfd expects a struct virtio_net_hdr */
+ hdr_len -= sizeof(struct virtio_net_hdr);
+ out_sg->iov_len -= hdr_len;
+ len += hdr_len;
+ }
+
+ ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num,
+ virtio_net_tx_complete);
+ if (ret == 0) {
+ virtio_queue_set_notification(n->tx_vq, 0);
+ n->async_tx.elem = elem;
+ n->async_tx.len = len;
+ return -EBUSY;
+ }
+
+ len += ret;
+
+ virtqueue_push(vq, &elem, len);
+ virtio_notify(&n->vdev, vq);
+
+ if (++num_packets >= n->tx_burst) {
+ break;
+ }
+ }
+ return num_packets;
+}
+
+static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ /* This happens when device was stopped but VCPU wasn't. */
+ if (!n->vdev.vm_running) {
+ n->tx_waiting = 1;
+ return;
+ }
+
+ if (n->tx_waiting) {
+ virtio_queue_set_notification(vq, 1);
+ qemu_del_timer(n->tx_timer);
+ n->tx_waiting = 0;
+ virtio_net_flush_tx(n, vq);
+ } else {
+ qemu_mod_timer(n->tx_timer,
+ qemu_get_clock(vm_clock) + n->tx_timeout);
+ n->tx_waiting = 1;
+ virtio_queue_set_notification(vq, 0);
+ }
+}
+
+static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ if (unlikely(n->tx_waiting)) {
+ return;
+ }
+ n->tx_waiting = 1;
+ /* This happens when device was stopped but VCPU wasn't. */
+ if (!n->vdev.vm_running) {
+ return;
+ }
+ virtio_queue_set_notification(vq, 0);
+ qemu_bh_schedule(n->tx_bh);
+}
+
+static void virtio_net_tx_timer(void *opaque)
+{
+ VirtIONet *n = opaque;
+ assert(n->vdev.vm_running);
+
+ n->tx_waiting = 0;
+
+ /* Just in case the driver is not ready on more */
+ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
+ return;
+
+ virtio_queue_set_notification(n->tx_vq, 1);
+ virtio_net_flush_tx(n, n->tx_vq);
+}
+
+static void virtio_net_tx_bh(void *opaque)
+{
+ VirtIONet *n = opaque;
+ int32_t ret;
+
+ assert(n->vdev.vm_running);
+
+ n->tx_waiting = 0;
+
+ /* Just in case the driver is not ready on more */
+ if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)))
+ return;
+
+ ret = virtio_net_flush_tx(n, n->tx_vq);
+ if (ret == -EBUSY) {
+ return; /* Notification re-enable handled by tx_complete */
+ }
+
+ /* If we flush a full burst of packets, assume there are
+ * more coming and immediately reschedule */
+ if (ret >= n->tx_burst) {
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+ return;
+ }
+
+ /* If less than a full burst, re-enable notification and flush
+ * anything that may have come in while we weren't looking. If
+ * we find something, assume the guest is still active and reschedule */
+ virtio_queue_set_notification(n->tx_vq, 1);
+ if (virtio_net_flush_tx(n, n->tx_vq) > 0) {
+ virtio_queue_set_notification(n->tx_vq, 0);
+ qemu_bh_schedule(n->tx_bh);
+ n->tx_waiting = 1;
+ }
+}
+
+static void virtio_net_save(QEMUFile *f, void *opaque)
+{
+ VirtIONet *n = opaque;
+
+ /* At this point, backend must be stopped, otherwise
+ * it might keep writing to memory. */
+ assert(!n->vhost_started);
+ virtio_save(&n->vdev, f);
+
+ qemu_put_buffer(f, n->mac, ETH_ALEN);
+ qemu_put_be32(f, n->tx_waiting);
+ qemu_put_be32(f, n->mergeable_rx_bufs);
+ qemu_put_be16(f, n->status);
+ qemu_put_byte(f, n->promisc);
+ qemu_put_byte(f, n->allmulti);
+ qemu_put_be32(f, n->mac_table.in_use);
+ qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
+ qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
+ qemu_put_be32(f, n->has_vnet_hdr);
+ qemu_put_byte(f, n->mac_table.multi_overflow);
+ qemu_put_byte(f, n->mac_table.uni_overflow);
+ qemu_put_byte(f, n->alluni);
+ qemu_put_byte(f, n->nomulti);
+ qemu_put_byte(f, n->nouni);
+ qemu_put_byte(f, n->nobcast);
+ qemu_put_byte(f, n->has_ufo);
+}
+
+static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIONet *n = opaque;
+ int i;
+
+ if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
+ return -EINVAL;
+
+ virtio_load(&n->vdev, f);
+
+ qemu_get_buffer(f, n->mac, ETH_ALEN);
+ n->tx_waiting = qemu_get_be32(f);
+ n->mergeable_rx_bufs = qemu_get_be32(f);
+
+ if (version_id >= 3)
+ n->status = qemu_get_be16(f);
+
+ if (version_id >= 4) {
+ if (version_id < 8) {
+ n->promisc = qemu_get_be32(f);
+ n->allmulti = qemu_get_be32(f);
+ } else {
+ n->promisc = qemu_get_byte(f);
+ n->allmulti = qemu_get_byte(f);
+ }
+ }
+
+ if (version_id >= 5) {
+ n->mac_table.in_use = qemu_get_be32(f);
+ /* MAC_TABLE_ENTRIES may be different from the saved image */
+ if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) {
+ qemu_get_buffer(f, n->mac_table.macs,
+ n->mac_table.in_use * ETH_ALEN);
+ } else if (n->mac_table.in_use) {
+ qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR);
+ n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1;
+ n->mac_table.in_use = 0;
+ }
+ }
+
+ if (version_id >= 6)
+ qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
+
+ if (version_id >= 7) {
+ if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) {
+ error_report("virtio-net: saved image requires vnet_hdr=on");
+ return -1;
+ }
+
+ if (n->has_vnet_hdr) {
+ tap_using_vnet_hdr(n->nic->nc.peer, 1);
+ tap_set_offload(n->nic->nc.peer,
+ (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1,
+ (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1,
+ (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1,
+ (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_ECN) & 1,
+ (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_UFO) & 1);
+ }
+ }
+
+ if (version_id >= 9) {
+ n->mac_table.multi_overflow = qemu_get_byte(f);
+ n->mac_table.uni_overflow = qemu_get_byte(f);
+ }
+
+ if (version_id >= 10) {
+ n->alluni = qemu_get_byte(f);
+ n->nomulti = qemu_get_byte(f);
+ n->nouni = qemu_get_byte(f);
+ n->nobcast = qemu_get_byte(f);
+ }
+
+ if (version_id >= 11) {
+ if (qemu_get_byte(f) && !peer_has_ufo(n)) {
+ error_report("virtio-net: saved image requires TUN_F_UFO support");
+ return -1;
+ }
+ }
+
+ /* Find the first multicast entry in the saved MAC filter */
+ for (i = 0; i < n->mac_table.in_use; i++) {
+ if (n->mac_table.macs[i * ETH_ALEN] & 1) {
+ break;
+ }
+ }
+ n->mac_table.first_multi = i;
+ return 0;
+}
+
+static void virtio_net_cleanup(VLANClientState *nc)
+{
+ VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ n->nic = NULL;
+}
+
+static NetClientInfo net_virtio_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = virtio_net_can_receive,
+ .receive = virtio_net_receive,
+ .cleanup = virtio_net_cleanup,
+ .link_status_changed = virtio_net_set_link_status,
+};
+
+VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
+ virtio_net_conf *net)
+{
+ VirtIONet *n;
+
+ n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET,
+ sizeof(struct virtio_net_config),
+ sizeof(VirtIONet));
+
+ n->vdev.get_config = virtio_net_get_config;
+ n->vdev.set_config = virtio_net_set_config;
+ n->vdev.get_features = virtio_net_get_features;
+ n->vdev.set_features = virtio_net_set_features;
+ n->vdev.bad_features = virtio_net_bad_features;
+ n->vdev.reset = virtio_net_reset;
+ n->vdev.set_status = virtio_net_set_status;
+ n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
+
+ if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) {
+ error_report("virtio-net: "
+ "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
+ net->tx);
+ error_report("Defaulting to \"bh\"");
+ }
+
+ if (net->tx && !strcmp(net->tx, "timer")) {
+ n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer);
+ n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
+ n->tx_timeout = net->txtimer;
+ } else {
+ n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh);
+ n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n);
+ }
+ n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl);
+ qemu_macaddr_default_if_unset(&conf->macaddr);
+ memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac));
+ n->status = VIRTIO_NET_S_LINK_UP;
+
+ n->nic = qemu_new_nic(&net_virtio_info, conf, dev->info->name, dev->id, n);
+
+ qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
+
+ n->tx_waiting = 0;
+ n->tx_burst = net->txburst;
+ n->mergeable_rx_bufs = 0;
+ n->promisc = 1; /* for compatibility */
+
+ n->mac_table.macs = qemu_mallocz(MAC_TABLE_ENTRIES * ETH_ALEN);
+
+ n->vlans = qemu_mallocz(MAX_VLAN >> 3);
+
+ n->qdev = dev;
+ register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
+ virtio_net_save, virtio_net_load, n);
+
+ add_boot_device_path(conf->bootindex, dev, "/ethernet-phy@0");
+
+ return &n->vdev;
+}
+
+void virtio_net_exit(VirtIODevice *vdev)
+{
+ VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev);
+
+ /* This will stop vhost backend if appropriate. */
+ virtio_net_set_status(vdev, 0);
+
+ qemu_purge_queued_packets(&n->nic->nc);
+
+ unregister_savevm(n->qdev, "virtio-net", n);
+
+ qemu_free(n->mac_table.macs);
+ qemu_free(n->vlans);
+
+ if (n->tx_timer) {
+ qemu_del_timer(n->tx_timer);
+ qemu_free_timer(n->tx_timer);
+ } else {
+ qemu_bh_delete(n->tx_bh);
+ }
+
+ virtio_cleanup(&n->vdev);
+ qemu_del_vlan_client(&n->nic->nc);
+}
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
new file mode 100644
index 0000000..8af9a1c
--- /dev/null
+++ b/hw/virtio-net.h
@@ -0,0 +1,189 @@
+/*
+ * Virtio Network Device
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VIRTIO_NET_H
+#define _QEMU_VIRTIO_NET_H
+
+#include "virtio.h"
+#include "net.h"
+#include "pci.h"
+
+#define ETH_ALEN 6
+
+/* from Linux's virtio_net.h */
+
+/* The ID for virtio_net */
+#define VIRTIO_ID_NET 1
+
+/* The feature bitmap for virtio net */
+#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */
+#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */
+#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */
+#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */
+#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */
+#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */
+#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */
+#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */
+#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */
+#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */
+#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */
+#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */
+#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */
+#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */
+
+#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
+
+#define TX_TIMER_INTERVAL 150000 /* 150 us */
+
+/* Limit the number of packets that can be sent via a single flush
+ * of the TX queue. This gives us a guaranteed exit condition and
+ * ensures fairness in the io path. 256 conveniently matches the
+ * length of the TX queue and shows a good balance of performance
+ * and latency. */
+#define TX_BURST 256
+
+typedef struct virtio_net_conf
+{
+ uint32_t txtimer;
+ int32_t txburst;
+ char *tx;
+} virtio_net_conf;
+
+/* Maximum packet size we can receive from tap device: header + 64k */
+#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10))
+
+struct virtio_net_config
+{
+ /* The config defining mac address ($ETH_ALEN bytes) */
+ uint8_t mac[ETH_ALEN];
+ /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
+ uint16_t status;
+} __attribute__((packed));
+
+/* This is the first element of the scatter-gather list. If you don't
+ * specify GSO or CSUM features, you can simply ignore the header. */
+struct virtio_net_hdr
+{
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset
+ uint8_t flags;
+#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame
+#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
+#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
+#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
+#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+};
+
+/* This is the version of the header to use when the MRG_RXBUF
+ * feature has been negotiated. */
+struct virtio_net_hdr_mrg_rxbuf
+{
+ struct virtio_net_hdr hdr;
+ uint16_t num_buffers; /* Number of merged rx buffers */
+};
+
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry. Data for the
+ * command goes in between.
+ */
+struct virtio_net_ctrl_hdr {
+ uint8_t class;
+ uint8_t cmd;
+};
+
+typedef uint8_t virtio_net_ctrl_ack;
+
+#define VIRTIO_NET_OK 0
+#define VIRTIO_NET_ERR 1
+
+/*
+ * Control the RX mode, ie. promisucous, allmulti, etc...
+ * All commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable. Commands
+ * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
+ */
+#define VIRTIO_NET_CTRL_RX_MODE 0
+ #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0
+ #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1
+ #define VIRTIO_NET_CTRL_RX_MODE_ALLUNI 2
+ #define VIRTIO_NET_CTRL_RX_MODE_NOMULTI 3
+ #define VIRTIO_NET_CTRL_RX_MODE_NOUNI 4
+ #define VIRTIO_NET_CTRL_RX_MODE_NOBCAST 5
+
+/*
+ * Control the MAC filter table.
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite. Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists. Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses. The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ */
+struct virtio_net_ctrl_mac {
+ uint32_t entries;
+ uint8_t macs[][ETH_ALEN];
+};
+#define VIRTIO_NET_CTRL_MAC 1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
+
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added may be filterd by the hypervisor. Del is the
+ * opposite of add. Both commands expect an out entry containing a 2
+ * byte VLAN ID. VLAN filterting is available with the
+ * VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN 2
+ #define VIRTIO_NET_CTRL_VLAN_ADD 0
+ #define VIRTIO_NET_CTRL_VLAN_DEL 1
+
+#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \
+ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
+ DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \
+ DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \
+ DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \
+ DEFINE_PROP_BIT("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \
+ DEFINE_PROP_BIT("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \
+ DEFINE_PROP_BIT("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \
+ DEFINE_PROP_BIT("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \
+ DEFINE_PROP_BIT("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \
+ DEFINE_PROP_BIT("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \
+ DEFINE_PROP_BIT("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \
+ DEFINE_PROP_BIT("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \
+ DEFINE_PROP_BIT("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \
+ DEFINE_PROP_BIT("status", _state, _field, VIRTIO_NET_F_STATUS, true), \
+ DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \
+ DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \
+ DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \
+ DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true)
+#endif
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
new file mode 100644
index 0000000..54c1a04
--- /dev/null
+++ b/hw/virtio-pci.c
@@ -0,0 +1,1026 @@
+/*
+ * Virtio PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Paul Brook <paul@codesourcery.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <inttypes.h>
+
+#include "virtio.h"
+#include "virtio-blk.h"
+#include "virtio-net.h"
+#include "pci.h"
+#include "qemu-error.h"
+#include "msix.h"
+#include "net.h"
+#include "loader.h"
+#include "kvm.h"
+#include "blockdev.h"
+
+/* from Linux's linux/virtio_pci.h */
+
+/* A 32-bit r/o bitmask of the features supported by the host */
+#define VIRTIO_PCI_HOST_FEATURES 0
+
+/* A 32-bit r/w bitmask of features activated by the guest */
+#define VIRTIO_PCI_GUEST_FEATURES 4
+
+/* A 32-bit r/w PFN for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_PFN 8
+
+/* A 16-bit r/o queue size for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_NUM 12
+
+/* A 16-bit r/w queue selector */
+#define VIRTIO_PCI_QUEUE_SEL 14
+
+/* A 16-bit r/w queue notifier */
+#define VIRTIO_PCI_QUEUE_NOTIFY 16
+
+/* An 8-bit device status register. */
+#define VIRTIO_PCI_STATUS 18
+
+/* An 8-bit r/o interrupt status register. Reading the value will return the
+ * current contents of the ISR and will also clear it. This is effectively
+ * a read-and-acknowledge. */
+#define VIRTIO_PCI_ISR 19
+
+/* MSI-X registers: only enabled if MSI-X is enabled. */
+/* A 16-bit vector for configuration changes. */
+#define VIRTIO_MSI_CONFIG_VECTOR 20
+/* A 16-bit vector for selected queue notifications. */
+#define VIRTIO_MSI_QUEUE_VECTOR 22
+
+/* Config space size */
+#define VIRTIO_PCI_CONFIG_NOMSI 20
+#define VIRTIO_PCI_CONFIG_MSI 24
+#define VIRTIO_PCI_REGION_SIZE(dev) (msix_present(dev) ? \
+ VIRTIO_PCI_CONFIG_MSI : \
+ VIRTIO_PCI_CONFIG_NOMSI)
+
+/* The remaining space is defined by each driver as the per-driver
+ * configuration space */
+#define VIRTIO_PCI_CONFIG(dev) (msix_enabled(dev) ? \
+ VIRTIO_PCI_CONFIG_MSI : \
+ VIRTIO_PCI_CONFIG_NOMSI)
+
+/* Virtio ABI version, if we increment this, we break the guest driver. */
+#define VIRTIO_PCI_ABI_VERSION 0
+
+/* How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to x86 page size. */
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12
+
+/* Flags track per-device state like workarounds for quirks in older guests. */
+#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0)
+
+/* Performance improves when virtqueue kick processing is decoupled from the
+ * vcpu thread using ioeventfd for some devices. */
+#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1
+#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT)
+
+/* QEMU doesn't strictly need write barriers since everything runs in
+ * lock-step. We'll leave the calls to wmb() in though to make it obvious for
+ * KVM or if kqemu gets SMP support.
+ */
+#define wmb() do { } while (0)
+
+/* PCI bindings. */
+
+typedef struct {
+ PCIDevice pci_dev;
+ VirtIODevice *vdev;
+ uint32_t flags;
+ uint32_t addr;
+ uint32_t class_code;
+ uint32_t nvectors;
+ BlockConf block;
+ NICConf nic;
+ uint32_t host_features;
+#ifdef CONFIG_LINUX
+ V9fsConf fsconf;
+#endif
+ /* Max. number of ports we can have for a the virtio-serial device */
+ uint32_t max_virtserial_ports;
+ virtio_net_conf net;
+ bool ioeventfd_disabled;
+ bool ioeventfd_started;
+} VirtIOPCIProxy;
+
+/* virtio device */
+
+static void virtio_pci_notify(void *opaque, uint16_t vector)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ if (msix_enabled(&proxy->pci_dev))
+ msix_notify(&proxy->pci_dev, vector);
+ else
+ qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1);
+}
+
+static void virtio_pci_save_config(void * opaque, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ pci_device_save(&proxy->pci_dev, f);
+ msix_save(&proxy->pci_dev, f);
+ if (msix_present(&proxy->pci_dev))
+ qemu_put_be16(f, proxy->vdev->config_vector);
+}
+
+static void virtio_pci_save_queue(void * opaque, int n, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ if (msix_present(&proxy->pci_dev))
+ qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n));
+}
+
+static int virtio_pci_load_config(void * opaque, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ int ret;
+ ret = pci_device_load(&proxy->pci_dev, f);
+ if (ret) {
+ return ret;
+ }
+ msix_load(&proxy->pci_dev, f);
+ if (msix_present(&proxy->pci_dev)) {
+ qemu_get_be16s(f, &proxy->vdev->config_vector);
+ } else {
+ proxy->vdev->config_vector = VIRTIO_NO_VECTOR;
+ }
+ if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
+ return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
+ }
+ return 0;
+}
+
+static int virtio_pci_load_queue(void * opaque, int n, QEMUFile *f)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint16_t vector;
+ if (msix_present(&proxy->pci_dev)) {
+ qemu_get_be16s(f, &vector);
+ } else {
+ vector = VIRTIO_NO_VECTOR;
+ }
+ virtio_queue_set_vector(proxy->vdev, n, vector);
+ if (vector != VIRTIO_NO_VECTOR) {
+ return msix_vector_use(&proxy->pci_dev, vector);
+ }
+ return 0;
+}
+
+static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
+ int n, bool assign)
+{
+ VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ int r;
+ if (assign) {
+ r = event_notifier_init(notifier, 1);
+ if (r < 0) {
+ error_report("%s: unable to init event notifier: %d",
+ __func__, r);
+ return r;
+ }
+ r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier),
+ proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY,
+ n, assign);
+ if (r < 0) {
+ error_report("%s: unable to map ioeventfd: %d",
+ __func__, r);
+ event_notifier_cleanup(notifier);
+ }
+ } else {
+ r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier),
+ proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY,
+ n, assign);
+ if (r < 0) {
+ error_report("%s: unable to unmap ioeventfd: %d",
+ __func__, r);
+ return r;
+ }
+
+ /* Handle the race condition where the guest kicked and we deassigned
+ * before we got around to handling the kick.
+ */
+ if (event_notifier_test_and_clear(notifier)) {
+ virtio_queue_notify_vq(vq);
+ }
+
+ event_notifier_cleanup(notifier);
+ }
+ return r;
+}
+
+static void virtio_pci_host_notifier_read(void *opaque)
+{
+ VirtQueue *vq = opaque;
+ EventNotifier *n = virtio_queue_get_host_notifier(vq);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_queue_notify_vq(vq);
+ }
+}
+
+static void virtio_pci_set_host_notifier_fd_handler(VirtIOPCIProxy *proxy,
+ int n, bool assign)
+{
+ VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+ if (assign) {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ virtio_pci_host_notifier_read, NULL, vq);
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ NULL, NULL, NULL);
+ }
+}
+
+static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
+{
+ int n, r;
+
+ if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
+ proxy->ioeventfd_disabled ||
+ proxy->ioeventfd_started) {
+ return;
+ }
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(proxy->vdev, n)) {
+ continue;
+ }
+
+ r = virtio_pci_set_host_notifier_internal(proxy, n, true);
+ if (r < 0) {
+ goto assign_error;
+ }
+
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, true);
+ }
+ proxy->ioeventfd_started = true;
+ return;
+
+assign_error:
+ while (--n >= 0) {
+ if (!virtio_queue_get_num(proxy->vdev, n)) {
+ continue;
+ }
+
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, false);
+ r = virtio_pci_set_host_notifier_internal(proxy, n, false);
+ assert(r >= 0);
+ }
+ proxy->ioeventfd_started = false;
+ error_report("%s: failed. Fallback to a userspace (slower).", __func__);
+}
+
+static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
+{
+ int r;
+ int n;
+
+ if (!proxy->ioeventfd_started) {
+ return;
+ }
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(proxy->vdev, n)) {
+ continue;
+ }
+
+ virtio_pci_set_host_notifier_fd_handler(proxy, n, false);
+ r = virtio_pci_set_host_notifier_internal(proxy, n, false);
+ assert(r >= 0);
+ }
+ proxy->ioeventfd_started = false;
+}
+
+static void virtio_pci_reset(DeviceState *d)
+{
+ VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev);
+ virtio_pci_stop_ioeventfd(proxy);
+ virtio_reset(proxy->vdev);
+ msix_reset(&proxy->pci_dev);
+ proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+}
+
+static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = proxy->vdev;
+ target_phys_addr_t pa;
+
+ switch (addr) {
+ case VIRTIO_PCI_GUEST_FEATURES:
+ /* Guest does not negotiate properly? We have to assume nothing. */
+ if (val & (1 << VIRTIO_F_BAD_FEATURE)) {
+ if (vdev->bad_features)
+ val = proxy->host_features & vdev->bad_features(vdev);
+ else
+ val = 0;
+ }
+ if (vdev->set_features)
+ vdev->set_features(vdev, val);
+ vdev->guest_features = val;
+ break;
+ case VIRTIO_PCI_QUEUE_PFN:
+ pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+ if (pa == 0) {
+ virtio_pci_stop_ioeventfd(proxy);
+ virtio_reset(proxy->vdev);
+ msix_unuse_all_vectors(&proxy->pci_dev);
+ }
+ else
+ virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
+ break;
+ case VIRTIO_PCI_QUEUE_SEL:
+ if (val < VIRTIO_PCI_QUEUE_MAX)
+ vdev->queue_sel = val;
+ break;
+ case VIRTIO_PCI_QUEUE_NOTIFY:
+ virtio_queue_notify(vdev, val);
+ break;
+ case VIRTIO_PCI_STATUS:
+ if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ virtio_pci_stop_ioeventfd(proxy);
+ }
+
+ virtio_set_status(vdev, val & 0xFF);
+
+ if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
+ virtio_pci_start_ioeventfd(proxy);
+ }
+
+ if (vdev->status == 0) {
+ virtio_reset(proxy->vdev);
+ msix_unuse_all_vectors(&proxy->pci_dev);
+ }
+
+ /* Linux before 2.6.34 sets the device as OK without enabling
+ the PCI device bus master bit. In this case we need to disable
+ some safety checks. */
+ if ((val & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+ proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+ }
+ break;
+ case VIRTIO_MSI_CONFIG_VECTOR:
+ msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+ /* Make it possible for guest to discover an error took place. */
+ if (msix_vector_use(&proxy->pci_dev, val) < 0)
+ val = VIRTIO_NO_VECTOR;
+ vdev->config_vector = val;
+ break;
+ case VIRTIO_MSI_QUEUE_VECTOR:
+ msix_vector_unuse(&proxy->pci_dev,
+ virtio_queue_vector(vdev, vdev->queue_sel));
+ /* Make it possible for guest to discover an error took place. */
+ if (msix_vector_use(&proxy->pci_dev, val) < 0)
+ val = VIRTIO_NO_VECTOR;
+ virtio_queue_set_vector(vdev, vdev->queue_sel, val);
+ break;
+ default:
+ error_report("%s: unexpected address 0x%x value 0x%x",
+ __func__, addr, val);
+ break;
+ }
+}
+
+static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
+{
+ VirtIODevice *vdev = proxy->vdev;
+ uint32_t ret = 0xFFFFFFFF;
+
+ switch (addr) {
+ case VIRTIO_PCI_HOST_FEATURES:
+ ret = proxy->host_features;
+ break;
+ case VIRTIO_PCI_GUEST_FEATURES:
+ ret = vdev->guest_features;
+ break;
+ case VIRTIO_PCI_QUEUE_PFN:
+ ret = virtio_queue_get_addr(vdev, vdev->queue_sel)
+ >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+ break;
+ case VIRTIO_PCI_QUEUE_NUM:
+ ret = virtio_queue_get_num(vdev, vdev->queue_sel);
+ break;
+ case VIRTIO_PCI_QUEUE_SEL:
+ ret = vdev->queue_sel;
+ break;
+ case VIRTIO_PCI_STATUS:
+ ret = vdev->status;
+ break;
+ case VIRTIO_PCI_ISR:
+ /* reading from the ISR also clears it. */
+ ret = vdev->isr;
+ vdev->isr = 0;
+ qemu_set_irq(proxy->pci_dev.irq[0], 0);
+ break;
+ case VIRTIO_MSI_CONFIG_VECTOR:
+ ret = vdev->config_vector;
+ break;
+ case VIRTIO_MSI_QUEUE_VECTOR:
+ ret = virtio_queue_vector(vdev, vdev->queue_sel);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ addr -= proxy->addr;
+ if (addr < config)
+ return virtio_ioport_read(proxy, addr);
+ addr -= config;
+ return virtio_config_readb(proxy->vdev, addr);
+}
+
+static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ addr -= proxy->addr;
+ if (addr < config)
+ return virtio_ioport_read(proxy, addr);
+ addr -= config;
+ return virtio_config_readw(proxy->vdev, addr);
+}
+
+static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ addr -= proxy->addr;
+ if (addr < config)
+ return virtio_ioport_read(proxy, addr);
+ addr -= config;
+ return virtio_config_readl(proxy->vdev, addr);
+}
+
+static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ addr -= proxy->addr;
+ if (addr < config) {
+ virtio_ioport_write(proxy, addr, val);
+ return;
+ }
+ addr -= config;
+ virtio_config_writeb(proxy->vdev, addr, val);
+}
+
+static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ addr -= proxy->addr;
+ if (addr < config) {
+ virtio_ioport_write(proxy, addr, val);
+ return;
+ }
+ addr -= config;
+ virtio_config_writew(proxy->vdev, addr, val);
+}
+
+static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+ addr -= proxy->addr;
+ if (addr < config) {
+ virtio_ioport_write(proxy, addr, val);
+ return;
+ }
+ addr -= config;
+ virtio_config_writel(proxy->vdev, addr, val);
+}
+
+static void virtio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ VirtIOPCIProxy *proxy = container_of(pci_dev, VirtIOPCIProxy, pci_dev);
+ VirtIODevice *vdev = proxy->vdev;
+ unsigned config_len = VIRTIO_PCI_REGION_SIZE(pci_dev) + vdev->config_len;
+
+ proxy->addr = addr;
+
+ register_ioport_write(addr, config_len, 1, virtio_pci_config_writeb, proxy);
+ register_ioport_write(addr, config_len, 2, virtio_pci_config_writew, proxy);
+ register_ioport_write(addr, config_len, 4, virtio_pci_config_writel, proxy);
+ register_ioport_read(addr, config_len, 1, virtio_pci_config_readb, proxy);
+ register_ioport_read(addr, config_len, 2, virtio_pci_config_readw, proxy);
+ register_ioport_read(addr, config_len, 4, virtio_pci_config_readl, proxy);
+
+ if (vdev->config_len)
+ vdev->get_config(vdev, vdev->config);
+}
+
+static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
+ uint32_t val, int len)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ if (PCI_COMMAND == address) {
+ if (!(val & PCI_COMMAND_MASTER)) {
+ if (!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) {
+ virtio_pci_stop_ioeventfd(proxy);
+ virtio_set_status(proxy->vdev,
+ proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
+ }
+ }
+ }
+
+ pci_default_write_config(pci_dev, address, val, len);
+ msix_write_config(pci_dev, address, val, len);
+}
+
+static unsigned virtio_pci_get_features(void *opaque)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ return proxy->host_features;
+}
+
+static void virtio_pci_guest_notifier_read(void *opaque)
+{
+ VirtQueue *vq = opaque;
+ EventNotifier *n = virtio_queue_get_guest_notifier(vq);
+ if (event_notifier_test_and_clear(n)) {
+ virtio_irq(vq);
+ }
+}
+
+static int virtio_pci_mask_vq(PCIDevice *dev, unsigned vector,
+ VirtQueue *vq, int masked)
+{
+#ifdef CONFIG_KVM
+ EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
+ int r = kvm_set_irqfd(dev->msix_irq_entries[vector].gsi,
+ event_notifier_get_fd(notifier),
+ !masked);
+ if (r < 0) {
+ return (r == -ENOSYS) ? 0 : r;
+ }
+ if (masked) {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ virtio_pci_guest_notifier_read, NULL, vq);
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ NULL, NULL, NULL);
+ }
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+static int virtio_pci_mask_notifier(PCIDevice *dev, unsigned vector,
+ int masked)
+{
+ VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
+ VirtIODevice *vdev = proxy->vdev;
+ int r, n;
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ break;
+ }
+ if (virtio_queue_vector(vdev, n) != vector) {
+ continue;
+ }
+ r = virtio_pci_mask_vq(dev, vector, virtio_get_queue(vdev, n), masked);
+ if (r < 0) {
+ goto undo;
+ }
+ }
+ return 0;
+undo:
+ while (--n >= 0) {
+ if (virtio_queue_vector(vdev, n) != vector) {
+ continue;
+ }
+ virtio_pci_mask_vq(dev, vector, virtio_get_queue(vdev, n), !masked);
+ }
+ return r;
+}
+
+
+static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+ EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
+
+ if (assign) {
+ int r = event_notifier_init(notifier, 0);
+ if (r < 0) {
+ return r;
+ }
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ virtio_pci_guest_notifier_read, NULL, vq);
+ } else {
+ qemu_set_fd_handler(event_notifier_get_fd(notifier),
+ NULL, NULL, NULL);
+ /* Test and clear notifier before closing it,
+ * in case poll callback didn't have time to run. */
+ virtio_pci_guest_notifier_read(vq);
+ event_notifier_cleanup(notifier);
+ }
+
+ return 0;
+}
+
+static bool virtio_pci_query_guest_notifiers(void *opaque)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ return msix_enabled(&proxy->pci_dev);
+}
+
+static int virtio_pci_set_guest_notifiers(void *opaque, bool assign)
+{
+ VirtIOPCIProxy *proxy = opaque;
+ VirtIODevice *vdev = proxy->vdev;
+ int r, n;
+
+ /* Must unset mask notifier while guest notifier
+ * is still assigned */
+ if (!assign) {
+ r = msix_unset_mask_notifier(&proxy->pci_dev);
+ assert(r >= 0);
+ }
+
+ for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+ if (!virtio_queue_get_num(vdev, n)) {
+ break;
+ }
+
+ r = virtio_pci_set_guest_notifier(opaque, n, assign);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+
+ /* Must set mask notifier after guest notifier
+ * has been assigned */
+ if (assign) {
+ r = msix_set_mask_notifier(&proxy->pci_dev,
+ virtio_pci_mask_notifier);
+ if (r < 0) {
+ goto assign_error;
+ }
+ }
+
+ return 0;
+
+assign_error:
+ /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
+ if (assign) {
+ msix_unset_mask_notifier(&proxy->pci_dev);
+ }
+
+ while (--n >= 0) {
+ virtio_pci_set_guest_notifier(opaque, n, !assign);
+ }
+
+ if (!assign) {
+ msix_set_mask_notifier(&proxy->pci_dev,
+ virtio_pci_mask_notifier);
+ }
+ return r;
+}
+
+static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign)
+{
+ VirtIOPCIProxy *proxy = opaque;
+
+ /* Stop using ioeventfd for virtqueue kick if the device starts using host
+ * notifiers. This makes it easy to avoid stepping on each others' toes.
+ */
+ proxy->ioeventfd_disabled = assign;
+ if (assign) {
+ virtio_pci_stop_ioeventfd(proxy);
+ }
+ /* We don't need to start here: it's not needed because backend
+ * currently only stops on status change away from ok,
+ * reset, vmstop and such. If we do add code to start here,
+ * need to check vmstate, device state etc. */
+ return virtio_pci_set_host_notifier_internal(proxy, n, assign);
+}
+
+static void virtio_pci_vmstate_change(void *opaque, bool running)
+{
+ VirtIOPCIProxy *proxy = opaque;
+
+ if (running) {
+ /* Try to find out if the guest has bus master disabled, but is
+ in ready state. Then we have a buggy guest OS. */
+ if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+ proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+ }
+ virtio_pci_start_ioeventfd(proxy);
+ } else {
+ virtio_pci_stop_ioeventfd(proxy);
+ }
+}
+
+static const VirtIOBindings virtio_pci_bindings = {
+ .notify = virtio_pci_notify,
+ .save_config = virtio_pci_save_config,
+ .load_config = virtio_pci_load_config,
+ .save_queue = virtio_pci_save_queue,
+ .load_queue = virtio_pci_load_queue,
+ .get_features = virtio_pci_get_features,
+ .query_guest_notifiers = virtio_pci_query_guest_notifiers,
+ .set_host_notifier = virtio_pci_set_host_notifier,
+ .set_guest_notifiers = virtio_pci_set_guest_notifiers,
+ .vmstate_change = virtio_pci_vmstate_change,
+};
+
+static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
+ uint16_t vendor, uint16_t device,
+ uint16_t class_code, uint8_t pif)
+{
+ uint8_t *config;
+ uint32_t size;
+
+ proxy->vdev = vdev;
+
+ config = proxy->pci_dev.config;
+ pci_config_set_vendor_id(config, vendor);
+ pci_config_set_device_id(config, device);
+
+ config[0x08] = VIRTIO_PCI_ABI_VERSION;
+
+ config[0x09] = pif;
+ pci_config_set_class(config, class_code);
+
+ config[0x2c] = vendor & 0xFF;
+ config[0x2d] = (vendor >> 8) & 0xFF;
+ config[0x2e] = vdev->device_id & 0xFF;
+ config[0x2f] = (vdev->device_id >> 8) & 0xFF;
+
+ config[0x3d] = 1;
+
+ if (vdev->nvectors && !msix_init(&proxy->pci_dev, vdev->nvectors, 1, 0)) {
+ pci_register_bar(&proxy->pci_dev, 1,
+ msix_bar_size(&proxy->pci_dev),
+ PCI_BASE_ADDRESS_SPACE_MEMORY,
+ msix_mmio_map);
+ } else
+ vdev->nvectors = 0;
+
+ proxy->pci_dev.config_write = virtio_write_config;
+
+ size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len;
+ if (size & (size-1))
+ size = 1 << qemu_fls(size);
+
+ pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO,
+ virtio_map);
+
+ if (!kvm_has_many_ioeventfds()) {
+ proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
+ }
+
+ virtio_bind_device(vdev, &virtio_pci_bindings, proxy);
+ proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
+ proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
+ proxy->host_features = vdev->get_features(vdev, proxy->host_features);
+}
+
+static int virtio_blk_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ if (proxy->class_code != PCI_CLASS_STORAGE_SCSI &&
+ proxy->class_code != PCI_CLASS_STORAGE_OTHER)
+ proxy->class_code = PCI_CLASS_STORAGE_SCSI;
+
+ vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block);
+ if (!vdev) {
+ return -1;
+ }
+ vdev->nvectors = proxy->nvectors;
+ virtio_init_pci(proxy, vdev,
+ PCI_VENDOR_ID_REDHAT_QUMRANET,
+ PCI_DEVICE_ID_VIRTIO_BLOCK,
+ proxy->class_code, 0x00);
+ /* make the actual value visible */
+ proxy->nvectors = vdev->nvectors;
+ return 0;
+}
+
+static int virtio_exit_pci(PCIDevice *pci_dev)
+{
+ return msix_uninit(pci_dev);
+}
+
+static int virtio_blk_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_pci_stop_ioeventfd(proxy);
+ virtio_blk_exit(proxy->vdev);
+ blockdev_mark_auto_del(proxy->block.bs);
+ return virtio_exit_pci(pci_dev);
+}
+
+static int virtio_serial_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ if (proxy->class_code != PCI_CLASS_COMMUNICATION_OTHER &&
+ proxy->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */
+ proxy->class_code != PCI_CLASS_OTHERS) /* qemu-kvm */
+ proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER;
+
+ vdev = virtio_serial_init(&pci_dev->qdev, proxy->max_virtserial_ports);
+ if (!vdev) {
+ return -1;
+ }
+ vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED
+ ? proxy->max_virtserial_ports + 1
+ : proxy->nvectors;
+ virtio_init_pci(proxy, vdev,
+ PCI_VENDOR_ID_REDHAT_QUMRANET,
+ PCI_DEVICE_ID_VIRTIO_CONSOLE,
+ proxy->class_code, 0x00);
+ proxy->nvectors = vdev->nvectors;
+ return 0;
+}
+
+static int virtio_serial_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_serial_exit(proxy->vdev);
+ return virtio_exit_pci(pci_dev);
+}
+
+static int virtio_net_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net);
+
+ vdev->nvectors = proxy->nvectors;
+ virtio_init_pci(proxy, vdev,
+ PCI_VENDOR_ID_REDHAT_QUMRANET,
+ PCI_DEVICE_ID_VIRTIO_NET,
+ PCI_CLASS_NETWORK_ETHERNET,
+ 0x00);
+
+ /* make the actual value visible */
+ proxy->nvectors = vdev->nvectors;
+ return 0;
+}
+
+static int virtio_net_exit_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+ virtio_pci_stop_ioeventfd(proxy);
+ virtio_net_exit(proxy->vdev);
+ return virtio_exit_pci(pci_dev);
+}
+
+static int virtio_balloon_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ vdev = virtio_balloon_init(&pci_dev->qdev);
+ virtio_init_pci(proxy, vdev,
+ PCI_VENDOR_ID_REDHAT_QUMRANET,
+ PCI_DEVICE_ID_VIRTIO_BALLOON,
+ PCI_CLASS_MEMORY_RAM,
+ 0x00);
+ return 0;
+}
+
+#ifdef CONFIG_VIRTFS
+static int virtio_9p_init_pci(PCIDevice *pci_dev)
+{
+ VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+ VirtIODevice *vdev;
+
+ vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf);
+ vdev->nvectors = proxy->nvectors;
+ virtio_init_pci(proxy, vdev,
+ PCI_VENDOR_ID_REDHAT_QUMRANET,
+ 0x1009,
+ 0x2,
+ 0x00);
+ /* make the actual value visible */
+ proxy->nvectors = vdev->nvectors;
+ return 0;
+}
+#endif
+
+static PCIDeviceInfo virtio_info[] = {
+ {
+ .qdev.name = "virtio-blk-pci",
+ .qdev.alias = "virtio-blk",
+ .qdev.size = sizeof(VirtIOPCIProxy),
+ .init = virtio_blk_init_pci,
+ .exit = virtio_blk_exit_pci,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block),
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ .qdev.reset = virtio_pci_reset,
+ },{
+ .qdev.name = "virtio-net-pci",
+ .qdev.size = sizeof(VirtIOPCIProxy),
+ .init = virtio_net_init_pci,
+ .exit = virtio_net_exit_pci,
+ .romfile = "pxe-virtio.bin",
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+ VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
+ DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic),
+ DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy,
+ net.txtimer, TX_TIMER_INTERVAL),
+ DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy,
+ net.txburst, TX_BURST),
+ DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ .qdev.reset = virtio_pci_reset,
+ },{
+ .qdev.name = "virtio-serial-pci",
+ .qdev.alias = "virtio-serial",
+ .qdev.size = sizeof(VirtIOPCIProxy),
+ .init = virtio_serial_init_pci,
+ .exit = virtio_serial_exit_pci,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+ DEV_NVECTORS_UNSPECIFIED),
+ DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, max_virtserial_ports,
+ 31),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ .qdev.reset = virtio_pci_reset,
+ },{
+ .qdev.name = "virtio-balloon-pci",
+ .qdev.size = sizeof(VirtIOPCIProxy),
+ .init = virtio_balloon_init_pci,
+ .exit = virtio_exit_pci,
+ .qdev.props = (Property[]) {
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ .qdev.reset = virtio_pci_reset,
+ },{
+#ifdef CONFIG_VIRTFS
+ .qdev.name = "virtio-9p-pci",
+ .qdev.size = sizeof(VirtIOPCIProxy),
+ .init = virtio_9p_init_pci,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag),
+ DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+ }, {
+#endif
+ /* end of list */
+ }
+};
+
+static void virtio_pci_register_devices(void)
+{
+ pci_qdev_register_many(virtio_info);
+}
+
+device_init(virtio_pci_register_devices)
diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c
new file mode 100644
index 0000000..e05ab5e
--- /dev/null
+++ b/hw/virtio-serial-bus.c
@@ -0,0 +1,908 @@
+/*
+ * A bus for connecting virtio serial and console ports
+ *
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ *
+ * Author(s):
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * Some earlier parts are:
+ * Copyright IBM, Corp. 2008
+ * authored by
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "iov.h"
+#include "monitor.h"
+#include "qemu-queue.h"
+#include "sysbus.h"
+#include "virtio-serial.h"
+
+/* The virtio-serial bus on top of which the ports will ride as devices */
+struct VirtIOSerialBus {
+ BusState qbus;
+
+ /* This is the parent device that provides the bus for ports. */
+ VirtIOSerial *vser;
+
+ /* The maximum number of ports that can ride on top of this bus */
+ uint32_t max_nr_ports;
+};
+
+struct VirtIOSerial {
+ VirtIODevice vdev;
+
+ VirtQueue *c_ivq, *c_ovq;
+ /* Arrays of ivqs and ovqs: one per port */
+ VirtQueue **ivqs, **ovqs;
+
+ VirtIOSerialBus *bus;
+
+ DeviceState *qdev;
+
+ QTAILQ_HEAD(, VirtIOSerialPort) ports;
+
+ /* bitmap for identifying active ports */
+ uint32_t *ports_map;
+
+ struct virtio_console_config config;
+};
+
+static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id)
+{
+ VirtIOSerialPort *port;
+
+ if (id == VIRTIO_CONSOLE_BAD_ID) {
+ return NULL;
+ }
+
+ QTAILQ_FOREACH(port, &vser->ports, next) {
+ if (port->id == id)
+ return port;
+ }
+ return NULL;
+}
+
+static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq)
+{
+ VirtIOSerialPort *port;
+
+ QTAILQ_FOREACH(port, &vser->ports, next) {
+ if (port->ivq == vq || port->ovq == vq)
+ return port;
+ }
+ return NULL;
+}
+
+static bool use_multiport(VirtIOSerial *vser)
+{
+ return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+}
+
+static size_t write_to_port(VirtIOSerialPort *port,
+ const uint8_t *buf, size_t size)
+{
+ VirtQueueElement elem;
+ VirtQueue *vq;
+ size_t offset;
+
+ vq = port->ivq;
+ if (!virtio_queue_ready(vq)) {
+ return 0;
+ }
+
+ offset = 0;
+ while (offset < size) {
+ size_t len;
+
+ if (!virtqueue_pop(vq, &elem)) {
+ break;
+ }
+
+ len = iov_from_buf(elem.in_sg, elem.in_num,
+ buf + offset, size - offset);
+ offset += len;
+
+ virtqueue_push(vq, &elem, len);
+ }
+
+ virtio_notify(&port->vser->vdev, vq);
+ return offset;
+}
+
+static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev)
+{
+ VirtQueueElement elem;
+
+ if (!virtio_queue_ready(vq)) {
+ return;
+ }
+ while (virtqueue_pop(vq, &elem)) {
+ virtqueue_push(vq, &elem, 0);
+ }
+ virtio_notify(vdev, vq);
+}
+
+static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
+ VirtIODevice *vdev)
+{
+ assert(port);
+ assert(virtio_queue_ready(vq));
+
+ while (!port->throttled) {
+ unsigned int i;
+
+ /* Pop an elem only if we haven't left off a previous one mid-way */
+ if (!port->elem.out_num) {
+ if (!virtqueue_pop(vq, &port->elem)) {
+ break;
+ }
+ port->iov_idx = 0;
+ port->iov_offset = 0;
+ }
+
+ for (i = port->iov_idx; i < port->elem.out_num; i++) {
+ size_t buf_size;
+ 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);
+ if (ret < 0 && ret != -EAGAIN) {
+ /* We don't handle any other type of errors here */
+ abort();
+ }
+ if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) {
+ virtio_serial_throttle_port(port, true);
+ port->iov_idx = i;
+ if (ret > 0) {
+ port->iov_offset += ret;
+ }
+ break;
+ }
+ port->iov_offset = 0;
+ }
+ if (port->throttled) {
+ break;
+ }
+ virtqueue_push(vq, &port->elem, 0);
+ port->elem.out_num = 0;
+ }
+ virtio_notify(vdev, vq);
+}
+
+static void flush_queued_data(VirtIOSerialPort *port)
+{
+ assert(port);
+
+ if (!virtio_queue_ready(port->ovq)) {
+ return;
+ }
+ do_flush_queued_data(port, port->ovq, &port->vser->vdev);
+}
+
+static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len)
+{
+ VirtQueueElement elem;
+ VirtQueue *vq;
+ struct virtio_console_control *cpkt;
+
+ vq = port->vser->c_ivq;
+ if (!virtio_queue_ready(vq)) {
+ return 0;
+ }
+ if (!virtqueue_pop(vq, &elem)) {
+ return 0;
+ }
+
+ cpkt = (struct virtio_console_control *)buf;
+ stl_p(&cpkt->id, port->id);
+ memcpy(elem.in_sg[0].iov_base, buf, len);
+
+ virtqueue_push(vq, &elem, len);
+ virtio_notify(&port->vser->vdev, vq);
+ return len;
+}
+
+static size_t send_control_event(VirtIOSerialPort *port, uint16_t event,
+ uint16_t value)
+{
+ struct virtio_console_control cpkt;
+
+ stw_p(&cpkt.event, event);
+ stw_p(&cpkt.value, value);
+
+ return send_control_msg(port, &cpkt, sizeof(cpkt));
+}
+
+/* Functions for use inside qemu to open and read from/write to ports */
+int virtio_serial_open(VirtIOSerialPort *port)
+{
+ /* Don't allow opening an already-open port */
+ if (port->host_connected) {
+ return 0;
+ }
+ /* Send port open notification to the guest */
+ port->host_connected = true;
+ send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
+
+ return 0;
+}
+
+int virtio_serial_close(VirtIOSerialPort *port)
+{
+ port->host_connected = false;
+ /*
+ * If there's any data the guest sent which the app didn't
+ * consume, reset the throttling flag and discard the data.
+ */
+ port->throttled = false;
+ discard_vq_data(port->ovq, &port->vser->vdev);
+
+ send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
+
+ return 0;
+}
+
+/* Individual ports/apps call this function to write to the guest. */
+ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
+ size_t size)
+{
+ if (!port || !port->host_connected || !port->guest_connected) {
+ return 0;
+ }
+ return write_to_port(port, buf, size);
+}
+
+/*
+ * Readiness of the guest to accept data on a port.
+ * Returns max. data the guest can receive
+ */
+size_t virtio_serial_guest_ready(VirtIOSerialPort *port)
+{
+ VirtQueue *vq = port->ivq;
+
+ if (!virtio_queue_ready(vq) ||
+ !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
+ virtio_queue_empty(vq)) {
+ return 0;
+ }
+ if (use_multiport(port->vser) && !port->guest_connected) {
+ return 0;
+ }
+
+ if (virtqueue_avail_bytes(vq, 4096, 0)) {
+ return 4096;
+ }
+ if (virtqueue_avail_bytes(vq, 1, 0)) {
+ return 1;
+ }
+ return 0;
+}
+
+void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle)
+{
+ if (!port) {
+ return;
+ }
+
+ port->throttled = throttle;
+ if (throttle) {
+ return;
+ }
+
+ flush_queued_data(port);
+}
+
+/* Guest wants to notify us of some event */
+static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len)
+{
+ struct VirtIOSerialPort *port;
+ struct virtio_console_control cpkt, *gcpkt;
+ uint8_t *buffer;
+ size_t buffer_len;
+
+ gcpkt = buf;
+
+ if (len < sizeof(cpkt)) {
+ /* The guest sent an invalid control packet */
+ return;
+ }
+
+ cpkt.event = lduw_p(&gcpkt->event);
+ cpkt.value = lduw_p(&gcpkt->value);
+
+ port = find_port_by_id(vser, ldl_p(&gcpkt->id));
+ if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY)
+ return;
+
+ switch(cpkt.event) {
+ case VIRTIO_CONSOLE_DEVICE_READY:
+ if (!cpkt.value) {
+ error_report("virtio-serial-bus: Guest failure in adding device %s\n",
+ vser->bus->qbus.name);
+ break;
+ }
+ /*
+ * The device is up, we can now tell the device about all the
+ * ports we have here.
+ */
+ QTAILQ_FOREACH(port, &vser->ports, next) {
+ send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1);
+ }
+ break;
+
+ 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);
+ break;
+ }
+ /*
+ * Now that we know the guest asked for the port name, we're
+ * sure the guest has initialised whatever state is necessary
+ * for this port. Now's a good time to let the guest know if
+ * this port is a console port so that the guest can hook it
+ * up to hvc.
+ */
+ if (port->is_console) {
+ send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1);
+ }
+
+ if (port->name) {
+ stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME);
+ stw_p(&cpkt.value, 1);
+
+ buffer_len = sizeof(cpkt) + strlen(port->name) + 1;
+ buffer = qemu_malloc(buffer_len);
+
+ memcpy(buffer, &cpkt, sizeof(cpkt));
+ memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name));
+ buffer[buffer_len - 1] = 0;
+
+ send_control_msg(port, buffer, buffer_len);
+ qemu_free(buffer);
+ }
+
+ if (port->host_connected) {
+ send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
+ }
+
+ /*
+ * When the guest has asked us for this information it means
+ * the guest is all setup and has its virtqueues
+ * initialised. If some app is interested in knowing about
+ * this event, let it know.
+ */
+ if (port->info->guest_ready) {
+ port->info->guest_ready(port);
+ }
+ break;
+
+ case VIRTIO_CONSOLE_PORT_OPEN:
+ port->guest_connected = cpkt.value;
+ if (cpkt.value && port->info->guest_open) {
+ /* Send the guest opened notification if an app is interested */
+ port->info->guest_open(port);
+ }
+
+ if (!cpkt.value && port->info->guest_close) {
+ /* Send the guest closed notification if an app is interested */
+ port->info->guest_close(port);
+ }
+ break;
+ }
+}
+
+static void control_in(VirtIODevice *vdev, VirtQueue *vq)
+{
+}
+
+static void control_out(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtQueueElement elem;
+ VirtIOSerial *vser;
+ uint8_t *buf;
+ size_t len;
+
+ vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
+
+ len = 0;
+ buf = NULL;
+ while (virtqueue_pop(vq, &elem)) {
+ size_t cur_len, copied;
+
+ cur_len = iov_size(elem.out_sg, elem.out_num);
+ /*
+ * Allocate a new buf only if we didn't have one previously or
+ * if the size of the buf differs
+ */
+ if (cur_len > len) {
+ qemu_free(buf);
+
+ buf = qemu_malloc(cur_len);
+ len = cur_len;
+ }
+ copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len);
+
+ handle_control_message(vser, buf, copied);
+ virtqueue_push(vq, &elem, 0);
+ }
+ qemu_free(buf);
+ virtio_notify(vdev, vq);
+}
+
+/* Guest wrote something to some port. */
+static void handle_output(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOSerial *vser;
+ VirtIOSerialPort *port;
+ bool discard;
+
+ vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
+ port = find_port_by_vq(vser, vq);
+
+ discard = false;
+ if (!port || !port->host_connected || !port->info->have_data) {
+ discard = true;
+ }
+
+ if (discard) {
+ discard_vq_data(vq, vdev);
+ return;
+ }
+ if (port->throttled) {
+ return;
+ }
+
+ do_flush_queued_data(port, vq, vdev);
+}
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+}
+
+static uint32_t get_features(VirtIODevice *vdev, uint32_t features)
+{
+ VirtIOSerial *vser;
+
+ vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
+
+ if (vser->bus->max_nr_ports > 1) {
+ features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+ }
+ return features;
+}
+
+/* Guest requested config info */
+static void get_config(VirtIODevice *vdev, uint8_t *config_data)
+{
+ VirtIOSerial *vser;
+
+ vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
+ memcpy(config_data, &vser->config, sizeof(struct virtio_console_config));
+}
+
+static void set_config(VirtIODevice *vdev, const uint8_t *config_data)
+{
+ struct virtio_console_config config;
+
+ memcpy(&config, config_data, sizeof(config));
+}
+
+static void virtio_serial_save(QEMUFile *f, void *opaque)
+{
+ VirtIOSerial *s = opaque;
+ VirtIOSerialPort *port;
+ uint32_t nr_active_ports;
+ unsigned int i;
+
+ /* The virtio device */
+ virtio_save(&s->vdev, f);
+
+ /* The config space */
+ qemu_put_be16s(f, &s->config.cols);
+ qemu_put_be16s(f, &s->config.rows);
+
+ qemu_put_be32s(f, &s->config.max_nr_ports);
+
+ /* The ports map */
+
+ for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) {
+ qemu_put_be32s(f, &s->ports_map[i]);
+ }
+
+ /* Ports */
+
+ nr_active_ports = 0;
+ QTAILQ_FOREACH(port, &s->ports, next) {
+ nr_active_ports++;
+ }
+
+ qemu_put_be32s(f, &nr_active_ports);
+
+ /*
+ * Items in struct VirtIOSerialPort.
+ */
+ QTAILQ_FOREACH(port, &s->ports, next) {
+ uint32_t elem_popped;
+
+ qemu_put_be32s(f, &port->id);
+ qemu_put_byte(f, port->guest_connected);
+ qemu_put_byte(f, port->host_connected);
+
+ elem_popped = 0;
+ if (port->elem.out_num) {
+ elem_popped = 1;
+ }
+ qemu_put_be32s(f, &elem_popped);
+ if (elem_popped) {
+ qemu_put_be32s(f, &port->iov_idx);
+ qemu_put_be64s(f, &port->iov_offset);
+
+ qemu_put_buffer(f, (unsigned char *)&port->elem,
+ sizeof(port->elem));
+ }
+ }
+}
+
+static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id)
+{
+ VirtIOSerial *s = opaque;
+ VirtIOSerialPort *port;
+ uint32_t max_nr_ports, nr_active_ports, ports_map;
+ unsigned int i;
+
+ if (version_id > 3) {
+ return -EINVAL;
+ }
+
+ /* The virtio device */
+ virtio_load(&s->vdev, f);
+
+ if (version_id < 2) {
+ return 0;
+ }
+
+ /* The config space */
+ qemu_get_be16s(f, &s->config.cols);
+ qemu_get_be16s(f, &s->config.rows);
+
+ qemu_get_be32s(f, &max_nr_ports);
+ if (max_nr_ports > s->config.max_nr_ports) {
+ /* Source could have had more ports than us. Fail migration. */
+ return -EINVAL;
+ }
+
+ for (i = 0; i < (max_nr_ports + 31) / 32; i++) {
+ qemu_get_be32s(f, &ports_map);
+
+ if (ports_map != s->ports_map[i]) {
+ /*
+ * Ports active on source and destination don't
+ * match. Fail migration.
+ */
+ return -EINVAL;
+ }
+ }
+
+ qemu_get_be32s(f, &nr_active_ports);
+
+ /* Items in struct VirtIOSerialPort */
+ for (i = 0; i < nr_active_ports; i++) {
+ uint32_t id;
+ bool host_connected;
+
+ id = qemu_get_be32(f);
+ port = find_port_by_id(s, id);
+
+ port->guest_connected = qemu_get_byte(f);
+ host_connected = qemu_get_byte(f);
+ if (host_connected != port->host_connected) {
+ /*
+ * We have to let the guest know of the host connection
+ * status change
+ */
+ send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN,
+ port->host_connected);
+ }
+
+ if (version_id > 2) {
+ uint32_t elem_popped;
+
+ qemu_get_be32s(f, &elem_popped);
+ if (elem_popped) {
+ qemu_get_be32s(f, &port->iov_idx);
+ qemu_get_be64s(f, &port->iov_offset);
+
+ qemu_get_buffer(f, (unsigned char *)&port->elem,
+ sizeof(port->elem));
+ virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr,
+ port->elem.in_num, 1);
+ virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr,
+ port->elem.out_num, 1);
+
+ /*
+ * Port was throttled on source machine. Let's
+ * unthrottle it here so data starts flowing again.
+ */
+ virtio_serial_throttle_port(port, false);
+ }
+ }
+ }
+ return 0;
+}
+
+static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
+
+static struct BusInfo virtser_bus_info = {
+ .name = "virtio-serial-bus",
+ .size = sizeof(VirtIOSerialBus),
+ .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)
+{
+ VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev);
+ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+
+ monitor_printf(mon, "%*s dev-prop-int: id: %u\n",
+ indent, "", port->id);
+ monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n",
+ indent, "", port->guest_connected);
+ monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n",
+ indent, "", port->host_connected);
+ monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n",
+ indent, "", port->throttled);
+}
+
+/* This function is only used if a port id is not provided by the user */
+static uint32_t find_free_port_id(VirtIOSerial *vser)
+{
+ unsigned int i;
+
+ for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) {
+ uint32_t map, bit;
+
+ map = vser->ports_map[i];
+ bit = ffs(~map);
+ if (bit) {
+ return (bit - 1) + i * 32;
+ }
+ }
+ return VIRTIO_CONSOLE_BAD_ID;
+}
+
+static void mark_port_added(VirtIOSerial *vser, uint32_t port_id)
+{
+ unsigned int i;
+
+ i = port_id / 32;
+ vser->ports_map[i] |= 1U << (port_id % 32);
+}
+
+static void add_port(VirtIOSerial *vser, uint32_t port_id)
+{
+ mark_port_added(vser, port_id);
+
+ send_control_event(find_port_by_id(vser, port_id),
+ VIRTIO_CONSOLE_PORT_ADD, 1);
+}
+
+static void remove_port(VirtIOSerial *vser, uint32_t port_id)
+{
+ VirtIOSerialPort *port;
+ unsigned int i;
+
+ i = port_id / 32;
+ vser->ports_map[i] &= ~(1U << (port_id % 32));
+
+ port = find_port_by_id(vser, port_id);
+ /* Flush out any unconsumed buffers first */
+ discard_vq_data(port->ovq, &port->vser->vdev);
+
+ send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1);
+}
+
+static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+ VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev);
+ VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base);
+ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+ VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus);
+ int ret;
+ bool plugging_port0;
+
+ port->vser = bus->vser;
+
+ /*
+ * Is the first console port we're seeing? If so, put it up at
+ * location 0. This is done for backward compatibility (old
+ * kernel, new qemu).
+ */
+ plugging_port0 = port->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",
+ port->id);
+ return -1;
+ }
+
+ if (port->id == VIRTIO_CONSOLE_BAD_ID) {
+ if (plugging_port0) {
+ port->id = 0;
+ } else {
+ port->id = find_free_port_id(port->vser);
+ if (port->id == VIRTIO_CONSOLE_BAD_ID) {
+ error_report("virtio-serial-bus: Maximum port limit for this device reached\n");
+ return -1;
+ }
+ }
+ }
+
+ if (port->id >= port->vser->config.max_nr_ports) {
+ error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n",
+ port->vser->config.max_nr_ports - 1);
+ return -1;
+ }
+
+ dev->info = info;
+ ret = info->init(dev);
+ if (ret) {
+ return ret;
+ }
+
+ if (!use_multiport(port->vser)) {
+ /*
+ * Allow writes to guest in this case; we have no way of
+ * knowing if a guest port is connected.
+ */
+ port->guest_connected = true;
+ }
+
+ port->elem.out_num = 0;
+
+ QTAILQ_INSERT_TAIL(&port->vser->ports, port, next);
+ port->ivq = port->vser->ivqs[port->id];
+ port->ovq = port->vser->ovqs[port->id];
+
+ add_port(port->vser, port->id);
+
+ /* Send an update to the guest about this new port added */
+ virtio_notify_config(&port->vser->vdev);
+
+ return ret;
+}
+
+static int virtser_port_qdev_exit(DeviceState *qdev)
+{
+ VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev);
+ VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+ VirtIOSerial *vser = port->vser;
+
+ remove_port(port->vser, port->id);
+
+ QTAILQ_REMOVE(&vser->ports, port, next);
+
+ if (port->info->exit)
+ port->info->exit(dev);
+
+ return 0;
+}
+
+void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info)
+{
+ info->qdev.init = virtser_port_qdev_init;
+ info->qdev.bus_info = &virtser_bus_info;
+ info->qdev.exit = virtser_port_qdev_exit;
+ info->qdev.unplug = qdev_simple_unplug_cb;
+ qdev_register(&info->qdev);
+}
+
+VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports)
+{
+ VirtIOSerial *vser;
+ VirtIODevice *vdev;
+ uint32_t i, max_supported_ports;
+
+ if (!max_nr_ports)
+ return NULL;
+
+ /* Each port takes 2 queues, and one pair is for the control queue */
+ max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1;
+
+ if (max_nr_ports > max_supported_ports) {
+ error_report("maximum ports supported: %u", max_supported_ports);
+ return NULL;
+ }
+
+ vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE,
+ sizeof(struct virtio_console_config),
+ sizeof(VirtIOSerial));
+
+ 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;
+ QTAILQ_INIT(&vser->ports);
+
+ vser->bus->max_nr_ports = max_nr_ports;
+ vser->ivqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
+ vser->ovqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *));
+
+ /* Add a queue for host to guest transfers for port 0 (backward compat) */
+ vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input);
+ /* Add a queue for guest to host transfers for port 0 (backward compat) */
+ vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output);
+
+ /* TODO: host to guest notifications can get dropped
+ * if the queue fills up. Implement queueing in host,
+ * this might also make it possible to reduce the control
+ * queue size: as guest preposts buffers there,
+ * this will save 4Kbyte of guest memory per entry. */
+
+ /* control queue: host to guest */
+ vser->c_ivq = virtio_add_queue(vdev, 32, control_in);
+ /* 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++) {
+ /* 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 */
+ vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output);
+ }
+
+ vser->config.max_nr_ports = max_nr_ports;
+ vser->ports_map = qemu_mallocz(((max_nr_ports + 31) / 32)
+ * sizeof(vser->ports_map[0]));
+ /*
+ * Reserve location 0 for a console port for backward compat
+ * (old kernel, new qemu)
+ */
+ mark_port_added(vser, 0);
+
+ vser->vdev.get_features = get_features;
+ vser->vdev.get_config = get_config;
+ vser->vdev.set_config = set_config;
+
+ vser->qdev = dev;
+
+ /*
+ * Register for the savevm section with the virtio-console name
+ * to preserve backward compat
+ */
+ register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save,
+ virtio_serial_load, vser);
+
+ return vdev;
+}
+
+void virtio_serial_exit(VirtIODevice *vdev)
+{
+ VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev);
+
+ unregister_savevm(vser->qdev, "virtio-console", vser);
+
+ qemu_free(vser->ivqs);
+ qemu_free(vser->ovqs);
+ qemu_free(vser->ports_map);
+
+ virtio_cleanup(vdev);
+}
diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h
new file mode 100644
index 0000000..a308196
--- /dev/null
+++ b/hw/virtio-serial.h
@@ -0,0 +1,202 @@
+/*
+ * Virtio Serial / Console Support
+ *
+ * Copyright IBM, Corp. 2008
+ * Copyright Red Hat, Inc. 2009, 2010
+ *
+ * Authors:
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#ifndef _QEMU_VIRTIO_SERIAL_H
+#define _QEMU_VIRTIO_SERIAL_H
+
+#include "qdev.h"
+#include "virtio.h"
+
+/* == Interface shared between the guest kernel and qemu == */
+
+/* The Virtio ID for virtio console / serial ports */
+#define VIRTIO_ID_CONSOLE 3
+
+/* Features supported */
+#define VIRTIO_CONSOLE_F_MULTIPORT 1
+
+#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0)
+
+struct virtio_console_config {
+ /*
+ * These two fields are used by VIRTIO_CONSOLE_F_SIZE which
+ * isn't implemented here yet
+ */
+ uint16_t cols;
+ uint16_t rows;
+
+ uint32_t max_nr_ports;
+} __attribute__((packed));
+
+struct virtio_console_control {
+ uint32_t id; /* Port number */
+ uint16_t event; /* The kind of control event (see below) */
+ uint16_t value; /* Extra information for the key */
+};
+
+/* Some events for the internal messages (control packets) */
+#define VIRTIO_CONSOLE_DEVICE_READY 0
+#define VIRTIO_CONSOLE_PORT_ADD 1
+#define VIRTIO_CONSOLE_PORT_REMOVE 2
+#define VIRTIO_CONSOLE_PORT_READY 3
+#define VIRTIO_CONSOLE_CONSOLE_PORT 4
+#define VIRTIO_CONSOLE_RESIZE 5
+#define VIRTIO_CONSOLE_PORT_OPEN 6
+#define VIRTIO_CONSOLE_PORT_NAME 7
+
+/* == In-qemu interface == */
+
+typedef struct VirtIOSerial VirtIOSerial;
+typedef struct VirtIOSerialBus VirtIOSerialBus;
+typedef struct VirtIOSerialPort VirtIOSerialPort;
+typedef struct VirtIOSerialPortInfo VirtIOSerialPortInfo;
+
+typedef struct VirtIOSerialDevice {
+ DeviceState qdev;
+ VirtIOSerialPortInfo *info;
+} VirtIOSerialDevice;
+
+/*
+ * This is the state that's shared between all the ports. Some of the
+ * state is configurable via command-line options. Some of it can be
+ * set by individual devices in their initfn routines. Some of the
+ * state is set by the generic qdev device init routine.
+ */
+struct VirtIOSerialPort {
+ DeviceState dev;
+ VirtIOSerialPortInfo *info;
+
+ QTAILQ_ENTRY(VirtIOSerialPort) next;
+
+ /*
+ * This field gives us the virtio device as well as the qdev bus
+ * that we are associated with
+ */
+ VirtIOSerial *vser;
+
+ VirtQueue *ivq, *ovq;
+
+ /*
+ * This name is sent to the guest and exported via sysfs.
+ * The guest could create symlinks based on this information.
+ * The name is in the reverse fqdn format, like org.qemu.console.0
+ */
+ char *name;
+
+ /*
+ * This id helps identify ports between the guest and the host.
+ * The guest sends a "header" with this id with each data packet
+ * that it sends and the host can then find out which associated
+ * device to send out this data to
+ */
+ uint32_t id;
+
+ /*
+ * This is the elem that we pop from the virtqueue. A slow
+ * backend that consumes guest data (e.g. the file backend for
+ * qemu chardevs) can cause the guest to block till all the output
+ * is flushed. This isn't desired, so we keep a note of the last
+ * element popped and continue consuming it once the backend
+ * becomes writable again.
+ */
+ VirtQueueElement elem;
+
+ /*
+ * The index and the offset into the iov buffer that was popped in
+ * elem above.
+ */
+ uint32_t iov_idx;
+ uint64_t iov_offset;
+
+ /* Identify if this is a port that binds with hvc in the guest */
+ uint8_t is_console;
+
+ /* Is the corresponding guest device open? */
+ bool guest_connected;
+ /* Is this device open for IO on the host? */
+ bool host_connected;
+ /* Do apps not want to receive data? */
+ bool throttled;
+};
+
+struct VirtIOSerialPortInfo {
+ DeviceInfo qdev;
+ /*
+ * The per-port (or per-app) init function that's called when a
+ * new device is found on the bus.
+ */
+ int (*init)(VirtIOSerialDevice *dev);
+ /*
+ * Per-port exit function that's called when a port gets
+ * hot-unplugged or removed.
+ */
+ int (*exit)(VirtIOSerialDevice *dev);
+
+ /* Callbacks for guest events */
+ /* Guest opened device. */
+ void (*guest_open)(VirtIOSerialPort *port);
+ /* Guest closed device. */
+ void (*guest_close)(VirtIOSerialPort *port);
+
+ /* Guest is now ready to accept data (virtqueues set up). */
+ void (*guest_ready)(VirtIOSerialPort *port);
+
+ /*
+ * Guest wrote some data to the port. This data is handed over to
+ * the app via this callback. The app can return a size less than
+ * 'len'. In this case, throttling will be enabled for this port.
+ */
+ ssize_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf,
+ size_t len);
+};
+
+/* Interface to the virtio-serial bus */
+
+/*
+ * Individual ports/apps should call this function to register the port
+ * with the virtio-serial bus
+ */
+void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info);
+
+/*
+ * Open a connection to the port
+ * Returns 0 on success (always).
+ */
+int virtio_serial_open(VirtIOSerialPort *port);
+
+/*
+ * Close the connection to the port
+ * Returns 0 on success (always).
+ */
+int virtio_serial_close(VirtIOSerialPort *port);
+
+/*
+ * Send data to Guest
+ */
+ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf,
+ size_t size);
+
+/*
+ * Query whether a guest is ready to receive data.
+ */
+size_t virtio_serial_guest_ready(VirtIOSerialPort *port);
+
+/*
+ * Flow control: Ports can signal to the virtio-serial core to stop
+ * sending data or re-start sending data, depending on the 'throttle'
+ * value here.
+ */
+void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle);
+
+#endif
diff --git a/hw/virtio.c b/hw/virtio.c
new file mode 100644
index 0000000..31bd9e3
--- /dev/null
+++ b/hw/virtio.c
@@ -0,0 +1,882 @@
+/*
+ * Virtio Support
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <inttypes.h>
+
+#include "trace.h"
+#include "qemu-error.h"
+#include "virtio.h"
+#include "sysemu.h"
+
+/* The alignment to use between consumer and producer parts of vring.
+ * x86 pagesize again. */
+#define VIRTIO_PCI_VRING_ALIGN 4096
+
+/* QEMU doesn't strictly need write barriers since everything runs in
+ * lock-step. We'll leave the calls to wmb() in though to make it obvious for
+ * KVM or if kqemu gets SMP support.
+ * In any case, we must prevent the compiler from reordering the code.
+ * TODO: we likely need some rmb()/mb() as well.
+ */
+
+#define wmb() __asm__ __volatile__("": : :"memory")
+
+typedef struct VRingDesc
+{
+ uint64_t addr;
+ uint32_t len;
+ uint16_t flags;
+ uint16_t next;
+} VRingDesc;
+
+typedef struct VRingAvail
+{
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[0];
+} VRingAvail;
+
+typedef struct VRingUsedElem
+{
+ uint32_t id;
+ uint32_t len;
+} VRingUsedElem;
+
+typedef struct VRingUsed
+{
+ uint16_t flags;
+ uint16_t idx;
+ VRingUsedElem ring[0];
+} VRingUsed;
+
+typedef struct VRing
+{
+ unsigned int num;
+ target_phys_addr_t desc;
+ target_phys_addr_t avail;
+ target_phys_addr_t used;
+} VRing;
+
+struct VirtQueue
+{
+ VRing vring;
+ target_phys_addr_t pa;
+ uint16_t last_avail_idx;
+ int inuse;
+ uint16_t vector;
+ void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
+ VirtIODevice *vdev;
+ EventNotifier guest_notifier;
+ EventNotifier host_notifier;
+};
+
+/* virt queue functions */
+static void virtqueue_init(VirtQueue *vq)
+{
+ target_phys_addr_t pa = vq->pa;
+
+ vq->vring.desc = pa;
+ vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
+ vq->vring.used = vring_align(vq->vring.avail +
+ offsetof(VRingAvail, ring[vq->vring.num]),
+ VIRTIO_PCI_VRING_ALIGN);
+}
+
+static inline uint64_t vring_desc_addr(target_phys_addr_t desc_pa, int i)
+{
+ target_phys_addr_t pa;
+ pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr);
+ return ldq_phys(pa);
+}
+
+static inline uint32_t vring_desc_len(target_phys_addr_t desc_pa, int i)
+{
+ target_phys_addr_t pa;
+ pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, len);
+ return ldl_phys(pa);
+}
+
+static inline uint16_t vring_desc_flags(target_phys_addr_t desc_pa, int i)
+{
+ target_phys_addr_t pa;
+ pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags);
+ return lduw_phys(pa);
+}
+
+static inline uint16_t vring_desc_next(target_phys_addr_t desc_pa, int i)
+{
+ target_phys_addr_t pa;
+ pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, next);
+ return lduw_phys(pa);
+}
+
+static inline uint16_t vring_avail_flags(VirtQueue *vq)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.avail + offsetof(VRingAvail, flags);
+ return lduw_phys(pa);
+}
+
+static inline uint16_t vring_avail_idx(VirtQueue *vq)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.avail + offsetof(VRingAvail, idx);
+ return lduw_phys(pa);
+}
+
+static inline uint16_t vring_avail_ring(VirtQueue *vq, int i)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.avail + offsetof(VRingAvail, ring[i]);
+ return lduw_phys(pa);
+}
+
+static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.used + offsetof(VRingUsed, ring[i].id);
+ stl_phys(pa, val);
+}
+
+static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.used + offsetof(VRingUsed, ring[i].len);
+ stl_phys(pa, val);
+}
+
+static uint16_t vring_used_idx(VirtQueue *vq)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.used + offsetof(VRingUsed, idx);
+ return lduw_phys(pa);
+}
+
+static inline void vring_used_idx_increment(VirtQueue *vq, uint16_t val)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.used + offsetof(VRingUsed, idx);
+ stw_phys(pa, vring_used_idx(vq) + val);
+}
+
+static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.used + offsetof(VRingUsed, flags);
+ stw_phys(pa, lduw_phys(pa) | mask);
+}
+
+static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask)
+{
+ target_phys_addr_t pa;
+ pa = vq->vring.used + offsetof(VRingUsed, flags);
+ stw_phys(pa, lduw_phys(pa) & ~mask);
+}
+
+void virtio_queue_set_notification(VirtQueue *vq, int enable)
+{
+ if (enable)
+ vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY);
+ else
+ vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY);
+}
+
+int virtio_queue_ready(VirtQueue *vq)
+{
+ return vq->vring.avail != 0;
+}
+
+int virtio_queue_empty(VirtQueue *vq)
+{
+ return vring_avail_idx(vq) == vq->last_avail_idx;
+}
+
+void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len, unsigned int idx)
+{
+ unsigned int offset;
+ int i;
+
+ trace_virtqueue_fill(vq, elem, len, idx);
+
+ offset = 0;
+ for (i = 0; i < elem->in_num; i++) {
+ size_t size = MIN(len - offset, elem->in_sg[i].iov_len);
+
+ cpu_physical_memory_unmap(elem->in_sg[i].iov_base,
+ elem->in_sg[i].iov_len,
+ 1, size);
+
+ offset += elem->in_sg[i].iov_len;
+ }
+
+ for (i = 0; i < elem->out_num; i++)
+ cpu_physical_memory_unmap(elem->out_sg[i].iov_base,
+ elem->out_sg[i].iov_len,
+ 0, elem->out_sg[i].iov_len);
+
+ idx = (idx + vring_used_idx(vq)) % vq->vring.num;
+
+ /* Get a pointer to the next entry in the used ring. */
+ vring_used_ring_id(vq, idx, elem->index);
+ vring_used_ring_len(vq, idx, len);
+}
+
+void virtqueue_flush(VirtQueue *vq, unsigned int count)
+{
+ /* Make sure buffer is written before we update index. */
+ wmb();
+ trace_virtqueue_flush(vq, count);
+ vring_used_idx_increment(vq, count);
+ vq->inuse -= count;
+}
+
+void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len)
+{
+ virtqueue_fill(vq, elem, len, 0);
+ virtqueue_flush(vq, 1);
+}
+
+static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx)
+{
+ uint16_t num_heads = vring_avail_idx(vq) - idx;
+
+ /* Check it isn't doing very strange things with descriptor numbers. */
+ if (num_heads > vq->vring.num) {
+ error_report("Guest moved used index from %u to %u",
+ idx, vring_avail_idx(vq));
+ exit(1);
+ }
+
+ return num_heads;
+}
+
+static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx)
+{
+ unsigned int head;
+
+ /* Grab the next descriptor number they're advertising, and increment
+ * the index we've seen. */
+ head = vring_avail_ring(vq, idx % vq->vring.num);
+
+ /* If their number is silly, that's a fatal mistake. */
+ if (head >= vq->vring.num) {
+ error_report("Guest says index %u is available", head);
+ exit(1);
+ }
+
+ return head;
+}
+
+static unsigned virtqueue_next_desc(target_phys_addr_t desc_pa,
+ unsigned int i, unsigned int max)
+{
+ unsigned int next;
+
+ /* If this descriptor says it doesn't chain, we're done. */
+ if (!(vring_desc_flags(desc_pa, i) & VRING_DESC_F_NEXT))
+ return max;
+
+ /* Check they're not leading us off end of descriptors. */
+ next = vring_desc_next(desc_pa, i);
+ /* Make sure compiler knows to grab that: we don't want it changing! */
+ wmb();
+
+ if (next >= max) {
+ error_report("Desc next is %u", next);
+ exit(1);
+ }
+
+ return next;
+}
+
+int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes)
+{
+ unsigned int idx;
+ int total_bufs, in_total, out_total;
+
+ idx = vq->last_avail_idx;
+
+ total_bufs = in_total = out_total = 0;
+ while (virtqueue_num_heads(vq, idx)) {
+ unsigned int max, num_bufs, indirect = 0;
+ target_phys_addr_t desc_pa;
+ int i;
+
+ max = vq->vring.num;
+ num_bufs = total_bufs;
+ i = virtqueue_get_head(vq, idx++);
+ desc_pa = vq->vring.desc;
+
+ if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) {
+ if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) {
+ error_report("Invalid size for indirect buffer table");
+ exit(1);
+ }
+
+ /* If we've got too many, that implies a descriptor loop. */
+ if (num_bufs >= max) {
+ error_report("Looped descriptor");
+ exit(1);
+ }
+
+ /* loop over the indirect descriptor table */
+ indirect = 1;
+ max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc);
+ num_bufs = i = 0;
+ desc_pa = vring_desc_addr(desc_pa, i);
+ }
+
+ do {
+ /* If we've got too many, that implies a descriptor loop. */
+ if (++num_bufs > max) {
+ error_report("Looped descriptor");
+ exit(1);
+ }
+
+ if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
+ if (in_bytes > 0 &&
+ (in_total += vring_desc_len(desc_pa, i)) >= in_bytes)
+ return 1;
+ } else {
+ if (out_bytes > 0 &&
+ (out_total += vring_desc_len(desc_pa, i)) >= out_bytes)
+ return 1;
+ }
+ } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
+
+ if (!indirect)
+ total_bufs = num_bufs;
+ else
+ total_bufs++;
+ }
+
+ return 0;
+}
+
+void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
+ size_t num_sg, int is_write)
+{
+ unsigned int i;
+ target_phys_addr_t len;
+
+ for (i = 0; i < num_sg; i++) {
+ len = sg[i].iov_len;
+ sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write);
+ if (sg[i].iov_base == NULL || len != sg[i].iov_len) {
+ error_report("virtio: trying to map MMIO memory");
+ exit(1);
+ }
+ }
+}
+
+int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
+{
+ unsigned int i, head, max;
+ target_phys_addr_t desc_pa = vq->vring.desc;
+
+ if (!virtqueue_num_heads(vq, vq->last_avail_idx))
+ return 0;
+
+ /* When we start there are none of either input nor output. */
+ elem->out_num = elem->in_num = 0;
+
+ max = vq->vring.num;
+
+ i = head = virtqueue_get_head(vq, vq->last_avail_idx++);
+
+ if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) {
+ if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) {
+ error_report("Invalid size for indirect buffer table");
+ exit(1);
+ }
+
+ /* loop over the indirect descriptor table */
+ max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc);
+ desc_pa = vring_desc_addr(desc_pa, i);
+ i = 0;
+ }
+
+ /* Collect all the descriptors */
+ do {
+ struct iovec *sg;
+
+ if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
+ elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i);
+ sg = &elem->in_sg[elem->in_num++];
+ } else {
+ elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i);
+ sg = &elem->out_sg[elem->out_num++];
+ }
+
+ sg->iov_len = vring_desc_len(desc_pa, i);
+
+ /* If we've got too many, that implies a descriptor loop. */
+ if ((elem->in_num + elem->out_num) > max) {
+ error_report("Looped descriptor");
+ exit(1);
+ }
+ } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
+
+ /* Now map what we have collected */
+ virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
+ virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0);
+
+ elem->index = head;
+
+ vq->inuse++;
+
+ trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num);
+ return elem->in_num + elem->out_num;
+}
+
+/* virtio device */
+static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector)
+{
+ if (vdev->binding->notify) {
+ vdev->binding->notify(vdev->binding_opaque, vector);
+ }
+}
+
+void virtio_update_irq(VirtIODevice *vdev)
+{
+ virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
+}
+
+void virtio_reset(void *opaque)
+{
+ VirtIODevice *vdev = opaque;
+ int i;
+
+ virtio_set_status(vdev, 0);
+
+ if (vdev->reset)
+ vdev->reset(vdev);
+
+ vdev->guest_features = 0;
+ vdev->queue_sel = 0;
+ vdev->status = 0;
+ vdev->isr = 0;
+ vdev->config_vector = VIRTIO_NO_VECTOR;
+ virtio_notify_vector(vdev, vdev->config_vector);
+
+ for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ vdev->vq[i].vring.desc = 0;
+ vdev->vq[i].vring.avail = 0;
+ vdev->vq[i].vring.used = 0;
+ vdev->vq[i].last_avail_idx = 0;
+ vdev->vq[i].pa = 0;
+ vdev->vq[i].vector = VIRTIO_NO_VECTOR;
+ }
+}
+
+uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr)
+{
+ uint8_t val;
+
+ vdev->get_config(vdev, vdev->config);
+
+ if (addr > (vdev->config_len - sizeof(val)))
+ return (uint32_t)-1;
+
+ memcpy(&val, vdev->config + addr, sizeof(val));
+ return val;
+}
+
+uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr)
+{
+ uint16_t val;
+
+ vdev->get_config(vdev, vdev->config);
+
+ if (addr > (vdev->config_len - sizeof(val)))
+ return (uint32_t)-1;
+
+ memcpy(&val, vdev->config + addr, sizeof(val));
+ return val;
+}
+
+uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr)
+{
+ uint32_t val;
+
+ vdev->get_config(vdev, vdev->config);
+
+ if (addr > (vdev->config_len - sizeof(val)))
+ return (uint32_t)-1;
+
+ memcpy(&val, vdev->config + addr, sizeof(val));
+ return val;
+}
+
+void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data)
+{
+ uint8_t val = data;
+
+ if (addr > (vdev->config_len - sizeof(val)))
+ return;
+
+ memcpy(vdev->config + addr, &val, sizeof(val));
+
+ if (vdev->set_config)
+ vdev->set_config(vdev, vdev->config);
+}
+
+void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data)
+{
+ uint16_t val = data;
+
+ if (addr > (vdev->config_len - sizeof(val)))
+ return;
+
+ memcpy(vdev->config + addr, &val, sizeof(val));
+
+ if (vdev->set_config)
+ vdev->set_config(vdev, vdev->config);
+}
+
+void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data)
+{
+ uint32_t val = data;
+
+ if (addr > (vdev->config_len - sizeof(val)))
+ return;
+
+ memcpy(vdev->config + addr, &val, sizeof(val));
+
+ if (vdev->set_config)
+ vdev->set_config(vdev, vdev->config);
+}
+
+void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr)
+{
+ vdev->vq[n].pa = addr;
+ virtqueue_init(&vdev->vq[n]);
+}
+
+target_phys_addr_t virtio_queue_get_addr(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].pa;
+}
+
+int virtio_queue_get_num(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.num;
+}
+
+void virtio_queue_notify_vq(VirtQueue *vq)
+{
+ if (vq->vring.desc) {
+ VirtIODevice *vdev = vq->vdev;
+ trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
+ vq->handle_output(vdev, vq);
+ }
+}
+
+void virtio_queue_notify(VirtIODevice *vdev, int n)
+{
+ if (n < VIRTIO_PCI_QUEUE_MAX) {
+ virtio_queue_notify_vq(&vdev->vq[n]);
+ }
+}
+
+uint16_t virtio_queue_vector(VirtIODevice *vdev, int n)
+{
+ return n < VIRTIO_PCI_QUEUE_MAX ? vdev->vq[n].vector :
+ VIRTIO_NO_VECTOR;
+}
+
+void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector)
+{
+ if (n < VIRTIO_PCI_QUEUE_MAX)
+ vdev->vq[n].vector = vector;
+}
+
+VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
+ void (*handle_output)(VirtIODevice *, VirtQueue *))
+{
+ int i;
+
+ for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ if (vdev->vq[i].vring.num == 0)
+ break;
+ }
+
+ if (i == VIRTIO_PCI_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE)
+ abort();
+
+ vdev->vq[i].vring.num = queue_size;
+ vdev->vq[i].handle_output = handle_output;
+
+ return &vdev->vq[i];
+}
+
+void virtio_irq(VirtQueue *vq)
+{
+ trace_virtio_irq(vq);
+ vq->vdev->isr |= 0x01;
+ virtio_notify_vector(vq->vdev, vq->vector);
+}
+
+void virtio_notify(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /* Always notify when queue is empty (when feature acknowledge) */
+ if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) &&
+ (!(vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) ||
+ (vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx)))
+ return;
+
+ trace_virtio_notify(vdev, vq);
+ vdev->isr |= 0x01;
+ virtio_notify_vector(vdev, vq->vector);
+}
+
+void virtio_notify_config(VirtIODevice *vdev)
+{
+ if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))
+ return;
+
+ vdev->isr |= 0x03;
+ virtio_notify_vector(vdev, vdev->config_vector);
+}
+
+void virtio_save(VirtIODevice *vdev, QEMUFile *f)
+{
+ int i;
+
+ if (vdev->binding->save_config)
+ vdev->binding->save_config(vdev->binding_opaque, f);
+
+ qemu_put_8s(f, &vdev->status);
+ qemu_put_8s(f, &vdev->isr);
+ qemu_put_be16s(f, &vdev->queue_sel);
+ qemu_put_be32s(f, &vdev->guest_features);
+ qemu_put_be32(f, vdev->config_len);
+ qemu_put_buffer(f, vdev->config, vdev->config_len);
+
+ for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ if (vdev->vq[i].vring.num == 0)
+ break;
+ }
+
+ qemu_put_be32(f, i);
+
+ for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ if (vdev->vq[i].vring.num == 0)
+ break;
+
+ qemu_put_be32(f, vdev->vq[i].vring.num);
+ qemu_put_be64(f, vdev->vq[i].pa);
+ qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
+ if (vdev->binding->save_queue)
+ vdev->binding->save_queue(vdev->binding_opaque, i, f);
+ }
+}
+
+int virtio_load(VirtIODevice *vdev, QEMUFile *f)
+{
+ int num, i, ret;
+ uint32_t features;
+ uint32_t supported_features =
+ vdev->binding->get_features(vdev->binding_opaque);
+
+ if (vdev->binding->load_config) {
+ ret = vdev->binding->load_config(vdev->binding_opaque, f);
+ if (ret)
+ return ret;
+ }
+
+ qemu_get_8s(f, &vdev->status);
+ qemu_get_8s(f, &vdev->isr);
+ qemu_get_be16s(f, &vdev->queue_sel);
+ qemu_get_be32s(f, &features);
+ if (features & ~supported_features) {
+ error_report("Features 0x%x unsupported. Allowed features: 0x%x",
+ features, supported_features);
+ return -1;
+ }
+ if (vdev->set_features)
+ vdev->set_features(vdev, features);
+ vdev->guest_features = features;
+ vdev->config_len = qemu_get_be32(f);
+ qemu_get_buffer(f, vdev->config, vdev->config_len);
+
+ num = qemu_get_be32(f);
+
+ for (i = 0; i < num; i++) {
+ vdev->vq[i].vring.num = qemu_get_be32(f);
+ vdev->vq[i].pa = qemu_get_be64(f);
+ qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
+
+ if (vdev->vq[i].pa) {
+ uint16_t nheads;
+ virtqueue_init(&vdev->vq[i]);
+ nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
+ /* Check it isn't doing very strange things with descriptor numbers. */
+ if (nheads > vdev->vq[i].vring.num) {
+ error_report("VQ %d size 0x%x Guest index 0x%x "
+ "inconsistent with Host index 0x%x: delta 0x%x\n",
+ i, vdev->vq[i].vring.num,
+ vring_avail_idx(&vdev->vq[i]),
+ vdev->vq[i].last_avail_idx, nheads);
+ return -1;
+ }
+ } else if (vdev->vq[i].last_avail_idx) {
+ error_report("VQ %d address 0x0 "
+ "inconsistent with Host index 0x%x\n",
+ i, vdev->vq[i].last_avail_idx);
+ return -1;
+ }
+ if (vdev->binding->load_queue) {
+ ret = vdev->binding->load_queue(vdev->binding_opaque, i, f);
+ if (ret)
+ return ret;
+ }
+ }
+
+ virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
+ return 0;
+}
+
+void virtio_cleanup(VirtIODevice *vdev)
+{
+ qemu_del_vm_change_state_handler(vdev->vmstate);
+ if (vdev->config)
+ qemu_free(vdev->config);
+ qemu_free(vdev->vq);
+}
+
+static void virtio_vmstate_change(void *opaque, int running, int reason)
+{
+ VirtIODevice *vdev = opaque;
+ bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK);
+ vdev->vm_running = running;
+
+ if (backend_run) {
+ virtio_set_status(vdev, vdev->status);
+ }
+
+ if (vdev->binding->vmstate_change) {
+ vdev->binding->vmstate_change(vdev->binding_opaque, backend_run);
+ }
+
+ if (!backend_run) {
+ virtio_set_status(vdev, vdev->status);
+ }
+}
+
+VirtIODevice *virtio_common_init(const char *name, uint16_t device_id,
+ size_t config_size, size_t struct_size)
+{
+ VirtIODevice *vdev;
+ int i;
+
+ vdev = qemu_mallocz(struct_size);
+
+ vdev->device_id = device_id;
+ vdev->status = 0;
+ vdev->isr = 0;
+ vdev->queue_sel = 0;
+ vdev->config_vector = VIRTIO_NO_VECTOR;
+ vdev->vq = qemu_mallocz(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX);
+ for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
+ vdev->vq[i].vector = VIRTIO_NO_VECTOR;
+ vdev->vq[i].vdev = vdev;
+ }
+
+ vdev->name = name;
+ vdev->config_len = config_size;
+ if (vdev->config_len)
+ vdev->config = qemu_mallocz(config_size);
+ else
+ vdev->config = NULL;
+
+ vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev);
+
+ return vdev;
+}
+
+void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,
+ void *opaque)
+{
+ vdev->binding = binding;
+ vdev->binding_opaque = opaque;
+}
+
+target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.desc;
+}
+
+target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.avail;
+}
+
+target_phys_addr_t virtio_queue_get_used_addr(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.used;
+}
+
+target_phys_addr_t virtio_queue_get_ring_addr(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.desc;
+}
+
+target_phys_addr_t virtio_queue_get_desc_size(VirtIODevice *vdev, int n)
+{
+ return sizeof(VRingDesc) * vdev->vq[n].vring.num;
+}
+
+target_phys_addr_t virtio_queue_get_avail_size(VirtIODevice *vdev, int n)
+{
+ return offsetof(VRingAvail, ring) +
+ sizeof(uint64_t) * vdev->vq[n].vring.num;
+}
+
+target_phys_addr_t virtio_queue_get_used_size(VirtIODevice *vdev, int n)
+{
+ return offsetof(VRingUsed, ring) +
+ sizeof(VRingUsedElem) * vdev->vq[n].vring.num;
+}
+
+target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.used - vdev->vq[n].vring.desc +
+ virtio_queue_get_used_size(vdev, n);
+}
+
+uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].last_avail_idx;
+}
+
+void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx)
+{
+ vdev->vq[n].last_avail_idx = idx;
+}
+
+VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n)
+{
+ return vdev->vq + n;
+}
+
+EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq)
+{
+ return &vq->guest_notifier;
+}
+EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq)
+{
+ return &vq->host_notifier;
+}
diff --git a/hw/virtio.h b/hw/virtio.h
new file mode 100644
index 0000000..d1fb8a0
--- /dev/null
+++ b/hw/virtio.h
@@ -0,0 +1,229 @@
+/*
+ * Virtio Support
+ *
+ * Copyright IBM, Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VIRTIO_H
+#define _QEMU_VIRTIO_H
+
+#include "hw.h"
+#include "net.h"
+#include "qdev.h"
+#include "sysemu.h"
+#include "block_int.h"
+#include "event_notifier.h"
+#ifdef CONFIG_LINUX
+#include "9p.h"
+#endif
+
+/* from Linux's linux/virtio_config.h */
+
+/* Status byte for guest to report progress, and synchronize features. */
+/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
+/* We have found a driver for the device. */
+#define VIRTIO_CONFIG_S_DRIVER 2
+/* Driver has used its parts of the config, and is happy */
+#define VIRTIO_CONFIG_S_DRIVER_OK 4
+/* We've given up on this device. */
+#define VIRTIO_CONFIG_S_FAILED 0x80
+
+/* Some virtio feature bits (currently bits 28 through 31) are reserved for the
+ * transport being used (eg. virtio_ring), the rest are per-device feature bits. */
+#define VIRTIO_TRANSPORT_F_START 28
+#define VIRTIO_TRANSPORT_F_END 32
+
+/* We notify when the ring is completely used, even if the guest is suppressing
+ * callbacks */
+#define VIRTIO_F_NOTIFY_ON_EMPTY 24
+/* We support indirect buffer descriptors */
+#define VIRTIO_RING_F_INDIRECT_DESC 28
+/* A guest should never accept this. It implies negotiation is broken. */
+#define VIRTIO_F_BAD_FEATURE 30
+
+/* from Linux's linux/virtio_ring.h */
+
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT 1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VRING_DESC_F_WRITE 2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT 4
+
+/* This means don't notify other side when buffer added. */
+#define VRING_USED_F_NO_NOTIFY 1
+/* This means don't interrupt guest when buffer consumed. */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+struct VirtQueue;
+
+static inline target_phys_addr_t vring_align(target_phys_addr_t addr,
+ unsigned long align)
+{
+ return (addr + align - 1) & ~(align - 1);
+}
+
+typedef struct VirtQueue VirtQueue;
+
+#define VIRTQUEUE_MAX_SIZE 1024
+
+typedef struct VirtQueueElement
+{
+ unsigned int index;
+ unsigned int out_num;
+ unsigned int in_num;
+ target_phys_addr_t in_addr[VIRTQUEUE_MAX_SIZE];
+ target_phys_addr_t out_addr[VIRTQUEUE_MAX_SIZE];
+ struct iovec in_sg[VIRTQUEUE_MAX_SIZE];
+ struct iovec out_sg[VIRTQUEUE_MAX_SIZE];
+} VirtQueueElement;
+
+typedef struct {
+ void (*notify)(void * opaque, uint16_t vector);
+ void (*save_config)(void * opaque, QEMUFile *f);
+ void (*save_queue)(void * opaque, int n, QEMUFile *f);
+ int (*load_config)(void * opaque, QEMUFile *f);
+ int (*load_queue)(void * opaque, int n, QEMUFile *f);
+ int (*load_done)(void * opaque, QEMUFile *f);
+ unsigned (*get_features)(void * opaque);
+ bool (*query_guest_notifiers)(void * opaque);
+ int (*set_guest_notifiers)(void * opaque, bool assigned);
+ int (*set_host_notifier)(void * opaque, int n, bool assigned);
+ void (*vmstate_change)(void * opaque, bool running);
+} VirtIOBindings;
+
+#define VIRTIO_PCI_QUEUE_MAX 64
+
+#define VIRTIO_NO_VECTOR 0xffff
+
+struct VirtIODevice
+{
+ const char *name;
+ uint8_t status;
+ uint8_t isr;
+ uint16_t queue_sel;
+ uint32_t guest_features;
+ size_t config_len;
+ void *config;
+ uint16_t config_vector;
+ int nvectors;
+ uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
+ uint32_t (*bad_features)(VirtIODevice *vdev);
+ void (*set_features)(VirtIODevice *vdev, uint32_t val);
+ void (*get_config)(VirtIODevice *vdev, uint8_t *config);
+ void (*set_config)(VirtIODevice *vdev, const uint8_t *config);
+ void (*reset)(VirtIODevice *vdev);
+ void (*set_status)(VirtIODevice *vdev, uint8_t val);
+ VirtQueue *vq;
+ const VirtIOBindings *binding;
+ void *binding_opaque;
+ uint16_t device_id;
+ bool vm_running;
+ VMChangeStateEntry *vmstate;
+};
+
+static inline void virtio_set_status(VirtIODevice *vdev, uint8_t val)
+{
+ if (vdev->set_status) {
+ vdev->set_status(vdev, val);
+ }
+ vdev->status = val;
+}
+
+VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
+ void (*handle_output)(VirtIODevice *,
+ VirtQueue *));
+
+void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len);
+void virtqueue_flush(VirtQueue *vq, unsigned int count);
+void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem,
+ unsigned int len, unsigned int idx);
+
+void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr,
+ size_t num_sg, int is_write);
+int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem);
+int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes);
+
+void virtio_notify(VirtIODevice *vdev, VirtQueue *vq);
+
+void virtio_save(VirtIODevice *vdev, QEMUFile *f);
+
+int virtio_load(VirtIODevice *vdev, QEMUFile *f);
+
+void virtio_cleanup(VirtIODevice *vdev);
+
+void virtio_notify_config(VirtIODevice *vdev);
+
+void virtio_queue_set_notification(VirtQueue *vq, int enable);
+
+int virtio_queue_ready(VirtQueue *vq);
+
+int virtio_queue_empty(VirtQueue *vq);
+
+/* Host binding interface. */
+
+VirtIODevice *virtio_common_init(const char *name, uint16_t device_id,
+ size_t config_size, size_t struct_size);
+uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr);
+uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr);
+uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr);
+void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data);
+void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data);
+void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data);
+void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr);
+target_phys_addr_t virtio_queue_get_addr(VirtIODevice *vdev, int n);
+int virtio_queue_get_num(VirtIODevice *vdev, int n);
+void virtio_queue_notify(VirtIODevice *vdev, int n);
+uint16_t virtio_queue_vector(VirtIODevice *vdev, int n);
+void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector);
+void virtio_reset(void *opaque);
+void virtio_update_irq(VirtIODevice *vdev);
+
+void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,
+ void *opaque);
+
+/* Base devices. */
+VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf);
+struct virtio_net_conf;
+VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
+ struct virtio_net_conf *net);
+VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports);
+VirtIODevice *virtio_balloon_init(DeviceState *dev);
+#ifdef CONFIG_LINUX
+VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
+#endif
+
+
+void virtio_net_exit(VirtIODevice *vdev);
+void virtio_blk_exit(VirtIODevice *vdev);
+void virtio_serial_exit(VirtIODevice *vdev);
+
+#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
+ DEFINE_PROP_BIT("indirect_desc", _state, _field, \
+ VIRTIO_RING_F_INDIRECT_DESC, true)
+
+target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_used_addr(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_ring_addr(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_desc_size(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_avail_size(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_used_size(VirtIODevice *vdev, int n);
+target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n);
+uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n);
+void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
+VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
+EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
+EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
+void virtio_queue_notify_vq(VirtQueue *vq);
+void virtio_irq(VirtQueue *vq);
+#endif
diff --git a/hw/vmmouse.c b/hw/vmmouse.c
new file mode 100644
index 0000000..2097119
--- /dev/null
+++ b/hw/vmmouse.c
@@ -0,0 +1,291 @@
+/*
+ * QEMU VMMouse emulation
+ *
+ * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "ps2.h"
+#include "pc.h"
+
+/* debug only vmmouse */
+//#define DEBUG_VMMOUSE
+
+/* VMMouse Commands */
+#define VMMOUSE_GETVERSION 10
+#define VMMOUSE_DATA 39
+#define VMMOUSE_STATUS 40
+#define VMMOUSE_COMMAND 41
+
+#define VMMOUSE_READ_ID 0x45414552
+#define VMMOUSE_DISABLE 0x000000f5
+#define VMMOUSE_REQUEST_RELATIVE 0x4c455252
+#define VMMOUSE_REQUEST_ABSOLUTE 0x53424152
+
+#define VMMOUSE_QUEUE_SIZE 1024
+
+#define VMMOUSE_VERSION 0x3442554a
+
+#ifdef DEBUG_VMMOUSE
+#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+typedef struct _VMMouseState
+{
+ uint32_t queue[VMMOUSE_QUEUE_SIZE];
+ int32_t queue_size;
+ uint16_t nb_queue;
+ uint16_t status;
+ uint8_t absolute;
+ QEMUPutMouseEntry *entry;
+ void *ps2_mouse;
+} VMMouseState;
+
+static uint32_t vmmouse_get_status(VMMouseState *s)
+{
+ DPRINTF("vmmouse_get_status()\n");
+ return (s->status << 16) | s->nb_queue;
+}
+
+static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state)
+{
+ VMMouseState *s = opaque;
+ int buttons = 0;
+
+ if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4))
+ return;
+
+ DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n",
+ x, y, dz, buttons_state);
+
+ if ((buttons_state & MOUSE_EVENT_LBUTTON))
+ buttons |= 0x20;
+ if ((buttons_state & MOUSE_EVENT_RBUTTON))
+ buttons |= 0x10;
+ if ((buttons_state & MOUSE_EVENT_MBUTTON))
+ buttons |= 0x08;
+
+ if (s->absolute) {
+ x <<= 1;
+ y <<= 1;
+ }
+
+ s->queue[s->nb_queue++] = buttons;
+ s->queue[s->nb_queue++] = x;
+ s->queue[s->nb_queue++] = y;
+ s->queue[s->nb_queue++] = dz;
+
+ /* need to still generate PS2 events to notify driver to
+ read from queue */
+ i8042_isa_mouse_fake_event(s->ps2_mouse);
+}
+
+static void vmmouse_remove_handler(VMMouseState *s)
+{
+ if (s->entry) {
+ qemu_remove_mouse_event_handler(s->entry);
+ s->entry = NULL;
+ }
+}
+
+static void vmmouse_update_handler(VMMouseState *s, int absolute)
+{
+ if (s->status != 0) {
+ return;
+ }
+ if (s->absolute != absolute) {
+ s->absolute = absolute;
+ vmmouse_remove_handler(s);
+ }
+ if (s->entry == NULL) {
+ s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event,
+ s, s->absolute,
+ "vmmouse");
+ qemu_activate_mouse_event_handler(s->entry);
+ }
+}
+
+static void vmmouse_read_id(VMMouseState *s)
+{
+ DPRINTF("vmmouse_read_id()\n");
+
+ if (s->nb_queue == VMMOUSE_QUEUE_SIZE)
+ return;
+
+ s->queue[s->nb_queue++] = VMMOUSE_VERSION;
+ s->status = 0;
+}
+
+static void vmmouse_request_relative(VMMouseState *s)
+{
+ DPRINTF("vmmouse_request_relative()\n");
+ vmmouse_update_handler(s, 0);
+}
+
+static void vmmouse_request_absolute(VMMouseState *s)
+{
+ DPRINTF("vmmouse_request_absolute()\n");
+ vmmouse_update_handler(s, 1);
+}
+
+static void vmmouse_disable(VMMouseState *s)
+{
+ DPRINTF("vmmouse_disable()\n");
+ s->status = 0xffff;
+ vmmouse_remove_handler(s);
+}
+
+static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size)
+{
+ int i;
+
+ DPRINTF("vmmouse_data(%d)\n", size);
+
+ if (size == 0 || size > 6 || size > s->nb_queue) {
+ printf("vmmouse: driver requested too much data %d\n", size);
+ s->status = 0xffff;
+ vmmouse_remove_handler(s);
+ return;
+ }
+
+ for (i = 0; i < size; i++)
+ data[i] = s->queue[i];
+
+ s->nb_queue -= size;
+ if (s->nb_queue)
+ memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue);
+}
+
+static void vmmouse_get_data(uint32_t *data)
+{
+ CPUState *env = cpu_single_env;
+
+ data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX];
+ data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX];
+ data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI];
+
+ DPRINTF("get_data = {%x, %x, %x, %x, %x, %x}\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+}
+
+static void vmmouse_set_data(const uint32_t *data)
+{
+ CPUState *env = cpu_single_env;
+
+ DPRINTF("set_data = {%x, %x, %x, %x, %x, %x}\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+
+ env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1];
+ env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3];
+ env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5];
+}
+
+static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr)
+{
+ VMMouseState *s = opaque;
+ uint32_t data[6];
+ uint16_t command;
+
+ vmmouse_get_data(data);
+
+ command = data[2] & 0xFFFF;
+
+ switch (command) {
+ case VMMOUSE_STATUS:
+ data[0] = vmmouse_get_status(s);
+ break;
+ case VMMOUSE_COMMAND:
+ switch (data[1]) {
+ case VMMOUSE_DISABLE:
+ vmmouse_disable(s);
+ break;
+ case VMMOUSE_READ_ID:
+ vmmouse_read_id(s);
+ break;
+ case VMMOUSE_REQUEST_RELATIVE:
+ vmmouse_request_relative(s);
+ break;
+ case VMMOUSE_REQUEST_ABSOLUTE:
+ vmmouse_request_absolute(s);
+ break;
+ default:
+ printf("vmmouse: unknown command %x\n", data[1]);
+ break;
+ }
+ break;
+ case VMMOUSE_DATA:
+ vmmouse_data(s, data, data[1]);
+ break;
+ default:
+ printf("vmmouse: unknown command %x\n", command);
+ break;
+ }
+
+ vmmouse_set_data(data);
+ return data[0];
+}
+
+static int vmmouse_post_load(void *opaque, int version_id)
+{
+ VMMouseState *s = opaque;
+
+ vmmouse_remove_handler(s);
+ vmmouse_update_handler(s, s->absolute);
+ return 0;
+}
+
+static const VMStateDescription vmstate_vmmouse = {
+ .name = "vmmouse",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = vmmouse_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_EQUAL(queue_size, VMMouseState),
+ VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE),
+ VMSTATE_UINT16(nb_queue, VMMouseState),
+ VMSTATE_UINT16(status, VMMouseState),
+ VMSTATE_UINT8(absolute, VMMouseState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void *vmmouse_init(void *m)
+{
+ VMMouseState *s = NULL;
+
+ DPRINTF("vmmouse_init\n");
+
+ s = qemu_mallocz(sizeof(VMMouseState));
+
+ s->status = 0xffff;
+ s->ps2_mouse = m;
+ s->queue_size = VMMOUSE_QUEUE_SIZE;
+
+ vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s);
+ vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s);
+ vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s);
+ vmstate_register(NULL, 0, &vmstate_vmmouse, s);
+
+ return s;
+}
diff --git a/hw/vmport.c b/hw/vmport.c
new file mode 100644
index 0000000..6c9d7c9
--- /dev/null
+++ b/hw/vmport.c
@@ -0,0 +1,111 @@
+/*
+ * QEMU VMPort emulation
+ *
+ * Copyright (C) 2007 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+#include "sysemu.h"
+#include "kvm.h"
+
+//#define VMPORT_DEBUG
+
+#define VMPORT_CMD_GETVERSION 0x0a
+#define VMPORT_CMD_GETRAMSIZE 0x14
+
+#define VMPORT_ENTRIES 0x2c
+#define VMPORT_MAGIC 0x564D5868
+
+typedef struct _VMPortState
+{
+ IOPortReadFunc *func[VMPORT_ENTRIES];
+ void *opaque[VMPORT_ENTRIES];
+} VMPortState;
+
+static VMPortState port_state;
+
+void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque)
+{
+ if (command >= VMPORT_ENTRIES)
+ return;
+
+ port_state.func[command] = func;
+ port_state.opaque[command] = opaque;
+}
+
+static uint32_t vmport_ioport_read(void *opaque, uint32_t addr)
+{
+ VMPortState *s = opaque;
+ CPUState *env = cpu_single_env;
+ unsigned char command;
+ uint32_t eax;
+
+ cpu_synchronize_state(env);
+
+ eax = env->regs[R_EAX];
+ if (eax != VMPORT_MAGIC)
+ return eax;
+
+ command = env->regs[R_ECX];
+ if (command >= VMPORT_ENTRIES)
+ return eax;
+ if (!s->func[command])
+ {
+#ifdef VMPORT_DEBUG
+ fprintf(stderr, "vmport: unknown command %x\n", command);
+#endif
+ return eax;
+ }
+
+ return s->func[command](s->opaque[command], addr);
+}
+
+static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ CPUState *env = cpu_single_env;
+
+ env->regs[R_EAX] = vmport_ioport_read(opaque, addr);
+}
+
+static uint32_t vmport_cmd_get_version(void *opaque, uint32_t addr)
+{
+ CPUState *env = cpu_single_env;
+ env->regs[R_EBX] = VMPORT_MAGIC;
+ return 6;
+}
+
+static uint32_t vmport_cmd_ram_size(void *opaque, uint32_t addr)
+{
+ CPUState *env = cpu_single_env;
+ env->regs[R_EBX] = 0x1177;
+ return ram_size;
+}
+
+void vmport_init(void)
+{
+ register_ioport_read(0x5658, 1, 4, vmport_ioport_read, &port_state);
+ register_ioport_write(0x5658, 1, 4, vmport_ioport_write, &port_state);
+
+ /* Register some generic port commands */
+ vmport_register(VMPORT_CMD_GETVERSION, vmport_cmd_get_version, NULL);
+ vmport_register(VMPORT_CMD_GETRAMSIZE, vmport_cmd_ram_size, NULL);
+}
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
new file mode 100644
index 0000000..6c59053
--- /dev/null
+++ b/hw/vmware_vga.c
@@ -0,0 +1,1330 @@
+/*
+ * QEMU VMware-SVGA "chipset".
+ *
+ * Copyright (c) 2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "loader.h"
+#include "console.h"
+#include "pci.h"
+#include "vmware_vga.h"
+
+#define VERBOSE
+#undef DIRECT_VRAM
+#define HW_RECT_ACCEL
+#define HW_FILL_ACCEL
+#define HW_MOUSE_ACCEL
+
+# include "vga_int.h"
+
+struct vmsvga_state_s {
+ VGACommonState vga;
+
+ int width;
+ int height;
+ int invalidated;
+ int depth;
+ int bypp;
+ int enable;
+ int config;
+ struct {
+ int id;
+ int x;
+ int y;
+ int on;
+ } cursor;
+
+ target_phys_addr_t vram_base;
+
+ int index;
+ int scratch_size;
+ uint32_t *scratch;
+ int new_width;
+ int new_height;
+ uint32_t guest;
+ uint32_t svgaid;
+ uint32_t wred;
+ uint32_t wgreen;
+ uint32_t wblue;
+ int syncing;
+ int fb_size;
+
+ ram_addr_t fifo_offset;
+ uint8_t *fifo_ptr;
+ unsigned int fifo_size;
+ target_phys_addr_t fifo_base;
+
+ union {
+ uint32_t *fifo;
+ struct __attribute__((__packed__)) {
+ uint32_t min;
+ uint32_t max;
+ uint32_t next_cmd;
+ uint32_t stop;
+ /* Add registers here when adding capabilities. */
+ uint32_t fifo[0];
+ } *cmd;
+ };
+
+#define REDRAW_FIFO_LEN 512
+ struct vmsvga_rect_s {
+ int x, y, w, h;
+ } redraw_fifo[REDRAW_FIFO_LEN];
+ int redraw_fifo_first, redraw_fifo_last;
+};
+
+struct pci_vmsvga_state_s {
+ PCIDevice card;
+ struct vmsvga_state_s chip;
+};
+
+#define SVGA_MAGIC 0x900000UL
+#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver))
+#define SVGA_ID_0 SVGA_MAKE_ID(0)
+#define SVGA_ID_1 SVGA_MAKE_ID(1)
+#define SVGA_ID_2 SVGA_MAKE_ID(2)
+
+#define SVGA_LEGACY_BASE_PORT 0x4560
+#define SVGA_INDEX_PORT 0x0
+#define SVGA_VALUE_PORT 0x1
+#define SVGA_BIOS_PORT 0x2
+
+#define SVGA_VERSION_2
+
+#ifdef SVGA_VERSION_2
+# define SVGA_ID SVGA_ID_2
+# define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT
+# define SVGA_IO_MUL 1
+# define SVGA_FIFO_SIZE 0x10000
+# define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA2
+#else
+# define SVGA_ID SVGA_ID_1
+# define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT
+# define SVGA_IO_MUL 4
+# define SVGA_FIFO_SIZE 0x10000
+# define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA
+#endif
+
+enum {
+ /* ID 0, 1 and 2 registers */
+ SVGA_REG_ID = 0,
+ SVGA_REG_ENABLE = 1,
+ SVGA_REG_WIDTH = 2,
+ SVGA_REG_HEIGHT = 3,
+ SVGA_REG_MAX_WIDTH = 4,
+ SVGA_REG_MAX_HEIGHT = 5,
+ SVGA_REG_DEPTH = 6,
+ SVGA_REG_BITS_PER_PIXEL = 7, /* Current bpp in the guest */
+ SVGA_REG_PSEUDOCOLOR = 8,
+ SVGA_REG_RED_MASK = 9,
+ SVGA_REG_GREEN_MASK = 10,
+ SVGA_REG_BLUE_MASK = 11,
+ SVGA_REG_BYTES_PER_LINE = 12,
+ SVGA_REG_FB_START = 13,
+ SVGA_REG_FB_OFFSET = 14,
+ SVGA_REG_VRAM_SIZE = 15,
+ SVGA_REG_FB_SIZE = 16,
+
+ /* ID 1 and 2 registers */
+ SVGA_REG_CAPABILITIES = 17,
+ SVGA_REG_MEM_START = 18, /* Memory for command FIFO */
+ SVGA_REG_MEM_SIZE = 19,
+ SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */
+ SVGA_REG_SYNC = 21, /* Write to force synchronization */
+ SVGA_REG_BUSY = 22, /* Read to check if sync is done */
+ SVGA_REG_GUEST_ID = 23, /* Set guest OS identifier */
+ SVGA_REG_CURSOR_ID = 24, /* ID of cursor */
+ SVGA_REG_CURSOR_X = 25, /* Set cursor X position */
+ SVGA_REG_CURSOR_Y = 26, /* Set cursor Y position */
+ SVGA_REG_CURSOR_ON = 27, /* Turn cursor on/off */
+ SVGA_REG_HOST_BITS_PER_PIXEL = 28, /* Current bpp in the host */
+ SVGA_REG_SCRATCH_SIZE = 29, /* Number of scratch registers */
+ SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */
+ SVGA_REG_NUM_DISPLAYS = 31, /* Number of guest displays */
+ SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */
+
+ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */
+ SVGA_PALETTE_END = SVGA_PALETTE_BASE + 767,
+ SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + 768,
+};
+
+#define SVGA_CAP_NONE 0
+#define SVGA_CAP_RECT_FILL (1 << 0)
+#define SVGA_CAP_RECT_COPY (1 << 1)
+#define SVGA_CAP_RECT_PAT_FILL (1 << 2)
+#define SVGA_CAP_LEGACY_OFFSCREEN (1 << 3)
+#define SVGA_CAP_RASTER_OP (1 << 4)
+#define SVGA_CAP_CURSOR (1 << 5)
+#define SVGA_CAP_CURSOR_BYPASS (1 << 6)
+#define SVGA_CAP_CURSOR_BYPASS_2 (1 << 7)
+#define SVGA_CAP_8BIT_EMULATION (1 << 8)
+#define SVGA_CAP_ALPHA_CURSOR (1 << 9)
+#define SVGA_CAP_GLYPH (1 << 10)
+#define SVGA_CAP_GLYPH_CLIPPING (1 << 11)
+#define SVGA_CAP_OFFSCREEN_1 (1 << 12)
+#define SVGA_CAP_ALPHA_BLEND (1 << 13)
+#define SVGA_CAP_3D (1 << 14)
+#define SVGA_CAP_EXTENDED_FIFO (1 << 15)
+#define SVGA_CAP_MULTIMON (1 << 16)
+#define SVGA_CAP_PITCHLOCK (1 << 17)
+
+/*
+ * FIFO offsets (seen as an array of 32-bit words)
+ */
+enum {
+ /*
+ * The original defined FIFO offsets
+ */
+ SVGA_FIFO_MIN = 0,
+ SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */
+ SVGA_FIFO_NEXT_CMD,
+ SVGA_FIFO_STOP,
+
+ /*
+ * Additional offsets added as of SVGA_CAP_EXTENDED_FIFO
+ */
+ SVGA_FIFO_CAPABILITIES = 4,
+ SVGA_FIFO_FLAGS,
+ SVGA_FIFO_FENCE,
+ SVGA_FIFO_3D_HWVERSION,
+ SVGA_FIFO_PITCHLOCK,
+};
+
+#define SVGA_FIFO_CAP_NONE 0
+#define SVGA_FIFO_CAP_FENCE (1 << 0)
+#define SVGA_FIFO_CAP_ACCELFRONT (1 << 1)
+#define SVGA_FIFO_CAP_PITCHLOCK (1 << 2)
+
+#define SVGA_FIFO_FLAG_NONE 0
+#define SVGA_FIFO_FLAG_ACCELFRONT (1 << 0)
+
+/* These values can probably be changed arbitrarily. */
+#define SVGA_SCRATCH_SIZE 0x8000
+#define SVGA_MAX_WIDTH 2360
+#define SVGA_MAX_HEIGHT 1770
+
+#ifdef VERBOSE
+# define GUEST_OS_BASE 0x5001
+static const char *vmsvga_guest_id[] = {
+ [0x00] = "Dos",
+ [0x01] = "Windows 3.1",
+ [0x02] = "Windows 95",
+ [0x03] = "Windows 98",
+ [0x04] = "Windows ME",
+ [0x05] = "Windows NT",
+ [0x06] = "Windows 2000",
+ [0x07] = "Linux",
+ [0x08] = "OS/2",
+ [0x09] = "an unknown OS",
+ [0x0a] = "BSD",
+ [0x0b] = "Whistler",
+ [0x0c] = "an unknown OS",
+ [0x0d] = "an unknown OS",
+ [0x0e] = "an unknown OS",
+ [0x0f] = "an unknown OS",
+ [0x10] = "an unknown OS",
+ [0x11] = "an unknown OS",
+ [0x12] = "an unknown OS",
+ [0x13] = "an unknown OS",
+ [0x14] = "an unknown OS",
+ [0x15] = "Windows 2003",
+};
+#endif
+
+enum {
+ SVGA_CMD_INVALID_CMD = 0,
+ SVGA_CMD_UPDATE = 1,
+ SVGA_CMD_RECT_FILL = 2,
+ SVGA_CMD_RECT_COPY = 3,
+ SVGA_CMD_DEFINE_BITMAP = 4,
+ SVGA_CMD_DEFINE_BITMAP_SCANLINE = 5,
+ SVGA_CMD_DEFINE_PIXMAP = 6,
+ SVGA_CMD_DEFINE_PIXMAP_SCANLINE = 7,
+ SVGA_CMD_RECT_BITMAP_FILL = 8,
+ SVGA_CMD_RECT_PIXMAP_FILL = 9,
+ SVGA_CMD_RECT_BITMAP_COPY = 10,
+ SVGA_CMD_RECT_PIXMAP_COPY = 11,
+ SVGA_CMD_FREE_OBJECT = 12,
+ SVGA_CMD_RECT_ROP_FILL = 13,
+ SVGA_CMD_RECT_ROP_COPY = 14,
+ SVGA_CMD_RECT_ROP_BITMAP_FILL = 15,
+ SVGA_CMD_RECT_ROP_PIXMAP_FILL = 16,
+ SVGA_CMD_RECT_ROP_BITMAP_COPY = 17,
+ SVGA_CMD_RECT_ROP_PIXMAP_COPY = 18,
+ SVGA_CMD_DEFINE_CURSOR = 19,
+ SVGA_CMD_DISPLAY_CURSOR = 20,
+ SVGA_CMD_MOVE_CURSOR = 21,
+ SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
+ SVGA_CMD_DRAW_GLYPH = 23,
+ SVGA_CMD_DRAW_GLYPH_CLIPPED = 24,
+ SVGA_CMD_UPDATE_VERBOSE = 25,
+ SVGA_CMD_SURFACE_FILL = 26,
+ SVGA_CMD_SURFACE_COPY = 27,
+ SVGA_CMD_SURFACE_ALPHA_BLEND = 28,
+ SVGA_CMD_FRONT_ROP_FILL = 29,
+ SVGA_CMD_FENCE = 30,
+};
+
+/* Legal values for the SVGA_REG_CURSOR_ON register in cursor bypass mode */
+enum {
+ SVGA_CURSOR_ON_HIDE = 0,
+ SVGA_CURSOR_ON_SHOW = 1,
+ SVGA_CURSOR_ON_REMOVE_FROM_FB = 2,
+ SVGA_CURSOR_ON_RESTORE_TO_FB = 3,
+};
+
+static inline void vmsvga_update_rect(struct vmsvga_state_s *s,
+ int x, int y, int w, int h)
+{
+#ifndef DIRECT_VRAM
+ int line;
+ int bypl;
+ int width;
+ int start;
+ uint8_t *src;
+ uint8_t *dst;
+
+ if (x + w > s->width) {
+ fprintf(stderr, "%s: update width too large x: %d, w: %d\n",
+ __FUNCTION__, x, w);
+ x = MIN(x, s->width);
+ w = s->width - x;
+ }
+
+ if (y + h > s->height) {
+ fprintf(stderr, "%s: update height too large y: %d, h: %d\n",
+ __FUNCTION__, y, h);
+ y = MIN(y, s->height);
+ h = s->height - y;
+ }
+
+ line = h;
+ bypl = s->bypp * s->width;
+ width = s->bypp * w;
+ start = s->bypp * x + bypl * y;
+ src = s->vga.vram_ptr + start;
+ dst = ds_get_data(s->vga.ds) + start;
+
+ for (; line > 0; line --, src += bypl, dst += bypl)
+ memcpy(dst, src, width);
+#endif
+
+ dpy_update(s->vga.ds, x, y, w, h);
+}
+
+static inline void vmsvga_update_screen(struct vmsvga_state_s *s)
+{
+#ifndef DIRECT_VRAM
+ memcpy(ds_get_data(s->vga.ds), s->vga.vram_ptr, s->bypp * s->width * s->height);
+#endif
+
+ dpy_update(s->vga.ds, 0, 0, s->width, s->height);
+}
+
+#ifdef DIRECT_VRAM
+# define vmsvga_update_rect_delayed vmsvga_update_rect
+#else
+static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s,
+ int x, int y, int w, int h)
+{
+ struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last ++];
+ s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1;
+ rect->x = x;
+ rect->y = y;
+ rect->w = w;
+ rect->h = h;
+}
+#endif
+
+static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
+{
+ struct vmsvga_rect_s *rect;
+ if (s->invalidated) {
+ s->redraw_fifo_first = s->redraw_fifo_last;
+ return;
+ }
+ /* Overlapping region updates can be optimised out here - if someone
+ * knows a smart algorithm to do that, please share. */
+ while (s->redraw_fifo_first != s->redraw_fifo_last) {
+ rect = &s->redraw_fifo[s->redraw_fifo_first ++];
+ s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1;
+ vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h);
+ }
+}
+
+#ifdef HW_RECT_ACCEL
+static inline void vmsvga_copy_rect(struct vmsvga_state_s *s,
+ int x0, int y0, int x1, int y1, int w, int h)
+{
+# ifdef DIRECT_VRAM
+ uint8_t *vram = ds_get_data(s->ds);
+# else
+ uint8_t *vram = s->vga.vram_ptr;
+# endif
+ int bypl = s->bypp * s->width;
+ int width = s->bypp * w;
+ int line = h;
+ uint8_t *ptr[2];
+
+# ifdef DIRECT_VRAM
+ if (s->ds->dpy_copy)
+ qemu_console_copy(s->ds, x0, y0, x1, y1, w, h);
+ else
+# endif
+ {
+ if (y1 > y0) {
+ ptr[0] = vram + s->bypp * x0 + bypl * (y0 + h - 1);
+ ptr[1] = vram + s->bypp * x1 + bypl * (y1 + h - 1);
+ for (; line > 0; line --, ptr[0] -= bypl, ptr[1] -= bypl)
+ memmove(ptr[1], ptr[0], width);
+ } else {
+ ptr[0] = vram + s->bypp * x0 + bypl * y0;
+ ptr[1] = vram + s->bypp * x1 + bypl * y1;
+ for (; line > 0; line --, ptr[0] += bypl, ptr[1] += bypl)
+ memmove(ptr[1], ptr[0], width);
+ }
+ }
+
+ vmsvga_update_rect_delayed(s, x1, y1, w, h);
+}
+#endif
+
+#ifdef HW_FILL_ACCEL
+static inline void vmsvga_fill_rect(struct vmsvga_state_s *s,
+ uint32_t c, int x, int y, int w, int h)
+{
+# ifdef DIRECT_VRAM
+ uint8_t *vram = ds_get_data(s->ds);
+# else
+ uint8_t *vram = s->vga.vram_ptr;
+# endif
+ int bypp = s->bypp;
+ int bypl = bypp * s->width;
+ int width = bypp * w;
+ int line = h;
+ int column;
+ uint8_t *fst = vram + bypp * x + bypl * y;
+ uint8_t *dst;
+ uint8_t *src;
+ uint8_t col[4];
+
+# ifdef DIRECT_VRAM
+ if (s->ds->dpy_fill)
+ s->ds->dpy_fill(s->ds, x, y, w, h, c);
+ else
+# endif
+ {
+ col[0] = c;
+ col[1] = c >> 8;
+ col[2] = c >> 16;
+ col[3] = c >> 24;
+
+ if (line --) {
+ dst = fst;
+ src = col;
+ for (column = width; column > 0; column --) {
+ *(dst ++) = *(src ++);
+ if (src - col == bypp)
+ src = col;
+ }
+ dst = fst;
+ for (; line > 0; line --) {
+ dst += bypl;
+ memcpy(dst, fst, width);
+ }
+ }
+ }
+
+ vmsvga_update_rect_delayed(s, x, y, w, h);
+}
+#endif
+
+struct vmsvga_cursor_definition_s {
+ int width;
+ int height;
+ int id;
+ int bpp;
+ int hot_x;
+ int hot_y;
+ uint32_t mask[1024];
+ uint32_t image[4096];
+};
+
+#define SVGA_BITMAP_SIZE(w, h) ((((w) + 31) >> 5) * (h))
+#define SVGA_PIXMAP_SIZE(w, h, bpp) (((((w) * (bpp)) + 31) >> 5) * (h))
+
+#ifdef HW_MOUSE_ACCEL
+static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
+ struct vmsvga_cursor_definition_s *c)
+{
+ QEMUCursor *qc;
+ int i, pixels;
+
+ qc = cursor_alloc(c->width, c->height);
+ qc->hot_x = c->hot_x;
+ qc->hot_y = c->hot_y;
+ switch (c->bpp) {
+ case 1:
+ cursor_set_mono(qc, 0xffffff, 0x000000, (void*)c->image,
+ 1, (void*)c->mask);
+#ifdef DEBUG
+ cursor_print_ascii_art(qc, "vmware/mono");
+#endif
+ break;
+ case 32:
+ /* fill alpha channel from mask, set color to zero */
+ cursor_set_mono(qc, 0x000000, 0x000000, (void*)c->mask,
+ 1, (void*)c->mask);
+ /* add in rgb values */
+ pixels = c->width * c->height;
+ for (i = 0; i < pixels; i++) {
+ qc->data[i] |= c->image[i] & 0xffffff;
+ }
+#ifdef DEBUG
+ cursor_print_ascii_art(qc, "vmware/32bit");
+#endif
+ break;
+ default:
+ fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n",
+ __FUNCTION__, c->bpp);
+ cursor_put(qc);
+ qc = cursor_builtin_left_ptr();
+ }
+
+ if (s->vga.ds->cursor_define)
+ s->vga.ds->cursor_define(qc);
+ cursor_put(qc);
+}
+#endif
+
+#define CMD(f) le32_to_cpu(s->cmd->f)
+
+static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
+{
+ int num;
+ if (!s->config || !s->enable)
+ return 0;
+ num = CMD(next_cmd) - CMD(stop);
+ if (num < 0)
+ num += CMD(max) - CMD(min);
+ return num >> 2;
+}
+
+static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s)
+{
+ uint32_t cmd = s->fifo[CMD(stop) >> 2];
+ s->cmd->stop = cpu_to_le32(CMD(stop) + 4);
+ if (CMD(stop) >= CMD(max))
+ s->cmd->stop = s->cmd->min;
+ return cmd;
+}
+
+static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s)
+{
+ return le32_to_cpu(vmsvga_fifo_read_raw(s));
+}
+
+static void vmsvga_fifo_run(struct vmsvga_state_s *s)
+{
+ uint32_t cmd, colour;
+ int args, len;
+ int x, y, dx, dy, width, height;
+ struct vmsvga_cursor_definition_s cursor;
+ uint32_t cmd_start;
+
+ len = vmsvga_fifo_length(s);
+ while (len > 0) {
+ /* May need to go back to the start of the command if incomplete */
+ cmd_start = s->cmd->stop;
+
+ switch (cmd = vmsvga_fifo_read(s)) {
+ case SVGA_CMD_UPDATE:
+ case SVGA_CMD_UPDATE_VERBOSE:
+ len -= 5;
+ if (len < 0)
+ goto rewind;
+
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ width = vmsvga_fifo_read(s);
+ height = vmsvga_fifo_read(s);
+ vmsvga_update_rect_delayed(s, x, y, width, height);
+ break;
+
+ case SVGA_CMD_RECT_FILL:
+ len -= 6;
+ if (len < 0)
+ goto rewind;
+
+ colour = vmsvga_fifo_read(s);
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ width = vmsvga_fifo_read(s);
+ height = vmsvga_fifo_read(s);
+#ifdef HW_FILL_ACCEL
+ vmsvga_fill_rect(s, colour, x, y, width, height);
+ break;
+#else
+ args = 0;
+ goto badcmd;
+#endif
+
+ case SVGA_CMD_RECT_COPY:
+ len -= 7;
+ if (len < 0)
+ goto rewind;
+
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ dx = vmsvga_fifo_read(s);
+ dy = vmsvga_fifo_read(s);
+ width = vmsvga_fifo_read(s);
+ height = vmsvga_fifo_read(s);
+#ifdef HW_RECT_ACCEL
+ vmsvga_copy_rect(s, x, y, dx, dy, width, height);
+ break;
+#else
+ args = 0;
+ goto badcmd;
+#endif
+
+ case SVGA_CMD_DEFINE_CURSOR:
+ len -= 8;
+ if (len < 0)
+ goto rewind;
+
+ cursor.id = vmsvga_fifo_read(s);
+ cursor.hot_x = vmsvga_fifo_read(s);
+ cursor.hot_y = vmsvga_fifo_read(s);
+ cursor.width = x = vmsvga_fifo_read(s);
+ cursor.height = y = vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ cursor.bpp = vmsvga_fifo_read(s);
+
+ args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
+ if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
+ SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image)
+ goto badcmd;
+
+ len -= args;
+ if (len < 0)
+ goto rewind;
+
+ for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args ++)
+ cursor.mask[args] = vmsvga_fifo_read_raw(s);
+ for (args = 0; args < SVGA_PIXMAP_SIZE(x, y, cursor.bpp); args ++)
+ cursor.image[args] = vmsvga_fifo_read_raw(s);
+#ifdef HW_MOUSE_ACCEL
+ vmsvga_cursor_define(s, &cursor);
+ break;
+#else
+ args = 0;
+ goto badcmd;
+#endif
+
+ /*
+ * Other commands that we at least know the number of arguments
+ * for so we can avoid FIFO desync if driver uses them illegally.
+ */
+ case SVGA_CMD_DEFINE_ALPHA_CURSOR:
+ len -= 6;
+ if (len < 0)
+ goto rewind;
+
+ vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ args = x * y;
+ goto badcmd;
+ case SVGA_CMD_RECT_ROP_FILL:
+ args = 6;
+ goto badcmd;
+ case SVGA_CMD_RECT_ROP_COPY:
+ args = 7;
+ goto badcmd;
+ case SVGA_CMD_DRAW_GLYPH_CLIPPED:
+ len -= 4;
+ if (len < 0)
+ goto rewind;
+
+ vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ args = 7 + (vmsvga_fifo_read(s) >> 2);
+ goto badcmd;
+ case SVGA_CMD_SURFACE_ALPHA_BLEND:
+ args = 12;
+ goto badcmd;
+
+ /*
+ * Other commands that are not listed as depending on any
+ * CAPABILITIES bits, but are not described in the README either.
+ */
+ case SVGA_CMD_SURFACE_FILL:
+ case SVGA_CMD_SURFACE_COPY:
+ case SVGA_CMD_FRONT_ROP_FILL:
+ case SVGA_CMD_FENCE:
+ case SVGA_CMD_INVALID_CMD:
+ break; /* Nop */
+
+ default:
+ args = 0;
+ badcmd:
+ len -= args;
+ if (len < 0)
+ goto rewind;
+ while (args --)
+ vmsvga_fifo_read(s);
+ printf("%s: Unknown command 0x%02x in SVGA command FIFO\n",
+ __FUNCTION__, cmd);
+ break;
+
+ rewind:
+ s->cmd->stop = cmd_start;
+ break;
+ }
+ }
+
+ s->syncing = 0;
+}
+
+static uint32_t vmsvga_index_read(void *opaque, uint32_t address)
+{
+ struct vmsvga_state_s *s = opaque;
+ return s->index;
+}
+
+static void vmsvga_index_write(void *opaque, uint32_t address, uint32_t index)
+{
+ struct vmsvga_state_s *s = opaque;
+ s->index = index;
+}
+
+static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
+{
+ uint32_t caps;
+ struct vmsvga_state_s *s = opaque;
+ switch (s->index) {
+ case SVGA_REG_ID:
+ return s->svgaid;
+
+ case SVGA_REG_ENABLE:
+ return s->enable;
+
+ case SVGA_REG_WIDTH:
+ return s->width;
+
+ case SVGA_REG_HEIGHT:
+ return s->height;
+
+ case SVGA_REG_MAX_WIDTH:
+ return SVGA_MAX_WIDTH;
+
+ case SVGA_REG_MAX_HEIGHT:
+ return SVGA_MAX_HEIGHT;
+
+ case SVGA_REG_DEPTH:
+ return s->depth;
+
+ case SVGA_REG_BITS_PER_PIXEL:
+ return (s->depth + 7) & ~7;
+
+ case SVGA_REG_PSEUDOCOLOR:
+ return 0x0;
+
+ case SVGA_REG_RED_MASK:
+ return s->wred;
+ case SVGA_REG_GREEN_MASK:
+ return s->wgreen;
+ case SVGA_REG_BLUE_MASK:
+ return s->wblue;
+
+ case SVGA_REG_BYTES_PER_LINE:
+ return ((s->depth + 7) >> 3) * s->new_width;
+
+ case SVGA_REG_FB_START:
+ return s->vram_base;
+
+ case SVGA_REG_FB_OFFSET:
+ return 0x0;
+
+ case SVGA_REG_VRAM_SIZE:
+ return s->vga.vram_size;
+
+ case SVGA_REG_FB_SIZE:
+ return s->fb_size;
+
+ case SVGA_REG_CAPABILITIES:
+ caps = SVGA_CAP_NONE;
+#ifdef HW_RECT_ACCEL
+ caps |= SVGA_CAP_RECT_COPY;
+#endif
+#ifdef HW_FILL_ACCEL
+ caps |= SVGA_CAP_RECT_FILL;
+#endif
+#ifdef HW_MOUSE_ACCEL
+ if (s->vga.ds->mouse_set)
+ caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 |
+ SVGA_CAP_CURSOR_BYPASS;
+#endif
+ return caps;
+
+ case SVGA_REG_MEM_START:
+ return s->fifo_base;
+
+ case SVGA_REG_MEM_SIZE:
+ return s->fifo_size;
+
+ case SVGA_REG_CONFIG_DONE:
+ return s->config;
+
+ case SVGA_REG_SYNC:
+ case SVGA_REG_BUSY:
+ return s->syncing;
+
+ case SVGA_REG_GUEST_ID:
+ return s->guest;
+
+ case SVGA_REG_CURSOR_ID:
+ return s->cursor.id;
+
+ case SVGA_REG_CURSOR_X:
+ return s->cursor.x;
+
+ case SVGA_REG_CURSOR_Y:
+ return s->cursor.x;
+
+ case SVGA_REG_CURSOR_ON:
+ return s->cursor.on;
+
+ case SVGA_REG_HOST_BITS_PER_PIXEL:
+ return (s->depth + 7) & ~7;
+
+ case SVGA_REG_SCRATCH_SIZE:
+ return s->scratch_size;
+
+ case SVGA_REG_MEM_REGS:
+ case SVGA_REG_NUM_DISPLAYS:
+ case SVGA_REG_PITCHLOCK:
+ case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
+ return 0;
+
+ default:
+ if (s->index >= SVGA_SCRATCH_BASE &&
+ s->index < SVGA_SCRATCH_BASE + s->scratch_size)
+ return s->scratch[s->index - SVGA_SCRATCH_BASE];
+ printf("%s: Bad register %02x\n", __FUNCTION__, s->index);
+ }
+
+ return 0;
+}
+
+static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
+{
+ struct vmsvga_state_s *s = opaque;
+ switch (s->index) {
+ case SVGA_REG_ID:
+ if (value == SVGA_ID_2 || value == SVGA_ID_1 || value == SVGA_ID_0)
+ s->svgaid = value;
+ break;
+
+ case SVGA_REG_ENABLE:
+ s->enable = value;
+ s->config &= !!value;
+ s->width = -1;
+ s->height = -1;
+ s->invalidated = 1;
+ s->vga.invalidate(&s->vga);
+ if (s->enable) {
+ s->fb_size = ((s->depth + 7) >> 3) * s->new_width * s->new_height;
+ vga_dirty_log_stop(&s->vga);
+ } else {
+ vga_dirty_log_start(&s->vga);
+ }
+ break;
+
+ case SVGA_REG_WIDTH:
+ s->new_width = value;
+ s->invalidated = 1;
+ break;
+
+ case SVGA_REG_HEIGHT:
+ s->new_height = value;
+ s->invalidated = 1;
+ break;
+
+ case SVGA_REG_DEPTH:
+ case SVGA_REG_BITS_PER_PIXEL:
+ if (value != s->depth) {
+ printf("%s: Bad colour depth: %i bits\n", __FUNCTION__, value);
+ s->config = 0;
+ }
+ break;
+
+ case SVGA_REG_CONFIG_DONE:
+ if (value) {
+ s->fifo = (uint32_t *) s->fifo_ptr;
+ /* Check range and alignment. */
+ if ((CMD(min) | CMD(max) |
+ CMD(next_cmd) | CMD(stop)) & 3)
+ break;
+ if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo)
+ break;
+ if (CMD(max) > SVGA_FIFO_SIZE)
+ break;
+ if (CMD(max) < CMD(min) + 10 * 1024)
+ break;
+ }
+ s->config = !!value;
+ break;
+
+ case SVGA_REG_SYNC:
+ s->syncing = 1;
+ vmsvga_fifo_run(s); /* Or should we just wait for update_display? */
+ break;
+
+ case SVGA_REG_GUEST_ID:
+ s->guest = value;
+#ifdef VERBOSE
+ if (value >= GUEST_OS_BASE && value < GUEST_OS_BASE +
+ ARRAY_SIZE(vmsvga_guest_id))
+ printf("%s: guest runs %s.\n", __FUNCTION__,
+ vmsvga_guest_id[value - GUEST_OS_BASE]);
+#endif
+ break;
+
+ case SVGA_REG_CURSOR_ID:
+ s->cursor.id = value;
+ break;
+
+ case SVGA_REG_CURSOR_X:
+ s->cursor.x = value;
+ break;
+
+ case SVGA_REG_CURSOR_Y:
+ s->cursor.y = value;
+ break;
+
+ case SVGA_REG_CURSOR_ON:
+ s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW);
+ s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE);
+#ifdef HW_MOUSE_ACCEL
+ if (s->vga.ds->mouse_set && value <= SVGA_CURSOR_ON_SHOW)
+ s->vga.ds->mouse_set(s->cursor.x, s->cursor.y, s->cursor.on);
+#endif
+ break;
+
+ case SVGA_REG_MEM_REGS:
+ case SVGA_REG_NUM_DISPLAYS:
+ case SVGA_REG_PITCHLOCK:
+ case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
+ break;
+
+ default:
+ if (s->index >= SVGA_SCRATCH_BASE &&
+ s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
+ s->scratch[s->index - SVGA_SCRATCH_BASE] = value;
+ break;
+ }
+ printf("%s: Bad register %02x\n", __FUNCTION__, s->index);
+ }
+}
+
+static uint32_t vmsvga_bios_read(void *opaque, uint32_t address)
+{
+ printf("%s: what are we supposed to return?\n", __FUNCTION__);
+ return 0xcafe;
+}
+
+static void vmsvga_bios_write(void *opaque, uint32_t address, uint32_t data)
+{
+ printf("%s: what are we supposed to do with (%08x)?\n",
+ __FUNCTION__, data);
+}
+
+static inline void vmsvga_size(struct vmsvga_state_s *s)
+{
+ if (s->new_width != s->width || s->new_height != s->height) {
+ s->width = s->new_width;
+ s->height = s->new_height;
+ qemu_console_resize(s->vga.ds, s->width, s->height);
+ s->invalidated = 1;
+ }
+}
+
+static void vmsvga_update_display(void *opaque)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (!s->enable) {
+ s->vga.update(&s->vga);
+ return;
+ }
+
+ vmsvga_size(s);
+
+ vmsvga_fifo_run(s);
+ vmsvga_update_rect_flush(s);
+
+ /*
+ * Is it more efficient to look at vram VGA-dirty bits or wait
+ * for the driver to issue SVGA_CMD_UPDATE?
+ */
+ if (s->invalidated) {
+ s->invalidated = 0;
+ vmsvga_update_screen(s);
+ }
+}
+
+static void vmsvga_reset(struct vmsvga_state_s *s)
+{
+ s->index = 0;
+ s->enable = 0;
+ s->config = 0;
+ s->width = -1;
+ s->height = -1;
+ s->svgaid = SVGA_ID;
+ s->depth = ds_get_bits_per_pixel(s->vga.ds);
+ s->bypp = ds_get_bytes_per_pixel(s->vga.ds);
+ s->cursor.on = 0;
+ s->redraw_fifo_first = 0;
+ s->redraw_fifo_last = 0;
+ switch (s->depth) {
+ case 8:
+ s->wred = 0x00000007;
+ s->wgreen = 0x00000038;
+ s->wblue = 0x000000c0;
+ break;
+ case 15:
+ s->wred = 0x0000001f;
+ s->wgreen = 0x000003e0;
+ s->wblue = 0x00007c00;
+ break;
+ case 16:
+ s->wred = 0x0000001f;
+ s->wgreen = 0x000007e0;
+ s->wblue = 0x0000f800;
+ break;
+ case 24:
+ s->wred = 0x00ff0000;
+ s->wgreen = 0x0000ff00;
+ s->wblue = 0x000000ff;
+ break;
+ case 32:
+ s->wred = 0x00ff0000;
+ s->wgreen = 0x0000ff00;
+ s->wblue = 0x000000ff;
+ break;
+ }
+ s->syncing = 0;
+
+ vga_dirty_log_start(&s->vga);
+}
+
+static void vmsvga_invalidate_display(void *opaque)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (!s->enable) {
+ s->vga.invalidate(&s->vga);
+ return;
+ }
+
+ s->invalidated = 1;
+}
+
+/* save the vga display in a PPM image even if no display is
+ available */
+static void vmsvga_screen_dump(void *opaque, const char *filename)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (!s->enable) {
+ s->vga.screen_dump(&s->vga, filename);
+ return;
+ }
+
+ if (s->depth == 32) {
+ DisplaySurface *ds = qemu_create_displaysurface_from(s->width,
+ s->height, 32, ds_get_linesize(s->vga.ds), s->vga.vram_ptr);
+ ppm_save(filename, ds);
+ qemu_free(ds);
+ }
+}
+
+static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ if (s->vga.text_update)
+ s->vga.text_update(&s->vga, chardata);
+}
+
+#ifdef DIRECT_VRAM
+static uint32_t vmsvga_vram_readb(void *opaque, target_phys_addr_t addr)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (addr < s->fb_size)
+ return *(uint8_t *) (ds_get_data(s->ds) + addr);
+ else
+ return *(uint8_t *) (s->vram_ptr + addr);
+}
+
+static uint32_t vmsvga_vram_readw(void *opaque, target_phys_addr_t addr)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (addr < s->fb_size)
+ return *(uint16_t *) (ds_get_data(s->ds) + addr);
+ else
+ return *(uint16_t *) (s->vram_ptr + addr);
+}
+
+static uint32_t vmsvga_vram_readl(void *opaque, target_phys_addr_t addr)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (addr < s->fb_size)
+ return *(uint32_t *) (ds_get_data(s->ds) + addr);
+ else
+ return *(uint32_t *) (s->vram_ptr + addr);
+}
+
+static void vmsvga_vram_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (addr < s->fb_size)
+ *(uint8_t *) (ds_get_data(s->ds) + addr) = value;
+ else
+ *(uint8_t *) (s->vram_ptr + addr) = value;
+}
+
+static void vmsvga_vram_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (addr < s->fb_size)
+ *(uint16_t *) (ds_get_data(s->ds) + addr) = value;
+ else
+ *(uint16_t *) (s->vram_ptr + addr) = value;
+}
+
+static void vmsvga_vram_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (addr < s->fb_size)
+ *(uint32_t *) (ds_get_data(s->ds) + addr) = value;
+ else
+ *(uint32_t *) (s->vram_ptr + addr) = value;
+}
+
+static CPUReadMemoryFunc * const vmsvga_vram_read[] = {
+ vmsvga_vram_readb,
+ vmsvga_vram_readw,
+ vmsvga_vram_readl,
+};
+
+static CPUWriteMemoryFunc * const vmsvga_vram_write[] = {
+ vmsvga_vram_writeb,
+ vmsvga_vram_writew,
+ vmsvga_vram_writel,
+};
+#endif
+
+static int vmsvga_post_load(void *opaque, int version_id)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ s->invalidated = 1;
+ if (s->config)
+ s->fifo = (uint32_t *) s->fifo_ptr;
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_vmware_vga_internal = {
+ .name = "vmware_vga_internal",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = vmsvga_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_EQUAL(depth, struct vmsvga_state_s),
+ VMSTATE_INT32(enable, struct vmsvga_state_s),
+ VMSTATE_INT32(config, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.id, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.x, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.y, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.on, struct vmsvga_state_s),
+ VMSTATE_INT32(index, struct vmsvga_state_s),
+ VMSTATE_VARRAY_INT32(scratch, struct vmsvga_state_s,
+ scratch_size, 0, vmstate_info_uint32, uint32_t),
+ VMSTATE_INT32(new_width, struct vmsvga_state_s),
+ VMSTATE_INT32(new_height, struct vmsvga_state_s),
+ VMSTATE_UINT32(guest, struct vmsvga_state_s),
+ VMSTATE_UINT32(svgaid, struct vmsvga_state_s),
+ VMSTATE_INT32(syncing, struct vmsvga_state_s),
+ VMSTATE_INT32(fb_size, struct vmsvga_state_s),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_vmware_vga = {
+ .name = "vmware_vga",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(card, struct pci_vmsvga_state_s),
+ VMSTATE_STRUCT(chip, struct pci_vmsvga_state_s, 0,
+ vmstate_vmware_vga_internal, struct vmsvga_state_s),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void vmsvga_init(struct vmsvga_state_s *s, int vga_ram_size)
+{
+ s->scratch_size = SVGA_SCRATCH_SIZE;
+ s->scratch = qemu_malloc(s->scratch_size * 4);
+
+ s->vga.ds = graphic_console_init(vmsvga_update_display,
+ vmsvga_invalidate_display,
+ vmsvga_screen_dump,
+ vmsvga_text_update, s);
+
+
+ s->fifo_size = SVGA_FIFO_SIZE;
+ s->fifo_offset = qemu_ram_alloc(NULL, "vmsvga.fifo", s->fifo_size);
+ s->fifo_ptr = qemu_get_ram_ptr(s->fifo_offset);
+
+ vga_common_init(&s->vga, vga_ram_size);
+ vga_init(&s->vga);
+ vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
+
+ vmsvga_reset(s);
+}
+
+static void pci_vmsvga_map_ioport(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ struct pci_vmsvga_state_s *d = (struct pci_vmsvga_state_s *) pci_dev;
+ struct vmsvga_state_s *s = &d->chip;
+
+ register_ioport_read(addr + SVGA_IO_MUL * SVGA_INDEX_PORT,
+ 1, 4, vmsvga_index_read, s);
+ register_ioport_write(addr + SVGA_IO_MUL * SVGA_INDEX_PORT,
+ 1, 4, vmsvga_index_write, s);
+ register_ioport_read(addr + SVGA_IO_MUL * SVGA_VALUE_PORT,
+ 1, 4, vmsvga_value_read, s);
+ register_ioport_write(addr + SVGA_IO_MUL * SVGA_VALUE_PORT,
+ 1, 4, vmsvga_value_write, s);
+ register_ioport_read(addr + SVGA_IO_MUL * SVGA_BIOS_PORT,
+ 1, 4, vmsvga_bios_read, s);
+ register_ioport_write(addr + SVGA_IO_MUL * SVGA_BIOS_PORT,
+ 1, 4, vmsvga_bios_write, s);
+}
+
+static void pci_vmsvga_map_mem(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ struct pci_vmsvga_state_s *d = (struct pci_vmsvga_state_s *) pci_dev;
+ struct vmsvga_state_s *s = &d->chip;
+ ram_addr_t iomemtype;
+
+ s->vram_base = addr;
+#ifdef DIRECT_VRAM
+ iomemtype = cpu_register_io_memory(vmsvga_vram_read,
+ vmsvga_vram_write, s, DEVICE_NATIVE_ENDIAN);
+#else
+ iomemtype = s->vga.vram_offset | IO_MEM_RAM;
+#endif
+ cpu_register_physical_memory(s->vram_base, s->vga.vram_size,
+ iomemtype);
+
+ s->vga.map_addr = addr;
+ s->vga.map_end = addr + s->vga.vram_size;
+ vga_dirty_log_restart(&s->vga);
+}
+
+static void pci_vmsvga_map_fifo(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ struct pci_vmsvga_state_s *d = (struct pci_vmsvga_state_s *) pci_dev;
+ struct vmsvga_state_s *s = &d->chip;
+ ram_addr_t iomemtype;
+
+ s->fifo_base = addr;
+ iomemtype = s->fifo_offset | IO_MEM_RAM;
+ cpu_register_physical_memory(s->fifo_base, s->fifo_size,
+ iomemtype);
+}
+
+static int pci_vmsvga_initfn(PCIDevice *dev)
+{
+ struct pci_vmsvga_state_s *s =
+ DO_UPCAST(struct pci_vmsvga_state_s, card, dev);
+
+ pci_config_set_vendor_id(s->card.config, PCI_VENDOR_ID_VMWARE);
+ pci_config_set_device_id(s->card.config, SVGA_PCI_DEVICE_ID);
+ pci_config_set_class(s->card.config, PCI_CLASS_DISPLAY_VGA);
+ s->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */
+ s->card.config[PCI_LATENCY_TIMER] = 0x40; /* Latency timer */
+ s->card.config[PCI_SUBSYSTEM_VENDOR_ID] = PCI_VENDOR_ID_VMWARE & 0xff;
+ s->card.config[PCI_SUBSYSTEM_VENDOR_ID + 1] = PCI_VENDOR_ID_VMWARE >> 8;
+ s->card.config[PCI_SUBSYSTEM_ID] = SVGA_PCI_DEVICE_ID & 0xff;
+ s->card.config[PCI_SUBSYSTEM_ID + 1] = SVGA_PCI_DEVICE_ID >> 8;
+ s->card.config[PCI_INTERRUPT_LINE] = 0xff; /* End */
+
+ pci_register_bar(&s->card, 0, 0x10,
+ PCI_BASE_ADDRESS_SPACE_IO, pci_vmsvga_map_ioport);
+ pci_register_bar(&s->card, 1, VGA_RAM_SIZE,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_mem);
+
+ pci_register_bar(&s->card, 2, SVGA_FIFO_SIZE,
+ PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_fifo);
+
+ vmsvga_init(&s->chip, VGA_RAM_SIZE);
+
+ if (!dev->rom_bar) {
+ /* compatibility with pc-0.13 and older */
+ vga_init_vbe(&s->chip.vga);
+ }
+
+ return 0;
+}
+
+void pci_vmsvga_init(PCIBus *bus)
+{
+ pci_create_simple(bus, -1, "vmware-svga");
+}
+
+static PCIDeviceInfo vmsvga_info = {
+ .qdev.name = "vmware-svga",
+ .qdev.size = sizeof(struct pci_vmsvga_state_s),
+ .qdev.vmsd = &vmstate_vmware_vga,
+ .no_hotplug = 1,
+ .init = pci_vmsvga_initfn,
+ .romfile = "vgabios-vmware.bin",
+};
+
+static void vmsvga_register(void)
+{
+ pci_qdev_register(&vmsvga_info);
+}
+device_init(vmsvga_register);
diff --git a/hw/vmware_vga.h b/hw/vmware_vga.h
new file mode 100644
index 0000000..2e0813c
--- /dev/null
+++ b/hw/vmware_vga.h
@@ -0,0 +1,9 @@
+#ifndef QEMU_VMWARE_VGA_H
+#define QEMU_VMWARE_VGA_H
+
+#include "qemu-common.h"
+
+/* vmware_vga.c */
+void pci_vmsvga_init(PCIBus *bus);
+
+#endif
diff --git a/hw/vt82c686.c b/hw/vt82c686.c
new file mode 100644
index 0000000..cacc217
--- /dev/null
+++ b/hw/vt82c686.c
@@ -0,0 +1,594 @@
+/*
+ * VT82C686B south bridge support
+ *
+ * Copyright (c) 2008 yajin (yajin@vm-kernel.org)
+ * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn)
+ * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com)
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "vt82c686.h"
+#include "i2c.h"
+#include "smbus.h"
+#include "pci.h"
+#include "isa.h"
+#include "sysbus.h"
+#include "mips.h"
+#include "apm.h"
+#include "acpi.h"
+#include "pm_smbus.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+
+typedef uint32_t pci_addr_t;
+#include "pci_host.h"
+//#define DEBUG_VT82C686B
+
+#ifdef DEBUG_VT82C686B
+#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+typedef struct SuperIOConfig
+{
+ uint8_t config[0xff];
+ uint8_t index;
+ uint8_t data;
+} SuperIOConfig;
+
+typedef struct VT82C686BState {
+ PCIDevice dev;
+ SuperIOConfig superio_conf;
+} VT82C686BState;
+
+static void superio_ioport_writeb(void *opaque, uint32_t addr, uint32_t data)
+{
+ int can_write;
+ SuperIOConfig *superio_conf = opaque;
+
+ DPRINTF("superio_ioport_writeb address 0x%x val 0x%x \n", addr, data);
+ if (addr == 0x3f0) {
+ superio_conf->index = data & 0xff;
+ } else {
+ /* 0x3f1 */
+ switch (superio_conf->index) {
+ case 0x00 ... 0xdf:
+ case 0xe4:
+ case 0xe5:
+ case 0xe9 ... 0xed:
+ case 0xf3:
+ case 0xf5:
+ case 0xf7:
+ case 0xf9 ... 0xfb:
+ case 0xfd ... 0xff:
+ can_write = 0;
+ break;
+ default:
+ can_write = 1;
+
+ if (can_write) {
+ switch (superio_conf->index) {
+ case 0xe7:
+ if ((data & 0xff) != 0xfe) {
+ DPRINTF("chage uart 1 base. unsupported yet \n");
+ }
+ break;
+ case 0xe8:
+ if ((data & 0xff) != 0xbe) {
+ DPRINTF("chage uart 2 base. unsupported yet \n");
+ }
+ break;
+
+ default:
+ superio_conf->config[superio_conf->index] = data & 0xff;
+ }
+ }
+ }
+ superio_conf->config[superio_conf->index] = data & 0xff;
+ }
+}
+
+static uint32_t superio_ioport_readb(void *opaque, uint32_t addr)
+{
+ SuperIOConfig *superio_conf = opaque;
+
+ DPRINTF("superio_ioport_readb address 0x%x \n", addr);
+ return (superio_conf->config[superio_conf->index]);
+}
+
+static void vt82c686b_reset(void * opaque)
+{
+ PCIDevice *d = opaque;
+ uint8_t *pci_conf = d->config;
+ VT82C686BState *vt82c = DO_UPCAST(VT82C686BState, dev, d);
+
+ pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0);
+ pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL);
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
+
+ pci_conf[0x48] = 0x01; /* Miscellaneous Control 3 */
+ pci_conf[0x4a] = 0x04; /* IDE interrupt Routing */
+ pci_conf[0x4f] = 0x03; /* DMA/Master Mem Access Control 3 */
+ pci_conf[0x50] = 0x2d; /* PnP DMA Request Control */
+ pci_conf[0x59] = 0x04;
+ pci_conf[0x5a] = 0x04; /* KBC/RTC Control*/
+ pci_conf[0x5f] = 0x04;
+ pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */
+
+ vt82c->superio_conf.config[0xe0] = 0x3c;
+ vt82c->superio_conf.config[0xe2] = 0x03;
+ vt82c->superio_conf.config[0xe3] = 0xfc;
+ vt82c->superio_conf.config[0xe6] = 0xde;
+ vt82c->superio_conf.config[0xe7] = 0xfe;
+ vt82c->superio_conf.config[0xe8] = 0xbe;
+}
+
+/* write config pci function0 registers. PCI-ISA bridge */
+static void vt82c686b_write_config(PCIDevice * d, uint32_t address,
+ uint32_t val, int len)
+{
+ VT82C686BState *vt686 = DO_UPCAST(VT82C686BState, dev, d);
+
+ DPRINTF("vt82c686b_write_config address 0x%x val 0x%x len 0x%x \n",
+ address, val, len);
+
+ pci_default_write_config(d, address, val, len);
+ if (address == 0x85) { /* enable or disable super IO configure */
+ if (val & 0x2) {
+ /* floppy also uses 0x3f0 and 0x3f1.
+ * But we do not emulate flopy,so just set it here. */
+ isa_unassign_ioport(0x3f0, 2);
+ register_ioport_read(0x3f0, 2, 1, superio_ioport_readb,
+ &vt686->superio_conf);
+ register_ioport_write(0x3f0, 2, 1, superio_ioport_writeb,
+ &vt686->superio_conf);
+ } else {
+ isa_unassign_ioport(0x3f0, 2);
+ }
+ }
+}
+
+#define ACPI_DBG_IO_ADDR 0xb044
+
+typedef struct VT686PMState {
+ PCIDevice dev;
+ uint16_t pmsts;
+ uint16_t pmen;
+ uint16_t pmcntrl;
+ APMState apm;
+ QEMUTimer *tmr_timer;
+ int64_t tmr_overflow_time;
+ PMSMBus smb;
+ uint32_t smb_io_base;
+} VT686PMState;
+
+typedef struct VT686AC97State {
+ PCIDevice dev;
+} VT686AC97State;
+
+typedef struct VT686MC97State {
+ PCIDevice dev;
+} VT686MC97State;
+
+#define RTC_EN (1 << 10)
+#define PWRBTN_EN (1 << 8)
+#define GBL_EN (1 << 5)
+#define TMROF_EN (1 << 0)
+#define SUS_EN (1 << 13)
+
+#define ACPI_ENABLE 0xf1
+#define ACPI_DISABLE 0xf0
+
+static uint32_t get_pmtmr(VT686PMState *s)
+{
+ uint32_t d;
+ d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec());
+ return d & 0xffffff;
+}
+
+static int get_pmsts(VT686PMState *s)
+{
+ int64_t d;
+ int pmsts;
+ pmsts = s->pmsts;
+ d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec());
+ if (d >= s->tmr_overflow_time)
+ s->pmsts |= TMROF_EN;
+ return pmsts;
+}
+
+static void pm_update_sci(VT686PMState *s)
+{
+ int sci_level, pmsts;
+ int64_t expire_time;
+
+ pmsts = get_pmsts(s);
+ sci_level = (((pmsts & s->pmen) &
+ (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0);
+ qemu_set_irq(s->dev.irq[0], sci_level);
+ /* schedule a timer interruption if needed */
+ if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) {
+ expire_time = muldiv64(s->tmr_overflow_time, get_ticks_per_sec(), PM_TIMER_FREQUENCY);
+ qemu_mod_timer(s->tmr_timer, expire_time);
+ } else {
+ qemu_del_timer(s->tmr_timer);
+ }
+}
+
+static void pm_tmr_timer(void *opaque)
+{
+ VT686PMState *s = opaque;
+ pm_update_sci(s);
+}
+
+static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+ VT686PMState *s = opaque;
+
+ addr &= 0x0f;
+ switch (addr) {
+ case 0x00:
+ {
+ int64_t d;
+ int pmsts;
+ pmsts = get_pmsts(s);
+ if (pmsts & val & TMROF_EN) {
+ /* if TMRSTS is reset, then compute the new overflow time */
+ d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec());
+ s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+ }
+ s->pmsts &= ~val;
+ pm_update_sci(s);
+ }
+ break;
+ case 0x02:
+ s->pmen = val;
+ pm_update_sci(s);
+ break;
+ case 0x04:
+ {
+ int sus_typ;
+ s->pmcntrl = val & ~(SUS_EN);
+ if (val & SUS_EN) {
+ /* change suspend type */
+ sus_typ = (val >> 10) & 3;
+ switch (sus_typ) {
+ case 0: /* soft power off */
+ qemu_system_shutdown_request();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ DPRINTF("PM writew port=0x%04x val=0x%02x\n", addr, val);
+}
+
+static uint32_t pm_ioport_readw(void *opaque, uint32_t addr)
+{
+ VT686PMState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x0f;
+ switch (addr) {
+ case 0x00:
+ val = get_pmsts(s);
+ break;
+ case 0x02:
+ val = s->pmen;
+ break;
+ case 0x04:
+ val = s->pmcntrl;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ DPRINTF("PM readw port=0x%04x val=0x%02x\n", addr, val);
+ return val;
+}
+
+static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+ addr &= 0x0f;
+ DPRINTF("PM writel port=0x%04x val=0x%08x\n", addr, val);
+}
+
+static uint32_t pm_ioport_readl(void *opaque, uint32_t addr)
+{
+ VT686PMState *s = opaque;
+ uint32_t val;
+
+ addr &= 0x0f;
+ switch (addr) {
+ case 0x08:
+ val = get_pmtmr(s);
+ break;
+ default:
+ val = 0;
+ break;
+ }
+ DPRINTF("PM readl port=0x%04x val=0x%08x\n", addr, val);
+ return val;
+}
+
+static void pm_io_space_update(VT686PMState *s)
+{
+ uint32_t pm_io_base;
+
+ if (s->dev.config[0x80] & 1) {
+ pm_io_base = pci_get_long(s->dev.config + 0x40);
+ pm_io_base &= 0xffc0;
+
+ /* XXX: need to improve memory and ioport allocation */
+ DPRINTF("PM: mapping to 0x%x\n", pm_io_base);
+ register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s);
+ register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s);
+ register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s);
+ register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s);
+ }
+}
+
+static void pm_write_config(PCIDevice *d,
+ uint32_t address, uint32_t val, int len)
+{
+ DPRINTF("pm_write_config address 0x%x val 0x%x len 0x%x \n",
+ address, val, len);
+ pci_default_write_config(d, address, val, len);
+}
+
+static int vmstate_acpi_post_load(void *opaque, int version_id)
+{
+ VT686PMState *s = opaque;
+
+ pm_io_space_update(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_acpi = {
+ .name = "vt82c686b_pm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = vmstate_acpi_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, VT686PMState),
+ VMSTATE_UINT16(pmsts, VT686PMState),
+ VMSTATE_UINT16(pmen, VT686PMState),
+ VMSTATE_UINT16(pmcntrl, VT686PMState),
+ VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState),
+ VMSTATE_TIMER(tmr_timer, VT686PMState),
+ VMSTATE_INT64(tmr_overflow_time, VT686PMState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/*
+ * TODO: vt82c686b_ac97_init() and vt82c686b_mc97_init()
+ * just register a PCI device now, functionalities will be implemented later.
+ */
+
+static int vt82c686b_ac97_initfn(PCIDevice *dev)
+{
+ VT686AC97State *s = DO_UPCAST(VT686AC97State, dev, dev);
+ uint8_t *pci_conf = s->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_AC97);
+ pci_config_set_class(pci_conf, PCI_CLASS_MULTIMEDIA_AUDIO);
+ pci_config_set_revision(pci_conf, 0x50);
+
+ pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
+ PCI_COMMAND_PARITY);
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST |
+ PCI_STATUS_DEVSEL_MEDIUM);
+ pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03);
+
+ return 0;
+}
+
+void vt82c686b_ac97_init(PCIBus *bus, int devfn)
+{
+ PCIDevice *dev;
+
+ dev = pci_create(bus, devfn, "VT82C686B_AC97");
+ qdev_init_nofail(&dev->qdev);
+}
+
+static PCIDeviceInfo via_ac97_info = {
+ .qdev.name = "VT82C686B_AC97",
+ .qdev.desc = "AC97",
+ .qdev.size = sizeof(VT686AC97State),
+ .init = vt82c686b_ac97_initfn,
+};
+
+static void vt82c686b_ac97_register(void)
+{
+ pci_qdev_register(&via_ac97_info);
+}
+
+device_init(vt82c686b_ac97_register);
+
+static int vt82c686b_mc97_initfn(PCIDevice *dev)
+{
+ VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev);
+ uint8_t *pci_conf = s->dev.config;
+
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_MC97);
+ pci_config_set_class(pci_conf, PCI_CLASS_COMMUNICATION_OTHER);
+ pci_config_set_revision(pci_conf, 0x30);
+
+ pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE |
+ PCI_COMMAND_VGA_PALETTE);
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
+ pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03);
+
+ return 0;
+}
+
+void vt82c686b_mc97_init(PCIBus *bus, int devfn)
+{
+ PCIDevice *dev;
+
+ dev = pci_create(bus, devfn, "VT82C686B_MC97");
+ qdev_init_nofail(&dev->qdev);
+}
+
+static PCIDeviceInfo via_mc97_info = {
+ .qdev.name = "VT82C686B_MC97",
+ .qdev.desc = "MC97",
+ .qdev.size = sizeof(VT686MC97State),
+ .init = vt82c686b_mc97_initfn,
+};
+
+static void vt82c686b_mc97_register(void)
+{
+ pci_qdev_register(&via_mc97_info);
+}
+
+device_init(vt82c686b_mc97_register);
+
+/* vt82c686 pm init */
+static int vt82c686b_pm_initfn(PCIDevice *dev)
+{
+ VT686PMState *s = DO_UPCAST(VT686PMState, dev, dev);
+ uint8_t *pci_conf;
+
+ pci_conf = s->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ACPI);
+ pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER);
+ pci_config_set_revision(pci_conf, 0x40);
+
+ pci_set_word(pci_conf + PCI_COMMAND, 0);
+ pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK |
+ PCI_STATUS_DEVSEL_MEDIUM);
+
+ /* 0x48-0x4B is Power Management I/O Base */
+ pci_set_long(pci_conf + 0x48, 0x00000001);
+
+ /* SMB ports:0xeee0~0xeeef */
+ s->smb_io_base =((s->smb_io_base & 0xfff0) + 0x0);
+ pci_conf[0x90] = s->smb_io_base | 1;
+ pci_conf[0x91] = s->smb_io_base >> 8;
+ pci_conf[0xd2] = 0x90;
+ register_ioport_write(s->smb_io_base, 0xf, 1, smb_ioport_writeb, &s->smb);
+ register_ioport_read(s->smb_io_base, 0xf, 1, smb_ioport_readb, &s->smb);
+
+ apm_init(&s->apm, NULL, s);
+
+ s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s);
+
+ pm_smbus_init(&s->dev.qdev, &s->smb);
+
+ return 0;
+}
+
+i2c_bus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+ qemu_irq sci_irq)
+{
+ PCIDevice *dev;
+ VT686PMState *s;
+
+ dev = pci_create(bus, devfn, "VT82C686B_PM");
+ qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
+
+ s = DO_UPCAST(VT686PMState, dev, dev);
+
+ qdev_init_nofail(&dev->qdev);
+
+ return s->smb.smbus;
+}
+
+static PCIDeviceInfo via_pm_info = {
+ .qdev.name = "VT82C686B_PM",
+ .qdev.desc = "PM",
+ .qdev.size = sizeof(VT686PMState),
+ .qdev.vmsd = &vmstate_acpi,
+ .init = vt82c686b_pm_initfn,
+ .config_write = pm_write_config,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void vt82c686b_pm_register(void)
+{
+ pci_qdev_register(&via_pm_info);
+}
+
+device_init(vt82c686b_pm_register);
+
+static const VMStateDescription vmstate_via = {
+ .name = "vt82c686b",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, VT82C686BState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/* init the PCI-to-ISA bridge */
+static int vt82c686b_initfn(PCIDevice *d)
+{
+ uint8_t *pci_conf;
+ uint8_t *wmask;
+ int i;
+
+ isa_bus_new(&d->qdev);
+
+ pci_conf = d->config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ISA_BRIDGE);
+ pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
+ pci_config_set_prog_interface(pci_conf, 0x0);
+ pci_config_set_revision(pci_conf,0x40); /* Revision 4.0 */
+
+ wmask = d->wmask;
+ for (i = 0x00; i < 0xff; i++) {
+ if (i<=0x03 || (i>=0x08 && i<=0x3f)) {
+ wmask[i] = 0x00;
+ }
+ }
+
+ qemu_register_reset(vt82c686b_reset, d);
+
+ return 0;
+}
+
+int vt82c686b_init(PCIBus *bus, int devfn)
+{
+ PCIDevice *d;
+
+ d = pci_create_simple_multifunction(bus, devfn, true, "VT82C686B");
+
+ return d->devfn;
+}
+
+static PCIDeviceInfo via_info = {
+ .qdev.name = "VT82C686B",
+ .qdev.desc = "ISA bridge",
+ .qdev.size = sizeof(VT82C686BState),
+ .qdev.vmsd = &vmstate_via,
+ .qdev.no_user = 1,
+ .init = vt82c686b_initfn,
+ .config_write = vt82c686b_write_config,
+};
+
+static void vt82c686b_register(void)
+{
+ pci_qdev_register(&via_info);
+}
+device_init(vt82c686b_register);
diff --git a/hw/vt82c686.h b/hw/vt82c686.h
new file mode 100644
index 0000000..e3270ca
--- /dev/null
+++ b/hw/vt82c686.h
@@ -0,0 +1,11 @@
+#ifndef HW_VT82C686_H
+#define HW_VT82C686_H
+
+/* vt82c686.c */
+int vt82c686b_init(PCIBus * bus, int devfn);
+void vt82c686b_ac97_init(PCIBus *bus, int devfn);
+void vt82c686b_mc97_init(PCIBus *bus, int devfn);
+i2c_bus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+ qemu_irq sci_irq);
+
+#endif
diff --git a/hw/watchdog.c b/hw/watchdog.c
new file mode 100644
index 0000000..e9dd56e
--- /dev/null
+++ b/hw/watchdog.c
@@ -0,0 +1,147 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "qemu-queue.h"
+#include "qemu-objects.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "hw/watchdog.h"
+
+/* Possible values for action parameter. */
+#define WDT_RESET 1 /* Hard reset. */
+#define WDT_SHUTDOWN 2 /* Shutdown. */
+#define WDT_POWEROFF 3 /* Quit. */
+#define WDT_PAUSE 4 /* Pause. */
+#define WDT_DEBUG 5 /* Prints a message and continues running. */
+#define WDT_NONE 6 /* Do nothing. */
+
+static int watchdog_action = WDT_RESET;
+static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
+
+void watchdog_add_model(WatchdogTimerModel *model)
+{
+ QLIST_INSERT_HEAD(&watchdog_list, model, entry);
+}
+
+/* Returns:
+ * 0 = continue
+ * 1 = exit program with error
+ * 2 = exit program without error
+ */
+int select_watchdog(const char *p)
+{
+ WatchdogTimerModel *model;
+ QemuOpts *opts;
+
+ /* -watchdog ? lists available devices and exits cleanly. */
+ if (strcmp(p, "?") == 0) {
+ QLIST_FOREACH(model, &watchdog_list, entry) {
+ fprintf(stderr, "\t%s\t%s\n",
+ model->wdt_name, model->wdt_description);
+ }
+ return 2;
+ }
+
+ QLIST_FOREACH(model, &watchdog_list, entry) {
+ if (strcasecmp(model->wdt_name, p) == 0) {
+ /* add the device */
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ qemu_opt_set(opts, "driver", p);
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
+ QLIST_FOREACH(model, &watchdog_list, entry) {
+ fprintf(stderr, "\t%s\t%s\n",
+ model->wdt_name, model->wdt_description);
+ }
+ return 1;
+}
+
+int select_watchdog_action(const char *p)
+{
+ if (strcasecmp(p, "reset") == 0)
+ watchdog_action = WDT_RESET;
+ else if (strcasecmp(p, "shutdown") == 0)
+ watchdog_action = WDT_SHUTDOWN;
+ else if (strcasecmp(p, "poweroff") == 0)
+ watchdog_action = WDT_POWEROFF;
+ else if (strcasecmp(p, "pause") == 0)
+ watchdog_action = WDT_PAUSE;
+ else if (strcasecmp(p, "debug") == 0)
+ watchdog_action = WDT_DEBUG;
+ else if (strcasecmp(p, "none") == 0)
+ watchdog_action = WDT_NONE;
+ else
+ return -1;
+
+ return 0;
+}
+
+static void watchdog_mon_event(const char *action)
+{
+ QObject *data;
+
+ data = qobject_from_jsonf("{ 'action': %s }", action);
+ monitor_protocol_event(QEVENT_WATCHDOG, data);
+ qobject_decref(data);
+}
+
+/* This actually performs the "action" once a watchdog has expired,
+ * ie. reboot, shutdown, exit, etc.
+ */
+void watchdog_perform_action(void)
+{
+ switch(watchdog_action) {
+ case WDT_RESET: /* same as 'system_reset' in monitor */
+ watchdog_mon_event("reset");
+ qemu_system_reset_request();
+ break;
+
+ case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */
+ watchdog_mon_event("shutdown");
+ qemu_system_powerdown_request();
+ break;
+
+ case WDT_POWEROFF: /* same as 'quit' command in monitor */
+ watchdog_mon_event("poweroff");
+ exit(0);
+ break;
+
+ case WDT_PAUSE: /* same as 'stop' command in monitor */
+ watchdog_mon_event("pause");
+ vm_stop(0);
+ break;
+
+ case WDT_DEBUG:
+ watchdog_mon_event("debug");
+ fprintf(stderr, "watchdog: timer fired\n");
+ break;
+
+ case WDT_NONE:
+ watchdog_mon_event("none");
+ break;
+ }
+}
diff --git a/hw/watchdog.h b/hw/watchdog.h
new file mode 100644
index 0000000..c12a293
--- /dev/null
+++ b/hw/watchdog.h
@@ -0,0 +1,43 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#ifndef QEMU_WATCHDOG_H
+#define QEMU_WATCHDOG_H
+
+#include "qemu-queue.h"
+
+struct WatchdogTimerModel {
+ QLIST_ENTRY(WatchdogTimerModel) entry;
+
+ /* Short name of the device - used to select it on the command line. */
+ const char *wdt_name;
+ /* Longer description (eg. manufacturer and full model number). */
+ const char *wdt_description;
+};
+typedef struct WatchdogTimerModel WatchdogTimerModel;
+
+/* in hw/watchdog.c */
+int select_watchdog(const char *p);
+int select_watchdog_action(const char *action);
+void watchdog_add_model(WatchdogTimerModel *model);
+void watchdog_perform_action(void);
+
+#endif /* QEMU_WATCHDOG_H */
diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c
new file mode 100644
index 0000000..90bf5f6
--- /dev/null
+++ b/hw/wdt_i6300esb.c
@@ -0,0 +1,448 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "pci.h"
+
+/*#define I6300ESB_DEBUG 1*/
+
+#ifdef I6300ESB_DEBUG
+#define i6300esb_debug(fs,...) \
+ fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define i6300esb_debug(fs,...)
+#endif
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG 0x60 /* Config register */
+#define ESB_LOCK_REG 0x68 /* WDT lock register */
+
+/* Memory mapped registers (offset from base address) */
+#define ESB_TIMER1_REG 0x00 /* Timer1 value after each reset */
+#define ESB_TIMER2_REG 0x04 /* Timer2 value after each reset */
+#define ESB_GINTSR_REG 0x08 /* General Interrupt Status Register */
+#define ESB_RELOAD_REG 0x0c /* Reload register */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC (0x01 << 2) /* Watchdog functionality */
+#define ESB_WDT_ENABLE (0x01 << 1) /* Enable WDT */
+#define ESB_WDT_LOCK (0x01 << 0) /* Lock (nowayout) */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT (0x01 << 5) /* Enable reboot on timeout */
+#define ESB_WDT_FREQ (0x01 << 2) /* Decrement frequency */
+#define ESB_WDT_INTTYPE (0x11 << 0) /* Interrupt type on timer1 timeout */
+
+/* Reload register bits */
+#define ESB_WDT_RELOAD (0x01 << 8) /* prevent timeout */
+
+/* Magic constants */
+#define ESB_UNLOCK1 0x80 /* Step 1 to unlock reset registers */
+#define ESB_UNLOCK2 0x86 /* Step 2 to unlock reset registers */
+
+/* Device state. */
+struct I6300State {
+ PCIDevice dev;
+
+ int reboot_enabled; /* "Reboot" on timer expiry. The real action
+ * performed depends on the -watchdog-action
+ * param passed on QEMU command line.
+ */
+ int clock_scale; /* Clock scale. */
+#define CLOCK_SCALE_1KHZ 0
+#define CLOCK_SCALE_1MHZ 1
+
+ int int_type; /* Interrupt type generated. */
+#define INT_TYPE_IRQ 0 /* APIC 1, INT 10 */
+#define INT_TYPE_SMI 2
+#define INT_TYPE_DISABLED 3
+
+ int free_run; /* If true, reload timer on expiry. */
+ int locked; /* If true, enabled field cannot be changed. */
+ int enabled; /* If true, watchdog is enabled. */
+
+ QEMUTimer *timer; /* The actual watchdog timer. */
+
+ uint32_t timer1_preload; /* Values preloaded into timer1, timer2. */
+ uint32_t timer2_preload;
+ int stage; /* Stage (1 or 2). */
+
+ int unlock_state; /* Guest writes 0x80, 0x86 to unlock the
+ * registers, and we transition through
+ * states 0 -> 1 -> 2 when this happens.
+ */
+
+ int previous_reboot_flag; /* If the watchdog caused the previous
+ * reboot, this flag will be set.
+ */
+};
+
+typedef struct I6300State I6300State;
+
+/* This function is called when the watchdog has either been enabled
+ * (hence it starts counting down) or has been keep-alived.
+ */
+static void i6300esb_restart_timer(I6300State *d, int stage)
+{
+ int64_t timeout;
+
+ if (!d->enabled)
+ return;
+
+ d->stage = stage;
+
+ if (d->stage <= 1)
+ timeout = d->timer1_preload;
+ else
+ timeout = d->timer2_preload;
+
+ if (d->clock_scale == CLOCK_SCALE_1KHZ)
+ timeout <<= 15;
+ else
+ timeout <<= 5;
+
+ /* Get the timeout in units of ticks_per_sec. */
+ timeout = get_ticks_per_sec() * timeout / 33000000;
+
+ i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
+
+ qemu_mod_timer(d->timer, qemu_get_clock(vm_clock) + timeout);
+}
+
+/* This is called when the guest disables the watchdog. */
+static void i6300esb_disable_timer(I6300State *d)
+{
+ i6300esb_debug("timer disabled\n");
+
+ qemu_del_timer(d->timer);
+}
+
+static void i6300esb_reset(DeviceState *dev)
+{
+ PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev);
+ I6300State *d = DO_UPCAST(I6300State, dev, pdev);
+
+ i6300esb_debug("I6300State = %p\n", d);
+
+ i6300esb_disable_timer(d);
+
+ /* NB: Don't change d->previous_reboot_flag in this function. */
+
+ d->reboot_enabled = 1;
+ d->clock_scale = CLOCK_SCALE_1KHZ;
+ d->int_type = INT_TYPE_IRQ;
+ d->free_run = 0;
+ d->locked = 0;
+ d->enabled = 0;
+ d->timer1_preload = 0xfffff;
+ d->timer2_preload = 0xfffff;
+ d->stage = 1;
+ d->unlock_state = 0;
+}
+
+/* This function is called when the watchdog expires. Note that
+ * the hardware has two timers, and so expiry happens in two stages.
+ * If d->stage == 1 then we perform the first stage action (usually,
+ * sending an interrupt) and then restart the timer again for the
+ * second stage. If the second stage expires then the watchdog
+ * really has run out.
+ */
+static void i6300esb_timer_expired(void *vp)
+{
+ I6300State *d = vp;
+
+ i6300esb_debug("stage %d\n", d->stage);
+
+ if (d->stage == 1) {
+ /* What to do at the end of stage 1? */
+ switch (d->int_type) {
+ case INT_TYPE_IRQ:
+ fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
+ break;
+ case INT_TYPE_SMI:
+ fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
+ break;
+ }
+
+ /* Start the second stage. */
+ i6300esb_restart_timer(d, 2);
+ } else {
+ /* Second stage expired, reboot for real. */
+ if (d->reboot_enabled) {
+ d->previous_reboot_flag = 1;
+ watchdog_perform_action(); /* This reboots, exits, etc */
+ i6300esb_reset(&d->dev.qdev);
+ }
+
+ /* In "free running mode" we start stage 1 again. */
+ if (d->free_run)
+ i6300esb_restart_timer(d, 1);
+ }
+}
+
+static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
+ uint32_t data, int len)
+{
+ I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ int old;
+
+ i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
+
+ if (addr == ESB_CONFIG_REG && len == 2) {
+ d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
+ d->clock_scale =
+ (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
+ d->int_type = (data & ESB_WDT_INTTYPE);
+ } else if (addr == ESB_LOCK_REG && len == 1) {
+ if (!d->locked) {
+ d->locked = (data & ESB_WDT_LOCK) != 0;
+ d->free_run = (data & ESB_WDT_FUNC) != 0;
+ old = d->enabled;
+ d->enabled = (data & ESB_WDT_ENABLE) != 0;
+ if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
+ i6300esb_restart_timer(d, 1);
+ else if (!d->enabled)
+ i6300esb_disable_timer(d);
+ }
+ } else {
+ pci_default_write_config(dev, addr, data, len);
+ }
+}
+
+static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
+{
+ I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ uint32_t data;
+
+ i6300esb_debug ("addr = %x, len = %d\n", addr, len);
+
+ if (addr == ESB_CONFIG_REG && len == 2) {
+ data =
+ (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
+ (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
+ d->int_type;
+ return data;
+ } else if (addr == ESB_LOCK_REG && len == 1) {
+ data =
+ (d->free_run ? ESB_WDT_FUNC : 0) |
+ (d->locked ? ESB_WDT_LOCK : 0) |
+ (d->enabled ? ESB_WDT_ENABLE : 0);
+ return data;
+ } else {
+ return pci_default_read_config(dev, addr, len);
+ }
+}
+
+static uint32_t i6300esb_mem_readb(void *vp, target_phys_addr_t addr)
+{
+ i6300esb_debug ("addr = %x\n", (int) addr);
+
+ return 0;
+}
+
+static uint32_t i6300esb_mem_readw(void *vp, target_phys_addr_t addr)
+{
+ uint32_t data = 0;
+ I6300State *d = vp;
+
+ i6300esb_debug("addr = %x\n", (int) addr);
+
+ if (addr == 0xc) {
+ /* The previous reboot flag is really bit 9, but there is
+ * a bug in the Linux driver where it thinks it's bit 12.
+ * Set both.
+ */
+ data = d->previous_reboot_flag ? 0x1200 : 0;
+ }
+
+ return data;
+}
+
+static uint32_t i6300esb_mem_readl(void *vp, target_phys_addr_t addr)
+{
+ i6300esb_debug("addr = %x\n", (int) addr);
+
+ return 0;
+}
+
+static void i6300esb_mem_writeb(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+ I6300State *d = vp;
+
+ i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
+
+ if (addr == 0xc && val == 0x80)
+ d->unlock_state = 1;
+ else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+ d->unlock_state = 2;
+}
+
+static void i6300esb_mem_writew(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+ I6300State *d = vp;
+
+ i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
+
+ if (addr == 0xc && val == 0x80)
+ d->unlock_state = 1;
+ else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+ d->unlock_state = 2;
+ else {
+ if (d->unlock_state == 2) {
+ if (addr == 0xc) {
+ if ((val & 0x100) != 0)
+ /* This is the "ping" from the userspace watchdog in
+ * the guest ...
+ */
+ i6300esb_restart_timer(d, 1);
+
+ /* Setting bit 9 resets the previous reboot flag.
+ * There's a bug in the Linux driver where it sets
+ * bit 12 instead.
+ */
+ if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
+ d->previous_reboot_flag = 0;
+ }
+ }
+
+ d->unlock_state = 0;
+ }
+ }
+}
+
+static void i6300esb_mem_writel(void *vp, target_phys_addr_t addr, uint32_t val)
+{
+ I6300State *d = vp;
+
+ i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+ if (addr == 0xc && val == 0x80)
+ d->unlock_state = 1;
+ else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+ d->unlock_state = 2;
+ else {
+ if (d->unlock_state == 2) {
+ if (addr == 0)
+ d->timer1_preload = val & 0xfffff;
+ else if (addr == 4)
+ d->timer2_preload = val & 0xfffff;
+
+ d->unlock_state = 0;
+ }
+ }
+}
+
+static void i6300esb_map(PCIDevice *dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ static CPUReadMemoryFunc * const mem_read[3] = {
+ i6300esb_mem_readb,
+ i6300esb_mem_readw,
+ i6300esb_mem_readl,
+ };
+ static CPUWriteMemoryFunc * const mem_write[3] = {
+ i6300esb_mem_writeb,
+ i6300esb_mem_writew,
+ i6300esb_mem_writel,
+ };
+ I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ int io_mem;
+
+ i6300esb_debug("addr = %"FMT_PCIBUS", size = %"FMT_PCIBUS", type = %d\n",
+ addr, size, type);
+
+ io_mem = cpu_register_io_memory(mem_read, mem_write, d,
+ DEVICE_NATIVE_ENDIAN);
+ cpu_register_physical_memory (addr, 0x10, io_mem);
+ /* qemu_register_coalesced_mmio (addr, 0x10); ? */
+}
+
+static const VMStateDescription vmstate_i6300esb = {
+ .name = "i6300esb_wdt",
+ .version_id = sizeof(I6300State),
+ .minimum_version_id = sizeof(I6300State),
+ .minimum_version_id_old = sizeof(I6300State),
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, I6300State),
+ VMSTATE_INT32(reboot_enabled, I6300State),
+ VMSTATE_INT32(clock_scale, I6300State),
+ VMSTATE_INT32(int_type, I6300State),
+ VMSTATE_INT32(free_run, I6300State),
+ VMSTATE_INT32(locked, I6300State),
+ VMSTATE_INT32(enabled, I6300State),
+ VMSTATE_TIMER(timer, I6300State),
+ VMSTATE_UINT32(timer1_preload, I6300State),
+ VMSTATE_UINT32(timer2_preload, I6300State),
+ VMSTATE_INT32(stage, I6300State),
+ VMSTATE_INT32(unlock_state, I6300State),
+ VMSTATE_INT32(previous_reboot_flag, I6300State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int i6300esb_init(PCIDevice *dev)
+{
+ I6300State *d = DO_UPCAST(I6300State, dev, dev);
+ uint8_t *pci_conf;
+
+ i6300esb_debug("I6300State = %p\n", d);
+
+ d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d);
+ d->previous_reboot_flag = 0;
+
+ pci_conf = d->dev.config;
+ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+ pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9);
+ pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER);
+
+ pci_register_bar(&d->dev, 0, 0x10,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, i6300esb_map);
+
+ return 0;
+}
+
+static WatchdogTimerModel model = {
+ .wdt_name = "i6300esb",
+ .wdt_description = "Intel 6300ESB",
+};
+
+static PCIDeviceInfo i6300esb_info = {
+ .qdev.name = "i6300esb",
+ .qdev.size = sizeof(I6300State),
+ .qdev.vmsd = &vmstate_i6300esb,
+ .qdev.reset = i6300esb_reset,
+ .config_read = i6300esb_config_read,
+ .config_write = i6300esb_config_write,
+ .init = i6300esb_init,
+};
+
+static void i6300esb_register_devices(void)
+{
+ watchdog_add_model(&model);
+ pci_qdev_register(&i6300esb_info);
+}
+
+device_init(i6300esb_register_devices);
diff --git a/hw/wdt_ib700.c b/hw/wdt_ib700.c
new file mode 100644
index 0000000..1248464
--- /dev/null
+++ b/hw/wdt_ib700.c
@@ -0,0 +1,137 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "watchdog.h"
+#include "hw.h"
+#include "isa.h"
+#include "pc.h"
+
+/*#define IB700_DEBUG 1*/
+
+#ifdef IB700_DEBUG
+#define ib700_debug(fs,...) \
+ fprintf(stderr,"ib700: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define ib700_debug(fs,...)
+#endif
+
+typedef struct IB700state {
+ ISADevice dev;
+ QEMUTimer *timer;
+} IB700State;
+
+/* This is the timer. We use a global here because the watchdog
+ * code ensures there is only one watchdog (it is located at a fixed,
+ * unchangable IO port, so there could only ever be one anyway).
+ */
+
+/* A write to this register enables the timer. */
+static void ib700_write_enable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+ IB700State *s = vp;
+ static int time_map[] = {
+ 30, 28, 26, 24, 22, 20, 18, 16,
+ 14, 12, 10, 8, 6, 4, 2, 0
+ };
+ int64_t timeout;
+
+ ib700_debug("addr = %x, data = %x\n", addr, data);
+
+ timeout = (int64_t) time_map[data & 0xF] * get_ticks_per_sec();
+ qemu_mod_timer(s->timer, qemu_get_clock (vm_clock) + timeout);
+}
+
+/* A write (of any value) to this register disables the timer. */
+static void ib700_write_disable_reg(void *vp, uint32_t addr, uint32_t data)
+{
+ IB700State *s = vp;
+
+ ib700_debug("addr = %x, data = %x\n", addr, data);
+
+ qemu_del_timer(s->timer);
+}
+
+/* This is called when the watchdog expires. */
+static void ib700_timer_expired(void *vp)
+{
+ IB700State *s = vp;
+
+ ib700_debug("watchdog expired\n");
+
+ watchdog_perform_action();
+ qemu_del_timer(s->timer);
+}
+
+static const VMStateDescription vmstate_ib700 = {
+ .name = "ib700_wdt",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_TIMER(timer, IB700State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int wdt_ib700_init(ISADevice *dev)
+{
+ IB700State *s = DO_UPCAST(IB700State, dev, dev);
+
+ ib700_debug("watchdog init\n");
+
+ s->timer = qemu_new_timer(vm_clock, ib700_timer_expired, s);
+ register_ioport_write(0x441, 2, 1, ib700_write_disable_reg, s);
+ register_ioport_write(0x443, 2, 1, ib700_write_enable_reg, s);
+
+ return 0;
+}
+
+static void wdt_ib700_reset(DeviceState *dev)
+{
+ IB700State *s = DO_UPCAST(IB700State, dev.qdev, dev);
+
+ ib700_debug("watchdog reset\n");
+
+ qemu_del_timer(s->timer);
+}
+
+static WatchdogTimerModel model = {
+ .wdt_name = "ib700",
+ .wdt_description = "iBASE 700",
+};
+
+static ISADeviceInfo wdt_ib700_info = {
+ .qdev.name = "ib700",
+ .qdev.size = sizeof(IB700State),
+ .qdev.vmsd = &vmstate_ib700,
+ .qdev.reset = wdt_ib700_reset,
+ .init = wdt_ib700_init,
+};
+
+static void wdt_ib700_register_devices(void)
+{
+ watchdog_add_model(&model);
+ isa_qdev_register(&wdt_ib700_info);
+}
+
+device_init(wdt_ib700_register_devices);
diff --git a/hw/wm8750.c b/hw/wm8750.c
new file mode 100644
index 0000000..c9c6744
--- /dev/null
+++ b/hw/wm8750.c
@@ -0,0 +1,707 @@
+/*
+ * WM8750 audio CODEC.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This file is licensed under GNU GPL.
+ */
+
+#include "hw.h"
+#include "i2c.h"
+#include "audio/audio.h"
+
+#define IN_PORT_N 3
+#define OUT_PORT_N 3
+
+#define CODEC "wm8750"
+
+typedef struct {
+ int adc;
+ int adc_hz;
+ int dac;
+ int dac_hz;
+} WMRate;
+
+typedef struct {
+ i2c_slave i2c;
+ uint8_t i2c_data[2];
+ int i2c_len;
+ QEMUSoundCard card;
+ SWVoiceIn *adc_voice[IN_PORT_N];
+ SWVoiceOut *dac_voice[OUT_PORT_N];
+ int enable;
+ void (*data_req)(void *, int, int);
+ void *opaque;
+ uint8_t data_in[4096];
+ uint8_t data_out[4096];
+ int idx_in, req_in;
+ int idx_out, req_out;
+
+ SWVoiceOut **out[2];
+ uint8_t outvol[7], outmute[2];
+ SWVoiceIn **in[2];
+ uint8_t invol[4], inmute[2];
+
+ uint8_t diff[2], pol, ds, monomix[2], alc, mute;
+ uint8_t path[4], mpath[2], power, format;
+ const WMRate *rate;
+ uint8_t rate_vmstate;
+ int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
+} WM8750State;
+
+/* pow(10.0, -i / 20.0) * 255, i = 0..42 */
+static const uint8_t wm8750_vol_db_table[] = {
+ 255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45,
+ 40, 36, 32, 29, 26, 23, 20, 18, 16, 14, 13, 11, 10, 9, 8, 7, 6, 6, 5, 5,
+ 4, 4, 3, 3, 3, 2, 2
+};
+
+#define WM8750_OUTVOL_TRANSFORM(x) wm8750_vol_db_table[(0x7f - x) / 3]
+#define WM8750_INVOL_TRANSFORM(x) (x << 2)
+
+static inline void wm8750_in_load(WM8750State *s)
+{
+ if (s->idx_in + s->req_in <= sizeof(s->data_in))
+ return;
+ s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in);
+ AUD_read(*s->in[0], s->data_in + s->idx_in,
+ sizeof(s->data_in) - s->idx_in);
+}
+
+static inline void wm8750_out_flush(WM8750State *s)
+{
+ int sent = 0;
+ while (sent < s->idx_out)
+ sent += AUD_write(*s->out[0], s->data_out + sent, s->idx_out - sent)
+ ?: s->idx_out;
+ s->idx_out = 0;
+}
+
+static void wm8750_audio_in_cb(void *opaque, int avail_b)
+{
+ WM8750State *s = (WM8750State *) opaque;
+ s->req_in = avail_b;
+ s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2);
+}
+
+static void wm8750_audio_out_cb(void *opaque, int free_b)
+{
+ WM8750State *s = (WM8750State *) opaque;
+
+ if (s->idx_out >= free_b) {
+ s->idx_out = free_b;
+ s->req_out = 0;
+ wm8750_out_flush(s);
+ } else
+ s->req_out = free_b - s->idx_out;
+
+ s->data_req(s->opaque, s->req_out >> 2, s->req_in >> 2);
+}
+
+static const WMRate wm_rate_table[] = {
+ { 256, 48000, 256, 48000 }, /* SR: 00000 */
+ { 384, 48000, 384, 48000 }, /* SR: 00001 */
+ { 256, 48000, 1536, 8000 }, /* SR: 00010 */
+ { 384, 48000, 2304, 8000 }, /* SR: 00011 */
+ { 1536, 8000, 256, 48000 }, /* SR: 00100 */
+ { 2304, 8000, 384, 48000 }, /* SR: 00101 */
+ { 1536, 8000, 1536, 8000 }, /* SR: 00110 */
+ { 2304, 8000, 2304, 8000 }, /* SR: 00111 */
+ { 1024, 12000, 1024, 12000 }, /* SR: 01000 */
+ { 1526, 12000, 1536, 12000 }, /* SR: 01001 */
+ { 768, 16000, 768, 16000 }, /* SR: 01010 */
+ { 1152, 16000, 1152, 16000 }, /* SR: 01011 */
+ { 384, 32000, 384, 32000 }, /* SR: 01100 */
+ { 576, 32000, 576, 32000 }, /* SR: 01101 */
+ { 128, 96000, 128, 96000 }, /* SR: 01110 */
+ { 192, 96000, 192, 96000 }, /* SR: 01111 */
+ { 256, 44100, 256, 44100 }, /* SR: 10000 */
+ { 384, 44100, 384, 44100 }, /* SR: 10001 */
+ { 256, 44100, 1408, 8018 }, /* SR: 10010 */
+ { 384, 44100, 2112, 8018 }, /* SR: 10011 */
+ { 1408, 8018, 256, 44100 }, /* SR: 10100 */
+ { 2112, 8018, 384, 44100 }, /* SR: 10101 */
+ { 1408, 8018, 1408, 8018 }, /* SR: 10110 */
+ { 2112, 8018, 2112, 8018 }, /* SR: 10111 */
+ { 1024, 11025, 1024, 11025 }, /* SR: 11000 */
+ { 1536, 11025, 1536, 11025 }, /* SR: 11001 */
+ { 512, 22050, 512, 22050 }, /* SR: 11010 */
+ { 768, 22050, 768, 22050 }, /* SR: 11011 */
+ { 512, 24000, 512, 24000 }, /* SR: 11100 */
+ { 768, 24000, 768, 24000 }, /* SR: 11101 */
+ { 128, 88200, 128, 88200 }, /* SR: 11110 */
+ { 192, 88200, 192, 88200 }, /* SR: 11111 */
+};
+
+static void wm8750_vol_update(WM8750State *s)
+{
+ /* FIXME: multiply all volumes by s->invol[2], s->invol[3] */
+
+ AUD_set_volume_in(s->adc_voice[0], s->mute,
+ s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
+ s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
+ AUD_set_volume_in(s->adc_voice[1], s->mute,
+ s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
+ s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
+ AUD_set_volume_in(s->adc_voice[2], s->mute,
+ s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
+ s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
+
+ /* FIXME: multiply all volumes by s->outvol[0], s->outvol[1] */
+
+ /* Speaker: LOUT2VOL ROUT2VOL */
+ AUD_set_volume_out(s->dac_voice[0], s->mute,
+ s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[4]),
+ s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[5]));
+
+ /* Headphone: LOUT1VOL ROUT1VOL */
+ AUD_set_volume_out(s->dac_voice[1], s->mute,
+ s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[2]),
+ s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[3]));
+
+ /* MONOOUT: MONOVOL MONOVOL */
+ AUD_set_volume_out(s->dac_voice[2], s->mute,
+ s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]),
+ s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]));
+}
+
+static void wm8750_set_format(WM8750State *s)
+{
+ int i;
+ struct audsettings in_fmt;
+ struct audsettings out_fmt;
+
+ wm8750_out_flush(s);
+
+ if (s->in[0] && *s->in[0])
+ AUD_set_active_in(*s->in[0], 0);
+ if (s->out[0] && *s->out[0])
+ AUD_set_active_out(*s->out[0], 0);
+
+ for (i = 0; i < IN_PORT_N; i ++)
+ if (s->adc_voice[i]) {
+ AUD_close_in(&s->card, s->adc_voice[i]);
+ s->adc_voice[i] = NULL;
+ }
+ for (i = 0; i < OUT_PORT_N; i ++)
+ if (s->dac_voice[i]) {
+ AUD_close_out(&s->card, s->dac_voice[i]);
+ s->dac_voice[i] = NULL;
+ }
+
+ if (!s->enable)
+ return;
+
+ /* Setup input */
+ in_fmt.endianness = 0;
+ in_fmt.nchannels = 2;
+ in_fmt.freq = s->adc_hz;
+ in_fmt.fmt = AUD_FMT_S16;
+
+ s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
+ CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
+ s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1],
+ CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt);
+ s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2],
+ CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
+
+ /* Setup output */
+ out_fmt.endianness = 0;
+ out_fmt.nchannels = 2;
+ out_fmt.freq = s->dac_hz;
+ out_fmt.fmt = AUD_FMT_S16;
+
+ s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
+ CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
+ s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1],
+ CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt);
+ /* MONOMIX is also in stereo for simplicity */
+ s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2],
+ CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt);
+ /* no sense emulating OUT3 which is a mix of other outputs */
+
+ wm8750_vol_update(s);
+
+ /* We should connect the left and right channels to their
+ * respective inputs/outputs but we have completely no need
+ * for mixing or combining paths to different ports, so we
+ * connect both channels to where the left channel is routed. */
+ if (s->in[0] && *s->in[0])
+ AUD_set_active_in(*s->in[0], 1);
+ if (s->out[0] && *s->out[0])
+ AUD_set_active_out(*s->out[0], 1);
+}
+
+static void wm8750_clk_update(WM8750State *s, int ext)
+{
+ if (s->master || !s->ext_dac_hz)
+ s->dac_hz = s->rate->dac_hz;
+ else
+ s->dac_hz = s->ext_dac_hz;
+
+ if (s->master || !s->ext_adc_hz)
+ s->adc_hz = s->rate->adc_hz;
+ else
+ s->adc_hz = s->ext_adc_hz;
+
+ if (s->master || (!s->ext_dac_hz && !s->ext_adc_hz)) {
+ if (!ext)
+ wm8750_set_format(s);
+ } else {
+ if (ext)
+ wm8750_set_format(s);
+ }
+}
+
+static void wm8750_reset(i2c_slave *i2c)
+{
+ WM8750State *s = (WM8750State *) i2c;
+ s->rate = &wm_rate_table[0];
+ s->enable = 0;
+ wm8750_clk_update(s, 1);
+ s->diff[0] = 0;
+ s->diff[1] = 0;
+ s->ds = 0;
+ s->alc = 0;
+ s->in[0] = &s->adc_voice[0];
+ s->invol[0] = 0x17;
+ s->invol[1] = 0x17;
+ s->invol[2] = 0xc3;
+ s->invol[3] = 0xc3;
+ s->out[0] = &s->dac_voice[0];
+ s->outvol[0] = 0xff;
+ s->outvol[1] = 0xff;
+ s->outvol[2] = 0x79;
+ s->outvol[3] = 0x79;
+ s->outvol[4] = 0x79;
+ s->outvol[5] = 0x79;
+ s->outvol[6] = 0x79;
+ s->inmute[0] = 0;
+ s->inmute[1] = 0;
+ s->outmute[0] = 0;
+ s->outmute[1] = 0;
+ s->mute = 1;
+ s->path[0] = 0;
+ s->path[1] = 0;
+ s->path[2] = 0;
+ s->path[3] = 0;
+ s->mpath[0] = 0;
+ s->mpath[1] = 0;
+ s->format = 0x0a;
+ s->idx_in = sizeof(s->data_in);
+ s->req_in = 0;
+ s->idx_out = 0;
+ s->req_out = 0;
+ wm8750_vol_update(s);
+ s->i2c_len = 0;
+}
+
+static void wm8750_event(i2c_slave *i2c, enum i2c_event event)
+{
+ WM8750State *s = (WM8750State *) i2c;
+
+ switch (event) {
+ case I2C_START_SEND:
+ s->i2c_len = 0;
+ break;
+ case I2C_FINISH:
+#ifdef VERBOSE
+ if (s->i2c_len < 2)
+ printf("%s: message too short (%i bytes)\n",
+ __FUNCTION__, s->i2c_len);
+#endif
+ break;
+ default:
+ break;
+ }
+}
+
+#define WM8750_LINVOL 0x00
+#define WM8750_RINVOL 0x01
+#define WM8750_LOUT1V 0x02
+#define WM8750_ROUT1V 0x03
+#define WM8750_ADCDAC 0x05
+#define WM8750_IFACE 0x07
+#define WM8750_SRATE 0x08
+#define WM8750_LDAC 0x0a
+#define WM8750_RDAC 0x0b
+#define WM8750_BASS 0x0c
+#define WM8750_TREBLE 0x0d
+#define WM8750_RESET 0x0f
+#define WM8750_3D 0x10
+#define WM8750_ALC1 0x11
+#define WM8750_ALC2 0x12
+#define WM8750_ALC3 0x13
+#define WM8750_NGATE 0x14
+#define WM8750_LADC 0x15
+#define WM8750_RADC 0x16
+#define WM8750_ADCTL1 0x17
+#define WM8750_ADCTL2 0x18
+#define WM8750_PWR1 0x19
+#define WM8750_PWR2 0x1a
+#define WM8750_ADCTL3 0x1b
+#define WM8750_ADCIN 0x1f
+#define WM8750_LADCIN 0x20
+#define WM8750_RADCIN 0x21
+#define WM8750_LOUTM1 0x22
+#define WM8750_LOUTM2 0x23
+#define WM8750_ROUTM1 0x24
+#define WM8750_ROUTM2 0x25
+#define WM8750_MOUTM1 0x26
+#define WM8750_MOUTM2 0x27
+#define WM8750_LOUT2V 0x28
+#define WM8750_ROUT2V 0x29
+#define WM8750_MOUTV 0x2a
+
+static int wm8750_tx(i2c_slave *i2c, uint8_t data)
+{
+ WM8750State *s = (WM8750State *) i2c;
+ uint8_t cmd;
+ uint16_t value;
+
+ if (s->i2c_len >= 2) {
+ printf("%s: long message (%i bytes)\n", __FUNCTION__, s->i2c_len);
+#ifdef VERBOSE
+ return 1;
+#endif
+ }
+ s->i2c_data[s->i2c_len ++] = data;
+ if (s->i2c_len != 2)
+ return 0;
+
+ cmd = s->i2c_data[0] >> 1;
+ value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff;
+
+ switch (cmd) {
+ case WM8750_LADCIN: /* ADC Signal Path Control (Left) */
+ s->diff[0] = (((value >> 6) & 3) == 3); /* LINSEL */
+ if (s->diff[0])
+ s->in[0] = &s->adc_voice[0 + s->ds * 1];
+ else
+ s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
+ break;
+
+ case WM8750_RADCIN: /* ADC Signal Path Control (Right) */
+ s->diff[1] = (((value >> 6) & 3) == 3); /* RINSEL */
+ if (s->diff[1])
+ s->in[1] = &s->adc_voice[0 + s->ds * 1];
+ else
+ s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
+ break;
+
+ case WM8750_ADCIN: /* ADC Input Mode */
+ s->ds = (value >> 8) & 1; /* DS */
+ if (s->diff[0])
+ s->in[0] = &s->adc_voice[0 + s->ds * 1];
+ if (s->diff[1])
+ s->in[1] = &s->adc_voice[0 + s->ds * 1];
+ s->monomix[0] = (value >> 6) & 3; /* MONOMIX */
+ break;
+
+ case WM8750_ADCTL1: /* Additional Control (1) */
+ s->monomix[1] = (value >> 1) & 1; /* DMONOMIX */
+ break;
+
+ case WM8750_PWR1: /* Power Management (1) */
+ s->enable = ((value >> 6) & 7) == 3; /* VMIDSEL, VREF */
+ wm8750_set_format(s);
+ break;
+
+ case WM8750_LINVOL: /* Left Channel PGA */
+ s->invol[0] = value & 0x3f; /* LINVOL */
+ s->inmute[0] = (value >> 7) & 1; /* LINMUTE */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_RINVOL: /* Right Channel PGA */
+ s->invol[1] = value & 0x3f; /* RINVOL */
+ s->inmute[1] = (value >> 7) & 1; /* RINMUTE */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ADCDAC: /* ADC and DAC Control */
+ s->pol = (value >> 5) & 3; /* ADCPOL */
+ s->mute = (value >> 3) & 1; /* DACMU */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ADCTL3: /* Additional Control (3) */
+ break;
+
+ case WM8750_LADC: /* Left ADC Digital Volume */
+ s->invol[2] = value & 0xff; /* LADCVOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_RADC: /* Right ADC Digital Volume */
+ s->invol[3] = value & 0xff; /* RADCVOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ALC1: /* ALC Control (1) */
+ s->alc = (value >> 7) & 3; /* ALCSEL */
+ break;
+
+ case WM8750_NGATE: /* Noise Gate Control */
+ case WM8750_3D: /* 3D enhance */
+ break;
+
+ case WM8750_LDAC: /* Left Channel Digital Volume */
+ s->outvol[0] = value & 0xff; /* LDACVOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_RDAC: /* Right Channel Digital Volume */
+ s->outvol[1] = value & 0xff; /* RDACVOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_BASS: /* Bass Control */
+ break;
+
+ case WM8750_LOUTM1: /* Left Mixer Control (1) */
+ s->path[0] = (value >> 8) & 1; /* LD2LO */
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_LOUTM2: /* Left Mixer Control (2) */
+ s->path[1] = (value >> 8) & 1; /* RD2LO */
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ROUTM1: /* Right Mixer Control (1) */
+ s->path[2] = (value >> 8) & 1; /* LD2RO */
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ROUTM2: /* Right Mixer Control (2) */
+ s->path[3] = (value >> 8) & 1; /* RD2RO */
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_MOUTM1: /* Mono Mixer Control (1) */
+ s->mpath[0] = (value >> 8) & 1; /* LD2MO */
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_MOUTM2: /* Mono Mixer Control (2) */
+ s->mpath[1] = (value >> 8) & 1; /* RD2MO */
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_LOUT1V: /* LOUT1 Volume */
+ s->outvol[2] = value & 0x7f; /* LOUT1VOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_LOUT2V: /* LOUT2 Volume */
+ s->outvol[4] = value & 0x7f; /* LOUT2VOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ROUT1V: /* ROUT1 Volume */
+ s->outvol[3] = value & 0x7f; /* ROUT1VOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ROUT2V: /* ROUT2 Volume */
+ s->outvol[5] = value & 0x7f; /* ROUT2VOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_MOUTV: /* MONOOUT Volume */
+ s->outvol[6] = value & 0x7f; /* MONOOUTVOL */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_ADCTL2: /* Additional Control (2) */
+ break;
+
+ case WM8750_PWR2: /* Power Management (2) */
+ s->power = value & 0x7e;
+ /* TODO: mute/unmute respective paths */
+ wm8750_vol_update(s);
+ break;
+
+ case WM8750_IFACE: /* Digital Audio Interface Format */
+ s->format = value;
+ s->master = (value >> 6) & 1; /* MS */
+ wm8750_clk_update(s, s->master);
+ break;
+
+ case WM8750_SRATE: /* Clocking and Sample Rate Control */
+ s->rate = &wm_rate_table[(value >> 1) & 0x1f];
+ wm8750_clk_update(s, 0);
+ break;
+
+ case WM8750_RESET: /* Reset */
+ wm8750_reset(&s->i2c);
+ break;
+
+#ifdef VERBOSE
+ default:
+ printf("%s: unknown register %02x\n", __FUNCTION__, cmd);
+#endif
+ }
+
+ return 0;
+}
+
+static int wm8750_rx(i2c_slave *i2c)
+{
+ return 0x00;
+}
+
+static void wm8750_pre_save(void *opaque)
+{
+ WM8750State *s = opaque;
+
+ s->rate_vmstate = (s->rate - wm_rate_table) / sizeof(*s->rate);
+}
+
+static int wm8750_post_load(void *opaque, int version_id)
+{
+ WM8750State *s = opaque;
+
+ s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
+ return 0;
+}
+
+static const VMStateDescription vmstate_wm8750 = {
+ .name = CODEC,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .pre_save = wm8750_pre_save,
+ .post_load = wm8750_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT8_ARRAY(i2c_data, WM8750State, 2),
+ VMSTATE_INT32(i2c_len, WM8750State),
+ VMSTATE_INT32(enable, WM8750State),
+ VMSTATE_INT32(idx_in, WM8750State),
+ VMSTATE_INT32(req_in, WM8750State),
+ VMSTATE_INT32(idx_out, WM8750State),
+ VMSTATE_INT32(req_out, WM8750State),
+ VMSTATE_UINT8_ARRAY(outvol, WM8750State, 7),
+ VMSTATE_UINT8_ARRAY(outmute, WM8750State, 2),
+ VMSTATE_UINT8_ARRAY(invol, WM8750State, 4),
+ VMSTATE_UINT8_ARRAY(inmute, WM8750State, 2),
+ VMSTATE_UINT8_ARRAY(diff, WM8750State, 2),
+ VMSTATE_UINT8(pol, WM8750State),
+ VMSTATE_UINT8(ds, WM8750State),
+ VMSTATE_UINT8_ARRAY(monomix, WM8750State, 2),
+ VMSTATE_UINT8(alc, WM8750State),
+ VMSTATE_UINT8(mute, WM8750State),
+ VMSTATE_UINT8_ARRAY(path, WM8750State, 4),
+ VMSTATE_UINT8_ARRAY(mpath, WM8750State, 2),
+ VMSTATE_UINT8(format, WM8750State),
+ VMSTATE_UINT8(power, WM8750State),
+ VMSTATE_UINT8(rate_vmstate, WM8750State),
+ VMSTATE_I2C_SLAVE(i2c, WM8750State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int wm8750_init(i2c_slave *i2c)
+{
+ WM8750State *s = FROM_I2C_SLAVE(WM8750State, i2c);
+
+ AUD_register_card(CODEC, &s->card);
+ wm8750_reset(&s->i2c);
+
+ return 0;
+}
+
+#if 0
+static void wm8750_fini(i2c_slave *i2c)
+{
+ WM8750State *s = (WM8750State *) i2c;
+ wm8750_reset(&s->i2c);
+ AUD_remove_card(&s->card);
+ qemu_free(s);
+}
+#endif
+
+void wm8750_data_req_set(DeviceState *dev,
+ void (*data_req)(void *, int, int), void *opaque)
+{
+ WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE_FROM_QDEV(dev));
+ s->data_req = data_req;
+ s->opaque = opaque;
+}
+
+void wm8750_dac_dat(void *opaque, uint32_t sample)
+{
+ WM8750State *s = (WM8750State *) opaque;
+
+ *(uint32_t *) &s->data_out[s->idx_out] = sample;
+ s->req_out -= 4;
+ s->idx_out += 4;
+ if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0)
+ wm8750_out_flush(s);
+}
+
+void *wm8750_dac_buffer(void *opaque, int samples)
+{
+ WM8750State *s = (WM8750State *) opaque;
+ /* XXX: Should check if there are <i>samples</i> free samples available */
+ void *ret = s->data_out + s->idx_out;
+
+ s->idx_out += samples << 2;
+ s->req_out -= samples << 2;
+ return ret;
+}
+
+void wm8750_dac_commit(void *opaque)
+{
+ WM8750State *s = (WM8750State *) opaque;
+
+ wm8750_out_flush(s);
+}
+
+uint32_t wm8750_adc_dat(void *opaque)
+{
+ WM8750State *s = (WM8750State *) opaque;
+ uint32_t *data;
+
+ if (s->idx_in >= sizeof(s->data_in))
+ wm8750_in_load(s);
+
+ data = (uint32_t *) &s->data_in[s->idx_in];
+ s->req_in -= 4;
+ s->idx_in += 4;
+ return *data;
+}
+
+void wm8750_set_bclk_in(void *opaque, int new_hz)
+{
+ WM8750State *s = (WM8750State *) opaque;
+
+ s->ext_adc_hz = new_hz;
+ s->ext_dac_hz = new_hz;
+ wm8750_clk_update(s, 1);
+}
+
+static I2CSlaveInfo wm8750_info = {
+ .qdev.name = "wm8750",
+ .qdev.size = sizeof(WM8750State),
+ .qdev.vmsd = &vmstate_wm8750,
+ .init = wm8750_init,
+ .event = wm8750_event,
+ .recv = wm8750_rx,
+ .send = wm8750_tx
+};
+
+static void wm8750_register_devices(void)
+{
+ i2c_register_slave(&wm8750_info);
+}
+
+device_init(wm8750_register_devices)
diff --git a/hw/xen.h b/hw/xen.h
new file mode 100644
index 0000000..780dcf7
--- /dev/null
+++ b/hw/xen.h
@@ -0,0 +1,21 @@
+#ifndef QEMU_HW_XEN_H
+#define QEMU_HW_XEN_H 1
+/*
+ * public xen header
+ * stuff needed outside xen-*.c, i.e. interfaces to qemu.
+ * must not depend on any xen headers being present in
+ * /usr/include/xen, so it can be included unconditionally.
+ */
+#include <inttypes.h>
+
+/* xen-machine.c */
+enum xen_mode {
+ XEN_EMULATE = 0, // xen emulation, using xenner (default)
+ XEN_CREATE, // create xen domain
+ XEN_ATTACH // attach to xen domain created by xend
+};
+
+extern uint32_t xen_domid;
+extern enum xen_mode xen_mode;
+
+#endif /* QEMU_HW_XEN_H */
diff --git a/hw/xen_backend.c b/hw/xen_backend.c
new file mode 100644
index 0000000..a2e408f
--- /dev/null
+++ b/hw/xen_backend.c
@@ -0,0 +1,714 @@
+/*
+ * xen backend driver infrastructure
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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: add some xenbus / xenstore concepts overview here.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/signal.h>
+
+#include <xs.h>
+#include <xenctrl.h>
+#include <xen/grant_table.h>
+
+#include "hw.h"
+#include "qemu-char.h"
+#include "qemu-log.h"
+#include "xen_backend.h"
+
+/* ------------------------------------------------------------- */
+
+/* public */
+int xen_xc;
+struct xs_handle *xenstore = NULL;
+const char *xen_protocol;
+
+/* private */
+static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs);
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+int xenstore_write_str(const char *base, const char *node, const char *val)
+{
+ char abspath[XEN_BUFSIZE];
+
+ snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
+ if (!xs_write(xenstore, 0, abspath, val, strlen(val)))
+ return -1;
+ return 0;
+}
+
+char *xenstore_read_str(const char *base, const char *node)
+{
+ char abspath[XEN_BUFSIZE];
+ unsigned int len;
+ char *str, *ret = NULL;
+
+ snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
+ str = xs_read(xenstore, 0, abspath, &len);
+ if (str != NULL) {
+ /* move to qemu-allocated memory to make sure
+ * callers can savely qemu_free() stuff. */
+ ret = qemu_strdup(str);
+ free(str);
+ }
+ return ret;
+}
+
+int xenstore_write_int(const char *base, const char *node, int ival)
+{
+ char val[32];
+
+ snprintf(val, sizeof(val), "%d", ival);
+ return xenstore_write_str(base, node, val);
+}
+
+int xenstore_read_int(const char *base, const char *node, int *ival)
+{
+ char *val;
+ int rc = -1;
+
+ val = xenstore_read_str(base, node);
+ if (val && 1 == sscanf(val, "%d", ival))
+ rc = 0;
+ qemu_free(val);
+ return rc;
+}
+
+int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val)
+{
+ return xenstore_write_str(xendev->be, node, val);
+}
+
+int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival)
+{
+ return xenstore_write_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_be_str(struct XenDevice *xendev, const char *node)
+{
+ return xenstore_read_str(xendev->be, node);
+}
+
+int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival)
+{
+ return xenstore_read_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
+{
+ return xenstore_read_str(xendev->fe, node);
+}
+
+int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival)
+{
+ return xenstore_read_int(xendev->fe, node, ival);
+}
+
+/* ------------------------------------------------------------- */
+
+const char *xenbus_strstate(enum xenbus_state state)
+{
+ static const char *const name[] = {
+ [ XenbusStateUnknown ] = "Unknown",
+ [ XenbusStateInitialising ] = "Initialising",
+ [ XenbusStateInitWait ] = "InitWait",
+ [ XenbusStateInitialised ] = "Initialised",
+ [ XenbusStateConnected ] = "Connected",
+ [ XenbusStateClosing ] = "Closing",
+ [ XenbusStateClosed ] = "Closed",
+ };
+ return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID";
+}
+
+int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
+{
+ int rc;
+
+ rc = xenstore_write_be_int(xendev, "state", state);
+ if (rc < 0)
+ return rc;
+ xen_be_printf(xendev, 1, "backend state: %s -> %s\n",
+ xenbus_strstate(xendev->be_state), xenbus_strstate(state));
+ xendev->be_state = state;
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev)
+{
+ struct XenDevice *xendev;
+
+ QTAILQ_FOREACH(xendev, &xendevs, next) {
+ if (xendev->dom != dom)
+ continue;
+ if (xendev->dev != dev)
+ continue;
+ if (strcmp(xendev->type, type) != 0)
+ continue;
+ return xendev;
+ }
+ return NULL;
+}
+
+/*
+ * get xen backend device, allocate a new one if it doesn't exist.
+ */
+static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
+ struct XenDevOps *ops)
+{
+ struct XenDevice *xendev;
+ char *dom0;
+
+ xendev = xen_be_find_xendev(type, dom, dev);
+ if (xendev)
+ return xendev;
+
+ /* init new xendev */
+ xendev = qemu_mallocz(ops->size);
+ xendev->type = type;
+ xendev->dom = dom;
+ xendev->dev = dev;
+ xendev->ops = ops;
+
+ dom0 = xs_get_domain_path(xenstore, 0);
+ snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d",
+ dom0, xendev->type, xendev->dom, xendev->dev);
+ snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
+ xendev->type, xendev->dev);
+ free(dom0);
+
+ xendev->debug = debug;
+ xendev->local_port = -1;
+
+ xendev->evtchndev = xc_evtchn_open();
+ if (xendev->evtchndev < 0) {
+ xen_be_printf(NULL, 0, "can't open evtchn device\n");
+ qemu_free(xendev);
+ return NULL;
+ }
+ fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC);
+
+ if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
+ xendev->gnttabdev = xc_gnttab_open();
+ if (xendev->gnttabdev < 0) {
+ xen_be_printf(NULL, 0, "can't open gnttab device\n");
+ xc_evtchn_close(xendev->evtchndev);
+ qemu_free(xendev);
+ return NULL;
+ }
+ } else {
+ xendev->gnttabdev = -1;
+ }
+
+ QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
+
+ if (xendev->ops->alloc)
+ xendev->ops->alloc(xendev);
+
+ return xendev;
+}
+
+/*
+ * release xen backend device.
+ */
+static struct XenDevice *xen_be_del_xendev(int dom, int dev)
+{
+ struct XenDevice *xendev, *xnext;
+
+ /*
+ * This is pretty much like QTAILQ_FOREACH(xendev, &xendevs, next) but
+ * we save the next pointer in xnext because we might free xendev.
+ */
+ xnext = xendevs.tqh_first;
+ while (xnext) {
+ xendev = xnext;
+ xnext = xendev->next.tqe_next;
+
+ if (xendev->dom != dom)
+ continue;
+ if (xendev->dev != dev && dev != -1)
+ continue;
+
+ if (xendev->ops->free)
+ xendev->ops->free(xendev);
+
+ if (xendev->fe) {
+ char token[XEN_BUFSIZE];
+ snprintf(token, sizeof(token), "fe:%p", xendev);
+ xs_unwatch(xenstore, xendev->fe, token);
+ qemu_free(xendev->fe);
+ }
+
+ if (xendev->evtchndev >= 0)
+ xc_evtchn_close(xendev->evtchndev);
+ if (xendev->gnttabdev >= 0)
+ xc_gnttab_close(xendev->gnttabdev);
+
+ QTAILQ_REMOVE(&xendevs, xendev, next);
+ qemu_free(xendev);
+ }
+ return NULL;
+}
+
+/*
+ * Sync internal data structures on xenstore updates.
+ * Node specifies the changed field. node = NULL means
+ * update all fields (used for initialization).
+ */
+static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
+{
+ if (node == NULL || strcmp(node, "online") == 0) {
+ if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1)
+ xendev->online = 0;
+ }
+
+ if (node) {
+ xen_be_printf(xendev, 2, "backend update: %s\n", node);
+ if (xendev->ops->backend_changed)
+ xendev->ops->backend_changed(xendev, node);
+ }
+}
+
+static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
+{
+ int fe_state;
+
+ if (node == NULL || strcmp(node, "state") == 0) {
+ if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1)
+ fe_state = XenbusStateUnknown;
+ if (xendev->fe_state != fe_state)
+ xen_be_printf(xendev, 1, "frontend state: %s -> %s\n",
+ xenbus_strstate(xendev->fe_state),
+ xenbus_strstate(fe_state));
+ xendev->fe_state = fe_state;
+ }
+ if (node == NULL || strcmp(node, "protocol") == 0) {
+ qemu_free(xendev->protocol);
+ xendev->protocol = xenstore_read_fe_str(xendev, "protocol");
+ if (xendev->protocol)
+ xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol);
+ }
+
+ if (node) {
+ xen_be_printf(xendev, 2, "frontend update: %s\n", node);
+ if (xendev->ops->frontend_changed)
+ xendev->ops->frontend_changed(xendev, node);
+ }
+}
+
+/* ------------------------------------------------------------- */
+/* Check for possible state transitions and perform them. */
+
+/*
+ * Initial xendev setup. Read frontend path, register watch for it.
+ * Should succeed once xend finished setting up the backend device.
+ *
+ * Also sets initial state (-> Initializing) when done. Which
+ * only affects the xendev->be_state variable as xenbus should
+ * already be put into that state by xend.
+ */
+static int xen_be_try_setup(struct XenDevice *xendev)
+{
+ char token[XEN_BUFSIZE];
+ int be_state;
+
+ if (xenstore_read_be_int(xendev, "state", &be_state) == -1) {
+ xen_be_printf(xendev, 0, "reading backend state failed\n");
+ return -1;
+ }
+
+ if (be_state != XenbusStateInitialising) {
+ xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n",
+ xenbus_strstate(be_state));
+ return -1;
+ }
+
+ xendev->fe = xenstore_read_be_str(xendev, "frontend");
+ if (xendev->fe == NULL) {
+ xen_be_printf(xendev, 0, "reading frontend path failed\n");
+ return -1;
+ }
+
+ /* setup frontend watch */
+ snprintf(token, sizeof(token), "fe:%p", xendev);
+ if (!xs_watch(xenstore, xendev->fe, token)) {
+ xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n",
+ xendev->fe);
+ return -1;
+ }
+ xen_be_set_state(xendev, XenbusStateInitialising);
+
+ xen_be_backend_changed(xendev, NULL);
+ xen_be_frontend_changed(xendev, NULL);
+ return 0;
+}
+
+/*
+ * Try initialize xendev. Prepare everything the backend can do
+ * without synchronizing with the frontend. Fakes hotplug-status. No
+ * hotplug involved here because this is about userspace drivers, thus
+ * there are kernel backend devices which could invoke hotplug.
+ *
+ * Goes to InitWait on success.
+ */
+static int xen_be_try_init(struct XenDevice *xendev)
+{
+ int rc = 0;
+
+ if (!xendev->online) {
+ xen_be_printf(xendev, 1, "not online\n");
+ return -1;
+ }
+
+ if (xendev->ops->init)
+ rc = xendev->ops->init(xendev);
+ if (rc != 0) {
+ xen_be_printf(xendev, 1, "init() failed\n");
+ return rc;
+ }
+
+ xenstore_write_be_str(xendev, "hotplug-status", "connected");
+ xen_be_set_state(xendev, XenbusStateInitWait);
+ return 0;
+}
+
+/*
+ * Try to connect xendev. Depends on the frontend being ready
+ * for it (shared ring and evtchn info in xenstore, state being
+ * Initialised or Connected).
+ *
+ * Goes to Connected on success.
+ */
+static int xen_be_try_connect(struct XenDevice *xendev)
+{
+ int rc = 0;
+
+ if (xendev->fe_state != XenbusStateInitialised &&
+ xendev->fe_state != XenbusStateConnected) {
+ if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) {
+ xen_be_printf(xendev, 2, "frontend not ready, ignoring\n");
+ } else {
+ xen_be_printf(xendev, 2, "frontend not ready (yet)\n");
+ return -1;
+ }
+ }
+
+ if (xendev->ops->connect)
+ rc = xendev->ops->connect(xendev);
+ if (rc != 0) {
+ xen_be_printf(xendev, 0, "connect() failed\n");
+ return rc;
+ }
+
+ xen_be_set_state(xendev, XenbusStateConnected);
+ return 0;
+}
+
+/*
+ * Teardown connection.
+ *
+ * Goes to Closed when done.
+ */
+static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
+{
+ if (xendev->be_state != XenbusStateClosing &&
+ xendev->be_state != XenbusStateClosed &&
+ xendev->ops->disconnect)
+ xendev->ops->disconnect(xendev);
+ if (xendev->be_state != state)
+ xen_be_set_state(xendev, state);
+}
+
+/*
+ * Try to reset xendev, for reconnection by another frontend instance.
+ */
+static int xen_be_try_reset(struct XenDevice *xendev)
+{
+ if (xendev->fe_state != XenbusStateInitialising)
+ return -1;
+
+ xen_be_printf(xendev, 1, "device reset (for re-connect)\n");
+ xen_be_set_state(xendev, XenbusStateInitialising);
+ return 0;
+}
+
+/*
+ * state change dispatcher function
+ */
+void xen_be_check_state(struct XenDevice *xendev)
+{
+ int rc = 0;
+
+ /* frontend may request shutdown from almost anywhere */
+ if (xendev->fe_state == XenbusStateClosing ||
+ xendev->fe_state == XenbusStateClosed) {
+ xen_be_disconnect(xendev, xendev->fe_state);
+ return;
+ }
+
+ /* check for possible backend state transitions */
+ for (;;) {
+ switch (xendev->be_state) {
+ case XenbusStateUnknown:
+ rc = xen_be_try_setup(xendev);
+ break;
+ case XenbusStateInitialising:
+ rc = xen_be_try_init(xendev);
+ break;
+ case XenbusStateInitWait:
+ rc = xen_be_try_connect(xendev);
+ break;
+ case XenbusStateClosed:
+ rc = xen_be_try_reset(xendev);
+ break;
+ default:
+ rc = -1;
+ }
+ if (rc != 0)
+ break;
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
+{
+ struct XenDevice *xendev;
+ char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
+ char **dev = NULL, *dom0;
+ unsigned int cdev, j;
+
+ /* setup watch */
+ dom0 = xs_get_domain_path(xenstore, 0);
+ snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
+ snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
+ free(dom0);
+ if (!xs_watch(xenstore, path, token)) {
+ xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path);
+ return -1;
+ }
+
+ /* look for backends */
+ dev = xs_directory(xenstore, 0, path, &cdev);
+ if (!dev)
+ return 0;
+ for (j = 0; j < cdev; j++) {
+ xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops);
+ if (xendev == NULL)
+ continue;
+ xen_be_check_state(xendev);
+ }
+ free(dev);
+ return 0;
+}
+
+static void xenstore_update_be(char *watch, char *type, int dom,
+ struct XenDevOps *ops)
+{
+ struct XenDevice *xendev;
+ char path[XEN_BUFSIZE], *dom0;
+ unsigned int len, dev;
+
+ dom0 = xs_get_domain_path(xenstore, 0);
+ len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
+ free(dom0);
+ if (strncmp(path, watch, len) != 0)
+ return;
+ if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) {
+ strcpy(path, "");
+ if (sscanf(watch+len, "/%u", &dev) != 1)
+ dev = -1;
+ }
+ if (dev == -1)
+ return;
+
+ if (0) {
+ /* FIXME: detect devices being deleted from xenstore ... */
+ xen_be_del_xendev(dom, dev);
+ }
+
+ xendev = xen_be_get_xendev(type, dom, dev, ops);
+ if (xendev != NULL) {
+ xen_be_backend_changed(xendev, path);
+ xen_be_check_state(xendev);
+ }
+}
+
+static void xenstore_update_fe(char *watch, struct XenDevice *xendev)
+{
+ char *node;
+ unsigned int len;
+
+ len = strlen(xendev->fe);
+ if (strncmp(xendev->fe, watch, len) != 0)
+ return;
+ if (watch[len] != '/')
+ return;
+ node = watch + len + 1;
+
+ xen_be_frontend_changed(xendev, node);
+ xen_be_check_state(xendev);
+}
+
+static void xenstore_update(void *unused)
+{
+ char **vec = NULL;
+ intptr_t type, ops, ptr;
+ unsigned int dom, count;
+
+ vec = xs_read_watch(xenstore, &count);
+ if (vec == NULL)
+ goto cleanup;
+
+ if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR,
+ &type, &dom, &ops) == 3)
+ xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops);
+ if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1)
+ xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr);
+
+cleanup:
+ free(vec);
+}
+
+static void xen_be_evtchn_event(void *opaque)
+{
+ struct XenDevice *xendev = opaque;
+ evtchn_port_t port;
+
+ port = xc_evtchn_pending(xendev->evtchndev);
+ if (port != xendev->local_port) {
+ xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n",
+ port, xendev->local_port);
+ return;
+ }
+ xc_evtchn_unmask(xendev->evtchndev, port);
+
+ if (xendev->ops->event)
+ xendev->ops->event(xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+int xen_be_init(void)
+{
+ xenstore = xs_daemon_open();
+ if (!xenstore) {
+ xen_be_printf(NULL, 0, "can't connect to xenstored\n");
+ return -1;
+ }
+
+ if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0)
+ goto err;
+
+ xen_xc = xc_interface_open();
+ if (xen_xc == -1) {
+ xen_be_printf(NULL, 0, "can't open xen interface\n");
+ goto err;
+ }
+ return 0;
+
+err:
+ qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL);
+ xs_daemon_close(xenstore);
+ xenstore = NULL;
+
+ return -1;
+}
+
+int xen_be_register(const char *type, struct XenDevOps *ops)
+{
+ return xenstore_scan(type, xen_domid, ops);
+}
+
+int xen_be_bind_evtchn(struct XenDevice *xendev)
+{
+ if (xendev->local_port != -1)
+ return 0;
+ xendev->local_port = xc_evtchn_bind_interdomain
+ (xendev->evtchndev, xendev->dom, xendev->remote_port);
+ if (xendev->local_port == -1) {
+ xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n");
+ return -1;
+ }
+ xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
+ qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
+ xen_be_evtchn_event, NULL, xendev);
+ return 0;
+}
+
+void xen_be_unbind_evtchn(struct XenDevice *xendev)
+{
+ if (xendev->local_port == -1)
+ return;
+ qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
+ xc_evtchn_unbind(xendev->evtchndev, xendev->local_port);
+ xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port);
+ xendev->local_port = -1;
+}
+
+int xen_be_send_notify(struct XenDevice *xendev)
+{
+ return xc_evtchn_notify(xendev->evtchndev, xendev->local_port);
+}
+
+/*
+ * msg_level:
+ * 0 == errors (stderr + logfile).
+ * 1 == informative debug messages (logfile only).
+ * 2 == noisy debug messages (logfile only).
+ * 3 == will flood your log (logfile only).
+ */
+void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...)
+{
+ va_list args;
+
+ if (xendev) {
+ if (msg_level > xendev->debug)
+ return;
+ qemu_log("xen be: %s: ", xendev->name);
+ if (msg_level == 0)
+ fprintf(stderr, "xen be: %s: ", xendev->name);
+ } else {
+ if (msg_level > debug)
+ return;
+ qemu_log("xen be core: ");
+ if (msg_level == 0)
+ fprintf(stderr, "xen be core: ");
+ }
+ va_start(args, fmt);
+ qemu_log_vprintf(fmt, args);
+ va_end(args);
+ if (msg_level == 0) {
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+ qemu_log_flush();
+}
diff --git a/hw/xen_backend.h b/hw/xen_backend.h
new file mode 100644
index 0000000..1b428e3
--- /dev/null
+++ b/hw/xen_backend.h
@@ -0,0 +1,106 @@
+#ifndef QEMU_HW_XEN_BACKEND_H
+#define QEMU_HW_XEN_BACKEND_H 1
+
+#include "xen_common.h"
+#include "sysemu.h"
+#include "net.h"
+
+/* ------------------------------------------------------------- */
+
+#define XEN_BUFSIZE 1024
+
+struct XenDevice;
+
+/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */
+#define DEVOPS_FLAG_NEED_GNTDEV 1
+/* don't expect frontend doing correct state transitions (aka console quirk) */
+#define DEVOPS_FLAG_IGNORE_STATE 2
+
+struct XenDevOps {
+ size_t size;
+ uint32_t flags;
+ void (*alloc)(struct XenDevice *xendev);
+ int (*init)(struct XenDevice *xendev);
+ int (*connect)(struct XenDevice *xendev);
+ void (*event)(struct XenDevice *xendev);
+ void (*disconnect)(struct XenDevice *xendev);
+ int (*free)(struct XenDevice *xendev);
+ void (*backend_changed)(struct XenDevice *xendev, const char *node);
+ void (*frontend_changed)(struct XenDevice *xendev, const char *node);
+};
+
+struct XenDevice {
+ const char *type;
+ int dom;
+ int dev;
+ char name[64];
+ int debug;
+
+ enum xenbus_state be_state;
+ enum xenbus_state fe_state;
+ int online;
+ char be[XEN_BUFSIZE];
+ char *fe;
+ char *protocol;
+ int remote_port;
+ int local_port;
+
+ int evtchndev;
+ int gnttabdev;
+
+ struct XenDevOps *ops;
+ QTAILQ_ENTRY(XenDevice) next;
+};
+
+/* ------------------------------------------------------------- */
+
+/* variables */
+extern int xen_xc;
+extern struct xs_handle *xenstore;
+extern const char *xen_protocol;
+
+/* xenstore helper functions */
+int xenstore_write_str(const char *base, const char *node, const char *val);
+int xenstore_write_int(const char *base, const char *node, int ival);
+char *xenstore_read_str(const char *base, const char *node);
+int xenstore_read_int(const char *base, const char *node, int *ival);
+
+int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val);
+int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival);
+char *xenstore_read_be_str(struct XenDevice *xendev, const char *node);
+int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival);
+char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node);
+int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival);
+
+const char *xenbus_strstate(enum xenbus_state state);
+struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev);
+void xen_be_check_state(struct XenDevice *xendev);
+
+/* xen backend driver bits */
+int xen_be_init(void);
+int xen_be_register(const char *type, struct XenDevOps *ops);
+int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state);
+int xen_be_bind_evtchn(struct XenDevice *xendev);
+void xen_be_unbind_evtchn(struct XenDevice *xendev);
+int xen_be_send_notify(struct XenDevice *xendev);
+void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...)
+ GCC_FMT_ATTR(3, 4);
+
+/* actual backend drivers */
+extern struct XenDevOps xen_console_ops; /* xen_console.c */
+extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */
+extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */
+extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */
+extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */
+
+void xen_init_display(int domid);
+
+/* configuration (aka xenbus setup) */
+void xen_config_cleanup(void);
+int xen_config_dev_blk(DriveInfo *disk);
+int xen_config_dev_nic(NICInfo *nic);
+int xen_config_dev_vfb(int vdev, const char *type);
+int xen_config_dev_vkbd(int vdev);
+int xen_config_dev_console(int vdev);
+
+#endif /* QEMU_HW_XEN_BACKEND_H */
diff --git a/hw/xen_blkif.h b/hw/xen_blkif.h
new file mode 100644
index 0000000..c0f4136
--- /dev/null
+++ b/hw/xen_blkif.h
@@ -0,0 +1,103 @@
+#ifndef __XEN_BLKIF_H__
+#define __XEN_BLKIF_H__
+
+#include <xen/io/ring.h>
+#include <xen/io/blkif.h>
+#include <xen/io/protocols.h>
+
+/* Not a real protocol. Used to generate ring structs which contain
+ * the elements common to all protocols only. This way we get a
+ * compiler-checkable way to use common struct elements, so we can
+ * avoid using switch(protocol) in a number of places. */
+struct blkif_common_request {
+ char dummy;
+};
+struct blkif_common_response {
+ char dummy;
+};
+
+/* i386 protocol version */
+#pragma pack(push, 4)
+struct blkif_x86_32_request {
+ uint8_t operation; /* BLKIF_OP_??? */
+ uint8_t nr_segments; /* number of segments */
+ blkif_vdev_t handle; /* only for read/write requests */
+ uint64_t id; /* private guest value, echoed in resp */
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+};
+struct blkif_x86_32_response {
+ uint64_t id; /* copied from request */
+ uint8_t operation; /* copied from request */
+ int16_t status; /* BLKIF_RSP_??? */
+};
+typedef struct blkif_x86_32_request blkif_x86_32_request_t;
+typedef struct blkif_x86_32_response blkif_x86_32_response_t;
+#pragma pack(pop)
+
+/* x86_64 protocol version */
+struct blkif_x86_64_request {
+ uint8_t operation; /* BLKIF_OP_??? */
+ uint8_t nr_segments; /* number of segments */
+ blkif_vdev_t handle; /* only for read/write requests */
+ uint64_t __attribute__((__aligned__(8))) id;
+ blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */
+ struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+};
+struct blkif_x86_64_response {
+ uint64_t __attribute__((__aligned__(8))) id;
+ uint8_t operation; /* copied from request */
+ int16_t status; /* BLKIF_RSP_??? */
+};
+typedef struct blkif_x86_64_request blkif_x86_64_request_t;
+typedef struct blkif_x86_64_response blkif_x86_64_response_t;
+
+DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response);
+DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response);
+DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response);
+
+union blkif_back_rings {
+ blkif_back_ring_t native;
+ blkif_common_back_ring_t common;
+ blkif_x86_32_back_ring_t x86_32_part;
+ blkif_x86_64_back_ring_t x86_64_part;
+};
+typedef union blkif_back_rings blkif_back_rings_t;
+
+enum blkif_protocol {
+ BLKIF_PROTOCOL_NATIVE = 1,
+ BLKIF_PROTOCOL_X86_32 = 2,
+ BLKIF_PROTOCOL_X86_64 = 3,
+};
+
+static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src)
+{
+ int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
+
+ dst->operation = src->operation;
+ dst->nr_segments = src->nr_segments;
+ dst->handle = src->handle;
+ dst->id = src->id;
+ dst->sector_number = src->sector_number;
+ if (n > src->nr_segments)
+ n = src->nr_segments;
+ for (i = 0; i < n; i++)
+ dst->seg[i] = src->seg[i];
+}
+
+static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src)
+{
+ int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST;
+
+ dst->operation = src->operation;
+ dst->nr_segments = src->nr_segments;
+ dst->handle = src->handle;
+ dst->id = src->id;
+ dst->sector_number = src->sector_number;
+ if (n > src->nr_segments)
+ n = src->nr_segments;
+ for (i = 0; i < n; i++)
+ dst->seg[i] = src->seg[i];
+}
+
+#endif /* __XEN_BLKIF_H__ */
diff --git a/hw/xen_common.h b/hw/xen_common.h
new file mode 100644
index 0000000..8a55b44
--- /dev/null
+++ b/hw/xen_common.h
@@ -0,0 +1,34 @@
+#ifndef QEMU_HW_XEN_COMMON_H
+#define QEMU_HW_XEN_COMMON_H 1
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#include <xenctrl.h>
+#include <xs.h>
+#include <xen/io/xenbus.h>
+
+#include "hw.h"
+#include "xen.h"
+#include "qemu-queue.h"
+
+/*
+ * tweaks needed to build with different xen versions
+ * 0x00030205 -> 3.1.0
+ * 0x00030207 -> 3.2.0
+ * 0x00030208 -> unstable
+ */
+#include <xen/xen-compat.h>
+#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205
+# define evtchn_port_or_error_t int
+#endif
+#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207
+# define xc_map_foreign_pages xc_map_foreign_batch
+#endif
+#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208
+# define xen_mb() mb()
+# define xen_rmb() rmb()
+# define xen_wmb() wmb()
+#endif
+
+#endif /* QEMU_HW_XEN_COMMON_H */
diff --git a/hw/xen_console.c b/hw/xen_console.c
new file mode 100644
index 0000000..d2261f4
--- /dev/null
+++ b/hw/xen_console.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (C) Red Hat 2007
+ *
+ * Xen Console
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <xs.h>
+#include <xen/io/console.h>
+#include <xenctrl.h>
+
+#include "hw.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "xen_backend.h"
+
+struct buffer {
+ uint8_t *data;
+ size_t consumed;
+ size_t size;
+ size_t capacity;
+ size_t max_capacity;
+};
+
+struct XenConsole {
+ struct XenDevice xendev; /* must be first */
+ struct buffer buffer;
+ char console[XEN_BUFSIZE];
+ int ring_ref;
+ void *sring;
+ CharDriverState *chr;
+ int backlog;
+};
+
+static void buffer_append(struct XenConsole *con)
+{
+ struct buffer *buffer = &con->buffer;
+ XENCONS_RING_IDX cons, prod, size;
+ struct xencons_interface *intf = con->sring;
+
+ cons = intf->out_cons;
+ prod = intf->out_prod;
+ xen_mb();
+
+ size = prod - cons;
+ if ((size == 0) || (size > sizeof(intf->out)))
+ return;
+
+ if ((buffer->capacity - buffer->size) < size) {
+ buffer->capacity += (size + 1024);
+ buffer->data = qemu_realloc(buffer->data, buffer->capacity);
+ }
+
+ while (cons != prod)
+ buffer->data[buffer->size++] = intf->out[
+ MASK_XENCONS_IDX(cons++, intf->out)];
+
+ xen_mb();
+ intf->out_cons = cons;
+ xen_be_send_notify(&con->xendev);
+
+ if (buffer->max_capacity &&
+ buffer->size > buffer->max_capacity) {
+ /* Discard the middle of the data. */
+
+ size_t over = buffer->size - buffer->max_capacity;
+ uint8_t *maxpos = buffer->data + buffer->max_capacity;
+
+ memmove(maxpos - over, maxpos, over);
+ buffer->data = qemu_realloc(buffer->data, buffer->max_capacity);
+ buffer->size = buffer->capacity = buffer->max_capacity;
+
+ if (buffer->consumed > buffer->max_capacity - over)
+ buffer->consumed = buffer->max_capacity - over;
+ }
+}
+
+static void buffer_advance(struct buffer *buffer, size_t len)
+{
+ buffer->consumed += len;
+ if (buffer->consumed == buffer->size) {
+ buffer->consumed = 0;
+ buffer->size = 0;
+ }
+}
+
+static int ring_free_bytes(struct XenConsole *con)
+{
+ struct xencons_interface *intf = con->sring;
+ XENCONS_RING_IDX cons, prod, space;
+
+ cons = intf->in_cons;
+ prod = intf->in_prod;
+ xen_mb();
+
+ space = prod - cons;
+ if (space > sizeof(intf->in))
+ return 0; /* ring is screwed: ignore it */
+
+ return (sizeof(intf->in) - space);
+}
+
+static int xencons_can_receive(void *opaque)
+{
+ struct XenConsole *con = opaque;
+ return ring_free_bytes(con);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+ struct XenConsole *con = opaque;
+ struct xencons_interface *intf = con->sring;
+ XENCONS_RING_IDX prod;
+ int i, max;
+
+ max = ring_free_bytes(con);
+ /* The can_receive() func limits this, but check again anyway */
+ if (max < len)
+ len = max;
+
+ prod = intf->in_prod;
+ for (i = 0; i < len; i++) {
+ intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
+ buf[i];
+ }
+ xen_wmb();
+ intf->in_prod = prod;
+ xen_be_send_notify(&con->xendev);
+}
+
+static void xencons_send(struct XenConsole *con)
+{
+ ssize_t len, size;
+
+ size = con->buffer.size - con->buffer.consumed;
+ if (con->chr)
+ len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed,
+ size);
+ else
+ len = size;
+ if (len < 1) {
+ if (!con->backlog) {
+ con->backlog = 1;
+ xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n");
+ }
+ } else {
+ buffer_advance(&con->buffer, len);
+ if (con->backlog && len == size) {
+ con->backlog = 0;
+ xen_be_printf(&con->xendev, 1, "backlog is gone\n");
+ }
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+static int con_init(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+ char *type, *dom;
+
+ /* setup */
+ dom = xs_get_domain_path(xenstore, con->xendev.dom);
+ snprintf(con->console, sizeof(con->console), "%s/console", dom);
+ free(dom);
+
+ type = xenstore_read_str(con->console, "type");
+ if (!type || strcmp(type, "ioemu") != 0) {
+ xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
+ return -1;
+ }
+
+ if (!serial_hds[con->xendev.dev])
+ xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n",
+ con->xendev.dev);
+ else
+ con->chr = serial_hds[con->xendev.dev];
+
+ return 0;
+}
+
+static int con_connect(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+ int limit;
+
+ if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
+ return -1;
+ if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
+ return -1;
+ if (xenstore_read_int(con->console, "limit", &limit) == 0)
+ con->buffer.max_capacity = limit;
+
+ con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom,
+ XC_PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ con->ring_ref);
+ if (!con->sring)
+ return -1;
+
+ xen_be_bind_evtchn(&con->xendev);
+ if (con->chr)
+ qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive,
+ NULL, con);
+
+ xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
+ con->ring_ref,
+ con->xendev.remote_port,
+ con->xendev.local_port,
+ con->buffer.max_capacity);
+ return 0;
+}
+
+static void con_disconnect(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+
+ if (con->chr)
+ qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
+ xen_be_unbind_evtchn(&con->xendev);
+
+ if (con->sring) {
+ munmap(con->sring, XC_PAGE_SIZE);
+ con->sring = NULL;
+ }
+}
+
+static void con_event(struct XenDevice *xendev)
+{
+ struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+
+ buffer_append(con);
+ if (con->buffer.size - con->buffer.consumed)
+ xencons_send(con);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct XenDevOps xen_console_ops = {
+ .size = sizeof(struct XenConsole),
+ .flags = DEVOPS_FLAG_IGNORE_STATE,
+ .init = con_init,
+ .connect = con_connect,
+ .event = con_event,
+ .disconnect = con_disconnect,
+};
diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c
new file mode 100644
index 0000000..8d50216
--- /dev/null
+++ b/hw/xen_devconfig.c
@@ -0,0 +1,173 @@
+#include "xen_backend.h"
+#include "blockdev.h"
+#include "block_int.h" /* XXX */
+
+/* ------------------------------------------------------------- */
+
+struct xs_dirs {
+ char *xs_dir;
+ QTAILQ_ENTRY(xs_dirs) list;
+};
+static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup);
+
+static void xen_config_cleanup_dir(char *dir)
+{
+ struct xs_dirs *d;
+
+ d = qemu_malloc(sizeof(*d));
+ d->xs_dir = dir;
+ QTAILQ_INSERT_TAIL(&xs_cleanup, d, list);
+}
+
+void xen_config_cleanup(void)
+{
+ struct xs_dirs *d;
+
+ QTAILQ_FOREACH(d, &xs_cleanup, list) {
+ xs_rm(xenstore, 0, d->xs_dir);
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xen_config_dev_mkdir(char *dev, int p)
+{
+ struct xs_permissions perms[2] = {{
+ .id = 0, /* set owner: dom0 */
+ },{
+ .id = xen_domid,
+ .perms = p,
+ }};
+
+ if (!xs_mkdir(xenstore, 0, dev)) {
+ xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev);
+ return -1;
+ }
+ xen_config_cleanup_dir(qemu_strdup(dev));
+
+ if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) {
+ xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev);
+ return -1;
+ }
+ return 0;
+}
+
+static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev,
+ char *fe, char *be, int len)
+{
+ char *dom;
+
+ dom = xs_get_domain_path(xenstore, xen_domid);
+ snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev);
+ free(dom);
+
+ dom = xs_get_domain_path(xenstore, 0);
+ snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev);
+ free(dom);
+
+ xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE);
+ xen_config_dev_mkdir(be, XS_PERM_READ);
+ return 0;
+}
+
+static int xen_config_dev_all(char *fe, char *be)
+{
+ /* frontend */
+ if (xen_protocol)
+ xenstore_write_str(fe, "protocol", xen_protocol);
+
+ xenstore_write_int(fe, "state", XenbusStateInitialising);
+ xenstore_write_int(fe, "backend-id", 0);
+ xenstore_write_str(fe, "backend", be);
+
+ /* backend */
+ xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name");
+ xenstore_write_int(be, "online", 1);
+ xenstore_write_int(be, "state", XenbusStateInitialising);
+ xenstore_write_int(be, "frontend-id", xen_domid);
+ xenstore_write_str(be, "frontend", fe);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+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;
+ const char *devtype = cdrom ? "cdrom" : "disk";
+ const char *mode = cdrom ? "r" : "w";
+
+ snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name),
+ "xvd%c", 'a' + disk->unit);
+ xen_be_printf(NULL, 1, "config disk %d [%s]: %s\n",
+ disk->unit, disk->bdrv->device_name, disk->bdrv->filename);
+ xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe));
+
+ /* frontend */
+ xenstore_write_int(fe, "virtual-device", vdev);
+ xenstore_write_str(fe, "device-type", devtype);
+
+ /* backend */
+ xenstore_write_str(be, "dev", disk->bdrv->device_name);
+ xenstore_write_str(be, "type", "file");
+ xenstore_write_str(be, "params", disk->bdrv->filename);
+ xenstore_write_str(be, "mode", mode);
+
+ /* common stuff */
+ return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_nic(NICInfo *nic)
+{
+ char fe[256], be[256];
+ char mac[20];
+
+ snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
+ nic->macaddr[0], nic->macaddr[1], nic->macaddr[2],
+ nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]);
+ xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", nic->vlan->id, mac);
+ xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe));
+
+ /* frontend */
+ xenstore_write_int(fe, "handle", nic->vlan->id);
+ xenstore_write_str(fe, "mac", mac);
+
+ /* backend */
+ xenstore_write_int(be, "handle", nic->vlan->id);
+ xenstore_write_str(be, "mac", mac);
+
+ /* common stuff */
+ return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_vfb(int vdev, const char *type)
+{
+ char fe[256], be[256];
+
+ xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe));
+
+ /* backend */
+ xenstore_write_str(be, "type", type);
+
+ /* common stuff */
+ return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_vkbd(int vdev)
+{
+ char fe[256], be[256];
+
+ xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe));
+ return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_console(int vdev)
+{
+ char fe[256], be[256];
+
+ xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe));
+ return xen_config_dev_all(fe, be);
+}
diff --git a/hw/xen_disk.c b/hw/xen_disk.c
new file mode 100644
index 0000000..ed9e5eb
--- /dev/null
+++ b/hw/xen_disk.c
@@ -0,0 +1,788 @@
+/*
+ * xen paravirt block device backend
+ *
+ * (c) Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#include <xs.h>
+#include <xenctrl.h>
+#include <xen/io/xenbus.h>
+
+#include "hw.h"
+#include "block_int.h"
+#include "qemu-char.h"
+#include "xen_blkif.h"
+#include "xen_backend.h"
+#include "blockdev.h"
+
+/* ------------------------------------------------------------- */
+
+static int syncwrite = 0;
+static int batch_maps = 0;
+
+static int max_requests = 32;
+static int use_aio = 1;
+
+/* ------------------------------------------------------------- */
+
+#define BLOCK_SIZE 512
+#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2)
+
+struct ioreq {
+ blkif_request_t req;
+ int16_t status;
+
+ /* parsed request */
+ off_t start;
+ QEMUIOVector v;
+ int presync;
+ int postsync;
+
+ /* grant mapping */
+ uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ int prot;
+ void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+ void *pages;
+
+ /* aio status */
+ int aio_inflight;
+ int aio_errors;
+
+ struct XenBlkDev *blkdev;
+ QLIST_ENTRY(ioreq) list;
+};
+
+struct XenBlkDev {
+ struct XenDevice xendev; /* must be first */
+ char *params;
+ char *mode;
+ char *type;
+ char *dev;
+ char *devtype;
+ const char *fileproto;
+ const char *filename;
+ int ring_ref;
+ void *sring;
+ int64_t file_blk;
+ int64_t file_size;
+ int protocol;
+ blkif_back_rings_t rings;
+ int more_work;
+ int cnt_map;
+
+ /* request lists */
+ QLIST_HEAD(inflight_head, ioreq) inflight;
+ QLIST_HEAD(finished_head, ioreq) finished;
+ QLIST_HEAD(freelist_head, ioreq) freelist;
+ int requests_total;
+ int requests_inflight;
+ int requests_finished;
+
+ /* qemu block driver */
+ DriveInfo *dinfo;
+ BlockDriverState *bs;
+ QEMUBH *bh;
+};
+
+/* ------------------------------------------------------------- */
+
+static struct ioreq *ioreq_start(struct XenBlkDev *blkdev)
+{
+ struct ioreq *ioreq = NULL;
+
+ if (QLIST_EMPTY(&blkdev->freelist)) {
+ if (blkdev->requests_total >= max_requests)
+ goto out;
+ /* allocate new struct */
+ ioreq = qemu_mallocz(sizeof(*ioreq));
+ ioreq->blkdev = blkdev;
+ blkdev->requests_total++;
+ qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+ } else {
+ /* get one from freelist */
+ ioreq = QLIST_FIRST(&blkdev->freelist);
+ QLIST_REMOVE(ioreq, list);
+ qemu_iovec_reset(&ioreq->v);
+ }
+ QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list);
+ blkdev->requests_inflight++;
+
+out:
+ return ioreq;
+}
+
+static void ioreq_finish(struct ioreq *ioreq)
+{
+ struct XenBlkDev *blkdev = ioreq->blkdev;
+
+ QLIST_REMOVE(ioreq, list);
+ QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list);
+ blkdev->requests_inflight--;
+ blkdev->requests_finished++;
+}
+
+static void ioreq_release(struct ioreq *ioreq)
+{
+ struct XenBlkDev *blkdev = ioreq->blkdev;
+
+ QLIST_REMOVE(ioreq, list);
+ memset(ioreq, 0, sizeof(*ioreq));
+ ioreq->blkdev = blkdev;
+ QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list);
+ blkdev->requests_finished--;
+}
+
+/*
+ * translate request into iovec + start offset
+ * do sanity checks along the way
+ */
+static int ioreq_parse(struct ioreq *ioreq)
+{
+ struct XenBlkDev *blkdev = ioreq->blkdev;
+ uintptr_t mem;
+ size_t len;
+ int i;
+
+ xen_be_printf(&blkdev->xendev, 3,
+ "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n",
+ ioreq->req.operation, ioreq->req.nr_segments,
+ ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number);
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ ioreq->prot = PROT_WRITE; /* to memory */
+ break;
+ case BLKIF_OP_WRITE_BARRIER:
+ if (!ioreq->req.nr_segments) {
+ ioreq->presync = 1;
+ return 0;
+ }
+ if (!syncwrite)
+ ioreq->presync = ioreq->postsync = 1;
+ /* fall through */
+ case BLKIF_OP_WRITE:
+ ioreq->prot = PROT_READ; /* from memory */
+ if (syncwrite)
+ ioreq->postsync = 1;
+ break;
+ default:
+ xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n",
+ ioreq->req.operation);
+ goto err;
+ };
+
+ if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') {
+ xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n");
+ goto err;
+ }
+
+ ioreq->start = ioreq->req.sector_number * blkdev->file_blk;
+ for (i = 0; i < ioreq->req.nr_segments; i++) {
+ if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
+ xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n");
+ goto err;
+ }
+ if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) {
+ xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n");
+ goto err;
+ }
+ if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) {
+ xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n");
+ goto err;
+ }
+
+ ioreq->domids[i] = blkdev->xendev.dom;
+ ioreq->refs[i] = ioreq->req.seg[i].gref;
+
+ mem = ioreq->req.seg[i].first_sect * blkdev->file_blk;
+ len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk;
+ qemu_iovec_add(&ioreq->v, (void*)mem, len);
+ }
+ if (ioreq->start + ioreq->v.size > blkdev->file_size) {
+ xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n");
+ goto err;
+ }
+ return 0;
+
+err:
+ ioreq->status = BLKIF_RSP_ERROR;
+ return -1;
+}
+
+static void ioreq_unmap(struct ioreq *ioreq)
+{
+ int gnt = ioreq->blkdev->xendev.gnttabdev;
+ int i;
+
+ if (ioreq->v.niov == 0)
+ return;
+ if (batch_maps) {
+ if (!ioreq->pages)
+ return;
+ if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0)
+ xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
+ strerror(errno));
+ ioreq->blkdev->cnt_map -= ioreq->v.niov;
+ ioreq->pages = NULL;
+ } else {
+ for (i = 0; i < ioreq->v.niov; i++) {
+ if (!ioreq->page[i])
+ continue;
+ if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0)
+ xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
+ strerror(errno));
+ ioreq->blkdev->cnt_map--;
+ ioreq->page[i] = NULL;
+ }
+ }
+}
+
+static int ioreq_map(struct ioreq *ioreq)
+{
+ int gnt = ioreq->blkdev->xendev.gnttabdev;
+ int i;
+
+ if (ioreq->v.niov == 0)
+ return 0;
+ if (batch_maps) {
+ ioreq->pages = xc_gnttab_map_grant_refs
+ (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot);
+ if (ioreq->pages == NULL) {
+ xen_be_printf(&ioreq->blkdev->xendev, 0,
+ "can't map %d grant refs (%s, %d maps)\n",
+ ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map);
+ return -1;
+ }
+ for (i = 0; i < ioreq->v.niov; i++)
+ ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE +
+ (uintptr_t)ioreq->v.iov[i].iov_base;
+ ioreq->blkdev->cnt_map += ioreq->v.niov;
+ } else {
+ for (i = 0; i < ioreq->v.niov; i++) {
+ ioreq->page[i] = xc_gnttab_map_grant_ref
+ (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot);
+ if (ioreq->page[i] == NULL) {
+ xen_be_printf(&ioreq->blkdev->xendev, 0,
+ "can't map grant ref %d (%s, %d maps)\n",
+ ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map);
+ ioreq_unmap(ioreq);
+ return -1;
+ }
+ ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base;
+ ioreq->blkdev->cnt_map++;
+ }
+ }
+ return 0;
+}
+
+static int ioreq_runio_qemu_sync(struct ioreq *ioreq)
+{
+ struct XenBlkDev *blkdev = ioreq->blkdev;
+ int i, rc, len = 0;
+ off_t pos;
+
+ if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1)
+ goto err;
+ if (ioreq->presync)
+ bdrv_flush(blkdev->bs);
+
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ pos = ioreq->start;
+ for (i = 0; i < ioreq->v.niov; i++) {
+ rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE,
+ ioreq->v.iov[i].iov_base,
+ ioreq->v.iov[i].iov_len / BLOCK_SIZE);
+ if (rc != 0) {
+ xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n",
+ ioreq->v.iov[i].iov_base,
+ ioreq->v.iov[i].iov_len);
+ goto err;
+ }
+ len += ioreq->v.iov[i].iov_len;
+ pos += ioreq->v.iov[i].iov_len;
+ }
+ break;
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ if (!ioreq->req.nr_segments)
+ break;
+ pos = ioreq->start;
+ for (i = 0; i < ioreq->v.niov; i++) {
+ rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE,
+ ioreq->v.iov[i].iov_base,
+ ioreq->v.iov[i].iov_len / BLOCK_SIZE);
+ if (rc != 0) {
+ xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n",
+ ioreq->v.iov[i].iov_base,
+ ioreq->v.iov[i].iov_len);
+ goto err;
+ }
+ len += ioreq->v.iov[i].iov_len;
+ pos += ioreq->v.iov[i].iov_len;
+ }
+ break;
+ default:
+ /* unknown operation (shouldn't happen -- parse catches this) */
+ goto err;
+ }
+
+ if (ioreq->postsync)
+ bdrv_flush(blkdev->bs);
+ ioreq->status = BLKIF_RSP_OKAY;
+
+ ioreq_unmap(ioreq);
+ ioreq_finish(ioreq);
+ return 0;
+
+err:
+ ioreq->status = BLKIF_RSP_ERROR;
+ return -1;
+}
+
+static void qemu_aio_complete(void *opaque, int ret)
+{
+ struct ioreq *ioreq = opaque;
+
+ if (ret != 0) {
+ xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n",
+ ioreq->req.operation == BLKIF_OP_READ ? "read" : "write");
+ ioreq->aio_errors++;
+ }
+
+ ioreq->aio_inflight--;
+ if (ioreq->aio_inflight > 0)
+ return;
+
+ ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
+ ioreq_unmap(ioreq);
+ ioreq_finish(ioreq);
+ qemu_bh_schedule(ioreq->blkdev->bh);
+}
+
+static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
+{
+ struct XenBlkDev *blkdev = ioreq->blkdev;
+
+ if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1)
+ goto err;
+
+ ioreq->aio_inflight++;
+ if (ioreq->presync)
+ bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */
+
+ switch (ioreq->req.operation) {
+ case BLKIF_OP_READ:
+ ioreq->aio_inflight++;
+ bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE,
+ &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+ qemu_aio_complete, ioreq);
+ break;
+ case BLKIF_OP_WRITE:
+ case BLKIF_OP_WRITE_BARRIER:
+ ioreq->aio_inflight++;
+ if (!ioreq->req.nr_segments)
+ break;
+ bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE,
+ &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+ qemu_aio_complete, ioreq);
+ break;
+ default:
+ /* unknown operation (shouldn't happen -- parse catches this) */
+ goto err;
+ }
+
+ if (ioreq->postsync)
+ bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */
+ qemu_aio_complete(ioreq, 0);
+
+ return 0;
+
+err:
+ ioreq->status = BLKIF_RSP_ERROR;
+ return -1;
+}
+
+static int blk_send_response_one(struct ioreq *ioreq)
+{
+ struct XenBlkDev *blkdev = ioreq->blkdev;
+ int send_notify = 0;
+ int have_requests = 0;
+ blkif_response_t resp;
+ void *dst;
+
+ resp.id = ioreq->req.id;
+ resp.operation = ioreq->req.operation;
+ resp.status = ioreq->status;
+
+ /* Place on the response ring for the relevant domain. */
+ switch (blkdev->protocol) {
+ case BLKIF_PROTOCOL_NATIVE:
+ dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt);
+ break;
+ case BLKIF_PROTOCOL_X86_32:
+ dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part,
+ blkdev->rings.x86_32_part.rsp_prod_pvt);
+ break;
+ case BLKIF_PROTOCOL_X86_64:
+ dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part,
+ blkdev->rings.x86_64_part.rsp_prod_pvt);
+ break;
+ default:
+ dst = NULL;
+ }
+ memcpy(dst, &resp, sizeof(resp));
+ blkdev->rings.common.rsp_prod_pvt++;
+
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify);
+ if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) {
+ /*
+ * Tail check for pending requests. Allows frontend to avoid
+ * notifications if requests are already in flight (lower
+ * overheads and promotes batching).
+ */
+ RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests);
+ } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) {
+ have_requests = 1;
+ }
+
+ if (have_requests)
+ blkdev->more_work++;
+ return send_notify;
+}
+
+/* walk finished list, send outstanding responses, free requests */
+static void blk_send_response_all(struct XenBlkDev *blkdev)
+{
+ struct ioreq *ioreq;
+ int send_notify = 0;
+
+ while (!QLIST_EMPTY(&blkdev->finished)) {
+ ioreq = QLIST_FIRST(&blkdev->finished);
+ send_notify += blk_send_response_one(ioreq);
+ ioreq_release(ioreq);
+ }
+ if (send_notify)
+ xen_be_send_notify(&blkdev->xendev);
+}
+
+static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc)
+{
+ switch (blkdev->protocol) {
+ case BLKIF_PROTOCOL_NATIVE:
+ memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc),
+ sizeof(ioreq->req));
+ break;
+ case BLKIF_PROTOCOL_X86_32:
+ blkif_get_x86_32_req(&ioreq->req,
+ RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc));
+ break;
+ case BLKIF_PROTOCOL_X86_64:
+ blkif_get_x86_64_req(&ioreq->req,
+ RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc));
+ break;
+ }
+ return 0;
+}
+
+static void blk_handle_requests(struct XenBlkDev *blkdev)
+{
+ RING_IDX rc, rp;
+ struct ioreq *ioreq;
+
+ blkdev->more_work = 0;
+
+ rc = blkdev->rings.common.req_cons;
+ rp = blkdev->rings.common.sring->req_prod;
+ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ if (use_aio)
+ blk_send_response_all(blkdev);
+ while (rc != rp) {
+ /* pull request from ring */
+ if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc))
+ break;
+ ioreq = ioreq_start(blkdev);
+ if (ioreq == NULL) {
+ blkdev->more_work++;
+ break;
+ }
+ blk_get_request(blkdev, ioreq, rc);
+ blkdev->rings.common.req_cons = ++rc;
+
+ /* parse them */
+ if (ioreq_parse(ioreq) != 0) {
+ if (blk_send_response_one(ioreq))
+ xen_be_send_notify(&blkdev->xendev);
+ ioreq_release(ioreq);
+ continue;
+ }
+
+ if (use_aio) {
+ /* run i/o in aio mode */
+ ioreq_runio_qemu_aio(ioreq);
+ } else {
+ /* run i/o in sync mode */
+ ioreq_runio_qemu_sync(ioreq);
+ }
+ }
+ if (!use_aio)
+ blk_send_response_all(blkdev);
+
+ if (blkdev->more_work && blkdev->requests_inflight < max_requests)
+ qemu_bh_schedule(blkdev->bh);
+}
+
+/* ------------------------------------------------------------- */
+
+static void blk_bh(void *opaque)
+{
+ struct XenBlkDev *blkdev = opaque;
+ blk_handle_requests(blkdev);
+}
+
+static void blk_alloc(struct XenDevice *xendev)
+{
+ struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+ QLIST_INIT(&blkdev->inflight);
+ QLIST_INIT(&blkdev->finished);
+ QLIST_INIT(&blkdev->freelist);
+ blkdev->bh = qemu_bh_new(blk_bh, blkdev);
+ if (xen_mode != XEN_EMULATE)
+ batch_maps = 1;
+}
+
+static int blk_init(struct XenDevice *xendev)
+{
+ struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+ int index, qflags, have_barriers, info = 0;
+ char *h;
+
+ /* read xenstore entries */
+ if (blkdev->params == NULL) {
+ blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params");
+ h = strchr(blkdev->params, ':');
+ if (h != NULL) {
+ blkdev->fileproto = blkdev->params;
+ blkdev->filename = h+1;
+ *h = 0;
+ } else {
+ blkdev->fileproto = "<unset>";
+ blkdev->filename = blkdev->params;
+ }
+ }
+ if (blkdev->mode == NULL)
+ blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
+ if (blkdev->type == NULL)
+ blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type");
+ if (blkdev->dev == NULL)
+ blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev");
+ if (blkdev->devtype == NULL)
+ blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type");
+
+ /* do we have all we need? */
+ if (blkdev->params == NULL ||
+ blkdev->mode == NULL ||
+ blkdev->type == NULL ||
+ blkdev->dev == NULL)
+ return -1;
+
+ /* read-only ? */
+ if (strcmp(blkdev->mode, "w") == 0) {
+ qflags = BDRV_O_RDWR;
+ } else {
+ qflags = 0;
+ info |= VDISK_READONLY;
+ }
+
+ /* cdrom ? */
+ if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom"))
+ info |= VDISK_CDROM;
+
+ /* init qemu block driver */
+ index = (blkdev->xendev.dev - 202 * 256) / 16;
+ blkdev->dinfo = drive_get(IF_XEN, 0, index);
+ if (!blkdev->dinfo) {
+ /* setup via xenbus -> create new block driver instance */
+ xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
+ blkdev->bs = bdrv_new(blkdev->dev);
+ if (bdrv_open(blkdev->bs, blkdev->filename, qflags,
+ bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
+ bdrv_delete(blkdev->bs);
+ return -1;
+ }
+ } else {
+ /* setup via qemu cmdline -> already setup for us */
+ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
+ blkdev->bs = blkdev->dinfo->bdrv;
+ }
+ blkdev->file_blk = BLOCK_SIZE;
+ blkdev->file_size = bdrv_getlength(blkdev->bs);
+ if (blkdev->file_size < 0) {
+ xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n",
+ (int)blkdev->file_size, strerror(-blkdev->file_size),
+ blkdev->bs->drv ? blkdev->bs->drv->format_name : "-");
+ blkdev->file_size = 0;
+ }
+ have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0;
+
+ xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\","
+ " size %" PRId64 " (%" PRId64 " MB)\n",
+ blkdev->type, blkdev->fileproto, blkdev->filename,
+ blkdev->file_size, blkdev->file_size >> 20);
+
+ /* fill info */
+ xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers);
+ xenstore_write_be_int(&blkdev->xendev, "info", info);
+ xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk);
+ xenstore_write_be_int(&blkdev->xendev, "sectors",
+ blkdev->file_size / blkdev->file_blk);
+ return 0;
+}
+
+static int blk_connect(struct XenDevice *xendev)
+{
+ struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+ if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1)
+ return -1;
+ if (xenstore_read_fe_int(&blkdev->xendev, "event-channel",
+ &blkdev->xendev.remote_port) == -1)
+ return -1;
+
+ blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
+ if (blkdev->xendev.protocol) {
+ if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0)
+ blkdev->protocol = BLKIF_PROTOCOL_X86_32;
+ if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0)
+ blkdev->protocol = BLKIF_PROTOCOL_X86_64;
+ }
+
+ blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev,
+ blkdev->xendev.dom,
+ blkdev->ring_ref,
+ PROT_READ | PROT_WRITE);
+ if (!blkdev->sring)
+ return -1;
+ blkdev->cnt_map++;
+
+ switch (blkdev->protocol) {
+ case BLKIF_PROTOCOL_NATIVE:
+ {
+ blkif_sring_t *sring_native = blkdev->sring;
+ BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_32:
+ {
+ blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring;
+
+ BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE);
+ break;
+ }
+ case BLKIF_PROTOCOL_X86_64:
+ {
+ blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring;
+
+ BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE);
+ break;
+ }
+ }
+
+ xen_be_bind_evtchn(&blkdev->xendev);
+
+ xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, "
+ "remote port %d, local port %d\n",
+ blkdev->xendev.protocol, blkdev->ring_ref,
+ blkdev->xendev.remote_port, blkdev->xendev.local_port);
+ return 0;
+}
+
+static void blk_disconnect(struct XenDevice *xendev)
+{
+ struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+ if (blkdev->bs) {
+ if (!blkdev->dinfo) {
+ /* close/delete only if we created it ourself */
+ bdrv_close(blkdev->bs);
+ bdrv_delete(blkdev->bs);
+ }
+ blkdev->bs = NULL;
+ }
+ xen_be_unbind_evtchn(&blkdev->xendev);
+
+ if (blkdev->sring) {
+ xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1);
+ blkdev->cnt_map--;
+ blkdev->sring = NULL;
+ }
+}
+
+static int blk_free(struct XenDevice *xendev)
+{
+ struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+ struct ioreq *ioreq;
+
+ while (!QLIST_EMPTY(&blkdev->freelist)) {
+ ioreq = QLIST_FIRST(&blkdev->freelist);
+ QLIST_REMOVE(ioreq, list);
+ qemu_iovec_destroy(&ioreq->v);
+ qemu_free(ioreq);
+ }
+
+ qemu_free(blkdev->params);
+ qemu_free(blkdev->mode);
+ qemu_free(blkdev->type);
+ qemu_free(blkdev->dev);
+ qemu_free(blkdev->devtype);
+ qemu_bh_delete(blkdev->bh);
+ return 0;
+}
+
+static void blk_event(struct XenDevice *xendev)
+{
+ struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+ qemu_bh_schedule(blkdev->bh);
+}
+
+struct XenDevOps xen_blkdev_ops = {
+ .size = sizeof(struct XenBlkDev),
+ .flags = DEVOPS_FLAG_NEED_GNTDEV,
+ .alloc = blk_alloc,
+ .init = blk_init,
+ .connect = blk_connect,
+ .disconnect = blk_disconnect,
+ .event = blk_event,
+ .free = blk_free,
+};
diff --git a/hw/xen_domainbuild.c b/hw/xen_domainbuild.c
new file mode 100644
index 0000000..7f1fd66
--- /dev/null
+++ b/hw/xen_domainbuild.c
@@ -0,0 +1,300 @@
+#include <signal.h>
+#include "xen_backend.h"
+#include "xen_domainbuild.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+#include "qemu-log.h"
+
+#include <xenguest.h>
+
+static int xenstore_domain_mkdir(char *path)
+{
+ struct xs_permissions perms_ro[] = {{
+ .id = 0, /* set owner: dom0 */
+ },{
+ .id = xen_domid,
+ .perms = XS_PERM_READ,
+ }};
+ struct xs_permissions perms_rw[] = {{
+ .id = 0, /* set owner: dom0 */
+ },{
+ .id = xen_domid,
+ .perms = XS_PERM_READ | XS_PERM_WRITE,
+ }};
+ const char *writable[] = { "device", "control", "error", NULL };
+ char subpath[256];
+ int i;
+
+ if (!xs_mkdir(xenstore, 0, path)) {
+ fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, path);
+ return -1;
+ }
+ if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) {
+ fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__);
+ return -1;
+ }
+
+ for (i = 0; writable[i]; i++) {
+ snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]);
+ if (!xs_mkdir(xenstore, 0, subpath)) {
+ fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, subpath);
+ return -1;
+ }
+ if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) {
+ fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int xenstore_domain_init1(const char *kernel, const char *ramdisk,
+ const char *cmdline)
+{
+ char *dom, uuid_string[42], vm[256], path[256];
+ int i;
+
+ snprintf(uuid_string, sizeof(uuid_string), UUID_FMT,
+ qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], qemu_uuid[3],
+ qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], qemu_uuid[7],
+ qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], qemu_uuid[11],
+ qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], qemu_uuid[15]);
+ dom = xs_get_domain_path(xenstore, xen_domid);
+ snprintf(vm, sizeof(vm), "/vm/%s", uuid_string);
+
+ xenstore_domain_mkdir(dom);
+
+ xenstore_write_str(vm, "image/ostype", "linux");
+ if (kernel)
+ xenstore_write_str(vm, "image/kernel", kernel);
+ if (ramdisk)
+ xenstore_write_str(vm, "image/ramdisk", ramdisk);
+ if (cmdline)
+ xenstore_write_str(vm, "image/cmdline", cmdline);
+
+ /* name + id */
+ xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name");
+ xenstore_write_str(vm, "uuid", uuid_string);
+ xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name");
+ xenstore_write_int(dom, "domid", xen_domid);
+ xenstore_write_str(dom, "vm", vm);
+
+ /* memory */
+ xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB
+ xenstore_write_int(vm, "memory", ram_size >> 20); // MB
+ xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB
+
+ /* cpus */
+ for (i = 0; i < smp_cpus; i++) {
+ snprintf(path, sizeof(path), "cpu/%d/availability",i);
+ xenstore_write_str(dom, path, "online");
+ }
+ xenstore_write_int(vm, "vcpu_avail", smp_cpus);
+ xenstore_write_int(vm, "vcpus", smp_cpus);
+
+ /* vnc password */
+ xenstore_write_str(vm, "vncpassword", "" /* FIXME */);
+
+ free(dom);
+ return 0;
+}
+
+int xenstore_domain_init2(int xenstore_port, int xenstore_mfn,
+ int console_port, int console_mfn)
+{
+ char *dom;
+
+ dom = xs_get_domain_path(xenstore, xen_domid);
+
+ /* signal new domain */
+ xs_introduce_domain(xenstore,
+ xen_domid,
+ xenstore_mfn,
+ xenstore_port);
+
+ /* xenstore */
+ xenstore_write_int(dom, "store/ring-ref", xenstore_mfn);
+ xenstore_write_int(dom, "store/port", xenstore_port);
+
+ /* console */
+ xenstore_write_str(dom, "console/type", "ioemu");
+ xenstore_write_int(dom, "console/limit", 128 * 1024);
+ xenstore_write_int(dom, "console/ring-ref", console_mfn);
+ xenstore_write_int(dom, "console/port", console_port);
+ xen_config_dev_console(0);
+
+ free(dom);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static QEMUTimer *xen_poll;
+
+/* check domain state once per second */
+static void xen_domain_poll(void *opaque)
+{
+ struct xc_dominfo info;
+ int rc;
+
+ rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info);
+ if ((rc != 1) || (info.domid != xen_domid)) {
+ qemu_log("xen: domain %d is gone\n", xen_domid);
+ goto quit;
+ }
+ if (info.dying) {
+ qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid,
+ info.crashed ? "crashed" : "",
+ info.shutdown ? "shutdown" : "");
+ goto quit;
+ }
+
+ qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000);
+ return;
+
+quit:
+ qemu_system_shutdown_request();
+ return;
+}
+
+static int xen_domain_watcher(void)
+{
+ int qemu_running = 1;
+ int fd[2], i, n, rc;
+ char byte;
+
+ if (pipe(fd) != 0) {
+ qemu_log("%s: Huh? pipe error: %s\n", __FUNCTION__, strerror(errno));
+ return -1;
+ }
+ if (fork() != 0)
+ return 0; /* not child */
+
+ /* close all file handles, except stdio/out/err,
+ * our watch pipe and the xen interface handle */
+ n = getdtablesize();
+ for (i = 3; i < n; i++) {
+ if (i == fd[0])
+ continue;
+ if (i == xen_xc)
+ continue;
+ close(i);
+ }
+
+ /* ignore term signals */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+
+ /* wait for qemu exiting */
+ while (qemu_running) {
+ rc = read(fd[0], &byte, 1);
+ switch (rc) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ qemu_log("%s: Huh? read error: %s\n", __FUNCTION__, strerror(errno));
+ qemu_running = 0;
+ break;
+ case 0:
+ /* EOF -> qemu exited */
+ qemu_running = 0;
+ break;
+ default:
+ qemu_log("%s: Huh? data on the watch pipe?\n", __FUNCTION__);
+ break;
+ }
+ }
+
+ /* cleanup */
+ qemu_log("%s: destroy domain %d\n", __FUNCTION__, xen_domid);
+ xc_domain_destroy(xen_xc, xen_domid);
+ _exit(0);
+}
+
+/* normal cleanup */
+static void xen_domain_cleanup(void)
+{
+ char *dom;
+
+ dom = xs_get_domain_path(xenstore, xen_domid);
+ if (dom) {
+ xs_rm(xenstore, 0, dom);
+ free(dom);
+ }
+ xs_release_domain(xenstore, xen_domid);
+}
+
+int xen_domain_build_pv(const char *kernel, const char *ramdisk,
+ const char *cmdline)
+{
+ uint32_t ssidref = 0;
+ uint32_t flags = 0;
+ xen_domain_handle_t uuid;
+ unsigned int xenstore_port = 0, console_port = 0;
+ unsigned long xenstore_mfn = 0, console_mfn = 0;
+ int rc;
+
+ memcpy(uuid, qemu_uuid, sizeof(uuid));
+ rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid);
+ if (rc < 0) {
+ fprintf(stderr, "xen: xc_domain_create() failed\n");
+ goto err;
+ }
+ qemu_log("xen: created domain %d\n", xen_domid);
+ atexit(xen_domain_cleanup);
+ if (xen_domain_watcher() == -1) {
+ goto err;
+ }
+
+ xenstore_domain_init1(kernel, ramdisk, cmdline);
+
+ rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus);
+ if (rc < 0) {
+ fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n");
+ goto err;
+ }
+
+#if 0
+ rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256);
+ if (rc < 0) {
+ fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n");
+ goto err;
+ }
+#endif
+
+ rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10);
+ if (rc < 0) {
+ fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n");
+ goto err;
+ }
+
+ xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0);
+ console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0);
+
+ rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20,
+ kernel, ramdisk, cmdline,
+ 0, flags,
+ xenstore_port, &xenstore_mfn,
+ console_port, &console_mfn);
+ if (rc < 0) {
+ fprintf(stderr, "xen: xc_linux_build() failed\n");
+ goto err;
+ }
+
+ xenstore_domain_init2(xenstore_port, xenstore_mfn,
+ console_port, console_mfn);
+
+ qemu_log("xen: unpausing domain %d\n", xen_domid);
+ rc = xc_domain_unpause(xen_xc, xen_domid);
+ if (rc < 0) {
+ fprintf(stderr, "xen: xc_domain_unpause() failed\n");
+ goto err;
+ }
+
+ xen_poll = qemu_new_timer(rt_clock, xen_domain_poll, NULL);
+ qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000);
+ return 0;
+
+err:
+ return -1;
+}
diff --git a/hw/xen_domainbuild.h b/hw/xen_domainbuild.h
new file mode 100644
index 0000000..dea0121
--- /dev/null
+++ b/hw/xen_domainbuild.h
@@ -0,0 +1,13 @@
+#ifndef QEMU_HW_XEN_DOMAINBUILD_H
+#define QEMU_HW_XEN_DOMAINBUILD_H 1
+
+#include "xen_common.h"
+
+int xenstore_domain_init1(const char *kernel, const char *ramdisk,
+ const char *cmdline);
+int xenstore_domain_init2(int xenstore_port, int xenstore_mfn,
+ int console_port, int console_mfn);
+int xen_domain_build_pv(const char *kernel, const char *ramdisk,
+ const char *cmdline);
+
+#endif /* QEMU_HW_XEN_DOMAINBUILD_H */
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
new file mode 100644
index 0000000..77a34bf
--- /dev/null
+++ b/hw/xen_machine_pv.c
@@ -0,0 +1,124 @@
+/*
+ * QEMU Xen PV Machine
+ *
+ * Copyright (c) 2007 Red Hat
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "sysemu.h"
+#include "boards.h"
+#include "xen_backend.h"
+#include "xen_domainbuild.h"
+#include "blockdev.h"
+
+static void xen_init_pv(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ CPUState *env;
+ DriveInfo *dinfo;
+ int i;
+
+ /* Initialize a dummy CPU */
+ if (cpu_model == NULL) {
+#ifdef TARGET_X86_64
+ cpu_model = "qemu64";
+#else
+ cpu_model = "qemu32";
+#endif
+ }
+ env = cpu_init(cpu_model);
+ env->halted = 1;
+
+ /* Initialize backend core & drivers */
+ if (xen_be_init() != 0) {
+ fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__);
+ exit(1);
+ }
+
+ switch (xen_mode) {
+ case XEN_ATTACH:
+ /* nothing to do, xend handles everything */
+ break;
+ case XEN_CREATE:
+ if (xen_domain_build_pv(kernel_filename, initrd_filename,
+ kernel_cmdline) < 0) {
+ fprintf(stderr, "xen pv domain creation failed\n");
+ exit(1);
+ }
+ break;
+ case XEN_EMULATE:
+ fprintf(stderr, "xen emulation not implemented (yet)\n");
+ exit(1);
+ break;
+ }
+
+ xen_be_register("console", &xen_console_ops);
+ xen_be_register("vkbd", &xen_kbdmouse_ops);
+ xen_be_register("vfb", &xen_framebuffer_ops);
+ xen_be_register("qdisk", &xen_blkdev_ops);
+ xen_be_register("qnic", &xen_netdev_ops);
+
+ /* configure framebuffer */
+ if (xenfb_enabled) {
+ xen_config_dev_vfb(0, "vnc");
+ xen_config_dev_vkbd(0);
+ }
+
+ /* configure disks */
+ for (i = 0; i < 16; i++) {
+ dinfo = drive_get(IF_XEN, 0, i);
+ if (!dinfo)
+ continue;
+ xen_config_dev_blk(dinfo);
+ }
+
+ /* configure nics */
+ for (i = 0; i < nb_nics; i++) {
+ if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen"))
+ continue;
+ xen_config_dev_nic(nd_table + i);
+ }
+
+ /* config cleanup hook */
+ atexit(xen_config_cleanup);
+
+ /* setup framebuffer */
+ xen_init_display(xen_domid);
+}
+
+static QEMUMachine xenpv_machine = {
+ .name = "xenpv",
+ .desc = "Xen Para-virtualized PC",
+ .init = xen_init_pv,
+ .max_cpus = 1,
+};
+
+static void xenpv_machine_init(void)
+{
+ qemu_register_machine(&xenpv_machine);
+}
+
+machine_init(xenpv_machine_init);
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
new file mode 100644
index 0000000..08055b8
--- /dev/null
+++ b/hw/xen_nic.c
@@ -0,0 +1,421 @@
+/*
+ * xen paravirt network card backend
+ *
+ * (c) Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <xs.h>
+#include <xenctrl.h>
+#include <xen/io/xenbus.h>
+#include <xen/io/netif.h>
+
+#include "hw.h"
+#include "net.h"
+#include "net/checksum.h"
+#include "net/util.h"
+#include "qemu-char.h"
+#include "xen_backend.h"
+
+/* ------------------------------------------------------------- */
+
+struct XenNetDev {
+ struct XenDevice xendev; /* must be first */
+ char *mac;
+ int tx_work;
+ int tx_ring_ref;
+ int rx_ring_ref;
+ struct netif_tx_sring *txs;
+ struct netif_rx_sring *rxs;
+ netif_tx_back_ring_t tx_ring;
+ netif_rx_back_ring_t rx_ring;
+ NICConf conf;
+ NICState *nic;
+};
+
+/* ------------------------------------------------------------- */
+
+static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
+{
+ RING_IDX i = netdev->tx_ring.rsp_prod_pvt;
+ netif_tx_response_t *resp;
+ int notify;
+
+ resp = RING_GET_RESPONSE(&netdev->tx_ring, i);
+ resp->id = txp->id;
+ resp->status = st;
+
+#if 0
+ if (txp->flags & NETTXF_extra_info)
+ RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL;
+#endif
+
+ netdev->tx_ring.rsp_prod_pvt = ++i;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
+ if (notify)
+ xen_be_send_notify(&netdev->xendev);
+
+ if (i == netdev->tx_ring.req_cons) {
+ int more_to_do;
+ RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do);
+ if (more_to_do)
+ netdev->tx_work++;
+ }
+}
+
+static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end)
+{
+#if 0
+ /*
+ * Hmm, why netback fails everything in the ring?
+ * Should we do that even when not supporting SG and TSO?
+ */
+ RING_IDX cons = netdev->tx_ring.req_cons;
+
+ do {
+ make_tx_response(netif, txp, NETIF_RSP_ERROR);
+ if (cons >= end)
+ break;
+ txp = RING_GET_REQUEST(&netdev->tx_ring, cons++);
+ } while (1);
+ netdev->tx_ring.req_cons = cons;
+ netif_schedule_work(netif);
+ netif_put(netif);
+#else
+ net_tx_response(netdev, txp, NETIF_RSP_ERROR);
+#endif
+}
+
+static void net_tx_packets(struct XenNetDev *netdev)
+{
+ netif_tx_request_t txreq;
+ RING_IDX rc, rp;
+ void *page;
+ void *tmpbuf = NULL;
+
+ for (;;) {
+ rc = netdev->tx_ring.req_cons;
+ rp = netdev->tx_ring.sring->req_prod;
+ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ while ((rc != rp)) {
+ if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc))
+ break;
+ memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
+ netdev->tx_ring.req_cons = ++rc;
+
+#if 1
+ /* should not happen in theory, we don't announce the *
+ * feature-{sg,gso,whatelse} flags in xenstore (yet?) */
+ if (txreq.flags & NETTXF_extra_info) {
+ xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n");
+ net_tx_error(netdev, &txreq, rc);
+ continue;
+ }
+ if (txreq.flags & NETTXF_more_data) {
+ xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n");
+ net_tx_error(netdev, &txreq, rc);
+ continue;
+ }
+#endif
+
+ if (txreq.size < 14) {
+ xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size);
+ net_tx_error(netdev, &txreq, rc);
+ continue;
+ }
+
+ if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) {
+ xen_be_printf(&netdev->xendev, 0, "error: page crossing\n");
+ net_tx_error(netdev, &txreq, rc);
+ continue;
+ }
+
+ xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n",
+ txreq.gref, txreq.offset, txreq.size, txreq.flags,
+ (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "",
+ (txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
+ (txreq.flags & NETTXF_more_data) ? " more_data" : "",
+ (txreq.flags & NETTXF_extra_info) ? " extra_info" : "");
+
+ page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ netdev->xendev.dom,
+ txreq.gref, PROT_READ);
+ if (page == NULL) {
+ xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n",
+ txreq.gref);
+ net_tx_error(netdev, &txreq, rc);
+ continue;
+ }
+ if (txreq.flags & NETTXF_csum_blank) {
+ /* have read-only mapping -> can't fill checksum in-place */
+ if (!tmpbuf)
+ tmpbuf = qemu_malloc(XC_PAGE_SIZE);
+ memcpy(tmpbuf, page + txreq.offset, txreq.size);
+ net_checksum_calculate(tmpbuf, txreq.size);
+ qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size);
+ } else {
+ qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size);
+ }
+ xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
+ net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
+ }
+ if (!netdev->tx_work)
+ break;
+ netdev->tx_work = 0;
+ }
+ qemu_free(tmpbuf);
+}
+
+/* ------------------------------------------------------------- */
+
+static void net_rx_response(struct XenNetDev *netdev,
+ netif_rx_request_t *req, int8_t st,
+ uint16_t offset, uint16_t size,
+ uint16_t flags)
+{
+ RING_IDX i = netdev->rx_ring.rsp_prod_pvt;
+ netif_rx_response_t *resp;
+ int notify;
+
+ resp = RING_GET_RESPONSE(&netdev->rx_ring, i);
+ resp->offset = offset;
+ resp->flags = flags;
+ resp->id = req->id;
+ resp->status = (int16_t)size;
+ if (st < 0)
+ resp->status = (int16_t)st;
+
+ xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n",
+ i, resp->status, resp->flags);
+
+ netdev->rx_ring.rsp_prod_pvt = ++i;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
+ if (notify)
+ xen_be_send_notify(&netdev->xendev);
+}
+
+#define NET_IP_ALIGN 2
+
+static int net_rx_ok(VLANClientState *nc)
+{
+ struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque;
+ RING_IDX rc, rp;
+
+ if (netdev->xendev.be_state != XenbusStateConnected)
+ return 0;
+
+ rc = netdev->rx_ring.req_cons;
+ rp = netdev->rx_ring.sring->req_prod;
+ xen_rmb();
+
+ if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
+ xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n",
+ __FUNCTION__, rc, rp);
+ return 0;
+ }
+ return 1;
+}
+
+static ssize_t net_rx_packet(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque;
+ netif_rx_request_t rxreq;
+ RING_IDX rc, rp;
+ void *page;
+
+ if (netdev->xendev.be_state != XenbusStateConnected)
+ return -1;
+
+ rc = netdev->rx_ring.req_cons;
+ rp = netdev->rx_ring.sring->req_prod;
+ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
+ xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n");
+ return -1;
+ }
+ if (size > XC_PAGE_SIZE - NET_IP_ALIGN) {
+ xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
+ (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN);
+ return -1;
+ }
+
+ memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
+ netdev->rx_ring.req_cons = ++rc;
+
+ page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ netdev->xendev.dom,
+ rxreq.gref, PROT_WRITE);
+ if (page == NULL) {
+ xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n",
+ rxreq.gref);
+ net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
+ return -1;
+ }
+ memcpy(page + NET_IP_ALIGN, buf, size);
+ xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
+ net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
+
+ return size;
+}
+
+/* ------------------------------------------------------------- */
+
+static NetClientInfo net_xen_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = net_rx_ok,
+ .receive = net_rx_packet,
+};
+
+static int net_init(struct XenDevice *xendev)
+{
+ struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+
+ /* read xenstore entries */
+ if (netdev->mac == NULL)
+ netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac");
+
+ /* do we have all we need? */
+ if (netdev->mac == NULL)
+ return -1;
+
+ if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0)
+ return -1;
+
+ netdev->conf.vlan = qemu_find_vlan(netdev->xendev.dev, 1);
+ netdev->conf.peer = NULL;
+
+ netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
+ "xen", NULL, netdev);
+
+ snprintf(netdev->nic->nc.info_str, sizeof(netdev->nic->nc.info_str),
+ "nic: xenbus vif macaddr=%s", netdev->mac);
+
+ /* fill info */
+ xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1);
+ xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0);
+
+ return 0;
+}
+
+static int net_connect(struct XenDevice *xendev)
+{
+ struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+ int rx_copy;
+
+ if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref",
+ &netdev->tx_ring_ref) == -1)
+ return -1;
+ if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref",
+ &netdev->rx_ring_ref) == -1)
+ return 1;
+ if (xenstore_read_fe_int(&netdev->xendev, "event-channel",
+ &netdev->xendev.remote_port) == -1)
+ return -1;
+
+ if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1)
+ rx_copy = 0;
+ if (rx_copy == 0) {
+ xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n");
+ return -1;
+ }
+
+ netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ netdev->xendev.dom,
+ netdev->tx_ring_ref,
+ PROT_READ | PROT_WRITE);
+ netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+ netdev->xendev.dom,
+ netdev->rx_ring_ref,
+ PROT_READ | PROT_WRITE);
+ if (!netdev->txs || !netdev->rxs)
+ return -1;
+ BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE);
+ BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE);
+
+ xen_be_bind_evtchn(&netdev->xendev);
+
+ xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, "
+ "remote port %d, local port %d\n",
+ netdev->tx_ring_ref, netdev->rx_ring_ref,
+ netdev->xendev.remote_port, netdev->xendev.local_port);
+
+ net_tx_packets(netdev);
+ return 0;
+}
+
+static void net_disconnect(struct XenDevice *xendev)
+{
+ struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+
+ xen_be_unbind_evtchn(&netdev->xendev);
+
+ if (netdev->txs) {
+ xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1);
+ netdev->txs = NULL;
+ }
+ if (netdev->rxs) {
+ xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1);
+ netdev->rxs = NULL;
+ }
+ if (netdev->nic) {
+ qemu_del_vlan_client(&netdev->nic->nc);
+ netdev->nic = NULL;
+ }
+}
+
+static void net_event(struct XenDevice *xendev)
+{
+ struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+ net_tx_packets(netdev);
+}
+
+static int net_free(struct XenDevice *xendev)
+{
+ struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+
+ qemu_free(netdev->mac);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+struct XenDevOps xen_netdev_ops = {
+ .size = sizeof(struct XenNetDev),
+ .flags = DEVOPS_FLAG_NEED_GNTDEV,
+ .init = net_init,
+ .connect = net_connect,
+ .event = net_event,
+ .disconnect = net_disconnect,
+ .free = net_free,
+};
diff --git a/hw/xenfb.c b/hw/xenfb.c
new file mode 100644
index 0000000..da5297b
--- /dev/null
+++ b/hw/xenfb.c
@@ -0,0 +1,1013 @@
+/*
+ * xen paravirt framebuffer backend
+ *
+ * Copyright IBM, Corp. 2005-2006
+ * Copyright Red Hat, Inc. 2006-2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>,
+ * Markus Armbruster <armbru@redhat.com>,
+ * Daniel P. Berrange <berrange@redhat.com>,
+ * Pat Campbell <plc@novell.com>,
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <xs.h>
+#include <xenctrl.h>
+#include <xen/event_channel.h>
+#include <xen/io/xenbus.h>
+#include <xen/io/fbif.h>
+#include <xen/io/kbdif.h>
+#include <xen/io/protocols.h>
+
+#include "hw.h"
+#include "sysemu.h"
+#include "console.h"
+#include "qemu-char.h"
+#include "xen_backend.h"
+
+#ifndef BTN_LEFT
+#define BTN_LEFT 0x110 /* from <linux/input.h> */
+#endif
+
+/* -------------------------------------------------------------------- */
+
+struct common {
+ struct XenDevice xendev; /* must be first */
+ void *page;
+ DisplayState *ds;
+};
+
+struct XenInput {
+ struct common c;
+ int abs_pointer_wanted; /* Whether guest supports absolute pointer */
+ int button_state; /* Last seen pointer button state */
+ int extended;
+ QEMUPutMouseEntry *qmouse;
+};
+
+#define UP_QUEUE 8
+
+struct XenFB {
+ struct common c;
+ size_t fb_len;
+ int row_stride;
+ int depth;
+ int width;
+ int height;
+ int offset;
+ void *pixels;
+ int fbpages;
+ int feature_update;
+ int refresh_period;
+ int bug_trigger;
+ int have_console;
+ int do_resize;
+
+ struct {
+ int x,y,w,h;
+ } up_rects[UP_QUEUE];
+ int up_count;
+ int up_fullscreen;
+};
+
+/* -------------------------------------------------------------------- */
+
+static int common_bind(struct common *c)
+{
+ int mfn;
+
+ if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1)
+ return -1;
+ if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
+ return -1;
+
+ c->page = xc_map_foreign_range(xen_xc, c->xendev.dom,
+ XC_PAGE_SIZE,
+ PROT_READ | PROT_WRITE, mfn);
+ if (c->page == NULL)
+ return -1;
+
+ xen_be_bind_evtchn(&c->xendev);
+ xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n",
+ mfn, c->xendev.remote_port, c->xendev.local_port);
+
+ return 0;
+}
+
+static void common_unbind(struct common *c)
+{
+ xen_be_unbind_evtchn(&c->xendev);
+ if (c->page) {
+ munmap(c->page, XC_PAGE_SIZE);
+ c->page = NULL;
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+#if 0
+/*
+ * These two tables are not needed any more, but left in here
+ * intentionally as documentation, to show how scancode2linux[]
+ * was generated.
+ *
+ * Tables to map from scancode to Linux input layer keycode.
+ * Scancodes are hardware-specific. These maps assumes a
+ * standard AT or PS/2 keyboard which is what QEMU feeds us.
+ */
+const unsigned char atkbd_set2_keycode[512] = {
+
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
+ 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
+ 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
+ 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
+ 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
+ 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
+ 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142,
+ 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
+ 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
+
+};
+
+const unsigned char atkbd_unxlate_table[128] = {
+
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+
+};
+#endif
+
+/*
+ * for (i = 0; i < 128; i++) {
+ * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
+ * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
+ * }
+ */
+static const unsigned char scancode2linux[512] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0,
+ 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0,
+ 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107,
+ 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142,
+ 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* Send an event to the keyboard frontend driver */
+static int xenfb_kbd_event(struct XenInput *xenfb,
+ union xenkbd_in_event *event)
+{
+ struct xenkbd_page *page = xenfb->c.page;
+ uint32_t prod;
+
+ if (xenfb->c.xendev.be_state != XenbusStateConnected)
+ return 0;
+ if (!page)
+ return 0;
+
+ prod = page->in_prod;
+ if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ xen_mb(); /* ensure ring space available */
+ XENKBD_IN_RING_REF(page, prod) = *event;
+ xen_wmb(); /* ensure ring contents visible */
+ page->in_prod = prod + 1;
+ return xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* Send a keyboard (or mouse button) event */
+static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode)
+{
+ union xenkbd_in_event event;
+
+ memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+ event.type = XENKBD_TYPE_KEY;
+ event.key.pressed = down ? 1 : 0;
+ event.key.keycode = keycode;
+
+ return xenfb_kbd_event(xenfb, &event);
+}
+
+/* Send a relative mouse movement event */
+static int xenfb_send_motion(struct XenInput *xenfb,
+ int rel_x, int rel_y, int rel_z)
+{
+ union xenkbd_in_event event;
+
+ memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+ event.type = XENKBD_TYPE_MOTION;
+ event.motion.rel_x = rel_x;
+ event.motion.rel_y = rel_y;
+#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207
+ event.motion.rel_z = rel_z;
+#endif
+
+ return xenfb_kbd_event(xenfb, &event);
+}
+
+/* Send an absolute mouse movement event */
+static int xenfb_send_position(struct XenInput *xenfb,
+ int abs_x, int abs_y, int z)
+{
+ union xenkbd_in_event event;
+
+ memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+ event.type = XENKBD_TYPE_POS;
+ event.pos.abs_x = abs_x;
+ event.pos.abs_y = abs_y;
+#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207
+ event.pos.abs_z = z;
+#endif
+#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208
+ event.pos.rel_z = z;
+#endif
+
+ return xenfb_kbd_event(xenfb, &event);
+}
+
+/*
+ * Send a key event from the client to the guest OS
+ * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
+ * We have to turn this into a Linux Input layer keycode.
+ *
+ * Extra complexity from the fact that with extended scancodes
+ * (like those produced by arrow keys) this method gets called
+ * twice, but we only want to send a single event. So we have to
+ * track the '0xe0' scancode state & collapse the extended keys
+ * as needed.
+ *
+ * Wish we could just send scancodes straight to the guest which
+ * already has code for dealing with this...
+ */
+static void xenfb_key_event(void *opaque, int scancode)
+{
+ struct XenInput *xenfb = opaque;
+ int down = 1;
+
+ if (scancode == 0xe0) {
+ xenfb->extended = 1;
+ return;
+ } else if (scancode & 0x80) {
+ scancode &= 0x7f;
+ down = 0;
+ }
+ if (xenfb->extended) {
+ scancode |= 0x80;
+ xenfb->extended = 0;
+ }
+ xenfb_send_key(xenfb, down, scancode2linux[scancode]);
+}
+
+/*
+ * Send a mouse event from the client to the guest OS
+ *
+ * The QEMU mouse can be in either relative, or absolute mode.
+ * Movement is sent separately from button state, which has to
+ * be encoded as virtual key events. We also don't actually get
+ * given any button up/down events, so have to track changes in
+ * the button state.
+ */
+static void xenfb_mouse_event(void *opaque,
+ int dx, int dy, int dz, int button_state)
+{
+ struct XenInput *xenfb = opaque;
+ int dw = ds_get_width(xenfb->c.ds);
+ int dh = ds_get_height(xenfb->c.ds);
+ int i;
+
+ if (xenfb->abs_pointer_wanted)
+ xenfb_send_position(xenfb,
+ dx * (dw - 1) / 0x7fff,
+ dy * (dh - 1) / 0x7fff,
+ dz);
+ else
+ xenfb_send_motion(xenfb, dx, dy, dz);
+
+ for (i = 0 ; i < 8 ; i++) {
+ int lastDown = xenfb->button_state & (1 << i);
+ int down = button_state & (1 << i);
+ if (down == lastDown)
+ continue;
+
+ if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0)
+ return;
+ }
+ xenfb->button_state = button_state;
+}
+
+static int input_init(struct XenDevice *xendev)
+{
+ struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+
+ if (!in->c.ds) {
+ xen_be_printf(xendev, 1, "ds not set (yet)\n");
+ return -1;
+ }
+
+ xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
+ return 0;
+}
+
+static int input_connect(struct XenDevice *xendev)
+{
+ struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+ int rc;
+
+ if (xenstore_read_fe_int(xendev, "request-abs-pointer",
+ &in->abs_pointer_wanted) == -1)
+ in->abs_pointer_wanted = 0;
+
+ rc = common_bind(&in->c);
+ if (rc != 0)
+ return rc;
+
+ qemu_add_kbd_event_handler(xenfb_key_event, in);
+ in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in,
+ in->abs_pointer_wanted,
+ "Xen PVFB Mouse");
+ return 0;
+}
+
+static void input_disconnect(struct XenDevice *xendev)
+{
+ struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+
+ if (in->qmouse) {
+ qemu_remove_mouse_event_handler(in->qmouse);
+ in->qmouse = NULL;
+ }
+ qemu_add_kbd_event_handler(NULL, NULL);
+ common_unbind(&in->c);
+}
+
+static void input_event(struct XenDevice *xendev)
+{
+ struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
+ struct xenkbd_page *page = xenfb->c.page;
+
+ /* We don't understand any keyboard events, so just ignore them. */
+ if (page->out_prod == page->out_cons)
+ return;
+ page->out_cons = page->out_prod;
+ xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
+{
+ uint32_t *src32 = src;
+ uint64_t *src64 = src;
+ int i;
+
+ for (i = 0; i < count; i++)
+ dst[i] = (mode == 32) ? src32[i] : src64[i];
+}
+
+static int xenfb_map_fb(struct XenFB *xenfb)
+{
+ struct xenfb_page *page = xenfb->c.page;
+ char *protocol = xenfb->c.xendev.protocol;
+ int n_fbdirs;
+ unsigned long *pgmfns = NULL;
+ unsigned long *fbmfns = NULL;
+ void *map, *pd;
+ int mode, ret = -1;
+
+ /* default to native */
+ pd = page->pd;
+ mode = sizeof(unsigned long) * 8;
+
+ if (!protocol) {
+ /*
+ * Undefined protocol, some guesswork needed.
+ *
+ * Old frontends which don't set the protocol use
+ * one page directory only, thus pd[1] must be zero.
+ * pd[1] of the 32bit struct layout and the lower
+ * 32 bits of pd[0] of the 64bit struct layout have
+ * the same location, so we can check that ...
+ */
+ uint32_t *ptr32 = NULL;
+ uint32_t *ptr64 = NULL;
+#if defined(__i386__)
+ ptr32 = (void*)page->pd;
+ ptr64 = ((void*)page->pd) + 4;
+#elif defined(__x86_64__)
+ ptr32 = ((void*)page->pd) - 4;
+ ptr64 = (void*)page->pd;
+#endif
+ if (ptr32) {
+ if (ptr32[1] == 0) {
+ mode = 32;
+ pd = ptr32;
+ } else {
+ mode = 64;
+ pd = ptr64;
+ }
+ }
+#if defined(__x86_64__)
+ } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
+ /* 64bit dom0, 32bit domU */
+ mode = 32;
+ pd = ((void*)page->pd) - 4;
+#elif defined(__i386__)
+ } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
+ /* 32bit dom0, 64bit domU */
+ mode = 64;
+ pd = ((void*)page->pd) + 4;
+#endif
+ }
+
+ if (xenfb->pixels) {
+ munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
+ xenfb->pixels = NULL;
+ }
+
+ xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+ n_fbdirs = xenfb->fbpages * mode / 8;
+ n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+
+ pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs);
+ fbmfns = qemu_mallocz(sizeof(unsigned long) * xenfb->fbpages);
+
+ xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
+ map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
+ PROT_READ, pgmfns, n_fbdirs);
+ if (map == NULL)
+ goto out;
+ xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
+ munmap(map, n_fbdirs * XC_PAGE_SIZE);
+
+ xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
+ PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages);
+ if (xenfb->pixels == NULL)
+ goto out;
+
+ ret = 0; /* all is fine */
+
+out:
+ qemu_free(pgmfns);
+ qemu_free(fbmfns);
+ return ret;
+}
+
+static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
+ int width, int height, int depth,
+ size_t fb_len, int offset, int row_stride)
+{
+ size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
+ size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
+ size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
+ size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
+ int max_width, max_height;
+
+ if (fb_len_lim > fb_len_max) {
+ xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n",
+ fb_len_lim, fb_len_max);
+ fb_len_lim = fb_len_max;
+ }
+ if (fb_len_lim && fb_len > fb_len_lim) {
+ xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n",
+ fb_len, fb_len_lim);
+ fb_len = fb_len_lim;
+ }
+ if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
+ xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n",
+ depth);
+ return -1;
+ }
+ if (row_stride <= 0 || row_stride > fb_len) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride);
+ return -1;
+ }
+ max_width = row_stride / (depth / 8);
+ if (width < 0 || width > max_width) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n",
+ width, max_width);
+ width = max_width;
+ }
+ if (offset < 0 || offset >= fb_len) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n",
+ offset, fb_len - 1);
+ return -1;
+ }
+ max_height = (fb_len - offset) / row_stride;
+ if (height < 0 || height > max_height) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n",
+ height, max_height);
+ height = max_height;
+ }
+ xenfb->fb_len = fb_len;
+ xenfb->row_stride = row_stride;
+ xenfb->depth = depth;
+ xenfb->width = width;
+ xenfb->height = height;
+ xenfb->offset = offset;
+ xenfb->up_fullscreen = 1;
+ xenfb->do_resize = 1;
+ xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n",
+ width, height, depth, offset, row_stride);
+ return 0;
+}
+
+/* A convenient function for munging pixels between different depths */
+#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
+ for (line = y ; line < (y+h) ; line++) { \
+ SRC_T *src = (SRC_T *)(xenfb->pixels \
+ + xenfb->offset \
+ + (line * xenfb->row_stride) \
+ + (x * xenfb->depth / 8)); \
+ DST_T *dst = (DST_T *)(data \
+ + (line * linesize) \
+ + (x * bpp / 8)); \
+ int col; \
+ const int RSS = 32 - (RSB + GSB + BSB); \
+ const int GSS = 32 - (GSB + BSB); \
+ const int BSS = 32 - (BSB); \
+ const uint32_t RSM = (~0U) << (32 - RSB); \
+ const uint32_t GSM = (~0U) << (32 - GSB); \
+ const uint32_t BSM = (~0U) << (32 - BSB); \
+ const int RDS = 32 - (RDB + GDB + BDB); \
+ const int GDS = 32 - (GDB + BDB); \
+ const int BDS = 32 - (BDB); \
+ const uint32_t RDM = (~0U) << (32 - RDB); \
+ const uint32_t GDM = (~0U) << (32 - GDB); \
+ const uint32_t BDM = (~0U) << (32 - BDB); \
+ for (col = x ; col < (x+w) ; col++) { \
+ uint32_t spix = *src; \
+ *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
+ (((spix << GSS) & GSM & GDM) >> GDS) | \
+ (((spix << BSS) & BSM & BDM) >> BDS); \
+ src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
+ dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
+ } \
+ }
+
+
+/*
+ * This copies data from the guest framebuffer region, into QEMU's
+ * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
+ * uses something else we must convert and copy, otherwise we can
+ * supply the buffer directly and no thing here.
+ */
+static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
+{
+ int line, oops = 0;
+ int bpp = ds_get_bits_per_pixel(xenfb->c.ds);
+ int linesize = ds_get_linesize(xenfb->c.ds);
+ uint8_t *data = ds_get_data(xenfb->c.ds);
+
+ if (!is_buffer_shared(xenfb->c.ds->surface)) {
+ switch (xenfb->depth) {
+ case 8:
+ if (bpp == 16) {
+ BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
+ } else if (bpp == 32) {
+ BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
+ } else {
+ oops = 1;
+ }
+ break;
+ case 24:
+ if (bpp == 16) {
+ BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
+ } else if (bpp == 32) {
+ BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
+ } else {
+ oops = 1;
+ }
+ break;
+ default:
+ oops = 1;
+ }
+ }
+ if (oops) /* should not happen */
+ xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
+ __FUNCTION__, xenfb->depth, bpp);
+
+ dpy_update(xenfb->c.ds, x, y, w, h);
+}
+
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+static int xenfb_queue_full(struct XenFB *xenfb)
+{
+ struct xenfb_page *page = xenfb->c.page;
+ uint32_t cons, prod;
+
+ if (!page)
+ return 1;
+
+ prod = page->in_prod;
+ cons = page->in_cons;
+ return prod - cons == XENFB_IN_RING_LEN;
+}
+
+static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event)
+{
+ uint32_t prod;
+ struct xenfb_page *page = xenfb->c.page;
+
+ prod = page->in_prod;
+ /* caller ensures !xenfb_queue_full() */
+ xen_mb(); /* ensure ring space available */
+ XENFB_IN_RING_REF(page, prod) = *event;
+ xen_wmb(); /* ensure ring contents visible */
+ page->in_prod = prod + 1;
+
+ xen_be_send_notify(&xenfb->c.xendev);
+}
+
+static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
+{
+ union xenfb_in_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.type = XENFB_TYPE_REFRESH_PERIOD;
+ event.refresh_period.period = period;
+ xenfb_send_event(xenfb, &event);
+}
+#endif
+
+/*
+ * Periodic update of display.
+ * Also transmit the refresh interval to the frontend.
+ *
+ * Never ever do any qemu display operations
+ * (resize, screen update) outside this function.
+ * Our screen might be inactive. When asked for
+ * an update we know it is active.
+ */
+static void xenfb_update(void *opaque)
+{
+ struct XenFB *xenfb = opaque;
+ int i;
+
+ if (xenfb->c.xendev.be_state != XenbusStateConnected)
+ return;
+
+ if (xenfb->feature_update) {
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+ struct DisplayChangeListener *l;
+ int period = 99999999;
+ int idle = 1;
+
+ if (xenfb_queue_full(xenfb))
+ return;
+
+ for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) {
+ if (l->idle)
+ continue;
+ idle = 0;
+ if (!l->gui_timer_interval) {
+ if (period > GUI_REFRESH_INTERVAL)
+ period = GUI_REFRESH_INTERVAL;
+ } else {
+ if (period > l->gui_timer_interval)
+ period = l->gui_timer_interval;
+ }
+ }
+ if (idle)
+ period = XENFB_NO_REFRESH;
+
+ if (xenfb->refresh_period != period) {
+ xenfb_send_refresh_period(xenfb, period);
+ xenfb->refresh_period = period;
+ xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period);
+ }
+#else
+ ; /* nothing */
+#endif
+ } else {
+ /* we don't get update notifications, thus use the
+ * sledge hammer approach ... */
+ xenfb->up_fullscreen = 1;
+ }
+
+ /* resize if needed */
+ if (xenfb->do_resize) {
+ xenfb->do_resize = 0;
+ switch (xenfb->depth) {
+ case 16:
+ case 32:
+ /* console.c supported depth -> buffer can be used directly */
+ qemu_free_displaysurface(xenfb->c.ds);
+ xenfb->c.ds->surface = qemu_create_displaysurface_from
+ (xenfb->width, xenfb->height, xenfb->depth,
+ xenfb->row_stride, xenfb->pixels + xenfb->offset);
+ break;
+ default:
+ /* we must convert stuff */
+ qemu_resize_displaysurface(xenfb->c.ds, xenfb->width, xenfb->height);
+ break;
+ }
+ xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n",
+ xenfb->width, xenfb->height, xenfb->depth,
+ is_buffer_shared(xenfb->c.ds->surface) ? " (shared)" : "");
+ dpy_resize(xenfb->c.ds);
+ xenfb->up_fullscreen = 1;
+ }
+
+ /* run queued updates */
+ if (xenfb->up_fullscreen) {
+ xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n");
+ xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
+ } else if (xenfb->up_count) {
+ xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count);
+ for (i = 0; i < xenfb->up_count; i++)
+ xenfb_guest_copy(xenfb,
+ xenfb->up_rects[i].x,
+ xenfb->up_rects[i].y,
+ xenfb->up_rects[i].w,
+ xenfb->up_rects[i].h);
+ } else {
+ xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n");
+ }
+ xenfb->up_count = 0;
+ xenfb->up_fullscreen = 0;
+}
+
+/* QEMU display state changed, so refresh the framebuffer copy */
+static void xenfb_invalidate(void *opaque)
+{
+ struct XenFB *xenfb = opaque;
+ xenfb->up_fullscreen = 1;
+}
+
+static void xenfb_handle_events(struct XenFB *xenfb)
+{
+ uint32_t prod, cons;
+ struct xenfb_page *page = xenfb->c.page;
+
+ prod = page->out_prod;
+ if (prod == page->out_cons)
+ return;
+ xen_rmb(); /* ensure we see ring contents up to prod */
+ for (cons = page->out_cons; cons != prod; cons++) {
+ union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+ int x, y, w, h;
+
+ switch (event->type) {
+ case XENFB_TYPE_UPDATE:
+ if (xenfb->up_count == UP_QUEUE)
+ xenfb->up_fullscreen = 1;
+ if (xenfb->up_fullscreen)
+ break;
+ x = MAX(event->update.x, 0);
+ y = MAX(event->update.y, 0);
+ w = MIN(event->update.width, xenfb->width - x);
+ h = MIN(event->update.height, xenfb->height - y);
+ if (w < 0 || h < 0) {
+ xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n");
+ break;
+ }
+ if (x != event->update.x ||
+ y != event->update.y ||
+ w != event->update.width ||
+ h != event->update.height) {
+ xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n");
+ }
+ if (w == xenfb->width && h > xenfb->height / 2) {
+ /* scroll detector: updated more than 50% of the lines,
+ * don't bother keeping track of the rectangles then */
+ xenfb->up_fullscreen = 1;
+ } else {
+ xenfb->up_rects[xenfb->up_count].x = x;
+ xenfb->up_rects[xenfb->up_count].y = y;
+ xenfb->up_rects[xenfb->up_count].w = w;
+ xenfb->up_rects[xenfb->up_count].h = h;
+ xenfb->up_count++;
+ }
+ break;
+#ifdef XENFB_TYPE_RESIZE
+ case XENFB_TYPE_RESIZE:
+ if (xenfb_configure_fb(xenfb, xenfb->fb_len,
+ event->resize.width,
+ event->resize.height,
+ event->resize.depth,
+ xenfb->fb_len,
+ event->resize.offset,
+ event->resize.stride) < 0)
+ break;
+ xenfb_invalidate(xenfb);
+ break;
+#endif
+ }
+ }
+ xen_mb(); /* ensure we're done with ring contents */
+ page->out_cons = cons;
+}
+
+static int fb_init(struct XenDevice *xendev)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+ fb->refresh_period = -1;
+
+#ifdef XENFB_TYPE_RESIZE
+ xenstore_write_be_int(xendev, "feature-resize", 1);
+#endif
+ return 0;
+}
+
+static int fb_connect(struct XenDevice *xendev)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+ struct xenfb_page *fb_page;
+ int videoram;
+ int rc;
+
+ if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1)
+ videoram = 0;
+
+ rc = common_bind(&fb->c);
+ if (rc != 0)
+ return rc;
+
+ fb_page = fb->c.page;
+ rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U,
+ fb_page->width, fb_page->height, fb_page->depth,
+ fb_page->mem_length, 0, fb_page->line_length);
+ if (rc != 0)
+ return rc;
+
+ rc = xenfb_map_fb(fb);
+ if (rc != 0)
+ return rc;
+
+#if 0 /* handled in xen_init_display() for now */
+ if (!fb->have_console) {
+ fb->c.ds = graphic_console_init(xenfb_update,
+ xenfb_invalidate,
+ NULL,
+ NULL,
+ fb);
+ fb->have_console = 1;
+ }
+#endif
+
+ if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
+ fb->feature_update = 0;
+ if (fb->feature_update)
+ xenstore_write_be_int(xendev, "request-update", 1);
+
+ xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
+ fb->feature_update, videoram);
+ return 0;
+}
+
+static void fb_disconnect(struct XenDevice *xendev)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+ /*
+ * FIXME: qemu can't un-init gfx display (yet?).
+ * Replacing the framebuffer with anonymous shared memory
+ * instead. This releases the guest pages and keeps qemu happy.
+ */
+ fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
+ -1, 0);
+ common_unbind(&fb->c);
+ fb->feature_update = 0;
+ fb->bug_trigger = 0;
+}
+
+static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+ /*
+ * Set state to Connected *again* once the frontend switched
+ * to connected. We must trigger the watch a second time to
+ * workaround a frontend bug.
+ */
+ if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 &&
+ xendev->fe_state == XenbusStateConnected &&
+ xendev->be_state == XenbusStateConnected) {
+ xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n");
+ xen_be_set_state(xendev, XenbusStateConnected);
+ fb->bug_trigger = 1; /* only once */
+ }
+}
+
+static void fb_event(struct XenDevice *xendev)
+{
+ struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
+
+ xenfb_handle_events(xenfb);
+ xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct XenDevOps xen_kbdmouse_ops = {
+ .size = sizeof(struct XenInput),
+ .init = input_init,
+ .connect = input_connect,
+ .disconnect = input_disconnect,
+ .event = input_event,
+};
+
+struct XenDevOps xen_framebuffer_ops = {
+ .size = sizeof(struct XenFB),
+ .init = fb_init,
+ .connect = fb_connect,
+ .disconnect = fb_disconnect,
+ .event = fb_event,
+ .frontend_changed = fb_frontend_changed,
+};
+
+/*
+ * FIXME/TODO: Kill this.
+ * Temporary needed while DisplayState reorganization is in flight.
+ */
+void xen_init_display(int domid)
+{
+ struct XenDevice *xfb, *xin;
+ struct XenFB *fb;
+ struct XenInput *in;
+ int i = 0;
+
+wait_more:
+ i++;
+ main_loop_wait(true);
+ xfb = xen_be_find_xendev("vfb", domid, 0);
+ xin = xen_be_find_xendev("vkbd", domid, 0);
+ if (!xfb || !xin) {
+ if (i < 256) {
+ usleep(10000);
+ goto wait_more;
+ }
+ xen_be_printf(NULL, 1, "displaystate setup failed\n");
+ return;
+ }
+
+ /* vfb */
+ fb = container_of(xfb, struct XenFB, c.xendev);
+ fb->c.ds = graphic_console_init(xenfb_update,
+ xenfb_invalidate,
+ NULL,
+ NULL,
+ fb);
+ fb->have_console = 1;
+
+ /* vkbd */
+ in = container_of(xin, struct XenInput, c.xendev);
+ in->c.ds = fb->c.ds;
+
+ /* retry ->init() */
+ xen_be_check_state(xin);
+ xen_be_check_state(xfb);
+}
diff --git a/hw/xilinx.h b/hw/xilinx.h
new file mode 100644
index 0000000..705ff5b
--- /dev/null
+++ b/hw/xilinx.h
@@ -0,0 +1,50 @@
+
+/* OPB Interrupt Controller. */
+qemu_irq *microblaze_pic_init_cpu(CPUState *env);
+
+static inline DeviceState *
+xilinx_intc_create(target_phys_addr_t base, qemu_irq irq, int kind_of_intr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "xilinx,intc");
+ qdev_prop_set_uint32(dev, "kind-of-intr", kind_of_intr);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* OPB Timer/Counter. */
+static inline DeviceState *
+xilinx_timer_create(target_phys_addr_t base, qemu_irq irq, int nr, int freq)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "xilinx,timer");
+ qdev_prop_set_uint32(dev, "nr-timers", nr);
+ qdev_prop_set_uint32(dev, "frequency", freq);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* XPS Ethernet Lite MAC. */
+static inline DeviceState *
+xilinx_ethlite_create(NICInfo *nd, target_phys_addr_t base, qemu_irq irq,
+ int txpingpong, int rxpingpong)
+{
+ DeviceState *dev;
+
+ qemu_check_nic_model(nd, "xilinx-ethlite");
+
+ dev = qdev_create(NULL, "xilinx,ethlite");
+ qdev_set_nic_properties(dev, nd);
+ qdev_prop_set_uint32(dev, "txpingpong", txpingpong);
+ qdev_prop_set_uint32(dev, "rxpingpong", rxpingpong);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c
new file mode 100644
index 0000000..54b57d7
--- /dev/null
+++ b/hw/xilinx_ethlite.c
@@ -0,0 +1,254 @@
+/*
+ * QEMU model of the Xilinx Ethernet Lite MAC.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "net.h"
+
+#define D(x)
+#define R_TX_BUF0 0
+#define R_TX_LEN0 (0x07f4 / 4)
+#define R_TX_GIE0 (0x07f8 / 4)
+#define R_TX_CTRL0 (0x07fc / 4)
+#define R_TX_BUF1 (0x0800 / 4)
+#define R_TX_LEN1 (0x0ff4 / 4)
+#define R_TX_CTRL1 (0x0ffc / 4)
+
+#define R_RX_BUF0 (0x1000 / 4)
+#define R_RX_CTRL0 (0x17fc / 4)
+#define R_RX_BUF1 (0x1800 / 4)
+#define R_RX_CTRL1 (0x1ffc / 4)
+#define R_MAX (0x2000 / 4)
+
+#define GIE_GIE 0x80000000
+
+#define CTRL_I 0x8
+#define CTRL_P 0x2
+#define CTRL_S 0x1
+
+struct xlx_ethlite
+{
+ SysBusDevice busdev;
+ qemu_irq irq;
+ NICState *nic;
+ NICConf conf;
+
+ uint32_t c_tx_pingpong;
+ uint32_t c_rx_pingpong;
+ unsigned int txbuf;
+ unsigned int rxbuf;
+
+ uint32_t regs[R_MAX];
+};
+
+static inline void eth_pulse_irq(struct xlx_ethlite *s)
+{
+ /* Only the first gie reg is active. */
+ if (s->regs[R_TX_GIE0] & GIE_GIE) {
+ qemu_irq_pulse(s->irq);
+ }
+}
+
+static uint32_t eth_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct xlx_ethlite *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+
+ switch (addr)
+ {
+ case R_TX_GIE0:
+ case R_TX_LEN0:
+ case R_TX_LEN1:
+ case R_TX_CTRL1:
+ case R_TX_CTRL0:
+ case R_RX_CTRL1:
+ case R_RX_CTRL0:
+ r = s->regs[addr];
+ D(qemu_log("%s %x=%x\n", __func__, addr * 4, r));
+ break;
+
+ /* Rx packet data is endian fixed at the way into the rx rams. This
+ * speeds things up because the ethlite MAC does not have a len
+ * register. That means the CPU will issue MMIO reads for the entire
+ * 2k rx buffer even for small packets.
+ */
+ default:
+ r = s->regs[addr];
+ break;
+ }
+ return r;
+}
+
+static void
+eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct xlx_ethlite *s = opaque;
+ unsigned int base = 0;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_TX_CTRL0:
+ case R_TX_CTRL1:
+ if (addr == R_TX_CTRL1)
+ base = 0x800 / 4;
+
+ D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
+ if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
+ qemu_send_packet(&s->nic->nc,
+ (void *) &s->regs[base],
+ s->regs[base + R_TX_LEN0]);
+ D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
+ if (s->regs[base + R_TX_CTRL0] & CTRL_I)
+ eth_pulse_irq(s);
+ } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
+ memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6);
+ if (s->regs[base + R_TX_CTRL0] & CTRL_I)
+ eth_pulse_irq(s);
+ }
+
+ /* We are fast and get ready pretty much immediately so
+ we actually never flip the S nor P bits to one. */
+ s->regs[addr] = value & ~(CTRL_P | CTRL_S);
+ break;
+
+ /* Keep these native. */
+ case R_TX_LEN0:
+ case R_TX_LEN1:
+ case R_TX_GIE0:
+ case R_RX_CTRL0:
+ case R_RX_CTRL1:
+ D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
+ s->regs[addr] = value;
+ break;
+
+ /* Packet data, make sure it stays BE. */
+ default:
+ s->regs[addr] = cpu_to_be32(value);
+ break;
+ }
+}
+
+static CPUReadMemoryFunc * const eth_read[] = {
+ NULL, NULL, &eth_readl,
+};
+
+static CPUWriteMemoryFunc * const eth_write[] = {
+ NULL, NULL, &eth_writel,
+};
+
+static int eth_can_rx(VLANClientState *nc)
+{
+ struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ int r;
+ r = !(s->regs[R_RX_CTRL0] & CTRL_S);
+ return r;
+}
+
+static ssize_t eth_rx(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque;
+ unsigned int rxbase = s->rxbuf * (0x800 / 4);
+ int i;
+
+ /* DA filter. */
+ if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6))
+ return size;
+
+ if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) {
+ D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0]));
+ return -1;
+ }
+
+ D(qemu_log("%s %d rxbase=%x\n", __func__, size, rxbase));
+ memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size);
+
+ /* Bring it into host endianess. */
+ for (i = 0; i < ((size + 3) / 4); i++) {
+ uint32_t d = s->regs[rxbase + R_RX_BUF0 + i];
+ s->regs[rxbase + R_RX_BUF0 + i] = be32_to_cpu(d);
+ }
+
+ s->regs[rxbase + R_RX_CTRL0] |= CTRL_S;
+ if (s->regs[rxbase + R_RX_CTRL0] & CTRL_I)
+ eth_pulse_irq(s);
+
+ /* If c_rx_pingpong was set flip buffers. */
+ s->rxbuf ^= s->c_rx_pingpong;
+ return size;
+}
+
+static void eth_cleanup(VLANClientState *nc)
+{
+ struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_xilinx_ethlite_info = {
+ .type = NET_CLIENT_TYPE_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_rx,
+ .receive = eth_rx,
+ .cleanup = eth_cleanup,
+};
+
+static int xilinx_ethlite_init(SysBusDevice *dev)
+{
+ struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev);
+ int regs;
+
+ sysbus_init_irq(dev, &s->irq);
+ s->rxbuf = 0;
+
+ regs = cpu_register_io_memory(eth_read, eth_write, s, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, R_MAX * 4, regs);
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
+ dev->qdev.info->name, dev->qdev.id, s);
+ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
+ return 0;
+}
+
+static SysBusDeviceInfo xilinx_ethlite_info = {
+ .init = xilinx_ethlite_init,
+ .qdev.name = "xilinx,ethlite",
+ .qdev.size = sizeof(struct xlx_ethlite),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("txpingpong", struct xlx_ethlite, c_tx_pingpong, 1),
+ DEFINE_PROP_UINT32("rxpingpong", struct xlx_ethlite, c_rx_pingpong, 1),
+ DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xilinx_ethlite_register(void)
+{
+ sysbus_register_withprop(&xilinx_ethlite_info);
+}
+
+device_init(xilinx_ethlite_register)
diff --git a/hw/xilinx_intc.c b/hw/xilinx_intc.c
new file mode 100644
index 0000000..cb72d5a
--- /dev/null
+++ b/hw/xilinx_intc.c
@@ -0,0 +1,176 @@
+/*
+ * QEMU Xilinx OPB Interrupt Controller.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+
+#define D(x)
+
+#define R_ISR 0
+#define R_IPR 1
+#define R_IER 2
+#define R_IAR 3
+#define R_SIE 4
+#define R_CIE 5
+#define R_IVR 6
+#define R_MER 7
+#define R_MAX 8
+
+struct xlx_pic
+{
+ SysBusDevice busdev;
+ qemu_irq parent_irq;
+
+ /* Configuration reg chosen at synthesis-time. QEMU populates
+ the bits at board-setup. */
+ uint32_t c_kind_of_intr;
+
+ /* Runtime control registers. */
+ uint32_t regs[R_MAX];
+};
+
+static void update_irq(struct xlx_pic *p)
+{
+ uint32_t i;
+ /* Update the pending register. */
+ p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER];
+
+ /* Update the vector register. */
+ for (i = 0; i < 32; i++) {
+ if (p->regs[R_IPR] & (1 << i))
+ break;
+ }
+ if (i == 32)
+ i = ~0;
+
+ p->regs[R_IVR] = i;
+ if ((p->regs[R_MER] & 1) && p->regs[R_IPR]) {
+ qemu_irq_raise(p->parent_irq);
+ } else {
+ qemu_irq_lower(p->parent_irq);
+ }
+}
+
+static uint32_t pic_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct xlx_pic *p = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ default:
+ if (addr < ARRAY_SIZE(p->regs))
+ r = p->regs[addr];
+ break;
+
+ }
+ D(printf("%s %x=%x\n", __func__, addr * 4, r));
+ return r;
+}
+
+static void
+pic_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct xlx_pic *p = opaque;
+
+ addr >>= 2;
+ D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
+ switch (addr)
+ {
+ case R_IAR:
+ p->regs[R_ISR] &= ~value; /* ACK. */
+ break;
+ case R_SIE:
+ p->regs[R_IER] |= value; /* Atomic set ie. */
+ break;
+ case R_CIE:
+ p->regs[R_IER] &= ~value; /* Atomic clear ie. */
+ break;
+ default:
+ if (addr < ARRAY_SIZE(p->regs))
+ p->regs[addr] = value;
+ break;
+ }
+ update_irq(p);
+}
+
+static CPUReadMemoryFunc * const pic_read[] = {
+ NULL, NULL,
+ &pic_readl,
+};
+
+static CPUWriteMemoryFunc * const pic_write[] = {
+ NULL, NULL,
+ &pic_writel,
+};
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ struct xlx_pic *p = opaque;
+
+ if (!(p->regs[R_MER] & 2)) {
+ qemu_irq_lower(p->parent_irq);
+ return;
+ }
+
+ /* Update source flops. Don't clear unless level triggered.
+ Edge triggered interrupts only go away when explicitely acked to
+ the interrupt controller. */
+ if (!(p->c_kind_of_intr & (1 << irq)) || level) {
+ p->regs[R_ISR] &= ~(1 << irq);
+ p->regs[R_ISR] |= (level << irq);
+ }
+ update_irq(p);
+}
+
+static int xilinx_intc_init(SysBusDevice *dev)
+{
+ struct xlx_pic *p = FROM_SYSBUS(typeof (*p), dev);
+ int pic_regs;
+
+ qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+ sysbus_init_irq(dev, &p->parent_irq);
+
+ pic_regs = cpu_register_io_memory(pic_read, pic_write, p, DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, R_MAX * 4, pic_regs);
+ return 0;
+}
+
+static SysBusDeviceInfo xilinx_intc_info = {
+ .init = xilinx_intc_init,
+ .qdev.name = "xilinx,intc",
+ .qdev.size = sizeof(struct xlx_pic),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("kind-of-intr", struct xlx_pic, c_kind_of_intr, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xilinx_intc_register(void)
+{
+ sysbus_register_withprop(&xilinx_intc_info);
+}
+
+device_init(xilinx_intc_register)
diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c
new file mode 100644
index 0000000..30827b0
--- /dev/null
+++ b/hw/xilinx_timer.c
@@ -0,0 +1,235 @@
+/*
+ * QEMU model of the Xilinx timer block.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+
+#define D(x)
+
+#define R_TCSR 0
+#define R_TLR 1
+#define R_TCR 2
+#define R_MAX 4
+
+#define TCSR_MDT (1<<0)
+#define TCSR_UDT (1<<1)
+#define TCSR_GENT (1<<2)
+#define TCSR_CAPT (1<<3)
+#define TCSR_ARHT (1<<4)
+#define TCSR_LOAD (1<<5)
+#define TCSR_ENIT (1<<6)
+#define TCSR_ENT (1<<7)
+#define TCSR_TINT (1<<8)
+#define TCSR_PWMA (1<<9)
+#define TCSR_ENALL (1<<10)
+
+struct xlx_timer
+{
+ QEMUBH *bh;
+ ptimer_state *ptimer;
+ void *parent;
+ int nr; /* for debug. */
+
+ unsigned long timer_div;
+
+ uint32_t regs[R_MAX];
+};
+
+struct timerblock
+{
+ SysBusDevice busdev;
+ qemu_irq irq;
+ uint32_t nr_timers;
+ uint32_t freq_hz;
+ struct xlx_timer *timers;
+};
+
+static inline unsigned int timer_from_addr(target_phys_addr_t addr)
+{
+ /* Timers get a 4x32bit control reg area each. */
+ return addr >> 2;
+}
+
+static void timer_update_irq(struct timerblock *t)
+{
+ unsigned int i, irq = 0;
+ uint32_t csr;
+
+ for (i = 0; i < t->nr_timers; i++) {
+ csr = t->timers[i].regs[R_TCSR];
+ irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT);
+ }
+
+ /* All timers within the same slave share a single IRQ line. */
+ qemu_set_irq(t->irq, !!irq);
+}
+
+static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct timerblock *t = opaque;
+ struct xlx_timer *xt;
+ uint32_t r = 0;
+ unsigned int timer;
+
+ addr >>= 2;
+ timer = timer_from_addr(addr);
+ xt = &t->timers[timer];
+ /* Further decoding to address a specific timers reg. */
+ addr &= 0x3;
+ switch (addr)
+ {
+ case R_TCR:
+ r = ptimer_get_count(xt->ptimer);
+ if (!(xt->regs[R_TCSR] & TCSR_UDT))
+ r = ~r;
+ D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n",
+ timer, r, xt->regs[R_TCSR] & TCSR_UDT));
+ break;
+ default:
+ if (addr < ARRAY_SIZE(xt->regs))
+ r = xt->regs[addr];
+ break;
+
+ }
+ D(printf("%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
+ return r;
+}
+
+static void timer_enable(struct xlx_timer *xt)
+{
+ uint64_t count;
+
+ D(printf("%s timer=%d down=%d\n", __func__,
+ xt->nr, xt->regs[R_TCSR] & TCSR_UDT));
+
+ ptimer_stop(xt->ptimer);
+
+ if (xt->regs[R_TCSR] & TCSR_UDT)
+ count = xt->regs[R_TLR];
+ else
+ count = ~0 - xt->regs[R_TLR];
+ ptimer_set_count(xt->ptimer, count);
+ ptimer_run(xt->ptimer, 1);
+}
+
+static void
+timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct timerblock *t = opaque;
+ struct xlx_timer *xt;
+ unsigned int timer;
+
+ addr >>= 2;
+ timer = timer_from_addr(addr);
+ xt = &t->timers[timer];
+ D(printf("%s addr=%x val=%x (timer=%d off=%d)\n",
+ __func__, addr * 4, value, timer, addr & 3));
+ /* Further decoding to address a specific timers reg. */
+ addr &= 3;
+ switch (addr)
+ {
+ case R_TCSR:
+ if (value & TCSR_TINT)
+ value &= ~TCSR_TINT;
+
+ xt->regs[addr] = value;
+ if (value & TCSR_ENT)
+ timer_enable(xt);
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(xt->regs))
+ xt->regs[addr] = value;
+ break;
+ }
+ timer_update_irq(t);
+}
+
+static CPUReadMemoryFunc * const timer_read[] = {
+ NULL, NULL,
+ &timer_readl,
+};
+
+static CPUWriteMemoryFunc * const timer_write[] = {
+ NULL, NULL,
+ &timer_writel,
+};
+
+static void timer_hit(void *opaque)
+{
+ struct xlx_timer *xt = opaque;
+ struct timerblock *t = xt->parent;
+ D(printf("%s %d\n", __func__, timer));
+ xt->regs[R_TCSR] |= TCSR_TINT;
+
+ if (xt->regs[R_TCSR] & TCSR_ARHT)
+ timer_enable(xt);
+ timer_update_irq(t);
+}
+
+static int xilinx_timer_init(SysBusDevice *dev)
+{
+ struct timerblock *t = FROM_SYSBUS(typeof (*t), dev);
+ unsigned int i;
+ int timer_regs;
+
+ /* All timers share a single irq line. */
+ sysbus_init_irq(dev, &t->irq);
+
+ /* Init all the ptimers. */
+ t->timers = qemu_mallocz(sizeof t->timers[0] * t->nr_timers);
+ for (i = 0; i < t->nr_timers; i++) {
+ struct xlx_timer *xt = &t->timers[i];
+
+ xt->parent = t;
+ xt->nr = i;
+ xt->bh = qemu_bh_new(timer_hit, xt);
+ xt->ptimer = ptimer_init(xt->bh);
+ ptimer_set_freq(xt->ptimer, t->freq_hz);
+ }
+
+ timer_regs = cpu_register_io_memory(timer_read, timer_write, t,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, R_MAX * 4 * t->nr_timers, timer_regs);
+ return 0;
+}
+
+static SysBusDeviceInfo xilinx_timer_info = {
+ .init = xilinx_timer_init,
+ .qdev.name = "xilinx,timer",
+ .qdev.size = sizeof(struct timerblock),
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("frequency", struct timerblock, freq_hz, 0),
+ DEFINE_PROP_UINT32("nr-timers", struct timerblock, nr_timers, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xilinx_timer_register(void)
+{
+ sysbus_register_withprop(&xilinx_timer_info);
+}
+
+device_init(xilinx_timer_register)
diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c
new file mode 100644
index 0000000..9b94e98
--- /dev/null
+++ b/hw/xilinx_uartlite.c
@@ -0,0 +1,220 @@
+/*
+ * QEMU model of Xilinx uartlite.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+
+#define DUART(x)
+
+#define R_RX 0
+#define R_TX 1
+#define R_STATUS 2
+#define R_CTRL 3
+#define R_MAX 4
+
+#define STATUS_RXVALID 0x01
+#define STATUS_RXFULL 0x02
+#define STATUS_TXEMPTY 0x04
+#define STATUS_TXFULL 0x08
+#define STATUS_IE 0x10
+#define STATUS_OVERRUN 0x20
+#define STATUS_FRAME 0x40
+#define STATUS_PARITY 0x80
+
+#define CONTROL_RST_TX 0x01
+#define CONTROL_RST_RX 0x02
+#define CONTROL_IE 0x10
+
+struct xlx_uartlite
+{
+ SysBusDevice busdev;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint8_t rx_fifo[8];
+ unsigned int rx_fifo_pos;
+ unsigned int rx_fifo_len;
+
+ uint32_t regs[R_MAX];
+};
+
+static void uart_update_irq(struct xlx_uartlite *s)
+{
+ unsigned int irq;
+
+ if (s->rx_fifo_len)
+ s->regs[R_STATUS] |= STATUS_IE;
+
+ irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE);
+ qemu_set_irq(s->irq, irq);
+}
+
+static void uart_update_status(struct xlx_uartlite *s)
+{
+ uint32_t r;
+
+ r = s->regs[R_STATUS];
+ r &= ~7;
+ r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */
+ r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1;
+ r |= (!!s->rx_fifo_len);
+ s->regs[R_STATUS] = r;
+}
+
+static uint32_t uart_readl (void *opaque, target_phys_addr_t addr)
+{
+ struct xlx_uartlite *s = opaque;
+ uint32_t r = 0;
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_RX:
+ r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7];
+ if (s->rx_fifo_len)
+ s->rx_fifo_len--;
+ uart_update_status(s);
+ uart_update_irq(s);
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs))
+ r = s->regs[addr];
+ DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r));
+ break;
+ }
+ return r;
+}
+
+static void
+uart_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ struct xlx_uartlite *s = opaque;
+ unsigned char ch = value;
+
+ addr >>= 2;
+ switch (addr)
+ {
+ case R_STATUS:
+ hw_error("write to UART STATUS?\n");
+ break;
+
+ case R_CTRL:
+ if (value & CONTROL_RST_RX) {
+ s->rx_fifo_pos = 0;
+ s->rx_fifo_len = 0;
+ }
+ s->regs[addr] = value;
+ break;
+
+ case R_TX:
+ if (s->chr)
+ qemu_chr_write(s->chr, &ch, 1);
+
+ s->regs[addr] = value;
+
+ /* hax. */
+ s->regs[R_STATUS] |= STATUS_IE;
+ break;
+
+ default:
+ DUART(printf("%s addr=%x v=%x\n", __func__, addr, value));
+ if (addr < ARRAY_SIZE(s->regs))
+ s->regs[addr] = value;
+ break;
+ }
+ uart_update_status(s);
+ uart_update_irq(s);
+}
+
+static CPUReadMemoryFunc * const uart_read[] = {
+ &uart_readl,
+ &uart_readl,
+ &uart_readl,
+};
+
+static CPUWriteMemoryFunc * const uart_write[] = {
+ &uart_writel,
+ &uart_writel,
+ &uart_writel,
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ struct xlx_uartlite *s = opaque;
+
+ /* Got a byte. */
+ if (s->rx_fifo_len >= 8) {
+ printf("WARNING: UART dropped char.\n");
+ return;
+ }
+ s->rx_fifo[s->rx_fifo_pos] = *buf;
+ s->rx_fifo_pos++;
+ s->rx_fifo_pos &= 0x7;
+ s->rx_fifo_len++;
+
+ uart_update_status(s);
+ uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ struct xlx_uartlite *s = opaque;
+ int r;
+
+ r = s->rx_fifo_len < sizeof(s->rx_fifo);
+ if (!r)
+ printf("cannot receive!\n");
+ return r;
+}
+
+static void uart_event(void *opaque, int event)
+{
+
+}
+
+static int xilinx_uartlite_init(SysBusDevice *dev)
+{
+ struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev);
+ int uart_regs;
+
+ sysbus_init_irq(dev, &s->irq);
+
+ uart_update_status(s);
+ uart_regs = cpu_register_io_memory(uart_read, uart_write, s,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, R_MAX * 4, uart_regs);
+
+ s->chr = qdev_init_chardev(&dev->qdev);
+ if (s->chr)
+ qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
+ return 0;
+}
+
+static void xilinx_uart_register(void)
+{
+ sysbus_register_dev("xilinx,uartlite", sizeof (struct xlx_uartlite),
+ xilinx_uartlite_init);
+}
+
+device_init(xilinx_uart_register)
diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c
new file mode 100644
index 0000000..5aa6a6b
--- /dev/null
+++ b/hw/xio3130_downstream.c
@@ -0,0 +1,211 @@
+/*
+ * x3130_downstream.c
+ * TI X3130 pci express downstream port switch
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "pci_ids.h"
+#include "msi.h"
+#include "pcie.h"
+#include "xio3130_downstream.h"
+
+#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
+#define XIO3130_REVISION 0x1
+#define XIO3130_MSI_OFFSET 0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR 1
+#define XIO3130_SSVID_OFFSET 0x80
+#define XIO3130_SSVID_SVID 0
+#define XIO3130_SSVID_SSID 0
+#define XIO3130_EXP_OFFSET 0x90
+#define XIO3130_AER_OFFSET 0x100
+
+static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_write_config(d, address, val, len);
+ pcie_cap_slot_write_config(d, address, val, len);
+ msi_write_config(d, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+}
+
+static void xio3130_downstream_reset(DeviceState *qdev)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+ msi_reset(d);
+ pcie_cap_deverr_reset(d);
+ pcie_cap_slot_reset(d);
+ pcie_cap_ari_reset(d);
+ pci_bridge_reset(qdev);
+}
+
+static int xio3130_downstream_initfn(PCIDevice *d)
+{
+ PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+ PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+ PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+ int rc;
+ int tmp;
+
+ rc = pci_bridge_initfn(d);
+ if (rc < 0) {
+ return rc;
+ }
+
+ pcie_port_init_reg(d);
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130D);
+ d->config[PCI_REVISION_ID] = XIO3130_REVISION;
+
+ rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+ XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
+ p->port);
+ if (rc < 0) {
+ goto err_msi;
+ }
+ pcie_cap_flr_init(d);
+ pcie_cap_deverr_init(d);
+ pcie_cap_slot_init(d, s->slot);
+ pcie_chassis_create(s->chassis);
+ rc = pcie_chassis_add_slot(s);
+ if (rc < 0) {
+ goto err_pcie_cap;
+ }
+ pcie_cap_ari_init(d);
+ rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
+ if (rc < 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pcie_chassis_del_slot(s);
+err_pcie_cap:
+ pcie_cap_exit(d);
+err_msi:
+ msi_uninit(d);
+err_bridge:
+ tmp = pci_bridge_exitfn(d);
+ assert(!tmp);
+ return rc;
+}
+
+static int xio3130_downstream_exitfn(PCIDevice *d)
+{
+ PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+ PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+ PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+
+ pcie_aer_exit(d);
+ pcie_chassis_del_slot(s);
+ pcie_cap_exit(d);
+ msi_uninit(d);
+ return pci_bridge_exitfn(d);
+}
+
+PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port, uint8_t chassis,
+ uint16_t slot)
+{
+ PCIDevice *d;
+ PCIBridge *br;
+ DeviceState *qdev;
+
+ d = pci_create_multifunction(bus, devfn, multifunction,
+ "xio3130-downstream");
+ if (!d) {
+ return NULL;
+ }
+ br = DO_UPCAST(PCIBridge, dev, d);
+
+ qdev = &br->dev.qdev;
+ pci_bridge_map_irq(br, bus_name, map_irq);
+ qdev_prop_set_uint8(qdev, "port", port);
+ qdev_prop_set_uint8(qdev, "chassis", chassis);
+ qdev_prop_set_uint16(qdev, "slot", slot);
+ qdev_init_nofail(qdev);
+
+ return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
+}
+
+static const VMStateDescription vmstate_xio3130_downstream = {
+ .name = "xio3130-express-downstream-port",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = pcie_cap_slot_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
+ VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0,
+ vmstate_pcie_aer_log, PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo xio3130_downstream_info = {
+ .qdev.name = "xio3130-downstream",
+ .qdev.desc = "TI X3130 Downstream Port of PCI Express Switch",
+ .qdev.size = sizeof(PCIESlot),
+ .qdev.reset = xio3130_downstream_reset,
+ .qdev.vmsd = &vmstate_xio3130_downstream,
+
+ .is_express = 1,
+ .is_bridge = 1,
+ .config_write = xio3130_downstream_write_config,
+ .init = xio3130_downstream_initfn,
+ .exit = xio3130_downstream_exitfn,
+
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
+ DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
+ DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
+ DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
+ port.br.dev.exp.aer_log.log_max,
+ PCIE_AER_LOG_MAX_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xio3130_downstream_register(void)
+{
+ pci_qdev_register(&xio3130_downstream_info);
+}
+
+device_init(xio3130_downstream_register);
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/xio3130_downstream.h b/hw/xio3130_downstream.h
new file mode 100644
index 0000000..010487f
--- /dev/null
+++ b/hw/xio3130_downstream.h
@@ -0,0 +1,11 @@
+#ifndef QEMU_XIO3130_DOWNSTREAM_H
+#define QEMU_XIO3130_DOWNSTREAM_H
+
+#include "pcie_port.h"
+
+PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port, uint8_t chassis,
+ uint16_t slot);
+
+#endif /* QEMU_XIO3130_DOWNSTREAM_H */
diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c
new file mode 100644
index 0000000..a7640f5
--- /dev/null
+++ b/hw/xio3130_upstream.c
@@ -0,0 +1,186 @@
+/*
+ * xio3130_upstream.c
+ * TI X3130 pci express upstream port switch
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include "pci_ids.h"
+#include "msi.h"
+#include "pcie.h"
+#include "xio3130_upstream.h"
+
+#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */
+#define XIO3130_REVISION 0x2
+#define XIO3130_MSI_OFFSET 0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR 1
+#define XIO3130_SSVID_OFFSET 0x80
+#define XIO3130_SSVID_SVID 0
+#define XIO3130_SSVID_SSID 0
+#define XIO3130_EXP_OFFSET 0x90
+#define XIO3130_AER_OFFSET 0x100
+
+static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
+ uint32_t val, int len)
+{
+ pci_bridge_write_config(d, address, val, len);
+ pcie_cap_flr_write_config(d, address, val, len);
+ msi_write_config(d, address, val, len);
+ pcie_aer_write_config(d, address, val, len);
+}
+
+static void xio3130_upstream_reset(DeviceState *qdev)
+{
+ PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
+ msi_reset(d);
+ pci_bridge_reset(qdev);
+ pcie_cap_deverr_reset(d);
+}
+
+static int xio3130_upstream_initfn(PCIDevice *d)
+{
+ PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+ PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+ int rc;
+ int tmp;
+
+ rc = pci_bridge_initfn(d);
+ if (rc < 0) {
+ return rc;
+ }
+
+ pcie_port_init_reg(d);
+ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI);
+ pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130U);
+ d->config[PCI_REVISION_ID] = XIO3130_REVISION;
+
+ rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+ XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+ XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
+ if (rc < 0) {
+ goto err_bridge;
+ }
+ rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
+ p->port);
+ if (rc < 0) {
+ goto err_msi;
+ }
+ pcie_cap_flr_init(d);
+ pcie_cap_deverr_init(d);
+ rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
+ if (rc < 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pcie_cap_exit(d);
+err_msi:
+ msi_uninit(d);
+err_bridge:
+ tmp = pci_bridge_exitfn(d);
+ assert(!tmp);
+ return rc;
+}
+
+static int xio3130_upstream_exitfn(PCIDevice *d)
+{
+ pcie_aer_exit(d);
+ pcie_cap_exit(d);
+ msi_uninit(d);
+ return pci_bridge_exitfn(d);
+}
+
+PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port)
+{
+ PCIDevice *d;
+ PCIBridge *br;
+ DeviceState *qdev;
+
+ d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream");
+ if (!d) {
+ return NULL;
+ }
+ br = DO_UPCAST(PCIBridge, dev, d);
+
+ qdev = &br->dev.qdev;
+ pci_bridge_map_irq(br, bus_name, map_irq);
+ qdev_prop_set_uint8(qdev, "port", port);
+ qdev_init_nofail(qdev);
+
+ return DO_UPCAST(PCIEPort, br, br);
+}
+
+static const VMStateDescription vmstate_xio3130_upstream = {
+ .name = "xio3130-express-upstream-port",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCIE_DEVICE(br.dev, PCIEPort),
+ VMSTATE_STRUCT(br.dev.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log,
+ PCIEAERLog),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIDeviceInfo xio3130_upstream_info = {
+ .qdev.name = "x3130-upstream",
+ .qdev.desc = "TI X3130 Upstream Port of PCI Express Switch",
+ .qdev.size = sizeof(PCIEPort),
+ .qdev.reset = xio3130_upstream_reset,
+ .qdev.vmsd = &vmstate_xio3130_upstream,
+
+ .is_express = 1,
+ .is_bridge = 1,
+ .config_write = xio3130_upstream_write_config,
+ .init = xio3130_upstream_initfn,
+ .exit = xio3130_upstream_exitfn,
+
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
+ DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max,
+ PCIE_AER_LOG_MAX_DEFAULT),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void xio3130_upstream_register(void)
+{
+ pci_qdev_register(&xio3130_upstream_info);
+}
+
+device_init(xio3130_upstream_register);
+
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/xio3130_upstream.h b/hw/xio3130_upstream.h
new file mode 100644
index 0000000..e996997
--- /dev/null
+++ b/hw/xio3130_upstream.h
@@ -0,0 +1,10 @@
+#ifndef QEMU_XIO3130_UPSTREAM_H
+#define QEMU_XIO3130_UPSTREAM_H
+
+#include "pcie_port.h"
+
+PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
+ const char *bus_name, pci_map_irq_fn map_irq,
+ uint8_t port);
+
+#endif /* QEMU_XIO3130_H */
diff --git a/hw/zaurus.c b/hw/zaurus.c
new file mode 100644
index 0000000..fca11a5
--- /dev/null
+++ b/hw/zaurus.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2006-2008 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+#include "hw.h"
+#include "pxa.h"
+#include "sharpsl.h"
+#include "sysbus.h"
+
+#undef REG_FMT
+#define REG_FMT "0x%02lx"
+
+/* SCOOP devices */
+
+typedef struct ScoopInfo ScoopInfo;
+struct ScoopInfo {
+ SysBusDevice busdev;
+ qemu_irq handler[16];
+ uint16_t status;
+ uint16_t power;
+ uint32_t gpio_level;
+ uint32_t gpio_dir;
+ uint32_t prev_level;
+
+ uint16_t mcr;
+ uint16_t cdr;
+ uint16_t ccr;
+ uint16_t irr;
+ uint16_t imr;
+ uint16_t isr;
+};
+
+#define SCOOP_MCR 0x00
+#define SCOOP_CDR 0x04
+#define SCOOP_CSR 0x08
+#define SCOOP_CPR 0x0c
+#define SCOOP_CCR 0x10
+#define SCOOP_IRR_IRM 0x14
+#define SCOOP_IMR 0x18
+#define SCOOP_ISR 0x1c
+#define SCOOP_GPCR 0x20
+#define SCOOP_GPWR 0x24
+#define SCOOP_GPRR 0x28
+
+static inline void scoop_gpio_handler_update(ScoopInfo *s) {
+ uint32_t level, diff;
+ int bit;
+ level = s->gpio_level & s->gpio_dir;
+
+ for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
+ bit = ffs(diff) - 1;
+ qemu_set_irq(s->handler[bit], (level >> bit) & 1);
+ }
+
+ s->prev_level = level;
+}
+
+static uint32_t scoop_readb(void *opaque, target_phys_addr_t addr)
+{
+ ScoopInfo *s = (ScoopInfo *) opaque;
+
+ switch (addr & 0x3f) {
+ case SCOOP_MCR:
+ return s->mcr;
+ case SCOOP_CDR:
+ return s->cdr;
+ case SCOOP_CSR:
+ return s->status;
+ case SCOOP_CPR:
+ return s->power;
+ case SCOOP_CCR:
+ return s->ccr;
+ case SCOOP_IRR_IRM:
+ return s->irr;
+ case SCOOP_IMR:
+ return s->imr;
+ case SCOOP_ISR:
+ return s->isr;
+ case SCOOP_GPCR:
+ return s->gpio_dir;
+ case SCOOP_GPWR:
+ case SCOOP_GPRR:
+ return s->gpio_level;
+ default:
+ zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr);
+ }
+
+ return 0;
+}
+
+static void scoop_writeb(void *opaque, target_phys_addr_t addr, uint32_t value)
+{
+ ScoopInfo *s = (ScoopInfo *) opaque;
+ value &= 0xffff;
+
+ switch (addr & 0x3f) {
+ case SCOOP_MCR:
+ s->mcr = value;
+ break;
+ case SCOOP_CDR:
+ s->cdr = value;
+ break;
+ case SCOOP_CPR:
+ s->power = value;
+ if (value & 0x80)
+ s->power |= 0x8040;
+ break;
+ case SCOOP_CCR:
+ s->ccr = value;
+ break;
+ case SCOOP_IRR_IRM:
+ s->irr = value;
+ break;
+ case SCOOP_IMR:
+ s->imr = value;
+ break;
+ case SCOOP_ISR:
+ s->isr = value;
+ break;
+ case SCOOP_GPCR:
+ s->gpio_dir = value;
+ scoop_gpio_handler_update(s);
+ break;
+ case SCOOP_GPWR:
+ case SCOOP_GPRR: /* GPRR is probably R/O in real HW */
+ s->gpio_level = value & s->gpio_dir;
+ scoop_gpio_handler_update(s);
+ break;
+ default:
+ zaurus_printf("Bad register offset " REG_FMT "\n", (unsigned long)addr);
+ }
+}
+
+static CPUReadMemoryFunc * const scoop_readfn[] = {
+ scoop_readb,
+ scoop_readb,
+ scoop_readb,
+};
+static CPUWriteMemoryFunc * const scoop_writefn[] = {
+ scoop_writeb,
+ scoop_writeb,
+ scoop_writeb,
+};
+
+static void scoop_gpio_set(void *opaque, int line, int level)
+{
+ ScoopInfo *s = (ScoopInfo *) opaque;
+
+ if (level)
+ s->gpio_level |= (1 << line);
+ else
+ s->gpio_level &= ~(1 << line);
+}
+
+static int scoop_init(SysBusDevice *dev)
+{
+ ScoopInfo *s = FROM_SYSBUS(ScoopInfo, dev);
+ int iomemtype;
+
+ s->status = 0x02;
+ qdev_init_gpio_out(&s->busdev.qdev, s->handler, 16);
+ qdev_init_gpio_in(&s->busdev.qdev, scoop_gpio_set, 16);
+ iomemtype = cpu_register_io_memory(scoop_readfn,
+ scoop_writefn, s, DEVICE_NATIVE_ENDIAN);
+
+ sysbus_init_mmio(dev, 0x1000, iomemtype);
+
+ return 0;
+}
+
+static bool is_version_0 (void *opaque, int version_id)
+{
+ return version_id == 0;
+}
+
+
+static const VMStateDescription vmstate_scoop_regs = {
+ .name = "scoop",
+ .version_id = 1,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT16(status, ScoopInfo),
+ VMSTATE_UINT16(power, ScoopInfo),
+ VMSTATE_UINT32(gpio_level, ScoopInfo),
+ VMSTATE_UINT32(gpio_dir, ScoopInfo),
+ VMSTATE_UINT32(prev_level, ScoopInfo),
+ VMSTATE_UINT16(mcr, ScoopInfo),
+ VMSTATE_UINT16(cdr, ScoopInfo),
+ VMSTATE_UINT16(ccr, ScoopInfo),
+ VMSTATE_UINT16(irr, ScoopInfo),
+ VMSTATE_UINT16(imr, ScoopInfo),
+ VMSTATE_UINT16(isr, ScoopInfo),
+ VMSTATE_UNUSED_TEST(is_version_0, 2),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static SysBusDeviceInfo scoop_sysbus_info = {
+ .init = scoop_init,
+ .qdev.name = "scoop",
+ .qdev.desc = "Scoop2 Sharp custom ASIC",
+ .qdev.size = sizeof(ScoopInfo),
+ .qdev.vmsd = &vmstate_scoop_regs,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void scoop_register(void)
+{
+ sysbus_register_withprop(&scoop_sysbus_info);
+}
+device_init(scoop_register);
+
+/* Write the bootloader parameters memory area. */
+
+#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a)
+
+static struct __attribute__ ((__packed__)) sl_param_info {
+ uint32_t comadj_keyword;
+ int32_t comadj;
+
+ uint32_t uuid_keyword;
+ char uuid[16];
+
+ uint32_t touch_keyword;
+ int32_t touch_xp;
+ int32_t touch_yp;
+ int32_t touch_xd;
+ int32_t touch_yd;
+
+ uint32_t adadj_keyword;
+ int32_t adadj;
+
+ uint32_t phad_keyword;
+ int32_t phadadj;
+} zaurus_bootparam = {
+ .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'),
+ .comadj = 125,
+ .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'),
+ .uuid = { -1 },
+ .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'),
+ .touch_xp = -1,
+ .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'),
+ .adadj = -1,
+ .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'),
+ .phadadj = 0x01,
+};
+
+void sl_bootparam_write(target_phys_addr_t ptr)
+{
+ cpu_physical_memory_write(ptr, (void *)&zaurus_bootparam,
+ sizeof(struct sl_param_info));
+}
diff --git a/i386-dis.c b/i386-dis.c
new file mode 100644
index 0000000..c4a81c9
--- /dev/null
+++ b/i386-dis.c
@@ -0,0 +1,6562 @@
+/* opcodes/i386-dis.c r1.126 */
+/* Print i386 instructions for GDB, the GNU debugger.
+ Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+/* 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
+ July 1988
+ modified by John Hassey (hassey@dg-rtp.dg.com)
+ x86-64 support added by Jan Hubicka (jh@suse.cz)
+ VIA PadLock support by Michal Ludvig (mludvig@suse.cz). */
+
+/* The main tables describing the instructions is essentially a copy
+ of the "Opcode Map" chapter (Appendix A) of the Intel 80386
+ Programmers Manual. Usually, there is a capital letter, followed
+ by a small letter. The capital letter tell the addressing mode,
+ and the small letter tells about the operand size. Refer to
+ the Intel manual for details. */
+
+#include <stdlib.h>
+#include "dis-asm.h"
+/* include/opcode/i386.h r1.78 */
+
+/* opcode/i386.h -- Intel 80386 opcode macros
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler, and GDB, the GNU Debugger.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+/* The SystemV/386 SVR3.2 assembler, and probably all AT&T derived
+ ix86 Unix assemblers, generate floating point instructions with
+ reversed source and destination registers in certain cases.
+ Unfortunately, gcc and possibly many other programs use this
+ reversed syntax, so we're stuck with it.
+
+ eg. `fsub %st(3),%st' results in st = st - st(3) as expected, but
+ `fsub %st,%st(3)' results in st(3) = st - st(3), rather than
+ the expected st(3) = st(3) - st
+
+ This happens with all the non-commutative arithmetic floating point
+ operations with two register operands, where the source register is
+ %st, and destination register is %st(i).
+
+ The affected opcode map is dceX, dcfX, deeX, defX. */
+
+#ifndef SYSV386_COMPAT
+/* Set non-zero for broken, compatible instructions. Set to zero for
+ non-broken opcodes at your peril. gcc generates SystemV/386
+ compatible instructions. */
+#define SYSV386_COMPAT 1
+#endif
+#ifndef OLDGCC_COMPAT
+/* Set non-zero to cater for old (<= 2.8.1) versions of gcc that could
+ generate nonsense fsubp, fsubrp, fdivp and fdivrp with operands
+ reversed. */
+#define OLDGCC_COMPAT SYSV386_COMPAT
+#endif
+
+#define MOV_AX_DISP32 0xa0
+#define POP_SEG_SHORT 0x07
+#define JUMP_PC_RELATIVE 0xeb
+#define INT_OPCODE 0xcd
+#define INT3_OPCODE 0xcc
+/* The opcode for the fwait instruction, which disassembler treats as a
+ prefix when it can. */
+#define FWAIT_OPCODE 0x9b
+#define ADDR_PREFIX_OPCODE 0x67
+#define DATA_PREFIX_OPCODE 0x66
+#define LOCK_PREFIX_OPCODE 0xf0
+#define CS_PREFIX_OPCODE 0x2e
+#define DS_PREFIX_OPCODE 0x3e
+#define ES_PREFIX_OPCODE 0x26
+#define FS_PREFIX_OPCODE 0x64
+#define GS_PREFIX_OPCODE 0x65
+#define SS_PREFIX_OPCODE 0x36
+#define REPNE_PREFIX_OPCODE 0xf2
+#define REPE_PREFIX_OPCODE 0xf3
+
+#define TWO_BYTE_OPCODE_ESCAPE 0x0f
+#define NOP_OPCODE (char) 0x90
+
+/* register numbers */
+#define EBP_REG_NUM 5
+#define ESP_REG_NUM 4
+
+/* modrm_byte.regmem for twobyte escape */
+#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM
+/* index_base_byte.index for no index register addressing */
+#define NO_INDEX_REGISTER ESP_REG_NUM
+/* index_base_byte.base for no base register addressing */
+#define NO_BASE_REGISTER EBP_REG_NUM
+#define NO_BASE_REGISTER_16 6
+
+/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */
+#define REGMEM_FIELD_HAS_REG 0x3/* always = 0x3 */
+#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG)
+
+/* x86-64 extension prefix. */
+#define REX_OPCODE 0x40
+
+/* Indicates 64 bit operand size. */
+#define REX_W 8
+/* High extension to reg field of modrm byte. */
+#define REX_R 4
+/* High extension to SIB index field. */
+#define REX_X 2
+/* High extension to base field of modrm or SIB, or reg field of opcode. */
+#define REX_B 1
+
+/* max operands per insn */
+#define MAX_OPERANDS 4
+
+/* max immediates per insn (lcall, ljmp, insertq, extrq) */
+#define MAX_IMMEDIATE_OPERANDS 2
+
+/* max memory refs per insn (string ops) */
+#define MAX_MEMORY_OPERANDS 2
+
+/* max size of insn mnemonics. */
+#define MAX_MNEM_SIZE 16
+
+/* max size of register name in insn mnemonics. */
+#define MAX_REG_NAME_SIZE 8
+
+/* opcodes/i386-dis.c r1.126 */
+#include "qemu-common.h"
+
+#include <setjmp.h>
+
+static int fetch_data2(struct disassemble_info *, bfd_byte *);
+static int fetch_data(struct disassemble_info *, bfd_byte *);
+static void ckprefix (void);
+static const char *prefix_name (int, int);
+static int print_insn (bfd_vma, disassemble_info *);
+static void dofloat (int);
+static void OP_ST (int, int);
+static void OP_STi (int, int);
+static int putop (const char *, int);
+static void oappend (const char *);
+static void append_seg (void);
+static void OP_indirE (int, int);
+static void print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp);
+static void print_displacement (char *, bfd_vma);
+static void OP_E (int, int);
+static void OP_G (int, int);
+static bfd_vma get64 (void);
+static bfd_signed_vma get32 (void);
+static bfd_signed_vma get32s (void);
+static int get16 (void);
+static void set_op (bfd_vma, int);
+static void OP_REG (int, int);
+static void OP_IMREG (int, int);
+static void OP_I (int, int);
+static void OP_I64 (int, int);
+static void OP_sI (int, int);
+static void OP_J (int, int);
+static void OP_SEG (int, int);
+static void OP_DIR (int, int);
+static void OP_OFF (int, int);
+static void OP_OFF64 (int, int);
+static void ptr_reg (int, int);
+static void OP_ESreg (int, int);
+static void OP_DSreg (int, int);
+static void OP_C (int, int);
+static void OP_D (int, int);
+static void OP_T (int, int);
+static void OP_R (int, int);
+static void OP_MMX (int, int);
+static void OP_XMM (int, int);
+static void OP_EM (int, int);
+static void OP_EX (int, int);
+static void OP_EMC (int,int);
+static void OP_MXC (int,int);
+static void OP_MS (int, int);
+static void OP_XS (int, int);
+static void OP_M (int, int);
+static void OP_VMX (int, int);
+static void OP_0fae (int, int);
+static void OP_0f07 (int, int);
+static void NOP_Fixup1 (int, int);
+static void NOP_Fixup2 (int, int);
+static void OP_3DNowSuffix (int, int);
+static void OP_SIMD_Suffix (int, int);
+static void SIMD_Fixup (int, int);
+static void PNI_Fixup (int, int);
+static void SVME_Fixup (int, int);
+static void INVLPG_Fixup (int, int);
+static void BadOp (void);
+static void VMX_Fixup (int, int);
+static void REP_Fixup (int, int);
+static void CMPXCHG8B_Fixup (int, int);
+static void XMM_Fixup (int, int);
+static void CRC32_Fixup (int, int);
+
+struct dis_private {
+ /* Points to first byte not fetched. */
+ bfd_byte *max_fetched;
+ bfd_byte the_buffer[MAX_MNEM_SIZE];
+ bfd_vma insn_start;
+ int orig_sizeflag;
+ jmp_buf bailout;
+};
+
+enum address_mode
+{
+ mode_16bit,
+ mode_32bit,
+ mode_64bit
+};
+
+static enum address_mode address_mode;
+
+/* Flags for the prefixes for the current instruction. See below. */
+static int prefixes;
+
+/* REX prefix the current instruction. See below. */
+static int rex;
+/* Bits of REX we've already used. */
+static int rex_used;
+/* Mark parts used in the REX prefix. When we are testing for
+ empty prefix (for 8bit register REX extension), just mask it
+ out. Otherwise test for REX bit is excuse for existence of REX
+ only in case value is nonzero. */
+#define USED_REX(value) \
+ { \
+ if (value) \
+ { \
+ if ((rex & value)) \
+ rex_used |= (value) | REX_OPCODE; \
+ } \
+ else \
+ rex_used |= REX_OPCODE; \
+ }
+
+/* Flags for prefixes which we somehow handled when printing the
+ current instruction. */
+static int used_prefixes;
+
+/* Flags stored in PREFIXES. */
+#define PREFIX_REPZ 1
+#define PREFIX_REPNZ 2
+#define PREFIX_LOCK 4
+#define PREFIX_CS 8
+#define PREFIX_SS 0x10
+#define PREFIX_DS 0x20
+#define PREFIX_ES 0x40
+#define PREFIX_FS 0x80
+#define PREFIX_GS 0x100
+#define PREFIX_DATA 0x200
+#define PREFIX_ADDR 0x400
+#define PREFIX_FWAIT 0x800
+
+/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
+ to ADDR (exclusive) are valid. Returns 1 for success, longjmps
+ on error. */
+static int
+fetch_data2(struct disassemble_info *info, bfd_byte *addr)
+{
+ int status;
+ struct dis_private *priv = (struct dis_private *) info->private_data;
+ bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
+
+ if (addr <= priv->the_buffer + MAX_MNEM_SIZE)
+ status = (*info->read_memory_func) (start,
+ priv->max_fetched,
+ addr - priv->max_fetched,
+ info);
+ else
+ status = -1;
+ if (status != 0)
+ {
+ /* If we did manage to read at least one byte, then
+ print_insn_i386 will do something sensible. Otherwise, print
+ an error. We do that here because this is where we know
+ STATUS. */
+ if (priv->max_fetched == priv->the_buffer)
+ (*info->memory_error_func) (status, start, info);
+ longjmp (priv->bailout, 1);
+ }
+ else
+ priv->max_fetched = addr;
+ return 1;
+}
+
+static int
+fetch_data(struct disassemble_info *info, bfd_byte *addr)
+{
+ if (addr <= ((struct dis_private *) (info->private_data))->max_fetched) {
+ return 1;
+ } else {
+ return fetch_data2(info, addr);
+ }
+}
+
+
+#define XX { NULL, 0 }
+
+#define Eb { OP_E, b_mode }
+#define Ev { OP_E, v_mode }
+#define Ed { OP_E, d_mode }
+#define Edq { OP_E, dq_mode }
+#define Edqw { OP_E, dqw_mode }
+#define Edqb { OP_E, dqb_mode }
+#define Edqd { OP_E, dqd_mode }
+#define indirEv { OP_indirE, stack_v_mode }
+#define indirEp { OP_indirE, f_mode }
+#define stackEv { OP_E, stack_v_mode }
+#define Em { OP_E, m_mode }
+#define Ew { OP_E, w_mode }
+#define M { OP_M, 0 } /* lea, lgdt, etc. */
+#define Ma { OP_M, v_mode }
+#define Mp { OP_M, f_mode } /* 32 or 48 bit memory operand for LDS, LES etc */
+#define Mq { OP_M, q_mode }
+#define Gb { OP_G, b_mode }
+#define Gv { OP_G, v_mode }
+#define Gd { OP_G, d_mode }
+#define Gdq { OP_G, dq_mode }
+#define Gm { OP_G, m_mode }
+#define Gw { OP_G, w_mode }
+#define Rd { OP_R, d_mode }
+#define Rm { OP_R, m_mode }
+#define Ib { OP_I, b_mode }
+#define sIb { OP_sI, b_mode } /* sign extened byte */
+#define Iv { OP_I, v_mode }
+#define Iq { OP_I, q_mode }
+#define Iv64 { OP_I64, v_mode }
+#define Iw { OP_I, w_mode }
+#define I1 { OP_I, const_1_mode }
+#define Jb { OP_J, b_mode }
+#define Jv { OP_J, v_mode }
+#define Cm { OP_C, m_mode }
+#define Dm { OP_D, m_mode }
+#define Td { OP_T, d_mode }
+
+#define RMeAX { OP_REG, eAX_reg }
+#define RMeBX { OP_REG, eBX_reg }
+#define RMeCX { OP_REG, eCX_reg }
+#define RMeDX { OP_REG, eDX_reg }
+#define RMeSP { OP_REG, eSP_reg }
+#define RMeBP { OP_REG, eBP_reg }
+#define RMeSI { OP_REG, eSI_reg }
+#define RMeDI { OP_REG, eDI_reg }
+#define RMrAX { OP_REG, rAX_reg }
+#define RMrBX { OP_REG, rBX_reg }
+#define RMrCX { OP_REG, rCX_reg }
+#define RMrDX { OP_REG, rDX_reg }
+#define RMrSP { OP_REG, rSP_reg }
+#define RMrBP { OP_REG, rBP_reg }
+#define RMrSI { OP_REG, rSI_reg }
+#define RMrDI { OP_REG, rDI_reg }
+#define RMAL { OP_REG, al_reg }
+#define RMAL { OP_REG, al_reg }
+#define RMCL { OP_REG, cl_reg }
+#define RMDL { OP_REG, dl_reg }
+#define RMBL { OP_REG, bl_reg }
+#define RMAH { OP_REG, ah_reg }
+#define RMCH { OP_REG, ch_reg }
+#define RMDH { OP_REG, dh_reg }
+#define RMBH { OP_REG, bh_reg }
+#define RMAX { OP_REG, ax_reg }
+#define RMDX { OP_REG, dx_reg }
+
+#define eAX { OP_IMREG, eAX_reg }
+#define eBX { OP_IMREG, eBX_reg }
+#define eCX { OP_IMREG, eCX_reg }
+#define eDX { OP_IMREG, eDX_reg }
+#define eSP { OP_IMREG, eSP_reg }
+#define eBP { OP_IMREG, eBP_reg }
+#define eSI { OP_IMREG, eSI_reg }
+#define eDI { OP_IMREG, eDI_reg }
+#define AL { OP_IMREG, al_reg }
+#define CL { OP_IMREG, cl_reg }
+#define DL { OP_IMREG, dl_reg }
+#define BL { OP_IMREG, bl_reg }
+#define AH { OP_IMREG, ah_reg }
+#define CH { OP_IMREG, ch_reg }
+#define DH { OP_IMREG, dh_reg }
+#define BH { OP_IMREG, bh_reg }
+#define AX { OP_IMREG, ax_reg }
+#define DX { OP_IMREG, dx_reg }
+#define zAX { OP_IMREG, z_mode_ax_reg }
+#define indirDX { OP_IMREG, indir_dx_reg }
+
+#define Sw { OP_SEG, w_mode }
+#define Sv { OP_SEG, v_mode }
+#define Ap { OP_DIR, 0 }
+#define Ob { OP_OFF64, b_mode }
+#define Ov { OP_OFF64, v_mode }
+#define Xb { OP_DSreg, eSI_reg }
+#define Xv { OP_DSreg, eSI_reg }
+#define Xz { OP_DSreg, eSI_reg }
+#define Yb { OP_ESreg, eDI_reg }
+#define Yv { OP_ESreg, eDI_reg }
+#define DSBX { OP_DSreg, eBX_reg }
+
+#define es { OP_REG, es_reg }
+#define ss { OP_REG, ss_reg }
+#define cs { OP_REG, cs_reg }
+#define ds { OP_REG, ds_reg }
+#define fs { OP_REG, fs_reg }
+#define gs { OP_REG, gs_reg }
+
+#define MX { OP_MMX, 0 }
+#define XM { OP_XMM, 0 }
+#define EM { OP_EM, v_mode }
+#define EMd { OP_EM, d_mode }
+#define EMq { OP_EM, q_mode }
+#define EXd { OP_EX, d_mode }
+#define EXq { OP_EX, q_mode }
+#define EXx { OP_EX, x_mode }
+#define MS { OP_MS, v_mode }
+#define XS { OP_XS, v_mode }
+#define EMC { OP_EMC, v_mode }
+#define MXC { OP_MXC, 0 }
+#define VM { OP_VMX, q_mode }
+#define OPSUF { OP_3DNowSuffix, 0 }
+#define OPSIMD { OP_SIMD_Suffix, 0 }
+#define XMM0 { XMM_Fixup, 0 }
+
+/* Used handle "rep" prefix for string instructions. */
+#define Xbr { REP_Fixup, eSI_reg }
+#define Xvr { REP_Fixup, eSI_reg }
+#define Ybr { REP_Fixup, eDI_reg }
+#define Yvr { REP_Fixup, eDI_reg }
+#define Yzr { REP_Fixup, eDI_reg }
+#define indirDXr { REP_Fixup, indir_dx_reg }
+#define ALr { REP_Fixup, al_reg }
+#define eAXr { REP_Fixup, eAX_reg }
+
+#define cond_jump_flag { NULL, cond_jump_mode }
+#define loop_jcxz_flag { NULL, loop_jcxz_mode }
+
+/* bits in sizeflag */
+#define SUFFIX_ALWAYS 4
+#define AFLAG 2
+#define DFLAG 1
+
+#define b_mode 1 /* byte operand */
+#define v_mode 2 /* operand size depends on prefixes */
+#define w_mode 3 /* word operand */
+#define d_mode 4 /* double word operand */
+#define q_mode 5 /* quad word operand */
+#define t_mode 6 /* ten-byte operand */
+#define x_mode 7 /* 16-byte XMM operand */
+#define m_mode 8 /* d_mode in 32bit, q_mode in 64bit mode. */
+#define cond_jump_mode 9
+#define loop_jcxz_mode 10
+#define dq_mode 11 /* operand size depends on REX prefixes. */
+#define dqw_mode 12 /* registers like dq_mode, memory like w_mode. */
+#define f_mode 13 /* 4- or 6-byte pointer operand */
+#define const_1_mode 14
+#define stack_v_mode 15 /* v_mode for stack-related opcodes. */
+#define z_mode 16 /* non-quad operand size depends on prefixes */
+#define o_mode 17 /* 16-byte operand */
+#define dqb_mode 18 /* registers like dq_mode, memory like b_mode. */
+#define dqd_mode 19 /* registers like dq_mode, memory like d_mode. */
+
+#define es_reg 100
+#define cs_reg 101
+#define ss_reg 102
+#define ds_reg 103
+#define fs_reg 104
+#define gs_reg 105
+
+#define eAX_reg 108
+#define eCX_reg 109
+#define eDX_reg 110
+#define eBX_reg 111
+#define eSP_reg 112
+#define eBP_reg 113
+#define eSI_reg 114
+#define eDI_reg 115
+
+#define al_reg 116
+#define cl_reg 117
+#define dl_reg 118
+#define bl_reg 119
+#define ah_reg 120
+#define ch_reg 121
+#define dh_reg 122
+#define bh_reg 123
+
+#define ax_reg 124
+#define cx_reg 125
+#define dx_reg 126
+#define bx_reg 127
+#define sp_reg 128
+#define bp_reg 129
+#define si_reg 130
+#define di_reg 131
+
+#define rAX_reg 132
+#define rCX_reg 133
+#define rDX_reg 134
+#define rBX_reg 135
+#define rSP_reg 136
+#define rBP_reg 137
+#define rSI_reg 138
+#define rDI_reg 139
+
+#define z_mode_ax_reg 149
+#define indir_dx_reg 150
+
+#define FLOATCODE 1
+#define USE_GROUPS 2
+#define USE_PREFIX_USER_TABLE 3
+#define X86_64_SPECIAL 4
+#define IS_3BYTE_OPCODE 5
+
+#define FLOAT NULL, { { NULL, FLOATCODE } }
+
+#define GRP1a NULL, { { NULL, USE_GROUPS }, { NULL, 0 } }
+#define GRP1b NULL, { { NULL, USE_GROUPS }, { NULL, 1 } }
+#define GRP1S NULL, { { NULL, USE_GROUPS }, { NULL, 2 } }
+#define GRP1Ss NULL, { { NULL, USE_GROUPS }, { NULL, 3 } }
+#define GRP2b NULL, { { NULL, USE_GROUPS }, { NULL, 4 } }
+#define GRP2S NULL, { { NULL, USE_GROUPS }, { NULL, 5 } }
+#define GRP2b_one NULL, { { NULL, USE_GROUPS }, { NULL, 6 } }
+#define GRP2S_one NULL, { { NULL, USE_GROUPS }, { NULL, 7 } }
+#define GRP2b_cl NULL, { { NULL, USE_GROUPS }, { NULL, 8 } }
+#define GRP2S_cl NULL, { { NULL, USE_GROUPS }, { NULL, 9 } }
+#define GRP3b NULL, { { NULL, USE_GROUPS }, { NULL, 10 } }
+#define GRP3S NULL, { { NULL, USE_GROUPS }, { NULL, 11 } }
+#define GRP4 NULL, { { NULL, USE_GROUPS }, { NULL, 12 } }
+#define GRP5 NULL, { { NULL, USE_GROUPS }, { NULL, 13 } }
+#define GRP6 NULL, { { NULL, USE_GROUPS }, { NULL, 14 } }
+#define GRP7 NULL, { { NULL, USE_GROUPS }, { NULL, 15 } }
+#define GRP8 NULL, { { NULL, USE_GROUPS }, { NULL, 16 } }
+#define GRP9 NULL, { { NULL, USE_GROUPS }, { NULL, 17 } }
+#define GRP11_C6 NULL, { { NULL, USE_GROUPS }, { NULL, 18 } }
+#define GRP11_C7 NULL, { { NULL, USE_GROUPS }, { NULL, 19 } }
+#define GRP12 NULL, { { NULL, USE_GROUPS }, { NULL, 20 } }
+#define GRP13 NULL, { { NULL, USE_GROUPS }, { NULL, 21 } }
+#define GRP14 NULL, { { NULL, USE_GROUPS }, { NULL, 22 } }
+#define GRP15 NULL, { { NULL, USE_GROUPS }, { NULL, 23 } }
+#define GRP16 NULL, { { NULL, USE_GROUPS }, { NULL, 24 } }
+#define GRPAMD NULL, { { NULL, USE_GROUPS }, { NULL, 25 } }
+#define GRPPADLCK1 NULL, { { NULL, USE_GROUPS }, { NULL, 26 } }
+#define GRPPADLCK2 NULL, { { NULL, USE_GROUPS }, { NULL, 27 } }
+
+#define PREGRP0 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 0 } }
+#define PREGRP1 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 1 } }
+#define PREGRP2 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 2 } }
+#define PREGRP3 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 3 } }
+#define PREGRP4 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 4 } }
+#define PREGRP5 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 5 } }
+#define PREGRP6 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 6 } }
+#define PREGRP7 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 7 } }
+#define PREGRP8 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 8 } }
+#define PREGRP9 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 9 } }
+#define PREGRP10 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 10 } }
+#define PREGRP11 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 11 } }
+#define PREGRP12 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 12 } }
+#define PREGRP13 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 13 } }
+#define PREGRP14 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 14 } }
+#define PREGRP15 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 15 } }
+#define PREGRP16 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 16 } }
+#define PREGRP17 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 17 } }
+#define PREGRP18 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 18 } }
+#define PREGRP19 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 19 } }
+#define PREGRP20 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 20 } }
+#define PREGRP21 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 21 } }
+#define PREGRP22 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 22 } }
+#define PREGRP23 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 23 } }
+#define PREGRP24 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 24 } }
+#define PREGRP25 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 25 } }
+#define PREGRP26 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 26 } }
+#define PREGRP27 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 27 } }
+#define PREGRP28 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 28 } }
+#define PREGRP29 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 29 } }
+#define PREGRP30 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 30 } }
+#define PREGRP31 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 31 } }
+#define PREGRP32 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 32 } }
+#define PREGRP33 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 33 } }
+#define PREGRP34 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 34 } }
+#define PREGRP35 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 35 } }
+#define PREGRP36 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 36 } }
+#define PREGRP37 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 37 } }
+#define PREGRP38 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 38 } }
+#define PREGRP39 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 39 } }
+#define PREGRP40 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 40 } }
+#define PREGRP41 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 41 } }
+#define PREGRP42 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 42 } }
+#define PREGRP43 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 43 } }
+#define PREGRP44 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 44 } }
+#define PREGRP45 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 45 } }
+#define PREGRP46 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 46 } }
+#define PREGRP47 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 47 } }
+#define PREGRP48 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 48 } }
+#define PREGRP49 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 49 } }
+#define PREGRP50 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 50 } }
+#define PREGRP51 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 51 } }
+#define PREGRP52 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 52 } }
+#define PREGRP53 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 53 } }
+#define PREGRP54 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 54 } }
+#define PREGRP55 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 55 } }
+#define PREGRP56 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 56 } }
+#define PREGRP57 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 57 } }
+#define PREGRP58 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 58 } }
+#define PREGRP59 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 59 } }
+#define PREGRP60 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 60 } }
+#define PREGRP61 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 61 } }
+#define PREGRP62 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 62 } }
+#define PREGRP63 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 63 } }
+#define PREGRP64 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 64 } }
+#define PREGRP65 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 65 } }
+#define PREGRP66 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 66 } }
+#define PREGRP67 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 67 } }
+#define PREGRP68 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 68 } }
+#define PREGRP69 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 69 } }
+#define PREGRP70 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 70 } }
+#define PREGRP71 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 71 } }
+#define PREGRP72 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 72 } }
+#define PREGRP73 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 73 } }
+#define PREGRP74 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 74 } }
+#define PREGRP75 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 75 } }
+#define PREGRP76 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 76 } }
+#define PREGRP77 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 77 } }
+#define PREGRP78 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 78 } }
+#define PREGRP79 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 79 } }
+#define PREGRP80 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 80 } }
+#define PREGRP81 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 81 } }
+#define PREGRP82 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 82 } }
+#define PREGRP83 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 83 } }
+#define PREGRP84 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 84 } }
+#define PREGRP85 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 85 } }
+#define PREGRP86 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 86 } }
+#define PREGRP87 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 87 } }
+#define PREGRP88 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 88 } }
+#define PREGRP89 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 89 } }
+#define PREGRP90 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 90 } }
+#define PREGRP91 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 91 } }
+#define PREGRP92 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 92 } }
+#define PREGRP93 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 93 } }
+#define PREGRP94 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 94 } }
+#define PREGRP95 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 95 } }
+#define PREGRP96 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 96 } }
+#define PREGRP97 NULL, { { NULL, USE_PREFIX_USER_TABLE }, { NULL, 97 } }
+
+
+#define X86_64_0 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 0 } }
+#define X86_64_1 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 1 } }
+#define X86_64_2 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 2 } }
+#define X86_64_3 NULL, { { NULL, X86_64_SPECIAL }, { NULL, 3 } }
+
+#define THREE_BYTE_0 NULL, { { NULL, IS_3BYTE_OPCODE }, { NULL, 0 } }
+#define THREE_BYTE_1 NULL, { { NULL, IS_3BYTE_OPCODE }, { NULL, 1 } }
+
+typedef void (*op_rtn) (int bytemode, int sizeflag);
+
+struct dis386 {
+ const char *name;
+ struct
+ {
+ op_rtn rtn;
+ int bytemode;
+ } op[MAX_OPERANDS];
+};
+
+/* Upper case letters in the instruction names here are macros.
+ 'A' => print 'b' if no register operands or suffix_always is true
+ 'B' => print 'b' if suffix_always is true
+ 'C' => print 's' or 'l' ('w' or 'd' in Intel mode) depending on operand
+ . size prefix
+ 'D' => print 'w' if no register operands or 'w', 'l' or 'q', if
+ . suffix_always is true
+ 'E' => print 'e' if 32-bit form of jcxz
+ 'F' => print 'w' or 'l' depending on address size prefix (loop insns)
+ 'G' => print 'w' or 'l' depending on operand size prefix (i/o insns)
+ 'H' => print ",pt" or ",pn" branch hint
+ 'I' => honor following macro letter even in Intel mode (implemented only
+ . for some of the macro letters)
+ 'J' => print 'l'
+ 'K' => print 'd' or 'q' if rex prefix is present.
+ 'L' => print 'l' if suffix_always is true
+ 'N' => print 'n' if instruction has no wait "prefix"
+ 'O' => print 'd' or 'o' (or 'q' in Intel mode)
+ 'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix,
+ . or suffix_always is true. print 'q' if rex prefix is present.
+ 'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always
+ . is true
+ 'R' => print 'w', 'l' or 'q' ('d' for 'l' and 'e' in Intel mode)
+ 'S' => print 'w', 'l' or 'q' if suffix_always is true
+ 'T' => print 'q' in 64bit mode and behave as 'P' otherwise
+ 'U' => print 'q' in 64bit mode and behave as 'Q' otherwise
+ 'V' => print 'q' in 64bit mode and behave as 'S' otherwise
+ 'W' => print 'b', 'w' or 'l' ('d' in Intel mode)
+ 'X' => print 's', 'd' depending on data16 prefix (for XMM)
+ 'Y' => 'q' if instruction has an REX 64bit overwrite prefix
+ 'Z' => print 'q' in 64bit mode and behave as 'L' otherwise
+
+ Many of the above letters print nothing in Intel mode. See "putop"
+ for the details.
+
+ Braces '{' and '}', and vertical bars '|', indicate alternative
+ mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel
+ modes. In cases where there are only two alternatives, the X86_64
+ instruction is reserved, and "(bad)" is printed.
+*/
+
+static const struct dis386 dis386[] = {
+ /* 00 */
+ { "addB", { Eb, Gb } },
+ { "addS", { Ev, Gv } },
+ { "addB", { Gb, Eb } },
+ { "addS", { Gv, Ev } },
+ { "addB", { AL, Ib } },
+ { "addS", { eAX, Iv } },
+ { "push{T|}", { es } },
+ { "pop{T|}", { es } },
+ /* 08 */
+ { "orB", { Eb, Gb } },
+ { "orS", { Ev, Gv } },
+ { "orB", { Gb, Eb } },
+ { "orS", { Gv, Ev } },
+ { "orB", { AL, Ib } },
+ { "orS", { eAX, Iv } },
+ { "push{T|}", { cs } },
+ { "(bad)", { XX } }, /* 0x0f extended opcode escape */
+ /* 10 */
+ { "adcB", { Eb, Gb } },
+ { "adcS", { Ev, Gv } },
+ { "adcB", { Gb, Eb } },
+ { "adcS", { Gv, Ev } },
+ { "adcB", { AL, Ib } },
+ { "adcS", { eAX, Iv } },
+ { "push{T|}", { ss } },
+ { "pop{T|}", { ss } },
+ /* 18 */
+ { "sbbB", { Eb, Gb } },
+ { "sbbS", { Ev, Gv } },
+ { "sbbB", { Gb, Eb } },
+ { "sbbS", { Gv, Ev } },
+ { "sbbB", { AL, Ib } },
+ { "sbbS", { eAX, Iv } },
+ { "push{T|}", { ds } },
+ { "pop{T|}", { ds } },
+ /* 20 */
+ { "andB", { Eb, Gb } },
+ { "andS", { Ev, Gv } },
+ { "andB", { Gb, Eb } },
+ { "andS", { Gv, Ev } },
+ { "andB", { AL, Ib } },
+ { "andS", { eAX, Iv } },
+ { "(bad)", { XX } }, /* SEG ES prefix */
+ { "daa{|}", { XX } },
+ /* 28 */
+ { "subB", { Eb, Gb } },
+ { "subS", { Ev, Gv } },
+ { "subB", { Gb, Eb } },
+ { "subS", { Gv, Ev } },
+ { "subB", { AL, Ib } },
+ { "subS", { eAX, Iv } },
+ { "(bad)", { XX } }, /* SEG CS prefix */
+ { "das{|}", { XX } },
+ /* 30 */
+ { "xorB", { Eb, Gb } },
+ { "xorS", { Ev, Gv } },
+ { "xorB", { Gb, Eb } },
+ { "xorS", { Gv, Ev } },
+ { "xorB", { AL, Ib } },
+ { "xorS", { eAX, Iv } },
+ { "(bad)", { XX } }, /* SEG SS prefix */
+ { "aaa{|}", { XX } },
+ /* 38 */
+ { "cmpB", { Eb, Gb } },
+ { "cmpS", { Ev, Gv } },
+ { "cmpB", { Gb, Eb } },
+ { "cmpS", { Gv, Ev } },
+ { "cmpB", { AL, Ib } },
+ { "cmpS", { eAX, Iv } },
+ { "(bad)", { XX } }, /* SEG DS prefix */
+ { "aas{|}", { XX } },
+ /* 40 */
+ { "inc{S|}", { RMeAX } },
+ { "inc{S|}", { RMeCX } },
+ { "inc{S|}", { RMeDX } },
+ { "inc{S|}", { RMeBX } },
+ { "inc{S|}", { RMeSP } },
+ { "inc{S|}", { RMeBP } },
+ { "inc{S|}", { RMeSI } },
+ { "inc{S|}", { RMeDI } },
+ /* 48 */
+ { "dec{S|}", { RMeAX } },
+ { "dec{S|}", { RMeCX } },
+ { "dec{S|}", { RMeDX } },
+ { "dec{S|}", { RMeBX } },
+ { "dec{S|}", { RMeSP } },
+ { "dec{S|}", { RMeBP } },
+ { "dec{S|}", { RMeSI } },
+ { "dec{S|}", { RMeDI } },
+ /* 50 */
+ { "pushV", { RMrAX } },
+ { "pushV", { RMrCX } },
+ { "pushV", { RMrDX } },
+ { "pushV", { RMrBX } },
+ { "pushV", { RMrSP } },
+ { "pushV", { RMrBP } },
+ { "pushV", { RMrSI } },
+ { "pushV", { RMrDI } },
+ /* 58 */
+ { "popV", { RMrAX } },
+ { "popV", { RMrCX } },
+ { "popV", { RMrDX } },
+ { "popV", { RMrBX } },
+ { "popV", { RMrSP } },
+ { "popV", { RMrBP } },
+ { "popV", { RMrSI } },
+ { "popV", { RMrDI } },
+ /* 60 */
+ { X86_64_0 },
+ { X86_64_1 },
+ { X86_64_2 },
+ { X86_64_3 },
+ { "(bad)", { XX } }, /* seg fs */
+ { "(bad)", { XX } }, /* seg gs */
+ { "(bad)", { XX } }, /* op size prefix */
+ { "(bad)", { XX } }, /* adr size prefix */
+ /* 68 */
+ { "pushT", { Iq } },
+ { "imulS", { Gv, Ev, Iv } },
+ { "pushT", { sIb } },
+ { "imulS", { Gv, Ev, sIb } },
+ { "ins{b||b|}", { Ybr, indirDX } },
+ { "ins{R||G|}", { Yzr, indirDX } },
+ { "outs{b||b|}", { indirDXr, Xb } },
+ { "outs{R||G|}", { indirDXr, Xz } },
+ /* 70 */
+ { "joH", { Jb, XX, cond_jump_flag } },
+ { "jnoH", { Jb, XX, cond_jump_flag } },
+ { "jbH", { Jb, XX, cond_jump_flag } },
+ { "jaeH", { Jb, XX, cond_jump_flag } },
+ { "jeH", { Jb, XX, cond_jump_flag } },
+ { "jneH", { Jb, XX, cond_jump_flag } },
+ { "jbeH", { Jb, XX, cond_jump_flag } },
+ { "jaH", { Jb, XX, cond_jump_flag } },
+ /* 78 */
+ { "jsH", { Jb, XX, cond_jump_flag } },
+ { "jnsH", { Jb, XX, cond_jump_flag } },
+ { "jpH", { Jb, XX, cond_jump_flag } },
+ { "jnpH", { Jb, XX, cond_jump_flag } },
+ { "jlH", { Jb, XX, cond_jump_flag } },
+ { "jgeH", { Jb, XX, cond_jump_flag } },
+ { "jleH", { Jb, XX, cond_jump_flag } },
+ { "jgH", { Jb, XX, cond_jump_flag } },
+ /* 80 */
+ { GRP1b },
+ { GRP1S },
+ { "(bad)", { XX } },
+ { GRP1Ss },
+ { "testB", { Eb, Gb } },
+ { "testS", { Ev, Gv } },
+ { "xchgB", { Eb, Gb } },
+ { "xchgS", { Ev, Gv } },
+ /* 88 */
+ { "movB", { Eb, Gb } },
+ { "movS", { Ev, Gv } },
+ { "movB", { Gb, Eb } },
+ { "movS", { Gv, Ev } },
+ { "movD", { Sv, Sw } },
+ { "leaS", { Gv, M } },
+ { "movD", { Sw, Sv } },
+ { GRP1a },
+ /* 90 */
+ { PREGRP38 },
+ { "xchgS", { RMeCX, eAX } },
+ { "xchgS", { RMeDX, eAX } },
+ { "xchgS", { RMeBX, eAX } },
+ { "xchgS", { RMeSP, eAX } },
+ { "xchgS", { RMeBP, eAX } },
+ { "xchgS", { RMeSI, eAX } },
+ { "xchgS", { RMeDI, eAX } },
+ /* 98 */
+ { "cW{t||t|}R", { XX } },
+ { "cR{t||t|}O", { XX } },
+ { "Jcall{T|}", { Ap } },
+ { "(bad)", { XX } }, /* fwait */
+ { "pushfT", { XX } },
+ { "popfT", { XX } },
+ { "sahf{|}", { XX } },
+ { "lahf{|}", { XX } },
+ /* a0 */
+ { "movB", { AL, Ob } },
+ { "movS", { eAX, Ov } },
+ { "movB", { Ob, AL } },
+ { "movS", { Ov, eAX } },
+ { "movs{b||b|}", { Ybr, Xb } },
+ { "movs{R||R|}", { Yvr, Xv } },
+ { "cmps{b||b|}", { Xb, Yb } },
+ { "cmps{R||R|}", { Xv, Yv } },
+ /* a8 */
+ { "testB", { AL, Ib } },
+ { "testS", { eAX, Iv } },
+ { "stosB", { Ybr, AL } },
+ { "stosS", { Yvr, eAX } },
+ { "lodsB", { ALr, Xb } },
+ { "lodsS", { eAXr, Xv } },
+ { "scasB", { AL, Yb } },
+ { "scasS", { eAX, Yv } },
+ /* b0 */
+ { "movB", { RMAL, Ib } },
+ { "movB", { RMCL, Ib } },
+ { "movB", { RMDL, Ib } },
+ { "movB", { RMBL, Ib } },
+ { "movB", { RMAH, Ib } },
+ { "movB", { RMCH, Ib } },
+ { "movB", { RMDH, Ib } },
+ { "movB", { RMBH, Ib } },
+ /* b8 */
+ { "movS", { RMeAX, Iv64 } },
+ { "movS", { RMeCX, Iv64 } },
+ { "movS", { RMeDX, Iv64 } },
+ { "movS", { RMeBX, Iv64 } },
+ { "movS", { RMeSP, Iv64 } },
+ { "movS", { RMeBP, Iv64 } },
+ { "movS", { RMeSI, Iv64 } },
+ { "movS", { RMeDI, Iv64 } },
+ /* c0 */
+ { GRP2b },
+ { GRP2S },
+ { "retT", { Iw } },
+ { "retT", { XX } },
+ { "les{S|}", { Gv, Mp } },
+ { "ldsS", { Gv, Mp } },
+ { GRP11_C6 },
+ { GRP11_C7 },
+ /* c8 */
+ { "enterT", { Iw, Ib } },
+ { "leaveT", { XX } },
+ { "lretP", { Iw } },
+ { "lretP", { XX } },
+ { "int3", { XX } },
+ { "int", { Ib } },
+ { "into{|}", { XX } },
+ { "iretP", { XX } },
+ /* d0 */
+ { GRP2b_one },
+ { GRP2S_one },
+ { GRP2b_cl },
+ { GRP2S_cl },
+ { "aam{|}", { sIb } },
+ { "aad{|}", { sIb } },
+ { "(bad)", { XX } },
+ { "xlat", { DSBX } },
+ /* d8 */
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ { FLOAT },
+ /* e0 */
+ { "loopneFH", { Jb, XX, loop_jcxz_flag } },
+ { "loopeFH", { Jb, XX, loop_jcxz_flag } },
+ { "loopFH", { Jb, XX, loop_jcxz_flag } },
+ { "jEcxzH", { Jb, XX, loop_jcxz_flag } },
+ { "inB", { AL, Ib } },
+ { "inG", { zAX, Ib } },
+ { "outB", { Ib, AL } },
+ { "outG", { Ib, zAX } },
+ /* e8 */
+ { "callT", { Jv } },
+ { "jmpT", { Jv } },
+ { "Jjmp{T|}", { Ap } },
+ { "jmp", { Jb } },
+ { "inB", { AL, indirDX } },
+ { "inG", { zAX, indirDX } },
+ { "outB", { indirDX, AL } },
+ { "outG", { indirDX, zAX } },
+ /* f0 */
+ { "(bad)", { XX } }, /* lock prefix */
+ { "icebp", { XX } },
+ { "(bad)", { XX } }, /* repne */
+ { "(bad)", { XX } }, /* repz */
+ { "hlt", { XX } },
+ { "cmc", { XX } },
+ { GRP3b },
+ { GRP3S },
+ /* f8 */
+ { "clc", { XX } },
+ { "stc", { XX } },
+ { "cli", { XX } },
+ { "sti", { XX } },
+ { "cld", { XX } },
+ { "std", { XX } },
+ { GRP4 },
+ { GRP5 },
+};
+
+static const struct dis386 dis386_twobyte[] = {
+ /* 00 */
+ { GRP6 },
+ { GRP7 },
+ { "larS", { Gv, Ew } },
+ { "lslS", { Gv, Ew } },
+ { "(bad)", { XX } },
+ { "syscall", { XX } },
+ { "clts", { XX } },
+ { "sysretP", { XX } },
+ /* 08 */
+ { "invd", { XX } },
+ { "wbinvd", { XX } },
+ { "(bad)", { XX } },
+ { "ud2a", { XX } },
+ { "(bad)", { XX } },
+ { GRPAMD },
+ { "femms", { XX } },
+ { "", { MX, EM, OPSUF } }, /* See OP_3DNowSuffix. */
+ /* 10 */
+ { PREGRP8 },
+ { PREGRP9 },
+ { PREGRP30 },
+ { "movlpX", { EXq, XM, { SIMD_Fixup, 'h' } } },
+ { "unpcklpX", { XM, EXq } },
+ { "unpckhpX", { XM, EXq } },
+ { PREGRP31 },
+ { "movhpX", { EXq, XM, { SIMD_Fixup, 'l' } } },
+ /* 18 */
+ { GRP16 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "nopQ", { Ev } },
+ /* 20 */
+ { "movZ", { Rm, Cm } },
+ { "movZ", { Rm, Dm } },
+ { "movZ", { Cm, Rm } },
+ { "movZ", { Dm, Rm } },
+ { "movL", { Rd, Td } },
+ { "(bad)", { XX } },
+ { "movL", { Td, Rd } },
+ { "(bad)", { XX } },
+ /* 28 */
+ { "movapX", { XM, EXx } },
+ { "movapX", { EXx, XM } },
+ { PREGRP2 },
+ { PREGRP33 },
+ { PREGRP4 },
+ { PREGRP3 },
+ { PREGRP93 },
+ { PREGRP94 },
+ /* 30 */
+ { "wrmsr", { XX } },
+ { "rdtsc", { XX } },
+ { "rdmsr", { XX } },
+ { "rdpmc", { XX } },
+ { "sysenter", { XX } },
+ { "sysexit", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 38 */
+ { THREE_BYTE_0 },
+ { "(bad)", { XX } },
+ { THREE_BYTE_1 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 40 */
+ { "cmovo", { Gv, Ev } },
+ { "cmovno", { Gv, Ev } },
+ { "cmovb", { Gv, Ev } },
+ { "cmovae", { Gv, Ev } },
+ { "cmove", { Gv, Ev } },
+ { "cmovne", { Gv, Ev } },
+ { "cmovbe", { Gv, Ev } },
+ { "cmova", { Gv, Ev } },
+ /* 48 */
+ { "cmovs", { Gv, Ev } },
+ { "cmovns", { Gv, Ev } },
+ { "cmovp", { Gv, Ev } },
+ { "cmovnp", { Gv, Ev } },
+ { "cmovl", { Gv, Ev } },
+ { "cmovge", { Gv, Ev } },
+ { "cmovle", { Gv, Ev } },
+ { "cmovg", { Gv, Ev } },
+ /* 50 */
+ { "movmskpX", { Gdq, XS } },
+ { PREGRP13 },
+ { PREGRP12 },
+ { PREGRP11 },
+ { "andpX", { XM, EXx } },
+ { "andnpX", { XM, EXx } },
+ { "orpX", { XM, EXx } },
+ { "xorpX", { XM, EXx } },
+ /* 58 */
+ { PREGRP0 },
+ { PREGRP10 },
+ { PREGRP17 },
+ { PREGRP16 },
+ { PREGRP14 },
+ { PREGRP7 },
+ { PREGRP5 },
+ { PREGRP6 },
+ /* 60 */
+ { PREGRP95 },
+ { PREGRP96 },
+ { PREGRP97 },
+ { "packsswb", { MX, EM } },
+ { "pcmpgtb", { MX, EM } },
+ { "pcmpgtw", { MX, EM } },
+ { "pcmpgtd", { MX, EM } },
+ { "packuswb", { MX, EM } },
+ /* 68 */
+ { "punpckhbw", { MX, EM } },
+ { "punpckhwd", { MX, EM } },
+ { "punpckhdq", { MX, EM } },
+ { "packssdw", { MX, EM } },
+ { PREGRP26 },
+ { PREGRP24 },
+ { "movd", { MX, Edq } },
+ { PREGRP19 },
+ /* 70 */
+ { PREGRP22 },
+ { GRP12 },
+ { GRP13 },
+ { GRP14 },
+ { "pcmpeqb", { MX, EM } },
+ { "pcmpeqw", { MX, EM } },
+ { "pcmpeqd", { MX, EM } },
+ { "emms", { XX } },
+ /* 78 */
+ { PREGRP34 },
+ { PREGRP35 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { PREGRP28 },
+ { PREGRP29 },
+ { PREGRP23 },
+ { PREGRP20 },
+ /* 80 */
+ { "joH", { Jv, XX, cond_jump_flag } },
+ { "jnoH", { Jv, XX, cond_jump_flag } },
+ { "jbH", { Jv, XX, cond_jump_flag } },
+ { "jaeH", { Jv, XX, cond_jump_flag } },
+ { "jeH", { Jv, XX, cond_jump_flag } },
+ { "jneH", { Jv, XX, cond_jump_flag } },
+ { "jbeH", { Jv, XX, cond_jump_flag } },
+ { "jaH", { Jv, XX, cond_jump_flag } },
+ /* 88 */
+ { "jsH", { Jv, XX, cond_jump_flag } },
+ { "jnsH", { Jv, XX, cond_jump_flag } },
+ { "jpH", { Jv, XX, cond_jump_flag } },
+ { "jnpH", { Jv, XX, cond_jump_flag } },
+ { "jlH", { Jv, XX, cond_jump_flag } },
+ { "jgeH", { Jv, XX, cond_jump_flag } },
+ { "jleH", { Jv, XX, cond_jump_flag } },
+ { "jgH", { Jv, XX, cond_jump_flag } },
+ /* 90 */
+ { "seto", { Eb } },
+ { "setno", { Eb } },
+ { "setb", { Eb } },
+ { "setae", { Eb } },
+ { "sete", { Eb } },
+ { "setne", { Eb } },
+ { "setbe", { Eb } },
+ { "seta", { Eb } },
+ /* 98 */
+ { "sets", { Eb } },
+ { "setns", { Eb } },
+ { "setp", { Eb } },
+ { "setnp", { Eb } },
+ { "setl", { Eb } },
+ { "setge", { Eb } },
+ { "setle", { Eb } },
+ { "setg", { Eb } },
+ /* a0 */
+ { "pushT", { fs } },
+ { "popT", { fs } },
+ { "cpuid", { XX } },
+ { "btS", { Ev, Gv } },
+ { "shldS", { Ev, Gv, Ib } },
+ { "shldS", { Ev, Gv, CL } },
+ { GRPPADLCK2 },
+ { GRPPADLCK1 },
+ /* a8 */
+ { "pushT", { gs } },
+ { "popT", { gs } },
+ { "rsm", { XX } },
+ { "btsS", { Ev, Gv } },
+ { "shrdS", { Ev, Gv, Ib } },
+ { "shrdS", { Ev, Gv, CL } },
+ { GRP15 },
+ { "imulS", { Gv, Ev } },
+ /* b0 */
+ { "cmpxchgB", { Eb, Gb } },
+ { "cmpxchgS", { Ev, Gv } },
+ { "lssS", { Gv, Mp } },
+ { "btrS", { Ev, Gv } },
+ { "lfsS", { Gv, Mp } },
+ { "lgsS", { Gv, Mp } },
+ { "movz{bR|x|bR|x}", { Gv, Eb } },
+ { "movz{wR|x|wR|x}", { Gv, Ew } }, /* yes, there really is movzww ! */
+ /* b8 */
+ { PREGRP37 },
+ { "ud2b", { XX } },
+ { GRP8 },
+ { "btcS", { Ev, Gv } },
+ { "bsfS", { Gv, Ev } },
+ { PREGRP36 },
+ { "movs{bR|x|bR|x}", { Gv, Eb } },
+ { "movs{wR|x|wR|x}", { Gv, Ew } }, /* yes, there really is movsww ! */
+ /* c0 */
+ { "xaddB", { Eb, Gb } },
+ { "xaddS", { Ev, Gv } },
+ { PREGRP1 },
+ { "movntiS", { Ev, Gv } },
+ { "pinsrw", { MX, Edqw, Ib } },
+ { "pextrw", { Gdq, MS, Ib } },
+ { "shufpX", { XM, EXx, Ib } },
+ { GRP9 },
+ /* c8 */
+ { "bswap", { RMeAX } },
+ { "bswap", { RMeCX } },
+ { "bswap", { RMeDX } },
+ { "bswap", { RMeBX } },
+ { "bswap", { RMeSP } },
+ { "bswap", { RMeBP } },
+ { "bswap", { RMeSI } },
+ { "bswap", { RMeDI } },
+ /* d0 */
+ { PREGRP27 },
+ { "psrlw", { MX, EM } },
+ { "psrld", { MX, EM } },
+ { "psrlq", { MX, EM } },
+ { "paddq", { MX, EM } },
+ { "pmullw", { MX, EM } },
+ { PREGRP21 },
+ { "pmovmskb", { Gdq, MS } },
+ /* d8 */
+ { "psubusb", { MX, EM } },
+ { "psubusw", { MX, EM } },
+ { "pminub", { MX, EM } },
+ { "pand", { MX, EM } },
+ { "paddusb", { MX, EM } },
+ { "paddusw", { MX, EM } },
+ { "pmaxub", { MX, EM } },
+ { "pandn", { MX, EM } },
+ /* e0 */
+ { "pavgb", { MX, EM } },
+ { "psraw", { MX, EM } },
+ { "psrad", { MX, EM } },
+ { "pavgw", { MX, EM } },
+ { "pmulhuw", { MX, EM } },
+ { "pmulhw", { MX, EM } },
+ { PREGRP15 },
+ { PREGRP25 },
+ /* e8 */
+ { "psubsb", { MX, EM } },
+ { "psubsw", { MX, EM } },
+ { "pminsw", { MX, EM } },
+ { "por", { MX, EM } },
+ { "paddsb", { MX, EM } },
+ { "paddsw", { MX, EM } },
+ { "pmaxsw", { MX, EM } },
+ { "pxor", { MX, EM } },
+ /* f0 */
+ { PREGRP32 },
+ { "psllw", { MX, EM } },
+ { "pslld", { MX, EM } },
+ { "psllq", { MX, EM } },
+ { "pmuludq", { MX, EM } },
+ { "pmaddwd", { MX, EM } },
+ { "psadbw", { MX, EM } },
+ { PREGRP18 },
+ /* f8 */
+ { "psubb", { MX, EM } },
+ { "psubw", { MX, EM } },
+ { "psubd", { MX, EM } },
+ { "psubq", { MX, EM } },
+ { "paddb", { MX, EM } },
+ { "paddw", { MX, EM } },
+ { "paddd", { MX, EM } },
+ { "(bad)", { XX } },
+};
+
+static const unsigned char onebyte_has_modrm[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */
+ /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */
+ /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */
+ /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
+ /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
+ /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */
+ /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */
+ /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */
+ /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static const unsigned char twobyte_has_modrm[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */
+ /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1, /* 1f */
+ /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, /* 3f */
+ /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */
+ /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */
+ /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */
+ /* 70 */ 1,1,1,1,1,1,1,0,1,1,0,0,1,1,1,1, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */
+ /* a0 */ 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1, /* af */
+ /* b0 */ 1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1, /* bf */
+ /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */
+ /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */
+ /* f0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0 /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static const unsigned char twobyte_uses_DATA_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */
+ /* 70 */ 1,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0 /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static const unsigned char twobyte_uses_REPNZ_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,1,0,0,0,0,0,0,1,1,1,0,1,1,1,1, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static const unsigned char twobyte_uses_REPZ_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, /* 6f */
+ /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, /* bf */
+ /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* This is used to determine if opcode 0f 38 XX uses DATA prefix. */
+static const unsigned char threebyte_0x38_uses_DATA_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, /* 0f */
+ /* 10 */ 1,0,0,0,1,1,0,1,0,0,0,0,1,1,1,0, /* 1f */
+ /* 20 */ 1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0, /* 2f */
+ /* 30 */ 1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1, /* 3f */
+ /* 40 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* This is used to determine if opcode 0f 38 XX uses REPNZ prefix. */
+static const unsigned char threebyte_0x38_uses_REPNZ_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* This is used to determine if opcode 0f 38 XX uses REPZ prefix. */
+static const unsigned char threebyte_0x38_uses_REPZ_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* This is used to determine if opcode 0f 3a XX uses DATA prefix. */
+static const unsigned char threebyte_0x3a_uses_DATA_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, /* 0f */
+ /* 10 */ 0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */
+ /* 60 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* This is used to determine if opcode 0f 3a XX uses REPNZ prefix. */
+static const unsigned char threebyte_0x3a_uses_REPNZ_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* This is used to determine if opcode 0f 3a XX uses REPZ prefix. */
+static const unsigned char threebyte_0x3a_uses_REPZ_prefix[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+ /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+ /* 20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 2f */
+ /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+ /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+ /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */
+ /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */
+ /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */
+ /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+ /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+ /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+ /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+ /* c0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+ /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */
+ /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */
+ /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+static char obuf[100];
+static char *obufp;
+static char scratchbuf[100];
+static unsigned char *start_codep;
+static unsigned char *insn_codep;
+static unsigned char *codep;
+static disassemble_info *the_info;
+static struct
+ {
+ int mod;
+ int reg;
+ int rm;
+ }
+modrm;
+static unsigned char need_modrm;
+
+/* If we are accessing mod/rm/reg without need_modrm set, then the
+ values are stale. Hitting this abort likely indicates that you
+ need to update onebyte_has_modrm or twobyte_has_modrm. */
+#define MODRM_CHECK if (!need_modrm) abort ()
+
+static const char * const *names64;
+static const char * const *names32;
+static const char * const *names16;
+static const char * const *names8;
+static const char * const *names8rex;
+static const char * const *names_seg;
+static const char * const *index16;
+
+static const char * const intel_names64[] = {
+ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+static const char * const intel_names32[] = {
+ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
+};
+static const char * const intel_names16[] = {
+ "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"
+};
+static const char * const intel_names8[] = {
+ "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+};
+static const char * const intel_names8rex[] = {
+ "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"
+};
+static const char * const intel_names_seg[] = {
+ "es", "cs", "ss", "ds", "fs", "gs", "?", "?",
+};
+static const char * const intel_index16[] = {
+ "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"
+};
+
+static const char * const att_names64[] = {
+ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
+};
+static const char * const att_names32[] = {
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
+ "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d"
+};
+static const char * const att_names16[] = {
+ "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+ "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w"
+};
+static const char * const att_names8[] = {
+ "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+};
+static const char * const att_names8rex[] = {
+ "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil",
+ "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+static const char * const att_names_seg[] = {
+ "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?",
+};
+static const char * const att_index16[] = {
+ "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx"
+};
+
+static const struct dis386 grps[][8] = {
+ /* GRP1a */
+ {
+ { "popU", { stackEv } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRP1b */
+ {
+ { "addA", { Eb, Ib } },
+ { "orA", { Eb, Ib } },
+ { "adcA", { Eb, Ib } },
+ { "sbbA", { Eb, Ib } },
+ { "andA", { Eb, Ib } },
+ { "subA", { Eb, Ib } },
+ { "xorA", { Eb, Ib } },
+ { "cmpA", { Eb, Ib } },
+ },
+ /* GRP1S */
+ {
+ { "addQ", { Ev, Iv } },
+ { "orQ", { Ev, Iv } },
+ { "adcQ", { Ev, Iv } },
+ { "sbbQ", { Ev, Iv } },
+ { "andQ", { Ev, Iv } },
+ { "subQ", { Ev, Iv } },
+ { "xorQ", { Ev, Iv } },
+ { "cmpQ", { Ev, Iv } },
+ },
+ /* GRP1Ss */
+ {
+ { "addQ", { Ev, sIb } },
+ { "orQ", { Ev, sIb } },
+ { "adcQ", { Ev, sIb } },
+ { "sbbQ", { Ev, sIb } },
+ { "andQ", { Ev, sIb } },
+ { "subQ", { Ev, sIb } },
+ { "xorQ", { Ev, sIb } },
+ { "cmpQ", { Ev, sIb } },
+ },
+ /* GRP2b */
+ {
+ { "rolA", { Eb, Ib } },
+ { "rorA", { Eb, Ib } },
+ { "rclA", { Eb, Ib } },
+ { "rcrA", { Eb, Ib } },
+ { "shlA", { Eb, Ib } },
+ { "shrA", { Eb, Ib } },
+ { "(bad)", { XX } },
+ { "sarA", { Eb, Ib } },
+ },
+ /* GRP2S */
+ {
+ { "rolQ", { Ev, Ib } },
+ { "rorQ", { Ev, Ib } },
+ { "rclQ", { Ev, Ib } },
+ { "rcrQ", { Ev, Ib } },
+ { "shlQ", { Ev, Ib } },
+ { "shrQ", { Ev, Ib } },
+ { "(bad)", { XX } },
+ { "sarQ", { Ev, Ib } },
+ },
+ /* GRP2b_one */
+ {
+ { "rolA", { Eb, I1 } },
+ { "rorA", { Eb, I1 } },
+ { "rclA", { Eb, I1 } },
+ { "rcrA", { Eb, I1 } },
+ { "shlA", { Eb, I1 } },
+ { "shrA", { Eb, I1 } },
+ { "(bad)", { XX } },
+ { "sarA", { Eb, I1 } },
+ },
+ /* GRP2S_one */
+ {
+ { "rolQ", { Ev, I1 } },
+ { "rorQ", { Ev, I1 } },
+ { "rclQ", { Ev, I1 } },
+ { "rcrQ", { Ev, I1 } },
+ { "shlQ", { Ev, I1 } },
+ { "shrQ", { Ev, I1 } },
+ { "(bad)", { XX } },
+ { "sarQ", { Ev, I1 } },
+ },
+ /* GRP2b_cl */
+ {
+ { "rolA", { Eb, CL } },
+ { "rorA", { Eb, CL } },
+ { "rclA", { Eb, CL } },
+ { "rcrA", { Eb, CL } },
+ { "shlA", { Eb, CL } },
+ { "shrA", { Eb, CL } },
+ { "(bad)", { XX } },
+ { "sarA", { Eb, CL } },
+ },
+ /* GRP2S_cl */
+ {
+ { "rolQ", { Ev, CL } },
+ { "rorQ", { Ev, CL } },
+ { "rclQ", { Ev, CL } },
+ { "rcrQ", { Ev, CL } },
+ { "shlQ", { Ev, CL } },
+ { "shrQ", { Ev, CL } },
+ { "(bad)", { XX } },
+ { "sarQ", { Ev, CL } },
+ },
+ /* GRP3b */
+ {
+ { "testA", { Eb, Ib } },
+ { "(bad)", { Eb } },
+ { "notA", { Eb } },
+ { "negA", { Eb } },
+ { "mulA", { Eb } }, /* Don't print the implicit %al register, */
+ { "imulA", { Eb } }, /* to distinguish these opcodes from other */
+ { "divA", { Eb } }, /* mul/imul opcodes. Do the same for div */
+ { "idivA", { Eb } }, /* and idiv for consistency. */
+ },
+ /* GRP3S */
+ {
+ { "testQ", { Ev, Iv } },
+ { "(bad)", { XX } },
+ { "notQ", { Ev } },
+ { "negQ", { Ev } },
+ { "mulQ", { Ev } }, /* Don't print the implicit register. */
+ { "imulQ", { Ev } },
+ { "divQ", { Ev } },
+ { "idivQ", { Ev } },
+ },
+ /* GRP4 */
+ {
+ { "incA", { Eb } },
+ { "decA", { Eb } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRP5 */
+ {
+ { "incQ", { Ev } },
+ { "decQ", { Ev } },
+ { "callT", { indirEv } },
+ { "JcallT", { indirEp } },
+ { "jmpT", { indirEv } },
+ { "JjmpT", { indirEp } },
+ { "pushU", { stackEv } },
+ { "(bad)", { XX } },
+ },
+ /* GRP6 */
+ {
+ { "sldtD", { Sv } },
+ { "strD", { Sv } },
+ { "lldt", { Ew } },
+ { "ltr", { Ew } },
+ { "verr", { Ew } },
+ { "verw", { Ew } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRP7 */
+ {
+ { "sgdt{Q|IQ||}", { { VMX_Fixup, 0 } } },
+ { "sidt{Q|IQ||}", { { PNI_Fixup, 0 } } },
+ { "lgdt{Q|Q||}", { M } },
+ { "lidt{Q|Q||}", { { SVME_Fixup, 0 } } },
+ { "smswD", { Sv } },
+ { "(bad)", { XX } },
+ { "lmsw", { Ew } },
+ { "invlpg", { { INVLPG_Fixup, w_mode } } },
+ },
+ /* GRP8 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "btQ", { Ev, Ib } },
+ { "btsQ", { Ev, Ib } },
+ { "btrQ", { Ev, Ib } },
+ { "btcQ", { Ev, Ib } },
+ },
+ /* GRP9 */
+ {
+ { "(bad)", { XX } },
+ { "cmpxchg8b", { { CMPXCHG8B_Fixup, q_mode } } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "", { VM } }, /* See OP_VMX. */
+ { "vmptrst", { Mq } },
+ },
+ /* GRP11_C6 */
+ {
+ { "movA", { Eb, Ib } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRP11_C7 */
+ {
+ { "movQ", { Ev, Iv } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRP12 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "psrlw", { MS, Ib } },
+ { "(bad)", { XX } },
+ { "psraw", { MS, Ib } },
+ { "(bad)", { XX } },
+ { "psllw", { MS, Ib } },
+ { "(bad)", { XX } },
+ },
+ /* GRP13 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "psrld", { MS, Ib } },
+ { "(bad)", { XX } },
+ { "psrad", { MS, Ib } },
+ { "(bad)", { XX } },
+ { "pslld", { MS, Ib } },
+ { "(bad)", { XX } },
+ },
+ /* GRP14 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "psrlq", { MS, Ib } },
+ { "psrldq", { MS, Ib } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "psllq", { MS, Ib } },
+ { "pslldq", { MS, Ib } },
+ },
+ /* GRP15 */
+ {
+ { "fxsave", { Ev } },
+ { "fxrstor", { Ev } },
+ { "ldmxcsr", { Ev } },
+ { "stmxcsr", { Ev } },
+ { "(bad)", { XX } },
+ { "lfence", { { OP_0fae, 0 } } },
+ { "mfence", { { OP_0fae, 0 } } },
+ { "clflush", { { OP_0fae, 0 } } },
+ },
+ /* GRP16 */
+ {
+ { "prefetchnta", { Ev } },
+ { "prefetcht0", { Ev } },
+ { "prefetcht1", { Ev } },
+ { "prefetcht2", { Ev } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRPAMD */
+ {
+ { "prefetch", { Eb } },
+ { "prefetchw", { Eb } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* GRPPADLCK1 */
+ {
+ { "xstore-rng", { { OP_0f07, 0 } } },
+ { "xcrypt-ecb", { { OP_0f07, 0 } } },
+ { "xcrypt-cbc", { { OP_0f07, 0 } } },
+ { "xcrypt-ctr", { { OP_0f07, 0 } } },
+ { "xcrypt-cfb", { { OP_0f07, 0 } } },
+ { "xcrypt-ofb", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ },
+ /* GRPPADLCK2 */
+ {
+ { "montmul", { { OP_0f07, 0 } } },
+ { "xsha1", { { OP_0f07, 0 } } },
+ { "xsha256", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ { "(bad)", { { OP_0f07, 0 } } },
+ }
+};
+
+static const struct dis386 prefix_user_table[][4] = {
+ /* PREGRP0 */
+ {
+ { "addps", { XM, EXx } },
+ { "addss", { XM, EXd } },
+ { "addpd", { XM, EXx } },
+ { "addsd", { XM, EXq } },
+ },
+ /* PREGRP1 */
+ {
+ { "", { XM, EXx, OPSIMD } }, /* See OP_SIMD_SUFFIX. */
+ { "", { XM, EXx, OPSIMD } },
+ { "", { XM, EXx, OPSIMD } },
+ { "", { XM, EXx, OPSIMD } },
+ },
+ /* PREGRP2 */
+ {
+ { "cvtpi2ps", { XM, EMC } },
+ { "cvtsi2ssY", { XM, Ev } },
+ { "cvtpi2pd", { XM, EMC } },
+ { "cvtsi2sdY", { XM, Ev } },
+ },
+ /* PREGRP3 */
+ {
+ { "cvtps2pi", { MXC, EXx } },
+ { "cvtss2siY", { Gv, EXx } },
+ { "cvtpd2pi", { MXC, EXx } },
+ { "cvtsd2siY", { Gv, EXx } },
+ },
+ /* PREGRP4 */
+ {
+ { "cvttps2pi", { MXC, EXx } },
+ { "cvttss2siY", { Gv, EXx } },
+ { "cvttpd2pi", { MXC, EXx } },
+ { "cvttsd2siY", { Gv, EXx } },
+ },
+ /* PREGRP5 */
+ {
+ { "divps", { XM, EXx } },
+ { "divss", { XM, EXx } },
+ { "divpd", { XM, EXx } },
+ { "divsd", { XM, EXx } },
+ },
+ /* PREGRP6 */
+ {
+ { "maxps", { XM, EXx } },
+ { "maxss", { XM, EXx } },
+ { "maxpd", { XM, EXx } },
+ { "maxsd", { XM, EXx } },
+ },
+ /* PREGRP7 */
+ {
+ { "minps", { XM, EXx } },
+ { "minss", { XM, EXx } },
+ { "minpd", { XM, EXx } },
+ { "minsd", { XM, EXx } },
+ },
+ /* PREGRP8 */
+ {
+ { "movups", { XM, EXx } },
+ { "movss", { XM, EXx } },
+ { "movupd", { XM, EXx } },
+ { "movsd", { XM, EXx } },
+ },
+ /* PREGRP9 */
+ {
+ { "movups", { EXx, XM } },
+ { "movss", { EXx, XM } },
+ { "movupd", { EXx, XM } },
+ { "movsd", { EXx, XM } },
+ },
+ /* PREGRP10 */
+ {
+ { "mulps", { XM, EXx } },
+ { "mulss", { XM, EXx } },
+ { "mulpd", { XM, EXx } },
+ { "mulsd", { XM, EXx } },
+ },
+ /* PREGRP11 */
+ {
+ { "rcpps", { XM, EXx } },
+ { "rcpss", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP12 */
+ {
+ { "rsqrtps",{ XM, EXx } },
+ { "rsqrtss",{ XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP13 */
+ {
+ { "sqrtps", { XM, EXx } },
+ { "sqrtss", { XM, EXx } },
+ { "sqrtpd", { XM, EXx } },
+ { "sqrtsd", { XM, EXx } },
+ },
+ /* PREGRP14 */
+ {
+ { "subps", { XM, EXx } },
+ { "subss", { XM, EXx } },
+ { "subpd", { XM, EXx } },
+ { "subsd", { XM, EXx } },
+ },
+ /* PREGRP15 */
+ {
+ { "(bad)", { XM, EXx } },
+ { "cvtdq2pd", { XM, EXq } },
+ { "cvttpd2dq", { XM, EXx } },
+ { "cvtpd2dq", { XM, EXx } },
+ },
+ /* PREGRP16 */
+ {
+ { "cvtdq2ps", { XM, EXx } },
+ { "cvttps2dq", { XM, EXx } },
+ { "cvtps2dq", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP17 */
+ {
+ { "cvtps2pd", { XM, EXq } },
+ { "cvtss2sd", { XM, EXx } },
+ { "cvtpd2ps", { XM, EXx } },
+ { "cvtsd2ss", { XM, EXx } },
+ },
+ /* PREGRP18 */
+ {
+ { "maskmovq", { MX, MS } },
+ { "(bad)", { XM, EXx } },
+ { "maskmovdqu", { XM, XS } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP19 */
+ {
+ { "movq", { MX, EM } },
+ { "movdqu", { XM, EXx } },
+ { "movdqa", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP20 */
+ {
+ { "movq", { EM, MX } },
+ { "movdqu", { EXx, XM } },
+ { "movdqa", { EXx, XM } },
+ { "(bad)", { EXx, XM } },
+ },
+ /* PREGRP21 */
+ {
+ { "(bad)", { EXx, XM } },
+ { "movq2dq",{ XM, MS } },
+ { "movq", { EXx, XM } },
+ { "movdq2q",{ MX, XS } },
+ },
+ /* PREGRP22 */
+ {
+ { "pshufw", { MX, EM, Ib } },
+ { "pshufhw",{ XM, EXx, Ib } },
+ { "pshufd", { XM, EXx, Ib } },
+ { "pshuflw",{ XM, EXx, Ib } },
+ },
+ /* PREGRP23 */
+ {
+ { "movd", { Edq, MX } },
+ { "movq", { XM, EXx } },
+ { "movd", { Edq, XM } },
+ { "(bad)", { Ed, XM } },
+ },
+ /* PREGRP24 */
+ {
+ { "(bad)", { MX, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "punpckhqdq", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP25 */
+ {
+ { "movntq", { EM, MX } },
+ { "(bad)", { EM, XM } },
+ { "movntdq",{ EM, XM } },
+ { "(bad)", { EM, XM } },
+ },
+ /* PREGRP26 */
+ {
+ { "(bad)", { MX, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "punpcklqdq", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ },
+ /* PREGRP27 */
+ {
+ { "(bad)", { MX, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "addsubpd", { XM, EXx } },
+ { "addsubps", { XM, EXx } },
+ },
+ /* PREGRP28 */
+ {
+ { "(bad)", { MX, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "haddpd", { XM, EXx } },
+ { "haddps", { XM, EXx } },
+ },
+ /* PREGRP29 */
+ {
+ { "(bad)", { MX, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "hsubpd", { XM, EXx } },
+ { "hsubps", { XM, EXx } },
+ },
+ /* PREGRP30 */
+ {
+ { "movlpX", { XM, EXq, { SIMD_Fixup, 'h' } } }, /* really only 2 operands */
+ { "movsldup", { XM, EXx } },
+ { "movlpd", { XM, EXq } },
+ { "movddup", { XM, EXq } },
+ },
+ /* PREGRP31 */
+ {
+ { "movhpX", { XM, EXq, { SIMD_Fixup, 'l' } } },
+ { "movshdup", { XM, EXx } },
+ { "movhpd", { XM, EXq } },
+ { "(bad)", { XM, EXq } },
+ },
+ /* PREGRP32 */
+ {
+ { "(bad)", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "(bad)", { XM, EXx } },
+ { "lddqu", { XM, M } },
+ },
+ /* PREGRP33 */
+ {
+ {"movntps", { Ev, XM } },
+ {"movntss", { Ev, XM } },
+ {"movntpd", { Ev, XM } },
+ {"movntsd", { Ev, XM } },
+ },
+
+ /* PREGRP34 */
+ {
+ {"vmread", { Em, Gm } },
+ {"(bad)", { XX } },
+ {"extrq", { XS, Ib, Ib } },
+ {"insertq", { XM, XS, Ib, Ib } },
+ },
+
+ /* PREGRP35 */
+ {
+ {"vmwrite", { Gm, Em } },
+ {"(bad)", { XX } },
+ {"extrq", { XM, XS } },
+ {"insertq", { XM, XS } },
+ },
+
+ /* PREGRP36 */
+ {
+ { "bsrS", { Gv, Ev } },
+ { "lzcntS", { Gv, Ev } },
+ { "bsrS", { Gv, Ev } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP37 */
+ {
+ { "(bad)", { XX } },
+ { "popcntS", { Gv, Ev } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP38 */
+ {
+ { "xchgS", { { NOP_Fixup1, eAX_reg }, { NOP_Fixup2, eAX_reg } } },
+ { "pause", { XX } },
+ { "xchgS", { { NOP_Fixup1, eAX_reg }, { NOP_Fixup2, eAX_reg } } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP39 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pblendvb", {XM, EXx, XMM0 } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP40 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "blendvps", {XM, EXx, XMM0 } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP41 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "blendvpd", { XM, EXx, XMM0 } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP42 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "ptest", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP43 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovsxbw", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP44 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovsxbd", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP45 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovsxbq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP46 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovsxwd", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP47 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovsxwq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP48 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovsxdq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP49 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmuldq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP50 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pcmpeqq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP51 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "movntdqa", { XM, EM } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP52 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "packusdw", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP53 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovzxbw", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP54 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovzxbd", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP55 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovzxbq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP56 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovzxwd", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP57 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovzxwq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP58 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmovzxdq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP59 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pminsb", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP60 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pminsd", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP61 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pminuw", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP62 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pminud", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP63 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmaxsb", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP64 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmaxsd", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP65 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmaxuw", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP66 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmaxud", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP67 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pmulld", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP68 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "phminposuw", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP69 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "roundps", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP70 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "roundpd", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP71 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "roundss", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP72 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "roundsd", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP73 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "blendps", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP74 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "blendpd", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP75 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pblendw", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP76 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pextrb", { Edqb, XM, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP77 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pextrw", { Edqw, XM, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP78 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pextrK", { Edq, XM, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP79 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "extractps", { Edqd, XM, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP80 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pinsrb", { XM, Edqb, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP81 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "insertps", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP82 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pinsrK", { XM, Edq, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP83 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "dpps", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP84 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "dppd", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP85 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "mpsadbw", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP86 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pcmpgtq", { XM, EXx } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP87 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "crc32", { Gdq, { CRC32_Fixup, b_mode } } },
+ },
+
+ /* PREGRP88 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "crc32", { Gdq, { CRC32_Fixup, v_mode } } },
+ },
+
+ /* PREGRP89 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pcmpestrm", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP90 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pcmpestri", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP91 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pcmpistrm", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP92 */
+ {
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pcmpistri", { XM, EXx, Ib } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP93 */
+ {
+ { "ucomiss",{ XM, EXd } },
+ { "(bad)", { XX } },
+ { "ucomisd",{ XM, EXq } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP94 */
+ {
+ { "comiss", { XM, EXd } },
+ { "(bad)", { XX } },
+ { "comisd", { XM, EXq } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP95 */
+ {
+ { "punpcklbw",{ MX, EMd } },
+ { "(bad)", { XX } },
+ { "punpcklbw",{ MX, EMq } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP96 */
+ {
+ { "punpcklwd",{ MX, EMd } },
+ { "(bad)", { XX } },
+ { "punpcklwd",{ MX, EMq } },
+ { "(bad)", { XX } },
+ },
+
+ /* PREGRP97 */
+ {
+ { "punpckldq",{ MX, EMd } },
+ { "(bad)", { XX } },
+ { "punpckldq",{ MX, EMq } },
+ { "(bad)", { XX } },
+ },
+};
+
+static const struct dis386 x86_64_table[][2] = {
+ {
+ { "pusha{P|}", { XX } },
+ { "(bad)", { XX } },
+ },
+ {
+ { "popa{P|}", { XX } },
+ { "(bad)", { XX } },
+ },
+ {
+ { "bound{S|}", { Gv, Ma } },
+ { "(bad)", { XX } },
+ },
+ {
+ { "arpl", { Ew, Gw } },
+ { "movs{||lq|xd}", { Gv, Ed } },
+ },
+};
+
+static const struct dis386 three_byte_table[][256] = {
+ /* THREE_BYTE_0 */
+ {
+ /* 00 */
+ { "pshufb", { MX, EM } },
+ { "phaddw", { MX, EM } },
+ { "phaddd", { MX, EM } },
+ { "phaddsw", { MX, EM } },
+ { "pmaddubsw", { MX, EM } },
+ { "phsubw", { MX, EM } },
+ { "phsubd", { MX, EM } },
+ { "phsubsw", { MX, EM } },
+ /* 08 */
+ { "psignb", { MX, EM } },
+ { "psignw", { MX, EM } },
+ { "psignd", { MX, EM } },
+ { "pmulhrsw", { MX, EM } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 10 */
+ { PREGRP39 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { PREGRP40 },
+ { PREGRP41 },
+ { "(bad)", { XX } },
+ { PREGRP42 },
+ /* 18 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "pabsb", { MX, EM } },
+ { "pabsw", { MX, EM } },
+ { "pabsd", { MX, EM } },
+ { "(bad)", { XX } },
+ /* 20 */
+ { PREGRP43 },
+ { PREGRP44 },
+ { PREGRP45 },
+ { PREGRP46 },
+ { PREGRP47 },
+ { PREGRP48 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 28 */
+ { PREGRP49 },
+ { PREGRP50 },
+ { PREGRP51 },
+ { PREGRP52 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 30 */
+ { PREGRP53 },
+ { PREGRP54 },
+ { PREGRP55 },
+ { PREGRP56 },
+ { PREGRP57 },
+ { PREGRP58 },
+ { "(bad)", { XX } },
+ { PREGRP86 },
+ /* 38 */
+ { PREGRP59 },
+ { PREGRP60 },
+ { PREGRP61 },
+ { PREGRP62 },
+ { PREGRP63 },
+ { PREGRP64 },
+ { PREGRP65 },
+ { PREGRP66 },
+ /* 40 */
+ { PREGRP67 },
+ { PREGRP68 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 48 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 50 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 58 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 60 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 68 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 70 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 78 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 80 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 88 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 90 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 98 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* a0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* a8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* b0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* b8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* c0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* c8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* d0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* d8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* e0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* e8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* f0 */
+ { PREGRP87 },
+ { PREGRP88 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* f8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* THREE_BYTE_1 */
+ {
+ /* 00 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 08 */
+ { PREGRP69 },
+ { PREGRP70 },
+ { PREGRP71 },
+ { PREGRP72 },
+ { PREGRP73 },
+ { PREGRP74 },
+ { PREGRP75 },
+ { "palignr", { MX, EM, Ib } },
+ /* 10 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { PREGRP76 },
+ { PREGRP77 },
+ { PREGRP78 },
+ { PREGRP79 },
+ /* 18 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 20 */
+ { PREGRP80 },
+ { PREGRP81 },
+ { PREGRP82 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 28 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 30 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 38 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 40 */
+ { PREGRP83 },
+ { PREGRP84 },
+ { PREGRP85 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 48 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 50 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 58 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 60 */
+ { PREGRP89 },
+ { PREGRP90 },
+ { PREGRP91 },
+ { PREGRP92 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 68 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 70 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 78 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 80 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 88 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 90 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* 98 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* a0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* a8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* b0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* b8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* c0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* c8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* d0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* d8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* e0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* e8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* f0 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ /* f8 */
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ }
+};
+
+#define INTERNAL_DISASSEMBLER_ERROR _("<internal disassembler error>")
+
+static void
+ckprefix (void)
+{
+ int newrex;
+ rex = 0;
+ prefixes = 0;
+ used_prefixes = 0;
+ rex_used = 0;
+ while (1)
+ {
+ fetch_data(the_info, codep + 1);
+ newrex = 0;
+ switch (*codep)
+ {
+ /* REX prefixes family. */
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ if (address_mode == mode_64bit)
+ newrex = *codep;
+ else
+ return;
+ break;
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ break;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ break;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ break;
+ case 0x2e:
+ prefixes |= PREFIX_CS;
+ break;
+ case 0x36:
+ prefixes |= PREFIX_SS;
+ break;
+ case 0x3e:
+ prefixes |= PREFIX_DS;
+ break;
+ case 0x26:
+ prefixes |= PREFIX_ES;
+ break;
+ case 0x64:
+ prefixes |= PREFIX_FS;
+ break;
+ case 0x65:
+ prefixes |= PREFIX_GS;
+ break;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ break;
+ case 0x67:
+ prefixes |= PREFIX_ADDR;
+ break;
+ case FWAIT_OPCODE:
+ /* fwait is really an instruction. If there are prefixes
+ before the fwait, they belong to the fwait, *not* to the
+ following instruction. */
+ if (prefixes || rex)
+ {
+ prefixes |= PREFIX_FWAIT;
+ codep++;
+ return;
+ }
+ prefixes = PREFIX_FWAIT;
+ break;
+ default:
+ return;
+ }
+ /* Rex is ignored when followed by another prefix. */
+ if (rex)
+ {
+ rex_used = rex;
+ return;
+ }
+ rex = newrex;
+ codep++;
+ }
+}
+
+/* Return the name of the prefix byte PREF, or NULL if PREF is not a
+ prefix byte. */
+
+static const char *
+prefix_name (int pref, int sizeflag)
+{
+ static const char * const rexes [16] =
+ {
+ "rex", /* 0x40 */
+ "rex.B", /* 0x41 */
+ "rex.X", /* 0x42 */
+ "rex.XB", /* 0x43 */
+ "rex.R", /* 0x44 */
+ "rex.RB", /* 0x45 */
+ "rex.RX", /* 0x46 */
+ "rex.RXB", /* 0x47 */
+ "rex.W", /* 0x48 */
+ "rex.WB", /* 0x49 */
+ "rex.WX", /* 0x4a */
+ "rex.WXB", /* 0x4b */
+ "rex.WR", /* 0x4c */
+ "rex.WRB", /* 0x4d */
+ "rex.WRX", /* 0x4e */
+ "rex.WRXB", /* 0x4f */
+ };
+
+ switch (pref)
+ {
+ /* REX prefixes family. */
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ return rexes [pref - 0x40];
+ case 0xf3:
+ return "repz";
+ case 0xf2:
+ return "repnz";
+ case 0xf0:
+ return "lock";
+ case 0x2e:
+ return "cs";
+ case 0x36:
+ return "ss";
+ case 0x3e:
+ return "ds";
+ case 0x26:
+ return "es";
+ case 0x64:
+ return "fs";
+ case 0x65:
+ return "gs";
+ case 0x66:
+ return (sizeflag & DFLAG) ? "data16" : "data32";
+ case 0x67:
+ if (address_mode == mode_64bit)
+ return (sizeflag & AFLAG) ? "addr32" : "addr64";
+ else
+ return (sizeflag & AFLAG) ? "addr16" : "addr32";
+ case FWAIT_OPCODE:
+ return "fwait";
+ default:
+ return NULL;
+ }
+}
+
+static char op_out[MAX_OPERANDS][100];
+static int op_ad, op_index[MAX_OPERANDS];
+static int two_source_ops;
+static bfd_vma op_address[MAX_OPERANDS];
+static bfd_vma op_riprel[MAX_OPERANDS];
+static bfd_vma start_pc;
+
+/*
+ * On the 386's of 1988, the maximum length of an instruction is 15 bytes.
+ * (see topic "Redundant prefixes" in the "Differences from 8086"
+ * section of the "Virtual 8086 Mode" chapter.)
+ * 'pc' should be the address of this instruction, it will
+ * be used to print the target address if this is a relative jump or call
+ * The function returns the length of this instruction in bytes.
+ */
+
+static char intel_syntax;
+static char open_char;
+static char close_char;
+static char separator_char;
+static char scale_char;
+
+int
+print_insn_i386 (bfd_vma pc, disassemble_info *info)
+{
+ intel_syntax = -1;
+
+ return print_insn (pc, info);
+}
+
+static int
+print_insn (bfd_vma pc, disassemble_info *info)
+{
+ const struct dis386 *dp;
+ int i;
+ char *op_txt[MAX_OPERANDS];
+ int needcomma;
+ unsigned char uses_DATA_prefix, uses_LOCK_prefix;
+ unsigned char uses_REPNZ_prefix, uses_REPZ_prefix;
+ int sizeflag;
+ const char *p;
+ struct dis_private priv;
+ unsigned char op;
+
+ if (info->mach == bfd_mach_x86_64_intel_syntax
+ || info->mach == bfd_mach_x86_64)
+ address_mode = mode_64bit;
+ else
+ address_mode = mode_32bit;
+
+ if (intel_syntax == (char) -1)
+ intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax
+ || info->mach == bfd_mach_x86_64_intel_syntax);
+
+ if (info->mach == bfd_mach_i386_i386
+ || info->mach == bfd_mach_x86_64
+ || info->mach == bfd_mach_i386_i386_intel_syntax
+ || info->mach == bfd_mach_x86_64_intel_syntax)
+ priv.orig_sizeflag = AFLAG | DFLAG;
+ else if (info->mach == bfd_mach_i386_i8086)
+ priv.orig_sizeflag = 0;
+ else
+ abort ();
+
+ for (p = info->disassembler_options; p != NULL; )
+ {
+ if (strncmp (p, "x86-64", 6) == 0)
+ {
+ address_mode = mode_64bit;
+ priv.orig_sizeflag = AFLAG | DFLAG;
+ }
+ else if (strncmp (p, "i386", 4) == 0)
+ {
+ address_mode = mode_32bit;
+ priv.orig_sizeflag = AFLAG | DFLAG;
+ }
+ else if (strncmp (p, "i8086", 5) == 0)
+ {
+ address_mode = mode_16bit;
+ priv.orig_sizeflag = 0;
+ }
+ else if (strncmp (p, "intel", 5) == 0)
+ {
+ intel_syntax = 1;
+ }
+ else if (strncmp (p, "att", 3) == 0)
+ {
+ intel_syntax = 0;
+ }
+ else if (strncmp (p, "addr", 4) == 0)
+ {
+ if (address_mode == mode_64bit)
+ {
+ if (p[4] == '3' && p[5] == '2')
+ priv.orig_sizeflag &= ~AFLAG;
+ else if (p[4] == '6' && p[5] == '4')
+ priv.orig_sizeflag |= AFLAG;
+ }
+ else
+ {
+ if (p[4] == '1' && p[5] == '6')
+ priv.orig_sizeflag &= ~AFLAG;
+ else if (p[4] == '3' && p[5] == '2')
+ priv.orig_sizeflag |= AFLAG;
+ }
+ }
+ else if (strncmp (p, "data", 4) == 0)
+ {
+ if (p[4] == '1' && p[5] == '6')
+ priv.orig_sizeflag &= ~DFLAG;
+ else if (p[4] == '3' && p[5] == '2')
+ priv.orig_sizeflag |= DFLAG;
+ }
+ else if (strncmp (p, "suffix", 6) == 0)
+ priv.orig_sizeflag |= SUFFIX_ALWAYS;
+
+ p = strchr (p, ',');
+ if (p != NULL)
+ p++;
+ }
+
+ if (intel_syntax)
+ {
+ names64 = intel_names64;
+ names32 = intel_names32;
+ names16 = intel_names16;
+ names8 = intel_names8;
+ names8rex = intel_names8rex;
+ names_seg = intel_names_seg;
+ index16 = intel_index16;
+ open_char = '[';
+ close_char = ']';
+ separator_char = '+';
+ scale_char = '*';
+ }
+ else
+ {
+ names64 = att_names64;
+ names32 = att_names32;
+ names16 = att_names16;
+ names8 = att_names8;
+ names8rex = att_names8rex;
+ names_seg = att_names_seg;
+ index16 = att_index16;
+ open_char = '(';
+ close_char = ')';
+ separator_char = ',';
+ scale_char = ',';
+ }
+
+ /* The output looks better if we put 7 bytes on a line, since that
+ puts most long word instructions on a single line. */
+ info->bytes_per_line = 7;
+
+ info->private_data = &priv;
+ priv.max_fetched = priv.the_buffer;
+ priv.insn_start = pc;
+
+ obuf[0] = 0;
+ for (i = 0; i < MAX_OPERANDS; ++i)
+ {
+ op_out[i][0] = 0;
+ op_index[i] = -1;
+ }
+
+ the_info = info;
+ start_pc = pc;
+ start_codep = priv.the_buffer;
+ codep = priv.the_buffer;
+
+ if (setjmp (priv.bailout) != 0)
+ {
+ const char *name;
+
+ /* Getting here means we tried for data but didn't get it. That
+ means we have an incomplete instruction of some sort. Just
+ print the first byte as a prefix or a .byte pseudo-op. */
+ if (codep > priv.the_buffer)
+ {
+ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+ if (name != NULL)
+ (*info->fprintf_func) (info->stream, "%s", name);
+ else
+ {
+ /* Just print the first byte as a .byte instruction. */
+ (*info->fprintf_func) (info->stream, ".byte 0x%x",
+ (unsigned int) priv.the_buffer[0]);
+ }
+
+ return 1;
+ }
+
+ return -1;
+ }
+
+ obufp = obuf;
+ ckprefix ();
+
+ insn_codep = codep;
+ sizeflag = priv.orig_sizeflag;
+
+ fetch_data(info, codep + 1);
+ two_source_ops = (*codep == 0x62) || (*codep == 0xc8);
+
+ if (((prefixes & PREFIX_FWAIT)
+ && ((*codep < 0xd8) || (*codep > 0xdf)))
+ || (rex && rex_used))
+ {
+ const char *name;
+
+ /* fwait not followed by floating point instruction, or rex followed
+ by other prefixes. Print the first prefix. */
+ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+ if (name == NULL)
+ name = INTERNAL_DISASSEMBLER_ERROR;
+ (*info->fprintf_func) (info->stream, "%s", name);
+ return 1;
+ }
+
+ op = 0;
+ if (*codep == 0x0f)
+ {
+ unsigned char threebyte;
+ fetch_data(info, codep + 2);
+ threebyte = *++codep;
+ dp = &dis386_twobyte[threebyte];
+ need_modrm = twobyte_has_modrm[*codep];
+ uses_DATA_prefix = twobyte_uses_DATA_prefix[*codep];
+ uses_REPNZ_prefix = twobyte_uses_REPNZ_prefix[*codep];
+ uses_REPZ_prefix = twobyte_uses_REPZ_prefix[*codep];
+ uses_LOCK_prefix = (*codep & ~0x02) == 0x20;
+ codep++;
+ if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE)
+ {
+ fetch_data(info, codep + 2);
+ op = *codep++;
+ switch (threebyte)
+ {
+ case 0x38:
+ uses_DATA_prefix = threebyte_0x38_uses_DATA_prefix[op];
+ uses_REPNZ_prefix = threebyte_0x38_uses_REPNZ_prefix[op];
+ uses_REPZ_prefix = threebyte_0x38_uses_REPZ_prefix[op];
+ break;
+ case 0x3a:
+ uses_DATA_prefix = threebyte_0x3a_uses_DATA_prefix[op];
+ uses_REPNZ_prefix = threebyte_0x3a_uses_REPNZ_prefix[op];
+ uses_REPZ_prefix = threebyte_0x3a_uses_REPZ_prefix[op];
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ dp = &dis386[*codep];
+ need_modrm = onebyte_has_modrm[*codep];
+ uses_DATA_prefix = 0;
+ uses_REPNZ_prefix = 0;
+ /* pause is 0xf3 0x90. */
+ uses_REPZ_prefix = *codep == 0x90;
+ uses_LOCK_prefix = 0;
+ codep++;
+ }
+
+ if (!uses_REPZ_prefix && (prefixes & PREFIX_REPZ))
+ {
+ oappend ("repz ");
+ used_prefixes |= PREFIX_REPZ;
+ }
+ if (!uses_REPNZ_prefix && (prefixes & PREFIX_REPNZ))
+ {
+ oappend ("repnz ");
+ used_prefixes |= PREFIX_REPNZ;
+ }
+
+ if (!uses_LOCK_prefix && (prefixes & PREFIX_LOCK))
+ {
+ oappend ("lock ");
+ used_prefixes |= PREFIX_LOCK;
+ }
+
+ if (prefixes & PREFIX_ADDR)
+ {
+ sizeflag ^= AFLAG;
+ if (dp->op[2].bytemode != loop_jcxz_mode || intel_syntax)
+ {
+ if ((sizeflag & AFLAG) || address_mode == mode_64bit)
+ oappend ("addr32 ");
+ else
+ oappend ("addr16 ");
+ used_prefixes |= PREFIX_ADDR;
+ }
+ }
+
+ if (!uses_DATA_prefix && (prefixes & PREFIX_DATA))
+ {
+ sizeflag ^= DFLAG;
+ if (dp->op[2].bytemode == cond_jump_mode
+ && dp->op[0].bytemode == v_mode
+ && !intel_syntax)
+ {
+ if (sizeflag & DFLAG)
+ oappend ("data32 ");
+ else
+ oappend ("data16 ");
+ used_prefixes |= PREFIX_DATA;
+ }
+ }
+
+ if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE)
+ {
+ dp = &three_byte_table[dp->op[1].bytemode][op];
+ modrm.mod = (*codep >> 6) & 3;
+ modrm.reg = (*codep >> 3) & 7;
+ modrm.rm = *codep & 7;
+ }
+ else if (need_modrm)
+ {
+ fetch_data(info, codep + 1);
+ modrm.mod = (*codep >> 6) & 3;
+ modrm.reg = (*codep >> 3) & 7;
+ modrm.rm = *codep & 7;
+ }
+
+ if (dp->name == NULL && dp->op[0].bytemode == FLOATCODE)
+ {
+ dofloat (sizeflag);
+ }
+ else
+ {
+ int index;
+ if (dp->name == NULL)
+ {
+ switch (dp->op[0].bytemode)
+ {
+ case USE_GROUPS:
+ dp = &grps[dp->op[1].bytemode][modrm.reg];
+ break;
+
+ case USE_PREFIX_USER_TABLE:
+ index = 0;
+ used_prefixes |= (prefixes & PREFIX_REPZ);
+ if (prefixes & PREFIX_REPZ)
+ index = 1;
+ else
+ {
+ /* We should check PREFIX_REPNZ and PREFIX_REPZ
+ before PREFIX_DATA. */
+ used_prefixes |= (prefixes & PREFIX_REPNZ);
+ if (prefixes & PREFIX_REPNZ)
+ index = 3;
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ index = 2;
+ }
+ }
+ dp = &prefix_user_table[dp->op[1].bytemode][index];
+ break;
+
+ case X86_64_SPECIAL:
+ index = address_mode == mode_64bit ? 1 : 0;
+ dp = &x86_64_table[dp->op[1].bytemode][index];
+ break;
+
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+ }
+
+ if (putop (dp->name, sizeflag) == 0)
+ {
+ for (i = 0; i < MAX_OPERANDS; ++i)
+ {
+ obufp = op_out[i];
+ op_ad = MAX_OPERANDS - 1 - i;
+ if (dp->op[i].rtn)
+ (*dp->op[i].rtn) (dp->op[i].bytemode, sizeflag);
+ }
+ }
+ }
+
+ /* See if any prefixes were not used. If so, print the first one
+ separately. If we don't do this, we'll wind up printing an
+ instruction stream which does not precisely correspond to the
+ bytes we are disassembling. */
+ if ((prefixes & ~used_prefixes) != 0)
+ {
+ const char *name;
+
+ name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+ if (name == NULL)
+ name = INTERNAL_DISASSEMBLER_ERROR;
+ (*info->fprintf_func) (info->stream, "%s", name);
+ return 1;
+ }
+ if (rex & ~rex_used)
+ {
+ const char *name;
+ name = prefix_name (rex | 0x40, priv.orig_sizeflag);
+ if (name == NULL)
+ name = INTERNAL_DISASSEMBLER_ERROR;
+ (*info->fprintf_func) (info->stream, "%s ", name);
+ }
+
+ obufp = obuf + strlen (obuf);
+ for (i = strlen (obuf); i < 6; i++)
+ oappend (" ");
+ oappend (" ");
+ (*info->fprintf_func) (info->stream, "%s", obuf);
+
+ /* The enter and bound instructions are printed with operands in the same
+ order as the intel book; everything else is printed in reverse order. */
+ if (intel_syntax || two_source_ops)
+ {
+ bfd_vma riprel;
+
+ for (i = 0; i < MAX_OPERANDS; ++i)
+ op_txt[i] = op_out[i];
+
+ for (i = 0; i < (MAX_OPERANDS >> 1); ++i)
+ {
+ op_ad = op_index[i];
+ op_index[i] = op_index[MAX_OPERANDS - 1 - i];
+ op_index[MAX_OPERANDS - 1 - i] = op_ad;
+ riprel = op_riprel[i];
+ op_riprel[i] = op_riprel [MAX_OPERANDS - 1 - i];
+ op_riprel[MAX_OPERANDS - 1 - i] = riprel;
+ }
+ }
+ else
+ {
+ for (i = 0; i < MAX_OPERANDS; ++i)
+ op_txt[MAX_OPERANDS - 1 - i] = op_out[i];
+ }
+
+ needcomma = 0;
+ for (i = 0; i < MAX_OPERANDS; ++i)
+ if (*op_txt[i])
+ {
+ if (needcomma)
+ (*info->fprintf_func) (info->stream, ",");
+ if (op_index[i] != -1 && !op_riprel[i])
+ (*info->print_address_func) ((bfd_vma) op_address[op_index[i]], info);
+ else
+ (*info->fprintf_func) (info->stream, "%s", op_txt[i]);
+ needcomma = 1;
+ }
+
+ for (i = 0; i < MAX_OPERANDS; i++)
+ if (op_index[i] != -1 && op_riprel[i])
+ {
+ (*info->fprintf_func) (info->stream, " # ");
+ (*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep
+ + op_address[op_index[i]]), info);
+ break;
+ }
+ return codep - priv.the_buffer;
+}
+
+static const char *float_mem[] = {
+ /* d8 */
+ "fadd{s||s|}",
+ "fmul{s||s|}",
+ "fcom{s||s|}",
+ "fcomp{s||s|}",
+ "fsub{s||s|}",
+ "fsubr{s||s|}",
+ "fdiv{s||s|}",
+ "fdivr{s||s|}",
+ /* d9 */
+ "fld{s||s|}",
+ "(bad)",
+ "fst{s||s|}",
+ "fstp{s||s|}",
+ "fldenvIC",
+ "fldcw",
+ "fNstenvIC",
+ "fNstcw",
+ /* da */
+ "fiadd{l||l|}",
+ "fimul{l||l|}",
+ "ficom{l||l|}",
+ "ficomp{l||l|}",
+ "fisub{l||l|}",
+ "fisubr{l||l|}",
+ "fidiv{l||l|}",
+ "fidivr{l||l|}",
+ /* db */
+ "fild{l||l|}",
+ "fisttp{l||l|}",
+ "fist{l||l|}",
+ "fistp{l||l|}",
+ "(bad)",
+ "fld{t||t|}",
+ "(bad)",
+ "fstp{t||t|}",
+ /* dc */
+ "fadd{l||l|}",
+ "fmul{l||l|}",
+ "fcom{l||l|}",
+ "fcomp{l||l|}",
+ "fsub{l||l|}",
+ "fsubr{l||l|}",
+ "fdiv{l||l|}",
+ "fdivr{l||l|}",
+ /* dd */
+ "fld{l||l|}",
+ "fisttp{ll||ll|}",
+ "fst{l||l|}",
+ "fstp{l||l|}",
+ "frstorIC",
+ "(bad)",
+ "fNsaveIC",
+ "fNstsw",
+ /* de */
+ "fiadd",
+ "fimul",
+ "ficom",
+ "ficomp",
+ "fisub",
+ "fisubr",
+ "fidiv",
+ "fidivr",
+ /* df */
+ "fild",
+ "fisttp",
+ "fist",
+ "fistp",
+ "fbld",
+ "fild{ll||ll|}",
+ "fbstp",
+ "fistp{ll||ll|}",
+};
+
+static const unsigned char float_mem_mode[] = {
+ /* d8 */
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ /* d9 */
+ d_mode,
+ 0,
+ d_mode,
+ d_mode,
+ 0,
+ w_mode,
+ 0,
+ w_mode,
+ /* da */
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ /* db */
+ d_mode,
+ d_mode,
+ d_mode,
+ d_mode,
+ 0,
+ t_mode,
+ 0,
+ t_mode,
+ /* dc */
+ q_mode,
+ q_mode,
+ q_mode,
+ q_mode,
+ q_mode,
+ q_mode,
+ q_mode,
+ q_mode,
+ /* dd */
+ q_mode,
+ q_mode,
+ q_mode,
+ q_mode,
+ 0,
+ 0,
+ 0,
+ w_mode,
+ /* de */
+ w_mode,
+ w_mode,
+ w_mode,
+ w_mode,
+ w_mode,
+ w_mode,
+ w_mode,
+ w_mode,
+ /* df */
+ w_mode,
+ w_mode,
+ w_mode,
+ w_mode,
+ t_mode,
+ q_mode,
+ t_mode,
+ q_mode
+};
+
+#define ST { OP_ST, 0 }
+#define STi { OP_STi, 0 }
+
+#define FGRPd9_2 NULL, { { NULL, 0 } }
+#define FGRPd9_4 NULL, { { NULL, 1 } }
+#define FGRPd9_5 NULL, { { NULL, 2 } }
+#define FGRPd9_6 NULL, { { NULL, 3 } }
+#define FGRPd9_7 NULL, { { NULL, 4 } }
+#define FGRPda_5 NULL, { { NULL, 5 } }
+#define FGRPdb_4 NULL, { { NULL, 6 } }
+#define FGRPde_3 NULL, { { NULL, 7 } }
+#define FGRPdf_4 NULL, { { NULL, 8 } }
+
+static const struct dis386 float_reg[][8] = {
+ /* d8 */
+ {
+ { "fadd", { ST, STi } },
+ { "fmul", { ST, STi } },
+ { "fcom", { STi } },
+ { "fcomp", { STi } },
+ { "fsub", { ST, STi } },
+ { "fsubr", { ST, STi } },
+ { "fdiv", { ST, STi } },
+ { "fdivr", { ST, STi } },
+ },
+ /* d9 */
+ {
+ { "fld", { STi } },
+ { "fxch", { STi } },
+ { FGRPd9_2 },
+ { "(bad)", { XX } },
+ { FGRPd9_4 },
+ { FGRPd9_5 },
+ { FGRPd9_6 },
+ { FGRPd9_7 },
+ },
+ /* da */
+ {
+ { "fcmovb", { ST, STi } },
+ { "fcmove", { ST, STi } },
+ { "fcmovbe",{ ST, STi } },
+ { "fcmovu", { ST, STi } },
+ { "(bad)", { XX } },
+ { FGRPda_5 },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* db */
+ {
+ { "fcmovnb",{ ST, STi } },
+ { "fcmovne",{ ST, STi } },
+ { "fcmovnbe",{ ST, STi } },
+ { "fcmovnu",{ ST, STi } },
+ { FGRPdb_4 },
+ { "fucomi", { ST, STi } },
+ { "fcomi", { ST, STi } },
+ { "(bad)", { XX } },
+ },
+ /* dc */
+ {
+ { "fadd", { STi, ST } },
+ { "fmul", { STi, ST } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+#if SYSV386_COMPAT
+ { "fsub", { STi, ST } },
+ { "fsubr", { STi, ST } },
+ { "fdiv", { STi, ST } },
+ { "fdivr", { STi, ST } },
+#else
+ { "fsubr", { STi, ST } },
+ { "fsub", { STi, ST } },
+ { "fdivr", { STi, ST } },
+ { "fdiv", { STi, ST } },
+#endif
+ },
+ /* dd */
+ {
+ { "ffree", { STi } },
+ { "(bad)", { XX } },
+ { "fst", { STi } },
+ { "fstp", { STi } },
+ { "fucom", { STi } },
+ { "fucomp", { STi } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ },
+ /* de */
+ {
+ { "faddp", { STi, ST } },
+ { "fmulp", { STi, ST } },
+ { "(bad)", { XX } },
+ { FGRPde_3 },
+#if SYSV386_COMPAT
+ { "fsubp", { STi, ST } },
+ { "fsubrp", { STi, ST } },
+ { "fdivp", { STi, ST } },
+ { "fdivrp", { STi, ST } },
+#else
+ { "fsubrp", { STi, ST } },
+ { "fsubp", { STi, ST } },
+ { "fdivrp", { STi, ST } },
+ { "fdivp", { STi, ST } },
+#endif
+ },
+ /* df */
+ {
+ { "ffreep", { STi } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { "(bad)", { XX } },
+ { FGRPdf_4 },
+ { "fucomip", { ST, STi } },
+ { "fcomip", { ST, STi } },
+ { "(bad)", { XX } },
+ },
+};
+
+static const char *fgrps[][8] = {
+ /* d9_2 0 */
+ {
+ "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* d9_4 1 */
+ {
+ "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)",
+ },
+
+ /* d9_5 2 */
+ {
+ "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)",
+ },
+
+ /* d9_6 3 */
+ {
+ "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp",
+ },
+
+ /* d9_7 4 */
+ {
+ "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos",
+ },
+
+ /* da_5 5 */
+ {
+ "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* db_4 6 */
+ {
+ "feni(287 only)","fdisi(287 only)","fNclex","fNinit",
+ "fNsetpm(287 only)","(bad)","(bad)","(bad)",
+ },
+
+ /* de_3 7 */
+ {
+ "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+
+ /* df_4 8 */
+ {
+ "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+ },
+};
+
+static void
+dofloat (int sizeflag)
+{
+ const struct dis386 *dp;
+ unsigned char floatop;
+
+ floatop = codep[-1];
+
+ if (modrm.mod != 3)
+ {
+ int fp_indx = (floatop - 0xd8) * 8 + modrm.reg;
+
+ putop (float_mem[fp_indx], sizeflag);
+ obufp = op_out[0];
+ op_ad = 2;
+ OP_E (float_mem_mode[fp_indx], sizeflag);
+ return;
+ }
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+
+ dp = &float_reg[floatop - 0xd8][modrm.reg];
+ if (dp->name == NULL)
+ {
+ putop (fgrps[dp->op[0].bytemode][modrm.rm], sizeflag);
+
+ /* Instruction fnstsw is only one with strange arg. */
+ if (floatop == 0xdf && codep[-1] == 0xe0)
+ pstrcpy (op_out[0], sizeof(op_out[0]), names16[0]);
+ }
+ else
+ {
+ putop (dp->name, sizeflag);
+
+ obufp = op_out[0];
+ op_ad = 2;
+ if (dp->op[0].rtn)
+ (*dp->op[0].rtn) (dp->op[0].bytemode, sizeflag);
+
+ obufp = op_out[1];
+ op_ad = 1;
+ if (dp->op[1].rtn)
+ (*dp->op[1].rtn) (dp->op[1].bytemode, sizeflag);
+ }
+}
+
+static void
+OP_ST (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ oappend ("%st" + intel_syntax);
+}
+
+static void
+OP_STi (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%st(%d)", modrm.rm);
+ oappend (scratchbuf + intel_syntax);
+}
+
+/* Capital letters in template are macros. */
+static int
+putop (const char *template, int sizeflag)
+{
+ const char *p;
+ int alt = 0;
+
+ for (p = template; *p; p++)
+ {
+ switch (*p)
+ {
+ default:
+ *obufp++ = *p;
+ break;
+ case '{':
+ alt = 0;
+ if (intel_syntax)
+ alt += 1;
+ if (address_mode == mode_64bit)
+ alt += 2;
+ while (alt != 0)
+ {
+ while (*++p != '|')
+ {
+ if (*p == '}')
+ {
+ /* Alternative not valid. */
+ pstrcpy (obuf, sizeof(obuf), "(bad)");
+ obufp = obuf + 5;
+ return 1;
+ }
+ else if (*p == '\0')
+ abort ();
+ }
+ alt--;
+ }
+ /* Fall through. */
+ case 'I':
+ alt = 1;
+ continue;
+ case '|':
+ while (*++p != '}')
+ {
+ if (*p == '\0')
+ abort ();
+ }
+ break;
+ case '}':
+ break;
+ case 'A':
+ if (intel_syntax)
+ break;
+ if (modrm.mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+ *obufp++ = 'b';
+ break;
+ case 'B':
+ if (intel_syntax)
+ break;
+ if (sizeflag & SUFFIX_ALWAYS)
+ *obufp++ = 'b';
+ break;
+ case 'C':
+ if (intel_syntax && !alt)
+ break;
+ if ((prefixes & PREFIX_DATA) || (sizeflag & SUFFIX_ALWAYS))
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = intel_syntax ? 'd' : 'l';
+ else
+ *obufp++ = intel_syntax ? 'w' : 's';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ break;
+ case 'D':
+ if (intel_syntax || !(sizeflag & SUFFIX_ALWAYS))
+ break;
+ USED_REX (REX_W);
+ if (modrm.mod == 3)
+ {
+ if (rex & REX_W)
+ *obufp++ = 'q';
+ else if (sizeflag & DFLAG)
+ *obufp++ = intel_syntax ? 'd' : 'l';
+ else
+ *obufp++ = 'w';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ else
+ *obufp++ = 'w';
+ break;
+ case 'E': /* For jcxz/jecxz */
+ if (address_mode == mode_64bit)
+ {
+ if (sizeflag & AFLAG)
+ *obufp++ = 'r';
+ else
+ *obufp++ = 'e';
+ }
+ else
+ if (sizeflag & AFLAG)
+ *obufp++ = 'e';
+ used_prefixes |= (prefixes & PREFIX_ADDR);
+ break;
+ case 'F':
+ if (intel_syntax)
+ break;
+ if ((prefixes & PREFIX_ADDR) || (sizeflag & SUFFIX_ALWAYS))
+ {
+ if (sizeflag & AFLAG)
+ *obufp++ = address_mode == mode_64bit ? 'q' : 'l';
+ else
+ *obufp++ = address_mode == mode_64bit ? 'l' : 'w';
+ used_prefixes |= (prefixes & PREFIX_ADDR);
+ }
+ break;
+ case 'G':
+ if (intel_syntax || (obufp[-1] != 's' && !(sizeflag & SUFFIX_ALWAYS)))
+ break;
+ if ((rex & REX_W) || (sizeflag & DFLAG))
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ if (!(rex & REX_W))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 'H':
+ if (intel_syntax)
+ break;
+ if ((prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_CS
+ || (prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_DS)
+ {
+ used_prefixes |= prefixes & (PREFIX_CS | PREFIX_DS);
+ *obufp++ = ',';
+ *obufp++ = 'p';
+ if (prefixes & PREFIX_DS)
+ *obufp++ = 't';
+ else
+ *obufp++ = 'n';
+ }
+ break;
+ case 'J':
+ if (intel_syntax)
+ break;
+ *obufp++ = 'l';
+ break;
+ case 'K':
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ *obufp++ = 'q';
+ else
+ *obufp++ = 'd';
+ break;
+ case 'Z':
+ if (intel_syntax)
+ break;
+ if (address_mode == mode_64bit && (sizeflag & SUFFIX_ALWAYS))
+ {
+ *obufp++ = 'q';
+ break;
+ }
+ /* Fall through. */
+ case 'L':
+ if (intel_syntax)
+ break;
+ if (sizeflag & SUFFIX_ALWAYS)
+ *obufp++ = 'l';
+ break;
+ case 'N':
+ if ((prefixes & PREFIX_FWAIT) == 0)
+ *obufp++ = 'n';
+ else
+ used_prefixes |= PREFIX_FWAIT;
+ break;
+ case 'O':
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ *obufp++ = 'o';
+ else if (intel_syntax && (sizeflag & DFLAG))
+ *obufp++ = 'q';
+ else
+ *obufp++ = 'd';
+ if (!(rex & REX_W))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 'T':
+ if (intel_syntax)
+ break;
+ if (address_mode == mode_64bit && (sizeflag & DFLAG))
+ {
+ *obufp++ = 'q';
+ break;
+ }
+ /* Fall through. */
+ case 'P':
+ if (intel_syntax)
+ break;
+ if ((prefixes & PREFIX_DATA)
+ || (rex & REX_W)
+ || (sizeflag & SUFFIX_ALWAYS))
+ {
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ *obufp++ = 'q';
+ else
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ break;
+ case 'U':
+ if (intel_syntax)
+ break;
+ if (address_mode == mode_64bit && (sizeflag & DFLAG))
+ {
+ if (modrm.mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+ *obufp++ = 'q';
+ break;
+ }
+ /* Fall through. */
+ case 'Q':
+ if (intel_syntax && !alt)
+ break;
+ USED_REX (REX_W);
+ if (modrm.mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+ {
+ if (rex & REX_W)
+ *obufp++ = 'q';
+ else
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = intel_syntax ? 'd' : 'l';
+ else
+ *obufp++ = 'w';
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ break;
+ case 'R':
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ *obufp++ = 'q';
+ else if (sizeflag & DFLAG)
+ {
+ if (intel_syntax)
+ *obufp++ = 'd';
+ else
+ *obufp++ = 'l';
+ }
+ else
+ *obufp++ = 'w';
+ if (intel_syntax && !p[1]
+ && ((rex & REX_W) || (sizeflag & DFLAG)))
+ *obufp++ = 'e';
+ if (!(rex & REX_W))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 'V':
+ if (intel_syntax)
+ break;
+ if (address_mode == mode_64bit && (sizeflag & DFLAG))
+ {
+ if (sizeflag & SUFFIX_ALWAYS)
+ *obufp++ = 'q';
+ break;
+ }
+ /* Fall through. */
+ case 'S':
+ if (intel_syntax)
+ break;
+ if (sizeflag & SUFFIX_ALWAYS)
+ {
+ if (rex & REX_W)
+ *obufp++ = 'q';
+ else
+ {
+ if (sizeflag & DFLAG)
+ *obufp++ = 'l';
+ else
+ *obufp++ = 'w';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ }
+ break;
+ case 'X':
+ if (prefixes & PREFIX_DATA)
+ *obufp++ = 'd';
+ else
+ *obufp++ = 's';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 'Y':
+ if (intel_syntax)
+ break;
+ if (rex & REX_W)
+ {
+ USED_REX (REX_W);
+ *obufp++ = 'q';
+ }
+ break;
+ /* implicit operand size 'l' for i386 or 'q' for x86-64 */
+ case 'W':
+ /* operand size flag for cwtl, cbtw */
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ {
+ if (intel_syntax)
+ *obufp++ = 'd';
+ else
+ *obufp++ = 'l';
+ }
+ else if (sizeflag & DFLAG)
+ *obufp++ = 'w';
+ else
+ *obufp++ = 'b';
+ if (!(rex & REX_W))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ }
+ alt = 0;
+ }
+ *obufp = 0;
+ return 0;
+}
+
+static void
+oappend (const char *s)
+{
+ strcpy (obufp, s);
+ obufp += strlen (s);
+}
+
+static void
+append_seg (void)
+{
+ if (prefixes & PREFIX_CS)
+ {
+ used_prefixes |= PREFIX_CS;
+ oappend ("%cs:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_DS)
+ {
+ used_prefixes |= PREFIX_DS;
+ oappend ("%ds:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_SS)
+ {
+ used_prefixes |= PREFIX_SS;
+ oappend ("%ss:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_ES)
+ {
+ used_prefixes |= PREFIX_ES;
+ oappend ("%es:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_FS)
+ {
+ used_prefixes |= PREFIX_FS;
+ oappend ("%fs:" + intel_syntax);
+ }
+ if (prefixes & PREFIX_GS)
+ {
+ used_prefixes |= PREFIX_GS;
+ oappend ("%gs:" + intel_syntax);
+ }
+}
+
+static void
+OP_indirE (int bytemode, int sizeflag)
+{
+ if (!intel_syntax)
+ oappend ("*");
+ OP_E (bytemode, sizeflag);
+}
+
+static void
+print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp)
+{
+ if (address_mode == mode_64bit)
+ {
+ if (hex)
+ {
+ char tmp[30];
+ int i;
+ buf[0] = '0';
+ buf[1] = 'x';
+ snprintf_vma (tmp, sizeof(tmp), disp);
+ for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++);
+ pstrcpy (buf + 2, bufsize - 2, tmp + i);
+ }
+ else
+ {
+ bfd_signed_vma v = disp;
+ char tmp[30];
+ int i;
+ if (v < 0)
+ {
+ *(buf++) = '-';
+ v = -disp;
+ /* Check for possible overflow on 0x8000000000000000. */
+ if (v < 0)
+ {
+ pstrcpy (buf, bufsize, "9223372036854775808");
+ return;
+ }
+ }
+ if (!v)
+ {
+ pstrcpy (buf, bufsize, "0");
+ return;
+ }
+
+ i = 0;
+ tmp[29] = 0;
+ while (v)
+ {
+ tmp[28 - i] = (v % 10) + '0';
+ v /= 10;
+ i++;
+ }
+ pstrcpy (buf, bufsize, tmp + 29 - i);
+ }
+ }
+ else
+ {
+ if (hex)
+ snprintf (buf, bufsize, "0x%x", (unsigned int) disp);
+ else
+ snprintf (buf, bufsize, "%d", (int) disp);
+ }
+}
+
+/* Put DISP in BUF as signed hex number. */
+
+static void
+print_displacement (char *buf, bfd_vma disp)
+{
+ bfd_signed_vma val = disp;
+ char tmp[30];
+ int i, j = 0;
+
+ if (val < 0)
+ {
+ buf[j++] = '-';
+ val = -disp;
+
+ /* Check for possible overflow. */
+ if (val < 0)
+ {
+ switch (address_mode)
+ {
+ case mode_64bit:
+ strcpy (buf + j, "0x8000000000000000");
+ break;
+ case mode_32bit:
+ strcpy (buf + j, "0x80000000");
+ break;
+ case mode_16bit:
+ strcpy (buf + j, "0x8000");
+ break;
+ }
+ return;
+ }
+ }
+
+ buf[j++] = '0';
+ buf[j++] = 'x';
+
+ snprintf_vma (tmp, sizeof(tmp), val);
+ for (i = 0; tmp[i] == '0'; i++)
+ continue;
+ if (tmp[i] == '\0')
+ i--;
+ strcpy (buf + j, tmp + i);
+}
+
+static void
+intel_operand_size (int bytemode, int sizeflag)
+{
+ switch (bytemode)
+ {
+ case b_mode:
+ case dqb_mode:
+ oappend ("BYTE PTR ");
+ break;
+ case w_mode:
+ case dqw_mode:
+ oappend ("WORD PTR ");
+ break;
+ case stack_v_mode:
+ if (address_mode == mode_64bit && (sizeflag & DFLAG))
+ {
+ oappend ("QWORD PTR ");
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ }
+ /* FALLTHRU */
+ case v_mode:
+ case dq_mode:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ oappend ("QWORD PTR ");
+ else if ((sizeflag & DFLAG) || bytemode == dq_mode)
+ oappend ("DWORD PTR ");
+ else
+ oappend ("WORD PTR ");
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case z_mode:
+ if ((rex & REX_W) || (sizeflag & DFLAG))
+ *obufp++ = 'D';
+ oappend ("WORD PTR ");
+ if (!(rex & REX_W))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case d_mode:
+ case dqd_mode:
+ oappend ("DWORD PTR ");
+ break;
+ case q_mode:
+ oappend ("QWORD PTR ");
+ break;
+ case m_mode:
+ if (address_mode == mode_64bit)
+ oappend ("QWORD PTR ");
+ else
+ oappend ("DWORD PTR ");
+ break;
+ case f_mode:
+ if (sizeflag & DFLAG)
+ oappend ("FWORD PTR ");
+ else
+ oappend ("DWORD PTR ");
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case t_mode:
+ oappend ("TBYTE PTR ");
+ break;
+ case x_mode:
+ oappend ("XMMWORD PTR ");
+ break;
+ case o_mode:
+ oappend ("OWORD PTR ");
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+OP_E (int bytemode, int sizeflag)
+{
+ bfd_vma disp;
+ int add = 0;
+ int riprel = 0;
+ USED_REX (REX_B);
+ if (rex & REX_B)
+ add += 8;
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+
+ if (modrm.mod == 3)
+ {
+ switch (bytemode)
+ {
+ case b_mode:
+ USED_REX (0);
+ if (rex)
+ oappend (names8rex[modrm.rm + add]);
+ else
+ oappend (names8[modrm.rm + add]);
+ break;
+ case w_mode:
+ oappend (names16[modrm.rm + add]);
+ break;
+ case d_mode:
+ oappend (names32[modrm.rm + add]);
+ break;
+ case q_mode:
+ oappend (names64[modrm.rm + add]);
+ break;
+ case m_mode:
+ if (address_mode == mode_64bit)
+ oappend (names64[modrm.rm + add]);
+ else
+ oappend (names32[modrm.rm + add]);
+ break;
+ case stack_v_mode:
+ if (address_mode == mode_64bit && (sizeflag & DFLAG))
+ {
+ oappend (names64[modrm.rm + add]);
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ }
+ bytemode = v_mode;
+ /* FALLTHRU */
+ case v_mode:
+ case dq_mode:
+ case dqb_mode:
+ case dqd_mode:
+ case dqw_mode:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ oappend (names64[modrm.rm + add]);
+ else if ((sizeflag & DFLAG) || bytemode != v_mode)
+ oappend (names32[modrm.rm + add]);
+ else
+ oappend (names16[modrm.rm + add]);
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case 0:
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+ return;
+ }
+
+ disp = 0;
+ if (intel_syntax)
+ intel_operand_size (bytemode, sizeflag);
+ append_seg ();
+
+ if ((sizeflag & AFLAG) || address_mode == mode_64bit)
+ {
+ /* 32/64 bit address mode */
+ int havedisp;
+ int havesib;
+ int havebase;
+ int base;
+ int index = 0;
+ int scale = 0;
+
+ havesib = 0;
+ havebase = 1;
+ base = modrm.rm;
+
+ if (base == 4)
+ {
+ havesib = 1;
+ fetch_data(the_info, codep + 1);
+ index = (*codep >> 3) & 7;
+ if (address_mode == mode_64bit || index != 0x4)
+ /* When INDEX == 0x4 in 32 bit mode, SCALE is ignored. */
+ scale = (*codep >> 6) & 3;
+ base = *codep & 7;
+ USED_REX (REX_X);
+ if (rex & REX_X)
+ index += 8;
+ codep++;
+ }
+ base += add;
+
+ switch (modrm.mod)
+ {
+ case 0:
+ if ((base & 7) == 5)
+ {
+ havebase = 0;
+ if (address_mode == mode_64bit && !havesib)
+ riprel = 1;
+ disp = get32s ();
+ }
+ break;
+ case 1:
+ fetch_data (the_info, codep + 1);
+ disp = *codep++;
+ if ((disp & 0x80) != 0)
+ disp -= 0x100;
+ break;
+ case 2:
+ disp = get32s ();
+ break;
+ }
+
+ havedisp = havebase || (havesib && (index != 4 || scale != 0));
+
+ if (!intel_syntax)
+ if (modrm.mod != 0 || (base & 7) == 5)
+ {
+ if (havedisp || riprel)
+ print_displacement (scratchbuf, disp);
+ else
+ print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp);
+ oappend (scratchbuf);
+ if (riprel)
+ {
+ set_op (disp, 1);
+ oappend ("(%rip)");
+ }
+ }
+
+ if (havedisp || (intel_syntax && riprel))
+ {
+ *obufp++ = open_char;
+ if (intel_syntax && riprel)
+ {
+ set_op (disp, 1);
+ oappend ("rip");
+ }
+ *obufp = '\0';
+ if (havebase)
+ oappend (address_mode == mode_64bit && (sizeflag & AFLAG)
+ ? names64[base] : names32[base]);
+ if (havesib)
+ {
+ if (index != 4)
+ {
+ if (!intel_syntax || havebase)
+ {
+ *obufp++ = separator_char;
+ *obufp = '\0';
+ }
+ oappend (address_mode == mode_64bit && (sizeflag & AFLAG)
+ ? names64[index] : names32[index]);
+ }
+ if (scale != 0 || (!intel_syntax && index != 4))
+ {
+ *obufp++ = scale_char;
+ *obufp = '\0';
+ snprintf (scratchbuf, sizeof(scratchbuf), "%d", 1 << scale);
+ oappend (scratchbuf);
+ }
+ }
+ if (intel_syntax
+ && (disp || modrm.mod != 0 || (base & 7) == 5))
+ {
+ if ((bfd_signed_vma) disp >= 0)
+ {
+ *obufp++ = '+';
+ *obufp = '\0';
+ }
+ else if (modrm.mod != 1)
+ {
+ *obufp++ = '-';
+ *obufp = '\0';
+ disp = - (bfd_signed_vma) disp;
+ }
+
+ print_displacement (scratchbuf, disp);
+ oappend (scratchbuf);
+ }
+
+ *obufp++ = close_char;
+ *obufp = '\0';
+ }
+ else if (intel_syntax)
+ {
+ if (modrm.mod != 0 || (base & 7) == 5)
+ {
+ if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS))
+ ;
+ else
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp);
+ oappend (scratchbuf);
+ }
+ }
+ }
+ else
+ { /* 16 bit address mode */
+ switch (modrm.mod)
+ {
+ case 0:
+ if (modrm.rm == 6)
+ {
+ disp = get16 ();
+ if ((disp & 0x8000) != 0)
+ disp -= 0x10000;
+ }
+ break;
+ case 1:
+ fetch_data(the_info, codep + 1);
+ disp = *codep++;
+ if ((disp & 0x80) != 0)
+ disp -= 0x100;
+ break;
+ case 2:
+ disp = get16 ();
+ if ((disp & 0x8000) != 0)
+ disp -= 0x10000;
+ break;
+ }
+
+ if (!intel_syntax)
+ if (modrm.mod != 0 || modrm.rm == 6)
+ {
+ print_displacement (scratchbuf, disp);
+ oappend (scratchbuf);
+ }
+
+ if (modrm.mod != 0 || modrm.rm != 6)
+ {
+ *obufp++ = open_char;
+ *obufp = '\0';
+ oappend (index16[modrm.rm]);
+ if (intel_syntax
+ && (disp || modrm.mod != 0 || modrm.rm == 6))
+ {
+ if ((bfd_signed_vma) disp >= 0)
+ {
+ *obufp++ = '+';
+ *obufp = '\0';
+ }
+ else if (modrm.mod != 1)
+ {
+ *obufp++ = '-';
+ *obufp = '\0';
+ disp = - (bfd_signed_vma) disp;
+ }
+
+ print_displacement (scratchbuf, disp);
+ oappend (scratchbuf);
+ }
+
+ *obufp++ = close_char;
+ *obufp = '\0';
+ }
+ else if (intel_syntax)
+ {
+ if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS))
+ ;
+ else
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ print_operand_value (scratchbuf, sizeof(scratchbuf), 1,
+ disp & 0xffff);
+ oappend (scratchbuf);
+ }
+ }
+}
+
+static void
+OP_G (int bytemode, int sizeflag)
+{
+ int add = 0;
+ USED_REX (REX_R);
+ if (rex & REX_R)
+ add += 8;
+ switch (bytemode)
+ {
+ case b_mode:
+ USED_REX (0);
+ if (rex)
+ oappend (names8rex[modrm.reg + add]);
+ else
+ oappend (names8[modrm.reg + add]);
+ break;
+ case w_mode:
+ oappend (names16[modrm.reg + add]);
+ break;
+ case d_mode:
+ oappend (names32[modrm.reg + add]);
+ break;
+ case q_mode:
+ oappend (names64[modrm.reg + add]);
+ break;
+ case v_mode:
+ case dq_mode:
+ case dqb_mode:
+ case dqd_mode:
+ case dqw_mode:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ oappend (names64[modrm.reg + add]);
+ else if ((sizeflag & DFLAG) || bytemode != v_mode)
+ oappend (names32[modrm.reg + add]);
+ else
+ oappend (names16[modrm.reg + add]);
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case m_mode:
+ if (address_mode == mode_64bit)
+ oappend (names64[modrm.reg + add]);
+ else
+ oappend (names32[modrm.reg + add]);
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+}
+
+static bfd_vma
+get64 (void)
+{
+ bfd_vma x;
+#ifdef BFD64
+ unsigned int a;
+ unsigned int b;
+
+ fetch_data(the_info, codep + 8);
+ a = *codep++ & 0xff;
+ a |= (*codep++ & 0xff) << 8;
+ a |= (*codep++ & 0xff) << 16;
+ a |= (*codep++ & 0xff) << 24;
+ b = *codep++ & 0xff;
+ b |= (*codep++ & 0xff) << 8;
+ b |= (*codep++ & 0xff) << 16;
+ b |= (*codep++ & 0xff) << 24;
+ x = a + ((bfd_vma) b << 32);
+#else
+ abort ();
+ x = 0;
+#endif
+ return x;
+}
+
+static bfd_signed_vma
+get32 (void)
+{
+ bfd_signed_vma x = 0;
+
+ fetch_data(the_info, codep + 4);
+ x = *codep++ & (bfd_signed_vma) 0xff;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 8;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 16;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 24;
+ return x;
+}
+
+static bfd_signed_vma
+get32s (void)
+{
+ bfd_signed_vma x = 0;
+
+ fetch_data(the_info, codep + 4);
+ x = *codep++ & (bfd_signed_vma) 0xff;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 8;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 16;
+ x |= (*codep++ & (bfd_signed_vma) 0xff) << 24;
+
+ x = (x ^ ((bfd_signed_vma) 1 << 31)) - ((bfd_signed_vma) 1 << 31);
+
+ return x;
+}
+
+static int
+get16 (void)
+{
+ int x = 0;
+
+ fetch_data(the_info, codep + 2);
+ x = *codep++ & 0xff;
+ x |= (*codep++ & 0xff) << 8;
+ return x;
+}
+
+static void
+set_op (bfd_vma op, int riprel)
+{
+ op_index[op_ad] = op_ad;
+ if (address_mode == mode_64bit)
+ {
+ op_address[op_ad] = op;
+ op_riprel[op_ad] = riprel;
+ }
+ else
+ {
+ /* Mask to get a 32-bit address. */
+ op_address[op_ad] = op & 0xffffffff;
+ op_riprel[op_ad] = riprel & 0xffffffff;
+ }
+}
+
+static void
+OP_REG (int code, int sizeflag)
+{
+ const char *s;
+ int add = 0;
+ USED_REX (REX_B);
+ if (rex & REX_B)
+ add = 8;
+
+ switch (code)
+ {
+ case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+ case sp_reg: case bp_reg: case si_reg: case di_reg:
+ s = names16[code - ax_reg + add];
+ break;
+ case es_reg: case ss_reg: case cs_reg:
+ case ds_reg: case fs_reg: case gs_reg:
+ s = names_seg[code - es_reg + add];
+ break;
+ case al_reg: case ah_reg: case cl_reg: case ch_reg:
+ case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+ USED_REX (0);
+ if (rex)
+ s = names8rex[code - al_reg + add];
+ else
+ s = names8[code - al_reg];
+ break;
+ case rAX_reg: case rCX_reg: case rDX_reg: case rBX_reg:
+ case rSP_reg: case rBP_reg: case rSI_reg: case rDI_reg:
+ if (address_mode == mode_64bit && (sizeflag & DFLAG))
+ {
+ s = names64[code - rAX_reg + add];
+ break;
+ }
+ code += eAX_reg - rAX_reg;
+ /* Fall through. */
+ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ s = names64[code - eAX_reg + add];
+ else if (sizeflag & DFLAG)
+ s = names32[code - eAX_reg + add];
+ else
+ s = names16[code - eAX_reg + add];
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ s = INTERNAL_DISASSEMBLER_ERROR;
+ break;
+ }
+ oappend (s);
+}
+
+static void
+OP_IMREG (int code, int sizeflag)
+{
+ const char *s;
+
+ switch (code)
+ {
+ case indir_dx_reg:
+ if (intel_syntax)
+ s = "dx";
+ else
+ s = "(%dx)";
+ break;
+ case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+ case sp_reg: case bp_reg: case si_reg: case di_reg:
+ s = names16[code - ax_reg];
+ break;
+ case es_reg: case ss_reg: case cs_reg:
+ case ds_reg: case fs_reg: case gs_reg:
+ s = names_seg[code - es_reg];
+ break;
+ case al_reg: case ah_reg: case cl_reg: case ch_reg:
+ case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+ USED_REX (0);
+ if (rex)
+ s = names8rex[code - al_reg];
+ else
+ s = names8[code - al_reg];
+ break;
+ case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+ case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ s = names64[code - eAX_reg];
+ else if (sizeflag & DFLAG)
+ s = names32[code - eAX_reg];
+ else
+ s = names16[code - eAX_reg];
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case z_mode_ax_reg:
+ if ((rex & REX_W) || (sizeflag & DFLAG))
+ s = *names32;
+ else
+ s = *names16;
+ if (!(rex & REX_W))
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ s = INTERNAL_DISASSEMBLER_ERROR;
+ break;
+ }
+ oappend (s);
+}
+
+static void
+OP_I (int bytemode, int sizeflag)
+{
+ bfd_signed_vma op;
+ bfd_signed_vma mask = -1;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ fetch_data(the_info, codep + 1);
+ op = *codep++;
+ mask = 0xff;
+ break;
+ case q_mode:
+ if (address_mode == mode_64bit)
+ {
+ op = get32s ();
+ break;
+ }
+ /* Fall through. */
+ case v_mode:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ op = get32s ();
+ else if (sizeflag & DFLAG)
+ {
+ op = get32 ();
+ mask = 0xffffffff;
+ }
+ else
+ {
+ op = get16 ();
+ mask = 0xfffff;
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case w_mode:
+ mask = 0xfffff;
+ op = get16 ();
+ break;
+ case const_1_mode:
+ if (intel_syntax)
+ oappend ("1");
+ return;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+
+ op &= mask;
+ scratchbuf[0] = '$';
+ print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op);
+ oappend (scratchbuf + intel_syntax);
+ scratchbuf[0] = '\0';
+}
+
+static void
+OP_I64 (int bytemode, int sizeflag)
+{
+ bfd_signed_vma op;
+ bfd_signed_vma mask = -1;
+
+ if (address_mode != mode_64bit)
+ {
+ OP_I (bytemode, sizeflag);
+ return;
+ }
+
+ switch (bytemode)
+ {
+ case b_mode:
+ fetch_data(the_info, codep + 1);
+ op = *codep++;
+ mask = 0xff;
+ break;
+ case v_mode:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ op = get64 ();
+ else if (sizeflag & DFLAG)
+ {
+ op = get32 ();
+ mask = 0xffffffff;
+ }
+ else
+ {
+ op = get16 ();
+ mask = 0xfffff;
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case w_mode:
+ mask = 0xfffff;
+ op = get16 ();
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+
+ op &= mask;
+ scratchbuf[0] = '$';
+ print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op);
+ oappend (scratchbuf + intel_syntax);
+ scratchbuf[0] = '\0';
+}
+
+static void
+OP_sI (int bytemode, int sizeflag)
+{
+ bfd_signed_vma op;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ fetch_data(the_info, codep + 1);
+ op = *codep++;
+ if ((op & 0x80) != 0)
+ op -= 0x100;
+ break;
+ case v_mode:
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ op = get32s ();
+ else if (sizeflag & DFLAG)
+ {
+ op = get32s ();
+ }
+ else
+ {
+ op = get16 ();
+ if ((op & 0x8000) != 0)
+ op -= 0x10000;
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ case w_mode:
+ op = get16 ();
+ if ((op & 0x8000) != 0)
+ op -= 0x10000;
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+
+ scratchbuf[0] = '$';
+ print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_J (int bytemode, int sizeflag)
+{
+ bfd_vma disp;
+ bfd_vma mask = -1;
+ bfd_vma segment = 0;
+
+ switch (bytemode)
+ {
+ case b_mode:
+ fetch_data(the_info, codep + 1);
+ disp = *codep++;
+ if ((disp & 0x80) != 0)
+ disp -= 0x100;
+ break;
+ case v_mode:
+ if ((sizeflag & DFLAG) || (rex & REX_W))
+ disp = get32s ();
+ else
+ {
+ disp = get16 ();
+ if ((disp & 0x8000) != 0)
+ disp -= 0x10000;
+ /* In 16bit mode, address is wrapped around at 64k within
+ the same segment. Otherwise, a data16 prefix on a jump
+ instruction means that the pc is masked to 16 bits after
+ the displacement is added! */
+ mask = 0xffff;
+ if ((prefixes & PREFIX_DATA) == 0)
+ segment = ((start_pc + codep - start_codep)
+ & ~((bfd_vma) 0xffff));
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ return;
+ }
+ disp = ((start_pc + codep - start_codep + disp) & mask) | segment;
+ set_op (disp, 0);
+ print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp);
+ oappend (scratchbuf);
+}
+
+static void
+OP_SEG (int bytemode, int sizeflag)
+{
+ if (bytemode == w_mode)
+ oappend (names_seg[modrm.reg]);
+ else
+ OP_E (modrm.mod == 3 ? bytemode : w_mode, sizeflag);
+}
+
+static void
+OP_DIR (int dummy ATTRIBUTE_UNUSED, int sizeflag)
+{
+ int seg, offset;
+
+ if (sizeflag & DFLAG)
+ {
+ offset = get32 ();
+ seg = get16 ();
+ }
+ else
+ {
+ offset = get16 ();
+ seg = get16 ();
+ }
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (intel_syntax)
+ snprintf (scratchbuf, sizeof(scratchbuf), "0x%x:0x%x", seg, offset);
+ else
+ snprintf (scratchbuf, sizeof(scratchbuf), "$0x%x,$0x%x", seg, offset);
+ oappend (scratchbuf);
+}
+
+static void
+OP_OFF (int bytemode, int sizeflag)
+{
+ bfd_vma off;
+
+ if (intel_syntax && (sizeflag & SUFFIX_ALWAYS))
+ intel_operand_size (bytemode, sizeflag);
+ append_seg ();
+
+ if ((sizeflag & AFLAG) || address_mode == mode_64bit)
+ off = get32 ();
+ else
+ off = get16 ();
+
+ if (intel_syntax)
+ {
+ if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS)))
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ }
+ print_operand_value (scratchbuf, sizeof(scratchbuf), 1, off);
+ oappend (scratchbuf);
+}
+
+static void
+OP_OFF64 (int bytemode, int sizeflag)
+{
+ bfd_vma off;
+
+ if (address_mode != mode_64bit
+ || (prefixes & PREFIX_ADDR))
+ {
+ OP_OFF (bytemode, sizeflag);
+ return;
+ }
+
+ if (intel_syntax && (sizeflag & SUFFIX_ALWAYS))
+ intel_operand_size (bytemode, sizeflag);
+ append_seg ();
+
+ off = get64 ();
+
+ if (intel_syntax)
+ {
+ if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+ | PREFIX_ES | PREFIX_FS | PREFIX_GS)))
+ {
+ oappend (names_seg[ds_reg - es_reg]);
+ oappend (":");
+ }
+ }
+ print_operand_value (scratchbuf, sizeof(scratchbuf), 1, off);
+ oappend (scratchbuf);
+}
+
+static void
+ptr_reg (int code, int sizeflag)
+{
+ const char *s;
+
+ *obufp++ = open_char;
+ used_prefixes |= (prefixes & PREFIX_ADDR);
+ if (address_mode == mode_64bit)
+ {
+ if (!(sizeflag & AFLAG))
+ s = names32[code - eAX_reg];
+ else
+ s = names64[code - eAX_reg];
+ }
+ else if (sizeflag & AFLAG)
+ s = names32[code - eAX_reg];
+ else
+ s = names16[code - eAX_reg];
+ oappend (s);
+ *obufp++ = close_char;
+ *obufp = 0;
+}
+
+static void
+OP_ESreg (int code, int sizeflag)
+{
+ if (intel_syntax)
+ {
+ switch (codep[-1])
+ {
+ case 0x6d: /* insw/insl */
+ intel_operand_size (z_mode, sizeflag);
+ break;
+ case 0xa5: /* movsw/movsl/movsq */
+ case 0xa7: /* cmpsw/cmpsl/cmpsq */
+ case 0xab: /* stosw/stosl */
+ case 0xaf: /* scasw/scasl */
+ intel_operand_size (v_mode, sizeflag);
+ break;
+ default:
+ intel_operand_size (b_mode, sizeflag);
+ }
+ }
+ oappend ("%es:" + intel_syntax);
+ ptr_reg (code, sizeflag);
+}
+
+static void
+OP_DSreg (int code, int sizeflag)
+{
+ if (intel_syntax)
+ {
+ switch (codep[-1])
+ {
+ case 0x6f: /* outsw/outsl */
+ intel_operand_size (z_mode, sizeflag);
+ break;
+ case 0xa5: /* movsw/movsl/movsq */
+ case 0xa7: /* cmpsw/cmpsl/cmpsq */
+ case 0xad: /* lodsw/lodsl/lodsq */
+ intel_operand_size (v_mode, sizeflag);
+ break;
+ default:
+ intel_operand_size (b_mode, sizeflag);
+ }
+ }
+ if ((prefixes
+ & (PREFIX_CS
+ | PREFIX_DS
+ | PREFIX_SS
+ | PREFIX_ES
+ | PREFIX_FS
+ | PREFIX_GS)) == 0)
+ prefixes |= PREFIX_DS;
+ append_seg ();
+ ptr_reg (code, sizeflag);
+}
+
+static void
+OP_C (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ int add = 0;
+ if (rex & REX_R)
+ {
+ USED_REX (REX_R);
+ add = 8;
+ }
+ else if (address_mode != mode_64bit && (prefixes & PREFIX_LOCK))
+ {
+ used_prefixes |= PREFIX_LOCK;
+ add = 8;
+ }
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%cr%d", modrm.reg + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_D (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ int add = 0;
+ USED_REX (REX_R);
+ if (rex & REX_R)
+ add = 8;
+ if (intel_syntax)
+ snprintf (scratchbuf, sizeof(scratchbuf), "db%d", modrm.reg + add);
+ else
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%db%d", modrm.reg + add);
+ oappend (scratchbuf);
+}
+
+static void
+OP_T (int dummy ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%tr%d", modrm.reg);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_R (int bytemode, int sizeflag)
+{
+ if (modrm.mod == 3)
+ OP_E (bytemode, sizeflag);
+ else
+ BadOp ();
+}
+
+static void
+OP_MMX (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ {
+ int add = 0;
+ USED_REX (REX_R);
+ if (rex & REX_R)
+ add = 8;
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.reg + add);
+ }
+ else
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.reg);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_XMM (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ int add = 0;
+ USED_REX (REX_R);
+ if (rex & REX_R)
+ add = 8;
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.reg + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_EM (int bytemode, int sizeflag)
+{
+ if (modrm.mod != 3)
+ {
+ if (intel_syntax && bytemode == v_mode)
+ {
+ bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode;
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ OP_E (bytemode, sizeflag);
+ return;
+ }
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ {
+ int add = 0;
+
+ USED_REX (REX_B);
+ if (rex & REX_B)
+ add = 8;
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.rm + add);
+ }
+ else
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.rm);
+ oappend (scratchbuf + intel_syntax);
+}
+
+/* cvt* are the only instructions in sse2 which have
+ both SSE and MMX operands and also have 0x66 prefix
+ in their opcode. 0x66 was originally used to differentiate
+ between SSE and MMX instruction(operands). So we have to handle the
+ cvt* separately using OP_EMC and OP_MXC */
+static void
+OP_EMC (int bytemode, int sizeflag)
+{
+ if (modrm.mod != 3)
+ {
+ if (intel_syntax && bytemode == v_mode)
+ {
+ bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode;
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ }
+ OP_E (bytemode, sizeflag);
+ return;
+ }
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.rm);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_MXC (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", modrm.reg);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_EX (int bytemode, int sizeflag)
+{
+ int add = 0;
+ if (modrm.mod != 3)
+ {
+ OP_E (bytemode, sizeflag);
+ return;
+ }
+ USED_REX (REX_B);
+ if (rex & REX_B)
+ add = 8;
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", modrm.rm + add);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_MS (int bytemode, int sizeflag)
+{
+ if (modrm.mod == 3)
+ OP_EM (bytemode, sizeflag);
+ else
+ BadOp ();
+}
+
+static void
+OP_XS (int bytemode, int sizeflag)
+{
+ if (modrm.mod == 3)
+ OP_EX (bytemode, sizeflag);
+ else
+ BadOp ();
+}
+
+static void
+OP_M (int bytemode, int sizeflag)
+{
+ if (modrm.mod == 3)
+ /* bad bound,lea,lds,les,lfs,lgs,lss,cmpxchg8b,vmptrst modrm */
+ BadOp ();
+ else
+ OP_E (bytemode, sizeflag);
+}
+
+static void
+OP_0f07 (int bytemode, int sizeflag)
+{
+ if (modrm.mod != 3 || modrm.rm != 0)
+ BadOp ();
+ else
+ OP_E (bytemode, sizeflag);
+}
+
+static void
+OP_0fae (int bytemode, int sizeflag)
+{
+ if (modrm.mod == 3)
+ {
+ if (modrm.reg == 7)
+ strcpy (obuf + strlen (obuf) - sizeof ("clflush") + 1, "sfence");
+
+ if (modrm.reg < 5 || modrm.rm != 0)
+ {
+ BadOp (); /* bad sfence, mfence, or lfence */
+ return;
+ }
+ }
+ else if (modrm.reg != 7)
+ {
+ BadOp (); /* bad clflush */
+ return;
+ }
+
+ OP_E (bytemode, sizeflag);
+}
+
+/* NOP is an alias of "xchg %ax,%ax" in 16bit mode, "xchg %eax,%eax" in
+ 32bit mode and "xchg %rax,%rax" in 64bit mode. */
+
+static void
+NOP_Fixup1 (int bytemode, int sizeflag)
+{
+ if ((prefixes & PREFIX_DATA) != 0
+ || (rex != 0
+ && rex != 0x48
+ && address_mode == mode_64bit))
+ OP_REG (bytemode, sizeflag);
+ else
+ strcpy (obuf, "nop");
+}
+
+static void
+NOP_Fixup2 (int bytemode, int sizeflag)
+{
+ if ((prefixes & PREFIX_DATA) != 0
+ || (rex != 0
+ && rex != 0x48
+ && address_mode == mode_64bit))
+ OP_IMREG (bytemode, sizeflag);
+}
+
+static const char *Suffix3DNow[] = {
+/* 00 */ NULL, NULL, NULL, NULL,
+/* 04 */ NULL, NULL, NULL, NULL,
+/* 08 */ NULL, NULL, NULL, NULL,
+/* 0C */ "pi2fw", "pi2fd", NULL, NULL,
+/* 10 */ NULL, NULL, NULL, NULL,
+/* 14 */ NULL, NULL, NULL, NULL,
+/* 18 */ NULL, NULL, NULL, NULL,
+/* 1C */ "pf2iw", "pf2id", NULL, NULL,
+/* 20 */ NULL, NULL, NULL, NULL,
+/* 24 */ NULL, NULL, NULL, NULL,
+/* 28 */ NULL, NULL, NULL, NULL,
+/* 2C */ NULL, NULL, NULL, NULL,
+/* 30 */ NULL, NULL, NULL, NULL,
+/* 34 */ NULL, NULL, NULL, NULL,
+/* 38 */ NULL, NULL, NULL, NULL,
+/* 3C */ NULL, NULL, NULL, NULL,
+/* 40 */ NULL, NULL, NULL, NULL,
+/* 44 */ NULL, NULL, NULL, NULL,
+/* 48 */ NULL, NULL, NULL, NULL,
+/* 4C */ NULL, NULL, NULL, NULL,
+/* 50 */ NULL, NULL, NULL, NULL,
+/* 54 */ NULL, NULL, NULL, NULL,
+/* 58 */ NULL, NULL, NULL, NULL,
+/* 5C */ NULL, NULL, NULL, NULL,
+/* 60 */ NULL, NULL, NULL, NULL,
+/* 64 */ NULL, NULL, NULL, NULL,
+/* 68 */ NULL, NULL, NULL, NULL,
+/* 6C */ NULL, NULL, NULL, NULL,
+/* 70 */ NULL, NULL, NULL, NULL,
+/* 74 */ NULL, NULL, NULL, NULL,
+/* 78 */ NULL, NULL, NULL, NULL,
+/* 7C */ NULL, NULL, NULL, NULL,
+/* 80 */ NULL, NULL, NULL, NULL,
+/* 84 */ NULL, NULL, NULL, NULL,
+/* 88 */ NULL, NULL, "pfnacc", NULL,
+/* 8C */ NULL, NULL, "pfpnacc", NULL,
+/* 90 */ "pfcmpge", NULL, NULL, NULL,
+/* 94 */ "pfmin", NULL, "pfrcp", "pfrsqrt",
+/* 98 */ NULL, NULL, "pfsub", NULL,
+/* 9C */ NULL, NULL, "pfadd", NULL,
+/* A0 */ "pfcmpgt", NULL, NULL, NULL,
+/* A4 */ "pfmax", NULL, "pfrcpit1", "pfrsqit1",
+/* A8 */ NULL, NULL, "pfsubr", NULL,
+/* AC */ NULL, NULL, "pfacc", NULL,
+/* B0 */ "pfcmpeq", NULL, NULL, NULL,
+/* B4 */ "pfmul", NULL, "pfrcpit2", "pmulhrw",
+/* B8 */ NULL, NULL, NULL, "pswapd",
+/* BC */ NULL, NULL, NULL, "pavgusb",
+/* C0 */ NULL, NULL, NULL, NULL,
+/* C4 */ NULL, NULL, NULL, NULL,
+/* C8 */ NULL, NULL, NULL, NULL,
+/* CC */ NULL, NULL, NULL, NULL,
+/* D0 */ NULL, NULL, NULL, NULL,
+/* D4 */ NULL, NULL, NULL, NULL,
+/* D8 */ NULL, NULL, NULL, NULL,
+/* DC */ NULL, NULL, NULL, NULL,
+/* E0 */ NULL, NULL, NULL, NULL,
+/* E4 */ NULL, NULL, NULL, NULL,
+/* E8 */ NULL, NULL, NULL, NULL,
+/* EC */ NULL, NULL, NULL, NULL,
+/* F0 */ NULL, NULL, NULL, NULL,
+/* F4 */ NULL, NULL, NULL, NULL,
+/* F8 */ NULL, NULL, NULL, NULL,
+/* FC */ NULL, NULL, NULL, NULL,
+};
+
+static void
+OP_3DNowSuffix (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ const char *mnemonic;
+
+ fetch_data(the_info, codep + 1);
+ /* AMD 3DNow! instructions are specified by an opcode suffix in the
+ place where an 8-bit immediate would normally go. ie. the last
+ byte of the instruction. */
+ obufp = obuf + strlen (obuf);
+ mnemonic = Suffix3DNow[*codep++ & 0xff];
+ if (mnemonic)
+ oappend (mnemonic);
+ else
+ {
+ /* Since a variable sized modrm/sib chunk is between the start
+ of the opcode (0x0f0f) and the opcode suffix, we need to do
+ all the modrm processing first, and don't know until now that
+ we have a bad opcode. This necessitates some cleaning up. */
+ op_out[0][0] = '\0';
+ op_out[1][0] = '\0';
+ BadOp ();
+ }
+}
+
+static const char *simd_cmp_op[] = {
+ "eq",
+ "lt",
+ "le",
+ "unord",
+ "neq",
+ "nlt",
+ "nle",
+ "ord"
+};
+
+static void
+OP_SIMD_Suffix (int bytemode ATTRIBUTE_UNUSED, int sizeflag ATTRIBUTE_UNUSED)
+{
+ unsigned int cmp_type;
+
+ fetch_data(the_info, codep + 1);
+ obufp = obuf + strlen (obuf);
+ cmp_type = *codep++ & 0xff;
+ if (cmp_type < 8)
+ {
+ char suffix1 = 'p', suffix2 = 's';
+ used_prefixes |= (prefixes & PREFIX_REPZ);
+ if (prefixes & PREFIX_REPZ)
+ suffix1 = 's';
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ if (prefixes & PREFIX_DATA)
+ suffix2 = 'd';
+ else
+ {
+ used_prefixes |= (prefixes & PREFIX_REPNZ);
+ if (prefixes & PREFIX_REPNZ)
+ suffix1 = 's', suffix2 = 'd';
+ }
+ }
+ snprintf (scratchbuf, sizeof(scratchbuf), "cmp%s%c%c",
+ simd_cmp_op[cmp_type], suffix1, suffix2);
+ used_prefixes |= (prefixes & PREFIX_REPZ);
+ oappend (scratchbuf);
+ }
+ else
+ {
+ /* We have a bad extension byte. Clean up. */
+ op_out[0][0] = '\0';
+ op_out[1][0] = '\0';
+ BadOp ();
+ }
+}
+
+static void
+SIMD_Fixup (int extrachar, int sizeflag ATTRIBUTE_UNUSED)
+{
+ /* Change movlps/movhps to movhlps/movlhps for 2 register operand
+ forms of these instructions. */
+ if (modrm.mod == 3)
+ {
+ char *p = obuf + strlen (obuf);
+ *(p + 1) = '\0';
+ *p = *(p - 1);
+ *(p - 1) = *(p - 2);
+ *(p - 2) = *(p - 3);
+ *(p - 3) = extrachar;
+ }
+}
+
+static void
+PNI_Fixup (int extrachar ATTRIBUTE_UNUSED, int sizeflag)
+{
+ if (modrm.mod == 3 && modrm.reg == 1 && modrm.rm <= 1)
+ {
+ /* Override "sidt". */
+ size_t olen = strlen (obuf);
+ char *p = obuf + olen - 4;
+ const char * const *names = (address_mode == mode_64bit
+ ? names64 : names32);
+
+ /* We might have a suffix when disassembling with -Msuffix. */
+ if (*p == 'i')
+ --p;
+
+ /* Remove "addr16/addr32" if we aren't in Intel mode. */
+ if (!intel_syntax
+ && (prefixes & PREFIX_ADDR)
+ && olen >= (4 + 7)
+ && *(p - 1) == ' '
+ && strncmp (p - 7, "addr", 4) == 0
+ && (strncmp (p - 3, "16", 2) == 0
+ || strncmp (p - 3, "32", 2) == 0))
+ p -= 7;
+
+ if (modrm.rm)
+ {
+ /* mwait %eax,%ecx */
+ strcpy (p, "mwait");
+ if (!intel_syntax)
+ strcpy (op_out[0], names[0]);
+ }
+ else
+ {
+ /* monitor %eax,%ecx,%edx" */
+ strcpy (p, "monitor");
+ if (!intel_syntax)
+ {
+ const char * const *op1_names;
+ if (!(prefixes & PREFIX_ADDR))
+ op1_names = (address_mode == mode_16bit
+ ? names16 : names);
+ else
+ {
+ op1_names = (address_mode != mode_32bit
+ ? names32 : names16);
+ used_prefixes |= PREFIX_ADDR;
+ }
+ strcpy (op_out[0], op1_names[0]);
+ strcpy (op_out[2], names[2]);
+ }
+ }
+ if (!intel_syntax)
+ {
+ strcpy (op_out[1], names[1]);
+ two_source_ops = 1;
+ }
+
+ codep++;
+ }
+ else
+ OP_M (0, sizeflag);
+}
+
+static void
+SVME_Fixup (int bytemode, int sizeflag)
+{
+ const char *alt;
+ char *p;
+
+ switch (*codep)
+ {
+ case 0xd8:
+ alt = "vmrun";
+ break;
+ case 0xd9:
+ alt = "vmmcall";
+ break;
+ case 0xda:
+ alt = "vmload";
+ break;
+ case 0xdb:
+ alt = "vmsave";
+ break;
+ case 0xdc:
+ alt = "stgi";
+ break;
+ case 0xdd:
+ alt = "clgi";
+ break;
+ case 0xde:
+ alt = "skinit";
+ break;
+ case 0xdf:
+ alt = "invlpga";
+ break;
+ default:
+ OP_M (bytemode, sizeflag);
+ return;
+ }
+ /* Override "lidt". */
+ p = obuf + strlen (obuf) - 4;
+ /* We might have a suffix. */
+ if (*p == 'i')
+ --p;
+ strcpy (p, alt);
+ if (!(prefixes & PREFIX_ADDR))
+ {
+ ++codep;
+ return;
+ }
+ used_prefixes |= PREFIX_ADDR;
+ switch (*codep++)
+ {
+ case 0xdf:
+ strcpy (op_out[1], names32[1]);
+ two_source_ops = 1;
+ /* Fall through. */
+ case 0xd8:
+ case 0xda:
+ case 0xdb:
+ *obufp++ = open_char;
+ if (address_mode == mode_64bit || (sizeflag & AFLAG))
+ alt = names32[0];
+ else
+ alt = names16[0];
+ strcpy (obufp, alt);
+ obufp += strlen (alt);
+ *obufp++ = close_char;
+ *obufp = '\0';
+ break;
+ }
+}
+
+static void
+INVLPG_Fixup (int bytemode, int sizeflag)
+{
+ const char *alt;
+
+ switch (*codep)
+ {
+ case 0xf8:
+ alt = "swapgs";
+ break;
+ case 0xf9:
+ alt = "rdtscp";
+ break;
+ default:
+ OP_M (bytemode, sizeflag);
+ return;
+ }
+ /* Override "invlpg". */
+ strcpy (obuf + strlen (obuf) - 6, alt);
+ codep++;
+}
+
+static void
+BadOp (void)
+{
+ /* Throw away prefixes and 1st. opcode byte. */
+ codep = insn_codep + 1;
+ oappend ("(bad)");
+}
+
+static void
+VMX_Fixup (int extrachar ATTRIBUTE_UNUSED, int sizeflag)
+{
+ if (modrm.mod == 3
+ && modrm.reg == 0
+ && modrm.rm >=1
+ && modrm.rm <= 4)
+ {
+ /* Override "sgdt". */
+ char *p = obuf + strlen (obuf) - 4;
+
+ /* We might have a suffix when disassembling with -Msuffix. */
+ if (*p == 'g')
+ --p;
+
+ switch (modrm.rm)
+ {
+ case 1:
+ strcpy (p, "vmcall");
+ break;
+ case 2:
+ strcpy (p, "vmlaunch");
+ break;
+ case 3:
+ strcpy (p, "vmresume");
+ break;
+ case 4:
+ strcpy (p, "vmxoff");
+ break;
+ }
+
+ codep++;
+ }
+ else
+ OP_E (0, sizeflag);
+}
+
+static void
+OP_VMX (int bytemode, int sizeflag)
+{
+ used_prefixes |= (prefixes & (PREFIX_DATA | PREFIX_REPZ));
+ if (prefixes & PREFIX_DATA)
+ strcpy (obuf, "vmclear");
+ else if (prefixes & PREFIX_REPZ)
+ strcpy (obuf, "vmxon");
+ else
+ strcpy (obuf, "vmptrld");
+ OP_E (bytemode, sizeflag);
+}
+
+static void
+REP_Fixup (int bytemode, int sizeflag)
+{
+ /* The 0xf3 prefix should be displayed as "rep" for ins, outs, movs,
+ lods and stos. */
+ size_t ilen = 0;
+
+ if (prefixes & PREFIX_REPZ)
+ switch (*insn_codep)
+ {
+ case 0x6e: /* outsb */
+ case 0x6f: /* outsw/outsl */
+ case 0xa4: /* movsb */
+ case 0xa5: /* movsw/movsl/movsq */
+ if (!intel_syntax)
+ ilen = 5;
+ else
+ ilen = 4;
+ break;
+ case 0xaa: /* stosb */
+ case 0xab: /* stosw/stosl/stosq */
+ case 0xac: /* lodsb */
+ case 0xad: /* lodsw/lodsl/lodsq */
+ if (!intel_syntax && (sizeflag & SUFFIX_ALWAYS))
+ ilen = 5;
+ else
+ ilen = 4;
+ break;
+ case 0x6c: /* insb */
+ case 0x6d: /* insl/insw */
+ if (!intel_syntax)
+ ilen = 4;
+ else
+ ilen = 3;
+ break;
+ default:
+ abort ();
+ break;
+ }
+
+ if (ilen != 0)
+ {
+ size_t olen;
+ char *p;
+
+ olen = strlen (obuf);
+ p = obuf + olen - ilen - 1 - 4;
+ /* Handle "repz [addr16|addr32]". */
+ if ((prefixes & PREFIX_ADDR))
+ p -= 1 + 6;
+
+ memmove (p + 3, p + 4, olen - (p + 3 - obuf));
+ }
+
+ switch (bytemode)
+ {
+ case al_reg:
+ case eAX_reg:
+ case indir_dx_reg:
+ OP_IMREG (bytemode, sizeflag);
+ break;
+ case eDI_reg:
+ OP_ESreg (bytemode, sizeflag);
+ break;
+ case eSI_reg:
+ OP_DSreg (bytemode, sizeflag);
+ break;
+ default:
+ abort ();
+ break;
+ }
+}
+
+static void
+CMPXCHG8B_Fixup (int bytemode, int sizeflag)
+{
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ {
+ /* Change cmpxchg8b to cmpxchg16b. */
+ char *p = obuf + strlen (obuf) - 2;
+ strcpy (p, "16b");
+ bytemode = o_mode;
+ }
+ OP_M (bytemode, sizeflag);
+}
+
+static void
+XMM_Fixup (int reg, int sizeflag ATTRIBUTE_UNUSED)
+{
+ snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", reg);
+ oappend (scratchbuf + intel_syntax);
+}
+
+static void
+CRC32_Fixup (int bytemode, int sizeflag)
+{
+ /* Add proper suffix to "crc32". */
+ char *p = obuf + strlen (obuf);
+
+ switch (bytemode)
+ {
+ case b_mode:
+ if (intel_syntax)
+ break;
+
+ *p++ = 'b';
+ break;
+ case v_mode:
+ if (intel_syntax)
+ break;
+
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ *p++ = 'q';
+ else if (sizeflag & DFLAG)
+ *p++ = 'l';
+ else
+ *p++ = 'w';
+ used_prefixes |= (prefixes & PREFIX_DATA);
+ break;
+ default:
+ oappend (INTERNAL_DISASSEMBLER_ERROR);
+ break;
+ }
+ *p = '\0';
+
+ if (modrm.mod == 3)
+ {
+ int add;
+
+ /* Skip mod/rm byte. */
+ MODRM_CHECK;
+ codep++;
+
+ USED_REX (REX_B);
+ add = (rex & REX_B) ? 8 : 0;
+ if (bytemode == b_mode)
+ {
+ USED_REX (0);
+ if (rex)
+ oappend (names8rex[modrm.rm + add]);
+ else
+ oappend (names8[modrm.rm + add]);
+ }
+ else
+ {
+ USED_REX (REX_W);
+ if (rex & REX_W)
+ oappend (names64[modrm.rm + add]);
+ else if ((prefixes & PREFIX_DATA))
+ oappend (names16[modrm.rm + add]);
+ else
+ oappend (names32[modrm.rm + add]);
+ }
+ }
+ else
+ OP_E (bytemode, sizeflag);
+}
diff --git a/i386.ld b/i386.ld
new file mode 100644
index 0000000..f8df7bf
--- /dev/null
+++ b/i386.ld
@@ -0,0 +1,153 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt :
+ {
+ *(.rel.plt)
+ PROVIDE_HIDDEN (__rel_iplt_start = .);
+ *(.rel.iplt)
+ PROVIDE_HIDDEN (__rel_iplt_end = .);
+ }
+ .rela.plt :
+ {
+ *(.rela.plt)
+ PROVIDE_HIDDEN (__rela_iplt_start = .);
+ *(.rela.iplt)
+ PROVIDE_HIDDEN (__rela_iplt_end = .);
+ }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/ia64-dis.c b/ia64-dis.c
new file mode 100644
index 0000000..2886df3
--- /dev/null
+++ b/ia64-dis.c
@@ -0,0 +1,10599 @@
+/* ia64-dis.c -- Disassemble ia64 instructions
+ Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 2, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <assert.h>
+#include <string.h>
+
+#include "dis-asm.h"
+
+/* ia64.h -- Header file for ia64 opcode table
+ Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006
+ Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com> */
+
+#include <sys/types.h>
+
+typedef uint64_t ia64_insn;
+
+enum ia64_insn_type
+ {
+ IA64_TYPE_NIL = 0, /* illegal type */
+ IA64_TYPE_A, /* integer alu (I- or M-unit) */
+ IA64_TYPE_I, /* non-alu integer (I-unit) */
+ IA64_TYPE_M, /* memory (M-unit) */
+ IA64_TYPE_B, /* branch (B-unit) */
+ IA64_TYPE_F, /* floating-point (F-unit) */
+ IA64_TYPE_X, /* long encoding (X-unit) */
+ IA64_TYPE_DYN, /* Dynamic opcode */
+ IA64_NUM_TYPES
+ };
+
+enum ia64_unit
+ {
+ IA64_UNIT_NIL = 0, /* illegal unit */
+ IA64_UNIT_I, /* integer unit */
+ IA64_UNIT_M, /* memory unit */
+ IA64_UNIT_B, /* branching unit */
+ IA64_UNIT_F, /* floating-point unit */
+ IA64_UNIT_L, /* long "unit" */
+ IA64_UNIT_X, /* may be integer or branch unit */
+ IA64_NUM_UNITS
+ };
+
+/* Changes to this enumeration must be propagated to the operand table in
+ bfd/cpu-ia64-opc.c
+ */
+enum ia64_opnd
+ {
+ IA64_OPND_NIL, /* no operand---MUST BE FIRST!*/
+
+ /* constants */
+ IA64_OPND_AR_CSD, /* application register csd (ar.csd) */
+ IA64_OPND_AR_CCV, /* application register ccv (ar.ccv) */
+ IA64_OPND_AR_PFS, /* application register pfs (ar.pfs) */
+ IA64_OPND_C1, /* the constant 1 */
+ IA64_OPND_C8, /* the constant 8 */
+ IA64_OPND_C16, /* the constant 16 */
+ IA64_OPND_GR0, /* gr0 */
+ IA64_OPND_IP, /* instruction pointer (ip) */
+ IA64_OPND_PR, /* predicate register (pr) */
+ IA64_OPND_PR_ROT, /* rotating predicate register (pr.rot) */
+ IA64_OPND_PSR, /* processor status register (psr) */
+ IA64_OPND_PSR_L, /* processor status register L (psr.l) */
+ IA64_OPND_PSR_UM, /* processor status register UM (psr.um) */
+
+ /* register operands: */
+ IA64_OPND_AR3, /* third application register # (bits 20-26) */
+ IA64_OPND_B1, /* branch register # (bits 6-8) */
+ IA64_OPND_B2, /* branch register # (bits 13-15) */
+ IA64_OPND_CR3, /* third control register # (bits 20-26) */
+ IA64_OPND_F1, /* first floating-point register # */
+ IA64_OPND_F2, /* second floating-point register # */
+ IA64_OPND_F3, /* third floating-point register # */
+ IA64_OPND_F4, /* fourth floating-point register # */
+ IA64_OPND_P1, /* first predicate # */
+ IA64_OPND_P2, /* second predicate # */
+ IA64_OPND_R1, /* first register # */
+ IA64_OPND_R2, /* second register # */
+ IA64_OPND_R3, /* third register # */
+ IA64_OPND_R3_2, /* third register # (limited to gr0-gr3) */
+
+ /* memory operands: */
+ IA64_OPND_MR3, /* memory at addr of third register # */
+
+ /* indirect operands: */
+ IA64_OPND_CPUID_R3, /* cpuid[reg] */
+ IA64_OPND_DBR_R3, /* dbr[reg] */
+ IA64_OPND_DTR_R3, /* dtr[reg] */
+ IA64_OPND_ITR_R3, /* itr[reg] */
+ IA64_OPND_IBR_R3, /* ibr[reg] */
+ IA64_OPND_MSR_R3, /* msr[reg] */
+ IA64_OPND_PKR_R3, /* pkr[reg] */
+ IA64_OPND_PMC_R3, /* pmc[reg] */
+ IA64_OPND_PMD_R3, /* pmd[reg] */
+ IA64_OPND_RR_R3, /* rr[reg] */
+
+ /* immediate operands: */
+ IA64_OPND_CCNT5, /* 5-bit count (31 - bits 20-24) */
+ IA64_OPND_CNT2a, /* 2-bit count (1 + bits 27-28) */
+ IA64_OPND_CNT2b, /* 2-bit count (bits 27-28): 1, 2, 3 */
+ IA64_OPND_CNT2c, /* 2-bit count (bits 30-31): 0, 7, 15, or 16 */
+ IA64_OPND_CNT5, /* 5-bit count (bits 14-18) */
+ IA64_OPND_CNT6, /* 6-bit count (bits 27-32) */
+ IA64_OPND_CPOS6a, /* 6-bit count (63 - bits 20-25) */
+ IA64_OPND_CPOS6b, /* 6-bit count (63 - bits 14-19) */
+ IA64_OPND_CPOS6c, /* 6-bit count (63 - bits 31-36) */
+ IA64_OPND_IMM1, /* signed 1-bit immediate (bit 36) */
+ IA64_OPND_IMMU2, /* unsigned 2-bit immediate (bits 13-14) */
+ IA64_OPND_IMMU5b, /* unsigned 5-bit immediate (32 + bits 14-18) */
+ IA64_OPND_IMMU7a, /* unsigned 7-bit immediate (bits 13-19) */
+ IA64_OPND_IMMU7b, /* unsigned 7-bit immediate (bits 20-26) */
+ IA64_OPND_SOF, /* 8-bit stack frame size */
+ IA64_OPND_SOL, /* 8-bit size of locals */
+ IA64_OPND_SOR, /* 6-bit number of rotating registers (scaled by 8) */
+ IA64_OPND_IMM8, /* signed 8-bit immediate (bits 13-19 & 36) */
+ IA64_OPND_IMM8U4, /* cmp4*u signed 8-bit immediate (bits 13-19 & 36) */
+ IA64_OPND_IMM8M1, /* signed 8-bit immediate -1 (bits 13-19 & 36) */
+ IA64_OPND_IMM8M1U4, /* cmp4*u signed 8-bit immediate -1 (bits 13-19 & 36)*/
+ IA64_OPND_IMM8M1U8, /* cmp*u signed 8-bit immediate -1 (bits 13-19 & 36) */
+ IA64_OPND_IMMU9, /* unsigned 9-bit immediate (bits 33-34, 20-26) */
+ IA64_OPND_IMM9a, /* signed 9-bit immediate (bits 6-12, 27, 36) */
+ IA64_OPND_IMM9b, /* signed 9-bit immediate (bits 13-19, 27, 36) */
+ IA64_OPND_IMM14, /* signed 14-bit immediate (bits 13-19, 27-32, 36) */
+ IA64_OPND_IMM17, /* signed 17-bit immediate (2*bits 6-12, 24-31, 36) */
+ IA64_OPND_IMMU21, /* unsigned 21-bit immediate (bits 6-25, 36) */
+ IA64_OPND_IMM22, /* signed 22-bit immediate (bits 13-19, 22-36) */
+ IA64_OPND_IMMU24, /* unsigned 24-bit immediate (bits 6-26, 31-32, 36) */
+ IA64_OPND_IMM44, /* signed 44-bit immediate (2^16*bits 6-32, 36) */
+ IA64_OPND_IMMU62, /* unsigned 62-bit immediate */
+ IA64_OPND_IMMU64, /* unsigned 64-bit immediate (lotsa bits...) */
+ IA64_OPND_INC3, /* signed 3-bit (bits 13-15): +/-1, 4, 8, 16 */
+ IA64_OPND_LEN4, /* 4-bit count (bits 27-30 + 1) */
+ IA64_OPND_LEN6, /* 6-bit count (bits 27-32 + 1) */
+ IA64_OPND_MBTYPE4, /* 4-bit mux type (bits 20-23) */
+ IA64_OPND_MHTYPE8, /* 8-bit mux type (bits 20-27) */
+ IA64_OPND_POS6, /* 6-bit count (bits 14-19) */
+ IA64_OPND_TAG13, /* signed 13-bit tag (ip + 16*bits 6-12, 33-34) */
+ IA64_OPND_TAG13b, /* signed 13-bit tag (ip + 16*bits 24-32) */
+ IA64_OPND_TGT25, /* signed 25-bit (ip + 16*bits 6-25, 36) */
+ IA64_OPND_TGT25b, /* signed 25-bit (ip + 16*bits 6-12, 20-32, 36) */
+ IA64_OPND_TGT25c, /* signed 25-bit (ip + 16*bits 13-32, 36) */
+ IA64_OPND_TGT64, /* 64-bit (ip + 16*bits 13-32, 36, 2-40(L)) */
+ IA64_OPND_LDXMOV, /* any symbol, generates R_IA64_LDXMOV. */
+
+ IA64_OPND_COUNT /* # of operand types (MUST BE LAST!) */
+ };
+
+enum ia64_dependency_mode
+{
+ IA64_DV_RAW,
+ IA64_DV_WAW,
+ IA64_DV_WAR,
+};
+
+enum ia64_dependency_semantics
+{
+ IA64_DVS_NONE,
+ IA64_DVS_IMPLIED,
+ IA64_DVS_IMPLIEDF,
+ IA64_DVS_DATA,
+ IA64_DVS_INSTR,
+ IA64_DVS_SPECIFIC,
+ IA64_DVS_STOP,
+ IA64_DVS_OTHER,
+};
+
+enum ia64_resource_specifier
+{
+ IA64_RS_ANY,
+ IA64_RS_AR_K,
+ IA64_RS_AR_UNAT,
+ IA64_RS_AR, /* 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111 */
+ IA64_RS_ARb, /* 48-63, 112-127 */
+ IA64_RS_BR,
+ IA64_RS_CFM,
+ IA64_RS_CPUID,
+ IA64_RS_CR_IRR,
+ IA64_RS_CR_LRR,
+ IA64_RS_CR, /* 3-7,10-15,18,26-63,75-79,82-127 */
+ IA64_RS_DBR,
+ IA64_RS_FR,
+ IA64_RS_FRb,
+ IA64_RS_GR0,
+ IA64_RS_GR,
+ IA64_RS_IBR,
+ IA64_RS_INSERVICE, /* CR[EOI] or CR[IVR] */
+ IA64_RS_MSR,
+ IA64_RS_PKR,
+ IA64_RS_PMC,
+ IA64_RS_PMD,
+ IA64_RS_PR, /* non-rotating, 1-15 */
+ IA64_RS_PRr, /* rotating, 16-62 */
+ IA64_RS_PR63,
+ IA64_RS_RR,
+
+ IA64_RS_ARX, /* ARs not in RS_AR or RS_ARb */
+ IA64_RS_CRX, /* CRs not in RS_CR */
+ IA64_RS_PSR, /* PSR bits */
+ IA64_RS_RSE, /* implementation-specific RSE resources */
+ IA64_RS_AR_FPSR,
+};
+
+enum ia64_rse_resource
+{
+ IA64_RSE_N_STACKED_PHYS,
+ IA64_RSE_BOF,
+ IA64_RSE_STORE_REG,
+ IA64_RSE_LOAD_REG,
+ IA64_RSE_BSPLOAD,
+ IA64_RSE_RNATBITINDEX,
+ IA64_RSE_CFLE,
+ IA64_RSE_NDIRTY,
+};
+
+/* Information about a given resource dependency */
+struct ia64_dependency
+{
+ /* Name of the resource */
+ const char *name;
+ /* Does this dependency need further specification? */
+ enum ia64_resource_specifier specifier;
+ /* Mode of dependency */
+ enum ia64_dependency_mode mode;
+ /* Dependency semantics */
+ enum ia64_dependency_semantics semantics;
+ /* Register index, if applicable (distinguishes AR, CR, and PSR deps) */
+#define REG_NONE (-1)
+ int regindex;
+ /* Special info on semantics */
+ const char *info;
+};
+
+/* Two arrays of indexes into the ia64_dependency table.
+ chks are dependencies to check for conflicts when an opcode is
+ encountered; regs are dependencies to register (mark as used) when an
+ opcode is used. chks correspond to readers (RAW) or writers (WAW or
+ WAR) of a resource, while regs correspond to writers (RAW or WAW) and
+ readers (WAR) of a resource. */
+struct ia64_opcode_dependency
+{
+ int nchks;
+ const unsigned short *chks;
+ int nregs;
+ const unsigned short *regs;
+};
+
+/* encode/extract the note/index for a dependency */
+#define RDEP(N,X) (((N)<<11)|(X))
+#define NOTE(X) (((X)>>11)&0x1F)
+#define DEP(X) ((X)&0x7FF)
+
+/* A template descriptor describes the execution units that are active
+ for each of the three slots. It also specifies the location of
+ instruction group boundaries that may be present between two slots. */
+struct ia64_templ_desc
+ {
+ int group_boundary; /* 0=no boundary, 1=between slot 0 & 1, etc. */
+ enum ia64_unit exec_unit[3];
+ const char *name;
+ };
+
+/* The opcode table is an array of struct ia64_opcode. */
+
+struct ia64_opcode
+ {
+ /* The opcode name. */
+ const char *name;
+
+ /* The type of the instruction: */
+ enum ia64_insn_type type;
+
+ /* Number of output operands: */
+ int num_outputs;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ ia64_insn opcode;
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ ia64_insn mask;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ enum ia64_opnd operands[5];
+
+ /* One bit flags for the opcode. These are primarily used to
+ indicate specific processors and environments support the
+ instructions. The defined values are listed below. */
+ unsigned int flags;
+
+ /* Used by ia64_find_next_opcode (). */
+ short ent_index;
+
+ /* Opcode dependencies. */
+ const struct ia64_opcode_dependency *dependencies;
+ };
+
+/* Values defined for the flags field of a struct ia64_opcode. */
+
+#define IA64_OPCODE_FIRST (1<<0) /* must be first in an insn group */
+#define IA64_OPCODE_X_IN_MLX (1<<1) /* insn is allowed in X slot of MLX */
+#define IA64_OPCODE_LAST (1<<2) /* must be last in an insn group */
+#define IA64_OPCODE_PRIV (1<<3) /* privileged instruct */
+#define IA64_OPCODE_SLOT2 (1<<4) /* insn allowed in slot 2 only */
+#define IA64_OPCODE_NO_PRED (1<<5) /* insn cannot be predicated */
+#define IA64_OPCODE_PSEUDO (1<<6) /* insn is a pseudo-op */
+#define IA64_OPCODE_F2_EQ_F3 (1<<7) /* constraint: F2 == F3 */
+#define IA64_OPCODE_LEN_EQ_64MCNT (1<<8) /* constraint: LEN == 64-CNT */
+#define IA64_OPCODE_MOD_RRBS (1<<9) /* modifies all rrbs in CFM */
+#define IA64_OPCODE_POSTINC (1<<10) /* postincrement MR3 operand */
+
+/* A macro to extract the major opcode from an instruction. */
+#define IA64_OP(i) (((i) >> 37) & 0xf)
+
+enum ia64_operand_class
+ {
+ IA64_OPND_CLASS_CST, /* constant */
+ IA64_OPND_CLASS_REG, /* register */
+ IA64_OPND_CLASS_IND, /* indirect register */
+ IA64_OPND_CLASS_ABS, /* absolute value */
+ IA64_OPND_CLASS_REL, /* IP-relative value */
+ };
+
+/* The operands table is an array of struct ia64_operand. */
+
+struct ia64_operand
+{
+ enum ia64_operand_class class;
+
+ /* Set VALUE as the operand bits for the operand of type SELF in the
+ instruction pointed to by CODE. If an error occurs, *CODE is not
+ modified and the returned string describes the cause of the
+ error. If no error occurs, NULL is returned. */
+ const char *(*insert) (const struct ia64_operand *self, ia64_insn value,
+ ia64_insn *code);
+
+ /* Extract the operand bits for an operand of type SELF from
+ instruction CODE store them in *VALUE. If an error occurs, the
+ cause of the error is described by the string returned. If no
+ error occurs, NULL is returned. */
+ const char *(*extract) (const struct ia64_operand *self, ia64_insn code,
+ ia64_insn *value);
+
+ /* A string whose meaning depends on the operand class. */
+
+ const char *str;
+
+ struct bit_field
+ {
+ /* The number of bits in the operand. */
+ int bits;
+
+ /* How far the operand is left shifted in the instruction. */
+ int shift;
+ }
+ field[4]; /* no operand has more than this many bit-fields */
+
+ unsigned int flags;
+
+ const char *desc; /* brief description */
+};
+
+/* Values defined for the flags field of a struct ia64_operand. */
+
+/* Disassemble as signed decimal (instead of hex): */
+#define IA64_OPND_FLAG_DECIMAL_SIGNED (1<<0)
+/* Disassemble as unsigned decimal (instead of hex): */
+#define IA64_OPND_FLAG_DECIMAL_UNSIGNED (1<<1)
+
+#define NELEMS(a) ((int) (sizeof (a) / sizeof (a[0])))
+
+static const char*
+ins_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED,
+ ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED)
+{
+ return "internal error---this shouldn't happen";
+}
+
+static const char*
+ext_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED,
+ ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED)
+{
+ return "internal error---this shouldn't happen";
+}
+
+static const char*
+ins_const (const struct ia64_operand *self ATTRIBUTE_UNUSED,
+ ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static const char*
+ext_const (const struct ia64_operand *self ATTRIBUTE_UNUSED,
+ ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static const char*
+ins_reg (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ if (value >= 1u << self->field[0].bits)
+ return "register number out of range";
+
+ *code |= value << self->field[0].shift;
+ return 0;
+}
+
+static const char*
+ext_reg (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ *valuep = ((code >> self->field[0].shift)
+ & ((1u << self->field[0].bits) - 1));
+ return 0;
+}
+
+static const char*
+ins_immu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ ia64_insn new = 0;
+ int i;
+
+ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i)
+ {
+ new |= ((value & ((((ia64_insn) 1) << self->field[i].bits) - 1))
+ << self->field[i].shift);
+ value >>= self->field[i].bits;
+ }
+ if (value)
+ return "integer operand out of range";
+
+ *code |= new;
+ return 0;
+}
+
+static const char*
+ext_immu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ uint64_t value = 0;
+ int i, bits = 0, total = 0;
+
+ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i)
+ {
+ bits = self->field[i].bits;
+ value |= ((code >> self->field[i].shift)
+ & ((((uint64_t) 1) << bits) - 1)) << total;
+ total += bits;
+ }
+ *valuep = value;
+ return 0;
+}
+
+static const char*
+ins_immu5b (const struct ia64_operand *self, ia64_insn value,
+ ia64_insn *code)
+{
+ if (value < 32 || value > 63)
+ return "value must be between 32 and 63";
+ return ins_immu (self, value - 32, code);
+}
+
+static const char*
+ext_immu5b (const struct ia64_operand *self, ia64_insn code,
+ ia64_insn *valuep)
+{
+ const char *result;
+
+ result = ext_immu (self, code, valuep);
+ if (result)
+ return result;
+
+ *valuep = *valuep + 32;
+ return 0;
+}
+
+static const char*
+ins_immus8 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ if (value & 0x7)
+ return "value not an integer multiple of 8";
+ return ins_immu (self, value >> 3, code);
+}
+
+static const char*
+ext_immus8 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ const char *result;
+
+ result = ext_immu (self, code, valuep);
+ if (result)
+ return result;
+
+ *valuep = *valuep << 3;
+ return 0;
+}
+
+static const char*
+ins_imms_scaled (const struct ia64_operand *self, ia64_insn value,
+ ia64_insn *code, int scale)
+{
+ int64_t svalue = value, sign_bit = 0;
+ ia64_insn new = 0;
+ int i;
+
+ svalue >>= scale;
+
+ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i)
+ {
+ new |= ((svalue & ((((ia64_insn) 1) << self->field[i].bits) - 1))
+ << self->field[i].shift);
+ sign_bit = (svalue >> (self->field[i].bits - 1)) & 1;
+ svalue >>= self->field[i].bits;
+ }
+ if ((!sign_bit && svalue != 0) || (sign_bit && svalue != -1))
+ return "integer operand out of range";
+
+ *code |= new;
+ return 0;
+}
+
+static const char*
+ext_imms_scaled (const struct ia64_operand *self, ia64_insn code,
+ ia64_insn *valuep, int scale)
+{
+ int i, bits = 0, total = 0;
+ int64_t val = 0, sign;
+
+ for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i)
+ {
+ bits = self->field[i].bits;
+ val |= ((code >> self->field[i].shift)
+ & ((((uint64_t) 1) << bits) - 1)) << total;
+ total += bits;
+ }
+ /* sign extend: */
+ sign = (int64_t) 1 << (total - 1);
+ val = (val ^ sign) - sign;
+
+ *valuep = (val << scale);
+ return 0;
+}
+
+static const char*
+ins_imms (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ return ins_imms_scaled (self, value, code, 0);
+}
+
+static const char*
+ins_immsu4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000;
+
+ return ins_imms_scaled (self, value, code, 0);
+}
+
+static const char*
+ext_imms (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ return ext_imms_scaled (self, code, valuep, 0);
+}
+
+static const char*
+ins_immsm1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ --value;
+ return ins_imms_scaled (self, value, code, 0);
+}
+
+static const char*
+ins_immsm1u4 (const struct ia64_operand *self, ia64_insn value,
+ ia64_insn *code)
+{
+ value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000;
+
+ --value;
+ return ins_imms_scaled (self, value, code, 0);
+}
+
+static const char*
+ext_immsm1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ const char *res = ext_imms_scaled (self, code, valuep, 0);
+
+ ++*valuep;
+ return res;
+}
+
+static const char*
+ins_imms1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ return ins_imms_scaled (self, value, code, 1);
+}
+
+static const char*
+ext_imms1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ return ext_imms_scaled (self, code, valuep, 1);
+}
+
+static const char*
+ins_imms4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ return ins_imms_scaled (self, value, code, 4);
+}
+
+static const char*
+ext_imms4 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ return ext_imms_scaled (self, code, valuep, 4);
+}
+
+static const char*
+ins_imms16 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ return ins_imms_scaled (self, value, code, 16);
+}
+
+static const char*
+ext_imms16 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ return ext_imms_scaled (self, code, valuep, 16);
+}
+
+static const char*
+ins_cimmu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ ia64_insn mask = (((ia64_insn) 1) << self->field[0].bits) - 1;
+ return ins_immu (self, value ^ mask, code);
+}
+
+static const char*
+ext_cimmu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ const char *result;
+ ia64_insn mask;
+
+ mask = (((ia64_insn) 1) << self->field[0].bits) - 1;
+ result = ext_immu (self, code, valuep);
+ if (!result)
+ {
+ mask = (((ia64_insn) 1) << self->field[0].bits) - 1;
+ *valuep ^= mask;
+ }
+ return result;
+}
+
+static const char*
+ins_cnt (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ --value;
+ if (value >= ((uint64_t) 1) << self->field[0].bits)
+ return "count out of range";
+
+ *code |= value << self->field[0].shift;
+ return 0;
+}
+
+static const char*
+ext_cnt (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ *valuep = ((code >> self->field[0].shift)
+ & ((((uint64_t) 1) << self->field[0].bits) - 1)) + 1;
+ return 0;
+}
+
+static const char*
+ins_cnt2b (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ --value;
+
+ if (value > 2)
+ return "count must be in range 1..3";
+
+ *code |= value << self->field[0].shift;
+ return 0;
+}
+
+static const char*
+ext_cnt2b (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ *valuep = ((code >> self->field[0].shift) & 0x3) + 1;
+ return 0;
+}
+
+static const char*
+ins_cnt2c (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ switch (value)
+ {
+ case 0: value = 0; break;
+ case 7: value = 1; break;
+ case 15: value = 2; break;
+ case 16: value = 3; break;
+ default: return "count must be 0, 7, 15, or 16";
+ }
+ *code |= value << self->field[0].shift;
+ return 0;
+}
+
+static const char*
+ext_cnt2c (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ ia64_insn value;
+
+ value = (code >> self->field[0].shift) & 0x3;
+ switch (value)
+ {
+ case 0: value = 0; break;
+ case 1: value = 7; break;
+ case 2: value = 15; break;
+ case 3: value = 16; break;
+ }
+ *valuep = value;
+ return 0;
+}
+
+static const char*
+ins_inc3 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code)
+{
+ int64_t val = value;
+ uint64_t sign = 0;
+
+ if (val < 0)
+ {
+ sign = 0x4;
+ value = -value;
+ }
+ switch (value)
+ {
+ case 1: value = 3; break;
+ case 4: value = 2; break;
+ case 8: value = 1; break;
+ case 16: value = 0; break;
+ default: return "count must be +/- 1, 4, 8, or 16";
+ }
+ *code |= (sign | value) << self->field[0].shift;
+ return 0;
+}
+
+static const char*
+ext_inc3 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep)
+{
+ int64_t val;
+ int negate;
+
+ val = (code >> self->field[0].shift) & 0x7;
+ negate = val & 0x4;
+ switch (val & 0x3)
+ {
+ case 0: val = 16; break;
+ case 1: val = 8; break;
+ case 2: val = 4; break;
+ case 3: val = 1; break;
+ }
+ if (negate)
+ val = -val;
+
+ *valuep = val;
+ return 0;
+}
+
+#define CST IA64_OPND_CLASS_CST
+#define REG IA64_OPND_CLASS_REG
+#define IND IA64_OPND_CLASS_IND
+#define ABS IA64_OPND_CLASS_ABS
+#define REL IA64_OPND_CLASS_REL
+
+#define SDEC IA64_OPND_FLAG_DECIMAL_SIGNED
+#define UDEC IA64_OPND_FLAG_DECIMAL_UNSIGNED
+
+const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT] =
+ {
+ /* constants: */
+ { CST, ins_const, ext_const, "NIL", {{ 0, 0}}, 0, "<none>" },
+ { CST, ins_const, ext_const, "ar.csd", {{ 0, 0}}, 0, "ar.csd" },
+ { CST, ins_const, ext_const, "ar.ccv", {{ 0, 0}}, 0, "ar.ccv" },
+ { CST, ins_const, ext_const, "ar.pfs", {{ 0, 0}}, 0, "ar.pfs" },
+ { CST, ins_const, ext_const, "1", {{ 0, 0}}, 0, "1" },
+ { CST, ins_const, ext_const, "8", {{ 0, 0}}, 0, "8" },
+ { CST, ins_const, ext_const, "16", {{ 0, 0}}, 0, "16" },
+ { CST, ins_const, ext_const, "r0", {{ 0, 0}}, 0, "r0" },
+ { CST, ins_const, ext_const, "ip", {{ 0, 0}}, 0, "ip" },
+ { CST, ins_const, ext_const, "pr", {{ 0, 0}}, 0, "pr" },
+ { CST, ins_const, ext_const, "pr.rot", {{ 0, 0}}, 0, "pr.rot" },
+ { CST, ins_const, ext_const, "psr", {{ 0, 0}}, 0, "psr" },
+ { CST, ins_const, ext_const, "psr.l", {{ 0, 0}}, 0, "psr.l" },
+ { CST, ins_const, ext_const, "psr.um", {{ 0, 0}}, 0, "psr.um" },
+
+ /* register operands: */
+ { REG, ins_reg, ext_reg, "ar", {{ 7, 20}}, 0, /* AR3 */
+ "an application register" },
+ { REG, ins_reg, ext_reg, "b", {{ 3, 6}}, 0, /* B1 */
+ "a branch register" },
+ { REG, ins_reg, ext_reg, "b", {{ 3, 13}}, 0, /* B2 */
+ "a branch register"},
+ { REG, ins_reg, ext_reg, "cr", {{ 7, 20}}, 0, /* CR */
+ "a control register"},
+ { REG, ins_reg, ext_reg, "f", {{ 7, 6}}, 0, /* F1 */
+ "a floating-point register" },
+ { REG, ins_reg, ext_reg, "f", {{ 7, 13}}, 0, /* F2 */
+ "a floating-point register" },
+ { REG, ins_reg, ext_reg, "f", {{ 7, 20}}, 0, /* F3 */
+ "a floating-point register" },
+ { REG, ins_reg, ext_reg, "f", {{ 7, 27}}, 0, /* F4 */
+ "a floating-point register" },
+ { REG, ins_reg, ext_reg, "p", {{ 6, 6}}, 0, /* P1 */
+ "a predicate register" },
+ { REG, ins_reg, ext_reg, "p", {{ 6, 27}}, 0, /* P2 */
+ "a predicate register" },
+ { REG, ins_reg, ext_reg, "r", {{ 7, 6}}, 0, /* R1 */
+ "a general register" },
+ { REG, ins_reg, ext_reg, "r", {{ 7, 13}}, 0, /* R2 */
+ "a general register" },
+ { REG, ins_reg, ext_reg, "r", {{ 7, 20}}, 0, /* R3 */
+ "a general register" },
+ { REG, ins_reg, ext_reg, "r", {{ 2, 20}}, 0, /* R3_2 */
+ "a general register r0-r3" },
+
+ /* memory operands: */
+ { IND, ins_reg, ext_reg, "", {{7, 20}}, 0, /* MR3 */
+ "a memory address" },
+
+ /* indirect operands: */
+ { IND, ins_reg, ext_reg, "cpuid", {{7, 20}}, 0, /* CPUID_R3 */
+ "a cpuid register" },
+ { IND, ins_reg, ext_reg, "dbr", {{7, 20}}, 0, /* DBR_R3 */
+ "a dbr register" },
+ { IND, ins_reg, ext_reg, "dtr", {{7, 20}}, 0, /* DTR_R3 */
+ "a dtr register" },
+ { IND, ins_reg, ext_reg, "itr", {{7, 20}}, 0, /* ITR_R3 */
+ "an itr register" },
+ { IND, ins_reg, ext_reg, "ibr", {{7, 20}}, 0, /* IBR_R3 */
+ "an ibr register" },
+ { IND, ins_reg, ext_reg, "msr", {{7, 20}}, 0, /* MSR_R3 */
+ "an msr register" },
+ { IND, ins_reg, ext_reg, "pkr", {{7, 20}}, 0, /* PKR_R3 */
+ "a pkr register" },
+ { IND, ins_reg, ext_reg, "pmc", {{7, 20}}, 0, /* PMC_R3 */
+ "a pmc register" },
+ { IND, ins_reg, ext_reg, "pmd", {{7, 20}}, 0, /* PMD_R3 */
+ "a pmd register" },
+ { IND, ins_reg, ext_reg, "rr", {{7, 20}}, 0, /* RR_R3 */
+ "an rr register" },
+
+ /* immediate operands: */
+ { ABS, ins_cimmu, ext_cimmu, 0, {{ 5, 20 }}, UDEC, /* CCNT5 */
+ "a 5-bit count (0-31)" },
+ { ABS, ins_cnt, ext_cnt, 0, {{ 2, 27 }}, UDEC, /* CNT2a */
+ "a 2-bit count (1-4)" },
+ { ABS, ins_cnt2b, ext_cnt2b, 0, {{ 2, 27 }}, UDEC, /* CNT2b */
+ "a 2-bit count (1-3)" },
+ { ABS, ins_cnt2c, ext_cnt2c, 0, {{ 2, 30 }}, UDEC, /* CNT2c */
+ "a count (0, 7, 15, or 16)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 5, 14}}, UDEC, /* CNT5 */
+ "a 5-bit count (0-31)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 6, 27}}, UDEC, /* CNT6 */
+ "a 6-bit count (0-63)" },
+ { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 20}}, UDEC, /* CPOS6a */
+ "a 6-bit bit pos (0-63)" },
+ { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 14}}, UDEC, /* CPOS6b */
+ "a 6-bit bit pos (0-63)" },
+ { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 31}}, UDEC, /* CPOS6c */
+ "a 6-bit bit pos (0-63)" },
+ { ABS, ins_imms, ext_imms, 0, {{ 1, 36}}, SDEC, /* IMM1 */
+ "a 1-bit integer (-1, 0)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 2, 13}}, UDEC, /* IMMU2 */
+ "a 2-bit unsigned (0-3)" },
+ { ABS, ins_immu5b, ext_immu5b, 0, {{ 5, 14}}, UDEC, /* IMMU5b */
+ "a 5-bit unsigned (32 + (0-31))" },
+ { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, 0, /* IMMU7a */
+ "a 7-bit unsigned (0-127)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, 0, /* IMMU7b */
+ "a 7-bit unsigned (0-127)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, UDEC, /* SOF */
+ "a frame size (register count)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, UDEC, /* SOL */
+ "a local register count" },
+ { ABS, ins_immus8,ext_immus8,0, {{ 4, 27}}, UDEC, /* SOR */
+ "a rotating register count (integer multiple of 8)" },
+ { ABS, ins_imms, ext_imms, 0, /* IMM8 */
+ {{ 7, 13}, { 1, 36}}, SDEC,
+ "an 8-bit integer (-128-127)" },
+ { ABS, ins_immsu4, ext_imms, 0, /* IMM8U4 */
+ {{ 7, 13}, { 1, 36}}, SDEC,
+ "an 8-bit signed integer for 32-bit unsigned compare (-128-127)" },
+ { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1 */
+ {{ 7, 13}, { 1, 36}}, SDEC,
+ "an 8-bit integer (-127-128)" },
+ { ABS, ins_immsm1u4, ext_immsm1, 0, /* IMM8M1U4 */
+ {{ 7, 13}, { 1, 36}}, SDEC,
+ "an 8-bit integer for 32-bit unsigned compare (-127-(-1),1-128,0x100000000)" },
+ { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1U8 */
+ {{ 7, 13}, { 1, 36}}, SDEC,
+ "an 8-bit integer for 64-bit unsigned compare (-127-(-1),1-128,0x10000000000000000)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 2, 33}, { 7, 20}}, 0, /* IMMU9 */
+ "a 9-bit unsigned (0-511)" },
+ { ABS, ins_imms, ext_imms, 0, /* IMM9a */
+ {{ 7, 6}, { 1, 27}, { 1, 36}}, SDEC,
+ "a 9-bit integer (-256-255)" },
+ { ABS, ins_imms, ext_imms, 0, /* IMM9b */
+ {{ 7, 13}, { 1, 27}, { 1, 36}}, SDEC,
+ "a 9-bit integer (-256-255)" },
+ { ABS, ins_imms, ext_imms, 0, /* IMM14 */
+ {{ 7, 13}, { 6, 27}, { 1, 36}}, SDEC,
+ "a 14-bit integer (-8192-8191)" },
+ { ABS, ins_imms1, ext_imms1, 0, /* IMM17 */
+ {{ 7, 6}, { 8, 24}, { 1, 36}}, 0,
+ "a 17-bit integer (-65536-65535)" },
+ { ABS, ins_immu, ext_immu, 0, {{20, 6}, { 1, 36}}, 0, /* IMMU21 */
+ "a 21-bit unsigned" },
+ { ABS, ins_imms, ext_imms, 0, /* IMM22 */
+ {{ 7, 13}, { 9, 27}, { 5, 22}, { 1, 36}}, SDEC,
+ "a 22-bit signed integer" },
+ { ABS, ins_immu, ext_immu, 0, /* IMMU24 */
+ {{21, 6}, { 2, 31}, { 1, 36}}, 0,
+ "a 24-bit unsigned" },
+ { ABS, ins_imms16,ext_imms16,0, {{27, 6}, { 1, 36}}, 0, /* IMM44 */
+ "a 44-bit unsigned (least 16 bits ignored/zeroes)" },
+ { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU62 */
+ "a 62-bit unsigned" },
+ { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU64 */
+ "a 64-bit unsigned" },
+ { ABS, ins_inc3, ext_inc3, 0, {{ 3, 13}}, SDEC, /* INC3 */
+ "an increment (+/- 1, 4, 8, or 16)" },
+ { ABS, ins_cnt, ext_cnt, 0, {{ 4, 27}}, UDEC, /* LEN4 */
+ "a 4-bit length (1-16)" },
+ { ABS, ins_cnt, ext_cnt, 0, {{ 6, 27}}, UDEC, /* LEN6 */
+ "a 6-bit length (1-64)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 4, 20}}, 0, /* MBTYPE4 */
+ "a mix type (@rev, @mix, @shuf, @alt, or @brcst)" },
+ { ABS, ins_immu, ext_immu, 0, {{ 8, 20}}, 0, /* MBTYPE8 */
+ "an 8-bit mix type" },
+ { ABS, ins_immu, ext_immu, 0, {{ 6, 14}}, UDEC, /* POS6 */
+ "a 6-bit bit pos (0-63)" },
+ { REL, ins_imms4, ext_imms4, 0, {{ 7, 6}, { 2, 33}}, 0, /* TAG13 */
+ "a branch tag" },
+ { REL, ins_imms4, ext_imms4, 0, {{ 9, 24}}, 0, /* TAG13b */
+ "a branch tag" },
+ { REL, ins_imms4, ext_imms4, 0, {{20, 6}, { 1, 36}}, 0, /* TGT25 */
+ "a branch target" },
+ { REL, ins_imms4, ext_imms4, 0, /* TGT25b */
+ {{ 7, 6}, {13, 20}, { 1, 36}}, 0,
+ "a branch target" },
+ { REL, ins_imms4, ext_imms4, 0, {{20, 13}, { 1, 36}}, 0, /* TGT25c */
+ "a branch target" },
+ { REL, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* TGT64 */
+ "a branch target" },
+
+ { ABS, ins_const, ext_const, 0, {{0, 0}}, 0, /* LDXMOV */
+ "ldxmov target" },
+ };
+
+
+/* ia64-asmtab.h -- Header for compacted IA-64 opcode tables.
+ Copyright 1999, 2000 Free Software Foundation, Inc.
+ Contributed by Bob Manson of Cygnus Support <manson@cygnus.com>
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 2, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+/* The primary opcode table is made up of the following: */
+struct ia64_main_table
+{
+ /* The entry in the string table that corresponds to the name of this
+ opcode. */
+ unsigned short name_index;
+
+ /* The type of opcode; corresponds to the TYPE field in
+ struct ia64_opcode. */
+ unsigned char opcode_type;
+
+ /* The number of outputs for this opcode. */
+ unsigned char num_outputs;
+
+ /* The base insn value for this opcode. It may be modified by completers. */
+ ia64_insn opcode;
+
+ /* The mask of valid bits in OPCODE. Zeros indicate operand fields. */
+ ia64_insn mask;
+
+ /* The operands of this instruction. Corresponds to the OPERANDS field
+ in struct ia64_opcode. */
+ unsigned char operands[5];
+
+ /* The flags for this instruction. Corresponds to the FLAGS field in
+ struct ia64_opcode. */
+ short flags;
+
+ /* The tree of completers for this instruction; this is an offset into
+ completer_table. */
+ short completers;
+};
+
+/* Each instruction has a set of possible "completers", or additional
+ suffixes that can alter the instruction's behavior, and which has
+ potentially different dependencies.
+
+ The completer entries modify certain bits in the instruction opcode.
+ Which bits are to be modified are marked by the BITS, MASK and
+ OFFSET fields. The completer entry may also note dependencies for the
+ opcode.
+
+ These completers are arranged in a DAG; the pointers are indexes
+ into the completer_table array. The completer DAG is searched by
+ find_completer () and ia64_find_matching_opcode ().
+
+ Note that each completer needs to be applied in turn, so that if we
+ have the instruction
+ cmp.lt.unc
+ the completer entries for both "lt" and "unc" would need to be applied
+ to the opcode's value.
+
+ Some instructions do not require any completers; these contain an
+ empty completer entry. Instructions that require a completer do
+ not contain an empty entry.
+
+ Terminal completers (those completers that validly complete an
+ instruction) are marked by having the TERMINAL_COMPLETER flag set.
+
+ Only dependencies listed in the terminal completer for an opcode are
+ considered to apply to that opcode instance. */
+
+struct ia64_completer_table
+{
+ /* The bit value that this completer sets. */
+ unsigned int bits;
+
+ /* And its mask. 1s are bits that are to be modified in the
+ instruction. */
+ unsigned int mask;
+
+ /* The entry in the string table that corresponds to the name of this
+ completer. */
+ unsigned short name_index;
+
+ /* An alternative completer, or -1 if this is the end of the chain. */
+ short alternative;
+
+ /* A pointer to the DAG of completers that can potentially follow
+ this one, or -1. */
+ short subentries;
+
+ /* The bit offset in the instruction where BITS and MASK should be
+ applied. */
+ unsigned char offset : 7;
+
+ unsigned char terminal_completer : 1;
+
+ /* Index into the dependency list table */
+ short dependencies;
+};
+
+/* This contains sufficient information for the disassembler to resolve
+ the complete name of the original instruction. */
+struct ia64_dis_names
+{
+ /* COMPLETER_INDEX represents the tree of completers that make up
+ the instruction. The LSB represents the top of the tree for the
+ specified instruction.
+
+ A 0 bit indicates to go to the next alternate completer via the
+ alternative field; a 1 bit indicates that the current completer
+ is part of the instruction, and to go down the subentries index.
+ We know we've reached the final completer when we run out of 1
+ bits.
+
+ There is always at least one 1 bit. */
+ unsigned int completer_index : 20;
+
+ /* The index in the main_table[] array for the instruction. */
+ unsigned short insn_index : 11;
+
+ /* If set, the next entry in this table is an alternate possibility
+ for this instruction encoding. Which one to use is determined by
+ the instruction type and other factors (see opcode_verify ()). */
+ unsigned int next_flag : 1;
+
+ /* The disassembly priority of this entry among instructions. */
+ unsigned short priority;
+};
+
+static const char * const ia64_strings[] = {
+ "", "0", "1", "a", "acq", "add", "addl", "addp4", "adds", "alloc", "and",
+ "andcm", "b", "bias", "br", "break", "brl", "brp", "bsw", "c", "call",
+ "cexit", "chk", "cloop", "clr", "clrrrb", "cmp", "cmp4", "cmp8xchg16",
+ "cmpxchg1", "cmpxchg2", "cmpxchg4", "cmpxchg8", "cond", "cover", "ctop",
+ "czx1", "czx2", "d", "dep", "dpnt", "dptk", "e", "epc", "eq", "excl",
+ "exit", "exp", "extr", "f", "fabs", "fadd", "famax", "famin", "fand",
+ "fandcm", "fault", "fc", "fchkf", "fclass", "fclrf", "fcmp", "fcvt",
+ "fetchadd4", "fetchadd8", "few", "fill", "flushrs", "fma", "fmax",
+ "fmerge", "fmin", "fmix", "fmpy", "fms", "fneg", "fnegabs", "fnma",
+ "fnmpy", "fnorm", "for", "fpabs", "fpack", "fpamax", "fpamin", "fpcmp",
+ "fpcvt", "fpma", "fpmax", "fpmerge", "fpmin", "fpmpy", "fpms", "fpneg",
+ "fpnegabs", "fpnma", "fpnmpy", "fprcpa", "fprsqrta", "frcpa", "frsqrta",
+ "fselect", "fsetc", "fsub", "fswap", "fsxt", "fwb", "fx", "fxor", "fxu",
+ "g", "ga", "ge", "getf", "geu", "gt", "gtu", "h", "hint", "hu", "i", "ia",
+ "imp", "invala", "itc", "itr", "l", "ld1", "ld16", "ld2", "ld4", "ld8",
+ "ldf", "ldf8", "ldfd", "ldfe", "ldfp8", "ldfpd", "ldfps", "ldfs", "le",
+ "leu", "lfetch", "loadrs", "loop", "lr", "lt", "ltu", "lu", "m", "many",
+ "mf", "mix1", "mix2", "mix4", "mov", "movl", "mux1", "mux2", "nc", "ne",
+ "neq", "nge", "ngt", "nl", "nle", "nlt", "nm", "nop", "nr", "ns", "nt1",
+ "nt2", "nta", "nz", "or", "orcm", "ord", "pack2", "pack4", "padd1",
+ "padd2", "padd4", "pavg1", "pavg2", "pavgsub1", "pavgsub2", "pcmp1",
+ "pcmp2", "pcmp4", "pmax1", "pmax2", "pmin1", "pmin2", "pmpy2", "pmpyshr2",
+ "popcnt", "pr", "probe", "psad1", "pshl2", "pshl4", "pshladd2", "pshr2",
+ "pshr4", "pshradd2", "psub1", "psub2", "psub4", "ptc", "ptr", "r", "raz",
+ "rel", "ret", "rfi", "rsm", "rum", "rw", "s", "s0", "s1", "s2", "s3",
+ "sa", "se", "setf", "shl", "shladd", "shladdp4", "shr", "shrp", "sig",
+ "spill", "spnt", "sptk", "srlz", "ssm", "sss", "st1", "st16", "st2",
+ "st4", "st8", "stf", "stf8", "stfd", "stfe", "stfs", "sub", "sum", "sxt1",
+ "sxt2", "sxt4", "sync", "tak", "tbit", "tf", "thash", "tnat", "tpa",
+ "trunc", "ttag", "u", "unc", "unord", "unpack1", "unpack2", "unpack4",
+ "uss", "uus", "uuu", "vmsw", "w", "wexit", "wtop", "x", "xchg1", "xchg2",
+ "xchg4", "xchg8", "xf", "xma", "xmpy", "xor", "xuf", "z", "zxt1", "zxt2",
+ "zxt4",
+};
+
+static const struct ia64_dependency
+dependencies[] = {
+ { "ALAT", 0, 0, 0, -1, NULL, },
+ { "AR[BSP]", 26, 0, 2, 17, NULL, },
+ { "AR[BSPSTORE]", 26, 0, 2, 18, NULL, },
+ { "AR[CCV]", 26, 0, 2, 32, NULL, },
+ { "AR[CFLG]", 26, 0, 2, 27, NULL, },
+ { "AR[CSD]", 26, 0, 2, 25, NULL, },
+ { "AR[EC]", 26, 0, 2, 66, NULL, },
+ { "AR[EFLAG]", 26, 0, 2, 24, NULL, },
+ { "AR[FCR]", 26, 0, 2, 21, NULL, },
+ { "AR[FDR]", 26, 0, 2, 30, NULL, },
+ { "AR[FIR]", 26, 0, 2, 29, NULL, },
+ { "AR[FPSR].sf0.controls", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf1.controls", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf2.controls", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf3.controls", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf0.flags", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf1.flags", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf2.flags", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].sf3.flags", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].traps", 30, 0, 2, -1, NULL, },
+ { "AR[FPSR].rv", 30, 0, 2, -1, NULL, },
+ { "AR[FSR]", 26, 0, 2, 28, NULL, },
+ { "AR[ITC]", 26, 0, 2, 44, NULL, },
+ { "AR[K%], % in 0 - 7", 1, 0, 2, -1, NULL, },
+ { "AR[LC]", 26, 0, 2, 65, NULL, },
+ { "AR[PFS]", 26, 0, 2, 64, NULL, },
+ { "AR[PFS]", 26, 0, 2, 64, NULL, },
+ { "AR[PFS]", 26, 0, 0, 64, NULL, },
+ { "AR[RNAT]", 26, 0, 2, 19, NULL, },
+ { "AR[RSC]", 26, 0, 2, 16, NULL, },
+ { "AR[SSD]", 26, 0, 2, 26, NULL, },
+ { "AR[UNAT]{%}, % in 0 - 63", 2, 0, 2, -1, NULL, },
+ { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 0, 0, -1, NULL, },
+ { "AR%, % in 48-63, 112-127", 4, 0, 2, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 0, 0, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, },
+ { "CFM", 6, 0, 2, -1, NULL, },
+ { "CFM", 6, 0, 2, -1, NULL, },
+ { "CFM", 6, 0, 2, -1, NULL, },
+ { "CFM", 6, 0, 2, -1, NULL, },
+ { "CFM", 6, 0, 0, -1, NULL, },
+ { "CPUID#", 7, 0, 5, -1, NULL, },
+ { "CR[CMCV]", 27, 0, 3, 74, NULL, },
+ { "CR[DCR]", 27, 0, 3, 0, NULL, },
+ { "CR[EOI]", 27, 0, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI Ã CR67)\" on page 2:119", },
+ { "CR[GPTA]", 27, 0, 3, 9, NULL, },
+ { "CR[IFA]", 27, 0, 1, 20, NULL, },
+ { "CR[IFA]", 27, 0, 3, 20, NULL, },
+ { "CR[IFS]", 27, 0, 3, 23, NULL, },
+ { "CR[IFS]", 27, 0, 1, 23, NULL, },
+ { "CR[IFS]", 27, 0, 1, 23, NULL, },
+ { "CR[IHA]", 27, 0, 3, 25, NULL, },
+ { "CR[IIM]", 27, 0, 3, 24, NULL, },
+ { "CR[IIP]", 27, 0, 3, 19, NULL, },
+ { "CR[IIP]", 27, 0, 1, 19, NULL, },
+ { "CR[IIPA]", 27, 0, 3, 22, NULL, },
+ { "CR[IPSR]", 27, 0, 3, 16, NULL, },
+ { "CR[IPSR]", 27, 0, 1, 16, NULL, },
+ { "CR[IRR%], % in 0 - 3", 8, 0, 3, -1, NULL, },
+ { "CR[ISR]", 27, 0, 3, 17, NULL, },
+ { "CR[ITIR]", 27, 0, 3, 21, NULL, },
+ { "CR[ITIR]", 27, 0, 1, 21, NULL, },
+ { "CR[ITM]", 27, 0, 3, 1, NULL, },
+ { "CR[ITV]", 27, 0, 3, 72, NULL, },
+ { "CR[IVA]", 27, 0, 4, 2, NULL, },
+ { "CR[IVR]", 27, 0, 7, 65, "SC Section 5.8.3.2, \"External Interrupt Vector Register (IVR Ã CR65)\" on page 2:118", },
+ { "CR[LID]", 27, 0, 7, 64, "SC Section 5.8.3.1, \"Local ID (LID Ã CR64)\" on page 2:117", },
+ { "CR[LRR%], % in 0 - 1", 9, 0, 3, -1, NULL, },
+ { "CR[PMV]", 27, 0, 3, 73, NULL, },
+ { "CR[PTA]", 27, 0, 3, 8, NULL, },
+ { "CR[TPR]", 27, 0, 3, 66, NULL, },
+ { "CR[TPR]", 27, 0, 7, 66, "SC Section 5.8.3.3, \"Task Priority Register (TPR Ã CR66)\" on page 2:119", },
+ { "CR[TPR]", 27, 0, 1, 66, NULL, },
+ { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 0, 0, -1, NULL, },
+ { "DBR#", 11, 0, 2, -1, NULL, },
+ { "DBR#", 11, 0, 3, -1, NULL, },
+ { "DTC", 0, 0, 3, -1, NULL, },
+ { "DTC", 0, 0, 2, -1, NULL, },
+ { "DTC", 0, 0, 0, -1, NULL, },
+ { "DTC", 0, 0, 2, -1, NULL, },
+ { "DTC_LIMIT*", 0, 0, 2, -1, NULL, },
+ { "DTR", 0, 0, 3, -1, NULL, },
+ { "DTR", 0, 0, 2, -1, NULL, },
+ { "DTR", 0, 0, 3, -1, NULL, },
+ { "DTR", 0, 0, 0, -1, NULL, },
+ { "DTR", 0, 0, 2, -1, NULL, },
+ { "FR%, % in 0 - 1", 12, 0, 0, -1, NULL, },
+ { "FR%, % in 2 - 127", 13, 0, 2, -1, NULL, },
+ { "FR%, % in 2 - 127", 13, 0, 0, -1, NULL, },
+ { "GR0", 14, 0, 0, -1, NULL, },
+ { "GR%, % in 1 - 127", 15, 0, 0, -1, NULL, },
+ { "GR%, % in 1 - 127", 15, 0, 2, -1, NULL, },
+ { "IBR#", 16, 0, 2, -1, NULL, },
+ { "InService*", 17, 0, 3, -1, NULL, },
+ { "InService*", 17, 0, 2, -1, NULL, },
+ { "InService*", 17, 0, 2, -1, NULL, },
+ { "IP", 0, 0, 0, -1, NULL, },
+ { "ITC", 0, 0, 4, -1, NULL, },
+ { "ITC", 0, 0, 2, -1, NULL, },
+ { "ITC", 0, 0, 0, -1, NULL, },
+ { "ITC", 0, 0, 4, -1, NULL, },
+ { "ITC", 0, 0, 2, -1, NULL, },
+ { "ITC_LIMIT*", 0, 0, 2, -1, NULL, },
+ { "ITR", 0, 0, 2, -1, NULL, },
+ { "ITR", 0, 0, 4, -1, NULL, },
+ { "ITR", 0, 0, 2, -1, NULL, },
+ { "ITR", 0, 0, 0, -1, NULL, },
+ { "ITR", 0, 0, 4, -1, NULL, },
+ { "memory", 0, 0, 0, -1, NULL, },
+ { "MSR#", 18, 0, 5, -1, NULL, },
+ { "PKR#", 19, 0, 3, -1, NULL, },
+ { "PKR#", 19, 0, 0, -1, NULL, },
+ { "PKR#", 19, 0, 2, -1, NULL, },
+ { "PKR#", 19, 0, 2, -1, NULL, },
+ { "PMC#", 20, 0, 2, -1, NULL, },
+ { "PMC#", 20, 0, 7, -1, "SC Section 7.2.1, \"Generic Performance Counter Registers\" for PMC[0].fr on page 2:150", },
+ { "PMD#", 21, 0, 2, -1, NULL, },
+ { "PR0", 0, 0, 0, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 0, 0, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 0, 0, -1, NULL, },
+ { "PR63", 24, 0, 2, -1, NULL, },
+ { "PR63", 24, 0, 2, -1, NULL, },
+ { "PR63", 24, 0, 0, -1, NULL, },
+ { "PSR.ac", 28, 0, 1, 3, NULL, },
+ { "PSR.ac", 28, 0, 3, 3, NULL, },
+ { "PSR.ac", 28, 0, 2, 3, NULL, },
+ { "PSR.ac", 28, 0, 2, 3, NULL, },
+ { "PSR.be", 28, 0, 1, 1, NULL, },
+ { "PSR.be", 28, 0, 3, 1, NULL, },
+ { "PSR.be", 28, 0, 2, 1, NULL, },
+ { "PSR.be", 28, 0, 2, 1, NULL, },
+ { "PSR.bn", 28, 0, 2, 44, NULL, },
+ { "PSR.cpl", 28, 0, 1, 32, NULL, },
+ { "PSR.cpl", 28, 0, 2, 32, NULL, },
+ { "PSR.da", 28, 0, 2, 38, NULL, },
+ { "PSR.db", 28, 0, 3, 24, NULL, },
+ { "PSR.db", 28, 0, 2, 24, NULL, },
+ { "PSR.db", 28, 0, 2, 24, NULL, },
+ { "PSR.dd", 28, 0, 2, 39, NULL, },
+ { "PSR.dfh", 28, 0, 3, 19, NULL, },
+ { "PSR.dfh", 28, 0, 2, 19, NULL, },
+ { "PSR.dfh", 28, 0, 2, 19, NULL, },
+ { "PSR.dfl", 28, 0, 3, 18, NULL, },
+ { "PSR.dfl", 28, 0, 2, 18, NULL, },
+ { "PSR.dfl", 28, 0, 2, 18, NULL, },
+ { "PSR.di", 28, 0, 3, 22, NULL, },
+ { "PSR.di", 28, 0, 2, 22, NULL, },
+ { "PSR.di", 28, 0, 2, 22, NULL, },
+ { "PSR.dt", 28, 0, 3, 17, NULL, },
+ { "PSR.dt", 28, 0, 2, 17, NULL, },
+ { "PSR.dt", 28, 0, 2, 17, NULL, },
+ { "PSR.ed", 28, 0, 2, 43, NULL, },
+ { "PSR.i", 28, 0, 2, 14, NULL, },
+ { "PSR.ia", 28, 0, 0, 14, NULL, },
+ { "PSR.ic", 28, 0, 2, 13, NULL, },
+ { "PSR.ic", 28, 0, 3, 13, NULL, },
+ { "PSR.ic", 28, 0, 2, 13, NULL, },
+ { "PSR.id", 28, 0, 0, 14, NULL, },
+ { "PSR.is", 28, 0, 0, 14, NULL, },
+ { "PSR.it", 28, 0, 2, 14, NULL, },
+ { "PSR.lp", 28, 0, 2, 25, NULL, },
+ { "PSR.lp", 28, 0, 3, 25, NULL, },
+ { "PSR.lp", 28, 0, 2, 25, NULL, },
+ { "PSR.mc", 28, 0, 2, 35, NULL, },
+ { "PSR.mfh", 28, 0, 2, 5, NULL, },
+ { "PSR.mfl", 28, 0, 2, 4, NULL, },
+ { "PSR.pk", 28, 0, 3, 15, NULL, },
+ { "PSR.pk", 28, 0, 2, 15, NULL, },
+ { "PSR.pk", 28, 0, 2, 15, NULL, },
+ { "PSR.pp", 28, 0, 2, 21, NULL, },
+ { "PSR.ri", 28, 0, 0, 41, NULL, },
+ { "PSR.rt", 28, 0, 2, 27, NULL, },
+ { "PSR.rt", 28, 0, 3, 27, NULL, },
+ { "PSR.rt", 28, 0, 2, 27, NULL, },
+ { "PSR.si", 28, 0, 2, 23, NULL, },
+ { "PSR.si", 28, 0, 3, 23, NULL, },
+ { "PSR.si", 28, 0, 2, 23, NULL, },
+ { "PSR.sp", 28, 0, 2, 20, NULL, },
+ { "PSR.sp", 28, 0, 3, 20, NULL, },
+ { "PSR.sp", 28, 0, 2, 20, NULL, },
+ { "PSR.ss", 28, 0, 2, 40, NULL, },
+ { "PSR.tb", 28, 0, 3, 26, NULL, },
+ { "PSR.tb", 28, 0, 2, 26, NULL, },
+ { "PSR.tb", 28, 0, 2, 26, NULL, },
+ { "PSR.up", 28, 0, 2, 2, NULL, },
+ { "PSR.vm", 28, 0, 1, 46, NULL, },
+ { "PSR.vm", 28, 0, 2, 46, NULL, },
+ { "RR#", 25, 0, 3, -1, NULL, },
+ { "RR#", 25, 0, 2, -1, NULL, },
+ { "RSE", 29, 0, 2, -1, NULL, },
+ { "ALAT", 0, 1, 0, -1, NULL, },
+ { "AR[BSP]", 26, 1, 2, 17, NULL, },
+ { "AR[BSPSTORE]", 26, 1, 2, 18, NULL, },
+ { "AR[CCV]", 26, 1, 2, 32, NULL, },
+ { "AR[CFLG]", 26, 1, 2, 27, NULL, },
+ { "AR[CSD]", 26, 1, 2, 25, NULL, },
+ { "AR[EC]", 26, 1, 2, 66, NULL, },
+ { "AR[EFLAG]", 26, 1, 2, 24, NULL, },
+ { "AR[FCR]", 26, 1, 2, 21, NULL, },
+ { "AR[FDR]", 26, 1, 2, 30, NULL, },
+ { "AR[FIR]", 26, 1, 2, 29, NULL, },
+ { "AR[FPSR].sf0.controls", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf1.controls", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf2.controls", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf3.controls", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf0.flags", 30, 1, 0, -1, NULL, },
+ { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf1.flags", 30, 1, 0, -1, NULL, },
+ { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf2.flags", 30, 1, 0, -1, NULL, },
+ { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf3.flags", 30, 1, 0, -1, NULL, },
+ { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].rv", 30, 1, 2, -1, NULL, },
+ { "AR[FPSR].traps", 30, 1, 2, -1, NULL, },
+ { "AR[FSR]", 26, 1, 2, 28, NULL, },
+ { "AR[ITC]", 26, 1, 2, 44, NULL, },
+ { "AR[K%], % in 0 - 7", 1, 1, 2, -1, NULL, },
+ { "AR[LC]", 26, 1, 2, 65, NULL, },
+ { "AR[PFS]", 26, 1, 0, 64, NULL, },
+ { "AR[PFS]", 26, 1, 2, 64, NULL, },
+ { "AR[PFS]", 26, 1, 2, 64, NULL, },
+ { "AR[RNAT]", 26, 1, 2, 19, NULL, },
+ { "AR[RSC]", 26, 1, 2, 16, NULL, },
+ { "AR[SSD]", 26, 1, 2, 26, NULL, },
+ { "AR[UNAT]{%}, % in 0 - 63", 2, 1, 2, -1, NULL, },
+ { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 1, 0, -1, NULL, },
+ { "AR%, % in 48 - 63, 112-127", 4, 1, 2, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, },
+ { "BR%, % in 0 - 7", 5, 1, 0, -1, NULL, },
+ { "CFM", 6, 1, 2, -1, NULL, },
+ { "CPUID#", 7, 1, 0, -1, NULL, },
+ { "CR[CMCV]", 27, 1, 2, 74, NULL, },
+ { "CR[DCR]", 27, 1, 2, 0, NULL, },
+ { "CR[EOI]", 27, 1, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI Ã CR67)\" on page 2:119", },
+ { "CR[GPTA]", 27, 1, 2, 9, NULL, },
+ { "CR[IFA]", 27, 1, 2, 20, NULL, },
+ { "CR[IFS]", 27, 1, 2, 23, NULL, },
+ { "CR[IHA]", 27, 1, 2, 25, NULL, },
+ { "CR[IIM]", 27, 1, 2, 24, NULL, },
+ { "CR[IIP]", 27, 1, 2, 19, NULL, },
+ { "CR[IIPA]", 27, 1, 2, 22, NULL, },
+ { "CR[IPSR]", 27, 1, 2, 16, NULL, },
+ { "CR[IRR%], % in 0 - 3", 8, 1, 2, -1, NULL, },
+ { "CR[ISR]", 27, 1, 2, 17, NULL, },
+ { "CR[ITIR]", 27, 1, 2, 21, NULL, },
+ { "CR[ITM]", 27, 1, 2, 1, NULL, },
+ { "CR[ITV]", 27, 1, 2, 72, NULL, },
+ { "CR[IVA]", 27, 1, 2, 2, NULL, },
+ { "CR[IVR]", 27, 1, 7, 65, "SC", },
+ { "CR[LID]", 27, 1, 7, 64, "SC", },
+ { "CR[LRR%], % in 0 - 1", 9, 1, 2, -1, NULL, },
+ { "CR[PMV]", 27, 1, 2, 73, NULL, },
+ { "CR[PTA]", 27, 1, 2, 8, NULL, },
+ { "CR[TPR]", 27, 1, 2, 66, NULL, },
+ { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 1, 0, -1, NULL, },
+ { "DBR#", 11, 1, 2, -1, NULL, },
+ { "DTC", 0, 1, 0, -1, NULL, },
+ { "DTC", 0, 1, 2, -1, NULL, },
+ { "DTC", 0, 1, 2, -1, NULL, },
+ { "DTC_LIMIT*", 0, 1, 2, -1, NULL, },
+ { "DTR", 0, 1, 2, -1, NULL, },
+ { "DTR", 0, 1, 2, -1, NULL, },
+ { "DTR", 0, 1, 2, -1, NULL, },
+ { "DTR", 0, 1, 0, -1, NULL, },
+ { "FR%, % in 0 - 1", 12, 1, 0, -1, NULL, },
+ { "FR%, % in 2 - 127", 13, 1, 2, -1, NULL, },
+ { "GR0", 14, 1, 0, -1, NULL, },
+ { "GR%, % in 1 - 127", 15, 1, 2, -1, NULL, },
+ { "IBR#", 16, 1, 2, -1, NULL, },
+ { "InService*", 17, 1, 7, -1, "SC", },
+ { "IP", 0, 1, 0, -1, NULL, },
+ { "ITC", 0, 1, 0, -1, NULL, },
+ { "ITC", 0, 1, 2, -1, NULL, },
+ { "ITC", 0, 1, 2, -1, NULL, },
+ { "ITR", 0, 1, 2, -1, NULL, },
+ { "ITR", 0, 1, 2, -1, NULL, },
+ { "ITR", 0, 1, 0, -1, NULL, },
+ { "memory", 0, 1, 0, -1, NULL, },
+ { "MSR#", 18, 1, 7, -1, "SC", },
+ { "PKR#", 19, 1, 0, -1, NULL, },
+ { "PKR#", 19, 1, 0, -1, NULL, },
+ { "PKR#", 19, 1, 2, -1, NULL, },
+ { "PMC#", 20, 1, 2, -1, NULL, },
+ { "PMD#", 21, 1, 2, -1, NULL, },
+ { "PR0", 0, 1, 0, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, },
+ { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, },
+ { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, },
+ { "PR63", 24, 1, 0, -1, NULL, },
+ { "PR63", 24, 1, 0, -1, NULL, },
+ { "PR63", 24, 1, 2, -1, NULL, },
+ { "PR63", 24, 1, 2, -1, NULL, },
+ { "PSR.ac", 28, 1, 2, 3, NULL, },
+ { "PSR.be", 28, 1, 2, 1, NULL, },
+ { "PSR.bn", 28, 1, 2, 44, NULL, },
+ { "PSR.cpl", 28, 1, 2, 32, NULL, },
+ { "PSR.da", 28, 1, 2, 38, NULL, },
+ { "PSR.db", 28, 1, 2, 24, NULL, },
+ { "PSR.dd", 28, 1, 2, 39, NULL, },
+ { "PSR.dfh", 28, 1, 2, 19, NULL, },
+ { "PSR.dfl", 28, 1, 2, 18, NULL, },
+ { "PSR.di", 28, 1, 2, 22, NULL, },
+ { "PSR.dt", 28, 1, 2, 17, NULL, },
+ { "PSR.ed", 28, 1, 2, 43, NULL, },
+ { "PSR.i", 28, 1, 2, 14, NULL, },
+ { "PSR.ia", 28, 1, 2, 14, NULL, },
+ { "PSR.ic", 28, 1, 2, 13, NULL, },
+ { "PSR.id", 28, 1, 2, 14, NULL, },
+ { "PSR.is", 28, 1, 2, 14, NULL, },
+ { "PSR.it", 28, 1, 2, 14, NULL, },
+ { "PSR.lp", 28, 1, 2, 25, NULL, },
+ { "PSR.mc", 28, 1, 2, 35, NULL, },
+ { "PSR.mfh", 28, 1, 0, 5, NULL, },
+ { "PSR.mfh", 28, 1, 2, 5, NULL, },
+ { "PSR.mfh", 28, 1, 2, 5, NULL, },
+ { "PSR.mfl", 28, 1, 0, 4, NULL, },
+ { "PSR.mfl", 28, 1, 2, 4, NULL, },
+ { "PSR.mfl", 28, 1, 2, 4, NULL, },
+ { "PSR.pk", 28, 1, 2, 15, NULL, },
+ { "PSR.pp", 28, 1, 2, 21, NULL, },
+ { "PSR.ri", 28, 1, 2, 41, NULL, },
+ { "PSR.rt", 28, 1, 2, 27, NULL, },
+ { "PSR.si", 28, 1, 2, 23, NULL, },
+ { "PSR.sp", 28, 1, 2, 20, NULL, },
+ { "PSR.ss", 28, 1, 2, 40, NULL, },
+ { "PSR.tb", 28, 1, 2, 26, NULL, },
+ { "PSR.up", 28, 1, 2, 2, NULL, },
+ { "PSR.vm", 28, 1, 2, 46, NULL, },
+ { "RR#", 25, 1, 2, -1, NULL, },
+ { "RSE", 29, 1, 2, -1, NULL, },
+ { "PR63", 24, 2, 6, -1, NULL, },
+};
+
+static const unsigned short dep0[] = {
+ 97, 282, 2140, 2327,
+};
+
+static const unsigned short dep1[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2327, 4135, 20616,
+};
+
+static const unsigned short dep2[] = {
+ 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2347, 2348, 2351,
+ 2352, 2355, 2356,
+};
+
+static const unsigned short dep3[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 20616,
+};
+
+static const unsigned short dep4[] = {
+ 97, 282, 22646, 22647, 22649, 22650, 22652, 22653, 22655, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep5[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 4135, 20616, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep6[] = {
+ 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2347, 2349,
+ 2351, 2353, 2355,
+};
+
+static const unsigned short dep7[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2344, 2345, 2348, 2349, 2352, 2353, 2356, 4135, 20616,
+};
+
+static const unsigned short dep8[] = {
+ 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2346, 2348, 2350,
+ 2352, 2354, 2356,
+};
+
+static const unsigned short dep9[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2344, 2346, 2347, 2350, 2351, 2354, 2355, 4135, 20616,
+};
+
+static const unsigned short dep10[] = {
+ 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2346, 2347,
+ 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356,
+};
+
+static const unsigned short dep11[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356,
+ 4135, 20616,
+};
+
+static const unsigned short dep12[] = {
+ 97, 282, 2395,
+};
+
+static const unsigned short dep13[] = {
+ 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2082, 2083, 2166, 2168,
+ 2169, 2171, 2172, 2174, 2175, 4135,
+};
+
+static const unsigned short dep14[] = {
+ 97, 163, 282, 325, 2395, 28866, 29018,
+};
+
+static const unsigned short dep15[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 28, 29, 30, 31, 32, 33, 40, 41, 97, 150, 152, 158, 162,
+ 164, 175, 185, 186, 188, 282, 325, 2082, 2083, 2166, 2168, 2169, 2171, 2172,
+ 2174, 2175, 4135, 28866, 29018,
+};
+
+static const unsigned short dep16[] = {
+ 1, 6, 40, 97, 137, 196, 201, 241, 282, 312, 2395, 28866, 29018,
+};
+
+static const unsigned short dep17[] = {
+ 1, 25, 27, 38, 40, 41, 97, 158, 162, 164, 166, 167, 175, 185, 186, 188, 196,
+ 201, 241, 282, 312, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175,
+ 4135, 28866, 29018,
+};
+
+static const unsigned short dep18[] = {
+ 1, 40, 51, 97, 196, 241, 248, 282, 28866, 29018,
+};
+
+static const unsigned short dep19[] = {
+ 1, 38, 40, 41, 97, 158, 160, 161, 162, 175, 185, 190, 191, 196, 241, 248,
+ 282, 4135, 28866, 29018,
+};
+
+static const unsigned short dep20[] = {
+ 40, 97, 241, 282,
+};
+
+static const unsigned short dep21[] = {
+ 97, 158, 162, 175, 185, 241, 282,
+};
+
+static const unsigned short dep22[] = {
+ 1, 40, 97, 131, 135, 136, 138, 139, 142, 143, 146, 149, 152, 155, 156, 157,
+ 158, 161, 162, 163, 164, 167, 168, 169, 170, 173, 174, 175, 178, 181, 184,
+ 185, 188, 189, 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316,
+ 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333,
+ 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 28866, 29018,
+};
+
+static const unsigned short dep23[] = {
+ 1, 38, 40, 41, 50, 51, 55, 58, 73, 97, 137, 138, 158, 162, 175, 185, 190,
+ 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
+ 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, 334, 335, 336,
+ 337, 338, 339, 340, 341, 342, 343, 344, 4135, 28866, 29018,
+};
+
+static const unsigned short dep24[] = {
+ 97, 136, 282, 311,
+};
+
+static const unsigned short dep25[] = {
+ 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 311,
+};
+
+static const unsigned short dep26[] = {
+ 97, 137, 282, 312,
+};
+
+static const unsigned short dep27[] = {
+ 25, 26, 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 312,
+
+};
+
+static const unsigned short dep28[] = {
+ 97, 190, 282, 344,
+};
+
+static const unsigned short dep29[] = {
+ 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 344,
+};
+
+static const unsigned short dep30[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175,
+ 4135,
+};
+
+static const unsigned short dep31[] = {
+ 1, 25, 40, 97, 196, 228, 229, 241, 282, 2082, 2285, 2288, 2395, 28866, 29018,
+
+};
+
+static const unsigned short dep32[] = {
+ 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228,
+ 230, 241, 282, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286,
+ 2288, 4135, 28866, 29018,
+};
+
+static const unsigned short dep33[] = {
+ 97, 282,
+};
+
+static const unsigned short dep34[] = {
+ 97, 158, 162, 175, 185, 282, 2082, 2084,
+};
+
+static const unsigned short dep35[] = {
+ 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2168, 2169, 2171,
+ 2172, 2174, 2175, 4135,
+};
+
+static const unsigned short dep36[] = {
+ 6, 37, 38, 39, 97, 125, 126, 201, 241, 282, 307, 308, 2395,
+};
+
+static const unsigned short dep37[] = {
+ 6, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 241, 282, 307,
+ 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135,
+};
+
+static const unsigned short dep38[] = {
+ 24, 97, 227, 282, 2395,
+};
+
+static const unsigned short dep39[] = {
+ 24, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 227, 282, 2166, 2168, 2169,
+ 2171, 2172, 2174, 2175, 4135,
+};
+
+static const unsigned short dep40[] = {
+ 6, 24, 37, 38, 39, 97, 125, 126, 201, 227, 241, 282, 307, 308, 2395,
+};
+
+static const unsigned short dep41[] = {
+ 6, 24, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 227, 241, 282,
+ 307, 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135,
+};
+
+static const unsigned short dep42[] = {
+ 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228,
+ 230, 241, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, 2288, 4135,
+ 28866, 29018,
+};
+
+static const unsigned short dep43[] = {
+ 97, 158, 162, 175, 185, 282,
+};
+
+static const unsigned short dep44[] = {
+ 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+ 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831,
+ 22832, 22835, 22836,
+};
+
+static const unsigned short dep45[] = {
+ 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep46[] = {
+ 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325,
+ 18601, 18602, 18761, 18762, 18764, 18765, 22646, 22647, 22648, 22650, 22651,
+ 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep47[] = {
+ 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215,
+ 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135,
+ 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep48[] = {
+ 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+ 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831,
+ 22832, 22835, 22836,
+};
+
+static const unsigned short dep49[] = {
+ 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep50[] = {
+ 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+ 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831,
+ 22832, 22835, 22836,
+};
+
+static const unsigned short dep51[] = {
+ 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep52[] = {
+ 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+ 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831,
+ 22832, 22835, 22836,
+};
+
+static const unsigned short dep53[] = {
+ 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep54[] = {
+ 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+
+};
+
+static const unsigned short dep55[] = {
+ 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766,
+};
+
+static const unsigned short dep56[] = {
+ 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325,
+ 18601, 18602, 18761, 18762, 18764, 18765,
+};
+
+static const unsigned short dep57[] = {
+ 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215,
+ 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135,
+ 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766,
+};
+
+static const unsigned short dep58[] = {
+ 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+
+};
+
+static const unsigned short dep59[] = {
+ 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766,
+};
+
+static const unsigned short dep60[] = {
+ 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+
+};
+
+static const unsigned short dep61[] = {
+ 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766,
+};
+
+static const unsigned short dep62[] = {
+ 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+
+};
+
+static const unsigned short dep63[] = {
+ 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763,
+ 18764, 18766,
+};
+
+static const unsigned short dep64[] = {
+ 97, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+};
+
+static const unsigned short dep65[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173,
+ 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766,
+};
+
+static const unsigned short dep66[] = {
+ 11, 97, 206, 282,
+};
+
+static const unsigned short dep67[] = {
+ 11, 40, 41, 97, 158, 162, 175, 185, 206, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep68[] = {
+ 11, 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep69[] = {
+ 12, 97, 207, 282,
+};
+
+static const unsigned short dep70[] = {
+ 11, 40, 41, 97, 158, 162, 175, 185, 207, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep71[] = {
+ 13, 97, 208, 282,
+};
+
+static const unsigned short dep72[] = {
+ 11, 40, 41, 97, 158, 162, 175, 185, 208, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep73[] = {
+ 14, 97, 209, 282,
+};
+
+static const unsigned short dep74[] = {
+ 11, 40, 41, 97, 158, 162, 175, 185, 209, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep75[] = {
+ 15, 97, 211, 212, 282,
+};
+
+static const unsigned short dep76[] = {
+ 40, 41, 97, 158, 162, 175, 185, 211, 212, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep77[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep78[] = {
+ 16, 97, 214, 215, 282,
+};
+
+static const unsigned short dep79[] = {
+ 40, 41, 97, 158, 162, 175, 185, 214, 215, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep80[] = {
+ 17, 97, 217, 218, 282,
+};
+
+static const unsigned short dep81[] = {
+ 40, 41, 97, 158, 162, 175, 185, 217, 218, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep82[] = {
+ 18, 97, 220, 221, 282,
+};
+
+static const unsigned short dep83[] = {
+ 40, 41, 97, 158, 162, 175, 185, 220, 221, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep84[] = {
+ 15, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167,
+ 2170, 2173, 4135,
+};
+
+static const unsigned short dep85[] = {
+ 15, 16, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166,
+ 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep86[] = {
+ 15, 17, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166,
+ 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep87[] = {
+ 15, 18, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166,
+ 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep88[] = {
+ 15, 97, 210, 211, 282,
+};
+
+static const unsigned short dep89[] = {
+ 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2166, 2167, 2170,
+ 2173, 4135,
+};
+
+static const unsigned short dep90[] = {
+ 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282,
+};
+
+static const unsigned short dep91[] = {
+ 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215,
+ 216, 218, 219, 221, 282, 2166, 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep92[] = {
+ 16, 97, 213, 214, 282,
+};
+
+static const unsigned short dep93[] = {
+ 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2166, 2167, 2170,
+ 2173, 4135,
+};
+
+static const unsigned short dep94[] = {
+ 17, 97, 216, 217, 282,
+};
+
+static const unsigned short dep95[] = {
+ 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2166, 2167, 2170,
+ 2173, 4135,
+};
+
+static const unsigned short dep96[] = {
+ 18, 97, 219, 220, 282,
+};
+
+static const unsigned short dep97[] = {
+ 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2166, 2167, 2170,
+ 2173, 4135,
+};
+
+static const unsigned short dep98[] = {
+ 15, 97, 210, 211, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347,
+ 2348, 2351, 2352, 2355, 2356,
+};
+
+static const unsigned short dep99[] = {
+ 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528,
+ 16530, 16531, 16533,
+};
+
+static const unsigned short dep100[] = {
+ 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2166, 2167,
+ 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, 2352, 2355, 2356,
+};
+
+static const unsigned short dep101[] = {
+ 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215,
+ 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2344, 2347,
+ 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533,
+};
+
+static const unsigned short dep102[] = {
+ 16, 97, 213, 214, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347,
+ 2348, 2351, 2352, 2355, 2356,
+};
+
+static const unsigned short dep103[] = {
+ 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528,
+ 16530, 16531, 16533,
+};
+
+static const unsigned short dep104[] = {
+ 17, 97, 216, 217, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347,
+ 2348, 2351, 2352, 2355, 2356,
+};
+
+static const unsigned short dep105[] = {
+ 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528,
+ 16530, 16531, 16533,
+};
+
+static const unsigned short dep106[] = {
+ 18, 97, 219, 220, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347,
+ 2348, 2351, 2352, 2355, 2356,
+};
+
+static const unsigned short dep107[] = {
+ 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528,
+ 16530, 16531, 16533,
+};
+
+static const unsigned short dep108[] = {
+ 15, 97, 210, 211, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824,
+ 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep109[] = {
+ 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep110[] = {
+ 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 22646, 22647,
+ 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835,
+ 22836,
+};
+
+static const unsigned short dep111[] = {
+ 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215,
+ 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 4135, 16528,
+ 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep112[] = {
+ 16, 97, 213, 214, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824,
+ 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep113[] = {
+ 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep114[] = {
+ 17, 97, 216, 217, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824,
+ 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep115[] = {
+ 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep116[] = {
+ 18, 97, 219, 220, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824,
+ 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep117[] = {
+ 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137,
+ 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep118[] = {
+ 97, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351,
+ 2352, 2355, 2356,
+};
+
+static const unsigned short dep119[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173,
+ 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533,
+
+};
+
+static const unsigned short dep120[] = {
+ 97, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828,
+ 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep121[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173,
+ 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835,
+ 22836,
+};
+
+static const unsigned short dep122[] = {
+ 19, 20, 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167,
+ 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766,
+
+};
+
+static const unsigned short dep123[] = {
+ 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2138, 2139, 2140, 2166,
+ 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep124[] = {
+ 97, 282, 2083, 2084, 2286, 2287,
+};
+
+static const unsigned short dep125[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2285, 2287, 4135, 20616,
+};
+
+static const unsigned short dep126[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2082, 2084, 2166, 2167, 2170, 2173, 2327,
+ 4135, 20616,
+};
+
+static const unsigned short dep127[] = {
+ 97, 282, 14455, 14457, 14458, 14460, 14461, 14463, 14635, 14636, 14639, 14640,
+ 14643, 14644,
+};
+
+static const unsigned short dep128[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 4135, 14635, 14636,
+ 14639, 14640, 14643, 14644, 20616, 24694, 24695, 24698, 24701,
+};
+
+static const unsigned short dep129[] = {
+ 97, 122, 124, 125, 127, 282, 303, 304, 307, 308,
+};
+
+static const unsigned short dep130[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 303, 304, 307, 308, 4135, 24694, 24695,
+ 24698, 24701,
+};
+
+static const unsigned short dep131[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+
+};
+
+static const unsigned short dep132[] = {
+ 40, 41, 97, 119, 122, 125, 158, 162, 175, 185, 282, 2327, 4135, 20616, 24694,
+
+};
+
+static const unsigned short dep133[] = {
+ 6, 24, 26, 27, 97, 201, 227, 230, 282, 2081, 2284,
+};
+
+static const unsigned short dep134[] = {
+ 40, 41, 97, 158, 162, 175, 185, 201, 227, 229, 282, 2138, 2139, 2140, 2166,
+ 2167, 2170, 2173, 2284, 4135, 20616,
+};
+
+static const unsigned short dep135[] = {
+ 6, 24, 25, 26, 40, 41, 97, 158, 162, 175, 185, 282, 2081, 2166, 2167, 2170,
+ 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep136[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2347, 2348,
+ 2351, 2352, 2355, 2356, 4135,
+};
+
+static const unsigned short dep137[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, 22824,
+ 22827, 22828, 22831, 22832, 22835, 22836,
+};
+
+static const unsigned short dep138[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2348,
+ 2349, 2352, 2353, 2356, 4135,
+};
+
+static const unsigned short dep139[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2346, 2347,
+ 2350, 2351, 2354, 2355, 4135,
+};
+
+static const unsigned short dep140[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2346,
+ 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 4135,
+};
+
+static const unsigned short dep141[] = {
+ 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, 2170, 2173,
+ 4135,
+};
+
+static const unsigned short dep142[] = {
+ 0, 97, 195, 282,
+};
+
+static const unsigned short dep143[] = {
+ 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 195, 282, 2166, 2167, 2170,
+ 2173, 4135,
+};
+
+static const unsigned short dep144[] = {
+ 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep145[] = {
+ 2, 28, 97, 197, 231, 282, 28866, 29018,
+};
+
+static const unsigned short dep146[] = {
+ 1, 2, 28, 29, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 28866, 29018,
+
+};
+
+static const unsigned short dep147[] = {
+ 1, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 4135,
+ 28866, 29018,
+};
+
+static const unsigned short dep148[] = {
+ 0, 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135,
+
+};
+
+static const unsigned short dep149[] = {
+ 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 202, 203, 204, 205, 206, 207,
+ 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, 231,
+ 232, 233, 234, 282, 2071, 2081, 2274, 2284, 28866, 29018,
+};
+
+static const unsigned short dep150[] = {
+ 29, 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 196, 197, 198, 199,
+ 200, 202, 203, 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218,
+ 220, 221, 222, 223, 224, 225, 231, 232, 233, 234, 282, 2138, 2139, 2140, 2166,
+ 2167, 2170, 2173, 2274, 2284, 4135, 20616, 28866, 29018,
+};
+
+static const unsigned short dep151[] = {
+ 97, 282, 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666,
+ 14667, 14669, 14670, 14679,
+};
+
+static const unsigned short dep152[] = {
+ 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 2166, 2167, 2170, 2173, 4135,
+ 14645, 14646, 14666, 14667, 14669, 14670, 14679,
+};
+
+static const unsigned short dep153[] = {
+ 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, 14667,
+ 14669, 14670, 14679,
+};
+
+static const unsigned short dep154[] = {
+ 183, 184, 14645, 14646, 14666, 14667, 14669, 14670, 14679,
+};
+
+static const unsigned short dep155[] = {
+ 97, 282, 14465, 14466, 14469, 14470, 14480, 14481, 14483, 14484, 14486, 14487,
+ 14489, 14490, 14493, 14495, 14496, 14505, 14506, 14507, 14508, 14510, 14515,
+ 14516, 14518, 14519, 14525, 14645, 14646, 14652, 14653, 14654, 14655, 14657,
+ 14659, 14666, 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679,
+};
+
+static const unsigned short dep156[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170,
+ 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666,
+ 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, 34888,
+};
+
+static const unsigned short dep157[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170,
+ 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666,
+ 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679,
+};
+
+static const unsigned short dep158[] = {
+ 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, 181, 185, 190, 191,
+ 282, 2071, 2081, 2166, 2167, 2170, 2173, 2327, 4135, 20616, 28866,
+};
+
+static const unsigned short dep159[] = {
+ 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63,
+ 64, 65, 67, 69, 70, 71, 72, 73, 94, 96, 97, 243, 244, 245, 246, 247, 248,
+ 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, 263, 264, 265, 281,
+ 282, 2116, 2310,
+};
+
+static const unsigned short dep160[] = {
+ 40, 41, 96, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, 243, 244,
+ 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261,
+ 263, 264, 265, 281, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2310, 4135,
+ 20616,
+};
+
+static const unsigned short dep161[] = {
+ 59, 95, 97, 254, 281, 282, 2140, 2327,
+};
+
+static const unsigned short dep162[] = {
+ 40, 41, 43, 44, 46, 48, 49, 51, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 66,
+ 67, 69, 70, 71, 94, 95, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191,
+ 254, 281, 282, 2107, 2116, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep163[] = {
+ 2, 28, 41, 97, 197, 231, 241, 282, 2140, 2327, 28866, 29018,
+};
+
+static const unsigned short dep164[] = {
+ 2, 25, 26, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231,
+ 241, 282, 2327, 4135, 20616, 28866, 29018,
+};
+
+static const unsigned short dep165[] = {
+ 97, 129, 130, 133, 134, 140, 141, 144, 145, 147, 148, 150, 151, 153, 154,
+ 157, 159, 160, 165, 166, 169, 170, 171, 172, 174, 176, 177, 179, 180, 182,
+ 183, 186, 187, 189, 282, 309, 310, 314, 316, 317, 318, 319, 321, 323, 327,
+ 330, 331, 333, 334, 335, 336, 338, 339, 340, 342, 343,
+};
+
+static const unsigned short dep166[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 309, 310, 314, 316,
+ 317, 318, 319, 321, 323, 327, 330, 331, 333, 334, 335, 336, 338, 339, 340,
+ 342, 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, 34888,
+};
+
+static const unsigned short dep167[] = {
+ 97, 128, 130, 132, 134, 169, 170, 189, 282, 309, 310, 330, 331, 333, 334,
+ 343,
+};
+
+static const unsigned short dep168[] = {
+ 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 309, 310, 330, 331, 333, 334,
+ 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep169[] = {
+ 40, 41, 97, 130, 131, 134, 135, 137, 138, 141, 142, 145, 146, 148, 149, 151,
+ 152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 167, 168, 169, 170, 172,
+ 173, 174, 175, 176, 178, 179, 181, 182, 184, 185, 187, 188, 189, 190, 191,
+ 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep170[] = {
+ 40, 41, 97, 130, 131, 134, 135, 158, 162, 169, 170, 175, 185, 189, 282, 2166,
+ 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep171[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 153, 155, 158, 162, 171, 173,
+ 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep172[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 139, 140, 142, 143, 153, 155,
+ 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170,
+ 2173, 4135, 20616,
+};
+
+static const unsigned short dep173[] = {
+ 77, 78, 97, 101, 102, 269, 270, 282, 284, 285,
+};
+
+static const unsigned short dep174[] = {
+ 40, 41, 47, 62, 78, 80, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, 175,
+ 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, 2167,
+ 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep175[] = {
+ 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162,
+ 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166,
+ 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep176[] = {
+ 97, 282, 12480, 12481, 12633,
+};
+
+static const unsigned short dep177[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 12633, 20616,
+};
+
+static const unsigned short dep178[] = {
+ 97, 282, 6219, 6220, 6411,
+};
+
+static const unsigned short dep179[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 6411, 20616,
+};
+
+static const unsigned short dep180[] = {
+ 97, 282, 6237, 6424,
+};
+
+static const unsigned short dep181[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 6424, 20616,
+};
+
+static const unsigned short dep182[] = {
+ 97, 282, 6255, 6256, 6257, 6258, 6435, 6437, 8484,
+};
+
+static const unsigned short dep183[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 6258, 6436, 6437, 8304, 8483, 20616,
+};
+
+static const unsigned short dep184[] = {
+ 97, 282, 6259, 6260, 6438,
+};
+
+static const unsigned short dep185[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 6438, 20616,
+};
+
+static const unsigned short dep186[] = {
+ 97, 282, 6261, 6439,
+};
+
+static const unsigned short dep187[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 6439, 20616,
+};
+
+static const unsigned short dep188[] = {
+ 97, 282, 10350, 10530,
+};
+
+static const unsigned short dep189[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 10530, 20616,
+};
+
+static const unsigned short dep190[] = {
+ 77, 78, 82, 83, 97, 101, 102, 269, 270, 272, 273, 282, 284, 285,
+};
+
+static const unsigned short dep191[] = {
+ 40, 41, 47, 62, 78, 80, 83, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162,
+ 175, 185, 190, 191, 192, 269, 270, 272, 274, 282, 284, 285, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep192[] = {
+ 77, 78, 97, 101, 102, 104, 105, 269, 270, 282, 284, 285, 286, 287,
+};
+
+static const unsigned short dep193[] = {
+ 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162,
+ 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 286, 287, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep194[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 12481, 20616,
+};
+
+static const unsigned short dep195[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 6219, 20616,
+};
+
+static const unsigned short dep196[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 6237, 20616,
+};
+
+static const unsigned short dep197[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 6257, 8303, 20616,
+};
+
+static const unsigned short dep198[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 6259, 20616,
+};
+
+static const unsigned short dep199[] = {
+ 40, 41, 97, 137, 138, 158, 162, 175, 183, 184, 185, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 6260, 6261, 20616,
+};
+
+static const unsigned short dep200[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2327, 4135, 10350, 20616,
+};
+
+static const unsigned short dep201[] = {
+ 40, 41, 97, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, 2166, 2167,
+ 2170, 2173, 2327, 4135, 6186, 20616,
+};
+
+static const unsigned short dep202[] = {
+ 77, 79, 80, 97, 98, 99, 100, 268, 269, 282, 283, 284,
+};
+
+static const unsigned short dep203[] = {
+ 40, 41, 78, 79, 83, 85, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185,
+ 190, 191, 192, 268, 270, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170,
+ 2173, 4135, 20616,
+};
+
+static const unsigned short dep204[] = {
+ 77, 79, 80, 81, 97, 98, 99, 100, 103, 268, 269, 271, 282, 283, 284,
+};
+
+static const unsigned short dep205[] = {
+ 40, 41, 78, 79, 81, 83, 85, 97, 100, 102, 103, 104, 107, 137, 138, 158, 162,
+ 175, 185, 190, 191, 192, 268, 270, 271, 282, 283, 285, 2138, 2139, 2140, 2166,
+ 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep206[] = {
+ 77, 79, 80, 84, 85, 86, 97, 98, 99, 100, 268, 269, 274, 275, 282, 283, 284,
+
+};
+
+static const unsigned short dep207[] = {
+ 40, 41, 78, 79, 83, 85, 97, 100, 102, 137, 138, 158, 162, 175, 185, 190, 191,
+ 192, 268, 270, 273, 275, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170,
+ 2173, 4135, 20616,
+};
+
+static const unsigned short dep208[] = {
+ 77, 79, 80, 97, 98, 99, 100, 106, 107, 108, 268, 269, 282, 283, 284, 287,
+ 288,
+};
+
+static const unsigned short dep209[] = {
+ 40, 41, 78, 79, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, 190,
+ 191, 192, 268, 270, 282, 283, 285, 286, 288, 2138, 2139, 2140, 2166, 2167,
+ 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep210[] = {
+ 40, 41, 46, 70, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140,
+ 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep211[] = {
+ 40, 41, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, 2166,
+ 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep212[] = {
+ 40, 41, 70, 77, 82, 84, 97, 137, 138, 153, 155, 158, 162, 175, 185, 190, 191,
+ 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep213[] = {
+ 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2135, 2136, 2137, 2138,
+ 2139, 2140, 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 20616,
+
+};
+
+static const unsigned short dep214[] = {
+ 40, 41, 70, 77, 82, 84, 97, 153, 155, 158, 162, 175, 185, 192, 282, 2138,
+ 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep215[] = {
+ 40, 41, 78, 79, 97, 100, 137, 138, 158, 162, 175, 185, 190, 191, 268, 270,
+ 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616,
+};
+
+static const unsigned short dep216[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep217[] = {
+ 5, 97, 200, 282, 2140, 2327,
+};
+
+static const unsigned short dep218[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 200, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+
+};
+
+static const unsigned short dep219[] = {
+ 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185,
+ 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep220[] = {
+ 0, 97, 195, 282, 2140, 2327,
+};
+
+static const unsigned short dep221[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190,
+ 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep222[] = {
+ 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133,
+ 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175,
+ 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327,
+ 4135, 20616,
+};
+
+static const unsigned short dep223[] = {
+ 31, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190,
+ 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616,
+
+};
+
+static const unsigned short dep224[] = {
+ 0, 97, 195, 282, 2327, 26715,
+};
+
+static const unsigned short dep225[] = {
+ 0, 97, 109, 195, 282, 289,
+};
+
+static const unsigned short dep226[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616,
+
+};
+
+static const unsigned short dep227[] = {
+ 0, 5, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616,
+
+};
+
+static const unsigned short dep228[] = {
+ 0, 31, 97, 109, 195, 234, 282, 289,
+};
+
+static const unsigned short dep229[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616,
+
+};
+
+static const unsigned short dep230[] = {
+ 0, 97, 109, 195, 282, 289, 2140, 2327,
+};
+
+static const unsigned short dep231[] = {
+ 0, 3, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190,
+ 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep232[] = {
+ 0, 3, 5, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133,
+ 135, 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185,
+ 190, 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327,
+ 4135, 20616,
+};
+
+static const unsigned short dep233[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190,
+ 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep234[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173,
+ 2327, 4135, 16528, 16530, 16531, 16533, 20616,
+};
+
+static const unsigned short dep235[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep236[] = {
+ 0, 31, 97, 109, 195, 234, 282, 289, 2140, 2327,
+};
+
+static const unsigned short dep237[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135,
+ 20616,
+};
+
+static const unsigned short dep238[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530,
+ 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep239[] = {
+ 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185,
+ 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135,
+ 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep240[] = {
+ 0, 97, 195, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+};
+
+static const unsigned short dep241[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190,
+ 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135,
+ 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep242[] = {
+ 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133,
+ 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175,
+ 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325,
+ 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep243[] = {
+ 0, 97, 195, 282, 2137, 2325, 18601, 18602, 18761, 18762, 18764, 18765,
+};
+
+static const unsigned short dep244[] = {
+ 97, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, 18765,
+
+};
+
+static const unsigned short dep245[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, 4135, 16528,
+ 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep246[] = {
+ 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185,
+ 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327,
+ 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep247[] = {
+ 0, 97, 195, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764,
+ 18765,
+};
+
+static const unsigned short dep248[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135,
+ 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190,
+ 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327,
+ 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+};
+
+static const unsigned short dep249[] = {
+ 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133,
+ 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175,
+ 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325,
+ 2327, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+
+};
+
+static const unsigned short dep250[] = {
+ 0, 97, 195, 282, 2137, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764,
+ 18765,
+};
+
+static const unsigned short dep251[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170,
+ 2173, 4135, 16528, 16530, 16531, 16533, 20616,
+};
+
+static const unsigned short dep252[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156,
+ 158, 162, 171, 173, 175, 185, 192, 282, 2166, 2167, 2170, 2173, 4135,
+};
+
+static const unsigned short dep253[] = {
+ 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156,
+ 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170,
+ 2173, 2327, 4135, 20616,
+};
+
+static const unsigned short dep254[] = {
+ 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173,
+ 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616,
+
+};
+
+static const unsigned short dep255[] = {
+ 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137,
+ 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191,
+ 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170,
+ 2173, 2327, 4135, 16528, 16530, 16531, 16533, 20616,
+};
+
+static const unsigned short dep256[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 24, 26, 27, 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 201, 202, 203,
+ 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222,
+ 223, 224, 225, 227, 230, 231, 232, 233, 234, 282, 2071, 2081, 2140, 2274,
+ 2284, 2327, 28866, 29018,
+};
+
+static const unsigned short dep257[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 24, 25, 26, 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180,
+ 181, 185, 190, 191, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225,
+ 227, 229, 231, 232, 233, 234, 282, 2071, 2081, 2138, 2139, 2140, 2166, 2167,
+ 2170, 2173, 2274, 2284, 2327, 4135, 20616, 28866, 29018,
+};
+
+#define NELS(X) (sizeof(X)/sizeof(X[0]))
+static const struct ia64_opcode_dependency
+op_dependencies[] = {
+ { NELS(dep1), dep1, NELS(dep0), dep0, },
+ { NELS(dep3), dep3, NELS(dep2), dep2, },
+ { NELS(dep5), dep5, NELS(dep4), dep4, },
+ { NELS(dep7), dep7, NELS(dep6), dep6, },
+ { NELS(dep9), dep9, NELS(dep8), dep8, },
+ { NELS(dep11), dep11, NELS(dep10), dep10, },
+ { NELS(dep13), dep13, NELS(dep12), dep12, },
+ { NELS(dep15), dep15, NELS(dep14), dep14, },
+ { NELS(dep17), dep17, NELS(dep16), dep16, },
+ { NELS(dep19), dep19, NELS(dep18), dep18, },
+ { NELS(dep21), dep21, NELS(dep20), dep20, },
+ { NELS(dep23), dep23, NELS(dep22), dep22, },
+ { NELS(dep25), dep25, NELS(dep24), dep24, },
+ { NELS(dep27), dep27, NELS(dep26), dep26, },
+ { NELS(dep29), dep29, NELS(dep28), dep28, },
+ { NELS(dep30), dep30, NELS(dep12), dep12, },
+ { NELS(dep32), dep32, NELS(dep31), dep31, },
+ { NELS(dep34), dep34, NELS(dep33), dep33, },
+ { NELS(dep35), dep35, NELS(dep12), dep12, },
+ { NELS(dep37), dep37, NELS(dep36), dep36, },
+ { NELS(dep39), dep39, NELS(dep38), dep38, },
+ { NELS(dep41), dep41, NELS(dep40), dep40, },
+ { NELS(dep42), dep42, NELS(dep31), dep31, },
+ { NELS(dep43), dep43, NELS(dep33), dep33, },
+ { NELS(dep45), dep45, NELS(dep44), dep44, },
+ { NELS(dep47), dep47, NELS(dep46), dep46, },
+ { NELS(dep49), dep49, NELS(dep48), dep48, },
+ { NELS(dep51), dep51, NELS(dep50), dep50, },
+ { NELS(dep53), dep53, NELS(dep52), dep52, },
+ { NELS(dep55), dep55, NELS(dep54), dep54, },
+ { NELS(dep57), dep57, NELS(dep56), dep56, },
+ { NELS(dep59), dep59, NELS(dep58), dep58, },
+ { NELS(dep61), dep61, NELS(dep60), dep60, },
+ { NELS(dep63), dep63, NELS(dep62), dep62, },
+ { NELS(dep65), dep65, NELS(dep64), dep64, },
+ { NELS(dep67), dep67, NELS(dep66), dep66, },
+ { NELS(dep68), dep68, NELS(dep33), dep33, },
+ { NELS(dep70), dep70, NELS(dep69), dep69, },
+ { NELS(dep72), dep72, NELS(dep71), dep71, },
+ { NELS(dep74), dep74, NELS(dep73), dep73, },
+ { NELS(dep76), dep76, NELS(dep75), dep75, },
+ { NELS(dep77), dep77, NELS(dep33), dep33, },
+ { NELS(dep79), dep79, NELS(dep78), dep78, },
+ { NELS(dep81), dep81, NELS(dep80), dep80, },
+ { NELS(dep83), dep83, NELS(dep82), dep82, },
+ { NELS(dep84), dep84, NELS(dep33), dep33, },
+ { NELS(dep85), dep85, NELS(dep33), dep33, },
+ { NELS(dep86), dep86, NELS(dep33), dep33, },
+ { NELS(dep87), dep87, NELS(dep33), dep33, },
+ { NELS(dep89), dep89, NELS(dep88), dep88, },
+ { NELS(dep91), dep91, NELS(dep90), dep90, },
+ { NELS(dep93), dep93, NELS(dep92), dep92, },
+ { NELS(dep95), dep95, NELS(dep94), dep94, },
+ { NELS(dep97), dep97, NELS(dep96), dep96, },
+ { NELS(dep99), dep99, NELS(dep98), dep98, },
+ { NELS(dep101), dep101, NELS(dep100), dep100, },
+ { NELS(dep103), dep103, NELS(dep102), dep102, },
+ { NELS(dep105), dep105, NELS(dep104), dep104, },
+ { NELS(dep107), dep107, NELS(dep106), dep106, },
+ { NELS(dep109), dep109, NELS(dep108), dep108, },
+ { NELS(dep111), dep111, NELS(dep110), dep110, },
+ { NELS(dep113), dep113, NELS(dep112), dep112, },
+ { NELS(dep115), dep115, NELS(dep114), dep114, },
+ { NELS(dep117), dep117, NELS(dep116), dep116, },
+ { NELS(dep119), dep119, NELS(dep118), dep118, },
+ { NELS(dep121), dep121, NELS(dep120), dep120, },
+ { NELS(dep122), dep122, NELS(dep64), dep64, },
+ { NELS(dep123), dep123, NELS(dep33), dep33, },
+ { NELS(dep125), dep125, NELS(dep124), dep124, },
+ { NELS(dep126), dep126, NELS(dep0), dep0, },
+ { NELS(dep128), dep128, NELS(dep127), dep127, },
+ { NELS(dep130), dep130, NELS(dep129), dep129, },
+ { NELS(dep131), dep131, NELS(dep0), dep0, },
+ { NELS(dep132), dep132, NELS(dep0), dep0, },
+ { NELS(dep134), dep134, NELS(dep133), dep133, },
+ { NELS(dep135), dep135, NELS(dep0), dep0, },
+ { NELS(dep136), dep136, NELS(dep2), dep2, },
+ { NELS(dep137), dep137, NELS(dep4), dep4, },
+ { NELS(dep138), dep138, NELS(dep6), dep6, },
+ { NELS(dep139), dep139, NELS(dep8), dep8, },
+ { NELS(dep140), dep140, NELS(dep10), dep10, },
+ { NELS(dep141), dep141, NELS(dep33), dep33, },
+ { NELS(dep143), dep143, NELS(dep142), dep142, },
+ { NELS(dep144), dep144, NELS(dep142), dep142, },
+ { NELS(dep146), dep146, NELS(dep145), dep145, },
+ { NELS(dep147), dep147, NELS(dep145), dep145, },
+ { NELS(dep148), dep148, NELS(dep142), dep142, },
+ { NELS(dep150), dep150, NELS(dep149), dep149, },
+ { NELS(dep152), dep152, NELS(dep151), dep151, },
+ { NELS(dep154), dep154, NELS(dep153), dep153, },
+ { NELS(dep156), dep156, NELS(dep155), dep155, },
+ { NELS(dep157), dep157, NELS(dep155), dep155, },
+ { NELS(dep158), dep158, NELS(dep0), dep0, },
+ { NELS(dep160), dep160, NELS(dep159), dep159, },
+ { NELS(dep162), dep162, NELS(dep161), dep161, },
+ { NELS(dep164), dep164, NELS(dep163), dep163, },
+ { NELS(dep166), dep166, NELS(dep165), dep165, },
+ { NELS(dep168), dep168, NELS(dep167), dep167, },
+ { NELS(dep169), dep169, NELS(dep0), dep0, },
+ { NELS(dep170), dep170, NELS(dep0), dep0, },
+ { NELS(dep171), dep171, NELS(dep0), dep0, },
+ { NELS(dep172), dep172, NELS(dep33), dep33, },
+ { NELS(dep174), dep174, NELS(dep173), dep173, },
+ { NELS(dep175), dep175, NELS(dep173), dep173, },
+ { NELS(dep177), dep177, NELS(dep176), dep176, },
+ { NELS(dep179), dep179, NELS(dep178), dep178, },
+ { NELS(dep181), dep181, NELS(dep180), dep180, },
+ { NELS(dep183), dep183, NELS(dep182), dep182, },
+ { NELS(dep185), dep185, NELS(dep184), dep184, },
+ { NELS(dep187), dep187, NELS(dep186), dep186, },
+ { NELS(dep189), dep189, NELS(dep188), dep188, },
+ { NELS(dep191), dep191, NELS(dep190), dep190, },
+ { NELS(dep193), dep193, NELS(dep192), dep192, },
+ { NELS(dep194), dep194, NELS(dep0), dep0, },
+ { NELS(dep195), dep195, NELS(dep0), dep0, },
+ { NELS(dep196), dep196, NELS(dep0), dep0, },
+ { NELS(dep197), dep197, NELS(dep0), dep0, },
+ { NELS(dep198), dep198, NELS(dep0), dep0, },
+ { NELS(dep199), dep199, NELS(dep0), dep0, },
+ { NELS(dep200), dep200, NELS(dep0), dep0, },
+ { NELS(dep201), dep201, NELS(dep0), dep0, },
+ { NELS(dep203), dep203, NELS(dep202), dep202, },
+ { NELS(dep205), dep205, NELS(dep204), dep204, },
+ { NELS(dep207), dep207, NELS(dep206), dep206, },
+ { NELS(dep209), dep209, NELS(dep208), dep208, },
+ { NELS(dep210), dep210, NELS(dep0), dep0, },
+ { NELS(dep211), dep211, NELS(dep0), dep0, },
+ { NELS(dep212), dep212, NELS(dep0), dep0, },
+ { NELS(dep213), dep213, NELS(dep33), dep33, },
+ { NELS(dep214), dep214, NELS(dep33), dep33, },
+ { NELS(dep215), dep215, NELS(dep202), dep202, },
+ { NELS(dep216), dep216, NELS(dep0), dep0, },
+ { NELS(dep218), dep218, NELS(dep217), dep217, },
+ { NELS(dep219), dep219, NELS(dep0), dep0, },
+ { NELS(dep221), dep221, NELS(dep220), dep220, },
+ { NELS(dep222), dep222, NELS(dep220), dep220, },
+ { NELS(dep223), dep223, NELS(dep0), dep0, },
+ { NELS(dep221), dep221, NELS(dep224), dep224, },
+ { NELS(dep226), dep226, NELS(dep225), dep225, },
+ { NELS(dep227), dep227, NELS(dep225), dep225, },
+ { NELS(dep229), dep229, NELS(dep228), dep228, },
+ { NELS(dep231), dep231, NELS(dep230), dep230, },
+ { NELS(dep232), dep232, NELS(dep230), dep230, },
+ { NELS(dep233), dep233, NELS(dep230), dep230, },
+ { NELS(dep234), dep234, NELS(dep0), dep0, },
+ { NELS(dep235), dep235, NELS(dep230), dep230, },
+ { NELS(dep237), dep237, NELS(dep236), dep236, },
+ { NELS(dep238), dep238, NELS(dep64), dep64, },
+ { NELS(dep239), dep239, NELS(dep64), dep64, },
+ { NELS(dep241), dep241, NELS(dep240), dep240, },
+ { NELS(dep242), dep242, NELS(dep240), dep240, },
+ { NELS(dep241), dep241, NELS(dep243), dep243, },
+ { NELS(dep245), dep245, NELS(dep244), dep244, },
+ { NELS(dep246), dep246, NELS(dep244), dep244, },
+ { NELS(dep248), dep248, NELS(dep247), dep247, },
+ { NELS(dep249), dep249, NELS(dep247), dep247, },
+ { NELS(dep248), dep248, NELS(dep250), dep250, },
+ { NELS(dep251), dep251, NELS(dep225), dep225, },
+ { NELS(dep252), dep252, NELS(dep33), dep33, },
+ { NELS(dep253), dep253, NELS(dep0), dep0, },
+ { NELS(dep254), dep254, NELS(dep64), dep64, },
+ { NELS(dep255), dep255, NELS(dep230), dep230, },
+ { 0, NULL, 0, NULL, },
+ { NELS(dep257), dep257, NELS(dep256), dep256, },
+};
+
+static const struct ia64_completer_table
+completer_table[] = {
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 95 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 95 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 594, -1, 0, 1, 6 },
+ { 0x0, 0x0, 0, 657, -1, 0, 1, 18 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 162 },
+ { 0x0, 0x0, 0, 756, -1, 0, 1, 18 },
+ { 0x0, 0x0, 0, 2198, -1, 0, 1, 10 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 9 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 13 },
+ { 0x1, 0x1, 0, -1, -1, 13, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, 2406, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, 1140, -1, 0, 1, 129 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 45 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 84 },
+ { 0x0, 0x0, 0, 2246, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 2473, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 2250, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, 2252, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 2482, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 2485, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 2507, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 2510, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 25 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 25 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 25 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 25 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 36 },
+ { 0x0, 0x0, 0, 2518, -1, 0, 1, 30 },
+ { 0x0, 0x0, 0, 1409, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 162 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 83 },
+ { 0x0, 0x0, 0, 1457, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1466, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1475, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1477, -1, 0, 1, 132 },
+ { 0x0, 0x0, 0, 1479, -1, 0, 1, 132 },
+ { 0x0, 0x0, 0, 1488, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1497, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1506, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1515, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1524, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1533, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1543, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1553, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1563, -1, 0, 1, 131 },
+ { 0x0, 0x0, 0, 1572, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1578, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1584, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1590, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1596, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1602, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1608, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1614, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1620, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1626, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1632, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1638, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1644, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1650, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1656, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1662, -1, 0, 1, 147 },
+ { 0x0, 0x0, 0, 1668, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1674, -1, 0, 1, 152 },
+ { 0x0, 0x0, 0, 1678, -1, 0, 1, 158 },
+ { 0x0, 0x0, 0, 1682, -1, 0, 1, 159 },
+ { 0x0, 0x0, 0, 1686, -1, 0, 1, 159 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 85 },
+ { 0x0, 0x0, 0, 258, -1, 0, 1, 41 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 68 },
+ { 0x1, 0x1, 0, 1166, -1, 20, 1, 68 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 69 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 70 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 70 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 71 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 72 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 73 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 93 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 94 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 96 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 97 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 98 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 99 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 104 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 105 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 106 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 107 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 108 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 109 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 110 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 113 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 114 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 115 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 116 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 117 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 118 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 119 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 120 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 163 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 163 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 163 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 72 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 162 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2858, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2859, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2210, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2211, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2873, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2874, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2875, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2876, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2877, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2860, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, 2861, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 11 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 91 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 89 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x1, 0x1, 0, -1, -1, 13, 1, 0 },
+ { 0x0, 0x0, 0, 2879, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 90 },
+ { 0x0, 0x0, 0, 1966, -1, 0, 1, 138 },
+ { 0x0, 0x0, 0, 1968, -1, 0, 1, 145 },
+ { 0x0, 0x0, 0, 1970, -1, 0, 1, 139 },
+ { 0x0, 0x0, 0, 1972, -1, 0, 1, 139 },
+ { 0x0, 0x0, 0, 1974, -1, 0, 1, 138 },
+ { 0x0, 0x0, 0, 1976, -1, 0, 1, 145 },
+ { 0x0, 0x0, 0, 1978, -1, 0, 1, 138 },
+ { 0x0, 0x0, 0, 1980, -1, 0, 1, 145 },
+ { 0x0, 0x0, 0, 1983, -1, 0, 1, 138 },
+ { 0x0, 0x0, 0, 1986, -1, 0, 1, 145 },
+ { 0x0, 0x0, 0, 1989, -1, 0, 1, 157 },
+ { 0x0, 0x0, 0, 1990, -1, 0, 1, 161 },
+ { 0x0, 0x0, 0, 1991, -1, 0, 1, 157 },
+ { 0x0, 0x0, 0, 1992, -1, 0, 1, 161 },
+ { 0x0, 0x0, 0, 1993, -1, 0, 1, 157 },
+ { 0x0, 0x0, 0, 1994, -1, 0, 1, 161 },
+ { 0x0, 0x0, 0, 1995, -1, 0, 1, 157 },
+ { 0x0, 0x0, 0, 1996, -1, 0, 1, 161 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 88 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 127 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 125 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 127 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 126 },
+ { 0x0, 0x0, 0, 1687, -1, 0, 1, 143 },
+ { 0x0, 0x0, 0, 1688, -1, 0, 1, 143 },
+ { 0x0, 0x0, 0, 1689, -1, 0, 1, 143 },
+ { 0x0, 0x0, 0, 1690, -1, 0, 1, 143 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 0, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 1, 224, -1, 0, 1, 12 },
+ { 0x0, 0x0, 1, 225, -1, 0, 1, 14 },
+ { 0x1, 0x1, 2, -1, -1, 27, 1, 12 },
+ { 0x1, 0x1, 2, -1, -1, 27, 1, 14 },
+ { 0x0, 0x0, 3, -1, 1340, 0, 0, -1 },
+ { 0x0, 0x0, 3, -1, 1341, 0, 0, -1 },
+ { 0x1, 0x1, 3, 2749, 1450, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2750, 1459, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2751, 1468, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2752, 1481, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2753, 1490, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2754, 1499, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2755, 1508, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2756, 1517, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2757, 1526, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2758, 1535, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2759, 1545, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2760, 1555, 33, 1, 134 },
+ { 0x1, 0x1, 3, 2761, 1568, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2762, 1574, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2763, 1580, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2764, 1586, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2765, 1592, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2766, 1598, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2767, 1604, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2768, 1610, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2769, 1616, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2770, 1622, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2771, 1628, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2772, 1634, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2773, 1640, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2774, 1646, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2775, 1652, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2776, 1658, 33, 1, 149 },
+ { 0x1, 0x1, 3, 2777, 1664, 33, 1, 154 },
+ { 0x1, 0x1, 3, 2778, 1670, 33, 1, 154 },
+ { 0x1, 0x1, 3, -1, -1, 27, 1, 41 },
+ { 0x0, 0x0, 4, 2212, 1425, 0, 1, 142 },
+ { 0x0, 0x0, 4, 2213, 1427, 0, 1, 142 },
+ { 0x0, 0x0, 4, 2214, 1429, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2215, 1431, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2216, 1433, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2217, 1435, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2218, 1437, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2219, 1439, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2220, 1441, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2221, 1443, 0, 1, 141 },
+ { 0x0, 0x0, 4, 2222, 1445, 0, 1, 143 },
+ { 0x0, 0x0, 4, 2223, 1447, 0, 1, 143 },
+ { 0x1, 0x1, 4, -1, 1454, 33, 1, 137 },
+ { 0x5, 0x5, 4, 552, 1453, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1463, 33, 1, 137 },
+ { 0x5, 0x5, 4, 553, 1462, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1472, 33, 1, 137 },
+ { 0x5, 0x5, 4, 554, 1471, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1476, 32, 1, 132 },
+ { 0x1, 0x1, 4, -1, 1478, 32, 1, 132 },
+ { 0x1, 0x1, 4, -1, 1485, 33, 1, 137 },
+ { 0x5, 0x5, 4, 555, 1484, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1494, 33, 1, 137 },
+ { 0x5, 0x5, 4, 556, 1493, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1503, 33, 1, 137 },
+ { 0x5, 0x5, 4, 557, 1502, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1512, 33, 1, 137 },
+ { 0x5, 0x5, 4, 558, 1511, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1521, 33, 1, 137 },
+ { 0x5, 0x5, 4, 559, 1520, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1530, 33, 1, 137 },
+ { 0x5, 0x5, 4, 560, 1529, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1540, 33, 1, 137 },
+ { 0x5, 0x5, 4, 1036, 1538, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1550, 33, 1, 137 },
+ { 0x5, 0x5, 4, 1037, 1548, 32, 1, 131 },
+ { 0x1, 0x1, 4, -1, 1560, 33, 1, 137 },
+ { 0x5, 0x5, 4, 1038, 1558, 32, 1, 131 },
+ { 0x1, 0x21, 10, 2013, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2014, -1, 12, 1, 3 },
+ { 0x1, 0x21, 10, 420, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2074, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, -1, 2075, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2076, 0, 0, -1 },
+ { 0x0, 0x0, 10, 2017, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2018, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 2019, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2020, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, 430, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2080, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 434, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2082, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, 438, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2084, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 442, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2086, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, 446, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2088, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 450, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2090, -1, 12, 1, 3 },
+ { 0x1, 0x21, 10, 2033, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2034, -1, 12, 1, 3 },
+ { 0x1, 0x21, 10, 460, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2096, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, -1, 2097, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2098, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2101, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2102, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2103, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2104, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2105, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2106, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2107, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2108, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2109, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2110, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2111, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2112, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2113, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2114, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2115, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2116, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2117, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2118, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2119, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2120, 0, 0, -1 },
+ { 0x1, 0x21, 10, 2037, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2038, -1, 12, 1, 3 },
+ { 0x1, 0x21, 10, 468, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2122, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, -1, 2123, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2124, 0, 0, -1 },
+ { 0x0, 0x0, 10, 2041, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2042, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 2043, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2044, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, 478, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2128, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 482, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2130, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, 486, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2132, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 490, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2134, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, 494, -1, 0, 1, 3 },
+ { 0x1, 0x1, 10, 2136, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 498, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2138, -1, 12, 1, 3 },
+ { 0x1, 0x21, 10, 2057, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2058, -1, 12, 1, 3 },
+ { 0x1, 0x21, 10, 508, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 10, 2144, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, -1, 2145, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2146, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2149, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2150, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2151, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2152, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2153, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2154, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2155, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2156, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2157, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2158, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2159, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2160, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2161, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2162, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2163, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2164, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2165, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2166, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2167, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2168, 0, 0, -1 },
+ { 0x1, 0x1, 10, 2061, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 10, 2062, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 2063, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 10, 2064, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, -1, 2169, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2171, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2173, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2175, 0, 0, -1 },
+ { 0x1, 0x1, 10, 2065, -1, 36, 1, 78 },
+ { 0x1000001, 0x1000001, 10, 2066, -1, 12, 1, 78 },
+ { 0x1, 0x1, 10, 2067, -1, 36, 1, 78 },
+ { 0x1000001, 0x1000001, 10, 2068, -1, 12, 1, 78 },
+ { 0x0, 0x0, 10, -1, 2177, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2179, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2181, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2183, 0, 0, -1 },
+ { 0x1, 0x1, 10, 2069, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 10, 2070, -1, 12, 1, 3 },
+ { 0x1, 0x1, 10, 2071, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 10, 2072, -1, 12, 1, 3 },
+ { 0x0, 0x0, 10, -1, 2185, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2187, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2189, 0, 0, -1 },
+ { 0x0, 0x0, 10, -1, 2191, 0, 0, -1 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x4200001, 11, 2015, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 300, -1, 33, 1, 3 },
+ { 0x0, 0x0, 11, 2077, -1, 0, 1, 3 },
+ { 0x1, 0x1, 11, 2078, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 2021, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x0, 0x0, 11, 308, -1, 0, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x200001, 11, 2023, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 310, -1, 33, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 2025, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x0, 0x0, 11, 312, -1, 0, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x200001, 11, 2027, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 314, -1, 33, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 2029, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x0, 0x0, 11, 316, -1, 0, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x200001, 11, 2031, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 318, -1, 33, 1, 3 },
+ { 0x0, 0x0, 11, 2091, -1, 0, 1, 3 },
+ { 0x1, 0x1, 11, 2092, -1, 12, 1, 3 },
+ { 0x1, 0x1, 11, 2093, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 11, 2094, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x4200001, 11, 2035, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 322, -1, 33, 1, 3 },
+ { 0x0, 0x0, 11, 2099, -1, 0, 1, 3 },
+ { 0x1, 0x1, 11, 2100, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x4200001, 11, 2039, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 348, -1, 33, 1, 3 },
+ { 0x0, 0x0, 11, 2125, -1, 0, 1, 3 },
+ { 0x1, 0x1, 11, 2126, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 2045, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x0, 0x0, 11, 356, -1, 0, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x200001, 11, 2047, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 358, -1, 33, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 2049, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x0, 0x0, 11, 360, -1, 0, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x200001, 11, 2051, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 362, -1, 33, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 2053, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x0, 0x0, 11, 364, -1, 0, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x200001, 11, 2055, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 366, -1, 33, 1, 3 },
+ { 0x0, 0x0, 11, 2139, -1, 0, 1, 3 },
+ { 0x1, 0x1, 11, 2140, -1, 12, 1, 3 },
+ { 0x1, 0x1, 11, 2141, -1, 33, 1, 3 },
+ { 0x200001, 0x200001, 11, 2142, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x200001, 0x4200001, 11, 2059, -1, 12, 1, 3 },
+ { 0x2, 0x3, 11, -1, -1, 37, 1, 5 },
+ { 0x1, 0x1, 11, 370, -1, 33, 1, 3 },
+ { 0x0, 0x0, 11, 2147, -1, 0, 1, 3 },
+ { 0x1, 0x1, 11, 2148, -1, 12, 1, 3 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, 2170, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 11, 2172, -1, 12, 1, 3 },
+ { 0x1, 0x1, 11, 2174, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 11, 2176, -1, 12, 1, 3 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 80 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 80 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 80 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 80 },
+ { 0x1, 0x1, 11, 2178, -1, 36, 1, 78 },
+ { 0x1000001, 0x1000001, 11, 2180, -1, 12, 1, 78 },
+ { 0x1, 0x1, 11, 2182, -1, 36, 1, 78 },
+ { 0x1000001, 0x1000001, 11, 2184, -1, 12, 1, 78 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, -1, -1, 36, 1, 5 },
+ { 0x1, 0x1, 11, 2186, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 11, 2188, -1, 12, 1, 3 },
+ { 0x1, 0x1, 11, 2190, -1, 36, 1, 3 },
+ { 0x1000001, 0x1000001, 11, 2192, -1, 12, 1, 3 },
+ { 0x0, 0x0, 12, -1, -1, 0, 1, 15 },
+ { 0x0, 0x0, 12, -1, -1, 0, 1, 15 },
+ { 0x0, 0x0, 12, -1, -1, 0, 1, 15 },
+ { 0x1, 0x1, 13, 272, 1452, 34, 1, 131 },
+ { 0x1, 0x1, 13, 274, 1461, 34, 1, 131 },
+ { 0x1, 0x1, 13, 276, 1470, 34, 1, 131 },
+ { 0x1, 0x1, 13, 280, 1483, 34, 1, 131 },
+ { 0x1, 0x1, 13, 282, 1492, 34, 1, 131 },
+ { 0x1, 0x1, 13, 284, 1501, 34, 1, 131 },
+ { 0x1, 0x1, 13, 286, 1510, 34, 1, 131 },
+ { 0x1, 0x1, 13, 288, 1519, 34, 1, 131 },
+ { 0x1, 0x1, 13, 290, 1528, 34, 1, 131 },
+ { 0x1, 0x1, 13, 292, 1537, 34, 1, 131 },
+ { 0x1, 0x1, 13, 294, 1547, 34, 1, 131 },
+ { 0x1, 0x1, 13, 296, 1557, 34, 1, 131 },
+ { 0x0, 0x0, 19, -1, 795, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 796, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 797, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 798, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 799, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 800, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 801, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 802, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 803, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 804, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 805, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 806, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 807, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 808, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 809, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 810, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 811, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 812, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 813, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 814, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 815, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 816, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 817, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 818, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 819, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 820, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 821, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 822, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 823, 0, 0, -1 },
+ { 0x0, 0x0, 19, -1, 824, 0, 0, -1 },
+ { 0x0, 0x0, 20, -1, 2827, 0, 0, -1 },
+ { 0x0, 0x0, 20, -1, 2828, 0, 0, -1 },
+ { 0x0, 0x0, 20, -1, 2843, 0, 0, -1 },
+ { 0x0, 0x0, 20, -1, 2844, 0, 0, -1 },
+ { 0x0, 0x0, 20, -1, 2849, 0, 0, -1 },
+ { 0x0, 0x0, 20, -1, 2850, 0, 0, -1 },
+ { 0x0, 0x0, 21, 831, 2839, 0, 0, -1 },
+ { 0x0, 0x0, 21, 832, 2841, 0, 0, -1 },
+ { 0x0, 0x0, 23, -1, 2837, 0, 0, -1 },
+ { 0x0, 0x0, 23, -1, 2838, 0, 0, -1 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, 1272, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 6 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 7 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 8 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 16 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, 1293, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 19 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 20 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 21 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, 1326, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 18 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 35, 1, 22 },
+ { 0x1, 0x1, 24, -1, -1, 33, 1, 82 },
+ { 0x1, 0x1, 24, -1, -1, 33, 1, 82 },
+ { 0x1, 0x1, 24, 1342, 1455, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1343, 1464, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1344, 1473, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1345, 1486, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1346, 1495, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1347, 1504, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1348, 1513, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1349, 1522, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1350, 1531, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1351, 1541, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1352, 1551, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1353, 1561, 35, 1, 137 },
+ { 0x1, 0x1, 24, 1354, 1570, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1355, 1576, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1356, 1582, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1357, 1588, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1358, 1594, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1359, 1600, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1360, 1606, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1361, 1612, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1362, 1618, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1363, 1624, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1364, 1630, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1365, 1636, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1366, 1642, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1367, 1648, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1368, 1654, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1369, 1660, 35, 1, 151 },
+ { 0x1, 0x1, 24, 1370, 1666, 35, 1, 156 },
+ { 0x1, 0x1, 24, 1371, 1672, 35, 1, 156 },
+ { 0x0, 0x0, 33, 2821, 2819, 0, 0, -1 },
+ { 0x0, 0x0, 33, 2824, 2822, 0, 0, -1 },
+ { 0x0, 0x0, 33, 2830, 2829, 0, 0, -1 },
+ { 0x0, 0x0, 33, 2832, 2831, 0, 0, -1 },
+ { 0x0, 0x0, 33, 2846, 2845, 0, 0, -1 },
+ { 0x0, 0x0, 33, 2848, 2847, 0, 0, -1 },
+ { 0x0, 0x0, 35, -1, 2840, 0, 0, -1 },
+ { 0x0, 0x0, 35, -1, 2842, 0, 0, -1 },
+ { 0x1, 0x1, 38, -1, 2290, 37, 1, 30 },
+ { 0x1, 0x1, 38, -1, 2349, 37, 1, 30 },
+ { 0x0, 0x0, 38, -1, 2352, 0, 0, -1 },
+ { 0x1, 0x1, 38, -1, -1, 37, 1, 30 },
+ { 0x1, 0x1, 38, -1, 2357, 37, 1, 30 },
+ { 0x0, 0x0, 38, -1, 2360, 0, 0, -1 },
+ { 0x1, 0x1, 38, -1, -1, 37, 1, 30 },
+ { 0x0, 0x0, 38, -1, 2363, 0, 0, -1 },
+ { 0x1, 0x1, 38, -1, -1, 37, 1, 30 },
+ { 0x1, 0x1, 38, -1, 2366, 37, 1, 30 },
+ { 0x1, 0x1, 38, -1, 2369, 37, 1, 30 },
+ { 0x1, 0x1, 38, -1, 2402, 37, 1, 30 },
+ { 0x3, 0x3, 38, -1, -1, 30, 1, 144 },
+ { 0x0, 0x0, 38, 1142, -1, 0, 1, 102 },
+ { 0x0, 0x0, 38, -1, -1, 0, 1, 111 },
+ { 0x0, 0x0, 38, 1148, -1, 0, 1, 123 },
+ { 0x3, 0x3, 38, -1, -1, 30, 1, 160 },
+ { 0x0, 0x0, 38, 1149, -1, 0, 1, 41 },
+ { 0x0, 0x0, 40, -1, 973, 0, 0, -1 },
+ { 0x0, 0x0, 40, -1, 981, 0, 0, -1 },
+ { 0x0, 0x0, 40, 1151, 977, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 622, 33, 1, 6 },
+ { 0x18000001, 0x18000001, 40, -1, 630, 6, 1, 7 },
+ { 0x3, 0x3, 40, 1152, 626, 33, 1, 6 },
+ { 0x0, 0x0, 40, -1, 985, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 642, 33, 1, 8 },
+ { 0x0, 0x0, 40, -1, 989, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 654, 33, 1, 16 },
+ { 0x0, 0x0, 40, -1, 994, 0, 0, -1 },
+ { 0x0, 0x0, 40, -1, 998, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 677, 33, 1, 18 },
+ { 0x3, 0x3, 40, -1, 681, 33, 1, 18 },
+ { 0x0, 0x0, 40, -1, 1002, 0, 0, -1 },
+ { 0x0, 0x0, 40, -1, 1006, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 701, 33, 1, 19 },
+ { 0x18000001, 0x18000001, 40, -1, 705, 6, 1, 19 },
+ { 0x0, 0x0, 40, -1, 1010, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 717, 33, 1, 20 },
+ { 0x0, 0x0, 40, -1, 1014, 0, 0, -1 },
+ { 0x0, 0x0, 40, -1, 1018, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 737, 33, 1, 21 },
+ { 0x18000001, 0x18000001, 40, -1, 741, 6, 1, 21 },
+ { 0x0, 0x0, 40, -1, 1022, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 753, 33, 1, 22 },
+ { 0x0, 0x0, 40, -1, 1027, 0, 0, -1 },
+ { 0x0, 0x0, 40, -1, 1031, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 776, 33, 1, 18 },
+ { 0x3, 0x3, 40, -1, 780, 33, 1, 18 },
+ { 0x0, 0x0, 40, -1, 1035, 0, 0, -1 },
+ { 0x3, 0x3, 40, -1, 792, 33, 1, 22 },
+ { 0x0, 0x0, 41, 851, 972, 0, 0, -1 },
+ { 0x0, 0x0, 41, 852, 980, 0, 0, -1 },
+ { 0x0, 0x0, 41, 853, 976, 0, 0, -1 },
+ { 0x1, 0x1, 41, 854, 621, 34, 1, 6 },
+ { 0x10000001, 0x10000001, 41, 855, 629, 6, 1, 7 },
+ { 0x1, 0x1, 41, 856, 625, 34, 1, 6 },
+ { 0x0, 0x0, 41, 857, 984, 0, 0, -1 },
+ { 0x1, 0x1, 41, 858, 641, 34, 1, 8 },
+ { 0x0, 0x0, 41, 859, 988, 0, 0, -1 },
+ { 0x1, 0x1, 41, 860, 653, 34, 1, 16 },
+ { 0x0, 0x0, 41, 861, 993, 0, 0, -1 },
+ { 0x0, 0x0, 41, 862, 997, 0, 0, -1 },
+ { 0x1, 0x1, 41, 863, 676, 34, 1, 18 },
+ { 0x1, 0x1, 41, 864, 680, 34, 1, 18 },
+ { 0x0, 0x0, 41, 865, 1001, 0, 0, -1 },
+ { 0x0, 0x0, 41, 866, 1005, 0, 0, -1 },
+ { 0x1, 0x1, 41, 867, 700, 34, 1, 19 },
+ { 0x10000001, 0x10000001, 41, 868, 704, 6, 1, 19 },
+ { 0x0, 0x0, 41, 869, 1009, 0, 0, -1 },
+ { 0x1, 0x1, 41, 870, 716, 34, 1, 20 },
+ { 0x0, 0x0, 41, 871, 1013, 0, 0, -1 },
+ { 0x0, 0x0, 41, 872, 1017, 0, 0, -1 },
+ { 0x1, 0x1, 41, 873, 736, 34, 1, 21 },
+ { 0x10000001, 0x10000001, 41, 874, 740, 6, 1, 21 },
+ { 0x0, 0x0, 41, 875, 1021, 0, 0, -1 },
+ { 0x1, 0x1, 41, 876, 752, 34, 1, 22 },
+ { 0x0, 0x0, 41, 877, 1026, 0, 0, -1 },
+ { 0x0, 0x0, 41, 878, 1030, 0, 0, -1 },
+ { 0x1, 0x1, 41, 879, 775, 34, 1, 18 },
+ { 0x1, 0x1, 41, 880, 779, 34, 1, 18 },
+ { 0x0, 0x0, 41, 881, 1034, 0, 0, -1 },
+ { 0x1, 0x1, 41, 882, 791, 34, 1, 22 },
+ { 0x800001, 0x800001, 41, -1, 1156, 4, 1, 17 },
+ { 0x1, 0x1, 41, 2236, 1154, 4, 1, 17 },
+ { 0x1, 0x1, 41, 957, 1159, 4, 1, 23 },
+ { 0x2, 0x3, 41, -1, 1164, 20, 1, 68 },
+ { 0x1, 0x1, 41, 2237, 1162, 21, 1, 68 },
+ { 0x0, 0x0, 42, -1, -1, 0, 1, 86 },
+ { 0x0, 0x0, 42, -1, -1, 0, 1, 86 },
+ { 0x0, 0x0, 42, -1, -1, 0, 1, 130 },
+ { 0x1, 0x1, 44, 1372, 297, 38, 1, 1 },
+ { 0x1, 0x1, 44, 1373, 299, 38, 1, 1 },
+ { 0x0, 0x0, 44, -1, 302, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 424, 0, 0, -1 },
+ { 0x1, 0x1, 44, 1377, 319, 38, 1, 1 },
+ { 0x1, 0x1, 44, 1378, 321, 38, 1, 1 },
+ { 0x0, 0x0, 44, -1, 324, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 464, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 326, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 344, 0, 0, -1 },
+ { 0x1, 0x1, 44, 1384, 345, 38, 1, 1 },
+ { 0x1, 0x1, 44, 1385, 347, 38, 1, 1 },
+ { 0x0, 0x0, 44, -1, 350, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 472, 0, 0, -1 },
+ { 0x1, 0x1, 44, 1389, 367, 38, 1, 1 },
+ { 0x1, 0x1, 44, 1390, 369, 38, 1, 1 },
+ { 0x0, 0x0, 44, -1, 372, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 512, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 374, 0, 0, -1 },
+ { 0x0, 0x0, 44, -1, 392, 0, 0, -1 },
+ { 0x0, 0x0, 44, 1248, 2297, 0, 0, -1 },
+ { 0x0, 0x0, 44, 1249, 2305, 0, 1, 55 },
+ { 0x0, 0x0, 44, 1250, 2972, 0, 1, 55 },
+ { 0x0, 0x0, 44, 1251, 2373, 0, 0, -1 },
+ { 0x0, 0x0, 44, 1252, -1, 0, 1, 50 },
+ { 0x0, 0x0, 44, 1120, -1, 0, 1, 0 },
+ { 0x0, 0x0, 44, 1121, -1, 0, 1, 0 },
+ { 0x0, 0x0, 44, 1122, -1, 0, 1, 0 },
+ { 0x1, 0x1, 45, -1, 1676, 30, 1, 158 },
+ { 0x1, 0x1, 45, 963, 1675, 30, 1, 158 },
+ { 0x1, 0x1, 45, -1, 1680, 30, 1, 159 },
+ { 0x1, 0x1, 45, 964, 1679, 30, 1, 159 },
+ { 0x1, 0x1, 45, -1, 1684, 30, 1, 159 },
+ { 0x1, 0x1, 45, 965, 1683, 30, 1, 159 },
+ { 0x3, 0x3, 46, -1, 1160, 3, 1, 23 },
+ { 0x1, 0x1, 47, 2257, -1, 30, 1, 144 },
+ { 0x1, 0x1, 47, 2288, -1, 30, 1, 160 },
+ { 0x0, 0x0, 49, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 49, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 49, -1, -1, 0, 1, 41 },
+ { 0x1, 0x1, 56, -1, 1677, 31, 1, 158 },
+ { 0x1, 0x1, 56, -1, 1681, 31, 1, 159 },
+ { 0x1, 0x1, 56, -1, 1685, 31, 1, 159 },
+ { 0x0, 0x0, 56, -1, -1, 0, 1, 101 },
+ { 0x2, 0x3, 56, -1, -1, 27, 1, 101 },
+ { 0x1, 0x1, 56, -1, -1, 28, 1, 101 },
+ { 0x0, 0x0, 65, 14, 592, 0, 1, 6 },
+ { 0x0, 0x0, 65, 1273, 595, 0, 1, 6 },
+ { 0x1, 0x1, 65, 1274, 597, 33, 1, 6 },
+ { 0x1, 0x1, 65, 1275, 599, 34, 1, 6 },
+ { 0x3, 0x3, 65, 1276, 601, 33, 1, 6 },
+ { 0x0, 0x0, 65, 1277, 603, 0, 1, 6 },
+ { 0x1, 0x1, 65, 1278, 605, 33, 1, 6 },
+ { 0x1, 0x1, 65, 1279, 607, 34, 1, 6 },
+ { 0x3, 0x3, 65, 1280, 609, 33, 1, 6 },
+ { 0x1, 0x1, 65, 1281, 611, 6, 1, 7 },
+ { 0x8000001, 0x8000001, 65, 1282, 613, 6, 1, 7 },
+ { 0x10000001, 0x10000001, 65, 1283, 615, 6, 1, 7 },
+ { 0x18000001, 0x18000001, 65, 1284, 617, 6, 1, 7 },
+ { 0x0, 0x0, 65, 1285, 631, 0, 1, 8 },
+ { 0x1, 0x1, 65, 1286, 633, 33, 1, 8 },
+ { 0x1, 0x1, 65, 1287, 635, 34, 1, 8 },
+ { 0x3, 0x3, 65, 1288, 637, 33, 1, 8 },
+ { 0x0, 0x0, 65, 1289, 643, 0, 1, 16 },
+ { 0x1, 0x1, 65, 1290, 645, 33, 1, 16 },
+ { 0x1, 0x1, 65, 1291, 647, 34, 1, 16 },
+ { 0x3, 0x3, 65, 1292, 649, 33, 1, 16 },
+ { 0x0, 0x0, 65, 15, 655, 0, 1, 18 },
+ { 0x0, 0x0, 65, 1294, 658, 0, 1, 18 },
+ { 0x1, 0x1, 65, 1295, 660, 33, 1, 18 },
+ { 0x1, 0x1, 65, 1296, 662, 34, 1, 18 },
+ { 0x3, 0x3, 65, 1297, 664, 33, 1, 18 },
+ { 0x0, 0x0, 65, 1298, 666, 0, 1, 18 },
+ { 0x1, 0x1, 65, 1299, 668, 33, 1, 18 },
+ { 0x1, 0x1, 65, 1300, 670, 34, 1, 18 },
+ { 0x3, 0x3, 65, 1301, 672, 33, 1, 18 },
+ { 0x0, 0x0, 65, 1302, 682, 0, 1, 19 },
+ { 0x1, 0x1, 65, 1303, 684, 33, 1, 19 },
+ { 0x1, 0x1, 65, 1304, 686, 34, 1, 19 },
+ { 0x3, 0x3, 65, 1305, 688, 33, 1, 19 },
+ { 0x1, 0x1, 65, 1306, 690, 6, 1, 19 },
+ { 0x8000001, 0x8000001, 65, 1307, 692, 6, 1, 19 },
+ { 0x10000001, 0x10000001, 65, 1308, 694, 6, 1, 19 },
+ { 0x18000001, 0x18000001, 65, 1309, 696, 6, 1, 19 },
+ { 0x0, 0x0, 65, 1310, 706, 0, 1, 20 },
+ { 0x1, 0x1, 65, 1311, 708, 33, 1, 20 },
+ { 0x1, 0x1, 65, 1312, 710, 34, 1, 20 },
+ { 0x3, 0x3, 65, 1313, 712, 33, 1, 20 },
+ { 0x0, 0x0, 65, 1314, 718, 0, 1, 21 },
+ { 0x1, 0x1, 65, 1315, 720, 33, 1, 21 },
+ { 0x1, 0x1, 65, 1316, 722, 34, 1, 21 },
+ { 0x3, 0x3, 65, 1317, 724, 33, 1, 21 },
+ { 0x1, 0x1, 65, 1318, 726, 6, 1, 21 },
+ { 0x8000001, 0x8000001, 65, 1319, 728, 6, 1, 21 },
+ { 0x10000001, 0x10000001, 65, 1320, 730, 6, 1, 21 },
+ { 0x18000001, 0x18000001, 65, 1321, 732, 6, 1, 21 },
+ { 0x0, 0x0, 65, 1322, 742, 0, 1, 22 },
+ { 0x1, 0x1, 65, 1323, 744, 33, 1, 22 },
+ { 0x1, 0x1, 65, 1324, 746, 34, 1, 22 },
+ { 0x3, 0x3, 65, 1325, 748, 33, 1, 22 },
+ { 0x0, 0x0, 65, 17, 754, 0, 1, 18 },
+ { 0x0, 0x0, 65, 1327, 757, 0, 1, 18 },
+ { 0x1, 0x1, 65, 1328, 759, 33, 1, 18 },
+ { 0x1, 0x1, 65, 1329, 761, 34, 1, 18 },
+ { 0x3, 0x3, 65, 1330, 763, 33, 1, 18 },
+ { 0x0, 0x0, 65, 1331, 765, 0, 1, 18 },
+ { 0x1, 0x1, 65, 1332, 767, 33, 1, 18 },
+ { 0x1, 0x1, 65, 1333, 769, 34, 1, 18 },
+ { 0x3, 0x3, 65, 1334, 771, 33, 1, 18 },
+ { 0x0, 0x0, 65, 1335, 781, 0, 1, 22 },
+ { 0x1, 0x1, 65, 1336, 783, 33, 1, 22 },
+ { 0x1, 0x1, 65, 1337, 785, 34, 1, 22 },
+ { 0x3, 0x3, 65, 1338, 787, 33, 1, 22 },
+ { 0x3, 0x3, 66, 561, 1539, 33, 1, 136 },
+ { 0x3, 0x3, 66, 562, 1549, 33, 1, 136 },
+ { 0x3, 0x3, 66, 563, 1559, 33, 1, 136 },
+ { 0x0, 0x0, 66, -1, 1564, 0, 1, 147 },
+ { 0x0, 0x0, 66, -1, 1565, 0, 1, 152 },
+ { 0x0, 0x0, 66, -1, 1566, 0, 1, 152 },
+ { 0x0, 0x0, 107, 1046, 2345, 0, 0, -1 },
+ { 0x0, 0x0, 107, 1047, 2864, 0, 1, 30 },
+ { 0x0, 0x0, 107, 1048, 2386, 0, 0, -1 },
+ { 0x0, 0x0, 107, 1049, 2868, 0, 1, 30 },
+ { 0x0, 0x0, 109, -1, 2347, 0, 0, -1 },
+ { 0x1, 0x1, 109, -1, 2865, 27, 1, 30 },
+ { 0x0, 0x0, 109, -1, 2388, 0, 0, -1 },
+ { 0x1, 0x1, 109, -1, 2869, 27, 1, 30 },
+ { 0x0, 0x0, 110, 1051, -1, 0, 1, 122 },
+ { 0x1, 0x1, 111, -1, -1, 27, 1, 122 },
+ { 0x0, 0x0, 112, 1082, 2894, 0, 1, 1 },
+ { 0x0, 0x0, 112, 1083, 2897, 0, 1, 1 },
+ { 0x0, 0x0, 112, 1224, 305, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1225, 309, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1185, 440, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1186, 448, 0, 0, -1 },
+ { 0x0, 0x0, 112, -1, 456, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1084, 2910, 0, 1, 1 },
+ { 0x0, 0x0, 112, 1085, 2913, 0, 1, 1 },
+ { 0x0, 0x0, 112, -1, 330, 0, 0, -1 },
+ { 0x0, 0x0, 112, -1, 334, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1233, 335, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1234, 339, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1086, 2934, 0, 1, 1 },
+ { 0x0, 0x0, 112, 1087, 2937, 0, 1, 1 },
+ { 0x0, 0x0, 112, 1237, 353, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1238, 357, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1198, 488, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1199, 496, 0, 0, -1 },
+ { 0x0, 0x0, 112, -1, 504, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1391, 2948, 0, 1, 1 },
+ { 0x0, 0x0, 112, 1392, 2950, 0, 1, 1 },
+ { 0x0, 0x0, 112, -1, 378, 0, 0, -1 },
+ { 0x0, 0x0, 112, -1, 382, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1246, 383, 0, 0, -1 },
+ { 0x0, 0x0, 112, 1247, 387, 0, 0, -1 },
+ { 0x0, 0x0, 112, -1, 2315, 0, 0, -1 },
+ { 0x1, 0x9, 112, -1, 2319, 33, 1, 55 },
+ { 0x1, 0x9, 112, -1, 2981, 33, 1, 55 },
+ { 0x2, 0x3, 112, 1408, 2382, 27, 1, 50 },
+ { 0x1, 0x1, 114, 1374, 2895, 37, 1, 1 },
+ { 0x1, 0x1, 114, 1375, 2898, 37, 1, 1 },
+ { 0x1, 0x1, 114, 1379, 2911, 37, 1, 1 },
+ { 0x1, 0x1, 114, 1380, 2914, 37, 1, 1 },
+ { 0x1, 0x1, 114, 1386, 2935, 37, 1, 1 },
+ { 0x1, 0x1, 114, 1387, 2938, 37, 1, 1 },
+ { 0x0, 0x0, 114, -1, 2958, 0, 1, 1 },
+ { 0x0, 0x0, 114, -1, 2959, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1123, 2890, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1124, 2892, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1183, 303, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1184, 307, 0, 0, -1 },
+ { 0x0, 0x0, 115, -1, 444, 0, 0, -1 },
+ { 0x0, 0x0, 115, -1, 452, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1228, 454, 0, 0, -1 },
+ { 0x0, 0x0, 115, -1, 2908, 0, 1, 1 },
+ { 0x0, 0x0, 115, -1, 2909, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1231, 328, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1232, 332, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1192, 337, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1193, 341, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1127, 2930, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1128, 2932, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1196, 351, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1197, 355, 0, 0, -1 },
+ { 0x0, 0x0, 115, -1, 492, 0, 0, -1 },
+ { 0x0, 0x0, 115, -1, 500, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1241, 502, 0, 0, -1 },
+ { 0x0, 0x0, 115, -1, 2946, 0, 1, 1 },
+ { 0x0, 0x0, 115, -1, 2947, 0, 1, 1 },
+ { 0x0, 0x0, 115, 1244, 376, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1245, 380, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1205, 385, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1206, 389, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1078, 2313, 0, 0, -1 },
+ { 0x0, 0x0, 115, 1079, 2317, 0, 1, 55 },
+ { 0x0, 0x0, 115, 1080, 2980, 0, 1, 55 },
+ { 0x0, 0x0, 115, 1081, 2381, 0, 1, 50 },
+ { 0x1, 0x1, 115, -1, -1, 27, 1, 0 },
+ { 0x1, 0x1, 115, -1, -1, 27, 1, 0 },
+ { 0x1, 0x1, 115, -1, -1, 27, 1, 0 },
+ { 0x1, 0x1, 116, -1, 2891, 37, 1, 1 },
+ { 0x1, 0x1, 116, -1, 2893, 37, 1, 1 },
+ { 0x0, 0x0, 116, -1, 2918, 0, 1, 1 },
+ { 0x0, 0x0, 116, -1, 2919, 0, 1, 1 },
+ { 0x1, 0x1, 116, -1, 2931, 37, 1, 1 },
+ { 0x1, 0x1, 116, -1, 2933, 37, 1, 1 },
+ { 0x0, 0x0, 116, -1, 2956, 0, 1, 1 },
+ { 0x0, 0x0, 116, -1, 2957, 0, 1, 1 },
+ { 0x0, 0x0, 117, 1176, -1, 0, 1, 0 },
+ { 0x0, 0x0, 117, 1177, -1, 0, 1, 0 },
+ { 0x0, 0x0, 117, 1178, -1, 0, 1, 0 },
+ { 0x3, 0x3, 117, 1136, -1, 34, 1, 34 },
+ { 0x3, 0x3, 117, 1137, -1, 34, 1, 41 },
+ { 0x1, 0x1, 119, -1, -1, 35, 1, 34 },
+ { 0x1, 0x1, 119, -1, -1, 35, 1, 41 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 67 },
+ { 0x1, 0x1, 120, -1, -1, 36, 1, 129 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 41 },
+ { 0x1, 0x1, 120, -1, -1, 27, 1, 103 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 112 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 74 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 74 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 75 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 41 },
+ { 0x1, 0x1, 120, -1, -1, 27, 1, 124 },
+ { 0x1, 0x1, 120, -1, -1, 27, 1, 41 },
+ { 0x0, 0x0, 120, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 121, -1, 2820, 0, 0, -1 },
+ { 0x0, 0x0, 121, -1, 2823, 0, 0, -1 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 17 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 17 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 17 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 17 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 23 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 23 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 23 },
+ { 0x1, 0x1, 122, -1, -1, 35, 1, 23 },
+ { 0x1, 0x1, 122, -1, -1, 23, 1, 68 },
+ { 0x1, 0x1, 122, -1, -1, 23, 1, 68 },
+ { 0x1, 0x1, 122, -1, -1, 23, 1, 68 },
+ { 0x1, 0x1, 122, -1, -1, 23, 1, 68 },
+ { 0x1, 0x1, 122, 918, -1, 23, 1, 68 },
+ { 0x9, 0x9, 122, 919, -1, 20, 1, 68 },
+ { 0x0, 0x0, 126, 2199, -1, 0, 1, 0 },
+ { 0x0, 0x0, 126, 2200, -1, 0, 1, 0 },
+ { 0x1, 0x1, 126, -1, -1, 28, 1, 34 },
+ { 0x1, 0x1, 126, -1, -1, 27, 1, 34 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x0, 0x0, 126, -1, -1, 0, 1, 121 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 126, -1, -1, 29, 1, 0 },
+ { 0x0, 0x0, 126, 1134, -1, 0, 1, 34 },
+ { 0x0, 0x0, 126, 1262, -1, 0, 1, 41 },
+ { 0x0, 0x0, 140, 1212, 2886, 0, 1, 1 },
+ { 0x0, 0x0, 140, 1213, 2888, 0, 1, 1 },
+ { 0x0, 0x0, 140, 1054, 304, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1055, 432, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1094, 313, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1095, 317, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1096, 453, 0, 0, -1 },
+ { 0x0, 0x0, 140, -1, 2906, 0, 1, 1 },
+ { 0x0, 0x0, 140, -1, 2907, 0, 1, 1 },
+ { 0x0, 0x0, 140, 1099, 327, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1100, 331, 0, 0, -1 },
+ { 0x0, 0x0, 140, -1, 338, 0, 0, -1 },
+ { 0x0, 0x0, 140, -1, 342, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1216, 2926, 0, 1, 1 },
+ { 0x0, 0x0, 140, 1217, 2928, 0, 1, 1 },
+ { 0x0, 0x0, 140, 1067, 352, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1068, 480, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1107, 361, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1108, 365, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1109, 501, 0, 0, -1 },
+ { 0x0, 0x0, 140, -1, 2944, 0, 1, 1 },
+ { 0x0, 0x0, 140, -1, 2945, 0, 1, 1 },
+ { 0x0, 0x0, 140, 1112, 375, 0, 0, -1 },
+ { 0x0, 0x0, 140, 1113, 379, 0, 0, -1 },
+ { 0x0, 0x0, 140, -1, 386, 0, 0, -1 },
+ { 0x0, 0x0, 140, -1, 390, 0, 0, -1 },
+ { 0x0, 0x0, 140, 3012, 2301, 0, 0, -1 },
+ { 0x1, 0x1, 140, 3013, 2309, 33, 1, 55 },
+ { 0x1, 0x1, 140, 3014, 2974, 33, 1, 55 },
+ { 0x0, 0x0, 140, 3015, 2375, 0, 0, -1 },
+ { 0x1, 0x1, 140, 3016, -1, 28, 1, 50 },
+ { 0x1, 0x1, 141, -1, 2887, 37, 1, 1 },
+ { 0x1, 0x1, 141, -1, 2889, 37, 1, 1 },
+ { 0x0, 0x0, 141, -1, 2916, 0, 1, 1 },
+ { 0x0, 0x0, 141, -1, 2917, 0, 1, 1 },
+ { 0x1, 0x1, 141, -1, 2927, 37, 1, 1 },
+ { 0x1, 0x1, 141, -1, 2929, 37, 1, 1 },
+ { 0x0, 0x0, 141, -1, 2954, 0, 1, 1 },
+ { 0x0, 0x0, 141, -1, 2955, 0, 1, 1 },
+ { 0x1, 0x1, 144, 917, 1158, 3, 1, 23 },
+ { 0x0, 0x0, 145, 2201, -1, 0, 1, 34 },
+ { 0x0, 0x0, 146, 923, 2880, 0, 1, 1 },
+ { 0x0, 0x0, 146, 924, 2883, 0, 1, 1 },
+ { 0x0, 0x0, 146, -1, 306, 0, 0, -1 },
+ { 0x0, 0x0, 146, -1, 436, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1056, 311, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1057, 315, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1058, 455, 0, 0, -1 },
+ { 0x0, 0x0, 146, 927, 2900, 0, 1, 1 },
+ { 0x0, 0x0, 146, 928, 2903, 0, 1, 1 },
+ { 0x0, 0x0, 146, 1061, 329, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1062, 333, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1101, 336, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1102, 340, 0, 0, -1 },
+ { 0x0, 0x0, 146, 933, 2920, 0, 1, 1 },
+ { 0x0, 0x0, 146, 934, 2923, 0, 1, 1 },
+ { 0x0, 0x0, 146, -1, 354, 0, 0, -1 },
+ { 0x0, 0x0, 146, -1, 484, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1069, 359, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1070, 363, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1071, 503, 0, 0, -1 },
+ { 0x0, 0x0, 146, 937, 2940, 0, 1, 1 },
+ { 0x0, 0x0, 146, 938, 2942, 0, 1, 1 },
+ { 0x0, 0x0, 146, 1074, 377, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1075, 381, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1114, 384, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1115, 388, 0, 0, -1 },
+ { 0x0, 0x0, 146, 1207, 2299, 0, 0, -1 },
+ { 0x1, 0x1, 146, 1208, 2307, 36, 1, 55 },
+ { 0x1, 0x1, 146, 1209, 2973, 36, 1, 55 },
+ { 0x0, 0x0, 146, 1210, 2374, 0, 0, -1 },
+ { 0x1, 0x1, 146, 1211, -1, 27, 1, 50 },
+ { 0x1, 0x1, 147, -1, 2882, 37, 1, 1 },
+ { 0x1, 0x1, 147, -1, 2885, 37, 1, 1 },
+ { 0x1, 0x1, 147, -1, 2902, 37, 1, 1 },
+ { 0x1, 0x1, 147, -1, 2905, 37, 1, 1 },
+ { 0x1, 0x1, 147, -1, 2922, 37, 1, 1 },
+ { 0x1, 0x1, 147, -1, 2925, 37, 1, 1 },
+ { 0x0, 0x0, 147, -1, 2952, 0, 1, 1 },
+ { 0x0, 0x0, 147, -1, 2953, 0, 1, 1 },
+ { 0x0, 0x0, 148, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 148, 1135, -1, 0, 1, 41 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 67 },
+ { 0x0, 0x0, 149, -1, 2960, 0, 1, 64 },
+ { 0x0, 0x0, 149, -1, 2961, 0, 1, 64 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 87 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 87 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 92 },
+ { 0x0, 0x0, 149, -1, -1, 0, 1, 41 },
+ { 0x1, 0x1, 150, -1, 593, 12, 1, 6 },
+ { 0x1, 0x1, 150, -1, 596, 12, 1, 6 },
+ { 0x200001, 0x200001, 150, -1, 598, 12, 1, 6 },
+ { 0x400001, 0x400001, 150, -1, 600, 12, 1, 6 },
+ { 0x600001, 0x600001, 150, -1, 602, 12, 1, 6 },
+ { 0x1, 0x1, 150, -1, 604, 12, 1, 6 },
+ { 0x200001, 0x200001, 150, -1, 606, 12, 1, 6 },
+ { 0x400001, 0x400001, 150, -1, 608, 12, 1, 6 },
+ { 0x600001, 0x600001, 150, -1, 610, 12, 1, 6 },
+ { 0x41, 0x41, 150, -1, 612, 6, 1, 7 },
+ { 0x8000041, 0x8000041, 150, -1, 614, 6, 1, 7 },
+ { 0x10000041, 0x10000041, 150, -1, 616, 6, 1, 7 },
+ { 0x18000041, 0x18000041, 150, -1, 618, 6, 1, 7 },
+ { 0x1, 0x1, 150, -1, 632, 12, 1, 8 },
+ { 0x200001, 0x200001, 150, -1, 634, 12, 1, 8 },
+ { 0x400001, 0x400001, 150, -1, 636, 12, 1, 8 },
+ { 0x600001, 0x600001, 150, -1, 638, 12, 1, 8 },
+ { 0x1, 0x1, 150, -1, 644, 12, 1, 16 },
+ { 0x200001, 0x200001, 150, -1, 646, 12, 1, 16 },
+ { 0x400001, 0x400001, 150, -1, 648, 12, 1, 16 },
+ { 0x600001, 0x600001, 150, -1, 650, 12, 1, 16 },
+ { 0x1, 0x1, 150, -1, 656, 12, 1, 18 },
+ { 0x1, 0x1, 150, -1, 659, 12, 1, 18 },
+ { 0x200001, 0x200001, 150, -1, 661, 12, 1, 18 },
+ { 0x400001, 0x400001, 150, -1, 663, 12, 1, 18 },
+ { 0x600001, 0x600001, 150, -1, 665, 12, 1, 18 },
+ { 0x1, 0x1, 150, -1, 667, 12, 1, 18 },
+ { 0x200001, 0x200001, 150, -1, 669, 12, 1, 18 },
+ { 0x400001, 0x400001, 150, -1, 671, 12, 1, 18 },
+ { 0x600001, 0x600001, 150, -1, 673, 12, 1, 18 },
+ { 0x1, 0x1, 150, -1, 683, 12, 1, 19 },
+ { 0x200001, 0x200001, 150, -1, 685, 12, 1, 19 },
+ { 0x400001, 0x400001, 150, -1, 687, 12, 1, 19 },
+ { 0x600001, 0x600001, 150, -1, 689, 12, 1, 19 },
+ { 0x41, 0x41, 150, -1, 691, 6, 1, 19 },
+ { 0x8000041, 0x8000041, 150, -1, 693, 6, 1, 19 },
+ { 0x10000041, 0x10000041, 150, -1, 695, 6, 1, 19 },
+ { 0x18000041, 0x18000041, 150, -1, 697, 6, 1, 19 },
+ { 0x1, 0x1, 150, -1, 707, 12, 1, 20 },
+ { 0x200001, 0x200001, 150, -1, 709, 12, 1, 20 },
+ { 0x400001, 0x400001, 150, -1, 711, 12, 1, 20 },
+ { 0x600001, 0x600001, 150, -1, 713, 12, 1, 20 },
+ { 0x1, 0x1, 150, -1, 719, 12, 1, 21 },
+ { 0x200001, 0x200001, 150, -1, 721, 12, 1, 21 },
+ { 0x400001, 0x400001, 150, -1, 723, 12, 1, 21 },
+ { 0x600001, 0x600001, 150, -1, 725, 12, 1, 21 },
+ { 0x41, 0x41, 150, -1, 727, 6, 1, 21 },
+ { 0x8000041, 0x8000041, 150, -1, 729, 6, 1, 21 },
+ { 0x10000041, 0x10000041, 150, -1, 731, 6, 1, 21 },
+ { 0x18000041, 0x18000041, 150, -1, 733, 6, 1, 21 },
+ { 0x1, 0x1, 150, -1, 743, 12, 1, 22 },
+ { 0x200001, 0x200001, 150, -1, 745, 12, 1, 22 },
+ { 0x400001, 0x400001, 150, -1, 747, 12, 1, 22 },
+ { 0x600001, 0x600001, 150, -1, 749, 12, 1, 22 },
+ { 0x1, 0x1, 150, -1, 755, 12, 1, 18 },
+ { 0x1, 0x1, 150, -1, 758, 12, 1, 18 },
+ { 0x200001, 0x200001, 150, -1, 760, 12, 1, 18 },
+ { 0x400001, 0x400001, 150, -1, 762, 12, 1, 18 },
+ { 0x600001, 0x600001, 150, -1, 764, 12, 1, 18 },
+ { 0x1, 0x1, 150, -1, 766, 12, 1, 18 },
+ { 0x200001, 0x200001, 150, -1, 768, 12, 1, 18 },
+ { 0x400001, 0x400001, 150, -1, 770, 12, 1, 18 },
+ { 0x600001, 0x600001, 150, -1, 772, 12, 1, 18 },
+ { 0x1, 0x1, 150, -1, 782, 12, 1, 22 },
+ { 0x200001, 0x200001, 150, -1, 784, 12, 1, 22 },
+ { 0x400001, 0x400001, 150, -1, 786, 12, 1, 22 },
+ { 0x600001, 0x600001, 150, -1, 788, 12, 1, 22 },
+ { 0x0, 0x0, 155, -1, -1, 0, 1, 131 },
+ { 0x0, 0x0, 159, 793, -1, 0, 1, 81 },
+ { 0x0, 0x0, 159, 794, -1, 0, 1, 81 },
+ { 0x9, 0x9, 159, -1, 1456, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1465, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1474, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1487, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1496, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1505, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1514, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1523, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1532, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1542, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1552, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1562, 32, 1, 137 },
+ { 0x9, 0x9, 159, -1, 1571, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1577, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1583, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1589, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1595, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1601, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1607, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1613, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1619, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1625, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1631, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1637, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1643, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1649, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1655, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1661, 32, 1, 151 },
+ { 0x9, 0x9, 159, -1, 1667, 32, 1, 156 },
+ { 0x9, 0x9, 159, -1, 1673, 32, 1, 156 },
+ { 0x0, 0x0, 160, 1253, 298, 0, 0, -1 },
+ { 0x0, 0x0, 160, 1254, 422, 0, 0, -1 },
+ { 0x1, 0x1, 160, -1, 2896, 38, 1, 1 },
+ { 0x1, 0x1, 160, 925, 2899, 38, 1, 1 },
+ { 0x0, 0x0, 160, 926, 423, 0, 0, -1 },
+ { 0x0, 0x0, 160, 1255, 320, 0, 0, -1 },
+ { 0x0, 0x0, 160, 1256, 462, 0, 0, -1 },
+ { 0x1, 0x1, 160, -1, 2912, 38, 1, 1 },
+ { 0x1, 0x1, 160, 929, 2915, 38, 1, 1 },
+ { 0x0, 0x0, 160, 930, 463, 0, 0, -1 },
+ { 0x0, 0x0, 160, 931, 325, 0, 0, -1 },
+ { 0x0, 0x0, 160, 932, 343, 0, 0, -1 },
+ { 0x0, 0x0, 160, 1257, 346, 0, 0, -1 },
+ { 0x0, 0x0, 160, 1258, 470, 0, 0, -1 },
+ { 0x1, 0x1, 160, -1, 2936, 38, 1, 1 },
+ { 0x1, 0x1, 160, 935, 2939, 38, 1, 1 },
+ { 0x0, 0x0, 160, 936, 471, 0, 0, -1 },
+ { 0x0, 0x0, 160, -1, 368, 0, 0, -1 },
+ { 0x0, 0x0, 160, -1, 510, 0, 0, -1 },
+ { 0x1, 0x1, 160, -1, 2949, 38, 1, 1 },
+ { 0x1, 0x1, 160, 939, 2951, 38, 1, 1 },
+ { 0x0, 0x0, 160, 940, 511, 0, 0, -1 },
+ { 0x0, 0x0, 160, 941, 373, 0, 0, -1 },
+ { 0x0, 0x0, 160, 942, 391, 0, 0, -1 },
+ { 0x0, 0x0, 161, 1415, 2321, 0, 0, -1 },
+ { 0x0, 0x0, 161, 1416, 2329, 0, 1, 55 },
+ { 0x0, 0x0, 161, 1417, 2990, 0, 1, 55 },
+ { 0x0, 0x0, 161, 1418, 2377, 0, 0, -1 },
+ { 0x1, 0x1, 161, 1419, -1, 29, 1, 50 },
+ { 0x0, 0x0, 162, -1, 2339, 0, 0, -1 },
+ { 0x1, 0x9, 162, -1, 2343, 33, 1, 55 },
+ { 0x1, 0x9, 162, -1, 2999, 33, 1, 55 },
+ { 0x6, 0x7, 162, -1, 2384, 27, 1, 50 },
+ { 0x0, 0x0, 163, 1401, 2337, 0, 0, -1 },
+ { 0x0, 0x0, 163, 1402, 2341, 0, 1, 55 },
+ { 0x0, 0x0, 163, 1403, 2998, 0, 1, 55 },
+ { 0x1, 0x1, 163, 1404, 2383, 29, 1, 50 },
+ { 0x1, 0x1, 164, 1422, -1, 27, 1, 34 },
+ { 0x0, 0x0, 165, 2193, 2325, 0, 0, -1 },
+ { 0x1, 0x1, 165, 2194, 2333, 33, 1, 55 },
+ { 0x1, 0x1, 165, 2195, 2992, 33, 1, 55 },
+ { 0x0, 0x0, 165, 2196, 2379, 0, 0, -1 },
+ { 0x3, 0x3, 165, 2197, -1, 28, 1, 50 },
+ { 0x0, 0x0, 166, 1410, 2323, 0, 0, -1 },
+ { 0x1, 0x1, 166, 1411, 2331, 36, 1, 55 },
+ { 0x1, 0x1, 166, 1412, 2991, 36, 1, 55 },
+ { 0x0, 0x0, 166, 1413, 2378, 0, 0, -1 },
+ { 0x5, 0x5, 166, 1414, -1, 27, 1, 50 },
+ { 0x0, 0x0, 167, -1, 2962, 0, 1, 64 },
+ { 0x0, 0x0, 167, -1, 2963, 0, 1, 64 },
+ { 0x1, 0x1, 169, -1, -1, 28, 1, 34 },
+ { 0x1, 0x1, 170, 2779, -1, 27, 1, 34 },
+ { 0x1, 0x1, 170, 2780, -1, 27, 1, 34 },
+ { 0x1, 0x1, 171, 1703, -1, 28, 1, 142 },
+ { 0x1, 0x1, 171, 1704, -1, 28, 1, 142 },
+ { 0x1, 0x1, 171, 1705, -1, 28, 1, 142 },
+ { 0x1, 0x1, 171, 1706, -1, 28, 1, 142 },
+ { 0x1, 0x1, 171, 1707, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1708, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1709, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1710, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1711, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1712, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1713, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1714, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1715, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1716, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1717, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1718, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1719, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1720, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1721, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1722, -1, 28, 1, 141 },
+ { 0x1, 0x1, 171, 1723, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 1724, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 1725, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 1726, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 1727, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1728, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1729, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1730, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1731, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1732, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1733, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1734, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1735, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1736, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1737, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1738, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1739, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1740, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1741, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1742, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1743, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1744, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1745, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1746, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1747, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1748, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1749, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1750, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1751, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1752, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1753, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1754, -1, 28, 1, 132 },
+ { 0x1, 0x1, 171, 1755, -1, 28, 1, 132 },
+ { 0x1, 0x1, 171, 1756, -1, 28, 1, 132 },
+ { 0x1, 0x1, 171, 1757, -1, 28, 1, 132 },
+ { 0x1, 0x1, 171, 1758, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1759, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1760, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1761, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1762, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1763, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1764, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1765, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1766, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1767, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1768, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1769, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1770, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1771, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1772, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1773, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1774, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1775, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1776, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1777, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1778, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1779, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1780, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1781, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1782, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1783, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1784, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1785, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1786, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1787, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1788, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1789, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1790, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1791, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1792, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1793, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1794, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1795, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1796, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1797, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1798, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1799, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1800, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1801, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1802, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1803, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1804, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1805, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1806, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1807, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1808, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1809, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1810, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1811, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1812, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1813, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1814, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1815, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1816, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1817, -1, 28, 1, 136 },
+ { 0x1, 0x1, 171, 1818, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1819, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1820, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1821, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1822, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1823, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1824, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1825, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1826, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1827, -1, 28, 1, 136 },
+ { 0x1, 0x1, 171, 1828, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1829, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1830, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1831, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1832, -1, 28, 1, 133 },
+ { 0x1, 0x1, 171, 1833, -1, 28, 1, 134 },
+ { 0x1, 0x1, 171, 1834, -1, 28, 1, 135 },
+ { 0x1, 0x1, 171, 1835, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1836, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1837, -1, 28, 1, 136 },
+ { 0x1, 0x1, 171, 1838, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1839, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1840, -1, 28, 1, 137 },
+ { 0x1, 0x1, 171, 1841, -1, 28, 1, 131 },
+ { 0x1, 0x1, 171, 1842, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1843, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1844, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1845, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1846, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1847, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1848, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1849, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1850, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1851, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1852, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1853, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1854, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1855, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1856, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1857, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1858, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1859, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1860, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1861, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1862, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1863, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1864, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1865, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1866, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1867, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1868, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1869, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1870, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1871, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1872, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1873, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1874, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1875, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1876, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1877, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1878, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1879, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1880, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1881, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1882, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1883, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1884, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1885, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1886, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1887, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1888, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1889, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1890, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1891, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1892, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1893, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1894, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1895, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1896, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1897, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1898, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1899, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1900, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1901, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1902, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1903, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1904, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1905, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1906, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1907, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1908, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1909, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1910, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1911, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1912, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1913, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1914, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1915, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1916, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1917, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1918, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1919, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1920, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1921, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1922, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1923, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1924, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1925, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1926, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1927, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1928, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1929, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1930, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1931, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1932, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1933, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1934, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1935, -1, 28, 1, 148 },
+ { 0x1, 0x1, 171, 1936, -1, 28, 1, 149 },
+ { 0x1, 0x1, 171, 1937, -1, 28, 1, 150 },
+ { 0x1, 0x1, 171, 1938, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1939, -1, 28, 1, 151 },
+ { 0x1, 0x1, 171, 1940, -1, 28, 1, 147 },
+ { 0x1, 0x1, 171, 1941, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1942, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1943, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1944, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1945, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1946, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1947, -1, 28, 1, 153 },
+ { 0x1, 0x1, 171, 1948, -1, 28, 1, 154 },
+ { 0x1, 0x1, 171, 1949, -1, 28, 1, 155 },
+ { 0x1, 0x1, 171, 1950, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1951, -1, 28, 1, 156 },
+ { 0x1, 0x1, 171, 1952, -1, 28, 1, 152 },
+ { 0x1, 0x1, 171, 1691, -1, 28, 1, 158 },
+ { 0x1, 0x1, 171, 1692, -1, 28, 1, 158 },
+ { 0x1, 0x1, 171, 1693, -1, 28, 1, 158 },
+ { 0x1, 0x1, 171, 1694, -1, 28, 1, 158 },
+ { 0x1, 0x1, 171, 1695, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1696, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1697, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1698, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1699, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1700, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1701, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1702, -1, 28, 1, 159 },
+ { 0x1, 0x1, 171, 1997, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 1998, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 1999, -1, 28, 1, 143 },
+ { 0x1, 0x1, 171, 2000, -1, 28, 1, 143 },
+ { 0x1, 0x1, 172, 1953, -1, 29, 1, 158 },
+ { 0x1, 0x1, 172, 1954, -1, 29, 1, 158 },
+ { 0x1, 0x1, 172, 1955, -1, 29, 1, 158 },
+ { 0x1, 0x1, 172, 1956, -1, 29, 1, 158 },
+ { 0x1, 0x1, 172, 1957, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1958, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1959, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1960, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1961, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1962, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1963, -1, 29, 1, 159 },
+ { 0x1, 0x1, 172, 1964, -1, 29, 1, 159 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 142 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 141 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 271, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2258, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 273, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2259, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 275, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2260, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 132 },
+ { 0x3, 0x3, 173, 277, -1, 28, 1, 132 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 132 },
+ { 0x3, 0x3, 173, 278, -1, 28, 1, 132 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 279, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2261, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 281, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2262, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 283, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2263, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 285, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2264, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 287, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2265, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 289, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2266, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 291, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2267, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 293, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2268, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 133 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 134 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 135 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 136 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 295, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 137 },
+ { 0x3, 0x3, 173, 2269, -1, 28, 1, 131 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2270, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2271, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2272, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2273, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2274, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2275, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2276, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2277, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2278, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2279, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2280, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2281, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2282, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2283, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2284, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 148 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 149 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 150 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 151 },
+ { 0x3, 0x3, 173, 2285, -1, 28, 1, 147 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2286, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 153 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 154 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 155 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 156 },
+ { 0x3, 0x3, 173, 2287, -1, 28, 1, 152 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 158 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 158 },
+ { 0x3, 0x3, 173, 951, -1, 28, 1, 158 },
+ { 0x3, 0x3, 173, 952, -1, 28, 1, 158 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, 953, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, 954, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, 955, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, 956, -1, 28, 1, 159 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, 2224, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, 2225, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 139 },
+ { 0x3, 0x3, 173, 2226, -1, 28, 1, 139 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 139 },
+ { 0x3, 0x3, 173, 2227, -1, 28, 1, 139 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, 2228, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, 2229, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, 2230, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, 2231, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 140 },
+ { 0x3, 0x3, 173, 2232, -1, 28, 1, 138 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 146 },
+ { 0x3, 0x3, 173, 2233, -1, 28, 1, 145 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 157 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 161 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 157 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 161 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 157 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 161 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 157 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 161 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 157 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 161 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x3, 0x3, 173, -1, -1, 28, 1, 143 },
+ { 0x0, 0x0, 174, -1, 394, 0, 0, -1 },
+ { 0x0, 0x0, 174, -1, 396, 0, 0, -1 },
+ { 0x0, 0x0, 174, 3042, 3002, 0, 1, 1 },
+ { 0x0, 0x0, 174, 3043, 3003, 0, 1, 1 },
+ { 0x0, 0x0, 174, -1, 402, 0, 0, -1 },
+ { 0x0, 0x0, 174, -1, 404, 0, 0, -1 },
+ { 0x0, 0x0, 174, 3046, 3006, 0, 1, 76 },
+ { 0x0, 0x0, 174, 3047, 3007, 0, 1, 76 },
+ { 0x0, 0x0, 174, -1, 410, 0, 0, -1 },
+ { 0x0, 0x0, 174, -1, 412, 0, 0, -1 },
+ { 0x0, 0x0, 174, 3050, 3010, 0, 1, 1 },
+ { 0x0, 0x0, 174, 3051, 3011, 0, 1, 1 },
+ { 0x11, 0x31, 175, 2881, 417, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 418, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2073, 419, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 421, 12, 1, 4 },
+ { 0x1, 0x1, 175, -1, 425, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 426, 12, 1, 4 },
+ { 0x11, 0x11, 175, -1, 427, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 428, 12, 1, 4 },
+ { 0x1, 0x1, 175, 2079, 429, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 431, 12, 1, 4 },
+ { 0x11, 0x11, 175, 2081, 433, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 435, 12, 1, 4 },
+ { 0x1, 0x1, 175, 2083, 437, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 439, 12, 1, 4 },
+ { 0x11, 0x11, 175, 2085, 441, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 443, 12, 1, 4 },
+ { 0x1, 0x1, 175, 2087, 445, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 447, 12, 1, 4 },
+ { 0x11, 0x11, 175, 2089, 449, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 451, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2901, 457, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 458, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2095, 459, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 461, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2921, 465, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 466, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2121, 467, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 469, 12, 1, 4 },
+ { 0x1, 0x1, 175, -1, 473, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 474, 12, 1, 4 },
+ { 0x11, 0x11, 175, -1, 475, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 476, 12, 1, 4 },
+ { 0x1, 0x1, 175, 2127, 477, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 479, 12, 1, 4 },
+ { 0x11, 0x11, 175, 2129, 481, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 483, 12, 1, 4 },
+ { 0x1, 0x1, 175, 2131, 485, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 487, 12, 1, 4 },
+ { 0x11, 0x11, 175, 2133, 489, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 491, 12, 1, 4 },
+ { 0x1, 0x1, 175, 2135, 493, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 175, -1, 495, 12, 1, 4 },
+ { 0x11, 0x11, 175, 2137, 497, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 499, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2941, 505, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 506, 12, 1, 4 },
+ { 0x11, 0x31, 175, 2143, 507, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 175, -1, 509, 12, 1, 4 },
+ { 0x1, 0x1, 175, -1, 513, 33, 1, 4 },
+ { 0x200001, 0x200001, 175, -1, 514, 12, 1, 4 },
+ { 0x1, 0x1, 175, -1, 515, 33, 1, 4 },
+ { 0x200001, 0x200001, 175, -1, 516, 12, 1, 4 },
+ { 0x1, 0x1, 175, -1, 521, 33, 1, 79 },
+ { 0x200001, 0x200001, 175, -1, 522, 12, 1, 79 },
+ { 0x1, 0x1, 175, -1, 523, 33, 1, 79 },
+ { 0x200001, 0x200001, 175, -1, 524, 12, 1, 79 },
+ { 0x1, 0x1, 175, -1, 529, 33, 1, 4 },
+ { 0x200001, 0x200001, 175, -1, 530, 12, 1, 4 },
+ { 0x1, 0x1, 175, -1, 531, 33, 1, 4 },
+ { 0x200001, 0x200001, 175, -1, 532, 12, 1, 4 },
+ { 0x2200001, 0x6200001, 176, 2884, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2016, -1, 33, 1, 4 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x1, 0x1, 176, 2022, -1, 37, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2024, -1, 33, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x1, 0x1, 176, 2026, -1, 37, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2028, -1, 33, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x1, 0x1, 176, 2030, -1, 37, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2032, -1, 33, 1, 4 },
+ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, -1, -1, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x2200001, 0x6200001, 176, 2904, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2036, -1, 33, 1, 4 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x2200001, 0x6200001, 176, 2924, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2040, -1, 33, 1, 4 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x1, 0x1, 176, 2046, -1, 37, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2048, -1, 33, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x1, 0x1, 176, 2050, -1, 37, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2052, -1, 33, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x1, 0x1, 176, 2054, -1, 37, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2056, -1, 33, 1, 4 },
+ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, -1, -1, 33, 1, 4 },
+ { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 },
+ { 0x2200001, 0x6200001, 176, 2943, -1, 12, 1, 4 },
+ { 0x11, 0x11, 176, 2060, -1, 33, 1, 4 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 37, 1, 4 },
+ { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 33, 1, 5 },
+ { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 },
+ { 0x0, 0x0, 176, -1, -1, 0, 1, 5 },
+ { 0x1, 0x1, 176, -1, -1, 12, 1, 5 },
+ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 },
+ { 0x1, 0x1, 176, 397, -1, 33, 1, 4 },
+ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 },
+ { 0x200001, 0x200001, 176, 398, -1, 12, 1, 4 },
+ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 },
+ { 0x1, 0x1, 176, 399, -1, 33, 1, 4 },
+ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 },
+ { 0x200001, 0x200001, 176, 400, -1, 12, 1, 4 },
+ { 0x9, 0x9, 176, -1, -1, 33, 1, 80 },
+ { 0x1, 0x1, 176, 405, -1, 33, 1, 79 },
+ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 },
+ { 0x200001, 0x200001, 176, 406, -1, 12, 1, 79 },
+ { 0x9, 0x9, 176, -1, -1, 33, 1, 80 },
+ { 0x1, 0x1, 176, 407, -1, 33, 1, 79 },
+ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 },
+ { 0x200001, 0x200001, 176, 408, -1, 12, 1, 79 },
+ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 },
+ { 0x1, 0x1, 176, 413, -1, 33, 1, 4 },
+ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 },
+ { 0x200001, 0x200001, 176, 414, -1, 12, 1, 4 },
+ { 0x9, 0x9, 176, -1, -1, 33, 1, 5 },
+ { 0x1, 0x1, 176, 415, -1, 33, 1, 4 },
+ { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 },
+ { 0x200001, 0x200001, 176, 416, -1, 12, 1, 4 },
+ { 0x0, 0x0, 177, -1, 2327, 0, 0, -1 },
+ { 0x9, 0x9, 177, -1, 2335, 33, 1, 50 },
+ { 0x9, 0x9, 177, -1, 2993, 33, 1, 50 },
+ { 0x0, 0x0, 177, -1, 2380, 0, 0, -1 },
+ { 0x7, 0x7, 177, -1, -1, 27, 1, 50 },
+ { 0x1, 0x1, 197, -1, -1, 27, 1, 10 },
+ { 0x1, 0x1, 211, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 211, -1, -1, 29, 1, 0 },
+ { 0x2, 0x3, 211, 1169, -1, 27, 1, 34 },
+ { 0x0, 0x0, 211, 1170, -1, 0, 1, 34 },
+ { 0x0, 0x0, 211, 1171, -1, 0, 1, 0 },
+ { 0x0, 0x0, 211, 1172, -1, 0, 1, 0 },
+ { 0x0, 0x0, 211, 1173, -1, 0, 1, 0 },
+ { 0x0, 0x0, 211, 1174, -1, 0, 1, 0 },
+ { 0x0, 0x0, 211, 3026, -1, 0, 1, 100 },
+ { 0x0, 0x0, 211, 3027, -1, 0, 1, 100 },
+ { 0x0, 0x0, 211, 3028, 967, 0, 0, -1 },
+ { 0x1, 0x1, 212, -1, -1, 27, 1, 0 },
+ { 0x1, 0x1, 212, -1, -1, 27, 1, 0 },
+ { 0x1, 0x1, 213, -1, 1426, 32, 1, 142 },
+ { 0x1, 0x1, 213, -1, 1428, 32, 1, 142 },
+ { 0x1, 0x1, 213, -1, 1430, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1432, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1434, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1436, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1438, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1440, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1442, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1444, 32, 1, 141 },
+ { 0x1, 0x1, 213, -1, 1446, 32, 1, 143 },
+ { 0x1, 0x1, 213, -1, 1448, 32, 1, 143 },
+ { 0x1, 0x1, 213, -1, 1965, 32, 1, 138 },
+ { 0x1, 0x1, 213, -1, 1967, 32, 1, 145 },
+ { 0x1, 0x1, 213, -1, 1969, 32, 1, 139 },
+ { 0x1, 0x1, 213, -1, 1971, 32, 1, 139 },
+ { 0x1, 0x1, 213, -1, 1973, 32, 1, 138 },
+ { 0x1, 0x1, 213, -1, 1975, 32, 1, 145 },
+ { 0x1, 0x1, 213, -1, 1977, 32, 1, 138 },
+ { 0x1, 0x1, 213, -1, 1979, 32, 1, 145 },
+ { 0x1, 0x1, 213, 2783, 1981, 32, 1, 138 },
+ { 0x1, 0x1, 213, 2784, 1984, 32, 1, 145 },
+ { 0x0, 0x0, 214, -1, 2825, 0, 0, -1 },
+ { 0x0, 0x0, 214, -1, 2826, 0, 0, -1 },
+ { 0x0, 0x0, 214, -1, 2851, 0, 0, -1 },
+ { 0x5, 0x5, 214, -1, 2854, 20, 1, 68 },
+ { 0x0, 0x0, 218, 2209, 966, 0, 0, -1 },
+ { 0x0, 0x0, 219, -1, 1139, 0, 0, -1 },
+ { 0x0, 0x0, 219, -1, 1264, 0, 0, -1 },
+ { 0x0, 0x0, 219, -1, -1, 0, 1, 128 },
+ { 0x0, 0x0, 219, -1, -1, 0, 1, 67 },
+ { 0x1, 0x1, 219, 833, 2289, 36, 1, 66 },
+ { 0x1, 0x1, 219, 834, 2348, 36, 1, 66 },
+ { 0x0, 0x0, 219, 835, 2351, 0, 0, -1 },
+ { 0x1, 0x1, 219, 836, -1, 36, 1, 66 },
+ { 0x0, 0x0, 219, 1423, -1, 0, 1, 34 },
+ { 0x1, 0x1, 219, 837, 2356, 36, 1, 66 },
+ { 0x0, 0x0, 219, 838, 2359, 0, 0, -1 },
+ { 0x1, 0x1, 219, 839, -1, 36, 1, 66 },
+ { 0x0, 0x0, 219, 840, 2362, 0, 0, -1 },
+ { 0x1, 0x1, 219, 841, -1, 36, 1, 66 },
+ { 0x1, 0x1, 219, 842, 2365, 36, 1, 66 },
+ { 0x1, 0x1, 219, 843, 2368, 36, 1, 66 },
+ { 0x0, 0x0, 219, 1424, -1, 0, 1, 34 },
+ { 0x1, 0x1, 219, 844, 2401, 36, 1, 66 },
+ { 0x1, 0x1, 219, 845, -1, 31, 1, 144 },
+ { 0x1, 0x1, 219, 228, 1449, 32, 1, 133 },
+ { 0x1, 0x1, 219, 229, 1458, 32, 1, 133 },
+ { 0x1, 0x1, 219, 230, 1467, 32, 1, 133 },
+ { 0x1, 0x1, 219, 231, 1480, 32, 1, 133 },
+ { 0x1, 0x1, 219, 232, 1489, 32, 1, 133 },
+ { 0x1, 0x1, 219, 233, 1498, 32, 1, 133 },
+ { 0x1, 0x1, 219, 234, 1507, 32, 1, 133 },
+ { 0x1, 0x1, 219, 235, 1516, 32, 1, 133 },
+ { 0x1, 0x1, 219, 236, 1525, 32, 1, 133 },
+ { 0x1, 0x1, 219, 237, 1534, 32, 1, 133 },
+ { 0x1, 0x1, 219, 238, 1544, 32, 1, 133 },
+ { 0x1, 0x1, 219, 239, 1554, 32, 1, 133 },
+ { 0x1, 0x1, 219, 240, 1567, 32, 1, 148 },
+ { 0x1, 0x1, 219, 241, 1573, 32, 1, 153 },
+ { 0x1, 0x1, 219, 242, 1579, 32, 1, 153 },
+ { 0x1, 0x1, 219, 243, 1585, 32, 1, 148 },
+ { 0x1, 0x1, 219, 244, 1591, 32, 1, 153 },
+ { 0x1, 0x1, 219, 245, 1597, 32, 1, 153 },
+ { 0x1, 0x1, 219, 246, 1603, 32, 1, 148 },
+ { 0x1, 0x1, 219, 247, 1609, 32, 1, 153 },
+ { 0x1, 0x1, 219, 248, 1615, 32, 1, 153 },
+ { 0x1, 0x1, 219, 249, 1621, 32, 1, 148 },
+ { 0x1, 0x1, 219, 250, 1627, 32, 1, 153 },
+ { 0x1, 0x1, 219, 251, 1633, 32, 1, 148 },
+ { 0x1, 0x1, 219, 252, 1639, 32, 1, 153 },
+ { 0x1, 0x1, 219, 253, 1645, 32, 1, 148 },
+ { 0x1, 0x1, 219, 254, 1651, 32, 1, 153 },
+ { 0x1, 0x1, 219, 255, 1657, 32, 1, 148 },
+ { 0x1, 0x1, 219, 256, 1663, 32, 1, 153 },
+ { 0x1, 0x1, 219, 257, 1669, 32, 1, 153 },
+ { 0x1, 0x1, 219, 849, -1, 31, 1, 160 },
+ { 0x0, 0x0, 220, 2404, -1, 0, 1, 66 },
+ { 0x0, 0x0, 220, 2405, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 25, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2407, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2408, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2409, -1, 0, 1, 45 },
+ { 0x0, 0x0, 220, 2410, -1, 0, 1, 40 },
+ { 0x1, 0x1, 220, 2411, -1, 12, 1, 59 },
+ { 0x0, 0x0, 220, 2412, -1, 0, 1, 54 },
+ { 0x1000001, 0x1000001, 220, 2413, -1, 12, 1, 59 },
+ { 0x1, 0x1, 220, 2414, -1, 36, 1, 54 },
+ { 0x200001, 0x200001, 220, 2415, -1, 12, 1, 59 },
+ { 0x1, 0x1, 220, 2416, -1, 33, 1, 54 },
+ { 0x1200001, 0x1200001, 220, 2417, -1, 12, 1, 49 },
+ { 0x9, 0x9, 220, 2418, -1, 33, 1, 49 },
+ { 0x0, 0x0, 220, 2419, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2420, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2421, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2422, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2423, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2424, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2425, -1, 0, 1, 49 },
+ { 0x0, 0x0, 220, 2426, -1, 0, 1, 49 },
+ { 0x1, 0x1, 220, 2427, -1, 12, 1, 59 },
+ { 0x0, 0x0, 220, 2428, -1, 0, 1, 54 },
+ { 0x200001, 0x1200001, 220, 2429, -1, 12, 1, 59 },
+ { 0x1, 0x9, 220, 2430, -1, 33, 1, 54 },
+ { 0x0, 0x0, 220, 2431, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2432, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2433, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2434, -1, 0, 1, 54 },
+ { 0x1, 0x1, 220, 2435, -1, 12, 1, 59 },
+ { 0x0, 0x0, 220, 2436, -1, 0, 1, 54 },
+ { 0x1000001, 0x1000001, 220, 2437, -1, 12, 1, 59 },
+ { 0x1, 0x1, 220, 2438, -1, 36, 1, 54 },
+ { 0x200001, 0x200001, 220, 2439, -1, 12, 1, 59 },
+ { 0x1, 0x1, 220, 2440, -1, 33, 1, 54 },
+ { 0x1200001, 0x1200001, 220, 2441, -1, 12, 1, 49 },
+ { 0x9, 0x9, 220, 2442, -1, 33, 1, 49 },
+ { 0x0, 0x0, 220, 2443, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2444, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2445, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2446, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2447, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2448, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2449, -1, 0, 1, 49 },
+ { 0x0, 0x0, 220, 2450, -1, 0, 1, 49 },
+ { 0x1, 0x1, 220, 2451, -1, 12, 1, 59 },
+ { 0x0, 0x0, 220, 2452, -1, 0, 1, 54 },
+ { 0x200001, 0x1200001, 220, 2453, -1, 12, 1, 59 },
+ { 0x1, 0x9, 220, 2454, -1, 33, 1, 54 },
+ { 0x0, 0x0, 220, 2455, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2456, -1, 0, 1, 54 },
+ { 0x0, 0x0, 220, 2457, -1, 0, 1, 59 },
+ { 0x0, 0x0, 220, 2458, -1, 0, 1, 54 },
+ { 0x1, 0x1, 220, 2459, -1, 28, 1, 29 },
+ { 0x0, 0x0, 220, 2460, -1, 0, 1, 29 },
+ { 0x3, 0x3, 220, 2461, -1, 27, 1, 29 },
+ { 0x1, 0x1, 220, 2462, -1, 27, 1, 29 },
+ { 0x0, 0x0, 220, 2463, -1, 0, 1, 66 },
+ { 0x0, 0x0, 220, 2464, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2465, -1, 0, 1, 29 },
+ { 0x1, 0x1, 220, 2466, -1, 36, 1, 66 },
+ { 0x1, 0x1, 220, 2467, -1, 37, 1, 29 },
+ { 0x0, 0x0, 220, 2468, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2469, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2470, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2471, -1, 0, 1, 66 },
+ { 0x0, 0x0, 220, 2472, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 37, -1, 0, 1, 29 },
+ { 0x1, 0x1, 220, 2474, -1, 36, 1, 66 },
+ { 0x1, 0x1, 220, 2475, -1, 37, 1, 29 },
+ { 0x0, 0x0, 220, 2476, -1, 0, 1, 29 },
+ { 0x1, 0x1, 220, 2477, -1, 36, 1, 66 },
+ { 0x1, 0x1, 220, 2478, -1, 37, 1, 29 },
+ { 0x0, 0x0, 220, 2479, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2480, -1, 0, 1, 66 },
+ { 0x0, 0x0, 220, 2481, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 42, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2483, -1, 0, 1, 66 },
+ { 0x0, 0x0, 220, 2484, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 43, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2486, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2487, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2488, -1, 0, 1, 49 },
+ { 0x1, 0x1, 220, 2489, -1, 27, 1, 49 },
+ { 0x1, 0x1, 220, 2490, -1, 28, 1, 49 },
+ { 0x3, 0x3, 220, 2491, -1, 27, 1, 49 },
+ { 0x1, 0x1, 220, 2492, -1, 29, 1, 49 },
+ { 0x5, 0x5, 220, 2493, -1, 27, 1, 49 },
+ { 0x3, 0x3, 220, 2494, -1, 28, 1, 49 },
+ { 0x7, 0x7, 220, 2495, -1, 27, 1, 49 },
+ { 0x0, 0x0, 220, 2496, -1, 0, 1, 49 },
+ { 0x0, 0x0, 220, 2497, -1, 0, 1, 49 },
+ { 0x0, 0x0, 220, 2498, -1, 0, 1, 49 },
+ { 0x0, 0x0, 220, 2499, -1, 0, 1, 49 },
+ { 0x1, 0x1, 220, 2500, -1, 28, 1, 29 },
+ { 0x0, 0x0, 220, 2501, -1, 0, 1, 29 },
+ { 0x3, 0x3, 220, 2502, -1, 27, 1, 29 },
+ { 0x1, 0x1, 220, 2503, -1, 27, 1, 29 },
+ { 0x0, 0x0, 220, 2504, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2505, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2506, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 52, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2508, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2509, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 57, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 2511, -1, 0, 1, 24 },
+ { 0x0, 0x0, 220, 2512, -1, 0, 1, 24 },
+ { 0x0, 0x0, 220, 2513, -1, 0, 1, 24 },
+ { 0x0, 0x0, 220, 2514, -1, 0, 1, 24 },
+ { 0x0, 0x0, 220, 2515, -1, 0, 1, 35 },
+ { 0x0, 0x0, 220, 2516, -1, 0, 1, 66 },
+ { 0x0, 0x0, 220, 2517, -1, 0, 1, 29 },
+ { 0x0, 0x0, 220, 64, -1, 0, 1, 29 },
+ { 0x1, 0x1, 221, 2519, -1, 34, 1, 66 },
+ { 0x1, 0x1, 221, 2520, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2521, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2522, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2523, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2524, -1, 34, 1, 46 },
+ { 0x1, 0x1, 221, 2525, -1, 34, 1, 42 },
+ { 0x400001, 0x400001, 221, 2526, -1, 12, 1, 61 },
+ { 0x1, 0x1, 221, 2527, -1, 34, 1, 56 },
+ { 0x1400001, 0x1400001, 221, 2528, -1, 12, 1, 61 },
+ { 0x5, 0x5, 221, 2529, -1, 34, 1, 56 },
+ { 0x600001, 0x600001, 221, 2530, -1, 12, 1, 61 },
+ { 0x3, 0x3, 221, 2531, -1, 33, 1, 56 },
+ { 0x1600001, 0x1600001, 221, 2532, -1, 12, 1, 51 },
+ { 0xb, 0xb, 221, 2533, -1, 33, 1, 51 },
+ { 0x1, 0x1, 221, 2534, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2535, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2536, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2537, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2538, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2539, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2540, -1, 34, 1, 51 },
+ { 0x1, 0x1, 221, 2541, -1, 34, 1, 51 },
+ { 0x400001, 0x400001, 221, 2542, -1, 12, 1, 61 },
+ { 0x1, 0x1, 221, 2543, -1, 34, 1, 56 },
+ { 0x600001, 0x1600001, 221, 2544, -1, 12, 1, 61 },
+ { 0x3, 0xb, 221, 2545, -1, 33, 1, 56 },
+ { 0x1, 0x1, 221, 2546, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2547, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2548, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2549, -1, 34, 1, 56 },
+ { 0x400001, 0x400001, 221, 2550, -1, 12, 1, 61 },
+ { 0x1, 0x1, 221, 2551, -1, 34, 1, 56 },
+ { 0x1400001, 0x1400001, 221, 2552, -1, 12, 1, 61 },
+ { 0x5, 0x5, 221, 2553, -1, 34, 1, 56 },
+ { 0x600001, 0x600001, 221, 2554, -1, 12, 1, 61 },
+ { 0x3, 0x3, 221, 2555, -1, 33, 1, 56 },
+ { 0x1600001, 0x1600001, 221, 2556, -1, 12, 1, 51 },
+ { 0xb, 0xb, 221, 2557, -1, 33, 1, 51 },
+ { 0x1, 0x1, 221, 2558, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2559, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2560, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2561, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2562, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2563, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2564, -1, 34, 1, 51 },
+ { 0x1, 0x1, 221, 2565, -1, 34, 1, 51 },
+ { 0x400001, 0x400001, 221, 2566, -1, 12, 1, 61 },
+ { 0x1, 0x1, 221, 2567, -1, 34, 1, 56 },
+ { 0x600001, 0x1600001, 221, 2568, -1, 12, 1, 61 },
+ { 0x3, 0xb, 221, 2569, -1, 33, 1, 56 },
+ { 0x1, 0x1, 221, 2570, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2571, -1, 34, 1, 56 },
+ { 0x1, 0x1, 221, 2572, -1, 34, 1, 61 },
+ { 0x1, 0x1, 221, 2573, -1, 34, 1, 56 },
+ { 0x41, 0x41, 221, 2574, -1, 28, 1, 31 },
+ { 0x1, 0x1, 221, 2575, -1, 34, 1, 31 },
+ { 0x83, 0x83, 221, 2576, -1, 27, 1, 31 },
+ { 0x81, 0x81, 221, 2577, -1, 27, 1, 31 },
+ { 0x1, 0x1, 221, 2578, -1, 34, 1, 66 },
+ { 0x1, 0x1, 221, 2579, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2580, -1, 34, 1, 31 },
+ { 0x5, 0x5, 221, 2581, -1, 34, 1, 66 },
+ { 0x9, 0x9, 221, 2582, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2583, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2584, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2585, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2586, -1, 34, 1, 66 },
+ { 0x1, 0x1, 221, 2587, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2588, -1, 34, 1, 31 },
+ { 0x5, 0x5, 221, 2589, -1, 34, 1, 66 },
+ { 0x9, 0x9, 221, 2590, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2591, -1, 34, 1, 31 },
+ { 0x5, 0x5, 221, 2592, -1, 34, 1, 66 },
+ { 0x9, 0x9, 221, 2593, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2594, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2595, -1, 34, 1, 66 },
+ { 0x1, 0x1, 221, 2596, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2597, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2598, -1, 34, 1, 66 },
+ { 0x1, 0x1, 221, 2599, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2600, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2601, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2602, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2603, -1, 34, 1, 51 },
+ { 0x81, 0x81, 221, 2604, -1, 27, 1, 51 },
+ { 0x41, 0x41, 221, 2605, -1, 28, 1, 51 },
+ { 0x83, 0x83, 221, 2606, -1, 27, 1, 51 },
+ { 0x21, 0x21, 221, 2607, -1, 29, 1, 51 },
+ { 0x85, 0x85, 221, 2608, -1, 27, 1, 51 },
+ { 0x43, 0x43, 221, 2609, -1, 28, 1, 51 },
+ { 0x87, 0x87, 221, 2610, -1, 27, 1, 51 },
+ { 0x1, 0x1, 221, 2611, -1, 34, 1, 51 },
+ { 0x1, 0x1, 221, 2612, -1, 34, 1, 51 },
+ { 0x1, 0x1, 221, 2613, -1, 34, 1, 51 },
+ { 0x1, 0x1, 221, 2614, -1, 34, 1, 51 },
+ { 0x41, 0x41, 221, 2615, -1, 28, 1, 31 },
+ { 0x1, 0x1, 221, 2616, -1, 34, 1, 31 },
+ { 0x83, 0x83, 221, 2617, -1, 27, 1, 31 },
+ { 0x81, 0x81, 221, 2618, -1, 27, 1, 31 },
+ { 0x1, 0x1, 221, 2619, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2620, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2621, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2622, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2623, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2624, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2625, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2626, -1, 34, 1, 26 },
+ { 0x1, 0x1, 221, 2627, -1, 34, 1, 26 },
+ { 0x1, 0x1, 221, 2628, -1, 34, 1, 26 },
+ { 0x1, 0x1, 221, 2629, -1, 34, 1, 26 },
+ { 0x1, 0x1, 221, 2630, -1, 34, 1, 37 },
+ { 0x1, 0x1, 221, 2631, -1, 34, 1, 66 },
+ { 0x1, 0x1, 221, 2632, -1, 34, 1, 31 },
+ { 0x1, 0x1, 221, 2633, -1, 34, 1, 31 },
+ { 0x1, 0x1, 222, 2634, -1, 35, 1, 66 },
+ { 0x1, 0x1, 222, 2635, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2636, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2637, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2638, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2639, -1, 35, 1, 47 },
+ { 0x1, 0x1, 222, 2640, -1, 35, 1, 43 },
+ { 0x800001, 0x800001, 222, 2641, -1, 12, 1, 62 },
+ { 0x1, 0x1, 222, 2642, -1, 35, 1, 57 },
+ { 0x1800001, 0x1800001, 222, 2643, -1, 12, 1, 62 },
+ { 0x3, 0x3, 222, 2644, -1, 35, 1, 57 },
+ { 0xa00001, 0xa00001, 222, 2645, -1, 12, 1, 62 },
+ { 0x5, 0x5, 222, 2646, -1, 33, 1, 57 },
+ { 0x1a00001, 0x1a00001, 222, 2647, -1, 12, 1, 52 },
+ { 0xd, 0xd, 222, 2648, -1, 33, 1, 52 },
+ { 0x1, 0x1, 222, 2649, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2650, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2651, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2652, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2653, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2654, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2655, -1, 35, 1, 52 },
+ { 0x1, 0x1, 222, 2656, -1, 35, 1, 52 },
+ { 0x800001, 0x800001, 222, 2657, -1, 12, 1, 62 },
+ { 0x1, 0x1, 222, 2658, -1, 35, 1, 57 },
+ { 0xa00001, 0x1a00001, 222, 2659, -1, 12, 1, 62 },
+ { 0x5, 0xd, 222, 2660, -1, 33, 1, 57 },
+ { 0x1, 0x1, 222, 2661, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2662, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2663, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2664, -1, 35, 1, 57 },
+ { 0x800001, 0x800001, 222, 2665, -1, 12, 1, 62 },
+ { 0x1, 0x1, 222, 2666, -1, 35, 1, 57 },
+ { 0x1800001, 0x1800001, 222, 2667, -1, 12, 1, 62 },
+ { 0x3, 0x3, 222, 2668, -1, 35, 1, 57 },
+ { 0xa00001, 0xa00001, 222, 2669, -1, 12, 1, 62 },
+ { 0x5, 0x5, 222, 2670, -1, 33, 1, 57 },
+ { 0x1a00001, 0x1a00001, 222, 2671, -1, 12, 1, 52 },
+ { 0xd, 0xd, 222, 2672, -1, 33, 1, 52 },
+ { 0x1, 0x1, 222, 2673, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2674, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2675, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2676, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2677, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2678, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2679, -1, 35, 1, 52 },
+ { 0x1, 0x1, 222, 2680, -1, 35, 1, 52 },
+ { 0x800001, 0x800001, 222, 2681, -1, 12, 1, 62 },
+ { 0x1, 0x1, 222, 2682, -1, 35, 1, 57 },
+ { 0xa00001, 0x1a00001, 222, 2683, -1, 12, 1, 62 },
+ { 0x5, 0xd, 222, 2684, -1, 33, 1, 57 },
+ { 0x1, 0x1, 222, 2685, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2686, -1, 35, 1, 57 },
+ { 0x1, 0x1, 222, 2687, -1, 35, 1, 62 },
+ { 0x1, 0x1, 222, 2688, -1, 35, 1, 57 },
+ { 0x81, 0x81, 222, 2689, -1, 28, 1, 32 },
+ { 0x1, 0x1, 222, 2690, -1, 35, 1, 32 },
+ { 0x103, 0x103, 222, 2691, -1, 27, 1, 32 },
+ { 0x101, 0x101, 222, 2692, -1, 27, 1, 32 },
+ { 0x1, 0x1, 222, 2693, -1, 35, 1, 66 },
+ { 0x1, 0x1, 222, 2694, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2695, -1, 35, 1, 32 },
+ { 0x3, 0x3, 222, 2696, -1, 35, 1, 66 },
+ { 0x5, 0x5, 222, 2697, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2698, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2699, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2700, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2701, -1, 35, 1, 66 },
+ { 0x1, 0x1, 222, 2702, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2703, -1, 35, 1, 32 },
+ { 0x3, 0x3, 222, 2704, -1, 35, 1, 66 },
+ { 0x5, 0x5, 222, 2705, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2706, -1, 35, 1, 32 },
+ { 0x3, 0x3, 222, 2707, -1, 35, 1, 66 },
+ { 0x5, 0x5, 222, 2708, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2709, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2710, -1, 35, 1, 66 },
+ { 0x1, 0x1, 222, 2711, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2712, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2713, -1, 35, 1, 66 },
+ { 0x1, 0x1, 222, 2714, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2715, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2716, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2717, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2718, -1, 35, 1, 52 },
+ { 0x101, 0x101, 222, 2719, -1, 27, 1, 52 },
+ { 0x81, 0x81, 222, 2720, -1, 28, 1, 52 },
+ { 0x103, 0x103, 222, 2721, -1, 27, 1, 52 },
+ { 0x41, 0x41, 222, 2722, -1, 29, 1, 52 },
+ { 0x105, 0x105, 222, 2723, -1, 27, 1, 52 },
+ { 0x83, 0x83, 222, 2724, -1, 28, 1, 52 },
+ { 0x107, 0x107, 222, 2725, -1, 27, 1, 52 },
+ { 0x1, 0x1, 222, 2726, -1, 35, 1, 52 },
+ { 0x1, 0x1, 222, 2727, -1, 35, 1, 52 },
+ { 0x1, 0x1, 222, 2728, -1, 35, 1, 52 },
+ { 0x1, 0x1, 222, 2729, -1, 35, 1, 52 },
+ { 0x81, 0x81, 222, 2730, -1, 28, 1, 32 },
+ { 0x1, 0x1, 222, 2731, -1, 35, 1, 32 },
+ { 0x103, 0x103, 222, 2732, -1, 27, 1, 32 },
+ { 0x101, 0x101, 222, 2733, -1, 27, 1, 32 },
+ { 0x1, 0x1, 222, 2734, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2735, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2736, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2737, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2738, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2739, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2740, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2741, -1, 35, 1, 27 },
+ { 0x1, 0x1, 222, 2742, -1, 35, 1, 27 },
+ { 0x1, 0x1, 222, 2743, -1, 35, 1, 27 },
+ { 0x1, 0x1, 222, 2744, -1, 35, 1, 27 },
+ { 0x1, 0x1, 222, 2745, -1, 35, 1, 38 },
+ { 0x1, 0x1, 222, 2746, -1, 35, 1, 66 },
+ { 0x1, 0x1, 222, 2747, -1, 35, 1, 32 },
+ { 0x1, 0x1, 222, 2748, -1, 35, 1, 32 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 66 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2243, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 48 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 44 },
+ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 },
+ { 0x3, 0x3, 223, 2964, -1, 34, 1, 58 },
+ { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 },
+ { 0x7, 0x7, 223, 2965, -1, 34, 1, 58 },
+ { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 },
+ { 0x7, 0x7, 223, 2966, -1, 33, 1, 58 },
+ { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 },
+ { 0xf, 0xf, 223, 2967, -1, 33, 1, 53 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2968, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2969, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2970, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0x3, 0x3, 223, 2971, -1, 34, 1, 53 },
+ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 },
+ { 0x3, 0x3, 223, 2976, -1, 34, 1, 58 },
+ { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 },
+ { 0x7, 0xf, 223, 2977, -1, 33, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2978, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2979, -1, 34, 1, 58 },
+ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 },
+ { 0x3, 0x3, 223, 2982, -1, 34, 1, 58 },
+ { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 },
+ { 0x7, 0x7, 223, 2983, -1, 34, 1, 58 },
+ { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 },
+ { 0x7, 0x7, 223, 2984, -1, 33, 1, 58 },
+ { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 },
+ { 0xf, 0xf, 223, 2985, -1, 33, 1, 53 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2986, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2987, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2988, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0x3, 0x3, 223, 2989, -1, 34, 1, 53 },
+ { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 },
+ { 0x3, 0x3, 223, 2994, -1, 34, 1, 58 },
+ { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 },
+ { 0x7, 0xf, 223, 2995, -1, 33, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2996, -1, 34, 1, 58 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 63 },
+ { 0x3, 0x3, 223, 2997, -1, 34, 1, 58 },
+ { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 },
+ { 0x3, 0x3, 223, 2862, -1, 34, 1, 33 },
+ { 0x183, 0x183, 223, -1, -1, 27, 1, 33 },
+ { 0x181, 0x181, 223, 2863, -1, 27, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 66 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2244, -1, 34, 1, 33 },
+ { 0x7, 0x7, 223, -1, -1, 34, 1, 66 },
+ { 0xb, 0xb, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2245, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 66 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2248, -1, 34, 1, 33 },
+ { 0x7, 0x7, 223, -1, -1, 34, 1, 66 },
+ { 0xb, 0xb, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2249, -1, 34, 1, 33 },
+ { 0x7, 0x7, 223, -1, -1, 34, 1, 66 },
+ { 0xb, 0xb, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2251, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 66 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2253, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 66 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2254, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0x181, 0x181, 223, -1, -1, 27, 1, 53 },
+ { 0xc1, 0xc1, 223, -1, -1, 28, 1, 53 },
+ { 0x183, 0x183, 223, -1, -1, 27, 1, 53 },
+ { 0x61, 0x61, 223, -1, -1, 29, 1, 53 },
+ { 0x185, 0x185, 223, -1, -1, 27, 1, 53 },
+ { 0xc3, 0xc3, 223, -1, -1, 28, 1, 53 },
+ { 0x187, 0x187, 223, -1, -1, 27, 1, 53 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 53 },
+ { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 },
+ { 0x3, 0x3, 223, 2866, -1, 34, 1, 33 },
+ { 0x183, 0x183, 223, -1, -1, 27, 1, 33 },
+ { 0x181, 0x181, 223, 2867, -1, 27, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 28 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 28 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 28 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 28 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 39 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 66 },
+ { 0x3, 0x3, 223, -1, -1, 34, 1, 33 },
+ { 0x3, 0x3, 223, 2256, -1, 34, 1, 33 },
+ { 0x3, 0x3, 224, 540, 1451, 32, 1, 135 },
+ { 0x3, 0x3, 224, 541, 1460, 32, 1, 135 },
+ { 0x3, 0x3, 224, 542, 1469, 32, 1, 135 },
+ { 0x3, 0x3, 224, 543, 1482, 32, 1, 135 },
+ { 0x3, 0x3, 224, 544, 1491, 32, 1, 135 },
+ { 0x3, 0x3, 224, 545, 1500, 32, 1, 135 },
+ { 0x3, 0x3, 224, 546, 1509, 32, 1, 135 },
+ { 0x3, 0x3, 224, 547, 1518, 32, 1, 135 },
+ { 0x3, 0x3, 224, 548, 1527, 32, 1, 135 },
+ { 0x3, 0x3, 224, 549, 1536, 32, 1, 135 },
+ { 0x3, 0x3, 224, 550, 1546, 32, 1, 135 },
+ { 0x3, 0x3, 224, 551, 1556, 32, 1, 135 },
+ { 0x3, 0x3, 224, 564, 1569, 32, 1, 150 },
+ { 0x3, 0x3, 224, 565, 1575, 32, 1, 155 },
+ { 0x3, 0x3, 224, 566, 1581, 32, 1, 155 },
+ { 0x3, 0x3, 224, 567, 1587, 32, 1, 150 },
+ { 0x3, 0x3, 224, 568, 1593, 32, 1, 155 },
+ { 0x3, 0x3, 224, 569, 1599, 32, 1, 155 },
+ { 0x3, 0x3, 224, 570, 1605, 32, 1, 150 },
+ { 0x3, 0x3, 224, 571, 1611, 32, 1, 155 },
+ { 0x3, 0x3, 224, 572, 1617, 32, 1, 155 },
+ { 0x3, 0x3, 224, 573, 1623, 32, 1, 150 },
+ { 0x3, 0x3, 224, 574, 1629, 32, 1, 155 },
+ { 0x3, 0x3, 224, 575, 1635, 32, 1, 150 },
+ { 0x3, 0x3, 224, 576, 1641, 32, 1, 155 },
+ { 0x3, 0x3, 224, 577, 1647, 32, 1, 150 },
+ { 0x3, 0x3, 224, 578, 1653, 32, 1, 155 },
+ { 0x3, 0x3, 224, 579, 1659, 32, 1, 150 },
+ { 0x3, 0x3, 224, 580, 1665, 32, 1, 155 },
+ { 0x3, 0x3, 224, 581, 1671, 32, 1, 155 },
+ { 0x1, 0x1, 225, -1, -1, 28, 1, 34 },
+ { 0x1, 0x1, 225, -1, -1, 28, 1, 34 },
+ { 0x0, 0x0, 232, 958, -1, 0, 1, 144 },
+ { 0x0, 0x0, 232, 959, -1, 0, 1, 160 },
+ { 0x1, 0x1, 233, -1, 1982, 33, 1, 140 },
+ { 0x1, 0x1, 233, -1, 1985, 33, 1, 146 },
+ { 0x0, 0x0, 233, -1, 1987, 0, 1, 157 },
+ { 0x0, 0x0, 233, -1, 1988, 0, 1, 161 },
+ { 0x0, 0x0, 234, 883, 971, 0, 0, -1 },
+ { 0x0, 0x0, 234, 884, 979, 0, 0, -1 },
+ { 0x0, 0x0, 234, 885, 975, 0, 0, -1 },
+ { 0x1, 0x1, 234, 886, 620, 33, 1, 6 },
+ { 0x8000001, 0x8000001, 234, 887, 628, 6, 1, 7 },
+ { 0x1, 0x1, 234, 888, 624, 33, 1, 6 },
+ { 0x0, 0x0, 234, 889, 983, 0, 0, -1 },
+ { 0x1, 0x1, 234, 890, 640, 33, 1, 8 },
+ { 0x0, 0x0, 234, 891, 987, 0, 0, -1 },
+ { 0x1, 0x1, 234, 892, 652, 33, 1, 16 },
+ { 0x0, 0x0, 234, 893, 992, 0, 0, -1 },
+ { 0x0, 0x0, 234, 894, 996, 0, 0, -1 },
+ { 0x1, 0x1, 234, 895, 675, 33, 1, 18 },
+ { 0x1, 0x1, 234, 896, 679, 33, 1, 18 },
+ { 0x0, 0x0, 234, 897, 1000, 0, 0, -1 },
+ { 0x0, 0x0, 234, 898, 1004, 0, 0, -1 },
+ { 0x1, 0x1, 234, 899, 699, 33, 1, 19 },
+ { 0x8000001, 0x8000001, 234, 900, 703, 6, 1, 19 },
+ { 0x0, 0x0, 234, 901, 1008, 0, 0, -1 },
+ { 0x1, 0x1, 234, 902, 715, 33, 1, 20 },
+ { 0x0, 0x0, 234, 903, 1012, 0, 0, -1 },
+ { 0x0, 0x0, 234, 904, 1016, 0, 0, -1 },
+ { 0x1, 0x1, 234, 905, 735, 33, 1, 21 },
+ { 0x8000001, 0x8000001, 234, 906, 739, 6, 1, 21 },
+ { 0x0, 0x0, 234, 907, 1020, 0, 0, -1 },
+ { 0x1, 0x1, 234, 908, 751, 33, 1, 22 },
+ { 0x0, 0x0, 234, 909, 1025, 0, 0, -1 },
+ { 0x0, 0x0, 234, 910, 1029, 0, 0, -1 },
+ { 0x1, 0x1, 234, 911, 774, 33, 1, 18 },
+ { 0x1, 0x1, 234, 912, 778, 33, 1, 18 },
+ { 0x0, 0x0, 234, 913, 1033, 0, 0, -1 },
+ { 0x1, 0x1, 234, 914, 790, 33, 1, 22 },
+ { 0x0, 0x0, 235, 2787, 970, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2788, 978, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2789, 974, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2790, 619, 0, 1, 6 },
+ { 0x1, 0x1, 235, 2791, 627, 6, 1, 7 },
+ { 0x0, 0x0, 235, 2792, 623, 0, 1, 6 },
+ { 0x0, 0x0, 235, 2793, 982, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2794, 639, 0, 1, 8 },
+ { 0x0, 0x0, 235, 2795, 986, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2796, 651, 0, 1, 16 },
+ { 0x0, 0x0, 235, 2797, 991, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2798, 995, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2799, 674, 0, 1, 18 },
+ { 0x0, 0x0, 235, 2800, 678, 0, 1, 18 },
+ { 0x0, 0x0, 235, 2801, 999, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2802, 1003, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2803, 698, 0, 1, 19 },
+ { 0x1, 0x1, 235, 2804, 702, 6, 1, 19 },
+ { 0x0, 0x0, 235, 2805, 1007, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2806, 714, 0, 1, 20 },
+ { 0x0, 0x0, 235, 2807, 1011, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2808, 1015, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2809, 734, 0, 1, 21 },
+ { 0x1, 0x1, 235, 2810, 738, 6, 1, 21 },
+ { 0x0, 0x0, 235, 2811, 1019, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2812, 750, 0, 1, 22 },
+ { 0x0, 0x0, 235, 2813, 1024, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2814, 1028, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2815, 773, 0, 1, 18 },
+ { 0x0, 0x0, 235, 2816, 777, 0, 1, 18 },
+ { 0x0, 0x0, 235, 2817, 1032, 0, 0, -1 },
+ { 0x0, 0x0, 235, 2818, 789, 0, 1, 22 },
+ { 0x1, 0x1, 235, 915, 1155, 27, 1, 17 },
+ { 0x0, 0x0, 235, 916, 1153, 0, 1, 17 },
+ { 0x0, 0x0, 235, 1220, 1157, 0, 1, 23 },
+ { 0x0, 0x1, 235, 1165, 1163, 20, 1, 68 },
+ { 0x0, 0x0, 235, 111, 1161, 0, 1, 68 },
+ { 0x1, 0x1, 238, -1, -1, 29, 1, 0 },
+ { 0x0, 0x0, 238, -1, -1, 0, 1, 0 },
+ { 0x1, 0x1, 238, 3022, -1, 27, 1, 0 },
+ { 0x1, 0x1, 238, 3023, -1, 27, 1, 0 },
+ { 0x1, 0x1, 238, 3024, -1, 27, 1, 0 },
+ { 0x1, 0x1, 238, 3025, -1, 27, 1, 0 },
+ { 0x0, 0x0, 261, -1, 2344, 0, 0, -1 },
+ { 0x0, 0x0, 261, -1, 2346, 0, 0, -1 },
+ { 0x1, 0x1, 261, -1, -1, 28, 1, 30 },
+ { 0x1, 0x1, 261, -1, -1, 28, 1, 30 },
+ { 0x0, 0x0, 261, -1, 2385, 0, 0, -1 },
+ { 0x0, 0x0, 261, -1, 2387, 0, 0, -1 },
+ { 0x1, 0x1, 261, -1, -1, 28, 1, 30 },
+ { 0x1, 0x1, 261, -1, -1, 28, 1, 30 },
+ { 0x0, 0x0, 263, 23, -1, 0, 1, 0 },
+ { 0x0, 0x0, 263, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 263, -1, -1, 0, 1, 0 },
+ { 0x0, 0x1, 263, -1, -1, 29, 1, 0 },
+ { 0x0, 0x1, 263, -1, -1, 29, 1, 0 },
+ { 0x0, 0x1, 263, -1, -1, 29, 1, 0 },
+ { 0x0, 0x1, 263, -1, -1, 29, 1, 0 },
+ { 0x0, 0x1, 263, -1, -1, 29, 1, 0 },
+ { 0x0, 0x0, 263, 180, -1, 0, 1, 0 },
+ { 0x0, 0x1, 263, -1, -1, 29, 1, 0 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 301, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 323, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 349, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 371, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 65 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 65 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 65 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 65 },
+ { 0x0, 0x0, 264, -1, 2296, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2298, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2300, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2302, 0, 0, -1 },
+ { 0x1, 0x1, 264, -1, 2304, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2306, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2308, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2310, 12, 1, 50 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 50 },
+ { 0x0, 0x0, 264, -1, 2312, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2314, 0, 0, -1 },
+ { 0x1, 0x1, 264, -1, 2316, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2318, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x0, 0x0, 264, -1, 2320, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2322, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2324, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2326, 0, 0, -1 },
+ { 0x1, 0x1, 264, -1, 2328, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2330, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2332, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2334, 12, 1, 50 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 50 },
+ { 0x0, 0x0, 264, -1, 2336, 0, 0, -1 },
+ { 0x0, 0x0, 264, -1, 2338, 0, 0, -1 },
+ { 0x1, 0x1, 264, -1, 2340, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, 2342, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, -1, -1, 12, 1, 60 },
+ { 0x1, 0x1, 264, 393, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 395, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 517, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 519, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 401, -1, 12, 1, 77 },
+ { 0x1, 0x1, 264, 403, -1, 12, 1, 77 },
+ { 0x1, 0x1, 264, 525, -1, 12, 1, 77 },
+ { 0x1, 0x1, 264, 527, -1, 12, 1, 77 },
+ { 0x1, 0x1, 264, 409, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 411, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 533, -1, 12, 1, 2 },
+ { 0x1, 0x1, 264, 535, -1, 12, 1, 2 },
+ { 0x0, 0x0, 265, -1, 2303, 0, 0, -1 },
+ { 0x9, 0x9, 265, -1, 2311, 33, 1, 50 },
+ { 0x9, 0x9, 265, -1, 2975, 33, 1, 50 },
+ { 0x0, 0x0, 265, 1399, 2376, 0, 0, -1 },
+ { 0x3, 0x3, 265, 1400, -1, 27, 1, 50 },
+ { 0x0, 0x0, 269, 2856, -1, 0, 1, 0 },
+ { 0x3, 0x3, 270, -1, -1, 27, 1, 0 },
+ { 0x3, 0x3, 270, -1, -1, 27, 1, 0 },
+ { 0x3, 0x3, 270, -1, -1, 27, 1, 0 },
+ { 0x3, 0x3, 270, -1, -1, 27, 1, 0 },
+ { 0x1, 0x1, 271, 3018, -1, 28, 1, 0 },
+ { 0x1, 0x1, 271, 3019, -1, 28, 1, 0 },
+ { 0x1, 0x1, 271, 3020, -1, 28, 1, 0 },
+ { 0x1, 0x1, 271, 3021, -1, 28, 1, 0 },
+ { 0x1, 0x1, 273, -1, -1, 27, 1, 100 },
+ { 0x1, 0x1, 273, -1, -1, 27, 1, 100 },
+ { 0x0, 0x0, 273, -1, 968, 0, 0, -1 },
+ { 0x0, 0x0, 274, 3031, 2833, 0, 0, -1 },
+ { 0x0, 0x0, 274, 3032, 2835, 0, 0, -1 },
+ { 0x0, 0x0, 275, -1, 2834, 0, 0, -1 },
+ { 0x0, 0x0, 275, -1, 2836, 0, 0, -1 },
+ { 0x0, 0x0, 276, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 276, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 276, -1, -1, 0, 1, 41 },
+ { 0x0, 0x0, 281, -1, -1, 0, 1, 34 },
+ { 0x0, 0x0, 285, -1, 2350, 0, 1, 30 },
+ { 0x0, 0x0, 286, -1, -1, 0, 1, 0 },
+ { 0x0, 0x0, 286, -1, -1, 0, 1, 72 },
+ { 0x0, 0x0, 286, 2001, 3000, 0, 1, 1 },
+ { 0x0, 0x0, 286, 2002, 3001, 0, 1, 1 },
+ { 0x0, 0x0, 286, -1, 518, 0, 0, -1 },
+ { 0x0, 0x0, 286, -1, 520, 0, 0, -1 },
+ { 0x0, 0x0, 286, 2005, 3004, 0, 1, 76 },
+ { 0x0, 0x0, 286, 2006, 3005, 0, 1, 76 },
+ { 0x0, 0x0, 286, -1, 526, 0, 0, -1 },
+ { 0x0, 0x0, 286, -1, 528, 0, 0, -1 },
+ { 0x0, 0x0, 286, 2009, 3008, 0, 1, 1 },
+ { 0x0, 0x0, 286, 2010, 3009, 0, 1, 1 },
+ { 0x0, 0x0, 286, -1, 534, 0, 0, -1 },
+ { 0x0, 0x0, 286, -1, 536, 0, 0, -1 },
+};
+
+static const struct ia64_main_table
+main_table[] = {
+ { 5, 1, 1, 0x0000010000000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 0, },
+ { 5, 1, 1, 0x0000010008000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 1, },
+ { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 2, },
+ { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 64, 26, 0, 0 }, 0x0, 3, },
+ { 6, 1, 1, 0x0000012000000000ull, 0x000001e000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 4, },
+ { 7, 1, 1, 0x0000010040000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 5, },
+ { 7, 1, 1, 0x0000010c00000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 6, },
+ { 8, 1, 1, 0x0000010800000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 7, },
+ { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 3, 53, 54, 55 }, 0x221, 8, },
+ { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 53, 54, 55, 0 }, 0x261, 9, },
+ { 10, 1, 1, 0x0000010060000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 10, },
+ { 10, 1, 1, 0x0000010160000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 11, },
+ { 11, 1, 1, 0x0000010068000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 12, },
+ { 11, 1, 1, 0x0000010168000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 13, },
+ { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011ffull, { 16, 0, 0, 0, 0 }, 0x40, 969, },
+ { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x0, 825, },
+ { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x40, 826, },
+ { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x200, 2234, },
+ { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x240, 2235, },
+ { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x0, 582, },
+ { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x40, 583, },
+ { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011ffull, { 82, 0, 0, 0, 0 }, 0x40, 990, },
+ { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x0, 827, },
+ { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x40, 828, },
+ { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x210, 3029, },
+ { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x250, 3030, },
+ { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x30, 590, },
+ { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x70, 591, },
+ { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x230, 588, },
+ { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x270, 589, },
+ { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x0, 584, },
+ { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x40, 585, },
+ { 15, 4, 0, 0x0000000000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 537, },
+ { 15, 5, 0, 0x0000000000000000ull, 0x000001e3f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 960, },
+ { 15, 2, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1138, },
+ { 15, 3, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1263, },
+ { 15, 6, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3033, },
+ { 15, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 16, },
+ { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011ffull, { 83, 0, 0, 0, 0 }, 0x40, 1023, },
+ { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x0, 829, },
+ { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x40, 830, },
+ { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x0, 586, },
+ { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x40, 587, },
+ { 17, 4, 0, 0x0000004080000000ull, 0x000001e9f8000018ull, { 16, 78, 0, 0, 0 }, 0x20, 2852, },
+ { 17, 4, 0, 0x000000e000000000ull, 0x000001e800000018ull, { 82, 78, 0, 0, 0 }, 0x20, 2853, },
+ { 18, 4, 0, 0x0000000060000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x2c, 222, },
+ { 22, 2, 0, 0x0000000200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2239, },
+ { 22, 3, 0, 0x0000000800000000ull, 0x000001ee00000000ull, { 24, 82, 0, 0, 0 }, 0x0, 226, },
+ { 22, 3, 0, 0x0000000c00000000ull, 0x000001ee00000000ull, { 18, 82, 0, 0, 0 }, 0x0, 227, },
+ { 22, 3, 0, 0x0000002200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2240, },
+ { 22, 3, 0, 0x0000002600000000ull, 0x000001ee00000000ull, { 19, 81, 0, 0, 0 }, 0x0, 2241, },
+ { 22, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2242, },
+ { 25, 4, 0, 0x0000000020000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 18, },
+ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1222, },
+ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1223, },
+ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1181, },
+ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1182, },
+ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1090, },
+ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1091, },
+ { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1052, },
+ { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1053, },
+ { 26, 1, 2, 0x0000018200000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1376, },
+ { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1092, },
+ { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1093, },
+ { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1226, },
+ { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1227, },
+ { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1187, },
+ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1229, },
+ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1230, },
+ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1188, },
+ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1189, },
+ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1097, },
+ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1098, },
+ { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1059, },
+ { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1060, },
+ { 26, 1, 2, 0x0000018a00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1381, },
+ { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 23, 60, 26, 0 }, 0x0, 1214, },
+ { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 60, 26, 0, 0 }, 0x40, 1215, },
+ { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 22, 60, 26, 0 }, 0x0, 1125, },
+ { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 60, 26, 0, 0 }, 0x40, 1126, },
+ { 26, 1, 2, 0x000001c200000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1382, },
+ { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1190, },
+ { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1191, },
+ { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1063, },
+ { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1064, },
+ { 26, 1, 2, 0x000001ca00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1383, },
+ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1235, },
+ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1236, },
+ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1194, },
+ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1195, },
+ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1103, },
+ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1104, },
+ { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1065, },
+ { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1066, },
+ { 27, 1, 2, 0x0000018600000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1388, },
+ { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1105, },
+ { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1106, },
+ { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1239, },
+ { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1240, },
+ { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1200, },
+ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1242, },
+ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1243, },
+ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1201, },
+ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1202, },
+ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1110, },
+ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1111, },
+ { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1072, },
+ { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1073, },
+ { 27, 1, 2, 0x0000018e00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1393, },
+ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 57, 26, 0 }, 0x0, 1259, },
+ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 57, 26, 0, 0 }, 0x40, 1260, },
+ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 59, 26, 0 }, 0x0, 1218, },
+ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 59, 26, 0, 0 }, 0x40, 1219, },
+ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 59, 26, 0 }, 0x0, 1129, },
+ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 59, 26, 0, 0 }, 0x40, 1130, },
+ { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 57, 26, 0 }, 0x0, 1088, },
+ { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 57, 26, 0, 0 }, 0x40, 1089, },
+ { 27, 1, 2, 0x000001c600000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1394, },
+ { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1203, },
+ { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1204, },
+ { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1076, },
+ { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1077, },
+ { 27, 1, 2, 0x000001ce00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1395, },
+ { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 1, 2 }, 0x0, 259, },
+ { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 260, },
+ { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 261, },
+ { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 262, },
+ { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 263, },
+ { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 264, },
+ { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 265, },
+ { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 266, },
+ { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 267, },
+ { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 268, },
+ { 34, 4, 0, 0x0000000010000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 19, },
+ { 36, 2, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1167, },
+ { 37, 2, 1, 0x00000000c8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1168, },
+ { 39, 2, 1, 0x0000008000000000ull, 0x000001e000000000ull, { 24, 25, 26, 47, 73 }, 0x0, 20, },
+ { 39, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 74, 0 }, 0x0, 3038, },
+ { 39, 2, 1, 0x000000a604000000ull, 0x000001ee04000000ull, { 24, 56, 45, 74, 0 }, 0x0, 3039, },
+ { 39, 2, 1, 0x000000ae00000000ull, 0x000001ee00000000ull, { 24, 48, 26, 46, 74 }, 0x0, 21, },
+ { 43, 4, 0, 0x0000000080000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x20, 22, },
+ { 48, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 74, 0 }, 0x0, 2870, },
+ { 50, 5, 1, 0x0000000080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 24, },
+ { 51, 5, 1, 0x0000010008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2291, },
+ { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2292, },
+ { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 26, },
+ { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2293, },
+ { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 27, },
+ { 54, 5, 1, 0x0000000160000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 28, },
+ { 55, 5, 1, 0x0000000168000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 29, },
+ { 57, 3, 0, 0x0000002180000000ull, 0x000001fff8000000ull, { 26, 0, 0, 0, 0 }, 0x0, 30, },
+ { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x0, 2294, },
+ { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x40, 31, },
+ { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 23, 19, 61, 0 }, 0x0, 1265, },
+ { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 19, 61, 0, 0 }, 0x40, 1266, },
+ { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 22, 19, 61, 0 }, 0x40, 1420, },
+ { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 19, 61, 0, 0 }, 0x40, 1421, },
+ { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 2295, },
+ { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x40, 32, },
+ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x0, 943, },
+ { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 19, 20, 0, 0 }, 0x40, 944, },
+ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x40, 945, },
+ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x0, 1116, },
+ { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 20, 19, 0, 0 }, 0x40, 1117, },
+ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x40, 1118, },
+ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x0, 1396, },
+ { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 19, 20, 0, 0 }, 0x40, 1397, },
+ { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x40, 1398, },
+ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x0, 1405, },
+ { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 20, 19, 0, 0 }, 0x40, 1406, },
+ { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x40, 1407, },
+ { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1042, },
+ { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1043, },
+ { 62, 5, 1, 0x00000000e0000000ull, 0x000001e3f8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 3036, },
+ { 62, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 3037, },
+ { 63, 3, 1, 0x0000008488000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 269, },
+ { 64, 3, 1, 0x00000084c8000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 270, },
+ { 67, 3, 0, 0x0000000060000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 33, },
+ { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2353, },
+ { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 34, },
+ { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2354, },
+ { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 35, },
+ { 70, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2247, },
+ { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2355, },
+ { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 36, },
+ { 72, 5, 1, 0x00000001c8000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 1221, },
+ { 73, 5, 1, 0x0000010000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2358, },
+ { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2361, },
+ { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 38, },
+ { 75, 5, 1, 0x0000000088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 39, },
+ { 76, 5, 1, 0x0000000088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 40, },
+ { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2364, },
+ { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 41, },
+ { 78, 5, 1, 0x0000018000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2367, },
+ { 79, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 2370, },
+ { 80, 5, 1, 0x0000000170000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 44, },
+ { 81, 5, 1, 0x0000002080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 45, },
+ { 82, 5, 1, 0x0000000140000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 46, },
+ { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2371, },
+ { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 47, },
+ { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2372, },
+ { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 48, },
+ { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 946, },
+ { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 947, },
+ { 85, 5, 1, 0x0000002188000000ull, 0x000001eff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 1119, },
+ { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1044, },
+ { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1045, },
+ { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2389, },
+ { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 49, },
+ { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2390, },
+ { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 50, },
+ { 89, 5, 1, 0x0000002080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2255, },
+ { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2391, },
+ { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 51, },
+ { 91, 5, 1, 0x0000013000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2392, },
+ { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2393, },
+ { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 53, },
+ { 93, 5, 1, 0x0000002088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 54, },
+ { 94, 5, 1, 0x0000002088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 55, },
+ { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2394, },
+ { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 56, },
+ { 96, 5, 1, 0x000001b000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2395, },
+ { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2396, },
+ { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 58, },
+ { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2397, },
+ { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 59, },
+ { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2398, },
+ { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 60, },
+ { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2399, },
+ { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 61, },
+ { 101, 5, 1, 0x000001c000000000ull, 0x000001f000000000ull, { 18, 20, 21, 19, 0 }, 0x0, 62, },
+ { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x0, 2400, },
+ { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x40, 63, },
+ { 103, 5, 1, 0x0000014008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2403, },
+ { 104, 5, 1, 0x00000001a0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 65, },
+ { 105, 5, 1, 0x00000001e0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2202, },
+ { 106, 3, 0, 0x0000000100000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 66, },
+ { 108, 5, 1, 0x0000000178000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 67, },
+ { 113, 3, 1, 0x0000008708000000ull, 0x000001ffc8000000ull, { 24, 19, 0, 0, 0 }, 0x0, 2781, },
+ { 118, 4, 0, 0x0000004008000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 538, },
+ { 118, 5, 0, 0x000000000c000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 961, },
+ { 118, 2, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1141, },
+ { 118, 3, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1267, },
+ { 118, 6, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3034, },
+ { 118, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 68, },
+ { 123, 3, 0, 0x0000000080000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 69, },
+ { 123, 3, 0, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 0, 0, 0, 0 }, 0x0, 920, },
+ { 123, 3, 0, 0x0000000098000000ull, 0x000001eff8000000ull, { 18, 0, 0, 0, 0 }, 0x0, 921, },
+ { 124, 3, 0, 0x0000002170000000ull, 0x000001eff8000000ull, { 25, 0, 0, 0, 0 }, 0xc, 846, },
+ { 125, 3, 1, 0x0000002070000000ull, 0x000001eff8000000ull, { 31, 25, 0, 0, 0 }, 0x8, 847, },
+ { 125, 3, 1, 0x0000002078000000ull, 0x000001eff8000000ull, { 32, 25, 0, 0, 0 }, 0x8, 1143, },
+ { 127, 3, 1, 0x0000008000000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 70, },
+ { 127, 3, 1, 0x0000009000000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 71, },
+ { 127, 3, 1, 0x000000a000000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 72, },
+ { 128, 3, 2, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 1, 28, 0, 0 }, 0x0, 73, },
+ { 128, 3, 1, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x40, 74, },
+ { 129, 3, 1, 0x0000008040000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 75, },
+ { 129, 3, 1, 0x0000009040000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 76, },
+ { 129, 3, 1, 0x000000a040000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 77, },
+ { 130, 3, 1, 0x0000008080000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 78, },
+ { 130, 3, 1, 0x0000009080000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 79, },
+ { 130, 3, 1, 0x000000a080000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 80, },
+ { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 81, },
+ { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 84, 0, 0 }, 0x0, 1339, },
+ { 131, 3, 1, 0x00000090c0000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 82, },
+ { 131, 3, 1, 0x000000a0c0000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 83, },
+ { 132, 3, 1, 0x000000c6c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 1039, },
+ { 132, 3, 1, 0x000000d6c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 1040, },
+ { 132, 3, 1, 0x000000e6c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 1041, },
+ { 133, 3, 1, 0x000000c040000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 84, },
+ { 133, 3, 1, 0x000000d040000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 85, },
+ { 133, 3, 1, 0x000000e040000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 86, },
+ { 134, 3, 1, 0x000000c0c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 87, },
+ { 134, 3, 1, 0x000000d0c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 88, },
+ { 134, 3, 1, 0x000000e0c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 89, },
+ { 135, 3, 1, 0x000000c000000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 90, },
+ { 135, 3, 1, 0x000000d000000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 91, },
+ { 135, 3, 1, 0x000000e000000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 92, },
+ { 136, 3, 2, 0x000000c048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 93, },
+ { 136, 3, 2, 0x000000d048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 94, },
+ { 137, 3, 2, 0x000000c0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 95, },
+ { 137, 3, 2, 0x000000d0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 96, },
+ { 138, 3, 2, 0x000000c088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 97, },
+ { 138, 3, 2, 0x000000d088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 5, 0 }, 0x400, 98, },
+ { 139, 3, 1, 0x000000c080000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 99, },
+ { 139, 3, 1, 0x000000d080000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 100, },
+ { 139, 3, 1, 0x000000e080000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 101, },
+ { 142, 3, 0, 0x000000cb00000000ull, 0x000001fff8000000ull, { 28, 0, 0, 0, 0 }, 0x0, 102, },
+ { 142, 3, 0, 0x000000db00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x400, 103, },
+ { 142, 3, 0, 0x000000eb00000000ull, 0x000001eff0000000ull, { 28, 63, 0, 0, 0 }, 0x400, 104, },
+ { 143, 3, 0, 0x0000000050000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 105, },
+ { 151, 3, 0, 0x0000000110000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 106, },
+ { 152, 2, 1, 0x000000e880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2203, },
+ { 153, 2, 1, 0x000000ea80000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2204, },
+ { 154, 2, 1, 0x000000f880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2205, },
+ { 155, 1, 1, 0x0000010800000000ull, 0x000001fff80fe000ull, { 24, 26, 0, 0, 0 }, 0x0, 107, },
+ { 155, 1, 1, 0x0000012000000000ull, 0x000001e000300000ull, { 24, 67, 0, 0, 0 }, 0x40, 108, },
+ { 155, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 109, },
+ { 155, 2, 1, 0x0000000e00100000ull, 0x000001ee00f00000ull, { 15, 25, 0, 0, 0 }, 0x40, 110, },
+ { 155, 2, 1, 0x0000000e00000000ull, 0x000001ee00f00000ull, { 15, 25, 79, 0, 0 }, 0x0, 2855, },
+ { 155, 2, 1, 0x0000000188000000ull, 0x000001eff8000000ull, { 24, 16, 0, 0, 0 }, 0x0, 112, },
+ { 155, 2, 1, 0x0000000600000000ull, 0x000001ee00000000ull, { 9, 25, 65, 0, 0 }, 0x0, 113, },
+ { 155, 2, 1, 0x00000016ff001fc0ull, 0x000001feff001fc0ull, { 9, 25, 0, 0, 0 }, 0x40, 114, },
+ { 155, 2, 1, 0x0000000400000000ull, 0x000001ee00000000ull, { 10, 69, 0, 0, 0 }, 0x0, 115, },
+ { 155, 2, 1, 0x0000000180000000ull, 0x000001eff8000000ull, { 24, 8, 0, 0, 0 }, 0x0, 116, },
+ { 155, 2, 1, 0x0000000198000000ull, 0x000001eff8000000ull, { 24, 9, 0, 0, 0 }, 0x0, 117, },
+ { 155, 2, 1, 0x0000000150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1144, },
+ { 155, 2, 1, 0x0000000050000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1145, },
+ { 155, 2, 1, 0x0000000190000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1146, },
+ { 155, 3, 1, 0x0000000140000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1268, },
+ { 155, 3, 1, 0x0000002150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1269, },
+ { 155, 3, 1, 0x0000002110000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1270, },
+ { 155, 3, 1, 0x0000002160000000ull, 0x000001eff8000000ull, { 17, 25, 0, 0, 0 }, 0x8, 118, },
+ { 155, 3, 1, 0x0000002120000000ull, 0x000001eff8000000ull, { 24, 17, 0, 0, 0 }, 0x8, 119, },
+ { 155, 3, 1, 0x0000002168000000ull, 0x000001eff8000000ull, { 12, 25, 0, 0, 0 }, 0x8, 120, },
+ { 155, 3, 1, 0x0000002148000000ull, 0x000001eff8000000ull, { 13, 25, 0, 0, 0 }, 0x0, 121, },
+ { 155, 3, 1, 0x0000002128000000ull, 0x000001eff8000000ull, { 24, 11, 0, 0, 0 }, 0x8, 122, },
+ { 155, 3, 1, 0x0000002108000000ull, 0x000001eff8000000ull, { 24, 13, 0, 0, 0 }, 0x0, 123, },
+ { 155, 3, 1, 0x0000002000000000ull, 0x000001eff8000000ull, { 38, 25, 0, 0, 0 }, 0x8, 124, },
+ { 155, 3, 1, 0x0000002008000000ull, 0x000001eff8000000ull, { 30, 25, 0, 0, 0 }, 0x8, 125, },
+ { 155, 3, 1, 0x0000002010000000ull, 0x000001eff8000000ull, { 33, 25, 0, 0, 0 }, 0x8, 126, },
+ { 155, 3, 1, 0x0000002018000000ull, 0x000001eff8000000ull, { 35, 25, 0, 0, 0 }, 0x8, 127, },
+ { 155, 3, 1, 0x0000002020000000ull, 0x000001eff8000000ull, { 36, 25, 0, 0, 0 }, 0x8, 128, },
+ { 155, 3, 1, 0x0000002028000000ull, 0x000001eff8000000ull, { 37, 25, 0, 0, 0 }, 0x8, 129, },
+ { 155, 3, 1, 0x0000002030000000ull, 0x000001eff8000000ull, { 34, 25, 0, 0, 0 }, 0x8, 130, },
+ { 155, 3, 1, 0x0000002080000000ull, 0x000001eff8000000ull, { 24, 38, 0, 0, 0 }, 0x8, 131, },
+ { 155, 3, 1, 0x0000002088000000ull, 0x000001eff8000000ull, { 24, 30, 0, 0, 0 }, 0x8, 132, },
+ { 155, 3, 1, 0x0000002090000000ull, 0x000001eff8000000ull, { 24, 33, 0, 0, 0 }, 0x8, 133, },
+ { 155, 3, 1, 0x0000002098000000ull, 0x000001eff8000000ull, { 24, 35, 0, 0, 0 }, 0x8, 134, },
+ { 155, 3, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 24, 36, 0, 0, 0 }, 0x8, 135, },
+ { 155, 3, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 24, 37, 0, 0, 0 }, 0x0, 136, },
+ { 155, 3, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 24, 34, 0, 0, 0 }, 0x8, 137, },
+ { 155, 3, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 24, 29, 0, 0, 0 }, 0x0, 138, },
+ { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 14, 0, 0, 0 }, 0x0, 139, },
+ { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 56, 0, 0, 0 }, 0x0, 140, },
+ { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 25, 0, 0, 0 }, 0x0, 141, },
+ { 156, 6, 1, 0x000000c000000000ull, 0x000001e000100000ull, { 24, 71, 0, 0, 0 }, 0x0, 142, },
+ { 157, 2, 1, 0x000000eca0000000ull, 0x000001fff0000000ull, { 24, 25, 75, 0, 0 }, 0x0, 143, },
+ { 158, 2, 1, 0x000000eea0000000ull, 0x000001fff0000000ull, { 24, 25, 76, 0, 0 }, 0x0, 144, },
+ { 168, 4, 0, 0x0000004000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 539, },
+ { 168, 5, 0, 0x0000000008000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 962, },
+ { 168, 2, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1147, },
+ { 168, 3, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1271, },
+ { 168, 6, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3035, },
+ { 168, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 145, },
+ { 175, 1, 1, 0x0000010070000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 146, },
+ { 175, 1, 1, 0x0000010170000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 147, },
+ { 178, 2, 1, 0x000000ea00000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 3017, },
+ { 179, 2, 1, 0x000000f820000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2857, },
+ { 180, 1, 1, 0x0000010400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 148, },
+ { 181, 1, 1, 0x0000010600000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 149, },
+ { 182, 1, 1, 0x0000011400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 150, },
+ { 183, 1, 1, 0x0000010450000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 151, },
+ { 184, 1, 1, 0x0000010650000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 152, },
+ { 185, 1, 1, 0x0000010470000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 153, },
+ { 186, 1, 1, 0x0000010670000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 154, },
+ { 187, 1, 1, 0x0000010520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 948, },
+ { 188, 1, 1, 0x0000010720000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 949, },
+ { 189, 1, 1, 0x0000011520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 950, },
+ { 190, 2, 1, 0x000000e850000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2871, },
+ { 191, 2, 1, 0x000000ea70000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 155, },
+ { 192, 2, 1, 0x000000e810000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2872, },
+ { 193, 2, 1, 0x000000ea30000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 156, },
+ { 194, 2, 1, 0x000000ead0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2206, },
+ { 195, 2, 1, 0x000000e230000000ull, 0x000001ff30000000ull, { 24, 25, 26, 42, 0 }, 0x0, 157, },
+ { 196, 2, 1, 0x000000e690000000ull, 0x000001fff0000000ull, { 24, 26, 0, 0, 0 }, 0x0, 158, },
+ { 198, 3, 1, 0x00000021c0000000ull, 0x000001eff8000000ull, { 24, 26, 25, 0, 0 }, 0x0, 2207, },
+ { 198, 3, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 24, 26, 49, 0, 0 }, 0x0, 2208, },
+ { 198, 3, 0, 0x0000002188000000ull, 0x000001eff8000000ull, { 26, 49, 0, 0, 0 }, 0x0, 2238, },
+ { 199, 2, 1, 0x000000e8b0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 159, },
+ { 200, 2, 1, 0x000000e240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 160, },
+ { 200, 2, 1, 0x000000ee50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 161, },
+ { 201, 2, 1, 0x000000f040000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 162, },
+ { 201, 2, 1, 0x000000fc50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 163, },
+ { 202, 1, 1, 0x0000010680000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 164, },
+ { 203, 2, 1, 0x000000e220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 165, },
+ { 203, 2, 1, 0x000000e630000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 166, },
+ { 204, 2, 1, 0x000000f020000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 167, },
+ { 204, 2, 1, 0x000000f430000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 168, },
+ { 205, 1, 1, 0x00000106c0000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 169, },
+ { 206, 1, 1, 0x0000010420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 170, },
+ { 207, 1, 1, 0x0000010620000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 171, },
+ { 208, 1, 1, 0x0000011420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 172, },
+ { 209, 3, 0, 0x0000002048000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 1175, },
+ { 209, 3, 0, 0x0000002050000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0xc, 1050, },
+ { 209, 3, 0, 0x00000021a0000000ull, 0x000001eff8000000ull, { 26, 0, 0, 0, 0 }, 0x8, 922, },
+ { 210, 3, 0, 0x0000002060000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 848, },
+ { 215, 4, 0, 0x0000000040000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x22c, 173, },
+ { 216, 3, 0, 0x0000000038000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 174, },
+ { 217, 3, 0, 0x0000000028000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 175, },
+ { 226, 3, 1, 0x000000c708000000ull, 0x000001ffc8000000ull, { 18, 25, 0, 0, 0 }, 0x0, 2782, },
+ { 227, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 0, 0 }, 0x140, 176, },
+ { 227, 2, 1, 0x000000f240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 177, },
+ { 228, 1, 1, 0x0000010080000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 178, },
+ { 229, 1, 1, 0x00000100c0000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 179, },
+ { 230, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 0, 0 }, 0x140, 2878, },
+ { 230, 2, 1, 0x000000f220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 181, },
+ { 231, 2, 1, 0x000000ac00000000ull, 0x000001ee00000000ull, { 24, 25, 26, 44, 0 }, 0x0, 182, },
+ { 236, 3, 0, 0x0000000180000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 850, },
+ { 237, 3, 0, 0x0000000030000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 183, },
+ { 239, 3, 1, 0x0000008c00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 184, },
+ { 239, 3, 1, 0x000000ac00000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 185, },
+ { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 1, 0, 0 }, 0x0, 186, },
+ { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x40, 187, },
+ { 241, 3, 1, 0x0000008c40000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 188, },
+ { 241, 3, 1, 0x000000ac40000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 189, },
+ { 242, 3, 1, 0x0000008c80000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 190, },
+ { 242, 3, 1, 0x000000ac80000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 191, },
+ { 243, 3, 1, 0x0000008cc0000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 192, },
+ { 243, 3, 1, 0x000000acc0000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 193, },
+ { 244, 3, 1, 0x000000cec0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 2785, },
+ { 244, 3, 1, 0x000000eec0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 2786, },
+ { 245, 3, 1, 0x000000cc40000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 194, },
+ { 245, 3, 1, 0x000000ec40000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 195, },
+ { 246, 3, 1, 0x000000ccc0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 196, },
+ { 246, 3, 1, 0x000000ecc0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 197, },
+ { 247, 3, 1, 0x000000cc00000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 198, },
+ { 247, 3, 1, 0x000000ec00000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 199, },
+ { 248, 3, 1, 0x000000cc80000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 200, },
+ { 248, 3, 1, 0x000000ec80000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 201, },
+ { 249, 1, 1, 0x0000010028000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 202, },
+ { 249, 1, 1, 0x0000010020000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 203, },
+ { 249, 1, 1, 0x0000010128000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 204, },
+ { 250, 3, 0, 0x0000000020000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 205, },
+ { 251, 2, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 206, },
+ { 252, 2, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 207, },
+ { 253, 2, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 208, },
+ { 254, 3, 0, 0x0000000198000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 1150, },
+ { 255, 3, 1, 0x00000020f8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 209, },
+ { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 23, 26, 77, 0 }, 0x0, 3040, },
+ { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 26, 77, 0, 0 }, 0x40, 3041, },
+ { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 22, 26, 77, 0 }, 0x40, 2003, },
+ { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 26, 77, 0, 0 }, 0x40, 2004, },
+ { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 23, 50, 0, 0 }, 0x0, 3044, },
+ { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 50, 0, 0, 0 }, 0x40, 3045, },
+ { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 22, 50, 0, 0 }, 0x40, 2007, },
+ { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 50, 0, 0, 0 }, 0x40, 2008, },
+ { 258, 3, 1, 0x00000020d0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 210, },
+ { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 23, 26, 0, 0 }, 0x0, 3048, },
+ { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 26, 0, 0, 0 }, 0x40, 3049, },
+ { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 22, 26, 0, 0 }, 0x40, 2011, },
+ { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 26, 0, 0, 0 }, 0x40, 2012, },
+ { 260, 3, 1, 0x00000020f0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 211, },
+ { 262, 3, 1, 0x00000020d8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 212, },
+ { 266, 2, 1, 0x000000e840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1131, },
+ { 267, 2, 1, 0x000000ea40000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1132, },
+ { 268, 2, 1, 0x000000f840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1133, },
+ { 272, 4, 0, 0x00000000c0000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x28, 223, },
+ { 277, 3, 1, 0x0000008208000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 213, },
+ { 278, 3, 1, 0x0000008248000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 214, },
+ { 279, 3, 1, 0x0000008288000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 215, },
+ { 280, 3, 1, 0x00000082c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 216, },
+ { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 1179, },
+ { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 1261, },
+ { 283, 5, 1, 0x000001d000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 1180, },
+ { 284, 1, 1, 0x0000010078000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 217, },
+ { 284, 1, 1, 0x0000010178000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 218, },
+ { 287, 2, 1, 0x0000000080000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 219, },
+ { 288, 2, 1, 0x0000000088000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 220, },
+ { 289, 2, 1, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 221, },
+};
+
+static const char dis_table[] = {
+0xa0, 0xc7, 0xc8, 0xa0, 0x2e, 0xd8, 0xa0, 0x2c, 0xc0, 0xa0, 0x1c, 0x00,
+0x98, 0xb0, 0x02, 0x50, 0x90, 0x50, 0x90, 0x28, 0x24, 0x39, 0x28, 0x24,
+0x39, 0x20, 0x90, 0x28, 0x24, 0x39, 0x18, 0x24, 0x39, 0x10, 0x91, 0x60,
+0x90, 0x28, 0x24, 0x39, 0x00, 0x10, 0x10, 0x58, 0x41, 0x61, 0xc7, 0xc0,
+0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+0x10, 0x10, 0x52, 0xc0, 0xc0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+0x10, 0x10, 0x10, 0x24, 0x24, 0x70, 0x90, 0x28, 0x24, 0x38, 0xf0, 0x24,
+0x38, 0xe8, 0xa8, 0x0b, 0x48, 0x15, 0x20, 0x97, 0x20, 0x95, 0xc8, 0x9a,
+0xb8, 0x05, 0x38, 0x91, 0x18, 0x90, 0xa0, 0x90, 0x60, 0x80, 0x90, 0x20,
+0x34, 0xa6, 0xa4, 0x25, 0x00, 0x34, 0xa3, 0x80, 0xa4, 0x36, 0xa0, 0x36,
+0xd9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x36, 0xcf, 0x80, 0x34, 0x86, 0x81,
+0x33, 0xe2, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x24, 0x10, 0x34,
+0x83, 0xa4, 0x1f, 0x08, 0x34, 0x80, 0x90, 0x38, 0xa4, 0x38, 0xa0, 0x37,
+0x1a, 0xa4, 0x38, 0x48, 0x37, 0x0e, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x37,
+0x20, 0x36, 0xef, 0xa4, 0x36, 0xf8, 0x36, 0xea, 0x80, 0xa4, 0x23, 0xf0,
+0x34, 0x7f, 0x92, 0x18, 0x91, 0xc0, 0x80, 0x91, 0x80, 0x90, 0xf8, 0xdb,
+0x84, 0x60, 0xf9, 0x40, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x68, 0x8c, 0x43,
+0xc8, 0x84, 0x38, 0x83, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x58, 0x8c, 0x43,
+0xa8, 0x84, 0x38, 0x81, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38,
+0x35, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x33, 0xa4, 0x1f, 0x18, 0x33, 0xe4,
+0x80, 0x90, 0x28, 0x80, 0x33, 0xe0, 0x80, 0x34, 0x88, 0x81, 0x90, 0x38,
+0xa4, 0x24, 0x80, 0x34, 0x8b, 0xa4, 0x24, 0x48, 0x34, 0x85, 0xc0, 0x40,
+0x10, 0x10, 0x90, 0x38, 0xa4, 0x1e, 0xf0, 0x33, 0xdf, 0xa4, 0x1e, 0xe0,
+0x33, 0xdd, 0x18, 0x24, 0x24, 0xf8, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0,
+0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x38, 0x38, 0x6d, 0xc0, 0xc0, 0x80, 0xa4,
+0x42, 0x28, 0x38, 0x69, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38,
+0x2f, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2d, 0x92, 0xb8, 0x99, 0x84, 0x24,
+0x68, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x98, 0x36,
+0xd8, 0x82, 0x36, 0xce, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38,
+0x98, 0x37, 0x19, 0xa4, 0x38, 0x40, 0x37, 0x0d, 0x80, 0x90, 0x38, 0xa4,
+0x37, 0x18, 0x36, 0xee, 0xa4, 0x36, 0xf0, 0x36, 0xe9, 0x83, 0x90, 0xa8,
+0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x08, 0x38, 0x61, 0xc0,
+0xc0, 0x80, 0xa4, 0x41, 0xf8, 0x38, 0x5d, 0xd3, 0x82, 0x40, 0x50, 0xc0,
+0xc0, 0x81, 0x38, 0x29, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x27, 0x18, 0x24,
+0x24, 0x78, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4,
+0x41, 0xd8, 0x38, 0x55, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xc8, 0x38, 0x51,
+0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x23, 0x50, 0xc0, 0xc0,
+0x81, 0x38, 0x21, 0x94, 0x50, 0x92, 0xf8, 0x99, 0x84, 0x1f, 0x48, 0x90,
+0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x90, 0x36, 0xd7, 0x82,
+0x36, 0xcd, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, 0x90, 0x37,
+0x18, 0xa4, 0x38, 0x38, 0x37, 0x0c, 0x80, 0x90, 0x38, 0xa4, 0x37, 0x10,
+0x36, 0xed, 0xa4, 0x36, 0xe8, 0x36, 0xe8, 0x83, 0x90, 0xe8, 0xd3, 0x83,
+0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x78, 0x8c, 0x43, 0xe8, 0x84, 0x38,
+0x85, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x60, 0x8c, 0x43, 0xb8, 0x84, 0x38,
+0x82, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x37, 0x50, 0xc0,
+0xc0, 0x81, 0x38, 0x34, 0x18, 0x24, 0x1f, 0x40, 0x83, 0x90, 0xa8, 0xd3,
+0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x48, 0x38, 0x71, 0xc0, 0xc0,
+0x80, 0xa4, 0x42, 0x30, 0x38, 0x6b, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0,
+0x81, 0x38, 0x31, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2e, 0x92, 0xb8, 0x99,
+0x84, 0x1f, 0x38, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36,
+0x88, 0x36, 0xd6, 0x82, 0x36, 0xcc, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38,
+0xa4, 0x38, 0x88, 0x37, 0x17, 0xa4, 0x38, 0x30, 0x37, 0x0b, 0x80, 0x90,
+0x38, 0xa4, 0x37, 0x08, 0x36, 0xec, 0xa4, 0x36, 0xe0, 0x36, 0xe7, 0x83,
+0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x18, 0x38,
+0x65, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x00, 0x38, 0x5f, 0xd3, 0x82, 0x40,
+0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2b, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x28,
+0x18, 0x20, 0x01, 0x48, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0,
+0x80, 0xa4, 0x41, 0xe8, 0x38, 0x59, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xd0,
+0x38, 0x53, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x25, 0x50,
+0xc0, 0xc0, 0x81, 0x38, 0x22, 0xda, 0x06, 0xe0, 0xf9, 0x80, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x24, 0xe8, 0x34, 0x9b, 0x80, 0x34, 0x98, 0x90, 0x38,
+0xa4, 0x24, 0x90, 0x34, 0x96, 0x80, 0x34, 0x93, 0x90, 0x60, 0x90, 0x38,
+0xa4, 0x24, 0xd0, 0x34, 0x9c, 0x80, 0x34, 0x99, 0x90, 0x38, 0xa4, 0x24,
+0xa8, 0x34, 0x97, 0x80, 0x34, 0x94, 0xc8, 0x40, 0x19, 0x00, 0x91, 0x58,
+0x90, 0x60, 0x82, 0x90, 0x20, 0x36, 0xcb, 0xa4, 0x36, 0x48, 0x36, 0xca,
+0x90, 0xc0, 0x80, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0xc1, 0x00, 0x85,
+0x37, 0x03, 0xc9, 0xe1, 0xc0, 0x40, 0x85, 0x37, 0x00, 0x80, 0x36, 0xff,
+0x10, 0x10, 0x81, 0x36, 0xdb, 0x90, 0xa8, 0x10, 0x10, 0x90, 0x28, 0x81,
+0x36, 0xf9, 0x90, 0x38, 0xa4, 0x37, 0xa0, 0x36, 0xf5, 0xa4, 0x37, 0x90,
+0x36, 0xf3, 0x90, 0x70, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x37, 0xb8, 0x36,
+0xf8, 0x80, 0x36, 0xf6, 0x90, 0x60, 0x90, 0x28, 0x24, 0x37, 0xf0, 0xa4,
+0x37, 0xe0, 0x36, 0xfd, 0x80, 0xa4, 0x37, 0xd0, 0x36, 0xfb, 0x80, 0x90,
+0xf8, 0x90, 0x90, 0x90, 0x50, 0x90, 0x28, 0x80, 0x38, 0x17, 0x80, 0x38,
+0x20, 0x80, 0xa4, 0x40, 0xf0, 0x38, 0x1f, 0x90, 0x28, 0x81, 0x38, 0x1d,
+0x80, 0xa4, 0x40, 0xd8, 0x38, 0x1c, 0x90, 0x28, 0x82, 0x38, 0x1a, 0x81,
+0xa4, 0x40, 0xc0, 0x38, 0x19, 0x98, 0xe8, 0x01, 0xb0, 0x90, 0x88, 0x90,
+0x60, 0xa4, 0x36, 0x38, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb7, 0x24,
+0x36, 0x30, 0x90, 0x28, 0x24, 0x36, 0x28, 0x24, 0x36, 0x20, 0x90, 0x88,
+0x90, 0x60, 0xa4, 0x36, 0x10, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb6,
+0x24, 0x36, 0x08, 0x90, 0x28, 0x24, 0x36, 0x00, 0x24, 0x35, 0xf8, 0xa8,
+0x09, 0x00, 0x0e, 0x20, 0x96, 0x48, 0x95, 0xe8, 0x93, 0x38, 0x91, 0xa0,
+0x90, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x60, 0x33, 0xcd, 0xa4,
+0x1e, 0x50, 0x33, 0xcb, 0x90, 0x38, 0xa4, 0x1e, 0x40, 0x33, 0xc9, 0x80,
+0x33, 0xc7, 0x90, 0x60, 0x90, 0x28, 0x24, 0x1e, 0x00, 0xa4, 0x1d, 0xf0,
+0x33, 0xbf, 0x90, 0x38, 0xa4, 0x1d, 0xe0, 0x33, 0xbd, 0xa4, 0x1e, 0x28,
+0x33, 0xc6, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x18, 0x33,
+0xc4, 0xa4, 0x1e, 0x08, 0x33, 0xc2, 0x90, 0x38, 0xa4, 0x35, 0xb0, 0x36,
+0xbc, 0xa4, 0x35, 0x50, 0x36, 0xb0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x32,
+0x90, 0x36, 0x5e, 0xa4, 0x32, 0x60, 0x36, 0x58, 0x10, 0x10, 0xa4, 0x1d,
+0xd0, 0x33, 0xbb, 0x99, 0x60, 0x02, 0x70, 0x90, 0x90, 0x90, 0x50, 0x90,
+0x28, 0x24, 0x1e, 0x90, 0x80, 0x33, 0xda, 0x80, 0xa4, 0x1e, 0x98, 0x33,
+0xd8, 0x90, 0x50, 0x90, 0x28, 0x24, 0x1e, 0xa0, 0x80, 0x33, 0xdb, 0x90,
+0x38, 0xa4, 0x1e, 0xa8, 0x33, 0xd9, 0xa4, 0x1e, 0x70, 0x33, 0xcf, 0x90,
+0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xe8, 0x36, 0xa5, 0xa4, 0x34,
+0x48, 0x36, 0x92, 0x90, 0x38, 0xa4, 0x33, 0xe0, 0x36, 0x83, 0xa4, 0x33,
+0x50, 0x36, 0x72, 0x81, 0xa4, 0x1e, 0x80, 0x33, 0xd1, 0xe4, 0xa2, 0x04,
+0x40, 0x38, 0x13, 0x18, 0x24, 0x1d, 0xc8, 0xe4, 0xe2, 0x02, 0xc0, 0x38,
+0x0d, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90,
+0x38, 0xa4, 0x35, 0xa8, 0x36, 0xbb, 0xa4, 0x35, 0x48, 0x36, 0xaf, 0x80,
+0x90, 0x38, 0xa4, 0x32, 0x88, 0x36, 0x5d, 0xa4, 0x32, 0x58, 0x36, 0x57,
+0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xd8,
+0x36, 0xa4, 0xa4, 0x34, 0x40, 0x36, 0x90, 0x90, 0x38, 0xa4, 0x33, 0xd0,
+0x36, 0x82, 0xa4, 0x33, 0x48, 0x36, 0x70, 0xe4, 0xa2, 0x01, 0x40, 0x38,
+0x07, 0x18, 0x24, 0x1d, 0xc0, 0xe4, 0xe1, 0xff, 0xc0, 0x38, 0x01, 0x92,
+0x90, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90,
+0x38, 0xa4, 0x35, 0xa0, 0x36, 0xba, 0xa4, 0x35, 0x40, 0x36, 0xae, 0x80,
+0x90, 0x38, 0xa4, 0x32, 0x80, 0x36, 0x5c, 0xa4, 0x32, 0x50, 0x36, 0x56,
+0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xc8,
+0x36, 0xa3, 0xa4, 0x34, 0x38, 0x36, 0x8e, 0x90, 0x38, 0xa4, 0x33, 0xc0,
+0x36, 0x81, 0xa4, 0x33, 0x40, 0x36, 0x6e, 0xe4, 0xa2, 0x04, 0x80, 0x38,
+0x15, 0x10, 0x10, 0xe4, 0xe2, 0x03, 0x00, 0x38, 0x0f, 0x92, 0x50, 0x99,
+0x1c, 0x1e, 0xb0, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4,
+0x35, 0x98, 0x36, 0xb9, 0xa4, 0x35, 0x38, 0x36, 0xad, 0x80, 0x90, 0x38,
+0xa4, 0x32, 0x78, 0x36, 0x5b, 0xa4, 0x32, 0x48, 0x36, 0x55, 0x18, 0x20,
+0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xb8, 0x36, 0xa2,
+0xa4, 0x34, 0x30, 0x36, 0x8c, 0x90, 0x38, 0xa4, 0x33, 0xb0, 0x36, 0x80,
+0xa4, 0x33, 0x38, 0x36, 0x6c, 0xe4, 0xa2, 0x01, 0x80, 0x38, 0x09, 0x10,
+0x10, 0xe4, 0xe2, 0x00, 0x00, 0x38, 0x03, 0xc0, 0x40, 0x80, 0x10, 0x10,
+0x81, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0x98, 0x80, 0x85, 0x36, 0x66,
+0xc9, 0xe1, 0x99, 0x00, 0x85, 0x36, 0x63, 0x80, 0x36, 0x61, 0x80, 0xd8,
+0x47, 0x80, 0x0d, 0xc0, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, 0xd5,
+0x81, 0x80, 0x80, 0x37, 0xfd, 0x80, 0x37, 0xfb, 0xd5, 0x81, 0x80, 0x80,
+0x37, 0xf9, 0x80, 0x37, 0xf7, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58,
+0xd5, 0x81, 0x80, 0x80, 0x37, 0xfe, 0x80, 0x37, 0xfc, 0xd5, 0x81, 0x80,
+0x80, 0x37, 0xfa, 0x80, 0x37, 0xf8, 0xc0, 0x80, 0x83, 0xa4, 0x3f, 0xa8,
+0x37, 0xf6, 0xa0, 0x59, 0x60, 0xa0, 0x41, 0xe0, 0xa8, 0x1e, 0xb0, 0x34,
+0x88, 0xa0, 0x12, 0x38, 0xa0, 0x0b, 0x48, 0x96, 0x00, 0x9a, 0xf0, 0x05,
+0xc0, 0x91, 0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x15, 0x58,
+0x33, 0xb5, 0xa4, 0x15, 0x78, 0x33, 0xb4, 0x10, 0x10, 0xa4, 0x15, 0x68,
+0x33, 0xb3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0xf8, 0x33, 0x9a, 0xa4,
+0x15, 0x18, 0x33, 0x99, 0x10, 0x10, 0xa4, 0x15, 0x08, 0x33, 0x98, 0x90,
+0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x98, 0x33, 0x7f, 0xa4, 0x14,
+0xb8, 0x33, 0x7e, 0x10, 0x10, 0xa4, 0x14, 0xa8, 0x33, 0x7d, 0x90, 0x70,
+0x90, 0x38, 0xa4, 0x14, 0x38, 0x33, 0x63, 0xa4, 0x14, 0x58, 0x33, 0x62,
+0x10, 0x10, 0xa4, 0x14, 0x48, 0x33, 0x61, 0x91, 0x70, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x15, 0x28, 0x33, 0xb0, 0xa4, 0x15, 0x48, 0x33,
+0xb2, 0x10, 0x10, 0xa4, 0x15, 0x38, 0x33, 0xb1, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x14, 0xc8, 0x33, 0x95, 0xa4, 0x14, 0xe8, 0x33, 0x97, 0x10, 0x10,
+0xa4, 0x14, 0xd8, 0x33, 0x96, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4,
+0x14, 0x68, 0x33, 0x7a, 0xa4, 0x14, 0x88, 0x33, 0x7c, 0x10, 0x10, 0xa4,
+0x14, 0x78, 0x33, 0x7b, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x08, 0x33,
+0x5e, 0xa4, 0x14, 0x28, 0x33, 0x60, 0x10, 0x10, 0xa4, 0x14, 0x18, 0x33,
+0x5f, 0xe4, 0xe1, 0x8b, 0x40, 0x36, 0x41, 0x9a, 0xf0, 0x05, 0x00, 0x91,
+0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0xa0, 0x33, 0xad,
+0xa4, 0x13, 0x98, 0x33, 0xaf, 0x10, 0x10, 0xa4, 0x13, 0x90, 0x33, 0xae,
+0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x88, 0x33, 0x92, 0xa4, 0x13, 0x80,
+0x33, 0x94, 0x10, 0x10, 0xa4, 0x13, 0x78, 0x33, 0x93, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x13, 0x70, 0x33, 0x77, 0xa4, 0x13, 0x68, 0x33,
+0x79, 0x10, 0x10, 0xa4, 0x13, 0x60, 0x33, 0x78, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x13, 0x58, 0x33, 0x5b, 0xa4, 0x13, 0x50, 0x33, 0x5d, 0x10, 0x10,
+0xa4, 0x13, 0x48, 0x33, 0x5c, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x33, 0xaa, 0x80, 0x33, 0xac, 0x10, 0x10, 0x80, 0x33, 0xab,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x8f, 0x80, 0x33, 0x91, 0x10, 0x10,
+0x80, 0x33, 0x90, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x74,
+0x80, 0x33, 0x76, 0x10, 0x10, 0x80, 0x33, 0x75, 0x90, 0x50, 0x90, 0x28,
+0x80, 0x33, 0x58, 0x80, 0x33, 0x5a, 0x10, 0x10, 0x80, 0x33, 0x59, 0xe4,
+0xe1, 0x66, 0x40, 0x35, 0xc1, 0x95, 0x40, 0x9a, 0x90, 0x05, 0x00, 0x91,
+0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xa7, 0x80, 0x33,
+0xa9, 0x10, 0x10, 0x80, 0x33, 0xa8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33,
+0x8c, 0x80, 0x33, 0x8e, 0x10, 0x10, 0x80, 0x33, 0x8d, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x13, 0x30, 0x33, 0x71, 0xa4, 0x13, 0x40, 0x33,
+0x73, 0x10, 0x10, 0xa4, 0x13, 0x38, 0x33, 0x72, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x13, 0x00, 0x33, 0x55, 0xa4, 0x13, 0x10, 0x33, 0x57, 0x10, 0x10,
+0xa4, 0x13, 0x08, 0x33, 0x56, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x33, 0xa4, 0x80, 0x33, 0xa6, 0x10, 0x10, 0x80, 0x33, 0xa5,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x89, 0x80, 0x33, 0x8b, 0x10, 0x10,
+0x80, 0x33, 0x8a, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x18,
+0x33, 0x6e, 0xa4, 0x13, 0x28, 0x33, 0x70, 0x10, 0x10, 0xa4, 0x13, 0x20,
+0x33, 0x6f, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x12, 0xe8, 0x33, 0x52, 0xa4,
+0x12, 0xf8, 0x33, 0x54, 0x10, 0x10, 0xa4, 0x12, 0xf0, 0x33, 0x53, 0xe4,
+0xe1, 0x8a, 0x40, 0x36, 0x3d, 0x98, 0xb8, 0x01, 0x68, 0x10, 0x10, 0x10,
+0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4f, 0x80, 0x33, 0x51, 0x10,
+0x10, 0x80, 0x33, 0x50, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x97, 0x00,
+0x60, 0xa0, 0x96, 0xc0, 0x90, 0x30, 0x60, 0xa0, 0x96, 0x80, 0x60, 0xa0,
+0x96, 0x40, 0xe4, 0xe1, 0x64, 0x40, 0x35, 0xb9, 0xa0, 0x08, 0x08, 0x94,
+0xe0, 0x9a, 0x60, 0x04, 0xa0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90,
+0x38, 0xa4, 0x13, 0xd8, 0x33, 0x9e, 0xa4, 0x13, 0xf8, 0x33, 0xa3, 0x10,
+0x10, 0xa4, 0x13, 0xe8, 0x33, 0xa2, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33,
+0x83, 0x80, 0x33, 0x88, 0x10, 0x10, 0x80, 0x33, 0x87, 0x90, 0x88, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x33, 0x68, 0x80, 0x33, 0x6d, 0x10, 0x10, 0x80,
+0x33, 0x6c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x49, 0x80, 0x33, 0x4e,
+0x10, 0x10, 0x80, 0x33, 0x4d, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90,
+0x38, 0xa4, 0x13, 0xa8, 0x33, 0x9b, 0xa4, 0x13, 0xc8, 0x33, 0x9d, 0x10,
+0x10, 0xa4, 0x13, 0xb8, 0x33, 0x9c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33,
+0x80, 0x80, 0x33, 0x82, 0x10, 0x10, 0x80, 0x33, 0x81, 0x90, 0x88, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x33, 0x65, 0x80, 0x33, 0x67, 0x10, 0x10, 0x80,
+0x33, 0x66, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x46, 0x80, 0x33, 0x48,
+0x10, 0x10, 0x80, 0x33, 0x47, 0xe4, 0xe1, 0x89, 0x40, 0x36, 0x39, 0x9a,
+0x60, 0x02, 0xe0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4,
+0x1a, 0x20, 0x33, 0x9f, 0xa4, 0x1a, 0x10, 0x33, 0xa1, 0x10, 0x10, 0xa4,
+0x1a, 0x00, 0x33, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x84, 0x80,
+0x33, 0x86, 0x10, 0x10, 0x80, 0x33, 0x85, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x33, 0x69, 0x80, 0x33, 0x6b, 0x10, 0x10, 0x80, 0x33, 0x6a,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4a, 0x80, 0x33, 0x4c, 0x10, 0x10,
+0x80, 0x33, 0x4b, 0x81, 0x90, 0x50, 0x90, 0x28, 0x24, 0x19, 0xd0, 0x24,
+0x19, 0xf0, 0x10, 0x10, 0x24, 0x19, 0xe0, 0xe4, 0xe1, 0x62, 0x40, 0x35,
+0xb1, 0x93, 0x90, 0x99, 0xb8, 0x03, 0x50, 0x90, 0xe8, 0x90, 0x88, 0x90,
+0x40, 0x80, 0xa4, 0x15, 0xb8, 0x32, 0xca, 0x10, 0x10, 0xa4, 0x15, 0xa8,
+0x32, 0xc9, 0x90, 0x28, 0x81, 0x32, 0xc6, 0x10, 0x10, 0x80, 0x32, 0xc5,
+0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xc2, 0x10, 0x10, 0x80, 0x32, 0xc1,
+0x90, 0x28, 0x81, 0x32, 0xbe, 0x10, 0x10, 0x80, 0x32, 0xbd, 0x90, 0xe8,
+0x90, 0x88, 0x90, 0x40, 0x80, 0xa4, 0x15, 0x88, 0x32, 0xc7, 0x10, 0x10,
+0xa4, 0x15, 0x98, 0x32, 0xc8, 0x90, 0x28, 0x81, 0x32, 0xc3, 0x10, 0x10,
+0x80, 0x32, 0xc4, 0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xbf, 0x10, 0x10,
+0x80, 0x32, 0xc0, 0x90, 0x28, 0x81, 0x32, 0xbb, 0x10, 0x10, 0x80, 0x32,
+0xbc, 0xe4, 0xe1, 0x88, 0x40, 0x36, 0x35, 0x88, 0x00, 0x88, 0x10, 0x10,
+0x10, 0x10, 0x90, 0x28, 0x81, 0x32, 0xb9, 0x10, 0x10, 0x80, 0x32, 0xba,
+0xe4, 0xe1, 0x60, 0x40, 0x35, 0xa9, 0xa0, 0x0e, 0x80, 0xa0, 0x09, 0x08,
+0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x33, 0x39, 0x80, 0x33, 0x38, 0x10, 0x10, 0x80, 0x33,
+0x37, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x1e, 0x80, 0x33, 0x1d, 0x10,
+0x10, 0x80, 0x33, 0x1c, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33,
+0x03, 0x80, 0x33, 0x02, 0x10, 0x10, 0x80, 0x33, 0x01, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x32, 0xe8, 0x80, 0x32, 0xe7, 0x10, 0x10, 0x80, 0x32, 0xe6,
+0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x34, 0x80,
+0x33, 0x36, 0x10, 0x10, 0x80, 0x33, 0x35, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x33, 0x19, 0x80, 0x33, 0x1b, 0x10, 0x10, 0x80, 0x33, 0x1a, 0x90, 0x88,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfe, 0x80, 0x33, 0x00, 0x10, 0x10,
+0x80, 0x32, 0xff, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe3, 0x80, 0x32,
+0xe5, 0x10, 0x10, 0x80, 0x32, 0xe4, 0xe4, 0xe1, 0x7a, 0x40, 0x36, 0x11,
+0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28,
+0x80, 0x33, 0x31, 0x80, 0x33, 0x33, 0x10, 0x10, 0x80, 0x33, 0x32, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x33, 0x16, 0x80, 0x33, 0x18, 0x10, 0x10, 0x80,
+0x33, 0x17, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfb, 0x80,
+0x32, 0xfd, 0x10, 0x10, 0x80, 0x32, 0xfc, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x32, 0xe0, 0x80, 0x32, 0xe2, 0x10, 0x10, 0x80, 0x32, 0xe1, 0x91, 0x10,
+0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x2e, 0x80, 0x33, 0x30,
+0x10, 0x10, 0x80, 0x33, 0x2f, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x13,
+0x80, 0x33, 0x15, 0x10, 0x10, 0x80, 0x33, 0x14, 0x90, 0x88, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x32, 0xf8, 0x80, 0x32, 0xfa, 0x10, 0x10, 0x80, 0x32,
+0xf9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xdd, 0x80, 0x32, 0xdf, 0x10,
+0x10, 0x80, 0x32, 0xde, 0xe4, 0xe1, 0x59, 0x40, 0x35, 0x79, 0x94, 0x80,
+0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28,
+0x80, 0x33, 0x2b, 0x80, 0x33, 0x2d, 0x10, 0x10, 0x80, 0x33, 0x2c, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x33, 0x10, 0x80, 0x33, 0x12, 0x10, 0x10, 0x80,
+0x33, 0x11, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xf5, 0x80,
+0x32, 0xf7, 0x10, 0x10, 0x80, 0x32, 0xf6, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x32, 0xda, 0x80, 0x32, 0xdc, 0x10, 0x10, 0x80, 0x32, 0xdb, 0x91, 0x10,
+0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x28, 0x80, 0x33, 0x2a,
+0x10, 0x10, 0x80, 0x33, 0x29, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x0d,
+0x80, 0x33, 0x0f, 0x10, 0x10, 0x80, 0x33, 0x0e, 0x90, 0x88, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x32, 0xf2, 0x80, 0x32, 0xf4, 0x10, 0x10, 0x80, 0x32,
+0xf3, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd7, 0x80, 0x32, 0xd9, 0x10,
+0x10, 0x80, 0x32, 0xd8, 0xe4, 0xe1, 0x78, 0x40, 0x36, 0x09, 0x88, 0x00,
+0xb0, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd4,
+0x80, 0x32, 0xd6, 0x10, 0x10, 0x80, 0x32, 0xd5, 0xe4, 0xe1, 0x58, 0x40,
+0x35, 0x75, 0x96, 0xe8, 0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10,
+0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x22, 0x80, 0x33, 0x27,
+0x10, 0x10, 0x80, 0x33, 0x26, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x07,
+0x80, 0x33, 0x0c, 0x10, 0x10, 0x80, 0x33, 0x0b, 0x90, 0x88, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x32, 0xec, 0x80, 0x32, 0xf1, 0x10, 0x10, 0x80, 0x32,
+0xf0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xce, 0x80, 0x32, 0xd3, 0x10,
+0x10, 0x80, 0x32, 0xd2, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28,
+0x80, 0x33, 0x1f, 0x80, 0x33, 0x21, 0x10, 0x10, 0x80, 0x33, 0x20, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x33, 0x04, 0x80, 0x33, 0x06, 0x10, 0x10, 0x80,
+0x33, 0x05, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe9, 0x80,
+0x32, 0xeb, 0x10, 0x10, 0x80, 0x32, 0xea, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x32, 0xcb, 0x80, 0x32, 0xcd, 0x10, 0x10, 0x80, 0x32, 0xcc, 0xe4, 0xe1,
+0x76, 0x40, 0x36, 0x01, 0x88, 0x02, 0x28, 0x91, 0x10, 0x90, 0x88, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x33, 0x23, 0x80, 0x33, 0x25, 0x10, 0x10, 0x80,
+0x33, 0x24, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x08, 0x80, 0x33, 0x0a,
+0x10, 0x10, 0x80, 0x33, 0x09, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x32, 0xed, 0x80, 0x32, 0xef, 0x10, 0x10, 0x80, 0x32, 0xee, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x32, 0xcf, 0x80, 0x32, 0xd1, 0x10, 0x10, 0x80, 0x32,
+0xd0, 0xe4, 0xe1, 0x57, 0x40, 0x35, 0x71, 0x90, 0x40, 0xe5, 0x21, 0x74,
+0x40, 0x35, 0xf9, 0xe5, 0x21, 0x56, 0x40, 0x35, 0x6d, 0x9e, 0xb4, 0x23,
+0xe8, 0x93, 0x70, 0x91, 0xd8, 0xd5, 0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90,
+0x48, 0x80, 0x8c, 0x3f, 0x38, 0x84, 0x37, 0xf1, 0xa4, 0x3d, 0x18, 0x37,
+0xbb, 0x90, 0x28, 0x24, 0x3c, 0x58, 0xa4, 0x3a, 0xd8, 0x37, 0x73, 0xd0,
+0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x18, 0x84, 0x37, 0xef, 0xa4,
+0x3d, 0x08, 0x37, 0xb9, 0x90, 0x28, 0x24, 0x3c, 0x48, 0xa4, 0x3a, 0xc8,
+0x37, 0x71, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37,
+0xdb, 0xa4, 0x3c, 0xe8, 0x37, 0xb5, 0x90, 0x28, 0x24, 0x3c, 0x28, 0xa4,
+0x3a, 0xa8, 0x37, 0x6d, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd7,
+0xa4, 0x3c, 0xd8, 0x37, 0xb3, 0x90, 0x28, 0x24, 0x3c, 0x18, 0xa4, 0x3a,
+0x98, 0x37, 0x6b, 0x91, 0x98, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90,
+0x28, 0x80, 0x37, 0xcf, 0xa4, 0x3c, 0xb8, 0x37, 0xaf, 0x90, 0x28, 0x24,
+0x3b, 0xf8, 0xa4, 0x3a, 0x78, 0x37, 0x67, 0xd0, 0xc3, 0x40, 0x90, 0x28,
+0x80, 0x37, 0xcb, 0xa4, 0x3c, 0xa8, 0x37, 0xad, 0x90, 0x28, 0x24, 0x3b,
+0xe8, 0xa4, 0x3a, 0x68, 0x37, 0x65, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40,
+0x90, 0x28, 0x80, 0x37, 0xc3, 0xa4, 0x3c, 0x88, 0x37, 0xa9, 0x90, 0x28,
+0x24, 0x3b, 0xc8, 0xa4, 0x3a, 0x48, 0x37, 0x61, 0xd0, 0xc3, 0x40, 0x90,
+0x28, 0x80, 0x37, 0xbf, 0xa4, 0x3c, 0x78, 0x37, 0xa7, 0x90, 0x28, 0x24,
+0x3b, 0xb8, 0xa4, 0x3a, 0x38, 0x37, 0x5f, 0x93, 0x70, 0x91, 0xd8, 0xd5,
+0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x58, 0x84,
+0x37, 0xf3, 0xa4, 0x3d, 0x28, 0x37, 0xbd, 0x90, 0x28, 0x24, 0x3c, 0x68,
+0xa4, 0x3a, 0xe8, 0x37, 0x75, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c,
+0x3f, 0x28, 0x84, 0x37, 0xf0, 0xa4, 0x3d, 0x10, 0x37, 0xba, 0x90, 0x28,
+0x24, 0x3c, 0x50, 0xa4, 0x3a, 0xd0, 0x37, 0x72, 0xd5, 0x06, 0x80, 0xd0,
+0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xdf, 0xa4, 0x3c, 0xf8, 0x37, 0xb7,
+0x90, 0x28, 0x24, 0x3c, 0x38, 0xa4, 0x3a, 0xb8, 0x37, 0x6f, 0xd0, 0xc3,
+0x40, 0x90, 0x28, 0x80, 0x37, 0xd9, 0xa4, 0x3c, 0xe0, 0x37, 0xb4, 0x90,
+0x28, 0x24, 0x3c, 0x20, 0xa4, 0x3a, 0xa0, 0x37, 0x6c, 0x91, 0x98, 0xd5,
+0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd3, 0xa4, 0x3c,
+0xc8, 0x37, 0xb1, 0x90, 0x28, 0x24, 0x3c, 0x08, 0xa4, 0x3a, 0x88, 0x37,
+0x69, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xcd, 0xa4, 0x3c, 0xb0,
+0x37, 0xae, 0x90, 0x28, 0x24, 0x3b, 0xf0, 0xa4, 0x3a, 0x70, 0x37, 0x66,
+0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc7, 0xa4,
+0x3c, 0x98, 0x37, 0xab, 0x90, 0x28, 0x24, 0x3b, 0xd8, 0xa4, 0x3a, 0x58,
+0x37, 0x63, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc1, 0xa4, 0x3c,
+0x80, 0x37, 0xa8, 0x90, 0x28, 0x24, 0x3b, 0xc0, 0xa4, 0x3a, 0x40, 0x37,
+0x60, 0x99, 0xd8, 0x03, 0x90, 0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03,
+0x40, 0x51, 0x40, 0xc0, 0xa4, 0x23, 0x80, 0x34, 0x60, 0xd1, 0x42, 0x00,
+0xa4, 0x22, 0x80, 0x34, 0x40, 0xa4, 0x21, 0x80, 0x34, 0x20, 0x5b, 0x41,
+0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, 0x22, 0xa0, 0x34, 0x64, 0xd1,
+0x42, 0x00, 0xa4, 0x21, 0xa0, 0x34, 0x44, 0xa4, 0x20, 0xa0, 0x34, 0x24,
+0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4,
+0x22, 0xe0, 0x34, 0x6c, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xe0, 0x34, 0x4c,
+0xa4, 0x20, 0xe0, 0x34, 0x2c, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40,
+0xc0, 0xa4, 0x22, 0xc0, 0x34, 0x68, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xc0,
+0x34, 0x48, 0xa4, 0x20, 0xc0, 0x34, 0x28, 0xa8, 0x0b, 0x18, 0x13, 0xa8,
+0x96, 0x80, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x12, 0xb8, 0x32, 0x58, 0x24, 0x12, 0xb0, 0x90, 0x38,
+0xa4, 0x11, 0xe0, 0x32, 0x3d, 0x24, 0x11, 0xd8, 0x90, 0x60, 0x90, 0x38,
+0xa4, 0x11, 0x08, 0x32, 0x22, 0x24, 0x11, 0x00, 0x90, 0x38, 0xa4, 0x10,
+0x30, 0x32, 0x07, 0x24, 0x10, 0x28, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38,
+0xa4, 0x12, 0xa8, 0x32, 0x53, 0x24, 0x12, 0xa0, 0x90, 0x38, 0xa4, 0x11,
+0xd0, 0x32, 0x38, 0x24, 0x11, 0xc8, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10,
+0xf8, 0x32, 0x1d, 0x24, 0x10, 0xf0, 0x90, 0x38, 0xa4, 0x10, 0x20, 0x32,
+0x02, 0x24, 0x10, 0x18, 0xe4, 0xe1, 0xd0, 0x40, 0x37, 0x43, 0x99, 0x90,
+0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x90, 0x32,
+0x50, 0x24, 0x12, 0x88, 0x90, 0x38, 0xa4, 0x11, 0xb8, 0x32, 0x35, 0x24,
+0x11, 0xb0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xe0, 0x32, 0x1a, 0x24,
+0x10, 0xd8, 0x90, 0x38, 0xa4, 0x10, 0x08, 0x31, 0xff, 0x24, 0x10, 0x00,
+0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x78, 0x32, 0x4d, 0x24,
+0x12, 0x70, 0x90, 0x38, 0xa4, 0x11, 0xa0, 0x32, 0x32, 0x24, 0x11, 0x98,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xc8, 0x32, 0x17, 0x24, 0x10, 0xc0,
+0x90, 0x38, 0xa4, 0x0f, 0xf0, 0x31, 0xfc, 0x24, 0x0f, 0xe8, 0xe4, 0xe1,
+0xce, 0xc0, 0x37, 0x3d, 0x93, 0x78, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x60, 0x32, 0x4a, 0x24, 0x12, 0x58,
+0x90, 0x38, 0xa4, 0x11, 0x88, 0x32, 0x2f, 0x24, 0x11, 0x80, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x10, 0xb0, 0x32, 0x14, 0x24, 0x10, 0xa8, 0x90, 0x38,
+0xa4, 0x0f, 0xd8, 0x31, 0xf9, 0x24, 0x0f, 0xd0, 0x90, 0xc0, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x12, 0x48, 0x32, 0x47, 0x24, 0x12, 0x40, 0x90, 0x38,
+0xa4, 0x11, 0x70, 0x32, 0x2c, 0x24, 0x11, 0x68, 0x90, 0x60, 0x90, 0x38,
+0xa4, 0x10, 0x98, 0x32, 0x11, 0x24, 0x10, 0x90, 0x90, 0x38, 0xa4, 0x0f,
+0xc0, 0x31, 0xf6, 0x24, 0x0f, 0xb8, 0xec, 0xa1, 0x1e, 0x00, 0x02, 0x00,
+0x34, 0x7a, 0xa4, 0x39, 0xa8, 0x37, 0x37, 0x88, 0x00, 0x88, 0x10, 0x10,
+0x10, 0x10, 0x90, 0x38, 0xa4, 0x0f, 0xa8, 0x31, 0xf3, 0x24, 0x0f, 0xa0,
+0xe9, 0x61, 0x1d, 0x40, 0x02, 0x00, 0x34, 0x76, 0xe3, 0x61, 0xcb, 0xc0,
+0x37, 0x31, 0x95, 0x08, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x30, 0x32, 0x41, 0x24, 0x12, 0x28,
+0x90, 0x38, 0xa4, 0x11, 0x58, 0x32, 0x26, 0x24, 0x11, 0x50, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x10, 0x80, 0x32, 0x0b, 0x24, 0x10, 0x78, 0x90, 0x38,
+0xa4, 0x0f, 0x90, 0x31, 0xed, 0x24, 0x0f, 0x88, 0x90, 0xc0, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x12, 0x00, 0x32, 0x3e, 0x24, 0x11, 0xf8, 0x90, 0x38,
+0xa4, 0x11, 0x28, 0x32, 0x23, 0x24, 0x11, 0x20, 0x90, 0x60, 0x90, 0x38,
+0xa4, 0x10, 0x50, 0x32, 0x08, 0x24, 0x10, 0x48, 0x90, 0x38, 0xa4, 0x0f,
+0x60, 0x31, 0xea, 0x24, 0x0f, 0x58, 0xe4, 0xe1, 0xd0, 0x80, 0x37, 0x45,
+0x88, 0x01, 0x88, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x20,
+0x32, 0x42, 0x24, 0x12, 0x18, 0x90, 0x38, 0xa4, 0x11, 0x48, 0x32, 0x27,
+0x24, 0x11, 0x40, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0x70, 0x32, 0x0c,
+0x24, 0x10, 0x68, 0x90, 0x38, 0xa4, 0x0f, 0x80, 0x31, 0xee, 0x24, 0x0f,
+0x78, 0xe4, 0xe1, 0xcf, 0x00, 0x37, 0x3f, 0x92, 0xd0, 0x99, 0x50, 0x02,
+0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe9, 0x24, 0x0f,
+0x40, 0x90, 0x28, 0x80, 0x31, 0xe5, 0x24, 0x0f, 0x20, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x31, 0xe1, 0x24, 0x0f, 0x00, 0x90, 0x28, 0x80, 0x31, 0xdd,
+0x24, 0x0e, 0xe0, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe6,
+0x24, 0x0f, 0x38, 0x90, 0x28, 0x80, 0x31, 0xe2, 0x24, 0x0f, 0x18, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x31, 0xde, 0x24, 0x0e, 0xf8, 0x90, 0x28, 0x80,
+0x31, 0xda, 0x24, 0x0e, 0xd8, 0xec, 0xe1, 0xcd, 0xa1, 0x1f, 0x00, 0x37,
+0x39, 0x88, 0x00, 0x78, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x80, 0x31,
+0xd8, 0x24, 0x0e, 0xc8, 0xec, 0xe1, 0xcc, 0x21, 0x1d, 0x00, 0x37, 0x33,
+0xe5, 0xa1, 0x55, 0x40, 0x35, 0x51, 0xa0, 0x2a, 0x10, 0xa8, 0x16, 0x60,
+0x29, 0xd8, 0xa0, 0x0c, 0x48, 0xa0, 0x0a, 0xc8, 0x95, 0x60, 0x92, 0xb0,
+0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xa1, 0x80,
+0x31, 0xa0, 0x10, 0x10, 0x80, 0x31, 0x9f, 0x90, 0x70, 0x90, 0x38, 0xa4,
+0x08, 0x98, 0x31, 0xb3, 0xa4, 0x08, 0x90, 0x31, 0xb2, 0x10, 0x10, 0xa4,
+0x08, 0x88, 0x31, 0xb1, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09,
+0xb8, 0x31, 0xd7, 0xa4, 0x09, 0xb0, 0x31, 0xd6, 0x10, 0x10, 0xa4, 0x09,
+0xa8, 0x31, 0xd5, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x28, 0x31, 0xc5,
+0xa4, 0x09, 0x20, 0x31, 0xc4, 0x10, 0x10, 0xa4, 0x09, 0x18, 0x31, 0xc3,
+0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x9c, 0x80,
+0x31, 0x9e, 0x10, 0x10, 0x80, 0x31, 0x9d, 0x90, 0x70, 0x90, 0x38, 0xa4,
+0x08, 0x70, 0x31, 0xae, 0xa4, 0x08, 0x80, 0x31, 0xb0, 0x10, 0x10, 0xa4,
+0x08, 0x78, 0x31, 0xaf, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09,
+0x90, 0x31, 0xd2, 0xa4, 0x09, 0xa0, 0x31, 0xd4, 0x10, 0x10, 0xa4, 0x09,
+0x98, 0x31, 0xd3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x00, 0x31, 0xc0,
+0xa4, 0x09, 0x10, 0x31, 0xc2, 0x10, 0x10, 0xa4, 0x09, 0x08, 0x31, 0xc1,
+0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31,
+0x99, 0x80, 0x31, 0x9b, 0x10, 0x10, 0x80, 0x31, 0x9a, 0x90, 0x70, 0x90,
+0x38, 0xa4, 0x08, 0x58, 0x31, 0xab, 0xa4, 0x08, 0x68, 0x31, 0xad, 0x10,
+0x10, 0xa4, 0x08, 0x60, 0x31, 0xac, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x09, 0x78, 0x31, 0xcf, 0xa4, 0x09, 0x88, 0x31, 0xd1, 0x10, 0x10,
+0xa4, 0x09, 0x80, 0x31, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xe8,
+0x31, 0xbd, 0xa4, 0x08, 0xf8, 0x31, 0xbf, 0x10, 0x10, 0xa4, 0x08, 0xf0,
+0x31, 0xbe, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31,
+0x96, 0x80, 0x31, 0x98, 0x10, 0x10, 0x80, 0x31, 0x97, 0x90, 0x70, 0x90,
+0x38, 0xa4, 0x08, 0x40, 0x31, 0xa8, 0xa4, 0x08, 0x50, 0x31, 0xaa, 0x10,
+0x10, 0xa4, 0x08, 0x48, 0x31, 0xa9, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x09, 0x60, 0x31, 0xcc, 0xa4, 0x09, 0x70, 0x31, 0xce, 0x10, 0x10,
+0xa4, 0x09, 0x68, 0x31, 0xcd, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xd0,
+0x31, 0xba, 0xa4, 0x08, 0xe0, 0x31, 0xbc, 0x10, 0x10, 0xa4, 0x08, 0xd8,
+0x31, 0xbb, 0x10, 0x10, 0x90, 0xa8, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x31, 0x8d, 0x80, 0x31, 0x8f, 0x10, 0x10, 0x80, 0x31,
+0x8e, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0xc0, 0x60, 0xa0, 0x2a,
+0x80, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0x40, 0x60, 0xa0, 0x2a, 0x00, 0x97,
+0xf0, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x31, 0x93, 0x80, 0x31, 0x95, 0x10, 0x10, 0x80, 0x31, 0x94,
+0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x28, 0x31, 0xa5, 0xa4, 0x08, 0x38,
+0x31, 0xa7, 0x10, 0x10, 0xa4, 0x08, 0x30, 0x31, 0xa6, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x09, 0x48, 0x31, 0xc9, 0xa4, 0x09, 0x58, 0x31,
+0xcb, 0x10, 0x10, 0xa4, 0x09, 0x50, 0x31, 0xca, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x08, 0xb8, 0x31, 0xb7, 0xa4, 0x08, 0xc8, 0x31, 0xb9, 0x10, 0x10,
+0xa4, 0x08, 0xc0, 0x31, 0xb8, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x31, 0x90, 0x80, 0x31, 0x92, 0x10, 0x10, 0x80, 0x31, 0x91,
+0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x10, 0x31, 0xa2, 0xa4, 0x08, 0x20,
+0x31, 0xa4, 0x10, 0x10, 0xa4, 0x08, 0x18, 0x31, 0xa3, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x09, 0x30, 0x31, 0xc6, 0xa4, 0x09, 0x40, 0x31,
+0xc8, 0x10, 0x10, 0xa4, 0x09, 0x38, 0x31, 0xc7, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x08, 0xa0, 0x31, 0xb4, 0xa4, 0x08, 0xb0, 0x31, 0xb6, 0x10, 0x10,
+0xa4, 0x08, 0xa8, 0x31, 0xb5, 0x10, 0x10, 0x91, 0x40, 0x90, 0xa0, 0x90,
+0x50, 0x90, 0x28, 0x80, 0x30, 0xcb, 0x80, 0x30, 0xca, 0x90, 0x28, 0x80,
+0x30, 0xc9, 0x80, 0x30, 0xc8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xc4,
+0x80, 0x30, 0xc7, 0x90, 0x28, 0x80, 0x30, 0xc6, 0x80, 0x30, 0xc5, 0x90,
+0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbc, 0x80, 0x30, 0xc3, 0x90,
+0x28, 0x80, 0x30, 0xc2, 0x80, 0x30, 0xc1, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x30, 0xbd, 0x80, 0x30, 0xc0, 0x90, 0x28, 0x80, 0x30, 0xbf, 0x80, 0x30,
+0xbe, 0x91, 0x88, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31,
+0x3b, 0x10, 0x10, 0x80, 0x31, 0x3a, 0x90, 0x28, 0x81, 0x31, 0x3d, 0x10,
+0x10, 0x80, 0x31, 0x3c, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, 0x41, 0x10,
+0x10, 0x80, 0x31, 0x40, 0x90, 0x28, 0x81, 0x31, 0x3f, 0x10, 0x10, 0x80,
+0x31, 0x3e, 0x80, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x81, 0x31, 0x38,
+0x10, 0x10, 0x80, 0x31, 0x39, 0xa0, 0x0b, 0x90, 0xa0, 0x0a, 0xc8, 0x95,
+0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x31, 0x56, 0x80, 0x31, 0x55, 0x10, 0x10, 0x80, 0x31, 0x54, 0x90, 0x70,
+0x90, 0x38, 0xa4, 0x06, 0xe8, 0x31, 0x68, 0xa4, 0x06, 0xe0, 0x31, 0x67,
+0x10, 0x10, 0xa4, 0x06, 0xd8, 0x31, 0x66, 0x90, 0xb8, 0x90, 0x70, 0x90,
+0x38, 0xa4, 0x08, 0x08, 0x31, 0x8c, 0xa4, 0x08, 0x00, 0x31, 0x8b, 0x10,
+0x10, 0xa4, 0x07, 0xf8, 0x31, 0x8a, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07,
+0x78, 0x31, 0x7a, 0xa4, 0x07, 0x70, 0x31, 0x79, 0x10, 0x10, 0xa4, 0x07,
+0x68, 0x31, 0x78, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x31, 0x51, 0x80, 0x31, 0x53, 0x10, 0x10, 0x80, 0x31, 0x52, 0x90, 0x70,
+0x90, 0x38, 0xa4, 0x06, 0xc0, 0x31, 0x63, 0xa4, 0x06, 0xd0, 0x31, 0x65,
+0x10, 0x10, 0xa4, 0x06, 0xc8, 0x31, 0x64, 0x90, 0xb8, 0x90, 0x70, 0x90,
+0x38, 0xa4, 0x07, 0xe0, 0x31, 0x87, 0xa4, 0x07, 0xf0, 0x31, 0x89, 0x10,
+0x10, 0xa4, 0x07, 0xe8, 0x31, 0x88, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07,
+0x50, 0x31, 0x75, 0xa4, 0x07, 0x60, 0x31, 0x77, 0x10, 0x10, 0xa4, 0x07,
+0x58, 0x31, 0x76, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x31, 0x4e, 0x80, 0x31, 0x50, 0x10, 0x10, 0x80, 0x31, 0x4f,
+0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0xa8, 0x31, 0x60, 0xa4, 0x06, 0xb8,
+0x31, 0x62, 0x10, 0x10, 0xa4, 0x06, 0xb0, 0x31, 0x61, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x07, 0xc8, 0x31, 0x84, 0xa4, 0x07, 0xd8, 0x31,
+0x86, 0x10, 0x10, 0xa4, 0x07, 0xd0, 0x31, 0x85, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x07, 0x38, 0x31, 0x72, 0xa4, 0x07, 0x48, 0x31, 0x74, 0x10, 0x10,
+0xa4, 0x07, 0x40, 0x31, 0x73, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90,
+0x28, 0x80, 0x31, 0x4b, 0x80, 0x31, 0x4d, 0x10, 0x10, 0x80, 0x31, 0x4c,
+0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x90, 0x31, 0x5d, 0xa4, 0x06, 0xa0,
+0x31, 0x5f, 0x10, 0x10, 0xa4, 0x06, 0x98, 0x31, 0x5e, 0x90, 0xb8, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x07, 0xb0, 0x31, 0x81, 0xa4, 0x07, 0xc0, 0x31,
+0x83, 0x10, 0x10, 0xa4, 0x07, 0xb8, 0x31, 0x82, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x07, 0x20, 0x31, 0x6f, 0xa4, 0x07, 0x30, 0x31, 0x71, 0x10, 0x10,
+0xa4, 0x07, 0x28, 0x31, 0x70, 0x10, 0x10, 0x80, 0x10, 0x10, 0x10, 0x10,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x42, 0x80, 0x31, 0x44, 0x10, 0x10,
+0x80, 0x31, 0x43, 0x80, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x48, 0x80, 0x31, 0x4a, 0x10, 0x10,
+0x80, 0x31, 0x49, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x78, 0x31, 0x5a,
+0xa4, 0x06, 0x88, 0x31, 0x5c, 0x10, 0x10, 0xa4, 0x06, 0x80, 0x31, 0x5b,
+0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x98, 0x31, 0x7e, 0xa4,
+0x07, 0xa8, 0x31, 0x80, 0x10, 0x10, 0xa4, 0x07, 0xa0, 0x31, 0x7f, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x07, 0x08, 0x31, 0x6c, 0xa4, 0x07, 0x18, 0x31,
+0x6e, 0x10, 0x10, 0xa4, 0x07, 0x10, 0x31, 0x6d, 0x91, 0x40, 0x90, 0x88,
+0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x45, 0x80, 0x31, 0x47, 0x10, 0x10,
+0x80, 0x31, 0x46, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x60, 0x31, 0x57,
+0xa4, 0x06, 0x70, 0x31, 0x59, 0x10, 0x10, 0xa4, 0x06, 0x68, 0x31, 0x58,
+0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x80, 0x31, 0x7b, 0xa4,
+0x07, 0x90, 0x31, 0x7d, 0x10, 0x10, 0xa4, 0x07, 0x88, 0x31, 0x7c, 0x90,
+0x70, 0x90, 0x38, 0xa4, 0x06, 0xf0, 0x31, 0x69, 0xa4, 0x07, 0x00, 0x31,
+0x6b, 0x10, 0x10, 0xa4, 0x06, 0xf8, 0x31, 0x6a, 0x10, 0x10, 0x91, 0x40,
+0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbb, 0x80, 0x30, 0xba,
+0x90, 0x28, 0x80, 0x30, 0xb9, 0x80, 0x30, 0xb8, 0x90, 0x50, 0x90, 0x28,
+0x80, 0x30, 0xb4, 0x80, 0x30, 0xb7, 0x90, 0x28, 0x80, 0x30, 0xb6, 0x80,
+0x30, 0xb5, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xac, 0x80,
+0x30, 0xb3, 0x90, 0x28, 0x80, 0x30, 0xb2, 0x80, 0x30, 0xb1, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x30, 0xad, 0x80, 0x30, 0xb0, 0x90, 0x28, 0x80, 0x30,
+0xaf, 0x80, 0x30, 0xae, 0xc3, 0xc0, 0x30, 0x42, 0x9c, 0xe8, 0x07, 0x60,
+0x91, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x80, 0x88, 0x00, 0x80, 0x90, 0x50,
+0x90, 0x28, 0x80, 0x33, 0xf8, 0x80, 0x33, 0xf9, 0x81, 0x33, 0xef, 0xd0,
+0x41, 0x80, 0x24, 0x20, 0x90, 0x24, 0x20, 0x98, 0x10, 0x10, 0x80, 0x90,
+0x58, 0x80, 0x90, 0x28, 0x24, 0x1f, 0x90, 0x24, 0x1f, 0x98, 0x81, 0x24,
+0x1f, 0x50, 0x92, 0x68, 0x91, 0x00, 0x80, 0x90, 0x90, 0x90, 0x30, 0x80,
+0x24, 0x20, 0x00, 0x90, 0x38, 0xa4, 0x1f, 0xf8, 0x34, 0x06, 0x80, 0x34,
+0x05, 0x80, 0x90, 0x28, 0x80, 0x34, 0x0f, 0xa4, 0x1f, 0xe0, 0x34, 0x0e,
+0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x80, 0x34, 0x09, 0xa4, 0x1f,
+0xf0, 0x34, 0x08, 0x90, 0x28, 0x80, 0x34, 0x04, 0xa4, 0x1f, 0xe8, 0x34,
+0x03, 0x90, 0x50, 0x90, 0x28, 0x80, 0x34, 0x0d, 0x80, 0x34, 0x0c, 0x90,
+0x28, 0x24, 0x20, 0x88, 0x24, 0x20, 0x80, 0x90, 0x58, 0x80, 0x10, 0x10,
+0x80, 0x10, 0x10, 0x80, 0x33, 0xfb, 0x80, 0x90, 0x40, 0x10, 0x10, 0x80,
+0x24, 0x1f, 0x60, 0x80, 0x10, 0x10, 0x80, 0x33, 0xfa, 0x91, 0x58, 0x91,
+0x00, 0x90, 0x80, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xf6, 0x80,
+0x33, 0xf7, 0x81, 0x33, 0xee, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33,
+0xf4, 0x80, 0x33, 0xf5, 0x81, 0x33, 0xed, 0x83, 0x90, 0x28, 0x24, 0x1f,
+0x80, 0x24, 0x1f, 0x88, 0x90, 0xe8, 0x81, 0x90, 0x88, 0x90, 0x38, 0x10,
+0x10, 0x80, 0x34, 0x07, 0x90, 0x28, 0x80, 0x34, 0x02, 0x80, 0x34, 0x01,
+0x80, 0x90, 0x28, 0x80, 0x34, 0x0b, 0x80, 0x34, 0x0a, 0x82, 0x10, 0x10,
+0x80, 0x24, 0x1f, 0x58, 0x97, 0x10, 0x9e, 0x10, 0x06, 0x98, 0x93, 0x00,
+0x91, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x80, 0x30,
+0x71, 0x24, 0x03, 0x78, 0x90, 0x38, 0xa4, 0x04, 0x10, 0x30, 0x83, 0x24,
+0x04, 0x08, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x30, 0x30, 0xa7, 0x24,
+0x05, 0x28, 0x90, 0x38, 0xa4, 0x04, 0xa0, 0x30, 0x95, 0x24, 0x04, 0x98,
+0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x70, 0x30, 0x6c, 0x24,
+0x03, 0x68, 0x90, 0x38, 0xa4, 0x04, 0x00, 0x30, 0x7e, 0x24, 0x03, 0xf8,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x20, 0x30, 0xa2, 0x24, 0x05, 0x18,
+0x90, 0x38, 0xa4, 0x04, 0x90, 0x30, 0x90, 0x24, 0x04, 0x88, 0x91, 0x80,
+0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x58, 0x30, 0x69, 0x24,
+0x03, 0x50, 0x90, 0x38, 0xa4, 0x03, 0xe8, 0x30, 0x7b, 0x24, 0x03, 0xe0,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x08, 0x30, 0x9f, 0x24, 0x05, 0x00,
+0x90, 0x38, 0xa4, 0x04, 0x78, 0x30, 0x8d, 0x24, 0x04, 0x70, 0x90, 0xc0,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x40, 0x30, 0x66, 0x24, 0x03, 0x38,
+0x90, 0x38, 0xa4, 0x03, 0xd0, 0x30, 0x78, 0x24, 0x03, 0xc8, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x04, 0xf0, 0x30, 0x9c, 0x24, 0x04, 0xe8, 0x90, 0x38,
+0xa4, 0x04, 0x60, 0x30, 0x8a, 0x24, 0x04, 0x58, 0x10, 0x10, 0x80, 0x10,
+0x10, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x02, 0xf8, 0x30, 0x5d, 0x24, 0x02,
+0xf0, 0xd7, 0x42, 0x00, 0xa4, 0x39, 0x58, 0x37, 0x2d, 0xa4, 0x39, 0x38,
+0x37, 0x29, 0x9c, 0xe0, 0x06, 0x90, 0x93, 0x00, 0x91, 0x80, 0x90, 0xc0,
+0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x28, 0x30, 0x63, 0x24, 0x03, 0x20,
+0x90, 0x38, 0xa4, 0x03, 0xb8, 0x30, 0x75, 0x24, 0x03, 0xb0, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x04, 0xd8, 0x30, 0x99, 0x24, 0x04, 0xd0, 0x90, 0x38,
+0xa4, 0x04, 0x48, 0x30, 0x87, 0x24, 0x04, 0x40, 0x90, 0xc0, 0x90, 0x60,
+0x90, 0x38, 0xa4, 0x03, 0x10, 0x30, 0x60, 0x24, 0x03, 0x08, 0x90, 0x38,
+0xa4, 0x03, 0xa0, 0x30, 0x72, 0x24, 0x03, 0x98, 0x90, 0x60, 0x90, 0x38,
+0xa4, 0x04, 0xc0, 0x30, 0x96, 0x24, 0x04, 0xb8, 0x90, 0x38, 0xa4, 0x04,
+0x30, 0x30, 0x84, 0x24, 0x04, 0x28, 0x10, 0x10, 0x90, 0xe0, 0x90, 0x70,
+0x90, 0x38, 0xa4, 0x02, 0x88, 0x30, 0x52, 0xa4, 0x02, 0x78, 0x30, 0x50,
+0x90, 0x38, 0xa4, 0x02, 0x70, 0x30, 0x4b, 0xa4, 0x02, 0x60, 0x30, 0x4d,
+0x90, 0x70, 0x90, 0x38, 0xa4, 0x02, 0x50, 0x30, 0x43, 0xa4, 0x02, 0x40,
+0x30, 0x49, 0x90, 0x38, 0xa4, 0x02, 0x38, 0x30, 0x44, 0xa4, 0x02, 0x28,
+0x30, 0x46, 0x91, 0x48, 0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80,
+0x30, 0x56, 0x24, 0x02, 0xa8, 0x90, 0x28, 0x80, 0x30, 0x58, 0x24, 0x02,
+0xb8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0x5c, 0x24, 0x02, 0xd8, 0x90,
+0x28, 0x80, 0x30, 0x5a, 0x24, 0x02, 0xc8, 0x80, 0x10, 0x10, 0x10, 0x10,
+0x90, 0x28, 0x80, 0x30, 0x53, 0x24, 0x02, 0xa0, 0xd7, 0x42, 0x00, 0xa4,
+0x39, 0x60, 0x37, 0x2e, 0xa4, 0x39, 0x40, 0x37, 0x2a, 0xa0, 0x14, 0x68,
+0xa0, 0x10, 0x90, 0xa0, 0x0c, 0x60, 0x9e, 0x88, 0x09, 0xd0, 0x94, 0xf0,
+0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4c, 0x40,
+0x85, 0x35, 0x4d, 0xcb, 0x61, 0x45, 0x00, 0x85, 0x35, 0x23, 0x9a, 0x00,
+0x03, 0xf8, 0x91, 0x98, 0x80, 0x91, 0x10, 0x90, 0xa0, 0x90, 0x68, 0x90,
+0x20, 0x3a, 0x75, 0xc9, 0xe2, 0x9c, 0xc0, 0x85, 0x35, 0x4b, 0xa4, 0x53,
+0x88, 0x3a, 0x72, 0x90, 0x38, 0xa4, 0x53, 0x50, 0x3a, 0x6b, 0xa4, 0x53,
+0x40, 0x3a, 0x69, 0x90, 0x48, 0x10, 0x10, 0xa4, 0x53, 0x08, 0x3a, 0x62,
+0x10, 0x10, 0x80, 0x3a, 0x5e, 0x81, 0x10, 0x10, 0x80, 0xa4, 0x52, 0xd8,
+0x3a, 0x5c, 0x91, 0xb0, 0x91, 0x60, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38,
+0xa4, 0x53, 0x78, 0x3a, 0x70, 0xa4, 0x53, 0x68, 0x3a, 0x6e, 0x90, 0x38,
+0xa4, 0x53, 0x30, 0x3a, 0x67, 0xa4, 0x53, 0x20, 0x3a, 0x65, 0x90, 0x48,
+0x10, 0x10, 0xa4, 0x52, 0xf8, 0x3a, 0x60, 0x10, 0x10, 0x80, 0x3a, 0x5d,
+0x90, 0x28, 0x80, 0x3a, 0x56, 0x80, 0x3a, 0x55, 0x81, 0x10, 0x10, 0x80,
+0xa4, 0x52, 0xc8, 0x3a, 0x5a, 0xcb, 0x61, 0x44, 0xc0, 0x85, 0x35, 0x22,
+0x90, 0xd8, 0x88, 0x00, 0x90, 0x84, 0x90, 0x38, 0xc1, 0xc0, 0x85, 0x3a,
+0x78, 0xc9, 0xe1, 0x4c, 0x00, 0x85, 0x35, 0x49, 0xcb, 0x61, 0x44, 0x80,
+0x85, 0x35, 0x21, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b,
+0xc0, 0x85, 0x35, 0x47, 0xcb, 0x61, 0x44, 0x40, 0x85, 0x35, 0x20, 0x91,
+0xf8, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b,
+0x40, 0x85, 0x35, 0x43, 0xcb, 0x61, 0x43, 0xc0, 0x85, 0x35, 0x1e, 0x88,
+0x01, 0x00, 0x90, 0xa0, 0x81, 0x90, 0x70, 0x80, 0x90, 0x20, 0x3a, 0x6c,
+0xc9, 0xe1, 0x4b, 0x00, 0x85, 0x35, 0x41, 0x81, 0x3a, 0x63, 0x81, 0x10,
+0x10, 0x80, 0xa4, 0x52, 0xb8, 0x3a, 0x58, 0xcb, 0x61, 0x43, 0x80, 0x85,
+0x35, 0x1d, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1,
+0x4a, 0xc0, 0x85, 0x35, 0x3f, 0xcb, 0x61, 0x43, 0x40, 0x85, 0x35, 0x1c,
+0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x80, 0x85, 0x35,
+0x3d, 0xcb, 0x61, 0x43, 0x00, 0x85, 0x35, 0x1b, 0x92, 0x38, 0x81, 0x91,
+0x68, 0x91, 0x18, 0x90, 0x80, 0x90, 0x40, 0x80, 0xa4, 0x54, 0x38, 0x3a,
+0x88, 0x80, 0xa4, 0x54, 0x30, 0x3a, 0x85, 0x90, 0x28, 0x81, 0x3a, 0x84,
+0x90, 0x38, 0xa4, 0x54, 0x10, 0x3a, 0x83, 0xa4, 0x54, 0x00, 0x3a, 0x81,
+0x90, 0x28, 0x80, 0x3a, 0x7f, 0x80, 0x3a, 0x7e, 0x80, 0x90, 0x40, 0x10,
+0x10, 0x80, 0x24, 0x53, 0xe8, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x53, 0xd8,
+0x3a, 0x7c, 0xa4, 0x53, 0xc8, 0x3a, 0x7a, 0x90, 0x28, 0x80, 0x3a, 0x77,
+0x80, 0x3a, 0x76, 0x9a, 0xd0, 0x03, 0xe0, 0x91, 0x60, 0x90, 0xb0, 0x88,
+0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x00, 0x85, 0x35, 0x39,
+0xcb, 0x61, 0x42, 0x80, 0x85, 0x35, 0x19, 0x88, 0x00, 0x68, 0x84, 0x10,
+0x10, 0xc9, 0xe1, 0x49, 0xc0, 0x85, 0x35, 0x37, 0xcb, 0x61, 0x42, 0x40,
+0x85, 0x35, 0x18, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9,
+0xe1, 0x49, 0x80, 0x85, 0x35, 0x35, 0xcb, 0x61, 0x42, 0x00, 0x85, 0x35,
+0x17, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x49, 0x40, 0x85,
+0x35, 0x33, 0xcb, 0x61, 0x41, 0xc0, 0x85, 0x35, 0x16, 0x90, 0x90, 0x90,
+0x48, 0xcb, 0xa1, 0x40, 0x00, 0x85, 0x35, 0x05, 0xcb, 0xa1, 0x3f, 0xc0,
+0x85, 0x35, 0x04, 0x90, 0x48, 0xcb, 0xa1, 0x3f, 0x80, 0x85, 0x35, 0x03,
+0xcb, 0xa1, 0x3f, 0x40, 0x85, 0x35, 0x02, 0xcb, 0xa2, 0x94, 0xc0, 0x80,
+0x3a, 0x54, 0x92, 0x40, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27,
+0x60, 0x84, 0x24, 0x27, 0xd8, 0x8c, 0x27, 0x58, 0x84, 0x24, 0x27, 0xd0,
+0x90, 0x48, 0x8c, 0x27, 0x50, 0x84, 0x24, 0x27, 0xc8, 0x8c, 0x27, 0x48,
+0x84, 0x24, 0x27, 0xc0, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x38, 0x84,
+0x24, 0x27, 0xb0, 0x8c, 0x27, 0x30, 0x84, 0x24, 0x27, 0xa8, 0x90, 0x48,
+0x8c, 0x27, 0x28, 0x84, 0x24, 0x27, 0xa0, 0x8c, 0x27, 0x20, 0x84, 0x24,
+0x27, 0x98, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x10, 0x84,
+0x24, 0x27, 0x88, 0x8c, 0x27, 0x08, 0x84, 0x24, 0x27, 0x80, 0x90, 0x48,
+0x8c, 0x27, 0x00, 0x84, 0x24, 0x27, 0x78, 0x8c, 0x26, 0xf8, 0x84, 0x24,
+0x27, 0x70, 0x90, 0x38, 0xa4, 0x26, 0xe0, 0x34, 0xdd, 0xa4, 0x26, 0xd0,
+0x34, 0xdb, 0xa0, 0x0f, 0x50, 0xa0, 0x09, 0x08, 0x9a, 0x30, 0x04, 0x40,
+0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x92, 0xc0,
+0x3a, 0x43, 0xe5, 0x22, 0x8a, 0xc0, 0x3a, 0x3f, 0xcb, 0x61, 0x32, 0x40,
+0x85, 0x34, 0xd8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x82, 0xc0, 0x3a,
+0x03, 0xe5, 0x22, 0x7a, 0xc0, 0x39, 0xff, 0xcb, 0x61, 0x32, 0x00, 0x85,
+0x34, 0xd7, 0x90, 0x48, 0xcb, 0xa1, 0x31, 0xc0, 0x85, 0x34, 0xd6, 0xcb,
+0xa1, 0x31, 0x80, 0x85, 0x34, 0xd5, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50,
+0x00, 0x80, 0xe5, 0x22, 0x6c, 0xc0, 0x39, 0xcb, 0xe5, 0x22, 0x60, 0xc0,
+0x39, 0x9b, 0xcb, 0x61, 0x31, 0x00, 0x85, 0x34, 0xd3, 0x98, 0x50, 0x00,
+0x80, 0xe5, 0x22, 0x54, 0xc0, 0x39, 0x6b, 0xe5, 0x22, 0x48, 0xc0, 0x39,
+0x3b, 0xcb, 0x61, 0x30, 0xc0, 0x85, 0x34, 0xd2, 0x90, 0x48, 0xcb, 0xa1,
+0x30, 0x80, 0x85, 0x34, 0xd1, 0xcb, 0xa1, 0x30, 0x40, 0x85, 0x34, 0xd0,
+0x92, 0x20, 0x91, 0x30, 0x90, 0xb8, 0xd5, 0x03, 0x00, 0xc0, 0xc0, 0x81,
+0x8c, 0x01, 0xa0, 0x84, 0x30, 0x3e, 0xc0, 0xc0, 0x81, 0x8c, 0x01, 0x80,
+0x84, 0x30, 0x3c, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, 0x30, 0x28, 0xc0,
+0xc0, 0x81, 0x30, 0x24, 0x90, 0x78, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81,
+0x30, 0x1c, 0xc0, 0xc0, 0x81, 0x30, 0x18, 0xd5, 0x02, 0x00, 0xc0, 0xc0,
+0x81, 0x30, 0x10, 0xc0, 0xc0, 0x81, 0x30, 0x0c, 0x91, 0x70, 0x90, 0xd8,
+0xd5, 0x03, 0x80, 0xc8, 0xe2, 0x40, 0xc0, 0x81, 0x8c, 0x01, 0xc0, 0x84,
+0x30, 0x40, 0xc8, 0xe2, 0x42, 0xc0, 0x81, 0x8c, 0x01, 0x90, 0x84, 0x30,
+0x3d, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x3f, 0xc0, 0x81, 0x30, 0x2c, 0xc8,
+0xe2, 0x3a, 0x40, 0x81, 0x30, 0x26, 0x90, 0x98, 0xd5, 0x02, 0x80, 0xc8,
+0xe2, 0x2f, 0x40, 0x81, 0x30, 0x20, 0xc8, 0xe2, 0x31, 0x40, 0x81, 0x30,
+0x1a, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x2e, 0x40, 0x81, 0x30, 0x14, 0xc8,
+0xe2, 0x28, 0xc0, 0x81, 0x30, 0x0e, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x90,
+0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x86, 0xc0, 0x3a, 0x13,
+0xe5, 0x22, 0x88, 0xc0, 0x3a, 0x37, 0xcb, 0x61, 0x2f, 0xc0, 0x85, 0x34,
+0xce, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x76, 0xc0, 0x39, 0xd3, 0xe5,
+0x22, 0x78, 0xc0, 0x39, 0xf7, 0xcb, 0x61, 0x2f, 0x80, 0x85, 0x34, 0xcd,
+0x90, 0x48, 0xcb, 0xa1, 0x2f, 0x40, 0x85, 0x34, 0xcc, 0xcb, 0xa1, 0x2f,
+0x00, 0x85, 0x34, 0xcb, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80,
+0xe5, 0x22, 0x68, 0xc0, 0x39, 0xbb, 0xe5, 0x22, 0x5c, 0xc0, 0x39, 0x8b,
+0xcb, 0x61, 0x2d, 0x40, 0x85, 0x34, 0xba, 0x98, 0x50, 0x00, 0x80, 0xe5,
+0x22, 0x50, 0xc0, 0x39, 0x5b, 0xe5, 0x22, 0x44, 0xc0, 0x39, 0x2b, 0xcb,
+0x61, 0x2d, 0x00, 0x85, 0x34, 0xb9, 0x90, 0x48, 0xcb, 0xa1, 0x2c, 0xc0,
+0x85, 0x34, 0xb8, 0xcb, 0xa1, 0x2c, 0x80, 0x85, 0x34, 0xb7, 0x91, 0x00,
+0x90, 0x80, 0x90, 0x40, 0xe5, 0x20, 0x02, 0x40, 0x30, 0x0a, 0xe5, 0x20,
+0x01, 0x80, 0x30, 0x07, 0x90, 0x40, 0xe5, 0x20, 0x00, 0xc0, 0x30, 0x04,
+0xe5, 0x20, 0x00, 0x00, 0x30, 0x01, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22,
+0x35, 0xc0, 0x38, 0xcd, 0xe5, 0x22, 0x38, 0x00, 0x38, 0xf5, 0x90, 0x40,
+0xe5, 0x22, 0x24, 0x40, 0x38, 0x87, 0xe5, 0x22, 0x26, 0x80, 0x38, 0xaf,
+0x80, 0x99, 0x28, 0x02, 0xf0, 0x8c, 0x25, 0x48, 0x90, 0x80, 0x90, 0x40,
+0xe5, 0x22, 0x8c, 0xc0, 0x3a, 0x2f, 0xe5, 0x22, 0x89, 0xc0, 0x3a, 0x3b,
+0x90, 0x40, 0xe5, 0x22, 0x7c, 0xc0, 0x39, 0xef, 0xe5, 0x22, 0x79, 0xc0,
+0x39, 0xfb, 0x91, 0x48, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22,
+0x6a, 0xc0, 0x39, 0xc3, 0xe5, 0x22, 0x5e, 0xc0, 0x39, 0x93, 0xcb, 0x61,
+0x2b, 0x00, 0x85, 0x34, 0xb0, 0x90, 0x40, 0xe5, 0x22, 0x52, 0xc0, 0x39,
+0x63, 0xe5, 0x22, 0x46, 0xc0, 0x39, 0x33, 0x90, 0x48, 0xcb, 0xa1, 0x2a,
+0x80, 0x85, 0x34, 0xae, 0xcb, 0xa1, 0x2a, 0xc0, 0x85, 0x34, 0xaf, 0x10,
+0x10, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, 0x3c, 0x40, 0x38, 0xed, 0xe5,
+0x22, 0x39, 0x40, 0x38, 0xfb, 0x90, 0x40, 0xe5, 0x22, 0x2a, 0xc0, 0x38,
+0xa7, 0xe5, 0x22, 0x27, 0xc0, 0x38, 0xb5,
+};
+
+static const struct ia64_dis_names ia64_dis_names[] = {
+{ 0x51, 41, 0, 10 },
+{ 0x31, 41, 1, 20 },
+{ 0x11, 42, 0, 19 },
+{ 0x29, 41, 0, 12 },
+{ 0x19, 41, 1, 24 },
+{ 0x9, 42, 0, 23 },
+{ 0x15, 41, 0, 14 },
+{ 0xd, 41, 1, 28 },
+{ 0x5, 42, 0, 27 },
+{ 0xb, 41, 0, 16 },
+{ 0x7, 41, 1, 32 },
+{ 0x3, 42, 0, 31 },
+{ 0x51, 39, 1, 58 },
+{ 0x50, 39, 0, 34 },
+{ 0xd1, 39, 1, 57 },
+{ 0xd0, 39, 0, 33 },
+{ 0x31, 39, 1, 68 },
+{ 0x30, 39, 1, 44 },
+{ 0x11, 40, 1, 67 },
+{ 0x10, 40, 0, 43 },
+{ 0x71, 39, 1, 66 },
+{ 0x70, 39, 1, 42 },
+{ 0x31, 40, 1, 65 },
+{ 0x30, 40, 0, 41 },
+{ 0x29, 39, 1, 60 },
+{ 0x28, 39, 0, 36 },
+{ 0x69, 39, 1, 59 },
+{ 0x68, 39, 0, 35 },
+{ 0x19, 39, 1, 72 },
+{ 0x18, 39, 1, 48 },
+{ 0x9, 40, 1, 71 },
+{ 0x8, 40, 0, 47 },
+{ 0x39, 39, 1, 70 },
+{ 0x38, 39, 1, 46 },
+{ 0x19, 40, 1, 69 },
+{ 0x18, 40, 0, 45 },
+{ 0x15, 39, 1, 62 },
+{ 0x14, 39, 0, 38 },
+{ 0x35, 39, 1, 61 },
+{ 0x34, 39, 0, 37 },
+{ 0xd, 39, 1, 76 },
+{ 0xc, 39, 1, 52 },
+{ 0x5, 40, 1, 75 },
+{ 0x4, 40, 0, 51 },
+{ 0x1d, 39, 1, 74 },
+{ 0x1c, 39, 1, 50 },
+{ 0xd, 40, 1, 73 },
+{ 0xc, 40, 0, 49 },
+{ 0xb, 39, 1, 64 },
+{ 0xa, 39, 0, 40 },
+{ 0x1b, 39, 1, 63 },
+{ 0x1a, 39, 0, 39 },
+{ 0x7, 39, 1, 80 },
+{ 0x6, 39, 1, 56 },
+{ 0x3, 40, 1, 79 },
+{ 0x2, 40, 0, 55 },
+{ 0xf, 39, 1, 78 },
+{ 0xe, 39, 1, 54 },
+{ 0x7, 40, 1, 77 },
+{ 0x6, 40, 0, 53 },
+{ 0x8, 38, 0, 82 },
+{ 0x18, 38, 0, 81 },
+{ 0x1, 38, 1, 86 },
+{ 0x2, 38, 0, 85 },
+{ 0x3, 38, 1, 84 },
+{ 0x4, 38, 0, 83 },
+{ 0x1, 336, 0, 87 },
+{ 0x20, 289, 0, 98 },
+{ 0x220, 289, 0, 94 },
+{ 0x1220, 289, 0, 91 },
+{ 0xa20, 289, 0, 92 },
+{ 0x620, 289, 0, 93 },
+{ 0x120, 289, 0, 95 },
+{ 0xa0, 289, 0, 96 },
+{ 0x60, 289, 0, 97 },
+{ 0x10, 289, 0, 102 },
+{ 0x90, 289, 0, 99 },
+{ 0x50, 289, 0, 100 },
+{ 0x30, 289, 0, 101 },
+{ 0x8, 289, 0, 103 },
+{ 0x4, 289, 0, 104 },
+{ 0x2, 289, 0, 105 },
+{ 0x1, 289, 0, 106 },
+{ 0x1, 411, 0, 108 },
+{ 0x3, 411, 0, 107 },
+{ 0x2, 417, 0, 109 },
+{ 0x1, 417, 0, 110 },
+{ 0x2, 413, 0, 111 },
+{ 0x1, 413, 0, 112 },
+{ 0x2, 415, 0, 113 },
+{ 0x1, 415, 0, 114 },
+{ 0x2, 419, 0, 115 },
+{ 0x1, 419, 0, 116 },
+{ 0x1, 268, 0, 143 },
+{ 0x5, 268, 0, 141 },
+{ 0x3, 268, 0, 142 },
+{ 0x140, 277, 0, 119 },
+{ 0x540, 277, 0, 117 },
+{ 0x340, 277, 0, 118 },
+{ 0xc0, 277, 0, 131 },
+{ 0x2c0, 277, 0, 129 },
+{ 0x1c0, 277, 0, 130 },
+{ 0x20, 277, 0, 146 },
+{ 0xa0, 277, 0, 144 },
+{ 0x60, 277, 0, 145 },
+{ 0x10, 277, 0, 158 },
+{ 0x50, 277, 0, 156 },
+{ 0x30, 277, 0, 157 },
+{ 0x8, 277, 0, 170 },
+{ 0x28, 277, 0, 168 },
+{ 0x18, 277, 0, 169 },
+{ 0x4, 277, 0, 180 },
+{ 0x2, 277, 0, 181 },
+{ 0x1, 277, 0, 182 },
+{ 0x140, 271, 0, 122 },
+{ 0x540, 271, 0, 120 },
+{ 0x340, 271, 0, 121 },
+{ 0xc0, 271, 0, 134 },
+{ 0x2c0, 271, 0, 132 },
+{ 0x1c0, 271, 0, 133 },
+{ 0x20, 271, 0, 149 },
+{ 0xa0, 271, 0, 147 },
+{ 0x60, 271, 0, 148 },
+{ 0x10, 271, 0, 161 },
+{ 0x50, 271, 0, 159 },
+{ 0x30, 271, 0, 160 },
+{ 0x8, 271, 0, 173 },
+{ 0x28, 271, 0, 171 },
+{ 0x18, 271, 0, 172 },
+{ 0x4, 271, 0, 183 },
+{ 0x2, 271, 0, 184 },
+{ 0x1, 271, 0, 185 },
+{ 0x140, 274, 0, 125 },
+{ 0x540, 274, 0, 123 },
+{ 0x340, 274, 0, 124 },
+{ 0xc0, 274, 0, 137 },
+{ 0x2c0, 274, 0, 135 },
+{ 0x1c0, 274, 0, 136 },
+{ 0x20, 274, 0, 152 },
+{ 0xa0, 274, 0, 150 },
+{ 0x60, 274, 0, 151 },
+{ 0x10, 274, 0, 164 },
+{ 0x50, 274, 0, 162 },
+{ 0x30, 274, 0, 163 },
+{ 0x8, 274, 0, 176 },
+{ 0x28, 274, 0, 174 },
+{ 0x18, 274, 0, 175 },
+{ 0x4, 274, 0, 186 },
+{ 0x2, 274, 0, 187 },
+{ 0x1, 274, 0, 188 },
+{ 0x140, 286, 0, 128 },
+{ 0x540, 286, 0, 126 },
+{ 0x340, 286, 0, 127 },
+{ 0xc0, 286, 0, 140 },
+{ 0x2c0, 286, 0, 138 },
+{ 0x1c0, 286, 0, 139 },
+{ 0x20, 286, 0, 155 },
+{ 0xa0, 286, 0, 153 },
+{ 0x60, 286, 0, 154 },
+{ 0x10, 286, 0, 167 },
+{ 0x50, 286, 0, 165 },
+{ 0x30, 286, 0, 166 },
+{ 0x8, 286, 0, 179 },
+{ 0x28, 286, 0, 177 },
+{ 0x18, 286, 0, 178 },
+{ 0x4, 286, 0, 189 },
+{ 0x2, 286, 0, 190 },
+{ 0x1, 286, 0, 191 },
+{ 0x8, 390, 0, 192 },
+{ 0x4, 390, 0, 193 },
+{ 0x2, 390, 0, 194 },
+{ 0x1, 390, 0, 195 },
+{ 0x20, 288, 0, 203 },
+{ 0x220, 288, 0, 199 },
+{ 0x1220, 288, 0, 196 },
+{ 0xa20, 288, 0, 197 },
+{ 0x620, 288, 0, 198 },
+{ 0x120, 288, 0, 200 },
+{ 0xa0, 288, 0, 201 },
+{ 0x60, 288, 0, 202 },
+{ 0x10, 288, 0, 207 },
+{ 0x90, 288, 0, 204 },
+{ 0x50, 288, 0, 205 },
+{ 0x30, 288, 0, 206 },
+{ 0x8, 288, 0, 208 },
+{ 0x4, 288, 0, 209 },
+{ 0x2, 288, 0, 210 },
+{ 0x1, 288, 0, 211 },
+{ 0x20, 287, 0, 219 },
+{ 0x220, 287, 0, 215 },
+{ 0x1220, 287, 0, 212 },
+{ 0xa20, 287, 0, 213 },
+{ 0x620, 287, 0, 214 },
+{ 0x120, 287, 0, 216 },
+{ 0xa0, 287, 0, 217 },
+{ 0x60, 287, 0, 218 },
+{ 0x10, 287, 0, 223 },
+{ 0x90, 287, 0, 220 },
+{ 0x50, 287, 0, 221 },
+{ 0x30, 287, 0, 222 },
+{ 0x8, 287, 0, 224 },
+{ 0x4, 287, 0, 225 },
+{ 0x2, 287, 0, 226 },
+{ 0x1, 287, 0, 227 },
+{ 0x140, 279, 0, 230 },
+{ 0x540, 279, 0, 228 },
+{ 0x340, 279, 0, 229 },
+{ 0xc0, 279, 0, 239 },
+{ 0x2c0, 279, 0, 237 },
+{ 0x1c0, 279, 0, 238 },
+{ 0x20, 279, 0, 248 },
+{ 0xa0, 279, 0, 246 },
+{ 0x60, 279, 0, 247 },
+{ 0x10, 279, 0, 257 },
+{ 0x50, 279, 0, 255 },
+{ 0x30, 279, 0, 256 },
+{ 0x8, 279, 0, 266 },
+{ 0x28, 279, 0, 264 },
+{ 0x18, 279, 0, 265 },
+{ 0x4, 279, 0, 273 },
+{ 0x2, 279, 0, 274 },
+{ 0x1, 279, 0, 275 },
+{ 0x140, 281, 0, 233 },
+{ 0x540, 281, 0, 231 },
+{ 0x340, 281, 0, 232 },
+{ 0xc0, 281, 0, 242 },
+{ 0x2c0, 281, 0, 240 },
+{ 0x1c0, 281, 0, 241 },
+{ 0x20, 281, 0, 251 },
+{ 0xa0, 281, 0, 249 },
+{ 0x60, 281, 0, 250 },
+{ 0x10, 281, 0, 260 },
+{ 0x50, 281, 0, 258 },
+{ 0x30, 281, 0, 259 },
+{ 0x8, 281, 0, 269 },
+{ 0x28, 281, 0, 267 },
+{ 0x18, 281, 0, 268 },
+{ 0x4, 281, 0, 276 },
+{ 0x2, 281, 0, 277 },
+{ 0x1, 281, 0, 278 },
+{ 0x140, 283, 0, 236 },
+{ 0x540, 283, 0, 234 },
+{ 0x340, 283, 0, 235 },
+{ 0xc0, 283, 0, 245 },
+{ 0x2c0, 283, 0, 243 },
+{ 0x1c0, 283, 0, 244 },
+{ 0x20, 283, 0, 254 },
+{ 0xa0, 283, 0, 252 },
+{ 0x60, 283, 0, 253 },
+{ 0x10, 283, 0, 263 },
+{ 0x50, 283, 0, 261 },
+{ 0x30, 283, 0, 262 },
+{ 0x8, 283, 0, 272 },
+{ 0x28, 283, 0, 270 },
+{ 0x18, 283, 0, 271 },
+{ 0x4, 283, 0, 279 },
+{ 0x2, 283, 0, 280 },
+{ 0x1, 283, 0, 281 },
+{ 0x140, 278, 0, 284 },
+{ 0x540, 278, 0, 282 },
+{ 0x340, 278, 0, 283 },
+{ 0xc0, 278, 0, 293 },
+{ 0x2c0, 278, 0, 291 },
+{ 0x1c0, 278, 0, 292 },
+{ 0x20, 278, 0, 302 },
+{ 0xa0, 278, 0, 300 },
+{ 0x60, 278, 0, 301 },
+{ 0x10, 278, 0, 311 },
+{ 0x50, 278, 0, 309 },
+{ 0x30, 278, 0, 310 },
+{ 0x8, 278, 0, 320 },
+{ 0x28, 278, 0, 318 },
+{ 0x18, 278, 0, 319 },
+{ 0x4, 278, 0, 327 },
+{ 0x2, 278, 0, 328 },
+{ 0x1, 278, 0, 329 },
+{ 0x140, 280, 0, 287 },
+{ 0x540, 280, 0, 285 },
+{ 0x340, 280, 0, 286 },
+{ 0xc0, 280, 0, 296 },
+{ 0x2c0, 280, 0, 294 },
+{ 0x1c0, 280, 0, 295 },
+{ 0x20, 280, 0, 305 },
+{ 0xa0, 280, 0, 303 },
+{ 0x60, 280, 0, 304 },
+{ 0x10, 280, 0, 314 },
+{ 0x50, 280, 0, 312 },
+{ 0x30, 280, 0, 313 },
+{ 0x8, 280, 0, 323 },
+{ 0x28, 280, 0, 321 },
+{ 0x18, 280, 0, 322 },
+{ 0x4, 280, 0, 330 },
+{ 0x2, 280, 0, 331 },
+{ 0x1, 280, 0, 332 },
+{ 0x140, 282, 0, 290 },
+{ 0x540, 282, 0, 288 },
+{ 0x340, 282, 0, 289 },
+{ 0xc0, 282, 0, 299 },
+{ 0x2c0, 282, 0, 297 },
+{ 0x1c0, 282, 0, 298 },
+{ 0x20, 282, 0, 308 },
+{ 0xa0, 282, 0, 306 },
+{ 0x60, 282, 0, 307 },
+{ 0x10, 282, 0, 317 },
+{ 0x50, 282, 0, 315 },
+{ 0x30, 282, 0, 316 },
+{ 0x8, 282, 0, 326 },
+{ 0x28, 282, 0, 324 },
+{ 0x18, 282, 0, 325 },
+{ 0x4, 282, 0, 333 },
+{ 0x2, 282, 0, 334 },
+{ 0x1, 282, 0, 335 },
+{ 0x1, 410, 0, 337 },
+{ 0x3, 410, 0, 336 },
+{ 0x2, 416, 0, 338 },
+{ 0x1, 416, 0, 339 },
+{ 0x2, 412, 0, 340 },
+{ 0x1, 412, 0, 341 },
+{ 0x2, 414, 0, 342 },
+{ 0x1, 414, 0, 343 },
+{ 0x2, 418, 0, 344 },
+{ 0x1, 418, 0, 345 },
+{ 0x1, 267, 0, 372 },
+{ 0x5, 267, 0, 370 },
+{ 0x3, 267, 0, 371 },
+{ 0x140, 276, 0, 348 },
+{ 0x540, 276, 0, 346 },
+{ 0x340, 276, 0, 347 },
+{ 0xc0, 276, 0, 360 },
+{ 0x2c0, 276, 0, 358 },
+{ 0x1c0, 276, 0, 359 },
+{ 0x20, 276, 0, 375 },
+{ 0xa0, 276, 0, 373 },
+{ 0x60, 276, 0, 374 },
+{ 0x10, 276, 0, 387 },
+{ 0x50, 276, 0, 385 },
+{ 0x30, 276, 0, 386 },
+{ 0x8, 276, 0, 399 },
+{ 0x28, 276, 0, 397 },
+{ 0x18, 276, 0, 398 },
+{ 0x4, 276, 0, 409 },
+{ 0x2, 276, 0, 410 },
+{ 0x1, 276, 0, 411 },
+{ 0x140, 270, 0, 351 },
+{ 0x540, 270, 0, 349 },
+{ 0x340, 270, 0, 350 },
+{ 0xc0, 270, 0, 363 },
+{ 0x2c0, 270, 0, 361 },
+{ 0x1c0, 270, 0, 362 },
+{ 0x20, 270, 0, 378 },
+{ 0xa0, 270, 0, 376 },
+{ 0x60, 270, 0, 377 },
+{ 0x10, 270, 0, 390 },
+{ 0x50, 270, 0, 388 },
+{ 0x30, 270, 0, 389 },
+{ 0x8, 270, 0, 402 },
+{ 0x28, 270, 0, 400 },
+{ 0x18, 270, 0, 401 },
+{ 0x4, 270, 0, 412 },
+{ 0x2, 270, 0, 413 },
+{ 0x1, 270, 0, 414 },
+{ 0x140, 273, 0, 354 },
+{ 0x540, 273, 0, 352 },
+{ 0x340, 273, 0, 353 },
+{ 0xc0, 273, 0, 366 },
+{ 0x2c0, 273, 0, 364 },
+{ 0x1c0, 273, 0, 365 },
+{ 0x20, 273, 0, 381 },
+{ 0xa0, 273, 0, 379 },
+{ 0x60, 273, 0, 380 },
+{ 0x10, 273, 0, 393 },
+{ 0x50, 273, 0, 391 },
+{ 0x30, 273, 0, 392 },
+{ 0x8, 273, 0, 405 },
+{ 0x28, 273, 0, 403 },
+{ 0x18, 273, 0, 404 },
+{ 0x4, 273, 0, 415 },
+{ 0x2, 273, 0, 416 },
+{ 0x1, 273, 0, 417 },
+{ 0x140, 285, 0, 357 },
+{ 0x540, 285, 0, 355 },
+{ 0x340, 285, 0, 356 },
+{ 0xc0, 285, 0, 369 },
+{ 0x2c0, 285, 0, 367 },
+{ 0x1c0, 285, 0, 368 },
+{ 0x20, 285, 0, 384 },
+{ 0xa0, 285, 0, 382 },
+{ 0x60, 285, 0, 383 },
+{ 0x10, 285, 0, 396 },
+{ 0x50, 285, 0, 394 },
+{ 0x30, 285, 0, 395 },
+{ 0x8, 285, 0, 408 },
+{ 0x28, 285, 0, 406 },
+{ 0x18, 285, 0, 407 },
+{ 0x4, 285, 0, 418 },
+{ 0x2, 285, 0, 419 },
+{ 0x1, 285, 0, 420 },
+{ 0x1, 266, 0, 447 },
+{ 0x5, 266, 0, 445 },
+{ 0x3, 266, 0, 446 },
+{ 0x140, 275, 0, 423 },
+{ 0x540, 275, 0, 421 },
+{ 0x340, 275, 0, 422 },
+{ 0xc0, 275, 0, 435 },
+{ 0x2c0, 275, 0, 433 },
+{ 0x1c0, 275, 0, 434 },
+{ 0x20, 275, 0, 450 },
+{ 0xa0, 275, 0, 448 },
+{ 0x60, 275, 0, 449 },
+{ 0x10, 275, 0, 462 },
+{ 0x50, 275, 0, 460 },
+{ 0x30, 275, 0, 461 },
+{ 0x8, 275, 0, 474 },
+{ 0x28, 275, 0, 472 },
+{ 0x18, 275, 0, 473 },
+{ 0x4, 275, 0, 484 },
+{ 0x2, 275, 0, 485 },
+{ 0x1, 275, 0, 486 },
+{ 0x140, 269, 0, 426 },
+{ 0x540, 269, 0, 424 },
+{ 0x340, 269, 0, 425 },
+{ 0xc0, 269, 0, 438 },
+{ 0x2c0, 269, 0, 436 },
+{ 0x1c0, 269, 0, 437 },
+{ 0x20, 269, 0, 453 },
+{ 0xa0, 269, 0, 451 },
+{ 0x60, 269, 0, 452 },
+{ 0x10, 269, 0, 465 },
+{ 0x50, 269, 0, 463 },
+{ 0x30, 269, 0, 464 },
+{ 0x8, 269, 0, 477 },
+{ 0x28, 269, 0, 475 },
+{ 0x18, 269, 0, 476 },
+{ 0x4, 269, 0, 487 },
+{ 0x2, 269, 0, 488 },
+{ 0x1, 269, 0, 489 },
+{ 0x140, 272, 0, 429 },
+{ 0x540, 272, 0, 427 },
+{ 0x340, 272, 0, 428 },
+{ 0xc0, 272, 0, 441 },
+{ 0x2c0, 272, 0, 439 },
+{ 0x1c0, 272, 0, 440 },
+{ 0x20, 272, 0, 456 },
+{ 0xa0, 272, 0, 454 },
+{ 0x60, 272, 0, 455 },
+{ 0x10, 272, 0, 468 },
+{ 0x50, 272, 0, 466 },
+{ 0x30, 272, 0, 467 },
+{ 0x8, 272, 0, 480 },
+{ 0x28, 272, 0, 478 },
+{ 0x18, 272, 0, 479 },
+{ 0x4, 272, 0, 490 },
+{ 0x2, 272, 0, 491 },
+{ 0x1, 272, 0, 492 },
+{ 0x140, 284, 0, 432 },
+{ 0x540, 284, 0, 430 },
+{ 0x340, 284, 0, 431 },
+{ 0xc0, 284, 0, 444 },
+{ 0x2c0, 284, 0, 442 },
+{ 0x1c0, 284, 0, 443 },
+{ 0x20, 284, 0, 459 },
+{ 0xa0, 284, 0, 457 },
+{ 0x60, 284, 0, 458 },
+{ 0x10, 284, 0, 471 },
+{ 0x50, 284, 0, 469 },
+{ 0x30, 284, 0, 470 },
+{ 0x8, 284, 0, 483 },
+{ 0x28, 284, 0, 481 },
+{ 0x18, 284, 0, 482 },
+{ 0x4, 284, 0, 493 },
+{ 0x2, 284, 0, 494 },
+{ 0x1, 284, 0, 495 },
+{ 0x8, 409, 0, 497 },
+{ 0x18, 409, 0, 496 },
+{ 0x4, 409, 0, 499 },
+{ 0xc, 409, 0, 498 },
+{ 0x2, 409, 0, 506 },
+{ 0x1, 409, 0, 507 },
+{ 0x4, 407, 0, 501 },
+{ 0xc, 407, 0, 500 },
+{ 0x2, 407, 0, 508 },
+{ 0x1, 407, 0, 509 },
+{ 0x4, 405, 0, 503 },
+{ 0xc, 405, 0, 502 },
+{ 0x2, 405, 0, 510 },
+{ 0x1, 405, 0, 511 },
+{ 0x4, 401, 0, 505 },
+{ 0xc, 401, 0, 504 },
+{ 0x2, 401, 0, 512 },
+{ 0x1, 401, 0, 513 },
+{ 0xa00, 265, 0, 528 },
+{ 0x2a00, 265, 0, 526 },
+{ 0x1a00, 265, 0, 527 },
+{ 0x600, 265, 0, 540 },
+{ 0x2600, 265, 0, 516 },
+{ 0xa600, 265, 0, 514 },
+{ 0x6600, 265, 0, 515 },
+{ 0x1600, 265, 0, 538 },
+{ 0xe00, 265, 0, 539 },
+{ 0x100, 265, 0, 552 },
+{ 0x500, 265, 0, 550 },
+{ 0x300, 265, 0, 551 },
+{ 0x80, 265, 0, 555 },
+{ 0x280, 265, 0, 553 },
+{ 0x180, 265, 0, 554 },
+{ 0x40, 265, 0, 567 },
+{ 0x140, 265, 0, 565 },
+{ 0xc0, 265, 0, 566 },
+{ 0x20, 265, 0, 579 },
+{ 0xa0, 265, 0, 577 },
+{ 0x60, 265, 0, 578 },
+{ 0x10, 265, 0, 591 },
+{ 0x50, 265, 0, 589 },
+{ 0x30, 265, 0, 590 },
+{ 0x8, 265, 0, 603 },
+{ 0x28, 265, 0, 601 },
+{ 0x18, 265, 0, 602 },
+{ 0x4, 265, 0, 613 },
+{ 0x2, 265, 0, 614 },
+{ 0x1, 265, 0, 615 },
+{ 0x500, 261, 0, 531 },
+{ 0x1500, 261, 0, 529 },
+{ 0xd00, 261, 0, 530 },
+{ 0x300, 261, 0, 543 },
+{ 0x1300, 261, 0, 519 },
+{ 0x5300, 261, 0, 517 },
+{ 0x3300, 261, 0, 518 },
+{ 0xb00, 261, 0, 541 },
+{ 0x700, 261, 0, 542 },
+{ 0x80, 261, 0, 558 },
+{ 0x280, 261, 0, 556 },
+{ 0x180, 261, 0, 557 },
+{ 0x40, 261, 0, 570 },
+{ 0x140, 261, 0, 568 },
+{ 0xc0, 261, 0, 569 },
+{ 0x20, 261, 0, 582 },
+{ 0xa0, 261, 0, 580 },
+{ 0x60, 261, 0, 581 },
+{ 0x10, 261, 0, 594 },
+{ 0x50, 261, 0, 592 },
+{ 0x30, 261, 0, 593 },
+{ 0x8, 261, 0, 606 },
+{ 0x28, 261, 0, 604 },
+{ 0x18, 261, 0, 605 },
+{ 0x4, 261, 0, 616 },
+{ 0x2, 261, 0, 617 },
+{ 0x1, 261, 0, 618 },
+{ 0x500, 258, 0, 534 },
+{ 0x1500, 258, 0, 532 },
+{ 0xd00, 258, 0, 533 },
+{ 0x300, 258, 0, 546 },
+{ 0x1300, 258, 0, 522 },
+{ 0x5300, 258, 0, 520 },
+{ 0x3300, 258, 0, 521 },
+{ 0xb00, 258, 0, 544 },
+{ 0x700, 258, 0, 545 },
+{ 0x80, 258, 0, 561 },
+{ 0x280, 258, 0, 559 },
+{ 0x180, 258, 0, 560 },
+{ 0x40, 258, 0, 573 },
+{ 0x140, 258, 0, 571 },
+{ 0xc0, 258, 0, 572 },
+{ 0x20, 258, 0, 585 },
+{ 0xa0, 258, 0, 583 },
+{ 0x60, 258, 0, 584 },
+{ 0x10, 258, 0, 597 },
+{ 0x50, 258, 0, 595 },
+{ 0x30, 258, 0, 596 },
+{ 0x8, 258, 0, 609 },
+{ 0x28, 258, 0, 607 },
+{ 0x18, 258, 0, 608 },
+{ 0x4, 258, 0, 619 },
+{ 0x2, 258, 0, 620 },
+{ 0x1, 258, 0, 621 },
+{ 0x500, 253, 0, 537 },
+{ 0x1500, 253, 0, 535 },
+{ 0xd00, 253, 0, 536 },
+{ 0x300, 253, 0, 549 },
+{ 0x1300, 253, 0, 525 },
+{ 0x5300, 253, 0, 523 },
+{ 0x3300, 253, 0, 524 },
+{ 0xb00, 253, 0, 547 },
+{ 0x700, 253, 0, 548 },
+{ 0x80, 253, 0, 564 },
+{ 0x280, 253, 0, 562 },
+{ 0x180, 253, 0, 563 },
+{ 0x40, 253, 0, 576 },
+{ 0x140, 253, 0, 574 },
+{ 0xc0, 253, 0, 575 },
+{ 0x20, 253, 0, 588 },
+{ 0xa0, 253, 0, 586 },
+{ 0x60, 253, 0, 587 },
+{ 0x10, 253, 0, 600 },
+{ 0x50, 253, 0, 598 },
+{ 0x30, 253, 0, 599 },
+{ 0x8, 253, 0, 612 },
+{ 0x28, 253, 0, 610 },
+{ 0x18, 253, 0, 611 },
+{ 0x4, 253, 0, 622 },
+{ 0x2, 253, 0, 623 },
+{ 0x1, 253, 0, 624 },
+{ 0x8, 238, 0, 625 },
+{ 0x4, 238, 0, 626 },
+{ 0x2, 238, 0, 627 },
+{ 0x1, 238, 0, 628 },
+{ 0x2, 176, 0, 631 },
+{ 0xa, 176, 0, 629 },
+{ 0x6, 176, 0, 630 },
+{ 0x1, 176, 0, 637 },
+{ 0x5, 176, 0, 635 },
+{ 0x3, 176, 0, 636 },
+{ 0x2, 175, 0, 634 },
+{ 0xa, 175, 0, 632 },
+{ 0x6, 175, 0, 633 },
+{ 0x1, 175, 0, 640 },
+{ 0x5, 175, 0, 638 },
+{ 0x3, 175, 0, 639 },
+{ 0x4, 451, 0, 641 },
+{ 0x2, 451, 0, 642 },
+{ 0x1, 451, 0, 643 },
+{ 0x4, 450, 0, 644 },
+{ 0x2, 450, 0, 645 },
+{ 0x1, 450, 0, 646 },
+{ 0x4, 449, 0, 647 },
+{ 0x2, 449, 0, 648 },
+{ 0x1, 449, 0, 649 },
+{ 0x4, 448, 0, 650 },
+{ 0x2, 448, 0, 651 },
+{ 0x1, 448, 0, 652 },
+{ 0x2, 123, 1, 658 },
+{ 0x2, 124, 0, 657 },
+{ 0xa, 123, 1, 654 },
+{ 0xa, 124, 0, 653 },
+{ 0x6, 123, 1, 656 },
+{ 0x6, 124, 0, 655 },
+{ 0x1, 123, 1, 688 },
+{ 0x1, 124, 0, 687 },
+{ 0x5, 123, 1, 684 },
+{ 0x5, 124, 0, 683 },
+{ 0x3, 123, 1, 686 },
+{ 0x3, 124, 0, 685 },
+{ 0x2, 131, 1, 664 },
+{ 0x2, 132, 0, 663 },
+{ 0xa, 131, 1, 660 },
+{ 0xa, 132, 0, 659 },
+{ 0x6, 131, 1, 662 },
+{ 0x6, 132, 0, 661 },
+{ 0x1, 131, 1, 694 },
+{ 0x1, 132, 0, 693 },
+{ 0x5, 131, 1, 690 },
+{ 0x5, 132, 0, 689 },
+{ 0x3, 131, 1, 692 },
+{ 0x3, 132, 0, 691 },
+{ 0x2, 129, 1, 670 },
+{ 0x2, 130, 0, 669 },
+{ 0xa, 129, 1, 666 },
+{ 0xa, 130, 0, 665 },
+{ 0x6, 129, 1, 668 },
+{ 0x6, 130, 0, 667 },
+{ 0x1, 129, 1, 700 },
+{ 0x1, 130, 0, 699 },
+{ 0x5, 129, 1, 696 },
+{ 0x5, 130, 0, 695 },
+{ 0x3, 129, 1, 698 },
+{ 0x3, 130, 0, 697 },
+{ 0x2, 127, 1, 676 },
+{ 0x2, 128, 0, 675 },
+{ 0xa, 127, 1, 672 },
+{ 0xa, 128, 0, 671 },
+{ 0x6, 127, 1, 674 },
+{ 0x6, 128, 0, 673 },
+{ 0x1, 127, 1, 706 },
+{ 0x1, 128, 0, 705 },
+{ 0x5, 127, 1, 702 },
+{ 0x5, 128, 0, 701 },
+{ 0x3, 127, 1, 704 },
+{ 0x3, 128, 0, 703 },
+{ 0x2, 125, 1, 682 },
+{ 0x2, 126, 0, 681 },
+{ 0xa, 125, 1, 678 },
+{ 0xa, 126, 0, 677 },
+{ 0x6, 125, 1, 680 },
+{ 0x6, 126, 0, 679 },
+{ 0x1, 125, 1, 712 },
+{ 0x1, 126, 0, 711 },
+{ 0x5, 125, 1, 708 },
+{ 0x5, 126, 0, 707 },
+{ 0x3, 125, 1, 710 },
+{ 0x3, 126, 0, 709 },
+{ 0x4, 402, 1, 718 },
+{ 0x4, 403, 0, 717 },
+{ 0xc, 402, 1, 716 },
+{ 0xc, 403, 0, 715 },
+{ 0x2, 402, 1, 728 },
+{ 0x2, 403, 0, 727 },
+{ 0x1, 402, 1, 730 },
+{ 0x1, 403, 0, 729 },
+{ 0x8, 408, 0, 714 },
+{ 0x18, 408, 0, 713 },
+{ 0x4, 408, 0, 720 },
+{ 0xc, 408, 0, 719 },
+{ 0x2, 408, 0, 731 },
+{ 0x1, 408, 0, 732 },
+{ 0x4, 406, 0, 722 },
+{ 0xc, 406, 0, 721 },
+{ 0x2, 406, 0, 733 },
+{ 0x1, 406, 0, 734 },
+{ 0x4, 404, 0, 724 },
+{ 0xc, 404, 0, 723 },
+{ 0x2, 404, 0, 735 },
+{ 0x1, 404, 0, 736 },
+{ 0x4, 400, 0, 726 },
+{ 0xc, 400, 0, 725 },
+{ 0x2, 400, 0, 737 },
+{ 0x1, 400, 0, 738 },
+{ 0xa00, 264, 0, 753 },
+{ 0x2a00, 264, 0, 751 },
+{ 0x1a00, 264, 0, 752 },
+{ 0x600, 264, 0, 765 },
+{ 0x2600, 264, 0, 741 },
+{ 0xa600, 264, 0, 739 },
+{ 0x6600, 264, 0, 740 },
+{ 0x1600, 264, 0, 763 },
+{ 0xe00, 264, 0, 764 },
+{ 0x100, 264, 0, 777 },
+{ 0x500, 264, 0, 775 },
+{ 0x300, 264, 0, 776 },
+{ 0x80, 264, 0, 780 },
+{ 0x280, 264, 0, 778 },
+{ 0x180, 264, 0, 779 },
+{ 0x40, 264, 0, 792 },
+{ 0x140, 264, 0, 790 },
+{ 0xc0, 264, 0, 791 },
+{ 0x20, 264, 0, 804 },
+{ 0xa0, 264, 0, 802 },
+{ 0x60, 264, 0, 803 },
+{ 0x10, 264, 0, 816 },
+{ 0x50, 264, 0, 814 },
+{ 0x30, 264, 0, 815 },
+{ 0x8, 264, 0, 828 },
+{ 0x28, 264, 0, 826 },
+{ 0x18, 264, 0, 827 },
+{ 0x4, 264, 0, 838 },
+{ 0x2, 264, 0, 839 },
+{ 0x1, 264, 0, 840 },
+{ 0x500, 260, 0, 756 },
+{ 0x1500, 260, 0, 754 },
+{ 0xd00, 260, 0, 755 },
+{ 0x300, 260, 0, 768 },
+{ 0x1300, 260, 0, 744 },
+{ 0x5300, 260, 0, 742 },
+{ 0x3300, 260, 0, 743 },
+{ 0xb00, 260, 0, 766 },
+{ 0x700, 260, 0, 767 },
+{ 0x80, 260, 0, 783 },
+{ 0x280, 260, 0, 781 },
+{ 0x180, 260, 0, 782 },
+{ 0x40, 260, 0, 795 },
+{ 0x140, 260, 0, 793 },
+{ 0xc0, 260, 0, 794 },
+{ 0x20, 260, 0, 807 },
+{ 0xa0, 260, 0, 805 },
+{ 0x60, 260, 0, 806 },
+{ 0x10, 260, 0, 819 },
+{ 0x50, 260, 0, 817 },
+{ 0x30, 260, 0, 818 },
+{ 0x8, 260, 0, 831 },
+{ 0x28, 260, 0, 829 },
+{ 0x18, 260, 0, 830 },
+{ 0x4, 260, 0, 841 },
+{ 0x2, 260, 0, 842 },
+{ 0x1, 260, 0, 843 },
+{ 0x500, 257, 0, 759 },
+{ 0x1500, 257, 0, 757 },
+{ 0xd00, 257, 0, 758 },
+{ 0x300, 257, 0, 771 },
+{ 0x1300, 257, 0, 747 },
+{ 0x5300, 257, 0, 745 },
+{ 0x3300, 257, 0, 746 },
+{ 0xb00, 257, 0, 769 },
+{ 0x700, 257, 0, 770 },
+{ 0x80, 257, 0, 786 },
+{ 0x280, 257, 0, 784 },
+{ 0x180, 257, 0, 785 },
+{ 0x40, 257, 0, 798 },
+{ 0x140, 257, 0, 796 },
+{ 0xc0, 257, 0, 797 },
+{ 0x20, 257, 0, 810 },
+{ 0xa0, 257, 0, 808 },
+{ 0x60, 257, 0, 809 },
+{ 0x10, 257, 0, 822 },
+{ 0x50, 257, 0, 820 },
+{ 0x30, 257, 0, 821 },
+{ 0x8, 257, 0, 834 },
+{ 0x28, 257, 0, 832 },
+{ 0x18, 257, 0, 833 },
+{ 0x4, 257, 0, 844 },
+{ 0x2, 257, 0, 845 },
+{ 0x1, 257, 0, 846 },
+{ 0x500, 252, 0, 762 },
+{ 0x1500, 252, 0, 760 },
+{ 0xd00, 252, 0, 761 },
+{ 0x300, 252, 0, 774 },
+{ 0x1300, 252, 0, 750 },
+{ 0x5300, 252, 0, 748 },
+{ 0x3300, 252, 0, 749 },
+{ 0xb00, 252, 0, 772 },
+{ 0x700, 252, 0, 773 },
+{ 0x80, 252, 0, 789 },
+{ 0x280, 252, 0, 787 },
+{ 0x180, 252, 0, 788 },
+{ 0x40, 252, 0, 801 },
+{ 0x140, 252, 0, 799 },
+{ 0xc0, 252, 0, 800 },
+{ 0x20, 252, 0, 813 },
+{ 0xa0, 252, 0, 811 },
+{ 0x60, 252, 0, 812 },
+{ 0x10, 252, 0, 825 },
+{ 0x50, 252, 0, 823 },
+{ 0x30, 252, 0, 824 },
+{ 0x8, 252, 0, 837 },
+{ 0x28, 252, 0, 835 },
+{ 0x18, 252, 0, 836 },
+{ 0x4, 252, 0, 847 },
+{ 0x2, 252, 0, 848 },
+{ 0x1, 252, 0, 849 },
+{ 0x8, 254, 1, 895 },
+{ 0x8, 255, 0, 894 },
+{ 0x28, 254, 1, 891 },
+{ 0x28, 255, 0, 890 },
+{ 0x18, 254, 1, 893 },
+{ 0x18, 255, 0, 892 },
+{ 0x4, 254, 1, 957 },
+{ 0x4, 255, 0, 956 },
+{ 0x2, 254, 1, 959 },
+{ 0x2, 255, 0, 958 },
+{ 0x1, 254, 1, 961 },
+{ 0x1, 255, 0, 960 },
+{ 0xa00, 262, 0, 865 },
+{ 0x2a00, 262, 0, 863 },
+{ 0x1a00, 262, 0, 864 },
+{ 0x600, 262, 0, 877 },
+{ 0x2600, 262, 0, 853 },
+{ 0xa600, 262, 0, 851 },
+{ 0x6600, 262, 0, 852 },
+{ 0x1600, 262, 0, 875 },
+{ 0xe00, 262, 0, 876 },
+{ 0x100, 262, 0, 889 },
+{ 0x500, 262, 0, 887 },
+{ 0x300, 262, 0, 888 },
+{ 0x80, 262, 0, 898 },
+{ 0x280, 262, 0, 896 },
+{ 0x180, 262, 0, 897 },
+{ 0x40, 262, 0, 910 },
+{ 0x140, 262, 0, 908 },
+{ 0xc0, 262, 0, 909 },
+{ 0x20, 262, 0, 922 },
+{ 0xa0, 262, 0, 920 },
+{ 0x60, 262, 0, 921 },
+{ 0x10, 262, 0, 934 },
+{ 0x50, 262, 0, 932 },
+{ 0x30, 262, 0, 933 },
+{ 0x8, 262, 0, 946 },
+{ 0x28, 262, 0, 944 },
+{ 0x18, 262, 0, 945 },
+{ 0x4, 262, 0, 962 },
+{ 0x2, 262, 0, 963 },
+{ 0x1, 262, 1, 964 },
+{ 0x1, 263, 0, 850 },
+{ 0x500, 259, 0, 868 },
+{ 0x1500, 259, 0, 866 },
+{ 0xd00, 259, 0, 867 },
+{ 0x300, 259, 0, 880 },
+{ 0x1300, 259, 0, 856 },
+{ 0x5300, 259, 0, 854 },
+{ 0x3300, 259, 0, 855 },
+{ 0xb00, 259, 0, 878 },
+{ 0x700, 259, 0, 879 },
+{ 0x80, 259, 0, 901 },
+{ 0x280, 259, 0, 899 },
+{ 0x180, 259, 0, 900 },
+{ 0x40, 259, 0, 913 },
+{ 0x140, 259, 0, 911 },
+{ 0xc0, 259, 0, 912 },
+{ 0x20, 259, 0, 925 },
+{ 0xa0, 259, 0, 923 },
+{ 0x60, 259, 0, 924 },
+{ 0x10, 259, 0, 937 },
+{ 0x50, 259, 0, 935 },
+{ 0x30, 259, 0, 936 },
+{ 0x8, 259, 0, 949 },
+{ 0x28, 259, 0, 947 },
+{ 0x18, 259, 0, 948 },
+{ 0x4, 259, 0, 965 },
+{ 0x2, 259, 0, 966 },
+{ 0x1, 259, 0, 967 },
+{ 0x500, 256, 0, 871 },
+{ 0x1500, 256, 0, 869 },
+{ 0xd00, 256, 0, 870 },
+{ 0x300, 256, 0, 883 },
+{ 0x1300, 256, 0, 859 },
+{ 0x5300, 256, 0, 857 },
+{ 0x3300, 256, 0, 858 },
+{ 0xb00, 256, 0, 881 },
+{ 0x700, 256, 0, 882 },
+{ 0x80, 256, 0, 904 },
+{ 0x280, 256, 0, 902 },
+{ 0x180, 256, 0, 903 },
+{ 0x40, 256, 0, 916 },
+{ 0x140, 256, 0, 914 },
+{ 0xc0, 256, 0, 915 },
+{ 0x20, 256, 0, 928 },
+{ 0xa0, 256, 0, 926 },
+{ 0x60, 256, 0, 927 },
+{ 0x10, 256, 0, 940 },
+{ 0x50, 256, 0, 938 },
+{ 0x30, 256, 0, 939 },
+{ 0x8, 256, 0, 952 },
+{ 0x28, 256, 0, 950 },
+{ 0x18, 256, 0, 951 },
+{ 0x4, 256, 0, 968 },
+{ 0x2, 256, 0, 969 },
+{ 0x1, 256, 0, 970 },
+{ 0x500, 251, 0, 874 },
+{ 0x1500, 251, 0, 872 },
+{ 0xd00, 251, 0, 873 },
+{ 0x300, 251, 0, 886 },
+{ 0x1300, 251, 0, 862 },
+{ 0x5300, 251, 0, 860 },
+{ 0x3300, 251, 0, 861 },
+{ 0xb00, 251, 0, 884 },
+{ 0x700, 251, 0, 885 },
+{ 0x80, 251, 0, 907 },
+{ 0x280, 251, 0, 905 },
+{ 0x180, 251, 0, 906 },
+{ 0x40, 251, 0, 919 },
+{ 0x140, 251, 0, 917 },
+{ 0xc0, 251, 0, 918 },
+{ 0x20, 251, 0, 931 },
+{ 0xa0, 251, 0, 929 },
+{ 0x60, 251, 0, 930 },
+{ 0x10, 251, 0, 943 },
+{ 0x50, 251, 0, 941 },
+{ 0x30, 251, 0, 942 },
+{ 0x8, 251, 0, 955 },
+{ 0x28, 251, 0, 953 },
+{ 0x18, 251, 0, 954 },
+{ 0x4, 251, 0, 971 },
+{ 0x2, 251, 0, 972 },
+{ 0x1, 251, 0, 973 },
+{ 0x2, 150, 0, 975 },
+{ 0x1, 150, 0, 976 },
+{ 0x1, 50, 0, 977 },
+{ 0x3, 49, 0, 978 },
+{ 0x1, 428, 0, 979 },
+{ 0x1, 442, 0, 980 },
+{ 0x2, 386, 0, 983 },
+{ 0x1, 386, 0, 984 },
+{ 0x2, 384, 0, 985 },
+{ 0x1, 384, 0, 986 },
+{ 0x1, 383, 0, 987 },
+{ 0x1, 328, 0, 992 },
+{ 0x1, 327, 0, 993 },
+{ 0x1, 326, 0, 994 },
+{ 0x1, 325, 0, 995 },
+{ 0x1, 250, 0, 996 },
+{ 0x1, 249, 0, 997 },
+{ 0x1, 324, 0, 998 },
+{ 0x1, 323, 0, 999 },
+{ 0x1, 322, 0, 1000 },
+{ 0x1, 321, 0, 1001 },
+{ 0x1, 320, 0, 1002 },
+{ 0x1, 319, 0, 1003 },
+{ 0x1, 318, 0, 1004 },
+{ 0x2, 248, 0, 1005 },
+{ 0x1, 248, 0, 1006 },
+{ 0x2, 366, 0, 1012 },
+{ 0x1, 366, 0, 1013 },
+{ 0x1, 317, 0, 1014 },
+{ 0x1, 316, 0, 1015 },
+{ 0x1, 315, 0, 1016 },
+{ 0x1, 314, 0, 1017 },
+{ 0x1, 8, 1, 1019 },
+{ 0x1, 9, 0, 1018 },
+{ 0x1, 313, 0, 1020 },
+{ 0x1, 312, 0, 1021 },
+{ 0x1, 311, 0, 1022 },
+{ 0x1, 310, 0, 1023 },
+{ 0x1, 388, 0, 1024 },
+{ 0x1, 399, 0, 1025 },
+{ 0x1, 389, 0, 1026 },
+{ 0x1, 423, 0, 1027 },
+{ 0x1, 309, 0, 1031 },
+{ 0x1, 247, 0, 1032 },
+{ 0x1, 177, 0, 1035 },
+{ 0x2, 291, 0, 1039 },
+{ 0x1, 291, 0, 1040 },
+{ 0x1, 236, 0, 1041 },
+{ 0x5, 48, 0, 1043 },
+{ 0x3, 48, 0, 1044 },
+{ 0x5, 47, 0, 1045 },
+{ 0x3, 47, 0, 1046 },
+{ 0x1, 365, 0, 1047 },
+{ 0x1, 373, 0, 1048 },
+{ 0x1, 371, 0, 1049 },
+{ 0x1, 392, 0, 1050 },
+{ 0x1, 372, 0, 1051 },
+{ 0x1, 370, 0, 1052 },
+{ 0x2, 378, 0, 1053 },
+{ 0x1, 378, 0, 1055 },
+{ 0x2, 376, 0, 1054 },
+{ 0x1, 376, 0, 1056 },
+{ 0x2, 396, 0, 1057 },
+{ 0x1, 396, 0, 1060 },
+{ 0x2, 377, 0, 1058 },
+{ 0x1, 377, 0, 1061 },
+{ 0x2, 375, 0, 1059 },
+{ 0x1, 375, 0, 1062 },
+{ 0x1, 338, 0, 1063 },
+{ 0x1, 337, 0, 1064 },
+{ 0x1, 369, 0, 1065 },
+{ 0x1, 360, 0, 1066 },
+{ 0x1, 362, 0, 1067 },
+{ 0x1, 359, 0, 1068 },
+{ 0x1, 361, 0, 1069 },
+{ 0x2, 446, 0, 1070 },
+{ 0x1, 446, 0, 1073 },
+{ 0x2, 445, 0, 1071 },
+{ 0x1, 445, 0, 1074 },
+{ 0x2, 444, 0, 1072 },
+{ 0x1, 444, 0, 1075 },
+{ 0x1, 348, 0, 1076 },
+{ 0x2, 347, 0, 1077 },
+{ 0x1, 347, 0, 1078 },
+{ 0x2, 294, 0, 1079 },
+{ 0x1, 294, 0, 1082 },
+{ 0x2, 293, 0, 1080 },
+{ 0x1, 293, 0, 1083 },
+{ 0x2, 292, 0, 1081 },
+{ 0x1, 292, 0, 1084 },
+{ 0x2, 363, 0, 1085 },
+{ 0x1, 363, 0, 1086 },
+{ 0x2, 364, 0, 1087 },
+{ 0x1, 364, 0, 1088 },
+{ 0xa, 438, 1, 1100 },
+{ 0xa, 439, 1, 1099 },
+{ 0xa, 440, 1, 1098 },
+{ 0xa, 441, 0, 1097 },
+{ 0x1a, 438, 1, 1092 },
+{ 0x1a, 439, 1, 1091 },
+{ 0x32, 440, 1, 1090 },
+{ 0x32, 441, 0, 1089 },
+{ 0x6, 438, 1, 1108 },
+{ 0x6, 439, 1, 1107 },
+{ 0x6, 440, 1, 1106 },
+{ 0x6, 441, 0, 1105 },
+{ 0x1, 438, 1, 1120 },
+{ 0x1, 439, 1, 1119 },
+{ 0x1, 440, 1, 1118 },
+{ 0x1, 441, 0, 1117 },
+{ 0x9, 438, 1, 1104 },
+{ 0x9, 439, 1, 1103 },
+{ 0x9, 440, 1, 1102 },
+{ 0x9, 441, 0, 1101 },
+{ 0x19, 438, 1, 1096 },
+{ 0x19, 439, 1, 1095 },
+{ 0x31, 440, 1, 1094 },
+{ 0x31, 441, 0, 1093 },
+{ 0x5, 438, 1, 1112 },
+{ 0x5, 439, 1, 1111 },
+{ 0x5, 440, 1, 1110 },
+{ 0x5, 441, 0, 1109 },
+{ 0x3, 438, 1, 1116 },
+{ 0x3, 439, 1, 1115 },
+{ 0x3, 440, 1, 1114 },
+{ 0x3, 441, 0, 1113 },
+{ 0xa, 429, 1, 1132 },
+{ 0xa, 430, 1, 1131 },
+{ 0xa, 431, 1, 1130 },
+{ 0xa, 432, 0, 1129 },
+{ 0x1a, 429, 1, 1124 },
+{ 0x1a, 430, 1, 1123 },
+{ 0x32, 431, 1, 1122 },
+{ 0x32, 432, 0, 1121 },
+{ 0x6, 429, 1, 1140 },
+{ 0x6, 430, 1, 1139 },
+{ 0x6, 431, 1, 1138 },
+{ 0x6, 432, 0, 1137 },
+{ 0x1, 429, 1, 1152 },
+{ 0x1, 430, 1, 1151 },
+{ 0x1, 431, 1, 1150 },
+{ 0x1, 432, 0, 1149 },
+{ 0x9, 429, 1, 1136 },
+{ 0x9, 430, 1, 1135 },
+{ 0x9, 431, 1, 1134 },
+{ 0x9, 432, 0, 1133 },
+{ 0x19, 429, 1, 1128 },
+{ 0x19, 430, 1, 1127 },
+{ 0x31, 431, 1, 1126 },
+{ 0x31, 432, 0, 1125 },
+{ 0x5, 429, 1, 1144 },
+{ 0x5, 430, 1, 1143 },
+{ 0x5, 431, 1, 1142 },
+{ 0x5, 432, 0, 1141 },
+{ 0x3, 429, 1, 1148 },
+{ 0x3, 430, 1, 1147 },
+{ 0x3, 431, 1, 1146 },
+{ 0x3, 432, 0, 1145 },
+{ 0xa, 433, 1, 1164 },
+{ 0xa, 434, 1, 1163 },
+{ 0xa, 435, 1, 1162 },
+{ 0xa, 436, 0, 1161 },
+{ 0x1a, 433, 1, 1156 },
+{ 0x1a, 434, 1, 1155 },
+{ 0x32, 435, 1, 1154 },
+{ 0x32, 436, 0, 1153 },
+{ 0x6, 433, 1, 1172 },
+{ 0x6, 434, 1, 1171 },
+{ 0x6, 435, 1, 1170 },
+{ 0x6, 436, 0, 1169 },
+{ 0x1, 433, 1, 1184 },
+{ 0x1, 434, 1, 1183 },
+{ 0x1, 435, 1, 1182 },
+{ 0x1, 436, 0, 1181 },
+{ 0x9, 433, 1, 1168 },
+{ 0x9, 434, 1, 1167 },
+{ 0x9, 435, 1, 1166 },
+{ 0x9, 436, 0, 1165 },
+{ 0x19, 433, 1, 1160 },
+{ 0x19, 434, 1, 1159 },
+{ 0x31, 435, 1, 1158 },
+{ 0x31, 436, 0, 1157 },
+{ 0x5, 433, 1, 1176 },
+{ 0x5, 434, 1, 1175 },
+{ 0x5, 435, 1, 1174 },
+{ 0x5, 436, 0, 1173 },
+{ 0x3, 433, 1, 1180 },
+{ 0x3, 434, 1, 1179 },
+{ 0x3, 435, 1, 1178 },
+{ 0x3, 436, 0, 1177 },
+{ 0x1, 139, 0, 1185 },
+{ 0x1, 138, 0, 1186 },
+{ 0x1, 391, 1, 1188 },
+{ 0x1, 137, 0, 1187 },
+{ 0x2, 395, 1, 1190 },
+{ 0x2, 141, 0, 1189 },
+{ 0x1, 395, 1, 1192 },
+{ 0x1, 141, 0, 1191 },
+{ 0x1, 397, 0, 1193 },
+{ 0x1, 136, 0, 1194 },
+{ 0x2, 135, 0, 1195 },
+{ 0x2, 134, 0, 1196 },
+{ 0x1, 459, 1, 1202 },
+{ 0x1, 246, 0, 1033 },
+{ 0x1, 458, 0, 1203 },
+{ 0x1, 457, 1, 1204 },
+{ 0x1, 245, 0, 1042 },
+{ 0x1, 308, 0, 1205 },
+{ 0x1, 307, 1, 1206 },
+{ 0x1, 290, 0, 1034 },
+{ 0x1, 306, 0, 1207 },
+{ 0x1, 305, 1, 1208 },
+{ 0x1, 427, 0, 1036 },
+{ 0x1, 304, 1, 1209 },
+{ 0x1, 398, 0, 1038 },
+{ 0x1, 303, 0, 1210 },
+{ 0x1, 302, 0, 1211 },
+{ 0x1, 301, 0, 1212 },
+{ 0x1, 300, 1, 1213 },
+{ 0x2, 398, 0, 1037 },
+{ 0x10, 299, 0, 1217 },
+{ 0x90, 299, 0, 1215 },
+{ 0x190, 299, 0, 1214 },
+{ 0x50, 299, 0, 1216 },
+{ 0x30, 299, 0, 1219 },
+{ 0x70, 299, 0, 1218 },
+{ 0x8, 299, 0, 1221 },
+{ 0x18, 299, 0, 1220 },
+{ 0x4, 299, 0, 1222 },
+{ 0x1, 299, 0, 1225 },
+{ 0x3, 299, 0, 1224 },
+{ 0x1, 298, 1, 1226 },
+{ 0x2, 299, 0, 1223 },
+{ 0x3, 46, 0, 1227 },
+{ 0x1, 241, 1, 1228 },
+{ 0x1, 242, 1, 1028 },
+{ 0x1, 243, 0, 88 },
+{ 0x1, 341, 1, 1229 },
+{ 0x1, 342, 1, 1029 },
+{ 0x1, 343, 0, 89 },
+{ 0x1, 34, 1, 1230 },
+{ 0x1, 35, 1, 1030 },
+{ 0x1, 36, 0, 90 },
+{ 0x1, 230, 0, 1231 },
+{ 0x4, 452, 0, 1232 },
+{ 0x2, 452, 0, 1233 },
+{ 0x1, 452, 1, 1235 },
+{ 0x1, 453, 0, 1234 },
+{ 0x8, 454, 0, 1236 },
+{ 0x4, 454, 0, 1237 },
+{ 0x1, 454, 1, 1239 },
+{ 0x2, 454, 0, 1238 },
+{ 0x8, 219, 0, 1240 },
+{ 0x4, 219, 0, 1241 },
+{ 0x2, 219, 0, 1242 },
+{ 0x1, 219, 1, 1244 },
+{ 0x1, 220, 0, 1243 },
+{ 0x10, 221, 0, 1245 },
+{ 0x8, 221, 0, 1246 },
+{ 0x4, 221, 0, 1247 },
+{ 0x1, 221, 1, 1249 },
+{ 0x2, 221, 0, 1248 },
+{ 0x220, 191, 0, 1250 },
+{ 0x120, 191, 0, 1251 },
+{ 0xa0, 191, 0, 1252 },
+{ 0x60, 191, 1, 1254 },
+{ 0x4, 192, 0, 1253 },
+{ 0x110, 191, 0, 1260 },
+{ 0x90, 191, 0, 1261 },
+{ 0x50, 191, 0, 1262 },
+{ 0x30, 191, 1, 1264 },
+{ 0x2, 192, 0, 1263 },
+{ 0x8, 191, 0, 1265 },
+{ 0x4, 191, 0, 1266 },
+{ 0x2, 191, 0, 1267 },
+{ 0x1, 191, 1, 1269 },
+{ 0x1, 192, 0, 1268 },
+{ 0x440, 193, 0, 1255 },
+{ 0x240, 193, 0, 1256 },
+{ 0x140, 193, 0, 1257 },
+{ 0xc0, 193, 1, 1259 },
+{ 0x40, 193, 0, 1258 },
+{ 0x220, 193, 0, 1270 },
+{ 0x120, 193, 0, 1271 },
+{ 0xa0, 193, 0, 1272 },
+{ 0x60, 193, 1, 1274 },
+{ 0x20, 193, 0, 1273 },
+{ 0x10, 193, 0, 1275 },
+{ 0x8, 193, 0, 1276 },
+{ 0x4, 193, 0, 1277 },
+{ 0x1, 193, 1, 1279 },
+{ 0x2, 193, 0, 1278 },
+{ 0x8, 215, 0, 1280 },
+{ 0x4, 215, 0, 1281 },
+{ 0x2, 215, 0, 1282 },
+{ 0x1, 215, 1, 1284 },
+{ 0x1, 216, 0, 1283 },
+{ 0x220, 187, 0, 1285 },
+{ 0x120, 187, 0, 1286 },
+{ 0xa0, 187, 0, 1287 },
+{ 0x60, 187, 1, 1289 },
+{ 0x4, 188, 0, 1288 },
+{ 0x110, 187, 0, 1295 },
+{ 0x90, 187, 0, 1296 },
+{ 0x50, 187, 0, 1297 },
+{ 0x30, 187, 1, 1299 },
+{ 0x2, 188, 0, 1298 },
+{ 0x8, 187, 0, 1300 },
+{ 0x4, 187, 0, 1301 },
+{ 0x2, 187, 0, 1302 },
+{ 0x1, 187, 1, 1304 },
+{ 0x1, 188, 0, 1303 },
+{ 0x440, 233, 0, 1290 },
+{ 0x240, 233, 0, 1291 },
+{ 0x140, 233, 0, 1292 },
+{ 0xc0, 233, 1, 1294 },
+{ 0x40, 233, 0, 1293 },
+{ 0x220, 233, 0, 1305 },
+{ 0x120, 233, 0, 1306 },
+{ 0xa0, 233, 0, 1307 },
+{ 0x60, 233, 1, 1309 },
+{ 0x20, 233, 0, 1308 },
+{ 0x10, 233, 0, 1310 },
+{ 0x8, 233, 0, 1311 },
+{ 0x4, 233, 0, 1312 },
+{ 0x1, 233, 1, 1314 },
+{ 0x2, 233, 0, 1313 },
+{ 0x8, 207, 0, 1315 },
+{ 0x4, 207, 0, 1316 },
+{ 0x2, 207, 0, 1317 },
+{ 0x1, 207, 1, 1319 },
+{ 0x1, 208, 0, 1318 },
+{ 0x10, 214, 0, 1320 },
+{ 0x8, 214, 0, 1321 },
+{ 0x4, 214, 0, 1322 },
+{ 0x1, 214, 1, 1324 },
+{ 0x2, 214, 0, 1323 },
+{ 0x220, 178, 0, 1325 },
+{ 0x120, 178, 0, 1326 },
+{ 0xa0, 178, 0, 1327 },
+{ 0x60, 178, 1, 1329 },
+{ 0x4, 179, 0, 1328 },
+{ 0x110, 178, 0, 1350 },
+{ 0x90, 178, 0, 1351 },
+{ 0x50, 178, 0, 1352 },
+{ 0x30, 178, 1, 1354 },
+{ 0x2, 179, 0, 1353 },
+{ 0x8, 178, 0, 1355 },
+{ 0x4, 178, 0, 1356 },
+{ 0x2, 178, 0, 1357 },
+{ 0x1, 178, 1, 1359 },
+{ 0x1, 179, 0, 1358 },
+{ 0x440, 186, 0, 1330 },
+{ 0x240, 186, 0, 1331 },
+{ 0x140, 186, 0, 1332 },
+{ 0xc0, 186, 1, 1334 },
+{ 0x40, 186, 0, 1333 },
+{ 0x220, 186, 0, 1360 },
+{ 0x120, 186, 0, 1361 },
+{ 0xa0, 186, 0, 1362 },
+{ 0x60, 186, 1, 1364 },
+{ 0x20, 186, 0, 1363 },
+{ 0x10, 186, 0, 1365 },
+{ 0x8, 186, 0, 1366 },
+{ 0x4, 186, 0, 1367 },
+{ 0x1, 186, 1, 1369 },
+{ 0x2, 186, 0, 1368 },
+{ 0x440, 143, 0, 1335 },
+{ 0x240, 143, 0, 1336 },
+{ 0x140, 143, 0, 1337 },
+{ 0xc0, 143, 1, 1339 },
+{ 0x40, 143, 0, 1338 },
+{ 0x220, 143, 0, 1370 },
+{ 0x120, 143, 0, 1371 },
+{ 0xa0, 143, 0, 1372 },
+{ 0x60, 143, 1, 1374 },
+{ 0x20, 143, 0, 1373 },
+{ 0x10, 143, 0, 1375 },
+{ 0x8, 143, 0, 1376 },
+{ 0x1, 143, 1, 1379 },
+{ 0x2, 143, 0, 1378 },
+{ 0x440, 194, 1, 1345 },
+{ 0x441, 174, 0, 1340 },
+{ 0x240, 194, 1, 1346 },
+{ 0x241, 174, 0, 1341 },
+{ 0x140, 194, 1, 1347 },
+{ 0x141, 174, 0, 1342 },
+{ 0xc0, 194, 1, 1349 },
+{ 0x40, 194, 1, 1348 },
+{ 0xc1, 174, 1, 1344 },
+{ 0x41, 174, 0, 1343 },
+{ 0x220, 194, 1, 1390 },
+{ 0x221, 174, 0, 1380 },
+{ 0x120, 194, 1, 1391 },
+{ 0x121, 174, 0, 1381 },
+{ 0xa0, 194, 1, 1392 },
+{ 0xa1, 174, 0, 1382 },
+{ 0x60, 194, 1, 1394 },
+{ 0x20, 194, 1, 1393 },
+{ 0x61, 174, 1, 1384 },
+{ 0x21, 174, 0, 1383 },
+{ 0x10, 194, 1, 1395 },
+{ 0x11, 174, 0, 1385 },
+{ 0x8, 194, 1, 1396 },
+{ 0x9, 174, 0, 1386 },
+{ 0x4, 194, 1, 1397 },
+{ 0x5, 174, 0, 1387 },
+{ 0x1, 194, 1, 1399 },
+{ 0x2, 194, 1, 1398 },
+{ 0x3, 174, 1, 1389 },
+{ 0x1, 174, 0, 1388 },
+{ 0x1, 153, 1, 1407 },
+{ 0x1, 154, 1, 1406 },
+{ 0x1, 155, 1, 1405 },
+{ 0x1, 156, 0, 1404 },
+{ 0x3, 153, 1, 1403 },
+{ 0x3, 154, 1, 1402 },
+{ 0x3, 155, 1, 1401 },
+{ 0x3, 156, 0, 1400 },
+{ 0x1108, 159, 1, 1569 },
+{ 0x1108, 160, 1, 1568 },
+{ 0x1108, 165, 1, 1409 },
+{ 0x1108, 166, 0, 1408 },
+{ 0x908, 159, 1, 1571 },
+{ 0x908, 160, 1, 1570 },
+{ 0x908, 165, 1, 1411 },
+{ 0x908, 166, 0, 1410 },
+{ 0x508, 159, 1, 1573 },
+{ 0x508, 160, 1, 1572 },
+{ 0x508, 165, 1, 1413 },
+{ 0x508, 166, 0, 1412 },
+{ 0x308, 159, 1, 1577 },
+{ 0x308, 160, 1, 1576 },
+{ 0x108, 160, 1, 1574 },
+{ 0x18, 161, 1, 1575 },
+{ 0x308, 165, 1, 1417 },
+{ 0x308, 166, 1, 1416 },
+{ 0x108, 166, 1, 1414 },
+{ 0x18, 167, 0, 1415 },
+{ 0x88, 159, 1, 1609 },
+{ 0x88, 160, 1, 1608 },
+{ 0x88, 165, 1, 1489 },
+{ 0x88, 166, 0, 1488 },
+{ 0x48, 159, 1, 1611 },
+{ 0x48, 160, 1, 1610 },
+{ 0x48, 165, 1, 1491 },
+{ 0x48, 166, 0, 1490 },
+{ 0x28, 159, 1, 1613 },
+{ 0x28, 160, 1, 1612 },
+{ 0x28, 165, 1, 1493 },
+{ 0x28, 166, 0, 1492 },
+{ 0x18, 159, 1, 1617 },
+{ 0x18, 160, 1, 1616 },
+{ 0x8, 160, 1, 1614 },
+{ 0x8, 161, 1, 1615 },
+{ 0x18, 165, 1, 1497 },
+{ 0x18, 166, 1, 1496 },
+{ 0x8, 166, 1, 1494 },
+{ 0x8, 167, 0, 1495 },
+{ 0x884, 159, 1, 1579 },
+{ 0x884, 160, 1, 1578 },
+{ 0x442, 162, 1, 1469 },
+{ 0x442, 163, 1, 1468 },
+{ 0x884, 165, 1, 1439 },
+{ 0x884, 166, 1, 1438 },
+{ 0x442, 168, 1, 1419 },
+{ 0x442, 169, 0, 1418 },
+{ 0x484, 159, 1, 1581 },
+{ 0x484, 160, 1, 1580 },
+{ 0x242, 162, 1, 1471 },
+{ 0x242, 163, 1, 1470 },
+{ 0x484, 165, 1, 1441 },
+{ 0x484, 166, 1, 1440 },
+{ 0x242, 168, 1, 1421 },
+{ 0x242, 169, 0, 1420 },
+{ 0x284, 159, 1, 1583 },
+{ 0x284, 160, 1, 1582 },
+{ 0x142, 162, 1, 1473 },
+{ 0x142, 163, 1, 1472 },
+{ 0x284, 165, 1, 1443 },
+{ 0x284, 166, 1, 1442 },
+{ 0x142, 168, 1, 1423 },
+{ 0x142, 169, 0, 1422 },
+{ 0x184, 159, 1, 1587 },
+{ 0x184, 160, 1, 1586 },
+{ 0x84, 160, 1, 1584 },
+{ 0xc, 161, 1, 1585 },
+{ 0xc2, 162, 1, 1477 },
+{ 0xc2, 163, 1, 1476 },
+{ 0x42, 163, 1, 1474 },
+{ 0x6, 164, 1, 1475 },
+{ 0x184, 165, 1, 1447 },
+{ 0x184, 166, 1, 1446 },
+{ 0x84, 166, 1, 1444 },
+{ 0xc, 167, 1, 1445 },
+{ 0xc2, 168, 1, 1427 },
+{ 0xc2, 169, 1, 1426 },
+{ 0x42, 169, 1, 1424 },
+{ 0x6, 170, 0, 1425 },
+{ 0x44, 159, 1, 1619 },
+{ 0x44, 160, 1, 1618 },
+{ 0x22, 162, 1, 1549 },
+{ 0x22, 163, 1, 1548 },
+{ 0x44, 165, 1, 1519 },
+{ 0x44, 166, 1, 1518 },
+{ 0x22, 168, 1, 1499 },
+{ 0x22, 169, 0, 1498 },
+{ 0x24, 159, 1, 1621 },
+{ 0x24, 160, 1, 1620 },
+{ 0x12, 162, 1, 1551 },
+{ 0x12, 163, 1, 1550 },
+{ 0x24, 165, 1, 1521 },
+{ 0x24, 166, 1, 1520 },
+{ 0x12, 168, 1, 1501 },
+{ 0x12, 169, 0, 1500 },
+{ 0x14, 159, 1, 1623 },
+{ 0x14, 160, 1, 1622 },
+{ 0xa, 162, 1, 1553 },
+{ 0xa, 163, 1, 1552 },
+{ 0x14, 165, 1, 1523 },
+{ 0x14, 166, 1, 1522 },
+{ 0xa, 168, 1, 1503 },
+{ 0xa, 169, 0, 1502 },
+{ 0xc, 159, 1, 1627 },
+{ 0xc, 160, 1, 1626 },
+{ 0x4, 160, 1, 1624 },
+{ 0x4, 161, 1, 1625 },
+{ 0x6, 162, 1, 1557 },
+{ 0x6, 163, 1, 1556 },
+{ 0x2, 163, 1, 1554 },
+{ 0x2, 164, 1, 1555 },
+{ 0xc, 165, 1, 1527 },
+{ 0xc, 166, 1, 1526 },
+{ 0x4, 166, 1, 1524 },
+{ 0x4, 167, 1, 1525 },
+{ 0x6, 168, 1, 1507 },
+{ 0x6, 169, 1, 1506 },
+{ 0x2, 169, 1, 1504 },
+{ 0x2, 170, 0, 1505 },
+{ 0x442, 159, 1, 1589 },
+{ 0x442, 160, 1, 1588 },
+{ 0x221, 162, 1, 1479 },
+{ 0x221, 163, 1, 1478 },
+{ 0x442, 165, 1, 1449 },
+{ 0x442, 166, 1, 1448 },
+{ 0x221, 168, 1, 1429 },
+{ 0x221, 169, 0, 1428 },
+{ 0x242, 159, 1, 1591 },
+{ 0x242, 160, 1, 1590 },
+{ 0x121, 162, 1, 1481 },
+{ 0x121, 163, 1, 1480 },
+{ 0x242, 165, 1, 1451 },
+{ 0x242, 166, 1, 1450 },
+{ 0x121, 168, 1, 1431 },
+{ 0x121, 169, 0, 1430 },
+{ 0x142, 159, 1, 1593 },
+{ 0x142, 160, 1, 1592 },
+{ 0xa1, 162, 1, 1483 },
+{ 0xa1, 163, 1, 1482 },
+{ 0x142, 165, 1, 1453 },
+{ 0x142, 166, 1, 1452 },
+{ 0xa1, 168, 1, 1433 },
+{ 0xa1, 169, 0, 1432 },
+{ 0xc2, 159, 1, 1597 },
+{ 0xc2, 160, 1, 1596 },
+{ 0x42, 160, 1, 1594 },
+{ 0x6, 161, 1, 1595 },
+{ 0x61, 162, 1, 1487 },
+{ 0x61, 163, 1, 1486 },
+{ 0x21, 163, 1, 1484 },
+{ 0x3, 164, 1, 1485 },
+{ 0xc2, 165, 1, 1457 },
+{ 0xc2, 166, 1, 1456 },
+{ 0x42, 166, 1, 1454 },
+{ 0x6, 167, 1, 1455 },
+{ 0x61, 168, 1, 1437 },
+{ 0x61, 169, 1, 1436 },
+{ 0x21, 169, 1, 1434 },
+{ 0x3, 170, 0, 1435 },
+{ 0x22, 159, 1, 1629 },
+{ 0x22, 160, 1, 1628 },
+{ 0x11, 162, 1, 1559 },
+{ 0x11, 163, 1, 1558 },
+{ 0x22, 165, 1, 1529 },
+{ 0x22, 166, 1, 1528 },
+{ 0x11, 168, 1, 1509 },
+{ 0x11, 169, 0, 1508 },
+{ 0x12, 159, 1, 1631 },
+{ 0x12, 160, 1, 1630 },
+{ 0x9, 162, 1, 1561 },
+{ 0x9, 163, 1, 1560 },
+{ 0x12, 165, 1, 1531 },
+{ 0x12, 166, 1, 1530 },
+{ 0x9, 168, 1, 1511 },
+{ 0x9, 169, 0, 1510 },
+{ 0xa, 159, 1, 1633 },
+{ 0xa, 160, 1, 1632 },
+{ 0x5, 162, 1, 1563 },
+{ 0x5, 163, 1, 1562 },
+{ 0xa, 165, 1, 1533 },
+{ 0xa, 166, 1, 1532 },
+{ 0x5, 168, 1, 1513 },
+{ 0x5, 169, 0, 1512 },
+{ 0x6, 159, 1, 1637 },
+{ 0x6, 160, 1, 1636 },
+{ 0x2, 160, 1, 1634 },
+{ 0x2, 161, 1, 1635 },
+{ 0x3, 162, 1, 1567 },
+{ 0x3, 163, 1, 1566 },
+{ 0x1, 163, 1, 1564 },
+{ 0x1, 164, 1, 1565 },
+{ 0x6, 165, 1, 1537 },
+{ 0x6, 166, 1, 1536 },
+{ 0x2, 166, 1, 1534 },
+{ 0x2, 167, 1, 1535 },
+{ 0x3, 168, 1, 1517 },
+{ 0x3, 169, 1, 1516 },
+{ 0x1, 169, 1, 1514 },
+{ 0x1, 170, 0, 1515 },
+{ 0x221, 159, 1, 1599 },
+{ 0x221, 160, 1, 1598 },
+{ 0x221, 165, 1, 1459 },
+{ 0x221, 166, 0, 1458 },
+{ 0x121, 159, 1, 1601 },
+{ 0x121, 160, 1, 1600 },
+{ 0x121, 165, 1, 1461 },
+{ 0x121, 166, 0, 1460 },
+{ 0xa1, 159, 1, 1603 },
+{ 0xa1, 160, 1, 1602 },
+{ 0xa1, 165, 1, 1463 },
+{ 0xa1, 166, 0, 1462 },
+{ 0x61, 159, 1, 1607 },
+{ 0x61, 160, 1, 1606 },
+{ 0x21, 160, 1, 1604 },
+{ 0x3, 161, 1, 1605 },
+{ 0x61, 165, 1, 1467 },
+{ 0x61, 166, 1, 1466 },
+{ 0x21, 166, 1, 1464 },
+{ 0x3, 167, 0, 1465 },
+{ 0x11, 159, 1, 1639 },
+{ 0x11, 160, 1, 1638 },
+{ 0x11, 165, 1, 1539 },
+{ 0x11, 166, 0, 1538 },
+{ 0x9, 159, 1, 1641 },
+{ 0x9, 160, 1, 1640 },
+{ 0x9, 165, 1, 1541 },
+{ 0x9, 166, 0, 1540 },
+{ 0x5, 159, 1, 1643 },
+{ 0x5, 160, 1, 1642 },
+{ 0x5, 165, 1, 1543 },
+{ 0x5, 166, 0, 1542 },
+{ 0x3, 159, 1, 1647 },
+{ 0x3, 160, 1, 1646 },
+{ 0x1, 160, 1, 1644 },
+{ 0x1, 161, 1, 1645 },
+{ 0x3, 165, 1, 1547 },
+{ 0x3, 166, 1, 1546 },
+{ 0x1, 166, 1, 1544 },
+{ 0x1, 167, 0, 1545 },
+{ 0x442, 205, 0, 1648 },
+{ 0x242, 205, 0, 1649 },
+{ 0x142, 205, 0, 1650 },
+{ 0xc2, 205, 1, 1652 },
+{ 0x6, 206, 1, 1651 },
+{ 0x1, 443, 0, 981 },
+{ 0x22, 205, 0, 1658 },
+{ 0x12, 205, 0, 1659 },
+{ 0xa, 205, 0, 1660 },
+{ 0x6, 205, 1, 1662 },
+{ 0x2, 206, 1, 1661 },
+{ 0x2, 367, 0, 1010 },
+{ 0x221, 205, 0, 1653 },
+{ 0x121, 205, 0, 1654 },
+{ 0xa1, 205, 0, 1655 },
+{ 0x61, 205, 1, 1657 },
+{ 0x3, 206, 1, 1656 },
+{ 0x1, 437, 0, 982 },
+{ 0x11, 205, 0, 1663 },
+{ 0x9, 205, 0, 1664 },
+{ 0x5, 205, 0, 1665 },
+{ 0x3, 205, 1, 1667 },
+{ 0x1, 206, 1, 1666 },
+{ 0x1, 367, 0, 1011 },
+{ 0x4, 211, 0, 1668 },
+{ 0x1, 211, 0, 1670 },
+{ 0x1, 218, 0, 1671 },
+{ 0x1, 217, 1, 1672 },
+{ 0x2, 211, 0, 1669 },
+{ 0x1, 196, 0, 1673 },
+{ 0x880, 202, 0, 1674 },
+{ 0x480, 202, 0, 1675 },
+{ 0x280, 202, 0, 1676 },
+{ 0x180, 202, 1, 1678 },
+{ 0x80, 203, 0, 1677 },
+{ 0x440, 202, 1, 1689 },
+{ 0x88, 204, 0, 1679 },
+{ 0x240, 202, 1, 1690 },
+{ 0x48, 204, 0, 1680 },
+{ 0x140, 202, 1, 1691 },
+{ 0x28, 204, 0, 1681 },
+{ 0xc0, 202, 1, 1693 },
+{ 0x40, 203, 1, 1692 },
+{ 0x18, 204, 1, 1683 },
+{ 0x8, 204, 0, 1682 },
+{ 0x220, 202, 1, 1694 },
+{ 0x44, 204, 0, 1684 },
+{ 0x120, 202, 1, 1695 },
+{ 0x24, 204, 0, 1685 },
+{ 0xa0, 202, 1, 1696 },
+{ 0x14, 204, 0, 1686 },
+{ 0x60, 202, 1, 1698 },
+{ 0x20, 203, 1, 1697 },
+{ 0xc, 204, 1, 1688 },
+{ 0x4, 204, 0, 1687 },
+{ 0x110, 202, 0, 1699 },
+{ 0x90, 202, 0, 1700 },
+{ 0x50, 202, 0, 1701 },
+{ 0x30, 202, 1, 1703 },
+{ 0x10, 203, 1, 1702 },
+{ 0x1, 385, 0, 974 },
+{ 0x88, 202, 0, 1704 },
+{ 0x48, 202, 0, 1705 },
+{ 0x28, 202, 0, 1706 },
+{ 0x18, 202, 1, 1708 },
+{ 0x8, 203, 1, 1707 },
+{ 0xc, 368, 0, 1007 },
+{ 0x44, 202, 1, 1719 },
+{ 0x22, 204, 0, 1709 },
+{ 0x24, 202, 1, 1720 },
+{ 0x12, 204, 0, 1710 },
+{ 0x14, 202, 1, 1721 },
+{ 0xa, 204, 0, 1711 },
+{ 0xc, 202, 1, 1723 },
+{ 0x4, 203, 1, 1722 },
+{ 0x6, 204, 1, 1713 },
+{ 0x2, 204, 1, 1712 },
+{ 0x6, 368, 0, 1008 },
+{ 0x22, 202, 1, 1724 },
+{ 0x11, 204, 0, 1714 },
+{ 0x12, 202, 1, 1725 },
+{ 0x9, 204, 0, 1715 },
+{ 0xa, 202, 1, 1726 },
+{ 0x5, 204, 0, 1716 },
+{ 0x6, 202, 1, 1728 },
+{ 0x2, 203, 1, 1727 },
+{ 0x3, 204, 1, 1718 },
+{ 0x1, 204, 1, 1717 },
+{ 0x3, 368, 0, 1009 },
+{ 0x11, 202, 0, 1729 },
+{ 0x9, 202, 0, 1730 },
+{ 0x5, 202, 0, 1731 },
+{ 0x3, 202, 1, 1733 },
+{ 0x1, 203, 0, 1732 },
+{ 0x8, 198, 0, 1734 },
+{ 0x4, 198, 0, 1735 },
+{ 0x2, 198, 0, 1736 },
+{ 0x1, 198, 1, 1738 },
+{ 0x1, 199, 1, 1737 },
+{ 0x1, 332, 0, 988 },
+{ 0x8, 200, 0, 1739 },
+{ 0x4, 200, 0, 1740 },
+{ 0x2, 200, 0, 1741 },
+{ 0x1, 200, 1, 1743 },
+{ 0x1, 201, 1, 1742 },
+{ 0x1, 331, 0, 989 },
+{ 0x8, 209, 0, 1744 },
+{ 0x4, 209, 0, 1745 },
+{ 0x2, 209, 0, 1746 },
+{ 0x1, 209, 1, 1748 },
+{ 0x1, 210, 1, 1747 },
+{ 0x1, 330, 0, 990 },
+{ 0x8, 212, 0, 1749 },
+{ 0x4, 212, 0, 1750 },
+{ 0x2, 212, 0, 1751 },
+{ 0x1, 212, 1, 1753 },
+{ 0x1, 213, 1, 1752 },
+{ 0x1, 329, 0, 991 },
+{ 0x8, 224, 0, 1754 },
+{ 0x4, 224, 0, 1755 },
+{ 0x2, 224, 0, 1756 },
+{ 0x1, 224, 1, 1758 },
+{ 0x1, 225, 0, 1757 },
+{ 0x8, 222, 0, 1759 },
+{ 0x4, 222, 0, 1760 },
+{ 0x2, 222, 0, 1761 },
+{ 0x1, 222, 1, 1763 },
+{ 0x1, 223, 0, 1762 },
+{ 0x1, 240, 0, 1764 },
+{ 0x1, 340, 0, 1765 },
+{ 0x1, 33, 0, 1766 },
+{ 0x8, 151, 0, 1767 },
+{ 0x4, 151, 0, 1768 },
+{ 0x2, 151, 0, 1769 },
+{ 0x1, 151, 1, 1771 },
+{ 0x1, 152, 0, 1770 },
+{ 0x8, 157, 0, 1772 },
+{ 0x4, 157, 0, 1773 },
+{ 0x2, 157, 0, 1774 },
+{ 0x1, 157, 1, 1776 },
+{ 0x1, 158, 0, 1775 },
+{ 0x8, 231, 0, 1777 },
+{ 0x4, 231, 0, 1778 },
+{ 0x2, 231, 0, 1779 },
+{ 0x1, 231, 1, 1781 },
+{ 0x1, 232, 0, 1780 },
+{ 0x1, 173, 0, 1782 },
+{ 0x442, 171, 0, 1783 },
+{ 0x242, 171, 0, 1784 },
+{ 0x142, 171, 0, 1785 },
+{ 0xc2, 171, 1, 1787 },
+{ 0x6, 172, 0, 1786 },
+{ 0x22, 171, 0, 1793 },
+{ 0x12, 171, 0, 1794 },
+{ 0xa, 171, 0, 1795 },
+{ 0x6, 171, 1, 1797 },
+{ 0x2, 172, 1, 1796 },
+{ 0x1, 135, 0, 1197 },
+{ 0x221, 171, 0, 1788 },
+{ 0x121, 171, 0, 1789 },
+{ 0xa1, 171, 0, 1790 },
+{ 0x61, 171, 1, 1792 },
+{ 0x3, 172, 0, 1791 },
+{ 0x11, 171, 0, 1798 },
+{ 0x9, 171, 0, 1799 },
+{ 0x5, 171, 0, 1800 },
+{ 0x3, 171, 1, 1802 },
+{ 0x1, 172, 1, 1801 },
+{ 0x1, 134, 0, 1198 },
+{ 0x1, 237, 0, 1803 },
+{ 0x1, 195, 0, 1804 },
+{ 0x1, 149, 0, 1805 },
+{ 0x1, 148, 0, 1806 },
+{ 0x4, 234, 0, 1807 },
+{ 0x2, 234, 0, 1808 },
+{ 0x1, 234, 0, 1809 },
+{ 0x1, 197, 0, 1810 },
+{ 0x2, 235, 0, 1811 },
+{ 0x1, 235, 0, 1812 },
+{ 0x4, 185, 0, 1813 },
+{ 0x2, 185, 0, 1814 },
+{ 0x1, 185, 0, 1815 },
+{ 0x4, 182, 0, 1816 },
+{ 0x1, 190, 0, 1819 },
+{ 0x1, 189, 1, 1820 },
+{ 0x2, 182, 0, 1817 },
+{ 0x1, 142, 0, 1821 },
+{ 0x1, 297, 1, 1822 },
+{ 0x1, 182, 0, 1818 },
+{ 0x8, 144, 0, 1823 },
+{ 0x4, 144, 0, 1824 },
+{ 0x2, 144, 0, 1825 },
+{ 0x1, 144, 1, 1827 },
+{ 0x1, 145, 0, 1826 },
+{ 0x8, 146, 0, 1828 },
+{ 0x4, 146, 0, 1829 },
+{ 0x2, 146, 0, 1830 },
+{ 0x1, 146, 1, 1832 },
+{ 0x1, 147, 1, 1831 },
+{ 0x1, 426, 0, 1199 },
+{ 0x8, 180, 0, 1833 },
+{ 0x4, 180, 0, 1834 },
+{ 0x2, 180, 0, 1835 },
+{ 0x1, 180, 1, 1837 },
+{ 0x1, 181, 1, 1836 },
+{ 0x1, 425, 0, 1200 },
+{ 0x8, 183, 0, 1838 },
+{ 0x4, 183, 0, 1839 },
+{ 0x2, 183, 0, 1840 },
+{ 0x1, 183, 1, 1842 },
+{ 0x1, 184, 1, 1841 },
+{ 0x1, 424, 0, 1201 },
+{ 0x8, 228, 0, 1843 },
+{ 0x4, 228, 0, 1844 },
+{ 0x2, 228, 0, 1845 },
+{ 0x1, 228, 1, 1847 },
+{ 0x1, 229, 0, 1846 },
+{ 0x8, 226, 0, 1848 },
+{ 0x4, 226, 0, 1849 },
+{ 0x2, 226, 0, 1850 },
+{ 0x1, 226, 1, 1852 },
+{ 0x1, 227, 0, 1851 },
+{ 0x8, 44, 0, 1857 },
+{ 0x18, 44, 0, 1853 },
+{ 0x4, 44, 0, 1858 },
+{ 0xc, 44, 0, 1854 },
+{ 0x2, 44, 0, 1859 },
+{ 0x6, 44, 0, 1855 },
+{ 0x1, 44, 0, 1860 },
+{ 0x3, 44, 0, 1856 },
+{ 0x51, 30, 0, 1862 },
+{ 0xd1, 30, 0, 1861 },
+{ 0x31, 30, 1, 1872 },
+{ 0x11, 31, 0, 1871 },
+{ 0x71, 30, 1, 1870 },
+{ 0x31, 31, 0, 1869 },
+{ 0x29, 30, 0, 1864 },
+{ 0x69, 30, 0, 1863 },
+{ 0x19, 30, 1, 1876 },
+{ 0x9, 31, 0, 1875 },
+{ 0x39, 30, 1, 1874 },
+{ 0x19, 31, 0, 1873 },
+{ 0x15, 30, 0, 1866 },
+{ 0x35, 30, 0, 1865 },
+{ 0xd, 30, 1, 1880 },
+{ 0x5, 31, 0, 1879 },
+{ 0x1d, 30, 1, 1878 },
+{ 0xd, 31, 0, 1877 },
+{ 0xb, 30, 0, 1868 },
+{ 0x1b, 30, 0, 1867 },
+{ 0x7, 30, 1, 1884 },
+{ 0x3, 31, 0, 1883 },
+{ 0xf, 30, 1, 1882 },
+{ 0x7, 31, 0, 1881 },
+{ 0xa2, 28, 0, 1886 },
+{ 0x1a2, 28, 0, 1885 },
+{ 0x62, 28, 1, 1896 },
+{ 0x22, 29, 0, 1895 },
+{ 0xe2, 28, 1, 1894 },
+{ 0x62, 29, 0, 1893 },
+{ 0x52, 28, 0, 1888 },
+{ 0xd2, 28, 0, 1887 },
+{ 0x32, 28, 1, 1900 },
+{ 0x12, 29, 0, 1899 },
+{ 0x72, 28, 1, 1898 },
+{ 0x32, 29, 0, 1897 },
+{ 0x2a, 28, 0, 1890 },
+{ 0x6a, 28, 0, 1889 },
+{ 0x1a, 28, 1, 1904 },
+{ 0xa, 29, 0, 1903 },
+{ 0x3a, 28, 1, 1902 },
+{ 0x1a, 29, 0, 1901 },
+{ 0x16, 28, 0, 1892 },
+{ 0x36, 28, 0, 1891 },
+{ 0xe, 28, 1, 1908 },
+{ 0x6, 29, 0, 1907 },
+{ 0x1e, 28, 1, 1906 },
+{ 0xe, 29, 0, 1905 },
+{ 0x51, 28, 0, 1910 },
+{ 0xd1, 28, 0, 1909 },
+{ 0x31, 28, 1, 1920 },
+{ 0x11, 29, 0, 1919 },
+{ 0x71, 28, 1, 1918 },
+{ 0x31, 29, 0, 1917 },
+{ 0x29, 28, 0, 1912 },
+{ 0x69, 28, 0, 1911 },
+{ 0x19, 28, 1, 1924 },
+{ 0x9, 29, 0, 1923 },
+{ 0x39, 28, 1, 1922 },
+{ 0x19, 29, 0, 1921 },
+{ 0x15, 28, 0, 1914 },
+{ 0x35, 28, 0, 1913 },
+{ 0xd, 28, 1, 1928 },
+{ 0x5, 29, 0, 1927 },
+{ 0x1d, 28, 1, 1926 },
+{ 0xd, 29, 0, 1925 },
+{ 0xb, 28, 0, 1916 },
+{ 0x1b, 28, 0, 1915 },
+{ 0x7, 28, 1, 1932 },
+{ 0x3, 29, 0, 1931 },
+{ 0xf, 28, 1, 1930 },
+{ 0x7, 29, 0, 1929 },
+{ 0x51, 26, 0, 1934 },
+{ 0xd1, 26, 0, 1933 },
+{ 0x31, 26, 1, 1944 },
+{ 0x11, 27, 0, 1943 },
+{ 0x71, 26, 1, 1942 },
+{ 0x31, 27, 0, 1941 },
+{ 0x29, 26, 0, 1936 },
+{ 0x69, 26, 0, 1935 },
+{ 0x19, 26, 1, 1948 },
+{ 0x9, 27, 0, 1947 },
+{ 0x39, 26, 1, 1946 },
+{ 0x19, 27, 0, 1945 },
+{ 0x15, 26, 0, 1938 },
+{ 0x35, 26, 0, 1937 },
+{ 0xd, 26, 1, 1952 },
+{ 0x5, 27, 0, 1951 },
+{ 0x1d, 26, 1, 1950 },
+{ 0xd, 27, 0, 1949 },
+{ 0xb, 26, 0, 1940 },
+{ 0x1b, 26, 0, 1939 },
+{ 0x7, 26, 1, 1956 },
+{ 0x3, 27, 0, 1955 },
+{ 0xf, 26, 1, 1954 },
+{ 0x7, 27, 0, 1953 },
+{ 0xa2, 24, 0, 1958 },
+{ 0x1a2, 24, 0, 1957 },
+{ 0x62, 24, 1, 1968 },
+{ 0x22, 25, 0, 1967 },
+{ 0xe2, 24, 1, 1966 },
+{ 0x62, 25, 0, 1965 },
+{ 0x52, 24, 0, 1960 },
+{ 0xd2, 24, 0, 1959 },
+{ 0x32, 24, 1, 1972 },
+{ 0x12, 25, 0, 1971 },
+{ 0x72, 24, 1, 1970 },
+{ 0x32, 25, 0, 1969 },
+{ 0x2a, 24, 0, 1962 },
+{ 0x6a, 24, 0, 1961 },
+{ 0x1a, 24, 1, 1976 },
+{ 0xa, 25, 0, 1975 },
+{ 0x3a, 24, 1, 1974 },
+{ 0x1a, 25, 0, 1973 },
+{ 0x16, 24, 0, 1964 },
+{ 0x36, 24, 0, 1963 },
+{ 0xe, 24, 1, 1980 },
+{ 0x6, 25, 0, 1979 },
+{ 0x1e, 24, 1, 1978 },
+{ 0xe, 25, 0, 1977 },
+{ 0x51, 24, 0, 1982 },
+{ 0xd1, 24, 0, 1981 },
+{ 0x31, 24, 1, 1992 },
+{ 0x11, 25, 0, 1991 },
+{ 0x71, 24, 1, 1990 },
+{ 0x31, 25, 0, 1989 },
+{ 0x29, 24, 0, 1984 },
+{ 0x69, 24, 0, 1983 },
+{ 0x19, 24, 1, 1996 },
+{ 0x9, 25, 0, 1995 },
+{ 0x39, 24, 1, 1994 },
+{ 0x19, 25, 0, 1993 },
+{ 0x15, 24, 0, 1986 },
+{ 0x35, 24, 0, 1985 },
+{ 0xd, 24, 1, 2000 },
+{ 0x5, 25, 0, 1999 },
+{ 0x1d, 24, 1, 1998 },
+{ 0xd, 25, 0, 1997 },
+{ 0xb, 24, 0, 1988 },
+{ 0x1b, 24, 0, 1987 },
+{ 0x7, 24, 1, 2004 },
+{ 0x3, 25, 0, 2003 },
+{ 0xf, 24, 1, 2002 },
+{ 0x7, 25, 0, 2001 },
+{ 0x51, 22, 1, 2030 },
+{ 0x50, 22, 0, 2006 },
+{ 0xd1, 22, 1, 2029 },
+{ 0xd0, 22, 0, 2005 },
+{ 0x31, 22, 1, 2040 },
+{ 0x30, 22, 1, 2016 },
+{ 0x11, 23, 1, 2039 },
+{ 0x10, 23, 0, 2015 },
+{ 0x71, 22, 1, 2038 },
+{ 0x70, 22, 1, 2014 },
+{ 0x31, 23, 1, 2037 },
+{ 0x30, 23, 0, 2013 },
+{ 0x29, 22, 1, 2032 },
+{ 0x28, 22, 0, 2008 },
+{ 0x69, 22, 1, 2031 },
+{ 0x68, 22, 0, 2007 },
+{ 0x19, 22, 1, 2044 },
+{ 0x18, 22, 1, 2020 },
+{ 0x9, 23, 1, 2043 },
+{ 0x8, 23, 0, 2019 },
+{ 0x39, 22, 1, 2042 },
+{ 0x38, 22, 1, 2018 },
+{ 0x19, 23, 1, 2041 },
+{ 0x18, 23, 0, 2017 },
+{ 0x15, 22, 1, 2034 },
+{ 0x14, 22, 0, 2010 },
+{ 0x35, 22, 1, 2033 },
+{ 0x34, 22, 0, 2009 },
+{ 0xd, 22, 1, 2048 },
+{ 0xc, 22, 1, 2024 },
+{ 0x5, 23, 1, 2047 },
+{ 0x4, 23, 0, 2023 },
+{ 0x1d, 22, 1, 2046 },
+{ 0x1c, 22, 1, 2022 },
+{ 0xd, 23, 1, 2045 },
+{ 0xc, 23, 0, 2021 },
+{ 0xb, 22, 1, 2036 },
+{ 0xa, 22, 0, 2012 },
+{ 0x1b, 22, 1, 2035 },
+{ 0x1a, 22, 0, 2011 },
+{ 0x7, 22, 1, 2052 },
+{ 0x6, 22, 1, 2028 },
+{ 0x3, 23, 1, 2051 },
+{ 0x2, 23, 0, 2027 },
+{ 0xf, 22, 1, 2050 },
+{ 0xe, 22, 1, 2026 },
+{ 0x7, 23, 1, 2049 },
+{ 0x6, 23, 0, 2025 },
+{ 0x8, 21, 0, 2054 },
+{ 0x18, 21, 0, 2053 },
+{ 0x1, 21, 1, 2058 },
+{ 0x2, 21, 0, 2057 },
+{ 0x3, 21, 1, 2056 },
+{ 0x4, 21, 0, 2055 },
+{ 0x1, 239, 0, 2059 },
+{ 0x1, 339, 0, 2060 },
+{ 0x14, 43, 0, 2063 },
+{ 0x34, 43, 0, 2061 },
+{ 0xc, 43, 0, 2064 },
+{ 0x1c, 43, 0, 2062 },
+{ 0x2, 43, 0, 2067 },
+{ 0x6, 43, 0, 2065 },
+{ 0x1, 43, 0, 2068 },
+{ 0x3, 43, 0, 2066 },
+{ 0x51, 19, 0, 2070 },
+{ 0xd1, 19, 0, 2069 },
+{ 0x31, 19, 1, 2080 },
+{ 0x11, 20, 0, 2079 },
+{ 0x71, 19, 1, 2078 },
+{ 0x31, 20, 0, 2077 },
+{ 0x29, 19, 0, 2072 },
+{ 0x69, 19, 0, 2071 },
+{ 0x19, 19, 1, 2084 },
+{ 0x9, 20, 0, 2083 },
+{ 0x39, 19, 1, 2082 },
+{ 0x19, 20, 0, 2081 },
+{ 0x15, 19, 0, 2074 },
+{ 0x35, 19, 0, 2073 },
+{ 0xd, 19, 1, 2088 },
+{ 0x5, 20, 0, 2087 },
+{ 0x1d, 19, 1, 2086 },
+{ 0xd, 20, 0, 2085 },
+{ 0xb, 19, 0, 2076 },
+{ 0x1b, 19, 0, 2075 },
+{ 0x7, 19, 1, 2092 },
+{ 0x3, 20, 0, 2091 },
+{ 0xf, 19, 1, 2090 },
+{ 0x7, 20, 0, 2089 },
+{ 0x1, 32, 0, 2093 },
+{ 0x2, 447, 0, 2094 },
+{ 0x1, 447, 0, 2095 },
+{ 0x1, 140, 0, 2096 },
+{ 0x2, 45, 0, 2097 },
+{ 0x1, 45, 0, 2098 },
+{ 0x1, 387, 0, 2099 },
+{ 0x2, 52, 0, 2100 },
+{ 0x1, 52, 0, 2101 },
+{ 0x1, 133, 0, 2102 },
+{ 0x51, 17, 0, 2104 },
+{ 0xd1, 17, 0, 2103 },
+{ 0x31, 17, 1, 2114 },
+{ 0x11, 18, 0, 2113 },
+{ 0x71, 17, 1, 2112 },
+{ 0x31, 18, 0, 2111 },
+{ 0x29, 17, 0, 2106 },
+{ 0x69, 17, 0, 2105 },
+{ 0x19, 17, 1, 2118 },
+{ 0x9, 18, 0, 2117 },
+{ 0x39, 17, 1, 2116 },
+{ 0x19, 18, 0, 2115 },
+{ 0x15, 17, 0, 2108 },
+{ 0x35, 17, 0, 2107 },
+{ 0xd, 17, 1, 2122 },
+{ 0x5, 18, 0, 2121 },
+{ 0x1d, 17, 1, 2120 },
+{ 0xd, 18, 0, 2119 },
+{ 0xb, 17, 0, 2110 },
+{ 0x1b, 17, 0, 2109 },
+{ 0x7, 17, 1, 2126 },
+{ 0x3, 18, 0, 2125 },
+{ 0xf, 17, 1, 2124 },
+{ 0x7, 18, 0, 2123 },
+{ 0xa20, 15, 0, 2128 },
+{ 0x1a20, 15, 0, 2127 },
+{ 0x620, 15, 1, 2138 },
+{ 0x220, 16, 0, 2137 },
+{ 0xe20, 15, 1, 2136 },
+{ 0x620, 16, 0, 2135 },
+{ 0x520, 15, 0, 2130 },
+{ 0xd20, 15, 0, 2129 },
+{ 0x320, 15, 1, 2142 },
+{ 0x120, 16, 0, 2141 },
+{ 0x720, 15, 1, 2140 },
+{ 0x320, 16, 0, 2139 },
+{ 0x2a0, 15, 0, 2132 },
+{ 0x6a0, 15, 0, 2131 },
+{ 0x1a0, 15, 1, 2146 },
+{ 0xa0, 16, 0, 2145 },
+{ 0x3a0, 15, 1, 2144 },
+{ 0x1a0, 16, 0, 2143 },
+{ 0x160, 15, 0, 2134 },
+{ 0x360, 15, 0, 2133 },
+{ 0xe0, 15, 1, 2150 },
+{ 0x60, 16, 0, 2149 },
+{ 0x1e0, 15, 1, 2148 },
+{ 0xe0, 16, 0, 2147 },
+{ 0x51, 15, 1, 2176 },
+{ 0x50, 15, 0, 2152 },
+{ 0xd1, 15, 1, 2175 },
+{ 0xd0, 15, 0, 2151 },
+{ 0x31, 15, 1, 2186 },
+{ 0x30, 15, 1, 2162 },
+{ 0x11, 16, 1, 2185 },
+{ 0x10, 16, 0, 2161 },
+{ 0x71, 15, 1, 2184 },
+{ 0x70, 15, 1, 2160 },
+{ 0x31, 16, 1, 2183 },
+{ 0x30, 16, 0, 2159 },
+{ 0x29, 15, 1, 2178 },
+{ 0x28, 15, 0, 2154 },
+{ 0x69, 15, 1, 2177 },
+{ 0x68, 15, 0, 2153 },
+{ 0x19, 15, 1, 2190 },
+{ 0x18, 15, 1, 2166 },
+{ 0x9, 16, 1, 2189 },
+{ 0x8, 16, 0, 2165 },
+{ 0x39, 15, 1, 2188 },
+{ 0x38, 15, 1, 2164 },
+{ 0x19, 16, 1, 2187 },
+{ 0x18, 16, 0, 2163 },
+{ 0x15, 15, 1, 2180 },
+{ 0x14, 15, 0, 2156 },
+{ 0x35, 15, 1, 2179 },
+{ 0x34, 15, 0, 2155 },
+{ 0xd, 15, 1, 2194 },
+{ 0xc, 15, 1, 2170 },
+{ 0x5, 16, 1, 2193 },
+{ 0x4, 16, 0, 2169 },
+{ 0x1d, 15, 1, 2192 },
+{ 0x1c, 15, 1, 2168 },
+{ 0xd, 16, 1, 2191 },
+{ 0xc, 16, 0, 2167 },
+{ 0xb, 15, 1, 2182 },
+{ 0xa, 15, 0, 2158 },
+{ 0x1b, 15, 1, 2181 },
+{ 0x1a, 15, 0, 2157 },
+{ 0x7, 15, 1, 2198 },
+{ 0x6, 15, 1, 2174 },
+{ 0x3, 16, 1, 2197 },
+{ 0x2, 16, 0, 2173 },
+{ 0xf, 15, 1, 2196 },
+{ 0xe, 15, 1, 2172 },
+{ 0x7, 16, 1, 2195 },
+{ 0x6, 16, 0, 2171 },
+{ 0x8, 14, 0, 2200 },
+{ 0x18, 14, 0, 2199 },
+{ 0x1, 14, 1, 2204 },
+{ 0x2, 14, 0, 2203 },
+{ 0x3, 14, 1, 2202 },
+{ 0x4, 14, 0, 2201 },
+{ 0x1, 109, 1, 2356 },
+{ 0x1, 110, 1, 2355 },
+{ 0x1, 111, 1, 2354 },
+{ 0x1, 112, 1, 2353 },
+{ 0x1, 113, 1, 2352 },
+{ 0x1, 114, 1, 2351 },
+{ 0x1, 115, 1, 2350 },
+{ 0x1, 116, 1, 2349 },
+{ 0x39, 41, 1, 22 },
+{ 0x19, 42, 0, 21 },
+{ 0x3, 109, 1, 2348 },
+{ 0x3, 110, 1, 2347 },
+{ 0x3, 111, 1, 2346 },
+{ 0x3, 112, 1, 2345 },
+{ 0x3, 113, 1, 2344 },
+{ 0x3, 114, 1, 2343 },
+{ 0x3, 115, 1, 2342 },
+{ 0x3, 116, 1, 2341 },
+{ 0x69, 41, 0, 11 },
+{ 0x14, 100, 1, 2336 },
+{ 0x22, 101, 1, 2333 },
+{ 0x44, 101, 1, 2335 },
+{ 0xa, 108, 1, 2334 },
+{ 0xd1, 41, 0, 9 },
+{ 0x34, 100, 1, 2208 },
+{ 0xc4, 101, 1, 2207 },
+{ 0x1c, 107, 1, 2205 },
+{ 0xe, 122, 0, 2206 },
+{ 0xc, 100, 1, 2496 },
+{ 0xa, 101, 1, 2493 },
+{ 0x14, 101, 1, 2495 },
+{ 0x6, 108, 0, 2494 },
+{ 0x2, 100, 1, 2220 },
+{ 0x2, 101, 1, 2219 },
+{ 0x2, 106, 1, 2218 },
+{ 0x2, 107, 0, 2217 },
+{ 0x12, 100, 1, 2216 },
+{ 0x42, 101, 1, 2215 },
+{ 0x6, 106, 1, 2214 },
+{ 0x6, 107, 0, 2213 },
+{ 0xa, 100, 1, 2340 },
+{ 0x12, 101, 1, 2339 },
+{ 0x24, 101, 1, 2337 },
+{ 0x5, 108, 1, 2338 },
+{ 0x71, 41, 1, 18 },
+{ 0x31, 42, 0, 17 },
+{ 0x1a, 100, 1, 2212 },
+{ 0x32, 101, 1, 2211 },
+{ 0x1a, 107, 1, 2209 },
+{ 0x7, 122, 0, 2210 },
+{ 0x6, 100, 1, 2500 },
+{ 0x6, 101, 1, 2499 },
+{ 0xc, 101, 1, 2497 },
+{ 0x3, 108, 0, 2498 },
+{ 0x1, 100, 1, 2516 },
+{ 0x1, 101, 1, 2515 },
+{ 0x1, 102, 1, 2514 },
+{ 0x1, 103, 1, 2513 },
+{ 0x1, 104, 1, 2512 },
+{ 0x1, 105, 1, 2511 },
+{ 0x1, 106, 1, 2510 },
+{ 0x1, 107, 0, 2509 },
+{ 0x3, 100, 1, 2508 },
+{ 0x3, 101, 1, 2507 },
+{ 0x3, 102, 1, 2506 },
+{ 0x3, 103, 1, 2505 },
+{ 0x3, 104, 1, 2504 },
+{ 0x3, 105, 1, 2503 },
+{ 0x3, 106, 1, 2502 },
+{ 0x3, 107, 0, 2501 },
+{ 0x8, 67, 1, 2380 },
+{ 0x8, 68, 1, 2379 },
+{ 0x2, 73, 1, 2374 },
+{ 0x2, 74, 1, 2373 },
+{ 0x1, 76, 1, 2378 },
+{ 0x1, 77, 1, 2377 },
+{ 0x1, 78, 1, 2376 },
+{ 0x1, 79, 1, 2375 },
+{ 0xf, 41, 1, 30 },
+{ 0x7, 42, 0, 29 },
+{ 0x18, 67, 1, 2372 },
+{ 0x18, 68, 1, 2371 },
+{ 0x6, 73, 1, 2366 },
+{ 0x6, 74, 1, 2365 },
+{ 0x3, 76, 1, 2370 },
+{ 0x3, 77, 1, 2369 },
+{ 0x3, 78, 1, 2368 },
+{ 0x3, 79, 1, 2367 },
+{ 0x1b, 41, 0, 15 },
+{ 0x14, 67, 1, 2360 },
+{ 0x22, 68, 1, 2357 },
+{ 0x44, 68, 1, 2359 },
+{ 0xa, 75, 1, 2358 },
+{ 0x35, 41, 0, 13 },
+{ 0x34, 67, 1, 2224 },
+{ 0xc4, 68, 1, 2223 },
+{ 0x38, 74, 1, 2221 },
+{ 0xe, 85, 0, 2222 },
+{ 0xc, 67, 1, 2520 },
+{ 0xa, 68, 1, 2517 },
+{ 0x14, 68, 1, 2519 },
+{ 0x6, 75, 0, 2518 },
+{ 0x2, 67, 1, 2236 },
+{ 0x2, 68, 1, 2235 },
+{ 0x4, 73, 1, 2234 },
+{ 0x4, 74, 0, 2233 },
+{ 0x12, 67, 1, 2232 },
+{ 0x42, 68, 1, 2231 },
+{ 0xc, 73, 1, 2230 },
+{ 0xc, 74, 0, 2229 },
+{ 0xa, 67, 1, 2364 },
+{ 0x12, 68, 1, 2363 },
+{ 0x24, 68, 1, 2361 },
+{ 0x5, 75, 1, 2362 },
+{ 0x1d, 41, 1, 26 },
+{ 0xd, 42, 0, 25 },
+{ 0x1a, 67, 1, 2228 },
+{ 0x32, 68, 1, 2227 },
+{ 0x34, 74, 1, 2225 },
+{ 0x7, 85, 0, 2226 },
+{ 0x6, 67, 1, 2524 },
+{ 0x6, 68, 1, 2523 },
+{ 0xc, 68, 1, 2521 },
+{ 0x3, 75, 0, 2522 },
+{ 0x1, 67, 1, 2540 },
+{ 0x1, 68, 1, 2539 },
+{ 0x1, 69, 1, 2538 },
+{ 0x1, 70, 1, 2537 },
+{ 0x1, 71, 1, 2536 },
+{ 0x1, 72, 1, 2535 },
+{ 0x1, 73, 1, 2534 },
+{ 0x1, 74, 0, 2533 },
+{ 0x3, 67, 1, 2532 },
+{ 0x3, 68, 1, 2531 },
+{ 0x3, 69, 1, 2530 },
+{ 0x3, 70, 1, 2529 },
+{ 0x3, 71, 1, 2528 },
+{ 0x3, 72, 1, 2527 },
+{ 0x3, 73, 1, 2526 },
+{ 0x3, 74, 0, 2525 },
+{ 0x28, 95, 1, 2388 },
+{ 0x44, 96, 1, 2383 },
+{ 0x88, 96, 1, 2387 },
+{ 0x44, 97, 1, 2382 },
+{ 0x88, 97, 1, 2386 },
+{ 0x44, 98, 1, 2381 },
+{ 0x88, 98, 1, 2385 },
+{ 0x28, 99, 0, 2384 },
+{ 0x68, 95, 1, 2244 },
+{ 0x188, 96, 1, 2243 },
+{ 0x188, 97, 1, 2242 },
+{ 0x188, 98, 1, 2241 },
+{ 0x38, 118, 1, 2240 },
+{ 0x38, 119, 1, 2239 },
+{ 0x38, 120, 1, 2238 },
+{ 0x38, 121, 0, 2237 },
+{ 0x18, 95, 1, 2548 },
+{ 0x14, 96, 1, 2543 },
+{ 0x28, 96, 1, 2547 },
+{ 0x14, 97, 1, 2542 },
+{ 0x28, 97, 1, 2546 },
+{ 0x14, 98, 1, 2541 },
+{ 0x28, 98, 1, 2545 },
+{ 0x18, 99, 0, 2544 },
+{ 0x14, 95, 1, 2396 },
+{ 0x24, 96, 1, 2395 },
+{ 0x48, 96, 1, 2391 },
+{ 0x24, 97, 1, 2394 },
+{ 0x48, 97, 1, 2390 },
+{ 0x24, 98, 1, 2393 },
+{ 0x48, 98, 1, 2389 },
+{ 0x14, 99, 0, 2392 },
+{ 0x34, 95, 1, 2252 },
+{ 0x64, 96, 1, 2251 },
+{ 0x64, 97, 1, 2250 },
+{ 0x64, 98, 1, 2249 },
+{ 0x1c, 118, 1, 2248 },
+{ 0x1c, 119, 1, 2247 },
+{ 0x1c, 120, 1, 2246 },
+{ 0x1c, 121, 0, 2245 },
+{ 0xc, 95, 1, 2556 },
+{ 0xc, 96, 1, 2555 },
+{ 0x18, 96, 1, 2551 },
+{ 0xc, 97, 1, 2554 },
+{ 0x18, 97, 1, 2550 },
+{ 0xc, 98, 1, 2553 },
+{ 0x18, 98, 1, 2549 },
+{ 0xc, 99, 0, 2552 },
+{ 0xa, 95, 1, 2404 },
+{ 0x11, 96, 1, 2399 },
+{ 0x22, 96, 1, 2403 },
+{ 0x11, 97, 1, 2398 },
+{ 0x22, 97, 1, 2402 },
+{ 0x11, 98, 1, 2397 },
+{ 0x22, 98, 1, 2401 },
+{ 0xa, 99, 0, 2400 },
+{ 0x1a, 95, 1, 2260 },
+{ 0x62, 96, 1, 2259 },
+{ 0x62, 97, 1, 2258 },
+{ 0x62, 98, 1, 2257 },
+{ 0xe, 118, 1, 2256 },
+{ 0xe, 119, 1, 2255 },
+{ 0xe, 120, 1, 2254 },
+{ 0xe, 121, 0, 2253 },
+{ 0x6, 95, 1, 2564 },
+{ 0x5, 96, 1, 2559 },
+{ 0xa, 96, 1, 2563 },
+{ 0x5, 97, 1, 2558 },
+{ 0xa, 97, 1, 2562 },
+{ 0x5, 98, 1, 2557 },
+{ 0xa, 98, 1, 2561 },
+{ 0x6, 99, 0, 2560 },
+{ 0x5, 95, 1, 2412 },
+{ 0x9, 96, 1, 2411 },
+{ 0x12, 96, 1, 2407 },
+{ 0x9, 97, 1, 2410 },
+{ 0x12, 97, 1, 2406 },
+{ 0x9, 98, 1, 2409 },
+{ 0x12, 98, 1, 2405 },
+{ 0x5, 99, 0, 2408 },
+{ 0xd, 95, 1, 2268 },
+{ 0x19, 96, 1, 2267 },
+{ 0x19, 97, 1, 2266 },
+{ 0x19, 98, 1, 2265 },
+{ 0x7, 118, 1, 2264 },
+{ 0x7, 119, 1, 2263 },
+{ 0x7, 120, 1, 2262 },
+{ 0x7, 121, 0, 2261 },
+{ 0x3, 95, 1, 2572 },
+{ 0x3, 96, 1, 2571 },
+{ 0x6, 96, 1, 2567 },
+{ 0x3, 97, 1, 2570 },
+{ 0x6, 97, 1, 2566 },
+{ 0x3, 98, 1, 2569 },
+{ 0x6, 98, 1, 2565 },
+{ 0x3, 99, 0, 2568 },
+{ 0x28, 62, 1, 2420 },
+{ 0x44, 63, 1, 2415 },
+{ 0x88, 63, 1, 2419 },
+{ 0x44, 64, 1, 2414 },
+{ 0x88, 64, 1, 2418 },
+{ 0x44, 65, 1, 2413 },
+{ 0x88, 65, 1, 2417 },
+{ 0x28, 66, 0, 2416 },
+{ 0x68, 62, 1, 2276 },
+{ 0x188, 63, 1, 2275 },
+{ 0x188, 64, 1, 2274 },
+{ 0x188, 65, 1, 2273 },
+{ 0x38, 81, 1, 2272 },
+{ 0x38, 82, 1, 2271 },
+{ 0x38, 83, 1, 2270 },
+{ 0x38, 84, 0, 2269 },
+{ 0x18, 62, 1, 2580 },
+{ 0x14, 63, 1, 2575 },
+{ 0x28, 63, 1, 2579 },
+{ 0x14, 64, 1, 2574 },
+{ 0x28, 64, 1, 2578 },
+{ 0x14, 65, 1, 2573 },
+{ 0x28, 65, 1, 2577 },
+{ 0x18, 66, 0, 2576 },
+{ 0x14, 62, 1, 2428 },
+{ 0x24, 63, 1, 2427 },
+{ 0x48, 63, 1, 2423 },
+{ 0x24, 64, 1, 2426 },
+{ 0x48, 64, 1, 2422 },
+{ 0x24, 65, 1, 2425 },
+{ 0x48, 65, 1, 2421 },
+{ 0x14, 66, 0, 2424 },
+{ 0x34, 62, 1, 2284 },
+{ 0x64, 63, 1, 2283 },
+{ 0x64, 64, 1, 2282 },
+{ 0x64, 65, 1, 2281 },
+{ 0x1c, 81, 1, 2280 },
+{ 0x1c, 82, 1, 2279 },
+{ 0x1c, 83, 1, 2278 },
+{ 0x1c, 84, 0, 2277 },
+{ 0xc, 62, 1, 2588 },
+{ 0xc, 63, 1, 2587 },
+{ 0x18, 63, 1, 2583 },
+{ 0xc, 64, 1, 2586 },
+{ 0x18, 64, 1, 2582 },
+{ 0xc, 65, 1, 2585 },
+{ 0x18, 65, 1, 2581 },
+{ 0xc, 66, 0, 2584 },
+{ 0xa, 62, 1, 2436 },
+{ 0x11, 63, 1, 2431 },
+{ 0x22, 63, 1, 2435 },
+{ 0x11, 64, 1, 2430 },
+{ 0x22, 64, 1, 2434 },
+{ 0x11, 65, 1, 2429 },
+{ 0x22, 65, 1, 2433 },
+{ 0xa, 66, 0, 2432 },
+{ 0x1a, 62, 1, 2292 },
+{ 0x62, 63, 1, 2291 },
+{ 0x62, 64, 1, 2290 },
+{ 0x62, 65, 1, 2289 },
+{ 0xe, 81, 1, 2288 },
+{ 0xe, 82, 1, 2287 },
+{ 0xe, 83, 1, 2286 },
+{ 0xe, 84, 0, 2285 },
+{ 0x6, 62, 1, 2596 },
+{ 0x5, 63, 1, 2591 },
+{ 0xa, 63, 1, 2595 },
+{ 0x5, 64, 1, 2590 },
+{ 0xa, 64, 1, 2594 },
+{ 0x5, 65, 1, 2589 },
+{ 0xa, 65, 1, 2593 },
+{ 0x6, 66, 0, 2592 },
+{ 0x5, 62, 1, 2444 },
+{ 0x9, 63, 1, 2443 },
+{ 0x12, 63, 1, 2439 },
+{ 0x9, 64, 1, 2442 },
+{ 0x12, 64, 1, 2438 },
+{ 0x9, 65, 1, 2441 },
+{ 0x12, 65, 1, 2437 },
+{ 0x5, 66, 0, 2440 },
+{ 0xd, 62, 1, 2300 },
+{ 0x19, 63, 1, 2299 },
+{ 0x19, 64, 1, 2298 },
+{ 0x19, 65, 1, 2297 },
+{ 0x7, 81, 1, 2296 },
+{ 0x7, 82, 1, 2295 },
+{ 0x7, 83, 1, 2294 },
+{ 0x7, 84, 0, 2293 },
+{ 0x3, 62, 1, 2604 },
+{ 0x3, 63, 1, 2603 },
+{ 0x6, 63, 1, 2599 },
+{ 0x3, 64, 1, 2602 },
+{ 0x6, 64, 1, 2598 },
+{ 0x3, 65, 1, 2601 },
+{ 0x6, 65, 1, 2597 },
+{ 0x3, 66, 0, 2600 },
+{ 0x8, 86, 1, 2468 },
+{ 0x8, 87, 1, 2467 },
+{ 0x2, 88, 1, 2466 },
+{ 0x2, 89, 1, 2465 },
+{ 0x2, 90, 1, 2464 },
+{ 0x2, 91, 1, 2463 },
+{ 0x2, 92, 1, 2462 },
+{ 0x2, 93, 0, 2461 },
+{ 0x18, 86, 1, 2460 },
+{ 0x18, 87, 1, 2459 },
+{ 0x6, 88, 1, 2458 },
+{ 0x6, 89, 1, 2457 },
+{ 0x6, 90, 1, 2456 },
+{ 0x6, 91, 1, 2455 },
+{ 0x6, 92, 1, 2454 },
+{ 0x6, 93, 0, 2453 },
+{ 0x14, 86, 1, 2448 },
+{ 0x22, 87, 1, 2445 },
+{ 0x44, 87, 1, 2447 },
+{ 0xa, 94, 0, 2446 },
+{ 0x34, 86, 1, 2304 },
+{ 0xc4, 87, 1, 2303 },
+{ 0x38, 93, 1, 2301 },
+{ 0xe, 117, 0, 2302 },
+{ 0xc, 86, 1, 2608 },
+{ 0xa, 87, 1, 2605 },
+{ 0x14, 87, 1, 2607 },
+{ 0x6, 94, 0, 2606 },
+{ 0x2, 86, 1, 2316 },
+{ 0x2, 87, 1, 2315 },
+{ 0x4, 92, 1, 2314 },
+{ 0x4, 93, 0, 2313 },
+{ 0x12, 86, 1, 2312 },
+{ 0x42, 87, 1, 2311 },
+{ 0xc, 92, 1, 2310 },
+{ 0xc, 93, 0, 2309 },
+{ 0xa, 86, 1, 2452 },
+{ 0x12, 87, 1, 2451 },
+{ 0x24, 87, 1, 2449 },
+{ 0x5, 94, 0, 2450 },
+{ 0x1a, 86, 1, 2308 },
+{ 0x32, 87, 1, 2307 },
+{ 0x34, 93, 1, 2305 },
+{ 0x7, 117, 0, 2306 },
+{ 0x6, 86, 1, 2612 },
+{ 0x6, 87, 1, 2611 },
+{ 0xc, 87, 1, 2609 },
+{ 0x3, 94, 0, 2610 },
+{ 0x1, 86, 1, 2628 },
+{ 0x1, 87, 1, 2627 },
+{ 0x1, 88, 1, 2626 },
+{ 0x1, 89, 1, 2625 },
+{ 0x1, 90, 1, 2624 },
+{ 0x1, 91, 1, 2623 },
+{ 0x1, 92, 1, 2622 },
+{ 0x1, 93, 0, 2621 },
+{ 0x3, 86, 1, 2620 },
+{ 0x3, 87, 1, 2619 },
+{ 0x3, 88, 1, 2618 },
+{ 0x3, 89, 1, 2617 },
+{ 0x3, 90, 1, 2616 },
+{ 0x3, 91, 1, 2615 },
+{ 0x3, 92, 1, 2614 },
+{ 0x3, 93, 0, 2613 },
+{ 0x8, 53, 1, 2492 },
+{ 0x8, 54, 1, 2491 },
+{ 0x2, 55, 1, 2490 },
+{ 0x2, 56, 1, 2489 },
+{ 0x2, 57, 1, 2488 },
+{ 0x2, 58, 1, 2487 },
+{ 0x2, 59, 1, 2486 },
+{ 0x2, 60, 0, 2485 },
+{ 0x18, 53, 1, 2484 },
+{ 0x18, 54, 1, 2483 },
+{ 0x6, 55, 1, 2482 },
+{ 0x6, 56, 1, 2481 },
+{ 0x6, 57, 1, 2480 },
+{ 0x6, 58, 1, 2479 },
+{ 0x6, 59, 1, 2478 },
+{ 0x6, 60, 0, 2477 },
+{ 0x14, 53, 1, 2472 },
+{ 0x22, 54, 1, 2469 },
+{ 0x44, 54, 1, 2471 },
+{ 0xa, 61, 0, 2470 },
+{ 0x34, 53, 1, 2320 },
+{ 0xc4, 54, 1, 2319 },
+{ 0x38, 60, 1, 2317 },
+{ 0xe, 80, 0, 2318 },
+{ 0xc, 53, 1, 2632 },
+{ 0xa, 54, 1, 2629 },
+{ 0x14, 54, 1, 2631 },
+{ 0x6, 61, 0, 2630 },
+{ 0x2, 53, 1, 2332 },
+{ 0x2, 54, 1, 2331 },
+{ 0x4, 59, 1, 2330 },
+{ 0x4, 60, 0, 2329 },
+{ 0x12, 53, 1, 2328 },
+{ 0x42, 54, 1, 2327 },
+{ 0xc, 59, 1, 2326 },
+{ 0xc, 60, 0, 2325 },
+{ 0xa, 53, 1, 2476 },
+{ 0x12, 54, 1, 2475 },
+{ 0x24, 54, 1, 2473 },
+{ 0x5, 61, 0, 2474 },
+{ 0x1a, 53, 1, 2324 },
+{ 0x32, 54, 1, 2323 },
+{ 0x34, 60, 1, 2321 },
+{ 0x7, 80, 0, 2322 },
+{ 0x6, 53, 1, 2636 },
+{ 0x6, 54, 1, 2635 },
+{ 0xc, 54, 1, 2633 },
+{ 0x3, 61, 0, 2634 },
+{ 0x1, 53, 1, 2652 },
+{ 0x1, 54, 1, 2651 },
+{ 0x1, 55, 1, 2650 },
+{ 0x1, 56, 1, 2649 },
+{ 0x1, 57, 1, 2648 },
+{ 0x1, 58, 1, 2647 },
+{ 0x1, 59, 1, 2646 },
+{ 0x1, 60, 0, 2645 },
+{ 0x3, 53, 1, 2644 },
+{ 0x3, 54, 1, 2643 },
+{ 0x3, 55, 1, 2642 },
+{ 0x3, 56, 1, 2641 },
+{ 0x3, 57, 1, 2640 },
+{ 0x3, 58, 1, 2639 },
+{ 0x3, 59, 1, 2638 },
+{ 0x3, 60, 0, 2637 },
+{ 0x1, 4, 0, 2653 },
+{ 0x1, 296, 0, 2654 },
+{ 0x1, 379, 0, 2655 },
+{ 0x1, 374, 0, 2656 },
+{ 0x2, 358, 0, 2657 },
+{ 0x1, 358, 0, 2660 },
+{ 0x2, 357, 0, 2658 },
+{ 0x1, 357, 0, 2661 },
+{ 0x2, 356, 0, 2659 },
+{ 0x1, 356, 0, 2662 },
+{ 0x1, 355, 0, 2663 },
+{ 0x1, 354, 0, 2664 },
+{ 0x2, 353, 0, 2665 },
+{ 0x1, 353, 0, 2667 },
+{ 0x2, 352, 0, 2666 },
+{ 0x1, 352, 0, 2668 },
+{ 0x1, 382, 0, 2675 },
+{ 0x8, 381, 0, 2669 },
+{ 0x4, 381, 0, 2671 },
+{ 0x2, 381, 0, 2673 },
+{ 0x1, 381, 0, 2676 },
+{ 0x8, 380, 0, 2670 },
+{ 0x4, 380, 0, 2672 },
+{ 0x2, 380, 0, 2674 },
+{ 0x1, 380, 0, 2677 },
+{ 0x1, 351, 0, 2684 },
+{ 0x8, 350, 0, 2678 },
+{ 0x4, 350, 0, 2680 },
+{ 0x2, 350, 0, 2682 },
+{ 0x1, 350, 0, 2685 },
+{ 0x8, 349, 0, 2679 },
+{ 0x4, 349, 0, 2681 },
+{ 0x2, 349, 1, 2683 },
+{ 0x4, 143, 0, 1377 },
+{ 0x1, 349, 0, 2686 },
+{ 0x1, 6, 0, 2687 },
+{ 0x1, 7, 0, 2688 },
+{ 0x1, 295, 0, 2689 },
+{ 0x1, 456, 0, 2690 },
+{ 0x1, 346, 0, 2691 },
+{ 0x1, 13, 0, 2692 },
+{ 0x1, 11, 0, 2693 },
+{ 0x1, 422, 0, 2694 },
+{ 0x1, 394, 0, 2695 },
+{ 0x1, 393, 0, 2696 },
+{ 0x1, 455, 0, 2697 },
+{ 0x1, 345, 0, 2698 },
+{ 0x1, 12, 0, 2699 },
+{ 0x1, 10, 0, 2700 },
+{ 0x1, 5, 0, 2701 },
+{ 0x1, 421, 0, 2702 },
+{ 0x1, 420, 0, 2703 },
+{ 0x1, 1, 0, 2704 },
+{ 0x1, 0, 0, 2705 },
+};
+
+
+/* ia64-opc.c -- Functions to access the compacted opcode table
+ Copyright 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
+ Written by Bob Manson of Cygnus Solutions, <manson@cygnus.com>
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 2, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+static const struct ia64_templ_desc ia64_templ_desc[16] =
+ {
+ { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, /* 0 */
+ { 2, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_L, IA64_UNIT_X }, "MLX" },
+ { 0, { 0, }, "-3-" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, /* 4 */
+ { 1, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_I }, "MFI" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_F }, "MMF" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_B }, "MIB" }, /* 8 */
+ { 0, { IA64_UNIT_M, IA64_UNIT_B, IA64_UNIT_B }, "MBB" },
+ { 0, { 0, }, "-a-" },
+ { 0, { IA64_UNIT_B, IA64_UNIT_B, IA64_UNIT_B }, "BBB" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_B }, "MMB" }, /* c */
+ { 0, { 0, }, "-d-" },
+ { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_B }, "MFB" },
+ { 0, { 0, }, "-f-" },
+ };
+
+/* Apply the completer referred to by COMPLETER_INDEX to OPCODE, and
+ return the result. */
+
+static ia64_insn
+apply_completer (ia64_insn opcode, int completer_index)
+{
+ ia64_insn mask = completer_table[completer_index].mask;
+ ia64_insn bits = completer_table[completer_index].bits;
+ int shiftamt = (completer_table[completer_index].offset & 63);
+
+ mask = mask << shiftamt;
+ bits = bits << shiftamt;
+ opcode = (opcode & ~mask) | bits;
+ return opcode;
+}
+
+/* Extract BITS number of bits starting from OP_POINTER + BITOFFSET in
+ the dis_table array, and return its value. (BITOFFSET is numbered
+ starting from MSB to LSB, so a BITOFFSET of 0 indicates the MSB of the
+ first byte in OP_POINTER.) */
+
+static int
+extract_op_bits (int op_pointer, int bitoffset, int bits)
+{
+ int res = 0;
+
+ op_pointer += (bitoffset / 8);
+
+ if (bitoffset % 8)
+ {
+ unsigned int op = dis_table[op_pointer++];
+ int numb = 8 - (bitoffset % 8);
+ int mask = (1 << numb) - 1;
+ int bata = (bits < numb) ? bits : numb;
+ int delta = numb - bata;
+
+ res = (res << bata) | ((op & mask) >> delta);
+ bitoffset += bata;
+ bits -= bata;
+ }
+ while (bits >= 8)
+ {
+ res = (res << 8) | (dis_table[op_pointer++] & 255);
+ bits -= 8;
+ }
+ if (bits > 0)
+ {
+ unsigned int op = (dis_table[op_pointer++] & 255);
+ res = (res << bits) | (op >> (8 - bits));
+ }
+ return res;
+}
+
+/* Examine the state machine entry at OP_POINTER in the dis_table
+ array, and extract its values into OPVAL and OP. The length of the
+ state entry in bits is returned. */
+
+static int
+extract_op (int op_pointer, int *opval, unsigned int *op)
+{
+ int oplen = 5;
+
+ *op = dis_table[op_pointer];
+
+ if ((*op) & 0x40)
+ {
+ opval[0] = extract_op_bits (op_pointer, oplen, 5);
+ oplen += 5;
+ }
+ switch ((*op) & 0x30)
+ {
+ case 0x10:
+ {
+ opval[1] = extract_op_bits (op_pointer, oplen, 8);
+ oplen += 8;
+ opval[1] += op_pointer;
+ break;
+ }
+ case 0x20:
+ {
+ opval[1] = extract_op_bits (op_pointer, oplen, 16);
+ if (! (opval[1] & 32768))
+ {
+ opval[1] += op_pointer;
+ }
+ oplen += 16;
+ break;
+ }
+ case 0x30:
+ {
+ oplen--;
+ opval[2] = extract_op_bits (op_pointer, oplen, 12);
+ oplen += 12;
+ opval[2] |= 32768;
+ break;
+ }
+ }
+ if (((*op) & 0x08) && (((*op) & 0x30) != 0x30))
+ {
+ opval[2] = extract_op_bits (op_pointer, oplen, 16);
+ oplen += 16;
+ if (! (opval[2] & 32768))
+ {
+ opval[2] += op_pointer;
+ }
+ }
+ return oplen;
+}
+
+/* Returns a non-zero value if the opcode in the main_table list at
+ PLACE matches OPCODE and is of type TYPE. */
+
+static int
+opcode_verify (ia64_insn opcode, int place, enum ia64_insn_type type)
+{
+ if (main_table[place].opcode_type != type)
+ {
+ return 0;
+ }
+ if (main_table[place].flags
+ & (IA64_OPCODE_F2_EQ_F3 | IA64_OPCODE_LEN_EQ_64MCNT))
+ {
+ const struct ia64_operand *o1, *o2;
+ ia64_insn f2, f3;
+
+ if (main_table[place].flags & IA64_OPCODE_F2_EQ_F3)
+ {
+ o1 = elf64_ia64_operands + IA64_OPND_F2;
+ o2 = elf64_ia64_operands + IA64_OPND_F3;
+ (*o1->extract) (o1, opcode, &f2);
+ (*o2->extract) (o2, opcode, &f3);
+ if (f2 != f3)
+ return 0;
+ }
+ else
+ {
+ ia64_insn len, count;
+
+ /* length must equal 64-count: */
+ o1 = elf64_ia64_operands + IA64_OPND_LEN6;
+ o2 = elf64_ia64_operands + main_table[place].operands[2];
+ (*o1->extract) (o1, opcode, &len);
+ (*o2->extract) (o2, opcode, &count);
+ if (len != 64 - count)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Find an instruction entry in the ia64_dis_names array that matches
+ opcode OPCODE and is of type TYPE. Returns either a positive index
+ into the array, or a negative value if an entry for OPCODE could
+ not be found. Checks all matches and returns the one with the highest
+ priority. */
+
+static int
+locate_opcode_ent (ia64_insn opcode, enum ia64_insn_type type)
+{
+ int currtest[41];
+ int bitpos[41];
+ int op_ptr[41];
+ int currstatenum = 0;
+ short found_disent = -1;
+ short found_priority = -1;
+
+ currtest[currstatenum] = 0;
+ op_ptr[currstatenum] = 0;
+ bitpos[currstatenum] = 40;
+
+ while (1)
+ {
+ int op_pointer = op_ptr[currstatenum];
+ unsigned int op;
+ int currbitnum = bitpos[currstatenum];
+ int oplen;
+ int opval[3] = {0};
+ int next_op;
+ int currbit;
+
+ oplen = extract_op (op_pointer, opval, &op);
+
+ bitpos[currstatenum] = currbitnum;
+
+ /* Skip opval[0] bits in the instruction. */
+ if (op & 0x40)
+ {
+ currbitnum -= opval[0];
+ }
+
+ /* The value of the current bit being tested. */
+ currbit = opcode & (((ia64_insn) 1) << currbitnum) ? 1 : 0;
+ next_op = -1;
+
+ /* We always perform the tests specified in the current state in
+ a particular order, falling through to the next test if the
+ previous one failed. */
+ switch (currtest[currstatenum])
+ {
+ case 0:
+ currtest[currstatenum]++;
+ if (currbit == 0 && (op & 0x80))
+ {
+ /* Check for a zero bit. If this test solely checks for
+ a zero bit, we can check for up to 8 consecutive zero
+ bits (the number to check is specified by the lower 3
+ bits in the state code.)
+
+ If the state instruction matches, we go to the very
+ next state instruction; otherwise, try the next test. */
+
+ if ((op & 0xf8) == 0x80)
+ {
+ int count = op & 0x7;
+ int x;
+
+ for (x = 0; x <= count; x++)
+ {
+ int i =
+ opcode & (((ia64_insn) 1) << (currbitnum - x)) ? 1 : 0;
+ if (i)
+ {
+ break;
+ }
+ }
+ if (x > count)
+ {
+ next_op = op_pointer + ((oplen + 7) / 8);
+ currbitnum -= count;
+ break;
+ }
+ }
+ else if (! currbit)
+ {
+ next_op = op_pointer + ((oplen + 7) / 8);
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+ case 1:
+ /* If the bit in the instruction is one, go to the state
+ instruction specified by opval[1]. */
+ currtest[currstatenum]++;
+ if (currbit && (op & 0x30) != 0 && ((op & 0x30) != 0x30))
+ {
+ next_op = opval[1];
+ break;
+ }
+ /* FALLTHROUGH */
+ case 2:
+ /* Don't care. Skip the current bit and go to the state
+ instruction specified by opval[2].
+
+ An encoding of 0x30 is special; this means that a 12-bit
+ offset into the ia64_dis_names[] array is specified. */
+ currtest[currstatenum]++;
+ if ((op & 0x08) || ((op & 0x30) == 0x30))
+ {
+ next_op = opval[2];
+ break;
+ }
+ }
+
+ /* If bit 15 is set in the address of the next state, an offset
+ in the ia64_dis_names array was specified instead. We then
+ check to see if an entry in the list of opcodes matches the
+ opcode we were given; if so, we have succeeded. */
+
+ if ((next_op >= 0) && (next_op & 32768))
+ {
+ short disent = next_op & 32767;
+ short priority = -1;
+
+ if (next_op > 65535)
+ {
+ abort ();
+ }
+
+ /* Run through the list of opcodes to check, trying to find
+ one that matches. */
+ while (disent >= 0)
+ {
+ int place = ia64_dis_names[disent].insn_index;
+
+ priority = ia64_dis_names[disent].priority;
+
+ if (opcode_verify (opcode, place, type)
+ && priority > found_priority)
+ {
+ break;
+ }
+ if (ia64_dis_names[disent].next_flag)
+ {
+ disent++;
+ }
+ else
+ {
+ disent = -1;
+ }
+ }
+
+ if (disent >= 0)
+ {
+ found_disent = disent;
+ found_priority = priority;
+ }
+ /* Try the next test in this state, regardless of whether a match
+ was found. */
+ next_op = -2;
+ }
+
+ /* next_op == -1 is "back up to the previous state".
+ next_op == -2 is "stay in this state and try the next test".
+ Otherwise, transition to the state indicated by next_op. */
+
+ if (next_op == -1)
+ {
+ currstatenum--;
+ if (currstatenum < 0)
+ {
+ return found_disent;
+ }
+ }
+ else if (next_op >= 0)
+ {
+ currstatenum++;
+ bitpos[currstatenum] = currbitnum - 1;
+ op_ptr[currstatenum] = next_op;
+ currtest[currstatenum] = 0;
+ }
+ }
+}
+
+/* Construct an ia64_opcode entry based on OPCODE, NAME and PLACE. */
+
+static struct ia64_opcode *
+make_ia64_opcode (ia64_insn opcode, const char *name, int place, int depind)
+{
+ struct ia64_opcode *res =
+ (struct ia64_opcode *) malloc (sizeof (struct ia64_opcode));
+ res->name = strdup (name);
+ res->type = main_table[place].opcode_type;
+ res->num_outputs = main_table[place].num_outputs;
+ res->opcode = opcode;
+ res->mask = main_table[place].mask;
+ res->operands[0] = main_table[place].operands[0];
+ res->operands[1] = main_table[place].operands[1];
+ res->operands[2] = main_table[place].operands[2];
+ res->operands[3] = main_table[place].operands[3];
+ res->operands[4] = main_table[place].operands[4];
+ res->flags = main_table[place].flags;
+ res->ent_index = place;
+ res->dependencies = &op_dependencies[depind];
+ return res;
+}
+
+/* Determine the ia64_opcode entry for the opcode specified by INSN
+ and TYPE. If a valid entry is not found, return NULL. */
+static struct ia64_opcode *
+ia64_dis_opcode (ia64_insn insn, enum ia64_insn_type type)
+{
+ int disent = locate_opcode_ent (insn, type);
+
+ if (disent < 0)
+ {
+ return NULL;
+ }
+ else
+ {
+ unsigned int cb = ia64_dis_names[disent].completer_index;
+ static char name[128];
+ int place = ia64_dis_names[disent].insn_index;
+ int ci = main_table[place].completers;
+ ia64_insn tinsn = main_table[place].opcode;
+
+ strcpy (name, ia64_strings [main_table[place].name_index]);
+
+ while (cb)
+ {
+ if (cb & 1)
+ {
+ int cname = completer_table[ci].name_index;
+
+ tinsn = apply_completer (tinsn, ci);
+
+ if (ia64_strings[cname][0] != '\0')
+ {
+ strcat (name, ".");
+ strcat (name, ia64_strings[cname]);
+ }
+ if (cb != 1)
+ {
+ ci = completer_table[ci].subentries;
+ }
+ }
+ else
+ {
+ ci = completer_table[ci].alternative;
+ }
+ if (ci < 0)
+ {
+ abort ();
+ }
+ cb = cb >> 1;
+ }
+ if (tinsn != (insn & main_table[place].mask))
+ {
+ abort ();
+ }
+ return make_ia64_opcode (insn, name, place,
+ completer_table[ci].dependencies);
+ }
+}
+
+/* Free any resources used by ENT. */
+static void
+ia64_free_opcode (struct ia64_opcode *ent)
+{
+ free ((void *)ent->name);
+ free (ent);
+}
+
+/* Disassemble ia64 instruction. */
+
+/* Return the instruction type for OPCODE found in unit UNIT. */
+
+static enum ia64_insn_type
+unit_to_type (ia64_insn opcode, enum ia64_unit unit)
+{
+ enum ia64_insn_type type;
+ int op;
+
+ op = IA64_OP (opcode);
+
+ if (op >= 8 && (unit == IA64_UNIT_I || unit == IA64_UNIT_M))
+ {
+ type = IA64_TYPE_A;
+ }
+ else
+ {
+ switch (unit)
+ {
+ case IA64_UNIT_I:
+ type = IA64_TYPE_I; break;
+ case IA64_UNIT_M:
+ type = IA64_TYPE_M; break;
+ case IA64_UNIT_B:
+ type = IA64_TYPE_B; break;
+ case IA64_UNIT_F:
+ type = IA64_TYPE_F; break;
+ case IA64_UNIT_L:
+ case IA64_UNIT_X:
+ type = IA64_TYPE_X; break;
+ default:
+ type = -1;
+ }
+ }
+ return type;
+}
+
+int
+print_insn_ia64 (bfd_vma memaddr, struct disassemble_info *info)
+{
+ ia64_insn t0, t1, slot[3], template, s_bit, insn;
+ int slotnum, j, status, need_comma, retval, slot_multiplier;
+ const struct ia64_operand *odesc;
+ const struct ia64_opcode *idesc;
+ const char *err, *str, *tname;
+ uint64_t value;
+ bfd_byte bundle[16];
+ enum ia64_unit unit;
+ char regname[16];
+
+ if (info->bytes_per_line == 0)
+ info->bytes_per_line = 6;
+ info->display_endian = info->endian;
+
+ slot_multiplier = info->bytes_per_line;
+ retval = slot_multiplier;
+
+ slotnum = (((long) memaddr) & 0xf) / slot_multiplier;
+ if (slotnum > 2)
+ return -1;
+
+ memaddr -= (memaddr & 0xf);
+ status = (*info->read_memory_func) (memaddr, bundle, sizeof (bundle), info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ /* bundles are always in little-endian byte order */
+ t0 = bfd_getl64 (bundle);
+ t1 = bfd_getl64 (bundle + 8);
+ s_bit = t0 & 1;
+ template = (t0 >> 1) & 0xf;
+ slot[0] = (t0 >> 5) & 0x1ffffffffffLL;
+ slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18);
+ slot[2] = (t1 >> 23) & 0x1ffffffffffLL;
+
+ tname = ia64_templ_desc[template].name;
+ if (slotnum == 0)
+ (*info->fprintf_func) (info->stream, "[%s] ", tname);
+ else
+ (*info->fprintf_func) (info->stream, " ");
+
+ unit = ia64_templ_desc[template].exec_unit[slotnum];
+
+ if (template == 2 && slotnum == 1)
+ {
+ /* skip L slot in MLI template: */
+ slotnum = 2;
+ retval += slot_multiplier;
+ }
+
+ insn = slot[slotnum];
+
+ if (unit == IA64_UNIT_NIL)
+ goto decoding_failed;
+
+ idesc = ia64_dis_opcode (insn, unit_to_type (insn, unit));
+ if (idesc == NULL)
+ goto decoding_failed;
+
+ /* print predicate, if any: */
+
+ if ((idesc->flags & IA64_OPCODE_NO_PRED)
+ || (insn & 0x3f) == 0)
+ (*info->fprintf_func) (info->stream, " ");
+ else
+ (*info->fprintf_func) (info->stream, "(p%02d) ", (int)(insn & 0x3f));
+
+ /* now the actual instruction: */
+
+ (*info->fprintf_func) (info->stream, "%s", idesc->name);
+ if (idesc->operands[0])
+ (*info->fprintf_func) (info->stream, " ");
+
+ need_comma = 0;
+ for (j = 0; j < NELEMS (idesc->operands) && idesc->operands[j]; ++j)
+ {
+ odesc = elf64_ia64_operands + idesc->operands[j];
+
+ if (need_comma)
+ (*info->fprintf_func) (info->stream, ",");
+
+ if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64)
+ {
+ /* special case of 64 bit immediate load: */
+ value = ((insn >> 13) & 0x7f) | (((insn >> 27) & 0x1ff) << 7)
+ | (((insn >> 22) & 0x1f) << 16) | (((insn >> 21) & 0x1) << 21)
+ | (slot[1] << 22) | (((insn >> 36) & 0x1) << 63);
+ }
+ else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62)
+ {
+ /* 62-bit immediate for nop.x/break.x */
+ value = ((slot[1] & 0x1ffffffffffLL) << 21)
+ | (((insn >> 36) & 0x1) << 20)
+ | ((insn >> 6) & 0xfffff);
+ }
+ else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64)
+ {
+ /* 60-bit immediate for long branches. */
+ value = (((insn >> 13) & 0xfffff)
+ | (((insn >> 36) & 1) << 59)
+ | (((slot[1] >> 2) & 0x7fffffffffLL) << 20)) << 4;
+ }
+ else
+ {
+ err = (*odesc->extract) (odesc, insn, &value);
+ if (err)
+ {
+ (*info->fprintf_func) (info->stream, "%s", err);
+ goto done;
+ }
+ }
+
+ switch (odesc->class)
+ {
+ case IA64_OPND_CLASS_CST:
+ (*info->fprintf_func) (info->stream, "%s", odesc->str);
+ break;
+
+ case IA64_OPND_CLASS_REG:
+ if (odesc->str[0] == 'a' && odesc->str[1] == 'r')
+ {
+ switch (value)
+ {
+ case 0: case 1: case 2: case 3:
+ case 4: case 5: case 6: case 7:
+ sprintf (regname, "ar.k%u", (unsigned int) value);
+ break;
+ case 16: strcpy (regname, "ar.rsc"); break;
+ case 17: strcpy (regname, "ar.bsp"); break;
+ case 18: strcpy (regname, "ar.bspstore"); break;
+ case 19: strcpy (regname, "ar.rnat"); break;
+ case 32: strcpy (regname, "ar.ccv"); break;
+ case 36: strcpy (regname, "ar.unat"); break;
+ case 40: strcpy (regname, "ar.fpsr"); break;
+ case 44: strcpy (regname, "ar.itc"); break;
+ case 64: strcpy (regname, "ar.pfs"); break;
+ case 65: strcpy (regname, "ar.lc"); break;
+ case 66: strcpy (regname, "ar.ec"); break;
+ default:
+ sprintf (regname, "ar%u", (unsigned int) value);
+ break;
+ }
+ (*info->fprintf_func) (info->stream, "%s", regname);
+ }
+ else
+ (*info->fprintf_func) (info->stream, "%s%d", odesc->str, (int)value);
+ break;
+
+ case IA64_OPND_CLASS_IND:
+ (*info->fprintf_func) (info->stream, "%s[r%d]", odesc->str, (int)value);
+ break;
+
+ case IA64_OPND_CLASS_ABS:
+ str = 0;
+ if (odesc - elf64_ia64_operands == IA64_OPND_MBTYPE4)
+ switch (value)
+ {
+ case 0x0: str = "@brcst"; break;
+ case 0x8: str = "@mix"; break;
+ case 0x9: str = "@shuf"; break;
+ case 0xa: str = "@alt"; break;
+ case 0xb: str = "@rev"; break;
+ }
+
+ if (str)
+ (*info->fprintf_func) (info->stream, "%s", str);
+ else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_SIGNED)
+ (*info->fprintf_func) (info->stream, "%" PRId64,
+ (int64_t) value);
+ else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_UNSIGNED)
+ (*info->fprintf_func) (info->stream, "%" PRIu64,
+ (uint64_t) value);
+ else
+ (*info->fprintf_func) (info->stream, "0x%" PRIx64,
+ (uint64_t) value);
+ break;
+
+ case IA64_OPND_CLASS_REL:
+ (*info->print_address_func) (memaddr + value, info);
+ break;
+ }
+
+ need_comma = 1;
+ if (j + 1 == idesc->num_outputs)
+ {
+ (*info->fprintf_func) (info->stream, "=");
+ need_comma = 0;
+ }
+ }
+ if (slotnum + 1 == ia64_templ_desc[template].group_boundary
+ || ((slotnum == 2) && s_bit))
+ (*info->fprintf_func) (info->stream, ";;");
+
+ done:
+ ia64_free_opcode ((struct ia64_opcode *)idesc);
+ failed:
+ if (slotnum == 2)
+ retval += 16 - 3*slot_multiplier;
+ return retval;
+
+ decoding_failed:
+ (*info->fprintf_func) (info->stream, " data8 %#011llx", (long long) insn);
+ goto failed;
+}
diff --git a/ia64.ld b/ia64.ld
new file mode 100644
index 0000000..081aaf1
--- /dev/null
+++ b/ia64.ld
@@ -0,0 +1,209 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf64-ia64-little", "elf64-ia64-little",
+ "elf64-ia64-little")
+OUTPUT_ARCH(ia64)
+ENTRY(_start)
+/* __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = 0x4000000060000000); . = 0x4000000060000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+ .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+ .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+ .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+ .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+ .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+ .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+ .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.IA_64.pltoff : { *(.rela.IA_64.pltoff) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x00300000010070000002000001000400
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x00300000010070000002000001000400
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x00300000010070000002000001000400
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .opd : { *(.opd) }
+ .IA_64.unwind_info : { *(.IA_64.unwind_info* .gnu.linkonce.ia64unwi.*) }
+ .IA_64.unwind : { *(.IA_64.unwind* .gnu.linkonce.ia64unw.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x10000) + (. & (0x10000 - 1));
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(64 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ /* Ensure __gp is outside the range of any normal data. We need to
+ do this to avoid the linker optimizing the code in op.o and getting
+ it out of sync with the relocs that we read when processing that
+ file. A better solution might be to ensure that the dynamically
+ generated code and static qemu code share a single gp-value. */
+ __gp = . + 0x200000;
+ .got : { *(.got.plt) *(.got) }
+ .IA_64.pltoff : { *(.IA_64.pltoff) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ PROVIDE (__sbss_end = .);
+ PROVIDE (___sbss_end = .);
+ }
+ .bss :
+ {
+ . += 0x400000; /* ensure .bss stuff is out of reach of gp */
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(64 / 8);
+ }
+ . = ALIGN(64 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/ia64intrin.h b/ia64intrin.h
new file mode 100644
index 0000000..ddd5ed9
--- /dev/null
+++ b/ia64intrin.h
@@ -0,0 +1,150 @@
+#ifndef IA64_INTRINSIC_H
+#define IA64_INTRINSIC_H
+
+/*
+ * Compiler-dependent Intrinsics
+ *
+ * Copyright (C) 2002,2003 Jun Nakajima <jun.nakajima@intel.com>
+ * Copyright (C) 2002,2003 Suresh Siddha <suresh.b.siddha@intel.com>
+ *
+ */
+extern long ia64_cmpxchg_called_with_bad_pointer (void);
+extern void ia64_bad_param_for_getreg (void);
+#define ia64_cmpxchg(sem,ptr,o,n,s) ({ \
+ uint64_t _o, _r; \
+ switch(s) { \
+ case 1: _o = (uint8_t)(long)(o); break; \
+ case 2: _o = (uint16_t)(long)(o); break; \
+ case 4: _o = (uint32_t)(long)(o); break; \
+ case 8: _o = (uint64_t)(long)(o); break; \
+ default: break; \
+ } \
+ switch(s) { \
+ case 1: \
+ _r = ia64_cmpxchg1_##sem((uint8_t*)ptr,n,_o); break; \
+ case 2: \
+ _r = ia64_cmpxchg2_##sem((uint16_t*)ptr,n,_o); break; \
+ case 4: \
+ _r = ia64_cmpxchg4_##sem((uint32_t*)ptr,n,_o); break; \
+ case 8: \
+ _r = ia64_cmpxchg8_##sem((uint64_t*)ptr,n,_o); break; \
+ default: \
+ _r = ia64_cmpxchg_called_with_bad_pointer(); break; \
+ } \
+ (__typeof__(o)) _r; \
+})
+
+#define cmpxchg_acq(ptr,o,n) ia64_cmpxchg(acq,ptr,o,n,sizeof(*ptr))
+#define cmpxchg_rel(ptr,o,n) ia64_cmpxchg(rel,ptr,o,n,sizeof(*ptr))
+
+#ifdef __INTEL_COMPILER
+void __fc(uint64_t *addr);
+void __synci(void);
+void __isrlz(void);
+void __dsrlz(void);
+uint64_t __getReg(const int whichReg);
+uint64_t _InterlockedCompareExchange8_rel(volatile uint8_t *dest, uint64_t xchg, uint64_t comp);
+uint64_t _InterlockedCompareExchange8_acq(volatile uint8_t *dest, uint64_t xchg, uint64_t comp);
+uint64_t _InterlockedCompareExchange16_rel(volatile uint16_t *dest, uint64_t xchg, uint64_t comp);
+uint64_t _InterlockedCompareExchange16_acq(volatile uint16_t *dest, uint64_t xchg, uint64_t comp);
+uint64_t _InterlockedCompareExchange_rel(volatile uint32_t *dest, uint64_t xchg, uint64_t comp);
+uint64_t _InterlockedCompareExchange_acq(volatile uint32_t *dest, uint64_t xchg, uint64_t comp);
+uint64_t _InterlockedCompareExchange64_rel(volatile uint64_t *dest, uint64_t xchg, uint64_t comp);
+u64_t _InterlockedCompareExchange64_acq(volatile uint64_t *dest, uint64_t xchg, uint64_t comp);
+
+#define ia64_cmpxchg1_rel _InterlockedCompareExchange8_rel
+#define ia64_cmpxchg1_acq _InterlockedCompareExchange8_acq
+#define ia64_cmpxchg2_rel _InterlockedCompareExchange16_rel
+#define ia64_cmpxchg2_acq _InterlockedCompareExchange16_acq
+#define ia64_cmpxchg4_rel _InterlockedCompareExchange_rel
+#define ia64_cmpxchg4_acq _InterlockedCompareExchange_acq
+#define ia64_cmpxchg8_rel _InterlockedCompareExchange64_rel
+#define ia64_cmpxchg8_acq _InterlockedCompareExchange64_acq
+
+#define ia64_srlz_d __dsrlz
+#define ia64_srlz_i __isrlz
+#define __ia64_fc __fc
+#define ia64_sync_i __synci
+#define __ia64_getreg __getReg
+#else /* __INTEL_COMPILER */
+#define ia64_cmpxchg1_acq(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ asm volatile ("cmpxchg1.acq %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg1_rel(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ asm volatile ("cmpxchg1.rel %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg2_acq(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ asm volatile ("cmpxchg2.acq %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg2_rel(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ \
+ asm volatile ("cmpxchg2.rel %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg4_acq(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ asm volatile ("cmpxchg4.acq %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg4_rel(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ asm volatile ("cmpxchg4.rel %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg8_acq(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ asm volatile ("cmpxchg8.acq %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_cmpxchg8_rel(ptr, new, old) \
+({ \
+ uint64_t ia64_intri_res; \
+ asm volatile ("mov ar.ccv=%0;;" :: "rO"(old)); \
+ \
+ asm volatile ("cmpxchg8.rel %0=[%1],%2,ar.ccv": \
+ "=r"(ia64_intri_res) : "r"(ptr), "r"(new) : "memory"); \
+ ia64_intri_res; \
+})
+
+#define ia64_srlz_i() asm volatile (";; srlz.i ;;" ::: "memory")
+#define ia64_srlz_d() asm volatile (";; srlz.d" ::: "memory");
+#define __ia64_fc(addr) asm volatile ("fc %0" :: "r"(addr) : "memory")
+#define ia64_sync_i() asm volatile (";; sync.i" ::: "memory")
+
+#endif /* __INTEL_COMPILER */
+#endif /* IA64_INTRINSIC_H */
diff --git a/input.c b/input.c
new file mode 100644
index 0000000..ec05548
--- /dev/null
+++ b/input.c
@@ -0,0 +1,288 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysemu.h"
+#include "net.h"
+#include "monitor.h"
+#include "console.h"
+#include "qjson.h"
+
+static QEMUPutKBDEvent *qemu_put_kbd_event;
+static void *qemu_put_kbd_event_opaque;
+static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = QTAILQ_HEAD_INITIALIZER(led_handlers);
+static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
+ QTAILQ_HEAD_INITIALIZER(mouse_handlers);
+static NotifierList mouse_mode_notifiers =
+ NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
+
+void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+{
+ qemu_put_kbd_event_opaque = opaque;
+ qemu_put_kbd_event = func;
+}
+
+void qemu_remove_kbd_event_handler(void)
+{
+ qemu_put_kbd_event_opaque = NULL;
+ qemu_put_kbd_event = NULL;
+}
+
+static void check_mode_change(void)
+{
+ static int current_is_absolute, current_has_absolute;
+ int is_absolute;
+ int has_absolute;
+
+ is_absolute = kbd_mouse_is_absolute();
+ has_absolute = kbd_mouse_has_absolute();
+
+ if (is_absolute != current_is_absolute ||
+ has_absolute != current_has_absolute) {
+ notifier_list_notify(&mouse_mode_notifiers);
+ }
+
+ current_is_absolute = is_absolute;
+ current_has_absolute = has_absolute;
+}
+
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+ void *opaque, int absolute,
+ const char *name)
+{
+ QEMUPutMouseEntry *s;
+ static int mouse_index = 0;
+
+ s = qemu_mallocz(sizeof(QEMUPutMouseEntry));
+
+ s->qemu_put_mouse_event = func;
+ s->qemu_put_mouse_event_opaque = opaque;
+ s->qemu_put_mouse_event_absolute = absolute;
+ s->qemu_put_mouse_event_name = qemu_strdup(name);
+ s->index = mouse_index++;
+
+ QTAILQ_INSERT_TAIL(&mouse_handlers, s, node);
+
+ check_mode_change();
+
+ return s;
+}
+
+void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+ QTAILQ_REMOVE(&mouse_handlers, entry, node);
+ QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node);
+
+ check_mode_change();
+}
+
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+ QTAILQ_REMOVE(&mouse_handlers, entry, node);
+
+ qemu_free(entry->qemu_put_mouse_event_name);
+ qemu_free(entry);
+
+ check_mode_change();
+}
+
+QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
+ void *opaque)
+{
+ QEMUPutLEDEntry *s;
+
+ s = qemu_mallocz(sizeof(QEMUPutLEDEntry));
+
+ s->put_led = func;
+ s->opaque = opaque;
+ QTAILQ_INSERT_TAIL(&led_handlers, s, next);
+ return s;
+}
+
+void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
+{
+ if (entry == NULL)
+ return;
+ QTAILQ_REMOVE(&led_handlers, entry, next);
+ qemu_free(entry);
+}
+
+void kbd_put_keycode(int keycode)
+{
+ if (qemu_put_kbd_event) {
+ qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode);
+ }
+}
+
+void kbd_put_ledstate(int ledstate)
+{
+ QEMUPutLEDEntry *cursor;
+
+ QTAILQ_FOREACH(cursor, &led_handlers, next) {
+ cursor->put_led(cursor->opaque, ledstate);
+ }
+}
+
+void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
+{
+ QEMUPutMouseEntry *entry;
+ QEMUPutMouseEvent *mouse_event;
+ void *mouse_event_opaque;
+ int width;
+
+ if (QTAILQ_EMPTY(&mouse_handlers)) {
+ return;
+ }
+
+ entry = QTAILQ_FIRST(&mouse_handlers);
+
+ mouse_event = entry->qemu_put_mouse_event;
+ mouse_event_opaque = entry->qemu_put_mouse_event_opaque;
+
+ if (mouse_event) {
+ if (graphic_rotate) {
+ if (entry->qemu_put_mouse_event_absolute)
+ width = 0x7fff;
+ else
+ width = graphic_width - 1;
+ mouse_event(mouse_event_opaque,
+ width - dy, dx, dz, buttons_state);
+ } else
+ mouse_event(mouse_event_opaque,
+ dx, dy, dz, buttons_state);
+ }
+}
+
+int kbd_mouse_is_absolute(void)
+{
+ if (QTAILQ_EMPTY(&mouse_handlers)) {
+ return 0;
+ }
+
+ return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute;
+}
+
+int kbd_mouse_has_absolute(void)
+{
+ QEMUPutMouseEntry *entry;
+
+ QTAILQ_FOREACH(entry, &mouse_handlers, node) {
+ if (entry->qemu_put_mouse_event_absolute) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void info_mice_iter(QObject *data, void *opaque)
+{
+ QDict *mouse;
+ Monitor *mon = opaque;
+
+ mouse = qobject_to_qdict(data);
+ monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n",
+ (qdict_get_bool(mouse, "current") ? '*' : ' '),
+ qdict_get_int(mouse, "index"), qdict_get_str(mouse, "name"),
+ qdict_get_bool(mouse, "absolute") ? " (absolute)" : "");
+}
+
+void do_info_mice_print(Monitor *mon, const QObject *data)
+{
+ QList *mice_list;
+
+ mice_list = qobject_to_qlist(data);
+ if (qlist_empty(mice_list)) {
+ monitor_printf(mon, "No mouse devices connected\n");
+ return;
+ }
+
+ qlist_iter(mice_list, info_mice_iter, mon);
+}
+
+void do_info_mice(Monitor *mon, QObject **ret_data)
+{
+ QEMUPutMouseEntry *cursor;
+ QList *mice_list;
+ int current;
+
+ mice_list = qlist_new();
+
+ if (QTAILQ_EMPTY(&mouse_handlers)) {
+ goto out;
+ }
+
+ current = QTAILQ_FIRST(&mouse_handlers)->index;
+
+ QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
+ QObject *obj;
+ obj = qobject_from_jsonf("{ 'name': %s,"
+ " 'index': %d,"
+ " 'current': %i,"
+ " 'absolute': %i }",
+ cursor->qemu_put_mouse_event_name,
+ cursor->index,
+ cursor->index == current,
+ !!cursor->qemu_put_mouse_event_absolute);
+ qlist_append_obj(mice_list, obj);
+ }
+
+out:
+ *ret_data = QOBJECT(mice_list);
+}
+
+void do_mouse_set(Monitor *mon, const QDict *qdict)
+{
+ QEMUPutMouseEntry *cursor;
+ int index = qdict_get_int(qdict, "index");
+ int found = 0;
+
+ if (QTAILQ_EMPTY(&mouse_handlers)) {
+ monitor_printf(mon, "No mouse devices connected\n");
+ return;
+ }
+
+ QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
+ if (cursor->index == index) {
+ found = 1;
+ qemu_activate_mouse_event_handler(cursor);
+ break;
+ }
+ }
+
+ if (!found) {
+ monitor_printf(mon, "Mouse at given index not found\n");
+ }
+
+ check_mode_change();
+}
+
+void qemu_add_mouse_mode_change_notifier(Notifier *notify)
+{
+ notifier_list_add(&mouse_mode_notifiers, notify);
+}
+
+void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
+{
+ notifier_list_remove(&mouse_mode_notifiers, notify);
+}
diff --git a/ioport-user.c b/ioport-user.c
new file mode 100644
index 0000000..03fac22
--- /dev/null
+++ b/ioport-user.c
@@ -0,0 +1,60 @@
+/*
+ * qemu user ioport functions
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdio.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+#include "ioport.h"
+
+void cpu_outb(pio_addr_t addr, uint8_t val)
+{
+ fprintf(stderr, "outb: port=0x%04"FMT_pioaddr", data=%02"PRIx8"\n",
+ addr, val);
+}
+
+void cpu_outw(pio_addr_t addr, uint16_t val)
+{
+ fprintf(stderr, "outw: port=0x%04"FMT_pioaddr", data=%04"PRIx16"\n",
+ addr, val);
+}
+
+void cpu_outl(pio_addr_t addr, uint32_t val)
+{
+ fprintf(stderr, "outl: port=0x%04"FMT_pioaddr", data=%08"PRIx32"\n",
+ addr, val);
+}
+
+uint8_t cpu_inb(pio_addr_t addr)
+{
+ fprintf(stderr, "inb: port=0x%04"FMT_pioaddr"\n", addr);
+ return 0;
+}
+
+uint16_t cpu_inw(pio_addr_t addr)
+{
+ fprintf(stderr, "inw: port=0x%04"FMT_pioaddr"\n", addr);
+ return 0;
+}
+
+uint32_t cpu_inl(pio_addr_t addr)
+{
+ fprintf(stderr, "inl: port=0x%04"FMT_pioaddr"\n", addr);
+ return 0;
+}
diff --git a/ioport.c b/ioport.c
new file mode 100644
index 0000000..aa4188a
--- /dev/null
+++ b/ioport.c
@@ -0,0 +1,306 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * splitted out ioport related stuffs from vl.c.
+ */
+
+#include "ioport.h"
+#include "trace.h"
+
+/***********************************************************/
+/* IO Port */
+
+//#define DEBUG_UNUSED_IOPORT
+//#define DEBUG_IOPORT
+
+#ifdef DEBUG_UNUSED_IOPORT
+# define LOG_UNUSED_IOPORT(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#else
+# define LOG_UNUSED_IOPORT(fmt, ...) do{ } while (0)
+#endif
+
+#ifdef DEBUG_IOPORT
+# define LOG_IOPORT(...) qemu_log_mask(CPU_LOG_IOPORT, ## __VA_ARGS__)
+#else
+# define LOG_IOPORT(...) do { } while (0)
+#endif
+
+/* XXX: use a two level table to limit memory usage */
+
+static void *ioport_opaque[MAX_IOPORTS];
+static IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
+static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
+
+static IOPortReadFunc default_ioport_readb, default_ioport_readw, default_ioport_readl;
+static IOPortWriteFunc default_ioport_writeb, default_ioport_writew, default_ioport_writel;
+
+static uint32_t ioport_read(int index, uint32_t address)
+{
+ static IOPortReadFunc * const default_func[3] = {
+ default_ioport_readb,
+ default_ioport_readw,
+ default_ioport_readl
+ };
+ IOPortReadFunc *func = ioport_read_table[index][address];
+ if (!func)
+ func = default_func[index];
+ return func(ioport_opaque[address], address);
+}
+
+static void ioport_write(int index, uint32_t address, uint32_t data)
+{
+ static IOPortWriteFunc * const default_func[3] = {
+ default_ioport_writeb,
+ default_ioport_writew,
+ default_ioport_writel
+ };
+ IOPortWriteFunc *func = ioport_write_table[index][address];
+ if (!func)
+ func = default_func[index];
+ func(ioport_opaque[address], address, data);
+}
+
+static uint32_t default_ioport_readb(void *opaque, uint32_t address)
+{
+ LOG_UNUSED_IOPORT("unused inb: port=0x%04"PRIx32"\n", address);
+ return 0xff;
+}
+
+static void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data)
+{
+ LOG_UNUSED_IOPORT("unused outb: port=0x%04"PRIx32" data=0x%02"PRIx32"\n",
+ address, data);
+}
+
+/* default is to make two byte accesses */
+static uint32_t default_ioport_readw(void *opaque, uint32_t address)
+{
+ uint32_t data;
+ data = ioport_read(0, address);
+ address = (address + 1) & IOPORTS_MASK;
+ data |= ioport_read(0, address) << 8;
+ return data;
+}
+
+static void default_ioport_writew(void *opaque, uint32_t address, uint32_t data)
+{
+ ioport_write(0, address, data & 0xff);
+ address = (address + 1) & IOPORTS_MASK;
+ ioport_write(0, address, (data >> 8) & 0xff);
+}
+
+static uint32_t default_ioport_readl(void *opaque, uint32_t address)
+{
+ LOG_UNUSED_IOPORT("unused inl: port=0x%04"PRIx32"\n", address);
+ return 0xffffffff;
+}
+
+static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data)
+{
+ LOG_UNUSED_IOPORT("unused outl: port=0x%04"PRIx32" data=0x%02"PRIx32"\n",
+ address, data);
+}
+
+static int ioport_bsize(int size, int *bsize)
+{
+ if (size == 1) {
+ *bsize = 0;
+ } else if (size == 2) {
+ *bsize = 1;
+ } else if (size == 4) {
+ *bsize = 2;
+ } else {
+ return -1;
+ }
+ return 0;
+}
+
+/* size is the word size in byte */
+int register_ioport_read(pio_addr_t start, int length, int size,
+ IOPortReadFunc *func, void *opaque)
+{
+ int i, bsize;
+
+ if (ioport_bsize(size, &bsize)) {
+ hw_error("register_ioport_read: invalid size");
+ return -1;
+ }
+ for(i = start; i < start + length; i += size) {
+ ioport_read_table[bsize][i] = func;
+ if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
+ hw_error("register_ioport_read: invalid opaque");
+ ioport_opaque[i] = opaque;
+ }
+ return 0;
+}
+
+/* size is the word size in byte */
+int register_ioport_write(pio_addr_t start, int length, int size,
+ IOPortWriteFunc *func, void *opaque)
+{
+ int i, bsize;
+
+ if (ioport_bsize(size, &bsize)) {
+ hw_error("register_ioport_write: invalid size");
+ return -1;
+ }
+ for(i = start; i < start + length; i += size) {
+ ioport_write_table[bsize][i] = func;
+ if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
+ hw_error("register_ioport_write: invalid opaque");
+ ioport_opaque[i] = opaque;
+ }
+ return 0;
+}
+
+static uint32_t ioport_readb_thunk(void *opaque, uint32_t addr)
+{
+ IORange *ioport = opaque;
+ uint64_t data;
+
+ ioport->ops->read(ioport, addr - ioport->base, 1, &data);
+ return data;
+}
+
+static uint32_t ioport_readw_thunk(void *opaque, uint32_t addr)
+{
+ IORange *ioport = opaque;
+ uint64_t data;
+
+ ioport->ops->read(ioport, addr - ioport->base, 2, &data);
+ return data;
+}
+
+static uint32_t ioport_readl_thunk(void *opaque, uint32_t addr)
+{
+ IORange *ioport = opaque;
+ uint64_t data;
+
+ ioport->ops->read(ioport, addr - ioport->base, 4, &data);
+ return data;
+}
+
+static void ioport_writeb_thunk(void *opaque, uint32_t addr, uint32_t data)
+{
+ IORange *ioport = opaque;
+
+ ioport->ops->write(ioport, addr - ioport->base, 1, data);
+}
+
+static void ioport_writew_thunk(void *opaque, uint32_t addr, uint32_t data)
+{
+ IORange *ioport = opaque;
+
+ ioport->ops->write(ioport, addr - ioport->base, 2, data);
+}
+
+static void ioport_writel_thunk(void *opaque, uint32_t addr, uint32_t data)
+{
+ IORange *ioport = opaque;
+
+ ioport->ops->write(ioport, addr - ioport->base, 4, data);
+}
+
+void ioport_register(IORange *ioport)
+{
+ register_ioport_read(ioport->base, ioport->len, 1,
+ ioport_readb_thunk, ioport);
+ register_ioport_read(ioport->base, ioport->len, 2,
+ ioport_readw_thunk, ioport);
+ register_ioport_read(ioport->base, ioport->len, 4,
+ ioport_readl_thunk, ioport);
+ register_ioport_write(ioport->base, ioport->len, 1,
+ ioport_writeb_thunk, ioport);
+ register_ioport_write(ioport->base, ioport->len, 2,
+ ioport_writew_thunk, ioport);
+ register_ioport_write(ioport->base, ioport->len, 4,
+ ioport_writel_thunk, ioport);
+}
+
+void isa_unassign_ioport(pio_addr_t start, int length)
+{
+ int i;
+
+ for(i = start; i < start + length; i++) {
+ ioport_read_table[0][i] = default_ioport_readb;
+ ioport_read_table[1][i] = default_ioport_readw;
+ ioport_read_table[2][i] = default_ioport_readl;
+
+ ioport_write_table[0][i] = default_ioport_writeb;
+ ioport_write_table[1][i] = default_ioport_writew;
+ ioport_write_table[2][i] = default_ioport_writel;
+
+ ioport_opaque[i] = NULL;
+ }
+}
+
+/***********************************************************/
+
+void cpu_outb(pio_addr_t addr, uint8_t val)
+{
+ LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
+ trace_cpu_out(addr, val);
+ ioport_write(0, addr, val);
+}
+
+void cpu_outw(pio_addr_t addr, uint16_t val)
+{
+ LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
+ trace_cpu_out(addr, val);
+ ioport_write(1, addr, val);
+}
+
+void cpu_outl(pio_addr_t addr, uint32_t val)
+{
+ LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
+ trace_cpu_out(addr, val);
+ ioport_write(2, addr, val);
+}
+
+uint8_t cpu_inb(pio_addr_t addr)
+{
+ uint8_t val;
+ val = ioport_read(0, addr);
+ trace_cpu_in(addr, val);
+ LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
+ return val;
+}
+
+uint16_t cpu_inw(pio_addr_t addr)
+{
+ uint16_t val;
+ val = ioport_read(1, addr);
+ trace_cpu_in(addr, val);
+ LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
+ return val;
+}
+
+uint32_t cpu_inl(pio_addr_t addr)
+{
+ uint32_t val;
+ val = ioport_read(2, addr);
+ trace_cpu_in(addr, val);
+ LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
+ return val;
+}
diff --git a/ioport.h b/ioport.h
new file mode 100644
index 0000000..5ae62a3
--- /dev/null
+++ b/ioport.h
@@ -0,0 +1,55 @@
+/*
+ * defines ioport related functions
+ *
+ * Copyright (c) 2003 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/>.
+ */
+
+/**************************************************************************
+ * IO ports API
+ */
+
+#ifndef IOPORT_H
+#define IOPORT_H
+
+#include "qemu-common.h"
+#include "iorange.h"
+
+typedef uint32_t pio_addr_t;
+#define FMT_pioaddr PRIx32
+
+#define MAX_IOPORTS (64 * 1024)
+#define IOPORTS_MASK (MAX_IOPORTS - 1)
+
+/* These should really be in isa.h, but are here to make pc.h happy. */
+typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data);
+typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address);
+
+void ioport_register(IORange *iorange);
+int register_ioport_read(pio_addr_t start, int length, int size,
+ IOPortReadFunc *func, void *opaque);
+int register_ioport_write(pio_addr_t start, int length, int size,
+ IOPortWriteFunc *func, void *opaque);
+void isa_unassign_ioport(pio_addr_t start, int length);
+
+
+void cpu_outb(pio_addr_t addr, uint8_t val);
+void cpu_outw(pio_addr_t addr, uint16_t val);
+void cpu_outl(pio_addr_t addr, uint32_t val);
+uint8_t cpu_inb(pio_addr_t addr);
+uint16_t cpu_inw(pio_addr_t addr);
+uint32_t cpu_inl(pio_addr_t addr);
+
+#endif /* IOPORT_H */
diff --git a/iorange.h b/iorange.h
new file mode 100644
index 0000000..9783168
--- /dev/null
+++ b/iorange.h
@@ -0,0 +1,30 @@
+#ifndef IORANGE_H
+#define IORANGE_H
+
+#include <stdint.h>
+
+typedef struct IORange IORange;
+typedef struct IORangeOps IORangeOps;
+
+struct IORangeOps {
+ void (*read)(IORange *iorange, uint64_t offset, unsigned width,
+ uint64_t *data);
+ void (*write)(IORange *iorange, uint64_t offset, unsigned width,
+ uint64_t data);
+};
+
+struct IORange {
+ const IORangeOps *ops;
+ uint64_t base;
+ uint64_t len;
+};
+
+static inline void iorange_init(IORange *iorange, const IORangeOps *ops,
+ uint64_t base, uint64_t len)
+{
+ iorange->ops = ops;
+ iorange->base = base;
+ iorange->len = len;
+}
+
+#endif
diff --git a/iov.c b/iov.c
new file mode 100644
index 0000000..588cd04
--- /dev/null
+++ b/iov.c
@@ -0,0 +1,70 @@
+/*
+ * Helpers for getting linearized buffers from iov / filling buffers into iovs
+ *
+ * Copyright IBM, Corp. 2007, 2008
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author(s):
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "iov.h"
+
+size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt,
+ const void *buf, size_t size)
+{
+ size_t offset;
+ unsigned int i;
+
+ offset = 0;
+ for (i = 0; offset < size && i < iovcnt; i++) {
+ size_t len;
+
+ len = MIN(iov[i].iov_len, size - offset);
+
+ memcpy(iov[i].iov_base, buf + offset, len);
+ offset += len;
+ }
+ return offset;
+}
+
+size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
+ void *buf, size_t offset, size_t size)
+{
+ uint8_t *ptr;
+ size_t iov_off, buf_off;
+ unsigned int i;
+
+ ptr = buf;
+ iov_off = 0;
+ buf_off = 0;
+ for (i = 0; i < iovcnt && size; i++) {
+ if (offset < (iov_off + iov[i].iov_len)) {
+ size_t len = MIN((iov_off + iov[i].iov_len) - offset , size);
+
+ memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len);
+
+ buf_off += len;
+ offset += len;
+ size -= len;
+ }
+ iov_off += iov[i].iov_len;
+ }
+ return buf_off;
+}
+
+size_t iov_size(const struct iovec *iov, const unsigned int iovcnt)
+{
+ size_t len;
+ unsigned int i;
+
+ len = 0;
+ for (i = 0; i < iovcnt; i++) {
+ len += iov[i].iov_len;
+ }
+ return len;
+}
diff --git a/iov.h b/iov.h
new file mode 100644
index 0000000..60a8547
--- /dev/null
+++ b/iov.h
@@ -0,0 +1,19 @@
+/*
+ * Helpers for getting linearized buffers from iov / filling buffers into iovs
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author(s):
+ * Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+
+size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt,
+ const void *buf, size_t size);
+size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt,
+ void *buf, size_t offset, size_t size);
+size_t iov_size(const struct iovec *iov, const unsigned int iovcnt);
diff --git a/json-lexer.c b/json-lexer.c
new file mode 100644
index 0000000..c736f42
--- /dev/null
+++ b/json-lexer.c
@@ -0,0 +1,339 @@
+/*
+ * JSON lexer
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qstring.h"
+#include "qlist.h"
+#include "qdict.h"
+#include "qint.h"
+#include "qemu-common.h"
+#include "json-lexer.h"
+
+/*
+ * \"([^\\\"]|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*\"
+ * '([^\\']|(\\\"\\'\\\\\\/\\b\\f\\n\\r\\t\\u[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]))*'
+ * 0|([1-9][0-9]*(.[0-9]+)?([eE]([-+])?[0-9]+))
+ * [{}\[\],:]
+ * [a-z]+
+ *
+ */
+
+enum json_lexer_state {
+ ERROR = 0,
+ IN_DQ_UCODE3,
+ IN_DQ_UCODE2,
+ IN_DQ_UCODE1,
+ IN_DQ_UCODE0,
+ IN_DQ_STRING_ESCAPE,
+ IN_DQ_STRING,
+ IN_SQ_UCODE3,
+ IN_SQ_UCODE2,
+ IN_SQ_UCODE1,
+ IN_SQ_UCODE0,
+ IN_SQ_STRING_ESCAPE,
+ IN_SQ_STRING,
+ IN_ZERO,
+ IN_DIGITS,
+ IN_DIGIT,
+ IN_EXP_E,
+ IN_MANTISSA,
+ IN_MANTISSA_DIGITS,
+ IN_NONZERO_NUMBER,
+ IN_NEG_NONZERO_NUMBER,
+ IN_KEYWORD,
+ IN_ESCAPE,
+ IN_ESCAPE_L,
+ IN_ESCAPE_LL,
+ IN_ESCAPE_I,
+ IN_ESCAPE_I6,
+ IN_ESCAPE_I64,
+ IN_WHITESPACE,
+ IN_START,
+};
+
+#define TERMINAL(state) [0 ... 0x7F] = (state)
+
+/* Return whether TERMINAL is a terminal state and the transition to it
+ from OLD_STATE required lookahead. This happens whenever the table
+ below uses the TERMINAL macro. */
+#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \
+ (json_lexer[(old_state)][0] == (terminal))
+
+static const uint8_t json_lexer[][256] = {
+ /* double quote string */
+ [IN_DQ_UCODE3] = {
+ ['0' ... '9'] = IN_DQ_STRING,
+ ['a' ... 'f'] = IN_DQ_STRING,
+ ['A' ... 'F'] = IN_DQ_STRING,
+ },
+ [IN_DQ_UCODE2] = {
+ ['0' ... '9'] = IN_DQ_UCODE3,
+ ['a' ... 'f'] = IN_DQ_UCODE3,
+ ['A' ... 'F'] = IN_DQ_UCODE3,
+ },
+ [IN_DQ_UCODE1] = {
+ ['0' ... '9'] = IN_DQ_UCODE2,
+ ['a' ... 'f'] = IN_DQ_UCODE2,
+ ['A' ... 'F'] = IN_DQ_UCODE2,
+ },
+ [IN_DQ_UCODE0] = {
+ ['0' ... '9'] = IN_DQ_UCODE1,
+ ['a' ... 'f'] = IN_DQ_UCODE1,
+ ['A' ... 'F'] = IN_DQ_UCODE1,
+ },
+ [IN_DQ_STRING_ESCAPE] = {
+ ['b'] = IN_DQ_STRING,
+ ['f'] = IN_DQ_STRING,
+ ['n'] = IN_DQ_STRING,
+ ['r'] = IN_DQ_STRING,
+ ['t'] = IN_DQ_STRING,
+ ['/'] = IN_DQ_STRING,
+ ['\\'] = IN_DQ_STRING,
+ ['\''] = IN_DQ_STRING,
+ ['\"'] = IN_DQ_STRING,
+ ['u'] = IN_DQ_UCODE0,
+ },
+ [IN_DQ_STRING] = {
+ [1 ... 0xFF] = IN_DQ_STRING,
+ ['\\'] = IN_DQ_STRING_ESCAPE,
+ ['"'] = JSON_STRING,
+ },
+
+ /* single quote string */
+ [IN_SQ_UCODE3] = {
+ ['0' ... '9'] = IN_SQ_STRING,
+ ['a' ... 'f'] = IN_SQ_STRING,
+ ['A' ... 'F'] = IN_SQ_STRING,
+ },
+ [IN_SQ_UCODE2] = {
+ ['0' ... '9'] = IN_SQ_UCODE3,
+ ['a' ... 'f'] = IN_SQ_UCODE3,
+ ['A' ... 'F'] = IN_SQ_UCODE3,
+ },
+ [IN_SQ_UCODE1] = {
+ ['0' ... '9'] = IN_SQ_UCODE2,
+ ['a' ... 'f'] = IN_SQ_UCODE2,
+ ['A' ... 'F'] = IN_SQ_UCODE2,
+ },
+ [IN_SQ_UCODE0] = {
+ ['0' ... '9'] = IN_SQ_UCODE1,
+ ['a' ... 'f'] = IN_SQ_UCODE1,
+ ['A' ... 'F'] = IN_SQ_UCODE1,
+ },
+ [IN_SQ_STRING_ESCAPE] = {
+ ['b'] = IN_SQ_STRING,
+ ['f'] = IN_SQ_STRING,
+ ['n'] = IN_SQ_STRING,
+ ['r'] = IN_SQ_STRING,
+ ['t'] = IN_SQ_STRING,
+ ['/'] = IN_DQ_STRING,
+ ['\\'] = IN_DQ_STRING,
+ ['\''] = IN_SQ_STRING,
+ ['\"'] = IN_SQ_STRING,
+ ['u'] = IN_SQ_UCODE0,
+ },
+ [IN_SQ_STRING] = {
+ [1 ... 0xFF] = IN_SQ_STRING,
+ ['\\'] = IN_SQ_STRING_ESCAPE,
+ ['\''] = JSON_STRING,
+ },
+
+ /* Zero */
+ [IN_ZERO] = {
+ TERMINAL(JSON_INTEGER),
+ ['0' ... '9'] = ERROR,
+ ['.'] = IN_MANTISSA,
+ },
+
+ /* Float */
+ [IN_DIGITS] = {
+ TERMINAL(JSON_FLOAT),
+ ['0' ... '9'] = IN_DIGITS,
+ },
+
+ [IN_DIGIT] = {
+ ['0' ... '9'] = IN_DIGITS,
+ },
+
+ [IN_EXP_E] = {
+ ['-'] = IN_DIGIT,
+ ['+'] = IN_DIGIT,
+ ['0' ... '9'] = IN_DIGITS,
+ },
+
+ [IN_MANTISSA_DIGITS] = {
+ TERMINAL(JSON_FLOAT),
+ ['0' ... '9'] = IN_MANTISSA_DIGITS,
+ ['e'] = IN_EXP_E,
+ ['E'] = IN_EXP_E,
+ },
+
+ [IN_MANTISSA] = {
+ ['0' ... '9'] = IN_MANTISSA_DIGITS,
+ },
+
+ /* Number */
+ [IN_NONZERO_NUMBER] = {
+ TERMINAL(JSON_INTEGER),
+ ['0' ... '9'] = IN_NONZERO_NUMBER,
+ ['e'] = IN_EXP_E,
+ ['E'] = IN_EXP_E,
+ ['.'] = IN_MANTISSA,
+ },
+
+ [IN_NEG_NONZERO_NUMBER] = {
+ ['0'] = IN_ZERO,
+ ['1' ... '9'] = IN_NONZERO_NUMBER,
+ },
+
+ /* keywords */
+ [IN_KEYWORD] = {
+ TERMINAL(JSON_KEYWORD),
+ ['a' ... 'z'] = IN_KEYWORD,
+ },
+
+ /* whitespace */
+ [IN_WHITESPACE] = {
+ TERMINAL(JSON_SKIP),
+ [' '] = IN_WHITESPACE,
+ ['\t'] = IN_WHITESPACE,
+ ['\r'] = IN_WHITESPACE,
+ ['\n'] = IN_WHITESPACE,
+ },
+
+ /* escape */
+ [IN_ESCAPE_LL] = {
+ ['d'] = JSON_ESCAPE,
+ },
+
+ [IN_ESCAPE_L] = {
+ ['d'] = JSON_ESCAPE,
+ ['l'] = IN_ESCAPE_LL,
+ },
+
+ [IN_ESCAPE_I64] = {
+ ['d'] = JSON_ESCAPE,
+ },
+
+ [IN_ESCAPE_I6] = {
+ ['4'] = IN_ESCAPE_I64,
+ },
+
+ [IN_ESCAPE_I] = {
+ ['6'] = IN_ESCAPE_I6,
+ },
+
+ [IN_ESCAPE] = {
+ ['d'] = JSON_ESCAPE,
+ ['i'] = JSON_ESCAPE,
+ ['p'] = JSON_ESCAPE,
+ ['s'] = JSON_ESCAPE,
+ ['f'] = JSON_ESCAPE,
+ ['l'] = IN_ESCAPE_L,
+ ['I'] = IN_ESCAPE_I,
+ },
+
+ /* top level rule */
+ [IN_START] = {
+ ['"'] = IN_DQ_STRING,
+ ['\''] = IN_SQ_STRING,
+ ['0'] = IN_ZERO,
+ ['1' ... '9'] = IN_NONZERO_NUMBER,
+ ['-'] = IN_NEG_NONZERO_NUMBER,
+ ['{'] = JSON_OPERATOR,
+ ['}'] = JSON_OPERATOR,
+ ['['] = JSON_OPERATOR,
+ [']'] = JSON_OPERATOR,
+ [','] = JSON_OPERATOR,
+ [':'] = JSON_OPERATOR,
+ ['a' ... 'z'] = IN_KEYWORD,
+ ['%'] = IN_ESCAPE,
+ [' '] = IN_WHITESPACE,
+ ['\t'] = IN_WHITESPACE,
+ ['\r'] = IN_WHITESPACE,
+ ['\n'] = IN_WHITESPACE,
+ },
+};
+
+void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func)
+{
+ lexer->emit = func;
+ lexer->state = IN_START;
+ lexer->token = qstring_new();
+ lexer->x = lexer->y = 0;
+}
+
+static int json_lexer_feed_char(JSONLexer *lexer, char ch)
+{
+ int char_consumed, new_state;
+
+ lexer->x++;
+ if (ch == '\n') {
+ lexer->x = 0;
+ lexer->y++;
+ }
+
+ do {
+ new_state = json_lexer[lexer->state][(uint8_t)ch];
+ char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state);
+ if (char_consumed) {
+ qstring_append_chr(lexer->token, ch);
+ }
+
+ switch (new_state) {
+ case JSON_OPERATOR:
+ case JSON_ESCAPE:
+ case JSON_INTEGER:
+ case JSON_FLOAT:
+ case JSON_KEYWORD:
+ case JSON_STRING:
+ lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y);
+ case JSON_SKIP:
+ QDECREF(lexer->token);
+ lexer->token = qstring_new();
+ new_state = IN_START;
+ break;
+ case ERROR:
+ return -EINVAL;
+ default:
+ break;
+ }
+ lexer->state = new_state;
+ } while (!char_consumed);
+ return 0;
+}
+
+int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ int err;
+
+ err = json_lexer_feed_char(lexer, buffer[i]);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int json_lexer_flush(JSONLexer *lexer)
+{
+ return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0);
+}
+
+void json_lexer_destroy(JSONLexer *lexer)
+{
+ QDECREF(lexer->token);
+}
diff --git a/json-lexer.h b/json-lexer.h
new file mode 100644
index 0000000..3b50c46
--- /dev/null
+++ b/json-lexer.h
@@ -0,0 +1,50 @@
+/*
+ * JSON lexer
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_JSON_LEXER_H
+#define QEMU_JSON_LEXER_H
+
+#include "qstring.h"
+#include "qlist.h"
+
+typedef enum json_token_type {
+ JSON_OPERATOR = 100,
+ JSON_INTEGER,
+ JSON_FLOAT,
+ JSON_KEYWORD,
+ JSON_STRING,
+ JSON_ESCAPE,
+ JSON_SKIP,
+} JSONTokenType;
+
+typedef struct JSONLexer JSONLexer;
+
+typedef void (JSONLexerEmitter)(JSONLexer *, QString *, JSONTokenType, int x, int y);
+
+struct JSONLexer
+{
+ JSONLexerEmitter *emit;
+ int state;
+ QString *token;
+ int x, y;
+};
+
+void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func);
+
+int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size);
+
+int json_lexer_flush(JSONLexer *lexer);
+
+void json_lexer_destroy(JSONLexer *lexer);
+
+#endif
diff --git a/json-parser.c b/json-parser.c
new file mode 100644
index 0000000..6c06ef9
--- /dev/null
+++ b/json-parser.c
@@ -0,0 +1,577 @@
+/*
+ * JSON Parser
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <stdarg.h>
+
+#include "qemu-common.h"
+#include "qstring.h"
+#include "qint.h"
+#include "qdict.h"
+#include "qlist.h"
+#include "qfloat.h"
+#include "qbool.h"
+#include "json-parser.h"
+#include "json-lexer.h"
+
+typedef struct JSONParserContext
+{
+} JSONParserContext;
+
+#define BUG_ON(cond) assert(!(cond))
+
+/**
+ * TODO
+ *
+ * 0) make errors meaningful again
+ * 1) add geometry information to tokens
+ * 3) should we return a parsed size?
+ * 4) deal with premature EOI
+ */
+
+static QObject *parse_value(JSONParserContext *ctxt, QList **tokens, va_list *ap);
+
+/**
+ * Token manipulators
+ *
+ * tokens are dictionaries that contain a type, a string value, and geometry information
+ * about a token identified by the lexer. These are routines that make working with
+ * these objects a bit easier.
+ */
+static const char *token_get_value(QObject *obj)
+{
+ return qdict_get_str(qobject_to_qdict(obj), "token");
+}
+
+static JSONTokenType token_get_type(QObject *obj)
+{
+ return qdict_get_int(qobject_to_qdict(obj), "type");
+}
+
+static int token_is_operator(QObject *obj, char op)
+{
+ const char *val;
+
+ if (token_get_type(obj) != JSON_OPERATOR) {
+ return 0;
+ }
+
+ val = token_get_value(obj);
+
+ return (val[0] == op) && (val[1] == 0);
+}
+
+static int token_is_keyword(QObject *obj, const char *value)
+{
+ if (token_get_type(obj) != JSON_KEYWORD) {
+ return 0;
+ }
+
+ return strcmp(token_get_value(obj), value) == 0;
+}
+
+static int token_is_escape(QObject *obj, const char *value)
+{
+ if (token_get_type(obj) != JSON_ESCAPE) {
+ return 0;
+ }
+
+ return (strcmp(token_get_value(obj), value) == 0);
+}
+
+/**
+ * Error handler
+ */
+static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
+ QObject *token, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ fprintf(stderr, "parse error: ");
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+/**
+ * String helpers
+ *
+ * These helpers are used to unescape strings.
+ */
+static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length)
+{
+ if (wchar <= 0x007F) {
+ BUG_ON(buffer_length < 2);
+
+ buffer[0] = wchar & 0x7F;
+ buffer[1] = 0;
+ } else if (wchar <= 0x07FF) {
+ BUG_ON(buffer_length < 3);
+
+ buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F);
+ buffer[1] = 0x80 | (wchar & 0x3F);
+ buffer[2] = 0;
+ } else {
+ BUG_ON(buffer_length < 4);
+
+ buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F);
+ buffer[1] = 0x80 | ((wchar >> 6) & 0x3F);
+ buffer[2] = 0x80 | (wchar & 0x3F);
+ buffer[3] = 0;
+ }
+}
+
+static int hex2decimal(char ch)
+{
+ if (ch >= '0' && ch <= '9') {
+ return (ch - '0');
+ } else if (ch >= 'a' && ch <= 'f') {
+ return 10 + (ch - 'a');
+ } else if (ch >= 'A' && ch <= 'F') {
+ return 10 + (ch - 'A');
+ }
+
+ return -1;
+}
+
+/**
+ * parse_string(): Parse a json string and return a QObject
+ *
+ * string
+ * ""
+ * " chars "
+ * chars
+ * char
+ * char chars
+ * char
+ * any-Unicode-character-
+ * except-"-or-\-or-
+ * control-character
+ * \"
+ * \\
+ * \/
+ * \b
+ * \f
+ * \n
+ * \r
+ * \t
+ * \u four-hex-digits
+ */
+static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token)
+{
+ const char *ptr = token_get_value(token);
+ QString *str;
+ int double_quote = 1;
+
+ if (*ptr == '"') {
+ double_quote = 1;
+ } else {
+ double_quote = 0;
+ }
+ ptr++;
+
+ str = qstring_new();
+ while (*ptr &&
+ ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) {
+ if (*ptr == '\\') {
+ ptr++;
+
+ switch (*ptr) {
+ case '"':
+ qstring_append(str, "\"");
+ ptr++;
+ break;
+ case '\'':
+ qstring_append(str, "'");
+ ptr++;
+ break;
+ case '\\':
+ qstring_append(str, "\\");
+ ptr++;
+ break;
+ case '/':
+ qstring_append(str, "/");
+ ptr++;
+ break;
+ case 'b':
+ qstring_append(str, "\b");
+ ptr++;
+ break;
+ case 'f':
+ qstring_append(str, "\f");
+ ptr++;
+ break;
+ case 'n':
+ qstring_append(str, "\n");
+ ptr++;
+ break;
+ case 'r':
+ qstring_append(str, "\r");
+ ptr++;
+ break;
+ case 't':
+ qstring_append(str, "\t");
+ ptr++;
+ break;
+ case 'u': {
+ uint16_t unicode_char = 0;
+ char utf8_char[4];
+ int i = 0;
+
+ ptr++;
+
+ for (i = 0; i < 4; i++) {
+ if (qemu_isxdigit(*ptr)) {
+ unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4);
+ } else {
+ parse_error(ctxt, token,
+ "invalid hex escape sequence in string");
+ goto out;
+ }
+ ptr++;
+ }
+
+ wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char));
+ qstring_append(str, utf8_char);
+ } break;
+ default:
+ parse_error(ctxt, token, "invalid escape sequence in string");
+ goto out;
+ }
+ } else {
+ char dummy[2];
+
+ dummy[0] = *ptr++;
+ dummy[1] = 0;
+
+ qstring_append(str, dummy);
+ }
+ }
+
+ return str;
+
+out:
+ QDECREF(str);
+ return NULL;
+}
+
+/**
+ * Parsing rules
+ */
+static int parse_pair(JSONParserContext *ctxt, QDict *dict, QList **tokens, va_list *ap)
+{
+ QObject *key, *token = NULL, *value, *peek;
+ QList *working = qlist_copy(*tokens);
+
+ peek = qlist_peek(working);
+ key = parse_value(ctxt, &working, ap);
+ if (!key || qobject_type(key) != QTYPE_QSTRING) {
+ parse_error(ctxt, peek, "key is not a string in object");
+ goto out;
+ }
+
+ token = qlist_pop(working);
+ if (!token_is_operator(token, ':')) {
+ parse_error(ctxt, token, "missing : in object pair");
+ goto out;
+ }
+
+ value = parse_value(ctxt, &working, ap);
+ if (value == NULL) {
+ parse_error(ctxt, token, "Missing value in dict");
+ goto out;
+ }
+
+ qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value);
+
+ qobject_decref(token);
+ qobject_decref(key);
+ QDECREF(*tokens);
+ *tokens = working;
+
+ return 0;
+
+out:
+ qobject_decref(token);
+ qobject_decref(key);
+ QDECREF(working);
+
+ return -1;
+}
+
+static QObject *parse_object(JSONParserContext *ctxt, QList **tokens, va_list *ap)
+{
+ QDict *dict = NULL;
+ QObject *token, *peek;
+ QList *working = qlist_copy(*tokens);
+
+ token = qlist_pop(working);
+ if (!token_is_operator(token, '{')) {
+ goto out;
+ }
+ qobject_decref(token);
+ token = NULL;
+
+ dict = qdict_new();
+
+ peek = qlist_peek(working);
+ if (!token_is_operator(peek, '}')) {
+ if (parse_pair(ctxt, dict, &working, ap) == -1) {
+ goto out;
+ }
+
+ token = qlist_pop(working);
+ while (!token_is_operator(token, '}')) {
+ if (!token_is_operator(token, ',')) {
+ parse_error(ctxt, token, "expected separator in dict");
+ goto out;
+ }
+ qobject_decref(token);
+ token = NULL;
+
+ if (parse_pair(ctxt, dict, &working, ap) == -1) {
+ goto out;
+ }
+
+ token = qlist_pop(working);
+ }
+ qobject_decref(token);
+ token = NULL;
+ } else {
+ token = qlist_pop(working);
+ qobject_decref(token);
+ token = NULL;
+ }
+
+ QDECREF(*tokens);
+ *tokens = working;
+
+ return QOBJECT(dict);
+
+out:
+ qobject_decref(token);
+ QDECREF(working);
+ QDECREF(dict);
+ return NULL;
+}
+
+static QObject *parse_array(JSONParserContext *ctxt, QList **tokens, va_list *ap)
+{
+ QList *list = NULL;
+ QObject *token, *peek;
+ QList *working = qlist_copy(*tokens);
+
+ token = qlist_pop(working);
+ if (!token_is_operator(token, '[')) {
+ goto out;
+ }
+ qobject_decref(token);
+ token = NULL;
+
+ list = qlist_new();
+
+ peek = qlist_peek(working);
+ if (!token_is_operator(peek, ']')) {
+ QObject *obj;
+
+ obj = parse_value(ctxt, &working, ap);
+ if (obj == NULL) {
+ parse_error(ctxt, token, "expecting value");
+ goto out;
+ }
+
+ qlist_append_obj(list, obj);
+
+ token = qlist_pop(working);
+ while (!token_is_operator(token, ']')) {
+ if (!token_is_operator(token, ',')) {
+ parse_error(ctxt, token, "expected separator in list");
+ goto out;
+ }
+
+ qobject_decref(token);
+ token = NULL;
+
+ obj = parse_value(ctxt, &working, ap);
+ if (obj == NULL) {
+ parse_error(ctxt, token, "expecting value");
+ goto out;
+ }
+
+ qlist_append_obj(list, obj);
+
+ token = qlist_pop(working);
+ }
+
+ qobject_decref(token);
+ token = NULL;
+ } else {
+ token = qlist_pop(working);
+ qobject_decref(token);
+ token = NULL;
+ }
+
+ QDECREF(*tokens);
+ *tokens = working;
+
+ return QOBJECT(list);
+
+out:
+ qobject_decref(token);
+ QDECREF(working);
+ QDECREF(list);
+ return NULL;
+}
+
+static QObject *parse_keyword(JSONParserContext *ctxt, QList **tokens)
+{
+ QObject *token, *ret;
+ QList *working = qlist_copy(*tokens);
+
+ token = qlist_pop(working);
+
+ if (token_get_type(token) != JSON_KEYWORD) {
+ goto out;
+ }
+
+ if (token_is_keyword(token, "true")) {
+ ret = QOBJECT(qbool_from_int(true));
+ } else if (token_is_keyword(token, "false")) {
+ ret = QOBJECT(qbool_from_int(false));
+ } else {
+ parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token));
+ goto out;
+ }
+
+ qobject_decref(token);
+ QDECREF(*tokens);
+ *tokens = working;
+
+ return ret;
+
+out:
+ qobject_decref(token);
+ QDECREF(working);
+
+ return NULL;
+}
+
+static QObject *parse_escape(JSONParserContext *ctxt, QList **tokens, va_list *ap)
+{
+ QObject *token = NULL, *obj;
+ QList *working = qlist_copy(*tokens);
+
+ if (ap == NULL) {
+ goto out;
+ }
+
+ token = qlist_pop(working);
+
+ if (token_is_escape(token, "%p")) {
+ obj = va_arg(*ap, QObject *);
+ } else if (token_is_escape(token, "%i")) {
+ obj = QOBJECT(qbool_from_int(va_arg(*ap, int)));
+ } else if (token_is_escape(token, "%d")) {
+ obj = QOBJECT(qint_from_int(va_arg(*ap, int)));
+ } else if (token_is_escape(token, "%ld")) {
+ obj = QOBJECT(qint_from_int(va_arg(*ap, long)));
+ } else if (token_is_escape(token, "%lld") ||
+ token_is_escape(token, "%I64d")) {
+ obj = QOBJECT(qint_from_int(va_arg(*ap, long long)));
+ } else if (token_is_escape(token, "%s")) {
+ obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
+ } else if (token_is_escape(token, "%f")) {
+ obj = QOBJECT(qfloat_from_double(va_arg(*ap, double)));
+ } else {
+ goto out;
+ }
+
+ qobject_decref(token);
+ QDECREF(*tokens);
+ *tokens = working;
+
+ return obj;
+
+out:
+ qobject_decref(token);
+ QDECREF(working);
+
+ return NULL;
+}
+
+static QObject *parse_literal(JSONParserContext *ctxt, QList **tokens)
+{
+ QObject *token, *obj;
+ QList *working = qlist_copy(*tokens);
+
+ token = qlist_pop(working);
+ switch (token_get_type(token)) {
+ case JSON_STRING:
+ obj = QOBJECT(qstring_from_escaped_str(ctxt, token));
+ break;
+ case JSON_INTEGER:
+ obj = QOBJECT(qint_from_int(strtoll(token_get_value(token), NULL, 10)));
+ break;
+ case JSON_FLOAT:
+ /* FIXME dependent on locale */
+ obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL)));
+ break;
+ default:
+ goto out;
+ }
+
+ qobject_decref(token);
+ QDECREF(*tokens);
+ *tokens = working;
+
+ return obj;
+
+out:
+ qobject_decref(token);
+ QDECREF(working);
+
+ return NULL;
+}
+
+static QObject *parse_value(JSONParserContext *ctxt, QList **tokens, va_list *ap)
+{
+ QObject *obj;
+
+ obj = parse_object(ctxt, tokens, ap);
+ if (obj == NULL) {
+ obj = parse_array(ctxt, tokens, ap);
+ }
+ if (obj == NULL) {
+ obj = parse_escape(ctxt, tokens, ap);
+ }
+ if (obj == NULL) {
+ obj = parse_keyword(ctxt, tokens);
+ }
+ if (obj == NULL) {
+ obj = parse_literal(ctxt, tokens);
+ }
+
+ return obj;
+}
+
+QObject *json_parser_parse(QList *tokens, va_list *ap)
+{
+ JSONParserContext ctxt = {};
+ QList *working = qlist_copy(tokens);
+ QObject *result;
+
+ result = parse_value(&ctxt, &working, ap);
+
+ QDECREF(working);
+
+ return result;
+}
diff --git a/json-parser.h b/json-parser.h
new file mode 100644
index 0000000..97f43f6
--- /dev/null
+++ b/json-parser.h
@@ -0,0 +1,22 @@
+/*
+ * JSON Parser
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_JSON_PARSER_H
+#define QEMU_JSON_PARSER_H
+
+#include "qemu-common.h"
+#include "qlist.h"
+
+QObject *json_parser_parse(QList *tokens, va_list *ap);
+
+#endif
diff --git a/json-streamer.c b/json-streamer.c
new file mode 100644
index 0000000..f7e7a68
--- /dev/null
+++ b/json-streamer.c
@@ -0,0 +1,88 @@
+/*
+ * JSON streaming support
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qlist.h"
+#include "qint.h"
+#include "qdict.h"
+#include "qemu-common.h"
+#include "json-lexer.h"
+#include "json-streamer.h"
+
+static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y)
+{
+ JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
+ QDict *dict;
+
+ if (type == JSON_OPERATOR) {
+ switch (qstring_get_str(token)[0]) {
+ case '{':
+ parser->brace_count++;
+ break;
+ case '}':
+ parser->brace_count--;
+ break;
+ case '[':
+ parser->bracket_count++;
+ break;
+ case ']':
+ parser->bracket_count--;
+ break;
+ default:
+ break;
+ }
+ }
+
+ dict = qdict_new();
+ qdict_put(dict, "type", qint_from_int(type));
+ QINCREF(token);
+ qdict_put(dict, "token", token);
+ qdict_put(dict, "x", qint_from_int(x));
+ qdict_put(dict, "y", qint_from_int(y));
+
+ qlist_append(parser->tokens, dict);
+
+ if (parser->brace_count == 0 &&
+ parser->bracket_count == 0) {
+ parser->emit(parser, parser->tokens);
+ QDECREF(parser->tokens);
+ parser->tokens = qlist_new();
+ }
+}
+
+void json_message_parser_init(JSONMessageParser *parser,
+ void (*func)(JSONMessageParser *, QList *))
+{
+ parser->emit = func;
+ parser->brace_count = 0;
+ parser->bracket_count = 0;
+ parser->tokens = qlist_new();
+
+ json_lexer_init(&parser->lexer, json_message_process_token);
+}
+
+int json_message_parser_feed(JSONMessageParser *parser,
+ const char *buffer, size_t size)
+{
+ return json_lexer_feed(&parser->lexer, buffer, size);
+}
+
+int json_message_parser_flush(JSONMessageParser *parser)
+{
+ return json_lexer_flush(&parser->lexer);
+}
+
+void json_message_parser_destroy(JSONMessageParser *parser)
+{
+ json_lexer_destroy(&parser->lexer);
+ QDECREF(parser->tokens);
+}
diff --git a/json-streamer.h b/json-streamer.h
new file mode 100644
index 0000000..09f3bd7
--- /dev/null
+++ b/json-streamer.h
@@ -0,0 +1,39 @@
+/*
+ * JSON streaming support
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_JSON_STREAMER_H
+#define QEMU_JSON_STREAMER_H
+
+#include "qlist.h"
+#include "json-lexer.h"
+
+typedef struct JSONMessageParser
+{
+ void (*emit)(struct JSONMessageParser *parser, QList *tokens);
+ JSONLexer lexer;
+ int brace_count;
+ int bracket_count;
+ QList *tokens;
+} JSONMessageParser;
+
+void json_message_parser_init(JSONMessageParser *parser,
+ void (*func)(JSONMessageParser *, QList *));
+
+int json_message_parser_feed(JSONMessageParser *parser,
+ const char *buffer, size_t size);
+
+int json_message_parser_flush(JSONMessageParser *parser);
+
+void json_message_parser_destroy(JSONMessageParser *parser);
+
+#endif
diff --git a/kvm-all.c b/kvm-all.c
new file mode 100644
index 0000000..0e404d6
--- /dev/null
+++ b/kvm-all.c
@@ -0,0 +1,1415 @@
+/*
+ * QEMU KVM support
+ *
+ * Copyright IBM, Corp. 2008
+ * Red Hat, Inc. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Glauber Costa <gcosta@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdarg.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu-barrier.h"
+#include "sysemu.h"
+#include "hw/hw.h"
+#include "gdbstub.h"
+#include "kvm.h"
+#include "bswap.h"
+
+/* This check must be after config-host.h is included */
+#ifdef CONFIG_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+/* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */
+#define PAGE_SIZE TARGET_PAGE_SIZE
+
+//#define DEBUG_KVM
+
+#ifdef DEBUG_KVM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#ifdef OBSOLETE_KVM_IMPL
+
+typedef struct KVMSlot
+{
+ target_phys_addr_t start_addr;
+ ram_addr_t memory_size;
+ ram_addr_t phys_offset;
+ int slot;
+ int flags;
+} KVMSlot;
+
+typedef struct kvm_dirty_log KVMDirtyLog;
+
+struct KVMState
+{
+ KVMSlot slots[32];
+ int fd;
+ int vmfd;
+ int coalesced_mmio;
+ struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
+ int broken_set_mem_region;
+ int migration_log;
+ int vcpu_events;
+ int robust_singlestep;
+ int debugregs;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_sw_breakpoint_head kvm_sw_breakpoints;
+#endif
+ int irqchip_in_kernel;
+ int pit_in_kernel;
+ int xsave, xcrs;
+ int many_ioeventfds;
+};
+
+static KVMState *kvm_state;
+
+
+static const KVMCapabilityInfo kvm_required_capabilites[] = {
+ KVM_CAP_INFO(USER_MEMORY),
+ KVM_CAP_INFO(DESTROY_MEMORY_REGION_WORKS),
+ KVM_CAP_LAST_INFO
+};
+
+#endif
+
+static KVMSlot *kvm_alloc_slot(KVMState *s)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ /* KVM private memory slots */
+ if (i >= 8 && i < 12) {
+ continue;
+ }
+ if (s->slots[i].memory_size == 0) {
+ return &s->slots[i];
+ }
+ }
+
+ fprintf(stderr, "%s: no free slot available\n", __func__);
+ abort();
+}
+
+static KVMSlot *kvm_lookup_matching_slot(KVMState *s,
+ target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ KVMSlot *mem = &s->slots[i];
+
+ if (start_addr == mem->start_addr &&
+ end_addr == mem->start_addr + mem->memory_size) {
+ return mem;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find overlapping slot with lowest start address
+ */
+static KVMSlot *kvm_lookup_overlapping_slot(KVMState *s,
+ target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
+{
+ KVMSlot *found = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ KVMSlot *mem = &s->slots[i];
+
+ if (mem->memory_size == 0 ||
+ (found && found->start_addr < mem->start_addr)) {
+ continue;
+ }
+
+ if (end_addr > mem->start_addr &&
+ start_addr < mem->start_addr + mem->memory_size) {
+ found = mem;
+ }
+ }
+
+ return found;
+}
+
+int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr,
+ target_phys_addr_t *phys_addr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ KVMSlot *mem = &s->slots[i];
+
+ if (ram_addr >= mem->phys_offset &&
+ ram_addr < mem->phys_offset + mem->memory_size) {
+ *phys_addr = mem->start_addr + (ram_addr - mem->phys_offset);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot)
+{
+ struct kvm_userspace_memory_region mem;
+
+ mem.slot = slot->slot;
+ mem.guest_phys_addr = slot->start_addr;
+ mem.memory_size = slot->memory_size;
+ mem.userspace_addr = (unsigned long)qemu_safe_ram_ptr(slot->phys_offset);
+ mem.flags = slot->flags;
+ if (s->migration_log) {
+ mem.flags |= KVM_MEM_LOG_DIRTY_PAGES;
+ }
+ return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+static void kvm_reset_vcpu(void *opaque)
+{
+ CPUState *env = opaque;
+
+ kvm_arch_reset_vcpu(env);
+}
+#endif
+
+int kvm_irqchip_in_kernel(void)
+{
+ return kvm_state->irqchip_in_kernel;
+}
+
+int kvm_pit_in_kernel(void)
+{
+ return kvm_state->pit_in_kernel;
+}
+
+
+#ifdef OBSOLETE_KVM_IMPL
+int kvm_init_vcpu(CPUState *env)
+{
+ KVMState *s = kvm_state;
+ long mmap_size;
+ int ret;
+
+ DPRINTF("kvm_init_vcpu\n");
+
+ ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, env->cpu_index);
+ if (ret < 0) {
+ DPRINTF("kvm_create_vcpu failed\n");
+ goto err;
+ }
+
+ env->kvm_fd = ret;
+ env->kvm_state = s;
+
+ mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (mmap_size < 0) {
+ DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n");
+ goto err;
+ }
+
+ env->kvm_run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ env->kvm_fd, 0);
+ if (env->kvm_run == MAP_FAILED) {
+ ret = -errno;
+ DPRINTF("mmap'ing vcpu state failed\n");
+ goto err;
+ }
+
+ if (s->coalesced_mmio && !s->coalesced_mmio_ring) {
+ s->coalesced_mmio_ring =
+ (void *)env->kvm_run + s->coalesced_mmio * PAGE_SIZE;
+ }
+
+ ret = kvm_arch_init_vcpu(env);
+ if (ret == 0) {
+ qemu_register_reset(kvm_reset_vcpu, env);
+ kvm_arch_reset_vcpu(env);
+ }
+err:
+ return ret;
+}
+#endif
+
+/*
+ * dirty pages logging control
+ */
+static int kvm_dirty_pages_log_change(target_phys_addr_t phys_addr,
+ ram_addr_t size, int flags, int mask)
+{
+ KVMState *s = kvm_state;
+ KVMSlot *mem = kvm_lookup_matching_slot(s, phys_addr, phys_addr + size);
+ int old_flags;
+
+ if (mem == NULL) {
+ fprintf(stderr, "BUG: %s: invalid parameters " TARGET_FMT_plx "-"
+ TARGET_FMT_plx "\n", __func__, phys_addr,
+ (target_phys_addr_t)(phys_addr + size - 1));
+ return -EINVAL;
+ }
+
+ old_flags = mem->flags;
+
+ flags = (mem->flags & ~mask) | flags;
+ mem->flags = flags;
+
+ /* If nothing changed effectively, no need to issue ioctl */
+ if (s->migration_log) {
+ flags |= KVM_MEM_LOG_DIRTY_PAGES;
+ }
+ if (flags == old_flags) {
+ return 0;
+ }
+
+ return kvm_set_user_memory_region(s, mem);
+}
+
+int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size)
+{
+ return kvm_dirty_pages_log_change(phys_addr, size, KVM_MEM_LOG_DIRTY_PAGES,
+ KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size)
+{
+ return kvm_dirty_pages_log_change(phys_addr, size, 0,
+ KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+static int kvm_set_migration_log(int enable)
+{
+ KVMState *s = kvm_state;
+ KVMSlot *mem;
+ int i, err;
+
+ s->migration_log = enable;
+
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ mem = &s->slots[i];
+
+ if (!mem->memory_size) {
+ continue;
+ }
+ if (!!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) == enable) {
+ continue;
+ }
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* get kvm's dirty pages bitmap and update qemu's */
+static int kvm_get_dirty_pages_log_range(unsigned long start_addr,
+ unsigned long *bitmap,
+ unsigned long offset,
+ unsigned long mem_size)
+{
+ unsigned int i, j;
+ unsigned long page_number, addr, addr1, c;
+ ram_addr_t ram_addr;
+ unsigned int len = ((mem_size / TARGET_PAGE_SIZE) + HOST_LONG_BITS - 1) /
+ HOST_LONG_BITS;
+
+ /*
+ * bitmap-traveling is faster than memory-traveling (for addr...)
+ * especially when most of the memory is not dirty.
+ */
+ for (i = 0; i < len; i++) {
+ if (bitmap[i] != 0) {
+ c = leul_to_cpu(bitmap[i]);
+ do {
+ j = ffsl(c) - 1;
+ c &= ~(1ul << j);
+ page_number = i * HOST_LONG_BITS + j;
+ addr1 = page_number * TARGET_PAGE_SIZE;
+ addr = offset + addr1;
+ ram_addr = cpu_get_physical_page_desc(addr);
+ cpu_physical_memory_set_dirty(ram_addr);
+ } while (c != 0);
+ }
+ }
+ return 0;
+}
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+
+/**
+ * kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space
+ * This function updates qemu's dirty bitmap using cpu_physical_memory_set_dirty().
+ * This means all bits are set to dirty.
+ *
+ * @start_add: start of logged region.
+ * @end_addr: end of logged region.
+ */
+static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
+{
+ KVMState *s = kvm_state;
+ unsigned long size, allocated_size = 0;
+ KVMDirtyLog d;
+ KVMSlot *mem;
+ int ret = 0;
+
+ d.dirty_bitmap = NULL;
+ while (start_addr < end_addr) {
+ mem = kvm_lookup_overlapping_slot(s, start_addr, end_addr);
+ if (mem == NULL) {
+ break;
+ }
+
+ size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), HOST_LONG_BITS) / 8;
+ if (!d.dirty_bitmap) {
+ d.dirty_bitmap = qemu_malloc(size);
+ } else if (size > allocated_size) {
+ d.dirty_bitmap = qemu_realloc(d.dirty_bitmap, size);
+ }
+ allocated_size = size;
+ memset(d.dirty_bitmap, 0, allocated_size);
+
+ d.slot = mem->slot;
+
+ if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
+ DPRINTF("ioctl failed %d\n", errno);
+ ret = -1;
+ break;
+ }
+
+ kvm_get_dirty_pages_log_range(mem->start_addr, d.dirty_bitmap,
+ mem->start_addr, mem->memory_size);
+ start_addr = mem->start_addr + mem->memory_size;
+ }
+ qemu_free(d.dirty_bitmap);
+
+ return ret;
+}
+
+int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
+{
+ int ret = -ENOSYS;
+ KVMState *s = kvm_state;
+
+ if (s->coalesced_mmio) {
+ struct kvm_coalesced_mmio_zone zone;
+
+ zone.addr = start;
+ zone.size = size;
+
+ ret = kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
+ }
+
+ return ret;
+}
+
+int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
+{
+ int ret = -ENOSYS;
+ KVMState *s = kvm_state;
+
+ if (s->coalesced_mmio) {
+ struct kvm_coalesced_mmio_zone zone;
+
+ zone.addr = start;
+ zone.size = size;
+
+ ret = kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
+ }
+
+ return ret;
+}
+
+int kvm_check_extension(KVMState *s, unsigned int extension)
+{
+ int ret;
+
+ ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, extension);
+ if (ret < 0) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int kvm_check_many_ioeventfds(void)
+{
+ /* Userspace can use ioeventfd for io notification. This requires a host
+ * that supports eventfd(2) and an I/O thread; since eventfd does not
+ * support SIGIO it cannot interrupt the vcpu.
+ *
+ * Older kernels have a 6 device limit on the KVM io bus. Find out so we
+ * can avoid creating too many ioeventfds.
+ */
+#if defined(CONFIG_EVENTFD) && defined(CONFIG_IOTHREAD)
+ int ioeventfds[7];
+ int i, ret = 0;
+ for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) {
+ ioeventfds[i] = eventfd(0, EFD_CLOEXEC);
+ if (ioeventfds[i] < 0) {
+ break;
+ }
+ ret = kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, true);
+ if (ret < 0) {
+ close(ioeventfds[i]);
+ break;
+ }
+ }
+
+ /* Decide whether many devices are supported or not */
+ ret = i == ARRAY_SIZE(ioeventfds);
+
+ while (i-- > 0) {
+ kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, false);
+ close(ioeventfds[i]);
+ }
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+static const KVMCapabilityInfo *
+kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list)
+{
+ while (list->name) {
+ if (!kvm_check_extension(s, list->value)) {
+ return list;
+ }
+ list++;
+ }
+ return NULL;
+}
+#endif
+
+static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size,
+ ram_addr_t phys_offset)
+{
+ KVMState *s = kvm_state;
+ ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
+ KVMSlot *mem, old;
+ int err;
+
+ /* kvm works in page size chunks, but the function may be called
+ with sub-page size and unaligned start address. */
+ size = TARGET_PAGE_ALIGN(size);
+ start_addr = TARGET_PAGE_ALIGN(start_addr);
+
+ /* KVM does not support read-only slots */
+ phys_offset &= ~IO_MEM_ROM;
+
+ while (1) {
+ mem = kvm_lookup_overlapping_slot(s, start_addr, start_addr + size);
+ if (!mem) {
+ break;
+ }
+
+ if (flags < IO_MEM_UNASSIGNED && start_addr >= mem->start_addr &&
+ (start_addr + size <= mem->start_addr + mem->memory_size) &&
+ (phys_offset - start_addr == mem->phys_offset - mem->start_addr)) {
+ /* The new slot fits into the existing one and comes with
+ * identical parameters - nothing to be done. */
+ return;
+ }
+
+ old = *mem;
+
+ /* unregister the overlapping slot */
+ mem->memory_size = 0;
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ fprintf(stderr, "%s: error unregistering overlapping slot: %s\n",
+ __func__, strerror(-err));
+ abort();
+ }
+
+ /* Workaround for older KVM versions: we can't join slots, even not by
+ * unregistering the previous ones and then registering the larger
+ * slot. We have to maintain the existing fragmentation. Sigh.
+ *
+ * This workaround assumes that the new slot starts at the same
+ * address as the first existing one. If not or if some overlapping
+ * slot comes around later, we will fail (not seen in practice so far)
+ * - and actually require a recent KVM version. */
+ if (s->broken_set_mem_region &&
+ old.start_addr == start_addr && old.memory_size < size &&
+ flags < IO_MEM_UNASSIGNED) {
+ mem = kvm_alloc_slot(s);
+ mem->memory_size = old.memory_size;
+ mem->start_addr = old.start_addr;
+ mem->phys_offset = old.phys_offset;
+ mem->flags = 0;
+
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ fprintf(stderr, "%s: error updating slot: %s\n", __func__,
+ strerror(-err));
+ abort();
+ }
+
+ start_addr += old.memory_size;
+ phys_offset += old.memory_size;
+ size -= old.memory_size;
+ continue;
+ }
+
+ /* register prefix slot */
+ if (old.start_addr < start_addr) {
+ mem = kvm_alloc_slot(s);
+ mem->memory_size = start_addr - old.start_addr;
+ mem->start_addr = old.start_addr;
+ mem->phys_offset = old.phys_offset;
+ mem->flags = 0;
+
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ fprintf(stderr, "%s: error registering prefix slot: %s\n",
+ __func__, strerror(-err));
+ abort();
+ }
+ }
+
+ /* register suffix slot */
+ if (old.start_addr + old.memory_size > start_addr + size) {
+ ram_addr_t size_delta;
+
+ mem = kvm_alloc_slot(s);
+ mem->start_addr = start_addr + size;
+ size_delta = mem->start_addr - old.start_addr;
+ mem->memory_size = old.memory_size - size_delta;
+ mem->phys_offset = old.phys_offset + size_delta;
+ mem->flags = 0;
+
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ fprintf(stderr, "%s: error registering suffix slot: %s\n",
+ __func__, strerror(-err));
+ abort();
+ }
+ }
+ }
+
+ /* in case the KVM bug workaround already "consumed" the new slot */
+ if (!size) {
+ return;
+ }
+ /* KVM does not need to know about this memory */
+ if (flags >= IO_MEM_UNASSIGNED) {
+ return;
+ }
+ mem = kvm_alloc_slot(s);
+ mem->memory_size = size;
+ mem->start_addr = start_addr;
+ mem->phys_offset = phys_offset;
+ mem->flags = 0;
+
+ err = kvm_set_user_memory_region(s, mem);
+ if (err) {
+ fprintf(stderr, "%s: error registering slot: %s\n", __func__,
+ strerror(-err));
+ abort();
+ }
+}
+
+static void kvm_client_set_memory(struct CPUPhysMemoryClient *client,
+ target_phys_addr_t start_addr,
+ ram_addr_t size, ram_addr_t phys_offset)
+{
+ kvm_set_phys_mem(start_addr, size, phys_offset);
+}
+
+static int kvm_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client,
+ target_phys_addr_t start_addr,
+ target_phys_addr_t end_addr)
+{
+ return kvm_physical_sync_dirty_bitmap(start_addr, end_addr);
+}
+
+static int kvm_client_migration_log(struct CPUPhysMemoryClient *client,
+ int enable)
+{
+ return kvm_set_migration_log(enable);
+}
+
+static CPUPhysMemoryClient kvm_cpu_phys_memory_client = {
+ .set_memory = kvm_client_set_memory,
+ .sync_dirty_bitmap = kvm_client_sync_dirty_bitmap,
+ .migration_log = kvm_client_migration_log,
+};
+
+void kvm_cpu_register_phys_memory_client(void)
+{
+ cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client);
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+
+int kvm_init(void)
+{
+ static const char upgrade_note[] =
+ "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
+ "(see http://sourceforge.net/projects/kvm).\n";
+ KVMState *s;
+ const KVMCapabilityInfo *missing_cap;
+ int ret;
+ int i;
+
+ s = qemu_mallocz(sizeof(KVMState));
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ QTAILQ_INIT(&s->kvm_sw_breakpoints);
+#endif
+ for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
+ s->slots[i].slot = i;
+ }
+ s->vmfd = -1;
+ s->fd = qemu_open("/dev/kvm", O_RDWR);
+ if (s->fd == -1) {
+ fprintf(stderr, "Could not access KVM kernel module: %m\n");
+ ret = -errno;
+ goto err;
+ }
+
+ ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0);
+ if (ret < KVM_API_VERSION) {
+ if (ret > 0) {
+ ret = -EINVAL;
+ }
+ fprintf(stderr, "kvm version too old\n");
+ goto err;
+ }
+
+ if (ret > KVM_API_VERSION) {
+ ret = -EINVAL;
+ fprintf(stderr, "kvm version not supported\n");
+ goto err;
+ }
+
+ s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0);
+ if (s->vmfd < 0) {
+#ifdef TARGET_S390X
+ fprintf(stderr, "Please add the 'switch_amode' kernel parameter to "
+ "your host kernel command line\n");
+#endif
+ goto err;
+ }
+
+ missing_cap = kvm_check_extension_list(s, kvm_required_capabilites);
+ if (!missing_cap) {
+ missing_cap =
+ kvm_check_extension_list(s, kvm_arch_required_capabilities);
+ }
+ if (missing_cap) {
+ ret = -EINVAL;
+ fprintf(stderr, "kvm does not support %s\n%s",
+ missing_cap->name, upgrade_note);
+ goto err;
+ }
+
+ s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
+
+ s->broken_set_mem_region = 1;
+#ifdef KVM_CAP_JOIN_MEMORY_REGIONS_WORKS
+ ret = kvm_check_extension(s, KVM_CAP_JOIN_MEMORY_REGIONS_WORKS);
+ if (ret > 0) {
+ s->broken_set_mem_region = 0;
+ }
+#endif
+
+ s->vcpu_events = 0;
+#ifdef KVM_CAP_VCPU_EVENTS
+ s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
+#endif
+
+ s->robust_singlestep = 0;
+#ifdef KVM_CAP_X86_ROBUST_SINGLESTEP
+ s->robust_singlestep =
+ kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP);
+#endif
+
+ s->debugregs = 0;
+#ifdef KVM_CAP_DEBUGREGS
+ s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS);
+#endif
+
+ s->xsave = 0;
+#ifdef KVM_CAP_XSAVE
+ s->xsave = kvm_check_extension(s, KVM_CAP_XSAVE);
+#endif
+
+ s->xcrs = 0;
+#ifdef KVM_CAP_XCRS
+ s->xcrs = kvm_check_extension(s, KVM_CAP_XCRS);
+#endif
+
+ ret = kvm_arch_init(s);
+ if (ret < 0) {
+ goto err;
+ }
+
+ kvm_state = s;
+ cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client);
+
+ s->many_ioeventfds = kvm_check_many_ioeventfds();
+
+ return 0;
+
+err:
+ if (s) {
+ if (s->vmfd != -1) {
+ close(s->vmfd);
+ }
+ if (s->fd != -1) {
+ close(s->fd);
+ }
+ }
+ qemu_free(s);
+
+ return ret;
+}
+#endif
+
+static int kvm_handle_io(uint16_t port, void *data, int direction, int size,
+ uint32_t count)
+{
+ int i;
+ uint8_t *ptr = data;
+
+ for (i = 0; i < count; i++) {
+ if (direction == KVM_EXIT_IO_IN) {
+ switch (size) {
+ case 1:
+ stb_p(ptr, cpu_inb(port));
+ break;
+ case 2:
+ stw_p(ptr, cpu_inw(port));
+ break;
+ case 4:
+ stl_p(ptr, cpu_inl(port));
+ break;
+ }
+ } else {
+ switch (size) {
+ case 1:
+ cpu_outb(port, ldub_p(ptr));
+ break;
+ case 2:
+ cpu_outw(port, lduw_p(ptr));
+ break;
+ case 4:
+ cpu_outl(port, ldl_p(ptr));
+ break;
+ }
+ }
+
+ ptr += size;
+ }
+
+ return 1;
+}
+
+#ifdef KVM_CAP_INTERNAL_ERROR_DATA
+static int kvm_handle_internal_error(CPUState *env, struct kvm_run *run)
+{
+ fprintf(stderr, "KVM internal error.");
+ if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) {
+ int i;
+
+ fprintf(stderr, " Suberror: %d\n", run->internal.suberror);
+ for (i = 0; i < run->internal.ndata; ++i) {
+ fprintf(stderr, "extra data[%d]: %"PRIx64"\n",
+ i, (uint64_t)run->internal.data[i]);
+ }
+ } else {
+ fprintf(stderr, "\n");
+ }
+ if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) {
+ fprintf(stderr, "emulation failure\n");
+ if (!kvm_arch_stop_on_emulation_error(env)) {
+ cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE);
+ return 0;
+ }
+ }
+ /* FIXME: Should trigger a qmp message to let management know
+ * something went wrong.
+ */
+ return -1;
+}
+#endif
+
+void kvm_flush_coalesced_mmio_buffer(void)
+{
+ KVMState *s = kvm_state;
+ if (s->coalesced_mmio_ring) {
+ struct kvm_coalesced_mmio_ring *ring = s->coalesced_mmio_ring;
+ while (ring->first != ring->last) {
+ struct kvm_coalesced_mmio *ent;
+
+ ent = &ring->coalesced_mmio[ring->first];
+
+ cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
+ smp_wmb();
+ ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
+ }
+ }
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+
+static void do_kvm_cpu_synchronize_state(void *_env)
+{
+ CPUState *env = _env;
+
+ if (!env->kvm_vcpu_dirty) {
+ kvm_arch_get_registers(env);
+ env->kvm_vcpu_dirty = 1;
+ }
+}
+
+void kvm_cpu_synchronize_state(CPUState *env)
+{
+ if (!env->kvm_vcpu_dirty) {
+ run_on_cpu(env, do_kvm_cpu_synchronize_state, env);
+ }
+}
+
+void kvm_cpu_synchronize_post_reset(CPUState *env)
+{
+ kvm_arch_put_registers(env, KVM_PUT_RESET_STATE);
+ env->kvm_vcpu_dirty = 0;
+}
+
+void kvm_cpu_synchronize_post_init(CPUState *env)
+{
+ kvm_arch_put_registers(env, KVM_PUT_FULL_STATE);
+ env->kvm_vcpu_dirty = 0;
+}
+
+int kvm_cpu_exec(CPUState *env)
+{
+ struct kvm_run *run = env->kvm_run;
+ int ret;
+
+ DPRINTF("kvm_cpu_exec()\n");
+
+ do {
+#ifndef CONFIG_IOTHREAD
+ if (env->exit_request) {
+ DPRINTF("interrupt exit requested\n");
+ ret = 0;
+ break;
+ }
+#endif
+
+ if (kvm_arch_process_irqchip_events(env)) {
+ ret = 0;
+ break;
+ }
+
+ if (env->kvm_vcpu_dirty) {
+ kvm_arch_put_registers(env, KVM_PUT_RUNTIME_STATE);
+ env->kvm_vcpu_dirty = 0;
+ }
+
+ kvm_arch_pre_run(env, run);
+ cpu_single_env = NULL;
+ qemu_mutex_unlock_iothread();
+ ret = kvm_vcpu_ioctl(env, KVM_RUN, 0);
+ qemu_mutex_lock_iothread();
+ cpu_single_env = env;
+ kvm_arch_post_run(env, run);
+
+ kvm_flush_coalesced_mmio_buffer();
+
+ if (ret == -EINTR || ret == -EAGAIN) {
+ cpu_exit(env);
+ DPRINTF("io window exit\n");
+ ret = 0;
+ break;
+ }
+
+ if (ret < 0) {
+ DPRINTF("kvm run failed %s\n", strerror(-ret));
+ abort();
+ }
+
+ ret = 0; /* exit loop */
+ switch (run->exit_reason) {
+ case KVM_EXIT_IO:
+ DPRINTF("handle_io\n");
+ ret = kvm_handle_io(run->io.port,
+ (uint8_t *)run + run->io.data_offset,
+ run->io.direction,
+ run->io.size,
+ run->io.count);
+ break;
+ case KVM_EXIT_MMIO:
+ DPRINTF("handle_mmio\n");
+ cpu_physical_memory_rw(run->mmio.phys_addr,
+ run->mmio.data,
+ run->mmio.len,
+ run->mmio.is_write);
+ ret = 1;
+ break;
+ case KVM_EXIT_IRQ_WINDOW_OPEN:
+ DPRINTF("irq_window_open\n");
+ break;
+ case KVM_EXIT_SHUTDOWN:
+ DPRINTF("shutdown\n");
+ qemu_system_reset_request();
+ ret = 1;
+ break;
+ case KVM_EXIT_UNKNOWN:
+ fprintf(stderr, "KVM: unknown exit, hardware reason %" PRIx64 "\n",
+ (uint64_t)run->hw.hardware_exit_reason);
+ ret = -1;
+ break;
+#ifdef KVM_CAP_INTERNAL_ERROR_DATA
+ case KVM_EXIT_INTERNAL_ERROR:
+ ret = kvm_handle_internal_error(env, run);
+ break;
+#endif
+ case KVM_EXIT_DEBUG:
+ DPRINTF("kvm_exit_debug\n");
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ if (kvm_arch_debug(&run->debug.arch)) {
+ env->exception_index = EXCP_DEBUG;
+ return 0;
+ }
+ /* re-enter, this exception was guest-internal */
+ ret = 1;
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
+ break;
+ default:
+ DPRINTF("kvm_arch_handle_exit\n");
+ ret = kvm_arch_handle_exit(env, run);
+ break;
+ }
+ } while (ret > 0);
+
+ if (ret < 0) {
+ cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE);
+ vm_stop(0);
+ env->exit_request = 1;
+ }
+ if (env->exit_request) {
+ env->exit_request = 0;
+ env->exception_index = EXCP_INTERRUPT;
+ }
+
+ return ret;
+}
+
+#endif
+int kvm_ioctl(KVMState *s, int type, ...)
+{
+ int ret;
+ void *arg;
+ va_list ap;
+
+ va_start(ap, type);
+ arg = va_arg(ap, void *);
+ va_end(ap);
+
+ ret = ioctl(s->fd, type, arg);
+ if (ret == -1) {
+ ret = -errno;
+ }
+ return ret;
+}
+
+int kvm_vm_ioctl(KVMState *s, int type, ...)
+{
+ int ret;
+ void *arg;
+ va_list ap;
+
+ va_start(ap, type);
+ arg = va_arg(ap, void *);
+ va_end(ap);
+
+ ret = ioctl(s->vmfd, type, arg);
+ if (ret == -1) {
+ ret = -errno;
+ }
+ return ret;
+}
+
+int kvm_vcpu_ioctl(CPUState *env, int type, ...)
+{
+ int ret;
+ void *arg;
+ va_list ap;
+
+ va_start(ap, type);
+ arg = va_arg(ap, void *);
+ va_end(ap);
+
+ ret = ioctl(env->kvm_fd, type, arg);
+ if (ret == -1) {
+ ret = -errno;
+ }
+ return ret;
+}
+
+int kvm_has_sync_mmu(void)
+{
+ return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
+}
+
+int kvm_has_vcpu_events(void)
+{
+ return kvm_state->vcpu_events;
+}
+
+int kvm_has_robust_singlestep(void)
+{
+ return kvm_state->robust_singlestep;
+}
+
+int kvm_has_debugregs(void)
+{
+ return kvm_state->debugregs;
+}
+
+int kvm_has_xsave(void)
+{
+ return kvm_state->xsave;
+}
+
+int kvm_has_xcrs(void)
+{
+ return kvm_state->xcrs;
+}
+
+int kvm_has_many_ioeventfds(void)
+{
+ if (!kvm_enabled()) {
+ return 0;
+ }
+ return kvm_state->many_ioeventfds;
+}
+
+void kvm_setup_guest_memory(void *start, size_t size)
+{
+ if (!kvm_has_sync_mmu()) {
+ int ret = qemu_madvise(start, size, QEMU_MADV_DONTFORK);
+
+ if (ret) {
+ perror("qemu_madvise");
+ fprintf(stderr,
+ "Need MADV_DONTFORK in absence of synchronous KVM MMU\n");
+ exit(1);
+ }
+ }
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+#ifndef OBSOLETE_KVM_IMPL
+#define run_on_cpu on_vcpu
+#endif /* !OBSOLETE_KVM_IMPL */
+
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env,
+ target_ulong pc)
+{
+ struct kvm_sw_breakpoint *bp;
+
+ QTAILQ_FOREACH(bp, &env->kvm_state->kvm_sw_breakpoints, entry) {
+ if (bp->pc == pc) {
+ return bp;
+ }
+ }
+ return NULL;
+}
+
+int kvm_sw_breakpoints_active(CPUState *env)
+{
+ return !QTAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints);
+}
+
+struct kvm_set_guest_debug_data {
+ struct kvm_guest_debug dbg;
+ CPUState *env;
+ int err;
+};
+
+static void kvm_invoke_set_guest_debug(void *data)
+{
+ struct kvm_set_guest_debug_data *dbg_data = data;
+ CPUState *env = dbg_data->env;
+
+ dbg_data->err = kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg_data->dbg);
+}
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ struct kvm_set_guest_debug_data data;
+
+ data.dbg.control = reinject_trap;
+
+ if (env->singlestep_enabled) {
+ data.dbg.control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
+ }
+ kvm_arch_update_guest_debug(env, &data.dbg);
+ data.env = env;
+
+ run_on_cpu(env, kvm_invoke_set_guest_debug, &data);
+ return data.err;
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ CPUState *env;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(current_env, addr);
+ if (bp) {
+ bp->use_count++;
+ return 0;
+ }
+
+ bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint));
+ if (!bp) {
+ return -ENOMEM;
+ }
+
+ bp->pc = addr;
+ bp->use_count = 1;
+ err = kvm_arch_insert_sw_breakpoint(current_env, bp);
+ if (err) {
+ free(bp);
+ return err;
+ }
+
+ QTAILQ_INSERT_HEAD(&current_env->kvm_state->kvm_sw_breakpoints,
+ bp, entry);
+ } else {
+ err = kvm_arch_insert_hw_breakpoint(addr, len, type);
+ if (err) {
+ return err;
+ }
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = kvm_update_guest_debug(env, 0);
+ if (err) {
+ return err;
+ }
+ }
+ return 0;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ struct kvm_sw_breakpoint *bp;
+ CPUState *env;
+ int err;
+
+ if (type == GDB_BREAKPOINT_SW) {
+ bp = kvm_find_sw_breakpoint(current_env, addr);
+ if (!bp) {
+ return -ENOENT;
+ }
+
+ if (bp->use_count > 1) {
+ bp->use_count--;
+ return 0;
+ }
+
+ err = kvm_arch_remove_sw_breakpoint(current_env, bp);
+ if (err) {
+ return err;
+ }
+
+ QTAILQ_REMOVE(&current_env->kvm_state->kvm_sw_breakpoints, bp, entry);
+ qemu_free(bp);
+ } else {
+ err = kvm_arch_remove_hw_breakpoint(addr, len, type);
+ if (err) {
+ return err;
+ }
+ }
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ err = kvm_update_guest_debug(env, 0);
+ if (err) {
+ return err;
+ }
+ }
+ return 0;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+ struct kvm_sw_breakpoint *bp, *next;
+ KVMState *s = current_env->kvm_state;
+ CPUState *env;
+
+ QTAILQ_FOREACH_SAFE(bp, &s->kvm_sw_breakpoints, entry, next) {
+ if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) {
+ /* Try harder to find a CPU that currently sees the breakpoint. */
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (kvm_arch_remove_sw_breakpoint(env, bp) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ kvm_arch_remove_all_hw_breakpoints();
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ kvm_update_guest_debug(env, 0);
+ }
+}
+
+#else /* !KVM_CAP_SET_GUEST_DEBUG */
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ return -EINVAL;
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+}
+#endif /* !KVM_CAP_SET_GUEST_DEBUG */
+
+int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset)
+{
+ struct kvm_signal_mask *sigmask;
+ int r;
+
+ if (!sigset) {
+ return kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, NULL);
+ }
+
+ sigmask = qemu_malloc(sizeof(*sigmask) + sizeof(*sigset));
+
+ sigmask->len = 8;
+ memcpy(sigmask->sigset, sigset, sizeof(*sigset));
+ r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask);
+ free(sigmask);
+
+ return r;
+}
+
+int kvm_set_ioeventfd_mmio_long(int fd, uint32_t addr, uint32_t val, bool assign)
+{
+#ifdef KVM_IOEVENTFD
+ int ret;
+ struct kvm_ioeventfd iofd;
+
+ iofd.datamatch = val;
+ iofd.addr = addr;
+ iofd.len = 4;
+ iofd.flags = KVM_IOEVENTFD_FLAG_DATAMATCH;
+ iofd.fd = fd;
+
+ if (!kvm_enabled()) {
+ return -ENOSYS;
+ }
+
+ if (!assign) {
+ iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
+ }
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd);
+
+ if (ret < 0) {
+ return -errno;
+ }
+
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
+{
+#ifdef KVM_IOEVENTFD
+ struct kvm_ioeventfd kick = {
+ .datamatch = val,
+ .addr = addr,
+ .len = 2,
+ .flags = KVM_IOEVENTFD_FLAG_DATAMATCH | KVM_IOEVENTFD_FLAG_PIO,
+ .fd = fd,
+ };
+ int r;
+ if (!kvm_enabled()) {
+ return -ENOSYS;
+ }
+ if (!assign) {
+ kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
+ }
+ r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick);
+ if (r < 0) {
+ return r;
+ }
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+#if defined(KVM_IRQFD)
+int kvm_set_irqfd(int gsi, int fd, bool assigned)
+{
+ struct kvm_irqfd irqfd = {
+ .fd = fd,
+ .gsi = gsi,
+ .flags = assigned ? 0 : KVM_IRQFD_FLAG_DEASSIGN,
+ };
+ int r;
+ if (!kvm_enabled() || !kvm_irqchip_in_kernel())
+ return -ENOSYS;
+
+ r = kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd);
+ if (r < 0)
+ return r;
+ return 0;
+}
+#endif
+
+#undef PAGE_SIZE
+#include "qemu-kvm.c"
diff --git a/kvm-stub.c b/kvm-stub.c
new file mode 100644
index 0000000..9ec2b3c
--- /dev/null
+++ b/kvm-stub.c
@@ -0,0 +1,193 @@
+/*
+ * QEMU KVM stub
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "hw/hw.h"
+#include "exec-all.h"
+#include "gdbstub.h"
+#include "kvm.h"
+
+int kvm_irqchip_in_kernel(void)
+{
+ return 0;
+}
+
+int kvm_pit_in_kernel(void)
+{
+ return 0;
+}
+
+
+int kvm_init_vcpu(CPUState *env)
+{
+ return -ENOSYS;
+}
+
+int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size)
+{
+ return -ENOSYS;
+}
+
+int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size)
+{
+ return -ENOSYS;
+}
+
+int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
+{
+ return -ENOSYS;
+}
+
+int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size)
+{
+ return -ENOSYS;
+}
+
+int kvm_check_extension(KVMState *s, unsigned int extension)
+{
+ return 0;
+}
+
+int kvm_init(void)
+{
+ return -ENOSYS;
+}
+
+void kvm_flush_coalesced_mmio_buffer(void)
+{
+}
+
+void kvm_cpu_synchronize_state(CPUState *env)
+{
+}
+
+void kvm_cpu_synchronize_post_reset(CPUState *env)
+{
+}
+
+void kvm_cpu_synchronize_post_init(CPUState *env)
+{
+}
+
+int kvm_cpu_exec(CPUState *env)
+{
+ abort ();
+}
+
+int kvm_has_sync_mmu(void)
+{
+ return 0;
+}
+
+int kvm_has_vcpu_events(void)
+{
+ return 0;
+}
+
+int kvm_has_robust_singlestep(void)
+{
+ return 0;
+}
+
+int kvm_has_many_ioeventfds(void)
+{
+ return 0;
+}
+
+void kvm_setup_guest_memory(void *start, size_t size)
+{
+}
+
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap)
+{
+ tb_flush(env);
+ return 0;
+}
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type)
+{
+ return -EINVAL;
+}
+
+void kvm_remove_all_breakpoints(CPUState *current_env)
+{
+}
+
+#ifndef _WIN32
+int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset)
+{
+ abort();
+}
+#endif
+
+int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign)
+{
+ return -ENOSYS;
+}
+
+int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign)
+{
+ return -ENOSYS;
+}
+
+int kvm_has_gsi_routing(void)
+{
+ return 0;
+}
+
+int kvm_get_irq_route_gsi(void)
+{
+ return -ENOSYS;
+}
+
+int kvm_add_msix(uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data)
+{
+ return -ENOSYS;
+}
+
+int kvm_del_msix(uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data)
+{
+ return -ENOSYS;
+}
+
+int kvm_update_msix(uint32_t old_gsi, uint32_t old_addr_lo,
+ uint32_t old_addr_hi, uint32_t old_data,
+ uint32_t new_gsi, uint32_t new_addr_lo,
+ uint32_t new_addr_hi, uint32_t new_data)
+{
+ return -ENOSYS;
+}
+
+int kvm_commit_irq_routes(void)
+{
+ return -ENOSYS;
+}
+
+int kvm_set_irq(int irq, int level, int *status)
+{
+ assert(0);
+ return -ENOSYS;
+}
+int kvm_on_sigbus(int code, void *addr)
+{
+ return 1;
+}
diff --git a/kvm-tpr-opt.c b/kvm-tpr-opt.c
new file mode 100644
index 0000000..c929fc8
--- /dev/null
+++ b/kvm-tpr-opt.c
@@ -0,0 +1,380 @@
+/*
+ * tpr optimization for qemu/kvm
+ *
+ * Copyright (C) 2007-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+
+#include "config.h"
+#include "config-host.h"
+
+#include <string.h>
+
+#include "hw/hw.h"
+#include "hw/isa.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "cpu.h"
+
+#include <stdio.h>
+
+static uint64_t map_addr(CPUState *env, target_ulong virt, unsigned *perms)
+{
+ uint64_t mask = ((1ull << 48) - 1) & ~4095ull;
+ uint64_t p, pp = 7;
+
+ p = env->cr[3];
+ if (env->cr[4] & 0x20) {
+ p &= ~31ull;
+ p = ldq_phys(p + 8 * (virt >> 30));
+ if (!(p & 1))
+ return -1ull;
+ p &= mask;
+ p = ldq_phys(p + 8 * ((virt >> 21) & 511));
+ if (!(p & 1))
+ return -1ull;
+ pp &= p;
+ if (p & 128) {
+ p += ((virt >> 12) & 511) << 12;
+ } else {
+ p &= mask;
+ p = ldq_phys(p + 8 * ((virt >> 12) & 511));
+ if (!(p & 1))
+ return -1ull;
+ pp &= p;
+ }
+ } else {
+ p &= mask;
+ p = ldl_phys(p + 4 * ((virt >> 22) & 1023));
+ if (!(p & 1))
+ return -1ull;
+ pp &= p;
+ if (p & 128) {
+ p += ((virt >> 12) & 1023) << 12;
+ } else {
+ p &= mask;
+ p = ldl_phys(p + 4 * ((virt >> 12) & 1023));
+ pp &= p;
+ if (!(p & 1))
+ return -1ull;
+ }
+ }
+ if (perms)
+ *perms = pp >> 1;
+ p &= mask;
+ return p + (virt & 4095);
+}
+
+static uint8_t read_byte_virt(CPUState *env, target_ulong virt)
+{
+ return ldub_phys(map_addr(env, virt, NULL));
+}
+
+static void write_byte_virt(CPUState *env, target_ulong virt, uint8_t b)
+{
+ cpu_physical_memory_write_rom(map_addr(env, virt, NULL), &b, 1);
+}
+
+struct vapic_bios {
+ char signature[8];
+ uint32_t virt_base;
+ uint32_t fixup_start;
+ uint32_t fixup_end;
+ uint32_t vapic;
+ uint32_t vapic_size;
+ uint32_t vcpu_shift;
+ uint32_t real_tpr;
+ struct vapic_patches {
+ uint32_t set_tpr;
+ uint32_t set_tpr_eax;
+ uint32_t get_tpr[8];
+ uint32_t get_tpr_stack;
+ } __attribute__((packed)) up, mp;
+} __attribute__((packed));
+
+static struct vapic_bios vapic_bios;
+
+static uint32_t real_tpr;
+static uint32_t bios_addr;
+static uint32_t vapic_phys;
+static uint32_t bios_enabled;
+static uint32_t vbios_desc_phys;
+static uint32_t vapic_bios_addr;
+
+static void update_vbios_real_tpr(void)
+{
+ cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 0);
+ vapic_bios.real_tpr = real_tpr;
+ vapic_bios.vcpu_shift = 7;
+ cpu_physical_memory_write_rom(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios);
+}
+
+static unsigned modrm_reg(uint8_t modrm)
+{
+ return (modrm >> 3) & 7;
+}
+
+static int is_abs_modrm(uint8_t modrm)
+{
+ return (modrm & 0xc7) == 0x05;
+}
+
+static int instruction_is_ok(CPUState *env, uint64_t rip, int is_write)
+{
+ uint8_t b1, b2;
+ unsigned addr_offset;
+ uint32_t addr;
+ uint64_t p;
+
+ if ((rip & 0xf0000000) != 0x80000000 && (rip & 0xf0000000) != 0xe0000000)
+ return 0;
+ if (env->regs[R_ESP] == 0)
+ return 0;
+ b1 = read_byte_virt(env, rip);
+ b2 = read_byte_virt(env, rip + 1);
+ switch (b1) {
+ case 0xc7: /* mov imm32, r/m32 (c7/0) */
+ if (modrm_reg(b2) != 0)
+ return 0;
+ /* fall through */
+ case 0x89: /* mov r32 to r/m32 */
+ case 0x8b: /* mov r/m32 to r32 */
+ if (!is_abs_modrm(b2))
+ return 0;
+ addr_offset = 2;
+ break;
+ case 0xa1: /* mov abs to eax */
+ case 0xa3: /* mov eax to abs */
+ addr_offset = 1;
+ break;
+ case 0xff: /* push r/m32 */
+ if (modrm_reg(b2) != 6 || !is_abs_modrm(b2))
+ return 0;
+ addr_offset = 2;
+ default:
+ return 0;
+ }
+ p = rip + addr_offset;
+ addr = read_byte_virt(env, p++);
+ addr |= read_byte_virt(env, p++) << 8;
+ addr |= read_byte_virt(env, p++) << 16;
+ addr |= read_byte_virt(env, p++) << 24;
+ if ((addr & 0xfff) != 0x80)
+ return 0;
+ real_tpr = addr;
+ update_vbios_real_tpr();
+ return 1;
+}
+
+static int bios_is_mapped(CPUState *env, uint64_t rip)
+{
+ uint32_t probe;
+ uint64_t phys;
+ unsigned perms;
+ uint32_t i;
+ uint32_t offset, fixup, start = vapic_bios_addr ? : 0xe0000;
+ uint32_t patch;
+
+ if (bios_enabled)
+ return 1;
+
+ probe = (rip & 0xf0000000) + start;
+ phys = map_addr(env, probe, &perms);
+ if (phys != start)
+ return 0;
+ bios_addr = probe;
+ for (i = 0; i < 64; ++i) {
+ cpu_physical_memory_read(phys, (void *)&vapic_bios, sizeof(vapic_bios));
+ if (memcmp(vapic_bios.signature, "kvm aPiC", 8) == 0)
+ break;
+ phys += 1024;
+ bios_addr += 1024;
+ }
+ if (i == 64)
+ return 0;
+ if (bios_addr == vapic_bios.virt_base)
+ return 1;
+ vbios_desc_phys = phys;
+ for (i = vapic_bios.fixup_start; i < vapic_bios.fixup_end; i += 4) {
+ offset = ldl_phys(phys + i - vapic_bios.virt_base);
+ fixup = phys + offset;
+ patch = ldl_phys(fixup) + bios_addr - vapic_bios.virt_base;
+ cpu_physical_memory_write_rom(fixup, (uint8_t *)&patch, 4);
+ }
+ vapic_phys = vapic_bios.vapic - vapic_bios.virt_base + phys;
+ return 1;
+}
+
+static int get_pcr_cpu(CPUState *env)
+{
+ uint8_t b;
+
+ cpu_synchronize_state(env);
+
+ if (cpu_memory_rw_debug(env, env->segs[R_FS].base + 0x51, &b, 1, 0) < 0)
+ return -1;
+
+ return (int)b;
+}
+
+int kvm_tpr_enable_vapic(CPUState *env)
+{
+ static uint8_t one = 1;
+ int pcr_cpu = get_pcr_cpu(env);
+
+ if (pcr_cpu < 0)
+ return 0;
+
+ kvm_enable_vapic(env, vapic_phys + (pcr_cpu << 7));
+ cpu_physical_memory_write_rom(vapic_phys + (pcr_cpu << 7) + 4, &one, 1);
+ env->kvm_vcpu_update_vapic = 0;
+ bios_enabled = 1;
+ return 1;
+}
+
+static void patch_call(CPUState *env, uint64_t rip, uint32_t target)
+{
+ uint32_t offset;
+
+ offset = target - vapic_bios.virt_base + bios_addr - rip - 5;
+ write_byte_virt(env, rip, 0xe8); /* call near */
+ write_byte_virt(env, rip + 1, offset);
+ write_byte_virt(env, rip + 2, offset >> 8);
+ write_byte_virt(env, rip + 3, offset >> 16);
+ write_byte_virt(env, rip + 4, offset >> 24);
+}
+
+static void patch_instruction(CPUState *env, uint64_t rip)
+{
+ uint8_t b1, b2;
+ struct vapic_patches *vp;
+
+ vp = smp_cpus == 1 ? &vapic_bios.up : &vapic_bios.mp;
+ b1 = read_byte_virt(env, rip);
+ b2 = read_byte_virt(env, rip + 1);
+ switch (b1) {
+ case 0x89: /* mov r32 to r/m32 */
+ write_byte_virt(env, rip, 0x50 + modrm_reg(b2)); /* push reg */
+ patch_call(env, rip + 1, vp->set_tpr);
+ break;
+ case 0x8b: /* mov r/m32 to r32 */
+ write_byte_virt(env, rip, 0x90);
+ patch_call(env, rip + 1, vp->get_tpr[modrm_reg(b2)]);
+ break;
+ case 0xa1: /* mov abs to eax */
+ patch_call(env, rip, vp->get_tpr[0]);
+ break;
+ case 0xa3: /* mov eax to abs */
+ patch_call(env, rip, vp->set_tpr_eax);
+ break;
+ case 0xc7: /* mov imm32, r/m32 (c7/0) */
+ write_byte_virt(env, rip, 0x68); /* push imm32 */
+ write_byte_virt(env, rip + 1, read_byte_virt(env, rip+6));
+ write_byte_virt(env, rip + 2, read_byte_virt(env, rip+7));
+ write_byte_virt(env, rip + 3, read_byte_virt(env, rip+8));
+ write_byte_virt(env, rip + 4, read_byte_virt(env, rip+9));
+ patch_call(env, rip + 5, vp->set_tpr);
+ break;
+ case 0xff: /* push r/m32 */
+ printf("patching push\n");
+ write_byte_virt(env, rip, 0x50); /* push eax */
+ patch_call(env, rip + 1, vp->get_tpr_stack);
+ break;
+ default:
+ printf("funny insn %02x %02x\n", b1, b2);
+ }
+}
+
+void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write)
+{
+ cpu_synchronize_state(env);
+ if (!instruction_is_ok(env, rip, is_write))
+ return;
+ if (!bios_is_mapped(env, rip))
+ return;
+ if (!kvm_tpr_enable_vapic(env))
+ return;
+ patch_instruction(env, rip);
+}
+
+static void tpr_save(QEMUFile *f, void *s)
+{
+ int i;
+
+ for (i = 0; i < (sizeof vapic_bios) / 4; ++i)
+ qemu_put_be32s(f, &((uint32_t *)&vapic_bios)[i]);
+ qemu_put_be32s(f, &bios_enabled);
+ qemu_put_be32s(f, &real_tpr);
+ qemu_put_be32s(f, &bios_addr);
+ qemu_put_be32s(f, &vapic_phys);
+ qemu_put_be32s(f, &vbios_desc_phys);
+}
+
+static int tpr_load(QEMUFile *f, void *s, int version_id)
+{
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ for (i = 0; i < (sizeof vapic_bios) / 4; ++i)
+ qemu_get_be32s(f, &((uint32_t *)&vapic_bios)[i]);
+ qemu_get_be32s(f, &bios_enabled);
+ qemu_get_be32s(f, &real_tpr);
+ qemu_get_be32s(f, &bios_addr);
+ qemu_get_be32s(f, &vapic_phys);
+ qemu_get_be32s(f, &vbios_desc_phys);
+
+ if (bios_enabled) {
+ CPUState *env = first_cpu->next_cpu;
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ env->kvm_vcpu_update_vapic = 1;
+ }
+
+ return 0;
+}
+
+static void vtpr_ioport_write16(void *opaque, uint32_t addr, uint32_t val)
+{
+ CPUState *env = cpu_single_env;
+
+ cpu_synchronize_state(env);
+
+ vapic_bios_addr = ((env->segs[R_CS].base + env->eip) & ~(512 - 1)) + val;
+ bios_enabled = 0;
+}
+
+static void vtpr_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ CPUState *env = cpu_single_env;
+ uint32_t rip;
+
+ cpu_synchronize_state(env);
+
+ rip = env->eip - 2;
+ write_byte_virt(env, rip, 0x66);
+ write_byte_virt(env, rip + 1, 0x90);
+ if (bios_enabled)
+ return;
+ if (!bios_is_mapped(env, rip))
+ printf("bios not mapped?\n");
+ for (addr = 0xfffff000u; addr >= 0x80000000u; addr -= 4096)
+ if (map_addr(env, addr, NULL) == 0xfee00000u) {
+ real_tpr = addr + 0x80;
+ break;
+ }
+ bios_enabled = 1;
+ update_vbios_real_tpr();
+ kvm_tpr_enable_vapic(env);
+}
+
+static void kvm_tpr_opt_setup(void)
+{
+ register_savevm(NULL, "kvm-tpr-opt", 0, 1, tpr_save, tpr_load, NULL);
+ register_ioport_write(0x7e, 1, 1, vtpr_ioport_write, NULL);
+ register_ioport_write(0x7e, 2, 2, vtpr_ioport_write16, NULL);
+}
+
+device_init(kvm_tpr_opt_setup);
diff --git a/kvm.h b/kvm.h
new file mode 100644
index 0000000..f06676c
--- /dev/null
+++ b/kvm.h
@@ -0,0 +1,237 @@
+/*
+ * QEMU KVM support
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_KVM_H
+#define QEMU_KVM_H
+
+#include <errno.h>
+#include "config-host.h"
+#include "qemu-queue.h"
+#ifdef NEED_CPU_H
+#include "qemu-kvm.h"
+#endif
+
+#ifdef CONFIG_KVM
+#include <linux/kvm.h>
+#endif
+
+extern int kvm_allowed;
+
+#if defined CONFIG_KVM || !defined NEED_CPU_H
+#define kvm_enabled() (kvm_allowed)
+#else
+#define kvm_enabled() (0)
+#endif
+
+#ifdef OBSOLETE_KVM_IMPL
+struct kvm_run;
+
+typedef struct KVMCapabilityInfo {
+ const char *name;
+ int value;
+} KVMCapabilityInfo;
+
+#define KVM_CAP_INFO(CAP) { "KVM_CAP_" stringify(CAP), KVM_CAP_##CAP }
+#define KVM_CAP_LAST_INFO { NULL, 0 }
+
+/* external API */
+
+int kvm_init(void);
+#endif /* OBSOLETE_KVM_IMPL */
+
+int kvm_has_sync_mmu(void);
+int kvm_has_vcpu_events(void);
+int kvm_has_robust_singlestep(void);
+int kvm_has_debugregs(void);
+int kvm_has_xsave(void);
+int kvm_has_xcrs(void);
+int kvm_has_many_ioeventfds(void);
+
+#ifdef NEED_CPU_H
+int kvm_init_vcpu(CPUState *env);
+
+int kvm_cpu_exec(CPUState *env);
+
+#if !defined(CONFIG_USER_ONLY)
+int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size);
+int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size);
+
+void kvm_cpu_register_phys_memory_client(void);
+
+void kvm_setup_guest_memory(void *start, size_t size);
+
+int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size);
+int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size);
+void kvm_flush_coalesced_mmio_buffer(void);
+#endif
+
+int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type);
+int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr,
+ target_ulong len, int type);
+void kvm_remove_all_breakpoints(CPUState *current_env);
+int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap);
+#ifndef _WIN32
+int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset);
+#endif
+
+int kvm_pit_in_kernel(void);
+
+/* internal API */
+
+struct KVMState;
+typedef struct KVMState KVMState;
+
+int kvm_ioctl(KVMState *s, int type, ...);
+
+int kvm_vm_ioctl(KVMState *s, int type, ...);
+
+int kvm_vcpu_ioctl(CPUState *env, int type, ...);
+
+/* Arch specific hooks */
+
+#ifdef OBSOLETE_KVM_IMPL
+extern const KVMCapabilityInfo kvm_arch_required_capabilities[];
+#endif
+
+int kvm_arch_post_run(CPUState *env, struct kvm_run *run);
+
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run);
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run);
+
+#ifdef OBSOLETE_KVM_IMPL
+int kvm_arch_process_irqchip_events(CPUState *env);
+#endif
+
+int kvm_arch_get_registers(CPUState *env);
+
+/* state subset only touched by the VCPU itself during runtime */
+#define KVM_PUT_RUNTIME_STATE 1
+/* state subset modified during VCPU reset */
+#define KVM_PUT_RESET_STATE 2
+/* full state set, modified during initialization or on vmload */
+#define KVM_PUT_FULL_STATE 3
+
+int kvm_arch_put_registers(CPUState *env, int level);
+
+int kvm_arch_init(KVMState *s);
+
+int kvm_arch_init_vcpu(CPUState *env);
+
+void kvm_arch_reset_vcpu(CPUState *env);
+
+int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr);
+int kvm_on_sigbus(int code, void *addr);
+
+struct kvm_guest_debug;
+struct kvm_debug_exit_arch;
+
+struct kvm_sw_breakpoint {
+ target_ulong pc;
+ target_ulong saved_insn;
+ int use_count;
+ QTAILQ_ENTRY(kvm_sw_breakpoint) entry;
+};
+
+QTAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint);
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info);
+
+struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env,
+ target_ulong pc);
+
+int kvm_sw_breakpoints_active(CPUState *env);
+
+int kvm_arch_insert_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_remove_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp);
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type);
+void kvm_arch_remove_all_hw_breakpoints(void);
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg);
+
+bool kvm_arch_stop_on_emulation_error(CPUState *env);
+
+int kvm_check_extension(KVMState *s, unsigned int extension);
+
+uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
+ uint32_t index, int reg);
+void kvm_cpu_synchronize_state(CPUState *env);
+void kvm_cpu_synchronize_post_reset(CPUState *env);
+void kvm_cpu_synchronize_post_init(CPUState *env);
+
+/* generic hooks - to be moved/refactored once there are more users */
+
+static inline void cpu_synchronize_state(CPUState *env)
+{
+ if (kvm_enabled()) {
+ kvm_cpu_synchronize_state(env);
+ }
+}
+
+static inline void cpu_synchronize_post_reset(CPUState *env)
+{
+ if (kvm_enabled()) {
+ kvm_cpu_synchronize_post_reset(env);
+ }
+}
+
+static inline void cpu_synchronize_post_init(CPUState *env)
+{
+ if (kvm_enabled()) {
+ kvm_cpu_synchronize_post_init(env);
+ }
+}
+
+#if !defined(CONFIG_USER_ONLY)
+int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr,
+ target_phys_addr_t *phys_addr);
+#endif
+
+#endif
+int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign);
+
+#if defined(KVM_IRQFD) && defined(CONFIG_KVM)
+int kvm_set_irqfd(int gsi, int fd, bool assigned);
+#else
+static inline
+int kvm_set_irqfd(int gsi, int fd, bool assigned)
+{
+ return -ENOSYS;
+}
+#endif
+
+int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign);
+
+int kvm_has_gsi_routing(void);
+int kvm_get_irq_route_gsi(void);
+int kvm_add_msix(uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data);
+int kvm_del_msix(uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data);
+int kvm_update_msix(uint32_t old_gsi, uint32_t old_addr_lo,
+ uint32_t old_addr_hi, uint32_t old_data,
+ uint32_t new_gsi, uint32_t new_addr_lo,
+ uint32_t new_addr_hi, uint32_t new_data);
+int kvm_commit_irq_routes(void);
+
+int kvm_irqchip_in_kernel(void);
+
+int kvm_set_irq(int irq, int level, int *status);
+
+#endif
diff --git a/kvm/.gitignore b/kvm/.gitignore
new file mode 100644
index 0000000..22a8200
--- /dev/null
+++ b/kvm/.gitignore
@@ -0,0 +1,66 @@
+*.o
+*.d
+*~
+*.flat
+*.a
+config.mak
+.*.cmd
+qemu/config-host.h
+qemu/config-host.mak
+user/test/bootstrap
+user/kvmctl
+qemu/dyngen
+qemu/x86_64-softmmu
+qemu/qemu-img
+qemu/qemu-nbd
+*.ko
+*.mod.c
+bios/*.bin
+bios/*.sym
+bios/*.txt
+bios/acpi-dsdt.aml
+vgabios/*.bin
+vgabios/*.txt
+extboot/extboot.bin
+extboot/extboot.img
+extboot/signrom
+kernel/config.kbuild
+kernel/modules.order
+kernel/Module.symvers
+kernel/Modules.symvers
+kernel/Module.markers
+kernel/.tmp_versions
+kernel/include-compat/asm
+kernel/include-compat/asm-x86/asm-x86
+kernel/include
+kernel/x86/modules.order
+kernel/x86/i825[49].[ch]
+kernel/x86/kvm_main.c
+kernel/x86/kvm_svm.h
+kernel/x86/vmx.[ch]
+kernel/x86/svm.[ch]
+kernel/x86/mmu.[ch]
+kernel/x86/paging_tmpl.h
+kernel/x86/x86_emulate.[ch]
+kernel/x86/ioapic.[ch]
+kernel/x86/iodev.h
+kernel/x86/irq.[ch]
+kernel/x86/kvm_trace.c
+kernel/x86/lapic.[ch]
+kernel/x86/tss.h
+kernel/x86/x86.[ch]
+kernel/x86/coalesced_mmio.[ch]
+kernel/x86/kvm_cache_regs.h
+kernel/x86/vtd.c
+kernel/x86/irq_comm.c
+kernel/x86/timer.c
+kernel/x86/kvm_timer.h
+kernel/x86/iommu.c
+qemu/pc-bios/extboot.bin
+qemu/qemu-doc.html
+qemu/*.[18]
+qemu/*.pod
+qemu/qemu-tech.html
+qemu/qemu-options.texi
+user/kvmtrace
+user/test/x86/bootstrap
diff --git a/kvm/Makefile b/kvm/Makefile
new file mode 100644
index 0000000..617504c
--- /dev/null
+++ b/kvm/Makefile
@@ -0,0 +1,125 @@
+
+include config.mak
+
+DESTDIR=
+
+rpmrelease = devel
+
+sane-arch = $(subst i386,x86,$(subst x86_64,x86,$(subst s390x,s390,$(ARCH))))
+
+.PHONY: kernel user libkvm qemu bios vgabios extboot clean libfdt cscope
+
+all: libkvm qemu
+ifneq '$(filter $(ARCH), x86_64 i386 ia64)' ''
+ all: $(if $(WANT_MODULE), kernel) user
+endif
+
+kcmd = $(if $(WANT_MODULE),,@\#)
+
+qemu kernel user libkvm:
+ $(MAKE) -C $@
+
+qemu: libkvm
+ifneq '$(filter $(ARCH), i386 x86_64)' ''
+ qemu: extboot
+endif
+ifneq '$(filter $(ARCH), powerpc ia64)' ''
+ qemu: libfdt
+endif
+user: libkvm
+
+# sync if kernel/Makefile exists and if using --with-patched-kernel
+user libkvm qemu: header-sync-$(if $(wildcard kernel/Makefile),$(if $(WANT_MODULE),n,y),n)
+
+header-sync-n:
+
+header-sync-y:
+ make -C kernel \
+ LINUX=$(if $(KERNELSOURCEDIR),$(KERNELSOURCEDIR),$(KERNELDIR)) \
+ header-sync
+ rm -f kernel/include/asm
+ ln -sf asm-$(sane-arch) kernel/include/asm
+
+bios:
+ $(MAKE) -C $@
+ cp bios/BIOS-bochs-latest qemu/pc-bios/bios.bin
+
+vgabios:
+ $(MAKE) -C $@
+ cp vgabios/VGABIOS-lgpl-latest.bin qemu/pc-bios/vgabios.bin
+ cp vgabios/VGABIOS-lgpl-latest.cirrus.bin qemu/pc-bios/vgabios-cirrus.bin
+
+extboot:
+ $(MAKE) -C $@
+ if ! [ -f qemu/pc-bios/extboot.bin ] \
+ || ! cmp -s qemu/pc-bios/extboot.bin extboot/extboot.bin; then \
+ cp extboot/extboot.bin qemu/pc-bios/extboot.bin; \
+ fi
+libfdt:
+ $(MAKE) -C $@
+
+LINUX=linux-2.6
+
+sync:
+ make -C kernel sync LINUX=$(shell readlink -f "$(LINUX)")
+
+bindir = /usr/bin
+bin = $(bindir)/kvm
+initdir = /etc/init.d
+confdir = /etc/kvm
+utilsdir = /etc/kvm/utils
+
+install-rpm:
+ mkdir -p $(DESTDIR)/$(bindir)
+ mkdir -p $(DESTDIR)/$(confdir)
+ mkdir -p $(DESTDIR)/$(initdir)
+ mkdir -p $(DESTDIR)/$(utilsdir)
+ mkdir -p $(DESTDIR)/etc/udev/rules.d
+ make -C qemu DESTDIR=$(DESTDIR)/ install
+ ln -sf /usr/kvm/bin/qemu-system-x86_64 $(DESTDIR)/$(bin)
+ install -m 755 kvm_stat $(DESTDIR)/$(bindir)/kvm_stat
+ cp scripts/kvm $(DESTDIR)/$(initdir)/kvm
+ cp scripts/qemu-ifup $(DESTDIR)/$(confdir)/qemu-ifup
+ install -t $(DESTDIR)/etc/udev/rules.d scripts/*kvm*.rules
+
+install:
+ $(kcmd)make -C kernel DESTDIR="$(DESTDIR)" install
+ make -C libkvm DESTDIR="$(DESTDIR)" install
+ make -C qemu DESTDIR="$(DESTDIR)" install
+
+tmpspec = .tmp.kvm.spec
+RPMTOPDIR = $$(pwd)/rpmtop
+
+rpm: srpm
+ rm -rf $(RPMTOPDIR)/BUILD
+ mkdir -p $(RPMTOPDIR)/{BUILD,RPMS/$$(uname -i)}
+ rpmbuild --rebuild \
+ --define="_topdir $(RPMTOPDIR)" \
+ $(RPMTOPDIR)/SRPMS/kvm-0.0-$(rpmrelease).src.rpm
+
+srpm:
+ mkdir -p $(RPMTOPDIR)/{SOURCES,SRPMS}
+ sed 's/^Release:.*/Release: $(rpmrelease)/' kvm.spec > $(tmpspec)
+ tar czf $(RPMTOPDIR)/SOURCES/kvm.tar.gz qemu
+ tar czf $(RPMTOPDIR)/SOURCES/user.tar.gz user
+ tar czf $(RPMTOPDIR)/SOURCES/libkvm.tar.gz libkvm
+ tar czf $(RPMTOPDIR)/SOURCES/kernel.tar.gz kernel
+ tar czf $(RPMTOPDIR)/SOURCES/scripts.tar.gz scripts
+ tar czf $(RPMTOPDIR)/SOURCES/extboot.tar.gz extboot
+ cp Makefile configure kvm_stat $(RPMTOPDIR)/SOURCES
+ rpmbuild --define="_topdir $(RPMTOPDIR)" -bs $(tmpspec)
+ $(RM) $(tmpspec)
+
+clean:
+ for i in $(if $(WANT_MODULE), kernel) user libkvm qemu libfdt; do \
+ make -C $$i clean; \
+ done
+ rm -f ./cscope.*
+
+distclean: clean
+ rm -f config.mak user/config.mak
+
+cscope:
+ rm -f ./cscope.*
+ find . -wholename './kernel' -prune -o -name "*.[ch]" -print > ./cscope.files
+ cscope -b
diff --git a/kvm/configure b/kvm/configure
new file mode 100755
index 0000000..a41ab72
--- /dev/null
+++ b/kvm/configure
@@ -0,0 +1,142 @@
+#!/bin/bash
+
+prefix=/usr/local
+kerneldir=/lib/modules/$(uname -r)/build
+cc=gcc
+ld=ld
+objcopy=objcopy
+ar=ar
+want_module=1
+qemu_cflags=
+qemu_ldflags=
+kvm_trace=
+qemu_opts=()
+cross_prefix=
+arch=`uname -m`
+target_exec=
+# don't use uname if kerneldir is set
+no_uname=
+if [ -z "TMPDIR" ] ; then
+ TMPDIR=.
+fi
+
+if [ ! -e kernel/Makefile ]; then
+ want_module=
+fi
+
+usage() {
+ cat <<-EOF
+ Usage: $0 [options]
+
+ Options include:
+ --arch=ARCH architecture to compile for ($arch)
+ --cross-prefix=PREFIX prefix for cross compile
+ --prefix=PREFIX where to install things ($prefix)
+ --with-patched-kernel don't use external module
+ --with-kvm-trace Enable kvm_trace
+ --kerneldir=DIR kernel build directory ($kerneldir)
+ --qemu-cflags=CFLAGS CFLAGS to add to qemu configuration
+ --qemu-ldflags=LDFLAGS LDFLAGS to add to qemu configuration
+
+EOF
+ exit 1
+}
+
+while [[ "$1" = -* ]]; do
+ opt="$1"; shift
+ arg=
+ hasarg=
+ if [[ "$opt" = *=* ]]; then
+ arg="${opt#*=}"
+ opt="${opt%%=*}"
+ hasarg=1
+ fi
+ case "$opt" in
+ --prefix)
+ prefix="$arg"
+ ;;
+ --kerneldir)
+ kerneldir="$arg"
+ no_uname=1
+ ;;
+ --with-patched-kernel)
+ want_module=
+ ;;
+ --with-kvm-trace)
+ kvm_trace=y
+ ;;
+ --qemu-cflags)
+ qemu_cflags="$arg"
+ ;;
+ --qemu-ldflags)
+ qemu_ldflags="$arg"
+ ;;
+ --arch)
+ arch="$arg"
+ ;;
+ --cross-prefix)
+ cross_prefix="$arg"
+ ;;
+ --help)
+ usage
+ ;;
+ *)
+ qemu_opts=("${qemu_opts[@]}" "$opt${hasarg:+=$arg}")
+ ;;
+ esac
+done
+
+
+#set kenel directory
+libkvm_kerneldir=$(readlink -f kernel)
+
+case $arch in
+ i?86*|x86_64*)
+ arch=${arch/#i?86/i386}
+ target_exec="x86_64-softmmu"
+ qemu_cflags="$qemu_cflags -DCONFIG_X86"
+ ;;
+ ia64*)
+ target_exec="ia64-softmmu"
+ ;;
+ powerpc*)
+ target_exec="ppcemb-softmmu"
+ qemu_cflags="$qemu_cflags -I $PWD/libfdt"
+ qemu_ldflags="$qemu_ldflags -L $PWD/libfdt"
+ ;;
+esac
+
+processor=${arch#*-}
+arch=${arch%%-*}
+
+#configure kernel module
+[ -e kernel/Makefile ] && (cd kernel;
+ ./configure \
+ --kerneldir="$kerneldir" \
+ --arch="$arch" \
+ $([ -z ${want_module} ] && echo "--with-patched-kernel") \
+ ${cross_prefix:+"--cross-prefix=$cross_prefix"} \
+ ${kvm_trace:+"--with-kvm-trace"}
+)
+
+#configure user dir
+(cd user; ./configure --prefix="$prefix" --kerneldir="$libkvm_kerneldir" \
+ --arch="$arch" --processor="$processor" \
+ ${cross_prefix:+"--cross-prefix=$cross_prefix"})
+
+
+
+cat <<EOF > config.mak
+ARCH=$arch
+PROCESSOR=$processor
+PREFIX=$prefix
+KERNELDIR=$kerneldir
+KERNELSOURCEDIR=$kernelsourcedir
+LIBKVM_KERNELDIR=$libkvm_kerneldir
+WANT_MODULE=$want_module
+CROSS_COMPILE=$cross_prefix
+CC=$cross_prefix$cc
+LD=$cross_prefix$ld
+OBJCOPY=$cross_prefix$objcopy
+AR=$cross_prefix$ar
+EOF
diff --git a/kvm/extboot/Makefile b/kvm/extboot/Makefile
new file mode 100644
index 0000000..ab2dae7
--- /dev/null
+++ b/kvm/extboot/Makefile
@@ -0,0 +1,41 @@
+OBJCOPY=objcopy
+
+# from kernel sources - scripts/Kbuild.include
+# try-run
+# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
+# Exit code chooses option. "$$TMP" is can be used as temporary file and
+# is automatically cleaned up.
+try-run = $(shell set -e; \
+ TMP="$(TMPOUT).$$$$.tmp"; \
+ if ($(1)) >/dev/null 2>&1; \
+ then echo "$(2)"; \
+ else echo "$(3)"; \
+ fi; \
+ rm -f "$$TMP")
+
+# cc-option-yn
+# Usage: flag := $(call cc-option-yn,-march=winchip-c6)
+cc-option-yn = $(call try-run,\
+ $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",y,n)
+
+CFLAGS = -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin
+ifeq ($(call cc-option-yn,-fno-stack-protector),y)
+CFLAGS += -fno-stack-protector
+endif
+
+all: extboot.bin
+
+%.o: %.S
+ $(CC) $(CFLAGS) -o $@ -c $<
+
+extboot.img: extboot.o
+ $(LD) --oformat binary -Ttext 0 -o $@ $<
+
+extboot.bin: extboot.img signrom
+ ./signrom extboot.img extboot.bin
+
+signrom: signrom.c
+ $(CC) -o $@ -g -Wall $^
+
+clean:
+ $(RM) *.o *.img *.bin signrom *~
diff --git a/kvm/extboot/STATUS b/kvm/extboot/STATUS
new file mode 100644
index 0000000..687c6d6
--- /dev/null
+++ b/kvm/extboot/STATUS
@@ -0,0 +1,6 @@
+Working
+-------
+
+Ubuntu Server 7.04 (i386)
+Windows 2000 Professional (i386)
+Windows XP SP2 (i386)
diff --git a/kvm/extboot/signrom.c b/kvm/extboot/signrom.c
new file mode 100644
index 0000000..fe8d677
--- /dev/null
+++ b/kvm/extboot/signrom.c
@@ -0,0 +1,79 @@
+/*
+ * Extended Boot Option ROM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ * Authors: Anthony Liguori <aliguori@us.ibm.com>
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ FILE *fin, *fout;
+ char buffer[512], oldbuffer[512];
+ int i, size, lag = 0;
+ uint8_t sum = 0;
+
+ if (argc != 3) {
+ printf("Usage: %s ROM OUTPUT\n", argv[0]);
+ return 1;
+ }
+
+ fin = fopen(argv[1], "rb");
+ fout = fopen(argv[2], "wb");
+
+ if (fin == NULL || fout == NULL) {
+ fprintf(stderr, "Could not open input/output files\n");
+ return 1;
+ }
+
+ do {
+ size = fread(buffer, 512, 1, fin);
+ if (size == 1) {
+ for (i = 0; i < 512; i++)
+ sum += buffer[i];
+
+ if (lag) {
+ if (fwrite(oldbuffer, 512, 1, fout) != 1) {
+ fprintf(stderr, "Write failed\n");
+ return 1;
+ }
+ }
+ lag = 1;
+ memcpy(oldbuffer, buffer, 512);
+ }
+ } while (size == 1);
+
+ if (size != 0) {
+ fprintf(stderr, "Failed to read from input file\n");
+ return 1;
+ }
+
+ oldbuffer[511] = -sum;
+
+ if (fwrite(oldbuffer, 512, 1, fout) != 1) {
+ fprintf(stderr, "Failed to write to output file\n");
+ return 1;
+ }
+
+ fclose(fin);
+ fclose(fout);
+
+ return 0;
+}
diff --git a/kvm/include/ia64/asm/kvm.h b/kvm/include/ia64/asm/kvm.h
new file mode 100644
index 0000000..bc90c75
--- /dev/null
+++ b/kvm/include/ia64/asm/kvm.h
@@ -0,0 +1,264 @@
+#ifndef __ASM_IA64_KVM_H
+#define __ASM_IA64_KVM_H
+
+/*
+ * kvm structure definitions for ia64
+ *
+ * Copyright (C) 2007 Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Select x86 specific features in <linux/kvm.h> */
+#define __KVM_HAVE_IOAPIC
+#define __KVM_HAVE_DEVICE_ASSIGNMENT
+
+/* Architectural interrupt line count. */
+#define KVM_NR_INTERRUPTS 256
+
+#define KVM_IOAPIC_NUM_PINS 48
+
+struct kvm_ioapic_state {
+ __u64 base_address;
+ __u32 ioregsel;
+ __u32 id;
+ __u32 irr;
+ __u32 pad;
+ union {
+ __u64 bits;
+ struct {
+ __u8 vector;
+ __u8 delivery_mode:3;
+ __u8 dest_mode:1;
+ __u8 delivery_status:1;
+ __u8 polarity:1;
+ __u8 remote_irr:1;
+ __u8 trig_mode:1;
+ __u8 mask:1;
+ __u8 reserve:7;
+ __u8 reserved[4];
+ __u8 dest_id;
+ } fields;
+ } redirtbl[KVM_IOAPIC_NUM_PINS];
+};
+
+#define KVM_IRQCHIP_PIC_MASTER 0
+#define KVM_IRQCHIP_PIC_SLAVE 1
+#define KVM_IRQCHIP_IOAPIC 2
+#define KVM_NR_IRQCHIPS 3
+
+#define KVM_CONTEXT_SIZE 8*1024
+
+struct kvm_fpreg {
+ union {
+ unsigned long bits[2];
+ long double __dummy; /* force 16-byte alignment */
+ } u;
+};
+
+union context {
+ /* 8K size */
+ char dummy[KVM_CONTEXT_SIZE];
+ struct {
+ unsigned long psr;
+ unsigned long pr;
+ unsigned long caller_unat;
+ unsigned long pad;
+ unsigned long gr[32];
+ unsigned long ar[128];
+ unsigned long br[8];
+ unsigned long cr[128];
+ unsigned long rr[8];
+ unsigned long ibr[8];
+ unsigned long dbr[8];
+ unsigned long pkr[8];
+ struct kvm_fpreg fr[128];
+ };
+};
+
+struct thash_data {
+ union {
+ struct {
+ unsigned long p : 1; /* 0 */
+ unsigned long rv1 : 1; /* 1 */
+ unsigned long ma : 3; /* 2-4 */
+ unsigned long a : 1; /* 5 */
+ unsigned long d : 1; /* 6 */
+ unsigned long pl : 2; /* 7-8 */
+ unsigned long ar : 3; /* 9-11 */
+ unsigned long ppn : 38; /* 12-49 */
+ unsigned long rv2 : 2; /* 50-51 */
+ unsigned long ed : 1; /* 52 */
+ unsigned long ig1 : 11; /* 53-63 */
+ };
+ struct {
+ unsigned long __rv1 : 53; /* 0-52 */
+ unsigned long contiguous : 1; /*53 */
+ unsigned long tc : 1; /* 54 TR or TC */
+ unsigned long cl : 1;
+ /* 55 I side or D side cache line */
+ unsigned long len : 4; /* 56-59 */
+ unsigned long io : 1; /* 60 entry is for io or not */
+ unsigned long nomap : 1;
+ /* 61 entry cann't be inserted into machine TLB.*/
+ unsigned long checked : 1;
+ /* 62 for VTLB/VHPT sanity check */
+ unsigned long invalid : 1;
+ /* 63 invalid entry */
+ };
+ unsigned long page_flags;
+ }; /* same for VHPT and TLB */
+
+ union {
+ struct {
+ unsigned long rv3 : 2;
+ unsigned long ps : 6;
+ unsigned long key : 24;
+ unsigned long rv4 : 32;
+ };
+ unsigned long itir;
+ };
+ union {
+ struct {
+ unsigned long ig2 : 12;
+ unsigned long vpn : 49;
+ unsigned long vrn : 3;
+ };
+ unsigned long ifa;
+ unsigned long vadr;
+ struct {
+ unsigned long tag : 63;
+ unsigned long ti : 1;
+ };
+ unsigned long etag;
+ };
+ union {
+ struct thash_data *next;
+ unsigned long rid;
+ unsigned long gpaddr;
+ };
+};
+
+#define NITRS 8
+#define NDTRS 8
+
+struct saved_vpd {
+ unsigned long vhpi;
+ unsigned long vgr[16];
+ unsigned long vbgr[16];
+ unsigned long vnat;
+ unsigned long vbnat;
+ unsigned long vcpuid[5];
+ unsigned long vpsr;
+ unsigned long vpr;
+ union {
+ unsigned long vcr[128];
+ struct {
+ unsigned long dcr;
+ unsigned long itm;
+ unsigned long iva;
+ unsigned long rsv1[5];
+ unsigned long pta;
+ unsigned long rsv2[7];
+ unsigned long ipsr;
+ unsigned long isr;
+ unsigned long rsv3;
+ unsigned long iip;
+ unsigned long ifa;
+ unsigned long itir;
+ unsigned long iipa;
+ unsigned long ifs;
+ unsigned long iim;
+ unsigned long iha;
+ unsigned long rsv4[38];
+ unsigned long lid;
+ unsigned long ivr;
+ unsigned long tpr;
+ unsigned long eoi;
+ unsigned long irr[4];
+ unsigned long itv;
+ unsigned long pmv;
+ unsigned long cmcv;
+ unsigned long rsv5[5];
+ unsigned long lrr0;
+ unsigned long lrr1;
+ unsigned long rsv6[46];
+ };
+ };
+};
+
+struct kvm_regs {
+ struct saved_vpd vpd;
+ /*Arch-regs*/
+ int mp_state;
+ unsigned long vmm_rr;
+ /* TR and TC. */
+ struct thash_data itrs[NITRS];
+ struct thash_data dtrs[NDTRS];
+ /* Bit is set if there is a tr/tc for the region. */
+ unsigned char itr_regions;
+ unsigned char dtr_regions;
+ unsigned char tc_regions;
+
+ char irq_check;
+ unsigned long saved_itc;
+ unsigned long itc_check;
+ unsigned long timer_check;
+ unsigned long timer_pending;
+ unsigned long last_itc;
+
+ unsigned long vrr[8];
+ unsigned long ibr[8];
+ unsigned long dbr[8];
+ unsigned long insvc[4]; /* Interrupt in service. */
+ unsigned long xtp;
+
+ unsigned long metaphysical_rr0; /* from kvm_arch (so is pinned) */
+ unsigned long metaphysical_rr4; /* from kvm_arch (so is pinned) */
+ unsigned long metaphysical_saved_rr0; /* from kvm_arch */
+ unsigned long metaphysical_saved_rr4; /* from kvm_arch */
+ unsigned long fp_psr; /*used for lazy float register */
+ unsigned long saved_gp;
+ /*for phycial emulation */
+
+ union context saved_guest;
+
+ unsigned long reserved[64]; /* for future use */
+};
+
+struct kvm_sregs {
+};
+
+struct kvm_fpu {
+};
+
+#define KVM_IA64_VCPU_STACK_SHIFT 16
+#define KVM_IA64_VCPU_STACK_SIZE (1UL << KVM_IA64_VCPU_STACK_SHIFT)
+
+struct kvm_ia64_vcpu_stack {
+ unsigned char stack[KVM_IA64_VCPU_STACK_SIZE];
+};
+
+struct kvm_debug_exit_arch {
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+};
+
+#endif
diff --git a/kvm/include/ia64/asm/kvm_para.h b/kvm/include/ia64/asm/kvm_para.h
new file mode 100644
index 0000000..1588aee
--- /dev/null
+++ b/kvm/include/ia64/asm/kvm_para.h
@@ -0,0 +1,31 @@
+#ifndef __IA64_KVM_PARA_H
+#define __IA64_KVM_PARA_H
+
+/*
+ * Copyright (C) 2007 Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#ifdef __KERNEL__
+
+static inline unsigned int kvm_arch_para_features(void)
+{
+ return 0;
+}
+
+#endif
+
+#endif
diff --git a/kvm/include/linux/compiler.h b/kvm/include/linux/compiler.h
new file mode 100644
index 0000000..f70c49f
--- /dev/null
+++ b/kvm/include/linux/compiler.h
@@ -0,0 +1,2 @@
+/* dummy file */
+
diff --git a/kvm/include/linux/config.h b/kvm/include/linux/config.h
new file mode 100644
index 0000000..a421f31
--- /dev/null
+++ b/kvm/include/linux/config.h
@@ -0,0 +1,43 @@
+#ifndef KVM_UNIFDEF_H
+#define KVM_UNIFDEF_H
+
+#ifdef __i386__
+#ifndef CONFIG_X86_32
+#define CONFIG_X86_32 1
+#endif
+#endif
+
+#ifdef __x86_64__
+#ifndef CONFIG_X86_64
+#define CONFIG_X86_64 1
+#endif
+#endif
+
+#if defined(__i386__) || defined (__x86_64__)
+#ifndef CONFIG_X86
+#define CONFIG_X86 1
+#endif
+#endif
+
+#ifdef __ia64__
+#ifndef CONFIG_IA64
+#define CONFIG_IA64 1
+#endif
+#endif
+
+#ifdef __PPC__
+#ifndef CONFIG_PPC
+#define CONFIG_PPC 1
+#endif
+#endif
+
+#ifdef __s390__
+#ifndef CONFIG_S390
+#define CONFIG_S390 1
+#endif
+#endif
+
+#endif
+
+
+#define __user
diff --git a/kvm/include/linux/kvm.h b/kvm/include/linux/kvm.h
new file mode 100644
index 0000000..e46729e
--- /dev/null
+++ b/kvm/include/linux/kvm.h
@@ -0,0 +1,784 @@
+#ifndef __LINUX_KVM_H
+#define __LINUX_KVM_H
+
+/*
+ * Userspace interface for /dev/kvm - kernel based virtual machine
+ *
+ * Note: you must update KVM_API_VERSION if you change this interface.
+ */
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/ioctl.h>
+#include <asm/kvm.h>
+
+#define KVM_API_VERSION 12
+
+/* *** Deprecated interfaces *** */
+
+#define KVM_TRC_SHIFT 16
+
+#define KVM_TRC_ENTRYEXIT (1 << KVM_TRC_SHIFT)
+#define KVM_TRC_HANDLER (1 << (KVM_TRC_SHIFT + 1))
+
+#define KVM_TRC_VMENTRY (KVM_TRC_ENTRYEXIT + 0x01)
+#define KVM_TRC_VMEXIT (KVM_TRC_ENTRYEXIT + 0x02)
+#define KVM_TRC_PAGE_FAULT (KVM_TRC_HANDLER + 0x01)
+
+#define KVM_TRC_HEAD_SIZE 12
+#define KVM_TRC_CYCLE_SIZE 8
+#define KVM_TRC_EXTRA_MAX 7
+
+#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02)
+#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03)
+#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04)
+#define KVM_TRC_IO_READ (KVM_TRC_HANDLER + 0x05)
+#define KVM_TRC_IO_WRITE (KVM_TRC_HANDLER + 0x06)
+#define KVM_TRC_CR_READ (KVM_TRC_HANDLER + 0x07)
+#define KVM_TRC_CR_WRITE (KVM_TRC_HANDLER + 0x08)
+#define KVM_TRC_DR_READ (KVM_TRC_HANDLER + 0x09)
+#define KVM_TRC_DR_WRITE (KVM_TRC_HANDLER + 0x0A)
+#define KVM_TRC_MSR_READ (KVM_TRC_HANDLER + 0x0B)
+#define KVM_TRC_MSR_WRITE (KVM_TRC_HANDLER + 0x0C)
+#define KVM_TRC_CPUID (KVM_TRC_HANDLER + 0x0D)
+#define KVM_TRC_INTR (KVM_TRC_HANDLER + 0x0E)
+#define KVM_TRC_NMI (KVM_TRC_HANDLER + 0x0F)
+#define KVM_TRC_VMMCALL (KVM_TRC_HANDLER + 0x10)
+#define KVM_TRC_HLT (KVM_TRC_HANDLER + 0x11)
+#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12)
+#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13)
+#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14)
+#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15)
+#define KVM_TRC_GTLB_WRITE (KVM_TRC_HANDLER + 0x16)
+#define KVM_TRC_STLB_WRITE (KVM_TRC_HANDLER + 0x17)
+#define KVM_TRC_STLB_INVAL (KVM_TRC_HANDLER + 0x18)
+#define KVM_TRC_PPC_INSTR (KVM_TRC_HANDLER + 0x19)
+
+struct kvm_user_trace_setup {
+ __u32 buf_size;
+ __u32 buf_nr;
+};
+
+#define __KVM_DEPRECATED_MAIN_W_0x06 \
+ _IOW(KVMIO, 0x06, struct kvm_user_trace_setup)
+#define __KVM_DEPRECATED_MAIN_0x07 _IO(KVMIO, 0x07)
+#define __KVM_DEPRECATED_MAIN_0x08 _IO(KVMIO, 0x08)
+
+#define __KVM_DEPRECATED_VM_R_0x70 _IOR(KVMIO, 0x70, struct kvm_assigned_irq)
+
+struct kvm_breakpoint {
+ __u32 enabled;
+ __u32 padding;
+ __u64 address;
+};
+
+struct kvm_debug_guest {
+ __u32 enabled;
+ __u32 pad;
+ struct kvm_breakpoint breakpoints[4];
+ __u32 singlestep;
+};
+
+#define __KVM_DEPRECATED_VCPU_W_0x87 _IOW(KVMIO, 0x87, struct kvm_debug_guest)
+
+/* *** End of deprecated interfaces *** */
+
+
+/* for KVM_CREATE_MEMORY_REGION */
+struct kvm_memory_region {
+ __u32 slot;
+ __u32 flags;
+ __u64 guest_phys_addr;
+ __u64 memory_size; /* bytes */
+};
+
+/* for KVM_SET_USER_MEMORY_REGION */
+struct kvm_userspace_memory_region {
+ __u32 slot;
+ __u32 flags;
+ __u64 guest_phys_addr;
+ __u64 memory_size; /* bytes */
+ __u64 userspace_addr; /* start of the userspace allocated memory */
+};
+
+/* for kvm_memory_region::flags */
+#define KVM_MEM_LOG_DIRTY_PAGES 1UL
+#define KVM_MEMSLOT_INVALID (1UL << 1)
+
+/* for KVM_IRQ_LINE */
+struct kvm_irq_level {
+ /*
+ * ACPI gsi notion of irq.
+ * For IA-64 (APIC model) IOAPIC0: irq 0-23; IOAPIC1: irq 24-47..
+ * For X86 (standard AT mode) PIC0/1: irq 0-15. IOAPIC0: 0-23..
+ */
+ union {
+ __u32 irq;
+ __s32 status;
+ };
+ __u32 level;
+};
+
+
+struct kvm_irqchip {
+ __u32 chip_id;
+ __u32 pad;
+ union {
+ char dummy[512]; /* reserving space */
+#ifdef __KVM_HAVE_PIT
+ struct kvm_pic_state pic;
+#endif
+#ifdef __KVM_HAVE_IOAPIC
+ struct kvm_ioapic_state ioapic;
+#endif
+ } chip;
+};
+
+/* for KVM_CREATE_PIT2 */
+struct kvm_pit_config {
+ __u32 flags;
+ __u32 pad[15];
+};
+
+#define KVM_PIT_SPEAKER_DUMMY 1
+
+#define KVM_EXIT_UNKNOWN 0
+#define KVM_EXIT_EXCEPTION 1
+#define KVM_EXIT_IO 2
+#define KVM_EXIT_HYPERCALL 3
+#define KVM_EXIT_DEBUG 4
+#define KVM_EXIT_HLT 5
+#define KVM_EXIT_MMIO 6
+#define KVM_EXIT_IRQ_WINDOW_OPEN 7
+#define KVM_EXIT_SHUTDOWN 8
+#define KVM_EXIT_FAIL_ENTRY 9
+#define KVM_EXIT_INTR 10
+#define KVM_EXIT_SET_TPR 11
+#define KVM_EXIT_TPR_ACCESS 12
+#define KVM_EXIT_S390_SIEIC 13
+#define KVM_EXIT_S390_RESET 14
+#define KVM_EXIT_DCR 15
+#define KVM_EXIT_NMI 16
+#define KVM_EXIT_INTERNAL_ERROR 17
+#define KVM_EXIT_OSI 18
+
+/* For KVM_EXIT_INTERNAL_ERROR */
+#define KVM_INTERNAL_ERROR_EMULATION 1
+#define KVM_INTERNAL_ERROR_SIMUL_EX 2
+
+/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */
+struct kvm_run {
+ /* in */
+ __u8 request_interrupt_window;
+ __u8 padding1[7];
+
+ /* out */
+ __u32 exit_reason;
+ __u8 ready_for_interrupt_injection;
+ __u8 if_flag;
+ __u8 padding2[2];
+
+ /* in (pre_kvm_run), out (post_kvm_run) */
+ __u64 cr8;
+ __u64 apic_base;
+
+#ifdef __KVM_S390
+ /* the processor status word for s390 */
+ __u64 psw_mask; /* psw upper half */
+ __u64 psw_addr; /* psw lower half */
+#endif
+ union {
+ /* KVM_EXIT_UNKNOWN */
+ struct {
+ __u64 hardware_exit_reason;
+ } hw;
+ /* KVM_EXIT_FAIL_ENTRY */
+ struct {
+ __u64 hardware_entry_failure_reason;
+ } fail_entry;
+ /* KVM_EXIT_EXCEPTION */
+ struct {
+ __u32 exception;
+ __u32 error_code;
+ } ex;
+ /* KVM_EXIT_IO */
+ struct {
+#define KVM_EXIT_IO_IN 0
+#define KVM_EXIT_IO_OUT 1
+ __u8 direction;
+ __u8 size; /* bytes */
+ __u16 port;
+ __u32 count;
+ __u64 data_offset; /* relative to kvm_run start */
+ } io;
+ struct {
+ struct kvm_debug_exit_arch arch;
+ } debug;
+ /* KVM_EXIT_MMIO */
+ struct {
+ __u64 phys_addr;
+ __u8 data[8];
+ __u32 len;
+ __u8 is_write;
+ } mmio;
+ /* KVM_EXIT_HYPERCALL */
+ struct {
+ __u64 nr;
+ __u64 args[6];
+ __u64 ret;
+ __u32 longmode;
+ __u32 pad;
+ } hypercall;
+ /* KVM_EXIT_TPR_ACCESS */
+ struct {
+ __u64 rip;
+ __u32 is_write;
+ __u32 pad;
+ } tpr_access;
+ /* KVM_EXIT_S390_SIEIC */
+ struct {
+ __u8 icptcode;
+ __u16 ipa;
+ __u32 ipb;
+ } s390_sieic;
+ /* KVM_EXIT_S390_RESET */
+#define KVM_S390_RESET_POR 1
+#define KVM_S390_RESET_CLEAR 2
+#define KVM_S390_RESET_SUBSYSTEM 4
+#define KVM_S390_RESET_CPU_INIT 8
+#define KVM_S390_RESET_IPL 16
+ __u64 s390_reset_flags;
+ /* KVM_EXIT_DCR */
+ struct {
+ __u32 dcrn;
+ __u32 data;
+ __u8 is_write;
+ } dcr;
+ struct {
+ __u32 suberror;
+ /* Available with KVM_CAP_INTERNAL_ERROR_DATA: */
+ __u32 ndata;
+ __u64 data[16];
+ } internal;
+ /* KVM_EXIT_OSI */
+ struct {
+ __u64 gprs[32];
+ } osi;
+ /* Fix the size of the union. */
+ char padding[256];
+ };
+};
+
+/* for KVM_REGISTER_COALESCED_MMIO / KVM_UNREGISTER_COALESCED_MMIO */
+
+struct kvm_coalesced_mmio_zone {
+ __u64 addr;
+ __u32 size;
+ __u32 pad;
+};
+
+struct kvm_coalesced_mmio {
+ __u64 phys_addr;
+ __u32 len;
+ __u32 pad;
+ __u8 data[8];
+};
+
+struct kvm_coalesced_mmio_ring {
+ __u32 first, last;
+ struct kvm_coalesced_mmio coalesced_mmio[0];
+};
+
+#define KVM_COALESCED_MMIO_MAX \
+ ((PAGE_SIZE - sizeof(struct kvm_coalesced_mmio_ring)) / \
+ sizeof(struct kvm_coalesced_mmio))
+
+/* for KVM_TRANSLATE */
+struct kvm_translation {
+ /* in */
+ __u64 linear_address;
+
+ /* out */
+ __u64 physical_address;
+ __u8 valid;
+ __u8 writeable;
+ __u8 usermode;
+ __u8 pad[5];
+};
+
+/* for KVM_INTERRUPT */
+struct kvm_interrupt {
+ /* in */
+ __u32 irq;
+};
+
+/* for KVM_GET_DIRTY_LOG */
+struct kvm_dirty_log {
+ __u32 slot;
+ __u32 padding1;
+ union {
+ void *dirty_bitmap; /* one bit per page */
+ __u64 padding2;
+ };
+};
+
+/* for KVM_SET_SIGNAL_MASK */
+struct kvm_signal_mask {
+ __u32 len;
+ __u8 sigset[0];
+};
+
+/* for KVM_TPR_ACCESS_REPORTING */
+struct kvm_tpr_access_ctl {
+ __u32 enabled;
+ __u32 flags;
+ __u32 reserved[8];
+};
+
+/* for KVM_SET_VAPIC_ADDR */
+struct kvm_vapic_addr {
+ __u64 vapic_addr;
+};
+
+/* for KVM_SET_MPSTATE */
+
+#define KVM_MP_STATE_RUNNABLE 0
+#define KVM_MP_STATE_UNINITIALIZED 1
+#define KVM_MP_STATE_INIT_RECEIVED 2
+#define KVM_MP_STATE_HALTED 3
+#define KVM_MP_STATE_SIPI_RECEIVED 4
+
+struct kvm_mp_state {
+ __u32 mp_state;
+};
+
+struct kvm_s390_psw {
+ __u64 mask;
+ __u64 addr;
+};
+
+/* valid values for type in kvm_s390_interrupt */
+#define KVM_S390_SIGP_STOP 0xfffe0000u
+#define KVM_S390_PROGRAM_INT 0xfffe0001u
+#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u
+#define KVM_S390_RESTART 0xfffe0003u
+#define KVM_S390_INT_VIRTIO 0xffff2603u
+#define KVM_S390_INT_SERVICE 0xffff2401u
+#define KVM_S390_INT_EMERGENCY 0xffff1201u
+
+struct kvm_s390_interrupt {
+ __u32 type;
+ __u32 parm;
+ __u64 parm64;
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+
+#define KVM_GUESTDBG_ENABLE 0x00000001
+#define KVM_GUESTDBG_SINGLESTEP 0x00000002
+
+struct kvm_guest_debug {
+ __u32 control;
+ __u32 pad;
+ struct kvm_guest_debug_arch arch;
+};
+
+enum {
+ kvm_ioeventfd_flag_nr_datamatch,
+ kvm_ioeventfd_flag_nr_pio,
+ kvm_ioeventfd_flag_nr_deassign,
+ kvm_ioeventfd_flag_nr_max,
+};
+
+#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 << kvm_ioeventfd_flag_nr_datamatch)
+#define KVM_IOEVENTFD_FLAG_PIO (1 << kvm_ioeventfd_flag_nr_pio)
+#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign)
+
+#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1)
+
+struct kvm_ioeventfd {
+ __u64 datamatch;
+ __u64 addr; /* legal pio/mmio address */
+ __u32 len; /* 1, 2, 4, or 8 bytes */
+ __s32 fd;
+ __u32 flags;
+ __u8 pad[36];
+};
+
+/* for KVM_ENABLE_CAP */
+struct kvm_enable_cap {
+ /* in */
+ __u32 cap;
+ __u32 flags;
+ __u64 args[4];
+ __u8 pad[64];
+};
+
+#define KVMIO 0xAE
+
+/*
+ * ioctls for /dev/kvm fds:
+ */
+#define KVM_GET_API_VERSION _IO(KVMIO, 0x00)
+#define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */
+#define KVM_GET_MSR_INDEX_LIST _IOWR(KVMIO, 0x02, struct kvm_msr_list)
+
+#define KVM_S390_ENABLE_SIE _IO(KVMIO, 0x06)
+/*
+ * Check if a kvm extension is available. Argument is extension number,
+ * return is 1 (yes) or 0 (no, sorry).
+ */
+#define KVM_CHECK_EXTENSION _IO(KVMIO, 0x03)
+/*
+ * Get size for mmap(vcpu_fd)
+ */
+#define KVM_GET_VCPU_MMAP_SIZE _IO(KVMIO, 0x04) /* in bytes */
+#define KVM_GET_SUPPORTED_CPUID _IOWR(KVMIO, 0x05, struct kvm_cpuid2)
+#define KVM_TRACE_ENABLE __KVM_DEPRECATED_MAIN_W_0x06
+#define KVM_TRACE_PAUSE __KVM_DEPRECATED_MAIN_0x07
+#define KVM_TRACE_DISABLE __KVM_DEPRECATED_MAIN_0x08
+
+/*
+ * Extension capability list.
+ */
+#define KVM_CAP_IRQCHIP 0
+#define KVM_CAP_HLT 1
+#define KVM_CAP_MMU_SHADOW_CACHE_CONTROL 2
+#define KVM_CAP_USER_MEMORY 3
+#define KVM_CAP_SET_TSS_ADDR 4
+#define KVM_CAP_VAPIC 6
+#define KVM_CAP_EXT_CPUID 7
+#define KVM_CAP_CLOCKSOURCE 8
+#define KVM_CAP_NR_VCPUS 9 /* returns max vcpus per vm */
+#define KVM_CAP_NR_MEMSLOTS 10 /* returns max memory slots per vm */
+#define KVM_CAP_PIT 11
+#define KVM_CAP_NOP_IO_DELAY 12
+#define KVM_CAP_PV_MMU 13
+#define KVM_CAP_MP_STATE 14
+#define KVM_CAP_COALESCED_MMIO 15
+#define KVM_CAP_SYNC_MMU 16 /* Changes to host mmap are reflected in guest */
+#ifdef __KVM_HAVE_DEVICE_ASSIGNMENT
+#define KVM_CAP_DEVICE_ASSIGNMENT 17
+#endif
+#define KVM_CAP_IOMMU 18
+#ifdef __KVM_HAVE_MSI
+#define KVM_CAP_DEVICE_MSI 20
+#endif
+/* Bug in KVM_SET_USER_MEMORY_REGION fixed: */
+#define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21
+#ifdef __KVM_HAVE_USER_NMI
+#define KVM_CAP_USER_NMI 22
+#endif
+#ifdef __KVM_HAVE_GUEST_DEBUG
+#define KVM_CAP_SET_GUEST_DEBUG 23
+#endif
+#ifdef __KVM_HAVE_PIT
+#define KVM_CAP_REINJECT_CONTROL 24
+#endif
+#ifdef __KVM_HAVE_IOAPIC
+#define KVM_CAP_IRQ_ROUTING 25
+#endif
+#define KVM_CAP_IRQ_INJECT_STATUS 26
+#ifdef __KVM_HAVE_DEVICE_ASSIGNMENT
+#define KVM_CAP_DEVICE_DEASSIGNMENT 27
+#endif
+#ifdef __KVM_HAVE_MSIX
+#define KVM_CAP_DEVICE_MSIX 28
+#endif
+#define KVM_CAP_ASSIGN_DEV_IRQ 29
+/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */
+#define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30
+#ifdef __KVM_HAVE_MCE
+#define KVM_CAP_MCE 31
+#endif
+#define KVM_CAP_IRQFD 32
+#ifdef __KVM_HAVE_PIT
+#define KVM_CAP_PIT2 33
+#endif
+#define KVM_CAP_SET_BOOT_CPU_ID 34
+#ifdef __KVM_HAVE_PIT_STATE2
+#define KVM_CAP_PIT_STATE2 35
+#endif
+#define KVM_CAP_IOEVENTFD 36
+#define KVM_CAP_SET_IDENTITY_MAP_ADDR 37
+#ifdef __KVM_HAVE_XEN_HVM
+#define KVM_CAP_XEN_HVM 38
+#endif
+#define KVM_CAP_ADJUST_CLOCK 39
+#define KVM_CAP_INTERNAL_ERROR_DATA 40
+#ifdef __KVM_HAVE_VCPU_EVENTS
+#define KVM_CAP_VCPU_EVENTS 41
+#endif
+#define KVM_CAP_S390_PSW 42
+#define KVM_CAP_PPC_SEGSTATE 43
+#define KVM_CAP_HYPERV 44
+#define KVM_CAP_HYPERV_VAPIC 45
+#define KVM_CAP_HYPERV_SPIN 46
+#define KVM_CAP_PCI_SEGMENT 47
+#define KVM_CAP_PPC_PAIRED_SINGLES 48
+#define KVM_CAP_INTR_SHADOW 49
+#ifdef __KVM_HAVE_DEBUGREGS
+#define KVM_CAP_DEBUGREGS 50
+#endif
+#define KVM_CAP_X86_ROBUST_SINGLESTEP 51
+#define KVM_CAP_PPC_OSI 52
+#define KVM_CAP_PPC_UNSET_IRQ 53
+#define KVM_CAP_ENABLE_CAP 54
+#ifdef __KVM_HAVE_XSAVE
+#define KVM_CAP_XSAVE 55
+#endif
+#ifdef __KVM_HAVE_XCRS
+#define KVM_CAP_XCRS 56
+#endif
+
+#ifdef KVM_CAP_IRQ_ROUTING
+
+struct kvm_irq_routing_irqchip {
+ __u32 irqchip;
+ __u32 pin;
+};
+
+struct kvm_irq_routing_msi {
+ __u32 address_lo;
+ __u32 address_hi;
+ __u32 data;
+ __u32 pad;
+};
+
+/* gsi routing entry types */
+#define KVM_IRQ_ROUTING_IRQCHIP 1
+#define KVM_IRQ_ROUTING_MSI 2
+
+struct kvm_irq_routing_entry {
+ __u32 gsi;
+ __u32 type;
+ __u32 flags;
+ __u32 pad;
+ union {
+ struct kvm_irq_routing_irqchip irqchip;
+ struct kvm_irq_routing_msi msi;
+ __u32 pad[8];
+ } u;
+};
+
+struct kvm_irq_routing {
+ __u32 nr;
+ __u32 flags;
+ struct kvm_irq_routing_entry entries[0];
+};
+
+#endif
+
+#ifdef KVM_CAP_MCE
+/* x86 MCE */
+struct kvm_x86_mce {
+ __u64 status;
+ __u64 addr;
+ __u64 misc;
+ __u64 mcg_status;
+ __u8 bank;
+ __u8 pad1[7];
+ __u64 pad2[3];
+};
+#endif
+
+#ifdef KVM_CAP_XEN_HVM
+struct kvm_xen_hvm_config {
+ __u32 flags;
+ __u32 msr;
+ __u64 blob_addr_32;
+ __u64 blob_addr_64;
+ __u8 blob_size_32;
+ __u8 blob_size_64;
+ __u8 pad2[30];
+};
+#endif
+
+#define KVM_IRQFD_FLAG_DEASSIGN (1 << 0)
+
+struct kvm_irqfd {
+ __u32 fd;
+ __u32 gsi;
+ __u32 flags;
+ __u8 pad[20];
+};
+
+struct kvm_clock_data {
+ __u64 clock;
+ __u32 flags;
+ __u32 pad[9];
+};
+
+/*
+ * ioctls for VM fds
+ */
+#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region)
+/*
+ * KVM_CREATE_VCPU receives as a parameter the vcpu slot, and returns
+ * a vcpu fd.
+ */
+#define KVM_CREATE_VCPU _IO(KVMIO, 0x41)
+#define KVM_GET_DIRTY_LOG _IOW(KVMIO, 0x42, struct kvm_dirty_log)
+#define KVM_SET_MEMORY_ALIAS _IOW(KVMIO, 0x43, struct kvm_memory_alias)
+#define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44)
+#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45)
+#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \
+ struct kvm_userspace_memory_region)
+#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47)
+#define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64)
+/* Device model IOC */
+#define KVM_CREATE_IRQCHIP _IO(KVMIO, 0x60)
+#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)
+#define KVM_GET_IRQCHIP _IOWR(KVMIO, 0x62, struct kvm_irqchip)
+#define KVM_SET_IRQCHIP _IOR(KVMIO, 0x63, struct kvm_irqchip)
+#define KVM_CREATE_PIT _IO(KVMIO, 0x64)
+#define KVM_GET_PIT _IOWR(KVMIO, 0x65, struct kvm_pit_state)
+#define KVM_SET_PIT _IOR(KVMIO, 0x66, struct kvm_pit_state)
+#define KVM_IRQ_LINE_STATUS _IOWR(KVMIO, 0x67, struct kvm_irq_level)
+#define KVM_REGISTER_COALESCED_MMIO \
+ _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone)
+#define KVM_UNREGISTER_COALESCED_MMIO \
+ _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone)
+#define KVM_ASSIGN_PCI_DEVICE _IOR(KVMIO, 0x69, \
+ struct kvm_assigned_pci_dev)
+#define KVM_SET_GSI_ROUTING _IOW(KVMIO, 0x6a, struct kvm_irq_routing)
+/* deprecated, replaced by KVM_ASSIGN_DEV_IRQ */
+#define KVM_ASSIGN_IRQ __KVM_DEPRECATED_VM_R_0x70
+#define KVM_ASSIGN_DEV_IRQ _IOW(KVMIO, 0x70, struct kvm_assigned_irq)
+#define KVM_REINJECT_CONTROL _IO(KVMIO, 0x71)
+#define KVM_DEASSIGN_PCI_DEVICE _IOW(KVMIO, 0x72, \
+ struct kvm_assigned_pci_dev)
+#define KVM_ASSIGN_SET_MSIX_NR _IOW(KVMIO, 0x73, \
+ struct kvm_assigned_msix_nr)
+#define KVM_ASSIGN_SET_MSIX_ENTRY _IOW(KVMIO, 0x74, \
+ struct kvm_assigned_msix_entry)
+#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq)
+#define KVM_IRQFD _IOW(KVMIO, 0x76, struct kvm_irqfd)
+#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x77, struct kvm_pit_config)
+#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78)
+#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd)
+#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config)
+#define KVM_SET_CLOCK _IOW(KVMIO, 0x7b, struct kvm_clock_data)
+#define KVM_GET_CLOCK _IOR(KVMIO, 0x7c, struct kvm_clock_data)
+/* Available with KVM_CAP_PIT_STATE2 */
+#define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2)
+#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2)
+
+/*
+ * ioctls for vcpu fds
+ */
+#define KVM_RUN _IO(KVMIO, 0x80)
+#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs)
+#define KVM_SET_REGS _IOW(KVMIO, 0x82, struct kvm_regs)
+#define KVM_GET_SREGS _IOR(KVMIO, 0x83, struct kvm_sregs)
+#define KVM_SET_SREGS _IOW(KVMIO, 0x84, struct kvm_sregs)
+#define KVM_TRANSLATE _IOWR(KVMIO, 0x85, struct kvm_translation)
+#define KVM_INTERRUPT _IOW(KVMIO, 0x86, struct kvm_interrupt)
+/* KVM_DEBUG_GUEST is no longer supported, use KVM_SET_GUEST_DEBUG instead */
+#define KVM_DEBUG_GUEST __KVM_DEPRECATED_VCPU_W_0x87
+#define KVM_GET_MSRS _IOWR(KVMIO, 0x88, struct kvm_msrs)
+#define KVM_SET_MSRS _IOW(KVMIO, 0x89, struct kvm_msrs)
+#define KVM_SET_CPUID _IOW(KVMIO, 0x8a, struct kvm_cpuid)
+#define KVM_SET_SIGNAL_MASK _IOW(KVMIO, 0x8b, struct kvm_signal_mask)
+#define KVM_GET_FPU _IOR(KVMIO, 0x8c, struct kvm_fpu)
+#define KVM_SET_FPU _IOW(KVMIO, 0x8d, struct kvm_fpu)
+#define KVM_GET_LAPIC _IOR(KVMIO, 0x8e, struct kvm_lapic_state)
+#define KVM_SET_LAPIC _IOW(KVMIO, 0x8f, struct kvm_lapic_state)
+#define KVM_SET_CPUID2 _IOW(KVMIO, 0x90, struct kvm_cpuid2)
+#define KVM_GET_CPUID2 _IOWR(KVMIO, 0x91, struct kvm_cpuid2)
+/* Available with KVM_CAP_VAPIC */
+#define KVM_TPR_ACCESS_REPORTING _IOWR(KVMIO, 0x92, struct kvm_tpr_access_ctl)
+/* Available with KVM_CAP_VAPIC */
+#define KVM_SET_VAPIC_ADDR _IOW(KVMIO, 0x93, struct kvm_vapic_addr)
+/* valid for virtual machine (for floating interrupt)_and_ vcpu */
+#define KVM_S390_INTERRUPT _IOW(KVMIO, 0x94, struct kvm_s390_interrupt)
+/* store status for s390 */
+#define KVM_S390_STORE_STATUS_NOADDR (-1ul)
+#define KVM_S390_STORE_STATUS_PREFIXED (-2ul)
+#define KVM_S390_STORE_STATUS _IOW(KVMIO, 0x95, unsigned long)
+/* initial ipl psw for s390 */
+#define KVM_S390_SET_INITIAL_PSW _IOW(KVMIO, 0x96, struct kvm_s390_psw)
+/* initial reset for s390 */
+#define KVM_S390_INITIAL_RESET _IO(KVMIO, 0x97)
+#define KVM_GET_MP_STATE _IOR(KVMIO, 0x98, struct kvm_mp_state)
+#define KVM_SET_MP_STATE _IOW(KVMIO, 0x99, struct kvm_mp_state)
+/* Available with KVM_CAP_NMI */
+#define KVM_NMI _IO(KVMIO, 0x9a)
+/* Available with KVM_CAP_SET_GUEST_DEBUG */
+#define KVM_SET_GUEST_DEBUG _IOW(KVMIO, 0x9b, struct kvm_guest_debug)
+/* MCE for x86 */
+#define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64)
+#define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64)
+#define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce)
+/* IA64 stack access */
+#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *)
+#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *)
+/* Available with KVM_CAP_VCPU_EVENTS */
+#define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events)
+#define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events)
+/* Available with KVM_CAP_DEBUGREGS */
+#define KVM_GET_DEBUGREGS _IOR(KVMIO, 0xa1, struct kvm_debugregs)
+#define KVM_SET_DEBUGREGS _IOW(KVMIO, 0xa2, struct kvm_debugregs)
+#define KVM_ENABLE_CAP _IOW(KVMIO, 0xa3, struct kvm_enable_cap)
+/* Available with KVM_CAP_XSAVE */
+#define KVM_GET_XSAVE _IOR(KVMIO, 0xa4, struct kvm_xsave)
+#define KVM_SET_XSAVE _IOW(KVMIO, 0xa5, struct kvm_xsave)
+/* Available with KVM_CAP_XCRS */
+#define KVM_GET_XCRS _IOR(KVMIO, 0xa6, struct kvm_xcrs)
+#define KVM_SET_XCRS _IOW(KVMIO, 0xa7, struct kvm_xcrs)
+
+#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
+
+struct kvm_assigned_pci_dev {
+ __u32 assigned_dev_id;
+ __u32 busnr;
+ __u32 devfn;
+ __u32 flags;
+ __u32 segnr;
+ union {
+ __u32 reserved[11];
+ };
+};
+
+#define KVM_DEV_IRQ_HOST_INTX (1 << 0)
+#define KVM_DEV_IRQ_HOST_MSI (1 << 1)
+#define KVM_DEV_IRQ_HOST_MSIX (1 << 2)
+
+#define KVM_DEV_IRQ_GUEST_INTX (1 << 8)
+#define KVM_DEV_IRQ_GUEST_MSI (1 << 9)
+#define KVM_DEV_IRQ_GUEST_MSIX (1 << 10)
+
+#define KVM_DEV_IRQ_HOST_MASK 0x00ff
+#define KVM_DEV_IRQ_GUEST_MASK 0xff00
+
+struct kvm_assigned_irq {
+ __u32 assigned_dev_id;
+ __u32 host_irq;
+ __u32 guest_irq;
+ __u32 flags;
+ union {
+ struct {
+ __u32 addr_lo;
+ __u32 addr_hi;
+ __u32 data;
+ } guest_msi;
+ __u32 reserved[12];
+ };
+};
+
+
+struct kvm_assigned_msix_nr {
+ __u32 assigned_dev_id;
+ __u16 entry_nr;
+ __u16 padding;
+};
+
+#define KVM_MAX_MSIX_PER_DEV 256
+struct kvm_assigned_msix_entry {
+ __u32 assigned_dev_id;
+ __u32 gsi;
+ __u16 entry; /* The index of entry in the MSI-X table */
+ __u16 padding[3];
+};
+
+#endif /* __LINUX_KVM_H */
diff --git a/kvm/include/linux/kvm_para.h b/kvm/include/linux/kvm_para.h
new file mode 100644
index 0000000..d731092
--- /dev/null
+++ b/kvm/include/linux/kvm_para.h
@@ -0,0 +1,41 @@
+#ifndef __LINUX_KVM_PARA_H
+#define __LINUX_KVM_PARA_H
+
+/*
+ * This header file provides a method for making a hypercall to the host
+ * Architectures should define:
+ * - kvm_hypercall0, kvm_hypercall1...
+ * - kvm_arch_para_features
+ * - kvm_para_available
+ */
+
+/* Return values for hypercalls */
+#define KVM_ENOSYS 1000
+#define KVM_EFAULT EFAULT
+#define KVM_E2BIG E2BIG
+#define KVM_EPERM EPERM
+
+#define KVM_HC_VAPIC_POLL_IRQ 1
+#define KVM_HC_MMU_OP 2
+
+/*
+ * hypercalls use architecture specific
+ */
+#include <asm/kvm_para.h>
+
+#ifdef __KERNEL__
+#ifdef CONFIG_KVM_GUEST
+void __init kvm_guest_init(void);
+#else
+#define kvm_guest_init() do { } while (0)
+#endif
+
+static inline int kvm_para_has_feature(unsigned int feature)
+{
+ if (kvm_arch_para_features() & (1UL << feature))
+ return 1;
+ return 0;
+}
+#endif /* __KERNEL__ */
+#endif /* __LINUX_KVM_PARA_H */
+
diff --git a/kvm/include/linux/vhost.h b/kvm/include/linux/vhost.h
new file mode 100644
index 0000000..165a484
--- /dev/null
+++ b/kvm/include/linux/vhost.h
@@ -0,0 +1,130 @@
+#ifndef _LINUX_VHOST_H
+#define _LINUX_VHOST_H
+/* Userspace interface for in-kernel virtio accelerators. */
+
+/* vhost is used to reduce the number of system calls involved in virtio.
+ *
+ * Existing virtio net code is used in the guest without modification.
+ *
+ * This header includes interface used by userspace hypervisor for
+ * device configuration.
+ */
+
+#include <linux/types.h>
+
+#include <linux/ioctl.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+
+struct vhost_vring_state {
+ unsigned int index;
+ unsigned int num;
+};
+
+struct vhost_vring_file {
+ unsigned int index;
+ int fd; /* Pass -1 to unbind from file. */
+
+};
+
+struct vhost_vring_addr {
+ unsigned int index;
+ /* Option flags. */
+ unsigned int flags;
+ /* Flag values: */
+ /* Whether log address is valid. If set enables logging. */
+#define VHOST_VRING_F_LOG 0
+
+ /* Start of array of descriptors (virtually contiguous) */
+ __u64 desc_user_addr;
+ /* Used structure address. Must be 32 bit aligned */
+ __u64 used_user_addr;
+ /* Available structure address. Must be 16 bit aligned */
+ __u64 avail_user_addr;
+ /* Logging support. */
+ /* Log writes to used structure, at offset calculated from specified
+ * address. Address must be 32 bit aligned. */
+ __u64 log_guest_addr;
+};
+
+struct vhost_memory_region {
+ __u64 guest_phys_addr;
+ __u64 memory_size; /* bytes */
+ __u64 userspace_addr;
+ __u64 flags_padding; /* No flags are currently specified. */
+};
+
+/* All region addresses and sizes must be 4K aligned. */
+#define VHOST_PAGE_SIZE 0x1000
+
+struct vhost_memory {
+ __u32 nregions;
+ __u32 padding;
+ struct vhost_memory_region regions[0];
+};
+
+/* ioctls */
+
+#define VHOST_VIRTIO 0xAF
+
+/* Features bitmask for forward compatibility. Transport bits are used for
+ * vhost specific features. */
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+
+/* Set current process as the (exclusive) owner of this file descriptor. This
+ * must be called before any other vhost command. Further calls to
+ * VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+/* Give up ownership, and reset the device to default values.
+ * Allows subsequent call to VHOST_OWNER_SET to succeed. */
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+
+/* Set up/modify memory layout */
+#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory)
+
+/* Write logging setup. */
+/* Memory writes can optionally be logged by setting bit at an offset
+ * (calculated from the physical address) from specified log base.
+ * The bit is set using an atomic 32 bit operation. */
+/* Set base address for logging. */
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+/* Specify an eventfd file descriptor to signal on log write. */
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+
+/* Ring setup. */
+/* Set number of descriptors in ring. This parameter can not
+ * be modified while ring is running (bound to a device). */
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+/* Set addresses for the ring. */
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+/* Base value where queue looks for available descriptors */
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+/* Get accessor: reads index, writes value in num */
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+
+/* The following ioctls use eventfd file descriptors to signal and poll
+ * for events. */
+
+/* Set eventfd to poll for added buffers */
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+/* Set eventfd to signal when buffers have beed used */
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+/* Set eventfd to signal an error */
+#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
+
+/* VHOST_NET specific defines */
+
+/* Attach virtio net ring to a raw socket, or tap device.
+ * The socket must be already bound to an ethernet device, this device will be
+ * used for transmit. Pass fd -1 to unbind from the socket and the transmit
+ * device. This can be used to stop the ring (e.g. for migration). */
+#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
+
+/* Feature bits */
+/* Log all write descriptors. Can be changed while device is active. */
+#define VHOST_F_LOG_ALL 26
+/* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */
+#define VHOST_NET_F_VIRTIO_NET_HDR 27
+
+#endif
diff --git a/kvm/include/powerpc/asm/kvm.h b/kvm/include/powerpc/asm/kvm.h
new file mode 100644
index 0000000..bb2de6a
--- /dev/null
+++ b/kvm/include/powerpc/asm/kvm.h
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2007
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#ifndef __LINUX_KVM_POWERPC_H
+#define __LINUX_KVM_POWERPC_H
+
+#include <linux/types.h>
+
+struct kvm_regs {
+ __u64 pc;
+ __u64 cr;
+ __u64 ctr;
+ __u64 lr;
+ __u64 xer;
+ __u64 msr;
+ __u64 srr0;
+ __u64 srr1;
+ __u64 pid;
+
+ __u64 sprg0;
+ __u64 sprg1;
+ __u64 sprg2;
+ __u64 sprg3;
+ __u64 sprg4;
+ __u64 sprg5;
+ __u64 sprg6;
+ __u64 sprg7;
+
+ __u64 gpr[32];
+};
+
+struct kvm_sregs {
+};
+
+struct kvm_fpu {
+ __u64 fpr[32];
+};
+
+struct kvm_debug_exit_arch {
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+};
+
+#endif /* __LINUX_KVM_POWERPC_H */
diff --git a/kvm/include/powerpc/asm/kvm_para.h b/kvm/include/powerpc/asm/kvm_para.h
new file mode 100644
index 0000000..2d48f6a
--- /dev/null
+++ b/kvm/include/powerpc/asm/kvm_para.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+#ifndef __POWERPC_KVM_PARA_H__
+#define __POWERPC_KVM_PARA_H__
+
+#ifdef __KERNEL__
+
+static inline int kvm_para_available(void)
+{
+ return 0;
+}
+
+static inline unsigned int kvm_arch_para_features(void)
+{
+ return 0;
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* __POWERPC_KVM_PARA_H__ */
diff --git a/kvm/include/x86/asm/kvm.h b/kvm/include/x86/asm/kvm.h
new file mode 100644
index 0000000..4d8dcbd
--- /dev/null
+++ b/kvm/include/x86/asm/kvm.h
@@ -0,0 +1,324 @@
+#ifndef _ASM_X86_KVM_H
+#define _ASM_X86_KVM_H
+
+/*
+ * KVM x86 specific structures and definitions
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Select x86 specific features in <linux/kvm.h> */
+#define __KVM_HAVE_PIT
+#define __KVM_HAVE_IOAPIC
+#define __KVM_HAVE_DEVICE_ASSIGNMENT
+#define __KVM_HAVE_MSI
+#define __KVM_HAVE_USER_NMI
+#define __KVM_HAVE_GUEST_DEBUG
+#define __KVM_HAVE_MSIX
+#define __KVM_HAVE_MCE
+#define __KVM_HAVE_PIT_STATE2
+#define __KVM_HAVE_XEN_HVM
+#define __KVM_HAVE_VCPU_EVENTS
+#define __KVM_HAVE_DEBUGREGS
+#define __KVM_HAVE_XSAVE
+#define __KVM_HAVE_XCRS
+
+/* Architectural interrupt line count. */
+#define KVM_NR_INTERRUPTS 256
+
+struct kvm_memory_alias {
+ __u32 slot; /* this has a different namespace than memory slots */
+ __u32 flags;
+ __u64 guest_phys_addr;
+ __u64 memory_size;
+ __u64 target_phys_addr;
+};
+
+/* for KVM_GET_IRQCHIP and KVM_SET_IRQCHIP */
+struct kvm_pic_state {
+ __u8 last_irr; /* edge detection */
+ __u8 irr; /* interrupt request register */
+ __u8 imr; /* interrupt mask register */
+ __u8 isr; /* interrupt service register */
+ __u8 priority_add; /* highest irq priority */
+ __u8 irq_base;
+ __u8 read_reg_select;
+ __u8 poll;
+ __u8 special_mask;
+ __u8 init_state;
+ __u8 auto_eoi;
+ __u8 rotate_on_auto_eoi;
+ __u8 special_fully_nested_mode;
+ __u8 init4; /* true if 4 byte init */
+ __u8 elcr; /* PIIX edge/trigger selection */
+ __u8 elcr_mask;
+};
+
+#define KVM_IOAPIC_NUM_PINS 24
+struct kvm_ioapic_state {
+ __u64 base_address;
+ __u32 ioregsel;
+ __u32 id;
+ __u32 irr;
+ __u32 pad;
+ union {
+ __u64 bits;
+ struct {
+ __u8 vector;
+ __u8 delivery_mode:3;
+ __u8 dest_mode:1;
+ __u8 delivery_status:1;
+ __u8 polarity:1;
+ __u8 remote_irr:1;
+ __u8 trig_mode:1;
+ __u8 mask:1;
+ __u8 reserve:7;
+ __u8 reserved[4];
+ __u8 dest_id;
+ } fields;
+ } redirtbl[KVM_IOAPIC_NUM_PINS];
+};
+
+#define KVM_IRQCHIP_PIC_MASTER 0
+#define KVM_IRQCHIP_PIC_SLAVE 1
+#define KVM_IRQCHIP_IOAPIC 2
+#define KVM_NR_IRQCHIPS 3
+
+/* for KVM_GET_REGS and KVM_SET_REGS */
+struct kvm_regs {
+ /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
+ __u64 rax, rbx, rcx, rdx;
+ __u64 rsi, rdi, rsp, rbp;
+ __u64 r8, r9, r10, r11;
+ __u64 r12, r13, r14, r15;
+ __u64 rip, rflags;
+};
+
+/* for KVM_GET_LAPIC and KVM_SET_LAPIC */
+#define KVM_APIC_REG_SIZE 0x400
+struct kvm_lapic_state {
+ char regs[KVM_APIC_REG_SIZE];
+};
+
+struct kvm_segment {
+ __u64 base;
+ __u32 limit;
+ __u16 selector;
+ __u8 type;
+ __u8 present, dpl, db, s, l, g, avl;
+ __u8 unusable;
+ __u8 padding;
+};
+
+struct kvm_dtable {
+ __u64 base;
+ __u16 limit;
+ __u16 padding[3];
+};
+
+
+/* for KVM_GET_SREGS and KVM_SET_SREGS */
+struct kvm_sregs {
+ /* out (KVM_GET_SREGS) / in (KVM_SET_SREGS) */
+ struct kvm_segment cs, ds, es, fs, gs, ss;
+ struct kvm_segment tr, ldt;
+ struct kvm_dtable gdt, idt;
+ __u64 cr0, cr2, cr3, cr4, cr8;
+ __u64 efer;
+ __u64 apic_base;
+ __u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64];
+};
+
+/* for KVM_GET_FPU and KVM_SET_FPU */
+struct kvm_fpu {
+ __u8 fpr[8][16];
+ __u16 fcw;
+ __u16 fsw;
+ __u8 ftwx; /* in fxsave format */
+ __u8 pad1;
+ __u16 last_opcode;
+ __u64 last_ip;
+ __u64 last_dp;
+ __u8 xmm[16][16];
+ __u32 mxcsr;
+ __u32 pad2;
+};
+
+struct kvm_msr_entry {
+ __u32 index;
+ __u32 reserved;
+ __u64 data;
+};
+
+/* for KVM_GET_MSRS and KVM_SET_MSRS */
+struct kvm_msrs {
+ __u32 nmsrs; /* number of msrs in entries */
+ __u32 pad;
+
+ struct kvm_msr_entry entries[0];
+};
+
+/* for KVM_GET_MSR_INDEX_LIST */
+struct kvm_msr_list {
+ __u32 nmsrs; /* number of msrs in entries */
+ __u32 indices[0];
+};
+
+
+struct kvm_cpuid_entry {
+ __u32 function;
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+ __u32 padding;
+};
+
+/* for KVM_SET_CPUID */
+struct kvm_cpuid {
+ __u32 nent;
+ __u32 padding;
+ struct kvm_cpuid_entry entries[0];
+};
+
+struct kvm_cpuid_entry2 {
+ __u32 function;
+ __u32 index;
+ __u32 flags;
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+ __u32 padding[3];
+};
+
+#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1
+#define KVM_CPUID_FLAG_STATEFUL_FUNC 2
+#define KVM_CPUID_FLAG_STATE_READ_NEXT 4
+
+/* for KVM_SET_CPUID2 */
+struct kvm_cpuid2 {
+ __u32 nent;
+ __u32 padding;
+ struct kvm_cpuid_entry2 entries[0];
+};
+
+/* for KVM_GET_PIT and KVM_SET_PIT */
+struct kvm_pit_channel_state {
+ __u32 count; /* can be 65536 */
+ __u16 latched_count;
+ __u8 count_latched;
+ __u8 status_latched;
+ __u8 status;
+ __u8 read_state;
+ __u8 write_state;
+ __u8 write_latch;
+ __u8 rw_mode;
+ __u8 mode;
+ __u8 bcd;
+ __u8 gate;
+ __s64 count_load_time;
+};
+
+struct kvm_debug_exit_arch {
+ __u32 exception;
+ __u32 pad;
+ __u64 pc;
+ __u64 dr6;
+ __u64 dr7;
+};
+
+#define KVM_GUESTDBG_USE_SW_BP 0x00010000
+#define KVM_GUESTDBG_USE_HW_BP 0x00020000
+#define KVM_GUESTDBG_INJECT_DB 0x00040000
+#define KVM_GUESTDBG_INJECT_BP 0x00080000
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+ __u64 debugreg[8];
+};
+
+struct kvm_pit_state {
+ struct kvm_pit_channel_state channels[3];
+};
+
+#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001
+
+struct kvm_pit_state2 {
+ struct kvm_pit_channel_state channels[3];
+ __u32 flags;
+ __u32 reserved[9];
+};
+
+struct kvm_reinject_control {
+ __u8 pit_reinject;
+ __u8 reserved[31];
+};
+
+/* When set in flags, include corresponding fields on KVM_SET_VCPU_EVENTS */
+#define KVM_VCPUEVENT_VALID_NMI_PENDING 0x00000001
+#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
+#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
+
+/* Interrupt shadow states */
+#define KVM_X86_SHADOW_INT_MOV_SS 0x01
+#define KVM_X86_SHADOW_INT_STI 0x02
+
+/* for KVM_GET/SET_VCPU_EVENTS */
+struct kvm_vcpu_events {
+ struct {
+ __u8 injected;
+ __u8 nr;
+ __u8 has_error_code;
+ __u8 pad;
+ __u32 error_code;
+ } exception;
+ struct {
+ __u8 injected;
+ __u8 nr;
+ __u8 soft;
+ __u8 shadow;
+ } interrupt;
+ struct {
+ __u8 injected;
+ __u8 pending;
+ __u8 masked;
+ __u8 pad;
+ } nmi;
+ __u32 sipi_vector;
+ __u32 flags;
+ __u32 reserved[10];
+};
+
+/* for KVM_GET/SET_DEBUGREGS */
+struct kvm_debugregs {
+ __u64 db[4];
+ __u64 dr6;
+ __u64 dr7;
+ __u64 flags;
+ __u64 reserved[9];
+};
+
+/* for KVM_CAP_XSAVE */
+struct kvm_xsave {
+ __u32 region[1024];
+};
+
+#define KVM_MAX_XCRS 16
+
+struct kvm_xcr {
+ __u32 xcr;
+ __u32 reserved;
+ __u64 value;
+};
+
+struct kvm_xcrs {
+ __u32 nr_xcrs;
+ __u32 flags;
+ struct kvm_xcr xcrs[KVM_MAX_XCRS];
+ __u64 padding[16];
+};
+
+#endif /* _ASM_X86_KVM_H */
diff --git a/kvm/include/x86/asm/kvm_para.h b/kvm/include/x86/asm/kvm_para.h
new file mode 100644
index 0000000..c584076
--- /dev/null
+++ b/kvm/include/x86/asm/kvm_para.h
@@ -0,0 +1,149 @@
+#ifndef _ASM_X86_KVM_PARA_H
+#define _ASM_X86_KVM_PARA_H
+
+#include <linux/types.h>
+
+/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It
+ * should be used to determine that a VM is running under KVM.
+ */
+#define KVM_CPUID_SIGNATURE 0x40000000
+
+/* This CPUID returns a feature bitmap in eax. Before enabling a particular
+ * paravirtualization, the appropriate feature bit should be checked.
+ */
+#define KVM_CPUID_FEATURES 0x40000001
+#define KVM_FEATURE_CLOCKSOURCE 0
+#define KVM_FEATURE_NOP_IO_DELAY 1
+#define KVM_FEATURE_MMU_OP 2
+
+#define MSR_KVM_WALL_CLOCK 0x11
+#define MSR_KVM_SYSTEM_TIME 0x12
+
+#define KVM_MAX_MMU_OP_BATCH 32
+
+/* Operations for KVM_HC_MMU_OP */
+#define KVM_MMU_OP_WRITE_PTE 1
+#define KVM_MMU_OP_FLUSH_TLB 2
+#define KVM_MMU_OP_RELEASE_PT 3
+
+/* Payload for KVM_HC_MMU_OP */
+struct kvm_mmu_op_header {
+ __u32 op;
+ __u32 pad;
+};
+
+struct kvm_mmu_op_write_pte {
+ struct kvm_mmu_op_header header;
+ __u64 pte_phys;
+ __u64 pte_val;
+};
+
+struct kvm_mmu_op_flush_tlb {
+ struct kvm_mmu_op_header header;
+};
+
+struct kvm_mmu_op_release_pt {
+ struct kvm_mmu_op_header header;
+ __u64 pt_phys;
+};
+
+#ifdef __KERNEL__
+#include <asm/processor.h>
+
+extern void kvmclock_init(void);
+
+
+/* This instruction is vmcall. On non-VT architectures, it will generate a
+ * trap that we will then rewrite to the appropriate instruction.
+ */
+#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
+
+/* For KVM hypercalls, a three-byte sequence of either the vmrun or the vmmrun
+ * instruction. The hypervisor may replace it with something else but only the
+ * instructions are guaranteed to be supported.
+ *
+ * Up to four arguments may be passed in rbx, rcx, rdx, and rsi respectively.
+ * The hypercall number should be placed in rax and the return value will be
+ * placed in rax. No other registers will be clobbered unless explicited
+ * noted by the particular hypercall.
+ */
+
+static inline long kvm_hypercall0(unsigned int nr)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr)
+ : "memory");
+ return ret;
+}
+
+static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr), "b"(p1)
+ : "memory");
+ return ret;
+}
+
+static inline long kvm_hypercall2(unsigned int nr, unsigned long p1,
+ unsigned long p2)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr), "b"(p1), "c"(p2)
+ : "memory");
+ return ret;
+}
+
+static inline long kvm_hypercall3(unsigned int nr, unsigned long p1,
+ unsigned long p2, unsigned long p3)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr), "b"(p1), "c"(p2), "d"(p3)
+ : "memory");
+ return ret;
+}
+
+static inline long kvm_hypercall4(unsigned int nr, unsigned long p1,
+ unsigned long p2, unsigned long p3,
+ unsigned long p4)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr), "b"(p1), "c"(p2), "d"(p3), "S"(p4)
+ : "memory");
+ return ret;
+}
+
+static inline int kvm_para_available(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+ char signature[13];
+
+ cpuid(KVM_CPUID_SIGNATURE, &eax, &ebx, &ecx, &edx);
+ memcpy(signature + 0, &ebx, 4);
+ memcpy(signature + 4, &ecx, 4);
+ memcpy(signature + 8, &edx, 4);
+ signature[12] = 0;
+
+ if (strcmp(signature, "KVMKVMKVM") == 0)
+ return 1;
+
+ return 0;
+}
+
+static inline unsigned int kvm_arch_para_features(void)
+{
+ return cpuid_eax(KVM_CPUID_FEATURES);
+}
+
+#endif
+
+#endif /* _ASM_X86_KVM_PARA_H */
diff --git a/kvm/kvm.spec b/kvm/kvm.spec
new file mode 100644
index 0000000..92acb0e
--- /dev/null
+++ b/kvm/kvm.spec
@@ -0,0 +1,139 @@
+Name: kvm
+Version: 0.0
+Release: 0
+Summary: Kernel Virtual Machine virtualization environment
+
+Group: System Environment/Kernel
+License: GPL
+URL: http://www.qumranet.com
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}
+
+ExclusiveArch: i386 x86_64 ia64
+
+Requires: kvm-kmod bridge-utils
+
+%define Distribution %(rpm -q -qf /etc/redhat-release --qf '%%{name}' | cut -d"-" -f 1)
+%define os_version %(rpm -q --qf '%%{version}' %{Distribution}-release)
+%define os_release %(rpm -q --qf '%%{release}' %{Distribution}-release | cut -d"." -f 1)
+
+%if %([ x"%{Distribution}" = x"fedora" -a x"%{os_version}" = x"5" ] && echo 1 || echo 0)
+%define require_gccver 32
+%endif
+
+%if %([ x"%{Distribution}" = x"fedora" -a 0"%{os_version}" -ge "8" ] && echo 1 || echo 0)
+%define qemuldflags --qemu-ldflags=-Wl,--build-id
+%else
+%define qemuldflags ""
+%endif
+
+%if %([ x"%{Distribution}" = x"centos" -a x"%{os_version}" = x"4" ] && echo 1 || echo 0)
+%define require_gccver 32
+%endif
+
+%if %([ x"%{Distribution}" = x"redhat" -a x"%{os_release}" = x"5" ] && echo 1 || echo 0)
+%define require_gccver 34
+%endif
+
+%if %( [ x"%{require_gccver}" = x"32" ] && echo 1 || echo 0)
+BuildRequires: compat-gcc-32
+%else
+BuildRequires: compat-gcc-34
+%endif
+
+BuildRequires: SDL-devel zlib-devel alsa-lib-devel
+
+%define _prebuilt %{?prebuilt:1}%{!?prebuilt:0}
+
+%if !%{_prebuilt}
+Source0: kvm.tar.gz
+Source1: user.tar.gz
+Source2: kernel.tar.gz
+Source3: scripts.tar.gz
+Source4: Makefile
+Source5: configure
+Source6: kvm_stat
+Source7: libkvm.tar.gz
+Source8: extboot.tar.gz
+%endif
+
+%description
+The Kernel Virtual Machine provides a virtualization enviroment for processors
+with hardware support for virtualization: Intel's VT-x&VT-i and AMD's AMD-V.
+
+%prep
+
+%if !%{_prebuilt}
+%setup -T -b 0 -n qemu
+%setup -T -b 1 -n user -D
+%setup -T -b 2 -n kernel -D
+%setup -T -b 7 -n libkvm -D
+%setup -T -b 3 -n scripts -D
+%setup -T -b 8 -n extboot -D
+cd ..
+cp %{_sourcedir}/Makefile %{_sourcedir}/configure %{_sourcedir}/kvm_stat .
+%endif
+
+%build
+
+rm -rf %{buildroot}
+
+%if !%{_prebuilt}
+cd ..
+./configure --prefix=/usr/kvm %{qemuldflags}
+make -C libkvm
+make -C user
+%ifarch i386 x86_64
+make extboot
+%endif
+#(cd qemu;
+# ./co
+# kpath="$(readlink -f ../kernel/include)"
+# upath="$(readlink -f ../user)"
+# ./configure --target-list=$(uname -i)-softmmu \
+# --extra-cflags="-I$kpath -I$upath" \
+# --extra-ldflags="-L$upath" \
+# --disable-kqemu --enable-kvm --prefix=/usr/kvm
+#)
+make -C qemu
+%endif
+
+%install
+
+%if !%{_prebuilt}
+cd ..
+%else
+cd %{objdir}
+%endif
+
+make DESTDIR=%{buildroot} install-rpm
+
+%define bindir /usr/bin
+%define bin %{bindir}/kvm
+%define initdir /etc/init.d
+%define confdir /etc/kvm
+%define utilsdir /etc/kvm/utils
+
+%post
+/sbin/chkconfig --add kvm
+/sbin/chkconfig --level 2345 kvm on
+/sbin/chkconfig --level 16 kvm off
+/usr/sbin/groupadd -fg 444 kvm
+
+%preun
+if [ "$1" != 0 ]; then
+ /sbin/service kvm stop
+ /sbin/chkconfig --level 2345 kvm off
+ /sbin/chkconfig --del kvm
+fi
+
+%clean
+%{__rm} -rf %{buildroot}
+
+%files
+/usr/bin/kvm
+/usr/bin/kvm_stat
+%{confdir}/qemu-ifup
+%{initdir}/kvm
+/etc/udev/rules.d/*kvm*.rules
+/usr/kvm
+%changelog
diff --git a/kvm/kvm_stat b/kvm/kvm_stat
new file mode 100755
index 0000000..f8a1399
--- /dev/null
+++ b/kvm/kvm_stat
@@ -0,0 +1,412 @@
+#!/usr/bin/python
+
+import curses
+import sys, os, time, optparse
+
+class DebugfsProvider(object):
+ def __init__(self):
+ self.base = '/sys/kernel/debug/kvm'
+ self._fields = os.listdir(self.base)
+ def fields(self):
+ return self._fields
+ def select(self, fields):
+ self._fields = fields
+ def read(self):
+ def val(key):
+ return int(file(self.base + '/' + key).read())
+ return dict([(key, val(key)) for key in self._fields])
+
+vmx_exit_reasons = {
+ 0: 'EXCEPTION_NMI',
+ 1: 'EXTERNAL_INTERRUPT',
+ 2: 'TRIPLE_FAULT',
+ 7: 'PENDING_INTERRUPT',
+ 8: 'NMI_WINDOW',
+ 9: 'TASK_SWITCH',
+ 10: 'CPUID',
+ 12: 'HLT',
+ 14: 'INVLPG',
+ 15: 'RDPMC',
+ 16: 'RDTSC',
+ 18: 'VMCALL',
+ 19: 'VMCLEAR',
+ 20: 'VMLAUNCH',
+ 21: 'VMPTRLD',
+ 22: 'VMPTRST',
+ 23: 'VMREAD',
+ 24: 'VMRESUME',
+ 25: 'VMWRITE',
+ 26: 'VMOFF',
+ 27: 'VMON',
+ 28: 'CR_ACCESS',
+ 29: 'DR_ACCESS',
+ 30: 'IO_INSTRUCTION',
+ 31: 'MSR_READ',
+ 32: 'MSR_WRITE',
+ 33: 'INVALID_STATE',
+ 36: 'MWAIT_INSTRUCTION',
+ 39: 'MONITOR_INSTRUCTION',
+ 40: 'PAUSE_INSTRUCTION',
+ 41: 'MCE_DURING_VMENTRY',
+ 43: 'TPR_BELOW_THRESHOLD',
+ 44: 'APIC_ACCESS',
+ 48: 'EPT_VIOLATION',
+ 49: 'EPT_MISCONFIG',
+ 54: 'WBINVD',
+ 55: 'XSETBV',
+}
+
+svm_exit_reasons = {
+ 0x000: 'READ_CR0',
+ 0x003: 'READ_CR3',
+ 0x004: 'READ_CR4',
+ 0x008: 'READ_CR8',
+ 0x010: 'WRITE_CR0',
+ 0x013: 'WRITE_CR3',
+ 0x014: 'WRITE_CR4',
+ 0x018: 'WRITE_CR8',
+ 0x020: 'READ_DR0',
+ 0x021: 'READ_DR1',
+ 0x022: 'READ_DR2',
+ 0x023: 'READ_DR3',
+ 0x024: 'READ_DR4',
+ 0x025: 'READ_DR5',
+ 0x026: 'READ_DR6',
+ 0x027: 'READ_DR7',
+ 0x030: 'WRITE_DR0',
+ 0x031: 'WRITE_DR1',
+ 0x032: 'WRITE_DR2',
+ 0x033: 'WRITE_DR3',
+ 0x034: 'WRITE_DR4',
+ 0x035: 'WRITE_DR5',
+ 0x036: 'WRITE_DR6',
+ 0x037: 'WRITE_DR7',
+ 0x040: 'EXCP_BASE',
+ 0x060: 'INTR',
+ 0x061: 'NMI',
+ 0x062: 'SMI',
+ 0x063: 'INIT',
+ 0x064: 'VINTR',
+ 0x065: 'CR0_SEL_WRITE',
+ 0x066: 'IDTR_READ',
+ 0x067: 'GDTR_READ',
+ 0x068: 'LDTR_READ',
+ 0x069: 'TR_READ',
+ 0x06a: 'IDTR_WRITE',
+ 0x06b: 'GDTR_WRITE',
+ 0x06c: 'LDTR_WRITE',
+ 0x06d: 'TR_WRITE',
+ 0x06e: 'RDTSC',
+ 0x06f: 'RDPMC',
+ 0x070: 'PUSHF',
+ 0x071: 'POPF',
+ 0x072: 'CPUID',
+ 0x073: 'RSM',
+ 0x074: 'IRET',
+ 0x075: 'SWINT',
+ 0x076: 'INVD',
+ 0x077: 'PAUSE',
+ 0x078: 'HLT',
+ 0x079: 'INVLPG',
+ 0x07a: 'INVLPGA',
+ 0x07b: 'IOIO',
+ 0x07c: 'MSR',
+ 0x07d: 'TASK_SWITCH',
+ 0x07e: 'FERR_FREEZE',
+ 0x07f: 'SHUTDOWN',
+ 0x080: 'VMRUN',
+ 0x081: 'VMMCALL',
+ 0x082: 'VMLOAD',
+ 0x083: 'VMSAVE',
+ 0x084: 'STGI',
+ 0x085: 'CLGI',
+ 0x086: 'SKINIT',
+ 0x087: 'RDTSCP',
+ 0x088: 'ICEBP',
+ 0x089: 'WBINVD',
+ 0x08a: 'MONITOR',
+ 0x08b: 'MWAIT',
+ 0x08c: 'MWAIT_COND',
+ 0x400: 'NPF',
+}
+
+vendor_exit_reasons = {
+ 'vmx': vmx_exit_reasons,
+ 'svm': svm_exit_reasons,
+}
+
+exit_reasons = None
+
+for line in file('/proc/cpuinfo').readlines():
+ if line.startswith('flags'):
+ for flag in line.split():
+ if flag in vendor_exit_reasons:
+ exit_reasons = vendor_exit_reasons[flag]
+
+filters = {
+ 'kvm_exit': ('exit_reason', exit_reasons)
+}
+
+def invert(d):
+ return dict((x[1], x[0]) for x in d.iteritems())
+
+for f in filters:
+ filters[f] = (filters[f][0], invert(filters[f][1]))
+
+import ctypes, struct, array
+
+libc = ctypes.CDLL('libc.so.6')
+syscall = libc.syscall
+class perf_event_attr(ctypes.Structure):
+ _fields_ = [('type', ctypes.c_uint32),
+ ('size', ctypes.c_uint32),
+ ('config', ctypes.c_uint64),
+ ('sample_freq', ctypes.c_uint64),
+ ('sample_type', ctypes.c_uint64),
+ ('read_format', ctypes.c_uint64),
+ ('flags', ctypes.c_uint64),
+ ('wakeup_events', ctypes.c_uint32),
+ ('bp_type', ctypes.c_uint32),
+ ('bp_addr', ctypes.c_uint64),
+ ('bp_len', ctypes.c_uint64),
+ ]
+def _perf_event_open(attr, pid, cpu, group_fd, flags):
+ return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
+ ctypes.c_int(cpu), ctypes.c_int(group_fd),
+ ctypes.c_long(flags))
+
+PERF_TYPE_HARDWARE = 0
+PERF_TYPE_SOFTWARE = 1
+PERF_TYPE_TRACEPOINT = 2
+PERF_TYPE_HW_CACHE = 3
+PERF_TYPE_RAW = 4
+PERF_TYPE_BREAKPOINT = 5
+
+PERF_SAMPLE_IP = 1 << 0
+PERF_SAMPLE_TID = 1 << 1
+PERF_SAMPLE_TIME = 1 << 2
+PERF_SAMPLE_ADDR = 1 << 3
+PERF_SAMPLE_READ = 1 << 4
+PERF_SAMPLE_CALLCHAIN = 1 << 5
+PERF_SAMPLE_ID = 1 << 6
+PERF_SAMPLE_CPU = 1 << 7
+PERF_SAMPLE_PERIOD = 1 << 8
+PERF_SAMPLE_STREAM_ID = 1 << 9
+PERF_SAMPLE_RAW = 1 << 10
+
+PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
+PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
+PERF_FORMAT_ID = 1 << 2
+PERF_FORMAT_GROUP = 1 << 3
+
+import re
+
+class TracepointProvider(object):
+ def __init__(self):
+ self.base = '/sys/kernel/debug/tracing/events/kvm/'
+ fields = [f
+ for f in os.listdir(self.base)
+ if os.path.isdir(self.base + '/' + f)]
+ extra = []
+ for f in fields:
+ if f in filters:
+ subfield, values = filters[f]
+ for name, number in values.iteritems():
+ extra.append(f + '(' + name + ')')
+ fields += extra
+ self.select(fields)
+ def fields(self):
+ return self._fields
+ def select(self, _fields):
+ self._fields = _fields
+ cpure = r'cpu([0-9]+)'
+ self.cpus = [int(re.match(cpure, x).group(1))
+ for x in os.listdir('/sys/devices/system/cpu')
+ if re.match(cpure, x)]
+ import resource
+ nfiles = len(self.cpus) * 1000
+ resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
+ fds = []
+ self.group_leaders = []
+ for cpu in self.cpus:
+ group_leader = -1
+ for f in _fields:
+ fbase, sub = f, None
+ m = re.match(r'(.*)\((.*)\)', f)
+ if m:
+ fbase, sub = m.groups()
+ attr = perf_event_attr()
+ attr.type = PERF_TYPE_TRACEPOINT
+ attr.size = ctypes.sizeof(attr)
+ id = int(file(self.base + fbase + '/id').read())
+ attr.config = id
+ attr.sample_type = (PERF_SAMPLE_RAW
+ | PERF_SAMPLE_TIME
+ | PERF_SAMPLE_CPU)
+ attr.sample_period = 1
+ attr.read_format = PERF_FORMAT_GROUP
+ fd = _perf_event_open(attr, -1, cpu, group_leader, 0)
+ if fd == -1:
+ raise Exception('perf_event_open failed')
+ if sub:
+ import fcntl
+ filter = '%s==%d\0' % (filters[fbase][0],
+ filters[fbase][1][sub])
+ fcntl.ioctl(fd, 0x40082406, filter)
+ if group_leader == -1:
+ group_leader = fd
+ fds.append(fd)
+ self.group_leaders.append(group_leader)
+ self.fds = fds
+ self.files = [os.fdopen(group_leader)
+ for group_leader in self.group_leaders]
+ def read(self):
+ ret = dict([(f, 0) for f in self._fields])
+ bytes = 8 * (1 + len(self._fields))
+ fmt = 'xxxxxxxx' + 'q' * len(self._fields)
+ for file in self.files:
+ a = struct.unpack(fmt, file.read(bytes))
+ for field, val in zip(self._fields, a):
+ ret[field] += val
+ return ret
+
+class Stats:
+ def __init__(self, provider, fields = None):
+ def wanted(key):
+ import re
+ if not fields:
+ return True
+ return re.match(fields, key) != None
+ self.provider = provider
+ self.values = dict([(key, None)
+ for key in provider.fields()
+ if wanted(key)])
+ self.provider.select(self.values.keys())
+ def get(self):
+ new = self.provider.read()
+ for key in self.provider.fields():
+ oldval = self.values[key]
+ newval = new[key]
+ newdelta = None
+ if oldval is not None:
+ newdelta = newval - oldval[0]
+ self.values[key] = (newval, newdelta)
+ return self.values
+
+if not os.access('/sys/kernel/debug', os.F_OK):
+ print 'Please enable CONFIG_DEBUG_FS in your kernel'
+ sys.exit(1)
+if not os.access('/sys/kernel/debug/kvm', os.F_OK):
+ print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
+ print "and ensure the kvm modules are loaded"
+ sys.exit(1)
+
+label_width = 40
+number_width = 10
+
+def tui(screen, stats):
+ curses.use_default_colors()
+ curses.noecho()
+ def refresh(sleeptime):
+ screen.erase()
+ screen.addstr(0, 0, 'kvm statistics')
+ row = 2
+ s = stats.get()
+ def sortkey(x):
+ if s[x][1]:
+ return (-s[x][1], -s[x][0])
+ else:
+ return (0, -s[x][0])
+ for key in sorted(s.keys(), key = sortkey):
+ if row >= screen.getmaxyx()[0]:
+ break
+ values = s[key]
+ if not values[0] and not values[1]:
+ break
+ col = 1
+ screen.addstr(row, col, key)
+ col += label_width
+ screen.addstr(row, col, '%10d' % (values[0],))
+ col += number_width
+ if values[1] is not None:
+ screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
+ row += 1
+ screen.refresh()
+
+ sleeptime = 0.25
+ while True:
+ refresh(sleeptime)
+ curses.halfdelay(int(sleeptime * 10))
+ sleeptime = 3
+ try:
+ c = screen.getkey()
+ if c == 'q':
+ break
+ except KeyboardInterrupt:
+ break
+ except curses.error:
+ continue
+
+def batch(stats):
+ s = stats.get()
+ time.sleep(1)
+ s = stats.get()
+ for key in sorted(s.keys()):
+ values = s[key]
+ print '%-22s%10d%10d' % (key, values[0], values[1])
+
+def log(stats):
+ keys = sorted(stats.get().iterkeys())
+ def banner():
+ for k in keys:
+ print '%10s' % k[0:9],
+ print
+ def statline():
+ s = stats.get()
+ for k in keys:
+ print ' %9d' % s[k][1],
+ print
+ line = 0
+ banner_repeat = 20
+ while True:
+ time.sleep(1)
+ if line % banner_repeat == 0:
+ banner()
+ statline()
+ line += 1
+
+options = optparse.OptionParser()
+options.add_option('-1', '--once', '--batch',
+ action = 'store_true',
+ default = False,
+ dest = 'once',
+ help = 'run in batch mode for one second',
+ )
+options.add_option('-l', '--log',
+ action = 'store_true',
+ default = False,
+ dest = 'log',
+ help = 'run in logging mode (like vmstat)',
+ )
+options.add_option('-f', '--fields',
+ action = 'store',
+ default = None,
+ dest = 'fields',
+ help = 'fields to display (regex)',
+ )
+(options, args) = options.parse_args(sys.argv)
+
+try:
+ provider = TracepointProvider()
+except:
+ provider = DebugfsProvider()
+
+stats = Stats(provider, fields = options.fields)
+
+if options.log:
+ log(stats)
+elif not options.once:
+ import curses.wrapper
+ curses.wrapper(tui, stats)
+else:
+ batch(stats)
diff --git a/kvm/libfdt/Makefile b/kvm/libfdt/Makefile
new file mode 100644
index 0000000..db80e47
--- /dev/null
+++ b/kvm/libfdt/Makefile
@@ -0,0 +1,19 @@
+include ../config.mak
+include ../user/config.mak
+
+LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c
+LIBFDT_INCLUDES = fdt.h libfdt.h
+LIBFDT_EXTRA = libfdt_internal.h
+LIBFDT_LIB = libfdt.a
+
+LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o)
+
+CFLAGS += -I .
+
+$(LIBFDT_LIB): $(LIBFDT_OBJS)
+ $(AR) rcs $@ $^
+
+all: $(LIBFDT_LIB)
+
+clean:
+ rm -rf *.o *.a
diff --git a/kvm/libfdt/README b/kvm/libfdt/README
new file mode 100644
index 0000000..491bc76
--- /dev/null
+++ b/kvm/libfdt/README
@@ -0,0 +1,3 @@
+libfdt was grabbed from dtc source. This is the upstream source for libfdt.
+It can be found here:
+http://www.jdl.com/software/
diff --git a/kvm/libfdt/fdt.c b/kvm/libfdt/fdt.c
new file mode 100644
index 0000000..bd91712
--- /dev/null
+++ b/kvm/libfdt/fdt.c
@@ -0,0 +1,194 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_check_header(const void *fdt)
+{
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ /* Complete tree */
+ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ } else if (fdt_magic(fdt) == SW_MAGIC) {
+ /* Unfinished sequential-write blob */
+ if (fdt_size_dt_struct(fdt) == 0)
+ return -FDT_ERR_BADSTATE;
+ } else {
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ return 0;
+}
+
+const void *fdt_offset_ptr(const void *fdt, int offset, int len)
+{
+ const void *p;
+
+ if (fdt_version(fdt) >= 0x11)
+ if (((offset + len) < offset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ p = _fdt_offset_ptr(fdt, offset);
+
+ if (p + len < p)
+ return NULL;
+ return p;
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset)
+{
+ const uint32_t *tagp, *lenp;
+ uint32_t tag;
+ const char *p;
+
+ if (offset % FDT_TAGSIZE)
+ return -1;
+
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (! tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /* skip name */
+ do {
+ p = fdt_offset_ptr(fdt, offset++, 1);
+ } while (p && (*p != '\0'));
+ if (! p)
+ return FDT_END;
+ break;
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (! lenp)
+ return FDT_END;
+ /* skip name offset, length and value */
+ offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
+ break;
+ }
+
+ if (nextoffset)
+ *nextoffset = ALIGN(offset, FDT_TAGSIZE);
+
+ return tag;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset >= 0) {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ case FDT_BEGIN_NODE:
+ if (depth)
+ (*depth)++;
+ break;
+
+ case FDT_END_NODE:
+ if (depth)
+ (*depth)--;
+ break;
+
+ case FDT_END:
+ return -FDT_ERR_NOTFOUND;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (tag != FDT_BEGIN_NODE);
+
+ return offset;
+}
+
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
+{
+ int len = strlen(s) + 1;
+ const char *last = strtab + tabsize - len;
+ const char *p;
+
+ for (p = strtab; p <= last; p++)
+ if (memeq(p, s, len))
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ CHECK_HEADER(fdt);
+
+ if (fdt_totalsize(fdt) > bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ memmove(buf, fdt, fdt_totalsize(fdt));
+ return 0;
+}
diff --git a/kvm/libfdt/fdt.h b/kvm/libfdt/fdt.h
new file mode 100644
index 0000000..48ccfd9
--- /dev/null
+++ b/kvm/libfdt/fdt.h
@@ -0,0 +1,60 @@
+#ifndef _FDT_H
+#define _FDT_H
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+ uint32_t magic; /* magic word FDT_MAGIC */
+ uint32_t totalsize; /* total size of DT block */
+ uint32_t off_dt_struct; /* offset to structure */
+ uint32_t off_dt_strings; /* offset to strings */
+ uint32_t off_mem_rsvmap; /* offset to memory reserve map */
+ uint32_t version; /* format version */
+ uint32_t last_comp_version; /* last compatible version */
+
+ /* version 2 fields below */
+ uint32_t boot_cpuid_phys; /* Which physical CPU id we're
+ booting on */
+ /* version 3 fields below */
+ uint32_t size_dt_strings; /* size of the strings block */
+
+ /* version 17 fields below */
+ uint32_t size_dt_struct; /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+ uint64_t address;
+ uint64_t size;
+};
+
+struct fdt_node_header {
+ uint32_t tag;
+ char name[0];
+};
+
+struct fdt_property {
+ uint32_t tag;
+ uint32_t len;
+ uint32_t nameoff;
+ char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_TAGSIZE sizeof(uint32_t)
+
+#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
+#define FDT_END_NODE 0x2 /* End node */
+#define FDT_PROP 0x3 /* Property: name off,
+ size, content */
+#define FDT_NOP 0x4 /* nop */
+#define FDT_END 0x9
+
+#define FDT_V1_SIZE (7*sizeof(uint32_t))
+#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t))
+#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t))
+#define FDT_V16_SIZE FDT_V3_SIZE
+#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t))
+
+#endif /* _FDT_H */
diff --git a/kvm/libfdt/fdt_ro.c b/kvm/libfdt/fdt_ro.c
new file mode 100644
index 0000000..63fa129
--- /dev/null
+++ b/kvm/libfdt/fdt_ro.c
@@ -0,0 +1,476 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int nodename_eq(const void *fdt, int offset,
+ const char *s, int len)
+{
+ const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
+
+ if (! p)
+ /* short match */
+ return 0;
+
+ if (memcmp(p, s, len) != 0)
+ return 0;
+
+ if (p[len] == '\0')
+ return 1;
+ else if (!memchr(s, '@', len) && (p[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const char *fdt_string(const void *fdt, int stroffset)
+{
+ return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ CHECK_HEADER(fdt);
+ *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
+ *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
+ return 0;
+}
+
+int fdt_num_mem_rsv(const void *fdt)
+{
+ int i = 0;
+
+ while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
+ i++;
+ return i;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int offset,
+ const char *name, int namelen)
+{
+ int depth;
+
+ CHECK_HEADER(fdt);
+
+ for (depth = 0;
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth < 0)
+ return -FDT_ERR_NOTFOUND;
+ else if ((depth == 1)
+ && nodename_eq(fdt, offset, name, namelen))
+ return offset;
+ }
+
+ return offset; /* error */
+}
+
+int fdt_subnode_offset(const void *fdt, int parentoffset,
+ const char *name)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ const char *end = path + strlen(path);
+ const char *p = path;
+ int offset = 0;
+
+ CHECK_HEADER(fdt);
+
+ if (*path != '/')
+ return -FDT_ERR_BADPATH;
+
+ while (*p) {
+ const char *q;
+
+ while (*p == '/')
+ p++;
+ if (! *p)
+ return offset;
+ q = strchr(p, '/');
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh;
+ int err;
+
+ if ((err = fdt_check_header(fdt)) != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
+ if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
+ goto fail;
+
+ if (len)
+ *len = strlen(nh->name);
+
+ return nh->name;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ uint32_t tag;
+ const struct fdt_property *prop;
+ int namestroff;
+ int offset, nextoffset;
+ int err;
+
+ if ((err = fdt_check_header(fdt)) != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ if (nodeoffset % FDT_TAGSIZE)
+ goto fail;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ goto fail;
+
+ do {
+ offset = nextoffset;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_END:
+ err = -FDT_ERR_TRUNCATED;
+ goto fail;
+
+ case FDT_BEGIN_NODE:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ case FDT_PROP:
+ err = -FDT_ERR_BADSTRUCTURE;
+ prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
+ if (! prop)
+ goto fail;
+ namestroff = fdt32_to_cpu(prop->nameoff);
+ if (streq(fdt_string(fdt, namestroff), name)) {
+ /* Found it! */
+ int len = fdt32_to_cpu(prop->len);
+ prop = fdt_offset_ptr(fdt, offset,
+ sizeof(*prop)+len);
+ if (! prop)
+ goto fail;
+
+ if (lenp)
+ *lenp = len;
+
+ return prop;
+ }
+ break;
+
+ default:
+ err = -FDT_ERR_BADSTRUCTURE;
+ goto fail;
+ }
+ } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
+
+ err = -FDT_ERR_NOTFOUND;
+ fail:
+ if (lenp)
+ *lenp = err;
+ return NULL;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property(fdt, nodeoffset, name, lenp);
+ if (! prop)
+ return NULL;
+
+ return prop->data;
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const uint32_t *php;
+ int len;
+
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+
+ return fdt32_to_cpu(*php);
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ int pdepth = 0, p = 0;
+ int offset, depth, namelen;
+ const char *name;
+
+ CHECK_HEADER(fdt);
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (pdepth < depth)
+ continue; /* overflowed buffer */
+
+ while (pdepth > depth) {
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ pdepth--;
+ }
+
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
+
+ if (offset == nodeoffset) {
+ if (pdepth < (depth + 1))
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return p;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int offset, depth;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ CHECK_HEADER(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth == supernodedepth)
+ supernodeoffset = offset;
+
+ if (offset == nodeoffset) {
+ if (nodedepth)
+ *nodedepth = depth;
+
+ if (supernodedepth > depth)
+ return -FDT_ERR_NOTFOUND;
+ else
+ return supernodeoffset;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_depth(const void *fdt, int nodeoffset)
+{
+ int nodedepth;
+ int err;
+
+ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+ if (err)
+ return (err < 0) ? err : -FDT_ERR_INTERNAL;
+ return nodedepth;
+}
+
+int fdt_parent_offset(const void *fdt, int nodeoffset)
+{
+ int nodedepth = fdt_node_depth(fdt, nodeoffset);
+
+ if (nodedepth < 0)
+ return nodedepth;
+ return fdt_supernode_atdepth_offset(fdt, nodeoffset,
+ nodedepth - 1, NULL);
+}
+
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ int offset;
+ const void *val;
+ int len;
+
+ CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_getprop(), then if that didn't
+ * find what we want, we scan over them again making our way
+ * to the next node. Still it's the easiest to implement
+ * approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ if ((phandle == 0) || (phandle == -1))
+ return -FDT_ERR_BADPHANDLE;
+ phandle = cpu_to_fdt32(phandle);
+ return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
+ &phandle, sizeof(phandle));
+}
+
+int _stringlist_contains(const void *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const void *p;
+
+ while (listlen >= len) {
+ if (memcmp(str, strlist, len+1) == 0)
+ return 1;
+ p = memchr(strlist, '\0', listlen);
+ if (!p)
+ return 0; /* malformed strlist.. */
+ listlen -= (p-strlist) + 1;
+ strlist = p + 1;
+ }
+ return 0;
+}
+
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ const void *prop;
+ int len;
+
+ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+ if (!prop)
+ return len;
+ if (_stringlist_contains(prop, len, compatible))
+ return 0;
+ else
+ return 1;
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ int offset, err;
+
+ CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_node_check_compatible(), then if
+ * that didn't find what we want, we scan over them again
+ * making our way to the next node. Still it's the easiest to
+ * implement approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ err = fdt_node_check_compatible(fdt, offset, compatible);
+ if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
diff --git a/kvm/libfdt/fdt_rw.c b/kvm/libfdt/fdt_rw.c
new file mode 100644
index 0000000..0df472b
--- /dev/null
+++ b/kvm/libfdt/fdt_rw.c
@@ -0,0 +1,467 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int _blocks_misordered(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < ALIGN(sizeof(struct fdt_header), 8))
+ || (fdt_off_dt_struct(fdt) <
+ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
+ || (fdt_off_dt_strings(fdt) <
+ (fdt_off_dt_struct(fdt) + struct_size))
+ || (fdt_totalsize(fdt) <
+ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
+}
+
+static int rw_check_header(void *fdt)
+{
+ CHECK_HEADER(fdt);
+
+ if (fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_BADLAYOUT;
+ if (fdt_version(fdt) > 17)
+ fdt_set_version(fdt, 17);
+
+ return 0;
+}
+
+#define RW_CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = rw_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+static inline int _blob_data_size(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int _blob_splice(void *fdt, void *p, int oldlen, int newlen)
+{
+ void *end = fdt + _blob_data_size(fdt);
+
+ if (((p + oldlen) < p) || ((p + oldlen) > end))
+ return -FDT_ERR_BADOFFSET;
+ if ((end - oldlen + newlen) > (fdt + fdt_totalsize(fdt)))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, end - p - oldlen);
+ return 0;
+}
+
+static int _blob_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = _blob_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
+ if (err)
+ return err;
+ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _blob_splice_struct(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = _blob_splice(fdt, p, oldlen, newlen)))
+ return err;
+
+ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _blob_splice_string(void *fdt, int newlen)
+{
+ void *p = fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = _blob_splice(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+static int _find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
+ const char *p;
+ char *new;
+ int len = strlen(s) + 1;
+ int err;
+
+ p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
+ if (p)
+ /* found it */
+ return (p - strtab);
+
+ new = strtab + fdt_size_dt_strings(fdt);
+ err = _blob_splice_string(fdt, len);
+ if (err)
+ return err;
+
+ memcpy(new, s, len);
+ return (new - strtab);
+}
+
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
+ err = _blob_splice_mem_rsv(fdt, re, 0, 1);
+ if (err)
+ return err;
+
+ re->address = cpu_to_fdt64(address);
+ re->size = cpu_to_fdt64(size);
+ return 0;
+}
+
+int fdt_del_mem_rsv(void *fdt, int n)
+{
+ struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ err = _blob_splice_mem_rsv(fdt, re, 1, 0);
+ if (err)
+ return err;
+ return 0;
+}
+
+static int _resize_property(void *fdt, int nodeoffset, const char *name, int len,
+ struct fdt_property **prop)
+{
+ int oldlen;
+ int err;
+
+ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (! (*prop))
+ return oldlen;
+
+ if ((err = _blob_splice_struct(fdt, (*prop)->data,
+ ALIGN(oldlen, FDT_TAGSIZE),
+ ALIGN(len, FDT_TAGSIZE))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int _add_property(void *fdt, int nodeoffset, const char *name, int len,
+ struct fdt_property **prop)
+{
+ uint32_t tag;
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+
+ namestroff = _find_add_string(fdt, name);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = _fdt_offset_ptr_w(fdt, nextoffset);
+ proplen = sizeof(**prop) + ALIGN(len, FDT_TAGSIZE);
+
+ err = _blob_splice_struct(fdt, *prop, 0, proplen);
+ if (err)
+ return err;
+
+ (*prop)->tag = cpu_to_fdt32(FDT_PROP);
+ (*prop)->nameoff = cpu_to_fdt32(namestroff);
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+int fdt_set_name(void *fdt, int nodeoffset, const char *name)
+{
+ char *namep;
+ int oldlen, newlen;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ namep = (char *)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = _blob_splice_struct(fdt, namep, ALIGN(oldlen+1, FDT_TAGSIZE),
+ ALIGN(newlen+1, FDT_TAGSIZE));
+ if (err)
+ return err;
+
+ memcpy(namep, name, newlen+1);
+ return 0;
+}
+
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err;
+
+ if ((err = rw_check_header(fdt)))
+ return err;
+
+ err = _resize_property(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = _add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_delprop(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len, proplen;
+
+ RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ proplen = sizeof(*prop) + ALIGN(len, FDT_TAGSIZE);
+ return _blob_splice_struct(fdt, prop, proplen, 0);
+}
+
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ struct fdt_node_header *nh;
+ int offset, nextoffset;
+ int nodelen;
+ int err;
+ uint32_t tag;
+ uint32_t *endtag;
+
+ RW_CHECK_HEADER(fdt);
+
+ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+ if (offset >= 0)
+ return -FDT_ERR_EXISTS;
+ else if (offset != -FDT_ERR_NOTFOUND)
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ } while ((tag == FDT_PROP) || (tag == FDT_NOP));
+
+ nh = _fdt_offset_ptr_w(fdt, offset);
+ nodelen = sizeof(*nh) + ALIGN(namelen+1, FDT_TAGSIZE) + FDT_TAGSIZE;
+
+ err = _blob_splice_struct(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, ALIGN(namelen+1, FDT_TAGSIZE));
+ memcpy(nh->name, name, namelen);
+ endtag = (uint32_t *)((void *)nh + nodelen - FDT_TAGSIZE);
+ *endtag = cpu_to_fdt32(FDT_END_NODE);
+
+ return offset;
+}
+
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
+{
+ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_del_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ RW_CHECK_HEADER(fdt);
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return _blob_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void _packblocks(const void *fdt, void *buf,
+ int mem_rsv_size, int struct_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(buf + mem_rsv_off, fdt + fdt_off_mem_rsvmap(fdt), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(buf, mem_rsv_off);
+
+ memmove(buf + struct_off, fdt + fdt_off_dt_struct(fdt), struct_size);
+ fdt_set_off_dt_struct(buf, struct_off);
+ fdt_set_size_dt_struct(buf, struct_size);
+
+ memmove(buf + strings_off, fdt + fdt_off_dt_strings(fdt),
+ fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(buf, strings_off);
+ fdt_set_size_dt_strings(buf, fdt_size_dt_strings(fdt));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ void *tmp;
+
+ CHECK_HEADER(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+
+ if (fdt_version(fdt) >= 17) {
+ struct_size = fdt_size_dt_struct(fdt);
+ } else {
+ struct_size = 0;
+ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+ ;
+ }
+
+ if (!_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
+ /* no further work necessary */
+ err = fdt_move(fdt, buf, bufsize);
+ if (err)
+ return err;
+ fdt_set_version(buf, 17);
+ fdt_set_size_dt_struct(buf, struct_size);
+ fdt_set_totalsize(buf, bufsize);
+ return 0;
+ }
+
+ /* Need to reorder */
+ newsize = ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ if (((buf + newsize) <= fdt)
+ || (buf >= (fdt + fdt_totalsize(fdt)))) {
+ tmp = buf;
+ } else {
+ tmp = (void *)fdt + fdt_totalsize(fdt);
+ if ((tmp + newsize) > (buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ _packblocks(fdt, tmp, mem_rsv_size, struct_size);
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+ fdt_set_totalsize(buf, bufsize);
+ fdt_set_version(buf, 17);
+ fdt_set_last_comp_version(buf, 16);
+ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
+
+ return 0;
+}
+
+int fdt_pack(void *fdt)
+{
+ int mem_rsv_size;
+ int err;
+
+ err = rw_check_header(fdt);
+ if (err)
+ return err;
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ _packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
+ fdt_set_totalsize(fdt, _blob_data_size(fdt));
+
+ return 0;
+}
diff --git a/kvm/libfdt/fdt_strerror.c b/kvm/libfdt/fdt_strerror.c
new file mode 100644
index 0000000..f9d32ef
--- /dev/null
+++ b/kvm/libfdt/fdt_strerror.c
@@ -0,0 +1,96 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+struct errtabent {
+ const char *str;
+};
+
+#define ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct errtabent errtable[] = {
+ ERRTABENT(FDT_ERR_NOTFOUND),
+ ERRTABENT(FDT_ERR_EXISTS),
+ ERRTABENT(FDT_ERR_NOSPACE),
+
+ ERRTABENT(FDT_ERR_BADOFFSET),
+ ERRTABENT(FDT_ERR_BADPATH),
+ ERRTABENT(FDT_ERR_BADSTATE),
+
+ ERRTABENT(FDT_ERR_TRUNCATED),
+ ERRTABENT(FDT_ERR_BADMAGIC),
+ ERRTABENT(FDT_ERR_BADVERSION),
+ ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ ERRTABENT(FDT_ERR_BADLAYOUT),
+};
+#define ERRTABSIZE (sizeof(errtable) / sizeof(errtable[0]))
+
+const char *fdt_strerror(int errval)
+{
+ if (errval > 0)
+ return "<valid offset/length>";
+ else if (errval == 0)
+ return "<no error>";
+ else if (errval > -ERRTABSIZE) {
+ const char *s = errtable[-errval].str;
+
+ if (s)
+ return s;
+ }
+
+ return "<unknown error>";
+}
diff --git a/kvm/libfdt/fdt_sw.c b/kvm/libfdt/fdt_sw.c
new file mode 100644
index 0000000..dda2de3
--- /dev/null
+++ b/kvm/libfdt/fdt_sw.c
@@ -0,0 +1,258 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+static int check_header_sw(void *fdt)
+{
+ if (fdt_magic(fdt) != SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ return 0;
+}
+
+static void *grab_space(void *fdt, int len)
+{
+ int offset = fdt_size_dt_struct(fdt);
+ int spaceleft;
+
+ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
+ - fdt_size_dt_strings(fdt);
+
+ if ((offset + len < offset) || (offset + len > spaceleft))
+ return NULL;
+
+ fdt_set_size_dt_struct(fdt, offset + len);
+ return fdt_offset_ptr_w(fdt, offset, len);
+}
+
+int fdt_create(void *buf, int bufsize)
+{
+ void *fdt = buf;
+
+ if (bufsize < sizeof(struct fdt_header))
+ return -FDT_ERR_NOSPACE;
+
+ memset(buf, 0, bufsize);
+
+ fdt_set_magic(fdt, SW_MAGIC);
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+ fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
+ fdt_set_totalsize(fdt, bufsize);
+
+ fdt_set_off_mem_rsvmap(fdt, ALIGN(sizeof(struct fdt_header),
+ sizeof(struct fdt_reserve_entry)));
+ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
+ fdt_set_off_dt_strings(fdt, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err = check_header_sw(fdt);
+ int offset;
+
+ if (err)
+ return err;
+ if (fdt_size_dt_struct(fdt))
+ return -FDT_ERR_BADSTATE;
+
+ offset = fdt_off_dt_struct(fdt);
+ if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+
+ re = (struct fdt_reserve_entry *)(fdt + offset);
+ re->address = cpu_to_fdt64(addr);
+ re->size = cpu_to_fdt64(size);
+
+ fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
+
+ return 0;
+}
+
+int fdt_finish_reservemap(void *fdt)
+{
+ return fdt_add_reservemap_entry(fdt, 0, 0);
+}
+
+int fdt_begin_node(void *fdt, const char *name)
+{
+ struct fdt_node_header *nh;
+ int err = check_header_sw(fdt);
+ int namelen = strlen(name) + 1;
+
+ if (err)
+ return err;
+
+ nh = grab_space(fdt, sizeof(*nh) + ALIGN(namelen, FDT_TAGSIZE));
+ if (! nh)
+ return -FDT_ERR_NOSPACE;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memcpy(nh->name, name, namelen);
+ return 0;
+}
+
+int fdt_end_node(void *fdt)
+{
+ uint32_t *en;
+ int err = check_header_sw(fdt);
+
+ if (err)
+ return err;
+
+ en = grab_space(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ const char *p;
+ int strtabsize = fdt_size_dt_strings(fdt);
+ int len = strlen(s) + 1;
+ int struct_top, offset;
+
+ p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
+ if (p)
+ return p - strtab;
+
+ /* Add it */
+ offset = -strtabsize - len;
+ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ if (fdt_totalsize(fdt) + offset < struct_top)
+ return 0; /* no more room :( */
+
+ memcpy(strtab + offset, s, len);
+ fdt_set_size_dt_strings(fdt, strtabsize + len);
+ return offset;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err = check_header_sw(fdt);
+ int nameoff;
+
+ if (err)
+ return err;
+
+ nameoff = find_add_string(fdt, name);
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = grab_space(fdt, sizeof(*prop) + ALIGN(len, FDT_TAGSIZE));
+ if (! prop)
+ return -FDT_ERR_NOSPACE;
+
+ prop->tag = cpu_to_fdt32(FDT_PROP);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ prop->len = cpu_to_fdt32(len);
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_finish(void *fdt)
+{
+ int err = check_header_sw(fdt);
+ char *p = (char *)fdt;
+ uint32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ if (err)
+ return err;
+
+ /* Add terminator */
+ end = grab_space(fdt, sizeof(*end));
+ if (! end)
+ return -FDT_ERR_NOSPACE;
+ *end = cpu_to_fdt32(FDT_END);
+
+ /* Relocate the string table */
+ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
+ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(fdt, newstroffset);
+
+ /* Walk the structure, correcting string offsets */
+ offset = 0;
+ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
+ if (tag == FDT_PROP) {
+ struct fdt_property *prop =
+ fdt_offset_ptr_w(fdt, offset, sizeof(*prop));
+ int nameoff;
+
+ if (! prop)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+
+ /* Finally, adjust the header */
+ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
+ fdt_set_magic(fdt, FDT_MAGIC);
+ return 0;
+}
diff --git a/kvm/libfdt/fdt_wip.c b/kvm/libfdt/fdt_wip.c
new file mode 100644
index 0000000..88e24b8
--- /dev/null
+++ b/kvm/libfdt/fdt_wip.c
@@ -0,0 +1,144 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include <fdt.h>
+#include <libfdt.h>
+
+#include "libfdt_internal.h"
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_w(fdt, nodeoffset, name, &proplen);
+ if (! propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ memcpy(propval, val, len);
+ return 0;
+}
+
+static void nop_region(void *start, int len)
+{
+ uint32_t *p;
+
+ for (p = start; (void *)p < (start + len); p++)
+ *p = cpu_to_fdt32(FDT_NOP);
+}
+
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ nop_region(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int _fdt_node_end_offset(void *fdt, int nodeoffset)
+{
+ int level = 0;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE)
+ return -FDT_ERR_BADOFFSET;
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ return offset;
+
+ case FDT_BEGIN_NODE:
+ level++;
+ break;
+
+ case FDT_END_NODE:
+ level--;
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (level >= 0);
+
+ return nextoffset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset);
+ return 0;
+}
diff --git a/kvm/libfdt/libfdt.h b/kvm/libfdt/libfdt.h
new file mode 100644
index 0000000..8645de0
--- /dev/null
+++ b/kvm/libfdt/libfdt.h
@@ -0,0 +1,1076 @@
+#ifndef _LIBFDT_H
+#define _LIBFDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <libfdt_env.h>
+#include <fdt.h>
+
+#define FDT_FIRST_SUPPORTED_VERSION 0x10
+#define FDT_LAST_SUPPORTED_VERSION 0x11
+
+/* Error codes: informative error codes */
+#define FDT_ERR_NOTFOUND 1
+ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
+#define FDT_ERR_EXISTS 2
+ /* FDT_ERR_EXISTS: Attemped to create a node or property which
+ * already exists */
+#define FDT_ERR_NOSPACE 3
+ /* FDT_ERR_NOSPACE: Operation needed to expand the device
+ * tree, but its buffer did not have sufficient space to
+ * contain the expanded tree. Use fdt_open_into() to move the
+ * device tree to a buffer with more space. */
+
+/* Error codes: codes for bad parameters */
+#define FDT_ERR_BADOFFSET 4
+ /* FDT_ERR_BADOFFSET: Function was passed a structure block
+ * offset which is out-of-bounds, or which points to an
+ * unsuitable part of the structure for the operation. */
+#define FDT_ERR_BADPATH 5
+ /* FDT_ERR_BADPATH: Function was passed a badly formatted path
+ * (e.g. missing a leading / for a function which requires an
+ * absolute path) */
+#define FDT_ERR_BADPHANDLE 6
+ /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle
+ * value. phandle values of 0 and -1 are not permitted. */
+#define FDT_ERR_BADSTATE 7
+ /* FDT_ERR_BADSTATE: Function was passed an incomplete device
+ * tree created by the sequential-write functions, which is
+ * not sufficiently complete for the requested operation. */
+
+/* Error codes: codes for bad device tree blobs */
+#define FDT_ERR_TRUNCATED 8
+ /* FDT_ERR_TRUNCATED: Structure block of the given device tree
+ * ends without an FDT_END tag. */
+#define FDT_ERR_BADMAGIC 9
+ /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
+ * device tree at all - it is missing the flattened device
+ * tree magic number. */
+#define FDT_ERR_BADVERSION 10
+ /* FDT_ERR_BADVERSION: Given device tree has a version which
+ * can't be handled by the requested operation. For
+ * read-write functions, this may mean that fdt_open_into() is
+ * required to convert the tree to the expected version. */
+#define FDT_ERR_BADSTRUCTURE 11
+ /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
+ * structure block or other serious error (e.g. misnested
+ * nodes, or subnodes preceding properties). */
+#define FDT_ERR_BADLAYOUT 12
+ /* FDT_ERR_BADLAYOUT: For read-write functions, the given
+ * device tree has it's sub-blocks in an order that the
+ * function can't handle (memory reserve map, then structure,
+ * then strings). Use fdt_open_into() to reorganize the tree
+ * into a form suitable for the read-write operations. */
+
+/* "Can't happen" error indicating a bug in libfdt */
+#define FDT_ERR_INTERNAL 13
+ /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
+ * Should never be returned, if it is, it indicates a bug in
+ * libfdt itself. */
+
+#define FDT_ERR_MAX 13
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these) */
+/**********************************************************************/
+
+const void *fdt_offset_ptr(const void *fdt, int offset, int checklen);
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+ return (void *)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+/**********************************************************************/
+/* Traversal functions */
+/**********************************************************************/
+
+int fdt_next_node(const void *fdt, int offset, int *depth);
+
+/**********************************************************************/
+/* General functions */
+/**********************************************************************/
+
+#define fdt_get_header(fdt, field) \
+ (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
+#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
+#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
+#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
+#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
+#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
+#define fdt_version(fdt) (fdt_get_header(fdt, version))
+#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
+#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
+#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
+#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
+
+#define __fdt_set_hdr(name) \
+ static inline void fdt_set_##name(void *fdt, uint32_t val) \
+ { \
+ struct fdt_header *fdth = fdt; \
+ fdth->name = cpu_to_fdt32(val); \
+ }
+__fdt_set_hdr(magic);
+__fdt_set_hdr(totalsize);
+__fdt_set_hdr(off_dt_struct);
+__fdt_set_hdr(off_dt_strings);
+__fdt_set_hdr(off_mem_rsvmap);
+__fdt_set_hdr(version);
+__fdt_set_hdr(last_comp_version);
+__fdt_set_hdr(boot_cpuid_phys);
+__fdt_set_hdr(size_dt_strings);
+__fdt_set_hdr(size_dt_struct);
+#undef __fdt_set_hdr
+
+/**
+ * fdt_check_header - sanity check a device tree or possible device tree
+ * @fdt: pointer to data which might be a flattened device tree
+ *
+ * fdt_check_header() checks that the given buffer contains what
+ * appears to be a flattened device tree with sane information in its
+ * header.
+ *
+ * returns:
+ * 0, if the buffer appears to contain a valid device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings, as above
+ */
+int fdt_check_header(const void *fdt);
+
+/**
+ * fdt_move - move a device tree around in memory
+ * @fdt: pointer to the device tree to move
+ * @buf: pointer to memory where the device is to be moved
+ * @bufsize: size of the memory space at buf
+ *
+ * fdt_move() relocates, if possible, the device tree blob located at
+ * fdt to the buffer at buf of size bufsize. The buffer may overlap
+ * with the existing device tree blob at fdt. Therefore,
+ * fdt_move(fdt, fdt, fdt_totalsize(fdt))
+ * should always succeed.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_move(const void *fdt, void *buf, int bufsize);
+
+/**********************************************************************/
+/* Read-only functions */
+/**********************************************************************/
+
+/**
+ * fdt_string - retreive a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ *
+ * fdt_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds
+ */
+const char *fdt_string(const void *fdt, int stroffset);
+
+/**
+ * fdt_num_mem_rsv - retreive the number of memory reserve map entries
+ * @fdt: pointer to the device tree blob
+ *
+ * Returns the number of entries in the device tree blob's memory
+ * reservation map. This does not include the terminating 0,0 entry
+ * or any other (0,0) entries reserved for expansion.
+ *
+ * returns:
+ * the number of entries
+ */
+int fdt_num_mem_rsv(const void *fdt);
+
+/**
+ * fdt_get_mem_rsv - retreive one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: pointers to 64-bit variables
+ *
+ * On success, *address and *size will contain the address and size of
+ * the n-th reserve map entry from the device tree blob, in
+ * native-endian format.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
+
+/**
+ * fdt_subnode_offset_namelen - find a subnode based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_subnode_offset(), but only examine the first
+ * namelen characters of name for matching the subnode name. This is
+ * useful for finding subnodes based on a portion of a larger string,
+ * such as a full path.
+ */
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+ const char *name, int namelen);
+/**
+ * fdt_subnode_offset - find a subnode of a given node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_subnode_offset() finds a subnode of the node at structure block
+ * offset parentoffset with the given name. name may include a unit
+ * address, in which case fdt_subnode_offset() will find the subnode
+ * with that unit address, or the unit address may be omitted, in
+ * which case fdt_subnode_offset() will find an arbitrary subnode
+ * whose name excluding unit address matches the given name.
+ *
+ * returns:
+ * structure block offset of the requested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_path_offset - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ *
+ * fdt_path_offset() finds a node of a given path in the device tree.
+ * Each path component may omit the unit address portion, but the
+ * results of this are undefined if any such path component is
+ * ambiguous (that is if there are multiple nodes at the relevant
+ * level matching the given component, differentiated only by unit
+ * address).
+ *
+ * returns:
+ * structure block offset of the node with the requested path (>=0), on success
+ * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
+ * -FDT_ERR_NOTFOUND, if the requested node does not exist
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_path_offset(const void *fdt, const char *path);
+
+/**
+ * fdt_get_name - retreive the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the starting node
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_name() retrieves the name (including unit address) of the
+ * device tree node at structure block offset nodeoffset. If lenp is
+ * non-NULL, the length of this name is also returned, in the integer
+ * pointed to by lenp.
+ *
+ * returns:
+ * pointer to the node's name, on success
+ * If lenp is non-NULL, *lenp contains the length of that name (>=0)
+ * NULL, on error
+ * if lenp is non-NULL *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+/**
+ * fdt_get_property - find a given property in a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property() retrieves a pointer to the fdt_property
+ * structure within the device tree blob corresponding to the property
+ * named 'name' of the node at offset nodeoffset. If lenp is
+ * non-NULL, the length of the property value also returned, in the
+ * integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
+ const char *name,
+ int *lenp)
+{
+ return (struct fdt_property *)fdt_get_property(fdt, nodeoffset,
+ name, lenp);
+}
+
+/**
+ * fdt_getprop - retrieve the value of a given property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop() retrieves a pointer to the value of the property
+ * named 'name' of the node at offset nodeoffset (this will be a
+ * pointer to within the device blob itself, not a copy of the value).
+ * If lenp is non-NULL, the length of the property value also
+ * returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return (void *)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retreive the phandle of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the node
+ *
+ * fdt_get_phandle() retrieves the phandle of the device tree node at
+ * structure block offset nodeoffset.
+ *
+ * returns:
+ * the phandle of the node at nodeoffset, on succes (!= 0, != -1)
+ * 0, if the node has no phandle, or another error occurs
+ */
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_get_path - determine the full path of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose path to find
+ * @buf: character buffer to contain the returned path (will be overwritten)
+ * @buflen: size of the character buffer at buf
+ *
+ * fdt_get_path() computes the full path of the node at offset
+ * nodeoffset, and records that path in the buffer at buf.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * 0, on success
+ * buf contains the absolute path of the node at
+ * nodeoffset, as a NUL-terminated string.
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
+ * characters and will not fit in the given buffer.
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
+
+/**
+ * fdt_supernode_atdepth_offset - find a specific ancestor of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ * @supernodedepth: depth of the ancestor to find
+ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_supernode_atdepth_offset() finds an ancestor of the given node
+ * at a specific depth from the root (where the root itself has depth
+ * 0, its immediate subnodes depth 1 and so forth). So
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
+ * will always return 0, the offset of the root node. If the node at
+ * nodeoffset has depth D, then:
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
+ * will return nodeoffset itself.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+
+ * structure block offset of the node at node offset's ancestor
+ * of depth supernodedepth (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth);
+
+/**
+ * fdt_node_depth - find the depth of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_node_depth() finds the depth of a given node. The root node
+ * has depth 0, its immediate subnodes depth 1 and so forth.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * depth of the node at nodeoffset (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_depth(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_parent_offset - find the parent of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_parent_offset() locates the parent node of a given node (that
+ * is, it finds the offset of the node which contains the node at
+ * nodeoffset as a subnode).
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset, *twice*.
+ *
+ * returns:
+ * stucture block offset of the parent of the node at nodeoffset
+ * (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_parent_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_node_offset_by_prop_value - find nodes with a given property value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @propname: property name to check
+ * @propval: property value to search for
+ * @proplen: length of the value in propval
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the first
+ * node after startoffset, which has a property named propname whose
+ * value is of length proplen and has value equal to propval; or if
+ * startoffset is -1, the very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
+ * propval, proplen);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+ * propval, proplen);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen);
+
+/**
+ * fdt_node_offset_by_phandle - find the node with a given phandle
+ * @fdt: pointer to the device tree blob
+ * @phandle: phandle value
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the node
+ * which has the given phandle value. If there is more than one node
+ * in the tree with the given phandle (an invalid tree), results are
+ * undefined.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0), on success
+ * -FDT_ERR_NOTFOUND, no node with that phandle exists
+ * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
+
+/**
+ * fdt_node_check_compatible: check a node's compatible property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ * 0, if the node has a 'compatible' property listing the given string
+ * 1, if the node has a 'compatible' property, but it does not list
+ * the given string
+ * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible);
+
+/**
+ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @compatible: 'compatible' string to match against
+ *
+ * fdt_node_offset_by_compatible() returns the offset of the first
+ * node after startoffset, which has a 'compatible' property which
+ * lists the given compatible string; or if startoffset is -1, the
+ * very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible);
+
+/**********************************************************************/
+/* Write-in-place functions */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace - change a property's value, but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * fdt_setprop_inplace() replaces the value of a given property with
+ * the data in val, of length len. This function cannot change the
+ * size of a property, and so will only work if len is equal to the
+ * current length of the property.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if len is not equal to the property's current length
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: cell (32-bit integer) value to replace the property with
+ *
+ * fdt_setprop_inplace_cell() replaces the value of a given property
+ * with the 32-bit integer cell value in val, converting val to
+ * big-endian if necessary. This function cannot change the size of a
+ * property, and so will only work if the property already exists and
+ * has length 4.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 4
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_nop_property - replace a property with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_nop_property() will replace a given property's representation
+ * in the blob with FDT_NOP tags, effectively removing it from the
+ * tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the property, and will not alter or move any other part of the
+ * tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_nop_node - replace a node (subtree) with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_nop_node() will replace a given node's representation in the
+ * blob, including all its subnodes, if any, with FDT_NOP tags,
+ * effectively removing it from the tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the node and its properties and subnodes, and will not alter or
+ * move any other part of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Sequential write functions */
+/**********************************************************************/
+
+int fdt_create(void *buf, int bufsize);
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+int fdt_finish_reservemap(void *fdt);
+int fdt_begin_node(void *fdt, const char *name);
+int fdt_property(void *fdt, const char *name, const void *val, int len);
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &val, sizeof(val));
+}
+#define fdt_property_string(fdt, name, str) \
+ fdt_property(fdt, name, str, strlen(str)+1)
+int fdt_end_node(void *fdt);
+int fdt_finish(void *fdt);
+
+/**********************************************************************/
+/* Read-write functions */
+/**********************************************************************/
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize);
+int fdt_pack(void *fdt);
+
+/**
+ * fdt_add_mem_rsv - add one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @addres, @size: 64-bit values (native endian)
+ *
+ * Adds a reserve map entry to the given blob reserving a region at
+ * address address of length size.
+ *
+ * This function will insert data into the reserve map and will
+ * therfore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new reservation entry
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
+
+/**
+ * fdt_del_mem_rsv - remove a memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @n: entry to remove
+ *
+ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
+ * the blob.
+ *
+ * This function will delete data from the reservation table and will
+ * therfore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
+ * are less than n+1 reserve map entries)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_mem_rsv(void *fdt, int n);
+
+/**
+ * fdt_set_name - change the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ * @name: name to give the node
+ *
+ * fdt_set_name() replaces the name (including unit address, if any)
+ * of the given node with the given string. NOTE: this function can't
+ * efficiently check if the new name is unique amongst the given
+ * node's siblings; results are undefined if this function is invoked
+ * with a name equal to one of the given node's siblings.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob
+ * to contain the new name
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_set_name(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_setprop - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop() sets the value of the named property in the given
+ * node to the given value and length, creeating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_cell() sets the value of the named property in the
+ * given node to the given cell value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ val = cpu_to_fdt32(val);
+ return fdt_setprop(fdt, nodeoffset, name, &val, sizeof(val));
+}
+
+/**
+ * fdt_setprop_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value for the property
+ *
+ * fdt_setprop_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_string(fdt, nodeoffset, name, str) \
+ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
+ * fdt_delprop - delete a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_del_property() will delete the given property.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_delprop(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_add_subnode_namelen - creates a new node based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_add_subnode(), but use only the first namelen
+ * characters of name as the name of the new node. This is useful for
+ * creating subnodes based on a portion of a larger string, such as a
+ * full path.
+ */
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen);
+
+/**
+ * fdt_add_subnode - creates a new node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_add_subnode() creates a new node as a subnode of the node at
+ * structure block offset parentoffset, with the given name (which
+ * should include the unit address, if any).
+ *
+ * This function will insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+
+ * returns:
+ * structure block offset of the created nodeequested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
+ * the given name
+ * -FDT_ERR_NOSPACE, if there is insufficient free space in the
+ * blob to contain the new node
+ * -FDT_ERR_NOSPACE
+ * -FDT_ERR_BADLAYOUT
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_del_node - delete a node (subtree)
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_del_node() will remove the given node, including all its
+ * subnodes if any, from the blob.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Debugging / informational functions */
+/**********************************************************************/
+
+const char *fdt_strerror(int errval);
+
+#endif /* _LIBFDT_H */
diff --git a/kvm/libfdt/libfdt_env.h b/kvm/libfdt/libfdt_env.h
new file mode 100644
index 0000000..59f2536
--- /dev/null
+++ b/kvm/libfdt/libfdt_env.h
@@ -0,0 +1,22 @@
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define fdt32_to_cpu(x) (x)
+#define cpu_to_fdt32(x) (x)
+#define fdt64_to_cpu(x) (x)
+#define cpu_to_fdt64(x) (x)
+#else
+#define fdt32_to_cpu(x) (bswap_32((x)))
+#define cpu_to_fdt32(x) (bswap_32((x)))
+#define fdt64_to_cpu(x) (bswap_64((x)))
+#define cpu_to_fdt64(x) (bswap_64((x)))
+#endif
+
+#endif /* _LIBFDT_ENV_H */
diff --git a/kvm/libfdt/libfdt_internal.h b/kvm/libfdt/libfdt_internal.h
new file mode 100644
index 0000000..52e1b8d
--- /dev/null
+++ b/kvm/libfdt/libfdt_internal.h
@@ -0,0 +1,96 @@
+#ifndef _LIBFDT_INTERNAL_H
+#define _LIBFDT_INTERNAL_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <fdt.h>
+
+#define ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define PALIGN(p, a) ((void *)ALIGN((unsigned long)(p), (a)))
+
+#define memeq(p, q, n) (memcmp((p), (q), (n)) == 0)
+#define streq(p, q) (strcmp((p), (q)) == 0)
+
+#define CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = fdt_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset);
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
+int _fdt_node_end_offset(void *fdt, int nodeoffset);
+
+static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
+{
+ return fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
+{
+ return (void *)_fdt_offset_ptr(fdt, offset);
+}
+
+static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
+{
+ const struct fdt_reserve_entry *rsv_table =
+ fdt + fdt_off_mem_rsvmap(fdt);
+
+ return rsv_table + n;
+}
+static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
+{
+ return (void *)_fdt_mem_rsv(fdt, n);
+}
+
+#define SW_MAGIC (~FDT_MAGIC)
+
+#endif /* _LIBFDT_INTERNAL_H */
diff --git a/kvm/libkvm/Makefile b/kvm/libkvm/Makefile
new file mode 100644
index 0000000..4db773d
--- /dev/null
+++ b/kvm/libkvm/Makefile
@@ -0,0 +1,54 @@
+all: libkvm.a
+
+include ../../config-host.mak
+ifneq ($(VPATH),)
+srcdir=$(VPATH)/kvm/libkvm
+else
+srcdir=.
+endif
+
+include $(srcdir)/config-$(ARCH).mak
+
+
+# libkvm is not -Wredundant-decls friendly yet
+CFLAGS += -Wno-redundant-decls
+
+# cc-option
+# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
+cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall
+CFLAGS += $(call cc-option, -fno-stack-protector, "")
+CFLAGS += $(call cc-option, -fno-stack-protector-all, "")
+CFLAGS += $(KVM_CFLAGS)
+
+LDFLAGS += $(CFLAGS)
+
+CXXFLAGS = $(autodepend-flags)
+
+VPATH:=$(VPATH)/kvm/libkvm
+
+autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
+
+
+libkvm.a: libkvm.o $(libkvm-$(ARCH)-objs)
+ $(AR) rcs $@ $^
+
+install:
+ @echo skipping libkvm install
+
+install-libkvm:
+ install -D libkvm.h $(DESTDIR)/$(PREFIX)/include/libkvm.h
+ install -D libkvm.a $(DESTDIR)/$(PREFIX)/$(LIBDIR)/libkvm.a
+
+install-kernel-headers:
+ install -D $(LIBKVM_KERNELDIR)/include/linux/kvm.h \
+ $(DESTDIR)/$(PREFIX)/include/linux/kvm.h
+ install -D $(LIBKVM_KERNELDIR)/include/linux/kvm_para.h \
+ $(DESTDIR)/$(PREFIX)/include/linux/kvm_para.h
+
+-include .*.d
+
+clean:
+ $(RM) *.o *.a .*.d
diff --git a/kvm/libkvm/config-i386.mak b/kvm/libkvm/config-i386.mak
new file mode 100644
index 0000000..2706b70
--- /dev/null
+++ b/kvm/libkvm/config-i386.mak
@@ -0,0 +1,6 @@
+
+LIBDIR := /lib
+CFLAGS += -m32
+CFLAGS += -D__i386__
+
+libkvm-$(ARCH)-objs := libkvm-x86.o
diff --git a/kvm/libkvm/config-ia64.mak b/kvm/libkvm/config-ia64.mak
new file mode 100644
index 0000000..568c397
--- /dev/null
+++ b/kvm/libkvm/config-ia64.mak
@@ -0,0 +1,5 @@
+
+LIBDIR := /lib
+CFLAGS += -D__ia64__
+
+libkvm-$(ARCH)-objs := libkvm-ia64.o
diff --git a/kvm/libkvm/config-ppc.mak b/kvm/libkvm/config-ppc.mak
new file mode 100644
index 0000000..091da37
--- /dev/null
+++ b/kvm/libkvm/config-ppc.mak
@@ -0,0 +1,4 @@
+
+LIBDIR := /lib
+
+libkvm-$(ARCH)-objs := libkvm-powerpc.o
diff --git a/kvm/libkvm/config-s390.mak b/kvm/libkvm/config-s390.mak
new file mode 100644
index 0000000..8177e4a
--- /dev/null
+++ b/kvm/libkvm/config-s390.mak
@@ -0,0 +1,3 @@
+# s390 31bit mode
+LIBDIR := /lib
+libkvm-$(ARCH)-objs := libkvm-s390.o
diff --git a/kvm/libkvm/config-s390x.mak b/kvm/libkvm/config-s390x.mak
new file mode 100644
index 0000000..f08ed3d
--- /dev/null
+++ b/kvm/libkvm/config-s390x.mak
@@ -0,0 +1,3 @@
+# s390 64 bit mode (arch=s390x)
+LIBDIR := /lib64
+libkvm-$(ARCH)-objs := libkvm-s390.o
diff --git a/kvm/libkvm/config-x86_64.mak b/kvm/libkvm/config-x86_64.mak
new file mode 100644
index 0000000..e638977
--- /dev/null
+++ b/kvm/libkvm/config-x86_64.mak
@@ -0,0 +1,6 @@
+
+LIBDIR := /lib64
+CFLAGS += -m64
+CFLAGS += -D__x86_64__
+
+libkvm-$(ARCH)-objs := libkvm-x86.o
diff --git a/kvm/libkvm/kvm-common.h b/kvm/libkvm/kvm-common.h
new file mode 100644
index 0000000..c95c591
--- /dev/null
+++ b/kvm/libkvm/kvm-common.h
@@ -0,0 +1,94 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_COMMON_H
+#define KVM_COMMON_H
+
+/* FIXME: share this number with kvm */
+/* FIXME: or dynamically alloc/realloc regions */
+#ifdef __s390__
+#define KVM_MAX_NUM_MEM_REGIONS 1u
+#define MAX_VCPUS 64
+#define LIBKVM_S390_ORIGIN (0UL)
+#elif defined(__ia64__)
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 256
+#else
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 16
+#endif
+
+
+/* kvm abi verison variable */
+extern int kvm_abi;
+
+/**
+ * \brief The KVM context
+ *
+ * The verbose KVM context
+ */
+
+struct kvm_context {
+ /// Filedescriptor to /dev/kvm
+ int fd;
+ int vm_fd;
+ int vcpu_fd[MAX_VCPUS];
+ struct kvm_run *run[MAX_VCPUS];
+ /// Callbacks that KVM uses to emulate various unvirtualizable functionality
+ struct kvm_callbacks *callbacks;
+ void *opaque;
+ /// is dirty pages logging enabled for all regions or not
+ int dirty_pages_log_all;
+ /// do not create in-kernel irqchip if set
+ int no_irqchip_creation;
+ /// in-kernel irqchip status
+ int irqchip_in_kernel;
+ /// ioctl to use to inject interrupts
+ int irqchip_inject_ioctl;
+ /// do not create in-kernel pit if set
+ int no_pit_creation;
+ /// in-kernel pit status
+ int pit_in_kernel;
+ /// in-kernel coalesced mmio
+ int coalesced_mmio;
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing *irq_routes;
+ int nr_allocated_irq_routes;
+#endif
+ void *used_gsi_bitmap;
+ int max_gsi;
+};
+
+int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
+ void **vm_mem);
+int kvm_alloc_userspace_memory(kvm_context_t kvm, unsigned long memory,
+ void **vm_mem);
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem);
+int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu);
+
+
+void kvm_show_code(kvm_context_t kvm, int vcpu);
+
+int handle_halt(kvm_context_t kvm, int vcpu);
+int handle_shutdown(kvm_context_t kvm, void *env);
+void post_kvm_run(kvm_context_t kvm, void *env);
+int pre_kvm_run(kvm_context_t kvm, void *env);
+int handle_io_window(kvm_context_t kvm);
+int handle_debug(kvm_context_t kvm, int vcpu, void *env);
+int try_push_interrupts(kvm_context_t kvm);
+
+#endif
diff --git a/kvm/libkvm/kvm-ia64.h b/kvm/libkvm/kvm-ia64.h
new file mode 100644
index 0000000..ad87ae7
--- /dev/null
+++ b/kvm/libkvm/kvm-ia64.h
@@ -0,0 +1,31 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for x86.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_IA64_H
+#define KVM_IA64_H
+
+#include "kvm-common.h"
+
+extern int kvm_page_size;
+
+#define PAGE_SIZE kvm_page_size
+#define PAGE_MASK (~(kvm_page_size - 1))
+
+#define ia64_mf() asm volatile ("mf" ::: "memory")
+#define smp_wmb() ia64_mf()
+
+#endif
diff --git a/kvm/libkvm/kvm-powerpc.h b/kvm/libkvm/kvm-powerpc.h
new file mode 100644
index 0000000..b09511c
--- /dev/null
+++ b/kvm/libkvm/kvm-powerpc.h
@@ -0,0 +1,36 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for powerpc.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by: Jerone Young <jyoung5@us.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_POWERPC_H
+#define KVM_POWERPC_H
+
+#include "kvm-common.h"
+
+extern int kvm_page_size;
+
+#define PAGE_SIZE kvm_page_size
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+static inline void eieio(void)
+{
+ asm volatile("eieio" : : : "memory");
+}
+
+#define smp_wmb() eieio()
+
+#endif
diff --git a/kvm/libkvm/kvm-s390.h b/kvm/libkvm/kvm-s390.h
new file mode 100644
index 0000000..9edd9a3
--- /dev/null
+++ b/kvm/libkvm/kvm-s390.h
@@ -0,0 +1,31 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for s390.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright 2008 IBM Corporation.
+ * Authors:
+ * Carsten Otte <cotte@de.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_S390_H
+#define KVM_S390_H
+
+#include <asm/ptrace.h>
+#include "kvm-common.h"
+
+#define PAGE_SIZE 4096ul
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+#define smp_wmb() asm volatile("" ::: "memory")
+
+#endif
diff --git a/kvm/libkvm/kvm-x86.h b/kvm/libkvm/kvm-x86.h
new file mode 100644
index 0000000..e988cb7
--- /dev/null
+++ b/kvm/libkvm/kvm-x86.h
@@ -0,0 +1,55 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for x86.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_X86_H
+#define KVM_X86_H
+
+#include "kvm-common.h"
+
+#define PAGE_SIZE 4096ul
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr);
+
+#ifdef KVM_CAP_VAPIC
+
+/*!
+ * \brief Enable kernel tpr access reporting
+ *
+ * When tpr access reporting is enabled, the kernel will call the
+ * ->tpr_access() callback every time the guest vcpu accesses the tpr.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to enable tpr access reporting on
+ */
+int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Disable kernel tpr access reporting
+ *
+ * Undoes the effect of kvm_enable_tpr_access_reporting().
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to disable tpr access reporting on
+ */
+int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+#endif
+
+#define smp_wmb() asm volatile("" ::: "memory")
+
+#endif
diff --git a/kvm/libkvm/libkvm-ia64.c b/kvm/libkvm/libkvm-ia64.c
new file mode 100644
index 0000000..2f15675
--- /dev/null
+++ b/kvm/libkvm/libkvm-ia64.c
@@ -0,0 +1,82 @@
+/*
+ * libkvm-ia64.c :Kernel-based Virtual Machine control library for ia64.
+ *
+ * This library provides an API to control the kvm hardware virtualization
+ * module.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright (C) 2007 Intel
+ * Added by : Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ *
+ */
+
+#include "libkvm.h"
+#include "kvm-ia64.h"
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int kvm_arch_run(struct kvm_run *run,kvm_context_t kvm, int vcpu)
+{
+ int r = 0;
+
+ switch (run->exit_reason) {
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr, "kvm_show_code not supported yet!\n");
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr,"kvm_show_regs not supportted today!\n");
+}
+
+int kvm_create_memory_alias(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys)
+{
+ return 0;
+}
+
+int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start)
+{
+ return 0;
+}
diff --git a/kvm/libkvm/libkvm-powerpc.c b/kvm/libkvm/libkvm-powerpc.c
new file mode 100644
index 0000000..f2cd8dc
--- /dev/null
+++ b/kvm/libkvm/libkvm-powerpc.c
@@ -0,0 +1,100 @@
+/*
+ * This file contains the powerpc specific implementation for the
+ * architecture dependent functions defined in kvm-common.h and
+ * libkvm.h
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright IBM Corp. 2007,2008
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include "libkvm.h"
+#include "kvm-powerpc.h"
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+int handle_dcr(struct kvm_run *run, kvm_context_t kvm, int vcpu)
+{
+ int ret = 0;
+
+ if (run->dcr.is_write)
+ ret = kvm->callbacks->powerpc_dcr_write(vcpu,
+ run->dcr.dcrn,
+ run->dcr.data);
+ else
+ ret = kvm->callbacks->powerpc_dcr_read(vcpu,
+ run->dcr.dcrn,
+ &(run->dcr.data));
+
+ return ret;
+}
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__);
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_regs regs;
+ int i;
+
+ if (kvm_get_regs(kvm, vcpu, &regs))
+ return;
+
+ fprintf(stderr,"guest vcpu #%d\n", vcpu);
+ fprintf(stderr,"pc: %016"PRIx64" msr: %016"PRIx64"\n",
+ regs.pc, regs.msr);
+ fprintf(stderr,"lr: %016"PRIx64" ctr: %016"PRIx64"\n",
+ regs.lr, regs.ctr);
+ fprintf(stderr,"srr0: %016"PRIx64" srr1: %016"PRIx64"\n",
+ regs.srr0, regs.srr1);
+ for (i=0; i<32; i+=4)
+ {
+ fprintf(stderr, "gpr%02d: %016"PRIx64" %016"PRIx64" %016"PRIx64
+ " %016"PRIx64"\n", i,
+ regs.gpr[i],
+ regs.gpr[i+1],
+ regs.gpr[i+2],
+ regs.gpr[i+3]);
+ }
+
+ fflush(stdout);
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu)
+{
+ int ret = 0;
+
+ switch (run->exit_reason){
+ case KVM_EXIT_DCR:
+ ret = handle_dcr(run, kvm, vcpu);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
diff --git a/kvm/libkvm/libkvm-s390.c b/kvm/libkvm/libkvm-s390.c
new file mode 100644
index 0000000..041c0ce
--- /dev/null
+++ b/kvm/libkvm/libkvm-s390.c
@@ -0,0 +1,110 @@
+/*
+ * This file contains the s390 specific implementation for the
+ * architecture dependent functions defined in kvm-common.h and
+ * libkvm.h
+ *
+ * Copyright (C) 2006 Qumranet
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ * Carsten Otte <cotte@de.ibm.com>
+ * Christian Borntraeger <borntraeger@de.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <sys/ioctl.h>
+#include <asm/ptrace.h>
+
+#include "libkvm.h"
+#include "kvm-common.h"
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+ fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__);
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int i;
+
+ if (kvm_get_regs(kvm, vcpu, &regs))
+ return;
+
+ if (kvm_get_sregs(kvm, vcpu, &sregs))
+ return;
+
+ fprintf(stderr, "guest vcpu #%d\n", vcpu);
+ fprintf(stderr, "PSW:\t%16.16lx %16.16lx\n",
+ kvm->run[vcpu]->s390_sieic.mask,
+ kvm->run[vcpu]->s390_sieic.addr);
+ fprintf(stderr,"GPRS:");
+ for (i=0; i<15; i+=4)
+ fprintf(stderr, "\t%16.16lx %16.16lx %16.16lx %16.16lx\n",
+ regs.gprs[i],
+ regs.gprs[i+1],
+ regs.gprs[i+2],
+ regs.gprs[i+3]);
+ fprintf(stderr,"ACRS:");
+ for (i=0; i<15; i+=4)
+ fprintf(stderr, "\t%8.8x %8.8x %8.8x %8.8x\n",
+ sregs.acrs[i],
+ sregs.acrs[i+1],
+ sregs.acrs[i+2],
+ sregs.acrs[i+3]);
+
+ fprintf(stderr,"CRS:");
+ for (i=0; i<15; i+=4)
+ fprintf(stderr, "\t%16.16lx %16.16lx %16.16lx %16.16lx\n",
+ sregs.crs[i],
+ sregs.crs[i+1],
+ sregs.crs[i+2],
+ sregs.crs[i+3]);
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ return 0;
+}
+
+int kvm_arch_run(struct kvm_run *run, kvm_context_t kvm, int vcpu)
+{
+ int ret = 0;
+
+ switch (run->exit_reason){
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+int kvm_s390_initial_reset(kvm_context_t kvm, int slot)
+{
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_INITIAL_RESET, NULL);
+}
+
+int kvm_s390_interrupt(kvm_context_t kvm, int slot,
+ struct kvm_s390_interrupt *kvmint)
+{
+ if (slot>=0)
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_INTERRUPT, kvmint);
+ else
+ return ioctl(kvm->vm_fd, KVM_S390_INTERRUPT, kvmint);
+}
+
+int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw)
+{
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_SET_INITIAL_PSW, &psw);
+}
+
+int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr)
+{
+ return ioctl(kvm->vcpu_fd[slot], KVM_S390_STORE_STATUS, addr);
+}
diff --git a/kvm/libkvm/libkvm-x86.c b/kvm/libkvm/libkvm-x86.c
new file mode 100644
index 0000000..2b12408
--- /dev/null
+++ b/kvm/libkvm/libkvm-x86.c
@@ -0,0 +1,637 @@
+#include "libkvm.h"
+#include "kvm-x86.h"
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr)
+{
+#ifdef KVM_CAP_SET_TSS_ADDR
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+ if (r > 0) {
+ r = ioctl(kvm->vm_fd, KVM_SET_TSS_ADDR, addr);
+ if (r == -1) {
+ fprintf(stderr, "kvm_set_tss_addr: %m\n");
+ return -errno;
+ }
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+static int kvm_init_tss(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_SET_TSS_ADDR
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+ if (r > 0) {
+ /*
+ * this address is 3 pages before the bios, and the bios should present
+ * as unavaible memory
+ */
+ r = kvm_set_tss_addr(kvm, 0xfffbd000);
+ if (r < 0) {
+ fprintf(stderr, "kvm_init_tss: unable to set tss addr\n");
+ return r;
+ }
+
+ }
+#endif
+ return 0;
+}
+
+static int kvm_create_pit(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_PIT
+ int r;
+
+ kvm->pit_in_kernel = 0;
+ if (!kvm->no_pit_creation) {
+#ifdef KVM_CAP_PIT2
+ struct kvm_pit_config config = { .flags = 0 };
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_PIT2);
+ if (r > 0)
+ r = ioctl(kvm->vm_fd, KVM_CREATE_PIT2, &config);
+ else
+#endif
+ {
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_PIT);
+ if (r <= 0)
+ return 0;
+
+ r = ioctl(kvm->vm_fd, KVM_CREATE_PIT);
+ }
+ if (r < 0) {
+ fprintf(stderr, "Create kernel PIC irqchip failed\n");
+ return r;
+ }
+ kvm->pit_in_kernel = 1;
+ }
+#endif
+ return 0;
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r = 0;
+
+ r = kvm_init_tss(kvm);
+ if (r < 0)
+ return r;
+
+ r = kvm_create_pit(kvm);
+ if (r < 0)
+ return r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+#ifdef KVM_EXIT_TPR_ACCESS
+
+static int handle_tpr_access(kvm_context_t kvm, struct kvm_run *run, int vcpu)
+{
+ return kvm->callbacks->tpr_access(kvm->opaque, vcpu,
+ run->tpr_access.rip,
+ run->tpr_access.is_write);
+}
+
+
+int kvm_enable_vapic(kvm_context_t kvm, int vcpu, uint64_t vapic)
+{
+ int r;
+ struct kvm_vapic_addr va = {
+ .vapic_addr = vapic,
+ };
+
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_VAPIC_ADDR, &va);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_enable_vapic");
+ return r;
+ }
+ return 0;
+}
+
+#endif
+
+int kvm_arch_run(struct kvm_run *run,kvm_context_t kvm, int vcpu)
+{
+ int r = 0;
+
+ switch (run->exit_reason) {
+#ifdef KVM_EXIT_SET_TPR
+ case KVM_EXIT_SET_TPR:
+ break;
+#endif
+#ifdef KVM_EXIT_TPR_ACCESS
+ case KVM_EXIT_TPR_ACCESS:
+ r = handle_tpr_access(kvm, run, vcpu);
+ break;
+#endif
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+#define MAX_ALIAS_SLOTS 4
+static struct {
+ uint64_t start;
+ uint64_t len;
+} kvm_aliases[MAX_ALIAS_SLOTS];
+
+static int get_alias_slot(uint64_t start)
+{
+ int i;
+
+ for (i=0; i<MAX_ALIAS_SLOTS; i++)
+ if (kvm_aliases[i].start == start)
+ return i;
+ return -1;
+}
+static int get_free_alias_slot(void)
+{
+ int i;
+
+ for (i=0; i<MAX_ALIAS_SLOTS; i++)
+ if (kvm_aliases[i].len == 0)
+ return i;
+ return -1;
+}
+
+static void register_alias(int slot, uint64_t start, uint64_t len)
+{
+ kvm_aliases[slot].start = start;
+ kvm_aliases[slot].len = len;
+}
+
+int kvm_create_memory_alias(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys)
+{
+ struct kvm_memory_alias alias = {
+ .flags = 0,
+ .guest_phys_addr = phys_start,
+ .memory_size = len,
+ .target_phys_addr = target_phys,
+ };
+ int fd = kvm->vm_fd;
+ int r;
+ int slot;
+
+ slot = get_alias_slot(phys_start);
+ if (slot < 0)
+ slot = get_free_alias_slot();
+ if (slot < 0)
+ return -EBUSY;
+ alias.slot = slot;
+
+ r = ioctl(fd, KVM_SET_MEMORY_ALIAS, &alias);
+ if (r == -1)
+ return -errno;
+
+ register_alias(slot, phys_start, len);
+ return 0;
+}
+
+int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start)
+{
+ return kvm_create_memory_alias(kvm, phys_start, 0, 0);
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_get_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s)
+{
+ int r;
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_LAPIC, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_get_lapic");
+ }
+ return r;
+}
+
+int kvm_set_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s)
+{
+ int r;
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_LAPIC, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_set_lapic");
+ }
+ return r;
+}
+
+#endif
+
+#ifdef KVM_CAP_PIT
+
+int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s)
+{
+ int r;
+ if (!kvm->pit_in_kernel)
+ return 0;
+ r = ioctl(kvm->vm_fd, KVM_GET_PIT, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_get_pit");
+ }
+ return r;
+}
+
+int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s)
+{
+ int r;
+ if (!kvm->pit_in_kernel)
+ return 0;
+ r = ioctl(kvm->vm_fd, KVM_SET_PIT, s);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_set_pit");
+ }
+ return r;
+}
+
+#endif
+
+void kvm_show_code(kvm_context_t kvm, int vcpu)
+{
+#define SHOW_CODE_LEN 50
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r, n;
+ int back_offset;
+ unsigned char code;
+ char code_str[SHOW_CODE_LEN * 3 + 1];
+ unsigned long rip;
+
+ r = ioctl(fd, KVM_GET_SREGS, &sregs);
+ if (r == -1) {
+ perror("KVM_GET_SREGS");
+ return;
+ }
+ r = ioctl(fd, KVM_GET_REGS, &regs);
+ if (r == -1) {
+ perror("KVM_GET_REGS");
+ return;
+ }
+ rip = sregs.cs.base + regs.rip;
+ back_offset = regs.rip;
+ if (back_offset > 20)
+ back_offset = 20;
+ *code_str = 0;
+ for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) {
+ if (n == 0)
+ strcat(code_str, " -->");
+ r = kvm->callbacks->mmio_read(kvm->opaque, rip + n, &code, 1);
+ if (r < 0) {
+ strcat(code_str, " xx");
+ continue;
+ }
+ sprintf(code_str + strlen(code_str), " %02x", code);
+ }
+ fprintf(stderr, "code:%s\n", code_str);
+}
+
+
+/*
+ * Returns available msr list. User must free.
+ */
+struct kvm_msr_list *kvm_get_msr_list(kvm_context_t kvm)
+{
+ struct kvm_msr_list sizer, *msrs;
+ int r, e;
+
+ sizer.nmsrs = 0;
+ r = ioctl(kvm->fd, KVM_GET_MSR_INDEX_LIST, &sizer);
+ if (r == -1 && errno != E2BIG)
+ return NULL;
+ msrs = malloc(sizeof *msrs + sizer.nmsrs * sizeof *msrs->indices);
+ if (!msrs) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ msrs->nmsrs = sizer.nmsrs;
+ r = ioctl(kvm->fd, KVM_GET_MSR_INDEX_LIST, msrs);
+ if (r == -1) {
+ e = errno;
+ free(msrs);
+ errno = e;
+ return NULL;
+ }
+ return msrs;
+}
+
+int kvm_get_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs,
+ int n)
+{
+ struct kvm_msrs *kmsrs = malloc(sizeof *kmsrs + n * sizeof *msrs);
+ int r, e;
+
+ if (!kmsrs) {
+ errno = ENOMEM;
+ return -1;
+ }
+ kmsrs->nmsrs = n;
+ memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_MSRS, kmsrs);
+ e = errno;
+ memcpy(msrs, kmsrs->entries, n * sizeof *msrs);
+ free(kmsrs);
+ errno = e;
+ return r;
+}
+
+int kvm_set_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs,
+ int n)
+{
+ struct kvm_msrs *kmsrs = malloc(sizeof *kmsrs + n * sizeof *msrs);
+ int r, e;
+
+ if (!kmsrs) {
+ errno = ENOMEM;
+ return -1;
+ }
+ kmsrs->nmsrs = n;
+ memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_MSRS, kmsrs);
+ e = errno;
+ free(kmsrs);
+ errno = e;
+ return r;
+}
+
+static void print_seg(FILE *file, const char *name, struct kvm_segment *seg)
+{
+ fprintf(stderr,
+ "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d"
+ " g %d avl %d)\n",
+ name, seg->selector, seg->base, seg->limit, seg->present,
+ seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g,
+ seg->avl);
+}
+
+static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt)
+{
+ fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit);
+}
+
+void kvm_show_regs(kvm_context_t kvm, int vcpu)
+{
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r;
+
+ r = ioctl(fd, KVM_GET_REGS, &regs);
+ if (r == -1) {
+ perror("KVM_GET_REGS");
+ return;
+ }
+ fprintf(stderr,
+ "rax %016llx rbx %016llx rcx %016llx rdx %016llx\n"
+ "rsi %016llx rdi %016llx rsp %016llx rbp %016llx\n"
+ "r8 %016llx r9 %016llx r10 %016llx r11 %016llx\n"
+ "r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n"
+ "rip %016llx rflags %08llx\n",
+ regs.rax, regs.rbx, regs.rcx, regs.rdx,
+ regs.rsi, regs.rdi, regs.rsp, regs.rbp,
+ regs.r8, regs.r9, regs.r10, regs.r11,
+ regs.r12, regs.r13, regs.r14, regs.r15,
+ regs.rip, regs.rflags);
+ r = ioctl(fd, KVM_GET_SREGS, &sregs);
+ if (r == -1) {
+ perror("KVM_GET_SREGS");
+ return;
+ }
+ print_seg(stderr, "cs", &sregs.cs);
+ print_seg(stderr, "ds", &sregs.ds);
+ print_seg(stderr, "es", &sregs.es);
+ print_seg(stderr, "ss", &sregs.ss);
+ print_seg(stderr, "fs", &sregs.fs);
+ print_seg(stderr, "gs", &sregs.gs);
+ print_seg(stderr, "tr", &sregs.tr);
+ print_seg(stderr, "ldt", &sregs.ldt);
+ print_dt(stderr, "gdt", &sregs.gdt);
+ print_dt(stderr, "idt", &sregs.idt);
+ fprintf(stderr, "cr0 %llx cr2 %llx cr3 %llx cr4 %llx cr8 %llx"
+ " efer %llx\n",
+ sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4, sregs.cr8,
+ sregs.efer);
+}
+
+uint64_t kvm_get_apic_base(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->apic_base;
+}
+
+void kvm_set_cr8(kvm_context_t kvm, int vcpu, uint64_t cr8)
+{
+ struct kvm_run *run = kvm->run[vcpu];
+
+ run->cr8 = cr8;
+}
+
+__u64 kvm_get_cr8(kvm_context_t kvm, int vcpu)
+{
+ return kvm->run[vcpu]->cr8;
+}
+
+int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages)
+{
+#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION,
+ KVM_CAP_MMU_SHADOW_CACHE_CONTROL);
+ if (r > 0) {
+ r = ioctl(kvm->vm_fd, KVM_SET_NR_MMU_PAGES, nrshadow_pages);
+ if (r == -1) {
+ fprintf(stderr, "kvm_set_shadow_pages: %m\n");
+ return -errno;
+ }
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages)
+{
+#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION,
+ KVM_CAP_MMU_SHADOW_CACHE_CONTROL);
+ if (r > 0) {
+ *nrshadow_pages = ioctl(kvm->vm_fd, KVM_GET_NR_MMU_PAGES);
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+#ifdef KVM_CAP_VAPIC
+
+static int tpr_access_reporting(kvm_context_t kvm, int vcpu, int enabled)
+{
+ int r;
+ struct kvm_tpr_access_ctl tac = {
+ .enabled = enabled,
+ };
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC);
+ if (r == -1 || r == 0)
+ return -ENOSYS;
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_TPR_ACCESS_REPORTING, &tac);
+ if (r == -1) {
+ r = -errno;
+ perror("KVM_TPR_ACCESS_REPORTING");
+ return r;
+ }
+ return 0;
+}
+
+int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu)
+{
+ return tpr_access_reporting(kvm, vcpu, 1);
+}
+
+int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu)
+{
+ return tpr_access_reporting(kvm, vcpu, 0);
+}
+
+#endif
+
+#ifdef KVM_CAP_EXT_CPUID
+
+static struct kvm_cpuid2 *try_get_cpuid(kvm_context_t kvm, int max)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r, size;
+
+ size = sizeof(*cpuid) + max * sizeof(*cpuid->entries);
+ cpuid = (struct kvm_cpuid2 *)malloc(size);
+ cpuid->nent = max;
+ r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, cpuid);
+ if (r == -1)
+ r = -errno;
+ else if (r == 0 && cpuid->nent >= max)
+ r = -E2BIG;
+ if (r < 0) {
+ if (r == -E2BIG) {
+ free(cpuid);
+ return NULL;
+ } else {
+ fprintf(stderr, "KVM_GET_SUPPORTED_CPUID failed: %s\n",
+ strerror(-r));
+ exit(1);
+ }
+ }
+ return cpuid;
+}
+
+#define R_EAX 0
+#define R_ECX 1
+#define R_EDX 2
+#define R_EBX 3
+#define R_ESP 4
+#define R_EBP 5
+#define R_ESI 6
+#define R_EDI 7
+
+uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg)
+{
+ struct kvm_cpuid2 *cpuid;
+ int i, max;
+ uint32_t ret = 0;
+ uint32_t cpuid_1_edx;
+
+ if (!kvm_check_extension(kvm, KVM_CAP_EXT_CPUID)) {
+ return -1U;
+ }
+
+ max = 1;
+ while ((cpuid = try_get_cpuid(kvm, max)) == NULL) {
+ max *= 2;
+ }
+
+ for (i = 0; i < cpuid->nent; ++i) {
+ if (cpuid->entries[i].function == function) {
+ switch (reg) {
+ case R_EAX:
+ ret = cpuid->entries[i].eax;
+ break;
+ case R_EBX:
+ ret = cpuid->entries[i].ebx;
+ break;
+ case R_ECX:
+ ret = cpuid->entries[i].ecx;
+ break;
+ case R_EDX:
+ ret = cpuid->entries[i].edx;
+ if (function == 1) {
+ /* kvm misreports the following features
+ */
+ ret |= 1 << 12; /* MTRR */
+ ret |= 1 << 16; /* PAT */
+ ret |= 1 << 7; /* MCE */
+ ret |= 1 << 14; /* MCA */
+ }
+
+ /* On Intel, kvm returns cpuid according to
+ * the Intel spec, so add missing bits
+ * according to the AMD spec:
+ */
+ if (function == 0x80000001) {
+ cpuid_1_edx = kvm_get_supported_cpuid(kvm, 1, R_EDX);
+ ret |= cpuid_1_edx & 0xdfeff7ff;
+ }
+ break;
+ }
+ }
+ }
+
+ free(cpuid);
+
+ return ret;
+}
+
+#else
+
+uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg)
+{
+ return -1U;
+}
+
+#endif
diff --git a/kvm/libkvm/libkvm.c b/kvm/libkvm/libkvm.c
new file mode 100644
index 0000000..c5d6a7f
--- /dev/null
+++ b/kvm/libkvm/libkvm.c
@@ -0,0 +1,1497 @@
+/*
+ * Kernel-based Virtual Machine control library
+ *
+ * This library provides an API to control the kvm hardware virtualization
+ * module.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef __user
+#define __user /* temporary, until installed via make headers_install */
+#endif
+
+#include <linux/kvm.h>
+
+#define EXPECTED_KVM_API_VERSION 12
+
+#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION
+#error libkvm: userspace and kernel version mismatch
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+#include "libkvm.h"
+
+#if defined(__x86_64__) || defined(__i386__)
+#include "kvm-x86.h"
+#endif
+
+#if defined(__ia64__)
+#include "kvm-ia64.h"
+#endif
+
+#if defined(__powerpc__)
+#include "kvm-powerpc.h"
+#endif
+
+#if defined(__s390__)
+#include "kvm-s390.h"
+#endif
+
+//#define DEBUG_MEMREG
+#ifdef DEBUG_MEMREG
+#define DPRINTF(fmt, args...) \
+ do { fprintf(stderr, "%s:%d " fmt , __func__, __LINE__, ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while (0)
+#endif
+
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+
+int kvm_abi = EXPECTED_KVM_API_VERSION;
+int kvm_page_size;
+
+static inline void set_gsi(kvm_context_t kvm, unsigned int gsi)
+{
+ uint32_t *bitmap = kvm->used_gsi_bitmap;
+
+ if (gsi < kvm->max_gsi)
+ bitmap[gsi / 32] |= 1U << (gsi % 32);
+ else
+ DPRINTF("Invalid GSI %d\n");
+}
+
+static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)
+{
+ uint32_t *bitmap = kvm->used_gsi_bitmap;
+
+ if (gsi < kvm->max_gsi)
+ bitmap[gsi / 32] &= ~(1U << (gsi % 32));
+ else
+ DPRINTF("Invalid GSI %d\n");
+}
+
+struct slot_info {
+ unsigned long phys_addr;
+ unsigned long len;
+ unsigned long userspace_addr;
+ unsigned flags;
+ int logging_count;
+};
+
+struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS];
+
+static void init_slots(void)
+{
+ int i;
+
+ for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i)
+ slots[i].len = 0;
+}
+
+static int get_free_slot(kvm_context_t kvm)
+{
+ int i;
+ int tss_ext;
+
+#if defined(KVM_CAP_SET_TSS_ADDR) && !defined(__s390__)
+ tss_ext = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+#else
+ tss_ext = 0;
+#endif
+
+ /*
+ * on older kernels where the set tss ioctl is not supprted we must save
+ * slot 0 to hold the extended memory, as the vmx will use the last 3
+ * pages of this slot.
+ */
+ if (tss_ext > 0)
+ i = 0;
+ else
+ i = 1;
+
+ for (; i < KVM_MAX_NUM_MEM_REGIONS; ++i)
+ if (!slots[i].len)
+ return i;
+ return -1;
+}
+
+static void register_slot(int slot, unsigned long phys_addr, unsigned long len,
+ unsigned long userspace_addr, unsigned flags)
+{
+ slots[slot].phys_addr = phys_addr;
+ slots[slot].len = len;
+ slots[slot].userspace_addr = userspace_addr;
+ slots[slot].flags = flags;
+}
+
+static void free_slot(int slot)
+{
+ slots[slot].len = 0;
+ slots[slot].logging_count = 0;
+}
+
+static int get_slot(unsigned long phys_addr)
+{
+ int i;
+
+ for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS ; ++i) {
+ if (slots[i].len && slots[i].phys_addr <= phys_addr &&
+ (slots[i].phys_addr + slots[i].len-1) >= phys_addr)
+ return i;
+ }
+ return -1;
+}
+
+/* Returns -1 if this slot is not totally contained on any other,
+ * and the number of the slot otherwise */
+static int get_container_slot(uint64_t phys_addr, unsigned long size)
+{
+ int i;
+
+ for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS ; ++i)
+ if (slots[i].len && slots[i].phys_addr <= phys_addr &&
+ (slots[i].phys_addr + slots[i].len) >= phys_addr + size)
+ return i;
+ return -1;
+}
+
+int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_addr, unsigned long size)
+{
+ int slot = get_container_slot(phys_addr, size);
+ if (slot == -1)
+ return 0;
+ return 1;
+}
+
+/*
+ * dirty pages logging control
+ */
+static int kvm_dirty_pages_log_change(kvm_context_t kvm,
+ unsigned long phys_addr,
+ unsigned flags,
+ unsigned mask)
+{
+ int r = -1;
+ int slot = get_slot(phys_addr);
+
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__);
+ return 1;
+ }
+
+ flags = (slots[slot].flags & ~mask) | flags;
+ if (flags == slots[slot].flags)
+ return 0;
+ slots[slot].flags = flags;
+
+ {
+ struct kvm_userspace_memory_region mem = {
+ .slot = slot,
+ .memory_size = slots[slot].len,
+ .guest_phys_addr = slots[slot].phys_addr,
+ .userspace_addr = slots[slot].userspace_addr,
+ .flags = slots[slot].flags,
+ };
+
+
+ DPRINTF("slot %d start %llx len %llx flags %x\n",
+ mem.slot,
+ mem.guest_phys_addr,
+ mem.memory_size,
+ mem.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
+ if (r == -1)
+ fprintf(stderr, "%s: %m\n", __FUNCTION__);
+ }
+ return r;
+}
+
+static int kvm_dirty_pages_log_change_all(kvm_context_t kvm,
+ int (*change)(kvm_context_t kvm,
+ uint64_t start,
+ uint64_t len))
+{
+ int i, r;
+
+ for (i=r=0; i<KVM_MAX_NUM_MEM_REGIONS && r==0; i++) {
+ if (slots[i].len)
+ r = change(kvm, slots[i].phys_addr, slots[i].len);
+ }
+ return r;
+}
+
+int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm,
+ uint64_t phys_addr,
+ uint64_t len)
+{
+ int slot = get_slot(phys_addr);
+
+ DPRINTF("start %"PRIx64" len %"PRIx64"\n", phys_addr, len);
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (slots[slot].logging_count++)
+ return 0;
+
+ return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr,
+ KVM_MEM_LOG_DIRTY_PAGES,
+ KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm,
+ uint64_t phys_addr,
+ uint64_t len)
+{
+ int slot = get_slot(phys_addr);
+
+ if (slot == -1) {
+ fprintf(stderr, "BUG: %s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ if (--slots[slot].logging_count)
+ return 0;
+
+ return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr,
+ 0,
+ KVM_MEM_LOG_DIRTY_PAGES);
+}
+
+/**
+ * Enable dirty page logging for all memory regions
+ */
+int kvm_dirty_pages_log_enable_all(kvm_context_t kvm)
+{
+ if (kvm->dirty_pages_log_all)
+ return 0;
+ kvm->dirty_pages_log_all = 1;
+ return kvm_dirty_pages_log_change_all(kvm,
+ kvm_dirty_pages_log_enable_slot);
+}
+
+/**
+ * Enable dirty page logging only for memory regions that were created with
+ * dirty logging enabled (disable for all other memory regions).
+ */
+int kvm_dirty_pages_log_reset(kvm_context_t kvm)
+{
+ if (!kvm->dirty_pages_log_all)
+ return 0;
+ kvm->dirty_pages_log_all = 0;
+ return kvm_dirty_pages_log_change_all(kvm,
+ kvm_dirty_pages_log_disable_slot);
+}
+
+
+kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
+ void *opaque)
+{
+ int fd;
+ kvm_context_t kvm;
+ int r, gsi_count;
+
+ fd = open("/dev/kvm", O_RDWR);
+ if (fd == -1) {
+ perror("open /dev/kvm");
+ return NULL;
+ }
+ r = ioctl(fd, KVM_GET_API_VERSION, 0);
+ if (r == -1) {
+ fprintf(stderr, "kvm kernel version too old: "
+ "KVM_GET_API_VERSION ioctl not supported\n");
+ goto out_close;
+ }
+ if (r < EXPECTED_KVM_API_VERSION) {
+ fprintf(stderr, "kvm kernel version too old: "
+ "We expect API version %d or newer, but got "
+ "version %d\n",
+ EXPECTED_KVM_API_VERSION, r);
+ goto out_close;
+ }
+ if (r > EXPECTED_KVM_API_VERSION) {
+ fprintf(stderr, "kvm userspace version too old\n");
+ goto out_close;
+ }
+ kvm_abi = r;
+ kvm_page_size = getpagesize();
+ kvm = malloc(sizeof(*kvm));
+ if (kvm == NULL)
+ goto out_close;
+ memset(kvm, 0, sizeof(*kvm));
+ kvm->fd = fd;
+ kvm->vm_fd = -1;
+ kvm->callbacks = callbacks;
+ kvm->opaque = opaque;
+ kvm->dirty_pages_log_all = 0;
+ kvm->no_irqchip_creation = 0;
+ kvm->no_pit_creation = 0;
+
+ gsi_count = kvm_get_gsi_count(kvm);
+ if (gsi_count > 0) {
+ int gsi_bits, i;
+
+ /* Round up so we can search ints using ffs */
+ gsi_bits = ALIGN(gsi_count, 32);
+ kvm->used_gsi_bitmap = malloc(gsi_bits / 8);
+ if (!kvm->used_gsi_bitmap)
+ goto out_close;
+ memset(kvm->used_gsi_bitmap, 0, gsi_bits / 8);
+ kvm->max_gsi = gsi_bits;
+
+ /* Mark any over-allocated bits as already in use */
+ for (i = gsi_count; i < gsi_bits; i++)
+ set_gsi(kvm, i);
+ }
+
+ return kvm;
+ out_close:
+ close(fd);
+ return NULL;
+}
+
+void kvm_finalize(kvm_context_t kvm)
+{
+ if (kvm->vcpu_fd[0] != -1)
+ close(kvm->vcpu_fd[0]);
+ if (kvm->vm_fd != -1)
+ close(kvm->vm_fd);
+ close(kvm->fd);
+ free(kvm);
+}
+
+void kvm_disable_irqchip_creation(kvm_context_t kvm)
+{
+ kvm->no_irqchip_creation = 1;
+}
+
+void kvm_disable_pit_creation(kvm_context_t kvm)
+{
+ kvm->no_pit_creation = 1;
+}
+
+int kvm_create_vcpu(kvm_context_t kvm, int slot)
+{
+ long mmap_size;
+ int r;
+
+ r = ioctl(kvm->vm_fd, KVM_CREATE_VCPU, slot);
+ if (r == -1) {
+ r = -errno;
+ fprintf(stderr, "kvm_create_vcpu: %m\n");
+ return r;
+ }
+ kvm->vcpu_fd[slot] = r;
+ mmap_size = ioctl(kvm->fd, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (mmap_size == -1) {
+ r = -errno;
+ fprintf(stderr, "get vcpu mmap size: %m\n");
+ return r;
+ }
+ kvm->run[slot] = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ kvm->vcpu_fd[slot], 0);
+ if (kvm->run[slot] == MAP_FAILED) {
+ r = -errno;
+ fprintf(stderr, "mmap vcpu area: %m\n");
+ return r;
+ }
+ return 0;
+}
+
+int kvm_create_vm(kvm_context_t kvm)
+{
+ int fd = kvm->fd;
+
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm->irq_routes = malloc(sizeof(*kvm->irq_routes));
+ if (!kvm->irq_routes)
+ return -ENOMEM;
+ memset(kvm->irq_routes, 0, sizeof(*kvm->irq_routes));
+ kvm->nr_allocated_irq_routes = 0;
+#endif
+
+ kvm->vcpu_fd[0] = -1;
+
+ fd = ioctl(fd, KVM_CREATE_VM, 0);
+ if (fd == -1) {
+ fprintf(stderr, "kvm_create_vm: %m\n");
+ return -1;
+ }
+ kvm->vm_fd = fd;
+ return 0;
+}
+
+static int kvm_create_default_phys_mem(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+#ifdef KVM_CAP_USER_MEMORY
+ int r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
+ if (r > 0)
+ return 0;
+ fprintf(stderr, "Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported\n");
+#else
+#error Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported
+#endif
+ return -1;
+}
+
+int kvm_check_extension(kvm_context_t kvm, int ext)
+{
+ int ret;
+
+ ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, ext);
+ if (ret > 0)
+ return ret;
+ return 0;
+}
+
+void kvm_create_irqchip(kvm_context_t kvm)
+{
+ int r;
+
+ kvm->irqchip_in_kernel = 0;
+#ifdef KVM_CAP_IRQCHIP
+ if (!kvm->no_irqchip_creation) {
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_IRQCHIP);
+ if (r > 0) { /* kernel irqchip supported */
+ r = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP);
+ if (r >= 0) {
+ kvm->irqchip_inject_ioctl = KVM_IRQ_LINE;
+#if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS)
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION,
+ KVM_CAP_IRQ_INJECT_STATUS);
+ if (r > 0)
+ kvm->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS;
+#endif
+ kvm->irqchip_in_kernel = 1;
+ }
+ else
+ fprintf(stderr, "Create kernel PIC irqchip failed\n");
+ }
+ }
+#endif
+}
+
+int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem)
+{
+ int r;
+
+ r = kvm_create_vm(kvm);
+ if (r < 0)
+ return r;
+ r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0)
+ return r;
+ init_slots();
+ r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0)
+ return r;
+ kvm_create_irqchip(kvm);
+
+ return 0;
+}
+
+
+void *kvm_create_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len, int log, int writable)
+{
+ int r;
+ int prot = PROT_READ;
+ void *ptr;
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = len,
+ .guest_phys_addr = phys_start,
+ .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0,
+ };
+
+ if (writable)
+ prot |= PROT_WRITE;
+
+#if !defined(__s390__)
+ ptr = mmap(NULL, len, prot, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+#else
+ ptr = mmap(LIBKVM_S390_ORIGIN, len, prot | PROT_EXEC,
+ MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+#endif
+ if (ptr == MAP_FAILED) {
+ fprintf(stderr, "%s: %s", __func__, strerror(errno));
+ return 0;
+ }
+
+ memset(ptr, 0, len);
+
+ memory.userspace_addr = (unsigned long)ptr;
+ memory.slot = get_free_slot(kvm);
+ DPRINTF("slot %d start %llx len %llx flags %x\n",
+ memory.slot,
+ memory.guest_phys_addr,
+ memory.memory_size,
+ memory.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "%s: %s", __func__, strerror(errno));
+ return 0;
+ }
+ register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size,
+ memory.userspace_addr, memory.flags);
+
+ return ptr;
+}
+
+int kvm_register_phys_mem(kvm_context_t kvm,
+ unsigned long phys_start, void *userspace_addr,
+ unsigned long len, int log)
+{
+
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = len,
+ .guest_phys_addr = phys_start,
+ .userspace_addr = (unsigned long)(intptr_t)userspace_addr,
+ .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0,
+ };
+ int r;
+
+ memory.slot = get_free_slot(kvm);
+ DPRINTF("memory: gpa: %llx, size: %llx, uaddr: %llx, slot: %x, flags: %lx\n",
+ memory.guest_phys_addr, memory.memory_size,
+ memory.userspace_addr, memory.slot, memory.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "create_userspace_phys_mem: %s\n", strerror(errno));
+ return -1;
+ }
+ register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size,
+ memory.userspace_addr, memory.flags);
+ return 0;
+}
+
+
+/* destroy/free a whole slot.
+ * phys_start, len and slot are the params passed to kvm_create_phys_mem()
+ */
+void kvm_destroy_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long len)
+{
+ int slot;
+ int r;
+ struct kvm_userspace_memory_region memory = {
+ .memory_size = 0,
+ .guest_phys_addr = phys_start,
+ .userspace_addr = 0,
+ .flags = 0,
+ };
+
+ slot = get_slot(phys_start);
+
+ if ((slot >= KVM_MAX_NUM_MEM_REGIONS) || (slot == -1)) {
+ fprintf(stderr, "BUG: %s: invalid parameters (slot=%d)\n",
+ __FUNCTION__, slot);
+ return;
+ }
+ if (phys_start != slots[slot].phys_addr) {
+ fprintf(stderr,
+ "WARNING: %s: phys_start is 0x%lx expecting 0x%lx\n",
+ __FUNCTION__, phys_start, slots[slot].phys_addr);
+ phys_start = slots[slot].phys_addr;
+ }
+
+ memory.slot = slot;
+ DPRINTF("slot %d start %llx len %llx flags %x\n",
+ memory.slot,
+ memory.guest_phys_addr,
+ memory.memory_size,
+ memory.flags);
+ r = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &memory);
+ if (r == -1) {
+ fprintf(stderr, "destroy_userspace_phys_mem: %s",
+ strerror(errno));
+ return;
+ }
+
+ free_slot(memory.slot);
+}
+
+void kvm_unregister_memory_area(kvm_context_t kvm, uint64_t phys_addr, unsigned long size)
+{
+
+ int slot = get_container_slot(phys_addr, size);
+
+ if (slot != -1) {
+ DPRINTF("Unregistering memory region %llx (%lx)\n", phys_addr, size);
+ kvm_destroy_phys_mem(kvm, phys_addr, size);
+ return;
+ }
+}
+
+static int kvm_get_map(kvm_context_t kvm, int ioctl_num, int slot, void *buf)
+{
+ int r;
+ struct kvm_dirty_log log = {
+ .slot = slot,
+ };
+
+ log.dirty_bitmap = buf;
+
+ r = ioctl(kvm->vm_fd, ioctl_num, &log);
+ if (r == -1)
+ return -errno;
+ return 0;
+}
+
+int kvm_get_dirty_pages(kvm_context_t kvm, unsigned long phys_addr, void *buf)
+{
+ int slot;
+
+ slot = get_slot(phys_addr);
+ return kvm_get_map(kvm, KVM_GET_DIRTY_LOG, slot, buf);
+}
+
+int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long len, void *buf, void *opaque,
+ int (*cb)(unsigned long start, unsigned long len,
+ void*bitmap, void *opaque))
+{
+ int i;
+ int r;
+ unsigned long end_addr = phys_addr + len;
+
+ for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) {
+ if ((slots[i].len && (uint64_t)slots[i].phys_addr >= phys_addr)
+ && ((uint64_t)slots[i].phys_addr + slots[i].len <= end_addr)) {
+ r = kvm_get_map(kvm, KVM_GET_DIRTY_LOG, i, buf);
+ if (r)
+ return r;
+ r = cb(slots[i].phys_addr, slots[i].len, buf, opaque);
+ if (r)
+ return r;
+ }
+ }
+ return 0;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status)
+{
+ struct kvm_irq_level event;
+ int r;
+
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ event.level = level;
+ event.irq = irq;
+ r = ioctl(kvm->vm_fd, kvm->irqchip_inject_ioctl, &event);
+ if (r == -1)
+ perror("kvm_set_irq_level");
+
+ if (status) {
+#ifdef KVM_CAP_IRQ_INJECT_STATUS
+ *status = (kvm->irqchip_inject_ioctl == KVM_IRQ_LINE) ?
+ 1 : event.status;
+#else
+ *status = 1;
+#endif
+ }
+
+ return 1;
+}
+
+int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip)
+{
+ int r;
+
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ r = ioctl(kvm->vm_fd, KVM_GET_IRQCHIP, chip);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_get_irqchip\n");
+ }
+ return r;
+}
+
+int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip)
+{
+ int r;
+
+ if (!kvm->irqchip_in_kernel)
+ return 0;
+ r = ioctl(kvm->vm_fd, KVM_SET_IRQCHIP, chip);
+ if (r == -1) {
+ r = -errno;
+ perror("kvm_set_irqchip\n");
+ }
+ return r;
+}
+
+#endif
+
+static int handle_io(kvm_context_t kvm, struct kvm_run *run, int vcpu)
+{
+ uint16_t addr = run->io.port;
+ int r;
+ int i;
+ void *p = (void *)run + run->io.data_offset;
+
+ for (i = 0; i < run->io.count; ++i) {
+ switch (run->io.direction) {
+ case KVM_EXIT_IO_IN:
+ switch (run->io.size) {
+ case 1:
+ r = kvm->callbacks->inb(kvm->opaque, addr, p);
+ break;
+ case 2:
+ r = kvm->callbacks->inw(kvm->opaque, addr, p);
+ break;
+ case 4:
+ r = kvm->callbacks->inl(kvm->opaque, addr, p);
+ break;
+ default:
+ fprintf(stderr, "bad I/O size %d\n", run->io.size);
+ return -EMSGSIZE;
+ }
+ break;
+ case KVM_EXIT_IO_OUT:
+ switch (run->io.size) {
+ case 1:
+ r = kvm->callbacks->outb(kvm->opaque, addr,
+ *(uint8_t *)p);
+ break;
+ case 2:
+ r = kvm->callbacks->outw(kvm->opaque, addr,
+ *(uint16_t *)p);
+ break;
+ case 4:
+ r = kvm->callbacks->outl(kvm->opaque, addr,
+ *(uint32_t *)p);
+ break;
+ default:
+ fprintf(stderr, "bad I/O size %d\n", run->io.size);
+ return -EMSGSIZE;
+ }
+ break;
+ default:
+ fprintf(stderr, "bad I/O direction %d\n", run->io.direction);
+ return -EPROTO;
+ }
+
+ p += run->io.size;
+ }
+
+ return 0;
+}
+
+int handle_debug(kvm_context_t kvm, int vcpu, void *env)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return kvm->callbacks->debug(kvm->opaque, env, &run->debug.arch);
+#else
+ return 0;
+#endif
+}
+
+int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_REGS, regs);
+}
+
+int kvm_set_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_REGS, regs);
+}
+
+int kvm_get_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_FPU, fpu);
+}
+
+int kvm_set_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_FPU, fpu);
+}
+
+int kvm_get_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *sregs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_SREGS, sregs);
+}
+
+int kvm_set_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *sregs)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SREGS, sregs);
+}
+
+#ifdef KVM_CAP_MP_STATE
+int kvm_get_mpstate(kvm_context_t kvm, int vcpu, struct kvm_mp_state *mp_state)
+{
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE);
+ if (r > 0)
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_GET_MP_STATE, mp_state);
+ return -ENOSYS;
+}
+
+int kvm_set_mpstate(kvm_context_t kvm, int vcpu, struct kvm_mp_state *mp_state)
+{
+ int r;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE);
+ if (r > 0)
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_MP_STATE, mp_state);
+ return -ENOSYS;
+}
+#endif
+
+static int handle_mmio(kvm_context_t kvm, struct kvm_run *kvm_run)
+{
+ unsigned long addr = kvm_run->mmio.phys_addr;
+ void *data = kvm_run->mmio.data;
+
+ /* hack: Red Hat 7.1 generates these weird accesses. */
+ if ((addr > 0xa0000-4 && addr <= 0xa0000) && kvm_run->mmio.len == 3)
+ return 0;
+
+ if (kvm_run->mmio.is_write)
+ return kvm->callbacks->mmio_write(kvm->opaque, addr, data,
+ kvm_run->mmio.len);
+ else
+ return kvm->callbacks->mmio_read(kvm->opaque, addr, data,
+ kvm_run->mmio.len);
+}
+
+int handle_io_window(kvm_context_t kvm)
+{
+ return kvm->callbacks->io_window(kvm->opaque);
+}
+
+int handle_halt(kvm_context_t kvm, int vcpu)
+{
+ return kvm->callbacks->halt(kvm->opaque, vcpu);
+}
+
+int handle_shutdown(kvm_context_t kvm, void *env)
+{
+ return kvm->callbacks->shutdown(kvm->opaque, env);
+}
+
+int try_push_interrupts(kvm_context_t kvm)
+{
+ return kvm->callbacks->try_push_interrupts(kvm->opaque);
+}
+
+static inline void push_nmi(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_USER_NMI
+ kvm->callbacks->push_nmi(kvm->opaque);
+#endif /* KVM_CAP_USER_NMI */
+}
+
+void post_kvm_run(kvm_context_t kvm, void *env)
+{
+ kvm->callbacks->post_kvm_run(kvm->opaque, env);
+}
+
+int pre_kvm_run(kvm_context_t kvm, void *env)
+{
+ return kvm->callbacks->pre_kvm_run(kvm->opaque, env);
+}
+
+int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->if_flag;
+}
+
+int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_run *run = kvm->run[vcpu];
+
+ return run->ready_for_interrupt_injection;
+}
+
+int kvm_run(kvm_context_t kvm, int vcpu, void *env)
+{
+ int r;
+ int fd = kvm->vcpu_fd[vcpu];
+ struct kvm_run *run = kvm->run[vcpu];
+
+again:
+ push_nmi(kvm);
+#if !defined(__s390__)
+ if (!kvm->irqchip_in_kernel)
+ run->request_interrupt_window = try_push_interrupts(kvm);
+#endif
+ r = pre_kvm_run(kvm, env);
+ if (r)
+ return r;
+ r = ioctl(fd, KVM_RUN, 0);
+
+ if (r == -1 && errno != EINTR && errno != EAGAIN) {
+ r = -errno;
+ post_kvm_run(kvm, env);
+ fprintf(stderr, "kvm_run: %s\n", strerror(-r));
+ return r;
+ }
+
+ post_kvm_run(kvm, env);
+
+#if defined(KVM_CAP_COALESCED_MMIO)
+ if (kvm->coalesced_mmio) {
+ struct kvm_coalesced_mmio_ring *ring = (void *)run +
+ kvm->coalesced_mmio * PAGE_SIZE;
+ while (ring->first != ring->last) {
+ kvm->callbacks->mmio_write(kvm->opaque,
+ ring->coalesced_mmio[ring->first].phys_addr,
+ &ring->coalesced_mmio[ring->first].data[0],
+ ring->coalesced_mmio[ring->first].len);
+ smp_wmb();
+ ring->first = (ring->first + 1) %
+ KVM_COALESCED_MMIO_MAX;
+ }
+ }
+#endif
+
+#if !defined(__s390__)
+ if (r == -1) {
+ r = handle_io_window(kvm);
+ goto more;
+ }
+#endif
+ if (1) {
+ switch (run->exit_reason) {
+ case KVM_EXIT_UNKNOWN:
+ fprintf(stderr, "unhandled vm exit: 0x%x vcpu_id %d\n",
+ (unsigned)run->hw.hardware_exit_reason, vcpu);
+ kvm_show_regs(kvm, vcpu);
+ abort();
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ fprintf(stderr, "kvm_run: failed entry, reason %u\n",
+ (unsigned)run->fail_entry.hardware_entry_failure_reason & 0xffff);
+ kvm_show_regs(kvm, vcpu);
+ return -ENOEXEC;
+ break;
+ case KVM_EXIT_EXCEPTION:
+ fprintf(stderr, "exception %d (%x)\n",
+ run->ex.exception,
+ run->ex.error_code);
+ kvm_show_regs(kvm, vcpu);
+ kvm_show_code(kvm, vcpu);
+ abort();
+ break;
+ case KVM_EXIT_IO:
+ r = handle_io(kvm, run, vcpu);
+ break;
+ case KVM_EXIT_DEBUG:
+ r = handle_debug(kvm, vcpu, env);
+ break;
+ case KVM_EXIT_MMIO:
+ r = handle_mmio(kvm, run);
+ break;
+ case KVM_EXIT_HLT:
+ r = handle_halt(kvm, vcpu);
+ break;
+ case KVM_EXIT_IRQ_WINDOW_OPEN:
+ break;
+ case KVM_EXIT_SHUTDOWN:
+ r = handle_shutdown(kvm, env);
+ break;
+#if defined(__s390__)
+ case KVM_EXIT_S390_SIEIC:
+ r = kvm->callbacks->s390_handle_intercept(kvm, vcpu,
+ run);
+ break;
+ case KVM_EXIT_S390_RESET:
+ r = kvm->callbacks->s390_handle_reset(kvm, vcpu, run);
+ break;
+#endif
+ default:
+ if (kvm_arch_run(run, kvm, vcpu)) {
+ fprintf(stderr, "unhandled vm exit: 0x%x\n",
+ run->exit_reason);
+ kvm_show_regs(kvm, vcpu);
+ abort();
+ }
+ break;
+ }
+ }
+more:
+ if (!r)
+ goto again;
+ return r;
+}
+
+int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq)
+{
+ struct kvm_interrupt intr;
+
+ intr.irq = irq;
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_INTERRUPT, &intr);
+}
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t kvm, int vcpu, struct kvm_guest_debug *dbg)
+{
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_SET_GUEST_DEBUG, dbg);
+}
+#endif
+
+int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset)
+{
+ struct kvm_signal_mask *sigmask;
+ int r;
+
+ if (!sigset) {
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SIGNAL_MASK, NULL);
+ if (r == -1)
+ r = -errno;
+ return r;
+ }
+ sigmask = malloc(sizeof(*sigmask) + sizeof(*sigset));
+ if (!sigmask)
+ return -ENOMEM;
+
+ sigmask->len = 8;
+ memcpy(sigmask->sigset, sigset, sizeof(*sigset));
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_SIGNAL_MASK, sigmask);
+ if (r == -1)
+ r = -errno;
+ free(sigmask);
+ return r;
+}
+
+int kvm_irqchip_in_kernel(kvm_context_t kvm)
+{
+ return kvm->irqchip_in_kernel;
+}
+
+int kvm_pit_in_kernel(kvm_context_t kvm)
+{
+ return kvm->pit_in_kernel;
+}
+
+int kvm_has_sync_mmu(kvm_context_t kvm)
+{
+ int r = 0;
+#ifdef KVM_CAP_SYNC_MMU
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU);
+#endif
+ return r;
+}
+
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu)
+{
+#ifdef KVM_CAP_USER_NMI
+ return ioctl(kvm->vcpu_fd[vcpu], KVM_NMI);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_init_coalesced_mmio(kvm_context_t kvm)
+{
+ int r = 0;
+ kvm->coalesced_mmio = 0;
+#ifdef KVM_CAP_COALESCED_MMIO
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO);
+ if (r > 0) {
+ kvm->coalesced_mmio = r;
+ return 0;
+ }
+#endif
+ return r;
+}
+
+int kvm_register_coalesced_mmio(kvm_context_t kvm, uint64_t addr, uint32_t size)
+{
+#ifdef KVM_CAP_COALESCED_MMIO
+ struct kvm_coalesced_mmio_zone zone;
+ int r;
+
+ if (kvm->coalesced_mmio) {
+
+ zone.addr = addr;
+ zone.size = size;
+
+ r = ioctl(kvm->vm_fd, KVM_REGISTER_COALESCED_MMIO, &zone);
+ if (r == -1) {
+ perror("kvm_register_coalesced_mmio_zone");
+ return -errno;
+ }
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+int kvm_unregister_coalesced_mmio(kvm_context_t kvm, uint64_t addr, uint32_t size)
+{
+#ifdef KVM_CAP_COALESCED_MMIO
+ struct kvm_coalesced_mmio_zone zone;
+ int r;
+
+ if (kvm->coalesced_mmio) {
+
+ zone.addr = addr;
+ zone.size = size;
+
+ r = ioctl(kvm->vm_fd, KVM_UNREGISTER_COALESCED_MMIO, &zone);
+ if (r == -1) {
+ perror("kvm_unregister_coalesced_mmio_zone");
+ return -errno;
+ }
+ DPRINTF("Unregistered coalesced mmio region for %llx (%lx)\n", addr, size);
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+int kvm_assign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_PCI_DEVICE, assigned_dev);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+static int kvm_old_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_IRQ, assigned_irq);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+#ifdef KVM_CAP_ASSIGN_DEV_IRQ
+int kvm_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ int ret;
+
+ ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ);
+ if (ret > 0) {
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_DEV_IRQ, assigned_irq);
+ if (ret < 0)
+ return -errno;
+ return ret;
+ }
+
+ return kvm_old_assign_irq(kvm, assigned_irq);
+}
+
+int kvm_deassign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_DEASSIGN_DEV_IRQ, assigned_irq);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+#else
+int kvm_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_old_assign_irq(kvm, assigned_irq);
+}
+#endif
+#endif
+
+#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
+int kvm_deassign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_DEASSIGN_PCI_DEVICE, assigned_dev);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+#endif
+
+int kvm_destroy_memory_region_works(kvm_context_t kvm)
+{
+ int ret = 0;
+
+#ifdef KVM_CAP_DESTROY_MEMORY_REGION_WORKS
+ ret = ioctl(kvm->fd, KVM_CHECK_EXTENSION,
+ KVM_CAP_DESTROY_MEMORY_REGION_WORKS);
+ if (ret <= 0)
+ ret = 0;
+#endif
+ return ret;
+}
+
+int kvm_reinject_control(kvm_context_t kvm, int pit_reinject)
+{
+#ifdef KVM_CAP_REINJECT_CONTROL
+ int r;
+ struct kvm_reinject_control control;
+
+ control.pit_reinject = pit_reinject;
+
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL);
+ if (r > 0) {
+ r = ioctl(kvm->vm_fd, KVM_REINJECT_CONTROL, &control);
+ if (r == -1)
+ return -errno;
+ return r;
+ }
+#endif
+ return -ENOSYS;
+}
+
+int kvm_has_gsi_routing(kvm_context_t kvm)
+{
+ int r = 0;
+
+#ifdef KVM_CAP_IRQ_ROUTING
+ r = kvm_check_extension(kvm, KVM_CAP_IRQ_ROUTING);
+#endif
+ return r;
+}
+
+int kvm_get_gsi_count(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ return kvm_check_extension(kvm, KVM_CAP_IRQ_ROUTING);
+#else
+ return -EINVAL;
+#endif
+}
+
+int kvm_clear_gsi_routes(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm->irq_routes->nr = 0;
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
+int kvm_add_routing_entry(kvm_context_t kvm,
+ struct kvm_irq_routing_entry* entry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing *z;
+ struct kvm_irq_routing_entry *new;
+ int n, size;
+
+ if (kvm->irq_routes->nr == kvm->nr_allocated_irq_routes) {
+ n = kvm->nr_allocated_irq_routes * 2;
+ if (n < 64)
+ n = 64;
+ size = sizeof(struct kvm_irq_routing);
+ size += n * sizeof(*new);
+ z = realloc(kvm->irq_routes, size);
+ if (!z)
+ return -ENOMEM;
+ kvm->nr_allocated_irq_routes = n;
+ kvm->irq_routes = z;
+ }
+ n = kvm->irq_routes->nr++;
+ new = &kvm->irq_routes->entries[n];
+ memset(new, 0, sizeof(*new));
+ new->gsi = entry->gsi;
+ new->type = entry->type;
+ new->flags = entry->flags;
+ new->u = entry->u;
+
+ set_gsi(kvm, entry->gsi);
+
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e;
+
+ e.gsi = gsi;
+ e.type = KVM_IRQ_ROUTING_IRQCHIP;
+ e.flags = 0;
+ e.u.irqchip.irqchip = irqchip;
+ e.u.irqchip.pin = pin;
+ return kvm_add_routing_entry(kvm, &e);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_del_routing_entry(kvm_context_t kvm,
+ struct kvm_irq_routing_entry* entry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry *e, *p;
+ int i, gsi, found = 0;
+
+ gsi = entry->gsi;
+
+ for (i = 0; i < kvm->irq_routes->nr; ++i) {
+ e = &kvm->irq_routes->entries[i];
+ if (e->type == entry->type
+ && e->gsi == gsi) {
+ switch (e->type)
+ {
+ case KVM_IRQ_ROUTING_IRQCHIP: {
+ if (e->u.irqchip.irqchip ==
+ entry->u.irqchip.irqchip
+ && e->u.irqchip.pin ==
+ entry->u.irqchip.pin) {
+ p = &kvm->irq_routes->
+ entries[--kvm->irq_routes->nr];
+ *e = *p;
+ found = 1;
+ }
+ break;
+ }
+ case KVM_IRQ_ROUTING_MSI: {
+ if (e->u.msi.address_lo ==
+ entry->u.msi.address_lo
+ && e->u.msi.address_hi ==
+ entry->u.msi.address_hi
+ && e->u.msi.data == entry->u.msi.data) {
+ p = &kvm->irq_routes->
+ entries[--kvm->irq_routes->nr];
+ *e = *p;
+ found = 1;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (found) {
+ /* If there are no other users of this GSI
+ * mark it available in the bitmap */
+ for (i = 0; i < kvm->irq_routes->nr; i++) {
+ e = &kvm->irq_routes->entries[i];
+ if (e->gsi == gsi)
+ break;
+ }
+ if (i == kvm->irq_routes->nr)
+ clear_gsi(kvm, gsi);
+
+ return 0;
+ }
+ }
+ }
+ return -ESRCH;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e;
+
+ e.gsi = gsi;
+ e.type = KVM_IRQ_ROUTING_IRQCHIP;
+ e.flags = 0;
+ e.u.irqchip.irqchip = irqchip;
+ e.u.irqchip.pin = pin;
+ return kvm_del_routing_entry(kvm, &e);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_commit_irq_routes(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ int r;
+
+ kvm->irq_routes->flags = 0;
+ r = ioctl(kvm->vm_fd, KVM_SET_GSI_ROUTING, kvm->irq_routes);
+ if (r == -1)
+ r = -errno;
+ return r;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_get_irq_route_gsi(kvm_context_t kvm)
+{
+ int i, bit;
+ uint32_t *buf = kvm->used_gsi_bitmap;
+
+ /* Return the lowest unused GSI in the bitmap */
+ for (i = 0; i < kvm->max_gsi / 32; i++) {
+ bit = ffs(~buf[i]);
+ if (!bit)
+ continue;
+
+ return bit - 1 + i * 32;
+ }
+
+ return -ENOSPC;
+}
+
+#ifdef KVM_CAP_DEVICE_MSIX
+int kvm_assign_set_msix_nr(kvm_context_t kvm,
+ struct kvm_assigned_msix_nr *msix_nr)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_SET_MSIX_NR, msix_nr);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+
+int kvm_assign_set_msix_entry(kvm_context_t kvm,
+ struct kvm_assigned_msix_entry *entry)
+{
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_ASSIGN_SET_MSIX_ENTRY, entry);
+ if (ret < 0)
+ return -errno;
+
+ return ret;
+}
+#endif
diff --git a/kvm/libkvm/libkvm.h b/kvm/libkvm/libkvm.h
new file mode 100644
index 0000000..a70945d
--- /dev/null
+++ b/kvm/libkvm/libkvm.h
@@ -0,0 +1,838 @@
+/** \file libkvm.h
+ * libkvm API
+ */
+
+#ifndef LIBKVM_H
+#define LIBKVM_H
+
+#if defined(__s390__)
+#include <asm/ptrace.h>
+#endif
+
+#include <stdint.h>
+
+#ifndef __user
+#define __user /* temporary, until installed via make headers_install */
+#endif
+
+#include <linux/kvm.h>
+
+#include <signal.h>
+
+struct kvm_context;
+
+typedef struct kvm_context *kvm_context_t;
+
+#if defined(__x86_64__) || defined(__i386__)
+struct kvm_msr_list *kvm_get_msr_list(kvm_context_t);
+int kvm_get_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
+int kvm_set_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
+#endif
+
+/*!
+ * \brief KVM callbacks structure
+ *
+ * This structure holds pointers to various functions that KVM will call
+ * when it encounters something that cannot be virtualized, such as
+ * accessing hardware devices via MMIO or regular IO.
+ */
+struct kvm_callbacks {
+ /// For 8bit IO reads from the guest (Usually when executing 'inb')
+ int (*inb)(void *opaque, uint16_t addr, uint8_t *data);
+ /// For 16bit IO reads from the guest (Usually when executing 'inw')
+ int (*inw)(void *opaque, uint16_t addr, uint16_t *data);
+ /// For 32bit IO reads from the guest (Usually when executing 'inl')
+ int (*inl)(void *opaque, uint16_t addr, uint32_t *data);
+ /// For 8bit IO writes from the guest (Usually when executing 'outb')
+ int (*outb)(void *opaque, uint16_t addr, uint8_t data);
+ /// For 16bit IO writes from the guest (Usually when executing 'outw')
+ int (*outw)(void *opaque, uint16_t addr, uint16_t data);
+ /// For 32bit IO writes from the guest (Usually when executing 'outl')
+ int (*outl)(void *opaque, uint16_t addr, uint32_t data);
+ /// generic memory reads to unmapped memory (For MMIO devices)
+ int (*mmio_read)(void *opaque, uint64_t addr, uint8_t *data,
+ int len);
+ /// generic memory writes to unmapped memory (For MMIO devices)
+ int (*mmio_write)(void *opaque, uint64_t addr, uint8_t *data,
+ int len);
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ int (*debug)(void *opaque, void *env,
+ struct kvm_debug_exit_arch *arch_info);
+#endif
+ /*!
+ * \brief Called when the VCPU issues an 'hlt' instruction.
+ *
+ * Typically, you should yeild here to prevent 100% CPU utilization
+ * on the host CPU.
+ */
+ int (*halt)(void *opaque, int vcpu);
+ int (*shutdown)(void *opaque, void *env);
+ int (*io_window)(void *opaque);
+ int (*try_push_interrupts)(void *opaque);
+#ifdef KVM_CAP_USER_NMI
+ void (*push_nmi)(void *opaque);
+#endif
+ void (*post_kvm_run)(void *opaque, void *env);
+ int (*pre_kvm_run)(void *opaque, void *env);
+ int (*tpr_access)(void *opaque, int vcpu, uint64_t rip, int is_write);
+#if defined(__powerpc__)
+ int (*powerpc_dcr_read)(int vcpu, uint32_t dcrn, uint32_t *data);
+ int (*powerpc_dcr_write)(int vcpu, uint32_t dcrn, uint32_t data);
+#endif
+#if defined(__s390__)
+ int (*s390_handle_intercept)(kvm_context_t context, int vcpu,
+ struct kvm_run *run);
+ int (*s390_handle_reset)(kvm_context_t context, int vcpu,
+ struct kvm_run *run);
+#endif
+};
+
+/*!
+ * \brief Create new KVM context
+ *
+ * This creates a new kvm_context. A KVM context is a small area of data that
+ * holds information about the KVM instance that gets created by this call.\n
+ * This should always be your first call to KVM.
+ *
+ * \param callbacks Pointer to a valid kvm_callbacks structure
+ * \param opaque Not used
+ * \return NULL on failure
+ */
+kvm_context_t kvm_init(struct kvm_callbacks *callbacks,
+ void *opaque);
+
+/*!
+ * \brief Cleanup the KVM context
+ *
+ * Should always be called when closing down KVM.\n
+ * Exception: If kvm_init() fails, this function should not be called, as the
+ * context would be invalid
+ *
+ * \param kvm Pointer to the kvm_context that is to be freed
+ */
+void kvm_finalize(kvm_context_t kvm);
+
+/*!
+ * \brief Disable the in-kernel IRQCHIP creation
+ *
+ * In-kernel irqchip is enabled by default. If userspace irqchip is to be used,
+ * this should be called prior to kvm_create().
+ *
+ * \param kvm Pointer to the kvm_context
+ */
+void kvm_disable_irqchip_creation(kvm_context_t kvm);
+
+/*!
+ * \brief Disable the in-kernel PIT creation
+ *
+ * In-kernel pit is enabled by default. If userspace pit is to be used,
+ * this should be called prior to kvm_create().
+ *
+ * \param kvm Pointer to the kvm_context
+ */
+void kvm_disable_pit_creation(kvm_context_t kvm);
+
+/*!
+ * \brief Create new virtual machine
+ *
+ * This creates a new virtual machine, maps physical RAM to it, and creates a
+ * virtual CPU for it.\n
+ * \n
+ * Memory gets mapped for addresses 0->0xA0000, 0xC0000->phys_mem_bytes
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param phys_mem_bytes The amount of physical ram you want the VM to have
+ * \param phys_mem This pointer will be set to point to the memory that
+ * kvm_create allocates for physical RAM
+ * \return 0 on success
+ */
+int kvm_create(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **phys_mem);
+int kvm_create_vm(kvm_context_t kvm);
+int kvm_check_extension(kvm_context_t kvm, int ext);
+void kvm_create_irqchip(kvm_context_t kvm);
+
+/*!
+ * \brief Create a new virtual cpu
+ *
+ * This creates a new virtual cpu (the first vcpu is created by kvm_create()).
+ * Should be called from a thread dedicated to the vcpu.
+ *
+ * \param kvm kvm context
+ * \param slot vcpu number (> 0)
+ * \return 0 on success, -errno on failure
+ */
+int kvm_create_vcpu(kvm_context_t kvm, int slot);
+
+/*!
+ * \brief Start the VCPU
+ *
+ * This starts the VCPU and virtualization is started.\n
+ * \n
+ * This function will not return until any of these conditions are met:
+ * - An IO/MMIO handler does not return "0"
+ * - An exception that neither the guest OS, nor KVM can handle occurs
+ *
+ * \note This function will call the callbacks registered in kvm_init()
+ * to emulate those functions
+ * \note If you at any point want to interrupt the VCPU, kvm_run() will
+ * listen to the EINTR signal. This allows you to simulate external interrupts
+ * and asyncronous IO.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be started
+ * \return 0 on success, but you really shouldn't expect this function to
+ * return except for when an error has occured, or when you have sent it
+ * an EINTR signal.
+ */
+int kvm_run(kvm_context_t kvm, int vcpu, void *env);
+
+/*!
+ * \brief Get interrupt flag from on last exit to userspace
+ *
+ * This gets the CPU interrupt flag as it was on the last exit to userspace.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return interrupt flag value (0 or 1)
+ */
+int kvm_get_interrupt_flag(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Get the value of the APIC_BASE msr as of last exit to userspace
+ *
+ * This gets the APIC_BASE msr as it was on the last exit to userspace.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return APIC_BASE msr contents
+ */
+uint64_t kvm_get_apic_base(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Check if a vcpu is ready for interrupt injection
+ *
+ * This checks if vcpu interrupts are not masked by mov ss or sti.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return boolean indicating interrupt injection readiness
+ */
+int kvm_is_ready_for_interrupt_injection(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Read VCPU registers
+ *
+ * This gets the GP registers from the VCPU and outputs them
+ * into a kvm_regs structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPUs GP registers, you should call kvm_set_regs()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_regs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_get_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs);
+
+/*!
+ * \brief Write VCPU registers
+ *
+ * This sets the GP registers on the VCPU from a kvm_regs structure
+ *
+ * \note When this function returns, the regs pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_regs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_set_regs(kvm_context_t kvm, int vcpu, struct kvm_regs *regs);
+/*!
+ * \brief Read VCPU fpu registers
+ *
+ * This gets the FPU registers from the VCPU and outputs them
+ * into a kvm_fpu structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPU FPU registers, you should call kvm_set_fpu()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param fpu Pointer to a kvm_fpu which will be populated with the VCPUs
+ * fpu registers values
+ * \return 0 on success
+ */
+int kvm_get_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu);
+
+/*!
+ * \brief Write VCPU fpu registers
+ *
+ * This sets the FPU registers on the VCPU from a kvm_fpu structure
+ *
+ * \note When this function returns, the fpu pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param fpu Pointer to a kvm_fpu which holds the new vcpu fpu state
+ * \return 0 on success
+ */
+int kvm_set_fpu(kvm_context_t kvm, int vcpu, struct kvm_fpu *fpu);
+
+/*!
+ * \brief Read VCPU system registers
+ *
+ * This gets the non-GP registers from the VCPU and outputs them
+ * into a kvm_sregs structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPUs non-GP registers, you should call
+ * kvm_set_sregs()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_get_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *regs);
+
+/*!
+ * \brief Write VCPU system registers
+ *
+ * This sets the non-GP registers on the VCPU from a kvm_sregs structure
+ *
+ * \note When this function returns, the regs pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_set_sregs(kvm_context_t kvm, int vcpu, struct kvm_sregs *regs);
+
+#ifdef KVM_CAP_MP_STATE
+/*!
+ * * \brief Read VCPU MP state
+ *
+ */
+int kvm_get_mpstate(kvm_context_t kvm, int vcpu,
+ struct kvm_mp_state *mp_state);
+
+/*!
+ * * \brief Write VCPU MP state
+ *
+ */
+int kvm_set_mpstate(kvm_context_t kvm, int vcpu,
+ struct kvm_mp_state *mp_state);
+/*!
+ * * \brief Reset VCPU MP state
+ *
+ */
+static inline int kvm_reset_mpstate(kvm_context_t kvm, int vcpu)
+{
+ struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED};
+ return kvm_set_mpstate(kvm, vcpu, &mp_state);
+}
+#endif
+
+/*!
+ * \brief Simulate an external vectored interrupt
+ *
+ * This allows you to simulate an external vectored interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param irq Vector number
+ * \return 0 on success
+ */
+int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq);
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_set_guest_debug(kvm_context_t, int vcpu, struct kvm_guest_debug *dbg);
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Setting the number of shadow pages to be allocated to the vm
+ *
+ * \param kvm pointer to kvm_context
+ * \param nrshadow_pages number of pages to be allocated
+ */
+int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages);
+
+/*!
+ * \brief Getting the number of shadow pages that are allocated to the vm
+ *
+ * \param kvm pointer to kvm_context
+ * \param nrshadow_pages number of pages to be allocated
+ */
+int kvm_get_shadow_pages(kvm_context_t kvm , unsigned int *nrshadow_pages);
+
+/*!
+ * \brief Set up cr8 for next time the vcpu is executed
+ *
+ * This is a fast setter for cr8, which will be applied when the
+ * vcpu next enters guest mode.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param cr8 next cr8 value
+ */
+void kvm_set_cr8(kvm_context_t kvm, int vcpu, uint64_t cr8);
+
+/*!
+ * \brief Get cr8 for sync tpr in qemu apic emulation
+ *
+ * This is a getter for cr8, which used to sync with the tpr in qemu
+ * apic emualtion.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ */
+__u64 kvm_get_cr8(kvm_context_t kvm, int vcpu);
+#endif
+
+/*!
+ * \brief Set a vcpu's signal mask for guest mode
+ *
+ * A vcpu can have different signals blocked in guest mode and user mode.
+ * This allows guest execution to be interrupted on a signal, without requiring
+ * that the signal be delivered to a signal handler (the signal can be
+ * dequeued using sigwait(2).
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be initialized
+ * \param sigset signal mask for guest mode
+ * \return 0 on success, or -errno on error
+ */
+int kvm_set_signal_mask(kvm_context_t kvm, int vcpu, const sigset_t *sigset);
+
+/*!
+ * \brief Dump all VCPU information
+ *
+ * This dumps \b all the information that KVM has about a virtual CPU, namely:
+ * - GP Registers
+ * - System registers (selectors, descriptors, etc)
+ * - VMCS Data
+ * - MSRS
+ * - Pending interrupts
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+int kvm_dump_vcpu(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Dump VCPU registers
+ *
+ * This dumps some of the information that KVM has about a virtual CPU, namely:
+ * - GP Registers
+ *
+ * A much more verbose version of this is available as kvm_dump_vcpu()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+void kvm_show_regs(kvm_context_t kvm, int vcpu);
+
+
+void *kvm_create_phys_mem(kvm_context_t, unsigned long phys_start,
+ unsigned long len, int log, int writable);
+void kvm_destroy_phys_mem(kvm_context_t, unsigned long phys_start,
+ unsigned long len);
+void kvm_unregister_memory_area(kvm_context_t, uint64_t phys_start,
+ unsigned long len);
+
+int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_start, unsigned long size);
+int kvm_register_phys_mem(kvm_context_t kvm,
+ unsigned long phys_start, void *userspace_addr,
+ unsigned long len, int log);
+int kvm_get_dirty_pages(kvm_context_t, unsigned long phys_addr, void *buf);
+int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long end_addr, void *buf, void*opaque,
+ int (*cb)(unsigned long start, unsigned long len,
+ void*bitmap, void *opaque));
+int kvm_register_coalesced_mmio(kvm_context_t kvm,
+ uint64_t addr, uint32_t size);
+int kvm_unregister_coalesced_mmio(kvm_context_t kvm,
+ uint64_t addr, uint32_t size);
+
+/*!
+ * \brief Create a memory alias
+ *
+ * Aliases a portion of physical memory to another portion. If the guest
+ * accesses the alias region, it will behave exactly as if it accessed
+ * the target memory.
+ */
+int kvm_create_memory_alias(kvm_context_t,
+ uint64_t phys_start, uint64_t len,
+ uint64_t target_phys);
+
+/*!
+ * \brief Destroy a memory alias
+ *
+ * Removes an alias created with kvm_create_memory_alias().
+ */
+int kvm_destroy_memory_alias(kvm_context_t, uint64_t phys_start);
+
+/*!
+ * \brief Get a bitmap of guest ram pages which are allocated to the guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param phys_addr Memory slot phys addr
+ * \param bitmap Long aligned address of a big enough bitmap (one bit per page)
+ */
+int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *bitmap);
+int kvm_get_mem_map_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long len, void *buf, void *opaque,
+ int (*cb)(unsigned long start,unsigned long len,
+ void* bitmap, void* opaque));
+int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status);
+
+int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len);
+int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len);
+/*!
+ * \brief Enable dirty-pages-logging for all memory regions
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_dirty_pages_log_enable_all(kvm_context_t kvm);
+
+/*!
+ * \brief Disable dirty-page-logging for some memory regions
+ *
+ * Disable dirty-pages-logging for those memory regions that were
+ * created with dirty-page-logging disabled.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_dirty_pages_log_reset(kvm_context_t kvm);
+
+/*!
+ * \brief Query whether in kernel irqchip is used
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_irqchip_in_kernel(kvm_context_t kvm);
+
+int kvm_has_sync_mmu(kvm_context_t kvm);
+
+#ifdef KVM_CAP_IRQCHIP
+/*!
+ * \brief Dump in kernel IRQCHIP contents
+ *
+ * Dump one of the in kernel irq chip devices, including PIC (master/slave)
+ * and IOAPIC into a kvm_irqchip structure
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param chip The irq chip device to be dumped
+ */
+int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip);
+
+/*!
+ * \brief Set in kernel IRQCHIP contents
+ *
+ * Write one of the in kernel irq chip devices, including PIC (master/slave)
+ * and IOAPIC
+ *
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param chip THe irq chip device to be written
+ */
+int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip);
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Get in kernel local APIC for vcpu
+ *
+ * Save the local apic state including the timer of a virtual CPU
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be accessed
+ * \param s Local apic state of the specific virtual CPU
+ */
+int kvm_get_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s);
+
+/*!
+ * \brief Set in kernel local APIC for vcpu
+ *
+ * Restore the local apic state including the timer of a virtual CPU
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be accessed
+ * \param s Local apic state of the specific virtual CPU
+ */
+int kvm_set_lapic(kvm_context_t kvm, int vcpu, struct kvm_lapic_state *s);
+
+#endif
+
+/*!
+ * \brief Simulate an NMI
+ *
+ * This allows you to simulate a non-maskable interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+int kvm_inject_nmi(kvm_context_t kvm, int vcpu);
+
+#endif
+
+/*!
+ * \brief Query wheather in kernel pit is used
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_pit_in_kernel(kvm_context_t kvm);
+
+/*!
+ * \brief Initialize coalesced MMIO
+ *
+ * Check for coalesced MMIO capability and store in context
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_init_coalesced_mmio(kvm_context_t kvm);
+
+#ifdef KVM_CAP_PIT
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Get in kernel PIT of the virtual domain
+ *
+ * Save the PIT state.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param s PIT state of the virtual domain
+ */
+int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s);
+
+/*!
+ * \brief Set in kernel PIT of the virtual domain
+ *
+ * Restore the PIT state.
+ * Timer would be retriggerred after restored.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param s PIT state of the virtual domain
+ */
+int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s);
+#endif
+
+int kvm_reinject_control(kvm_context_t kvm, int pit_reinject);
+
+#endif
+
+#ifdef KVM_CAP_VAPIC
+
+/*!
+ * \brief Enable kernel tpr access reporting
+ *
+ * When tpr access reporting is enabled, the kernel will call the
+ * ->tpr_access() callback every time the guest vcpu accesses the tpr.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to enable tpr access reporting on
+ */
+int kvm_enable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+/*!
+ * \brief Disable kernel tpr access reporting
+ *
+ * Undoes the effect of kvm_enable_tpr_access_reporting().
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu vcpu to disable tpr access reporting on
+ */
+int kvm_disable_tpr_access_reporting(kvm_context_t kvm, int vcpu);
+
+int kvm_enable_vapic(kvm_context_t kvm, int vcpu, uint64_t vapic);
+
+#endif
+
+#if defined(__s390__)
+int kvm_s390_initial_reset(kvm_context_t kvm, int slot);
+int kvm_s390_interrupt(kvm_context_t kvm, int slot,
+ struct kvm_s390_interrupt *kvmint);
+int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw);
+int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr);
+#endif
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+/*!
+ * \brief Notifies host kernel about a PCI device to be assigned to a guest
+ *
+ * Used for PCI device assignment, this function notifies the host
+ * kernel about the assigning of the physical PCI device to a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_assign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev);
+
+/*!
+ * \brief Assign IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function assigns IRQ numbers for
+ * an physical device and guest IRQ handling.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq);
+
+#ifdef KVM_CAP_ASSIGN_DEV_IRQ
+/*!
+ * \brief Deassign IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function deassigns IRQ numbers
+ * for an assigned device.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_deassign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq);
+#endif
+#endif
+
+/*!
+ * \brief Determines whether destroying memory regions is allowed
+ *
+ * KVM before 2.6.29 had a bug when destroying memory regions.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_destroy_memory_region_works(kvm_context_t kvm);
+
+#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
+/*!
+ * \brief Notifies host kernel about a PCI device to be deassigned from a guest
+ *
+ * Used for hot remove PCI device, this function notifies the host
+ * kernel about the deassigning of the physical PCI device from a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_deassign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev);
+#endif
+
+/*!
+ * \brief Checks whether the generic irq routing capability is present
+ *
+ * Checks whether kvm can reroute interrupts among the various interrupt
+ * controllers.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_has_gsi_routing(kvm_context_t kvm);
+
+/*!
+ * \brief Determines the number of gsis that can be routed
+ *
+ * Returns the number of distinct gsis that can be routed by kvm. This is
+ * also the number of distinct routes (if a gsi has two routes, than another
+ * gsi cannot be used...)
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_get_gsi_count(kvm_context_t kvm);
+
+/*!
+ * \brief Clears the temporary irq routing table
+ *
+ * Clears the temporary irq routing table. Nothing is committed to the
+ * running VM.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_clear_gsi_routes(kvm_context_t kvm);
+
+/*!
+ * \brief Adds an irq route to the temporary irq routing table
+ *
+ * Adds an irq route to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin);
+
+/*!
+ * \brief Removes an irq route from the temporary irq routing table
+ *
+ * Adds an irq route to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin);
+
+struct kvm_irq_routing_entry;
+/*!
+ * \brief Adds a routing entry to the temporary irq routing table
+ *
+ * Adds a filled routing entry to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_add_routing_entry(kvm_context_t kvm,
+ struct kvm_irq_routing_entry* entry);
+
+/*!
+ * \brief Removes a routing from the temporary irq routing table
+ *
+ * Remove a routing to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_del_routing_entry(kvm_context_t kvm,
+ struct kvm_irq_routing_entry* entry);
+
+/*!
+ * \brief Commit the temporary irq routing table
+ *
+ * Commit the temporary irq routing table to the running VM.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_commit_irq_routes(kvm_context_t kvm);
+
+/*!
+ * \brief Get unused GSI number for irq routing table
+ *
+ * Get unused GSI number for irq routing table
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_get_irq_route_gsi(kvm_context_t kvm);
+
+#ifdef KVM_CAP_DEVICE_MSIX
+int kvm_assign_set_msix_nr(kvm_context_t kvm,
+ struct kvm_assigned_msix_nr *msix_nr);
+int kvm_assign_set_msix_entry(kvm_context_t kvm,
+ struct kvm_assigned_msix_entry *entry);
+#endif
+
+uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg);
+
+#endif
diff --git a/kvm/scripts/65-kvm.rules b/kvm/scripts/65-kvm.rules
new file mode 100644
index 0000000..481cfcf
--- /dev/null
+++ b/kvm/scripts/65-kvm.rules
@@ -0,0 +1 @@
+KERNEL=="kvm", NAME="%k", GROUP="kvm", MODE="0660"
diff --git a/kvm/scripts/kvm b/kvm/scripts/kvm
new file mode 100755
index 0000000..cddc931
--- /dev/null
+++ b/kvm/scripts/kvm
@@ -0,0 +1,226 @@
+#!/bin/sh
+# kvm init script Takes care for all VMM tasks
+#
+# chkconfig: - 99 01
+# description: The KVM is a kernel level Virtual Machine Monitor. \
+# Currently it starts a bridge and attached eth0 for it
+
+dir=$(dirname "$0")
+
+ifnum=${ifnum:-$(ip route list | awk '/^default / { print $NF }' | sed 's/^[^0-9]*//')}
+ifnum=${ifnum:-0}
+switch=${sw0:-sw${ifnum}}
+pif=${pif:-eth${ifnum}}
+antispoof=${antispoof:-no}
+command=$1
+
+if [ -f /etc/sysconfig/network-scripts/network-functions ]; then
+ . /etc/sysconfig/network-scripts/network-functions
+fi
+
+#check for bonding link aggregation
+bond_int=$(awk < /etc/sysconfig/network-scripts/ifcfg-${pif} '/^MASTER=/ { print $BF }' | sed 's/MASTER=//')
+if [ ${bond_int}"0" != "0" ]; then
+ pif=${bond_int}
+fi
+
+if [ -f /etc/sysconfig/network-scripts/ifcfg-${pif} ]; then
+ . /etc/sysconfig/network-scripts/ifcfg-${pif}
+fi
+
+get_ip_info() {
+ addr=`ip addr show dev $1 | egrep '^ *inet' | sed -e 's/ *inet //' -e 's/ .*//'`
+ gateway=$(ip route list | awk '/^default / { print $3 }')
+ broadcast=$(/sbin/ip addr show dev $1 | grep inet | awk '/brd / { print $4 }')
+}
+
+#When a bonding device link goes down, its slave interfaces
+#are getting detached so they should be re-added
+bond_link_up () {
+ dev=$1
+ is_bonding=$(echo ${dev} | awk '/^bond/ { print $NF }')
+ if [ ${is_bonding}"0" != "0" ]; then
+ for slave in `awk < /proc/net/bonding/bond0 '/Slave Interface: / {print $3 }'`; do
+ ifenslave $dev $slave
+ done
+ fi
+}
+
+
+do_ifup() {
+ if [ ${addr} ] ; then
+ ip addr flush $1
+ bond_link_up $1
+ ip addr add ${addr} broadcast ${broadcast} dev $1
+ ip link set dev $1 up
+ fi
+}
+
+link_exists()
+{
+ if ip link show "$1" >/dev/null 2>/dev/null
+ then
+ return 0
+ else
+ return 1
+ fi
+}
+
+create_switch () {
+ local switch=$1
+
+ if [ ! -e "/sys/class/net/${switch}/bridge" ]; then
+ brctl addbr ${switch} >/dev/null 2>&1
+ brctl stp ${switch} off >/dev/null 2>&1
+ brctl setfd ${switch} 0.1 >/dev/null 2>&1
+ fi
+ ip link set ${switch} up >/dev/null 2>&1
+}
+
+
+add_to_switch () {
+ local switch=$1
+ local dev=$2
+
+ if [ ! -e "/sys/class/net/${switch}/brif/${dev}" ]; then
+ brctl addif ${switch} ${dev} >/dev/null 2>&1
+ fi
+
+ ip link set ${dev} up >/dev/null 2>&1
+}
+
+#taken from Xen
+transfer_routes () {
+ local src=$1
+ local dst=$2
+ # List all routes and grep the ones with $src in.
+ # Stick 'ip route del' on the front to delete.
+ # Change $src to $dst and use 'ip route add' to add.
+ ip route list | sed -ne "
+/dev ${src}\( \|$\)/ {
+ h
+ s/^/ip route del /
+ P
+ g
+ s/${src}/${dst}/
+ s/^/ip route add /
+ P
+ d
+}" | sh -e
+}
+
+
+change_ips() {
+ local src=$1
+ local dst=$2
+
+ #take care also for case we do not have /etc/sysconfig data (the switch as a src case)
+ if [ -x $BOOTPROTO ]; then
+ if [ -x $(pgrep dhclient) ];then
+ BOOTPROTO="null"
+ else
+ BOOTPROTO="dhcp"
+ fi
+ fi
+
+ if [ $BOOTPROTO = "dhcp" ]; then
+ ifdown ${src} >/dev/null 2>&1 || true
+ ip link set ${src} up >/dev/null 2>&1
+ bond_link_up ${src}
+ pkill dhclient >/dev/null 2>&1
+ for ((i=0;i<3;i++)); do
+ pgrep dhclient >/dev/null 2>&1 || i=4
+ sleep 1
+ done
+ dhclient ${dst} >/dev/null 2>&1
+ else
+ get_ip_info ${src}
+ ifconfig ${src} 0.0.0.0
+ do_ifup ${dst}
+ transfer_routes ${src} ${dst}
+ ip route add default via ${gateway} dev ${dst}
+ fi
+}
+
+antispoofing () {
+ iptables -P FORWARD DROP >/dev/null 2>&1
+ iptables -F FORWARD >/dev/null 2>&1
+ iptables -A FORWARD -m physdev --physdev-in ${dev} -j ACCEPT >/dev/null 2>&1
+}
+
+status () {
+ local dev=$1
+ local sw=$2
+
+ echo '============================================================'
+ ip addr show ${dev}
+ ip addr show ${sw}
+ echo ' '
+ brctl show ${sw}
+ echo ' '
+ ip route list
+ echo ' '
+ route -n
+ echo '============================================================'
+ gateway=$(ip route list | awk '/^default / { print $3 }')
+ ping -c 1 ${gateway} || true
+ echo '============================================================'
+}
+
+start () {
+ if [ "${switch}" = "null" ] ; then
+ return
+ fi
+
+ create_switch ${switch}
+ add_to_switch ${switch} ${pif}
+ change_ips ${pif} ${switch}
+
+ if [ ${antispoof} = 'yes' ] ; then
+ antispoofing
+ fi
+
+ grep -q GenuineIntel /proc/cpuinfo && /sbin/modprobe kvm-intel
+ grep -q AuthenticAMD /proc/cpuinfo && /sbin/modprobe kvm-amd
+}
+
+stop () {
+ if [ "${switch}" = "null" ]; then
+ return
+ fi
+ if ! link_exists "$switch"; then
+ return
+ fi
+
+ change_ips ${switch} ${pif}
+ ip link set ${switch} down
+ brctl delbr ${switch}
+
+ grep -q GenuineIntel /proc/cpuinfo && /sbin/modprobe -r kvm-intel
+ grep -q AuthenticAMD /proc/cpuinfo && /sbin/modprobe -r kvm-amd
+ /sbin/modprobe -r kvm
+}
+
+
+case "$command" in
+ start)
+ echo -n $"Starting KVM: "
+ start
+ echo
+ ;;
+
+ stop)
+ echo -n $"Shutting down KVM: "
+ stop
+ echo
+ ;;
+
+ status)
+ status ${pif} ${switch}
+ ;;
+
+ *)
+ echo "Unknown command: $command" >&2
+ echo 'Valid commands are: start, stop, status' >&2
+ exit 1
+esac
diff --git a/kvm/scripts/make-combined-release b/kvm/scripts/make-combined-release
new file mode 100755
index 0000000..adef8f6
--- /dev/null
+++ b/kvm/scripts/make-combined-release
@@ -0,0 +1,36 @@
+#!/usr/bin/python
+
+import sys, tarfile, os.path
+
+# usage: $0 combined.tar.gz qemu.tar.gz kvm-kmod.tar.gz
+
+outname, qemuname, kmodname = sys.argv[1:4]
+
+out = tarfile.open(name = outname, mode = 'w:gz')
+
+def tarcopy(dst, src, transform):
+ for member in src:
+ f = src.extractfile(member)
+ member.name = transform(member.name)
+ dst.addfile(member, f)
+
+def stem(fname):
+ fname = os.path.basename(fname)
+ if fname.endswith('.tar.gz'):
+ fname = fname[:-7]
+ return fname
+
+def transformer(old, new):
+ def transform(fname):
+ if fname.startswith(old + '/'):
+ fname = new + fname[len(old):]
+ return fname
+ return transform
+
+tarcopy(out, tarfile.open(name = qemuname),
+ transformer(stem(qemuname), stem(outname)))
+
+tarcopy(out, tarfile.open(name = kmodname),
+ transformer(stem(kmodname), stem(outname) + '/kvm/kernel'))
+
+
diff --git a/kvm/scripts/make-release b/kvm/scripts/make-release
new file mode 100755
index 0000000..2d050fc
--- /dev/null
+++ b/kvm/scripts/make-release
@@ -0,0 +1,81 @@
+#!/bin/bash -e
+
+usage() {
+ echo "usage: $0 [--upload] [--formal] commit [name] [tarball] [user]"
+ exit 1
+}
+
+[[ -f ~/.kvmreleaserc ]] && . ~/.kvmreleaserc
+
+upload=
+formal=
+
+releasedir=~/sf-release
+[[ -z "$TMP" ]] && TMP="/tmp"
+tmpdir=`mktemp -d "$TMP/qemu-kvm-make-release.XXXXXXXXXX"`
+while [[ "$1" = -* ]]; do
+ opt="$1"
+ shift
+ case "$opt" in
+ --upload)
+ upload="yes"
+ ;;
+ --formal)
+ formal="yes"
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+
+commit="$1"
+name="$2"
+
+if [[ -z "$commit" ]]; then
+ usage
+fi
+
+if [[ -z "$name" ]]; then
+ name="$commit"
+fi
+
+tarball="$3"
+if [[ -z "$tarball" ]]; then
+ tarball="$releasedir/$name.tar.gz"
+fi
+#strip trailing .gz if any
+tarball=${tarball/%.gz/}
+
+cd "$(dirname "$0")"/../..
+mkdir -p "$(dirname "$tarball")"
+git archive --prefix="$name/" --format=tar "$commit" > "$tarball"
+
+mtime=`git show --pretty=format:%ct "$commit""^{commit}" -- | head -n 1`
+tarargs="--owner=root --group=root"
+
+mkdir -p "$tmpdir/$name"
+git cat-file -p "${commit}:roms" | awk ' { print $4, $3 } ' \
+ > "$tmpdir/$name/EXTERNAL_DEPENDENCIES"
+touch -d "@$mtime" "$tmpdir/$name/EXTERNAL_DEPENDENCIES"
+tar -rf "$tarball" -C "$tmpdir" \
+ $tarargs \
+ "$name/EXTERNAL_DEPENDENCIES"
+rm -rf "$tmpdir"
+
+if [[ -n "$formal" ]]; then
+ mkdir -p "$tmpdir/$name"
+ echo "$name" > "$tmpdir/$name/KVM_VERSION"
+ touch -d "@$mtime" "$tmpdir/$name/KVM_VERSION"
+ tar -rf "$tarball" -C "$tmpdir" "$name/KVM_VERSION" \
+ $tarargs
+ rm -rf "$tmpdir"
+fi
+
+rm -f "$tarball.gz"
+gzip -9 "$tarball"
+tarball="$tarball.gz"
+
+if [[ -n "$upload" ]]; then
+ rsync --progress -h "$tarball" avik@frs.sourceforge.net:uploads/
+fi
diff --git a/kvm/scripts/mkbootdisk b/kvm/scripts/mkbootdisk
new file mode 100755
index 0000000..3b7f7c0
--- /dev/null
+++ b/kvm/scripts/mkbootdisk
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+set -e
+
+kernel="$1"
+mnt_dir="/tmp/mkbootdisk/mnt"
+img_file="/tmp/mkbootdisk/boot.img"
+
+[[ -f "$kernel" ]] || { echo need kernel; exit 1; }
+
+mkdir -p $mnt_dir
+
+[[ -d "$mnt_dir" ]] || { echo mount dir err; exit 1; }
+
+dd < /dev/zero > $img_file bs=1M count=10
+mkfs -t vfat $img_file
+
+mount -o loop $img_file $mnt_dir
+
+cp "$kernel" $mnt_dir/kernel
+
+cat <<EOF > $mnt_dir/SYSLINUX.CFG
+DEFAULT kernel
+APPEND console=ttyS0
+EOF
+
+umount $mnt_dir
+
+syslinux $img_file
+
diff --git a/kvm/scripts/qemu-ifup b/kvm/scripts/qemu-ifup
new file mode 100755
index 0000000..284b176
--- /dev/null
+++ b/kvm/scripts/qemu-ifup
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+switch=$(/sbin/ip route list | awk '/^default / { print $5 }')
+/sbin/ifconfig $1 0.0.0.0 up
+/usr/sbin/brctl addif ${switch} $1
diff --git a/kvm/scripts/run_img b/kvm/scripts/run_img
new file mode 100755
index 0000000..10c7497
--- /dev/null
+++ b/kvm/scripts/run_img
@@ -0,0 +1,4 @@
+sudo /sbin/rmmod kvm
+sudo /sbin/insmod ../kernel/kvm.ko
+sudo chmod a+rw /dev/hvm
+../qemu/x86_64-softmmu/qemu-system-x86_64 -boot c -L /usr/share/qemu -hda /tmp/mkbootdisk/boot.img -m 384 -serial file:/tmp/qemu_serial.out
diff --git a/kvm/scripts/vmxcap b/kvm/scripts/vmxcap
new file mode 100755
index 0000000..08fd120
--- /dev/null
+++ b/kvm/scripts/vmxcap
@@ -0,0 +1,216 @@
+#!/usr/bin/python
+
+MSR_IA32_VMX_BASIC = 0x480
+MSR_IA32_VMX_PINBASED_CTLS = 0x481
+MSR_IA32_VMX_PROCBASED_CTLS = 0x482
+MSR_IA32_VMX_EXIT_CTLS = 0x483
+MSR_IA32_VMX_ENTRY_CTLS = 0x484
+MSR_IA32_VMX_MISC_CTLS = 0x485
+MSR_IA32_VMX_PROCBASED_CTLS2 = 0x48B
+MSR_IA32_VMX_EPT_VPID_CAP = 0x48C
+MSR_IA32_VMX_TRUE_PINBASED_CTLS = 0x48D
+MSR_IA32_VMX_TRUE_PROCBASED_CTLS = 0x48E
+MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F
+MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490
+
+class msr(object):
+ def __init__(self):
+ try:
+ self.f = file('/dev/cpu/0/msr')
+ except:
+ self.f = file('/dev/msr0')
+ def read(self, index, default = None):
+ import struct
+ self.f.seek(index)
+ try:
+ return struct.unpack('Q', self.f.read(8))[0]
+ except:
+ return default
+
+class Control(object):
+ def __init__(self, name, bits, cap_msr, true_cap_msr = None):
+ self.name = name
+ self.bits = bits
+ self.cap_msr = cap_msr
+ self.true_cap_msr = true_cap_msr
+ def read2(self, nr):
+ m = msr()
+ val = m.read(nr, 0)
+ return (val & 0xffffffff, val >> 32)
+ def show(self):
+ print self.name
+ mbz, mb1 = self.read2(self.cap_msr)
+ tmbz, tmb1 = 0, 0
+ if self.true_cap_msr:
+ tmbz, tmb1 = self.read2(self.true_cap_msr)
+ for bit in sorted(self.bits.keys()):
+ zero = not (mbz & (1 << bit))
+ one = mb1 & (1 << bit)
+ true_zero = not (tmbz & (1 << bit))
+ true_one = tmb1 & (1 << bit)
+ s= '?'
+ if (self.true_cap_msr and true_zero and true_one
+ and one and not zero):
+ s = 'default'
+ elif zero and not one:
+ s = 'no'
+ elif one and not zero:
+ s = 'forced'
+ elif one and zero:
+ s = 'yes'
+ print ' %-40s %s' % (self.bits[bit], s)
+
+class Misc(object):
+ def __init__(self, name, bits, msr):
+ self.name = name
+ self.bits = bits
+ self.msr = msr
+ def show(self):
+ print self.name
+ value = msr().read(self.msr, 0)
+ def first_bit(key):
+ if type(key) is tuple:
+ return key[0]
+ else:
+ return key
+ for bits in sorted(self.bits.keys(), key = first_bit):
+ if type(bits) is tuple:
+ lo, hi = bits
+ fmt = int
+ else:
+ lo = hi = bits
+ def fmt(x):
+ return { True: 'yes', False: 'no' }[x]
+ v = (value >> lo) & ((1 << (hi - lo + 1)) - 1)
+ print ' %-40s %s' % (self.bits[bits], fmt(v))
+
+controls = [
+ Control(
+ name = 'pin-based controls',
+ bits = {
+ 0: 'External interrupt exiting',
+ 3: 'NMI exiting',
+ 5: 'Virtual NMIs',
+ 6: 'Activate VMX-preemption timer',
+ },
+ cap_msr = MSR_IA32_VMX_PINBASED_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_PINBASED_CTLS,
+ ),
+
+ Control(
+ name = 'primary processor-based controls',
+ bits = {
+ 2: 'Interrupt window exiting',
+ 3: 'Use TSC offsetting',
+ 7: 'HLT exiting',
+ 9: 'INVLPG exiting',
+ 10: 'MWAIT exiting',
+ 11: 'RDPMC exiting',
+ 12: 'RDTSC exiting',
+ 15: 'CR3-load exiting',
+ 16: 'CR3-store exiting',
+ 19: 'CR8-load exiting',
+ 20: 'CR8-store exiting',
+ 21: 'Use TPR shadow',
+ 22: 'NMI-window exiting',
+ 23: 'MOV-DR exiting',
+ 24: 'Unconditional I/O exiting',
+ 25: 'Use I/O bitmaps',
+ 27: 'Monitor trap flag',
+ 28: 'Use MSR bitmaps',
+ 29: 'MONITOR exiting',
+ 30: 'PAUSE exiting',
+ 31: 'Activate secondary control',
+ },
+ cap_msr = MSR_IA32_VMX_PROCBASED_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_PROCBASED_CTLS,
+ ),
+
+ Control(
+ name = 'secondary processor-based controls',
+ bits = {
+ 0: 'Virtualize APIC accesses',
+ 1: 'Enable EPT',
+ 2: 'Descriptor-table exiting',
+ 4: 'Virtualize x2APIC mode',
+ 5: 'Enable VPID',
+ 6: 'WBINVD exiting',
+ 7: 'Unrestricted guest',
+ 10: 'PAUSE-loop exiting',
+ },
+ cap_msr = MSR_IA32_VMX_PROCBASED_CTLS2,
+ ),
+
+ Control(
+ name = 'VM-Exit controls',
+ bits = {
+ 2: 'Save debug controls',
+ 9: 'Host address-space size',
+ 12: 'Load IA32_PERF_GLOBAL_CTRL',
+ 15: 'Acknowledge interrupt on exit',
+ 18: 'Save IA32_PAT',
+ 19: 'Load IA32_PAT',
+ 20: 'Save IA32_EFER',
+ 21: 'Load IA32_EFER',
+ 22: 'Save VMX-preemption timer value',
+ },
+ cap_msr = MSR_IA32_VMX_EXIT_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_EXIT_CTLS,
+ ),
+
+ Control(
+ name = 'VM-Entry controls',
+ bits = {
+ 2: 'Load debug controls',
+ 9: 'IA-64 mode guest',
+ 10: 'Entry to SMM',
+ 11: 'Deactivate dual-monitor treatment',
+ 13: 'Load IA32_PERF_GLOBAL_CTRL',
+ 14: 'Load IA32_PAT',
+ 15: 'Load IA32_EFER',
+ },
+ cap_msr = MSR_IA32_VMX_ENTRY_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_ENTRY_CTLS,
+ ),
+
+ Misc(
+ name = 'Miscellaneous data',
+ bits = {
+ (0,4): 'VMX-preemption timer scale (log2)',
+ 5: 'Store EFER.LMA into IA-32e mode guest control',
+ 6: 'HLT activity state',
+ 7: 'Shutdown activity state',
+ 8: 'Wait-for-SIPI activity state',
+ (16,24): 'Number of CR3-target values',
+ (25,27): 'MSR-load/store count recommenation',
+ (32,62): 'MSEG revision identifier',
+ },
+ msr = MSR_IA32_VMX_MISC_CTLS,
+ ),
+
+ Misc(
+ name = 'VPID and EPT capabilities',
+ bits = {
+ 0: 'Execute-only EPT translations',
+ 6: 'Page-walk length 4',
+ 8: 'Paging-structure memory type UC',
+ 14: 'Paging-structure memory type WB',
+ 16: '2MB EPT pages',
+ 17: '1GB EPT pages',
+ 20: 'INVEPT supported',
+ 25: 'Single-context INVEPT',
+ 26: 'All-context INVEPT',
+ 32: 'INVVPID supported',
+ 40: 'Individual-address INVVPID',
+ 41: 'Single-context INVVPID',
+ 42: 'All-context INVVPID',
+ 43: 'Single-context-retaining-globals INVVPID',
+ },
+ msr = MSR_IA32_VMX_EPT_VPID_CAP,
+ ),
+ ]
+
+for c in controls:
+ c.show()
+
+
diff --git a/kvm/vgabios/.cvsignore b/kvm/vgabios/.cvsignore
new file mode 100644
index 0000000..1df04b7
--- /dev/null
+++ b/kvm/vgabios/.cvsignore
@@ -0,0 +1 @@
+vbetables.h
diff --git a/kvm/vgabios/BUGS b/kvm/vgabios/BUGS
new file mode 100644
index 0000000..785f4dc
--- /dev/null
+++ b/kvm/vgabios/BUGS
@@ -0,0 +1,3 @@
+Not all the functions have been implemented yet.
+
+Please report any bugs to <info@vruppert.de>
diff --git a/kvm/vgabios/COPYING b/kvm/vgabios/COPYING
new file mode 100644
index 0000000..223ede7
--- /dev/null
+++ b/kvm/vgabios/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/kvm/vgabios/ChangeLog b/kvm/vgabios/ChangeLog
new file mode 100644
index 0000000..75be5bd
--- /dev/null
+++ b/kvm/vgabios/ChangeLog
@@ -0,0 +1,1264 @@
+2008-05-11 08:40 vruppert
+
+ * biossums.c (1.6):
+
+ - fixed a warning
+
+2008-03-02 08:47 vruppert
+
+ * vbe.c (1.60):
+
+ - added debug message for unsupported VBE modes
+
+2008-02-24 09:18 vruppert
+
+ * vbe.c (1.59):
+
+ - in LFB modes the number of banks must be set to 1
+
+2008-01-27 10:44 vruppert
+
+ * Makefile (1.21), biossums.c (1.5), vgabios.c (1.67):
+
+ - added PCI data structure for the Cirrus VGABIOS images
+ - added support for the PCI data structure in biossums
+ - updated year in copyright
+
+2008-01-26 11:46 vruppert
+
+ * BUGS (1.4), Makefile (1.20), README (1.14), TODO (1.13), vbe_display_api.txt (1.14):
+
+ - whitespace cleanup
+
+2006-11-26 10:43 vruppert
+
+ * Makefile (1.19):
+
+ - disable the generation of linemarkers by the preprocessor, since the latest
+ versions of bcc don't like them
+
+2006-09-02 13:15 vruppert
+
+ * biossums.c (1.4):
+
+ - the biossums utility no longer modifies VGABIOS images with proper checksum
+ and size
+
+2006-08-19 14:28 vruppert
+
+ * Changelog (1.26), README (1.13), TODO (1.12):
+
+ - updates for 0.6a release
+
+2006-08-19 09:39 vruppert
+
+ * vbe.c (1.58):
+
+ - improved VGA compatible setup for VBE modes (disable CGA and Hercules
+ compatible memory layout)
+
+2006-08-18 20:39 vruppert
+
+ * vbe.c (1.57):
+
+ - improved VGA compatible setup for >=8bpp VBE modes (CRTC doubleword mode and
+ GRDC shift register setting added)
+ - now using symbolic name for CRTC address register
+
+2006-08-15 20:42 vruppert
+
+ * vbe.c (1.56), vbetables-gen.c (1.4):
+
+ - init 4bpp VBE modes by a temporary switch to VGA mode 0x6A
+ - all 4bpp VBE modes now enabled
+
+2006-08-14 20:24 vruppert
+
+ * vbe.c (1.55):
+
+ - VGA compatible setup for VBE modes improved (Bochs hack can be removed now)
+
+2006-08-12 07:51 vruppert
+
+ * .cvsignore (1.1):
+
+ - .cvsignore added for auto-generated file
+
+2006-08-12 07:47 vruppert
+
+ * vbe.c (1.54), vbe.h (1.27), vbe_display_api.txt (1.13), vbetables-gen.c (1.3):
+
+ - cleaned up VBE memory size definitions (removed duplicate defines, main
+ definition now in vbetables-gen.c)
+
+2006-08-09 21:28 vruppert
+
+ * vbetables.h (1.30):
+
+ - removed auto-generated file
+
+2006-08-09 21:26 vruppert
+
+ * vbe.c (1.53), vbe.h (1.26), vbe_display_api.txt (1.12), vbetables-gen.c (1.2),
+ vbetables.h (1.29):
+
+ - VBE video memory increased to 8 MB
+ - VBE dispi ID changed to B0C4
+ - documentation update
+
+2006-07-11 08:03 vruppert
+
+ * Makefile (1.18), vbetables-gen.c (1.1), vbetables.h (1.28):
+
+ - generate vbetables.h dynamicly
+ * initial patch from the qemu project by Fabrice Bellard
+ * only add modes that fit in video memory (still 4 MB)
+ * several other fixes (e.g. 4 bpp specific stuff, number of pages)
+
+2006-07-10 07:47 vruppert
+
+ * vgabios.c (1.66):
+
+ - biosfn_scroll(): check variable 'i' for underflowing when scrolling downwards
+ to avoid screen corruption
+
+2006-07-10 07:47 vruppert
+
+ * vbe.c (1.52):
+
+ - VBE set bank functions failure handling added
+ - VBE get/set logical scan line length fixes for the 4bpp mode
+
+2006-07-08 13:27 vruppert
+
+ * vbe.c (1.51), vbetables.h (1.27):
+
+ - added special case for the 4 bpp when setting VBE display start
+ - VBE mode table fixes
+
+2006-07-07 13:30 vruppert
+
+ * clext.c (1.12):
+
+ - bank pointer must be set to 0 after a mode set
+
+2006-06-21 16:58 vruppert
+
+ * vbe.c (1.50), vbetables.h (1.26):
+
+ - improved VBE display capabilities check (X resulution checked now)
+ - removed obsolete defines (LFB always available, always generate dynamic list)
+ - CR/LF to LF fixes
+
+2006-06-18 15:22 vruppert
+
+ * clext.c (1.11), vbe.c (1.49), vbe.h (1.25), vbetables.h (1.25), vgabios.c
+ (1.65):
+
+ - applied patch from the qemu project (Fabrice Bellard)
+ * Cirrus SVGA now supports the "no clear" bit when switching to Cirrus or
+ VESA mode
+ * Bochs VBE protected mode interface improved
+ * save/restore video state support for Bochs VBE and standard VGA added
+ * Bochs VBE prepared for more modi
+
+2006-03-25 10:19 vruppert
+
+ * clext.c (1.10), vgabios.c (1.64), vgatables.h (1.10):
+
+ - applied patch from Fabrice Bellard
+ * added minimal support for the video parameter table (VPT)
+ * added Cirrus SVGA mode 0x7b (1600x1200x8)
+
+2005-12-26 19:50 vruppert
+
+ * vbe.c (1.48), vgabios.c (1.63):
+
+ - Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com)
+
+2005-12-26 19:50 vruppert
+
+ * biossums.c (1.3):
+
+ - biossums utility now supports VGABIOS sizes up to 64 kBytes
+
+2005-09-21 18:45 vruppert
+
+ * vgatables.h (1.9):
+
+ - mode 0x11: all color planes must be enabled in this 2-color VGA mode
+
+2005-08-30 18:41 vruppert
+
+ * biossums.c (1.2):
+
+ - missing license text added in biossums.c
+
+2005-07-02 18:39 vruppert
+
+ * vgabios.c (1.62):
+
+ - BIOS configuration word usually reports initial mode 80x25 color text
+ - vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the
+ cursor row value
+
+2005-05-24 16:50 vruppert
+
+ * vbe.c (1.47), vgabios.c (1.61):
+
+ - output to the vgabios info port can be disabled now. It is still enabled by
+ default and always possible in debug mode. (based on a patch from Alex Beregszaszi)
+
+2005-05-20 16:06 vruppert
+
+ * vbe.c (1.46), vgabios.c (1.60):
+
+ - fixed return value for the default case in the VBE section (non-debug mode)
+ - removed unused macros HALT and PANIC_PORT
+
+2005-03-07 20:39 vruppert
+
+ * README (1.9):
+
+ - updates for 0.5a release
+
+2005-03-06 13:06 vruppert
+
+ * Makefile (1.17):
+
+ - vgabios files with cirrus support added to release target
+
+2005-03-06 12:24 vruppert
+
+ * Makefile (1.16):
+
+ - cross compilation support added (patch from Alex Beregszaszi)
+
+2005-03-05 13:03 vruppert
+
+ * BUGS (1.3), README (1.8), TODO (1.11):
+
+ - documentation updates
+
+2004-12-04 15:26 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.61), VGABIOS-lgpl-latest.cirrus.bin
+ (1.13), VGABIOS-lgpl-latest.cirrus.debug.bin (1.13),
+ VGABIOS-lgpl-latest.debug.bin (1.61), clext.c (1.9):
+
+ - Cirrus extension: support for 1280x1024x15 and 1280x1024x16 modes added (patch
+ from Fabrice Bellard)
+
+2004-08-08 16:53 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.60), VGABIOS-lgpl-latest.cirrus.bin (1.12),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.12),
+ VGABIOS-lgpl-latest.debug.bin (1.60), clext.c (1.8):
+
+ - use single bank mode for VBE
+ - enable 16k granularity for VBE only
+
+2004-07-30 19:33 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.59), VGABIOS-lgpl-latest.cirrus.bin (1.11),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.11),
+ VGABIOS-lgpl-latest.debug.bin (1.59), clext.c (1.7):
+
+ - cirrus init: set standard vga mode and reset bitblt
+
+2004-07-22 18:38 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.58), VGABIOS-lgpl-latest.cirrus.bin (1.10),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.10),
+ VGABIOS-lgpl-latest.debug.bin (1.58), clext.c (1.6), vbe.c (1.45),
+ vbetables.h (1.24):
+
+ - cirrus extension: tables for mode 1280x1024x8 added
+ - vbe: dispi_set_xres() and dispi_set_virt_width() now modify vga compatible
+ registers
+ - vbe: mode list entry for mode 800x600x4 fixed
+
+2004-07-18 20:23 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.57), VGABIOS-lgpl-latest.cirrus.bin (1.9),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.9),
+ VGABIOS-lgpl-latest.debug.bin (1.57), vgabios.c (1.59), vgatables.h (1.8):
+
+ - disable CRTC write protection before setting new values
+ - CRTC line for mode 0x6a fixed
+
+2004-07-07 16:08 vruppert
+
+ * Makefile (1.15), VGABIOS-lgpl-latest.bin (1.56),
+ VGABIOS-lgpl-latest.cirrus.bin (1.8), VGABIOS-lgpl-latest.cirrus.debug.bin (1.8),
+ VGABIOS-lgpl-latest.debug.bin (1.56), biossums.c (1.1), clext.c (1.5):
+
+ - biossums utility for the Bochs BIOS adapted for the LGPL'd VGABIOS
+ - VESA3 PMINFO checksum calculated in the source
+ - 24 bpp mode entries fixed (patch from Fabrice Bellard)
+
+2004-06-25 18:28 vruppert
+
+ * VGABIOS-lgpl-latest.cirrus.bin (1.7), VGABIOS-lgpl-latest.cirrus.debug.bin (1.7),
+ clext.c (1.4):
+
+ - 4MB memory probe added (patch from Fabrice Bellard)
+
+2004-06-25 17:31 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.55), VGABIOS-lgpl-latest.cirrus.bin (1.6),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.6),
+ VGABIOS-lgpl-latest.debug.bin (1.55), clext.c (1.3):
+
+ - fixed value of sequencer reset register in cirrus mode table
+ - fixed possible overflow error if cirrus start address is >256k
+
+2004-06-23 21:11 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.54), VGABIOS-lgpl-latest.cirrus.bin (1.5),
+ VGABIOS-lgpl-latest.cirrus.debug.bin (1.5),
+ VGABIOS-lgpl-latest.debug.bin (1.54), clext.c (1.2):
+
+ - applied new patch for the cirrus extension from suzu
+ * enable VESA LFB support if a Cirrus PCI adapter is detected
+ * prepared VBE3 protected mode info block (test case required)
+ - added VBE functions 4F06h and 4F07h
+ - some bugfixes
+
+2004-06-17 18:57 vruppert
+
+ * Makefile (1.14), VGABIOS-lgpl-latest.bin (1.53),
+ VGABIOS-lgpl-latest.cirrus.bin (1.2), VGABIOS-lgpl-latest.cirrus.debug.bin (1.2),
+ VGABIOS-lgpl-latest.debug.bin (1.53):
+
+ - fixed makefile targets for the binaries with cirrus extension
+
+2004-06-16 21:11 vruppert
+
+ * Makefile (1.13), VGABIOS-lgpl-latest.bin (1.52),
+ VGABIOS-lgpl-latest.cirrus.bin (1.1), VGABIOS-lgpl-latest.cirrus.debug.bin (1.1),
+ VGABIOS-lgpl-latest.debug.bin (1.52), clext.c (1.1), vgabios.c (1.58):
+
+ - applied suzu's cirrus extension patch. Cirrus SVGA detection, most of the
+ cirrus-specific modes and some basic VBE features are present now.
+
+2004-05-31 21:15 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.51), VGABIOS-lgpl-latest.debug.bin (1.51),
+ vgabios.c (1.57):
+
+ - write character in planar graphics modes: sequencer map mask must be 0x0f and
+ bit operation must be 'replace' if bit 7 of attribute is clear
+ - read/write pixel in planar graphics modes: bit mask setup simplified
+
+2004-05-11 18:08 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.50), VGABIOS-lgpl-latest.debug.bin (1.50),
+ vgabios.c (1.56):
+
+ - biosfn_select_vert_res rewritten in assembler
+ - scroll text in planar graphics modes: attribute for blank line fixed
+ - write character in planar graphics modes: graphics controller values fixed
+
+2004-05-09 20:32 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.49), VGABIOS-lgpl-latest.debug.bin (1.49),
+ vbe.c (1.44), vbe.h (1.24), vgabios.c (1.55):
+
+ - VBE init code and some dispi ioport functions rewritten in assembler
+ - text scroll functions for CGA graphics modes added
+ - scroll text in graphics modes: attribute for blank line fixed
+
+2004-05-08 16:06 vruppert
+
+ * BUGS (1.2), README (1.7), TODO (1.10), VGABIOS-lgpl-latest.bin (1.48),
+ VGABIOS-lgpl-latest.debug.bin (1.48), vbe.c (1.43), vbe.h (1.23),
+ vbe_display_api.txt (1.11), vgabios.c (1.54):
+
+ - VBE internal functions dispi_set_enable and dispi_set_bank now called both from C
+ and asm code
+ - VBE function 0x03 rewritten in assembler
+ - VBE function 0x08 cleaned up
+ - text output and scroll functions for graphics modes rewritten using case
+ structures
+ - documentation and comments updated
+
+2004-05-06 21:18 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.47), VGABIOS-lgpl-latest.debug.bin (1.47),
+ vbe.c (1.42), vbe.h (1.22), vgabios.c (1.53):
+
+ - VBE functions 0x05, 0x06, 0x07 and some dispi ioport functions rewritten in
+ assembler
+ - VBE functions 0x06 and 0x07: get functions now supported, 15 bpp bug fixed
+
+2004-05-05 19:24 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.46), VGABIOS-lgpl-latest.debug.bin (1.46),
+ vbe.c (1.41), vbe.h (1.21), vbe_display_api.txt (1.10), vgabios.c (1.52):
+
+ - 8 bit DAC capability flag set
+ - vbe_biosfn_set_get_dac_palette_format implemented
+ - VBE api description updated
+ - C definitions from header files now used assembler code
+
+2004-05-02 17:27 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.45), VGABIOS-lgpl-latest.debug.bin (1.45),
+ vgabios.c (1.51):
+
+ - text scroll functions for PLANAR1/PLANAR4 graphics modes added
+ - function biosfn_get_ega_info rewritten in assembler
+ - read/write graphics pixel functions rewritten using a case structure
+
+2004-05-01 16:03 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.44), VGABIOS-lgpl-latest.debug.bin (1.44),
+ vgabios.c (1.50):
+
+ - biosfn_enable_cursor_emulation rewritten in assembler
+ - remap of the cursor shape depends on modeset control bit 0
+ - text output in PLANAR4 modes now supports attribute bit 7 (XOR with background)
+
+2004-04-25 20:13 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.43), VGABIOS-lgpl-latest.debug.bin (1.43),
+ vgabios.c (1.49), vgatables.h (1.7):
+
+ - table entries for vga mode 0x0f fixed (PLANAR2 exists on EGA only)
+ - function release_font_access now supports the monochrome text mode
+ - PLANAR1 modes now supported in text output functions and read/write pixel
+ - function AH=0x12/BL=0x32 rewritten in assembler
+
+2004-04-25 08:45 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.42), VGABIOS-lgpl-latest.debug.bin (1.42),
+ vgabios.c (1.48):
+
+ - block address calculation in font functions fixed
+ - functions AX=0x1103, AH=0x12/BL=0x31 and AH=0x12/BL=0x33 rewritten in assembler
+
+2004-04-24 09:59 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.41), VGABIOS-lgpl-latest.debug.bin (1.41),
+ vgabios.c (1.47):
+
+ - read/write graphics pixel for PLANAR4 modes added
+ - CGA specific functions (group AH = 0x0B) implemented
+
+2004-04-23 14:34 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.40), VGABIOS-lgpl-latest.debug.bin (1.40),
+ vgabios.c (1.46):
+
+ - remaining palette and dac read/write functions (except gray scale summing)
+ rewritten in assembler
+
+2004-04-18 13:43 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.39), VGABIOS-lgpl-latest.debug.bin (1.39),
+ vgabios.c (1.45):
+
+ - some palette and dac read/write functions rewritten in assembler
+ - main int10 debug message now works with assembler functions, too
+
+2004-04-18 09:15 japj
+
+ * vbe.c (1.40):
+
+ updated my email address + put vgabios url in the bios copyright string
+ (instead of my old email address)
+
+2004-04-17 07:18 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.38), VGABIOS-lgpl-latest.debug.bin (1.38),
+ vgabios.c (1.44):
+
+ - biosfn_set_video_mode: don't load DAC registers if default palette loading is
+ disabled. Perform gray scale summing if enabled.
+ - biosfn_perform_gray_scale_summing: switch between DAC read and write mode is
+ required to make this function work. Maximum DAC value always set to 0x3f.
+
+2004-04-08 17:50 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.37), VGABIOS-lgpl-latest.debug.bin (1.37),
+ vgabios.c (1.43):
+
+ - write character function for the LINEAR8 mode
+ - get_font_access() and release_font_access() rewritten in assembler
+ - fixed wrong variable name in the init code
+
+2004-04-06 19:31 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.36), VGABIOS-lgpl-latest.debug.bin (1.36),
+ vgabios.c (1.42):
+
+ - init functions rewitten in assembler
+ - function biosfn_set_display_code rewritten in assembler
+
+2004-04-05 19:40 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.35), VGABIOS-lgpl-latest.debug.bin (1.35),
+ vgabios.c (1.41):
+
+ - functions biosfn_get_video_mode() and biosfn_read_display_code() rewritten
+ in assembler
+
+2004-04-04 18:20 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.34), VGABIOS-lgpl-latest.debug.bin (1.34),
+ vgabios.c (1.40):
+
+ - write character function for CGA modes added
+ - read/write graphics pixel for CGA and LINEAR8 modes added
+
+2004-02-23 21:08 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.33), VGABIOS-lgpl-latest.debug.bin (1.33),
+ vbe.c (1.39):
+
+ - dispi_get_max_bpp(): restore the original value of the vbe enable register
+
+2004-02-22 14:17 vruppert
+
+ * README (1.6), vbe.c (1.38), vbe.h (1.20), vbe_display_api.txt (1.9),
+ VGABIOS-lgpl-latest.bin (1.32), VGABIOS-lgpl-latest.debug.bin (1.32):
+
+ - new function dispi_get_max_bpp() returns the bpp capabilities of the Bochs gui
+ - create the mode list depending on the supported bpp capability
+ - unused stuff removed
+ - documentation updated
+
+2004-02-21 18:20 vruppert
+
+ * vbe.c (1.37), vbe.h (1.19), vbetables.h (1.23),
+ VGABIOS-lgpl-latest.bin (1.31), VGABIOS-lgpl-latest.debug.bin (1.31):
+
+ - dynamicly genarated vbe mode_info list works now
+
+2003-11-17 21:04 vruppert
+
+ * vbe.c (1.36), vbetables.h (1.22), vgabios.c (1.39), vgatables.h (1.6),
+ VGABIOS-lgpl-latest.bin (1.30), VGABIOS-lgpl-latest.debug.bin (1.30):
+
+ - new VBE presence flag stored at unused BDA address 0xB9
+ - VBE init code rewritten
+ - added BIOS TTY flag for VBE mode 0x0102 (TODO: scrolling)
+ - vgabios_init_func: load and activate text font already done by set_video_mode
+ - function biosfn_get_all_palette_reg() fixed
+
+2003-11-06 00:26 cbothamy
+
+ * README (1.5):
+
+ - add changes for 0.4c release
+
+2003-11-06 00:22 cbothamy
+
+ * VGABIOS-lgpl-latest.bin (1.29), VGABIOS-lgpl-latest.debug.bin
+ (1.29):
+
+ - compile vgabios.c rev1.38
+
+2003-11-06 00:21 cbothamy
+
+ * vgabios.c (1.38):
+
+ - activate char table after loading it when setting a text video
+ mode
+
+2003-11-06 00:19 cbothamy
+
+ * Makefile (1.12):
+
+ - when making a release, remove unwanted files first, and exclude
+ CVS from the tarball
+
+2003-11-04 22:50 cbothamy
+
+ * ChangeLog (1.20, v0_4b):
+
+ - update ChangeLog for 0.4b release
+
+2003-11-04 22:49 cbothamy
+
+ * README (1.4, v0_4b):
+
+ - update Changes for 0.4b release
+
+2003-11-04 20:26 vruppert
+
+ * vgabios.c (1.37), VGABIOS-lgpl-latest.bin (1.28),
+ VGABIOS-lgpl-latest.debug.bin (1.28) (utags: v0_4b):
+
+ - biosfn_get_font_info(): character height must be returned in CX
+
+2003-11-03 21:57 vruppert
+
+ * vbe.c (1.35, v0_4b), vgabios.c (1.36), VGABIOS-lgpl-latest.bin
+ (1.27), VGABIOS-lgpl-latest.debug.bin (1.27):
+
+ - the 'noclearmem' flag is not stored in the 'current video mode'
+ register (0040h:0049h) - VBE also stores the 'noclear' flag in
+ the 'video control' register (0040h:0087h)
+
+2003-10-05 10:06 vruppert
+
+ * vbe.h (1.18, v0_4b), vbe_display_api.txt (1.8, v0_4b),
+ VGABIOS-lgpl-latest.bin (1.26), VGABIOS-lgpl-latest.debug.bin
+ (1.26):
+
+ - changed VBE i/o registers to 0x01CE/CF (suggestion from Daniel
+ Gimpelevich)
+
+2003-08-18 18:38 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.25), VGABIOS-lgpl-latest.debug.bin
+ (1.25), vgabios.c (1.35):
+
+ - wrong offsets to the character tables (INT 0x1F/0x43) fixed
+ (underscore added) - functions accessing the CRT controller
+ optimized using a local variable 'crtc_addr'
+
+2003-08-17 15:46 cbothamy
+
+ * ChangeLog (1.19, v0_4a):
+
+ - ChangeLog is now automatically generated by running "cvs2cl -r
+ -t -P -S" - update ChangeLog for 0.4a release
+
+2003-08-17 15:44 cbothamy
+
+ * README (1.3, v0_4a):
+
+ - added the old ChangeLog in the HOSTORY section of the README
+ file - update History for 0.4a release, with a summary of Changes
+
+2003-08-17 15:24 cbothamy
+
+ * Makefile (1.11, v0_4b, v0_4a):
+
+ - fix Makefile for "release" target
+
+2003-08-16 01:49 cbothamy
+
+ * Makefile (1.10), README (1.2), VGABIOS-lgpl-latest.bin (1.24,
+ v0_4a), VGABIOS-lgpl-latest.debug.bin (1.24, v0_4a), vgabios.c
+ (1.34, v0_4a):
+
+ - update the Makefile for releases - remove references to old
+ plex86 website - update the Makefile so it build
+ VGABIOS-lgpl-latest.bin and VGABIOS-lgpl-latest.debug.bin
+
+2003-08-07 18:17 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.23), VGABIOS-lgpl-latest.debug.bin
+ (1.23):
+
+ - current VBE mode now stored in BDA (unused address 0xBA)
+
+2003-08-07 17:54 vruppert
+
+ * vbe.c (1.34), vgatables.h (1.5, v0_4b) (utags: v0_4a):
+
+ - current VBE mode now stored in BDA (unused address 0xBA)
+
+2003-07-20 18:05 vruppert
+
+ * vgabios.c (1.33), VGABIOS-lgpl-latest.bin (1.22),
+ VGABIOS-lgpl-latest.debug.bin (1.22):
+
+ - fixed a few functions accessing the attribute controller
+
+2003-07-19 09:33 vruppert
+
+ * vgabios.c (1.32), VGABIOS-lgpl-latest.bin (1.21),
+ VGABIOS-lgpl-latest.debug.bin (1.21):
+
+ - re-enable video after programming the attribute controller -
+ biosfn_set_all_palette_reg(): number of palette registers fixed
+
+2003-07-16 22:32 vruppert
+
+ * ChangeLog (1.18), vbe.c (1.33), vbe.h (1.17, v0_4a),
+ vbe_display_api.txt (1.7, v0_4a), vgabios.c (1.31),
+ VGABIOS-lgpl-latest.bin (1.20), VGABIOS-lgpl-latest.debug.bin
+ (1.20):
+
+ - LFB flag now stored in the register VBE_DISPI_INDEX_ENABLE -
+ release date in Changelog fixed - release date of VBE BIOS 0.6
+ was the same as VGA BIOS 0.3b - year changed in copyright
+ messages
+
+2003-07-15 12:40 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.19), VGABIOS-lgpl-latest.debug.bin
+ (1.19):
+
+ - new function dispi_get_bpp() - function
+ vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp -
+ number of image pages of all VBE modes fixed
+
+2003-07-15 12:35 vruppert
+
+ * vbe.c (1.32), vbetables.h (1.21, v0_4b, v0_4a):
+
+ - new function dispi_get_bpp() - function
+ vbe_biosfn_set_get_logical_scan_line_length() fixed for >8bpp -
+ number of image pages of all VBE modes fixed
+
+2003-07-14 19:45 vruppert
+
+ * vbe_display_api.txt (1.6):
+
+ - description of VBE_DISPI_ interface 0xb0c2 added
+
+2003-07-10 19:07 vruppert
+
+ * vbe.c (1.31), vbetables.h (1.20), VGABIOS-lgpl-latest.bin (1.18),
+ VGABIOS-lgpl-latest.debug.bin (1.18):
+
+ - 15 bpp VBE modes added - "Bochs own" mode 0x142 (640x480x32bpp)
+ added
+
+2003-07-01 19:00 vruppert
+
+ * vbe.c (1.30), vbe.h (1.16), vbetables.h (1.19),
+ VGABIOS-lgpl-latest.bin (1.17), VGABIOS-lgpl-latest.debug.bin
+ (1.17):
+
+ - VBE preserve display memory feature implemented - VBE mode
+ entries 0x117 and 0x118 added
+
+2003-06-30 21:27 vruppert
+
+ * vbe.c (1.29), vbe.h (1.15), vbetables.h (1.18),
+ VGABIOS-lgpl-latest.bin (1.16), VGABIOS-lgpl-latest.debug.bin
+ (1.16):
+
+ - VBE mode info blocks of modes with >8bpp enabled - VBE modes
+ with 24 bpp: bytes per scanline fixed - vbe_biosfn_set_mode() now
+ supports >8bpp - VBE will be enabled with new VBE_DISPI_ID2
+ (0xB0C2)
+
+2003-06-29 12:53 vruppert
+
+ * vbetables.h (1.17), VGABIOS-lgpl-latest.bin (1.15),
+ VGABIOS-lgpl-latest.debug.bin (1.15):
+
+ - duplicate lines with VBE_MODE_ATTRIBUTE_GRAPHICS_MODE removed -
+ VBE mode info items of currently unsupported modes fixed
+
+2003-06-15 21:19 vruppert
+
+ * vgabios.c (1.30), VGABIOS-lgpl-latest.bin (1.14),
+ VGABIOS-lgpl-latest.debug.bin (1.14):
+
+ - function write_gfx_char() rewritten
+
+2003-04-26 09:27 vruppert
+
+ * VGABIOS-lgpl-latest.debug.bin (1.13):
+
+ - added missing VBE function dispi_get_bank() - added missing
+ return codes for VBE function 4F05h - memory size is always
+ reported in VBE function 4F00h - fixed scan line length for VBE
+ mode 0102h - fixed function set_active_page() for graphics modes
+ - fixed the page sizes of some VGA modes
+
+2003-04-26 09:22 vruppert
+
+ * vbe.c (1.28), vbetables.h (1.16), vgabios.c (1.29), vgatables.h
+ (1.4), VGABIOS-lgpl-latest.bin (1.13):
+
+ - added missing VBE function dispi_get_bank() - added missing
+ return codes for VBE function 4F05h - memory size is always
+ reported in VBE function 4F00h - fixed scan line length for VBE
+ mode 0102h - fixed function set_active_page() for graphics modes
+ - fixed the page sizes of some VGA modes
+
+2003-04-20 09:51 vruppert
+
+ * vgabios.c (1.28), vgatables.h (1.3), VGABIOS-lgpl-latest.bin
+ (1.12), VGABIOS-lgpl-latest.debug.bin (1.12):
+
+ - function write_gfx_char() now supports different font sizes -
+ some entries of the static functionality table fixed
+
+2003-04-18 09:23 vruppert
+
+ * vbe.c (1.27), vbe.h (1.14), vbetables.h (1.15):
+
+ - applied patch #1331 * new function dispi_set_bank_farcall()
+ * VBE mode info item WinFuncPtr points to the new function if the
+ flag VBE_WINDOW_ATTRIBUTE_RELOCATABLE is set * flag
+ VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE added
+
+2003-02-11 20:17 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.11), VGABIOS-lgpl-latest.debug.bin
+ (1.11), vbe.c (1.26), vbetables.h (1.14):
+
+ - VBE mode search rewritten * improved function
+ mode_info_find_mode() is now used by the VBE functions 0x4F01
+ and 0x4F02 * removed all mode list entries with the LFB bit
+ set. LFB detection is now present in the function
+ mode_info_find_mode()
+
+2003-02-09 20:59 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.10), VGABIOS-lgpl-latest.debug.bin
+ (1.10), vgabios.c (1.27):
+
+ - function write_gfx_char(): memory address now calculated in
+ this function; background color is always black - function
+ biosfn_write_char_attr(): the count parameter is now used in
+ graphics modes too - function biosfn_write_char_only() works
+ the same way as function biosfn_write_char_attr() in graphics
+ mode - copying charmap data optimized using memcpyb()
+
+2003-02-09 11:36 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.9), VGABIOS-lgpl-latest.debug.bin
+ (1.9):
+
+ - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA
+ modes with the LFB flag set removed from the list (Linux doesn't
+ like mode numbers > 0x07ff)
+
+2003-02-09 11:02 vruppert
+
+ * vbe.c (1.25), vbe.h (1.13), vbetables.h (1.13):
+
+ - VESA mode 0x102 added (uses existing SVGA mode 0x6a) - all VESA
+ modes with the LFB flag set removed from the list (Linux doesn't
+ like mode numbers > 0x07ff)
+
+2003-02-08 13:04 vruppert
+
+ * vbe.c (1.24), vgabios.c (1.26):
+
+ - vbe_biosfn_return_current_mode() now returns the active
+ standard VGA mode TODO: return VESA mode if enabled -
+ biosfn_set_video_mode() now clears the screen in CGA mode
+ correctly - write character functions are now working in all
+ PLANAR4 graphics modes - added stubs for unimplemented features
+ in graphics modes
+
+2003-02-04 22:19 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.8), VGABIOS-lgpl-latest.debug.bin
+ (1.8):
+
+ - set video mode: clear vga memory in graphics mode - set video
+ mode: load default font in text mode - write character
+ implemented for graphics mode 0x12
+
+2003-02-04 22:06 vruppert
+
+ * vgabios.c (1.25):
+
+ - set video mode: clear vga memory in graphics mode - set video
+ mode: load default font in text mode - write character
+ implemented for graphics mode 0x12
+
+2003-01-21 19:30 vruppert
+
+ * vgabios.c (1.24):
+
+ - remap the cursor size if the char height is > 8 and the new
+ values are < 8
+
+2003-01-20 18:24 cbothamy
+
+ * Makefile (1.9):
+
+ - fix so make -j2 does not overwrite temp files
+
+2003-01-19 12:35 vruppert
+
+ * vgabios.c (1.23):
+
+ - function set_scan_lines() recalculates the number of rows and
+ the page size - new values for char height, text rows and page
+ size are stored in the BIOS data segment - asm helper function
+ idiv_u added
+
+2003-01-15 18:49 cbothamy
+
+ * VGABIOS-lgpl-latest.bin (1.7), VGABIOS-lgpl-latest.debug.bin
+ (1.7):
+
+ - compile vgabios rev 1.22
+
+2003-01-15 18:49 cbothamy
+
+ * vgabios.c (1.22):
+
+ - fix bug found by ams : a 8bits index value was compared to
+ 0x100 in some cases in biosfn_set_all_dac_reg,
+ biosfn_read_all_dac_reg, biosfn_perform_gray_scale_summing
+
+2003-01-15 17:34 cbothamy
+
+ * Makefile (1.8):
+
+ - fix symbol table file names, discovered by ams
+
+2003-01-04 21:20 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.6), VGABIOS-lgpl-latest.debug.bin
+ (1.6), vgabios.c (1.21):
+
+ - biosfn_set_video_mode(): reset attribute controller flip-flop
+ before setting up the controller's registers (bug found with
+ amidiag)
+
+2003-01-04 09:50 vruppert
+
+ * vbe.c (1.23):
+
+ - VBE function 0x00 returns VBE 1.x compatible information if no
+ VBE signature is present
+
+2003-01-01 12:44 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.5), VGABIOS-lgpl-latest.debug.bin
+ (1.5):
+
+ - SVGA mode 0x6A (800x600x4) added to the list of graphics modes
+
+2002-12-31 18:07 vruppert
+
+ * vgatables.h (1.2):
+
+ - SVGA mode 0x6A (800x600x4) added to the list of graphics modes
+
+2002-11-23 10:38 cbothamy
+
+ * ChangeLog (1.17, v0_3b):
+
+ - fix changelog for 0.3b release
+
+2002-10-20 17:12 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.4), VGABIOS-lgpl-latest.debug.bin
+ (1.4), vgabios.c (1.20) (utags: v0_3b):
+
+ - new function set_scan_lines() for the font size change (patch
+ from Hartmut Birr) - cursor shape start and end must be updated
+ in set_scan_lines() - set_scan_lines() is called by the functions
+ 0x1110, 0x1111, 0x1112 and 0x1114 after copying the font data
+
+2002-10-04 08:20 vruppert
+
+ * VGABIOS-lgpl-latest.bin (1.3), VGABIOS-lgpl-latest.debug.bin
+ (1.3), vgabios.c (1.19):
+
+ - biosfn_set_single_dac_reg(): the red value is stored in DH
+
+2002-09-19 19:05 cbothamy
+
+ * VGABIOS-lgpl-latest.bin (1.2), VGABIOS-lgpl-latest.debug.bin
+ (1.2):
+
+ - updated with latest changes
+
+2002-09-19 19:03 cbothamy
+
+ * ChangeLog (1.16), Makefile (1.7, v0_3b), vbe.c (1.22, v0_3b),
+ vgabios.c (1.18), vgabios.h (1.3, v0_4b, v0_4a, v0_3b):
+
+ - updated the Makefile - removed display of copyrights. -
+ changed the Copyright string to "LGPL VGABios developers"
+
+2002-09-08 21:14 vruppert
+
+ * vgabios.c (1.17):
+
+ - set the cursor shape depending on the current font height -
+ clear BL before calling int 0x10 function 0x1103 in
+ vgabios_init_func
+
+2002-08-23 22:58 cbothamy
+
+ * vbe.c (1.21), vbetables.h (1.12, v0_3b):
+
+ - added lfb-mode numbers (patch from mathis)
+
+2002-07-21 21:57 japj
+
+ * vbe.c (1.20), vgabios.c (1.16):
+
+ gcc2/3 preprocessing fix
+
+2002-05-18 16:55 cbothamy
+
+ * vgabios.c (1.15):
+
+ - include patch from Volker that adds some text font functions
+
+2002-05-01 23:13 japj
+
+ * VGABIOS-lgpl-latest.bin (1.1), VGABIOS-lgpl-latest.debug.bin
+ (1.1):
+
+ adding latest bin & debug bin of the vgabios
+
+2002-04-29 14:50 japj
+
+ * ChangeLog (1.15), vbe.c (1.19), vbe.h (1.12, v0_3b), vbetables.h
+ (1.11), vgabios.c (1.14):
+
+ - applying hw scrolling/multibuffering patch
+
+2002-04-25 21:59 japj
+
+ * Makefile (1.6), vbe.c (1.18), vgabios.c (1.13):
+
+ - reverting #asm/##asm & endasm patch (does not work with with
+ cygwin)
+
+2002-04-19 19:38 japj
+
+ * Makefile (1.5), vbe.c (1.17), vgabios.c (1.12):
+
+ - fixing preprocessing of vgabios with latest gcc (from Mandrake
+ 8.2)
+
+2002-04-08 23:44 japj
+
+ * ChangeLog (1.14), vbe_display_api.txt (1.5, v0_3b):
+
+ - preparing docs for new DISPI interface (for hardware scrolling)
+
+2002-04-03 19:06 japj
+
+ * ChangeLog (1.13), TODO (1.9, v0_4b, v0_4a, v0_3b), vbe.c (1.16):
+
+ - defaulting LFB on + updated changelog & todo
+
+2002-04-03 00:38 cbothamy
+
+ * vbe.c (1.15), vgabios.c (1.11):
+
+ - changed the logging ports to 0x500 -> 0x502
+
+2002-03-14 17:54 japj
+
+ * vbe.c (1.14):
+
+ - vbetables.h is dependant upon some defines (VBE_HAVE_LFB), so
+ put the include *after* the define
+
+2002-03-13 21:47 japj
+
+ * ChangeLog (1.12), TODO (1.8), vbe.c (1.13), vbetables.h (1.10),
+ vgabios.c (1.10):
+
+ - made LFB dependant upon define - not implement vbe functions
+ return failure - updated todo & docs for things after bochs 1.4
+
+2002-03-13 19:46 japj
+
+ * vbe.h (1.11), vbe_display_api.txt (1.4):
+
+ - added max video memory + documented what is in the 0xb0c0
+ interface
+
+2002-03-12 02:33 cbothamy
+
+ * ChangeLog (1.11), Makefile (1.4):
+
+ - updated for 0.3a. Merged vgabios.bin and vbebios.bin
+
+2002-03-10 21:36 japj
+
+ * ChangeLog (1.10), vbetables.h (1.9):
+
+ - added LFB modes for testing with vbe-lfb patch in Bochs
+
+2002-03-10 17:42 japj
+
+ * vbe.c (1.12, v0_3a):
+
+ - show people when they do NOT have VBE support available
+
+2002-03-10 17:36 japj
+
+ * TODO (1.7, v0_3a), vbe.c (1.11), vbe.h (1.10, v0_3a), vgabios.c
+ (1.9, v0_3a):
+
+ - cleanup of vbe internal functions (set 8bpp mode is now
+ dependant on ModeInfo content instead of hardcoded functions)
+
+2002-03-10 17:20 cbothamy
+
+ * ChangeLog (1.9, v0_3a), TODO (1.6):
+
+ - updated for 0.3a
+
+2002-03-10 17:19 cbothamy
+
+ * vbe.c (1.10), vbe.h (1.9):
+
+ - added vbe_has_vbe_display function that detects an attached vbe
+ display
+
+2002-03-10 17:12 cbothamy
+
+ * vgabios.c (1.8):
+
+ - vbe calls are done only if a vbe display is detected
+
+2002-03-10 11:25 japj
+
+ * vbe.h (1.8), vbe_display_api.txt (1.3, v0_3a):
+
+ - preparing for LFB support
+
+2002-03-09 14:25 japj
+
+ * vgabios.c (1.7):
+
+ - fixing initial cursor shape to _ instead of -
+
+2002-03-08 23:08 japj
+
+ * ChangeLog (1.8), TODO (1.5), vbe.c (1.9), vbe.h (1.7), vgabios.c
+ (1.6):
+
+ - updating vbe code to new API
+
+2002-03-08 21:48 japj
+
+ * vbe.c (1.8), vbe.h (1.6), vbetables.h (1.8, v0_3a):
+
+ - updating vbe code with #defines from API
+
+2002-03-08 21:31 japj
+
+ * vbe_display_api.txt (1.2):
+
+ - adding some text about how banks work
+
+2002-03-08 21:09 japj
+
+ * ChangeLog (1.7), vbe_display_api.txt (1.1):
+
+ - adding vbe_display_api documentation
+
+2002-03-07 21:36 japj
+
+ * ChangeLog (1.6), vbe.c (1.7), vbetables.h (1.7):
+
+ - added 1024x768xbpp support - some more cleanups/comments
+
+2002-03-06 21:55 japj
+
+ * ChangeLog (1.5), TODO (1.4), vbe.c (1.6), vbetables.h (1.6),
+ vgabios.c (1.5):
+
+ - updated changelog with new modi - added 640x480x8 (Mandrake
+ Installer can use this!) - added pre VBE2 compatible 'detection'
+ - fixed problem when normal vga set mode wouldn't disable vbe
+ mode
+
+2002-03-06 20:59 japj
+
+ * TODO (1.3), vbe.c (1.5), vbe.h (1.5), vbetables.h (1.5),
+ vgabios.c (1.4):
+
+ - adding 640x400x8 and 800x600x8 vbe support (this depends
+ HEAVILY on my bochs vga code patch - japj)
+
+2002-03-06 18:00 japj
+
+ * vbe.c (1.4), vbe.h (1.4), vbetables.h (1.4):
+
+ - implemented banked & lfb support for 320x200x8bpp (some fixes
+ for vbetest program not displaying anything)
+
+2002-03-05 20:25 japj
+
+ * Makefile (1.3, v0_3a):
+
+ for vbe debug bios: - print debugging information in assembly
+ output - print source code in assembly output
+
+2002-03-01 19:39 japj
+
+ * ChangeLog (1.4), TODO (1.2), vbe.c (1.3), vbe.h (1.3),
+ vbetables.h (1.3):
+
+ - added vbe support for 320x200x8 using the standard vgamode
+ (0x13)
+
+2002-02-19 00:29 japj
+
+ * ChangeLog (1.3):
+
+ - updating ChangeLog with lfbprof
+
+2002-02-18 23:26 japj
+
+ * tests/lfbprof/: lfbprof.c (1.2), lfbprof.h (1.2) (utags: v0_3a,
+ v0_3b, v0_4a, v0_4b):
+
+ - fixed unsigned short for mode list (-1 != 0xffff otherwise) -
+ fixed LfbMapRealPointer macro mask problem (some modes were
+ skipped) - added some extra 'debugging' printf's
+
+2002-02-18 23:07 japj
+
+ * tests/lfbprof/: Makefile (1.1, v0_4b, v0_4a, v0_3b, v0_3a),
+ lfbprof.c (1.1), lfbprof.h (1.1):
+
+ - Adding lfbprof testprogram (for vbe testing purposes) It
+ needs to be compiled with the Watcom C Compiler
+
+2002-02-18 18:48 japj
+
+ * vbe.c (1.2), vbe.h (1.2):
+
+ - cosmetic updates to vbe.c/h + added bunch of FIXMEs for work
+ that needs to be done
+
+2002-02-18 18:34 japj
+
+ * vbetables.h (1.2):
+
+ - cosmetic updates in vbetables.h
+
+2002-02-18 18:32 japj
+
+ * ChangeLog (1.2):
+
+ updated changelog with merge of vbebios 0.2
+
+2002-02-18 18:07 japj
+
+ * vgabios.c (1.3):
+
+ - small cosmetic cleanup in vgabios vbe code + added FIXMEs
+
+2002-02-18 17:55 japj
+
+ * Makefile (1.2), dataseghack (1.2, v0_4b, v0_4a, v0_3b, v0_3a),
+ vbe.c (1.1), vbe.h (1.1), vbetables.h (1.1), vgabios.c (1.2),
+ vgabios.h (1.2, v0_3a):
+
+ - merging with vbebios 0.2 release
+
+2002-02-18 11:31 cbothamy
+
+ * BUGS (1.1, v0_4b, v0_4a, v0_3b, v0_3a), COPYING (1.1, v0_4b,
+ v0_4a, v0_3b, v0_3a), ChangeLog (1.1), Makefile (1.1), Notes
+ (1.1, v0_4b, v0_4a, v0_3b, v0_3a), README (1.1, v0_3b, v0_3a),
+ TODO (1.1), dataseghack (1.1), vgabios.c (1.1), vgabios.h (1.1),
+ vgafonts.h (1.1, v0_4b, v0_4a, v0_3b, v0_3a), vgatables.h (1.1,
+ v0_3b, v0_3a), tests/testbios.c (1.1, v0_4b, v0_4a, v0_3b,
+ v0_3a):
+
+ - initial import
+
diff --git a/kvm/vgabios/Makefile b/kvm/vgabios/Makefile
new file mode 100644
index 0000000..00e8c66
--- /dev/null
+++ b/kvm/vgabios/Makefile
@@ -0,0 +1,87 @@
+SHELL = /bin/sh
+
+CC = gcc
+CFLAGS = -g -O2 -Wall -Wstrict-prototypes
+LDFLAGS =
+
+GCC = gcc
+BCC = bcc
+AS86 = as86
+
+RELEASE = `pwd | sed "s-.*/--"`
+RELDATE = `date '+%d %b %Y'`
+RELVERS = `pwd | sed "s-.*/--" | sed "s/vgabios//" | sed "s/-//"`
+
+VGABIOS_DATE = "-DVGABIOS_DATE=\"$(RELDATE)\""
+
+all: bios cirrus-bios
+
+
+bios: biossums vgabios.bin vgabios.debug.bin
+
+cirrus-bios: vgabios-cirrus.bin vgabios-cirrus.debug.bin
+
+clean:
+ /bin/rm -f biossums vbetables-gen vbetables.h *.o *.s *.ld86 \
+ temp.awk.* vgabios*.orig _vgabios_* _vgabios-debug_* core vgabios*.bin vgabios*.txt $(RELEASE).bin *.bak
+
+dist-clean: clean
+
+release:
+ VGABIOS_VERS=\"-DVGABIOS_VERS=\\\"$(RELVERS)\\\"\" make bios cirrus-bios
+ /bin/rm -f *.o *.s *.ld86 \
+ temp.awk.* vgabios.*.orig _vgabios_.*.c core *.bak .#*
+ cp VGABIOS-lgpl-latest.bin ../$(RELEASE).bin
+ cp VGABIOS-lgpl-latest.debug.bin ../$(RELEASE).debug.bin
+ cp VGABIOS-lgpl-latest.cirrus.bin ../$(RELEASE).cirrus.bin
+ cp VGABIOS-lgpl-latest.cirrus.debug.bin ../$(RELEASE).cirrus.debug.bin
+ tar czvf ../$(RELEASE).tgz --exclude CVS -C .. $(RELEASE)/
+
+vgabios.bin: vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE $(VGABIOS_DATE) > _vgabios_.c
+ $(BCC) -o vgabios.s -C-c -D__i86__ -S -0 _vgabios_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios.s > _vgabios_.s
+ $(AS86) _vgabios_.s -b vgabios.bin -u -w- -g -0 -j -O -l vgabios.txt
+ rm -f _vgabios_.s _vgabios_.c vgabios.s
+ mv vgabios.bin VGABIOS-lgpl-latest.bin
+ ./biossums VGABIOS-lgpl-latest.bin
+ ls -l VGABIOS-lgpl-latest.bin
+
+vgabios.debug.bin: vgabios.c vgabios.h vgafonts.h vgatables.h vbe.h vbe.c vbetables.h
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DVBE -DDEBUG $(VGABIOS_DATE) > _vgabios-debug_.c
+ $(BCC) -o vgabios-debug.s -C-c -D__i86__ -S -0 _vgabios-debug_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios-debug.s > _vgabios-debug_.s
+ $(AS86) _vgabios-debug_.s -b vgabios.debug.bin -u -w- -g -0 -j -O -l vgabios.debug.txt
+ rm -f _vgabios-debug_.s _vgabios-debug_.c vgabios-debug.s
+ mv vgabios.debug.bin VGABIOS-lgpl-latest.debug.bin
+ ./biossums VGABIOS-lgpl-latest.debug.bin
+ ls -l VGABIOS-lgpl-latest.debug.bin
+
+vgabios-cirrus.bin: vgabios.c vgabios.h vgafonts.h vgatables.h clext.c
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus_.c
+ $(BCC) -o vgabios-cirrus.s -C-c -D__i86__ -S -0 _vgabios-cirrus_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus.s > _vgabios-cirrus_.s
+ $(AS86) _vgabios-cirrus_.s -b vgabios-cirrus.bin -u -w- -g -0 -j -O -l vgabios.cirrus.txt
+ rm -f _vgabios-cirrus_.s _vgabios-cirrus_.c vgabios-cirrus.s
+ mv vgabios-cirrus.bin VGABIOS-lgpl-latest.cirrus.bin
+ ./biossums VGABIOS-lgpl-latest.cirrus.bin
+ ls -l VGABIOS-lgpl-latest.cirrus.bin
+
+vgabios-cirrus.debug.bin: vgabios.c vgabios.h vgafonts.h vgatables.h clext.c
+ $(GCC) -E -P vgabios.c $(VGABIOS_VERS) -DCIRRUS -DCIRRUS_DEBUG -DPCIBIOS $(VGABIOS_DATE) > _vgabios-cirrus-debug_.c
+ $(BCC) -o vgabios-cirrus-debug.s -C-c -D__i86__ -S -0 _vgabios-cirrus-debug_.c
+ sed -e 's/^\.text//' -e 's/^\.data//' vgabios-cirrus-debug.s > _vgabios-cirrus-debug_.s
+ $(AS86) _vgabios-cirrus-debug_.s -b vgabios.cirrus.debug.bin -u -w- -g -0 -j -O -l vgabios.cirrus.debug.txt
+ rm -f _vgabios-cirrus-debug_.s _vgabios-cirrus-debug_.c vgabios-cirrus-debug.s
+ mv vgabios.cirrus.debug.bin VGABIOS-lgpl-latest.cirrus.debug.bin
+ ./biossums VGABIOS-lgpl-latest.cirrus.debug.bin
+ ls -l VGABIOS-lgpl-latest.cirrus.debug.bin
+
+biossums: biossums.c
+ $(CC) -o biossums biossums.c
+
+vbetables-gen: vbetables-gen.c
+ $(CC) -o vbetables-gen vbetables-gen.c
+
+vbetables.h: vbetables-gen
+ ./vbetables-gen > $@
diff --git a/kvm/vgabios/Notes b/kvm/vgabios/Notes
new file mode 100644
index 0000000..d5b708d
--- /dev/null
+++ b/kvm/vgabios/Notes
@@ -0,0 +1,11 @@
+Development notes
+-----------------
+
+- need to split video init function
+ 1. set bios variables
+ 2. do the real init with io based on bios variables
+
+- characters format switching will set the bios
+ variables and call function #2 above
+
+- need to rework the tables as explained in Interrupt list
diff --git a/kvm/vgabios/README b/kvm/vgabios/README
new file mode 100644
index 0000000..90141d4
--- /dev/null
+++ b/kvm/vgabios/README
@@ -0,0 +1,219 @@
+Plex86/Bochs VGABios
+--------------------
+
+The goal of this project is to have a LGPL'd Video Bios in plex86,
+Bochs and qemu.
+This VGA Bios is very specific to the emulated VGA card.
+It is NOT meant to drive a physical vga card.
+
+
+Cirrus SVGA extension
+---------------------
+
+The Cirrus SVGA extension is designed for the Cirrus emulation in Bochs and
+qemu. The initial patch for the Cirrus extension has been written by Makoto
+Suzuki (suzu).
+
+
+Install
+-------
+To compile the VGA Bios you will need :
+- gcc
+- bcc
+- as86
+- ld86
+
+Untar the archive, and type make. You should get a "VGABIOS-lgpl-latest.bin"
+file. Alternatively, you can use the binary file "VGABIOS-lgpl-latest.bin",
+i have compiled for you.
+
+Edit your plex86/bochs conf file, and modify the load-rom command in the
+VGA BIOS section, to point to the new vgabios image file.
+
+
+Debugging
+---------
+You can get a very basic debugging system: messages printed by the vgabios.
+You have to register the "unmapped" device driver in plex86 or bochs, and make
+sure it grabs port 0xfff0.
+
+Comment the #undef DEBUG at the beginning of vgabios.c.
+You can then use the "printf" function in the bios.
+
+
+Testing
+-------
+Look at the "testvga.c" file in the archive. This is a minimal Turbo C 2.0
+source file that calls a few int10 functions. Feel free to modify it to suit
+your needs.
+
+
+Copyright and License
+---------------------
+This program has been written by Christophe Bothamy
+It is protected by the GNU Lesser Public License, which you should
+have received a copy of along with this package.
+
+
+Reverse Engineering
+-------------------
+The VGA Bios has been written without reverse-engineering any existing Bios.
+
+
+Acknowledgment
+--------------
+The source code contains code ripped from rombios.c of plex86, written
+by Kevin Lawton <kevin2001@yahoo.com>
+
+The source code contains fonts from fntcol16.zip (c) by Joseph Gil avalable at :
+ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+These fonts are public domain
+
+The source code is based on information taken from :
+- Kevin Lawton's vga card emulation for bochs/plex86
+- Ralf Brown's interrupts list avalaible at
+ http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
+- Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/
+- Michael Abrash's Graphics Programming Black Book
+- Francois Gervais' book "programmation des cartes graphiques cga-ega-vga"
+ edited by sybex
+- DOSEMU 1.0.1 source code for several tables values and formulas
+
+
+Feedback
+--------
+Please report any bugs, comments, patches for this VGA Bios to info@vruppert.de
+You can find the latest release at : http://www.nongnu.org/vgabios/
+For any information on bochs, visit the website http://bochs.sourceforge.net/
+For any information on qemu, visit the website http://fabrice.bellard.free.fr/qemu/
+
+
+History
+-------
+vgabios-0.6b : May 30 2008
+ - Volker
+ . added PCI data structure for the Cirrus VGABIOS images
+ . minor bugfixes in biossums utility, VBE support and makefile
+
+vgabios-0.6a : Aug 19 2006
+ - Volker
+ . added minimal support for the video parameter table (VPT)
+ . Cirrus SVGA now supports the "no clear" bit in Cirrus and VESA mode
+ . Bochs VBE protected mode interface improved
+ . save/restore video state support for Bochs VBE and standard VGA added
+ . generate vbetables.h dynamicly
+ . VBE video memory increased to 8 MB (VBE dispi ID changed to B0C4)
+ . lots of 4bpp VBE fixes (all 4bpp VBE modes now enabled)
+ . VGA compatible setup for VBE modes added
+
+vgabios-0.5d : Dec 29 2005
+ - Volker
+ . Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com)
+ . biossums utility now supports VGABIOS sizes up to 64 kBytes
+ . VGA mode 0x11: all color planes must be enabled in this 2-color VGA mode
+
+vgabios-0.5c : Jul 07 2005
+ - Volker
+ . BIOS configuration word usually reports initial mode 80x25 color text
+ . vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the
+ cursor row value
+
+vgabios-0.5b : May 24 2005
+ - Volker
+ . fixed return value for the default case in the VBE section (non-debug mode)
+ . removed unused stuff
+
+vgabios-0.5a : Mar 07 2005
+ - Volker
+ . Cirrus SVGA extension (initial patches from Makoto Suzuki, improvements
+ from Fabrice Bellard)
+ . vgabios image size is now exactly 32k with a checksum
+ . a lot of vgabios and vbe functions rewritten in assembler
+ . dynamicly generated VBE mode info list
+ . write character function for CGA and LINEAR8 modes
+ . read/write graphics pixel for some graphics modes
+ . text scroll feature for some graphics modes
+ . VBE 8-bit DAC support
+
+vgabios-0.4c : Nov 06 2003
+ - Christophe
+ . fix font problem on initial screen of NT4 Loader
+
+vgabios-0.4b : Nov 04 2003
+ - Volker
+ . fix offset of character tables
+ . optimizations of CRT controller accesses
+ . VBE i/o registers changed to 0x01CE/CF
+ (suggestion from Daniel Gimpelevich)
+ . "noclear" flag stored in BIOS area
+ . fix character height returned by get_font_info function
+
+vgabios-0.4a : Aug 17 2003
+ - Volker
+ . VBE mode search rewritten (VBE modes with LFB bit removed)
+ . many bugfixes and optimizations
+ . write character function implemented for graphics modes
+ . support for 15bpp, 16bpp, 24bpp and 32bpp VBE modes added
+ . SVGA mode 0x6A added
+ . VBE modes 0x102, 0x117, 0x118 and 0x142 (Bochs specific)
+
+vgabios-0.3b : Nov 23 2002
+ - Christophe
+ . added lfb-mode numbers (patch from mathis)
+ . updated the Makefile
+ . removed display of copyrights.
+ . changed the Copyright string to "LGPL VGABios developers"
+ - Volker
+ . set the cursor shape depending on the current font height
+ . clear BL before calling int 0x10 function 0x1103 in vgabios_init_func
+ . added some text font functions
+ - Jeroen
+ . Forced to new DISPI (0xb0c1) interface (requires latest bochs vbe code)
+ . Added multibuffering support
+ . Added new DISPI interface for: virt width, height, x offset, y offset
+ . Added LFB modes (to be used with the vbe-lfb patch in bochs)
+ see VBE_HAVE_LFB in vbe.c (currently default enabled)
+ . updated TODO & docs for changes after bochs 1.4
+
+vgabios-0.3a : Mar 10 2002
+ - Christophe
+ . Fixed bug in function ah=13
+ - Jeroen
+ . updated vbebios implementation to new api
+ . added vbe_display_api documentation
+ . added 640x400x8, 640x480x8, 800x600x8, 1024x768
+ (>640x480 needs a special bochs patch atm)
+ . added 320x200x8 vbe support (uses the standard 320x200x8 vga mode to
+ display, this allows for testing & having something on screen as well,
+ at least until bochs host side display is up & running)
+ . adding lfbprof (vbe) testprogram (+some small fixes to it)
+ . merging with vbebios 0.2
+
+vgabios-0.2b : Nov 19 2001
+ - Christophe
+ . Fixed bug in function ah=13
+
+vgabios-0.2a : Nov 09 2001
+ - Christophe
+ . Included bugfix from techt@pikeonline.net about grayscale summing
+ . Added the "IBM" string at org 0x1e as Bart Oldeman suggested
+ . Fixed DS and ES that where inverted in the int10 parameters list!
+ . The following have been implemented :
+ - function ax=1a00, ax=1a01, ah=1b
+ - function ax=1130
+ . Added debug messages for unimplemented/unknown functions
+ Must be compiled with DEBUG defined. The output is trapped
+ by the unknown-ioport driver of plex/bochs (port 0xfff0 is used)
+
+vgabios-0.1a : May 8 2001
+ - Christophe
+ . First release. The work has been focused only on text mode.
+ . The following have been implemented :
+ - inits
+ - int 10 handler
+ - functions ah=00, ah=01, ah=02, ah=03, ah=05, ah=06, ah=07, ah=08
+ ah=09, ah=0a, ah=0e, ah=0f, ax=1000, ax=1001, ax=1002, ax=1003
+ ax=1007, ax=1008, ax=1009, ax=1010, ax=1012, ax=1013, ax=1015
+ ax=1017, ax=1018, ax=1019, ax=101a, ax=101b, ah=12 bl=10,
+ ah=12 bl=30, ah=12 bl=31, ah=12 bl=32, ah=12 bl=33, ah=12 bl=34
+ ah=13
diff --git a/kvm/vgabios/TODO b/kvm/vgabios/TODO
new file mode 100644
index 0000000..b08ee4b
--- /dev/null
+++ b/kvm/vgabios/TODO
@@ -0,0 +1,26 @@
+Short term :
+------------
+
+General
+ - Fix init mode (ah=00). Should use more BIOS variables
+ - Add new functionalities and modify static functionality table
+ - Performance : 16 bits IO
+
+v0.7
+ - Implement the remaining functions (don't know if all are needed):
+ - chargen ax=1120, ax=1121, ax=1122, ax=1123, ax=1124
+ - display switch interface ah=12 bl=35
+ - video refresh control ah=12 bl=36
+ - Graphic modes
+
+v1.0
+ - Bugfixes
+
+
+=================================================================================================
+VBE:
+----
+Long term:
+- have plex86 host side display interface
+- have text io functions in vbe mode
+
diff --git a/kvm/vgabios/biossums.c b/kvm/vgabios/biossums.c
new file mode 100644
index 0000000..d5816f4
--- /dev/null
+++ b/kvm/vgabios/biossums.c
@@ -0,0 +1,282 @@
+/* biossums.c --- written by Eike W. for the Bochs BIOS */
+/* adapted for the LGPL'd VGABIOS by vruppert */
+
+/* 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef unsigned char byte;
+
+void check( int value, char* message );
+
+#define MAX_BIOS_DATA 0x10000
+
+long chksum_bios_get_offset( byte* data, long offset );
+byte chksum_bios_calc_value( byte* data, long offset );
+byte chksum_bios_get_value( byte* data, long offset );
+void chksum_bios_set_value( byte* data, long offset, byte value );
+
+#define PMID_LEN 20
+#define PMID_CHKSUM 19
+
+long chksum_pmid_get_offset( byte* data, long offset );
+byte chksum_pmid_calc_value( byte* data, long offset );
+byte chksum_pmid_get_value( byte* data, long offset );
+void chksum_pmid_set_value( byte* data, long offset, byte value );
+
+#define PCIR_LEN 24
+
+long chksum_pcir_get_offset( byte* data, long offset );
+
+
+byte bios_data[MAX_BIOS_DATA];
+long bios_len;
+
+
+int main(int argc, char* argv[])
+{
+ FILE* stream;
+ long offset, tmp_offset, pcir_offset;
+ byte bios_len_byte, cur_val = 0, new_val = 0;
+ int hits, modified;
+
+ if (argc != 2) {
+ printf( "Error. Need a file-name as an argument.\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ if ((stream = fopen(argv[1], "rb")) == NULL) {
+ printf("Error opening %s for reading.\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ memset(bios_data, 0, MAX_BIOS_DATA);
+ bios_len = fread(bios_data, 1, MAX_BIOS_DATA, stream);
+ if (bios_len > MAX_BIOS_DATA) {
+ printf("Error reading max. 65536 Bytes from %s.\n", argv[1]);
+ fclose(stream);
+ exit(EXIT_FAILURE);
+ }
+ fclose(stream);
+ modified = 0;
+ if (bios_len < 0x8000) {
+ bios_len = 0x8000;
+ modified = 1;
+ } else if ((bios_len & 0x1FF) != 0) {
+ bios_len = (bios_len + 0x200) & ~0x1FF;
+ modified = 1;
+ }
+ bios_len_byte = (byte)(bios_len / 512);
+ if (bios_len_byte != bios_data[2]) {
+ if (modified == 0) {
+ bios_len += 0x200;
+ }
+ bios_data[2] = (byte)(bios_len / 512);
+ modified = 1;
+ }
+
+ hits = 0;
+ offset = 0L;
+ while( (tmp_offset = chksum_pmid_get_offset( bios_data, offset )) != -1L ) {
+ offset = tmp_offset;
+ cur_val = chksum_pmid_get_value( bios_data, offset );
+ new_val = chksum_pmid_calc_value( bios_data, offset );
+ printf( "\nPMID entry at: 0x%4lX\n", offset );
+ printf( "Current checksum: 0x%02X\n", cur_val );
+ printf( "Calculated checksum: 0x%02X ", new_val );
+ hits++;
+ }
+ if ((hits == 1) && (cur_val != new_val)) {
+ printf("Setting checksum.");
+ chksum_pmid_set_value( bios_data, offset, new_val );
+ if (modified == 0) {
+ bios_len += 0x200;
+ bios_data[2]++;
+ }
+ modified = 1;
+ }
+ if (hits >= 2) {
+ printf( "Multiple PMID entries! No checksum set." );
+ }
+ if (hits) {
+ printf("\n");
+ }
+
+ offset = 0L;
+ pcir_offset = chksum_pcir_get_offset( bios_data, offset );
+ if (pcir_offset != -1L) {
+ if (bios_data[pcir_offset + 16] != bios_data[2]) {
+ bios_data[pcir_offset + 16] = bios_data[2];
+ if (modified == 0) {
+ bios_len += 0x200;
+ bios_data[2]++;
+ bios_data[pcir_offset + 16]++;
+ }
+ modified = 1;
+ }
+ }
+
+ offset = 0L;
+ do {
+ offset = chksum_bios_get_offset(bios_data, offset);
+ cur_val = chksum_bios_get_value(bios_data, offset);
+ new_val = chksum_bios_calc_value(bios_data, offset);
+ if ((cur_val != new_val) && (modified == 0)) {
+ bios_len += 0x200;
+ bios_data[2]++;
+ if (pcir_offset != -1L) {
+ bios_data[pcir_offset + 16]++;
+ }
+ modified = 1;
+ } else {
+ printf("\nBios checksum at: 0x%4lX\n", offset);
+ printf("Current checksum: 0x%02X\n", cur_val);
+ printf("Calculated checksum: 0x%02X ", new_val);
+ if (cur_val != new_val) {
+ printf("Setting checksum.");
+ chksum_bios_set_value(bios_data, offset, new_val);
+ cur_val = new_val;
+ modified = 1;
+ }
+ printf( "\n" );
+ }
+ } while (cur_val != new_val);
+
+ if (modified == 1) {
+ if ((stream = fopen( argv[1], "wb")) == NULL) {
+ printf("Error opening %s for writing.\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+ if (fwrite(bios_data, 1, bios_len, stream) < bios_len) {
+ printf("Error writing %d KBytes to %s.\n", bios_len / 1024, argv[1]);
+ fclose(stream);
+ exit(EXIT_FAILURE);
+ }
+ fclose(stream);
+ }
+
+ return (EXIT_SUCCESS);
+}
+
+
+void check( int okay, char* message ) {
+
+ if( !okay ) {
+ printf( "\n\nError. %s.\n", message );
+ exit( EXIT_FAILURE );
+ }
+}
+
+
+long chksum_bios_get_offset( byte* data, long offset ) {
+
+ return (bios_len - 1);
+}
+
+
+byte chksum_bios_calc_value( byte* data, long offset ) {
+
+ int i;
+ byte sum;
+
+ sum = 0;
+ for( i = 0; i < offset; i++ ) {
+ sum = sum + *( data + i );
+ }
+ sum = -sum; /* iso ensures -s + s == 0 on unsigned types */
+ return( sum );
+}
+
+
+byte chksum_bios_get_value( byte* data, long offset ) {
+
+ return( *( data + offset ) );
+}
+
+
+void chksum_bios_set_value( byte* data, long offset, byte value ) {
+
+ *( data + offset ) = value;
+}
+
+
+byte chksum_pmid_calc_value( byte* data, long offset ) {
+
+ int i;
+ int len;
+ byte sum;
+
+ len = PMID_LEN;
+ check((offset + len) <= (bios_len - 1), "PMID entry length out of bounds" );
+ sum = 0;
+ for( i = 0; i < len; i++ ) {
+ if( i != PMID_CHKSUM ) {
+ sum = sum + *( data + offset + i );
+ }
+ }
+ sum = -sum;
+ return( sum );
+}
+
+
+long chksum_pmid_get_offset( byte* data, long offset ) {
+
+ long result = -1L;
+
+ while ((offset + PMID_LEN) < (bios_len - 1)) {
+ offset = offset + 1;
+ if( *( data + offset + 0 ) == 'P' && \
+ *( data + offset + 1 ) == 'M' && \
+ *( data + offset + 2 ) == 'I' && \
+ *( data + offset + 3 ) == 'D' ) {
+ result = offset;
+ break;
+ }
+ }
+ return( result );
+}
+
+
+byte chksum_pmid_get_value( byte* data, long offset ) {
+
+ check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" );
+ return( *( data + offset + PMID_CHKSUM ) );
+}
+
+
+void chksum_pmid_set_value( byte* data, long offset, byte value ) {
+
+ check((offset + PMID_CHKSUM) <= (bios_len - 1), "PMID checksum out of bounds" );
+ *( data + offset + PMID_CHKSUM ) = value;
+}
+
+
+long chksum_pcir_get_offset( byte* data, long offset ) {
+
+ long result = -1L;
+
+ while ((offset + PCIR_LEN) < (bios_len - 1)) {
+ offset = offset + 1;
+ if( *( data + offset + 0 ) == 'P' && \
+ *( data + offset + 1 ) == 'C' && \
+ *( data + offset + 2 ) == 'I' && \
+ *( data + offset + 3 ) == 'R' ) {
+ result = offset;
+ break;
+ }
+ }
+ return( result );
+}
diff --git a/kvm/vgabios/clext.c b/kvm/vgabios/clext.c
new file mode 100644
index 0000000..c7a2ad0
--- /dev/null
+++ b/kvm/vgabios/clext.c
@@ -0,0 +1,1688 @@
+//
+// QEMU Cirrus CLGD 54xx VGABIOS Extension.
+//
+// Copyright (c) 2004 Makoto Suzuki (suzu)
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+
+//#define CIRRUS_VESA3_PMINFO
+#ifdef VBE
+#undef CIRRUS_VESA3_PMINFO
+#endif
+
+#define PM_BIOSMEM_CURRENT_MODE 0x449
+#define PM_BIOSMEM_CRTC_ADDRESS 0x463
+#define PM_BIOSMEM_VBE_MODE 0x4BA
+
+typedef struct
+{
+ /* + 0 */
+ unsigned short mode;
+ unsigned short width;
+ unsigned short height;
+ unsigned short depth;
+ /* + 8 */
+ unsigned short hidden_dac; /* 0x3c6 */
+ unsigned short *seq; /* 0x3c4 */
+ unsigned short *graph; /* 0x3ce */
+ unsigned short *crtc; /* 0x3d4 */
+ /* +16 */
+ unsigned char bitsperpixel;
+ unsigned char vesacolortype;
+ unsigned char vesaredmask;
+ unsigned char vesaredpos;
+ unsigned char vesagreenmask;
+ unsigned char vesagreenpos;
+ unsigned char vesabluemask;
+ unsigned char vesabluepos;
+ /* +24 */
+ unsigned char vesareservedmask;
+ unsigned char vesareservedpos;
+} cirrus_mode_t;
+#define CIRRUS_MODE_SIZE 26
+
+
+/* For VESA BIOS 3.0 */
+#define CIRRUS_PM16INFO_SIZE 20
+
+/* VGA */
+unsigned short cseq_vga[] = {0x0007,0xffff};
+unsigned short cgraph_vga[] = {0x0009,0x000a,0x000b,0xffff};
+unsigned short ccrtc_vga[] = {0x001a,0x001b,0x001d,0xffff};
+
+/* extensions */
+unsigned short cgraph_svgacolor[] = {
+0x0000,0x0001,0x0002,0x0003,0x0004,0x4005,0x0506,0x0f07,0xff08,
+0x0009,0x000a,0x000b,
+0xffff
+};
+/* 640x480x8 */
+unsigned short cseq_640x480x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x580b,0x580c,0x580d,0x580e,
+0x0412,0x0013,0x2017,
+0x331b,0x331c,0x331d,0x331e,
+0xffff
+};
+unsigned short ccrtc_640x480x8[] = {
+0x2c11,
+0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
+0x4009,0x000c,0x000d,
+0xea10,0xdf12,0x5013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 640x480x16 */
+unsigned short cseq_640x480x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x580b,0x580c,0x580d,0x580e,
+0x0412,0x0013,0x2017,
+0x331b,0x331c,0x331d,0x331e,
+0xffff
+};
+unsigned short ccrtc_640x480x16[] = {
+0x2c11,
+0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
+0x4009,0x000c,0x000d,
+0xea10,0xdf12,0xa013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 640x480x24 */
+unsigned short cseq_640x480x24[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
+0x580b,0x580c,0x580d,0x580e,
+0x0412,0x0013,0x2017,
+0x331b,0x331c,0x331d,0x331e,
+0xffff
+};
+unsigned short ccrtc_640x480x24[] = {
+0x2c11,
+0x5f00,0x4f01,0x4f02,0x8003,0x5204,0x1e05,0x0b06,0x3e07,
+0x4009,0x000c,0x000d,
+0xea10,0xdf12,0x0013,0x4014,0xdf15,0x0b16,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 800x600x8 */
+unsigned short cseq_800x600x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x230b,0x230c,0x230d,0x230e,
+0x0412,0x0013,0x2017,
+0x141b,0x141c,0x141d,0x141e,
+0xffff
+};
+unsigned short ccrtc_800x600x8[] = {
+0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
+0x6009,0x000c,0x000d,
+0x7d10,0x5712,0x6413,0x4014,0x5715,0x9816,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 800x600x16 */
+unsigned short cseq_800x600x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x230b,0x230c,0x230d,0x230e,
+0x0412,0x0013,0x2017,
+0x141b,0x141c,0x141d,0x141e,
+0xffff
+};
+unsigned short ccrtc_800x600x16[] = {
+0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
+0x6009,0x000c,0x000d,
+0x7d10,0x5712,0xc813,0x4014,0x5715,0x9816,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 800x600x24 */
+unsigned short cseq_800x600x24[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
+0x230b,0x230c,0x230d,0x230e,
+0x0412,0x0013,0x2017,
+0x141b,0x141c,0x141d,0x141e,
+0xffff
+};
+unsigned short ccrtc_800x600x24[] = {
+0x2311,0x7d00,0x6301,0x6302,0x8003,0x6b04,0x1a05,0x9806,0xf007,
+0x6009,0x000c,0x000d,
+0x7d10,0x5712,0x2c13,0x4014,0x5715,0x9816,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 1024x768x8 */
+unsigned short cseq_1024x768x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1024x768x8[] = {
+0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 1024x768x16 */
+unsigned short cseq_1024x768x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1024x768x16[] = {
+0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x0013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 1024x768x24 */
+unsigned short cseq_1024x768x24[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1507,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1024x768x24[] = {
+0x2911,0xa300,0x7f01,0x7f02,0x8603,0x8304,0x9405,0x2406,0xf507,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x8013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+/* 1280x1024x8 */
+unsigned short cseq_1280x1024x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1280x1024x8[] = {
+0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+/* 1280x1024x16 */
+unsigned short cseq_1280x1024x16[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1707,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1280x1024x16[] = {
+0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0x4013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x321b,0x001d,
+0xffff
+};
+
+/* 1600x1200x8 */
+unsigned short cseq_1600x1200x8[] = {
+0x0300,0x2101,0x0f02,0x0003,0x0e04,0x1107,
+0x760b,0x760c,0x760d,0x760e,
+0x0412,0x0013,0x2017,
+0x341b,0x341c,0x341d,0x341e,
+0xffff
+};
+unsigned short ccrtc_1600x1200x8[] = {
+0x2911,0xc300,0x9f01,0x9f02,0x8603,0x8304,0x9405,0x2406,0xf707,
+0x6009,0x000c,0x000d,
+0x0310,0xff12,0xa013,0x4014,0xff15,0x2416,0xc317,0xff18,
+0x001a,0x221b,0x001d,
+0xffff
+};
+
+cirrus_mode_t cirrus_modes[] =
+{
+ {0x5f,640,480,8,0x00,
+ cseq_640x480x8,cgraph_svgacolor,ccrtc_640x480x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x64,640,480,16,0xe1,
+ cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16,
+ 6,5,11,6,5,5,0,0,0},
+ {0x66,640,480,15,0xf0,
+ cseq_640x480x16,cgraph_svgacolor,ccrtc_640x480x16,16,
+ 6,5,10,5,5,5,0,1,15},
+ {0x71,640,480,24,0xe5,
+ cseq_640x480x24,cgraph_svgacolor,ccrtc_640x480x24,24,
+ 6,8,16,8,8,8,0,0,0},
+
+ {0x5c,800,600,8,0x00,
+ cseq_800x600x8,cgraph_svgacolor,ccrtc_800x600x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x65,800,600,16,0xe1,
+ cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16,
+ 6,5,11,6,5,5,0,0,0},
+ {0x67,800,600,15,0xf0,
+ cseq_800x600x16,cgraph_svgacolor,ccrtc_800x600x16,16,
+ 6,5,10,5,5,5,0,1,15},
+
+ {0x60,1024,768,8,0x00,
+ cseq_1024x768x8,cgraph_svgacolor,ccrtc_1024x768x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x74,1024,768,16,0xe1,
+ cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16,
+ 6,5,11,6,5,5,0,0,0},
+ {0x68,1024,768,15,0xf0,
+ cseq_1024x768x16,cgraph_svgacolor,ccrtc_1024x768x16,16,
+ 6,5,10,5,5,5,0,1,15},
+
+ {0x78,800,600,24,0xe5,
+ cseq_800x600x24,cgraph_svgacolor,ccrtc_800x600x24,24,
+ 6,8,16,8,8,8,0,0,0},
+ {0x79,1024,768,24,0xe5,
+ cseq_1024x768x24,cgraph_svgacolor,ccrtc_1024x768x24,24,
+ 6,8,16,8,8,8,0,0,0},
+
+ {0x6d,1280,1024,8,0x00,
+ cseq_1280x1024x8,cgraph_svgacolor,ccrtc_1280x1024x8,8,
+ 4,0,0,0,0,0,0,0,0},
+ {0x69,1280,1024,15,0xf0,
+ cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16,
+ 6,5,10,5,5,5,0,1,15},
+ {0x75,1280,1024,16,0xe1,
+ cseq_1280x1024x16,cgraph_svgacolor,ccrtc_1280x1024x16,16,
+ 6,5,11,6,5,5,0,0,0},
+
+ {0x7b,1600,1200,8,0x00,
+ cseq_1600x1200x8,cgraph_svgacolor,ccrtc_1600x1200x8,8,
+ 4,0,0,0,0,0,0,0,0},
+
+ {0xfe,0,0,0,0,cseq_vga,cgraph_vga,ccrtc_vga,0,
+ 0xff,0,0,0,0,0,0,0,0},
+ {0xff,0,0,0,0,0,0,0,0,
+ 0xff,0,0,0,0,0,0,0,0},
+};
+
+unsigned char cirrus_id_table[] = {
+ // 5430
+ 0xA0, 0x32,
+ // 5446
+ 0xB8, 0x39,
+
+ 0xff, 0xff
+};
+
+
+unsigned short cirrus_vesa_modelist[] = {
+// 640x480x8
+ 0x101, 0x5f,
+// 640x480x15
+ 0x110, 0x66,
+// 640x480x16
+ 0x111, 0x64,
+// 640x480x24
+ 0x112, 0x71,
+// 800x600x8
+ 0x103, 0x5c,
+// 800x600x15
+ 0x113, 0x67,
+// 800x600x16
+ 0x114, 0x65,
+// 800x600x24
+ 0x115, 0x78,
+// 1024x768x8
+ 0x105, 0x60,
+// 1024x768x15
+ 0x116, 0x68,
+// 1024x768x16
+ 0x117, 0x74,
+// 1024x768x24
+ 0x118, 0x79,
+// 1280x1024x8
+ 0x107, 0x6d,
+// 1280x1024x15
+ 0x119, 0x69,
+// 1280x1024x16
+ 0x11a, 0x75,
+// invalid
+ 0xffff,0xffff
+};
+
+
+ASM_START
+
+cirrus_installed:
+.ascii "cirrus-compatible VGA is detected"
+.byte 0x0d,0x0a
+.byte 0x0d,0x0a,0x00
+
+cirrus_not_installed:
+.ascii "cirrus-compatible VGA is not detected"
+.byte 0x0d,0x0a
+.byte 0x0d,0x0a,0x00
+
+cirrus_vesa_vendorname:
+cirrus_vesa_productname:
+cirrus_vesa_oemname:
+.ascii "VGABIOS Cirrus extension"
+.byte 0
+cirrus_vesa_productrevision:
+.ascii "1.0"
+.byte 0
+
+cirrus_init:
+ call cirrus_check
+ jnz no_cirrus
+ SET_INT_VECTOR(0x10, #0xC000, #cirrus_int10_handler)
+ mov al, #0x0f ; memory setup
+ mov dx, #0x3C4
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x18
+ mov ah, al
+ mov al, #0x0a
+ dec dx
+ out dx, ax
+ mov ax, #0x0007 ; set vga mode
+ out dx, ax
+ mov ax, #0x0431 ; reset bitblt
+ mov dx, #0x3CE
+ out dx, ax
+ mov ax, #0x0031
+ out dx, ax
+no_cirrus:
+ ret
+
+cirrus_display_info:
+ push ds
+ push si
+ push cs
+ pop ds
+ call cirrus_check
+ mov si, #cirrus_not_installed
+ jnz cirrus_msgnotinstalled
+ mov si, #cirrus_installed
+
+cirrus_msgnotinstalled:
+ call _display_string
+ pop si
+ pop ds
+ ret
+
+cirrus_check:
+ push ax
+ push dx
+ mov ax, #0x9206
+ mov dx, #0x3C4
+ out dx, ax
+ inc dx
+ in al, dx
+ cmp al, #0x12
+ pop dx
+ pop ax
+ ret
+
+
+cirrus_int10_handler:
+ pushf
+ push bp
+ cmp ah, #0x00 ;; set video mode
+ jz cirrus_set_video_mode
+ cmp ah, #0x12 ;; cirrus extension
+ jz cirrus_extbios
+ cmp ah, #0x4F ;; VESA extension
+ jz cirrus_vesa
+
+cirrus_unhandled:
+ pop bp
+ popf
+ jmp vgabios_int10_handler
+
+cirrus_return:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ pop bp
+ popf
+ iret
+
+cirrus_set_video_mode:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ push si
+ push ax
+ push bx
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov si, [cirrus_vesa_sel0000_data]
+#else
+ xor si, si
+#endif
+ mov ds, si
+ xor bx, bx
+ mov [PM_BIOSMEM_VBE_MODE], bx
+ pop ds
+ pop bx
+ call cirrus_get_modeentry
+ jnc cirrus_set_video_mode_extended
+ mov al, #0xfe
+ call cirrus_get_modeentry_nomask
+ call cirrus_switch_mode
+ pop ax
+ pop si
+ jmp cirrus_unhandled
+
+cirrus_extbios:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ cmp bl, #0x80
+ jb cirrus_unhandled
+ cmp bl, #0xAF
+ ja cirrus_unhandled
+ push bx
+ and bx, #0x7F
+ shl bx, 1
+ db 0x2e ;; cs:
+ mov bp, cirrus_extbios_handlers[bx]
+ pop bx
+ push #cirrus_return
+ push bp
+ ret
+
+cirrus_vesa:
+#ifdef CIRRUS_DEBUG
+ call cirrus_debug_dump
+#endif
+ cmp al, #0x10
+ ja cirrus_vesa_not_handled
+ push bx
+ xor bx, bx
+ mov bl, al
+ shl bx, 1
+ db 0x2e ;; cs:
+ mov bp, cirrus_vesa_handlers[bx]
+ pop bx
+ push #cirrus_return
+ push bp
+ ret
+
+cirrus_vesa_not_handled:
+ mov ax, #0x014F ;; not implemented
+ jmp cirrus_return
+
+#ifdef CIRRUS_DEBUG
+cirrus_debug_dump:
+ push es
+ push ds
+ pusha
+ push cs
+ pop ds
+ call _cirrus_debugmsg
+ popa
+ pop ds
+ pop es
+ ret
+#endif
+
+cirrus_set_video_mode_extended:
+ call cirrus_switch_mode
+ pop ax ;; mode
+ test al, #0x80
+ jnz cirrus_set_video_mode_extended_1
+ push ax
+ mov ax, #0xffff ; set to 0xff to keep win 2K happy
+ call cirrus_clear_vram
+ pop ax
+cirrus_set_video_mode_extended_1:
+ and al, #0x7f
+
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov si, [cirrus_vesa_sel0000_data]
+#else
+ xor si, si
+#endif
+ mov ds, si
+ mov [PM_BIOSMEM_CURRENT_MODE], al
+ pop ds
+
+ mov al, #0x20
+
+ pop si
+ jmp cirrus_return
+
+cirrus_vesa_pmbios_init:
+ retf
+cirrus_vesa_pmbios_entry:
+ pushf
+ push bp
+ cmp ah, #0x4F
+ jnz cirrus_vesa_pmbios_unimplemented
+ cmp al, #0x0F
+ ja cirrus_vesa_pmbios_unimplemented
+ push bx
+ xor bx, bx
+ mov bl, al
+ shl bx, 1
+ db 0x2e ;; cs:
+ mov bp, cirrus_vesa_handlers[bx]
+ pop bx
+ push #cirrus_vesa_pmbios_return
+ push bp
+ ret
+cirrus_vesa_pmbios_unimplemented:
+ mov ax, #0x014F
+cirrus_vesa_pmbios_return:
+ pop bp
+ popf
+ retf
+
+; in si:mode table
+cirrus_switch_mode:
+ push ds
+ push bx
+ push dx
+ push cs
+ pop ds
+
+ mov bx, [si+10] ;; seq
+ mov dx, #0x3c4
+ mov ax, #0x1206
+ out dx, ax ;; Unlock cirrus special
+ call cirrus_switch_mode_setregs
+
+ mov bx, [si+12] ;; graph
+ mov dx, #0x3ce
+ call cirrus_switch_mode_setregs
+
+ mov bx, [si+14] ;; crtc
+ call cirrus_get_crtc
+ call cirrus_switch_mode_setregs
+
+ mov dx, #0x3c6
+ mov al, #0x00
+ out dx, al
+ in al, dx
+ in al, dx
+ in al, dx
+ in al, dx
+ mov al, [si+8] ;; hidden dac
+ out dx, al
+ mov al, #0xff
+ out dx, al
+
+ mov al, #0x00
+ mov bl, [si+17] ;; memory model
+ or bl, bl
+ jz is_text_mode
+ mov al, #0x01
+ cmp bl, #0x03
+ jnz is_text_mode
+ or al, #0x40
+is_text_mode:
+ mov bl, #0x10
+ call biosfn_get_single_palette_reg
+ and bh, #0xfe
+ or bh, al
+ call biosfn_set_single_palette_reg
+
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+cirrus_enable_16k_granularity:
+ push ax
+ push dx
+ mov dx, #0x3ce
+ mov al, #0x0b
+ out dx, al
+ inc dx
+ in al, dx
+ or al, #0x20 ;; enable 16k
+ out dx, al
+ pop dx
+ pop ax
+ ret
+
+cirrus_switch_mode_setregs:
+csms_1:
+ mov ax, [bx]
+ cmp ax, #0xffff
+ jz csms_2
+ out dx, ax
+ add bx, #0x2
+ jmp csms_1
+csms_2:
+ ret
+
+cirrus_extbios_80h:
+ push dx
+ call cirrus_get_crtc
+ mov al, #0x27
+ out dx, al
+ inc dx
+ in al, dx
+ mov bx, #_cirrus_id_table
+c80h_1:
+ db 0x2e ;; cs:
+ mov ah, [bx]
+ cmp ah, al
+ jz c80h_2
+ cmp ah, #0xff
+ jz c80h_2
+ inc bx
+ inc bx
+ jmp c80h_1
+c80h_2:
+ db 0x2e ;; cs:
+ mov al, 0x1[bx]
+ pop dx
+ mov ah, #0x00
+ xor bx, bx
+ ret
+
+cirrus_extbios_81h:
+ mov ax, #0x100 ;; XXX
+ ret
+cirrus_extbios_82h:
+ push dx
+ call cirrus_get_crtc
+ xor ax, ax
+ mov al, #0x27
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x03
+ mov ah, #0xAF
+ pop dx
+ ret
+
+cirrus_extbios_85h:
+ push cx
+ push dx
+ mov dx, #0x3C4
+ mov al, #0x0f ;; get DRAM band width
+ out dx, al
+ inc dx
+ in al, dx
+ ;; al = 4 << bandwidth
+ mov cl, al
+ shr cl, #0x03
+ and cl, #0x03
+ cmp cl, #0x03
+ je c85h2
+ mov al, #0x04
+ shl al, cl
+ jmp c85h3
+c85h2:
+;; 4MB or 2MB
+ and al, #0x80
+ mov al, #0x20 ;; 2 MB
+ je c85h3
+ mov al, #0x40 ;; 4 MB
+c85h3:
+ pop dx
+ pop cx
+ ret
+
+cirrus_extbios_9Ah:
+ mov ax, #0x4060
+ mov cx, #0x1132
+ ret
+
+cirrus_extbios_A0h:
+ call cirrus_get_modeentry
+ mov ah, #0x01
+ sbb ah, #0x00
+ mov bx, cirrus_extbios_A0h_callback
+ mov si, #0xffff
+ mov di, bx
+ mov ds, bx
+ mov es, bx
+ ret
+
+cirrus_extbios_A0h_callback:
+ ;; fatal: not implemented yet
+ cli
+ hlt
+ retf
+
+cirrus_extbios_A1h:
+ mov bx, #0x0E00 ;; IBM 8512/8513, color
+ ret
+
+cirrus_extbios_A2h:
+ mov al, #0x07 ;; HSync 31.5 - 64.0 kHz
+ ret
+
+cirrus_extbios_AEh:
+ mov al, #0x01 ;; High Refresh 75Hz
+ ret
+
+cirrus_extbios_unimplemented:
+ ret
+
+cirrus_vesa_00h:
+ push ds
+ push si
+ mov bp, di
+ push es
+ pop ds
+ cld
+ mov ax, [di]
+ cmp ax, #0x4256 ;; VB
+ jnz cv00_1
+ mov ax, [di+2]
+ cmp ax, #0x3245 ;; E2
+ jnz cv00_1
+ ;; VBE2
+ lea di, 0x14[bp]
+ mov ax, #0x0100 ;; soft ver.
+ stosw
+ mov ax, # cirrus_vesa_vendorname
+ stosw
+ mov ax, cs
+ stosw
+ mov ax, # cirrus_vesa_productname
+ stosw
+ mov ax, cs
+ stosw
+ mov ax, # cirrus_vesa_productrevision
+ stosw
+ mov ax, cs
+ stosw
+cv00_1:
+ mov di, bp
+ mov ax, #0x4556 ;; VE
+ stosw
+ mov ax, #0x4153 ;; SA
+ stosw
+ mov ax, #0x0200 ;; v2.00
+ stosw
+ mov ax, # cirrus_vesa_oemname
+ stosw
+ mov ax, cs
+ stosw
+ xor ax, ax ;; caps
+ stosw
+ stosw
+ lea ax, 0x40[bp]
+ stosw
+ mov ax, es
+ stosw
+ call cirrus_extbios_85h ;; vram in 64k
+ mov ah, #0x00
+ stosw
+
+ push cs
+ pop ds
+ lea di, 0x40[bp]
+ mov si, #_cirrus_vesa_modelist
+cv00_2:
+ lodsw
+ stosw
+ add si, #2
+ cmp ax, #0xffff
+ jnz cv00_2
+
+ mov ax, #0x004F
+ mov di, bp
+ pop si
+ pop ds
+ ret
+
+cirrus_vesa_01h:
+ mov ax, cx
+ and ax, #0x3fff
+ call cirrus_vesamode_to_mode
+ cmp ax, #0xffff
+ jnz cirrus_vesa_01h_1
+ jmp cirrus_vesa_unimplemented
+cirrus_vesa_01h_1:
+ push ds
+ push si
+ push cx
+ push dx
+ push bx
+ mov bp, di
+ cld
+ push cs
+ pop ds
+ call cirrus_get_modeentry_nomask
+
+ push di
+ xor ax, ax
+ mov cx, #0x80
+ rep
+ stosw ;; clear buffer
+ pop di
+
+ mov ax, #0x003b ;; mode
+ stosw
+ mov ax, #0x0007 ;; attr
+ stosw
+ mov ax, #0x0010 ;; granularity =16K
+ stosw
+ mov ax, #0x0040 ;; size =64K
+ stosw
+ mov ax, #0xA000 ;; segment A
+ stosw
+ xor ax, ax ;; no segment B
+ stosw
+ mov ax, #cirrus_vesa_05h_farentry
+ stosw
+ mov ax, cs
+ stosw
+ call cirrus_get_line_offset_entry
+ stosw ;; bytes per scan line
+ mov ax, [si+2] ;; width
+ stosw
+ mov ax, [si+4] ;; height
+ stosw
+ mov ax, #0x08
+ stosb
+ mov ax, #0x10
+ stosb
+ mov al, #1 ;; count of planes
+ stosb
+ mov al, [si+6] ;; bpp
+ stosb
+ mov al, #0x1 ;; XXX number of banks
+ stosb
+ mov al, [si+17]
+ stosb ;; memory model
+ mov al, #0x0 ;; XXX size of bank in K
+ stosb
+ call cirrus_get_line_offset_entry
+ mov bx, [si+4]
+ mul bx ;; dx:ax=vramdisp
+ or ax, ax
+ jz cirrus_vesa_01h_3
+ inc dx
+cirrus_vesa_01h_3:
+ call cirrus_extbios_85h ;; al=vram in 64k
+ mov ah, #0x00
+ mov cx, dx
+ xor dx, dx
+ div cx
+ dec ax
+ stosb ;; number of image pages = vramtotal/vramdisp-1
+ mov al, #0x00
+ stosb
+
+ ;; v1.2+ stuffs
+ push si
+ add si, #18
+ movsw
+ movsw
+ movsw
+ movsw
+ pop si
+
+ mov ah, [si+16]
+ mov al, #0x0
+ sub ah, #9
+ rcl al, #1 ; bit 0=palette flag
+ stosb ;; direct screen mode info
+
+ ;; v2.0+ stuffs
+ ;; 32-bit LFB address
+ xor ax, ax
+ stosw
+ call cirrus_get_lfb_addr
+ stosw
+ or ax, ax
+ jz cirrus_vesa_01h_4
+ push di
+ mov di, bp
+ db 0x26 ;; es:
+ mov ax, [di]
+ or ax, #0x0080 ;; mode bit 7:LFB
+ stosw
+ pop di
+cirrus_vesa_01h_4:
+
+ xor ax, ax
+ stosw ; reserved
+ stosw ; reserved
+ stosw ; reserved
+
+ mov ax, #0x004F
+ mov di, bp
+ pop bx
+ pop dx
+ pop cx
+ pop si
+ pop ds
+
+ test cx, #0x4000 ;; LFB flag
+ jz cirrus_vesa_01h_5
+ push cx
+ db 0x26 ;; es:
+ mov cx, [di]
+ cmp cx, #0x0080 ;; is LFB supported?
+ jnz cirrus_vesa_01h_6
+ mov ax, #0x014F ;; error - no LFB
+cirrus_vesa_01h_6:
+ pop cx
+cirrus_vesa_01h_5:
+ ret
+
+cirrus_vesa_02h:
+ ;; XXX support CRTC registers
+ test bx, #0x3e00
+ jnz cirrus_vesa_02h_2 ;; unknown flags
+ mov ax, bx
+ and ax, #0x1ff ;; bit 8-0 mode
+ cmp ax, #0x100 ;; legacy VGA mode
+ jb cirrus_vesa_02h_legacy
+ call cirrus_vesamode_to_mode
+ cmp ax, #0xffff
+ jnz cirrus_vesa_02h_1
+cirrus_vesa_02h_2:
+ jmp cirrus_vesa_unimplemented
+cirrus_vesa_02h_legacy:
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ cmp byte ptr [cirrus_vesa_is_protected_mode], #0
+ jnz cirrus_vesa_02h_2
+#endif // CIRRUS_VESA3_PMINFO
+ int #0x10
+ mov ax, #0x004F
+ ret
+cirrus_vesa_02h_1:
+ push si
+ push ax
+ call cirrus_get_modeentry_nomask
+ call cirrus_switch_mode
+ test bx, #0x4000 ;; LFB
+ jnz cirrus_vesa_02h_3
+ call cirrus_enable_16k_granularity
+cirrus_vesa_02h_3:
+ test bx, #0x8000 ;; no clear
+ jnz cirrus_vesa_02h_4
+ push ax
+ xor ax,ax
+ call cirrus_clear_vram
+ pop ax
+cirrus_vesa_02h_4:
+ pop ax
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov si, [cirrus_vesa_sel0000_data]
+#else
+ xor si, si
+#endif
+ mov ds, si
+ mov [PM_BIOSMEM_CURRENT_MODE], al
+ mov [PM_BIOSMEM_VBE_MODE], bx
+ pop ds
+ pop si
+ mov ax, #0x004F
+ ret
+
+cirrus_vesa_03h:
+ push ds
+#ifdef CIRRUS_VESA3_PMINFO
+ db 0x2e ;; cs:
+ mov ax, [cirrus_vesa_sel0000_data]
+#else
+ xor ax, ax
+#endif
+ mov ds, ax
+ mov bx, # PM_BIOSMEM_VBE_MODE
+ mov ax, [bx]
+ mov bx, ax
+ test bx, bx
+ jnz cirrus_vesa_03h_1
+ mov bx, # PM_BIOSMEM_CURRENT_MODE
+ mov al, [bx]
+ mov bl, al
+ xor bh, bh
+cirrus_vesa_03h_1:
+ mov ax, #0x004f
+ pop ds
+ ret
+
+cirrus_vesa_05h_farentry:
+ call cirrus_vesa_05h
+ retf
+
+cirrus_vesa_05h:
+ cmp bl, #0x01
+ ja cirrus_vesa_05h_1
+ cmp bh, #0x00
+ jz cirrus_vesa_05h_setmempage
+ cmp bh, #0x01
+ jz cirrus_vesa_05h_getmempage
+cirrus_vesa_05h_1:
+ jmp cirrus_vesa_unimplemented
+cirrus_vesa_05h_setmempage:
+ or dh, dh ; address must be < 0x100
+ jnz cirrus_vesa_05h_1
+ push dx
+ mov al, bl ;; bl=bank number
+ add al, #0x09
+ mov ah, dl ;; dx=window address in granularity
+ mov dx, #0x3ce
+ out dx, ax
+ pop dx
+ mov ax, #0x004F
+ ret
+cirrus_vesa_05h_getmempage:
+ mov al, bl ;; bl=bank number
+ add al, #0x09
+ mov dx, #0x3ce
+ out dx, al
+ inc dx
+ in al, dx
+ xor dx, dx
+ mov dl, al ;; dx=window address in granularity
+ mov ax, #0x004F
+ ret
+
+cirrus_vesa_06h:
+ mov ax, cx
+ cmp bl, #0x01
+ je cirrus_vesa_06h_3
+ cmp bl, #0x02
+ je cirrus_vesa_06h_2
+ jb cirrus_vesa_06h_1
+ mov ax, #0x0100
+ ret
+cirrus_vesa_06h_1:
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ mov ax, cx
+ mul bx
+cirrus_vesa_06h_2:
+ call cirrus_set_line_offset
+cirrus_vesa_06h_3:
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ xor dx, dx
+ call cirrus_get_line_offset
+ push ax
+ div bx
+ mov cx, ax
+ pop bx
+ call cirrus_extbios_85h ;; al=vram in 64k
+ xor dx, dx
+ mov dl, al
+ xor ax, ax
+ div bx
+ mov dx, ax
+ mov ax, #0x004f
+ ret
+
+cirrus_vesa_07h:
+ cmp bl, #0x80
+ je cirrus_vesa_07h_1
+ cmp bl, #0x01
+ je cirrus_vesa_07h_2
+ jb cirrus_vesa_07h_1
+ mov ax, #0x0100
+ ret
+cirrus_vesa_07h_1:
+ push dx
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ mov ax, cx
+ mul bx
+ pop bx
+ push ax
+ call cirrus_get_line_offset
+ mul bx
+ pop bx
+ add ax, bx
+ jnc cirrus_vesa_07h_3
+ inc dx
+cirrus_vesa_07h_3:
+ push dx
+ and dx, #0x0003
+ mov bx, #0x04
+ div bx
+ pop dx
+ shr dx, #2
+ call cirrus_set_start_addr
+ mov ax, #0x004f
+ ret
+cirrus_vesa_07h_2:
+ call cirrus_get_start_addr
+ shl dx, #2
+ push dx
+ mov bx, #0x04
+ mul bx
+ pop bx
+ or dx, bx
+ push ax
+ call cirrus_get_line_offset
+ mov bx, ax
+ pop ax
+ div bx
+ push ax
+ push dx
+ call cirrus_get_bpp_bytes
+ mov bl, al
+ xor bh, bh
+ pop ax
+ xor dx, dx
+ div bx
+ mov cx, ax
+ pop dx
+ mov ax, #0x004f
+ ret
+
+cirrus_vesa_10h:
+ cmp bl, #0x00
+ jne cirrus_vesa_10h_01
+ mov bx, #0x0f30
+ mov ax, #0x004f
+ ret
+cirrus_vesa_10h_01:
+ cmp bl, #0x01
+ jne cirrus_vesa_10h_02
+ push dx
+ push ds
+ mov dx, #0x40
+ mov ds, dx
+ mov [0xb9], bh
+ pop ds
+ pop dx
+ mov ax, #0x004f
+ ret
+cirrus_vesa_10h_02:
+ cmp bl, #0x02
+ jne cirrus_vesa_unimplemented
+ push dx
+ push ds
+ mov dx, #0x40
+ mov ds, dx
+ mov bh, [0xb9]
+ pop ds
+ pop dx
+ mov ax, #0x004f
+ ret
+
+cirrus_vesa_unimplemented:
+ mov ax, #0x014F ;; not implemented
+ ret
+
+
+;; in ax:vesamode, out ax:cirrusmode
+cirrus_vesamode_to_mode:
+ push ds
+ push cx
+ push si
+ push cs
+ pop ds
+ mov cx, #0xffff
+ mov si, #_cirrus_vesa_modelist
+cvtm_1:
+ cmp [si],ax
+ jz cvtm_2
+ cmp [si],cx
+ jz cvtm_2
+ add si, #4
+ jmp cvtm_1
+cvtm_2:
+ mov ax,[si+2]
+ pop si
+ pop cx
+ pop ds
+ ret
+
+ ; cirrus_get_crtc
+ ;; NOTE - may be called in protected mode
+cirrus_get_crtc:
+ push ds
+ push ax
+ mov dx, #0x3cc
+ in al, dx
+ and al, #0x01
+ shl al, #5
+ mov dx, #0x3b4
+ add dl, al
+ pop ax
+ pop ds
+ ret
+
+;; in - al:mode, out - cflag:result, si:table, ax:destroyed
+cirrus_get_modeentry:
+ and al, #0x7f
+cirrus_get_modeentry_nomask:
+ mov si, #_cirrus_modes
+cgm_1:
+ db 0x2e ;; cs:
+ mov ah, [si]
+ cmp al, ah
+ jz cgm_2
+ cmp ah, #0xff
+ jz cgm_4
+ add si, # CIRRUS_MODE_SIZE
+ jmp cgm_1
+cgm_4:
+ xor si, si
+ stc ;; video mode is not supported
+ jmp cgm_3
+cgm_2:
+ clc ;; video mode is supported
+cgm_3:
+ ret
+
+ ; get LFB address
+ ; out - ax:LFB address (high 16 bit)
+ ;; NOTE - may be called in protected mode
+cirrus_get_lfb_addr:
+ push cx
+ push dx
+ push eax
+ xor cx, cx
+ mov dl, #0x00
+ call cirrus_pci_read
+ cmp ax, #0xffff
+ jz cirrus_get_lfb_addr_5
+ cirrus_get_lfb_addr_3:
+ mov dl, #0x00
+ call cirrus_pci_read
+ cmp ax, #0x1013 ;; cirrus
+ jz cirrus_get_lfb_addr_4
+ add cx, #0x8
+ cmp cx, #0x200 ;; search bus #0 and #1
+ jb cirrus_get_lfb_addr_3
+ cirrus_get_lfb_addr_5:
+ xor dx, dx ;; no LFB
+ jmp cirrus_get_lfb_addr_6
+ cirrus_get_lfb_addr_4:
+ mov dl, #0x10 ;; I/O space #0
+ call cirrus_pci_read
+ test ax, #0xfff1
+ jnz cirrus_get_lfb_addr_5
+ shr eax, #16
+ mov dx, ax ;; LFB address
+ cirrus_get_lfb_addr_6:
+ pop eax
+ mov ax, dx
+ pop dx
+ pop cx
+ ret
+
+cirrus_pci_read:
+ mov eax, #0x00800000
+ mov ax, cx
+ shl eax, #8
+ mov al, dl
+ mov dx, #0xcf8
+ out dx, eax
+ add dl, #4
+ in eax, dx
+ ret
+
+;; out - al:bytes per pixel
+cirrus_get_bpp_bytes:
+ push dx
+ mov dx, #0x03c4
+ mov al, #0x07
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x0e
+ cmp al, #0x06
+ jne cirrus_get_bpp_bytes_1
+ and al, #0x02
+cirrus_get_bpp_bytes_1:
+ shr al, #1
+ cmp al, #0x04
+ je cirrus_get_bpp_bytes_2
+ inc al
+cirrus_get_bpp_bytes_2:
+ pop dx
+ ret
+
+;; in - ax: new line offset
+cirrus_set_line_offset:
+ shr ax, #3
+ push ax
+ call cirrus_get_crtc
+ mov al, #0x13
+ out dx, al
+ inc dx
+ pop ax
+ out dx, al
+ dec dx
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ shl ah, #4
+ in al, dx
+ and al, #ef
+ or al, ah
+ out dx, al
+ ret
+
+;; out - ax: active line offset
+cirrus_get_line_offset:
+ push dx
+ push bx
+ call cirrus_get_crtc
+ mov al, #0x13
+ out dx, al
+ inc dx
+ in al, dx
+ mov bl, al
+ dec dx
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ in al, dx
+ mov ah, al
+ shr ah, #4
+ and ah, #0x01
+ mov al, bl
+ shl ax, #3
+ pop bx
+ pop dx
+ ret
+
+;; in - si: table
+;; out - ax: line offset for mode
+cirrus_get_line_offset_entry:
+ push bx
+ mov bx, [si+14] ;; crtc table
+ push bx
+offset_loop1:
+ mov ax, [bx]
+ cmp al, #0x13
+ je offset_found1
+ inc bx
+ inc bx
+ jnz offset_loop1
+offset_found1:
+ xor al, al
+ shr ax, #5
+ pop bx
+ push ax
+offset_loop2:
+ mov ax, [bx]
+ cmp al, #0x1b
+ je offset_found2
+ inc bx
+ inc bx
+ jnz offset_loop2
+offset_found2:
+ pop bx
+ and ax, #0x1000
+ shr ax, #1
+ or ax, bx
+ pop bx
+ ret
+
+;; in - new address in DX:AX
+cirrus_set_start_addr:
+ push bx
+ push dx
+ push ax
+ call cirrus_get_crtc
+ mov al, #0x0d
+ out dx, al
+ inc dx
+ pop ax
+ out dx, al
+ dec dx
+ mov al, #0x0c
+ out dx, al
+ inc dx
+ mov al, ah
+ out dx, al
+ dec dx
+ mov al, #0x1d
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x7f
+ pop bx
+ mov ah, bl
+ shl bl, #4
+ and bl, #0x80
+ or al, bl
+ out dx, al
+ dec dx
+ mov bl, ah
+ and ah, #0x01
+ shl bl, #1
+ and bl, #0x0c
+ or ah, bl
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0xf2
+ or al, ah
+ out dx, al
+ pop bx
+ ret
+
+;; out - current address in DX:AX
+cirrus_get_start_addr:
+ push bx
+ call cirrus_get_crtc
+ mov al, #0x0c
+ out dx, al
+ inc dx
+ in al, dx
+ mov ah, al
+ dec dx
+ mov al, #0x0d
+ out dx, al
+ inc dx
+ in al, dx
+ push ax
+ dec dx
+ mov al, #0x1b
+ out dx, al
+ inc dx
+ in al, dx
+ dec dx
+ mov bl, al
+ and al, #0x01
+ and bl, #0x0c
+ shr bl, #1
+ or bl, al
+ mov al, #0x1d
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0x80
+ shr al, #4
+ or bl, al
+ mov dl, bl
+ xor dh, dh
+ pop ax
+ pop bx
+ ret
+
+cirrus_clear_vram:
+ pusha
+ push es
+ mov si, ax
+
+ call cirrus_enable_16k_granularity
+ call cirrus_extbios_85h
+ shl al, #2
+ mov bl, al
+ xor ah,ah
+cirrus_clear_vram_1:
+ mov al, #0x09
+ mov dx, #0x3ce
+ out dx, ax
+ push ax
+ mov cx, #0xa000
+ mov es, cx
+ xor di, di
+ mov ax, si
+ mov cx, #8192
+ cld
+ rep
+ stosw
+ pop ax
+ inc ah
+ cmp ah, bl
+ jne cirrus_clear_vram_1
+
+ xor ah,ah
+ mov dx, #0x3ce
+ out dx, ax
+
+ pop es
+ popa
+ ret
+
+cirrus_extbios_handlers:
+ ;; 80h
+ dw cirrus_extbios_80h
+ dw cirrus_extbios_81h
+ dw cirrus_extbios_82h
+ dw cirrus_extbios_unimplemented
+ ;; 84h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_85h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 88h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 8Ch
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 90h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 94h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; 98h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_9Ah
+ dw cirrus_extbios_unimplemented
+ ;; 9Ch
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; A0h
+ dw cirrus_extbios_A0h
+ dw cirrus_extbios_A1h
+ dw cirrus_extbios_A2h
+ dw cirrus_extbios_unimplemented
+ ;; A4h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; A8h
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ ;; ACh
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_unimplemented
+ dw cirrus_extbios_AEh
+ dw cirrus_extbios_unimplemented
+
+cirrus_vesa_handlers:
+ ;; 00h
+ dw cirrus_vesa_00h
+ dw cirrus_vesa_01h
+ dw cirrus_vesa_02h
+ dw cirrus_vesa_03h
+ ;; 04h
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_05h
+ dw cirrus_vesa_06h
+ dw cirrus_vesa_07h
+ ;; 08h
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ ;; 0Ch
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ dw cirrus_vesa_unimplemented
+ ;; 10h
+ dw cirrus_vesa_10h
+
+
+ASM_END
+
+#ifdef CIRRUS_VESA3_PMINFO
+ASM_START
+cirrus_vesa_pminfo:
+ /* + 0 */
+ .byte 0x50,0x4d,0x49,0x44 ;; signature[4]
+ /* + 4 */
+ dw cirrus_vesa_pmbios_entry ;; entry_bios
+ dw cirrus_vesa_pmbios_init ;; entry_init
+ /* + 8 */
+cirrus_vesa_sel0000_data:
+ dw 0x0000 ;; sel_00000
+cirrus_vesa_selA000_data:
+ dw 0xA000 ;; sel_A0000
+ /* +12 */
+cirrus_vesa_selB000_data:
+ dw 0xB000 ;; sel_B0000
+cirrus_vesa_selB800_data:
+ dw 0xB800 ;; sel_B8000
+ /* +16 */
+cirrus_vesa_selC000_data:
+ dw 0xC000 ;; sel_C0000
+cirrus_vesa_is_protected_mode:
+ ;; protected mode flag and checksum
+ dw (~((0xf2 + (cirrus_vesa_pmbios_entry >> 8) + (cirrus_vesa_pmbios_entry) \
+ + (cirrus_vesa_pmbios_init >> 8) + (cirrus_vesa_pmbios_init)) & 0xff) << 8) + 0x01
+ASM_END
+#endif // CIRRUS_VESA3_PMINFO
+
+
+#ifdef CIRRUS_DEBUG
+static void cirrus_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+ Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+ if((GET_AH()!=0x0E)&&(GET_AH()!=0x02)&&(GET_AH()!=0x09)&&(AX!=0x4F05))
+ printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX);
+}
+#endif
diff --git a/kvm/vgabios/dataseghack b/kvm/vgabios/dataseghack
new file mode 100755
index 0000000..02a2d4c
--- /dev/null
+++ b/kvm/vgabios/dataseghack
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+awk \
+ 'BEGIN { }\
+ /^\.text/,/DATA_SEG_DEFS_HERE/ { print }\
+ END { }'\
+ $1 > temp.awk.1
+
+awk \
+ 'BEGIN { i = 0; last = "hello" }\
+ /BLOCK_STRINGS_BEGIN/,/^\.bss/ { if ( i > 1 ) { print last } last = $0; i = i + 1 }\
+ END { }'\
+ $1 > temp.awk.2
+
+awk \
+ 'BEGIN { }\
+ /DATA_SEG_DEFS_HERE/,/BLOCK_STRINGS_BEGIN/ { print }\
+ END { }'\
+ $1 > temp.awk.3
+
+cp $1 $1.orig
+cat temp.awk.1 temp.awk.2 temp.awk.3 | sed -e 's/^\.data//' -e 's/^\.bss//' -e 's/^\.text//' > $1
+/bin/rm -f temp.awk.1 temp.awk.2 temp.awk.3 $1.orig
diff --git a/kvm/vgabios/tests/lfbprof/Makefile b/kvm/vgabios/tests/lfbprof/Makefile
new file mode 100644
index 0000000..7c42e38
--- /dev/null
+++ b/kvm/vgabios/tests/lfbprof/Makefile
@@ -0,0 +1,5 @@
+# Very simple makefile for LFBPROF.C using Watcom C++ 10.0a with DOS4GW
+
+lfbprof.exe: lfbprof.c lfbprof.h
+ wcl386 -zq -s -d2 lfbprof.c
+
diff --git a/kvm/vgabios/tests/lfbprof/lfbprof.c b/kvm/vgabios/tests/lfbprof/lfbprof.c
new file mode 100644
index 0000000..df37452
--- /dev/null
+++ b/kvm/vgabios/tests/lfbprof/lfbprof.c
@@ -0,0 +1,594 @@
+/****************************************************************************
+*
+* VBE 2.0 Linear Framebuffer Profiler
+* By Kendall Bennett and Brian Hook
+*
+* Filename: LFBPROF.C
+* Language: ANSI C
+* Environment: Watcom C/C++ 10.0a with DOS4GW
+*
+* Description: Simple program to profile the speed of screen clearing
+* and full screen BitBlt operations using a VESA VBE 2.0
+* linear framebuffer from 32 bit protected mode.
+*
+* For simplicity, this program only supports 256 color
+* SuperVGA video modes that support a linear framebuffer.
+*
+*
+* 2002/02/18: Jeroen Janssen <japj at xs4all dot nl>
+* - fixed unsigned short for mode list (-1 != 0xffff otherwise)
+* - fixed LfbMapRealPointer macro mask problem (some modes were skipped)
+*
+****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <conio.h>
+#include <dos.h>
+#include "lfbprof.h"
+
+/*---------------------------- Global Variables ---------------------------*/
+
+int VESABuf_len = 1024; /* Length of VESABuf */
+int VESABuf_sel = 0; /* Selector for VESABuf */
+int VESABuf_rseg; /* Real mode segment of VESABuf */
+unsigned short modeList[50]; /* List of available VBE modes */
+float clearsPerSec; /* Number of clears per second */
+float clearsMbPerSec; /* Memory transfer for clears */
+float bitBltsPerSec; /* Number of BitBlt's per second */
+float bitBltsMbPerSec; /* Memory transfer for bitblt's */
+int xres,yres; /* Video mode resolution */
+int bytesperline; /* Bytes per scanline for mode */
+long imageSize; /* Length of the video image */
+char *LFBPtr; /* Pointer to linear framebuffer */
+
+/*------------------------- DPMI interface routines -----------------------*/
+
+void DPMI_allocRealSeg(int size,int *sel,int *r_seg)
+/****************************************************************************
+*
+* Function: DPMI_allocRealSeg
+* Parameters: size - Size of memory block to allocate
+* sel - Place to return protected mode selector
+* r_seg - Place to return real mode segment
+*
+* Description: Allocates a block of real mode memory using DPMI services.
+* This routine returns both a protected mode selector and
+* real mode segment for accessing the memory block.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 0x100; /* DPMI allocate DOS memory */
+ r.w.bx = (size + 0xF) >> 4; /* number of paragraphs */
+ int386(0x31, &r, &r);
+ if (r.w.cflag)
+ FatalError("DPMI_allocRealSeg failed!");
+ *sel = r.w.dx; /* Protected mode selector */
+ *r_seg = r.w.ax; /* Real mode segment */
+}
+
+void DPMI_freeRealSeg(unsigned sel)
+/****************************************************************************
+*
+* Function: DPMI_allocRealSeg
+* Parameters: sel - Protected mode selector of block to free
+*
+* Description: Frees a block of real mode memory.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 0x101; /* DPMI free DOS memory */
+ r.w.dx = sel; /* DX := selector from 0x100 */
+ int386(0x31, &r, &r);
+}
+
+typedef struct {
+ long edi;
+ long esi;
+ long ebp;
+ long reserved;
+ long ebx;
+ long edx;
+ long ecx;
+ long eax;
+ short flags;
+ short es,ds,fs,gs,ip,cs,sp,ss;
+ } _RMREGS;
+
+#define IN(reg) rmregs.e##reg = in->x.reg
+#define OUT(reg) out->x.reg = rmregs.e##reg
+
+int DPMI_int86(int intno, RMREGS *in, RMREGS *out)
+/****************************************************************************
+*
+* Function: DPMI_int86
+* Parameters: intno - Interrupt number to issue
+* in - Pointer to structure for input registers
+* out - Pointer to structure for output registers
+* Returns: Value returned by interrupt in AX
+*
+* Description: Issues a real mode interrupt using DPMI services.
+*
+****************************************************************************/
+{
+ _RMREGS rmregs;
+ union REGS r;
+ struct SREGS sr;
+
+ memset(&rmregs, 0, sizeof(rmregs));
+ IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
+
+ segread(&sr);
+ r.w.ax = 0x300; /* DPMI issue real interrupt */
+ r.h.bl = intno;
+ r.h.bh = 0;
+ r.w.cx = 0;
+ sr.es = sr.ds;
+ r.x.edi = (unsigned)&rmregs;
+ int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
+
+ OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
+ out->x.cflag = rmregs.flags & 0x1;
+ return out->x.ax;
+}
+
+int DPMI_int86x(int intno, RMREGS *in, RMREGS *out, RMSREGS *sregs)
+/****************************************************************************
+*
+* Function: DPMI_int86
+* Parameters: intno - Interrupt number to issue
+* in - Pointer to structure for input registers
+* out - Pointer to structure for output registers
+* sregs - Values to load into segment registers
+* Returns: Value returned by interrupt in AX
+*
+* Description: Issues a real mode interrupt using DPMI services.
+*
+****************************************************************************/
+{
+ _RMREGS rmregs;
+ union REGS r;
+ struct SREGS sr;
+
+ memset(&rmregs, 0, sizeof(rmregs));
+ IN(ax); IN(bx); IN(cx); IN(dx); IN(si); IN(di);
+ rmregs.es = sregs->es;
+ rmregs.ds = sregs->ds;
+
+ segread(&sr);
+ r.w.ax = 0x300; /* DPMI issue real interrupt */
+ r.h.bl = intno;
+ r.h.bh = 0;
+ r.w.cx = 0;
+ sr.es = sr.ds;
+ r.x.edi = (unsigned)&rmregs;
+ int386x(0x31, &r, &r, &sr); /* Issue the interrupt */
+
+ OUT(ax); OUT(bx); OUT(cx); OUT(dx); OUT(si); OUT(di);
+ sregs->es = rmregs.es;
+ sregs->cs = rmregs.cs;
+ sregs->ss = rmregs.ss;
+ sregs->ds = rmregs.ds;
+ out->x.cflag = rmregs.flags & 0x1;
+ return out->x.ax;
+}
+
+int DPMI_allocSelector(void)
+/****************************************************************************
+*
+* Function: DPMI_allocSelector
+* Returns: Newly allocated protected mode selector
+*
+* Description: Allocates a new protected mode selector using DPMI
+* services. This selector has a base address and limit of 0.
+*
+****************************************************************************/
+{
+ int sel;
+ union REGS r;
+
+ r.w.ax = 0; /* DPMI allocate selector */
+ r.w.cx = 1; /* Allocate a single selector */
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_allocSelector() failed!");
+ sel = r.w.ax;
+
+ r.w.ax = 9; /* DPMI set access rights */
+ r.w.bx = sel;
+ r.w.cx = 0x8092; /* 32 bit page granular */
+ int386(0x31, &r, &r);
+ return sel;
+}
+
+long DPMI_mapPhysicalToLinear(long physAddr,long limit)
+/****************************************************************************
+*
+* Function: DPMI_mapPhysicalToLinear
+* Parameters: physAddr - Physical memory address to map
+* limit - Length-1 of physical memory region to map
+* Returns: Starting linear address for mapped memory
+*
+* Description: Maps a section of physical memory into the linear address
+* space of a process using DPMI calls. Note that this linear
+* address cannot be used directly, but must be used as the
+* base address for a selector.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 0x800; /* DPMI map physical to linear */
+ r.w.bx = physAddr >> 16;
+ r.w.cx = physAddr & 0xFFFF;
+ r.w.si = limit >> 16;
+ r.w.di = limit & 0xFFFF;
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_mapPhysicalToLinear() failed!");
+ return ((long)r.w.bx << 16) + r.w.cx;
+}
+
+void DPMI_setSelectorBase(int sel,long linAddr)
+/****************************************************************************
+*
+* Function: DPMI_setSelectorBase
+* Parameters: sel - Selector to change base address for
+* linAddr - Linear address used for new base address
+*
+* Description: Sets the base address for the specified selector.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 7; /* DPMI set selector base address */
+ r.w.bx = sel;
+ r.w.cx = linAddr >> 16;
+ r.w.dx = linAddr & 0xFFFF;
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_setSelectorBase() failed!");
+}
+
+void DPMI_setSelectorLimit(int sel,long limit)
+/****************************************************************************
+*
+* Function: DPMI_setSelectorLimit
+* Parameters: sel - Selector to change limit for
+* limit - Limit-1 for the selector
+*
+* Description: Sets the memory limit for the specified selector.
+*
+****************************************************************************/
+{
+ union REGS r;
+
+ r.w.ax = 8; /* DPMI set selector limit */
+ r.w.bx = sel;
+ r.w.cx = limit >> 16;
+ r.w.dx = limit & 0xFFFF;
+ int386(0x31, &r, &r);
+ if (r.x.cflag)
+ FatalError("DPMI_setSelectorLimit() failed!");
+}
+
+/*-------------------------- VBE Interface routines -----------------------*/
+
+void FatalError(char *msg)
+{
+ fprintf(stderr,"%s\n", msg);
+ exit(1);
+}
+
+static void ExitVBEBuf(void)
+{
+ DPMI_freeRealSeg(VESABuf_sel);
+}
+
+void VBE_initRMBuf(void)
+/****************************************************************************
+*
+* Function: VBE_initRMBuf
+* Description: Initialises the VBE transfer buffer in real mode memory.
+* This routine is called by the VESAVBE module every time
+* it needs to use the transfer buffer, so we simply allocate
+* it once and then return.
+*
+****************************************************************************/
+{
+ if (!VESABuf_sel) {
+ DPMI_allocRealSeg(VESABuf_len, &VESABuf_sel, &VESABuf_rseg);
+ atexit(ExitVBEBuf);
+ }
+}
+
+void VBE_callESDI(RMREGS *regs, void *buffer, int size)
+/****************************************************************************
+*
+* Function: VBE_callESDI
+* Parameters: regs - Registers to load when calling VBE
+* buffer - Buffer to copy VBE info block to
+* size - Size of buffer to fill
+*
+* Description: Calls the VESA VBE and passes in a buffer for the VBE to
+* store information in, which is then copied into the users
+* buffer space. This works in protected mode as the buffer
+* passed to the VESA VBE is allocated in conventional
+* memory, and is then copied into the users memory block.
+*
+****************************************************************************/
+{
+ RMSREGS sregs;
+
+ VBE_initRMBuf();
+ sregs.es = VESABuf_rseg;
+ regs->x.di = 0;
+ _fmemcpy(MK_FP(VESABuf_sel,0),buffer,size);
+ DPMI_int86x(0x10, regs, regs, &sregs);
+ _fmemcpy(buffer,MK_FP(VESABuf_sel,0),size);
+}
+
+int VBE_detect(void)
+/****************************************************************************
+*
+* Function: VBE_detect
+* Parameters: vgaInfo - Place to store the VGA information block
+* Returns: VBE version number, or 0 if not detected.
+*
+* Description: Detects if a VESA VBE is out there and functioning
+* correctly. If we detect a VBE interface we return the
+* VGAInfoBlock returned by the VBE and the VBE version number.
+*
+****************************************************************************/
+{
+ RMREGS regs;
+ unsigned short *p1,*p2;
+ VBE_vgaInfo vgaInfo;
+
+ /* Put 'VBE2' into the signature area so that the VBE 2.0 BIOS knows
+ * that we have passed a 512 byte extended block to it, and wish
+ * the extended information to be filled in.
+ */
+ strncpy(vgaInfo.VESASignature,"VBE2",4);
+
+ /* Get the SuperVGA Information block */
+ regs.x.ax = 0x4F00;
+ VBE_callESDI(&regs, &vgaInfo, sizeof(VBE_vgaInfo));
+ if (regs.x.ax != 0x004F)
+ return 0;
+ if (strncmp(vgaInfo.VESASignature,"VESA",4) != 0)
+ return 0;
+
+ /* Now that we have detected a VBE interface, copy the list of available
+ * video modes into our local buffer. We *must* copy this mode list,
+ * since the VBE will build the mode list in the VBE_vgaInfo buffer
+ * that we have passed, so the next call to the VBE will trash the
+ * list of modes.
+ */
+ printf("videomodeptr %x\n",vgaInfo.VideoModePtr);
+ p1 = LfbMapRealPointer(vgaInfo.VideoModePtr);
+ p2 = modeList;
+ while (*p1 != -1)
+ {
+ printf("found mode %x\n",*p1);
+ *p2++ = *p1++;
+ }
+ *p2 = -1;
+ return vgaInfo.VESAVersion;
+}
+
+int VBE_getModeInfo(int mode,VBE_modeInfo *modeInfo)
+/****************************************************************************
+*
+* Function: VBE_getModeInfo
+* Parameters: mode - VBE mode to get information for
+* modeInfo - Place to store VBE mode information
+* Returns: 1 on success, 0 if function failed.
+*
+* Description: Obtains information about a specific video mode from the
+* VBE. You should use this function to find the video mode
+* you wish to set, as the new VBE 2.0 mode numbers may be
+* completely arbitrary.
+*
+****************************************************************************/
+{
+ RMREGS regs;
+
+ regs.x.ax = 0x4F01; /* Get mode information */
+ regs.x.cx = mode;
+ VBE_callESDI(&regs, modeInfo, sizeof(VBE_modeInfo));
+ if (regs.x.ax != 0x004F)
+ return 0;
+ if ((modeInfo->ModeAttributes & vbeMdAvailable) == 0)
+ return 0;
+ return 1;
+}
+
+void VBE_setVideoMode(int mode)
+/****************************************************************************
+*
+* Function: VBE_setVideoMode
+* Parameters: mode - VBE mode number to initialise
+*
+****************************************************************************/
+{
+ RMREGS regs;
+ regs.x.ax = 0x4F02;
+ regs.x.bx = mode;
+ DPMI_int86(0x10,&regs,&regs);
+}
+
+/*-------------------- Application specific routines ----------------------*/
+
+void *GetPtrToLFB(long physAddr)
+/****************************************************************************
+*
+* Function: GetPtrToLFB
+* Parameters: physAddr - Physical memory address of linear framebuffer
+* Returns: Far pointer to the linear framebuffer memory
+*
+****************************************************************************/
+{
+ int sel;
+ long linAddr,limit = (4096 * 1024) - 1;
+
+// sel = DPMI_allocSelector();
+ linAddr = DPMI_mapPhysicalToLinear(physAddr,limit);
+// DPMI_setSelectorBase(sel,linAddr);
+// DPMI_setSelectorLimit(sel,limit);
+// return MK_FP(sel,0);
+ return (void*)linAddr;
+}
+
+void AvailableModes(void)
+/****************************************************************************
+*
+* Function: AvailableModes
+*
+* Description: Display a list of available LFB mode resolutions.
+*
+****************************************************************************/
+{
+ unsigned short *p;
+ VBE_modeInfo modeInfo;
+
+ printf("Usage: LFBPROF <xres> <yres>\n\n");
+ printf("Available 256 color video modes:\n");
+ for (p = modeList; *p != -1; p++) {
+ if (VBE_getModeInfo(*p, &modeInfo)) {
+ /* Filter out only 8 bit linear framebuffer modes */
+ if ((modeInfo.ModeAttributes & vbeMdLinear) == 0)
+ continue;
+ if (modeInfo.MemoryModel != vbeMemPK
+ || modeInfo.BitsPerPixel != 8
+ || modeInfo.NumberOfPlanes != 1)
+ continue;
+ printf(" %4d x %4d %d bits per pixel\n",
+ modeInfo.XResolution, modeInfo.YResolution,
+ modeInfo.BitsPerPixel);
+ }
+ }
+ exit(1);
+}
+
+void InitGraphics(int x,int y)
+/****************************************************************************
+*
+* Function: InitGraphics
+* Parameters: x,y - Requested video mode resolution
+*
+* Description: Initialise the specified video mode. We search through
+* the list of available video modes for one that matches
+* the resolution and color depth are are looking for.
+*
+****************************************************************************/
+{
+ unsigned short *p;
+ VBE_modeInfo modeInfo;
+ printf("InitGraphics\n");
+
+ for (p = modeList; *p != -1; p++) {
+ if (VBE_getModeInfo(*p, &modeInfo)) {
+ /* Filter out only 8 bit linear framebuffer modes */
+ if ((modeInfo.ModeAttributes & vbeMdLinear) == 0)
+ continue;
+ if (modeInfo.MemoryModel != vbeMemPK
+ || modeInfo.BitsPerPixel != 8
+ || modeInfo.NumberOfPlanes != 1)
+ continue;
+ if (modeInfo.XResolution != x || modeInfo.YResolution != y)
+ continue;
+ xres = x;
+ yres = y;
+ bytesperline = modeInfo.BytesPerScanLine;
+ imageSize = bytesperline * yres;
+ VBE_setVideoMode(*p | vbeUseLFB);
+ LFBPtr = GetPtrToLFB(modeInfo.PhysBasePtr);
+ return;
+ }
+ }
+ printf("Valid video mode not found\n");
+ exit(1);
+}
+
+void EndGraphics(void)
+/****************************************************************************
+*
+* Function: EndGraphics
+*
+* Description: Restores text mode.
+*
+****************************************************************************/
+{
+ RMREGS regs;
+ printf("EndGraphics\n");
+ regs.x.ax = 0x3;
+ DPMI_int86(0x10, &regs, &regs);
+}
+
+void ProfileMode(void)
+/****************************************************************************
+*
+* Function: ProfileMode
+*
+* Description: Profiles framebuffer performance for simple screen clearing
+* and for copying from system memory to video memory (BitBlt).
+* This routine thrashes the CPU cache by cycling through
+* enough system memory buffers to invalidate the entire
+* CPU external cache before re-using the first memory buffer
+* again.
+*
+****************************************************************************/
+{
+ int i,numClears,numBlts,maxImages;
+ long startTicks,endTicks;
+ void *image[10],*dst;
+ printf("ProfileMode\n");
+
+ /* Profile screen clearing operation */
+ startTicks = LfbGetTicks();
+ numClears = 0;
+ while ((LfbGetTicks() - startTicks) < 182)
+ LfbMemset(LFBPtr,numClears++,imageSize);
+ endTicks = LfbGetTicks();
+ clearsPerSec = numClears / ((endTicks - startTicks) * 0.054925);
+ clearsMbPerSec = (clearsPerSec * imageSize) / 1048576.0;
+
+ /* Profile system memory to video memory copies */
+ maxImages = ((512 * 1024U) / imageSize) + 2;
+ for (i = 0; i < maxImages; i++) {
+ image[i] = malloc(imageSize);
+ if (image[i] == NULL)
+ FatalError("Not enough memory to profile BitBlt!");
+ memset(image[i],i+1,imageSize);
+ }
+ startTicks = LfbGetTicks();
+ numBlts = 0;
+ while ((LfbGetTicks() - startTicks) < 182)
+ LfbMemcpy(LFBPtr,image[numBlts++ % maxImages],imageSize);
+ endTicks = LfbGetTicks();
+ bitBltsPerSec = numBlts / ((endTicks - startTicks) * 0.054925);
+ bitBltsMbPerSec = (bitBltsPerSec * imageSize) / 1048576.0;
+}
+
+void main(int argc, char *argv[])
+{
+ if (VBE_detect() < 0x200)
+ FatalError("This program requires VBE 2.0; Please install UniVBE 5.1.");
+ if (argc != 3)
+ AvailableModes(); /* Display available modes */
+
+ InitGraphics(atoi(argv[1]),atoi(argv[2])); /* Start graphics */
+ ProfileMode(); /* Profile the video mode */
+ EndGraphics(); /* Restore text mode */
+
+ printf("Profiling results for %dx%d 8 bits per pixel.\n",xres,yres);
+ printf("%3.2f clears/s, %2.2f Mb/s\n", clearsPerSec, clearsMbPerSec);
+ printf("%3.2f bitBlt/s, %2.2f Mb/s\n", bitBltsPerSec, bitBltsMbPerSec);
+}
diff --git a/kvm/vgabios/tests/lfbprof/lfbprof.h b/kvm/vgabios/tests/lfbprof/lfbprof.h
new file mode 100644
index 0000000..bae0e09
--- /dev/null
+++ b/kvm/vgabios/tests/lfbprof/lfbprof.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+*
+* VBE 2.0 Linear Framebuffer Profiler
+* By Kendall Bennett and Brian Hook
+*
+* Filename: LFBPROF.H
+* Language: ANSI C
+* Environment: Watcom C/C++ 10.0a with DOS4GW
+*
+* Description: Header file for the LFBPROF.C progam.
+*
+****************************************************************************/
+
+#ifndef __LFBPROF_H
+#define __LFBPROF_H
+
+/*---------------------- Macros and type definitions ----------------------*/
+
+#pragma pack(1)
+
+/* SuperVGA information block */
+
+typedef struct {
+ char VESASignature[4]; /* 'VESA' 4 byte signature */
+ short VESAVersion; /* VBE version number */
+ long OemStringPtr; /* Pointer to OEM string */
+ long Capabilities; /* Capabilities of video card */
+ long VideoModePtr; /* Pointer to supported modes */
+ short TotalMemory; /* Number of 64kb memory blocks */
+
+ /* VBE 2.0 extensions */
+
+ short OemSoftwareRev; /* OEM Software revision number */
+ long OemVendorNamePtr; /* Pointer to Vendor Name string */
+ long OemProductNamePtr; /* Pointer to Product Name string */
+ long OemProductRevPtr; /* Pointer to Product Revision str */
+ char reserved[222]; /* Pad to 256 byte block size */
+ char OemDATA[256]; /* Scratch pad for OEM data */
+ } VBE_vgaInfo;
+
+/* SuperVGA mode information block */
+
+typedef struct {
+ short ModeAttributes; /* Mode attributes */
+ char WinAAttributes; /* Window A attributes */
+ char WinBAttributes; /* Window B attributes */
+ short WinGranularity; /* Window granularity in k */
+ short WinSize; /* Window size in k */
+ short WinASegment; /* Window A segment */
+ short WinBSegment; /* Window B segment */
+ long WinFuncPtr; /* Pointer to window function */
+ short BytesPerScanLine; /* Bytes per scanline */
+ short XResolution; /* Horizontal resolution */
+ short YResolution; /* Vertical resolution */
+ char XCharSize; /* Character cell width */
+ char YCharSize; /* Character cell height */
+ char NumberOfPlanes; /* Number of memory planes */
+ char BitsPerPixel; /* Bits per pixel */
+ char NumberOfBanks; /* Number of CGA style banks */
+ char MemoryModel; /* Memory model type */
+ char BankSize; /* Size of CGA style banks */
+ char NumberOfImagePages; /* Number of images pages */
+ char res1; /* Reserved */
+ char RedMaskSize; /* Size of direct color red mask */
+ char RedFieldPosition; /* Bit posn of lsb of red mask */
+ char GreenMaskSize; /* Size of direct color green mask */
+ char GreenFieldPosition; /* Bit posn of lsb of green mask */
+ char BlueMaskSize; /* Size of direct color blue mask */
+ char BlueFieldPosition; /* Bit posn of lsb of blue mask */
+ char RsvdMaskSize; /* Size of direct color res mask */
+ char RsvdFieldPosition; /* Bit posn of lsb of res mask */
+ char DirectColorModeInfo; /* Direct color mode attributes */
+
+ /* VBE 2.0 extensions */
+
+ long PhysBasePtr; /* Physical address for linear buf */
+ long OffScreenMemOffset; /* Pointer to start of offscreen mem*/
+ short OffScreenMemSize; /* Amount of offscreen mem in 1K's */
+ char res2[206]; /* Pad to 256 byte block size */
+ } VBE_modeInfo;
+
+#define vbeMemPK 4 /* Packed Pixel memory model */
+#define vbeUseLFB 0x4000 /* Enable linear framebuffer mode */
+
+/* Flags for the mode attributes returned by VBE_getModeInfo. If
+ * vbeMdNonBanked is set to 1 and vbeMdLinear is also set to 1, then only
+ * the linear framebuffer mode is available.
+ */
+
+#define vbeMdAvailable 0x0001 /* Video mode is available */
+#define vbeMdColorMode 0x0008 /* Mode is a color video mode */
+#define vbeMdGraphMode 0x0010 /* Mode is a graphics mode */
+#define vbeMdNonBanked 0x0040 /* Banked mode is not supported */
+#define vbeMdLinear 0x0080 /* Linear mode supported */
+
+/* Structures for issuing real mode interrupts with DPMI */
+
+struct _RMWORDREGS {
+ unsigned short ax, bx, cx, dx, si, di, cflag;
+ };
+
+struct _RMBYTEREGS {
+ unsigned char al, ah, bl, bh, cl, ch, dl, dh;
+ };
+
+typedef union {
+ struct _RMWORDREGS x;
+ struct _RMBYTEREGS h;
+ } RMREGS;
+
+typedef struct {
+ unsigned short es;
+ unsigned short cs;
+ unsigned short ss;
+ unsigned short ds;
+ } RMSREGS;
+
+/* Inline assembler block fill/move routines */
+
+void LfbMemset(void *p,int c,int n);
+#pragma aux LfbMemset = \
+ "shr ecx,2" \
+ "xor eax,eax" \
+ "mov al,bl" \
+ "shl ebx,8" \
+ "or ax,bx" \
+ "mov ebx,eax" \
+ "shl ebx,16" \
+ "or eax,ebx" \
+ "rep stosd" \
+ parm [edi] [ebx] [ecx];
+
+void LfbMemcpy(void *dst,void *src,int n);
+#pragma aux LfbMemcpy = \
+ "shr ecx,2" \
+ "rep movsd" \
+ parm [edi] [esi] [ecx];
+
+/* Map a real mode pointer into address space */
+
+#define LfbMapRealPointer(p) (void*)(((unsigned)((p) & 0xFFFF0000) >> 12) + ((p) & 0xFFFF))
+
+/* Get the current timer tick count */
+
+#define LfbGetTicks() *((long*)0x46C)
+
+#pragma pack()
+
+#endif /* __LFBPROF_H */
diff --git a/kvm/vgabios/tests/testbios.c b/kvm/vgabios/tests/testbios.c
new file mode 100644
index 0000000..99da5a6
--- /dev/null
+++ b/kvm/vgabios/tests/testbios.c
@@ -0,0 +1,353 @@
+/*
+ This is a little turbo C program that executes
+ several int10, and let you inspect the content
+ of the vgabios area
+
+ It is used to test the behavior of the vgabios
+*/
+
+#include <stdio.h>
+#include <dos.h>
+#include <conio.h>
+
+
+typedef unsigned char Bit8u;
+typedef unsigned short Bit16u;
+
+typedef struct
+{Bit8u initial;
+ Bit8u current;
+ Bit16u nbcols;
+ Bit16u regen;
+ Bit16u start;
+ Bit16u curpos[8];
+ Bit8u curtyp;
+ Bit8u curpage;
+ Bit16u crtc;
+ Bit16u msr;
+ Bit16u cgapal;
+ Bit8u nbrows;
+ Bit16u cheight;
+ Bit8u ctl;
+ Bit8u switches;
+ Bit8u modeset;
+ Bit8u dcc;
+ Bit16u vsseg;
+ Bit16u vsoffset;
+} BIOSAREA;
+
+void int10ax0003(struct REGPACK *regs)
+{
+ regs->r_ax=0x0003;
+ intr(0x10,regs);
+}
+
+void int10ax02(struct REGPACK *regs)
+{
+ regs->r_ax=0x0200;
+ regs->r_bx=0x0000;
+ regs->r_dx=0x1710;
+ intr(0x10,regs);
+ printf("We are now at 24/17");
+}
+
+void int10ax03(struct REGPACK *regs)
+{
+ regs->r_ax=0x0300;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+ printf("\nCursor is ax%04x cx%04x dx%04x\n",regs->r_ax,regs->r_cx,regs->r_dx);
+}
+
+void int10ax0501(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0e61;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+ printf("We are now on page 2");
+}
+
+void int10ax0602(struct REGPACK *regs)
+{
+ regs->r_ax=0x0602;
+ regs->r_bx=0x0700;
+ regs->r_cx=0x0101;
+ regs->r_dx=0x0a0a;
+ intr(0x10,regs);
+ printf("Scrolled 2 up");
+}
+
+void int10ax0702(struct REGPACK *regs)
+{
+ regs->r_ax=0x0702;
+ regs->r_bx=0x0700;
+ regs->r_cx=0x0101;
+ regs->r_dx=0x0a0a;
+ intr(0x10,regs);
+ printf("Scrolled 2 down");
+}
+
+void int10ax08(struct REGPACK *regs)
+{
+ regs->r_ax=0x0800;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+}
+
+void int10ax09(struct REGPACK *regs)
+{
+ char attr;
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ for(attr=0;attr<16;attr++)
+ {printf("%02x ",attr);
+ regs->r_ax=0x0961+attr;
+ regs->r_bx=0x0100+attr;
+ regs->r_cx=0x0016;
+ intr(0x10,regs);
+ printf("\n");
+ }
+}
+
+void int10ax0a(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0a62;
+ regs->r_bx=0x0101;
+ regs->r_cx=0x0016;
+ intr(0x10,regs);
+}
+
+void int10ax0f(struct REGPACK *regs)
+{
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x0f00;
+ intr(0x10,regs);
+}
+
+void int10ax1b(struct REGPACK *regs)
+{unsigned char table[64];
+ unsigned char far *ptable;
+ int i;
+
+ regs->r_ax=0x0501;
+ intr(0x10,regs);
+ regs->r_ax=0x1b00;
+ regs->r_bx=0x0000;
+ ptable=&table;
+ regs->r_es=FP_SEG(ptable);
+ regs->r_di=FP_OFF(ptable);
+ printf("Read state info in %04x:%04x\n",regs->r_es,regs->r_di);
+ intr(0x10,regs);
+
+ for(i=0;i<64;i++)
+ {if(i%16==0)printf("\n%02x ",i);
+ printf("%02x ",table[i]);
+ }
+ printf("\n");
+}
+
+static unsigned char var[64];
+
+void int10ax13(struct REGPACK *regs)
+{unsigned char far *pvar;
+
+ pvar=&var;
+
+ regs->r_ax=0x1300;
+ regs->r_bx=0x000b;
+ regs->r_dx=0x1010;
+ regs->r_cx=0x0002;
+ regs->r_es=FP_SEG(pvar);
+ regs->r_bp=FP_OFF(pvar);
+ pokeb(regs->r_es,regs->r_bp,'t');
+ pokeb(regs->r_es,regs->r_bp+1,'b');
+ printf("Writing from %04x:%04x\n",regs->r_es,regs->r_bp);
+ intr(0x10,regs);
+
+}
+
+void switch_50(struct REGPACK *regs)
+{
+ regs->r_ax=0x1202;
+ regs->r_bx=0x3000;
+ intr(0x10,regs);
+ regs->r_ax=0x0003;
+ intr(0x10,regs);
+ regs->r_ax=0x1112;
+ regs->r_bx=0x0000;
+ intr(0x10,regs);
+}
+
+char exec_function(struct REGPACK *regs)
+{char c;
+
+ printf("--- Functions --------------------\n");
+ printf("a. int10 ax0003\t");
+ printf("b. int10 ax02\t");
+ printf("c. int10 ax03\t");
+ printf("d. int10 ax0501\n");
+ printf("e. int10 ax0602\t");
+ printf("f. int10 ax0702\t");
+ printf("g. int10 ax08\t");
+ printf("h. int10 ax09\t");
+ printf("i. int10 ax0a\n");
+ printf("j. int10 ax0f\t");
+ printf("k. int10 ax1b\t");
+ printf("l. int10 ax13\n");
+ printf("q. Quit\t");
+ printf("r. switch to 50 lines\n");
+ c=getche();
+
+ switch(c)
+ {case 'a':
+ int10ax0003(regs);
+ break;
+ case 'b':
+ int10ax02(regs);
+ break;
+ case 'c':
+ int10ax03(regs);
+ break;
+ case 'd':
+ int10ax0501(regs);
+ break;
+ case 'e':
+ int10ax0602(regs);
+ break;
+ case 'f':
+ int10ax0702(regs);
+ break;
+ case 'g':
+ int10ax08(regs);
+ break;
+ case 'h':
+ int10ax09(regs);
+ break;
+ case 'i':
+ int10ax0a(regs);
+ break;
+ case 'j':
+ int10ax0f(regs);
+ break;
+ case 'k':
+ int10ax1b(regs);
+ break;
+ case 'l':
+ int10ax13(regs);
+ break;
+ case 'q':
+ break;
+ case 'r':
+ switch_50(regs);
+ break;
+ default:
+ printf("No such function!\n");
+ }
+
+ if(c=='q')return 1;
+ while(kbhit()==0);
+ c=getch();
+
+ return 0;
+}
+
+void read_bios_area(BIOSAREA *biosarea)
+{
+ biosarea->initial=peekb(0x40,0x10);
+ biosarea->current=peekb(0x40,0x49);
+ biosarea->nbcols=peek(0x40,0x4a);
+ biosarea->regen=peek(0x40,0x4c);
+ biosarea->start=peek(0x40,0x4e);
+ biosarea->curpos[0]=peek(0x40,0x50);
+ biosarea->curpos[1]=peek(0x40,0x52);
+ biosarea->curpos[2]=peek(0x40,0x54);
+ biosarea->curpos[3]=peek(0x40,0x56);
+ biosarea->curpos[4]=peek(0x40,0x58);
+ biosarea->curpos[5]=peek(0x40,0x5a);
+ biosarea->curpos[6]=peek(0x40,0x5c);
+ biosarea->curpos[7]=peek(0x40,0x5e);
+ biosarea->curtyp=peek(0x40,0x60);
+ biosarea->curpage=peekb(0x40,0x62);
+ biosarea->crtc=peek(0x40,0x63);
+ biosarea->msr=peekb(0x40,0x65);
+ biosarea->cgapal=peekb(0x40,0x66);
+ biosarea->nbrows=peekb(0x40,0x84);
+ biosarea->cheight=peek(0x40,0x85);
+ biosarea->ctl=peekb(0x40,0x87);
+ biosarea->switches=peekb(0x40,0x88);
+ biosarea->modeset=peekb(0x40,0x89);
+ biosarea->dcc=peekb(0x40,0x8a);
+ biosarea->vsseg=peek(0x40,0xa8);
+ biosarea->vsoffset=peek(0x40,0xaa);
+}
+
+void show_bios_area(BIOSAREA *biosarea)
+{
+ printf("--- BIOS area --------------------\n");
+ printf("initial : %02x\t",biosarea->initial);
+ printf("current : %02x\t",biosarea->current);
+ printf("nbcols : %04x\t",biosarea->nbcols);
+ printf("regen : %04x\t",biosarea->regen);
+ printf("start : %04x\n",biosarea->start);
+ printf("curpos : %04x %04x %04x %04x %04x %04x %04x %04x\n",
+ biosarea->curpos[0], biosarea->curpos[1], biosarea->curpos[2], biosarea->curpos[3],
+ biosarea->curpos[4], biosarea->curpos[5], biosarea->curpos[6], biosarea->curpos[7]);
+ printf("curtyp : %04x\t",biosarea->curtyp);
+ printf("curpage : %02x\t",biosarea->curpage);
+ printf("crtc : %04x\t",biosarea->crtc);
+ printf("msr : %04x\n",biosarea->msr);
+ printf("cgapal : %04x\t",biosarea->cgapal);
+ printf("nbrows-1: %02x\t",biosarea->nbrows);
+ printf("cheight : %04x\t",biosarea->cheight);
+ printf("ctl : %02x\n",biosarea->ctl);
+ printf("switches: %02x\t",biosarea->switches);
+ printf("modeset : %02x\t",biosarea->modeset);
+ printf("dcc : %02x\t",biosarea->dcc);
+ printf("vs : %04x:%04x\n",biosarea->vsseg,biosarea->vsoffset);
+}
+
+void show_regs(struct REGPACK *regs)
+{
+ printf("--- Registers --------------------\n");
+ printf("ax %04x\t",regs->r_ax);
+ printf("bx %04x\t",regs->r_bx);
+ printf("cx %04x\t",regs->r_cx);
+ printf("dx %04x\t",regs->r_dx);
+ printf("ds %04x\t",regs->r_ds);
+ printf("si %04x\t",regs->r_si);
+ printf("es %04x\t",regs->r_es);
+ printf("di %04x\n",regs->r_di);
+}
+
+void reset_videomode()
+{
+ struct REGPACK regs;
+
+ regs.r_ax=0x0003;
+ intr(0x10,&regs);
+}
+
+void main()
+{
+
+ BIOSAREA biosarea;
+ struct REGPACK regs;
+
+ directvideo=0;
+
+ while(1)
+ {
+ read_bios_area(&biosarea);
+
+ reset_videomode();
+ show_bios_area(&biosarea);
+ show_regs(&regs);
+
+ if(exec_function(&regs)!=0)break;
+ }
+}
diff --git a/kvm/vgabios/vbe.c b/kvm/vgabios/vbe.c
new file mode 100644
index 0000000..6173ca0
--- /dev/null
+++ b/kvm/vgabios/vbe.c
@@ -0,0 +1,1432 @@
+// ============================================================================================
+//
+// Copyright (C) 2002 Jeroen Janssen
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// ============================================================================================
+//
+// This VBE is part of the VGA Bios specific to the plex86/bochs Emulated VGA card.
+// You can NOT drive any physical vga card with it.
+//
+// ============================================================================================
+//
+// This VBE Bios is based on information taken from :
+// - VESA BIOS EXTENSION (VBE) Core Functions Standard Version 3.0 located at www.vesa.org
+//
+// ============================================================================================
+
+
+// defines available
+
+// disable VESA/VBE2 check in vbe info
+//#define VBE2_NO_VESA_CHECK
+
+
+#include "vbe.h"
+#include "vbetables.h"
+
+#define VBE_TOTAL_VIDEO_MEMORY_DIV_64K (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB*1024/64)
+
+// The current OEM Software Revision of this VBE Bios
+#define VBE_OEM_SOFTWARE_REV 0x0002;
+
+extern char vbebios_copyright;
+extern char vbebios_vendor_name;
+extern char vbebios_product_name;
+extern char vbebios_product_revision;
+
+ASM_START
+// FIXME: 'merge' these (c) etc strings with the vgabios.c strings?
+_vbebios_copyright:
+.ascii "Bochs/Plex86 VBE(C) 2003 http://savannah.nongnu.org/projects/vgabios/"
+.byte 0x00
+
+_vbebios_vendor_name:
+.ascii "Bochs/Plex86 Developers"
+.byte 0x00
+
+_vbebios_product_name:
+.ascii "Bochs/Plex86 VBE Adapter"
+.byte 0x00
+
+_vbebios_product_revision:
+.ascii "$Id$"
+.byte 0x00
+
+_vbebios_info_string:
+.ascii "Bochs VBE Display Adapter enabled"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+_no_vbebios_info_string:
+.ascii "NO Bochs VBE Support available!"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+msg_vbe_init:
+.ascii "VBE Bios $Id$"
+.byte 0x0a,0x0d, 0x00
+#endif
+
+ .align 2
+vesa_pm_start:
+ dw vesa_pm_set_window - vesa_pm_start
+ dw vesa_pm_set_display_start - vesa_pm_start
+ dw vesa_pm_unimplemented - vesa_pm_start
+ dw vesa_pm_io_ports_table - vesa_pm_start
+vesa_pm_io_ports_table:
+ dw VBE_DISPI_IOPORT_INDEX
+ dw VBE_DISPI_IOPORT_INDEX + 1
+ dw VBE_DISPI_IOPORT_DATA
+ dw VBE_DISPI_IOPORT_DATA + 1
+ dw 0xffff
+ dw 0xffff
+
+ USE32
+vesa_pm_set_window:
+ cmp bx, #0x00
+ je vesa_pm_set_display_window1
+ mov ax, #0x0100
+ ret
+vesa_pm_set_display_window1:
+ mov ax, dx
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BANK
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ in ax, dx
+ pop dx
+ cmp dx, ax
+ jne illegal_window
+ mov ax, #0x004f
+ ret
+illegal_window:
+ mov ax, #0x014f
+ ret
+
+vesa_pm_set_display_start:
+ cmp bl, #0x80
+ je vesa_pm_set_display_start1
+ cmp bl, #0x00
+ je vesa_pm_set_display_start1
+ mov ax, #0x0100
+ ret
+vesa_pm_set_display_start1:
+; convert offset to (X, Y) coordinate
+; (would be simpler to change Bochs VBE API...)
+ push eax
+ push ecx
+ push edx
+ push esi
+ push edi
+ shl edx, #16
+ and ecx, #0xffff
+ or ecx, edx
+ shl ecx, #2
+ mov eax, ecx
+
+ push eax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ movzx ecx, ax
+
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ movzx esi, ax
+ pop eax
+
+ cmp esi, #4
+ jz bpp4_mode
+ add esi, #7
+ shr esi, #3
+ imul ecx, esi
+ xor edx, edx
+ div ecx
+ mov edi, eax
+ mov eax, edx
+ xor edx, edx
+ div esi
+ jmp set_xy_regs
+
+bpp4_mode:
+ shr ecx, #1
+ xor edx, edx
+ div ecx
+ mov edi, eax
+ mov eax, edx
+ shl eax, #1
+
+set_xy_regs:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_X_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+
+ mov ax, di
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop eax
+ mov ax, #0x004f
+ ret
+
+vesa_pm_unimplemented:
+ mov ax, #0x014f
+ ret
+ USE16
+vesa_pm_end:
+
+; DISPI ioport functions
+
+dispi_get_id:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ID
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+dispi_set_id:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ID
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+ASM_END
+
+static void dispi_set_xres(xres)
+ Bit16u xres;
+{
+ASM_START
+ push bp
+ mov bp, sp
+ push ax
+ push dx
+
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_XRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ mov ax, 4[bp] ; xres
+ out dx, ax
+
+ pop dx
+ pop ax
+ pop bp
+ASM_END
+}
+
+static void dispi_set_yres(yres)
+ Bit16u yres;
+{
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_YRES);
+ outw(VBE_DISPI_IOPORT_DATA,yres);
+}
+
+static void dispi_set_bpp(bpp)
+ Bit16u bpp;
+{
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_BPP);
+ outw(VBE_DISPI_IOPORT_DATA,bpp);
+}
+
+ASM_START
+; AL = bits per pixel / AH = bytes per pixel
+dispi_get_bpp:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ mov ah, al
+ shr ah, 3
+ test al, #0x07
+ jz get_bpp_noinc
+ inc ah
+get_bpp_noinc:
+ pop dx
+ ret
+
+; get display capabilities
+
+_dispi_get_max_xres:
+ push dx
+ push bx
+ call dispi_get_enable
+ mov bx, ax
+ or ax, # VBE_DISPI_GETCAPS
+ call _dispi_set_enable
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_XRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ push ax
+ mov ax, bx
+ call _dispi_set_enable
+ pop ax
+ pop bx
+ pop dx
+ ret
+
+_dispi_get_max_bpp:
+ push dx
+ push bx
+ call dispi_get_enable
+ mov bx, ax
+ or ax, # VBE_DISPI_GETCAPS
+ call _dispi_set_enable
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ push ax
+ mov ax, bx
+ call _dispi_set_enable
+ pop ax
+ pop bx
+ pop dx
+ ret
+
+_dispi_set_enable:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ENABLE
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_enable:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_ENABLE
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+_dispi_set_bank:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BANK
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_bank:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BANK
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+ASM_END
+
+static void dispi_set_bank_farcall()
+{
+ASM_START
+ cmp bx,#0x0100
+ je dispi_set_bank_farcall_get
+ or bx,bx
+ jnz dispi_set_bank_farcall_error
+ mov ax,dx
+ push dx
+ push ax
+ mov ax,# VBE_DISPI_INDEX_BANK
+ mov dx,# VBE_DISPI_IOPORT_INDEX
+ out dx,ax
+ pop ax
+ mov dx,# VBE_DISPI_IOPORT_DATA
+ out dx,ax
+ in ax,dx
+ pop dx
+ cmp dx,ax
+ jne dispi_set_bank_farcall_error
+ mov ax, #0x004f
+ retf
+dispi_set_bank_farcall_get:
+ mov ax,# VBE_DISPI_INDEX_BANK
+ mov dx,# VBE_DISPI_IOPORT_INDEX
+ out dx,ax
+ mov dx,# VBE_DISPI_IOPORT_DATA
+ in ax,dx
+ mov dx,ax
+ retf
+dispi_set_bank_farcall_error:
+ mov ax,#0x014F
+ retf
+ASM_END
+}
+
+ASM_START
+dispi_set_x_offset:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_X_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_x_offset:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_X_OFFSET
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+dispi_set_y_offset:
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_y_offset:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_Y_OFFSET
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+vga_set_virt_width:
+ push ax
+ push bx
+ push dx
+ mov bx, ax
+ call dispi_get_bpp
+ cmp al, #0x04
+ ja set_width_svga
+ shr bx, #1
+set_width_svga:
+ shr bx, #3
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ah, bl
+ mov al, #0x13
+ out dx, ax
+ pop dx
+ pop bx
+ pop ax
+ ret
+
+dispi_set_virt_width:
+ call vga_set_virt_width
+ push dx
+ push ax
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
+ out dx, ax
+ pop ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ out dx, ax
+ pop dx
+ ret
+
+dispi_get_virt_width:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_WIDTH
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+dispi_get_virt_height:
+ push dx
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_VIRT_HEIGHT
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ pop dx
+ ret
+
+_vga_compat_setup:
+ push ax
+ push dx
+
+ ; set CRT X resolution
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_XRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ push ax
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ax, #0x0011
+ out dx, ax
+ pop ax
+ push ax
+ shr ax, #3
+ dec ax
+ mov ah, al
+ mov al, #0x01
+ out dx, ax
+ pop ax
+ call vga_set_virt_width
+
+ ; set CRT Y resolution
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_YRES
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ dec ax
+ push ax
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ah, al
+ mov al, #0x12
+ out dx, ax
+ pop ax
+ mov al, #0x07
+ out dx, al
+ inc dx
+ in al, dx
+ and al, #0xbd
+ test ah, #0x01
+ jz bit8_clear
+ or al, #0x02
+bit8_clear:
+ test ah, #0x02
+ jz bit9_clear
+ or al, #0x40
+bit9_clear:
+ out dx, al
+
+ ; other settings
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov ax, #0x0009
+ out dx, ax
+ mov al, #0x17
+ out dx, al
+ mov dx, # VGAREG_VGA_CRTC_DATA
+ in al, dx
+ or al, #0x03
+ out dx, al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ or al, #0x01
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0x0506
+ out dx, ax
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0f02
+ out dx, ax
+
+ ; settings for >= 8bpp
+ mov dx, # VBE_DISPI_IOPORT_INDEX
+ mov ax, # VBE_DISPI_INDEX_BPP
+ out dx, ax
+ mov dx, # VBE_DISPI_IOPORT_DATA
+ in ax, dx
+ cmp al, #0x08
+ jb vga_compat_end
+ mov dx, # VGAREG_VGA_CRTC_ADDRESS
+ mov al, #0x14
+ out dx, al
+ mov dx, # VGAREG_VGA_CRTC_DATA
+ in al, dx
+ or al, #0x40
+ out dx, al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ or al, #0x40
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov al, #0x04
+ out dx, al
+ mov dx, # VGAREG_SEQU_DATA
+ in al, dx
+ or al, #0x08
+ out dx, al
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov al, #0x05
+ out dx, al
+ mov dx, # VGAREG_GRDC_DATA
+ in al, dx
+ and al, #0x9f
+ or al, #0x40
+ out dx, al
+
+vga_compat_end:
+ pop dx
+ pop ax
+ASM_END
+
+
+// ModeInfo helper function
+static ModeInfoListItem* mode_info_find_mode(mode, using_lfb)
+ Bit16u mode; Boolean using_lfb;
+{
+ ModeInfoListItem *cur_info=&mode_info_list;
+
+ while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST)
+ {
+ if (cur_info->mode == mode)
+ {
+ if (!using_lfb)
+ {
+ return cur_info;
+ }
+ else if (cur_info->info.ModeAttributes & VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE)
+ {
+ return cur_info;
+ }
+ else
+ {
+ cur_info++;
+ }
+ }
+ else
+ {
+ cur_info++;
+ }
+ }
+
+ return 0;
+}
+
+ASM_START
+
+; Has VBE display - Returns true if VBE display detected
+
+_vbe_has_vbe_display:
+ push ds
+ push bx
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_VBE_FLAG
+ mov al, [bx]
+ and al, #0x01
+ xor ah, ah
+ pop bx
+ pop ds
+ ret
+
+; VBE Init - Initialise the Vesa Bios Extension Code
+; This function does a sanity check on the host side display code interface.
+
+vbe_init:
+ mov ax, # VBE_DISPI_ID0
+ call dispi_set_id
+ call dispi_get_id
+ cmp ax, # VBE_DISPI_ID0
+ jne no_vbe_interface
+ push ds
+ push bx
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_VBE_FLAG
+ mov al, #0x01
+ mov [bx], al
+ pop bx
+ pop ds
+ mov ax, # VBE_DISPI_ID4
+ call dispi_set_id
+no_vbe_interface:
+#if defined(USE_BX_INFO) || defined(DEBUG)
+ mov bx, #msg_vbe_init
+ push bx
+ call _printf
+ inc sp
+ inc sp
+#endif
+ ret
+
+; VBE Display Info - Display information on screen about the VBE
+
+vbe_display_info:
+ call _vbe_has_vbe_display
+ test ax, ax
+ jz no_vbe_flag
+ mov ax, #0xc000
+ mov ds, ax
+ mov si, #_vbebios_info_string
+ jmp _display_string
+no_vbe_flag:
+ mov ax, #0xc000
+ mov ds, ax
+ mov si, #_no_vbebios_info_string
+ jmp _display_string
+ASM_END
+
+/** Function 00h - Return VBE Controller Information
+ *
+ * Input:
+ * AX = 4F00h
+ * ES:DI = Pointer to buffer in which to place VbeInfoBlock structure
+ * (VbeSignature should be VBE2 when VBE 2.0 information is desired and
+ * the info block is 512 bytes in size)
+ * Output:
+ * AX = VBE Return Status
+ *
+ */
+void vbe_biosfn_return_controller_information(AX, ES, DI)
+Bit16u *AX;Bit16u ES;Bit16u DI;
+{
+ Bit16u ss=get_SS();
+ VbeInfoBlock vbe_info_block;
+ Bit16u status;
+ Bit16u result;
+ Bit16u vbe2_info;
+ Bit16u cur_mode=0;
+ Bit16u cur_ptr=34;
+ ModeInfoListItem *cur_info=&mode_info_list;
+
+ status = read_word(ss, AX);
+
+#ifdef DEBUG
+ printf("VBE vbe_biosfn_return_vbe_info ES%x DI%x AX%x\n",ES,DI,status);
+#endif
+
+ vbe2_info = 0;
+#ifdef VBE2_NO_VESA_CHECK
+#else
+ // get vbe_info_block into local variable
+ memcpyb(ss, &vbe_info_block, ES, DI, sizeof(vbe_info_block));
+
+ // check for VBE2 signature
+ if (((vbe_info_block.VbeSignature[0] == 'V') &&
+ (vbe_info_block.VbeSignature[1] == 'B') &&
+ (vbe_info_block.VbeSignature[2] == 'E') &&
+ (vbe_info_block.VbeSignature[3] == '2')) ||
+
+ ((vbe_info_block.VbeSignature[0] == 'V') &&
+ (vbe_info_block.VbeSignature[1] == 'E') &&
+ (vbe_info_block.VbeSignature[2] == 'S') &&
+ (vbe_info_block.VbeSignature[3] == 'A')) )
+ {
+ vbe2_info = 1;
+#ifdef DEBUG
+ printf("VBE correct VESA/VBE2 signature found\n");
+#endif
+ }
+#endif
+
+ // VBE Signature
+ vbe_info_block.VbeSignature[0] = 'V';
+ vbe_info_block.VbeSignature[1] = 'E';
+ vbe_info_block.VbeSignature[2] = 'S';
+ vbe_info_block.VbeSignature[3] = 'A';
+
+ // VBE Version supported
+ vbe_info_block.VbeVersion = 0x0200;
+
+ // OEM String
+ vbe_info_block.OemStringPtr_Seg = 0xc000;
+ vbe_info_block.OemStringPtr_Off = &vbebios_copyright;
+
+ // Capabilities
+ vbe_info_block.Capabilities[0] = VBE_CAPABILITY_8BIT_DAC;
+ vbe_info_block.Capabilities[1] = 0;
+ vbe_info_block.Capabilities[2] = 0;
+ vbe_info_block.Capabilities[3] = 0;
+
+ // VBE Video Mode Pointer (dynamicly generated from the mode_info_list)
+ vbe_info_block.VideoModePtr_Seg= ES ;
+ vbe_info_block.VideoModePtr_Off= DI + 34;
+
+ // VBE Total Memory (in 64b blocks)
+ vbe_info_block.TotalMemory = VBE_TOTAL_VIDEO_MEMORY_DIV_64K;
+
+ if (vbe2_info)
+ {
+ // OEM Stuff
+ vbe_info_block.OemSoftwareRev = VBE_OEM_SOFTWARE_REV;
+ vbe_info_block.OemVendorNamePtr_Seg = 0xc000;
+ vbe_info_block.OemVendorNamePtr_Off = &vbebios_vendor_name;
+ vbe_info_block.OemProductNamePtr_Seg = 0xc000;
+ vbe_info_block.OemProductNamePtr_Off = &vbebios_product_name;
+ vbe_info_block.OemProductRevPtr_Seg = 0xc000;
+ vbe_info_block.OemProductRevPtr_Off = &vbebios_product_revision;
+
+ // copy updates in vbe_info_block back
+ memcpyb(ES, DI, ss, &vbe_info_block, sizeof(vbe_info_block));
+ }
+ else
+ {
+ // copy updates in vbe_info_block back (VBE 1.x compatibility)
+ memcpyb(ES, DI, ss, &vbe_info_block, 256);
+ }
+
+ do
+ {
+ if ((cur_info->info.XResolution <= dispi_get_max_xres()) &&
+ (cur_info->info.BitsPerPixel <= dispi_get_max_bpp())) {
+#ifdef DEBUG
+ printf("VBE found mode %x => %x\n", cur_info->mode,cur_mode);
+#endif
+ write_word(ES, DI + cur_ptr, cur_info->mode);
+ cur_mode++;
+ cur_ptr+=2;
+ } else {
+#ifdef DEBUG
+ printf("VBE mode %x (xres=%x / bpp=%02x) not supported by display\n", cur_info->mode,cur_info->info.XResolution,cur_info->info.BitsPerPixel);
+#endif
+ }
+ cur_info++;
+ } while (cur_info->mode != VBE_VESA_MODE_END_OF_LIST);
+
+ // Add vesa mode list terminator
+ write_word(ES, DI + cur_ptr, cur_info->mode);
+
+ result = 0x4f;
+
+ write_word(ss, AX, result);
+}
+
+
+/** Function 01h - Return VBE Mode Information
+ *
+ * Input:
+ * AX = 4F01h
+ * CX = Mode Number
+ * ES:DI = Pointer to buffer in which to place ModeInfoBlock structure
+ * Output:
+ * AX = VBE Return Status
+ *
+ */
+void vbe_biosfn_return_mode_information(AX, CX, ES, DI)
+Bit16u *AX;Bit16u CX; Bit16u ES;Bit16u DI;
+{
+ Bit16u result=0x0100;
+ Bit16u ss=get_SS();
+ ModeInfoBlock info;
+ ModeInfoListItem *cur_info;
+ Boolean using_lfb;
+
+#ifdef DEBUG
+ printf("VBE vbe_biosfn_return_mode_information ES%x DI%x CX%x\n",ES,DI,CX);
+#endif
+
+ using_lfb=((CX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER);
+
+ CX = (CX & 0x1ff);
+
+ cur_info = mode_info_find_mode(CX, using_lfb, &cur_info);
+
+ if (cur_info != 0)
+ {
+#ifdef DEBUG
+ printf("VBE found mode %x\n",CX);
+#endif
+ memsetb(ss, &info, 0, sizeof(ModeInfoBlock));
+ memcpyb(ss, &info, 0xc000, &(cur_info->info), sizeof(ModeInfoBlockCompact));
+ if (using_lfb) {
+ info.NumberOfBanks = 1;
+ }
+ if (info.WinAAttributes & VBE_WINDOW_ATTRIBUTE_RELOCATABLE) {
+ info.WinFuncPtr = 0xC0000000UL;
+ *(Bit16u *)&(info.WinFuncPtr) = (Bit16u)(dispi_set_bank_farcall);
+ }
+
+ result = 0x4f;
+ }
+ else
+ {
+#ifdef DEBUG
+ printf("VBE *NOT* found mode %x\n",CX);
+#endif
+ result = 0x100;
+ }
+
+ if (result == 0x4f)
+ {
+ // copy updates in mode_info_block back
+ memcpyb(ES, DI, ss, &info, sizeof(info));
+ }
+
+ write_word(ss, AX, result);
+}
+
+/** Function 02h - Set VBE Mode
+ *
+ * Input:
+ * AX = 4F02h
+ * BX = Desired Mode to set
+ * ES:DI = Pointer to CRTCInfoBlock structure
+ * Output:
+ * AX = VBE Return Status
+ *
+ */
+void vbe_biosfn_set_mode(AX, BX, ES, DI)
+Bit16u *AX;Bit16u BX; Bit16u ES;Bit16u DI;
+{
+ Bit16u ss = get_SS();
+ Bit16u result;
+ ModeInfoListItem *cur_info;
+ Boolean using_lfb;
+ Bit8u no_clear;
+ Bit8u lfb_flag;
+
+ using_lfb=((BX & VBE_MODE_LINEAR_FRAME_BUFFER) == VBE_MODE_LINEAR_FRAME_BUFFER);
+ lfb_flag=using_lfb?VBE_DISPI_LFB_ENABLED:0;
+ no_clear=((BX & VBE_MODE_PRESERVE_DISPLAY_MEMORY) == VBE_MODE_PRESERVE_DISPLAY_MEMORY)?VBE_DISPI_NOCLEARMEM:0;
+
+ BX = (BX & 0x1ff);
+
+ //result=read_word(ss,AX);
+
+ // check for non vesa mode
+ if (BX<VBE_MODE_VESA_DEFINED)
+ {
+ Bit8u mode;
+
+ dispi_set_enable(VBE_DISPI_DISABLED);
+ // call the vgabios in order to set the video mode
+ // this allows for going back to textmode with a VBE call (some applications expect that to work)
+
+ mode=(BX & 0xff);
+ biosfn_set_video_mode(mode);
+ result = 0x4f;
+ }
+
+ cur_info = mode_info_find_mode(BX, using_lfb, &cur_info);
+
+ if (cur_info != 0)
+ {
+#ifdef DEBUG
+ printf("VBE found mode %x, setting:\n", BX);
+ printf("\txres%x yres%x bpp%x\n",
+ cur_info->info.XResolution,
+ cur_info->info.YResolution,
+ cur_info->info.BitsPerPixel);
+#endif
+
+ // first disable current mode (when switching between vesa modi)
+ dispi_set_enable(VBE_DISPI_DISABLED);
+
+ if (cur_info->info.BitsPerPixel == 4)
+ {
+ biosfn_set_video_mode(0x6a);
+ }
+
+ dispi_set_bpp(cur_info->info.BitsPerPixel);
+ dispi_set_xres(cur_info->info.XResolution);
+ dispi_set_yres(cur_info->info.YResolution);
+ dispi_set_bank(0);
+ dispi_set_enable(VBE_DISPI_ENABLED | no_clear | lfb_flag);
+ vga_compat_setup();
+
+ write_word(BIOSMEM_SEG,BIOSMEM_VBE_MODE,BX);
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60 | no_clear));
+
+ result = 0x4f;
+ }
+ else
+ {
+#ifdef DEBUG
+ printf("VBE *NOT* found mode %x\n" , BX);
+#endif
+ result = 0x100;
+
+ // FIXME: redirect non VBE modi to normal VGA bios operation
+ // (switch back to VGA mode
+ if (BX == 3)
+ result = 0x4f;
+ }
+
+ write_word(ss, AX, result);
+}
+
+/** Function 03h - Return Current VBE Mode
+ *
+ * Input:
+ * AX = 4F03h
+ * Output:
+ * AX = VBE Return Status
+ * BX = Current VBE Mode
+ *
+ */
+ASM_START
+vbe_biosfn_return_current_mode:
+ push ds
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ call dispi_get_enable
+ and ax, # VBE_DISPI_ENABLED
+ jz no_vbe_mode
+ mov bx, # BIOSMEM_VBE_MODE
+ mov ax, [bx]
+ mov bx, ax
+ jnz vbe_03_ok
+no_vbe_mode:
+ mov bx, # BIOSMEM_CURRENT_MODE
+ mov al, [bx]
+ mov bl, al
+ xor bh, bh
+vbe_03_ok:
+ mov ax, #0x004f
+ pop ds
+ ret
+ASM_END
+
+
+Bit16u vbe_biosfn_read_video_state_size()
+{
+ return 9 * 2;
+}
+
+void vbe_biosfn_save_video_state(ES, BX)
+ Bit16u ES; Bit16u BX;
+{
+ Bit16u enable, i;
+
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
+ enable = inw(VBE_DISPI_IOPORT_DATA);
+ write_word(ES, BX, enable);
+ BX += 2;
+ if (!(enable & VBE_DISPI_ENABLED))
+ return;
+ for(i = VBE_DISPI_INDEX_XRES; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
+ if (i != VBE_DISPI_INDEX_ENABLE) {
+ outw(VBE_DISPI_IOPORT_INDEX, i);
+ write_word(ES, BX, inw(VBE_DISPI_IOPORT_DATA));
+ BX += 2;
+ }
+ }
+}
+
+
+void vbe_biosfn_restore_video_state(ES, BX)
+ Bit16u ES; Bit16u BX;
+{
+ Bit16u enable, i;
+
+ enable = read_word(ES, BX);
+ BX += 2;
+
+ if (!(enable & VBE_DISPI_ENABLED)) {
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
+ outw(VBE_DISPI_IOPORT_DATA, enable);
+ } else {
+ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ outw(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ outw(VBE_DISPI_IOPORT_INDEX,VBE_DISPI_INDEX_ENABLE);
+ outw(VBE_DISPI_IOPORT_DATA, enable);
+
+ for(i = VBE_DISPI_INDEX_BANK; i <= VBE_DISPI_INDEX_Y_OFFSET; i++) {
+ outw(VBE_DISPI_IOPORT_INDEX, i);
+ outw(VBE_DISPI_IOPORT_DATA, read_word(ES, BX));
+ BX += 2;
+ }
+ }
+}
+
+/** Function 04h - Save/Restore State
+ *
+ * Input:
+ * AX = 4F04h
+ * DL = 00h Return Save/Restore State buffer size
+ * 01h Save State
+ * 02h Restore State
+ * CX = Requested states
+ * ES:BX = Pointer to buffer (if DL <> 00h)
+ * Output:
+ * AX = VBE Return Status
+ * BX = Number of 64-byte blocks to hold the state buffer (if DL=00h)
+ *
+ */
+void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX)
+Bit16u *AX; Bit16u CX; Bit16u DX; Bit16u ES; Bit16u *BX;
+{
+ Bit16u ss=get_SS();
+ Bit16u result, val;
+
+ result = 0x4f;
+ switch(GET_DL()) {
+ case 0x00:
+ val = biosfn_read_video_state_size2(CX);
+#ifdef DEBUG
+ printf("VGA state size=%x\n", val);
+#endif
+ if (CX & 8)
+ val += vbe_biosfn_read_video_state_size();
+ write_word(ss, BX, val);
+ break;
+ case 0x01:
+ val = read_word(ss, BX);
+ val = biosfn_save_video_state(CX, ES, val);
+#ifdef DEBUG
+ printf("VGA save_state offset=%x\n", val);
+#endif
+ if (CX & 8)
+ vbe_biosfn_save_video_state(ES, val);
+ break;
+ case 0x02:
+ val = read_word(ss, BX);
+ val = biosfn_restore_video_state(CX, ES, val);
+#ifdef DEBUG
+ printf("VGA restore_state offset=%x\n", val);
+#endif
+ if (CX & 8)
+ vbe_biosfn_restore_video_state(ES, val);
+ break;
+ default:
+ // function failed
+ result = 0x100;
+ break;
+ }
+ write_word(ss, AX, result);
+}
+
+/** Function 05h - Display Window Control
+ *
+ * Input:
+ * AX = 4F05h
+ * (16-bit) BH = 00h Set memory window
+ * = 01h Get memory window
+ * BL = Window number
+ * = 00h Window A
+ * = 01h Window B
+ * DX = Window number in video memory in window
+ * granularity units (Set Memory Window only)
+ * Note:
+ * If this function is called while in a linear frame buffer mode,
+ * this function must fail with completion code AH=03h
+ *
+ * Output:
+ * AX = VBE Return Status
+ * DX = Window number in window granularity units
+ * (Get Memory Window only)
+ */
+ASM_START
+vbe_biosfn_display_window_control:
+ cmp bl, #0x00
+ jne vbe_05_failed
+ cmp bh, #0x01
+ je get_display_window
+ jb set_display_window
+ mov ax, #0x0100
+ ret
+set_display_window:
+ mov ax, dx
+ call _dispi_set_bank
+ call dispi_get_bank
+ cmp ax, dx
+ jne vbe_05_failed
+ mov ax, #0x004f
+ ret
+get_display_window:
+ call dispi_get_bank
+ mov dx, ax
+ mov ax, #0x004f
+ ret
+vbe_05_failed:
+ mov ax, #0x014f
+ ret
+ASM_END
+
+
+/** Function 06h - Set/Get Logical Scan Line Length
+ *
+ * Input:
+ * AX = 4F06h
+ * BL = 00h Set Scan Line Length in Pixels
+ * = 01h Get Scan Line Length
+ * = 02h Set Scan Line Length in Bytes
+ * = 03h Get Maximum Scan Line Length
+ * CX = If BL=00h Desired Width in Pixels
+ * If BL=02h Desired Width in Bytes
+ * (Ignored for Get Functions)
+ *
+ * Output:
+ * AX = VBE Return Status
+ * BX = Bytes Per Scan Line
+ * CX = Actual Pixels Per Scan Line
+ * (truncated to nearest complete pixel)
+ * DX = Maximum Number of Scan Lines
+ */
+ASM_START
+vbe_biosfn_set_get_logical_scan_line_length:
+ mov ax, cx
+ cmp bl, #0x01
+ je get_logical_scan_line_length
+ cmp bl, #0x02
+ je set_logical_scan_line_bytes
+ jb set_logical_scan_line_pixels
+ mov ax, #0x0100
+ ret
+set_logical_scan_line_bytes:
+ push ax
+ call dispi_get_bpp
+ xor bh, bh
+ mov bl, ah
+ or bl, bl
+ jnz no_4bpp_1
+ shl ax, #3
+ mov bl, #1
+no_4bpp_1:
+ xor dx, dx
+ pop ax
+ div bx
+set_logical_scan_line_pixels:
+ call dispi_set_virt_width
+get_logical_scan_line_length:
+ call dispi_get_bpp
+ xor bh, bh
+ mov bl, ah
+ call dispi_get_virt_width
+ mov cx, ax
+ or bl, bl
+ jnz no_4bpp_2
+ shr ax, #3
+ mov bl, #1
+no_4bpp_2:
+ mul bx
+ mov bx, ax
+ call dispi_get_virt_height
+ mov dx, ax
+ mov ax, #0x004f
+ ret
+ASM_END
+
+
+/** Function 07h - Set/Get Display Start
+ *
+ * Input(16-bit):
+ * AX = 4F07h
+ * BH = 00h Reserved and must be 00h
+ * BL = 00h Set Display Start
+ * = 01h Get Display Start
+ * = 02h Schedule Display Start (Alternate)
+ * = 03h Schedule Stereoscopic Display Start
+ * = 04h Get Scheduled Display Start Status
+ * = 05h Enable Stereoscopic Mode
+ * = 06h Disable Stereoscopic Mode
+ * = 80h Set Display Start during Vertical Retrace
+ * = 82h Set Display Start during Vertical Retrace (Alternate)
+ * = 83h Set Stereoscopic Display Start during Vertical Retrace
+ * ECX = If BL=02h/82h Display Start Address in bytes
+ * If BL=03h/83h Left Image Start Address in bytes
+ * EDX = If BL=03h/83h Right Image Start Address in bytes
+ * CX = If BL=00h/80h First Displayed Pixel In Scan Line
+ * DX = If BL=00h/80h First Displayed Scan Line
+ *
+ * Output:
+ * AX = VBE Return Status
+ * BH = If BL=01h Reserved and will be 0
+ * CX = If BL=01h First Displayed Pixel In Scan Line
+ * If BL=04h 0 if flip has not occurred, not 0 if it has
+ * DX = If BL=01h First Displayed Scan Line
+ *
+ * Input(32-bit):
+ * BH = 00h Reserved and must be 00h
+ * BL = 00h Set Display Start
+ * = 80h Set Display Start during Vertical Retrace
+ * CX = Bits 0-15 of display start address
+ * DX = Bits 16-31 of display start address
+ * ES = Selector for memory mapped registers
+ */
+ASM_START
+vbe_biosfn_set_get_display_start:
+ cmp bl, #0x80
+ je set_display_start
+ cmp bl, #0x01
+ je get_display_start
+ jb set_display_start
+ mov ax, #0x0100
+ ret
+set_display_start:
+ mov ax, cx
+ call dispi_set_x_offset
+ mov ax, dx
+ call dispi_set_y_offset
+ mov ax, #0x004f
+ ret
+get_display_start:
+ call dispi_get_x_offset
+ mov cx, ax
+ call dispi_get_y_offset
+ mov dx, ax
+ xor bh, bh
+ mov ax, #0x004f
+ ret
+ASM_END
+
+
+/** Function 08h - Set/Get Dac Palette Format
+ *
+ * Input:
+ * AX = 4F08h
+ * BL = 00h set DAC palette width
+ * = 01h get DAC palette width
+ * BH = If BL=00h: desired number of bits per primary color
+ * Output:
+ * AX = VBE Return Status
+ * BH = current number of bits per primary color (06h = standard VGA)
+ */
+ASM_START
+vbe_biosfn_set_get_dac_palette_format:
+ cmp bl, #0x01
+ je get_dac_palette_format
+ jb set_dac_palette_format
+ mov ax, #0x0100
+ ret
+set_dac_palette_format:
+ call dispi_get_enable
+ cmp bh, #0x06
+ je set_normal_dac
+ cmp bh, #0x08
+ jne vbe_08_unsupported
+ or ax, # VBE_DISPI_8BIT_DAC
+ jnz set_dac_mode
+set_normal_dac:
+ and ax, #~ VBE_DISPI_8BIT_DAC
+set_dac_mode:
+ call _dispi_set_enable
+get_dac_palette_format:
+ mov bh, #0x06
+ call dispi_get_enable
+ and ax, # VBE_DISPI_8BIT_DAC
+ jz vbe_08_ok
+ mov bh, #0x08
+vbe_08_ok:
+ mov ax, #0x004f
+ ret
+vbe_08_unsupported:
+ mov ax, #0x014f
+ ret
+ASM_END
+
+
+/** Function 09h - Set/Get Palette Data
+ *
+ * Input:
+ * AX = 4F09h
+ * Output:
+ * AX = VBE Return Status
+ *
+ * FIXME: incomplete API description, Input & Output
+ */
+void vbe_biosfn_set_get_palette_data(AX)
+{
+}
+
+/** Function 0Ah - Return VBE Protected Mode Interface
+ * Input: AX = 4F0Ah VBE 2.0 Protected Mode Interface
+ * BL = 00h Return protected mode table
+ *
+ *
+ * Output: AX = Status
+ * ES = Real Mode Segment of Table
+ * DI = Offset of Table
+ * CX = Length of Table including protected mode code
+ * (for copying purposes)
+ */
+ASM_START
+vbe_biosfn_return_protected_mode_interface:
+ test bl, bl
+ jnz _fail
+ mov di, #0xc000
+ mov es, di
+ mov di, # vesa_pm_start
+ mov cx, # vesa_pm_end
+ sub cx, di
+ mov ax, #0x004f
+ ret
+_fail:
+ mov ax, #0x014f
+ ret
+ASM_END
diff --git a/kvm/vgabios/vbe.h b/kvm/vgabios/vbe.h
new file mode 100644
index 0000000..60434ac
--- /dev/null
+++ b/kvm/vgabios/vbe.h
@@ -0,0 +1,313 @@
+#ifndef vbe_h_included
+#define vbe_h_included
+
+#include "vgabios.h"
+
+// DISPI helper function
+void dispi_set_enable(enable);
+
+/** VBE int10 API
+ *
+ * See the function descriptions in vbe.c for more information
+ */
+Boolean vbe_has_vbe_display();
+void vbe_biosfn_return_controller_information(AX, ES, DI);
+void vbe_biosfn_return_mode_information(AX, CX, ES, DI);
+void vbe_biosfn_set_mode(AX, BX, ES, DI);
+void vbe_biosfn_save_restore_state(AX, CX, DX, ES, BX);
+void vbe_biosfn_set_get_palette_data(AX);
+void vbe_biosfn_return_protected_mode_interface(AX);
+
+// The official VBE Information Block
+typedef struct VbeInfoBlock
+{
+ Bit8u VbeSignature[4];
+ Bit16u VbeVersion;
+ Bit16u OemStringPtr_Off;
+ Bit16u OemStringPtr_Seg;
+ Bit8u Capabilities[4];
+ Bit16u VideoModePtr_Off;
+ Bit16u VideoModePtr_Seg;
+ Bit16u TotalMemory;
+ Bit16u OemSoftwareRev;
+ Bit16u OemVendorNamePtr_Off;
+ Bit16u OemVendorNamePtr_Seg;
+ Bit16u OemProductNamePtr_Off;
+ Bit16u OemProductNamePtr_Seg;
+ Bit16u OemProductRevPtr_Off;
+ Bit16u OemProductRevPtr_Seg;
+ Bit16u Reserved[111]; // used for dynamicly generated mode list
+ Bit8u OemData[256];
+} VbeInfoBlock;
+
+
+// This one is for compactly storing a static list of mode info blocks
+// this saves us 189 bytes per block
+typedef struct ModeInfoBlockCompact
+{
+// Mandatory information for all VBE revisions
+ Bit16u ModeAttributes;
+ Bit8u WinAAttributes;
+ Bit8u WinBAttributes;
+ Bit16u WinGranularity;
+ Bit16u WinSize;
+ Bit16u WinASegment;
+ Bit16u WinBSegment;
+ Bit32u WinFuncPtr;
+ Bit16u BytesPerScanLine;
+// Mandatory information for VBE 1.2 and above
+ Bit16u XResolution;
+ Bit16u YResolution;
+ Bit8u XCharSize;
+ Bit8u YCharSize;
+ Bit8u NumberOfPlanes;
+ Bit8u BitsPerPixel;
+ Bit8u NumberOfBanks;
+ Bit8u MemoryModel;
+ Bit8u BankSize;
+ Bit8u NumberOfImagePages;
+ Bit8u Reserved_page;
+// Direct Color fields (required for direct/6 and YUV/7 memory models)
+ Bit8u RedMaskSize;
+ Bit8u RedFieldPosition;
+ Bit8u GreenMaskSize;
+ Bit8u GreenFieldPosition;
+ Bit8u BlueMaskSize;
+ Bit8u BlueFieldPosition;
+ Bit8u RsvdMaskSize;
+ Bit8u RsvdFieldPosition;
+ Bit8u DirectColorModeInfo;
+// Mandatory information for VBE 2.0 and above
+ Bit32u PhysBasePtr;
+ Bit32u OffScreenMemOffset;
+ Bit16u OffScreenMemSize;
+// Mandatory information for VBE 3.0 and above
+ Bit16u LinBytesPerScanLine;
+ Bit8u BnkNumberOfPages;
+ Bit8u LinNumberOfPages;
+ Bit8u LinRedMaskSize;
+ Bit8u LinRedFieldPosition;
+ Bit8u LinGreenMaskSize;
+ Bit8u LinGreenFieldPosition;
+ Bit8u LinBlueMaskSize;
+ Bit8u LinBlueFieldPosition;
+ Bit8u LinRsvdMaskSize;
+ Bit8u LinRsvdFieldPosition;
+ Bit32u MaxPixelClock;
+// Bit8u Reserved[189]; // DO NOT PUT THIS IN HERE because of Compact Mode Info storage in bios
+} ModeInfoBlockCompact;
+
+typedef struct ModeInfoBlock
+{
+// Mandatory information for all VBE revisions
+ Bit16u ModeAttributes;
+ Bit8u WinAAttributes;
+ Bit8u WinBAttributes;
+ Bit16u WinGranularity;
+ Bit16u WinSize;
+ Bit16u WinASegment;
+ Bit16u WinBSegment;
+ Bit32u WinFuncPtr;
+ Bit16u BytesPerScanLine;
+// Mandatory information for VBE 1.2 and above
+ Bit16u XResolution;
+ Bit16u YResolution;
+ Bit8u XCharSize;
+ Bit8u YCharSize;
+ Bit8u NumberOfPlanes;
+ Bit8u BitsPerPixel;
+ Bit8u NumberOfBanks;
+ Bit8u MemoryModel;
+ Bit8u BankSize;
+ Bit8u NumberOfImagePages;
+ Bit8u Reserved_page;
+// Direct Color fields (required for direct/6 and YUV/7 memory models)
+ Bit8u RedMaskSize;
+ Bit8u RedFieldPosition;
+ Bit8u GreenMaskSize;
+ Bit8u GreenFieldPosition;
+ Bit8u BlueMaskSize;
+ Bit8u BlueFieldPosition;
+ Bit8u RsvdMaskSize;
+ Bit8u RsvdFieldPosition;
+ Bit8u DirectColorModeInfo;
+// Mandatory information for VBE 2.0 and above
+ Bit32u PhysBasePtr;
+ Bit32u OffScreenMemOffset;
+ Bit16u OffScreenMemSize;
+// Mandatory information for VBE 3.0 and above
+ Bit16u LinBytesPerScanLine;
+ Bit8u BnkNumberOfPages;
+ Bit8u LinNumberOfPages;
+ Bit8u LinRedMaskSize;
+ Bit8u LinRedFieldPosition;
+ Bit8u LinGreenMaskSize;
+ Bit8u LinGreenFieldPosition;
+ Bit8u LinBlueMaskSize;
+ Bit8u LinBlueFieldPosition;
+ Bit8u LinRsvdMaskSize;
+ Bit8u LinRsvdFieldPosition;
+ Bit32u MaxPixelClock;
+ Bit8u Reserved[189];
+} ModeInfoBlock;
+
+typedef struct ModeInfoListItem
+{
+ Bit16u mode;
+ ModeInfoBlockCompact info;
+} ModeInfoListItem;
+
+// VBE Return Status Info
+// AL
+#define VBE_RETURN_STATUS_SUPPORTED 0x4F
+#define VBE_RETURN_STATUS_UNSUPPORTED 0x00
+// AH
+#define VBE_RETURN_STATUS_SUCCESSFULL 0x00
+#define VBE_RETURN_STATUS_FAILED 0x01
+#define VBE_RETURN_STATUS_NOT_SUPPORTED 0x02
+#define VBE_RETURN_STATUS_INVALID 0x03
+
+// VBE Mode Numbers
+
+#define VBE_MODE_VESA_DEFINED 0x0100
+#define VBE_MODE_REFRESH_RATE_USE_CRTC 0x0800
+#define VBE_MODE_LINEAR_FRAME_BUFFER 0x4000
+#define VBE_MODE_PRESERVE_DISPLAY_MEMORY 0x8000
+
+// VBE GFX Mode Number
+
+#define VBE_VESA_MODE_640X400X8 0x100
+#define VBE_VESA_MODE_640X480X8 0x101
+#define VBE_VESA_MODE_800X600X4 0x102
+#define VBE_VESA_MODE_800X600X8 0x103
+#define VBE_VESA_MODE_1024X768X4 0x104
+#define VBE_VESA_MODE_1024X768X8 0x105
+#define VBE_VESA_MODE_1280X1024X4 0x106
+#define VBE_VESA_MODE_1280X1024X8 0x107
+#define VBE_VESA_MODE_320X200X1555 0x10D
+#define VBE_VESA_MODE_320X200X565 0x10E
+#define VBE_VESA_MODE_320X200X888 0x10F
+#define VBE_VESA_MODE_640X480X1555 0x110
+#define VBE_VESA_MODE_640X480X565 0x111
+#define VBE_VESA_MODE_640X480X888 0x112
+#define VBE_VESA_MODE_800X600X1555 0x113
+#define VBE_VESA_MODE_800X600X565 0x114
+#define VBE_VESA_MODE_800X600X888 0x115
+#define VBE_VESA_MODE_1024X768X1555 0x116
+#define VBE_VESA_MODE_1024X768X565 0x117
+#define VBE_VESA_MODE_1024X768X888 0x118
+#define VBE_VESA_MODE_1280X1024X1555 0x119
+#define VBE_VESA_MODE_1280X1024X565 0x11A
+#define VBE_VESA_MODE_1280X1024X888 0x11B
+#define VBE_VESA_MODE_1600X1200X8 0x11C
+#define VBE_VESA_MODE_1600X1200X1555 0x11D
+#define VBE_VESA_MODE_1600X1200X565 0x11E
+#define VBE_VESA_MODE_1600X1200X888 0x11F
+
+// BOCHS/PLEX86 'own' mode numbers
+#define VBE_OWN_MODE_320X200X8888 0x140
+#define VBE_OWN_MODE_640X400X8888 0x141
+#define VBE_OWN_MODE_640X480X8888 0x142
+#define VBE_OWN_MODE_800X600X8888 0x143
+#define VBE_OWN_MODE_1024X768X8888 0x144
+#define VBE_OWN_MODE_1280X1024X8888 0x145
+#define VBE_OWN_MODE_320X200X8 0x146
+#define VBE_OWN_MODE_1600X1200X8888 0x147
+#define VBE_OWN_MODE_1152X864X8 0x148
+#define VBE_OWN_MODE_1152X864X1555 0x149
+#define VBE_OWN_MODE_1152X864X565 0x14a
+#define VBE_OWN_MODE_1152X864X888 0x14b
+#define VBE_OWN_MODE_1152X864X8888 0x14c
+
+#define VBE_VESA_MODE_END_OF_LIST 0xFFFF
+
+// Capabilities
+
+#define VBE_CAPABILITY_8BIT_DAC 0x0001
+#define VBE_CAPABILITY_NOT_VGA_COMPATIBLE 0x0002
+#define VBE_CAPABILITY_RAMDAC_USE_BLANK_BIT 0x0004
+#define VBE_CAPABILITY_STEREOSCOPIC_SUPPORT 0x0008
+#define VBE_CAPABILITY_STEREO_VIA_VESA_EVC 0x0010
+
+// Mode Attributes
+
+#define VBE_MODE_ATTRIBUTE_SUPPORTED 0x0001
+#define VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE 0x0002
+#define VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT 0x0004
+#define VBE_MODE_ATTRIBUTE_COLOR_MODE 0x0008
+#define VBE_MODE_ATTRIBUTE_GRAPHICS_MODE 0x0010
+#define VBE_MODE_ATTRIBUTE_NOT_VGA_COMPATIBLE 0x0020
+#define VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW 0x0040
+#define VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE 0x0080
+#define VBE_MODE_ATTRIBUTE_DOUBLE_SCAN_MODE 0x0100
+#define VBE_MODE_ATTRIBUTE_INTERLACE_MODE 0x0200
+#define VBE_MODE_ATTRIBUTE_HARDWARE_TRIPLE_BUFFER 0x0400
+#define VBE_MODE_ATTRIBUTE_HARDWARE_STEREOSCOPIC_DISPLAY 0x0800
+#define VBE_MODE_ATTRIBUTE_DUAL_DISPLAY_START_ADDRESS 0x1000
+
+#define VBE_MODE_ATTTRIBUTE_LFB_ONLY ( VBE_MODE_ATTRIBUTE_NO_VGA_COMPATIBLE_WINDOW | VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE )
+
+// Window attributes
+
+#define VBE_WINDOW_ATTRIBUTE_RELOCATABLE 0x01
+#define VBE_WINDOW_ATTRIBUTE_READABLE 0x02
+#define VBE_WINDOW_ATTRIBUTE_WRITEABLE 0x04
+
+// Memory model
+
+#define VBE_MEMORYMODEL_TEXT_MODE 0x00
+#define VBE_MEMORYMODEL_CGA_GRAPHICS 0x01
+#define VBE_MEMORYMODEL_HERCULES_GRAPHICS 0x02
+#define VBE_MEMORYMODEL_PLANAR 0x03
+#define VBE_MEMORYMODEL_PACKED_PIXEL 0x04
+#define VBE_MEMORYMODEL_NON_CHAIN_4_256 0x05
+#define VBE_MEMORYMODEL_DIRECT_COLOR 0x06
+#define VBE_MEMORYMODEL_YUV 0x07
+
+// DirectColorModeInfo
+
+#define VBE_DIRECTCOLOR_COLOR_RAMP_PROGRAMMABLE 0x01
+#define VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE 0x02
+
+// GUEST <-> HOST Communication API
+
+// FIXME: either dynamicly ask host for this or put somewhere high in physical memory
+// like 0xE0000000
+
+
+ #define VBE_DISPI_BANK_ADDRESS 0xA0000
+ #define VBE_DISPI_BANK_SIZE_KB 64
+
+ #define VBE_DISPI_MAX_XRES 1024
+ #define VBE_DISPI_MAX_YRES 768
+
+ #define VBE_DISPI_IOPORT_INDEX 0x01CE
+ #define VBE_DISPI_IOPORT_DATA 0x01CF
+
+ #define VBE_DISPI_INDEX_ID 0x0
+ #define VBE_DISPI_INDEX_XRES 0x1
+ #define VBE_DISPI_INDEX_YRES 0x2
+ #define VBE_DISPI_INDEX_BPP 0x3
+ #define VBE_DISPI_INDEX_ENABLE 0x4
+ #define VBE_DISPI_INDEX_BANK 0x5
+ #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+ #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+ #define VBE_DISPI_INDEX_X_OFFSET 0x8
+ #define VBE_DISPI_INDEX_Y_OFFSET 0x9
+
+ #define VBE_DISPI_ID0 0xB0C0
+ #define VBE_DISPI_ID1 0xB0C1
+ #define VBE_DISPI_ID2 0xB0C2
+ #define VBE_DISPI_ID3 0xB0C3
+ #define VBE_DISPI_ID4 0xB0C4
+
+ #define VBE_DISPI_DISABLED 0x00
+ #define VBE_DISPI_ENABLED 0x01
+ #define VBE_DISPI_GETCAPS 0x02
+ #define VBE_DISPI_8BIT_DAC 0x20
+ #define VBE_DISPI_LFB_ENABLED 0x40
+ #define VBE_DISPI_NOCLEARMEM 0x80
+
+ #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+#endif
diff --git a/kvm/vgabios/vbetables-gen.c b/kvm/vgabios/vbetables-gen.c
new file mode 100644
index 0000000..3bf979d
--- /dev/null
+++ b/kvm/vgabios/vbetables-gen.c
@@ -0,0 +1,264 @@
+/* Generate the VGABIOS VBE Tables */
+#include <stdlib.h>
+#include <stdio.h>
+
+#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 16
+
+typedef struct {
+ int width;
+ int height;
+ int depth;
+ int mode;
+} ModeInfo;
+
+ModeInfo modes[] = {
+ /* standard VESA modes */
+{ 640, 400, 8 , 0x100},
+{ 640, 480, 8 , 0x101},
+{ 800, 600, 4 , 0x102},
+{ 800, 600, 8 , 0x103},
+{ 1024, 768, 4 , 0x104},
+{ 1024, 768, 8 , 0x105},
+{ 1280, 1024, 4 , 0x106},
+{ 1280, 1024, 8 , 0x107},
+{ 320, 200, 15 , 0x10D},
+{ 320, 200, 16 , 0x10E},
+{ 320, 200, 24 , 0x10F},
+{ 640, 480, 15 , 0x110},
+{ 640, 480, 16 , 0x111},
+{ 640, 480, 24 , 0x112},
+{ 800, 600, 15 , 0x113},
+{ 800, 600, 16 , 0x114},
+{ 800, 600, 24 , 0x115},
+{ 1024, 768, 15 , 0x116},
+{ 1024, 768, 16 , 0x117},
+{ 1024, 768, 24 , 0x118},
+{ 1280, 1024, 15 , 0x119},
+{ 1280, 1024, 16 , 0x11A},
+{ 1280, 1024, 24 , 0x11B},
+{ 1600, 1200, 8 , 0x11C},
+{ 1600, 1200, 15 , 0x11D},
+{ 1600, 1200, 16 , 0x11E},
+{ 1600, 1200, 24 , 0x11F},
+
+ /* BOCHS/PLE, 86 'own' mode numbers */
+{ 320, 200, 32 , 0x140},
+{ 640, 400, 32 , 0x141},
+{ 640, 480, 32 , 0x142},
+{ 800, 600, 32 , 0x143},
+{ 1024, 768, 32 , 0x144},
+{ 1280, 1024, 32 , 0x145},
+{ 320, 200, 8 , 0x146},
+{ 1600, 1200, 32 , 0x147},
+{ 1152, 864, 8 , 0x148},
+{ 1152, 864, 15 , 0x149},
+{ 1152, 864, 16 , 0x14a},
+{ 1152, 864, 24 , 0x14b},
+{ 1152, 864, 32 , 0x14c},
+{ 1280, 768, 16 , 0x175},
+{ 1280, 768, 24 , 0x176},
+{ 1280, 768, 32 , 0x177},
+{ 1280, 800, 16 , 0x178},
+{ 1280, 800, 24 , 0x179},
+{ 1280, 800, 32 , 0x17a},
+{ 1280, 960, 16 , 0x17b},
+{ 1280, 960, 24 , 0x17c},
+{ 1280, 960, 32 , 0x17d},
+{ 1440, 900, 16 , 0x17e},
+{ 1440, 900, 24 , 0x17f},
+{ 1440, 900, 32 , 0x180},
+{ 1400, 1050, 16 , 0x181},
+{ 1400, 1050, 24 , 0x182},
+{ 1400, 1050, 32 , 0x183},
+{ 1680, 1050, 16 , 0x184},
+{ 1680, 1050, 24 , 0x185},
+{ 1680, 1050, 32 , 0x186},
+{ 1920, 1200, 16 , 0x187},
+{ 1920, 1200, 24 , 0x188},
+{ 1920, 1200, 32 , 0x189},
+{ 2560, 1600, 16 , 0x18a},
+{ 2560, 1600, 24 , 0x18b},
+{ 2560, 1600, 32 , 0x18c},
+{ 0, },
+};
+
+int main(int argc, char **argv)
+{
+ const ModeInfo *pm;
+ int pages, pitch;
+ int r_size, r_pos, g_size, g_pos, b_size, b_pos, a_size, a_pos;
+ const char *str;
+ long vram_size = VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024 * 1024;
+
+ printf("/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n");
+ printf("#define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB %d\n\n", VBE_DISPI_TOTAL_VIDEO_MEMORY_MB);
+ printf("static ModeInfoListItem mode_info_list[]=\n");
+ printf("{\n");
+ for (pm = modes; pm->mode != 0; pm++) {
+ if (pm->depth == 4)
+ pitch = (pm->width + 7) / 8;
+ else
+ pitch = pm->width * ((pm->depth + 7) / 8);
+ pages = vram_size / (pm->height * pitch);
+ if (pages > 0) {
+ printf("{ 0x%04x, /* %dx%dx%d */\n",
+ pm->mode, pm->width, pm->height, pm->depth);
+ if (pm->depth == 4)
+ printf("{ /*Bit16u ModeAttributes*/ %s,\n",
+ "VBE_MODE_ATTRIBUTE_SUPPORTED | "
+ "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | "
+ "VBE_MODE_ATTRIBUTE_COLOR_MODE | "
+ "VBE_MODE_ATTRIBUTE_TTY_BIOS_SUPPORT | "
+ "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE");
+ else
+ printf("{ /*Bit16u ModeAttributes*/ %s,\n",
+ "VBE_MODE_ATTRIBUTE_SUPPORTED | "
+ "VBE_MODE_ATTRIBUTE_EXTENDED_INFORMATION_AVAILABLE | "
+ "VBE_MODE_ATTRIBUTE_COLOR_MODE | "
+ "VBE_MODE_ATTRIBUTE_LINEAR_FRAME_BUFFER_MODE | "
+ "VBE_MODE_ATTRIBUTE_GRAPHICS_MODE");
+ printf("/*Bit8u WinAAttributes*/ %s,\n",
+ "VBE_WINDOW_ATTRIBUTE_RELOCATABLE | "
+ "VBE_WINDOW_ATTRIBUTE_READABLE | "
+ "VBE_WINDOW_ATTRIBUTE_WRITEABLE");
+
+ printf("/*Bit8u WinBAttributes*/ %d,\n", 0);
+
+ printf("/*Bit16u WinGranularity*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB");
+
+ printf("/*Bit16u WinSize*/ %s,\n", "VBE_DISPI_BANK_SIZE_KB");
+
+ printf("/*Bit16u WinASegment*/ %s,\n", "VGAMEM_GRAPH");
+
+ printf("/*Bit16u WinBSegment*/ 0x%04x,\n", 0);
+
+ printf("/*Bit32u WinFuncPtr*/ %d,\n", 0);
+
+ printf("/*Bit16u BytesPerScanLine*/ %d,\n", pitch);
+
+ // Mandatory information for VBE 1.2 and above
+ printf("/*Bit16u XResolution*/ %d,\n", pm->width);
+ printf("/*Bit16u YResolution*/ %d,\n", pm->height);
+ printf("/*Bit8u XCharSize*/ %d,\n", 8);
+ printf("/*Bit8u YCharSize*/ %d,\n", 16);
+ if (pm->depth == 4) {
+ printf("/*Bit8u NumberOfPlanes*/ %d,\n", 4);
+ } else {
+ printf("/*Bit8u NumberOfPlanes*/ %d,\n", 1);
+ }
+ printf("/*Bit8u BitsPerPixel*/ %d,\n", pm->depth);
+ printf("/*Bit8u NumberOfBanks*/ %d,\n",
+ (pm->height * pitch + 65535) / 65536);
+
+ if (pm->depth == 4)
+ str = "VBE_MEMORYMODEL_PLANAR";
+ else if (pm->depth == 8)
+ str = "VBE_MEMORYMODEL_PACKED_PIXEL";
+ else
+ str = "VBE_MEMORYMODEL_DIRECT_COLOR";
+ printf("/*Bit8u MemoryModel*/ %s,\n", str);
+ printf("/*Bit8u BankSize*/ %d,\n", 0);
+ if (pm->depth == 4)
+ printf("/*Bit8u NumberOfImagePages*/ %d,\n", (pages / 4) - 1);
+ else
+ printf("/*Bit8u NumberOfImagePages*/ %d,\n", pages - 1);
+ printf("/*Bit8u Reserved_page*/ %d,\n", 0);
+
+ // Direct Color fields (required for direct/6 and YUV/7 memory models)
+ switch(pm->depth) {
+ case 15:
+ r_size = 5;
+ r_pos = 10;
+ g_size = 5;
+ g_pos = 5;
+ b_size = 5;
+ b_pos = 0;
+ a_size = 1;
+ a_pos = 15;
+ break;
+ case 16:
+ r_size = 5;
+ r_pos = 11;
+ g_size = 6;
+ g_pos = 5;
+ b_size = 5;
+ b_pos = 0;
+ a_size = 0;
+ a_pos = 0;
+ break;
+ case 24:
+ r_size = 8;
+ r_pos = 16;
+ g_size = 8;
+ g_pos = 8;
+ b_size = 8;
+ b_pos = 0;
+ a_size = 0;
+ a_pos = 0;
+ break;
+ case 32:
+ r_size = 8;
+ r_pos = 16;
+ g_size = 8;
+ g_pos = 8;
+ b_size = 8;
+ b_pos = 0;
+ a_size = 8;
+ a_pos = 24;
+ break;
+ default:
+ r_size = 0;
+ r_pos = 0;
+ g_size = 0;
+ g_pos = 0;
+ b_size = 0;
+ b_pos = 0;
+ a_size = 0;
+ a_pos = 0;
+ break;
+ }
+
+ printf("/*Bit8u RedMaskSize*/ %d,\n", r_size);
+ printf("/*Bit8u RedFieldPosition*/ %d,\n", r_pos);
+ printf("/*Bit8u GreenMaskSize*/ %d,\n", g_size);
+ printf("/*Bit8u GreenFieldPosition*/ %d,\n", g_pos);
+ printf("/*Bit8u BlueMaskSize*/ %d,\n", b_size);
+ printf("/*Bit8u BlueFieldPosition*/ %d,\n", b_pos);
+ printf("/*Bit8u RsvdMaskSize*/ %d,\n", a_size);
+ printf("/*Bit8u RsvdFieldPosition*/ %d,\n", a_pos);
+ if (pm->depth == 32)
+ printf("/*Bit8u DirectColorModeInfo*/ %s,\n",
+ "VBE_DIRECTCOLOR_RESERVED_BITS_AVAILABLE");
+ else
+ printf("/*Bit8u DirectColorModeInfo*/ %s,\n", "0");
+
+// Mandatory information for VBE 2.0 and above
+ if (pm->depth > 4)
+ printf("/*Bit32u PhysBasePtr*/ %s,\n",
+ "VBE_DISPI_LFB_PHYSICAL_ADDRESS");
+ else
+ printf("/*Bit32u PhysBasePtr*/ %s,\n", "0");
+ printf("/*Bit32u OffScreenMemOffset*/ %d,\n", 0);
+ printf("/*Bit16u OffScreenMemSize*/ %d,\n", 0);
+ // Mandatory information for VBE 3.0 and above
+ printf("/*Bit16u LinBytesPerScanLine*/ %d,\n", pitch);
+ printf("/*Bit8u BnkNumberOfPages*/ %d,\n", 0);
+ printf("/*Bit8u LinNumberOfPages*/ %d,\n", 0);
+ printf("/*Bit8u LinRedMaskSize*/ %d,\n", r_size);
+ printf("/*Bit8u LinRedFieldPosition*/ %d,\n", r_pos);
+ printf("/*Bit8u LinGreenMaskSize*/ %d,\n", g_size);
+ printf("/*Bit8u LinGreenFieldPosition*/ %d,\n", g_pos);
+ printf("/*Bit8u LinBlueMaskSize*/ %d,\n", b_size);
+ printf("/*Bit8u LinBlueFieldPosition*/ %d,\n", b_pos);
+ printf("/*Bit8u LinRsvdMaskSize*/ %d,\n", a_size);
+ printf("/*Bit8u LinRsvdFieldPosition*/ %d,\n", a_pos);
+ printf("/*Bit32u MaxPixelClock*/ %d,\n", 0);
+ printf("} },\n");
+ }
+ }
+ printf("{ VBE_VESA_MODE_END_OF_LIST,\n");
+ printf("{ 0,\n");
+ printf("} },\n");
+ printf("};\n");
+ return 0;
+}
diff --git a/kvm/vgabios/vgabios.c b/kvm/vgabios/vgabios.c
new file mode 100644
index 0000000..e6fe2a0
--- /dev/null
+++ b/kvm/vgabios/vgabios.c
@@ -0,0 +1,3853 @@
+// ============================================================================================
+/*
+ * vgabios.c
+ */
+// ============================================================================================
+//
+// Copyright (C) 2001-2008 the LGPL VGABios developers Team
+//
+// 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, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// ============================================================================================
+//
+// This VGA Bios is specific to the plex86/bochs Emulated VGA card.
+// You can NOT drive any physical vga card with it.
+//
+// ============================================================================================
+//
+// This file contains code ripped from :
+// - rombios.c of plex86
+//
+// This VGA Bios contains fonts from :
+// - fntcol16.zip (c) by Joseph Gil avalable at :
+// ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+// These fonts are public domain
+//
+// This VGA Bios is based on information taken from :
+// - Kevin Lawton's vga card emulation for bochs/plex86
+// - Ralf Brown's interrupts list available at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
+// - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/
+// - Michael Abrash's Graphics Programming Black Book
+// - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex
+// - DOSEMU 1.0.1 source code for several tables values and formulas
+//
+// Thanks for patches, comments and ideas to :
+// - techt@pikeonline.net
+//
+// ============================================================================================
+
+#include "vgabios.h"
+
+#ifdef VBE
+#include "vbe.h"
+#endif
+
+#define USE_BX_INFO
+
+/* Declares */
+static Bit8u read_byte();
+static Bit16u read_word();
+static void write_byte();
+static void write_word();
+static Bit8u inb();
+static Bit16u inw();
+static void outb();
+static void outw();
+
+static Bit16u get_SS();
+
+// Output
+static void printf();
+static void unimplemented();
+static void unknown();
+
+static Bit8u find_vga_entry();
+
+static void memsetb();
+static void memsetw();
+static void memcpyb();
+static void memcpyw();
+
+static void biosfn_set_video_mode();
+static void biosfn_set_cursor_shape();
+static void biosfn_set_cursor_pos();
+static void biosfn_get_cursor_pos();
+static void biosfn_set_active_page();
+static void biosfn_scroll();
+static void biosfn_read_char_attr();
+static void biosfn_write_char_attr();
+static void biosfn_write_char_only();
+static void biosfn_write_pixel();
+static void biosfn_read_pixel();
+static void biosfn_write_teletype();
+static void biosfn_perform_gray_scale_summing();
+static void biosfn_load_text_user_pat();
+static void biosfn_load_text_8_14_pat();
+static void biosfn_load_text_8_8_pat();
+static void biosfn_load_text_8_16_pat();
+static void biosfn_load_gfx_8_8_chars();
+static void biosfn_load_gfx_user_chars();
+static void biosfn_load_gfx_8_14_chars();
+static void biosfn_load_gfx_8_8_dd_chars();
+static void biosfn_load_gfx_8_16_chars();
+static void biosfn_get_font_info();
+static void biosfn_alternate_prtsc();
+static void biosfn_switch_video_interface();
+static void biosfn_enable_video_refresh_control();
+static void biosfn_write_string();
+static void biosfn_read_state_info();
+static void biosfn_read_video_state_size();
+static Bit16u biosfn_save_video_state();
+static Bit16u biosfn_restore_video_state();
+extern Bit8u video_save_pointer_table[];
+
+// This is for compiling with gcc2 and gcc3
+#define ASM_START #asm
+#define ASM_END #endasm
+
+ASM_START
+
+MACRO SET_INT_VECTOR
+ push ds
+ xor ax, ax
+ mov ds, ax
+ mov ax, ?3
+ mov ?1*4, ax
+ mov ax, ?2
+ mov ?1*4+2, ax
+ pop ds
+MEND
+
+ASM_END
+
+ASM_START
+.text
+.rom
+.org 0
+
+use16 386
+
+vgabios_start:
+.byte 0x55, 0xaa /* BIOS signature, required for BIOS extensions */
+
+.byte 0x40 /* BIOS extension length in units of 512 bytes */
+
+
+vgabios_entry_point:
+
+ jmp vgabios_init_func
+
+#ifdef PCIBIOS
+.org 0x18
+.word vgabios_pci_data
+#endif
+
+// Info from Bart Oldeman
+.org 0x1e
+.ascii "IBM"
+.byte 0x00
+
+vgabios_name:
+.ascii "Plex86/Bochs VGABios"
+#ifdef PCIBIOS
+.ascii " (PCI)"
+#endif
+.ascii " "
+.byte 0x00
+
+vgabios_version:
+#ifndef VGABIOS_VERS
+.ascii "current-cvs"
+#else
+.ascii VGABIOS_VERS
+#endif
+.ascii " "
+
+vgabios_date:
+.ascii VGABIOS_DATE
+.byte 0x0a,0x0d
+.byte 0x00
+
+vgabios_copyright:
+.ascii "(C) 2008 the LGPL VGABios developers Team"
+.byte 0x0a,0x0d
+.byte 0x00
+
+vgabios_license:
+.ascii "This VGA/VBE Bios is released under the GNU LGPL"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+vgabios_website:
+.ascii "Please visit :"
+.byte 0x0a,0x0d
+;;.ascii " . http://www.plex86.org"
+;;.byte 0x0a,0x0d
+.ascii " . http://bochs.sourceforge.net"
+.byte 0x0a,0x0d
+.ascii " . http://www.nongnu.org/vgabios"
+.byte 0x0a,0x0d
+.byte 0x0a,0x0d
+.byte 0x00
+
+#ifdef PCIBIOS
+vgabios_pci_data:
+.ascii "PCIR"
+#ifdef CIRRUS
+.word 0x1013
+.word 0x00b8 // CLGD5446
+#else
+#error "Unknown PCI vendor and device id"
+#endif
+.word 0 // reserved
+.word 0x18 // dlen
+.byte 0 // revision
+.byte 0x0 // class,hi: vga display
+.word 0x300 // class,lo: vga display
+.word 0x40 // bios size
+.word 1 // revision
+.byte 0 // intel x86 data
+.byte 0x80 // last image
+.word 0 // reserved
+#endif
+
+
+;; ============================================================================================
+;;
+;; Init Entry point
+;;
+;; ============================================================================================
+vgabios_init_func:
+
+;; init vga card
+ call init_vga_card
+
+;; init basic bios vars
+ call init_bios_area
+
+#ifdef VBE
+;; init vbe functions
+ call vbe_init
+#endif
+
+;; set int10 vect
+ SET_INT_VECTOR(0x10, #0xC000, #vgabios_int10_handler)
+
+#ifdef CIRRUS
+ call cirrus_init
+#endif
+
+;; display splash screen
+ call _display_splash_screen
+
+;; init video mode and clear the screen
+ mov ax,#0x0003
+ int #0x10
+
+;; show info
+ call _display_info
+
+#ifdef VBE
+;; show vbe info
+ call vbe_display_info
+#endif
+
+#ifdef CIRRUS
+;; show cirrus info
+ call cirrus_display_info
+#endif
+
+ retf
+ASM_END
+
+/*
+ * int10 handled here
+ */
+ASM_START
+vgabios_int10_handler:
+ pushf
+#ifdef DEBUG
+ push es
+ push ds
+ pusha
+ mov bx, #0xc000
+ mov ds, bx
+ call _int10_debugmsg
+ popa
+ pop ds
+ pop es
+#endif
+ cmp ah, #0x0f
+ jne int10_test_1A
+ call biosfn_get_video_mode
+ jmp int10_end
+int10_test_1A:
+ cmp ah, #0x1a
+ jne int10_test_0B
+ call biosfn_group_1A
+ jmp int10_end
+int10_test_0B:
+ cmp ah, #0x0b
+ jne int10_test_1103
+ call biosfn_group_0B
+ jmp int10_end
+int10_test_1103:
+ cmp ax, #0x1103
+ jne int10_test_12
+ call biosfn_set_text_block_specifier
+ jmp int10_end
+int10_test_12:
+ cmp ah, #0x12
+ jne int10_test_101B
+ cmp bl, #0x10
+ jne int10_test_BL30
+ call biosfn_get_ega_info
+ jmp int10_end
+int10_test_BL30:
+ cmp bl, #0x30
+ jne int10_test_BL31
+ call biosfn_select_vert_res
+ jmp int10_end
+int10_test_BL31:
+ cmp bl, #0x31
+ jne int10_test_BL32
+ call biosfn_enable_default_palette_loading
+ jmp int10_end
+int10_test_BL32:
+ cmp bl, #0x32
+ jne int10_test_BL33
+ call biosfn_enable_video_addressing
+ jmp int10_end
+int10_test_BL33:
+ cmp bl, #0x33
+ jne int10_test_BL34
+ call biosfn_enable_grayscale_summing
+ jmp int10_end
+int10_test_BL34:
+ cmp bl, #0x34
+ jne int10_normal
+ call biosfn_enable_cursor_emulation
+ jmp int10_end
+int10_test_101B:
+ cmp ax, #0x101b
+ je int10_normal
+ cmp ah, #0x10
+#ifndef VBE
+ jne int10_normal
+#else
+ jne int10_test_4F
+#endif
+ call biosfn_group_10
+ jmp int10_end
+#ifdef VBE
+int10_test_4F:
+ cmp ah, #0x4f
+ jne int10_normal
+ cmp al, #0x03
+ jne int10_test_vbe_05
+ call vbe_biosfn_return_current_mode
+ jmp int10_end
+int10_test_vbe_05:
+ cmp al, #0x05
+ jne int10_test_vbe_06
+ call vbe_biosfn_display_window_control
+ jmp int10_end
+int10_test_vbe_06:
+ cmp al, #0x06
+ jne int10_test_vbe_07
+ call vbe_biosfn_set_get_logical_scan_line_length
+ jmp int10_end
+int10_test_vbe_07:
+ cmp al, #0x07
+ jne int10_test_vbe_08
+ call vbe_biosfn_set_get_display_start
+ jmp int10_end
+int10_test_vbe_08:
+ cmp al, #0x08
+ jne int10_test_vbe_0A
+ call vbe_biosfn_set_get_dac_palette_format
+ jmp int10_end
+int10_test_vbe_0A:
+ cmp al, #0x0A
+ jne int10_normal
+ call vbe_biosfn_return_protected_mode_interface
+ jmp int10_end
+#endif
+
+int10_normal:
+ push es
+ push ds
+ pusha
+
+;; We have to set ds to access the right data segment
+ mov bx, #0xc000
+ mov ds, bx
+ call _int10_func
+
+ popa
+ pop ds
+ pop es
+int10_end:
+ popf
+ iret
+ASM_END
+
+#include "vgatables.h"
+#include "vgafonts.h"
+
+/*
+ * Boot time harware inits
+ */
+ASM_START
+init_vga_card:
+;; switch to color mode and enable CPU access 480 lines
+ mov dx, #0x3C2
+ mov al, #0xC3
+ outb dx,al
+
+;; more than 64k 3C4/04
+ mov dx, #0x3C4
+ mov al, #0x04
+ outb dx,al
+ mov dx, #0x3C5
+ mov al, #0x02
+ outb dx,al
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+ mov bx, #msg_vga_init
+ push bx
+ call _printf
+#endif
+ inc sp
+ inc sp
+ ret
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+msg_vga_init:
+.ascii "VGABios $Id$"
+.byte 0x0d,0x0a,0x00
+#endif
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+/*
+ * Boot time bios area inits
+ */
+ASM_START
+init_bios_area:
+ push ds
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+
+;; init detected hardware BIOS Area
+ mov bx, # BIOSMEM_INITIAL_MODE
+ mov ax, [bx]
+ and ax, #0xffcf
+;; set 80x25 color (not clear from RBIL but usual)
+ or ax, #0x0020
+ mov [bx], ax
+
+;; Just for the first int10 find its children
+
+;; the default char height
+ mov bx, # BIOSMEM_CHAR_HEIGHT
+ mov al, #0x10
+ mov [bx], al
+
+;; Clear the screen
+ mov bx, # BIOSMEM_VIDEO_CTL
+ mov al, #0x60
+ mov [bx], al
+
+;; Set the basic screen we have
+ mov bx, # BIOSMEM_SWITCHES
+ mov al, #0xf9
+ mov [bx], al
+
+;; Set the basic modeset options
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, #0x51
+ mov [bx], al
+
+;; Set the default MSR
+ mov bx, # BIOSMEM_CURRENT_MSR
+ mov al, #0x09
+ mov [bx], al
+
+ pop ds
+ ret
+
+_video_save_pointer_table:
+ .word _video_param_table
+ .word 0xc000
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ .word 0 /* XXX: fill it */
+ .word 0
+
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+/*
+ * Boot time Splash screen
+ */
+static void display_splash_screen()
+{
+}
+
+// --------------------------------------------------------------------------------------------
+/*
+ * Tell who we are
+ */
+
+static void display_info()
+{
+ASM_START
+ mov ax,#0xc000
+ mov ds,ax
+ mov si,#vgabios_name
+ call _display_string
+ mov si,#vgabios_version
+ call _display_string
+
+ ;;mov si,#vgabios_copyright
+ ;;call _display_string
+ ;;mov si,#crlf
+ ;;call _display_string
+
+ mov si,#vgabios_license
+ call _display_string
+ mov si,#vgabios_website
+ call _display_string
+ASM_END
+}
+
+static void display_string()
+{
+ // Get length of string
+ASM_START
+ mov ax,ds
+ mov es,ax
+ mov di,si
+ xor cx,cx
+ not cx
+ xor al,al
+ cld
+ repne
+ scasb
+ not cx
+ dec cx
+ push cx
+
+ mov ax,#0x0300
+ mov bx,#0x0000
+ int #0x10
+
+ pop cx
+ mov ax,#0x1301
+ mov bx,#0x000b
+ mov bp,si
+ int #0x10
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+#ifdef DEBUG
+static void int10_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+ Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+ // 0E is write char...
+ if(GET_AH()!=0x0E)
+ printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX);
+}
+#endif
+
+// --------------------------------------------------------------------------------------------
+/*
+ * int10 main dispatcher
+ */
+static void int10_func(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+ Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+
+ // BIOS functions
+ switch(GET_AH())
+ {
+ case 0x00:
+ biosfn_set_video_mode(GET_AL());
+ switch(GET_AL()&0x7F)
+ {case 6:
+ SET_AL(0x3F);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 7:
+ SET_AL(0x30);
+ break;
+ default:
+ SET_AL(0x20);
+ }
+ break;
+ case 0x01:
+ biosfn_set_cursor_shape(GET_CH(),GET_CL());
+ break;
+ case 0x02:
+ biosfn_set_cursor_pos(GET_BH(),DX);
+ break;
+ case 0x03:
+ biosfn_get_cursor_pos(GET_BH(),&CX,&DX);
+ break;
+ case 0x04:
+ // Read light pen pos (unimplemented)
+#ifdef DEBUG
+ unimplemented();
+#endif
+ AX=0x00;
+ BX=0x00;
+ CX=0x00;
+ DX=0x00;
+ break;
+ case 0x05:
+ biosfn_set_active_page(GET_AL());
+ break;
+ case 0x06:
+ biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_UP);
+ break;
+ case 0x07:
+ biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_DOWN);
+ break;
+ case 0x08:
+ biosfn_read_char_attr(GET_BH(),&AX);
+ break;
+ case 0x09:
+ biosfn_write_char_attr(GET_AL(),GET_BH(),GET_BL(),CX);
+ break;
+ case 0x0A:
+ biosfn_write_char_only(GET_AL(),GET_BH(),GET_BL(),CX);
+ break;
+ case 0x0C:
+ biosfn_write_pixel(GET_BH(),GET_AL(),CX,DX);
+ break;
+ case 0x0D:
+ biosfn_read_pixel(GET_BH(),CX,DX,&AX);
+ break;
+ case 0x0E:
+ // Ralf Brown Interrupt list is WRONG on bh(page)
+ // We do output only on the current page !
+ biosfn_write_teletype(GET_AL(),0xff,GET_BL(),NO_ATTR);
+ break;
+ case 0x10:
+ // All other functions of group AH=0x10 rewritten in assembler
+ biosfn_perform_gray_scale_summing(BX,CX);
+ break;
+ case 0x11:
+ switch(GET_AL())
+ {
+ case 0x00:
+ case 0x10:
+ biosfn_load_text_user_pat(GET_AL(),ES,BP,CX,DX,GET_BL(),GET_BH());
+ break;
+ case 0x01:
+ case 0x11:
+ biosfn_load_text_8_14_pat(GET_AL(),GET_BL());
+ break;
+ case 0x02:
+ case 0x12:
+ biosfn_load_text_8_8_pat(GET_AL(),GET_BL());
+ break;
+ case 0x04:
+ case 0x14:
+ biosfn_load_text_8_16_pat(GET_AL(),GET_BL());
+ break;
+ case 0x20:
+ biosfn_load_gfx_8_8_chars(ES,BP);
+ break;
+ case 0x21:
+ biosfn_load_gfx_user_chars(ES,BP,CX,GET_BL(),GET_DL());
+ break;
+ case 0x22:
+ biosfn_load_gfx_8_14_chars(GET_BL());
+ break;
+ case 0x23:
+ biosfn_load_gfx_8_8_dd_chars(GET_BL());
+ break;
+ case 0x24:
+ biosfn_load_gfx_8_16_chars(GET_BL());
+ break;
+ case 0x30:
+ biosfn_get_font_info(GET_BH(),&ES,&BP,&CX,&DX);
+ break;
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+
+ break;
+ case 0x12:
+ switch(GET_BL())
+ {
+ case 0x20:
+ biosfn_alternate_prtsc();
+ break;
+ case 0x35:
+ biosfn_switch_video_interface(GET_AL(),ES,DX);
+ SET_AL(0x12);
+ break;
+ case 0x36:
+ biosfn_enable_video_refresh_control(GET_AL());
+ SET_AL(0x12);
+ break;
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+ break;
+ case 0x13:
+ biosfn_write_string(GET_AL(),GET_BH(),GET_BL(),CX,GET_DH(),GET_DL(),ES,BP);
+ break;
+ case 0x1B:
+ biosfn_read_state_info(BX,ES,DI);
+ SET_AL(0x1B);
+ break;
+ case 0x1C:
+ switch(GET_AL())
+ {
+ case 0x00:
+ biosfn_read_video_state_size(CX,&BX);
+ break;
+ case 0x01:
+ biosfn_save_video_state(CX,ES,BX);
+ break;
+ case 0x02:
+ biosfn_restore_video_state(CX,ES,BX);
+ break;
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+ SET_AL(0x1C);
+ break;
+
+#ifdef VBE
+ case 0x4f:
+ if (vbe_has_vbe_display()) {
+ switch(GET_AL())
+ {
+ case 0x00:
+ vbe_biosfn_return_controller_information(&AX,ES,DI);
+ break;
+ case 0x01:
+ vbe_biosfn_return_mode_information(&AX,CX,ES,DI);
+ break;
+ case 0x02:
+ vbe_biosfn_set_mode(&AX,BX,ES,DI);
+ break;
+ case 0x04:
+ vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX);
+ break;
+ case 0x09:
+ //FIXME
+#ifdef DEBUG
+ unimplemented();
+#endif
+ // function failed
+ AX=0x100;
+ break;
+ case 0x0A:
+ //FIXME
+#ifdef DEBUG
+ unimplemented();
+#endif
+ // function failed
+ AX=0x100;
+ break;
+ default:
+#ifdef DEBUG
+ unknown();
+#endif
+ // function failed
+ AX=0x100;
+ }
+ }
+ else {
+ // No VBE display
+ AX=0x0100;
+ }
+ break;
+#endif
+
+#ifdef DEBUG
+ default:
+ unknown();
+#endif
+ }
+}
+
+// ============================================================================================
+//
+// BIOS functions
+//
+// ============================================================================================
+
+static void biosfn_set_video_mode(mode) Bit8u mode;
+{// mode: Bit 7 is 1 if no clear screen
+
+ // Should we clear the screen ?
+ Bit8u noclearmem=mode&0x80;
+ Bit8u line,mmask,*palette,vpti;
+ Bit16u i,twidth,theightm1,cheight;
+ Bit8u modeset_ctl,video_ctl,vga_switches;
+ Bit16u crtc_addr;
+
+#ifdef VBE
+ if (vbe_has_vbe_display()) {
+ dispi_set_enable(VBE_DISPI_DISABLED);
+ }
+#endif // def VBE
+
+ // The real mode
+ mode=mode&0x7f;
+
+ // find the entry in the video modes
+ line=find_vga_entry(mode);
+
+#ifdef DEBUG
+ printf("mode search %02x found line %02x\n",mode,line);
+#endif
+
+ if(line==0xFF)
+ return;
+
+ vpti=line_to_vpti[line];
+ twidth=video_param_table[vpti].twidth;
+ theightm1=video_param_table[vpti].theightm1;
+ cheight=video_param_table[vpti].cheight;
+
+ // Read the bios vga control
+ video_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL);
+
+ // Read the bios vga switches
+ vga_switches=read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES);
+
+ // Read the bios mode set control
+ modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+
+ // Then we know the number of lines
+// FIXME
+
+ // if palette loading (bit 3 of modeset ctl = 0)
+ if((modeset_ctl&0x08)==0)
+ {// Set the PEL mask
+ outb(VGAREG_PEL_MASK,vga_modes[line].pelmask);
+
+ // Set the whole dac always, from 0
+ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+
+ // From which palette
+ switch(vga_modes[line].dacmodel)
+ {case 0:
+ palette=&palette0;
+ break;
+ case 1:
+ palette=&palette1;
+ break;
+ case 2:
+ palette=&palette2;
+ break;
+ case 3:
+ palette=&palette3;
+ break;
+ }
+ // Always 256*3 values
+ for(i=0;i<0x0100;i++)
+ {if(i<=dac_regs[vga_modes[line].dacmodel])
+ {outb(VGAREG_DAC_DATA,palette[(i*3)+0]);
+ outb(VGAREG_DAC_DATA,palette[(i*3)+1]);
+ outb(VGAREG_DAC_DATA,palette[(i*3)+2]);
+ }
+ else
+ {outb(VGAREG_DAC_DATA,0);
+ outb(VGAREG_DAC_DATA,0);
+ outb(VGAREG_DAC_DATA,0);
+ }
+ }
+ if((modeset_ctl&0x02)==0x02)
+ {
+ biosfn_perform_gray_scale_summing(0x00, 0x100);
+ }
+ }
+
+ // Reset Attribute Ctl flip-flop
+ inb(VGAREG_ACTL_RESET);
+
+ // Set Attribute Ctl
+ for(i=0;i<=0x13;i++)
+ {outb(VGAREG_ACTL_ADDRESS,i);
+ outb(VGAREG_ACTL_WRITE_DATA,video_param_table[vpti].actl_regs[i]);
+ }
+ outb(VGAREG_ACTL_ADDRESS,0x14);
+ outb(VGAREG_ACTL_WRITE_DATA,0x00);
+
+ // Set Sequencer Ctl
+ outb(VGAREG_SEQU_ADDRESS,0);
+ outb(VGAREG_SEQU_DATA,0x03);
+ for(i=1;i<=4;i++)
+ {outb(VGAREG_SEQU_ADDRESS,i);
+ outb(VGAREG_SEQU_DATA,video_param_table[vpti].sequ_regs[i - 1]);
+ }
+
+ // Set Grafx Ctl
+ for(i=0;i<=8;i++)
+ {outb(VGAREG_GRDC_ADDRESS,i);
+ outb(VGAREG_GRDC_DATA,video_param_table[vpti].grdc_regs[i]);
+ }
+
+ // Set CRTC address VGA or MDA
+ crtc_addr=vga_modes[line].memmodel==MTEXT?VGAREG_MDA_CRTC_ADDRESS:VGAREG_VGA_CRTC_ADDRESS;
+
+ // Disable CRTC write protection
+ outw(crtc_addr,0x0011);
+ // Set CRTC regs
+ for(i=0;i<=0x18;i++)
+ {outb(crtc_addr,i);
+ outb(crtc_addr+1,video_param_table[vpti].crtc_regs[i]);
+ }
+
+ // Set the misc register
+ outb(VGAREG_WRITE_MISC_OUTPUT,video_param_table[vpti].miscreg);
+
+ // Enable video
+ outb(VGAREG_ACTL_ADDRESS,0x20);
+ inb(VGAREG_ACTL_RESET);
+
+ if(noclearmem==0x00)
+ {
+ if(vga_modes[line].class==TEXT)
+ {
+ memsetw(vga_modes[line].sstart,0,0x0720,0x4000); // 32k
+ }
+ else
+ {
+ if(mode<0x0d)
+ {
+ memsetw(vga_modes[line].sstart,0,0x0000,0x4000); // 32k
+ }
+ else
+ {
+ outb( VGAREG_SEQU_ADDRESS, 0x02 );
+ mmask = inb( VGAREG_SEQU_DATA );
+ outb( VGAREG_SEQU_DATA, 0x0f ); // all planes
+ memsetw(vga_modes[line].sstart,0,0x0000,0x8000); // 64k
+ outb( VGAREG_SEQU_DATA, mmask );
+ }
+ }
+ }
+
+ // Set the BIOS mem
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,mode);
+ write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS,twidth);
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,*(Bit16u *)&video_param_table[vpti].slength_l);
+ write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,crtc_addr);
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theightm1);
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,cheight);
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|noclearmem));
+ write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9);
+ write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x7f);
+
+ // FIXME We nearly have the good tables. to be reworked
+ write_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x08); // 8 is VGA should be ok for now
+ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER, video_save_pointer_table);
+ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2, 0xc000);
+
+ // FIXME
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x00); // Unavailable on vanilla vga, but...
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x00); // Unavailable on vanilla vga, but...
+
+ // Set cursor shape
+ if(vga_modes[line].class==TEXT)
+ {
+ biosfn_set_cursor_shape(0x06,0x07);
+ }
+
+ // Set cursor pos for page 0..7
+ for(i=0;i<8;i++)
+ biosfn_set_cursor_pos(i,0x0000);
+
+ // Set active page 0
+ biosfn_set_active_page(0x00);
+
+ // Write the fonts in memory
+ if(vga_modes[line].class==TEXT)
+ {
+ASM_START
+ ;; copy and activate 8x16 font
+ mov ax, #0x1104
+ mov bl, #0x00
+ int #0x10
+ mov ax, #0x1103
+ mov bl, #0x00
+ int #0x10
+ASM_END
+ }
+
+ // Set the ints 0x1F and 0x43
+ASM_START
+ SET_INT_VECTOR(0x1f, #0xC000, #_vgafont8+128*8)
+ASM_END
+
+ switch(cheight)
+ {case 8:
+ASM_START
+ SET_INT_VECTOR(0x43, #0xC000, #_vgafont8)
+ASM_END
+ break;
+ case 14:
+ASM_START
+ SET_INT_VECTOR(0x43, #0xC000, #_vgafont14)
+ASM_END
+ break;
+ case 16:
+ASM_START
+ SET_INT_VECTOR(0x43, #0xC000, #_vgafont16)
+ASM_END
+ break;
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_cursor_shape (CH,CL)
+Bit8u CH;Bit8u CL;
+{Bit16u cheight,curs,crtc_addr;
+ Bit8u modeset_ctl;
+
+ CH&=0x3f;
+ CL&=0x1f;
+
+ curs=(CH<<8)+CL;
+ write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,curs);
+
+ modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+ cheight = read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ if((modeset_ctl&0x01) && (cheight>8) && (CL<8) && (CH<0x20))
+ {
+ if(CL!=(CH+1))
+ {
+ CH = ((CH+1) * cheight / 8) -1;
+ }
+ else
+ {
+ CH = ((CL+1) * cheight / 8) - 2;
+ }
+ CL = ((CL+1) * cheight / 8) - 1;
+ }
+
+ // CTRC regs 0x0a and 0x0b
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0a);
+ outb(crtc_addr+1,CH);
+ outb(crtc_addr,0x0b);
+ outb(crtc_addr+1,CL);
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_cursor_pos (page, cursor)
+Bit8u page;Bit16u cursor;
+{
+ Bit8u xcurs,ycurs,current;
+ Bit16u nbcols,nbrows,address,crtc_addr;
+
+ // Should not happen...
+ if(page>7)return;
+
+ // Bios cursor pos
+ write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*page, cursor);
+
+ // Set the hardware cursor
+ current=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ if(page==current)
+ {
+ // Get the dimensions
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Calculate the address knowing nbcols nbrows and page num
+ address=SCREEN_IO_START(nbcols,nbrows,page)+xcurs+ycurs*nbcols;
+
+ // CRTC regs 0x0e and 0x0f
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0e);
+ outb(crtc_addr+1,(address&0xff00)>>8);
+ outb(crtc_addr,0x0f);
+ outb(crtc_addr+1,address&0x00ff);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_get_cursor_pos (page,shape, pos)
+Bit8u page;Bit16u *shape;Bit16u *pos;
+{
+ Bit16u ss=get_SS();
+
+ // Default
+ write_word(ss, shape, 0);
+ write_word(ss, pos, 0);
+
+ if(page>7)return;
+ // FIXME should handle VGA 14/16 lines
+ write_word(ss,shape,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE));
+ write_word(ss,pos,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2));
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_active_page (page)
+Bit8u page;
+{
+ Bit16u cursor,dummy,crtc_addr;
+ Bit16u nbcols,nbrows,address;
+ Bit8u mode,line;
+
+ if(page>7)return;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get pos curs pos for the right page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Get the dimensions
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+
+ // Calculate the address knowing nbcols nbrows and page num
+ address=SCREEN_MEM_START(nbcols,nbrows,page);
+ write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START,address);
+
+ // Start address
+ address=SCREEN_IO_START(nbcols,nbrows,page);
+ }
+ else
+ {
+ address = page * (*(Bit16u *)&video_param_table[line_to_vpti[line]].slength_l);
+ }
+
+ // CRTC regs 0x0c and 0x0d
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0c);
+ outb(crtc_addr+1,(address&0xff00)>>8);
+ outb(crtc_addr,0x0d);
+ outb(crtc_addr+1,address&0x00ff);
+
+ // And change the BIOS page
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page);
+
+#ifdef DEBUG
+ printf("Set active page %02x address %04x\n",page,address);
+#endif
+
+ // Display the cursor, now the page is active
+ biosfn_set_cursor_pos(page,cursor);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight)
+Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight;
+{
+ Bit16u src,dest;
+ Bit8u i;
+
+ src=ysrc*cheight*nbcols+xstart;
+ dest=ydest*cheight*nbcols+xstart;
+ outw(VGAREG_GRDC_ADDRESS, 0x0105);
+ for(i=0;i<cheight;i++)
+ {
+ memcpyb(0xa000,dest+i*nbcols,0xa000,src+i*nbcols,cols);
+ }
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_fill_pl4(xstart,ystart,cols,nbcols,cheight,attr)
+Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr;
+{
+ Bit16u dest;
+ Bit8u i;
+
+ dest=ystart*cheight*nbcols+xstart;
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ for(i=0;i<cheight;i++)
+ {
+ memsetb(0xa000,dest+i*nbcols,attr,cols);
+ }
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_copy_cga(xstart,ysrc,ydest,cols,nbcols,cheight)
+Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight;
+{
+ Bit16u src,dest;
+ Bit8u i;
+
+ src=((ysrc*cheight*nbcols)>>1)+xstart;
+ dest=((ydest*cheight*nbcols)>>1)+xstart;
+ for(i=0;i<cheight;i++)
+ {
+ if (i & 1)
+ memcpyb(0xb800,0x2000+dest+(i>>1)*nbcols,0xb800,0x2000+src+(i>>1)*nbcols,cols);
+ else
+ memcpyb(0xb800,dest+(i>>1)*nbcols,0xb800,src+(i>>1)*nbcols,cols);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_fill_cga(xstart,ystart,cols,nbcols,cheight,attr)
+Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr;
+{
+ Bit16u dest;
+ Bit8u i;
+
+ dest=((ystart*cheight*nbcols)>>1)+xstart;
+ for(i=0;i<cheight;i++)
+ {
+ if (i & 1)
+ memsetb(0xb800,0x2000+dest+(i>>1)*nbcols,attr,cols);
+ else
+ memsetb(0xb800,dest+(i>>1)*nbcols,attr,cols);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_scroll (nblines,attr,rul,cul,rlr,clr,page,dir)
+Bit8u nblines;Bit8u attr;Bit8u rul;Bit8u cul;Bit8u rlr;Bit8u clr;Bit8u page;Bit8u dir;
+{
+ // page == 0xFF if current
+
+ Bit8u mode,line,cheight,bpp,cols;
+ Bit16u nbcols,nbrows,i;
+ Bit16u address;
+
+ if(rul>rlr)return;
+ if(cul>clr)return;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ // Get the current page
+ if(page==0xFF)
+ page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ if(rlr>=nbrows)rlr=nbrows-1;
+ if(clr>=nbcols)clr=nbcols-1;
+ if(nblines>nbrows)nblines=0;
+ cols=clr-cul+1;
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page);
+#ifdef DEBUG
+ printf("Scroll, address %04x (%04x %04x %02x)\n",address,nbrows,nbcols,page);
+#endif
+
+ if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+ {
+ memsetw(vga_modes[line].sstart,address,(Bit16u)attr*0x100+' ',nbrows*nbcols);
+ }
+ else
+ {// if Scroll up
+ if(dir==SCROLL_UP)
+ {for(i=rul;i<=rlr;i++)
+ {
+ if((i+nblines>rlr)||(nblines==0))
+ memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols);
+ else
+ memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i+nblines)*nbcols+cul)*2,cols);
+ }
+ }
+ else
+ {for(i=rlr;i>=rul;i--)
+ {
+ if((i<rul+nblines)||(nblines==0))
+ memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols);
+ else
+ memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i-nblines)*nbcols+cul)*2,cols);
+ if (i>rlr) break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight);
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+ }
+ else
+ {// if Scroll up
+ if(dir==SCROLL_UP)
+ {for(i=rul;i<=rlr;i++)
+ {
+ if((i+nblines>rlr)||(nblines==0))
+ vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight);
+ }
+ }
+ else
+ {for(i=rlr;i>=rul;i--)
+ {
+ if((i<rul+nblines)||(nblines==0))
+ vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_pl4(cul,i,i-nblines,cols,nbcols,cheight);
+ if (i>rlr) break;
+ }
+ }
+ }
+ break;
+ case CGA:
+ bpp=vga_modes[line].pixbits;
+ if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+ {
+ memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight*bpp);
+ }
+ else
+ {
+ if(bpp==2)
+ {
+ cul<<=1;
+ cols<<=1;
+ nbcols<<=1;
+ }
+ // if Scroll up
+ if(dir==SCROLL_UP)
+ {for(i=rul;i<=rlr;i++)
+ {
+ if((i+nblines>rlr)||(nblines==0))
+ vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_cga(cul,i+nblines,i,cols,nbcols,cheight);
+ }
+ }
+ else
+ {for(i=rlr;i>=rul;i--)
+ {
+ if((i<rul+nblines)||(nblines==0))
+ vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr);
+ else
+ vgamem_copy_cga(cul,i,i-nblines,cols,nbcols,cheight);
+ if (i>rlr) break;
+ }
+ }
+ }
+ break;
+#ifdef DEBUG
+ default:
+ printf("Scroll in graphics mode ");
+ unimplemented();
+#endif
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_char_attr (page,car)
+Bit8u page;Bit16u *car;
+{Bit16u ss=get_SS();
+ Bit8u xcurs,ycurs,mode,line;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ write_word(ss,car,read_word(vga_modes[line].sstart,address));
+ }
+ else
+ {
+ // FIXME gfx mode
+#ifdef DEBUG
+ unimplemented();
+#endif
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u cheight;
+{
+ Bit8u i,j,mask;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ switch(cheight)
+ {case 14:
+ fdata = &vgafont14;
+ break;
+ case 16:
+ fdata = &vgafont16;
+ break;
+ default:
+ fdata = &vgafont8;
+ }
+ addr=xcurs+ycurs*cheight*nbcols;
+ src = car * cheight;
+ outw(VGAREG_SEQU_ADDRESS, 0x0f02);
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ if(attr&0x80)
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x1803);
+ }
+ else
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x0003);
+ }
+ for(i=0;i<cheight;i++)
+ {
+ dest=addr+i*nbcols;
+ for(j=0;j<8;j++)
+ {
+ mask=0x80>>j;
+ outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
+ read_byte(0xa000,dest);
+ if(fdata[src+i]&mask)
+ {
+ write_byte(0xa000,dest,attr&0x0f);
+ }
+ else
+ {
+ write_byte(0xa000,dest,0x00);
+ }
+ }
+ }
+ASM_START
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0xff08
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0003
+ out dx, ax
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u bpp;
+{
+ Bit8u i,j,mask,data;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ fdata = &vgafont8;
+ addr=(xcurs*bpp)+ycurs*320;
+ src = car * 8;
+ for(i=0;i<8;i++)
+ {
+ dest=addr+(i>>1)*80;
+ if (i & 1) dest += 0x2000;
+ mask = 0x80;
+ if (bpp == 1)
+ {
+ if (attr & 0x80)
+ {
+ data = read_byte(0xb800,dest);
+ }
+ else
+ {
+ data = 0x00;
+ }
+ for(j=0;j<8;j++)
+ {
+ if (fdata[src+i] & mask)
+ {
+ if (attr & 0x80)
+ {
+ data ^= (attr & 0x01) << (7-j);
+ }
+ else
+ {
+ data |= (attr & 0x01) << (7-j);
+ }
+ }
+ mask >>= 1;
+ }
+ write_byte(0xb800,dest,data);
+ }
+ else
+ {
+ while (mask > 0)
+ {
+ if (attr & 0x80)
+ {
+ data = read_byte(0xb800,dest);
+ }
+ else
+ {
+ data = 0x00;
+ }
+ for(j=0;j<4;j++)
+ {
+ if (fdata[src+i] & mask)
+ {
+ if (attr & 0x80)
+ {
+ data ^= (attr & 0x03) << ((3-j)*2);
+ }
+ else
+ {
+ data |= (attr & 0x03) << ((3-j)*2);
+ }
+ }
+ mask >>= 1;
+ }
+ write_byte(0xb800,dest,data);
+ dest += 1;
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;
+{
+ Bit8u i,j,mask,data;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ fdata = &vgafont8;
+ addr=xcurs*8+ycurs*nbcols*64;
+ src = car * 8;
+ for(i=0;i<8;i++)
+ {
+ dest=addr+i*nbcols*8;
+ mask = 0x80;
+ for(j=0;j<8;j++)
+ {
+ data = 0x00;
+ if (fdata[src+i] & mask)
+ {
+ data = attr;
+ }
+ write_byte(0xa000,dest+j,data);
+ mask >>= 1;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_char_attr (car,page,attr,count)
+Bit8u car;Bit8u page;Bit8u attr;Bit16u count;
+{
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ dummy=((Bit16u)attr<<8)+car;
+ memsetw(vga_modes[line].sstart,address,dummy,count);
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ bpp=vga_modes[line].pixbits;
+ while((count-->0) && (xcurs<nbcols))
+ {
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+ break;
+ case CGA:
+ write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+ break;
+ case LINEAR8:
+ write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+ xcurs++;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_char_only (car,page,attr,count)
+Bit8u car;Bit8u page;Bit8u attr;Bit16u count;
+{
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ while(count-->0)
+ {write_byte(vga_modes[line].sstart,address,car);
+ address+=2;
+ }
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ bpp=vga_modes[line].pixbits;
+ while((count-->0) && (xcurs<nbcols))
+ {
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+ break;
+ case CGA:
+ write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+ break;
+ case LINEAR8:
+ write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+ xcurs++;
+ }
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_0B:
+ cmp bh, #0x00
+ je biosfn_set_border_color
+ cmp bh, #0x01
+ je biosfn_set_palette
+#ifdef DEBUG
+ call _unknown
+#endif
+ ret
+biosfn_set_border_color:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x00
+ out dx, al
+ mov al, bl
+ and al, #0x0f
+ test al, #0x08
+ jz set_low_border
+ add al, #0x08
+set_low_border:
+ out dx, al
+ mov cl, #0x01
+ and bl, #0x10
+set_intensity_loop:
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, cl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and al, #0xef
+ or al, bl
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ inc cl
+ cmp cl, #0x04
+ jne set_intensity_loop
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+biosfn_set_palette:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov cl, #0x01
+ and bl, #0x01
+set_cga_palette_loop:
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, cl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and al, #0xfe
+ or al, bl
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ inc cl
+ cmp cl, #0x04
+ jne set_cga_palette_loop
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_pixel (BH,AL,CX,DX) Bit8u BH;Bit8u AL;Bit16u CX;Bit16u DX;
+{
+ Bit8u mode,line,mask,attr,data;
+ Bit16u addr;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+ if(vga_modes[line].class==TEXT)return;
+
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ mask = 0x80 >> (CX & 0x07);
+ outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ data = read_byte(0xa000,addr);
+ if (AL & 0x80)
+ {
+ outw(VGAREG_GRDC_ADDRESS, 0x1803);
+ }
+ write_byte(0xa000,addr,AL);
+ASM_START
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0xff08
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0003
+ out dx, ax
+ASM_END
+ break;
+ case CGA:
+ if(vga_modes[line].pixbits==2)
+ {
+ addr=(CX>>2)+(DX>>1)*80;
+ }
+ else
+ {
+ addr=(CX>>3)+(DX>>1)*80;
+ }
+ if (DX & 1) addr += 0x2000;
+ data = read_byte(0xb800,addr);
+ if(vga_modes[line].pixbits==2)
+ {
+ attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
+ mask = 0x03 << ((3 - (CX & 0x03)) * 2);
+ }
+ else
+ {
+ attr = (AL & 0x01) << (7 - (CX & 0x07));
+ mask = 0x01 << (7 - (CX & 0x07));
+ }
+ if (AL & 0x80)
+ {
+ data ^= attr;
+ }
+ else
+ {
+ data &= ~mask;
+ data |= attr;
+ }
+ write_byte(0xb800,addr,data);
+ break;
+ case LINEAR8:
+ addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ write_byte(0xa000,addr,AL);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_pixel (BH,CX,DX,AX) Bit8u BH;Bit16u CX;Bit16u DX;Bit16u *AX;
+{
+ Bit8u mode,line,mask,attr,data,i;
+ Bit16u addr;
+ Bit16u ss=get_SS();
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+ if(vga_modes[line].class==TEXT)return;
+
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ mask = 0x80 >> (CX & 0x07);
+ attr = 0x00;
+ for(i=0;i<4;i++)
+ {
+ outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04);
+ data = read_byte(0xa000,addr) & mask;
+ if (data > 0) attr |= (0x01 << i);
+ }
+ break;
+ case CGA:
+ addr=(CX>>2)+(DX>>1)*80;
+ if (DX & 1) addr += 0x2000;
+ data = read_byte(0xb800,addr);
+ if(vga_modes[line].pixbits==2)
+ {
+ attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
+ }
+ else
+ {
+ attr = (data >> (7 - (CX & 0x07))) & 0x01;
+ }
+ break;
+ case LINEAR8:
+ addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+ attr=read_byte(0xa000,addr);
+ break;
+ default:
+#ifdef DEBUG
+ unimplemented();
+#endif
+ attr = 0;
+ }
+ write_word(ss,AX,(read_word(ss,AX) & 0xff00) | attr);
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_teletype (car, page, attr, flag)
+Bit8u car;Bit8u page;Bit8u attr;Bit8u flag;
+{// flag = WITH_ATTR / NO_ATTR
+
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // special case if page is 0xff, use current page
+ if(page==0xff)
+ page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ switch(car)
+ {
+ case 7:
+ //FIXME should beep
+ break;
+
+ case 8:
+ if(xcurs>0)xcurs--;
+ break;
+
+ case '\r':
+ xcurs=0;
+ break;
+
+ case '\n':
+ ycurs++;
+ break;
+
+ case '\t':
+ do
+ {
+ biosfn_write_teletype(' ',page,attr,flag);
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+ }while(xcurs%8==0);
+ break;
+
+ default:
+
+ if(vga_modes[line].class==TEXT)
+ {
+ // Compute the address
+ address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+ // Write the char
+ write_byte(vga_modes[line].sstart,address,car);
+
+ if(flag==WITH_ATTR)
+ write_byte(vga_modes[line].sstart,address+1,attr);
+ }
+ else
+ {
+ // FIXME gfx mode not complete
+ cheight=video_param_table[line_to_vpti[line]].cheight;
+ bpp=vga_modes[line].pixbits;
+ switch(vga_modes[line].memmodel)
+ {
+ case PLANAR4:
+ case PLANAR1:
+ write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+ break;
+ case CGA:
+ write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+ break;
+ case LINEAR8:
+ write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+ break;
+#ifdef DEBUG
+ default:
+ unimplemented();
+#endif
+ }
+ }
+ xcurs++;
+ }
+
+ // Do we need to wrap ?
+ if(xcurs==nbcols)
+ {xcurs=0;
+ ycurs++;
+ }
+
+ // Do we need to scroll ?
+ if(ycurs==nbrows)
+ {
+ if(vga_modes[line].class==TEXT)
+ {
+ biosfn_scroll(0x01,0x07,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
+ }
+ else
+ {
+ biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
+ }
+ ycurs-=1;
+ }
+
+ // Set the cursor for the page
+ cursor=ycurs; cursor<<=8; cursor+=xcurs;
+ biosfn_set_cursor_pos(page,cursor);
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_video_mode:
+ push ds
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ push bx
+ mov bx, # BIOSMEM_CURRENT_PAGE
+ mov al, [bx]
+ pop bx
+ mov bh, al
+ push bx
+ mov bx, # BIOSMEM_VIDEO_CTL
+ mov ah, [bx]
+ and ah, #0x80
+ mov bx, # BIOSMEM_CURRENT_MODE
+ mov al, [bx]
+ or al, ah
+ mov bx, # BIOSMEM_NB_COLS
+ mov ah, [bx]
+ pop bx
+ pop ds
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_10:
+ cmp al, #0x00
+ jne int10_test_1001
+ jmp biosfn_set_single_palette_reg
+int10_test_1001:
+ cmp al, #0x01
+ jne int10_test_1002
+ jmp biosfn_set_overscan_border_color
+int10_test_1002:
+ cmp al, #0x02
+ jne int10_test_1003
+ jmp biosfn_set_all_palette_reg
+int10_test_1003:
+ cmp al, #0x03
+ jne int10_test_1007
+ jmp biosfn_toggle_intensity
+int10_test_1007:
+ cmp al, #0x07
+ jne int10_test_1008
+ jmp biosfn_get_single_palette_reg
+int10_test_1008:
+ cmp al, #0x08
+ jne int10_test_1009
+ jmp biosfn_read_overscan_border_color
+int10_test_1009:
+ cmp al, #0x09
+ jne int10_test_1010
+ jmp biosfn_get_all_palette_reg
+int10_test_1010:
+ cmp al, #0x10
+ jne int10_test_1012
+ jmp biosfn_set_single_dac_reg
+int10_test_1012:
+ cmp al, #0x12
+ jne int10_test_1013
+ jmp biosfn_set_all_dac_reg
+int10_test_1013:
+ cmp al, #0x13
+ jne int10_test_1015
+ jmp biosfn_select_video_dac_color_page
+int10_test_1015:
+ cmp al, #0x15
+ jne int10_test_1017
+ jmp biosfn_read_single_dac_reg
+int10_test_1017:
+ cmp al, #0x17
+ jne int10_test_1018
+ jmp biosfn_read_all_dac_reg
+int10_test_1018:
+ cmp al, #0x18
+ jne int10_test_1019
+ jmp biosfn_set_pel_mask
+int10_test_1019:
+ cmp al, #0x19
+ jne int10_test_101A
+ jmp biosfn_read_pel_mask
+int10_test_101A:
+ cmp al, #0x1a
+ jne int10_group_10_unknown
+ jmp biosfn_read_video_dac_state
+int10_group_10_unknown:
+#ifdef DEBUG
+ call _unknown
+#endif
+ ret
+
+biosfn_set_single_palette_reg:
+ cmp bl, #0x14
+ ja no_actl_reg1
+ push ax
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, bl
+ out dx, al
+ mov al, bh
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop ax
+no_actl_reg1:
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_overscan_border_color:
+ push bx
+ mov bl, #0x11
+ call biosfn_set_single_palette_reg
+ pop bx
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_all_palette_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov bx, dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov cl, #0x00
+ mov dx, # VGAREG_ACTL_ADDRESS
+set_palette_loop:
+ mov al, cl
+ out dx, al
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ inc cl
+ cmp cl, #0x10
+ jne set_palette_loop
+ mov al, #0x11
+ out dx, al
+ seg es
+ mov al, [bx]
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_toggle_intensity:
+ push ax
+ push bx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and al, #0xf7
+ and bl, #0x01
+ shl bl, 3
+ or al, bl
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_single_palette_reg:
+ cmp bl, #0x14
+ ja no_actl_reg2
+ push ax
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, bl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ mov bh, al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop ax
+no_actl_reg2:
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_overscan_border_color:
+ push ax
+ push bx
+ mov bl, #0x11
+ call biosfn_get_single_palette_reg
+ mov al, bh
+ pop bx
+ mov bh, al
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_all_palette_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov bx, dx
+ mov cl, #0x00
+get_palette_loop:
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, cl
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ inc cl
+ cmp cl, #0x10
+ jne get_palette_loop
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x11
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ seg es
+ mov [bx], al
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_single_dac_reg:
+ push ax
+ push dx
+ mov dx, # VGAREG_DAC_WRITE_ADDRESS
+ mov al, bl
+ out dx, al
+ mov dx, # VGAREG_DAC_DATA
+ pop ax
+ push ax
+ mov al, ah
+ out dx, al
+ mov al, ch
+ out dx, al
+ mov al, cl
+ out dx, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_all_dac_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_DAC_WRITE_ADDRESS
+ mov al, bl
+ out dx, al
+ pop dx
+ push dx
+ mov bx, dx
+ mov dx, # VGAREG_DAC_DATA
+set_dac_loop:
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ seg es
+ mov al, [bx]
+ out dx, al
+ inc bx
+ dec cx
+ jnz set_dac_loop
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_select_video_dac_color_page:
+ push ax
+ push bx
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ and bl, #0x01
+ jnz set_dac_page
+ and al, #0x7f
+ shl bh, 7
+ or al, bh
+ mov dx, # VGAREG_ACTL_ADDRESS
+ out dx, al
+ jmp set_actl_normal
+set_dac_page:
+ push ax
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x14
+ out dx, al
+ pop ax
+ and al, #0x80
+ jnz set_dac_16_page
+ shl bh, 2
+set_dac_16_page:
+ and bh, #0x0f
+ mov al, bh
+ out dx, al
+set_actl_normal:
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_single_dac_reg:
+ push ax
+ push dx
+ mov dx, # VGAREG_DAC_READ_ADDRESS
+ mov al, bl
+ out dx, al
+ pop ax
+ mov ah, al
+ mov dx, # VGAREG_DAC_DATA
+ in al, dx
+ xchg al, ah
+ push ax
+ in al, dx
+ mov ch, al
+ in al, dx
+ mov cl, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_all_dac_reg:
+ push ax
+ push bx
+ push cx
+ push dx
+ mov dx, # VGAREG_DAC_READ_ADDRESS
+ mov al, bl
+ out dx, al
+ pop dx
+ push dx
+ mov bx, dx
+ mov dx, # VGAREG_DAC_DATA
+read_dac_loop:
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ in al, dx
+ seg es
+ mov [bx], al
+ inc bx
+ dec cx
+ jnz read_dac_loop
+ pop dx
+ pop cx
+ pop bx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_pel_mask:
+ push ax
+ push dx
+ mov dx, # VGAREG_PEL_MASK
+ mov al, bl
+ out dx, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_pel_mask:
+ push ax
+ push dx
+ mov dx, # VGAREG_PEL_MASK
+ in al, dx
+ mov bl, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_video_dac_state:
+ push ax
+ push dx
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x10
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ mov bl, al
+ shr bl, 7
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x14
+ out dx, al
+ mov dx, # VGAREG_ACTL_READ_DATA
+ in al, dx
+ mov bh, al
+ and bh, #0x0f
+ test bl, #0x01
+ jnz get_dac_16_page
+ shr bh, 2
+get_dac_16_page:
+ mov dx, # VGAREG_ACTL_RESET
+ in al, dx
+ mov dx, # VGAREG_ACTL_ADDRESS
+ mov al, #0x20
+ out dx, al
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_perform_gray_scale_summing (start,count)
+Bit16u start;Bit16u count;
+{Bit8u r,g,b;
+ Bit16u i;
+ Bit16u index;
+
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS,0x00);
+
+ for( index = 0; index < count; index++ )
+ {
+ // set read address and switch to read mode
+ outb(VGAREG_DAC_READ_ADDRESS,start);
+ // get 6-bit wide RGB data values
+ r=inb( VGAREG_DAC_DATA );
+ g=inb( VGAREG_DAC_DATA );
+ b=inb( VGAREG_DAC_DATA );
+
+ // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue )
+ i = ( ( 77*r + 151*g + 28*b ) + 0x80 ) >> 8;
+
+ if(i>0x3f)i=0x3f;
+
+ // set write address and switch to write mode
+ outb(VGAREG_DAC_WRITE_ADDRESS,start);
+ // write new intensity value
+ outb( VGAREG_DAC_DATA, i&0xff );
+ outb( VGAREG_DAC_DATA, i&0xff );
+ outb( VGAREG_DAC_DATA, i&0xff );
+ start++;
+ }
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS,0x20);
+}
+
+// --------------------------------------------------------------------------------------------
+static void get_font_access()
+{
+ASM_START
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0100
+ out dx, ax
+ mov ax, #0x0402
+ out dx, ax
+ mov ax, #0x0704
+ out dx, ax
+ mov ax, #0x0300
+ out dx, ax
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0x0204
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0406
+ out dx, ax
+ASM_END
+}
+
+static void release_font_access()
+{
+ASM_START
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0100
+ out dx, ax
+ mov ax, #0x0302
+ out dx, ax
+ mov ax, #0x0304
+ out dx, ax
+ mov ax, #0x0300
+ out dx, ax
+ mov dx, # VGAREG_READ_MISC_OUTPUT
+ in al, dx
+ and al, #0x01
+ shl al, 2
+ or al, #0x0a
+ mov ah, al
+ mov al, #0x06
+ mov dx, # VGAREG_GRDC_ADDRESS
+ out dx, ax
+ mov ax, #0x0004
+ out dx, ax
+ mov ax, #0x1005
+ out dx, ax
+ASM_END
+}
+
+ASM_START
+idiv_u:
+ xor dx,dx
+ div bx
+ ret
+ASM_END
+
+static void set_scan_lines(lines) Bit8u lines;
+{
+ Bit16u crtc_addr,cols,page,vde;
+ Bit8u crtc_r9,ovl,rows;
+
+ crtc_addr = read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr, 0x09);
+ crtc_r9 = inb(crtc_addr+1);
+ crtc_r9 = (crtc_r9 & 0xe0) | (lines - 1);
+ outb(crtc_addr+1, crtc_r9);
+ if(lines==8)
+ {
+ biosfn_set_cursor_shape(0x06,0x07);
+ }
+ else
+ {
+ biosfn_set_cursor_shape(lines-4,lines-3);
+ }
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, lines);
+ outb(crtc_addr, 0x12);
+ vde = inb(crtc_addr+1);
+ outb(crtc_addr, 0x07);
+ ovl = inb(crtc_addr+1);
+ vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1);
+ rows = vde / lines;
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, rows-1);
+ cols = read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, rows * cols * 2);
+}
+
+static void biosfn_load_text_user_pat (AL,ES,BP,CX,DX,BL,BH) Bit8u AL;Bit16u ES;Bit16u BP;Bit16u CX;Bit16u DX;Bit8u BL;Bit8u BH;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<CX;i++)
+ {
+ src = BP + i * BH;
+ dest = blockaddr + (DX + i) * 32;
+ memcpyb(0xA000, dest, ES, src, BH);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(BH);
+ }
+}
+
+static void biosfn_load_text_8_14_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+ {
+ src = i * 14;
+ dest = blockaddr + i * 32;
+ memcpyb(0xA000, dest, 0xC000, vgafont14+src, 14);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(14);
+ }
+}
+
+static void biosfn_load_text_8_8_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+ {
+ src = i * 8;
+ dest = blockaddr + i * 32;
+ memcpyb(0xA000, dest, 0xC000, vgafont8+src, 8);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(8);
+ }
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_text_block_specifier:
+ push ax
+ push dx
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ah, bl
+ mov al, #0x03
+ out dx, ax
+ pop dx
+ pop ax
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_load_text_8_16_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+ {
+ src = i * 16;
+ dest = blockaddr + i * 32;
+ memcpyb(0xA000, dest, 0xC000, vgafont16+src, 16);
+ }
+ release_font_access();
+ if(AL>=0x10)
+ {
+ set_scan_lines(16);
+ }
+}
+
+static void biosfn_load_gfx_8_8_chars (ES,BP) Bit16u ES;Bit16u BP;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_user_chars (ES,BP,CX,BL,DL) Bit16u ES;Bit16u BP;Bit16u CX;Bit8u BL;Bit8u DL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_14_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_8_dd_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_16_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+// --------------------------------------------------------------------------------------------
+static void biosfn_get_font_info (BH,ES,BP,CX,DX)
+Bit8u BH;Bit16u *ES;Bit16u *BP;Bit16u *CX;Bit16u *DX;
+{Bit16u ss=get_SS();
+
+ switch(BH)
+ {case 0x00:
+ write_word(ss,ES,read_word(0x00,0x1f*4));
+ write_word(ss,BP,read_word(0x00,(0x1f*4)+2));
+ break;
+ case 0x01:
+ write_word(ss,ES,read_word(0x00,0x43*4));
+ write_word(ss,BP,read_word(0x00,(0x43*4)+2));
+ break;
+ case 0x02:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont14);
+ break;
+ case 0x03:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont8);
+ break;
+ case 0x04:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont8+128*8);
+ break;
+ case 0x05:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont14alt);
+ break;
+ case 0x06:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont16);
+ break;
+ case 0x07:
+ write_word(ss,ES,0xC000);
+ write_word(ss,BP,vgafont16alt);
+ break;
+ default:
+ #ifdef DEBUG
+ printf("Get font info BH(%02x) was discarded\n",BH);
+ #endif
+ return;
+ }
+ // Set byte/char of on screen font
+ write_word(ss,CX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT));
+
+ // Set Highest char row
+ write_word(ss,DX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS));
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_ega_info:
+ push ds
+ push ax
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ xor ch, ch
+ mov bx, # BIOSMEM_SWITCHES
+ mov cl, [bx]
+ and cl, #0x0f
+ mov bx, # BIOSMEM_CRTC_ADDRESS
+ mov ax, [bx]
+ mov bx, #0x0003
+ cmp ax, # VGAREG_MDA_CRTC_ADDRESS
+ jne mode_ega_color
+ mov bh, #0x01
+mode_ega_color:
+ pop ax
+ pop ds
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_alternate_prtsc()
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_select_vert_res:
+
+; res : 00 200 lines, 01 350 lines, 02 400 lines
+
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ mov bx, # BIOSMEM_SWITCHES
+ mov ah, [bx]
+ cmp dl, #0x01
+ je vert_res_350
+ jb vert_res_200
+ cmp dl, #0x02
+ je vert_res_400
+#ifdef DEBUG
+ mov al, dl
+ xor ah, ah
+ push ax
+ mov bx, #msg_vert_res
+ push bx
+ call _printf
+ add sp, #4
+#endif
+ jmp set_retcode
+vert_res_400:
+
+ ; reset modeset ctl bit 7 and set bit 4
+ ; set switches bit 3-0 to 0x09
+
+ and al, #0x7f
+ or al, #0x10
+ and ah, #0xf0
+ or ah, #0x09
+ jnz set_vert_res
+vert_res_350:
+
+ ; reset modeset ctl bit 7 and bit 4
+ ; set switches bit 3-0 to 0x09
+
+ and al, #0x6f
+ and ah, #0xf0
+ or ah, #0x09
+ jnz set_vert_res
+vert_res_200:
+
+ ; set modeset ctl bit 7 and reset bit 4
+ ; set switches bit 3-0 to 0x08
+
+ and al, #0xef
+ or al, #0x80
+ and ah, #0xf0
+ or ah, #0x08
+set_vert_res:
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov [bx], al
+ mov bx, # BIOSMEM_SWITCHES
+ mov [bx], ah
+set_retcode:
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+#ifdef DEBUG
+msg_vert_res:
+.ascii "Select vert res (%02x) was discarded"
+.byte 0x0d,0x0a,0x00
+#endif
+
+
+biosfn_enable_default_palette_loading:
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ and dl, #0x01
+ shl dl, 3
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ and al, #0xf7
+ or al, dl
+ mov [bx], al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+
+biosfn_enable_video_addressing:
+ push bx
+ push dx
+ mov bl, al
+ and bl, #0x01
+ xor bl, #0x01
+ shl bl, 1
+ mov dx, # VGAREG_READ_MISC_OUTPUT
+ in al, dx
+ and al, #0xfd
+ or al, bl
+ mov dx, # VGAREG_WRITE_MISC_OUTPUT
+ out dx, al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ ret
+
+
+biosfn_enable_grayscale_summing:
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ and dl, #0x01
+ xor dl, #0x01
+ shl dl, 1
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ and al, #0xfd
+ or al, dl
+ mov [bx], al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+
+
+biosfn_enable_cursor_emulation:
+ push ds
+ push bx
+ push dx
+ mov dl, al
+ and dl, #0x01
+ xor dl, #0x01
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_MODESET_CTL
+ mov al, [bx]
+ and al, #0xfe
+ or al, dl
+ mov [bx], al
+ mov ax, #0x1212
+ pop dx
+ pop bx
+ pop ds
+ ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_switch_video_interface (AL,ES,DX) Bit8u AL;Bit16u ES;Bit16u DX;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_enable_video_refresh_control (AL) Bit8u AL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_string (flag,page,attr,count,row,col,seg,offset)
+Bit8u flag;Bit8u page;Bit8u attr;Bit16u count;Bit8u row;Bit8u col;Bit16u seg;Bit16u offset;
+{
+ Bit16u newcurs,oldcurs,dummy;
+ Bit8u car,carattr;
+
+ // Read curs info for the page
+ biosfn_get_cursor_pos(page,&dummy,&oldcurs);
+
+ // if row=0xff special case : use current cursor position
+ if(row==0xff)
+ {col=oldcurs&0x00ff;
+ row=(oldcurs&0xff00)>>8;
+ }
+
+ newcurs=row; newcurs<<=8; newcurs+=col;
+ biosfn_set_cursor_pos(page,newcurs);
+
+ while(count--!=0)
+ {
+ car=read_byte(seg,offset++);
+ if((flag&0x02)!=0)
+ attr=read_byte(seg,offset++);
+
+ biosfn_write_teletype(car,page,attr,WITH_ATTR);
+ }
+
+ // Set back curs pos
+ if((flag&0x01)==0)
+ biosfn_set_cursor_pos(page,oldcurs);
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_1A:
+ cmp al, #0x00
+ je biosfn_read_display_code
+ cmp al, #0x01
+ je biosfn_set_display_code
+#ifdef DEBUG
+ call _unknown
+#endif
+ ret
+biosfn_read_display_code:
+ push ds
+ push ax
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov bx, # BIOSMEM_DCC_INDEX
+ mov al, [bx]
+ mov bl, al
+ xor bh, bh
+ pop ax
+ mov al, ah
+ pop ds
+ ret
+biosfn_set_display_code:
+ push ds
+ push ax
+ push bx
+ mov ax, # BIOSMEM_SEG
+ mov ds, ax
+ mov ax, bx
+ mov bx, # BIOSMEM_DCC_INDEX
+ mov [bx], al
+#ifdef DEBUG
+ mov al, ah
+ xor ah, ah
+ push ax
+ mov bx, #msg_alt_dcc
+ push bx
+ call _printf
+ add sp, #4
+#endif
+ pop bx
+ pop ax
+ mov al, ah
+ pop ds
+ ret
+
+#ifdef DEBUG
+msg_alt_dcc:
+.ascii "Alternate Display code (%02x) was discarded"
+.byte 0x0d,0x0a,0x00
+#endif
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_state_info (BX,ES,DI)
+Bit16u BX;Bit16u ES;Bit16u DI;
+{
+ // Address of static functionality table
+ write_word(ES,DI+0x00,&static_functionality);
+ write_word(ES,DI+0x02,0xC000);
+
+ // Hard coded copy from BIOS area. Should it be cleaner ?
+ memcpyb(ES,DI+0x04,BIOSMEM_SEG,0x49,30);
+ memcpyb(ES,DI+0x22,BIOSMEM_SEG,0x84,3);
+
+ write_byte(ES,DI+0x25,read_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX));
+ write_byte(ES,DI+0x26,0);
+ write_byte(ES,DI+0x27,16);
+ write_byte(ES,DI+0x28,0);
+ write_byte(ES,DI+0x29,8);
+ write_byte(ES,DI+0x2a,2);
+ write_byte(ES,DI+0x2b,0);
+ write_byte(ES,DI+0x2c,0);
+ write_byte(ES,DI+0x31,3);
+ write_byte(ES,DI+0x32,0);
+
+ memsetb(ES,DI+0x33,0,13);
+}
+
+// --------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------
+static Bit16u biosfn_read_video_state_size2 (CX)
+ Bit16u CX;
+{
+ Bit16u size;
+ size = 0;
+ if (CX & 1) {
+ size += 0x46;
+ }
+ if (CX & 2) {
+ size += (5 + 8 + 5) * 2 + 6;
+ }
+ if (CX & 4) {
+ size += 3 + 256 * 3 + 1;
+}
+ return size;
+}
+
+static void biosfn_read_video_state_size (CX, BX)
+ Bit16u CX; Bit16u *BX;
+{
+ Bit16u ss=get_SS();
+ write_word(ss, BX, biosfn_read_video_state_size2(CX));
+}
+
+static Bit16u biosfn_save_video_state (CX,ES,BX)
+ Bit16u CX;Bit16u ES;Bit16u BX;
+{
+ Bit16u i, v, crtc_addr, ar_index;
+
+ crtc_addr = read_word(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS);
+ if (CX & 1) {
+ write_byte(ES, BX, inb(VGAREG_SEQU_ADDRESS)); BX++;
+ write_byte(ES, BX, inb(crtc_addr)); BX++;
+ write_byte(ES, BX, inb(VGAREG_GRDC_ADDRESS)); BX++;
+ inb(VGAREG_ACTL_RESET);
+ ar_index = inb(VGAREG_ACTL_ADDRESS);
+ write_byte(ES, BX, ar_index); BX++;
+ write_byte(ES, BX, inb(VGAREG_READ_FEATURE_CTL)); BX++;
+
+ for(i=1;i<=4;i++){
+ outb(VGAREG_SEQU_ADDRESS, i);
+ write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
+ }
+ outb(VGAREG_SEQU_ADDRESS, 0);
+ write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
+
+ for(i=0;i<=0x18;i++) {
+ outb(crtc_addr,i);
+ write_byte(ES, BX, inb(crtc_addr+1)); BX++;
+ }
+
+ for(i=0;i<=0x13;i++) {
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
+ write_byte(ES, BX, inb(VGAREG_ACTL_READ_DATA)); BX++;
+ }
+ inb(VGAREG_ACTL_RESET);
+
+ for(i=0;i<=8;i++) {
+ outb(VGAREG_GRDC_ADDRESS,i);
+ write_byte(ES, BX, inb(VGAREG_GRDC_DATA)); BX++;
+ }
+
+ write_word(ES, BX, crtc_addr); BX+= 2;
+
+ /* XXX: read plane latches */
+ write_byte(ES, BX, 0); BX++;
+ write_byte(ES, BX, 0); BX++;
+ write_byte(ES, BX, 0); BX++;
+ write_byte(ES, BX, 0); BX++;
+ }
+ if (CX & 2) {
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)); BX++;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)); BX += 2;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)); BX += 2;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)); BX += 2;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); BX++;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); BX += 2;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)); BX++;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES)); BX++;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)); BX++;
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); BX += 2;
+ for(i=0;i<8;i++) {
+ write_word(ES, BX, read_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i));
+ BX += 2;
+ }
+ write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START)); BX += 2;
+ write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)); BX++;
+ /* current font */
+ write_word(ES, BX, read_word(0, 0x1f * 4)); BX += 2;
+ write_word(ES, BX, read_word(0, 0x1f * 4 + 2)); BX += 2;
+ write_word(ES, BX, read_word(0, 0x43 * 4)); BX += 2;
+ write_word(ES, BX, read_word(0, 0x43 * 4 + 2)); BX += 2;
+ }
+ if (CX & 4) {
+ /* XXX: check this */
+ write_byte(ES, BX, inb(VGAREG_DAC_STATE)); BX++; /* read/write mode dac */
+ write_byte(ES, BX, inb(VGAREG_DAC_WRITE_ADDRESS)); BX++; /* pix address */
+ write_byte(ES, BX, inb(VGAREG_PEL_MASK)); BX++;
+ // Set the whole dac always, from 0
+ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+ for(i=0;i<256*3;i++) {
+ write_byte(ES, BX, inb(VGAREG_DAC_DATA)); BX++;
+ }
+ write_byte(ES, BX, 0); BX++; /* color select register */
+ }
+ return BX;
+}
+
+static Bit16u biosfn_restore_video_state (CX,ES,BX)
+ Bit16u CX;Bit16u ES;Bit16u BX;
+{
+ Bit16u i, crtc_addr, v, addr1, ar_index;
+
+ if (CX & 1) {
+ // Reset Attribute Ctl flip-flop
+ inb(VGAREG_ACTL_RESET);
+
+ crtc_addr = read_word(ES, BX + 0x40);
+ addr1 = BX;
+ BX += 5;
+
+ for(i=1;i<=4;i++){
+ outb(VGAREG_SEQU_ADDRESS, i);
+ outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
+ }
+ outb(VGAREG_SEQU_ADDRESS, 0);
+ outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
+
+ // Disable CRTC write protection
+ outw(crtc_addr,0x0011);
+ // Set CRTC regs
+ for(i=0;i<=0x18;i++) {
+ if (i != 0x11) {
+ outb(crtc_addr,i);
+ outb(crtc_addr+1, read_byte(ES, BX));
+ }
+ BX++;
+ }
+ // select crtc base address
+ v = inb(VGAREG_READ_MISC_OUTPUT) & ~0x01;
+ if (crtc_addr = 0x3d4)
+ v |= 0x01;
+ outb(VGAREG_WRITE_MISC_OUTPUT, v);
+
+ // enable write protection if needed
+ outb(crtc_addr, 0x11);
+ outb(crtc_addr+1, read_byte(ES, BX - 0x18 + 0x11));
+
+ // Set Attribute Ctl
+ ar_index = read_byte(ES, addr1 + 0x03);
+ inb(VGAREG_ACTL_RESET);
+ for(i=0;i<=0x13;i++) {
+ outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
+ outb(VGAREG_ACTL_WRITE_DATA, read_byte(ES, BX)); BX++;
+ }
+ outb(VGAREG_ACTL_ADDRESS, ar_index);
+ inb(VGAREG_ACTL_RESET);
+
+ for(i=0;i<=8;i++) {
+ outb(VGAREG_GRDC_ADDRESS,i);
+ outb(VGAREG_GRDC_DATA, read_byte(ES, BX)); BX++;
+ }
+ BX += 2; /* crtc_addr */
+ BX += 4; /* plane latches */
+
+ outb(VGAREG_SEQU_ADDRESS, read_byte(ES, addr1)); addr1++;
+ outb(crtc_addr, read_byte(ES, addr1)); addr1++;
+ outb(VGAREG_GRDC_ADDRESS, read_byte(ES, addr1)); addr1++;
+ addr1++;
+ outb(crtc_addr - 0x4 + 0xa, read_byte(ES, addr1)); addr1++;
+ }
+ if (CX & 2) {
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE, read_byte(ES, BX)); BX++;
+ write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS, read_word(ES, BX)); BX += 2;
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, read_word(ES, BX)); BX += 2;
+ write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS, read_word(ES, BX)); BX += 2;
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, read_byte(ES, BX)); BX++;
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, read_word(ES, BX)); BX += 2;
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL, read_byte(ES, BX)); BX++;
+ write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES, read_byte(ES, BX)); BX++;
+ write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL, read_byte(ES, BX)); BX++;
+ write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE, read_word(ES, BX)); BX += 2;
+ for(i=0;i<8;i++) {
+ write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i, read_word(ES, BX));
+ BX += 2;
+ }
+ write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START, read_word(ES, BX)); BX += 2;
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE, read_byte(ES, BX)); BX++;
+ /* current font */
+ write_word(0, 0x1f * 4, read_word(ES, BX)); BX += 2;
+ write_word(0, 0x1f * 4 + 2, read_word(ES, BX)); BX += 2;
+ write_word(0, 0x43 * 4, read_word(ES, BX)); BX += 2;
+ write_word(0, 0x43 * 4 + 2, read_word(ES, BX)); BX += 2;
+ }
+ if (CX & 4) {
+ BX++;
+ v = read_byte(ES, BX); BX++;
+ outb(VGAREG_PEL_MASK, read_byte(ES, BX)); BX++;
+ // Set the whole dac always, from 0
+ outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+ for(i=0;i<256*3;i++) {
+ outb(VGAREG_DAC_DATA, read_byte(ES, BX)); BX++;
+ }
+ BX++;
+ outb(VGAREG_DAC_WRITE_ADDRESS, v);
+ }
+ return BX;
+}
+
+// ============================================================================================
+//
+// Video Utils
+//
+// ============================================================================================
+
+// --------------------------------------------------------------------------------------------
+static Bit8u find_vga_entry(mode)
+Bit8u mode;
+{
+ Bit8u i,line=0xFF;
+ for(i=0;i<=MODE_MAX;i++)
+ if(vga_modes[i].svgamode==mode)
+ {line=i;
+ break;
+ }
+ return line;
+}
+
+/* =========================================================== */
+/*
+ * Misc Utils
+*/
+/* =========================================================== */
+
+// --------------------------------------------------------------------------------------------
+static void memsetb(seg,offset,value,count)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u value;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+
+ mov cx, 10[bp] ; count
+ cmp cx, #0x00
+ je memsetb_end
+ mov ax, 4[bp] ; segment
+ mov es, ax
+ mov ax, 6[bp] ; offset
+ mov di, ax
+ mov al, 8[bp] ; value
+ cld
+ rep
+ stosb
+
+memsetb_end:
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memsetw(seg,offset,value,count)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u value;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+
+ mov cx, 10[bp] ; count
+ cmp cx, #0x00
+ je memsetw_end
+ mov ax, 4[bp] ; segment
+ mov es, ax
+ mov ax, 6[bp] ; offset
+ mov di, ax
+ mov ax, 8[bp] ; value
+ cld
+ rep
+ stosw
+
+memsetw_end:
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memcpyb(dseg,doffset,sseg,soffset,count)
+ Bit16u dseg;
+ Bit16u doffset;
+ Bit16u sseg;
+ Bit16u soffset;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+ push ds
+ push si
+
+ mov cx, 12[bp] ; count
+ cmp cx, #0x0000
+ je memcpyb_end
+ mov ax, 4[bp] ; dsegment
+ mov es, ax
+ mov ax, 6[bp] ; doffset
+ mov di, ax
+ mov ax, 8[bp] ; ssegment
+ mov ds, ax
+ mov ax, 10[bp] ; soffset
+ mov si, ax
+ cld
+ rep
+ movsb
+
+memcpyb_end:
+ pop si
+ pop ds
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memcpyw(dseg,doffset,sseg,soffset,count)
+ Bit16u dseg;
+ Bit16u doffset;
+ Bit16u sseg;
+ Bit16u soffset;
+ Bit16u count;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push cx
+ push es
+ push di
+ push ds
+ push si
+
+ mov cx, 12[bp] ; count
+ cmp cx, #0x0000
+ je memcpyw_end
+ mov ax, 4[bp] ; dsegment
+ mov es, ax
+ mov ax, 6[bp] ; doffset
+ mov di, ax
+ mov ax, 8[bp] ; ssegment
+ mov ds, ax
+ mov ax, 10[bp] ; soffset
+ mov si, ax
+ cld
+ rep
+ movsw
+
+memcpyw_end:
+ pop si
+ pop ds
+ pop di
+ pop es
+ pop cx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+/* =========================================================== */
+/*
+ * These functions where ripped from Kevin's rombios.c
+*/
+/* =========================================================== */
+
+// --------------------------------------------------------------------------------------------
+static Bit8u
+read_byte(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov al, [bx]
+ ;; al = return value (byte)
+ pop ds
+ pop bx
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static Bit16u
+read_word(seg, offset)
+ Bit16u seg;
+ Bit16u offset;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, [bx]
+ ;; ax = return value (word)
+ pop ds
+ pop bx
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void
+write_byte(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit8u data;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov al, 8[bp] ; data byte
+ mov [bx], al ; write data byte
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void
+write_word(seg, offset, data)
+ Bit16u seg;
+ Bit16u offset;
+ Bit16u data;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push bx
+ push ds
+ mov ax, 4[bp] ; segment
+ mov ds, ax
+ mov bx, 6[bp] ; offset
+ mov ax, 8[bp] ; data word
+ mov [bx], ax ; write data word
+ pop ds
+ pop bx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ Bit8u
+inb(port)
+ Bit16u port;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push dx
+ mov dx, 4[bp]
+ in al, dx
+ pop dx
+
+ pop bp
+ASM_END
+}
+
+ Bit16u
+inw(port)
+ Bit16u port;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push dx
+ mov dx, 4[bp]
+ in ax, dx
+ pop dx
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ void
+outb(port, val)
+ Bit16u port;
+ Bit8u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push dx
+ mov dx, 4[bp]
+ mov al, 6[bp]
+ out dx, al
+ pop dx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ void
+outw(port, val)
+ Bit16u port;
+ Bit16u val;
+{
+ASM_START
+ push bp
+ mov bp, sp
+
+ push ax
+ push dx
+ mov dx, 4[bp]
+ mov ax, 6[bp]
+ out dx, ax
+ pop dx
+ pop ax
+
+ pop bp
+ASM_END
+}
+
+Bit16u get_SS()
+{
+ASM_START
+ mov ax, ss
+ASM_END
+}
+
+#ifdef DEBUG
+void unimplemented()
+{
+ printf("--> Unimplemented\n");
+}
+
+void unknown()
+{
+ printf("--> Unknown int10\n");
+}
+#endif
+
+// --------------------------------------------------------------------------------------------
+#if defined(USE_BX_INFO) || defined(DEBUG) || defined(CIRRUS_DEBUG)
+void printf(s)
+ Bit8u *s;
+{
+ Bit8u c, format_char;
+ Boolean in_format;
+ unsigned format_width, i;
+ Bit16u *arg_ptr;
+ Bit16u arg_seg, arg, digit, nibble, shift_count;
+
+ arg_ptr = &s;
+ arg_seg = get_SS();
+
+ in_format = 0;
+ format_width = 0;
+
+ while (c = read_byte(0xc000, s)) {
+ if ( c == '%' ) {
+ in_format = 1;
+ format_width = 0;
+ }
+ else if (in_format) {
+ if ( (c>='0') && (c<='9') ) {
+ format_width = (format_width * 10) + (c - '0');
+ }
+ else if (c == 'x') {
+ arg_ptr++; // increment to next arg
+ arg = read_word(arg_seg, arg_ptr);
+ if (format_width == 0)
+ format_width = 4;
+ i = 0;
+ digit = format_width - 1;
+ for (i=0; i<format_width; i++) {
+ nibble = (arg >> (4 * digit)) & 0x000f;
+ if (nibble <= 9)
+ outb(0x0500, nibble + '0');
+ else
+ outb(0x0500, (nibble - 10) + 'A');
+ digit--;
+ }
+ in_format = 0;
+ }
+ //else if (c == 'd') {
+ // in_format = 0;
+ // }
+ }
+ else {
+ outb(0x0500, c);
+ }
+ s ++;
+ }
+}
+#endif
+
+#ifdef VBE
+#include "vbe.c"
+#endif
+
+#ifdef CIRRUS
+#include "clext.c"
+#endif
+
+// --------------------------------------------------------------------------------------------
+
+ASM_START
+;; DATA_SEG_DEFS_HERE
+ASM_END
+
+ASM_START
+.ascii "vgabios ends here"
+.byte 0x00
+vgabios_end:
+.byte 0xCB
+;; BLOCK_STRINGS_BEGIN
+ASM_END
diff --git a/kvm/vgabios/vgabios.h b/kvm/vgabios/vgabios.h
new file mode 100644
index 0000000..3ad4bae
--- /dev/null
+++ b/kvm/vgabios/vgabios.h
@@ -0,0 +1,47 @@
+#ifndef vgabios_h_included
+#define vgabios_h_included
+
+/* Types */
+typedef unsigned char Bit8u;
+typedef unsigned short Bit16u;
+typedef unsigned long Bit32u;
+typedef unsigned short Boolean;
+
+/* Defines */
+
+#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
+#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
+#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
+#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
+#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
+#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
+#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
+#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
+
+#define GET_AL() ( AX & 0x00ff )
+#define GET_BL() ( BX & 0x00ff )
+#define GET_CL() ( CX & 0x00ff )
+#define GET_DL() ( DX & 0x00ff )
+#define GET_AH() ( AX >> 8 )
+#define GET_BH() ( BX >> 8 )
+#define GET_CH() ( CX >> 8 )
+#define GET_DH() ( DX >> 8 )
+
+#define SET_CF() FLAGS |= 0x0001
+#define CLEAR_CF() FLAGS &= 0xfffe
+#define GET_CF() (FLAGS & 0x0001)
+
+#define SET_ZF() FLAGS |= 0x0040
+#define CLEAR_ZF() FLAGS &= 0xffbf
+#define GET_ZF() (FLAGS & 0x0040)
+
+#define SCROLL_DOWN 0
+#define SCROLL_UP 1
+#define NO_ATTR 2
+#define WITH_ATTR 3
+
+#define SCREEN_SIZE(x,y) (((x*y*2)|0x00ff)+1)
+#define SCREEN_MEM_START(x,y,p) ((((x*y*2)|0x00ff)+1)*p)
+#define SCREEN_IO_START(x,y,p) ((((x*y)|0x00ff)+1)*p)
+
+#endif
diff --git a/kvm/vgabios/vgafonts.h b/kvm/vgabios/vgafonts.h
new file mode 100644
index 0000000..0c213e6
--- /dev/null
+++ b/kvm/vgabios/vgafonts.h
@@ -0,0 +1,784 @@
+/*
+ * These fonts come from ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+ * The package is (c) by Joseph Gil
+ * The individual fonts are public domain
+ */
+static Bit8u vgafont8[256*8]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+ 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00,
+ 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38,
+ 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00,
+ 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00,
+ 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00,
+ 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00,
+ 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00,
+ 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00,
+ 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00,
+ 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00,
+ 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18,
+ 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00,
+ 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30,
+ 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7,
+ 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70,
+ 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00,
+ 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00,
+ 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00,
+ 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00,
+ 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00,
+ 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f,
+ 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03,
+ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00,
+ 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00,
+ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00,
+ 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0,
+ 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00,
+ 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00,
+ 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00,
+ 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0,
+ 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc,
+ 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00,
+ 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00,
+ 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0,
+ 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00,
+ 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00,
+ 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00,
+ 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00,
+ 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70,
+ 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00,
+ 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c,
+ 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static Bit8u vgafont14[256*14]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0xc6, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0x40, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static Bit8u vgafont16[256*16]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00,
+ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00,
+ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+ 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00,
+ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static Bit8u vgafont14alt[1]={0x00};
+static Bit8u vgafont16alt[1]={0x00};
diff --git a/kvm/vgabios/vgatables.h b/kvm/vgabios/vgatables.h
new file mode 100644
index 0000000..3ac96bb
--- /dev/null
+++ b/kvm/vgabios/vgatables.h
@@ -0,0 +1,622 @@
+/*
+ *
+ * BIOS Memory
+ *
+ */
+#define BIOSMEM_SEG 0x40
+
+#define BIOSMEM_INITIAL_MODE 0x10
+#define BIOSMEM_CURRENT_MODE 0x49
+#define BIOSMEM_NB_COLS 0x4A
+#define BIOSMEM_PAGE_SIZE 0x4C
+#define BIOSMEM_CURRENT_START 0x4E
+#define BIOSMEM_CURSOR_POS 0x50
+#define BIOSMEM_CURSOR_TYPE 0x60
+#define BIOSMEM_CURRENT_PAGE 0x62
+#define BIOSMEM_CRTC_ADDRESS 0x63
+#define BIOSMEM_CURRENT_MSR 0x65
+#define BIOSMEM_CURRENT_PAL 0x66
+#define BIOSMEM_NB_ROWS 0x84
+#define BIOSMEM_CHAR_HEIGHT 0x85
+#define BIOSMEM_VIDEO_CTL 0x87
+#define BIOSMEM_SWITCHES 0x88
+#define BIOSMEM_MODESET_CTL 0x89
+#define BIOSMEM_DCC_INDEX 0x8A
+#define BIOSMEM_VS_POINTER 0xA8
+#define BIOSMEM_VBE_FLAG 0xB9
+#define BIOSMEM_VBE_MODE 0xBA
+
+
+/*
+ *
+ * VGA registers
+ *
+ */
+#define VGAREG_ACTL_ADDRESS 0x3c0
+#define VGAREG_ACTL_WRITE_DATA 0x3c0
+#define VGAREG_ACTL_READ_DATA 0x3c1
+
+#define VGAREG_INPUT_STATUS 0x3c2
+#define VGAREG_WRITE_MISC_OUTPUT 0x3c2
+#define VGAREG_VIDEO_ENABLE 0x3c3
+#define VGAREG_SEQU_ADDRESS 0x3c4
+#define VGAREG_SEQU_DATA 0x3c5
+
+#define VGAREG_PEL_MASK 0x3c6
+#define VGAREG_DAC_STATE 0x3c7
+#define VGAREG_DAC_READ_ADDRESS 0x3c7
+#define VGAREG_DAC_WRITE_ADDRESS 0x3c8
+#define VGAREG_DAC_DATA 0x3c9
+
+#define VGAREG_READ_FEATURE_CTL 0x3ca
+#define VGAREG_READ_MISC_OUTPUT 0x3cc
+
+#define VGAREG_GRDC_ADDRESS 0x3ce
+#define VGAREG_GRDC_DATA 0x3cf
+
+#define VGAREG_MDA_CRTC_ADDRESS 0x3b4
+#define VGAREG_MDA_CRTC_DATA 0x3b5
+#define VGAREG_VGA_CRTC_ADDRESS 0x3d4
+#define VGAREG_VGA_CRTC_DATA 0x3d5
+
+#define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba
+#define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da
+#define VGAREG_ACTL_RESET 0x3da
+
+#define VGAREG_MDA_MODECTL 0x3b8
+#define VGAREG_CGA_MODECTL 0x3d8
+#define VGAREG_CGA_PALETTE 0x3d9
+
+/* Video memory */
+#define VGAMEM_GRAPH 0xA000
+#define VGAMEM_CTEXT 0xB800
+#define VGAMEM_MTEXT 0xB000
+
+/*
+ *
+ * Tables of default values for each mode
+ *
+ */
+#define MODE_MAX 15
+#define TEXT 0x00
+#define GRAPH 0x01
+
+#define CTEXT 0x00
+#define MTEXT 0x01
+#define CGA 0x02
+#define PLANAR1 0x03
+#define PLANAR4 0x04
+#define LINEAR8 0x05
+
+// for SVGA
+#define LINEAR15 0x10
+#define LINEAR16 0x11
+#define LINEAR24 0x12
+#define LINEAR32 0x13
+
+typedef struct
+{Bit8u svgamode;
+ Bit8u class; /* TEXT, GRAPH */
+ Bit8u memmodel; /* CTEXT,MTEXT,CGA,PL1,PL2,PL4,P8,P15,P16,P24,P32 */
+ Bit8u pixbits;
+ Bit16u sstart;
+ Bit8u pelmask;
+ Bit8u dacmodel; /* 0 1 2 3 */
+} VGAMODES;
+
+static VGAMODES vga_modes[MODE_MAX+1]=
+{//mode class model bits sstart pelm dac
+ {0x00, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x01, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x02, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x03, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02},
+ {0x04, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01},
+ {0x05, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01},
+ {0x06, GRAPH, CGA, 1, 0xB800, 0xFF, 0x01},
+ {0x07, TEXT, MTEXT, 4, 0xB000, 0xFF, 0x00},
+ {0x0D, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01},
+ {0x0E, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01},
+ {0x0F, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x00},
+ {0x10, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02},
+ {0x11, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x02},
+ {0x12, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02},
+ {0x13, GRAPH, LINEAR8, 8, 0xA000, 0xFF, 0x03},
+ {0x6A, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}
+};
+
+/* convert index in vga_modes[] to index in video_param_table[] */
+static Bit8u line_to_vpti[MODE_MAX+1]={
+ 0x17, 0x17, 0x18, 0x18, 0x04, 0x05, 0x06, 0x07,
+ 0x0d, 0x0e, 0x11, 0x12, 0x1a, 0x1b, 0x1c, 0x1d,
+};
+
+/* Default Palette */
+#define DAC_MAX_MODEL 3
+
+static Bit8u dac_regs[DAC_MAX_MODEL+1]=
+{0x3f,0x3f,0x3f,0xff};
+
+/* standard BIOS Video Parameter Table */
+typedef struct {
+ Bit8u twidth;
+ Bit8u theightm1;
+ Bit8u cheight;
+ Bit8u slength_l;
+ Bit8u slength_h;
+ Bit8u sequ_regs[4];
+ Bit8u miscreg;
+ Bit8u crtc_regs[25];
+ Bit8u actl_regs[20];
+ Bit8u grdc_regs[9];
+} VideoParamTableEntry;
+
+static VideoParamTableEntry video_param_table[30] = {
+{
+ /* index=0x00 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x01 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x02 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x03 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x04 vga mode 0x04 */
+ 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */
+ 0x09, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2,
+ 0xff, /* crtc_regs */
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x03, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x05 vga mode 0x05 */
+ 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */
+ 0x09, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2,
+ 0xff, /* crtc_regs */
+ 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x03, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x06 vga mode 0x06 */
+ 80, 24, 8, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x01, 0x01, 0x00, 0x06, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2,
+ 0xff, /* crtc_regs */
+ 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x01, 0x00, 0x01, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x07 vga mode 0x07 */
+ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x66, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x08 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x09 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0a no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0b no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0c no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x0d vga mode 0x0d */
+ 40, 24, 8, 0x00, 0x20, /* tw, th-1, ch, slength */
+ 0x09, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x0e vga mode 0x0e */
+ 80, 24, 8, 0x00, 0x40, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x0f no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x10 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x11 vga mode 0x0f */
+ 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xa3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x12 vga mode 0x10 */
+ 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xa3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x13 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x14 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x15 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x16 no mode defined */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+},
+{
+ /* index=0x17 vga mode 0x01 */
+ 40, 24, 16, 0x00, 0x08, /* tw, th-1, ch, slength */
+ 0x08, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x67, /* miscreg */
+ 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x18 vga mode 0x03 */
+ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x67, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x19 vga mode 0x07 */
+ 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */
+ 0x00, 0x03, 0x00, 0x02, /* sequ_regs */
+ 0x66, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f,
+ 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1a vga mode 0x11 */
+ 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xe3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f,
+ 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1b vga mode 0x12 */
+ 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xe3, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1c vga mode 0x13 */
+ 40, 24, 8, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x0e, /* sequ_regs */
+ 0x63, /* miscreg */
+ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x41, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+{
+ /* index=0x1d vga mode 0x6a */
+ 100, 36, 16, 0x00, 0x00, /* tw, th-1, ch, slength */
+ 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */
+ 0xe3, /* miscreg */
+ 0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x59, 0x8d, 0x57, 0x32, 0x00, 0x57, 0x73, 0xe3,
+ 0xff, /* crtc_regs */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x01, 0x00, 0x0f, 0x00, /* actl_regs */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */
+},
+};
+
+/* Mono */
+static Bit8u palette0[63+1][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a,
+ 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f
+};
+
+static Bit8u palette1[63+1][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f
+};
+
+static Bit8u palette2[63+1][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a,
+ 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f, 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f,
+ 0x00,0x15,0x00, 0x00,0x15,0x2a, 0x00,0x3f,0x00, 0x00,0x3f,0x2a, 0x2a,0x15,0x00, 0x2a,0x15,0x2a, 0x2a,0x3f,0x00, 0x2a,0x3f,0x2a,
+ 0x00,0x15,0x15, 0x00,0x15,0x3f, 0x00,0x3f,0x15, 0x00,0x3f,0x3f, 0x2a,0x15,0x15, 0x2a,0x15,0x3f, 0x2a,0x3f,0x15, 0x2a,0x3f,0x3f,
+ 0x15,0x00,0x00, 0x15,0x00,0x2a, 0x15,0x2a,0x00, 0x15,0x2a,0x2a, 0x3f,0x00,0x00, 0x3f,0x00,0x2a, 0x3f,0x2a,0x00, 0x3f,0x2a,0x2a,
+ 0x15,0x00,0x15, 0x15,0x00,0x3f, 0x15,0x2a,0x15, 0x15,0x2a,0x3f, 0x3f,0x00,0x15, 0x3f,0x00,0x3f, 0x3f,0x2a,0x15, 0x3f,0x2a,0x3f,
+ 0x15,0x15,0x00, 0x15,0x15,0x2a, 0x15,0x3f,0x00, 0x15,0x3f,0x2a, 0x3f,0x15,0x00, 0x3f,0x15,0x2a, 0x3f,0x3f,0x00, 0x3f,0x3f,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f
+};
+
+static Bit8u palette3[256][3]=
+{
+ 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a,
+ 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x00, 0x05,0x05,0x05, 0x08,0x08,0x08, 0x0b,0x0b,0x0b, 0x0e,0x0e,0x0e, 0x11,0x11,0x11, 0x14,0x14,0x14, 0x18,0x18,0x18,
+ 0x1c,0x1c,0x1c, 0x20,0x20,0x20, 0x24,0x24,0x24, 0x28,0x28,0x28, 0x2d,0x2d,0x2d, 0x32,0x32,0x32, 0x38,0x38,0x38, 0x3f,0x3f,0x3f,
+ 0x00,0x00,0x3f, 0x10,0x00,0x3f, 0x1f,0x00,0x3f, 0x2f,0x00,0x3f, 0x3f,0x00,0x3f, 0x3f,0x00,0x2f, 0x3f,0x00,0x1f, 0x3f,0x00,0x10,
+ 0x3f,0x00,0x00, 0x3f,0x10,0x00, 0x3f,0x1f,0x00, 0x3f,0x2f,0x00, 0x3f,0x3f,0x00, 0x2f,0x3f,0x00, 0x1f,0x3f,0x00, 0x10,0x3f,0x00,
+ 0x00,0x3f,0x00, 0x00,0x3f,0x10, 0x00,0x3f,0x1f, 0x00,0x3f,0x2f, 0x00,0x3f,0x3f, 0x00,0x2f,0x3f, 0x00,0x1f,0x3f, 0x00,0x10,0x3f,
+ 0x1f,0x1f,0x3f, 0x27,0x1f,0x3f, 0x2f,0x1f,0x3f, 0x37,0x1f,0x3f, 0x3f,0x1f,0x3f, 0x3f,0x1f,0x37, 0x3f,0x1f,0x2f, 0x3f,0x1f,0x27,
+
+ 0x3f,0x1f,0x1f, 0x3f,0x27,0x1f, 0x3f,0x2f,0x1f, 0x3f,0x37,0x1f, 0x3f,0x3f,0x1f, 0x37,0x3f,0x1f, 0x2f,0x3f,0x1f, 0x27,0x3f,0x1f,
+ 0x1f,0x3f,0x1f, 0x1f,0x3f,0x27, 0x1f,0x3f,0x2f, 0x1f,0x3f,0x37, 0x1f,0x3f,0x3f, 0x1f,0x37,0x3f, 0x1f,0x2f,0x3f, 0x1f,0x27,0x3f,
+ 0x2d,0x2d,0x3f, 0x31,0x2d,0x3f, 0x36,0x2d,0x3f, 0x3a,0x2d,0x3f, 0x3f,0x2d,0x3f, 0x3f,0x2d,0x3a, 0x3f,0x2d,0x36, 0x3f,0x2d,0x31,
+ 0x3f,0x2d,0x2d, 0x3f,0x31,0x2d, 0x3f,0x36,0x2d, 0x3f,0x3a,0x2d, 0x3f,0x3f,0x2d, 0x3a,0x3f,0x2d, 0x36,0x3f,0x2d, 0x31,0x3f,0x2d,
+ 0x2d,0x3f,0x2d, 0x2d,0x3f,0x31, 0x2d,0x3f,0x36, 0x2d,0x3f,0x3a, 0x2d,0x3f,0x3f, 0x2d,0x3a,0x3f, 0x2d,0x36,0x3f, 0x2d,0x31,0x3f,
+ 0x00,0x00,0x1c, 0x07,0x00,0x1c, 0x0e,0x00,0x1c, 0x15,0x00,0x1c, 0x1c,0x00,0x1c, 0x1c,0x00,0x15, 0x1c,0x00,0x0e, 0x1c,0x00,0x07,
+ 0x1c,0x00,0x00, 0x1c,0x07,0x00, 0x1c,0x0e,0x00, 0x1c,0x15,0x00, 0x1c,0x1c,0x00, 0x15,0x1c,0x00, 0x0e,0x1c,0x00, 0x07,0x1c,0x00,
+ 0x00,0x1c,0x00, 0x00,0x1c,0x07, 0x00,0x1c,0x0e, 0x00,0x1c,0x15, 0x00,0x1c,0x1c, 0x00,0x15,0x1c, 0x00,0x0e,0x1c, 0x00,0x07,0x1c,
+
+ 0x0e,0x0e,0x1c, 0x11,0x0e,0x1c, 0x15,0x0e,0x1c, 0x18,0x0e,0x1c, 0x1c,0x0e,0x1c, 0x1c,0x0e,0x18, 0x1c,0x0e,0x15, 0x1c,0x0e,0x11,
+ 0x1c,0x0e,0x0e, 0x1c,0x11,0x0e, 0x1c,0x15,0x0e, 0x1c,0x18,0x0e, 0x1c,0x1c,0x0e, 0x18,0x1c,0x0e, 0x15,0x1c,0x0e, 0x11,0x1c,0x0e,
+ 0x0e,0x1c,0x0e, 0x0e,0x1c,0x11, 0x0e,0x1c,0x15, 0x0e,0x1c,0x18, 0x0e,0x1c,0x1c, 0x0e,0x18,0x1c, 0x0e,0x15,0x1c, 0x0e,0x11,0x1c,
+ 0x14,0x14,0x1c, 0x16,0x14,0x1c, 0x18,0x14,0x1c, 0x1a,0x14,0x1c, 0x1c,0x14,0x1c, 0x1c,0x14,0x1a, 0x1c,0x14,0x18, 0x1c,0x14,0x16,
+ 0x1c,0x14,0x14, 0x1c,0x16,0x14, 0x1c,0x18,0x14, 0x1c,0x1a,0x14, 0x1c,0x1c,0x14, 0x1a,0x1c,0x14, 0x18,0x1c,0x14, 0x16,0x1c,0x14,
+ 0x14,0x1c,0x14, 0x14,0x1c,0x16, 0x14,0x1c,0x18, 0x14,0x1c,0x1a, 0x14,0x1c,0x1c, 0x14,0x1a,0x1c, 0x14,0x18,0x1c, 0x14,0x16,0x1c,
+ 0x00,0x00,0x10, 0x04,0x00,0x10, 0x08,0x00,0x10, 0x0c,0x00,0x10, 0x10,0x00,0x10, 0x10,0x00,0x0c, 0x10,0x00,0x08, 0x10,0x00,0x04,
+ 0x10,0x00,0x00, 0x10,0x04,0x00, 0x10,0x08,0x00, 0x10,0x0c,0x00, 0x10,0x10,0x00, 0x0c,0x10,0x00, 0x08,0x10,0x00, 0x04,0x10,0x00,
+
+ 0x00,0x10,0x00, 0x00,0x10,0x04, 0x00,0x10,0x08, 0x00,0x10,0x0c, 0x00,0x10,0x10, 0x00,0x0c,0x10, 0x00,0x08,0x10, 0x00,0x04,0x10,
+ 0x08,0x08,0x10, 0x0a,0x08,0x10, 0x0c,0x08,0x10, 0x0e,0x08,0x10, 0x10,0x08,0x10, 0x10,0x08,0x0e, 0x10,0x08,0x0c, 0x10,0x08,0x0a,
+ 0x10,0x08,0x08, 0x10,0x0a,0x08, 0x10,0x0c,0x08, 0x10,0x0e,0x08, 0x10,0x10,0x08, 0x0e,0x10,0x08, 0x0c,0x10,0x08, 0x0a,0x10,0x08,
+ 0x08,0x10,0x08, 0x08,0x10,0x0a, 0x08,0x10,0x0c, 0x08,0x10,0x0e, 0x08,0x10,0x10, 0x08,0x0e,0x10, 0x08,0x0c,0x10, 0x08,0x0a,0x10,
+ 0x0b,0x0b,0x10, 0x0c,0x0b,0x10, 0x0d,0x0b,0x10, 0x0f,0x0b,0x10, 0x10,0x0b,0x10, 0x10,0x0b,0x0f, 0x10,0x0b,0x0d, 0x10,0x0b,0x0c,
+ 0x10,0x0b,0x0b, 0x10,0x0c,0x0b, 0x10,0x0d,0x0b, 0x10,0x0f,0x0b, 0x10,0x10,0x0b, 0x0f,0x10,0x0b, 0x0d,0x10,0x0b, 0x0c,0x10,0x0b,
+ 0x0b,0x10,0x0b, 0x0b,0x10,0x0c, 0x0b,0x10,0x0d, 0x0b,0x10,0x0f, 0x0b,0x10,0x10, 0x0b,0x0f,0x10, 0x0b,0x0d,0x10, 0x0b,0x0c,0x10,
+ 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00
+};
+
+static Bit8u static_functionality[0x10]=
+{
+ /* 0 */ 0xff, // All modes supported #1
+ /* 1 */ 0xe0, // All modes supported #2
+ /* 2 */ 0x0f, // All modes supported #3
+ /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved
+ /* 7 */ 0x07, // 200, 350, 400 scan lines
+ /* 8 */ 0x02, // mamimum number of visible charsets in text mode
+ /* 9 */ 0x08, // total number of charset blocks in text mode
+ /* a */ 0xe7, // Change to add new functions
+ /* b */ 0x0c, // Change to add new functions
+ /* c */ 0x00, // reserved
+ /* d */ 0x00, // reserved
+ /* e */ 0x00, // Change to add new functions
+ /* f */ 0x00 // reserved
+};
diff --git a/libfdt_env.h b/libfdt_env.h
new file mode 100644
index 0000000..ee0419f
--- /dev/null
+++ b/libfdt_env.h
@@ -0,0 +1,40 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 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/>.
+ *
+ * Copyright IBM Corp. 2008
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ */
+
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define fdt32_to_cpu(x) (x)
+#define cpu_to_fdt32(x) (x)
+#define fdt64_to_cpu(x) (x)
+#define cpu_to_fdt64(x) (x)
+#else
+#define fdt32_to_cpu(x) (bswap_32((x)))
+#define cpu_to_fdt32(x) (bswap_32((x)))
+#define fdt64_to_cpu(x) (bswap_64((x)))
+#define cpu_to_fdt64(x) (bswap_64((x)))
+#endif
+
+#endif /* _LIBFDT_ENV_H */
diff --git a/linux-aio.c b/linux-aio.c
new file mode 100644
index 0000000..68f4b3d
--- /dev/null
+++ b/linux-aio.c
@@ -0,0 +1,261 @@
+/*
+ * Linux native AIO support.
+ *
+ * Copyright (C) 2009 IBM, Corp.
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu-common.h"
+#include "qemu-aio.h"
+#include "block_int.h"
+#include "block/raw-posix-aio.h"
+
+#include <sys/eventfd.h>
+#include <libaio.h>
+
+/*
+ * Queue size (per-device).
+ *
+ * XXX: eventually we need to communicate this to the guest and/or make it
+ * tunable by the guest. If we get more outstanding requests at a time
+ * than this we will get EAGAIN from io_submit which is communicated to
+ * the guest as an I/O error.
+ */
+#define MAX_EVENTS 128
+
+struct qemu_laiocb {
+ BlockDriverAIOCB common;
+ struct qemu_laio_state *ctx;
+ struct iocb iocb;
+ ssize_t ret;
+ size_t nbytes;
+ int async_context_id;
+ QLIST_ENTRY(qemu_laiocb) node;
+};
+
+struct qemu_laio_state {
+ io_context_t ctx;
+ int efd;
+ int count;
+ QLIST_HEAD(, qemu_laiocb) completed_reqs;
+};
+
+static inline ssize_t io_event_ret(struct io_event *ev)
+{
+ return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res);
+}
+
+/*
+ * Completes an AIO request (calls the callback and frees the ACB).
+ * Be sure to be in the right AsyncContext before calling this function.
+ */
+static void qemu_laio_process_completion(struct qemu_laio_state *s,
+ struct qemu_laiocb *laiocb)
+{
+ int ret;
+
+ s->count--;
+
+ ret = laiocb->ret;
+ if (ret != -ECANCELED) {
+ if (ret == laiocb->nbytes)
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EINVAL;
+
+ laiocb->common.cb(laiocb->common.opaque, ret);
+ }
+
+ qemu_aio_release(laiocb);
+}
+
+/*
+ * Processes all queued AIO requests, i.e. requests that have return from OS
+ * but their callback was not called yet. Requests that cannot have their
+ * callback called in the current AsyncContext, remain in the queue.
+ *
+ * Returns 1 if at least one request could be completed, 0 otherwise.
+ */
+static int qemu_laio_process_requests(void *opaque)
+{
+ struct qemu_laio_state *s = opaque;
+ struct qemu_laiocb *laiocb, *next;
+ int res = 0;
+
+ QLIST_FOREACH_SAFE (laiocb, &s->completed_reqs, node, next) {
+ if (laiocb->async_context_id == get_async_context_id()) {
+ qemu_laio_process_completion(s, laiocb);
+ QLIST_REMOVE(laiocb, node);
+ res = 1;
+ }
+ }
+
+ return res;
+}
+
+/*
+ * Puts a request in the completion queue so that its callback is called the
+ * next time when it's possible. If we already are in the right AsyncContext,
+ * the request is completed immediately instead.
+ */
+static void qemu_laio_enqueue_completed(struct qemu_laio_state *s,
+ struct qemu_laiocb* laiocb)
+{
+ if (laiocb->async_context_id == get_async_context_id()) {
+ qemu_laio_process_completion(s, laiocb);
+ } else {
+ QLIST_INSERT_HEAD(&s->completed_reqs, laiocb, node);
+ }
+}
+
+static void qemu_laio_completion_cb(void *opaque)
+{
+ struct qemu_laio_state *s = opaque;
+
+ while (1) {
+ struct io_event events[MAX_EVENTS];
+ uint64_t val;
+ ssize_t ret;
+ struct timespec ts = { 0 };
+ int nevents, i;
+
+ do {
+ ret = read(s->efd, &val, sizeof(val));
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1 && errno == EAGAIN)
+ break;
+
+ if (ret != 8)
+ break;
+
+ do {
+ nevents = io_getevents(s->ctx, val, MAX_EVENTS, events, &ts);
+ } while (nevents == -EINTR);
+
+ for (i = 0; i < nevents; i++) {
+ struct iocb *iocb = events[i].obj;
+ struct qemu_laiocb *laiocb =
+ container_of(iocb, struct qemu_laiocb, iocb);
+
+ laiocb->ret = io_event_ret(&events[i]);
+ qemu_laio_enqueue_completed(s, laiocb);
+ }
+ }
+}
+
+static int qemu_laio_flush_cb(void *opaque)
+{
+ struct qemu_laio_state *s = opaque;
+
+ return (s->count > 0) ? 1 : 0;
+}
+
+static void laio_cancel(BlockDriverAIOCB *blockacb)
+{
+ struct qemu_laiocb *laiocb = (struct qemu_laiocb *)blockacb;
+ struct io_event event;
+ int ret;
+
+ if (laiocb->ret != -EINPROGRESS)
+ return;
+
+ /*
+ * Note that as of Linux 2.6.31 neither the block device code nor any
+ * filesystem implements cancellation of AIO request.
+ * Thus the polling loop below is the normal code path.
+ */
+ ret = io_cancel(laiocb->ctx->ctx, &laiocb->iocb, &event);
+ if (ret == 0) {
+ laiocb->ret = -ECANCELED;
+ return;
+ }
+
+ /*
+ * We have to wait for the iocb to finish.
+ *
+ * The only way to get the iocb status update is by polling the io context.
+ * We might be able to do this slightly more optimal by removing the
+ * O_NONBLOCK flag.
+ */
+ while (laiocb->ret == -EINPROGRESS)
+ qemu_laio_completion_cb(laiocb->ctx);
+}
+
+static AIOPool laio_pool = {
+ .aiocb_size = sizeof(struct qemu_laiocb),
+ .cancel = laio_cancel,
+};
+
+BlockDriverAIOCB *laio_submit(BlockDriverState *bs, void *aio_ctx, int fd,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int type)
+{
+ struct qemu_laio_state *s = aio_ctx;
+ struct qemu_laiocb *laiocb;
+ struct iocb *iocbs;
+ off_t offset = sector_num * 512;
+
+ laiocb = qemu_aio_get(&laio_pool, bs, cb, opaque);
+ if (!laiocb)
+ return NULL;
+ laiocb->nbytes = nb_sectors * 512;
+ laiocb->ctx = s;
+ laiocb->ret = -EINPROGRESS;
+ laiocb->async_context_id = get_async_context_id();
+
+ iocbs = &laiocb->iocb;
+
+ switch (type) {
+ case QEMU_AIO_WRITE:
+ io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset);
+ break;
+ case QEMU_AIO_READ:
+ io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
+ break;
+ default:
+ fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
+ __func__, type);
+ goto out_free_aiocb;
+ }
+ io_set_eventfd(&laiocb->iocb, s->efd);
+ s->count++;
+
+ if (io_submit(s->ctx, 1, &iocbs) < 0)
+ goto out_dec_count;
+ return &laiocb->common;
+
+out_free_aiocb:
+ qemu_aio_release(laiocb);
+out_dec_count:
+ s->count--;
+ return NULL;
+}
+
+void *laio_init(void)
+{
+ struct qemu_laio_state *s;
+
+ s = qemu_mallocz(sizeof(*s));
+ QLIST_INIT(&s->completed_reqs);
+ s->efd = eventfd(0, 0);
+ if (s->efd == -1)
+ goto out_free_state;
+ fcntl(s->efd, F_SETFL, O_NONBLOCK);
+
+ if (io_setup(MAX_EVENTS, &s->ctx) != 0)
+ goto out_close_efd;
+
+ qemu_aio_set_fd_handler(s->efd, qemu_laio_completion_cb, NULL,
+ qemu_laio_flush_cb, qemu_laio_process_requests, s);
+
+ return s;
+
+out_close_efd:
+ close(s->efd);
+out_free_state:
+ qemu_free(s);
+ return NULL;
+}
diff --git a/linux-user/alpha/syscall.h b/linux-user/alpha/syscall.h
new file mode 100644
index 0000000..15a0100
--- /dev/null
+++ b/linux-user/alpha/syscall.h
@@ -0,0 +1,253 @@
+/* default linux values for the selectors */
+#define __USER_DS (1)
+
+struct target_pt_regs {
+ abi_ulong r0;
+ abi_ulong r1;
+ abi_ulong r2;
+ abi_ulong r3;
+ abi_ulong r4;
+ abi_ulong r5;
+ abi_ulong r6;
+ abi_ulong r7;
+ abi_ulong r8;
+ abi_ulong r19;
+ abi_ulong r20;
+ abi_ulong r21;
+ abi_ulong r22;
+ abi_ulong r23;
+ abi_ulong r24;
+ abi_ulong r25;
+ abi_ulong r26;
+ abi_ulong r27;
+ abi_ulong r28;
+ abi_ulong hae;
+/* JRP - These are the values provided to a0-a2 by PALcode */
+ abi_ulong trap_a0;
+ abi_ulong trap_a1;
+ abi_ulong trap_a2;
+/* These are saved by PAL-code: */
+ abi_ulong ps;
+ abi_ulong pc;
+ abi_ulong gp;
+ abi_ulong r16;
+ abi_ulong r17;
+ abi_ulong r18;
+/* Those is needed by qemu to temporary store the user stack pointer */
+ abi_ulong usp;
+ abi_ulong unique;
+};
+
+#define UNAME_MACHINE "alpha"
+
+#undef TARGET_EDEADLK
+#define TARGET_EDEADLK 11
+#undef TARGET_EAGAIN
+#define TARGET_EAGAIN 35
+#undef TARGET_EINPROGRESS
+#define TARGET_EINPROGRESS 36
+#undef TARGET_EALREADY
+#define TARGET_EALREADY 37
+#undef TARGET_ENOTSOCK
+#define TARGET_ENOTSOCK 38
+#undef TARGET_EDESTADDRREQ
+#define TARGET_EDESTADDRREQ 39
+#undef TARGET_EMSGSIZE
+#define TARGET_EMSGSIZE 40
+#undef TARGET_EPROTOTYPE
+#define TARGET_EPROTOTYPE 41
+#undef TARGET_ENOPROTOOPT
+#define TARGET_ENOPROTOOPT 42
+#undef TARGET_EPROTONOSUPPORT
+#define TARGET_EPROTONOSUPPORT 43
+#undef TARGET_ESOCKTNOSUPPORT
+#define TARGET_ESOCKTNOSUPPORT 44
+#undef TARGET_EOPNOTSUPP
+#define TARGET_EOPNOTSUPP 45
+#undef TARGET_EPFNOSUPPORT
+#define TARGET_EPFNOSUPPORT 46
+#undef TARGET_EAFNOSUPPORT
+#define TARGET_EAFNOSUPPORT 47
+#undef TARGET_EADDRINUSE
+#define TARGET_EADDRINUSE 48
+#undef TARGET_EADDRNOTAVAIL
+#define TARGET_EADDRNOTAVAIL 49
+#undef TARGET_ENETDOWN
+#define TARGET_ENETDOWN 50
+#undef TARGET_ENETUNREACH
+#define TARGET_ENETUNREACH 51
+#undef TARGET_ENETRESET
+#define TARGET_ENETRESET 52
+#undef TARGET_ECONNABORTED
+#define TARGET_ECONNABORTED 53
+#undef TARGET_ECONNRESET
+#define TARGET_ECONNRESET 54
+#undef TARGET_ENOBUFS
+#define TARGET_ENOBUFS 55
+#undef TARGET_EISCONN
+#define TARGET_EISCONN 56
+#undef TARGET_ENOTCONN
+#define TARGET_ENOTCONN 57
+#undef TARGET_ESHUTDOWN
+#define TARGET_ESHUTDOWN 58
+#undef TARGET_ETOOMANYREFS
+#define TARGET_ETOOMANYREFS 59
+#undef TARGET_ETIMEDOUT
+#define TARGET_ETIMEDOUT 60
+#undef TARGET_ECONNREFUSED
+#define TARGET_ECONNREFUSED 61
+#undef TARGET_ELOOP
+#define TARGET_ELOOP 62
+#undef TARGET_ENAMETOOLONG
+#define TARGET_ENAMETOOLONG 63
+#undef TARGET_EHOSTDOWN
+#define TARGET_EHOSTDOWN 64
+#undef TARGET_EHOSTUNREACH
+#define TARGET_EHOSTUNREACH 65
+#undef TARGET_ENOTEMPTY
+#define TARGET_ENOTEMPTY 66
+// Unused 67
+#undef TARGET_EUSERS
+#define TARGET_EUSERS 68
+#undef TARGET_EDQUOT
+#define TARGET_EDQUOT 69
+#undef TARGET_ESTALE
+#define TARGET_ESTALE 70
+#undef TARGET_EREMOTE
+#define TARGET_EREMOTE 71
+// Unused 72-76
+#undef TARGET_ENOLCK
+#define TARGET_ENOLCK 77
+#undef TARGET_ENOSYS
+#define TARGET_ENOSYS 78
+// Unused 79
+#undef TARGET_ENOMSG
+#define TARGET_ENOMSG 80
+#undef TARGET_EIDRM
+#define TARGET_EIDRM 81
+#undef TARGET_ENOSR
+#define TARGET_ENOSR 82
+#undef TARGET_ETIME
+#define TARGET_ETIME 83
+#undef TARGET_EBADMSG
+#define TARGET_EBADMSG 84
+#undef TARGET_EPROTO
+#define TARGET_EPROTO 85
+#undef TARGET_ENODATA
+#define TARGET_ENODATA 86
+#undef TARGET_ENOSTR
+#define TARGET_ENOSTR 87
+#undef TARGET_ECHRNG
+#define TARGET_ECHRNG 88
+#undef TARGET_EL2NSYNC
+#define TARGET_EL2NSYNC 89
+#undef TARGET_EL3HLT
+#define TARGET_EL3HLT 90
+#undef TARGET_EL3RST
+#define TARGET_EL3RST 91
+#undef TARGET_ENOPKG
+#define TARGET_ENOPKG 92
+#undef TARGET_ELNRNG
+#define TARGET_ELNRNG 93
+#undef TARGET_EUNATCH
+#define TARGET_EUNATCH 94
+#undef TARGET_ENOCSI
+#define TARGET_ENOCSI 95
+#undef TARGET_EL2HLT
+#define TARGET_EL2HLT 96
+#undef TARGET_EBADE
+#define TARGET_EBADE 97
+#undef TARGET_EBADR
+#define TARGET_EBADR 98
+#undef TARGET_EXFULL
+#define TARGET_EXFULL 99
+#undef TARGET_ENOANO
+#define TARGET_ENOANO 100
+#undef TARGET_EBADRQC
+#define TARGET_EBADRQC 101
+#undef TARGET_EBADSLT
+#define TARGET_EBADSLT 102
+// Unused 103
+#undef TARGET_EBFONT
+#define TARGET_EBFONT 104
+#undef TARGET_ENONET
+#define TARGET_ENONET 105
+#undef TARGET_ENOLINK
+#define TARGET_ENOLINK 106
+#undef TARGET_EADV
+#define TARGET_EADV 107
+#undef TARGET_ESRMNT
+#define TARGET_ESRMNT 108
+#undef TARGET_ECOMM
+#define TARGET_ECOMM 109
+#undef TARGET_EMULTIHOP
+#define TARGET_EMULTIHOP 110
+#undef TARGET_EDOTDOT
+#define TARGET_EDOTDOT 111
+#undef TARGET_EOVERFLOW
+#define TARGET_EOVERFLOW 112
+#undef TARGET_ENOTUNIQ
+#define TARGET_ENOTUNIQ 113
+#undef TARGET_EBADFD
+#define TARGET_EBADFD 114
+#undef TARGET_EREMCHG
+#define TARGET_EREMCHG 115
+#undef TARGET_EILSEQ
+#define TARGET_EILSEQ 116
+
+// Same as default 117-121
+
+#undef TARGET_ELIBACC
+#define TARGET_ELIBACC 122
+#undef TARGET_ELIBBAD
+#define TARGET_ELIBBAD 123
+#undef TARGET_ELIBSCN
+#define TARGET_ELIBSCN 124
+#undef TARGET_ELIBMAX
+#define TARGET_ELIBMAX 125
+#undef TARGET_ELIBEXEC
+#define TARGET_ELIBEXEC 126
+#undef TARGET_ERESTART
+#define TARGET_ERESTART 127
+#undef TARGET_ESTRPIPE
+#define TARGET_ESTRPIPE 128
+#undef TARGET_ENOMEDIUM
+#define TARGET_ENOMEDIUM 129
+#undef TARGET_EMEDIUMTYPE
+#define TARGET_EMEDIUMTYPE 130
+#undef TARGET_ECANCELED
+#define TARGET_ECANCELED 131
+#undef TARGET_ENOKEY
+#define TARGET_ENOKEY 132
+#undef TARGET_EKEYEXPIRED
+#define TARGET_EKEYEXPIRED 133
+#undef TARGET_EKEYREVOKED
+#define TARGET_EKEYREVOKED 134
+#undef TARGET_EKEYREJECTED
+#define TARGET_EKEYREJECTED 135
+#undef TARGET_EOWNERDEAD
+#define TARGET_EOWNERDEAD 136
+#undef TARGET_ENOTRECOVERABLE
+#define TARGET_ENOTRECOVERABLE 137
+#undef TARGET_ERFKILL
+#define TARGET_ERFKILL 138
+
+// For sys_osf_getsysinfo
+#define TARGET_GSI_UACPROC 8
+#define TARGET_GSI_IEEE_FP_CONTROL 45
+#define TARGET_GSI_IEEE_STATE_AT_SIGNAL 46
+#define TARGET_GSI_PROC_TYPE 60
+#define TARGET_GSI_GET_HWRPB 101
+
+// For sys_ofs_setsysinfo
+#define TARGET_SSI_NVPAIRS 1
+#define TARGET_SSI_IEEE_FP_CONTROL 14
+#define TARGET_SSI_IEEE_STATE_AT_SIGNAL 15
+#define TARGET_SSI_IEEE_IGNORE_STATE_AT_SIGNAL 16
+#define TARGET_SSI_IEEE_RAISE_EXCEPTION 1001
+
+#define TARGET_SSIN_UACPROC 6
+
+#define TARGET_UAC_NOPRINT 1
+#define TARGET_UAC_NOFIX 2
+#define TARGET_UAC_SIGBUS 4
diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h
new file mode 100644
index 0000000..7182223
--- /dev/null
+++ b/linux-user/alpha/syscall_nr.h
@@ -0,0 +1,421 @@
+#define TARGET_NR_osf_syscall 0 /* not implemented */
+#define TARGET_NR_exit 1
+#define TARGET_NR_fork 2
+#define TARGET_NR_read 3
+#define TARGET_NR_write 4
+#define TARGET_NR_osf_old_open 5 /* not implemented */
+#define TARGET_NR_close 6
+#define TARGET_NR_osf_wait4 7
+#define TARGET_NR_osf_old_creat 8 /* not implemented */
+#define TARGET_NR_link 9
+#define TARGET_NR_unlink 10
+#define TARGET_NR_osf_execve 11 /* not implemented */
+#define TARGET_NR_chdir 12
+#define TARGET_NR_fchdir 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_chown 16
+#define TARGET_NR_brk 17
+#define TARGET_NR_osf_getfsstat 18 /* not implemented */
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getxpid 20
+#define TARGET_NR_osf_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getxuid 24
+#define TARGET_NR_exec_with_loader 25 /* not implemented */
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_osf_nrecvmsg 27 /* not implemented */
+#define TARGET_NR_osf_nsendmsg 28 /* not implemented */
+#define TARGET_NR_osf_nrecvfrom 29 /* not implemented */
+#define TARGET_NR_osf_naccept 30 /* not implemented */
+#define TARGET_NR_osf_ngetpeername 31 /* not implemented */
+#define TARGET_NR_osf_ngetsockname 32 /* not implemented */
+#define TARGET_NR_access 33
+#define TARGET_NR_osf_chflags 34 /* not implemented */
+#define TARGET_NR_osf_fchflags 35 /* not implemented */
+#define TARGET_NR_sync 36
+#define TARGET_NR_kill 37
+#define TARGET_NR_osf_old_stat 38 /* not implemented */
+#define TARGET_NR_setpgid 39
+#define TARGET_NR_osf_old_lstat 40 /* not implemented */
+#define TARGET_NR_dup 41
+#define TARGET_NR_pipe 42
+#define TARGET_NR_osf_set_program_attributes 43
+#define TARGET_NR_osf_profil 44 /* not implemented */
+#define TARGET_NR_open 45
+#define TARGET_NR_osf_old_sigaction 46 /* not implemented */
+#define TARGET_NR_getxgid 47
+#define TARGET_NR_osf_sigprocmask 48
+#define TARGET_NR_osf_getlogin 49 /* not implemented */
+#define TARGET_NR_osf_setlogin 50 /* not implemented */
+#define TARGET_NR_acct 51
+#define TARGET_NR_sigpending 52
+
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_osf_reboot 55 /* not implemented */
+#define TARGET_NR_osf_revoke 56 /* not implemented */
+#define TARGET_NR_symlink 57
+#define TARGET_NR_readlink 58
+#define TARGET_NR_execve 59
+#define TARGET_NR_umask 60
+#define TARGET_NR_chroot 61
+#define TARGET_NR_osf_old_fstat 62 /* not implemented */
+#define TARGET_NR_getpgrp 63
+#define TARGET_NR_getpagesize 64
+#define TARGET_NR_osf_mremap 65 /* not implemented */
+#define TARGET_NR_vfork 66
+#define TARGET_NR_stat 67
+#define TARGET_NR_lstat 68
+#define TARGET_NR_osf_sbrk 69 /* not implemented */
+#define TARGET_NR_osf_sstk 70 /* not implemented */
+#define TARGET_NR_mmap 71 /* OSF/1 mmap is superset of Linux */
+#define TARGET_NR_osf_old_vadvise 72 /* not implemented */
+#define TARGET_NR_munmap 73
+#define TARGET_NR_mprotect 74
+#define TARGET_NR_madvise 75
+#define TARGET_NR_vhangup 76
+#define TARGET_NR_osf_kmodcall 77 /* not implemented */
+#define TARGET_NR_osf_mincore 78 /* not implemented */
+#define TARGET_NR_getgroups 79
+#define TARGET_NR_setgroups 80
+#define TARGET_NR_osf_old_getpgrp 81 /* not implemented */
+#define TARGET_NR_setpgrp 82 /* BSD alias for setpgid */
+#define TARGET_NR_osf_setitimer 83
+#define TARGET_NR_osf_old_wait 84 /* not implemented */
+#define TARGET_NR_osf_table 85 /* not implemented */
+#define TARGET_NR_osf_getitimer 86
+#define TARGET_NR_gethostname 87
+#define TARGET_NR_sethostname 88
+#define TARGET_NR_getdtablesize 89
+#define TARGET_NR_dup2 90
+#define TARGET_NR_fstat 91
+#define TARGET_NR_fcntl 92
+#define TARGET_NR_osf_select 93
+#define TARGET_NR_poll 94
+#define TARGET_NR_fsync 95
+#define TARGET_NR_setpriority 96
+#define TARGET_NR_socket 97
+#define TARGET_NR_connect 98
+#define TARGET_NR_accept 99
+#define TARGET_NR_getpriority 100
+#define TARGET_NR_send 101
+#define TARGET_NR_recv 102
+#define TARGET_NR_sigreturn 103
+#define TARGET_NR_bind 104
+#define TARGET_NR_setsockopt 105
+#define TARGET_NR_listen 106
+#define TARGET_NR_osf_plock 107 /* not implemented */
+#define TARGET_NR_osf_old_sigvec 108 /* not implemented */
+#define TARGET_NR_osf_old_sigblock 109 /* not implemented */
+#define TARGET_NR_osf_old_sigsetmask 110 /* not implemented */
+#define TARGET_NR_sigsuspend 111
+#define TARGET_NR_osf_sigstack 112
+#define TARGET_NR_recvmsg 113
+#define TARGET_NR_sendmsg 114
+#define TARGET_NR_osf_old_vtrace 115 /* not implemented */
+#define TARGET_NR_osf_gettimeofday 116
+#define TARGET_NR_osf_getrusage 117
+#define TARGET_NR_getsockopt 118
+
+#define TARGET_NR_readv 120
+#define TARGET_NR_writev 121
+#define TARGET_NR_osf_settimeofday 122
+#define TARGET_NR_fchown 123
+#define TARGET_NR_fchmod 124
+#define TARGET_NR_recvfrom 125
+#define TARGET_NR_setreuid 126
+#define TARGET_NR_setregid 127
+#define TARGET_NR_rename 128
+#define TARGET_NR_truncate 129
+#define TARGET_NR_ftruncate 130
+#define TARGET_NR_flock 131
+#define TARGET_NR_setgid 132
+#define TARGET_NR_sendto 133
+#define TARGET_NR_shutdown 134
+#define TARGET_NR_socketpair 135
+#define TARGET_NR_mkdir 136
+#define TARGET_NR_rmdir 137
+#define TARGET_NR_osf_utimes 138
+#define TARGET_NR_osf_old_sigreturn 139 /* not implemented */
+#define TARGET_NR_osf_adjtime 140 /* not implemented */
+#define TARGET_NR_getpeername 141
+#define TARGET_NR_osf_gethostid 142 /* not implemented */
+#define TARGET_NR_osf_sethostid 143 /* not implemented */
+#define TARGET_NR_getrlimit 144
+#define TARGET_NR_setrlimit 145
+#define TARGET_NR_osf_old_killpg 146 /* not implemented */
+#define TARGET_NR_setsid 147
+#define TARGET_NR_quotactl 148
+#define TARGET_NR_osf_oldquota 149 /* not implemented */
+#define TARGET_NR_getsockname 150
+
+#define TARGET_NR_osf_pid_block 153 /* not implemented */
+#define TARGET_NR_osf_pid_unblock 154 /* not implemented */
+
+#define TARGET_NR_sigaction 156
+#define TARGET_NR_osf_sigwaitprim 157 /* not implemented */
+#define TARGET_NR_osf_nfssvc 158 /* not implemented */
+#define TARGET_NR_osf_getdirentries 159
+#define TARGET_NR_osf_statfs 160
+#define TARGET_NR_osf_fstatfs 161
+
+#define TARGET_NR_osf_asynch_daemon 163 /* not implemented */
+#define TARGET_NR_osf_getfh 164 /* not implemented */
+#define TARGET_NR_osf_getdomainname 165
+#define TARGET_NR_setdomainname 166
+
+#define TARGET_NR_osf_exportfs 169 /* not implemented */
+
+#define TARGET_NR_osf_alt_plock 181 /* not implemented */
+
+#define TARGET_NR_osf_getmnt 184 /* not implemented */
+
+#define TARGET_NR_osf_alt_sigpending 187 /* not implemented */
+#define TARGET_NR_osf_alt_setsid 188 /* not implemented */
+
+#define TARGET_NR_osf_swapon 199
+#define TARGET_NR_msgctl 200
+#define TARGET_NR_msgget 201
+#define TARGET_NR_msgrcv 202
+#define TARGET_NR_msgsnd 203
+#define TARGET_NR_semctl 204
+#define TARGET_NR_semget 205
+#define TARGET_NR_semop 206
+#define TARGET_NR_osf_utsname 207
+#define TARGET_NR_lchown 208
+#define TARGET_NR_osf_shmat 209
+#define TARGET_NR_shmctl 210
+#define TARGET_NR_shmdt 211
+#define TARGET_NR_shmget 212
+#define TARGET_NR_osf_mvalid 213 /* not implemented */
+#define TARGET_NR_osf_getaddressconf 214 /* not implemented */
+#define TARGET_NR_osf_msleep 215 /* not implemented */
+#define TARGET_NR_osf_mwakeup 216 /* not implemented */
+#define TARGET_NR_msync 217
+#define TARGET_NR_osf_signal 218 /* not implemented */
+#define TARGET_NR_osf_utc_gettime 219 /* not implemented */
+#define TARGET_NR_osf_utc_adjtime 220 /* not implemented */
+
+#define TARGET_NR_osf_security 222 /* not implemented */
+#define TARGET_NR_osf_kloadcall 223 /* not implemented */
+
+#define TARGET_NR_getpgid 233
+#define TARGET_NR_getsid 234
+#define TARGET_NR_sigaltstack 235
+#define TARGET_NR_osf_waitid 236 /* not implemented */
+#define TARGET_NR_osf_priocntlset 237 /* not implemented */
+#define TARGET_NR_osf_sigsendset 238 /* not implemented */
+#define TARGET_NR_osf_set_speculative 239 /* not implemented */
+#define TARGET_NR_osf_msfs_syscall 240 /* not implemented */
+#define TARGET_NR_osf_sysinfo 241
+#define TARGET_NR_osf_uadmin 242 /* not implemented */
+#define TARGET_NR_osf_fuser 243 /* not implemented */
+#define TARGET_NR_osf_proplist_syscall 244
+#define TARGET_NR_osf_ntp_adjtime 245 /* not implemented */
+#define TARGET_NR_osf_ntp_gettime 246 /* not implemented */
+#define TARGET_NR_osf_pathconf 247 /* not implemented */
+#define TARGET_NR_osf_fpathconf 248 /* not implemented */
+
+#define TARGET_NR_osf_uswitch 250 /* not implemented */
+#define TARGET_NR_osf_usleep_thread 251
+#define TARGET_NR_osf_audcntl 252 /* not implemented */
+#define TARGET_NR_osf_audgen 253 /* not implemented */
+#define TARGET_NR_sysfs 254
+#define TARGET_NR_osf_subsys_info 255 /* not implemented */
+#define TARGET_NR_osf_getsysinfo 256
+#define TARGET_NR_osf_setsysinfo 257
+#define TARGET_NR_osf_afs_syscall 258 /* not implemented */
+#define TARGET_NR_osf_swapctl 259 /* not implemented */
+#define TARGET_NR_osf_memcntl 260 /* not implemented */
+#define TARGET_NR_osf_fdatasync 261 /* not implemented */
+
+
+/*
+ * Linux-specific system calls begin at 300
+ */
+#define TARGET_NR_bdflush 300
+#define TARGET_NR_sethae 301
+#define TARGET_NR_mount 302
+#define TARGET_NR_old_adjtimex 303
+#define TARGET_NR_swapoff 304
+#define TARGET_NR_getdents 305
+#define TARGET_NR_create_module 306
+#define TARGET_NR_init_module 307
+#define TARGET_NR_delete_module 308
+#define TARGET_NR_get_kernel_syms 309
+#define TARGET_NR_syslog 310
+#define TARGET_NR_reboot 311
+#define TARGET_NR_clone 312
+#define TARGET_NR_uselib 313
+#define TARGET_NR_mlock 314
+#define TARGET_NR_munlock 315
+#define TARGET_NR_mlockall 316
+#define TARGET_NR_munlockall 317
+#define TARGET_NR_sysinfo 318
+#define TARGET_NR__sysctl 319
+/* 320 was sys_idle. */
+#define TARGET_NR_oldumount 321
+#define TARGET_NR_swapon 322
+#define TARGET_NR_times 323
+#define TARGET_NR_personality 324
+#define TARGET_NR_setfsuid 325
+#define TARGET_NR_setfsgid 326
+#define TARGET_NR_ustat 327
+#define TARGET_NR_statfs 328
+#define TARGET_NR_fstatfs 329
+#define TARGET_NR_sched_setparam 330
+#define TARGET_NR_sched_getparam 331
+#define TARGET_NR_sched_setscheduler 332
+#define TARGET_NR_sched_getscheduler 333
+#define TARGET_NR_sched_yield 334
+#define TARGET_NR_sched_get_priority_max 335
+#define TARGET_NR_sched_get_priority_min 336
+#define TARGET_NR_sched_rr_get_interval 337
+#define TARGET_NR_afs_syscall 338
+#define TARGET_NR_uname 339
+#define TARGET_NR_nanosleep 340
+#define TARGET_NR_mremap 341
+#define TARGET_NR_nfsservctl 342
+#define TARGET_NR_setresuid 343
+#define TARGET_NR_getresuid 344
+#define TARGET_NR_pciconfig_read 345
+#define TARGET_NR_pciconfig_write 346
+#define TARGET_NR_query_module 347
+#define TARGET_NR_prctl 348
+#define TARGET_NR_pread64 349
+#define TARGET_NR_pwrite64 350
+#define TARGET_NR_rt_sigreturn 351
+#define TARGET_NR_rt_sigaction 352
+#define TARGET_NR_rt_sigprocmask 353
+#define TARGET_NR_rt_sigpending 354
+#define TARGET_NR_rt_sigtimedwait 355
+#define TARGET_NR_rt_sigqueueinfo 356
+#define TARGET_NR_rt_sigsuspend 357
+#define TARGET_NR_select 358
+#define TARGET_NR_gettimeofday 359
+#define TARGET_NR_settimeofday 360
+#define TARGET_NR_getitimer 361
+#define TARGET_NR_setitimer 362
+#define TARGET_NR_utimes 363
+#define TARGET_NR_getrusage 364
+#define TARGET_NR_wait4 365
+#define TARGET_NR_adjtimex 366
+#define TARGET_NR_getcwd 367
+#define TARGET_NR_capget 368
+#define TARGET_NR_capset 369
+#define TARGET_NR_sendfile 370
+#define TARGET_NR_setresgid 371
+#define TARGET_NR_getresgid 372
+#define TARGET_NR_dipc 373
+#define TARGET_NR_pivot_root 374
+#define TARGET_NR_mincore 375
+#define TARGET_NR_pciconfig_iobase 376
+#define TARGET_NR_getdents64 377
+#define TARGET_NR_gettid 378
+#define TARGET_NR_readahead 379
+/* 380 is unused */
+#define TARGET_NR_tkill 381
+#define TARGET_NR_setxattr 382
+#define TARGET_NR_lsetxattr 383
+#define TARGET_NR_fsetxattr 384
+#define TARGET_NR_getxattr 385
+#define TARGET_NR_lgetxattr 386
+#define TARGET_NR_fgetxattr 387
+#define TARGET_NR_listxattr 388
+#define TARGET_NR_llistxattr 389
+#define TARGET_NR_flistxattr 390
+#define TARGET_NR_removexattr 391
+#define TARGET_NR_lremovexattr 392
+#define TARGET_NR_fremovexattr 393
+#define TARGET_NR_futex 394
+#define TARGET_NR_sched_setaffinity 395
+#define TARGET_NR_sched_getaffinity 396
+#define TARGET_NR_tuxcall 397
+#define TARGET_NR_io_setup 398
+#define TARGET_NR_io_destroy 399
+#define TARGET_NR_io_getevents 400
+#define TARGET_NR_io_submit 401
+#define TARGET_NR_io_cancel 402
+#define TARGET_NR_exit_group 405
+#define TARGET_NR_lookup_dcookie 406
+#define TARGET_NR_sys_epoll_create 407
+#define TARGET_NR_sys_epoll_ctl 408
+#define TARGET_NR_sys_epoll_wait 409
+#define TARGET_NR_remap_file_pages 410
+#define TARGET_NR_set_tid_address 411
+#define TARGET_NR_restart_syscall 412
+#define TARGET_NR_fadvise64 413
+#define TARGET_NR_timer_create 414
+#define TARGET_NR_timer_settime 415
+#define TARGET_NR_timer_gettime 416
+#define TARGET_NR_timer_getoverrun 417
+#define TARGET_NR_timer_delete 418
+#define TARGET_NR_clock_settime 419
+#define TARGET_NR_clock_gettime 420
+#define TARGET_NR_clock_getres 421
+#define TARGET_NR_clock_nanosleep 422
+#define TARGET_NR_semtimedop 423
+#define TARGET_NR_tgkill 424
+#define TARGET_NR_stat64 425
+#define TARGET_NR_lstat64 426
+#define TARGET_NR_fstat64 427
+#define TARGET_NR_vserver 428
+#define TARGET_NR_mbind 429
+#define TARGET_NR_get_mempolicy 430
+#define TARGET_NR_set_mempolicy 431
+#define TARGET_NR_mq_open 432
+#define TARGET_NR_mq_unlink 433
+#define TARGET_NR_mq_timedsend 434
+#define TARGET_NR_mq_timedreceive 435
+#define TARGET_NR_mq_notify 436
+#define TARGET_NR_mq_getsetattr 437
+#define TARGET_NR_waitid 438
+#define TARGET_NR_add_key 439
+#define TARGET_NR_request_key 440
+#define TARGET_NR_keyctl 441
+#define TARGET_NR_ioprio_set 442
+#define TARGET_NR_ioprio_get 443
+#define TARGET_NR_inotify_init 444
+#define TARGET_NR_inotify_add_watch 445
+#define TARGET_NR_inotify_rm_watch 446
+#define TARGET_NR_fdatasync 447
+#define TARGET_NR_kexec_load 448
+#define TARGET_NR_migrate_pages 449
+#define TARGET_NR_openat 450
+#define TARGET_NR_mkdirat 451
+#define TARGET_NR_mknodat 452
+#define TARGET_NR_fchownat 453
+#define TARGET_NR_futimesat 454
+#define TARGET_NR_fstatat64 455
+#define TARGET_NR_unlinkat 456
+#define TARGET_NR_renameat 457
+#define TARGET_NR_linkat 458
+#define TARGET_NR_symlinkat 459
+#define TARGET_NR_readlinkat 460
+#define TARGET_NR_fchmodat 461
+#define TARGET_NR_faccessat 462
+#define TARGET_NR_pselect6 463
+#define TARGET_NR_ppoll 464
+#define TARGET_NR_unshare 465
+#define TARGET_NR_set_robust_list 466
+#define TARGET_NR_get_robust_list 467
+#define TARGET_NR_splice 468
+#define TARGET_NR_sync_file_range 469
+#define TARGET_NR_tee 470
+#define TARGET_NR_vmsplice 471
+#define TARGET_NR_move_pages 472
+#define TARGET_NR_getcpu 473
+#define TARGET_NR_epoll_pwait 474
+#define TARGET_NR_utimensat 475
+#define TARGET_NR_signalfd 476
+#define TARGET_NR_timerfd 477
+#define TARGET_NR_eventfd 478
+
+/* The following aliases are defined in order to match up with the
+ standard i386 syscalls implemented in syscalls.c. */
+#define TARGET_NR_chown32 TARGET_NR_chown
+#define TARGET_NR_setuid32 TARGET_NR_setuid
+#define TARGET_NR_setgid32 TARGET_NR_setgid
+#define TARGET_NR_setfsuid32 TARGET_NR_setfsuid
+#define TARGET_NR_setfsgid32 TARGET_NR_setfsgid
diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h
new file mode 100644
index 0000000..94f15f6
--- /dev/null
+++ b/linux-user/alpha/target_signal.h
@@ -0,0 +1,56 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+
+/*
+ * sigaltstack controls
+ */
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 4096
+#define TARGET_SIGSTKSZ 16384
+
+static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state)
+{
+ return state->ir[IR_SP];
+}
+
+/* From <asm/gentrap.h>. */
+#define TARGET_GEN_INTOVF -1 /* integer overflow */
+#define TARGET_GEN_INTDIV -2 /* integer division by zero */
+#define TARGET_GEN_FLTOVF -3 /* fp overflow */
+#define TARGET_GEN_FLTDIV -4 /* fp division by zero */
+#define TARGET_GEN_FLTUND -5 /* fp underflow */
+#define TARGET_GEN_FLTINV -6 /* invalid fp operand */
+#define TARGET_GEN_FLTINE -7 /* inexact fp operand */
+#define TARGET_GEN_DECOVF -8 /* decimal overflow (for COBOL??) */
+#define TARGET_GEN_DECDIV -9 /* decimal division by zero */
+#define TARGET_GEN_DECINV -10 /* invalid decimal operand */
+#define TARGET_GEN_ROPRAND -11 /* reserved operand */
+#define TARGET_GEN_ASSERTERR -12 /* assertion error */
+#define TARGET_GEN_NULPTRERR -13 /* null pointer error */
+#define TARGET_GEN_STKOVF -14 /* stack overflow */
+#define TARGET_GEN_STRLENERR -15 /* string length error */
+#define TARGET_GEN_SUBSTRERR -16 /* substring error */
+#define TARGET_GEN_RANGERR -17 /* range error */
+#define TARGET_GEN_SUBRNG -18
+#define TARGET_GEN_SUBRNG1 -19
+#define TARGET_GEN_SUBRNG2 -20
+#define TARGET_GEN_SUBRNG3 -21
+#define TARGET_GEN_SUBRNG4 -22
+#define TARGET_GEN_SUBRNG5 -23
+#define TARGET_GEN_SUBRNG6 -24
+#define TARGET_GEN_SUBRNG7 -25
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/alpha/termbits.h b/linux-user/alpha/termbits.h
new file mode 100644
index 0000000..6406b6a
--- /dev/null
+++ b/linux-user/alpha/termbits.h
@@ -0,0 +1,264 @@
+typedef unsigned char target_cc_t;
+typedef unsigned int target_speed_t;
+typedef unsigned int target_tcflag_t;
+
+#define TARGET_NCCS 19
+struct target_termios {
+ target_tcflag_t c_iflag; /* input mode flags */
+ target_tcflag_t c_oflag; /* output mode flags */
+ target_tcflag_t c_cflag; /* control mode flags */
+ target_tcflag_t c_lflag; /* local mode flags */
+ target_cc_t c_cc[TARGET_NCCS]; /* control characters */
+ target_cc_t c_line; /* line discipline (== c_cc[19]) */
+ target_speed_t c_ispeed; /* input speed */
+ target_speed_t c_ospeed; /* output speed */
+};
+
+/* c_cc characters */
+#define TARGET_VEOF 0
+#define TARGET_VEOL 1
+#define TARGET_VEOL2 2
+#define TARGET_VERASE 3
+#define TARGET_VWERASE 4
+#define TARGET_VKILL 5
+#define TARGET_VREPRINT 6
+#define TARGET_VSWTC 7
+#define TARGET_VINTR 8
+#define TARGET_VQUIT 9
+#define TARGET_VSUSP 10
+#define TARGET_VSTART 12
+#define TARGET_VSTOP 13
+#define TARGET_VLNEXT 14
+#define TARGET_VDISCARD 15
+#define TARGET_VMIN 16
+#define TARGET_VTIME 17
+
+/* 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_IXON 0001000
+#define TARGET_IXOFF 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IUCLC 0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8 0040000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_ONLCR 0000002
+#define TARGET_OLCUC 0000004
+
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+
+#define TARGET_OFILL 00000100
+#define TARGET_OFDEL 00000200
+#define TARGET_NLDLY 00001400
+#define TARGET_NL0 00000000
+#define TARGET_NL1 00000400
+#define TARGET_NL2 00001000
+#define TARGET_NL3 00001400
+#define TARGET_TABDLY 00006000
+#define TARGET_TAB0 00000000
+#define TARGET_TAB1 00002000
+#define TARGET_TAB2 00004000
+#define TARGET_TAB3 00006000
+#define TARGET_CRDLY 00030000
+#define TARGET_CR0 00000000
+#define TARGET_CR1 00010000
+#define TARGET_CR2 00020000
+#define TARGET_CR3 00030000
+#define TARGET_FFDLY 00040000
+#define TARGET_FF0 00000000
+#define TARGET_FF1 00040000
+#define TARGET_BSDLY 00100000
+#define TARGET_BS0 00000000
+#define TARGET_BS1 00100000
+#define TARGET_VTDLY 00200000
+#define TARGET_VT0 00000000
+#define TARGET_VT1 00200000
+#define TARGET_XTABS 01000000 /* Hmm.. Linux/i386 considers this part of TABDLY.. */
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0000037
+#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_CBAUDEX 0000000
+#define TARGET_B57600 00020
+#define TARGET_B115200 00021
+#define TARGET_B230400 00022
+#define TARGET_B460800 00023
+#define TARGET_B500000 00024
+#define TARGET_B576000 00025
+#define TARGET_B921600 00026
+#define TARGET_B1000000 00027
+#define TARGET_B1152000 00030
+#define TARGET_B1500000 00031
+#define TARGET_B2000000 00032
+#define TARGET_B2500000 00033
+#define TARGET_B3000000 00034
+#define TARGET_B3500000 00035
+#define TARGET_B4000000 00036
+
+#define TARGET_CSIZE 00001400
+#define TARGET_CS5 00000000
+#define TARGET_CS6 00000400
+#define TARGET_CS7 00001000
+#define TARGET_CS8 00001400
+
+#define TARGET_CSTOPB 00002000
+#define TARGET_CREAD 00004000
+#define TARGET_PARENB 00010000
+#define TARGET_PARODD 00020000
+#define TARGET_HUPCL 00040000
+
+#define TARGET_CLOCAL 00100000
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000080
+#define TARGET_ICANON 0x00000100
+#define TARGET_XCASE 0x00004000
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000002
+#define TARGET_ECHOK 0x00000004
+#define TARGET_ECHONL 0x00000010
+#define TARGET_NOFLSH 0x80000000
+#define TARGET_TOSTOP 0x00400000
+#define TARGET_ECHOCTL 0x00000040
+#define TARGET_ECHOPRT 0x00000020
+#define TARGET_ECHOKE 0x00000001
+#define TARGET_FLUSHO 0x00800000
+#define TARGET_PENDIN 0x20000000
+#define TARGET_IEXTEN 0x00000400
+
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ FIONREAD
+#define TARGET_FIOQSIZE TARGET_IOR('f', 128, loff_t)
+
+#define TARGET_TIOCGETP TARGET_IOR('t', 8, struct target_sgttyb)
+#define TARGET_TIOCSETP TARGET_IOW('t', 9, struct target_sgttyb)
+#define TARGET_TIOCSETN TARGET_IOW('t', 10, struct target_sgttyb) /* TIOCSETP wo flush */
+
+#define TARGET_TIOCSETC TARGET_IOW('t', 17, struct target_tchars)
+#define TARGET_TIOCGETC TARGET_IOR('t', 18, struct target_tchars)
+#define TARGET_TCGETS TARGET_IOR('t', 19, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('t', 20, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('t', 21, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('t', 22, struct target_termios)
+
+#define TARGET_TCGETA TARGET_IOR('t', 23, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('t', 24, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('t', 25, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('t', 28, struct target_termio)
+
+#define TARGET_TCSBRK TARGET_IO('t', 29)
+#define TARGET_TCXONC TARGET_IO('t', 30)
+#define TARGET_TCFLSH TARGET_IO('t', 31)
+
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct target_winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct target_winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCGLTC TARGET_IOR('t', 116, struct target_ltchars)
+#define TARGET_TIOCSLTC TARGET_IOW('t', 117, struct target_ltchars)
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+
+#define TARGET_TIOCEXCL 0x540C
+#define TARGET_TIOCNXCL 0x540D
+#define TARGET_TIOCSCTTY 0x540E
+
+#define TARGET_TIOCSTI 0x5412
+#define TARGET_TIOCMGET 0x5415
+#define TARGET_TIOCMBIS 0x5416
+#define TARGET_TIOCMBIC 0x5417
+#define TARGET_TIOCMSET 0x5418
+# define TARGET_TIOCM_LE 0x001
+# define TARGET_TIOCM_DTR 0x002
+# define TARGET_TIOCM_RTS 0x004
+# define TARGET_TIOCM_ST 0x008
+# define TARGET_TIOCM_SR 0x010
+# define TARGET_TIOCM_CTS 0x020
+# define TARGET_TIOCM_CAR 0x040
+# define TARGET_TIOCM_RNG 0x080
+# define TARGET_TIOCM_DSR 0x100
+# define TARGET_TIOCM_CD TIOCM_CAR
+# define TARGET_TIOCM_RI TIOCM_RNG
+# define TARGET_TIOCM_OUT1 0x2000
+# define TARGET_TIOCM_OUT2 0x4000
+# define TARGET_TIOCM_LOOP 0x8000
+
+#define TARGET_TIOCGSOFTCAR 0x5419
+#define TARGET_TIOCSSOFTCAR 0x541A
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCCONS 0x541D
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TIOCPKT 0x5420
+# 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_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_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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 */
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+# define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+#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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
diff --git a/linux-user/arm/nwfpe/double_cpdo.c b/linux-user/arm/nwfpe/double_cpdo.c
new file mode 100644
index 0000000..8e9b28f
--- /dev/null
+++ b/linux-user/arm/nwfpe/double_cpdo.c
@@ -0,0 +1,295 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+
+float64 float64_exp(float64 Fm);
+float64 float64_ln(float64 Fm);
+float64 float64_sin(float64 rFm);
+float64 float64_cos(float64 rFm);
+float64 float64_arcsin(float64 rFm);
+float64 float64_arctan(float64 rFm);
+float64 float64_log(float64 rFm);
+float64 float64_tan(float64 rFm);
+float64 float64_arccos(float64 rFm);
+float64 float64_pow(float64 rFn,float64 rFm);
+float64 float64_pol(float64 rFn,float64 rFm);
+
+unsigned int DoubleCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ float64 rFm, rFn = float64_zero;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ //printk("DoubleCPDO(0x%08x)\n",opcode);
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getDoubleConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ rFm = float32_to_float64(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFm = fpa11->fpreg[Fm].fDouble;
+ break;
+
+ case typeExtended:
+ // !! patb
+ //printk("not implemented! why not?\n");
+ //!! ScottB
+ // should never get here, if extended involved
+ // then other operand should be promoted then
+ // ExtendedCPDO called.
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ rFn = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFn = fpa11->fpreg[Fn].fDouble;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ /* !! this switch isn't optimized; better (opcode & MASK_ARITHMETIC_OPCODE)>>24, sort of */
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_add(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_mul(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sub(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sub(rFm,rFn, &fpa11->fp_status);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_div(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_div(rFm,rFn, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_rem(rFn,rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fDouble = rFm;
+ break;
+
+ case MNF_CODE:
+ {
+ unsigned int *p = (unsigned int*)&rFm;
+#ifdef HOST_WORDS_BIGENDIAN
+ p[0] ^= 0x80000000;
+#else
+ p[1] ^= 0x80000000;
+#endif
+ fpa11->fpreg[Fd].fDouble = rFm;
+ }
+ break;
+
+ case ABS_CODE:
+ {
+ unsigned int *p = (unsigned int*)&rFm;
+#ifdef HOST_WORDS_BIGENDIAN
+ p[0] &= 0x7fffffff;
+#else
+ p[1] &= 0x7fffffff;
+#endif
+ fpa11->fpreg[Fd].fDouble = rFm;
+ }
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_round_to_int(rFm, &fpa11->fp_status);
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sqrt(rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fDouble = float64_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fType[Fd] = typeDouble;
+ return nRc;
+}
+
+#if 0
+float64 float64_exp(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_ln(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_sin(float64 rFm)
+{
+ return rFm;
+//series
+}
+
+float64 float64_cos(float64 rFm)
+{
+ return rFm;
+ //series
+}
+
+#if 0
+float64 float64_arcsin(float64 rFm)
+{
+//series
+}
+
+float64 float64_arctan(float64 rFm)
+{
+ //series
+}
+#endif
+
+float64 float64_log(float64 rFm)
+{
+ return float64_div(float64_ln(rFm),getDoubleConstant(7));
+}
+
+float64 float64_tan(float64 rFm)
+{
+ return float64_div(float64_sin(rFm),float64_cos(rFm));
+}
+
+float64 float64_arccos(float64 rFm)
+{
+return rFm;
+ //return float64_sub(halfPi,float64_arcsin(rFm));
+}
+
+float64 float64_pow(float64 rFn,float64 rFm)
+{
+ return float64_exp(float64_mul(rFm,float64_ln(rFn)));
+}
+
+float64 float64_pol(float64 rFn,float64 rFm)
+{
+ return float64_arctan(float64_div(rFn,rFm));
+}
+#endif
diff --git a/linux-user/arm/nwfpe/extended_cpdo.c b/linux-user/arm/nwfpe/extended_cpdo.c
new file mode 100644
index 0000000..880ce03
--- /dev/null
+++ b/linux-user/arm/nwfpe/extended_cpdo.c
@@ -0,0 +1,272 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+
+floatx80 floatx80_exp(floatx80 Fm);
+floatx80 floatx80_ln(floatx80 Fm);
+floatx80 floatx80_sin(floatx80 rFm);
+floatx80 floatx80_cos(floatx80 rFm);
+floatx80 floatx80_arcsin(floatx80 rFm);
+floatx80 floatx80_arctan(floatx80 rFm);
+floatx80 floatx80_log(floatx80 rFm);
+floatx80 floatx80_tan(floatx80 rFm);
+floatx80 floatx80_arccos(floatx80 rFm);
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm);
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm);
+
+unsigned int ExtendedCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ floatx80 rFm, rFn;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ //printk("ExtendedCPDO(0x%08x)\n",opcode);
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getExtendedConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ rFm = fpa11->fpreg[Fm].fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ rFn = fpa11->fpreg[Fn].fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_add(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_mul(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sub(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sub(rFm,rFn, &fpa11->fp_status);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_div(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_div(rFm,rFn, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_rem(rFn,rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fExtended = rFm;
+ break;
+
+ case MNF_CODE:
+ rFm.high ^= 0x8000;
+ fpa11->fpreg[Fd].fExtended = rFm;
+ break;
+
+ case ABS_CODE:
+ rFm.high &= 0x7fff;
+ fpa11->fpreg[Fd].fExtended = rFm;
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_round_to_int(rFm, &fpa11->fp_status);
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sqrt(rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fExtended = floatx80_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fType[Fd] = typeExtended;
+ return nRc;
+}
+
+#if 0
+floatx80 floatx80_exp(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_ln(floatx80 Fm)
+{
+//series
+}
+
+floatx80 floatx80_sin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_cos(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arcsin(floatx80 rFm)
+{
+//series
+}
+
+floatx80 floatx80_arctan(floatx80 rFm)
+{
+ //series
+}
+
+floatx80 floatx80_log(floatx80 rFm)
+{
+ return floatx80_div(floatx80_ln(rFm),getExtendedConstant(7));
+}
+
+floatx80 floatx80_tan(floatx80 rFm)
+{
+ return floatx80_div(floatx80_sin(rFm),floatx80_cos(rFm));
+}
+
+floatx80 floatx80_arccos(floatx80 rFm)
+{
+ //return floatx80_sub(halfPi,floatx80_arcsin(rFm));
+}
+
+floatx80 floatx80_pow(floatx80 rFn,floatx80 rFm)
+{
+ return floatx80_exp(floatx80_mul(rFm,floatx80_ln(rFn)));
+}
+
+floatx80 floatx80_pol(floatx80 rFn,floatx80 rFm)
+{
+ return floatx80_arctan(floatx80_div(rFn,rFm));
+}
+#endif
diff --git a/linux-user/arm/nwfpe/fpa11.c b/linux-user/arm/nwfpe/fpa11.c
new file mode 100644
index 0000000..0a87c43
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpa11.c
@@ -0,0 +1,237 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+
+#include "fpopcode.h"
+
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+//#include <asm/system.h>
+
+#include <stdio.h>
+
+FPA11* qemufpa = NULL;
+CPUARMState* user_registers;
+
+/* Reset the FPA11 chip. Called to initialize and reset the emulator. */
+void resetFPA11(void)
+{
+ int i;
+ FPA11 *fpa11 = GET_FPA11();
+
+ /* initialize the register type array */
+ for (i=0;i<=7;i++)
+ {
+ fpa11->fType[i] = typeNone;
+ }
+
+ /* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */
+ fpa11->fpsr = FP_EMULATOR | BIT_AC;
+
+ /* FPCR: set SB, AB and DA bits, clear all others */
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr = MASK_RESET;
+#endif
+}
+
+void SetRoundingMode(const unsigned int opcode)
+{
+ int rounding_mode;
+ FPA11 *fpa11 = GET_FPA11();
+
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr &= ~MASK_ROUNDING_MODE;
+#endif
+ switch (opcode & MASK_ROUNDING_MODE)
+ {
+ default:
+ case ROUND_TO_NEAREST:
+ rounding_mode = float_round_nearest_even;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_NEAREST;
+#endif
+ break;
+
+ case ROUND_TO_PLUS_INFINITY:
+ rounding_mode = float_round_up;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
+#endif
+ break;
+
+ case ROUND_TO_MINUS_INFINITY:
+ rounding_mode = float_round_down;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
+#endif
+ break;
+
+ case ROUND_TO_ZERO:
+ rounding_mode = float_round_to_zero;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_TO_ZERO;
+#endif
+ break;
+ }
+ set_float_rounding_mode(rounding_mode, &fpa11->fp_status);
+}
+
+void SetRoundingPrecision(const unsigned int opcode)
+{
+ int rounding_precision;
+ FPA11 *fpa11 = GET_FPA11();
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
+#endif
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case ROUND_SINGLE:
+ rounding_precision = 32;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_SINGLE;
+#endif
+ break;
+
+ case ROUND_DOUBLE:
+ rounding_precision = 64;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_DOUBLE;
+#endif
+ break;
+
+ case ROUND_EXTENDED:
+ rounding_precision = 80;
+#ifdef MAINTAIN_FPCR
+ fpa11->fpcr |= ROUND_EXTENDED;
+#endif
+ break;
+
+ default: rounding_precision = 80;
+ }
+ set_floatx80_rounding_precision(rounding_precision, &fpa11->fp_status);
+}
+
+/* Emulate the instruction in the opcode. */
+/* ??? This is not thread safe. */
+unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs)
+{
+ unsigned int nRc = 0;
+// unsigned long flags;
+ FPA11 *fpa11;
+// save_flags(flags); sti();
+
+ qemufpa=qfpa;
+ user_registers=qregs;
+
+#if 0
+ fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n",
+ opcode, qregs[REG_PC]);
+#endif
+ fpa11 = GET_FPA11();
+
+ if (fpa11->initflag == 0) /* good place for __builtin_expect */
+ {
+ resetFPA11();
+ SetRoundingMode(ROUND_TO_NEAREST);
+ SetRoundingPrecision(ROUND_EXTENDED);
+ fpa11->initflag = 1;
+ }
+
+ set_float_exception_flags(0, &fpa11->fp_status);
+
+ if (TEST_OPCODE(opcode,MASK_CPRT))
+ {
+ //fprintf(stderr,"emulating CPRT\n");
+ /* Emulate conversion opcodes. */
+ /* Emulate register transfer opcodes. */
+ /* Emulate comparison opcodes. */
+ nRc = EmulateCPRT(opcode);
+ }
+ else if (TEST_OPCODE(opcode,MASK_CPDO))
+ {
+ //fprintf(stderr,"emulating CPDO\n");
+ /* Emulate monadic arithmetic opcodes. */
+ /* Emulate dyadic arithmetic opcodes. */
+ nRc = EmulateCPDO(opcode);
+ }
+ else if (TEST_OPCODE(opcode,MASK_CPDT))
+ {
+ //fprintf(stderr,"emulating CPDT\n");
+ /* Emulate load/store opcodes. */
+ /* Emulate load/store multiple opcodes. */
+ nRc = EmulateCPDT(opcode);
+ }
+ else
+ {
+ /* Invalid instruction detected. Return FALSE. */
+ nRc = 0;
+ }
+
+// restore_flags(flags);
+ if(nRc == 1 && get_float_exception_flags(&fpa11->fp_status))
+ {
+ //printf("fef 0x%x\n",float_exception_flags);
+ nRc = -get_float_exception_flags(&fpa11->fp_status);
+ }
+
+ //printf("returning %d\n",nRc);
+ return(nRc);
+}
+
+#if 0
+unsigned int EmulateAll1(unsigned int opcode)
+{
+ switch ((opcode >> 24) & 0xf)
+ {
+ case 0xc:
+ case 0xd:
+ if ((opcode >> 20) & 0x1)
+ {
+ switch ((opcode >> 8) & 0xf)
+ {
+ case 0x1: return PerformLDF(opcode); break;
+ case 0x2: return PerformLFM(opcode); break;
+ default: return 0;
+ }
+ }
+ else
+ {
+ switch ((opcode >> 8) & 0xf)
+ {
+ case 0x1: return PerformSTF(opcode); break;
+ case 0x2: return PerformSFM(opcode); break;
+ default: return 0;
+ }
+ }
+ break;
+
+ case 0xe:
+ if (opcode & 0x10)
+ return EmulateCPDO(opcode);
+ else
+ return EmulateCPRT(opcode);
+ break;
+
+ default: return 0;
+ }
+}
+#endif
diff --git a/linux-user/arm/nwfpe/fpa11.h b/linux-user/arm/nwfpe/fpa11.h
new file mode 100644
index 0000000..f17647b
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpa11.h
@@ -0,0 +1,130 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.com, 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#ifndef __FPA11_H__
+#define __FPA11_H__
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <cpu.h>
+
+#define GET_FPA11() (qemufpa)
+
+/*
+ * The processes registers are always at the very top of the 8K
+ * stack+task struct. Use the same method as 'current' uses to
+ * reach them.
+ */
+extern CPUARMState *user_registers;
+
+#define GET_USERREG() (user_registers)
+
+/* Need task_struct */
+//#include <linux/sched.h>
+
+/* includes */
+#include "fpsr.h" /* FP control and status register definitions */
+#include "softfloat.h"
+
+#define typeNone 0x00
+#define typeSingle 0x01
+#define typeDouble 0x02
+#define typeExtended 0x03
+
+/*
+ * This must be no more and no less than 12 bytes.
+ */
+typedef union tagFPREG {
+ floatx80 fExtended;
+ float64 fDouble;
+ float32 fSingle;
+} FPREG;
+
+/*
+ * FPA11 device model.
+ *
+ * This structure is exported to user space. Do not re-order.
+ * Only add new stuff to the end, and do not change the size of
+ * any element. Elements of this structure are used by user
+ * space, and must match struct user_fp in include/asm-arm/user.h.
+ * We include the byte offsets below for documentation purposes.
+ *
+ * The size of this structure and FPREG are checked by fpmodule.c
+ * on initialisation. If the rules have been broken, NWFPE will
+ * not initialise.
+ */
+typedef struct tagFPA11 {
+/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */
+/* 96 */ FPSR fpsr; /* floating point status register */
+/* 100 */ FPCR fpcr; /* floating point control register */
+/* 104 */ unsigned char fType[8]; /* type of floating point value held in
+ floating point registers. One of none
+ single, double or extended. */
+/* 112 */ int initflag; /* this is special. The kernel guarantees
+ to set it to 0 when a thread is launched,
+ so we can use it to detect whether this
+ instance of the emulator needs to be
+ initialised. */
+ float_status fp_status; /* QEMU float emulator status */
+} FPA11;
+
+extern FPA11* qemufpa;
+
+void resetFPA11(void);
+void SetRoundingMode(const unsigned int);
+void SetRoundingPrecision(const unsigned int);
+
+static inline unsigned int readRegister(unsigned int reg)
+{
+ return (user_registers->regs[(reg)]);
+}
+
+static inline void writeRegister(unsigned int x, unsigned int y)
+{
+#if 0
+ printf("writing %d to r%d\n",y,x);
+#endif
+ user_registers->regs[(x)]=(y);
+}
+
+static inline void writeConditionCodes(unsigned int x)
+{
+ cpsr_write(user_registers,x,CPSR_NZCV);
+}
+
+#define REG_PC 15
+
+unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs);
+
+unsigned int EmulateCPDO(const unsigned int);
+unsigned int EmulateCPDT(const unsigned int);
+unsigned int EmulateCPRT(const unsigned int);
+
+unsigned int SingleCPDO(const unsigned int opcode);
+unsigned int DoubleCPDO(const unsigned int opcode);
+unsigned int ExtendedCPDO(const unsigned int opcode);
+
+
+/* included only for get_user/put_user macros */
+#include "qemu.h"
+
+#endif
diff --git a/linux-user/arm/nwfpe/fpa11.inl b/linux-user/arm/nwfpe/fpa11.inl
new file mode 100644
index 0000000..6c6f380
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpa11.inl
@@ -0,0 +1,50 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+
+/* Read and write floating point status register */
+static inline unsigned int readFPSR(void)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ return(fpa11->fpsr);
+}
+
+static inline void writeFPSR(FPSR reg)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ /* the sysid byte in the status register is readonly */
+ fpa11->fpsr = (fpa11->fpsr & MASK_SYSID) | (reg & ~MASK_SYSID);
+}
+
+/* Read and write floating point control register */
+static inline FPCR readFPCR(void)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ /* clear SB, AB and DA bits before returning FPCR */
+ return(fpa11->fpcr & ~MASK_RFC);
+}
+
+static inline void writeFPCR(FPCR reg)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ fpa11->fpcr &= ~MASK_WFC; /* clear SB, AB and DA bits */
+ fpa11->fpcr |= (reg & MASK_WFC); /* write SB, AB and DA bits */
+}
diff --git a/linux-user/arm/nwfpe/fpa11_cpdo.c b/linux-user/arm/nwfpe/fpa11_cpdo.c
new file mode 100644
index 0000000..5f4a6a4
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpa11_cpdo.c
@@ -0,0 +1,112 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "fpopcode.h"
+
+unsigned int EmulateCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int Fd, nType, nDest, nRc = 1;
+
+ //printk("EmulateCPDO(0x%08x)\n",opcode);
+
+ /* Get the destination size. If not valid let Linux perform
+ an invalid instruction trap. */
+ nDest = getDestinationSize(opcode);
+ if (typeNone == nDest) return 0;
+
+ SetRoundingMode(opcode);
+
+ /* Compare the size of the operands in Fn and Fm.
+ Choose the largest size and perform operations in that size,
+ in order to make use of all the precision of the operands.
+ If Fm is a constant, we just grab a constant of a size
+ matching the size of the operand in Fn. */
+ if (MONADIC_INSTRUCTION(opcode))
+ nType = nDest;
+ else
+ nType = fpa11->fType[getFn(opcode)];
+
+ if (!CONSTANT_FM(opcode))
+ {
+ register unsigned int Fm = getFm(opcode);
+ if (nType < fpa11->fType[Fm])
+ {
+ nType = fpa11->fType[Fm];
+ }
+ }
+
+ switch (nType)
+ {
+ case typeSingle : nRc = SingleCPDO(opcode); break;
+ case typeDouble : nRc = DoubleCPDO(opcode); break;
+ case typeExtended : nRc = ExtendedCPDO(opcode); break;
+ default : nRc = 0;
+ }
+
+ /* If the operation succeeded, check to see if the result in the
+ destination register is the correct size. If not force it
+ to be. */
+ Fd = getFd(opcode);
+ nType = fpa11->fType[Fd];
+ if ((0 != nRc) && (nDest != nType))
+ {
+ switch (nDest)
+ {
+ case typeSingle:
+ {
+ if (typeDouble == nType)
+ fpa11->fpreg[Fd].fSingle =
+ float64_to_float32(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status);
+ else
+ fpa11->fpreg[Fd].fSingle =
+ floatx80_to_float32(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status);
+ }
+ break;
+
+ case typeDouble:
+ {
+ if (typeSingle == nType)
+ fpa11->fpreg[Fd].fDouble =
+ float32_to_float64(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status);
+ else
+ fpa11->fpreg[Fd].fDouble =
+ floatx80_to_float64(fpa11->fpreg[Fd].fExtended, &fpa11->fp_status);
+ }
+ break;
+
+ case typeExtended:
+ {
+ if (typeSingle == nType)
+ fpa11->fpreg[Fd].fExtended =
+ float32_to_floatx80(fpa11->fpreg[Fd].fSingle, &fpa11->fp_status);
+ else
+ fpa11->fpreg[Fd].fExtended =
+ float64_to_floatx80(fpa11->fpreg[Fd].fDouble, &fpa11->fp_status);
+ }
+ break;
+ }
+
+ fpa11->fType[Fd] = nDest;
+ }
+
+ return nRc;
+}
diff --git a/linux-user/arm/nwfpe/fpa11_cpdt.c b/linux-user/arm/nwfpe/fpa11_cpdt.c
new file mode 100644
index 0000000..1346fd6
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpa11_cpdt.c
@@ -0,0 +1,381 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.com, 1998-1999
+ (c) Philip Blundell, 1998
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+//#include <asm/uaccess.h>
+
+static inline
+void loadSingle(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ fpa11->fType[Fn] = typeSingle;
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(fpa11->fpreg[Fn].fSingle, addr);
+}
+
+static inline
+void loadDouble(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int *p;
+ p = (unsigned int*)&fpa11->fpreg[Fn].fDouble;
+ fpa11->fType[Fn] = typeDouble;
+#ifdef HOST_WORDS_BIGENDIAN
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(p[0], addr); /* sign & exponent */
+ get_user_u32(p[1], addr + 4);
+#else
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(p[0], addr + 4);
+ get_user_u32(p[1], addr); /* sign & exponent */
+#endif
+}
+
+static inline
+void loadExtended(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int *p;
+ p = (unsigned int*)&fpa11->fpreg[Fn].fExtended;
+ fpa11->fType[Fn] = typeExtended;
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(p[0], addr); /* sign & exponent */
+ get_user_u32(p[1], addr + 8); /* ls bits */
+ get_user_u32(p[2], addr + 4); /* ms bits */
+}
+
+static inline
+void loadMultiple(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ register unsigned int *p;
+ unsigned long x;
+
+ p = (unsigned int*)&(fpa11->fpreg[Fn]);
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(x, addr);
+ fpa11->fType[Fn] = (x >> 14) & 0x00000003;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ case typeDouble:
+ {
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(p[0], addr + 8); /* Single */
+ get_user_u32(p[1], addr + 4); /* double msw */
+ p[2] = 0; /* empty */
+ }
+ break;
+
+ case typeExtended:
+ {
+ /* FIXME - handle failure of get_user() */
+ get_user_u32(p[1], addr + 8);
+ get_user_u32(p[2], addr + 4); /* msw */
+ p[0] = (x & 0x80003fff);
+ }
+ break;
+ }
+}
+
+static inline
+void storeSingle(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ float32 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeDouble:
+ val = float64_to_float32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ val = floatx80_to_float32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fSingle;
+ }
+
+ /* FIXME - handle put_user() failures */
+ put_user_u32(p[0], addr);
+}
+
+static inline
+void storeDouble(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ float64 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ val = float32_to_float64(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ val = floatx80_to_float64(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fDouble;
+ }
+ /* FIXME - handle put_user() failures */
+#ifdef HOST_WORDS_BIGENDIAN
+ put_user_u32(p[0], addr); /* msw */
+ put_user_u32(p[1], addr + 4); /* lsw */
+#else
+ put_user_u32(p[1], addr); /* msw */
+ put_user_u32(p[0], addr + 4); /* lsw */
+#endif
+}
+
+static inline
+void storeExtended(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ floatx80 val;
+ register unsigned int *p = (unsigned int*)&val;
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ val = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ val = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ default: val = fpa11->fpreg[Fn].fExtended;
+ }
+
+ /* FIXME - handle put_user() failures */
+ put_user_u32(p[0], addr); /* sign & exp */
+ put_user_u32(p[1], addr + 8);
+ put_user_u32(p[2], addr + 4); /* msw */
+}
+
+static inline
+void storeMultiple(const unsigned int Fn, target_ulong addr)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ register unsigned int nType, *p;
+
+ p = (unsigned int*)&(fpa11->fpreg[Fn]);
+ nType = fpa11->fType[Fn];
+
+ switch (nType)
+ {
+ case typeSingle:
+ case typeDouble:
+ {
+ put_user_u32(p[0], addr + 8); /* single */
+ put_user_u32(p[1], addr + 4); /* double msw */
+ put_user_u32(nType << 14, addr);
+ }
+ break;
+
+ case typeExtended:
+ {
+ put_user_u32(p[2], addr + 4); /* msw */
+ put_user_u32(p[1], addr + 8);
+ put_user_u32((p[0] & 0x80003fff) | (nType << 14), addr);
+ }
+ break;
+ }
+}
+
+static unsigned int PerformLDF(const unsigned int opcode)
+{
+ target_ulong pBase, pAddress, pFinal;
+ unsigned int nRc = 1,
+ write_back = WRITE_BACK(opcode);
+
+ //printk("PerformLDF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+
+ pBase = readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 8;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode) * 4;
+ else
+ pFinal -= getOffset(opcode) * 4;
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case TRANSFER_SINGLE : loadSingle(getFd(opcode),pAddress); break;
+ case TRANSFER_DOUBLE : loadDouble(getFd(opcode),pAddress); break;
+ case TRANSFER_EXTENDED: loadExtended(getFd(opcode),pAddress); break;
+ default: nRc = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return nRc;
+}
+
+static unsigned int PerformSTF(const unsigned int opcode)
+{
+ target_ulong pBase, pAddress, pFinal;
+ unsigned int nRc = 1,
+ write_back = WRITE_BACK(opcode);
+
+ //printk("PerformSTF(0x%08x), Fd = 0x%08x\n",opcode,getFd(opcode));
+ SetRoundingMode(ROUND_TO_NEAREST);
+
+ pBase = readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 8;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode) * 4;
+ else
+ pFinal -= getOffset(opcode) * 4;
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ switch (opcode & MASK_TRANSFER_LENGTH)
+ {
+ case TRANSFER_SINGLE : storeSingle(getFd(opcode),pAddress); break;
+ case TRANSFER_DOUBLE : storeDouble(getFd(opcode),pAddress); break;
+ case TRANSFER_EXTENDED: storeExtended(getFd(opcode),pAddress); break;
+ default: nRc = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return nRc;
+}
+
+static unsigned int PerformLFM(const unsigned int opcode)
+{
+ unsigned int i, Fd,
+ write_back = WRITE_BACK(opcode);
+ target_ulong pBase, pAddress, pFinal;
+
+ pBase = readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 8;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode) * 4;
+ else
+ pFinal -= getOffset(opcode) * 4;
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ Fd = getFd(opcode);
+ for (i=getRegisterCount(opcode);i>0;i--)
+ {
+ loadMultiple(Fd,pAddress);
+ pAddress += 12; Fd++;
+ if (Fd == 8) Fd = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return 1;
+}
+
+static unsigned int PerformSFM(const unsigned int opcode)
+{
+ unsigned int i, Fd,
+ write_back = WRITE_BACK(opcode);
+ target_ulong pBase, pAddress, pFinal;
+
+ pBase = readRegister(getRn(opcode));
+ if (REG_PC == getRn(opcode))
+ {
+ pBase += 8;
+ write_back = 0;
+ }
+
+ pFinal = pBase;
+ if (BIT_UP_SET(opcode))
+ pFinal += getOffset(opcode) * 4;
+ else
+ pFinal -= getOffset(opcode) * 4;
+
+ if (PREINDEXED(opcode)) pAddress = pFinal; else pAddress = pBase;
+
+ Fd = getFd(opcode);
+ for (i=getRegisterCount(opcode);i>0;i--)
+ {
+ storeMultiple(Fd,pAddress);
+ pAddress += 12; Fd++;
+ if (Fd == 8) Fd = 0;
+ }
+
+ if (write_back) writeRegister(getRn(opcode),(unsigned int)pFinal);
+ return 1;
+}
+
+#if 1
+unsigned int EmulateCPDT(const unsigned int opcode)
+{
+ unsigned int nRc = 0;
+
+ //printk("EmulateCPDT(0x%08x)\n",opcode);
+
+ if (LDF_OP(opcode))
+ {
+ nRc = PerformLDF(opcode);
+ }
+ else if (LFM_OP(opcode))
+ {
+ nRc = PerformLFM(opcode);
+ }
+ else if (STF_OP(opcode))
+ {
+ nRc = PerformSTF(opcode);
+ }
+ else if (SFM_OP(opcode))
+ {
+ nRc = PerformSFM(opcode);
+ }
+ else
+ {
+ nRc = 0;
+ }
+
+ return nRc;
+}
+#endif
diff --git a/linux-user/arm/nwfpe/fpa11_cprt.c b/linux-user/arm/nwfpe/fpa11_cprt.c
new file mode 100644
index 0000000..be54e95
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpa11_cprt.c
@@ -0,0 +1,283 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+ (c) Philip Blundell, 1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpa11.inl"
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+unsigned int PerformFLT(const unsigned int opcode);
+unsigned int PerformFIX(const unsigned int opcode);
+
+static unsigned int
+PerformComparison(const unsigned int opcode);
+
+unsigned int EmulateCPRT(const unsigned int opcode)
+{
+ unsigned int nRc = 1;
+
+ //printk("EmulateCPRT(0x%08x)\n",opcode);
+
+ if (opcode & 0x800000)
+ {
+ /* This is some variant of a comparison (PerformComparison will
+ sort out which one). Since most of the other CPRT
+ instructions are oddball cases of some sort or other it makes
+ sense to pull this out into a fast path. */
+ return PerformComparison(opcode);
+ }
+
+ /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
+ switch ((opcode & 0x700000) >> 20)
+ {
+ case FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
+ case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
+
+ case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
+ case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
+
+#if 0 /* We currently have no use for the FPCR, so there's no point
+ in emulating it. */
+ case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
+ case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
+#endif
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+unsigned int PerformFLT(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+
+ unsigned int nRc = 1;
+ SetRoundingMode(opcode);
+
+ switch (opcode & MASK_ROUNDING_PRECISION)
+ {
+ case ROUND_SINGLE:
+ {
+ fpa11->fType[getFn(opcode)] = typeSingle;
+ fpa11->fpreg[getFn(opcode)].fSingle =
+ int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
+ }
+ break;
+
+ case ROUND_DOUBLE:
+ {
+ fpa11->fType[getFn(opcode)] = typeDouble;
+ fpa11->fpreg[getFn(opcode)].fDouble =
+ int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
+ }
+ break;
+
+ case ROUND_EXTENDED:
+ {
+ fpa11->fType[getFn(opcode)] = typeExtended;
+ fpa11->fpreg[getFn(opcode)].fExtended =
+ int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
+ }
+ break;
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+unsigned int PerformFIX(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int nRc = 1;
+ unsigned int Fn = getFm(opcode);
+
+ SetRoundingMode(opcode);
+
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ {
+ writeRegister(getRd(opcode),
+ float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
+ }
+ break;
+
+ case typeDouble:
+ {
+ //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
+ writeRegister(getRd(opcode),
+ float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
+ }
+ break;
+
+ case typeExtended:
+ {
+ writeRegister(getRd(opcode),
+ floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
+ }
+ break;
+
+ default: nRc = 0;
+ }
+
+ return nRc;
+}
+
+
+static __inline unsigned int
+PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int flags = 0;
+
+ /* test for less than condition */
+ if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
+ {
+ flags |= CC_NEGATIVE;
+ }
+
+ /* test for equal condition */
+ if (floatx80_eq(Fn,Fm, &fpa11->fp_status))
+ {
+ flags |= CC_ZERO;
+ }
+
+ /* test for greater than or equal condition */
+ if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
+ {
+ flags |= CC_CARRY;
+ }
+
+ writeConditionCodes(flags);
+ return 1;
+}
+
+/* This instruction sets the flags N, Z, C, V in the FPSR. */
+
+static unsigned int PerformComparison(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ unsigned int Fn, Fm;
+ floatx80 rFn, rFm;
+ int e_flag = opcode & 0x400000; /* 1 if CxFE */
+ int n_flag = opcode & 0x200000; /* 1 if CNxx */
+ unsigned int flags = 0;
+
+ //printk("PerformComparison(0x%08x)\n",opcode);
+
+ Fn = getFn(opcode);
+ Fm = getFm(opcode);
+
+ /* Check for unordered condition and convert all operands to 80-bit
+ format.
+ ?? Might be some mileage in avoiding this conversion if possible.
+ Eg, if both operands are 32-bit, detect this and do a 32-bit
+ comparison (cheaper than an 80-bit one). */
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ //printk("single.\n");
+ if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
+ goto unordered;
+ rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ //printk("double.\n");
+ if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
+ goto unordered;
+ rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ //printk("extended.\n");
+ if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
+ goto unordered;
+ rFn = fpa11->fpreg[Fn].fExtended;
+ break;
+
+ default: return 0;
+ }
+
+ if (CONSTANT_FM(opcode))
+ {
+ //printk("Fm is a constant: #%d.\n",Fm);
+ rFm = getExtendedConstant(Fm);
+ if (floatx80_is_any_nan(rFm))
+ goto unordered;
+ }
+ else
+ {
+ //printk("Fm = r%d which contains a ",Fm);
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ //printk("single.\n");
+ if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
+ goto unordered;
+ rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
+ break;
+
+ case typeDouble:
+ //printk("double.\n");
+ if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
+ goto unordered;
+ rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
+ break;
+
+ case typeExtended:
+ //printk("extended.\n");
+ if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
+ goto unordered;
+ rFm = fpa11->fpreg[Fm].fExtended;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (n_flag)
+ {
+ rFm.high ^= 0x8000;
+ }
+
+ return PerformComparisonOperation(rFn,rFm);
+
+ unordered:
+ /* ?? The FPA data sheet is pretty vague about this, in particular
+ about whether the non-E comparisons can ever raise exceptions.
+ This implementation is based on a combination of what it says in
+ the data sheet, observation of how the Acorn emulator actually
+ behaves (and how programs expect it to) and guesswork. */
+ flags |= CC_OVERFLOW;
+ flags &= ~(CC_ZERO | CC_NEGATIVE);
+
+ if (BIT_AC & readFPSR()) flags |= CC_CARRY;
+
+ if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
+
+ writeConditionCodes(flags);
+ return 1;
+}
diff --git a/linux-user/arm/nwfpe/fpopcode.c b/linux-user/arm/nwfpe/fpopcode.c
new file mode 100644
index 0000000..240061d
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpopcode.c
@@ -0,0 +1,112 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+#include "fpsr.h"
+//#include "fpmodule.h"
+//#include "fpmodule.inl"
+
+const floatx80 floatx80Constant[] = {
+ { 0x0000000000000000ULL, 0x0000}, /* extended 0.0 */
+ { 0x8000000000000000ULL, 0x3fff}, /* extended 1.0 */
+ { 0x8000000000000000ULL, 0x4000}, /* extended 2.0 */
+ { 0xc000000000000000ULL, 0x4000}, /* extended 3.0 */
+ { 0x8000000000000000ULL, 0x4001}, /* extended 4.0 */
+ { 0xa000000000000000ULL, 0x4001}, /* extended 5.0 */
+ { 0x8000000000000000ULL, 0x3ffe}, /* extended 0.5 */
+ { 0xa000000000000000ULL, 0x4002} /* extended 10.0 */
+};
+
+const float64 float64Constant[] = {
+ 0x0000000000000000ULL, /* double 0.0 */
+ 0x3ff0000000000000ULL, /* double 1.0 */
+ 0x4000000000000000ULL, /* double 2.0 */
+ 0x4008000000000000ULL, /* double 3.0 */
+ 0x4010000000000000ULL, /* double 4.0 */
+ 0x4014000000000000ULL, /* double 5.0 */
+ 0x3fe0000000000000ULL, /* double 0.5 */
+ 0x4024000000000000ULL /* double 10.0 */
+};
+
+const float32 float32Constant[] = {
+ 0x00000000, /* single 0.0 */
+ 0x3f800000, /* single 1.0 */
+ 0x40000000, /* single 2.0 */
+ 0x40400000, /* single 3.0 */
+ 0x40800000, /* single 4.0 */
+ 0x40a00000, /* single 5.0 */
+ 0x3f000000, /* single 0.5 */
+ 0x41200000 /* single 10.0 */
+};
+
+unsigned int getRegisterCount(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_REGISTER_COUNT)
+ {
+ case 0x00000000: nRc = 4; break;
+ case 0x00008000: nRc = 1; break;
+ case 0x00400000: nRc = 2; break;
+ case 0x00408000: nRc = 3; break;
+ default: nRc = 0;
+ }
+
+ return(nRc);
+}
+
+unsigned int getDestinationSize(const unsigned int opcode)
+{
+ unsigned int nRc;
+
+ switch (opcode & MASK_DESTINATION_SIZE)
+ {
+ case 0x00000000: nRc = typeSingle; break;
+ case 0x00000080: nRc = typeDouble; break;
+ case 0x00080000: nRc = typeExtended; break;
+ default: nRc = typeNone;
+ }
+
+ return(nRc);
+}
+
+/* condition code lookup table
+ index into the table is test code: EQ, NE, ... LT, GT, AL, NV
+ bit position in short is condition code: NZCV */
+static const unsigned short aCC[16] = {
+ 0xF0F0, // EQ == Z set
+ 0x0F0F, // NE
+ 0xCCCC, // CS == C set
+ 0x3333, // CC
+ 0xFF00, // MI == N set
+ 0x00FF, // PL
+ 0xAAAA, // VS == V set
+ 0x5555, // VC
+ 0x0C0C, // HI == C set && Z clear
+ 0xF3F3, // LS == C clear || Z set
+ 0xAA55, // GE == (N==V)
+ 0x55AA, // LT == (N!=V)
+ 0x0A05, // GT == (!Z && (N==V))
+ 0xF5FA, // LE == (Z || (N!=V))
+ 0xFFFF, // AL always
+ 0 // NV
+};
diff --git a/linux-user/arm/nwfpe/fpopcode.h b/linux-user/arm/nwfpe/fpopcode.h
new file mode 100644
index 0000000..e7d1009
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpopcode.h
@@ -0,0 +1,390 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#ifndef __FPOPCODE_H__
+#define __FPOPCODE_H__
+
+/*
+ARM Floating Point Instruction Classes
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+|c o n d|1 1 0 P|U|u|W|L| Rn |v| Fd |0|0|0|1| o f f s e t | CPDT
+|c o n d|1 1 0 P|U|w|W|L| Rn |x| Fd |0|0|0|1| o f f s e t | CPDT
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+|c o n d|1 1 1 0|a|b|c|d|e| Fn |j| Fd |0|0|0|1|f|g|h|0|i| Fm | CPDO
+|c o n d|1 1 1 0|a|b|c|L|e| Fn | Rd |0|0|0|1|f|g|h|1|i| Fm | CPRT
+|c o n d|1 1 1 0|a|b|c|1|e| Fn |1|1|1|1|0|0|0|1|f|g|h|1|i| Fm | comparisons
+| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+
+CPDT data transfer instructions
+ LDF, STF, LFM, SFM
+
+CPDO dyadic arithmetic instructions
+ ADF, MUF, SUF, RSF, DVF, RDF,
+ POW, RPW, RMF, FML, FDV, FRD, POL
+
+CPDO monadic arithmetic instructions
+ MVF, MNF, ABS, RND, SQT, LOG, LGN, EXP,
+ SIN, COS, TAN, ASN, ACS, ATN, URD, NRM
+
+CPRT joint arithmetic/data transfer instructions
+ FIX (arithmetic followed by load/store)
+ FLT (load/store followed by arithmetic)
+ CMF, CNF CMFE, CNFE (comparisons)
+ WFS, RFS (write/read floating point status register)
+ WFC, RFC (write/read floating point control register)
+
+cond condition codes
+P pre/post index bit: 0 = postindex, 1 = preindex
+U up/down bit: 0 = stack grows down, 1 = stack grows up
+W write back bit: 1 = update base register (Rn)
+L load/store bit: 0 = store, 1 = load
+Rn base register
+Rd destination/source register
+Fd floating point destination register
+Fn floating point source register
+Fm floating point source register or floating point constant
+
+uv transfer length (TABLE 1)
+wx register count (TABLE 2)
+abcd arithmetic opcode (TABLES 3 & 4)
+ef destination size (rounding precision) (TABLE 5)
+gh rounding mode (TABLE 6)
+j dyadic/monadic bit: 0 = dyadic, 1 = monadic
+i constant bit: 1 = constant (TABLE 6)
+*/
+
+/*
+TABLE 1
++-------------------------+---+---+---------+---------+
+| Precision | u | v | FPSR.EP | length |
++-------------------------+---+---+---------+---------+
+| Single | 0 ü 0 | x | 1 words |
+| Double | 1 ü 1 | x | 2 words |
+| Extended | 1 ü 1 | x | 3 words |
+| Packed decimal | 1 ü 1 | 0 | 3 words |
+| Expanded packed decimal | 1 ü 1 | 1 | 4 words |
++-------------------------+---+---+---------+---------+
+Note: x = don't care
+*/
+
+/*
+TABLE 2
++---+---+---------------------------------+
+| w | x | Number of registers to transfer |
++---+---+---------------------------------+
+| 0 ü 1 | 1 |
+| 1 ü 0 | 2 |
+| 1 ü 1 | 3 |
+| 0 ü 0 | 4 |
++---+---+---------------------------------+
+*/
+
+/*
+TABLE 3: Dyadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description | Operation |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | ADF | Add | Fd := Fn + Fm |
+| 0 | 0 | 0 | 1 | MUF | Multiply | Fd := Fn * Fm |
+| 0 | 0 | 1 | 0 | SUF | Subtract | Fd := Fn - Fm |
+| 0 | 0 | 1 | 1 | RSF | Reverse subtract | Fd := Fm - Fn |
+| 0 | 1 | 0 | 0 | DVF | Divide | Fd := Fn / Fm |
+| 0 | 1 | 0 | 1 | RDF | Reverse divide | Fd := Fm / Fn |
+| 0 | 1 | 1 | 0 | POW | Power | Fd := Fn ^ Fm |
+| 0 | 1 | 1 | 1 | RPW | Reverse power | Fd := Fm ^ Fn |
+| 1 | 0 | 0 | 0 | RMF | Remainder | Fd := IEEE rem(Fn/Fm) |
+| 1 | 0 | 0 | 1 | FML | Fast Multiply | Fd := Fn * Fm |
+| 1 | 0 | 1 | 0 | FDV | Fast Divide | Fd := Fn / Fm |
+| 1 | 0 | 1 | 1 | FRD | Fast reverse divide | Fd := Fm / Fn |
+| 1 | 1 | 0 | 0 | POL | Polar angle (ArcTan2) | Fd := arctan2(Fn,Fm) |
+| 1 | 1 | 0 | 1 | | undefined instruction | trap |
+| 1 | 1 | 1 | 0 | | undefined instruction | trap |
+| 1 | 1 | 1 | 1 | | undefined instruction | trap |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: POW, RPW, POL are deprecated, and are available for backwards
+ compatibility only.
+*/
+
+/*
+TABLE 4: Monadic Floating Point Opcodes
++---+---+---+---+----------+-----------------------+-----------------------+
+| a | b | c | d | Mnemonic | Description | Operation |
++---+---+---+---+----------+-----------------------+-----------------------+
+| 0 | 0 | 0 | 0 | MVF | Move | Fd := Fm |
+| 0 | 0 | 0 | 1 | MNF | Move negated | Fd := - Fm |
+| 0 | 0 | 1 | 0 | ABS | Absolute value | Fd := abs(Fm) |
+| 0 | 0 | 1 | 1 | RND | Round to integer | Fd := int(Fm) |
+| 0 | 1 | 0 | 0 | SQT | Square root | Fd := sqrt(Fm) |
+| 0 | 1 | 0 | 1 | LOG | Log base 10 | Fd := log10(Fm) |
+| 0 | 1 | 1 | 0 | LGN | Log base e | Fd := ln(Fm) |
+| 0 | 1 | 1 | 1 | EXP | Exponent | Fd := e ^ Fm |
+| 1 | 0 | 0 | 0 | SIN | Sine | Fd := sin(Fm) |
+| 1 | 0 | 0 | 1 | COS | Cosine | Fd := cos(Fm) |
+| 1 | 0 | 1 | 0 | TAN | Tangent | Fd := tan(Fm) |
+| 1 | 0 | 1 | 1 | ASN | Arc Sine | Fd := arcsin(Fm) |
+| 1 | 1 | 0 | 0 | ACS | Arc Cosine | Fd := arccos(Fm) |
+| 1 | 1 | 0 | 1 | ATN | Arc Tangent | Fd := arctan(Fm) |
+| 1 | 1 | 1 | 0 | URD | Unnormalized round | Fd := int(Fm) |
+| 1 | 1 | 1 | 1 | NRM | Normalize | Fd := norm(Fm) |
++---+---+---+---+----------+-----------------------+-----------------------+
+Note: LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN are deprecated, and are
+ available for backwards compatibility only.
+*/
+
+/*
+TABLE 5
++-------------------------+---+---+
+| Rounding Precision | e | f |
++-------------------------+---+---+
+| IEEE Single precision | 0 ü 0 |
+| IEEE Double precision | 0 ü 1 |
+| IEEE Extended precision | 1 ü 0 |
+| undefined (trap) | 1 ü 1 |
++-------------------------+---+---+
+*/
+
+/*
+TABLE 5
++---------------------------------+---+---+
+| Rounding Mode | g | h |
++---------------------------------+---+---+
+| Round to nearest (default) | 0 ü 0 |
+| Round toward plus infinity | 0 ü 1 |
+| Round toward negative infinity | 1 ü 0 |
+| Round toward zero | 1 ü 1 |
++---------------------------------+---+---+
+*/
+
+/*
+===
+=== Definitions for load and store instructions
+===
+*/
+
+/* bit masks */
+#define BIT_PREINDEX 0x01000000
+#define BIT_UP 0x00800000
+#define BIT_WRITE_BACK 0x00200000
+#define BIT_LOAD 0x00100000
+
+/* masks for load/store */
+#define MASK_CPDT 0x0c000000 /* data processing opcode */
+#define MASK_OFFSET 0x000000ff
+#define MASK_TRANSFER_LENGTH 0x00408000
+#define MASK_REGISTER_COUNT MASK_TRANSFER_LENGTH
+#define MASK_COPROCESSOR 0x00000f00
+
+/* Tests for transfer length */
+#define TRANSFER_SINGLE 0x00000000
+#define TRANSFER_DOUBLE 0x00008000
+#define TRANSFER_EXTENDED 0x00400000
+#define TRANSFER_PACKED MASK_TRANSFER_LENGTH
+
+/* Get the coprocessor number from the opcode. */
+#define getCoprocessorNumber(opcode) ((opcode & MASK_COPROCESSOR) >> 8)
+
+/* Get the offset from the opcode. */
+#define getOffset(opcode) (opcode & MASK_OFFSET)
+
+/* Tests for specific data transfer load/store opcodes. */
+#define TEST_OPCODE(opcode,mask) (((opcode) & (mask)) == (mask))
+
+#define LOAD_OP(opcode) TEST_OPCODE((opcode),MASK_CPDT | BIT_LOAD)
+#define STORE_OP(opcode) ((opcode & (MASK_CPDT | BIT_LOAD)) == MASK_CPDT)
+
+#define LDF_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define LFM_OP(opcode) (LOAD_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+#define STF_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 1))
+#define SFM_OP(opcode) (STORE_OP(opcode) && (getCoprocessorNumber(opcode) == 2))
+
+#define PREINDEXED(opcode) ((opcode & BIT_PREINDEX) != 0)
+#define POSTINDEXED(opcode) ((opcode & BIT_PREINDEX) == 0)
+#define BIT_UP_SET(opcode) ((opcode & BIT_UP) != 0)
+#define BIT_UP_CLEAR(opcode) ((opcode & BIT_DOWN) == 0)
+#define WRITE_BACK(opcode) ((opcode & BIT_WRITE_BACK) != 0)
+#define LOAD(opcode) ((opcode & BIT_LOAD) != 0)
+#define STORE(opcode) ((opcode & BIT_LOAD) == 0)
+
+/*
+===
+=== Definitions for arithmetic instructions
+===
+*/
+/* bit masks */
+#define BIT_MONADIC 0x00008000
+#define BIT_CONSTANT 0x00000008
+
+#define CONSTANT_FM(opcode) ((opcode & BIT_CONSTANT) != 0)
+#define MONADIC_INSTRUCTION(opcode) ((opcode & BIT_MONADIC) != 0)
+
+/* instruction identification masks */
+#define MASK_CPDO 0x0e000000 /* arithmetic opcode */
+#define MASK_ARITHMETIC_OPCODE 0x00f08000
+#define MASK_DESTINATION_SIZE 0x00080080
+
+/* dyadic arithmetic opcodes. */
+#define ADF_CODE 0x00000000
+#define MUF_CODE 0x00100000
+#define SUF_CODE 0x00200000
+#define RSF_CODE 0x00300000
+#define DVF_CODE 0x00400000
+#define RDF_CODE 0x00500000
+#define POW_CODE 0x00600000
+#define RPW_CODE 0x00700000
+#define RMF_CODE 0x00800000
+#define FML_CODE 0x00900000
+#define FDV_CODE 0x00a00000
+#define FRD_CODE 0x00b00000
+#define POL_CODE 0x00c00000
+/* 0x00d00000 is an invalid dyadic arithmetic opcode */
+/* 0x00e00000 is an invalid dyadic arithmetic opcode */
+/* 0x00f00000 is an invalid dyadic arithmetic opcode */
+
+/* monadic arithmetic opcodes. */
+#define MVF_CODE 0x00008000
+#define MNF_CODE 0x00108000
+#define ABS_CODE 0x00208000
+#define RND_CODE 0x00308000
+#define SQT_CODE 0x00408000
+#define LOG_CODE 0x00508000
+#define LGN_CODE 0x00608000
+#define EXP_CODE 0x00708000
+#define SIN_CODE 0x00808000
+#define COS_CODE 0x00908000
+#define TAN_CODE 0x00a08000
+#define ASN_CODE 0x00b08000
+#define ACS_CODE 0x00c08000
+#define ATN_CODE 0x00d08000
+#define URD_CODE 0x00e08000
+#define NRM_CODE 0x00f08000
+
+/*
+===
+=== Definitions for register transfer and comparison instructions
+===
+*/
+
+#define MASK_CPRT 0x0e000010 /* register transfer opcode */
+#define MASK_CPRT_CODE 0x00f00000
+#define FLT_CODE 0x00000000
+#define FIX_CODE 0x00100000
+#define WFS_CODE 0x00200000
+#define RFS_CODE 0x00300000
+#define WFC_CODE 0x00400000
+#define RFC_CODE 0x00500000
+#define CMF_CODE 0x00900000
+#define CNF_CODE 0x00b00000
+#define CMFE_CODE 0x00d00000
+#define CNFE_CODE 0x00f00000
+
+/*
+===
+=== Common definitions
+===
+*/
+
+/* register masks */
+#define MASK_Rd 0x0000f000
+#define MASK_Rn 0x000f0000
+#define MASK_Fd 0x00007000
+#define MASK_Fm 0x00000007
+#define MASK_Fn 0x00070000
+
+/* condition code masks */
+#define CC_MASK 0xf0000000
+#define CC_NEGATIVE 0x80000000
+#define CC_ZERO 0x40000000
+#define CC_CARRY 0x20000000
+#define CC_OVERFLOW 0x10000000
+#define CC_EQ 0x00000000
+#define CC_NE 0x10000000
+#define CC_CS 0x20000000
+#define CC_HS CC_CS
+#define CC_CC 0x30000000
+#define CC_LO CC_CC
+#define CC_MI 0x40000000
+#define CC_PL 0x50000000
+#define CC_VS 0x60000000
+#define CC_VC 0x70000000
+#define CC_HI 0x80000000
+#define CC_LS 0x90000000
+#define CC_GE 0xa0000000
+#define CC_LT 0xb0000000
+#define CC_GT 0xc0000000
+#define CC_LE 0xd0000000
+#define CC_AL 0xe0000000
+#define CC_NV 0xf0000000
+
+/* rounding masks/values */
+#define MASK_ROUNDING_MODE 0x00000060
+#define ROUND_TO_NEAREST 0x00000000
+#define ROUND_TO_PLUS_INFINITY 0x00000020
+#define ROUND_TO_MINUS_INFINITY 0x00000040
+#define ROUND_TO_ZERO 0x00000060
+
+#define MASK_ROUNDING_PRECISION 0x00080080
+#define ROUND_SINGLE 0x00000000
+#define ROUND_DOUBLE 0x00000080
+#define ROUND_EXTENDED 0x00080000
+
+/* Get the condition code from the opcode. */
+#define getCondition(opcode) (opcode >> 28)
+
+/* Get the source register from the opcode. */
+#define getRn(opcode) ((opcode & MASK_Rn) >> 16)
+
+/* Get the destination floating point register from the opcode. */
+#define getFd(opcode) ((opcode & MASK_Fd) >> 12)
+
+/* Get the first source floating point register from the opcode. */
+#define getFn(opcode) ((opcode & MASK_Fn) >> 16)
+
+/* Get the second source floating point register from the opcode. */
+#define getFm(opcode) (opcode & MASK_Fm)
+
+/* Get the destination register from the opcode. */
+#define getRd(opcode) ((opcode & MASK_Rd) >> 12)
+
+/* Get the rounding mode from the opcode. */
+#define getRoundingMode(opcode) ((opcode & MASK_ROUNDING_MODE) >> 5)
+
+extern const floatx80 floatx80Constant[];
+extern const float64 float64Constant[];
+extern const float32 float32Constant[];
+
+static inline floatx80 getExtendedConstant(const unsigned int nIndex)
+{
+ return floatx80Constant[nIndex];
+}
+
+static inline float64 getDoubleConstant(const unsigned int nIndex)
+{
+ return float64Constant[nIndex];
+}
+
+static inline float32 getSingleConstant(const unsigned int nIndex)
+{
+ return float32Constant[nIndex];
+}
+
+unsigned int getRegisterCount(const unsigned int opcode);
+unsigned int getDestinationSize(const unsigned int opcode);
+
+#endif
diff --git a/linux-user/arm/nwfpe/fpsr.h b/linux-user/arm/nwfpe/fpsr.h
new file mode 100644
index 0000000..859dcd5
--- /dev/null
+++ b/linux-user/arm/nwfpe/fpsr.h
@@ -0,0 +1,107 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.com, 1998-1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#ifndef __FPSR_H__
+#define __FPSR_H__
+
+/*
+The FPSR is a 32 bit register consisting of 4 parts, each exactly
+one byte.
+
+ SYSTEM ID
+ EXCEPTION TRAP ENABLE BYTE
+ SYSTEM CONTROL BYTE
+ CUMULATIVE EXCEPTION FLAGS BYTE
+
+The FPCR is a 32 bit register consisting of bit flags.
+*/
+
+/* SYSTEM ID
+------------
+Note: the system id byte is read only */
+
+typedef unsigned int FPSR; /* type for floating point status register */
+typedef unsigned int FPCR; /* type for floating point control register */
+
+#define MASK_SYSID 0xff000000
+#define BIT_HARDWARE 0x80000000
+#define FP_EMULATOR 0x01000000 /* System ID for emulator */
+#define FP_ACCELERATOR 0x81000000 /* System ID for FPA11 */
+
+/* EXCEPTION TRAP ENABLE BYTE
+----------------------------- */
+
+#define MASK_TRAP_ENABLE 0x00ff0000
+#define MASK_TRAP_ENABLE_STRICT 0x001f0000
+#define BIT_IXE 0x00100000 /* inexact exception enable */
+#define BIT_UFE 0x00080000 /* underflow exception enable */
+#define BIT_OFE 0x00040000 /* overflow exception enable */
+#define BIT_DZE 0x00020000 /* divide by zero exception enable */
+#define BIT_IOE 0x00010000 /* invalid operation exception enable */
+
+/* SYSTEM CONTROL BYTE
+---------------------- */
+
+#define MASK_SYSTEM_CONTROL 0x0000ff00
+#define MASK_TRAP_STRICT 0x00001f00
+
+#define BIT_AC 0x00001000 /* use alternative C-flag definition
+ for compares */
+#define BIT_EP 0x00000800 /* use expanded packed decimal format */
+#define BIT_SO 0x00000400 /* select synchronous operation of FPA */
+#define BIT_NE 0x00000200 /* NaN exception bit */
+#define BIT_ND 0x00000100 /* no denormalized numbers bit */
+
+/* CUMULATIVE EXCEPTION FLAGS BYTE
+---------------------------------- */
+
+#define MASK_EXCEPTION_FLAGS 0x000000ff
+#define MASK_EXCEPTION_FLAGS_STRICT 0x0000001f
+
+#define BIT_IXC 0x00000010 /* inexact exception flag */
+#define BIT_UFC 0x00000008 /* underflow exception flag */
+#define BIT_OFC 0x00000004 /* overfloat exception flag */
+#define BIT_DZC 0x00000002 /* divide by zero exception flag */
+#define BIT_IOC 0x00000001 /* invalid operation exception flag */
+
+/* Floating Point Control Register
+----------------------------------*/
+
+#define BIT_RU 0x80000000 /* rounded up bit */
+#define BIT_IE 0x10000000 /* inexact bit */
+#define BIT_MO 0x08000000 /* mantissa overflow bit */
+#define BIT_EO 0x04000000 /* exponent overflow bit */
+#define BIT_SB 0x00000800 /* store bounce */
+#define BIT_AB 0x00000400 /* arithmetic bounce */
+#define BIT_RE 0x00000200 /* rounding exception */
+#define BIT_DA 0x00000100 /* disable FPA */
+
+#define MASK_OP 0x00f08010 /* AU operation code */
+#define MASK_PR 0x00080080 /* AU precision */
+#define MASK_S1 0x00070000 /* AU source register 1 */
+#define MASK_S2 0x00000007 /* AU source register 2 */
+#define MASK_DS 0x00007000 /* AU destination register */
+#define MASK_RM 0x00000060 /* AU rounding mode */
+#define MASK_ALU 0x9cfff2ff /* only ALU can write these bits */
+#define MASK_RESET 0x00000d00 /* bits set on reset, all others cleared */
+#define MASK_WFC MASK_RESET
+#define MASK_RFC ~MASK_RESET
+
+#endif
diff --git a/linux-user/arm/nwfpe/single_cpdo.c b/linux-user/arm/nwfpe/single_cpdo.c
new file mode 100644
index 0000000..26168e2
--- /dev/null
+++ b/linux-user/arm/nwfpe/single_cpdo.c
@@ -0,0 +1,252 @@
+/*
+ NetWinder Floating Point Emulator
+ (c) Rebel.COM, 1998,1999
+
+ Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>.
+*/
+
+#include "fpa11.h"
+#include "softfloat.h"
+#include "fpopcode.h"
+
+float32 float32_exp(float32 Fm);
+float32 float32_ln(float32 Fm);
+float32 float32_sin(float32 rFm);
+float32 float32_cos(float32 rFm);
+float32 float32_arcsin(float32 rFm);
+float32 float32_arctan(float32 rFm);
+float32 float32_log(float32 rFm);
+float32 float32_tan(float32 rFm);
+float32 float32_arccos(float32 rFm);
+float32 float32_pow(float32 rFn,float32 rFm);
+float32 float32_pol(float32 rFn,float32 rFm);
+
+unsigned int SingleCPDO(const unsigned int opcode)
+{
+ FPA11 *fpa11 = GET_FPA11();
+ float32 rFm, rFn = float32_zero;
+ unsigned int Fd, Fm, Fn, nRc = 1;
+
+ Fm = getFm(opcode);
+ if (CONSTANT_FM(opcode))
+ {
+ rFm = getSingleConstant(Fm);
+ }
+ else
+ {
+ switch (fpa11->fType[Fm])
+ {
+ case typeSingle:
+ rFm = fpa11->fpreg[Fm].fSingle;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ if (!MONADIC_INSTRUCTION(opcode))
+ {
+ Fn = getFn(opcode);
+ switch (fpa11->fType[Fn])
+ {
+ case typeSingle:
+ rFn = fpa11->fpreg[Fn].fSingle;
+ break;
+
+ default: return 0;
+ }
+ }
+
+ Fd = getFd(opcode);
+ switch (opcode & MASK_ARITHMETIC_OPCODE)
+ {
+ /* dyadic opcodes */
+ case ADF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_add(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case MUF_CODE:
+ case FML_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_mul(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case SUF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sub(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RSF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sub(rFm,rFn, &fpa11->fp_status);
+ break;
+
+ case DVF_CODE:
+ case FDV_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_div(rFn,rFm, &fpa11->fp_status);
+ break;
+
+ case RDF_CODE:
+ case FRD_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_div(rFm,rFn, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POW_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_pow(rFn,rFm);
+ break;
+
+ case RPW_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_pow(rFm,rFn);
+ break;
+#endif
+
+ case RMF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_rem(rFn,rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case POL_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_pol(rFn,rFm);
+ break;
+#endif
+
+ /* monadic opcodes */
+ case MVF_CODE:
+ fpa11->fpreg[Fd].fSingle = rFm;
+ break;
+
+ case MNF_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_chs(rFm);
+ break;
+
+ case ABS_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_abs(rFm);
+ break;
+
+ case RND_CODE:
+ case URD_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_round_to_int(rFm, &fpa11->fp_status);
+ break;
+
+ case SQT_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sqrt(rFm, &fpa11->fp_status);
+ break;
+
+#if 0
+ case LOG_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_log(rFm);
+ break;
+
+ case LGN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_ln(rFm);
+ break;
+
+ case EXP_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_exp(rFm);
+ break;
+
+ case SIN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_sin(rFm);
+ break;
+
+ case COS_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_cos(rFm);
+ break;
+
+ case TAN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_tan(rFm);
+ break;
+
+ case ASN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_arcsin(rFm);
+ break;
+
+ case ACS_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_arccos(rFm);
+ break;
+
+ case ATN_CODE:
+ fpa11->fpreg[Fd].fSingle = float32_arctan(rFm);
+ break;
+#endif
+
+ case NRM_CODE:
+ break;
+
+ default:
+ {
+ nRc = 0;
+ }
+ }
+
+ if (0 != nRc) fpa11->fType[Fd] = typeSingle;
+ return nRc;
+}
+
+#if 0
+float32 float32_exp(float32 Fm)
+{
+//series
+}
+
+float32 float32_ln(float32 Fm)
+{
+//series
+}
+
+float32 float32_sin(float32 rFm)
+{
+//series
+}
+
+float32 float32_cos(float32 rFm)
+{
+//series
+}
+
+float32 float32_arcsin(float32 rFm)
+{
+//series
+}
+
+float32 float32_arctan(float32 rFm)
+{
+ //series
+}
+
+float32 float32_arccos(float32 rFm)
+{
+ //return float32_sub(halfPi,float32_arcsin(rFm));
+}
+
+float32 float32_log(float32 rFm)
+{
+ return float32_div(float32_ln(rFm),getSingleConstant(7));
+}
+
+float32 float32_tan(float32 rFm)
+{
+ return float32_div(float32_sin(rFm),float32_cos(rFm));
+}
+
+float32 float32_pow(float32 rFn,float32 rFm)
+{
+ return float32_exp(float32_mul(rFm,float32_ln(rFn)));
+}
+
+float32 float32_pol(float32 rFn,float32 rFm)
+{
+ return float32_arctan(float32_div(rFn,rFm));
+}
+#endif
diff --git a/linux-user/arm/syscall.h b/linux-user/arm/syscall.h
new file mode 100644
index 0000000..003d424
--- /dev/null
+++ b/linux-user/arm/syscall.h
@@ -0,0 +1,42 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+ abi_long uregs[18];
+};
+
+#define ARM_cpsr uregs[16]
+#define ARM_pc uregs[15]
+#define ARM_lr uregs[14]
+#define ARM_sp uregs[13]
+#define ARM_ip uregs[12]
+#define ARM_fp uregs[11]
+#define ARM_r10 uregs[10]
+#define ARM_r9 uregs[9]
+#define ARM_r8 uregs[8]
+#define ARM_r7 uregs[7]
+#define ARM_r6 uregs[6]
+#define ARM_r5 uregs[5]
+#define ARM_r4 uregs[4]
+#define ARM_r3 uregs[3]
+#define ARM_r2 uregs[2]
+#define ARM_r1 uregs[1]
+#define ARM_r0 uregs[0]
+#define ARM_ORIG_r0 uregs[17]
+
+#define ARM_SYSCALL_BASE 0x900000
+#define ARM_THUMB_SYSCALL 0
+
+#define ARM_NR_BASE 0xf0000
+#define ARM_NR_cacheflush (ARM_NR_BASE + 2)
+#define ARM_NR_set_tls (ARM_NR_BASE + 5)
+
+#define ARM_NR_semihosting 0x123456
+#define ARM_NR_thumb_semihosting 0xAB
+
+#if defined(TARGET_WORDS_BIGENDIAN)
+#define UNAME_MACHINE "armv5teb"
+#else
+#define UNAME_MACHINE "armv5tel"
+#endif
diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h
new file mode 100644
index 0000000..79a216a
--- /dev/null
+++ b/linux-user/arm/syscall_nr.h
@@ -0,0 +1,367 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_restart_syscall ( 0)
+#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_waitpid ( 7) /* removed */
+#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_time ( 13)
+#define TARGET_NR_mknod ( 14)
+#define TARGET_NR_chmod ( 15)
+#define TARGET_NR_lchown ( 16)
+#define TARGET_NR_break ( 17) /* removed */
+ /* 18 was sys_stat */
+#define TARGET_NR_lseek ( 19)
+#define TARGET_NR_getpid ( 20)
+#define TARGET_NR_mount ( 21)
+#define TARGET_NR_umount ( 22)
+#define TARGET_NR_setuid ( 23)
+#define TARGET_NR_getuid ( 24)
+#define TARGET_NR_stime ( 25)
+#define TARGET_NR_ptrace ( 26)
+#define TARGET_NR_alarm ( 27)
+
+#define TARGET_NR_pause ( 29)
+#define TARGET_NR_utime ( 30)
+#define TARGET_NR_stty ( 31) /* removed */
+#define TARGET_NR_gtty ( 32) /* removed */
+#define TARGET_NR_access ( 33)
+#define TARGET_NR_nice ( 34)
+#define TARGET_NR_ftime ( 35) /* removed */
+#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_prof ( 44) /* removed */
+#define TARGET_NR_brk ( 45)
+#define TARGET_NR_setgid ( 46)
+#define TARGET_NR_getgid ( 47)
+#define TARGET_NR_signal ( 48) /* removed */
+#define TARGET_NR_geteuid ( 49)
+#define TARGET_NR_getegid ( 50)
+#define TARGET_NR_acct ( 51)
+#define TARGET_NR_umount2 ( 52)
+#define TARGET_NR_lock ( 53) /* removed */
+#define TARGET_NR_ioctl ( 54)
+#define TARGET_NR_fcntl ( 55)
+#define TARGET_NR_mpx ( 56) /* removed */
+#define TARGET_NR_setpgid ( 57)
+#define TARGET_NR_ulimit ( 58) /* removed */
+ /* 59 was sys_olduname */
+#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_sgetmask ( 68) /* removed */
+#define TARGET_NR_ssetmask ( 69) /* removed */
+#define TARGET_NR_setreuid ( 70)
+#define TARGET_NR_setregid ( 71)
+#define TARGET_NR_sigsuspend ( 72)
+#define TARGET_NR_sigpending ( 73)
+#define TARGET_NR_sethostname ( 74)
+#define TARGET_NR_setrlimit ( 75)
+#define TARGET_NR_getrlimit ( 76) /* Back compat 2GB limited rlimit */
+#define TARGET_NR_getrusage ( 77)
+#define TARGET_NR_gettimeofday ( 78)
+#define TARGET_NR_settimeofday ( 79)
+#define TARGET_NR_getgroups ( 80)
+#define TARGET_NR_setgroups ( 81)
+#define TARGET_NR_select ( 82)
+#define TARGET_NR_symlink ( 83)
+ /* 84 was sys_lstat */
+#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_fchown ( 95)
+#define TARGET_NR_getpriority ( 96)
+#define TARGET_NR_setpriority ( 97)
+#define TARGET_NR_profil ( 98) /* removed */
+#define TARGET_NR_statfs ( 99)
+#define TARGET_NR_fstatfs (100)
+#define TARGET_NR_ioperm (101)
+#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)
+ /* 109 was sys_uname */
+ /* 110 was sys_iopl */
+#define TARGET_NR_vhangup (111)
+#define TARGET_NR_idle (112)
+#define TARGET_NR_syscall (113) /* syscall to call a syscall! */
+#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_modify_ldt (123)
+#define TARGET_NR_adjtimex (124)
+#define TARGET_NR_mprotect (125)
+#define TARGET_NR_sigprocmask (126)
+#define TARGET_NR_create_module (127) /* removed */
+#define TARGET_NR_init_module (128)
+#define TARGET_NR_delete_module (129)
+#define TARGET_NR_get_kernel_syms (130) /* removed */
+#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_setfsuid (138)
+#define TARGET_NR_setfsgid (139)
+#define TARGET_NR__llseek (140)
+#define TARGET_NR_getdents (141)
+#define TARGET_NR__newselect (142)
+#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_setresuid (164)
+#define TARGET_NR_getresuid (165)
+#define TARGET_NR_vm86 (166) /* removed */
+#define TARGET_NR_query_module (167) /* removed */
+#define TARGET_NR_poll (168)
+#define TARGET_NR_nfsservctl (169)
+#define TARGET_NR_setresgid (170)
+#define TARGET_NR_getresgid (171)
+#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_pread (180)
+#define TARGET_NR_pwrite (181)
+#define TARGET_NR_chown (182)
+#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)
+ /* 188 reserved */
+ /* 189 reserved */
+#define TARGET_NR_vfork (190)
+#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_getdents64 (217)
+#define TARGET_NR_pivot_root (218)
+#define TARGET_NR_mincore (219)
+#define TARGET_NR_madvise (220)
+#define TARGET_NR_fcntl64 (221)
+ /* 222 for tux */
+ /* 223 is unused */
+#define TARGET_NR_gettid (224)
+#define TARGET_NR_readahead (225)
+#define TARGET_NR_setxattr (226)
+#define TARGET_NR_lsetxattr (227)
+#define TARGET_NR_fsetxattr (228)
+#define TARGET_NR_getxattr (229)
+#define TARGET_NR_lgetxattr (230)
+#define TARGET_NR_fgetxattr (231)
+#define TARGET_NR_listxattr (232)
+#define TARGET_NR_llistxattr (233)
+#define TARGET_NR_flistxattr (234)
+#define TARGET_NR_removexattr (235)
+#define TARGET_NR_lremovexattr (236)
+#define TARGET_NR_fremovexattr (237)
+#define TARGET_NR_tkill (238)
+#define TARGET_NR_sendfile64 (239)
+#define TARGET_NR_futex (240)
+#define TARGET_NR_sched_setaffinity (241)
+#define TARGET_NR_sched_getaffinity (242)
+#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_lookup_dcookie (249)
+#define TARGET_NR_epoll_create (250)
+#define TARGET_NR_epoll_ctl (251)
+#define TARGET_NR_epoll_wait (252)
+#define TARGET_NR_remap_file_pages (253)
+ /* 254 for set_thread_area */
+ /* 255 for get_thread_area */
+ /* 256 for set_tid_address */
+#define TARGET_NR_set_tid_address 256
+#define TARGET_NR_timer_create 257
+#define TARGET_NR_timer_settime 258
+#define TARGET_NR_timer_gettime 259
+#define TARGET_NR_timer_getoverrun 260
+#define TARGET_NR_timer_delete 261
+#define TARGET_NR_clock_settime 262
+#define TARGET_NR_clock_gettime 263
+#define TARGET_NR_clock_getres 264
+#define TARGET_NR_clock_nanosleep 265
+#define TARGET_NR_statfs64 266
+#define TARGET_NR_fstatfs64 267
+#define TARGET_NR_tgkill 268
+#define TARGET_NR_utimes 269
+#define TARGET_NR_arm_fadvise64_64 270
+#define TARGET_NR_pciconfig_iobase 271
+#define TARGET_NR_pciconfig_read 272
+#define TARGET_NR_pciconfig_write 273
+#define TARGET_NR_mq_open 274
+#define TARGET_NR_mq_unlink 275
+#define TARGET_NR_mq_timedsend 276
+#define TARGET_NR_mq_timedreceive 277
+#define TARGET_NR_mq_notify 278
+#define TARGET_NR_mq_getsetattr 279
+#define TARGET_NR_waitid 280
+#define TARGET_NR_socket 281
+#define TARGET_NR_bind 282
+#define TARGET_NR_connect 283
+#define TARGET_NR_listen 284
+#define TARGET_NR_accept 285
+#define TARGET_NR_getsockname 286
+#define TARGET_NR_getpeername 287
+#define TARGET_NR_socketpair 288
+#define TARGET_NR_send 289
+#define TARGET_NR_sendto 290
+#define TARGET_NR_recv 291
+#define TARGET_NR_recvfrom 292
+#define TARGET_NR_shutdown 293
+#define TARGET_NR_setsockopt 294
+#define TARGET_NR_getsockopt 295
+#define TARGET_NR_sendmsg 296
+#define TARGET_NR_recvmsg 297
+#define TARGET_NR_semop 298
+#define TARGET_NR_semget 299
+#define TARGET_NR_semctl 300
+#define TARGET_NR_msgsnd 301
+#define TARGET_NR_msgrcv 302
+#define TARGET_NR_msgget 303
+#define TARGET_NR_msgctl 304
+#define TARGET_NR_shmat 305
+#define TARGET_NR_shmdt 306
+#define TARGET_NR_shmget 307
+#define TARGET_NR_shmctl 308
+#define TARGET_NR_add_key 309
+#define TARGET_NR_request_key 310
+#define TARGET_NR_keyctl 311
+#define TARGET_NR_semtimedop 312
+#define TARGET_NR_vserver 313
+#define TARGET_NR_ioprio_set 314
+#define TARGET_NR_ioprio_get 315
+#define TARGET_NR_inotify_init 316
+#define TARGET_NR_inotify_add_watch 317
+#define TARGET_NR_inotify_rm_watch 318
+#define TARGET_NR_mbind 319
+#define TARGET_NR_get_mempolicy 320
+#define TARGET_NR_set_mempolicy 321
+#define TARGET_NR_openat (322)
+#define TARGET_NR_mkdirat (323)
+#define TARGET_NR_mknodat (324)
+#define TARGET_NR_fchownat (325)
+#define TARGET_NR_futimesat (326)
+#define TARGET_NR_fstatat64 (327)
+#define TARGET_NR_unlinkat (328)
+#define TARGET_NR_renameat (329)
+#define TARGET_NR_linkat (330)
+#define TARGET_NR_symlinkat (331)
+#define TARGET_NR_readlinkat (332)
+#define TARGET_NR_fchmodat (333)
+#define TARGET_NR_faccessat (334)
+#define TARGET_NR_pselect6 (335)
+ /* 336 for ppoll */
+#define TARGET_NR_unshare (337)
+#define TARGET_NR_set_robust_list (338)
+#define TARGET_NR_get_robust_list (339)
+#define TARGET_NR_splice (340)
+#define TARGET_NR_arm_sync_file_range (341)
+#define TARGET_NR_sync_file_range2 TARGET_NR_arm_sync_file_range
+#define TARGET_NR_tee (342)
+#define TARGET_NR_vmsplice (343)
+#define TARGET_NR_move_pages (344)
+#define TARGET_NR_getcpu (345)
+ /* 346 for epoll_pwait */
+#define TARGET_NR_kexec_load (347)
+#define TARGET_NR_utimensat (348)
+#define TARGET_NR_signalfd (349)
+#define TARGET_NR_timerfd (350)
+#define TARGET_NR_eventfd (351)
+#define TARGET_NR_fallocate (352)
+#define TARGET_NR_timerfd_settime (353)
+#define TARGET_NR_timerfd_gettime (354)
+#define TARGET_NR_signalfd4 (355)
+#define TARGET_NR_eventfd2 (356)
+#define TARGET_NR_epoll_create1 (357)
+#define TARGET_NR_dup3 (358)
+#define TARGET_NR_pipe2 (359)
+#define TARGET_NR_inotify_init1 (360)
diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h
new file mode 100644
index 0000000..2b32813
--- /dev/null
+++ b/linux-user/arm/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long 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(CPUARMState *state)
+{
+ return state->regs[13];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/arm/termbits.h b/linux-user/arm/termbits.h
new file mode 100644
index 0000000..7772df1
--- /dev/null
+++ b/linux-user/arm/termbits.h
@@ -0,0 +1,216 @@
+/* from asm/termbits.h */
+/* NOTE: exactly the same as i386 */
+
+#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 */
+};
+
+/* 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_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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
+
+/* c_cc character offsets */
+#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
+
+/* ioctls */
+
+#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 TARGET_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_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* 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/cpu-uname.c b/linux-user/cpu-uname.c
new file mode 100644
index 0000000..23afede
--- /dev/null
+++ b/linux-user/cpu-uname.c
@@ -0,0 +1,72 @@
+/*
+ * cpu to uname machine name map
+ *
+ * Copyright (c) 2009 Loïc Minier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdio.h>
+
+#include "qemu.h"
+//#include "qemu-common.h"
+#include "cpu-uname.h"
+
+/* return highest utsname machine name for emulated instruction set
+ *
+ * NB: the default emulated CPU ("any") might not match any existing CPU, e.g.
+ * on ARM it has all features turned on, so there is no perfect arch string to
+ * return here */
+const char *cpu_to_uname_machine(void *cpu_env)
+{
+#ifdef TARGET_ARM
+ /* utsname machine name on linux arm is CPU arch name + endianness, e.g.
+ * armv7l; to get a list of CPU arch names from the linux source, use:
+ * grep arch_name: -A1 linux/arch/arm/mm/proc-*.S
+ * see arch/arm/kernel/setup.c: setup_processor()
+ *
+ * to test by CPU id, compare cpu_env->cp15.c0_cpuid to ARM_CPUID_*
+ * defines and to test by CPU feature, use arm_feature(cpu_env,
+ * ARM_FEATURE_*) */
+
+ /* in theory, endianness is configurable on some ARM CPUs, but this isn't
+ * used in user mode emulation */
+#ifdef TARGET_WORDS_BIGENDIAN
+#define utsname_suffix "b"
+#else
+#define utsname_suffix "l"
+#endif
+ if (arm_feature(cpu_env, ARM_FEATURE_V7))
+ return "armv7" utsname_suffix;
+ if (arm_feature(cpu_env, ARM_FEATURE_V6))
+ return "armv6" utsname_suffix;
+ /* earliest emulated CPU is ARMv5TE; qemu can emulate the 1026, but not its
+ * Jazelle support */
+ return "armv5te" utsname_suffix;
+#elif defined(TARGET_X86_64)
+ return "x86-64";
+#elif defined(TARGET_I386)
+ /* see arch/x86/kernel/cpu/bugs.c: check_bugs(), 386, 486, 586, 686 */
+ uint32_t cpuid_version = ((CPUX86State *)cpu_env)->cpuid_version;
+ int family = ((cpuid_version >> 8) & 0x0f) + ((cpuid_version >> 20) & 0xff);
+ if (family == 4)
+ return "i486";
+ if (family == 5)
+ return "i586";
+ return "i686";
+#else
+ /* default is #define-d in each arch/ subdir */
+ return UNAME_MACHINE;
+#endif
+}
diff --git a/linux-user/cpu-uname.h b/linux-user/cpu-uname.h
new file mode 100644
index 0000000..32492de
--- /dev/null
+++ b/linux-user/cpu-uname.h
@@ -0,0 +1 @@
+const char *cpu_to_uname_machine(void *cpu_env);
diff --git a/linux-user/cris/syscall.h b/linux-user/cris/syscall.h
new file mode 100644
index 0000000..24f92ba
--- /dev/null
+++ b/linux-user/cris/syscall.h
@@ -0,0 +1,36 @@
+
+#define UNAME_MACHINE "cris"
+
+/* pt_regs not only specifices the format in the user-struct during
+ * ptrace but is also the frame format used in the kernel prologue/epilogues
+ * themselves
+ */
+
+struct target_pt_regs {
+ unsigned long orig_r10;
+ /* pushed by movem r13, [sp] in SAVE_ALL. */
+ unsigned long r0;
+ unsigned long r1;
+ unsigned long r2;
+ unsigned long r3;
+ unsigned long r4;
+ unsigned long r5;
+ unsigned long r6;
+ unsigned long r7;
+ unsigned long r8;
+ unsigned long r9;
+ unsigned long r10;
+ unsigned long r11;
+ unsigned long r12;
+ unsigned long r13;
+ unsigned long acr;
+ unsigned long srs;
+ unsigned long mof;
+ unsigned long spc;
+ unsigned long ccs;
+ unsigned long srp;
+ unsigned long erp; /* This is actually the debugged process' PC */
+ /* For debugging purposes; saved only when needed. */
+ unsigned long exs;
+ unsigned long eda;
+};
diff --git a/linux-user/cris/syscall_nr.h b/linux-user/cris/syscall_nr.h
new file mode 100644
index 0000000..6132817
--- /dev/null
+++ b/linux-user/cris/syscall_nr.h
@@ -0,0 +1,335 @@
+/*
+ * This file contains the system call numbers, and stub macros for libc.
+ */
+
+#define TARGET_NR_restart_syscall 0
+#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_waitpid 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_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#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_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#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_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#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_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#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_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86 113
+#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_modify_ldt 123
+#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_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#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_setresuid 164
+#define TARGET_NR_getresuid 165
+
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#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_chown 182
+#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 /* some people actually want streams */
+#define TARGET_NR_putpmsg 189 /* some people actually want streams */
+#define TARGET_NR_vfork 190
+#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_pivot_root 217
+#define TARGET_NR_mincore 218
+#define TARGET_NR_madvise 219
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_fcntl64 221
+/* 223 is unused */
+#define TARGET_NR_gettid 224
+#define TARGET_NR_readahead 225
+#define TARGET_NR_setxattr 226
+#define TARGET_NR_lsetxattr 227
+#define TARGET_NR_fsetxattr 228
+#define TARGET_NR_getxattr 229
+#define TARGET_NR_lgetxattr 230
+#define TARGET_NR_fgetxattr 231
+#define TARGET_NR_listxattr 232
+#define TARGET_NR_llistxattr 233
+#define TARGET_NR_flistxattr 234
+#define TARGET_NR_removexattr 235
+#define TARGET_NR_lremovexattr 236
+#define TARGET_NR_fremovexattr 237
+#define TARGET_NR_tkill 238
+#define TARGET_NR_sendfile64 239
+#define TARGET_NR_futex 240
+#define TARGET_NR_sched_setaffinity 241
+#define TARGET_NR_sched_getaffinity 242
+#define TARGET_NR_set_thread_area 243
+#define TARGET_NR_get_thread_area 244
+#define TARGET_NR_io_setup 245
+#define TARGET_NR_io_destroy 246
+#define TARGET_NR_io_getevents 247
+#define TARGET_NR_io_submit 248
+#define TARGET_NR_io_cancel 249
+#define TARGET_NR_fadvise64 250
+#define TARGET_NR_exit_group 252
+#define TARGET_NR_lookup_dcookie 253
+#define TARGET_NR_epoll_create 254
+#define TARGET_NR_epoll_ctl 255
+#define TARGET_NR_epoll_wait 256
+#define TARGET_NR_remap_file_pages 257
+#define TARGET_NR_set_tid_address 258
+#define TARGET_NR_timer_create 259
+#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)
+#define TARGET_NR_statfs64 268
+#define TARGET_NR_fstatfs64 269
+#define TARGET_NR_tgkill 270
+#define TARGET_NR_utimes 271
+#define TARGET_NR_fadvise64_64 272
+#define TARGET_NR_vserver 273
+#define TARGET_NR_mbind 274
+#define TARGET_NR_get_mempolicy 275
+#define TARGET_NR_set_mempolicy 276
+#define TARGET_NR_mq_open 277
+#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1)
+#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3)
+#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5)
+#define TARGET_NR_kexec_load 283
+#define TARGET_NR_waitid 284
+/* #define TARGET_NR_sys_setaltroot 285 */
+#define TARGET_NR_add_key 286
+#define TARGET_NR_request_key 287
+#define TARGET_NR_keyctl 288
+#define TARGET_NR_ioprio_set 289
+#define TARGET_NR_ioprio_get 290
+#define TARGET_NR_inotify_init 291
+#define TARGET_NR_inotify_add_watch 292
+#define TARGET_NR_inotify_rm_watch 293
+#define TARGET_NR_migrate_pages 294
+#define TARGET_NR_openat 295
+#define TARGET_NR_mkdirat 296
+#define TARGET_NR_mknodat 297
+#define TARGET_NR_fchownat 298
+#define TARGET_NR_futimesat 299
+#define TARGET_NR_fstatat64 300
+#define TARGET_NR_unlinkat 301
+#define TARGET_NR_renameat 302
+#define TARGET_NR_linkat 303
+#define TARGET_NR_symlinkat 304
+#define TARGET_NR_readlinkat 305
+#define TARGET_NR_fchmodat 306
+#define TARGET_NR_faccessat 307
+#define TARGET_NR_pselect6 308
+#define TARGET_NR_ppoll 309
+#define TARGET_NR_unshare 310
+#define TARGET_NR_set_robust_list 311
+#define TARGET_NR_get_robust_list 312
+#define TARGET_NR_splice 313
+#define TARGET_NR_sync_file_range 314
+#define TARGET_NR_tee 315
+#define TARGET_NR_vmsplice 316
+#define TARGET_NR_move_pages 317
+#define TARGET_NR_getcpu 318
+#define TARGET_NR_epoll_pwait 319
+#define TARGET_NR_utimensat 320
+#define TARGET_NR_signalfd 321
+#define TARGET_NR_timerfd_create 322
+#define TARGET_NR_eventfd 323
+#define TARGET_NR_fallocate 324
+#define TARGET_NR_timerfd_settime 325
+#define TARGET_NR_timerfd_gettime 326
+#define TARGET_NR_signalfd4 327
+#define TARGET_NR_eventfd2 328
+#define TARGET_NR_epoll_create1 329
+#define TARGET_NR_dup3 330
+#define TARGET_NR_pipe2 331
+#define TARGET_NR_inotify_init1 332
diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h
new file mode 100644
index 0000000..5611840
--- /dev/null
+++ b/linux-user/cris/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_ulong ss_size;
+ abi_long ss_flags;
+} 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(CPUCRISState *state)
+{
+ return state->regs[14];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/cris/termbits.h b/linux-user/cris/termbits.h
new file mode 100644
index 0000000..fc82ca0
--- /dev/null
+++ b/linux-user/cris/termbits.h
@@ -0,0 +1,213 @@
+/* from asm/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 */
+};
+
+/* 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
+
+/* 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_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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
+
+/* c_cc character offsets */
+#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
+
+/* ioctls */
+
+#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 TARGET_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_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* 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/elfload.c b/linux-user/elfload.c
new file mode 100644
index 0000000..08c44d8
--- /dev/null
+++ b/linux-user/elfload.c
@@ -0,0 +1,2496 @@
+/* This is the Linux kernel elf-loading code, ported into user space */
+#include <sys/time.h>
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "qemu.h"
+#include "disas.h"
+
+#ifdef _ARCH_PPC64
+#undef ARCH_DLINFO
+#undef ELF_PLATFORM
+#undef ELF_HWCAP
+#undef ELF_CLASS
+#undef ELF_DATA
+#undef ELF_ARCH
+#endif
+
+#define ELF_OSABI ELFOSABI_SYSV
+
+/* from personality.h */
+
+/*
+ * Flags for bug emulation.
+ *
+ * These occupy the top three bytes.
+ */
+enum {
+ ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */
+ FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to
+ descriptors (signal handling) */
+ MMAP_PAGE_ZERO = 0x0100000,
+ ADDR_COMPAT_LAYOUT = 0x0200000,
+ READ_IMPLIES_EXEC = 0x0400000,
+ ADDR_LIMIT_32BIT = 0x0800000,
+ SHORT_INODE = 0x1000000,
+ WHOLE_SECONDS = 0x2000000,
+ STICKY_TIMEOUTS = 0x4000000,
+ ADDR_LIMIT_3GB = 0x8000000,
+};
+
+/*
+ * Personality types.
+ *
+ * These go in the low byte. Avoid using the top bit, it will
+ * conflict with error returns.
+ */
+enum {
+ PER_LINUX = 0x0000,
+ PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT,
+ PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS,
+ PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+ PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS | SHORT_INODE,
+ PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
+ PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS,
+ PER_BSD = 0x0006,
+ PER_SUNOS = 0x0006 | STICKY_TIMEOUTS,
+ PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
+ PER_LINUX32 = 0x0008,
+ PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB,
+ PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
+ PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
+ PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
+ PER_RISCOS = 0x000c,
+ PER_SOLARIS = 0x000d | STICKY_TIMEOUTS,
+ PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
+ PER_OSF4 = 0x000f, /* OSF/1 v4 */
+ PER_HPUX = 0x0010,
+ PER_MASK = 0x00ff,
+};
+
+/*
+ * Return the base personality without flags.
+ */
+#define personality(pers) (pers & PER_MASK)
+
+/* this flag is uneffective under linux too, should be deleted */
+#ifndef MAP_DENYWRITE
+#define MAP_DENYWRITE 0
+#endif
+
+/* should probably go in elf.h */
+#ifndef ELIBBAD
+#define ELIBBAD 80
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define ELF_DATA ELFDATA2MSB
+#else
+#define ELF_DATA ELFDATA2LSB
+#endif
+
+typedef target_ulong target_elf_greg_t;
+#ifdef USE_UID16
+typedef uint16_t target_uid_t;
+typedef uint16_t target_gid_t;
+#else
+typedef uint32_t target_uid_t;
+typedef uint32_t target_gid_t;
+#endif
+typedef int32_t target_pid_t;
+
+#ifdef TARGET_I386
+
+#define ELF_PLATFORM get_elf_platform()
+
+static const char *get_elf_platform(void)
+{
+ static char elf_platform[] = "i386";
+ int family = (thread_env->cpuid_version >> 8) & 0xff;
+ if (family > 6)
+ family = 6;
+ if (family >= 3)
+ elf_platform[1] = '0' + family;
+ return elf_platform;
+}
+
+#define ELF_HWCAP get_elf_hwcap()
+
+static uint32_t get_elf_hwcap(void)
+{
+ return thread_env->cpuid_features;
+}
+
+#ifdef TARGET_X86_64
+#define ELF_START_MMAP 0x2aaaaab000ULL
+#define elf_check_arch(x) ( ((x) == ELF_ARCH) )
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_X86_64
+
+static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ regs->rax = 0;
+ regs->rsp = infop->start_stack;
+ regs->rip = infop->entry;
+}
+
+#define ELF_NREG 27
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+/*
+ * Note that ELF_NREG should be 29 as there should be place for
+ * TRAPNO and ERR "registers" as well but linux doesn't dump
+ * those.
+ *
+ * See linux kernel: arch/x86/include/asm/elf.h
+ */
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ (*regs)[0] = env->regs[15];
+ (*regs)[1] = env->regs[14];
+ (*regs)[2] = env->regs[13];
+ (*regs)[3] = env->regs[12];
+ (*regs)[4] = env->regs[R_EBP];
+ (*regs)[5] = env->regs[R_EBX];
+ (*regs)[6] = env->regs[11];
+ (*regs)[7] = env->regs[10];
+ (*regs)[8] = env->regs[9];
+ (*regs)[9] = env->regs[8];
+ (*regs)[10] = env->regs[R_EAX];
+ (*regs)[11] = env->regs[R_ECX];
+ (*regs)[12] = env->regs[R_EDX];
+ (*regs)[13] = env->regs[R_ESI];
+ (*regs)[14] = env->regs[R_EDI];
+ (*regs)[15] = env->regs[R_EAX]; /* XXX */
+ (*regs)[16] = env->eip;
+ (*regs)[17] = env->segs[R_CS].selector & 0xffff;
+ (*regs)[18] = env->eflags;
+ (*regs)[19] = env->regs[R_ESP];
+ (*regs)[20] = env->segs[R_SS].selector & 0xffff;
+ (*regs)[21] = env->segs[R_FS].selector & 0xffff;
+ (*regs)[22] = env->segs[R_GS].selector & 0xffff;
+ (*regs)[23] = env->segs[R_DS].selector & 0xffff;
+ (*regs)[24] = env->segs[R_ES].selector & 0xffff;
+ (*regs)[25] = env->segs[R_FS].selector & 0xffff;
+ (*regs)[26] = env->segs[R_GS].selector & 0xffff;
+}
+
+#else
+
+#define ELF_START_MMAP 0x80000000
+
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) )
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_386
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->esp = infop->start_stack;
+ regs->eip = infop->entry;
+
+ /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program
+ starts %edx contains a pointer to a function which might be
+ registered using `atexit'. This provides a mean for the
+ dynamic linker to call DT_FINI functions for shared libraries
+ that have been loaded before the code runs.
+
+ A value of 0 tells we have no such handler. */
+ regs->edx = 0;
+}
+
+#define ELF_NREG 17
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+/*
+ * Note that ELF_NREG should be 19 as there should be place for
+ * TRAPNO and ERR "registers" as well but linux doesn't dump
+ * those.
+ *
+ * See linux kernel: arch/x86/include/asm/elf.h
+ */
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ (*regs)[0] = env->regs[R_EBX];
+ (*regs)[1] = env->regs[R_ECX];
+ (*regs)[2] = env->regs[R_EDX];
+ (*regs)[3] = env->regs[R_ESI];
+ (*regs)[4] = env->regs[R_EDI];
+ (*regs)[5] = env->regs[R_EBP];
+ (*regs)[6] = env->regs[R_EAX];
+ (*regs)[7] = env->segs[R_DS].selector & 0xffff;
+ (*regs)[8] = env->segs[R_ES].selector & 0xffff;
+ (*regs)[9] = env->segs[R_FS].selector & 0xffff;
+ (*regs)[10] = env->segs[R_GS].selector & 0xffff;
+ (*regs)[11] = env->regs[R_EAX]; /* XXX */
+ (*regs)[12] = env->eip;
+ (*regs)[13] = env->segs[R_CS].selector & 0xffff;
+ (*regs)[14] = env->eflags;
+ (*regs)[15] = env->regs[R_ESP];
+ (*regs)[16] = env->segs[R_SS].selector & 0xffff;
+}
+#endif
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_ARM
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_ARM )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_ARM
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ abi_long stack = infop->start_stack;
+ memset(regs, 0, sizeof(*regs));
+ regs->ARM_cpsr = 0x10;
+ if (infop->entry & 1)
+ regs->ARM_cpsr |= CPSR_T;
+ regs->ARM_pc = infop->entry & 0xfffffffe;
+ regs->ARM_sp = infop->start_stack;
+ /* FIXME - what to for failure of get_user()? */
+ get_user_ual(regs->ARM_r2, stack + 8); /* envp */
+ get_user_ual(regs->ARM_r1, stack + 4); /* envp */
+ /* XXX: it seems that r0 is zeroed after ! */
+ regs->ARM_r0 = 0;
+ /* For uClinux PIC binaries. */
+ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */
+ regs->ARM_r10 = infop->start_data;
+}
+
+#define ELF_NREG 18
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ (*regs)[0] = tswapl(env->regs[0]);
+ (*regs)[1] = tswapl(env->regs[1]);
+ (*regs)[2] = tswapl(env->regs[2]);
+ (*regs)[3] = tswapl(env->regs[3]);
+ (*regs)[4] = tswapl(env->regs[4]);
+ (*regs)[5] = tswapl(env->regs[5]);
+ (*regs)[6] = tswapl(env->regs[6]);
+ (*regs)[7] = tswapl(env->regs[7]);
+ (*regs)[8] = tswapl(env->regs[8]);
+ (*regs)[9] = tswapl(env->regs[9]);
+ (*regs)[10] = tswapl(env->regs[10]);
+ (*regs)[11] = tswapl(env->regs[11]);
+ (*regs)[12] = tswapl(env->regs[12]);
+ (*regs)[13] = tswapl(env->regs[13]);
+ (*regs)[14] = tswapl(env->regs[14]);
+ (*regs)[15] = tswapl(env->regs[15]);
+
+ (*regs)[16] = tswapl(cpsr_read((CPUState *)env));
+ (*regs)[17] = tswapl(env->regs[0]); /* XXX */
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+enum
+{
+ ARM_HWCAP_ARM_SWP = 1 << 0,
+ ARM_HWCAP_ARM_HALF = 1 << 1,
+ ARM_HWCAP_ARM_THUMB = 1 << 2,
+ ARM_HWCAP_ARM_26BIT = 1 << 3,
+ ARM_HWCAP_ARM_FAST_MULT = 1 << 4,
+ ARM_HWCAP_ARM_FPA = 1 << 5,
+ ARM_HWCAP_ARM_VFP = 1 << 6,
+ ARM_HWCAP_ARM_EDSP = 1 << 7,
+ ARM_HWCAP_ARM_JAVA = 1 << 8,
+ ARM_HWCAP_ARM_IWMMXT = 1 << 9,
+ ARM_HWCAP_ARM_THUMBEE = 1 << 10,
+ ARM_HWCAP_ARM_NEON = 1 << 11,
+ ARM_HWCAP_ARM_VFPv3 = 1 << 12,
+ ARM_HWCAP_ARM_VFPv3D16 = 1 << 13,
+};
+
+#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \
+ | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \
+ | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \
+ | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 )
+
+#endif
+
+#ifdef TARGET_SPARC
+#ifdef TARGET_SPARC64
+
+#define ELF_START_MMAP 0x80000000
+
+#ifndef TARGET_ABI32
+#define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS )
+#else
+#define elf_check_arch(x) ( (x) == EM_SPARC32PLUS || (x) == EM_SPARC )
+#endif
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_SPARCV9
+
+#define STACK_BIAS 2047
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+#ifndef TARGET_ABI32
+ regs->tstate = 0;
+#endif
+ regs->pc = infop->entry;
+ regs->npc = regs->pc + 4;
+ regs->y = 0;
+#ifdef TARGET_ABI32
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+#else
+ if (personality(infop->personality) == PER_LINUX32)
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+ else
+ regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS;
+#endif
+}
+
+#else
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SPARC )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_SPARC
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->psr = 0;
+ regs->pc = infop->entry;
+ regs->npc = regs->pc + 4;
+ regs->y = 0;
+ regs->u_regs[14] = infop->start_stack - 16 * 4;
+}
+
+#endif
+#endif
+
+#ifdef TARGET_PPC
+
+#define ELF_START_MMAP 0x80000000
+
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+
+#define elf_check_arch(x) ( (x) == EM_PPC64 )
+
+#define ELF_CLASS ELFCLASS64
+
+#else
+
+#define elf_check_arch(x) ( (x) == EM_PPC )
+
+#define ELF_CLASS ELFCLASS32
+
+#endif
+
+#define ELF_ARCH EM_PPC
+
+/* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP).
+ See arch/powerpc/include/asm/cputable.h. */
+enum {
+ QEMU_PPC_FEATURE_32 = 0x80000000,
+ QEMU_PPC_FEATURE_64 = 0x40000000,
+ QEMU_PPC_FEATURE_601_INSTR = 0x20000000,
+ QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000,
+ QEMU_PPC_FEATURE_HAS_FPU = 0x08000000,
+ QEMU_PPC_FEATURE_HAS_MMU = 0x04000000,
+ QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000,
+ QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000,
+ QEMU_PPC_FEATURE_HAS_SPE = 0x00800000,
+ QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000,
+ QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000,
+ QEMU_PPC_FEATURE_NO_TB = 0x00100000,
+ QEMU_PPC_FEATURE_POWER4 = 0x00080000,
+ QEMU_PPC_FEATURE_POWER5 = 0x00040000,
+ QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000,
+ QEMU_PPC_FEATURE_CELL = 0x00010000,
+ QEMU_PPC_FEATURE_BOOKE = 0x00008000,
+ QEMU_PPC_FEATURE_SMT = 0x00004000,
+ QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000,
+ QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000,
+ QEMU_PPC_FEATURE_PA6T = 0x00000800,
+ QEMU_PPC_FEATURE_HAS_DFP = 0x00000400,
+ QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200,
+ QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100,
+ QEMU_PPC_FEATURE_HAS_VSX = 0x00000080,
+ QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040,
+
+ QEMU_PPC_FEATURE_TRUE_LE = 0x00000002,
+ QEMU_PPC_FEATURE_PPC_LE = 0x00000001,
+};
+
+#define ELF_HWCAP get_elf_hwcap()
+
+static uint32_t get_elf_hwcap(void)
+{
+ CPUState *e = thread_env;
+ uint32_t features = 0;
+
+ /* We don't have to be terribly complete here; the high points are
+ Altivec/FP/SPE support. Anything else is just a bonus. */
+#define GET_FEATURE(flag, feature) \
+ do {if (e->insns_flags & flag) features |= feature; } while(0)
+ GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64);
+ GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU);
+ GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC);
+ GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE);
+ GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE);
+ GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE);
+ GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE);
+ GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC);
+#undef GET_FEATURE
+
+ return features;
+}
+
+/*
+ * The requirements here are:
+ * - keep the final alignment of sp (sp & 0xf)
+ * - make sure the 32-bit value at the first 16 byte aligned position of
+ * AUXV is greater than 16 for glibc compatibility.
+ * AT_IGNOREPPC is used for that.
+ * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC,
+ * even if DLINFO_ARCH_ITEMS goes to zero or is undefined.
+ */
+#define DLINFO_ARCH_ITEMS 5
+#define ARCH_DLINFO \
+ do { \
+ NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \
+ NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
+ /* \
+ * Now handle glibc compatibility. \
+ */ \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
+ } while (0)
+
+static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
+{
+ _regs->gpr[1] = infop->start_stack;
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ _regs->gpr[2] = ldq_raw(infop->entry + 8) + infop->load_addr;
+ infop->entry = ldq_raw(infop->entry) + infop->load_addr;
+#endif
+ _regs->nip = infop->entry;
+}
+
+/* See linux kernel: arch/powerpc/include/asm/elf.h. */
+#define ELF_NREG 48
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ int i;
+ target_ulong ccr = 0;
+
+ for (i = 0; i < ARRAY_SIZE(env->gpr); i++) {
+ (*regs)[i] = tswapl(env->gpr[i]);
+ }
+
+ (*regs)[32] = tswapl(env->nip);
+ (*regs)[33] = tswapl(env->msr);
+ (*regs)[35] = tswapl(env->ctr);
+ (*regs)[36] = tswapl(env->lr);
+ (*regs)[37] = tswapl(env->xer);
+
+ for (i = 0; i < ARRAY_SIZE(env->crf); i++) {
+ ccr |= env->crf[i] << (32 - ((i + 1) * 4));
+ }
+ (*regs)[38] = tswapl(ccr);
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_MIPS
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_MIPS )
+
+#ifdef TARGET_MIPS64
+#define ELF_CLASS ELFCLASS64
+#else
+#define ELF_CLASS ELFCLASS32
+#endif
+#define ELF_ARCH EM_MIPS
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->cp0_status = 2 << CP0St_KSU;
+ regs->cp0_epc = infop->entry;
+ regs->regs[29] = infop->start_stack;
+}
+
+/* See linux kernel: arch/mips/include/asm/elf.h. */
+#define ELF_NREG 45
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+/* See linux kernel: arch/mips/include/asm/reg.h. */
+enum {
+#ifdef TARGET_MIPS64
+ TARGET_EF_R0 = 0,
+#else
+ TARGET_EF_R0 = 6,
+#endif
+ TARGET_EF_R26 = TARGET_EF_R0 + 26,
+ TARGET_EF_R27 = TARGET_EF_R0 + 27,
+ TARGET_EF_LO = TARGET_EF_R0 + 32,
+ TARGET_EF_HI = TARGET_EF_R0 + 33,
+ TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34,
+ TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35,
+ TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36,
+ TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37
+};
+
+/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ int i;
+
+ for (i = 0; i < TARGET_EF_R0; i++) {
+ (*regs)[i] = 0;
+ }
+ (*regs)[TARGET_EF_R0] = 0;
+
+ for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) {
+ (*regs)[TARGET_EF_R0 + i] = tswapl(env->active_tc.gpr[i]);
+ }
+
+ (*regs)[TARGET_EF_R26] = 0;
+ (*regs)[TARGET_EF_R27] = 0;
+ (*regs)[TARGET_EF_LO] = tswapl(env->active_tc.LO[0]);
+ (*regs)[TARGET_EF_HI] = tswapl(env->active_tc.HI[0]);
+ (*regs)[TARGET_EF_CP0_EPC] = tswapl(env->active_tc.PC);
+ (*regs)[TARGET_EF_CP0_BADVADDR] = tswapl(env->CP0_BadVAddr);
+ (*regs)[TARGET_EF_CP0_STATUS] = tswapl(env->CP0_Status);
+ (*regs)[TARGET_EF_CP0_CAUSE] = tswapl(env->CP0_Cause);
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif /* TARGET_MIPS */
+
+#ifdef TARGET_MICROBLAZE
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_MICROBLAZE
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->pc = infop->entry;
+ regs->r1 = infop->start_stack;
+
+}
+
+#define ELF_EXEC_PAGESIZE 4096
+
+#define USE_ELF_CORE_DUMP
+#define ELF_NREG 38
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ int i, pos = 0;
+
+ for (i = 0; i < 32; i++) {
+ (*regs)[pos++] = tswapl(env->regs[i]);
+ }
+
+ for (i = 0; i < 6; i++) {
+ (*regs)[pos++] = tswapl(env->sregs[i]);
+ }
+}
+
+#endif /* TARGET_MICROBLAZE */
+
+#ifdef TARGET_SH4
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_SH )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_SH
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ /* Check other registers XXXXX */
+ regs->pc = infop->entry;
+ regs->regs[15] = infop->start_stack;
+}
+
+/* See linux kernel: arch/sh/include/asm/elf.h. */
+#define ELF_NREG 23
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+/* See linux kernel: arch/sh/include/asm/ptrace.h. */
+enum {
+ TARGET_REG_PC = 16,
+ TARGET_REG_PR = 17,
+ TARGET_REG_SR = 18,
+ TARGET_REG_GBR = 19,
+ TARGET_REG_MACH = 20,
+ TARGET_REG_MACL = 21,
+ TARGET_REG_SYSCALL = 22
+};
+
+static inline void elf_core_copy_regs(target_elf_gregset_t *regs,
+ const CPUState *env)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ (*regs[i]) = tswapl(env->gregs[i]);
+ }
+
+ (*regs)[TARGET_REG_PC] = tswapl(env->pc);
+ (*regs)[TARGET_REG_PR] = tswapl(env->pr);
+ (*regs)[TARGET_REG_SR] = tswapl(env->sr);
+ (*regs)[TARGET_REG_GBR] = tswapl(env->gbr);
+ (*regs)[TARGET_REG_MACH] = tswapl(env->mach);
+ (*regs)[TARGET_REG_MACL] = tswapl(env->macl);
+ (*regs)[TARGET_REG_SYSCALL] = 0; /* FIXME */
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif
+
+#ifdef TARGET_CRIS
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_CRIS )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_CRIS
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->erp = infop->entry;
+}
+
+#define ELF_EXEC_PAGESIZE 8192
+
+#endif
+
+#ifdef TARGET_M68K
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ( (x) == EM_68K )
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_68K
+
+/* ??? Does this need to do anything?
+ #define ELF_PLAT_INIT(_r) */
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->usp = infop->start_stack;
+ regs->sr = 0;
+ regs->pc = infop->entry;
+}
+
+/* See linux kernel: arch/m68k/include/asm/elf.h. */
+#define ELF_NREG 20
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ (*regs)[0] = tswapl(env->dregs[1]);
+ (*regs)[1] = tswapl(env->dregs[2]);
+ (*regs)[2] = tswapl(env->dregs[3]);
+ (*regs)[3] = tswapl(env->dregs[4]);
+ (*regs)[4] = tswapl(env->dregs[5]);
+ (*regs)[5] = tswapl(env->dregs[6]);
+ (*regs)[6] = tswapl(env->dregs[7]);
+ (*regs)[7] = tswapl(env->aregs[0]);
+ (*regs)[8] = tswapl(env->aregs[1]);
+ (*regs)[9] = tswapl(env->aregs[2]);
+ (*regs)[10] = tswapl(env->aregs[3]);
+ (*regs)[11] = tswapl(env->aregs[4]);
+ (*regs)[12] = tswapl(env->aregs[5]);
+ (*regs)[13] = tswapl(env->aregs[6]);
+ (*regs)[14] = tswapl(env->dregs[0]);
+ (*regs)[15] = tswapl(env->aregs[7]);
+ (*regs)[16] = tswapl(env->dregs[0]); /* FIXME: orig_d0 */
+ (*regs)[17] = tswapl(env->sr);
+ (*regs)[18] = tswapl(env->pc);
+ (*regs)[19] = 0; /* FIXME: regs->format | regs->vector */
+}
+
+#define USE_ELF_CORE_DUMP
+#define ELF_EXEC_PAGESIZE 8192
+
+#endif
+
+#ifdef TARGET_ALPHA
+
+#define ELF_START_MMAP (0x30000000000ULL)
+
+#define elf_check_arch(x) ( (x) == ELF_ARCH )
+
+#define ELF_CLASS ELFCLASS64
+#define ELF_ARCH EM_ALPHA
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ regs->pc = infop->entry;
+ regs->ps = 8;
+ regs->usp = infop->start_stack;
+}
+
+#define ELF_EXEC_PAGESIZE 8192
+
+#endif /* TARGET_ALPHA */
+
+#ifndef ELF_PLATFORM
+#define ELF_PLATFORM (NULL)
+#endif
+
+#ifndef ELF_HWCAP
+#define ELF_HWCAP 0
+#endif
+
+#ifdef TARGET_ABI32
+#undef ELF_CLASS
+#define ELF_CLASS ELFCLASS32
+#undef bswaptls
+#define bswaptls(ptr) bswap32s(ptr)
+#endif
+
+#include "elf.h"
+
+struct exec
+{
+ unsigned int a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned int a_text; /* length of text, in bytes */
+ unsigned int a_data; /* length of data, in bytes */
+ unsigned int a_bss; /* length of uninitialized data area, in bytes */
+ unsigned int a_syms; /* length of symbol table data in file, in bytes */
+ unsigned int a_entry; /* start address */
+ unsigned int a_trsize; /* length of relocation info for text, in bytes */
+ unsigned int a_drsize; /* length of relocation info for data, in bytes */
+};
+
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+
+/* Necessary parameters */
+#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE
+#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1))
+#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1))
+
+#define DLINFO_ITEMS 12
+
+static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
+{
+ memcpy(to, from, n);
+}
+
+#ifdef BSWAP_NEEDED
+static void bswap_ehdr(struct elfhdr *ehdr)
+{
+ bswap16s(&ehdr->e_type); /* Object file type */
+ bswap16s(&ehdr->e_machine); /* Architecture */
+ bswap32s(&ehdr->e_version); /* Object file version */
+ bswaptls(&ehdr->e_entry); /* Entry point virtual address */
+ bswaptls(&ehdr->e_phoff); /* Program header table file offset */
+ bswaptls(&ehdr->e_shoff); /* Section header table file offset */
+ bswap32s(&ehdr->e_flags); /* Processor-specific flags */
+ bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */
+ bswap16s(&ehdr->e_phentsize); /* Program header table entry size */
+ bswap16s(&ehdr->e_phnum); /* Program header table entry count */
+ bswap16s(&ehdr->e_shentsize); /* Section header table entry size */
+ bswap16s(&ehdr->e_shnum); /* Section header table entry count */
+ bswap16s(&ehdr->e_shstrndx); /* Section header string table index */
+}
+
+static void bswap_phdr(struct elf_phdr *phdr, int phnum)
+{
+ int i;
+ for (i = 0; i < phnum; ++i, ++phdr) {
+ bswap32s(&phdr->p_type); /* Segment type */
+ bswap32s(&phdr->p_flags); /* Segment flags */
+ bswaptls(&phdr->p_offset); /* Segment file offset */
+ bswaptls(&phdr->p_vaddr); /* Segment virtual address */
+ bswaptls(&phdr->p_paddr); /* Segment physical address */
+ bswaptls(&phdr->p_filesz); /* Segment size in file */
+ bswaptls(&phdr->p_memsz); /* Segment size in memory */
+ bswaptls(&phdr->p_align); /* Segment alignment */
+ }
+}
+
+static void bswap_shdr(struct elf_shdr *shdr, int shnum)
+{
+ int i;
+ for (i = 0; i < shnum; ++i, ++shdr) {
+ bswap32s(&shdr->sh_name);
+ bswap32s(&shdr->sh_type);
+ bswaptls(&shdr->sh_flags);
+ bswaptls(&shdr->sh_addr);
+ bswaptls(&shdr->sh_offset);
+ bswaptls(&shdr->sh_size);
+ bswap32s(&shdr->sh_link);
+ bswap32s(&shdr->sh_info);
+ bswaptls(&shdr->sh_addralign);
+ bswaptls(&shdr->sh_entsize);
+ }
+}
+
+static void bswap_sym(struct elf_sym *sym)
+{
+ bswap32s(&sym->st_name);
+ bswaptls(&sym->st_value);
+ bswaptls(&sym->st_size);
+ bswap16s(&sym->st_shndx);
+}
+#else
+static inline void bswap_ehdr(struct elfhdr *ehdr) { }
+static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { }
+static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { }
+static inline void bswap_sym(struct elf_sym *sym) { }
+#endif
+
+#ifdef USE_ELF_CORE_DUMP
+static int elf_core_dump(int, const CPUState *);
+#endif /* USE_ELF_CORE_DUMP */
+static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias);
+
+/* Verify the portions of EHDR within E_IDENT for the target.
+ This can be performed before bswapping the entire header. */
+static bool elf_check_ident(struct elfhdr *ehdr)
+{
+ return (ehdr->e_ident[EI_MAG0] == ELFMAG0
+ && ehdr->e_ident[EI_MAG1] == ELFMAG1
+ && ehdr->e_ident[EI_MAG2] == ELFMAG2
+ && ehdr->e_ident[EI_MAG3] == ELFMAG3
+ && ehdr->e_ident[EI_CLASS] == ELF_CLASS
+ && ehdr->e_ident[EI_DATA] == ELF_DATA
+ && ehdr->e_ident[EI_VERSION] == EV_CURRENT);
+}
+
+/* Verify the portions of EHDR outside of E_IDENT for the target.
+ This has to wait until after bswapping the header. */
+static bool elf_check_ehdr(struct elfhdr *ehdr)
+{
+ return (elf_check_arch(ehdr->e_machine)
+ && ehdr->e_ehsize == sizeof(struct elfhdr)
+ && ehdr->e_phentsize == sizeof(struct elf_phdr)
+ && ehdr->e_shentsize == sizeof(struct elf_shdr)
+ && (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN));
+}
+
+/*
+ * 'copy_elf_strings()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ *
+ */
+static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
+ abi_ulong p)
+{
+ char *tmp, *tmp1, *pag = NULL;
+ int len, offset = 0;
+
+ if (!p) {
+ return 0; /* bullet-proofing */
+ }
+ while (argc-- > 0) {
+ tmp = argv[argc];
+ if (!tmp) {
+ fprintf(stderr, "VFS: argc is wrong");
+ exit(-1);
+ }
+ tmp1 = tmp;
+ while (*tmp++);
+ len = tmp - tmp1;
+ if (p < len) { /* this shouldn't happen - 128kB */
+ return 0;
+ }
+ while (len) {
+ --p; --tmp; --len;
+ if (--offset < 0) {
+ offset = p % TARGET_PAGE_SIZE;
+ pag = (char *)page[p/TARGET_PAGE_SIZE];
+ if (!pag) {
+ pag = (char *)malloc(TARGET_PAGE_SIZE);
+ memset(pag, 0, TARGET_PAGE_SIZE);
+ page[p/TARGET_PAGE_SIZE] = pag;
+ if (!pag)
+ return 0;
+ }
+ }
+ if (len == 0 || offset == 0) {
+ *(pag + offset) = *tmp;
+ }
+ else {
+ int bytes_to_copy = (len > offset) ? offset : len;
+ tmp -= bytes_to_copy;
+ p -= bytes_to_copy;
+ offset -= bytes_to_copy;
+ len -= bytes_to_copy;
+ memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
+ }
+ }
+ }
+ return p;
+}
+
+static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
+ struct image_info *info)
+{
+ abi_ulong stack_base, size, error, guard;
+ int i;
+
+ /* Create enough stack to hold everything. If we don't use
+ it for args, we'll use it for something else. */
+ size = guest_stack_size;
+ if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) {
+ size = MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ }
+ guard = TARGET_PAGE_SIZE;
+ if (guard < qemu_real_host_page_size) {
+ guard = qemu_real_host_page_size;
+ }
+
+ error = target_mmap(0, size + guard, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (error == -1) {
+ perror("mmap stack");
+ exit(-1);
+ }
+
+ /* We reserve one extra page at the top of the stack as guard. */
+ target_mprotect(error, guard, PROT_NONE);
+
+ info->stack_limit = error + guard;
+ stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE;
+ p += stack_base;
+
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ if (bprm->page[i]) {
+ info->rss++;
+ /* FIXME - check return value of memcpy_to_target() for failure */
+ memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
+ free(bprm->page[i]);
+ }
+ stack_base += TARGET_PAGE_SIZE;
+ }
+ return p;
+}
+
+/* Map and zero the bss. We need to explicitly zero any fractional pages
+ after the data section (i.e. bss). */
+static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot)
+{
+ uintptr_t host_start, host_map_start, host_end;
+
+ last_bss = TARGET_PAGE_ALIGN(last_bss);
+
+ /* ??? There is confusion between qemu_real_host_page_size and
+ qemu_host_page_size here and elsewhere in target_mmap, which
+ may lead to the end of the data section mapping from the file
+ not being mapped. At least there was an explicit test and
+ comment for that here, suggesting that "the file size must
+ be known". The comment probably pre-dates the introduction
+ of the fstat system call in target_mmap which does in fact
+ find out the size. What isn't clear is if the workaround
+ here is still actually needed. For now, continue with it,
+ but merge it with the "normal" mmap that would allocate the bss. */
+
+ host_start = (uintptr_t) g2h(elf_bss);
+ host_end = (uintptr_t) g2h(last_bss);
+ host_map_start = (host_start + qemu_real_host_page_size - 1);
+ host_map_start &= -qemu_real_host_page_size;
+
+ if (host_map_start < host_end) {
+ void *p = mmap((void *)host_map_start, host_end - host_map_start,
+ prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (p == MAP_FAILED) {
+ perror("cannot mmap brk");
+ exit(-1);
+ }
+
+ /* Since we didn't use target_mmap, make sure to record
+ the validity of the pages with qemu. */
+ page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot|PAGE_VALID);
+ }
+
+ if (host_start < host_map_start) {
+ memset((void *)host_start, 0, host_map_start - host_start);
+ }
+}
+
+static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
+ struct elfhdr *exec,
+ struct image_info *info,
+ struct image_info *interp_info)
+{
+ abi_ulong sp;
+ int size;
+ abi_ulong u_platform;
+ const char *k_platform;
+ const int n = sizeof(elf_addr_t);
+
+ sp = p;
+ u_platform = 0;
+ k_platform = ELF_PLATFORM;
+ if (k_platform) {
+ size_t len = strlen(k_platform) + 1;
+ sp -= (len + n - 1) & ~(n - 1);
+ u_platform = sp;
+ /* FIXME - check return value of memcpy_to_target() for failure */
+ memcpy_to_target(sp, k_platform, len);
+ }
+ /*
+ * Force 16 byte _final_ alignment here for generality.
+ */
+ sp = sp &~ (abi_ulong)15;
+ size = (DLINFO_ITEMS + 1) * 2;
+ if (k_platform)
+ size += 2;
+#ifdef DLINFO_ARCH_ITEMS
+ size += DLINFO_ARCH_ITEMS * 2;
+#endif
+ size += envc + argc + 2;
+ size += 1; /* argc itself */
+ size *= n;
+ if (size & 15)
+ sp -= 16 - (size & 15);
+
+ /* This is correct because Linux defines
+ * elf_addr_t as Elf32_Off / Elf64_Off
+ */
+#define NEW_AUX_ENT(id, val) do { \
+ sp -= n; put_user_ual(val, sp); \
+ sp -= n; put_user_ual(id, sp); \
+ } while(0)
+
+ NEW_AUX_ENT (AT_NULL, 0);
+
+ /* There must be exactly DLINFO_ITEMS entries here. */
+ NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff));
+ NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
+ NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
+ NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE));
+ NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0));
+ NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0);
+ NEW_AUX_ENT(AT_ENTRY, info->entry);
+ NEW_AUX_ENT(AT_UID, (abi_ulong) getuid());
+ NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid());
+ NEW_AUX_ENT(AT_GID, (abi_ulong) getgid());
+ NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid());
+ NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP);
+ NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK));
+ if (k_platform)
+ NEW_AUX_ENT(AT_PLATFORM, u_platform);
+#ifdef ARCH_DLINFO
+ /*
+ * ARCH_DLINFO must come last so platform specific code can enforce
+ * special alignment requirements on the AUXV if necessary (eg. PPC).
+ */
+ ARCH_DLINFO;
+#endif
+#undef NEW_AUX_ENT
+
+ info->saved_auxv = sp;
+
+ sp = loader_build_argptr(envc, argc, sp, p, 0);
+ return sp;
+}
+
+/* Load an ELF image into the address space.
+
+ IMAGE_NAME is the filename of the image, to use in error messages.
+ IMAGE_FD is the open file descriptor for the image.
+
+ BPRM_BUF is a copy of the beginning of the file; this of course
+ contains the elf file header at offset 0. It is assumed that this
+ buffer is sufficiently aligned to present no problems to the host
+ in accessing data at aligned offsets within the buffer.
+
+ On return: INFO values will be filled in, as necessary or available. */
+
+static void load_elf_image(const char *image_name, int image_fd,
+ struct image_info *info, char **pinterp_name,
+ char bprm_buf[BPRM_BUF_SIZE])
+{
+ struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
+ struct elf_phdr *phdr;
+ abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
+ int i, retval;
+ const char *errmsg;
+
+ /* First of all, some simple consistency checks */
+ errmsg = "Invalid ELF image for this architecture";
+ if (!elf_check_ident(ehdr)) {
+ goto exit_errmsg;
+ }
+ bswap_ehdr(ehdr);
+ if (!elf_check_ehdr(ehdr)) {
+ goto exit_errmsg;
+ }
+
+ i = ehdr->e_phnum * sizeof(struct elf_phdr);
+ if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) {
+ phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff);
+ } else {
+ phdr = (struct elf_phdr *) alloca(i);
+ retval = pread(image_fd, phdr, i, ehdr->e_phoff);
+ if (retval != i) {
+ goto exit_read;
+ }
+ }
+ bswap_phdr(phdr, ehdr->e_phnum);
+
+ /* Find the maximum size of the image and allocate an appropriate
+ amount of memory to handle that. */
+ loaddr = -1, hiaddr = 0;
+ for (i = 0; i < ehdr->e_phnum; ++i) {
+ if (phdr[i].p_type == PT_LOAD) {
+ abi_ulong a = phdr[i].p_vaddr;
+ if (a < loaddr) {
+ loaddr = a;
+ }
+ a += phdr[i].p_memsz;
+ if (a > hiaddr) {
+ hiaddr = a;
+ }
+ }
+ }
+
+ load_addr = loaddr;
+ if (ehdr->e_type == ET_DYN) {
+ /* The image indicates that it can be loaded anywhere. Find a
+ location that can hold the memory space required. If the
+ image is pre-linked, LOADDR will be non-zero. Since we do
+ not supply MAP_FIXED here we'll use that address if and
+ only if it remains available. */
+ load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+ -1, 0);
+ if (load_addr == -1) {
+ goto exit_perror;
+ }
+ } else if (pinterp_name != NULL) {
+ /* This is the main executable. Make sure that the low
+ address does not conflict with MMAP_MIN_ADDR or the
+ QEMU application itself. */
+#if defined(CONFIG_USE_GUEST_BASE)
+ /*
+ * In case where user has not explicitly set the guest_base, we
+ * probe here that should we set it automatically.
+ */
+ if (!have_guest_base && !reserved_va) {
+ unsigned long host_start, real_start, host_size;
+
+ /* Round addresses to page boundaries. */
+ loaddr &= qemu_host_page_mask;
+ hiaddr = HOST_PAGE_ALIGN(hiaddr);
+
+ if (loaddr < mmap_min_addr) {
+ host_start = HOST_PAGE_ALIGN(mmap_min_addr);
+ } else {
+ host_start = loaddr;
+ if (host_start != loaddr) {
+ errmsg = "Address overflow loading ELF binary";
+ goto exit_errmsg;
+ }
+ }
+ host_size = hiaddr - loaddr;
+ while (1) {
+ /* Do not use mmap_find_vma here because that is limited to the
+ guest address space. We are going to make the
+ guest address space fit whatever we're given. */
+ real_start = (unsigned long)
+ mmap((void *)host_start, host_size, PROT_NONE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
+ if (real_start == (unsigned long)-1) {
+ goto exit_perror;
+ }
+ if (real_start == host_start) {
+ break;
+ }
+ /* That address didn't work. Unmap and try a different one.
+ The address the host picked because is typically right at
+ the top of the host address space and leaves the guest with
+ no usable address space. Resort to a linear search. We
+ already compensated for mmap_min_addr, so this should not
+ happen often. Probably means we got unlucky and host
+ address space randomization put a shared library somewhere
+ inconvenient. */
+ munmap((void *)real_start, host_size);
+ host_start += qemu_host_page_size;
+ if (host_start == loaddr) {
+ /* Theoretically possible if host doesn't have any suitably
+ aligned areas. Normally the first mmap will fail. */
+ errmsg = "Unable to find space for application";
+ goto exit_errmsg;
+ }
+ }
+ qemu_log("Relocating guest address space from 0x"
+ TARGET_ABI_FMT_lx " to 0x%lx\n", loaddr, real_start);
+ guest_base = real_start - loaddr;
+ }
+#endif
+ }
+ load_bias = load_addr - loaddr;
+
+ info->load_bias = load_bias;
+ info->load_addr = load_addr;
+ info->entry = ehdr->e_entry + load_bias;
+ info->start_code = -1;
+ info->end_code = 0;
+ info->start_data = -1;
+ info->end_data = 0;
+ info->brk = 0;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ struct elf_phdr *eppnt = phdr + i;
+ if (eppnt->p_type == PT_LOAD) {
+ abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em;
+ int elf_prot = 0;
+
+ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
+ if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+
+ vaddr = load_bias + eppnt->p_vaddr;
+ vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr);
+ vaddr_ps = TARGET_ELF_PAGESTART(vaddr);
+
+ error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po,
+ elf_prot, MAP_PRIVATE | MAP_FIXED,
+ image_fd, eppnt->p_offset - vaddr_po);
+ if (error == -1) {
+ goto exit_perror;
+ }
+
+ vaddr_ef = vaddr + eppnt->p_filesz;
+ vaddr_em = vaddr + eppnt->p_memsz;
+
+ /* If the load segment requests extra zeros (e.g. bss), map it. */
+ if (vaddr_ef < vaddr_em) {
+ zero_bss(vaddr_ef, vaddr_em, elf_prot);
+ }
+
+ /* Find the full program boundaries. */
+ if (elf_prot & PROT_EXEC) {
+ if (vaddr < info->start_code) {
+ info->start_code = vaddr;
+ }
+ if (vaddr_ef > info->end_code) {
+ info->end_code = vaddr_ef;
+ }
+ }
+ if (elf_prot & PROT_WRITE) {
+ if (vaddr < info->start_data) {
+ info->start_data = vaddr;
+ }
+ if (vaddr_ef > info->end_data) {
+ info->end_data = vaddr_ef;
+ }
+ if (vaddr_em > info->brk) {
+ info->brk = vaddr_em;
+ }
+ }
+ } else if (eppnt->p_type == PT_INTERP && pinterp_name) {
+ char *interp_name;
+
+ if (*pinterp_name) {
+ errmsg = "Multiple PT_INTERP entries";
+ goto exit_errmsg;
+ }
+ interp_name = malloc(eppnt->p_filesz);
+ if (!interp_name) {
+ goto exit_perror;
+ }
+
+ if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
+ memcpy(interp_name, bprm_buf + eppnt->p_offset,
+ eppnt->p_filesz);
+ } else {
+ retval = pread(image_fd, interp_name, eppnt->p_filesz,
+ eppnt->p_offset);
+ if (retval != eppnt->p_filesz) {
+ goto exit_perror;
+ }
+ }
+ if (interp_name[eppnt->p_filesz - 1] != 0) {
+ errmsg = "Invalid PT_INTERP entry";
+ goto exit_errmsg;
+ }
+ *pinterp_name = interp_name;
+ }
+ }
+
+ if (info->end_data == 0) {
+ info->start_data = info->end_code;
+ info->end_data = info->end_code;
+ info->brk = info->end_code;
+ }
+
+ if (qemu_log_enabled()) {
+ load_symbols(ehdr, image_fd, load_bias);
+ }
+
+ close(image_fd);
+ return;
+
+ exit_read:
+ if (retval >= 0) {
+ errmsg = "Incomplete read of file header";
+ goto exit_errmsg;
+ }
+ exit_perror:
+ errmsg = strerror(errno);
+ exit_errmsg:
+ fprintf(stderr, "%s: %s\n", image_name, errmsg);
+ exit(-1);
+}
+
+static void load_elf_interp(const char *filename, struct image_info *info,
+ char bprm_buf[BPRM_BUF_SIZE])
+{
+ int fd, retval;
+
+ fd = open(path(filename), O_RDONLY);
+ if (fd < 0) {
+ goto exit_perror;
+ }
+
+ retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
+ if (retval < 0) {
+ goto exit_perror;
+ }
+ if (retval < BPRM_BUF_SIZE) {
+ memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
+ }
+
+ load_elf_image(filename, fd, info, NULL, bprm_buf);
+ return;
+
+ exit_perror:
+ fprintf(stderr, "%s: %s\n", filename, strerror(errno));
+ exit(-1);
+}
+
+static int symfind(const void *s0, const void *s1)
+{
+ struct elf_sym *key = (struct elf_sym *)s0;
+ struct elf_sym *sym = (struct elf_sym *)s1;
+ int result = 0;
+ if (key->st_value < sym->st_value) {
+ result = -1;
+ } else if (key->st_value >= sym->st_value + sym->st_size) {
+ result = 1;
+ }
+ return result;
+}
+
+static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr)
+{
+#if ELF_CLASS == ELFCLASS32
+ struct elf_sym *syms = s->disas_symtab.elf32;
+#else
+ struct elf_sym *syms = s->disas_symtab.elf64;
+#endif
+
+ // binary search
+ struct elf_sym key;
+ struct elf_sym *sym;
+
+ key.st_value = orig_addr;
+
+ sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind);
+ if (sym != NULL) {
+ return s->disas_strtab + sym->st_name;
+ }
+
+ return "";
+}
+
+/* FIXME: This should use elf_ops.h */
+static int symcmp(const void *s0, const void *s1)
+{
+ struct elf_sym *sym0 = (struct elf_sym *)s0;
+ struct elf_sym *sym1 = (struct elf_sym *)s1;
+ return (sym0->st_value < sym1->st_value)
+ ? -1
+ : ((sym0->st_value > sym1->st_value) ? 1 : 0);
+}
+
+/* Best attempt to load symbols from this ELF object. */
+static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
+{
+ int i, shnum, nsyms, sym_idx = 0, str_idx = 0;
+ struct elf_shdr *shdr;
+ char *strings;
+ struct syminfo *s;
+ struct elf_sym *syms, *new_syms;
+
+ shnum = hdr->e_shnum;
+ i = shnum * sizeof(struct elf_shdr);
+ shdr = (struct elf_shdr *)alloca(i);
+ if (pread(fd, shdr, i, hdr->e_shoff) != i) {
+ return;
+ }
+
+ bswap_shdr(shdr, shnum);
+ for (i = 0; i < shnum; ++i) {
+ if (shdr[i].sh_type == SHT_SYMTAB) {
+ sym_idx = i;
+ str_idx = shdr[i].sh_link;
+ goto found;
+ }
+ }
+
+ /* There will be no symbol table if the file was stripped. */
+ return;
+
+ found:
+ /* Now know where the strtab and symtab are. Snarf them. */
+ s = malloc(sizeof(*s));
+ if (!s) {
+ return;
+ }
+
+ i = shdr[str_idx].sh_size;
+ s->disas_strtab = strings = malloc(i);
+ if (!strings || pread(fd, strings, i, shdr[str_idx].sh_offset) != i) {
+ free(s);
+ free(strings);
+ return;
+ }
+
+ i = shdr[sym_idx].sh_size;
+ syms = malloc(i);
+ if (!syms || pread(fd, syms, i, shdr[sym_idx].sh_offset) != i) {
+ free(s);
+ free(strings);
+ free(syms);
+ return;
+ }
+
+ nsyms = i / sizeof(struct elf_sym);
+ for (i = 0; i < nsyms; ) {
+ bswap_sym(syms + i);
+ /* Throw away entries which we do not need. */
+ if (syms[i].st_shndx == SHN_UNDEF
+ || syms[i].st_shndx >= SHN_LORESERVE
+ || ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) {
+ if (i < --nsyms) {
+ syms[i] = syms[nsyms];
+ }
+ } else {
+#if defined(TARGET_ARM) || defined (TARGET_MIPS)
+ /* The bottom address bit marks a Thumb or MIPS16 symbol. */
+ syms[i].st_value &= ~(target_ulong)1;
+#endif
+ syms[i].st_value += load_bias;
+ i++;
+ }
+ }
+
+ /* Attempt to free the storage associated with the local symbols
+ that we threw away. Whether or not this has any effect on the
+ memory allocation depends on the malloc implementation and how
+ many symbols we managed to discard. */
+ new_syms = realloc(syms, nsyms * sizeof(*syms));
+ if (new_syms == NULL) {
+ free(s);
+ free(syms);
+ free(strings);
+ return;
+ }
+ syms = new_syms;
+
+ qsort(syms, nsyms, sizeof(*syms), symcmp);
+
+ s->disas_num_syms = nsyms;
+#if ELF_CLASS == ELFCLASS32
+ s->disas_symtab.elf32 = syms;
+#else
+ s->disas_symtab.elf64 = syms;
+#endif
+ s->lookup_symbol = lookup_symbolxx;
+ s->next = syminfos;
+ syminfos = s;
+}
+
+int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info)
+{
+ struct image_info interp_info;
+ struct elfhdr elf_ex;
+ char *elf_interpreter = NULL;
+
+ info->start_mmap = (abi_ulong)ELF_START_MMAP;
+ info->mmap = 0;
+ info->rss = 0;
+
+ load_elf_image(bprm->filename, bprm->fd, info,
+ &elf_interpreter, bprm->buf);
+
+ /* ??? We need a copy of the elf header for passing to create_elf_tables.
+ If we do nothing, we'll have overwritten this when we re-use bprm->buf
+ when we load the interpreter. */
+ elf_ex = *(struct elfhdr *)bprm->buf;
+
+ bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);
+ bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);
+ bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);
+ if (!bprm->p) {
+ fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG));
+ exit(-1);
+ }
+
+ /* Do this so that we can load the interpreter, if need be. We will
+ change some of these later */
+ bprm->p = setup_arg_pages(bprm->p, bprm, info);
+
+ if (elf_interpreter) {
+ load_elf_interp(elf_interpreter, &interp_info, bprm->buf);
+
+ /* If the program interpreter is one of these two, then assume
+ an iBCS2 image. Otherwise assume a native linux image. */
+
+ if (strcmp(elf_interpreter, "/usr/lib/libc.so.1") == 0
+ || strcmp(elf_interpreter, "/usr/lib/ld.so.1") == 0) {
+ info->personality = PER_SVR4;
+
+ /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
+ and some applications "depend" upon this behavior. Since
+ we do not have the power to recompile these, we emulate
+ the SVr4 behavior. Sigh. */
+ target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, -1, 0);
+ }
+ }
+
+ bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
+ info, (elf_interpreter ? &interp_info : NULL));
+ info->start_stack = bprm->p;
+
+ /* If we have an interpreter, set that as the program's entry point.
+ Copy the load_addr as well, to help PPC64 interpret the entry
+ point as a function descriptor. Do this after creating elf tables
+ so that we copy the original program entry point into the AUXV. */
+ if (elf_interpreter) {
+ info->load_addr = interp_info.load_addr;
+ info->entry = interp_info.entry;
+ free(elf_interpreter);
+ }
+
+#ifdef USE_ELF_CORE_DUMP
+ bprm->core_dump = &elf_core_dump;
+#endif
+
+ return 0;
+}
+
+#ifdef USE_ELF_CORE_DUMP
+/*
+ * Definitions to generate Intel SVR4-like core files.
+ * These mostly have the same names as the SVR4 types with "target_elf_"
+ * tacked on the front to prevent clashes with linux definitions,
+ * and the typedef forms have been avoided. This is mostly like
+ * the SVR4 structure, but more Linuxy, with things that Linux does
+ * not support and which gdb doesn't really use excluded.
+ *
+ * Fields we don't dump (their contents is zero) in linux-user qemu
+ * are marked with XXX.
+ *
+ * Core dump code is copied from linux kernel (fs/binfmt_elf.c).
+ *
+ * Porting ELF coredump for target is (quite) simple process. First you
+ * define USE_ELF_CORE_DUMP in target ELF code (where init_thread() for
+ * the target resides):
+ *
+ * #define USE_ELF_CORE_DUMP
+ *
+ * Next you define type of register set used for dumping. ELF specification
+ * says that it needs to be array of elf_greg_t that has size of ELF_NREG.
+ *
+ * typedef <target_regtype> target_elf_greg_t;
+ * #define ELF_NREG <number of registers>
+ * typedef taret_elf_greg_t target_elf_gregset_t[ELF_NREG];
+ *
+ * Last step is to implement target specific function that copies registers
+ * from given cpu into just specified register set. Prototype is:
+ *
+ * static void elf_core_copy_regs(taret_elf_gregset_t *regs,
+ * const CPUState *env);
+ *
+ * Parameters:
+ * regs - copy register values into here (allocated and zeroed by caller)
+ * env - copy registers from here
+ *
+ * Example for ARM target is provided in this file.
+ */
+
+/* An ELF note in memory */
+struct memelfnote {
+ const char *name;
+ size_t namesz;
+ size_t namesz_rounded;
+ int type;
+ size_t datasz;
+ void *data;
+ size_t notesz;
+};
+
+struct target_elf_siginfo {
+ int si_signo; /* signal number */
+ int si_code; /* extra code */
+ int si_errno; /* errno */
+};
+
+struct target_elf_prstatus {
+ struct target_elf_siginfo pr_info; /* Info associated with signal */
+ short pr_cursig; /* Current signal */
+ target_ulong pr_sigpend; /* XXX */
+ target_ulong pr_sighold; /* XXX */
+ target_pid_t pr_pid;
+ target_pid_t pr_ppid;
+ target_pid_t pr_pgrp;
+ target_pid_t pr_sid;
+ struct target_timeval pr_utime; /* XXX User time */
+ struct target_timeval pr_stime; /* XXX System time */
+ struct target_timeval pr_cutime; /* XXX Cumulative user time */
+ struct target_timeval pr_cstime; /* XXX Cumulative system time */
+ target_elf_gregset_t pr_reg; /* GP registers */
+ int pr_fpvalid; /* XXX */
+};
+
+#define ELF_PRARGSZ (80) /* Number of chars for args */
+
+struct target_elf_prpsinfo {
+ char pr_state; /* numeric process state */
+ char pr_sname; /* char for pr_state */
+ char pr_zomb; /* zombie */
+ char pr_nice; /* nice val */
+ target_ulong pr_flag; /* flags */
+ target_uid_t pr_uid;
+ target_gid_t pr_gid;
+ target_pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid;
+ /* Lots missing */
+ char pr_fname[16]; /* filename of executable */
+ char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
+};
+
+/* Here is the structure in which status of each thread is captured. */
+struct elf_thread_status {
+ QTAILQ_ENTRY(elf_thread_status) ets_link;
+ struct target_elf_prstatus prstatus; /* NT_PRSTATUS */
+#if 0
+ elf_fpregset_t fpu; /* NT_PRFPREG */
+ struct task_struct *thread;
+ elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */
+#endif
+ struct memelfnote notes[1];
+ int num_notes;
+};
+
+struct elf_note_info {
+ struct memelfnote *notes;
+ struct target_elf_prstatus *prstatus; /* NT_PRSTATUS */
+ struct target_elf_prpsinfo *psinfo; /* NT_PRPSINFO */
+
+ QTAILQ_HEAD(thread_list_head, elf_thread_status) thread_list;
+#if 0
+ /*
+ * Current version of ELF coredump doesn't support
+ * dumping fp regs etc.
+ */
+ elf_fpregset_t *fpu;
+ elf_fpxregset_t *xfpu;
+ int thread_status_size;
+#endif
+ int notes_size;
+ int numnote;
+};
+
+struct vm_area_struct {
+ abi_ulong vma_start; /* start vaddr of memory region */
+ abi_ulong vma_end; /* end vaddr of memory region */
+ abi_ulong vma_flags; /* protection etc. flags for the region */
+ QTAILQ_ENTRY(vm_area_struct) vma_link;
+};
+
+struct mm_struct {
+ QTAILQ_HEAD(, vm_area_struct) mm_mmap;
+ int mm_count; /* number of mappings */
+};
+
+static struct mm_struct *vma_init(void);
+static void vma_delete(struct mm_struct *);
+static int vma_add_mapping(struct mm_struct *, abi_ulong,
+ abi_ulong, abi_ulong);
+static int vma_get_mapping_count(const struct mm_struct *);
+static struct vm_area_struct *vma_first(const struct mm_struct *);
+static struct vm_area_struct *vma_next(struct vm_area_struct *);
+static abi_ulong vma_dump_size(const struct vm_area_struct *);
+static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
+ unsigned long flags);
+
+static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t);
+static void fill_note(struct memelfnote *, const char *, int,
+ unsigned int, void *);
+static void fill_prstatus(struct target_elf_prstatus *, const TaskState *, int);
+static int fill_psinfo(struct target_elf_prpsinfo *, const TaskState *);
+static void fill_auxv_note(struct memelfnote *, const TaskState *);
+static void fill_elf_note_phdr(struct elf_phdr *, int, off_t);
+static size_t note_size(const struct memelfnote *);
+static void free_note_info(struct elf_note_info *);
+static int fill_note_info(struct elf_note_info *, long, const CPUState *);
+static void fill_thread_info(struct elf_note_info *, const CPUState *);
+static int core_dump_filename(const TaskState *, char *, size_t);
+
+static int dump_write(int, const void *, size_t);
+static int write_note(struct memelfnote *, int);
+static int write_note_info(struct elf_note_info *, int);
+
+#ifdef BSWAP_NEEDED
+static void bswap_prstatus(struct target_elf_prstatus *prstatus)
+{
+ prstatus->pr_info.si_signo = tswapl(prstatus->pr_info.si_signo);
+ prstatus->pr_info.si_code = tswapl(prstatus->pr_info.si_code);
+ prstatus->pr_info.si_errno = tswapl(prstatus->pr_info.si_errno);
+ prstatus->pr_cursig = tswap16(prstatus->pr_cursig);
+ prstatus->pr_sigpend = tswapl(prstatus->pr_sigpend);
+ prstatus->pr_sighold = tswapl(prstatus->pr_sighold);
+ prstatus->pr_pid = tswap32(prstatus->pr_pid);
+ prstatus->pr_ppid = tswap32(prstatus->pr_ppid);
+ prstatus->pr_pgrp = tswap32(prstatus->pr_pgrp);
+ prstatus->pr_sid = tswap32(prstatus->pr_sid);
+ /* cpu times are not filled, so we skip them */
+ /* regs should be in correct format already */
+ prstatus->pr_fpvalid = tswap32(prstatus->pr_fpvalid);
+}
+
+static void bswap_psinfo(struct target_elf_prpsinfo *psinfo)
+{
+ psinfo->pr_flag = tswapl(psinfo->pr_flag);
+ psinfo->pr_uid = tswap16(psinfo->pr_uid);
+ psinfo->pr_gid = tswap16(psinfo->pr_gid);
+ psinfo->pr_pid = tswap32(psinfo->pr_pid);
+ psinfo->pr_ppid = tswap32(psinfo->pr_ppid);
+ psinfo->pr_pgrp = tswap32(psinfo->pr_pgrp);
+ psinfo->pr_sid = tswap32(psinfo->pr_sid);
+}
+
+static void bswap_note(struct elf_note *en)
+{
+ bswap32s(&en->n_namesz);
+ bswap32s(&en->n_descsz);
+ bswap32s(&en->n_type);
+}
+#else
+static inline void bswap_prstatus(struct target_elf_prstatus *p) { }
+static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {}
+static inline void bswap_note(struct elf_note *en) { }
+#endif /* BSWAP_NEEDED */
+
+/*
+ * Minimal support for linux memory regions. These are needed
+ * when we are finding out what memory exactly belongs to
+ * emulated process. No locks needed here, as long as
+ * thread that received the signal is stopped.
+ */
+
+static struct mm_struct *vma_init(void)
+{
+ struct mm_struct *mm;
+
+ if ((mm = qemu_malloc(sizeof (*mm))) == NULL)
+ return (NULL);
+
+ mm->mm_count = 0;
+ QTAILQ_INIT(&mm->mm_mmap);
+
+ return (mm);
+}
+
+static void vma_delete(struct mm_struct *mm)
+{
+ struct vm_area_struct *vma;
+
+ while ((vma = vma_first(mm)) != NULL) {
+ QTAILQ_REMOVE(&mm->mm_mmap, vma, vma_link);
+ qemu_free(vma);
+ }
+ qemu_free(mm);
+}
+
+static int vma_add_mapping(struct mm_struct *mm, abi_ulong start,
+ abi_ulong end, abi_ulong flags)
+{
+ struct vm_area_struct *vma;
+
+ if ((vma = qemu_mallocz(sizeof (*vma))) == NULL)
+ return (-1);
+
+ vma->vma_start = start;
+ vma->vma_end = end;
+ vma->vma_flags = flags;
+
+ QTAILQ_INSERT_TAIL(&mm->mm_mmap, vma, vma_link);
+ mm->mm_count++;
+
+ return (0);
+}
+
+static struct vm_area_struct *vma_first(const struct mm_struct *mm)
+{
+ return (QTAILQ_FIRST(&mm->mm_mmap));
+}
+
+static struct vm_area_struct *vma_next(struct vm_area_struct *vma)
+{
+ return (QTAILQ_NEXT(vma, vma_link));
+}
+
+static int vma_get_mapping_count(const struct mm_struct *mm)
+{
+ return (mm->mm_count);
+}
+
+/*
+ * Calculate file (dump) size of given memory region.
+ */
+static abi_ulong vma_dump_size(const struct vm_area_struct *vma)
+{
+ /* if we cannot even read the first page, skip it */
+ if (!access_ok(VERIFY_READ, vma->vma_start, TARGET_PAGE_SIZE))
+ return (0);
+
+ /*
+ * Usually we don't dump executable pages as they contain
+ * non-writable code that debugger can read directly from
+ * target library etc. However, thread stacks are marked
+ * also executable so we read in first page of given region
+ * and check whether it contains elf header. If there is
+ * no elf header, we dump it.
+ */
+ if (vma->vma_flags & PROT_EXEC) {
+ char page[TARGET_PAGE_SIZE];
+
+ copy_from_user(page, vma->vma_start, sizeof (page));
+ if ((page[EI_MAG0] == ELFMAG0) &&
+ (page[EI_MAG1] == ELFMAG1) &&
+ (page[EI_MAG2] == ELFMAG2) &&
+ (page[EI_MAG3] == ELFMAG3)) {
+ /*
+ * Mappings are possibly from ELF binary. Don't dump
+ * them.
+ */
+ return (0);
+ }
+ }
+
+ return (vma->vma_end - vma->vma_start);
+}
+
+static int vma_walker(void *priv, abi_ulong start, abi_ulong end,
+ unsigned long flags)
+{
+ struct mm_struct *mm = (struct mm_struct *)priv;
+
+ vma_add_mapping(mm, start, end, flags);
+ return (0);
+}
+
+static void fill_note(struct memelfnote *note, const char *name, int type,
+ unsigned int sz, void *data)
+{
+ unsigned int namesz;
+
+ namesz = strlen(name) + 1;
+ note->name = name;
+ note->namesz = namesz;
+ note->namesz_rounded = roundup(namesz, sizeof (int32_t));
+ note->type = type;
+ note->datasz = roundup(sz, sizeof (int32_t));;
+ note->data = data;
+
+ /*
+ * We calculate rounded up note size here as specified by
+ * ELF document.
+ */
+ note->notesz = sizeof (struct elf_note) +
+ note->namesz_rounded + note->datasz;
+}
+
+static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
+ uint32_t flags)
+{
+ (void) memset(elf, 0, sizeof(*elf));
+
+ (void) memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELF_CLASS;
+ elf->e_ident[EI_DATA] = ELF_DATA;
+ elf->e_ident[EI_VERSION] = EV_CURRENT;
+ elf->e_ident[EI_OSABI] = ELF_OSABI;
+
+ elf->e_type = ET_CORE;
+ elf->e_machine = machine;
+ elf->e_version = EV_CURRENT;
+ elf->e_phoff = sizeof(struct elfhdr);
+ elf->e_flags = flags;
+ elf->e_ehsize = sizeof(struct elfhdr);
+ elf->e_phentsize = sizeof(struct elf_phdr);
+ elf->e_phnum = segs;
+
+ bswap_ehdr(elf);
+}
+
+static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
+{
+ phdr->p_type = PT_NOTE;
+ phdr->p_offset = offset;
+ phdr->p_vaddr = 0;
+ phdr->p_paddr = 0;
+ phdr->p_filesz = sz;
+ phdr->p_memsz = 0;
+ phdr->p_flags = 0;
+ phdr->p_align = 0;
+
+ bswap_phdr(phdr, 1);
+}
+
+static size_t note_size(const struct memelfnote *note)
+{
+ return (note->notesz);
+}
+
+static void fill_prstatus(struct target_elf_prstatus *prstatus,
+ const TaskState *ts, int signr)
+{
+ (void) memset(prstatus, 0, sizeof (*prstatus));
+ prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
+ prstatus->pr_pid = ts->ts_tid;
+ prstatus->pr_ppid = getppid();
+ prstatus->pr_pgrp = getpgrp();
+ prstatus->pr_sid = getsid(0);
+
+ bswap_prstatus(prstatus);
+}
+
+static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts)
+{
+ char *filename, *base_filename;
+ unsigned int i, len;
+
+ (void) memset(psinfo, 0, sizeof (*psinfo));
+
+ len = ts->info->arg_end - ts->info->arg_start;
+ if (len >= ELF_PRARGSZ)
+ len = ELF_PRARGSZ - 1;
+ if (copy_from_user(&psinfo->pr_psargs, ts->info->arg_start, len))
+ return -EFAULT;
+ for (i = 0; i < len; i++)
+ if (psinfo->pr_psargs[i] == 0)
+ psinfo->pr_psargs[i] = ' ';
+ psinfo->pr_psargs[len] = 0;
+
+ psinfo->pr_pid = getpid();
+ psinfo->pr_ppid = getppid();
+ psinfo->pr_pgrp = getpgrp();
+ psinfo->pr_sid = getsid(0);
+ psinfo->pr_uid = getuid();
+ psinfo->pr_gid = getgid();
+
+ filename = strdup(ts->bprm->filename);
+ base_filename = strdup(basename(filename));
+ (void) strncpy(psinfo->pr_fname, base_filename,
+ sizeof(psinfo->pr_fname));
+ free(base_filename);
+ free(filename);
+
+ bswap_psinfo(psinfo);
+ return (0);
+}
+
+static void fill_auxv_note(struct memelfnote *note, const TaskState *ts)
+{
+ elf_addr_t auxv = (elf_addr_t)ts->info->saved_auxv;
+ elf_addr_t orig_auxv = auxv;
+ abi_ulong val;
+ void *ptr;
+ int i, len;
+
+ /*
+ * Auxiliary vector is stored in target process stack. It contains
+ * {type, value} pairs that we need to dump into note. This is not
+ * strictly necessary but we do it here for sake of completeness.
+ */
+
+ /* find out lenght of the vector, AT_NULL is terminator */
+ i = len = 0;
+ do {
+ get_user_ual(val, auxv);
+ i += 2;
+ auxv += 2 * sizeof (elf_addr_t);
+ } while (val != AT_NULL);
+ len = i * sizeof (elf_addr_t);
+
+ /* read in whole auxv vector and copy it to memelfnote */
+ ptr = lock_user(VERIFY_READ, orig_auxv, len, 0);
+ if (ptr != NULL) {
+ fill_note(note, "CORE", NT_AUXV, len, ptr);
+ unlock_user(ptr, auxv, len);
+ }
+}
+
+/*
+ * Constructs name of coredump file. We have following convention
+ * for the name:
+ * qemu_<basename-of-target-binary>_<date>-<time>_<pid>.core
+ *
+ * Returns 0 in case of success, -1 otherwise (errno is set).
+ */
+static int core_dump_filename(const TaskState *ts, char *buf,
+ size_t bufsize)
+{
+ char timestamp[64];
+ char *filename = NULL;
+ char *base_filename = NULL;
+ struct timeval tv;
+ struct tm tm;
+
+ assert(bufsize >= PATH_MAX);
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ (void) fprintf(stderr, "unable to get current timestamp: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ filename = strdup(ts->bprm->filename);
+ base_filename = strdup(basename(filename));
+ (void) strftime(timestamp, sizeof (timestamp), "%Y%m%d-%H%M%S",
+ localtime_r(&tv.tv_sec, &tm));
+ (void) snprintf(buf, bufsize, "qemu_%s_%s_%d.core",
+ base_filename, timestamp, (int)getpid());
+ free(base_filename);
+ free(filename);
+
+ return (0);
+}
+
+static int dump_write(int fd, const void *ptr, size_t size)
+{
+ const char *bufp = (const char *)ptr;
+ ssize_t bytes_written, bytes_left;
+ struct rlimit dumpsize;
+ off_t pos;
+
+ bytes_written = 0;
+ getrlimit(RLIMIT_CORE, &dumpsize);
+ if ((pos = lseek(fd, 0, SEEK_CUR))==-1) {
+ if (errno == ESPIPE) { /* not a seekable stream */
+ bytes_left = size;
+ } else {
+ return pos;
+ }
+ } else {
+ if (dumpsize.rlim_cur <= pos) {
+ return -1;
+ } else if (dumpsize.rlim_cur == RLIM_INFINITY) {
+ bytes_left = size;
+ } else {
+ size_t limit_left=dumpsize.rlim_cur - pos;
+ bytes_left = limit_left >= size ? size : limit_left ;
+ }
+ }
+
+ /*
+ * In normal conditions, single write(2) should do but
+ * in case of socket etc. this mechanism is more portable.
+ */
+ do {
+ bytes_written = write(fd, bufp, bytes_left);
+ if (bytes_written < 0) {
+ if (errno == EINTR)
+ continue;
+ return (-1);
+ } else if (bytes_written == 0) { /* eof */
+ return (-1);
+ }
+ bufp += bytes_written;
+ bytes_left -= bytes_written;
+ } while (bytes_left > 0);
+
+ return (0);
+}
+
+static int write_note(struct memelfnote *men, int fd)
+{
+ struct elf_note en;
+
+ en.n_namesz = men->namesz;
+ en.n_type = men->type;
+ en.n_descsz = men->datasz;
+
+ bswap_note(&en);
+
+ if (dump_write(fd, &en, sizeof(en)) != 0)
+ return (-1);
+ if (dump_write(fd, men->name, men->namesz_rounded) != 0)
+ return (-1);
+ if (dump_write(fd, men->data, men->datasz) != 0)
+ return (-1);
+
+ return (0);
+}
+
+static void fill_thread_info(struct elf_note_info *info, const CPUState *env)
+{
+ TaskState *ts = (TaskState *)env->opaque;
+ struct elf_thread_status *ets;
+
+ ets = qemu_mallocz(sizeof (*ets));
+ ets->num_notes = 1; /* only prstatus is dumped */
+ fill_prstatus(&ets->prstatus, ts, 0);
+ elf_core_copy_regs(&ets->prstatus.pr_reg, env);
+ fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus),
+ &ets->prstatus);
+
+ QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link);
+
+ info->notes_size += note_size(&ets->notes[0]);
+}
+
+static int fill_note_info(struct elf_note_info *info,
+ long signr, const CPUState *env)
+{
+#define NUMNOTES 3
+ CPUState *cpu = NULL;
+ TaskState *ts = (TaskState *)env->opaque;
+ int i;
+
+ (void) memset(info, 0, sizeof (*info));
+
+ QTAILQ_INIT(&info->thread_list);
+
+ info->notes = qemu_mallocz(NUMNOTES * sizeof (struct memelfnote));
+ if (info->notes == NULL)
+ return (-ENOMEM);
+ info->prstatus = qemu_mallocz(sizeof (*info->prstatus));
+ if (info->prstatus == NULL)
+ return (-ENOMEM);
+ info->psinfo = qemu_mallocz(sizeof (*info->psinfo));
+ if (info->prstatus == NULL)
+ return (-ENOMEM);
+
+ /*
+ * First fill in status (and registers) of current thread
+ * including process info & aux vector.
+ */
+ fill_prstatus(info->prstatus, ts, signr);
+ elf_core_copy_regs(&info->prstatus->pr_reg, env);
+ fill_note(&info->notes[0], "CORE", NT_PRSTATUS,
+ sizeof (*info->prstatus), info->prstatus);
+ fill_psinfo(info->psinfo, ts);
+ fill_note(&info->notes[1], "CORE", NT_PRPSINFO,
+ sizeof (*info->psinfo), info->psinfo);
+ fill_auxv_note(&info->notes[2], ts);
+ info->numnote = 3;
+
+ info->notes_size = 0;
+ for (i = 0; i < info->numnote; i++)
+ info->notes_size += note_size(&info->notes[i]);
+
+ /* read and fill status of all threads */
+ cpu_list_lock();
+ for (cpu = first_cpu; cpu != NULL; cpu = cpu->next_cpu) {
+ if (cpu == thread_env)
+ continue;
+ fill_thread_info(info, cpu);
+ }
+ cpu_list_unlock();
+
+ return (0);
+}
+
+static void free_note_info(struct elf_note_info *info)
+{
+ struct elf_thread_status *ets;
+
+ while (!QTAILQ_EMPTY(&info->thread_list)) {
+ ets = QTAILQ_FIRST(&info->thread_list);
+ QTAILQ_REMOVE(&info->thread_list, ets, ets_link);
+ qemu_free(ets);
+ }
+
+ qemu_free(info->prstatus);
+ qemu_free(info->psinfo);
+ qemu_free(info->notes);
+}
+
+static int write_note_info(struct elf_note_info *info, int fd)
+{
+ struct elf_thread_status *ets;
+ int i, error = 0;
+
+ /* write prstatus, psinfo and auxv for current thread */
+ for (i = 0; i < info->numnote; i++)
+ if ((error = write_note(&info->notes[i], fd)) != 0)
+ return (error);
+
+ /* write prstatus for each thread */
+ for (ets = info->thread_list.tqh_first; ets != NULL;
+ ets = ets->ets_link.tqe_next) {
+ if ((error = write_note(&ets->notes[0], fd)) != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+/*
+ * Write out ELF coredump.
+ *
+ * See documentation of ELF object file format in:
+ * http://www.caldera.com/developers/devspecs/gabi41.pdf
+ *
+ * Coredump format in linux is following:
+ *
+ * 0 +----------------------+ \
+ * | ELF header | ET_CORE |
+ * +----------------------+ |
+ * | ELF program headers | |--- headers
+ * | - NOTE section | |
+ * | - PT_LOAD sections | |
+ * +----------------------+ /
+ * | NOTEs: |
+ * | - NT_PRSTATUS |
+ * | - NT_PRSINFO |
+ * | - NT_AUXV |
+ * +----------------------+ <-- aligned to target page
+ * | Process memory dump |
+ * : :
+ * . .
+ * : :
+ * | |
+ * +----------------------+
+ *
+ * NT_PRSTATUS -> struct elf_prstatus (per thread)
+ * NT_PRSINFO -> struct elf_prpsinfo
+ * NT_AUXV is array of { type, value } pairs (see fill_auxv_note()).
+ *
+ * Format follows System V format as close as possible. Current
+ * version limitations are as follows:
+ * - no floating point registers are dumped
+ *
+ * Function returns 0 in case of success, negative errno otherwise.
+ *
+ * TODO: make this work also during runtime: it should be
+ * possible to force coredump from running process and then
+ * continue processing. For example qemu could set up SIGUSR2
+ * handler (provided that target process haven't registered
+ * handler for that) that does the dump when signal is received.
+ */
+static int elf_core_dump(int signr, const CPUState *env)
+{
+ const TaskState *ts = (const TaskState *)env->opaque;
+ struct vm_area_struct *vma = NULL;
+ char corefile[PATH_MAX];
+ struct elf_note_info info;
+ struct elfhdr elf;
+ struct elf_phdr phdr;
+ struct rlimit dumpsize;
+ struct mm_struct *mm = NULL;
+ off_t offset = 0, data_offset = 0;
+ int segs = 0;
+ int fd = -1;
+
+ errno = 0;
+ getrlimit(RLIMIT_CORE, &dumpsize);
+ if (dumpsize.rlim_cur == 0)
+ return 0;
+
+ if (core_dump_filename(ts, corefile, sizeof (corefile)) < 0)
+ return (-errno);
+
+ if ((fd = open(corefile, O_WRONLY | O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
+ return (-errno);
+
+ /*
+ * Walk through target process memory mappings and
+ * set up structure containing this information. After
+ * this point vma_xxx functions can be used.
+ */
+ if ((mm = vma_init()) == NULL)
+ goto out;
+
+ walk_memory_regions(mm, vma_walker);
+ segs = vma_get_mapping_count(mm);
+
+ /*
+ * Construct valid coredump ELF header. We also
+ * add one more segment for notes.
+ */
+ fill_elf_header(&elf, segs + 1, ELF_MACHINE, 0);
+ if (dump_write(fd, &elf, sizeof (elf)) != 0)
+ goto out;
+
+ /* fill in in-memory version of notes */
+ if (fill_note_info(&info, signr, env) < 0)
+ goto out;
+
+ offset += sizeof (elf); /* elf header */
+ offset += (segs + 1) * sizeof (struct elf_phdr); /* program headers */
+
+ /* write out notes program header */
+ fill_elf_note_phdr(&phdr, info.notes_size, offset);
+
+ offset += info.notes_size;
+ if (dump_write(fd, &phdr, sizeof (phdr)) != 0)
+ goto out;
+
+ /*
+ * ELF specification wants data to start at page boundary so
+ * we align it here.
+ */
+ offset = roundup(offset, ELF_EXEC_PAGESIZE);
+
+ /*
+ * Write program headers for memory regions mapped in
+ * the target process.
+ */
+ for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) {
+ (void) memset(&phdr, 0, sizeof (phdr));
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = vma->vma_start;
+ phdr.p_paddr = 0;
+ phdr.p_filesz = vma_dump_size(vma);
+ offset += phdr.p_filesz;
+ phdr.p_memsz = vma->vma_end - vma->vma_start;
+ phdr.p_flags = vma->vma_flags & PROT_READ ? PF_R : 0;
+ if (vma->vma_flags & PROT_WRITE)
+ phdr.p_flags |= PF_W;
+ if (vma->vma_flags & PROT_EXEC)
+ phdr.p_flags |= PF_X;
+ phdr.p_align = ELF_EXEC_PAGESIZE;
+
+ dump_write(fd, &phdr, sizeof (phdr));
+ }
+
+ /*
+ * Next we write notes just after program headers. No
+ * alignment needed here.
+ */
+ if (write_note_info(&info, fd) < 0)
+ goto out;
+
+ /* align data to page boundary */
+ data_offset = lseek(fd, 0, SEEK_CUR);
+ data_offset = TARGET_PAGE_ALIGN(data_offset);
+ if (lseek(fd, data_offset, SEEK_SET) != data_offset)
+ goto out;
+
+ /*
+ * Finally we can dump process memory into corefile as well.
+ */
+ for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) {
+ abi_ulong addr;
+ abi_ulong end;
+
+ end = vma->vma_start + vma_dump_size(vma);
+
+ for (addr = vma->vma_start; addr < end;
+ addr += TARGET_PAGE_SIZE) {
+ char page[TARGET_PAGE_SIZE];
+ int error;
+
+ /*
+ * Read in page from target process memory and
+ * write it to coredump file.
+ */
+ error = copy_from_user(page, addr, sizeof (page));
+ if (error != 0) {
+ (void) fprintf(stderr, "unable to dump " TARGET_ABI_FMT_lx "\n",
+ addr);
+ errno = -error;
+ goto out;
+ }
+ if (dump_write(fd, page, TARGET_PAGE_SIZE) < 0)
+ goto out;
+ }
+ }
+
+ out:
+ free_note_info(&info);
+ if (mm != NULL)
+ vma_delete(mm);
+ (void) close(fd);
+
+ if (errno != 0)
+ return (-errno);
+ return (0);
+}
+#endif /* USE_ELF_CORE_DUMP */
+
+void do_init_thread(struct target_pt_regs *regs, struct image_info *infop)
+{
+ init_thread(regs, infop);
+}
diff --git a/linux-user/errno_defs.h b/linux-user/errno_defs.h
new file mode 100644
index 0000000..8a1cf76
--- /dev/null
+++ b/linux-user/errno_defs.h
@@ -0,0 +1,141 @@
+/*
+ * Target definitions of errnos. These may be overridden by an
+ * architecture specific header if needed.
+ *
+ * Taken from asm-generic/errno-base.h and asm-generic/errno.h
+ */
+#define TARGET_EPERM 1 /* Operation not permitted */
+#define TARGET_ENOENT 2 /* No such file or directory */
+#define TARGET_ESRCH 3 /* No such process */
+#define TARGET_EINTR 4 /* Interrupted system call */
+#define TARGET_EIO 5 /* I/O error */
+#define TARGET_ENXIO 6 /* No such device or address */
+#define TARGET_E2BIG 7 /* Argument list too long */
+#define TARGET_ENOEXEC 8 /* TARGET_Exec format error */
+#define TARGET_EBADF 9 /* Bad file number */
+#define TARGET_ECHILD 10 /* No child processes */
+#define TARGET_EAGAIN 11 /* Try again */
+#define TARGET_ENOMEM 12 /* Out of memory */
+#define TARGET_EACCES 13 /* Permission denied */
+#define TARGET_EFAULT 14 /* Bad address */
+#define TARGET_ENOTBLK 15 /* Block device required */
+#define TARGET_EBUSY 16 /* Device or resource busy */
+#define TARGET_EEXIST 17 /* File exists */
+#define TARGET_EXDEV 18 /* Cross-device link */
+#define TARGET_ENODEV 19 /* No such device */
+#define TARGET_ENOTDIR 20 /* Not a directory */
+#define TARGET_EISDIR 21 /* Is a directory */
+#define TARGET_EINVAL 22 /* Invalid argument */
+#define TARGET_ENFILE 23 /* File table overflow */
+#define TARGET_EMFILE 24 /* Too many open files */
+#define TARGET_ENOTTY 25 /* Not a typewriter */
+#define TARGET_ETXTBSY 26 /* Text file busy */
+#define TARGET_EFBIG 27 /* File too large */
+#define TARGET_ENOSPC 28 /* No space left on device */
+#define TARGET_ESPIPE 29 /* Illegal seek */
+#define TARGET_EROFS 30 /* Read-only file system */
+#define TARGET_EMLINK 31 /* Too many links */
+#define TARGET_EPIPE 32 /* Broken pipe */
+#define TARGET_EDOM 33 /* Math argument out of domain of func */
+#define TARGET_ERANGE 34 /* Math result not representable */
+
+#define TARGET_EDEADLK 35 /* Resource deadlock would occur */
+#define TARGET_ENAMETOOLONG 36 /* File name too long */
+#define TARGET_ENOLCK 37 /* No record locks available */
+#define TARGET_ENOSYS 38 /* Function not implemented */
+#define TARGET_ENOTEMPTY 39 /* Directory not empty */
+#define TARGET_ELOOP 40 /* Too many symbolic links encountered */
+
+#define TARGET_ENOMSG 42 /* No message of desired type */
+#define TARGET_EIDRM 43 /* Identifier removed */
+#define TARGET_ECHRNG 44 /* Channel number out of range */
+#define TARGET_EL2NSYNC 45 /* Level 2 not synchronized */
+#define TARGET_EL3HLT 46 /* Level 3 halted */
+#define TARGET_EL3RST 47 /* Level 3 reset */
+#define TARGET_ELNRNG 48 /* Link number out of range */
+#define TARGET_EUNATCH 49 /* Protocol driver not attached */
+#define TARGET_ENOCSI 50 /* No CSI structure available */
+#define TARGET_EL2HLT 51 /* Level 2 halted */
+#define TARGET_EBADE 52 /* Invalid exchange */
+#define TARGET_EBADR 53 /* Invalid request descriptor */
+#define TARGET_EXFULL 54 /* TARGET_Exchange full */
+#define TARGET_ENOANO 55 /* No anode */
+#define TARGET_EBADRQC 56 /* Invalid request code */
+#define TARGET_EBADSLT 57 /* Invalid slot */
+
+#define TARGET_EBFONT 59 /* Bad font file format */
+#define TARGET_ENOSTR 60 /* Device not a stream */
+#define TARGET_ENODATA 61 /* No data available */
+#define TARGET_ETIME 62 /* Timer expired */
+#define TARGET_ENOSR 63 /* Out of streams resources */
+#define TARGET_ENONET 64 /* Machine is not on the network */
+#define TARGET_ENOPKG 65 /* Package not installed */
+#define TARGET_EREMOTE 66 /* Object is remote */
+#define TARGET_ENOLINK 67 /* Link has been severed */
+#define TARGET_EADV 68 /* Advertise error */
+#define TARGET_ESRMNT 69 /* Srmount error */
+#define TARGET_ECOMM 70 /* Communication error on send */
+#define TARGET_EPROTO 71 /* Protocol error */
+#define TARGET_EMULTIHOP 72 /* Multihop attempted */
+#define TARGET_EDOTDOT 73 /* RFS specific error */
+#define TARGET_EBADMSG 74 /* Not a data message */
+#define TARGET_EOVERFLOW 75 /* Value too large for defined data type */
+#define TARGET_ENOTUNIQ 76 /* Name not unique on network */
+#define TARGET_EBADFD 77 /* File descriptor in bad state */
+#define TARGET_EREMCHG 78 /* Remote address changed */
+#define TARGET_ELIBACC 79 /* Can not access a needed shared library */
+#define TARGET_ELIBBAD 80 /* Accessing a corrupted shared library */
+#define TARGET_ELIBSCN 81 /* .lib section in a.out corrupted */
+#define TARGET_ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define TARGET_ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define TARGET_EILSEQ 84 /* Illegal byte sequence */
+#define TARGET_ERESTART 85 /* Interrupted system call should be restarted */
+#define TARGET_ESTRPIPE 86 /* Streams pipe error */
+#define TARGET_EUSERS 87 /* Too many users */
+#define TARGET_ENOTSOCK 88 /* Socket operation on non-socket */
+#define TARGET_EDESTADDRREQ 89 /* Destination address required */
+#define TARGET_EMSGSIZE 90 /* Message too long */
+#define TARGET_EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define TARGET_ENOPROTOOPT 92 /* Protocol not available */
+#define TARGET_EPROTONOSUPPORT 93 /* Protocol not supported */
+#define TARGET_ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define TARGET_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define TARGET_EPFNOSUPPORT 96 /* Protocol family not supported */
+#define TARGET_EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define TARGET_EADDRINUSE 98 /* Address already in use */
+#define TARGET_EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define TARGET_ENETDOWN 100 /* Network is down */
+#define TARGET_ENETUNREACH 101 /* Network is unreachable */
+#define TARGET_ENETRESET 102 /* Network dropped connection because of reset */
+#define TARGET_ECONNABORTED 103 /* Software caused connection abort */
+#define TARGET_ECONNRESET 104 /* Connection reset by peer */
+#define TARGET_ENOBUFS 105 /* No buffer space available */
+#define TARGET_EISCONN 106 /* Transport endpoint is already connected */
+#define TARGET_ENOTCONN 107 /* Transport endpoint is not connected */
+#define TARGET_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define TARGET_ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define TARGET_ETIMEDOUT 110 /* Connection timed out */
+#define TARGET_ECONNREFUSED 111 /* Connection refused */
+#define TARGET_EHOSTDOWN 112 /* Host is down */
+#define TARGET_EHOSTUNREACH 113 /* No route to host */
+#define TARGET_EALREADY 114 /* Operation already in progress */
+#define TARGET_EINPROGRESS 115 /* Operation now in progress */
+#define TARGET_ESTALE 116 /* Stale NFS file handle */
+#define TARGET_EUCLEAN 117 /* Structure needs cleaning */
+#define TARGET_ENOTNAM 118 /* Not a XENIX named type file */
+#define TARGET_ENAVAIL 119 /* No XENIX semaphores available */
+#define TARGET_EISNAM 120 /* Is a named type file */
+#define TARGET_EREMOTEIO 121 /* Remote I/O error */
+#define TARGET_EDQUOT 122 /* Quota exceeded */
+
+#define TARGET_ENOMEDIUM 123 /* No medium found */
+#define TARGET_EMEDIUMTYPE 124 /* Wrong medium type */
+#define TARGET_ECANCELED 125 /* Operation Canceled */
+#define TARGET_ENOKEY 126 /* Required key not available */
+#define TARGET_EKEYEXPIRED 127 /* Key has expired */
+#define TARGET_EKEYREVOKED 128 /* Key has been revoked */
+#define TARGET_EKEYREJECTED 129 /* Key was rejected by service */
+
+/* for robust mutexes */
+#define TARGET_EOWNERDEAD 130 /* Owner died */
+#define TARGET_ENOTRECOVERABLE 131 /* State not recoverable */
diff --git a/linux-user/flat.h b/linux-user/flat.h
new file mode 100644
index 0000000..6f2d0c4
--- /dev/null
+++ b/linux-user/flat.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2002-2003 David McCullough <davidm@snapgear.com>
+ * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
+ * The Silver Hammer Group, Ltd.
+ *
+ * This file provides the definitions and structures needed to
+ * support uClinux flat-format executables.
+ */
+
+#define FLAT_VERSION 0x00000004L
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+#define MAX_SHARED_LIBS (4)
+#else
+#define MAX_SHARED_LIBS (1)
+#endif
+
+/*
+ * To make everything easier to port and manage cross platform
+ * development, all fields are in network byte order.
+ */
+
+struct flat_hdr {
+ char magic[4];
+ abi_ulong rev; /* version (as above) */
+ abi_ulong entry; /* Offset of first executable instruction
+ with text segment from beginning of file */
+ abi_ulong data_start; /* Offset of data segment from beginning of
+ file */
+ abi_ulong data_end; /* Offset of end of data segment
+ from beginning of file */
+ abi_ulong bss_end; /* Offset of end of bss segment from beginning
+ of file */
+
+ /* (It is assumed that data_end through bss_end forms the bss segment.) */
+
+ abi_ulong stack_size; /* Size of stack, in bytes */
+ abi_ulong reloc_start; /* Offset of relocation records from
+ beginning of file */
+ abi_ulong reloc_count; /* Number of relocation records */
+ abi_ulong flags;
+ abi_ulong build_date; /* When the program/library was built */
+ abi_ulong filler[5]; /* Reservered, set to zero */
+};
+
+#define FLAT_FLAG_RAM 0x0001 /* load program entirely into RAM */
+#define FLAT_FLAG_GOTPIC 0x0002 /* program is PIC with GOT */
+#define FLAT_FLAG_GZIP 0x0004 /* all but the header is compressed */
+#define FLAT_FLAG_GZDATA 0x0008 /* only data/relocs are compressed (for XIP) */
+#define FLAT_FLAG_KTRACE 0x0010 /* output useful kernel trace for debugging */
+
+
+/*
+ * While it would be nice to keep this header clean, users of older
+ * tools still need this support in the kernel. So this section is
+ * purely for compatibility with old tool chains.
+ *
+ * DO NOT make changes or enhancements to the old format please, just work
+ * with the format above, except to fix bugs with old format support.
+ */
+
+#define OLD_FLAT_VERSION 0x00000002L
+#define OLD_FLAT_RELOC_TYPE_TEXT 0
+#define OLD_FLAT_RELOC_TYPE_DATA 1
+#define OLD_FLAT_RELOC_TYPE_BSS 2
+
+# define OLD_FLAT_FLAG_RAM 0x1 /* load program entirely into RAM */
diff --git a/linux-user/flatload.c b/linux-user/flatload.c
new file mode 100644
index 0000000..8f9f4a5
--- /dev/null
+++ b/linux-user/flatload.c
@@ -0,0 +1,815 @@
+/****************************************************************************/
+/*
+ * QEMU bFLT binary loader. Based on linux/fs/binfmt_flat.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * Copyright (C) 2006 CodeSourcery.
+ * Copyright (C) 2000-2003 David McCullough <davidm@snapgear.com>
+ * Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com>
+ * Copyright (C) 2002 SnapGear, by Paul Dale <pauli@snapgear.com>
+ * Copyright (C) 2000, 2001 Lineo, by David McCullough <davidm@lineo.com>
+ * based heavily on:
+ *
+ * linux/fs/binfmt_aout.c:
+ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
+ * linux/fs/binfmt_flat.c for 2.0 kernel
+ * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>
+ * JAN/99 -- coded full program relocation (gerg@snapgear.com)
+ */
+
+/* ??? ZFLAT and shared library support is currently disabled. */
+
+/****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "qemu.h"
+#include "flat.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DBG_FLT(...) printf(__VA_ARGS__)
+#else
+#define DBG_FLT(...)
+#endif
+
+#define flat_reloc_valid(reloc, size) ((reloc) <= (size))
+#define flat_old_ram_flag(flag) (flag)
+#ifdef TARGET_WORDS_BIGENDIAN
+#define flat_get_relocate_addr(relval) (relval)
+#else
+#define flat_get_relocate_addr(relval) bswap32(relval)
+#endif
+
+#define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */
+#define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */
+
+struct lib_info {
+ abi_ulong start_code; /* Start of text segment */
+ abi_ulong start_data; /* Start of data segment */
+ abi_ulong end_data; /* Start of bss section */
+ abi_ulong start_brk; /* End of data segment */
+ abi_ulong text_len; /* Length of text segment */
+ abi_ulong entry; /* Start address for this module */
+ abi_ulong build_date; /* When this one was compiled */
+ short loaded; /* Has this library been loaded? */
+};
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+static int load_flat_shared_library(int id, struct lib_info *p);
+#endif
+
+struct linux_binprm;
+
+#define ntohl(x) be32_to_cpu(x)
+
+/****************************************************************************/
+/*
+ * create_flat_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+
+/* Push a block of strings onto the guest stack. */
+static abi_ulong copy_strings(abi_ulong p, int n, char **s)
+{
+ int len;
+
+ while (n-- > 0) {
+ len = strlen(s[n]) + 1;
+ p -= len;
+ memcpy_to_target(p, s[n], len);
+ }
+
+ return p;
+}
+
+static int target_pread(int fd, abi_ulong ptr, abi_ulong len,
+ abi_ulong offset)
+{
+ void *buf;
+ int ret;
+
+ buf = lock_user(VERIFY_WRITE, ptr, len, 0);
+ ret = pread(fd, buf, len, offset);
+ unlock_user(buf, ptr, len);
+ return ret;
+}
+/****************************************************************************/
+
+#ifdef CONFIG_BINFMT_ZFLAT
+
+#include <linux/zlib.h>
+
+#define LBUFSIZE 4000
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
+#define RESERVED 0xC0 /* bit 6,7: reserved */
+
+static int decompress_exec(
+ struct linux_binprm *bprm,
+ unsigned long offset,
+ char *dst,
+ long len,
+ int fd)
+{
+ unsigned char *buf;
+ z_stream strm;
+ loff_t fpos;
+ int ret, retval;
+
+ DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n",(int)offset, (int)dst, (int)len);
+
+ memset(&strm, 0, sizeof(strm));
+ strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
+ if (strm.workspace == NULL) {
+ DBG_FLT("binfmt_flat: no memory for decompress workspace\n");
+ return -ENOMEM;
+ }
+ buf = kmalloc(LBUFSIZE, GFP_KERNEL);
+ if (buf == NULL) {
+ DBG_FLT("binfmt_flat: no memory for read buffer\n");
+ retval = -ENOMEM;
+ goto out_free;
+ }
+
+ /* Read in first chunk of data and parse gzip header. */
+ fpos = offset;
+ ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos);
+
+ strm.next_in = buf;
+ strm.avail_in = ret;
+ strm.total_in = 0;
+
+ retval = -ENOEXEC;
+
+ /* Check minimum size -- gzip header */
+ if (ret < 10) {
+ DBG_FLT("binfmt_flat: file too small?\n");
+ goto out_free_buf;
+ }
+
+ /* Check gzip magic number */
+ if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) {
+ DBG_FLT("binfmt_flat: unknown compression magic?\n");
+ goto out_free_buf;
+ }
+
+ /* Check gzip method */
+ if (buf[2] != 8) {
+ DBG_FLT("binfmt_flat: unknown compression method?\n");
+ goto out_free_buf;
+ }
+ /* Check gzip flags */
+ if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) ||
+ (buf[3] & RESERVED)) {
+ DBG_FLT("binfmt_flat: unknown flags?\n");
+ goto out_free_buf;
+ }
+
+ ret = 10;
+ if (buf[3] & EXTRA_FIELD) {
+ ret += 2 + buf[10] + (buf[11] << 8);
+ if (unlikely(LBUFSIZE == ret)) {
+ DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n");
+ goto out_free_buf;
+ }
+ }
+ if (buf[3] & ORIG_NAME) {
+ for (; ret < LBUFSIZE && (buf[ret] != 0); ret++)
+ ;
+ if (unlikely(LBUFSIZE == ret)) {
+ DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n");
+ goto out_free_buf;
+ }
+ }
+ if (buf[3] & COMMENT) {
+ for (; ret < LBUFSIZE && (buf[ret] != 0); ret++)
+ ;
+ if (unlikely(LBUFSIZE == ret)) {
+ DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n");
+ goto out_free_buf;
+ }
+ }
+
+ strm.next_in += ret;
+ strm.avail_in -= ret;
+
+ strm.next_out = dst;
+ strm.avail_out = len;
+ strm.total_out = 0;
+
+ if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
+ DBG_FLT("binfmt_flat: zlib init failed?\n");
+ goto out_free_buf;
+ }
+
+ while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) {
+ ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos);
+ if (ret <= 0)
+ break;
+ if (ret >= (unsigned long) -4096)
+ break;
+ len -= ret;
+
+ strm.next_in = buf;
+ strm.avail_in = ret;
+ strm.total_in = 0;
+ }
+
+ if (ret < 0) {
+ DBG_FLT("binfmt_flat: decompression failed (%d), %s\n",
+ ret, strm.msg);
+ goto out_zlib;
+ }
+
+ retval = 0;
+out_zlib:
+ zlib_inflateEnd(&strm);
+out_free_buf:
+ kfree(buf);
+out_free:
+ kfree(strm.workspace);
+out:
+ return retval;
+}
+
+#endif /* CONFIG_BINFMT_ZFLAT */
+
+/****************************************************************************/
+
+static abi_ulong
+calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp)
+{
+ abi_ulong addr;
+ int id;
+ abi_ulong start_brk;
+ abi_ulong start_data;
+ abi_ulong text_len;
+ abi_ulong start_code;
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+#error needs checking
+ if (r == 0)
+ id = curid; /* Relocs of 0 are always self referring */
+ else {
+ id = (r >> 24) & 0xff; /* Find ID for this reloc */
+ r &= 0x00ffffff; /* Trim ID off here */
+ }
+ if (id >= MAX_SHARED_LIBS) {
+ fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n",
+ (unsigned) r, id);
+ goto failed;
+ }
+ if (curid != id) {
+ if (internalp) {
+ fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not "
+ "in same module (%d != %d)\n",
+ (unsigned) r, curid, id);
+ goto failed;
+ } else if ( ! p[id].loaded &&
+ load_flat_shared_library(id, p) > (unsigned long) -4096) {
+ fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id);
+ goto failed;
+ }
+ /* Check versioning information (i.e. time stamps) */
+ if (p[id].build_date && p[curid].build_date
+ && p[curid].build_date < p[id].build_date) {
+ fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n",
+ id, curid);
+ goto failed;
+ }
+ }
+#else
+ id = 0;
+#endif
+
+ start_brk = p[id].start_brk;
+ start_data = p[id].start_data;
+ start_code = p[id].start_code;
+ text_len = p[id].text_len;
+
+ if (!flat_reloc_valid(r, start_brk - start_data + text_len)) {
+ fprintf(stderr, "BINFMT_FLAT: reloc outside program 0x%x "
+ "(0 - 0x%x/0x%x)\n",
+ (int) r,(int)(start_brk-start_code),(int)text_len);
+ goto failed;
+ }
+
+ if (r < text_len) /* In text segment */
+ addr = r + start_code;
+ else /* In data segment */
+ addr = r - text_len + start_data;
+
+ /* Range checked already above so doing the range tests is redundant...*/
+ return(addr);
+
+failed:
+ abort();
+ return RELOC_FAILED;
+}
+
+/****************************************************************************/
+
+/* ??? This does not handle endianness correctly. */
+static void old_reloc(struct lib_info *libinfo, uint32_t rl)
+{
+#ifdef DEBUG
+ const char *segment[] = { "TEXT", "DATA", "BSS", "*UNKNOWN*" };
+#endif
+ uint32_t *ptr;
+ uint32_t offset;
+ int reloc_type;
+
+ offset = rl & 0x3fffffff;
+ reloc_type = rl >> 30;
+ /* ??? How to handle this? */
+#if defined(CONFIG_COLDFIRE)
+ ptr = (uint32_t *) ((unsigned long) libinfo->start_code + offset);
+#else
+ ptr = (uint32_t *) ((unsigned long) libinfo->start_data + offset);
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "Relocation of variable at DATASEG+%x "
+ "(address %p, currently %x) into segment %s\n",
+ offset, ptr, (int)*ptr, segment[reloc_type]);
+#endif
+
+ switch (reloc_type) {
+ case OLD_FLAT_RELOC_TYPE_TEXT:
+ *ptr += libinfo->start_code;
+ break;
+ case OLD_FLAT_RELOC_TYPE_DATA:
+ *ptr += libinfo->start_data;
+ break;
+ case OLD_FLAT_RELOC_TYPE_BSS:
+ *ptr += libinfo->end_data;
+ break;
+ default:
+ fprintf(stderr, "BINFMT_FLAT: Unknown relocation type=%x\n",
+ reloc_type);
+ break;
+ }
+ DBG_FLT("Relocation became %x\n", (int)*ptr);
+}
+
+/****************************************************************************/
+
+static int load_flat_file(struct linux_binprm * bprm,
+ struct lib_info *libinfo, int id, abi_ulong *extra_stack)
+{
+ struct flat_hdr * hdr;
+ abi_ulong textpos = 0, datapos = 0;
+ abi_long result;
+ abi_ulong realdatastart = 0;
+ abi_ulong text_len, data_len, bss_len, stack_len, flags;
+ abi_ulong memp = 0; /* for finding the brk area */
+ abi_ulong extra;
+ abi_ulong reloc = 0, rp;
+ int i, rev, relocs = 0;
+ abi_ulong fpos;
+ abi_ulong start_code, end_code;
+ abi_ulong indx_len;
+
+ hdr = ((struct flat_hdr *) bprm->buf); /* exec-header */
+
+ text_len = ntohl(hdr->data_start);
+ data_len = ntohl(hdr->data_end) - ntohl(hdr->data_start);
+ bss_len = ntohl(hdr->bss_end) - ntohl(hdr->data_end);
+ stack_len = ntohl(hdr->stack_size);
+ if (extra_stack) {
+ stack_len += *extra_stack;
+ *extra_stack = stack_len;
+ }
+ relocs = ntohl(hdr->reloc_count);
+ flags = ntohl(hdr->flags);
+ rev = ntohl(hdr->rev);
+
+ DBG_FLT("BINFMT_FLAT: Loading file: %s\n", bprm->filename);
+
+ if (rev != FLAT_VERSION && rev != OLD_FLAT_VERSION) {
+ fprintf(stderr, "BINFMT_FLAT: bad magic/rev (0x%x, need 0x%x)\n",
+ rev, (int) FLAT_VERSION);
+ return -ENOEXEC;
+ }
+
+ /* Don't allow old format executables to use shared libraries */
+ if (rev == OLD_FLAT_VERSION && id != 0) {
+ fprintf(stderr, "BINFMT_FLAT: shared libraries are not available\n");
+ return -ENOEXEC;
+ }
+
+ /*
+ * fix up the flags for the older format, there were all kinds
+ * of endian hacks, this only works for the simple cases
+ */
+ if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags))
+ flags = FLAT_FLAG_RAM;
+
+#ifndef CONFIG_BINFMT_ZFLAT
+ if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) {
+ fprintf(stderr, "Support for ZFLAT executables is not enabled\n");
+ return -ENOEXEC;
+ }
+#endif
+
+ /*
+ * calculate the extra space we need to map in
+ */
+ extra = relocs * sizeof(abi_ulong);
+ if (extra < bss_len + stack_len)
+ extra = bss_len + stack_len;
+
+ /* Add space for library base pointers. Make sure this does not
+ misalign the doesn't misalign the data segment. */
+ indx_len = MAX_SHARED_LIBS * sizeof(abi_ulong);
+ indx_len = (indx_len + 15) & ~(abi_ulong)15;
+
+ /*
+ * there are a couple of cases here, the separate code/data
+ * case, and then the fully copied to RAM case which lumps
+ * it all together.
+ */
+ if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) {
+ /*
+ * this should give us a ROM ptr, but if it doesn't we don't
+ * really care
+ */
+ DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n");
+
+ textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC,
+ MAP_PRIVATE, bprm->fd, 0);
+ if (textpos == -1) {
+ fprintf(stderr, "Unable to mmap process text\n");
+ return -1;
+ }
+
+ realdatastart = target_mmap(0, data_len + extra + indx_len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ if (realdatastart == -1) {
+ fprintf(stderr, "Unable to allocate RAM for process data\n");
+ return realdatastart;
+ }
+ datapos = realdatastart + indx_len;
+
+ DBG_FLT("BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x\n",
+ (int)(data_len + bss_len + stack_len), (int)datapos);
+
+ fpos = ntohl(hdr->data_start);
+#ifdef CONFIG_BINFMT_ZFLAT
+ if (flags & FLAT_FLAG_GZDATA) {
+ result = decompress_exec(bprm, fpos, (char *) datapos,
+ data_len + (relocs * sizeof(abi_ulong)))
+ } else
+#endif
+ {
+ result = target_pread(bprm->fd, datapos,
+ data_len + (relocs * sizeof(abi_ulong)),
+ fpos);
+ }
+ if (result < 0) {
+ fprintf(stderr, "Unable to read data+bss\n");
+ return result;
+ }
+
+ reloc = datapos + (ntohl(hdr->reloc_start) - text_len);
+ memp = realdatastart;
+
+ } else {
+
+ textpos = target_mmap(0, text_len + data_len + extra + indx_len,
+ PROT_READ | PROT_EXEC | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (textpos == -1 ) {
+ fprintf(stderr, "Unable to allocate RAM for process text/data\n");
+ return -1;
+ }
+
+ realdatastart = textpos + ntohl(hdr->data_start);
+ datapos = realdatastart + indx_len;
+ reloc = (textpos + ntohl(hdr->reloc_start) + indx_len);
+ memp = textpos;
+
+#ifdef CONFIG_BINFMT_ZFLAT
+#error code needs checking
+ /*
+ * load it all in and treat it like a RAM load from now on
+ */
+ if (flags & FLAT_FLAG_GZIP) {
+ result = decompress_exec(bprm, sizeof (struct flat_hdr),
+ (((char *) textpos) + sizeof (struct flat_hdr)),
+ (text_len + data_len + (relocs * sizeof(unsigned long))
+ - sizeof (struct flat_hdr)),
+ 0);
+ memmove((void *) datapos, (void *) realdatastart,
+ data_len + (relocs * sizeof(unsigned long)));
+ } else if (flags & FLAT_FLAG_GZDATA) {
+ fpos = 0;
+ result = bprm->file->f_op->read(bprm->file,
+ (char *) textpos, text_len, &fpos);
+ if (result < (unsigned long) -4096)
+ result = decompress_exec(bprm, text_len, (char *) datapos,
+ data_len + (relocs * sizeof(unsigned long)), 0);
+ }
+ else
+#endif
+ {
+ result = target_pread(bprm->fd, textpos,
+ text_len, 0);
+ if (result >= 0) {
+ result = target_pread(bprm->fd, datapos,
+ data_len + (relocs * sizeof(abi_ulong)),
+ ntohl(hdr->data_start));
+ }
+ }
+ if (result < 0) {
+ fprintf(stderr, "Unable to read code+data+bss\n");
+ return result;
+ }
+ }
+
+ DBG_FLT("Mapping is 0x%x, Entry point is 0x%x, data_start is 0x%x\n",
+ (int)textpos, 0x00ffffff&ntohl(hdr->entry),
+ ntohl(hdr->data_start));
+
+ /* The main program needs a little extra setup in the task structure */
+ start_code = textpos + sizeof (struct flat_hdr);
+ end_code = textpos + text_len;
+
+ DBG_FLT("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n",
+ id ? "Lib" : "Load", bprm->filename,
+ (int) start_code, (int) end_code,
+ (int) datapos,
+ (int) (datapos + data_len),
+ (int) (datapos + data_len),
+ (int) (((datapos + data_len + bss_len) + 3) & ~3));
+
+ text_len -= sizeof(struct flat_hdr); /* the real code len */
+
+ /* Store the current module values into the global library structure */
+ libinfo[id].start_code = start_code;
+ libinfo[id].start_data = datapos;
+ libinfo[id].end_data = datapos + data_len;
+ libinfo[id].start_brk = datapos + data_len + bss_len;
+ libinfo[id].text_len = text_len;
+ libinfo[id].loaded = 1;
+ libinfo[id].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos;
+ libinfo[id].build_date = ntohl(hdr->build_date);
+
+ /*
+ * We just load the allocations into some temporary memory to
+ * help simplify all this mumbo jumbo
+ *
+ * We've got two different sections of relocation entries.
+ * The first is the GOT which resides at the begining of the data segment
+ * and is terminated with a -1. This one can be relocated in place.
+ * The second is the extra relocation entries tacked after the image's
+ * data segment. These require a little more processing as the entry is
+ * really an offset into the image which contains an offset into the
+ * image.
+ */
+ if (flags & FLAT_FLAG_GOTPIC) {
+ rp = datapos;
+ while (1) {
+ abi_ulong addr;
+ if (get_user_ual(addr, rp))
+ return -EFAULT;
+ if (addr == -1)
+ break;
+ if (addr) {
+ addr = calc_reloc(addr, libinfo, id, 0);
+ if (addr == RELOC_FAILED)
+ return -ENOEXEC;
+ if (put_user_ual(addr, rp))
+ return -EFAULT;
+ }
+ rp += sizeof(abi_ulong);
+ }
+ }
+
+ /*
+ * Now run through the relocation entries.
+ * We've got to be careful here as C++ produces relocatable zero
+ * entries in the constructor and destructor tables which are then
+ * tested for being not zero (which will always occur unless we're
+ * based from address zero). This causes an endless loop as __start
+ * is at zero. The solution used is to not relocate zero addresses.
+ * This has the negative side effect of not allowing a global data
+ * reference to be statically initialised to _stext (I've moved
+ * __start to address 4 so that is okay).
+ */
+ if (rev > OLD_FLAT_VERSION) {
+ for (i = 0; i < relocs; i++) {
+ abi_ulong addr, relval;
+
+ /* Get the address of the pointer to be
+ relocated (of course, the address has to be
+ relocated first). */
+ if (get_user_ual(relval, reloc + i * sizeof(abi_ulong)))
+ return -EFAULT;
+ addr = flat_get_relocate_addr(relval);
+ rp = calc_reloc(addr, libinfo, id, 1);
+ if (rp == RELOC_FAILED)
+ return -ENOEXEC;
+
+ /* Get the pointer's value. */
+ if (get_user_ual(addr, rp))
+ return -EFAULT;
+ if (addr != 0) {
+ /*
+ * Do the relocation. PIC relocs in the data section are
+ * already in target order
+ */
+
+#ifndef TARGET_WORDS_BIGENDIAN
+ if ((flags & FLAT_FLAG_GOTPIC) == 0)
+ addr = bswap32(addr);
+#endif
+ addr = calc_reloc(addr, libinfo, id, 0);
+ if (addr == RELOC_FAILED)
+ return -ENOEXEC;
+
+ /* Write back the relocated pointer. */
+ if (put_user_ual(addr, rp))
+ return -EFAULT;
+ }
+ }
+ } else {
+ for (i = 0; i < relocs; i++) {
+ abi_ulong relval;
+ if (get_user_ual(relval, reloc + i * sizeof(abi_ulong)))
+ return -EFAULT;
+ old_reloc(&libinfo[0], relval);
+ }
+ }
+
+ /* zero the BSS. */
+ memset((void *)((unsigned long)datapos + data_len), 0, bss_len);
+
+ return 0;
+}
+
+
+/****************************************************************************/
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+
+/*
+ * Load a shared library into memory. The library gets its own data
+ * segment (including bss) but not argv/argc/environ.
+ */
+
+static int load_flat_shared_library(int id, struct lib_info *libs)
+{
+ struct linux_binprm bprm;
+ int res;
+ char buf[16];
+
+ /* Create the file name */
+ sprintf(buf, "/lib/lib%d.so", id);
+
+ /* Open the file up */
+ bprm.filename = buf;
+ bprm.file = open_exec(bprm.filename);
+ res = PTR_ERR(bprm.file);
+ if (IS_ERR(bprm.file))
+ return res;
+
+ res = prepare_binprm(&bprm);
+
+ if (res <= (unsigned long)-4096)
+ res = load_flat_file(&bprm, libs, id, NULL);
+ if (bprm.file) {
+ allow_write_access(bprm.file);
+ fput(bprm.file);
+ bprm.file = NULL;
+ }
+ return(res);
+}
+
+#endif /* CONFIG_BINFMT_SHARED_FLAT */
+
+int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info)
+{
+ struct lib_info libinfo[MAX_SHARED_LIBS];
+ abi_ulong p = bprm->p;
+ abi_ulong stack_len;
+ abi_ulong start_addr;
+ abi_ulong sp;
+ int res;
+ int i, j;
+
+ memset(libinfo, 0, sizeof(libinfo));
+ /*
+ * We have to add the size of our arguments to our stack size
+ * otherwise it's too easy for users to create stack overflows
+ * by passing in a huge argument list. And yes, we have to be
+ * pedantic and include space for the argv/envp array as it may have
+ * a lot of entries.
+ */
+#define TOP_OF_ARGS (TARGET_PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *))
+ stack_len = TOP_OF_ARGS - bprm->p; /* the strings */
+ stack_len += (bprm->argc + 1) * 4; /* the argv array */
+ stack_len += (bprm->envc + 1) * 4; /* the envp array */
+
+
+ res = load_flat_file(bprm, libinfo, 0, &stack_len);
+ if (res > (unsigned long)-4096)
+ return res;
+
+ /* Update data segment pointers for all libraries */
+ for (i=0; i<MAX_SHARED_LIBS; i++) {
+ if (libinfo[i].loaded) {
+ abi_ulong p;
+ p = libinfo[i].start_data;
+ for (j=0; j<MAX_SHARED_LIBS; j++) {
+ p -= 4;
+ /* FIXME - handle put_user() failures */
+ if (put_user_ual(libinfo[j].loaded
+ ? libinfo[j].start_data
+ : UNLOADED_LIB,
+ p))
+ return -EFAULT;
+ }
+ }
+ }
+
+ p = ((libinfo[0].start_brk + stack_len + 3) & ~3) - 4;
+ DBG_FLT("p=%x\n", (int)p);
+
+ /* Copy argv/envp. */
+ p = copy_strings(p, bprm->envc, bprm->envp);
+ p = copy_strings(p, bprm->argc, bprm->argv);
+ /* Align stack. */
+ sp = p & ~(abi_ulong)(sizeof(abi_ulong) - 1);
+ /* Enforce final stack alignment of 16 bytes. This is sufficient
+ for all current targets, and excess alignment is harmless. */
+ stack_len = bprm->envc + bprm->argc + 2;
+ stack_len += 3; /* argc, arvg, argp */
+ stack_len *= sizeof(abi_ulong);
+ if ((sp + stack_len) & 15)
+ sp -= 16 - ((sp + stack_len) & 15);
+ sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, 1);
+
+ /* Fake some return addresses to ensure the call chain will
+ * initialise library in order for us. We are required to call
+ * lib 1 first, then 2, ... and finally the main program (id 0).
+ */
+ start_addr = libinfo[0].entry;
+
+#ifdef CONFIG_BINFMT_SHARED_FLAT
+#error here
+ for (i = MAX_SHARED_LIBS-1; i>0; i--) {
+ if (libinfo[i].loaded) {
+ /* Push previos first to call address */
+ --sp;
+ if (put_user_ual(start_addr, sp))
+ return -EFAULT;
+ start_addr = libinfo[i].entry;
+ }
+ }
+#endif
+
+ /* Stash our initial stack pointer into the mm structure */
+ info->start_code = libinfo[0].start_code;
+ info->end_code = libinfo[0].start_code = libinfo[0].text_len;
+ info->start_data = libinfo[0].start_data;
+ info->end_data = libinfo[0].end_data;
+ info->start_brk = libinfo[0].start_brk;
+ info->start_stack = sp;
+ info->stack_limit = libinfo[0].start_brk;
+ info->entry = start_addr;
+ info->code_offset = info->start_code;
+ info->data_offset = info->start_data - libinfo[0].text_len;
+
+ DBG_FLT("start_thread(entry=0x%x, start_stack=0x%x)\n",
+ (int)info->entry, (int)info->start_stack);
+
+ return 0;
+}
diff --git a/linux-user/i386/syscall.h b/linux-user/i386/syscall.h
new file mode 100644
index 0000000..266e2c4
--- /dev/null
+++ b/linux-user/i386/syscall.h
@@ -0,0 +1,146 @@
+/* default linux values for the selectors */
+#define __USER_CS (0x23)
+#define __USER_DS (0x2B)
+
+struct target_pt_regs {
+ long ebx;
+ long ecx;
+ long edx;
+ long esi;
+ long edi;
+ long ebp;
+ long eax;
+ int xds;
+ int xes;
+ long orig_eax;
+ long eip;
+ int xcs;
+ long eflags;
+ long esp;
+ int xss;
+};
+
+/* ioctls */
+
+#define TARGET_LDT_ENTRIES 8192
+#define TARGET_LDT_ENTRY_SIZE 8
+
+#define TARGET_GDT_ENTRIES 9
+#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
+#define TARGET_GDT_ENTRY_TLS_MIN 6
+#define TARGET_GDT_ENTRY_TLS_MAX (TARGET_GDT_ENTRY_TLS_MIN + TARGET_GDT_ENTRY_TLS_ENTRIES - 1)
+
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ abi_ulong base_addr;
+ unsigned int limit;
+ unsigned int flags;
+};
+
+/* vm86 defines */
+
+#define TARGET_BIOSSEG 0x0f000
+
+#define TARGET_CPU_086 0
+#define TARGET_CPU_186 1
+#define TARGET_CPU_286 2
+#define TARGET_CPU_386 3
+#define TARGET_CPU_486 4
+#define TARGET_CPU_586 5
+
+#define TARGET_VM86_SIGNAL 0 /* return due to signal */
+#define TARGET_VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
+#define TARGET_VM86_INTx 2 /* int3/int x instruction (ARG = x) */
+#define TARGET_VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
+
+/*
+ * Additional return values when invoking new vm86()
+ */
+#define TARGET_VM86_PICRETURN 4 /* return due to pending PIC request */
+#define TARGET_VM86_TRAP 6 /* return due to DOS-debugger request */
+
+/*
+ * function codes when invoking new vm86()
+ */
+#define TARGET_VM86_PLUS_INSTALL_CHECK 0
+#define TARGET_VM86_ENTER 1
+#define TARGET_VM86_ENTER_NO_BYPASS 2
+#define TARGET_VM86_REQUEST_IRQ 3
+#define TARGET_VM86_FREE_IRQ 4
+#define TARGET_VM86_GET_IRQ_BITS 5
+#define TARGET_VM86_GET_AND_RESET_IRQ 6
+
+/*
+ * This is the stack-layout seen by the user space program when we have
+ * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
+ * is 'kernel_vm86_regs' (see below).
+ */
+
+struct target_vm86_regs {
+/*
+ * normal regs, with special meaning for the segment descriptors..
+ */
+ abi_long ebx;
+ abi_long ecx;
+ abi_long edx;
+ abi_long esi;
+ abi_long edi;
+ abi_long ebp;
+ abi_long eax;
+ abi_long __null_ds;
+ abi_long __null_es;
+ abi_long __null_fs;
+ abi_long __null_gs;
+ abi_long orig_eax;
+ abi_long eip;
+ unsigned short cs, __csh;
+ abi_long eflags;
+ abi_long esp;
+ unsigned short ss, __ssh;
+/*
+ * these are specific to v86 mode:
+ */
+ unsigned short es, __esh;
+ unsigned short ds, __dsh;
+ unsigned short fs, __fsh;
+ unsigned short gs, __gsh;
+};
+
+struct target_revectored_struct {
+ abi_ulong __map[8]; /* 256 bits */
+};
+
+struct target_vm86_struct {
+ struct target_vm86_regs regs;
+ abi_ulong flags;
+ abi_ulong screen_bitmap;
+ abi_ulong cpu_type;
+ struct target_revectored_struct int_revectored;
+ struct target_revectored_struct int21_revectored;
+};
+
+/*
+ * flags masks
+ */
+#define TARGET_VM86_SCREEN_BITMAP 0x0001
+
+struct target_vm86plus_info_struct {
+ abi_ulong flags;
+#define TARGET_force_return_for_pic (1 << 0)
+#define TARGET_vm86dbg_active (1 << 1) /* for debugger */
+#define TARGET_vm86dbg_TFpendig (1 << 2) /* for debugger */
+#define TARGET_is_vm86pus (1 << 31) /* for vm86 internal use */
+ unsigned char vm86dbg_intxxtab[32]; /* for debugger */
+};
+
+struct target_vm86plus_struct {
+ struct target_vm86_regs regs;
+ abi_ulong flags;
+ abi_ulong screen_bitmap;
+ abi_ulong cpu_type;
+ struct target_revectored_struct int_revectored;
+ struct target_revectored_struct int21_revectored;
+ struct target_vm86plus_info_struct vm86plus;
+};
+
+#define UNAME_MACHINE "i686"
diff --git a/linux-user/i386/syscall_nr.h b/linux-user/i386/syscall_nr.h
new file mode 100644
index 0000000..3ef71ce
--- /dev/null
+++ b/linux-user/i386/syscall_nr.h
@@ -0,0 +1,337 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_restart_syscall 0
+#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_waitpid 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_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#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_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#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_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#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_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#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_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86old 113
+#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_modify_ldt 123
+#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_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#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_setresuid 164
+#define TARGET_NR_getresuid 165
+#define TARGET_NR_vm86 166
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#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_pread 180
+#define TARGET_NR_pwrite 181
+#define TARGET_NR_chown 182
+#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 /* some people actually want streams */
+#define TARGET_NR_putpmsg 189 /* some people actually want streams */
+#define TARGET_NR_vfork 190
+#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_pivot_root 217
+#define TARGET_NR_mincore 218
+#define TARGET_NR_madvise 219
+#define TARGET_NR_madvise1 219 /* delete when C lib stub is removed */
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_fcntl64 221
+/* 223 is unused */
+#define TARGET_NR_gettid 224
+#define TARGET_NR_readahead 225
+#define TARGET_NR_setxattr 226
+#define TARGET_NR_lsetxattr 227
+#define TARGET_NR_fsetxattr 228
+#define TARGET_NR_getxattr 229
+#define TARGET_NR_lgetxattr 230
+#define TARGET_NR_fgetxattr 231
+#define TARGET_NR_listxattr 232
+#define TARGET_NR_llistxattr 233
+#define TARGET_NR_flistxattr 234
+#define TARGET_NR_removexattr 235
+#define TARGET_NR_lremovexattr 236
+#define TARGET_NR_fremovexattr 237
+#define TARGET_NR_tkill 238
+#define TARGET_NR_sendfile64 239
+#define TARGET_NR_futex 240
+#define TARGET_NR_sched_setaffinity 241
+#define TARGET_NR_sched_getaffinity 242
+#define TARGET_NR_set_thread_area 243
+#define TARGET_NR_get_thread_area 244
+#define TARGET_NR_io_setup 245
+#define TARGET_NR_io_destroy 246
+#define TARGET_NR_io_getevents 247
+#define TARGET_NR_io_submit 248
+#define TARGET_NR_io_cancel 249
+#define TARGET_NR_fadvise64 250
+/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */
+#define TARGET_NR_exit_group 252
+#define TARGET_NR_lookup_dcookie 253
+#define TARGET_NR_epoll_create 254
+#define TARGET_NR_epoll_ctl 255
+#define TARGET_NR_epoll_wait 256
+#define TARGET_NR_remap_file_pages 257
+#define TARGET_NR_set_tid_address 258
+#define TARGET_NR_timer_create 259
+#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)
+#define TARGET_NR_statfs64 268
+#define TARGET_NR_fstatfs64 269
+#define TARGET_NR_tgkill 270
+#define TARGET_NR_utimes 271
+#define TARGET_NR_fadvise64_64 272
+#define TARGET_NR_vserver 273
+#define TARGET_NR_mbind 274
+#define TARGET_NR_get_mempolicy 275
+#define TARGET_NR_set_mempolicy 276
+#define TARGET_NR_mq_open 277
+#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1)
+#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3)
+#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5)
+#define TARGET_NR_kexec_load 283
+#define TARGET_NR_waitid 284
+/* #define TARGET_NR_sys_setaltroot 285 */
+#define TARGET_NR_add_key 286
+#define TARGET_NR_request_key 287
+#define TARGET_NR_keyctl 288
+#define TARGET_NR_ioprio_set 289
+#define TARGET_NR_ioprio_get 290
+#define TARGET_NR_inotify_init 291
+#define TARGET_NR_inotify_add_watch 292
+#define TARGET_NR_inotify_rm_watch 293
+#define TARGET_NR_migrate_pages 294
+#define TARGET_NR_openat 295
+#define TARGET_NR_mkdirat 296
+#define TARGET_NR_mknodat 297
+#define TARGET_NR_fchownat 298
+#define TARGET_NR_futimesat 299
+#define TARGET_NR_fstatat64 300
+#define TARGET_NR_unlinkat 301
+#define TARGET_NR_renameat 302
+#define TARGET_NR_linkat 303
+#define TARGET_NR_symlinkat 304
+#define TARGET_NR_readlinkat 305
+#define TARGET_NR_fchmodat 306
+#define TARGET_NR_faccessat 307
+#define TARGET_NR_pselect6 308
+#define TARGET_NR_ppoll 309
+#define TARGET_NR_unshare 310
+#define TARGET_NR_set_robust_list 311
+#define TARGET_NR_get_robust_list 312
+#define TARGET_NR_splice 313
+#define TARGET_NR_sync_file_range 314
+#define TARGET_NR_tee 315
+#define TARGET_NR_vmsplice 316
+#define TARGET_NR_move_pages 317
+#define TARGET_NR_getcpu 318
+#define TARGET_NR_epoll_pwait 319
+#define TARGET_NR_utimensat 320
+#define TARGET_NR_signalfd 321
+#define TARGET_NR_timerfd 322
+#define TARGET_NR_eventfd 323
+#define TARGET_NR_fallocate 324
+#define TARGET_NR_timerfd_settime 325
+#define TARGET_NR_timerfd_gettime 326
+#define TARGET_NR_signalfd4 327
+#define TARGET_NR_eventfd2 328
+#define TARGET_NR_epoll_create1 329
+#define TARGET_NR_dup3 330
+#define TARGET_NR_pipe2 331
+#define TARGET_NR_inotify_init1 332
diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h
new file mode 100644
index 0000000..9baf7fb
--- /dev/null
+++ b/linux-user/i386/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long 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(CPUX86State *state)
+{
+ return state->regs[R_ESP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/i386/termbits.h b/linux-user/i386/termbits.h
new file mode 100644
index 0000000..e051a3a
--- /dev/null
+++ b/linux-user/i386/termbits.h
@@ -0,0 +1,226 @@
+/* from asm/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 */
+};
+
+/* 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_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 (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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
+
+/* c_cc character offsets */
+#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
+
+/* ioctls */
+
+#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 TARGET_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_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* 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/ioctls.h b/linux-user/ioctls.h
new file mode 100644
index 0000000..526aaa2
--- /dev/null
+++ b/linux-user/ioctls.h
@@ -0,0 +1,332 @@
+ /* emulated ioctl list */
+
+ IOCTL(TCGETS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TCSETS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TCSETSF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TCSETSW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize)))
+ IOCTL(TIOCSWINSZ, IOC_W, MK_PTR(MK_STRUCT(STRUCT_winsize)))
+ IOCTL(FIONREAD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TCGETA, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TCSETA, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TCSETAW, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TCSETAF, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TCSBRK, 0, TYPE_INT)
+ IOCTL(TCSBRKP, 0, TYPE_INT)
+ IOCTL(TCXONC, 0, TYPE_INT)
+ IOCTL(TCFLSH, 0, TYPE_INT)
+ IOCTL(TIOCEXCL, 0, TYPE_NULL)
+ IOCTL(TIOCNXCL, 0, TYPE_NULL)
+ IOCTL(TIOCSCTTY, 0, TYPE_INT)
+ IOCTL(TIOCGPGRP, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSPGRP, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCOUTQ, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSTI, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMGET, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMBIS, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMBIC, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCMSET, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCGSOFTCAR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSSOFTCAR, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCLINUX, IOC_R | IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCCONS, 0, TYPE_NULL)
+ IOCTL(TIOCGSERIAL, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSSERIAL, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCPKT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(FIONBIO, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCNOTTY, 0, TYPE_NULL)
+ IOCTL(TIOCGETD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSETD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCGPTN, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSPTLCK, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(FIOCLEX, 0, TYPE_NULL)
+ IOCTL(FIONCLEX, 0, TYPE_NULL)
+ IOCTL(FIOASYNC, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(TIOCGLCKTRMIOS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCSLCKTRMIOS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios)))
+ IOCTL(TIOCSERCONFIG, 0, TYPE_NULL)
+ IOCTL(TIOCSERGETLSR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(TIOCSERGETMULTI, IOC_R, MK_PTR(MK_STRUCT(STRUCT_serial_multiport_struct)))
+ IOCTL(TIOCSERSETMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_serial_multiport_struct)))
+ IOCTL(TIOCMIWAIT, 0, TYPE_INT)
+ IOCTL(TIOCGICOUNT, IOC_R, MK_PTR(MK_STRUCT(STRUCT_serial_icounter_struct)))
+
+ IOCTL(KIOCSOUND, 0, TYPE_INT)
+ IOCTL(KDMKTONE, 0, TYPE_INT)
+ IOCTL(KDSETMODE, 0, TYPE_INT)
+ IOCTL(KDGKBTYPE, IOC_R, MK_PTR(TYPE_CHAR))
+ IOCTL(KDGKBMODE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(KDSKBMODE, 0, TYPE_INT)
+ IOCTL(KDGKBENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbentry)))
+ IOCTL(KDGKBSENT, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_kbsentry)))
+
+ IOCTL(BLKROSET, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(BLKROGET, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(BLKRRPART, 0, TYPE_NULL)
+ IOCTL(BLKGETSIZE, IOC_R, MK_PTR(TYPE_ULONG))
+#ifdef BLKGETSIZE64
+ IOCTL(BLKGETSIZE64, IOC_R, MK_PTR(TYPE_ULONGLONG))
+#endif
+ IOCTL(BLKFLSBUF, 0, TYPE_NULL)
+ IOCTL(BLKRASET, 0, TYPE_INT)
+ IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG))
+#ifdef FIBMAP
+ IOCTL(FIBMAP, IOC_W | IOC_R, MK_PTR(TYPE_LONG))
+#endif
+#ifdef FIGETBSZ
+ IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG))
+#endif
+#ifdef CONFIG_FIEMAP
+ IOCTL_SPECIAL(FS_IOC_FIEMAP, IOC_W | IOC_R, do_ioctl_fs_ioc_fiemap,
+ MK_PTR(MK_STRUCT(STRUCT_fiemap)))
+#endif
+
+ IOCTL(SIOCATMARK, 0, TYPE_NULL)
+ IOCTL(SIOCADDRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
+ IOCTL(SIOCDELRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
+ IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+ IOCTL(SIOCSIFFLAGS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+ IOCTL(SIOCGIFADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFBRDADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFBRDADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFDSTADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFDSTADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFNETMASK, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFNETMASK, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFHWADDR, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFHWADDR, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFTXQLEN, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFTXQLEN, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCGIFMETRIC, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCSIFMETRIC, IOC_W, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCGIFMTU, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCSIFMTU, IOC_W, MK_PTR(MK_STRUCT(STRUCT_int_ifreq)))
+ IOCTL(SIOCGIFMAP, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifmap_ifreq)))
+ IOCTL(SIOCSIFMAP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_ifmap_ifreq)))
+ IOCTL(SIOCGIFSLAVE, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_char_ifreq)))
+ IOCTL(SIOCSIFSLAVE, IOC_W, MK_PTR(MK_STRUCT(STRUCT_char_ifreq)))
+ IOCTL(SIOCGIFMEM, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ptr_ifreq)))
+ IOCTL(SIOCSIFMEM, IOC_W, MK_PTR(MK_STRUCT(STRUCT_ptr_ifreq)))
+ IOCTL(SIOCADDMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCDELMULTI, IOC_W, MK_PTR(MK_STRUCT(STRUCT_sockaddr_ifreq)))
+ IOCTL(SIOCSIFLINK, 0, TYPE_NULL)
+ IOCTL(SIOCGIFCONF, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_ifconf)))
+ IOCTL(SIOCGIFENCAP, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SIOCSIFENCAP, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SIOCDARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCSARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCGARP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCDRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCSRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+ IOCTL(SIOCGRARP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_arpreq)))
+
+ IOCTL(CDROMPAUSE, 0, TYPE_NULL)
+ IOCTL(CDROMSTART, 0, TYPE_NULL)
+ IOCTL(CDROMSTOP, 0, TYPE_NULL)
+ IOCTL(CDROMRESUME, 0, TYPE_NULL)
+ IOCTL(CDROMEJECT, 0, TYPE_NULL)
+ IOCTL(CDROMEJECT_SW, 0, TYPE_INT)
+ IOCTL(CDROMCLOSETRAY, 0, TYPE_NULL)
+ IOCTL(CDROMRESET, 0, TYPE_NULL)
+ IOCTL(CDROMPLAYMSF, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMPLAYTRKIND, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADTOCHDR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADTOCENTRY, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMVOLCTRL, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMSUBCHNL, IOC_RW, MK_PTR(TYPE_INT))
+ /* XXX: incorrect (need specific handling) */
+ IOCTL(CDROMREADAUDIO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_cdrom_read_audio)))
+ IOCTL(CDROMREADCOOKED, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADRAW, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADMODE1, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADMODE2, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMREADALL, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROMMULTISESSION, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(CDROM_GET_UPC, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(CDROMVOLREAD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(CDROMSEEK, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROMPLAYBLK, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(CDROM_MEDIA_CHANGED, 0, TYPE_NULL)
+ IOCTL(CDROM_SET_OPTIONS, 0, TYPE_INT)
+ IOCTL(CDROM_CLEAR_OPTIONS, 0, TYPE_INT)
+ IOCTL(CDROM_SELECT_SPEED, 0, TYPE_INT)
+ IOCTL(CDROM_SELECT_DISC, 0, TYPE_INT)
+ IOCTL(CDROM_DRIVE_STATUS, 0, TYPE_NULL)
+ IOCTL(CDROM_DISC_STATUS, 0, TYPE_NULL)
+ IOCTL(CDROMAUDIOBUFSIZ, 0, TYPE_INT)
+
+#if 0
+ IOCTL(SNDCTL_COPR_HALT, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_LOAD, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RCODE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RCVMSG, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RDATA, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_RESET, 0, TYPE_NULL)
+ IOCTL(SNDCTL_COPR_RUN, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_SENDMSG, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_WCODE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_COPR_WDATA, IOC_W, MK_PTR(TYPE_INT))
+#endif
+ IOCTL(SNDCTL_DSP_CHANNELS, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETBLKSIZE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETCAPS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETFMTS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_GETIPTR, IOC_R, MK_PTR(MK_STRUCT(STRUCT_count_info)))
+ IOCTL(SNDCTL_DSP_GETOPTR, IOC_R, MK_PTR(MK_STRUCT(STRUCT_count_info)))
+ IOCTL(SNDCTL_DSP_GETISPACE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_audio_buf_info)))
+ IOCTL(SNDCTL_DSP_GETOSPACE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_audio_buf_info)))
+ IOCTL(SNDCTL_DSP_GETTRIGGER, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_MAPINBUF, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_MAPOUTBUF, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_NONBLOCK, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_POST, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_RESET, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_SETDUPLEX, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_SETFMT, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SETFRAGMENT, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SETSYNCRO, 0, TYPE_NULL)
+ IOCTL(SNDCTL_DSP_SETTRIGGER, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SPEED, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_STEREO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SUBDIVIDE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_DSP_SYNC, 0, TYPE_NULL)
+#if 0
+ IOCTL(SNDCTL_FM_4OP_ENABLE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_FM_LOAD_INSTR, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_INFO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_MPUCMD, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_MPUMODE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_MIDI_PRETIME, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_CTRLRATE, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_GETINCOUNT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_GETOUTCOUNT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_NRMIDIS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_NRSYNTHS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_OUTOFBAND, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_PANIC, 0, TYPE_NULL)
+ IOCTL(SNDCTL_SEQ_PERCMODE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_RESET, 0, TYPE_NULL)
+ IOCTL(SNDCTL_SEQ_RESETSAMPLES, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_SYNC, 0, TYPE_NULL)
+ IOCTL(SNDCTL_SEQ_TESTMIDI, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SEQ_THRESHOLD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SYNTH_INFO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_SYNTH_MEMAVL, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_CONTINUE, 0, TYPE_NULL)
+ IOCTL(SNDCTL_TMR_METRONOME, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_SELECT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_SOURCE, IOC_RW, MK_PTR(TYPE_INT))
+#if 0
+ /* we invalidate these defines because they have a same number as
+ termios ioctls */
+ IOCTL(SNDCTL_TMR_START, 0, TYPE_NULL)
+ IOCTL(SNDCTL_TMR_STOP, 0, TYPE_NULL)
+#endif
+ IOCTL(SNDCTL_TMR_TEMPO, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SNDCTL_TMR_TIMEBASE, IOC_RW, MK_PTR(TYPE_INT))
+
+ IOCTL(SOUND_PCM_WRITE_FILTER, IOC_W | IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_RATE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_CHANNELS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_BITS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_PCM_READ_FILTER, IOC_R, MK_PTR(TYPE_INT))
+#endif
+ IOCTL(SOUND_MIXER_INFO, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_ACCESS, 0, TYPE_PTRVOID)
+ IOCTL(SOUND_MIXER_PRIVATE1, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE2, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE3, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE4, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_PRIVATE5, IOC_RW, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_VOLUME, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_BASS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_TREBLE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_SYNTH, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_PCM, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_SPEAKER, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_MIC, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_CD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_IMIX, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_ALTPCM, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_RECLEV, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_IGAIN, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_OGAIN, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE1, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE2, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LINE3, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_MUTE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_ENHANCE, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_LOUD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_RECSRC, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_DEVMASK, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_RECMASK, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_STEREODEVS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_READ_CAPS, IOC_R, MK_PTR(TYPE_INT))
+
+ IOCTL(SOUND_MIXER_WRITE_VOLUME, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_BASS, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_TREBLE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_SYNTH, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_PCM, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_SPEAKER, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_MIC, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_CD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_IMIX, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_ALTPCM, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_RECLEV, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_IGAIN, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_OGAIN, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE1, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE2, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LINE3, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_MUTE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_ENHANCE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_LOUD, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(SOUND_MIXER_WRITE_RECSRC, IOC_W, MK_PTR(TYPE_INT))
+
+ IOCTL(HDIO_GETGEO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_hd_geometry)))
+ IOCTL(HDIO_GET_UNMASKINTR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_MULTCOUNT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_IDENTITY, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_KEEPSETTINGS, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_NOWERR, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_DMA, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_GET_32BIT, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_DRIVE_CMD, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(HDIO_SET_UNMASKINTR, 0, TYPE_INT)
+ IOCTL(HDIO_SET_MULTCOUNT, 0, TYPE_INT)
+ IOCTL(HDIO_SET_KEEPSETTINGS, 0, TYPE_INT)
+ IOCTL(HDIO_SET_NOWERR, 0, TYPE_INT)
+ IOCTL(HDIO_SET_DMA, 0, TYPE_INT)
+ IOCTL(HDIO_SET_32BIT, 0, TYPE_INT)
+ IOCTL(HDIO_SET_PIO_MODE, 0, TYPE_INT)
+
+ IOCTL(VFAT_IOCTL_READDIR_BOTH, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2)))
+ IOCTL(VFAT_IOCTL_READDIR_SHORT, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_dirent), 2)))
+
+ IOCTL(LOOP_SET_FD, 0, TYPE_INT)
+ IOCTL(LOOP_CLR_FD, 0, TYPE_INT)
+ IOCTL(LOOP_SET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
+ IOCTL(LOOP_GET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info)))
+ IOCTL(LOOP_SET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
+ IOCTL(LOOP_GET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64)))
+ IOCTL(LOOP_CHANGE_FD, 0, TYPE_INT)
+
+ IOCTL(MTIOCTOP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_mtop)))
+ IOCTL(MTIOCGET, IOC_R, MK_PTR(MK_STRUCT(STRUCT_mtget)))
+ IOCTL(MTIOCPOS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_mtpos)))
+
+ IOCTL(FBIOGET_FSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_fix_screeninfo)))
+ IOCTL(FBIOGET_VSCREENINFO, IOC_R, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo)))
+ IOCTL(FBIOPUT_VSCREENINFO, IOC_W, MK_PTR(MK_STRUCT(STRUCT_fb_var_screeninfo)))
+
+ IOCTL(VT_OPENQRY, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(VT_GETSTATE, IOC_R, MK_PTR(MK_STRUCT(STRUCT_vt_stat)))
+ IOCTL(VT_ACTIVATE, 0, TYPE_INT)
+ IOCTL(VT_WAITACTIVE, 0, TYPE_INT)
+ IOCTL(VT_LOCKSWITCH, 0, TYPE_INT)
+ IOCTL(VT_UNLOCKSWITCH, 0, TYPE_INT)
diff --git a/linux-user/linux_loop.h b/linux-user/linux_loop.h
new file mode 100644
index 0000000..8974caa
--- /dev/null
+++ b/linux-user/linux_loop.h
@@ -0,0 +1,95 @@
+/* Copied from 2.6.25 kernel headers to avoid problems on older hosts. */
+#ifndef _LINUX_LOOP_H
+#define _LINUX_LOOP_H
+
+/*
+ * include/linux/loop.h
+ *
+ * Written by Theodore Ts'o, 3/29/93.
+ *
+ * Copyright 1993 by Theodore Ts'o. Redistribution of this file is
+ * permitted under the GNU General Public License.
+ */
+
+#define LO_NAME_SIZE 64
+#define LO_KEY_SIZE 32
+
+
+/*
+ * Loop flags
+ */
+enum {
+ LO_FLAGS_READ_ONLY = 1,
+ LO_FLAGS_USE_AOPS = 2,
+ LO_FLAGS_AUTOCLEAR = 4,
+};
+
+#include <linux/version.h>
+#include <asm/posix_types.h> /* for __kernel_old_dev_t */
+#include <asm/types.h> /* for __u64 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) /* This is a guess. */
+#define __kernel_old_dev_t __kernel_dev_t
+#endif
+
+/* Backwards compatibility version */
+struct loop_info {
+ int lo_number; /* ioctl r/o */
+ __kernel_old_dev_t lo_device; /* ioctl r/o */
+ unsigned long lo_inode; /* ioctl r/o */
+ __kernel_old_dev_t lo_rdevice; /* ioctl r/o */
+ int lo_offset;
+ int lo_encrypt_type;
+ int lo_encrypt_key_size; /* ioctl w/o */
+ int lo_flags; /* ioctl r/o */
+ char lo_name[LO_NAME_SIZE];
+ unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+ unsigned long lo_init[2];
+ char reserved[4];
+};
+
+struct loop_info64 {
+ __u64 lo_device; /* ioctl r/o */
+ __u64 lo_inode; /* ioctl r/o */
+ __u64 lo_rdevice; /* ioctl r/o */
+ __u64 lo_offset;
+ __u64 lo_sizelimit;/* bytes, 0 == max available */
+ __u32 lo_number; /* ioctl r/o */
+ __u32 lo_encrypt_type;
+ __u32 lo_encrypt_key_size; /* ioctl w/o */
+ __u32 lo_flags; /* ioctl r/o */
+ __u8 lo_file_name[LO_NAME_SIZE];
+ __u8 lo_crypt_name[LO_NAME_SIZE];
+ __u8 lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+ __u64 lo_init[2];
+};
+
+/*
+ * Loop filter types
+ */
+
+#define LO_CRYPT_NONE 0
+#define LO_CRYPT_XOR 1
+#define LO_CRYPT_DES 2
+#define LO_CRYPT_FISH2 3 /* Twofish encryption */
+#define LO_CRYPT_BLOW 4
+#define LO_CRYPT_CAST128 5
+#define LO_CRYPT_IDEA 6
+#define LO_CRYPT_DUMMY 9
+#define LO_CRYPT_SKIPJACK 10
+#define LO_CRYPT_CRYPTOAPI 18
+#define MAX_LO_CRYPT 20
+
+/*
+ * IOCTL commands --- we will commandeer 0x4C ('L')
+ */
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+#define LOOP_SET_STATUS 0x4C02
+#define LOOP_GET_STATUS 0x4C03
+#define LOOP_SET_STATUS64 0x4C04
+#define LOOP_GET_STATUS64 0x4C05
+#define LOOP_CHANGE_FD 0x4C06
+
+#endif
diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c
new file mode 100644
index 0000000..ac8c486
--- /dev/null
+++ b/linux-user/linuxload.c
@@ -0,0 +1,207 @@
+/* Code for loading Linux executables. Mostly linux kernel code. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "qemu.h"
+
+#define NGROUPS 32
+
+/* ??? This should really be somewhere else. */
+abi_long memcpy_to_target(abi_ulong dest, const void *src,
+ unsigned long len)
+{
+ void *host_ptr;
+
+ host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
+ if (!host_ptr)
+ return -TARGET_EFAULT;
+ memcpy(host_ptr, src, len);
+ unlock_user(host_ptr, dest, 1);
+ return 0;
+}
+
+static int in_group_p(gid_t g)
+{
+ /* return TRUE if we're in the specified group, FALSE otherwise */
+ int ngroup;
+ int i;
+ gid_t grouplist[NGROUPS];
+
+ ngroup = getgroups(NGROUPS, grouplist);
+ for(i = 0; i < ngroup; i++) {
+ if(grouplist[i] == g) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int count(char ** vec)
+{
+ int i;
+
+ for(i = 0; *vec; i++) {
+ vec++;
+ }
+
+ return(i);
+}
+
+static int prepare_binprm(struct linux_binprm *bprm)
+{
+ struct stat st;
+ int mode;
+ int retval, id_change;
+
+ if(fstat(bprm->fd, &st) < 0) {
+ return(-errno);
+ }
+
+ mode = st.st_mode;
+ if(!S_ISREG(mode)) { /* Must be regular file */
+ return(-EACCES);
+ }
+ if(!(mode & 0111)) { /* Must have at least one execute bit set */
+ return(-EACCES);
+ }
+
+ bprm->e_uid = geteuid();
+ bprm->e_gid = getegid();
+ id_change = 0;
+
+ /* Set-uid? */
+ if(mode & S_ISUID) {
+ bprm->e_uid = st.st_uid;
+ if(bprm->e_uid != geteuid()) {
+ id_change = 1;
+ }
+ }
+
+ /* Set-gid? */
+ /*
+ * If setgid is set but no group execute bit then this
+ * is a candidate for mandatory locking, not a setgid
+ * executable.
+ */
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ bprm->e_gid = st.st_gid;
+ if (!in_group_p(bprm->e_gid)) {
+ id_change = 1;
+ }
+ }
+
+ retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE);
+ if (retval < 0) {
+ perror("prepare_binprm");
+ exit(-1);
+ }
+ if (retval < BPRM_BUF_SIZE) {
+ /* Make sure the rest of the loader won't read garbage. */
+ memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval);
+ }
+ return retval;
+}
+
+/* Construct the envp and argv tables on the target stack. */
+abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
+ abi_ulong stringp, int push_ptr)
+{
+ TaskState *ts = (TaskState *)thread_env->opaque;
+ int n = sizeof(abi_ulong);
+ abi_ulong envp;
+ abi_ulong argv;
+
+ sp -= (envc + 1) * n;
+ envp = sp;
+ sp -= (argc + 1) * n;
+ argv = sp;
+ if (push_ptr) {
+ /* FIXME - handle put_user() failures */
+ sp -= n;
+ put_user_ual(envp, sp);
+ sp -= n;
+ put_user_ual(argv, sp);
+ }
+ sp -= n;
+ /* FIXME - handle put_user() failures */
+ put_user_ual(argc, sp);
+ ts->info->arg_start = stringp;
+ while (argc-- > 0) {
+ /* FIXME - handle put_user() failures */
+ put_user_ual(stringp, argv);
+ argv += n;
+ stringp += target_strlen(stringp) + 1;
+ }
+ ts->info->arg_end = stringp;
+ /* FIXME - handle put_user() failures */
+ put_user_ual(0, argv);
+ while (envc-- > 0) {
+ /* FIXME - handle put_user() failures */
+ put_user_ual(stringp, envp);
+ envp += n;
+ stringp += target_strlen(stringp) + 1;
+ }
+ /* FIXME - handle put_user() failures */
+ put_user_ual(0, envp);
+
+ return sp;
+}
+
+int loader_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs, struct image_info *infop,
+ struct linux_binprm *bprm)
+{
+ int retval;
+ int i;
+
+ bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int);
+ memset(bprm->page, 0, sizeof(bprm->page));
+ retval = open(filename, O_RDONLY);
+ if (retval < 0)
+ return retval;
+ bprm->fd = retval;
+ bprm->filename = (char *)filename;
+ bprm->argc = count(argv);
+ bprm->argv = argv;
+ bprm->envc = count(envp);
+ bprm->envp = envp;
+
+ retval = prepare_binprm(bprm);
+
+ if(retval>=0) {
+ if (bprm->buf[0] == 0x7f
+ && bprm->buf[1] == 'E'
+ && bprm->buf[2] == 'L'
+ && bprm->buf[3] == 'F') {
+ retval = load_elf_binary(bprm, regs, infop);
+#if defined(TARGET_HAS_BFLT)
+ } else if (bprm->buf[0] == 'b'
+ && bprm->buf[1] == 'F'
+ && bprm->buf[2] == 'L'
+ && bprm->buf[3] == 'T') {
+ retval = load_flt_binary(bprm,regs,infop);
+#endif
+ } else {
+ fprintf(stderr, "Unknown binary format\n");
+ return -1;
+ }
+ }
+
+ if(retval>=0) {
+ /* success. Initialize important registers */
+ do_init_thread(regs, infop);
+ return retval;
+ }
+
+ /* Something went wrong, return the inode and free the argument pages*/
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) {
+ free(bprm->page[i]);
+ }
+ return(retval);
+}
diff --git a/linux-user/m68k-sim.c b/linux-user/m68k-sim.c
new file mode 100644
index 0000000..d5926ee
--- /dev/null
+++ b/linux-user/m68k-sim.c
@@ -0,0 +1,170 @@
+/*
+ * m68k simulator syscall interface
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC. Written by Paul Brook.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "qemu.h"
+
+#define SYS_EXIT 1
+#define SYS_READ 3
+#define SYS_WRITE 4
+#define SYS_OPEN 5
+#define SYS_CLOSE 6
+#define SYS_BRK 17
+#define SYS_FSTAT 28
+#define SYS_ISATTY 29
+#define SYS_LSEEK 199
+
+struct m68k_sim_stat {
+ uint16_t sim_st_dev;
+ uint16_t sim_st_ino;
+ uint32_t sim_st_mode;
+ uint16_t sim_st_nlink;
+ uint16_t sim_st_uid;
+ uint16_t sim_st_gid;
+ uint16_t sim_st_rdev;
+ uint32_t sim_st_size;
+ uint32_t sim_st_atime;
+ uint32_t sim_st_mtime;
+ uint32_t sim_st_ctime;
+ uint32_t sim_st_blksize;
+ uint32_t sim_st_blocks;
+};
+
+static inline uint32_t check_err(CPUM68KState *env, uint32_t code)
+{
+ env->dregs[0] = code;
+ if (code == (uint32_t)-1) {
+ env->dregs[1] = errno;
+ } else {
+ env->dregs[1] = 0;
+ }
+ return code;
+}
+
+#define SIM_O_APPEND 0x0008
+#define SIM_O_CREAT 0x0200
+#define SIM_O_TRUNC 0x0400
+#define SIM_O_EXCL 0x0800
+#define SIM_O_NONBLOCK 0x4000
+#define SIM_O_NOCTTY 0x8000
+#define SIM_O_SYNC 0x2000
+
+static int translate_openflags(int flags)
+{
+ int hf;
+
+ switch (flags & 3) {
+ case 0: hf = O_RDONLY; break;
+ case 1: hf = O_WRONLY; break;
+ case 2: hf = O_RDWR; break;
+ default: hf = O_RDWR; break;
+ }
+
+ if (flags & SIM_O_APPEND) hf |= O_APPEND;
+ if (flags & SIM_O_CREAT) hf |= O_CREAT;
+ if (flags & SIM_O_TRUNC) hf |= O_TRUNC;
+ if (flags & SIM_O_EXCL) hf |= O_EXCL;
+ if (flags & SIM_O_NONBLOCK) hf |= O_NONBLOCK;
+ if (flags & SIM_O_NOCTTY) hf |= O_NOCTTY;
+ if (flags & SIM_O_SYNC) hf |= O_SYNC;
+
+ return hf;
+}
+
+#define ARG(x) tswap32(args[x])
+void do_m68k_simcall(CPUM68KState *env, int nr)
+{
+ uint32_t *args;
+
+ args = (uint32_t *)(unsigned long)(env->aregs[7] + 4);
+ switch (nr) {
+ case SYS_EXIT:
+ exit(ARG(0));
+ case SYS_READ:
+ check_err(env, read(ARG(0), (void *)(unsigned long)ARG(1), ARG(2)));
+ break;
+ case SYS_WRITE:
+ check_err(env, write(ARG(0), (void *)(unsigned long)ARG(1), ARG(2)));
+ break;
+ case SYS_OPEN:
+ check_err(env, open((char *)(unsigned long)ARG(0),
+ translate_openflags(ARG(1)), ARG(2)));
+ break;
+ case SYS_CLOSE:
+ {
+ /* Ignore attempts to close stdin/out/err. */
+ int fd = ARG(0);
+ if (fd > 2)
+ check_err(env, close(fd));
+ else
+ check_err(env, 0);
+ break;
+ }
+ case SYS_BRK:
+ {
+ int32_t ret;
+
+ ret = do_brk((abi_ulong)ARG(0));
+ if (ret == -ENOMEM)
+ ret = -1;
+ check_err(env, ret);
+ }
+ break;
+ case SYS_FSTAT:
+ {
+ struct stat s;
+ int rc;
+ struct m68k_sim_stat *p;
+ rc = check_err(env, fstat(ARG(0), &s));
+ if (rc == 0) {
+ p = (struct m68k_sim_stat *)(unsigned long)ARG(1);
+ p->sim_st_dev = tswap16(s.st_dev);
+ p->sim_st_ino = tswap16(s.st_ino);
+ p->sim_st_mode = tswap32(s.st_mode);
+ p->sim_st_nlink = tswap16(s.st_nlink);
+ p->sim_st_uid = tswap16(s.st_uid);
+ p->sim_st_gid = tswap16(s.st_gid);
+ p->sim_st_rdev = tswap16(s.st_rdev);
+ p->sim_st_size = tswap32(s.st_size);
+ p->sim_st_atime = tswap32(s.st_atime);
+ p->sim_st_mtime = tswap32(s.st_mtime);
+ p->sim_st_ctime = tswap32(s.st_ctime);
+ p->sim_st_blksize = tswap32(s.st_blksize);
+ p->sim_st_blocks = tswap32(s.st_blocks);
+ }
+ }
+ break;
+ case SYS_ISATTY:
+ check_err(env, isatty(ARG(0)));
+ break;
+ case SYS_LSEEK:
+ check_err(env, lseek(ARG(0), (int32_t)ARG(1), ARG(2)));
+ break;
+ default:
+ cpu_abort(env, "Unsupported m68k sim syscall %d\n", nr);
+ }
+}
diff --git a/linux-user/m68k/syscall.h b/linux-user/m68k/syscall.h
new file mode 100644
index 0000000..2fd85dd
--- /dev/null
+++ b/linux-user/m68k/syscall.h
@@ -0,0 +1,21 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+ abi_long d1, d2, d3, d4, d5, d6, d7;
+ abi_long a0, a1, a2, a3, a4, a5, a6;
+ abi_ulong d0;
+ abi_ulong usp;
+ abi_ulong orig_d0;
+ int16_t stkadj;
+ uint16_t sr;
+ abi_ulong pc;
+ uint16_t fntvex;
+ uint16_t __fill;
+};
+
+
+#define UNAME_MACHINE "m68k"
+
+void do_m68k_simcall(CPUState *, int);
diff --git a/linux-user/m68k/syscall_nr.h b/linux-user/m68k/syscall_nr.h
new file mode 100644
index 0000000..1c0ba07
--- /dev/null
+++ b/linux-user/m68k/syscall_nr.h
@@ -0,0 +1,330 @@
+/*
+ * 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_waitpid 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_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_chown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#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_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#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_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#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_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#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_olduname 109
+//#define TARGET_NR_iopl /* 110 */ not supported
+#define TARGET_NR_vhangup 111
+//#define TARGET_NR_idle /* 112 */ Obsolete
+//#define TARGET_NR_vm86 /* 113 */ not supported
+#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_cacheflush 123
+#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_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#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_setresuid 164
+#define TARGET_NR_getresuid 165
+#define TARGET_NR_getpagesize 166
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#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_lchown 182
+#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 /* some people actually want streams */
+#define TARGET_NR_putpmsg 189 /* some people actually want streams */
+#define TARGET_NR_vfork 190
+#define TARGET_NR_ugetrlimit 191
+#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_chown32 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_lchown32 212
+#define TARGET_NR_setuid32 213
+#define TARGET_NR_setgid32 214
+#define TARGET_NR_setfsuid32 215
+#define TARGET_NR_setfsgid32 216
+#define TARGET_NR_pivot_root 217
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_gettid 221
+#define TARGET_NR_tkill 222
+#define TARGET_NR_setxattr 223
+#define TARGET_NR_lsetxattr 224
+#define TARGET_NR_fsetxattr 225
+#define TARGET_NR_getxattr 226
+#define TARGET_NR_lgetxattr 227
+#define TARGET_NR_fgetxattr 228
+#define TARGET_NR_listxattr 229
+#define TARGET_NR_llistxattr 230
+#define TARGET_NR_flistxattr 231
+#define TARGET_NR_removexattr 232
+#define TARGET_NR_lremovexattr 233
+#define TARGET_NR_fremovexattr 234
+#define TARGET_NR_futex 235
+#define TARGET_NR_sendfile64 236
+#define TARGET_NR_mincore 237
+#define TARGET_NR_madvise 238
+#define TARGET_NR_fcntl64 239
+#define TARGET_NR_readahead 240
+#define TARGET_NR_io_setup 241
+#define TARGET_NR_io_destroy 242
+#define TARGET_NR_io_getevents 243
+#define TARGET_NR_io_submit 244
+#define TARGET_NR_io_cancel 245
+#define TARGET_NR_fadvise64 246
+#define TARGET_NR_exit_group 247
+#define TARGET_NR_lookup_dcookie 248
+#define TARGET_NR_epoll_create 249
+#define TARGET_NR_epoll_ctl 250
+#define TARGET_NR_epoll_wait 251
+#define TARGET_NR_remap_file_pages 252
+#define TARGET_NR_set_tid_address 253
+#define TARGET_NR_timer_create 254
+#define TARGET_NR_timer_settime 255
+#define TARGET_NR_timer_gettime 256
+#define TARGET_NR_timer_getoverrun 257
+#define TARGET_NR_timer_delete 258
+#define TARGET_NR_clock_settime 259
+#define TARGET_NR_clock_gettime 260
+#define TARGET_NR_clock_getres 261
+#define TARGET_NR_clock_nanosleep 262
+#define TARGET_NR_statfs64 263
+#define TARGET_NR_fstatfs64 264
+#define TARGET_NR_tgkill 265
+#define TARGET_NR_utimes 266
+#define TARGET_NR_fadvise64_64 267
+#define TARGET_NR_mbind 268
+#define TARGET_NR_get_mempolicy 269
+#define TARGET_NR_set_mempolicy 270
+#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_waitid 277
+#define TARGET_NR_vserver 278
+#define TARGET_NR_add_key 279
+#define TARGET_NR_request_key 280
+#define TARGET_NR_keyctl 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
+#define TARGET_NR_migrate_pages 287
+#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_fstatat64 293
+#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
+#define TARGET_NR_move_pages 310
+#define TARGET_NR_sched_setaffinity 311
+#define TARGET_NR_sched_getaffinity 312
+#define TARGET_NR_kexec_load 313
+#define TARGET_NR_getcpu 314
+#define TARGET_NR_epoll_pwait 315
+#define TARGET_NR_utimensat 316
+#define TARGET_NR_signalfd 317
+#define TARGET_NR_timerfd 318
+#define TARGET_NR_eventfd 319
+#define TARGET_NR_fallocate 320
+#define TARGET_NR_timerfd_settime 321
+#define TARGET_NR_timerfd_gettime 322
+#define TARGET_NR_signalfd4 323
+#define TARGET_NR_eventfd2 324
+#define TARGET_NR_epoll_create1 325
+#define TARGET_NR_dup3 326
+#define TARGET_NR_pipe2 327
+#define TARGET_NR_inotify_init1 328
diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h
new file mode 100644
index 0000000..479758a
--- /dev/null
+++ b/linux-user/m68k/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long 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(CPUM68KState *state)
+{
+ return state->aregs[7];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/m68k/termbits.h b/linux-user/m68k/termbits.h
new file mode 100644
index 0000000..f7982fb
--- /dev/null
+++ b/linux-user/m68k/termbits.h
@@ -0,0 +1,227 @@
+/* from asm/termbits.h */
+/* NOTE: exactly the same as i386 */
+
+#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 */
+};
+
+/* 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_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 (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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
+
+/* c_cc character offsets */
+#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
+
+/* ioctls */
+
+#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 TARGET_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_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* 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/main.c b/linux-user/main.c
new file mode 100644
index 0000000..0d627d6
--- /dev/null
+++ b/linux-user/main.c
@@ -0,0 +1,3375 @@
+/*
+ * qemu user main
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/resource.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+#include "cache-utils.h"
+/* For tb_lock */
+#include "exec-all.h"
+#include "tcg.h"
+#include "qemu-timer.h"
+#include "envlist.h"
+
+#define DEBUG_LOGFILE "/tmp/qemu.log"
+
+char *exec_path;
+
+int singlestep;
+unsigned long mmap_min_addr;
+#if defined(CONFIG_USE_GUEST_BASE)
+unsigned long guest_base;
+int have_guest_base;
+unsigned long reserved_va;
+#endif
+
+static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
+const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
+
+/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
+ we allocate a bigger stack. Need a better solution, for example
+ by remapping the process stack directly at the right place */
+unsigned long guest_stack_size = 8 * 1024 * 1024UL;
+
+void gemu_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+#if defined(TARGET_I386)
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ return -1;
+}
+#endif
+
+/* timers for rdtsc */
+
+#if 0
+
+static uint64_t emu_time;
+
+int64_t cpu_get_real_ticks(void)
+{
+ return emu_time++;
+}
+
+#endif
+
+#if defined(CONFIG_USE_NPTL)
+/***********************************************************/
+/* Helper routines for implementing atomic operations. */
+
+/* To implement exclusive operations we force all cpus to syncronise.
+ We don't require a full sync, only that no cpus are executing guest code.
+ The alternative is to map target atomic ops onto host equivalents,
+ which requires quite a lot of per host/target work. */
+static pthread_mutex_t cpu_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t exclusive_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t exclusive_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t exclusive_resume = PTHREAD_COND_INITIALIZER;
+static int pending_cpus;
+
+/* Make sure everything is in a consistent state for calling fork(). */
+void fork_start(void)
+{
+ pthread_mutex_lock(&tb_lock);
+ pthread_mutex_lock(&exclusive_lock);
+ mmap_fork_start();
+}
+
+void fork_end(int child)
+{
+ mmap_fork_end(child);
+ if (child) {
+ /* Child processes created by fork() only have a single thread.
+ Discard information about the parent threads. */
+ first_cpu = thread_env;
+ thread_env->next_cpu = NULL;
+ pending_cpus = 0;
+ pthread_mutex_init(&exclusive_lock, NULL);
+ pthread_mutex_init(&cpu_list_mutex, NULL);
+ pthread_cond_init(&exclusive_cond, NULL);
+ pthread_cond_init(&exclusive_resume, NULL);
+ pthread_mutex_init(&tb_lock, NULL);
+ gdbserver_fork(thread_env);
+ } else {
+ pthread_mutex_unlock(&exclusive_lock);
+ pthread_mutex_unlock(&tb_lock);
+ }
+}
+
+/* Wait for pending exclusive operations to complete. The exclusive lock
+ must be held. */
+static inline void exclusive_idle(void)
+{
+ while (pending_cpus) {
+ pthread_cond_wait(&exclusive_resume, &exclusive_lock);
+ }
+}
+
+/* Start an exclusive operation.
+ Must only be called from outside cpu_arm_exec. */
+static inline void start_exclusive(void)
+{
+ CPUState *other;
+ pthread_mutex_lock(&exclusive_lock);
+ exclusive_idle();
+
+ pending_cpus = 1;
+ /* Make all other cpus stop executing. */
+ for (other = first_cpu; other; other = other->next_cpu) {
+ if (other->running) {
+ pending_cpus++;
+ cpu_exit(other);
+ }
+ }
+ if (pending_cpus > 1) {
+ pthread_cond_wait(&exclusive_cond, &exclusive_lock);
+ }
+}
+
+/* Finish an exclusive operation. */
+static inline void end_exclusive(void)
+{
+ pending_cpus = 0;
+ pthread_cond_broadcast(&exclusive_resume);
+ pthread_mutex_unlock(&exclusive_lock);
+}
+
+/* Wait for exclusive ops to finish, and begin cpu execution. */
+static inline void cpu_exec_start(CPUState *env)
+{
+ pthread_mutex_lock(&exclusive_lock);
+ exclusive_idle();
+ env->running = 1;
+ pthread_mutex_unlock(&exclusive_lock);
+}
+
+/* Mark cpu as not executing, and release pending exclusive ops. */
+static inline void cpu_exec_end(CPUState *env)
+{
+ pthread_mutex_lock(&exclusive_lock);
+ env->running = 0;
+ if (pending_cpus > 1) {
+ pending_cpus--;
+ if (pending_cpus == 1) {
+ pthread_cond_signal(&exclusive_cond);
+ }
+ }
+ exclusive_idle();
+ pthread_mutex_unlock(&exclusive_lock);
+}
+
+void cpu_list_lock(void)
+{
+ pthread_mutex_lock(&cpu_list_mutex);
+}
+
+void cpu_list_unlock(void)
+{
+ pthread_mutex_unlock(&cpu_list_mutex);
+}
+#else /* if !CONFIG_USE_NPTL */
+/* These are no-ops because we are not threadsafe. */
+static inline void cpu_exec_start(CPUState *env)
+{
+}
+
+static inline void cpu_exec_end(CPUState *env)
+{
+}
+
+static inline void start_exclusive(void)
+{
+}
+
+static inline void end_exclusive(void)
+{
+}
+
+void fork_start(void)
+{
+}
+
+void fork_end(int child)
+{
+ if (child) {
+ gdbserver_fork(thread_env);
+ }
+}
+
+void cpu_list_lock(void)
+{
+}
+
+void cpu_list_unlock(void)
+{
+}
+#endif
+
+
+#ifdef TARGET_I386
+/***********************************************************/
+/* CPUX86 core interface */
+
+void cpu_smm_update(CPUState *env)
+{
+}
+
+uint64_t cpu_get_tsc(CPUX86State *env)
+{
+ return cpu_get_real_ticks();
+}
+
+static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
+ int flags)
+{
+ unsigned int e1, e2;
+ uint32_t *p;
+ e1 = (addr << 16) | (limit & 0xffff);
+ e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
+ e2 |= flags;
+ p = ptr;
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
+}
+
+static uint64_t *idt_table;
+#ifdef TARGET_X86_64
+static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
+ uint64_t addr, unsigned int sel)
+{
+ uint32_t *p, e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ p = ptr;
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
+ p[2] = tswap32(addr >> 32);
+ p[3] = 0;
+}
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
+}
+#else
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ uint32_t addr, unsigned int sel)
+{
+ uint32_t *p, e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ p = ptr;
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
+}
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
+#endif
+
+void cpu_loop(CPUX86State *env)
+{
+ int trapnr;
+ abi_ulong pc;
+ target_siginfo_t info;
+
+ for(;;) {
+ trapnr = cpu_x86_exec(env);
+ switch(trapnr) {
+ case 0x80:
+ /* linux syscall from int $0x80 */
+ env->regs[R_EAX] = do_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EBX],
+ env->regs[R_ECX],
+ env->regs[R_EDX],
+ env->regs[R_ESI],
+ env->regs[R_EDI],
+ env->regs[R_EBP]);
+ break;
+#ifndef TARGET_ABI32
+ case EXCP_SYSCALL:
+ /* linux syscall from syscall intruction */
+ env->regs[R_EAX] = do_syscall(env,
+ env->regs[R_EAX],
+ env->regs[R_EDI],
+ env->regs[R_ESI],
+ env->regs[R_EDX],
+ env->regs[10],
+ env->regs[8],
+ env->regs[9]);
+ env->eip = env->exception_next_eip;
+ break;
+#endif
+ case EXCP0B_NOSEG:
+ case EXCP0C_STACK:
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP0D_GPF:
+ /* XXX: potential problem if ABI32 */
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_fault(env);
+ } else
+#endif
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP0E_PAGE:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ if (!(env->error_code & 1))
+ info.si_code = TARGET_SEGV_MAPERR;
+ else
+ info.si_code = TARGET_SEGV_ACCERR;
+ info._sifields._sigfault._addr = env->cr[2];
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP00_DIVZ:
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else
+#endif
+ {
+ /* division by zero */
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_INTDIV;
+ info._sifields._sigfault._addr = env->eip;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP01_DB:
+ case EXCP03_INT3:
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else
+#endif
+ {
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ if (trapnr == EXCP01_DB) {
+ info.si_code = TARGET_TRAP_BRKPT;
+ info._sifields._sigfault._addr = env->eip;
+ } else {
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ }
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP04_INTO:
+ case EXCP05_BOUND:
+#ifndef TARGET_X86_64
+ if (env->eflags & VM_MASK) {
+ handle_vm86_trap(env, trapnr);
+ } else
+#endif
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SI_KERNEL;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP06_ILLOP:
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPN;
+ info._sifields._sigfault._addr = env->eip;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ 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;
+ default:
+ pc = env->segs[R_CS].base + env->eip;
+ fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
+ (long)pc, trapnr);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#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)
+{
+ uint32_t addr;
+ uint32_t cpsr;
+ uint32_t val;
+
+ switch (env->regs[15]) {
+ case 0xffff0fa0: /* __kernel_memory_barrier */
+ /* ??? No-op. Will need to do better for SMP. */
+ break;
+ case 0xffff0fc0: /* __kernel_cmpxchg */
+ /* XXX: This only works between threads, not between processes.
+ It's probably possible to implement this with native host
+ operations. However things like ldrex/strex are much harder so
+ there's not much point trying. */
+ start_exclusive();
+ cpsr = cpsr_read(env);
+ addr = env->regs[2];
+ /* FIXME: This should SEGV if the access fails. */
+ if (get_user_u32(val, addr))
+ val = ~env->regs[0];
+ if (val == env->regs[0]) {
+ val = env->regs[1];
+ /* FIXME: Check for segfaults. */
+ put_user_u32(val, addr);
+ env->regs[0] = 0;
+ cpsr |= CPSR_C;
+ } else {
+ env->regs[0] = -1;
+ cpsr &= ~CPSR_C;
+ }
+ cpsr_write(env, cpsr, CPSR_C);
+ end_exclusive();
+ break;
+ case 0xffff0fe0: /* __kernel_get_tls */
+ env->regs[0] = env->cp15.c13_tls2;
+ break;
+ default:
+ return 1;
+ }
+ /* Jump back to the caller. */
+ addr = env->regs[14];
+ if (addr & 1) {
+ env->thumb = 1;
+ addr &= ~1;
+ }
+ env->regs[15] = addr;
+
+ return 0;
+}
+
+static int do_strex(CPUARMState *env)
+{
+ uint32_t val;
+ int size;
+ int rc = 1;
+ int segv = 0;
+ uint32_t addr;
+ start_exclusive();
+ addr = env->exclusive_addr;
+ if (addr != env->exclusive_test) {
+ goto fail;
+ }
+ size = env->exclusive_info & 0xf;
+ switch (size) {
+ case 0:
+ segv = get_user_u8(val, addr);
+ break;
+ case 1:
+ segv = get_user_u16(val, addr);
+ break;
+ case 2:
+ case 3:
+ segv = get_user_u32(val, addr);
+ break;
+ default:
+ abort();
+ }
+ if (segv) {
+ env->cp15.c6_data = addr;
+ goto done;
+ }
+ if (val != env->exclusive_val) {
+ goto fail;
+ }
+ if (size == 3) {
+ segv = get_user_u32(val, addr + 4);
+ if (segv) {
+ env->cp15.c6_data = addr + 4;
+ goto done;
+ }
+ if (val != env->exclusive_high) {
+ goto fail;
+ }
+ }
+ val = env->regs[(env->exclusive_info >> 8) & 0xf];
+ switch (size) {
+ case 0:
+ segv = put_user_u8(val, addr);
+ break;
+ case 1:
+ segv = put_user_u16(val, addr);
+ break;
+ case 2:
+ case 3:
+ segv = put_user_u32(val, addr);
+ break;
+ }
+ if (segv) {
+ env->cp15.c6_data = addr;
+ goto done;
+ }
+ if (size == 3) {
+ val = env->regs[(env->exclusive_info >> 12) & 0xf];
+ segv = put_user_u32(val, addr + 4);
+ if (segv) {
+ env->cp15.c6_data = addr + 4;
+ goto done;
+ }
+ }
+ rc = 0;
+fail:
+ env->regs[15] += 4;
+ env->regs[(env->exclusive_info >> 4) & 0xf] = rc;
+done:
+ end_exclusive();
+ return segv;
+}
+
+void cpu_loop(CPUARMState *env)
+{
+ int trapnr;
+ unsigned int n, insn;
+ target_siginfo_t info;
+ uint32_t addr;
+
+ for(;;) {
+ cpu_exec_start(env);
+ trapnr = cpu_arm_exec(env);
+ cpu_exec_end(env);
+ switch(trapnr) {
+ case EXCP_UDEF:
+ {
+ TaskState *ts = env->opaque;
+ uint32_t opcode;
+ int rc;
+
+ /* we handle the FPU emulation here, as Linux */
+ /* we get the opcode */
+ /* FIXME - what to do if get_user() fails? */
+ get_user_u32(opcode, env->regs[15]);
+
+ rc = EmulateAll(opcode, &ts->fpa, env);
+ if (rc == 0) { /* illegal instruction */
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPN;
+ info._sifields._sigfault._addr = env->regs[15];
+ queue_signal(env, info.si_signo, &info);
+ } else if (rc < 0) { /* FP exception */
+ int arm_fpe=0;
+
+ /* translate softfloat flags to FPSR flags */
+ if (-rc & float_flag_invalid)
+ arm_fpe |= BIT_IOC;
+ if (-rc & float_flag_divbyzero)
+ arm_fpe |= BIT_DZC;
+ if (-rc & float_flag_overflow)
+ arm_fpe |= BIT_OFC;
+ if (-rc & float_flag_underflow)
+ arm_fpe |= BIT_UFC;
+ if (-rc & float_flag_inexact)
+ arm_fpe |= BIT_IXC;
+
+ FPSR fpsr = ts->fpa.fpsr;
+ //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe);
+
+ if (fpsr & (arm_fpe << 16)) { /* exception enabled? */
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+
+ /* ordered by priority, least first */
+ if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES;
+ if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND;
+ if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF;
+ if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV;
+ if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV;
+
+ info._sifields._sigfault._addr = env->regs[15];
+ queue_signal(env, info.si_signo, &info);
+ } else {
+ env->regs[15] += 4;
+ }
+
+ /* accumulate unenabled exceptions */
+ if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC))
+ fpsr |= BIT_IXC;
+ if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC))
+ fpsr |= BIT_UFC;
+ if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC))
+ fpsr |= BIT_OFC;
+ if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC))
+ fpsr |= BIT_DZC;
+ if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC))
+ fpsr |= BIT_IOC;
+ ts->fpa.fpsr=fpsr;
+ } else { /* everything OK */
+ /* increment PC */
+ env->regs[15] += 4;
+ }
+ }
+ break;
+ case EXCP_SWI:
+ case EXCP_BKPT:
+ {
+ env->eabi = 1;
+ /* system call */
+ if (trapnr == EXCP_BKPT) {
+ if (env->thumb) {
+ /* FIXME - what to do if get_user() fails? */
+ get_user_u16(insn, env->regs[15]);
+ n = insn & 0xff;
+ env->regs[15] += 2;
+ } else {
+ /* FIXME - what to do if get_user() fails? */
+ get_user_u32(insn, env->regs[15]);
+ n = (insn & 0xf) | ((insn >> 4) & 0xff0);
+ env->regs[15] += 4;
+ }
+ } else {
+ if (env->thumb) {
+ /* FIXME - what to do if get_user() fails? */
+ get_user_u16(insn, env->regs[15] - 2);
+ n = insn & 0xff;
+ } else {
+ /* FIXME - what to do if get_user() fails? */
+ get_user_u32(insn, env->regs[15] - 4);
+ n = insn & 0xffffff;
+ }
+ }
+
+ if (n == ARM_NR_cacheflush) {
+ arm_cache_flush(env->regs[0], env->regs[1]);
+ } else if (n == ARM_NR_semihosting
+ || n == ARM_NR_thumb_semihosting) {
+ env->regs[0] = do_arm_semihosting (env);
+ } else if (n == 0 || n >= ARM_SYSCALL_BASE
+ || (env->thumb && n == ARM_THUMB_SYSCALL)) {
+ /* linux syscall */
+ if (env->thumb || n == 0) {
+ n = env->regs[7];
+ } else {
+ n -= ARM_SYSCALL_BASE;
+ env->eabi = 0;
+ }
+ if ( n > ARM_NR_BASE) {
+ switch (n) {
+ case ARM_NR_cacheflush:
+ arm_cache_flush(env->regs[0], env->regs[1]);
+ break;
+ case ARM_NR_set_tls:
+ cpu_set_tls(env, env->regs[0]);
+ env->regs[0] = 0;
+ break;
+ default:
+ gemu_log("qemu: Unsupported ARM syscall: 0x%x\n",
+ n);
+ env->regs[0] = -TARGET_ENOSYS;
+ break;
+ }
+ } else {
+ env->regs[0] = do_syscall(env,
+ n,
+ env->regs[0],
+ env->regs[1],
+ env->regs[2],
+ env->regs[3],
+ env->regs[4],
+ env->regs[5]);
+ }
+ } else {
+ goto error;
+ }
+ }
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_PREFETCH_ABORT:
+ addr = env->cp15.c6_insn;
+ goto do_segv;
+ case EXCP_DATA_ABORT:
+ addr = env->cp15.c6_data;
+ goto do_segv;
+ do_segv:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = addr;
+ queue_signal(env, info.si_signo, &info);
+ }
+ 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_KERNEL_TRAP:
+ if (do_kernel_trap(env))
+ goto error;
+ break;
+ case EXCP_STREX:
+ if (do_strex(env)) {
+ addr = env->cp15.c6_data;
+ goto do_segv;
+ }
+ break;
+ default:
+ error:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+
+#endif
+
+#ifdef TARGET_SPARC
+#define SPARC64_STACK_BIAS 2047
+
+//#define DEBUG_WIN
+
+/* WARNING: dealing with register windows _is_ complicated. More info
+ can be found at http://www.sics.se/~psm/sparcstack.html */
+static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
+{
+ index = (index + cwp * 16) % (16 * env->nwindows);
+ /* wrap handling : if cwp is on the last window, then we use the
+ registers 'after' the end */
+ if (index < 8 && env->cwp == env->nwindows - 1)
+ index += 16 * env->nwindows;
+ return index;
+}
+
+/* save the register window 'cwp1' */
+static inline void save_window_offset(CPUSPARCState *env, int cwp1)
+{
+ unsigned int i;
+ abi_ulong sp_ptr;
+
+ sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#ifdef TARGET_SPARC64
+ if (sp_ptr & 3)
+ sp_ptr += SPARC64_STACK_BIAS;
+#endif
+#if defined(DEBUG_WIN)
+ printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n",
+ sp_ptr, cwp1);
+#endif
+ for(i = 0; i < 16; i++) {
+ /* FIXME - what to do if put_user() fails? */
+ put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
+ sp_ptr += sizeof(abi_ulong);
+ }
+}
+
+static void save_window(CPUSPARCState *env)
+{
+#ifndef TARGET_SPARC64
+ unsigned int new_wim;
+ new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) &
+ ((1LL << env->nwindows) - 1);
+ save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
+ env->wim = new_wim;
+#else
+ save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
+ env->cansave++;
+ env->canrestore--;
+#endif
+}
+
+static void restore_window(CPUSPARCState *env)
+{
+#ifndef TARGET_SPARC64
+ unsigned int new_wim;
+#endif
+ unsigned int i, cwp1;
+ abi_ulong sp_ptr;
+
+#ifndef TARGET_SPARC64
+ new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
+ ((1LL << env->nwindows) - 1);
+#endif
+
+ /* restore the invalid window */
+ cwp1 = cpu_cwp_inc(env, env->cwp + 1);
+ sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#ifdef TARGET_SPARC64
+ if (sp_ptr & 3)
+ sp_ptr += SPARC64_STACK_BIAS;
+#endif
+#if defined(DEBUG_WIN)
+ printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n",
+ sp_ptr, cwp1);
+#endif
+ for(i = 0; i < 16; i++) {
+ /* FIXME - what to do if get_user() fails? */
+ get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
+ sp_ptr += sizeof(abi_ulong);
+ }
+#ifdef TARGET_SPARC64
+ env->canrestore++;
+ if (env->cleanwin < env->nwindows - 1)
+ env->cleanwin++;
+ env->cansave--;
+#else
+ env->wim = new_wim;
+#endif
+}
+
+static void flush_windows(CPUSPARCState *env)
+{
+ int offset, cwp1;
+
+ offset = 1;
+ for(;;) {
+ /* if restore would invoke restore_window(), then we can stop */
+ cwp1 = cpu_cwp_inc(env, env->cwp + offset);
+#ifndef TARGET_SPARC64
+ if (env->wim & (1 << cwp1))
+ break;
+#else
+ if (env->canrestore == 0)
+ break;
+ env->cansave++;
+ env->canrestore--;
+#endif
+ save_window_offset(env, cwp1);
+ offset++;
+ }
+ cwp1 = cpu_cwp_inc(env, env->cwp + 1);
+#ifndef TARGET_SPARC64
+ /* set wim so that restore will reload the registers */
+ env->wim = 1 << cwp1;
+#endif
+#if defined(DEBUG_WIN)
+ printf("flush_windows: nb=%d\n", offset - 1);
+#endif
+}
+
+void cpu_loop (CPUSPARCState *env)
+{
+ int trapnr;
+ abi_long ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_sparc_exec (env);
+
+ switch (trapnr) {
+#ifndef TARGET_SPARC64
+ case 0x88:
+ case 0x90:
+#else
+ case 0x110:
+ case 0x16d:
+#endif
+ ret = do_syscall (env, env->gregs[1],
+ env->regwptr[0], env->regwptr[1],
+ env->regwptr[2], env->regwptr[3],
+ env->regwptr[4], env->regwptr[5]);
+ if ((abi_ulong)ret >= (abi_ulong)(-515)) {
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+ env->xcc |= PSR_CARRY;
+#else
+ env->psr |= PSR_CARRY;
+#endif
+ ret = -ret;
+ } else {
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+ env->xcc &= ~PSR_CARRY;
+#else
+ env->psr &= ~PSR_CARRY;
+#endif
+ }
+ env->regwptr[0] = ret;
+ /* next instruction */
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ break;
+ case 0x83: /* flush windows */
+#ifdef TARGET_ABI32
+ case 0x103:
+#endif
+ flush_windows(env);
+ /* next instruction */
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ break;
+#ifndef TARGET_SPARC64
+ case TT_WIN_OVF: /* window overflow */
+ save_window(env);
+ break;
+ case TT_WIN_UNF: /* window underflow */
+ restore_window(env);
+ break;
+ case TT_TFAULT:
+ case TT_DFAULT:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->mmuregs[4];
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+#else
+ case TT_SPILL: /* window overflow */
+ save_window(env);
+ break;
+ case TT_FILL: /* window underflow */
+ restore_window(env);
+ break;
+ case TT_TFAULT:
+ case TT_DFAULT:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ if (trapnr == TT_DFAULT)
+ info._sifields._sigfault._addr = env->dmmuregs[4];
+ else
+ info._sifields._sigfault._addr = cpu_tsptr(env)->tpc;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+#ifndef TARGET_ABI32
+ case 0x16e:
+ flush_windows(env);
+ sparc64_get_context(env);
+ break;
+ case 0x16f:
+ flush_windows(env);
+ sparc64_set_context(env);
+ break;
+#endif
+#endif
+ 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;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+
+#endif
+
+#ifdef TARGET_PPC
+static inline uint64_t cpu_ppc_get_tb (CPUState *env)
+{
+ /* TO FIX */
+ return 0;
+}
+
+uint64_t cpu_ppc_load_tbl (CPUState *env)
+{
+ return cpu_ppc_get_tb(env);
+}
+
+uint32_t cpu_ppc_load_tbu (CPUState *env)
+{
+ return cpu_ppc_get_tb(env) >> 32;
+}
+
+uint64_t cpu_ppc_load_atbl (CPUState *env)
+{
+ return cpu_ppc_get_tb(env);
+}
+
+uint32_t cpu_ppc_load_atbu (CPUState *env)
+{
+ return cpu_ppc_get_tb(env) >> 32;
+}
+
+uint32_t cpu_ppc601_load_rtcu (CPUState *env)
+__attribute__ (( alias ("cpu_ppc_load_tbu") ));
+
+uint32_t cpu_ppc601_load_rtcl (CPUState *env)
+{
+ return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
+}
+
+/* XXX: to be fixed */
+int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
+{
+ return -1;
+}
+
+int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
+{
+ return -1;
+}
+
+#define EXCP_DUMP(env, fmt, ...) \
+do { \
+ fprintf(stderr, fmt , ## __VA_ARGS__); \
+ cpu_dump_state(env, stderr, fprintf, 0); \
+ qemu_log(fmt, ## __VA_ARGS__); \
+ if (logfile) \
+ log_cpu_state(env, 0); \
+} while (0)
+
+static int do_store_exclusive(CPUPPCState *env)
+{
+ target_ulong addr;
+ target_ulong page_addr;
+ target_ulong val;
+ int flags;
+ int segv = 0;
+
+ addr = env->reserve_ea;
+ page_addr = addr & TARGET_PAGE_MASK;
+ start_exclusive();
+ mmap_lock();
+ flags = page_get_flags(page_addr);
+ if ((flags & PAGE_READ) == 0) {
+ segv = 1;
+ } else {
+ int reg = env->reserve_info & 0x1f;
+ int size = (env->reserve_info >> 5) & 0xf;
+ int stored = 0;
+
+ if (addr == env->reserve_addr) {
+ switch (size) {
+ case 1: segv = get_user_u8(val, addr); break;
+ case 2: segv = get_user_u16(val, addr); break;
+ case 4: segv = get_user_u32(val, addr); break;
+#if defined(TARGET_PPC64)
+ case 8: segv = get_user_u64(val, addr); break;
+#endif
+ default: abort();
+ }
+ if (!segv && val == env->reserve_val) {
+ val = env->gpr[reg];
+ switch (size) {
+ case 1: segv = put_user_u8(val, addr); break;
+ case 2: segv = put_user_u16(val, addr); break;
+ case 4: segv = put_user_u32(val, addr); break;
+#if defined(TARGET_PPC64)
+ case 8: segv = put_user_u64(val, addr); break;
+#endif
+ default: abort();
+ }
+ if (!segv) {
+ stored = 1;
+ }
+ }
+ }
+ env->crf[0] = (stored << 1) | xer_so;
+ env->reserve_addr = (target_ulong)-1;
+ }
+ if (!segv) {
+ env->nip += 4;
+ }
+ mmap_unlock();
+ end_exclusive();
+ return segv;
+}
+
+void cpu_loop(CPUPPCState *env)
+{
+ target_siginfo_t info;
+ int trapnr;
+ uint32_t ret;
+
+ for(;;) {
+ cpu_exec_start(env);
+ trapnr = cpu_ppc_exec(env);
+ cpu_exec_end(env);
+ switch(trapnr) {
+ case POWERPC_EXCP_NONE:
+ /* Just go on */
+ break;
+ case POWERPC_EXCP_CRITICAL: /* Critical input */
+ cpu_abort(env, "Critical interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ cpu_abort(env, "Machine check exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ EXCP_DUMP(env, "Invalid data memory access: 0x" TARGET_FMT_lx "\n",
+ env->spr[SPR_DAR]);
+ /* XXX: check this. Seems bugged */
+ switch (env->error_code & 0xFF000000) {
+ case 0x40000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ case 0x04000000:
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLADR;
+ break;
+ case 0x08000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_ACCERR;
+ break;
+ default:
+ /* Let's send a regular segfault... */
+ EXCP_DUMP(env, "Invalid segfault errno (%02x)\n",
+ env->error_code);
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ }
+ info._sifields._sigfault._addr = env->nip;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ EXCP_DUMP(env, "Invalid instruction fetch: 0x\n" TARGET_FMT_lx
+ "\n", env->spr[SPR_SRR0]);
+ /* XXX: check this */
+ switch (env->error_code & 0xFF000000) {
+ case 0x40000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ case 0x10000000:
+ case 0x08000000:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_ACCERR;
+ break;
+ default:
+ /* Let's send a regular segfault... */
+ EXCP_DUMP(env, "Invalid segfault errno (%02x)\n",
+ env->error_code);
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ break;
+ }
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ cpu_abort(env, "External interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ EXCP_DUMP(env, "Unaligned memory access\n");
+ /* XXX: check this */
+ info.si_signo = TARGET_SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_BUS_ADRALN;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ /* XXX: check this */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ EXCP_DUMP(env, "Floating point program exception\n");
+ info.si_signo = TARGET_SIGFPE;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case POWERPC_EXCP_FP_OX:
+ info.si_code = TARGET_FPE_FLTOVF;
+ break;
+ case POWERPC_EXCP_FP_UX:
+ info.si_code = TARGET_FPE_FLTUND;
+ break;
+ case POWERPC_EXCP_FP_ZX:
+ case POWERPC_EXCP_FP_VXZDZ:
+ info.si_code = TARGET_FPE_FLTDIV;
+ break;
+ case POWERPC_EXCP_FP_XX:
+ info.si_code = TARGET_FPE_FLTRES;
+ break;
+ case POWERPC_EXCP_FP_VXSOFT:
+ info.si_code = TARGET_FPE_FLTINV;
+ break;
+ case POWERPC_EXCP_FP_VXSNAN:
+ case POWERPC_EXCP_FP_VXISI:
+ case POWERPC_EXCP_FP_VXIDI:
+ case POWERPC_EXCP_FP_VXIMZ:
+ case POWERPC_EXCP_FP_VXVC:
+ case POWERPC_EXCP_FP_VXSQRT:
+ case POWERPC_EXCP_FP_VXCVI:
+ info.si_code = TARGET_FPE_FLTSUB;
+ break;
+ default:
+ EXCP_DUMP(env, "Unknown floating point exception (%02x)\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_INVAL:
+ EXCP_DUMP(env, "Invalid instruction\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case POWERPC_EXCP_INVAL_INVAL:
+ info.si_code = TARGET_ILL_ILLOPC;
+ break;
+ case POWERPC_EXCP_INVAL_LSWX:
+ info.si_code = TARGET_ILL_ILLOPN;
+ break;
+ case POWERPC_EXCP_INVAL_SPR:
+ info.si_code = TARGET_ILL_PRVREG;
+ break;
+ case POWERPC_EXCP_INVAL_FP:
+ info.si_code = TARGET_ILL_COPROC;
+ break;
+ default:
+ EXCP_DUMP(env, "Unknown invalid operation (%02x)\n",
+ env->error_code & 0xF);
+ info.si_code = TARGET_ILL_ILLADR;
+ break;
+ }
+ break;
+ case POWERPC_EXCP_PRIV:
+ EXCP_DUMP(env, "Privilege violation\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ switch (env->error_code & 0xF) {
+ case POWERPC_EXCP_PRIV_OPC:
+ info.si_code = TARGET_ILL_PRVOPC;
+ break;
+ case POWERPC_EXCP_PRIV_REG:
+ info.si_code = TARGET_ILL_PRVREG;
+ break;
+ default:
+ EXCP_DUMP(env, "Unknown privilege violation (%02x)\n",
+ env->error_code & 0xF);
+ info.si_code = TARGET_ILL_PRVOPC;
+ break;
+ }
+ break;
+ case POWERPC_EXCP_TRAP:
+ cpu_abort(env, "Tried to call a TRAP\n");
+ break;
+ default:
+ /* Should not happen ! */
+ cpu_abort(env, "Unknown program exception (%02x)\n",
+ env->error_code);
+ break;
+ }
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
+ EXCP_DUMP(env, "No floating point allowed\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_COPROC;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ cpu_abort(env, "Syscall exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
+ EXCP_DUMP(env, "No APU instruction allowed\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_COPROC;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ cpu_abort(env, "Decrementer interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
+ cpu_abort(env, "Fix interval timer interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
+ cpu_abort(env, "Watchdog timer interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DTLB: /* Data TLB error */
+ cpu_abort(env, "Data TLB exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_ITLB: /* Instruction TLB error */
+ cpu_abort(env, "Instruction TLB exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */
+ EXCP_DUMP(env, "No SPE/floating-point instruction allowed\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_COPROC;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */
+ cpu_abort(env, "Embedded floating-point data IRQ not handled\n");
+ break;
+ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */
+ cpu_abort(env, "Embedded floating-point round IRQ not handled\n");
+ break;
+ case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */
+ cpu_abort(env, "Performance monitor exception not handled\n");
+ break;
+ case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
+ cpu_abort(env, "Doorbell interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
+ cpu_abort(env, "Doorbell critical interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ cpu_abort(env, "Reset interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_DSEG: /* Data segment exception */
+ cpu_abort(env, "Data segment exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_ISEG: /* Instruction segment exception */
+ cpu_abort(env, "Instruction segment exception "
+ "while in user mode. Aborting\n");
+ break;
+ /* PowerPC 64 with hypervisor mode support */
+ case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
+ cpu_abort(env, "Hypervisor decrementer interrupt "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ /* Nothing to do:
+ * we use this exception to emulate step-by-step execution mode.
+ */
+ break;
+ /* PowerPC 64 with hypervisor mode support */
+ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
+ cpu_abort(env, "Hypervisor data storage exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */
+ cpu_abort(env, "Hypervisor instruction storage exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */
+ cpu_abort(env, "Hypervisor data segment exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */
+ cpu_abort(env, "Hypervisor instruction segment exception "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_VPU: /* Vector unavailable exception */
+ EXCP_DUMP(env, "No Altivec instructions allowed\n");
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_COPROC;
+ info._sifields._sigfault._addr = env->nip - 4;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */
+ cpu_abort(env, "Programable interval timer interrupt "
+ "while in user mode. Aborting\n");
+ break;
+ case POWERPC_EXCP_IO: /* IO error exception */
+ cpu_abort(env, "IO error exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_RUNM: /* Run mode exception */
+ cpu_abort(env, "Run mode exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_EMUL: /* Emulation trap exception */
+ cpu_abort(env, "Emulation trap exception not handled\n");
+ break;
+ case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
+ cpu_abort(env, "Instruction fetch TLB exception "
+ "while in user-mode. Aborting");
+ break;
+ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
+ cpu_abort(env, "Data load TLB exception while in user-mode. "
+ "Aborting");
+ break;
+ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
+ cpu_abort(env, "Data store TLB exception while in user-mode. "
+ "Aborting");
+ break;
+ case POWERPC_EXCP_FPA: /* Floating-point assist exception */
+ cpu_abort(env, "Floating-point assist exception not handled\n");
+ break;
+ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
+ cpu_abort(env, "Instruction address breakpoint exception "
+ "not handled\n");
+ break;
+ case POWERPC_EXCP_SMI: /* System management interrupt */
+ cpu_abort(env, "System management interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_THERM: /* Thermal interrupt */
+ cpu_abort(env, "Thermal interrupt interrupt while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */
+ cpu_abort(env, "Performance monitor exception not handled\n");
+ break;
+ case POWERPC_EXCP_VPUA: /* Vector assist exception */
+ cpu_abort(env, "Vector assist exception not handled\n");
+ break;
+ case POWERPC_EXCP_SOFTP: /* Soft patch exception */
+ cpu_abort(env, "Soft patch exception not handled\n");
+ break;
+ case POWERPC_EXCP_MAINT: /* Maintenance exception */
+ cpu_abort(env, "Maintenance exception while in user mode. "
+ "Aborting\n");
+ break;
+ case POWERPC_EXCP_STOP: /* stop translation */
+ /* We did invalidate the instruction cache. Go on */
+ break;
+ case POWERPC_EXCP_BRANCH: /* branch instruction: */
+ /* We just stopped because of a branch. Go on */
+ break;
+ case POWERPC_EXCP_SYSCALL_USER:
+ /* system call in user-mode emulation */
+ /* WARNING:
+ * PPC ABI uses overflow flag in cr0 to signal an error
+ * in syscalls.
+ */
+#if 0
+ printf("syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", env->gpr[0],
+ env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6]);
+#endif
+ env->crf[0] &= ~0x1;
+ ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->gpr[7],
+ env->gpr[8]);
+ if (ret == (uint32_t)(-TARGET_QEMU_ESIGRETURN)) {
+ /* Returning from a successful sigreturn syscall.
+ Avoid corrupting register state. */
+ break;
+ }
+ if (ret > (uint32_t)(-515)) {
+ env->crf[0] |= 0x1;
+ ret = -ret;
+ }
+ env->gpr[3] = ret;
+#if 0
+ printf("syscall returned 0x%08x (%d)\n", ret, ret);
+#endif
+ break;
+ case POWERPC_EXCP_STCX:
+ if (do_store_exclusive(env)) {
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->nip;
+ queue_signal(env, info.si_signo, &info);
+ }
+ 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_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ default:
+ cpu_abort(env, "Unknown exception 0x%d. Aborting\n", trapnr);
+ break;
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#ifdef TARGET_MIPS
+
+#define MIPS_SYS(name, args) args,
+
+static const uint8_t mips_syscall_args[] = {
+ MIPS_SYS(sys_syscall , 0) /* 4000 */
+ MIPS_SYS(sys_exit , 1)
+ MIPS_SYS(sys_fork , 0)
+ MIPS_SYS(sys_read , 3)
+ MIPS_SYS(sys_write , 3)
+ MIPS_SYS(sys_open , 3) /* 4005 */
+ MIPS_SYS(sys_close , 1)
+ MIPS_SYS(sys_waitpid , 3)
+ MIPS_SYS(sys_creat , 2)
+ MIPS_SYS(sys_link , 2)
+ MIPS_SYS(sys_unlink , 1) /* 4010 */
+ MIPS_SYS(sys_execve , 0)
+ MIPS_SYS(sys_chdir , 1)
+ MIPS_SYS(sys_time , 1)
+ MIPS_SYS(sys_mknod , 3)
+ MIPS_SYS(sys_chmod , 2) /* 4015 */
+ MIPS_SYS(sys_lchown , 3)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */
+ MIPS_SYS(sys_lseek , 3)
+ MIPS_SYS(sys_getpid , 0) /* 4020 */
+ MIPS_SYS(sys_mount , 5)
+ MIPS_SYS(sys_oldumount , 1)
+ MIPS_SYS(sys_setuid , 1)
+ MIPS_SYS(sys_getuid , 0)
+ MIPS_SYS(sys_stime , 1) /* 4025 */
+ MIPS_SYS(sys_ptrace , 4)
+ MIPS_SYS(sys_alarm , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */
+ MIPS_SYS(sys_pause , 0)
+ MIPS_SYS(sys_utime , 2) /* 4030 */
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_access , 2)
+ MIPS_SYS(sys_nice , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4035 */
+ MIPS_SYS(sys_sync , 0)
+ MIPS_SYS(sys_kill , 2)
+ MIPS_SYS(sys_rename , 2)
+ MIPS_SYS(sys_mkdir , 2)
+ MIPS_SYS(sys_rmdir , 1) /* 4040 */
+ MIPS_SYS(sys_dup , 1)
+ MIPS_SYS(sys_pipe , 0)
+ MIPS_SYS(sys_times , 1)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_brk , 1) /* 4045 */
+ MIPS_SYS(sys_setgid , 1)
+ MIPS_SYS(sys_getgid , 0)
+ MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */
+ MIPS_SYS(sys_geteuid , 0)
+ MIPS_SYS(sys_getegid , 0) /* 4050 */
+ MIPS_SYS(sys_acct , 0)
+ MIPS_SYS(sys_umount , 2)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ioctl , 3)
+ MIPS_SYS(sys_fcntl , 3) /* 4055 */
+ MIPS_SYS(sys_ni_syscall , 2)
+ MIPS_SYS(sys_setpgid , 2)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_olduname , 1)
+ MIPS_SYS(sys_umask , 1) /* 4060 */
+ MIPS_SYS(sys_chroot , 1)
+ MIPS_SYS(sys_ustat , 2)
+ MIPS_SYS(sys_dup2 , 2)
+ MIPS_SYS(sys_getppid , 0)
+ MIPS_SYS(sys_getpgrp , 0) /* 4065 */
+ MIPS_SYS(sys_setsid , 0)
+ MIPS_SYS(sys_sigaction , 3)
+ MIPS_SYS(sys_sgetmask , 0)
+ MIPS_SYS(sys_ssetmask , 1)
+ MIPS_SYS(sys_setreuid , 2) /* 4070 */
+ MIPS_SYS(sys_setregid , 2)
+ MIPS_SYS(sys_sigsuspend , 0)
+ MIPS_SYS(sys_sigpending , 1)
+ MIPS_SYS(sys_sethostname , 2)
+ MIPS_SYS(sys_setrlimit , 2) /* 4075 */
+ MIPS_SYS(sys_getrlimit , 2)
+ MIPS_SYS(sys_getrusage , 2)
+ MIPS_SYS(sys_gettimeofday, 2)
+ MIPS_SYS(sys_settimeofday, 2)
+ MIPS_SYS(sys_getgroups , 2) /* 4080 */
+ MIPS_SYS(sys_setgroups , 2)
+ MIPS_SYS(sys_ni_syscall , 0) /* old_select */
+ MIPS_SYS(sys_symlink , 2)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */
+ MIPS_SYS(sys_readlink , 3) /* 4085 */
+ MIPS_SYS(sys_uselib , 1)
+ MIPS_SYS(sys_swapon , 2)
+ MIPS_SYS(sys_reboot , 3)
+ MIPS_SYS(old_readdir , 3)
+ MIPS_SYS(old_mmap , 6) /* 4090 */
+ MIPS_SYS(sys_munmap , 2)
+ MIPS_SYS(sys_truncate , 2)
+ MIPS_SYS(sys_ftruncate , 2)
+ MIPS_SYS(sys_fchmod , 2)
+ MIPS_SYS(sys_fchown , 3) /* 4095 */
+ MIPS_SYS(sys_getpriority , 2)
+ MIPS_SYS(sys_setpriority , 3)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_statfs , 2)
+ MIPS_SYS(sys_fstatfs , 2) /* 4100 */
+ MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */
+ MIPS_SYS(sys_socketcall , 2)
+ MIPS_SYS(sys_syslog , 3)
+ MIPS_SYS(sys_setitimer , 3)
+ MIPS_SYS(sys_getitimer , 2) /* 4105 */
+ MIPS_SYS(sys_newstat , 2)
+ MIPS_SYS(sys_newlstat , 2)
+ MIPS_SYS(sys_newfstat , 2)
+ MIPS_SYS(sys_uname , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */
+ MIPS_SYS(sys_vhangup , 0)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */
+ MIPS_SYS(sys_wait4 , 4)
+ MIPS_SYS(sys_swapoff , 1) /* 4115 */
+ MIPS_SYS(sys_sysinfo , 1)
+ MIPS_SYS(sys_ipc , 6)
+ MIPS_SYS(sys_fsync , 1)
+ MIPS_SYS(sys_sigreturn , 0)
+ MIPS_SYS(sys_clone , 6) /* 4120 */
+ MIPS_SYS(sys_setdomainname, 2)
+ MIPS_SYS(sys_newuname , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */
+ MIPS_SYS(sys_adjtimex , 1)
+ MIPS_SYS(sys_mprotect , 3) /* 4125 */
+ MIPS_SYS(sys_sigprocmask , 3)
+ MIPS_SYS(sys_ni_syscall , 0) /* was create_module */
+ MIPS_SYS(sys_init_module , 5)
+ MIPS_SYS(sys_delete_module, 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */
+ MIPS_SYS(sys_quotactl , 0)
+ MIPS_SYS(sys_getpgid , 1)
+ MIPS_SYS(sys_fchdir , 1)
+ MIPS_SYS(sys_bdflush , 2)
+ MIPS_SYS(sys_sysfs , 3) /* 4135 */
+ MIPS_SYS(sys_personality , 1)
+ MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */
+ MIPS_SYS(sys_setfsuid , 1)
+ MIPS_SYS(sys_setfsgid , 1)
+ MIPS_SYS(sys_llseek , 5) /* 4140 */
+ MIPS_SYS(sys_getdents , 3)
+ MIPS_SYS(sys_select , 5)
+ MIPS_SYS(sys_flock , 2)
+ MIPS_SYS(sys_msync , 3)
+ MIPS_SYS(sys_readv , 3) /* 4145 */
+ MIPS_SYS(sys_writev , 3)
+ MIPS_SYS(sys_cacheflush , 3)
+ MIPS_SYS(sys_cachectl , 3)
+ MIPS_SYS(sys_sysmips , 4)
+ MIPS_SYS(sys_ni_syscall , 0) /* 4150 */
+ MIPS_SYS(sys_getsid , 1)
+ MIPS_SYS(sys_fdatasync , 0)
+ MIPS_SYS(sys_sysctl , 1)
+ MIPS_SYS(sys_mlock , 2)
+ MIPS_SYS(sys_munlock , 2) /* 4155 */
+ MIPS_SYS(sys_mlockall , 1)
+ MIPS_SYS(sys_munlockall , 0)
+ MIPS_SYS(sys_sched_setparam, 2)
+ MIPS_SYS(sys_sched_getparam, 2)
+ MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */
+ MIPS_SYS(sys_sched_getscheduler, 1)
+ MIPS_SYS(sys_sched_yield , 0)
+ MIPS_SYS(sys_sched_get_priority_max, 1)
+ MIPS_SYS(sys_sched_get_priority_min, 1)
+ MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */
+ MIPS_SYS(sys_nanosleep, 2)
+ MIPS_SYS(sys_mremap , 4)
+ MIPS_SYS(sys_accept , 3)
+ MIPS_SYS(sys_bind , 3)
+ MIPS_SYS(sys_connect , 3) /* 4170 */
+ MIPS_SYS(sys_getpeername , 3)
+ MIPS_SYS(sys_getsockname , 3)
+ MIPS_SYS(sys_getsockopt , 5)
+ MIPS_SYS(sys_listen , 2)
+ MIPS_SYS(sys_recv , 4) /* 4175 */
+ MIPS_SYS(sys_recvfrom , 6)
+ MIPS_SYS(sys_recvmsg , 3)
+ MIPS_SYS(sys_send , 4)
+ MIPS_SYS(sys_sendmsg , 3)
+ MIPS_SYS(sys_sendto , 6) /* 4180 */
+ MIPS_SYS(sys_setsockopt , 5)
+ MIPS_SYS(sys_shutdown , 2)
+ MIPS_SYS(sys_socket , 3)
+ MIPS_SYS(sys_socketpair , 4)
+ MIPS_SYS(sys_setresuid , 3) /* 4185 */
+ MIPS_SYS(sys_getresuid , 3)
+ MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */
+ MIPS_SYS(sys_poll , 3)
+ MIPS_SYS(sys_nfsservctl , 3)
+ MIPS_SYS(sys_setresgid , 3) /* 4190 */
+ MIPS_SYS(sys_getresgid , 3)
+ MIPS_SYS(sys_prctl , 5)
+ MIPS_SYS(sys_rt_sigreturn, 0)
+ MIPS_SYS(sys_rt_sigaction, 4)
+ MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */
+ MIPS_SYS(sys_rt_sigpending, 2)
+ MIPS_SYS(sys_rt_sigtimedwait, 4)
+ MIPS_SYS(sys_rt_sigqueueinfo, 3)
+ MIPS_SYS(sys_rt_sigsuspend, 0)
+ MIPS_SYS(sys_pread64 , 6) /* 4200 */
+ MIPS_SYS(sys_pwrite64 , 6)
+ MIPS_SYS(sys_chown , 3)
+ MIPS_SYS(sys_getcwd , 2)
+ MIPS_SYS(sys_capget , 2)
+ MIPS_SYS(sys_capset , 2) /* 4205 */
+ MIPS_SYS(sys_sigaltstack , 0)
+ MIPS_SYS(sys_sendfile , 4)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_mmap2 , 6) /* 4210 */
+ MIPS_SYS(sys_truncate64 , 4)
+ MIPS_SYS(sys_ftruncate64 , 4)
+ MIPS_SYS(sys_stat64 , 2)
+ MIPS_SYS(sys_lstat64 , 2)
+ MIPS_SYS(sys_fstat64 , 2) /* 4215 */
+ MIPS_SYS(sys_pivot_root , 2)
+ MIPS_SYS(sys_mincore , 3)
+ MIPS_SYS(sys_madvise , 3)
+ MIPS_SYS(sys_getdents64 , 3)
+ MIPS_SYS(sys_fcntl64 , 3) /* 4220 */
+ MIPS_SYS(sys_ni_syscall , 0)
+ MIPS_SYS(sys_gettid , 0)
+ MIPS_SYS(sys_readahead , 5)
+ MIPS_SYS(sys_setxattr , 5)
+ MIPS_SYS(sys_lsetxattr , 5) /* 4225 */
+ MIPS_SYS(sys_fsetxattr , 5)
+ MIPS_SYS(sys_getxattr , 4)
+ MIPS_SYS(sys_lgetxattr , 4)
+ MIPS_SYS(sys_fgetxattr , 4)
+ MIPS_SYS(sys_listxattr , 3) /* 4230 */
+ MIPS_SYS(sys_llistxattr , 3)
+ MIPS_SYS(sys_flistxattr , 3)
+ MIPS_SYS(sys_removexattr , 2)
+ MIPS_SYS(sys_lremovexattr, 2)
+ MIPS_SYS(sys_fremovexattr, 2) /* 4235 */
+ MIPS_SYS(sys_tkill , 2)
+ MIPS_SYS(sys_sendfile64 , 5)
+ MIPS_SYS(sys_futex , 2)
+ MIPS_SYS(sys_sched_setaffinity, 3)
+ MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */
+ MIPS_SYS(sys_io_setup , 2)
+ MIPS_SYS(sys_io_destroy , 1)
+ MIPS_SYS(sys_io_getevents, 5)
+ MIPS_SYS(sys_io_submit , 3)
+ MIPS_SYS(sys_io_cancel , 3) /* 4245 */
+ MIPS_SYS(sys_exit_group , 1)
+ MIPS_SYS(sys_lookup_dcookie, 3)
+ MIPS_SYS(sys_epoll_create, 1)
+ MIPS_SYS(sys_epoll_ctl , 4)
+ MIPS_SYS(sys_epoll_wait , 3) /* 4250 */
+ MIPS_SYS(sys_remap_file_pages, 5)
+ MIPS_SYS(sys_set_tid_address, 1)
+ MIPS_SYS(sys_restart_syscall, 0)
+ MIPS_SYS(sys_fadvise64_64, 7)
+ MIPS_SYS(sys_statfs64 , 3) /* 4255 */
+ MIPS_SYS(sys_fstatfs64 , 2)
+ MIPS_SYS(sys_timer_create, 3)
+ MIPS_SYS(sys_timer_settime, 4)
+ MIPS_SYS(sys_timer_gettime, 2)
+ MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */
+ MIPS_SYS(sys_timer_delete, 1)
+ MIPS_SYS(sys_clock_settime, 2)
+ MIPS_SYS(sys_clock_gettime, 2)
+ MIPS_SYS(sys_clock_getres, 2)
+ MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */
+ MIPS_SYS(sys_tgkill , 3)
+ MIPS_SYS(sys_utimes , 2)
+ MIPS_SYS(sys_mbind , 4)
+ MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */
+ MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */
+ MIPS_SYS(sys_mq_open , 4)
+ MIPS_SYS(sys_mq_unlink , 1)
+ MIPS_SYS(sys_mq_timedsend, 5)
+ MIPS_SYS(sys_mq_timedreceive, 5)
+ MIPS_SYS(sys_mq_notify , 2) /* 4275 */
+ MIPS_SYS(sys_mq_getsetattr, 3)
+ MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */
+ MIPS_SYS(sys_waitid , 4)
+ MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */
+ MIPS_SYS(sys_add_key , 5)
+ MIPS_SYS(sys_request_key, 4)
+ MIPS_SYS(sys_keyctl , 5)
+ MIPS_SYS(sys_set_thread_area, 1)
+ MIPS_SYS(sys_inotify_init, 0)
+ MIPS_SYS(sys_inotify_add_watch, 3) /* 4285 */
+ MIPS_SYS(sys_inotify_rm_watch, 2)
+ MIPS_SYS(sys_migrate_pages, 4)
+ MIPS_SYS(sys_openat, 4)
+ MIPS_SYS(sys_mkdirat, 3)
+ MIPS_SYS(sys_mknodat, 4) /* 4290 */
+ MIPS_SYS(sys_fchownat, 5)
+ MIPS_SYS(sys_futimesat, 3)
+ MIPS_SYS(sys_fstatat64, 4)
+ MIPS_SYS(sys_unlinkat, 3)
+ MIPS_SYS(sys_renameat, 4) /* 4295 */
+ MIPS_SYS(sys_linkat, 5)
+ MIPS_SYS(sys_symlinkat, 3)
+ MIPS_SYS(sys_readlinkat, 4)
+ MIPS_SYS(sys_fchmodat, 3)
+ MIPS_SYS(sys_faccessat, 3) /* 4300 */
+ MIPS_SYS(sys_pselect6, 6)
+ MIPS_SYS(sys_ppoll, 5)
+ MIPS_SYS(sys_unshare, 1)
+ MIPS_SYS(sys_splice, 4)
+ MIPS_SYS(sys_sync_file_range, 7) /* 4305 */
+ MIPS_SYS(sys_tee, 4)
+ MIPS_SYS(sys_vmsplice, 4)
+ MIPS_SYS(sys_move_pages, 6)
+ MIPS_SYS(sys_set_robust_list, 2)
+ MIPS_SYS(sys_get_robust_list, 3) /* 4310 */
+ MIPS_SYS(sys_kexec_load, 4)
+ MIPS_SYS(sys_getcpu, 3)
+ MIPS_SYS(sys_epoll_pwait, 6)
+ MIPS_SYS(sys_ioprio_set, 3)
+ MIPS_SYS(sys_ioprio_get, 2)
+};
+
+#undef MIPS_SYS
+
+static int do_store_exclusive(CPUMIPSState *env)
+{
+ target_ulong addr;
+ target_ulong page_addr;
+ target_ulong val;
+ int flags;
+ int segv = 0;
+ int reg;
+ int d;
+
+ addr = env->lladdr;
+ page_addr = addr & TARGET_PAGE_MASK;
+ start_exclusive();
+ mmap_lock();
+ flags = page_get_flags(page_addr);
+ if ((flags & PAGE_READ) == 0) {
+ segv = 1;
+ } else {
+ reg = env->llreg & 0x1f;
+ d = (env->llreg & 0x20) != 0;
+ if (d) {
+ segv = get_user_s64(val, addr);
+ } else {
+ segv = get_user_s32(val, addr);
+ }
+ if (!segv) {
+ if (val != env->llval) {
+ env->active_tc.gpr[reg] = 0;
+ } else {
+ if (d) {
+ segv = put_user_u64(env->llnewval, addr);
+ } else {
+ segv = put_user_u32(env->llnewval, addr);
+ }
+ if (!segv) {
+ env->active_tc.gpr[reg] = 1;
+ }
+ }
+ }
+ }
+ env->lladdr = -1;
+ if (!segv) {
+ env->active_tc.PC += 4;
+ }
+ mmap_unlock();
+ end_exclusive();
+ return segv;
+}
+
+void cpu_loop(CPUMIPSState *env)
+{
+ target_siginfo_t info;
+ int trapnr, ret;
+ unsigned int syscall_num;
+
+ for(;;) {
+ cpu_exec_start(env);
+ trapnr = cpu_mips_exec(env);
+ cpu_exec_end(env);
+ switch(trapnr) {
+ case EXCP_SYSCALL:
+ syscall_num = env->active_tc.gpr[2] - 4000;
+ env->active_tc.PC += 4;
+ if (syscall_num >= sizeof(mips_syscall_args)) {
+ ret = -ENOSYS;
+ } else {
+ int nb_args;
+ abi_ulong sp_reg;
+ abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0;
+
+ nb_args = mips_syscall_args[syscall_num];
+ sp_reg = env->active_tc.gpr[29];
+ switch (nb_args) {
+ /* these arguments are taken from the stack */
+ /* FIXME - what to do if get_user() fails? */
+ case 8: get_user_ual(arg8, sp_reg + 28);
+ case 7: get_user_ual(arg7, sp_reg + 24);
+ case 6: get_user_ual(arg6, sp_reg + 20);
+ case 5: get_user_ual(arg5, sp_reg + 16);
+ default:
+ break;
+ }
+ ret = do_syscall(env, env->active_tc.gpr[2],
+ env->active_tc.gpr[4],
+ env->active_tc.gpr[5],
+ env->active_tc.gpr[6],
+ env->active_tc.gpr[7],
+ arg5, arg6/*, arg7, arg8*/);
+ }
+ if (ret == -TARGET_QEMU_ESIGRETURN) {
+ /* Returning from a successful sigreturn syscall.
+ Avoid clobbering register state. */
+ break;
+ }
+ if ((unsigned int)ret >= (unsigned int)(-1133)) {
+ env->active_tc.gpr[7] = 1; /* error flag */
+ ret = -ret;
+ } else {
+ env->active_tc.gpr[7] = 0; /* error flag */
+ }
+ env->active_tc.gpr[2] = ret;
+ break;
+ case EXCP_TLBL:
+ case EXCP_TLBS:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->CP0_BadVAddr;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_CpU:
+ case EXCP_RI:
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ 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_SC:
+ if (do_store_exclusive(env)) {
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->active_tc.PC;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ default:
+ // error:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif
+
+#ifdef TARGET_SH4
+void cpu_loop (CPUState *env)
+{
+ int trapnr, ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_sh4_exec (env);
+
+ switch (trapnr) {
+ case 0x160:
+ env->pc += 2;
+ ret = do_syscall(env,
+ env->gregs[3],
+ env->gregs[4],
+ env->gregs[5],
+ env->gregs[6],
+ env->gregs[7],
+ env->gregs[0],
+ env->gregs[1]);
+ env->gregs[0] = ret;
+ break;
+ 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 0xa0:
+ case 0xc0:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->tea;
+ 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
+
+#ifdef TARGET_CRIS
+void cpu_loop (CPUState *env)
+{
+ int trapnr, ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_cris_exec (env);
+ switch (trapnr) {
+ case 0xaa:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->pregs[PR_EDA];
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_BREAK:
+ ret = do_syscall(env,
+ env->regs[9],
+ env->regs[10],
+ env->regs[11],
+ env->regs[12],
+ env->regs[13],
+ env->pregs[7],
+ env->pregs[11]);
+ env->regs[10] = ret;
+ 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;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+#endif
+
+#ifdef TARGET_MICROBLAZE
+void cpu_loop (CPUState *env)
+{
+ int trapnr, ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_mb_exec (env);
+ switch (trapnr) {
+ case 0xaa:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_BREAK:
+ /* Return address is 4 bytes after the call. */
+ env->regs[14] += 4;
+ ret = do_syscall(env,
+ env->regs[12],
+ env->regs[5],
+ env->regs[6],
+ env->regs[7],
+ env->regs[8],
+ env->regs[9],
+ env->regs[10]);
+ env->regs[3] = ret;
+ env->sregs[SR_PC] = env->regs[14];
+ break;
+ case EXCP_HW_EXCP:
+ env->regs[17] = env->sregs[SR_PC] + 4;
+ if (env->iflags & D_FLAG) {
+ env->sregs[SR_ESR] |= 1 << 12;
+ env->sregs[SR_PC] -= 4;
+ /* FIXME: if branch was immed, replay the imm aswell. */
+ }
+
+ env->iflags &= ~(IMM_FLAG | D_FLAG);
+
+ switch (env->sregs[SR_ESR] & 31) {
+ case ESR_EC_FPU:
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ if (env->sregs[SR_FSR] & FSR_IO) {
+ info.si_code = TARGET_FPE_FLTINV;
+ }
+ if (env->sregs[SR_FSR] & FSR_DZ) {
+ info.si_code = TARGET_FPE_FLTDIV;
+ }
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ default:
+ printf ("Unhandled hw-exception: 0x%x\n",
+ env->sregs[SR_ESR] & 5);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ break;
+ }
+ 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;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+#endif
+
+#ifdef TARGET_M68K
+
+void cpu_loop(CPUM68KState *env)
+{
+ int trapnr;
+ unsigned int n;
+ target_siginfo_t info;
+ TaskState *ts = env->opaque;
+
+ for(;;) {
+ trapnr = cpu_m68k_exec(env);
+ switch(trapnr) {
+ case EXCP_ILLEGAL:
+ {
+ if (ts->sim_syscalls) {
+ uint16_t nr;
+ nr = lduw(env->pc + 2);
+ env->pc += 4;
+ do_m68k_simcall(env, nr);
+ } else {
+ goto do_sigill;
+ }
+ }
+ break;
+ case EXCP_HALT_INSN:
+ /* Semihosing syscall. */
+ env->pc += 4;
+ do_m68k_semihosting(env, env->dregs[0]);
+ break;
+ case EXCP_LINEA:
+ case EXCP_LINEF:
+ case EXCP_UNSUPPORTED:
+ do_sigill:
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPN;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_TRAP0:
+ {
+ ts->sim_syscalls = 0;
+ n = env->dregs[0];
+ env->pc += 2;
+ env->dregs[0] = do_syscall(env,
+ n,
+ env->dregs[1],
+ env->dregs[2],
+ env->dregs[3],
+ env->dregs[4],
+ env->dregs[5],
+ env->aregs[0]);
+ }
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_ACCESS:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->mmu.ar;
+ queue_signal(env, info.si_signo, &info);
+ }
+ 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;
+ default:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
+ trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+ }
+ process_pending_signals(env);
+ }
+}
+#endif /* TARGET_M68K */
+
+#ifdef TARGET_ALPHA
+static void do_store_exclusive(CPUAlphaState *env, int reg, int quad)
+{
+ target_ulong addr, val, tmp;
+ target_siginfo_t info;
+ int ret = 0;
+
+ addr = env->lock_addr;
+ tmp = env->lock_st_addr;
+ env->lock_addr = -1;
+ env->lock_st_addr = 0;
+
+ start_exclusive();
+ mmap_lock();
+
+ if (addr == tmp) {
+ if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
+ goto do_sigsegv;
+ }
+
+ if (val == env->lock_value) {
+ tmp = env->ir[reg];
+ if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) {
+ goto do_sigsegv;
+ }
+ ret = 1;
+ }
+ }
+ env->ir[reg] = ret;
+ env->pc += 4;
+
+ mmap_unlock();
+ end_exclusive();
+ return;
+
+ do_sigsegv:
+ mmap_unlock();
+ end_exclusive();
+
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = addr;
+ queue_signal(env, TARGET_SIGSEGV, &info);
+}
+
+void cpu_loop (CPUState *env)
+{
+ int trapnr;
+ target_siginfo_t info;
+ abi_long sysret;
+
+ while (1) {
+ trapnr = cpu_alpha_exec (env);
+
+ /* All of the traps imply a transition through PALcode, which
+ implies an REI instruction has been executed. Which means
+ that the intr_flag should be cleared. */
+ env->intr_flag = 0;
+
+ switch (trapnr) {
+ case EXCP_RESET:
+ fprintf(stderr, "Reset requested. Exit\n");
+ exit(1);
+ break;
+ case EXCP_MCHK:
+ fprintf(stderr, "Machine check exception. Exit\n");
+ exit(1);
+ break;
+ case EXCP_ARITH:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_FLTINV;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_HW_INTERRUPT:
+ fprintf(stderr, "External interrupt. Exit\n");
+ exit(1);
+ break;
+ case EXCP_DFAULT:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = (page_get_flags(env->ipr[IPR_EXC_ADDR]) & PAGE_VALID
+ ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR);
+ info._sifields._sigfault._addr = env->ipr[IPR_EXC_ADDR];
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_DTB_MISS_PAL:
+ fprintf(stderr, "MMU data TLB miss in PALcode\n");
+ exit(1);
+ break;
+ case EXCP_ITB_MISS:
+ fprintf(stderr, "MMU instruction TLB miss\n");
+ exit(1);
+ break;
+ case EXCP_ITB_ACV:
+ fprintf(stderr, "MMU instruction access violation\n");
+ exit(1);
+ break;
+ case EXCP_DTB_MISS_NATIVE:
+ fprintf(stderr, "MMU data TLB miss\n");
+ exit(1);
+ break;
+ case EXCP_UNALIGN:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_BUS_ADRALN;
+ info._sifields._sigfault._addr = env->ipr[IPR_EXC_ADDR];
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_OPCDEC:
+ do_sigill:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPC;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_FEN:
+ /* No-op. Linux simply re-enables the FPU. */
+ break;
+ case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1):
+ env->lock_addr = -1;
+ switch ((trapnr >> 6) | 0x80) {
+ case 0x80:
+ /* BPT */
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case 0x81:
+ /* BUGCHK */
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = 0;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case 0x83:
+ /* CALLSYS */
+ trapnr = env->ir[IR_V0];
+ sysret = do_syscall(env, trapnr,
+ env->ir[IR_A0], env->ir[IR_A1],
+ env->ir[IR_A2], env->ir[IR_A3],
+ env->ir[IR_A4], env->ir[IR_A5]);
+ if (trapnr == TARGET_NR_sigreturn
+ || trapnr == TARGET_NR_rt_sigreturn) {
+ break;
+ }
+ /* Syscall writes 0 to V0 to bypass error check, similar
+ to how this is handled internal to Linux kernel. */
+ if (env->ir[IR_V0] == 0) {
+ env->ir[IR_V0] = sysret;
+ } else {
+ env->ir[IR_V0] = (sysret < 0 ? -sysret : sysret);
+ env->ir[IR_A3] = (sysret < 0);
+ }
+ break;
+ case 0x86:
+ /* IMB */
+ /* ??? We can probably elide the code using page_unprotect
+ that is checking for self-modifying code. Instead we
+ could simply call tb_flush here. Until we work out the
+ changes required to turn off the extra write protection,
+ this can be a no-op. */
+ break;
+ case 0x9E:
+ /* RDUNIQUE */
+ /* Handled in the translator for usermode. */
+ abort();
+ case 0x9F:
+ /* WRUNIQUE */
+ /* Handled in the translator for usermode. */
+ abort();
+ case 0xAA:
+ /* GENTRAP */
+ info.si_signo = TARGET_SIGFPE;
+ switch (env->ir[IR_A0]) {
+ case TARGET_GEN_INTOVF:
+ info.si_code = TARGET_FPE_INTOVF;
+ break;
+ case TARGET_GEN_INTDIV:
+ info.si_code = TARGET_FPE_INTDIV;
+ break;
+ case TARGET_GEN_FLTOVF:
+ info.si_code = TARGET_FPE_FLTOVF;
+ break;
+ case TARGET_GEN_FLTUND:
+ info.si_code = TARGET_FPE_FLTUND;
+ break;
+ case TARGET_GEN_FLTINV:
+ info.si_code = TARGET_FPE_FLTINV;
+ break;
+ case TARGET_GEN_FLTINE:
+ info.si_code = TARGET_FPE_FLTRES;
+ break;
+ case TARGET_GEN_ROPRAND:
+ info.si_code = 0;
+ break;
+ default:
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_code = 0;
+ break;
+ }
+ info.si_errno = 0;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ default:
+ goto do_sigill;
+ }
+ break;
+ case EXCP_CALL_PALP ... (EXCP_CALL_PALE - 1):
+ goto do_sigill;
+ case EXCP_DEBUG:
+ info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (info.si_signo) {
+ env->lock_addr = -1;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_STL_C:
+ case EXCP_STQ_C:
+ do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C);
+ break;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+#endif /* TARGET_ALPHA */
+
+static void usage(void)
+{
+ printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
+ "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
+ "Linux CPU emulator (compiled for %s emulation)\n"
+ "\n"
+ "Standard options:\n"
+ "-h print this help\n"
+ "-g port wait gdb connection to port\n"
+ "-L path set the elf interpreter prefix (default=%s)\n"
+ "-s size set the stack size in bytes (default=%ld)\n"
+ "-cpu model select CPU (-cpu ? for list)\n"
+ "-drop-ld-preload drop LD_PRELOAD for target process\n"
+ "-E var=value sets/modifies targets environment variable(s)\n"
+ "-U var unsets targets environment variable(s)\n"
+ "-0 argv0 forces target process argv[0] to be argv0\n"
+#if defined(CONFIG_USE_GUEST_BASE)
+ "-B address set guest_base address to address\n"
+ "-R size reserve size bytes for guest virtual address space\n"
+#endif
+ "\n"
+ "Debug options:\n"
+ "-d options activate log (logfile=%s)\n"
+ "-p pagesize set the host page size to 'pagesize'\n"
+ "-singlestep always run in singlestep mode\n"
+ "-strace log system calls\n"
+ "\n"
+ "Environment variables:\n"
+ "QEMU_STRACE Print system calls and arguments similar to the\n"
+ " 'strace' program. Enable by setting to any value.\n"
+ "You can use -E and -U options to set/unset environment variables\n"
+ "for target process. It is possible to provide several variables\n"
+ "by repeating the option. For example:\n"
+ " -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
+ "Note that if you provide several changes to single variable\n"
+ "last change will stay in effect.\n"
+ ,
+ TARGET_ARCH,
+ interp_prefix,
+ guest_stack_size,
+ DEBUG_LOGFILE);
+ exit(1);
+}
+
+THREAD CPUState *thread_env;
+
+void task_settid(TaskState *ts)
+{
+ if (ts->ts_tid == 0) {
+#ifdef CONFIG_USE_NPTL
+ ts->ts_tid = (pid_t)syscall(SYS_gettid);
+#else
+ /* when no threads are used, tid becomes pid */
+ ts->ts_tid = getpid();
+#endif
+ }
+}
+
+void stop_all_tasks(void)
+{
+ /*
+ * We trust that when using NPTL, start_exclusive()
+ * handles thread stopping correctly.
+ */
+ start_exclusive();
+}
+
+/* Assumes contents are already zeroed. */
+void init_task_state(TaskState *ts)
+{
+ int i;
+
+ ts->used = 1;
+ ts->first_free = ts->sigqueue_table;
+ for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) {
+ ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1];
+ }
+ ts->sigqueue_table[i].next = NULL;
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ const char *filename;
+ const char *cpu_model;
+ struct target_pt_regs regs1, *regs = &regs1;
+ struct image_info info1, *info = &info1;
+ struct linux_binprm bprm;
+ TaskState *ts;
+ CPUState *env;
+ int optind;
+ const char *r;
+ int gdbstub_port = 0;
+ char **target_environ, **wrk;
+ char **target_argv;
+ int target_argc;
+ envlist_t *envlist = NULL;
+ const char *argv0 = NULL;
+ int i;
+ int ret;
+
+ if (argc <= 1)
+ usage();
+
+ qemu_cache_utils_init(envp);
+
+ /* init debug */
+ cpu_set_log_filename(DEBUG_LOGFILE);
+
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
+
+ /* add current environment into the list */
+ for (wrk = environ; *wrk != NULL; wrk++) {
+ (void) envlist_setenv(envlist, *wrk);
+ }
+
+ /* Read the stack limit from the kernel. If it's "unlimited",
+ then we can do little else besides use the default. */
+ {
+ struct rlimit lim;
+ if (getrlimit(RLIMIT_STACK, &lim) == 0
+ && lim.rlim_cur != RLIM_INFINITY
+ && lim.rlim_cur == (target_long)lim.rlim_cur) {
+ guest_stack_size = lim.rlim_cur;
+ }
+ }
+
+ cpu_model = NULL;
+#if defined(cpudef_setup)
+ cpudef_setup(); /* parse cpu definitions in target config file (TBD) */
+#endif
+
+ optind = 1;
+ for(;;) {
+ if (optind >= argc)
+ break;
+ r = argv[optind];
+ if (r[0] != '-')
+ break;
+ optind++;
+ r++;
+ if (!strcmp(r, "-")) {
+ break;
+ } else if (!strcmp(r, "d")) {
+ int mask;
+ const CPULogItem *item;
+
+ if (optind >= argc)
+ break;
+
+ r = argv[optind++];
+ mask = cpu_str_to_log_mask(r);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+ } else if (!strcmp(r, "E")) {
+ r = argv[optind++];
+ if (envlist_setenv(envlist, r) != 0)
+ usage();
+ } else if (!strcmp(r, "ignore-environment")) {
+ envlist_free(envlist);
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
+ } else if (!strcmp(r, "U")) {
+ r = argv[optind++];
+ if (envlist_unsetenv(envlist, r) != 0)
+ usage();
+ } else if (!strcmp(r, "0")) {
+ r = argv[optind++];
+ argv0 = r;
+ } else if (!strcmp(r, "s")) {
+ if (optind >= argc)
+ break;
+ r = argv[optind++];
+ guest_stack_size = strtoul(r, (char **)&r, 0);
+ if (guest_stack_size == 0)
+ usage();
+ if (*r == 'M')
+ guest_stack_size *= 1024 * 1024;
+ else if (*r == 'k' || *r == 'K')
+ guest_stack_size *= 1024;
+ } else if (!strcmp(r, "L")) {
+ interp_prefix = argv[optind++];
+ } else if (!strcmp(r, "p")) {
+ if (optind >= argc)
+ break;
+ qemu_host_page_size = atoi(argv[optind++]);
+ if (qemu_host_page_size == 0 ||
+ (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
+ fprintf(stderr, "page size must be a power of two\n");
+ exit(1);
+ }
+ } else if (!strcmp(r, "g")) {
+ if (optind >= argc)
+ break;
+ gdbstub_port = atoi(argv[optind++]);
+ } else if (!strcmp(r, "r")) {
+ qemu_uname_release = argv[optind++];
+ } else if (!strcmp(r, "cpu")) {
+ cpu_model = argv[optind++];
+ if (cpu_model == NULL || strcmp(cpu_model, "?") == 0) {
+/* XXX: implement xxx_cpu_list for targets that still miss it */
+#if defined(cpu_list_id)
+ cpu_list_id(stdout, &fprintf, "");
+#elif defined(cpu_list)
+ cpu_list(stdout, &fprintf); /* deprecated */
+#endif
+ exit(1);
+ }
+#if defined(CONFIG_USE_GUEST_BASE)
+ } else if (!strcmp(r, "B")) {
+ guest_base = strtol(argv[optind++], NULL, 0);
+ have_guest_base = 1;
+ } else if (!strcmp(r, "R")) {
+ char *p;
+ int shift = 0;
+ reserved_va = strtoul(argv[optind++], &p, 0);
+ switch (*p) {
+ case 'k':
+ case 'K':
+ shift = 10;
+ break;
+ case 'M':
+ shift = 20;
+ break;
+ case 'G':
+ shift = 30;
+ break;
+ }
+ if (shift) {
+ unsigned long unshifted = reserved_va;
+ p++;
+ reserved_va <<= shift;
+ if (((reserved_va >> shift) != unshifted)
+#if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
+ || (reserved_va > (1ul << TARGET_VIRT_ADDR_SPACE_BITS))
+#endif
+ ) {
+ fprintf(stderr, "Reserved virtual address too big\n");
+ exit(1);
+ }
+ }
+ if (*p) {
+ fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p);
+ exit(1);
+ }
+#endif
+ } else if (!strcmp(r, "drop-ld-preload")) {
+ (void) envlist_unsetenv(envlist, "LD_PRELOAD");
+ } else if (!strcmp(r, "singlestep")) {
+ singlestep = 1;
+ } else if (!strcmp(r, "strace")) {
+ do_strace = 1;
+ } else
+ {
+ usage();
+ }
+ }
+ if (optind >= argc)
+ usage();
+ filename = argv[optind];
+ exec_path = argv[optind];
+
+ /* Zero out regs */
+ memset(regs, 0, sizeof(struct target_pt_regs));
+
+ /* Zero out image_info */
+ memset(info, 0, sizeof(struct image_info));
+
+ memset(&bprm, 0, sizeof (bprm));
+
+ /* Scan interp_prefix dir for replacement files. */
+ init_paths(interp_prefix);
+
+ if (cpu_model == NULL) {
+#if defined(TARGET_I386)
+#ifdef TARGET_X86_64
+ cpu_model = "qemu64";
+#else
+ cpu_model = "qemu32";
+#endif
+#elif defined(TARGET_ARM)
+ cpu_model = "any";
+#elif defined(TARGET_M68K)
+ cpu_model = "any";
+#elif defined(TARGET_SPARC)
+#ifdef TARGET_SPARC64
+ cpu_model = "TI UltraSparc II";
+#else
+ cpu_model = "Fujitsu MB86904";
+#endif
+#elif defined(TARGET_MIPS)
+#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64)
+ cpu_model = "20Kc";
+#else
+ cpu_model = "24Kf";
+#endif
+#elif defined(TARGET_PPC)
+#ifdef TARGET_PPC64
+ cpu_model = "970fx";
+#else
+ cpu_model = "750";
+#endif
+#else
+ cpu_model = "any";
+#endif
+ }
+ cpu_exec_init_all(0);
+ /* NOTE: we need to init the CPU at this stage to get
+ qemu_host_page_size */
+ env = cpu_init(cpu_model);
+ if (!env) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
+ cpu_reset(env);
+#endif
+
+ thread_env = env;
+
+ if (getenv("QEMU_STRACE")) {
+ do_strace = 1;
+ }
+
+ target_environ = envlist_to_environ(envlist, NULL);
+ envlist_free(envlist);
+
+#if defined(CONFIG_USE_GUEST_BASE)
+ /*
+ * Now that page sizes are configured in cpu_init() we can do
+ * proper page alignment for guest_base.
+ */
+ guest_base = HOST_PAGE_ALIGN(guest_base);
+
+ if (reserved_va) {
+ void *p;
+ int flags;
+
+ flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
+ if (have_guest_base) {
+ flags |= MAP_FIXED;
+ }
+ p = mmap((void *)guest_base, reserved_va, PROT_NONE, flags, -1, 0);
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "Unable to reserve guest address space\n");
+ exit(1);
+ }
+ guest_base = (unsigned long)p;
+ /* Make sure the address is properly aligned. */
+ if (guest_base & ~qemu_host_page_mask) {
+ munmap(p, reserved_va);
+ p = mmap((void *)guest_base, reserved_va + qemu_host_page_size,
+ PROT_NONE, flags, -1, 0);
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "Unable to reserve guest address space\n");
+ exit(1);
+ }
+ guest_base = HOST_PAGE_ALIGN((unsigned long)p);
+ }
+ qemu_log("Reserved 0x%lx bytes of guest address space\n", reserved_va);
+ }
+#endif /* CONFIG_USE_GUEST_BASE */
+
+ /*
+ * Read in mmap_min_addr kernel parameter. This value is used
+ * When loading the ELF image to determine whether guest_base
+ * is needed. It is also used in mmap_find_vma.
+ */
+ {
+ FILE *fp;
+
+ if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
+ unsigned long tmp;
+ if (fscanf(fp, "%lu", &tmp) == 1) {
+ mmap_min_addr = tmp;
+ qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr);
+ }
+ fclose(fp);
+ }
+ }
+
+ /*
+ * Prepare copy of argv vector for target.
+ */
+ target_argc = argc - optind;
+ target_argv = calloc(target_argc + 1, sizeof (char *));
+ if (target_argv == NULL) {
+ (void) fprintf(stderr, "Unable to allocate memory for target_argv\n");
+ exit(1);
+ }
+
+ /*
+ * If argv0 is specified (using '-0' switch) we replace
+ * argv[0] pointer with the given one.
+ */
+ i = 0;
+ if (argv0 != NULL) {
+ target_argv[i++] = strdup(argv0);
+ }
+ for (; i < target_argc; i++) {
+ target_argv[i] = strdup(argv[optind + i]);
+ }
+ target_argv[target_argc] = NULL;
+
+ ts = qemu_mallocz (sizeof(TaskState));
+ init_task_state(ts);
+ /* build Task State */
+ ts->info = info;
+ ts->bprm = &bprm;
+ env->opaque = ts;
+ task_settid(ts);
+
+ ret = loader_exec(filename, target_argv, target_environ, regs,
+ info, &bprm);
+ if (ret != 0) {
+ printf("Error %d while loading %s\n", ret, filename);
+ _exit(1);
+ }
+
+ for (i = 0; i < target_argc; i++) {
+ free(target_argv[i]);
+ }
+ free(target_argv);
+
+ for (wrk = target_environ; *wrk; wrk++) {
+ free(*wrk);
+ }
+
+ free(target_environ);
+
+ if (qemu_log_enabled()) {
+#if defined(CONFIG_USE_GUEST_BASE)
+ qemu_log("guest_base 0x%lx\n", guest_base);
+#endif
+ log_page_dump();
+
+ qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
+ qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code);
+ qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_code);
+ qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_data);
+ qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data);
+ qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_stack);
+ qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk);
+ qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry);
+ }
+
+ target_set_brk(info->brk);
+ syscall_init();
+ signal_init();
+
+#if defined(CONFIG_USE_GUEST_BASE)
+ /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
+ generating the prologue until now so that the prologue can take
+ the real value of GUEST_BASE into account. */
+ tcg_prologue_init(&tcg_ctx);
+#endif
+
+#if defined(TARGET_I386)
+ cpu_x86_set_cpl(env, 3);
+
+ env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
+ env->hflags |= HF_PE_MASK;
+ if (env->cpuid_features & CPUID_SSE) {
+ env->cr[4] |= CR4_OSFXSR_MASK;
+ env->hflags |= HF_OSFXSR_MASK;
+ }
+#ifndef TARGET_ABI32
+ /* enable 64 bit mode if possible */
+ if (!(env->cpuid_ext2_features & CPUID_EXT2_LM)) {
+ fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
+ exit(1);
+ }
+ env->cr[4] |= CR4_PAE_MASK;
+ env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
+ env->hflags |= HF_LMA_MASK;
+#endif
+
+ /* flags setup : we activate the IRQs by default as in user mode */
+ env->eflags |= IF_MASK;
+
+ /* linux register setup */
+#ifndef TARGET_ABI32
+ env->regs[R_EAX] = regs->rax;
+ env->regs[R_EBX] = regs->rbx;
+ env->regs[R_ECX] = regs->rcx;
+ env->regs[R_EDX] = regs->rdx;
+ env->regs[R_ESI] = regs->rsi;
+ env->regs[R_EDI] = regs->rdi;
+ env->regs[R_EBP] = regs->rbp;
+ env->regs[R_ESP] = regs->rsp;
+ env->eip = regs->rip;
+#else
+ env->regs[R_EAX] = regs->eax;
+ env->regs[R_EBX] = regs->ebx;
+ env->regs[R_ECX] = regs->ecx;
+ env->regs[R_EDX] = regs->edx;
+ env->regs[R_ESI] = regs->esi;
+ env->regs[R_EDI] = regs->edi;
+ env->regs[R_EBP] = regs->ebp;
+ env->regs[R_ESP] = regs->esp;
+ env->eip = regs->eip;
+#endif
+
+ /* linux interrupt setup */
+#ifndef TARGET_ABI32
+ env->idt.limit = 511;
+#else
+ env->idt.limit = 255;
+#endif
+ env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1),
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ idt_table = g2h(env->idt.base);
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 0);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+ set_idt(0x80, 3);
+
+ /* linux segment setup */
+ {
+ uint64_t *gdt_table;
+ env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
+ gdt_table = g2h(env->gdt.base);
+#ifdef TARGET_ABI32
+ write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+#else
+ /* 64 bit code segment */
+ write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ DESC_L_MASK |
+ (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
+#endif
+ write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
+ (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
+ }
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+#ifdef TARGET_ABI32
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_FS, __USER_DS);
+ cpu_x86_load_seg(env, R_GS, __USER_DS);
+ /* This hack makes Wine work... */
+ env->segs[R_FS].selector = 0;
+#else
+ cpu_x86_load_seg(env, R_DS, 0);
+ cpu_x86_load_seg(env, R_ES, 0);
+ cpu_x86_load_seg(env, R_FS, 0);
+ cpu_x86_load_seg(env, R_GS, 0);
+#endif
+#elif defined(TARGET_ARM)
+ {
+ int i;
+ cpsr_write(env, regs->uregs[16], 0xffffffff);
+ for(i = 0; i < 16; i++) {
+ env->regs[i] = regs->uregs[i];
+ }
+ }
+#elif defined(TARGET_SPARC)
+ {
+ int i;
+ env->pc = regs->pc;
+ env->npc = regs->npc;
+ env->y = regs->y;
+ for(i = 0; i < 8; i++)
+ env->gregs[i] = regs->u_regs[i];
+ for(i = 0; i < 8; i++)
+ env->regwptr[i] = regs->u_regs[i + 8];
+ }
+#elif defined(TARGET_PPC)
+ {
+ int i;
+
+#if defined(TARGET_PPC64)
+#if defined(TARGET_ABI32)
+ env->msr &= ~((target_ulong)1 << MSR_SF);
+#else
+ env->msr |= (target_ulong)1 << MSR_SF;
+#endif
+#endif
+ env->nip = regs->nip;
+ for(i = 0; i < 32; i++) {
+ env->gpr[i] = regs->gpr[i];
+ }
+ }
+#elif defined(TARGET_M68K)
+ {
+ env->pc = regs->pc;
+ env->dregs[0] = regs->d0;
+ env->dregs[1] = regs->d1;
+ env->dregs[2] = regs->d2;
+ env->dregs[3] = regs->d3;
+ env->dregs[4] = regs->d4;
+ env->dregs[5] = regs->d5;
+ env->dregs[6] = regs->d6;
+ env->dregs[7] = regs->d7;
+ env->aregs[0] = regs->a0;
+ env->aregs[1] = regs->a1;
+ env->aregs[2] = regs->a2;
+ env->aregs[3] = regs->a3;
+ env->aregs[4] = regs->a4;
+ env->aregs[5] = regs->a5;
+ env->aregs[6] = regs->a6;
+ env->aregs[7] = regs->usp;
+ env->sr = regs->sr;
+ ts->sim_syscalls = 1;
+ }
+#elif defined(TARGET_MICROBLAZE)
+ {
+ env->regs[0] = regs->r0;
+ env->regs[1] = regs->r1;
+ env->regs[2] = regs->r2;
+ env->regs[3] = regs->r3;
+ env->regs[4] = regs->r4;
+ env->regs[5] = regs->r5;
+ env->regs[6] = regs->r6;
+ env->regs[7] = regs->r7;
+ env->regs[8] = regs->r8;
+ env->regs[9] = regs->r9;
+ env->regs[10] = regs->r10;
+ env->regs[11] = regs->r11;
+ env->regs[12] = regs->r12;
+ env->regs[13] = regs->r13;
+ env->regs[14] = regs->r14;
+ env->regs[15] = regs->r15;
+ env->regs[16] = regs->r16;
+ env->regs[17] = regs->r17;
+ env->regs[18] = regs->r18;
+ env->regs[19] = regs->r19;
+ env->regs[20] = regs->r20;
+ env->regs[21] = regs->r21;
+ env->regs[22] = regs->r22;
+ env->regs[23] = regs->r23;
+ env->regs[24] = regs->r24;
+ env->regs[25] = regs->r25;
+ env->regs[26] = regs->r26;
+ env->regs[27] = regs->r27;
+ env->regs[28] = regs->r28;
+ env->regs[29] = regs->r29;
+ env->regs[30] = regs->r30;
+ env->regs[31] = regs->r31;
+ env->sregs[SR_PC] = regs->pc;
+ }
+#elif defined(TARGET_MIPS)
+ {
+ int i;
+
+ for(i = 0; i < 32; i++) {
+ env->active_tc.gpr[i] = regs->regs[i];
+ }
+ env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1;
+ if (regs->cp0_epc & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ }
+ }
+#elif defined(TARGET_SH4)
+ {
+ int i;
+
+ for(i = 0; i < 16; i++) {
+ env->gregs[i] = regs->regs[i];
+ }
+ env->pc = regs->pc;
+ }
+#elif defined(TARGET_ALPHA)
+ {
+ int i;
+
+ for(i = 0; i < 28; i++) {
+ env->ir[i] = ((abi_ulong *)regs)[i];
+ }
+ env->ir[IR_SP] = regs->usp;
+ env->pc = regs->pc;
+ }
+#elif defined(TARGET_CRIS)
+ {
+ env->regs[0] = regs->r0;
+ env->regs[1] = regs->r1;
+ env->regs[2] = regs->r2;
+ env->regs[3] = regs->r3;
+ env->regs[4] = regs->r4;
+ env->regs[5] = regs->r5;
+ env->regs[6] = regs->r6;
+ env->regs[7] = regs->r7;
+ env->regs[8] = regs->r8;
+ env->regs[9] = regs->r9;
+ env->regs[10] = regs->r10;
+ env->regs[11] = regs->r11;
+ env->regs[12] = regs->r12;
+ env->regs[13] = regs->r13;
+ env->regs[14] = info->start_stack;
+ env->regs[15] = regs->acr;
+ env->pc = regs->erp;
+ }
+#else
+#error unsupported target CPU
+#endif
+
+#if defined(TARGET_ARM) || defined(TARGET_M68K)
+ ts->stack_base = info->start_stack;
+ ts->heap_base = info->brk;
+ /* This will be filled in on the first SYS_HEAPINFO call. */
+ ts->heap_limit = 0;
+#endif
+
+ if (gdbstub_port) {
+ gdbserver_start (gdbstub_port);
+ gdb_handlesig(env, 0);
+ }
+ cpu_loop(env);
+ /* never exits */
+ return 0;
+}
diff --git a/linux-user/microblaze/syscall.h b/linux-user/microblaze/syscall.h
new file mode 100644
index 0000000..db1f98a
--- /dev/null
+++ b/linux-user/microblaze/syscall.h
@@ -0,0 +1,45 @@
+#define UNAME_MACHINE "microblaze"
+
+/* We use microblaze_reg_t to keep things similar to the kernel sources. */
+typedef uint32_t microblaze_reg_t;
+
+struct target_pt_regs {
+ microblaze_reg_t r0;
+ microblaze_reg_t r1;
+ microblaze_reg_t r2;
+ microblaze_reg_t r3;
+ microblaze_reg_t r4;
+ microblaze_reg_t r5;
+ microblaze_reg_t r6;
+ microblaze_reg_t r7;
+ microblaze_reg_t r8;
+ microblaze_reg_t r9;
+ microblaze_reg_t r10;
+ microblaze_reg_t r11;
+ microblaze_reg_t r12;
+ microblaze_reg_t r13;
+ microblaze_reg_t r14;
+ microblaze_reg_t r15;
+ microblaze_reg_t r16;
+ microblaze_reg_t r17;
+ microblaze_reg_t r18;
+ microblaze_reg_t r19;
+ microblaze_reg_t r20;
+ microblaze_reg_t r21;
+ microblaze_reg_t r22;
+ microblaze_reg_t r23;
+ microblaze_reg_t r24;
+ microblaze_reg_t r25;
+ microblaze_reg_t r26;
+ microblaze_reg_t r27;
+ microblaze_reg_t r28;
+ microblaze_reg_t r29;
+ microblaze_reg_t r30;
+ microblaze_reg_t r31;
+ microblaze_reg_t pc;
+ microblaze_reg_t msr;
+ microblaze_reg_t ear;
+ microblaze_reg_t esr;
+ microblaze_reg_t fsr;
+ uint32_t kernel_mode;
+};
diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h
new file mode 100644
index 0000000..3e641cd
--- /dev/null
+++ b/linux-user/microblaze/syscall_nr.h
@@ -0,0 +1,369 @@
+#define TARGET_NR_restart_syscall 0 /* ok */
+#define TARGET_NR_exit 1 /* ok */
+#define TARGET_NR_fork 2 /* not for no MMU - weird */
+#define TARGET_NR_read 3 /* ok */
+#define TARGET_NR_write 4 /* ok */
+#define TARGET_NR_open 5 /* openat */
+#define TARGET_NR_close 6 /* ok */
+#define TARGET_NR_waitpid 7 /* waitid */
+#define TARGET_NR_creat 8 /* openat */
+#define TARGET_NR_link 9 /* linkat */
+#define TARGET_NR_unlink 10 /* unlinkat */
+#define TARGET_NR_execve 11 /* ok */
+#define TARGET_NR_chdir 12 /* ok */
+#define TARGET_NR_time 13 /* obsolete -> sys_gettimeofday */
+#define TARGET_NR_mknod 14 /* mknodat */
+#define TARGET_NR_chmod 15 /* fchmodat */
+#define TARGET_NR_lchown 16 /* ok */
+#define TARGET_NR_break 17 /* don't know */
+#define TARGET_NR_oldstat 18 /* remove */
+#define TARGET_NR_lseek 19 /* ok */
+#define TARGET_NR_getpid 20 /* ok */
+#define TARGET_NR_mount 21 /* ok */
+#define TARGET_NR_umount 22 /* ok */ /* use only umount2 */
+#define TARGET_NR_setuid 23 /* ok */
+#define TARGET_NR_getuid 24 /* ok */
+#define TARGET_NR_stime 25 /* obsolete -> sys_settimeofday */
+#define TARGET_NR_ptrace 26 /* ok */
+#define TARGET_NR_alarm 27 /* obsolete -> sys_setitimer */
+#define TARGET_NR_oldfstat 28 /* remove */
+#define TARGET_NR_pause 29 /* obsolete -> sys_rt_sigtimedwait */
+#define TARGET_NR_utime 30 /* obsolete -> sys_utimesat */
+#define TARGET_NR_stty 31 /* remove */
+#define TARGET_NR_gtty 32 /* remove */
+#define TARGET_NR_access 33 /* faccessat */
+#define TARGET_NR_nice 34 /* can be implemented by sys_setpriority */
+#define TARGET_NR_ftime 35 /* remove */
+#define TARGET_NR_sync 36 /* ok */
+#define TARGET_NR_kill 37 /* ok */
+#define TARGET_NR_rename 38 /* renameat */
+#define TARGET_NR_mkdir 39 /* mkdirat */
+#define TARGET_NR_rmdir 40 /* unlinkat */
+#define TARGET_NR_dup 41 /* ok */
+#define TARGET_NR_pipe 42 /* ok */
+#define TARGET_NR_times 43 /* ok */
+#define TARGET_NR_prof 44 /* remove */
+#define TARGET_NR_brk 45 /* ok -mmu, nommu specific */
+#define TARGET_NR_setgid 46 /* ok */
+#define TARGET_NR_getgid 47 /* ok */
+#define TARGET_NR_signal 48 /* obsolete -> sys_rt_sigaction */
+#define TARGET_NR_geteuid 49 /* ok */
+#define TARGET_NR_getegid 50 /* ok */
+#define TARGET_NR_acct 51 /* add it and then I can disable it */
+#define TARGET_NR_umount2 52 /* remove */
+#define TARGET_NR_lock 53 /* remove */
+#define TARGET_NR_ioctl 54 /* ok */
+#define TARGET_NR_fcntl 55 /* ok -> 64bit version*/
+#define TARGET_NR_mpx 56 /* remove */
+#define TARGET_NR_setpgid 57 /* ok */
+#define TARGET_NR_ulimit 58 /* remove */
+#define TARGET_NR_oldolduname 59 /* remove */
+#define TARGET_NR_umask 60 /* ok */
+#define TARGET_NR_chroot 61 /* ok */
+#define TARGET_NR_ustat 62 /* obsolete -> statfs64 */
+#define TARGET_NR_dup2 63 /* ok */
+#define TARGET_NR_getppid 64 /* ok */
+#define TARGET_NR_getpgrp 65 /* obsolete -> sys_getpgid */
+#define TARGET_NR_setsid 66 /* ok */
+#define TARGET_NR_sigaction 67 /* obsolete -> rt_sigaction */
+#define TARGET_NR_sgetmask 68 /* obsolete -> sys_rt_sigprocmask */
+#define TARGET_NR_ssetmask 69 /* obsolete ->sys_rt_sigprocmask */
+#define TARGET_NR_setreuid 70 /* ok */
+#define TARGET_NR_setregid 71 /* ok */
+#define TARGET_NR_sigsuspend 72 /* obsolete -> rt_sigsuspend */
+#define TARGET_NR_sigpending 73 /* obsolete -> sys_rt_sigpending */
+#define TARGET_NR_sethostname 74 /* ok */
+#define TARGET_NR_setrlimit 75 /* ok */
+#define TARGET_NR_getrlimit 76 /* ok Back compatible 2Gig limited rlimit */
+#define TARGET_NR_getrusage 77 /* ok */
+#define TARGET_NR_gettimeofday 78 /* ok */
+#define TARGET_NR_settimeofday 79 /* ok */
+#define TARGET_NR_getgroups 80 /* ok */
+#define TARGET_NR_setgroups 81 /* ok */
+#define TARGET_NR_select 82 /* obsolete -> sys_pselect7 */
+#define TARGET_NR_symlink 83 /* symlinkat */
+#define TARGET_NR_oldlstat 84 /* remove */
+#define TARGET_NR_readlink 85 /* obsolete -> sys_readlinkat */
+#define TARGET_NR_uselib 86 /* remove */
+#define TARGET_NR_swapon 87 /* ok */
+#define TARGET_NR_reboot 88 /* ok */
+#define TARGET_NR_readdir 89 /* remove ? */
+#define TARGET_NR_mmap 90 /* obsolete -> sys_mmap2 */
+#define TARGET_NR_munmap 91 /* ok - mmu and nommu */
+#define TARGET_NR_truncate 92 /* ok or truncate64 */
+#define TARGET_NR_ftruncate 93 /* ok or ftruncate64 */
+#define TARGET_NR_fchmod 94 /* ok */
+#define TARGET_NR_fchown 95 /* ok */
+#define TARGET_NR_getpriority 96 /* ok */
+#define TARGET_NR_setpriority 97 /* ok */
+#define TARGET_NR_profil 98 /* remove */
+#define TARGET_NR_statfs 99 /* ok or statfs64 */
+#define TARGET_NR_fstatfs 100 /* ok or fstatfs64 */
+#define TARGET_NR_ioperm 101 /* remove */
+#define TARGET_NR_socketcall 102 /* remove */
+#define TARGET_NR_syslog 103 /* ok */
+#define TARGET_NR_setitimer 104 /* ok */
+#define TARGET_NR_getitimer 105 /* ok */
+#define TARGET_NR_stat 106 /* remove */
+#define TARGET_NR_lstat 107 /* remove */
+#define TARGET_NR_fstat 108 /* remove */
+#define TARGET_NR_olduname 109 /* remove */
+#define TARGET_NR_iopl 110 /* remove */
+#define TARGET_NR_vhangup 111 /* ok */
+#define TARGET_NR_idle 112 /* remove */
+#define TARGET_NR_vm86old 113 /* remove */
+#define TARGET_NR_wait4 114 /* obsolete -> waitid */
+#define TARGET_NR_swapoff 115 /* ok */
+#define TARGET_NR_sysinfo 116 /* ok */
+#define TARGET_NR_ipc 117 /* remove - direct call */
+#define TARGET_NR_fsync 118 /* ok */
+#define TARGET_NR_sigreturn 119 /* obsolete -> sys_rt_sigreturn */
+#define TARGET_NR_clone 120 /* ok */
+#define TARGET_NR_setdomainname 121 /* ok */
+#define TARGET_NR_uname 122 /* remove */
+#define TARGET_NR_modify_ldt 123 /* remove */
+#define TARGET_NR_adjtimex 124 /* ok */
+#define TARGET_NR_mprotect 125 /* remove */
+#define TARGET_NR_sigprocmask 126 /* obsolete -> sys_rt_sigprocmask */
+#define TARGET_NR_create_module 127 /* remove */
+#define TARGET_NR_init_module 128 /* ok */
+#define TARGET_NR_delete_module 129 /* ok */
+#define TARGET_NR_get_kernel_syms 130 /* remove */
+#define TARGET_NR_quotactl 131 /* ok */
+#define TARGET_NR_getpgid 132 /* ok */
+#define TARGET_NR_fchdir 133 /* ok */
+#define TARGET_NR_bdflush 134 /* remove */
+#define TARGET_NR_sysfs 135 /* needed for busybox */
+#define TARGET_NR_personality 136 /* ok */
+#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid 138 /* ok */
+#define TARGET_NR_setfsgid 139 /* ok */
+#define TARGET_NR__llseek 140 /* remove only lseek */
+#define TARGET_NR_getdents 141 /* ok or getdents64 */
+#define TARGET_NR__newselect 142 /* remove */
+#define TARGET_NR_flock 143 /* ok */
+#define TARGET_NR_msync 144 /* remove */
+#define TARGET_NR_readv 145 /* ok */
+#define TARGET_NR_writev 146 /* ok */
+#define TARGET_NR_getsid 147 /* ok */
+#define TARGET_NR_fdatasync 148 /* ok */
+#define TARGET_NR__sysctl 149 /* remove */
+#define TARGET_NR_mlock 150 /* ok - nommu or mmu */
+#define TARGET_NR_munlock 151 /* ok - nommu or mmu */
+#define TARGET_NR_mlockall 152 /* ok - nommu or mmu */
+#define TARGET_NR_munlockall 153 /* ok - nommu or mmu */
+#define TARGET_NR_sched_setparam 154 /* ok */
+#define TARGET_NR_sched_getparam 155 /* ok */
+#define TARGET_NR_sched_setscheduler 156 /* ok */
+#define TARGET_NR_sched_getscheduler 157 /* ok */
+#define TARGET_NR_sched_yield 158 /* ok */
+#define TARGET_NR_sched_get_priority_max 159 /* ok */
+#define TARGET_NR_sched_get_priority_min 160 /* ok */
+#define TARGET_NR_sched_rr_get_interval 161 /* ok */
+#define TARGET_NR_nanosleep 162 /* ok */
+#define TARGET_NR_mremap 163 /* ok - nommu or mmu */
+#define TARGET_NR_setresuid 164 /* ok */
+#define TARGET_NR_getresuid 165 /* ok */
+#define TARGET_NR_vm86 166 /* remove */
+#define TARGET_NR_query_module 167 /* ok */
+#define TARGET_NR_poll 168 /* obsolete -> sys_ppoll */
+#define TARGET_NR_nfsservctl 169 /* ok */
+#define TARGET_NR_setresgid 170 /* ok */
+#define TARGET_NR_getresgid 171 /* ok */
+#define TARGET_NR_prctl 172 /* ok */
+#define TARGET_NR_rt_sigreturn 173 /* ok */
+#define TARGET_NR_rt_sigaction 174 /* ok */
+#define TARGET_NR_rt_sigprocmask 175 /* ok */
+#define TARGET_NR_rt_sigpending 176 /* ok */
+#define TARGET_NR_rt_sigtimedwait 177 /* ok */
+#define TARGET_NR_rt_sigqueueinfo 178 /* ok */
+#define TARGET_NR_rt_sigsuspend 179 /* ok */
+#define TARGET_NR_pread64 180 /* ok */
+#define TARGET_NR_pwrite64 181 /* ok */
+#define TARGET_NR_chown 182 /* obsolete -> fchownat */
+#define TARGET_NR_getcwd 183 /* ok */
+#define TARGET_NR_capget 184 /* ok */
+#define TARGET_NR_capset 185 /* ok */
+#define TARGET_NR_sigaltstack 186 /* remove */
+#define TARGET_NR_sendfile 187 /* ok -> exist 64bit version*/
+#define TARGET_NR_getpmsg 188 /* remove - some people actually want streams */
+#define TARGET_NR_putpmsg 189 /* remove - some people actually want streams */
+#define TARGET_NR_vfork 190 /* for noMMU - group with clone -> maybe remove */
+#define TARGET_NR_ugetrlimit 191 /* remove - SuS compliant getrlimit */
+#define TARGET_NR_mmap2 192 /* ok */
+#define TARGET_NR_truncate64 193 /* ok */
+#define TARGET_NR_ftruncate64 194 /* ok */
+#define TARGET_NR_stat64 195 /* remove _ARCH_WANT_STAT64 */
+#define TARGET_NR_lstat64 196 /* remove _ARCH_WANT_STAT64 */
+#define TARGET_NR_fstat64 197 /* remove _ARCH_WANT_STAT64 */
+#define TARGET_NR_lchown32 198 /* ok - without 32 */
+#define TARGET_NR_getuid32 199 /* ok - without 32 */
+#define TARGET_NR_getgid32 200 /* ok - without 32 */
+#define TARGET_NR_geteuid32 201 /* ok - without 32 */
+#define TARGET_NR_getegid32 202 /* ok - without 32 */
+#define TARGET_NR_setreuid32 203 /* ok - without 32 */
+#define TARGET_NR_setregid32 204 /* ok - without 32 */
+#define TARGET_NR_getgroups32 205 /* ok - without 32 */
+#define TARGET_NR_setgroups32 206 /* ok - without 32 */
+#define TARGET_NR_fchown32 207 /* ok - without 32 */
+#define TARGET_NR_setresuid32 208 /* ok - without 32 */
+#define TARGET_NR_getresuid32 209 /* ok - without 32 */
+#define TARGET_NR_setresgid32 210 /* ok - without 32 */
+#define TARGET_NR_getresgid32 211 /* ok - without 32 */
+#define TARGET_NR_chown32 212 /* ok - without 32 -obsolete -> fchownat */
+#define TARGET_NR_setuid32 213 /* ok - without 32 */
+#define TARGET_NR_setgid32 214 /* ok - without 32 */
+#define TARGET_NR_setfsuid32 215 /* ok - without 32 */
+#define TARGET_NR_setfsgid32 216 /* ok - without 32 */
+#define TARGET_NR_pivot_root 217 /* ok */
+#define TARGET_NR_mincore 218 /* ok */
+#define TARGET_NR_madvise 219 /* ok */
+//#define TARGET_NR_madvise1 219 /* remove delete when C lib stub is removed */
+#define TARGET_NR_getdents64 220 /* ok */
+#define TARGET_NR_fcntl64 221 /* ok */
+/* 223 is unused */
+#define TARGET_NR_gettid 224 /* ok */
+#define TARGET_NR_readahead 225 /* ok */
+#define TARGET_NR_setxattr 226 /* ok */
+#define TARGET_NR_lsetxattr 227 /* ok */
+#define TARGET_NR_fsetxattr 228 /* ok */
+#define TARGET_NR_getxattr 229 /* ok */
+#define TARGET_NR_lgetxattr 230 /* ok */
+#define TARGET_NR_fgetxattr 231 /* ok */
+#define TARGET_NR_listxattr 232 /* ok */
+#define TARGET_NR_llistxattr 233 /* ok */
+#define TARGET_NR_flistxattr 234 /* ok */
+#define TARGET_NR_removexattr 235 /* ok */
+#define TARGET_NR_lremovexattr 236 /* ok */
+#define TARGET_NR_fremovexattr 237 /* ok */
+#define TARGET_NR_tkill 238 /* ok */
+#define TARGET_NR_sendfile64 239 /* ok */
+#define TARGET_NR_futex 240 /* ok */
+#define TARGET_NR_sched_setaffinity 241 /* ok */
+#define TARGET_NR_sched_getaffinity 242 /* ok */
+#define TARGET_NR_set_thread_area 243 /* remove */
+#define TARGET_NR_get_thread_area 244 /* remove */
+#define TARGET_NR_io_setup 245 /* ok */
+#define TARGET_NR_io_destroy 246 /* ok */
+#define TARGET_NR_io_getevents 247 /* ok */
+#define TARGET_NR_io_submit 248 /* ok */
+#define TARGET_NR_io_cancel 249 /* ok */
+#define TARGET_NR_fadvise64 250 /* remove -> sys_fadvise64_64 */
+/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */
+#define TARGET_NR_exit_group 252 /* ok */
+#define TARGET_NR_lookup_dcookie 253 /* ok */
+#define TARGET_NR_epoll_create 254 /* ok */
+#define TARGET_NR_epoll_ctl 255 /* ok */
+#define TARGET_NR_epoll_wait 256 /* obsolete -> sys_epoll_pwait */
+#define TARGET_NR_remap_file_pages 257 /* only for mmu */
+#define TARGET_NR_set_tid_address 258 /* ok */
+#define TARGET_NR_timer_create 259 /* ok */
+#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) /* 260 */ /* ok */
+#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) /* 261 */ /* ok */
+#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) /* 262 */ /* ok */
+#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) /* 263 */ /* ok */
+#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) /* 264 */ /* ok */
+#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) /* 265 */ /* ok */
+#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) /* 266 */ /* ok */
+#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) /* 267 */ /* ok */
+#define TARGET_NR_statfs64 268 /* ok */
+#define TARGET_NR_fstatfs64 269 /* ok */
+#define TARGET_NR_tgkill 270 /* ok */
+#define TARGET_NR_utimes 271 /* obsolete -> sys_futimesat */
+#define TARGET_NR_fadvise64_64 272 /* ok */
+#define TARGET_NR_vserver 273 /* ok */
+#define TARGET_NR_mbind 274 /* only for mmu */
+#define TARGET_NR_get_mempolicy 275 /* only for mmu */
+#define TARGET_NR_set_mempolicy 276 /* only for mmu */
+#define TARGET_NR_mq_open 277 /* ok */
+#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1) /* 278 */ /* ok */
+#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2) /* 279 */ /* ok */
+#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3) /* 280 */ /* ok */
+#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4) /* 281 */ /* ok */
+#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5) /* 282 */ /* ok */
+#define TARGET_NR_kexec_load 283 /* ok */
+#define TARGET_NR_waitid 284 /* ok */
+/* #define TARGET_NR_sys_setaltroot 285 */
+#define TARGET_NR_add_key 286 /* ok */
+#define TARGET_NR_request_key 287 /* ok */
+#define TARGET_NR_keyctl 288 /* ok */
+#define TARGET_NR_ioprio_set 289 /* ok */
+#define TARGET_NR_ioprio_get 290 /* ok */
+#define TARGET_NR_inotify_init 291 /* ok */
+#define TARGET_NR_inotify_add_watch 292 /* ok */
+#define TARGET_NR_inotify_rm_watch 293 /* ok */
+#define TARGET_NR_migrate_pages 294 /* mmu */
+#define TARGET_NR_openat 295 /* ok */
+#define TARGET_NR_mkdirat 296 /* ok */
+#define TARGET_NR_mknodat 297 /* ok */
+#define TARGET_NR_fchownat 298 /* ok */
+#define TARGET_NR_futimesat 299 /* obsolete -> sys_utimesat */
+#define TARGET_NR_fstatat64 300 /* stat64 */
+#define TARGET_NR_unlinkat 301 /* ok */
+#define TARGET_NR_renameat 302 /* ok */
+#define TARGET_NR_linkat 303 /* ok */
+#define TARGET_NR_symlinkat 304 /* ok */
+#define TARGET_NR_readlinkat 305 /* ok */
+#define TARGET_NR_fchmodat 306 /* ok */
+#define TARGET_NR_faccessat 307 /* ok */
+#define TARGET_NR_pselect6 308 /* obsolete -> sys_pselect7 */
+#define TARGET_NR_ppoll 309 /* ok */
+#define TARGET_NR_unshare 310 /* ok */
+#define TARGET_NR_set_robust_list 311 /* ok */
+#define TARGET_NR_get_robust_list 312 /* ok */
+#define TARGET_NR_splice 313 /* ok */
+#define TARGET_NR_sync_file_range 314 /* ok */
+#define TARGET_NR_tee 315 /* ok */
+#define TARGET_NR_vmsplice 316 /* ok */
+#define TARGET_NR_move_pages 317 /* mmu */
+#define TARGET_NR_getcpu 318 /* ok */
+#define TARGET_NR_epoll_pwait 319 /* ok */
+#define TARGET_NR_utimensat 320 /* ok */
+#define TARGET_NR_signalfd 321 /* ok */
+#define TARGET_NR_timerfd_create 322 /* ok */
+#define TARGET_NR_eventfd 323 /* ok */
+#define TARGET_NR_fallocate 324 /* ok */
+#define TARGET_NR_semtimedop 325 /* ok - semaphore group */
+#define TARGET_NR_timerfd_settime 326 /* ok */
+#define TARGET_NR_timerfd_gettime 327 /* ok */
+/* sysv ipc syscalls */
+#define TARGET_NR_semctl 328 /* ok */
+#define TARGET_NR_semget 329 /* ok */
+#define TARGET_NR_semop 330 /* ok */
+#define TARGET_NR_msgctl 331 /* ok */
+#define TARGET_NR_msgget 332 /* ok */
+#define TARGET_NR_msgrcv 333 /* ok */
+#define TARGET_NR_msgsnd 334 /* ok */
+#define TARGET_NR_shmat 335 /* ok */
+#define TARGET_NR_shmctl 336 /* ok */
+#define TARGET_NR_shmdt 337 /* ok */
+#define TARGET_NR_shmget 338 /* ok */
+
+
+#define TARGET_NR_signalfd4 339 /* new */
+#define TARGET_NR_eventfd2 340 /* new */
+#define TARGET_NR_epoll_create1 341 /* new */
+#define TARGET_NR_dup3 342 /* new */
+#define TARGET_NR_pipe2 343 /* new */
+#define TARGET_NR_inotify_init1 344 /* new */
+#define TARGET_NR_socket 345 /* new */
+#define TARGET_NR_socketpair 346 /* new */
+#define TARGET_NR_bind 347 /* new */
+#define TARGET_NR_listen 348 /* new */
+#define TARGET_NR_accept 349 /* new */
+#define TARGET_NR_connect 350 /* new */
+#define TARGET_NR_getsockname 351 /* new */
+#define TARGET_NR_getpeername 352 /* new */
+#define TARGET_NR_sendto 353 /* new */
+#define TARGET_NR_send 354 /* new */
+#define TARGET_NR_recvfrom 355 /* new */
+#define TARGET_NR_recv 356 /* new */
+#define TARGET_NR_setsockopt 357 /* new */
+#define TARGET_NR_getsockopt 358 /* new */
+#define TARGET_NR_shutdown 359 /* new */
+#define TARGET_NR_sendmsg 360 /* new */
+#define TARGET_NR_recvmsg 361 /* new */
+#define TARGET_NR_accept04 362 /* new */
+
+#define TARGET_NR_syscalls 363
+
diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h
new file mode 100644
index 0000000..3d1f7a7
--- /dev/null
+++ b/linux-user/microblaze/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_ulong ss_size;
+ abi_long ss_flags;
+} 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(CPUMBState *state)
+{
+ return state->regs[14];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/microblaze/termbits.h b/linux-user/microblaze/termbits.h
new file mode 100644
index 0000000..fc82ca0
--- /dev/null
+++ b/linux-user/microblaze/termbits.h
@@ -0,0 +1,213 @@
+/* from asm/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 */
+};
+
+/* 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
+
+/* 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_B57600 0010001
+#define TARGET_B115200 0010002
+#define TARGET_B230400 0010003
+#define TARGET_B460800 0010004
+#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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
+
+/* c_cc character offsets */
+#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
+
+/* ioctls */
+
+#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 TARGET_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_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+
+/* 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/mips/syscall.h b/linux-user/mips/syscall.h
new file mode 100644
index 0000000..3deb862
--- /dev/null
+++ b/linux-user/mips/syscall.h
@@ -0,0 +1,227 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+ /* Pad bytes for argument save space on the stack. */
+ abi_ulong pad0[6];
+
+ /* Saved main processor registers. */
+ abi_ulong regs[32];
+
+ /* Saved special registers. */
+ abi_ulong cp0_status;
+ abi_ulong lo;
+ abi_ulong hi;
+ abi_ulong cp0_badvaddr;
+ abi_ulong cp0_cause;
+ abi_ulong cp0_epc;
+};
+
+/* Target errno definitions taken from asm-mips/errno.h */
+#undef TARGET_ENOMSG
+#define TARGET_ENOMSG 35 /* Identifier removed */
+#undef TARGET_EIDRM
+#define TARGET_EIDRM 36 /* Identifier removed */
+#undef TARGET_ECHRNG
+#define TARGET_ECHRNG 37 /* Channel number out of range */
+#undef TARGET_EL2NSYNC
+#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */
+#undef TARGET_EL3HLT
+#define TARGET_EL3HLT 39 /* Level 3 halted */
+#undef TARGET_EL3RST
+#define TARGET_EL3RST 40 /* Level 3 reset */
+#undef TARGET_ELNRNG
+#define TARGET_ELNRNG 41 /* Link number out of range */
+#undef TARGET_EUNATCH
+#define TARGET_EUNATCH 42 /* Protocol driver not attached */
+#undef TARGET_ENOCSI
+#define TARGET_ENOCSI 43 /* No CSI structure available */
+#undef TARGET_EL2HLT
+#define TARGET_EL2HLT 44 /* Level 2 halted */
+#undef TARGET_EDEADLK
+#define TARGET_EDEADLK 45 /* Resource deadlock would occur */
+#undef TARGET_ENOLCK
+#define TARGET_ENOLCK 46 /* No record locks available */
+#undef TARGET_EBADE
+#define TARGET_EBADE 50 /* Invalid exchange */
+#undef TARGET_EBADR
+#define TARGET_EBADR 51 /* Invalid request descriptor */
+#undef TARGET_EXFULL
+#define TARGET_EXFULL 52 /* TARGET_Exchange full */
+#undef TARGET_ENOANO
+#define TARGET_ENOANO 53 /* No anode */
+#undef TARGET_EBADRQC
+#define TARGET_EBADRQC 54 /* Invalid request code */
+#undef TARGET_EBADSLT
+#define TARGET_EBADSLT 55 /* Invalid slot */
+#undef TARGET_EDEADLOCK
+#define TARGET_EDEADLOCK 56 /* File locking deadlock error */
+#undef TARGET_EBFONT
+#define TARGET_EBFONT 59 /* Bad font file format */
+#undef TARGET_ENOSTR
+#define TARGET_ENOSTR 60 /* Device not a stream */
+#undef TARGET_ENODATA
+#define TARGET_ENODATA 61 /* No data available */
+#undef TARGET_ETIME
+#define TARGET_ETIME 62 /* Timer expired */
+#undef TARGET_ENOSR
+#define TARGET_ENOSR 63 /* Out of streams resources */
+#undef TARGET_ENONET
+#define TARGET_ENONET 64 /* Machine is not on the network */
+#undef TARGET_ENOPKG
+#define TARGET_ENOPKG 65 /* Package not installed */
+#undef TARGET_EREMOTE
+#define TARGET_EREMOTE 66 /* Object is remote */
+#undef TARGET_ENOLINK
+#define TARGET_ENOLINK 67 /* Link has been severed */
+#undef TARGET_EADV
+#define TARGET_EADV 68 /* Advertise error */
+#undef TARGET_ESRMNT
+#define TARGET_ESRMNT 69 /* Srmount error */
+#undef TARGET_ECOMM
+#define TARGET_ECOMM 70 /* Communication error on send */
+#undef TARGET_EPROTO
+#define TARGET_EPROTO 71 /* Protocol error */
+#undef TARGET_EDOTDOT
+#define TARGET_EDOTDOT 73 /* RFS specific error */
+#undef TARGET_EMULTIHOP
+#define TARGET_EMULTIHOP 74 /* Multihop attempted */
+#undef TARGET_EBADMSG
+#define TARGET_EBADMSG 77 /* Not a data message */
+#undef TARGET_ENAMETOOLONG
+#define TARGET_ENAMETOOLONG 78 /* File name too long */
+#undef TARGET_EOVERFLOW
+#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */
+#undef TARGET_ENOTUNIQ
+#define TARGET_ENOTUNIQ 80 /* Name not unique on network */
+#undef TARGET_EBADFD
+#define TARGET_EBADFD 81 /* File descriptor in bad state */
+#undef TARGET_EREMCHG
+#define TARGET_EREMCHG 82 /* Remote address changed */
+#undef TARGET_ELIBACC
+#define TARGET_ELIBACC 83 /* Can not access a needed shared library */
+#undef TARGET_ELIBBAD
+#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */
+#undef TARGET_ELIBSCN
+#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */
+#undef TARGET_ELIBMAX
+#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */
+#undef TARGET_ELIBEXEC
+#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */
+#undef TARGET_EILSEQ
+#define TARGET_EILSEQ 88 /* Illegal byte sequence */
+#undef TARGET_ENOSYS
+#define TARGET_ENOSYS 89 /* Function not implemented */
+#undef TARGET_ELOOP
+#define TARGET_ELOOP 90 /* Too many symbolic links encountered */
+#undef TARGET_ERESTART
+#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */
+#undef TARGET_ESTRPIPE
+#define TARGET_ESTRPIPE 92 /* Streams pipe error */
+#undef TARGET_ENOTEMPTY
+#define TARGET_ENOTEMPTY 93 /* Directory not empty */
+#undef TARGET_EUSERS
+#define TARGET_EUSERS 94 /* Too many users */
+#undef TARGET_ENOTSOCK
+#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */
+#undef TARGET_EDESTADDRREQ
+#define TARGET_EDESTADDRREQ 96 /* Destination address required */
+#undef TARGET_EMSGSIZE
+#define TARGET_EMSGSIZE 97 /* Message too long */
+#undef TARGET_EPROTOTYPE
+#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */
+#undef TARGET_ENOPROTOOPT
+#define TARGET_ENOPROTOOPT 99 /* Protocol not available */
+#undef TARGET_EPROTONOSUPPORT
+#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */
+#undef TARGET_ESOCKTNOSUPPORT
+#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */
+#undef TARGET_EOPNOTSUPP
+#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */
+#undef TARGET_EPFNOSUPPORT
+#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */
+#undef TARGET_EAFNOSUPPORT
+#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */
+#undef TARGET_EADDRINUSE
+#define TARGET_EADDRINUSE 125 /* Address already in use */
+#undef TARGET_EADDRNOTAVAIL
+#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */
+#undef TARGET_ENETDOWN
+#define TARGET_ENETDOWN 127 /* Network is down */
+#undef TARGET_ENETUNREACH
+#define TARGET_ENETUNREACH 128 /* Network is unreachable */
+#undef TARGET_ENETRESET
+#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */
+#undef TARGET_ECONNABORTED
+#define TARGET_ECONNABORTED 130 /* Software caused connection abort */
+#undef TARGET_ECONNRESET
+#define TARGET_ECONNRESET 131 /* Connection reset by peer */
+#undef TARGET_ENOBUFS
+#define TARGET_ENOBUFS 132 /* No buffer space available */
+#undef TARGET_EISCONN
+#define TARGET_EISCONN 133 /* Transport endpoint is already connected */
+#undef TARGET_ENOTCONN
+#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */
+#undef TARGET_EUCLEAN
+#define TARGET_EUCLEAN 135 /* Structure needs cleaning */
+#undef TARGET_ENOTNAM
+#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */
+#undef TARGET_ENAVAIL
+#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */
+#undef TARGET_EISNAM
+#define TARGET_EISNAM 139 /* Is a named type file */
+#undef TARGET_EREMOTEIO
+#define TARGET_EREMOTEIO 140 /* Remote I/O error */
+#undef TARGET_EINIT
+#define TARGET_EINIT 141 /* Reserved */
+#undef TARGET_EREMDEV
+#define TARGET_EREMDEV 142 /* TARGET_Error 142 */
+#undef TARGET_ESHUTDOWN
+#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */
+#undef TARGET_ETOOMANYREFS
+#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */
+#undef TARGET_ETIMEDOUT
+#define TARGET_ETIMEDOUT 145 /* Connection timed out */
+#undef TARGET_ECONNREFUSED
+#define TARGET_ECONNREFUSED 146 /* Connection refused */
+#undef TARGET_EHOSTDOWN
+#define TARGET_EHOSTDOWN 147 /* Host is down */
+#undef TARGET_EHOSTUNREACH
+#define TARGET_EHOSTUNREACH 148 /* No route to host */
+#undef TARGET_EALREADY
+#define TARGET_EALREADY 149 /* Operation already in progress */
+#undef TARGET_EINPROGRESS
+#define TARGET_EINPROGRESS 150 /* Operation now in progress */
+#undef TARGET_ESTALE
+#define TARGET_ESTALE 151 /* Stale NFS file handle */
+#undef TARGET_ECANCELED
+#define TARGET_ECANCELED 158 /* AIO operation canceled */
+/*
+ * These error are Linux extensions.
+ */
+#undef TARGET_ENOMEDIUM
+#define TARGET_ENOMEDIUM 159 /* No medium found */
+#undef TARGET_EMEDIUMTYPE
+#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */
+#undef TARGET_ENOKEY
+#define TARGET_ENOKEY 161 /* Required key not available */
+#undef TARGET_EKEYEXPIRED
+#define TARGET_EKEYEXPIRED 162 /* Key has expired */
+#undef TARGET_EKEYREVOKED
+#define TARGET_EKEYREVOKED 163 /* Key has been revoked */
+#undef TARGET_EKEYREJECTED
+#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */
+
+/* for robust mutexes */
+#undef TARGET_EOWNERDEAD
+#define TARGET_EOWNERDEAD 165 /* Owner died */
+#undef TARGET_ENOTRECOVERABLE
+#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */
+
+
+
+/* Nasty hack: define a fake errno value for use by sigreturn. */
+#define TARGET_QEMU_ESIGRETURN 255
+
+#define UNAME_MACHINE "mips"
diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h
new file mode 100644
index 0000000..0595308
--- /dev/null
+++ b/linux-user/mips/syscall_nr.h
@@ -0,0 +1,334 @@
+/*
+ * Linux o32 style syscalls are in the range from 4000 to 4999.
+ */
+#define TARGET_NR_Linux 4000
+#define TARGET_NR_syscall (TARGET_NR_Linux + 0)
+#define TARGET_NR_exit (TARGET_NR_Linux + 1)
+#define TARGET_NR_fork (TARGET_NR_Linux + 2)
+#define TARGET_NR_read (TARGET_NR_Linux + 3)
+#define TARGET_NR_write (TARGET_NR_Linux + 4)
+#define TARGET_NR_open (TARGET_NR_Linux + 5)
+#define TARGET_NR_close (TARGET_NR_Linux + 6)
+#define TARGET_NR_waitpid (TARGET_NR_Linux + 7)
+#define TARGET_NR_creat (TARGET_NR_Linux + 8)
+#define TARGET_NR_link (TARGET_NR_Linux + 9)
+#define TARGET_NR_unlink (TARGET_NR_Linux + 10)
+#define TARGET_NR_execve (TARGET_NR_Linux + 11)
+#define TARGET_NR_chdir (TARGET_NR_Linux + 12)
+#define TARGET_NR_time (TARGET_NR_Linux + 13)
+#define TARGET_NR_mknod (TARGET_NR_Linux + 14)
+#define TARGET_NR_chmod (TARGET_NR_Linux + 15)
+#define TARGET_NR_lchown (TARGET_NR_Linux + 16)
+#define TARGET_NR_break (TARGET_NR_Linux + 17)
+#define TARGET_NR_unused18 (TARGET_NR_Linux + 18)
+#define TARGET_NR_lseek (TARGET_NR_Linux + 19)
+#define TARGET_NR_getpid (TARGET_NR_Linux + 20)
+#define TARGET_NR_mount (TARGET_NR_Linux + 21)
+#define TARGET_NR_umount (TARGET_NR_Linux + 22)
+#define TARGET_NR_setuid (TARGET_NR_Linux + 23)
+#define TARGET_NR_getuid (TARGET_NR_Linux + 24)
+#define TARGET_NR_stime (TARGET_NR_Linux + 25)
+#define TARGET_NR_ptrace (TARGET_NR_Linux + 26)
+#define TARGET_NR_alarm (TARGET_NR_Linux + 27)
+#define TARGET_NR_unused28 (TARGET_NR_Linux + 28)
+#define TARGET_NR_pause (TARGET_NR_Linux + 29)
+#define TARGET_NR_utime (TARGET_NR_Linux + 30)
+#define TARGET_NR_stty (TARGET_NR_Linux + 31)
+#define TARGET_NR_gtty (TARGET_NR_Linux + 32)
+#define TARGET_NR_access (TARGET_NR_Linux + 33)
+#define TARGET_NR_nice (TARGET_NR_Linux + 34)
+#define TARGET_NR_ftime (TARGET_NR_Linux + 35)
+#define TARGET_NR_sync (TARGET_NR_Linux + 36)
+#define TARGET_NR_kill (TARGET_NR_Linux + 37)
+#define TARGET_NR_rename (TARGET_NR_Linux + 38)
+#define TARGET_NR_mkdir (TARGET_NR_Linux + 39)
+#define TARGET_NR_rmdir (TARGET_NR_Linux + 40)
+#define TARGET_NR_dup (TARGET_NR_Linux + 41)
+#define TARGET_NR_pipe (TARGET_NR_Linux + 42)
+#define TARGET_NR_times (TARGET_NR_Linux + 43)
+#define TARGET_NR_prof (TARGET_NR_Linux + 44)
+#define TARGET_NR_brk (TARGET_NR_Linux + 45)
+#define TARGET_NR_setgid (TARGET_NR_Linux + 46)
+#define TARGET_NR_getgid (TARGET_NR_Linux + 47)
+#define TARGET_NR_signal (TARGET_NR_Linux + 48)
+#define TARGET_NR_geteuid (TARGET_NR_Linux + 49)
+#define TARGET_NR_getegid (TARGET_NR_Linux + 50)
+#define TARGET_NR_acct (TARGET_NR_Linux + 51)
+#define TARGET_NR_umount2 (TARGET_NR_Linux + 52)
+#define TARGET_NR_lock (TARGET_NR_Linux + 53)
+#define TARGET_NR_ioctl (TARGET_NR_Linux + 54)
+#define TARGET_NR_fcntl (TARGET_NR_Linux + 55)
+#define TARGET_NR_mpx (TARGET_NR_Linux + 56)
+#define TARGET_NR_setpgid (TARGET_NR_Linux + 57)
+#define TARGET_NR_ulimit (TARGET_NR_Linux + 58)
+#define TARGET_NR_unused59 (TARGET_NR_Linux + 59)
+#define TARGET_NR_umask (TARGET_NR_Linux + 60)
+#define TARGET_NR_chroot (TARGET_NR_Linux + 61)
+#define TARGET_NR_ustat (TARGET_NR_Linux + 62)
+#define TARGET_NR_dup2 (TARGET_NR_Linux + 63)
+#define TARGET_NR_getppid (TARGET_NR_Linux + 64)
+#define TARGET_NR_getpgrp (TARGET_NR_Linux + 65)
+#define TARGET_NR_setsid (TARGET_NR_Linux + 66)
+#define TARGET_NR_sigaction (TARGET_NR_Linux + 67)
+#define TARGET_NR_sgetmask (TARGET_NR_Linux + 68)
+#define TARGET_NR_ssetmask (TARGET_NR_Linux + 69)
+#define TARGET_NR_setreuid (TARGET_NR_Linux + 70)
+#define TARGET_NR_setregid (TARGET_NR_Linux + 71)
+#define TARGET_NR_sigsuspend (TARGET_NR_Linux + 72)
+#define TARGET_NR_sigpending (TARGET_NR_Linux + 73)
+#define TARGET_NR_sethostname (TARGET_NR_Linux + 74)
+#define TARGET_NR_setrlimit (TARGET_NR_Linux + 75)
+#define TARGET_NR_getrlimit (TARGET_NR_Linux + 76)
+#define TARGET_NR_getrusage (TARGET_NR_Linux + 77)
+#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 78)
+#define TARGET_NR_settimeofday (TARGET_NR_Linux + 79)
+#define TARGET_NR_getgroups (TARGET_NR_Linux + 80)
+#define TARGET_NR_setgroups (TARGET_NR_Linux + 81)
+#define TARGET_NR_reserved82 (TARGET_NR_Linux + 82)
+#define TARGET_NR_symlink (TARGET_NR_Linux + 83)
+#define TARGET_NR_unused84 (TARGET_NR_Linux + 84)
+#define TARGET_NR_readlink (TARGET_NR_Linux + 85)
+#define TARGET_NR_uselib (TARGET_NR_Linux + 86)
+#define TARGET_NR_swapon (TARGET_NR_Linux + 87)
+#define TARGET_NR_reboot (TARGET_NR_Linux + 88)
+#define TARGET_NR_readdir (TARGET_NR_Linux + 89)
+#define TARGET_NR_mmap (TARGET_NR_Linux + 90)
+#define TARGET_NR_munmap (TARGET_NR_Linux + 91)
+#define TARGET_NR_truncate (TARGET_NR_Linux + 92)
+#define TARGET_NR_ftruncate (TARGET_NR_Linux + 93)
+#define TARGET_NR_fchmod (TARGET_NR_Linux + 94)
+#define TARGET_NR_fchown (TARGET_NR_Linux + 95)
+#define TARGET_NR_getpriority (TARGET_NR_Linux + 96)
+#define TARGET_NR_setpriority (TARGET_NR_Linux + 97)
+#define TARGET_NR_profil (TARGET_NR_Linux + 98)
+#define TARGET_NR_statfs (TARGET_NR_Linux + 99)
+#define TARGET_NR_fstatfs (TARGET_NR_Linux + 100)
+#define TARGET_NR_ioperm (TARGET_NR_Linux + 101)
+#define TARGET_NR_socketcall (TARGET_NR_Linux + 102)
+#define TARGET_NR_syslog (TARGET_NR_Linux + 103)
+#define TARGET_NR_setitimer (TARGET_NR_Linux + 104)
+#define TARGET_NR_getitimer (TARGET_NR_Linux + 105)
+#define TARGET_NR_stat (TARGET_NR_Linux + 106)
+#define TARGET_NR_lstat (TARGET_NR_Linux + 107)
+#define TARGET_NR_fstat (TARGET_NR_Linux + 108)
+#define TARGET_NR_unused109 (TARGET_NR_Linux + 109)
+#define TARGET_NR_iopl (TARGET_NR_Linux + 110)
+#define TARGET_NR_vhangup (TARGET_NR_Linux + 111)
+#define TARGET_NR_idle (TARGET_NR_Linux + 112)
+#define TARGET_NR_vm86 (TARGET_NR_Linux + 113)
+#define TARGET_NR_wait4 (TARGET_NR_Linux + 114)
+#define TARGET_NR_swapoff (TARGET_NR_Linux + 115)
+#define TARGET_NR_sysinfo (TARGET_NR_Linux + 116)
+#define TARGET_NR_ipc (TARGET_NR_Linux + 117)
+#define TARGET_NR_fsync (TARGET_NR_Linux + 118)
+#define TARGET_NR_sigreturn (TARGET_NR_Linux + 119)
+#define TARGET_NR_clone (TARGET_NR_Linux + 120)
+#define TARGET_NR_setdomainname (TARGET_NR_Linux + 121)
+#define TARGET_NR_uname (TARGET_NR_Linux + 122)
+#define TARGET_NR_modify_ldt (TARGET_NR_Linux + 123)
+#define TARGET_NR_adjtimex (TARGET_NR_Linux + 124)
+#define TARGET_NR_mprotect (TARGET_NR_Linux + 125)
+#define TARGET_NR_sigprocmask (TARGET_NR_Linux + 126)
+#define TARGET_NR_create_module (TARGET_NR_Linux + 127)
+#define TARGET_NR_init_module (TARGET_NR_Linux + 128)
+#define TARGET_NR_delete_module (TARGET_NR_Linux + 129)
+#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 130)
+#define TARGET_NR_quotactl (TARGET_NR_Linux + 131)
+#define TARGET_NR_getpgid (TARGET_NR_Linux + 132)
+#define TARGET_NR_fchdir (TARGET_NR_Linux + 133)
+#define TARGET_NR_bdflush (TARGET_NR_Linux + 134)
+#define TARGET_NR_sysfs (TARGET_NR_Linux + 135)
+#define TARGET_NR_personality (TARGET_NR_Linux + 136)
+#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 137) /* Syscall for Andrew File System */
+#define TARGET_NR_setfsuid (TARGET_NR_Linux + 138)
+#define TARGET_NR_setfsgid (TARGET_NR_Linux + 139)
+#define TARGET_NR__llseek (TARGET_NR_Linux + 140)
+#define TARGET_NR_getdents (TARGET_NR_Linux + 141)
+#define TARGET_NR__newselect (TARGET_NR_Linux + 142)
+#define TARGET_NR_flock (TARGET_NR_Linux + 143)
+#define TARGET_NR_msync (TARGET_NR_Linux + 144)
+#define TARGET_NR_readv (TARGET_NR_Linux + 145)
+#define TARGET_NR_writev (TARGET_NR_Linux + 146)
+#define TARGET_NR_cacheflush (TARGET_NR_Linux + 147)
+#define TARGET_NR_cachectl (TARGET_NR_Linux + 148)
+#define TARGET_NR_sysmips (TARGET_NR_Linux + 149)
+#define TARGET_NR_unused150 (TARGET_NR_Linux + 150)
+#define TARGET_NR_getsid (TARGET_NR_Linux + 151)
+#define TARGET_NR_fdatasync (TARGET_NR_Linux + 152)
+#define TARGET_NR__sysctl (TARGET_NR_Linux + 153)
+#define TARGET_NR_mlock (TARGET_NR_Linux + 154)
+#define TARGET_NR_munlock (TARGET_NR_Linux + 155)
+#define TARGET_NR_mlockall (TARGET_NR_Linux + 156)
+#define TARGET_NR_munlockall (TARGET_NR_Linux + 157)
+#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 158)
+#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 159)
+#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 160)
+#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 161)
+#define TARGET_NR_sched_yield (TARGET_NR_Linux + 162)
+#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 163)
+#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 164)
+#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 165)
+#define TARGET_NR_nanosleep (TARGET_NR_Linux + 166)
+#define TARGET_NR_mremap (TARGET_NR_Linux + 167)
+#define TARGET_NR_accept (TARGET_NR_Linux + 168)
+#define TARGET_NR_bind (TARGET_NR_Linux + 169)
+#define TARGET_NR_connect (TARGET_NR_Linux + 170)
+#define TARGET_NR_getpeername (TARGET_NR_Linux + 171)
+#define TARGET_NR_getsockname (TARGET_NR_Linux + 172)
+#define TARGET_NR_getsockopt (TARGET_NR_Linux + 173)
+#define TARGET_NR_listen (TARGET_NR_Linux + 174)
+#define TARGET_NR_recv (TARGET_NR_Linux + 175)
+#define TARGET_NR_recvfrom (TARGET_NR_Linux + 176)
+#define TARGET_NR_recvmsg (TARGET_NR_Linux + 177)
+#define TARGET_NR_send (TARGET_NR_Linux + 178)
+#define TARGET_NR_sendmsg (TARGET_NR_Linux + 179)
+#define TARGET_NR_sendto (TARGET_NR_Linux + 180)
+#define TARGET_NR_setsockopt (TARGET_NR_Linux + 181)
+#define TARGET_NR_shutdown (TARGET_NR_Linux + 182)
+#define TARGET_NR_socket (TARGET_NR_Linux + 183)
+#define TARGET_NR_socketpair (TARGET_NR_Linux + 184)
+#define TARGET_NR_setresuid (TARGET_NR_Linux + 185)
+#define TARGET_NR_getresuid (TARGET_NR_Linux + 186)
+#define TARGET_NR_query_module (TARGET_NR_Linux + 187)
+#define TARGET_NR_poll (TARGET_NR_Linux + 188)
+#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 189)
+#define TARGET_NR_setresgid (TARGET_NR_Linux + 190)
+#define TARGET_NR_getresgid (TARGET_NR_Linux + 191)
+#define TARGET_NR_prctl (TARGET_NR_Linux + 192)
+#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 193)
+#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 194)
+#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 195)
+#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 196)
+#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 197)
+#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 198)
+#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 199)
+#define TARGET_NR_pread64 (TARGET_NR_Linux + 200)
+#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 201)
+#define TARGET_NR_chown (TARGET_NR_Linux + 202)
+#define TARGET_NR_getcwd (TARGET_NR_Linux + 203)
+#define TARGET_NR_capget (TARGET_NR_Linux + 204)
+#define TARGET_NR_capset (TARGET_NR_Linux + 205)
+#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 206)
+#define TARGET_NR_sendfile (TARGET_NR_Linux + 207)
+#define TARGET_NR_getpmsg (TARGET_NR_Linux + 208)
+#define TARGET_NR_putpmsg (TARGET_NR_Linux + 209)
+#define TARGET_NR_mmap2 (TARGET_NR_Linux + 210)
+#define TARGET_NR_truncate64 (TARGET_NR_Linux + 211)
+#define TARGET_NR_ftruncate64 (TARGET_NR_Linux + 212)
+#define TARGET_NR_stat64 (TARGET_NR_Linux + 213)
+#define TARGET_NR_lstat64 (TARGET_NR_Linux + 214)
+#define TARGET_NR_fstat64 (TARGET_NR_Linux + 215)
+#define TARGET_NR_pivot_root (TARGET_NR_Linux + 216)
+#define TARGET_NR_mincore (TARGET_NR_Linux + 217)
+#define TARGET_NR_madvise (TARGET_NR_Linux + 218)
+#define TARGET_NR_getdents64 (TARGET_NR_Linux + 219)
+#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 220)
+#define TARGET_NR_reserved221 (TARGET_NR_Linux + 221)
+#define TARGET_NR_gettid (TARGET_NR_Linux + 222)
+#define TARGET_NR_readahead (TARGET_NR_Linux + 223)
+#define TARGET_NR_setxattr (TARGET_NR_Linux + 224)
+#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 225)
+#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 226)
+#define TARGET_NR_getxattr (TARGET_NR_Linux + 227)
+#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 228)
+#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 229)
+#define TARGET_NR_listxattr (TARGET_NR_Linux + 230)
+#define TARGET_NR_llistxattr (TARGET_NR_Linux + 231)
+#define TARGET_NR_flistxattr (TARGET_NR_Linux + 232)
+#define TARGET_NR_removexattr (TARGET_NR_Linux + 233)
+#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 234)
+#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 235)
+#define TARGET_NR_tkill (TARGET_NR_Linux + 236)
+#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 237)
+#define TARGET_NR_futex (TARGET_NR_Linux + 238)
+#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 239)
+#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 240)
+#define TARGET_NR_io_setup (TARGET_NR_Linux + 241)
+#define TARGET_NR_io_destroy (TARGET_NR_Linux + 242)
+#define TARGET_NR_io_getevents (TARGET_NR_Linux + 243)
+#define TARGET_NR_io_submit (TARGET_NR_Linux + 244)
+#define TARGET_NR_io_cancel (TARGET_NR_Linux + 245)
+#define TARGET_NR_exit_group (TARGET_NR_Linux + 246)
+#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 247)
+#define TARGET_NR_epoll_create (TARGET_NR_Linux + 248)
+#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 249)
+#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 250)
+#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 251)
+#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 252)
+#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 253)
+#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 254)
+#define TARGET_NR_statfs64 (TARGET_NR_Linux + 255)
+#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 256)
+#define TARGET_NR_timer_create (TARGET_NR_Linux + 257)
+#define TARGET_NR_timer_settime (TARGET_NR_Linux + 258)
+#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 259)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 260)
+#define TARGET_NR_timer_delete (TARGET_NR_Linux + 261)
+#define TARGET_NR_clock_settime (TARGET_NR_Linux + 262)
+#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 263)
+#define TARGET_NR_clock_getres (TARGET_NR_Linux + 264)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 265)
+#define TARGET_NR_tgkill (TARGET_NR_Linux + 266)
+#define TARGET_NR_utimes (TARGET_NR_Linux + 267)
+#define TARGET_NR_mbind (TARGET_NR_Linux + 268)
+#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 269)
+#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 270)
+#define TARGET_NR_mq_open (TARGET_NR_Linux + 271)
+#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 272)
+#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 273)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 274)
+#define TARGET_NR_mq_notify (TARGET_NR_Linux + 275)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 276)
+#define TARGET_NR_vserver (TARGET_NR_Linux + 277)
+#define TARGET_NR_waitid (TARGET_NR_Linux + 278)
+/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 279) */
+#define TARGET_NR_add_key (TARGET_NR_Linux + 280)
+#define TARGET_NR_request_key (TARGET_NR_Linux + 281)
+#define TARGET_NR_keyctl (TARGET_NR_Linux + 282)
+#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 283)
+#define TARGET_NR_inotify_init (TARGET_NR_Linux + 284)
+#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 285)
+#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 286)
+#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 287)
+#define TARGET_NR_openat (TARGET_NR_Linux + 288)
+#define TARGET_NR_mkdirat (TARGET_NR_Linux + 289)
+#define TARGET_NR_mknodat (TARGET_NR_Linux + 290)
+#define TARGET_NR_fchownat (TARGET_NR_Linux + 291)
+#define TARGET_NR_futimesat (TARGET_NR_Linux + 292)
+#define TARGET_NR_fstatat64 (TARGET_NR_Linux + 293)
+#define TARGET_NR_unlinkat (TARGET_NR_Linux + 294)
+#define TARGET_NR_renameat (TARGET_NR_Linux + 295)
+#define TARGET_NR_linkat (TARGET_NR_Linux + 296)
+#define TARGET_NR_symlinkat (TARGET_NR_Linux + 297)
+#define TARGET_NR_readlinkat (TARGET_NR_Linux + 298)
+#define TARGET_NR_fchmodat (TARGET_NR_Linux + 299)
+#define TARGET_NR_faccessat (TARGET_NR_Linux + 300)
+#define TARGET_NR_pselect6 (TARGET_NR_Linux + 301)
+#define TARGET_NR_ppoll (TARGET_NR_Linux + 302)
+#define TARGET_NR_unshare (TARGET_NR_Linux + 303)
+#define TARGET_NR_splice (TARGET_NR_Linux + 304)
+#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 305)
+#define TARGET_NR_tee (TARGET_NR_Linux + 306)
+#define TARGET_NR_vmsplice (TARGET_NR_Linux + 307)
+#define TARGET_NR_move_pages (TARGET_NR_Linux + 308)
+#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 309)
+#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 310)
+#define TARGET_NR_kexec_load (TARGET_NR_Linux + 311)
+#define TARGET_NR_getcpu (TARGET_NR_Linux + 312)
+#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 313)
+#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 314)
+#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 315)
+#define TARGET_NR_utimensat (TARGET_NR_Linux + 316)
+#define TARGET_NR_signalfd (TARGET_NR_Linux + 317)
+#define TARGET_NR_timerfd (TARGET_NR_Linux + 318)
+#define TARGET_NR_eventfd (TARGET_NR_Linux + 319)
+#define TARGET_NR_fallocate (TARGET_NR_Linux + 320)
+#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 321)
+#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 322)
+#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 323)
+#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 324)
+#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 325)
+#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 326)
+#define TARGET_NR_dup3 (TARGET_NR_Linux + 327)
+#define TARGET_NR_pipe2 (TARGET_NR_Linux + 328)
+#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 329)
diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h
new file mode 100644
index 0000000..6e1dc8b
--- /dev/null
+++ b/linux-user/mips/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_long ss_sp;
+ abi_ulong ss_size;
+ abi_long ss_flags;
+} 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(CPUMIPSState *state)
+{
+ return state->active_tc.gpr[29];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/mips/termbits.h b/linux-user/mips/termbits.h
new file mode 100644
index 0000000..d3a6cf8
--- /dev/null
+++ b/linux-user/mips/termbits.h
@@ -0,0 +1,245 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 23
+
+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 */
+};
+
+/* 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 (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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_IEXTEN 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_TOSTOP 0100000
+#define TARGET_ITOSTOP TARGET_TOSTOP
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VMIN 4
+#define TARGET_VTIME 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+/* VDSUSP not supported */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOF 16
+#define TARGET_VEOL 17
+
+/* ioctls */
+
+#define TARGET_TCGETA 0x5401
+#define TARGET_TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */
+#define TARGET_TCSETAW 0x5403
+#define TARGET_TCSETAF 0x5404
+
+#define TARGET_TCSBRK 0x5405
+#define TARGET_TCXONC 0x5406
+#define TARGET_TCFLSH 0x5407
+
+#define TARGET_TCGETS 0x540d
+#define TARGET_TCSETS 0x540e
+#define TARGET_TCSETSW 0x540f
+#define TARGET_TCSETSF 0x5410
+
+#define TARGET_TIOCEXCL 0x740d /* set exclusive use of tty */
+#define TARGET_TIOCNXCL 0x740e /* reset exclusive use of tty */
+#define TARGET_TIOCOUTQ 0x7472 /* output queue size */
+#define TARGET_TIOCSTI 0x5472 /* simulate terminal input */
+#define TARGET_TIOCMGET 0x741d /* get all modem bits */
+#define TARGET_TIOCMBIS 0x741b /* bis modem bits */
+#define TARGET_TIOCMBIC 0x741c /* bic modem bits */
+#define TARGET_TIOCMSET 0x741a /* set all modem bits */
+#define TARGET_TIOCPKT 0x5470 /* pty: set/clear packet mode */
+#define TARGET_TIOCPKT_DATA 0x00 /* data packet */
+#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */
+#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */
+#define TARGET_TIOCPKT_STOP 0x04 /* stop output */
+#define TARGET_TIOCPKT_START 0x08 /* start output */
+#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */
+#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */
+/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize) /* set window size */
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize) /* get window size */
+#define TARGET_TIOCNOTTY 0x5471 /* void tty association */
+#define TARGET_TIOCSETD 0x7401
+#define TARGET_TIOCGETD 0x7400
+
+#define TARGET_FIOCLEX 0x6601
+#define TARGET_FIONCLEX 0x6602
+#define TARGET_FIOASYNC 0x667d
+#define TARGET_FIONBIO 0x667e
+#define TARGET_FIOQSIZE 0x667f
+
+#define TARGET_TIOCGLTC 0x7474 /* get special local chars */
+#define TARGET_TIOCSLTC 0x7475 /* set special local chars */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */
+#define TARGET_TIOCCONS TARGET_IOW('t', 120, int) /* become virtual console */
+
+#define TARGET_FIONREAD 0x467f
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+#define TARGET_TIOCGETP 0x7408
+#define TARGET_TIOCSETP 0x7409
+#define TARGET_TIOCSETN 0x740a /* TIOCSETP wo flush */
+
+/* #define TARGET_TIOCSETA TARGET_IOW('t', 20, struct termios) set termios struct */
+/* #define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct termios) drain output, set */
+/* #define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct termios) drn out, fls in, set */
+/* #define TARGET_TIOCGETD TARGET_IOR('t', 26, int) get line discipline */
+/* #define TARGET_TIOCSETD TARGET_IOW('t', 27, int) set line discipline */
+ /* 127-124 compat */
+
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+/* I hope the range from 0x5480 on is free ... */
+#define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */
+#define TARGET_TIOCGSOFTCAR 0x5481
+#define TARGET_TIOCSSOFTCAR 0x5482
+#define TARGET_TIOCLINUX 0x5483
+#define TARGET_TIOCGSERIAL 0x5484
+#define TARGET_TIOCSSERIAL 0x5485
+#define TARGET_TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCSERCONFIG 0x5488
+#define TARGET_TIOCSERGWILD 0x5489
+#define TARGET_TIOCSERSWILD 0x548a
+#define TARGET_TIOCGLCKTRMIOS 0x548b
+#define TARGET_TIOCSLCKTRMIOS 0x548c
+#define TARGET_TIOCSERGSTRUCT 0x548d /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x548e /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x548f /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x5490 /* Set multiport config */
+#define TARGET_TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */
diff --git a/linux-user/mips64/syscall.h b/linux-user/mips64/syscall.h
new file mode 100644
index 0000000..668a2b9
--- /dev/null
+++ b/linux-user/mips64/syscall.h
@@ -0,0 +1,221 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+ /* Saved main processor registers. */
+ abi_ulong regs[32];
+
+ /* Saved special registers. */
+ abi_ulong cp0_status;
+ abi_ulong lo;
+ abi_ulong hi;
+ abi_ulong cp0_badvaddr;
+ abi_ulong cp0_cause;
+ abi_ulong cp0_epc;
+};
+
+/* Target errno definitions taken from asm-mips/errno.h */
+#undef TARGET_ENOMSG
+#define TARGET_ENOMSG 35 /* Identifier removed */
+#undef TARGET_EIDRM
+#define TARGET_EIDRM 36 /* Identifier removed */
+#undef TARGET_ECHRNG
+#define TARGET_ECHRNG 37 /* Channel number out of range */
+#undef TARGET_EL2NSYNC
+#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */
+#undef TARGET_EL3HLT
+#define TARGET_EL3HLT 39 /* Level 3 halted */
+#undef TARGET_EL3RST
+#define TARGET_EL3RST 40 /* Level 3 reset */
+#undef TARGET_ELNRNG
+#define TARGET_ELNRNG 41 /* Link number out of range */
+#undef TARGET_EUNATCH
+#define TARGET_EUNATCH 42 /* Protocol driver not attached */
+#undef TARGET_ENOCSI
+#define TARGET_ENOCSI 43 /* No CSI structure available */
+#undef TARGET_EL2HLT
+#define TARGET_EL2HLT 44 /* Level 2 halted */
+#undef TARGET_EDEADLK
+#define TARGET_EDEADLK 45 /* Resource deadlock would occur */
+#undef TARGET_ENOLCK
+#define TARGET_ENOLCK 46 /* No record locks available */
+#undef TARGET_EBADE
+#define TARGET_EBADE 50 /* Invalid exchange */
+#undef TARGET_EBADR
+#define TARGET_EBADR 51 /* Invalid request descriptor */
+#undef TARGET_EXFULL
+#define TARGET_EXFULL 52 /* TARGET_Exchange full */
+#undef TARGET_ENOANO
+#define TARGET_ENOANO 53 /* No anode */
+#undef TARGET_EBADRQC
+#define TARGET_EBADRQC 54 /* Invalid request code */
+#undef TARGET_EBADSLT
+#define TARGET_EBADSLT 55 /* Invalid slot */
+#undef TARGET_EDEADLOCK
+#define TARGET_EDEADLOCK 56 /* File locking deadlock error */
+#undef TARGET_EBFONT
+#define TARGET_EBFONT 59 /* Bad font file format */
+#undef TARGET_ENOSTR
+#define TARGET_ENOSTR 60 /* Device not a stream */
+#undef TARGET_ENODATA
+#define TARGET_ENODATA 61 /* No data available */
+#undef TARGET_ETIME
+#define TARGET_ETIME 62 /* Timer expired */
+#undef TARGET_ENOSR
+#define TARGET_ENOSR 63 /* Out of streams resources */
+#undef TARGET_ENONET
+#define TARGET_ENONET 64 /* Machine is not on the network */
+#undef TARGET_ENOPKG
+#define TARGET_ENOPKG 65 /* Package not installed */
+#undef TARGET_EREMOTE
+#define TARGET_EREMOTE 66 /* Object is remote */
+#undef TARGET_ENOLINK
+#define TARGET_ENOLINK 67 /* Link has been severed */
+#undef TARGET_EADV
+#define TARGET_EADV 68 /* Advertise error */
+#undef TARGET_ESRMNT
+#define TARGET_ESRMNT 69 /* Srmount error */
+#undef TARGET_ECOMM
+#define TARGET_ECOMM 70 /* Communication error on send */
+#undef TARGET_EPROTO
+#define TARGET_EPROTO 71 /* Protocol error */
+#undef TARGET_EDOTDOT
+#define TARGET_EDOTDOT 73 /* RFS specific error */
+#undef TARGET_EMULTIHOP
+#define TARGET_EMULTIHOP 74 /* Multihop attempted */
+#undef TARGET_EBADMSG
+#define TARGET_EBADMSG 77 /* Not a data message */
+#undef TARGET_ENAMETOOLONG
+#define TARGET_ENAMETOOLONG 78 /* File name too long */
+#undef TARGET_EOVERFLOW
+#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */
+#undef TARGET_ENOTUNIQ
+#define TARGET_ENOTUNIQ 80 /* Name not unique on network */
+#undef TARGET_EBADFD
+#define TARGET_EBADFD 81 /* File descriptor in bad state */
+#undef TARGET_EREMCHG
+#define TARGET_EREMCHG 82 /* Remote address changed */
+#undef TARGET_ELIBACC
+#define TARGET_ELIBACC 83 /* Can not access a needed shared library */
+#undef TARGET_ELIBBAD
+#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */
+#undef TARGET_ELIBSCN
+#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */
+#undef TARGET_ELIBMAX
+#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */
+#undef TARGET_ELIBEXEC
+#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */
+#undef TARGET_EILSEQ
+#define TARGET_EILSEQ 88 /* Illegal byte sequence */
+#undef TARGET_ENOSYS
+#define TARGET_ENOSYS 89 /* Function not implemented */
+#undef TARGET_ELOOP
+#define TARGET_ELOOP 90 /* Too many symbolic links encountered */
+#undef TARGET_ERESTART
+#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */
+#undef TARGET_ESTRPIPE
+#define TARGET_ESTRPIPE 92 /* Streams pipe error */
+#undef TARGET_ENOTEMPTY
+#define TARGET_ENOTEMPTY 93 /* Directory not empty */
+#undef TARGET_EUSERS
+#define TARGET_EUSERS 94 /* Too many users */
+#undef TARGET_ENOTSOCK
+#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */
+#undef TARGET_EDESTADDRREQ
+#define TARGET_EDESTADDRREQ 96 /* Destination address required */
+#undef TARGET_EMSGSIZE
+#define TARGET_EMSGSIZE 97 /* Message too long */
+#undef TARGET_EPROTOTYPE
+#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */
+#undef TARGET_ENOPROTOOPT
+#define TARGET_ENOPROTOOPT 99 /* Protocol not available */
+#undef TARGET_EPROTONOSUPPORT
+#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */
+#undef TARGET_ESOCKTNOSUPPORT
+#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */
+#undef TARGET_EOPNOTSUPP
+#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */
+#undef TARGET_EPFNOSUPPORT
+#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */
+#undef TARGET_EAFNOSUPPORT
+#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */
+#undef TARGET_EADDRINUSE
+#define TARGET_EADDRINUSE 125 /* Address already in use */
+#undef TARGET_EADDRNOTAVAIL
+#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */
+#undef TARGET_ENETDOWN
+#define TARGET_ENETDOWN 127 /* Network is down */
+#undef TARGET_ENETUNREACH
+#define TARGET_ENETUNREACH 128 /* Network is unreachable */
+#undef TARGET_ENETRESET
+#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */
+#undef TARGET_ECONNABORTED
+#define TARGET_ECONNABORTED 130 /* Software caused connection abort */
+#undef TARGET_ECONNRESET
+#define TARGET_ECONNRESET 131 /* Connection reset by peer */
+#undef TARGET_ENOBUFS
+#define TARGET_ENOBUFS 132 /* No buffer space available */
+#undef TARGET_EISCONN
+#define TARGET_EISCONN 133 /* Transport endpoint is already connected */
+#undef TARGET_ENOTCONN
+#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */
+#undef TARGET_EUCLEAN
+#define TARGET_EUCLEAN 135 /* Structure needs cleaning */
+#undef TARGET_ENOTNAM
+#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */
+#undef TARGET_ENAVAIL
+#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */
+#undef TARGET_EISNAM
+#define TARGET_EISNAM 139 /* Is a named type file */
+#undef TARGET_EREMOTEIO
+#define TARGET_EREMOTEIO 140 /* Remote I/O error */
+#undef TARGET_EINIT
+#define TARGET_EINIT 141 /* Reserved */
+#undef TARGET_EREMDEV
+#define TARGET_EREMDEV 142 /* TARGET_Error 142 */
+#undef TARGET_ESHUTDOWN
+#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */
+#undef TARGET_ETOOMANYREFS
+#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */
+#undef TARGET_ETIMEDOUT
+#define TARGET_ETIMEDOUT 145 /* Connection timed out */
+#undef TARGET_ECONNREFUSED
+#define TARGET_ECONNREFUSED 146 /* Connection refused */
+#undef TARGET_EHOSTDOWN
+#define TARGET_EHOSTDOWN 147 /* Host is down */
+#undef TARGET_EHOSTUNREACH
+#define TARGET_EHOSTUNREACH 148 /* No route to host */
+#undef TARGET_EALREADY
+#define TARGET_EALREADY 149 /* Operation already in progress */
+#undef TARGET_EINPROGRESS
+#define TARGET_EINPROGRESS 150 /* Operation now in progress */
+#undef TARGET_ESTALE
+#define TARGET_ESTALE 151 /* Stale NFS file handle */
+#undef TARGET_ECANCELED
+#define TARGET_ECANCELED 158 /* AIO operation canceled */
+/*
+ * These error are Linux extensions.
+ */
+#undef TARGET_ENOMEDIUM
+#define TARGET_ENOMEDIUM 159 /* No medium found */
+#undef TARGET_EMEDIUMTYPE
+#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */
+#undef TARGET_ENOKEY
+#define TARGET_ENOKEY 161 /* Required key not available */
+#undef TARGET_EKEYEXPIRED
+#define TARGET_EKEYEXPIRED 162 /* Key has expired */
+#undef TARGET_EKEYREVOKED
+#define TARGET_EKEYREVOKED 163 /* Key has been revoked */
+#undef TARGET_EKEYREJECTED
+#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */
+
+/* for robust mutexes */
+#undef TARGET_EOWNERDEAD
+#define TARGET_EOWNERDEAD 165 /* Owner died */
+#undef TARGET_ENOTRECOVERABLE
+#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */
+
+
+
+#define UNAME_MACHINE "mips64"
diff --git a/linux-user/mips64/syscall_nr.h b/linux-user/mips64/syscall_nr.h
new file mode 100644
index 0000000..ee1d134
--- /dev/null
+++ b/linux-user/mips64/syscall_nr.h
@@ -0,0 +1,293 @@
+/*
+ * Linux 64-bit syscalls are in the range from 5000 to 5999.
+ */
+#define TARGET_NR_Linux 5000
+#define TARGET_NR_read (TARGET_NR_Linux + 0)
+#define TARGET_NR_write (TARGET_NR_Linux + 1)
+#define TARGET_NR_open (TARGET_NR_Linux + 2)
+#define TARGET_NR_close (TARGET_NR_Linux + 3)
+#define TARGET_NR_stat (TARGET_NR_Linux + 4)
+#define TARGET_NR_fstat (TARGET_NR_Linux + 5)
+#define TARGET_NR_lstat (TARGET_NR_Linux + 6)
+#define TARGET_NR_poll (TARGET_NR_Linux + 7)
+#define TARGET_NR_lseek (TARGET_NR_Linux + 8)
+#define TARGET_NR_mmap (TARGET_NR_Linux + 9)
+#define TARGET_NR_mprotect (TARGET_NR_Linux + 10)
+#define TARGET_NR_munmap (TARGET_NR_Linux + 11)
+#define TARGET_NR_brk (TARGET_NR_Linux + 12)
+#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13)
+#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14)
+#define TARGET_NR_ioctl (TARGET_NR_Linux + 15)
+#define TARGET_NR_pread64 (TARGET_NR_Linux + 16)
+#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17)
+#define TARGET_NR_readv (TARGET_NR_Linux + 18)
+#define TARGET_NR_writev (TARGET_NR_Linux + 19)
+#define TARGET_NR_access (TARGET_NR_Linux + 20)
+#define TARGET_NR_pipe (TARGET_NR_Linux + 21)
+#define TARGET_NR__newselect (TARGET_NR_Linux + 22)
+#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23)
+#define TARGET_NR_mremap (TARGET_NR_Linux + 24)
+#define TARGET_NR_msync (TARGET_NR_Linux + 25)
+#define TARGET_NR_mincore (TARGET_NR_Linux + 26)
+#define TARGET_NR_madvise (TARGET_NR_Linux + 27)
+#define TARGET_NR_shmget (TARGET_NR_Linux + 28)
+#define TARGET_NR_shmat (TARGET_NR_Linux + 29)
+#define TARGET_NR_shmctl (TARGET_NR_Linux + 30)
+#define TARGET_NR_dup (TARGET_NR_Linux + 31)
+#define TARGET_NR_dup2 (TARGET_NR_Linux + 32)
+#define TARGET_NR_pause (TARGET_NR_Linux + 33)
+#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34)
+#define TARGET_NR_getitimer (TARGET_NR_Linux + 35)
+#define TARGET_NR_setitimer (TARGET_NR_Linux + 36)
+#define TARGET_NR_alarm (TARGET_NR_Linux + 37)
+#define TARGET_NR_getpid (TARGET_NR_Linux + 38)
+#define TARGET_NR_sendfile (TARGET_NR_Linux + 39)
+#define TARGET_NR_socket (TARGET_NR_Linux + 40)
+#define TARGET_NR_connect (TARGET_NR_Linux + 41)
+#define TARGET_NR_accept (TARGET_NR_Linux + 42)
+#define TARGET_NR_sendto (TARGET_NR_Linux + 43)
+#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44)
+#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45)
+#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46)
+#define TARGET_NR_shutdown (TARGET_NR_Linux + 47)
+#define TARGET_NR_bind (TARGET_NR_Linux + 48)
+#define TARGET_NR_listen (TARGET_NR_Linux + 49)
+#define TARGET_NR_getsockname (TARGET_NR_Linux + 50)
+#define TARGET_NR_getpeername (TARGET_NR_Linux + 51)
+#define TARGET_NR_socketpair (TARGET_NR_Linux + 52)
+#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53)
+#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54)
+#define TARGET_NR_clone (TARGET_NR_Linux + 55)
+#define TARGET_NR_fork (TARGET_NR_Linux + 56)
+#define TARGET_NR_execve (TARGET_NR_Linux + 57)
+#define TARGET_NR_exit (TARGET_NR_Linux + 58)
+#define TARGET_NR_wait4 (TARGET_NR_Linux + 59)
+#define TARGET_NR_kill (TARGET_NR_Linux + 60)
+#define TARGET_NR_uname (TARGET_NR_Linux + 61)
+#define TARGET_NR_semget (TARGET_NR_Linux + 62)
+#define TARGET_NR_semop (TARGET_NR_Linux + 63)
+#define TARGET_NR_semctl (TARGET_NR_Linux + 64)
+#define TARGET_NR_shmdt (TARGET_NR_Linux + 65)
+#define TARGET_NR_msgget (TARGET_NR_Linux + 66)
+#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67)
+#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68)
+#define TARGET_NR_msgctl (TARGET_NR_Linux + 69)
+#define TARGET_NR_fcntl (TARGET_NR_Linux + 70)
+#define TARGET_NR_flock (TARGET_NR_Linux + 71)
+#define TARGET_NR_fsync (TARGET_NR_Linux + 72)
+#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73)
+#define TARGET_NR_truncate (TARGET_NR_Linux + 74)
+#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75)
+#define TARGET_NR_getdents (TARGET_NR_Linux + 76)
+#define TARGET_NR_getcwd (TARGET_NR_Linux + 77)
+#define TARGET_NR_chdir (TARGET_NR_Linux + 78)
+#define TARGET_NR_fchdir (TARGET_NR_Linux + 79)
+#define TARGET_NR_rename (TARGET_NR_Linux + 80)
+#define TARGET_NR_mkdir (TARGET_NR_Linux + 81)
+#define TARGET_NR_rmdir (TARGET_NR_Linux + 82)
+#define TARGET_NR_creat (TARGET_NR_Linux + 83)
+#define TARGET_NR_link (TARGET_NR_Linux + 84)
+#define TARGET_NR_unlink (TARGET_NR_Linux + 85)
+#define TARGET_NR_symlink (TARGET_NR_Linux + 86)
+#define TARGET_NR_readlink (TARGET_NR_Linux + 87)
+#define TARGET_NR_chmod (TARGET_NR_Linux + 88)
+#define TARGET_NR_fchmod (TARGET_NR_Linux + 89)
+#define TARGET_NR_chown (TARGET_NR_Linux + 90)
+#define TARGET_NR_fchown (TARGET_NR_Linux + 91)
+#define TARGET_NR_lchown (TARGET_NR_Linux + 92)
+#define TARGET_NR_umask (TARGET_NR_Linux + 93)
+#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94)
+#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95)
+#define TARGET_NR_getrusage (TARGET_NR_Linux + 96)
+#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97)
+#define TARGET_NR_times (TARGET_NR_Linux + 98)
+#define TARGET_NR_ptrace (TARGET_NR_Linux + 99)
+#define TARGET_NR_getuid (TARGET_NR_Linux + 100)
+#define TARGET_NR_syslog (TARGET_NR_Linux + 101)
+#define TARGET_NR_getgid (TARGET_NR_Linux + 102)
+#define TARGET_NR_setuid (TARGET_NR_Linux + 103)
+#define TARGET_NR_setgid (TARGET_NR_Linux + 104)
+#define TARGET_NR_geteuid (TARGET_NR_Linux + 105)
+#define TARGET_NR_getegid (TARGET_NR_Linux + 106)
+#define TARGET_NR_setpgid (TARGET_NR_Linux + 107)
+#define TARGET_NR_getppid (TARGET_NR_Linux + 108)
+#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109)
+#define TARGET_NR_setsid (TARGET_NR_Linux + 110)
+#define TARGET_NR_setreuid (TARGET_NR_Linux + 111)
+#define TARGET_NR_setregid (TARGET_NR_Linux + 112)
+#define TARGET_NR_getgroups (TARGET_NR_Linux + 113)
+#define TARGET_NR_setgroups (TARGET_NR_Linux + 114)
+#define TARGET_NR_setresuid (TARGET_NR_Linux + 115)
+#define TARGET_NR_getresuid (TARGET_NR_Linux + 116)
+#define TARGET_NR_setresgid (TARGET_NR_Linux + 117)
+#define TARGET_NR_getresgid (TARGET_NR_Linux + 118)
+#define TARGET_NR_getpgid (TARGET_NR_Linux + 119)
+#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120)
+#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121)
+#define TARGET_NR_getsid (TARGET_NR_Linux + 122)
+#define TARGET_NR_capget (TARGET_NR_Linux + 123)
+#define TARGET_NR_capset (TARGET_NR_Linux + 124)
+#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125)
+#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126)
+#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127)
+#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128)
+#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129)
+#define TARGET_NR_utime (TARGET_NR_Linux + 130)
+#define TARGET_NR_mknod (TARGET_NR_Linux + 131)
+#define TARGET_NR_personality (TARGET_NR_Linux + 132)
+#define TARGET_NR_ustat (TARGET_NR_Linux + 133)
+#define TARGET_NR_statfs (TARGET_NR_Linux + 134)
+#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135)
+#define TARGET_NR_sysfs (TARGET_NR_Linux + 136)
+#define TARGET_NR_getpriority (TARGET_NR_Linux + 137)
+#define TARGET_NR_setpriority (TARGET_NR_Linux + 138)
+#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139)
+#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140)
+#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141)
+#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142)
+#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143)
+#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144)
+#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145)
+#define TARGET_NR_mlock (TARGET_NR_Linux + 146)
+#define TARGET_NR_munlock (TARGET_NR_Linux + 147)
+#define TARGET_NR_mlockall (TARGET_NR_Linux + 148)
+#define TARGET_NR_munlockall (TARGET_NR_Linux + 149)
+#define TARGET_NR_vhangup (TARGET_NR_Linux + 150)
+#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151)
+#define TARGET_NR__sysctl (TARGET_NR_Linux + 152)
+#define TARGET_NR_prctl (TARGET_NR_Linux + 153)
+#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154)
+#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155)
+#define TARGET_NR_chroot (TARGET_NR_Linux + 156)
+#define TARGET_NR_sync (TARGET_NR_Linux + 157)
+#define TARGET_NR_acct (TARGET_NR_Linux + 158)
+#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159)
+#define TARGET_NR_mount (TARGET_NR_Linux + 160)
+#define TARGET_NR_umount2 (TARGET_NR_Linux + 161)
+#define TARGET_NR_swapon (TARGET_NR_Linux + 162)
+#define TARGET_NR_swapoff (TARGET_NR_Linux + 163)
+#define TARGET_NR_reboot (TARGET_NR_Linux + 164)
+#define TARGET_NR_sethostname (TARGET_NR_Linux + 165)
+#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166)
+#define TARGET_NR_create_module (TARGET_NR_Linux + 167)
+#define TARGET_NR_init_module (TARGET_NR_Linux + 168)
+#define TARGET_NR_delete_module (TARGET_NR_Linux + 169)
+#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170)
+#define TARGET_NR_query_module (TARGET_NR_Linux + 171)
+#define TARGET_NR_quotactl (TARGET_NR_Linux + 172)
+#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173)
+#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174)
+#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175)
+#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176)
+#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177)
+#define TARGET_NR_gettid (TARGET_NR_Linux + 178)
+#define TARGET_NR_readahead (TARGET_NR_Linux + 179)
+#define TARGET_NR_setxattr (TARGET_NR_Linux + 180)
+#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181)
+#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182)
+#define TARGET_NR_getxattr (TARGET_NR_Linux + 183)
+#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184)
+#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185)
+#define TARGET_NR_listxattr (TARGET_NR_Linux + 186)
+#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187)
+#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188)
+#define TARGET_NR_removexattr (TARGET_NR_Linux + 189)
+#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190)
+#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191)
+#define TARGET_NR_tkill (TARGET_NR_Linux + 192)
+#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193)
+#define TARGET_NR_futex (TARGET_NR_Linux + 194)
+#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195)
+#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196)
+#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197)
+#define TARGET_NR_cachectl (TARGET_NR_Linux + 198)
+#define TARGET_NR_sysmips (TARGET_NR_Linux + 199)
+#define TARGET_NR_io_setup (TARGET_NR_Linux + 200)
+#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201)
+#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202)
+#define TARGET_NR_io_submit (TARGET_NR_Linux + 203)
+#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204)
+#define TARGET_NR_exit_group (TARGET_NR_Linux + 205)
+#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206)
+#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207)
+#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208)
+#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209)
+#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210)
+#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211)
+#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 212)
+#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 213)
+#define TARGET_NR_semtimedop (TARGET_NR_Linux + 214)
+#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 215)
+#define TARGET_NR_timer_create (TARGET_NR_Linux + 216)
+#define TARGET_NR_timer_settime (TARGET_NR_Linux + 217)
+#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 218)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 219)
+#define TARGET_NR_timer_delete (TARGET_NR_Linux + 220)
+#define TARGET_NR_clock_settime (TARGET_NR_Linux + 221)
+#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 222)
+#define TARGET_NR_clock_getres (TARGET_NR_Linux + 223)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 224)
+#define TARGET_NR_tgkill (TARGET_NR_Linux + 225)
+#define TARGET_NR_utimes (TARGET_NR_Linux + 226)
+#define TARGET_NR_mbind (TARGET_NR_Linux + 227)
+#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 228)
+#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 229)
+#define TARGET_NR_mq_open (TARGET_NR_Linux + 230)
+#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 231)
+#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 232)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 233)
+#define TARGET_NR_mq_notify (TARGET_NR_Linux + 234)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 235)
+#define TARGET_NR_vserver (TARGET_NR_Linux + 236)
+#define TARGET_NR_waitid (TARGET_NR_Linux + 237)
+/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 238) */
+#define TARGET_NR_add_key (TARGET_NR_Linux + 239)
+#define TARGET_NR_request_key (TARGET_NR_Linux + 240)
+#define TARGET_NR_keyctl (TARGET_NR_Linux + 241)
+#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 242)
+#define TARGET_NR_inotify_init (TARGET_NR_Linux + 243)
+#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 244)
+#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 245)
+#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 246)
+#define TARGET_NR_openat (TARGET_NR_Linux + 247)
+#define TARGET_NR_mkdirat (TARGET_NR_Linux + 248)
+#define TARGET_NR_mknodat (TARGET_NR_Linux + 249)
+#define TARGET_NR_fchownat (TARGET_NR_Linux + 250)
+#define TARGET_NR_futimesat (TARGET_NR_Linux + 251)
+#define TARGET_NR_newfstatat (TARGET_NR_Linux + 252)
+#define TARGET_NR_unlinkat (TARGET_NR_Linux + 253)
+#define TARGET_NR_renameat (TARGET_NR_Linux + 254)
+#define TARGET_NR_linkat (TARGET_NR_Linux + 255)
+#define TARGET_NR_symlinkat (TARGET_NR_Linux + 256)
+#define TARGET_NR_readlinkat (TARGET_NR_Linux + 257)
+#define TARGET_NR_fchmodat (TARGET_NR_Linux + 258)
+#define TARGET_NR_faccessat (TARGET_NR_Linux + 259)
+#define TARGET_NR_pselect6 (TARGET_NR_Linux + 260)
+#define TARGET_NR_ppoll (TARGET_NR_Linux + 261)
+#define TARGET_NR_unshare (TARGET_NR_Linux + 262)
+#define TARGET_NR_splice (TARGET_NR_Linux + 263)
+#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 264)
+#define TARGET_NR_tee (TARGET_NR_Linux + 265)
+#define TARGET_NR_vmsplice (TARGET_NR_Linux + 266)
+#define TARGET_NR_move_pages (TARGET_NR_Linux + 267)
+#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 268)
+#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 269)
+#define TARGET_NR_kexec_load (TARGET_NR_Linux + 270)
+#define TARGET_NR_getcpu (TARGET_NR_Linux + 271)
+#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 272)
+#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 273)
+#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 274)
+#define TARGET_NR_utimensat (TARGET_NR_Linux + 275)
+#define TARGET_NR_signalfd (TARGET_NR_Linux + 276)
+#define TARGET_NR_timerfd (TARGET_NR_Linux + 277)
+#define TARGET_NR_eventfd (TARGET_NR_Linux + 278)
+#define TARGET_NR_fallocate (TARGET_NR_Linux + 279)
+#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 280)
+#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 281)
+#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 282)
+#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 283)
+#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 284)
+#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 285)
+#define TARGET_NR_dup3 (TARGET_NR_Linux + 286)
+#define TARGET_NR_pipe2 (TARGET_NR_Linux + 287)
+#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 288)
diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h
new file mode 100644
index 0000000..6e1dc8b
--- /dev/null
+++ b/linux-user/mips64/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_long ss_sp;
+ abi_ulong ss_size;
+ abi_long ss_flags;
+} 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(CPUMIPSState *state)
+{
+ return state->active_tc.gpr[29];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/mips64/termbits.h b/linux-user/mips64/termbits.h
new file mode 100644
index 0000000..d3a6cf8
--- /dev/null
+++ b/linux-user/mips64/termbits.h
@@ -0,0 +1,245 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 23
+
+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 */
+};
+
+/* 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 (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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_IEXTEN 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_TOSTOP 0100000
+#define TARGET_ITOSTOP TARGET_TOSTOP
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VMIN 4
+#define TARGET_VTIME 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+/* VDSUSP not supported */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOF 16
+#define TARGET_VEOL 17
+
+/* ioctls */
+
+#define TARGET_TCGETA 0x5401
+#define TARGET_TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */
+#define TARGET_TCSETAW 0x5403
+#define TARGET_TCSETAF 0x5404
+
+#define TARGET_TCSBRK 0x5405
+#define TARGET_TCXONC 0x5406
+#define TARGET_TCFLSH 0x5407
+
+#define TARGET_TCGETS 0x540d
+#define TARGET_TCSETS 0x540e
+#define TARGET_TCSETSW 0x540f
+#define TARGET_TCSETSF 0x5410
+
+#define TARGET_TIOCEXCL 0x740d /* set exclusive use of tty */
+#define TARGET_TIOCNXCL 0x740e /* reset exclusive use of tty */
+#define TARGET_TIOCOUTQ 0x7472 /* output queue size */
+#define TARGET_TIOCSTI 0x5472 /* simulate terminal input */
+#define TARGET_TIOCMGET 0x741d /* get all modem bits */
+#define TARGET_TIOCMBIS 0x741b /* bis modem bits */
+#define TARGET_TIOCMBIC 0x741c /* bic modem bits */
+#define TARGET_TIOCMSET 0x741a /* set all modem bits */
+#define TARGET_TIOCPKT 0x5470 /* pty: set/clear packet mode */
+#define TARGET_TIOCPKT_DATA 0x00 /* data packet */
+#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */
+#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */
+#define TARGET_TIOCPKT_STOP 0x04 /* stop output */
+#define TARGET_TIOCPKT_START 0x08 /* start output */
+#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */
+#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */
+/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize) /* set window size */
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize) /* get window size */
+#define TARGET_TIOCNOTTY 0x5471 /* void tty association */
+#define TARGET_TIOCSETD 0x7401
+#define TARGET_TIOCGETD 0x7400
+
+#define TARGET_FIOCLEX 0x6601
+#define TARGET_FIONCLEX 0x6602
+#define TARGET_FIOASYNC 0x667d
+#define TARGET_FIONBIO 0x667e
+#define TARGET_FIOQSIZE 0x667f
+
+#define TARGET_TIOCGLTC 0x7474 /* get special local chars */
+#define TARGET_TIOCSLTC 0x7475 /* set special local chars */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */
+#define TARGET_TIOCCONS TARGET_IOW('t', 120, int) /* become virtual console */
+
+#define TARGET_FIONREAD 0x467f
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+#define TARGET_TIOCGETP 0x7408
+#define TARGET_TIOCSETP 0x7409
+#define TARGET_TIOCSETN 0x740a /* TIOCSETP wo flush */
+
+/* #define TARGET_TIOCSETA TARGET_IOW('t', 20, struct termios) set termios struct */
+/* #define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct termios) drain output, set */
+/* #define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct termios) drn out, fls in, set */
+/* #define TARGET_TIOCGETD TARGET_IOR('t', 26, int) get line discipline */
+/* #define TARGET_TIOCSETD TARGET_IOW('t', 27, int) set line discipline */
+ /* 127-124 compat */
+
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+/* I hope the range from 0x5480 on is free ... */
+#define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */
+#define TARGET_TIOCGSOFTCAR 0x5481
+#define TARGET_TIOCSSOFTCAR 0x5482
+#define TARGET_TIOCLINUX 0x5483
+#define TARGET_TIOCGSERIAL 0x5484
+#define TARGET_TIOCSSERIAL 0x5485
+#define TARGET_TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCSERCONFIG 0x5488
+#define TARGET_TIOCSERGWILD 0x5489
+#define TARGET_TIOCSERSWILD 0x548a
+#define TARGET_TIOCGLCKTRMIOS 0x548b
+#define TARGET_TIOCSLCKTRMIOS 0x548c
+#define TARGET_TIOCSERGSTRUCT 0x548d /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x548e /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x548f /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x5490 /* Set multiport config */
+#define TARGET_TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */
diff --git a/linux-user/mipsn32/syscall.h b/linux-user/mipsn32/syscall.h
new file mode 100644
index 0000000..4ec506c
--- /dev/null
+++ b/linux-user/mipsn32/syscall.h
@@ -0,0 +1,221 @@
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct target_pt_regs {
+ /* Saved main processor registers. */
+ target_ulong regs[32];
+
+ /* Saved special registers. */
+ target_ulong cp0_status;
+ target_ulong lo;
+ target_ulong hi;
+ target_ulong cp0_badvaddr;
+ target_ulong cp0_cause;
+ target_ulong cp0_epc;
+};
+
+/* Target errno definitions taken from asm-mips/errno.h */
+#undef TARGET_ENOMSG
+#define TARGET_ENOMSG 35 /* Identifier removed */
+#undef TARGET_EIDRM
+#define TARGET_EIDRM 36 /* Identifier removed */
+#undef TARGET_ECHRNG
+#define TARGET_ECHRNG 37 /* Channel number out of range */
+#undef TARGET_EL2NSYNC
+#define TARGET_EL2NSYNC 38 /* Level 2 not synchronized */
+#undef TARGET_EL3HLT
+#define TARGET_EL3HLT 39 /* Level 3 halted */
+#undef TARGET_EL3RST
+#define TARGET_EL3RST 40 /* Level 3 reset */
+#undef TARGET_ELNRNG
+#define TARGET_ELNRNG 41 /* Link number out of range */
+#undef TARGET_EUNATCH
+#define TARGET_EUNATCH 42 /* Protocol driver not attached */
+#undef TARGET_ENOCSI
+#define TARGET_ENOCSI 43 /* No CSI structure available */
+#undef TARGET_EL2HLT
+#define TARGET_EL2HLT 44 /* Level 2 halted */
+#undef TARGET_EDEADLK
+#define TARGET_EDEADLK 45 /* Resource deadlock would occur */
+#undef TARGET_ENOLCK
+#define TARGET_ENOLCK 46 /* No record locks available */
+#undef TARGET_EBADE
+#define TARGET_EBADE 50 /* Invalid exchange */
+#undef TARGET_EBADR
+#define TARGET_EBADR 51 /* Invalid request descriptor */
+#undef TARGET_EXFULL
+#define TARGET_EXFULL 52 /* TARGET_Exchange full */
+#undef TARGET_ENOANO
+#define TARGET_ENOANO 53 /* No anode */
+#undef TARGET_EBADRQC
+#define TARGET_EBADRQC 54 /* Invalid request code */
+#undef TARGET_EBADSLT
+#define TARGET_EBADSLT 55 /* Invalid slot */
+#undef TARGET_EDEADLOCK
+#define TARGET_EDEADLOCK 56 /* File locking deadlock error */
+#undef TARGET_EBFONT
+#define TARGET_EBFONT 59 /* Bad font file format */
+#undef TARGET_ENOSTR
+#define TARGET_ENOSTR 60 /* Device not a stream */
+#undef TARGET_ENODATA
+#define TARGET_ENODATA 61 /* No data available */
+#undef TARGET_ETIME
+#define TARGET_ETIME 62 /* Timer expired */
+#undef TARGET_ENOSR
+#define TARGET_ENOSR 63 /* Out of streams resources */
+#undef TARGET_ENONET
+#define TARGET_ENONET 64 /* Machine is not on the network */
+#undef TARGET_ENOPKG
+#define TARGET_ENOPKG 65 /* Package not installed */
+#undef TARGET_EREMOTE
+#define TARGET_EREMOTE 66 /* Object is remote */
+#undef TARGET_ENOLINK
+#define TARGET_ENOLINK 67 /* Link has been severed */
+#undef TARGET_EADV
+#define TARGET_EADV 68 /* Advertise error */
+#undef TARGET_ESRMNT
+#define TARGET_ESRMNT 69 /* Srmount error */
+#undef TARGET_ECOMM
+#define TARGET_ECOMM 70 /* Communication error on send */
+#undef TARGET_EPROTO
+#define TARGET_EPROTO 71 /* Protocol error */
+#undef TARGET_EDOTDOT
+#define TARGET_EDOTDOT 73 /* RFS specific error */
+#undef TARGET_EMULTIHOP
+#define TARGET_EMULTIHOP 74 /* Multihop attempted */
+#undef TARGET_EBADMSG
+#define TARGET_EBADMSG 77 /* Not a data message */
+#undef TARGET_ENAMETOOLONG
+#define TARGET_ENAMETOOLONG 78 /* File name too long */
+#undef TARGET_EOVERFLOW
+#define TARGET_EOVERFLOW 79 /* Value too large for defined data type */
+#undef TARGET_ENOTUNIQ
+#define TARGET_ENOTUNIQ 80 /* Name not unique on network */
+#undef TARGET_EBADFD
+#define TARGET_EBADFD 81 /* File descriptor in bad state */
+#undef TARGET_EREMCHG
+#define TARGET_EREMCHG 82 /* Remote address changed */
+#undef TARGET_ELIBACC
+#define TARGET_ELIBACC 83 /* Can not access a needed shared library */
+#undef TARGET_ELIBBAD
+#define TARGET_ELIBBAD 84 /* Accessing a corrupted shared library */
+#undef TARGET_ELIBSCN
+#define TARGET_ELIBSCN 85 /* .lib section in a.out corrupted */
+#undef TARGET_ELIBMAX
+#define TARGET_ELIBMAX 86 /* Attempting to link in too many shared libraries */
+#undef TARGET_ELIBEXEC
+#define TARGET_ELIBEXEC 87 /* Cannot exec a shared library directly */
+#undef TARGET_EILSEQ
+#define TARGET_EILSEQ 88 /* Illegal byte sequence */
+#undef TARGET_ENOSYS
+#define TARGET_ENOSYS 89 /* Function not implemented */
+#undef TARGET_ELOOP
+#define TARGET_ELOOP 90 /* Too many symbolic links encountered */
+#undef TARGET_ERESTART
+#define TARGET_ERESTART 91 /* Interrupted system call should be restarted */
+#undef TARGET_ESTRPIPE
+#define TARGET_ESTRPIPE 92 /* Streams pipe error */
+#undef TARGET_ENOTEMPTY
+#define TARGET_ENOTEMPTY 93 /* Directory not empty */
+#undef TARGET_EUSERS
+#define TARGET_EUSERS 94 /* Too many users */
+#undef TARGET_ENOTSOCK
+#define TARGET_ENOTSOCK 95 /* Socket operation on non-socket */
+#undef TARGET_EDESTADDRREQ
+#define TARGET_EDESTADDRREQ 96 /* Destination address required */
+#undef TARGET_EMSGSIZE
+#define TARGET_EMSGSIZE 97 /* Message too long */
+#undef TARGET_EPROTOTYPE
+#define TARGET_EPROTOTYPE 98 /* Protocol wrong type for socket */
+#undef TARGET_ENOPROTOOPT
+#define TARGET_ENOPROTOOPT 99 /* Protocol not available */
+#undef TARGET_EPROTONOSUPPORT
+#define TARGET_EPROTONOSUPPORT 120 /* Protocol not supported */
+#undef TARGET_ESOCKTNOSUPPORT
+#define TARGET_ESOCKTNOSUPPORT 121 /* Socket type not supported */
+#undef TARGET_EOPNOTSUPP
+#define TARGET_EOPNOTSUPP 122 /* Operation not supported on transport endpoint */
+#undef TARGET_EPFNOSUPPORT
+#define TARGET_EPFNOSUPPORT 123 /* Protocol family not supported */
+#undef TARGET_EAFNOSUPPORT
+#define TARGET_EAFNOSUPPORT 124 /* Address family not supported by protocol */
+#undef TARGET_EADDRINUSE
+#define TARGET_EADDRINUSE 125 /* Address already in use */
+#undef TARGET_EADDRNOTAVAIL
+#define TARGET_EADDRNOTAVAIL 126 /* Cannot assign requested address */
+#undef TARGET_ENETDOWN
+#define TARGET_ENETDOWN 127 /* Network is down */
+#undef TARGET_ENETUNREACH
+#define TARGET_ENETUNREACH 128 /* Network is unreachable */
+#undef TARGET_ENETRESET
+#define TARGET_ENETRESET 129 /* Network dropped connection because of reset */
+#undef TARGET_ECONNABORTED
+#define TARGET_ECONNABORTED 130 /* Software caused connection abort */
+#undef TARGET_ECONNRESET
+#define TARGET_ECONNRESET 131 /* Connection reset by peer */
+#undef TARGET_ENOBUFS
+#define TARGET_ENOBUFS 132 /* No buffer space available */
+#undef TARGET_EISCONN
+#define TARGET_EISCONN 133 /* Transport endpoint is already connected */
+#undef TARGET_ENOTCONN
+#define TARGET_ENOTCONN 134 /* Transport endpoint is not connected */
+#undef TARGET_EUCLEAN
+#define TARGET_EUCLEAN 135 /* Structure needs cleaning */
+#undef TARGET_ENOTNAM
+#define TARGET_ENOTNAM 137 /* Not a XENIX named type file */
+#undef TARGET_ENAVAIL
+#define TARGET_ENAVAIL 138 /* No XENIX semaphores available */
+#undef TARGET_EISNAM
+#define TARGET_EISNAM 139 /* Is a named type file */
+#undef TARGET_EREMOTEIO
+#define TARGET_EREMOTEIO 140 /* Remote I/O error */
+#undef TARGET_EINIT
+#define TARGET_EINIT 141 /* Reserved */
+#undef TARGET_EREMDEV
+#define TARGET_EREMDEV 142 /* TARGET_Error 142 */
+#undef TARGET_ESHUTDOWN
+#define TARGET_ESHUTDOWN 143 /* Cannot send after transport endpoint shutdown */
+#undef TARGET_ETOOMANYREFS
+#define TARGET_ETOOMANYREFS 144 /* Too many references: cannot splice */
+#undef TARGET_ETIMEDOUT
+#define TARGET_ETIMEDOUT 145 /* Connection timed out */
+#undef TARGET_ECONNREFUSED
+#define TARGET_ECONNREFUSED 146 /* Connection refused */
+#undef TARGET_EHOSTDOWN
+#define TARGET_EHOSTDOWN 147 /* Host is down */
+#undef TARGET_EHOSTUNREACH
+#define TARGET_EHOSTUNREACH 148 /* No route to host */
+#undef TARGET_EALREADY
+#define TARGET_EALREADY 149 /* Operation already in progress */
+#undef TARGET_EINPROGRESS
+#define TARGET_EINPROGRESS 150 /* Operation now in progress */
+#undef TARGET_ESTALE
+#define TARGET_ESTALE 151 /* Stale NFS file handle */
+#undef TARGET_ECANCELED
+#define TARGET_ECANCELED 158 /* AIO operation canceled */
+/*
+ * These error are Linux extensions.
+ */
+#undef TARGET_ENOMEDIUM
+#define TARGET_ENOMEDIUM 159 /* No medium found */
+#undef TARGET_EMEDIUMTYPE
+#define TARGET_EMEDIUMTYPE 160 /* Wrong medium type */
+#undef TARGET_ENOKEY
+#define TARGET_ENOKEY 161 /* Required key not available */
+#undef TARGET_EKEYEXPIRED
+#define TARGET_EKEYEXPIRED 162 /* Key has expired */
+#undef TARGET_EKEYREVOKED
+#define TARGET_EKEYREVOKED 163 /* Key has been revoked */
+#undef TARGET_EKEYREJECTED
+#define TARGET_EKEYREJECTED 164 /* Key was rejected by service */
+
+/* for robust mutexes */
+#undef TARGET_EOWNERDEAD
+#define TARGET_EOWNERDEAD 165 /* Owner died */
+#undef TARGET_ENOTRECOVERABLE
+#define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */
+
+
+
+#define UNAME_MACHINE "mips64"
diff --git a/linux-user/mipsn32/syscall_nr.h b/linux-user/mipsn32/syscall_nr.h
new file mode 100644
index 0000000..60a99dd
--- /dev/null
+++ b/linux-user/mipsn32/syscall_nr.h
@@ -0,0 +1,297 @@
+/*
+ * Linux N32 syscalls are in the range from 6000 to 6999.
+ */
+#define TARGET_NR_Linux 6000
+#define TARGET_NR_read (TARGET_NR_Linux + 0)
+#define TARGET_NR_write (TARGET_NR_Linux + 1)
+#define TARGET_NR_open (TARGET_NR_Linux + 2)
+#define TARGET_NR_close (TARGET_NR_Linux + 3)
+#define TARGET_NR_stat (TARGET_NR_Linux + 4)
+#define TARGET_NR_fstat (TARGET_NR_Linux + 5)
+#define TARGET_NR_lstat (TARGET_NR_Linux + 6)
+#define TARGET_NR_poll (TARGET_NR_Linux + 7)
+#define TARGET_NR_lseek (TARGET_NR_Linux + 8)
+#define TARGET_NR_mmap (TARGET_NR_Linux + 9)
+#define TARGET_NR_mprotect (TARGET_NR_Linux + 10)
+#define TARGET_NR_munmap (TARGET_NR_Linux + 11)
+#define TARGET_NR_brk (TARGET_NR_Linux + 12)
+#define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 13)
+#define TARGET_NR_rt_sigprocmask (TARGET_NR_Linux + 14)
+#define TARGET_NR_ioctl (TARGET_NR_Linux + 15)
+#define TARGET_NR_pread64 (TARGET_NR_Linux + 16)
+#define TARGET_NR_pwrite64 (TARGET_NR_Linux + 17)
+#define TARGET_NR_readv (TARGET_NR_Linux + 18)
+#define TARGET_NR_writev (TARGET_NR_Linux + 19)
+#define TARGET_NR_access (TARGET_NR_Linux + 20)
+#define TARGET_NR_pipe (TARGET_NR_Linux + 21)
+#define TARGET_NR__newselect (TARGET_NR_Linux + 22)
+#define TARGET_NR_sched_yield (TARGET_NR_Linux + 23)
+#define TARGET_NR_mremap (TARGET_NR_Linux + 24)
+#define TARGET_NR_msync (TARGET_NR_Linux + 25)
+#define TARGET_NR_mincore (TARGET_NR_Linux + 26)
+#define TARGET_NR_madvise (TARGET_NR_Linux + 27)
+#define TARGET_NR_shmget (TARGET_NR_Linux + 28)
+#define TARGET_NR_shmat (TARGET_NR_Linux + 29)
+#define TARGET_NR_shmctl (TARGET_NR_Linux + 30)
+#define TARGET_NR_dup (TARGET_NR_Linux + 31)
+#define TARGET_NR_dup2 (TARGET_NR_Linux + 32)
+#define TARGET_NR_pause (TARGET_NR_Linux + 33)
+#define TARGET_NR_nanosleep (TARGET_NR_Linux + 34)
+#define TARGET_NR_getitimer (TARGET_NR_Linux + 35)
+#define TARGET_NR_setitimer (TARGET_NR_Linux + 36)
+#define TARGET_NR_alarm (TARGET_NR_Linux + 37)
+#define TARGET_NR_getpid (TARGET_NR_Linux + 38)
+#define TARGET_NR_sendfile (TARGET_NR_Linux + 39)
+#define TARGET_NR_socket (TARGET_NR_Linux + 40)
+#define TARGET_NR_connect (TARGET_NR_Linux + 41)
+#define TARGET_NR_accept (TARGET_NR_Linux + 42)
+#define TARGET_NR_sendto (TARGET_NR_Linux + 43)
+#define TARGET_NR_recvfrom (TARGET_NR_Linux + 44)
+#define TARGET_NR_sendmsg (TARGET_NR_Linux + 45)
+#define TARGET_NR_recvmsg (TARGET_NR_Linux + 46)
+#define TARGET_NR_shutdown (TARGET_NR_Linux + 47)
+#define TARGET_NR_bind (TARGET_NR_Linux + 48)
+#define TARGET_NR_listen (TARGET_NR_Linux + 49)
+#define TARGET_NR_getsockname (TARGET_NR_Linux + 50)
+#define TARGET_NR_getpeername (TARGET_NR_Linux + 51)
+#define TARGET_NR_socketpair (TARGET_NR_Linux + 52)
+#define TARGET_NR_setsockopt (TARGET_NR_Linux + 53)
+#define TARGET_NR_getsockopt (TARGET_NR_Linux + 54)
+#define TARGET_NR_clone (TARGET_NR_Linux + 55)
+#define TARGET_NR_fork (TARGET_NR_Linux + 56)
+#define TARGET_NR_execve (TARGET_NR_Linux + 57)
+#define TARGET_NR_exit (TARGET_NR_Linux + 58)
+#define TARGET_NR_wait4 (TARGET_NR_Linux + 59)
+#define TARGET_NR_kill (TARGET_NR_Linux + 60)
+#define TARGET_NR_uname (TARGET_NR_Linux + 61)
+#define TARGET_NR_semget (TARGET_NR_Linux + 62)
+#define TARGET_NR_semop (TARGET_NR_Linux + 63)
+#define TARGET_NR_semctl (TARGET_NR_Linux + 64)
+#define TARGET_NR_shmdt (TARGET_NR_Linux + 65)
+#define TARGET_NR_msgget (TARGET_NR_Linux + 66)
+#define TARGET_NR_msgsnd (TARGET_NR_Linux + 67)
+#define TARGET_NR_msgrcv (TARGET_NR_Linux + 68)
+#define TARGET_NR_msgctl (TARGET_NR_Linux + 69)
+#define TARGET_NR_fcntl (TARGET_NR_Linux + 70)
+#define TARGET_NR_flock (TARGET_NR_Linux + 71)
+#define TARGET_NR_fsync (TARGET_NR_Linux + 72)
+#define TARGET_NR_fdatasync (TARGET_NR_Linux + 73)
+#define TARGET_NR_truncate (TARGET_NR_Linux + 74)
+#define TARGET_NR_ftruncate (TARGET_NR_Linux + 75)
+#define TARGET_NR_getdents (TARGET_NR_Linux + 76)
+#define TARGET_NR_getcwd (TARGET_NR_Linux + 77)
+#define TARGET_NR_chdir (TARGET_NR_Linux + 78)
+#define TARGET_NR_fchdir (TARGET_NR_Linux + 79)
+#define TARGET_NR_rename (TARGET_NR_Linux + 80)
+#define TARGET_NR_mkdir (TARGET_NR_Linux + 81)
+#define TARGET_NR_rmdir (TARGET_NR_Linux + 82)
+#define TARGET_NR_creat (TARGET_NR_Linux + 83)
+#define TARGET_NR_link (TARGET_NR_Linux + 84)
+#define TARGET_NR_unlink (TARGET_NR_Linux + 85)
+#define TARGET_NR_symlink (TARGET_NR_Linux + 86)
+#define TARGET_NR_readlink (TARGET_NR_Linux + 87)
+#define TARGET_NR_chmod (TARGET_NR_Linux + 88)
+#define TARGET_NR_fchmod (TARGET_NR_Linux + 89)
+#define TARGET_NR_chown (TARGET_NR_Linux + 90)
+#define TARGET_NR_fchown (TARGET_NR_Linux + 91)
+#define TARGET_NR_lchown (TARGET_NR_Linux + 92)
+#define TARGET_NR_umask (TARGET_NR_Linux + 93)
+#define TARGET_NR_gettimeofday (TARGET_NR_Linux + 94)
+#define TARGET_NR_getrlimit (TARGET_NR_Linux + 95)
+#define TARGET_NR_getrusage (TARGET_NR_Linux + 96)
+#define TARGET_NR_sysinfo (TARGET_NR_Linux + 97)
+#define TARGET_NR_times (TARGET_NR_Linux + 98)
+#define TARGET_NR_ptrace (TARGET_NR_Linux + 99)
+#define TARGET_NR_getuid (TARGET_NR_Linux + 100)
+#define TARGET_NR_syslog (TARGET_NR_Linux + 101)
+#define TARGET_NR_getgid (TARGET_NR_Linux + 102)
+#define TARGET_NR_setuid (TARGET_NR_Linux + 103)
+#define TARGET_NR_setgid (TARGET_NR_Linux + 104)
+#define TARGET_NR_geteuid (TARGET_NR_Linux + 105)
+#define TARGET_NR_getegid (TARGET_NR_Linux + 106)
+#define TARGET_NR_setpgid (TARGET_NR_Linux + 107)
+#define TARGET_NR_getppid (TARGET_NR_Linux + 108)
+#define TARGET_NR_getpgrp (TARGET_NR_Linux + 109)
+#define TARGET_NR_setsid (TARGET_NR_Linux + 110)
+#define TARGET_NR_setreuid (TARGET_NR_Linux + 111)
+#define TARGET_NR_setregid (TARGET_NR_Linux + 112)
+#define TARGET_NR_getgroups (TARGET_NR_Linux + 113)
+#define TARGET_NR_setgroups (TARGET_NR_Linux + 114)
+#define TARGET_NR_setresuid (TARGET_NR_Linux + 115)
+#define TARGET_NR_getresuid (TARGET_NR_Linux + 116)
+#define TARGET_NR_setresgid (TARGET_NR_Linux + 117)
+#define TARGET_NR_getresgid (TARGET_NR_Linux + 118)
+#define TARGET_NR_getpgid (TARGET_NR_Linux + 119)
+#define TARGET_NR_setfsuid (TARGET_NR_Linux + 120)
+#define TARGET_NR_setfsgid (TARGET_NR_Linux + 121)
+#define TARGET_NR_getsid (TARGET_NR_Linux + 122)
+#define TARGET_NR_capget (TARGET_NR_Linux + 123)
+#define TARGET_NR_capset (TARGET_NR_Linux + 124)
+#define TARGET_NR_rt_sigpending (TARGET_NR_Linux + 125)
+#define TARGET_NR_rt_sigtimedwait (TARGET_NR_Linux + 126)
+#define TARGET_NR_rt_sigqueueinfo (TARGET_NR_Linux + 127)
+#define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 128)
+#define TARGET_NR_sigaltstack (TARGET_NR_Linux + 129)
+#define TARGET_NR_utime (TARGET_NR_Linux + 130)
+#define TARGET_NR_mknod (TARGET_NR_Linux + 131)
+#define TARGET_NR_personality (TARGET_NR_Linux + 132)
+#define TARGET_NR_ustat (TARGET_NR_Linux + 133)
+#define TARGET_NR_statfs (TARGET_NR_Linux + 134)
+#define TARGET_NR_fstatfs (TARGET_NR_Linux + 135)
+#define TARGET_NR_sysfs (TARGET_NR_Linux + 136)
+#define TARGET_NR_getpriority (TARGET_NR_Linux + 137)
+#define TARGET_NR_setpriority (TARGET_NR_Linux + 138)
+#define TARGET_NR_sched_setparam (TARGET_NR_Linux + 139)
+#define TARGET_NR_sched_getparam (TARGET_NR_Linux + 140)
+#define TARGET_NR_sched_setscheduler (TARGET_NR_Linux + 141)
+#define TARGET_NR_sched_getscheduler (TARGET_NR_Linux + 142)
+#define TARGET_NR_sched_get_priority_max (TARGET_NR_Linux + 143)
+#define TARGET_NR_sched_get_priority_min (TARGET_NR_Linux + 144)
+#define TARGET_NR_sched_rr_get_interval (TARGET_NR_Linux + 145)
+#define TARGET_NR_mlock (TARGET_NR_Linux + 146)
+#define TARGET_NR_munlock (TARGET_NR_Linux + 147)
+#define TARGET_NR_mlockall (TARGET_NR_Linux + 148)
+#define TARGET_NR_munlockall (TARGET_NR_Linux + 149)
+#define TARGET_NR_vhangup (TARGET_NR_Linux + 150)
+#define TARGET_NR_pivot_root (TARGET_NR_Linux + 151)
+#define TARGET_NR__sysctl (TARGET_NR_Linux + 152)
+#define TARGET_NR_prctl (TARGET_NR_Linux + 153)
+#define TARGET_NR_adjtimex (TARGET_NR_Linux + 154)
+#define TARGET_NR_setrlimit (TARGET_NR_Linux + 155)
+#define TARGET_NR_chroot (TARGET_NR_Linux + 156)
+#define TARGET_NR_sync (TARGET_NR_Linux + 157)
+#define TARGET_NR_acct (TARGET_NR_Linux + 158)
+#define TARGET_NR_settimeofday (TARGET_NR_Linux + 159)
+#define TARGET_NR_mount (TARGET_NR_Linux + 160)
+#define TARGET_NR_umount2 (TARGET_NR_Linux + 161)
+#define TARGET_NR_swapon (TARGET_NR_Linux + 162)
+#define TARGET_NR_swapoff (TARGET_NR_Linux + 163)
+#define TARGET_NR_reboot (TARGET_NR_Linux + 164)
+#define TARGET_NR_sethostname (TARGET_NR_Linux + 165)
+#define TARGET_NR_setdomainname (TARGET_NR_Linux + 166)
+#define TARGET_NR_create_module (TARGET_NR_Linux + 167)
+#define TARGET_NR_init_module (TARGET_NR_Linux + 168)
+#define TARGET_NR_delete_module (TARGET_NR_Linux + 169)
+#define TARGET_NR_get_kernel_syms (TARGET_NR_Linux + 170)
+#define TARGET_NR_query_module (TARGET_NR_Linux + 171)
+#define TARGET_NR_quotactl (TARGET_NR_Linux + 172)
+#define TARGET_NR_nfsservctl (TARGET_NR_Linux + 173)
+#define TARGET_NR_getpmsg (TARGET_NR_Linux + 174)
+#define TARGET_NR_putpmsg (TARGET_NR_Linux + 175)
+#define TARGET_NR_afs_syscall (TARGET_NR_Linux + 176)
+#define TARGET_NR_reserved177 (TARGET_NR_Linux + 177)
+#define TARGET_NR_gettid (TARGET_NR_Linux + 178)
+#define TARGET_NR_readahead (TARGET_NR_Linux + 179)
+#define TARGET_NR_setxattr (TARGET_NR_Linux + 180)
+#define TARGET_NR_lsetxattr (TARGET_NR_Linux + 181)
+#define TARGET_NR_fsetxattr (TARGET_NR_Linux + 182)
+#define TARGET_NR_getxattr (TARGET_NR_Linux + 183)
+#define TARGET_NR_lgetxattr (TARGET_NR_Linux + 184)
+#define TARGET_NR_fgetxattr (TARGET_NR_Linux + 185)
+#define TARGET_NR_listxattr (TARGET_NR_Linux + 186)
+#define TARGET_NR_llistxattr (TARGET_NR_Linux + 187)
+#define TARGET_NR_flistxattr (TARGET_NR_Linux + 188)
+#define TARGET_NR_removexattr (TARGET_NR_Linux + 189)
+#define TARGET_NR_lremovexattr (TARGET_NR_Linux + 190)
+#define TARGET_NR_fremovexattr (TARGET_NR_Linux + 191)
+#define TARGET_NR_tkill (TARGET_NR_Linux + 192)
+#define TARGET_NR_reserved193 (TARGET_NR_Linux + 193)
+#define TARGET_NR_futex (TARGET_NR_Linux + 194)
+#define TARGET_NR_sched_setaffinity (TARGET_NR_Linux + 195)
+#define TARGET_NR_sched_getaffinity (TARGET_NR_Linux + 196)
+#define TARGET_NR_cacheflush (TARGET_NR_Linux + 197)
+#define TARGET_NR_cachectl (TARGET_NR_Linux + 198)
+#define TARGET_NR_sysmips (TARGET_NR_Linux + 199)
+#define TARGET_NR_io_setup (TARGET_NR_Linux + 200)
+#define TARGET_NR_io_destroy (TARGET_NR_Linux + 201)
+#define TARGET_NR_io_getevents (TARGET_NR_Linux + 202)
+#define TARGET_NR_io_submit (TARGET_NR_Linux + 203)
+#define TARGET_NR_io_cancel (TARGET_NR_Linux + 204)
+#define TARGET_NR_exit_group (TARGET_NR_Linux + 205)
+#define TARGET_NR_lookup_dcookie (TARGET_NR_Linux + 206)
+#define TARGET_NR_epoll_create (TARGET_NR_Linux + 207)
+#define TARGET_NR_epoll_ctl (TARGET_NR_Linux + 208)
+#define TARGET_NR_epoll_wait (TARGET_NR_Linux + 209)
+#define TARGET_NR_remap_file_pages (TARGET_NR_Linux + 210)
+#define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 211)
+#define TARGET_NR_fcntl64 (TARGET_NR_Linux + 212)
+#define TARGET_NR_set_tid_address (TARGET_NR_Linux + 213)
+#define TARGET_NR_restart_syscall (TARGET_NR_Linux + 214)
+#define TARGET_NR_semtimedop (TARGET_NR_Linux + 215)
+#define TARGET_NR_fadvise64 (TARGET_NR_Linux + 216)
+#define TARGET_NR_statfs64 (TARGET_NR_Linux + 217)
+#define TARGET_NR_fstatfs64 (TARGET_NR_Linux + 218)
+#define TARGET_NR_sendfile64 (TARGET_NR_Linux + 219)
+#define TARGET_NR_timer_create (TARGET_NR_Linux + 220)
+#define TARGET_NR_timer_settime (TARGET_NR_Linux + 221)
+#define TARGET_NR_timer_gettime (TARGET_NR_Linux + 222)
+#define TARGET_NR_timer_getoverrun (TARGET_NR_Linux + 223)
+#define TARGET_NR_timer_delete (TARGET_NR_Linux + 224)
+#define TARGET_NR_clock_settime (TARGET_NR_Linux + 225)
+#define TARGET_NR_clock_gettime (TARGET_NR_Linux + 226)
+#define TARGET_NR_clock_getres (TARGET_NR_Linux + 227)
+#define TARGET_NR_clock_nanosleep (TARGET_NR_Linux + 228)
+#define TARGET_NR_tgkill (TARGET_NR_Linux + 229)
+#define TARGET_NR_utimes (TARGET_NR_Linux + 230)
+#define TARGET_NR_mbind (TARGET_NR_Linux + 231)
+#define TARGET_NR_get_mempolicy (TARGET_NR_Linux + 232)
+#define TARGET_NR_set_mempolicy (TARGET_NR_Linux + 233)
+#define TARGET_NR_mq_open (TARGET_NR_Linux + 234)
+#define TARGET_NR_mq_unlink (TARGET_NR_Linux + 235)
+#define TARGET_NR_mq_timedsend (TARGET_NR_Linux + 236)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_Linux + 237)
+#define TARGET_NR_mq_notify (TARGET_NR_Linux + 238)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_Linux + 239)
+#define TARGET_NR_vserver (TARGET_NR_Linux + 240)
+#define TARGET_NR_waitid (TARGET_NR_Linux + 241)
+/* #define TARGET_NR_sys_setaltroot (TARGET_NR_Linux + 242) */
+#define TARGET_NR_add_key (TARGET_NR_Linux + 243)
+#define TARGET_NR_request_key (TARGET_NR_Linux + 244)
+#define TARGET_NR_keyctl (TARGET_NR_Linux + 245)
+#define TARGET_NR_set_thread_area (TARGET_NR_Linux + 246)
+#define TARGET_NR_inotify_init (TARGET_NR_Linux + 247)
+#define TARGET_NR_inotify_add_watch (TARGET_NR_Linux + 248)
+#define TARGET_NR_inotify_rm_watch (TARGET_NR_Linux + 249)
+#define TARGET_NR_migrate_pages (TARGET_NR_Linux + 250)
+#define TARGET_NR_openat (TARGET_NR_Linux + 251)
+#define TARGET_NR_mkdirat (TARGET_NR_Linux + 252)
+#define TARGET_NR_mknodat (TARGET_NR_Linux + 253)
+#define TARGET_NR_fchownat (TARGET_NR_Linux + 254)
+#define TARGET_NR_futimesat (TARGET_NR_Linux + 255)
+#define TARGET_NR_newfstatat (TARGET_NR_Linux + 256)
+#define TARGET_NR_unlinkat (TARGET_NR_Linux + 257)
+#define TARGET_NR_renameat (TARGET_NR_Linux + 258)
+#define TARGET_NR_linkat (TARGET_NR_Linux + 259)
+#define TARGET_NR_symlinkat (TARGET_NR_Linux + 260)
+#define TARGET_NR_readlinkat (TARGET_NR_Linux + 261)
+#define TARGET_NR_fchmodat (TARGET_NR_Linux + 262)
+#define TARGET_NR_faccessat (TARGET_NR_Linux + 263)
+#define TARGET_NR_pselect6 (TARGET_NR_Linux + 264)
+#define TARGET_NR_ppoll (TARGET_NR_Linux + 265)
+#define TARGET_NR_unshare (TARGET_NR_Linux + 266)
+#define TARGET_NR_splice (TARGET_NR_Linux + 267)
+#define TARGET_NR_sync_file_range (TARGET_NR_Linux + 268)
+#define TARGET_NR_tee (TARGET_NR_Linux + 269)
+#define TARGET_NR_vmsplice (TARGET_NR_Linux + 270)
+#define TARGET_NR_move_pages (TARGET_NR_Linux + 271)
+#define TARGET_NR_set_robust_list (TARGET_NR_Linux + 272)
+#define TARGET_NR_get_robust_list (TARGET_NR_Linux + 273)
+#define TARGET_NR_kexec_load (TARGET_NR_Linux + 274)
+#define TARGET_NR_getcpu (TARGET_NR_Linux + 275)
+#define TARGET_NR_epoll_pwait (TARGET_NR_Linux + 276)
+#define TARGET_NR_ioprio_set (TARGET_NR_Linux + 277)
+#define TARGET_NR_ioprio_get (TARGET_NR_Linux + 278)
+#define TARGET_NR_utimensat (TARGET_NR_Linux + 279)
+#define TARGET_NR_signalfd (TARGET_NR_Linux + 280)
+#define TARGET_NR_timerfd (TARGET_NR_Linux + 281)
+#define TARGET_NR_eventfd (TARGET_NR_Linux + 282)
+#define TARGET_NR_fallocate (TARGET_NR_Linux + 283)
+#define TARGET_NR_timerfd_create (TARGET_NR_Linux + 284)
+#define TARGET_NR_timerfd_gettime (TARGET_NR_Linux + 285)
+#define TARGET_NR_timerfd_settime (TARGET_NR_Linux + 286)
+#define TARGET_NR_signalfd4 (TARGET_NR_Linux + 287)
+#define TARGET_NR_eventfd2 (TARGET_NR_Linux + 288)
+#define TARGET_NR_epoll_create1 (TARGET_NR_Linux + 289)
+#define TARGET_NR_dup3 (TARGET_NR_Linux + 290)
+#define TARGET_NR_pipe2 (TARGET_NR_Linux + 291)
+#define TARGET_NR_inotify_init1 (TARGET_NR_Linux + 292)
diff --git a/linux-user/mipsn32/target_signal.h b/linux-user/mipsn32/target_signal.h
new file mode 100644
index 0000000..ff20d9e
--- /dev/null
+++ b/linux-user/mipsn32/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ int32_t ss_sp;
+ uint32_t ss_size;
+ int32_t ss_flags;
+} 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 target_ulong get_sp_from_cpustate(CPUMIPSState *state)
+{
+ return state->active_tc.gpr[29];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/mipsn32/termbits.h b/linux-user/mipsn32/termbits.h
new file mode 100644
index 0000000..d3a6cf8
--- /dev/null
+++ b/linux-user/mipsn32/termbits.h
@@ -0,0 +1,245 @@
+/* from asm/termbits.h */
+
+#define TARGET_NCCS 23
+
+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 */
+};
+
+/* 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 (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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_IEXTEN 0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE 0004000
+#define TARGET_FLUSHO 0010000
+#define TARGET_PENDIN 0040000
+#define TARGET_TOSTOP 0100000
+#define TARGET_ITOSTOP TARGET_TOSTOP
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VMIN 4
+#define TARGET_VTIME 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+#define TARGET_VSUSP 10
+/* VDSUSP not supported */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+#define TARGET_VEOF 16
+#define TARGET_VEOL 17
+
+/* ioctls */
+
+#define TARGET_TCGETA 0x5401
+#define TARGET_TCSETA 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */
+#define TARGET_TCSETAW 0x5403
+#define TARGET_TCSETAF 0x5404
+
+#define TARGET_TCSBRK 0x5405
+#define TARGET_TCXONC 0x5406
+#define TARGET_TCFLSH 0x5407
+
+#define TARGET_TCGETS 0x540d
+#define TARGET_TCSETS 0x540e
+#define TARGET_TCSETSW 0x540f
+#define TARGET_TCSETSF 0x5410
+
+#define TARGET_TIOCEXCL 0x740d /* set exclusive use of tty */
+#define TARGET_TIOCNXCL 0x740e /* reset exclusive use of tty */
+#define TARGET_TIOCOUTQ 0x7472 /* output queue size */
+#define TARGET_TIOCSTI 0x5472 /* simulate terminal input */
+#define TARGET_TIOCMGET 0x741d /* get all modem bits */
+#define TARGET_TIOCMBIS 0x741b /* bis modem bits */
+#define TARGET_TIOCMBIC 0x741c /* bic modem bits */
+#define TARGET_TIOCMSET 0x741a /* set all modem bits */
+#define TARGET_TIOCPKT 0x5470 /* pty: set/clear packet mode */
+#define TARGET_TIOCPKT_DATA 0x00 /* data packet */
+#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */
+#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */
+#define TARGET_TIOCPKT_STOP 0x04 /* stop output */
+#define TARGET_TIOCPKT_START 0x08 /* start output */
+#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */
+#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */
+/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize) /* set window size */
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize) /* get window size */
+#define TARGET_TIOCNOTTY 0x5471 /* void tty association */
+#define TARGET_TIOCSETD 0x7401
+#define TARGET_TIOCGETD 0x7400
+
+#define TARGET_FIOCLEX 0x6601
+#define TARGET_FIONCLEX 0x6602
+#define TARGET_FIOASYNC 0x667d
+#define TARGET_FIONBIO 0x667e
+#define TARGET_FIOQSIZE 0x667f
+
+#define TARGET_TIOCGLTC 0x7474 /* get special local chars */
+#define TARGET_TIOCSLTC 0x7475 /* set special local chars */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */
+#define TARGET_TIOCCONS TARGET_IOW('t', 120, int) /* become virtual console */
+
+#define TARGET_FIONREAD 0x467f
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+#define TARGET_TIOCGETP 0x7408
+#define TARGET_TIOCSETP 0x7409
+#define TARGET_TIOCSETN 0x740a /* TIOCSETP wo flush */
+
+/* #define TARGET_TIOCSETA TARGET_IOW('t', 20, struct termios) set termios struct */
+/* #define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct termios) drain output, set */
+/* #define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct termios) drn out, fls in, set */
+/* #define TARGET_TIOCGETD TARGET_IOR('t', 26, int) get line discipline */
+/* #define TARGET_TIOCSETD TARGET_IOW('t', 27, int) set line discipline */
+ /* 127-124 compat */
+
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x7416 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+/* I hope the range from 0x5480 on is free ... */
+#define TARGET_TIOCSCTTY 0x5480 /* become controlling tty */
+#define TARGET_TIOCGSOFTCAR 0x5481
+#define TARGET_TIOCSSOFTCAR 0x5482
+#define TARGET_TIOCLINUX 0x5483
+#define TARGET_TIOCGSERIAL 0x5484
+#define TARGET_TIOCSSERIAL 0x5485
+#define TARGET_TCSBRKP 0x5486 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCSERCONFIG 0x5488
+#define TARGET_TIOCSERGWILD 0x5489
+#define TARGET_TIOCSERSWILD 0x548a
+#define TARGET_TIOCGLCKTRMIOS 0x548b
+#define TARGET_TIOCSLCKTRMIOS 0x548c
+#define TARGET_TIOCSERGSTRUCT 0x548d /* For debugging only */
+#define TARGET_TIOCSERGETLSR 0x548e /* Get line status register */
+#define TARGET_TIOCSERGETMULTI 0x548f /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI 0x5490 /* Set multiport config */
+#define TARGET_TIOCMIWAIT 0x5491 /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT 0x5492 /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP 0x5493 /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x5494 /* Set Hayes ESP configuration */
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
new file mode 100644
index 0000000..abf21f6
--- /dev/null
+++ b/linux-user/mmap.c
@@ -0,0 +1,759 @@
+/*
+ * mmap support for qemu
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <linux/mman.h>
+#include <linux/unistd.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+
+//#define DEBUG_MMAP
+
+#if defined(CONFIG_USE_NPTL)
+static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
+static __thread int mmap_lock_count;
+
+void mmap_lock(void)
+{
+ if (mmap_lock_count++ == 0) {
+ pthread_mutex_lock(&mmap_mutex);
+ }
+}
+
+void mmap_unlock(void)
+{
+ if (--mmap_lock_count == 0) {
+ pthread_mutex_unlock(&mmap_mutex);
+ }
+}
+
+/* Grab lock to make sure things are in a consistent state after fork(). */
+void mmap_fork_start(void)
+{
+ if (mmap_lock_count)
+ abort();
+ pthread_mutex_lock(&mmap_mutex);
+}
+
+void mmap_fork_end(int child)
+{
+ if (child)
+ pthread_mutex_init(&mmap_mutex, NULL);
+ else
+ pthread_mutex_unlock(&mmap_mutex);
+}
+#else
+/* We aren't threadsafe to start with, so no need to worry about locking. */
+void mmap_lock(void)
+{
+}
+
+void mmap_unlock(void)
+{
+}
+#endif
+
+/* NOTE: all the constants are the HOST ones, but addresses are target. */
+int target_mprotect(abi_ulong start, abi_ulong len, int prot)
+{
+ abi_ulong end, host_start, host_end, addr;
+ int prot1, ret;
+
+#ifdef DEBUG_MMAP
+ printf("mprotect: start=0x" TARGET_ABI_FMT_lx
+ "len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c\n", start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+#endif
+
+ if ((start & ~TARGET_PAGE_MASK) != 0)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ prot &= PROT_READ | PROT_WRITE | PROT_EXEC;
+ if (len == 0)
+ return 0;
+
+ mmap_lock();
+ host_start = start & qemu_host_page_mask;
+ host_end = HOST_PAGE_ALIGN(end);
+ if (start > host_start) {
+ /* handle host page containing start */
+ prot1 = prot;
+ for(addr = host_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ if (host_end == host_start + qemu_host_page_size) {
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ end = host_end;
+ }
+ ret = mprotect(g2h(host_start), qemu_host_page_size, prot1 & PAGE_BITS);
+ if (ret != 0)
+ goto error;
+ host_start += qemu_host_page_size;
+ }
+ if (end < host_end) {
+ prot1 = prot;
+ for(addr = end; addr < host_end; addr += TARGET_PAGE_SIZE) {
+ prot1 |= page_get_flags(addr);
+ }
+ ret = mprotect(g2h(host_end - qemu_host_page_size), qemu_host_page_size,
+ prot1 & PAGE_BITS);
+ if (ret != 0)
+ goto error;
+ host_end -= qemu_host_page_size;
+ }
+
+ /* handle the pages in the middle */
+ if (host_start < host_end) {
+ ret = mprotect(g2h(host_start), host_end - host_start, prot);
+ if (ret != 0)
+ goto error;
+ }
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ mmap_unlock();
+ return 0;
+error:
+ mmap_unlock();
+ return ret;
+}
+
+/* map an incomplete host page */
+static int mmap_frag(abi_ulong real_start,
+ abi_ulong start, abi_ulong end,
+ int prot, int flags, int fd, abi_ulong offset)
+{
+ abi_ulong real_end, addr;
+ void *host_start;
+ int prot1, prot_new;
+
+ real_end = real_start + qemu_host_page_size;
+ host_start = g2h(real_start);
+
+ /* get the protection of the target pages outside the mapping */
+ prot1 = 0;
+ for(addr = real_start; addr < real_end; addr++) {
+ if (addr < start || addr >= end)
+ prot1 |= page_get_flags(addr);
+ }
+
+ if (prot1 == 0) {
+ /* no page was there, so we allocate one */
+ void *p = mmap(host_start, qemu_host_page_size, prot,
+ flags | MAP_ANONYMOUS, -1, 0);
+ if (p == MAP_FAILED)
+ return -1;
+ prot1 = prot;
+ }
+ prot1 &= PAGE_BITS;
+
+ prot_new = prot | prot1;
+ if (!(flags & MAP_ANONYMOUS)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+ if ((flags & MAP_TYPE) == MAP_SHARED &&
+ (prot & PROT_WRITE))
+ return -1;
+
+ /* adjust protection to be able to read */
+ if (!(prot1 & PROT_WRITE))
+ mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE);
+
+ /* read the corresponding file data */
+ if (pread(fd, g2h(start), end - start, offset) == -1)
+ return -1;
+
+ /* put final protection */
+ if (prot_new != (prot1 | PROT_WRITE))
+ mprotect(host_start, qemu_host_page_size, prot_new);
+ } else {
+ /* just update the protection */
+ if (prot_new != prot1) {
+ mprotect(host_start, qemu_host_page_size, prot_new);
+ }
+ }
+ return 0;
+}
+
+#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
+# define TASK_UNMAPPED_BASE (1ul << 38)
+#elif defined(__CYGWIN__)
+/* Cygwin doesn't have a whole lot of address space. */
+# define TASK_UNMAPPED_BASE 0x18000000
+#else
+# define TASK_UNMAPPED_BASE 0x40000000
+#endif
+static abi_ulong mmap_next_start = TASK_UNMAPPED_BASE;
+
+unsigned long last_brk;
+
+/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
+ of guest address space. */
+static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
+{
+ abi_ulong addr;
+ abi_ulong last_addr;
+ int prot;
+ int looped = 0;
+
+ if (size > RESERVED_VA) {
+ return (abi_ulong)-1;
+ }
+
+ last_addr = start;
+ for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) {
+ if (last_addr + size >= RESERVED_VA
+ || (abi_ulong)(last_addr + size) < last_addr) {
+ if (looped) {
+ return (abi_ulong)-1;
+ }
+ last_addr = qemu_host_page_size;
+ addr = 0;
+ looped = 1;
+ continue;
+ }
+ prot = page_get_flags(addr);
+ if (prot) {
+ last_addr = addr + qemu_host_page_size;
+ }
+ }
+ mmap_next_start = addr;
+ return last_addr;
+}
+
+/*
+ * Find and reserve a free memory area of size 'size'. The search
+ * starts at 'start'.
+ * It must be called with mmap_lock() held.
+ * Return -1 if error.
+ */
+abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
+{
+ void *ptr, *prev;
+ abi_ulong addr;
+ int wrapped, repeat;
+
+ /* If 'start' == 0, then a default start address is used. */
+ if (start == 0) {
+ start = mmap_next_start;
+ } else {
+ start &= qemu_host_page_mask;
+ }
+
+ size = HOST_PAGE_ALIGN(size);
+
+ if (RESERVED_VA) {
+ return mmap_find_vma_reserved(start, size);
+ }
+
+ addr = start;
+ wrapped = repeat = 0;
+ prev = 0;
+
+ for (;; prev = ptr) {
+ /*
+ * Reserve needed memory area to avoid a race.
+ * It should be discarded using:
+ * - mmap() with MAP_FIXED flag
+ * - mremap() with MREMAP_FIXED flag
+ * - shmat() with SHM_REMAP flag
+ */
+ ptr = mmap(g2h(addr), size, PROT_NONE,
+ MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
+
+ /* ENOMEM, if host address space has no memory */
+ if (ptr == MAP_FAILED) {
+ return (abi_ulong)-1;
+ }
+
+ /* Count the number of sequential returns of the same address.
+ This is used to modify the search algorithm below. */
+ repeat = (ptr == prev ? repeat + 1 : 0);
+
+ if (h2g_valid(ptr + size - 1)) {
+ addr = h2g(ptr);
+
+ if ((addr & ~TARGET_PAGE_MASK) == 0) {
+ /* Success. */
+ if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
+ mmap_next_start = addr + size;
+ }
+ return addr;
+ }
+
+ /* The address is not properly aligned for the target. */
+ switch (repeat) {
+ case 0:
+ /* Assume the result that the kernel gave us is the
+ first with enough free space, so start again at the
+ next higher target page. */
+ addr = TARGET_PAGE_ALIGN(addr);
+ break;
+ case 1:
+ /* Sometimes the kernel decides to perform the allocation
+ at the top end of memory instead. */
+ addr &= TARGET_PAGE_MASK;
+ break;
+ case 2:
+ /* Start over at low memory. */
+ addr = 0;
+ break;
+ default:
+ /* Fail. This unaligned block must the last. */
+ addr = -1;
+ break;
+ }
+ } else {
+ /* Since the result the kernel gave didn't fit, start
+ again at low memory. If any repetition, fail. */
+ addr = (repeat ? -1 : 0);
+ }
+
+ /* Unmap and try again. */
+ munmap(ptr, size);
+
+ /* ENOMEM if we checked the whole of the target address space. */
+ if (addr == (abi_ulong)-1) {
+ return (abi_ulong)-1;
+ } else if (addr == 0) {
+ if (wrapped) {
+ return (abi_ulong)-1;
+ }
+ wrapped = 1;
+ /* Don't actually use 0 when wrapping, instead indicate
+ that we'd truely like an allocation in low memory. */
+ addr = (mmap_min_addr > TARGET_PAGE_SIZE
+ ? TARGET_PAGE_ALIGN(mmap_min_addr)
+ : TARGET_PAGE_SIZE);
+ } else if (wrapped && addr >= start) {
+ return (abi_ulong)-1;
+ }
+ }
+}
+
+/* NOTE: all the constants are the HOST ones */
+abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset)
+{
+ abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
+ unsigned long host_start;
+
+ mmap_lock();
+#ifdef DEBUG_MMAP
+ {
+ printf("mmap: start=0x" TARGET_ABI_FMT_lx
+ " len=0x" TARGET_ABI_FMT_lx " prot=%c%c%c flags=",
+ start, len,
+ prot & PROT_READ ? 'r' : '-',
+ prot & PROT_WRITE ? 'w' : '-',
+ prot & PROT_EXEC ? 'x' : '-');
+ if (flags & MAP_FIXED)
+ printf("MAP_FIXED ");
+ if (flags & MAP_ANONYMOUS)
+ printf("MAP_ANON ");
+ switch(flags & MAP_TYPE) {
+ case MAP_PRIVATE:
+ printf("MAP_PRIVATE ");
+ break;
+ case MAP_SHARED:
+ printf("MAP_SHARED ");
+ break;
+ default:
+ printf("[MAP_TYPE=0x%x] ", flags & MAP_TYPE);
+ break;
+ }
+ printf("fd=%d offset=" TARGET_ABI_FMT_lx "\n", fd, offset);
+ }
+#endif
+
+ if (offset & ~TARGET_PAGE_MASK) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ goto the_end;
+ real_start = start & qemu_host_page_mask;
+
+ /* When mapping files into a memory area larger than the file, accesses
+ to pages beyond the file size will cause a SIGBUS.
+
+ For example, if mmaping a file of 100 bytes on a host with 4K pages
+ emulating a target with 8K pages, the target expects to be able to
+ access the first 8K. But the host will trap us on any access beyond
+ 4K.
+
+ When emulating a target with a larger page-size than the hosts, we
+ may need to truncate file maps at EOF and add extra anonymous pages
+ up to the targets page boundary. */
+
+ if ((qemu_real_host_page_size < TARGET_PAGE_SIZE)
+ && !(flags & MAP_ANONYMOUS)) {
+ struct stat sb;
+
+ if (fstat (fd, &sb) == -1)
+ goto fail;
+
+ /* Are we trying to create a map beyond EOF?. */
+ if (offset + len > sb.st_size) {
+ /* If so, truncate the file map at eof aligned with
+ the hosts real pagesize. Additional anonymous maps
+ will be created beyond EOF. */
+ len = (sb.st_size - offset);
+ len += qemu_real_host_page_size - 1;
+ len &= ~(qemu_real_host_page_size - 1);
+ }
+ }
+
+ if (!(flags & MAP_FIXED)) {
+ abi_ulong mmap_start;
+ void *p;
+ host_offset = offset & qemu_host_page_mask;
+ host_len = len + offset - host_offset;
+ host_len = HOST_PAGE_ALIGN(host_len);
+ mmap_start = mmap_find_vma(real_start, host_len);
+ if (mmap_start == (abi_ulong)-1) {
+ errno = ENOMEM;
+ goto fail;
+ }
+ /* Note: we prefer to control the mapping address. It is
+ especially important if qemu_host_page_size >
+ qemu_real_host_page_size */
+ p = mmap(g2h(mmap_start),
+ host_len, prot, flags | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
+ if (p == MAP_FAILED)
+ goto fail;
+ /* update start so that it points to the file position at 'offset' */
+ host_start = (unsigned long)p;
+ if (!(flags & MAP_ANONYMOUS)) {
+ p = mmap(g2h(mmap_start), len, prot,
+ flags | MAP_FIXED, fd, host_offset);
+ host_start += offset - host_offset;
+ }
+ start = h2g(host_start);
+ } else {
+ if (start & ~TARGET_PAGE_MASK) {
+ errno = EINVAL;
+ goto fail;
+ }
+ end = start + len;
+ real_end = HOST_PAGE_ALIGN(end);
+
+ /*
+ * Test if requested memory area fits target address space
+ * It can fail only on 64-bit host with 32-bit target.
+ * On any other target/host host mmap() handles this error correctly.
+ */
+ if ((unsigned long)start + len - 1 > (abi_ulong) -1) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ /* worst case: we cannot map the file because the offset is not
+ aligned, so we read it */
+ if (!(flags & MAP_ANONYMOUS) &&
+ (offset & ~qemu_host_page_mask) != (start & ~qemu_host_page_mask)) {
+ /* msync() won't work here, so we return an error if write is
+ possible while it is a shared mapping */
+ if ((flags & MAP_TYPE) == MAP_SHARED &&
+ (prot & PROT_WRITE)) {
+ errno = EINVAL;
+ goto fail;
+ }
+ retaddr = target_mmap(start, len, prot | PROT_WRITE,
+ MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ if (retaddr == -1)
+ goto fail;
+ if (pread(fd, g2h(start), len, offset) == -1)
+ goto fail;
+ if (!(prot & PROT_WRITE)) {
+ ret = target_mprotect(start, len, prot);
+ if (ret != 0) {
+ start = ret;
+ goto the_end;
+ }
+ }
+ goto the_end;
+ }
+
+ /* handle the start of the mapping */
+ if (start > real_start) {
+ if (real_end == real_start + qemu_host_page_size) {
+ /* one single host page */
+ ret = mmap_frag(real_start, start, end,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ goto fail;
+ goto the_end1;
+ }
+ ret = mmap_frag(real_start, start, real_start + qemu_host_page_size,
+ prot, flags, fd, offset);
+ if (ret == -1)
+ goto fail;
+ real_start += qemu_host_page_size;
+ }
+ /* handle the end of the mapping */
+ if (end < real_end) {
+ ret = mmap_frag(real_end - qemu_host_page_size,
+ real_end - qemu_host_page_size, real_end,
+ prot, flags, fd,
+ offset + real_end - qemu_host_page_size - start);
+ if (ret == -1)
+ goto fail;
+ real_end -= qemu_host_page_size;
+ }
+
+ /* map the middle (easier) */
+ if (real_start < real_end) {
+ void *p;
+ unsigned long offset1;
+ if (flags & MAP_ANONYMOUS)
+ offset1 = 0;
+ else
+ offset1 = offset + real_start - start;
+ p = mmap(g2h(real_start), real_end - real_start,
+ prot, flags, fd, offset1);
+ if (p == MAP_FAILED)
+ goto fail;
+ }
+ }
+ the_end1:
+ page_set_flags(start, start + len, prot | PAGE_VALID);
+ the_end:
+#ifdef DEBUG_MMAP
+ printf("ret=0x" TARGET_ABI_FMT_lx "\n", start);
+ page_dump(stdout);
+ printf("\n");
+#endif
+ mmap_unlock();
+ return start;
+fail:
+ mmap_unlock();
+ return -1;
+}
+
+static void mmap_reserve(abi_ulong start, abi_ulong size)
+{
+ abi_ulong real_start;
+ abi_ulong real_end;
+ abi_ulong addr;
+ abi_ulong end;
+ int prot;
+
+ real_start = start & qemu_host_page_mask;
+ real_end = HOST_PAGE_ALIGN(start + size);
+ end = start + size;
+ if (start > real_start) {
+ /* handle host page containing start */
+ prot = 0;
+ for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (real_end == real_start + qemu_host_page_size) {
+ for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ end = real_end;
+ }
+ if (prot != 0)
+ real_start += qemu_host_page_size;
+ }
+ if (end < real_end) {
+ prot = 0;
+ for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (prot != 0)
+ real_end -= qemu_host_page_size;
+ }
+ if (real_start != real_end) {
+ mmap(g2h(real_start), real_end - real_start, PROT_NONE,
+ MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE,
+ -1, 0);
+ }
+}
+
+int target_munmap(abi_ulong start, abi_ulong len)
+{
+ abi_ulong end, real_start, real_end, addr;
+ int prot, ret;
+
+#ifdef DEBUG_MMAP
+ printf("munmap: start=0x" TARGET_ABI_FMT_lx " len=0x"
+ TARGET_ABI_FMT_lx "\n",
+ start, len);
+#endif
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ if (len == 0)
+ return -EINVAL;
+ mmap_lock();
+ end = start + len;
+ real_start = start & qemu_host_page_mask;
+ real_end = HOST_PAGE_ALIGN(end);
+
+ if (start > real_start) {
+ /* handle host page containing start */
+ prot = 0;
+ for(addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (real_end == real_start + qemu_host_page_size) {
+ for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ end = real_end;
+ }
+ if (prot != 0)
+ real_start += qemu_host_page_size;
+ }
+ if (end < real_end) {
+ prot = 0;
+ for(addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) {
+ prot |= page_get_flags(addr);
+ }
+ if (prot != 0)
+ real_end -= qemu_host_page_size;
+ }
+
+ ret = 0;
+ /* unmap what we can */
+ if (real_start < real_end) {
+ if (RESERVED_VA) {
+ mmap_reserve(real_start, real_end - real_start);
+ } else {
+ ret = munmap(g2h(real_start), real_end - real_start);
+ }
+ }
+
+ if (ret == 0)
+ page_set_flags(start, start + len, 0);
+ mmap_unlock();
+ return ret;
+}
+
+abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
+ abi_ulong new_size, unsigned long flags,
+ abi_ulong new_addr)
+{
+ int prot;
+ void *host_addr;
+
+ mmap_lock();
+
+ if (flags & MREMAP_FIXED) {
+ host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
+ old_size, new_size,
+ flags,
+ g2h(new_addr));
+
+ if (RESERVED_VA && host_addr != MAP_FAILED) {
+ /* If new and old addresses overlap then the above mremap will
+ already have failed with EINVAL. */
+ mmap_reserve(old_addr, old_size);
+ }
+ } else if (flags & MREMAP_MAYMOVE) {
+ abi_ulong mmap_start;
+
+ mmap_start = mmap_find_vma(0, new_size);
+
+ if (mmap_start == -1) {
+ errno = ENOMEM;
+ host_addr = MAP_FAILED;
+ } else {
+ host_addr = (void *) syscall(__NR_mremap, g2h(old_addr),
+ old_size, new_size,
+ flags | MREMAP_FIXED,
+ g2h(mmap_start));
+ if ( RESERVED_VA ) {
+ mmap_reserve(old_addr, old_size);
+ }
+ }
+ } else {
+ int prot = 0;
+ if (RESERVED_VA && old_size < new_size) {
+ abi_ulong addr;
+ for (addr = old_addr + old_size;
+ addr < old_addr + new_size;
+ addr++) {
+ prot |= page_get_flags(addr);
+ }
+ }
+ if (prot == 0) {
+ host_addr = mremap(g2h(old_addr), old_size, new_size, flags);
+ if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) {
+ mmap_reserve(old_addr + old_size, new_size - old_size);
+ }
+ } else {
+ errno = ENOMEM;
+ host_addr = MAP_FAILED;
+ }
+ /* Check if address fits target address space */
+ if ((unsigned long)host_addr + new_size > (abi_ulong)-1) {
+ /* Revert mremap() changes */
+ host_addr = mremap(g2h(old_addr), new_size, old_size, flags);
+ errno = ENOMEM;
+ host_addr = MAP_FAILED;
+ }
+ }
+
+ if (host_addr == MAP_FAILED) {
+ new_addr = -1;
+ } else {
+ new_addr = h2g(host_addr);
+ prot = page_get_flags(old_addr);
+ page_set_flags(old_addr, old_addr + old_size, 0);
+ page_set_flags(new_addr, new_addr + new_size, prot | PAGE_VALID);
+ }
+ mmap_unlock();
+ return new_addr;
+}
+
+int target_msync(abi_ulong start, abi_ulong len, int flags)
+{
+ abi_ulong end;
+
+ if (start & ~TARGET_PAGE_MASK)
+ return -EINVAL;
+ len = TARGET_PAGE_ALIGN(len);
+ end = start + len;
+ if (end < start)
+ return -EINVAL;
+ if (end == start)
+ return 0;
+
+ start &= qemu_host_page_mask;
+ return msync(g2h(start), end - start, flags);
+}
diff --git a/linux-user/ppc/syscall.h b/linux-user/ppc/syscall.h
new file mode 100644
index 0000000..481047b
--- /dev/null
+++ b/linux-user/ppc/syscall.h
@@ -0,0 +1,64 @@
+/*
+ * PPC emulation for qemu: syscall definitions.
+ *
+ * Copyright (c) 2003 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+/* XXX: ABSOLUTELY BUGGY:
+ * for now, this is quite just a cut-and-paste from i386 target...
+ */
+
+/* default linux values for the selectors */
+#define __USER_DS (1)
+
+struct target_pt_regs {
+ abi_ulong gpr[32];
+ abi_ulong nip;
+ abi_ulong msr;
+ abi_ulong orig_gpr3; /* Used for restarting system calls */
+ abi_ulong ctr;
+ abi_ulong link;
+ abi_ulong xer;
+ abi_ulong ccr;
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ abi_ulong softe;
+#else
+ abi_ulong mq; /* 601 only (not used at present) */
+#endif
+ /* Used on APUS to hold IPL value. */
+ abi_ulong trap; /* Reason for being here */
+ abi_ulong dar; /* Fault registers */
+ abi_ulong dsisr;
+ abi_ulong result; /* Result of a system call */
+};
+
+/* ioctls */
+struct target_revectored_struct {
+ abi_ulong __map[8]; /* 256 bits */
+};
+
+/* Nasty hack: define a fake errno value for use by sigreturn. */
+#define TARGET_QEMU_ESIGRETURN 255
+
+/*
+ * flags masks
+ */
+
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+#define UNAME_MACHINE "ppc64"
+#else
+#define UNAME_MACHINE "ppc"
+#endif
diff --git a/linux-user/ppc/syscall_nr.h b/linux-user/ppc/syscall_nr.h
new file mode 100644
index 0000000..cc84a4c
--- /dev/null
+++ b/linux-user/ppc/syscall_nr.h
@@ -0,0 +1,334 @@
+/*
+ * This file contains the system call numbers.
+ */
+#define TARGET_NR_restart_syscall 0
+#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_waitpid 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_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#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_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#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_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#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_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#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_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86 113
+#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_modify_ldt 123
+#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_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#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_setresuid32 164
+#define TARGET_NR_getresuid32 165
+#define TARGET_NR_query_module 166
+#define TARGET_NR_poll 167
+#define TARGET_NR_nfsservctl 168
+#define TARGET_NR_setresgid32 169
+#define TARGET_NR_getresgid32 170
+#define TARGET_NR_prctl 171
+#define TARGET_NR_rt_sigreturn 172
+#define TARGET_NR_rt_sigaction 173
+#define TARGET_NR_rt_sigprocmask 174
+#define TARGET_NR_rt_sigpending 175
+#define TARGET_NR_rt_sigtimedwait 176
+#define TARGET_NR_rt_sigqueueinfo 177
+#define TARGET_NR_rt_sigsuspend 178
+#define TARGET_NR_pread64 179
+#define TARGET_NR_pwrite64 180
+#define TARGET_NR_chown 181
+#define TARGET_NR_getcwd 182
+#define TARGET_NR_capget 183
+#define TARGET_NR_capset 184
+#define TARGET_NR_sigaltstack 185
+#define TARGET_NR_sendfile 186
+#define TARGET_NR_getpmsg 187 /* some people actually want streams */
+#define TARGET_NR_putpmsg 188 /* some people actually want streams */
+#define TARGET_NR_vfork 189
+#define TARGET_NR_ugetrlimit 190 /* SuS compliant getrlimit */
+#define TARGET_NR_readahead 191
+#if !defined(TARGET_PPC64) || defined(TARGET_ABI32)
+#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
+#endif
+#define TARGET_NR_pciconfig_read 198
+#define TARGET_NR_pciconfig_write 199
+#define TARGET_NR_pciconfig_iobase 200
+#define TARGET_NR_multiplexer 201
+#define TARGET_NR_getdents64 202
+#define TARGET_NR_pivot_root 203
+#if !defined(TARGET_PPC64) || defined(TARGET_ABI32)
+#define TARGET_NR_fcntl64 204
+#endif
+#define TARGET_NR_madvise 205
+#define TARGET_NR_mincore 206
+#define TARGET_NR_gettid 207
+#define TARGET_NR_tkill 208
+#define TARGET_NR_setxattr 209
+#define TARGET_NR_lsetxattr 210
+#define TARGET_NR_fsetxattr 211
+#define TARGET_NR_getxattr 212
+#define TARGET_NR_lgetxattr 213
+#define TARGET_NR_fgetxattr 214
+#define TARGET_NR_listxattr 215
+#define TARGET_NR_llistxattr 216
+#define TARGET_NR_flistxattr 217
+#define TARGET_NR_removexattr 218
+#define TARGET_NR_lremovexattr 219
+#define TARGET_NR_fremovexattr 220
+#define TARGET_NR_futex 221
+#define TARGET_NR_sched_setaffinity 222
+#define TARGET_NR_sched_getaffinity 223
+/* 224 currently unused */
+#define TARGET_NR_tuxcall 225
+#if !defined(TARGET_PPC64) || defined(TARGET_ABI32)
+#define TARGET_NR_sendfile64 226
+#endif
+#define TARGET_NR_io_setup 227
+#define TARGET_NR_io_destroy 228
+#define TARGET_NR_io_getevents 229
+#define TARGET_NR_io_submit 230
+#define TARGET_NR_io_cancel 231
+#define TARGET_NR_set_tid_address 232
+#define TARGET_NR_fadvise64 233
+#define TARGET_NR_exit_group 234
+#define TARGET_NR_lookup_dcookie 235
+#define TARGET_NR_epoll_create 236
+#define TARGET_NR_epoll_ctl 237
+#define TARGET_NR_epoll_wait 238
+#define TARGET_NR_remap_file_pages 239
+#define TARGET_NR_timer_create 240
+#define TARGET_NR_timer_settime 241
+#define TARGET_NR_timer_gettime 242
+#define TARGET_NR_timer_getoverrun 243
+#define TARGET_NR_timer_delete 244
+#define TARGET_NR_clock_settime 245
+#define TARGET_NR_clock_gettime 246
+#define TARGET_NR_clock_getres 247
+#define TARGET_NR_clock_nanosleep 248
+#define TARGET_NR_swapcontext 249
+#define TARGET_NR_tgkill 250
+#define TARGET_NR_utimes 251
+#define TARGET_NR_statfs64 252
+#define TARGET_NR_fstatfs64 253
+#if !defined(TARGET_PPC64) || defined(TARGET_ABI32)
+#define TARGET_NR_fadvise64_64 254
+#endif
+#define TARGET_NR_rtas 255
+#define TARGET_NR_sys_debug_setcontext 256
+/* Number 257 is reserved for vserver */
+#define TARGET_NR_migrate_pages 258
+#define TARGET_NR_mbind 259
+#define TARGET_NR_get_mempolicy 260
+#define TARGET_NR_set_mempolicy 261
+#define TARGET_NR_mq_open 262
+#define TARGET_NR_mq_unlink 263
+#define TARGET_NR_mq_timedsend 264
+#define TARGET_NR_mq_timedreceive 265
+#define TARGET_NR_mq_notify 266
+#define TARGET_NR_mq_getsetattr 267
+#define TARGET_NR_kexec_load 268
+#define TARGET_NR_add_key 269
+#define TARGET_NR_request_key 270
+#define TARGET_NR_keyctl 271
+#define TARGET_NR_waitid 272
+#define TARGET_NR_ioprio_set 273
+#define TARGET_NR_ioprio_get 274
+#define TARGET_NR_inotify_init 275
+#define TARGET_NR_inotify_add_watch 276
+#define TARGET_NR_inotify_rm_watch 277
+#define TARGET_NR_spu_run 278
+#define TARGET_NR_spu_create 279
+#define TARGET_NR_pselect6 280
+#define TARGET_NR_ppoll 281
+#define TARGET_NR_unshare 282
+#define TARGET_NR_splice 283
+#define TARGET_NR_tee 284
+#define TARGET_NR_vmsplice 285
+#define TARGET_NR_openat 286
+#define TARGET_NR_mkdirat 287
+#define TARGET_NR_mknodat 288
+#define TARGET_NR_fchownat 289
+#define TARGET_NR_futimesat 290
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+#define TARGET_NR_newfstatat 291
+#else
+#define TARGET_NR_fstatat64 291
+#endif
+#define TARGET_NR_unlinkat 292
+#define TARGET_NR_renameat 293
+#define TARGET_NR_linkat 294
+#define TARGET_NR_symlinkat 295
+#define TARGET_NR_readlinkat 296
+#define TARGET_NR_fchmodat 297
+#define TARGET_NR_faccessat 298
+#define TARGET_NR_get_robust_list 299
+#define TARGET_NR_set_robust_list 300
+#define TARGET_NR_move_pages 301
+#define TARGET_NR_getcpu 302
+#define TARGET_NR_epoll_pwait 303
+#define TARGET_NR_utimensat 304
+#define TARGET_NR_signalfd 305
+#define TARGET_NR_timerfd 306
+#define TARGET_NR_eventfd 307
+#define TARGET_NR_sync_file_range2 308
+#define TARGET_NR_fallocate 309
+#define TARGET_NR_subpage_prot 310
+#define TARGET_NR_timerfd_settime 311
+#define TARGET_NR_timerfd_gettime 312
+#define TARGET_NR_signalfd4 313
+#define TARGET_NR_eventfd2 314
+#define TARGET_NR_epoll_create1 315
+#define TARGET_NR_dup3 316
+#define TARGET_NR_pipe2 317
+#define TARGET_NR_inotify_init1 318
diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h
new file mode 100644
index 0000000..a93b5cf
--- /dev/null
+++ b/linux-user/ppc/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+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(CPUPPCState *state)
+{
+ return state->gpr[1];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/ppc/termbits.h b/linux-user/ppc/termbits.h
new file mode 100644
index 0000000..73e7151
--- /dev/null
+++ b/linux-user/ppc/termbits.h
@@ -0,0 +1,236 @@
+/* from asm/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_cc[TARGET_NCCS]; /* control characters */
+ unsigned char c_line; /* line discipline */
+ unsigned int c_ispeed; /* input speed */
+ unsigned int c_ospeed; /* output speed */
+};
+
+/* c_cc character offsets */
+#define TARGET_VINTR 0
+#define TARGET_VQUIT 1
+#define TARGET_VERASE 2
+#define TARGET_VKILL 3
+#define TARGET_VEOF 4
+#define TARGET_VMIN 5
+#define TARGET_VEOL 6
+#define TARGET_VTIME 7
+#define TARGET_VEOL2 8
+#define TARGET_VSWTC 9
+
+#define TARGET_VWERASE 10
+#define TARGET_VREPRINT 11
+#define TARGET_VSUSP 12
+#define TARGET_VSTART 13
+#define TARGET_VSTOP 14
+#define TARGET_VLNEXT 15
+#define TARGET_VDISCARD 16
+
+#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_IXON 0001000
+#define TARGET_IXOFF 0002000
+#define TARGET_IXANY 0004000
+#define TARGET_IUCLC 0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8 0040000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0000001
+#define TARGET_ONLCR 0000002
+#define TARGET_OLCUC 0000004
+
+#define TARGET_OCRNL 0000010
+#define TARGET_ONOCR 0000020
+#define TARGET_ONLRET 0000040
+
+#define TARGET_OFILL 00000100
+#define TARGET_OFDEL 00000200
+#define TARGET_NLDLY 00001400
+#define TARGET_NL0 00000000
+#define TARGET_NL1 00000400
+#define TARGET_NL2 00001000
+#define TARGET_NL3 00001400
+#define TARGET_TABDLY 00006000
+#define TARGET_TAB0 00000000
+#define TARGET_TAB1 00002000
+#define TARGET_TAB2 00004000
+#define TARGET_TAB3 00006000
+#define TARGET_XTABS 00006000 /* required by POSIX to == TAB3 */
+#define TARGET_CRDLY 00030000
+#define TARGET_CR0 00000000
+#define TARGET_CR1 00010000
+#define TARGET_CR2 00020000
+#define TARGET_CR3 00030000
+#define TARGET_FFDLY 00040000
+#define TARGET_FF0 00000000
+#define TARGET_FF1 00040000
+#define TARGET_BSDLY 00100000
+#define TARGET_BS0 00000000
+#define TARGET_BS1 00100000
+#define TARGET_VTDLY 00200000
+#define TARGET_VT0 00000000
+#define TARGET_VT1 00200000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0000377
+#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_CBAUDEX 0000000
+#define TARGET_B57600 00020
+#define TARGET_B115200 00021
+#define TARGET_B230400 00022
+#define TARGET_B460800 00023
+#define TARGET_B500000 00024
+#define TARGET_B576000 00025
+#define TARGET_B921600 00026
+#define TARGET_B1000000 00027
+#define TARGET_B1152000 00030
+#define TARGET_B1500000 00031
+#define TARGET_B2000000 00032
+#define TARGET_B2500000 00033
+#define TARGET_B3000000 00034
+#define TARGET_B3500000 00035
+#define TARGET_B4000000 00036
+
+#define TARGET_CSIZE 00001400
+#define TARGET_CS5 00000000
+#define TARGET_CS6 00000400
+#define TARGET_CS7 00001000
+#define TARGET_CS8 00001400
+
+#define TARGET_CSTOPB 00002000
+#define TARGET_CREAD 00004000
+#define TARGET_PARENB 00010000
+#define TARGET_PARODD 00020000
+#define TARGET_HUPCL 00040000
+
+#define TARGET_CLOCAL 00100000
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000080
+#define TARGET_ICANON 0x00000100
+#define TARGET_XCASE 0x00004000
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000002
+#define TARGET_ECHOK 0x00000004
+#define TARGET_ECHONL 0x00000010
+#define TARGET_NOFLSH 0x80000000
+#define TARGET_TOSTOP 0x00400000
+#define TARGET_ECHOCTL 0x00000040
+#define TARGET_ECHOPRT 0x00000020
+#define TARGET_ECHOKE 0x00000001
+#define TARGET_FLUSHO 0x00800000
+#define TARGET_PENDIN 0x20000000
+#define TARGET_IEXTEN 0x00000400
+
+/* ioctls */
+
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+//#define TARGET_FIOQSIZE TARGET_IOR('f', 128, loff_t)
+
+#define TARGET_TCGETS TARGET_IOR('t', 19, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('t', 20, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('t', 21, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('t', 22, struct target_termios)
+
+#define TARGET_TCGETA TARGET_IOR('t', 23, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('t', 24, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('t', 25, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('t', 28, struct target_termio)
+
+#define TARGET_TCSBRK TARGET_IO('t', 29)
+#define TARGET_TCXONC TARGET_IO('t', 30)
+#define TARGET_TCFLSH TARGET_IO('t', 31)
+
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct target_winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct target_winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCGLTC TARGET_IOR('t', 116, struct target_ltchars)
+#define TARGET_TIOCSLTC TARGET_IOW('t', 117, struct target_ltchars)
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+
+#define TARGET_TIOCEXCL 0x540C
+#define TARGET_TIOCNXCL 0x540D
+#define TARGET_TIOCSCTTY 0x540E
+
+#define TARGET_TIOCSTI 0x5412
+#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_TIOCLINUX 0x541C
+#define TARGET_TIOCCONS 0x541D
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TIOCPKT 0x5420
+
+#define TARGET_TIOCNOTTY 0x5422
+#define TARGET_TIOCSETD 0x5423
+#define TARGET_TIOCGETD 0x5424
+#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+#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 */
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+# define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+#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 */
diff --git a/linux-user/qemu-types.h b/linux-user/qemu-types.h
new file mode 100644
index 0000000..1adda9f
--- /dev/null
+++ b/linux-user/qemu-types.h
@@ -0,0 +1,24 @@
+#ifndef QEMU_TYPES_H
+#define QEMU_TYPES_H
+#include "cpu.h"
+
+#ifdef TARGET_ABI32
+typedef uint32_t abi_ulong;
+typedef int32_t abi_long;
+#define TARGET_ABI_FMT_lx "%08x"
+#define TARGET_ABI_FMT_ld "%d"
+#define TARGET_ABI_FMT_lu "%u"
+#define TARGET_ABI_BITS 32
+#else
+typedef target_ulong abi_ulong;
+typedef target_long abi_long;
+#define TARGET_ABI_FMT_lx TARGET_FMT_lx
+#define TARGET_ABI_FMT_ld TARGET_FMT_ld
+#define TARGET_ABI_FMT_lu TARGET_FMT_lu
+#define TARGET_ABI_BITS TARGET_LONG_BITS
+/* for consistency, define ABI32 too */
+#if TARGET_ABI_BITS == 32
+#define TARGET_ABI32 1
+#endif
+#endif
+#endif
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
new file mode 100644
index 0000000..32de241
--- /dev/null
+++ b/linux-user/qemu.h
@@ -0,0 +1,440 @@
+#ifndef QEMU_H
+#define QEMU_H
+
+#include <signal.h>
+#include <string.h>
+
+#include "cpu.h"
+
+#undef DEBUG_REMAP
+#ifdef DEBUG_REMAP
+#include <stdlib.h>
+#endif /* DEBUG_REMAP */
+
+#include "qemu-types.h"
+
+#include "thunk.h"
+#include "syscall_defs.h"
+#include "syscall.h"
+#include "target_signal.h"
+#include "gdbstub.h"
+#include "qemu-queue.h"
+
+#if defined(CONFIG_USE_NPTL)
+#define THREAD __thread
+#else
+#define THREAD
+#endif
+
+/* This struct is used to hold certain information about the image.
+ * Basically, it replicates in user space what would be certain
+ * task_struct fields in the kernel
+ */
+struct image_info {
+ abi_ulong load_bias;
+ abi_ulong load_addr;
+ abi_ulong start_code;
+ abi_ulong end_code;
+ abi_ulong start_data;
+ abi_ulong end_data;
+ abi_ulong start_brk;
+ abi_ulong brk;
+ abi_ulong start_mmap;
+ abi_ulong mmap;
+ abi_ulong rss;
+ abi_ulong start_stack;
+ abi_ulong stack_limit;
+ abi_ulong entry;
+ abi_ulong code_offset;
+ abi_ulong data_offset;
+ abi_ulong saved_auxv;
+ abi_ulong arg_start;
+ abi_ulong arg_end;
+ int personality;
+};
+
+#ifdef TARGET_I386
+/* Information about the current linux thread */
+struct vm86_saved_state {
+ uint32_t eax; /* return code */
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t eflags;
+ uint32_t eip;
+ uint16_t cs, ss, ds, es, fs, gs;
+};
+#endif
+
+#ifdef TARGET_ARM
+/* FPU emulator */
+#include "nwfpe/fpa11.h"
+#endif
+
+#define MAX_SIGQUEUE_SIZE 1024
+
+struct sigqueue {
+ struct sigqueue *next;
+ target_siginfo_t info;
+};
+
+struct emulated_sigtable {
+ int pending; /* true if signal is pending */
+ struct sigqueue *first;
+ struct sigqueue info; /* in order to always have memory for the
+ first signal, we put it here */
+};
+
+/* NOTE: we force a big alignment so that the stack stored after is
+ aligned too */
+typedef struct TaskState {
+ pid_t ts_tid; /* tid (or pid) of this task */
+#ifdef TARGET_ARM
+ /* FPA state */
+ FPA11 fpa;
+ int swi_errno;
+#endif
+#if defined(TARGET_I386) && !defined(TARGET_X86_64)
+ abi_ulong target_v86;
+ struct vm86_saved_state vm86_saved_regs;
+ struct target_vm86plus_struct vm86plus;
+ uint32_t v86flags;
+ uint32_t v86mask;
+#endif
+#ifdef CONFIG_USE_NPTL
+ abi_ulong child_tidptr;
+#endif
+#ifdef TARGET_M68K
+ int sim_syscalls;
+#endif
+#if defined(TARGET_ARM) || defined(TARGET_M68K)
+ /* Extra fields for semihosted binaries. */
+ uint32_t stack_base;
+ uint32_t heap_base;
+ uint32_t heap_limit;
+#endif
+ int used; /* non zero if used */
+ struct image_info *info;
+ struct linux_binprm *bprm;
+
+ struct emulated_sigtable sigtab[TARGET_NSIG];
+ struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
+ struct sigqueue *first_free; /* first free siginfo queue entry */
+ int signal_pending; /* non zero if a signal may be pending */
+} __attribute__((aligned(16))) TaskState;
+
+extern char *exec_path;
+void init_task_state(TaskState *ts);
+void task_settid(TaskState *);
+void stop_all_tasks(void);
+extern const char *qemu_uname_release;
+extern unsigned long mmap_min_addr;
+
+/* ??? See if we can avoid exposing so much of the loader internals. */
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB w/4KB pages!
+ */
+#define MAX_ARG_PAGES 33
+
+/* Read a good amount of data initially, to hopefully get all the
+ program headers loaded. */
+#define BPRM_BUF_SIZE 1024
+
+/*
+ * This structure is used to hold the arguments that are
+ * used when loading binaries.
+ */
+struct linux_binprm {
+ char buf[BPRM_BUF_SIZE] __attribute__((aligned));
+ void *page[MAX_ARG_PAGES];
+ abi_ulong p;
+ int fd;
+ int e_uid, e_gid;
+ int argc, envc;
+ char **argv;
+ char **envp;
+ char * filename; /* Name of binary */
+ int (*core_dump)(int, const CPUState *); /* coredump routine */
+};
+
+void do_init_thread(struct target_pt_regs *regs, struct image_info *infop);
+abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
+ abi_ulong stringp, int push_ptr);
+int loader_exec(const char * filename, char ** argv, char ** envp,
+ struct target_pt_regs * regs, struct image_info *infop,
+ struct linux_binprm *);
+
+int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info);
+int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,
+ struct image_info * info);
+
+abi_long memcpy_to_target(abi_ulong dest, const void *src,
+ unsigned long len);
+void target_set_brk(abi_ulong new_brk);
+abi_long do_brk(abi_ulong new_brk);
+void syscall_init(void);
+abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6);
+void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+extern THREAD CPUState *thread_env;
+void cpu_loop(CPUState *env);
+char *target_strerror(int err);
+int get_osversion(void);
+void fork_start(void);
+void fork_end(int child);
+
+#include "qemu-log.h"
+
+/* strace.c */
+void print_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6);
+void print_syscall_ret(int num, abi_long arg1);
+extern int do_strace;
+
+/* signal.c */
+void process_pending_signals(CPUState *cpu_env);
+void signal_init(void);
+int queue_signal(CPUState *env, int sig, target_siginfo_t *info);
+void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
+void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
+int target_to_host_signal(int sig);
+int host_to_target_signal(int sig);
+long do_sigreturn(CPUState *env);
+long do_rt_sigreturn(CPUState *env);
+abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
+
+#ifdef TARGET_I386
+/* vm86.c */
+void save_v86_state(CPUX86State *env);
+void handle_vm86_trap(CPUX86State *env, int trapno);
+void handle_vm86_fault(CPUX86State *env);
+int do_vm86(CPUX86State *env, long subfunction, abi_ulong v86_addr);
+#elif defined(TARGET_SPARC64)
+void sparc64_set_context(CPUSPARCState *env);
+void sparc64_get_context(CPUSPARCState *env);
+#endif
+
+/* mmap.c */
+int target_mprotect(abi_ulong start, abi_ulong len, int prot);
+abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset);
+int target_munmap(abi_ulong start, abi_ulong len);
+abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
+ abi_ulong new_size, unsigned long flags,
+ abi_ulong new_addr);
+int target_msync(abi_ulong start, abi_ulong len, int flags);
+extern unsigned long last_brk;
+void mmap_lock(void);
+void mmap_unlock(void);
+abi_ulong mmap_find_vma(abi_ulong, abi_ulong);
+void cpu_list_lock(void);
+void cpu_list_unlock(void);
+#if defined(CONFIG_USE_NPTL)
+void mmap_fork_start(void);
+void mmap_fork_end(int child);
+#endif
+
+/* main.c */
+extern unsigned long guest_stack_size;
+
+/* user access */
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1 /* implies read access */
+
+static inline int access_ok(int type, abi_ulong addr, abi_ulong size)
+{
+ return page_check_range((target_ulong)addr, size,
+ (type == VERIFY_READ) ? PAGE_READ : (PAGE_READ | PAGE_WRITE)) == 0;
+}
+
+/* NOTE __get_user and __put_user use host pointers and don't check access. */
+/* These are usually used to access struct data members once the
+ * struct has been locked - usually with lock_user_struct().
+ */
+#define __put_user(x, hptr)\
+({\
+ switch(sizeof(*hptr)) {\
+ case 1:\
+ *(uint8_t *)(hptr) = (uint8_t)(typeof(*hptr))(x);\
+ break;\
+ case 2:\
+ *(uint16_t *)(hptr) = tswap16((uint16_t)(typeof(*hptr))(x));\
+ break;\
+ case 4:\
+ *(uint32_t *)(hptr) = tswap32((uint32_t)(typeof(*hptr))(x));\
+ break;\
+ case 8:\
+ *(uint64_t *)(hptr) = tswap64((typeof(*hptr))(x));\
+ break;\
+ default:\
+ abort();\
+ }\
+ 0;\
+})
+
+#define __get_user(x, hptr) \
+({\
+ switch(sizeof(*hptr)) {\
+ case 1:\
+ x = (typeof(*hptr))*(uint8_t *)(hptr);\
+ break;\
+ case 2:\
+ x = (typeof(*hptr))tswap16(*(uint16_t *)(hptr));\
+ break;\
+ case 4:\
+ x = (typeof(*hptr))tswap32(*(uint32_t *)(hptr));\
+ break;\
+ case 8:\
+ x = (typeof(*hptr))tswap64(*(uint64_t *)(hptr));\
+ break;\
+ default:\
+ /* avoid warning */\
+ x = 0;\
+ abort();\
+ }\
+ 0;\
+})
+
+/* put_user()/get_user() take a guest address and check access */
+/* These are usually used to access an atomic data type, such as an int,
+ * that has been passed by address. These internally perform locking
+ * and unlocking on the data type.
+ */
+#define put_user(x, gaddr, target_type) \
+({ \
+ abi_ulong __gaddr = (gaddr); \
+ target_type *__hptr; \
+ abi_long __ret; \
+ if ((__hptr = lock_user(VERIFY_WRITE, __gaddr, sizeof(target_type), 0))) { \
+ __ret = __put_user((x), __hptr); \
+ unlock_user(__hptr, __gaddr, sizeof(target_type)); \
+ } else \
+ __ret = -TARGET_EFAULT; \
+ __ret; \
+})
+
+#define get_user(x, gaddr, target_type) \
+({ \
+ abi_ulong __gaddr = (gaddr); \
+ target_type *__hptr; \
+ abi_long __ret; \
+ if ((__hptr = lock_user(VERIFY_READ, __gaddr, sizeof(target_type), 1))) { \
+ __ret = __get_user((x), __hptr); \
+ unlock_user(__hptr, __gaddr, 0); \
+ } else { \
+ /* avoid warning */ \
+ (x) = 0; \
+ __ret = -TARGET_EFAULT; \
+ } \
+ __ret; \
+})
+
+#define put_user_ual(x, gaddr) put_user((x), (gaddr), abi_ulong)
+#define put_user_sal(x, gaddr) put_user((x), (gaddr), abi_long)
+#define put_user_u64(x, gaddr) put_user((x), (gaddr), uint64_t)
+#define put_user_s64(x, gaddr) put_user((x), (gaddr), int64_t)
+#define put_user_u32(x, gaddr) put_user((x), (gaddr), uint32_t)
+#define put_user_s32(x, gaddr) put_user((x), (gaddr), int32_t)
+#define put_user_u16(x, gaddr) put_user((x), (gaddr), uint16_t)
+#define put_user_s16(x, gaddr) put_user((x), (gaddr), int16_t)
+#define put_user_u8(x, gaddr) put_user((x), (gaddr), uint8_t)
+#define put_user_s8(x, gaddr) put_user((x), (gaddr), int8_t)
+
+#define get_user_ual(x, gaddr) get_user((x), (gaddr), abi_ulong)
+#define get_user_sal(x, gaddr) get_user((x), (gaddr), abi_long)
+#define get_user_u64(x, gaddr) get_user((x), (gaddr), uint64_t)
+#define get_user_s64(x, gaddr) get_user((x), (gaddr), int64_t)
+#define get_user_u32(x, gaddr) get_user((x), (gaddr), uint32_t)
+#define get_user_s32(x, gaddr) get_user((x), (gaddr), int32_t)
+#define get_user_u16(x, gaddr) get_user((x), (gaddr), uint16_t)
+#define get_user_s16(x, gaddr) get_user((x), (gaddr), int16_t)
+#define get_user_u8(x, gaddr) get_user((x), (gaddr), uint8_t)
+#define get_user_s8(x, gaddr) get_user((x), (gaddr), int8_t)
+
+/* copy_from_user() and copy_to_user() are usually used to copy data
+ * buffers between the target and host. These internally perform
+ * locking/unlocking of the memory.
+ */
+abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len);
+abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len);
+
+/* Functions for accessing guest memory. The tget and tput functions
+ read/write single values, byteswapping as neccessary. The lock_user
+ gets a pointer to a contiguous area of guest memory, but does not perform
+ and byteswapping. lock_user may return either a pointer to the guest
+ memory, or a temporary buffer. */
+
+/* Lock an area of guest memory into the host. If copy is true then the
+ host area will have the same contents as the guest. */
+static inline void *lock_user(int type, abi_ulong guest_addr, long len, int copy)
+{
+ if (!access_ok(type, guest_addr, len))
+ return NULL;
+#ifdef DEBUG_REMAP
+ {
+ void *addr;
+ addr = malloc(len);
+ if (copy)
+ memcpy(addr, g2h(guest_addr), len);
+ else
+ memset(addr, 0, len);
+ return addr;
+ }
+#else
+ return g2h(guest_addr);
+#endif
+}
+
+/* Unlock an area of guest memory. The first LEN bytes must be
+ flushed back to guest memory. host_ptr = NULL is explicitly
+ allowed and does nothing. */
+static inline void unlock_user(void *host_ptr, abi_ulong guest_addr,
+ long len)
+{
+
+#ifdef DEBUG_REMAP
+ if (!host_ptr)
+ return;
+ if (host_ptr == g2h(guest_addr))
+ return;
+ if (len > 0)
+ memcpy(g2h(guest_addr), host_ptr, len);
+ free(host_ptr);
+#endif
+}
+
+/* Return the length of a string in target memory or -TARGET_EFAULT if
+ access error. */
+abi_long target_strlen(abi_ulong gaddr);
+
+/* Like lock_user but for null terminated strings. */
+static inline void *lock_user_string(abi_ulong guest_addr)
+{
+ abi_long len;
+ len = target_strlen(guest_addr);
+ if (len < 0)
+ return NULL;
+ return lock_user(VERIFY_READ, guest_addr, (long)(len + 1), 1);
+}
+
+/* Helper macros for locking/ulocking a target struct. */
+#define lock_user_struct(type, host_ptr, guest_addr, copy) \
+ (host_ptr = lock_user(type, guest_addr, sizeof(*host_ptr), copy))
+#define unlock_user_struct(host_ptr, guest_addr, copy) \
+ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0)
+
+#if defined(CONFIG_USE_NPTL)
+#include <pthread.h>
+#endif
+
+#endif /* QEMU_H */
diff --git a/linux-user/sh4/syscall.h b/linux-user/sh4/syscall.h
new file mode 100644
index 0000000..014bf58
--- /dev/null
+++ b/linux-user/sh4/syscall.h
@@ -0,0 +1,12 @@
+struct target_pt_regs {
+ unsigned long regs[16];
+ unsigned long pc;
+ unsigned long pr;
+ unsigned long sr;
+ unsigned long gbr;
+ unsigned long mach;
+ unsigned long macl;
+ long tra;
+};
+
+#define UNAME_MACHINE "sh4"
diff --git a/linux-user/sh4/syscall_nr.h b/linux-user/sh4/syscall_nr.h
new file mode 100644
index 0000000..262b236
--- /dev/null
+++ b/linux-user/sh4/syscall_nr.h
@@ -0,0 +1,336 @@
+/*
+ * This file contains the system call numbers.
+ */
+
+#define TARGET_NR_restart_syscall 0
+#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_waitpid 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_time 13
+#define TARGET_NR_mknod 14
+#define TARGET_NR_chmod 15
+#define TARGET_NR_lchown 16
+#define TARGET_NR_break 17
+#define TARGET_NR_oldstat 18
+#define TARGET_NR_lseek 19
+#define TARGET_NR_getpid 20
+#define TARGET_NR_mount 21
+#define TARGET_NR_umount 22
+#define TARGET_NR_setuid 23
+#define TARGET_NR_getuid 24
+#define TARGET_NR_stime 25
+#define TARGET_NR_ptrace 26
+#define TARGET_NR_alarm 27
+#define TARGET_NR_oldfstat 28
+#define TARGET_NR_pause 29
+#define TARGET_NR_utime 30
+#define TARGET_NR_stty 31
+#define TARGET_NR_gtty 32
+#define TARGET_NR_access 33
+#define TARGET_NR_nice 34
+#define TARGET_NR_ftime 35
+#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_prof 44
+#define TARGET_NR_brk 45
+#define TARGET_NR_setgid 46
+#define TARGET_NR_getgid 47
+#define TARGET_NR_signal 48
+#define TARGET_NR_geteuid 49
+#define TARGET_NR_getegid 50
+#define TARGET_NR_acct 51
+#define TARGET_NR_umount2 52
+#define TARGET_NR_lock 53
+#define TARGET_NR_ioctl 54
+#define TARGET_NR_fcntl 55
+#define TARGET_NR_mpx 56
+#define TARGET_NR_setpgid 57
+#define TARGET_NR_ulimit 58
+#define TARGET_NR_oldolduname 59
+#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_sgetmask 68
+#define TARGET_NR_ssetmask 69
+#define TARGET_NR_setreuid 70
+#define TARGET_NR_setregid 71
+#define TARGET_NR_sigsuspend 72
+#define TARGET_NR_sigpending 73
+#define TARGET_NR_sethostname 74
+#define TARGET_NR_setrlimit 75
+#define TARGET_NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */
+#define TARGET_NR_getrusage 77
+#define TARGET_NR_gettimeofday 78
+#define TARGET_NR_settimeofday 79
+#define TARGET_NR_getgroups 80
+#define TARGET_NR_setgroups 81
+#define TARGET_NR_select 82
+#define TARGET_NR_symlink 83
+#define TARGET_NR_oldlstat 84
+#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_fchown 95
+#define TARGET_NR_getpriority 96
+#define TARGET_NR_setpriority 97
+#define TARGET_NR_profil 98
+#define TARGET_NR_statfs 99
+#define TARGET_NR_fstatfs 100
+#define TARGET_NR_ioperm 101
+#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_olduname 109
+#define TARGET_NR_iopl 110
+#define TARGET_NR_vhangup 111
+#define TARGET_NR_idle 112
+#define TARGET_NR_vm86old 113
+#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_modify_ldt 123
+#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_setfsuid 138
+#define TARGET_NR_setfsgid 139
+#define TARGET_NR__llseek 140
+#define TARGET_NR_getdents 141
+#define TARGET_NR__newselect 142
+#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_setresuid 164
+#define TARGET_NR_getresuid 165
+#define TARGET_NR_vm86 166
+#define TARGET_NR_query_module 167
+#define TARGET_NR_poll 168
+#define TARGET_NR_nfsservctl 169
+#define TARGET_NR_setresgid 170
+#define TARGET_NR_getresgid 171
+#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_chown 182
+#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_streams1 188 /* some people actually want it */
+#define TARGET_NR_streams2 189 /* some people actually want it */
+#define TARGET_NR_vfork 190
+#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_pivot_root 217
+#define TARGET_NR_mincore 218
+#define TARGET_NR_madvise 219
+#define TARGET_NR_getdents64 220
+#define TARGET_NR_fcntl64 221
+/* 223 is unused */
+#define TARGET_NR_gettid 224
+#define TARGET_NR_readahead 225
+#define TARGET_NR_setxattr 226
+#define TARGET_NR_lsetxattr 227
+#define TARGET_NR_fsetxattr 228
+#define TARGET_NR_getxattr 229
+#define TARGET_NR_lgetxattr 230
+#define TARGET_NR_fgetxattr 231
+#define TARGET_NR_listxattr 232
+#define TARGET_NR_llistxattr 233
+#define TARGET_NR_flistxattr 234
+#define TARGET_NR_removexattr 235
+#define TARGET_NR_lremovexattr 236
+#define TARGET_NR_fremovexattr 237
+#define TARGET_NR_tkill 238
+#define TARGET_NR_sendfile64 239
+#define TARGET_NR_futex 240
+#define TARGET_NR_sched_setaffinity 241
+#define TARGET_NR_sched_getaffinity 242
+#define TARGET_NR_set_thread_area 243
+#define TARGET_NR_get_thread_area 244
+#define TARGET_NR_io_setup 245
+#define TARGET_NR_io_destroy 246
+#define TARGET_NR_io_getevents 247
+#define TARGET_NR_io_submit 248
+#define TARGET_NR_io_cancel 249
+#define TARGET_NR_fadvise64 250
+
+#define TARGET_NR_exit_group 252
+#define TARGET_NR_lookup_dcookie 253
+#define TARGET_NR_epoll_create 254
+#define TARGET_NR_epoll_ctl 255
+#define TARGET_NR_epoll_wait 256
+#define TARGET_NR_remap_file_pages 257
+#define TARGET_NR_set_tid_address 258
+#define TARGET_NR_timer_create 259
+#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)
+#define TARGET_NR_statfs64 268
+#define TARGET_NR_fstatfs64 269
+#define TARGET_NR_tgkill 270
+#define TARGET_NR_utimes 271
+#define TARGET_NR_fadvise64_64 272
+#define TARGET_NR_vserver 273
+#define TARGET_NR_mbind 274
+#define TARGET_NR_get_mempolicy 275
+#define TARGET_NR_set_mempolicy 276
+#define TARGET_NR_mq_open 277
+#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1)
+#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2)
+#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3)
+#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4)
+#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5)
+#define TARGET_NR_sys_kexec_load 283
+#define TARGET_NR_waitid 284
+#define TARGET_NR_add_key 285
+#define TARGET_NR_request_key 286
+#define TARGET_NR_keyctl 287
+#define TARGET_NR_ioprio_set 288
+#define TARGET_NR_ioprio_get 289
+#define TARGET_NR_inotify_init 290
+#define TARGET_NR_inotify_add_watch 291
+#define TARGET_NR_inotify_rm_watch 292
+/* 293 is unused */
+#define TARGET_NR_migrate_pages 294
+#define TARGET_NR_openat 295
+#define TARGET_NR_mkdirat 296
+#define TARGET_NR_mknodat 297
+#define TARGET_NR_fchownat 298
+#define TARGET_NR_futimesat 299
+#define TARGET_NR_fstatat64 300
+#define TARGET_NR_unlinkat 301
+#define TARGET_NR_renameat 302
+#define TARGET_NR_linkat 303
+#define TARGET_NR_symlinkat 304
+#define TARGET_NR_readlinkat 305
+#define TARGET_NR_fchmodat 306
+#define TARGET_NR_faccessat 307
+#define TARGET_NR_pselect6 308
+#define TARGET_NR_ppoll 309
+#define TARGET_NR_unshare 310
+#define TARGET_NR_set_robust_list 311
+#define TARGET_NR_get_robust_list 312
+#define TARGET_NR_splice 313
+#define TARGET_NR_sync_file_range 314
+#define TARGET_NR_tee 315
+#define TARGET_NR_vmsplice 316
+#define TARGET_NR_move_pages 317
+#define TARGET_NR_getcpu 318
+#define TARGET_NR_epoll_pwait 319
+#define TARGET_NR_utimensat 320
+#define TARGET_NR_signalfd 321
+#define TARGET_NR_timerfd 322
+#define TARGET_NR_eventfd 323
+#define TARGET_NR_fallocate 324
+#define TARGET_NR_timerfd_settime 325
+#define TARGET_NR_timerfd_gettime 326
+#define TARGET_NR_signalfd4 327
+#define TARGET_NR_eventfd2 328
+#define TARGET_NR_epoll_create1 329
+#define TARGET_NR_dup3 330
+#define TARGET_NR_pipe2 331
+#define TARGET_NR_inotify_init1 332
diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h
new file mode 100644
index 0000000..e148da0
--- /dev/null
+++ b/linux-user/sh4/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long 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(CPUSH4State *state)
+{
+ return state->gregs[15];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/sh4/termbits.h b/linux-user/sh4/termbits.h
new file mode 100644
index 0000000..2ff774f
--- /dev/null
+++ b/linux-user/sh4/termbits.h
@@ -0,0 +1,274 @@
+/* from asm/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 */
+};
+
+/* 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_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 (not used) */
+#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 020000000000 /* flow control */
+
+/* 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_TARGET_TCSAFLUSH 2
+
+/* ioctl */
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+#define TARGET_FIOQSIZE TARGET_IOR('f', 128, loff_t)
+#define TARGET_TCGETS 0x5401
+#define TARGET_TCSETS 0x5402
+#define TARGET_TCSETSW 0x5403
+#define TARGET_TCSETSF 0x5404
+#define TARGET_TCGETA TARGET_IOR('t', 23, struct termio)
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+
+#define TARGET_TCSETA TARGET_IOW('t', 24, struct termio)
+#define TARGET_TCSETAW TARGET_IOW('t', 25, struct termio)
+#define TARGET_TCSETAF TARGET_IOW('t', 28, struct termio)
+#define TARGET_TCSBRK TARGET_IO('t', 29)
+#define TARGET_TCXONC TARGET_IO('t', 30)
+#define TARGET_TCFLSH TARGET_IO('t', 31)
+
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+#define TARGET_TIOCSTART TARGET_IO('t', 110) /* start output, like ^Q */
+#define TARGET_TIOCSTOP TARGET_IO('t', 111) /* stop output, like ^S */
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */
+
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int)
+#define TARGET_TIOCEXCL TARGET_IO('T', 12) /* 0x540C */
+#define TARGET_TIOCNXCL TARGET_IO('T', 13) /* 0x540D */
+#define TARGET_TIOCSCTTY TARGET_IO('T', 14) /* 0x540E */
+
+#define TARGET_TIOCSTI TARGET_IOW('T', 18, char) /* 0x5412 */
+#define TARGET_TIOCMGET TARGET_IOR('T', 21, unsigned int) /* 0x5415 */
+#define TARGET_TIOCMBIS TARGET_IOW('T', 22, unsigned int) /* 0x5416 */
+#define TARGET_TIOCMBIC TARGET_IOW('T', 23, unsigned int) /* 0x5417 */
+#define TARGET_TIOCMSET TARGET_IOW('T', 24, unsigned int) /* 0x5418 */
+#define TARGET_TIOCM_LE 0x001
+#define TARGET_TIOCM_DTR 0x002
+#define TARGET_TIOCM_RTS 0x004
+#define TARGET_TIOCM_ST 0x008
+#define TARGET_TIOCM_SR 0x010
+#define TARGET_TIOCM_CTS 0x020
+#define TARGET_TIOCM_CAR 0x040
+#define TARGET_TIOCM_RNG 0x080
+#define TARGET_TIOCM_DSR 0x100
+#define TARGET_TIOCM_CD TARGET_TIOCM_CAR
+#define TARGET_TIOCM_RI TARGET_TIOCM_RNG
+
+#define TARGET_TIOCGSOFTCAR TARGET_IOR('T', 25, unsigned int) /* 0x5419 */
+#define TARGET_TIOCSSOFTCAR TARGET_IOW('T', 26, unsigned int) /* 0x541A */
+#define TARGET_TIOCLINUX TARGET_IOW('T', 28, char) /* 0x541C */
+#define TARGET_TIOCCONS TARGET_IO('T', 29) /* 0x541D */
+#define TARGET_TIOCGSERIAL TARGET_IOR('T', 30, int) /* 0x541E */
+#define TARGET_TIOCSSERIAL TARGET_IOW('T', 31, int) /* 0x541F */
+#define TARGET_TIOCPKT TARGET_IOW('T', 32, int) /* 0x5420 */
+#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_TIOCNOTTY TARGET_IO('T', 34) /* 0x5422 */
+#define TARGET_TIOCSETD TARGET_IOW('T', 35, int) /* 0x5423 */
+#define TARGET_TIOCGETD TARGET_IOR('T', 36, int) /* 0x5424 */
+#define TARGET_TCSBRKP TARGET_IOW('T', 37, int) /* 0x5425 */ /* Needed for POSIX tcse
+ndbreak() */
+#define TARGET_TIOCSBRK TARGET_IO('T', 39) /* 0x5427 */ /* BSD compatibility */
+#define TARGET_TIOCCBRK TARGET_IO('T', 40) /* 0x5428 */ /* BSD compatibility */
+#define TARGET_TIOCGSID TARGET_IOR('T', 41, pid_t) /* 0x5429 */ /* Return the session
+ID of FD */
+#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-m
+ux device) */
+#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */
+
+
+#define TARGET_TIOCSERCONFIG TARGET_IO('T', 83) /* 0x5453 */
+#define TARGET_TIOCSERGWILD TARGET_IOR('T', 84, int) /* 0x5454 */
+#define TARGET_TIOCSERSWILD TARGET_IOW('T', 85, int) /* 0x5455 */
+#define TARGET_TIOCGLCKTRMIOS 0x5456
+#define TARGET_TIOCSLCKTRMIOS 0x5457
+#define TARGET_TIOCSERGSTRUCT TARGET_IOR('T', 88, int) /* 0x5458 */ /* For d
+ebugging only */
+#define TARGET_TIOCSERGETLSR TARGET_IOR('T', 89, unsigned int) /* 0x5459 */ /* Get line sta
+tus register */
+ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+# define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+#define TARGET_TIOCSERGETMULTI TARGET_IOR('T', 90, int) /* 0x545A
+*/ /* Get multiport config */
+#define TARGET_TIOCSERSETMULTI TARGET_IOW('T', 91, int) /* 0x545B
+*/ /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT TARGET_IO('T', 92) /* 0x545C */ /* wait for a change on
+serial input line(s) */
+#define TARGET_TIOCGICOUNT TARGET_IOR('T', 93, int) /* 0x545D */ /* read
+serial port inline interrupt counts */
diff --git a/linux-user/signal.c b/linux-user/signal.c
new file mode 100644
index 0000000..b01bd64
--- /dev/null
+++ b/linux-user/signal.c
@@ -0,0 +1,4981 @@
+/*
+ * Emulation of Linux signals
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/ucontext.h>
+#include <sys/resource.h>
+
+#include "qemu.h"
+#include "qemu-common.h"
+#include "target_signal.h"
+
+//#define DEBUG_SIGNAL
+
+static struct target_sigaltstack target_sigaltstack_used = {
+ .ss_sp = 0,
+ .ss_size = 0,
+ .ss_flags = TARGET_SS_DISABLE,
+};
+
+static struct target_sigaction sigact_table[TARGET_NSIG];
+
+static void host_signal_handler(int host_signum, siginfo_t *info,
+ void *puc);
+
+static uint8_t host_to_target_signal_table[_NSIG] = {
+ [SIGHUP] = TARGET_SIGHUP,
+ [SIGINT] = TARGET_SIGINT,
+ [SIGQUIT] = TARGET_SIGQUIT,
+ [SIGILL] = TARGET_SIGILL,
+ [SIGTRAP] = TARGET_SIGTRAP,
+ [SIGABRT] = TARGET_SIGABRT,
+/* [SIGIOT] = TARGET_SIGIOT,*/
+ [SIGBUS] = TARGET_SIGBUS,
+ [SIGFPE] = TARGET_SIGFPE,
+ [SIGKILL] = TARGET_SIGKILL,
+ [SIGUSR1] = TARGET_SIGUSR1,
+ [SIGSEGV] = TARGET_SIGSEGV,
+ [SIGUSR2] = TARGET_SIGUSR2,
+ [SIGPIPE] = TARGET_SIGPIPE,
+ [SIGALRM] = TARGET_SIGALRM,
+ [SIGTERM] = TARGET_SIGTERM,
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = TARGET_SIGSTKFLT,
+#endif
+ [SIGCHLD] = TARGET_SIGCHLD,
+ [SIGCONT] = TARGET_SIGCONT,
+ [SIGSTOP] = TARGET_SIGSTOP,
+ [SIGTSTP] = TARGET_SIGTSTP,
+ [SIGTTIN] = TARGET_SIGTTIN,
+ [SIGTTOU] = TARGET_SIGTTOU,
+ [SIGURG] = TARGET_SIGURG,
+ [SIGXCPU] = TARGET_SIGXCPU,
+ [SIGXFSZ] = TARGET_SIGXFSZ,
+ [SIGVTALRM] = TARGET_SIGVTALRM,
+ [SIGPROF] = TARGET_SIGPROF,
+ [SIGWINCH] = TARGET_SIGWINCH,
+ [SIGIO] = TARGET_SIGIO,
+ [SIGPWR] = TARGET_SIGPWR,
+ [SIGSYS] = TARGET_SIGSYS,
+ /* next signals stay the same */
+ /* Nasty hack: Reverse SIGRTMIN and SIGRTMAX to avoid overlap with
+ host libpthread signals. This assumes noone actually uses SIGRTMAX :-/
+ To fix this properly we need to do manual signal delivery multiplexed
+ over a single host signal. */
+ [__SIGRTMIN] = __SIGRTMAX,
+ [__SIGRTMAX] = __SIGRTMIN,
+};
+static uint8_t target_to_host_signal_table[_NSIG];
+
+static inline int on_sig_stack(unsigned long sp)
+{
+ return (sp - target_sigaltstack_used.ss_sp
+ < target_sigaltstack_used.ss_size);
+}
+
+static inline int sas_ss_flags(unsigned long sp)
+{
+ return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE
+ : on_sig_stack(sp) ? SS_ONSTACK : 0);
+}
+
+int host_to_target_signal(int sig)
+{
+ if (sig >= _NSIG)
+ return sig;
+ return host_to_target_signal_table[sig];
+}
+
+int target_to_host_signal(int sig)
+{
+ if (sig >= _NSIG)
+ return sig;
+ return target_to_host_signal_table[sig];
+}
+
+static inline void target_sigemptyset(target_sigset_t *set)
+{
+ memset(set, 0, sizeof(*set));
+}
+
+static inline void target_sigaddset(target_sigset_t *set, int signum)
+{
+ signum--;
+ abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW);
+ set->sig[signum / TARGET_NSIG_BPW] |= mask;
+}
+
+static inline int target_sigismember(const target_sigset_t *set, int signum)
+{
+ signum--;
+ abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW);
+ return ((set->sig[signum / TARGET_NSIG_BPW] & mask) != 0);
+}
+
+static void host_to_target_sigset_internal(target_sigset_t *d,
+ const sigset_t *s)
+{
+ int i;
+ target_sigemptyset(d);
+ for (i = 1; i <= TARGET_NSIG; i++) {
+ if (sigismember(s, i)) {
+ target_sigaddset(d, host_to_target_signal(i));
+ }
+ }
+}
+
+void host_to_target_sigset(target_sigset_t *d, const sigset_t *s)
+{
+ target_sigset_t d1;
+ int i;
+
+ host_to_target_sigset_internal(&d1, s);
+ for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ d->sig[i] = tswapl(d1.sig[i]);
+}
+
+static void target_to_host_sigset_internal(sigset_t *d,
+ const target_sigset_t *s)
+{
+ int i;
+ sigemptyset(d);
+ for (i = 1; i <= TARGET_NSIG; i++) {
+ if (target_sigismember(s, i)) {
+ sigaddset(d, target_to_host_signal(i));
+ }
+ }
+}
+
+void target_to_host_sigset(sigset_t *d, const target_sigset_t *s)
+{
+ target_sigset_t s1;
+ int i;
+
+ for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ s1.sig[i] = tswapl(s->sig[i]);
+ target_to_host_sigset_internal(d, &s1);
+}
+
+void host_to_target_old_sigset(abi_ulong *old_sigset,
+ const sigset_t *sigset)
+{
+ target_sigset_t d;
+ host_to_target_sigset(&d, sigset);
+ *old_sigset = d.sig[0];
+}
+
+void target_to_host_old_sigset(sigset_t *sigset,
+ const abi_ulong *old_sigset)
+{
+ target_sigset_t d;
+ int i;
+
+ d.sig[0] = *old_sigset;
+ for(i = 1;i < TARGET_NSIG_WORDS; i++)
+ d.sig[i] = 0;
+ target_to_host_sigset(sigset, &d);
+}
+
+/* siginfo conversion */
+
+static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
+ const siginfo_t *info)
+{
+ int sig;
+ sig = host_to_target_signal(info->si_signo);
+ tinfo->si_signo = sig;
+ tinfo->si_errno = 0;
+ tinfo->si_code = info->si_code;
+ if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV ||
+ sig == SIGBUS || sig == SIGTRAP) {
+ /* should never come here, but who knows. The information for
+ the target is irrelevant */
+ tinfo->_sifields._sigfault._addr = 0;
+ } else if (sig == SIGIO) {
+ tinfo->_sifields._sigpoll._fd = info->si_fd;
+ } else if (sig >= TARGET_SIGRTMIN) {
+ tinfo->_sifields._rt._pid = info->si_pid;
+ tinfo->_sifields._rt._uid = info->si_uid;
+ /* XXX: potential problem if 64 bit */
+ tinfo->_sifields._rt._sigval.sival_ptr =
+ (abi_ulong)(unsigned long)info->si_value.sival_ptr;
+ }
+}
+
+static void tswap_siginfo(target_siginfo_t *tinfo,
+ const target_siginfo_t *info)
+{
+ int sig;
+ sig = info->si_signo;
+ tinfo->si_signo = tswap32(sig);
+ tinfo->si_errno = tswap32(info->si_errno);
+ tinfo->si_code = tswap32(info->si_code);
+ if (sig == SIGILL || sig == SIGFPE || sig == SIGSEGV ||
+ sig == SIGBUS || sig == SIGTRAP) {
+ tinfo->_sifields._sigfault._addr =
+ tswapl(info->_sifields._sigfault._addr);
+ } else if (sig == SIGIO) {
+ tinfo->_sifields._sigpoll._fd = tswap32(info->_sifields._sigpoll._fd);
+ } else if (sig >= TARGET_SIGRTMIN) {
+ tinfo->_sifields._rt._pid = tswap32(info->_sifields._rt._pid);
+ tinfo->_sifields._rt._uid = tswap32(info->_sifields._rt._uid);
+ tinfo->_sifields._rt._sigval.sival_ptr =
+ tswapl(info->_sifields._rt._sigval.sival_ptr);
+ }
+}
+
+
+void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
+{
+ host_to_target_siginfo_noswap(tinfo, info);
+ tswap_siginfo(tinfo, tinfo);
+}
+
+/* XXX: we support only POSIX RT signals are used. */
+/* XXX: find a solution for 64 bit (additional malloced data is needed) */
+void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
+{
+ info->si_signo = tswap32(tinfo->si_signo);
+ info->si_errno = tswap32(tinfo->si_errno);
+ info->si_code = tswap32(tinfo->si_code);
+ info->si_pid = tswap32(tinfo->_sifields._rt._pid);
+ info->si_uid = tswap32(tinfo->_sifields._rt._uid);
+ info->si_value.sival_ptr =
+ (void *)(long)tswapl(tinfo->_sifields._rt._sigval.sival_ptr);
+}
+
+static int fatal_signal (int sig)
+{
+ switch (sig) {
+ case TARGET_SIGCHLD:
+ case TARGET_SIGURG:
+ case TARGET_SIGWINCH:
+ /* Ignored by default. */
+ return 0;
+ case TARGET_SIGCONT:
+ case TARGET_SIGSTOP:
+ case TARGET_SIGTSTP:
+ case TARGET_SIGTTIN:
+ case TARGET_SIGTTOU:
+ /* Job control signals. */
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+/* returns 1 if given signal should dump core if not handled */
+static int core_dump_signal(int sig)
+{
+ switch (sig) {
+ case TARGET_SIGABRT:
+ case TARGET_SIGFPE:
+ case TARGET_SIGILL:
+ case TARGET_SIGQUIT:
+ case TARGET_SIGSEGV:
+ case TARGET_SIGTRAP:
+ case TARGET_SIGBUS:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+void signal_init(void)
+{
+ struct sigaction act;
+ struct sigaction oact;
+ int i, j;
+ int host_sig;
+
+ /* generate signal conversion tables */
+ for(i = 1; i < _NSIG; i++) {
+ if (host_to_target_signal_table[i] == 0)
+ host_to_target_signal_table[i] = i;
+ }
+ for(i = 1; i < _NSIG; i++) {
+ j = host_to_target_signal_table[i];
+ target_to_host_signal_table[j] = i;
+ }
+
+ /* set all host signal handlers. ALL signals are blocked during
+ the handlers to serialize them. */
+ memset(sigact_table, 0, sizeof(sigact_table));
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = host_signal_handler;
+ for(i = 1; i <= TARGET_NSIG; i++) {
+ host_sig = target_to_host_signal(i);
+ sigaction(host_sig, NULL, &oact);
+ if (oact.sa_sigaction == (void *)SIG_IGN) {
+ sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
+ } else if (oact.sa_sigaction == (void *)SIG_DFL) {
+ sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL;
+ }
+ /* If there's already a handler installed then something has
+ gone horribly wrong, so don't even try to handle that case. */
+ /* Install some handlers for our own use. We need at least
+ SIGSEGV and SIGBUS, to detect exceptions. We can not just
+ trap all signals because it affects syscall interrupt
+ behavior. But do trap all default-fatal signals. */
+ if (fatal_signal (i))
+ sigaction(host_sig, &act, NULL);
+ }
+}
+
+/* signal queue handling */
+
+static inline struct sigqueue *alloc_sigqueue(CPUState *env)
+{
+ TaskState *ts = env->opaque;
+ struct sigqueue *q = ts->first_free;
+ if (!q)
+ return NULL;
+ ts->first_free = q->next;
+ return q;
+}
+
+static inline void free_sigqueue(CPUState *env, struct sigqueue *q)
+{
+ TaskState *ts = env->opaque;
+ q->next = ts->first_free;
+ ts->first_free = q;
+}
+
+/* abort execution with signal */
+static void QEMU_NORETURN force_sig(int target_sig)
+{
+ TaskState *ts = (TaskState *)thread_env->opaque;
+ int host_sig, core_dumped = 0;
+ struct sigaction act;
+ host_sig = target_to_host_signal(target_sig);
+ gdb_signalled(thread_env, target_sig);
+
+ /* dump core if supported by target binary format */
+ if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) {
+ stop_all_tasks();
+ core_dumped =
+ ((*ts->bprm->core_dump)(target_sig, thread_env) == 0);
+ }
+ if (core_dumped) {
+ /* we already dumped the core of target process, we don't want
+ * a coredump of qemu itself */
+ struct rlimit nodump;
+ getrlimit(RLIMIT_CORE, &nodump);
+ nodump.rlim_cur=0;
+ setrlimit(RLIMIT_CORE, &nodump);
+ (void) fprintf(stderr, "qemu: uncaught target signal %d (%s) - %s\n",
+ target_sig, strsignal(host_sig), "core dumped" );
+ }
+
+ /* The proper exit code for dieing from an uncaught signal is
+ * -<signal>. The kernel doesn't allow exit() or _exit() to pass
+ * a negative value. To get the proper exit code we need to
+ * actually die from an uncaught signal. Here the default signal
+ * handler is installed, we send ourself a signal and we wait for
+ * it to arrive. */
+ sigfillset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+ sigaction(host_sig, &act, NULL);
+
+ /* For some reason raise(host_sig) doesn't send the signal when
+ * statically linked on x86-64. */
+ kill(getpid(), host_sig);
+
+ /* Make sure the signal isn't masked (just reuse the mask inside
+ of act) */
+ sigdelset(&act.sa_mask, host_sig);
+ sigsuspend(&act.sa_mask);
+
+ /* unreachable */
+ abort();
+}
+
+/* queue a signal so that it will be send to the virtual CPU as soon
+ as possible */
+int queue_signal(CPUState *env, int sig, target_siginfo_t *info)
+{
+ TaskState *ts = env->opaque;
+ struct emulated_sigtable *k;
+ struct sigqueue *q, **pq;
+ abi_ulong handler;
+ int queue;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "queue_signal: sig=%d\n",
+ sig);
+#endif
+ k = &ts->sigtab[sig - 1];
+ queue = gdb_queuesig ();
+ handler = sigact_table[sig - 1]._sa_handler;
+ if (!queue && handler == TARGET_SIG_DFL) {
+ if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) {
+ kill(getpid(),SIGSTOP);
+ return 0;
+ } else
+ /* default handler : ignore some signal. The other are fatal */
+ if (sig != TARGET_SIGCHLD &&
+ sig != TARGET_SIGURG &&
+ sig != TARGET_SIGWINCH &&
+ sig != TARGET_SIGCONT) {
+ force_sig(sig);
+ } else {
+ return 0; /* indicate ignored */
+ }
+ } else if (!queue && handler == TARGET_SIG_IGN) {
+ /* ignore signal */
+ return 0;
+ } else if (!queue && handler == TARGET_SIG_ERR) {
+ force_sig(sig);
+ } else {
+ pq = &k->first;
+ if (sig < TARGET_SIGRTMIN) {
+ /* if non real time signal, we queue exactly one signal */
+ if (!k->pending)
+ q = &k->info;
+ else
+ return 0;
+ } else {
+ if (!k->pending) {
+ /* first signal */
+ q = &k->info;
+ } else {
+ q = alloc_sigqueue(env);
+ if (!q)
+ return -EAGAIN;
+ while (*pq != NULL)
+ pq = &(*pq)->next;
+ }
+ }
+ *pq = q;
+ q->info = *info;
+ q->next = NULL;
+ k->pending = 1;
+ /* signal that a new signal is pending */
+ ts->signal_pending = 1;
+ return 1; /* indicates that the signal was queued */
+ }
+}
+
+static void host_signal_handler(int host_signum, siginfo_t *info,
+ void *puc)
+{
+ int sig;
+ target_siginfo_t tinfo;
+
+ /* the CPU emulator uses some host signals to detect exceptions,
+ we forward to it some signals */
+ if ((host_signum == SIGSEGV || host_signum == SIGBUS)
+ && info->si_code > 0) {
+ if (cpu_signal_handler(host_signum, info, puc))
+ return;
+ }
+
+ /* get target signal number */
+ sig = host_to_target_signal(host_signum);
+ if (sig < 1 || sig > TARGET_NSIG)
+ return;
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "qemu: got signal %d\n", sig);
+#endif
+ host_to_target_siginfo_noswap(&tinfo, info);
+ if (queue_signal(thread_env, sig, &tinfo) == 1) {
+ /* interrupt the virtual CPU as soon as possible */
+ cpu_exit(thread_env);
+ }
+}
+
+/* do_sigaltstack() returns target values and errnos. */
+/* compare linux/kernel/signal.c:do_sigaltstack() */
+abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
+{
+ int ret;
+ struct target_sigaltstack oss;
+
+ /* XXX: test errors */
+ if(uoss_addr)
+ {
+ __put_user(target_sigaltstack_used.ss_sp, &oss.ss_sp);
+ __put_user(target_sigaltstack_used.ss_size, &oss.ss_size);
+ __put_user(sas_ss_flags(sp), &oss.ss_flags);
+ }
+
+ if(uss_addr)
+ {
+ struct target_sigaltstack *uss;
+ struct target_sigaltstack ss;
+
+ ret = -TARGET_EFAULT;
+ if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)
+ || __get_user(ss.ss_sp, &uss->ss_sp)
+ || __get_user(ss.ss_size, &uss->ss_size)
+ || __get_user(ss.ss_flags, &uss->ss_flags))
+ goto out;
+ unlock_user_struct(uss, uss_addr, 0);
+
+ ret = -TARGET_EPERM;
+ if (on_sig_stack(sp))
+ goto out;
+
+ ret = -TARGET_EINVAL;
+ if (ss.ss_flags != TARGET_SS_DISABLE
+ && ss.ss_flags != TARGET_SS_ONSTACK
+ && ss.ss_flags != 0)
+ goto out;
+
+ if (ss.ss_flags == TARGET_SS_DISABLE) {
+ ss.ss_size = 0;
+ ss.ss_sp = 0;
+ } else {
+ ret = -TARGET_ENOMEM;
+ if (ss.ss_size < MINSIGSTKSZ)
+ goto out;
+ }
+
+ target_sigaltstack_used.ss_sp = ss.ss_sp;
+ target_sigaltstack_used.ss_size = ss.ss_size;
+ }
+
+ if (uoss_addr) {
+ ret = -TARGET_EFAULT;
+ if (copy_to_user(uoss_addr, &oss, sizeof(oss)))
+ goto out;
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/* do_sigaction() return host values and errnos */
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact)
+{
+ struct target_sigaction *k;
+ struct sigaction act1;
+ int host_sig;
+ int ret = 0;
+
+ if (sig < 1 || sig > TARGET_NSIG || sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP)
+ return -EINVAL;
+ k = &sigact_table[sig - 1];
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "sigaction sig=%d act=0x%p, oact=0x%p\n",
+ sig, act, oact);
+#endif
+ if (oact) {
+ oact->_sa_handler = tswapl(k->_sa_handler);
+ oact->sa_flags = tswapl(k->sa_flags);
+#if !defined(TARGET_MIPS)
+ oact->sa_restorer = tswapl(k->sa_restorer);
+#endif
+ oact->sa_mask = k->sa_mask;
+ }
+ if (act) {
+ /* FIXME: This is not threadsafe. */
+ k->_sa_handler = tswapl(act->_sa_handler);
+ k->sa_flags = tswapl(act->sa_flags);
+#if !defined(TARGET_MIPS)
+ k->sa_restorer = tswapl(act->sa_restorer);
+#endif
+ k->sa_mask = act->sa_mask;
+
+ /* we update the host linux signal state */
+ host_sig = target_to_host_signal(sig);
+ if (host_sig != SIGSEGV && host_sig != SIGBUS) {
+ sigfillset(&act1.sa_mask);
+ act1.sa_flags = SA_SIGINFO;
+ if (k->sa_flags & TARGET_SA_RESTART)
+ act1.sa_flags |= SA_RESTART;
+ /* NOTE: it is important to update the host kernel signal
+ ignore state to avoid getting unexpected interrupted
+ syscalls */
+ if (k->_sa_handler == TARGET_SIG_IGN) {
+ act1.sa_sigaction = (void *)SIG_IGN;
+ } else if (k->_sa_handler == TARGET_SIG_DFL) {
+ if (fatal_signal (sig))
+ act1.sa_sigaction = host_signal_handler;
+ else
+ act1.sa_sigaction = (void *)SIG_DFL;
+ } else {
+ act1.sa_sigaction = host_signal_handler;
+ }
+ ret = sigaction(host_sig, &act1, NULL);
+ }
+ }
+ return ret;
+}
+
+static inline int copy_siginfo_to_user(target_siginfo_t *tinfo,
+ const target_siginfo_t *info)
+{
+ tswap_siginfo(tinfo, info);
+ return 0;
+}
+
+static inline int current_exec_domain_sig(int sig)
+{
+ return /* current->exec_domain && current->exec_domain->signal_invmap
+ && sig < 32 ? current->exec_domain->signal_invmap[sig] : */ sig;
+}
+
+#if defined(TARGET_I386) && TARGET_ABI_BITS == 32
+
+/* from the Linux kernel */
+
+struct target_fpreg {
+ uint16_t significand[4];
+ uint16_t exponent;
+};
+
+struct target_fpxreg {
+ uint16_t significand[4];
+ uint16_t exponent;
+ uint16_t padding[3];
+};
+
+struct target_xmmreg {
+ abi_ulong element[4];
+};
+
+struct target_fpstate {
+ /* Regular FPU environment */
+ abi_ulong cw;
+ abi_ulong sw;
+ abi_ulong tag;
+ abi_ulong ipoff;
+ abi_ulong cssel;
+ abi_ulong dataoff;
+ abi_ulong datasel;
+ struct target_fpreg _st[8];
+ uint16_t status;
+ uint16_t magic; /* 0xffff = regular FPU data only */
+
+ /* FXSR FPU environment */
+ abi_ulong _fxsr_env[6]; /* FXSR FPU env is ignored */
+ abi_ulong mxcsr;
+ abi_ulong reserved;
+ struct target_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */
+ struct target_xmmreg _xmm[8];
+ abi_ulong padding[56];
+};
+
+#define X86_FXSR_MAGIC 0x0000
+
+struct target_sigcontext {
+ uint16_t gs, __gsh;
+ uint16_t fs, __fsh;
+ uint16_t es, __esh;
+ uint16_t ds, __dsh;
+ abi_ulong edi;
+ abi_ulong esi;
+ abi_ulong ebp;
+ abi_ulong esp;
+ abi_ulong ebx;
+ abi_ulong edx;
+ abi_ulong ecx;
+ abi_ulong eax;
+ abi_ulong trapno;
+ abi_ulong err;
+ abi_ulong eip;
+ uint16_t cs, __csh;
+ abi_ulong eflags;
+ abi_ulong esp_at_signal;
+ uint16_t ss, __ssh;
+ abi_ulong fpstate; /* pointer */
+ abi_ulong oldmask;
+ abi_ulong cr2;
+};
+
+struct target_ucontext {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
+};
+
+struct sigframe
+{
+ abi_ulong pretcode;
+ int sig;
+ struct target_sigcontext sc;
+ struct target_fpstate fpstate;
+ abi_ulong extramask[TARGET_NSIG_WORDS-1];
+ char retcode[8];
+};
+
+struct rt_sigframe
+{
+ abi_ulong pretcode;
+ int sig;
+ abi_ulong pinfo;
+ abi_ulong puc;
+ struct target_siginfo info;
+ struct target_ucontext uc;
+ struct target_fpstate fpstate;
+ char retcode[8];
+};
+
+/*
+ * Set up a signal frame.
+ */
+
+/* XXX: save x87 state */
+static int
+setup_sigcontext(struct target_sigcontext *sc, struct target_fpstate *fpstate,
+ CPUX86State *env, abi_ulong mask, abi_ulong fpstate_addr)
+{
+ int err = 0;
+ uint16_t magic;
+
+ /* already locked in setup_frame() */
+ err |= __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
+ err |= __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs);
+ err |= __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es);
+ err |= __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds);
+ err |= __put_user(env->regs[R_EDI], &sc->edi);
+ err |= __put_user(env->regs[R_ESI], &sc->esi);
+ err |= __put_user(env->regs[R_EBP], &sc->ebp);
+ err |= __put_user(env->regs[R_ESP], &sc->esp);
+ err |= __put_user(env->regs[R_EBX], &sc->ebx);
+ err |= __put_user(env->regs[R_EDX], &sc->edx);
+ err |= __put_user(env->regs[R_ECX], &sc->ecx);
+ err |= __put_user(env->regs[R_EAX], &sc->eax);
+ err |= __put_user(env->exception_index, &sc->trapno);
+ err |= __put_user(env->error_code, &sc->err);
+ err |= __put_user(env->eip, &sc->eip);
+ err |= __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
+ err |= __put_user(env->eflags, &sc->eflags);
+ err |= __put_user(env->regs[R_ESP], &sc->esp_at_signal);
+ err |= __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss);
+
+ cpu_x86_fsave(env, fpstate_addr, 1);
+ fpstate->status = fpstate->sw;
+ magic = 0xffff;
+ err |= __put_user(magic, &fpstate->magic);
+ err |= __put_user(fpstate_addr, &sc->fpstate);
+
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+ err |= __put_user(env->cr[2], &sc->cr2);
+ return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+
+static inline abi_ulong
+get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size)
+{
+ unsigned long esp;
+
+ /* Default to using normal stack */
+ esp = env->regs[R_ESP];
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa_flags & TARGET_SA_ONSTACK) {
+ if (sas_ss_flags(esp) == 0)
+ esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+
+ /* This is the legacy signal stack switching. */
+ else
+ if ((env->segs[R_SS].selector & 0xffff) != __USER_DS &&
+ !(ka->sa_flags & TARGET_SA_RESTORER) &&
+ ka->sa_restorer) {
+ esp = (unsigned long) ka->sa_restorer;
+ }
+ return (esp - frame_size) & -8ul;
+}
+
+/* compare linux/arch/i386/kernel/signal.c:setup_frame() */
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUX86State *env)
+{
+ abi_ulong frame_addr;
+ struct sigframe *frame;
+ int i, err = 0;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ err |= __put_user(current_exec_domain_sig(sig),
+ &frame->sig);
+ if (err)
+ goto give_sigsegv;
+
+ setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0],
+ frame_addr + offsetof(struct sigframe, fpstate));
+ if (err)
+ goto give_sigsegv;
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ goto give_sigsegv;
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ err |= __put_user(ka->sa_restorer, &frame->pretcode);
+ } else {
+ uint16_t val16;
+ abi_ulong retcode_addr;
+ retcode_addr = frame_addr + offsetof(struct sigframe, retcode);
+ err |= __put_user(retcode_addr, &frame->pretcode);
+ /* This is popl %eax ; movl $,%eax ; int $0x80 */
+ val16 = 0xb858;
+ err |= __put_user(val16, (uint16_t *)(frame->retcode+0));
+ err |= __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2));
+ val16 = 0x80cd;
+ err |= __put_user(val16, (uint16_t *)(frame->retcode+6));
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ env->regs[R_ESP] = frame_addr;
+ env->eip = ka->_sa_handler;
+
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ env->eflags &= ~TF_MASK;
+
+ unlock_user_struct(frame, frame_addr, 1);
+
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ if (sig == TARGET_SIGSEGV)
+ ka->_sa_handler = TARGET_SIG_DFL;
+ force_sig(TARGET_SIGSEGV /* , current */);
+}
+
+/* compare linux/arch/i386/kernel/signal.c:setup_rt_frame() */
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUX86State *env)
+{
+ abi_ulong frame_addr, addr;
+ struct rt_sigframe *frame;
+ int i, err = 0;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ err |= __put_user(current_exec_domain_sig(sig),
+ &frame->sig);
+ addr = frame_addr + offsetof(struct rt_sigframe, info);
+ err |= __put_user(addr, &frame->pinfo);
+ addr = frame_addr + offsetof(struct rt_sigframe, uc);
+ err |= __put_user(addr, &frame->puc);
+ err |= copy_siginfo_to_user(&frame->info, info);
+ if (err)
+ goto give_sigsegv;
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.tuc_flags);
+ err |= __put_user(0, &frame->uc.tuc_link);
+ err |= __put_user(target_sigaltstack_used.ss_sp,
+ &frame->uc.tuc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(get_sp_from_cpustate(env)),
+ &frame->uc.tuc_stack.ss_flags);
+ err |= __put_user(target_sigaltstack_used.ss_size,
+ &frame->uc.tuc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate,
+ env, set->sig[0],
+ frame_addr + offsetof(struct rt_sigframe, fpstate));
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]))
+ goto give_sigsegv;
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ err |= __put_user(ka->sa_restorer, &frame->pretcode);
+ } else {
+ uint16_t val16;
+ addr = frame_addr + offsetof(struct rt_sigframe, retcode);
+ err |= __put_user(addr, &frame->pretcode);
+ /* This is movl $,%eax ; int $0x80 */
+ err |= __put_user(0xb8, (char *)(frame->retcode+0));
+ err |= __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1));
+ val16 = 0x80cd;
+ err |= __put_user(val16, (uint16_t *)(frame->retcode+5));
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ env->regs[R_ESP] = frame_addr;
+ env->eip = ka->_sa_handler;
+
+ cpu_x86_load_seg(env, R_DS, __USER_DS);
+ cpu_x86_load_seg(env, R_ES, __USER_DS);
+ cpu_x86_load_seg(env, R_SS, __USER_DS);
+ cpu_x86_load_seg(env, R_CS, __USER_CS);
+ env->eflags &= ~TF_MASK;
+
+ unlock_user_struct(frame, frame_addr, 1);
+
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ if (sig == TARGET_SIGSEGV)
+ ka->_sa_handler = TARGET_SIG_DFL;
+ force_sig(TARGET_SIGSEGV /* , current */);
+}
+
+static int
+restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax)
+{
+ unsigned int err = 0;
+ abi_ulong fpstate_addr;
+ unsigned int tmpflags;
+
+ cpu_x86_load_seg(env, R_GS, tswap16(sc->gs));
+ cpu_x86_load_seg(env, R_FS, tswap16(sc->fs));
+ cpu_x86_load_seg(env, R_ES, tswap16(sc->es));
+ cpu_x86_load_seg(env, R_DS, tswap16(sc->ds));
+
+ env->regs[R_EDI] = tswapl(sc->edi);
+ env->regs[R_ESI] = tswapl(sc->esi);
+ env->regs[R_EBP] = tswapl(sc->ebp);
+ env->regs[R_ESP] = tswapl(sc->esp);
+ env->regs[R_EBX] = tswapl(sc->ebx);
+ env->regs[R_EDX] = tswapl(sc->edx);
+ env->regs[R_ECX] = tswapl(sc->ecx);
+ env->eip = tswapl(sc->eip);
+
+ cpu_x86_load_seg(env, R_CS, lduw(&sc->cs) | 3);
+ cpu_x86_load_seg(env, R_SS, lduw(&sc->ss) | 3);
+
+ tmpflags = tswapl(sc->eflags);
+ env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5);
+ // regs->orig_eax = -1; /* disable syscall checks */
+
+ fpstate_addr = tswapl(sc->fpstate);
+ if (fpstate_addr != 0) {
+ if (!access_ok(VERIFY_READ, fpstate_addr,
+ sizeof(struct target_fpstate)))
+ goto badframe;
+ cpu_x86_frstor(env, fpstate_addr, 1);
+ }
+
+ *peax = tswapl(sc->eax);
+ return err;
+badframe:
+ return 1;
+}
+
+long do_sigreturn(CPUX86State *env)
+{
+ struct sigframe *frame;
+ abi_ulong frame_addr = env->regs[R_ESP] - 8;
+ target_sigset_t target_set;
+ sigset_t set;
+ int eax, i;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_sigreturn\n");
+#endif
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+ /* set blocked signals */
+ if (__get_user(target_set.sig[0], &frame->sc.oldmask))
+ goto badframe;
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ /* restore registers */
+ if (restore_sigcontext(env, &frame->sc, &eax))
+ goto badframe;
+ unlock_user_struct(frame, frame_addr, 0);
+ return eax;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUX86State *env)
+{
+ abi_ulong frame_addr;
+ struct rt_sigframe *frame;
+ sigset_t set;
+ int eax;
+
+ frame_addr = env->regs[R_ESP] - 4;
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+ target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ if (restore_sigcontext(env, &frame->uc.tuc_mcontext, &eax))
+ goto badframe;
+
+ if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0,
+ get_sp_from_cpustate(env)) == -EFAULT)
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return eax;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+#elif defined(TARGET_ARM)
+
+struct target_sigcontext {
+ abi_ulong trap_no;
+ abi_ulong error_code;
+ abi_ulong oldmask;
+ abi_ulong arm_r0;
+ abi_ulong arm_r1;
+ abi_ulong arm_r2;
+ abi_ulong arm_r3;
+ abi_ulong arm_r4;
+ abi_ulong arm_r5;
+ abi_ulong arm_r6;
+ abi_ulong arm_r7;
+ abi_ulong arm_r8;
+ abi_ulong arm_r9;
+ abi_ulong arm_r10;
+ abi_ulong arm_fp;
+ abi_ulong arm_ip;
+ abi_ulong arm_sp;
+ abi_ulong arm_lr;
+ abi_ulong arm_pc;
+ abi_ulong arm_cpsr;
+ abi_ulong fault_address;
+};
+
+struct target_ucontext_v1 {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
+};
+
+struct target_ucontext_v2 {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
+ char __unused[128 - sizeof(target_sigset_t)];
+ abi_ulong tuc_regspace[128] __attribute__((__aligned__(8)));
+};
+
+struct target_user_vfp {
+ uint64_t fpregs[32];
+ abi_ulong fpscr;
+};
+
+struct target_user_vfp_exc {
+ abi_ulong fpexc;
+ abi_ulong fpinst;
+ abi_ulong fpinst2;
+};
+
+struct target_vfp_sigframe {
+ abi_ulong magic;
+ abi_ulong size;
+ struct target_user_vfp ufp;
+ struct target_user_vfp_exc ufp_exc;
+} __attribute__((__aligned__(8)));
+
+struct target_iwmmxt_sigframe {
+ abi_ulong magic;
+ abi_ulong size;
+ uint64_t regs[16];
+ /* Note that not all the coprocessor control registers are stored here */
+ uint32_t wcssf;
+ uint32_t wcasf;
+ uint32_t wcgr0;
+ uint32_t wcgr1;
+ uint32_t wcgr2;
+ uint32_t wcgr3;
+} __attribute__((__aligned__(8)));
+
+#define TARGET_VFP_MAGIC 0x56465001
+#define TARGET_IWMMXT_MAGIC 0x12ef842a
+
+struct sigframe_v1
+{
+ struct target_sigcontext sc;
+ abi_ulong extramask[TARGET_NSIG_WORDS-1];
+ abi_ulong retcode;
+};
+
+struct sigframe_v2
+{
+ struct target_ucontext_v2 uc;
+ abi_ulong retcode;
+};
+
+struct rt_sigframe_v1
+{
+ abi_ulong pinfo;
+ abi_ulong puc;
+ struct target_siginfo info;
+ struct target_ucontext_v1 uc;
+ abi_ulong retcode;
+};
+
+struct rt_sigframe_v2
+{
+ struct target_siginfo info;
+ struct target_ucontext_v2 uc;
+ abi_ulong retcode;
+};
+
+#define TARGET_CONFIG_CPU_32 1
+
+/*
+ * For ARM syscalls, we encode the syscall number into the instruction.
+ */
+#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE))
+#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE))
+
+/*
+ * For Thumb syscalls, we pass the syscall number via r7. We therefore
+ * need two 16-bit instructions.
+ */
+#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn))
+#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn))
+
+static const abi_ulong retcodes[4] = {
+ SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN,
+ SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN
+};
+
+
+#define __get_user_error(x,p,e) __get_user(x, p)
+
+static inline int valid_user_regs(CPUState *regs)
+{
+ return 1;
+}
+
+static void
+setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
+ CPUState *env, abi_ulong mask)
+{
+ __put_user(env->regs[0], &sc->arm_r0);
+ __put_user(env->regs[1], &sc->arm_r1);
+ __put_user(env->regs[2], &sc->arm_r2);
+ __put_user(env->regs[3], &sc->arm_r3);
+ __put_user(env->regs[4], &sc->arm_r4);
+ __put_user(env->regs[5], &sc->arm_r5);
+ __put_user(env->regs[6], &sc->arm_r6);
+ __put_user(env->regs[7], &sc->arm_r7);
+ __put_user(env->regs[8], &sc->arm_r8);
+ __put_user(env->regs[9], &sc->arm_r9);
+ __put_user(env->regs[10], &sc->arm_r10);
+ __put_user(env->regs[11], &sc->arm_fp);
+ __put_user(env->regs[12], &sc->arm_ip);
+ __put_user(env->regs[13], &sc->arm_sp);
+ __put_user(env->regs[14], &sc->arm_lr);
+ __put_user(env->regs[15], &sc->arm_pc);
+#ifdef TARGET_CONFIG_CPU_32
+ __put_user(cpsr_read(env), &sc->arm_cpsr);
+#endif
+
+ __put_user(/* current->thread.trap_no */ 0, &sc->trap_no);
+ __put_user(/* current->thread.error_code */ 0, &sc->error_code);
+ __put_user(/* current->thread.address */ 0, &sc->fault_address);
+ __put_user(mask, &sc->oldmask);
+}
+
+static inline abi_ulong
+get_sigframe(struct target_sigaction *ka, CPUState *regs, int framesize)
+{
+ unsigned long sp = regs->regs[13];
+
+ /*
+ * This is the X/Open sanctioned signal stack switching.
+ */
+ if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp))
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ /*
+ * ATPCS B01 mandates 8-byte alignment
+ */
+ return (sp - framesize) & ~7;
+}
+
+static int
+setup_return(CPUState *env, struct target_sigaction *ka,
+ abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr)
+{
+ abi_ulong handler = ka->_sa_handler;
+ abi_ulong retcode;
+ int thumb = handler & 1;
+ uint32_t cpsr = cpsr_read(env);
+
+ cpsr &= ~CPSR_IT;
+ if (thumb) {
+ cpsr |= CPSR_T;
+ } else {
+ cpsr &= ~CPSR_T;
+ }
+
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ retcode = ka->sa_restorer;
+ } else {
+ unsigned int idx = thumb;
+
+ if (ka->sa_flags & TARGET_SA_SIGINFO)
+ idx += 2;
+
+ if (__put_user(retcodes[idx], rc))
+ return 1;
+#if 0
+ flush_icache_range((abi_ulong)rc,
+ (abi_ulong)(rc + 1));
+#endif
+ retcode = rc_addr + thumb;
+ }
+
+ env->regs[0] = usig;
+ env->regs[13] = frame_addr;
+ env->regs[14] = retcode;
+ env->regs[15] = handler & (thumb ? ~1 : ~3);
+ cpsr_write(env, cpsr, 0xffffffff);
+
+ return 0;
+}
+
+static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUState *env)
+{
+ int i;
+ struct target_vfp_sigframe *vfpframe;
+ vfpframe = (struct target_vfp_sigframe *)regspace;
+ __put_user(TARGET_VFP_MAGIC, &vfpframe->magic);
+ __put_user(sizeof(*vfpframe), &vfpframe->size);
+ for (i = 0; i < 32; i++) {
+ __put_user(env->vfp.regs[i], &vfpframe->ufp.fpregs[i]);
+ }
+ __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr);
+ __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc);
+ __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst);
+ __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2);
+ return (abi_ulong*)(vfpframe+1);
+}
+
+static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, CPUState *env)
+{
+ int i;
+ struct target_iwmmxt_sigframe *iwmmxtframe;
+ iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace;
+ __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic);
+ __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size);
+ for (i = 0; i < 16; i++) {
+ __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]);
+ }
+ __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf);
+ __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf);
+ __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0);
+ __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1);
+ __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2);
+ __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3);
+ return (abi_ulong*)(iwmmxtframe+1);
+}
+
+static void setup_sigframe_v2(struct target_ucontext_v2 *uc,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_sigaltstack stack;
+ int i;
+ abi_ulong *regspace;
+
+ /* Clear all the bits of the ucontext we don't use. */
+ memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext));
+
+ memset(&stack, 0, sizeof(stack));
+ __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp);
+ __put_user(target_sigaltstack_used.ss_size, &stack.ss_size);
+ __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags);
+ memcpy(&uc->tuc_stack, &stack, sizeof(stack));
+
+ setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]);
+ /* Save coprocessor signal frame. */
+ regspace = uc->tuc_regspace;
+ if (arm_feature(env, ARM_FEATURE_VFP)) {
+ regspace = setup_sigframe_v2_vfp(regspace, env);
+ }
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ regspace = setup_sigframe_v2_iwmmxt(regspace, env);
+ }
+
+ /* Write terminating magic word */
+ __put_user(0, regspace);
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]);
+ }
+}
+
+/* compare linux/arch/arm/kernel/signal.c:setup_frame() */
+static void setup_frame_v1(int usig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ struct sigframe_v1 *frame;
+ abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame));
+ int i;
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ return;
+
+ setup_sigcontext(&frame->sc, regs, set->sig[0]);
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ goto end;
+ }
+
+ setup_return(regs, ka, &frame->retcode, frame_addr, usig,
+ frame_addr + offsetof(struct sigframe_v1, retcode));
+
+end:
+ unlock_user_struct(frame, frame_addr, 1);
+}
+
+static void setup_frame_v2(int usig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ struct sigframe_v2 *frame;
+ abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ return;
+
+ setup_sigframe_v2(&frame->uc, set, regs);
+
+ setup_return(regs, ka, &frame->retcode, frame_addr, usig,
+ frame_addr + offsetof(struct sigframe_v2, retcode));
+
+ unlock_user_struct(frame, frame_addr, 1);
+}
+
+static void setup_frame(int usig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ if (get_osversion() >= 0x020612) {
+ setup_frame_v2(usig, ka, set, regs);
+ } else {
+ setup_frame_v1(usig, ka, set, regs);
+ }
+}
+
+/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */
+static void setup_rt_frame_v1(int usig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ struct rt_sigframe_v1 *frame;
+ abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ struct target_sigaltstack stack;
+ int i;
+ abi_ulong info_addr, uc_addr;
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ return /* 1 */;
+
+ info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info);
+ __put_user(info_addr, &frame->pinfo);
+ uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc);
+ __put_user(uc_addr, &frame->puc);
+ copy_siginfo_to_user(&frame->info, info);
+
+ /* Clear all the bits of the ucontext we don't use. */
+ memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext));
+
+ memset(&stack, 0, sizeof(stack));
+ __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp);
+ __put_user(target_sigaltstack_used.ss_size, &stack.ss_size);
+ __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags);
+ memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack));
+
+ setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]);
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]))
+ goto end;
+ }
+
+ setup_return(env, ka, &frame->retcode, frame_addr, usig,
+ frame_addr + offsetof(struct rt_sigframe_v1, retcode));
+
+ env->regs[1] = info_addr;
+ env->regs[2] = uc_addr;
+
+end:
+ unlock_user_struct(frame, frame_addr, 1);
+}
+
+static void setup_rt_frame_v2(int usig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ struct rt_sigframe_v2 *frame;
+ abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ abi_ulong info_addr, uc_addr;
+
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ return /* 1 */;
+
+ info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info);
+ uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc);
+ copy_siginfo_to_user(&frame->info, info);
+
+ setup_sigframe_v2(&frame->uc, set, env);
+
+ setup_return(env, ka, &frame->retcode, frame_addr, usig,
+ frame_addr + offsetof(struct rt_sigframe_v2, retcode));
+
+ env->regs[1] = info_addr;
+ env->regs[2] = uc_addr;
+
+ unlock_user_struct(frame, frame_addr, 1);
+}
+
+static void setup_rt_frame(int usig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ if (get_osversion() >= 0x020612) {
+ setup_rt_frame_v2(usig, ka, info, set, env);
+ } else {
+ setup_rt_frame_v1(usig, ka, info, set, env);
+ }
+}
+
+static int
+restore_sigcontext(CPUState *env, struct target_sigcontext *sc)
+{
+ int err = 0;
+ uint32_t cpsr;
+
+ __get_user_error(env->regs[0], &sc->arm_r0, err);
+ __get_user_error(env->regs[1], &sc->arm_r1, err);
+ __get_user_error(env->regs[2], &sc->arm_r2, err);
+ __get_user_error(env->regs[3], &sc->arm_r3, err);
+ __get_user_error(env->regs[4], &sc->arm_r4, err);
+ __get_user_error(env->regs[5], &sc->arm_r5, err);
+ __get_user_error(env->regs[6], &sc->arm_r6, err);
+ __get_user_error(env->regs[7], &sc->arm_r7, err);
+ __get_user_error(env->regs[8], &sc->arm_r8, err);
+ __get_user_error(env->regs[9], &sc->arm_r9, err);
+ __get_user_error(env->regs[10], &sc->arm_r10, err);
+ __get_user_error(env->regs[11], &sc->arm_fp, err);
+ __get_user_error(env->regs[12], &sc->arm_ip, err);
+ __get_user_error(env->regs[13], &sc->arm_sp, err);
+ __get_user_error(env->regs[14], &sc->arm_lr, err);
+ __get_user_error(env->regs[15], &sc->arm_pc, err);
+#ifdef TARGET_CONFIG_CPU_32
+ __get_user_error(cpsr, &sc->arm_cpsr, err);
+ cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC);
+#endif
+
+ err |= !valid_user_regs(env);
+
+ return err;
+}
+
+static long do_sigreturn_v1(CPUState *env)
+{
+ abi_ulong frame_addr;
+ struct sigframe_v1 *frame;
+ target_sigset_t set;
+ sigset_t host_set;
+ int i;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (env->regs[13] & 7)
+ goto badframe;
+
+ frame_addr = env->regs[13];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ if (__get_user(set.sig[0], &frame->sc.oldmask))
+ goto badframe;
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&host_set, &set);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (restore_sigcontext(env, &frame->sc))
+ goto badframe;
+
+#if 0
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt(current))
+ send_sig(SIGTRAP, current, 1);
+#endif
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[0];
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV /* , current */);
+ return 0;
+}
+
+static abi_ulong *restore_sigframe_v2_vfp(CPUState *env, abi_ulong *regspace)
+{
+ int i;
+ abi_ulong magic, sz;
+ uint32_t fpscr, fpexc;
+ struct target_vfp_sigframe *vfpframe;
+ vfpframe = (struct target_vfp_sigframe *)regspace;
+
+ __get_user(magic, &vfpframe->magic);
+ __get_user(sz, &vfpframe->size);
+ if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) {
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ __get_user(env->vfp.regs[i], &vfpframe->ufp.fpregs[i]);
+ }
+ __get_user(fpscr, &vfpframe->ufp.fpscr);
+ vfp_set_fpscr(env, fpscr);
+ __get_user(fpexc, &vfpframe->ufp_exc.fpexc);
+ /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid
+ * and the exception flag is cleared
+ */
+ fpexc |= (1 << 30);
+ fpexc &= ~((1 << 31) | (1 << 28));
+ env->vfp.xregs[ARM_VFP_FPEXC] = fpexc;
+ __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst);
+ __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2);
+ return (abi_ulong*)(vfpframe + 1);
+}
+
+static abi_ulong *restore_sigframe_v2_iwmmxt(CPUState *env, abi_ulong *regspace)
+{
+ int i;
+ abi_ulong magic, sz;
+ struct target_iwmmxt_sigframe *iwmmxtframe;
+ iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace;
+
+ __get_user(magic, &iwmmxtframe->magic);
+ __get_user(sz, &iwmmxtframe->size);
+ if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) {
+ return 0;
+ }
+ for (i = 0; i < 16; i++) {
+ __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]);
+ }
+ __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf);
+ __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf);
+ __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0);
+ __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1);
+ __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2);
+ __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3);
+ return (abi_ulong*)(iwmmxtframe + 1);
+}
+
+static int do_sigframe_return_v2(CPUState *env, target_ulong frame_addr,
+ struct target_ucontext_v2 *uc)
+{
+ sigset_t host_set;
+ abi_ulong *regspace;
+
+ target_to_host_sigset(&host_set, &uc->tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (restore_sigcontext(env, &uc->tuc_mcontext))
+ return 1;
+
+ /* Restore coprocessor signal frame */
+ regspace = uc->tuc_regspace;
+ if (arm_feature(env, ARM_FEATURE_VFP)) {
+ regspace = restore_sigframe_v2_vfp(env, regspace);
+ if (!regspace) {
+ return 1;
+ }
+ }
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ regspace = restore_sigframe_v2_iwmmxt(env, regspace);
+ if (!regspace) {
+ return 1;
+ }
+ }
+
+ if (do_sigaltstack(frame_addr + offsetof(struct target_ucontext_v2, tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT)
+ return 1;
+
+#if 0
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt(current))
+ send_sig(SIGTRAP, current, 1);
+#endif
+
+ return 0;
+}
+
+static long do_sigreturn_v2(CPUState *env)
+{
+ abi_ulong frame_addr;
+ struct sigframe_v2 *frame;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (env->regs[13] & 7)
+ goto badframe;
+
+ frame_addr = env->regs[13];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ if (do_sigframe_return_v2(env, frame_addr, &frame->uc))
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[0];
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV /* , current */);
+ return 0;
+}
+
+long do_sigreturn(CPUState *env)
+{
+ if (get_osversion() >= 0x020612) {
+ return do_sigreturn_v2(env);
+ } else {
+ return do_sigreturn_v1(env);
+ }
+}
+
+static long do_rt_sigreturn_v1(CPUState *env)
+{
+ abi_ulong frame_addr;
+ struct rt_sigframe_v1 *frame;
+ sigset_t host_set;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (env->regs[13] & 7)
+ goto badframe;
+
+ frame_addr = env->regs[13];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (restore_sigcontext(env, &frame->uc.tuc_mcontext))
+ goto badframe;
+
+ if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT)
+ goto badframe;
+
+#if 0
+ /* Send SIGTRAP if we're single-stepping */
+ if (ptrace_cancel_bpt(current))
+ send_sig(SIGTRAP, current, 1);
+#endif
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[0];
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV /* , current */);
+ return 0;
+}
+
+static long do_rt_sigreturn_v2(CPUState *env)
+{
+ abi_ulong frame_addr;
+ struct rt_sigframe_v2 *frame;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (env->regs[13] & 7)
+ goto badframe;
+
+ frame_addr = env->regs[13];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ if (do_sigframe_return_v2(env, frame_addr, &frame->uc))
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[0];
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV /* , current */);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ if (get_osversion() >= 0x020612) {
+ return do_rt_sigreturn_v2(env);
+ } else {
+ return do_rt_sigreturn_v1(env);
+ }
+}
+
+#elif defined(TARGET_SPARC)
+
+#define __SUNOS_MAXWIN 31
+
+/* This is what SunOS does, so shall I. */
+struct target_sigcontext {
+ abi_ulong sigc_onstack; /* state to restore */
+
+ abi_ulong sigc_mask; /* sigmask to restore */
+ abi_ulong sigc_sp; /* stack pointer */
+ abi_ulong sigc_pc; /* program counter */
+ abi_ulong sigc_npc; /* next program counter */
+ abi_ulong sigc_psr; /* for condition codes etc */
+ abi_ulong sigc_g1; /* User uses these two registers */
+ abi_ulong sigc_o0; /* within the trampoline code. */
+
+ /* Now comes information regarding the users window set
+ * at the time of the signal.
+ */
+ abi_ulong sigc_oswins; /* outstanding windows */
+
+ /* stack ptrs for each regwin buf */
+ char *sigc_spbuf[__SUNOS_MAXWIN];
+
+ /* Windows to restore after signal */
+ struct {
+ abi_ulong locals[8];
+ abi_ulong ins[8];
+ } sigc_wbuf[__SUNOS_MAXWIN];
+};
+/* A Sparc stack frame */
+struct sparc_stackf {
+ abi_ulong locals[8];
+ abi_ulong ins[8];
+ /* It's simpler to treat fp and callers_pc as elements of ins[]
+ * since we never need to access them ourselves.
+ */
+ char *structptr;
+ abi_ulong xargs[6];
+ abi_ulong xxargs[1];
+};
+
+typedef struct {
+ struct {
+ abi_ulong psr;
+ abi_ulong pc;
+ abi_ulong npc;
+ abi_ulong y;
+ abi_ulong u_regs[16]; /* globals and ins */
+ } si_regs;
+ int si_mask;
+} __siginfo_t;
+
+typedef struct {
+ unsigned long si_float_regs [32];
+ unsigned long si_fsr;
+ unsigned long si_fpqdepth;
+ struct {
+ unsigned long *insn_addr;
+ unsigned long insn;
+ } si_fpqueue [16];
+} qemu_siginfo_fpu_t;
+
+
+struct target_signal_frame {
+ struct sparc_stackf ss;
+ __siginfo_t info;
+ abi_ulong fpu_save;
+ abi_ulong insns[2] __attribute__ ((aligned (8)));
+ abi_ulong extramask[TARGET_NSIG_WORDS - 1];
+ abi_ulong extra_size; /* Should be 0 */
+ qemu_siginfo_fpu_t fpu_state;
+};
+struct target_rt_signal_frame {
+ struct sparc_stackf ss;
+ siginfo_t info;
+ abi_ulong regs[20];
+ sigset_t mask;
+ abi_ulong fpu_save;
+ unsigned int insns[2];
+ stack_t stack;
+ unsigned int extra_size; /* Should be 0 */
+ qemu_siginfo_fpu_t fpu_state;
+};
+
+#define UREG_O0 16
+#define UREG_O6 22
+#define UREG_I0 0
+#define UREG_I1 1
+#define UREG_I2 2
+#define UREG_I3 3
+#define UREG_I4 4
+#define UREG_I5 5
+#define UREG_I6 6
+#define UREG_I7 7
+#define UREG_L0 8
+#define UREG_FP UREG_I6
+#define UREG_SP UREG_O6
+
+static inline abi_ulong get_sigframe(struct target_sigaction *sa,
+ CPUState *env, unsigned long framesize)
+{
+ abi_ulong sp;
+
+ sp = env->regwptr[UREG_FP];
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (sa->sa_flags & TARGET_SA_ONSTACK) {
+ if (!on_sig_stack(sp)
+ && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7))
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+ return sp - framesize;
+}
+
+static int
+setup___siginfo(__siginfo_t *si, CPUState *env, abi_ulong mask)
+{
+ int err = 0, i;
+
+ err |= __put_user(env->psr, &si->si_regs.psr);
+ err |= __put_user(env->pc, &si->si_regs.pc);
+ err |= __put_user(env->npc, &si->si_regs.npc);
+ err |= __put_user(env->y, &si->si_regs.y);
+ for (i=0; i < 8; i++) {
+ err |= __put_user(env->gregs[i], &si->si_regs.u_regs[i]);
+ }
+ for (i=0; i < 8; i++) {
+ err |= __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]);
+ }
+ err |= __put_user(mask, &si->si_mask);
+ return err;
+}
+
+#if 0
+static int
+setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
+ CPUState *env, unsigned long mask)
+{
+ int err = 0;
+
+ err |= __put_user(mask, &sc->sigc_mask);
+ err |= __put_user(env->regwptr[UREG_SP], &sc->sigc_sp);
+ err |= __put_user(env->pc, &sc->sigc_pc);
+ err |= __put_user(env->npc, &sc->sigc_npc);
+ err |= __put_user(env->psr, &sc->sigc_psr);
+ err |= __put_user(env->gregs[1], &sc->sigc_g1);
+ err |= __put_user(env->regwptr[UREG_O0], &sc->sigc_o0);
+
+ return err;
+}
+#endif
+#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7)))
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ abi_ulong sf_addr;
+ struct target_signal_frame *sf;
+ int sigframe_size, err, i;
+
+ /* 1. Make sure everything is clean */
+ //synchronize_user_stack();
+
+ sigframe_size = NF_ALIGNEDSZ;
+ sf_addr = get_sigframe(ka, env, sigframe_size);
+
+ sf = lock_user(VERIFY_WRITE, sf_addr,
+ sizeof(struct target_signal_frame), 0);
+ if (!sf)
+ goto sigsegv;
+
+ //fprintf(stderr, "sf: %x pc %x fp %x sp %x\n", sf, env->pc, env->regwptr[UREG_FP], env->regwptr[UREG_SP]);
+#if 0
+ if (invalid_frame_pointer(sf, sigframe_size))
+ goto sigill_and_return;
+#endif
+ /* 2. Save the current process state */
+ err = setup___siginfo(&sf->info, env, set->sig[0]);
+ err |= __put_user(0, &sf->extra_size);
+
+ //err |= save_fpu_state(regs, &sf->fpu_state);
+ //err |= __put_user(&sf->fpu_state, &sf->fpu_save);
+
+ err |= __put_user(set->sig[0], &sf->info.si_mask);
+ for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
+ err |= __put_user(set->sig[i + 1], &sf->extramask[i]);
+ }
+
+ for (i = 0; i < 8; i++) {
+ err |= __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ err |= __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]);
+ }
+ if (err)
+ goto sigsegv;
+
+ /* 3. signal handler back-trampoline and parameters */
+ env->regwptr[UREG_FP] = sf_addr;
+ env->regwptr[UREG_I0] = sig;
+ env->regwptr[UREG_I1] = sf_addr +
+ offsetof(struct target_signal_frame, info);
+ env->regwptr[UREG_I2] = sf_addr +
+ offsetof(struct target_signal_frame, info);
+
+ /* 4. signal handler */
+ env->pc = ka->_sa_handler;
+ env->npc = (env->pc + 4);
+ /* 5. return to kernel instructions */
+ if (ka->sa_restorer)
+ env->regwptr[UREG_I7] = ka->sa_restorer;
+ else {
+ uint32_t val32;
+
+ env->regwptr[UREG_I7] = sf_addr +
+ offsetof(struct target_signal_frame, insns) - 2 * 4;
+
+ /* mov __NR_sigreturn, %g1 */
+ val32 = 0x821020d8;
+ err |= __put_user(val32, &sf->insns[0]);
+
+ /* t 0x10 */
+ val32 = 0x91d02010;
+ err |= __put_user(val32, &sf->insns[1]);
+ if (err)
+ goto sigsegv;
+
+ /* Flush instruction space. */
+ //flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
+ // tb_flush(env);
+ }
+ unlock_user(sf, sf_addr, sizeof(struct target_signal_frame));
+ return;
+#if 0
+sigill_and_return:
+ force_sig(TARGET_SIGILL);
+#endif
+sigsegv:
+ //fprintf(stderr, "force_sig\n");
+ unlock_user(sf, sf_addr, sizeof(struct target_signal_frame));
+ force_sig(TARGET_SIGSEGV);
+}
+static inline int
+restore_fpu_state(CPUState *env, qemu_siginfo_fpu_t *fpu)
+{
+ int err;
+#if 0
+#ifdef CONFIG_SMP
+ if (current->flags & PF_USEDFPU)
+ regs->psr &= ~PSR_EF;
+#else
+ if (current == last_task_used_math) {
+ last_task_used_math = 0;
+ regs->psr &= ~PSR_EF;
+ }
+#endif
+ current->used_math = 1;
+ current->flags &= ~PF_USEDFPU;
+#endif
+#if 0
+ if (verify_area (VERIFY_READ, fpu, sizeof(*fpu)))
+ return -EFAULT;
+#endif
+
+#if 0
+ /* XXX: incorrect */
+ err = __copy_from_user(&env->fpr[0], &fpu->si_float_regs[0],
+ (sizeof(unsigned long) * 32));
+#endif
+ err |= __get_user(env->fsr, &fpu->si_fsr);
+#if 0
+ err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth);
+ if (current->thread.fpqdepth != 0)
+ err |= __copy_from_user(&current->thread.fpqueue[0],
+ &fpu->si_fpqueue[0],
+ ((sizeof(unsigned long) +
+ (sizeof(unsigned long *)))*16));
+#endif
+ return err;
+}
+
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ abi_ulong sf_addr;
+ struct target_signal_frame *sf;
+ uint32_t up_psr, pc, npc;
+ target_sigset_t set;
+ sigset_t host_set;
+ abi_ulong fpu_save_addr;
+ int err, i;
+
+ sf_addr = env->regwptr[UREG_FP];
+ if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1))
+ goto segv_and_exit;
+#if 0
+ fprintf(stderr, "sigreturn\n");
+ fprintf(stderr, "sf: %x pc %x fp %x sp %x\n", sf, env->pc, env->regwptr[UREG_FP], env->regwptr[UREG_SP]);
+#endif
+ //cpu_dump_state(env, stderr, fprintf, 0);
+
+ /* 1. Make sure we are not getting garbage from the user */
+
+ if (sf_addr & 3)
+ goto segv_and_exit;
+
+ err = __get_user(pc, &sf->info.si_regs.pc);
+ err |= __get_user(npc, &sf->info.si_regs.npc);
+
+ if ((pc | npc) & 3)
+ goto segv_and_exit;
+
+ /* 2. Restore the state */
+ err |= __get_user(up_psr, &sf->info.si_regs.psr);
+
+ /* User can only change condition codes and FPU enabling in %psr. */
+ env->psr = (up_psr & (PSR_ICC /* | PSR_EF */))
+ | (env->psr & ~(PSR_ICC /* | PSR_EF */));
+
+ env->pc = pc;
+ env->npc = npc;
+ err |= __get_user(env->y, &sf->info.si_regs.y);
+ for (i=0; i < 8; i++) {
+ err |= __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]);
+ }
+ for (i=0; i < 8; i++) {
+ err |= __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]);
+ }
+
+ err |= __get_user(fpu_save_addr, &sf->fpu_save);
+
+ //if (fpu_save)
+ // err |= restore_fpu_state(env, fpu_save);
+
+ /* This is pretty much atomic, no amount locking would prevent
+ * the races which exist anyways.
+ */
+ err |= __get_user(set.sig[0], &sf->info.si_mask);
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ err |= (__get_user(set.sig[i], &sf->extramask[i - 1]));
+ }
+
+ target_to_host_sigset_internal(&host_set, &set);
+ sigprocmask(SIG_SETMASK, &host_set, NULL);
+
+ if (err)
+ goto segv_and_exit;
+ unlock_user_struct(sf, sf_addr, 0);
+ return env->regwptr[0];
+
+segv_and_exit:
+ unlock_user_struct(sf, sf_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+#define MC_TSTATE 0
+#define MC_PC 1
+#define MC_NPC 2
+#define MC_Y 3
+#define MC_G1 4
+#define MC_G2 5
+#define MC_G3 6
+#define MC_G4 7
+#define MC_G5 8
+#define MC_G6 9
+#define MC_G7 10
+#define MC_O0 11
+#define MC_O1 12
+#define MC_O2 13
+#define MC_O3 14
+#define MC_O4 15
+#define MC_O5 16
+#define MC_O6 17
+#define MC_O7 18
+#define MC_NGREG 19
+
+typedef abi_ulong target_mc_greg_t;
+typedef target_mc_greg_t target_mc_gregset_t[MC_NGREG];
+
+struct target_mc_fq {
+ abi_ulong *mcfq_addr;
+ uint32_t mcfq_insn;
+};
+
+struct target_mc_fpu {
+ union {
+ uint32_t sregs[32];
+ uint64_t dregs[32];
+ //uint128_t qregs[16];
+ } mcfpu_fregs;
+ abi_ulong mcfpu_fsr;
+ abi_ulong mcfpu_fprs;
+ abi_ulong mcfpu_gsr;
+ struct target_mc_fq *mcfpu_fq;
+ unsigned char mcfpu_qcnt;
+ unsigned char mcfpu_qentsz;
+ unsigned char mcfpu_enab;
+};
+typedef struct target_mc_fpu target_mc_fpu_t;
+
+typedef struct {
+ target_mc_gregset_t mc_gregs;
+ target_mc_greg_t mc_fp;
+ target_mc_greg_t mc_i7;
+ target_mc_fpu_t mc_fpregs;
+} target_mcontext_t;
+
+struct target_ucontext {
+ struct target_ucontext *tuc_link;
+ abi_ulong tuc_flags;
+ target_sigset_t tuc_sigmask;
+ target_mcontext_t tuc_mcontext;
+};
+
+/* A V9 register window */
+struct target_reg_window {
+ abi_ulong locals[8];
+ abi_ulong ins[8];
+};
+
+#define TARGET_STACK_BIAS 2047
+
+/* {set, get}context() needed for 64-bit SparcLinux userland. */
+void sparc64_set_context(CPUSPARCState *env)
+{
+ abi_ulong ucp_addr;
+ struct target_ucontext *ucp;
+ target_mc_gregset_t *grp;
+ abi_ulong pc, npc, tstate;
+ abi_ulong fp, i7, w_addr;
+ unsigned char fenab;
+ int err;
+ unsigned int i;
+
+ ucp_addr = env->regwptr[UREG_I0];
+ if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1))
+ goto do_sigsegv;
+ grp = &ucp->tuc_mcontext.mc_gregs;
+ err = __get_user(pc, &((*grp)[MC_PC]));
+ err |= __get_user(npc, &((*grp)[MC_NPC]));
+ if (err || ((pc | npc) & 3))
+ goto do_sigsegv;
+ if (env->regwptr[UREG_I1]) {
+ target_sigset_t target_set;
+ sigset_t set;
+
+ if (TARGET_NSIG_WORDS == 1) {
+ if (__get_user(target_set.sig[0], &ucp->tuc_sigmask.sig[0]))
+ goto do_sigsegv;
+ } else {
+ abi_ulong *src, *dst;
+ src = ucp->tuc_sigmask.sig;
+ dst = target_set.sig;
+ for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong);
+ i++, dst++, src++)
+ err |= __get_user(*dst, src);
+ if (err)
+ goto do_sigsegv;
+ }
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+ }
+ env->pc = pc;
+ env->npc = npc;
+ err |= __get_user(env->y, &((*grp)[MC_Y]));
+ err |= __get_user(tstate, &((*grp)[MC_TSTATE]));
+ env->asi = (tstate >> 24) & 0xff;
+ cpu_put_ccr(env, tstate >> 32);
+ cpu_put_cwp64(env, tstate & 0x1f);
+ err |= __get_user(env->gregs[1], (&(*grp)[MC_G1]));
+ err |= __get_user(env->gregs[2], (&(*grp)[MC_G2]));
+ err |= __get_user(env->gregs[3], (&(*grp)[MC_G3]));
+ err |= __get_user(env->gregs[4], (&(*grp)[MC_G4]));
+ err |= __get_user(env->gregs[5], (&(*grp)[MC_G5]));
+ err |= __get_user(env->gregs[6], (&(*grp)[MC_G6]));
+ err |= __get_user(env->gregs[7], (&(*grp)[MC_G7]));
+ err |= __get_user(env->regwptr[UREG_I0], (&(*grp)[MC_O0]));
+ err |= __get_user(env->regwptr[UREG_I1], (&(*grp)[MC_O1]));
+ err |= __get_user(env->regwptr[UREG_I2], (&(*grp)[MC_O2]));
+ err |= __get_user(env->regwptr[UREG_I3], (&(*grp)[MC_O3]));
+ err |= __get_user(env->regwptr[UREG_I4], (&(*grp)[MC_O4]));
+ err |= __get_user(env->regwptr[UREG_I5], (&(*grp)[MC_O5]));
+ err |= __get_user(env->regwptr[UREG_I6], (&(*grp)[MC_O6]));
+ err |= __get_user(env->regwptr[UREG_I7], (&(*grp)[MC_O7]));
+
+ err |= __get_user(fp, &(ucp->tuc_mcontext.mc_fp));
+ err |= __get_user(i7, &(ucp->tuc_mcontext.mc_i7));
+
+ w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6];
+ if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]),
+ abi_ulong) != 0)
+ goto do_sigsegv;
+ if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]),
+ abi_ulong) != 0)
+ goto do_sigsegv;
+ err |= __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab));
+ err |= __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs));
+ {
+ uint32_t *src, *dst;
+ src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs;
+ dst = env->fpr;
+ /* XXX: check that the CPU storage is the same as user context */
+ for (i = 0; i < 64; i++, dst++, src++)
+ err |= __get_user(*dst, src);
+ }
+ err |= __get_user(env->fsr,
+ &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr));
+ err |= __get_user(env->gsr,
+ &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr));
+ if (err)
+ goto do_sigsegv;
+ unlock_user_struct(ucp, ucp_addr, 0);
+ return;
+ do_sigsegv:
+ unlock_user_struct(ucp, ucp_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+}
+
+void sparc64_get_context(CPUSPARCState *env)
+{
+ abi_ulong ucp_addr;
+ struct target_ucontext *ucp;
+ target_mc_gregset_t *grp;
+ target_mcontext_t *mcp;
+ abi_ulong fp, i7, w_addr;
+ int err;
+ unsigned int i;
+ target_sigset_t target_set;
+ sigset_t set;
+
+ ucp_addr = env->regwptr[UREG_I0];
+ if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0))
+ goto do_sigsegv;
+
+ mcp = &ucp->tuc_mcontext;
+ grp = &mcp->mc_gregs;
+
+ /* Skip over the trap instruction, first. */
+ env->pc = env->npc;
+ env->npc += 4;
+
+ err = 0;
+
+ sigprocmask(0, NULL, &set);
+ host_to_target_sigset_internal(&target_set, &set);
+ if (TARGET_NSIG_WORDS == 1) {
+ err |= __put_user(target_set.sig[0],
+ (abi_ulong *)&ucp->tuc_sigmask);
+ } else {
+ abi_ulong *src, *dst;
+ src = target_set.sig;
+ dst = ucp->tuc_sigmask.sig;
+ for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong);
+ i++, dst++, src++)
+ err |= __put_user(*src, dst);
+ if (err)
+ goto do_sigsegv;
+ }
+
+ /* XXX: tstate must be saved properly */
+ // err |= __put_user(env->tstate, &((*grp)[MC_TSTATE]));
+ err |= __put_user(env->pc, &((*grp)[MC_PC]));
+ err |= __put_user(env->npc, &((*grp)[MC_NPC]));
+ err |= __put_user(env->y, &((*grp)[MC_Y]));
+ err |= __put_user(env->gregs[1], &((*grp)[MC_G1]));
+ err |= __put_user(env->gregs[2], &((*grp)[MC_G2]));
+ err |= __put_user(env->gregs[3], &((*grp)[MC_G3]));
+ err |= __put_user(env->gregs[4], &((*grp)[MC_G4]));
+ err |= __put_user(env->gregs[5], &((*grp)[MC_G5]));
+ err |= __put_user(env->gregs[6], &((*grp)[MC_G6]));
+ err |= __put_user(env->gregs[7], &((*grp)[MC_G7]));
+ err |= __put_user(env->regwptr[UREG_I0], &((*grp)[MC_O0]));
+ err |= __put_user(env->regwptr[UREG_I1], &((*grp)[MC_O1]));
+ err |= __put_user(env->regwptr[UREG_I2], &((*grp)[MC_O2]));
+ err |= __put_user(env->regwptr[UREG_I3], &((*grp)[MC_O3]));
+ err |= __put_user(env->regwptr[UREG_I4], &((*grp)[MC_O4]));
+ err |= __put_user(env->regwptr[UREG_I5], &((*grp)[MC_O5]));
+ err |= __put_user(env->regwptr[UREG_I6], &((*grp)[MC_O6]));
+ err |= __put_user(env->regwptr[UREG_I7], &((*grp)[MC_O7]));
+
+ w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6];
+ fp = i7 = 0;
+ if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]),
+ abi_ulong) != 0)
+ goto do_sigsegv;
+ if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]),
+ abi_ulong) != 0)
+ goto do_sigsegv;
+ err |= __put_user(fp, &(mcp->mc_fp));
+ err |= __put_user(i7, &(mcp->mc_i7));
+
+ {
+ uint32_t *src, *dst;
+ src = env->fpr;
+ dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs;
+ /* XXX: check that the CPU storage is the same as user context */
+ for (i = 0; i < 64; i++, dst++, src++)
+ err |= __put_user(*src, dst);
+ }
+ err |= __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr));
+ err |= __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr));
+ err |= __put_user(env->fprs, &(mcp->mc_fpregs.mcfpu_fprs));
+
+ if (err)
+ goto do_sigsegv;
+ unlock_user_struct(ucp, ucp_addr, 1);
+ return;
+ do_sigsegv:
+ unlock_user_struct(ucp, ucp_addr, 1);
+ force_sig(TARGET_SIGSEGV);
+}
+#endif
+#elif defined(TARGET_ABI_MIPSN64)
+
+# warning signal handling not implemented
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_frame: not implemented\n");
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+#elif defined(TARGET_ABI_MIPSN32)
+
+# warning signal handling not implemented
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_frame: not implemented\n");
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+#elif defined(TARGET_ABI_MIPSO32)
+
+struct target_sigcontext {
+ uint32_t sc_regmask; /* Unused */
+ uint32_t sc_status;
+ uint64_t sc_pc;
+ uint64_t sc_regs[32];
+ uint64_t sc_fpregs[32];
+ uint32_t sc_ownedfp; /* Unused */
+ uint32_t sc_fpc_csr;
+ uint32_t sc_fpc_eir; /* Unused */
+ uint32_t sc_used_math;
+ uint32_t sc_dsp; /* dsp status, was sc_ssflags */
+ uint32_t pad0;
+ uint64_t sc_mdhi;
+ uint64_t sc_mdlo;
+ target_ulong sc_hi1; /* Was sc_cause */
+ target_ulong sc_lo1; /* Was sc_badvaddr */
+ target_ulong sc_hi2; /* Was sc_sigset[4] */
+ target_ulong sc_lo2;
+ target_ulong sc_hi3;
+ target_ulong sc_lo3;
+};
+
+struct sigframe {
+ uint32_t sf_ass[4]; /* argument save space for o32 */
+ uint32_t sf_code[2]; /* signal trampoline */
+ struct target_sigcontext sf_sc;
+ target_sigset_t sf_mask;
+};
+
+struct target_ucontext {
+ target_ulong tuc_flags;
+ target_ulong tuc_link;
+ target_stack_t tuc_stack;
+ target_ulong pad0;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask;
+};
+
+struct target_rt_sigframe {
+ uint32_t rs_ass[4]; /* argument save space for o32 */
+ uint32_t rs_code[2]; /* signal trampoline */
+ struct target_siginfo rs_info;
+ struct target_ucontext rs_uc;
+};
+
+/* Install trampoline to jump back from signal handler */
+static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
+{
+ int err;
+
+ /*
+ * Set up the return code ...
+ *
+ * li v0, __NR__foo_sigreturn
+ * syscall
+ */
+
+ err = __put_user(0x24020000 + syscall, tramp + 0);
+ err |= __put_user(0x0000000c , tramp + 1);
+ /* flush_cache_sigtramp((unsigned long) tramp); */
+ return err;
+}
+
+static inline int
+setup_sigcontext(CPUState *regs, struct target_sigcontext *sc)
+{
+ int err = 0;
+
+ err |= __put_user(regs->active_tc.PC, &sc->sc_pc);
+
+#define save_gp_reg(i) do { \
+ err |= __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); \
+ } while(0)
+ __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2);
+ save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6);
+ save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10);
+ save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14);
+ save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18);
+ save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22);
+ save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26);
+ save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30);
+ save_gp_reg(31);
+#undef save_gp_reg
+
+ err |= __put_user(regs->active_tc.HI[0], &sc->sc_mdhi);
+ err |= __put_user(regs->active_tc.LO[0], &sc->sc_mdlo);
+
+ /* Not used yet, but might be useful if we ever have DSP suppport */
+#if 0
+ if (cpu_has_dsp) {
+ err |= __put_user(mfhi1(), &sc->sc_hi1);
+ err |= __put_user(mflo1(), &sc->sc_lo1);
+ err |= __put_user(mfhi2(), &sc->sc_hi2);
+ err |= __put_user(mflo2(), &sc->sc_lo2);
+ err |= __put_user(mfhi3(), &sc->sc_hi3);
+ err |= __put_user(mflo3(), &sc->sc_lo3);
+ err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+ }
+ /* same with 64 bit */
+#ifdef CONFIG_64BIT
+ err |= __put_user(regs->hi, &sc->sc_hi[0]);
+ err |= __put_user(regs->lo, &sc->sc_lo[0]);
+ if (cpu_has_dsp) {
+ err |= __put_user(mfhi1(), &sc->sc_hi[1]);
+ err |= __put_user(mflo1(), &sc->sc_lo[1]);
+ err |= __put_user(mfhi2(), &sc->sc_hi[2]);
+ err |= __put_user(mflo2(), &sc->sc_lo[2]);
+ err |= __put_user(mfhi3(), &sc->sc_hi[3]);
+ err |= __put_user(mflo3(), &sc->sc_lo[3]);
+ err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+ }
+#endif
+#endif
+
+#if 0
+ err |= __put_user(!!used_math(), &sc->sc_used_math);
+
+ if (!used_math())
+ goto out;
+
+ /*
+ * Save FPU state to signal context. Signal handler will "inherit"
+ * current FPU state.
+ */
+ preempt_disable();
+
+ if (!is_fpu_owner()) {
+ own_fpu();
+ restore_fp(current);
+ }
+ err |= save_fp_context(sc);
+
+ preempt_enable();
+ out:
+#endif
+ return err;
+}
+
+static inline int
+restore_sigcontext(CPUState *regs, struct target_sigcontext *sc)
+{
+ int err = 0;
+
+ err |= __get_user(regs->CP0_EPC, &sc->sc_pc);
+
+ err |= __get_user(regs->active_tc.HI[0], &sc->sc_mdhi);
+ err |= __get_user(regs->active_tc.LO[0], &sc->sc_mdlo);
+
+#define restore_gp_reg(i) do { \
+ err |= __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); \
+ } while(0)
+ restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3);
+ restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6);
+ restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9);
+ restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12);
+ restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15);
+ restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18);
+ restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21);
+ restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24);
+ restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27);
+ restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30);
+ restore_gp_reg(31);
+#undef restore_gp_reg
+
+#if 0
+ if (cpu_has_dsp) {
+ err |= __get_user(treg, &sc->sc_hi1); mthi1(treg);
+ err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg);
+ err |= __get_user(treg, &sc->sc_hi2); mthi2(treg);
+ err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg);
+ err |= __get_user(treg, &sc->sc_hi3); mthi3(treg);
+ err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg);
+ err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+ }
+#ifdef CONFIG_64BIT
+ err |= __get_user(regs->hi, &sc->sc_hi[0]);
+ err |= __get_user(regs->lo, &sc->sc_lo[0]);
+ if (cpu_has_dsp) {
+ err |= __get_user(treg, &sc->sc_hi[1]); mthi1(treg);
+ err |= __get_user(treg, &sc->sc_lo[1]); mthi1(treg);
+ err |= __get_user(treg, &sc->sc_hi[2]); mthi2(treg);
+ err |= __get_user(treg, &sc->sc_lo[2]); mthi2(treg);
+ err |= __get_user(treg, &sc->sc_hi[3]); mthi3(treg);
+ err |= __get_user(treg, &sc->sc_lo[3]); mthi3(treg);
+ err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+ }
+#endif
+
+ err |= __get_user(used_math, &sc->sc_used_math);
+ conditional_used_math(used_math);
+
+ preempt_disable();
+
+ if (used_math()) {
+ /* restore fpu context if we have used it before */
+ own_fpu();
+ err |= restore_fp_context(sc);
+ } else {
+ /* signal handler may have used FPU. Give it up. */
+ lose_fpu();
+ }
+
+ preempt_enable();
+#endif
+ return err;
+}
+/*
+ * Determine which stack to use..
+ */
+static inline abi_ulong
+get_sigframe(struct target_sigaction *ka, CPUState *regs, size_t frame_size)
+{
+ unsigned long sp;
+
+ /* Default to using normal stack */
+ sp = regs->active_tc.gpr[29];
+
+ /*
+ * FPU emulator may have it's own trampoline active just
+ * above the user stack, 16-bytes before the next lowest
+ * 16 byte boundary. Try to avoid trashing it.
+ */
+ sp -= 32;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) {
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+
+ return (sp - frame_size) & ~7;
+}
+
+/* compare linux/arch/mips/kernel/signal.c:setup_frame() */
+static void setup_frame(int sig, struct target_sigaction * ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ struct sigframe *frame;
+ abi_ulong frame_addr;
+ int i;
+
+ frame_addr = get_sigframe(ka, regs, sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
+
+ if(setup_sigcontext(regs, &frame->sf_sc))
+ goto give_sigsegv;
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if(__put_user(set->sig[i], &frame->sf_mask.sig[i]))
+ goto give_sigsegv;
+ }
+
+ /*
+ * Arguments to signal handler:
+ *
+ * a0 = signal number
+ * a1 = 0 (should be cause)
+ * a2 = pointer to struct sigcontext
+ *
+ * $25 and PC point to the signal handler, $29 points to the
+ * struct sigframe.
+ */
+ regs->active_tc.gpr[ 4] = sig;
+ regs->active_tc.gpr[ 5] = 0;
+ regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc);
+ regs->active_tc.gpr[29] = frame_addr;
+ regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code);
+ /* The original kernel code sets CP0_EPC to the handler
+ * since it returns to userland using eret
+ * we cannot do this here, and we must set PC directly */
+ regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler;
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV/*, current*/);
+ return;
+}
+
+long do_sigreturn(CPUState *regs)
+{
+ struct sigframe *frame;
+ abi_ulong frame_addr;
+ sigset_t blocked;
+ target_sigset_t target_set;
+ int i;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_sigreturn\n");
+#endif
+ frame_addr = regs->active_tc.gpr[29];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if(__get_user(target_set.sig[i], &frame->sf_mask.sig[i]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&blocked, &target_set);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+ if (restore_sigcontext(regs, &frame->sf_sc))
+ goto badframe;
+
+#if 0
+ /*
+ * Don't let your children do this ...
+ */
+ __asm__ __volatile__(
+ "move\t$29, %0\n\t"
+ "j\tsyscall_exit"
+ :/* no outputs */
+ :"r" (&regs));
+ /* Unreached */
+#endif
+
+ regs->active_tc.PC = regs->CP0_EPC;
+ /* I am not sure this is right, but it seems to work
+ * maybe a problem with nested signals ? */
+ regs->CP0_EPC = 0;
+ return -TARGET_QEMU_ESIGRETURN;
+
+badframe:
+ force_sig(TARGET_SIGSEGV/*, current*/);
+ return 0;
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_rt_sigframe *frame;
+ abi_ulong frame_addr;
+ int i;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
+
+ copy_siginfo_to_user(&frame->rs_info, info);
+
+ __put_user(0, &frame->rs_uc.tuc_flags);
+ __put_user(0, &frame->rs_uc.tuc_link);
+ __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp);
+ __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size);
+ __put_user(sas_ss_flags(get_sp_from_cpustate(env)),
+ &frame->rs_uc.tuc_stack.ss_flags);
+
+ setup_sigcontext(env, &frame->rs_uc.tuc_mcontext);
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
+ }
+
+ /*
+ * Arguments to signal handler:
+ *
+ * a0 = signal number
+ * a1 = pointer to struct siginfo
+ * a2 = pointer to struct ucontext
+ *
+ * $25 and PC point to the signal handler, $29 points to the
+ * struct sigframe.
+ */
+ env->active_tc.gpr[ 4] = sig;
+ env->active_tc.gpr[ 5] = frame_addr
+ + offsetof(struct target_rt_sigframe, rs_info);
+ env->active_tc.gpr[ 6] = frame_addr
+ + offsetof(struct target_rt_sigframe, rs_uc);
+ env->active_tc.gpr[29] = frame_addr;
+ env->active_tc.gpr[31] = frame_addr
+ + offsetof(struct target_rt_sigframe, rs_code);
+ /* The original kernel code sets CP0_EPC to the handler
+ * since it returns to userland using eret
+ * we cannot do this here, and we must set PC directly */
+ env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV/*, current*/);
+ return;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ struct target_rt_sigframe *frame;
+ abi_ulong frame_addr;
+ sigset_t blocked;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_rt_sigreturn\n");
+#endif
+ frame_addr = env->active_tc.gpr[29];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+ if (restore_sigcontext(env, &frame->rs_uc.tuc_mcontext))
+ goto badframe;
+
+ if (do_sigaltstack(frame_addr +
+ offsetof(struct target_rt_sigframe, rs_uc.tuc_stack),
+ 0, get_sp_from_cpustate(env)) == -EFAULT)
+ goto badframe;
+
+ env->active_tc.PC = env->CP0_EPC;
+ /* I am not sure this is right, but it seems to work
+ * maybe a problem with nested signals ? */
+ env->CP0_EPC = 0;
+ return -TARGET_QEMU_ESIGRETURN;
+
+badframe:
+ force_sig(TARGET_SIGSEGV/*, current*/);
+ return 0;
+}
+
+#elif defined(TARGET_SH4)
+
+/*
+ * code and data structures from linux kernel:
+ * include/asm-sh/sigcontext.h
+ * arch/sh/kernel/signal.c
+ */
+
+struct target_sigcontext {
+ target_ulong oldmask;
+
+ /* CPU registers */
+ target_ulong sc_gregs[16];
+ target_ulong sc_pc;
+ target_ulong sc_pr;
+ target_ulong sc_sr;
+ target_ulong sc_gbr;
+ target_ulong sc_mach;
+ target_ulong sc_macl;
+
+ /* FPU registers */
+ target_ulong sc_fpregs[16];
+ target_ulong sc_xfpregs[16];
+ unsigned int sc_fpscr;
+ unsigned int sc_fpul;
+ unsigned int sc_ownedfp;
+};
+
+struct target_sigframe
+{
+ struct target_sigcontext sc;
+ target_ulong extramask[TARGET_NSIG_WORDS-1];
+ uint16_t retcode[3];
+};
+
+
+struct target_ucontext {
+ target_ulong tuc_flags;
+ struct target_ucontext *tuc_link;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask; /* mask last for extensibility */
+};
+
+struct target_rt_sigframe
+{
+ struct target_siginfo info;
+ struct target_ucontext uc;
+ uint16_t retcode[3];
+};
+
+
+#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
+#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */
+
+static abi_ulong get_sigframe(struct target_sigaction *ka,
+ unsigned long sp, size_t frame_size)
+{
+ if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) {
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+
+ return (sp - frame_size) & -8ul;
+}
+
+static int setup_sigcontext(struct target_sigcontext *sc,
+ CPUState *regs, unsigned long mask)
+{
+ int err = 0;
+ int i;
+
+#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
+ COPY(gregs[0]); COPY(gregs[1]);
+ COPY(gregs[2]); COPY(gregs[3]);
+ COPY(gregs[4]); COPY(gregs[5]);
+ COPY(gregs[6]); COPY(gregs[7]);
+ COPY(gregs[8]); COPY(gregs[9]);
+ COPY(gregs[10]); COPY(gregs[11]);
+ COPY(gregs[12]); COPY(gregs[13]);
+ COPY(gregs[14]); COPY(gregs[15]);
+ COPY(gbr); COPY(mach);
+ COPY(macl); COPY(pr);
+ COPY(sr); COPY(pc);
+#undef COPY
+
+ for (i=0; i<16; i++) {
+ err |= __put_user(regs->fregs[i], &sc->sc_fpregs[i]);
+ }
+ err |= __put_user(regs->fpscr, &sc->sc_fpscr);
+ err |= __put_user(regs->fpul, &sc->sc_fpul);
+
+ /* non-iBCS2 extensions.. */
+ err |= __put_user(mask, &sc->oldmask);
+
+ return err;
+}
+
+static int restore_sigcontext(CPUState *regs, struct target_sigcontext *sc,
+ target_ulong *r0_p)
+{
+ unsigned int err = 0;
+ int i;
+
+#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
+ COPY(gregs[1]);
+ COPY(gregs[2]); COPY(gregs[3]);
+ COPY(gregs[4]); COPY(gregs[5]);
+ COPY(gregs[6]); COPY(gregs[7]);
+ COPY(gregs[8]); COPY(gregs[9]);
+ COPY(gregs[10]); COPY(gregs[11]);
+ COPY(gregs[12]); COPY(gregs[13]);
+ COPY(gregs[14]); COPY(gregs[15]);
+ COPY(gbr); COPY(mach);
+ COPY(macl); COPY(pr);
+ COPY(sr); COPY(pc);
+#undef COPY
+
+ for (i=0; i<16; i++) {
+ err |= __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
+ }
+ err |= __get_user(regs->fpscr, &sc->sc_fpscr);
+ err |= __get_user(regs->fpul, &sc->sc_fpul);
+
+ regs->tra = -1; /* disable syscall checks */
+ err |= __get_user(*r0_p, &sc->sc_gregs[0]);
+ return err;
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *regs)
+{
+ struct target_sigframe *frame;
+ abi_ulong frame_addr;
+ int i;
+ int err = 0;
+ int signal;
+
+ frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ signal = current_exec_domain_sig(sig);
+
+ err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
+
+ for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
+ err |= __put_user(set->sig[i + 1], &frame->extramask[i]);
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ regs->pr = (unsigned long) ka->sa_restorer;
+ } else {
+ /* Generate return code (system call to sigreturn) */
+ err |= __put_user(MOVW(2), &frame->retcode[0]);
+ err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
+ err |= __put_user((TARGET_NR_sigreturn), &frame->retcode[2]);
+ regs->pr = (unsigned long) frame->retcode;
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->gregs[15] = (unsigned long) frame;
+ regs->gregs[4] = signal; /* Arg for signal handler */
+ regs->gregs[5] = 0;
+ regs->gregs[6] = (unsigned long) &frame->sc;
+ regs->pc = (unsigned long) ka->_sa_handler;
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ 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 *regs)
+{
+ struct target_rt_sigframe *frame;
+ abi_ulong frame_addr;
+ int i;
+ int err = 0;
+ int signal;
+
+ frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ signal = current_exec_domain_sig(sig);
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.tuc_flags);
+ err |= __put_user(0, (unsigned long *)&frame->uc.tuc_link);
+ err |= __put_user((unsigned long)target_sigaltstack_used.ss_sp,
+ &frame->uc.tuc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->gregs[15]),
+ &frame->uc.tuc_stack.ss_flags);
+ err |= __put_user(target_sigaltstack_used.ss_size,
+ &frame->uc.tuc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.tuc_mcontext,
+ regs, set->sig[0]);
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ err |= __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
+ }
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ regs->pr = (unsigned long) ka->sa_restorer;
+ } else {
+ /* Generate return code (system call to sigreturn) */
+ err |= __put_user(MOVW(2), &frame->retcode[0]);
+ err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
+ err |= __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]);
+ regs->pr = (unsigned long) frame->retcode;
+ }
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->gregs[15] = (unsigned long) frame;
+ regs->gregs[4] = signal; /* Arg for signal handler */
+ regs->gregs[5] = (unsigned long) &frame->info;
+ regs->gregs[6] = (unsigned long) &frame->uc;
+ regs->pc = (unsigned long) ka->_sa_handler;
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_sigreturn(CPUState *regs)
+{
+ struct target_sigframe *frame;
+ abi_ulong frame_addr;
+ sigset_t blocked;
+ target_sigset_t target_set;
+ target_ulong r0;
+ int i;
+ int err = 0;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_sigreturn\n");
+#endif
+ frame_addr = regs->gregs[15];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ err |= __get_user(target_set.sig[0], &frame->sc.oldmask);
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ err |= (__get_user(target_set.sig[i], &frame->extramask[i - 1]));
+ }
+
+ if (err)
+ goto badframe;
+
+ target_to_host_sigset_internal(&blocked, &target_set);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+ if (restore_sigcontext(regs, &frame->sc, &r0))
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return r0;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUState *regs)
+{
+ struct target_rt_sigframe *frame;
+ abi_ulong frame_addr;
+ sigset_t blocked;
+ target_ulong r0;
+
+#if defined(DEBUG_SIGNAL)
+ fprintf(stderr, "do_rt_sigreturn\n");
+#endif
+ frame_addr = regs->gregs[15];
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+ if (restore_sigcontext(regs, &frame->uc.tuc_mcontext, &r0))
+ goto badframe;
+
+ if (do_sigaltstack(frame_addr +
+ offsetof(struct target_rt_sigframe, uc.tuc_stack),
+ 0, get_sp_from_cpustate(regs)) == -EFAULT)
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return r0;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+#elif defined(TARGET_MICROBLAZE)
+
+struct target_sigcontext {
+ struct target_pt_regs regs; /* needs to be first */
+ uint32_t oldmask;
+};
+
+struct target_stack_t {
+ abi_ulong ss_sp;
+ int ss_flags;
+ unsigned int ss_size;
+};
+
+struct target_ucontext {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ struct target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ uint32_t tuc_extramask[TARGET_NSIG_WORDS - 1];
+};
+
+/* Signal frames. */
+struct target_signal_frame {
+ struct target_ucontext uc;
+ uint32_t extramask[TARGET_NSIG_WORDS - 1];
+ uint32_t tramp[2];
+};
+
+struct rt_signal_frame {
+ struct siginfo info;
+ struct ucontext uc;
+ uint32_t tramp[2];
+};
+
+static void setup_sigcontext(struct target_sigcontext *sc, CPUState *env)
+{
+ __put_user(env->regs[0], &sc->regs.r0);
+ __put_user(env->regs[1], &sc->regs.r1);
+ __put_user(env->regs[2], &sc->regs.r2);
+ __put_user(env->regs[3], &sc->regs.r3);
+ __put_user(env->regs[4], &sc->regs.r4);
+ __put_user(env->regs[5], &sc->regs.r5);
+ __put_user(env->regs[6], &sc->regs.r6);
+ __put_user(env->regs[7], &sc->regs.r7);
+ __put_user(env->regs[8], &sc->regs.r8);
+ __put_user(env->regs[9], &sc->regs.r9);
+ __put_user(env->regs[10], &sc->regs.r10);
+ __put_user(env->regs[11], &sc->regs.r11);
+ __put_user(env->regs[12], &sc->regs.r12);
+ __put_user(env->regs[13], &sc->regs.r13);
+ __put_user(env->regs[14], &sc->regs.r14);
+ __put_user(env->regs[15], &sc->regs.r15);
+ __put_user(env->regs[16], &sc->regs.r16);
+ __put_user(env->regs[17], &sc->regs.r17);
+ __put_user(env->regs[18], &sc->regs.r18);
+ __put_user(env->regs[19], &sc->regs.r19);
+ __put_user(env->regs[20], &sc->regs.r20);
+ __put_user(env->regs[21], &sc->regs.r21);
+ __put_user(env->regs[22], &sc->regs.r22);
+ __put_user(env->regs[23], &sc->regs.r23);
+ __put_user(env->regs[24], &sc->regs.r24);
+ __put_user(env->regs[25], &sc->regs.r25);
+ __put_user(env->regs[26], &sc->regs.r26);
+ __put_user(env->regs[27], &sc->regs.r27);
+ __put_user(env->regs[28], &sc->regs.r28);
+ __put_user(env->regs[29], &sc->regs.r29);
+ __put_user(env->regs[30], &sc->regs.r30);
+ __put_user(env->regs[31], &sc->regs.r31);
+ __put_user(env->sregs[SR_PC], &sc->regs.pc);
+}
+
+static void restore_sigcontext(struct target_sigcontext *sc, CPUState *env)
+{
+ __get_user(env->regs[0], &sc->regs.r0);
+ __get_user(env->regs[1], &sc->regs.r1);
+ __get_user(env->regs[2], &sc->regs.r2);
+ __get_user(env->regs[3], &sc->regs.r3);
+ __get_user(env->regs[4], &sc->regs.r4);
+ __get_user(env->regs[5], &sc->regs.r5);
+ __get_user(env->regs[6], &sc->regs.r6);
+ __get_user(env->regs[7], &sc->regs.r7);
+ __get_user(env->regs[8], &sc->regs.r8);
+ __get_user(env->regs[9], &sc->regs.r9);
+ __get_user(env->regs[10], &sc->regs.r10);
+ __get_user(env->regs[11], &sc->regs.r11);
+ __get_user(env->regs[12], &sc->regs.r12);
+ __get_user(env->regs[13], &sc->regs.r13);
+ __get_user(env->regs[14], &sc->regs.r14);
+ __get_user(env->regs[15], &sc->regs.r15);
+ __get_user(env->regs[16], &sc->regs.r16);
+ __get_user(env->regs[17], &sc->regs.r17);
+ __get_user(env->regs[18], &sc->regs.r18);
+ __get_user(env->regs[19], &sc->regs.r19);
+ __get_user(env->regs[20], &sc->regs.r20);
+ __get_user(env->regs[21], &sc->regs.r21);
+ __get_user(env->regs[22], &sc->regs.r22);
+ __get_user(env->regs[23], &sc->regs.r23);
+ __get_user(env->regs[24], &sc->regs.r24);
+ __get_user(env->regs[25], &sc->regs.r25);
+ __get_user(env->regs[26], &sc->regs.r26);
+ __get_user(env->regs[27], &sc->regs.r27);
+ __get_user(env->regs[28], &sc->regs.r28);
+ __get_user(env->regs[29], &sc->regs.r29);
+ __get_user(env->regs[30], &sc->regs.r30);
+ __get_user(env->regs[31], &sc->regs.r31);
+ __get_user(env->sregs[SR_PC], &sc->regs.pc);
+}
+
+static abi_ulong get_sigframe(struct target_sigaction *ka,
+ CPUState *env, int frame_size)
+{
+ abi_ulong sp = env->regs[1];
+
+ if ((ka->sa_flags & SA_ONSTACK) != 0 && !on_sig_stack(sp))
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+
+ return ((sp - frame_size) & -8UL);
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_signal_frame *frame;
+ abi_ulong frame_addr;
+ int err = 0;
+ int i;
+
+ frame_addr = get_sigframe(ka, env, sizeof *frame);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto badframe;
+
+ /* Save the mask. */
+ err |= __put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask);
+ if (err)
+ goto badframe;
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ setup_sigcontext(&frame->uc.tuc_mcontext, env);
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ /* minus 8 is offset to cater for "rtsd r15,8" offset */
+ if (ka->sa_flags & TARGET_SA_RESTORER) {
+ env->regs[15] = ((unsigned long)ka->sa_restorer)-8;
+ } else {
+ uint32_t t;
+ /* Note, these encodings are _big endian_! */
+ /* addi r12, r0, __NR_sigreturn */
+ t = 0x31800000UL | TARGET_NR_sigreturn;
+ err |= __put_user(t, frame->tramp + 0);
+ /* brki r14, 0x8 */
+ t = 0xb9cc0008UL;
+ err |= __put_user(t, frame->tramp + 1);
+
+ /* Return from sighandler will jump to the tramp.
+ Negative 8 offset because return is rtsd r15, 8 */
+ env->regs[15] = ((unsigned long)frame->tramp) - 8;
+ }
+
+ if (err)
+ goto badframe;
+
+ /* Set up registers for signal handler */
+ env->regs[1] = (unsigned long) frame;
+ /* Signal handler args: */
+ env->regs[5] = sig; /* Arg 0: signum */
+ env->regs[6] = 0;
+ env->regs[7] = (unsigned long) &frame->uc; /* arg 1: sigcontext */
+
+ /* Offset of 4 to handle microblaze rtid r14, 0 */
+ env->sregs[SR_PC] = (unsigned long)ka->_sa_handler;
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+ badframe:
+ 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)
+{
+ fprintf(stderr, "Microblaze setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct target_signal_frame *frame;
+ abi_ulong frame_addr;
+ target_sigset_t target_set;
+ sigset_t set;
+ int i;
+
+ frame_addr = env->regs[R_SP];
+ /* Make sure the guest isn't playing games. */
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1))
+ goto badframe;
+
+ /* Restore blocked signals */
+ if (__get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask))
+ goto badframe;
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ restore_sigcontext(&frame->uc.tuc_mcontext, env);
+ /* We got here through a sigreturn syscall, our path back is via an
+ rtb insn so setup r14 for that. */
+ env->regs[14] = env->sregs[SR_PC];
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[10];
+ badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+#elif defined(TARGET_CRIS)
+
+struct target_sigcontext {
+ struct target_pt_regs regs; /* needs to be first */
+ uint32_t oldmask;
+ uint32_t usp; /* usp before stacking this gunk on it */
+};
+
+/* Signal frames. */
+struct target_signal_frame {
+ struct target_sigcontext sc;
+ uint32_t extramask[TARGET_NSIG_WORDS - 1];
+ uint8_t retcode[8]; /* Trampoline code. */
+};
+
+struct rt_signal_frame {
+ struct siginfo *pinfo;
+ void *puc;
+ struct siginfo info;
+ struct ucontext uc;
+ uint8_t retcode[8]; /* Trampoline code. */
+};
+
+static void setup_sigcontext(struct target_sigcontext *sc, CPUState *env)
+{
+ __put_user(env->regs[0], &sc->regs.r0);
+ __put_user(env->regs[1], &sc->regs.r1);
+ __put_user(env->regs[2], &sc->regs.r2);
+ __put_user(env->regs[3], &sc->regs.r3);
+ __put_user(env->regs[4], &sc->regs.r4);
+ __put_user(env->regs[5], &sc->regs.r5);
+ __put_user(env->regs[6], &sc->regs.r6);
+ __put_user(env->regs[7], &sc->regs.r7);
+ __put_user(env->regs[8], &sc->regs.r8);
+ __put_user(env->regs[9], &sc->regs.r9);
+ __put_user(env->regs[10], &sc->regs.r10);
+ __put_user(env->regs[11], &sc->regs.r11);
+ __put_user(env->regs[12], &sc->regs.r12);
+ __put_user(env->regs[13], &sc->regs.r13);
+ __put_user(env->regs[14], &sc->usp);
+ __put_user(env->regs[15], &sc->regs.acr);
+ __put_user(env->pregs[PR_MOF], &sc->regs.mof);
+ __put_user(env->pregs[PR_SRP], &sc->regs.srp);
+ __put_user(env->pc, &sc->regs.erp);
+}
+
+static void restore_sigcontext(struct target_sigcontext *sc, CPUState *env)
+{
+ __get_user(env->regs[0], &sc->regs.r0);
+ __get_user(env->regs[1], &sc->regs.r1);
+ __get_user(env->regs[2], &sc->regs.r2);
+ __get_user(env->regs[3], &sc->regs.r3);
+ __get_user(env->regs[4], &sc->regs.r4);
+ __get_user(env->regs[5], &sc->regs.r5);
+ __get_user(env->regs[6], &sc->regs.r6);
+ __get_user(env->regs[7], &sc->regs.r7);
+ __get_user(env->regs[8], &sc->regs.r8);
+ __get_user(env->regs[9], &sc->regs.r9);
+ __get_user(env->regs[10], &sc->regs.r10);
+ __get_user(env->regs[11], &sc->regs.r11);
+ __get_user(env->regs[12], &sc->regs.r12);
+ __get_user(env->regs[13], &sc->regs.r13);
+ __get_user(env->regs[14], &sc->usp);
+ __get_user(env->regs[15], &sc->regs.acr);
+ __get_user(env->pregs[PR_MOF], &sc->regs.mof);
+ __get_user(env->pregs[PR_SRP], &sc->regs.srp);
+ __get_user(env->pc, &sc->regs.erp);
+}
+
+static abi_ulong get_sigframe(CPUState *env, int framesize)
+{
+ abi_ulong sp;
+ /* Align the stack downwards to 4. */
+ sp = (env->regs[R_SP] & ~3);
+ return sp - framesize;
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_signal_frame *frame;
+ abi_ulong frame_addr;
+ int err = 0;
+ int i;
+
+ frame_addr = get_sigframe(env, sizeof *frame);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto badframe;
+
+ /*
+ * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't
+ * use this trampoline anymore but it sets it up for GDB.
+ * In QEMU, using the trampoline simplifies things a bit so we use it.
+ *
+ * This is movu.w __NR_sigreturn, r9; break 13;
+ */
+ err |= __put_user(0x9c5f, frame->retcode+0);
+ err |= __put_user(TARGET_NR_sigreturn,
+ frame->retcode+2);
+ err |= __put_user(0xe93d, frame->retcode+4);
+
+ /* Save the mask. */
+ err |= __put_user(set->sig[0], &frame->sc.oldmask);
+ if (err)
+ goto badframe;
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ setup_sigcontext(&frame->sc, env);
+
+ /* Move the stack and setup the arguments for the handler. */
+ env->regs[R_SP] = (uint32_t) (unsigned long) frame;
+ env->regs[10] = sig;
+ env->pc = (unsigned long) ka->_sa_handler;
+ /* Link SRP so the guest returns through the trampoline. */
+ env->pregs[PR_SRP] = (uint32_t) (unsigned long) &frame->retcode[0];
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+ badframe:
+ 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)
+{
+ fprintf(stderr, "CRIS setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct target_signal_frame *frame;
+ abi_ulong frame_addr;
+ target_sigset_t target_set;
+ sigset_t set;
+ int i;
+
+ frame_addr = env->regs[R_SP];
+ /* Make sure the guest isn't playing games. */
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1))
+ goto badframe;
+
+ /* Restore blocked signals */
+ if (__get_user(target_set.sig[0], &frame->sc.oldmask))
+ goto badframe;
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ restore_sigcontext(&frame->sc, env);
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->regs[10];
+ badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+#elif defined(TARGET_PPC) && !defined(TARGET_PPC64)
+
+/* FIXME: Many of the structures are defined for both PPC and PPC64, but
+ the signal handling is different enough that we haven't implemented
+ support for PPC64 yet. Hence the restriction above.
+
+ There are various #if'd blocks for code for TARGET_PPC64. These
+ blocks should go away so that we can successfully run 32-bit and
+ 64-bit binaries on a QEMU configured for PPC64. */
+
+/* Size of dummy stack frame allocated when calling signal handler.
+ See arch/powerpc/include/asm/ptrace.h. */
+#if defined(TARGET_PPC64)
+#define SIGNAL_FRAMESIZE 128
+#else
+#define SIGNAL_FRAMESIZE 64
+#endif
+
+/* See arch/powerpc/include/asm/sigcontext.h. */
+struct target_sigcontext {
+ target_ulong _unused[4];
+ int32_t signal;
+#if defined(TARGET_PPC64)
+ int32_t pad0;
+#endif
+ target_ulong handler;
+ target_ulong oldmask;
+ target_ulong regs; /* struct pt_regs __user * */
+ /* TODO: PPC64 includes extra bits here. */
+};
+
+/* Indices for target_mcontext.mc_gregs, below.
+ See arch/powerpc/include/asm/ptrace.h for details. */
+enum {
+ TARGET_PT_R0 = 0,
+ TARGET_PT_R1 = 1,
+ TARGET_PT_R2 = 2,
+ TARGET_PT_R3 = 3,
+ TARGET_PT_R4 = 4,
+ TARGET_PT_R5 = 5,
+ TARGET_PT_R6 = 6,
+ TARGET_PT_R7 = 7,
+ TARGET_PT_R8 = 8,
+ TARGET_PT_R9 = 9,
+ TARGET_PT_R10 = 10,
+ TARGET_PT_R11 = 11,
+ TARGET_PT_R12 = 12,
+ TARGET_PT_R13 = 13,
+ TARGET_PT_R14 = 14,
+ TARGET_PT_R15 = 15,
+ TARGET_PT_R16 = 16,
+ TARGET_PT_R17 = 17,
+ TARGET_PT_R18 = 18,
+ TARGET_PT_R19 = 19,
+ TARGET_PT_R20 = 20,
+ TARGET_PT_R21 = 21,
+ TARGET_PT_R22 = 22,
+ TARGET_PT_R23 = 23,
+ TARGET_PT_R24 = 24,
+ TARGET_PT_R25 = 25,
+ TARGET_PT_R26 = 26,
+ TARGET_PT_R27 = 27,
+ TARGET_PT_R28 = 28,
+ TARGET_PT_R29 = 29,
+ TARGET_PT_R30 = 30,
+ TARGET_PT_R31 = 31,
+ TARGET_PT_NIP = 32,
+ TARGET_PT_MSR = 33,
+ TARGET_PT_ORIG_R3 = 34,
+ TARGET_PT_CTR = 35,
+ TARGET_PT_LNK = 36,
+ TARGET_PT_XER = 37,
+ TARGET_PT_CCR = 38,
+ /* Yes, there are two registers with #39. One is 64-bit only. */
+ TARGET_PT_MQ = 39,
+ TARGET_PT_SOFTE = 39,
+ TARGET_PT_TRAP = 40,
+ TARGET_PT_DAR = 41,
+ TARGET_PT_DSISR = 42,
+ TARGET_PT_RESULT = 43,
+ TARGET_PT_REGS_COUNT = 44
+};
+
+/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC;
+ on 64-bit PPC, sigcontext and mcontext are one and the same. */
+struct target_mcontext {
+ target_ulong mc_gregs[48];
+ /* Includes fpscr. */
+ uint64_t mc_fregs[33];
+ target_ulong mc_pad[2];
+ /* We need to handle Altivec and SPE at the same time, which no
+ kernel needs to do. Fortunately, the kernel defines this bit to
+ be Altivec-register-large all the time, rather than trying to
+ twiddle it based on the specific platform. */
+ union {
+ /* SPE vector registers. One extra for SPEFSCR. */
+ uint32_t spe[33];
+ /* Altivec vector registers. The packing of VSCR and VRSAVE
+ varies depending on whether we're PPC64 or not: PPC64 splits
+ them apart; PPC32 stuffs them together. */
+#if defined(TARGET_PPC64)
+#define QEMU_NVRREG 34
+#else
+#define QEMU_NVRREG 33
+#endif
+ ppc_avr_t altivec[QEMU_NVRREG];
+#undef QEMU_NVRREG
+ } mc_vregs __attribute__((__aligned__(16)));
+};
+
+struct target_ucontext {
+ target_ulong tuc_flags;
+ target_ulong tuc_link; /* struct ucontext __user * */
+ struct target_sigaltstack tuc_stack;
+#if !defined(TARGET_PPC64)
+ int32_t tuc_pad[7];
+ target_ulong tuc_regs; /* struct mcontext __user *
+ points to uc_mcontext field */
+#endif
+ target_sigset_t tuc_sigmask;
+#if defined(TARGET_PPC64)
+ target_sigset_t unused[15]; /* Allow for uc_sigmask growth */
+ struct target_sigcontext tuc_mcontext;
+#else
+ int32_t tuc_maskext[30];
+ int32_t tuc_pad2[3];
+ struct target_mcontext tuc_mcontext;
+#endif
+};
+
+/* See arch/powerpc/kernel/signal_32.c. */
+struct target_sigframe {
+ struct target_sigcontext sctx;
+ struct target_mcontext mctx;
+ int32_t abigap[56];
+};
+
+struct target_rt_sigframe {
+ struct target_siginfo info;
+ struct target_ucontext uc;
+ int32_t abigap[56];
+};
+
+/* We use the mc_pad field for the signal return trampoline. */
+#define tramp mc_pad
+
+/* See arch/powerpc/kernel/signal.c. */
+static target_ulong get_sigframe(struct target_sigaction *ka,
+ CPUState *env,
+ int frame_size)
+{
+ target_ulong oldsp, newsp;
+
+ oldsp = env->gpr[1];
+
+ if ((ka->sa_flags & TARGET_SA_ONSTACK) &&
+ (sas_ss_flags(oldsp))) {
+ oldsp = (target_sigaltstack_used.ss_sp
+ + target_sigaltstack_used.ss_size);
+ }
+
+ newsp = (oldsp - frame_size) & ~0xFUL;
+
+ return newsp;
+}
+
+static int save_user_regs(CPUState *env, struct target_mcontext *frame,
+ int sigret)
+{
+ target_ulong msr = env->msr;
+ int i;
+ target_ulong ccr = 0;
+
+ /* In general, the kernel attempts to be intelligent about what it
+ needs to save for Altivec/FP/SPE registers. We don't care that
+ much, so we just go ahead and save everything. */
+
+ /* Save general registers. */
+ for (i = 0; i < ARRAY_SIZE(env->gpr); i++) {
+ if (__put_user(env->gpr[i], &frame->mc_gregs[i])) {
+ return 1;
+ }
+ }
+ if (__put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP])
+ || __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR])
+ || __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK])
+ || __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]))
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(env->crf); i++) {
+ ccr |= env->crf[i] << (32 - ((i + 1) * 4));
+ }
+ if (__put_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]))
+ return 1;
+
+ /* Save Altivec registers if necessary. */
+ if (env->insns_flags & PPC_ALTIVEC) {
+ for (i = 0; i < ARRAY_SIZE(env->avr); i++) {
+ ppc_avr_t *avr = &env->avr[i];
+ ppc_avr_t *vreg = &frame->mc_vregs.altivec[i];
+
+ if (__put_user(avr->u64[0], &vreg->u64[0]) ||
+ __put_user(avr->u64[1], &vreg->u64[1])) {
+ return 1;
+ }
+ }
+ /* Set MSR_VR in the saved MSR value to indicate that
+ frame->mc_vregs contains valid data. */
+ msr |= MSR_VR;
+ if (__put_user((uint32_t)env->spr[SPR_VRSAVE],
+ &frame->mc_vregs.altivec[32].u32[3]))
+ return 1;
+ }
+
+ /* Save floating point registers. */
+ if (env->insns_flags & PPC_FLOAT) {
+ for (i = 0; i < ARRAY_SIZE(env->fpr); i++) {
+ if (__put_user(env->fpr[i], &frame->mc_fregs[i])) {
+ return 1;
+ }
+ }
+ if (__put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]))
+ return 1;
+ }
+
+ /* Save SPE registers. The kernel only saves the high half. */
+ if (env->insns_flags & PPC_SPE) {
+#if defined(TARGET_PPC64)
+ for (i = 0; i < ARRAY_SIZE(env->gpr); i++) {
+ if (__put_user(env->gpr[i] >> 32, &frame->mc_vregs.spe[i])) {
+ return 1;
+ }
+ }
+#else
+ for (i = 0; i < ARRAY_SIZE(env->gprh); i++) {
+ if (__put_user(env->gprh[i], &frame->mc_vregs.spe[i])) {
+ return 1;
+ }
+ }
+#endif
+ /* Set MSR_SPE in the saved MSR value to indicate that
+ frame->mc_vregs contains valid data. */
+ msr |= MSR_SPE;
+ if (__put_user(env->spe_fscr, &frame->mc_vregs.spe[32]))
+ return 1;
+ }
+
+ /* Store MSR. */
+ if (__put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]))
+ return 1;
+
+ /* Set up the sigreturn trampoline: li r0,sigret; sc. */
+ if (sigret) {
+ if (__put_user(0x38000000UL | sigret, &frame->tramp[0]) ||
+ __put_user(0x44000002UL, &frame->tramp[1])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int restore_user_regs(CPUState *env,
+ struct target_mcontext *frame, int sig)
+{
+ target_ulong save_r2 = 0;
+ target_ulong msr;
+ target_ulong ccr;
+
+ int i;
+
+ if (!sig) {
+ save_r2 = env->gpr[2];
+ }
+
+ /* Restore general registers. */
+ for (i = 0; i < ARRAY_SIZE(env->gpr); i++) {
+ if (__get_user(env->gpr[i], &frame->mc_gregs[i])) {
+ return 1;
+ }
+ }
+ if (__get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP])
+ || __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR])
+ || __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK])
+ || __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]))
+ return 1;
+ if (__get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]))
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(env->crf); i++) {
+ env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf;
+ }
+
+ if (!sig) {
+ env->gpr[2] = save_r2;
+ }
+ /* Restore MSR. */
+ if (__get_user(msr, &frame->mc_gregs[TARGET_PT_MSR]))
+ return 1;
+
+ /* If doing signal return, restore the previous little-endian mode. */
+ if (sig)
+ env->msr = (env->msr & ~MSR_LE) | (msr & MSR_LE);
+
+ /* Restore Altivec registers if necessary. */
+ if (env->insns_flags & PPC_ALTIVEC) {
+ for (i = 0; i < ARRAY_SIZE(env->avr); i++) {
+ ppc_avr_t *avr = &env->avr[i];
+ ppc_avr_t *vreg = &frame->mc_vregs.altivec[i];
+
+ if (__get_user(avr->u64[0], &vreg->u64[0]) ||
+ __get_user(avr->u64[1], &vreg->u64[1])) {
+ return 1;
+ }
+ }
+ /* Set MSR_VEC in the saved MSR value to indicate that
+ frame->mc_vregs contains valid data. */
+ if (__get_user(env->spr[SPR_VRSAVE],
+ (target_ulong *)(&frame->mc_vregs.altivec[32].u32[3])))
+ return 1;
+ }
+
+ /* Restore floating point registers. */
+ if (env->insns_flags & PPC_FLOAT) {
+ uint64_t fpscr;
+ for (i = 0; i < ARRAY_SIZE(env->fpr); i++) {
+ if (__get_user(env->fpr[i], &frame->mc_fregs[i])) {
+ return 1;
+ }
+ }
+ if (__get_user(fpscr, &frame->mc_fregs[32]))
+ return 1;
+ env->fpscr = (uint32_t) fpscr;
+ }
+
+ /* Save SPE registers. The kernel only saves the high half. */
+ if (env->insns_flags & PPC_SPE) {
+#if defined(TARGET_PPC64)
+ for (i = 0; i < ARRAY_SIZE(env->gpr); i++) {
+ uint32_t hi;
+
+ if (__get_user(hi, &frame->mc_vregs.spe[i])) {
+ return 1;
+ }
+ env->gpr[i] = ((uint64_t)hi << 32) | ((uint32_t) env->gpr[i]);
+ }
+#else
+ for (i = 0; i < ARRAY_SIZE(env->gprh); i++) {
+ if (__get_user(env->gprh[i], &frame->mc_vregs.spe[i])) {
+ return 1;
+ }
+ }
+#endif
+ if (__get_user(env->spe_fscr, &frame->mc_vregs.spe[32]))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_sigframe *frame;
+ struct target_sigcontext *sc;
+ target_ulong frame_addr, newsp;
+ int err = 0;
+ int signal;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1))
+ goto sigsegv;
+ sc = &frame->sctx;
+
+ signal = current_exec_domain_sig(sig);
+
+ err |= __put_user(h2g(ka->_sa_handler), &sc->handler);
+ err |= __put_user(set->sig[0], &sc->oldmask);
+#if defined(TARGET_PPC64)
+ err |= __put_user(set->sig[0] >> 32, &sc->_unused[3]);
+#else
+ err |= __put_user(set->sig[1], &sc->_unused[3]);
+#endif
+ err |= __put_user(h2g(&frame->mctx), &sc->regs);
+ err |= __put_user(sig, &sc->signal);
+
+ /* Save user regs. */
+ err |= save_user_regs(env, &frame->mctx, TARGET_NR_sigreturn);
+
+ /* The kernel checks for the presence of a VDSO here. We don't
+ emulate a vdso, so use a sigreturn system call. */
+ env->lr = (target_ulong) h2g(frame->mctx.tramp);
+
+ /* Turn off all fp exceptions. */
+ env->fpscr = 0;
+
+ /* Create a stack frame for the caller of the handler. */
+ newsp = frame_addr - SIGNAL_FRAMESIZE;
+ err |= __put_user(env->gpr[1], (target_ulong *)(uintptr_t) newsp);
+
+ if (err)
+ goto sigsegv;
+
+ /* Set up registers for signal handler. */
+ env->gpr[1] = newsp;
+ env->gpr[3] = signal;
+ env->gpr[4] = (target_ulong) h2g(sc);
+ env->nip = (target_ulong) ka->_sa_handler;
+ /* Signal handlers are entered in big-endian mode. */
+ env->msr &= ~MSR_LE;
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ if (logfile)
+ fprintf (logfile, "segfaulting from setup_frame\n");
+ 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)
+{
+ struct target_rt_sigframe *rt_sf;
+ struct target_mcontext *frame;
+ target_ulong rt_sf_addr, newsp = 0;
+ int i, err = 0;
+ int signal;
+
+ rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf));
+ if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1))
+ goto sigsegv;
+
+ signal = current_exec_domain_sig(sig);
+
+ err |= copy_siginfo_to_user(&rt_sf->info, info);
+
+ err |= __put_user(0, &rt_sf->uc.tuc_flags);
+ err |= __put_user(0, &rt_sf->uc.tuc_link);
+ err |= __put_user((target_ulong)target_sigaltstack_used.ss_sp,
+ &rt_sf->uc.tuc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(env->gpr[1]),
+ &rt_sf->uc.tuc_stack.ss_flags);
+ err |= __put_user(target_sigaltstack_used.ss_size,
+ &rt_sf->uc.tuc_stack.ss_size);
+ err |= __put_user(h2g (&rt_sf->uc.tuc_mcontext),
+ &rt_sf->uc.tuc_regs);
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ err |= __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]);
+ }
+
+ frame = &rt_sf->uc.tuc_mcontext;
+ err |= save_user_regs(env, frame, TARGET_NR_rt_sigreturn);
+
+ /* The kernel checks for the presence of a VDSO here. We don't
+ emulate a vdso, so use a sigreturn system call. */
+ env->lr = (target_ulong) h2g(frame->tramp);
+
+ /* Turn off all fp exceptions. */
+ env->fpscr = 0;
+
+ /* Create a stack frame for the caller of the handler. */
+ newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16);
+ err |= __put_user(env->gpr[1], (target_ulong *)(uintptr_t) newsp);
+
+ if (err)
+ goto sigsegv;
+
+ /* Set up registers for signal handler. */
+ env->gpr[1] = newsp;
+ env->gpr[3] = (target_ulong) signal;
+ env->gpr[4] = (target_ulong) h2g(&rt_sf->info);
+ env->gpr[5] = (target_ulong) h2g(&rt_sf->uc);
+ env->gpr[6] = (target_ulong) h2g(rt_sf);
+ env->nip = (target_ulong) ka->_sa_handler;
+ /* Signal handlers are entered in big-endian mode. */
+ env->msr &= ~MSR_LE;
+
+ unlock_user_struct(rt_sf, rt_sf_addr, 1);
+ return;
+
+sigsegv:
+ unlock_user_struct(rt_sf, rt_sf_addr, 1);
+ if (logfile)
+ fprintf (logfile, "segfaulting from setup_rt_frame\n");
+ force_sig(TARGET_SIGSEGV);
+
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct target_sigcontext *sc = NULL;
+ struct target_mcontext *sr = NULL;
+ target_ulong sr_addr, sc_addr;
+ sigset_t blocked;
+ target_sigset_t set;
+
+ sc_addr = env->gpr[1] + SIGNAL_FRAMESIZE;
+ if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1))
+ goto sigsegv;
+
+#if defined(TARGET_PPC64)
+ set.sig[0] = sc->oldmask + ((long)(sc->_unused[3]) << 32);
+#else
+ if(__get_user(set.sig[0], &sc->oldmask) ||
+ __get_user(set.sig[1], &sc->_unused[3]))
+ goto sigsegv;
+#endif
+ target_to_host_sigset_internal(&blocked, &set);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+
+ if (__get_user(sr_addr, &sc->regs))
+ goto sigsegv;
+ if (!lock_user_struct(VERIFY_READ, sr, sr_addr, 1))
+ goto sigsegv;
+ if (restore_user_regs(env, sr, 1))
+ goto sigsegv;
+
+ unlock_user_struct(sr, sr_addr, 1);
+ unlock_user_struct(sc, sc_addr, 1);
+ return -TARGET_QEMU_ESIGRETURN;
+
+sigsegv:
+ unlock_user_struct(sr, sr_addr, 1);
+ unlock_user_struct(sc, sc_addr, 1);
+ if (logfile)
+ fprintf (logfile, "segfaulting from do_sigreturn\n");
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+/* See arch/powerpc/kernel/signal_32.c. */
+static int do_setcontext(struct target_ucontext *ucp, CPUState *env, int sig)
+{
+ struct target_mcontext *mcp;
+ target_ulong mcp_addr;
+ sigset_t blocked;
+ target_sigset_t set;
+
+ if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, tuc_sigmask),
+ sizeof (set)))
+ return 1;
+
+#if defined(TARGET_PPC64)
+ fprintf (stderr, "do_setcontext: not implemented\n");
+ return 0;
+#else
+ if (__get_user(mcp_addr, &ucp->tuc_regs))
+ return 1;
+
+ if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1))
+ return 1;
+
+ target_to_host_sigset_internal(&blocked, &set);
+ sigprocmask(SIG_SETMASK, &blocked, NULL);
+ if (restore_user_regs(env, mcp, sig))
+ goto sigsegv;
+
+ unlock_user_struct(mcp, mcp_addr, 1);
+ return 0;
+
+sigsegv:
+ unlock_user_struct(mcp, mcp_addr, 1);
+ return 1;
+#endif
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ struct target_rt_sigframe *rt_sf = NULL;
+ target_ulong rt_sf_addr;
+
+ rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + 16;
+ if (!lock_user_struct(VERIFY_READ, rt_sf, rt_sf_addr, 1))
+ goto sigsegv;
+
+ if (do_setcontext(&rt_sf->uc, env, 1))
+ goto sigsegv;
+
+ do_sigaltstack(rt_sf_addr
+ + offsetof(struct target_rt_sigframe, uc.tuc_stack),
+ 0, env->gpr[1]);
+
+ unlock_user_struct(rt_sf, rt_sf_addr, 1);
+ return -TARGET_QEMU_ESIGRETURN;
+
+sigsegv:
+ unlock_user_struct(rt_sf, rt_sf_addr, 1);
+ if (logfile)
+ fprintf (logfile, "segfaulting from do_rt_sigreturn\n");
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+#elif defined(TARGET_M68K)
+
+struct target_sigcontext {
+ abi_ulong sc_mask;
+ abi_ulong sc_usp;
+ abi_ulong sc_d0;
+ abi_ulong sc_d1;
+ abi_ulong sc_a0;
+ abi_ulong sc_a1;
+ unsigned short sc_sr;
+ abi_ulong sc_pc;
+};
+
+struct target_sigframe
+{
+ abi_ulong pretcode;
+ int sig;
+ int code;
+ abi_ulong psc;
+ char retcode[8];
+ abi_ulong extramask[TARGET_NSIG_WORDS-1];
+ struct target_sigcontext sc;
+};
+
+typedef int target_greg_t;
+#define TARGET_NGREG 18
+typedef target_greg_t target_gregset_t[TARGET_NGREG];
+
+typedef struct target_fpregset {
+ int f_fpcntl[3];
+ int f_fpregs[8*3];
+} target_fpregset_t;
+
+struct target_mcontext {
+ int version;
+ target_gregset_t gregs;
+ target_fpregset_t fpregs;
+};
+
+#define TARGET_MCONTEXT_VERSION 2
+
+struct target_ucontext {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ target_stack_t tuc_stack;
+ struct target_mcontext tuc_mcontext;
+ abi_long tuc_filler[80];
+ target_sigset_t tuc_sigmask;
+};
+
+struct target_rt_sigframe
+{
+ abi_ulong pretcode;
+ int sig;
+ abi_ulong pinfo;
+ abi_ulong puc;
+ char retcode[8];
+ struct target_siginfo info;
+ struct target_ucontext uc;
+};
+
+static int
+setup_sigcontext(struct target_sigcontext *sc, CPUState *env, abi_ulong mask)
+{
+ int err = 0;
+
+ err |= __put_user(mask, &sc->sc_mask);
+ err |= __put_user(env->aregs[7], &sc->sc_usp);
+ err |= __put_user(env->dregs[0], &sc->sc_d0);
+ err |= __put_user(env->dregs[1], &sc->sc_d1);
+ err |= __put_user(env->aregs[0], &sc->sc_a0);
+ err |= __put_user(env->aregs[1], &sc->sc_a1);
+ err |= __put_user(env->sr, &sc->sc_sr);
+ err |= __put_user(env->pc, &sc->sc_pc);
+
+ return err;
+}
+
+static int
+restore_sigcontext(CPUState *env, struct target_sigcontext *sc, int *pd0)
+{
+ int err = 0;
+ int temp;
+
+ err |= __get_user(env->aregs[7], &sc->sc_usp);
+ err |= __get_user(env->dregs[1], &sc->sc_d1);
+ err |= __get_user(env->aregs[0], &sc->sc_a0);
+ err |= __get_user(env->aregs[1], &sc->sc_a1);
+ err |= __get_user(env->pc, &sc->sc_pc);
+ err |= __get_user(temp, &sc->sc_sr);
+ env->sr = (env->sr & 0xff00) | (temp & 0xff);
+
+ *pd0 = tswapl(sc->sc_d0);
+
+ return err;
+}
+
+/*
+ * Determine which stack to use..
+ */
+static inline abi_ulong
+get_sigframe(struct target_sigaction *ka, CPUState *regs, size_t frame_size)
+{
+ unsigned long sp;
+
+ sp = regs->aregs[7];
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) {
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+
+ return ((sp - frame_size) & -8UL);
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_sigframe *frame;
+ abi_ulong frame_addr;
+ abi_ulong retcode_addr;
+ abi_ulong sc_addr;
+ int err = 0;
+ int i;
+
+ frame_addr = get_sigframe(ka, env, sizeof *frame);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ err |= __put_user(sig, &frame->sig);
+
+ sc_addr = frame_addr + offsetof(struct target_sigframe, sc);
+ err |= __put_user(sc_addr, &frame->psc);
+
+ err |= setup_sigcontext(&frame->sc, env, set->sig[0]);
+ if (err)
+ goto give_sigsegv;
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->extramask[i - 1]))
+ goto give_sigsegv;
+ }
+
+ /* Set up to return from userspace. */
+
+ retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode);
+ err |= __put_user(retcode_addr, &frame->pretcode);
+
+ /* moveq #,d0; trap #0 */
+
+ err |= __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16),
+ (long *)(frame->retcode));
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace */
+
+ env->aregs[7] = frame_addr;
+ env->pc = ka->_sa_handler;
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV);
+}
+
+static inline int target_rt_setup_ucontext(struct target_ucontext *uc,
+ CPUState *env)
+{
+ target_greg_t *gregs = uc->tuc_mcontext.gregs;
+ int err;
+
+ err = __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version);
+ err |= __put_user(env->dregs[0], &gregs[0]);
+ err |= __put_user(env->dregs[1], &gregs[1]);
+ err |= __put_user(env->dregs[2], &gregs[2]);
+ err |= __put_user(env->dregs[3], &gregs[3]);
+ err |= __put_user(env->dregs[4], &gregs[4]);
+ err |= __put_user(env->dregs[5], &gregs[5]);
+ err |= __put_user(env->dregs[6], &gregs[6]);
+ err |= __put_user(env->dregs[7], &gregs[7]);
+ err |= __put_user(env->aregs[0], &gregs[8]);
+ err |= __put_user(env->aregs[1], &gregs[9]);
+ err |= __put_user(env->aregs[2], &gregs[10]);
+ err |= __put_user(env->aregs[3], &gregs[11]);
+ err |= __put_user(env->aregs[4], &gregs[12]);
+ err |= __put_user(env->aregs[5], &gregs[13]);
+ err |= __put_user(env->aregs[6], &gregs[14]);
+ err |= __put_user(env->aregs[7], &gregs[15]);
+ err |= __put_user(env->pc, &gregs[16]);
+ err |= __put_user(env->sr, &gregs[17]);
+
+ return err;
+}
+
+static inline int target_rt_restore_ucontext(CPUState *env,
+ struct target_ucontext *uc,
+ int *pd0)
+{
+ int temp;
+ int err;
+ target_greg_t *gregs = uc->tuc_mcontext.gregs;
+
+ err = __get_user(temp, &uc->tuc_mcontext.version);
+ if (temp != TARGET_MCONTEXT_VERSION)
+ goto badframe;
+
+ /* restore passed registers */
+ err |= __get_user(env->dregs[0], &gregs[0]);
+ err |= __get_user(env->dregs[1], &gregs[1]);
+ err |= __get_user(env->dregs[2], &gregs[2]);
+ err |= __get_user(env->dregs[3], &gregs[3]);
+ err |= __get_user(env->dregs[4], &gregs[4]);
+ err |= __get_user(env->dregs[5], &gregs[5]);
+ err |= __get_user(env->dregs[6], &gregs[6]);
+ err |= __get_user(env->dregs[7], &gregs[7]);
+ err |= __get_user(env->aregs[0], &gregs[8]);
+ err |= __get_user(env->aregs[1], &gregs[9]);
+ err |= __get_user(env->aregs[2], &gregs[10]);
+ err |= __get_user(env->aregs[3], &gregs[11]);
+ err |= __get_user(env->aregs[4], &gregs[12]);
+ err |= __get_user(env->aregs[5], &gregs[13]);
+ err |= __get_user(env->aregs[6], &gregs[14]);
+ err |= __get_user(env->aregs[7], &gregs[15]);
+ err |= __get_user(env->pc, &gregs[16]);
+ err |= __get_user(temp, &gregs[17]);
+ env->sr = (env->sr & 0xff00) | (temp & 0xff);
+
+ *pd0 = env->dregs[0];
+ return err;
+
+badframe:
+ return 1;
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ struct target_rt_sigframe *frame;
+ abi_ulong frame_addr;
+ abi_ulong retcode_addr;
+ abi_ulong info_addr;
+ abi_ulong uc_addr;
+ int err = 0;
+ int i;
+
+ frame_addr = get_sigframe(ka, env, sizeof *frame);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ goto give_sigsegv;
+
+ err |= __put_user(sig, &frame->sig);
+
+ info_addr = frame_addr + offsetof(struct target_rt_sigframe, info);
+ err |= __put_user(info_addr, &frame->pinfo);
+
+ uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc);
+ err |= __put_user(uc_addr, &frame->puc);
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ /* Create the ucontext */
+
+ err |= __put_user(0, &frame->uc.tuc_flags);
+ err |= __put_user(0, &frame->uc.tuc_link);
+ err |= __put_user(target_sigaltstack_used.ss_sp,
+ &frame->uc.tuc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(env->aregs[7]),
+ &frame->uc.tuc_stack.ss_flags);
+ err |= __put_user(target_sigaltstack_used.ss_size,
+ &frame->uc.tuc_stack.ss_size);
+ err |= target_rt_setup_ucontext(&frame->uc, env);
+
+ if (err)
+ goto give_sigsegv;
+
+ for(i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]))
+ goto give_sigsegv;
+ }
+
+ /* Set up to return from userspace. */
+
+ retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode);
+ err |= __put_user(retcode_addr, &frame->pretcode);
+
+ /* moveq #,d0; notb d0; trap #0 */
+
+ err |= __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16),
+ (long *)(frame->retcode + 0));
+ err |= __put_user(0x4e40, (short *)(frame->retcode + 4));
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace */
+
+ env->aregs[7] = frame_addr;
+ env->pc = ka->_sa_handler;
+
+ unlock_user_struct(frame, frame_addr, 1);
+ return;
+
+give_sigsegv:
+ unlock_user_struct(frame, frame_addr, 1);
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct target_sigframe *frame;
+ abi_ulong frame_addr = env->aregs[7] - 4;
+ target_sigset_t target_set;
+ sigset_t set;
+ int d0, i;
+
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ /* set blocked signals */
+
+ if (__get_user(target_set.sig[0], &frame->sc.sc_mask))
+ goto badframe;
+
+ for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.sig[i], &frame->extramask[i - 1]))
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ /* restore registers */
+
+ if (restore_sigcontext(env, &frame->sc, &d0))
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return d0;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ struct target_rt_sigframe *frame;
+ abi_ulong frame_addr = env->aregs[7] - 4;
+ target_sigset_t target_set;
+ sigset_t set;
+ int d0;
+
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
+ goto badframe;
+
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ /* restore registers */
+
+ if (target_rt_restore_ucontext(env, &frame->uc, &d0))
+ goto badframe;
+
+ if (do_sigaltstack(frame_addr +
+ offsetof(struct target_rt_sigframe, uc.tuc_stack),
+ 0, get_sp_from_cpustate(env)) == -EFAULT)
+ goto badframe;
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return d0;
+
+badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return 0;
+}
+
+#elif defined(TARGET_ALPHA)
+
+struct target_sigcontext {
+ abi_long sc_onstack;
+ abi_long sc_mask;
+ abi_long sc_pc;
+ abi_long sc_ps;
+ abi_long sc_regs[32];
+ abi_long sc_ownedfp;
+ abi_long sc_fpregs[32];
+ abi_ulong sc_fpcr;
+ abi_ulong sc_fp_control;
+ abi_ulong sc_reserved1;
+ abi_ulong sc_reserved2;
+ abi_ulong sc_ssize;
+ abi_ulong sc_sbase;
+ abi_ulong sc_traparg_a0;
+ abi_ulong sc_traparg_a1;
+ abi_ulong sc_traparg_a2;
+ abi_ulong sc_fp_trap_pc;
+ abi_ulong sc_fp_trigger_sum;
+ abi_ulong sc_fp_trigger_inst;
+};
+
+struct target_ucontext {
+ abi_ulong tuc_flags;
+ abi_ulong tuc_link;
+ abi_ulong tuc_osf_sigmask;
+ target_stack_t tuc_stack;
+ struct target_sigcontext tuc_mcontext;
+ target_sigset_t tuc_sigmask;
+};
+
+struct target_sigframe {
+ struct target_sigcontext sc;
+ unsigned int retcode[3];
+};
+
+struct target_rt_sigframe {
+ target_siginfo_t info;
+ struct target_ucontext uc;
+ unsigned int retcode[3];
+};
+
+#define INSN_MOV_R30_R16 0x47fe0410
+#define INSN_LDI_R0 0x201f0000
+#define INSN_CALLSYS 0x00000083
+
+static int setup_sigcontext(struct target_sigcontext *sc, CPUState *env,
+ abi_ulong frame_addr, target_sigset_t *set)
+{
+ int i, err = 0;
+
+ err |= __put_user(on_sig_stack(frame_addr), &sc->sc_onstack);
+ err |= __put_user(set->sig[0], &sc->sc_mask);
+ err |= __put_user(env->pc, &sc->sc_pc);
+ err |= __put_user(8, &sc->sc_ps);
+
+ for (i = 0; i < 31; ++i) {
+ err |= __put_user(env->ir[i], &sc->sc_regs[i]);
+ }
+ err |= __put_user(0, &sc->sc_regs[31]);
+
+ for (i = 0; i < 31; ++i) {
+ err |= __put_user(env->fir[i], &sc->sc_fpregs[i]);
+ }
+ err |= __put_user(0, &sc->sc_fpregs[31]);
+ err |= __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr);
+
+ err |= __put_user(0, &sc->sc_traparg_a0); /* FIXME */
+ err |= __put_user(0, &sc->sc_traparg_a1); /* FIXME */
+ err |= __put_user(0, &sc->sc_traparg_a2); /* FIXME */
+
+ return err;
+}
+
+static int restore_sigcontext(CPUState *env, struct target_sigcontext *sc)
+{
+ uint64_t fpcr;
+ int i, err = 0;
+
+ err |= __get_user(env->pc, &sc->sc_pc);
+
+ for (i = 0; i < 31; ++i) {
+ err |= __get_user(env->ir[i], &sc->sc_regs[i]);
+ }
+ for (i = 0; i < 31; ++i) {
+ err |= __get_user(env->fir[i], &sc->sc_fpregs[i]);
+ }
+
+ err |= __get_user(fpcr, &sc->sc_fpcr);
+ cpu_alpha_store_fpcr(env, fpcr);
+
+ return err;
+}
+
+static inline abi_ulong get_sigframe(struct target_sigaction *sa,
+ CPUState *env, unsigned long framesize)
+{
+ abi_ulong sp = env->ir[IR_SP];
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) {
+ sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
+ }
+ return (sp - framesize) & -32;
+}
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ abi_ulong frame_addr, r26;
+ struct target_sigframe *frame;
+ int err = 0;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+ goto give_sigsegv;
+ }
+
+ err |= setup_sigcontext(&frame->sc, env, frame_addr, set);
+
+ if (ka->sa_restorer) {
+ r26 = ka->sa_restorer;
+ } else {
+ err |= __put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
+ err |= __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn,
+ &frame->retcode[1]);
+ err |= __put_user(INSN_CALLSYS, &frame->retcode[2]);
+ /* imb() */
+ r26 = frame_addr;
+ }
+
+ unlock_user_struct(frame, frame_addr, 1);
+
+ if (err) {
+ give_sigsegv:
+ if (sig == TARGET_SIGSEGV) {
+ ka->_sa_handler = TARGET_SIG_DFL;
+ }
+ force_sig(TARGET_SIGSEGV);
+ }
+
+ env->ir[IR_RA] = r26;
+ env->ir[IR_PV] = env->pc = ka->_sa_handler;
+ env->ir[IR_A0] = sig;
+ env->ir[IR_A1] = 0;
+ env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc);
+ env->ir[IR_SP] = frame_addr;
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ abi_ulong frame_addr, r26;
+ struct target_rt_sigframe *frame;
+ int i, err = 0;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+ goto give_sigsegv;
+ }
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+
+ err |= __put_user(0, &frame->uc.tuc_flags);
+ err |= __put_user(0, &frame->uc.tuc_link);
+ err |= __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask);
+ err |= __put_user(target_sigaltstack_used.ss_sp,
+ &frame->uc.tuc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(env->ir[IR_SP]),
+ &frame->uc.tuc_stack.ss_flags);
+ err |= __put_user(target_sigaltstack_used.ss_size,
+ &frame->uc.tuc_stack.ss_size);
+ err |= setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set);
+ for (i = 0; i < TARGET_NSIG_WORDS; ++i) {
+ err |= __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
+ }
+
+ if (ka->sa_restorer) {
+ r26 = ka->sa_restorer;
+ } else {
+ err |= __put_user(INSN_MOV_R30_R16, &frame->retcode[0]);
+ err |= __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn,
+ &frame->retcode[1]);
+ err |= __put_user(INSN_CALLSYS, &frame->retcode[2]);
+ /* imb(); */
+ r26 = frame_addr;
+ }
+
+ if (err) {
+ give_sigsegv:
+ if (sig == TARGET_SIGSEGV) {
+ ka->_sa_handler = TARGET_SIG_DFL;
+ }
+ force_sig(TARGET_SIGSEGV);
+ }
+
+ env->ir[IR_RA] = r26;
+ env->ir[IR_PV] = env->pc = ka->_sa_handler;
+ env->ir[IR_A0] = sig;
+ env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info);
+ env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
+ env->ir[IR_SP] = frame_addr;
+}
+
+long do_sigreturn(CPUState *env)
+{
+ struct target_sigcontext *sc;
+ abi_ulong sc_addr = env->ir[IR_A0];
+ target_sigset_t target_set;
+ sigset_t set;
+
+ if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) {
+ goto badframe;
+ }
+
+ target_sigemptyset(&target_set);
+ if (__get_user(target_set.sig[0], &sc->sc_mask)) {
+ goto badframe;
+ }
+
+ target_to_host_sigset_internal(&set, &target_set);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ if (restore_sigcontext(env, sc)) {
+ goto badframe;
+ }
+ unlock_user_struct(sc, sc_addr, 0);
+ return env->ir[IR_V0];
+
+ badframe:
+ unlock_user_struct(sc, sc_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ abi_ulong frame_addr = env->ir[IR_A0];
+ struct target_rt_sigframe *frame;
+ sigset_t set;
+
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+ goto badframe;
+ }
+ target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
+ sigprocmask(SIG_SETMASK, &set, NULL);
+
+ if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) {
+ goto badframe;
+ }
+ if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe,
+ uc.tuc_stack),
+ 0, env->ir[IR_SP]) == -EFAULT) {
+ goto badframe;
+ }
+
+ unlock_user_struct(frame, frame_addr, 0);
+ return env->ir[IR_V0];
+
+
+ badframe:
+ unlock_user_struct(frame, frame_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+}
+
+#else
+
+static void setup_frame(int sig, struct target_sigaction *ka,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_frame: not implemented\n");
+}
+
+static void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUState *env)
+{
+ fprintf(stderr, "setup_rt_frame: not implemented\n");
+}
+
+long do_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+long do_rt_sigreturn(CPUState *env)
+{
+ fprintf(stderr, "do_rt_sigreturn: not implemented\n");
+ return -TARGET_ENOSYS;
+}
+
+#endif
+
+void process_pending_signals(CPUState *cpu_env)
+{
+ int sig;
+ abi_ulong handler;
+ sigset_t set, old_set;
+ target_sigset_t target_old_set;
+ struct emulated_sigtable *k;
+ struct target_sigaction *sa;
+ struct sigqueue *q;
+ TaskState *ts = cpu_env->opaque;
+
+ if (!ts->signal_pending)
+ return;
+
+ /* FIXME: This is not threadsafe. */
+ k = ts->sigtab;
+ for(sig = 1; sig <= TARGET_NSIG; sig++) {
+ if (k->pending)
+ goto handle_signal;
+ k++;
+ }
+ /* if no signal is pending, just return */
+ ts->signal_pending = 0;
+ return;
+
+ handle_signal:
+#ifdef DEBUG_SIGNAL
+ fprintf(stderr, "qemu: process signal %d\n", sig);
+#endif
+ /* dequeue signal */
+ q = k->first;
+ k->first = q->next;
+ if (!k->first)
+ k->pending = 0;
+
+ sig = gdb_handlesig (cpu_env, sig);
+ if (!sig) {
+ sa = NULL;
+ handler = TARGET_SIG_IGN;
+ } else {
+ sa = &sigact_table[sig - 1];
+ handler = sa->_sa_handler;
+ }
+
+ if (handler == TARGET_SIG_DFL) {
+ /* default handler : ignore some signal. The other are job control or fatal */
+ if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) {
+ kill(getpid(),SIGSTOP);
+ } else if (sig != TARGET_SIGCHLD &&
+ sig != TARGET_SIGURG &&
+ sig != TARGET_SIGWINCH &&
+ sig != TARGET_SIGCONT) {
+ force_sig(sig);
+ }
+ } else if (handler == TARGET_SIG_IGN) {
+ /* ignore sig */
+ } else if (handler == TARGET_SIG_ERR) {
+ force_sig(sig);
+ } else {
+ /* compute the blocked signals during the handler execution */
+ target_to_host_sigset(&set, &sa->sa_mask);
+ /* SA_NODEFER indicates that the current signal should not be
+ blocked during the handler */
+ if (!(sa->sa_flags & TARGET_SA_NODEFER))
+ sigaddset(&set, target_to_host_signal(sig));
+
+ /* block signals in the handler using Linux */
+ sigprocmask(SIG_BLOCK, &set, &old_set);
+ /* save the previous blocked signal state to restore it at the
+ end of the signal execution (see do_sigreturn) */
+ host_to_target_sigset_internal(&target_old_set, &old_set);
+
+ /* if the CPU is in VM86 mode, we restore the 32 bit values */
+#if defined(TARGET_I386) && !defined(TARGET_X86_64)
+ {
+ CPUX86State *env = cpu_env;
+ if (env->eflags & VM_MASK)
+ save_v86_state(env);
+ }
+#endif
+ /* prepare the stack frame of the virtual CPU */
+ if (sa->sa_flags & TARGET_SA_SIGINFO)
+ setup_rt_frame(sig, sa, &q->info, &target_old_set, cpu_env);
+ else
+ setup_frame(sig, sa, &target_old_set, cpu_env);
+ if (sa->sa_flags & TARGET_SA_RESETHAND)
+ sa->_sa_handler = TARGET_SIG_DFL;
+ }
+ if (q != &k->info)
+ free_sigqueue(cpu_env, q);
+}
diff --git a/linux-user/socket.h b/linux-user/socket.h
new file mode 100644
index 0000000..93d4782
--- /dev/null
+++ b/linux-user/socket.h
@@ -0,0 +1,147 @@
+
+#if defined(TARGET_MIPS)
+ // MIPS special values for constants
+
+ /*
+ * For setsockopt(2)
+ *
+ * This defines are ABI conformant as far as Linux supports these ...
+ */
+ #define TARGET_SOL_SOCKET 0xffff
+
+ #define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */
+ #define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */
+ #define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send
+ SIGPIPE when they die. */
+ #define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */
+ #define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of
+ broadcast messages. */
+ #define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable
+ socket to transmit pending data. */
+ #define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. */
+ #if 0
+ To add: #define TARGET_SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */
+ #endif
+
+ #define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */
+ #define TARGET_SO_STYLE SO_TYPE /* Synonym */
+ #define TARGET_SO_ERROR 0x1007 /* get error status and clear */
+ #define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */
+ #define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */
+ #define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */
+ #define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */
+ #define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */
+ #define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */
+ #define TARGET_SO_ACCEPTCONN 0x1009
+
+ /* linux-specific, might as well be the same as on i386 */
+ #define TARGET_SO_NO_CHECK 11
+ #define TARGET_SO_PRIORITY 12
+ #define TARGET_SO_BSDCOMPAT 14
+
+ #define TARGET_SO_PASSCRED 17
+ #define TARGET_SO_PEERCRED 18
+
+ /* Security levels - as per NRL IPv6 - don't actually do anything */
+ #define TARGET_SO_SECURITY_AUTHENTICATION 22
+ #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23
+ #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24
+
+ #define TARGET_SO_BINDTODEVICE 25
+
+ /* Socket filtering */
+ #define TARGET_SO_ATTACH_FILTER 26
+ #define TARGET_SO_DETACH_FILTER 27
+
+ #define TARGET_SO_PEERNAME 28
+ #define TARGET_SO_TIMESTAMP 29
+ #define SCM_TIMESTAMP SO_TIMESTAMP
+
+ #define TARGET_SO_PEERSEC 30
+ #define TARGET_SO_SNDBUFFORCE 31
+ #define TARGET_SO_RCVBUFFORCE 33
+
+ /** sock_type - Socket types
+ *
+ * Please notice that for binary compat reasons MIPS has to
+ * override the enum sock_type in include/linux/net.h, so
+ * we define ARCH_HAS_SOCKET_TYPES here.
+ *
+ * @SOCK_DGRAM - datagram (conn.less) socket
+ * @SOCK_STREAM - stream (connection) socket
+ * @SOCK_RAW - raw socket
+ * @SOCK_RDM - reliably-delivered message
+ * @SOCK_SEQPACKET - sequential packet socket
+ * @SOCK_PACKET - linux specific way of getting packets at the dev level.
+ * For writing rarp and other similar things on the user level.
+ */
+ enum sock_type {
+ TARGET_SOCK_DGRAM = 1,
+ TARGET_SOCK_STREAM = 2,
+ TARGET_SOCK_RAW = 3,
+ TARGET_SOCK_RDM = 4,
+ TARGET_SOCK_SEQPACKET = 5,
+ TARGET_SOCK_DCCP = 6,
+ TARGET_SOCK_PACKET = 10,
+ };
+
+ #define TARGET_SOCK_MAX (SOCK_PACKET + 1)
+
+#else
+
+ /* For setsockopt(2) */
+ #define TARGET_SOL_SOCKET 1
+
+ #define TARGET_SO_DEBUG 1
+ #define TARGET_SO_REUSEADDR 2
+ #define TARGET_SO_TYPE 3
+ #define TARGET_SO_ERROR 4
+ #define TARGET_SO_DONTROUTE 5
+ #define TARGET_SO_BROADCAST 6
+ #define TARGET_SO_SNDBUF 7
+ #define TARGET_SO_RCVBUF 8
+ #define TARGET_SO_SNDBUFFORCE 32
+ #define TARGET_SO_RCVBUFFORCE 33
+ #define TARGET_SO_KEEPALIVE 9
+ #define TARGET_SO_OOBINLINE 10
+ #define TARGET_SO_NO_CHECK 11
+ #define TARGET_SO_PRIORITY 12
+ #define TARGET_SO_LINGER 13
+ #define TARGET_SO_BSDCOMPAT 14
+ /* To add :#define TARGET_SO_REUSEPORT 15 */
+#if defined(TARGET_PPC)
+ #define TARGET_SO_RCVLOWAT 16
+ #define TARGET_SO_SNDLOWAT 17
+ #define TARGET_SO_RCVTIMEO 18
+ #define TARGET_SO_SNDTIMEO 19
+ #define TARGET_SO_PASSCRED 20
+ #define TARGET_SO_PEERCRED 21
+#else
+ #define TARGET_SO_PASSCRED 16
+ #define TARGET_SO_PEERCRED 17
+ #define TARGET_SO_RCVLOWAT 18
+ #define TARGET_SO_SNDLOWAT 19
+ #define TARGET_SO_RCVTIMEO 20
+ #define TARGET_SO_SNDTIMEO 21
+#endif
+
+ /* Security levels - as per NRL IPv6 - don't actually do anything */
+ #define TARGET_SO_SECURITY_AUTHENTICATION 22
+ #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23
+ #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24
+
+ #define TARGET_SO_BINDTODEVICE 25
+
+ /* Socket filtering */
+ #define TARGET_SO_ATTACH_FILTER 26
+ #define TARGET_SO_DETACH_FILTER 27
+
+ #define TARGET_SO_PEERNAME 28
+ #define TARGET_SO_TIMESTAMP 29
+ #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP
+
+ #define TARGET_SO_ACCEPTCONN 30
+
+ #define TARGET_SO_PEERSEC 31
+
+#endif
diff --git a/linux-user/sparc/syscall.h b/linux-user/sparc/syscall.h
new file mode 100644
index 0000000..5a9bb7e
--- /dev/null
+++ b/linux-user/sparc/syscall.h
@@ -0,0 +1,9 @@
+struct target_pt_regs {
+ abi_ulong psr;
+ abi_ulong pc;
+ abi_ulong npc;
+ abi_ulong y;
+ abi_ulong u_regs[16];
+};
+
+#define UNAME_MACHINE "sun4"
diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h
new file mode 100644
index 0000000..5d1ac21
--- /dev/null
+++ b/linux-user/sparc/syscall_nr.h
@@ -0,0 +1,287 @@
+#define TARGET_NR_exit 1 /* Common */
+#define TARGET_NR_fork 2 /* Common */
+#define TARGET_NR_read 3 /* Common */
+#define TARGET_NR_write 4 /* Common */
+#define TARGET_NR_open 5 /* Common */
+#define TARGET_NR_close 6 /* Common */
+#define TARGET_NR_wait4 7 /* Common */
+#define TARGET_NR_creat 8 /* Common */
+#define TARGET_NR_link 9 /* Common */
+#define TARGET_NR_unlink 10 /* Common */
+#define TARGET_NR_execv 11 /* SunOS Specific */
+#define TARGET_NR_chdir 12 /* Common */
+#define TARGET_NR_chown 13 /* Common */
+#define TARGET_NR_mknod 14 /* Common */
+#define TARGET_NR_chmod 15 /* Common */
+#define TARGET_NR_lchown 16 /* Common */
+#define TARGET_NR_brk 17 /* Common */
+#define TARGET_NR_perfctr 18 /* Performance counter operations */
+#define TARGET_NR_lseek 19 /* Common */
+#define TARGET_NR_getpid 20 /* Common */
+#define TARGET_NR_capget 21 /* Linux Specific */
+#define TARGET_NR_capset 22 /* Linux Specific */
+#define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */
+#define TARGET_NR_getuid 24 /* Common */
+#define TARGET_NR_ptrace 26 /* Common */
+#define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */
+#define TARGET_NR_sigaltstack 28 /* Common */
+#define TARGET_NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
+#define TARGET_NR_utime 30 /* Implemented via utimes() under SunOS */
+#define TARGET_NR_lchown32 31 /* Linux sparc32 specific */
+#define TARGET_NR_fchown32 32 /* Linux sparc32 specific */
+#define TARGET_NR_access 33 /* Common */
+#define TARGET_NR_nice 34 /* Implemented via get/setpriority() in SunOS */
+#define TARGET_NR_chown32 35 /* Linux sparc32 specific */
+#define TARGET_NR_sync 36 /* Common */
+#define TARGET_NR_kill 37 /* Common */
+#define TARGET_NR_stat 38 /* Common */
+#define TARGET_NR_sendfile 39 /* Linux Specific */
+#define TARGET_NR_lstat 40 /* Common */
+#define TARGET_NR_dup 41 /* Common */
+#define TARGET_NR_pipe 42 /* Common */
+#define TARGET_NR_times 43 /* Implemented via getrusage() in SunOS */
+#define TARGET_NR_getuid32 44 /* Linux sparc32 specific */
+#define TARGET_NR_umount2 45 /* Linux Specific */
+#define TARGET_NR_setgid 46 /* Implemented via setregid() in SunOS */
+#define TARGET_NR_getgid 47 /* Common */
+#define TARGET_NR_signal 48 /* Implemented via sigvec() in SunOS */
+#define TARGET_NR_geteuid 49 /* SunOS calls getuid() */
+#define TARGET_NR_getegid 50 /* SunOS calls getgid() */
+#define TARGET_NR_acct 51 /* Common */
+#define TARGET_NR_getgid32 53 /* Linux sparc32 specific */
+#define TARGET_NR_ioctl 54 /* Common */
+#define TARGET_NR_reboot 55 /* Common */
+#define TARGET_NR_mmap2 56 /* Linux sparc32 Specific */
+#define TARGET_NR_symlink 57 /* Common */
+#define TARGET_NR_readlink 58 /* Common */
+#define TARGET_NR_execve 59 /* Common */
+#define TARGET_NR_umask 60 /* Common */
+#define TARGET_NR_chroot 61 /* Common */
+#define TARGET_NR_fstat 62 /* Common */
+#define TARGET_NR_fstat64 63 /* Linux sparc32 Specific */
+#define TARGET_NR_getpagesize 64 /* Common */
+#define TARGET_NR_msync 65 /* Common in newer 1.3.x revs... */
+#define TARGET_NR_vfork 66 /* Common */
+#define TARGET_NR_pread 67 /* Linux Specific */
+#define TARGET_NR_pwrite 68 /* Linux Specific */
+#define TARGET_NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */
+#define TARGET_NR_getegid32 70 /* Linux sparc32, sstk under SunOS */
+#define TARGET_NR_mmap 71 /* Common */
+#define TARGET_NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */
+#define TARGET_NR_munmap 73 /* Common */
+#define TARGET_NR_mprotect 74 /* Common */
+#define TARGET_NR_madvise 75 /* Common */
+#define TARGET_NR_vhangup 76 /* Common */
+#define TARGET_NR_truncate64 77 /* Linux sparc32 Specific */
+#define TARGET_NR_mincore 78 /* Common */
+#define TARGET_NR_getgroups 79 /* Common */
+#define TARGET_NR_setgroups 80 /* Common */
+#define TARGET_NR_getpgrp 81 /* Common */
+#define TARGET_NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */
+#define TARGET_NR_setitimer 83 /* Common */
+#define TARGET_NR_ftruncate64 84 /* Linux sparc32 Specific */
+#define TARGET_NR_swapon 85 /* Common */
+#define TARGET_NR_getitimer 86 /* Common */
+#define TARGET_NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */
+#define TARGET_NR_sethostname 88 /* Common */
+#define TARGET_NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */
+#define TARGET_NR_dup2 90 /* Common */
+#define TARGET_NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */
+#define TARGET_NR_fcntl 92 /* Common */
+#define TARGET_NR_select 93 /* Common */
+#define TARGET_NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */
+#define TARGET_NR_fsync 95 /* Common */
+#define TARGET_NR_setpriority 96 /* Common */
+#define TARGET_NR_socket 97 /* Common */
+#define TARGET_NR_connect 98 /* Common */
+#define TARGET_NR_accept 99 /* Common */
+#define TARGET_NR_getpriority 100 /* Common */
+#define TARGET_NR_rt_sigreturn 101 /* Linux Specific */
+#define TARGET_NR_rt_sigaction 102 /* Linux Specific */
+#define TARGET_NR_rt_sigprocmask 103 /* Linux Specific */
+#define TARGET_NR_rt_sigpending 104 /* Linux Specific */
+#define TARGET_NR_rt_sigtimedwait 105 /* Linux Specific */
+#define TARGET_NR_rt_sigqueueinfo 106 /* Linux Specific */
+#define TARGET_NR_rt_sigsuspend 107 /* Linux Specific */
+#define TARGET_NR_setresuid32 108 /* Linux Specific, sigvec under SunOS */
+#define TARGET_NR_getresuid32 109 /* Linux Specific, sigblock under SunOS */
+#define TARGET_NR_setresgid32 110 /* Linux Specific, sigsetmask under SunOS */
+#define TARGET_NR_getresgid32 111 /* Linux Specific, sigpause under SunOS */
+#define TARGET_NR_setregid32 112 /* Linux sparc32, sigstack under SunOS */
+#define TARGET_NR_recvmsg 113 /* Common */
+#define TARGET_NR_sendmsg 114 /* Common */
+#define TARGET_NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */
+#define TARGET_NR_gettimeofday 116 /* Common */
+#define TARGET_NR_getrusage 117 /* Common */
+#define TARGET_NR_getsockopt 118 /* Common */
+#define TARGET_NR_getcwd 119 /* Linux Specific */
+#define TARGET_NR_readv 120 /* Common */
+#define TARGET_NR_writev 121 /* Common */
+#define TARGET_NR_settimeofday 122 /* Common */
+#define TARGET_NR_fchown 123 /* Common */
+#define TARGET_NR_fchmod 124 /* Common */
+#define TARGET_NR_recvfrom 125 /* Common */
+#define TARGET_NR_setreuid 126 /* Common */
+#define TARGET_NR_setregid 127 /* Common */
+#define TARGET_NR_rename 128 /* Common */
+#define TARGET_NR_truncate 129 /* Common */
+#define TARGET_NR_ftruncate 130 /* Common */
+#define TARGET_NR_flock 131 /* Common */
+#define TARGET_NR_lstat64 132 /* Linux sparc32 Specific */
+#define TARGET_NR_sendto 133 /* Common */
+#define TARGET_NR_shutdown 134 /* Common */
+#define TARGET_NR_socketpair 135 /* Common */
+#define TARGET_NR_mkdir 136 /* Common */
+#define TARGET_NR_rmdir 137 /* Common */
+#define TARGET_NR_utimes 138 /* SunOS Specific */
+#define TARGET_NR_stat64 139 /* Linux sparc32 Specific */
+#define TARGET_NR_getpeername 141 /* Common */
+#define TARGET_NR_gettid 143 /* ENOSYS under SunOS */
+#define TARGET_NR_getrlimit 144 /* Common */
+#define TARGET_NR_setrlimit 145 /* Common */
+#define TARGET_NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
+#define TARGET_NR_prctl 147 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */
+#define TARGET_NR_getsockname 150 /* Common */
+#define TARGET_NR_poll 153 /* Common */
+#define TARGET_NR_getdents64 154 /* Linux specific */
+#define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */
+#define TARGET_NR_statfs 157 /* Common */
+#define TARGET_NR_fstatfs 158 /* Common */
+#define TARGET_NR_umount 159 /* Common */
+#define TARGET_NR_getdomainname 162 /* SunOS Specific */
+#define TARGET_NR_setdomainname 163 /* Common */
+#define TARGET_NR_quotactl 165 /* Common */
+#define TARGET_NR_mount 167 /* Common */
+#define TARGET_NR_ustat 168 /* Common */
+#define TARGET_NR_getdents 174 /* Common */
+#define TARGET_NR_setsid 175 /* Common */
+#define TARGET_NR_fchdir 176 /* Common */
+#define TARGET_NR_sigpending 183 /* Common */
+#define TARGET_NR_query_module 184 /* Linux Specific */
+#define TARGET_NR_setpgid 185 /* Common */
+#define TARGET_NR_tkill 187 /* SunOS: fpathconf */
+#define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
+#define TARGET_NR_uname 189 /* Linux Specific */
+#define TARGET_NR_init_module 190 /* Linux Specific */
+#define TARGET_NR_personality 191 /* Linux Specific */
+#define TARGET_NR_getppid 197 /* Linux Specific */
+#define TARGET_NR_sigaction 198 /* Linux Specific */
+#define TARGET_NR_sgetmask 199 /* Linux Specific */
+#define TARGET_NR_ssetmask 200 /* Linux Specific */
+#define TARGET_NR_sigsuspend 201 /* Linux Specific */
+#define TARGET_NR_oldlstat 202 /* Linux Specific */
+#define TARGET_NR_uselib 203 /* Linux Specific */
+#define TARGET_NR_readdir 204 /* Linux Specific */
+#define TARGET_NR_readahead 205 /* Linux Specific */
+#define TARGET_NR_socketcall 206 /* Linux Specific */
+#define TARGET_NR_syslog 207 /* Linux Specific */
+#define TARGET_NR_waitpid 212 /* Linux Specific */
+#define TARGET_NR_swapoff 213 /* Linux Specific */
+#define TARGET_NR_sysinfo 214 /* Linux Specific */
+#define TARGET_NR_ipc 215 /* Linux Specific */
+#define TARGET_NR_sigreturn 216 /* Linux Specific */
+#define TARGET_NR_clone 217 /* Linux Specific */
+#define TARGET_NR_adjtimex 219 /* Linux Specific */
+#define TARGET_NR_sigprocmask 220 /* Linux Specific */
+#define TARGET_NR_create_module 221 /* Linux Specific */
+#define TARGET_NR_delete_module 222 /* Linux Specific */
+#define TARGET_NR_get_kernel_syms 223 /* Linux Specific */
+#define TARGET_NR_getpgid 224 /* Linux Specific */
+#define TARGET_NR_bdflush 225 /* Linux Specific */
+#define TARGET_NR_sysfs 226 /* Linux Specific */
+#define TARGET_NR_afs_syscall 227 /* Linux Specific */
+#define TARGET_NR_setfsuid 228 /* Linux Specific */
+#define TARGET_NR_setfsgid 229 /* Linux Specific */
+#define TARGET_NR__newselect 230 /* Linux Specific */
+#define TARGET_NR_time 231 /* Linux Specific */
+#define TARGET_NR_stime 233 /* Linux Specific */
+#define TARGET_NR__llseek 236 /* Linux Specific */
+#define TARGET_NR_mlock 237
+#define TARGET_NR_munlock 238
+#define TARGET_NR_mlockall 239
+#define TARGET_NR_munlockall 240
+#define TARGET_NR_sched_setparam 241
+#define TARGET_NR_sched_getparam 242
+#define TARGET_NR_sched_setscheduler 243
+#define TARGET_NR_sched_getscheduler 244
+#define TARGET_NR_sched_yield 245
+#define TARGET_NR_sched_get_priority_max 246
+#define TARGET_NR_sched_get_priority_min 247
+#define TARGET_NR_sched_rr_get_interval 248
+#define TARGET_NR_nanosleep 249
+#define TARGET_NR_mremap 250
+#define TARGET_NR__sysctl 251
+#define TARGET_NR_getsid 252
+#define TARGET_NR_fdatasync 253
+#define TARGET_NR_nfsservctl 254
+#define TARGET_NR_aplib 255
+#define TARGET_NR_clock_settime 256
+#define TARGET_NR_clock_gettime 257
+#define TARGET_NR_clock_getres 258
+#define TARGET_NR_clock_nanosleep 259
+#define TARGET_NR_sched_getaffinity 260
+#define TARGET_NR_sched_setaffinity 261
+#define TARGET_NR_timer_settime 262
+#define TARGET_NR_timer_gettime 263
+#define TARGET_NR_timer_getoverrun 264
+#define TARGET_NR_timer_delete 265
+#define TARGET_NR_timer_create 266
+/* #define TARGET_NR_vserver 267 Reserved for VSERVER */
+#define TARGET_NR_io_setup 268
+#define TARGET_NR_io_destroy 269
+#define TARGET_NR_io_submit 270
+#define TARGET_NR_io_cancel 271
+#define TARGET_NR_io_getevents 272
+#define TARGET_NR_mq_open 273
+#define TARGET_NR_mq_unlink 274
+#define TARGET_NR_mq_timedsend 275
+#define TARGET_NR_mq_timedreceive 276
+#define TARGET_NR_mq_notify 277
+#define TARGET_NR_mq_getsetattr 278
+#define TARGET_NR_waitid 279
+#define TARGET_NR_tee 280
+#define TARGET_NR_add_key 281
+#define TARGET_NR_request_key 282
+#define TARGET_NR_keyctl 283
+#define TARGET_NR_openat 284
+#define TARGET_NR_mkdirat 285
+#define TARGET_NR_mknodat 286
+#define TARGET_NR_fchownat 287
+#define TARGET_NR_futimesat 288
+#define TARGET_NR_fstatat64 289
+#define TARGET_NR_unlinkat 290
+#define TARGET_NR_renameat 291
+#define TARGET_NR_linkat 292
+#define TARGET_NR_symlinkat 293
+#define TARGET_NR_readlinkat 294
+#define TARGET_NR_fchmodat 295
+#define TARGET_NR_faccessat 296
+#define TARGET_NR_pselect6 297
+#define TARGET_NR_ppoll 298
+#define TARGET_NR_unshare 299
+#define TARGET_NR_set_robust_list 300
+#define TARGET_NR_get_robust_list 301
+#define TARGET_NR_migrate_pages 302
+#define TARGET_NR_mbind 303
+#define TARGET_NR_get_mempolicy 304
+#define TARGET_NR_set_mempolicy 305
+#define TARGET_NR_kexec_load 306
+#define TARGET_NR_move_pages 307
+#define TARGET_NR_getcpu 308
+#define TARGET_NR_epoll_pwait 309
+#define TARGET_NR_utimensat 310
+#define TARGET_NR_signalfd 311
+#define TARGET_NR_timerfd 312
+#define TARGET_NR_eventfd 313
+#define TARGET_NR_fallocate 314
+#define TARGET_NR_timerfd_settime 315
+#define TARGET_NR_timerfd_gettime 316
+#define TARGET_NR_signalfd4 317
+#define TARGET_NR_eventfd2 318
+#define TARGET_NR_epoll_create1 319
+#define TARGET_NR_dup3 320
+#define TARGET_NR_pipe2 321
+#define TARGET_NR_inotify_init1 322
+#define TARGET_NR_accept4 323
diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h
new file mode 100644
index 0000000..c7de300
--- /dev/null
+++ b/linux-user/sparc/target_signal.h
@@ -0,0 +1,36 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+
+/*
+ * sigaltstack controls
+ */
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 4096
+#define TARGET_SIGSTKSZ 16384
+
+#ifndef UREG_I6
+#define UREG_I6 6
+#endif
+#ifndef UREG_FP
+#define UREG_FP UREG_I6
+#endif
+
+static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state)
+{
+ return state->regwptr[UREG_FP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/sparc/termbits.h b/linux-user/sparc/termbits.h
new file mode 100644
index 0000000..691600d
--- /dev/null
+++ b/linux-user/sparc/termbits.h
@@ -0,0 +1,279 @@
+/* from asm/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 */
+};
+
+/* 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_VEOL 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+
+#define TARGET_VSUSP 10
+#define TARGET_VDSUSP 11 /* SunOS POSIX nicety I do believe... */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+
+/* Kernel keeps vmin/vtime separated, user apps assume vmin/vtime is
+ * shared with eof/eol
+ */
+#define TARGET_VMIN TARGET_VEOF
+#define TARGET_VTIME TARGET_VEOL
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0x00000001
+#define TARGET_BRKINT 0x00000002
+#define TARGET_IGNPAR 0x00000004
+#define TARGET_PARMRK 0x00000008
+#define TARGET_INPCK 0x00000010
+#define TARGET_ISTRIP 0x00000020
+#define TARGET_INLCR 0x00000040
+#define TARGET_IGNCR 0x00000080
+#define TARGET_ICRNL 0x00000100
+#define TARGET_IUCLC 0x00000200
+#define TARGET_IXON 0x00000400
+#define TARGET_IXANY 0x00000800
+#define TARGET_IXOFF 0x00001000
+#define TARGET_IMAXBEL 0x00002000
+#define TARGET_IUTF8 0x00004000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0x00000001
+#define TARGET_OLCUC 0x00000002
+#define TARGET_ONLCR 0x00000004
+#define TARGET_OCRNL 0x00000008
+#define TARGET_ONOCR 0x00000010
+#define TARGET_ONLRET 0x00000020
+#define TARGET_OFILL 0x00000040
+#define TARGET_OFDEL 0x00000080
+#define TARGET_NLDLY 0x00000100
+#define TARGET_NL0 0x00000000
+#define TARGET_NL1 0x00000100
+#define TARGET_CRDLY 0x00000600
+#define TARGET_CR0 0x00000000
+#define TARGET_CR1 0x00000200
+#define TARGET_CR2 0x00000400
+#define TARGET_CR3 0x00000600
+#define TARGET_TABDLY 0x00001800
+#define TARGET_TAB0 0x00000000
+#define TARGET_TAB1 0x00000800
+#define TARGET_TAB2 0x00001000
+#define TARGET_TAB3 0x00001800
+#define TARGET_XTABS 0x00001800
+#define TARGET_BSDLY 0x00002000
+#define TARGET_BS0 0x00000000
+#define TARGET_BS1 0x00002000
+#define TARGET_VTDLY 0x00004000
+#define TARGET_VT0 0x00000000
+#define TARGET_VT1 0x00004000
+#define TARGET_FFDLY 0x00008000
+#define TARGET_FF0 0x00000000
+#define TARGET_FF1 0x00008000
+#define TARGET_PAGEOUT 0x00010000 /* SUNOS specific */
+#define TARGET_WRAP 0x00020000 /* SUNOS specific */
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0x0000100f
+#define TARGET_B0 0x00000000 /* hang up */
+#define TARGET_B50 0x00000001
+#define TARGET_B75 0x00000002
+#define TARGET_B110 0x00000003
+#define TARGET_B134 0x00000004
+#define TARGET_B150 0x00000005
+#define TARGET_B200 0x00000006
+#define TARGET_B300 0x00000007
+#define TARGET_B600 0x00000008
+#define TARGET_B1200 0x00000009
+#define TARGET_B1800 0x0000000a
+#define TARGET_B2400 0x0000000b
+#define TARGET_B4800 0x0000000c
+#define TARGET_B9600 0x0000000d
+#define TARGET_B19200 0x0000000e
+#define TARGET_B38400 0x0000000f
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0x00000030
+#define TARGET_CS5 0x00000000
+#define TARGET_CS6 0x00000010
+#define TARGET_CS7 0x00000020
+#define TARGET_CS8 0x00000030
+#define TARGET_CSTOPB 0x00000040
+#define TARGET_CREAD 0x00000080
+#define TARGET_PARENB 0x00000100
+#define TARGET_PARODD 0x00000200
+#define TARGET_HUPCL 0x00000400
+#define TARGET_CLOCAL 0x00000800
+#define TARGET_CBAUDEX 0x00001000
+/* We'll never see these speeds with the Zilogs, but for completeness... */
+#define TARGET_B57600 0x00001001
+#define TARGET_B115200 0x00001002
+#define TARGET_B230400 0x00001003
+#define TARGET_B460800 0x00001004
+/* This is what we can do with the Zilogs. */
+#define TARGET_B76800 0x00001005
+/* This is what we can do with the SAB82532. */
+#define TARGET_B153600 0x00001006
+#define TARGET_B307200 0x00001007
+#define TARGET_B614400 0x00001008
+#define TARGET_B921600 0x00001009
+/* And these are the rest... */
+#define TARGET_B500000 0x0000100a
+#define TARGET_B576000 0x0000100b
+#define TARGET_B1000000 0x0000100c
+#define TARGET_B1152000 0x0000100d
+#define TARGET_B1500000 0x0000100e
+#define TARGET_B2000000 0x0000100f
+/* These have totally bogus values and nobody uses them
+ so far. Later on we'd have to use say 0x10000x and
+ adjust CBAUD constant and drivers accordingly.
+#define B2500000 0x00001010
+#define B3000000 0x00001011
+#define B3500000 0x00001012
+#define B4000000 0x00001013 */
+#define TARGET_CIBAUD 0x100f0000 /* input baud rate (not used) */
+#define TARGET_CMSPAR 0x40000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 0x80000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000001
+#define TARGET_ICANON 0x00000002
+#define TARGET_XCASE 0x00000004
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000010
+#define TARGET_ECHOK 0x00000020
+#define TARGET_ECHONL 0x00000040
+#define TARGET_NOFLSH 0x00000080
+#define TARGET_TOSTOP 0x00000100
+#define TARGET_ECHOCTL 0x00000200
+#define TARGET_ECHOPRT 0x00000400
+#define TARGET_ECHOKE 0x00000800
+#define TARGET_DEFECHO 0x00001000 /* SUNOS thing, what is it? */
+#define TARGET_FLUSHO 0x00002000
+#define TARGET_PENDIN 0x00004000
+#define TARGET_IEXTEN 0x00008000
+
+/* ioctls */
+
+/* Big T */
+#define TARGET_TCGETA TARGET_IOR('T', 1, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('T', 2, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('T', 3, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('T', 4, struct target_termio)
+#define TARGET_TCSBRK TARGET_IO('T', 5)
+#define TARGET_TCXONC TARGET_IO('T', 6)
+#define TARGET_TCFLSH TARGET_IO('T', 7)
+#define TARGET_TCGETS TARGET_IOR('T', 8, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('T', 9, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('T', 10, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('T', 11, struct target_termios)
+
+/* Note that all the ioctls that are not available in Linux have a
+ * double underscore on the front to: a) avoid some programs to
+ * thing we support some ioctls under Linux (autoconfiguration stuff)
+ */
+/* Little t */
+#define TARGET_TIOCGETD TARGET_IOR('t', 0, int)
+#define TARGET_TIOCSETD TARGET_IOW('t', 1, int)
+//#define __TIOCHPCL _IO('t', 2) /* SunOS Specific */
+//#define __TIOCMODG _IOR('t', 3, int) /* SunOS Specific */
+//#define __TIOCMODS _IOW('t', 4, int) /* SunOS Specific */
+//#define __TIOCGETP _IOR('t', 8, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETP _IOW('t', 9, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETN _IOW('t', 10, struct sgttyb) /* SunOS Specific */
+#define TARGET_TIOCEXCL TARGET_IO('t', 13)
+#define TARGET_TIOCNXCL TARGET_IO('t', 14)
+//#define __TIOCFLUSH _IOW('t', 16, int) /* SunOS Specific */
+//#define __TIOCSETC _IOW('t', 17, struct tchars) /* SunOS Specific */
+//#define __TIOCGETC _IOR('t', 18, struct tchars) /* SunOS Specific */
+//#define __TIOCTCNTL _IOW('t', 32, int) /* SunOS Specific */
+//#define __TIOCSIGNAL _IOW('t', 33, int) /* SunOS Specific */
+//#define __TIOCSETX _IOW('t', 34, int) /* SunOS Specific */
+//#define __TIOCGETX _IOR('t', 35, int) /* SunOS Specific */
+#define TARGET_TIOCCONS TARGET_IO('t', 36)
+//#define __TIOCSSIZE _IOW('t', 37, struct sunos_ttysize) /* SunOS Specific */
+//#define __TIOCGSIZE _IOR('t', 38, struct sunos_ttysize) /* SunOS Specific */
+#define TARGET_TIOCGSOFTCAR TARGET_IOR('t', 100, int)
+#define TARGET_TIOCSSOFTCAR TARGET_IOW('t', 101, int)
+//#define __TIOCUCNTL _IOW('t', 102, int) /* SunOS Specific */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+//#define __TIOCREMOTE _IOW('t', 105, int) /* SunOS Specific */
+#define TARGET_TIOCMGET TARGET_IOR('t', 106, int)
+#define TARGET_TIOCMBIC TARGET_IOW('t', 107, int)
+#define TARGET_TIOCMBIS TARGET_IOW('t', 108, int)
+#define TARGET_TIOCMSET TARGET_IOW('t', 109, int)
+#define TARGET_TIOCSTART TARGET_IO('t', 110)
+#define TARGET_TIOCSTOP TARGET_IO('t', 111)
+#define TARGET_TIOCPKT TARGET_IOW('t', 112, int)
+#define TARGET_TIOCNOTTY TARGET_IO('t', 113)
+#define TARGET_TIOCSTI TARGET_IOW('t', 114, char)
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int)
+//#define __TIOCGLTC _IOR('t', 116, struct ltchars) /* SunOS Specific */
+//#define __TIOCSLTC _IOW('t', 117, struct ltchars) /* SunOS Specific */
+/* 118 is the non-posix setpgrp tty ioctl */
+/* 119 is the non-posix getpgrp tty ioctl */
+//#define __TIOCCDTR TARGET_IO('t', 120) /* SunOS Specific */
+//#define __TIOCSDTR TARGET_IO('t', 121) /* SunOS Specific */
+#define TARGET_TIOCCBRK TARGET_IO('t', 122)
+#define TARGET_TIOCSBRK TARGET_IO('t', 123)
+//#define __TIOCLGET TARGET_IOW('t', 124, int) /* SunOS Specific */
+//#define __TIOCLSET TARGET_IOW('t', 125, int) /* SunOS Specific */
+//#define __TIOCLBIC TARGET_IOW('t', 126, int) /* SunOS Specific */
+//#define __TIOCLBIS TARGET_IOW('t', 127, int) /* SunOS Specific */
+//#define __TIOCISPACE TARGET_IOR('t', 128, int) /* SunOS Specific */
+//#define __TIOCISIZE TARGET_IOR('t', 129, int) /* SunOS Specific */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 130, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 131, int)
+#define TARGET_TIOCSCTTY TARGET_IO('t', 132)
+#define TARGET_TIOCGSID TARGET_IOR('t', 133, int)
+/* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
+#define TARGET_TIOCGPTN TARGET_IOR('t', 134, unsigned int) /* Get Pty Number */
+#define TARGET_TIOCSPTLCK TARGET_IOW('t', 135, int) /* Lock/unlock PTY */
+
+/* Little f */
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
+ * someday. This is completely bogus, I know...
+ */
+//#define __TCGETSTAT TARGET_IO('T', 200) /* Rutgers specific */
+//#define __TCSETSTAT TARGET_IO('T', 201) /* Rutgers specific */
+
+/* Linux specific, no SunOS equivalent. */
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TCSBRKP 0x5425
+#define TARGET_TIOCTTYGSTRUCT 0x5426
+#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 input */
+#define TARGET_TIOCGICOUNT 0x545D /* Read serial port inline interrupt counts */
diff --git a/linux-user/sparc64/syscall.h b/linux-user/sparc64/syscall.h
new file mode 100644
index 0000000..81a816d
--- /dev/null
+++ b/linux-user/sparc64/syscall.h
@@ -0,0 +1,10 @@
+struct target_pt_regs {
+ abi_ulong u_regs[16];
+ abi_ulong tstate;
+ abi_ulong pc;
+ abi_ulong npc;
+ abi_ulong y;
+ abi_ulong fprs;
+};
+
+#define UNAME_MACHINE "sun4u"
diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h
new file mode 100644
index 0000000..bdca2a7
--- /dev/null
+++ b/linux-user/sparc64/syscall_nr.h
@@ -0,0 +1,324 @@
+#define TARGET_NR_restart_syscall 0 /* Linux Specific */
+#define TARGET_NR_exit 1 /* Common */
+#define TARGET_NR_fork 2 /* Common */
+#define TARGET_NR_read 3 /* Common */
+#define TARGET_NR_write 4 /* Common */
+#define TARGET_NR_open 5 /* Common */
+#define TARGET_NR_close 6 /* Common */
+#define TARGET_NR_wait4 7 /* Common */
+#define TARGET_NR_creat 8 /* Common */
+#define TARGET_NR_link 9 /* Common */
+#define TARGET_NR_unlink 10 /* Common */
+#define TARGET_NR_execv 11 /* SunOS Specific */
+#define TARGET_NR_chdir 12 /* Common */
+#define TARGET_NR_chown 13 /* Common */
+#define TARGET_NR_mknod 14 /* Common */
+#define TARGET_NR_chmod 15 /* Common */
+#define TARGET_NR_lchown 16 /* Common */
+#define TARGET_NR_brk 17 /* Common */
+#define TARGET_NR_perfctr 18 /* Performance counter operations */
+#define TARGET_NR_lseek 19 /* Common */
+#define TARGET_NR_getpid 20 /* Common */
+#define TARGET_NR_capget 21 /* Linux Specific */
+#define TARGET_NR_capset 22 /* Linux Specific */
+#define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */
+#define TARGET_NR_getuid 24 /* Common */
+/* #define TARGET_NR_time alias 25 ENOSYS under SunOS */
+#define TARGET_NR_ptrace 26 /* Common */
+#define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */
+#define TARGET_NR_sigaltstack 28 /* Common */
+#define TARGET_NR_pause 29 /* Is sigblock(0)->sigpause() in SunOS */
+#define TARGET_NR_utime 30 /* Implemented via utimes() under SunOS */
+#define TARGET_NR_lchown32 31 /* Linux sparc32 specific */
+#define TARGET_NR_fchown32 32 /* Linux sparc32 specific */
+#define TARGET_NR_access 33 /* Common */
+#define TARGET_NR_nice 34 /* Implemented via get/setpriority() in SunOS */
+#define TARGET_NR_chown32 35 /* Linux sparc32 specific */
+#define TARGET_NR_sync 36 /* Common */
+#define TARGET_NR_kill 37 /* Common */
+#define TARGET_NR_stat 38 /* Common */
+#define TARGET_NR_sendfile 39 /* Linux Specific */
+#define TARGET_NR_lstat 40 /* Common */
+#define TARGET_NR_dup 41 /* Common */
+#define TARGET_NR_pipe 42 /* Common */
+#define TARGET_NR_times 43 /* Implemented via getrusage() in SunOS */
+#define TARGET_NR_getuid32 44 /* Linux sparc32 specific */
+#define TARGET_NR_umount2 45 /* Linux Specific */
+#define TARGET_NR_setgid 46 /* Implemented via setregid() in SunOS */
+#define TARGET_NR_getgid 47 /* Common */
+#define TARGET_NR_signal 48 /* Implemented via sigvec() in SunOS */
+#define TARGET_NR_geteuid 49 /* SunOS calls getuid() */
+#define TARGET_NR_getegid 50 /* SunOS calls getgid() */
+#define TARGET_NR_acct 51 /* Common */
+#define TARGET_NR_memory_ordering 52 /* Linux Specific */
+#define TARGET_NR_getgid32 53 /* Linux sparc32 specific */
+#define TARGET_NR_ioctl 54 /* Common */
+#define TARGET_NR_reboot 55 /* Common */
+#define TARGET_NR_mmap2 56 /* Linux sparc32 Specific */
+#define TARGET_NR_symlink 57 /* Common */
+#define TARGET_NR_readlink 58 /* Common */
+#define TARGET_NR_execve 59 /* Common */
+#define TARGET_NR_umask 60 /* Common */
+#define TARGET_NR_chroot 61 /* Common */
+#define TARGET_NR_fstat 62 /* Common */
+#define TARGET_NR_fstat64 63 /* Linux sparc32 Specific */
+#define TARGET_NR_getpagesize 64 /* Common */
+#define TARGET_NR_msync 65 /* Common in newer 1.3.x revs... */
+#define TARGET_NR_vfork 66 /* Common */
+#define TARGET_NR_pread64 67 /* Linux Specific */
+#define TARGET_NR_pwrite64 68 /* Linux Specific */
+#define TARGET_NR_geteuid32 69 /* Linux sparc32, sbrk under SunOS */
+#define TARGET_NR_getegid32 70 /* Linux sparc32, sstk under SunOS */
+#define TARGET_NR_mmap 71 /* Common */
+#define TARGET_NR_setreuid32 72 /* Linux sparc32, vadvise under SunOS */
+#define TARGET_NR_munmap 73 /* Common */
+#define TARGET_NR_mprotect 74 /* Common */
+#define TARGET_NR_madvise 75 /* Common */
+#define TARGET_NR_vhangup 76 /* Common */
+#define TARGET_NR_truncate64 77 /* Linux sparc32 Specific */
+#define TARGET_NR_mincore 78 /* Common */
+#define TARGET_NR_getgroups 79 /* Common */
+#define TARGET_NR_setgroups 80 /* Common */
+#define TARGET_NR_getpgrp 81 /* Common */
+#define TARGET_NR_setgroups32 82 /* Linux sparc32, setpgrp under SunOS */
+#define TARGET_NR_setitimer 83 /* Common */
+#define TARGET_NR_ftruncate64 84 /* Linux sparc32 Specific */
+#define TARGET_NR_swapon 85 /* Common */
+#define TARGET_NR_getitimer 86 /* Common */
+#define TARGET_NR_setuid32 87 /* Linux sparc32, gethostname under SunOS */
+#define TARGET_NR_sethostname 88 /* Common */
+#define TARGET_NR_setgid32 89 /* Linux sparc32, getdtablesize under SunOS */
+#define TARGET_NR_dup2 90 /* Common */
+#define TARGET_NR_setfsuid32 91 /* Linux sparc32, getdopt under SunOS */
+#define TARGET_NR_fcntl 92 /* Common */
+#define TARGET_NR_select 93 /* Common */
+#define TARGET_NR_setfsgid32 94 /* Linux sparc32, setdopt under SunOS */
+#define TARGET_NR_fsync 95 /* Common */
+#define TARGET_NR_setpriority 96 /* Common */
+#define TARGET_NR_socket 97 /* Common */
+#define TARGET_NR_connect 98 /* Common */
+#define TARGET_NR_accept 99 /* Common */
+#define TARGET_NR_getpriority 100 /* Common */
+#define TARGET_NR_rt_sigreturn 101 /* Linux Specific */
+#define TARGET_NR_rt_sigaction 102 /* Linux Specific */
+#define TARGET_NR_rt_sigprocmask 103 /* Linux Specific */
+#define TARGET_NR_rt_sigpending 104 /* Linux Specific */
+#define TARGET_NR_rt_sigtimedwait 105 /* Linux Specific */
+#define TARGET_NR_rt_sigqueueinfo 106 /* Linux Specific */
+#define TARGET_NR_rt_sigsuspend 107 /* Linux Specific */
+#define TARGET_NR_setresuid 108 /* Linux Specific, sigvec under SunOS */
+#define TARGET_NR_getresuid 109 /* Linux Specific, sigblock under SunOS */
+#define TARGET_NR_setresgid 110 /* Linux Specific, sigsetmask under SunOS */
+#define TARGET_NR_getresgid 111 /* Linux Specific, sigpause under SunOS */
+/* #define TARGET_NR_setregid32 75 Linux sparc32, sigstack under SunOS */
+#define TARGET_NR_recvmsg 113 /* Common */
+#define TARGET_NR_sendmsg 114 /* Common */
+#define TARGET_NR_getgroups32 115 /* Linux sparc32, vtrace under SunOS */
+#define TARGET_NR_gettimeofday 116 /* Common */
+#define TARGET_NR_getrusage 117 /* Common */
+#define TARGET_NR_getsockopt 118 /* Common */
+#define TARGET_NR_getcwd 119 /* Linux Specific */
+#define TARGET_NR_readv 120 /* Common */
+#define TARGET_NR_writev 121 /* Common */
+#define TARGET_NR_settimeofday 122 /* Common */
+#define TARGET_NR_fchown 123 /* Common */
+#define TARGET_NR_fchmod 124 /* Common */
+#define TARGET_NR_recvfrom 125 /* Common */
+#define TARGET_NR_setreuid 126 /* Common */
+#define TARGET_NR_setregid 127 /* Common */
+#define TARGET_NR_rename 128 /* Common */
+#define TARGET_NR_truncate 129 /* Common */
+#define TARGET_NR_ftruncate 130 /* Common */
+#define TARGET_NR_flock 131 /* Common */
+#define TARGET_NR_lstat64 132 /* Linux sparc32 Specific */
+#define TARGET_NR_sendto 133 /* Common */
+#define TARGET_NR_shutdown 134 /* Common */
+#define TARGET_NR_socketpair 135 /* Common */
+#define TARGET_NR_mkdir 136 /* Common */
+#define TARGET_NR_rmdir 137 /* Common */
+#define TARGET_NR_utimes 138 /* SunOS Specific */
+#define TARGET_NR_stat64 139 /* Linux sparc32 Specific */
+#define TARGET_NR_sendfile64 140 /* adjtime under SunOS */
+#define TARGET_NR_getpeername 141 /* Common */
+#define TARGET_NR_futex 142 /* gethostid under SunOS */
+#define TARGET_NR_gettid 143 /* ENOSYS under SunOS */
+#define TARGET_NR_getrlimit 144 /* Common */
+#define TARGET_NR_setrlimit 145 /* Common */
+#define TARGET_NR_pivot_root 146 /* Linux Specific, killpg under SunOS */
+#define TARGET_NR_prctl 147 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */
+#define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */
+#define TARGET_NR_getsockname 150 /* Common */
+/* #define TARGET_NR_getmsg 151 SunOS Specific */
+/* #define TARGET_NR_putmsg 152 SunOS Specific */
+#define TARGET_NR_poll 153 /* Common */
+#define TARGET_NR_getdents64 154 /* Linux specific */
+#define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */
+/* #define TARGET_NR_getdirentries 156 SunOS Specific */
+#define TARGET_NR_statfs 157 /* Common */
+#define TARGET_NR_fstatfs 158 /* Common */
+#define TARGET_NR_umount 159 /* Common */
+#define TARGET_NR_sched_set_affinity 160 /* Linux specific, async_daemon under SunOS */
+#define TARGET_NR_sched_get_affinity 161 /* Linux specific, getfh under SunOS */
+#define TARGET_NR_getdomainname 162 /* SunOS Specific */
+#define TARGET_NR_setdomainname 163 /* Common */
+#define TARGET_NR_utrap_install 164 /* SYSV ABI/v9 required */
+#define TARGET_NR_quotactl 165 /* Common */
+#define TARGET_NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */
+#define TARGET_NR_mount 167 /* Common */
+#define TARGET_NR_ustat 168 /* Common */
+#define TARGET_NR_setxattr 169 /* SunOS: semsys */
+#define TARGET_NR_lsetxattr 170 /* SunOS: msgsys */
+#define TARGET_NR_fsetxattr 171 /* SunOS: shmsys */
+#define TARGET_NR_getxattr 172 /* SunOS: auditsys */
+#define TARGET_NR_lgetxattr 173 /* SunOS: rfssys */
+#define TARGET_NR_getdents 174 /* Common */
+#define TARGET_NR_setsid 175 /* Common */
+#define TARGET_NR_fchdir 176 /* Common */
+#define TARGET_NR_fgetxattr 177 /* SunOS: fchroot */
+#define TARGET_NR_listxattr 178 /* SunOS: vpixsys */
+#define TARGET_NR_llistxattr 179 /* SunOS: aioread */
+#define TARGET_NR_flistxattr 180 /* SunOS: aiowrite */
+#define TARGET_NR_removexattr 181 /* SunOS: aiowait */
+#define TARGET_NR_lremovexattr 182 /* SunOS: aiocancel */
+#define TARGET_NR_sigpending 183 /* Common */
+#define TARGET_NR_query_module 184 /* Linux Specific */
+#define TARGET_NR_setpgid 185 /* Common */
+#define TARGET_NR_fremovexattr 186 /* SunOS: pathconf */
+#define TARGET_NR_tkill 187 /* SunOS: fpathconf */
+#define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */
+#define TARGET_NR_uname 189 /* Linux Specific */
+#define TARGET_NR_init_module 190 /* Linux Specific */
+#define TARGET_NR_personality 191 /* Linux Specific */
+#define TARGET_NR_remap_file_pages 192 /* Linux Specific */
+#define TARGET_NR_epoll_create 193 /* Linux Specific */
+#define TARGET_NR_epoll_ctl 194 /* Linux Specific */
+#define TARGET_NR_epoll_wait 195 /* Linux Specific */
+/* #define TARGET_NR_ulimit 196 Linux Specific */
+#define TARGET_NR_getppid 197 /* Linux Specific */
+#define TARGET_NR_sigaction 198 /* Linux Specific */
+#define TARGET_NR_sgetmask 199 /* Linux Specific */
+#define TARGET_NR_ssetmask 200 /* Linux Specific */
+#define TARGET_NR_sigsuspend 201 /* Linux Specific */
+#define TARGET_NR_oldlstat 202 /* Linux Specific */
+#define TARGET_NR_uselib 203 /* Linux Specific */
+#define TARGET_NR_readdir 204 /* Linux Specific */
+#define TARGET_NR_readahead 205 /* Linux Specific */
+#define TARGET_NR_socketcall 206 /* Linux Specific */
+#define TARGET_NR_syslog 207 /* Linux Specific */
+#define TARGET_NR_lookup_dcookie 208 /* Linux Specific */
+#define TARGET_NR_fadvise64 209 /* Linux Specific */
+#define TARGET_NR_fadvise64_64 210 /* Linux Specific */
+#define TARGET_NR_tgkill 211 /* Linux Specific */
+#define TARGET_NR_waitpid 212 /* Linux Specific */
+#define TARGET_NR_swapoff 213 /* Linux Specific */
+#define TARGET_NR_sysinfo 214 /* Linux Specific */
+#define TARGET_NR_ipc 215 /* Linux Specific */
+#define TARGET_NR_sigreturn 216 /* Linux Specific */
+#define TARGET_NR_clone 217 /* Linux Specific */
+/* #define TARGET_NR_modify_ldt 218 Linux Specific - i386 specific, unused */
+#define TARGET_NR_adjtimex 219 /* Linux Specific */
+#define TARGET_NR_sigprocmask 220 /* Linux Specific */
+#define TARGET_NR_create_module 221 /* Linux Specific */
+#define TARGET_NR_delete_module 222 /* Linux Specific */
+#define TARGET_NR_get_kernel_syms 223 /* Linux Specific */
+#define TARGET_NR_getpgid 224 /* Linux Specific */
+#define TARGET_NR_bdflush 225 /* Linux Specific */
+#define TARGET_NR_sysfs 226 /* Linux Specific */
+#define TARGET_NR_afs_syscall 227 /* Linux Specific */
+#define TARGET_NR_setfsuid 228 /* Linux Specific */
+#define TARGET_NR_setfsgid 229 /* Linux Specific */
+#define TARGET_NR__newselect 230 /* Linux Specific */
+#define TARGET_NR_time 231 /* Linux sparc32 */
+/* #define TARGET_NR_oldstat 232 Linux Specific */
+#define TARGET_NR_stime 233 /* Linux Specific */
+#define TARGET_NR_statfs64 234 /* Linux Specific */
+#define TARGET_NR_fstatfs64 235 /* Linux Specific */
+#define TARGET_NR__llseek 236 /* Linux Specific */
+#define TARGET_NR_mlock 237
+#define TARGET_NR_munlock 238
+#define TARGET_NR_mlockall 239
+#define TARGET_NR_munlockall 240
+#define TARGET_NR_sched_setparam 241
+#define TARGET_NR_sched_getparam 242
+#define TARGET_NR_sched_setscheduler 243
+#define TARGET_NR_sched_getscheduler 244
+#define TARGET_NR_sched_yield 245
+#define TARGET_NR_sched_get_priority_max 246
+#define TARGET_NR_sched_get_priority_min 247
+#define TARGET_NR_sched_rr_get_interval 248
+#define TARGET_NR_nanosleep 249
+#define TARGET_NR_mremap 250
+#define TARGET_NR__sysctl 251
+#define TARGET_NR_getsid 252
+#define TARGET_NR_fdatasync 253
+#define TARGET_NR_nfsservctl 254
+#define TARGET_NR_aplib 255
+#define TARGET_NR_clock_settime 256
+#define TARGET_NR_clock_gettime 257
+#define TARGET_NR_clock_getres 258
+#define TARGET_NR_clock_nanosleep 259
+#define TARGET_NR_sched_getaffinity 260
+#define TARGET_NR_sched_setaffinity 261
+#define TARGET_NR_timer_settime 262
+#define TARGET_NR_timer_gettime 263
+#define TARGET_NR_timer_getoverrun 264
+#define TARGET_NR_timer_delete 265
+#define TARGET_NR_timer_create 266
+/* #define TARGET_NR_vserver 267 Reserved for VSERVER */
+#define TARGET_NR_io_setup 268
+#define TARGET_NR_io_destroy 269
+#define TARGET_NR_io_submit 270
+#define TARGET_NR_io_cancel 271
+#define TARGET_NR_io_getevents 272
+#define TARGET_NR_mq_open 273
+#define TARGET_NR_mq_unlink 274
+#define TARGET_NR_mq_timedsend 275
+#define TARGET_NR_mq_timedreceive 276
+#define TARGET_NR_mq_notify 277
+#define TARGET_NR_mq_getsetattr 278
+#define TARGET_NR_waitid 279
+/*#define TARGET_NR_sys_setaltroot 280 available (was setaltroot) */
+#define TARGET_NR_add_key 281
+#define TARGET_NR_request_key 282
+#define TARGET_NR_keyctl 283
+#define TARGET_NR_openat 284
+#define TARGET_NR_mkdirat 285
+#define TARGET_NR_mknodat 286
+#define TARGET_NR_fchownat 287
+#define TARGET_NR_futimesat 288
+#define TARGET_NR_fstatat64 289
+#define TARGET_NR_unlinkat 290
+#define TARGET_NR_renameat 291
+#define TARGET_NR_linkat 292
+#define TARGET_NR_symlinkat 293
+#define TARGET_NR_readlinkat 294
+#define TARGET_NR_fchmodat 295
+#define TARGET_NR_faccessat 296
+#define TARGET_NR_pselect6 297
+#define TARGET_NR_ppoll 298
+#define TARGET_NR_unshare 299
+#define TARGET_NR_set_robust_list 300
+#define TARGET_NR_get_robust_list 301
+#define TARGET_NR_migrate_pages 302
+#define TARGET_NR_mbind 303
+#define TARGET_NR_get_mempolicy 304
+#define TARGET_NR_set_mempolicy 305
+#define TARGET_NR_kexec_load 306
+#define TARGET_NR_move_pages 307
+#define TARGET_NR_getcpu 308
+#define TARGET_NR_epoll_pwait 309
+#define TARGET_NR_utimensat 310
+#define TARGET_NR_signalfd 311
+#define TARGET_NR_timerfd 312
+#define TARGET_NR_eventfd 313
+#define TARGET_NR_fallocate 314
+#define TARGET_NR_timerfd_settime 315
+#define TARGET_NR_timerfd_gettime 316
+#define TARGET_NR_signalfd4 317
+#define TARGET_NR_eventfd2 318
+#define TARGET_NR_epoll_create1 319
+#define TARGET_NR_dup3 320
+#define TARGET_NR_pipe2 321
+#define TARGET_NR_inotify_init1 322
+#define TARGET_NR_accept4 323
diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h
new file mode 100644
index 0000000..c7de300
--- /dev/null
+++ b/linux-user/sparc64/target_signal.h
@@ -0,0 +1,36 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long ss_flags;
+ abi_ulong ss_size;
+} target_stack_t;
+
+
+/*
+ * sigaltstack controls
+ */
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 4096
+#define TARGET_SIGSTKSZ 16384
+
+#ifndef UREG_I6
+#define UREG_I6 6
+#endif
+#ifndef UREG_FP
+#define UREG_FP UREG_I6
+#endif
+
+static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state)
+{
+ return state->regwptr[UREG_FP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/sparc64/termbits.h b/linux-user/sparc64/termbits.h
new file mode 100644
index 0000000..691600d
--- /dev/null
+++ b/linux-user/sparc64/termbits.h
@@ -0,0 +1,279 @@
+/* from asm/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 */
+};
+
+/* 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_VEOL 5
+#define TARGET_VEOL2 6
+#define TARGET_VSWTC 7
+#define TARGET_VSTART 8
+#define TARGET_VSTOP 9
+
+#define TARGET_VSUSP 10
+#define TARGET_VDSUSP 11 /* SunOS POSIX nicety I do believe... */
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE 14
+#define TARGET_VLNEXT 15
+
+/* Kernel keeps vmin/vtime separated, user apps assume vmin/vtime is
+ * shared with eof/eol
+ */
+#define TARGET_VMIN TARGET_VEOF
+#define TARGET_VTIME TARGET_VEOL
+
+/* c_iflag bits */
+#define TARGET_IGNBRK 0x00000001
+#define TARGET_BRKINT 0x00000002
+#define TARGET_IGNPAR 0x00000004
+#define TARGET_PARMRK 0x00000008
+#define TARGET_INPCK 0x00000010
+#define TARGET_ISTRIP 0x00000020
+#define TARGET_INLCR 0x00000040
+#define TARGET_IGNCR 0x00000080
+#define TARGET_ICRNL 0x00000100
+#define TARGET_IUCLC 0x00000200
+#define TARGET_IXON 0x00000400
+#define TARGET_IXANY 0x00000800
+#define TARGET_IXOFF 0x00001000
+#define TARGET_IMAXBEL 0x00002000
+#define TARGET_IUTF8 0x00004000
+
+/* c_oflag bits */
+#define TARGET_OPOST 0x00000001
+#define TARGET_OLCUC 0x00000002
+#define TARGET_ONLCR 0x00000004
+#define TARGET_OCRNL 0x00000008
+#define TARGET_ONOCR 0x00000010
+#define TARGET_ONLRET 0x00000020
+#define TARGET_OFILL 0x00000040
+#define TARGET_OFDEL 0x00000080
+#define TARGET_NLDLY 0x00000100
+#define TARGET_NL0 0x00000000
+#define TARGET_NL1 0x00000100
+#define TARGET_CRDLY 0x00000600
+#define TARGET_CR0 0x00000000
+#define TARGET_CR1 0x00000200
+#define TARGET_CR2 0x00000400
+#define TARGET_CR3 0x00000600
+#define TARGET_TABDLY 0x00001800
+#define TARGET_TAB0 0x00000000
+#define TARGET_TAB1 0x00000800
+#define TARGET_TAB2 0x00001000
+#define TARGET_TAB3 0x00001800
+#define TARGET_XTABS 0x00001800
+#define TARGET_BSDLY 0x00002000
+#define TARGET_BS0 0x00000000
+#define TARGET_BS1 0x00002000
+#define TARGET_VTDLY 0x00004000
+#define TARGET_VT0 0x00000000
+#define TARGET_VT1 0x00004000
+#define TARGET_FFDLY 0x00008000
+#define TARGET_FF0 0x00000000
+#define TARGET_FF1 0x00008000
+#define TARGET_PAGEOUT 0x00010000 /* SUNOS specific */
+#define TARGET_WRAP 0x00020000 /* SUNOS specific */
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD 0x0000100f
+#define TARGET_B0 0x00000000 /* hang up */
+#define TARGET_B50 0x00000001
+#define TARGET_B75 0x00000002
+#define TARGET_B110 0x00000003
+#define TARGET_B134 0x00000004
+#define TARGET_B150 0x00000005
+#define TARGET_B200 0x00000006
+#define TARGET_B300 0x00000007
+#define TARGET_B600 0x00000008
+#define TARGET_B1200 0x00000009
+#define TARGET_B1800 0x0000000a
+#define TARGET_B2400 0x0000000b
+#define TARGET_B4800 0x0000000c
+#define TARGET_B9600 0x0000000d
+#define TARGET_B19200 0x0000000e
+#define TARGET_B38400 0x0000000f
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE 0x00000030
+#define TARGET_CS5 0x00000000
+#define TARGET_CS6 0x00000010
+#define TARGET_CS7 0x00000020
+#define TARGET_CS8 0x00000030
+#define TARGET_CSTOPB 0x00000040
+#define TARGET_CREAD 0x00000080
+#define TARGET_PARENB 0x00000100
+#define TARGET_PARODD 0x00000200
+#define TARGET_HUPCL 0x00000400
+#define TARGET_CLOCAL 0x00000800
+#define TARGET_CBAUDEX 0x00001000
+/* We'll never see these speeds with the Zilogs, but for completeness... */
+#define TARGET_B57600 0x00001001
+#define TARGET_B115200 0x00001002
+#define TARGET_B230400 0x00001003
+#define TARGET_B460800 0x00001004
+/* This is what we can do with the Zilogs. */
+#define TARGET_B76800 0x00001005
+/* This is what we can do with the SAB82532. */
+#define TARGET_B153600 0x00001006
+#define TARGET_B307200 0x00001007
+#define TARGET_B614400 0x00001008
+#define TARGET_B921600 0x00001009
+/* And these are the rest... */
+#define TARGET_B500000 0x0000100a
+#define TARGET_B576000 0x0000100b
+#define TARGET_B1000000 0x0000100c
+#define TARGET_B1152000 0x0000100d
+#define TARGET_B1500000 0x0000100e
+#define TARGET_B2000000 0x0000100f
+/* These have totally bogus values and nobody uses them
+ so far. Later on we'd have to use say 0x10000x and
+ adjust CBAUD constant and drivers accordingly.
+#define B2500000 0x00001010
+#define B3000000 0x00001011
+#define B3500000 0x00001012
+#define B4000000 0x00001013 */
+#define TARGET_CIBAUD 0x100f0000 /* input baud rate (not used) */
+#define TARGET_CMSPAR 0x40000000 /* mark or space (stick) parity */
+#define TARGET_CRTSCTS 0x80000000 /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG 0x00000001
+#define TARGET_ICANON 0x00000002
+#define TARGET_XCASE 0x00000004
+#define TARGET_ECHO 0x00000008
+#define TARGET_ECHOE 0x00000010
+#define TARGET_ECHOK 0x00000020
+#define TARGET_ECHONL 0x00000040
+#define TARGET_NOFLSH 0x00000080
+#define TARGET_TOSTOP 0x00000100
+#define TARGET_ECHOCTL 0x00000200
+#define TARGET_ECHOPRT 0x00000400
+#define TARGET_ECHOKE 0x00000800
+#define TARGET_DEFECHO 0x00001000 /* SUNOS thing, what is it? */
+#define TARGET_FLUSHO 0x00002000
+#define TARGET_PENDIN 0x00004000
+#define TARGET_IEXTEN 0x00008000
+
+/* ioctls */
+
+/* Big T */
+#define TARGET_TCGETA TARGET_IOR('T', 1, struct target_termio)
+#define TARGET_TCSETA TARGET_IOW('T', 2, struct target_termio)
+#define TARGET_TCSETAW TARGET_IOW('T', 3, struct target_termio)
+#define TARGET_TCSETAF TARGET_IOW('T', 4, struct target_termio)
+#define TARGET_TCSBRK TARGET_IO('T', 5)
+#define TARGET_TCXONC TARGET_IO('T', 6)
+#define TARGET_TCFLSH TARGET_IO('T', 7)
+#define TARGET_TCGETS TARGET_IOR('T', 8, struct target_termios)
+#define TARGET_TCSETS TARGET_IOW('T', 9, struct target_termios)
+#define TARGET_TCSETSW TARGET_IOW('T', 10, struct target_termios)
+#define TARGET_TCSETSF TARGET_IOW('T', 11, struct target_termios)
+
+/* Note that all the ioctls that are not available in Linux have a
+ * double underscore on the front to: a) avoid some programs to
+ * thing we support some ioctls under Linux (autoconfiguration stuff)
+ */
+/* Little t */
+#define TARGET_TIOCGETD TARGET_IOR('t', 0, int)
+#define TARGET_TIOCSETD TARGET_IOW('t', 1, int)
+//#define __TIOCHPCL _IO('t', 2) /* SunOS Specific */
+//#define __TIOCMODG _IOR('t', 3, int) /* SunOS Specific */
+//#define __TIOCMODS _IOW('t', 4, int) /* SunOS Specific */
+//#define __TIOCGETP _IOR('t', 8, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETP _IOW('t', 9, struct sgttyb) /* SunOS Specific */
+//#define __TIOCSETN _IOW('t', 10, struct sgttyb) /* SunOS Specific */
+#define TARGET_TIOCEXCL TARGET_IO('t', 13)
+#define TARGET_TIOCNXCL TARGET_IO('t', 14)
+//#define __TIOCFLUSH _IOW('t', 16, int) /* SunOS Specific */
+//#define __TIOCSETC _IOW('t', 17, struct tchars) /* SunOS Specific */
+//#define __TIOCGETC _IOR('t', 18, struct tchars) /* SunOS Specific */
+//#define __TIOCTCNTL _IOW('t', 32, int) /* SunOS Specific */
+//#define __TIOCSIGNAL _IOW('t', 33, int) /* SunOS Specific */
+//#define __TIOCSETX _IOW('t', 34, int) /* SunOS Specific */
+//#define __TIOCGETX _IOR('t', 35, int) /* SunOS Specific */
+#define TARGET_TIOCCONS TARGET_IO('t', 36)
+//#define __TIOCSSIZE _IOW('t', 37, struct sunos_ttysize) /* SunOS Specific */
+//#define __TIOCGSIZE _IOR('t', 38, struct sunos_ttysize) /* SunOS Specific */
+#define TARGET_TIOCGSOFTCAR TARGET_IOR('t', 100, int)
+#define TARGET_TIOCSSOFTCAR TARGET_IOW('t', 101, int)
+//#define __TIOCUCNTL _IOW('t', 102, int) /* SunOS Specific */
+#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct winsize)
+#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct winsize)
+//#define __TIOCREMOTE _IOW('t', 105, int) /* SunOS Specific */
+#define TARGET_TIOCMGET TARGET_IOR('t', 106, int)
+#define TARGET_TIOCMBIC TARGET_IOW('t', 107, int)
+#define TARGET_TIOCMBIS TARGET_IOW('t', 108, int)
+#define TARGET_TIOCMSET TARGET_IOW('t', 109, int)
+#define TARGET_TIOCSTART TARGET_IO('t', 110)
+#define TARGET_TIOCSTOP TARGET_IO('t', 111)
+#define TARGET_TIOCPKT TARGET_IOW('t', 112, int)
+#define TARGET_TIOCNOTTY TARGET_IO('t', 113)
+#define TARGET_TIOCSTI TARGET_IOW('t', 114, char)
+#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int)
+//#define __TIOCGLTC _IOR('t', 116, struct ltchars) /* SunOS Specific */
+//#define __TIOCSLTC _IOW('t', 117, struct ltchars) /* SunOS Specific */
+/* 118 is the non-posix setpgrp tty ioctl */
+/* 119 is the non-posix getpgrp tty ioctl */
+//#define __TIOCCDTR TARGET_IO('t', 120) /* SunOS Specific */
+//#define __TIOCSDTR TARGET_IO('t', 121) /* SunOS Specific */
+#define TARGET_TIOCCBRK TARGET_IO('t', 122)
+#define TARGET_TIOCSBRK TARGET_IO('t', 123)
+//#define __TIOCLGET TARGET_IOW('t', 124, int) /* SunOS Specific */
+//#define __TIOCLSET TARGET_IOW('t', 125, int) /* SunOS Specific */
+//#define __TIOCLBIC TARGET_IOW('t', 126, int) /* SunOS Specific */
+//#define __TIOCLBIS TARGET_IOW('t', 127, int) /* SunOS Specific */
+//#define __TIOCISPACE TARGET_IOR('t', 128, int) /* SunOS Specific */
+//#define __TIOCISIZE TARGET_IOR('t', 129, int) /* SunOS Specific */
+#define TARGET_TIOCSPGRP TARGET_IOW('t', 130, int)
+#define TARGET_TIOCGPGRP TARGET_IOR('t', 131, int)
+#define TARGET_TIOCSCTTY TARGET_IO('t', 132)
+#define TARGET_TIOCGSID TARGET_IOR('t', 133, int)
+/* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */
+#define TARGET_TIOCGPTN TARGET_IOR('t', 134, unsigned int) /* Get Pty Number */
+#define TARGET_TIOCSPTLCK TARGET_IOW('t', 135, int) /* Lock/unlock PTY */
+
+/* Little f */
+#define TARGET_FIOCLEX TARGET_IO('f', 1)
+#define TARGET_FIONCLEX TARGET_IO('f', 2)
+#define TARGET_FIOASYNC TARGET_IOW('f', 125, int)
+#define TARGET_FIONBIO TARGET_IOW('f', 126, int)
+#define TARGET_FIONREAD TARGET_IOR('f', 127, int)
+#define TARGET_TIOCINQ TARGET_FIONREAD
+
+/* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it
+ * someday. This is completely bogus, I know...
+ */
+//#define __TCGETSTAT TARGET_IO('T', 200) /* Rutgers specific */
+//#define __TCSETSTAT TARGET_IO('T', 201) /* Rutgers specific */
+
+/* Linux specific, no SunOS equivalent. */
+#define TARGET_TIOCLINUX 0x541C
+#define TARGET_TIOCGSERIAL 0x541E
+#define TARGET_TIOCSSERIAL 0x541F
+#define TARGET_TCSBRKP 0x5425
+#define TARGET_TIOCTTYGSTRUCT 0x5426
+#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 input */
+#define TARGET_TIOCGICOUNT 0x545D /* Read serial port inline interrupt counts */
diff --git a/linux-user/strace.c b/linux-user/strace.c
new file mode 100644
index 0000000..bf9a0d9
--- /dev/null
+++ b/linux-user/strace.c
@@ -0,0 +1,1356 @@
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "qemu.h"
+
+int do_strace=0;
+
+struct syscallname {
+ int nr;
+ const char *name;
+ const char *format;
+ void (*call)(const struct syscallname *,
+ abi_long, abi_long, abi_long,
+ abi_long, abi_long, abi_long);
+ void (*result)(const struct syscallname *, abi_long);
+};
+
+#ifdef __GNUC__
+/*
+ * It is possible that target doesn't have syscall that uses
+ * following flags but we don't want the compiler to warn
+ * us about them being unused. Same applies to utility print
+ * functions. It is ok to keep them while not used.
+ */
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif
+
+/*
+ * Structure used to translate flag values into strings. This is
+ * similar that is in the actual strace tool.
+ */
+struct flags {
+ abi_long f_value; /* flag */
+ const char *f_string; /* stringified flag */
+};
+
+/* common flags for all architectures */
+#define FLAG_GENERIC(name) { name, #name }
+/* target specific flags (syscall_defs.h has TARGET_<flag>) */
+#define FLAG_TARGET(name) { TARGET_ ## name, #name }
+/* end of flags array */
+#define FLAG_END { 0, NULL }
+
+UNUSED static const char *get_comma(int);
+UNUSED static void print_pointer(abi_long, int);
+UNUSED static void print_flags(const struct flags *, abi_long, int);
+UNUSED static void print_at_dirfd(abi_long, int);
+UNUSED static void print_file_mode(abi_long, int);
+UNUSED static void print_open_flags(abi_long, int);
+UNUSED static void print_syscall_prologue(const struct syscallname *);
+UNUSED static void print_syscall_epilogue(const struct syscallname *);
+UNUSED static void print_string(abi_long, int);
+UNUSED static void print_raw_param(const char *, abi_long, int);
+UNUSED static void print_timeval(abi_ulong, int);
+UNUSED static void print_number(abi_long, int);
+
+/*
+ * Utility functions
+ */
+static void
+print_ipc_cmd(int cmd)
+{
+#define output_cmd(val) \
+if( cmd == val ) { \
+ gemu_log(#val); \
+ return; \
+}
+
+ cmd &= 0xff;
+
+ /* General IPC commands */
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_SET );
+ output_cmd( IPC_STAT );
+ output_cmd( IPC_INFO );
+ /* msgctl() commands */
+ #ifdef __USER_MISC
+ output_cmd( MSG_STAT );
+ output_cmd( MSG_INFO );
+ #endif
+ /* shmctl() commands */
+ output_cmd( SHM_LOCK );
+ output_cmd( SHM_UNLOCK );
+ output_cmd( SHM_STAT );
+ output_cmd( SHM_INFO );
+ /* semctl() commands */
+ output_cmd( GETPID );
+ output_cmd( GETVAL );
+ output_cmd( GETALL );
+ output_cmd( GETNCNT );
+ output_cmd( GETZCNT );
+ output_cmd( SETVAL );
+ output_cmd( SETALL );
+ output_cmd( SEM_STAT );
+ output_cmd( SEM_INFO );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+ output_cmd( IPC_RMID );
+
+ /* Some value we don't recognize */
+ gemu_log("%d",cmd);
+}
+
+#ifdef TARGET_NR__newselect
+static void
+print_fdset(int n, abi_ulong target_fds_addr)
+{
+ int i;
+
+ gemu_log("[");
+ if( target_fds_addr ) {
+ abi_long *target_fds;
+
+ target_fds = lock_user(VERIFY_READ,
+ target_fds_addr,
+ sizeof(*target_fds)*(n / TARGET_ABI_BITS + 1),
+ 1);
+
+ if (!target_fds)
+ return;
+
+ for (i=n; i>=0; i--) {
+ if ((tswapl(target_fds[i / TARGET_ABI_BITS]) >> (i & (TARGET_ABI_BITS - 1))) & 1)
+ gemu_log("%d,", i );
+ }
+ unlock_user(target_fds, target_fds_addr, 0);
+ }
+ gemu_log("]");
+}
+#endif
+
+/*
+ * Sysycall specific output functions
+ */
+
+/* select */
+#ifdef TARGET_NR__newselect
+static long newselect_arg1 = 0;
+static long newselect_arg2 = 0;
+static long newselect_arg3 = 0;
+static long newselect_arg4 = 0;
+static long newselect_arg5 = 0;
+
+static void
+print_newselect(const struct syscallname *name,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ gemu_log("%s(" TARGET_ABI_FMT_ld ",", name->name, arg1);
+ print_fdset(arg1, arg2);
+ gemu_log(",");
+ print_fdset(arg1, arg3);
+ gemu_log(",");
+ print_fdset(arg1, arg4);
+ gemu_log(",");
+ print_timeval(arg5, 1);
+ gemu_log(")");
+
+ /* save for use in the return output function below */
+ newselect_arg1=arg1;
+ newselect_arg2=arg2;
+ newselect_arg3=arg3;
+ newselect_arg4=arg4;
+ newselect_arg5=arg5;
+}
+#endif
+
+#ifdef TARGET_NR_semctl
+static void
+print_semctl(const struct syscallname *name,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ gemu_log("%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", name->name, arg1, arg2);
+ print_ipc_cmd(arg3);
+ gemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4);
+}
+#endif
+
+static void
+print_execve(const struct syscallname *name,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ abi_ulong arg_ptr_addr;
+ char *s;
+
+ if (!(s = lock_user_string(arg1)))
+ return;
+ gemu_log("%s(\"%s\",{", name->name, s);
+ unlock_user(s, arg1, 0);
+
+ for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) {
+ abi_ulong *arg_ptr, arg_addr;
+
+ arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1);
+ if (!arg_ptr)
+ return;
+ arg_addr = tswapl(*arg_ptr);
+ unlock_user(arg_ptr, arg_ptr_addr, 0);
+ if (!arg_addr)
+ break;
+ if ((s = lock_user_string(arg_addr))) {
+ gemu_log("\"%s\",", s);
+ unlock_user(s, arg_addr, 0);
+ }
+ }
+
+ gemu_log("NULL})");
+}
+
+#ifdef TARGET_NR_ipc
+static void
+print_ipc(const struct syscallname *name,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ switch(arg1) {
+ case IPCOP_semctl:
+ gemu_log("semctl(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ",", arg1, arg2);
+ print_ipc_cmd(arg3);
+ gemu_log(",0x" TARGET_ABI_FMT_lx ")", arg4);
+ break;
+ default:
+ gemu_log("%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ")",
+ name->name, arg1, arg2, arg3, arg4);
+ }
+}
+#endif
+
+/*
+ * Variants for the return value output function
+ */
+
+static void
+print_syscall_ret_addr(const struct syscallname *name, abi_long ret)
+{
+if( ret == -1 ) {
+ gemu_log(" = -1 errno=%d (%s)\n", errno, target_strerror(errno));
+ } else {
+ gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret);
+ }
+}
+
+#if 0 /* currently unused */
+static void
+print_syscall_ret_raw(struct syscallname *name, abi_long ret)
+{
+ gemu_log(" = 0x" TARGET_ABI_FMT_lx "\n", ret);
+}
+#endif
+
+#ifdef TARGET_NR__newselect
+static void
+print_syscall_ret_newselect(const struct syscallname *name, abi_long ret)
+{
+ gemu_log(" = 0x" TARGET_ABI_FMT_lx " (", ret);
+ print_fdset(newselect_arg1,newselect_arg2);
+ gemu_log(",");
+ print_fdset(newselect_arg1,newselect_arg3);
+ gemu_log(",");
+ print_fdset(newselect_arg1,newselect_arg4);
+ gemu_log(",");
+ print_timeval(newselect_arg5, 1);
+ gemu_log(")\n");
+}
+#endif
+
+UNUSED static struct flags access_flags[] = {
+ FLAG_GENERIC(F_OK),
+ FLAG_GENERIC(R_OK),
+ FLAG_GENERIC(W_OK),
+ FLAG_GENERIC(X_OK),
+ FLAG_END,
+};
+
+UNUSED static struct flags at_file_flags[] = {
+#ifdef AT_EACCESS
+ FLAG_GENERIC(AT_EACCESS),
+#endif
+#ifdef AT_SYMLINK_NOFOLLOW
+ FLAG_GENERIC(AT_SYMLINK_NOFOLLOW),
+#endif
+ FLAG_END,
+};
+
+UNUSED static struct flags unlinkat_flags[] = {
+#ifdef AT_REMOVEDIR
+ FLAG_GENERIC(AT_REMOVEDIR),
+#endif
+ FLAG_END,
+};
+
+UNUSED static struct flags mode_flags[] = {
+ FLAG_GENERIC(S_IFSOCK),
+ FLAG_GENERIC(S_IFLNK),
+ FLAG_GENERIC(S_IFREG),
+ FLAG_GENERIC(S_IFBLK),
+ FLAG_GENERIC(S_IFDIR),
+ FLAG_GENERIC(S_IFCHR),
+ FLAG_GENERIC(S_IFIFO),
+ FLAG_END,
+};
+
+UNUSED static struct flags open_access_flags[] = {
+ FLAG_TARGET(O_RDONLY),
+ FLAG_TARGET(O_WRONLY),
+ FLAG_TARGET(O_RDWR),
+ FLAG_END,
+};
+
+UNUSED static struct flags open_flags[] = {
+ FLAG_TARGET(O_APPEND),
+ FLAG_TARGET(O_CREAT),
+ FLAG_TARGET(O_DIRECTORY),
+ FLAG_TARGET(O_EXCL),
+ FLAG_TARGET(O_LARGEFILE),
+ FLAG_TARGET(O_NOCTTY),
+ FLAG_TARGET(O_NOFOLLOW),
+ FLAG_TARGET(O_NONBLOCK), /* also O_NDELAY */
+ FLAG_TARGET(O_SYNC),
+ FLAG_TARGET(O_TRUNC),
+#ifdef O_DIRECT
+ FLAG_TARGET(O_DIRECT),
+#endif
+ FLAG_END,
+};
+
+UNUSED static struct flags mount_flags[] = {
+#ifdef MS_BIND
+ FLAG_GENERIC(MS_BIND),
+#endif
+#ifdef MS_DIRSYNC
+ FLAG_GENERIC(MS_DIRSYNC),
+#endif
+ FLAG_GENERIC(MS_MANDLOCK),
+#ifdef MS_MOVE
+ FLAG_GENERIC(MS_MOVE),
+#endif
+ FLAG_GENERIC(MS_NOATIME),
+ FLAG_GENERIC(MS_NODEV),
+ FLAG_GENERIC(MS_NODIRATIME),
+ FLAG_GENERIC(MS_NOEXEC),
+ FLAG_GENERIC(MS_NOSUID),
+ FLAG_GENERIC(MS_RDONLY),
+#ifdef MS_RELATIME
+ FLAG_GENERIC(MS_RELATIME),
+#endif
+ FLAG_GENERIC(MS_REMOUNT),
+ FLAG_GENERIC(MS_SYNCHRONOUS),
+ FLAG_END,
+};
+
+UNUSED static struct flags umount2_flags[] = {
+#ifdef MNT_FORCE
+ FLAG_GENERIC(MNT_FORCE),
+#endif
+#ifdef MNT_DETACH
+ FLAG_GENERIC(MNT_DETACH),
+#endif
+#ifdef MNT_EXPIRE
+ FLAG_GENERIC(MNT_EXPIRE),
+#endif
+ FLAG_END,
+};
+
+UNUSED static struct flags mmap_prot_flags[] = {
+ FLAG_GENERIC(PROT_NONE),
+ FLAG_GENERIC(PROT_EXEC),
+ FLAG_GENERIC(PROT_READ),
+ FLAG_GENERIC(PROT_WRITE),
+ FLAG_TARGET(PROT_SEM),
+ FLAG_GENERIC(PROT_GROWSDOWN),
+ FLAG_GENERIC(PROT_GROWSUP),
+ FLAG_END,
+};
+
+UNUSED static struct flags mmap_flags[] = {
+ FLAG_TARGET(MAP_SHARED),
+ FLAG_TARGET(MAP_PRIVATE),
+ FLAG_TARGET(MAP_ANONYMOUS),
+ FLAG_TARGET(MAP_DENYWRITE),
+ FLAG_TARGET(MAP_FIXED),
+ FLAG_TARGET(MAP_GROWSDOWN),
+#ifdef MAP_LOCKED
+ FLAG_TARGET(MAP_LOCKED),
+#endif
+#ifdef MAP_NONBLOCK
+ FLAG_TARGET(MAP_NONBLOCK),
+#endif
+ FLAG_TARGET(MAP_NORESERVE),
+#ifdef MAP_POPULATE
+ FLAG_TARGET(MAP_POPULATE),
+#endif
+ FLAG_END,
+};
+
+UNUSED static struct flags fcntl_flags[] = {
+ FLAG_TARGET(F_DUPFD),
+ FLAG_TARGET(F_GETFD),
+ FLAG_TARGET(F_SETFD),
+ FLAG_TARGET(F_GETFL),
+ FLAG_TARGET(F_SETFL),
+ FLAG_TARGET(F_GETLK),
+ FLAG_TARGET(F_SETLK),
+ FLAG_TARGET(F_SETLKW),
+ FLAG_END,
+};
+
+/*
+ * print_xxx utility functions. These are used to print syscall
+ * parameters in certain format. All of these have parameter
+ * named 'last'. This parameter is used to add comma to output
+ * when last == 0.
+ */
+
+static const char *
+get_comma(int last)
+{
+ return ((last) ? "" : ",");
+}
+
+static void
+print_flags(const struct flags *f, abi_long tflags, int last)
+{
+ const char *sep = "";
+ int flags;
+ int n;
+
+ flags = (int)tswap32(tflags);
+
+ if ((flags == 0) && (f->f_value == 0)) {
+ gemu_log("%s%s", f->f_string, get_comma(last));
+ return;
+ }
+ for (n = 0; f->f_string != NULL; f++) {
+ if ((f->f_value != 0) && ((flags & f->f_value) == f->f_value)) {
+ gemu_log("%s%s", sep, f->f_string);
+ flags &= ~f->f_value;
+ sep = "|";
+ n++;
+ }
+ }
+
+ if (n > 0) {
+ /* print rest of the flags as numeric */
+ if (flags != 0) {
+ gemu_log("%s%#x%s", sep, flags, get_comma(last));
+ } else {
+ gemu_log("%s", get_comma(last));
+ }
+ } else {
+ /* no string version of flags found, print them in hex then */
+ gemu_log("%#x%s", flags, get_comma(last));
+ }
+}
+
+static void
+print_at_dirfd(abi_long tdirfd, int last)
+{
+ int dirfd = tswap32(tdirfd);
+
+#ifdef AT_FDCWD
+ if (dirfd == AT_FDCWD) {
+ gemu_log("AT_FDCWD%s", get_comma(last));
+ return;
+ }
+#endif
+ gemu_log("%d%s", dirfd, get_comma(last));
+}
+
+static void
+print_file_mode(abi_long tmode, int last)
+{
+ const char *sep = "";
+ const struct flags *m;
+ mode_t mode = (mode_t)tswap32(tmode);
+
+ for (m = &mode_flags[0]; m->f_string != NULL; m++) {
+ if ((m->f_value & mode) == m->f_value) {
+ gemu_log("%s%s", m->f_string, sep);
+ sep = "|";
+ mode &= ~m->f_value;
+ break;
+ }
+ }
+
+ mode &= ~S_IFMT;
+ /* print rest of the mode as octal */
+ if (mode != 0)
+ gemu_log("%s%#o", sep, mode);
+
+ gemu_log("%s", get_comma(last));
+}
+
+static void
+print_open_flags(abi_long tflags, int last)
+{
+ int flags = tswap32(tflags);
+
+ print_flags(open_access_flags, flags & TARGET_O_ACCMODE, 1);
+ flags &= ~TARGET_O_ACCMODE;
+ if (flags == 0) {
+ gemu_log("%s", get_comma(last));
+ return;
+ }
+ gemu_log("|");
+ print_flags(open_flags, flags, last);
+}
+
+static void
+print_syscall_prologue(const struct syscallname *sc)
+{
+ gemu_log("%s(", sc->name);
+}
+
+/*ARGSUSED*/
+static void
+print_syscall_epilogue(const struct syscallname *sc)
+{
+ (void)sc;
+ gemu_log(")");
+}
+
+static void
+print_string(abi_long addr, int last)
+{
+ char *s;
+
+ if ((s = lock_user_string(addr)) != NULL) {
+ gemu_log("\"%s\"%s", s, get_comma(last));
+ unlock_user(s, addr, 0);
+ } else {
+ /* can't get string out of it, so print it as pointer */
+ print_pointer(addr, last);
+ }
+}
+
+/*
+ * Prints out raw parameter using given format. Caller needs
+ * to do byte swapping if needed.
+ */
+static void
+print_raw_param(const char *fmt, abi_long param, int last)
+{
+ char format[64];
+
+ (void) snprintf(format, sizeof (format), "%s%s", fmt, get_comma(last));
+ gemu_log(format, param);
+}
+
+static void
+print_pointer(abi_long p, int last)
+{
+ if (p == 0)
+ gemu_log("NULL%s", get_comma(last));
+ else
+ gemu_log("0x" TARGET_ABI_FMT_lx "%s", p, get_comma(last));
+}
+
+/*
+ * Reads 32-bit (int) number from guest address space from
+ * address 'addr' and prints it.
+ */
+static void
+print_number(abi_long addr, int last)
+{
+ if (addr == 0) {
+ gemu_log("NULL%s", get_comma(last));
+ } else {
+ int num;
+
+ get_user_s32(num, addr);
+ gemu_log("[%d]%s", num, get_comma(last));
+ }
+}
+
+static void
+print_timeval(abi_ulong tv_addr, int last)
+{
+ if( tv_addr ) {
+ struct target_timeval *tv;
+
+ tv = lock_user(VERIFY_READ, tv_addr, sizeof(*tv), 1);
+ if (!tv)
+ return;
+ gemu_log("{" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "}%s",
+ tv->tv_sec, tv->tv_usec, get_comma(last));
+ unlock_user(tv, tv_addr, 0);
+ } else
+ gemu_log("NULL%s", get_comma(last));
+}
+
+#undef UNUSED
+
+#ifdef TARGET_NR_accept
+static void
+print_accept(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_raw_param("%d", tswap32(arg0), 0);
+ print_pointer(arg1, 0);
+ print_number(arg2, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_access
+static void
+print_access(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_flags(access_flags, arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_brk
+static void
+print_brk(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_pointer(arg0, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_chdir
+static void
+print_chdir(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_chmod
+static void
+print_chmod(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_file_mode(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_creat
+static void
+print_creat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_file_mode(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_execv
+static void
+print_execv(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_raw_param("0x" TARGET_ABI_FMT_lx, tswapl(arg1), 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_faccessat
+static void
+print_faccessat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_flags(access_flags, arg2, 0);
+ print_flags(at_file_flags, arg3, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_fchmodat
+static void
+print_fchmodat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_file_mode(arg2, 0);
+ print_flags(at_file_flags, arg3, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_fchownat
+static void
+print_fchownat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+#ifdef USE_UID16
+ print_raw_param("%d", tswap16(arg2), 0);
+ print_raw_param("%d", tswap16(arg3), 0);
+#else
+ print_raw_param("%d", tswap32(arg2), 0);
+ print_raw_param("%d", tswap32(arg3), 0);
+#endif
+ print_flags(at_file_flags, arg4, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#if defined(TARGET_NR_fcntl) || defined(TARGET_NR_fcntl64)
+static void
+print_fcntl(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_raw_param("%d", tswap32(arg0), 0);
+ print_flags(fcntl_flags, arg1, 0);
+ /*
+ * TODO: check flags and print following argument only
+ * when needed.
+ */
+ print_pointer(arg2, 1);
+ print_syscall_epilogue(name);
+}
+#define print_fcntl64 print_fcntl
+#endif
+
+
+#ifdef TARGET_NR_futimesat
+static void
+print_futimesat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_timeval(arg2, 0);
+ print_timeval(arg2 + sizeof (struct target_timeval), 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_link
+static void
+print_link(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_string(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_linkat
+static void
+print_linkat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_at_dirfd(arg2, 0);
+ print_string(arg3, 0);
+ print_flags(at_file_flags, arg4, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#if defined(TARGET_NR_stat) || defined(TARGET_NR_stat64) || \
+ defined(TARGET_NR_lstat) || defined(TARGET_NR_lstat64)
+static void
+print_stat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_pointer(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#define print_lstat print_stat
+#define print_stat64 print_stat
+#define print_lstat64 print_stat
+#endif
+
+#if defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64)
+static void
+print_fstat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_raw_param("%d", tswap32(arg0), 0);
+ print_pointer(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#define print_fstat64 print_fstat
+#endif
+
+#ifdef TARGET_NR_mkdir
+static void
+print_mkdir(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_file_mode(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mkdirat
+static void
+print_mkdirat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_file_mode(arg2, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mknod
+static void
+print_mknod(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ int hasdev = (tswapl(arg1) & (S_IFCHR|S_IFBLK));
+
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_file_mode(arg1, (hasdev == 0));
+ if (hasdev) {
+ print_raw_param("makedev(%d", major(tswapl(arg2)), 0);
+ print_raw_param("%d)", minor(tswapl(arg2)), 1);
+ }
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mknodat
+static void
+print_mknodat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ int hasdev = (tswapl(arg2) & (S_IFCHR|S_IFBLK));
+
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_file_mode(arg2, (hasdev == 0));
+ if (hasdev) {
+ print_raw_param("makedev(%d", major(tswapl(arg3)), 0);
+ print_raw_param("%d)", minor(tswapl(arg3)), 1);
+ }
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mq_open
+static void
+print_mq_open(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ int is_creat = (tswapl(arg1) & TARGET_O_CREAT);
+
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_open_flags(arg1, (is_creat == 0));
+ if (is_creat) {
+ print_file_mode(arg2, 0);
+ print_pointer(arg3, 1);
+ }
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_open
+static void
+print_open(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ int is_creat = (tswap32(arg1) & TARGET_O_CREAT);
+
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_open_flags(arg1, (is_creat == 0));
+ if (is_creat)
+ print_file_mode(arg2, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_openat
+static void
+print_openat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ int is_creat = (tswap32(arg2) & TARGET_O_CREAT);
+
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_open_flags(arg2, (is_creat == 0));
+ if (is_creat)
+ print_file_mode(arg3, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mq_unlink
+static void
+print_mq_unlink(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#if defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)
+static void
+print_fstatat64(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_pointer(arg2, 0);
+ print_flags(at_file_flags, arg3, 1);
+ print_syscall_epilogue(name);
+}
+#define print_newfstatat print_fstatat64
+#endif
+
+#ifdef TARGET_NR_readlink
+static void
+print_readlink(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_pointer(arg1, 0);
+ print_raw_param("%u", tswapl(arg2), 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_readlinkat
+static void
+print_readlinkat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_pointer(arg2, 0);
+ print_raw_param("%u", tswapl(arg3), 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_rename
+static void
+print_rename(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_string(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_renameat
+static void
+print_renameat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_at_dirfd(arg2, 0);
+ print_string(arg3, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_statfs
+static void
+print_statfs(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_pointer(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#define print_statfs64 print_statfs
+#endif
+
+#ifdef TARGET_NR_symlink
+static void
+print_symlink(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_string(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_symlinkat
+static void
+print_symlinkat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_at_dirfd(arg1, 0);
+ print_string(arg2, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mount
+static void
+print_mount(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_string(arg1, 0);
+ print_string(arg2, 0);
+ print_flags(mount_flags, arg3, 0);
+ print_pointer(arg4, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_umount
+static void
+print_umount(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_umount2
+static void
+print_umount2(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_flags(umount2_flags, arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_unlink
+static void
+print_unlink(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_unlinkat
+static void
+print_unlinkat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_flags(unlinkat_flags, arg2, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_utime
+static void
+print_utime(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_pointer(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_utimes
+static void
+print_utimes(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 0);
+ print_pointer(arg1, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_utimensat
+static void
+print_utimensat(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_at_dirfd(arg0, 0);
+ print_string(arg1, 0);
+ print_pointer(arg2, 0);
+ print_flags(at_file_flags, arg3, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_mmap
+static void
+print_mmap(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_pointer(arg0, 0);
+ print_raw_param("%d", tswapl(arg1), 0);
+ print_flags(mmap_prot_flags, arg2, 0);
+ print_flags(mmap_flags, arg3, 0);
+ print_raw_param("%d", tswapl(arg4), 0);
+ print_raw_param("%#x", tswapl(arg5), 1);
+ print_syscall_epilogue(name);
+}
+#define print_mmap2 print_mmap
+#endif
+
+#ifdef TARGET_NR_mprotect
+static void
+print_mprotect(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_pointer(arg0, 0);
+ print_raw_param("%d", tswapl(arg1), 0);
+ print_flags(mmap_prot_flags, arg2, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_munmap
+static void
+print_munmap(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_pointer(arg0, 0);
+ print_raw_param("%d", tswapl(arg1), 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+#ifdef TARGET_NR_futex
+static void print_futex_op(abi_long tflag, int last)
+{
+#define print_op(val) \
+if( cmd == val ) { \
+ gemu_log(#val); \
+ return; \
+}
+
+ int cmd = (int)tswap32(tflag);
+#ifdef FUTEX_PRIVATE_FLAG
+ if (cmd & FUTEX_PRIVATE_FLAG) {
+ gemu_log("FUTEX_PRIVATE_FLAG|");
+ cmd &= ~FUTEX_PRIVATE_FLAG;
+ }
+#endif
+ print_op(FUTEX_WAIT)
+ print_op(FUTEX_WAKE)
+ print_op(FUTEX_FD)
+ print_op(FUTEX_REQUEUE)
+ print_op(FUTEX_CMP_REQUEUE)
+ print_op(FUTEX_WAKE_OP)
+ print_op(FUTEX_LOCK_PI)
+ print_op(FUTEX_UNLOCK_PI)
+ print_op(FUTEX_TRYLOCK_PI)
+#ifdef FUTEX_WAIT_BITSET
+ print_op(FUTEX_WAIT_BITSET)
+#endif
+#ifdef FUTEX_WAKE_BITSET
+ print_op(FUTEX_WAKE_BITSET)
+#endif
+ /* unknown values */
+ gemu_log("%d",cmd);
+}
+
+static void
+print_futex(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_pointer(arg0, 0);
+ print_futex_op(arg1, 0);
+ print_raw_param(",%d", tswapl(arg2), 0);
+ print_pointer(arg3, 0); /* struct timespec */
+ print_pointer(arg4, 0);
+ print_raw_param("%d", tswapl(arg4), 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
+/*
+ * An array of all of the syscalls we know about
+ */
+
+static const struct syscallname scnames[] = {
+#include "strace.list"
+};
+
+static int nsyscalls = ARRAY_SIZE(scnames);
+
+/*
+ * The public interface to this module.
+ */
+void
+print_syscall(int num,
+ abi_long arg1, abi_long arg2, abi_long arg3,
+ abi_long arg4, abi_long arg5, abi_long arg6)
+{
+ int i;
+ const char *format="%s(" TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld "," TARGET_ABI_FMT_ld ")";
+
+ gemu_log("%d ", getpid() );
+
+ for(i=0;i<nsyscalls;i++)
+ if( scnames[i].nr == num ) {
+ if( scnames[i].call != NULL ) {
+ scnames[i].call(&scnames[i],arg1,arg2,arg3,arg4,arg5,arg6);
+ } else {
+ /* XXX: this format system is broken because it uses
+ host types and host pointers for strings */
+ if( scnames[i].format != NULL )
+ format = scnames[i].format;
+ gemu_log(format,scnames[i].name, arg1,arg2,arg3,arg4,arg5,arg6);
+ }
+ return;
+ }
+ gemu_log("Unknown syscall %d\n", num);
+}
+
+
+void
+print_syscall_ret(int num, abi_long ret)
+{
+ int i;
+
+ for(i=0;i<nsyscalls;i++)
+ if( scnames[i].nr == num ) {
+ if( scnames[i].result != NULL ) {
+ scnames[i].result(&scnames[i],ret);
+ } else {
+ if( ret < 0 ) {
+ gemu_log(" = -1 errno=" TARGET_ABI_FMT_ld " (%s)\n", -ret, target_strerror(-ret));
+ } else {
+ gemu_log(" = " TARGET_ABI_FMT_ld "\n", ret);
+ }
+ }
+ break;
+ }
+}
diff --git a/linux-user/strace.list b/linux-user/strace.list
new file mode 100644
index 0000000..d7be0e7
--- /dev/null
+++ b/linux-user/strace.list
@@ -0,0 +1,1526 @@
+/*
+ * Note that if you change format strings in these, check also
+ * that corresponding print functions are able to handle string
+ * locking correctly (see strace.c).
+ */
+#ifdef TARGET_NR_accept
+{ TARGET_NR_accept, "accept" , NULL, print_accept, NULL },
+#endif
+#ifdef TARGET_NR_access
+{ TARGET_NR_access, "access" , NULL, print_access, NULL },
+#endif
+#ifdef TARGET_NR_acct
+{ TARGET_NR_acct, "acct" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_add_key
+{ TARGET_NR_add_key, "add_key" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_adjtimex
+{ TARGET_NR_adjtimex, "adjtimex" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_afs_syscall
+{ TARGET_NR_afs_syscall, "afs_syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_alarm
+{ TARGET_NR_alarm, "alarm" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_aplib
+{ TARGET_NR_aplib, "aplib" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_arch_prctl
+{ TARGET_NR_arch_prctl, "arch_prctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_arm_fadvise64_64
+{ TARGET_NR_arm_fadvise64_64, "arm_fadvise64_64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_bdflush
+{ TARGET_NR_bdflush, "bdflush" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_bind
+{ TARGET_NR_bind, "bind" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_break
+{ TARGET_NR_break, "break" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_brk
+{ TARGET_NR_brk, "brk" , NULL, print_brk, print_syscall_ret_addr },
+#endif
+#ifdef TARGET_NR_cachectl
+{ TARGET_NR_cachectl, "cachectl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_cacheflush
+{ TARGET_NR_cacheflush, "cacheflush" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_capget
+{ TARGET_NR_capget, "capget" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_capset
+{ TARGET_NR_capset, "capset" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_chdir
+{ TARGET_NR_chdir, "chdir" , NULL, print_chdir, NULL },
+#endif
+#ifdef TARGET_NR_chmod
+{ TARGET_NR_chmod, "chmod" , NULL, print_chmod, NULL },
+#endif
+#ifdef TARGET_NR_chown
+{ TARGET_NR_chown, "chown" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_chown32
+{ TARGET_NR_chown32, "chown32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_chroot
+{ TARGET_NR_chroot, "chroot" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_clock_getres
+{ TARGET_NR_clock_getres, "clock_getres" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_clock_gettime
+{ TARGET_NR_clock_gettime, "clock_gettime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_clock_nanosleep
+{ TARGET_NR_clock_nanosleep, "clock_nanosleep" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_clock_settime
+{ TARGET_NR_clock_settime, "clock_settime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_clone
+{ TARGET_NR_clone, "clone" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_close
+{ TARGET_NR_close, "close" , "%s(%d)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_connect
+{ TARGET_NR_connect, "connect" , "%s(%d,%#x,%d)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_creat
+{ TARGET_NR_creat, "creat" , NULL, print_creat, NULL },
+#endif
+#ifdef TARGET_NR_create_module
+{ TARGET_NR_create_module, "create_module" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_delete_module
+{ TARGET_NR_delete_module, "delete_module" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_dipc
+{ TARGET_NR_dipc, "dipc" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_dup
+{ TARGET_NR_dup, "dup" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_dup2
+{ TARGET_NR_dup2, "dup2" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_epoll_create
+{ TARGET_NR_epoll_create, "epoll_create" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_epoll_ctl
+{ TARGET_NR_epoll_ctl, "epoll_ctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_epoll_ctl_old
+{ TARGET_NR_epoll_ctl_old, "epoll_ctl_old" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_epoll_wait
+{ TARGET_NR_epoll_wait, "epoll_wait" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_epoll_wait_old
+{ TARGET_NR_epoll_wait_old, "epoll_wait_old" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_execv
+{ TARGET_NR_execv, "execv" , NULL, print_execv, NULL },
+#endif
+#ifdef TARGET_NR_execve
+{ TARGET_NR_execve, "execve" , NULL, print_execve, NULL },
+#endif
+#ifdef TARGET_NR_exec_with_loader
+{ TARGET_NR_exec_with_loader, "exec_with_loader" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_exit
+{ TARGET_NR_exit, "exit" , "%s(%d)\n", NULL, NULL },
+#endif
+#ifdef TARGET_NR__exit
+{ TARGET_NR__exit, "_exit" , "%s(%d)\n", NULL, NULL },
+#endif
+#ifdef TARGET_NR_exit_group
+{ TARGET_NR_exit_group, "exit_group" , "%s(%d)\n", NULL, NULL },
+#endif
+#ifdef TARGET_NR_faccessat
+{ TARGET_NR_faccessat, "faccessat" , NULL, print_faccessat, NULL },
+#endif
+#ifdef TARGET_NR_fadvise64
+{ TARGET_NR_fadvise64, "fadvise64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fadvise64_64
+{ TARGET_NR_fadvise64_64, "fadvise64_64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fchdir
+{ TARGET_NR_fchdir, "fchdir" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fchmod
+{ TARGET_NR_fchmod, "fchmod" , "%s(%d,%#o)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_fchmodat
+{ TARGET_NR_fchmodat, "fchmodat" , NULL, print_fchmodat, NULL },
+#endif
+#ifdef TARGET_NR_fchown
+{ TARGET_NR_fchown, "fchown" , "%s(%d,%d,%d)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_fchown32
+{ TARGET_NR_fchown32, "fchown32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fchownat
+{ TARGET_NR_fchownat, "fchownat" , NULL, print_fchownat, NULL },
+#endif
+#ifdef TARGET_NR_fcntl
+{ TARGET_NR_fcntl, "fcntl" , NULL, print_fcntl, NULL },
+#endif
+#ifdef TARGET_NR_fcntl64
+{ TARGET_NR_fcntl64, "fcntl64" , NULL, print_fcntl64, NULL },
+#endif
+#ifdef TARGET_NR_fdatasync
+{ TARGET_NR_fdatasync, "fdatasync" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fgetxattr
+{ TARGET_NR_fgetxattr, "fgetxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_flistxattr
+{ TARGET_NR_flistxattr, "flistxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_flock
+{ TARGET_NR_flock, "flock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fork
+{ TARGET_NR_fork, "fork" , "%s()", NULL, NULL },
+#endif
+#ifdef TARGET_NR_fremovexattr
+{ TARGET_NR_fremovexattr, "fremovexattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fsetxattr
+{ TARGET_NR_fsetxattr, "fsetxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fstat
+{ TARGET_NR_fstat, "fstat" , NULL, print_fstat, NULL },
+#endif
+#ifdef TARGET_NR_fstat64
+{ TARGET_NR_fstat64, "fstat64" , NULL, print_fstat64, NULL },
+#endif
+#ifdef TARGET_NR_fstatfs
+{ TARGET_NR_fstatfs, "fstatfs" , "%s(%d,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_fstatfs64
+{ TARGET_NR_fstatfs64, "fstatfs64" , "%s(%d,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_fsync
+{ TARGET_NR_fsync, "fsync" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ftime
+{ TARGET_NR_ftime, "ftime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ftruncate
+{ TARGET_NR_ftruncate, "ftruncate" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ftruncate64
+{ TARGET_NR_ftruncate64, "ftruncate64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_futex
+{ TARGET_NR_futex, "futex" , NULL, print_futex, NULL },
+#endif
+#ifdef TARGET_NR_futimesat
+{ TARGET_NR_futimesat, "futimesat" , NULL, print_futimesat, NULL },
+#endif
+#ifdef TARGET_NR_getcwd
+{ TARGET_NR_getcwd, "getcwd" , "%s(%p,%d)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_getdents
+{ TARGET_NR_getdents, "getdents" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getdents64
+{ TARGET_NR_getdents64, "getdents64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getdomainname
+{ TARGET_NR_getdomainname, "getdomainname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getdtablesize
+{ TARGET_NR_getdtablesize, "getdtablesize" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getegid
+{ TARGET_NR_getegid, "getegid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getegid32
+{ TARGET_NR_getegid32, "getegid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_geteuid
+{ TARGET_NR_geteuid, "geteuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_geteuid32
+{ TARGET_NR_geteuid32, "geteuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getgid
+{ TARGET_NR_getgid, "getgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getgid32
+{ TARGET_NR_getgid32, "getgid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getgroups
+{ TARGET_NR_getgroups, "getgroups" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getgroups32
+{ TARGET_NR_getgroups32, "getgroups32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_gethostname
+{ TARGET_NR_gethostname, "gethostname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getitimer
+{ TARGET_NR_getitimer, "getitimer" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_get_kernel_syms
+{ TARGET_NR_get_kernel_syms, "get_kernel_syms" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_get_mempolicy
+{ TARGET_NR_get_mempolicy, "get_mempolicy" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpagesize
+{ TARGET_NR_getpagesize, "getpagesize" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpeername
+{ TARGET_NR_getpeername, "getpeername" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpgid
+{ TARGET_NR_getpgid, "getpgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpgrp
+{ TARGET_NR_getpgrp, "getpgrp" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpid
+{ TARGET_NR_getpid, "getpid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpmsg
+{ TARGET_NR_getpmsg, "getpmsg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getppid
+{ TARGET_NR_getppid, "getppid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getpriority
+{ TARGET_NR_getpriority, "getpriority", "%s(%#x,%#x)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_getresgid
+{ TARGET_NR_getresgid, "getresgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getresgid32
+{ TARGET_NR_getresgid32, "getresgid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getresuid
+{ TARGET_NR_getresuid, "getresuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getresuid32
+{ TARGET_NR_getresuid32, "getresuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getrlimit
+{ TARGET_NR_getrlimit, "getrlimit" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_get_robust_list
+{ TARGET_NR_get_robust_list, "get_robust_list" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getrusage
+{ TARGET_NR_getrusage, "getrusage" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getsid
+{ TARGET_NR_getsid, "getsid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getsockname
+{ TARGET_NR_getsockname, "getsockname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getsockopt
+{ TARGET_NR_getsockopt, "getsockopt" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_get_thread_area
+{ TARGET_NR_get_thread_area, "get_thread_area" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_gettid
+{ TARGET_NR_gettid, "gettid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_gettimeofday
+{ TARGET_NR_gettimeofday, "gettimeofday" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getuid
+{ TARGET_NR_getuid, "getuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getuid32
+{ TARGET_NR_getuid32, "getuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getxattr
+{ TARGET_NR_getxattr, "getxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getxgid
+{ TARGET_NR_getxgid, "getxgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getxpid
+{ TARGET_NR_getxpid, "getxpid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_getxuid
+{ TARGET_NR_getxuid, "getxuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_gtty
+{ TARGET_NR_gtty, "gtty" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_idle
+{ TARGET_NR_idle, "idle" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_init_module
+{ TARGET_NR_init_module, "init_module" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_inotify_add_watch
+{ TARGET_NR_inotify_add_watch, "inotify_add_watch" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_inotify_init
+{ TARGET_NR_inotify_init, "inotify_init" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_inotify_rm_watch
+{ TARGET_NR_inotify_rm_watch, "inotify_rm_watch" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_io_cancel
+{ TARGET_NR_io_cancel, "io_cancel" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ioctl
+{ TARGET_NR_ioctl, "ioctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_io_destroy
+{ TARGET_NR_io_destroy, "io_destroy" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_io_getevents
+{ TARGET_NR_io_getevents, "io_getevents" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ioperm
+{ TARGET_NR_ioperm, "ioperm" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_iopl
+{ TARGET_NR_iopl, "iopl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ioprio_get
+{ TARGET_NR_ioprio_get, "ioprio_get" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ioprio_set
+{ TARGET_NR_ioprio_set, "ioprio_set" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_io_setup
+{ TARGET_NR_io_setup, "io_setup" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_io_submit
+{ TARGET_NR_io_submit, "io_submit" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ipc
+{ TARGET_NR_ipc, "ipc" , NULL, print_ipc, NULL },
+#endif
+#ifdef TARGET_NR_kexec_load
+{ TARGET_NR_kexec_load, "kexec_load" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_keyctl
+{ TARGET_NR_keyctl, "keyctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_kill
+{ TARGET_NR_kill, "kill" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lchown
+{ TARGET_NR_lchown, "lchown" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lchown32
+{ TARGET_NR_lchown32, "lchown32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lgetxattr
+{ TARGET_NR_lgetxattr, "lgetxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_link
+{ TARGET_NR_link, "link" , NULL, print_link, NULL },
+#endif
+#ifdef TARGET_NR_linkat
+{ TARGET_NR_linkat, "linkat" , NULL, print_linkat, NULL },
+#endif
+#ifdef TARGET_NR_Linux
+{ TARGET_NR_Linux, "Linux" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_listen
+{ TARGET_NR_listen, "listen" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_listxattr
+{ TARGET_NR_listxattr, "listxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_llistxattr
+{ TARGET_NR_llistxattr, "llistxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR__llseek
+{ TARGET_NR__llseek, "_llseek" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lock
+{ TARGET_NR_lock, "lock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lookup_dcookie
+{ TARGET_NR_lookup_dcookie, "lookup_dcookie" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lremovexattr
+{ TARGET_NR_lremovexattr, "lremovexattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lseek
+{ TARGET_NR_lseek, "lseek" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lsetxattr
+{ TARGET_NR_lsetxattr, "lsetxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_lstat
+{ TARGET_NR_lstat, "lstat" , NULL, print_lstat, NULL },
+#endif
+#ifdef TARGET_NR_lstat64
+{ TARGET_NR_lstat64, "lstat64" , NULL, print_lstat64, NULL },
+#endif
+#ifdef TARGET_NR_madvise
+{ TARGET_NR_madvise, "madvise" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_madvise1
+{ TARGET_NR_madvise1, "madvise1" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mbind
+{ TARGET_NR_mbind, "mbind" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_memory_ordering
+{ TARGET_NR_memory_ordering, "memory_ordering" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_migrate_pages
+{ TARGET_NR_migrate_pages, "migrate_pages" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mincore
+{ TARGET_NR_mincore, "mincore" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mkdir
+{ TARGET_NR_mkdir, "mkdir" , NULL, print_mkdir, NULL },
+#endif
+#ifdef TARGET_NR_mkdirat
+{ TARGET_NR_mkdirat, "mkdirat" , NULL, print_mkdirat, NULL },
+#endif
+#ifdef TARGET_NR_mknod
+{ TARGET_NR_mknod, "mknod" , NULL, print_mknod, NULL },
+#endif
+#ifdef TARGET_NR_mknodat
+{ TARGET_NR_mknodat, "mknodat" , NULL, print_mknodat, NULL },
+#endif
+#ifdef TARGET_NR_mlock
+{ TARGET_NR_mlock, "mlock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mlockall
+{ TARGET_NR_mlockall, "mlockall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mmap
+{ TARGET_NR_mmap, "mmap" , NULL, print_mmap, print_syscall_ret_addr },
+#endif
+#ifdef TARGET_NR_mmap2
+{ TARGET_NR_mmap2, "mmap2" , NULL, print_mmap2, print_syscall_ret_addr },
+#endif
+#ifdef TARGET_NR_modify_ldt
+{ TARGET_NR_modify_ldt, "modify_ldt" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mount
+{ TARGET_NR_mount, "mount" , NULL, print_mount, NULL },
+#endif
+#ifdef TARGET_NR_move_pages
+{ TARGET_NR_move_pages, "move_pages" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mprotect
+{ TARGET_NR_mprotect, "mprotect" , NULL, print_mprotect, NULL },
+#endif
+#ifdef TARGET_NR_mpx
+{ TARGET_NR_mpx, "mpx" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_mq_getsetattr
+{ TARGET_NR_mq_getsetattr, "mq_getsetattr" , "%s(%d,%p,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_mq_notify
+{ TARGET_NR_mq_notify, "mq_notify" , "%s(%d,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_mq_open
+{ TARGET_NR_mq_open, "mq_open" , NULL, print_mq_open, NULL },
+#endif
+#ifdef TARGET_NR_mq_timedreceive
+{ TARGET_NR_mq_timedreceive, "mq_timedreceive" , "%s(%d,%p,%d,%u,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_mq_timedsend
+{ TARGET_NR_mq_timedsend, "mq_timedsend" , "%s(%d,%p,%d,%u,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_mq_unlink
+{ TARGET_NR_mq_unlink, "mq_unlink" , NULL, print_mq_unlink, NULL },
+#endif
+#ifdef TARGET_NR_mremap
+{ TARGET_NR_mremap, "mremap" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_msgctl
+{ TARGET_NR_msgctl, "msgctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_msgget
+{ TARGET_NR_msgget, "msgget" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_msgrcv
+{ TARGET_NR_msgrcv, "msgrcv" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_msgsnd
+{ TARGET_NR_msgsnd, "msgsnd" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_msync
+{ TARGET_NR_msync, "msync" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_multiplexer
+{ TARGET_NR_multiplexer, "multiplexer" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_munlock
+{ TARGET_NR_munlock, "munlock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_munlockall
+{ TARGET_NR_munlockall, "munlockall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_munmap
+{ TARGET_NR_munmap, "munmap" , NULL, print_munmap, NULL },
+#endif
+#ifdef TARGET_NR_nanosleep
+{ TARGET_NR_nanosleep, "nanosleep" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_fstatat64
+{ TARGET_NR_fstatat64, "fstatat64" , NULL, print_fstatat64, NULL },
+#endif
+#ifdef TARGET_NR_newfstatat
+{ TARGET_NR_newfstatat, "newfstatat" , NULL, print_newfstatat, NULL },
+#endif
+#ifdef TARGET_NR__newselect
+{ TARGET_NR__newselect, "_newselect" , NULL, print_newselect, print_syscall_ret_newselect },
+#endif
+#ifdef TARGET_NR_nfsservctl
+{ TARGET_NR_nfsservctl, "nfsservctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_nice
+{ TARGET_NR_nice, "nice" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_old_adjtimex
+{ TARGET_NR_old_adjtimex, "old_adjtimex" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_oldfstat
+{ TARGET_NR_oldfstat, "oldfstat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_oldlstat
+{ TARGET_NR_oldlstat, "oldlstat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_oldolduname
+{ TARGET_NR_oldolduname, "oldolduname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_oldstat
+{ TARGET_NR_oldstat, "oldstat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_oldumount
+{ TARGET_NR_oldumount, "oldumount" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_olduname
+{ TARGET_NR_olduname, "olduname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_open
+{ TARGET_NR_open, "open" , NULL, print_open, NULL },
+#endif
+#ifdef TARGET_NR_openat
+{ TARGET_NR_openat, "openat" , NULL, print_openat, NULL },
+#endif
+#ifdef TARGET_NR_osf_adjtime
+{ TARGET_NR_osf_adjtime, "osf_adjtime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_afs_syscall
+{ TARGET_NR_osf_afs_syscall, "osf_afs_syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_alt_plock
+{ TARGET_NR_osf_alt_plock, "osf_alt_plock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_alt_setsid
+{ TARGET_NR_osf_alt_setsid, "osf_alt_setsid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_alt_sigpending
+{ TARGET_NR_osf_alt_sigpending, "osf_alt_sigpending" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_asynch_daemon
+{ TARGET_NR_osf_asynch_daemon, "osf_asynch_daemon" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_audcntl
+{ TARGET_NR_osf_audcntl, "osf_audcntl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_audgen
+{ TARGET_NR_osf_audgen, "osf_audgen" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_chflags
+{ TARGET_NR_osf_chflags, "osf_chflags" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_execve
+{ TARGET_NR_osf_execve, "osf_execve" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_exportfs
+{ TARGET_NR_osf_exportfs, "osf_exportfs" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_fchflags
+{ TARGET_NR_osf_fchflags, "osf_fchflags" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_fdatasync
+{ TARGET_NR_osf_fdatasync, "osf_fdatasync" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_fpathconf
+{ TARGET_NR_osf_fpathconf, "osf_fpathconf" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_fstatfs
+{ TARGET_NR_osf_fstatfs, "osf_fstatfs" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_fuser
+{ TARGET_NR_osf_fuser, "osf_fuser" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getaddressconf
+{ TARGET_NR_osf_getaddressconf, "osf_getaddressconf" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getdirentries
+{ TARGET_NR_osf_getdirentries, "osf_getdirentries" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getdomainname
+{ TARGET_NR_osf_getdomainname, "osf_getdomainname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getfh
+{ TARGET_NR_osf_getfh, "osf_getfh" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getfsstat
+{ TARGET_NR_osf_getfsstat, "osf_getfsstat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_gethostid
+{ TARGET_NR_osf_gethostid, "osf_gethostid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getitimer
+{ TARGET_NR_osf_getitimer, "osf_getitimer" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getlogin
+{ TARGET_NR_osf_getlogin, "osf_getlogin" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getmnt
+{ TARGET_NR_osf_getmnt, "osf_getmnt" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getrusage
+{ TARGET_NR_osf_getrusage, "osf_getrusage" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_getsysinfo
+{ TARGET_NR_osf_getsysinfo, "osf_getsysinfo" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_gettimeofday
+{ TARGET_NR_osf_gettimeofday, "osf_gettimeofday" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_kloadcall
+{ TARGET_NR_osf_kloadcall, "osf_kloadcall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_kmodcall
+{ TARGET_NR_osf_kmodcall, "osf_kmodcall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_memcntl
+{ TARGET_NR_osf_memcntl, "osf_memcntl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_mincore
+{ TARGET_NR_osf_mincore, "osf_mincore" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_mount
+{ TARGET_NR_osf_mount, "osf_mount" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_mremap
+{ TARGET_NR_osf_mremap, "osf_mremap" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_msfs_syscall
+{ TARGET_NR_osf_msfs_syscall, "osf_msfs_syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_msleep
+{ TARGET_NR_osf_msleep, "osf_msleep" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_mvalid
+{ TARGET_NR_osf_mvalid, "osf_mvalid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_mwakeup
+{ TARGET_NR_osf_mwakeup, "osf_mwakeup" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_naccept
+{ TARGET_NR_osf_naccept, "osf_naccept" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_nfssvc
+{ TARGET_NR_osf_nfssvc, "osf_nfssvc" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_ngetpeername
+{ TARGET_NR_osf_ngetpeername, "osf_ngetpeername" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_ngetsockname
+{ TARGET_NR_osf_ngetsockname, "osf_ngetsockname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_nrecvfrom
+{ TARGET_NR_osf_nrecvfrom, "osf_nrecvfrom" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_nrecvmsg
+{ TARGET_NR_osf_nrecvmsg, "osf_nrecvmsg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_nsendmsg
+{ TARGET_NR_osf_nsendmsg, "osf_nsendmsg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_ntp_adjtime
+{ TARGET_NR_osf_ntp_adjtime, "osf_ntp_adjtime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_ntp_gettime
+{ TARGET_NR_osf_ntp_gettime, "osf_ntp_gettime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_creat
+{ TARGET_NR_osf_old_creat, "osf_old_creat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_fstat
+{ TARGET_NR_osf_old_fstat, "osf_old_fstat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_getpgrp
+{ TARGET_NR_osf_old_getpgrp, "osf_old_getpgrp" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_killpg
+{ TARGET_NR_osf_old_killpg, "osf_old_killpg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_lstat
+{ TARGET_NR_osf_old_lstat, "osf_old_lstat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_open
+{ TARGET_NR_osf_old_open, "osf_old_open" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_oldquota
+{ TARGET_NR_osf_oldquota, "osf_oldquota" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_sigaction
+{ TARGET_NR_osf_old_sigaction, "osf_old_sigaction" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_sigblock
+{ TARGET_NR_osf_old_sigblock, "osf_old_sigblock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_sigreturn
+{ TARGET_NR_osf_old_sigreturn, "osf_old_sigreturn" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_sigsetmask
+{ TARGET_NR_osf_old_sigsetmask, "osf_old_sigsetmask" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_sigvec
+{ TARGET_NR_osf_old_sigvec, "osf_old_sigvec" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_stat
+{ TARGET_NR_osf_old_stat, "osf_old_stat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_vadvise
+{ TARGET_NR_osf_old_vadvise, "osf_old_vadvise" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_vtrace
+{ TARGET_NR_osf_old_vtrace, "osf_old_vtrace" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_old_wait
+{ TARGET_NR_osf_old_wait, "osf_old_wait" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_pathconf
+{ TARGET_NR_osf_pathconf, "osf_pathconf" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_pid_block
+{ TARGET_NR_osf_pid_block, "osf_pid_block" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_pid_unblock
+{ TARGET_NR_osf_pid_unblock, "osf_pid_unblock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_plock
+{ TARGET_NR_osf_plock, "osf_plock" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_priocntlset
+{ TARGET_NR_osf_priocntlset, "osf_priocntlset" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_profil
+{ TARGET_NR_osf_profil, "osf_profil" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_proplist_syscall
+{ TARGET_NR_osf_proplist_syscall, "osf_proplist_syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_reboot
+{ TARGET_NR_osf_reboot, "osf_reboot" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_revoke
+{ TARGET_NR_osf_revoke, "osf_revoke" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sbrk
+{ TARGET_NR_osf_sbrk, "osf_sbrk" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_security
+{ TARGET_NR_osf_security, "osf_security" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_select
+{ TARGET_NR_osf_select, "osf_select" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sethostid
+{ TARGET_NR_osf_sethostid, "osf_sethostid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_setitimer
+{ TARGET_NR_osf_setitimer, "osf_setitimer" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_setlogin
+{ TARGET_NR_osf_setlogin, "osf_setlogin" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_set_program_attributes
+{ TARGET_NR_osf_set_program_attributes, "osf_set_program_attributes" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_set_speculative
+{ TARGET_NR_osf_set_speculative, "osf_set_speculative" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_setsysinfo
+{ TARGET_NR_osf_setsysinfo, "osf_setsysinfo" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_settimeofday
+{ TARGET_NR_osf_settimeofday, "osf_settimeofday" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_shmat
+{ TARGET_NR_osf_shmat, "osf_shmat" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_signal
+{ TARGET_NR_osf_signal, "osf_signal" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sigprocmask
+{ TARGET_NR_osf_sigprocmask, "osf_sigprocmask" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sigsendset
+{ TARGET_NR_osf_sigsendset, "osf_sigsendset" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sigstack
+{ TARGET_NR_osf_sigstack, "osf_sigstack" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sigwaitprim
+{ TARGET_NR_osf_sigwaitprim, "osf_sigwaitprim" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sstk
+{ TARGET_NR_osf_sstk, "osf_sstk" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_statfs
+{ TARGET_NR_osf_statfs, "osf_statfs" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_subsys_info
+{ TARGET_NR_osf_subsys_info, "osf_subsys_info" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_swapctl
+{ TARGET_NR_osf_swapctl, "osf_swapctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_swapon
+{ TARGET_NR_osf_swapon, "osf_swapon" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_syscall
+{ TARGET_NR_osf_syscall, "osf_syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_sysinfo
+{ TARGET_NR_osf_sysinfo, "osf_sysinfo" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_table
+{ TARGET_NR_osf_table, "osf_table" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_uadmin
+{ TARGET_NR_osf_uadmin, "osf_uadmin" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_usleep_thread
+{ TARGET_NR_osf_usleep_thread, "osf_usleep_thread" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_uswitch
+{ TARGET_NR_osf_uswitch, "osf_uswitch" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_utc_adjtime
+{ TARGET_NR_osf_utc_adjtime, "osf_utc_adjtime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_utc_gettime
+{ TARGET_NR_osf_utc_gettime, "osf_utc_gettime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_utimes
+{ TARGET_NR_osf_utimes, "osf_utimes" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_utsname
+{ TARGET_NR_osf_utsname, "osf_utsname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_wait4
+{ TARGET_NR_osf_wait4, "osf_wait4" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_osf_waitid
+{ TARGET_NR_osf_waitid, "osf_waitid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pause
+{ TARGET_NR_pause, "pause" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pciconfig_iobase
+{ TARGET_NR_pciconfig_iobase, "pciconfig_iobase" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pciconfig_read
+{ TARGET_NR_pciconfig_read, "pciconfig_read" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pciconfig_write
+{ TARGET_NR_pciconfig_write, "pciconfig_write" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_perfctr
+{ TARGET_NR_perfctr, "perfctr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_personality
+{ TARGET_NR_personality, "personality" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pipe
+{ TARGET_NR_pipe, "pipe" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pivot_root
+{ TARGET_NR_pivot_root, "pivot_root" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_poll
+{ TARGET_NR_poll, "poll" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ppoll
+{ TARGET_NR_ppoll, "ppoll" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_prctl
+{ TARGET_NR_prctl, "prctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pread
+{ TARGET_NR_pread, "pread" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pread64
+{ TARGET_NR_pread64, "pread64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_prof
+{ TARGET_NR_prof, "prof" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_profil
+{ TARGET_NR_profil, "profil" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pselect6
+{ TARGET_NR_pselect6, "pselect6" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ptrace
+{ TARGET_NR_ptrace, "ptrace" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_putpmsg
+{ TARGET_NR_putpmsg, "putpmsg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pwrite
+{ TARGET_NR_pwrite, "pwrite" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_pwrite64
+{ TARGET_NR_pwrite64, "pwrite64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_query_module
+{ TARGET_NR_query_module, "query_module" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_quotactl
+{ TARGET_NR_quotactl, "quotactl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_read
+{ TARGET_NR_read, "read" , "%s(%d,%#x,%d)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_readahead
+{ TARGET_NR_readahead, "readahead" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_readdir
+{ TARGET_NR_readdir, "readdir" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_readlink
+{ TARGET_NR_readlink, "readlink" , NULL, print_readlink, NULL },
+#endif
+#ifdef TARGET_NR_readlinkat
+{ TARGET_NR_readlinkat, "readlinkat" , NULL, print_readlinkat, NULL },
+#endif
+#ifdef TARGET_NR_readv
+{ TARGET_NR_readv, "readv" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_reboot
+{ TARGET_NR_reboot, "reboot" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_recv
+{ TARGET_NR_recv, "recv" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_recvfrom
+{ TARGET_NR_recvfrom, "recvfrom" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_recvmsg
+{ TARGET_NR_recvmsg, "recvmsg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_remap_file_pages
+{ TARGET_NR_remap_file_pages, "remap_file_pages" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_removexattr
+{ TARGET_NR_removexattr, "removexattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rename
+{ TARGET_NR_rename, "rename" , NULL, print_rename, NULL },
+#endif
+#ifdef TARGET_NR_renameat
+{ TARGET_NR_renameat, "renameat" , NULL, print_renameat, NULL },
+#endif
+#ifdef TARGET_NR_request_key
+{ TARGET_NR_request_key, "request_key" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_reserved221
+{ TARGET_NR_reserved221, "reserved221" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_reserved82
+{ TARGET_NR_reserved82, "reserved82" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_restart_syscall
+{ TARGET_NR_restart_syscall, "restart_syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rmdir
+{ TARGET_NR_rmdir, "rmdir" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigaction
+{ TARGET_NR_rt_sigaction, "rt_sigaction" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigpending
+{ TARGET_NR_rt_sigpending, "rt_sigpending" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigprocmask
+{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigqueueinfo
+{ TARGET_NR_rt_sigqueueinfo, "rt_sigqueueinfo" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigreturn
+{ TARGET_NR_rt_sigreturn, "rt_sigreturn" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigsuspend
+{ TARGET_NR_rt_sigsuspend, "rt_sigsuspend" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_rt_sigtimedwait
+{ TARGET_NR_rt_sigtimedwait, "rt_sigtimedwait" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_getaffinity
+{ TARGET_NR_sched_getaffinity, "sched_getaffinity" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_get_affinity
+{ TARGET_NR_sched_get_affinity, "sched_get_affinity" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_getparam
+{ TARGET_NR_sched_getparam, "sched_getparam" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_get_priority_max
+{ TARGET_NR_sched_get_priority_max, "sched_get_priority_max" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_get_priority_min
+{ TARGET_NR_sched_get_priority_min, "sched_get_priority_min" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_getscheduler
+{ TARGET_NR_sched_getscheduler, "sched_getscheduler" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_rr_get_interval
+{ TARGET_NR_sched_rr_get_interval, "sched_rr_get_interval" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_setaffinity
+{ TARGET_NR_sched_setaffinity, "sched_setaffinity" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_set_affinity
+{ TARGET_NR_sched_set_affinity, "sched_set_affinity" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_setparam
+{ TARGET_NR_sched_setparam, "sched_setparam" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_setscheduler
+{ TARGET_NR_sched_setscheduler, "sched_setscheduler" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sched_yield
+{ TARGET_NR_sched_yield, "sched_yield" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_security
+{ TARGET_NR_security, "security" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_select
+{ TARGET_NR_select, "select" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_semctl
+{ TARGET_NR_semctl, "semctl" , NULL, print_semctl, NULL },
+#endif
+#ifdef TARGET_NR_semget
+{ TARGET_NR_semget, "semget" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_semop
+{ TARGET_NR_semop, "semop" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_semtimedop
+{ TARGET_NR_semtimedop, "semtimedop" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_send
+{ TARGET_NR_send, "send" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sendfile
+{ TARGET_NR_sendfile, "sendfile" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sendfile64
+{ TARGET_NR_sendfile64, "sendfile64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sendmsg
+{ TARGET_NR_sendmsg, "sendmsg" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sendto
+{ TARGET_NR_sendto, "sendto" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setdomainname
+{ TARGET_NR_setdomainname, "setdomainname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setfsgid
+{ TARGET_NR_setfsgid, "setfsgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setfsgid32
+{ TARGET_NR_setfsgid32, "setfsgid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setfsuid
+{ TARGET_NR_setfsuid, "setfsuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setfsuid32
+{ TARGET_NR_setfsuid32, "setfsuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setgid
+{ TARGET_NR_setgid, "setgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setgid32
+{ TARGET_NR_setgid32, "setgid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setgroups
+{ TARGET_NR_setgroups, "setgroups" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setgroups32
+{ TARGET_NR_setgroups32, "setgroups32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sethae
+{ TARGET_NR_sethae, "sethae" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sethostname
+{ TARGET_NR_sethostname, "sethostname" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setitimer
+{ TARGET_NR_setitimer, "setitimer" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_set_mempolicy
+{ TARGET_NR_set_mempolicy, "set_mempolicy" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setpgid
+{ TARGET_NR_setpgid, "setpgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setpgrp
+{ TARGET_NR_setpgrp, "setpgrp" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setpriority
+{ TARGET_NR_setpriority, "setpriority" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setregid
+{ TARGET_NR_setregid, "setregid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setregid32
+{ TARGET_NR_setregid32, "setregid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setresgid
+{ TARGET_NR_setresgid, "setresgid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setresgid32
+{ TARGET_NR_setresgid32, "setresgid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setresuid
+{ TARGET_NR_setresuid, "setresuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setresuid32
+{ TARGET_NR_setresuid32, "setresuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setreuid
+{ TARGET_NR_setreuid, "setreuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setreuid32
+{ TARGET_NR_setreuid32, "setreuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setrlimit
+{ TARGET_NR_setrlimit, "setrlimit" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_set_robust_list
+{ TARGET_NR_set_robust_list, "set_robust_list" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setsid
+{ TARGET_NR_setsid, "setsid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setsockopt
+{ TARGET_NR_setsockopt, "setsockopt" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_set_thread_area
+{ TARGET_NR_set_thread_area, "set_thread_area" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_set_tid_address
+{ TARGET_NR_set_tid_address, "set_tid_address" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_settimeofday
+{ TARGET_NR_settimeofday, "settimeofday" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setuid
+{ TARGET_NR_setuid, "setuid" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setuid32
+{ TARGET_NR_setuid32, "setuid32" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_setxattr
+{ TARGET_NR_setxattr, "setxattr" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sgetmask
+{ TARGET_NR_sgetmask, "sgetmask" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_shmat
+{ TARGET_NR_shmat, "shmat" , NULL, NULL, print_syscall_ret_addr },
+#endif
+#ifdef TARGET_NR_shmctl
+{ TARGET_NR_shmctl, "shmctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_shmdt
+{ TARGET_NR_shmdt, "shmdt" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_shmget
+{ TARGET_NR_shmget, "shmget" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_shutdown
+{ TARGET_NR_shutdown, "shutdown" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sigaction
+{ TARGET_NR_sigaction, "sigaction" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sigaltstack
+{ TARGET_NR_sigaltstack, "sigaltstack" , "%s(%p,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_signal
+{ TARGET_NR_signal, "signal" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sigpending
+{ TARGET_NR_sigpending, "sigpending" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sigprocmask
+{ TARGET_NR_sigprocmask, "sigprocmask" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sigreturn
+{ TARGET_NR_sigreturn, "sigreturn" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sigsuspend
+{ TARGET_NR_sigsuspend, "sigsuspend" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_socket
+{ TARGET_NR_socket, "socket" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_socketcall
+{ TARGET_NR_socketcall, "socketcall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_socketpair
+{ TARGET_NR_socketpair, "socketpair" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_splice
+{ TARGET_NR_splice, "splice" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ssetmask
+{ TARGET_NR_ssetmask, "ssetmask" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_stat
+{ TARGET_NR_stat, "stat" , NULL, print_stat, NULL },
+#endif
+#ifdef TARGET_NR_stat64
+{ TARGET_NR_stat64, "stat64" , NULL, print_stat64, NULL },
+#endif
+#ifdef TARGET_NR_statfs
+{ TARGET_NR_statfs, "statfs" , NULL, print_statfs, NULL },
+#endif
+#ifdef TARGET_NR_statfs64
+{ TARGET_NR_statfs64, "statfs64" , NULL, print_statfs64, NULL },
+#endif
+#ifdef TARGET_NR_stime
+{ TARGET_NR_stime, "stime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_streams1
+{ TARGET_NR_streams1, "streams1" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_streams2
+{ TARGET_NR_streams2, "streams2" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_stty
+{ TARGET_NR_stty, "stty" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_swapcontext
+{ TARGET_NR_swapcontext, "swapcontext" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_swapoff
+{ TARGET_NR_swapoff, "swapoff" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_swapon
+{ TARGET_NR_swapon, "swapon" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_symlink
+{ TARGET_NR_symlink, "symlink" , NULL, print_symlink, NULL },
+#endif
+#ifdef TARGET_NR_symlinkat
+{ TARGET_NR_symlinkat, "symlinkat", NULL, print_symlinkat, NULL },
+#endif
+#ifdef TARGET_NR_sync
+{ TARGET_NR_sync, "sync" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sync_file_range
+{ TARGET_NR_sync_file_range, "sync_file_range" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_syscall
+{ TARGET_NR_syscall, "syscall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR__sysctl
+{ TARGET_NR__sysctl, "_sysctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sys_epoll_create
+{ TARGET_NR_sys_epoll_create, "sys_epoll_create" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sys_epoll_ctl
+{ TARGET_NR_sys_epoll_ctl, "sys_epoll_ctl" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sys_epoll_wait
+{ TARGET_NR_sys_epoll_wait, "sys_epoll_wait" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sysfs
+{ TARGET_NR_sysfs, "sysfs" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sysinfo
+{ TARGET_NR_sysinfo, "sysinfo" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sys_kexec_load
+{ TARGET_NR_sys_kexec_load, "sys_kexec_load" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_syslog
+{ TARGET_NR_syslog, "syslog" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sysmips
+{ TARGET_NR_sysmips, "sysmips" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sys_setaltroot
+{ TARGET_NR_sys_setaltroot, "sys_setaltroot" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_tee
+{ TARGET_NR_tee, "tee" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_tgkill
+{ TARGET_NR_tgkill, "tgkill" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_time
+{ TARGET_NR_time, "time" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_timer_create
+{ TARGET_NR_timer_create, "timer_create" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_timer_delete
+{ TARGET_NR_timer_delete, "timer_delete" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_timer_getoverrun
+{ TARGET_NR_timer_getoverrun, "timer_getoverrun" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_timer_gettime
+{ TARGET_NR_timer_gettime, "timer_gettime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_timer_settime
+{ TARGET_NR_timer_settime, "timer_settime" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_times
+{ TARGET_NR_times, "times" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_tkill
+{ TARGET_NR_tkill, "tkill" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_truncate
+{ TARGET_NR_truncate, "truncate" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_truncate64
+{ TARGET_NR_truncate64, "truncate64" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_tuxcall
+{ TARGET_NR_tuxcall, "tuxcall" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ugetrlimit
+{ TARGET_NR_ugetrlimit, "ugetrlimit" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ulimit
+{ TARGET_NR_ulimit, "ulimit" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_umask
+{ TARGET_NR_umask, "umask" , "%s(%#o)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_umount
+{ TARGET_NR_umount, "umount" , NULL, print_umount, NULL },
+#endif
+#ifdef TARGET_NR_umount2
+{ TARGET_NR_umount2, "umount2" , NULL, print_umount2, NULL },
+#endif
+#ifdef TARGET_NR_uname
+{ TARGET_NR_uname, "uname" , "%s(%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_unlink
+{ TARGET_NR_unlink, "unlink" , NULL, print_unlink, NULL },
+#endif
+#ifdef TARGET_NR_unlinkat
+{ TARGET_NR_unlinkat, "unlinkat" , NULL, print_unlinkat, NULL },
+#endif
+#ifdef TARGET_NR_unshare
+{ TARGET_NR_unshare, "unshare" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_unused109
+{ TARGET_NR_unused109, "unused109" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_unused150
+{ TARGET_NR_unused150, "unused150" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_unused18
+{ TARGET_NR_unused18, "unused18" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_unused28
+{ TARGET_NR_unused28, "unused28" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_unused59
+{ TARGET_NR_unused59, "unused59" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_unused84
+{ TARGET_NR_unused84, "unused84" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_uselib
+{ TARGET_NR_uselib, "uselib" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_ustat
+{ TARGET_NR_ustat, "ustat" , "%s(%#x,%p)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_utime
+{ TARGET_NR_utime, "utime" , NULL, print_utime, NULL },
+#endif
+#ifdef TARGET_NR_utimes
+{ TARGET_NR_utimes, "utimes" , NULL, print_utimes, NULL },
+#endif
+#ifdef TARGET_NR_utrap_install
+{ TARGET_NR_utrap_install, "utrap_install" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_vfork
+{ TARGET_NR_vfork, "vfork" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_vhangup
+{ TARGET_NR_vhangup, "vhangup" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_vm86
+{ TARGET_NR_vm86, "vm86" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_vm86old
+{ TARGET_NR_vm86old, "vm86old" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_vmsplice
+{ TARGET_NR_vmsplice, "vmsplice" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_vserver
+{ TARGET_NR_vserver, "vserver" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_wait4
+{ TARGET_NR_wait4, "wait4" , NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_waitid
+{ TARGET_NR_waitid, "waitid" , "%s(%#x,%d,%p,%#x)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_waitpid
+{ TARGET_NR_waitpid, "waitpid" , "%s(%d,%p,%#x)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_write
+{ TARGET_NR_write, "write" , "%s(%d,%#x,%d)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_writev
+{ TARGET_NR_writev, "writev" , "%s(%d,%p,%#x)", NULL, NULL },
+#endif
+#ifdef TARGET_NR_utimensat
+{ TARGET_NR_utimensat, "utimensat", NULL, print_utimensat, NULL },
+#endif
+#ifdef TARGET_NR_sync_file_range
+{ TARGET_NR_sync_file_range, "sync_file_range", NULL, NULL, NULL },
+#endif
+#ifdef TARGET_NR_sync_file_range2
+{ TARGET_NR_sync_file_range2, "sync_file_range2", NULL, NULL, NULL },
+#endif
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
new file mode 100644
index 0000000..499c4d7
--- /dev/null
+++ b/linux-user/syscall.c
@@ -0,0 +1,7514 @@
+/*
+ * Linux syscalls
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#define _ATFILE_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <elf.h>
+#include <endian.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/swap.h>
+#include <signal.h>
+#include <sched.h>
+#ifdef __ia64__
+int __clone2(int (*fn)(void *), void *child_stack_base,
+ size_t stack_size, int flags, void *arg, ...);
+#endif
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/times.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <sys/statfs.h>
+#include <utime.h>
+#include <sys/sysinfo.h>
+#include <sys/utsname.h>
+//#include <sys/user.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <qemu-common.h>
+#ifdef TARGET_GPROF
+#include <sys/gmon.h>
+#endif
+#ifdef CONFIG_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+#define termios host_termios
+#define winsize host_winsize
+#define termio host_termio
+#define sgttyb host_sgttyb /* same as target */
+#define tchars host_tchars /* same as target */
+#define ltchars host_ltchars /* same as target */
+
+#include <linux/termios.h>
+#include <linux/unistd.h>
+#include <linux/utsname.h>
+#include <linux/cdrom.h>
+#include <linux/hdreg.h>
+#include <linux/soundcard.h>
+#include <linux/kd.h>
+#include <linux/mtio.h>
+#include <linux/fs.h>
+#if defined(CONFIG_FIEMAP)
+#include <linux/fiemap.h>
+#endif
+#include <linux/fb.h>
+#include <linux/vt.h>
+#include "linux_loop.h"
+#include "cpu-uname.h"
+
+#include "qemu.h"
+#include "qemu-common.h"
+
+#if defined(CONFIG_USE_NPTL)
+#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \
+ CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)
+#else
+/* XXX: Hardcode the above values. */
+#define CLONE_NPTL_FLAGS2 0
+#endif
+
+//#define DEBUG
+
+//#include <linux/msdos_fs.h>
+#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct linux_dirent [2])
+#define VFAT_IOCTL_READDIR_SHORT _IOR('r', 2, struct linux_dirent [2])
+
+
+#undef _syscall0
+#undef _syscall1
+#undef _syscall2
+#undef _syscall3
+#undef _syscall4
+#undef _syscall5
+#undef _syscall6
+
+#define _syscall0(type,name) \
+static type name (void) \
+{ \
+ return syscall(__NR_##name); \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+static type name (type1 arg1) \
+{ \
+ return syscall(__NR_##name, arg1); \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+static type name (type1 arg1,type2 arg2) \
+{ \
+ return syscall(__NR_##name, arg1, arg2); \
+}
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+static type name (type1 arg1,type2 arg2,type3 arg3) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3); \
+}
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3, arg4); \
+}
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \
+}
+
+
+#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \
+ type6 arg6) \
+{ \
+ return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \
+}
+
+
+#define __NR_sys_uname __NR_uname
+#define __NR_sys_faccessat __NR_faccessat
+#define __NR_sys_fchmodat __NR_fchmodat
+#define __NR_sys_fchownat __NR_fchownat
+#define __NR_sys_fstatat64 __NR_fstatat64
+#define __NR_sys_futimesat __NR_futimesat
+#define __NR_sys_getcwd1 __NR_getcwd
+#define __NR_sys_getdents __NR_getdents
+#define __NR_sys_getdents64 __NR_getdents64
+#define __NR_sys_getpriority __NR_getpriority
+#define __NR_sys_linkat __NR_linkat
+#define __NR_sys_mkdirat __NR_mkdirat
+#define __NR_sys_mknodat __NR_mknodat
+#define __NR_sys_newfstatat __NR_newfstatat
+#define __NR_sys_openat __NR_openat
+#define __NR_sys_readlinkat __NR_readlinkat
+#define __NR_sys_renameat __NR_renameat
+#define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo
+#define __NR_sys_symlinkat __NR_symlinkat
+#define __NR_sys_syslog __NR_syslog
+#define __NR_sys_tgkill __NR_tgkill
+#define __NR_sys_tkill __NR_tkill
+#define __NR_sys_unlinkat __NR_unlinkat
+#define __NR_sys_utimensat __NR_utimensat
+#define __NR_sys_futex __NR_futex
+#define __NR_sys_inotify_init __NR_inotify_init
+#define __NR_sys_inotify_add_watch __NR_inotify_add_watch
+#define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch
+
+#if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
+#define __NR__llseek __NR_lseek
+#endif
+
+#ifdef __NR_gettid
+_syscall0(int, gettid)
+#else
+/* This is a replacement for the host gettid() and must return a host
+ errno. */
+static int gettid(void) {
+ return -ENOSYS;
+}
+#endif
+_syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count);
+#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
+_syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count);
+#endif
+_syscall2(int, sys_getpriority, int, which, int, who);
+#if defined(TARGET_NR__llseek) && defined(__NR_llseek)
+_syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
+ loff_t *, res, uint, wh);
+#endif
+_syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo)
+_syscall3(int,sys_syslog,int,type,char*,bufp,int,len)
+#if defined(TARGET_NR_tgkill) && defined(__NR_tgkill)
+_syscall3(int,sys_tgkill,int,tgid,int,pid,int,sig)
+#endif
+#if defined(TARGET_NR_tkill) && defined(__NR_tkill)
+_syscall2(int,sys_tkill,int,tid,int,sig)
+#endif
+#ifdef __NR_exit_group
+_syscall1(int,exit_group,int,error_code)
+#endif
+#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address)
+_syscall1(int,set_tid_address,int *,tidptr)
+#endif
+#if defined(CONFIG_USE_NPTL)
+#if defined(TARGET_NR_futex) && defined(__NR_futex)
+_syscall6(int,sys_futex,int *,uaddr,int,op,int,val,
+ const struct timespec *,timeout,int *,uaddr2,int,val3)
+#endif
+#endif
+
+static bitmask_transtbl fcntl_flags_tbl[] = {
+ { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, },
+ { TARGET_O_ACCMODE, TARGET_O_RDWR, O_ACCMODE, O_RDWR, },
+ { TARGET_O_CREAT, TARGET_O_CREAT, O_CREAT, O_CREAT, },
+ { TARGET_O_EXCL, TARGET_O_EXCL, O_EXCL, O_EXCL, },
+ { TARGET_O_NOCTTY, TARGET_O_NOCTTY, O_NOCTTY, O_NOCTTY, },
+ { TARGET_O_TRUNC, TARGET_O_TRUNC, O_TRUNC, O_TRUNC, },
+ { TARGET_O_APPEND, TARGET_O_APPEND, O_APPEND, O_APPEND, },
+ { TARGET_O_NONBLOCK, TARGET_O_NONBLOCK, O_NONBLOCK, O_NONBLOCK, },
+ { TARGET_O_SYNC, TARGET_O_SYNC, O_SYNC, O_SYNC, },
+ { TARGET_FASYNC, TARGET_FASYNC, FASYNC, FASYNC, },
+ { TARGET_O_DIRECTORY, TARGET_O_DIRECTORY, O_DIRECTORY, O_DIRECTORY, },
+ { TARGET_O_NOFOLLOW, TARGET_O_NOFOLLOW, O_NOFOLLOW, O_NOFOLLOW, },
+ { TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, },
+#if defined(O_DIRECT)
+ { TARGET_O_DIRECT, TARGET_O_DIRECT, O_DIRECT, O_DIRECT, },
+#endif
+ { 0, 0, 0, 0 }
+};
+
+#define COPY_UTSNAME_FIELD(dest, src) \
+ do { \
+ /* __NEW_UTS_LEN doesn't include terminating null */ \
+ (void) strncpy((dest), (src), __NEW_UTS_LEN); \
+ (dest)[__NEW_UTS_LEN] = '\0'; \
+ } while (0)
+
+static int sys_uname(struct new_utsname *buf)
+{
+ struct utsname uts_buf;
+
+ if (uname(&uts_buf) < 0)
+ return (-1);
+
+ /*
+ * Just in case these have some differences, we
+ * translate utsname to new_utsname (which is the
+ * struct linux kernel uses).
+ */
+
+ bzero(buf, sizeof (*buf));
+ COPY_UTSNAME_FIELD(buf->sysname, uts_buf.sysname);
+ COPY_UTSNAME_FIELD(buf->nodename, uts_buf.nodename);
+ COPY_UTSNAME_FIELD(buf->release, uts_buf.release);
+ COPY_UTSNAME_FIELD(buf->version, uts_buf.version);
+ COPY_UTSNAME_FIELD(buf->machine, uts_buf.machine);
+#ifdef _GNU_SOURCE
+ COPY_UTSNAME_FIELD(buf->domainname, uts_buf.domainname);
+#endif
+ return (0);
+
+#undef COPY_UTSNAME_FIELD
+}
+
+static int sys_getcwd1(char *buf, size_t size)
+{
+ if (getcwd(buf, size) == NULL) {
+ /* getcwd() sets errno */
+ return (-1);
+ }
+ return strlen(buf)+1;
+}
+
+#ifdef CONFIG_ATFILE
+/*
+ * Host system seems to have atfile syscall stubs available. We
+ * now enable them one by one as specified by target syscall_nr.h.
+ */
+
+#ifdef TARGET_NR_faccessat
+static int sys_faccessat(int dirfd, const char *pathname, int mode)
+{
+ return (faccessat(dirfd, pathname, mode, 0));
+}
+#endif
+#ifdef TARGET_NR_fchmodat
+static int sys_fchmodat(int dirfd, const char *pathname, mode_t mode)
+{
+ return (fchmodat(dirfd, pathname, mode, 0));
+}
+#endif
+#if defined(TARGET_NR_fchownat) && defined(USE_UID16)
+static int sys_fchownat(int dirfd, const char *pathname, uid_t owner,
+ gid_t group, int flags)
+{
+ return (fchownat(dirfd, pathname, owner, group, flags));
+}
+#endif
+#ifdef __NR_fstatat64
+static int sys_fstatat64(int dirfd, const char *pathname, struct stat *buf,
+ int flags)
+{
+ return (fstatat(dirfd, pathname, buf, flags));
+}
+#endif
+#ifdef __NR_newfstatat
+static int sys_newfstatat(int dirfd, const char *pathname, struct stat *buf,
+ int flags)
+{
+ return (fstatat(dirfd, pathname, buf, flags));
+}
+#endif
+#ifdef TARGET_NR_futimesat
+static int sys_futimesat(int dirfd, const char *pathname,
+ const struct timeval times[2])
+{
+ return (futimesat(dirfd, pathname, times));
+}
+#endif
+#ifdef TARGET_NR_linkat
+static int sys_linkat(int olddirfd, const char *oldpath,
+ int newdirfd, const char *newpath, int flags)
+{
+ return (linkat(olddirfd, oldpath, newdirfd, newpath, flags));
+}
+#endif
+#ifdef TARGET_NR_mkdirat
+static int sys_mkdirat(int dirfd, const char *pathname, mode_t mode)
+{
+ return (mkdirat(dirfd, pathname, mode));
+}
+#endif
+#ifdef TARGET_NR_mknodat
+static int sys_mknodat(int dirfd, const char *pathname, mode_t mode,
+ dev_t dev)
+{
+ return (mknodat(dirfd, pathname, mode, dev));
+}
+#endif
+#ifdef TARGET_NR_openat
+static int sys_openat(int dirfd, const char *pathname, int flags, ...)
+{
+ /*
+ * open(2) has extra parameter 'mode' when called with
+ * flag O_CREAT.
+ */
+ if ((flags & O_CREAT) != 0) {
+ va_list ap;
+ mode_t mode;
+
+ /*
+ * Get the 'mode' parameter and translate it to
+ * host bits.
+ */
+ va_start(ap, flags);
+ mode = va_arg(ap, mode_t);
+ mode = target_to_host_bitmask(mode, fcntl_flags_tbl);
+ va_end(ap);
+
+ return (openat(dirfd, pathname, flags, mode));
+ }
+ return (openat(dirfd, pathname, flags));
+}
+#endif
+#ifdef TARGET_NR_readlinkat
+static int sys_readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz)
+{
+ return (readlinkat(dirfd, pathname, buf, bufsiz));
+}
+#endif
+#ifdef TARGET_NR_renameat
+static int sys_renameat(int olddirfd, const char *oldpath,
+ int newdirfd, const char *newpath)
+{
+ return (renameat(olddirfd, oldpath, newdirfd, newpath));
+}
+#endif
+#ifdef TARGET_NR_symlinkat
+static int sys_symlinkat(const char *oldpath, int newdirfd, const char *newpath)
+{
+ return (symlinkat(oldpath, newdirfd, newpath));
+}
+#endif
+#ifdef TARGET_NR_unlinkat
+static int sys_unlinkat(int dirfd, const char *pathname, int flags)
+{
+ return (unlinkat(dirfd, pathname, flags));
+}
+#endif
+#else /* !CONFIG_ATFILE */
+
+/*
+ * Try direct syscalls instead
+ */
+#if defined(TARGET_NR_faccessat) && defined(__NR_faccessat)
+_syscall3(int,sys_faccessat,int,dirfd,const char *,pathname,int,mode)
+#endif
+#if defined(TARGET_NR_fchmodat) && defined(__NR_fchmodat)
+_syscall3(int,sys_fchmodat,int,dirfd,const char *,pathname, mode_t,mode)
+#endif
+#if defined(TARGET_NR_fchownat) && defined(__NR_fchownat) && defined(USE_UID16)
+_syscall5(int,sys_fchownat,int,dirfd,const char *,pathname,
+ uid_t,owner,gid_t,group,int,flags)
+#endif
+#if (defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)) && \
+ defined(__NR_fstatat64)
+_syscall4(int,sys_fstatat64,int,dirfd,const char *,pathname,
+ struct stat *,buf,int,flags)
+#endif
+#if defined(TARGET_NR_futimesat) && defined(__NR_futimesat)
+_syscall3(int,sys_futimesat,int,dirfd,const char *,pathname,
+ const struct timeval *,times)
+#endif
+#if (defined(TARGET_NR_newfstatat) || defined(TARGET_NR_fstatat64) ) && \
+ defined(__NR_newfstatat)
+_syscall4(int,sys_newfstatat,int,dirfd,const char *,pathname,
+ struct stat *,buf,int,flags)
+#endif
+#if defined(TARGET_NR_linkat) && defined(__NR_linkat)
+_syscall5(int,sys_linkat,int,olddirfd,const char *,oldpath,
+ int,newdirfd,const char *,newpath,int,flags)
+#endif
+#if defined(TARGET_NR_mkdirat) && defined(__NR_mkdirat)
+_syscall3(int,sys_mkdirat,int,dirfd,const char *,pathname,mode_t,mode)
+#endif
+#if defined(TARGET_NR_mknodat) && defined(__NR_mknodat)
+_syscall4(int,sys_mknodat,int,dirfd,const char *,pathname,
+ mode_t,mode,dev_t,dev)
+#endif
+#if defined(TARGET_NR_openat) && defined(__NR_openat)
+_syscall4(int,sys_openat,int,dirfd,const char *,pathname,int,flags,mode_t,mode)
+#endif
+#if defined(TARGET_NR_readlinkat) && defined(__NR_readlinkat)
+_syscall4(int,sys_readlinkat,int,dirfd,const char *,pathname,
+ char *,buf,size_t,bufsize)
+#endif
+#if defined(TARGET_NR_renameat) && defined(__NR_renameat)
+_syscall4(int,sys_renameat,int,olddirfd,const char *,oldpath,
+ int,newdirfd,const char *,newpath)
+#endif
+#if defined(TARGET_NR_symlinkat) && defined(__NR_symlinkat)
+_syscall3(int,sys_symlinkat,const char *,oldpath,
+ int,newdirfd,const char *,newpath)
+#endif
+#if defined(TARGET_NR_unlinkat) && defined(__NR_unlinkat)
+_syscall3(int,sys_unlinkat,int,dirfd,const char *,pathname,int,flags)
+#endif
+
+#endif /* CONFIG_ATFILE */
+
+#ifdef CONFIG_UTIMENSAT
+static int sys_utimensat(int dirfd, const char *pathname,
+ const struct timespec times[2], int flags)
+{
+ if (pathname == NULL)
+ return futimens(dirfd, times);
+ else
+ return utimensat(dirfd, pathname, times, flags);
+}
+#else
+#if defined(TARGET_NR_utimensat) && defined(__NR_utimensat)
+_syscall4(int,sys_utimensat,int,dirfd,const char *,pathname,
+ const struct timespec *,tsp,int,flags)
+#endif
+#endif /* CONFIG_UTIMENSAT */
+
+#ifdef CONFIG_INOTIFY
+#include <sys/inotify.h>
+
+#if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)
+static int sys_inotify_init(void)
+{
+ return (inotify_init());
+}
+#endif
+#if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch)
+static int sys_inotify_add_watch(int fd,const char *pathname, int32_t mask)
+{
+ return (inotify_add_watch(fd, pathname, mask));
+}
+#endif
+#if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch)
+static int sys_inotify_rm_watch(int fd, int32_t wd)
+{
+ return (inotify_rm_watch(fd, wd));
+}
+#endif
+#ifdef CONFIG_INOTIFY1
+#if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1)
+static int sys_inotify_init1(int flags)
+{
+ return (inotify_init1(flags));
+}
+#endif
+#endif
+#else
+/* Userspace can usually survive runtime without inotify */
+#undef TARGET_NR_inotify_init
+#undef TARGET_NR_inotify_init1
+#undef TARGET_NR_inotify_add_watch
+#undef TARGET_NR_inotify_rm_watch
+#endif /* CONFIG_INOTIFY */
+
+
+extern int personality(int);
+extern int flock(int, int);
+extern int setfsuid(int);
+extern int setfsgid(int);
+extern int setgroups(int, gid_t *);
+
+#define ERRNO_TABLE_SIZE 1200
+
+/* target_to_host_errno_table[] is initialized from
+ * host_to_target_errno_table[] in syscall_init(). */
+static uint16_t target_to_host_errno_table[ERRNO_TABLE_SIZE] = {
+};
+
+/*
+ * This list is the union of errno values overridden in asm-<arch>/errno.h
+ * minus the errnos that are not actually generic to all archs.
+ */
+static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = {
+ [EIDRM] = TARGET_EIDRM,
+ [ECHRNG] = TARGET_ECHRNG,
+ [EL2NSYNC] = TARGET_EL2NSYNC,
+ [EL3HLT] = TARGET_EL3HLT,
+ [EL3RST] = TARGET_EL3RST,
+ [ELNRNG] = TARGET_ELNRNG,
+ [EUNATCH] = TARGET_EUNATCH,
+ [ENOCSI] = TARGET_ENOCSI,
+ [EL2HLT] = TARGET_EL2HLT,
+ [EDEADLK] = TARGET_EDEADLK,
+ [ENOLCK] = TARGET_ENOLCK,
+ [EBADE] = TARGET_EBADE,
+ [EBADR] = TARGET_EBADR,
+ [EXFULL] = TARGET_EXFULL,
+ [ENOANO] = TARGET_ENOANO,
+ [EBADRQC] = TARGET_EBADRQC,
+ [EBADSLT] = TARGET_EBADSLT,
+ [EBFONT] = TARGET_EBFONT,
+ [ENOSTR] = TARGET_ENOSTR,
+ [ENODATA] = TARGET_ENODATA,
+ [ETIME] = TARGET_ETIME,
+ [ENOSR] = TARGET_ENOSR,
+ [ENONET] = TARGET_ENONET,
+ [ENOPKG] = TARGET_ENOPKG,
+ [EREMOTE] = TARGET_EREMOTE,
+ [ENOLINK] = TARGET_ENOLINK,
+ [EADV] = TARGET_EADV,
+ [ESRMNT] = TARGET_ESRMNT,
+ [ECOMM] = TARGET_ECOMM,
+ [EPROTO] = TARGET_EPROTO,
+ [EDOTDOT] = TARGET_EDOTDOT,
+ [EMULTIHOP] = TARGET_EMULTIHOP,
+ [EBADMSG] = TARGET_EBADMSG,
+ [ENAMETOOLONG] = TARGET_ENAMETOOLONG,
+ [EOVERFLOW] = TARGET_EOVERFLOW,
+ [ENOTUNIQ] = TARGET_ENOTUNIQ,
+ [EBADFD] = TARGET_EBADFD,
+ [EREMCHG] = TARGET_EREMCHG,
+ [ELIBACC] = TARGET_ELIBACC,
+ [ELIBBAD] = TARGET_ELIBBAD,
+ [ELIBSCN] = TARGET_ELIBSCN,
+ [ELIBMAX] = TARGET_ELIBMAX,
+ [ELIBEXEC] = TARGET_ELIBEXEC,
+ [EILSEQ] = TARGET_EILSEQ,
+ [ENOSYS] = TARGET_ENOSYS,
+ [ELOOP] = TARGET_ELOOP,
+ [ERESTART] = TARGET_ERESTART,
+ [ESTRPIPE] = TARGET_ESTRPIPE,
+ [ENOTEMPTY] = TARGET_ENOTEMPTY,
+ [EUSERS] = TARGET_EUSERS,
+ [ENOTSOCK] = TARGET_ENOTSOCK,
+ [EDESTADDRREQ] = TARGET_EDESTADDRREQ,
+ [EMSGSIZE] = TARGET_EMSGSIZE,
+ [EPROTOTYPE] = TARGET_EPROTOTYPE,
+ [ENOPROTOOPT] = TARGET_ENOPROTOOPT,
+ [EPROTONOSUPPORT] = TARGET_EPROTONOSUPPORT,
+ [ESOCKTNOSUPPORT] = TARGET_ESOCKTNOSUPPORT,
+ [EOPNOTSUPP] = TARGET_EOPNOTSUPP,
+ [EPFNOSUPPORT] = TARGET_EPFNOSUPPORT,
+ [EAFNOSUPPORT] = TARGET_EAFNOSUPPORT,
+ [EADDRINUSE] = TARGET_EADDRINUSE,
+ [EADDRNOTAVAIL] = TARGET_EADDRNOTAVAIL,
+ [ENETDOWN] = TARGET_ENETDOWN,
+ [ENETUNREACH] = TARGET_ENETUNREACH,
+ [ENETRESET] = TARGET_ENETRESET,
+ [ECONNABORTED] = TARGET_ECONNABORTED,
+ [ECONNRESET] = TARGET_ECONNRESET,
+ [ENOBUFS] = TARGET_ENOBUFS,
+ [EISCONN] = TARGET_EISCONN,
+ [ENOTCONN] = TARGET_ENOTCONN,
+ [EUCLEAN] = TARGET_EUCLEAN,
+ [ENOTNAM] = TARGET_ENOTNAM,
+ [ENAVAIL] = TARGET_ENAVAIL,
+ [EISNAM] = TARGET_EISNAM,
+ [EREMOTEIO] = TARGET_EREMOTEIO,
+ [ESHUTDOWN] = TARGET_ESHUTDOWN,
+ [ETOOMANYREFS] = TARGET_ETOOMANYREFS,
+ [ETIMEDOUT] = TARGET_ETIMEDOUT,
+ [ECONNREFUSED] = TARGET_ECONNREFUSED,
+ [EHOSTDOWN] = TARGET_EHOSTDOWN,
+ [EHOSTUNREACH] = TARGET_EHOSTUNREACH,
+ [EALREADY] = TARGET_EALREADY,
+ [EINPROGRESS] = TARGET_EINPROGRESS,
+ [ESTALE] = TARGET_ESTALE,
+ [ECANCELED] = TARGET_ECANCELED,
+ [ENOMEDIUM] = TARGET_ENOMEDIUM,
+ [EMEDIUMTYPE] = TARGET_EMEDIUMTYPE,
+#ifdef ENOKEY
+ [ENOKEY] = TARGET_ENOKEY,
+#endif
+#ifdef EKEYEXPIRED
+ [EKEYEXPIRED] = TARGET_EKEYEXPIRED,
+#endif
+#ifdef EKEYREVOKED
+ [EKEYREVOKED] = TARGET_EKEYREVOKED,
+#endif
+#ifdef EKEYREJECTED
+ [EKEYREJECTED] = TARGET_EKEYREJECTED,
+#endif
+#ifdef EOWNERDEAD
+ [EOWNERDEAD] = TARGET_EOWNERDEAD,
+#endif
+#ifdef ENOTRECOVERABLE
+ [ENOTRECOVERABLE] = TARGET_ENOTRECOVERABLE,
+#endif
+};
+
+static inline int host_to_target_errno(int err)
+{
+ if(host_to_target_errno_table[err])
+ return host_to_target_errno_table[err];
+ return err;
+}
+
+static inline int target_to_host_errno(int err)
+{
+ if (target_to_host_errno_table[err])
+ return target_to_host_errno_table[err];
+ return err;
+}
+
+static inline abi_long get_errno(abi_long ret)
+{
+ if (ret == -1)
+ return -host_to_target_errno(errno);
+ else
+ return ret;
+}
+
+static inline int is_error(abi_long ret)
+{
+ return (abi_ulong)ret >= (abi_ulong)(-4096);
+}
+
+char *target_strerror(int err)
+{
+ return strerror(target_to_host_errno(err));
+}
+
+static abi_ulong target_brk;
+static abi_ulong target_original_brk;
+
+void target_set_brk(abi_ulong new_brk)
+{
+ target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk);
+}
+
+/* do_brk() must return target values and target errnos. */
+abi_long do_brk(abi_ulong new_brk)
+{
+ abi_ulong brk_page;
+ abi_long mapped_addr;
+ int new_alloc_size;
+
+ if (!new_brk)
+ return target_brk;
+ if (new_brk < target_original_brk)
+ return target_brk;
+
+ brk_page = HOST_PAGE_ALIGN(target_brk);
+
+ /* If the new brk is less than this, set it and we're done... */
+ if (new_brk < brk_page) {
+ target_brk = new_brk;
+ return target_brk;
+ }
+
+ /* We need to allocate more memory after the brk... */
+ new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
+ mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0));
+
+#if defined(TARGET_ALPHA)
+ /* We (partially) emulate OSF/1 on Alpha, which requires we
+ return a proper errno, not an unchanged brk value. */
+ if (is_error(mapped_addr)) {
+ return -TARGET_ENOMEM;
+ }
+#endif
+
+ if (!is_error(mapped_addr)) {
+ target_brk = new_brk;
+ }
+ return target_brk;
+}
+
+static inline abi_long copy_from_user_fdset(fd_set *fds,
+ abi_ulong target_fds_addr,
+ int n)
+{
+ int i, nw, j, k;
+ abi_ulong b, *target_fds;
+
+ nw = (n + TARGET_ABI_BITS - 1) / TARGET_ABI_BITS;
+ if (!(target_fds = lock_user(VERIFY_READ,
+ target_fds_addr,
+ sizeof(abi_ulong) * nw,
+ 1)))
+ return -TARGET_EFAULT;
+
+ FD_ZERO(fds);
+ k = 0;
+ for (i = 0; i < nw; i++) {
+ /* grab the abi_ulong */
+ __get_user(b, &target_fds[i]);
+ for (j = 0; j < TARGET_ABI_BITS; j++) {
+ /* check the bit inside the abi_ulong */
+ if ((b >> j) & 1)
+ FD_SET(k, fds);
+ k++;
+ }
+ }
+
+ unlock_user(target_fds, target_fds_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long copy_to_user_fdset(abi_ulong target_fds_addr,
+ const fd_set *fds,
+ int n)
+{
+ int i, nw, j, k;
+ abi_long v;
+ abi_ulong *target_fds;
+
+ nw = (n + TARGET_ABI_BITS - 1) / TARGET_ABI_BITS;
+ if (!(target_fds = lock_user(VERIFY_WRITE,
+ target_fds_addr,
+ sizeof(abi_ulong) * nw,
+ 0)))
+ return -TARGET_EFAULT;
+
+ k = 0;
+ for (i = 0; i < nw; i++) {
+ v = 0;
+ for (j = 0; j < TARGET_ABI_BITS; j++) {
+ v |= ((FD_ISSET(k, fds) != 0) << j);
+ k++;
+ }
+ __put_user(v, &target_fds[i]);
+ }
+
+ unlock_user(target_fds, target_fds_addr, sizeof(abi_ulong) * nw);
+
+ return 0;
+}
+
+#if defined(__alpha__)
+#define HOST_HZ 1024
+#else
+#define HOST_HZ 100
+#endif
+
+static inline abi_long host_to_target_clock_t(long ticks)
+{
+#if HOST_HZ == TARGET_HZ
+ return ticks;
+#else
+ return ((int64_t)ticks * TARGET_HZ) / HOST_HZ;
+#endif
+}
+
+static inline abi_long host_to_target_rusage(abi_ulong target_addr,
+ const struct rusage *rusage)
+{
+ struct target_rusage *target_rusage;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_rusage, target_addr, 0))
+ return -TARGET_EFAULT;
+ target_rusage->ru_utime.tv_sec = tswapl(rusage->ru_utime.tv_sec);
+ target_rusage->ru_utime.tv_usec = tswapl(rusage->ru_utime.tv_usec);
+ target_rusage->ru_stime.tv_sec = tswapl(rusage->ru_stime.tv_sec);
+ target_rusage->ru_stime.tv_usec = tswapl(rusage->ru_stime.tv_usec);
+ target_rusage->ru_maxrss = tswapl(rusage->ru_maxrss);
+ target_rusage->ru_ixrss = tswapl(rusage->ru_ixrss);
+ target_rusage->ru_idrss = tswapl(rusage->ru_idrss);
+ target_rusage->ru_isrss = tswapl(rusage->ru_isrss);
+ target_rusage->ru_minflt = tswapl(rusage->ru_minflt);
+ target_rusage->ru_majflt = tswapl(rusage->ru_majflt);
+ target_rusage->ru_nswap = tswapl(rusage->ru_nswap);
+ target_rusage->ru_inblock = tswapl(rusage->ru_inblock);
+ target_rusage->ru_oublock = tswapl(rusage->ru_oublock);
+ target_rusage->ru_msgsnd = tswapl(rusage->ru_msgsnd);
+ target_rusage->ru_msgrcv = tswapl(rusage->ru_msgrcv);
+ target_rusage->ru_nsignals = tswapl(rusage->ru_nsignals);
+ target_rusage->ru_nvcsw = tswapl(rusage->ru_nvcsw);
+ target_rusage->ru_nivcsw = tswapl(rusage->ru_nivcsw);
+ unlock_user_struct(target_rusage, target_addr, 1);
+
+ return 0;
+}
+
+static inline rlim_t target_to_host_rlim(target_ulong target_rlim)
+{
+ if (target_rlim == TARGET_RLIM_INFINITY)
+ return RLIM_INFINITY;
+ else
+ return tswapl(target_rlim);
+}
+
+static inline target_ulong host_to_target_rlim(rlim_t rlim)
+{
+ if (rlim == RLIM_INFINITY || rlim != (target_long)rlim)
+ return TARGET_RLIM_INFINITY;
+ else
+ return tswapl(rlim);
+}
+
+static inline abi_long copy_from_user_timeval(struct timeval *tv,
+ abi_ulong target_tv_addr)
+{
+ struct target_timeval *target_tv;
+
+ if (!lock_user_struct(VERIFY_READ, target_tv, target_tv_addr, 1))
+ return -TARGET_EFAULT;
+
+ __get_user(tv->tv_sec, &target_tv->tv_sec);
+ __get_user(tv->tv_usec, &target_tv->tv_usec);
+
+ unlock_user_struct(target_tv, target_tv_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long copy_to_user_timeval(abi_ulong target_tv_addr,
+ const struct timeval *tv)
+{
+ struct target_timeval *target_tv;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_tv, target_tv_addr, 0))
+ return -TARGET_EFAULT;
+
+ __put_user(tv->tv_sec, &target_tv->tv_sec);
+ __put_user(tv->tv_usec, &target_tv->tv_usec);
+
+ unlock_user_struct(target_tv, target_tv_addr, 1);
+
+ return 0;
+}
+
+#if defined(TARGET_NR_mq_open) && defined(__NR_mq_open)
+#include <mqueue.h>
+
+static inline abi_long copy_from_user_mq_attr(struct mq_attr *attr,
+ abi_ulong target_mq_attr_addr)
+{
+ struct target_mq_attr *target_mq_attr;
+
+ if (!lock_user_struct(VERIFY_READ, target_mq_attr,
+ target_mq_attr_addr, 1))
+ return -TARGET_EFAULT;
+
+ __get_user(attr->mq_flags, &target_mq_attr->mq_flags);
+ __get_user(attr->mq_maxmsg, &target_mq_attr->mq_maxmsg);
+ __get_user(attr->mq_msgsize, &target_mq_attr->mq_msgsize);
+ __get_user(attr->mq_curmsgs, &target_mq_attr->mq_curmsgs);
+
+ unlock_user_struct(target_mq_attr, target_mq_attr_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long copy_to_user_mq_attr(abi_ulong target_mq_attr_addr,
+ const struct mq_attr *attr)
+{
+ struct target_mq_attr *target_mq_attr;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_mq_attr,
+ target_mq_attr_addr, 0))
+ return -TARGET_EFAULT;
+
+ __put_user(attr->mq_flags, &target_mq_attr->mq_flags);
+ __put_user(attr->mq_maxmsg, &target_mq_attr->mq_maxmsg);
+ __put_user(attr->mq_msgsize, &target_mq_attr->mq_msgsize);
+ __put_user(attr->mq_curmsgs, &target_mq_attr->mq_curmsgs);
+
+ unlock_user_struct(target_mq_attr, target_mq_attr_addr, 1);
+
+ return 0;
+}
+#endif
+
+/* do_select() must return target values and target errnos. */
+static abi_long do_select(int n,
+ abi_ulong rfd_addr, abi_ulong wfd_addr,
+ abi_ulong efd_addr, abi_ulong target_tv_addr)
+{
+ fd_set rfds, wfds, efds;
+ fd_set *rfds_ptr, *wfds_ptr, *efds_ptr;
+ struct timeval tv, *tv_ptr;
+ abi_long ret;
+
+ if (rfd_addr) {
+ if (copy_from_user_fdset(&rfds, rfd_addr, n))
+ return -TARGET_EFAULT;
+ rfds_ptr = &rfds;
+ } else {
+ rfds_ptr = NULL;
+ }
+ if (wfd_addr) {
+ if (copy_from_user_fdset(&wfds, wfd_addr, n))
+ return -TARGET_EFAULT;
+ wfds_ptr = &wfds;
+ } else {
+ wfds_ptr = NULL;
+ }
+ if (efd_addr) {
+ if (copy_from_user_fdset(&efds, efd_addr, n))
+ return -TARGET_EFAULT;
+ efds_ptr = &efds;
+ } else {
+ efds_ptr = NULL;
+ }
+
+ if (target_tv_addr) {
+ if (copy_from_user_timeval(&tv, target_tv_addr))
+ return -TARGET_EFAULT;
+ tv_ptr = &tv;
+ } else {
+ tv_ptr = NULL;
+ }
+
+ ret = get_errno(select(n, rfds_ptr, wfds_ptr, efds_ptr, tv_ptr));
+
+ if (!is_error(ret)) {
+ if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n))
+ return -TARGET_EFAULT;
+ if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n))
+ return -TARGET_EFAULT;
+ if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n))
+ return -TARGET_EFAULT;
+
+ if (target_tv_addr && copy_to_user_timeval(target_tv_addr, &tv))
+ return -TARGET_EFAULT;
+ }
+
+ return ret;
+}
+
+static abi_long do_pipe2(int host_pipe[], int flags)
+{
+#ifdef CONFIG_PIPE2
+ return pipe2(host_pipe, flags);
+#else
+ return -ENOSYS;
+#endif
+}
+
+static abi_long do_pipe(void *cpu_env, abi_ulong pipedes,
+ int flags, int is_pipe2)
+{
+ int host_pipe[2];
+ abi_long ret;
+ ret = flags ? do_pipe2(host_pipe, flags) : pipe(host_pipe);
+
+ if (is_error(ret))
+ return get_errno(ret);
+
+ /* Several targets have special calling conventions for the original
+ pipe syscall, but didn't replicate this into the pipe2 syscall. */
+ if (!is_pipe2) {
+#if defined(TARGET_ALPHA)
+ ((CPUAlphaState *)cpu_env)->ir[IR_A4] = host_pipe[1];
+ return host_pipe[0];
+#elif defined(TARGET_MIPS)
+ ((CPUMIPSState*)cpu_env)->active_tc.gpr[3] = host_pipe[1];
+ return host_pipe[0];
+#elif defined(TARGET_SH4)
+ ((CPUSH4State*)cpu_env)->gregs[1] = host_pipe[1];
+ return host_pipe[0];
+#endif
+ }
+
+ if (put_user_s32(host_pipe[0], pipedes)
+ || put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0])))
+ return -TARGET_EFAULT;
+ return get_errno(ret);
+}
+
+static inline abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn,
+ abi_ulong target_addr,
+ socklen_t len)
+{
+ struct target_ip_mreqn *target_smreqn;
+
+ target_smreqn = lock_user(VERIFY_READ, target_addr, len, 1);
+ if (!target_smreqn)
+ return -TARGET_EFAULT;
+ mreqn->imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr;
+ mreqn->imr_address.s_addr = target_smreqn->imr_address.s_addr;
+ if (len == sizeof(struct target_ip_mreqn))
+ mreqn->imr_ifindex = tswapl(target_smreqn->imr_ifindex);
+ unlock_user(target_smreqn, target_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long target_to_host_sockaddr(struct sockaddr *addr,
+ abi_ulong target_addr,
+ socklen_t len)
+{
+ const socklen_t unix_maxlen = sizeof (struct sockaddr_un);
+ sa_family_t sa_family;
+ struct target_sockaddr *target_saddr;
+
+ target_saddr = lock_user(VERIFY_READ, target_addr, len, 1);
+ if (!target_saddr)
+ return -TARGET_EFAULT;
+
+ sa_family = tswap16(target_saddr->sa_family);
+
+ /* Oops. The caller might send a incomplete sun_path; sun_path
+ * must be terminated by \0 (see the manual page), but
+ * unfortunately it is quite common to specify sockaddr_un
+ * length as "strlen(x->sun_path)" while it should be
+ * "strlen(...) + 1". We'll fix that here if needed.
+ * Linux kernel has a similar feature.
+ */
+
+ if (sa_family == AF_UNIX) {
+ if (len < unix_maxlen && len > 0) {
+ char *cp = (char*)target_saddr;
+
+ if ( cp[len-1] && !cp[len] )
+ len++;
+ }
+ if (len > unix_maxlen)
+ len = unix_maxlen;
+ }
+
+ memcpy(addr, target_saddr, len);
+ addr->sa_family = sa_family;
+ unlock_user(target_saddr, target_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long host_to_target_sockaddr(abi_ulong target_addr,
+ struct sockaddr *addr,
+ socklen_t len)
+{
+ struct target_sockaddr *target_saddr;
+
+ target_saddr = lock_user(VERIFY_WRITE, target_addr, len, 0);
+ if (!target_saddr)
+ return -TARGET_EFAULT;
+ memcpy(target_saddr, addr, len);
+ target_saddr->sa_family = tswap16(addr->sa_family);
+ unlock_user(target_saddr, target_addr, len);
+
+ return 0;
+}
+
+/* ??? Should this also swap msgh->name? */
+static inline abi_long target_to_host_cmsg(struct msghdr *msgh,
+ struct target_msghdr *target_msgh)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
+ abi_long msg_controllen;
+ abi_ulong target_cmsg_addr;
+ struct target_cmsghdr *target_cmsg;
+ socklen_t space = 0;
+
+ msg_controllen = tswapl(target_msgh->msg_controllen);
+ if (msg_controllen < sizeof (struct target_cmsghdr))
+ goto the_end;
+ target_cmsg_addr = tswapl(target_msgh->msg_control);
+ target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
+ if (!target_cmsg)
+ return -TARGET_EFAULT;
+
+ while (cmsg && target_cmsg) {
+ void *data = CMSG_DATA(cmsg);
+ void *target_data = TARGET_CMSG_DATA(target_cmsg);
+
+ int len = tswapl(target_cmsg->cmsg_len)
+ - TARGET_CMSG_ALIGN(sizeof (struct target_cmsghdr));
+
+ space += CMSG_SPACE(len);
+ if (space > msgh->msg_controllen) {
+ space -= CMSG_SPACE(len);
+ gemu_log("Host cmsg overflow\n");
+ break;
+ }
+
+ cmsg->cmsg_level = tswap32(target_cmsg->cmsg_level);
+ cmsg->cmsg_type = tswap32(target_cmsg->cmsg_type);
+ cmsg->cmsg_len = CMSG_LEN(len);
+
+ if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(data, target_data, len);
+ } else {
+ int *fd = (int *)data;
+ int *target_fd = (int *)target_data;
+ int i, numfds = len / sizeof(int);
+
+ for (i = 0; i < numfds; i++)
+ fd[i] = tswap32(target_fd[i]);
+ }
+
+ cmsg = CMSG_NXTHDR(msgh, cmsg);
+ target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
+ }
+ unlock_user(target_cmsg, target_cmsg_addr, 0);
+ the_end:
+ msgh->msg_controllen = space;
+ return 0;
+}
+
+/* ??? Should this also swap msgh->name? */
+static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
+ struct msghdr *msgh)
+{
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh);
+ abi_long msg_controllen;
+ abi_ulong target_cmsg_addr;
+ struct target_cmsghdr *target_cmsg;
+ socklen_t space = 0;
+
+ msg_controllen = tswapl(target_msgh->msg_controllen);
+ if (msg_controllen < sizeof (struct target_cmsghdr))
+ goto the_end;
+ target_cmsg_addr = tswapl(target_msgh->msg_control);
+ target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
+ if (!target_cmsg)
+ return -TARGET_EFAULT;
+
+ while (cmsg && target_cmsg) {
+ void *data = CMSG_DATA(cmsg);
+ void *target_data = TARGET_CMSG_DATA(target_cmsg);
+
+ int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr));
+
+ space += TARGET_CMSG_SPACE(len);
+ if (space > msg_controllen) {
+ space -= TARGET_CMSG_SPACE(len);
+ gemu_log("Target cmsg overflow\n");
+ break;
+ }
+
+ target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level);
+ target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
+ target_cmsg->cmsg_len = tswapl(TARGET_CMSG_LEN(len));
+
+ if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(target_data, data, len);
+ } else {
+ int *fd = (int *)data;
+ int *target_fd = (int *)target_data;
+ int i, numfds = len / sizeof(int);
+
+ for (i = 0; i < numfds; i++)
+ target_fd[i] = tswap32(fd[i]);
+ }
+
+ cmsg = CMSG_NXTHDR(msgh, cmsg);
+ target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg);
+ }
+ unlock_user(target_cmsg, target_cmsg_addr, space);
+ the_end:
+ target_msgh->msg_controllen = tswapl(space);
+ return 0;
+}
+
+/* do_setsockopt() Must return target values and target errnos. */
+static abi_long do_setsockopt(int sockfd, int level, int optname,
+ abi_ulong optval_addr, socklen_t optlen)
+{
+ abi_long ret;
+ int val;
+ struct ip_mreqn *ip_mreq;
+ struct ip_mreq_source *ip_mreq_source;
+
+ switch(level) {
+ case SOL_TCP:
+ /* TCP options all take an 'int' value. */
+ if (optlen < sizeof(uint32_t))
+ return -TARGET_EINVAL;
+
+ if (get_user_u32(val, optval_addr))
+ return -TARGET_EFAULT;
+ ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
+ break;
+ case SOL_IP:
+ switch(optname) {
+ case IP_TOS:
+ case IP_TTL:
+ case IP_HDRINCL:
+ case IP_ROUTER_ALERT:
+ case IP_RECVOPTS:
+ case IP_RETOPTS:
+ case IP_PKTINFO:
+ case IP_MTU_DISCOVER:
+ case IP_RECVERR:
+ case IP_RECVTOS:
+#ifdef IP_FREEBIND
+ case IP_FREEBIND:
+#endif
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ val = 0;
+ if (optlen >= sizeof(uint32_t)) {
+ if (get_user_u32(val, optval_addr))
+ return -TARGET_EFAULT;
+ } else if (optlen >= 1) {
+ if (get_user_u8(val, optval_addr))
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ if (optlen < sizeof (struct target_ip_mreq) ||
+ optlen > sizeof (struct target_ip_mreqn))
+ return -TARGET_EINVAL;
+
+ ip_mreq = (struct ip_mreqn *) alloca(optlen);
+ target_to_host_ip_mreq(ip_mreq, optval_addr, optlen);
+ ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq, optlen));
+ break;
+
+ case IP_BLOCK_SOURCE:
+ case IP_UNBLOCK_SOURCE:
+ case IP_ADD_SOURCE_MEMBERSHIP:
+ case IP_DROP_SOURCE_MEMBERSHIP:
+ if (optlen != sizeof (struct target_ip_mreq_source))
+ return -TARGET_EINVAL;
+
+ ip_mreq_source = lock_user(VERIFY_READ, optval_addr, optlen, 1);
+ ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq_source, optlen));
+ unlock_user (ip_mreq_source, optval_addr, 0);
+ break;
+
+ default:
+ goto unimplemented;
+ }
+ break;
+ case TARGET_SOL_SOCKET:
+ switch (optname) {
+ /* Options with 'int' argument. */
+ case TARGET_SO_DEBUG:
+ optname = SO_DEBUG;
+ break;
+ case TARGET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ break;
+ case TARGET_SO_TYPE:
+ optname = SO_TYPE;
+ break;
+ case TARGET_SO_ERROR:
+ optname = SO_ERROR;
+ break;
+ case TARGET_SO_DONTROUTE:
+ optname = SO_DONTROUTE;
+ break;
+ case TARGET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ break;
+ case TARGET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ break;
+ case TARGET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ break;
+ case TARGET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ break;
+ case TARGET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ break;
+ case TARGET_SO_NO_CHECK:
+ optname = SO_NO_CHECK;
+ break;
+ case TARGET_SO_PRIORITY:
+ optname = SO_PRIORITY;
+ break;
+#ifdef SO_BSDCOMPAT
+ case TARGET_SO_BSDCOMPAT:
+ optname = SO_BSDCOMPAT;
+ break;
+#endif
+ case TARGET_SO_PASSCRED:
+ optname = SO_PASSCRED;
+ break;
+ case TARGET_SO_TIMESTAMP:
+ optname = SO_TIMESTAMP;
+ break;
+ case TARGET_SO_RCVLOWAT:
+ optname = SO_RCVLOWAT;
+ break;
+ case TARGET_SO_RCVTIMEO:
+ optname = SO_RCVTIMEO;
+ break;
+ case TARGET_SO_SNDTIMEO:
+ optname = SO_SNDTIMEO;
+ break;
+ break;
+ default:
+ goto unimplemented;
+ }
+ if (optlen < sizeof(uint32_t))
+ return -TARGET_EINVAL;
+
+ if (get_user_u32(val, optval_addr))
+ return -TARGET_EFAULT;
+ ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, sizeof(val)));
+ break;
+ default:
+ unimplemented:
+ gemu_log("Unsupported setsockopt level=%d optname=%d \n", level, optname);
+ ret = -TARGET_ENOPROTOOPT;
+ }
+ return ret;
+}
+
+/* do_getsockopt() Must return target values and target errnos. */
+static abi_long do_getsockopt(int sockfd, int level, int optname,
+ abi_ulong optval_addr, abi_ulong optlen)
+{
+ abi_long ret;
+ int len, val;
+ socklen_t lv;
+
+ switch(level) {
+ case TARGET_SOL_SOCKET:
+ level = SOL_SOCKET;
+ switch (optname) {
+ /* These don't just return a single integer */
+ case TARGET_SO_LINGER:
+ case TARGET_SO_RCVTIMEO:
+ case TARGET_SO_SNDTIMEO:
+ case TARGET_SO_PEERCRED:
+ case TARGET_SO_PEERNAME:
+ goto unimplemented;
+ /* Options with 'int' argument. */
+ case TARGET_SO_DEBUG:
+ optname = SO_DEBUG;
+ goto int_case;
+ case TARGET_SO_REUSEADDR:
+ optname = SO_REUSEADDR;
+ goto int_case;
+ case TARGET_SO_TYPE:
+ optname = SO_TYPE;
+ goto int_case;
+ case TARGET_SO_ERROR:
+ optname = SO_ERROR;
+ goto int_case;
+ case TARGET_SO_DONTROUTE:
+ optname = SO_DONTROUTE;
+ goto int_case;
+ case TARGET_SO_BROADCAST:
+ optname = SO_BROADCAST;
+ goto int_case;
+ case TARGET_SO_SNDBUF:
+ optname = SO_SNDBUF;
+ goto int_case;
+ case TARGET_SO_RCVBUF:
+ optname = SO_RCVBUF;
+ goto int_case;
+ case TARGET_SO_KEEPALIVE:
+ optname = SO_KEEPALIVE;
+ goto int_case;
+ case TARGET_SO_OOBINLINE:
+ optname = SO_OOBINLINE;
+ goto int_case;
+ case TARGET_SO_NO_CHECK:
+ optname = SO_NO_CHECK;
+ goto int_case;
+ case TARGET_SO_PRIORITY:
+ optname = SO_PRIORITY;
+ goto int_case;
+#ifdef SO_BSDCOMPAT
+ case TARGET_SO_BSDCOMPAT:
+ optname = SO_BSDCOMPAT;
+ goto int_case;
+#endif
+ case TARGET_SO_PASSCRED:
+ optname = SO_PASSCRED;
+ goto int_case;
+ case TARGET_SO_TIMESTAMP:
+ optname = SO_TIMESTAMP;
+ goto int_case;
+ case TARGET_SO_RCVLOWAT:
+ optname = SO_RCVLOWAT;
+ goto int_case;
+ default:
+ goto int_case;
+ }
+ break;
+ case SOL_TCP:
+ /* TCP options all take an 'int' value. */
+ int_case:
+ if (get_user_u32(len, optlen))
+ return -TARGET_EFAULT;
+ if (len < 0)
+ return -TARGET_EINVAL;
+ lv = sizeof(int);
+ ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
+ if (ret < 0)
+ return ret;
+ if (len > lv)
+ len = lv;
+ if (len == 4) {
+ if (put_user_u32(val, optval_addr))
+ return -TARGET_EFAULT;
+ } else {
+ if (put_user_u8(val, optval_addr))
+ return -TARGET_EFAULT;
+ }
+ if (put_user_u32(len, optlen))
+ return -TARGET_EFAULT;
+ break;
+ case SOL_IP:
+ switch(optname) {
+ case IP_TOS:
+ case IP_TTL:
+ case IP_HDRINCL:
+ case IP_ROUTER_ALERT:
+ case IP_RECVOPTS:
+ case IP_RETOPTS:
+ case IP_PKTINFO:
+ case IP_MTU_DISCOVER:
+ case IP_RECVERR:
+ case IP_RECVTOS:
+#ifdef IP_FREEBIND
+ case IP_FREEBIND:
+#endif
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_LOOP:
+ if (get_user_u32(len, optlen))
+ return -TARGET_EFAULT;
+ if (len < 0)
+ return -TARGET_EINVAL;
+ lv = sizeof(int);
+ ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) {
+ len = 1;
+ if (put_user_u32(len, optlen)
+ || put_user_u8(val, optval_addr))
+ return -TARGET_EFAULT;
+ } else {
+ if (len > sizeof(int))
+ len = sizeof(int);
+ if (put_user_u32(len, optlen)
+ || put_user_u32(val, optval_addr))
+ return -TARGET_EFAULT;
+ }
+ break;
+ default:
+ ret = -TARGET_ENOPROTOOPT;
+ break;
+ }
+ break;
+ default:
+ unimplemented:
+ gemu_log("getsockopt level=%d optname=%d not yet supported\n",
+ level, optname);
+ ret = -TARGET_EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+/* FIXME
+ * lock_iovec()/unlock_iovec() have a return code of 0 for success where
+ * other lock functions have a return code of 0 for failure.
+ */
+static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ abi_ulong base;
+ int i;
+
+ target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
+ if (!target_vec)
+ return -TARGET_EFAULT;
+ for(i = 0;i < count; i++) {
+ base = tswapl(target_vec[i].iov_base);
+ vec[i].iov_len = tswapl(target_vec[i].iov_len);
+ if (vec[i].iov_len != 0) {
+ vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
+ /* Don't check lock_user return value. We must call writev even
+ if a element has invalid base address. */
+ } else {
+ /* zero length pointer is ignored */
+ vec[i].iov_base = NULL;
+ }
+ }
+ unlock_user (target_vec, target_addr, 0);
+ return 0;
+}
+
+static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
+ int count, int copy)
+{
+ struct target_iovec *target_vec;
+ abi_ulong base;
+ int i;
+
+ target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
+ if (!target_vec)
+ return -TARGET_EFAULT;
+ for(i = 0;i < count; i++) {
+ if (target_vec[i].iov_base) {
+ base = tswapl(target_vec[i].iov_base);
+ unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
+ }
+ }
+ unlock_user (target_vec, target_addr, 0);
+
+ return 0;
+}
+
+/* do_socket() Must return target values and target errnos. */
+static abi_long do_socket(int domain, int type, int protocol)
+{
+#if defined(TARGET_MIPS)
+ switch(type) {
+ case TARGET_SOCK_DGRAM:
+ type = SOCK_DGRAM;
+ break;
+ case TARGET_SOCK_STREAM:
+ type = SOCK_STREAM;
+ break;
+ case TARGET_SOCK_RAW:
+ type = SOCK_RAW;
+ break;
+ case TARGET_SOCK_RDM:
+ type = SOCK_RDM;
+ break;
+ case TARGET_SOCK_SEQPACKET:
+ type = SOCK_SEQPACKET;
+ break;
+ case TARGET_SOCK_PACKET:
+ type = SOCK_PACKET;
+ break;
+ }
+#endif
+ if (domain == PF_NETLINK)
+ return -EAFNOSUPPORT; /* do not NETLINK socket connections possible */
+ return get_errno(socket(domain, type, protocol));
+}
+
+/* do_bind() Must return target values and target errnos. */
+static abi_long do_bind(int sockfd, abi_ulong target_addr,
+ socklen_t addrlen)
+{
+ void *addr;
+ abi_long ret;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ addr = alloca(addrlen+1);
+
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+ if (ret)
+ return ret;
+
+ return get_errno(bind(sockfd, addr, addrlen));
+}
+
+/* do_connect() Must return target values and target errnos. */
+static abi_long do_connect(int sockfd, abi_ulong target_addr,
+ socklen_t addrlen)
+{
+ void *addr;
+ abi_long ret;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ addr = alloca(addrlen);
+
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+ if (ret)
+ return ret;
+
+ return get_errno(connect(sockfd, addr, addrlen));
+}
+
+/* do_sendrecvmsg() Must return target values and target errnos. */
+static abi_long do_sendrecvmsg(int fd, abi_ulong target_msg,
+ int flags, int send)
+{
+ abi_long ret, len;
+ struct target_msghdr *msgp;
+ struct msghdr msg;
+ int count;
+ struct iovec *vec;
+ abi_ulong target_vec;
+
+ /* FIXME */
+ if (!lock_user_struct(send ? VERIFY_READ : VERIFY_WRITE,
+ msgp,
+ target_msg,
+ send ? 1 : 0))
+ return -TARGET_EFAULT;
+ if (msgp->msg_name) {
+ msg.msg_namelen = tswap32(msgp->msg_namelen);
+ msg.msg_name = alloca(msg.msg_namelen);
+ ret = target_to_host_sockaddr(msg.msg_name, tswapl(msgp->msg_name),
+ msg.msg_namelen);
+ if (ret) {
+ unlock_user_struct(msgp, target_msg, send ? 0 : 1);
+ return ret;
+ }
+ } else {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ }
+ msg.msg_controllen = 2 * tswapl(msgp->msg_controllen);
+ msg.msg_control = alloca(msg.msg_controllen);
+ msg.msg_flags = tswap32(msgp->msg_flags);
+
+ count = tswapl(msgp->msg_iovlen);
+ vec = alloca(count * sizeof(struct iovec));
+ target_vec = tswapl(msgp->msg_iov);
+ lock_iovec(send ? VERIFY_READ : VERIFY_WRITE, vec, target_vec, count, send);
+ msg.msg_iovlen = count;
+ msg.msg_iov = vec;
+
+ if (send) {
+ ret = target_to_host_cmsg(&msg, msgp);
+ if (ret == 0)
+ ret = get_errno(sendmsg(fd, &msg, flags));
+ } else {
+ ret = get_errno(recvmsg(fd, &msg, flags));
+ if (!is_error(ret)) {
+ len = ret;
+ ret = host_to_target_cmsg(msgp, &msg);
+ if (!is_error(ret))
+ ret = len;
+ }
+ }
+ unlock_iovec(vec, target_vec, count, !send);
+ unlock_user_struct(msgp, target_msg, send ? 0 : 1);
+ return ret;
+}
+
+/* do_accept() Must return target values and target errnos. */
+static abi_long do_accept(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (target_addr == 0)
+ return get_errno(accept(fd, NULL, NULL));
+
+ /* linux returns EINVAL if addrlen pointer is invalid */
+ if (get_user_u32(addrlen, target_addrlen_addr))
+ return -TARGET_EINVAL;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen))
+ return -TARGET_EINVAL;
+
+ addr = alloca(addrlen);
+
+ ret = get_errno(accept(fd, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ if (put_user_u32(addrlen, target_addrlen_addr))
+ ret = -TARGET_EFAULT;
+ }
+ return ret;
+}
+
+/* do_getpeername() Must return target values and target errnos. */
+static abi_long do_getpeername(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (get_user_u32(addrlen, target_addrlen_addr))
+ return -TARGET_EFAULT;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen))
+ return -TARGET_EFAULT;
+
+ addr = alloca(addrlen);
+
+ ret = get_errno(getpeername(fd, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ if (put_user_u32(addrlen, target_addrlen_addr))
+ ret = -TARGET_EFAULT;
+ }
+ return ret;
+}
+
+/* do_getsockname() Must return target values and target errnos. */
+static abi_long do_getsockname(int fd, abi_ulong target_addr,
+ abi_ulong target_addrlen_addr)
+{
+ socklen_t addrlen;
+ void *addr;
+ abi_long ret;
+
+ if (get_user_u32(addrlen, target_addrlen_addr))
+ return -TARGET_EFAULT;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ if (!access_ok(VERIFY_WRITE, target_addr, addrlen))
+ return -TARGET_EFAULT;
+
+ addr = alloca(addrlen);
+
+ ret = get_errno(getsockname(fd, addr, &addrlen));
+ if (!is_error(ret)) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ if (put_user_u32(addrlen, target_addrlen_addr))
+ ret = -TARGET_EFAULT;
+ }
+ return ret;
+}
+
+/* do_socketpair() Must return target values and target errnos. */
+static abi_long do_socketpair(int domain, int type, int protocol,
+ abi_ulong target_tab_addr)
+{
+ int tab[2];
+ abi_long ret;
+
+ ret = get_errno(socketpair(domain, type, protocol, tab));
+ if (!is_error(ret)) {
+ if (put_user_s32(tab[0], target_tab_addr)
+ || put_user_s32(tab[1], target_tab_addr + sizeof(tab[0])))
+ ret = -TARGET_EFAULT;
+ }
+ return ret;
+}
+
+/* do_sendto() Must return target values and target errnos. */
+static abi_long do_sendto(int fd, abi_ulong msg, size_t len, int flags,
+ abi_ulong target_addr, socklen_t addrlen)
+{
+ void *addr;
+ void *host_msg;
+ abi_long ret;
+
+ if ((int)addrlen < 0) {
+ return -TARGET_EINVAL;
+ }
+
+ host_msg = lock_user(VERIFY_READ, msg, len, 1);
+ if (!host_msg)
+ return -TARGET_EFAULT;
+ if (target_addr) {
+ addr = alloca(addrlen);
+ ret = target_to_host_sockaddr(addr, target_addr, addrlen);
+ if (ret) {
+ unlock_user(host_msg, msg, 0);
+ return ret;
+ }
+ ret = get_errno(sendto(fd, host_msg, len, flags, addr, addrlen));
+ } else {
+ ret = get_errno(send(fd, host_msg, len, flags));
+ }
+ unlock_user(host_msg, msg, 0);
+ return ret;
+}
+
+/* do_recvfrom() Must return target values and target errnos. */
+static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
+ abi_ulong target_addr,
+ abi_ulong target_addrlen)
+{
+ socklen_t addrlen;
+ void *addr;
+ void *host_msg;
+ abi_long ret;
+
+ host_msg = lock_user(VERIFY_WRITE, msg, len, 0);
+ if (!host_msg)
+ return -TARGET_EFAULT;
+ if (target_addr) {
+ if (get_user_u32(addrlen, target_addrlen)) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ if ((int)addrlen < 0) {
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ addr = alloca(addrlen);
+ ret = get_errno(recvfrom(fd, host_msg, len, flags, addr, &addrlen));
+ } else {
+ addr = NULL; /* To keep compiler quiet. */
+ ret = get_errno(recv(fd, host_msg, len, flags));
+ }
+ if (!is_error(ret)) {
+ if (target_addr) {
+ host_to_target_sockaddr(target_addr, addr, addrlen);
+ if (put_user_u32(addrlen, target_addrlen)) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ }
+ unlock_user(host_msg, msg, len);
+ } else {
+fail:
+ unlock_user(host_msg, msg, 0);
+ }
+ return ret;
+}
+
+#ifdef TARGET_NR_socketcall
+/* do_socketcall() Must return target values and target errnos. */
+static abi_long do_socketcall(int num, abi_ulong vptr)
+{
+ abi_long ret;
+ const int n = sizeof(abi_ulong);
+
+ switch(num) {
+ case SOCKOP_socket:
+ {
+ abi_ulong domain, type, protocol;
+
+ if (get_user_ual(domain, vptr)
+ || get_user_ual(type, vptr + n)
+ || get_user_ual(protocol, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_socket(domain, type, protocol);
+ }
+ break;
+ case SOCKOP_bind:
+ {
+ abi_ulong sockfd;
+ abi_ulong target_addr;
+ socklen_t addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(target_addr, vptr + n)
+ || get_user_ual(addrlen, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_bind(sockfd, target_addr, addrlen);
+ }
+ break;
+ case SOCKOP_connect:
+ {
+ abi_ulong sockfd;
+ abi_ulong target_addr;
+ socklen_t addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(target_addr, vptr + n)
+ || get_user_ual(addrlen, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_connect(sockfd, target_addr, addrlen);
+ }
+ break;
+ case SOCKOP_listen:
+ {
+ abi_ulong sockfd, backlog;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(backlog, vptr + n))
+ return -TARGET_EFAULT;
+
+ ret = get_errno(listen(sockfd, backlog));
+ }
+ break;
+ case SOCKOP_accept:
+ {
+ abi_ulong sockfd;
+ abi_ulong target_addr, target_addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(target_addr, vptr + n)
+ || get_user_ual(target_addrlen, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_accept(sockfd, target_addr, target_addrlen);
+ }
+ break;
+ case SOCKOP_getsockname:
+ {
+ abi_ulong sockfd;
+ abi_ulong target_addr, target_addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(target_addr, vptr + n)
+ || get_user_ual(target_addrlen, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_getsockname(sockfd, target_addr, target_addrlen);
+ }
+ break;
+ case SOCKOP_getpeername:
+ {
+ abi_ulong sockfd;
+ abi_ulong target_addr, target_addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(target_addr, vptr + n)
+ || get_user_ual(target_addrlen, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_getpeername(sockfd, target_addr, target_addrlen);
+ }
+ break;
+ case SOCKOP_socketpair:
+ {
+ abi_ulong domain, type, protocol;
+ abi_ulong tab;
+
+ if (get_user_ual(domain, vptr)
+ || get_user_ual(type, vptr + n)
+ || get_user_ual(protocol, vptr + 2 * n)
+ || get_user_ual(tab, vptr + 3 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_socketpair(domain, type, protocol, tab);
+ }
+ break;
+ case SOCKOP_send:
+ {
+ abi_ulong sockfd;
+ abi_ulong msg;
+ size_t len;
+ abi_ulong flags;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(msg, vptr + n)
+ || get_user_ual(len, vptr + 2 * n)
+ || get_user_ual(flags, vptr + 3 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_sendto(sockfd, msg, len, flags, 0, 0);
+ }
+ break;
+ case SOCKOP_recv:
+ {
+ abi_ulong sockfd;
+ abi_ulong msg;
+ size_t len;
+ abi_ulong flags;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(msg, vptr + n)
+ || get_user_ual(len, vptr + 2 * n)
+ || get_user_ual(flags, vptr + 3 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_recvfrom(sockfd, msg, len, flags, 0, 0);
+ }
+ break;
+ case SOCKOP_sendto:
+ {
+ abi_ulong sockfd;
+ abi_ulong msg;
+ size_t len;
+ abi_ulong flags;
+ abi_ulong addr;
+ socklen_t addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(msg, vptr + n)
+ || get_user_ual(len, vptr + 2 * n)
+ || get_user_ual(flags, vptr + 3 * n)
+ || get_user_ual(addr, vptr + 4 * n)
+ || get_user_ual(addrlen, vptr + 5 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_sendto(sockfd, msg, len, flags, addr, addrlen);
+ }
+ break;
+ case SOCKOP_recvfrom:
+ {
+ abi_ulong sockfd;
+ abi_ulong msg;
+ size_t len;
+ abi_ulong flags;
+ abi_ulong addr;
+ socklen_t addrlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(msg, vptr + n)
+ || get_user_ual(len, vptr + 2 * n)
+ || get_user_ual(flags, vptr + 3 * n)
+ || get_user_ual(addr, vptr + 4 * n)
+ || get_user_ual(addrlen, vptr + 5 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_recvfrom(sockfd, msg, len, flags, addr, addrlen);
+ }
+ break;
+ case SOCKOP_shutdown:
+ {
+ abi_ulong sockfd, how;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(how, vptr + n))
+ return -TARGET_EFAULT;
+
+ ret = get_errno(shutdown(sockfd, how));
+ }
+ break;
+ case SOCKOP_sendmsg:
+ case SOCKOP_recvmsg:
+ {
+ abi_ulong fd;
+ abi_ulong target_msg;
+ abi_ulong flags;
+
+ if (get_user_ual(fd, vptr)
+ || get_user_ual(target_msg, vptr + n)
+ || get_user_ual(flags, vptr + 2 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_sendrecvmsg(fd, target_msg, flags,
+ (num == SOCKOP_sendmsg));
+ }
+ break;
+ case SOCKOP_setsockopt:
+ {
+ abi_ulong sockfd;
+ abi_ulong level;
+ abi_ulong optname;
+ abi_ulong optval;
+ socklen_t optlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(level, vptr + n)
+ || get_user_ual(optname, vptr + 2 * n)
+ || get_user_ual(optval, vptr + 3 * n)
+ || get_user_ual(optlen, vptr + 4 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_setsockopt(sockfd, level, optname, optval, optlen);
+ }
+ break;
+ case SOCKOP_getsockopt:
+ {
+ abi_ulong sockfd;
+ abi_ulong level;
+ abi_ulong optname;
+ abi_ulong optval;
+ socklen_t optlen;
+
+ if (get_user_ual(sockfd, vptr)
+ || get_user_ual(level, vptr + n)
+ || get_user_ual(optname, vptr + 2 * n)
+ || get_user_ual(optval, vptr + 3 * n)
+ || get_user_ual(optlen, vptr + 4 * n))
+ return -TARGET_EFAULT;
+
+ ret = do_getsockopt(sockfd, level, optname, optval, optlen);
+ }
+ break;
+ default:
+ gemu_log("Unsupported socketcall: %d\n", num);
+ ret = -TARGET_ENOSYS;
+ break;
+ }
+ return ret;
+}
+#endif
+
+#define N_SHM_REGIONS 32
+
+static struct shm_region {
+ abi_ulong start;
+ abi_ulong size;
+} shm_regions[N_SHM_REGIONS];
+
+struct target_ipc_perm
+{
+ abi_long __key;
+ abi_ulong uid;
+ abi_ulong gid;
+ abi_ulong cuid;
+ abi_ulong cgid;
+ unsigned short int mode;
+ unsigned short int __pad1;
+ unsigned short int __seq;
+ unsigned short int __pad2;
+ abi_ulong __unused1;
+ abi_ulong __unused2;
+};
+
+struct target_semid_ds
+{
+ struct target_ipc_perm sem_perm;
+ abi_ulong sem_otime;
+ abi_ulong __unused1;
+ abi_ulong sem_ctime;
+ abi_ulong __unused2;
+ abi_ulong sem_nsems;
+ abi_ulong __unused3;
+ abi_ulong __unused4;
+};
+
+static inline abi_long target_to_host_ipc_perm(struct ipc_perm *host_ip,
+ abi_ulong target_addr)
+{
+ struct target_ipc_perm *target_ip;
+ struct target_semid_ds *target_sd;
+
+ if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1))
+ return -TARGET_EFAULT;
+ target_ip = &(target_sd->sem_perm);
+ host_ip->__key = tswapl(target_ip->__key);
+ host_ip->uid = tswapl(target_ip->uid);
+ host_ip->gid = tswapl(target_ip->gid);
+ host_ip->cuid = tswapl(target_ip->cuid);
+ host_ip->cgid = tswapl(target_ip->cgid);
+ host_ip->mode = tswapl(target_ip->mode);
+ unlock_user_struct(target_sd, target_addr, 0);
+ return 0;
+}
+
+static inline abi_long host_to_target_ipc_perm(abi_ulong target_addr,
+ struct ipc_perm *host_ip)
+{
+ struct target_ipc_perm *target_ip;
+ struct target_semid_ds *target_sd;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0))
+ return -TARGET_EFAULT;
+ target_ip = &(target_sd->sem_perm);
+ target_ip->__key = tswapl(host_ip->__key);
+ target_ip->uid = tswapl(host_ip->uid);
+ target_ip->gid = tswapl(host_ip->gid);
+ target_ip->cuid = tswapl(host_ip->cuid);
+ target_ip->cgid = tswapl(host_ip->cgid);
+ target_ip->mode = tswapl(host_ip->mode);
+ unlock_user_struct(target_sd, target_addr, 1);
+ return 0;
+}
+
+static inline abi_long target_to_host_semid_ds(struct semid_ds *host_sd,
+ abi_ulong target_addr)
+{
+ struct target_semid_ds *target_sd;
+
+ if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1))
+ return -TARGET_EFAULT;
+ if (target_to_host_ipc_perm(&(host_sd->sem_perm),target_addr))
+ return -TARGET_EFAULT;
+ host_sd->sem_nsems = tswapl(target_sd->sem_nsems);
+ host_sd->sem_otime = tswapl(target_sd->sem_otime);
+ host_sd->sem_ctime = tswapl(target_sd->sem_ctime);
+ unlock_user_struct(target_sd, target_addr, 0);
+ return 0;
+}
+
+static inline abi_long host_to_target_semid_ds(abi_ulong target_addr,
+ struct semid_ds *host_sd)
+{
+ struct target_semid_ds *target_sd;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0))
+ return -TARGET_EFAULT;
+ if (host_to_target_ipc_perm(target_addr,&(host_sd->sem_perm)))
+ return -TARGET_EFAULT;;
+ target_sd->sem_nsems = tswapl(host_sd->sem_nsems);
+ target_sd->sem_otime = tswapl(host_sd->sem_otime);
+ target_sd->sem_ctime = tswapl(host_sd->sem_ctime);
+ unlock_user_struct(target_sd, target_addr, 1);
+ return 0;
+}
+
+struct target_seminfo {
+ int semmap;
+ int semmni;
+ int semmns;
+ int semmnu;
+ int semmsl;
+ int semopm;
+ int semume;
+ int semusz;
+ int semvmx;
+ int semaem;
+};
+
+static inline abi_long host_to_target_seminfo(abi_ulong target_addr,
+ struct seminfo *host_seminfo)
+{
+ struct target_seminfo *target_seminfo;
+ if (!lock_user_struct(VERIFY_WRITE, target_seminfo, target_addr, 0))
+ return -TARGET_EFAULT;
+ __put_user(host_seminfo->semmap, &target_seminfo->semmap);
+ __put_user(host_seminfo->semmni, &target_seminfo->semmni);
+ __put_user(host_seminfo->semmns, &target_seminfo->semmns);
+ __put_user(host_seminfo->semmnu, &target_seminfo->semmnu);
+ __put_user(host_seminfo->semmsl, &target_seminfo->semmsl);
+ __put_user(host_seminfo->semopm, &target_seminfo->semopm);
+ __put_user(host_seminfo->semume, &target_seminfo->semume);
+ __put_user(host_seminfo->semusz, &target_seminfo->semusz);
+ __put_user(host_seminfo->semvmx, &target_seminfo->semvmx);
+ __put_user(host_seminfo->semaem, &target_seminfo->semaem);
+ unlock_user_struct(target_seminfo, target_addr, 1);
+ return 0;
+}
+
+union semun {
+ int val;
+ struct semid_ds *buf;
+ unsigned short *array;
+ struct seminfo *__buf;
+};
+
+union target_semun {
+ int val;
+ abi_ulong buf;
+ abi_ulong array;
+ abi_ulong __buf;
+};
+
+static inline abi_long target_to_host_semarray(int semid, unsigned short **host_array,
+ abi_ulong target_addr)
+{
+ int nsems;
+ unsigned short *array;
+ union semun semun;
+ struct semid_ds semid_ds;
+ int i, ret;
+
+ semun.buf = &semid_ds;
+
+ ret = semctl(semid, 0, IPC_STAT, semun);
+ if (ret == -1)
+ return get_errno(ret);
+
+ nsems = semid_ds.sem_nsems;
+
+ *host_array = malloc(nsems*sizeof(unsigned short));
+ array = lock_user(VERIFY_READ, target_addr,
+ nsems*sizeof(unsigned short), 1);
+ if (!array)
+ return -TARGET_EFAULT;
+
+ for(i=0; i<nsems; i++) {
+ __get_user((*host_array)[i], &array[i]);
+ }
+ unlock_user(array, target_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long host_to_target_semarray(int semid, abi_ulong target_addr,
+ unsigned short **host_array)
+{
+ int nsems;
+ unsigned short *array;
+ union semun semun;
+ struct semid_ds semid_ds;
+ int i, ret;
+
+ semun.buf = &semid_ds;
+
+ ret = semctl(semid, 0, IPC_STAT, semun);
+ if (ret == -1)
+ return get_errno(ret);
+
+ nsems = semid_ds.sem_nsems;
+
+ array = lock_user(VERIFY_WRITE, target_addr,
+ nsems*sizeof(unsigned short), 0);
+ if (!array)
+ return -TARGET_EFAULT;
+
+ for(i=0; i<nsems; i++) {
+ __put_user((*host_array)[i], &array[i]);
+ }
+ free(*host_array);
+ unlock_user(array, target_addr, 1);
+
+ return 0;
+}
+
+static inline abi_long do_semctl(int semid, int semnum, int cmd,
+ union target_semun target_su)
+{
+ union semun arg;
+ struct semid_ds dsarg;
+ unsigned short *array = NULL;
+ struct seminfo seminfo;
+ abi_long ret = -TARGET_EINVAL;
+ abi_long err;
+ cmd &= 0xff;
+
+ switch( cmd ) {
+ case GETVAL:
+ case SETVAL:
+ arg.val = tswapl(target_su.val);
+ ret = get_errno(semctl(semid, semnum, cmd, arg));
+ target_su.val = tswapl(arg.val);
+ break;
+ case GETALL:
+ case SETALL:
+ err = target_to_host_semarray(semid, &array, target_su.array);
+ if (err)
+ return err;
+ arg.array = array;
+ ret = get_errno(semctl(semid, semnum, cmd, arg));
+ err = host_to_target_semarray(semid, target_su.array, &array);
+ if (err)
+ return err;
+ break;
+ case IPC_STAT:
+ case IPC_SET:
+ case SEM_STAT:
+ err = target_to_host_semid_ds(&dsarg, target_su.buf);
+ if (err)
+ return err;
+ arg.buf = &dsarg;
+ ret = get_errno(semctl(semid, semnum, cmd, arg));
+ err = host_to_target_semid_ds(target_su.buf, &dsarg);
+ if (err)
+ return err;
+ break;
+ case IPC_INFO:
+ case SEM_INFO:
+ arg.__buf = &seminfo;
+ ret = get_errno(semctl(semid, semnum, cmd, arg));
+ err = host_to_target_seminfo(target_su.__buf, &seminfo);
+ if (err)
+ return err;
+ break;
+ case IPC_RMID:
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ ret = get_errno(semctl(semid, semnum, cmd, NULL));
+ break;
+ }
+
+ return ret;
+}
+
+struct target_sembuf {
+ unsigned short sem_num;
+ short sem_op;
+ short sem_flg;
+};
+
+static inline abi_long target_to_host_sembuf(struct sembuf *host_sembuf,
+ abi_ulong target_addr,
+ unsigned nsops)
+{
+ struct target_sembuf *target_sembuf;
+ int i;
+
+ target_sembuf = lock_user(VERIFY_READ, target_addr,
+ nsops*sizeof(struct target_sembuf), 1);
+ if (!target_sembuf)
+ return -TARGET_EFAULT;
+
+ for(i=0; i<nsops; i++) {
+ __get_user(host_sembuf[i].sem_num, &target_sembuf[i].sem_num);
+ __get_user(host_sembuf[i].sem_op, &target_sembuf[i].sem_op);
+ __get_user(host_sembuf[i].sem_flg, &target_sembuf[i].sem_flg);
+ }
+
+ unlock_user(target_sembuf, target_addr, 0);
+
+ return 0;
+}
+
+static inline abi_long do_semop(int semid, abi_long ptr, unsigned nsops)
+{
+ struct sembuf sops[nsops];
+
+ if (target_to_host_sembuf(sops, ptr, nsops))
+ return -TARGET_EFAULT;
+
+ return semop(semid, sops, nsops);
+}
+
+struct target_msqid_ds
+{
+ struct target_ipc_perm msg_perm;
+ abi_ulong msg_stime;
+#if TARGET_ABI_BITS == 32
+ abi_ulong __unused1;
+#endif
+ abi_ulong msg_rtime;
+#if TARGET_ABI_BITS == 32
+ abi_ulong __unused2;
+#endif
+ abi_ulong msg_ctime;
+#if TARGET_ABI_BITS == 32
+ abi_ulong __unused3;
+#endif
+ abi_ulong __msg_cbytes;
+ abi_ulong msg_qnum;
+ abi_ulong msg_qbytes;
+ abi_ulong msg_lspid;
+ abi_ulong msg_lrpid;
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
+static inline abi_long target_to_host_msqid_ds(struct msqid_ds *host_md,
+ abi_ulong target_addr)
+{
+ struct target_msqid_ds *target_md;
+
+ if (!lock_user_struct(VERIFY_READ, target_md, target_addr, 1))
+ return -TARGET_EFAULT;
+ if (target_to_host_ipc_perm(&(host_md->msg_perm),target_addr))
+ return -TARGET_EFAULT;
+ host_md->msg_stime = tswapl(target_md->msg_stime);
+ host_md->msg_rtime = tswapl(target_md->msg_rtime);
+ host_md->msg_ctime = tswapl(target_md->msg_ctime);
+ host_md->__msg_cbytes = tswapl(target_md->__msg_cbytes);
+ host_md->msg_qnum = tswapl(target_md->msg_qnum);
+ host_md->msg_qbytes = tswapl(target_md->msg_qbytes);
+ host_md->msg_lspid = tswapl(target_md->msg_lspid);
+ host_md->msg_lrpid = tswapl(target_md->msg_lrpid);
+ unlock_user_struct(target_md, target_addr, 0);
+ return 0;
+}
+
+static inline abi_long host_to_target_msqid_ds(abi_ulong target_addr,
+ struct msqid_ds *host_md)
+{
+ struct target_msqid_ds *target_md;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_md, target_addr, 0))
+ return -TARGET_EFAULT;
+ if (host_to_target_ipc_perm(target_addr,&(host_md->msg_perm)))
+ return -TARGET_EFAULT;
+ target_md->msg_stime = tswapl(host_md->msg_stime);
+ target_md->msg_rtime = tswapl(host_md->msg_rtime);
+ target_md->msg_ctime = tswapl(host_md->msg_ctime);
+ target_md->__msg_cbytes = tswapl(host_md->__msg_cbytes);
+ target_md->msg_qnum = tswapl(host_md->msg_qnum);
+ target_md->msg_qbytes = tswapl(host_md->msg_qbytes);
+ target_md->msg_lspid = tswapl(host_md->msg_lspid);
+ target_md->msg_lrpid = tswapl(host_md->msg_lrpid);
+ unlock_user_struct(target_md, target_addr, 1);
+ return 0;
+}
+
+struct target_msginfo {
+ int msgpool;
+ int msgmap;
+ int msgmax;
+ int msgmnb;
+ int msgmni;
+ int msgssz;
+ int msgtql;
+ unsigned short int msgseg;
+};
+
+static inline abi_long host_to_target_msginfo(abi_ulong target_addr,
+ struct msginfo *host_msginfo)
+{
+ struct target_msginfo *target_msginfo;
+ if (!lock_user_struct(VERIFY_WRITE, target_msginfo, target_addr, 0))
+ return -TARGET_EFAULT;
+ __put_user(host_msginfo->msgpool, &target_msginfo->msgpool);
+ __put_user(host_msginfo->msgmap, &target_msginfo->msgmap);
+ __put_user(host_msginfo->msgmax, &target_msginfo->msgmax);
+ __put_user(host_msginfo->msgmnb, &target_msginfo->msgmnb);
+ __put_user(host_msginfo->msgmni, &target_msginfo->msgmni);
+ __put_user(host_msginfo->msgssz, &target_msginfo->msgssz);
+ __put_user(host_msginfo->msgtql, &target_msginfo->msgtql);
+ __put_user(host_msginfo->msgseg, &target_msginfo->msgseg);
+ unlock_user_struct(target_msginfo, target_addr, 1);
+ return 0;
+}
+
+static inline abi_long do_msgctl(int msgid, int cmd, abi_long ptr)
+{
+ struct msqid_ds dsarg;
+ struct msginfo msginfo;
+ abi_long ret = -TARGET_EINVAL;
+
+ cmd &= 0xff;
+
+ switch (cmd) {
+ case IPC_STAT:
+ case IPC_SET:
+ case MSG_STAT:
+ if (target_to_host_msqid_ds(&dsarg,ptr))
+ return -TARGET_EFAULT;
+ ret = get_errno(msgctl(msgid, cmd, &dsarg));
+ if (host_to_target_msqid_ds(ptr,&dsarg))
+ return -TARGET_EFAULT;
+ break;
+ case IPC_RMID:
+ ret = get_errno(msgctl(msgid, cmd, NULL));
+ break;
+ case IPC_INFO:
+ case MSG_INFO:
+ ret = get_errno(msgctl(msgid, cmd, (struct msqid_ds *)&msginfo));
+ if (host_to_target_msginfo(ptr, &msginfo))
+ return -TARGET_EFAULT;
+ break;
+ }
+
+ return ret;
+}
+
+struct target_msgbuf {
+ abi_long mtype;
+ char mtext[1];
+};
+
+static inline abi_long do_msgsnd(int msqid, abi_long msgp,
+ unsigned int msgsz, int msgflg)
+{
+ struct target_msgbuf *target_mb;
+ struct msgbuf *host_mb;
+ abi_long ret = 0;
+
+ if (!lock_user_struct(VERIFY_READ, target_mb, msgp, 0))
+ return -TARGET_EFAULT;
+ host_mb = malloc(msgsz+sizeof(long));
+ host_mb->mtype = (abi_long) tswapl(target_mb->mtype);
+ memcpy(host_mb->mtext, target_mb->mtext, msgsz);
+ ret = get_errno(msgsnd(msqid, host_mb, msgsz, msgflg));
+ free(host_mb);
+ unlock_user_struct(target_mb, msgp, 0);
+
+ return ret;
+}
+
+static inline abi_long do_msgrcv(int msqid, abi_long msgp,
+ unsigned int msgsz, abi_long msgtyp,
+ int msgflg)
+{
+ struct target_msgbuf *target_mb;
+ char *target_mtext;
+ struct msgbuf *host_mb;
+ abi_long ret = 0;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0))
+ return -TARGET_EFAULT;
+
+ host_mb = malloc(msgsz+sizeof(long));
+ ret = get_errno(msgrcv(msqid, host_mb, msgsz, tswapl(msgtyp), msgflg));
+
+ if (ret > 0) {
+ abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong);
+ target_mtext = lock_user(VERIFY_WRITE, target_mtext_addr, ret, 0);
+ if (!target_mtext) {
+ ret = -TARGET_EFAULT;
+ goto end;
+ }
+ memcpy(target_mb->mtext, host_mb->mtext, ret);
+ unlock_user(target_mtext, target_mtext_addr, ret);
+ }
+
+ target_mb->mtype = tswapl(host_mb->mtype);
+ free(host_mb);
+
+end:
+ if (target_mb)
+ unlock_user_struct(target_mb, msgp, 1);
+ return ret;
+}
+
+struct target_shmid_ds
+{
+ struct target_ipc_perm shm_perm;
+ abi_ulong shm_segsz;
+ abi_ulong shm_atime;
+#if TARGET_ABI_BITS == 32
+ abi_ulong __unused1;
+#endif
+ abi_ulong shm_dtime;
+#if TARGET_ABI_BITS == 32
+ abi_ulong __unused2;
+#endif
+ abi_ulong shm_ctime;
+#if TARGET_ABI_BITS == 32
+ abi_ulong __unused3;
+#endif
+ int shm_cpid;
+ int shm_lpid;
+ abi_ulong shm_nattch;
+ unsigned long int __unused4;
+ unsigned long int __unused5;
+};
+
+static inline abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd,
+ abi_ulong target_addr)
+{
+ struct target_shmid_ds *target_sd;
+
+ if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1))
+ return -TARGET_EFAULT;
+ if (target_to_host_ipc_perm(&(host_sd->shm_perm), target_addr))
+ return -TARGET_EFAULT;
+ __get_user(host_sd->shm_segsz, &target_sd->shm_segsz);
+ __get_user(host_sd->shm_atime, &target_sd->shm_atime);
+ __get_user(host_sd->shm_dtime, &target_sd->shm_dtime);
+ __get_user(host_sd->shm_ctime, &target_sd->shm_ctime);
+ __get_user(host_sd->shm_cpid, &target_sd->shm_cpid);
+ __get_user(host_sd->shm_lpid, &target_sd->shm_lpid);
+ __get_user(host_sd->shm_nattch, &target_sd->shm_nattch);
+ unlock_user_struct(target_sd, target_addr, 0);
+ return 0;
+}
+
+static inline abi_long host_to_target_shmid_ds(abi_ulong target_addr,
+ struct shmid_ds *host_sd)
+{
+ struct target_shmid_ds *target_sd;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0))
+ return -TARGET_EFAULT;
+ if (host_to_target_ipc_perm(target_addr, &(host_sd->shm_perm)))
+ return -TARGET_EFAULT;
+ __put_user(host_sd->shm_segsz, &target_sd->shm_segsz);
+ __put_user(host_sd->shm_atime, &target_sd->shm_atime);
+ __put_user(host_sd->shm_dtime, &target_sd->shm_dtime);
+ __put_user(host_sd->shm_ctime, &target_sd->shm_ctime);
+ __put_user(host_sd->shm_cpid, &target_sd->shm_cpid);
+ __put_user(host_sd->shm_lpid, &target_sd->shm_lpid);
+ __put_user(host_sd->shm_nattch, &target_sd->shm_nattch);
+ unlock_user_struct(target_sd, target_addr, 1);
+ return 0;
+}
+
+struct target_shminfo {
+ abi_ulong shmmax;
+ abi_ulong shmmin;
+ abi_ulong shmmni;
+ abi_ulong shmseg;
+ abi_ulong shmall;
+};
+
+static inline abi_long host_to_target_shminfo(abi_ulong target_addr,
+ struct shminfo *host_shminfo)
+{
+ struct target_shminfo *target_shminfo;
+ if (!lock_user_struct(VERIFY_WRITE, target_shminfo, target_addr, 0))
+ return -TARGET_EFAULT;
+ __put_user(host_shminfo->shmmax, &target_shminfo->shmmax);
+ __put_user(host_shminfo->shmmin, &target_shminfo->shmmin);
+ __put_user(host_shminfo->shmmni, &target_shminfo->shmmni);
+ __put_user(host_shminfo->shmseg, &target_shminfo->shmseg);
+ __put_user(host_shminfo->shmall, &target_shminfo->shmall);
+ unlock_user_struct(target_shminfo, target_addr, 1);
+ return 0;
+}
+
+struct target_shm_info {
+ int used_ids;
+ abi_ulong shm_tot;
+ abi_ulong shm_rss;
+ abi_ulong shm_swp;
+ abi_ulong swap_attempts;
+ abi_ulong swap_successes;
+};
+
+static inline abi_long host_to_target_shm_info(abi_ulong target_addr,
+ struct shm_info *host_shm_info)
+{
+ struct target_shm_info *target_shm_info;
+ if (!lock_user_struct(VERIFY_WRITE, target_shm_info, target_addr, 0))
+ return -TARGET_EFAULT;
+ __put_user(host_shm_info->used_ids, &target_shm_info->used_ids);
+ __put_user(host_shm_info->shm_tot, &target_shm_info->shm_tot);
+ __put_user(host_shm_info->shm_rss, &target_shm_info->shm_rss);
+ __put_user(host_shm_info->shm_swp, &target_shm_info->shm_swp);
+ __put_user(host_shm_info->swap_attempts, &target_shm_info->swap_attempts);
+ __put_user(host_shm_info->swap_successes, &target_shm_info->swap_successes);
+ unlock_user_struct(target_shm_info, target_addr, 1);
+ return 0;
+}
+
+static inline abi_long do_shmctl(int shmid, int cmd, abi_long buf)
+{
+ struct shmid_ds dsarg;
+ struct shminfo shminfo;
+ struct shm_info shm_info;
+ abi_long ret = -TARGET_EINVAL;
+
+ cmd &= 0xff;
+
+ switch(cmd) {
+ case IPC_STAT:
+ case IPC_SET:
+ case SHM_STAT:
+ if (target_to_host_shmid_ds(&dsarg, buf))
+ return -TARGET_EFAULT;
+ ret = get_errno(shmctl(shmid, cmd, &dsarg));
+ if (host_to_target_shmid_ds(buf, &dsarg))
+ return -TARGET_EFAULT;
+ break;
+ case IPC_INFO:
+ ret = get_errno(shmctl(shmid, cmd, (struct shmid_ds *)&shminfo));
+ if (host_to_target_shminfo(buf, &shminfo))
+ return -TARGET_EFAULT;
+ break;
+ case SHM_INFO:
+ ret = get_errno(shmctl(shmid, cmd, (struct shmid_ds *)&shm_info));
+ if (host_to_target_shm_info(buf, &shm_info))
+ return -TARGET_EFAULT;
+ break;
+ case IPC_RMID:
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ ret = get_errno(shmctl(shmid, cmd, NULL));
+ break;
+ }
+
+ return ret;
+}
+
+static inline abi_ulong do_shmat(int shmid, abi_ulong shmaddr, int shmflg)
+{
+ abi_long raddr;
+ void *host_raddr;
+ struct shmid_ds shm_info;
+ int i,ret;
+
+ /* find out the length of the shared memory segment */
+ ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info));
+ if (is_error(ret)) {
+ /* can't get length, bail out */
+ return ret;
+ }
+
+ mmap_lock();
+
+ if (shmaddr)
+ host_raddr = shmat(shmid, (void *)g2h(shmaddr), shmflg);
+ else {
+ abi_ulong mmap_start;
+
+ mmap_start = mmap_find_vma(0, shm_info.shm_segsz);
+
+ if (mmap_start == -1) {
+ errno = ENOMEM;
+ host_raddr = (void *)-1;
+ } else
+ host_raddr = shmat(shmid, g2h(mmap_start), shmflg | SHM_REMAP);
+ }
+
+ if (host_raddr == (void *)-1) {
+ mmap_unlock();
+ return get_errno((long)host_raddr);
+ }
+ raddr=h2g((unsigned long)host_raddr);
+
+ page_set_flags(raddr, raddr + shm_info.shm_segsz,
+ PAGE_VALID | PAGE_READ |
+ ((shmflg & SHM_RDONLY)? 0 : PAGE_WRITE));
+
+ for (i = 0; i < N_SHM_REGIONS; i++) {
+ if (shm_regions[i].start == 0) {
+ shm_regions[i].start = raddr;
+ shm_regions[i].size = shm_info.shm_segsz;
+ break;
+ }
+ }
+
+ mmap_unlock();
+ return raddr;
+
+}
+
+static inline abi_long do_shmdt(abi_ulong shmaddr)
+{
+ int i;
+
+ for (i = 0; i < N_SHM_REGIONS; ++i) {
+ if (shm_regions[i].start == shmaddr) {
+ shm_regions[i].start = 0;
+ page_set_flags(shmaddr, shmaddr + shm_regions[i].size, 0);
+ break;
+ }
+ }
+
+ return get_errno(shmdt(g2h(shmaddr)));
+}
+
+#ifdef TARGET_NR_ipc
+/* ??? This only works with linear mappings. */
+/* do_ipc() must return target values and target errnos. */
+static abi_long do_ipc(unsigned int call, int first,
+ int second, int third,
+ abi_long ptr, abi_long fifth)
+{
+ int version;
+ abi_long ret = 0;
+
+ version = call >> 16;
+ call &= 0xffff;
+
+ switch (call) {
+ case IPCOP_semop:
+ ret = do_semop(first, ptr, second);
+ break;
+
+ case IPCOP_semget:
+ ret = get_errno(semget(first, second, third));
+ break;
+
+ case IPCOP_semctl:
+ ret = do_semctl(first, second, third, (union target_semun)(abi_ulong) ptr);
+ break;
+
+ case IPCOP_msgget:
+ ret = get_errno(msgget(first, second));
+ break;
+
+ case IPCOP_msgsnd:
+ ret = do_msgsnd(first, ptr, second, third);
+ break;
+
+ case IPCOP_msgctl:
+ ret = do_msgctl(first, second, ptr);
+ break;
+
+ case IPCOP_msgrcv:
+ switch (version) {
+ case 0:
+ {
+ struct target_ipc_kludge {
+ abi_long msgp;
+ abi_long msgtyp;
+ } *tmp;
+
+ if (!lock_user_struct(VERIFY_READ, tmp, ptr, 1)) {
+ ret = -TARGET_EFAULT;
+ break;
+ }
+
+ ret = do_msgrcv(first, tmp->msgp, second, tmp->msgtyp, third);
+
+ unlock_user_struct(tmp, ptr, 0);
+ break;
+ }
+ default:
+ ret = do_msgrcv(first, ptr, second, fifth, third);
+ }
+ break;
+
+ case IPCOP_shmat:
+ switch (version) {
+ default:
+ {
+ abi_ulong raddr;
+ raddr = do_shmat(first, ptr, second);
+ if (is_error(raddr))
+ return get_errno(raddr);
+ if (put_user_ual(raddr, third))
+ return -TARGET_EFAULT;
+ break;
+ }
+ case 1:
+ ret = -TARGET_EINVAL;
+ break;
+ }
+ break;
+ case IPCOP_shmdt:
+ ret = do_shmdt(ptr);
+ break;
+
+ case IPCOP_shmget:
+ /* IPC_* flag values are the same on all linux platforms */
+ ret = get_errno(shmget(first, second, third));
+ break;
+
+ /* IPC_* and SHM_* command values are the same on all linux platforms */
+ case IPCOP_shmctl:
+ ret = do_shmctl(first, second, third);
+ break;
+ default:
+ gemu_log("Unsupported ipc call: %d (version %d)\n", call, version);
+ ret = -TARGET_ENOSYS;
+ break;
+ }
+ return ret;
+}
+#endif
+
+/* kernel structure types definitions */
+#define IFNAMSIZ 16
+
+#define STRUCT(name, ...) STRUCT_ ## name,
+#define STRUCT_SPECIAL(name) STRUCT_ ## name,
+enum {
+#include "syscall_types.h"
+};
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+#define STRUCT(name, ...) static const argtype struct_ ## name ## _def[] = { __VA_ARGS__, TYPE_NULL };
+#define STRUCT_SPECIAL(name)
+#include "syscall_types.h"
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+typedef struct IOCTLEntry IOCTLEntry;
+
+typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, abi_long cmd, abi_long arg);
+
+struct IOCTLEntry {
+ unsigned int target_cmd;
+ unsigned int host_cmd;
+ const char *name;
+ int access;
+ do_ioctl_fn *do_ioctl;
+ const argtype arg_type[5];
+};
+
+#define IOC_R 0x0001
+#define IOC_W 0x0002
+#define IOC_RW (IOC_R | IOC_W)
+
+#define MAX_STRUCT_SIZE 4096
+
+#ifdef CONFIG_FIEMAP
+/* So fiemap access checks don't overflow on 32 bit systems.
+ * This is very slightly smaller than the limit imposed by
+ * the underlying kernel.
+ */
+#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap)) \
+ / sizeof(struct fiemap_extent))
+
+static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, abi_long cmd, abi_long arg)
+{
+ /* The parameter for this ioctl is a struct fiemap followed
+ * by an array of struct fiemap_extent whose size is set
+ * in fiemap->fm_extent_count. The array is filled in by the
+ * ioctl.
+ */
+ int target_size_in, target_size_out;
+ struct fiemap *fm;
+ const argtype *arg_type = ie->arg_type;
+ const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) };
+ void *argptr, *p;
+ abi_long ret;
+ int i, extent_size = thunk_type_size(extent_arg_type, 0);
+ uint32_t outbufsz;
+ int free_fm = 0;
+
+ assert(arg_type[0] == TYPE_PTR);
+ assert(ie->access == IOC_RW);
+ arg_type++;
+ target_size_in = thunk_type_size(arg_type, 0);
+ argptr = lock_user(VERIFY_READ, arg, target_size_in, 1);
+ if (!argptr) {
+ return -TARGET_EFAULT;
+ }
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ fm = (struct fiemap *)buf_temp;
+ if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) {
+ return -TARGET_EINVAL;
+ }
+
+ outbufsz = sizeof (*fm) +
+ (sizeof(struct fiemap_extent) * fm->fm_extent_count);
+
+ if (outbufsz > MAX_STRUCT_SIZE) {
+ /* We can't fit all the extents into the fixed size buffer.
+ * Allocate one that is large enough and use it instead.
+ */
+ fm = malloc(outbufsz);
+ if (!fm) {
+ return -TARGET_ENOMEM;
+ }
+ memcpy(fm, buf_temp, sizeof(struct fiemap));
+ free_fm = 1;
+ }
+ ret = get_errno(ioctl(fd, ie->host_cmd, fm));
+ if (!is_error(ret)) {
+ target_size_out = target_size_in;
+ /* An extent_count of 0 means we were only counting the extents
+ * so there are no structs to copy
+ */
+ if (fm->fm_extent_count != 0) {
+ target_size_out += fm->fm_mapped_extents * extent_size;
+ }
+ argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0);
+ if (!argptr) {
+ ret = -TARGET_EFAULT;
+ } else {
+ /* Convert the struct fiemap */
+ thunk_convert(argptr, fm, arg_type, THUNK_TARGET);
+ if (fm->fm_extent_count != 0) {
+ p = argptr + target_size_in;
+ /* ...and then all the struct fiemap_extents */
+ for (i = 0; i < fm->fm_mapped_extents; i++) {
+ thunk_convert(p, &fm->fm_extents[i], extent_arg_type,
+ THUNK_TARGET);
+ p += extent_size;
+ }
+ }
+ unlock_user(argptr, arg, target_size_out);
+ }
+ }
+ if (free_fm) {
+ free(fm);
+ }
+ return ret;
+}
+#endif
+
+static IOCTLEntry ioctl_entries[] = {
+#define IOCTL(cmd, access, ...) \
+ { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } },
+#define IOCTL_SPECIAL(cmd, access, dofn, ...) \
+ { TARGET_ ## cmd, cmd, #cmd, access, dofn, { __VA_ARGS__ } },
+#include "ioctls.h"
+ { 0, 0, },
+};
+
+/* ??? Implement proper locking for ioctls. */
+/* do_ioctl() Must return target values and target errnos. */
+static abi_long do_ioctl(int fd, abi_long cmd, abi_long arg)
+{
+ const IOCTLEntry *ie;
+ const argtype *arg_type;
+ abi_long ret;
+ uint8_t buf_temp[MAX_STRUCT_SIZE];
+ int target_size;
+ void *argptr;
+
+ ie = ioctl_entries;
+ for(;;) {
+ if (ie->target_cmd == 0) {
+ gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd);
+ return -TARGET_ENOSYS;
+ }
+ if (ie->target_cmd == cmd)
+ break;
+ ie++;
+ }
+ arg_type = ie->arg_type;
+#if defined(DEBUG)
+ gemu_log("ioctl: cmd=0x%04lx (%s)\n", (long)cmd, ie->name);
+#endif
+ if (ie->do_ioctl) {
+ return ie->do_ioctl(ie, buf_temp, fd, cmd, arg);
+ }
+
+ switch(arg_type[0]) {
+ case TYPE_NULL:
+ /* no argument */
+ ret = get_errno(ioctl(fd, ie->host_cmd));
+ break;
+ case TYPE_PTRVOID:
+ case TYPE_INT:
+ /* int argment */
+ ret = get_errno(ioctl(fd, ie->host_cmd, arg));
+ break;
+ case TYPE_PTR:
+ arg_type++;
+ target_size = thunk_type_size(arg_type, 0);
+ switch(ie->access) {
+ case IOC_R:
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ if (!is_error(ret)) {
+ argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+ if (!argptr)
+ return -TARGET_EFAULT;
+ thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+ }
+ break;
+ case IOC_W:
+ argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+ if (!argptr)
+ return -TARGET_EFAULT;
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ break;
+ default:
+ case IOC_RW:
+ argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+ if (!argptr)
+ return -TARGET_EFAULT;
+ thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+ ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp));
+ if (!is_error(ret)) {
+ argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+ if (!argptr)
+ return -TARGET_EFAULT;
+ thunk_convert(argptr, buf_temp, arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+ }
+ break;
+ }
+ break;
+ default:
+ gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n",
+ (long)cmd, arg_type[0]);
+ ret = -TARGET_ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+static const bitmask_transtbl iflag_tbl[] = {
+ { TARGET_IGNBRK, TARGET_IGNBRK, IGNBRK, IGNBRK },
+ { TARGET_BRKINT, TARGET_BRKINT, BRKINT, BRKINT },
+ { TARGET_IGNPAR, TARGET_IGNPAR, IGNPAR, IGNPAR },
+ { TARGET_PARMRK, TARGET_PARMRK, PARMRK, PARMRK },
+ { TARGET_INPCK, TARGET_INPCK, INPCK, INPCK },
+ { TARGET_ISTRIP, TARGET_ISTRIP, ISTRIP, ISTRIP },
+ { TARGET_INLCR, TARGET_INLCR, INLCR, INLCR },
+ { TARGET_IGNCR, TARGET_IGNCR, IGNCR, IGNCR },
+ { TARGET_ICRNL, TARGET_ICRNL, ICRNL, ICRNL },
+ { TARGET_IUCLC, TARGET_IUCLC, IUCLC, IUCLC },
+ { TARGET_IXON, TARGET_IXON, IXON, IXON },
+ { TARGET_IXANY, TARGET_IXANY, IXANY, IXANY },
+ { TARGET_IXOFF, TARGET_IXOFF, IXOFF, IXOFF },
+ { TARGET_IMAXBEL, TARGET_IMAXBEL, IMAXBEL, IMAXBEL },
+ { 0, 0, 0, 0 }
+};
+
+static const bitmask_transtbl oflag_tbl[] = {
+ { TARGET_OPOST, TARGET_OPOST, OPOST, OPOST },
+ { TARGET_OLCUC, TARGET_OLCUC, OLCUC, OLCUC },
+ { TARGET_ONLCR, TARGET_ONLCR, ONLCR, ONLCR },
+ { TARGET_OCRNL, TARGET_OCRNL, OCRNL, OCRNL },
+ { TARGET_ONOCR, TARGET_ONOCR, ONOCR, ONOCR },
+ { TARGET_ONLRET, TARGET_ONLRET, ONLRET, ONLRET },
+ { TARGET_OFILL, TARGET_OFILL, OFILL, OFILL },
+ { TARGET_OFDEL, TARGET_OFDEL, OFDEL, OFDEL },
+ { TARGET_NLDLY, TARGET_NL0, NLDLY, NL0 },
+ { TARGET_NLDLY, TARGET_NL1, NLDLY, NL1 },
+ { TARGET_CRDLY, TARGET_CR0, CRDLY, CR0 },
+ { TARGET_CRDLY, TARGET_CR1, CRDLY, CR1 },
+ { TARGET_CRDLY, TARGET_CR2, CRDLY, CR2 },
+ { TARGET_CRDLY, TARGET_CR3, CRDLY, CR3 },
+ { TARGET_TABDLY, TARGET_TAB0, TABDLY, TAB0 },
+ { TARGET_TABDLY, TARGET_TAB1, TABDLY, TAB1 },
+ { TARGET_TABDLY, TARGET_TAB2, TABDLY, TAB2 },
+ { TARGET_TABDLY, TARGET_TAB3, TABDLY, TAB3 },
+ { TARGET_BSDLY, TARGET_BS0, BSDLY, BS0 },
+ { TARGET_BSDLY, TARGET_BS1, BSDLY, BS1 },
+ { TARGET_VTDLY, TARGET_VT0, VTDLY, VT0 },
+ { TARGET_VTDLY, TARGET_VT1, VTDLY, VT1 },
+ { TARGET_FFDLY, TARGET_FF0, FFDLY, FF0 },
+ { TARGET_FFDLY, TARGET_FF1, FFDLY, FF1 },
+ { 0, 0, 0, 0 }
+};
+
+static const bitmask_transtbl cflag_tbl[] = {
+ { TARGET_CBAUD, TARGET_B0, CBAUD, B0 },
+ { TARGET_CBAUD, TARGET_B50, CBAUD, B50 },
+ { TARGET_CBAUD, TARGET_B75, CBAUD, B75 },
+ { TARGET_CBAUD, TARGET_B110, CBAUD, B110 },
+ { TARGET_CBAUD, TARGET_B134, CBAUD, B134 },
+ { TARGET_CBAUD, TARGET_B150, CBAUD, B150 },
+ { TARGET_CBAUD, TARGET_B200, CBAUD, B200 },
+ { TARGET_CBAUD, TARGET_B300, CBAUD, B300 },
+ { TARGET_CBAUD, TARGET_B600, CBAUD, B600 },
+ { TARGET_CBAUD, TARGET_B1200, CBAUD, B1200 },
+ { TARGET_CBAUD, TARGET_B1800, CBAUD, B1800 },
+ { TARGET_CBAUD, TARGET_B2400, CBAUD, B2400 },
+ { TARGET_CBAUD, TARGET_B4800, CBAUD, B4800 },
+ { TARGET_CBAUD, TARGET_B9600, CBAUD, B9600 },
+ { TARGET_CBAUD, TARGET_B19200, CBAUD, B19200 },
+ { TARGET_CBAUD, TARGET_B38400, CBAUD, B38400 },
+ { TARGET_CBAUD, TARGET_B57600, CBAUD, B57600 },
+ { TARGET_CBAUD, TARGET_B115200, CBAUD, B115200 },
+ { TARGET_CBAUD, TARGET_B230400, CBAUD, B230400 },
+ { TARGET_CBAUD, TARGET_B460800, CBAUD, B460800 },
+ { TARGET_CSIZE, TARGET_CS5, CSIZE, CS5 },
+ { TARGET_CSIZE, TARGET_CS6, CSIZE, CS6 },
+ { TARGET_CSIZE, TARGET_CS7, CSIZE, CS7 },
+ { TARGET_CSIZE, TARGET_CS8, CSIZE, CS8 },
+ { TARGET_CSTOPB, TARGET_CSTOPB, CSTOPB, CSTOPB },
+ { TARGET_CREAD, TARGET_CREAD, CREAD, CREAD },
+ { TARGET_PARENB, TARGET_PARENB, PARENB, PARENB },
+ { TARGET_PARODD, TARGET_PARODD, PARODD, PARODD },
+ { TARGET_HUPCL, TARGET_HUPCL, HUPCL, HUPCL },
+ { TARGET_CLOCAL, TARGET_CLOCAL, CLOCAL, CLOCAL },
+ { TARGET_CRTSCTS, TARGET_CRTSCTS, CRTSCTS, CRTSCTS },
+ { 0, 0, 0, 0 }
+};
+
+static const bitmask_transtbl lflag_tbl[] = {
+ { TARGET_ISIG, TARGET_ISIG, ISIG, ISIG },
+ { TARGET_ICANON, TARGET_ICANON, ICANON, ICANON },
+ { TARGET_XCASE, TARGET_XCASE, XCASE, XCASE },
+ { TARGET_ECHO, TARGET_ECHO, ECHO, ECHO },
+ { TARGET_ECHOE, TARGET_ECHOE, ECHOE, ECHOE },
+ { TARGET_ECHOK, TARGET_ECHOK, ECHOK, ECHOK },
+ { TARGET_ECHONL, TARGET_ECHONL, ECHONL, ECHONL },
+ { TARGET_NOFLSH, TARGET_NOFLSH, NOFLSH, NOFLSH },
+ { TARGET_TOSTOP, TARGET_TOSTOP, TOSTOP, TOSTOP },
+ { TARGET_ECHOCTL, TARGET_ECHOCTL, ECHOCTL, ECHOCTL },
+ { TARGET_ECHOPRT, TARGET_ECHOPRT, ECHOPRT, ECHOPRT },
+ { TARGET_ECHOKE, TARGET_ECHOKE, ECHOKE, ECHOKE },
+ { TARGET_FLUSHO, TARGET_FLUSHO, FLUSHO, FLUSHO },
+ { TARGET_PENDIN, TARGET_PENDIN, PENDIN, PENDIN },
+ { TARGET_IEXTEN, TARGET_IEXTEN, IEXTEN, IEXTEN },
+ { 0, 0, 0, 0 }
+};
+
+static void target_to_host_termios (void *dst, const void *src)
+{
+ struct host_termios *host = dst;
+ const struct target_termios *target = src;
+
+ host->c_iflag =
+ target_to_host_bitmask(tswap32(target->c_iflag), iflag_tbl);
+ host->c_oflag =
+ target_to_host_bitmask(tswap32(target->c_oflag), oflag_tbl);
+ host->c_cflag =
+ target_to_host_bitmask(tswap32(target->c_cflag), cflag_tbl);
+ host->c_lflag =
+ target_to_host_bitmask(tswap32(target->c_lflag), lflag_tbl);
+ host->c_line = target->c_line;
+
+ memset(host->c_cc, 0, sizeof(host->c_cc));
+ host->c_cc[VINTR] = target->c_cc[TARGET_VINTR];
+ host->c_cc[VQUIT] = target->c_cc[TARGET_VQUIT];
+ host->c_cc[VERASE] = target->c_cc[TARGET_VERASE];
+ host->c_cc[VKILL] = target->c_cc[TARGET_VKILL];
+ host->c_cc[VEOF] = target->c_cc[TARGET_VEOF];
+ host->c_cc[VTIME] = target->c_cc[TARGET_VTIME];
+ host->c_cc[VMIN] = target->c_cc[TARGET_VMIN];
+ host->c_cc[VSWTC] = target->c_cc[TARGET_VSWTC];
+ host->c_cc[VSTART] = target->c_cc[TARGET_VSTART];
+ host->c_cc[VSTOP] = target->c_cc[TARGET_VSTOP];
+ host->c_cc[VSUSP] = target->c_cc[TARGET_VSUSP];
+ host->c_cc[VEOL] = target->c_cc[TARGET_VEOL];
+ host->c_cc[VREPRINT] = target->c_cc[TARGET_VREPRINT];
+ host->c_cc[VDISCARD] = target->c_cc[TARGET_VDISCARD];
+ host->c_cc[VWERASE] = target->c_cc[TARGET_VWERASE];
+ host->c_cc[VLNEXT] = target->c_cc[TARGET_VLNEXT];
+ host->c_cc[VEOL2] = target->c_cc[TARGET_VEOL2];
+}
+
+static void host_to_target_termios (void *dst, const void *src)
+{
+ struct target_termios *target = dst;
+ const struct host_termios *host = src;
+
+ target->c_iflag =
+ tswap32(host_to_target_bitmask(host->c_iflag, iflag_tbl));
+ target->c_oflag =
+ tswap32(host_to_target_bitmask(host->c_oflag, oflag_tbl));
+ target->c_cflag =
+ tswap32(host_to_target_bitmask(host->c_cflag, cflag_tbl));
+ target->c_lflag =
+ tswap32(host_to_target_bitmask(host->c_lflag, lflag_tbl));
+ target->c_line = host->c_line;
+
+ memset(target->c_cc, 0, sizeof(target->c_cc));
+ target->c_cc[TARGET_VINTR] = host->c_cc[VINTR];
+ target->c_cc[TARGET_VQUIT] = host->c_cc[VQUIT];
+ target->c_cc[TARGET_VERASE] = host->c_cc[VERASE];
+ target->c_cc[TARGET_VKILL] = host->c_cc[VKILL];
+ target->c_cc[TARGET_VEOF] = host->c_cc[VEOF];
+ target->c_cc[TARGET_VTIME] = host->c_cc[VTIME];
+ target->c_cc[TARGET_VMIN] = host->c_cc[VMIN];
+ target->c_cc[TARGET_VSWTC] = host->c_cc[VSWTC];
+ target->c_cc[TARGET_VSTART] = host->c_cc[VSTART];
+ target->c_cc[TARGET_VSTOP] = host->c_cc[VSTOP];
+ target->c_cc[TARGET_VSUSP] = host->c_cc[VSUSP];
+ target->c_cc[TARGET_VEOL] = host->c_cc[VEOL];
+ target->c_cc[TARGET_VREPRINT] = host->c_cc[VREPRINT];
+ target->c_cc[TARGET_VDISCARD] = host->c_cc[VDISCARD];
+ target->c_cc[TARGET_VWERASE] = host->c_cc[VWERASE];
+ target->c_cc[TARGET_VLNEXT] = host->c_cc[VLNEXT];
+ target->c_cc[TARGET_VEOL2] = host->c_cc[VEOL2];
+}
+
+static const StructEntry struct_termios_def = {
+ .convert = { host_to_target_termios, target_to_host_termios },
+ .size = { sizeof(struct target_termios), sizeof(struct host_termios) },
+ .align = { __alignof__(struct target_termios), __alignof__(struct host_termios) },
+};
+
+static bitmask_transtbl mmap_flags_tbl[] = {
+ { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED },
+ { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE },
+ { TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED },
+ { TARGET_MAP_ANONYMOUS, TARGET_MAP_ANONYMOUS, MAP_ANONYMOUS, MAP_ANONYMOUS },
+ { TARGET_MAP_GROWSDOWN, TARGET_MAP_GROWSDOWN, MAP_GROWSDOWN, MAP_GROWSDOWN },
+ { TARGET_MAP_DENYWRITE, TARGET_MAP_DENYWRITE, MAP_DENYWRITE, MAP_DENYWRITE },
+ { TARGET_MAP_EXECUTABLE, TARGET_MAP_EXECUTABLE, MAP_EXECUTABLE, MAP_EXECUTABLE },
+ { TARGET_MAP_LOCKED, TARGET_MAP_LOCKED, MAP_LOCKED, MAP_LOCKED },
+ { 0, 0, 0, 0 }
+};
+
+#if defined(TARGET_I386)
+
+/* NOTE: there is really one LDT for all the threads */
+static uint8_t *ldt_table;
+
+static abi_long read_ldt(abi_ulong ptr, unsigned long bytecount)
+{
+ int size;
+ void *p;
+
+ if (!ldt_table)
+ return 0;
+ size = TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE;
+ if (size > bytecount)
+ size = bytecount;
+ p = lock_user(VERIFY_WRITE, ptr, size, 0);
+ if (!p)
+ return -TARGET_EFAULT;
+ /* ??? Should this by byteswapped? */
+ memcpy(p, ldt_table, size);
+ unlock_user(p, ptr, size);
+ return size;
+}
+
+/* XXX: add locking support */
+static abi_long write_ldt(CPUX86State *env,
+ abi_ulong ptr, unsigned long bytecount, int oldmode)
+{
+ struct target_modify_ldt_ldt_s ldt_info;
+ struct target_modify_ldt_ldt_s *target_ldt_info;
+ int seg_32bit, contents, read_exec_only, limit_in_pages;
+ int seg_not_present, useable, lm;
+ uint32_t *lp, entry_1, entry_2;
+
+ if (bytecount != sizeof(ldt_info))
+ return -TARGET_EINVAL;
+ if (!lock_user_struct(VERIFY_READ, target_ldt_info, ptr, 1))
+ return -TARGET_EFAULT;
+ ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
+ ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
+ ldt_info.limit = tswap32(target_ldt_info->limit);
+ ldt_info.flags = tswap32(target_ldt_info->flags);
+ unlock_user_struct(target_ldt_info, ptr, 0);
+
+ if (ldt_info.entry_number >= TARGET_LDT_ENTRIES)
+ return -TARGET_EINVAL;
+ seg_32bit = ldt_info.flags & 1;
+ contents = (ldt_info.flags >> 1) & 3;
+ read_exec_only = (ldt_info.flags >> 3) & 1;
+ limit_in_pages = (ldt_info.flags >> 4) & 1;
+ seg_not_present = (ldt_info.flags >> 5) & 1;
+ useable = (ldt_info.flags >> 6) & 1;
+#ifdef TARGET_ABI32
+ lm = 0;
+#else
+ lm = (ldt_info.flags >> 7) & 1;
+#endif
+ if (contents == 3) {
+ if (oldmode)
+ return -TARGET_EINVAL;
+ if (seg_not_present == 0)
+ return -TARGET_EINVAL;
+ }
+ /* allocate the LDT */
+ if (!ldt_table) {
+ env->ldt.base = target_mmap(0,
+ TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ if (env->ldt.base == -1)
+ return -TARGET_ENOMEM;
+ memset(g2h(env->ldt.base), 0,
+ TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE);
+ env->ldt.limit = 0xffff;
+ ldt_table = g2h(env->ldt.base);
+ }
+
+ /* NOTE: same code as Linux kernel */
+ /* Allow LDTs to be cleared by the user. */
+ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+ if (oldmode ||
+ (contents == 0 &&
+ read_exec_only == 1 &&
+ seg_32bit == 0 &&
+ limit_in_pages == 0 &&
+ seg_not_present == 1 &&
+ useable == 0 )) {
+ entry_1 = 0;
+ entry_2 = 0;
+ goto install;
+ }
+ }
+
+ entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+ (ldt_info.limit & 0x0ffff);
+ entry_2 = (ldt_info.base_addr & 0xff000000) |
+ ((ldt_info.base_addr & 0x00ff0000) >> 16) |
+ (ldt_info.limit & 0xf0000) |
+ ((read_exec_only ^ 1) << 9) |
+ (contents << 10) |
+ ((seg_not_present ^ 1) << 15) |
+ (seg_32bit << 22) |
+ (limit_in_pages << 23) |
+ (lm << 21) |
+ 0x7000;
+ if (!oldmode)
+ entry_2 |= (useable << 20);
+
+ /* Install the new entry ... */
+install:
+ lp = (uint32_t *)(ldt_table + (ldt_info.entry_number << 3));
+ lp[0] = tswap32(entry_1);
+ lp[1] = tswap32(entry_2);
+ return 0;
+}
+
+/* specific and weird i386 syscalls */
+static abi_long do_modify_ldt(CPUX86State *env, int func, abi_ulong ptr,
+ unsigned long bytecount)
+{
+ abi_long ret;
+
+ switch (func) {
+ case 0:
+ ret = read_ldt(ptr, bytecount);
+ break;
+ case 1:
+ ret = write_ldt(env, ptr, bytecount, 1);
+ break;
+ case 0x11:
+ ret = write_ldt(env, ptr, bytecount, 0);
+ break;
+ default:
+ ret = -TARGET_ENOSYS;
+ break;
+ }
+ return ret;
+}
+
+#if defined(TARGET_I386) && defined(TARGET_ABI32)
+static abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr)
+{
+ uint64_t *gdt_table = g2h(env->gdt.base);
+ struct target_modify_ldt_ldt_s ldt_info;
+ struct target_modify_ldt_ldt_s *target_ldt_info;
+ int seg_32bit, contents, read_exec_only, limit_in_pages;
+ int seg_not_present, useable, lm;
+ uint32_t *lp, entry_1, entry_2;
+ int i;
+
+ lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
+ if (!target_ldt_info)
+ return -TARGET_EFAULT;
+ ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
+ ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
+ ldt_info.limit = tswap32(target_ldt_info->limit);
+ ldt_info.flags = tswap32(target_ldt_info->flags);
+ if (ldt_info.entry_number == -1) {
+ for (i=TARGET_GDT_ENTRY_TLS_MIN; i<=TARGET_GDT_ENTRY_TLS_MAX; i++) {
+ if (gdt_table[i] == 0) {
+ ldt_info.entry_number = i;
+ target_ldt_info->entry_number = tswap32(i);
+ break;
+ }
+ }
+ }
+ unlock_user_struct(target_ldt_info, ptr, 1);
+
+ if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN ||
+ ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX)
+ return -TARGET_EINVAL;
+ seg_32bit = ldt_info.flags & 1;
+ contents = (ldt_info.flags >> 1) & 3;
+ read_exec_only = (ldt_info.flags >> 3) & 1;
+ limit_in_pages = (ldt_info.flags >> 4) & 1;
+ seg_not_present = (ldt_info.flags >> 5) & 1;
+ useable = (ldt_info.flags >> 6) & 1;
+#ifdef TARGET_ABI32
+ lm = 0;
+#else
+ lm = (ldt_info.flags >> 7) & 1;
+#endif
+
+ if (contents == 3) {
+ if (seg_not_present == 0)
+ return -TARGET_EINVAL;
+ }
+
+ /* NOTE: same code as Linux kernel */
+ /* Allow LDTs to be cleared by the user. */
+ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+ if ((contents == 0 &&
+ read_exec_only == 1 &&
+ seg_32bit == 0 &&
+ limit_in_pages == 0 &&
+ seg_not_present == 1 &&
+ useable == 0 )) {
+ entry_1 = 0;
+ entry_2 = 0;
+ goto install;
+ }
+ }
+
+ entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+ (ldt_info.limit & 0x0ffff);
+ entry_2 = (ldt_info.base_addr & 0xff000000) |
+ ((ldt_info.base_addr & 0x00ff0000) >> 16) |
+ (ldt_info.limit & 0xf0000) |
+ ((read_exec_only ^ 1) << 9) |
+ (contents << 10) |
+ ((seg_not_present ^ 1) << 15) |
+ (seg_32bit << 22) |
+ (limit_in_pages << 23) |
+ (useable << 20) |
+ (lm << 21) |
+ 0x7000;
+
+ /* Install the new entry ... */
+install:
+ lp = (uint32_t *)(gdt_table + ldt_info.entry_number);
+ lp[0] = tswap32(entry_1);
+ lp[1] = tswap32(entry_2);
+ return 0;
+}
+
+static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
+{
+ struct target_modify_ldt_ldt_s *target_ldt_info;
+ uint64_t *gdt_table = g2h(env->gdt.base);
+ uint32_t base_addr, limit, flags;
+ int seg_32bit, contents, read_exec_only, limit_in_pages, idx;
+ int seg_not_present, useable, lm;
+ uint32_t *lp, entry_1, entry_2;
+
+ lock_user_struct(VERIFY_WRITE, target_ldt_info, ptr, 1);
+ if (!target_ldt_info)
+ return -TARGET_EFAULT;
+ idx = tswap32(target_ldt_info->entry_number);
+ if (idx < TARGET_GDT_ENTRY_TLS_MIN ||
+ idx > TARGET_GDT_ENTRY_TLS_MAX) {
+ unlock_user_struct(target_ldt_info, ptr, 1);
+ return -TARGET_EINVAL;
+ }
+ lp = (uint32_t *)(gdt_table + idx);
+ entry_1 = tswap32(lp[0]);
+ entry_2 = tswap32(lp[1]);
+
+ read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
+ contents = (entry_2 >> 10) & 3;
+ seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
+ seg_32bit = (entry_2 >> 22) & 1;
+ limit_in_pages = (entry_2 >> 23) & 1;
+ useable = (entry_2 >> 20) & 1;
+#ifdef TARGET_ABI32
+ lm = 0;
+#else
+ lm = (entry_2 >> 21) & 1;
+#endif
+ flags = (seg_32bit << 0) | (contents << 1) |
+ (read_exec_only << 3) | (limit_in_pages << 4) |
+ (seg_not_present << 5) | (useable << 6) | (lm << 7);
+ limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000);
+ base_addr = (entry_1 >> 16) |
+ (entry_2 & 0xff000000) |
+ ((entry_2 & 0xff) << 16);
+ target_ldt_info->base_addr = tswapl(base_addr);
+ target_ldt_info->limit = tswap32(limit);
+ target_ldt_info->flags = tswap32(flags);
+ unlock_user_struct(target_ldt_info, ptr, 1);
+ return 0;
+}
+#endif /* TARGET_I386 && TARGET_ABI32 */
+
+#ifndef TARGET_ABI32
+static abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr)
+{
+ abi_long ret;
+ abi_ulong val;
+ int idx;
+
+ switch(code) {
+ case TARGET_ARCH_SET_GS:
+ case TARGET_ARCH_SET_FS:
+ if (code == TARGET_ARCH_SET_GS)
+ idx = R_GS;
+ else
+ idx = R_FS;
+ cpu_x86_load_seg(env, idx, 0);
+ env->segs[idx].base = addr;
+ break;
+ case TARGET_ARCH_GET_GS:
+ case TARGET_ARCH_GET_FS:
+ if (code == TARGET_ARCH_GET_GS)
+ idx = R_GS;
+ else
+ idx = R_FS;
+ val = env->segs[idx].base;
+ if (put_user(val, addr, abi_ulong))
+ return -TARGET_EFAULT;
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ break;
+ }
+ return 0;
+}
+#endif
+
+#endif /* defined(TARGET_I386) */
+
+#if defined(CONFIG_USE_NPTL)
+
+#define NEW_STACK_SIZE PTHREAD_STACK_MIN
+
+static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER;
+typedef struct {
+ CPUState *env;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ pthread_t thread;
+ uint32_t tid;
+ abi_ulong child_tidptr;
+ abi_ulong parent_tidptr;
+ sigset_t sigmask;
+} new_thread_info;
+
+static void *clone_func(void *arg)
+{
+ new_thread_info *info = arg;
+ CPUState *env;
+ TaskState *ts;
+
+ env = info->env;
+ thread_env = env;
+ ts = (TaskState *)thread_env->opaque;
+ info->tid = gettid();
+ env->host_tid = info->tid;
+ task_settid(ts);
+ if (info->child_tidptr)
+ put_user_u32(info->tid, info->child_tidptr);
+ if (info->parent_tidptr)
+ put_user_u32(info->tid, info->parent_tidptr);
+ /* Enable signals. */
+ sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
+ /* Signal to the parent that we're ready. */
+ pthread_mutex_lock(&info->mutex);
+ pthread_cond_broadcast(&info->cond);
+ pthread_mutex_unlock(&info->mutex);
+ /* Wait until the parent has finshed initializing the tls state. */
+ pthread_mutex_lock(&clone_lock);
+ pthread_mutex_unlock(&clone_lock);
+ cpu_loop(env);
+ /* never exits */
+ return NULL;
+}
+#else
+/* this stack is the equivalent of the kernel stack associated with a
+ thread/process */
+#define NEW_STACK_SIZE 8192
+
+static int clone_func(void *arg)
+{
+ CPUState *env = arg;
+ cpu_loop(env);
+ /* never exits */
+ return 0;
+}
+#endif
+
+/* do_fork() Must return host values and target errnos (unlike most
+ do_*() functions). */
+static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
+ abi_ulong parent_tidptr, target_ulong newtls,
+ abi_ulong child_tidptr)
+{
+ int ret;
+ TaskState *ts;
+ CPUState *new_env;
+#if defined(CONFIG_USE_NPTL)
+ unsigned int nptl_flags;
+ sigset_t sigmask;
+#else
+ uint8_t *new_stack;
+#endif
+
+ /* Emulate vfork() with fork() */
+ if (flags & CLONE_VFORK)
+ flags &= ~(CLONE_VFORK | CLONE_VM);
+
+ if (flags & CLONE_VM) {
+ TaskState *parent_ts = (TaskState *)env->opaque;
+#if defined(CONFIG_USE_NPTL)
+ new_thread_info info;
+ pthread_attr_t attr;
+#endif
+ ts = qemu_mallocz(sizeof(TaskState));
+ init_task_state(ts);
+ /* we create a new CPU instance. */
+ new_env = cpu_copy(env);
+#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
+ cpu_reset(new_env);
+#endif
+ /* Init regs that differ from the parent. */
+ cpu_clone_regs(new_env, newsp);
+ new_env->opaque = ts;
+ ts->bprm = parent_ts->bprm;
+ ts->info = parent_ts->info;
+#if defined(CONFIG_USE_NPTL)
+ nptl_flags = flags;
+ flags &= ~CLONE_NPTL_FLAGS2;
+
+ if (nptl_flags & CLONE_CHILD_CLEARTID) {
+ ts->child_tidptr = child_tidptr;
+ }
+
+ if (nptl_flags & CLONE_SETTLS)
+ cpu_set_tls (new_env, newtls);
+
+ /* Grab a mutex so that thread setup appears atomic. */
+ pthread_mutex_lock(&clone_lock);
+
+ memset(&info, 0, sizeof(info));
+ pthread_mutex_init(&info.mutex, NULL);
+ pthread_mutex_lock(&info.mutex);
+ pthread_cond_init(&info.cond, NULL);
+ info.env = new_env;
+ if (nptl_flags & CLONE_CHILD_SETTID)
+ info.child_tidptr = child_tidptr;
+ if (nptl_flags & CLONE_PARENT_SETTID)
+ info.parent_tidptr = parent_tidptr;
+
+ ret = pthread_attr_init(&attr);
+ ret = pthread_attr_setstacksize(&attr, NEW_STACK_SIZE);
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ /* It is not safe to deliver signals until the child has finished
+ initializing, so temporarily block all signals. */
+ sigfillset(&sigmask);
+ sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
+
+ ret = pthread_create(&info.thread, &attr, clone_func, &info);
+ /* TODO: Free new CPU state if thread creation failed. */
+
+ sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
+ pthread_attr_destroy(&attr);
+ if (ret == 0) {
+ /* Wait for the child to initialize. */
+ pthread_cond_wait(&info.cond, &info.mutex);
+ ret = info.tid;
+ if (flags & CLONE_PARENT_SETTID)
+ put_user_u32(ret, parent_tidptr);
+ } else {
+ ret = -1;
+ }
+ pthread_mutex_unlock(&info.mutex);
+ pthread_cond_destroy(&info.cond);
+ pthread_mutex_destroy(&info.mutex);
+ pthread_mutex_unlock(&clone_lock);
+#else
+ if (flags & CLONE_NPTL_FLAGS2)
+ return -EINVAL;
+ /* This is probably going to die very quickly, but do it anyway. */
+ new_stack = qemu_mallocz (NEW_STACK_SIZE);
+#ifdef __ia64__
+ ret = __clone2(clone_func, new_stack, NEW_STACK_SIZE, flags, new_env);
+#else
+ ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+#endif
+#endif
+ } else {
+ /* if no CLONE_VM, we consider it is a fork */
+ if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0)
+ return -EINVAL;
+ fork_start();
+ ret = fork();
+ if (ret == 0) {
+ /* Child Process. */
+ cpu_clone_regs(env, newsp);
+ fork_end(1);
+#if defined(CONFIG_USE_NPTL)
+ /* There is a race condition here. The parent process could
+ theoretically read the TID in the child process before the child
+ tid is set. This would require using either ptrace
+ (not implemented) or having *_tidptr to point at a shared memory
+ mapping. We can't repeat the spinlock hack used above because
+ the child process gets its own copy of the lock. */
+ if (flags & CLONE_CHILD_SETTID)
+ put_user_u32(gettid(), child_tidptr);
+ if (flags & CLONE_PARENT_SETTID)
+ put_user_u32(gettid(), parent_tidptr);
+ ts = (TaskState *)env->opaque;
+ if (flags & CLONE_SETTLS)
+ cpu_set_tls (env, newtls);
+ if (flags & CLONE_CHILD_CLEARTID)
+ ts->child_tidptr = child_tidptr;
+#endif
+ } else {
+ fork_end(0);
+ }
+ }
+ return ret;
+}
+
+/* warning : doesn't handle linux specific flags... */
+static int target_to_host_fcntl_cmd(int cmd)
+{
+ switch(cmd) {
+ case TARGET_F_DUPFD:
+ case TARGET_F_GETFD:
+ case TARGET_F_SETFD:
+ case TARGET_F_GETFL:
+ case TARGET_F_SETFL:
+ return cmd;
+ case TARGET_F_GETLK:
+ return F_GETLK;
+ case TARGET_F_SETLK:
+ return F_SETLK;
+ case TARGET_F_SETLKW:
+ return F_SETLKW;
+ case TARGET_F_GETOWN:
+ return F_GETOWN;
+ case TARGET_F_SETOWN:
+ return F_SETOWN;
+ case TARGET_F_GETSIG:
+ return F_GETSIG;
+ case TARGET_F_SETSIG:
+ return F_SETSIG;
+#if TARGET_ABI_BITS == 32
+ case TARGET_F_GETLK64:
+ return F_GETLK64;
+ case TARGET_F_SETLK64:
+ return F_SETLK64;
+ case TARGET_F_SETLKW64:
+ return F_SETLKW64;
+#endif
+ case TARGET_F_SETLEASE:
+ return F_SETLEASE;
+ case TARGET_F_GETLEASE:
+ return F_GETLEASE;
+#ifdef F_DUPFD_CLOEXEC
+ case TARGET_F_DUPFD_CLOEXEC:
+ return F_DUPFD_CLOEXEC;
+#endif
+ case TARGET_F_NOTIFY:
+ return F_NOTIFY;
+ default:
+ return -TARGET_EINVAL;
+ }
+ return -TARGET_EINVAL;
+}
+
+static abi_long do_fcntl(int fd, int cmd, abi_ulong arg)
+{
+ struct flock fl;
+ struct target_flock *target_fl;
+ struct flock64 fl64;
+ struct target_flock64 *target_fl64;
+ abi_long ret;
+ int host_cmd = target_to_host_fcntl_cmd(cmd);
+
+ if (host_cmd == -TARGET_EINVAL)
+ return host_cmd;
+
+ switch(cmd) {
+ case TARGET_F_GETLK:
+ if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1))
+ return -TARGET_EFAULT;
+ fl.l_type = tswap16(target_fl->l_type);
+ fl.l_whence = tswap16(target_fl->l_whence);
+ fl.l_start = tswapl(target_fl->l_start);
+ fl.l_len = tswapl(target_fl->l_len);
+ fl.l_pid = tswap32(target_fl->l_pid);
+ unlock_user_struct(target_fl, arg, 0);
+ ret = get_errno(fcntl(fd, host_cmd, &fl));
+ if (ret == 0) {
+ if (!lock_user_struct(VERIFY_WRITE, target_fl, arg, 0))
+ return -TARGET_EFAULT;
+ target_fl->l_type = tswap16(fl.l_type);
+ target_fl->l_whence = tswap16(fl.l_whence);
+ target_fl->l_start = tswapl(fl.l_start);
+ target_fl->l_len = tswapl(fl.l_len);
+ target_fl->l_pid = tswap32(fl.l_pid);
+ unlock_user_struct(target_fl, arg, 1);
+ }
+ break;
+
+ case TARGET_F_SETLK:
+ case TARGET_F_SETLKW:
+ if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1))
+ return -TARGET_EFAULT;
+ fl.l_type = tswap16(target_fl->l_type);
+ fl.l_whence = tswap16(target_fl->l_whence);
+ fl.l_start = tswapl(target_fl->l_start);
+ fl.l_len = tswapl(target_fl->l_len);
+ fl.l_pid = tswap32(target_fl->l_pid);
+ unlock_user_struct(target_fl, arg, 0);
+ ret = get_errno(fcntl(fd, host_cmd, &fl));
+ break;
+
+ case TARGET_F_GETLK64:
+ if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1))
+ return -TARGET_EFAULT;
+ fl64.l_type = tswap16(target_fl64->l_type) >> 1;
+ fl64.l_whence = tswap16(target_fl64->l_whence);
+ fl64.l_start = tswapl(target_fl64->l_start);
+ fl64.l_len = tswapl(target_fl64->l_len);
+ fl64.l_pid = tswap32(target_fl64->l_pid);
+ unlock_user_struct(target_fl64, arg, 0);
+ ret = get_errno(fcntl(fd, host_cmd, &fl64));
+ if (ret == 0) {
+ if (!lock_user_struct(VERIFY_WRITE, target_fl64, arg, 0))
+ return -TARGET_EFAULT;
+ target_fl64->l_type = tswap16(fl64.l_type) >> 1;
+ target_fl64->l_whence = tswap16(fl64.l_whence);
+ target_fl64->l_start = tswapl(fl64.l_start);
+ target_fl64->l_len = tswapl(fl64.l_len);
+ target_fl64->l_pid = tswap32(fl64.l_pid);
+ unlock_user_struct(target_fl64, arg, 1);
+ }
+ break;
+ case TARGET_F_SETLK64:
+ case TARGET_F_SETLKW64:
+ if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1))
+ return -TARGET_EFAULT;
+ fl64.l_type = tswap16(target_fl64->l_type) >> 1;
+ fl64.l_whence = tswap16(target_fl64->l_whence);
+ fl64.l_start = tswapl(target_fl64->l_start);
+ fl64.l_len = tswapl(target_fl64->l_len);
+ fl64.l_pid = tswap32(target_fl64->l_pid);
+ unlock_user_struct(target_fl64, arg, 0);
+ ret = get_errno(fcntl(fd, host_cmd, &fl64));
+ break;
+
+ case TARGET_F_GETFL:
+ ret = get_errno(fcntl(fd, host_cmd, arg));
+ if (ret >= 0) {
+ ret = host_to_target_bitmask(ret, fcntl_flags_tbl);
+ }
+ break;
+
+ case TARGET_F_SETFL:
+ ret = get_errno(fcntl(fd, host_cmd, target_to_host_bitmask(arg, fcntl_flags_tbl)));
+ break;
+
+ case TARGET_F_SETOWN:
+ case TARGET_F_GETOWN:
+ case TARGET_F_SETSIG:
+ case TARGET_F_GETSIG:
+ case TARGET_F_SETLEASE:
+ case TARGET_F_GETLEASE:
+ ret = get_errno(fcntl(fd, host_cmd, arg));
+ break;
+
+ default:
+ ret = get_errno(fcntl(fd, cmd, arg));
+ break;
+ }
+ return ret;
+}
+
+#ifdef USE_UID16
+
+static inline int high2lowuid(int uid)
+{
+ if (uid > 65535)
+ return 65534;
+ else
+ return uid;
+}
+
+static inline int high2lowgid(int gid)
+{
+ if (gid > 65535)
+ return 65534;
+ else
+ return gid;
+}
+
+static inline int low2highuid(int uid)
+{
+ if ((int16_t)uid == -1)
+ return -1;
+ else
+ return uid;
+}
+
+static inline int low2highgid(int gid)
+{
+ if ((int16_t)gid == -1)
+ return -1;
+ else
+ return gid;
+}
+
+#endif /* USE_UID16 */
+
+void syscall_init(void)
+{
+ IOCTLEntry *ie;
+ const argtype *arg_type;
+ int size;
+ int i;
+
+#define STRUCT(name, ...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def);
+#define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def);
+#include "syscall_types.h"
+#undef STRUCT
+#undef STRUCT_SPECIAL
+
+ /* we patch the ioctl size if necessary. We rely on the fact that
+ no ioctl has all the bits at '1' in the size field */
+ ie = ioctl_entries;
+ while (ie->target_cmd != 0) {
+ if (((ie->target_cmd >> TARGET_IOC_SIZESHIFT) & TARGET_IOC_SIZEMASK) ==
+ TARGET_IOC_SIZEMASK) {
+ arg_type = ie->arg_type;
+ if (arg_type[0] != TYPE_PTR) {
+ fprintf(stderr, "cannot patch size for ioctl 0x%x\n",
+ ie->target_cmd);
+ exit(1);
+ }
+ arg_type++;
+ size = thunk_type_size(arg_type, 0);
+ ie->target_cmd = (ie->target_cmd &
+ ~(TARGET_IOC_SIZEMASK << TARGET_IOC_SIZESHIFT)) |
+ (size << TARGET_IOC_SIZESHIFT);
+ }
+
+ /* Build target_to_host_errno_table[] table from
+ * host_to_target_errno_table[]. */
+ for (i=0; i < ERRNO_TABLE_SIZE; i++)
+ target_to_host_errno_table[host_to_target_errno_table[i]] = i;
+
+ /* automatic consistency check if same arch */
+#if (defined(__i386__) && defined(TARGET_I386) && defined(TARGET_ABI32)) || \
+ (defined(__x86_64__) && defined(TARGET_X86_64))
+ if (unlikely(ie->target_cmd != ie->host_cmd)) {
+ fprintf(stderr, "ERROR: ioctl(%s): target=0x%x host=0x%x\n",
+ ie->name, ie->target_cmd, ie->host_cmd);
+ }
+#endif
+ ie++;
+ }
+}
+
+#if TARGET_ABI_BITS == 32
+static inline uint64_t target_offset64(uint32_t word0, uint32_t word1)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ return ((uint64_t)word0 << 32) | word1;
+#else
+ return ((uint64_t)word1 << 32) | word0;
+#endif
+}
+#else /* TARGET_ABI_BITS == 32 */
+static inline uint64_t target_offset64(uint64_t word0, uint64_t word1)
+{
+ return word0;
+}
+#endif /* TARGET_ABI_BITS != 32 */
+
+#ifdef TARGET_NR_truncate64
+static inline abi_long target_truncate64(void *cpu_env, const char *arg1,
+ abi_long arg2,
+ abi_long arg3,
+ abi_long arg4)
+{
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ {
+ arg2 = arg3;
+ arg3 = arg4;
+ }
+#endif
+ return get_errno(truncate64(arg1, target_offset64(arg2, arg3)));
+}
+#endif
+
+#ifdef TARGET_NR_ftruncate64
+static inline abi_long target_ftruncate64(void *cpu_env, abi_long arg1,
+ abi_long arg2,
+ abi_long arg3,
+ abi_long arg4)
+{
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ {
+ arg2 = arg3;
+ arg3 = arg4;
+ }
+#endif
+ return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3)));
+}
+#endif
+
+static inline abi_long target_to_host_timespec(struct timespec *host_ts,
+ abi_ulong target_addr)
+{
+ struct target_timespec *target_ts;
+
+ if (!lock_user_struct(VERIFY_READ, target_ts, target_addr, 1))
+ return -TARGET_EFAULT;
+ host_ts->tv_sec = tswapl(target_ts->tv_sec);
+ host_ts->tv_nsec = tswapl(target_ts->tv_nsec);
+ unlock_user_struct(target_ts, target_addr, 0);
+ return 0;
+}
+
+static inline abi_long host_to_target_timespec(abi_ulong target_addr,
+ struct timespec *host_ts)
+{
+ struct target_timespec *target_ts;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_ts, target_addr, 0))
+ return -TARGET_EFAULT;
+ target_ts->tv_sec = tswapl(host_ts->tv_sec);
+ target_ts->tv_nsec = tswapl(host_ts->tv_nsec);
+ unlock_user_struct(target_ts, target_addr, 1);
+ return 0;
+}
+
+#if defined(TARGET_NR_stat64) || defined(TARGET_NR_newfstatat)
+static inline abi_long host_to_target_stat64(void *cpu_env,
+ abi_ulong target_addr,
+ struct stat *host_st)
+{
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ struct target_eabi_stat64 *target_st;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0))
+ return -TARGET_EFAULT;
+ memset(target_st, 0, sizeof(struct target_eabi_stat64));
+ __put_user(host_st->st_dev, &target_st->st_dev);
+ __put_user(host_st->st_ino, &target_st->st_ino);
+#ifdef TARGET_STAT64_HAS_BROKEN_ST_INO
+ __put_user(host_st->st_ino, &target_st->__st_ino);
+#endif
+ __put_user(host_st->st_mode, &target_st->st_mode);
+ __put_user(host_st->st_nlink, &target_st->st_nlink);
+ __put_user(host_st->st_uid, &target_st->st_uid);
+ __put_user(host_st->st_gid, &target_st->st_gid);
+ __put_user(host_st->st_rdev, &target_st->st_rdev);
+ __put_user(host_st->st_size, &target_st->st_size);
+ __put_user(host_st->st_blksize, &target_st->st_blksize);
+ __put_user(host_st->st_blocks, &target_st->st_blocks);
+ __put_user(host_st->st_atime, &target_st->target_st_atime);
+ __put_user(host_st->st_mtime, &target_st->target_st_mtime);
+ __put_user(host_st->st_ctime, &target_st->target_st_ctime);
+ unlock_user_struct(target_st, target_addr, 1);
+ } else
+#endif
+ {
+#if TARGET_ABI_BITS == 64 && !defined(TARGET_ALPHA)
+ struct target_stat *target_st;
+#else
+ struct target_stat64 *target_st;
+#endif
+
+ if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0))
+ return -TARGET_EFAULT;
+ memset(target_st, 0, sizeof(*target_st));
+ __put_user(host_st->st_dev, &target_st->st_dev);
+ __put_user(host_st->st_ino, &target_st->st_ino);
+#ifdef TARGET_STAT64_HAS_BROKEN_ST_INO
+ __put_user(host_st->st_ino, &target_st->__st_ino);
+#endif
+ __put_user(host_st->st_mode, &target_st->st_mode);
+ __put_user(host_st->st_nlink, &target_st->st_nlink);
+ __put_user(host_st->st_uid, &target_st->st_uid);
+ __put_user(host_st->st_gid, &target_st->st_gid);
+ __put_user(host_st->st_rdev, &target_st->st_rdev);
+ /* XXX: better use of kernel struct */
+ __put_user(host_st->st_size, &target_st->st_size);
+ __put_user(host_st->st_blksize, &target_st->st_blksize);
+ __put_user(host_st->st_blocks, &target_st->st_blocks);
+ __put_user(host_st->st_atime, &target_st->target_st_atime);
+ __put_user(host_st->st_mtime, &target_st->target_st_mtime);
+ __put_user(host_st->st_ctime, &target_st->target_st_ctime);
+ unlock_user_struct(target_st, target_addr, 1);
+ }
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_USE_NPTL)
+/* ??? Using host futex calls even when target atomic operations
+ are not really atomic probably breaks things. However implementing
+ futexes locally would make futexes shared between multiple processes
+ tricky. However they're probably useless because guest atomic
+ operations won't work either. */
+static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout,
+ target_ulong uaddr2, int val3)
+{
+ struct timespec ts, *pts;
+ int base_op;
+
+ /* ??? We assume FUTEX_* constants are the same on both host
+ and target. */
+#ifdef FUTEX_CMD_MASK
+ base_op = op & FUTEX_CMD_MASK;
+#else
+ base_op = op;
+#endif
+ switch (base_op) {
+ case FUTEX_WAIT:
+ if (timeout) {
+ pts = &ts;
+ target_to_host_timespec(pts, timeout);
+ } else {
+ pts = NULL;
+ }
+ return get_errno(sys_futex(g2h(uaddr), op, tswap32(val),
+ pts, NULL, 0));
+ case FUTEX_WAKE:
+ return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0));
+ case FUTEX_FD:
+ return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0));
+ case FUTEX_REQUEUE:
+ case FUTEX_CMP_REQUEUE:
+ case FUTEX_WAKE_OP:
+ /* For FUTEX_REQUEUE, FUTEX_CMP_REQUEUE, and FUTEX_WAKE_OP, the
+ TIMEOUT parameter is interpreted as a uint32_t by the kernel.
+ But the prototype takes a `struct timespec *'; insert casts
+ to satisfy the compiler. We do not need to tswap TIMEOUT
+ since it's not compared to guest memory. */
+ pts = (struct timespec *)(uintptr_t) timeout;
+ return get_errno(sys_futex(g2h(uaddr), op, val, pts,
+ g2h(uaddr2),
+ (base_op == FUTEX_CMP_REQUEUE
+ ? tswap32(val3)
+ : val3)));
+ default:
+ return -TARGET_ENOSYS;
+ }
+}
+#endif
+
+/* Map host to target signal numbers for the wait family of syscalls.
+ Assume all other status bits are the same. */
+static int host_to_target_waitstatus(int status)
+{
+ if (WIFSIGNALED(status)) {
+ return host_to_target_signal(WTERMSIG(status)) | (status & ~0x7f);
+ }
+ if (WIFSTOPPED(status)) {
+ return (host_to_target_signal(WSTOPSIG(status)) << 8)
+ | (status & 0xff);
+ }
+ return status;
+}
+
+int get_osversion(void)
+{
+ static int osversion;
+ struct new_utsname buf;
+ const char *s;
+ int i, n, tmp;
+ if (osversion)
+ return osversion;
+ if (qemu_uname_release && *qemu_uname_release) {
+ s = qemu_uname_release;
+ } else {
+ if (sys_uname(&buf))
+ return 0;
+ s = buf.release;
+ }
+ tmp = 0;
+ for (i = 0; i < 3; i++) {
+ n = 0;
+ while (*s >= '0' && *s <= '9') {
+ n *= 10;
+ n += *s - '0';
+ s++;
+ }
+ tmp = (tmp << 8) + n;
+ if (*s == '.')
+ s++;
+ }
+ osversion = tmp;
+ return osversion;
+}
+
+/* do_syscall() should always have a single exit point at the end so
+ that actions, such as logging of syscall results, can be performed.
+ All errnos that do_syscall() returns must be -TARGET_<errcode>. */
+abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
+ abi_long arg2, abi_long arg3, abi_long arg4,
+ abi_long arg5, abi_long arg6)
+{
+ abi_long ret;
+ struct stat st;
+ struct statfs stfs;
+ void *p;
+
+#ifdef DEBUG
+ gemu_log("syscall %d", num);
+#endif
+ if(do_strace)
+ print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+
+ switch(num) {
+ case TARGET_NR_exit:
+#ifdef CONFIG_USE_NPTL
+ /* In old applications this may be used to implement _exit(2).
+ However in threaded applictions it is used for thread termination,
+ and _exit_group is used for application termination.
+ Do thread termination if we have more then one thread. */
+ /* FIXME: This probably breaks if a signal arrives. We should probably
+ be disabling signals. */
+ if (first_cpu->next_cpu) {
+ TaskState *ts;
+ CPUState **lastp;
+ CPUState *p;
+
+ cpu_list_lock();
+ lastp = &first_cpu;
+ p = first_cpu;
+ while (p && p != (CPUState *)cpu_env) {
+ lastp = &p->next_cpu;
+ p = p->next_cpu;
+ }
+ /* If we didn't find the CPU for this thread then something is
+ horribly wrong. */
+ if (!p)
+ abort();
+ /* Remove the CPU from the list. */
+ *lastp = p->next_cpu;
+ cpu_list_unlock();
+ ts = ((CPUState *)cpu_env)->opaque;
+ if (ts->child_tidptr) {
+ put_user_u32(0, ts->child_tidptr);
+ sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX,
+ NULL, NULL, 0);
+ }
+ thread_env = NULL;
+ qemu_free(cpu_env);
+ qemu_free(ts);
+ pthread_exit(NULL);
+ }
+#endif
+#ifdef TARGET_GPROF
+ _mcleanup();
+#endif
+ gdb_exit(cpu_env, arg1);
+ _exit(arg1);
+ ret = 0; /* avoid warning */
+ break;
+ case TARGET_NR_read:
+ if (arg3 == 0)
+ ret = 0;
+ else {
+ if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
+ goto efault;
+ ret = get_errno(read(arg1, p, arg3));
+ unlock_user(p, arg2, ret);
+ }
+ break;
+ case TARGET_NR_write:
+ if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
+ goto efault;
+ ret = get_errno(write(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+ case TARGET_NR_open:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(open(path(p),
+ target_to_host_bitmask(arg2, fcntl_flags_tbl),
+ arg3));
+ unlock_user(p, arg1, 0);
+ break;
+#if defined(TARGET_NR_openat) && defined(__NR_openat)
+ case TARGET_NR_openat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_openat(arg1,
+ path(p),
+ target_to_host_bitmask(arg3, fcntl_flags_tbl),
+ arg4));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_close:
+ ret = get_errno(close(arg1));
+ break;
+ case TARGET_NR_brk:
+ ret = do_brk(arg1);
+ break;
+ case TARGET_NR_fork:
+ ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0));
+ break;
+#ifdef TARGET_NR_waitpid
+ case TARGET_NR_waitpid:
+ {
+ int status;
+ ret = get_errno(waitpid(arg1, &status, arg3));
+ if (!is_error(ret) && arg2
+ && put_user_s32(host_to_target_waitstatus(status), arg2))
+ goto efault;
+ }
+ break;
+#endif
+#ifdef TARGET_NR_waitid
+ case TARGET_NR_waitid:
+ {
+ siginfo_t info;
+ info.si_pid = 0;
+ ret = get_errno(waitid(arg1, arg2, &info, arg4));
+ if (!is_error(ret) && arg3 && info.si_pid != 0) {
+ if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0)))
+ goto efault;
+ host_to_target_siginfo(p, &info);
+ unlock_user(p, arg3, sizeof(target_siginfo_t));
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_creat /* not on alpha */
+ case TARGET_NR_creat:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(creat(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+ case TARGET_NR_link:
+ {
+ void * p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(link(p, p2));
+ unlock_user(p2, arg2, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#if defined(TARGET_NR_linkat) && defined(__NR_linkat)
+ case TARGET_NR_linkat:
+ {
+ void * p2 = NULL;
+ if (!arg2 || !arg4)
+ goto efault;
+ p = lock_user_string(arg2);
+ p2 = lock_user_string(arg4);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(sys_linkat(arg1, p, arg3, p2, arg5));
+ unlock_user(p, arg2, 0);
+ unlock_user(p2, arg4, 0);
+ }
+ break;
+#endif
+ case TARGET_NR_unlink:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(unlink(p));
+ unlock_user(p, arg1, 0);
+ break;
+#if defined(TARGET_NR_unlinkat) && defined(__NR_unlinkat)
+ case TARGET_NR_unlinkat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_unlinkat(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_execve:
+ {
+ char **argp, **envp;
+ int argc, envc;
+ abi_ulong gp;
+ abi_ulong guest_argp;
+ abi_ulong guest_envp;
+ abi_ulong addr;
+ char **q;
+
+ argc = 0;
+ guest_argp = arg2;
+ for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) {
+ if (get_user_ual(addr, gp))
+ goto efault;
+ if (!addr)
+ break;
+ argc++;
+ }
+ envc = 0;
+ guest_envp = arg3;
+ for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) {
+ if (get_user_ual(addr, gp))
+ goto efault;
+ if (!addr)
+ break;
+ envc++;
+ }
+
+ argp = alloca((argc + 1) * sizeof(void *));
+ envp = alloca((envc + 1) * sizeof(void *));
+
+ for (gp = guest_argp, q = argp; gp;
+ gp += sizeof(abi_ulong), q++) {
+ if (get_user_ual(addr, gp))
+ goto execve_efault;
+ if (!addr)
+ break;
+ if (!(*q = lock_user_string(addr)))
+ goto execve_efault;
+ }
+ *q = NULL;
+
+ for (gp = guest_envp, q = envp; gp;
+ gp += sizeof(abi_ulong), q++) {
+ if (get_user_ual(addr, gp))
+ goto execve_efault;
+ if (!addr)
+ break;
+ if (!(*q = lock_user_string(addr)))
+ goto execve_efault;
+ }
+ *q = NULL;
+
+ if (!(p = lock_user_string(arg1)))
+ goto execve_efault;
+ ret = get_errno(execve(p, argp, envp));
+ unlock_user(p, arg1, 0);
+
+ goto execve_end;
+
+ execve_efault:
+ ret = -TARGET_EFAULT;
+
+ execve_end:
+ for (gp = guest_argp, q = argp; *q;
+ gp += sizeof(abi_ulong), q++) {
+ if (get_user_ual(addr, gp)
+ || !addr)
+ break;
+ unlock_user(*q, addr, 0);
+ }
+ for (gp = guest_envp, q = envp; *q;
+ gp += sizeof(abi_ulong), q++) {
+ if (get_user_ual(addr, gp)
+ || !addr)
+ break;
+ unlock_user(*q, addr, 0);
+ }
+ }
+ break;
+ case TARGET_NR_chdir:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(chdir(p));
+ unlock_user(p, arg1, 0);
+ break;
+#ifdef TARGET_NR_time
+ case TARGET_NR_time:
+ {
+ time_t host_time;
+ ret = get_errno(time(&host_time));
+ if (!is_error(ret)
+ && arg1
+ && put_user_sal(host_time, arg1))
+ goto efault;
+ }
+ break;
+#endif
+ case TARGET_NR_mknod:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(mknod(p, arg2, arg3));
+ unlock_user(p, arg1, 0);
+ break;
+#if defined(TARGET_NR_mknodat) && defined(__NR_mknodat)
+ case TARGET_NR_mknodat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_mknodat(arg1, p, arg3, arg4));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_chmod:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(chmod(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#ifdef TARGET_NR_break
+ case TARGET_NR_break:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_oldstat
+ case TARGET_NR_oldstat:
+ goto unimplemented;
+#endif
+ case TARGET_NR_lseek:
+ ret = get_errno(lseek(arg1, arg2, arg3));
+ break;
+#if defined(TARGET_NR_getxpid) && defined(TARGET_ALPHA)
+ /* Alpha specific */
+ case TARGET_NR_getxpid:
+ ((CPUAlphaState *)cpu_env)->ir[IR_A4] = getppid();
+ ret = get_errno(getpid());
+ break;
+#endif
+#ifdef TARGET_NR_getpid
+ case TARGET_NR_getpid:
+ ret = get_errno(getpid());
+ break;
+#endif
+ case TARGET_NR_mount:
+ {
+ /* need to look at the data field */
+ void *p2, *p3;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ p3 = lock_user_string(arg3);
+ if (!p || !p2 || !p3)
+ ret = -TARGET_EFAULT;
+ else {
+ /* FIXME - arg5 should be locked, but it isn't clear how to
+ * do that since it's not guaranteed to be a NULL-terminated
+ * string.
+ */
+ if ( ! arg5 )
+ ret = get_errno(mount(p, p2, p3, (unsigned long)arg4, NULL));
+ else
+ ret = get_errno(mount(p, p2, p3, (unsigned long)arg4, g2h(arg5)));
+ }
+ unlock_user(p, arg1, 0);
+ unlock_user(p2, arg2, 0);
+ unlock_user(p3, arg3, 0);
+ break;
+ }
+#ifdef TARGET_NR_umount
+ case TARGET_NR_umount:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(umount(p));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_stime /* not on alpha */
+ case TARGET_NR_stime:
+ {
+ time_t host_time;
+ if (get_user_sal(host_time, arg1))
+ goto efault;
+ ret = get_errno(stime(&host_time));
+ }
+ break;
+#endif
+ case TARGET_NR_ptrace:
+ goto unimplemented;
+#ifdef TARGET_NR_alarm /* not on alpha */
+ case TARGET_NR_alarm:
+ ret = alarm(arg1);
+ break;
+#endif
+#ifdef TARGET_NR_oldfstat
+ case TARGET_NR_oldfstat:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_pause /* not on alpha */
+ case TARGET_NR_pause:
+ ret = get_errno(pause());
+ break;
+#endif
+#ifdef TARGET_NR_utime
+ case TARGET_NR_utime:
+ {
+ struct utimbuf tbuf, *host_tbuf;
+ struct target_utimbuf *target_tbuf;
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, target_tbuf, arg2, 1))
+ goto efault;
+ tbuf.actime = tswapl(target_tbuf->actime);
+ tbuf.modtime = tswapl(target_tbuf->modtime);
+ unlock_user_struct(target_tbuf, arg2, 0);
+ host_tbuf = &tbuf;
+ } else {
+ host_tbuf = NULL;
+ }
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(utime(p, host_tbuf));
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#endif
+ case TARGET_NR_utimes:
+ {
+ struct timeval *tvp, tv[2];
+ if (arg2) {
+ if (copy_from_user_timeval(&tv[0], arg2)
+ || copy_from_user_timeval(&tv[1],
+ arg2 + sizeof(struct target_timeval)))
+ goto efault;
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(utimes(p, tvp));
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#if defined(TARGET_NR_futimesat) && defined(__NR_futimesat)
+ case TARGET_NR_futimesat:
+ {
+ struct timeval *tvp, tv[2];
+ if (arg3) {
+ if (copy_from_user_timeval(&tv[0], arg3)
+ || copy_from_user_timeval(&tv[1],
+ arg3 + sizeof(struct target_timeval)))
+ goto efault;
+ tvp = tv;
+ } else {
+ tvp = NULL;
+ }
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_futimesat(arg1, path(p), tvp));
+ unlock_user(p, arg2, 0);
+ }
+ break;
+#endif
+#ifdef TARGET_NR_stty
+ case TARGET_NR_stty:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_gtty
+ case TARGET_NR_gtty:
+ goto unimplemented;
+#endif
+ case TARGET_NR_access:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(access(path(p), arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#if defined(TARGET_NR_faccessat) && defined(__NR_faccessat)
+ case TARGET_NR_faccessat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_faccessat(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+#ifdef TARGET_NR_nice /* not on alpha */
+ case TARGET_NR_nice:
+ ret = get_errno(nice(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_ftime
+ case TARGET_NR_ftime:
+ goto unimplemented;
+#endif
+ case TARGET_NR_sync:
+ sync();
+ ret = 0;
+ break;
+ case TARGET_NR_kill:
+ ret = get_errno(kill(arg1, target_to_host_signal(arg2)));
+ break;
+ case TARGET_NR_rename:
+ {
+ void *p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(rename(p, p2));
+ unlock_user(p2, arg2, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#if defined(TARGET_NR_renameat) && defined(__NR_renameat)
+ case TARGET_NR_renameat:
+ {
+ void *p2;
+ p = lock_user_string(arg2);
+ p2 = lock_user_string(arg4);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(sys_renameat(arg1, p, arg3, p2));
+ unlock_user(p2, arg4, 0);
+ unlock_user(p, arg2, 0);
+ }
+ break;
+#endif
+ case TARGET_NR_mkdir:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(mkdir(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#if defined(TARGET_NR_mkdirat) && defined(__NR_mkdirat)
+ case TARGET_NR_mkdirat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_mkdirat(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_rmdir:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(rmdir(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_dup:
+ ret = get_errno(dup(arg1));
+ break;
+ case TARGET_NR_pipe:
+ ret = do_pipe(cpu_env, arg1, 0, 0);
+ break;
+#ifdef TARGET_NR_pipe2
+ case TARGET_NR_pipe2:
+ ret = do_pipe(cpu_env, arg1, arg2, 1);
+ break;
+#endif
+ case TARGET_NR_times:
+ {
+ struct target_tms *tmsp;
+ struct tms tms;
+ ret = get_errno(times(&tms));
+ if (arg1) {
+ tmsp = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_tms), 0);
+ if (!tmsp)
+ goto efault;
+ tmsp->tms_utime = tswapl(host_to_target_clock_t(tms.tms_utime));
+ tmsp->tms_stime = tswapl(host_to_target_clock_t(tms.tms_stime));
+ tmsp->tms_cutime = tswapl(host_to_target_clock_t(tms.tms_cutime));
+ tmsp->tms_cstime = tswapl(host_to_target_clock_t(tms.tms_cstime));
+ }
+ if (!is_error(ret))
+ ret = host_to_target_clock_t(ret);
+ }
+ break;
+#ifdef TARGET_NR_prof
+ case TARGET_NR_prof:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_signal
+ case TARGET_NR_signal:
+ goto unimplemented;
+#endif
+ case TARGET_NR_acct:
+ if (arg1 == 0) {
+ ret = get_errno(acct(NULL));
+ } else {
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(acct(path(p)));
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#ifdef TARGET_NR_umount2 /* not on alpha */
+ case TARGET_NR_umount2:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(umount2(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_lock
+ case TARGET_NR_lock:
+ goto unimplemented;
+#endif
+ case TARGET_NR_ioctl:
+ ret = do_ioctl(arg1, arg2, arg3);
+ break;
+ case TARGET_NR_fcntl:
+ ret = do_fcntl(arg1, arg2, arg3);
+ break;
+#ifdef TARGET_NR_mpx
+ case TARGET_NR_mpx:
+ goto unimplemented;
+#endif
+ case TARGET_NR_setpgid:
+ ret = get_errno(setpgid(arg1, arg2));
+ break;
+#ifdef TARGET_NR_ulimit
+ case TARGET_NR_ulimit:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_oldolduname
+ case TARGET_NR_oldolduname:
+ goto unimplemented;
+#endif
+ case TARGET_NR_umask:
+ ret = get_errno(umask(arg1));
+ break;
+ case TARGET_NR_chroot:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(chroot(p));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_ustat:
+ goto unimplemented;
+ case TARGET_NR_dup2:
+ ret = get_errno(dup2(arg1, arg2));
+ break;
+#if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3)
+ case TARGET_NR_dup3:
+ ret = get_errno(dup3(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_getppid /* not on alpha */
+ case TARGET_NR_getppid:
+ ret = get_errno(getppid());
+ break;
+#endif
+ case TARGET_NR_getpgrp:
+ ret = get_errno(getpgrp());
+ break;
+ case TARGET_NR_setsid:
+ ret = get_errno(setsid());
+ break;
+#ifdef TARGET_NR_sigaction
+ case TARGET_NR_sigaction:
+ {
+#if defined(TARGET_ALPHA)
+ struct target_sigaction act, oact, *pact = 0;
+ struct target_old_sigaction *old_act;
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1))
+ goto efault;
+ act._sa_handler = old_act->_sa_handler;
+ target_siginitset(&act.sa_mask, old_act->sa_mask);
+ act.sa_flags = old_act->sa_flags;
+ act.sa_restorer = 0;
+ unlock_user_struct(old_act, arg2, 0);
+ pact = &act;
+ }
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+ if (!is_error(ret) && arg3) {
+ if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0))
+ goto efault;
+ old_act->_sa_handler = oact._sa_handler;
+ old_act->sa_mask = oact.sa_mask.sig[0];
+ old_act->sa_flags = oact.sa_flags;
+ unlock_user_struct(old_act, arg3, 1);
+ }
+#elif defined(TARGET_MIPS)
+ struct target_sigaction act, oact, *pact, *old_act;
+
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1))
+ goto efault;
+ act._sa_handler = old_act->_sa_handler;
+ target_siginitset(&act.sa_mask, old_act->sa_mask.sig[0]);
+ act.sa_flags = old_act->sa_flags;
+ unlock_user_struct(old_act, arg2, 0);
+ pact = &act;
+ } else {
+ pact = NULL;
+ }
+
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+
+ if (!is_error(ret) && arg3) {
+ if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0))
+ goto efault;
+ old_act->_sa_handler = oact._sa_handler;
+ old_act->sa_flags = oact.sa_flags;
+ old_act->sa_mask.sig[0] = oact.sa_mask.sig[0];
+ old_act->sa_mask.sig[1] = 0;
+ old_act->sa_mask.sig[2] = 0;
+ old_act->sa_mask.sig[3] = 0;
+ unlock_user_struct(old_act, arg3, 1);
+ }
+#else
+ struct target_old_sigaction *old_act;
+ struct target_sigaction act, oact, *pact;
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1))
+ goto efault;
+ act._sa_handler = old_act->_sa_handler;
+ target_siginitset(&act.sa_mask, old_act->sa_mask);
+ act.sa_flags = old_act->sa_flags;
+ act.sa_restorer = old_act->sa_restorer;
+ unlock_user_struct(old_act, arg2, 0);
+ pact = &act;
+ } else {
+ pact = NULL;
+ }
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+ if (!is_error(ret) && arg3) {
+ if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0))
+ goto efault;
+ old_act->_sa_handler = oact._sa_handler;
+ old_act->sa_mask = oact.sa_mask.sig[0];
+ old_act->sa_flags = oact.sa_flags;
+ old_act->sa_restorer = oact.sa_restorer;
+ unlock_user_struct(old_act, arg3, 1);
+ }
+#endif
+ }
+ break;
+#endif
+ case TARGET_NR_rt_sigaction:
+ {
+#if defined(TARGET_ALPHA)
+ struct target_sigaction act, oact, *pact = 0;
+ struct target_rt_sigaction *rt_act;
+ /* ??? arg4 == sizeof(sigset_t). */
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, rt_act, arg2, 1))
+ goto efault;
+ act._sa_handler = rt_act->_sa_handler;
+ act.sa_mask = rt_act->sa_mask;
+ act.sa_flags = rt_act->sa_flags;
+ act.sa_restorer = arg5;
+ unlock_user_struct(rt_act, arg2, 0);
+ pact = &act;
+ }
+ ret = get_errno(do_sigaction(arg1, pact, &oact));
+ if (!is_error(ret) && arg3) {
+ if (!lock_user_struct(VERIFY_WRITE, rt_act, arg3, 0))
+ goto efault;
+ rt_act->_sa_handler = oact._sa_handler;
+ rt_act->sa_mask = oact.sa_mask;
+ rt_act->sa_flags = oact.sa_flags;
+ unlock_user_struct(rt_act, arg3, 1);
+ }
+#else
+ struct target_sigaction *act;
+ struct target_sigaction *oact;
+
+ if (arg2) {
+ if (!lock_user_struct(VERIFY_READ, act, arg2, 1))
+ goto efault;
+ } else
+ act = NULL;
+ if (arg3) {
+ if (!lock_user_struct(VERIFY_WRITE, oact, arg3, 0)) {
+ ret = -TARGET_EFAULT;
+ goto rt_sigaction_fail;
+ }
+ } else
+ oact = NULL;
+ ret = get_errno(do_sigaction(arg1, act, oact));
+ rt_sigaction_fail:
+ if (act)
+ unlock_user_struct(act, arg2, 0);
+ if (oact)
+ unlock_user_struct(oact, arg3, 1);
+#endif
+ }
+ break;
+#ifdef TARGET_NR_sgetmask /* not on alpha */
+ case TARGET_NR_sgetmask:
+ {
+ sigset_t cur_set;
+ abi_ulong target_set;
+ sigprocmask(0, NULL, &cur_set);
+ host_to_target_old_sigset(&target_set, &cur_set);
+ ret = target_set;
+ }
+ break;
+#endif
+#ifdef TARGET_NR_ssetmask /* not on alpha */
+ case TARGET_NR_ssetmask:
+ {
+ sigset_t set, oset, cur_set;
+ abi_ulong target_set = arg1;
+ sigprocmask(0, NULL, &cur_set);
+ target_to_host_old_sigset(&set, &target_set);
+ sigorset(&set, &set, &cur_set);
+ sigprocmask(SIG_SETMASK, &set, &oset);
+ host_to_target_old_sigset(&target_set, &oset);
+ ret = target_set;
+ }
+ break;
+#endif
+#ifdef TARGET_NR_sigprocmask
+ case TARGET_NR_sigprocmask:
+ {
+#if defined(TARGET_ALPHA)
+ sigset_t set, oldset;
+ abi_ulong mask;
+ int how;
+
+ switch (arg1) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ mask = arg2;
+ target_to_host_old_sigset(&set, &mask);
+
+ ret = get_errno(sigprocmask(how, &set, &oldset));
+
+ if (!is_error(ret)) {
+ host_to_target_old_sigset(&mask, &oldset);
+ ret = mask;
+ ((CPUAlphaState *)cpu_env)->[IR_V0] = 0; /* force no error */
+ }
+#else
+ sigset_t set, oldset, *set_ptr;
+ int how;
+
+ if (arg2) {
+ switch (arg1) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1)))
+ goto efault;
+ target_to_host_old_sigset(&set, p);
+ unlock_user(p, arg2, 0);
+ set_ptr = &set;
+ } else {
+ how = 0;
+ set_ptr = NULL;
+ }
+ ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+ if (!is_error(ret) && arg3) {
+ if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
+ goto efault;
+ host_to_target_old_sigset(p, &oldset);
+ unlock_user(p, arg3, sizeof(target_sigset_t));
+ }
+#endif
+ }
+ break;
+#endif
+ case TARGET_NR_rt_sigprocmask:
+ {
+ int how = arg1;
+ sigset_t set, oldset, *set_ptr;
+
+ if (arg2) {
+ switch(how) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1)))
+ goto efault;
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg2, 0);
+ set_ptr = &set;
+ } else {
+ how = 0;
+ set_ptr = NULL;
+ }
+ ret = get_errno(sigprocmask(how, set_ptr, &oldset));
+ if (!is_error(ret) && arg3) {
+ if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
+ goto efault;
+ host_to_target_sigset(p, &oldset);
+ unlock_user(p, arg3, sizeof(target_sigset_t));
+ }
+ }
+ break;
+#ifdef TARGET_NR_sigpending
+ case TARGET_NR_sigpending:
+ {
+ sigset_t set;
+ ret = get_errno(sigpending(&set));
+ if (!is_error(ret)) {
+ if (!(p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0)))
+ goto efault;
+ host_to_target_old_sigset(p, &set);
+ unlock_user(p, arg1, sizeof(target_sigset_t));
+ }
+ }
+ break;
+#endif
+ case TARGET_NR_rt_sigpending:
+ {
+ sigset_t set;
+ ret = get_errno(sigpending(&set));
+ if (!is_error(ret)) {
+ if (!(p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0)))
+ goto efault;
+ host_to_target_sigset(p, &set);
+ unlock_user(p, arg1, sizeof(target_sigset_t));
+ }
+ }
+ break;
+#ifdef TARGET_NR_sigsuspend
+ case TARGET_NR_sigsuspend:
+ {
+ sigset_t set;
+#if defined(TARGET_ALPHA)
+ abi_ulong mask = arg1;
+ target_to_host_old_sigset(&set, &mask);
+#else
+ if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
+ goto efault;
+ target_to_host_old_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+#endif
+ ret = get_errno(sigsuspend(&set));
+ }
+ break;
+#endif
+ case TARGET_NR_rt_sigsuspend:
+ {
+ sigset_t set;
+ if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
+ goto efault;
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sigsuspend(&set));
+ }
+ break;
+ case TARGET_NR_rt_sigtimedwait:
+ {
+ sigset_t set;
+ struct timespec uts, *puts;
+ siginfo_t uinfo;
+
+ if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1)))
+ goto efault;
+ target_to_host_sigset(&set, p);
+ unlock_user(p, arg1, 0);
+ if (arg3) {
+ puts = &uts;
+ target_to_host_timespec(puts, arg3);
+ } else {
+ puts = NULL;
+ }
+ ret = get_errno(sigtimedwait(&set, &uinfo, puts));
+ if (!is_error(ret) && arg2) {
+ if (!(p = lock_user(VERIFY_WRITE, arg2, sizeof(target_siginfo_t), 0)))
+ goto efault;
+ host_to_target_siginfo(p, &uinfo);
+ unlock_user(p, arg2, sizeof(target_siginfo_t));
+ }
+ }
+ break;
+ case TARGET_NR_rt_sigqueueinfo:
+ {
+ siginfo_t uinfo;
+ if (!(p = lock_user(VERIFY_READ, arg3, sizeof(target_sigset_t), 1)))
+ goto efault;
+ target_to_host_siginfo(&uinfo, p);
+ unlock_user(p, arg1, 0);
+ ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo));
+ }
+ break;
+#ifdef TARGET_NR_sigreturn
+ case TARGET_NR_sigreturn:
+ /* NOTE: ret is eax, so not transcoding must be done */
+ ret = do_sigreturn(cpu_env);
+ break;
+#endif
+ case TARGET_NR_rt_sigreturn:
+ /* NOTE: ret is eax, so not transcoding must be done */
+ ret = do_rt_sigreturn(cpu_env);
+ break;
+ case TARGET_NR_sethostname:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(sethostname(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_setrlimit:
+ {
+ int resource = arg1;
+ struct target_rlimit *target_rlim;
+ struct rlimit rlim;
+ if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1))
+ goto efault;
+ rlim.rlim_cur = target_to_host_rlim(target_rlim->rlim_cur);
+ rlim.rlim_max = target_to_host_rlim(target_rlim->rlim_max);
+ unlock_user_struct(target_rlim, arg2, 0);
+ ret = get_errno(setrlimit(resource, &rlim));
+ }
+ break;
+ case TARGET_NR_getrlimit:
+ {
+ int resource = arg1;
+ struct target_rlimit *target_rlim;
+ struct rlimit rlim;
+
+ ret = get_errno(getrlimit(resource, &rlim));
+ if (!is_error(ret)) {
+ if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0))
+ goto efault;
+ target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur);
+ target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max);
+ unlock_user_struct(target_rlim, arg2, 1);
+ }
+ }
+ break;
+ case TARGET_NR_getrusage:
+ {
+ struct rusage rusage;
+ ret = get_errno(getrusage(arg1, &rusage));
+ if (!is_error(ret)) {
+ host_to_target_rusage(arg2, &rusage);
+ }
+ }
+ break;
+ case TARGET_NR_gettimeofday:
+ {
+ struct timeval tv;
+ ret = get_errno(gettimeofday(&tv, NULL));
+ if (!is_error(ret)) {
+ if (copy_to_user_timeval(arg1, &tv))
+ goto efault;
+ }
+ }
+ break;
+ case TARGET_NR_settimeofday:
+ {
+ struct timeval tv;
+ if (copy_from_user_timeval(&tv, arg1))
+ goto efault;
+ ret = get_errno(settimeofday(&tv, NULL));
+ }
+ break;
+#ifdef TARGET_NR_select
+ case TARGET_NR_select:
+ {
+ struct target_sel_arg_struct *sel;
+ abi_ulong inp, outp, exp, tvp;
+ long nsel;
+
+ if (!lock_user_struct(VERIFY_READ, sel, arg1, 1))
+ goto efault;
+ nsel = tswapl(sel->n);
+ inp = tswapl(sel->inp);
+ outp = tswapl(sel->outp);
+ exp = tswapl(sel->exp);
+ tvp = tswapl(sel->tvp);
+ unlock_user_struct(sel, arg1, 0);
+ ret = do_select(nsel, inp, outp, exp, tvp);
+ }
+ break;
+#endif
+#ifdef TARGET_NR_pselect6
+ case TARGET_NR_pselect6:
+ goto unimplemented_nowarn;
+#endif
+ case TARGET_NR_symlink:
+ {
+ void *p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg2);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(symlink(p, p2));
+ unlock_user(p2, arg2, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#if defined(TARGET_NR_symlinkat) && defined(__NR_symlinkat)
+ case TARGET_NR_symlinkat:
+ {
+ void *p2;
+ p = lock_user_string(arg1);
+ p2 = lock_user_string(arg3);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(sys_symlinkat(p, arg2, p2));
+ unlock_user(p2, arg3, 0);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#endif
+#ifdef TARGET_NR_oldlstat
+ case TARGET_NR_oldlstat:
+ goto unimplemented;
+#endif
+ case TARGET_NR_readlink:
+ {
+ void *p2, *temp;
+ p = lock_user_string(arg1);
+ p2 = lock_user(VERIFY_WRITE, arg2, arg3, 0);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else {
+ if (strncmp((const char *)p, "/proc/self/exe", 14) == 0) {
+ char real[PATH_MAX];
+ temp = realpath(exec_path,real);
+ ret = (temp==NULL) ? get_errno(-1) : strlen(real) ;
+ snprintf((char *)p2, arg3, "%s", real);
+ }
+ else
+ ret = get_errno(readlink(path(p), p2, arg3));
+ }
+ unlock_user(p2, arg2, ret);
+ unlock_user(p, arg1, 0);
+ }
+ break;
+#if defined(TARGET_NR_readlinkat) && defined(__NR_readlinkat)
+ case TARGET_NR_readlinkat:
+ {
+ void *p2;
+ p = lock_user_string(arg2);
+ p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0);
+ if (!p || !p2)
+ ret = -TARGET_EFAULT;
+ else
+ ret = get_errno(sys_readlinkat(arg1, path(p), p2, arg4));
+ unlock_user(p2, arg3, ret);
+ unlock_user(p, arg2, 0);
+ }
+ break;
+#endif
+#ifdef TARGET_NR_uselib
+ case TARGET_NR_uselib:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_swapon
+ case TARGET_NR_swapon:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(swapon(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+ case TARGET_NR_reboot:
+ goto unimplemented;
+#ifdef TARGET_NR_readdir
+ case TARGET_NR_readdir:
+ goto unimplemented;
+#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)
+ {
+ abi_ulong *v;
+ abi_ulong v1, v2, v3, v4, v5, v6;
+ if (!(v = lock_user(VERIFY_READ, arg1, 6 * sizeof(abi_ulong), 1)))
+ goto efault;
+ v1 = tswapl(v[0]);
+ v2 = tswapl(v[1]);
+ v3 = tswapl(v[2]);
+ v4 = tswapl(v[3]);
+ v5 = tswapl(v[4]);
+ v6 = tswapl(v[5]);
+ unlock_user(v, arg1, 0);
+ ret = get_errno(target_mmap(v1, v2, v3,
+ target_to_host_bitmask(v4, mmap_flags_tbl),
+ v5, v6));
+ }
+#else
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6));
+#endif
+ break;
+#endif
+#ifdef TARGET_NR_mmap2
+ case TARGET_NR_mmap2:
+#ifndef MMAP_SHIFT
+#define MMAP_SHIFT 12
+#endif
+ ret = get_errno(target_mmap(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6 << MMAP_SHIFT));
+ break;
+#endif
+ case TARGET_NR_munmap:
+ ret = get_errno(target_munmap(arg1, arg2));
+ break;
+ case TARGET_NR_mprotect:
+ {
+ TaskState *ts = ((CPUState *)cpu_env)->opaque;
+ /* Special hack to detect libc making the stack executable. */
+ if ((arg3 & PROT_GROWSDOWN)
+ && arg1 >= ts->info->stack_limit
+ && arg1 <= ts->info->start_stack) {
+ arg3 &= ~PROT_GROWSDOWN;
+ arg2 = arg2 + arg1 - ts->info->stack_limit;
+ arg1 = ts->info->stack_limit;
+ }
+ }
+ ret = get_errno(target_mprotect(arg1, arg2, arg3));
+ break;
+#ifdef TARGET_NR_mremap
+ case TARGET_NR_mremap:
+ ret = get_errno(target_mremap(arg1, arg2, arg3, arg4, arg5));
+ break;
+#endif
+ /* ??? msync/mlock/munlock are broken for softmmu. */
+#ifdef TARGET_NR_msync
+ case TARGET_NR_msync:
+ ret = get_errno(msync(g2h(arg1), arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_mlock
+ case TARGET_NR_mlock:
+ ret = get_errno(mlock(g2h(arg1), arg2));
+ break;
+#endif
+#ifdef TARGET_NR_munlock
+ case TARGET_NR_munlock:
+ ret = get_errno(munlock(g2h(arg1), arg2));
+ break;
+#endif
+#ifdef TARGET_NR_mlockall
+ case TARGET_NR_mlockall:
+ ret = get_errno(mlockall(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_munlockall
+ case TARGET_NR_munlockall:
+ ret = get_errno(munlockall());
+ break;
+#endif
+ case TARGET_NR_truncate:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(truncate(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_ftruncate:
+ ret = get_errno(ftruncate(arg1, arg2));
+ break;
+ case TARGET_NR_fchmod:
+ ret = get_errno(fchmod(arg1, arg2));
+ break;
+#if defined(TARGET_NR_fchmodat) && defined(__NR_fchmodat)
+ case TARGET_NR_fchmodat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_fchmodat(arg1, p, arg3));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_getpriority:
+ /* libc does special remapping of the return value of
+ * sys_getpriority() so it's just easiest to call
+ * sys_getpriority() directly rather than through libc. */
+ ret = get_errno(sys_getpriority(arg1, arg2));
+ break;
+ case TARGET_NR_setpriority:
+ ret = get_errno(setpriority(arg1, arg2, arg3));
+ break;
+#ifdef TARGET_NR_profil
+ case TARGET_NR_profil:
+ goto unimplemented;
+#endif
+ case TARGET_NR_statfs:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(statfs(path(p), &stfs));
+ unlock_user(p, arg1, 0);
+ convert_statfs:
+ if (!is_error(ret)) {
+ struct target_statfs *target_stfs;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_stfs, arg2, 0))
+ goto efault;
+ __put_user(stfs.f_type, &target_stfs->f_type);
+ __put_user(stfs.f_bsize, &target_stfs->f_bsize);
+ __put_user(stfs.f_blocks, &target_stfs->f_blocks);
+ __put_user(stfs.f_bfree, &target_stfs->f_bfree);
+ __put_user(stfs.f_bavail, &target_stfs->f_bavail);
+ __put_user(stfs.f_files, &target_stfs->f_files);
+ __put_user(stfs.f_ffree, &target_stfs->f_ffree);
+ __put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]);
+ __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]);
+ __put_user(stfs.f_namelen, &target_stfs->f_namelen);
+ unlock_user_struct(target_stfs, arg2, 1);
+ }
+ break;
+ case TARGET_NR_fstatfs:
+ ret = get_errno(fstatfs(arg1, &stfs));
+ goto convert_statfs;
+#ifdef TARGET_NR_statfs64
+ case TARGET_NR_statfs64:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(statfs(path(p), &stfs));
+ unlock_user(p, arg1, 0);
+ convert_statfs64:
+ if (!is_error(ret)) {
+ struct target_statfs64 *target_stfs;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_stfs, arg3, 0))
+ goto efault;
+ __put_user(stfs.f_type, &target_stfs->f_type);
+ __put_user(stfs.f_bsize, &target_stfs->f_bsize);
+ __put_user(stfs.f_blocks, &target_stfs->f_blocks);
+ __put_user(stfs.f_bfree, &target_stfs->f_bfree);
+ __put_user(stfs.f_bavail, &target_stfs->f_bavail);
+ __put_user(stfs.f_files, &target_stfs->f_files);
+ __put_user(stfs.f_ffree, &target_stfs->f_ffree);
+ __put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]);
+ __put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]);
+ __put_user(stfs.f_namelen, &target_stfs->f_namelen);
+ unlock_user_struct(target_stfs, arg3, 1);
+ }
+ break;
+ case TARGET_NR_fstatfs64:
+ ret = get_errno(fstatfs(arg1, &stfs));
+ goto convert_statfs64;
+#endif
+#ifdef TARGET_NR_ioperm
+ case TARGET_NR_ioperm:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_socketcall
+ case TARGET_NR_socketcall:
+ ret = do_socketcall(arg1, arg2);
+ break;
+#endif
+#ifdef TARGET_NR_accept
+ case TARGET_NR_accept:
+ ret = do_accept(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_bind
+ case TARGET_NR_bind:
+ ret = do_bind(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_connect
+ case TARGET_NR_connect:
+ ret = do_connect(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_getpeername
+ case TARGET_NR_getpeername:
+ ret = do_getpeername(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_getsockname
+ case TARGET_NR_getsockname:
+ ret = do_getsockname(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_getsockopt
+ case TARGET_NR_getsockopt:
+ ret = do_getsockopt(arg1, arg2, arg3, arg4, arg5);
+ break;
+#endif
+#ifdef TARGET_NR_listen
+ case TARGET_NR_listen:
+ ret = get_errno(listen(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_recv
+ case TARGET_NR_recv:
+ ret = do_recvfrom(arg1, arg2, arg3, arg4, 0, 0);
+ break;
+#endif
+#ifdef TARGET_NR_recvfrom
+ case TARGET_NR_recvfrom:
+ ret = do_recvfrom(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_recvmsg
+ case TARGET_NR_recvmsg:
+ ret = do_sendrecvmsg(arg1, arg2, arg3, 0);
+ break;
+#endif
+#ifdef TARGET_NR_send
+ case TARGET_NR_send:
+ ret = do_sendto(arg1, arg2, arg3, arg4, 0, 0);
+ break;
+#endif
+#ifdef TARGET_NR_sendmsg
+ case TARGET_NR_sendmsg:
+ ret = do_sendrecvmsg(arg1, arg2, arg3, 1);
+ break;
+#endif
+#ifdef TARGET_NR_sendto
+ case TARGET_NR_sendto:
+ ret = do_sendto(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_shutdown
+ case TARGET_NR_shutdown:
+ ret = get_errno(shutdown(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_socket
+ case TARGET_NR_socket:
+ ret = do_socket(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_socketpair
+ case TARGET_NR_socketpair:
+ ret = do_socketpair(arg1, arg2, arg3, arg4);
+ break;
+#endif
+#ifdef TARGET_NR_setsockopt
+ case TARGET_NR_setsockopt:
+ ret = do_setsockopt(arg1, arg2, arg3, arg4, (socklen_t) arg5);
+ break;
+#endif
+
+ case TARGET_NR_syslog:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_syslog((int)arg1, p, (int)arg3));
+ unlock_user(p, arg2, 0);
+ break;
+
+ case TARGET_NR_setitimer:
+ {
+ struct itimerval value, ovalue, *pvalue;
+
+ if (arg2) {
+ pvalue = &value;
+ if (copy_from_user_timeval(&pvalue->it_interval, arg2)
+ || copy_from_user_timeval(&pvalue->it_value,
+ arg2 + sizeof(struct target_timeval)))
+ goto efault;
+ } else {
+ pvalue = NULL;
+ }
+ ret = get_errno(setitimer(arg1, pvalue, &ovalue));
+ if (!is_error(ret) && arg3) {
+ if (copy_to_user_timeval(arg3,
+ &ovalue.it_interval)
+ || copy_to_user_timeval(arg3 + sizeof(struct target_timeval),
+ &ovalue.it_value))
+ goto efault;
+ }
+ }
+ break;
+ case TARGET_NR_getitimer:
+ {
+ struct itimerval value;
+
+ ret = get_errno(getitimer(arg1, &value));
+ if (!is_error(ret) && arg2) {
+ if (copy_to_user_timeval(arg2,
+ &value.it_interval)
+ || copy_to_user_timeval(arg2 + sizeof(struct target_timeval),
+ &value.it_value))
+ goto efault;
+ }
+ }
+ break;
+ case TARGET_NR_stat:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(stat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ goto do_stat;
+ case TARGET_NR_lstat:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(lstat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ goto do_stat;
+ case TARGET_NR_fstat:
+ {
+ ret = get_errno(fstat(arg1, &st));
+ do_stat:
+ if (!is_error(ret)) {
+ struct target_stat *target_st;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_st, arg2, 0))
+ goto efault;
+ memset(target_st, 0, sizeof(*target_st));
+ __put_user(st.st_dev, &target_st->st_dev);
+ __put_user(st.st_ino, &target_st->st_ino);
+ __put_user(st.st_mode, &target_st->st_mode);
+ __put_user(st.st_uid, &target_st->st_uid);
+ __put_user(st.st_gid, &target_st->st_gid);
+ __put_user(st.st_nlink, &target_st->st_nlink);
+ __put_user(st.st_rdev, &target_st->st_rdev);
+ __put_user(st.st_size, &target_st->st_size);
+ __put_user(st.st_blksize, &target_st->st_blksize);
+ __put_user(st.st_blocks, &target_st->st_blocks);
+ __put_user(st.st_atime, &target_st->target_st_atime);
+ __put_user(st.st_mtime, &target_st->target_st_mtime);
+ __put_user(st.st_ctime, &target_st->target_st_ctime);
+ unlock_user_struct(target_st, arg2, 1);
+ }
+ }
+ break;
+#ifdef TARGET_NR_olduname
+ case TARGET_NR_olduname:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_iopl
+ case TARGET_NR_iopl:
+ goto unimplemented;
+#endif
+ case TARGET_NR_vhangup:
+ ret = get_errno(vhangup());
+ break;
+#ifdef TARGET_NR_idle
+ case TARGET_NR_idle:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_syscall
+ case TARGET_NR_syscall:
+ ret = do_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
+ break;
+#endif
+ case TARGET_NR_wait4:
+ {
+ int status;
+ abi_long status_ptr = arg2;
+ struct rusage rusage, *rusage_ptr;
+ abi_ulong target_rusage = arg4;
+ if (target_rusage)
+ rusage_ptr = &rusage;
+ else
+ rusage_ptr = NULL;
+ ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr));
+ if (!is_error(ret)) {
+ if (status_ptr) {
+ status = host_to_target_waitstatus(status);
+ if (put_user_s32(status, status_ptr))
+ goto efault;
+ }
+ if (target_rusage)
+ host_to_target_rusage(target_rusage, &rusage);
+ }
+ }
+ break;
+#ifdef TARGET_NR_swapoff
+ case TARGET_NR_swapoff:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(swapoff(p));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+ case TARGET_NR_sysinfo:
+ {
+ struct target_sysinfo *target_value;
+ struct sysinfo value;
+ ret = get_errno(sysinfo(&value));
+ if (!is_error(ret) && arg1)
+ {
+ if (!lock_user_struct(VERIFY_WRITE, target_value, arg1, 0))
+ goto efault;
+ __put_user(value.uptime, &target_value->uptime);
+ __put_user(value.loads[0], &target_value->loads[0]);
+ __put_user(value.loads[1], &target_value->loads[1]);
+ __put_user(value.loads[2], &target_value->loads[2]);
+ __put_user(value.totalram, &target_value->totalram);
+ __put_user(value.freeram, &target_value->freeram);
+ __put_user(value.sharedram, &target_value->sharedram);
+ __put_user(value.bufferram, &target_value->bufferram);
+ __put_user(value.totalswap, &target_value->totalswap);
+ __put_user(value.freeswap, &target_value->freeswap);
+ __put_user(value.procs, &target_value->procs);
+ __put_user(value.totalhigh, &target_value->totalhigh);
+ __put_user(value.freehigh, &target_value->freehigh);
+ __put_user(value.mem_unit, &target_value->mem_unit);
+ unlock_user_struct(target_value, arg1, 1);
+ }
+ }
+ break;
+#ifdef TARGET_NR_ipc
+ case TARGET_NR_ipc:
+ ret = do_ipc(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#ifdef TARGET_NR_semget
+ case TARGET_NR_semget:
+ ret = get_errno(semget(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_semop
+ case TARGET_NR_semop:
+ ret = get_errno(do_semop(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_semctl
+ case TARGET_NR_semctl:
+ ret = do_semctl(arg1, arg2, arg3, (union target_semun)(abi_ulong)arg4);
+ break;
+#endif
+#ifdef TARGET_NR_msgctl
+ case TARGET_NR_msgctl:
+ ret = do_msgctl(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_msgget
+ case TARGET_NR_msgget:
+ ret = get_errno(msgget(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_msgrcv
+ case TARGET_NR_msgrcv:
+ ret = do_msgrcv(arg1, arg2, arg3, arg4, arg5);
+ break;
+#endif
+#ifdef TARGET_NR_msgsnd
+ case TARGET_NR_msgsnd:
+ ret = do_msgsnd(arg1, arg2, arg3, arg4);
+ break;
+#endif
+#ifdef TARGET_NR_shmget
+ case TARGET_NR_shmget:
+ ret = get_errno(shmget(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_shmctl
+ case TARGET_NR_shmctl:
+ ret = do_shmctl(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_shmat
+ case TARGET_NR_shmat:
+ ret = do_shmat(arg1, arg2, arg3);
+ break;
+#endif
+#ifdef TARGET_NR_shmdt
+ case TARGET_NR_shmdt:
+ ret = do_shmdt(arg1);
+ break;
+#endif
+ case TARGET_NR_fsync:
+ ret = get_errno(fsync(arg1));
+ break;
+ case TARGET_NR_clone:
+#if defined(TARGET_SH4) || defined(TARGET_ALPHA)
+ 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));
+#else
+ ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5));
+#endif
+ break;
+#ifdef __NR_exit_group
+ /* new thread calls */
+ case TARGET_NR_exit_group:
+#ifdef TARGET_GPROF
+ _mcleanup();
+#endif
+ gdb_exit(cpu_env, arg1);
+ ret = get_errno(exit_group(arg1));
+ break;
+#endif
+ case TARGET_NR_setdomainname:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(setdomainname(p, arg2));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_uname:
+ /* no need to transcode because we use the linux syscall */
+ {
+ struct new_utsname * buf;
+
+ if (!lock_user_struct(VERIFY_WRITE, buf, arg1, 0))
+ goto efault;
+ ret = get_errno(sys_uname(buf));
+ if (!is_error(ret)) {
+ /* Overrite the native machine name with whatever is being
+ emulated. */
+ strcpy (buf->machine, cpu_to_uname_machine(cpu_env));
+ /* Allow the user to override the reported release. */
+ if (qemu_uname_release && *qemu_uname_release)
+ strcpy (buf->release, qemu_uname_release);
+ }
+ unlock_user_struct(buf, arg1, 1);
+ }
+ break;
+#ifdef TARGET_I386
+ case TARGET_NR_modify_ldt:
+ ret = do_modify_ldt(cpu_env, arg1, arg2, arg3);
+ break;
+#if !defined(TARGET_X86_64)
+ case TARGET_NR_vm86old:
+ goto unimplemented;
+ case TARGET_NR_vm86:
+ ret = do_vm86(cpu_env, arg1, arg2);
+ break;
+#endif
+#endif
+ case TARGET_NR_adjtimex:
+ goto unimplemented;
+#ifdef TARGET_NR_create_module
+ case TARGET_NR_create_module:
+#endif
+ case TARGET_NR_init_module:
+ case TARGET_NR_delete_module:
+#ifdef TARGET_NR_get_kernel_syms
+ case TARGET_NR_get_kernel_syms:
+#endif
+ goto unimplemented;
+ case TARGET_NR_quotactl:
+ goto unimplemented;
+ case TARGET_NR_getpgid:
+ ret = get_errno(getpgid(arg1));
+ break;
+ case TARGET_NR_fchdir:
+ ret = get_errno(fchdir(arg1));
+ break;
+#ifdef TARGET_NR_bdflush /* not on x86_64 */
+ case TARGET_NR_bdflush:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_sysfs
+ case TARGET_NR_sysfs:
+ goto unimplemented;
+#endif
+ case TARGET_NR_personality:
+ ret = get_errno(personality(arg1));
+ break;
+#ifdef TARGET_NR_afs_syscall
+ case TARGET_NR_afs_syscall:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR__llseek /* Not on alpha */
+ case TARGET_NR__llseek:
+ {
+#if !defined(__NR_llseek)
+ ret = get_errno(lseek(arg1, ((uint64_t )arg2 << 32) | arg3, arg5));
+ if (put_user_s64(ret, arg4))
+ goto efault;
+#else
+ int64_t res;
+ ret = get_errno(_llseek(arg1, arg2, arg3, &res, arg5));
+ if (put_user_s64(res, arg4))
+ goto efault;
+#endif
+ }
+ break;
+#endif
+ case TARGET_NR_getdents:
+#if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64
+ {
+ struct target_dirent *target_dirp;
+ struct linux_dirent *dirp;
+ abi_long count = arg3;
+
+ dirp = malloc(count);
+ if (!dirp) {
+ ret = -TARGET_ENOMEM;
+ goto fail;
+ }
+
+ ret = get_errno(sys_getdents(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct linux_dirent *de;
+ struct target_dirent *tde;
+ int len = ret;
+ int reclen, treclen;
+ int count1, tnamelen;
+
+ count1 = 0;
+ de = dirp;
+ if (!(target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
+ goto efault;
+ tde = target_dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ treclen = reclen - (2 * (sizeof(long) - sizeof(abi_long)));
+ tde->d_reclen = tswap16(treclen);
+ tde->d_ino = tswapl(de->d_ino);
+ tde->d_off = tswapl(de->d_off);
+ tnamelen = treclen - (2 * sizeof(abi_long) + 2);
+ if (tnamelen > 256)
+ tnamelen = 256;
+ /* XXX: may not be correct */
+ pstrcpy(tde->d_name, tnamelen, de->d_name);
+ de = (struct linux_dirent *)((char *)de + reclen);
+ len -= reclen;
+ tde = (struct target_dirent *)((char *)tde + treclen);
+ count1 += treclen;
+ }
+ ret = count1;
+ unlock_user(target_dirp, arg2, ret);
+ }
+ free(dirp);
+ }
+#else
+ {
+ struct linux_dirent *dirp;
+ abi_long count = arg3;
+
+ if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
+ goto efault;
+ ret = get_errno(sys_getdents(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct linux_dirent *de;
+ int len = ret;
+ int reclen;
+ de = dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ if (reclen > len)
+ break;
+ de->d_reclen = tswap16(reclen);
+ tswapls(&de->d_ino);
+ tswapls(&de->d_off);
+ de = (struct linux_dirent *)((char *)de + reclen);
+ len -= reclen;
+ }
+ }
+ unlock_user(dirp, arg2, ret);
+ }
+#endif
+ break;
+#if defined(TARGET_NR_getdents64) && defined(__NR_getdents64)
+ case TARGET_NR_getdents64:
+ {
+ struct linux_dirent64 *dirp;
+ abi_long count = arg3;
+ if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0)))
+ goto efault;
+ ret = get_errno(sys_getdents64(arg1, dirp, count));
+ if (!is_error(ret)) {
+ struct linux_dirent64 *de;
+ int len = ret;
+ int reclen;
+ de = dirp;
+ while (len > 0) {
+ reclen = de->d_reclen;
+ if (reclen > len)
+ break;
+ de->d_reclen = tswap16(reclen);
+ tswap64s((uint64_t *)&de->d_ino);
+ tswap64s((uint64_t *)&de->d_off);
+ de = (struct linux_dirent64 *)((char *)de + reclen);
+ len -= reclen;
+ }
+ }
+ unlock_user(dirp, arg2, ret);
+ }
+ break;
+#endif /* TARGET_NR_getdents64 */
+#ifdef TARGET_NR__newselect
+ case TARGET_NR__newselect:
+ ret = do_select(arg1, arg2, arg3, arg4, arg5);
+ break;
+#endif
+#ifdef TARGET_NR_poll
+ case TARGET_NR_poll:
+ {
+ struct target_pollfd *target_pfd;
+ unsigned int nfds = arg2;
+ int timeout = arg3;
+ struct pollfd *pfd;
+ unsigned int i;
+
+ target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1);
+ if (!target_pfd)
+ goto efault;
+ pfd = alloca(sizeof(struct pollfd) * nfds);
+ for(i = 0; i < nfds; i++) {
+ pfd[i].fd = tswap32(target_pfd[i].fd);
+ pfd[i].events = tswap16(target_pfd[i].events);
+ }
+ ret = get_errno(poll(pfd, nfds, timeout));
+ if (!is_error(ret)) {
+ for(i = 0; i < nfds; i++) {
+ target_pfd[i].revents = tswap16(pfd[i].revents);
+ }
+ ret += nfds * (sizeof(struct target_pollfd)
+ - sizeof(struct pollfd));
+ }
+ unlock_user(target_pfd, arg1, ret);
+ }
+ break;
+#endif
+ case TARGET_NR_flock:
+ /* NOTE: the flock constant seems to be the same for every
+ Linux platform */
+ ret = get_errno(flock(arg1, arg2));
+ break;
+ case TARGET_NR_readv:
+ {
+ int count = arg3;
+ struct iovec *vec;
+
+ vec = alloca(count * sizeof(struct iovec));
+ if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0)
+ goto efault;
+ ret = get_errno(readv(arg1, vec, count));
+ unlock_iovec(vec, arg2, count, 1);
+ }
+ break;
+ case TARGET_NR_writev:
+ {
+ int count = arg3;
+ struct iovec *vec;
+
+ vec = alloca(count * sizeof(struct iovec));
+ if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
+ goto efault;
+ ret = get_errno(writev(arg1, vec, count));
+ unlock_iovec(vec, arg2, count, 0);
+ }
+ break;
+ case TARGET_NR_getsid:
+ ret = get_errno(getsid(arg1));
+ break;
+#if defined(TARGET_NR_fdatasync) /* Not on alpha (osf_datasync ?) */
+ case TARGET_NR_fdatasync:
+ ret = get_errno(fdatasync(arg1));
+ break;
+#endif
+ case TARGET_NR__sysctl:
+ /* We don't implement this, but ENOTDIR is always a safe
+ return value. */
+ ret = -TARGET_ENOTDIR;
+ break;
+ case TARGET_NR_sched_setparam:
+ {
+ struct sched_param *target_schp;
+ struct sched_param schp;
+
+ if (!lock_user_struct(VERIFY_READ, target_schp, arg2, 1))
+ goto efault;
+ schp.sched_priority = tswap32(target_schp->sched_priority);
+ unlock_user_struct(target_schp, arg2, 0);
+ ret = get_errno(sched_setparam(arg1, &schp));
+ }
+ break;
+ case TARGET_NR_sched_getparam:
+ {
+ struct sched_param *target_schp;
+ struct sched_param schp;
+ ret = get_errno(sched_getparam(arg1, &schp));
+ if (!is_error(ret)) {
+ if (!lock_user_struct(VERIFY_WRITE, target_schp, arg2, 0))
+ goto efault;
+ target_schp->sched_priority = tswap32(schp.sched_priority);
+ unlock_user_struct(target_schp, arg2, 1);
+ }
+ }
+ break;
+ case TARGET_NR_sched_setscheduler:
+ {
+ struct sched_param *target_schp;
+ struct sched_param schp;
+ if (!lock_user_struct(VERIFY_READ, target_schp, arg3, 1))
+ goto efault;
+ schp.sched_priority = tswap32(target_schp->sched_priority);
+ unlock_user_struct(target_schp, arg3, 0);
+ ret = get_errno(sched_setscheduler(arg1, arg2, &schp));
+ }
+ break;
+ case TARGET_NR_sched_getscheduler:
+ ret = get_errno(sched_getscheduler(arg1));
+ break;
+ case TARGET_NR_sched_yield:
+ ret = get_errno(sched_yield());
+ break;
+ case TARGET_NR_sched_get_priority_max:
+ ret = get_errno(sched_get_priority_max(arg1));
+ break;
+ case TARGET_NR_sched_get_priority_min:
+ ret = get_errno(sched_get_priority_min(arg1));
+ break;
+ case TARGET_NR_sched_rr_get_interval:
+ {
+ struct timespec ts;
+ ret = get_errno(sched_rr_get_interval(arg1, &ts));
+ if (!is_error(ret)) {
+ host_to_target_timespec(arg2, &ts);
+ }
+ }
+ break;
+ case TARGET_NR_nanosleep:
+ {
+ struct timespec req, rem;
+ target_to_host_timespec(&req, arg1);
+ ret = get_errno(nanosleep(&req, &rem));
+ if (is_error(ret) && arg2) {
+ host_to_target_timespec(arg2, &rem);
+ }
+ }
+ break;
+#ifdef TARGET_NR_query_module
+ case TARGET_NR_query_module:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_nfsservctl
+ case TARGET_NR_nfsservctl:
+ goto unimplemented;
+#endif
+ case TARGET_NR_prctl:
+ switch (arg1)
+ {
+ case PR_GET_PDEATHSIG:
+ {
+ int deathsig;
+ ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5));
+ if (!is_error(ret) && arg2
+ && put_user_ual(deathsig, arg2))
+ goto efault;
+ }
+ break;
+ default:
+ ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5));
+ break;
+ }
+ break;
+#ifdef TARGET_NR_arch_prctl
+ case TARGET_NR_arch_prctl:
+#if defined(TARGET_I386) && !defined(TARGET_ABI32)
+ ret = do_arch_prctl(cpu_env, arg1, arg2);
+ break;
+#else
+ goto unimplemented;
+#endif
+#endif
+#ifdef TARGET_NR_pread
+ case TARGET_NR_pread:
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ arg4 = arg5;
+#endif
+ if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
+ goto efault;
+ ret = get_errno(pread(arg1, p, arg3, arg4));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_NR_pwrite:
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ arg4 = arg5;
+#endif
+ if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
+ goto efault;
+ ret = get_errno(pwrite(arg1, p, arg3, arg4));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+#ifdef TARGET_NR_pread64
+ case TARGET_NR_pread64:
+ if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
+ goto efault;
+ ret = get_errno(pread64(arg1, p, arg3, target_offset64(arg4, arg5)));
+ unlock_user(p, arg2, ret);
+ break;
+ case TARGET_NR_pwrite64:
+ if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
+ goto efault;
+ ret = get_errno(pwrite64(arg1, p, arg3, target_offset64(arg4, arg5)));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+ case TARGET_NR_getcwd:
+ if (!(p = lock_user(VERIFY_WRITE, arg1, arg2, 0)))
+ goto efault;
+ ret = get_errno(sys_getcwd1(p, arg2));
+ unlock_user(p, arg1, ret);
+ break;
+ case TARGET_NR_capget:
+ goto unimplemented;
+ case TARGET_NR_capset:
+ goto unimplemented;
+ 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)
+ ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUState *)cpu_env));
+ break;
+#else
+ goto unimplemented;
+#endif
+ case TARGET_NR_sendfile:
+ goto unimplemented;
+#ifdef TARGET_NR_getpmsg
+ case TARGET_NR_getpmsg:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_putpmsg
+ case TARGET_NR_putpmsg:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_vfork
+ case TARGET_NR_vfork:
+ ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD,
+ 0, 0, 0, 0));
+ break;
+#endif
+#ifdef TARGET_NR_ugetrlimit
+ case TARGET_NR_ugetrlimit:
+ {
+ struct rlimit rlim;
+ ret = get_errno(getrlimit(arg1, &rlim));
+ if (!is_error(ret)) {
+ struct target_rlimit *target_rlim;
+ if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0))
+ goto efault;
+ target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur);
+ target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max);
+ unlock_user_struct(target_rlim, arg2, 1);
+ }
+ break;
+ }
+#endif
+#ifdef TARGET_NR_truncate64
+ case TARGET_NR_truncate64:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = target_truncate64(cpu_env, p, arg2, arg3, arg4);
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_ftruncate64
+ case TARGET_NR_ftruncate64:
+ ret = target_ftruncate64(cpu_env, arg1, arg2, arg3, arg4);
+ break;
+#endif
+#ifdef TARGET_NR_stat64
+ case TARGET_NR_stat64:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(stat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ if (!is_error(ret))
+ ret = host_to_target_stat64(cpu_env, arg2, &st);
+ break;
+#endif
+#ifdef TARGET_NR_lstat64
+ case TARGET_NR_lstat64:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(lstat(path(p), &st));
+ unlock_user(p, arg1, 0);
+ if (!is_error(ret))
+ ret = host_to_target_stat64(cpu_env, arg2, &st);
+ break;
+#endif
+#ifdef TARGET_NR_fstat64
+ case TARGET_NR_fstat64:
+ ret = get_errno(fstat(arg1, &st));
+ if (!is_error(ret))
+ ret = host_to_target_stat64(cpu_env, arg2, &st);
+ break;
+#endif
+#if (defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)) && \
+ (defined(__NR_fstatat64) || defined(__NR_newfstatat))
+#ifdef TARGET_NR_fstatat64
+ case TARGET_NR_fstatat64:
+#endif
+#ifdef TARGET_NR_newfstatat
+ case TARGET_NR_newfstatat:
+#endif
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+#ifdef __NR_fstatat64
+ ret = get_errno(sys_fstatat64(arg1, path(p), &st, arg4));
+#else
+ ret = get_errno(sys_newfstatat(arg1, path(p), &st, arg4));
+#endif
+ if (!is_error(ret))
+ ret = host_to_target_stat64(cpu_env, arg3, &st);
+ break;
+#endif
+#ifdef USE_UID16
+ case TARGET_NR_lchown:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3)));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_getuid:
+ ret = get_errno(high2lowuid(getuid()));
+ break;
+ case TARGET_NR_getgid:
+ ret = get_errno(high2lowgid(getgid()));
+ break;
+ case TARGET_NR_geteuid:
+ ret = get_errno(high2lowuid(geteuid()));
+ break;
+ case TARGET_NR_getegid:
+ ret = get_errno(high2lowgid(getegid()));
+ break;
+ case TARGET_NR_setreuid:
+ ret = get_errno(setreuid(low2highuid(arg1), low2highuid(arg2)));
+ break;
+ case TARGET_NR_setregid:
+ ret = get_errno(setregid(low2highgid(arg1), low2highgid(arg2)));
+ break;
+ case TARGET_NR_getgroups:
+ {
+ int gidsetsize = arg1;
+ uint16_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ ret = get_errno(getgroups(gidsetsize, grouplist));
+ if (gidsetsize == 0)
+ break;
+ if (!is_error(ret)) {
+ target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 2, 0);
+ if (!target_grouplist)
+ goto efault;
+ for(i = 0;i < ret; i++)
+ target_grouplist[i] = tswap16(grouplist[i]);
+ unlock_user(target_grouplist, arg2, gidsetsize * 2);
+ }
+ }
+ break;
+ case TARGET_NR_setgroups:
+ {
+ int gidsetsize = arg1;
+ uint16_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 2, 1);
+ if (!target_grouplist) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ for(i = 0;i < gidsetsize; i++)
+ grouplist[i] = tswap16(target_grouplist[i]);
+ unlock_user(target_grouplist, arg2, 0);
+ ret = get_errno(setgroups(gidsetsize, grouplist));
+ }
+ break;
+ case TARGET_NR_fchown:
+ ret = get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3)));
+ break;
+#if defined(TARGET_NR_fchownat) && defined(__NR_fchownat)
+ case TARGET_NR_fchownat:
+ if (!(p = lock_user_string(arg2)))
+ goto efault;
+ ret = get_errno(sys_fchownat(arg1, p, low2highuid(arg3), low2highgid(arg4), arg5));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+#ifdef TARGET_NR_setresuid
+ case TARGET_NR_setresuid:
+ ret = get_errno(setresuid(low2highuid(arg1),
+ low2highuid(arg2),
+ low2highuid(arg3)));
+ break;
+#endif
+#ifdef TARGET_NR_getresuid
+ case TARGET_NR_getresuid:
+ {
+ uid_t ruid, euid, suid;
+ ret = get_errno(getresuid(&ruid, &euid, &suid));
+ if (!is_error(ret)) {
+ if (put_user_u16(high2lowuid(ruid), arg1)
+ || put_user_u16(high2lowuid(euid), arg2)
+ || put_user_u16(high2lowuid(suid), arg3))
+ goto efault;
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_getresgid
+ case TARGET_NR_setresgid:
+ ret = get_errno(setresgid(low2highgid(arg1),
+ low2highgid(arg2),
+ low2highgid(arg3)));
+ break;
+#endif
+#ifdef TARGET_NR_getresgid
+ case TARGET_NR_getresgid:
+ {
+ gid_t rgid, egid, sgid;
+ ret = get_errno(getresgid(&rgid, &egid, &sgid));
+ if (!is_error(ret)) {
+ if (put_user_u16(high2lowgid(rgid), arg1)
+ || put_user_u16(high2lowgid(egid), arg2)
+ || put_user_u16(high2lowgid(sgid), arg3))
+ goto efault;
+ }
+ }
+ break;
+#endif
+ case TARGET_NR_chown:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(chown(p, low2highuid(arg2), low2highgid(arg3)));
+ unlock_user(p, arg1, 0);
+ break;
+ case TARGET_NR_setuid:
+ ret = get_errno(setuid(low2highuid(arg1)));
+ break;
+ case TARGET_NR_setgid:
+ ret = get_errno(setgid(low2highgid(arg1)));
+ break;
+ case TARGET_NR_setfsuid:
+ ret = get_errno(setfsuid(arg1));
+ break;
+ case TARGET_NR_setfsgid:
+ ret = get_errno(setfsgid(arg1));
+ break;
+#endif /* USE_UID16 */
+
+#ifdef TARGET_NR_lchown32
+ case TARGET_NR_lchown32:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(lchown(p, arg2, arg3));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_getuid32
+ case TARGET_NR_getuid32:
+ ret = get_errno(getuid());
+ break;
+#endif
+
+#if defined(TARGET_NR_getxuid) && defined(TARGET_ALPHA)
+ /* Alpha specific */
+ case TARGET_NR_getxuid:
+ {
+ uid_t euid;
+ euid=geteuid();
+ ((CPUAlphaState *)cpu_env)->ir[IR_A4]=euid;
+ }
+ ret = get_errno(getuid());
+ break;
+#endif
+#if defined(TARGET_NR_getxgid) && defined(TARGET_ALPHA)
+ /* Alpha specific */
+ case TARGET_NR_getxgid:
+ {
+ uid_t egid;
+ egid=getegid();
+ ((CPUAlphaState *)cpu_env)->ir[IR_A4]=egid;
+ }
+ ret = get_errno(getgid());
+ break;
+#endif
+#if defined(TARGET_NR_osf_getsysinfo) && defined(TARGET_ALPHA)
+ /* Alpha specific */
+ case TARGET_NR_osf_getsysinfo:
+ ret = -TARGET_EOPNOTSUPP;
+ switch (arg1) {
+ case TARGET_GSI_IEEE_FP_CONTROL:
+ {
+ uint64_t swcr, fpcr = cpu_alpha_load_fpcr (cpu_env);
+
+ /* Copied from linux ieee_fpcr_to_swcr. */
+ swcr = (fpcr >> 35) & SWCR_STATUS_MASK;
+ swcr |= (fpcr >> 36) & SWCR_MAP_DMZ;
+ swcr |= (~fpcr >> 48) & (SWCR_TRAP_ENABLE_INV
+ | SWCR_TRAP_ENABLE_DZE
+ | SWCR_TRAP_ENABLE_OVF);
+ swcr |= (~fpcr >> 57) & (SWCR_TRAP_ENABLE_UNF
+ | SWCR_TRAP_ENABLE_INE);
+ swcr |= (fpcr >> 47) & SWCR_MAP_UMZ;
+ swcr |= (~fpcr >> 41) & SWCR_TRAP_ENABLE_DNO;
+
+ if (put_user_u64 (swcr, arg2))
+ goto efault;
+ ret = 0;
+ }
+ break;
+
+ /* case GSI_IEEE_STATE_AT_SIGNAL:
+ -- Not implemented in linux kernel.
+ case GSI_UACPROC:
+ -- Retrieves current unaligned access state; not much used.
+ case GSI_PROC_TYPE:
+ -- Retrieves implver information; surely not used.
+ case GSI_GET_HWRPB:
+ -- Grabs a copy of the HWRPB; surely not used.
+ */
+ }
+ break;
+#endif
+#if defined(TARGET_NR_osf_setsysinfo) && defined(TARGET_ALPHA)
+ /* Alpha specific */
+ case TARGET_NR_osf_setsysinfo:
+ ret = -TARGET_EOPNOTSUPP;
+ switch (arg1) {
+ case TARGET_SSI_IEEE_FP_CONTROL:
+ case TARGET_SSI_IEEE_RAISE_EXCEPTION:
+ {
+ uint64_t swcr, fpcr, orig_fpcr;
+
+ if (get_user_u64 (swcr, arg2))
+ goto efault;
+ orig_fpcr = cpu_alpha_load_fpcr (cpu_env);
+ fpcr = orig_fpcr & FPCR_DYN_MASK;
+
+ /* Copied from linux ieee_swcr_to_fpcr. */
+ fpcr |= (swcr & SWCR_STATUS_MASK) << 35;
+ fpcr |= (swcr & SWCR_MAP_DMZ) << 36;
+ fpcr |= (~swcr & (SWCR_TRAP_ENABLE_INV
+ | SWCR_TRAP_ENABLE_DZE
+ | SWCR_TRAP_ENABLE_OVF)) << 48;
+ fpcr |= (~swcr & (SWCR_TRAP_ENABLE_UNF
+ | SWCR_TRAP_ENABLE_INE)) << 57;
+ fpcr |= (swcr & SWCR_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0);
+ fpcr |= (~swcr & SWCR_TRAP_ENABLE_DNO) << 41;
+
+ cpu_alpha_store_fpcr (cpu_env, fpcr);
+ ret = 0;
+
+ if (arg1 == TARGET_SSI_IEEE_RAISE_EXCEPTION) {
+ /* Old exceptions are not signaled. */
+ fpcr &= ~(orig_fpcr & FPCR_STATUS_MASK);
+
+ /* If any exceptions set by this call, and are unmasked,
+ send a signal. */
+ /* ??? FIXME */
+ }
+ }
+ break;
+
+ /* case SSI_NVPAIRS:
+ -- Used with SSIN_UACPROC to enable unaligned accesses.
+ case SSI_IEEE_STATE_AT_SIGNAL:
+ case SSI_IEEE_IGNORE_STATE_AT_SIGNAL:
+ -- Not implemented in linux kernel
+ */
+ }
+ break;
+#endif
+#ifdef TARGET_NR_osf_sigprocmask
+ /* Alpha specific. */
+ case TARGET_NR_osf_sigprocmask:
+ {
+ abi_ulong mask;
+ int how = arg1;
+ sigset_t set, oldset;
+
+ switch(arg1) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ mask = arg2;
+ target_to_host_old_sigset(&set, &mask);
+ sigprocmask(arg1, &set, &oldset);
+ host_to_target_old_sigset(&mask, &oldset);
+ ret = mask;
+ }
+ break;
+#endif
+
+#ifdef TARGET_NR_getgid32
+ case TARGET_NR_getgid32:
+ ret = get_errno(getgid());
+ break;
+#endif
+#ifdef TARGET_NR_geteuid32
+ case TARGET_NR_geteuid32:
+ ret = get_errno(geteuid());
+ break;
+#endif
+#ifdef TARGET_NR_getegid32
+ case TARGET_NR_getegid32:
+ ret = get_errno(getegid());
+ break;
+#endif
+#ifdef TARGET_NR_setreuid32
+ case TARGET_NR_setreuid32:
+ ret = get_errno(setreuid(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_setregid32
+ case TARGET_NR_setregid32:
+ ret = get_errno(setregid(arg1, arg2));
+ break;
+#endif
+#ifdef TARGET_NR_getgroups32
+ case TARGET_NR_getgroups32:
+ {
+ int gidsetsize = arg1;
+ uint32_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ ret = get_errno(getgroups(gidsetsize, grouplist));
+ if (gidsetsize == 0)
+ break;
+ if (!is_error(ret)) {
+ target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0);
+ if (!target_grouplist) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ for(i = 0;i < ret; i++)
+ target_grouplist[i] = tswap32(grouplist[i]);
+ unlock_user(target_grouplist, arg2, gidsetsize * 4);
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_setgroups32
+ case TARGET_NR_setgroups32:
+ {
+ int gidsetsize = arg1;
+ uint32_t *target_grouplist;
+ gid_t *grouplist;
+ int i;
+
+ grouplist = alloca(gidsetsize * sizeof(gid_t));
+ target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1);
+ if (!target_grouplist) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ for(i = 0;i < gidsetsize; i++)
+ grouplist[i] = tswap32(target_grouplist[i]);
+ unlock_user(target_grouplist, arg2, 0);
+ ret = get_errno(setgroups(gidsetsize, grouplist));
+ }
+ break;
+#endif
+#ifdef TARGET_NR_fchown32
+ case TARGET_NR_fchown32:
+ ret = get_errno(fchown(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_setresuid32
+ case TARGET_NR_setresuid32:
+ ret = get_errno(setresuid(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_getresuid32
+ case TARGET_NR_getresuid32:
+ {
+ uid_t ruid, euid, suid;
+ ret = get_errno(getresuid(&ruid, &euid, &suid));
+ if (!is_error(ret)) {
+ if (put_user_u32(ruid, arg1)
+ || put_user_u32(euid, arg2)
+ || put_user_u32(suid, arg3))
+ goto efault;
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_setresgid32
+ case TARGET_NR_setresgid32:
+ ret = get_errno(setresgid(arg1, arg2, arg3));
+ break;
+#endif
+#ifdef TARGET_NR_getresgid32
+ case TARGET_NR_getresgid32:
+ {
+ gid_t rgid, egid, sgid;
+ ret = get_errno(getresgid(&rgid, &egid, &sgid));
+ if (!is_error(ret)) {
+ if (put_user_u32(rgid, arg1)
+ || put_user_u32(egid, arg2)
+ || put_user_u32(sgid, arg3))
+ goto efault;
+ }
+ }
+ break;
+#endif
+#ifdef TARGET_NR_chown32
+ case TARGET_NR_chown32:
+ if (!(p = lock_user_string(arg1)))
+ goto efault;
+ ret = get_errno(chown(p, arg2, arg3));
+ unlock_user(p, arg1, 0);
+ break;
+#endif
+#ifdef TARGET_NR_setuid32
+ case TARGET_NR_setuid32:
+ ret = get_errno(setuid(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_setgid32
+ case TARGET_NR_setgid32:
+ ret = get_errno(setgid(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_setfsuid32
+ case TARGET_NR_setfsuid32:
+ ret = get_errno(setfsuid(arg1));
+ break;
+#endif
+#ifdef TARGET_NR_setfsgid32
+ case TARGET_NR_setfsgid32:
+ ret = get_errno(setfsgid(arg1));
+ break;
+#endif
+
+ case TARGET_NR_pivot_root:
+ goto unimplemented;
+#ifdef TARGET_NR_mincore
+ case TARGET_NR_mincore:
+ {
+ void *a;
+ ret = -TARGET_EFAULT;
+ if (!(a = lock_user(VERIFY_READ, arg1,arg2, 0)))
+ goto efault;
+ if (!(p = lock_user_string(arg3)))
+ goto mincore_fail;
+ ret = get_errno(mincore(a, arg2, p));
+ unlock_user(p, arg3, ret);
+ mincore_fail:
+ unlock_user(a, arg1, 0);
+ }
+ break;
+#endif
+#ifdef TARGET_NR_arm_fadvise64_64
+ case TARGET_NR_arm_fadvise64_64:
+ {
+ /*
+ * arm_fadvise64_64 looks like fadvise64_64 but
+ * with different argument order
+ */
+ abi_long temp;
+ temp = arg3;
+ arg3 = arg4;
+ arg4 = temp;
+ }
+#endif
+#if defined(TARGET_NR_fadvise64_64) || defined(TARGET_NR_arm_fadvise64_64) || defined(TARGET_NR_fadvise64)
+#ifdef TARGET_NR_fadvise64_64
+ case TARGET_NR_fadvise64_64:
+#endif
+#ifdef TARGET_NR_fadvise64
+ case TARGET_NR_fadvise64:
+#endif
+#ifdef TARGET_S390X
+ switch (arg4) {
+ case 4: arg4 = POSIX_FADV_NOREUSE + 1; break; /* make sure it's an invalid value */
+ case 5: arg4 = POSIX_FADV_NOREUSE + 2; break; /* ditto */
+ case 6: arg4 = POSIX_FADV_DONTNEED; break;
+ case 7: arg4 = POSIX_FADV_NOREUSE; break;
+ default: break;
+ }
+#endif
+ ret = -posix_fadvise(arg1, arg2, arg3, arg4);
+ break;
+#endif
+#ifdef TARGET_NR_madvise
+ case TARGET_NR_madvise:
+ /* A straight passthrough may not be safe because qemu sometimes
+ turns private flie-backed mappings into anonymous mappings.
+ This will break MADV_DONTNEED.
+ This is a hint, so ignoring and returning success is ok. */
+ ret = get_errno(0);
+ break;
+#endif
+#if TARGET_ABI_BITS == 32
+ case TARGET_NR_fcntl64:
+ {
+ int cmd;
+ struct flock64 fl;
+ struct target_flock64 *target_fl;
+#ifdef TARGET_ARM
+ struct target_eabi_flock64 *target_efl;
+#endif
+
+ cmd = target_to_host_fcntl_cmd(arg2);
+ if (cmd == -TARGET_EINVAL)
+ return cmd;
+
+ switch(arg2) {
+ case TARGET_F_GETLK64:
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ if (!lock_user_struct(VERIFY_READ, target_efl, arg3, 1))
+ goto efault;
+ fl.l_type = tswap16(target_efl->l_type);
+ fl.l_whence = tswap16(target_efl->l_whence);
+ fl.l_start = tswap64(target_efl->l_start);
+ fl.l_len = tswap64(target_efl->l_len);
+ fl.l_pid = tswap32(target_efl->l_pid);
+ unlock_user_struct(target_efl, arg3, 0);
+ } else
+#endif
+ {
+ if (!lock_user_struct(VERIFY_READ, target_fl, arg3, 1))
+ goto efault;
+ fl.l_type = tswap16(target_fl->l_type);
+ fl.l_whence = tswap16(target_fl->l_whence);
+ fl.l_start = tswap64(target_fl->l_start);
+ fl.l_len = tswap64(target_fl->l_len);
+ fl.l_pid = tswap32(target_fl->l_pid);
+ unlock_user_struct(target_fl, arg3, 0);
+ }
+ ret = get_errno(fcntl(arg1, cmd, &fl));
+ if (ret == 0) {
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ if (!lock_user_struct(VERIFY_WRITE, target_efl, arg3, 0))
+ goto efault;
+ target_efl->l_type = tswap16(fl.l_type);
+ target_efl->l_whence = tswap16(fl.l_whence);
+ target_efl->l_start = tswap64(fl.l_start);
+ target_efl->l_len = tswap64(fl.l_len);
+ target_efl->l_pid = tswap32(fl.l_pid);
+ unlock_user_struct(target_efl, arg3, 1);
+ } else
+#endif
+ {
+ if (!lock_user_struct(VERIFY_WRITE, target_fl, arg3, 0))
+ goto efault;
+ target_fl->l_type = tswap16(fl.l_type);
+ target_fl->l_whence = tswap16(fl.l_whence);
+ target_fl->l_start = tswap64(fl.l_start);
+ target_fl->l_len = tswap64(fl.l_len);
+ target_fl->l_pid = tswap32(fl.l_pid);
+ unlock_user_struct(target_fl, arg3, 1);
+ }
+ }
+ break;
+
+ case TARGET_F_SETLK64:
+ case TARGET_F_SETLKW64:
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi) {
+ if (!lock_user_struct(VERIFY_READ, target_efl, arg3, 1))
+ goto efault;
+ fl.l_type = tswap16(target_efl->l_type);
+ fl.l_whence = tswap16(target_efl->l_whence);
+ fl.l_start = tswap64(target_efl->l_start);
+ fl.l_len = tswap64(target_efl->l_len);
+ fl.l_pid = tswap32(target_efl->l_pid);
+ unlock_user_struct(target_efl, arg3, 0);
+ } else
+#endif
+ {
+ if (!lock_user_struct(VERIFY_READ, target_fl, arg3, 1))
+ goto efault;
+ fl.l_type = tswap16(target_fl->l_type);
+ fl.l_whence = tswap16(target_fl->l_whence);
+ fl.l_start = tswap64(target_fl->l_start);
+ fl.l_len = tswap64(target_fl->l_len);
+ fl.l_pid = tswap32(target_fl->l_pid);
+ unlock_user_struct(target_fl, arg3, 0);
+ }
+ ret = get_errno(fcntl(arg1, cmd, &fl));
+ break;
+ default:
+ ret = do_fcntl(arg1, arg2, arg3);
+ break;
+ }
+ break;
+ }
+#endif
+#ifdef TARGET_NR_cacheflush
+ case TARGET_NR_cacheflush:
+ /* self-modifying code is handled automatically, so nothing needed */
+ ret = 0;
+ break;
+#endif
+#ifdef TARGET_NR_security
+ case TARGET_NR_security:
+ goto unimplemented;
+#endif
+#ifdef TARGET_NR_getpagesize
+ case TARGET_NR_getpagesize:
+ ret = TARGET_PAGE_SIZE;
+ break;
+#endif
+ case TARGET_NR_gettid:
+ ret = get_errno(gettid());
+ break;
+#ifdef TARGET_NR_readahead
+ case TARGET_NR_readahead:
+#if TARGET_ABI_BITS == 32
+#ifdef TARGET_ARM
+ if (((CPUARMState *)cpu_env)->eabi)
+ {
+ arg2 = arg3;
+ arg3 = arg4;
+ arg4 = arg5;
+ }
+#endif
+ ret = get_errno(readahead(arg1, ((off64_t)arg3 << 32) | arg2, arg4));
+#else
+ ret = get_errno(readahead(arg1, arg2, arg3));
+#endif
+ break;
+#endif
+#ifdef TARGET_NR_setxattr
+ case TARGET_NR_setxattr:
+ case TARGET_NR_lsetxattr:
+ case TARGET_NR_fsetxattr:
+ case TARGET_NR_getxattr:
+ case TARGET_NR_lgetxattr:
+ case TARGET_NR_fgetxattr:
+ case TARGET_NR_listxattr:
+ case TARGET_NR_llistxattr:
+ case TARGET_NR_flistxattr:
+ case TARGET_NR_removexattr:
+ case TARGET_NR_lremovexattr:
+ case TARGET_NR_fremovexattr:
+ ret = -TARGET_EOPNOTSUPP;
+ break;
+#endif
+#ifdef TARGET_NR_set_thread_area
+ case TARGET_NR_set_thread_area:
+#if defined(TARGET_MIPS)
+ ((CPUMIPSState *) cpu_env)->tls_value = arg1;
+ ret = 0;
+ break;
+#elif defined(TARGET_CRIS)
+ if (arg1 & 0xff)
+ ret = -TARGET_EINVAL;
+ else {
+ ((CPUCRISState *) cpu_env)->pregs[PR_PID] = arg1;
+ ret = 0;
+ }
+ break;
+#elif defined(TARGET_I386) && defined(TARGET_ABI32)
+ ret = do_set_thread_area(cpu_env, arg1);
+ break;
+#else
+ goto unimplemented_nowarn;
+#endif
+#endif
+#ifdef TARGET_NR_get_thread_area
+ case TARGET_NR_get_thread_area:
+#if defined(TARGET_I386) && defined(TARGET_ABI32)
+ ret = do_get_thread_area(cpu_env, arg1);
+#else
+ goto unimplemented_nowarn;
+#endif
+#endif
+#ifdef TARGET_NR_getdomainname
+ case TARGET_NR_getdomainname:
+ goto unimplemented_nowarn;
+#endif
+
+#ifdef TARGET_NR_clock_gettime
+ case TARGET_NR_clock_gettime:
+ {
+ struct timespec ts;
+ ret = get_errno(clock_gettime(arg1, &ts));
+ if (!is_error(ret)) {
+ host_to_target_timespec(arg2, &ts);
+ }
+ break;
+ }
+#endif
+#ifdef TARGET_NR_clock_getres
+ case TARGET_NR_clock_getres:
+ {
+ struct timespec ts;
+ ret = get_errno(clock_getres(arg1, &ts));
+ if (!is_error(ret)) {
+ host_to_target_timespec(arg2, &ts);
+ }
+ break;
+ }
+#endif
+#ifdef TARGET_NR_clock_nanosleep
+ case TARGET_NR_clock_nanosleep:
+ {
+ struct timespec ts;
+ target_to_host_timespec(&ts, arg3);
+ ret = get_errno(clock_nanosleep(arg1, arg2, &ts, arg4 ? &ts : NULL));
+ if (arg4)
+ host_to_target_timespec(arg4, &ts);
+ break;
+ }
+#endif
+
+#if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address)
+ case TARGET_NR_set_tid_address:
+ ret = get_errno(set_tid_address((int *)g2h(arg1)));
+ break;
+#endif
+
+#if defined(TARGET_NR_tkill) && defined(__NR_tkill)
+ case TARGET_NR_tkill:
+ ret = get_errno(sys_tkill((int)arg1, target_to_host_signal(arg2)));
+ break;
+#endif
+
+#if defined(TARGET_NR_tgkill) && defined(__NR_tgkill)
+ case TARGET_NR_tgkill:
+ ret = get_errno(sys_tgkill((int)arg1, (int)arg2,
+ target_to_host_signal(arg3)));
+ break;
+#endif
+
+#ifdef TARGET_NR_set_robust_list
+ case TARGET_NR_set_robust_list:
+ goto unimplemented_nowarn;
+#endif
+
+#if defined(TARGET_NR_utimensat) && defined(__NR_utimensat)
+ case TARGET_NR_utimensat:
+ {
+ struct timespec *tsp, ts[2];
+ if (!arg3) {
+ tsp = NULL;
+ } else {
+ target_to_host_timespec(ts, arg3);
+ target_to_host_timespec(ts+1, arg3+sizeof(struct target_timespec));
+ tsp = ts;
+ }
+ if (!arg2)
+ ret = get_errno(sys_utimensat(arg1, NULL, tsp, arg4));
+ else {
+ if (!(p = lock_user_string(arg2))) {
+ ret = -TARGET_EFAULT;
+ goto fail;
+ }
+ ret = get_errno(sys_utimensat(arg1, path(p), tsp, arg4));
+ unlock_user(p, arg2, 0);
+ }
+ }
+ break;
+#endif
+#if defined(CONFIG_USE_NPTL)
+ case TARGET_NR_futex:
+ ret = do_futex(arg1, arg2, arg3, arg4, arg5, arg6);
+ break;
+#endif
+#if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)
+ case TARGET_NR_inotify_init:
+ ret = get_errno(sys_inotify_init());
+ break;
+#endif
+#ifdef CONFIG_INOTIFY1
+#if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1)
+ case TARGET_NR_inotify_init1:
+ ret = get_errno(sys_inotify_init1(arg1));
+ break;
+#endif
+#endif
+#if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch)
+ case TARGET_NR_inotify_add_watch:
+ p = lock_user_string(arg2);
+ ret = get_errno(sys_inotify_add_watch(arg1, path(p), arg3));
+ unlock_user(p, arg2, 0);
+ break;
+#endif
+#if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch)
+ case TARGET_NR_inotify_rm_watch:
+ ret = get_errno(sys_inotify_rm_watch(arg1, arg2));
+ break;
+#endif
+
+#if defined(TARGET_NR_mq_open) && defined(__NR_mq_open)
+ case TARGET_NR_mq_open:
+ {
+ struct mq_attr posix_mq_attr;
+
+ p = lock_user_string(arg1 - 1);
+ if (arg4 != 0)
+ copy_from_user_mq_attr (&posix_mq_attr, arg4);
+ ret = get_errno(mq_open(p, arg2, arg3, &posix_mq_attr));
+ unlock_user (p, arg1, 0);
+ }
+ break;
+
+ case TARGET_NR_mq_unlink:
+ p = lock_user_string(arg1 - 1);
+ ret = get_errno(mq_unlink(p));
+ unlock_user (p, arg1, 0);
+ break;
+
+ case TARGET_NR_mq_timedsend:
+ {
+ struct timespec ts;
+
+ p = lock_user (VERIFY_READ, arg2, arg3, 1);
+ if (arg5 != 0) {
+ target_to_host_timespec(&ts, arg5);
+ ret = get_errno(mq_timedsend(arg1, p, arg3, arg4, &ts));
+ host_to_target_timespec(arg5, &ts);
+ }
+ else
+ ret = get_errno(mq_send(arg1, p, arg3, arg4));
+ unlock_user (p, arg2, arg3);
+ }
+ break;
+
+ case TARGET_NR_mq_timedreceive:
+ {
+ struct timespec ts;
+ unsigned int prio;
+
+ p = lock_user (VERIFY_READ, arg2, arg3, 1);
+ if (arg5 != 0) {
+ target_to_host_timespec(&ts, arg5);
+ ret = get_errno(mq_timedreceive(arg1, p, arg3, &prio, &ts));
+ host_to_target_timespec(arg5, &ts);
+ }
+ else
+ ret = get_errno(mq_receive(arg1, p, arg3, &prio));
+ unlock_user (p, arg2, arg3);
+ if (arg4 != 0)
+ put_user_u32(prio, arg4);
+ }
+ break;
+
+ /* Not implemented for now... */
+/* case TARGET_NR_mq_notify: */
+/* break; */
+
+ case TARGET_NR_mq_getsetattr:
+ {
+ struct mq_attr posix_mq_attr_in, posix_mq_attr_out;
+ ret = 0;
+ if (arg3 != 0) {
+ ret = mq_getattr(arg1, &posix_mq_attr_out);
+ copy_to_user_mq_attr(arg3, &posix_mq_attr_out);
+ }
+ if (arg2 != 0) {
+ copy_from_user_mq_attr(&posix_mq_attr_in, arg2);
+ ret |= mq_setattr(arg1, &posix_mq_attr_in, &posix_mq_attr_out);
+ }
+
+ }
+ break;
+#endif
+
+#ifdef CONFIG_SPLICE
+#ifdef TARGET_NR_tee
+ case TARGET_NR_tee:
+ {
+ ret = get_errno(tee(arg1,arg2,arg3,arg4));
+ }
+ break;
+#endif
+#ifdef TARGET_NR_splice
+ case TARGET_NR_splice:
+ {
+ loff_t loff_in, loff_out;
+ loff_t *ploff_in = NULL, *ploff_out = NULL;
+ if(arg2) {
+ get_user_u64(loff_in, arg2);
+ ploff_in = &loff_in;
+ }
+ if(arg4) {
+ get_user_u64(loff_out, arg2);
+ ploff_out = &loff_out;
+ }
+ ret = get_errno(splice(arg1, ploff_in, arg3, ploff_out, arg5, arg6));
+ }
+ break;
+#endif
+#ifdef TARGET_NR_vmsplice
+ case TARGET_NR_vmsplice:
+ {
+ int count = arg3;
+ struct iovec *vec;
+
+ vec = alloca(count * sizeof(struct iovec));
+ if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
+ goto efault;
+ ret = get_errno(vmsplice(arg1, vec, count, arg4));
+ unlock_iovec(vec, arg2, count, 0);
+ }
+ break;
+#endif
+#endif /* CONFIG_SPLICE */
+#ifdef CONFIG_EVENTFD
+#if defined(TARGET_NR_eventfd)
+ case TARGET_NR_eventfd:
+ ret = get_errno(eventfd(arg1, 0));
+ break;
+#endif
+#if defined(TARGET_NR_eventfd2)
+ case TARGET_NR_eventfd2:
+ ret = get_errno(eventfd(arg1, arg2));
+ break;
+#endif
+#endif /* CONFIG_EVENTFD */
+#if defined(CONFIG_FALLOCATE) && defined(TARGET_NR_fallocate)
+ case TARGET_NR_fallocate:
+ ret = get_errno(fallocate(arg1, arg2, arg3, arg4));
+ break;
+#endif
+#if defined(CONFIG_SYNC_FILE_RANGE)
+#if defined(TARGET_NR_sync_file_range)
+ case TARGET_NR_sync_file_range:
+#if TARGET_ABI_BITS == 32
+ ret = get_errno(sync_file_range(arg1, target_offset64(arg2, arg3),
+ target_offset64(arg4, arg5), arg6));
+#else
+ ret = get_errno(sync_file_range(arg1, arg2, arg3, arg4));
+#endif
+ break;
+#endif
+#if defined(TARGET_NR_sync_file_range2)
+ case TARGET_NR_sync_file_range2:
+ /* This is like sync_file_range but the arguments are reordered */
+#if TARGET_ABI_BITS == 32
+ ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4),
+ target_offset64(arg5, arg6), arg2));
+#else
+ ret = get_errno(sync_file_range(arg1, arg3, arg4, arg2));
+#endif
+ break;
+#endif
+#endif
+ default:
+ unimplemented:
+ gemu_log("qemu: Unsupported syscall: %d\n", num);
+#if defined(TARGET_NR_setxattr) || defined(TARGET_NR_get_thread_area) || defined(TARGET_NR_getdomainname) || defined(TARGET_NR_set_robust_list)
+ unimplemented_nowarn:
+#endif
+ ret = -TARGET_ENOSYS;
+ break;
+ }
+fail:
+#ifdef DEBUG
+ gemu_log(" = " TARGET_ABI_FMT_ld "\n", ret);
+#endif
+ if(do_strace)
+ print_syscall_ret(num, ret);
+ return ret;
+efault:
+ ret = -TARGET_EFAULT;
+ goto fail;
+}
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
new file mode 100644
index 0000000..d02a9bf
--- /dev/null
+++ b/linux-user/syscall_defs.h
@@ -0,0 +1,2207 @@
+/* common syscall defines for all architectures */
+
+/* Note: although the syscall numbers change between architectures,
+ most of them stay the same, so we handle it by puting ifdefs if
+ necessary */
+
+#include "syscall_nr.h"
+
+#define SOCKOP_socket 1
+#define SOCKOP_bind 2
+#define SOCKOP_connect 3
+#define SOCKOP_listen 4
+#define SOCKOP_accept 5
+#define SOCKOP_getsockname 6
+#define SOCKOP_getpeername 7
+#define SOCKOP_socketpair 8
+#define SOCKOP_send 9
+#define SOCKOP_recv 10
+#define SOCKOP_sendto 11
+#define SOCKOP_recvfrom 12
+#define SOCKOP_shutdown 13
+#define SOCKOP_setsockopt 14
+#define SOCKOP_getsockopt 15
+#define SOCKOP_sendmsg 16
+#define SOCKOP_recvmsg 17
+
+#define IPCOP_semop 1
+#define IPCOP_semget 2
+#define IPCOP_semctl 3
+#define IPCOP_semtimedop 4
+#define IPCOP_msgsnd 11
+#define IPCOP_msgrcv 12
+#define IPCOP_msgget 13
+#define IPCOP_msgctl 14
+#define IPCOP_shmat 21
+#define IPCOP_shmdt 22
+#define IPCOP_shmget 23
+#define IPCOP_shmctl 24
+
+/*
+ * The following is for compatibility across the various Linux
+ * platforms. The i386 ioctl numbering scheme doesn't really enforce
+ * a type field. De facto, however, the top 8 bits of the lower 16
+ * bits are indeed used as a type field, so we might just as well make
+ * this explicit here. Please be sure to use the decoding macros
+ * below from now on.
+ */
+#define TARGET_IOC_NRBITS 8
+#define TARGET_IOC_TYPEBITS 8
+
+#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \
+ || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) || defined(TARGET_PPC) || defined(TARGET_MIPS)
+ /* 16 bit uid wrappers emulation */
+#define USE_UID16
+#endif
+
+#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \
+ || defined(TARGET_M68K) || defined(TARGET_CRIS)
+
+#define TARGET_IOC_SIZEBITS 14
+#define TARGET_IOC_DIRBITS 2
+
+#define TARGET_IOC_NONE 0U
+#define TARGET_IOC_WRITE 1U
+#define TARGET_IOC_READ 2U
+
+#elif defined(TARGET_PPC) || defined(TARGET_ALPHA) || \
+ defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE) || \
+ defined(TARGET_MIPS)
+
+#define TARGET_IOC_SIZEBITS 13
+#define TARGET_IOC_DIRBITS 3
+
+#define TARGET_IOC_NONE 1U
+#define TARGET_IOC_READ 2U
+#define TARGET_IOC_WRITE 4U
+
+#else
+#error unsupported CPU
+#endif
+
+#define TARGET_IOC_NRMASK ((1 << TARGET_IOC_NRBITS)-1)
+#define TARGET_IOC_TYPEMASK ((1 << TARGET_IOC_TYPEBITS)-1)
+#define TARGET_IOC_SIZEMASK ((1 << TARGET_IOC_SIZEBITS)-1)
+#define TARGET_IOC_DIRMASK ((1 << TARGET_IOC_DIRBITS)-1)
+
+#define TARGET_IOC_NRSHIFT 0
+#define TARGET_IOC_TYPESHIFT (TARGET_IOC_NRSHIFT+TARGET_IOC_NRBITS)
+#define TARGET_IOC_SIZESHIFT (TARGET_IOC_TYPESHIFT+TARGET_IOC_TYPEBITS)
+#define TARGET_IOC_DIRSHIFT (TARGET_IOC_SIZESHIFT+TARGET_IOC_SIZEBITS)
+
+#define TARGET_IOC(dir,type,nr,size) \
+ (((dir) << TARGET_IOC_DIRSHIFT) | \
+ ((type) << TARGET_IOC_TYPESHIFT) | \
+ ((nr) << TARGET_IOC_NRSHIFT) | \
+ ((size) << TARGET_IOC_SIZESHIFT))
+
+/* used to create numbers */
+#define TARGET_IO(type,nr) TARGET_IOC(TARGET_IOC_NONE,(type),(nr),0)
+#define TARGET_IOR(type,nr,size) TARGET_IOC(TARGET_IOC_READ,(type),(nr),sizeof(size))
+#define TARGET_IOW(type,nr,size) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),sizeof(size))
+#define TARGET_IOWR(type,nr,size) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),sizeof(size))
+
+/* the size is automatically computed for these defines */
+#define TARGET_IORU(type,nr) TARGET_IOC(TARGET_IOC_READ,(type),(nr),TARGET_IOC_SIZEMASK)
+#define TARGET_IOWU(type,nr) TARGET_IOC(TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK)
+#define TARGET_IOWRU(type,nr) TARGET_IOC(TARGET_IOC_READ|TARGET_IOC_WRITE,(type),(nr),TARGET_IOC_SIZEMASK)
+
+struct target_sockaddr {
+ uint16_t sa_family;
+ uint8_t sa_data[14];
+};
+
+struct target_in_addr {
+ uint32_t s_addr; /* big endian */
+};
+
+struct target_ip_mreq {
+ struct target_in_addr imr_multiaddr;
+ struct target_in_addr imr_address;
+};
+
+struct target_ip_mreqn {
+ struct target_in_addr imr_multiaddr;
+ struct target_in_addr imr_address;
+ abi_long imr_ifindex;
+};
+
+struct target_ip_mreq_source {
+ /* big endian */
+ uint32_t imr_multiaddr;
+ uint32_t imr_interface;
+ uint32_t imr_sourceaddr;
+};
+
+struct target_timeval {
+ abi_long tv_sec;
+ abi_long tv_usec;
+};
+
+struct target_timespec {
+ abi_long tv_sec;
+ abi_long tv_nsec;
+};
+
+struct target_itimerval {
+ struct target_timeval it_interval;
+ struct target_timeval it_value;
+};
+
+typedef abi_long target_clock_t;
+
+#define TARGET_HZ 100
+
+struct target_tms {
+ target_clock_t tms_utime;
+ target_clock_t tms_stime;
+ target_clock_t tms_cutime;
+ target_clock_t tms_cstime;
+};
+
+struct target_utimbuf {
+ abi_long actime;
+ abi_long modtime;
+};
+
+struct target_sel_arg_struct {
+ abi_long n;
+ abi_long inp, outp, exp;
+ abi_long tvp;
+};
+
+struct target_iovec {
+ abi_long iov_base; /* Starting address */
+ abi_long iov_len; /* Number of bytes */
+};
+
+struct target_msghdr {
+ abi_long msg_name; /* Socket name */
+ int msg_namelen; /* Length of name */
+ abi_long msg_iov; /* Data blocks */
+ abi_long msg_iovlen; /* Number of blocks */
+ abi_long msg_control; /* Per protocol magic (eg BSD file descriptor passing) */
+ abi_long msg_controllen; /* Length of cmsg list */
+ unsigned int msg_flags;
+};
+
+struct target_cmsghdr {
+ abi_long cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+#define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1))
+#define TARGET_CMSG_NXTHDR(mhdr, cmsg) __target_cmsg_nxthdr (mhdr, cmsg)
+#define TARGET_CMSG_ALIGN(len) (((len) + sizeof (abi_long) - 1) \
+ & (size_t) ~(sizeof (abi_long) - 1))
+#define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \
+ + TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)))
+#define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len))
+
+static __inline__ struct target_cmsghdr *
+__target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg)
+{
+ struct target_cmsghdr *__ptr;
+
+ __ptr = (struct target_cmsghdr *)((unsigned char *) __cmsg
+ + TARGET_CMSG_ALIGN (tswapl(__cmsg->cmsg_len)));
+ if ((unsigned long)((char *)(__ptr+1) - (char *)(size_t)tswapl(__mhdr->msg_control))
+ > tswapl(__mhdr->msg_controllen))
+ /* No more entries. */
+ return (struct target_cmsghdr *)0;
+ return __cmsg;
+}
+
+
+struct target_rusage {
+ struct target_timeval ru_utime; /* user time used */
+ struct target_timeval ru_stime; /* system time used */
+ abi_long ru_maxrss; /* maximum resident set size */
+ abi_long ru_ixrss; /* integral shared memory size */
+ abi_long ru_idrss; /* integral unshared data size */
+ abi_long ru_isrss; /* integral unshared stack size */
+ abi_long ru_minflt; /* page reclaims */
+ abi_long ru_majflt; /* page faults */
+ abi_long ru_nswap; /* swaps */
+ abi_long ru_inblock; /* block input operations */
+ abi_long ru_oublock; /* block output operations */
+ abi_long ru_msgsnd; /* messages sent */
+ abi_long ru_msgrcv; /* messages received */
+ abi_long ru_nsignals; /* signals received */
+ abi_long ru_nvcsw; /* voluntary context switches */
+ abi_long ru_nivcsw; /* involuntary " */
+};
+
+typedef struct {
+ int val[2];
+} kernel_fsid_t;
+
+struct kernel_statfs {
+ int f_type;
+ int f_bsize;
+ int f_blocks;
+ int f_bfree;
+ int f_bavail;
+ int f_files;
+ int f_ffree;
+ kernel_fsid_t f_fsid;
+ int f_namelen;
+ int f_spare[6];
+};
+
+struct target_dirent {
+ abi_long d_ino;
+ abi_long d_off;
+ unsigned short d_reclen;
+ char d_name[256]; /* We must not include limits.h! */
+};
+
+struct target_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[256];
+};
+
+
+/* mostly generic signal stuff */
+#define TARGET_SIG_DFL ((abi_long)0) /* default signal handling */
+#define TARGET_SIG_IGN ((abi_long)1) /* ignore signal */
+#define TARGET_SIG_ERR ((abi_long)-1) /* error return from signal */
+
+#ifdef TARGET_MIPS
+#define TARGET_NSIG 128
+#else
+#define TARGET_NSIG 64
+#endif
+#define TARGET_NSIG_BPW TARGET_ABI_BITS
+#define TARGET_NSIG_WORDS (TARGET_NSIG / TARGET_NSIG_BPW)
+
+typedef struct {
+ abi_ulong sig[TARGET_NSIG_WORDS];
+} target_sigset_t;
+
+#ifdef BSWAP_NEEDED
+static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
+{
+ int i;
+ for(i = 0;i < TARGET_NSIG_WORDS; i++)
+ d->sig[i] = tswapl(s->sig[i]);
+}
+#else
+static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
+{
+ *d = *s;
+}
+#endif
+
+static inline void target_siginitset(target_sigset_t *d, abi_ulong set)
+{
+ int i;
+ d->sig[0] = set;
+ for(i = 1;i < TARGET_NSIG_WORDS; i++)
+ d->sig[i] = 0;
+}
+
+void host_to_target_sigset(target_sigset_t *d, const sigset_t *s);
+void target_to_host_sigset(sigset_t *d, const target_sigset_t *s);
+void host_to_target_old_sigset(abi_ulong *old_sigset,
+ const sigset_t *sigset);
+void target_to_host_old_sigset(sigset_t *sigset,
+ const abi_ulong *old_sigset);
+struct target_sigaction;
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact);
+
+#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)
+
+#if defined(TARGET_SPARC)
+#define TARGET_SA_NOCLDSTOP 8u
+#define TARGET_SA_NOCLDWAIT 0x100u
+#define TARGET_SA_SIGINFO 0x200u
+#define TARGET_SA_ONSTACK 1u
+#define TARGET_SA_RESTART 2u
+#define TARGET_SA_NODEFER 0x20u
+#define TARGET_SA_RESETHAND 4u
+#elif defined(TARGET_MIPS)
+#define TARGET_SA_NOCLDSTOP 0x00000001
+#define TARGET_SA_NOCLDWAIT 0x00010000
+#define TARGET_SA_SIGINFO 0x00000008
+#define TARGET_SA_ONSTACK 0x08000000
+#define TARGET_SA_NODEFER 0x40000000
+#define TARGET_SA_RESTART 0x10000000
+#define TARGET_SA_RESETHAND 0x80000000
+#if !defined(TARGET_ABI_MIPSN32) && !defined(TARGET_ABI_MIPSN64)
+#define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */
+#endif
+#elif defined(TARGET_ALPHA)
+#define TARGET_SA_ONSTACK 0x00000001
+#define TARGET_SA_RESTART 0x00000002
+#define TARGET_SA_NOCLDSTOP 0x00000004
+#define TARGET_SA_NODEFER 0x00000008
+#define TARGET_SA_RESETHAND 0x00000010
+#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */
+#define TARGET_SA_SIGINFO 0x00000040
+#else
+#define TARGET_SA_NOCLDSTOP 0x00000001
+#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */
+#define TARGET_SA_SIGINFO 0x00000004
+#define TARGET_SA_ONSTACK 0x08000000
+#define TARGET_SA_RESTART 0x10000000
+#define TARGET_SA_NODEFER 0x40000000
+#define TARGET_SA_RESETHAND 0x80000000
+#define TARGET_SA_RESTORER 0x04000000
+#endif
+
+#if defined(TARGET_SPARC)
+
+#define TARGET_SIGHUP 1
+#define TARGET_SIGINT 2
+#define TARGET_SIGQUIT 3
+#define TARGET_SIGILL 4
+#define TARGET_SIGTRAP 5
+#define TARGET_SIGABRT 6
+#define TARGET_SIGIOT 6
+#define TARGET_SIGSTKFLT 7 /* actually EMT */
+#define TARGET_SIGFPE 8
+#define TARGET_SIGKILL 9
+#define TARGET_SIGBUS 10
+#define TARGET_SIGSEGV 11
+#define TARGET_SIGSYS 12
+#define TARGET_SIGPIPE 13
+#define TARGET_SIGALRM 14
+#define TARGET_SIGTERM 15
+#define TARGET_SIGURG 16
+#define TARGET_SIGSTOP 17
+#define TARGET_SIGTSTP 18
+#define TARGET_SIGCONT 19
+#define TARGET_SIGCHLD 20
+#define TARGET_SIGTTIN 21
+#define TARGET_SIGTTOU 22
+#define TARGET_SIGIO 23
+#define TARGET_SIGXCPU 24
+#define TARGET_SIGXFSZ 25
+#define TARGET_SIGVTALRM 26
+#define TARGET_SIGPROF 27
+#define TARGET_SIGWINCH 28
+#define TARGET_SIGPWR 29
+#define TARGET_SIGUSR1 30
+#define TARGET_SIGUSR2 31
+#define TARGET_SIGRTMIN 32
+
+#define TARGET_SIG_BLOCK 0x01 /* for blocking signals */
+#define TARGET_SIG_UNBLOCK 0x02 /* for unblocking signals */
+#define TARGET_SIG_SETMASK 0x04 /* for setting the signal mask */
+
+#elif defined(TARGET_MIPS)
+
+#define TARGET_SIGHUP 1 /* Hangup (POSIX). */
+#define TARGET_SIGINT 2 /* Interrupt (ANSI). */
+#define TARGET_SIGQUIT 3 /* Quit (POSIX). */
+#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */
+#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */
+#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */
+#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */
+#define TARGET_SIGEMT 7
+#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */
+#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */
+#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */
+#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */
+#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */
+#define TARGET_SIGSYS 12
+#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */
+#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */
+#define TARGET_SIGTERM 15 /* Termination (ANSI). */
+#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */
+#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */
+#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */
+#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */
+#define TARGET_SIGPWR 19 /* Power failure restart (System V). */
+#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */
+#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */
+#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */
+#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */
+#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */
+#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */
+#define TARGET_SIGCONT 25 /* Continue (POSIX). */
+#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */
+#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */
+#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */
+#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */
+#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */
+#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */
+#define TARGET_SIGRTMIN 32
+
+#define TARGET_SIG_BLOCK 1 /* for blocking signals */
+#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */
+#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */
+
+#else
+
+#define TARGET_SIGHUP 1
+#define TARGET_SIGINT 2
+#define TARGET_SIGQUIT 3
+#define TARGET_SIGILL 4
+#define TARGET_SIGTRAP 5
+#define TARGET_SIGABRT 6
+#define TARGET_SIGIOT 6
+#define TARGET_SIGBUS 7
+#define TARGET_SIGFPE 8
+#define TARGET_SIGKILL 9
+#define TARGET_SIGUSR1 10
+#define TARGET_SIGSEGV 11
+#define TARGET_SIGUSR2 12
+#define TARGET_SIGPIPE 13
+#define TARGET_SIGALRM 14
+#define TARGET_SIGTERM 15
+#define TARGET_SIGSTKFLT 16
+#define TARGET_SIGCHLD 17
+#define TARGET_SIGCONT 18
+#define TARGET_SIGSTOP 19
+#define TARGET_SIGTSTP 20
+#define TARGET_SIGTTIN 21
+#define TARGET_SIGTTOU 22
+#define TARGET_SIGURG 23
+#define TARGET_SIGXCPU 24
+#define TARGET_SIGXFSZ 25
+#define TARGET_SIGVTALRM 26
+#define TARGET_SIGPROF 27
+#define TARGET_SIGWINCH 28
+#define TARGET_SIGIO 29
+#define TARGET_SIGPWR 30
+#define TARGET_SIGSYS 31
+#define TARGET_SIGRTMIN 32
+
+#define TARGET_SIG_BLOCK 0 /* for blocking signals */
+#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */
+#define TARGET_SIG_SETMASK 2 /* for setting the signal mask */
+
+#endif
+
+#if defined(TARGET_ALPHA)
+struct target_old_sigaction {
+ abi_ulong _sa_handler;
+ abi_ulong sa_mask;
+ abi_ulong sa_flags;
+};
+
+struct target_rt_sigaction {
+ abi_ulong _sa_handler;
+ abi_ulong sa_flags;
+ target_sigset_t sa_mask;
+};
+
+/* This is the struct used inside the kernel. The ka_restorer
+ field comes from the 5th argument to sys_rt_sigaction. */
+struct target_sigaction {
+ abi_ulong _sa_handler;
+ abi_ulong sa_flags;
+ target_sigset_t sa_mask;
+ abi_ulong sa_restorer;
+};
+#elif defined(TARGET_MIPS)
+struct target_sigaction {
+ uint32_t sa_flags;
+#if defined(TARGET_ABI_MIPSN32)
+ uint32_t _sa_handler;
+#else
+ abi_ulong _sa_handler;
+#endif
+ target_sigset_t sa_mask;
+};
+#else
+struct target_old_sigaction {
+ abi_ulong _sa_handler;
+ abi_ulong sa_mask;
+ abi_ulong sa_flags;
+ abi_ulong sa_restorer;
+};
+
+struct target_sigaction {
+ abi_ulong _sa_handler;
+ abi_ulong sa_flags;
+ abi_ulong sa_restorer;
+ target_sigset_t sa_mask;
+};
+#endif
+
+typedef union target_sigval {
+ int sival_int;
+ abi_ulong sival_ptr;
+} target_sigval_t;
+#if 0
+#if defined (TARGET_SPARC)
+typedef struct {
+ struct {
+ abi_ulong psr;
+ abi_ulong pc;
+ abi_ulong npc;
+ abi_ulong y;
+ abi_ulong u_regs[16]; /* globals and ins */
+ } si_regs;
+ int si_mask;
+} __siginfo_t;
+
+typedef struct {
+ unsigned long si_float_regs [32];
+ unsigned long si_fsr;
+ unsigned long si_fpqdepth;
+ struct {
+ unsigned long *insn_addr;
+ unsigned long insn;
+ } si_fpqueue [16];
+} __siginfo_fpu_t;
+#endif
+#endif
+
+#define TARGET_SI_MAX_SIZE 128
+#define TARGET_SI_PAD_SIZE ((TARGET_SI_MAX_SIZE/sizeof(int)) - 3)
+
+typedef struct target_siginfo {
+#ifdef TARGET_MIPS
+ int si_signo;
+ int si_code;
+ int si_errno;
+#else
+ int si_signo;
+ int si_errno;
+ int si_code;
+#endif
+
+ union {
+ int _pad[TARGET_SI_PAD_SIZE];
+
+ /* kill() */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ } _kill;
+
+ /* POSIX.1b timers */
+ struct {
+ unsigned int _timer1;
+ unsigned int _timer2;
+ } _timer;
+
+ /* POSIX.1b signals */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ target_sigval_t _sigval;
+ } _rt;
+
+ /* SIGCHLD */
+ struct {
+ pid_t _pid; /* which child */
+ uid_t _uid; /* sender's uid */
+ int _status; /* exit code */
+ target_clock_t _utime;
+ target_clock_t _stime;
+ } _sigchld;
+
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ struct {
+ abi_ulong _addr; /* faulting insn/memory ref. */
+ } _sigfault;
+
+ /* SIGPOLL */
+ struct {
+ int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} target_siginfo_t;
+
+/*
+ * si_code values
+ * Digital reserves positive values for kernel-generated signals.
+ */
+#define TARGET_SI_USER 0 /* sent by kill, sigsend, raise */
+#define TARGET_SI_KERNEL 0x80 /* sent by the kernel from somewhere */
+#define TARGET_SI_QUEUE -1 /* sent by sigqueue */
+#define TARGET_SI_TIMER -2 /* sent by timer expiration */
+#define TARGET_SI_MESGQ -3 /* sent by real time mesq state change */
+#define TARGET_SI_ASYNCIO -4 /* sent by AIO completion */
+#define TARGET_SI_SIGIO -5 /* sent by queued SIGIO */
+
+/*
+ * SIGILL si_codes
+ */
+#define TARGET_ILL_ILLOPC (1) /* illegal opcode */
+#define TARGET_ILL_ILLOPN (2) /* illegal operand */
+#define TARGET_ILL_ILLADR (3) /* illegal addressing mode */
+#define TARGET_ILL_ILLTRP (4) /* illegal trap */
+#define TARGET_ILL_PRVOPC (5) /* privileged opcode */
+#define TARGET_ILL_PRVREG (6) /* privileged register */
+#define TARGET_ILL_COPROC (7) /* coprocessor error */
+#define TARGET_ILL_BADSTK (8) /* internal stack error */
+
+/*
+ * SIGFPE si_codes
+ */
+#define TARGET_FPE_INTDIV (1) /* integer divide by zero */
+#define TARGET_FPE_INTOVF (2) /* integer overflow */
+#define TARGET_FPE_FLTDIV (3) /* floating point divide by zero */
+#define TARGET_FPE_FLTOVF (4) /* floating point overflow */
+#define TARGET_FPE_FLTUND (5) /* floating point underflow */
+#define TARGET_FPE_FLTRES (6) /* floating point inexact result */
+#define TARGET_FPE_FLTINV (7) /* floating point invalid operation */
+#define TARGET_FPE_FLTSUB (8) /* subscript out of range */
+#define TARGET_NSIGFPE 8
+
+/*
+ * SIGSEGV si_codes
+ */
+#define TARGET_SEGV_MAPERR (1) /* address not mapped to object */
+#define TARGET_SEGV_ACCERR (2) /* invalid permissions for mapped object */
+
+/*
+ * SIGBUS si_codes
+ */
+#define TARGET_BUS_ADRALN (1) /* invalid address alignment */
+#define TARGET_BUS_ADRERR (2) /* non-existant physical address */
+#define TARGET_BUS_OBJERR (3) /* object specific hardware error */
+
+/*
+ * SIGTRAP si_codes
+ */
+#define TARGET_TRAP_BRKPT (1) /* process breakpoint */
+#define TARGET_TRAP_TRACE (2) /* process trace trap */
+
+#endif /* defined(TARGET_I386) || defined(TARGET_ARM) */
+
+struct target_rlimit {
+ abi_ulong rlim_cur;
+ abi_ulong rlim_max;
+};
+
+#if defined(TARGET_ALPHA)
+#define TARGET_RLIM_INFINITY 0x7fffffffffffffffull
+#elif defined(TARGET_MIPS) || defined(TARGET_SPARC)
+#define TARGET_RLIM_INFINITY 0x7fffffffUL
+#else
+#define TARGET_RLIM_INFINITY ((target_ulong)~0UL)
+#endif
+
+struct target_pollfd {
+ int fd; /* file descriptor */
+ short events; /* requested events */
+ short revents; /* returned events */
+};
+
+/* virtual terminal ioctls */
+#define TARGET_KIOCSOUND 0x4B2F /* start sound generation (0 for off) */
+#define TARGET_KDMKTONE 0x4B30 /* generate tone */
+#define TARGET_KDGKBTYPE 0x4b33
+#define TARGET_KDSETMODE 0x4b3a
+#define TARGET_KDGKBMODE 0x4b44
+#define TARGET_KDSKBMODE 0x4b45
+#define TARGET_KDGKBENT 0x4B46 /* gets one entry in translation table */
+#define TARGET_KDGKBSENT 0x4B48 /* gets one function key string entry */
+
+#define TARGET_SIOCATMARK 0x8905
+
+/* Networking ioctls */
+#define TARGET_SIOCADDRT 0x890B /* add routing table entry */
+#define TARGET_SIOCDELRT 0x890C /* delete routing table entry */
+#define TARGET_SIOCGIFNAME 0x8910 /* get iface name */
+#define TARGET_SIOCSIFLINK 0x8911 /* set iface channel */
+#define TARGET_SIOCGIFCONF 0x8912 /* get iface list */
+#define TARGET_SIOCGIFFLAGS 0x8913 /* get flags */
+#define TARGET_SIOCSIFFLAGS 0x8914 /* set flags */
+#define TARGET_SIOCGIFADDR 0x8915 /* get PA address */
+#define TARGET_SIOCSIFADDR 0x8916 /* set PA address */
+#define TARGET_SIOCGIFDSTADDR 0x8917 /* get remote PA address */
+#define TARGET_SIOCSIFDSTADDR 0x8918 /* set remote PA address */
+#define TARGET_SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
+#define TARGET_SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
+#define TARGET_SIOCGIFNETMASK 0x891b /* get network PA mask */
+#define TARGET_SIOCSIFNETMASK 0x891c /* set network PA mask */
+#define TARGET_SIOCGIFMETRIC 0x891d /* get metric */
+#define TARGET_SIOCSIFMETRIC 0x891e /* set metric */
+#define TARGET_SIOCGIFMEM 0x891f /* get memory address (BSD) */
+#define TARGET_SIOCSIFMEM 0x8920 /* set memory address (BSD) */
+#define TARGET_SIOCGIFMTU 0x8921 /* get MTU size */
+#define TARGET_SIOCSIFMTU 0x8922 /* set MTU size */
+#define TARGET_SIOCSIFHWADDR 0x8924 /* set hardware address (NI) */
+#define TARGET_SIOCGIFENCAP 0x8925 /* get/set slip encapsulation */
+#define TARGET_SIOCSIFENCAP 0x8926
+#define TARGET_SIOCGIFHWADDR 0x8927 /* Get hardware address */
+#define TARGET_SIOCGIFSLAVE 0x8929 /* Driver slaving support */
+#define TARGET_SIOCSIFSLAVE 0x8930
+#define TARGET_SIOCADDMULTI 0x8931 /* Multicast address lists */
+#define TARGET_SIOCDELMULTI 0x8932
+
+/* Bridging control calls */
+#define TARGET_SIOCGIFBR 0x8940 /* Bridging support */
+#define TARGET_SIOCSIFBR 0x8941 /* Set bridging options */
+
+#define TARGET_SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */
+#define TARGET_SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */
+
+/* ARP cache control calls. */
+#define TARGET_OLD_SIOCDARP 0x8950 /* old delete ARP table entry */
+#define TARGET_OLD_SIOCGARP 0x8951 /* old get ARP table entry */
+#define TARGET_OLD_SIOCSARP 0x8952 /* old set ARP table entry */
+#define TARGET_SIOCDARP 0x8953 /* delete ARP table entry */
+#define TARGET_SIOCGARP 0x8954 /* get ARP table entry */
+#define TARGET_SIOCSARP 0x8955 /* set ARP table entry */
+
+/* RARP cache control calls. */
+#define TARGET_SIOCDRARP 0x8960 /* delete RARP table entry */
+#define TARGET_SIOCGRARP 0x8961 /* get RARP table entry */
+#define TARGET_SIOCSRARP 0x8962 /* set RARP table entry */
+
+/* Driver configuration calls */
+#define TARGET_SIOCGIFMAP 0x8970 /* Get device parameters */
+#define TARGET_SIOCSIFMAP 0x8971 /* Set device parameters */
+
+/* DLCI configuration calls */
+#define TARGET_SIOCADDDLCI 0x8980 /* Create new DLCI device */
+#define TARGET_SIOCDELDLCI 0x8981 /* Delete DLCI device */
+
+
+/* From <linux/fs.h> */
+
+#define TARGET_BLKROSET TARGET_IO(0x12,93) /* set device read-only (0 = read-write) */
+#define TARGET_BLKROGET TARGET_IO(0x12,94) /* get read-only status (0 = read_write) */
+#define TARGET_BLKRRPART TARGET_IO(0x12,95) /* re-read partition table */
+#define TARGET_BLKGETSIZE TARGET_IO(0x12,96) /* return device size /512 (long *arg) */
+#define TARGET_BLKFLSBUF TARGET_IO(0x12,97) /* flush buffer cache */
+#define TARGET_BLKRASET TARGET_IO(0x12,98) /* Set read ahead for block device */
+#define TARGET_BLKRAGET TARGET_IO(0x12,99) /* get current read ahead setting */
+#define TARGET_BLKFRASET TARGET_IO(0x12,100)/* set filesystem (mm/filemap.c) read-ahead */
+#define TARGET_BLKFRAGET TARGET_IO(0x12,101)/* get filesystem (mm/filemap.c) read-ahead */
+#define TARGET_BLKSECTSET TARGET_IO(0x12,102)/* set max sectors per request (ll_rw_blk.c) */
+#define TARGET_BLKSECTGET TARGET_IO(0x12,103)/* get max sectors per request (ll_rw_blk.c) */
+#define TARGET_BLKSSZGET TARGET_IO(0x12,104)/* get block device sector size */
+/* A jump here: 108-111 have been used for various private purposes. */
+#define TARGET_BLKBSZGET TARGET_IOR(0x12,112,sizeof(int))
+#define TARGET_BLKBSZSET TARGET_IOW(0x12,113,sizeof(int))
+#define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */
+#define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */
+#define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */
+#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap)
+
+/* cdrom commands */
+#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */
+#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */
+#define TARGET_CDROMPLAYMSF 0x5303 /* Play Audio MSF (struct cdrom_msf) */
+#define TARGET_CDROMPLAYTRKIND 0x5304 /* Play Audio Track/index
+ (struct cdrom_ti) */
+#define TARGET_CDROMREADTOCHDR 0x5305 /* Read TOC header
+ (struct cdrom_tochdr) */
+#define TARGET_CDROMREADTOCENTRY 0x5306 /* Read TOC entry
+ (struct cdrom_tocentry) */
+#define TARGET_CDROMSTOP 0x5307 /* Stop the cdrom drive */
+#define TARGET_CDROMSTART 0x5308 /* Start the cdrom drive */
+#define TARGET_CDROMEJECT 0x5309 /* Ejects the cdrom media */
+#define TARGET_CDROMVOLCTRL 0x530a /* Control output volume
+ (struct cdrom_volctrl) */
+#define TARGET_CDROMSUBCHNL 0x530b /* Read subchannel data
+ (struct cdrom_subchnl) */
+#define TARGET_CDROMREADMODE2 0x530c /* Read TARGET_CDROM mode 2 data (2336 Bytes)
+ (struct cdrom_read) */
+#define TARGET_CDROMREADMODE1 0x530d /* Read TARGET_CDROM mode 1 data (2048 Bytes)
+ (struct cdrom_read) */
+#define TARGET_CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */
+#define TARGET_CDROMEJECT_SW 0x530f /* enable(1)/disable(0) auto-ejecting */
+#define TARGET_CDROMMULTISESSION 0x5310 /* Obtain the start-of-last-session
+ address of multi session disks
+ (struct cdrom_multisession) */
+#define TARGET_CDROM_GET_MCN 0x5311 /* Obtain the "Universal Product Code"
+ if available (struct cdrom_mcn) */
+#define TARGET_CDROM_GET_UPC TARGET_CDROM_GET_MCN /* This one is depricated,
+ but here anyway for compatability */
+#define TARGET_CDROMRESET 0x5312 /* hard-reset the drive */
+#define TARGET_CDROMVOLREAD 0x5313 /* Get the drive's volume setting
+ (struct cdrom_volctrl) */
+#define TARGET_CDROMREADRAW 0x5314 /* read data in raw mode (2352 Bytes)
+ (struct cdrom_read) */
+/*
+ * These ioctls are used only used in aztcd.c and optcd.c
+ */
+#define TARGET_CDROMREADCOOKED 0x5315 /* read data in cooked mode */
+#define TARGET_CDROMSEEK 0x5316 /* seek msf address */
+
+/*
+ * This ioctl is only used by the scsi-cd driver.
+ It is for playing audio in logical block addressing mode.
+ */
+#define TARGET_CDROMPLAYBLK 0x5317 /* (struct cdrom_blk) */
+
+/*
+ * These ioctls are only used in optcd.c
+ */
+#define TARGET_CDROMREADALL 0x5318 /* read all 2646 bytes */
+
+/*
+ * These ioctls are (now) only in ide-cd.c for controlling
+ * drive spindown time. They should be implemented in the
+ * Uniform driver, via generic packet commands, GPCMD_MODE_SELECT_10,
+ * GPCMD_MODE_SENSE_10 and the GPMODE_POWER_PAGE...
+ * -Erik
+ */
+#define TARGET_CDROMGETSPINDOWN 0x531d
+#define TARGET_CDROMSETSPINDOWN 0x531e
+
+/*
+ * These ioctls are implemented through the uniform CD-ROM driver
+ * They _will_ be adopted by all CD-ROM drivers, when all the CD-ROM
+ * drivers are eventually ported to the uniform CD-ROM driver interface.
+ */
+#define TARGET_CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
+#define TARGET_CDROM_SET_OPTIONS 0x5320 /* Set behavior options */
+#define TARGET_CDROM_CLEAR_OPTIONS 0x5321 /* Clear behavior options */
+#define TARGET_CDROM_SELECT_SPEED 0x5322 /* Set the CD-ROM speed */
+#define TARGET_CDROM_SELECT_DISC 0x5323 /* Select disc (for juke-boxes) */
+#define TARGET_CDROM_MEDIA_CHANGED 0x5325 /* Check is media changed */
+#define TARGET_CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
+#define TARGET_CDROM_DISC_STATUS 0x5327 /* Get disc type, etc. */
+#define TARGET_CDROM_CHANGER_NSLOTS 0x5328 /* Get number of slots */
+#define TARGET_CDROM_LOCKDOOR 0x5329 /* lock or unlock door */
+#define TARGET_CDROM_DEBUG 0x5330 /* Turn debug messages on/off */
+#define TARGET_CDROM_GET_CAPABILITY 0x5331 /* get capabilities */
+
+/* Note that scsi/scsi_ioctl.h also uses 0x5382 - 0x5386.
+ * Future CDROM ioctls should be kept below 0x537F
+ */
+
+/* This ioctl is only used by sbpcd at the moment */
+#define TARGET_CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */
+ /* conflict with SCSI_IOCTL_GET_IDLUN */
+
+/* DVD-ROM Specific ioctls */
+#define TARGET_DVD_READ_STRUCT 0x5390 /* Read structure */
+#define TARGET_DVD_WRITE_STRUCT 0x5391 /* Write structure */
+#define TARGET_DVD_AUTH 0x5392 /* Authentication */
+
+#define TARGET_CDROM_SEND_PACKET 0x5393 /* send a packet to the drive */
+#define TARGET_CDROM_NEXT_WRITABLE 0x5394 /* get next writable block */
+#define TARGET_CDROM_LAST_WRITTEN 0x5395 /* get last block written on disc */
+
+/* HD commands */
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define TARGET_HDIO_GETGEO 0x0301 /* get device geometry */
+#define TARGET_HDIO_GET_UNMASKINTR 0x0302 /* get current unmask setting */
+#define TARGET_HDIO_GET_MULTCOUNT 0x0304 /* get current IDE blockmode setting */
+#define TARGET_HDIO_GET_KEEPSETTINGS 0x0308 /* get keep-settings-on-reset flag */
+#define TARGET_HDIO_GET_32BIT 0x0309 /* get current io_32bit setting */
+#define TARGET_HDIO_GET_NOWERR 0x030a /* get ignore-write-error flag */
+#define TARGET_HDIO_GET_DMA 0x030b /* get use-dma flag */
+#define TARGET_HDIO_GET_IDENTITY 0x030d /* get IDE identification info */
+#define TARGET_HDIO_DRIVE_CMD 0x031f /* execute a special drive command */
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define TARGET_HDIO_SET_MULTCOUNT 0x0321 /* change IDE blockmode */
+#define TARGET_HDIO_SET_UNMASKINTR 0x0322 /* permit other irqs during I/O */
+#define TARGET_HDIO_SET_KEEPSETTINGS 0x0323 /* keep ioctl settings on reset */
+#define TARGET_HDIO_SET_32BIT 0x0324 /* change io_32bit flags */
+#define TARGET_HDIO_SET_NOWERR 0x0325 /* change ignore-write-error flag */
+#define TARGET_HDIO_SET_DMA 0x0326 /* change use-dma flag */
+#define TARGET_HDIO_SET_PIO_MODE 0x0327 /* reconfig interface to new speed */
+
+/* loop ioctls */
+#define TARGET_LOOP_SET_FD 0x4C00
+#define TARGET_LOOP_CLR_FD 0x4C01
+#define TARGET_LOOP_SET_STATUS 0x4C02
+#define TARGET_LOOP_GET_STATUS 0x4C03
+#define TARGET_LOOP_SET_STATUS64 0x4C04
+#define TARGET_LOOP_GET_STATUS64 0x4C05
+#define TARGET_LOOP_CHANGE_FD 0x4C06
+
+/* fb ioctls */
+#define TARGET_FBIOGET_VSCREENINFO 0x4600
+#define TARGET_FBIOPUT_VSCREENINFO 0x4601
+#define TARGET_FBIOGET_FSCREENINFO 0x4602
+
+/* vt ioctls */
+#define TARGET_VT_OPENQRY 0x5600
+#define TARGET_VT_GETSTATE 0x5603
+#define TARGET_VT_ACTIVATE 0x5606
+#define TARGET_VT_WAITACTIVE 0x5607
+#define TARGET_VT_LOCKSWITCH 0x560b
+#define TARGET_VT_UNLOCKSWITCH 0x560c
+
+/* from asm/termbits.h */
+
+#define TARGET_NCC 8
+struct target_termio {
+ unsigned short c_iflag; /* input mode flags */
+ unsigned short c_oflag; /* output mode flags */
+ unsigned short c_cflag; /* control mode flags */
+ unsigned short c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[TARGET_NCC]; /* control characters */
+};
+
+struct target_winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel;
+ unsigned short ws_ypixel;
+};
+
+#include "termbits.h"
+
+#if defined(TARGET_MIPS)
+#define TARGET_PROT_SEM 0x10
+#else
+#define TARGET_PROT_SEM 0x08
+#endif
+
+/* Common */
+#define TARGET_MAP_SHARED 0x01 /* Share changes */
+#define TARGET_MAP_PRIVATE 0x02 /* Changes are private */
+#define TARGET_MAP_TYPE 0x0f /* Mask for type of mapping */
+
+/* Target specific */
+#if defined(TARGET_MIPS)
+#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
+#define TARGET_MAP_ANONYMOUS 0x0800 /* don't use a file */
+#define TARGET_MAP_GROWSDOWN 0x1000 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x2000 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x4000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x8000 /* pages are locked */
+#define TARGET_MAP_NORESERVE 0x0400 /* don't check for reservations */
+#define TARGET_MAP_POPULATE 0x10000 /* populate (prefault) pagetables */
+#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */
+#elif defined(TARGET_PPC)
+#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
+#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */
+#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x0080 /* pages are locked */
+#define TARGET_MAP_NORESERVE 0x0040 /* don't check for reservations */
+#define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
+#define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */
+#elif defined(TARGET_ALPHA)
+#define TARGET_MAP_ANONYMOUS 0x10 /* don't use a file */
+#define TARGET_MAP_FIXED 0x100 /* Interpret addr exactly */
+#define TARGET_MAP_GROWSDOWN 0x01000 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x02000 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x04000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x08000 /* lock the mapping */
+#define TARGET_MAP_NORESERVE 0x10000 /* no check for reservations */
+#define TARGET_MAP_POPULATE 0x20000 /* pop (prefault) pagetables */
+#define TARGET_MAP_NONBLOCK 0x40000 /* do not block on IO */
+#else
+#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
+#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */
+#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */
+#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */
+#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */
+#define TARGET_MAP_LOCKED 0x2000 /* pages are locked */
+#define TARGET_MAP_NORESERVE 0x4000 /* don't check for reservations */
+#define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
+#define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */
+#endif
+
+#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_CRIS)
+struct target_stat {
+ unsigned short st_dev;
+ unsigned short __pad1;
+ abi_ulong st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ unsigned short __pad2;
+ abi_ulong st_size;
+ abi_ulong st_blksize;
+ abi_ulong st_blocks;
+ abi_ulong target_st_atime;
+ abi_ulong __unused1;
+ abi_ulong target_st_mtime;
+ abi_ulong __unused2;
+ abi_ulong target_st_ctime;
+ abi_ulong __unused3;
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
+/* This matches struct stat64 in glibc2.1, hence the absolutely
+ * insane amounts of padding around dev_t's.
+ */
+struct target_stat64 {
+ unsigned short st_dev;
+ unsigned char __pad0[10];
+
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ abi_ulong __st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ abi_ulong st_uid;
+ abi_ulong st_gid;
+
+ unsigned short st_rdev;
+ unsigned char __pad3[10];
+
+ long long st_size;
+ abi_ulong st_blksize;
+
+ abi_ulong st_blocks; /* Number 512-byte blocks allocated. */
+ abi_ulong __pad4; /* future possible st_blocks high bits */
+
+ abi_ulong target_st_atime;
+ abi_ulong __pad5;
+
+ abi_ulong target_st_mtime;
+ abi_ulong __pad6;
+
+ abi_ulong target_st_ctime;
+ abi_ulong __pad7; /* will be high 32 bits of ctime someday */
+
+ unsigned long long st_ino;
+} __attribute__((packed));
+
+#ifdef TARGET_ARM
+struct target_eabi_stat64 {
+ unsigned long long st_dev;
+ unsigned int __pad1;
+ abi_ulong __st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ abi_ulong st_uid;
+ abi_ulong st_gid;
+
+ unsigned long long st_rdev;
+ unsigned int __pad2[2];
+
+ long long st_size;
+ abi_ulong st_blksize;
+ unsigned int __pad3;
+ unsigned long long st_blocks;
+
+ 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;
+
+ unsigned long long st_ino;
+} __attribute__ ((packed));
+#endif
+
+#elif defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
+struct target_stat {
+ unsigned int st_dev;
+ abi_ulong st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned int st_rdev;
+ abi_long st_size;
+ abi_long target_st_atime;
+ abi_long target_st_mtime;
+ abi_long target_st_ctime;
+ abi_long st_blksize;
+ abi_long st_blocks;
+ abi_ulong __unused4[2];
+};
+
+struct target_stat64 {
+ unsigned char __pad0[6];
+ unsigned short st_dev;
+
+ uint64_t st_ino;
+ uint64_t st_nlink;
+
+ unsigned int st_mode;
+
+ unsigned int st_uid;
+ unsigned int st_gid;
+
+ unsigned char __pad2[6];
+ unsigned short st_rdev;
+
+ int64_t st_size;
+ int64_t st_blksize;
+
+ unsigned char __pad4[4];
+ unsigned int st_blocks;
+
+ abi_ulong target_st_atime;
+ abi_ulong __unused1;
+
+ abi_ulong target_st_mtime;
+ abi_ulong __unused2;
+
+ abi_ulong target_st_ctime;
+ abi_ulong __unused3;
+
+ abi_ulong __unused4[3];
+};
+
+#elif defined(TARGET_SPARC)
+
+struct target_stat {
+ unsigned short st_dev;
+ abi_ulong st_ino;
+ unsigned short st_mode;
+ short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ abi_long st_size;
+ abi_long target_st_atime;
+ abi_ulong __unused1;
+ abi_long target_st_mtime;
+ abi_ulong __unused2;
+ abi_long target_st_ctime;
+ abi_ulong __unused3;
+ abi_long st_blksize;
+ abi_long st_blocks;
+ abi_ulong __unused4[2];
+};
+
+struct target_stat64 {
+ unsigned char __pad0[6];
+ unsigned short st_dev;
+
+ uint64_t st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ unsigned int st_uid;
+ unsigned int st_gid;
+
+ unsigned char __pad2[6];
+ unsigned short st_rdev;
+
+ unsigned char __pad3[8];
+
+ int64_t st_size;
+ unsigned int st_blksize;
+
+ unsigned char __pad4[8];
+ unsigned int st_blocks;
+
+ unsigned int target_st_atime;
+ unsigned int __unused1;
+
+ unsigned int target_st_mtime;
+ unsigned int __unused2;
+
+ unsigned int target_st_ctime;
+ unsigned int __unused3;
+
+ unsigned int __unused4;
+ unsigned int __unused5;
+};
+
+#elif defined(TARGET_PPC)
+
+struct target_stat {
+ abi_ulong st_dev;
+ abi_ulong st_ino;
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ abi_ulong st_nlink;
+ unsigned int st_mode;
+#else
+ unsigned int st_mode;
+ unsigned short st_nlink;
+#endif
+ unsigned int st_uid;
+ unsigned int st_gid;
+ abi_ulong st_rdev;
+ abi_ulong st_size;
+ abi_ulong st_blksize;
+ abi_ulong st_blocks;
+ 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 __unused4;
+ abi_ulong __unused5;
+#if defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ abi_ulong __unused6;
+#endif
+};
+
+struct __attribute__((__packed__)) target_stat64 {
+ unsigned long long st_dev;
+ unsigned long long st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned long long st_rdev;
+ unsigned long long __pad0;
+ long long st_size;
+ int st_blksize;
+ unsigned int __pad1;
+ long long st_blocks; /* Number 512-byte blocks allocated. */
+ int target_st_atime;
+ unsigned int target_st_atime_nsec;
+ int target_st_mtime;
+ unsigned int target_st_mtime_nsec;
+ int target_st_ctime;
+ unsigned int target_st_ctime_nsec;
+ unsigned int __unused4;
+ unsigned int __unused5;
+};
+
+#elif defined(TARGET_MICROBLAZE)
+
+struct target_stat {
+ abi_ulong st_dev;
+ abi_ulong st_ino;
+ unsigned int st_mode;
+ unsigned short st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ abi_ulong st_rdev;
+ abi_ulong st_size;
+ abi_ulong st_blksize;
+ abi_ulong st_blocks;
+ 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 __unused4;
+ abi_ulong __unused5;
+};
+
+/* FIXME: Microblaze no-mmu user-space has a difference stat64 layout... */
+struct __attribute__((__packed__)) target_stat64 {
+ uint64_t st_dev;
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ uint32_t pad0;
+ uint32_t __st_ino;
+
+ uint32_t st_mode;
+ uint32_t st_nlink;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_rdev;
+ uint64_t __pad1;
+
+ int64_t st_size;
+ int32_t st_blksize;
+ uint32_t __pad2;
+ int64_t st_blocks; /* Number 512-byte blocks allocated. */
+
+ int target_st_atime;
+ unsigned int target_st_atime_nsec;
+ int target_st_mtime;
+ unsigned int target_st_mtime_nsec;
+ int target_st_ctime;
+ unsigned int target_st_ctime_nsec;
+ uint64_t st_ino;
+};
+
+#elif defined(TARGET_M68K)
+
+struct target_stat {
+ unsigned short st_dev;
+ unsigned short __pad1;
+ abi_ulong st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ unsigned short __pad2;
+ abi_ulong st_size;
+ abi_ulong st_blksize;
+ abi_ulong st_blocks;
+ abi_ulong target_st_atime;
+ abi_ulong __unused1;
+ abi_ulong target_st_mtime;
+ abi_ulong __unused2;
+ abi_ulong target_st_ctime;
+ abi_ulong __unused3;
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
+/* This matches struct stat64 in glibc2.1, hence the absolutely
+ * insane amounts of padding around dev_t's.
+ */
+struct target_stat64 {
+ unsigned long long st_dev;
+ unsigned char __pad1[2];
+
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ abi_ulong __st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ abi_ulong st_uid;
+ abi_ulong st_gid;
+
+ unsigned long long st_rdev;
+ unsigned char __pad3[2];
+
+ long long st_size;
+ abi_ulong st_blksize;
+
+ abi_ulong __pad4; /* future possible st_blocks high bits */
+ abi_ulong st_blocks; /* Number 512-byte blocks allocated. */
+
+ 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;
+
+ unsigned long long st_ino;
+} __attribute__((packed));
+
+#elif defined(TARGET_ABI_MIPSN64)
+
+/* The memory layout is the same as of struct stat64 of the 32-bit kernel. */
+struct target_stat {
+ unsigned int st_dev;
+ unsigned int st_pad0[3]; /* Reserved for st_dev expansion */
+
+ abi_ulong st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ int st_uid;
+ int st_gid;
+
+ unsigned int st_rdev;
+ unsigned int st_pad1[3]; /* Reserved for st_rdev expansion */
+
+ abi_ulong st_size;
+
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ unsigned int target_st_atime;
+ unsigned int target_st_atime_nsec;
+
+ unsigned int target_st_mtime;
+ unsigned int target_st_mtime_nsec;
+
+ unsigned int target_st_ctime;
+ unsigned int target_st_ctime_nsec;
+
+ unsigned int st_blksize;
+ unsigned int st_pad2;
+
+ abi_ulong st_blocks;
+};
+
+#elif defined(TARGET_ABI_MIPSN32)
+
+struct target_stat {
+ unsigned st_dev;
+ int st_pad1[3]; /* Reserved for network id */
+ unsigned int st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ int st_uid;
+ int st_gid;
+ unsigned st_rdev;
+ unsigned int st_pad2[2];
+ unsigned int st_size;
+ unsigned int st_pad3;
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ unsigned int target_st_atime;
+ unsigned int target_st_atime_nsec;
+ unsigned int target_st_mtime;
+ unsigned int target_st_mtime_nsec;
+ unsigned int target_st_ctime;
+ unsigned int target_st_ctime_nsec;
+ unsigned int st_blksize;
+ unsigned int st_blocks;
+ unsigned int st_pad4[14];
+};
+
+/*
+ * This matches struct stat64 in glibc2.1, hence the absolutely insane
+ * amounts of padding around dev_t's. The memory layout is the same as of
+ * struct stat of the 64-bit kernel.
+ */
+
+struct target_stat64 {
+ unsigned int st_dev;
+ unsigned int st_pad0[3]; /* Reserved for st_dev expansion */
+
+ target_ulong st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ int st_uid;
+ int st_gid;
+
+ unsigned int st_rdev;
+ unsigned int st_pad1[3]; /* Reserved for st_rdev expansion */
+
+ int st_size;
+
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ int target_st_atime;
+ unsigned int target_st_atime_nsec; /* Reserved for st_atime expansion */
+
+ int target_st_mtime;
+ unsigned int target_st_mtime_nsec; /* Reserved for st_mtime expansion */
+
+ int target_st_ctime;
+ unsigned int target_st_ctime_nsec; /* Reserved for st_ctime expansion */
+
+ unsigned int st_blksize;
+ unsigned int st_pad2;
+
+ int st_blocks;
+};
+
+#elif defined(TARGET_ABI_MIPSO32)
+
+struct target_stat {
+ unsigned st_dev;
+ abi_long st_pad1[3]; /* Reserved for network id */
+ abi_ulong st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ int st_uid;
+ int st_gid;
+ unsigned st_rdev;
+ abi_long st_pad2[2];
+ abi_long st_size;
+ abi_long st_pad3;
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ abi_long target_st_atime;
+ abi_long target_st_atime_nsec;
+ abi_long target_st_mtime;
+ abi_long target_st_mtime_nsec;
+ abi_long target_st_ctime;
+ abi_long target_st_ctime_nsec;
+ abi_long st_blksize;
+ abi_long st_blocks;
+ abi_long st_pad4[14];
+};
+
+/*
+ * This matches struct stat64 in glibc2.1, hence the absolutely insane
+ * amounts of padding around dev_t's. The memory layout is the same as of
+ * struct stat of the 64-bit kernel.
+ */
+
+struct target_stat64 {
+ abi_ulong st_dev;
+ abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */
+
+ uint64_t st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ int st_uid;
+ int st_gid;
+
+ abi_ulong st_rdev;
+ abi_ulong st_pad1[3]; /* Reserved for st_rdev expansion */
+
+ int64_t st_size;
+
+ /*
+ * Actually this should be timestruc_t st_atime, st_mtime and st_ctime
+ * but we don't have it under Linux.
+ */
+ abi_long target_st_atime;
+ abi_ulong target_st_atime_nsec; /* Reserved for st_atime expansion */
+
+ abi_long target_st_mtime;
+ abi_ulong target_st_mtime_nsec; /* Reserved for st_mtime expansion */
+
+ abi_long target_st_ctime;
+ abi_ulong target_st_ctime_nsec; /* Reserved for st_ctime expansion */
+
+ abi_ulong st_blksize;
+ abi_ulong st_pad2;
+
+ int64_t st_blocks;
+};
+
+#elif defined(TARGET_ALPHA)
+
+struct target_stat {
+ unsigned int st_dev;
+ unsigned int st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned int st_rdev;
+ abi_long st_size;
+ abi_ulong target_st_atime;
+ abi_ulong target_st_mtime;
+ abi_ulong target_st_ctime;
+ unsigned int st_blksize;
+ unsigned int st_blocks;
+ unsigned int st_flags;
+ unsigned int st_gen;
+};
+
+struct target_stat64 {
+ abi_ulong st_dev;
+ abi_ulong st_ino;
+ abi_ulong st_rdev;
+ abi_long st_size;
+ abi_ulong st_blocks;
+
+ unsigned int st_mode;
+ unsigned int st_uid;
+ unsigned int st_gid;
+ unsigned int st_blksize;
+ unsigned int st_nlink;
+ unsigned int __pad0;
+
+ 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_long __unused[3];
+};
+
+#elif defined(TARGET_SH4)
+
+struct target_stat {
+ abi_ulong st_dev;
+ abi_ulong st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ abi_ulong st_rdev;
+ abi_ulong st_size;
+ abi_ulong st_blksize;
+ abi_ulong st_blocks;
+ 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 __unused4;
+ abi_ulong __unused5;
+};
+
+/* This matches struct stat64 in glibc2.1, hence the absolutely
+ * insane amounts of padding around dev_t's.
+ */
+struct __attribute__((__packed__)) target_stat64 {
+ unsigned long long st_dev;
+ unsigned char __pad0[4];
+
+#define TARGET_STAT64_HAS_BROKEN_ST_INO 1
+ abi_ulong __st_ino;
+
+ unsigned int st_mode;
+ unsigned int st_nlink;
+
+ abi_ulong st_uid;
+ abi_ulong st_gid;
+
+ unsigned long long st_rdev;
+ unsigned char __pad3[4];
+
+ long long st_size;
+ abi_ulong st_blksize;
+
+ unsigned long long st_blocks; /* Number 512-byte blocks allocated. */
+
+ 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;
+
+ unsigned long long st_ino;
+};
+
+#elif defined(TARGET_I386) && !defined(TARGET_ABI32)
+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 __pad0;
+ abi_ulong st_rdev;
+ abi_long st_size;
+ abi_long st_blksize;
+ abi_long st_blocks; /* Number 512-byte blocks allocated. */
+
+ 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_long __unused[3];
+};
+#else
+#error unsupported CPU
+#endif
+
+typedef struct {
+ int val[2];
+} target_fsid_t;
+
+#ifdef TARGET_MIPS
+#ifdef TARGET_ABI_MIPSN32
+struct target_statfs {
+ int32_t f_type;
+ int32_t f_bsize;
+ int32_t f_frsize; /* Fragment size - unsupported */
+ int32_t f_blocks;
+ int32_t f_bfree;
+ int32_t f_files;
+ int32_t f_ffree;
+ int32_t f_bavail;
+
+ /* Linux specials */
+ target_fsid_t f_fsid;
+ int32_t f_namelen;
+ int32_t f_spare[6];
+};
+#else
+struct target_statfs {
+ abi_long f_type;
+ abi_long f_bsize;
+ abi_long f_frsize; /* Fragment size - unsupported */
+ abi_long f_blocks;
+ abi_long f_bfree;
+ abi_long f_files;
+ abi_long f_ffree;
+ abi_long f_bavail;
+
+ /* Linux specials */
+ target_fsid_t f_fsid;
+ abi_long f_namelen;
+ abi_long f_spare[6];
+};
+#endif
+
+struct target_statfs64 {
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint32_t f_frsize; /* Fragment size - unsupported */
+ uint32_t __pad;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ uint64_t f_bavail;
+ target_fsid_t f_fsid;
+ uint32_t f_namelen;
+ uint32_t f_spare[6];
+};
+#elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \
+ defined(TARGET_SPARC64)) && !defined(TARGET_ABI32)
+struct target_statfs {
+ abi_long f_type;
+ abi_long f_bsize;
+ abi_long f_blocks;
+ abi_long f_bfree;
+ abi_long f_bavail;
+ abi_long f_files;
+ abi_long f_ffree;
+ target_fsid_t f_fsid;
+ abi_long f_namelen;
+ abi_long f_frsize;
+ abi_long f_spare[5];
+};
+
+struct target_statfs64 {
+ abi_long f_type;
+ abi_long f_bsize;
+ abi_long f_blocks;
+ abi_long f_bfree;
+ abi_long f_bavail;
+ abi_long f_files;
+ abi_long f_ffree;
+ target_fsid_t f_fsid;
+ abi_long f_namelen;
+ abi_long f_frsize;
+ abi_long f_spare[5];
+};
+#else
+struct target_statfs {
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint32_t f_blocks;
+ uint32_t f_bfree;
+ uint32_t f_bavail;
+ uint32_t f_files;
+ uint32_t f_ffree;
+ target_fsid_t f_fsid;
+ uint32_t f_namelen;
+ uint32_t f_frsize;
+ uint32_t f_spare[5];
+};
+
+struct target_statfs64 {
+ uint32_t f_type;
+ uint32_t f_bsize;
+ uint64_t f_blocks;
+ uint64_t f_bfree;
+ uint64_t f_bavail;
+ uint64_t f_files;
+ uint64_t f_ffree;
+ target_fsid_t f_fsid;
+ uint32_t f_namelen;
+ uint32_t f_frsize;
+ uint32_t f_spare[5];
+};
+#endif
+
+
+#define TARGET_F_DUPFD 0 /* dup */
+#define TARGET_F_GETFD 1 /* get close_on_exec */
+#define TARGET_F_SETFD 2 /* set/clear close_on_exec */
+#define TARGET_F_GETFL 3 /* get file->f_flags */
+#define TARGET_F_SETFL 4 /* set file->f_flags */
+
+#if defined(TARGET_ALPHA)
+#define TARGET_F_GETLK 7
+#define TARGET_F_SETLK 8
+#define TARGET_F_SETLKW 9
+#define TARGET_F_SETOWN 5 /* for sockets. */
+#define TARGET_F_GETOWN 6 /* for sockets. */
+#elif defined(TARGET_MIPS)
+#define TARGET_F_GETLK 14
+#define TARGET_F_SETLK 6
+#define TARGET_F_SETLKW 7
+#define TARGET_F_SETOWN 24 /* for sockets. */
+#define TARGET_F_GETOWN 25 /* for sockets. */
+#else
+#define TARGET_F_GETLK 5
+#define TARGET_F_SETLK 6
+#define TARGET_F_SETLKW 7
+#define TARGET_F_SETOWN 8 /* for sockets. */
+#define TARGET_F_GETOWN 9 /* for sockets. */
+#endif
+
+#define TARGET_F_SETSIG 10 /* for sockets. */
+#define TARGET_F_GETSIG 11 /* for sockets. */
+
+#if defined(TARGET_MIPS)
+#define TARGET_F_GETLK64 33 /* using 'struct flock64' */
+#define TARGET_F_SETLK64 34
+#define TARGET_F_SETLKW64 35
+#else
+#define TARGET_F_GETLK64 12 /* using 'struct flock64' */
+#define TARGET_F_SETLK64 13
+#define TARGET_F_SETLKW64 14
+#endif
+
+#define TARGET_F_LINUX_SPECIFIC_BASE 1024
+#define TARGET_F_SETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 0)
+#define TARGET_F_GETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 1)
+#define TARGET_F_DUPFD_CLOEXEC (TARGET_F_LINUX_SPECIFIC_BASE + 6)
+#define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2)
+
+#if defined (TARGET_ARM)
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECTORY 040000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */
+#define TARGET_O_DIRECT 0200000 /* direct disk access hint */
+#define TARGET_O_LARGEFILE 0400000
+#elif defined (TARGET_PPC)
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECTORY 040000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */
+#define TARGET_O_LARGEFILE 0200000
+#define TARGET_O_DIRECT 0400000 /* direct disk access hint */
+#elif defined (TARGET_MICROBLAZE)
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECTORY 040000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */
+#define TARGET_O_LARGEFILE 0200000
+#define TARGET_O_DIRECT 0400000 /* direct disk access hint */
+#elif defined (TARGET_SPARC)
+#define TARGET_O_RDONLY 0x0000
+#define TARGET_O_WRONLY 0x0001
+#define TARGET_O_RDWR 0x0002
+#define TARGET_O_ACCMODE 0x0003
+#define TARGET_O_APPEND 0x0008
+#define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */
+#define TARGET_O_CREAT 0x0200 /* not fcntl */
+#define TARGET_O_TRUNC 0x0400 /* not fcntl */
+#define TARGET_O_EXCL 0x0800 /* not fcntl */
+#define TARGET_O_SYNC 0x2000
+#define TARGET_O_NONBLOCK 0x4000
+#define TARGET_O_NDELAY (0x0004 | TARGET_O_NONBLOCK)
+#define TARGET_O_NOCTTY 0x8000 /* not fcntl */
+#define TARGET_O_DIRECTORY 0x10000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0x20000 /* don't follow links */
+#define TARGET_O_LARGEFILE 0x40000
+#define TARGET_O_DIRECT 0x100000 /* direct disk access hint */
+#elif defined(TARGET_MIPS)
+#define TARGET_O_ACCMODE 0x0003
+#define TARGET_O_RDONLY 0x0000
+#define TARGET_O_WRONLY 0x0001
+#define TARGET_O_RDWR 0x0002
+#define TARGET_O_APPEND 0x0008
+#define TARGET_O_SYNC 0x0010
+#define TARGET_O_NONBLOCK 0x0080
+#define TARGET_O_CREAT 0x0100 /* not fcntl */
+#define TARGET_O_TRUNC 0x0200 /* not fcntl */
+#define TARGET_O_EXCL 0x0400 /* not fcntl */
+#define TARGET_O_NOCTTY 0x0800 /* not fcntl */
+#define TARGET_FASYNC 0x1000 /* fcntl, for BSD compatibility */
+#define TARGET_O_LARGEFILE 0x2000 /* allow large file opens */
+#define TARGET_O_DIRECT 0x8000 /* direct disk access hint */
+#define TARGET_O_DIRECTORY 0x10000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0x20000 /* don't follow links */
+#define TARGET_O_NOATIME 0x40000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#elif defined(TARGET_ALPHA)
+#define TARGET_O_ACCMODE 0x0003
+#define TARGET_O_RDONLY 0x0000
+#define TARGET_O_WRONLY 0x0001
+#define TARGET_O_RDWR 0x0002
+#define TARGET_O_APPEND 0x0008
+#define TARGET_O_SYNC 0x4000
+#define TARGET_O_NONBLOCK 0x0004
+#define TARGET_O_CREAT 0x0200 /* not fcntl */
+#define TARGET_O_TRUNC 0x0400 /* not fcntl */
+#define TARGET_O_EXCL 0x0800 /* not fcntl */
+#define TARGET_O_NOCTTY 0x1000 /* not fcntl */
+#define TARGET_FASYNC 0x2000 /* fcntl, for BSD compatibility */
+#define TARGET_O_LARGEFILE 0x0000 /* not necessary, always 64-bit */
+#define TARGET_O_DIRECT 0x80000 /* direct disk access hint */
+#define TARGET_O_DIRECTORY 0x8000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0x10000 /* don't follow links */
+#define TARGET_O_NOATIME 0x100000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#else
+#define TARGET_O_ACCMODE 0003
+#define TARGET_O_RDONLY 00
+#define TARGET_O_WRONLY 01
+#define TARGET_O_RDWR 02
+#define TARGET_O_CREAT 0100 /* not fcntl */
+#define TARGET_O_EXCL 0200 /* not fcntl */
+#define TARGET_O_NOCTTY 0400 /* not fcntl */
+#define TARGET_O_TRUNC 01000 /* not fcntl */
+#define TARGET_O_APPEND 02000
+#define TARGET_O_NONBLOCK 04000
+#define TARGET_O_NDELAY TARGET_O_NONBLOCK
+#define TARGET_O_SYNC 010000
+#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */
+#define TARGET_O_DIRECT 040000 /* direct disk access hint */
+#define TARGET_O_LARGEFILE 0100000
+#define TARGET_O_DIRECTORY 0200000 /* must be a directory */
+#define TARGET_O_NOFOLLOW 0400000 /* don't follow links */
+#endif
+
+struct target_flock {
+ short l_type;
+ short l_whence;
+ abi_ulong l_start;
+ abi_ulong l_len;
+ int l_pid;
+};
+
+struct target_flock64 {
+ short l_type;
+ short l_whence;
+#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) || defined(TARGET_SPARC) || defined(TARGET_HPPA) || defined (TARGET_MICROBLAZE)
+ int __pad;
+#endif
+ unsigned long long l_start;
+ unsigned long long l_len;
+ int l_pid;
+}__attribute__((packed));
+
+#ifdef TARGET_ARM
+struct target_eabi_flock64 {
+ short l_type;
+ short l_whence;
+ int __pad;
+ unsigned long long l_start;
+ unsigned long long l_len;
+ int l_pid;
+}__attribute__((packed));
+#endif
+
+/* soundcard defines */
+/* XXX: convert them all to arch indepedent entries */
+#define TARGET_SNDCTL_COPR_HALT TARGET_IOWR('C', 7, int);
+#define TARGET_SNDCTL_COPR_LOAD 0xcfb04301
+#define TARGET_SNDCTL_COPR_RCODE 0xc0144303
+#define TARGET_SNDCTL_COPR_RCVMSG 0x8fa44309
+#define TARGET_SNDCTL_COPR_RDATA 0xc0144302
+#define TARGET_SNDCTL_COPR_RESET 0x00004300
+#define TARGET_SNDCTL_COPR_RUN 0xc0144306
+#define TARGET_SNDCTL_COPR_SENDMSG 0xcfa44308
+#define TARGET_SNDCTL_COPR_WCODE 0x40144305
+#define TARGET_SNDCTL_COPR_WDATA 0x40144304
+#define TARGET_SNDCTL_DSP_RESET TARGET_IO('P', 0)
+#define TARGET_SNDCTL_DSP_SYNC TARGET_IO('P', 1)
+#define TARGET_SNDCTL_DSP_SPEED TARGET_IOWR('P', 2, int)
+#define TARGET_SNDCTL_DSP_STEREO TARGET_IOWR('P', 3, int)
+#define TARGET_SNDCTL_DSP_GETBLKSIZE TARGET_IOWR('P', 4, int)
+#define TARGET_SNDCTL_DSP_SETFMT TARGET_IOWR('P', 5, int)
+#define TARGET_SNDCTL_DSP_CHANNELS TARGET_IOWR('P', 6, int)
+#define TARGET_SOUND_PCM_WRITE_FILTER TARGET_IOWR('P', 7, int)
+#define TARGET_SNDCTL_DSP_POST TARGET_IO('P', 8)
+#define TARGET_SNDCTL_DSP_SUBDIVIDE TARGET_IOWR('P', 9, int)
+#define TARGET_SNDCTL_DSP_SETFRAGMENT TARGET_IOWR('P',10, int)
+#define TARGET_SNDCTL_DSP_GETFMTS TARGET_IOR('P', 11, int)
+#define TARGET_SNDCTL_DSP_GETOSPACE TARGET_IORU('P',12)
+#define TARGET_SNDCTL_DSP_GETISPACE TARGET_IORU('P',13)
+#define TARGET_SNDCTL_DSP_GETCAPS TARGET_IOR('P', 15, int)
+#define TARGET_SNDCTL_DSP_GETTRIGGER TARGET_IOR('P',16, int)
+#define TARGET_SNDCTL_DSP_GETIPTR TARGET_IORU('P',17)
+#define TARGET_SNDCTL_DSP_GETOPTR TARGET_IORU('P',18)
+#define TARGET_SNDCTL_DSP_MAPINBUF 0x80085013
+#define TARGET_SNDCTL_DSP_MAPOUTBUF 0x80085014
+#define TARGET_SNDCTL_DSP_NONBLOCK 0x0000500e
+#define TARGET_SNDCTL_DSP_SAMPLESIZE 0xc0045005
+#define TARGET_SNDCTL_DSP_SETDUPLEX 0x00005016
+#define TARGET_SNDCTL_DSP_SETSYNCRO 0x00005015
+#define TARGET_SNDCTL_DSP_SETTRIGGER 0x40045010
+#define TARGET_SNDCTL_FM_4OP_ENABLE 0x4004510f
+#define TARGET_SNDCTL_FM_LOAD_INSTR 0x40285107
+#define TARGET_SNDCTL_MIDI_INFO 0xc074510c
+#define TARGET_SNDCTL_MIDI_MPUCMD 0xc0216d02
+#define TARGET_SNDCTL_MIDI_MPUMODE 0xc0046d01
+#define TARGET_SNDCTL_MIDI_PRETIME 0xc0046d00
+#define TARGET_SNDCTL_PMGR_ACCESS 0xcfb85110
+#define TARGET_SNDCTL_PMGR_IFACE 0xcfb85001
+#define TARGET_SNDCTL_SEQ_CTRLRATE 0xc0045103
+#define TARGET_SNDCTL_SEQ_GETINCOUNT 0x80045105
+#define TARGET_SNDCTL_SEQ_GETOUTCOUNT 0x80045104
+#define TARGET_SNDCTL_SEQ_NRMIDIS 0x8004510b
+#define TARGET_SNDCTL_SEQ_NRSYNTHS 0x8004510a
+#define TARGET_SNDCTL_SEQ_OUTOFBAND 0x40085112
+#define TARGET_SNDCTL_SEQ_PANIC 0x00005111
+#define TARGET_SNDCTL_SEQ_PERCMODE 0x40045106
+#define TARGET_SNDCTL_SEQ_RESET 0x00005100
+#define TARGET_SNDCTL_SEQ_RESETSAMPLES 0x40045109
+#define TARGET_SNDCTL_SEQ_SYNC 0x00005101
+#define TARGET_SNDCTL_SEQ_TESTMIDI 0x40045108
+#define TARGET_SNDCTL_SEQ_THRESHOLD 0x4004510d
+#define TARGET_SNDCTL_SEQ_TRESHOLD 0x4004510d
+#define TARGET_SNDCTL_SYNTH_INFO 0xc08c5102
+#define TARGET_SNDCTL_SYNTH_MEMAVL 0xc004510e
+#define TARGET_SNDCTL_TMR_CONTINUE 0x00005404
+#define TARGET_SNDCTL_TMR_METRONOME 0x40045407
+#define TARGET_SNDCTL_TMR_SELECT 0x40045408
+#define TARGET_SNDCTL_TMR_SOURCE 0xc0045406
+#define TARGET_SNDCTL_TMR_START 0x00005402
+#define TARGET_SNDCTL_TMR_STOP 0x00005403
+#define TARGET_SNDCTL_TMR_TEMPO 0xc0045405
+#define TARGET_SNDCTL_TMR_TIMEBASE 0xc0045401
+#define TARGET_SOUND_PCM_READ_RATE 0x80045002
+#define TARGET_SOUND_PCM_READ_CHANNELS 0x80045006
+#define TARGET_SOUND_PCM_READ_BITS 0x80045005
+#define TARGET_SOUND_PCM_READ_FILTER 0x80045007
+#define TARGET_SOUND_MIXER_INFO TARGET_IOR ('M', 101, mixer_info)
+#define TARGET_SOUND_MIXER_ACCESS 0xc0804d66
+#define TARGET_SOUND_MIXER_PRIVATE1 TARGET_IOWR('M', 111, int)
+#define TARGET_SOUND_MIXER_PRIVATE2 TARGET_IOWR('M', 112, int)
+#define TARGET_SOUND_MIXER_PRIVATE3 TARGET_IOWR('M', 113, int)
+#define TARGET_SOUND_MIXER_PRIVATE4 TARGET_IOWR('M', 114, int)
+#define TARGET_SOUND_MIXER_PRIVATE5 TARGET_IOWR('M', 115, int)
+
+#define TARGET_MIXER_READ(dev) TARGET_IOR('M', dev, int)
+
+#define TARGET_SOUND_MIXER_READ_VOLUME TARGET_MIXER_READ(SOUND_MIXER_VOLUME)
+#define TARGET_SOUND_MIXER_READ_BASS TARGET_MIXER_READ(SOUND_MIXER_BASS)
+#define TARGET_SOUND_MIXER_READ_TREBLE TARGET_MIXER_READ(SOUND_MIXER_TREBLE)
+#define TARGET_SOUND_MIXER_READ_SYNTH TARGET_MIXER_READ(SOUND_MIXER_SYNTH)
+#define TARGET_SOUND_MIXER_READ_PCM TARGET_MIXER_READ(SOUND_MIXER_PCM)
+#define TARGET_SOUND_MIXER_READ_SPEAKER TARGET_MIXER_READ(SOUND_MIXER_SPEAKER)
+#define TARGET_SOUND_MIXER_READ_LINE TARGET_MIXER_READ(SOUND_MIXER_LINE)
+#define TARGET_SOUND_MIXER_READ_MIC TARGET_MIXER_READ(SOUND_MIXER_MIC)
+#define TARGET_SOUND_MIXER_READ_CD TARGET_MIXER_READ(SOUND_MIXER_CD)
+#define TARGET_SOUND_MIXER_READ_IMIX TARGET_MIXER_READ(SOUND_MIXER_IMIX)
+#define TARGET_SOUND_MIXER_READ_ALTPCM TARGET_MIXER_READ(SOUND_MIXER_ALTPCM)
+#define TARGET_SOUND_MIXER_READ_RECLEV TARGET_MIXER_READ(SOUND_MIXER_RECLEV)
+#define TARGET_SOUND_MIXER_READ_IGAIN TARGET_MIXER_READ(SOUND_MIXER_IGAIN)
+#define TARGET_SOUND_MIXER_READ_OGAIN TARGET_MIXER_READ(SOUND_MIXER_OGAIN)
+#define TARGET_SOUND_MIXER_READ_LINE1 TARGET_MIXER_READ(SOUND_MIXER_LINE1)
+#define TARGET_SOUND_MIXER_READ_LINE2 TARGET_MIXER_READ(SOUND_MIXER_LINE2)
+#define TARGET_SOUND_MIXER_READ_LINE3 TARGET_MIXER_READ(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define TARGET_SOUND_MIXER_READ_MUTE TARGET_MIXER_READ(SOUND_MIXER_MUTE)
+#define TARGET_SOUND_MIXER_READ_ENHANCE TARGET_MIXER_READ(SOUND_MIXER_ENHANCE)
+#define TARGET_SOUND_MIXER_READ_LOUD TARGET_MIXER_READ(SOUND_MIXER_LOUD)
+
+#define TARGET_SOUND_MIXER_READ_RECSRC TARGET_MIXER_READ(SOUND_MIXER_RECSRC)
+#define TARGET_SOUND_MIXER_READ_DEVMASK TARGET_MIXER_READ(SOUND_MIXER_DEVMASK)
+#define TARGET_SOUND_MIXER_READ_RECMASK TARGET_MIXER_READ(SOUND_MIXER_RECMASK)
+#define TARGET_SOUND_MIXER_READ_STEREODEVS TARGET_MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define TARGET_SOUND_MIXER_READ_CAPS TARGET_MIXER_READ(SOUND_MIXER_CAPS)
+
+#define TARGET_MIXER_WRITE(dev) TARGET_IOWR('M', dev, int)
+
+#define TARGET_SOUND_MIXER_WRITE_VOLUME TARGET_MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define TARGET_SOUND_MIXER_WRITE_BASS TARGET_MIXER_WRITE(SOUND_MIXER_BASS)
+#define TARGET_SOUND_MIXER_WRITE_TREBLE TARGET_MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define TARGET_SOUND_MIXER_WRITE_SYNTH TARGET_MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define TARGET_SOUND_MIXER_WRITE_PCM TARGET_MIXER_WRITE(SOUND_MIXER_PCM)
+#define TARGET_SOUND_MIXER_WRITE_SPEAKER TARGET_MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define TARGET_SOUND_MIXER_WRITE_LINE TARGET_MIXER_WRITE(SOUND_MIXER_LINE)
+#define TARGET_SOUND_MIXER_WRITE_MIC TARGET_MIXER_WRITE(SOUND_MIXER_MIC)
+#define TARGET_SOUND_MIXER_WRITE_CD TARGET_MIXER_WRITE(SOUND_MIXER_CD)
+#define TARGET_SOUND_MIXER_WRITE_IMIX TARGET_MIXER_WRITE(SOUND_MIXER_IMIX)
+#define TARGET_SOUND_MIXER_WRITE_ALTPCM TARGET_MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define TARGET_SOUND_MIXER_WRITE_RECLEV TARGET_MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define TARGET_SOUND_MIXER_WRITE_IGAIN TARGET_MIXER_WRITE(SOUND_MIXER_IGAIN)
+#define TARGET_SOUND_MIXER_WRITE_OGAIN TARGET_MIXER_WRITE(SOUND_MIXER_OGAIN)
+#define TARGET_SOUND_MIXER_WRITE_LINE1 TARGET_MIXER_WRITE(SOUND_MIXER_LINE1)
+#define TARGET_SOUND_MIXER_WRITE_LINE2 TARGET_MIXER_WRITE(SOUND_MIXER_LINE2)
+#define TARGET_SOUND_MIXER_WRITE_LINE3 TARGET_MIXER_WRITE(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define TARGET_SOUND_MIXER_WRITE_MUTE TARGET_MIXER_WRITE(SOUND_MIXER_MUTE)
+#define TARGET_SOUND_MIXER_WRITE_ENHANCE TARGET_MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define TARGET_SOUND_MIXER_WRITE_LOUD TARGET_MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define TARGET_SOUND_MIXER_WRITE_RECSRC TARGET_MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+/* vfat ioctls */
+#define TARGET_VFAT_IOCTL_READDIR_BOTH TARGET_IORU('r', 1)
+#define TARGET_VFAT_IOCTL_READDIR_SHORT TARGET_IORU('r', 2)
+
+#define TARGET_MTIOCTOP TARGET_IOW('m', 1, struct mtop)
+#define TARGET_MTIOCGET TARGET_IOR('m', 2, struct mtget)
+#define TARGET_MTIOCPOS TARGET_IOR('m', 3, struct mtpos)
+
+struct target_sysinfo {
+ abi_long uptime; /* Seconds since boot */
+ abi_ulong loads[3]; /* 1, 5, and 15 minute load averages */
+ abi_ulong totalram; /* Total usable main memory size */
+ abi_ulong freeram; /* Available memory size */
+ abi_ulong sharedram; /* Amount of shared memory */
+ abi_ulong bufferram; /* Memory used by buffers */
+ abi_ulong totalswap; /* Total swap space size */
+ abi_ulong freeswap; /* swap space still available */
+ unsigned short procs; /* Number of current processes */
+ unsigned short pad; /* explicit padding for m68k */
+ abi_ulong totalhigh; /* Total high memory size */
+ abi_ulong freehigh; /* Available high memory size */
+ unsigned int mem_unit; /* Memory unit size in bytes */
+ char _f[20-2*sizeof(abi_long)-sizeof(int)]; /* Padding: libc5 uses this.. */
+};
+
+struct linux_dirent {
+ long d_ino;
+ unsigned long d_off;
+ unsigned short d_reclen;
+ char d_name[256]; /* We must not include limits.h! */
+};
+
+struct linux_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[256];
+};
+
+struct target_mq_attr {
+ abi_long mq_flags;
+ abi_long mq_maxmsg;
+ abi_long mq_msgsize;
+ abi_long mq_curmsgs;
+};
+
+#include "socket.h"
+
+#include "errno_defs.h"
+
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+#define FUTEX_FD 2
+#define FUTEX_REQUEUE 3
+#define FUTEX_CMP_REQUEUE 4
+#define FUTEX_WAKE_OP 5
+#define FUTEX_LOCK_PI 6
+#define FUTEX_UNLOCK_PI 7
+#define FUTEX_TRYLOCK_PI 8
+#define FUTEX_WAIT_BITSET 9
+#define FUTEX_WAKE_BITSET 10
+
+#define FUTEX_PRIVATE_FLAG 128
+#define FUTEX_CLOCK_REALTIME 256
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
new file mode 100644
index 0000000..0e67cd8
--- /dev/null
+++ b/linux-user/syscall_types.h
@@ -0,0 +1,183 @@
+STRUCT_SPECIAL(termios)
+
+STRUCT(winsize,
+ TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT)
+
+STRUCT(serial_multiport_struct,
+ TYPE_INT, TYPE_INT, TYPE_CHAR, TYPE_CHAR, TYPE_INT, TYPE_CHAR, TYPE_CHAR,
+ TYPE_INT, TYPE_CHAR, TYPE_CHAR, TYPE_INT, TYPE_CHAR, TYPE_CHAR, TYPE_INT,
+ MK_ARRAY(TYPE_INT, 32))
+
+STRUCT(serial_icounter_struct,
+ TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, MK_ARRAY(TYPE_INT, 16))
+
+STRUCT(sockaddr,
+ TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 14))
+
+STRUCT(rtentry,
+ TYPE_ULONG, MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr),
+ TYPE_SHORT, TYPE_SHORT, TYPE_ULONG, TYPE_PTRVOID, TYPE_SHORT, TYPE_PTRVOID,
+ TYPE_ULONG, TYPE_ULONG, TYPE_SHORT)
+
+STRUCT(ifmap,
+ TYPE_ULONG, TYPE_ULONG, TYPE_SHORT, TYPE_CHAR, TYPE_CHAR, TYPE_CHAR,
+ /* Spare 3 bytes */
+ TYPE_CHAR, TYPE_CHAR, TYPE_CHAR)
+
+/* The *_ifreq_list arrays deal with the fact that struct ifreq has unions */
+
+STRUCT(sockaddr_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), MK_STRUCT(STRUCT_sockaddr))
+
+STRUCT(short_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), TYPE_SHORT)
+
+STRUCT(int_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), TYPE_INT)
+
+STRUCT(ifmap_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), MK_STRUCT(STRUCT_ifmap))
+
+STRUCT(char_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ),
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ))
+
+STRUCT(ptr_ifreq,
+ MK_ARRAY(TYPE_CHAR, IFNAMSIZ), TYPE_PTRVOID)
+
+STRUCT(ifconf,
+ TYPE_INT, TYPE_PTRVOID)
+
+STRUCT(arpreq,
+ MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), TYPE_INT, MK_STRUCT(STRUCT_sockaddr),
+ MK_ARRAY(TYPE_CHAR, 16))
+
+STRUCT(arpreq_old,
+ MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), TYPE_INT, MK_STRUCT(STRUCT_sockaddr))
+
+STRUCT(cdrom_read_audio,
+ TYPE_CHAR, TYPE_CHAR, TYPE_CHAR, TYPE_CHAR, TYPE_CHAR, TYPE_INT, TYPE_PTRVOID,
+ TYPE_NULL)
+
+STRUCT(hd_geometry,
+ TYPE_CHAR, TYPE_CHAR, TYPE_SHORT, TYPE_ULONG)
+
+STRUCT(dirent,
+ TYPE_LONG, TYPE_LONG, TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 256))
+
+STRUCT(kbentry,
+ TYPE_CHAR, TYPE_CHAR, TYPE_SHORT)
+
+STRUCT(kbsentry,
+ TYPE_CHAR, MK_ARRAY(TYPE_CHAR, 512))
+
+STRUCT(audio_buf_info,
+ TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT)
+
+STRUCT(count_info,
+ TYPE_INT, TYPE_INT, TYPE_INT)
+
+STRUCT(mixer_info,
+ MK_ARRAY(TYPE_CHAR, 16), MK_ARRAY(TYPE_CHAR, 32), TYPE_INT, MK_ARRAY(TYPE_INT, 10))
+
+/* loop device ioctls */
+STRUCT(loop_info,
+ TYPE_INT, /* lo_number */
+ TYPE_SHORT, /* lo_device */
+ TYPE_ULONG, /* lo_inode */
+ TYPE_SHORT, /* lo_rdevice */
+ TYPE_INT, /* lo_offset */
+ TYPE_INT, /* lo_encrypt_type */
+ TYPE_INT, /* lo_encrypt_key_size */
+ TYPE_INT, /* lo_flags */
+ MK_ARRAY(TYPE_CHAR, 64), /* lo_name */
+ MK_ARRAY(TYPE_CHAR, 32), /* lo_encrypt_key */
+ MK_ARRAY(TYPE_ULONG, 2), /* lo_init */
+ MK_ARRAY(TYPE_CHAR, 4)) /* reserved */
+
+STRUCT(loop_info64,
+ TYPE_ULONGLONG, /* lo_device */
+ TYPE_ULONGLONG, /* lo_inode */
+ TYPE_ULONGLONG, /* lo_rdevice */
+ TYPE_ULONGLONG, /* lo_offset */
+ TYPE_ULONG, /* lo_number */
+ TYPE_ULONG, /* lo_encrypt_type */
+ TYPE_ULONG, /* lo_encrypt_key_size */
+ TYPE_ULONG, /* lo_flags */
+ MK_ARRAY(TYPE_CHAR, 64), /* lo_name */
+ MK_ARRAY(TYPE_CHAR, 64), /* lo_crypt_name */
+ MK_ARRAY(TYPE_CHAR, 32), /* lo_encrypt_key */
+ MK_ARRAY(TYPE_ULONGLONG, 2)) /* lo_init */
+
+/* mag tape ioctls */
+STRUCT(mtop, TYPE_SHORT, TYPE_INT)
+STRUCT(mtget, TYPE_LONG, TYPE_LONG, TYPE_LONG, TYPE_LONG, TYPE_LONG,
+ TYPE_INT, TYPE_INT)
+STRUCT(mtpos, TYPE_LONG)
+
+STRUCT(fb_fix_screeninfo,
+ MK_ARRAY(TYPE_CHAR, 16), /* id */
+ TYPE_ULONG, /* smem_start */
+ TYPE_INT, /* smem_len */
+ TYPE_INT, /* type */
+ TYPE_INT, /* type_aux */
+ TYPE_INT, /* visual */
+ TYPE_SHORT, /* xpanstep */
+ TYPE_SHORT, /* ypanstep */
+ TYPE_SHORT, /* ywrapstep */
+ TYPE_INT, /* line_length */
+ TYPE_ULONG, /* mmio_start */
+ TYPE_INT, /* mmio_len */
+ TYPE_INT, /* accel */
+ MK_ARRAY(TYPE_CHAR, 3)) /* reserved */
+
+STRUCT(fb_var_screeninfo,
+ TYPE_INT, /* xres */
+ TYPE_INT, /* yres */
+ TYPE_INT, /* xres_virtual */
+ TYPE_INT, /* yres_virtual */
+ TYPE_INT, /* xoffset */
+ TYPE_INT, /* yoffset */
+ TYPE_INT, /* bits_per_pixel */
+ TYPE_INT, /* grayscale */
+ MK_ARRAY(TYPE_INT, 3), /* red */
+ MK_ARRAY(TYPE_INT, 3), /* green */
+ MK_ARRAY(TYPE_INT, 3), /* blue */
+ MK_ARRAY(TYPE_INT, 3), /* transp */
+ TYPE_INT, /* nonstd */
+ TYPE_INT, /* activate */
+ TYPE_INT, /* height */
+ TYPE_INT, /* width */
+ TYPE_INT, /* accel_flags */
+ TYPE_INT, /* pixclock */
+ TYPE_INT, /* left_margin */
+ TYPE_INT, /* right_margin */
+ TYPE_INT, /* upper_margin */
+ TYPE_INT, /* lower_margin */
+ TYPE_INT, /* hsync_len */
+ TYPE_INT, /* vsync_len */
+ TYPE_INT, /* sync */
+ TYPE_INT, /* vmode */
+ TYPE_INT, /* rotate */
+ MK_ARRAY(TYPE_INT, 5)) /* reserved */
+
+STRUCT(vt_stat,
+ TYPE_SHORT, /* v_active */
+ TYPE_SHORT, /* v_signal */
+ TYPE_SHORT) /* v_state */
+
+STRUCT(fiemap_extent,
+ TYPE_ULONGLONG, /* fe_logical */
+ TYPE_ULONGLONG, /* fe_physical */
+ TYPE_ULONGLONG, /* fe_length */
+ MK_ARRAY(TYPE_ULONGLONG, 2), /* fe_reserved64[2] */
+ TYPE_INT, /* fe_flags */
+ MK_ARRAY(TYPE_INT, 3)) /* fe_reserved[3] */
+
+STRUCT(fiemap,
+ TYPE_ULONGLONG, /* fm_start */
+ TYPE_ULONGLONG, /* fm_length */
+ TYPE_INT, /* fm_flags */
+ TYPE_INT, /* fm_mapped_extents */
+ TYPE_INT, /* fm_extent_count */
+ TYPE_INT) /* fm_reserved */
diff --git a/linux-user/uaccess.c b/linux-user/uaccess.c
new file mode 100644
index 0000000..a4d108c
--- /dev/null
+++ b/linux-user/uaccess.c
@@ -0,0 +1,65 @@
+/* User memory access */
+#include <stdio.h>
+#include <string.h>
+
+#include "qemu.h"
+
+/* copy_from_user() and copy_to_user() are usually used to copy data
+ * buffers between the target and host. These internally perform
+ * locking/unlocking of the memory.
+ */
+abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len)
+{
+ abi_long ret = 0;
+ void *ghptr;
+
+ if ((ghptr = lock_user(VERIFY_READ, gaddr, len, 1))) {
+ memcpy(hptr, ghptr, len);
+ unlock_user(ghptr, gaddr, 0);
+ } else
+ ret = -TARGET_EFAULT;
+
+ return ret;
+}
+
+
+abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len)
+{
+ abi_long ret = 0;
+ void *ghptr;
+
+ if ((ghptr = lock_user(VERIFY_WRITE, gaddr, len, 0))) {
+ memcpy(ghptr, hptr, len);
+ unlock_user(ghptr, gaddr, len);
+ } else
+ ret = -TARGET_EFAULT;
+
+ return ret;
+}
+
+/* Return the length of a string in target memory or -TARGET_EFAULT if
+ access error */
+abi_long target_strlen(abi_ulong guest_addr1)
+{
+ uint8_t *ptr;
+ abi_ulong guest_addr;
+ int max_len, len;
+
+ guest_addr = guest_addr1;
+ for(;;) {
+ max_len = TARGET_PAGE_SIZE - (guest_addr & ~TARGET_PAGE_MASK);
+ ptr = lock_user(VERIFY_READ, guest_addr, max_len, 1);
+ if (!ptr)
+ return -TARGET_EFAULT;
+ len = qemu_strnlen((const char *)ptr, max_len);
+ unlock_user(ptr, guest_addr, 0);
+ guest_addr += len;
+ /* we don't allow wrapping or integer overflow */
+ if (guest_addr == 0 ||
+ (guest_addr - guest_addr1) > 0x7fffffff)
+ return -TARGET_EFAULT;
+ if (len != max_len)
+ break;
+ }
+ return guest_addr - guest_addr1;
+}
diff --git a/linux-user/vm86.c b/linux-user/vm86.c
new file mode 100644
index 0000000..0b2439d
--- /dev/null
+++ b/linux-user/vm86.c
@@ -0,0 +1,481 @@
+/*
+ * vm86 linux syscall support
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "qemu.h"
+
+//#define DEBUG_VM86
+
+#ifdef DEBUG_VM86
+# define LOG_VM86(...) qemu_log(__VA_ARGS__);
+#else
+# define LOG_VM86(...) do { } while (0)
+#endif
+
+
+#define set_flags(X,new,mask) \
+((X) = ((X) & ~(mask)) | ((new) & (mask)))
+
+#define SAFE_MASK (0xDD5)
+#define RETURN_MASK (0xDFF)
+
+static inline int is_revectored(int nr, struct target_revectored_struct *bitmap)
+{
+ return (((uint8_t *)bitmap)[nr >> 3] >> (nr & 7)) & 1;
+}
+
+static inline void vm_putw(uint32_t segptr, unsigned int reg16, unsigned int val)
+{
+ stw(segptr + (reg16 & 0xffff), val);
+}
+
+static inline void vm_putl(uint32_t segptr, unsigned int reg16, unsigned int val)
+{
+ stl(segptr + (reg16 & 0xffff), val);
+}
+
+static inline unsigned int vm_getb(uint32_t segptr, unsigned int reg16)
+{
+ return ldub(segptr + (reg16 & 0xffff));
+}
+
+static inline unsigned int vm_getw(uint32_t segptr, unsigned int reg16)
+{
+ return lduw(segptr + (reg16 & 0xffff));
+}
+
+static inline unsigned int vm_getl(uint32_t segptr, unsigned int reg16)
+{
+ return ldl(segptr + (reg16 & 0xffff));
+}
+
+void save_v86_state(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+ struct target_vm86plus_struct * target_v86;
+
+ if (!lock_user_struct(VERIFY_WRITE, target_v86, ts->target_v86, 0))
+ /* FIXME - should return an error */
+ return;
+ /* put the VM86 registers in the userspace register structure */
+ target_v86->regs.eax = tswap32(env->regs[R_EAX]);
+ target_v86->regs.ebx = tswap32(env->regs[R_EBX]);
+ target_v86->regs.ecx = tswap32(env->regs[R_ECX]);
+ target_v86->regs.edx = tswap32(env->regs[R_EDX]);
+ target_v86->regs.esi = tswap32(env->regs[R_ESI]);
+ target_v86->regs.edi = tswap32(env->regs[R_EDI]);
+ target_v86->regs.ebp = tswap32(env->regs[R_EBP]);
+ target_v86->regs.esp = tswap32(env->regs[R_ESP]);
+ target_v86->regs.eip = tswap32(env->eip);
+ target_v86->regs.cs = tswap16(env->segs[R_CS].selector);
+ target_v86->regs.ss = tswap16(env->segs[R_SS].selector);
+ target_v86->regs.ds = tswap16(env->segs[R_DS].selector);
+ target_v86->regs.es = tswap16(env->segs[R_ES].selector);
+ target_v86->regs.fs = tswap16(env->segs[R_FS].selector);
+ target_v86->regs.gs = tswap16(env->segs[R_GS].selector);
+ set_flags(env->eflags, ts->v86flags, VIF_MASK | ts->v86mask);
+ target_v86->regs.eflags = tswap32(env->eflags);
+ unlock_user_struct(target_v86, ts->target_v86, 1);
+ LOG_VM86("save_v86_state: eflags=%08x cs:ip=%04x:%04x\n",
+ env->eflags, env->segs[R_CS].selector, env->eip);
+
+ /* restore 32 bit registers */
+ env->regs[R_EAX] = ts->vm86_saved_regs.eax;
+ env->regs[R_EBX] = ts->vm86_saved_regs.ebx;
+ env->regs[R_ECX] = ts->vm86_saved_regs.ecx;
+ env->regs[R_EDX] = ts->vm86_saved_regs.edx;
+ env->regs[R_ESI] = ts->vm86_saved_regs.esi;
+ env->regs[R_EDI] = ts->vm86_saved_regs.edi;
+ env->regs[R_EBP] = ts->vm86_saved_regs.ebp;
+ env->regs[R_ESP] = ts->vm86_saved_regs.esp;
+ env->eflags = ts->vm86_saved_regs.eflags;
+ env->eip = ts->vm86_saved_regs.eip;
+
+ cpu_x86_load_seg(env, R_CS, ts->vm86_saved_regs.cs);
+ cpu_x86_load_seg(env, R_SS, ts->vm86_saved_regs.ss);
+ cpu_x86_load_seg(env, R_DS, ts->vm86_saved_regs.ds);
+ cpu_x86_load_seg(env, R_ES, ts->vm86_saved_regs.es);
+ cpu_x86_load_seg(env, R_FS, ts->vm86_saved_regs.fs);
+ cpu_x86_load_seg(env, R_GS, ts->vm86_saved_regs.gs);
+}
+
+/* return from vm86 mode to 32 bit. The vm86() syscall will return
+ 'retval' */
+static inline void return_to_32bit(CPUX86State *env, int retval)
+{
+ LOG_VM86("return_to_32bit: ret=0x%x\n", retval);
+ save_v86_state(env);
+ env->regs[R_EAX] = retval;
+}
+
+static inline int set_IF(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ ts->v86flags |= VIF_MASK;
+ if (ts->v86flags & VIP_MASK) {
+ return_to_32bit(env, TARGET_VM86_STI);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void clear_IF(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ ts->v86flags &= ~VIF_MASK;
+}
+
+static inline void clear_TF(CPUX86State *env)
+{
+ env->eflags &= ~TF_MASK;
+}
+
+static inline void clear_AC(CPUX86State *env)
+{
+ env->eflags &= ~AC_MASK;
+}
+
+static inline int set_vflags_long(unsigned long eflags, CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ set_flags(ts->v86flags, eflags, ts->v86mask);
+ set_flags(env->eflags, eflags, SAFE_MASK);
+ if (eflags & IF_MASK)
+ return set_IF(env);
+ else
+ clear_IF(env);
+ return 0;
+}
+
+static inline int set_vflags_short(unsigned short flags, CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+
+ set_flags(ts->v86flags, flags, ts->v86mask & 0xffff);
+ set_flags(env->eflags, flags, SAFE_MASK);
+ if (flags & IF_MASK)
+ return set_IF(env);
+ else
+ clear_IF(env);
+ return 0;
+}
+
+static inline unsigned int get_vflags(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+ unsigned int flags;
+
+ flags = env->eflags & RETURN_MASK;
+ if (ts->v86flags & VIF_MASK)
+ flags |= IF_MASK;
+ flags |= IOPL_MASK;
+ return flags | (ts->v86flags & ts->v86mask);
+}
+
+#define ADD16(reg, val) reg = (reg & ~0xffff) | ((reg + (val)) & 0xffff)
+
+/* handle VM86 interrupt (NOTE: the CPU core currently does not
+ support TSS interrupt revectoring, so this code is always executed) */
+static void do_int(CPUX86State *env, int intno)
+{
+ TaskState *ts = env->opaque;
+ uint32_t int_addr, segoffs, ssp;
+ unsigned int sp;
+
+ if (env->segs[R_CS].selector == TARGET_BIOSSEG)
+ goto cannot_handle;
+ if (is_revectored(intno, &ts->vm86plus.int_revectored))
+ goto cannot_handle;
+ if (intno == 0x21 && is_revectored((env->regs[R_EAX] >> 8) & 0xff,
+ &ts->vm86plus.int21_revectored))
+ goto cannot_handle;
+ int_addr = (intno << 2);
+ segoffs = ldl(int_addr);
+ if ((segoffs >> 16) == TARGET_BIOSSEG)
+ goto cannot_handle;
+ LOG_VM86("VM86: emulating int 0x%x. CS:IP=%04x:%04x\n",
+ intno, segoffs >> 16, segoffs & 0xffff);
+ /* save old state */
+ ssp = env->segs[R_SS].selector << 4;
+ sp = env->regs[R_ESP] & 0xffff;
+ vm_putw(ssp, sp - 2, get_vflags(env));
+ vm_putw(ssp, sp - 4, env->segs[R_CS].selector);
+ vm_putw(ssp, sp - 6, env->eip);
+ ADD16(env->regs[R_ESP], -6);
+ /* goto interrupt handler */
+ env->eip = segoffs & 0xffff;
+ cpu_x86_load_seg(env, R_CS, segoffs >> 16);
+ clear_TF(env);
+ clear_IF(env);
+ clear_AC(env);
+ return;
+ cannot_handle:
+ LOG_VM86("VM86: return to 32 bits int 0x%x\n", intno);
+ return_to_32bit(env, TARGET_VM86_INTx | (intno << 8));
+}
+
+void handle_vm86_trap(CPUX86State *env, int trapno)
+{
+ if (trapno == 1 || trapno == 3) {
+ return_to_32bit(env, TARGET_VM86_TRAP + (trapno << 8));
+ } else {
+ do_int(env, trapno);
+ }
+}
+
+#define CHECK_IF_IN_TRAP() \
+ if ((ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) && \
+ (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_TFpendig)) \
+ newflags |= TF_MASK
+
+#define VM86_FAULT_RETURN \
+ if ((ts->vm86plus.vm86plus.flags & TARGET_force_return_for_pic) && \
+ (ts->v86flags & (IF_MASK | VIF_MASK))) \
+ return_to_32bit(env, TARGET_VM86_PICRETURN); \
+ return
+
+void handle_vm86_fault(CPUX86State *env)
+{
+ TaskState *ts = env->opaque;
+ uint32_t csp, ssp;
+ unsigned int ip, sp, newflags, newip, newcs, opcode, intno;
+ int data32, pref_done;
+
+ csp = env->segs[R_CS].selector << 4;
+ ip = env->eip & 0xffff;
+
+ ssp = env->segs[R_SS].selector << 4;
+ sp = env->regs[R_ESP] & 0xffff;
+
+ LOG_VM86("VM86 exception %04x:%08x\n",
+ env->segs[R_CS].selector, env->eip);
+
+ data32 = 0;
+ pref_done = 0;
+ do {
+ opcode = vm_getb(csp, ip);
+ ADD16(ip, 1);
+ switch (opcode) {
+ case 0x66: /* 32-bit data */ data32=1; break;
+ case 0x67: /* 32-bit address */ break;
+ case 0x2e: /* CS */ break;
+ case 0x3e: /* DS */ break;
+ case 0x26: /* ES */ break;
+ case 0x36: /* SS */ break;
+ case 0x65: /* GS */ break;
+ case 0x64: /* FS */ break;
+ case 0xf2: /* repnz */ break;
+ case 0xf3: /* rep */ break;
+ default: pref_done = 1;
+ }
+ } while (!pref_done);
+
+ /* VM86 mode */
+ switch(opcode) {
+ case 0x9c: /* pushf */
+ if (data32) {
+ vm_putl(ssp, sp - 4, get_vflags(env));
+ ADD16(env->regs[R_ESP], -4);
+ } else {
+ vm_putw(ssp, sp - 2, get_vflags(env));
+ ADD16(env->regs[R_ESP], -2);
+ }
+ env->eip = ip;
+ VM86_FAULT_RETURN;
+
+ case 0x9d: /* popf */
+ if (data32) {
+ newflags = vm_getl(ssp, sp);
+ ADD16(env->regs[R_ESP], 4);
+ } else {
+ newflags = vm_getw(ssp, sp);
+ ADD16(env->regs[R_ESP], 2);
+ }
+ env->eip = ip;
+ CHECK_IF_IN_TRAP();
+ if (data32) {
+ if (set_vflags_long(newflags, env))
+ return;
+ } else {
+ if (set_vflags_short(newflags, env))
+ return;
+ }
+ VM86_FAULT_RETURN;
+
+ case 0xcd: /* int */
+ intno = vm_getb(csp, ip);
+ ADD16(ip, 1);
+ env->eip = ip;
+ if (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) {
+ if ( (ts->vm86plus.vm86plus.vm86dbg_intxxtab[intno >> 3] >>
+ (intno &7)) & 1) {
+ return_to_32bit(env, TARGET_VM86_INTx + (intno << 8));
+ return;
+ }
+ }
+ do_int(env, intno);
+ break;
+
+ case 0xcf: /* iret */
+ if (data32) {
+ newip = vm_getl(ssp, sp) & 0xffff;
+ newcs = vm_getl(ssp, sp + 4) & 0xffff;
+ newflags = vm_getl(ssp, sp + 8);
+ ADD16(env->regs[R_ESP], 12);
+ } else {
+ newip = vm_getw(ssp, sp);
+ newcs = vm_getw(ssp, sp + 2);
+ newflags = vm_getw(ssp, sp + 4);
+ ADD16(env->regs[R_ESP], 6);
+ }
+ env->eip = newip;
+ cpu_x86_load_seg(env, R_CS, newcs);
+ CHECK_IF_IN_TRAP();
+ if (data32) {
+ if (set_vflags_long(newflags, env))
+ return;
+ } else {
+ if (set_vflags_short(newflags, env))
+ return;
+ }
+ VM86_FAULT_RETURN;
+
+ case 0xfa: /* cli */
+ env->eip = ip;
+ clear_IF(env);
+ VM86_FAULT_RETURN;
+
+ case 0xfb: /* sti */
+ env->eip = ip;
+ if (set_IF(env))
+ return;
+ VM86_FAULT_RETURN;
+
+ default:
+ /* real VM86 GPF exception */
+ return_to_32bit(env, TARGET_VM86_UNKNOWN);
+ break;
+ }
+}
+
+int do_vm86(CPUX86State *env, long subfunction, abi_ulong vm86_addr)
+{
+ TaskState *ts = env->opaque;
+ struct target_vm86plus_struct * target_v86;
+ int ret;
+
+ switch (subfunction) {
+ case TARGET_VM86_REQUEST_IRQ:
+ case TARGET_VM86_FREE_IRQ:
+ case TARGET_VM86_GET_IRQ_BITS:
+ case TARGET_VM86_GET_AND_RESET_IRQ:
+ gemu_log("qemu: unsupported vm86 subfunction (%ld)\n", subfunction);
+ ret = -TARGET_EINVAL;
+ goto out;
+ case TARGET_VM86_PLUS_INSTALL_CHECK:
+ /* NOTE: on old vm86 stuff this will return the error
+ from verify_area(), because the subfunction is
+ interpreted as (invalid) address to vm86_struct.
+ So the installation check works.
+ */
+ ret = 0;
+ goto out;
+ }
+
+ /* save current CPU regs */
+ ts->vm86_saved_regs.eax = 0; /* default vm86 syscall return code */
+ ts->vm86_saved_regs.ebx = env->regs[R_EBX];
+ ts->vm86_saved_regs.ecx = env->regs[R_ECX];
+ ts->vm86_saved_regs.edx = env->regs[R_EDX];
+ ts->vm86_saved_regs.esi = env->regs[R_ESI];
+ ts->vm86_saved_regs.edi = env->regs[R_EDI];
+ ts->vm86_saved_regs.ebp = env->regs[R_EBP];
+ ts->vm86_saved_regs.esp = env->regs[R_ESP];
+ ts->vm86_saved_regs.eflags = env->eflags;
+ ts->vm86_saved_regs.eip = env->eip;
+ ts->vm86_saved_regs.cs = env->segs[R_CS].selector;
+ ts->vm86_saved_regs.ss = env->segs[R_SS].selector;
+ ts->vm86_saved_regs.ds = env->segs[R_DS].selector;
+ ts->vm86_saved_regs.es = env->segs[R_ES].selector;
+ ts->vm86_saved_regs.fs = env->segs[R_FS].selector;
+ ts->vm86_saved_regs.gs = env->segs[R_GS].selector;
+
+ ts->target_v86 = vm86_addr;
+ if (!lock_user_struct(VERIFY_READ, target_v86, vm86_addr, 1))
+ return -TARGET_EFAULT;
+ /* build vm86 CPU state */
+ ts->v86flags = tswap32(target_v86->regs.eflags);
+ env->eflags = (env->eflags & ~SAFE_MASK) |
+ (tswap32(target_v86->regs.eflags) & SAFE_MASK) | VM_MASK;
+
+ ts->vm86plus.cpu_type = tswapl(target_v86->cpu_type);
+ switch (ts->vm86plus.cpu_type) {
+ case TARGET_CPU_286:
+ ts->v86mask = 0;
+ break;
+ case TARGET_CPU_386:
+ ts->v86mask = NT_MASK | IOPL_MASK;
+ break;
+ case TARGET_CPU_486:
+ ts->v86mask = AC_MASK | NT_MASK | IOPL_MASK;
+ break;
+ default:
+ ts->v86mask = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK;
+ break;
+ }
+
+ env->regs[R_EBX] = tswap32(target_v86->regs.ebx);
+ env->regs[R_ECX] = tswap32(target_v86->regs.ecx);
+ env->regs[R_EDX] = tswap32(target_v86->regs.edx);
+ env->regs[R_ESI] = tswap32(target_v86->regs.esi);
+ env->regs[R_EDI] = tswap32(target_v86->regs.edi);
+ env->regs[R_EBP] = tswap32(target_v86->regs.ebp);
+ env->regs[R_ESP] = tswap32(target_v86->regs.esp);
+ env->eip = tswap32(target_v86->regs.eip);
+ cpu_x86_load_seg(env, R_CS, tswap16(target_v86->regs.cs));
+ cpu_x86_load_seg(env, R_SS, tswap16(target_v86->regs.ss));
+ cpu_x86_load_seg(env, R_DS, tswap16(target_v86->regs.ds));
+ cpu_x86_load_seg(env, R_ES, tswap16(target_v86->regs.es));
+ cpu_x86_load_seg(env, R_FS, tswap16(target_v86->regs.fs));
+ cpu_x86_load_seg(env, R_GS, tswap16(target_v86->regs.gs));
+ ret = tswap32(target_v86->regs.eax); /* eax will be restored at
+ the end of the syscall */
+ memcpy(&ts->vm86plus.int_revectored,
+ &target_v86->int_revectored, 32);
+ memcpy(&ts->vm86plus.int21_revectored,
+ &target_v86->int21_revectored, 32);
+ ts->vm86plus.vm86plus.flags = tswapl(target_v86->vm86plus.flags);
+ memcpy(&ts->vm86plus.vm86plus.vm86dbg_intxxtab,
+ target_v86->vm86plus.vm86dbg_intxxtab, 32);
+ unlock_user_struct(target_v86, vm86_addr, 0);
+
+ LOG_VM86("do_vm86: cs:ip=%04x:%04x\n",
+ env->segs[R_CS].selector, env->eip);
+ /* now the virtual CPU is ready for vm86 execution ! */
+ out:
+ return ret;
+}
diff --git a/linux-user/x86_64/syscall.h b/linux-user/x86_64/syscall.h
new file mode 100644
index 0000000..2a8d696
--- /dev/null
+++ b/linux-user/x86_64/syscall.h
@@ -0,0 +1,98 @@
+#define __USER_CS (0x33)
+#define __USER_DS (0x2B)
+
+struct target_pt_regs {
+ abi_ulong r15;
+ abi_ulong r14;
+ abi_ulong r13;
+ abi_ulong r12;
+ abi_ulong rbp;
+ abi_ulong rbx;
+/* arguments: non interrupts/non tracing syscalls only save upto here*/
+ abi_ulong r11;
+ abi_ulong r10;
+ abi_ulong r9;
+ abi_ulong r8;
+ abi_ulong rax;
+ abi_ulong rcx;
+ abi_ulong rdx;
+ abi_ulong rsi;
+ abi_ulong rdi;
+ abi_ulong orig_rax;
+/* end of arguments */
+/* cpu exception frame or undefined */
+ abi_ulong rip;
+ abi_ulong cs;
+ abi_ulong eflags;
+ abi_ulong rsp;
+ abi_ulong ss;
+/* top of stack page */
+};
+
+/* Maximum number of LDT entries supported. */
+#define TARGET_LDT_ENTRIES 8192
+/* The size of each LDT entry. */
+#define TARGET_LDT_ENTRY_SIZE 8
+
+#define TARGET_GDT_ENTRIES 16
+#define TARGET_GDT_ENTRY_TLS_ENTRIES 3
+#define TARGET_GDT_ENTRY_TLS_MIN 12
+#define TARGET_GDT_ENTRY_TLS_MAX 14
+
+#if 0 // Redefine this
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ abi_ulong base_addr;
+ unsigned int limit;
+ unsigned int seg_32bit:1;
+ unsigned int contents:2;
+ unsigned int read_exec_only:1;
+ unsigned int limit_in_pages:1;
+ unsigned int seg_not_present:1;
+ unsigned int useable:1;
+ unsigned int lm:1;
+};
+#else
+struct target_modify_ldt_ldt_s {
+ unsigned int entry_number;
+ abi_ulong base_addr;
+ unsigned int limit;
+ unsigned int flags;
+};
+#endif
+
+struct target_ipc64_perm
+{
+ int key;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t cuid;
+ uint32_t cgid;
+ unsigned short mode;
+ unsigned short __pad1;
+ unsigned short seq;
+ unsigned short __pad2;
+ abi_ulong __unused1;
+ abi_ulong __unused2;
+};
+
+struct target_msqid64_ds {
+ struct target_ipc64_perm msg_perm;
+ unsigned int msg_stime; /* last msgsnd time */
+ unsigned int msg_rtime; /* last msgrcv time */
+ unsigned int msg_ctime; /* last change time */
+ abi_ulong msg_cbytes; /* current number of bytes on queue */
+ abi_ulong msg_qnum; /* number of messages in queue */
+ abi_ulong msg_qbytes; /* max number of bytes on queue */
+ unsigned int msg_lspid; /* pid of last msgsnd */
+ unsigned int msg_lrpid; /* last receive pid */
+ abi_ulong __unused4;
+ abi_ulong __unused5;
+};
+
+#define UNAME_MACHINE "x86_64"
+
+#define TARGET_ARCH_SET_GS 0x1001
+#define TARGET_ARCH_SET_FS 0x1002
+#define TARGET_ARCH_GET_FS 0x1003
+#define TARGET_ARCH_GET_GS 0x1004
diff --git a/linux-user/x86_64/syscall_nr.h b/linux-user/x86_64/syscall_nr.h
new file mode 100644
index 0000000..568a901
--- /dev/null
+++ b/linux-user/x86_64/syscall_nr.h
@@ -0,0 +1,295 @@
+#define TARGET_NR_read 0
+#define TARGET_NR_write 1
+#define TARGET_NR_open 2
+#define TARGET_NR_close 3
+#define TARGET_NR_stat 4
+#define TARGET_NR_fstat 5
+#define TARGET_NR_lstat 6
+#define TARGET_NR_poll 7
+#define TARGET_NR_lseek 8
+#define TARGET_NR_mmap 9
+#define TARGET_NR_mprotect 10
+#define TARGET_NR_munmap 11
+#define TARGET_NR_brk 12
+#define TARGET_NR_rt_sigaction 13
+#define TARGET_NR_rt_sigprocmask 14
+#define TARGET_NR_rt_sigreturn 15
+#define TARGET_NR_ioctl 16
+#define TARGET_NR_pread64 17
+#define TARGET_NR_pwrite64 18
+#define TARGET_NR_readv 19
+#define TARGET_NR_writev 20
+#define TARGET_NR_access 21
+#define TARGET_NR_pipe 22
+#define TARGET_NR_select 23
+#define TARGET_NR_sched_yield 24
+#define TARGET_NR_mremap 25
+#define TARGET_NR_msync 26
+#define TARGET_NR_mincore 27
+#define TARGET_NR_madvise 28
+#define TARGET_NR_shmget 29
+#define TARGET_NR_shmat 30
+#define TARGET_NR_shmctl 31
+#define TARGET_NR_dup 32
+#define TARGET_NR_dup2 33
+#define TARGET_NR_pause 34
+#define TARGET_NR_nanosleep 35
+#define TARGET_NR_getitimer 36
+#define TARGET_NR_alarm 37
+#define TARGET_NR_setitimer 38
+#define TARGET_NR_getpid 39
+#define TARGET_NR_sendfile 40
+#define TARGET_NR_socket 41
+#define TARGET_NR_connect 42
+#define TARGET_NR_accept 43
+#define TARGET_NR_sendto 44
+#define TARGET_NR_recvfrom 45
+#define TARGET_NR_sendmsg 46
+#define TARGET_NR_recvmsg 47
+#define TARGET_NR_shutdown 48
+#define TARGET_NR_bind 49
+#define TARGET_NR_listen 50
+#define TARGET_NR_getsockname 51
+#define TARGET_NR_getpeername 52
+#define TARGET_NR_socketpair 53
+#define TARGET_NR_setsockopt 54
+#define TARGET_NR_getsockopt 55
+#define TARGET_NR_clone 56
+#define TARGET_NR_fork 57
+#define TARGET_NR_vfork 58
+#define TARGET_NR_execve 59
+#define TARGET_NR_exit 60
+#define TARGET_NR_wait4 61
+#define TARGET_NR_kill 62
+#define TARGET_NR_uname 63
+#define TARGET_NR_semget 64
+#define TARGET_NR_semop 65
+#define TARGET_NR_semctl 66
+#define TARGET_NR_shmdt 67
+#define TARGET_NR_msgget 68
+#define TARGET_NR_msgsnd 69
+#define TARGET_NR_msgrcv 70
+#define TARGET_NR_msgctl 71
+#define TARGET_NR_fcntl 72
+#define TARGET_NR_flock 73
+#define TARGET_NR_fsync 74
+#define TARGET_NR_fdatasync 75
+#define TARGET_NR_truncate 76
+#define TARGET_NR_ftruncate 77
+#define TARGET_NR_getdents 78
+#define TARGET_NR_getcwd 79
+#define TARGET_NR_chdir 80
+#define TARGET_NR_fchdir 81
+#define TARGET_NR_rename 82
+#define TARGET_NR_mkdir 83
+#define TARGET_NR_rmdir 84
+#define TARGET_NR_creat 85
+#define TARGET_NR_link 86
+#define TARGET_NR_unlink 87
+#define TARGET_NR_symlink 88
+#define TARGET_NR_readlink 89
+#define TARGET_NR_chmod 90
+#define TARGET_NR_fchmod 91
+#define TARGET_NR_chown 92
+#define TARGET_NR_fchown 93
+#define TARGET_NR_lchown 94
+#define TARGET_NR_umask 95
+#define TARGET_NR_gettimeofday 96
+#define TARGET_NR_getrlimit 97
+#define TARGET_NR_getrusage 98
+#define TARGET_NR_sysinfo 99
+#define TARGET_NR_times 100
+#define TARGET_NR_ptrace 101
+#define TARGET_NR_getuid 102
+#define TARGET_NR_syslog 103
+#define TARGET_NR_getgid 104
+#define TARGET_NR_setuid 105
+#define TARGET_NR_setgid 106
+#define TARGET_NR_geteuid 107
+#define TARGET_NR_getegid 108
+#define TARGET_NR_setpgid 109
+#define TARGET_NR_getppid 110
+#define TARGET_NR_getpgrp 111
+#define TARGET_NR_setsid 112
+#define TARGET_NR_setreuid 113
+#define TARGET_NR_setregid 114
+#define TARGET_NR_getgroups 115
+#define TARGET_NR_setgroups 116
+#define TARGET_NR_setresuid 117
+#define TARGET_NR_getresuid 118
+#define TARGET_NR_setresgid 119
+#define TARGET_NR_getresgid 120
+#define TARGET_NR_getpgid 121
+#define TARGET_NR_setfsuid 122
+#define TARGET_NR_setfsgid 123
+#define TARGET_NR_getsid 124
+#define TARGET_NR_capget 125
+#define TARGET_NR_capset 126
+#define TARGET_NR_rt_sigpending 127
+#define TARGET_NR_rt_sigtimedwait 128
+#define TARGET_NR_rt_sigqueueinfo 129
+#define TARGET_NR_rt_sigsuspend 130
+#define TARGET_NR_sigaltstack 131
+#define TARGET_NR_utime 132
+#define TARGET_NR_mknod 133
+#define TARGET_NR_uselib 134
+#define TARGET_NR_personality 135
+#define TARGET_NR_ustat 136
+#define TARGET_NR_statfs 137
+#define TARGET_NR_fstatfs 138
+#define TARGET_NR_sysfs 139
+#define TARGET_NR_getpriority 140
+#define TARGET_NR_setpriority 141
+#define TARGET_NR_sched_setparam 142
+#define TARGET_NR_sched_getparam 143
+#define TARGET_NR_sched_setscheduler 144
+#define TARGET_NR_sched_getscheduler 145
+#define TARGET_NR_sched_get_priority_max 146
+#define TARGET_NR_sched_get_priority_min 147
+#define TARGET_NR_sched_rr_get_interval 148
+#define TARGET_NR_mlock 149
+#define TARGET_NR_munlock 150
+#define TARGET_NR_mlockall 151
+#define TARGET_NR_munlockall 152
+#define TARGET_NR_vhangup 153
+#define TARGET_NR_modify_ldt 154
+#define TARGET_NR_pivot_root 155
+#define TARGET_NR__sysctl 156
+#define TARGET_NR_prctl 157
+#define TARGET_NR_arch_prctl 158
+#define TARGET_NR_adjtimex 159
+#define TARGET_NR_setrlimit 160
+#define TARGET_NR_chroot 161
+#define TARGET_NR_sync 162
+#define TARGET_NR_acct 163
+#define TARGET_NR_settimeofday 164
+#define TARGET_NR_mount 165
+#define TARGET_NR_umount2 166
+#define TARGET_NR_swapon 167
+#define TARGET_NR_swapoff 168
+#define TARGET_NR_reboot 169
+#define TARGET_NR_sethostname 170
+#define TARGET_NR_setdomainname 171
+#define TARGET_NR_iopl 172
+#define TARGET_NR_ioperm 173
+#define TARGET_NR_create_module 174
+#define TARGET_NR_init_module 175
+#define TARGET_NR_delete_module 176
+#define TARGET_NR_get_kernel_syms 177
+#define TARGET_NR_query_module 178
+#define TARGET_NR_quotactl 179
+#define TARGET_NR_nfsservctl 180
+#define TARGET_NR_getpmsg 181 /* reserved for LiS/STREAMS */
+#define TARGET_NR_putpmsg 182 /* reserved for LiS/STREAMS */
+#define TARGET_NR_afs_syscall 183 /* reserved for AFS */
+#define TARGET_NR_tuxcall 184 /* reserved for tux */
+#define TARGET_NR_security 185
+#define TARGET_NR_gettid 186
+#define TARGET_NR_readahead 187
+#define TARGET_NR_setxattr 188
+#define TARGET_NR_lsetxattr 189
+#define TARGET_NR_fsetxattr 190
+#define TARGET_NR_getxattr 191
+#define TARGET_NR_lgetxattr 192
+#define TARGET_NR_fgetxattr 193
+#define TARGET_NR_listxattr 194
+#define TARGET_NR_llistxattr 195
+#define TARGET_NR_flistxattr 196
+#define TARGET_NR_removexattr 197
+#define TARGET_NR_lremovexattr 198
+#define TARGET_NR_fremovexattr 199
+#define TARGET_NR_tkill 200
+#define TARGET_NR_time 201
+#define TARGET_NR_futex 202
+#define TARGET_NR_sched_setaffinity 203
+#define TARGET_NR_sched_getaffinity 204
+#define TARGET_NR_set_thread_area 205
+#define TARGET_NR_io_setup 206
+#define TARGET_NR_io_destroy 207
+#define TARGET_NR_io_getevents 208
+#define TARGET_NR_io_submit 209
+#define TARGET_NR_io_cancel 210
+#define TARGET_NR_get_thread_area 211
+#define TARGET_NR_lookup_dcookie 212
+#define TARGET_NR_epoll_create 213
+#define TARGET_NR_epoll_ctl_old 214
+#define TARGET_NR_epoll_wait_old 215
+#define TARGET_NR_remap_file_pages 216
+#define TARGET_NR_getdents64 217
+#define TARGET_NR_set_tid_address 218
+#define TARGET_NR_restart_syscall 219
+#define TARGET_NR_semtimedop 220
+#define TARGET_NR_fadvise64 221
+#define TARGET_NR_timer_create 222
+#define TARGET_NR_timer_settime 223
+#define TARGET_NR_timer_gettime 224
+#define TARGET_NR_timer_getoverrun 225
+#define TARGET_NR_timer_delete 226
+#define TARGET_NR_clock_settime 227
+#define TARGET_NR_clock_gettime 228
+#define TARGET_NR_clock_getres 229
+#define TARGET_NR_clock_nanosleep 230
+#define TARGET_NR_exit_group 231
+#define TARGET_NR_epoll_wait 232
+#define TARGET_NR_epoll_ctl 233
+#define TARGET_NR_tgkill 234
+#define TARGET_NR_utimes 235
+#define TARGET_NR_vserver 236
+#define TARGET_NR_mbind 237
+#define TARGET_NR_set_mempolicy 238
+#define TARGET_NR_get_mempolicy 239
+#define TARGET_NR_mq_open 240
+#define TARGET_NR_mq_unlink 241
+#define TARGET_NR_mq_timedsend 242
+#define TARGET_NR_mq_timedreceive 243
+#define TARGET_NR_mq_notify 244
+#define TARGET_NR_mq_getsetattr 245
+#define TARGET_NR_kexec_load 246
+#define TARGET_NR_waitid 247
+#define TARGET_NR_add_key 248
+#define TARGET_NR_request_key 249
+#define TARGET_NR_keyctl 250
+#define TARGET_NR_ioprio_set 251
+#define TARGET_NR_ioprio_get 252
+#define TARGET_NR_inotify_init 253
+#define TARGET_NR_inotify_add_watch 254
+#define TARGET_NR_inotify_rm_watch 255
+#define TARGET_NR_migrate_pages 256
+#define TARGET_NR_openat 257
+#define TARGET_NR_mkdirat 258
+#define TARGET_NR_mknodat 259
+#define TARGET_NR_fchownat 260
+#define TARGET_NR_futimesat 261
+#define TARGET_NR_newfstatat 262
+#define TARGET_NR_unlinkat 263
+#define TARGET_NR_renameat 264
+#define TARGET_NR_linkat 265
+#define TARGET_NR_symlinkat 266
+#define TARGET_NR_readlinkat 267
+#define TARGET_NR_fchmodat 268
+#define TARGET_NR_faccessat 269
+#define TARGET_NR_pselect6 270
+#define TARGET_NR_ppoll 271
+#define TARGET_NR_unshare 272
+#define TARGET_NR_set_robust_list 273
+#define TARGET_NR_get_robust_list 274
+#define TARGET_NR_splice 275
+#define TARGET_NR_tee 276
+#define TARGET_NR_sync_file_range 277
+#define TARGET_NR_vmsplice 278
+#define TARGET_NR_move_pages 279
+#define TARGET_NR_utimensat 280
+#define TARGET_NR_epoll_pwait 281
+#define TARGET_NR_signalfd 282
+#define TARGET_NR_timerfd 283
+#define TARGET_NR_eventfd 284
+#define TARGET_NR_fallocate 285
+#define TARGET_NR_timerfd_settime 286
+#define TARGET_NR_timerfd_gettime 287
+#define TARGET_NR_accept4 288
+#define TARGET_NR_signalfd4 289
+#define TARGET_NR_eventfd2 290
+#define TARGET_NR_epoll_create1 291
+#define TARGET_NR_dup3 292
+#define TARGET_NR_pipe2 293
+#define TARGET_NR_inotify_init1 294
diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h
new file mode 100644
index 0000000..9baf7fb
--- /dev/null
+++ b/linux-user/x86_64/target_signal.h
@@ -0,0 +1,29 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+/* this struct defines a stack used during syscall handling */
+
+typedef struct target_sigaltstack {
+ abi_ulong ss_sp;
+ abi_long 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(CPUX86State *state)
+{
+ return state->regs[R_ESP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/x86_64/termbits.h b/linux-user/x86_64/termbits.h
new file mode 100644
index 0000000..1c3445c
--- /dev/null
+++ b/linux-user/x86_64/termbits.h
@@ -0,0 +1,247 @@
+#define TARGET_NCCS 19
+
+typedef unsigned char target_cc_t;
+typedef unsigned int target_speed_t;
+typedef unsigned int target_tcflag_t;
+struct target_termios {
+ target_tcflag_t c_iflag; /* input mode flags */
+ target_tcflag_t c_oflag; /* output mode flags */
+ target_tcflag_t c_cflag; /* control mode flags */
+ target_tcflag_t c_lflag; /* local mode flags */
+ target_cc_t c_line; /* line discipline */
+ target_cc_t c_cc[TARGET_NCCS]; /* control characters */
+};
+
+/* 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 /* non standard rate */
+#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 8 /* 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
+
+#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_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_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */
+#define TARGET_FIOQSIZE 0x5460
+
+/* 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/m68k-dis.c b/m68k-dis.c
new file mode 100644
index 0000000..04f837a
--- /dev/null
+++ b/m68k-dis.c
@@ -0,0 +1,5051 @@
+/* This file is composed of several different files from the upstream
+ sourceware.org CVS. Original file boundaries marked with **** */
+
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "dis-asm.h"
+
+/* **** floatformat.h from sourceware.org CVS 2005-08-14. */
+/* IEEE floating point support declarations, for GDB, the GNU Debugger.
+ Copyright 1991, 1994, 1995, 1997, 2000, 2003 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 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/>. */
+
+#if !defined (FLOATFORMAT_H)
+#define FLOATFORMAT_H 1
+
+/*#include "ansidecl.h" */
+
+/* A floatformat consists of a sign bit, an exponent and a mantissa. Once the
+ bytes are concatenated according to the byteorder flag, then each of those
+ fields is contiguous. We number the bits with 0 being the most significant
+ (i.e. BITS_BIG_ENDIAN type numbering), and specify which bits each field
+ contains with the *_start and *_len fields. */
+
+/* What is the order of the bytes. */
+
+enum floatformat_byteorders {
+
+ /* Standard little endian byte order.
+ EX: 1.2345678e10 => 00 00 80 c5 e0 fe 06 42 */
+
+ floatformat_little,
+
+ /* Standard big endian byte order.
+ EX: 1.2345678e10 => 42 06 fe e0 c5 80 00 00 */
+
+ floatformat_big,
+
+ /* Little endian byte order but big endian word order.
+ EX: 1.2345678e10 => e0 fe 06 42 00 00 80 c5 */
+
+ floatformat_littlebyte_bigword
+
+};
+
+enum floatformat_intbit { floatformat_intbit_yes, floatformat_intbit_no };
+
+struct floatformat
+{
+ enum floatformat_byteorders byteorder;
+ unsigned int totalsize; /* Total size of number in bits */
+
+ /* Sign bit is always one bit long. 1 means negative, 0 means positive. */
+ unsigned int sign_start;
+
+ unsigned int exp_start;
+ unsigned int exp_len;
+ /* Bias added to a "true" exponent to form the biased exponent. It
+ is intentionally signed as, otherwize, -exp_bias can turn into a
+ very large number (e.g., given the exp_bias of 0x3fff and a 64
+ bit long, the equation (long)(1 - exp_bias) evaluates to
+ 4294950914) instead of -16382). */
+ int exp_bias;
+ /* Exponent value which indicates NaN. This is the actual value stored in
+ the float, not adjusted by the exp_bias. This usually consists of all
+ one bits. */
+ unsigned int exp_nan;
+
+ unsigned int man_start;
+ unsigned int man_len;
+
+ /* Is the integer bit explicit or implicit? */
+ enum floatformat_intbit intbit;
+
+ /* Internal name for debugging. */
+ const char *name;
+
+ /* Validator method. */
+ int (*is_valid) (const struct floatformat *fmt, const char *from);
+};
+
+/* floatformats for IEEE single and double, big and little endian. */
+
+extern const struct floatformat floatformat_ieee_single_big;
+extern const struct floatformat floatformat_ieee_single_little;
+extern const struct floatformat floatformat_ieee_double_big;
+extern const struct floatformat floatformat_ieee_double_little;
+
+/* floatformat for ARM IEEE double, little endian bytes and big endian words */
+
+extern const struct floatformat floatformat_ieee_double_littlebyte_bigword;
+
+/* floatformats for various extendeds. */
+
+extern const struct floatformat floatformat_i387_ext;
+extern const struct floatformat floatformat_m68881_ext;
+extern const struct floatformat floatformat_i960_ext;
+extern const struct floatformat floatformat_m88110_ext;
+extern const struct floatformat floatformat_m88110_harris_ext;
+extern const struct floatformat floatformat_arm_ext_big;
+extern const struct floatformat floatformat_arm_ext_littlebyte_bigword;
+/* IA-64 Floating Point register spilt into memory. */
+extern const struct floatformat floatformat_ia64_spill_big;
+extern const struct floatformat floatformat_ia64_spill_little;
+extern const struct floatformat floatformat_ia64_quad_big;
+extern const struct floatformat floatformat_ia64_quad_little;
+
+/* Convert from FMT to a double.
+ FROM is the address of the extended float.
+ Store the double in *TO. */
+
+extern void
+floatformat_to_double (const struct floatformat *, const char *, double *);
+
+/* The converse: convert the double *FROM to FMT
+ and store where TO points. */
+
+extern void
+floatformat_from_double (const struct floatformat *, const double *, char *);
+
+/* Return non-zero iff the data at FROM is a valid number in format FMT. */
+
+extern int
+floatformat_is_valid (const struct floatformat *fmt, const char *from);
+
+#endif /* defined (FLOATFORMAT_H) */
+/* **** End of floatformat.h */
+/* **** m68k-dis.h from sourceware.org CVS 2005-08-14. */
+/* Opcode table header for m680[01234]0/m6888[12]/m68851.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2001,
+ 2003, 2004 Free Software Foundation, Inc.
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 1, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+/* These are used as bit flags for the arch field in the m68k_opcode
+ structure. */
+#define _m68k_undef 0
+#define m68000 0x001
+#define m68008 m68000 /* Synonym for -m68000. otherwise unused. */
+#define m68010 0x002
+#define m68020 0x004
+#define m68030 0x008
+#define m68ec030 m68030 /* Similar enough to -m68030 to ignore differences;
+ gas will deal with the few differences. */
+#define m68040 0x010
+/* There is no 68050. */
+#define m68060 0x020
+#define m68881 0x040
+#define m68882 m68881 /* Synonym for -m68881. otherwise unused. */
+#define m68851 0x080
+#define cpu32 0x100 /* e.g., 68332 */
+
+#define mcfmac 0x200 /* ColdFire MAC. */
+#define mcfemac 0x400 /* ColdFire EMAC. */
+#define cfloat 0x800 /* ColdFire FPU. */
+#define mcfhwdiv 0x1000 /* ColdFire hardware divide. */
+
+#define mcfisa_a 0x2000 /* ColdFire ISA_A. */
+#define mcfisa_aa 0x4000 /* ColdFire ISA_A+. */
+#define mcfisa_b 0x8000 /* ColdFire ISA_B. */
+#define mcfusp 0x10000 /* ColdFire USP instructions. */
+
+#define mcf5200 0x20000
+#define mcf5206e 0x40000
+#define mcf521x 0x80000
+#define mcf5249 0x100000
+#define mcf528x 0x200000
+#define mcf5307 0x400000
+#define mcf5407 0x800000
+#define mcf5470 0x1000000
+#define mcf5480 0x2000000
+
+ /* Handy aliases. */
+#define m68040up (m68040 | m68060)
+#define m68030up (m68030 | m68040up)
+#define m68020up (m68020 | m68030up)
+#define m68010up (m68010 | cpu32 | m68020up)
+#define m68000up (m68000 | m68010up)
+
+#define mfloat (m68881 | m68882 | m68040 | m68060)
+#define mmmu (m68851 | m68030 | m68040 | m68060)
+
+/* The structure used to hold information for an opcode. */
+
+struct m68k_opcode
+{
+ /* The opcode name. */
+ const char *name;
+ /* The pseudo-size of the instruction(in bytes). Used to determine
+ number of bytes necessary to disassemble the instruction. */
+ unsigned int size;
+ /* The opcode itself. */
+ unsigned long opcode;
+ /* The mask used by the disassembler. */
+ unsigned long match;
+ /* The arguments. */
+ const char *args;
+ /* The architectures which support this opcode. */
+ unsigned int arch;
+};
+
+/* The structure used to hold information for an opcode alias. */
+
+struct m68k_opcode_alias
+{
+ /* The alias name. */
+ const char *alias;
+ /* The instruction for which this is an alias. */
+ const char *primary;
+};
+
+/* We store four bytes of opcode for all opcodes because that is the
+ most any of them need. The actual length of an instruction is
+ always at least 2 bytes, and is as much longer as necessary to hold
+ the operands it has.
+
+ The match field is a mask saying which bits must match particular
+ opcode in order for an instruction to be an instance of that
+ opcode.
+
+ The args field is a string containing two characters for each
+ operand of the instruction. The first specifies the kind of
+ operand; the second, the place it is stored. */
+
+/* Kinds of operands:
+ Characters used: AaBbCcDdEeFfGgHIiJkLlMmnOopQqRrSsTtU VvWwXxYyZz01234|*~%;@!&$?/<>#^+-
+
+ D data register only. Stored as 3 bits.
+ A address register only. Stored as 3 bits.
+ a address register indirect only. Stored as 3 bits.
+ R either kind of register. Stored as 4 bits.
+ r either kind of register indirect only. Stored as 4 bits.
+ At the moment, used only for cas2 instruction.
+ F floating point coprocessor register only. Stored as 3 bits.
+ O an offset (or width): immediate data 0-31 or data register.
+ Stored as 6 bits in special format for BF... insns.
+ + autoincrement only. Stored as 3 bits (number of the address register).
+ - autodecrement only. Stored as 3 bits (number of the address register).
+ Q quick immediate data. Stored as 3 bits.
+ This matches an immediate operand only when value is in range 1 .. 8.
+ M moveq immediate data. Stored as 8 bits.
+ This matches an immediate operand only when value is in range -128..127
+ T trap vector immediate data. Stored as 4 bits.
+
+ k K-factor for fmove.p instruction. Stored as a 7-bit constant or
+ a three bit register offset, depending on the field type.
+
+ # immediate data. Stored in special places (b, w or l)
+ which say how many bits to store.
+ ^ immediate data for floating point instructions. Special places
+ are offset by 2 bytes from '#'...
+ B pc-relative address, converted to an offset
+ that is treated as immediate data.
+ d displacement and register. Stores the register as 3 bits
+ and stores the displacement in the entire second word.
+
+ C the CCR. No need to store it; this is just for filtering validity.
+ S the SR. No need to store, just as with CCR.
+ U the USP. No need to store, just as with CCR.
+ E the MAC ACC. No need to store, just as with CCR.
+ e the EMAC ACC[0123].
+ G the MAC/EMAC MACSR. No need to store, just as with CCR.
+ g the EMAC ACCEXT{01,23}.
+ H the MASK. No need to store, just as with CCR.
+ i the MAC/EMAC scale factor.
+
+ I Coprocessor ID. Not printed if 1. The Coprocessor ID is always
+ extracted from the 'd' field of word one, which means that an extended
+ coprocessor opcode can be skipped using the 'i' place, if needed.
+
+ s System Control register for the floating point coprocessor.
+
+ J Misc register for movec instruction, stored in 'j' format.
+ Possible values:
+ 0x000 SFC Source Function Code reg [60, 40, 30, 20, 10]
+ 0x001 DFC Data Function Code reg [60, 40, 30, 20, 10]
+ 0x002 CACR Cache Control Register [60, 40, 30, 20, mcf]
+ 0x003 TC MMU Translation Control [60, 40]
+ 0x004 ITT0 Instruction Transparent
+ Translation reg 0 [60, 40]
+ 0x005 ITT1 Instruction Transparent
+ Translation reg 1 [60, 40]
+ 0x006 DTT0 Data Transparent
+ Translation reg 0 [60, 40]
+ 0x007 DTT1 Data Transparent
+ Translation reg 1 [60, 40]
+ 0x008 BUSCR Bus Control Register [60]
+ 0x800 USP User Stack Pointer [60, 40, 30, 20, 10]
+ 0x801 VBR Vector Base reg [60, 40, 30, 20, 10, mcf]
+ 0x802 CAAR Cache Address Register [ 30, 20]
+ 0x803 MSP Master Stack Pointer [ 40, 30, 20]
+ 0x804 ISP Interrupt Stack Pointer [ 40, 30, 20]
+ 0x805 MMUSR MMU Status reg [ 40]
+ 0x806 URP User Root Pointer [60, 40]
+ 0x807 SRP Supervisor Root Pointer [60, 40]
+ 0x808 PCR Processor Configuration reg [60]
+ 0xC00 ROMBAR ROM Base Address Register [520X]
+ 0xC04 RAMBAR0 RAM Base Address Register 0 [520X]
+ 0xC05 RAMBAR1 RAM Base Address Register 0 [520X]
+ 0xC0F MBAR0 RAM Base Address Register 0 [520X]
+ 0xC04 FLASHBAR FLASH Base Address Register [mcf528x]
+ 0xC05 RAMBAR Static RAM Base Address Register [mcf528x]
+
+ L Register list of the type d0-d7/a0-a7 etc.
+ (New! Improved! Can also hold fp0-fp7, as well!)
+ The assembler tries to see if the registers match the insn by
+ looking at where the insn wants them stored.
+
+ l Register list like L, but with all the bits reversed.
+ Used for going the other way. . .
+
+ c cache identifier which may be "nc" for no cache, "ic"
+ for instruction cache, "dc" for data cache, or "bc"
+ for both caches. Used in cinv and cpush. Always
+ stored in position "d".
+
+ u Any register, with ``upper'' or ``lower'' specification. Used
+ in the mac instructions with size word.
+
+ The remainder are all stored as 6 bits using an address mode and a
+ register number; they differ in which addressing modes they match.
+
+ * all (modes 0-6,7.0-4)
+ ~ alterable memory (modes 2-6,7.0,7.1)
+ (not 0,1,7.2-4)
+ % alterable (modes 0-6,7.0,7.1)
+ (not 7.2-4)
+ ; data (modes 0,2-6,7.0-4)
+ (not 1)
+ @ data, but not immediate (modes 0,2-6,7.0-3)
+ (not 1,7.4)
+ ! control (modes 2,5,6,7.0-3)
+ (not 0,1,3,4,7.4)
+ & alterable control (modes 2,5,6,7.0,7.1)
+ (not 0,1,3,4,7.2-4)
+ $ alterable data (modes 0,2-6,7.0,7.1)
+ (not 1,7.2-4)
+ ? alterable control, or data register (modes 0,2,5,6,7.0,7.1)
+ (not 1,3,4,7.2-4)
+ / control, or data register (modes 0,2,5,6,7.0-3)
+ (not 1,3,4,7.4)
+ > *save operands (modes 2,4,5,6,7.0,7.1)
+ (not 0,1,3,7.2-4)
+ < *restore operands (modes 2,3,5,6,7.0-3)
+ (not 0,1,4,7.4)
+
+ coldfire move operands:
+ m (modes 0-4)
+ n (modes 5,7.2)
+ o (modes 6,7.0,7.1,7.3,7.4)
+ p (modes 0-5)
+
+ coldfire bset/bclr/btst/mulsl/mulul operands:
+ q (modes 0,2-5)
+ v (modes 0,2-5,7.0,7.1)
+ b (modes 0,2-5,7.2)
+ w (modes 2-5,7.2)
+ y (modes 2,5)
+ z (modes 2,5,7.2)
+ x mov3q immediate operand.
+ 4 (modes 2,3,4,5)
+ */
+
+/* For the 68851: */
+/* I didn't use much imagination in choosing the
+ following codes, so many of them aren't very
+ mnemonic. -rab
+
+ 0 32 bit pmmu register
+ Possible values:
+ 000 TC Translation Control Register (68030, 68851)
+
+ 1 16 bit pmmu register
+ 111 AC Access Control (68851)
+
+ 2 8 bit pmmu register
+ 100 CAL Current Access Level (68851)
+ 101 VAL Validate Access Level (68851)
+ 110 SCC Stack Change Control (68851)
+
+ 3 68030-only pmmu registers (32 bit)
+ 010 TT0 Transparent Translation reg 0
+ (aka Access Control reg 0 -- AC0 -- on 68ec030)
+ 011 TT1 Transparent Translation reg 1
+ (aka Access Control reg 1 -- AC1 -- on 68ec030)
+
+ W wide pmmu registers
+ Possible values:
+ 001 DRP Dma Root Pointer (68851)
+ 010 SRP Supervisor Root Pointer (68030, 68851)
+ 011 CRP Cpu Root Pointer (68030, 68851)
+
+ f function code register (68030, 68851)
+ 0 SFC
+ 1 DFC
+
+ V VAL register only (68851)
+
+ X BADx, BACx (16 bit)
+ 100 BAD Breakpoint Acknowledge Data (68851)
+ 101 BAC Breakpoint Acknowledge Control (68851)
+
+ Y PSR (68851) (MMUSR on 68030) (ACUSR on 68ec030)
+ Z PCSR (68851)
+
+ | memory (modes 2-6, 7.*)
+
+ t address test level (68030 only)
+ Stored as 3 bits, range 0-7.
+ Also used for breakpoint instruction now.
+
+*/
+
+/* Places to put an operand, for non-general operands:
+ Characters used: BbCcDdFfGgHhIijkLlMmNnostWw123456789/
+
+ s source, low bits of first word.
+ d dest, shifted 9 in first word
+ 1 second word, shifted 12
+ 2 second word, shifted 6
+ 3 second word, shifted 0
+ 4 third word, shifted 12
+ 5 third word, shifted 6
+ 6 third word, shifted 0
+ 7 second word, shifted 7
+ 8 second word, shifted 10
+ 9 second word, shifted 5
+ D store in both place 1 and place 3; for divul and divsl.
+ B first word, low byte, for branch displacements
+ W second word (entire), for branch displacements
+ L second and third words (entire), for branch displacements
+ (also overloaded for move16)
+ b second word, low byte
+ w second word (entire) [variable word/long branch offset for dbra]
+ W second word (entire) (must be signed 16 bit value)
+ l second and third word (entire)
+ g variable branch offset for bra and similar instructions.
+ The place to store depends on the magnitude of offset.
+ t store in both place 7 and place 8; for floating point operations
+ c branch offset for cpBcc operations.
+ The place to store is word two if bit six of word one is zero,
+ and words two and three if bit six of word one is one.
+ i Increment by two, to skip over coprocessor extended operands. Only
+ works with the 'I' format.
+ k Dynamic K-factor field. Bits 6-4 of word 2, used as a register number.
+ Also used for dynamic fmovem instruction.
+ C floating point coprocessor constant - 7 bits. Also used for static
+ K-factors...
+ j Movec register #, stored in 12 low bits of second word.
+ m For M[S]ACx; 4 bits split with MSB shifted 6 bits in first word
+ and remaining 3 bits of register shifted 9 bits in first word.
+ Indicate upper/lower in 1 bit shifted 7 bits in second word.
+ Use with `R' or `u' format.
+ n `m' withouth upper/lower indication. (For M[S]ACx; 4 bits split
+ with MSB shifted 6 bits in first word and remaining 3 bits of
+ register shifted 9 bits in first word. No upper/lower
+ indication is done.) Use with `R' or `u' format.
+ o For M[S]ACw; 4 bits shifted 12 in second word (like `1').
+ Indicate upper/lower in 1 bit shifted 7 bits in second word.
+ Use with `R' or `u' format.
+ M For M[S]ACw; 4 bits in low bits of first word. Indicate
+ upper/lower in 1 bit shifted 6 bits in second word. Use with
+ `R' or `u' format.
+ N For M[S]ACw; 4 bits in low bits of second word. Indicate
+ upper/lower in 1 bit shifted 6 bits in second word. Use with
+ `R' or `u' format.
+ h shift indicator (scale factor), 1 bit shifted 10 in second word
+
+ Places to put operand, for general operands:
+ d destination, shifted 6 bits in first word
+ b source, at low bit of first word, and immediate uses one byte
+ w source, at low bit of first word, and immediate uses two bytes
+ l source, at low bit of first word, and immediate uses four bytes
+ s source, at low bit of first word.
+ Used sometimes in contexts where immediate is not allowed anyway.
+ f single precision float, low bit of 1st word, immediate uses 4 bytes
+ F double precision float, low bit of 1st word, immediate uses 8 bytes
+ x extended precision float, low bit of 1st word, immediate uses 12 bytes
+ p packed float, low bit of 1st word, immediate uses 12 bytes
+ G EMAC accumulator, load (bit 4 2nd word, !bit8 first word)
+ H EMAC accumulator, non load (bit 4 2nd word, bit 8 first word)
+ F EMAC ACCx
+ f EMAC ACCy
+ I MAC/EMAC scale factor
+ / Like 's', but set 2nd word, bit 5 if trailing_ampersand set
+ ] first word, bit 10
+*/
+
+extern const struct m68k_opcode m68k_opcodes[];
+extern const struct m68k_opcode_alias m68k_opcode_aliases[];
+
+extern const int m68k_numopcodes, m68k_numaliases;
+
+/* **** End of m68k-opcode.h */
+/* **** m68k-dis.c from sourceware.org CVS 2005-08-14. */
+/* Print Motorola 68k instructions.
+ Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+/* Local function prototypes. */
+
+static const char * const fpcr_names[] =
+{
+ "", "%fpiar", "%fpsr", "%fpiar/%fpsr", "%fpcr",
+ "%fpiar/%fpcr", "%fpsr/%fpcr", "%fpiar/%fpsr/%fpcr"
+};
+
+static const char *const reg_names[] =
+{
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%fp", "%sp",
+ "%ps", "%pc"
+};
+
+/* Name of register halves for MAC/EMAC.
+ Separate from reg_names since 'spu', 'fpl' look weird. */
+static const char *const reg_half_names[] =
+{
+ "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
+ "%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
+ "%ps", "%pc"
+};
+
+/* Sign-extend an (unsigned char). */
+#if __STDC__ == 1
+#define COERCE_SIGNED_CHAR(ch) ((signed char) (ch))
+#else
+#define COERCE_SIGNED_CHAR(ch) ((int) (((ch) ^ 0x80) & 0xFF) - 128)
+#endif
+
+/* Get a 1 byte signed integer. */
+#define NEXTBYTE(p) (p += 2, fetch_data(info, p), COERCE_SIGNED_CHAR(p[-1]))
+
+/* Get a 2 byte signed integer. */
+#define COERCE16(x) ((int) (((x) ^ 0x8000) - 0x8000))
+#define NEXTWORD(p) \
+ (p += 2, fetch_data(info, p), \
+ COERCE16 ((p[-2] << 8) + p[-1]))
+
+/* Get a 4 byte signed integer. */
+#define COERCE32(x) ((bfd_signed_vma) ((x) ^ 0x80000000) - 0x80000000)
+#define NEXTLONG(p) \
+ (p += 4, fetch_data(info, p), \
+ (COERCE32 ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1])))
+
+/* Get a 4 byte unsigned integer. */
+#define NEXTULONG(p) \
+ (p += 4, fetch_data(info, p), \
+ (unsigned int) ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]))
+
+/* Get a single precision float. */
+#define NEXTSINGLE(val, p) \
+ (p += 4, fetch_data(info, p), \
+ floatformat_to_double (&floatformat_ieee_single_big, (char *) p - 4, &val))
+
+/* Get a double precision float. */
+#define NEXTDOUBLE(val, p) \
+ (p += 8, fetch_data(info, p), \
+ floatformat_to_double (&floatformat_ieee_double_big, (char *) p - 8, &val))
+
+/* Get an extended precision float. */
+#define NEXTEXTEND(val, p) \
+ (p += 12, fetch_data(info, p), \
+ floatformat_to_double (&floatformat_m68881_ext, (char *) p - 12, &val))
+
+/* Need a function to convert from packed to double
+ precision. Actually, it's easier to print a
+ packed number than a double anyway, so maybe
+ there should be a special case to handle this... */
+#define NEXTPACKED(p) \
+ (p += 12, fetch_data(info, p), 0.0)
+
+/* Maximum length of an instruction. */
+#define MAXLEN 22
+
+#include <setjmp.h>
+
+struct private
+{
+ /* Points to first byte not fetched. */
+ bfd_byte *max_fetched;
+ bfd_byte the_buffer[MAXLEN];
+ bfd_vma insn_start;
+ jmp_buf bailout;
+};
+
+/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
+ to ADDR (exclusive) are valid. Returns 1 for success, longjmps
+ on error. */
+static int
+fetch_data2(struct disassemble_info *info, bfd_byte *addr)
+{
+ int status;
+ struct private *priv = (struct private *)info->private_data;
+ bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
+
+ status = (*info->read_memory_func) (start,
+ priv->max_fetched,
+ addr - priv->max_fetched,
+ info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, start, info);
+ longjmp (priv->bailout, 1);
+ }
+ else
+ priv->max_fetched = addr;
+ return 1;
+}
+
+static int
+fetch_data(struct disassemble_info *info, bfd_byte *addr)
+{
+ if (addr <= ((struct private *) (info->private_data))->max_fetched) {
+ return 1;
+ } else {
+ return fetch_data2(info, addr);
+ }
+}
+
+/* This function is used to print to the bit-bucket. */
+static int
+dummy_printer (FILE *file ATTRIBUTE_UNUSED,
+ const char *format ATTRIBUTE_UNUSED,
+ ...)
+{
+ return 0;
+}
+
+static void
+dummy_print_address (bfd_vma vma ATTRIBUTE_UNUSED,
+ struct disassemble_info *info ATTRIBUTE_UNUSED)
+{
+}
+
+/* Fetch BITS bits from a position in the instruction specified by CODE.
+ CODE is a "place to put an argument", or 'x' for a destination
+ that is a general address (mode and register).
+ BUFFER contains the instruction. */
+
+static int
+fetch_arg (unsigned char *buffer,
+ int code,
+ int bits,
+ disassemble_info *info)
+{
+ int val = 0;
+
+ switch (code)
+ {
+ case '/': /* MAC/EMAC mask bit. */
+ val = buffer[3] >> 5;
+ break;
+
+ case 'G': /* EMAC ACC load. */
+ val = ((buffer[3] >> 3) & 0x2) | ((~buffer[1] >> 7) & 0x1);
+ break;
+
+ case 'H': /* EMAC ACC !load. */
+ val = ((buffer[3] >> 3) & 0x2) | ((buffer[1] >> 7) & 0x1);
+ break;
+
+ case ']': /* EMAC ACCEXT bit. */
+ val = buffer[0] >> 2;
+ break;
+
+ case 'I': /* MAC/EMAC scale factor. */
+ val = buffer[2] >> 1;
+ break;
+
+ case 'F': /* EMAC ACCx. */
+ val = buffer[0] >> 1;
+ break;
+
+ case 'f':
+ val = buffer[1];
+ break;
+
+ case 's':
+ val = buffer[1];
+ break;
+
+ case 'd': /* Destination, for register or quick. */
+ val = (buffer[0] << 8) + buffer[1];
+ val >>= 9;
+ break;
+
+ case 'x': /* Destination, for general arg. */
+ val = (buffer[0] << 8) + buffer[1];
+ val >>= 6;
+ break;
+
+ case 'k':
+ fetch_data(info, buffer + 3);
+ val = (buffer[3] >> 4);
+ break;
+
+ case 'C':
+ fetch_data(info, buffer + 3);
+ val = buffer[3];
+ break;
+
+ case '1':
+ fetch_data(info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 12;
+ break;
+
+ case '2':
+ fetch_data(info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 6;
+ break;
+
+ case '3':
+ case 'j':
+ fetch_data(info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ break;
+
+ case '4':
+ fetch_data(info, buffer + 5);
+ val = (buffer[4] << 8) + buffer[5];
+ val >>= 12;
+ break;
+
+ case '5':
+ fetch_data(info, buffer + 5);
+ val = (buffer[4] << 8) + buffer[5];
+ val >>= 6;
+ break;
+
+ case '6':
+ fetch_data(info, buffer + 5);
+ val = (buffer[4] << 8) + buffer[5];
+ break;
+
+ case '7':
+ fetch_data(info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 7;
+ break;
+
+ case '8':
+ fetch_data(info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 10;
+ break;
+
+ case '9':
+ fetch_data(info, buffer + 3);
+ val = (buffer[2] << 8) + buffer[3];
+ val >>= 5;
+ break;
+
+ case 'e':
+ val = (buffer[1] >> 6);
+ break;
+
+ case 'm':
+ val = (buffer[1] & 0x40 ? 0x8 : 0)
+ | ((buffer[0] >> 1) & 0x7)
+ | (buffer[3] & 0x80 ? 0x10 : 0);
+ break;
+
+ case 'n':
+ val = (buffer[1] & 0x40 ? 0x8 : 0) | ((buffer[0] >> 1) & 0x7);
+ break;
+
+ case 'o':
+ val = (buffer[2] >> 4) | (buffer[3] & 0x80 ? 0x10 : 0);
+ break;
+
+ case 'M':
+ val = (buffer[1] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0);
+ break;
+
+ case 'N':
+ val = (buffer[3] & 0xf) | (buffer[3] & 0x40 ? 0x10 : 0);
+ break;
+
+ case 'h':
+ val = buffer[2] >> 2;
+ break;
+
+ default:
+ abort ();
+ }
+
+ switch (bits)
+ {
+ case 1:
+ return val & 1;
+ case 2:
+ return val & 3;
+ case 3:
+ return val & 7;
+ case 4:
+ return val & 017;
+ case 5:
+ return val & 037;
+ case 6:
+ return val & 077;
+ case 7:
+ return val & 0177;
+ case 8:
+ return val & 0377;
+ case 12:
+ return val & 07777;
+ default:
+ abort ();
+ }
+}
+
+/* Check if an EA is valid for a particular code. This is required
+ for the EMAC instructions since the type of source address determines
+ if it is a EMAC-load instruciton if the EA is mode 2-5, otherwise it
+ is a non-load EMAC instruction and the bits mean register Ry.
+ A similar case exists for the movem instructions where the register
+ mask is interpreted differently for different EAs. */
+
+static bfd_boolean
+m68k_valid_ea (char code, int val)
+{
+ int mode, mask;
+#define M(n0,n1,n2,n3,n4,n5,n6,n70,n71,n72,n73,n74) \
+ (n0 | n1 << 1 | n2 << 2 | n3 << 3 | n4 << 4 | n5 << 5 | n6 << 6 \
+ | n70 << 7 | n71 << 8 | n72 << 9 | n73 << 10 | n74 << 11)
+
+ switch (code)
+ {
+ case '*':
+ mask = M (1,1,1,1,1,1,1,1,1,1,1,1);
+ break;
+ case '~':
+ mask = M (0,0,1,1,1,1,1,1,1,0,0,0);
+ break;
+ case '%':
+ mask = M (1,1,1,1,1,1,1,1,1,0,0,0);
+ break;
+ case ';':
+ mask = M (1,0,1,1,1,1,1,1,1,1,1,1);
+ break;
+ case '@':
+ mask = M (1,0,1,1,1,1,1,1,1,1,1,0);
+ break;
+ case '!':
+ mask = M (0,0,1,0,0,1,1,1,1,1,1,0);
+ break;
+ case '&':
+ mask = M (0,0,1,0,0,1,1,1,1,0,0,0);
+ break;
+ case '$':
+ mask = M (1,0,1,1,1,1,1,1,1,0,0,0);
+ break;
+ case '?':
+ mask = M (1,0,1,0,0,1,1,1,1,0,0,0);
+ break;
+ case '/':
+ mask = M (1,0,1,0,0,1,1,1,1,1,1,0);
+ break;
+ case '|':
+ mask = M (0,0,1,0,0,1,1,1,1,1,1,0);
+ break;
+ case '>':
+ mask = M (0,0,1,0,1,1,1,1,1,0,0,0);
+ break;
+ case '<':
+ mask = M (0,0,1,1,0,1,1,1,1,1,1,0);
+ break;
+ case 'm':
+ mask = M (1,1,1,1,1,0,0,0,0,0,0,0);
+ break;
+ case 'n':
+ mask = M (0,0,0,0,0,1,0,0,0,1,0,0);
+ break;
+ case 'o':
+ mask = M (0,0,0,0,0,0,1,1,1,0,1,1);
+ break;
+ case 'p':
+ mask = M (1,1,1,1,1,1,0,0,0,0,0,0);
+ break;
+ case 'q':
+ mask = M (1,0,1,1,1,1,0,0,0,0,0,0);
+ break;
+ case 'v':
+ mask = M (1,0,1,1,1,1,0,1,1,0,0,0);
+ break;
+ case 'b':
+ mask = M (1,0,1,1,1,1,0,0,0,1,0,0);
+ break;
+ case 'w':
+ mask = M (0,0,1,1,1,1,0,0,0,1,0,0);
+ break;
+ case 'y':
+ mask = M (0,0,1,0,0,1,0,0,0,0,0,0);
+ break;
+ case 'z':
+ mask = M (0,0,1,0,0,1,0,0,0,1,0,0);
+ break;
+ case '4':
+ mask = M (0,0,1,1,1,1,0,0,0,0,0,0);
+ break;
+ default:
+ abort ();
+ }
+#undef M
+
+ mode = (val >> 3) & 7;
+ if (mode == 7)
+ mode += val & 7;
+ return (mask & (1 << mode)) != 0;
+}
+
+/* Print a base register REGNO and displacement DISP, on INFO->STREAM.
+ REGNO = -1 for pc, -2 for none (suppressed). */
+
+static void
+print_base (int regno, bfd_vma disp, disassemble_info *info)
+{
+ if (regno == -1)
+ {
+ (*info->fprintf_func) (info->stream, "%%pc@(");
+ (*info->print_address_func) (disp, info);
+ }
+ else
+ {
+ char buf[50];
+
+ if (regno == -2)
+ (*info->fprintf_func) (info->stream, "@(");
+ else if (regno == -3)
+ (*info->fprintf_func) (info->stream, "%%zpc@(");
+ else
+ (*info->fprintf_func) (info->stream, "%s@(", reg_names[regno]);
+
+ sprintf_vma (buf, disp);
+ (*info->fprintf_func) (info->stream, "%s", buf);
+ }
+}
+
+/* Print an indexed argument. The base register is BASEREG (-1 for pc).
+ P points to extension word, in buffer.
+ ADDR is the nominal core address of that extension word. */
+
+static unsigned char *
+print_indexed (int basereg,
+ unsigned char *p,
+ bfd_vma addr,
+ disassemble_info *info)
+{
+ int word;
+ static const char *const scales[] = { "", ":2", ":4", ":8" };
+ bfd_vma base_disp;
+ bfd_vma outer_disp;
+ char buf[40];
+ char vmabuf[50];
+
+ word = NEXTWORD (p);
+
+ /* Generate the text for the index register.
+ Where this will be output is not yet determined. */
+ sprintf (buf, "%s:%c%s",
+ reg_names[(word >> 12) & 0xf],
+ (word & 0x800) ? 'l' : 'w',
+ scales[(word >> 9) & 3]);
+
+ /* Handle the 68000 style of indexing. */
+
+ if ((word & 0x100) == 0)
+ {
+ base_disp = word & 0xff;
+ if ((base_disp & 0x80) != 0)
+ base_disp -= 0x100;
+ if (basereg == -1)
+ base_disp += addr;
+ print_base (basereg, base_disp, info);
+ (*info->fprintf_func) (info->stream, ",%s)", buf);
+ return p;
+ }
+
+ /* Handle the generalized kind. */
+ /* First, compute the displacement to add to the base register. */
+ if (word & 0200)
+ {
+ if (basereg == -1)
+ basereg = -3;
+ else
+ basereg = -2;
+ }
+ if (word & 0100)
+ buf[0] = '\0';
+ base_disp = 0;
+ switch ((word >> 4) & 3)
+ {
+ case 2:
+ base_disp = NEXTWORD (p);
+ break;
+ case 3:
+ base_disp = NEXTLONG (p);
+ }
+ if (basereg == -1)
+ base_disp += addr;
+
+ /* Handle single-level case (not indirect). */
+ if ((word & 7) == 0)
+ {
+ print_base (basereg, base_disp, info);
+ if (buf[0] != '\0')
+ (*info->fprintf_func) (info->stream, ",%s", buf);
+ (*info->fprintf_func) (info->stream, ")");
+ return p;
+ }
+
+ /* Two level. Compute displacement to add after indirection. */
+ outer_disp = 0;
+ switch (word & 3)
+ {
+ case 2:
+ outer_disp = NEXTWORD (p);
+ break;
+ case 3:
+ outer_disp = NEXTLONG (p);
+ }
+
+ print_base (basereg, base_disp, info);
+ if ((word & 4) == 0 && buf[0] != '\0')
+ {
+ (*info->fprintf_func) (info->stream, ",%s", buf);
+ buf[0] = '\0';
+ }
+ sprintf_vma (vmabuf, outer_disp);
+ (*info->fprintf_func) (info->stream, ")@(%s", vmabuf);
+ if (buf[0] != '\0')
+ (*info->fprintf_func) (info->stream, ",%s", buf);
+ (*info->fprintf_func) (info->stream, ")");
+
+ return p;
+}
+
+/* Returns number of bytes "eaten" by the operand, or
+ return -1 if an invalid operand was found, or -2 if
+ an opcode tabe error was found.
+ ADDR is the pc for this arg to be relative to. */
+
+static int
+print_insn_arg (const char *d,
+ unsigned char *buffer,
+ unsigned char *p0,
+ bfd_vma addr,
+ disassemble_info *info)
+{
+ int val = 0;
+ int place = d[1];
+ unsigned char *p = p0;
+ int regno;
+ const char *regname;
+ unsigned char *p1;
+ double flval;
+ int flt_p;
+ bfd_signed_vma disp;
+ unsigned int uval;
+
+ switch (*d)
+ {
+ case 'c': /* Cache identifier. */
+ {
+ static const char *const cacheFieldName[] = { "nc", "dc", "ic", "bc" };
+ val = fetch_arg (buffer, place, 2, info);
+ (*info->fprintf_func) (info->stream, "%s", cacheFieldName[val]);
+ break;
+ }
+
+ case 'a': /* Address register indirect only. Cf. case '+'. */
+ {
+ (*info->fprintf_func)
+ (info->stream,
+ "%s@",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8]);
+ break;
+ }
+
+ case '_': /* 32-bit absolute address for move16. */
+ {
+ uval = NEXTULONG (p);
+ (*info->print_address_func) (uval, info);
+ break;
+ }
+
+ case 'C':
+ (*info->fprintf_func) (info->stream, "%%ccr");
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "%%sr");
+ break;
+
+ case 'U':
+ (*info->fprintf_func) (info->stream, "%%usp");
+ break;
+
+ case 'E':
+ (*info->fprintf_func) (info->stream, "%%acc");
+ break;
+
+ case 'G':
+ (*info->fprintf_func) (info->stream, "%%macsr");
+ break;
+
+ case 'H':
+ (*info->fprintf_func) (info->stream, "%%mask");
+ break;
+
+ case 'J':
+ {
+ /* FIXME: There's a problem here, different m68k processors call the
+ same address different names. This table can't get it right
+ because it doesn't know which processor it's disassembling for. */
+ static const struct { const char *name; int value; } names[]
+ = {{"%sfc", 0x000}, {"%dfc", 0x001}, {"%cacr", 0x002},
+ {"%tc", 0x003}, {"%itt0",0x004}, {"%itt1", 0x005},
+ {"%dtt0",0x006}, {"%dtt1",0x007}, {"%buscr",0x008},
+ {"%usp", 0x800}, {"%vbr", 0x801}, {"%caar", 0x802},
+ {"%msp", 0x803}, {"%isp", 0x804},
+ {"%flashbar", 0xc04}, {"%rambar", 0xc05}, /* mcf528x added these. */
+
+ /* Should we be calling this psr like we do in case 'Y'? */
+ {"%mmusr",0x805},
+
+ {"%urp", 0x806}, {"%srp", 0x807}, {"%pcr", 0x808}};
+
+ val = fetch_arg (buffer, place, 12, info);
+ for (regno = sizeof names / sizeof names[0] - 1; regno >= 0; regno--)
+ if (names[regno].value == val)
+ {
+ (*info->fprintf_func) (info->stream, "%s", names[regno].name);
+ break;
+ }
+ if (regno < 0)
+ (*info->fprintf_func) (info->stream, "%d", val);
+ }
+ break;
+
+ case 'Q':
+ val = fetch_arg (buffer, place, 3, info);
+ /* 0 means 8, except for the bkpt instruction... */
+ if (val == 0 && d[1] != 's')
+ val = 8;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'x':
+ val = fetch_arg (buffer, place, 3, info);
+ /* 0 means -1. */
+ if (val == 0)
+ val = -1;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'M':
+ if (place == 'h')
+ {
+ static const char *const scalefactor_name[] = { "<<", ">>" };
+ val = fetch_arg (buffer, place, 1, info);
+ (*info->fprintf_func) (info->stream, "%s", scalefactor_name[val]);
+ }
+ else
+ {
+ val = fetch_arg (buffer, place, 8, info);
+ if (val & 0x80)
+ val = val - 0x100;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ }
+ break;
+
+ case 'T':
+ val = fetch_arg (buffer, place, 4, info);
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'D':
+ (*info->fprintf_func) (info->stream, "%s",
+ reg_names[fetch_arg (buffer, place, 3, info)]);
+ break;
+
+ case 'A':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ reg_names[fetch_arg (buffer, place, 3, info) + 010]);
+ break;
+
+ case 'R':
+ (*info->fprintf_func)
+ (info->stream, "%s",
+ reg_names[fetch_arg (buffer, place, 4, info)]);
+ break;
+
+ case 'r':
+ regno = fetch_arg (buffer, place, 4, info);
+ if (regno > 7)
+ (*info->fprintf_func) (info->stream, "%s@", reg_names[regno]);
+ else
+ (*info->fprintf_func) (info->stream, "@(%s)", reg_names[regno]);
+ break;
+
+ case 'F':
+ (*info->fprintf_func)
+ (info->stream, "%%fp%d",
+ fetch_arg (buffer, place, 3, info));
+ break;
+
+ case 'O':
+ val = fetch_arg (buffer, place, 6, info);
+ if (val & 0x20)
+ (*info->fprintf_func) (info->stream, "%s", reg_names[val & 7]);
+ else
+ (*info->fprintf_func) (info->stream, "%d", val);
+ break;
+
+ case '+':
+ (*info->fprintf_func)
+ (info->stream, "%s@+",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8]);
+ break;
+
+ case '-':
+ (*info->fprintf_func)
+ (info->stream, "%s@-",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8]);
+ break;
+
+ case 'k':
+ if (place == 'k')
+ (*info->fprintf_func)
+ (info->stream, "{%s}",
+ reg_names[fetch_arg (buffer, place, 3, info)]);
+ else if (place == 'C')
+ {
+ val = fetch_arg (buffer, place, 7, info);
+ if (val > 63) /* This is a signed constant. */
+ val -= 128;
+ (*info->fprintf_func) (info->stream, "{#%d}", val);
+ }
+ else
+ return -2;
+ break;
+
+ case '#':
+ case '^':
+ p1 = buffer + (*d == '#' ? 2 : 4);
+ if (place == 's')
+ val = fetch_arg (buffer, place, 4, info);
+ else if (place == 'C')
+ val = fetch_arg (buffer, place, 7, info);
+ else if (place == '8')
+ val = fetch_arg (buffer, place, 3, info);
+ else if (place == '3')
+ val = fetch_arg (buffer, place, 8, info);
+ else if (place == 'b')
+ val = NEXTBYTE (p1);
+ else if (place == 'w' || place == 'W')
+ val = NEXTWORD (p1);
+ else if (place == 'l')
+ val = NEXTLONG (p1);
+ else
+ return -2;
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ case 'B':
+ if (place == 'b')
+ disp = NEXTBYTE (p);
+ else if (place == 'B')
+ disp = COERCE_SIGNED_CHAR (buffer[1]);
+ else if (place == 'w' || place == 'W')
+ disp = NEXTWORD (p);
+ else if (place == 'l' || place == 'L' || place == 'C')
+ disp = NEXTLONG (p);
+ else if (place == 'g')
+ {
+ disp = NEXTBYTE (buffer);
+ if (disp == 0)
+ disp = NEXTWORD (p);
+ else if (disp == -1)
+ disp = NEXTLONG (p);
+ }
+ else if (place == 'c')
+ {
+ if (buffer[1] & 0x40) /* If bit six is one, long offset. */
+ disp = NEXTLONG (p);
+ else
+ disp = NEXTWORD (p);
+ }
+ else
+ return -2;
+
+ (*info->print_address_func) (addr + disp, info);
+ break;
+
+ case 'd':
+ val = NEXTWORD (p);
+ (*info->fprintf_func)
+ (info->stream, "%s@(%d)",
+ reg_names[fetch_arg (buffer, place, 3, info) + 8], val);
+ break;
+
+ case 's':
+ (*info->fprintf_func) (info->stream, "%s",
+ fpcr_names[fetch_arg (buffer, place, 3, info)]);
+ break;
+
+ case 'e':
+ val = fetch_arg(buffer, place, 2, info);
+ (*info->fprintf_func) (info->stream, "%%acc%d", val);
+ break;
+
+ case 'g':
+ val = fetch_arg(buffer, place, 1, info);
+ (*info->fprintf_func) (info->stream, "%%accext%s", val==0 ? "01" : "23");
+ break;
+
+ case 'i':
+ val = fetch_arg(buffer, place, 2, info);
+ if (val == 1)
+ (*info->fprintf_func) (info->stream, "<<");
+ else if (val == 3)
+ (*info->fprintf_func) (info->stream, ">>");
+ else
+ return -1;
+ break;
+
+ case 'I':
+ /* Get coprocessor ID... */
+ val = fetch_arg (buffer, 'd', 3, info);
+
+ if (val != 1) /* Unusual coprocessor ID? */
+ (*info->fprintf_func) (info->stream, "(cpid=%d) ", val);
+ break;
+
+ case '4':
+ case '*':
+ case '~':
+ case '%':
+ case ';':
+ case '@':
+ case '!':
+ case '$':
+ case '?':
+ case '/':
+ case '&':
+ case '|':
+ case '<':
+ case '>':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'v':
+ case 'b':
+ case 'w':
+ case 'y':
+ case 'z':
+ if (place == 'd')
+ {
+ val = fetch_arg (buffer, 'x', 6, info);
+ val = ((val & 7) << 3) + ((val >> 3) & 7);
+ }
+ else
+ val = fetch_arg (buffer, 's', 6, info);
+
+ /* If the <ea> is invalid for *d, then reject this match. */
+ if (!m68k_valid_ea (*d, val))
+ return -1;
+
+ /* Get register number assuming address register. */
+ regno = (val & 7) + 8;
+ regname = reg_names[regno];
+ switch (val >> 3)
+ {
+ case 0:
+ (*info->fprintf_func) (info->stream, "%s", reg_names[val]);
+ break;
+
+ case 1:
+ (*info->fprintf_func) (info->stream, "%s", regname);
+ break;
+
+ case 2:
+ (*info->fprintf_func) (info->stream, "%s@", regname);
+ break;
+
+ case 3:
+ (*info->fprintf_func) (info->stream, "%s@+", regname);
+ break;
+
+ case 4:
+ (*info->fprintf_func) (info->stream, "%s@-", regname);
+ break;
+
+ case 5:
+ val = NEXTWORD (p);
+ (*info->fprintf_func) (info->stream, "%s@(%d)", regname, val);
+ break;
+
+ case 6:
+ p = print_indexed (regno, p, addr, info);
+ break;
+
+ case 7:
+ switch (val & 7)
+ {
+ case 0:
+ val = NEXTWORD (p);
+ (*info->print_address_func) (val, info);
+ break;
+
+ case 1:
+ uval = NEXTULONG (p);
+ (*info->print_address_func) (uval, info);
+ break;
+
+ case 2:
+ val = NEXTWORD (p);
+ (*info->fprintf_func) (info->stream, "%%pc@(");
+ (*info->print_address_func) (addr + val, info);
+ (*info->fprintf_func) (info->stream, ")");
+ break;
+
+ case 3:
+ p = print_indexed (-1, p, addr, info);
+ break;
+
+ case 4:
+ flt_p = 1; /* Assume it's a float... */
+ switch (place)
+ {
+ case 'b':
+ val = NEXTBYTE (p);
+ flt_p = 0;
+ break;
+
+ case 'w':
+ val = NEXTWORD (p);
+ flt_p = 0;
+ break;
+
+ case 'l':
+ val = NEXTLONG (p);
+ flt_p = 0;
+ break;
+
+ case 'f':
+ NEXTSINGLE (flval, p);
+ break;
+
+ case 'F':
+ NEXTDOUBLE (flval, p);
+ break;
+
+ case 'x':
+ NEXTEXTEND (flval, p);
+ break;
+
+ case 'p':
+ flval = NEXTPACKED (p);
+ break;
+
+ default:
+ return -1;
+ }
+ if (flt_p) /* Print a float? */
+ (*info->fprintf_func) (info->stream, "#%g", flval);
+ else
+ (*info->fprintf_func) (info->stream, "#%d", val);
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ /* If place is '/', then this is the case of the mask bit for
+ mac/emac loads. Now that the arg has been printed, grab the
+ mask bit and if set, add a '&' to the arg. */
+ if (place == '/')
+ {
+ val = fetch_arg (buffer, place, 1, info);
+ if (val)
+ info->fprintf_func (info->stream, "&");
+ }
+ break;
+
+ case 'L':
+ case 'l':
+ if (place == 'w')
+ {
+ char doneany;
+ p1 = buffer + 2;
+ val = NEXTWORD (p1);
+ /* Move the pointer ahead if this point is farther ahead
+ than the last. */
+ p = p1 > p ? p1 : p;
+ if (val == 0)
+ {
+ (*info->fprintf_func) (info->stream, "#0");
+ break;
+ }
+ if (*d == 'l')
+ {
+ int newval = 0;
+
+ for (regno = 0; regno < 16; ++regno)
+ if (val & (0x8000 >> regno))
+ newval |= 1 << regno;
+ val = newval;
+ }
+ val &= 0xffff;
+ doneany = 0;
+ for (regno = 0; regno < 16; ++regno)
+ if (val & (1 << regno))
+ {
+ int first_regno;
+
+ if (doneany)
+ (*info->fprintf_func) (info->stream, "/");
+ doneany = 1;
+ (*info->fprintf_func) (info->stream, "%s", reg_names[regno]);
+ first_regno = regno;
+ while (val & (1 << (regno + 1)))
+ ++regno;
+ if (regno > first_regno)
+ (*info->fprintf_func) (info->stream, "-%s",
+ reg_names[regno]);
+ }
+ }
+ else if (place == '3')
+ {
+ /* `fmovem' insn. */
+ char doneany;
+ val = fetch_arg (buffer, place, 8, info);
+ if (val == 0)
+ {
+ (*info->fprintf_func) (info->stream, "#0");
+ break;
+ }
+ if (*d == 'l')
+ {
+ int newval = 0;
+
+ for (regno = 0; regno < 8; ++regno)
+ if (val & (0x80 >> regno))
+ newval |= 1 << regno;
+ val = newval;
+ }
+ val &= 0xff;
+ doneany = 0;
+ for (regno = 0; regno < 8; ++regno)
+ if (val & (1 << regno))
+ {
+ int first_regno;
+ if (doneany)
+ (*info->fprintf_func) (info->stream, "/");
+ doneany = 1;
+ (*info->fprintf_func) (info->stream, "%%fp%d", regno);
+ first_regno = regno;
+ while (val & (1 << (regno + 1)))
+ ++regno;
+ if (regno > first_regno)
+ (*info->fprintf_func) (info->stream, "-%%fp%d", regno);
+ }
+ }
+ else if (place == '8')
+ {
+ /* fmoveml for FP status registers. */
+ (*info->fprintf_func) (info->stream, "%s",
+ fpcr_names[fetch_arg (buffer, place, 3,
+ info)]);
+ }
+ else
+ return -2;
+ break;
+
+ case 'X':
+ place = '8';
+ case 'Y':
+ case 'Z':
+ case 'W':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ {
+ int val = fetch_arg (buffer, place, 5, info);
+ const char *name = 0;
+
+ switch (val)
+ {
+ case 2: name = "%tt0"; break;
+ case 3: name = "%tt1"; break;
+ case 0x10: name = "%tc"; break;
+ case 0x11: name = "%drp"; break;
+ case 0x12: name = "%srp"; break;
+ case 0x13: name = "%crp"; break;
+ case 0x14: name = "%cal"; break;
+ case 0x15: name = "%val"; break;
+ case 0x16: name = "%scc"; break;
+ case 0x17: name = "%ac"; break;
+ case 0x18: name = "%psr"; break;
+ case 0x19: name = "%pcsr"; break;
+ case 0x1c:
+ case 0x1d:
+ {
+ int break_reg = ((buffer[3] >> 2) & 7);
+
+ (*info->fprintf_func)
+ (info->stream, val == 0x1c ? "%%bad%d" : "%%bac%d",
+ break_reg);
+ }
+ break;
+ default:
+ (*info->fprintf_func) (info->stream, "<mmu register %d>", val);
+ }
+ if (name)
+ (*info->fprintf_func) (info->stream, "%s", name);
+ }
+ break;
+
+ case 'f':
+ {
+ int fc = fetch_arg (buffer, place, 5, info);
+
+ if (fc == 1)
+ (*info->fprintf_func) (info->stream, "%%dfc");
+ else if (fc == 0)
+ (*info->fprintf_func) (info->stream, "%%sfc");
+ else
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream, _("<function code %d>"), fc);
+ }
+ break;
+
+ case 'V':
+ (*info->fprintf_func) (info->stream, "%%val");
+ break;
+
+ case 't':
+ {
+ int level = fetch_arg (buffer, place, 3, info);
+
+ (*info->fprintf_func) (info->stream, "%d", level);
+ }
+ break;
+
+ case 'u':
+ {
+ short is_upper = 0;
+ int reg = fetch_arg (buffer, place, 5, info);
+
+ if (reg & 0x10)
+ {
+ is_upper = 1;
+ reg &= 0xf;
+ }
+ (*info->fprintf_func) (info->stream, "%s%s",
+ reg_half_names[reg],
+ is_upper ? "u" : "l");
+ }
+ break;
+
+ default:
+ return -2;
+ }
+
+ return p - p0;
+}
+
+/* Try to match the current instruction to best and if so, return the
+ number of bytes consumed from the instruction stream, else zero. */
+
+static int
+match_insn_m68k (bfd_vma memaddr,
+ disassemble_info * info,
+ const struct m68k_opcode * best,
+ struct private * priv)
+{
+ unsigned char *save_p;
+ unsigned char *p;
+ const char *d;
+
+ bfd_byte *buffer = priv->the_buffer;
+ fprintf_function save_printer = info->fprintf_func;
+ void (* save_print_address) (bfd_vma, struct disassemble_info *)
+ = info->print_address_func;
+
+ /* Point at first word of argument data,
+ and at descriptor for first argument. */
+ p = buffer + 2;
+
+ /* Figure out how long the fixed-size portion of the instruction is.
+ The only place this is stored in the opcode table is
+ in the arguments--look for arguments which specify fields in the 2nd
+ or 3rd words of the instruction. */
+ for (d = best->args; *d; d += 2)
+ {
+ /* I don't think it is necessary to be checking d[0] here;
+ I suspect all this could be moved to the case statement below. */
+ if (d[0] == '#')
+ {
+ if (d[1] == 'l' && p - buffer < 6)
+ p = buffer + 6;
+ else if (p - buffer < 4 && d[1] != 'C' && d[1] != '8')
+ p = buffer + 4;
+ }
+
+ if ((d[0] == 'L' || d[0] == 'l') && d[1] == 'w' && p - buffer < 4)
+ p = buffer + 4;
+
+ switch (d[1])
+ {
+ case '1':
+ case '2':
+ case '3':
+ case '7':
+ case '8':
+ case '9':
+ case 'i':
+ if (p - buffer < 4)
+ p = buffer + 4;
+ break;
+ case '4':
+ case '5':
+ case '6':
+ if (p - buffer < 6)
+ p = buffer + 6;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* pflusha is an exceptions. It takes no arguments but is two words
+ long. Recognize it by looking at the lower 16 bits of the mask. */
+ if (p - buffer < 4 && (best->match & 0xFFFF) != 0)
+ p = buffer + 4;
+
+ /* lpstop is another exception. It takes a one word argument but is
+ three words long. */
+ if (p - buffer < 6
+ && (best->match & 0xffff) == 0xffff
+ && best->args[0] == '#'
+ && best->args[1] == 'w')
+ {
+ /* Copy the one word argument into the usual location for a one
+ word argument, to simplify printing it. We can get away with
+ this because we know exactly what the second word is, and we
+ aren't going to print anything based on it. */
+ p = buffer + 6;
+ fetch_data(info, p);
+ buffer[2] = buffer[4];
+ buffer[3] = buffer[5];
+ }
+
+ fetch_data(info, p);
+
+ d = best->args;
+
+ save_p = p;
+ info->print_address_func = dummy_print_address;
+ info->fprintf_func = dummy_printer;
+
+ /* We scan the operands twice. The first time we don't print anything,
+ but look for errors. */
+ for (; *d; d += 2)
+ {
+ int eaten = print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
+
+ if (eaten >= 0)
+ p += eaten;
+ else if (eaten == -1)
+ {
+ info->fprintf_func = save_printer;
+ info->print_address_func = save_print_address;
+ return 0;
+ }
+ else
+ {
+ info->fprintf_func (info->stream,
+ /* xgettext:c-format */
+ _("<internal error in opcode table: %s %s>\n"),
+ best->name, best->args);
+ info->fprintf_func = save_printer;
+ info->print_address_func = save_print_address;
+ return 2;
+ }
+ }
+
+ p = save_p;
+ info->fprintf_func = save_printer;
+ info->print_address_func = save_print_address;
+
+ d = best->args;
+
+ info->fprintf_func (info->stream, "%s", best->name);
+
+ if (*d)
+ info->fprintf_func (info->stream, " ");
+
+ while (*d)
+ {
+ p += print_insn_arg (d, buffer, p, memaddr + (p - buffer), info);
+ d += 2;
+
+ if (*d && *(d - 2) != 'I' && *d != 'k')
+ info->fprintf_func (info->stream, ",");
+ }
+
+ return p - buffer;
+}
+
+/* Print the m68k instruction at address MEMADDR in debugged memory,
+ on INFO->STREAM. Returns length of the instruction, in bytes. */
+
+int
+print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
+{
+ int i;
+ const char *d;
+ unsigned int arch_mask;
+ struct private priv;
+ bfd_byte *buffer = priv.the_buffer;
+ int major_opcode;
+ static int numopcodes[16];
+ static const struct m68k_opcode **opcodes[16];
+ int val;
+
+ if (!opcodes[0])
+ {
+ /* Speed up the matching by sorting the opcode
+ table on the upper four bits of the opcode. */
+ const struct m68k_opcode **opc_pointer[16];
+
+ /* First count how many opcodes are in each of the sixteen buckets. */
+ for (i = 0; i < m68k_numopcodes; i++)
+ numopcodes[(m68k_opcodes[i].opcode >> 28) & 15]++;
+
+ /* Then create a sorted table of pointers
+ that point into the unsorted table. */
+ opc_pointer[0] = malloc (sizeof (struct m68k_opcode *)
+ * m68k_numopcodes);
+ opcodes[0] = opc_pointer[0];
+
+ for (i = 1; i < 16; i++)
+ {
+ opc_pointer[i] = opc_pointer[i - 1] + numopcodes[i - 1];
+ opcodes[i] = opc_pointer[i];
+ }
+
+ for (i = 0; i < m68k_numopcodes; i++)
+ *opc_pointer[(m68k_opcodes[i].opcode >> 28) & 15]++ = &m68k_opcodes[i];
+ }
+
+ info->private_data = (PTR) &priv;
+ /* Tell objdump to use two bytes per chunk
+ and six bytes per line for displaying raw data. */
+ info->bytes_per_chunk = 2;
+ info->bytes_per_line = 6;
+ info->display_endian = BFD_ENDIAN_BIG;
+ priv.max_fetched = priv.the_buffer;
+ priv.insn_start = memaddr;
+
+ if (setjmp (priv.bailout) != 0)
+ /* Error return. */
+ return -1;
+
+ switch (info->mach)
+ {
+ default:
+ case 0:
+ arch_mask = (unsigned int) -1;
+ break;
+ case bfd_mach_m68000:
+ arch_mask = m68000|m68881|m68851;
+ break;
+ case bfd_mach_m68008:
+ arch_mask = m68008|m68881|m68851;
+ break;
+ case bfd_mach_m68010:
+ arch_mask = m68010|m68881|m68851;
+ break;
+ case bfd_mach_m68020:
+ arch_mask = m68020|m68881|m68851;
+ break;
+ case bfd_mach_m68030:
+ arch_mask = m68030|m68881|m68851;
+ break;
+ case bfd_mach_m68040:
+ arch_mask = m68040|m68881|m68851;
+ break;
+ case bfd_mach_m68060:
+ arch_mask = m68060|m68881|m68851;
+ break;
+ case bfd_mach_mcf5200:
+ arch_mask = mcfisa_a;
+ break;
+ case bfd_mach_mcf521x:
+ case bfd_mach_mcf528x:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfisa_aa|mcfusp|mcfemac;
+ break;
+ case bfd_mach_mcf5206e:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfmac;
+ break;
+ case bfd_mach_mcf5249:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfemac;
+ break;
+ case bfd_mach_mcf5307:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfmac;
+ break;
+ case bfd_mach_mcf5407:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfmac;
+ break;
+ case bfd_mach_mcf547x:
+ case bfd_mach_mcf548x:
+ case bfd_mach_mcfv4e:
+ arch_mask = mcfisa_a|mcfhwdiv|mcfisa_b|mcfusp|cfloat|mcfemac;
+ break;
+ }
+
+ fetch_data(info, buffer + 2);
+ major_opcode = (buffer[0] >> 4) & 15;
+
+ for (i = 0; i < numopcodes[major_opcode]; i++)
+ {
+ const struct m68k_opcode *opc = opcodes[major_opcode][i];
+ unsigned long opcode = opc->opcode;
+ unsigned long match = opc->match;
+
+ if (((0xff & buffer[0] & (match >> 24)) == (0xff & (opcode >> 24)))
+ && ((0xff & buffer[1] & (match >> 16)) == (0xff & (opcode >> 16)))
+ /* Only fetch the next two bytes if we need to. */
+ && (((0xffff & match) == 0)
+ ||
+ (fetch_data(info, buffer + 4)
+ && ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8)))
+ && ((0xff & buffer[3] & match) == (0xff & opcode)))
+ )
+ && (opc->arch & arch_mask) != 0)
+ {
+ /* Don't use for printout the variants of divul and divsl
+ that have the same register number in two places.
+ The more general variants will match instead. */
+ for (d = opc->args; *d; d += 2)
+ if (d[1] == 'D')
+ break;
+
+ /* Don't use for printout the variants of most floating
+ point coprocessor instructions which use the same
+ register number in two places, as above. */
+ if (*d == '\0')
+ for (d = opc->args; *d; d += 2)
+ if (d[1] == 't')
+ break;
+
+ /* Don't match fmovel with more than one register;
+ wait for fmoveml. */
+ if (*d == '\0')
+ {
+ for (d = opc->args; *d; d += 2)
+ {
+ if (d[0] == 's' && d[1] == '8')
+ {
+ val = fetch_arg (buffer, d[1], 3, info);
+ if ((val & (val - 1)) != 0)
+ break;
+ }
+ }
+ }
+
+ if (*d == '\0')
+ if ((val = match_insn_m68k (memaddr, info, opc, & priv)))
+ return val;
+ }
+ }
+
+ /* Handle undefined instructions. */
+ info->fprintf_func (info->stream, "0%o", (buffer[0] << 8) + buffer[1]);
+ return 2;
+}
+/* **** End of m68k-dis.c */
+/* **** m68k-opc.h from sourceware.org CVS 2005-08-14. */
+/* Opcode table for m680[012346]0/m6888[12]/m68851/mcf5200.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2003, 2004, 2005
+ Free Software Foundation, Inc.
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 1, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+#define one(x) ((unsigned int) (x) << 16)
+#define two(x, y) (((unsigned int) (x) << 16) + (y))
+
+/* The assembler requires that all instances of the same mnemonic must
+ be consecutive. If they aren't, the assembler will bomb at
+ runtime. */
+
+const struct m68k_opcode m68k_opcodes[] =
+{
+{"abcd", 2, one(0140400), one(0170770), "DsDd", m68000up },
+{"abcd", 2, one(0140410), one(0170770), "-s-d", m68000up },
+
+{"addaw", 2, one(0150300), one(0170700), "*wAd", m68000up },
+{"addal", 2, one(0150700), one(0170700), "*lAd", m68000up | mcfisa_a },
+
+{"addib", 4, one(0003000), one(0177700), "#b$s", m68000up },
+{"addiw", 4, one(0003100), one(0177700), "#w$s", m68000up },
+{"addil", 6, one(0003200), one(0177700), "#l$s", m68000up },
+{"addil", 6, one(0003200), one(0177700), "#lDs", mcfisa_a },
+
+{"addqb", 2, one(0050000), one(0170700), "Qd$b", m68000up },
+{"addqw", 2, one(0050100), one(0170700), "Qd%w", m68000up },
+{"addql", 2, one(0050200), one(0170700), "Qd%l", m68000up | mcfisa_a },
+
+/* The add opcode can generate the adda, addi, and addq instructions. */
+{"addb", 2, one(0050000), one(0170700), "Qd$b", m68000up },
+{"addb", 4, one(0003000), one(0177700), "#b$s", m68000up },
+{"addb", 2, one(0150000), one(0170700), ";bDd", m68000up },
+{"addb", 2, one(0150400), one(0170700), "Dd~b", m68000up },
+{"addw", 2, one(0050100), one(0170700), "Qd%w", m68000up },
+{"addw", 2, one(0150300), one(0170700), "*wAd", m68000up },
+{"addw", 4, one(0003100), one(0177700), "#w$s", m68000up },
+{"addw", 2, one(0150100), one(0170700), "*wDd", m68000up },
+{"addw", 2, one(0150500), one(0170700), "Dd~w", m68000up },
+{"addl", 2, one(0050200), one(0170700), "Qd%l", m68000up | mcfisa_a },
+{"addl", 6, one(0003200), one(0177700), "#l$s", m68000up },
+{"addl", 6, one(0003200), one(0177700), "#lDs", mcfisa_a },
+{"addl", 2, one(0150700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"addl", 2, one(0150200), one(0170700), "*lDd", m68000up | mcfisa_a },
+{"addl", 2, one(0150600), one(0170700), "Dd~l", m68000up | mcfisa_a },
+
+{"addxb", 2, one(0150400), one(0170770), "DsDd", m68000up },
+{"addxb", 2, one(0150410), one(0170770), "-s-d", m68000up },
+{"addxw", 2, one(0150500), one(0170770), "DsDd", m68000up },
+{"addxw", 2, one(0150510), one(0170770), "-s-d", m68000up },
+{"addxl", 2, one(0150600), one(0170770), "DsDd", m68000up | mcfisa_a },
+{"addxl", 2, one(0150610), one(0170770), "-s-d", m68000up },
+
+{"andib", 4, one(0001000), one(0177700), "#b$s", m68000up },
+{"andib", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"andiw", 4, one(0001100), one(0177700), "#w$s", m68000up },
+{"andiw", 4, one(0001174), one(0177777), "#wSs", m68000up },
+{"andil", 6, one(0001200), one(0177700), "#l$s", m68000up },
+{"andil", 6, one(0001200), one(0177700), "#lDs", mcfisa_a },
+{"andi", 4, one(0001100), one(0177700), "#w$s", m68000up },
+{"andi", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"andi", 4, one(0001174), one(0177777), "#wSs", m68000up },
+
+/* The and opcode can generate the andi instruction. */
+{"andb", 4, one(0001000), one(0177700), "#b$s", m68000up },
+{"andb", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"andb", 2, one(0140000), one(0170700), ";bDd", m68000up },
+{"andb", 2, one(0140400), one(0170700), "Dd~b", m68000up },
+{"andw", 4, one(0001100), one(0177700), "#w$s", m68000up },
+{"andw", 4, one(0001174), one(0177777), "#wSs", m68000up },
+{"andw", 2, one(0140100), one(0170700), ";wDd", m68000up },
+{"andw", 2, one(0140500), one(0170700), "Dd~w", m68000up },
+{"andl", 6, one(0001200), one(0177700), "#l$s", m68000up },
+{"andl", 6, one(0001200), one(0177700), "#lDs", mcfisa_a },
+{"andl", 2, one(0140200), one(0170700), ";lDd", m68000up | mcfisa_a },
+{"andl", 2, one(0140600), one(0170700), "Dd~l", m68000up | mcfisa_a },
+{"and", 4, one(0001100), one(0177700), "#w$w", m68000up },
+{"and", 4, one(0001074), one(0177777), "#bCs", m68000up },
+{"and", 4, one(0001174), one(0177777), "#wSs", m68000up },
+{"and", 2, one(0140100), one(0170700), ";wDd", m68000up },
+{"and", 2, one(0140500), one(0170700), "Dd~w", m68000up },
+
+{"aslb", 2, one(0160400), one(0170770), "QdDs", m68000up },
+{"aslb", 2, one(0160440), one(0170770), "DdDs", m68000up },
+{"aslw", 2, one(0160500), one(0170770), "QdDs", m68000up },
+{"aslw", 2, one(0160540), one(0170770), "DdDs", m68000up },
+{"aslw", 2, one(0160700), one(0177700), "~s", m68000up },
+{"asll", 2, one(0160600), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"asll", 2, one(0160640), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"asrb", 2, one(0160000), one(0170770), "QdDs", m68000up },
+{"asrb", 2, one(0160040), one(0170770), "DdDs", m68000up },
+{"asrw", 2, one(0160100), one(0170770), "QdDs", m68000up },
+{"asrw", 2, one(0160140), one(0170770), "DdDs", m68000up },
+{"asrw", 2, one(0160300), one(0177700), "~s", m68000up },
+{"asrl", 2, one(0160200), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"asrl", 2, one(0160240), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"bhiw", 2, one(0061000), one(0177777), "BW", m68000up | mcfisa_a },
+{"blsw", 2, one(0061400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bccw", 2, one(0062000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bcsw", 2, one(0062400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bnew", 2, one(0063000), one(0177777), "BW", m68000up | mcfisa_a },
+{"beqw", 2, one(0063400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bvcw", 2, one(0064000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bvsw", 2, one(0064400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bplw", 2, one(0065000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bmiw", 2, one(0065400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bgew", 2, one(0066000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bltw", 2, one(0066400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bgtw", 2, one(0067000), one(0177777), "BW", m68000up | mcfisa_a },
+{"blew", 2, one(0067400), one(0177777), "BW", m68000up | mcfisa_a },
+
+{"bhil", 2, one(0061377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"blsl", 2, one(0061777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bccl", 2, one(0062377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bcsl", 2, one(0062777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bnel", 2, one(0063377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"beql", 2, one(0063777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bvcl", 2, one(0064377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bvsl", 2, one(0064777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bpll", 2, one(0065377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bmil", 2, one(0065777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bgel", 2, one(0066377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bltl", 2, one(0066777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bgtl", 2, one(0067377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"blel", 2, one(0067777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+
+{"bhis", 2, one(0061000), one(0177400), "BB", m68000up | mcfisa_a },
+{"blss", 2, one(0061400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bccs", 2, one(0062000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bcss", 2, one(0062400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bnes", 2, one(0063000), one(0177400), "BB", m68000up | mcfisa_a },
+{"beqs", 2, one(0063400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bvcs", 2, one(0064000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bvss", 2, one(0064400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bpls", 2, one(0065000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bmis", 2, one(0065400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bges", 2, one(0066000), one(0177400), "BB", m68000up | mcfisa_a },
+{"blts", 2, one(0066400), one(0177400), "BB", m68000up | mcfisa_a },
+{"bgts", 2, one(0067000), one(0177400), "BB", m68000up | mcfisa_a },
+{"bles", 2, one(0067400), one(0177400), "BB", m68000up | mcfisa_a },
+
+{"jhi", 2, one(0061000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jls", 2, one(0061400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jcc", 2, one(0062000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jcs", 2, one(0062400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jne", 2, one(0063000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jeq", 2, one(0063400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jvc", 2, one(0064000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jvs", 2, one(0064400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jpl", 2, one(0065000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jmi", 2, one(0065400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jge", 2, one(0066000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jlt", 2, one(0066400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jgt", 2, one(0067000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jle", 2, one(0067400), one(0177400), "Bg", m68000up | mcfisa_a },
+
+{"bchg", 2, one(0000500), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"bchg", 4, one(0004100), one(0177700), "#b$s", m68000up },
+{"bchg", 4, one(0004100), one(0177700), "#bqs", mcfisa_a },
+
+{"bclr", 2, one(0000600), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"bclr", 4, one(0004200), one(0177700), "#b$s", m68000up },
+{"bclr", 4, one(0004200), one(0177700), "#bqs", mcfisa_a },
+
+{"bfchg", 4, two(0165300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bfclr", 4, two(0166300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bfexts", 4, two(0165700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfextu", 4, two(0164700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfffo", 4, two(0166700, 0), two(0177700, 0100000), "/sO2O3D1", m68020up },
+{"bfins", 4, two(0167700, 0), two(0177700, 0100000), "D1?sO2O3", m68020up },
+{"bfset", 4, two(0167300, 0), two(0177700, 0170000), "?sO2O3", m68020up },
+{"bftst", 4, two(0164300, 0), two(0177700, 0170000), "/sO2O3", m68020up },
+
+{"bgnd", 2, one(0045372), one(0177777), "", cpu32 },
+
+{"bitrev", 2, one(0000300), one(0177770), "Ds", mcfisa_aa},
+
+{"bkpt", 2, one(0044110), one(0177770), "ts", m68010up },
+
+{"braw", 2, one(0060000), one(0177777), "BW", m68000up | mcfisa_a },
+{"bral", 2, one(0060377), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bras", 2, one(0060000), one(0177400), "BB", m68000up | mcfisa_a },
+
+{"bset", 2, one(0000700), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"bset", 2, one(0000700), one(0170700), "Ddvs", mcfisa_a },
+{"bset", 4, one(0004300), one(0177700), "#b$s", m68000up },
+{"bset", 4, one(0004300), one(0177700), "#bqs", mcfisa_a },
+
+{"bsrw", 2, one(0060400), one(0177777), "BW", m68000up | mcfisa_a },
+{"bsrl", 2, one(0060777), one(0177777), "BL", m68020up | cpu32 | mcfisa_b},
+{"bsrs", 2, one(0060400), one(0177400), "BB", m68000up | mcfisa_a },
+
+{"btst", 2, one(0000400), one(0170700), "Dd;b", m68000up | mcfisa_a },
+{"btst", 4, one(0004000), one(0177700), "#b@s", m68000up },
+{"btst", 4, one(0004000), one(0177700), "#bqs", mcfisa_a },
+
+{"byterev", 2, one(0001300), one(0177770), "Ds", mcfisa_aa},
+
+{"callm", 4, one(0003300), one(0177700), "#b!s", m68020 },
+
+{"cas2w", 6, two(0006374,0), two(0177777,0007070), "D3D6D2D5r1r4", m68020up },
+{"cas2w", 6, two(0006374,0), two(0177777,0007070), "D3D6D2D5R1R4", m68020up },
+{"cas2l", 6, two(0007374,0), two(0177777,0007070), "D3D6D2D5r1r4", m68020up },
+{"cas2l", 6, two(0007374,0), two(0177777,0007070), "D3D6D2D5R1R4", m68020up },
+
+{"casb", 4, two(0005300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+{"casw", 4, two(0006300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+{"casl", 4, two(0007300, 0), two(0177700, 0177070), "D3D2~s", m68020up },
+
+{"chk2b", 4, two(0000300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"chk2w", 4, two(0001300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"chk2l", 4, two(0002300,0004000), two(0177700,07777), "!sR1", m68020up | cpu32 },
+
+{"chkl", 2, one(0040400), one(0170700), ";lDd", m68000up },
+{"chkw", 2, one(0040600), one(0170700), ";wDd", m68000up },
+
+#define SCOPE_LINE (0x1 << 3)
+#define SCOPE_PAGE (0x2 << 3)
+#define SCOPE_ALL (0x3 << 3)
+
+{"cinva", 2, one(0xf400|SCOPE_ALL), one(0xff38), "ce", m68040up },
+{"cinvl", 2, one(0xf400|SCOPE_LINE), one(0xff38), "ceas", m68040up },
+{"cinvp", 2, one(0xf400|SCOPE_PAGE), one(0xff38), "ceas", m68040up },
+
+{"cpusha", 2, one(0xf420|SCOPE_ALL), one(0xff38), "ce", m68040up },
+{"cpushl", 2, one(0xf420|SCOPE_LINE), one(0xff38), "ceas", m68040up | mcfisa_a },
+{"cpushp", 2, one(0xf420|SCOPE_PAGE), one(0xff38), "ceas", m68040up },
+
+#undef SCOPE_LINE
+#undef SCOPE_PAGE
+#undef SCOPE_ALL
+
+{"clrb", 2, one(0041000), one(0177700), "$s", m68000up | mcfisa_a },
+{"clrw", 2, one(0041100), one(0177700), "$s", m68000up | mcfisa_a },
+{"clrl", 2, one(0041200), one(0177700), "$s", m68000up | mcfisa_a },
+
+{"cmp2b", 4, two(0000300,0), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"cmp2w", 4, two(0001300,0), two(0177700,07777), "!sR1", m68020up | cpu32 },
+{"cmp2l", 4, two(0002300,0), two(0177700,07777), "!sR1", m68020up | cpu32 },
+
+{"cmpaw", 2, one(0130300), one(0170700), "*wAd", m68000up },
+{"cmpal", 2, one(0130700), one(0170700), "*lAd", m68000up | mcfisa_a },
+
+{"cmpib", 4, one(0006000), one(0177700), "#b@s", m68000up },
+{"cmpib", 4, one(0006000), one(0177700), "#bDs", mcfisa_b },
+{"cmpiw", 4, one(0006100), one(0177700), "#w@s", m68000up },
+{"cmpiw", 4, one(0006100), one(0177700), "#wDs", mcfisa_b },
+{"cmpil", 6, one(0006200), one(0177700), "#l@s", m68000up },
+{"cmpil", 6, one(0006200), one(0177700), "#lDs", mcfisa_a },
+
+{"cmpmb", 2, one(0130410), one(0170770), "+s+d", m68000up },
+{"cmpmw", 2, one(0130510), one(0170770), "+s+d", m68000up },
+{"cmpml", 2, one(0130610), one(0170770), "+s+d", m68000up },
+
+/* The cmp opcode can generate the cmpa, cmpm, and cmpi instructions. */
+{"cmpb", 4, one(0006000), one(0177700), "#b@s", m68000up },
+{"cmpb", 4, one(0006000), one(0177700), "#bDs", mcfisa_b },
+{"cmpb", 2, one(0130410), one(0170770), "+s+d", m68000up },
+{"cmpb", 2, one(0130000), one(0170700), ";bDd", m68000up },
+{"cmpb", 2, one(0130000), one(0170700), "*bDd", mcfisa_b },
+{"cmpw", 2, one(0130300), one(0170700), "*wAd", m68000up },
+{"cmpw", 4, one(0006100), one(0177700), "#w@s", m68000up },
+{"cmpw", 4, one(0006100), one(0177700), "#wDs", mcfisa_b },
+{"cmpw", 2, one(0130510), one(0170770), "+s+d", m68000up },
+{"cmpw", 2, one(0130100), one(0170700), "*wDd", m68000up | mcfisa_b },
+{"cmpl", 2, one(0130700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"cmpl", 6, one(0006200), one(0177700), "#l@s", m68000up },
+{"cmpl", 6, one(0006200), one(0177700), "#lDs", mcfisa_a },
+{"cmpl", 2, one(0130610), one(0170770), "+s+d", m68000up },
+{"cmpl", 2, one(0130200), one(0170700), "*lDd", m68000up | mcfisa_a },
+
+{"dbcc", 2, one(0052310), one(0177770), "DsBw", m68000up },
+{"dbcs", 2, one(0052710), one(0177770), "DsBw", m68000up },
+{"dbeq", 2, one(0053710), one(0177770), "DsBw", m68000up },
+{"dbf", 2, one(0050710), one(0177770), "DsBw", m68000up },
+{"dbge", 2, one(0056310), one(0177770), "DsBw", m68000up },
+{"dbgt", 2, one(0057310), one(0177770), "DsBw", m68000up },
+{"dbhi", 2, one(0051310), one(0177770), "DsBw", m68000up },
+{"dble", 2, one(0057710), one(0177770), "DsBw", m68000up },
+{"dbls", 2, one(0051710), one(0177770), "DsBw", m68000up },
+{"dblt", 2, one(0056710), one(0177770), "DsBw", m68000up },
+{"dbmi", 2, one(0055710), one(0177770), "DsBw", m68000up },
+{"dbne", 2, one(0053310), one(0177770), "DsBw", m68000up },
+{"dbpl", 2, one(0055310), one(0177770), "DsBw", m68000up },
+{"dbt", 2, one(0050310), one(0177770), "DsBw", m68000up },
+{"dbvc", 2, one(0054310), one(0177770), "DsBw", m68000up },
+{"dbvs", 2, one(0054710), one(0177770), "DsBw", m68000up },
+
+{"divsw", 2, one(0100700), one(0170700), ";wDd", m68000up | mcfhwdiv },
+
+{"divsl", 4, two(0046100,0006000),two(0177700,0107770),";lD3D1", m68020up|cpu32 },
+{"divsl", 4, two(0046100,0004000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+{"divsl", 4, two(0046100,0004000),two(0177700,0107770),"qsDD", mcfhwdiv },
+
+{"divsll", 4, two(0046100,0004000),two(0177700,0107770),";lD3D1",m68020up|cpu32 },
+{"divsll", 4, two(0046100,0004000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+
+{"divuw", 2, one(0100300), one(0170700), ";wDd", m68000up | mcfhwdiv },
+
+{"divul", 4, two(0046100,0002000),two(0177700,0107770),";lD3D1", m68020up|cpu32 },
+{"divul", 4, two(0046100,0000000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+{"divul", 4, two(0046100,0000000),two(0177700,0107770),"qsDD", mcfhwdiv },
+
+{"divull", 4, two(0046100,0000000),two(0177700,0107770),";lD3D1",m68020up|cpu32 },
+{"divull", 4, two(0046100,0000000),two(0177700,0107770),";lDD", m68020up|cpu32 },
+
+{"eorib", 4, one(0005000), one(0177700), "#b$s", m68000up },
+{"eorib", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eoriw", 4, one(0005100), one(0177700), "#w$s", m68000up },
+{"eoriw", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eoril", 6, one(0005200), one(0177700), "#l$s", m68000up },
+{"eoril", 6, one(0005200), one(0177700), "#lDs", mcfisa_a },
+{"eori", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eori", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eori", 4, one(0005100), one(0177700), "#w$s", m68000up },
+
+/* The eor opcode can generate the eori instruction. */
+{"eorb", 4, one(0005000), one(0177700), "#b$s", m68000up },
+{"eorb", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eorb", 2, one(0130400), one(0170700), "Dd$s", m68000up },
+{"eorw", 4, one(0005100), one(0177700), "#w$s", m68000up },
+{"eorw", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eorw", 2, one(0130500), one(0170700), "Dd$s", m68000up },
+{"eorl", 6, one(0005200), one(0177700), "#l$s", m68000up },
+{"eorl", 6, one(0005200), one(0177700), "#lDs", mcfisa_a },
+{"eorl", 2, one(0130600), one(0170700), "Dd$s", m68000up | mcfisa_a },
+{"eor", 4, one(0005074), one(0177777), "#bCs", m68000up },
+{"eor", 4, one(0005174), one(0177777), "#wSs", m68000up },
+{"eor", 4, one(0005100), one(0177700), "#w$s", m68000up },
+{"eor", 2, one(0130500), one(0170700), "Dd$s", m68000up },
+
+{"exg", 2, one(0140500), one(0170770), "DdDs", m68000up },
+{"exg", 2, one(0140510), one(0170770), "AdAs", m68000up },
+{"exg", 2, one(0140610), one(0170770), "DdAs", m68000up },
+{"exg", 2, one(0140610), one(0170770), "AsDd", m68000up },
+
+{"extw", 2, one(0044200), one(0177770), "Ds", m68000up|mcfisa_a },
+{"extl", 2, one(0044300), one(0177770), "Ds", m68000up|mcfisa_a },
+{"extbl", 2, one(0044700), one(0177770), "Ds", m68020up|cpu32|mcfisa_a },
+
+{"ff1", 2, one(0002300), one(0177770), "Ds", mcfisa_aa},
+
+/* float stuff starts here */
+
+{"fabsb", 4, two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fabsb", 4, two(0xF000, 0x5818), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fabsd", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fabsd", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fabsd", 4, two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fabsd", 4, two(0xF000, 0x5418), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fabsl", 4, two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fabsl", 4, two(0xF000, 0x4018), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fabsp", 4, two(0xF000, 0x4C18), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fabss", 4, two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", cfloat },
+{"fabss", 4, two(0xF000, 0x4418), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fabsw", 4, two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fabsw", 4, two(0xF000, 0x5018), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fabsx", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fabsx", 4, two(0xF000, 0x4818), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fabsx", 4, two(0xF000, 0x0018), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsabsb", 4, two(0xF000, 0x5858), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsabsb", 4, two(0xF000, 0x5858), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabsd", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsabsd", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fsabsd", 4, two(0xF000, 0x5458), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsabsd", 4, two(0xF000, 0x5458), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsabsl", 4, two(0xF000, 0x4058), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsabsl", 4, two(0xF000, 0x4058), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabsp", 4, two(0xF000, 0x4C58), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsabss", 4, two(0xF000, 0x4258), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabss", 4, two(0xF000, 0x4458), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsabsw", 4, two(0xF000, 0x5058), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsabsw", 4, two(0xF000, 0x5058), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsabsx", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsabsx", 4, two(0xF000, 0x4858), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fsabsx", 4, two(0xF000, 0x0058), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdabsb", 4, two(0xF000, 0x585C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabsb", 4, two(0xF000, 0x585c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up},
+{"fdabsd", 4, two(0xF000, 0x005C), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdabsd", 4, two(0xF000, 0x005C), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fdabsd", 4, two(0xF000, 0x545C), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdabsd", 4, two(0xF000, 0x545c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up},
+{"fdabsl", 4, two(0xF000, 0x405C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabsl", 4, two(0xF000, 0x405c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up},
+{"fdabsp", 4, two(0xF000, 0x4C5c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up},
+{"fdabss", 4, two(0xF000, 0x425C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabss", 4, two(0xF000, 0x445c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up},
+{"fdabsw", 4, two(0xF000, 0x505C), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdabsw", 4, two(0xF000, 0x505c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up},
+{"fdabsx", 4, two(0xF000, 0x005c), two(0xF1C0, 0xE07F), "IiF8F7", m68040up},
+{"fdabsx", 4, two(0xF000, 0x485c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up},
+{"fdabsx", 4, two(0xF000, 0x005c), two(0xF1C0, 0xE07F), "IiFt", m68040up},
+
+{"facosb", 4, two(0xF000, 0x581C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"facosd", 4, two(0xF000, 0x541C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"facosl", 4, two(0xF000, 0x401C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"facosp", 4, two(0xF000, 0x4C1C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"facoss", 4, two(0xF000, 0x441C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"facosw", 4, two(0xF000, 0x501C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"facosx", 4, two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"facosx", 4, two(0xF000, 0x481C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"facosx", 4, two(0xF000, 0x001C), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"faddb", 4, two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"faddb", 4, two(0xF000, 0x5822), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddd", 4, two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"faddd", 4, two(0xF000, 0x5422), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"faddl", 4, two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"faddl", 4, two(0xF000, 0x4022), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddp", 4, two(0xF000, 0x4C22), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fadds", 4, two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fadds", 4, two(0xF000, 0x4422), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddw", 4, two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"faddw", 4, two(0xF000, 0x5022), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"faddx", 4, two(0xF000, 0x0022), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"faddx", 4, two(0xF000, 0x4822), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fsaddb", 4, two(0xF000, 0x5862), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsaddb", 4, two(0xF000, 0x5862), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddd", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsaddd", 4, two(0xF000, 0x5462), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsaddd", 4, two(0xF000, 0x5462), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsaddl", 4, two(0xF000, 0x4062), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsaddl", 4, two(0xF000, 0x4062), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddp", 4, two(0xF000, 0x4C62), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsadds", 4, two(0xF000, 0x4462), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsadds", 4, two(0xF000, 0x4862), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddw", 4, two(0xF000, 0x5062), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsaddw", 4, two(0xF000, 0x5062), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsaddx", 4, two(0xF000, 0x0062), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsaddx", 4, two(0xF000, 0x4862), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fdaddb", 4, two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddb", 4, two(0xF000, 0x5866), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdaddd", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdaddd", 4, two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddd", 4, two(0xF000, 0x5466), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdaddl", 4, two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdaddl", 4, two(0xF000, 0x4066), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdaddp", 4, two(0xF000, 0x4C66), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdadds", 4, two(0xF000, 0x4466), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdadds", 4, two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddw", 4, two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdaddw", 4, two(0xF000, 0x5066), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdaddx", 4, two(0xF000, 0x0066), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdaddx", 4, two(0xF000, 0x4866), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fasinb", 4, two(0xF000, 0x580C), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fasind", 4, two(0xF000, 0x540C), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fasinl", 4, two(0xF000, 0x400C), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fasinp", 4, two(0xF000, 0x4C0C), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fasins", 4, two(0xF000, 0x440C), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fasinw", 4, two(0xF000, 0x500C), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fasinx", 4, two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fasinx", 4, two(0xF000, 0x480C), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fasinx", 4, two(0xF000, 0x000C), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fatanb", 4, two(0xF000, 0x580A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fatand", 4, two(0xF000, 0x540A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fatanl", 4, two(0xF000, 0x400A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fatanp", 4, two(0xF000, 0x4C0A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fatans", 4, two(0xF000, 0x440A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fatanw", 4, two(0xF000, 0x500A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fatanx", 4, two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fatanx", 4, two(0xF000, 0x480A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fatanx", 4, two(0xF000, 0x000A), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fatanhb", 4, two(0xF000, 0x580D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fatanhd", 4, two(0xF000, 0x540D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fatanhl", 4, two(0xF000, 0x400D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fatanhp", 4, two(0xF000, 0x4C0D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fatanhs", 4, two(0xF000, 0x440D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fatanhw", 4, two(0xF000, 0x500D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fatanhx", 4, two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fatanhx", 4, two(0xF000, 0x480D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fatanhx", 4, two(0xF000, 0x000D), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fbeq", 2, one(0xF081), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbf", 2, one(0xF080), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbge", 2, one(0xF093), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbgl", 2, one(0xF096), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbgle", 2, one(0xF097), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbgt", 2, one(0xF092), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fble", 2, one(0xF095), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fblt", 2, one(0xF094), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbne", 2, one(0xF08E), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbnge", 2, one(0xF09C), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbngl", 2, one(0xF099), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbngle", 2, one(0xF098), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbngt", 2, one(0xF09D), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbnle", 2, one(0xF09A), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbnlt", 2, one(0xF09B), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fboge", 2, one(0xF083), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbogl", 2, one(0xF086), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbogt", 2, one(0xF082), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbole", 2, one(0xF085), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbolt", 2, one(0xF084), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbor", 2, one(0xF087), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbseq", 2, one(0xF091), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbsf", 2, one(0xF090), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbsne", 2, one(0xF09E), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbst", 2, one(0xF09F), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbt", 2, one(0xF08F), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbueq", 2, one(0xF089), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbuge", 2, one(0xF08B), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbugt", 2, one(0xF08A), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbule", 2, one(0xF08D), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbult", 2, one(0xF08C), one(0xF1FF), "IdBW", mfloat | cfloat },
+{"fbun", 2, one(0xF088), one(0xF1FF), "IdBW", mfloat | cfloat },
+
+{"fbeql", 2, one(0xF0C1), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbfl", 2, one(0xF0C0), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbgel", 2, one(0xF0D3), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbgll", 2, one(0xF0D6), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbglel", 2, one(0xF0D7), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbgtl", 2, one(0xF0D2), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fblel", 2, one(0xF0D5), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbltl", 2, one(0xF0D4), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnel", 2, one(0xF0CE), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbngel", 2, one(0xF0DC), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbngll", 2, one(0xF0D9), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnglel", 2, one(0xF0D8), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbngtl", 2, one(0xF0DD), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnlel", 2, one(0xF0DA), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbnltl", 2, one(0xF0DB), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbogel", 2, one(0xF0C3), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbogll", 2, one(0xF0C6), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbogtl", 2, one(0xF0C2), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbolel", 2, one(0xF0C5), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fboltl", 2, one(0xF0C4), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fborl", 2, one(0xF0C7), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbseql", 2, one(0xF0D1), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbsfl", 2, one(0xF0D0), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbsnel", 2, one(0xF0DE), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbstl", 2, one(0xF0DF), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbtl", 2, one(0xF0CF), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbueql", 2, one(0xF0C9), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbugel", 2, one(0xF0CB), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbugtl", 2, one(0xF0CA), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbulel", 2, one(0xF0CD), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbultl", 2, one(0xF0CC), one(0xF1FF), "IdBC", mfloat | cfloat },
+{"fbunl", 2, one(0xF0C8), one(0xF1FF), "IdBC", mfloat | cfloat },
+
+{"fjeq", 2, one(0xF081), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjf", 2, one(0xF080), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjge", 2, one(0xF093), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjgl", 2, one(0xF096), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjgle", 2, one(0xF097), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjgt", 2, one(0xF092), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjle", 2, one(0xF095), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjlt", 2, one(0xF094), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjne", 2, one(0xF08E), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjnge", 2, one(0xF09C), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjngl", 2, one(0xF099), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjngle", 2, one(0xF098), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjngt", 2, one(0xF09D), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjnle", 2, one(0xF09A), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjnlt", 2, one(0xF09B), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjoge", 2, one(0xF083), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjogl", 2, one(0xF086), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjogt", 2, one(0xF082), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjole", 2, one(0xF085), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjolt", 2, one(0xF084), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjor", 2, one(0xF087), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjseq", 2, one(0xF091), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjsf", 2, one(0xF090), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjsne", 2, one(0xF09E), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjst", 2, one(0xF09F), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjt", 2, one(0xF08F), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjueq", 2, one(0xF089), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjuge", 2, one(0xF08B), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjugt", 2, one(0xF08A), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjule", 2, one(0xF08D), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjult", 2, one(0xF08C), one(0xF1BF), "IdBc", mfloat | cfloat },
+{"fjun", 2, one(0xF088), one(0xF1BF), "IdBc", mfloat | cfloat },
+
+{"fcmpb", 4, two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpb", 4, two(0xF000, 0x5838), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcmpd", 4, two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcmpd", 4, two(0xF000, 0x5438), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fcmpd", 4, two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fcmpl", 4, two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcmpl", 4, two(0xF000, 0x4038), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpp", 4, two(0xF000, 0x4C38), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcmps", 4, two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcmps", 4, two(0xF000, 0x4438), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpw", 4, two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcmpw", 4, two(0xF000, 0x5038), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fcmpx", 4, two(0xF000, 0x0038), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcmpx", 4, two(0xF000, 0x4838), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fcosb", 4, two(0xF000, 0x581D), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcosd", 4, two(0xF000, 0x541D), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcosl", 4, two(0xF000, 0x401D), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcosp", 4, two(0xF000, 0x4C1D), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcoss", 4, two(0xF000, 0x441D), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcosw", 4, two(0xF000, 0x501D), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcosx", 4, two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcosx", 4, two(0xF000, 0x481D), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fcosx", 4, two(0xF000, 0x001D), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fcoshb", 4, two(0xF000, 0x5819), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fcoshd", 4, two(0xF000, 0x5419), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fcoshl", 4, two(0xF000, 0x4019), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fcoshp", 4, two(0xF000, 0x4C19), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fcoshs", 4, two(0xF000, 0x4419), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fcoshw", 4, two(0xF000, 0x5019), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fcoshx", 4, two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fcoshx", 4, two(0xF000, 0x4819), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fcoshx", 4, two(0xF000, 0x0019), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fdbeq", 4, two(0xF048, 0x0001), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbf", 4, two(0xF048, 0x0000), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbge", 4, two(0xF048, 0x0013), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgl", 4, two(0xF048, 0x0016), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgle", 4, two(0xF048, 0x0017), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbgt", 4, two(0xF048, 0x0012), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdble", 4, two(0xF048, 0x0015), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdblt", 4, two(0xF048, 0x0014), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbne", 4, two(0xF048, 0x000E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnge", 4, two(0xF048, 0x001C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngl", 4, two(0xF048, 0x0019), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngle", 4, two(0xF048, 0x0018), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbngt", 4, two(0xF048, 0x001D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnle", 4, two(0xF048, 0x001A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbnlt", 4, two(0xF048, 0x001B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdboge", 4, two(0xF048, 0x0003), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbogl", 4, two(0xF048, 0x0006), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbogt", 4, two(0xF048, 0x0002), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbole", 4, two(0xF048, 0x0005), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbolt", 4, two(0xF048, 0x0004), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbor", 4, two(0xF048, 0x0007), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbseq", 4, two(0xF048, 0x0011), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbsf", 4, two(0xF048, 0x0010), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbsne", 4, two(0xF048, 0x001E), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbst", 4, two(0xF048, 0x001F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbt", 4, two(0xF048, 0x000F), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbueq", 4, two(0xF048, 0x0009), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbuge", 4, two(0xF048, 0x000B), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbugt", 4, two(0xF048, 0x000A), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbule", 4, two(0xF048, 0x000D), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbult", 4, two(0xF048, 0x000C), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+{"fdbun", 4, two(0xF048, 0x0008), two(0xF1F8, 0xFFFF), "IiDsBw", mfloat },
+
+{"fdivb", 4, two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fdivb", 4, two(0xF000, 0x5820), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivd", 4, two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdivd", 4, two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fdivd", 4, two(0xF000, 0x5420), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdivl", 4, two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fdivl", 4, two(0xF000, 0x4020), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivp", 4, two(0xF000, 0x4C20), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fdivs", 4, two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fdivs", 4, two(0xF000, 0x4420), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivw", 4, two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fdivw", 4, two(0xF000, 0x5020), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdivx", 4, two(0xF000, 0x0020), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fdivx", 4, two(0xF000, 0x4820), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fsdivb", 4, two(0xF000, 0x5860), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsdivb", 4, two(0xF000, 0x5860), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivd", 4, two(0xF000, 0x0060), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsdivd", 4, two(0xF000, 0x5460), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsdivd", 4, two(0xF000, 0x5460), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsdivl", 4, two(0xF000, 0x4060), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsdivl", 4, two(0xF000, 0x4060), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivp", 4, two(0xF000, 0x4C60), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsdivs", 4, two(0xF000, 0x4460), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsdivs", 4, two(0xF000, 0x4460), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivw", 4, two(0xF000, 0x5060), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsdivw", 4, two(0xF000, 0x5060), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsdivx", 4, two(0xF000, 0x0060), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsdivx", 4, two(0xF000, 0x4860), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fddivb", 4, two(0xF000, 0x5864), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fddivb", 4, two(0xF000, 0x5864), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivd", 4, two(0xF000, 0x0064), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fddivd", 4, two(0xF000, 0x5464), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fddivd", 4, two(0xF000, 0x5464), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fddivl", 4, two(0xF000, 0x4064), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fddivl", 4, two(0xF000, 0x4064), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivp", 4, two(0xF000, 0x4C64), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fddivs", 4, two(0xF000, 0x4464), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fddivs", 4, two(0xF000, 0x4464), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivw", 4, two(0xF000, 0x5064), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fddivw", 4, two(0xF000, 0x5064), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fddivx", 4, two(0xF000, 0x0064), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fddivx", 4, two(0xF000, 0x4864), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fetoxb", 4, two(0xF000, 0x5810), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fetoxd", 4, two(0xF000, 0x5410), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fetoxl", 4, two(0xF000, 0x4010), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fetoxp", 4, two(0xF000, 0x4C10), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fetoxs", 4, two(0xF000, 0x4410), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fetoxw", 4, two(0xF000, 0x5010), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fetoxx", 4, two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fetoxx", 4, two(0xF000, 0x4810), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fetoxx", 4, two(0xF000, 0x0010), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fetoxm1b", 4, two(0xF000, 0x5808), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fetoxm1d", 4, two(0xF000, 0x5408), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fetoxm1l", 4, two(0xF000, 0x4008), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fetoxm1p", 4, two(0xF000, 0x4C08), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fetoxm1s", 4, two(0xF000, 0x4408), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fetoxm1w", 4, two(0xF000, 0x5008), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fetoxm1x", 4, two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fetoxm1x", 4, two(0xF000, 0x4808), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fetoxm1x", 4, two(0xF000, 0x0008), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fgetexpb", 4, two(0xF000, 0x581E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fgetexpd", 4, two(0xF000, 0x541E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fgetexpl", 4, two(0xF000, 0x401E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fgetexpp", 4, two(0xF000, 0x4C1E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fgetexps", 4, two(0xF000, 0x441E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fgetexpw", 4, two(0xF000, 0x501E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fgetexpx", 4, two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fgetexpx", 4, two(0xF000, 0x481E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fgetexpx", 4, two(0xF000, 0x001E), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fgetmanb", 4, two(0xF000, 0x581F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fgetmand", 4, two(0xF000, 0x541F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fgetmanl", 4, two(0xF000, 0x401F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fgetmanp", 4, two(0xF000, 0x4C1F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fgetmans", 4, two(0xF000, 0x441F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fgetmanw", 4, two(0xF000, 0x501F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fgetmanx", 4, two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fgetmanx", 4, two(0xF000, 0x481F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fgetmanx", 4, two(0xF000, 0x001F), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fintb", 4, two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fintb", 4, two(0xF000, 0x5801), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintd", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fintd", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fintd", 4, two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fintd", 4, two(0xF000, 0x5401), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fintl", 4, two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fintl", 4, two(0xF000, 0x4001), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintp", 4, two(0xF000, 0x4C01), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fints", 4, two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fints", 4, two(0xF000, 0x4401), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintw", 4, two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fintw", 4, two(0xF000, 0x5001), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintx", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fintx", 4, two(0xF000, 0x4801), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fintx", 4, two(0xF000, 0x0001), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fintrzb", 4, two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fintrzb", 4, two(0xF000, 0x5803), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzd", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fintrzd", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fintrzd", 4, two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fintrzd", 4, two(0xF000, 0x5403), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fintrzl", 4, two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fintrzl", 4, two(0xF000, 0x4003), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzp", 4, two(0xF000, 0x4C03), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fintrzs", 4, two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fintrzs", 4, two(0xF000, 0x4403), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzw", 4, two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fintrzw", 4, two(0xF000, 0x5003), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fintrzx", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fintrzx", 4, two(0xF000, 0x4803), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fintrzx", 4, two(0xF000, 0x0003), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flog10b", 4, two(0xF000, 0x5815), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flog10d", 4, two(0xF000, 0x5415), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flog10l", 4, two(0xF000, 0x4015), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flog10p", 4, two(0xF000, 0x4C15), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flog10s", 4, two(0xF000, 0x4415), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flog10w", 4, two(0xF000, 0x5015), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flog10x", 4, two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flog10x", 4, two(0xF000, 0x4815), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flog10x", 4, two(0xF000, 0x0015), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flog2b", 4, two(0xF000, 0x5816), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flog2d", 4, two(0xF000, 0x5416), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flog2l", 4, two(0xF000, 0x4016), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flog2p", 4, two(0xF000, 0x4C16), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flog2s", 4, two(0xF000, 0x4416), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flog2w", 4, two(0xF000, 0x5016), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flog2x", 4, two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flog2x", 4, two(0xF000, 0x4816), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flog2x", 4, two(0xF000, 0x0016), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flognb", 4, two(0xF000, 0x5814), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flognd", 4, two(0xF000, 0x5414), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flognl", 4, two(0xF000, 0x4014), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flognp", 4, two(0xF000, 0x4C14), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flogns", 4, two(0xF000, 0x4414), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flognw", 4, two(0xF000, 0x5014), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flognx", 4, two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flognx", 4, two(0xF000, 0x4814), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flognx", 4, two(0xF000, 0x0014), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"flognp1b", 4, two(0xF000, 0x5806), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"flognp1d", 4, two(0xF000, 0x5406), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"flognp1l", 4, two(0xF000, 0x4006), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"flognp1p", 4, two(0xF000, 0x4C06), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"flognp1s", 4, two(0xF000, 0x4406), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"flognp1w", 4, two(0xF000, 0x5006), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"flognp1x", 4, two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"flognp1x", 4, two(0xF000, 0x4806), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"flognp1x", 4, two(0xF000, 0x0006), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fmodb", 4, two(0xF000, 0x5821), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmodd", 4, two(0xF000, 0x5421), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmodl", 4, two(0xF000, 0x4021), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmodp", 4, two(0xF000, 0x4C21), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmods", 4, two(0xF000, 0x4421), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmodw", 4, two(0xF000, 0x5021), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmodx", 4, two(0xF000, 0x0021), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fmodx", 4, two(0xF000, 0x4821), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fmoveb", 4, two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmoveb", 4, two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7bs", cfloat },
+{"fmoveb", 4, two(0xF000, 0x5800), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmoveb", 4, two(0xF000, 0x7800), two(0xF1C0, 0xFC7F), "IiF7$b", mfloat },
+{"fmoved", 4, two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmoved", 4, two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7~F", mfloat },
+{"fmoved", 4, two(0xF000, 0x0000), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fmoved", 4, two(0xF000, 0x5400), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fmoved", 4, two(0xF000, 0x7400), two(0xF1C0, 0xFC7F), "IiF7ws", cfloat },
+{"fmovel", 4, two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmovel", 4, two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7$l", mfloat },
+/* FIXME: the next two variants should not permit moving an address
+ register to anything but the floating point instruction register. */
+{"fmovel", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat },
+{"fmovel", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ls8", mfloat },
+{"fmovel", 4, two(0xF000, 0x4000), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmovel", 4, two(0xF000, 0x6000), two(0xF1C0, 0xFC7F), "IiF7bs", cfloat },
+ /* Move the FP control registers. */
+{"fmovel", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8ps", cfloat },
+{"fmovel", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Iibss8", cfloat },
+{"fmovep", 4, two(0xF000, 0x4C00), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmovep", 4, two(0xF000, 0x6C00), two(0xF1C0, 0xFC00), "IiF7~pkC", mfloat },
+{"fmovep", 4, two(0xF000, 0x7C00), two(0xF1C0, 0xFC0F), "IiF7~pDk", mfloat },
+{"fmoves", 4, two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmoves", 4, two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7$f", mfloat },
+{"fmoves", 4, two(0xF000, 0x4400), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmoves", 4, two(0xF000, 0x6400), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fmovew", 4, two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmovew", 4, two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7$w", mfloat },
+{"fmovew", 4, two(0xF000, 0x5000), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmovew", 4, two(0xF000, 0x7000), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fmovex", 4, two(0xF000, 0x0000), two(0xF1FF, 0xE07F), "IiF8F7", mfloat },
+{"fmovex", 4, two(0xF000, 0x4800), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fmovex", 4, two(0xF000, 0x6800), two(0xF1C0, 0xFC7F), "IiF7~x", mfloat },
+
+{"fsmoveb", 4, two(0xF000, 0x5840), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsmoveb", 4, two(0xF000, 0x5840), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmoveb", 4, two(0xF000, 0x7840), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmoved", 4, two(0xF000, 0x0040), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsmoved", 4, two(0xF000, 0x5440), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsmoved", 4, two(0xF000, 0x5440), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsmoved", 4, two(0xF000, 0x7440), two(0xF1C0, 0xFC7F), "IiF7ws", cfloat },
+{"fsmovel", 4, two(0xF000, 0x4040), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsmovel", 4, two(0xF000, 0x4040), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmovel", 4, two(0xF000, 0x6040), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmoves", 4, two(0xF000, 0x4440), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsmoves", 4, two(0xF000, 0x4440), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmoves", 4, two(0xF000, 0x6440), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmovew", 4, two(0xF000, 0x5040), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsmovew", 4, two(0xF000, 0x5040), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmovew", 4, two(0xF000, 0x7040), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fsmovex", 4, two(0xF000, 0x0040), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsmovex", 4, two(0xF000, 0x4840), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fsmovep", 4, two(0xF000, 0x4C40), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+
+{"fdmoveb", 4, two(0xF000, 0x5844), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdmoveb", 4, two(0xF000, 0x5844), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmoveb", 4, two(0xF000, 0x7844), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmoved", 4, two(0xF000, 0x0044), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdmoved", 4, two(0xF000, 0x5444), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdmoved", 4, two(0xF000, 0x5444), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdmoved", 4, two(0xF000, 0x7444), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmovel", 4, two(0xF000, 0x4044), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdmovel", 4, two(0xF000, 0x4044), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmovel", 4, two(0xF000, 0x6044), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmoves", 4, two(0xF000, 0x4444), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdmoves", 4, two(0xF000, 0x4444), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmoves", 4, two(0xF000, 0x6444), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmovew", 4, two(0xF000, 0x5044), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdmovew", 4, two(0xF000, 0x5044), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmovew", 4, two(0xF000, 0x7044), two(0xF1C0, 0xFC7F), "IiF7qs", cfloat },
+{"fdmovex", 4, two(0xF000, 0x0044), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdmovex", 4, two(0xF000, 0x4844), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdmovep", 4, two(0xF000, 0x4C44), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+
+{"fmovecrx", 4, two(0xF000, 0x5C00), two(0xF1FF, 0xFC00), "Ii#CF7", mfloat },
+
+{"fmovemd", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizsl3", cfloat },
+{"fmovemd", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizs#3", cfloat },
+{"fmovemd", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Ii#3ys", cfloat },
+{"fmovemd", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Iil3ys", cfloat },
+
+{"fmovemx", 4, two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat },
+{"fmovemx", 4, two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat },
+{"fmovemx", 4, two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat },
+{"fmovemx", 4, two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat },
+{"fmovemx", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat },
+{"fmovemx", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat },
+{"fmovemx", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat },
+{"fmovemx", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat },
+{"fmovemx", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat },
+{"fmovemx", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat },
+{"fmovemx", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat },
+{"fmovemx", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat },
+
+{"fmoveml", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat },
+{"fmoveml", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8~s", mfloat },
+/* FIXME: In the next instruction, we should only permit %dn if the
+ target is a single register. We should only permit %an if the
+ target is a single %fpiar. */
+{"fmoveml", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*lL8", mfloat },
+
+{"fmovem", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "IizsL3", cfloat },
+{"fmovem", 4, two(0xF000, 0xD000), two(0xFFC0, 0xFF00), "Iizs#3", cfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "Ii#3ys", cfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xFFC0, 0xFF00), "IiL3ys", cfloat },
+
+{"fmovem", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "IdL3-s", mfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Idl3&s", mfloat },
+{"fmovem", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+sl3", mfloat },
+{"fmovem", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&sl3", mfloat },
+{"fmovem", 4, two(0xF020, 0xE000), two(0xF1F8, 0xFF00), "Id#3-s", mfloat },
+{"fmovem", 4, two(0xF020, 0xE800), two(0xF1F8, 0xFF8F), "IiDk-s", mfloat },
+{"fmovem", 4, two(0xF000, 0xF000), two(0xF1C0, 0xFF00), "Id#3&s", mfloat },
+{"fmovem", 4, two(0xF000, 0xF800), two(0xF1C0, 0xFF8F), "IiDk&s", mfloat },
+{"fmovem", 4, two(0xF018, 0xD000), two(0xF1F8, 0xFF00), "Id+s#3", mfloat },
+{"fmovem", 4, two(0xF018, 0xD800), two(0xF1F8, 0xFF8F), "Ii+sDk", mfloat },
+{"fmovem", 4, two(0xF000, 0xD000), two(0xF1C0, 0xFF00), "Id&s#3", mfloat },
+{"fmovem", 4, two(0xF000, 0xD800), two(0xF1C0, 0xFF8F), "Ii&sDk", mfloat },
+{"fmovem", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "Iis8%s", mfloat },
+{"fmovem", 4, two(0xF000, 0x8000), two(0xF1C0, 0xE3FF), "Ii*ss8", mfloat },
+{"fmovem", 4, two(0xF000, 0xA000), two(0xF1C0, 0xE3FF), "IiL8~s", mfloat },
+{"fmovem", 4, two(0xF000, 0x8000), two(0xF2C0, 0xE3FF), "Ii*sL8", mfloat },
+
+{"fmulb", 4, two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fmulb", 4, two(0xF000, 0x5823), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmuld", 4, two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fmuld", 4, two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fmuld", 4, two(0xF000, 0x5423), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fmull", 4, two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fmull", 4, two(0xF000, 0x4023), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmulp", 4, two(0xF000, 0x4C23), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fmuls", 4, two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fmuls", 4, two(0xF000, 0x4423), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmulw", 4, two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fmulw", 4, two(0xF000, 0x5023), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fmulx", 4, two(0xF000, 0x0023), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fmulx", 4, two(0xF000, 0x4823), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"fsmulb", 4, two(0xF000, 0x5863), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsmulb", 4, two(0xF000, 0x5863), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmuld", 4, two(0xF000, 0x0063), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsmuld", 4, two(0xF000, 0x5463), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsmuld", 4, two(0xF000, 0x5463), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsmull", 4, two(0xF000, 0x4063), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsmull", 4, two(0xF000, 0x4063), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmulp", 4, two(0xF000, 0x4C63), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsmuls", 4, two(0xF000, 0x4463), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsmuls", 4, two(0xF000, 0x4463), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmulw", 4, two(0xF000, 0x5063), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsmulw", 4, two(0xF000, 0x5063), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsmulx", 4, two(0xF000, 0x0063), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsmulx", 4, two(0xF000, 0x4863), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fdmulb", 4, two(0xF000, 0x5867), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdmulb", 4, two(0xF000, 0x5867), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmuld", 4, two(0xF000, 0x0067), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdmuld", 4, two(0xF000, 0x5467), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdmuld", 4, two(0xF000, 0x5467), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdmull", 4, two(0xF000, 0x4067), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdmull", 4, two(0xF000, 0x4067), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmulp", 4, two(0xF000, 0x4C67), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdmuls", 4, two(0xF000, 0x4467), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdmuls", 4, two(0xF000, 0x4467), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmulw", 4, two(0xF000, 0x5067), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdmulw", 4, two(0xF000, 0x5067), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdmulx", 4, two(0xF000, 0x0067), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdmulx", 4, two(0xF000, 0x4867), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+
+{"fnegb", 4, two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fnegb", 4, two(0xF000, 0x581A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegd", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fnegd", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fnegd", 4, two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fnegd", 4, two(0xF000, 0x541A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fnegl", 4, two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fnegl", 4, two(0xF000, 0x401A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegp", 4, two(0xF000, 0x4C1A), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fnegs", 4, two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fnegs", 4, two(0xF000, 0x441A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegw", 4, two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fnegw", 4, two(0xF000, 0x501A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fnegx", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fnegx", 4, two(0xF000, 0x481A), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fnegx", 4, two(0xF000, 0x001A), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsnegb", 4, two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fsnegb", 4, two(0xF000, 0x585A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegd", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsnegd", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fsnegd", 4, two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fsnegd", 4, two(0xF000, 0x545A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsnegl", 4, two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fsnegl", 4, two(0xF000, 0x405A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegp", 4, two(0xF000, 0x4C5A), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fsnegs", 4, two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fsnegs", 4, two(0xF000, 0x445A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegw", 4, two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fsnegw", 4, two(0xF000, 0x505A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsnegx", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fsnegx", 4, two(0xF000, 0x485A), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fsnegx", 4, two(0xF000, 0x005A), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdnegb", 4, two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdnegb", 4, two(0xF000, 0x585E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegd", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdnegd", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fdnegd", 4, two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdnegd", 4, two(0xF000, 0x545E), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdnegl", 4, two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdnegl", 4, two(0xF000, 0x405E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegp", 4, two(0xF000, 0x4C5E), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdnegs", 4, two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdnegs", 4, two(0xF000, 0x445E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegw", 4, two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdnegw", 4, two(0xF000, 0x505E), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdnegx", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdnegx", 4, two(0xF000, 0x485E), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdnegx", 4, two(0xF000, 0x005E), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fnop", 4, two(0xF280, 0x0000), two(0xFFFF, 0xFFFF), "Ii", mfloat | cfloat },
+
+{"fremb", 4, two(0xF000, 0x5825), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fremd", 4, two(0xF000, 0x5425), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"freml", 4, two(0xF000, 0x4025), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fremp", 4, two(0xF000, 0x4C25), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"frems", 4, two(0xF000, 0x4425), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fremw", 4, two(0xF000, 0x5025), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fremx", 4, two(0xF000, 0x0025), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fremx", 4, two(0xF000, 0x4825), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+{"frestore", 2, one(0xF140), one(0xF1C0), "Id<s", mfloat },
+{"frestore", 2, one(0xF140), one(0xF1C0), "Idys", cfloat },
+
+{"fsave", 2, one(0xF100), one(0xF1C0), "Id>s", mfloat },
+{"fsave", 2, one(0xF100), one(0xF1C0), "Idzs", cfloat },
+
+{"fscaleb", 4, two(0xF000, 0x5826), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fscaled", 4, two(0xF000, 0x5426), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fscalel", 4, two(0xF000, 0x4026), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fscalep", 4, two(0xF000, 0x4C26), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fscales", 4, two(0xF000, 0x4426), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fscalew", 4, two(0xF000, 0x5026), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fscalex", 4, two(0xF000, 0x0026), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fscalex", 4, two(0xF000, 0x4826), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+
+/* $ is necessary to prevent the assembler from using PC-relative.
+ If @ were used, "label: fseq label" could produce "ftrapeq", 2,
+ because "label" became "pc@label". */
+{"fseq", 4, two(0xF040, 0x0001), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsf", 4, two(0xF040, 0x0000), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsge", 4, two(0xF040, 0x0013), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgl", 4, two(0xF040, 0x0016), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgle", 4, two(0xF040, 0x0017), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsgt", 4, two(0xF040, 0x0012), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsle", 4, two(0xF040, 0x0015), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fslt", 4, two(0xF040, 0x0014), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsne", 4, two(0xF040, 0x000E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnge", 4, two(0xF040, 0x001C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngl", 4, two(0xF040, 0x0019), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngle", 4, two(0xF040, 0x0018), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsngt", 4, two(0xF040, 0x001D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnle", 4, two(0xF040, 0x001A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsnlt", 4, two(0xF040, 0x001B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsoge", 4, two(0xF040, 0x0003), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsogl", 4, two(0xF040, 0x0006), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsogt", 4, two(0xF040, 0x0002), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsole", 4, two(0xF040, 0x0005), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsolt", 4, two(0xF040, 0x0004), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsor", 4, two(0xF040, 0x0007), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsseq", 4, two(0xF040, 0x0011), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fssf", 4, two(0xF040, 0x0010), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fssne", 4, two(0xF040, 0x001E), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsst", 4, two(0xF040, 0x001F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fst", 4, two(0xF040, 0x000F), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsueq", 4, two(0xF040, 0x0009), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsuge", 4, two(0xF040, 0x000B), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsugt", 4, two(0xF040, 0x000A), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsule", 4, two(0xF040, 0x000D), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsult", 4, two(0xF040, 0x000C), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+{"fsun", 4, two(0xF040, 0x0008), two(0xF1C0, 0xFFFF), "Ii$s", mfloat },
+
+{"fsgldivb", 4, two(0xF000, 0x5824), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsgldivd", 4, two(0xF000, 0x5424), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsgldivl", 4, two(0xF000, 0x4024), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsgldivp", 4, two(0xF000, 0x4C24), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsgldivs", 4, two(0xF000, 0x4424), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsgldivw", 4, two(0xF000, 0x5024), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsgldivx", 4, two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsgldivx", 4, two(0xF000, 0x4824), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsgldivx", 4, two(0xF000, 0x0024), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsglmulb", 4, two(0xF000, 0x5827), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsglmuld", 4, two(0xF000, 0x5427), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsglmull", 4, two(0xF000, 0x4027), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsglmulp", 4, two(0xF000, 0x4C27), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsglmuls", 4, two(0xF000, 0x4427), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsglmulw", 4, two(0xF000, 0x5027), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsglmulx", 4, two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsglmulx", 4, two(0xF000, 0x4827), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsglmulx", 4, two(0xF000, 0x0027), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsinb", 4, two(0xF000, 0x580E), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsind", 4, two(0xF000, 0x540E), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsinl", 4, two(0xF000, 0x400E), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsinp", 4, two(0xF000, 0x4C0E), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsins", 4, two(0xF000, 0x440E), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsinw", 4, two(0xF000, 0x500E), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsinx", 4, two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsinx", 4, two(0xF000, 0x480E), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsinx", 4, two(0xF000, 0x000E), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsincosb", 4, two(0xF000, 0x5830), two(0xF1C0, 0xFC78), "Ii;bF3F7", mfloat },
+{"fsincosd", 4, two(0xF000, 0x5430), two(0xF1C0, 0xFC78), "Ii;FF3F7", mfloat },
+{"fsincosl", 4, two(0xF000, 0x4030), two(0xF1C0, 0xFC78), "Ii;lF3F7", mfloat },
+{"fsincosp", 4, two(0xF000, 0x4C30), two(0xF1C0, 0xFC78), "Ii;pF3F7", mfloat },
+{"fsincoss", 4, two(0xF000, 0x4430), two(0xF1C0, 0xFC78), "Ii;fF3F7", mfloat },
+{"fsincosw", 4, two(0xF000, 0x5030), two(0xF1C0, 0xFC78), "Ii;wF3F7", mfloat },
+{"fsincosx", 4, two(0xF000, 0x0030), two(0xF1C0, 0xE078), "IiF8F3F7", mfloat },
+{"fsincosx", 4, two(0xF000, 0x4830), two(0xF1C0, 0xFC78), "Ii;xF3F7", mfloat },
+
+{"fsinhb", 4, two(0xF000, 0x5802), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsinhd", 4, two(0xF000, 0x5402), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsinhl", 4, two(0xF000, 0x4002), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsinhp", 4, two(0xF000, 0x4C02), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsinhs", 4, two(0xF000, 0x4402), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsinhw", 4, two(0xF000, 0x5002), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsinhx", 4, two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsinhx", 4, two(0xF000, 0x4802), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsinhx", 4, two(0xF000, 0x0002), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fsqrtb", 4, two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsqrtb", 4, two(0xF000, 0x5804), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtd", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsqrtd", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fsqrtd", 4, two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsqrtd", 4, two(0xF000, 0x5404), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsqrtl", 4, two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsqrtl", 4, two(0xF000, 0x4004), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtp", 4, two(0xF000, 0x4C04), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsqrts", 4, two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsqrts", 4, two(0xF000, 0x4404), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtw", 4, two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsqrtw", 4, two(0xF000, 0x5004), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsqrtx", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsqrtx", 4, two(0xF000, 0x4804), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsqrtx", 4, two(0xF000, 0x0004), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fssqrtb", 4, two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fssqrtb", 4, two(0xF000, 0x5841), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtd", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fssqrtd", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fssqrtd", 4, two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fssqrtd", 4, two(0xF000, 0x5441), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fssqrtl", 4, two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fssqrtl", 4, two(0xF000, 0x4041), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtp", 4, two(0xF000, 0x4C41), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fssqrts", 4, two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fssqrts", 4, two(0xF000, 0x4441), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtw", 4, two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fssqrtw", 4, two(0xF000, 0x5041), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssqrtx", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fssqrtx", 4, two(0xF000, 0x4841), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fssqrtx", 4, two(0xF000, 0x0041), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdsqrtb", 4, two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdsqrtb", 4, two(0xF000, 0x5845), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtd", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdsqrtd", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", cfloat },
+{"fdsqrtd", 4, two(0xF000, 0x5445), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdsqrtl", 4, two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdsqrtl", 4, two(0xF000, 0x4045), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtp", 4, two(0xF000, 0x4C45), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdsqrts", 4, two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdsqrts", 4, two(0xF000, 0x4445), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtw", 4, two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdsqrtw", 4, two(0xF000, 0x5045), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsqrtx", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdsqrtx", 4, two(0xF000, 0x4845), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdsqrtx", 4, two(0xF000, 0x0045), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fsubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"fsubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubd", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fsubd", 4, two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"fsubd", 4, two(0xF000, 0x5428), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fsubl", 4, two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"fsubl", 4, two(0xF000, 0x4028), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubp", 4, two(0xF000, 0x4C28), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"fsubs", 4, two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"fsubs", 4, two(0xF000, 0x4428), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubw", 4, two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"fsubw", 4, two(0xF000, 0x5028), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fsubx", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"fsubx", 4, two(0xF000, 0x4828), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"fsubx", 4, two(0xF000, 0x0028), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"fssubb", 4, two(0xF000, 0x5828), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubb", 4, two(0xF000, 0x5868), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fssubd", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fssubd", 4, two(0xF000, 0x5468), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fssubd", 4, two(0xF000, 0x5468), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fssubl", 4, two(0xF000, 0x4068), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fssubl", 4, two(0xF000, 0x4068), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubp", 4, two(0xF000, 0x4C68), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fssubs", 4, two(0xF000, 0x4468), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fssubs", 4, two(0xF000, 0x4468), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubw", 4, two(0xF000, 0x5068), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fssubw", 4, two(0xF000, 0x5068), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fssubx", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fssubx", 4, two(0xF000, 0x4868), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fssubx", 4, two(0xF000, 0x0068), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"fdsubb", 4, two(0xF000, 0x586A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubb", 4, two(0xF000, 0x586c), two(0xF1C0, 0xFC7F), "Ii;bF7", m68040up },
+{"fdsubd", 4, two(0xF000, 0x006A), two(0xF1C0, 0xE07F), "IiF8F7", cfloat },
+{"fdsubd", 4, two(0xF000, 0x546A), two(0xF1C0, 0xFC7F), "IiwsF7", cfloat },
+{"fdsubd", 4, two(0xF000, 0x546c), two(0xF1C0, 0xFC7F), "Ii;FF7", m68040up },
+{"fdsubl", 4, two(0xF000, 0x406A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubl", 4, two(0xF000, 0x406c), two(0xF1C0, 0xFC7F), "Ii;lF7", m68040up },
+{"fdsubp", 4, two(0xF000, 0x4C6c), two(0xF1C0, 0xFC7F), "Ii;pF7", m68040up },
+{"fdsubs", 4, two(0xF000, 0x446A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubs", 4, two(0xF000, 0x446c), two(0xF1C0, 0xFC7F), "Ii;fF7", m68040up },
+{"fdsubw", 4, two(0xF000, 0x506A), two(0xF1C0, 0xFC7F), "IibsF7", cfloat },
+{"fdsubw", 4, two(0xF000, 0x506c), two(0xF1C0, 0xFC7F), "Ii;wF7", m68040up },
+{"fdsubx", 4, two(0xF000, 0x006c), two(0xF1C0, 0xE07F), "IiF8F7", m68040up },
+{"fdsubx", 4, two(0xF000, 0x486c), two(0xF1C0, 0xFC7F), "Ii;xF7", m68040up },
+{"fdsubx", 4, two(0xF000, 0x006c), two(0xF1C0, 0xE07F), "IiFt", m68040up },
+
+{"ftanb", 4, two(0xF000, 0x580F), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftand", 4, two(0xF000, 0x540F), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftanl", 4, two(0xF000, 0x400F), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftanp", 4, two(0xF000, 0x4C0F), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftans", 4, two(0xF000, 0x440F), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftanw", 4, two(0xF000, 0x500F), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftanx", 4, two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftanx", 4, two(0xF000, 0x480F), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftanx", 4, two(0xF000, 0x000F), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftanhb", 4, two(0xF000, 0x5809), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftanhd", 4, two(0xF000, 0x5409), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftanhl", 4, two(0xF000, 0x4009), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftanhp", 4, two(0xF000, 0x4C09), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftanhs", 4, two(0xF000, 0x4409), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftanhw", 4, two(0xF000, 0x5009), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftanhx", 4, two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftanhx", 4, two(0xF000, 0x4809), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftanhx", 4, two(0xF000, 0x0009), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftentoxb", 4, two(0xF000, 0x5812), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftentoxd", 4, two(0xF000, 0x5412), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftentoxl", 4, two(0xF000, 0x4012), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftentoxp", 4, two(0xF000, 0x4C12), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftentoxs", 4, two(0xF000, 0x4412), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftentoxw", 4, two(0xF000, 0x5012), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftentoxx", 4, two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftentoxx", 4, two(0xF000, 0x4812), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftentoxx", 4, two(0xF000, 0x0012), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"ftrapeq", 4, two(0xF07C, 0x0001), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapf", 4, two(0xF07C, 0x0000), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapge", 4, two(0xF07C, 0x0013), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgl", 4, two(0xF07C, 0x0016), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgle", 4, two(0xF07C, 0x0017), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapgt", 4, two(0xF07C, 0x0012), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftraple", 4, two(0xF07C, 0x0015), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftraplt", 4, two(0xF07C, 0x0014), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapne", 4, two(0xF07C, 0x000E), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnge", 4, two(0xF07C, 0x001C), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngl", 4, two(0xF07C, 0x0019), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngle", 4,two(0xF07C, 0x0018), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapngt", 4, two(0xF07C, 0x001D), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnle", 4, two(0xF07C, 0x001A), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapnlt", 4, two(0xF07C, 0x001B), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapoge", 4, two(0xF07C, 0x0003), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapogl", 4, two(0xF07C, 0x0006), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapogt", 4, two(0xF07C, 0x0002), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapole", 4, two(0xF07C, 0x0005), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapolt", 4, two(0xF07C, 0x0004), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapor", 4, two(0xF07C, 0x0007), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapseq", 4, two(0xF07C, 0x0011), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapsf", 4, two(0xF07C, 0x0010), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapsne", 4, two(0xF07C, 0x001E), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapst", 4, two(0xF07C, 0x001F), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapt", 4, two(0xF07C, 0x000F), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapueq", 4, two(0xF07C, 0x0009), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapuge", 4, two(0xF07C, 0x000B), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapugt", 4, two(0xF07C, 0x000A), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapule", 4, two(0xF07C, 0x000D), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapult", 4, two(0xF07C, 0x000C), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+{"ftrapun", 4, two(0xF07C, 0x0008), two(0xF1FF, 0xFFFF), "Ii", mfloat },
+
+{"ftrapeqw", 4, two(0xF07A, 0x0001), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapfw", 4, two(0xF07A, 0x0000), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapgew", 4, two(0xF07A, 0x0013), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapglw", 4, two(0xF07A, 0x0016), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapglew", 4,two(0xF07A, 0x0017), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapgtw", 4, two(0xF07A, 0x0012), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraplew", 4, two(0xF07A, 0x0015), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapltw", 4, two(0xF07A, 0x0014), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnew", 4, two(0xF07A, 0x000E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapngew", 4,two(0xF07A, 0x001C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnglw", 4,two(0xF07A, 0x0019), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnglew", 4,two(0xF07A, 0x0018), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapngtw", 4,two(0xF07A, 0x001D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnlew", 4,two(0xF07A, 0x001A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapnltw", 4,two(0xF07A, 0x001B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapogew", 4,two(0xF07A, 0x0003), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapoglw", 4,two(0xF07A, 0x0006), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapogtw", 4,two(0xF07A, 0x0002), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapolew", 4,two(0xF07A, 0x0005), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapoltw", 4,two(0xF07A, 0x0004), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraporw", 4, two(0xF07A, 0x0007), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapseqw", 4,two(0xF07A, 0x0011), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapsfw", 4, two(0xF07A, 0x0010), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapsnew", 4,two(0xF07A, 0x001E), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapstw", 4, two(0xF07A, 0x001F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftraptw", 4, two(0xF07A, 0x000F), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapueqw", 4,two(0xF07A, 0x0009), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapugew", 4,two(0xF07A, 0x000B), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapugtw", 4,two(0xF07A, 0x000A), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapulew", 4,two(0xF07A, 0x000D), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapultw", 4,two(0xF07A, 0x000C), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+{"ftrapunw", 4, two(0xF07A, 0x0008), two(0xF1FF, 0xFFFF), "Ii^w", mfloat },
+
+{"ftrapeql", 4, two(0xF07B, 0x0001), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapfl", 4, two(0xF07B, 0x0000), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgel", 4, two(0xF07B, 0x0013), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgll", 4, two(0xF07B, 0x0016), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapglel", 4,two(0xF07B, 0x0017), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapgtl", 4, two(0xF07B, 0x0012), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraplel", 4, two(0xF07B, 0x0015), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapltl", 4, two(0xF07B, 0x0014), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnel", 4, two(0xF07B, 0x000E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngel", 4,two(0xF07B, 0x001C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngll", 4,two(0xF07B, 0x0019), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnglel", 4,two(0xF07B, 0x0018), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapngtl", 4,two(0xF07B, 0x001D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnlel", 4,two(0xF07B, 0x001A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapnltl", 4,two(0xF07B, 0x001B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogel", 4,two(0xF07B, 0x0003), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogll", 4,two(0xF07B, 0x0006), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapogtl", 4,two(0xF07B, 0x0002), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapolel", 4,two(0xF07B, 0x0005), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapoltl", 4,two(0xF07B, 0x0004), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraporl", 4, two(0xF07B, 0x0007), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapseql", 4,two(0xF07B, 0x0011), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapsfl", 4, two(0xF07B, 0x0010), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapsnel", 4,two(0xF07B, 0x001E), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapstl", 4, two(0xF07B, 0x001F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftraptl", 4, two(0xF07B, 0x000F), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapueql", 4,two(0xF07B, 0x0009), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapugel", 4,two(0xF07B, 0x000B), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapugtl", 4,two(0xF07B, 0x000A), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapulel", 4,two(0xF07B, 0x000D), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapultl", 4,two(0xF07B, 0x000C), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+{"ftrapunl", 4, two(0xF07B, 0x0008), two(0xF1FF, 0xFFFF), "Ii^l", mfloat },
+
+{"ftstb", 4, two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Ii;b", mfloat },
+{"ftstb", 4, two(0xF000, 0x583A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstd", 4, two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", cfloat },
+{"ftstd", 4, two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Ii;F", mfloat },
+{"ftstd", 4, two(0xF000, 0x543A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstl", 4, two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Ii;l", mfloat },
+{"ftstl", 4, two(0xF000, 0x403A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstp", 4, two(0xF000, 0x4C3A), two(0xF1C0, 0xFC7F), "Ii;p", mfloat },
+{"ftsts", 4, two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Ii;f", mfloat },
+{"ftsts", 4, two(0xF000, 0x443A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstw", 4, two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Ii;w", mfloat },
+{"ftstw", 4, two(0xF000, 0x503A), two(0xF1C0, 0xFC7F), "Iibs", cfloat },
+{"ftstx", 4, two(0xF000, 0x003A), two(0xF1C0, 0xE07F), "IiF8", mfloat },
+{"ftstx", 4, two(0xF000, 0x483A), two(0xF1C0, 0xFC7F), "Ii;x", mfloat },
+
+{"ftwotoxb", 4, two(0xF000, 0x5811), two(0xF1C0, 0xFC7F), "Ii;bF7", mfloat },
+{"ftwotoxd", 4, two(0xF000, 0x5411), two(0xF1C0, 0xFC7F), "Ii;FF7", mfloat },
+{"ftwotoxl", 4, two(0xF000, 0x4011), two(0xF1C0, 0xFC7F), "Ii;lF7", mfloat },
+{"ftwotoxp", 4, two(0xF000, 0x4C11), two(0xF1C0, 0xFC7F), "Ii;pF7", mfloat },
+{"ftwotoxs", 4, two(0xF000, 0x4411), two(0xF1C0, 0xFC7F), "Ii;fF7", mfloat },
+{"ftwotoxw", 4, two(0xF000, 0x5011), two(0xF1C0, 0xFC7F), "Ii;wF7", mfloat },
+{"ftwotoxx", 4, two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiF8F7", mfloat },
+{"ftwotoxx", 4, two(0xF000, 0x4811), two(0xF1C0, 0xFC7F), "Ii;xF7", mfloat },
+{"ftwotoxx", 4, two(0xF000, 0x0011), two(0xF1C0, 0xE07F), "IiFt", mfloat },
+
+{"halt", 2, one(0045310), one(0177777), "", m68060 | mcfisa_a },
+
+{"illegal", 2, one(0045374), one(0177777), "", m68000up | mcfisa_a },
+{"intouch", 2, one(0xf428), one(0xfff8), "As", mcfisa_b },
+
+{"jmp", 2, one(0047300), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"jra", 2, one(0060000), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jra", 2, one(0047300), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"jsr", 2, one(0047200), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"jbsr", 2, one(0060400), one(0177400), "Bg", m68000up | mcfisa_a },
+{"jbsr", 2, one(0047200), one(0177700), "!s", m68000up | mcfisa_a },
+
+{"lea", 2, one(0040700), one(0170700), "!sAd", m68000up | mcfisa_a },
+
+{"lpstop", 6, two(0174000,0000700),two(0177777,0177777),"#w", cpu32|m68060 },
+
+{"linkw", 4, one(0047120), one(0177770), "As#w", m68000up | mcfisa_a },
+{"linkl", 6, one(0044010), one(0177770), "As#l", m68020up | cpu32 },
+{"link", 4, one(0047120), one(0177770), "As#W", m68000up | mcfisa_a },
+{"link", 6, one(0044010), one(0177770), "As#l", m68020up | cpu32 },
+
+{"lslb", 2, one(0160410), one(0170770), "QdDs", m68000up },
+{"lslb", 2, one(0160450), one(0170770), "DdDs", m68000up },
+{"lslw", 2, one(0160510), one(0170770), "QdDs", m68000up },
+{"lslw", 2, one(0160550), one(0170770), "DdDs", m68000up },
+{"lslw", 2, one(0161700), one(0177700), "~s", m68000up },
+{"lsll", 2, one(0160610), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"lsll", 2, one(0160650), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"lsrb", 2, one(0160010), one(0170770), "QdDs", m68000up },
+{"lsrb", 2, one(0160050), one(0170770), "DdDs", m68000up },
+{"lsrw", 2, one(0160110), one(0170770), "QdDs", m68000up },
+{"lsrw", 2, one(0160150), one(0170770), "DdDs", m68000up },
+{"lsrw", 2, one(0161300), one(0177700), "~s", m68000up },
+{"lsrl", 2, one(0160210), one(0170770), "QdDs", m68000up | mcfisa_a },
+{"lsrl", 2, one(0160250), one(0170770), "DdDs", m68000up | mcfisa_a },
+
+{"macw", 4, two(0xa080, 0x0000), two(0xf180, 0x0910), "uNuoiI4/Rn", mcfmac },
+{"macw", 4, two(0xa080, 0x0200), two(0xf180, 0x0910), "uNuoMh4/Rn", mcfmac },
+{"macw", 4, two(0xa080, 0x0000), two(0xf180, 0x0f10), "uNuo4/Rn", mcfmac },
+{"macw", 4, two(0xa000, 0x0000), two(0xf1b0, 0x0900), "uMumiI", mcfmac },
+{"macw", 4, two(0xa000, 0x0200), two(0xf1b0, 0x0900), "uMumMh", mcfmac },
+{"macw", 4, two(0xa000, 0x0000), two(0xf1b0, 0x0f00), "uMum", mcfmac },
+
+{"macw", 4, two(0xa000, 0x0000), two(0xf100, 0x0900), "uNuoiI4/RneG", mcfemac },/* Ry,Rx,SF,<ea>,accX. */
+{"macw", 4, two(0xa000, 0x0200), two(0xf100, 0x0900), "uNuoMh4/RneG", mcfemac },/* Ry,Rx,+1/-1,<ea>,accX. */
+{"macw", 4, two(0xa000, 0x0000), two(0xf100, 0x0f00), "uNuo4/RneG", mcfemac },/* Ry,Rx,<ea>,accX. */
+{"macw", 4, two(0xa000, 0x0000), two(0xf130, 0x0900), "uMumiIeH", mcfemac },/* Ry,Rx,SF,accX. */
+{"macw", 4, two(0xa000, 0x0200), two(0xf130, 0x0900), "uMumMheH", mcfemac },/* Ry,Rx,+1/-1,accX. */
+{"macw", 4, two(0xa000, 0x0000), two(0xf130, 0x0f00), "uMumeH", mcfemac }, /* Ry,Rx,accX. */
+
+{"macl", 4, two(0xa080, 0x0800), two(0xf180, 0x0910), "RNRoiI4/Rn", mcfmac },
+{"macl", 4, two(0xa080, 0x0a00), two(0xf180, 0x0910), "RNRoMh4/Rn", mcfmac },
+{"macl", 4, two(0xa080, 0x0800), two(0xf180, 0x0f10), "RNRo4/Rn", mcfmac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf1b0, 0x0b00), "RMRmiI", mcfmac },
+{"macl", 4, two(0xa000, 0x0a00), two(0xf1b0, 0x0b00), "RMRmMh", mcfmac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf1b0, 0x0800), "RMRm", mcfmac },
+
+{"macl", 4, two(0xa000, 0x0800), two(0xf100, 0x0900), "R3R1iI4/RneG", mcfemac },
+{"macl", 4, two(0xa000, 0x0a00), two(0xf100, 0x0900), "R3R1Mh4/RneG", mcfemac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf100, 0x0f00), "R3R14/RneG", mcfemac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf130, 0x0900), "RMRmiIeH", mcfemac },
+{"macl", 4, two(0xa000, 0x0a00), two(0xf130, 0x0900), "RMRmMheH", mcfemac },
+{"macl", 4, two(0xa000, 0x0800), two(0xf130, 0x0f00), "RMRmeH", mcfemac },
+
+/* NOTE: The mcf5200 family programmer's reference manual does not
+ indicate the byte form of the movea instruction is invalid (as it
+ is on 68000 family cpus). However, experiments on the 5202 yeild
+ unexpected results. The value is copied, but it is not sign extended
+ (as is done with movea.w) and the top three bytes in the address
+ register are not disturbed. I don't know if this is the intended
+ behavior --- it could be a hole in instruction decoding (Motorola
+ decided not to trap all invalid instructions for performance reasons)
+ --- but I suspect that it is not.
+
+ I reported this to Motorola ISD Technical Communications Support,
+ which replied that other coldfire assemblers reject movea.b. For
+ this reason I've decided to not allow moveab.
+
+ jtc@cygnus.com - 97/01/24. */
+
+{"moveal", 2, one(0020100), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"moveaw", 2, one(0030100), one(0170700), "*wAd", m68000up | mcfisa_a },
+
+{"movclrl", 2, one(0xA1C0), one(0xf9f0), "eFRs", mcfemac },
+
+{"movec", 4, one(0047173), one(0177777), "R1Jj", m68010up | mcfisa_a },
+{"movec", 4, one(0047173), one(0177777), "R1#j", m68010up | mcfisa_a },
+{"movec", 4, one(0047172), one(0177777), "JjR1", m68010up },
+{"movec", 4, one(0047172), one(0177777), "#jR1", m68010up },
+
+{"movemw", 4, one(0044200), one(0177700), "Lw&s", m68000up },
+{"movemw", 4, one(0044240), one(0177770), "lw-s", m68000up },
+{"movemw", 4, one(0044200), one(0177700), "#w>s", m68000up },
+{"movemw", 4, one(0046200), one(0177700), "<sLw", m68000up },
+{"movemw", 4, one(0046200), one(0177700), "<s#w", m68000up },
+{"moveml", 4, one(0044300), one(0177700), "Lw&s", m68000up },
+{"moveml", 4, one(0044340), one(0177770), "lw-s", m68000up },
+{"moveml", 4, one(0044300), one(0177700), "#w>s", m68000up },
+{"moveml", 4, one(0046300), one(0177700), "<sLw", m68000up },
+{"moveml", 4, one(0046300), one(0177700), "<s#w", m68000up },
+/* FIXME: need specifier for mode 2 and 5 to simplify below insn patterns. */
+{"moveml", 4, one(0044320), one(0177770), "Lwas", mcfisa_a },
+{"moveml", 4, one(0044320), one(0177770), "#was", mcfisa_a },
+{"moveml", 4, one(0044350), one(0177770), "Lwds", mcfisa_a },
+{"moveml", 4, one(0044350), one(0177770), "#wds", mcfisa_a },
+{"moveml", 4, one(0046320), one(0177770), "asLw", mcfisa_a },
+{"moveml", 4, one(0046320), one(0177770), "as#w", mcfisa_a },
+{"moveml", 4, one(0046350), one(0177770), "dsLw", mcfisa_a },
+{"moveml", 4, one(0046350), one(0177770), "ds#w", mcfisa_a },
+
+{"movepw", 2, one(0000410), one(0170770), "dsDd", m68000up },
+{"movepw", 2, one(0000610), one(0170770), "Ddds", m68000up },
+{"movepl", 2, one(0000510), one(0170770), "dsDd", m68000up },
+{"movepl", 2, one(0000710), one(0170770), "Ddds", m68000up },
+
+{"moveq", 2, one(0070000), one(0170400), "MsDd", m68000up | mcfisa_a },
+{"moveq", 2, one(0070000), one(0170400), "#BDd", m68000up | mcfisa_a },
+
+/* The move opcode can generate the movea and moveq instructions. */
+{"moveb", 2, one(0010000), one(0170000), ";b$d", m68000up },
+{"moveb", 2, one(0010000), one(0170070), "Ds$d", mcfisa_a },
+{"moveb", 2, one(0010020), one(0170070), "as$d", mcfisa_a },
+{"moveb", 2, one(0010030), one(0170070), "+s$d", mcfisa_a },
+{"moveb", 2, one(0010040), one(0170070), "-s$d", mcfisa_a },
+{"moveb", 2, one(0010000), one(0170000), "nsqd", mcfisa_a },
+{"moveb", 2, one(0010000), one(0170700), "obDd", mcfisa_a },
+{"moveb", 2, one(0010200), one(0170700), "obad", mcfisa_a },
+{"moveb", 2, one(0010300), one(0170700), "ob+d", mcfisa_a },
+{"moveb", 2, one(0010400), one(0170700), "ob-d", mcfisa_a },
+{"moveb", 2, one(0010000), one(0170000), "obnd", mcfisa_b },
+
+{"movew", 2, one(0030000), one(0170000), "*w%d", m68000up },
+{"movew", 2, one(0030000), one(0170000), "ms%d", mcfisa_a },
+{"movew", 2, one(0030000), one(0170000), "nspd", mcfisa_a },
+{"movew", 2, one(0030000), one(0170000), "owmd", mcfisa_a },
+{"movew", 2, one(0030000), one(0170000), "ownd", mcfisa_b },
+{"movew", 2, one(0040300), one(0177700), "Ss$s", m68000up },
+{"movew", 2, one(0040300), one(0177770), "SsDs", mcfisa_a },
+{"movew", 2, one(0041300), one(0177700), "Cs$s", m68010up },
+{"movew", 2, one(0041300), one(0177770), "CsDs", mcfisa_a },
+{"movew", 2, one(0042300), one(0177700), ";wCd", m68000up },
+{"movew", 2, one(0042300), one(0177700), "DsCd", mcfisa_a },
+{"movew", 4, one(0042374), one(0177777), "#wCd", mcfisa_a },
+{"movew", 2, one(0043300), one(0177700), ";wSd", m68000up },
+{"movew", 2, one(0043300), one(0177700), "DsSd", mcfisa_a },
+{"movew", 4, one(0043374), one(0177777), "#wSd", mcfisa_a },
+
+{"movel", 2, one(0070000), one(0170400), "MsDd", m68000up | mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "*l%d", m68000up },
+{"movel", 2, one(0020000), one(0170000), "ms%d", mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "nspd", mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "olmd", mcfisa_a },
+{"movel", 2, one(0020000), one(0170000), "olnd", mcfisa_b },
+{"movel", 2, one(0047140), one(0177770), "AsUd", m68000up | mcfusp },
+{"movel", 2, one(0047150), one(0177770), "UdAs", m68000up | mcfusp },
+{"movel", 2, one(0120600), one(0177760), "EsRs", mcfmac },
+{"movel", 2, one(0120400), one(0177760), "RsEs", mcfmac },
+{"movel", 6, one(0120474), one(0177777), "#lEs", mcfmac },
+{"movel", 2, one(0124600), one(0177760), "GsRs", mcfmac },
+{"movel", 2, one(0124400), one(0177760), "RsGs", mcfmac },
+{"movel", 6, one(0124474), one(0177777), "#lGs", mcfmac },
+{"movel", 2, one(0126600), one(0177760), "HsRs", mcfmac },
+{"movel", 2, one(0126400), one(0177760), "RsHs", mcfmac },
+{"movel", 6, one(0126474), one(0177777), "#lHs", mcfmac },
+{"movel", 2, one(0124700), one(0177777), "GsCs", mcfmac },
+
+{"movel", 2, one(0xa180), one(0xf9f0), "eFRs", mcfemac }, /* ACCx,Rx. */
+{"movel", 2, one(0xab80), one(0xfbf0), "g]Rs", mcfemac }, /* ACCEXTx,Rx. */
+{"movel", 2, one(0xa980), one(0xfff0), "G-Rs", mcfemac }, /* macsr,Rx. */
+{"movel", 2, one(0xad80), one(0xfff0), "H-Rs", mcfemac }, /* mask,Rx. */
+{"movel", 2, one(0xa110), one(0xf9fc), "efeF", mcfemac }, /* ACCy,ACCx. */
+{"movel", 2, one(0xa9c0), one(0xffff), "G-C-", mcfemac }, /* macsr,ccr. */
+{"movel", 2, one(0xa100), one(0xf9f0), "RseF", mcfemac }, /* Rx,ACCx. */
+{"movel", 6, one(0xa13c), one(0xf9ff), "#leF", mcfemac }, /* #,ACCx. */
+{"movel", 2, one(0xab00), one(0xfbc0), "Rsg]", mcfemac }, /* Rx,ACCEXTx. */
+{"movel", 6, one(0xab3c), one(0xfbff), "#lg]", mcfemac }, /* #,ACCEXTx. */
+{"movel", 2, one(0xa900), one(0xffc0), "RsG-", mcfemac }, /* Rx,macsr. */
+{"movel", 6, one(0xa93c), one(0xffff), "#lG-", mcfemac }, /* #,macsr. */
+{"movel", 2, one(0xad00), one(0xffc0), "RsH-", mcfemac }, /* Rx,mask. */
+{"movel", 6, one(0xad3c), one(0xffff), "#lH-", mcfemac }, /* #,mask. */
+
+{"move", 2, one(0030000), one(0170000), "*w%d", m68000up },
+{"move", 2, one(0030000), one(0170000), "ms%d", mcfisa_a },
+{"move", 2, one(0030000), one(0170000), "nspd", mcfisa_a },
+{"move", 2, one(0030000), one(0170000), "owmd", mcfisa_a },
+{"move", 2, one(0030000), one(0170000), "ownd", mcfisa_b },
+{"move", 2, one(0040300), one(0177700), "Ss$s", m68000up },
+{"move", 2, one(0040300), one(0177770), "SsDs", mcfisa_a },
+{"move", 2, one(0041300), one(0177700), "Cs$s", m68010up },
+{"move", 2, one(0041300), one(0177770), "CsDs", mcfisa_a },
+{"move", 2, one(0042300), one(0177700), ";wCd", m68000up },
+{"move", 2, one(0042300), one(0177700), "DsCd", mcfisa_a },
+{"move", 4, one(0042374), one(0177777), "#wCd", mcfisa_a },
+{"move", 2, one(0043300), one(0177700), ";wSd", m68000up },
+{"move", 2, one(0043300), one(0177700), "DsSd", mcfisa_a },
+{"move", 4, one(0043374), one(0177777), "#wSd", mcfisa_a },
+
+{"move", 2, one(0047140), one(0177770), "AsUd", m68000up },
+{"move", 2, one(0047150), one(0177770), "UdAs", m68000up },
+
+{"mov3ql", 2, one(0120500), one(0170700), "xd%s", mcfisa_b },
+{"mvsb", 2, one(0070400), one(0170700), "*bDd", mcfisa_b },
+{"mvsw", 2, one(0070500), one(0170700), "*wDd", mcfisa_b },
+{"mvzb", 2, one(0070600), one(0170700), "*bDd", mcfisa_b },
+{"mvzw", 2, one(0070700), one(0170700), "*wDd", mcfisa_b },
+
+{"movesb", 4, two(0007000, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movesb", 4, two(0007000, 04000), two(0177700, 07777), "R1~s", m68010up },
+{"movesw", 4, two(0007100, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movesw", 4, two(0007100, 04000), two(0177700, 07777), "R1~s", m68010up },
+{"movesl", 4, two(0007200, 0), two(0177700, 07777), "~sR1", m68010up },
+{"movesl", 4, two(0007200, 04000), two(0177700, 07777), "R1~s", m68010up },
+
+{"move16", 4, two(0xf620, 0x8000), two(0xfff8, 0x8fff), "+s+1", m68040up },
+{"move16", 2, one(0xf600), one(0xfff8), "+s_L", m68040up },
+{"move16", 2, one(0xf608), one(0xfff8), "_L+s", m68040up },
+{"move16", 2, one(0xf610), one(0xfff8), "as_L", m68040up },
+{"move16", 2, one(0xf618), one(0xfff8), "_Las", m68040up },
+
+{"msacw", 4, two(0xa080, 0x0100), two(0xf180, 0x0910), "uNuoiI4/Rn", mcfmac },
+{"msacw", 4, two(0xa080, 0x0300), two(0xf180, 0x0910), "uNuoMh4/Rn", mcfmac },
+{"msacw", 4, two(0xa080, 0x0100), two(0xf180, 0x0f10), "uNuo4/Rn", mcfmac },
+{"msacw", 4, two(0xa000, 0x0100), two(0xf1b0, 0x0900), "uMumiI", mcfmac },
+{"msacw", 4, two(0xa000, 0x0300), two(0xf1b0, 0x0900), "uMumMh", mcfmac },
+{"msacw", 4, two(0xa000, 0x0100), two(0xf1b0, 0x0f00), "uMum", mcfmac },
+
+{"msacw", 4, two(0xa000, 0x0100), two(0xf100, 0x0900), "uMumiI4/RneG", mcfemac },/* Ry,Rx,SF,<ea>,accX. */
+{"msacw", 4, two(0xa000, 0x0300), two(0xf100, 0x0900), "uMumMh4/RneG", mcfemac },/* Ry,Rx,+1/-1,<ea>,accX. */
+{"msacw", 4, two(0xa000, 0x0100), two(0xf100, 0x0f00), "uMum4/RneG", mcfemac },/* Ry,Rx,<ea>,accX. */
+{"msacw", 4, two(0xa000, 0x0100), two(0xf130, 0x0900), "uMumiIeH", mcfemac },/* Ry,Rx,SF,accX. */
+{"msacw", 4, two(0xa000, 0x0300), two(0xf130, 0x0900), "uMumMheH", mcfemac },/* Ry,Rx,+1/-1,accX. */
+{"msacw", 4, two(0xa000, 0x0100), two(0xf130, 0x0f00), "uMumeH", mcfemac }, /* Ry,Rx,accX. */
+
+{"msacl", 4, two(0xa080, 0x0900), two(0xf180, 0x0910), "RNRoiI4/Rn", mcfmac },
+{"msacl", 4, two(0xa080, 0x0b00), two(0xf180, 0x0910), "RNRoMh4/Rn", mcfmac },
+{"msacl", 4, two(0xa080, 0x0900), two(0xf180, 0x0f10), "RNRo4/Rn", mcfmac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf1b0, 0x0b00), "RMRmiI", mcfmac },
+{"msacl", 4, two(0xa000, 0x0b00), two(0xf1b0, 0x0b00), "RMRmMh", mcfmac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf1b0, 0x0800), "RMRm", mcfmac },
+
+{"msacl", 4, two(0xa000, 0x0900), two(0xf100, 0x0900), "R3R1iI4/RneG", mcfemac },
+{"msacl", 4, two(0xa000, 0x0b00), two(0xf100, 0x0900), "R3R1Mh4/RneG", mcfemac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf100, 0x0f00), "R3R14/RneG", mcfemac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf130, 0x0900), "RMRmiIeH", mcfemac },
+{"msacl", 4, two(0xa000, 0x0b00), two(0xf130, 0x0900), "RMRmMheH", mcfemac },
+{"msacl", 4, two(0xa000, 0x0900), two(0xf130, 0x0f00), "RMRmeH", mcfemac },
+
+{"mulsw", 2, one(0140700), one(0170700), ";wDd", m68000up|mcfisa_a },
+{"mulsl", 4, two(0046000,004000), two(0177700,0107770), ";lD1", m68020up|cpu32 },
+{"mulsl", 4, two(0046000,004000), two(0177700,0107770), "qsD1", mcfisa_a },
+{"mulsl", 4, two(0046000,006000), two(0177700,0107770), ";lD3D1",m68020up|cpu32 },
+
+{"muluw", 2, one(0140300), one(0170700), ";wDd", m68000up|mcfisa_a },
+{"mulul", 4, two(0046000,000000), two(0177700,0107770), ";lD1", m68020up|cpu32 },
+{"mulul", 4, two(0046000,000000), two(0177700,0107770), "qsD1", mcfisa_a },
+{"mulul", 4, two(0046000,002000), two(0177700,0107770), ";lD3D1",m68020up|cpu32 },
+
+{"nbcd", 2, one(0044000), one(0177700), "$s", m68000up },
+
+{"negb", 2, one(0042000), one(0177700), "$s", m68000up },
+{"negw", 2, one(0042100), one(0177700), "$s", m68000up },
+{"negl", 2, one(0042200), one(0177700), "$s", m68000up },
+{"negl", 2, one(0042200), one(0177700), "Ds", mcfisa_a},
+
+{"negxb", 2, one(0040000), one(0177700), "$s", m68000up },
+{"negxw", 2, one(0040100), one(0177700), "$s", m68000up },
+{"negxl", 2, one(0040200), one(0177700), "$s", m68000up },
+{"negxl", 2, one(0040200), one(0177700), "Ds", mcfisa_a},
+
+{"nop", 2, one(0047161), one(0177777), "", m68000up | mcfisa_a},
+
+{"notb", 2, one(0043000), one(0177700), "$s", m68000up },
+{"notw", 2, one(0043100), one(0177700), "$s", m68000up },
+{"notl", 2, one(0043200), one(0177700), "$s", m68000up },
+{"notl", 2, one(0043200), one(0177700), "Ds", mcfisa_a},
+
+{"orib", 4, one(0000000), one(0177700), "#b$s", m68000up },
+{"orib", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"oriw", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"oriw", 4, one(0000174), one(0177777), "#wSs", m68000up },
+{"oril", 6, one(0000200), one(0177700), "#l$s", m68000up },
+{"oril", 6, one(0000200), one(0177700), "#lDs", mcfisa_a },
+{"ori", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"ori", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"ori", 4, one(0000174), one(0177777), "#wSs", m68000up },
+
+/* The or opcode can generate the ori instruction. */
+{"orb", 4, one(0000000), one(0177700), "#b$s", m68000up },
+{"orb", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"orb", 2, one(0100000), one(0170700), ";bDd", m68000up },
+{"orb", 2, one(0100400), one(0170700), "Dd~s", m68000up },
+{"orw", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"orw", 4, one(0000174), one(0177777), "#wSs", m68000up },
+{"orw", 2, one(0100100), one(0170700), ";wDd", m68000up },
+{"orw", 2, one(0100500), one(0170700), "Dd~s", m68000up },
+{"orl", 6, one(0000200), one(0177700), "#l$s", m68000up },
+{"orl", 6, one(0000200), one(0177700), "#lDs", mcfisa_a },
+{"orl", 2, one(0100200), one(0170700), ";lDd", m68000up | mcfisa_a },
+{"orl", 2, one(0100600), one(0170700), "Dd~s", m68000up | mcfisa_a },
+{"or", 4, one(0000074), one(0177777), "#bCs", m68000up },
+{"or", 4, one(0000100), one(0177700), "#w$s", m68000up },
+{"or", 4, one(0000174), one(0177777), "#wSs", m68000up },
+{"or", 2, one(0100100), one(0170700), ";wDd", m68000up },
+{"or", 2, one(0100500), one(0170700), "Dd~s", m68000up },
+
+{"pack", 4, one(0100500), one(0170770), "DsDd#w", m68020up },
+{"pack", 4, one(0100510), one(0170770), "-s-d#w", m68020up },
+
+{"pbac", 2, one(0xf087), one(0xffbf), "Bc", m68851 },
+{"pbacw", 2, one(0xf087), one(0xffff), "BW", m68851 },
+{"pbas", 2, one(0xf086), one(0xffbf), "Bc", m68851 },
+{"pbasw", 2, one(0xf086), one(0xffff), "BW", m68851 },
+{"pbbc", 2, one(0xf081), one(0xffbf), "Bc", m68851 },
+{"pbbcw", 2, one(0xf081), one(0xffff), "BW", m68851 },
+{"pbbs", 2, one(0xf080), one(0xffbf), "Bc", m68851 },
+{"pbbsw", 2, one(0xf080), one(0xffff), "BW", m68851 },
+{"pbcc", 2, one(0xf08f), one(0xffbf), "Bc", m68851 },
+{"pbccw", 2, one(0xf08f), one(0xffff), "BW", m68851 },
+{"pbcs", 2, one(0xf08e), one(0xffbf), "Bc", m68851 },
+{"pbcsw", 2, one(0xf08e), one(0xffff), "BW", m68851 },
+{"pbgc", 2, one(0xf08d), one(0xffbf), "Bc", m68851 },
+{"pbgcw", 2, one(0xf08d), one(0xffff), "BW", m68851 },
+{"pbgs", 2, one(0xf08c), one(0xffbf), "Bc", m68851 },
+{"pbgsw", 2, one(0xf08c), one(0xffff), "BW", m68851 },
+{"pbic", 2, one(0xf08b), one(0xffbf), "Bc", m68851 },
+{"pbicw", 2, one(0xf08b), one(0xffff), "BW", m68851 },
+{"pbis", 2, one(0xf08a), one(0xffbf), "Bc", m68851 },
+{"pbisw", 2, one(0xf08a), one(0xffff), "BW", m68851 },
+{"pblc", 2, one(0xf083), one(0xffbf), "Bc", m68851 },
+{"pblcw", 2, one(0xf083), one(0xffff), "BW", m68851 },
+{"pbls", 2, one(0xf082), one(0xffbf), "Bc", m68851 },
+{"pblsw", 2, one(0xf082), one(0xffff), "BW", m68851 },
+{"pbsc", 2, one(0xf085), one(0xffbf), "Bc", m68851 },
+{"pbscw", 2, one(0xf085), one(0xffff), "BW", m68851 },
+{"pbss", 2, one(0xf084), one(0xffbf), "Bc", m68851 },
+{"pbssw", 2, one(0xf084), one(0xffff), "BW", m68851 },
+{"pbwc", 2, one(0xf089), one(0xffbf), "Bc", m68851 },
+{"pbwcw", 2, one(0xf089), one(0xffff), "BW", m68851 },
+{"pbws", 2, one(0xf088), one(0xffbf), "Bc", m68851 },
+{"pbwsw", 2, one(0xf088), one(0xffff), "BW", m68851 },
+
+{"pdbac", 4, two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbas", 4, two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbbc", 4, two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbbs", 4, two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbcc", 4, two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbcs", 4, two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbgc", 4, two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbgs", 4, two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbic", 4, two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbis", 4, two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdblc", 4, two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbls", 4, two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbsc", 4, two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbss", 4, two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbwc", 4, two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw", m68851 },
+{"pdbws", 4, two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw", m68851 },
+
+{"pea", 2, one(0044100), one(0177700), "!s", m68000up|mcfisa_a },
+
+{"pflusha", 2, one(0xf518), one(0xfff8), "", m68040up },
+{"pflusha", 4, two(0xf000,0x2400), two(0xffff,0xffff), "", m68030 | m68851 },
+
+{"pflush", 4, two(0xf000,0x3010), two(0xffc0,0xfe10), "T3T9", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3810), two(0xffc0,0xfe10), "T3T9&s", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3008), two(0xffc0,0xfe18), "D3T9", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3808), two(0xffc0,0xfe18), "D3T9&s", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3000), two(0xffc0,0xfe1e), "f3T9", m68030|m68851 },
+{"pflush", 4, two(0xf000,0x3800), two(0xffc0,0xfe1e), "f3T9&s", m68030|m68851 },
+{"pflush", 2, one(0xf508), one(0xfff8), "as", m68040up },
+{"pflush", 2, one(0xf508), one(0xfff8), "As", m68040up },
+
+{"pflushan", 2, one(0xf510), one(0xfff8), "", m68040up },
+{"pflushn", 2, one(0xf500), one(0xfff8), "as", m68040up },
+{"pflushn", 2, one(0xf500), one(0xfff8), "As", m68040up },
+
+{"pflushr", 4, two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s", m68851 },
+
+{"pflushs", 4, two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9", m68851 },
+{"pflushs", 4, two(0xf000, 0x3c10), two(0xfff8, 0xfe10), "T3T9&s", m68851 },
+{"pflushs", 4, two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9", m68851 },
+{"pflushs", 4, two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s", m68851 },
+{"pflushs", 4, two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9", m68851 },
+{"pflushs", 4, two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s", m68851 },
+
+{"ploadr", 4, two(0xf000,0x2210), two(0xffc0,0xfff0), "T3&s", m68030|m68851 },
+{"ploadr", 4, two(0xf000,0x2208), two(0xffc0,0xfff8), "D3&s", m68030|m68851 },
+{"ploadr", 4, two(0xf000,0x2200), two(0xffc0,0xfffe), "f3&s", m68030|m68851 },
+{"ploadw", 4, two(0xf000,0x2010), two(0xffc0,0xfff0), "T3&s", m68030|m68851 },
+{"ploadw", 4, two(0xf000,0x2008), two(0xffc0,0xfff8), "D3&s", m68030|m68851 },
+{"ploadw", 4, two(0xf000,0x2000), two(0xffc0,0xfffe), "f3&s", m68030|m68851 },
+
+{"plpar", 2, one(0xf5c8), one(0xfff8), "as", m68060 },
+{"plpaw", 2, one(0xf588), one(0xfff8), "as", m68060 },
+
+{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xffff), "*l08", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x5c00), two(0xffc0,0xffff), "*w18", m68851 },
+{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xe3ff), "*b28", m68851 },
+{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xffff), "08%s", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x5e00), two(0xffc0,0xffff), "18%s", m68851 },
+{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xe3ff), "28%s", m68851 },
+{"pmove", 4, two(0xf000,0x4000), two(0xffc0,0xe3ff), "|sW8", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x4200), two(0xffc0,0xe3ff), "W8~s", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x6200), two(0xffc0,0xe3e3), "*wX3", m68851 },
+{"pmove", 4, two(0xf000,0x6000), two(0xffc0,0xe3e3), "X3%s", m68851 },
+{"pmove", 4, two(0xf000,0x6000), two(0xffc0,0xffff), "*wY8", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x6200), two(0xffc0,0xffff), "Y8%s", m68030|m68851 },
+{"pmove", 4, two(0xf000,0x6600), two(0xffc0,0xffff), "Z8%s", m68851 },
+{"pmove", 4, two(0xf000,0x0800), two(0xffc0,0xfbff), "*l38", m68030 },
+{"pmove", 4, two(0xf000,0x0a00), two(0xffc0,0xfbff), "38%s", m68030 },
+
+{"pmovefd", 4, two(0xf000, 0x4100), two(0xffc0, 0xe3ff), "*l08", m68030 },
+{"pmovefd", 4, two(0xf000, 0x4100), two(0xffc0, 0xe3ff), "|sW8", m68030 },
+{"pmovefd", 4, two(0xf000, 0x0900), two(0xffc0, 0xfbff), "*l38", m68030 },
+
+{"prestore", 2, one(0xf140), one(0xffc0), "<s", m68851 },
+
+{"psave", 2, one(0xf100), one(0xffc0), ">s", m68851 },
+
+{"psac", 4, two(0xf040, 0x0007), two(0xffc0, 0xffff), "$s", m68851 },
+{"psas", 4, two(0xf040, 0x0006), two(0xffc0, 0xffff), "$s", m68851 },
+{"psbc", 4, two(0xf040, 0x0001), two(0xffc0, 0xffff), "$s", m68851 },
+{"psbs", 4, two(0xf040, 0x0000), two(0xffc0, 0xffff), "$s", m68851 },
+{"pscc", 4, two(0xf040, 0x000f), two(0xffc0, 0xffff), "$s", m68851 },
+{"pscs", 4, two(0xf040, 0x000e), two(0xffc0, 0xffff), "$s", m68851 },
+{"psgc", 4, two(0xf040, 0x000d), two(0xffc0, 0xffff), "$s", m68851 },
+{"psgs", 4, two(0xf040, 0x000c), two(0xffc0, 0xffff), "$s", m68851 },
+{"psic", 4, two(0xf040, 0x000b), two(0xffc0, 0xffff), "$s", m68851 },
+{"psis", 4, two(0xf040, 0x000a), two(0xffc0, 0xffff), "$s", m68851 },
+{"pslc", 4, two(0xf040, 0x0003), two(0xffc0, 0xffff), "$s", m68851 },
+{"psls", 4, two(0xf040, 0x0002), two(0xffc0, 0xffff), "$s", m68851 },
+{"pssc", 4, two(0xf040, 0x0005), two(0xffc0, 0xffff), "$s", m68851 },
+{"psss", 4, two(0xf040, 0x0004), two(0xffc0, 0xffff), "$s", m68851 },
+{"pswc", 4, two(0xf040, 0x0009), two(0xffc0, 0xffff), "$s", m68851 },
+{"psws", 4, two(0xf040, 0x0008), two(0xffc0, 0xffff), "$s", m68851 },
+
+{"ptestr", 4, two(0xf000,0x8210), two(0xffc0, 0xe3f0), "T3&st8", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8310), two(0xffc0,0xe310), "T3&st8A9", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8208), two(0xffc0,0xe3f8), "D3&st8", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8308), two(0xffc0,0xe318), "D3&st8A9", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8200), two(0xffc0,0xe3fe), "f3&st8", m68030|m68851 },
+{"ptestr", 4, two(0xf000,0x8300), two(0xffc0,0xe31e), "f3&st8A9", m68030|m68851 },
+{"ptestr", 2, one(0xf568), one(0xfff8), "as", m68040 },
+
+{"ptestw", 4, two(0xf000,0x8010), two(0xffc0,0xe3f0), "T3&st8", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8110), two(0xffc0,0xe310), "T3&st8A9", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8008), two(0xffc0,0xe3f8), "D3&st8", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8108), two(0xffc0,0xe318), "D3&st8A9", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8000), two(0xffc0,0xe3fe), "f3&st8", m68030|m68851 },
+{"ptestw", 4, two(0xf000,0x8100), two(0xffc0,0xe31e), "f3&st8A9", m68030|m68851 },
+{"ptestw", 2, one(0xf548), one(0xfff8), "as", m68040 },
+
+{"ptrapacw", 6, two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapacl", 6, two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapac", 4, two(0xf07c, 0x0007), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapasw", 6, two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapasl", 6, two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapas", 4, two(0xf07c, 0x0006), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapbcw", 6, two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapbcl", 6, two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapbc", 4, two(0xf07c, 0x0001), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapbsw", 6, two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapbsl", 6, two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapbs", 4, two(0xf07c, 0x0000), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapccw", 6, two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapccl", 6, two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapcc", 4, two(0xf07c, 0x000f), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapcsw", 6, two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapcsl", 6, two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapcs", 4, two(0xf07c, 0x000e), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapgcw", 6, two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapgcl", 6, two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapgc", 4, two(0xf07c, 0x000d), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapgsw", 6, two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapgsl", 6, two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapgs", 4, two(0xf07c, 0x000c), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapicw", 6, two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapicl", 6, two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapic", 4, two(0xf07c, 0x000b), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapisw", 6, two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapisl", 6, two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapis", 4, two(0xf07c, 0x000a), two(0xffff, 0xffff), "", m68851 },
+
+{"ptraplcw", 6, two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w", m68851 },
+{"ptraplcl", 6, two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l", m68851 },
+{"ptraplc", 4, two(0xf07c, 0x0003), two(0xffff, 0xffff), "", m68851 },
+
+{"ptraplsw", 6, two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w", m68851 },
+{"ptraplsl", 6, two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapls", 4, two(0xf07c, 0x0002), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapscw", 6, two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapscl", 6, two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapsc", 4, two(0xf07c, 0x0005), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapssw", 6, two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapssl", 6, two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapss", 4, two(0xf07c, 0x0004), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapwcw", 6, two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapwcl", 6, two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapwc", 4, two(0xf07c, 0x0009), two(0xffff, 0xffff), "", m68851 },
+
+{"ptrapwsw", 6, two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w", m68851 },
+{"ptrapwsl", 6, two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l", m68851 },
+{"ptrapws", 4, two(0xf07c, 0x0008), two(0xffff, 0xffff), "", m68851 },
+
+{"pulse", 2, one(0045314), one(0177777), "", m68060 | mcfisa_a },
+
+{"pvalid", 4, two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s", m68851 },
+{"pvalid", 4, two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s", m68851 },
+
+ /* FIXME: don't allow Dw==Dx. */
+{"remsl", 4, two(0x4c40, 0x0800), two(0xffc0, 0x8ff8), "qsD3D1", mcfhwdiv },
+{"remul", 4, two(0x4c40, 0x0000), two(0xffc0, 0x8ff8), "qsD3D1", mcfhwdiv },
+
+{"reset", 2, one(0047160), one(0177777), "", m68000up },
+
+{"rolb", 2, one(0160430), one(0170770), "QdDs", m68000up },
+{"rolb", 2, one(0160470), one(0170770), "DdDs", m68000up },
+{"rolw", 2, one(0160530), one(0170770), "QdDs", m68000up },
+{"rolw", 2, one(0160570), one(0170770), "DdDs", m68000up },
+{"rolw", 2, one(0163700), one(0177700), "~s", m68000up },
+{"roll", 2, one(0160630), one(0170770), "QdDs", m68000up },
+{"roll", 2, one(0160670), one(0170770), "DdDs", m68000up },
+
+{"rorb", 2, one(0160030), one(0170770), "QdDs", m68000up },
+{"rorb", 2, one(0160070), one(0170770), "DdDs", m68000up },
+{"rorw", 2, one(0160130), one(0170770), "QdDs", m68000up },
+{"rorw", 2, one(0160170), one(0170770), "DdDs", m68000up },
+{"rorw", 2, one(0163300), one(0177700), "~s", m68000up },
+{"rorl", 2, one(0160230), one(0170770), "QdDs", m68000up },
+{"rorl", 2, one(0160270), one(0170770), "DdDs", m68000up },
+
+{"roxlb", 2, one(0160420), one(0170770), "QdDs", m68000up },
+{"roxlb", 2, one(0160460), one(0170770), "DdDs", m68000up },
+{"roxlw", 2, one(0160520), one(0170770), "QdDs", m68000up },
+{"roxlw", 2, one(0160560), one(0170770), "DdDs", m68000up },
+{"roxlw", 2, one(0162700), one(0177700), "~s", m68000up },
+{"roxll", 2, one(0160620), one(0170770), "QdDs", m68000up },
+{"roxll", 2, one(0160660), one(0170770), "DdDs", m68000up },
+
+{"roxrb", 2, one(0160020), one(0170770), "QdDs", m68000up },
+{"roxrb", 2, one(0160060), one(0170770), "DdDs", m68000up },
+{"roxrw", 2, one(0160120), one(0170770), "QdDs", m68000up },
+{"roxrw", 2, one(0160160), one(0170770), "DdDs", m68000up },
+{"roxrw", 2, one(0162300), one(0177700), "~s", m68000up },
+{"roxrl", 2, one(0160220), one(0170770), "QdDs", m68000up },
+{"roxrl", 2, one(0160260), one(0170770), "DdDs", m68000up },
+
+{"rtd", 4, one(0047164), one(0177777), "#w", m68010up },
+
+{"rte", 2, one(0047163), one(0177777), "", m68000up | mcfisa_a },
+
+{"rtm", 2, one(0003300), one(0177760), "Rs", m68020 },
+
+{"rtr", 2, one(0047167), one(0177777), "", m68000up },
+
+{"rts", 2, one(0047165), one(0177777), "", m68000up | mcfisa_a },
+
+{"satsl", 2, one(0046200), one(0177770), "Ds", mcfisa_b },
+
+{"sbcd", 2, one(0100400), one(0170770), "DsDd", m68000up },
+{"sbcd", 2, one(0100410), one(0170770), "-s-d", m68000up },
+
+{"scc", 2, one(0052300), one(0177700), "$s", m68000up },
+{"scc", 2, one(0052300), one(0177700), "Ds", mcfisa_a },
+{"scs", 2, one(0052700), one(0177700), "$s", m68000up },
+{"scs", 2, one(0052700), one(0177700), "Ds", mcfisa_a },
+{"seq", 2, one(0053700), one(0177700), "$s", m68000up },
+{"seq", 2, one(0053700), one(0177700), "Ds", mcfisa_a },
+{"sf", 2, one(0050700), one(0177700), "$s", m68000up },
+{"sf", 2, one(0050700), one(0177700), "Ds", mcfisa_a },
+{"sge", 2, one(0056300), one(0177700), "$s", m68000up },
+{"sge", 2, one(0056300), one(0177700), "Ds", mcfisa_a },
+{"sgt", 2, one(0057300), one(0177700), "$s", m68000up },
+{"sgt", 2, one(0057300), one(0177700), "Ds", mcfisa_a },
+{"shi", 2, one(0051300), one(0177700), "$s", m68000up },
+{"shi", 2, one(0051300), one(0177700), "Ds", mcfisa_a },
+{"sle", 2, one(0057700), one(0177700), "$s", m68000up },
+{"sle", 2, one(0057700), one(0177700), "Ds", mcfisa_a },
+{"sls", 2, one(0051700), one(0177700), "$s", m68000up },
+{"sls", 2, one(0051700), one(0177700), "Ds", mcfisa_a },
+{"slt", 2, one(0056700), one(0177700), "$s", m68000up },
+{"slt", 2, one(0056700), one(0177700), "Ds", mcfisa_a },
+{"smi", 2, one(0055700), one(0177700), "$s", m68000up },
+{"smi", 2, one(0055700), one(0177700), "Ds", mcfisa_a },
+{"sne", 2, one(0053300), one(0177700), "$s", m68000up },
+{"sne", 2, one(0053300), one(0177700), "Ds", mcfisa_a },
+{"spl", 2, one(0055300), one(0177700), "$s", m68000up },
+{"spl", 2, one(0055300), one(0177700), "Ds", mcfisa_a },
+{"st", 2, one(0050300), one(0177700), "$s", m68000up },
+{"st", 2, one(0050300), one(0177700), "Ds", mcfisa_a },
+{"svc", 2, one(0054300), one(0177700), "$s", m68000up },
+{"svc", 2, one(0054300), one(0177700), "Ds", mcfisa_a },
+{"svs", 2, one(0054700), one(0177700), "$s", m68000up },
+{"svs", 2, one(0054700), one(0177700), "Ds", mcfisa_a },
+
+{"stop", 4, one(0047162), one(0177777), "#w", m68000up | mcfisa_a },
+
+{"strldsr", 4, two(0040347,0043374), two(0177777,0177777), "#w", mcfisa_aa},
+
+{"subal", 2, one(0110700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"subaw", 2, one(0110300), one(0170700), "*wAd", m68000up },
+
+{"subib", 4, one(0002000), one(0177700), "#b$s", m68000up },
+{"subiw", 4, one(0002100), one(0177700), "#w$s", m68000up },
+{"subil", 6, one(0002200), one(0177700), "#l$s", m68000up },
+{"subil", 6, one(0002200), one(0177700), "#lDs", mcfisa_a },
+
+{"subqb", 2, one(0050400), one(0170700), "Qd%s", m68000up },
+{"subqw", 2, one(0050500), one(0170700), "Qd%s", m68000up },
+{"subql", 2, one(0050600), one(0170700), "Qd%s", m68000up | mcfisa_a },
+
+/* The sub opcode can generate the suba, subi, and subq instructions. */
+{"subb", 2, one(0050400), one(0170700), "Qd%s", m68000up },
+{"subb", 4, one(0002000), one(0177700), "#b$s", m68000up },
+{"subb", 2, one(0110000), one(0170700), ";bDd", m68000up },
+{"subb", 2, one(0110400), one(0170700), "Dd~s", m68000up },
+{"subw", 2, one(0050500), one(0170700), "Qd%s", m68000up },
+{"subw", 4, one(0002100), one(0177700), "#w$s", m68000up },
+{"subw", 2, one(0110300), one(0170700), "*wAd", m68000up },
+{"subw", 2, one(0110100), one(0170700), "*wDd", m68000up },
+{"subw", 2, one(0110500), one(0170700), "Dd~s", m68000up },
+{"subl", 2, one(0050600), one(0170700), "Qd%s", m68000up | mcfisa_a },
+{"subl", 6, one(0002200), one(0177700), "#l$s", m68000up },
+{"subl", 6, one(0002200), one(0177700), "#lDs", mcfisa_a },
+{"subl", 2, one(0110700), one(0170700), "*lAd", m68000up | mcfisa_a },
+{"subl", 2, one(0110200), one(0170700), "*lDd", m68000up | mcfisa_a },
+{"subl", 2, one(0110600), one(0170700), "Dd~s", m68000up | mcfisa_a },
+
+{"subxb", 2, one(0110400), one(0170770), "DsDd", m68000up },
+{"subxb", 2, one(0110410), one(0170770), "-s-d", m68000up },
+{"subxw", 2, one(0110500), one(0170770), "DsDd", m68000up },
+{"subxw", 2, one(0110510), one(0170770), "-s-d", m68000up },
+{"subxl", 2, one(0110600), one(0170770), "DsDd", m68000up | mcfisa_a },
+{"subxl", 2, one(0110610), one(0170770), "-s-d", m68000up },
+
+{"swap", 2, one(0044100), one(0177770), "Ds", m68000up | mcfisa_a },
+
+/* swbeg and swbegl are magic constants used on sysV68. The compiler
+ generates them before a switch table. They tell the debugger and
+ disassembler that a switch table follows. The parameter is the
+ number of elements in the table. swbeg means that the entries in
+ the table are word (2 byte) sized, and swbegl means that the
+ entries in the table are longword (4 byte) sized. */
+{"swbeg", 4, one(0045374), one(0177777), "#w", m68000up | mcfisa_a },
+{"swbegl", 6, one(0045375), one(0177777), "#l", m68000up | mcfisa_a },
+
+{"tas", 2, one(0045300), one(0177700), "$s", m68000up | mcfisa_b},
+
+#define TBL1(name,insn_size,signed,round,size) \
+ {name, insn_size, two(0174000, (signed<<11)|(!round<<10)|(size<<6)|0000400), \
+ two(0177700,0107777), "!sD1", cpu32 }, \
+ {name, insn_size, two(0174000, (signed<<11)|(!round<<10)|(size<<6)), \
+ two(0177770,0107770), "DsD3D1", cpu32 }
+#define TBL(name1, name2, name3, s, r) \
+ TBL1(name1, 4, s, r, 0), TBL1(name2, 4, s, r, 1), TBL1(name3, 4, s, r, 2)
+TBL("tblsb", "tblsw", "tblsl", 2, 1),
+TBL("tblsnb", "tblsnw", "tblsnl", 2, 0),
+TBL("tblub", "tbluw", "tblul", 0, 1),
+TBL("tblunb", "tblunw", "tblunl", 0, 0),
+
+{"trap", 2, one(0047100), one(0177760), "Ts", m68000up | mcfisa_a },
+
+{"trapcc", 2, one(0052374), one(0177777), "", m68020up | cpu32 },
+{"trapcs", 2, one(0052774), one(0177777), "", m68020up | cpu32 },
+{"trapeq", 2, one(0053774), one(0177777), "", m68020up | cpu32 },
+{"trapf", 2, one(0050774), one(0177777), "", m68020up | cpu32 | mcfisa_a },
+{"trapge", 2, one(0056374), one(0177777), "", m68020up | cpu32 },
+{"trapgt", 2, one(0057374), one(0177777), "", m68020up | cpu32 },
+{"traphi", 2, one(0051374), one(0177777), "", m68020up | cpu32 },
+{"traple", 2, one(0057774), one(0177777), "", m68020up | cpu32 },
+{"trapls", 2, one(0051774), one(0177777), "", m68020up | cpu32 },
+{"traplt", 2, one(0056774), one(0177777), "", m68020up | cpu32 },
+{"trapmi", 2, one(0055774), one(0177777), "", m68020up | cpu32 },
+{"trapne", 2, one(0053374), one(0177777), "", m68020up | cpu32 },
+{"trappl", 2, one(0055374), one(0177777), "", m68020up | cpu32 },
+{"trapt", 2, one(0050374), one(0177777), "", m68020up | cpu32 },
+{"trapvc", 2, one(0054374), one(0177777), "", m68020up | cpu32 },
+{"trapvs", 2, one(0054774), one(0177777), "", m68020up | cpu32 },
+
+{"trapccw", 4, one(0052372), one(0177777), "#w", m68020up|cpu32 },
+{"trapcsw", 4, one(0052772), one(0177777), "#w", m68020up|cpu32 },
+{"trapeqw", 4, one(0053772), one(0177777), "#w", m68020up|cpu32 },
+{"trapfw", 4, one(0050772), one(0177777), "#w", m68020up|cpu32|mcfisa_a},
+{"trapgew", 4, one(0056372), one(0177777), "#w", m68020up|cpu32 },
+{"trapgtw", 4, one(0057372), one(0177777), "#w", m68020up|cpu32 },
+{"traphiw", 4, one(0051372), one(0177777), "#w", m68020up|cpu32 },
+{"traplew", 4, one(0057772), one(0177777), "#w", m68020up|cpu32 },
+{"traplsw", 4, one(0051772), one(0177777), "#w", m68020up|cpu32 },
+{"trapltw", 4, one(0056772), one(0177777), "#w", m68020up|cpu32 },
+{"trapmiw", 4, one(0055772), one(0177777), "#w", m68020up|cpu32 },
+{"trapnew", 4, one(0053372), one(0177777), "#w", m68020up|cpu32 },
+{"trapplw", 4, one(0055372), one(0177777), "#w", m68020up|cpu32 },
+{"traptw", 4, one(0050372), one(0177777), "#w", m68020up|cpu32 },
+{"trapvcw", 4, one(0054372), one(0177777), "#w", m68020up|cpu32 },
+{"trapvsw", 4, one(0054772), one(0177777), "#w", m68020up|cpu32 },
+
+{"trapccl", 6, one(0052373), one(0177777), "#l", m68020up|cpu32 },
+{"trapcsl", 6, one(0052773), one(0177777), "#l", m68020up|cpu32 },
+{"trapeql", 6, one(0053773), one(0177777), "#l", m68020up|cpu32 },
+{"trapfl", 6, one(0050773), one(0177777), "#l", m68020up|cpu32|mcfisa_a},
+{"trapgel", 6, one(0056373), one(0177777), "#l", m68020up|cpu32 },
+{"trapgtl", 6, one(0057373), one(0177777), "#l", m68020up|cpu32 },
+{"traphil", 6, one(0051373), one(0177777), "#l", m68020up|cpu32 },
+{"traplel", 6, one(0057773), one(0177777), "#l", m68020up|cpu32 },
+{"traplsl", 6, one(0051773), one(0177777), "#l", m68020up|cpu32 },
+{"trapltl", 6, one(0056773), one(0177777), "#l", m68020up|cpu32 },
+{"trapmil", 6, one(0055773), one(0177777), "#l", m68020up|cpu32 },
+{"trapnel", 6, one(0053373), one(0177777), "#l", m68020up|cpu32 },
+{"trappll", 6, one(0055373), one(0177777), "#l", m68020up|cpu32 },
+{"traptl", 6, one(0050373), one(0177777), "#l", m68020up|cpu32 },
+{"trapvcl", 6, one(0054373), one(0177777), "#l", m68020up|cpu32 },
+{"trapvsl", 6, one(0054773), one(0177777), "#l", m68020up|cpu32 },
+
+{"trapv", 2, one(0047166), one(0177777), "", m68000up },
+
+{"tstb", 2, one(0045000), one(0177700), ";b", m68020up|cpu32|mcfisa_a },
+{"tstb", 2, one(0045000), one(0177700), "$b", m68000up },
+{"tstw", 2, one(0045100), one(0177700), "*w", m68020up|cpu32|mcfisa_a },
+{"tstw", 2, one(0045100), one(0177700), "$w", m68000up },
+{"tstl", 2, one(0045200), one(0177700), "*l", m68020up|cpu32|mcfisa_a },
+{"tstl", 2, one(0045200), one(0177700), "$l", m68000up },
+
+{"unlk", 2, one(0047130), one(0177770), "As", m68000up | mcfisa_a },
+
+{"unpk", 4, one(0100600), one(0170770), "DsDd#w", m68020up },
+{"unpk", 4, one(0100610), one(0170770), "-s-d#w", m68020up },
+
+{"wddatab", 2, one(0175400), one(0177700), "~s", mcfisa_a },
+{"wddataw", 2, one(0175500), one(0177700), "~s", mcfisa_a },
+{"wddatal", 2, one(0175600), one(0177700), "~s", mcfisa_a },
+
+{"wdebug", 4, two(0175720, 03), two(0177770, 0xffff), "as", mcfisa_a },
+{"wdebug", 4, two(0175750, 03), two(0177770, 0xffff), "ds", mcfisa_a },
+};
+
+const int m68k_numopcodes = sizeof m68k_opcodes / sizeof m68k_opcodes[0];
+
+/* These aliases used to be in the above table, each one duplicating
+ all of the entries for its primary exactly. This table was
+ constructed by mechanical processing of the opcode table, with a
+ small number of tweaks done by hand. There are probably a lot more
+ aliases above that could be moved down here, except for very minor
+ differences. */
+
+const struct m68k_opcode_alias m68k_opcode_aliases[] =
+{
+ { "add", "addw", },
+ { "adda", "addaw", },
+ { "addi", "addiw", },
+ { "addq", "addqw", },
+ { "addx", "addxw", },
+ { "asl", "aslw", },
+ { "asr", "asrw", },
+ { "bhi", "bhiw", },
+ { "bls", "blsw", },
+ { "bcc", "bccw", },
+ { "bcs", "bcsw", },
+ { "bne", "bnew", },
+ { "beq", "beqw", },
+ { "bvc", "bvcw", },
+ { "bvs", "bvsw", },
+ { "bpl", "bplw", },
+ { "bmi", "bmiw", },
+ { "bge", "bgew", },
+ { "blt", "bltw", },
+ { "bgt", "bgtw", },
+ { "ble", "blew", },
+ { "bra", "braw", },
+ { "bsr", "bsrw", },
+ { "bhib", "bhis", },
+ { "blsb", "blss", },
+ { "bccb", "bccs", },
+ { "bcsb", "bcss", },
+ { "bneb", "bnes", },
+ { "beqb", "beqs", },
+ { "bvcb", "bvcs", },
+ { "bvsb", "bvss", },
+ { "bplb", "bpls", },
+ { "bmib", "bmis", },
+ { "bgeb", "bges", },
+ { "bltb", "blts", },
+ { "bgtb", "bgts", },
+ { "bleb", "bles", },
+ { "brab", "bras", },
+ { "bsrb", "bsrs", },
+ { "bhs", "bccw" },
+ { "bhss", "bccs" },
+ { "bhsb", "bccs" },
+ { "bhsw", "bccw" },
+ { "bhsl", "bccl" },
+ { "blo", "bcsw" },
+ { "blos", "bcss" },
+ { "blob", "bcss" },
+ { "blow", "bcsw" },
+ { "blol", "bcsl" },
+ { "br", "braw", },
+ { "brs", "bras", },
+ { "brb", "bras", },
+ { "brw", "braw", },
+ { "brl", "bral", },
+ { "jfnlt", "bcc", }, /* Apparently a sun alias. */
+ { "jfngt", "ble", }, /* Apparently a sun alias. */
+ { "jfeq", "beqs", }, /* Apparently a sun alias. */
+ { "bchgb", "bchg", },
+ { "bchgl", "bchg", },
+ { "bclrb", "bclr", },
+ { "bclrl", "bclr", },
+ { "bsetb", "bset", },
+ { "bsetl", "bset", },
+ { "btstb", "btst", },
+ { "btstl", "btst", },
+ { "cas2", "cas2w", },
+ { "cas", "casw", },
+ { "chk2", "chk2w", },
+ { "chk", "chkw", },
+ { "clr", "clrw", },
+ { "cmp2", "cmp2w", },
+ { "cmpa", "cmpaw", },
+ { "cmpi", "cmpiw", },
+ { "cmpm", "cmpmw", },
+ { "cmp", "cmpw", },
+ { "dbccw", "dbcc", },
+ { "dbcsw", "dbcs", },
+ { "dbeqw", "dbeq", },
+ { "dbfw", "dbf", },
+ { "dbgew", "dbge", },
+ { "dbgtw", "dbgt", },
+ { "dbhiw", "dbhi", },
+ { "dblew", "dble", },
+ { "dblsw", "dbls", },
+ { "dbltw", "dblt", },
+ { "dbmiw", "dbmi", },
+ { "dbnew", "dbne", },
+ { "dbplw", "dbpl", },
+ { "dbtw", "dbt", },
+ { "dbvcw", "dbvc", },
+ { "dbvsw", "dbvs", },
+ { "dbhs", "dbcc", },
+ { "dbhsw", "dbcc", },
+ { "dbra", "dbf", },
+ { "dbraw", "dbf", },
+ { "tdivsl", "divsl", },
+ { "divs", "divsw", },
+ { "divu", "divuw", },
+ { "ext", "extw", },
+ { "extbw", "extw", },
+ { "extwl", "extl", },
+ { "fbneq", "fbne", },
+ { "fbsneq", "fbsne", },
+ { "fdbneq", "fdbne", },
+ { "fdbsneq", "fdbsne", },
+ { "fmovecr", "fmovecrx", },
+ { "fmovm", "fmovem", },
+ { "fsneq", "fsne", },
+ { "fssneq", "fssne", },
+ { "ftrapneq", "ftrapne", },
+ { "ftrapsneq", "ftrapsne", },
+ { "fjneq", "fjne", },
+ { "fjsneq", "fjsne", },
+ { "jmpl", "jmp", },
+ { "jmps", "jmp", },
+ { "jsrl", "jsr", },
+ { "jsrs", "jsr", },
+ { "leal", "lea", },
+ { "lsl", "lslw", },
+ { "lsr", "lsrw", },
+ { "mac", "macw" },
+ { "movea", "moveaw", },
+ { "movem", "movemw", },
+ { "movml", "moveml", },
+ { "movmw", "movemw", },
+ { "movm", "movemw", },
+ { "movep", "movepw", },
+ { "movpw", "movepw", },
+ { "moves", "movesw" },
+ { "muls", "mulsw", },
+ { "mulu", "muluw", },
+ { "msac", "msacw" },
+ { "nbcdb", "nbcd" },
+ { "neg", "negw", },
+ { "negx", "negxw", },
+ { "not", "notw", },
+ { "peal", "pea", },
+ { "rol", "rolw", },
+ { "ror", "rorw", },
+ { "roxl", "roxlw", },
+ { "roxr", "roxrw", },
+ { "sats", "satsl", },
+ { "sbcdb", "sbcd", },
+ { "sccb", "scc", },
+ { "scsb", "scs", },
+ { "seqb", "seq", },
+ { "sfb", "sf", },
+ { "sgeb", "sge", },
+ { "sgtb", "sgt", },
+ { "shib", "shi", },
+ { "sleb", "sle", },
+ { "slsb", "sls", },
+ { "sltb", "slt", },
+ { "smib", "smi", },
+ { "sneb", "sne", },
+ { "splb", "spl", },
+ { "stb", "st", },
+ { "svcb", "svc", },
+ { "svsb", "svs", },
+ { "sfge", "sge", },
+ { "sfgt", "sgt", },
+ { "sfle", "sle", },
+ { "sflt", "slt", },
+ { "sfneq", "sne", },
+ { "suba", "subaw", },
+ { "subi", "subiw", },
+ { "subq", "subqw", },
+ { "sub", "subw", },
+ { "subx", "subxw", },
+ { "swapw", "swap", },
+ { "tasb", "tas", },
+ { "tpcc", "trapcc", },
+ { "tcc", "trapcc", },
+ { "tst", "tstw", },
+ { "jbra", "jra", },
+ { "jbhi", "jhi", },
+ { "jbls", "jls", },
+ { "jbcc", "jcc", },
+ { "jbcs", "jcs", },
+ { "jbne", "jne", },
+ { "jbeq", "jeq", },
+ { "jbvc", "jvc", },
+ { "jbvs", "jvs", },
+ { "jbpl", "jpl", },
+ { "jbmi", "jmi", },
+ { "jbge", "jge", },
+ { "jblt", "jlt", },
+ { "jbgt", "jgt", },
+ { "jble", "jle", },
+ { "movql", "moveq", },
+ { "moveql", "moveq", },
+ { "movl", "movel", },
+ { "movq", "moveq", },
+ { "moval", "moveal", },
+ { "movaw", "moveaw", },
+ { "movb", "moveb", },
+ { "movc", "movec", },
+ { "movecl", "movec", },
+ { "movpl", "movepl", },
+ { "movw", "movew", },
+ { "movsb", "movesb", },
+ { "movsl", "movesl", },
+ { "movsw", "movesw", },
+ { "mov3q", "mov3ql", },
+
+ { "tdivul", "divul", }, /* For m68k-svr4. */
+ { "fmovb", "fmoveb", },
+ { "fsmovb", "fsmoveb", },
+ { "fdmovb", "fdmoveb", },
+ { "fmovd", "fmoved", },
+ { "fsmovd", "fsmoved", },
+ { "fmovl", "fmovel", },
+ { "fsmovl", "fsmovel", },
+ { "fdmovl", "fdmovel", },
+ { "fmovp", "fmovep", },
+ { "fsmovp", "fsmovep", },
+ { "fdmovp", "fdmovep", },
+ { "fmovs", "fmoves", },
+ { "fsmovs", "fsmoves", },
+ { "fdmovs", "fdmoves", },
+ { "fmovw", "fmovew", },
+ { "fsmovw", "fsmovew", },
+ { "fdmovw", "fdmovew", },
+ { "fmovx", "fmovex", },
+ { "fsmovx", "fsmovex", },
+ { "fdmovx", "fdmovex", },
+ { "fmovcr", "fmovecr", },
+ { "fmovcrx", "fmovecrx", },
+ { "ftestb", "ftstb", },
+ { "ftestd", "ftstd", },
+ { "ftestl", "ftstl", },
+ { "ftestp", "ftstp", },
+ { "ftests", "ftsts", },
+ { "ftestw", "ftstw", },
+ { "ftestx", "ftstx", },
+
+ { "bitrevl", "bitrev", },
+ { "byterevl", "byterev", },
+ { "ff1l", "ff1", },
+
+};
+
+const int m68k_numaliases =
+ sizeof m68k_opcode_aliases / sizeof m68k_opcode_aliases[0];
+/* **** End of m68k-opc.c */
+/* **** floatformat.c from sourceware.org CVS 2005-08-14. */
+/* IEEE floating point support routines, for GDB, the GNU Debugger.
+ Copyright (C) 1991, 1994, 1999, 2000, 2003 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 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/>. */
+
+/* This is needed to pick up the NAN macro on some systems. */
+//#define _GNU_SOURCE
+
+#ifndef INFINITY
+#ifdef HUGE_VAL
+#define INFINITY HUGE_VAL
+#else
+#define INFINITY (1.0 / 0.0)
+#endif
+#endif
+
+#ifndef NAN
+#define NAN (0.0 / 0.0)
+#endif
+
+static unsigned long get_field (const unsigned char *,
+ enum floatformat_byteorders,
+ unsigned int,
+ unsigned int,
+ unsigned int);
+static int floatformat_always_valid (const struct floatformat *fmt,
+ const char *from);
+
+static int
+floatformat_always_valid (const struct floatformat *fmt ATTRIBUTE_UNUSED,
+ const char *from ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not
+ going to bother with trying to muck around with whether it is defined in
+ a system header, what we do if not, etc. */
+#define FLOATFORMAT_CHAR_BIT 8
+
+/* floatformats for IEEE single and double, big and little endian. */
+const struct floatformat floatformat_ieee_single_big =
+{
+ floatformat_big, 32, 0, 1, 8, 127, 255, 9, 23,
+ floatformat_intbit_no,
+ "floatformat_ieee_single_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ieee_single_little =
+{
+ floatformat_little, 32, 0, 1, 8, 127, 255, 9, 23,
+ floatformat_intbit_no,
+ "floatformat_ieee_single_little",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ieee_double_big =
+{
+ floatformat_big, 64, 0, 1, 11, 1023, 2047, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_ieee_double_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ieee_double_little =
+{
+ floatformat_little, 64, 0, 1, 11, 1023, 2047, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_ieee_double_little",
+ floatformat_always_valid
+};
+
+/* floatformat for IEEE double, little endian byte order, with big endian word
+ ordering, as on the ARM. */
+
+const struct floatformat floatformat_ieee_double_littlebyte_bigword =
+{
+ floatformat_littlebyte_bigword, 64, 0, 1, 11, 1023, 2047, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_ieee_double_littlebyte_bigword",
+ floatformat_always_valid
+};
+
+static int floatformat_i387_ext_is_valid (const struct floatformat *fmt, const char *from);
+
+static int
+floatformat_i387_ext_is_valid (const struct floatformat *fmt, const char *from)
+{
+ /* In the i387 double-extended format, if the exponent is all ones,
+ then the integer bit must be set. If the exponent is neither 0
+ nor ~0, the intbit must also be set. Only if the exponent is
+ zero can it be zero, and then it must be zero. */
+ unsigned long exponent, int_bit;
+ const unsigned char *ufrom = (const unsigned char *) from;
+
+ exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ fmt->exp_start, fmt->exp_len);
+ int_bit = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ fmt->man_start, 1);
+
+ if ((exponent == 0) != (int_bit == 0))
+ return 0;
+ else
+ return 1;
+}
+
+const struct floatformat floatformat_i387_ext =
+{
+ floatformat_little, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
+ floatformat_intbit_yes,
+ "floatformat_i387_ext",
+ floatformat_i387_ext_is_valid
+};
+const struct floatformat floatformat_m68881_ext =
+{
+ /* Note that the bits from 16 to 31 are unused. */
+ floatformat_big, 96, 0, 1, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_m68881_ext",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_i960_ext =
+{
+ /* Note that the bits from 0 to 15 are unused. */
+ floatformat_little, 96, 16, 17, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_i960_ext",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_m88110_ext =
+{
+ floatformat_big, 80, 0, 1, 15, 0x3fff, 0x7fff, 16, 64,
+ floatformat_intbit_yes,
+ "floatformat_m88110_ext",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_m88110_harris_ext =
+{
+ /* Harris uses raw format 128 bytes long, but the number is just an ieee
+ double, and the last 64 bits are wasted. */
+ floatformat_big,128, 0, 1, 11, 0x3ff, 0x7ff, 12, 52,
+ floatformat_intbit_no,
+ "floatformat_m88110_ext_harris",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_arm_ext_big =
+{
+ /* Bits 1 to 16 are unused. */
+ floatformat_big, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_arm_ext_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_arm_ext_littlebyte_bigword =
+{
+ /* Bits 1 to 16 are unused. */
+ floatformat_littlebyte_bigword, 96, 0, 17, 15, 0x3fff, 0x7fff, 32, 64,
+ floatformat_intbit_yes,
+ "floatformat_arm_ext_littlebyte_bigword",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_spill_big =
+{
+ floatformat_big, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64,
+ floatformat_intbit_yes,
+ "floatformat_ia64_spill_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_spill_little =
+{
+ floatformat_little, 128, 0, 1, 17, 65535, 0x1ffff, 18, 64,
+ floatformat_intbit_yes,
+ "floatformat_ia64_spill_little",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_quad_big =
+{
+ floatformat_big, 128, 0, 1, 15, 16383, 0x7fff, 16, 112,
+ floatformat_intbit_no,
+ "floatformat_ia64_quad_big",
+ floatformat_always_valid
+};
+const struct floatformat floatformat_ia64_quad_little =
+{
+ floatformat_little, 128, 0, 1, 15, 16383, 0x7fff, 16, 112,
+ floatformat_intbit_no,
+ "floatformat_ia64_quad_little",
+ floatformat_always_valid
+};
+
+/* Extract a field which starts at START and is LEN bits long. DATA and
+ TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
+static unsigned long
+get_field (const unsigned char *data, enum floatformat_byteorders order,
+ unsigned int total_len, unsigned int start, unsigned int len)
+{
+ unsigned long result;
+ unsigned int cur_byte;
+ int cur_bitshift;
+
+ /* Start at the least significant part of the field. */
+ cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
+ cur_bitshift =
+ ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
+ result = *(data + cur_byte) >> (-cur_bitshift);
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+
+ /* Move towards the most significant part of the field. */
+ while ((unsigned int) cur_bitshift < len)
+ {
+ if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
+ /* This is the last byte; zero out the bits which are not part of
+ this field. */
+ result |=
+ (*(data + cur_byte) & ((1 << (len - cur_bitshift)) - 1))
+ << cur_bitshift;
+ else
+ result |= *(data + cur_byte) << cur_bitshift;
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+ }
+ return result;
+}
+
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Convert from FMT to a double.
+ FROM is the address of the extended float.
+ Store the double in *TO. */
+
+void
+floatformat_to_double (const struct floatformat *fmt,
+ const char *from, double *to)
+{
+ const unsigned char *ufrom = (const unsigned char *)from;
+ double dto;
+ long exponent;
+ unsigned long mant;
+ unsigned int mant_bits, mant_off;
+ int mant_bits_left;
+ int special_exponent; /* It's a NaN, denorm or zero */
+
+ exponent = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ fmt->exp_start, fmt->exp_len);
+
+ /* If the exponent indicates a NaN, we don't have information to
+ decide what to do. So we handle it like IEEE, except that we
+ don't try to preserve the type of NaN. FIXME. */
+ if ((unsigned long) exponent == fmt->exp_nan)
+ {
+ int nan;
+
+ mant_off = fmt->man_start;
+ mant_bits_left = fmt->man_len;
+ nan = 0;
+ while (mant_bits_left > 0)
+ {
+ mant_bits = min (mant_bits_left, 32);
+
+ if (get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ mant_off, mant_bits) != 0)
+ {
+ /* This is a NaN. */
+ nan = 1;
+ break;
+ }
+
+ mant_off += mant_bits;
+ mant_bits_left -= mant_bits;
+ }
+
+ /* On certain systems (such as GNU/Linux), the use of the
+ INFINITY macro below may generate a warning that can not be
+ silenced due to a bug in GCC (PR preprocessor/11931). The
+ preprocessor fails to recognise the __extension__ keyword in
+ conjunction with the GNU/C99 extension for hexadecimal
+ floating point constants and will issue a warning when
+ compiling with -pedantic. */
+ if (nan)
+ dto = NAN;
+ else
+ dto = INFINITY;
+
+ if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
+ dto = -dto;
+
+ *to = dto;
+
+ return;
+ }
+
+ mant_bits_left = fmt->man_len;
+ mant_off = fmt->man_start;
+ dto = 0.0;
+
+ special_exponent = exponent == 0 || (unsigned long) exponent == fmt->exp_nan;
+
+ /* Don't bias zero's, denorms or NaNs. */
+ if (!special_exponent)
+ exponent -= fmt->exp_bias;
+
+ /* Build the result algebraically. Might go infinite, underflow, etc;
+ who cares. */
+
+ /* If this format uses a hidden bit, explicitly add it in now. Otherwise,
+ increment the exponent by one to account for the integer bit. */
+
+ if (!special_exponent)
+ {
+ if (fmt->intbit == floatformat_intbit_no)
+ dto = ldexp (1.0, exponent);
+ else
+ exponent++;
+ }
+
+ while (mant_bits_left > 0)
+ {
+ mant_bits = min (mant_bits_left, 32);
+
+ mant = get_field (ufrom, fmt->byteorder, fmt->totalsize,
+ mant_off, mant_bits);
+
+ /* Handle denormalized numbers. FIXME: What should we do for
+ non-IEEE formats? */
+ if (exponent == 0 && mant != 0)
+ dto += ldexp ((double)mant,
+ (- fmt->exp_bias
+ - mant_bits
+ - (mant_off - fmt->man_start)
+ + 1));
+ else
+ dto += ldexp ((double)mant, exponent - mant_bits);
+ if (exponent != 0)
+ exponent -= mant_bits;
+ mant_off += mant_bits;
+ mant_bits_left -= mant_bits;
+ }
+
+ /* Negate it if negative. */
+ if (get_field (ufrom, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1))
+ dto = -dto;
+ *to = dto;
+}
+
+static void put_field (unsigned char *, enum floatformat_byteorders,
+ unsigned int,
+ unsigned int,
+ unsigned int,
+ unsigned long);
+
+/* Set a field which starts at START and is LEN bits long. DATA and
+ TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
+static void
+put_field (unsigned char *data, enum floatformat_byteorders order,
+ unsigned int total_len, unsigned int start, unsigned int len,
+ unsigned long stuff_to_put)
+{
+ unsigned int cur_byte;
+ int cur_bitshift;
+
+ /* Start at the least significant part of the field. */
+ cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
+ cur_bitshift =
+ ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
+ *(data + cur_byte) &=
+ ~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1) << (-cur_bitshift));
+ *(data + cur_byte) |=
+ (stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift);
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+
+ /* Move towards the most significant part of the field. */
+ while ((unsigned int) cur_bitshift < len)
+ {
+ if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
+ {
+ /* This is the last byte. */
+ *(data + cur_byte) &=
+ ~((1 << (len - cur_bitshift)) - 1);
+ *(data + cur_byte) |= (stuff_to_put >> cur_bitshift);
+ }
+ else
+ *(data + cur_byte) = ((stuff_to_put >> cur_bitshift)
+ & ((1 << FLOATFORMAT_CHAR_BIT) - 1));
+ cur_bitshift += FLOATFORMAT_CHAR_BIT;
+ if (order == floatformat_little)
+ ++cur_byte;
+ else
+ --cur_byte;
+ }
+}
+
+/* The converse: convert the double *FROM to an extended float
+ and store where TO points. Neither FROM nor TO have any alignment
+ restrictions. */
+
+void
+floatformat_from_double (const struct floatformat *fmt,
+ const double *from, char *to)
+{
+ double dfrom;
+ int exponent;
+ double mant;
+ unsigned int mant_bits, mant_off;
+ int mant_bits_left;
+ unsigned char *uto = (unsigned char *)to;
+
+ dfrom = *from;
+ memset (uto, 0, fmt->totalsize / FLOATFORMAT_CHAR_BIT);
+
+ /* If negative, set the sign bit. */
+ if (dfrom < 0)
+ {
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->sign_start, 1, 1);
+ dfrom = -dfrom;
+ }
+
+ if (dfrom == 0)
+ {
+ /* 0.0. */
+ return;
+ }
+
+ if (dfrom != dfrom)
+ {
+ /* NaN. */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, fmt->exp_nan);
+ /* Be sure it's not infinity, but NaN value is irrelevant. */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->man_start,
+ 32, 1);
+ return;
+ }
+
+ if (dfrom + dfrom == dfrom)
+ {
+ /* This can only happen for an infinite value (or zero, which we
+ already handled above). */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, fmt->exp_nan);
+ return;
+ }
+
+ mant = frexp (dfrom, &exponent);
+ if (exponent + fmt->exp_bias - 1 > 0)
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, exponent + fmt->exp_bias - 1);
+ else
+ {
+ /* Handle a denormalized number. FIXME: What should we do for
+ non-IEEE formats? */
+ put_field (uto, fmt->byteorder, fmt->totalsize, fmt->exp_start,
+ fmt->exp_len, 0);
+ mant = ldexp (mant, exponent + fmt->exp_bias - 1);
+ }
+
+ mant_bits_left = fmt->man_len;
+ mant_off = fmt->man_start;
+ while (mant_bits_left > 0)
+ {
+ unsigned long mant_long;
+ mant_bits = mant_bits_left < 32 ? mant_bits_left : 32;
+
+ mant *= 4294967296.0;
+ mant_long = (unsigned long)mant;
+ mant -= mant_long;
+
+ /* If the integer bit is implicit, and we are not creating a
+ denormalized number, then we need to discard it. */
+ if ((unsigned int) mant_bits_left == fmt->man_len
+ && fmt->intbit == floatformat_intbit_no
+ && exponent + fmt->exp_bias - 1 > 0)
+ {
+ mant_long &= 0x7fffffff;
+ mant_bits -= 1;
+ }
+ else if (mant_bits < 32)
+ {
+ /* The bits we want are in the most significant MANT_BITS bits of
+ mant_long. Move them to the least significant. */
+ mant_long >>= 32 - mant_bits;
+ }
+
+ put_field (uto, fmt->byteorder, fmt->totalsize,
+ mant_off, mant_bits, mant_long);
+ mant_off += mant_bits;
+ mant_bits_left -= mant_bits;
+ }
+}
+
+/* Return non-zero iff the data at FROM is a valid number in format FMT. */
+
+int
+floatformat_is_valid (const struct floatformat *fmt, const char *from)
+{
+ return fmt->is_valid (fmt, from);
+}
+
+
+#ifdef IEEE_DEBUG
+
+/* This is to be run on a host which uses IEEE floating point. */
+
+void
+ieee_test (double n)
+{
+ double result;
+
+ floatformat_to_double (&floatformat_ieee_double_little, (char *) &n,
+ &result);
+ if ((n != result && (! isnan (n) || ! isnan (result)))
+ || (n < 0 && result >= 0)
+ || (n >= 0 && result < 0))
+ printf ("Differ(to): %.20g -> %.20g\n", n, result);
+
+ floatformat_from_double (&floatformat_ieee_double_little, &n,
+ (char *) &result);
+ if ((n != result && (! isnan (n) || ! isnan (result)))
+ || (n < 0 && result >= 0)
+ || (n >= 0 && result < 0))
+ printf ("Differ(from): %.20g -> %.20g\n", n, result);
+
+#if 0
+ {
+ char exten[16];
+
+ floatformat_from_double (&floatformat_m68881_ext, &n, exten);
+ floatformat_to_double (&floatformat_m68881_ext, exten, &result);
+ if (n != result)
+ printf ("Differ(to+from): %.20g -> %.20g\n", n, result);
+ }
+#endif
+
+#if IEEE_DEBUG > 1
+ /* This is to be run on a host which uses 68881 format. */
+ {
+ long double ex = *(long double *)exten;
+ if (ex != n)
+ printf ("Differ(from vs. extended): %.20g\n", n);
+ }
+#endif
+}
+
+int
+main (void)
+{
+ ieee_test (0.0);
+ ieee_test (0.5);
+ ieee_test (256.0);
+ ieee_test (0.12345);
+ ieee_test (234235.78907234);
+ ieee_test (-512.0);
+ ieee_test (-0.004321);
+ ieee_test (1.2E-70);
+ ieee_test (1.2E-316);
+ ieee_test (4.9406564584124654E-324);
+ ieee_test (- 4.9406564584124654E-324);
+ ieee_test (- 0.0);
+ ieee_test (- INFINITY);
+ ieee_test (- NAN);
+ ieee_test (INFINITY);
+ ieee_test (NAN);
+ return 0;
+}
+#endif
+/* **** End of floatformat.c */
diff --git a/m68k-semi.c b/m68k-semi.c
new file mode 100644
index 0000000..0371089
--- /dev/null
+++ b/m68k-semi.c
@@ -0,0 +1,407 @@
+/*
+ * m68k/ColdFire Semihosting syscall interface
+ *
+ * Copyright (c) 2005-2007 CodeSourcery.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "cpu.h"
+#if defined(CONFIG_USER_ONLY)
+#include "qemu.h"
+#define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024)
+#else
+#include "qemu-common.h"
+#include "gdbstub.h"
+#include "softmmu-semi.h"
+#endif
+#include "sysemu.h"
+
+#define HOSTED_EXIT 0
+#define HOSTED_INIT_SIM 1
+#define HOSTED_OPEN 2
+#define HOSTED_CLOSE 3
+#define HOSTED_READ 4
+#define HOSTED_WRITE 5
+#define HOSTED_LSEEK 6
+#define HOSTED_RENAME 7
+#define HOSTED_UNLINK 8
+#define HOSTED_STAT 9
+#define HOSTED_FSTAT 10
+#define HOSTED_GETTIMEOFDAY 11
+#define HOSTED_ISATTY 12
+#define HOSTED_SYSTEM 13
+
+typedef uint32_t gdb_mode_t;
+typedef uint32_t gdb_time_t;
+
+struct m68k_gdb_stat {
+ uint32_t gdb_st_dev; /* device */
+ uint32_t gdb_st_ino; /* inode */
+ gdb_mode_t gdb_st_mode; /* protection */
+ uint32_t gdb_st_nlink; /* number of hard links */
+ uint32_t gdb_st_uid; /* user ID of owner */
+ uint32_t gdb_st_gid; /* group ID of owner */
+ uint32_t gdb_st_rdev; /* device type (if inode device) */
+ uint64_t gdb_st_size; /* total size, in bytes */
+ uint64_t gdb_st_blksize; /* blocksize for filesystem I/O */
+ uint64_t gdb_st_blocks; /* number of blocks allocated */
+ gdb_time_t gdb_st_atime; /* time of last access */
+ gdb_time_t gdb_st_mtime; /* time of last modification */
+ gdb_time_t gdb_st_ctime; /* time of last change */
+} __attribute__((packed));
+
+struct gdb_timeval {
+ gdb_time_t tv_sec; /* second */
+ uint64_t tv_usec; /* microsecond */
+} __attribute__((packed));
+
+#define GDB_O_RDONLY 0x0
+#define GDB_O_WRONLY 0x1
+#define GDB_O_RDWR 0x2
+#define GDB_O_APPEND 0x8
+#define GDB_O_CREAT 0x200
+#define GDB_O_TRUNC 0x400
+#define GDB_O_EXCL 0x800
+
+static int translate_openflags(int flags)
+{
+ int hf;
+
+ if (flags & GDB_O_WRONLY)
+ hf = O_WRONLY;
+ else if (flags & GDB_O_RDWR)
+ hf = O_RDWR;
+ else
+ hf = O_RDONLY;
+
+ if (flags & GDB_O_APPEND) hf |= O_APPEND;
+ if (flags & GDB_O_CREAT) hf |= O_CREAT;
+ if (flags & GDB_O_TRUNC) hf |= O_TRUNC;
+ if (flags & GDB_O_EXCL) hf |= O_EXCL;
+
+ return hf;
+}
+
+static void translate_stat(CPUState *env, target_ulong addr, struct stat *s)
+{
+ struct m68k_gdb_stat *p;
+
+ if (!(p = lock_user(VERIFY_WRITE, addr, sizeof(struct m68k_gdb_stat), 0)))
+ /* FIXME - should this return an error code? */
+ return;
+ p->gdb_st_dev = cpu_to_be32(s->st_dev);
+ p->gdb_st_ino = cpu_to_be32(s->st_ino);
+ p->gdb_st_mode = cpu_to_be32(s->st_mode);
+ p->gdb_st_nlink = cpu_to_be32(s->st_nlink);
+ p->gdb_st_uid = cpu_to_be32(s->st_uid);
+ p->gdb_st_gid = cpu_to_be32(s->st_gid);
+ p->gdb_st_rdev = cpu_to_be32(s->st_rdev);
+ p->gdb_st_size = cpu_to_be64(s->st_size);
+#ifdef _WIN32
+ /* Windows stat is missing some fields. */
+ p->gdb_st_blksize = 0;
+ p->gdb_st_blocks = 0;
+#else
+ p->gdb_st_blksize = cpu_to_be64(s->st_blksize);
+ p->gdb_st_blocks = cpu_to_be64(s->st_blocks);
+#endif
+ p->gdb_st_atime = cpu_to_be32(s->st_atime);
+ p->gdb_st_mtime = cpu_to_be32(s->st_mtime);
+ p->gdb_st_ctime = cpu_to_be32(s->st_ctime);
+ unlock_user(p, addr, sizeof(struct m68k_gdb_stat));
+}
+
+static int m68k_semi_is_fseek;
+
+static void m68k_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+ target_ulong args;
+
+ args = env->dregs[1];
+ if (m68k_semi_is_fseek) {
+ /* FIXME: We've already lost the high bits of the fseek
+ return value. */
+ /* FIXME - handle put_user() failure */
+ put_user_u32(0, args);
+ args += 4;
+ m68k_semi_is_fseek = 0;
+ }
+ /* FIXME - handle put_user() failure */
+ put_user_u32(ret, args);
+ put_user_u32(errno, args + 4);
+}
+
+#define ARG(n) \
+({ \
+ target_ulong __arg; \
+ /* FIXME - handle get_user() failure */ \
+ get_user_ual(__arg, args + (n) * 4); \
+ __arg; \
+})
+#define PARG(x) ((unsigned long)ARG(x))
+void do_m68k_semihosting(CPUM68KState *env, int nr)
+{
+ uint32_t args;
+ void *p;
+ void *q;
+ uint32_t len;
+ uint32_t result;
+
+ args = env->dregs[1];
+ switch (nr) {
+ case HOSTED_EXIT:
+ gdb_exit(env, env->dregs[0]);
+ exit(env->dregs[0]);
+ case HOSTED_OPEN:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "open,%s,%x,%x", ARG(0), (int)ARG(1),
+ ARG(2), ARG(3));
+ return;
+ } else {
+ if (!(p = lock_user_string(ARG(0)))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = open(p, translate_openflags(ARG(2)), ARG(3));
+ unlock_user(p, ARG(0), 0);
+ }
+ }
+ break;
+ case HOSTED_CLOSE:
+ {
+ /* Ignore attempts to close stdin/out/err. */
+ int fd = ARG(0);
+ if (fd > 2) {
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "close,%x", ARG(0));
+ return;
+ } else {
+ result = close(fd);
+ }
+ } else {
+ result = 0;
+ }
+ break;
+ }
+ case HOSTED_READ:
+ len = ARG(2);
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "read,%x,%x,%x",
+ ARG(0), ARG(1), len);
+ return;
+ } else {
+ if (!(p = lock_user(VERIFY_WRITE, ARG(1), len, 0))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = read(ARG(0), p, len);
+ unlock_user(p, ARG(1), len);
+ }
+ }
+ break;
+ case HOSTED_WRITE:
+ len = ARG(2);
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "write,%x,%x,%x",
+ ARG(0), ARG(1), len);
+ return;
+ } else {
+ if (!(p = lock_user(VERIFY_READ, ARG(1), len, 1))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = write(ARG(0), p, len);
+ unlock_user(p, ARG(0), 0);
+ }
+ }
+ break;
+ case HOSTED_LSEEK:
+ {
+ uint64_t off;
+ off = (uint32_t)ARG(2) | ((uint64_t)ARG(1) << 32);
+ if (use_gdb_syscalls()) {
+ m68k_semi_is_fseek = 1;
+ gdb_do_syscall(m68k_semi_cb, "fseek,%x,%lx,%x",
+ ARG(0), off, ARG(3));
+ } else {
+ off = lseek(ARG(0), off, ARG(3));
+ /* FIXME - handle put_user() failure */
+ put_user_u32(off >> 32, args);
+ put_user_u32(off, args + 4);
+ put_user_u32(errno, args + 8);
+ }
+ return;
+ }
+ case HOSTED_RENAME:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "rename,%s,%s",
+ ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
+ return;
+ } else {
+ p = lock_user_string(ARG(0));
+ q = lock_user_string(ARG(2));
+ if (!p || !q) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = rename(p, q);
+ }
+ unlock_user(p, ARG(0), 0);
+ unlock_user(q, ARG(2), 0);
+ }
+ break;
+ case HOSTED_UNLINK:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "unlink,%s",
+ ARG(0), (int)ARG(1));
+ return;
+ } else {
+ if (!(p = lock_user_string(ARG(0)))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = unlink(p);
+ unlock_user(p, ARG(0), 0);
+ }
+ }
+ break;
+ case HOSTED_STAT:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "stat,%s,%x",
+ ARG(0), (int)ARG(1), ARG(2));
+ return;
+ } else {
+ struct stat s;
+ if (!(p = lock_user_string(ARG(0)))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = stat(p, &s);
+ unlock_user(p, ARG(0), 0);
+ }
+ if (result == 0) {
+ translate_stat(env, ARG(2), &s);
+ }
+ }
+ break;
+ case HOSTED_FSTAT:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "fstat,%x,%x",
+ ARG(0), ARG(1));
+ return;
+ } else {
+ struct stat s;
+ result = fstat(ARG(0), &s);
+ if (result == 0) {
+ translate_stat(env, ARG(1), &s);
+ }
+ }
+ break;
+ case HOSTED_GETTIMEOFDAY:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "gettimeofday,%x,%x",
+ ARG(0), ARG(1));
+ return;
+ } else {
+ qemu_timeval tv;
+ struct gdb_timeval *p;
+ result = qemu_gettimeofday(&tv);
+ if (result != 0) {
+ if (!(p = lock_user(VERIFY_WRITE,
+ ARG(0), sizeof(struct gdb_timeval), 0))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ p->tv_sec = cpu_to_be32(tv.tv_sec);
+ p->tv_usec = cpu_to_be64(tv.tv_usec);
+ unlock_user(p, ARG(0), sizeof(struct gdb_timeval));
+ }
+ }
+ }
+ break;
+ case HOSTED_ISATTY:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "isatty,%x", ARG(0));
+ return;
+ } else {
+ result = isatty(ARG(0));
+ }
+ break;
+ case HOSTED_SYSTEM:
+ if (use_gdb_syscalls()) {
+ gdb_do_syscall(m68k_semi_cb, "system,%s",
+ ARG(0), (int)ARG(1));
+ return;
+ } else {
+ if (!(p = lock_user_string(ARG(0)))) {
+ /* FIXME - check error code? */
+ result = -1;
+ } else {
+ result = system(p);
+ unlock_user(p, ARG(0), 0);
+ }
+ }
+ break;
+ case HOSTED_INIT_SIM:
+#if defined(CONFIG_USER_ONLY)
+ {
+ TaskState *ts = env->opaque;
+ /* Allocate the heap using sbrk. */
+ if (!ts->heap_limit) {
+ long ret;
+ uint32_t size;
+ uint32_t base;
+
+ base = do_brk(0);
+ size = SEMIHOSTING_HEAP_SIZE;
+ /* Try a big heap, and reduce the size if that fails. */
+ for (;;) {
+ ret = do_brk(base + size);
+ if (ret != -1)
+ break;
+ size >>= 1;
+ }
+ ts->heap_limit = base + size;
+ }
+ /* This call may happen before we have writable memory, so return
+ values directly in registers. */
+ env->dregs[1] = ts->heap_limit;
+ env->aregs[7] = ts->stack_base;
+ }
+#else
+ /* FIXME: This is wrong for boards where RAM does not start at
+ address zero. */
+ env->dregs[1] = ram_size;
+ env->aregs[7] = ram_size;
+#endif
+ return;
+ default:
+ cpu_abort(env, "Unsupported semihosting syscall %d\n", nr);
+ result = 0;
+ }
+ /* FIXME - handle put_user() failure */
+ put_user_u32(result, args);
+ put_user_u32(errno, args + 4);
+}
diff --git a/m68k.ld b/m68k.ld
new file mode 100644
index 0000000..0e3d9de
--- /dev/null
+++ b/m68k.ld
@@ -0,0 +1,175 @@
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf32-m68k", "elf32-m68k",
+ "elf32-m68k")
+OUTPUT_ARCH(m68k)
+ENTRY(_start)
+/* __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x4e754e75
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x4e754e75
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x4e754e75
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x2000) + (. & (0x2000 - 1));
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .got : { *(.got.plt) *(.got) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(32 / 8);
+ }
+ . = ALIGN(32 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}
diff --git a/microblaze-dis.c b/microblaze-dis.c
new file mode 100644
index 0000000..16c312f
--- /dev/null
+++ b/microblaze-dis.c
@@ -0,0 +1,1100 @@
+/* Disassemble Xilinx microblaze instructions.
+ Copyright (C) 1993, 1999, 2000 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 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/>. */
+
+/*
+ * Copyright (c) 2001 Xilinx, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Xilinx, Inc. The name of the Company may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Xilinx, Inc.
+ */
+
+
+#include <stdio.h>
+#define STATIC_TABLE
+#define DEFINE_TABLE
+
+#define TRUE 1
+#define FALSE 0
+
+#ifndef MICROBLAZE_OPC
+#define MICROBLAZE_OPC
+/* Assembler instructions for Xilinx's microblaze processor
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 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/>. */
+
+/*
+ * Copyright (c) 2001 Xilinx, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Xilinx, Inc. The name of the Company may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Xilinx, Inc.
+ */
+
+
+#ifndef MICROBLAZE_OPCM
+#define MICROBLAZE_OPCM
+
+/*
+ * Copyright (c) 2001 Xilinx, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Xilinx, Inc. The name of the Company may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Xilinx, Inc.
+ * $Header:
+ */
+
+enum microblaze_instr {
+ add, rsub, addc, rsubc, addk, rsubk, addkc, rsubkc, cmp, cmpu,
+ addi, rsubi, addic, rsubic, addik, rsubik, addikc, rsubikc, mul, mulh, mulhu, mulhsu,
+ idiv, idivu, bsll, bsra, bsrl, get, put, nget, nput, cget, cput,
+ ncget, ncput, muli, bslli, bsrai, bsrli, mului, or, and, xor,
+ andn, pcmpbf, pcmpbc, pcmpeq, pcmpne, sra, src, srl, sext8, sext16, wic, wdc, wdcclear, wdcflush, mts, mfs, br, brd,
+ brld, bra, brad, brald, microblaze_brk, beq, beqd, bne, bned, blt,
+ bltd, ble, bled, bgt, bgtd, bge, bged, ori, andi, xori, andni,
+ imm, rtsd, rtid, rtbd, rted, bri, brid, brlid, brai, braid, bralid,
+ brki, beqi, beqid, bnei, bneid, blti, bltid, blei, bleid, bgti,
+ bgtid, bgei, bgeid, lbu, lhu, lw, lwx, sb, sh, sw, swx, lbui, lhui, lwi,
+ sbi, shi, swi, msrset, msrclr, tuqula, fadd, frsub, fmul, fdiv,
+ fcmp_lt, fcmp_eq, fcmp_le, fcmp_gt, fcmp_ne, fcmp_ge, fcmp_un, flt, fint, fsqrt,
+ tget, tcget, tnget, tncget, tput, tcput, tnput, tncput,
+ eget, ecget, neget, necget, eput, ecput, neput, necput,
+ teget, tecget, tneget, tnecget, teput, tecput, tneput, tnecput,
+ aget, caget, naget, ncaget, aput, caput, naput, ncaput,
+ taget, tcaget, tnaget, tncaget, taput, tcaput, tnaput, tncaput,
+ eaget, ecaget, neaget, necaget, eaput, ecaput, neaput, necaput,
+ teaget, tecaget, tneaget, tnecaget, teaput, tecaput, tneaput, tnecaput,
+ getd, tgetd, cgetd, tcgetd, ngetd, tngetd, ncgetd, tncgetd,
+ putd, tputd, cputd, tcputd, nputd, tnputd, ncputd, tncputd,
+ egetd, tegetd, ecgetd, tecgetd, negetd, tnegetd, necgetd, tnecgetd,
+ eputd, teputd, ecputd, tecputd, neputd, tneputd, necputd, tnecputd,
+ agetd, tagetd, cagetd, tcagetd, nagetd, tnagetd, ncagetd, tncagetd,
+ aputd, taputd, caputd, tcaputd, naputd, tnaputd, ncaputd, tncaputd,
+ eagetd, teagetd, ecagetd, tecagetd, neagetd, tneagetd, necagetd, tnecagetd,
+ eaputd, teaputd, ecaputd, tecaputd, neaputd, tneaputd, necaputd, tnecaputd,
+ invalid_inst } ;
+
+enum microblaze_instr_type {
+ arithmetic_inst, logical_inst, mult_inst, div_inst, branch_inst,
+ return_inst, immediate_inst, special_inst, memory_load_inst,
+ memory_store_inst, barrel_shift_inst, anyware_inst };
+
+#define INST_WORD_SIZE 4
+
+/* gen purpose regs go from 0 to 31 */
+/* mask is reg num - max_reg_num, ie reg_num - 32 in this case */
+
+#define REG_PC_MASK 0x8000
+#define REG_MSR_MASK 0x8001
+#define REG_EAR_MASK 0x8003
+#define REG_ESR_MASK 0x8005
+#define REG_FSR_MASK 0x8007
+#define REG_BTR_MASK 0x800b
+#define REG_EDR_MASK 0x800d
+#define REG_PVR_MASK 0xa000
+
+#define REG_PID_MASK 0x9000
+#define REG_ZPR_MASK 0x9001
+#define REG_TLBX_MASK 0x9002
+#define REG_TLBLO_MASK 0x9003
+#define REG_TLBHI_MASK 0x9004
+#define REG_TLBSX_MASK 0x9005
+
+#define MIN_REGNUM 0
+#define MAX_REGNUM 31
+
+#define MIN_PVR_REGNUM 0
+#define MAX_PVR_REGNUM 15
+
+#define REG_PC 32 /* PC */
+#define REG_MSR 33 /* machine status reg */
+#define REG_EAR 35 /* Exception reg */
+#define REG_ESR 37 /* Exception reg */
+#define REG_FSR 39 /* FPU Status reg */
+#define REG_BTR 43 /* Branch Target reg */
+#define REG_EDR 45 /* Exception reg */
+#define REG_PVR 40960 /* Program Verification reg */
+
+#define REG_PID 36864 /* MMU: Process ID reg */
+#define REG_ZPR 36865 /* MMU: Zone Protect reg */
+#define REG_TLBX 36866 /* MMU: TLB Index reg */
+#define REG_TLBLO 36867 /* MMU: TLB Low reg */
+#define REG_TLBHI 36868 /* MMU: TLB High reg */
+#define REG_TLBSX 36869 /* MMU: TLB Search Index reg */
+
+/* alternate names for gen purpose regs */
+#define REG_SP 1 /* stack pointer */
+#define REG_ROSDP 2 /* read-only small data pointer */
+#define REG_RWSDP 13 /* read-write small data pointer */
+
+/* Assembler Register - Used in Delay Slot Optimization */
+#define REG_AS 18
+#define REG_ZERO 0
+
+#define RD_LOW 21 /* low bit for RD */
+#define RA_LOW 16 /* low bit for RA */
+#define RB_LOW 11 /* low bit for RB */
+#define IMM_LOW 0 /* low bit for immediate */
+
+#define RD_MASK 0x03E00000
+#define RA_MASK 0x001F0000
+#define RB_MASK 0x0000F800
+#define IMM_MASK 0x0000FFFF
+
+// imm mask for barrel shifts
+#define IMM5_MASK 0x0000001F
+
+
+// FSL imm mask for get, put instructions
+#define RFSL_MASK 0x000000F
+
+// imm mask for msrset, msrclr instructions
+#define IMM15_MASK 0x00007FFF
+
+#endif /* MICROBLAZE-OPCM */
+
+#define INST_TYPE_RD_R1_R2 0
+#define INST_TYPE_RD_R1_IMM 1
+#define INST_TYPE_RD_R1_UNSIGNED_IMM 2
+#define INST_TYPE_RD_R1 3
+#define INST_TYPE_RD_R2 4
+#define INST_TYPE_RD_IMM 5
+#define INST_TYPE_R2 6
+#define INST_TYPE_R1_R2 7
+#define INST_TYPE_R1_IMM 8
+#define INST_TYPE_IMM 9
+#define INST_TYPE_SPECIAL_R1 10
+#define INST_TYPE_RD_SPECIAL 11
+#define INST_TYPE_R1 12
+ // new instn type for barrel shift imms
+#define INST_TYPE_RD_R1_IMM5 13
+#define INST_TYPE_RD_RFSL 14
+#define INST_TYPE_R1_RFSL 15
+
+ // new insn type for insn cache
+#define INST_TYPE_RD_R1_SPECIAL 16
+
+// new insn type for msrclr, msrset insns.
+#define INST_TYPE_RD_IMM15 17
+
+// new insn type for tuqula rd - addik rd, r0, 42
+#define INST_TYPE_RD 18
+
+// new insn type for t*put
+#define INST_TYPE_RFSL 19
+
+#define INST_TYPE_NONE 25
+
+
+
+#define INST_PC_OFFSET 1 /* instructions where the label address is resolved as a PC offset (for branch label)*/
+#define INST_NO_OFFSET 0 /* instructions where the label address is resolved as an absolute value (for data mem or abs address)*/
+
+#define IMMVAL_MASK_NON_SPECIAL 0x0000
+#define IMMVAL_MASK_MTS 0x4000
+#define IMMVAL_MASK_MFS 0x0000
+
+#define OPCODE_MASK_H 0xFC000000 /* High 6 bits only */
+#define OPCODE_MASK_H1 0xFFE00000 /* High 11 bits */
+#define OPCODE_MASK_H2 0xFC1F0000 /* High 6 and bits 20-16 */
+#define OPCODE_MASK_H12 0xFFFF0000 /* High 16 */
+#define OPCODE_MASK_H4 0xFC0007FF /* High 6 and low 11 bits */
+#define OPCODE_MASK_H13S 0xFFE0EFF0 /* High 11 and 15:1 bits and last nibble of last byte for spr */
+#define OPCODE_MASK_H23S 0xFC1FC000 /* High 6, 20-16 and 15:1 bits and last nibble of last byte for spr */
+#define OPCODE_MASK_H34 0xFC00FFFF /* High 6 and low 16 bits */
+#define OPCODE_MASK_H14 0xFFE007FF /* High 11 and low 11 bits */
+#define OPCODE_MASK_H24 0xFC1F07FF /* High 6, bits 20-16 and low 11 bits */
+#define OPCODE_MASK_H124 0xFFFF07FF /* High 16, and low 11 bits */
+#define OPCODE_MASK_H1234 0xFFFFFFFF /* All 32 bits */
+#define OPCODE_MASK_H3 0xFC000600 /* High 6 bits and bits 21, 22 */
+#define OPCODE_MASK_H32 0xFC00FC00 /* High 6 bits and bit 16-21 */
+#define OPCODE_MASK_H34B 0xFC0000FF /* High 6 bits and low 8 bits */
+#define OPCODE_MASK_H34C 0xFC0007E0 /* High 6 bits and bits 21-26 */
+
+// New Mask for msrset, msrclr insns.
+#define OPCODE_MASK_H23N 0xFC1F8000 /* High 6 and bits 11 - 16 */
+
+#define DELAY_SLOT 1
+#define NO_DELAY_SLOT 0
+
+#define MAX_OPCODES 280
+
+struct op_code_struct {
+ const char *name;
+ short inst_type; /* registers and immediate values involved */
+ short inst_offset_type; /* immediate vals offset from PC? (= 1 for branches) */
+ short delay_slots; /* info about delay slots needed after this instr. */
+ short immval_mask;
+ unsigned long bit_sequence; /* all the fixed bits for the op are set and all the variable bits (reg names, imm vals) are set to 0 */
+ unsigned long opcode_mask; /* which bits define the opcode */
+ enum microblaze_instr instr;
+ enum microblaze_instr_type instr_type;
+ /* more info about output format here */
+} opcodes[MAX_OPCODES] =
+
+{
+ {"add", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x00000000, OPCODE_MASK_H4, add, arithmetic_inst },
+ {"rsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H4, rsub, arithmetic_inst },
+ {"addc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x08000000, OPCODE_MASK_H4, addc, arithmetic_inst },
+ {"rsubc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x0C000000, OPCODE_MASK_H4, rsubc, arithmetic_inst },
+ {"addk", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x10000000, OPCODE_MASK_H4, addk, arithmetic_inst },
+ {"rsubk", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000000, OPCODE_MASK_H4, rsubk, arithmetic_inst },
+ {"cmp", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000001, OPCODE_MASK_H4, cmp, arithmetic_inst },
+ {"cmpu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x14000003, OPCODE_MASK_H4, cmpu, arithmetic_inst },
+ {"addkc", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x18000000, OPCODE_MASK_H4, addkc, arithmetic_inst },
+ {"rsubkc",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x1C000000, OPCODE_MASK_H4, rsubkc, arithmetic_inst },
+ {"addi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x20000000, OPCODE_MASK_H, addi, arithmetic_inst },
+ {"rsubi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x24000000, OPCODE_MASK_H, rsubi, arithmetic_inst },
+ {"addic", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x28000000, OPCODE_MASK_H, addic, arithmetic_inst },
+ {"rsubic",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x2C000000, OPCODE_MASK_H, rsubic, arithmetic_inst },
+ {"addik", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x30000000, OPCODE_MASK_H, addik, arithmetic_inst },
+ {"rsubik",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x34000000, OPCODE_MASK_H, rsubik, arithmetic_inst },
+ {"addikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x38000000, OPCODE_MASK_H, addikc, arithmetic_inst },
+ {"rsubikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3C000000, OPCODE_MASK_H, rsubikc, arithmetic_inst },
+ {"mul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000000, OPCODE_MASK_H4, mul, mult_inst },
+ {"mulh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000001, OPCODE_MASK_H4, mulh, mult_inst },
+ {"mulhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000003, OPCODE_MASK_H4, mulhu, mult_inst },
+ {"mulhsu",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000002, OPCODE_MASK_H4, mulhsu, mult_inst },
+ {"idiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000000, OPCODE_MASK_H4, idiv, div_inst },
+ {"idivu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000002, OPCODE_MASK_H4, idivu, div_inst },
+ {"bsll", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000400, OPCODE_MASK_H3, bsll, barrel_shift_inst },
+ {"bsra", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000200, OPCODE_MASK_H3, bsra, barrel_shift_inst },
+ {"bsrl", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000000, OPCODE_MASK_H3, bsrl, barrel_shift_inst },
+ {"get", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000000, OPCODE_MASK_H32, get, anyware_inst },
+ {"put", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008000, OPCODE_MASK_H32, put, anyware_inst },
+ {"nget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004000, OPCODE_MASK_H32, nget, anyware_inst },
+ {"nput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C000, OPCODE_MASK_H32, nput, anyware_inst },
+ {"cget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002000, OPCODE_MASK_H32, cget, anyware_inst },
+ {"cput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A000, OPCODE_MASK_H32, cput, anyware_inst },
+ {"ncget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006000, OPCODE_MASK_H32, ncget, anyware_inst },
+ {"ncput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E000, OPCODE_MASK_H32, ncput, anyware_inst },
+ {"muli", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x60000000, OPCODE_MASK_H, muli, mult_inst },
+ {"bslli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000400, OPCODE_MASK_H3, bslli, barrel_shift_inst },
+ {"bsrai", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000200, OPCODE_MASK_H3, bsrai, barrel_shift_inst },
+ {"bsrli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000000, OPCODE_MASK_H3, bsrli, barrel_shift_inst },
+ {"or", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000000, OPCODE_MASK_H4, or, logical_inst },
+ {"and", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x84000000, OPCODE_MASK_H4, and, logical_inst },
+ {"xor", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x88000000, OPCODE_MASK_H4, xor, logical_inst },
+ {"andn", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x8C000000, OPCODE_MASK_H4, andn, logical_inst },
+ {"pcmpbf",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000400, OPCODE_MASK_H4, pcmpbf, logical_inst },
+ {"pcmpbc",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x84000400, OPCODE_MASK_H4, pcmpbc, logical_inst },
+ {"pcmpeq",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x88000400, OPCODE_MASK_H4, pcmpeq, logical_inst },
+ {"pcmpne",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x8C000400, OPCODE_MASK_H4, pcmpne, logical_inst },
+ {"sra", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000001, OPCODE_MASK_H34, sra, logical_inst },
+ {"src", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000021, OPCODE_MASK_H34, src, logical_inst },
+ {"srl", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000041, OPCODE_MASK_H34, srl, logical_inst },
+ {"sext8", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000060, OPCODE_MASK_H34, sext8, logical_inst },
+ {"sext16",INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000061, OPCODE_MASK_H34, sext16, logical_inst },
+ {"wic", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000068, OPCODE_MASK_H34B, wic, special_inst },
+ {"wdc", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000064, OPCODE_MASK_H34B, wdc, special_inst },
+ {"wdc.clear", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000066, OPCODE_MASK_H34B, wdcclear, special_inst },
+ {"wdc.flush", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000074, OPCODE_MASK_H34B, wdcflush, special_inst },
+ {"mts", INST_TYPE_SPECIAL_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MTS, 0x9400C000, OPCODE_MASK_H13S, mts, special_inst },
+ {"mfs", INST_TYPE_RD_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MFS, 0x94008000, OPCODE_MASK_H23S, mfs, special_inst },
+ {"br", INST_TYPE_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98000000, OPCODE_MASK_H124, br, branch_inst },
+ {"brd", INST_TYPE_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98100000, OPCODE_MASK_H124, brd, branch_inst },
+ {"brld", INST_TYPE_RD_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98140000, OPCODE_MASK_H24, brld, branch_inst },
+ {"bra", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98080000, OPCODE_MASK_H124, bra, branch_inst },
+ {"brad", INST_TYPE_R2, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98180000, OPCODE_MASK_H124, brad, branch_inst },
+ {"brald", INST_TYPE_RD_R2, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x981C0000, OPCODE_MASK_H24, brald, branch_inst },
+ {"brk", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x980C0000, OPCODE_MASK_H24, microblaze_brk, branch_inst },
+ {"beq", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C000000, OPCODE_MASK_H14, beq, branch_inst },
+ {"beqd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E000000, OPCODE_MASK_H14, beqd, branch_inst },
+ {"bne", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C200000, OPCODE_MASK_H14, bne, branch_inst },
+ {"bned", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E200000, OPCODE_MASK_H14, bned, branch_inst },
+ {"blt", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C400000, OPCODE_MASK_H14, blt, branch_inst },
+ {"bltd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E400000, OPCODE_MASK_H14, bltd, branch_inst },
+ {"ble", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C600000, OPCODE_MASK_H14, ble, branch_inst },
+ {"bled", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E600000, OPCODE_MASK_H14, bled, branch_inst },
+ {"bgt", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9C800000, OPCODE_MASK_H14, bgt, branch_inst },
+ {"bgtd", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9E800000, OPCODE_MASK_H14, bgtd, branch_inst },
+ {"bge", INST_TYPE_R1_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9CA00000, OPCODE_MASK_H14, bge, branch_inst },
+ {"bged", INST_TYPE_R1_R2, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x9EA00000, OPCODE_MASK_H14, bged, branch_inst },
+ {"ori", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA0000000, OPCODE_MASK_H, ori, logical_inst },
+ {"andi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA4000000, OPCODE_MASK_H, andi, logical_inst },
+ {"xori", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA8000000, OPCODE_MASK_H, xori, logical_inst },
+ {"andni", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xAC000000, OPCODE_MASK_H, andni, logical_inst },
+ {"imm", INST_TYPE_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB0000000, OPCODE_MASK_H12, imm, immediate_inst },
+ {"rtsd", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6000000, OPCODE_MASK_H1, rtsd, return_inst },
+ {"rtid", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6200000, OPCODE_MASK_H1, rtid, return_inst },
+ {"rtbd", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6400000, OPCODE_MASK_H1, rtbd, return_inst },
+ {"rted", INST_TYPE_R1_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6800000, OPCODE_MASK_H1, rted, return_inst },
+ {"bri", INST_TYPE_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8000000, OPCODE_MASK_H12, bri, branch_inst },
+ {"brid", INST_TYPE_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8100000, OPCODE_MASK_H12, brid, branch_inst },
+ {"brlid", INST_TYPE_RD_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8140000, OPCODE_MASK_H2, brlid, branch_inst },
+ {"brai", INST_TYPE_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8080000, OPCODE_MASK_H12, brai, branch_inst },
+ {"braid", INST_TYPE_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB8180000, OPCODE_MASK_H12, braid, branch_inst },
+ {"bralid",INST_TYPE_RD_IMM, INST_NO_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB81C0000, OPCODE_MASK_H2, bralid, branch_inst },
+ {"brki", INST_TYPE_RD_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB80C0000, OPCODE_MASK_H2, brki, branch_inst },
+ {"beqi", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC000000, OPCODE_MASK_H1, beqi, branch_inst },
+ {"beqid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE000000, OPCODE_MASK_H1, beqid, branch_inst },
+ {"bnei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC200000, OPCODE_MASK_H1, bnei, branch_inst },
+ {"bneid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE200000, OPCODE_MASK_H1, bneid, branch_inst },
+ {"blti", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC400000, OPCODE_MASK_H1, blti, branch_inst },
+ {"bltid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE400000, OPCODE_MASK_H1, bltid, branch_inst },
+ {"blei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC600000, OPCODE_MASK_H1, blei, branch_inst },
+ {"bleid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE600000, OPCODE_MASK_H1, bleid, branch_inst },
+ {"bgti", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBC800000, OPCODE_MASK_H1, bgti, branch_inst },
+ {"bgtid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBE800000, OPCODE_MASK_H1, bgtid, branch_inst },
+ {"bgei", INST_TYPE_R1_IMM, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBCA00000, OPCODE_MASK_H1, bgei, branch_inst },
+ {"bgeid", INST_TYPE_R1_IMM, INST_PC_OFFSET, DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xBEA00000, OPCODE_MASK_H1, bgeid, branch_inst },
+ {"lbu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC0000000, OPCODE_MASK_H4, lbu, memory_load_inst },
+ {"lhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC4000000, OPCODE_MASK_H4, lhu, memory_load_inst },
+ {"lw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000000, OPCODE_MASK_H4, lw, memory_load_inst },
+ {"lwx", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000400, OPCODE_MASK_H4, lwx, memory_load_inst },
+ {"sb", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD0000000, OPCODE_MASK_H4, sb, memory_store_inst },
+ {"sh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD4000000, OPCODE_MASK_H4, sh, memory_store_inst },
+ {"sw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000000, OPCODE_MASK_H4, sw, memory_store_inst },
+ {"swx", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000400, OPCODE_MASK_H4, swx, memory_store_inst },
+ {"lbui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE0000000, OPCODE_MASK_H, lbui, memory_load_inst },
+ {"lhui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE4000000, OPCODE_MASK_H, lhui, memory_load_inst },
+ {"lwi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, lwi, memory_load_inst },
+ {"sbi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF0000000, OPCODE_MASK_H, sbi, memory_store_inst },
+ {"shi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF4000000, OPCODE_MASK_H, shi, memory_store_inst },
+ {"swi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, swi, memory_store_inst },
+ {"nop", INST_TYPE_NONE, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x80000000, OPCODE_MASK_H1234, invalid_inst, logical_inst }, /* translates to or r0, r0, r0 */
+ {"la", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x30000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* la translates to addik */
+ {"tuqula",INST_TYPE_RD, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3000002A, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* tuqula rd translates to addik rd, r0, 42 */
+ {"not", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xA800FFFF, OPCODE_MASK_H34, invalid_inst, logical_inst }, /* not translates to xori rd,ra,-1 */
+ {"neg", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* neg translates to rsub rd, ra, r0 */
+ {"rtb", INST_TYPE_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xB6000004, OPCODE_MASK_H1, invalid_inst, return_inst }, /* rtb translates to rts rd, 4 */
+ {"sub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* sub translates to rsub rd, rb, ra */
+ {"lmi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, invalid_inst, memory_load_inst },
+ {"smi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, invalid_inst, memory_store_inst },
+ {"msrset",INST_TYPE_RD_IMM15, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94100000, OPCODE_MASK_H23N, msrset, special_inst },
+ {"msrclr",INST_TYPE_RD_IMM15, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94110000, OPCODE_MASK_H23N, msrclr, special_inst },
+ {"fadd", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000000, OPCODE_MASK_H4, fadd, arithmetic_inst },
+ {"frsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000080, OPCODE_MASK_H4, frsub, arithmetic_inst },
+ {"fmul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000100, OPCODE_MASK_H4, fmul, arithmetic_inst },
+ {"fdiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000180, OPCODE_MASK_H4, fdiv, arithmetic_inst },
+ {"fcmp.lt", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000210, OPCODE_MASK_H4, fcmp_lt, arithmetic_inst },
+ {"fcmp.eq", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000220, OPCODE_MASK_H4, fcmp_eq, arithmetic_inst },
+ {"fcmp.le", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000230, OPCODE_MASK_H4, fcmp_le, arithmetic_inst },
+ {"fcmp.gt", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000240, OPCODE_MASK_H4, fcmp_gt, arithmetic_inst },
+ {"fcmp.ne", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000250, OPCODE_MASK_H4, fcmp_ne, arithmetic_inst },
+ {"fcmp.ge", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000260, OPCODE_MASK_H4, fcmp_ge, arithmetic_inst },
+ {"fcmp.un", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000200, OPCODE_MASK_H4, fcmp_un, arithmetic_inst },
+ {"flt", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000280, OPCODE_MASK_H4, flt, arithmetic_inst },
+ {"fint", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000300, OPCODE_MASK_H4, fint, arithmetic_inst },
+ {"fsqrt", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000380, OPCODE_MASK_H4, fsqrt, arithmetic_inst },
+ {"tget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001000, OPCODE_MASK_H32, tget, anyware_inst },
+ {"tcget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003000, OPCODE_MASK_H32, tcget, anyware_inst },
+ {"tnget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005000, OPCODE_MASK_H32, tnget, anyware_inst },
+ {"tncget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007000, OPCODE_MASK_H32, tncget, anyware_inst },
+ {"tput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009000, OPCODE_MASK_H32, tput, anyware_inst },
+ {"tcput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B000, OPCODE_MASK_H32, tcput, anyware_inst },
+ {"tnput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D000, OPCODE_MASK_H32, tnput, anyware_inst },
+ {"tncput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F000, OPCODE_MASK_H32, tncput, anyware_inst },
+
+ {"eget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000400, OPCODE_MASK_H32, eget, anyware_inst },
+ {"ecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002400, OPCODE_MASK_H32, ecget, anyware_inst },
+ {"neget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004400, OPCODE_MASK_H32, neget, anyware_inst },
+ {"necget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006400, OPCODE_MASK_H32, necget, anyware_inst },
+ {"eput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008400, OPCODE_MASK_H32, eput, anyware_inst },
+ {"ecput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A400, OPCODE_MASK_H32, ecput, anyware_inst },
+ {"neput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C400, OPCODE_MASK_H32, neput, anyware_inst },
+ {"necput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E400, OPCODE_MASK_H32, necput, anyware_inst },
+
+ {"teget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001400, OPCODE_MASK_H32, teget, anyware_inst },
+ {"tecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003400, OPCODE_MASK_H32, tecget, anyware_inst },
+ {"tneget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005400, OPCODE_MASK_H32, tneget, anyware_inst },
+ {"tnecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007400, OPCODE_MASK_H32, tnecget, anyware_inst },
+ {"teput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009400, OPCODE_MASK_H32, teput, anyware_inst },
+ {"tecput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B400, OPCODE_MASK_H32, tecput, anyware_inst },
+ {"tneput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D400, OPCODE_MASK_H32, tneput, anyware_inst },
+ {"tnecput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F400, OPCODE_MASK_H32, tnecput, anyware_inst },
+
+ {"aget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000800, OPCODE_MASK_H32, aget, anyware_inst },
+ {"caget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002800, OPCODE_MASK_H32, caget, anyware_inst },
+ {"naget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004800, OPCODE_MASK_H32, naget, anyware_inst },
+ {"ncaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006800, OPCODE_MASK_H32, ncaget, anyware_inst },
+ {"aput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008800, OPCODE_MASK_H32, aput, anyware_inst },
+ {"caput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A800, OPCODE_MASK_H32, caput, anyware_inst },
+ {"naput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C800, OPCODE_MASK_H32, naput, anyware_inst },
+ {"ncaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E800, OPCODE_MASK_H32, ncaput, anyware_inst },
+
+ {"taget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001800, OPCODE_MASK_H32, taget, anyware_inst },
+ {"tcaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003800, OPCODE_MASK_H32, tcaget, anyware_inst },
+ {"tnaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005800, OPCODE_MASK_H32, tnaget, anyware_inst },
+ {"tncaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007800, OPCODE_MASK_H32, tncaget, anyware_inst },
+ {"taput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009800, OPCODE_MASK_H32, taput, anyware_inst },
+ {"tcaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B800, OPCODE_MASK_H32, tcaput, anyware_inst },
+ {"tnaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D800, OPCODE_MASK_H32, tnaput, anyware_inst },
+ {"tncaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F800, OPCODE_MASK_H32, tncaput, anyware_inst },
+
+ {"eaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000C00, OPCODE_MASK_H32, eget, anyware_inst },
+ {"ecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002C00, OPCODE_MASK_H32, ecget, anyware_inst },
+ {"neaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004C00, OPCODE_MASK_H32, neget, anyware_inst },
+ {"necaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006C00, OPCODE_MASK_H32, necget, anyware_inst },
+ {"eaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008C00, OPCODE_MASK_H32, eput, anyware_inst },
+ {"ecaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00AC00, OPCODE_MASK_H32, ecput, anyware_inst },
+ {"neaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00CC00, OPCODE_MASK_H32, neput, anyware_inst },
+ {"necaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00EC00, OPCODE_MASK_H32, necput, anyware_inst },
+
+ {"teaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001C00, OPCODE_MASK_H32, teaget, anyware_inst },
+ {"tecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003C00, OPCODE_MASK_H32, tecaget, anyware_inst },
+ {"tneaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005C00, OPCODE_MASK_H32, tneaget, anyware_inst },
+ {"tnecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007C00, OPCODE_MASK_H32, tnecaget, anyware_inst },
+ {"teaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009C00, OPCODE_MASK_H32, teaput, anyware_inst },
+ {"tecaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00BC00, OPCODE_MASK_H32, tecaput, anyware_inst },
+ {"tneaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00DC00, OPCODE_MASK_H32, tneaput, anyware_inst },
+ {"tnecaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00FC00, OPCODE_MASK_H32, tnecaput, anyware_inst },
+
+ {"getd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000000, OPCODE_MASK_H34C, getd, anyware_inst },
+ {"tgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000080, OPCODE_MASK_H34C, tgetd, anyware_inst },
+ {"cgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000100, OPCODE_MASK_H34C, cgetd, anyware_inst },
+ {"tcgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000180, OPCODE_MASK_H34C, tcgetd, anyware_inst },
+ {"ngetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000200, OPCODE_MASK_H34C, ngetd, anyware_inst },
+ {"tngetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000280, OPCODE_MASK_H34C, tngetd, anyware_inst },
+ {"ncgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000300, OPCODE_MASK_H34C, ncgetd, anyware_inst },
+ {"tncgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000380, OPCODE_MASK_H34C, tncgetd, anyware_inst },
+ {"putd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000400, OPCODE_MASK_H34C, putd, anyware_inst },
+ {"tputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000480, OPCODE_MASK_H34C, tputd, anyware_inst },
+ {"cputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000500, OPCODE_MASK_H34C, cputd, anyware_inst },
+ {"tcputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000580, OPCODE_MASK_H34C, tcputd, anyware_inst },
+ {"nputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000600, OPCODE_MASK_H34C, nputd, anyware_inst },
+ {"tnputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000680, OPCODE_MASK_H34C, tnputd, anyware_inst },
+ {"ncputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000700, OPCODE_MASK_H34C, ncputd, anyware_inst },
+ {"tncputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000780, OPCODE_MASK_H34C, tncputd, anyware_inst },
+
+ {"egetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000020, OPCODE_MASK_H34C, egetd, anyware_inst },
+ {"tegetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000A0, OPCODE_MASK_H34C, tegetd, anyware_inst },
+ {"ecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000120, OPCODE_MASK_H34C, ecgetd, anyware_inst },
+ {"tecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001A0, OPCODE_MASK_H34C, tecgetd, anyware_inst },
+ {"negetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000220, OPCODE_MASK_H34C, negetd, anyware_inst },
+ {"tnegetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002A0, OPCODE_MASK_H34C, tnegetd, anyware_inst },
+ {"necgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000320, OPCODE_MASK_H34C, necgetd, anyware_inst },
+ {"tnecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003A0, OPCODE_MASK_H34C, tnecgetd, anyware_inst },
+ {"eputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000420, OPCODE_MASK_H34C, eputd, anyware_inst },
+ {"teputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004A0, OPCODE_MASK_H34C, teputd, anyware_inst },
+ {"ecputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000520, OPCODE_MASK_H34C, ecputd, anyware_inst },
+ {"tecputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005A0, OPCODE_MASK_H34C, tecputd, anyware_inst },
+ {"neputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000620, OPCODE_MASK_H34C, neputd, anyware_inst },
+ {"tneputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006A0, OPCODE_MASK_H34C, tneputd, anyware_inst },
+ {"necputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000720, OPCODE_MASK_H34C, necputd, anyware_inst },
+ {"tnecputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007A0, OPCODE_MASK_H34C, tnecputd, anyware_inst },
+
+ {"agetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000040, OPCODE_MASK_H34C, agetd, anyware_inst },
+ {"tagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000C0, OPCODE_MASK_H34C, tagetd, anyware_inst },
+ {"cagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000140, OPCODE_MASK_H34C, cagetd, anyware_inst },
+ {"tcagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001C0, OPCODE_MASK_H34C, tcagetd, anyware_inst },
+ {"nagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000240, OPCODE_MASK_H34C, nagetd, anyware_inst },
+ {"tnagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002C0, OPCODE_MASK_H34C, tnagetd, anyware_inst },
+ {"ncagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000340, OPCODE_MASK_H34C, ncagetd, anyware_inst },
+ {"tncagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003C0, OPCODE_MASK_H34C, tncagetd, anyware_inst },
+ {"aputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000440, OPCODE_MASK_H34C, aputd, anyware_inst },
+ {"taputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004C0, OPCODE_MASK_H34C, taputd, anyware_inst },
+ {"caputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000540, OPCODE_MASK_H34C, caputd, anyware_inst },
+ {"tcaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005C0, OPCODE_MASK_H34C, tcaputd, anyware_inst },
+ {"naputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000640, OPCODE_MASK_H34C, naputd, anyware_inst },
+ {"tnaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006C0, OPCODE_MASK_H34C, tnaputd, anyware_inst },
+ {"ncaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000740, OPCODE_MASK_H34C, ncaputd, anyware_inst },
+ {"tncaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007C0, OPCODE_MASK_H34C, tncaputd, anyware_inst },
+
+ {"eagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000060, OPCODE_MASK_H34C, eagetd, anyware_inst },
+ {"teagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000E0, OPCODE_MASK_H34C, teagetd, anyware_inst },
+ {"ecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000160, OPCODE_MASK_H34C, ecagetd, anyware_inst },
+ {"tecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001E0, OPCODE_MASK_H34C, tecagetd, anyware_inst },
+ {"neagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000260, OPCODE_MASK_H34C, neagetd, anyware_inst },
+ {"tneagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002E0, OPCODE_MASK_H34C, tneagetd, anyware_inst },
+ {"necagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000360, OPCODE_MASK_H34C, necagetd, anyware_inst },
+ {"tnecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003E0, OPCODE_MASK_H34C, tnecagetd, anyware_inst },
+ {"eaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000460, OPCODE_MASK_H34C, eaputd, anyware_inst },
+ {"teaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004E0, OPCODE_MASK_H34C, teaputd, anyware_inst },
+ {"ecaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000560, OPCODE_MASK_H34C, ecaputd, anyware_inst },
+ {"tecaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005E0, OPCODE_MASK_H34C, tecaputd, anyware_inst },
+ {"neaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000660, OPCODE_MASK_H34C, neaputd, anyware_inst },
+ {"tneaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006E0, OPCODE_MASK_H34C, tneaputd, anyware_inst },
+ {"necaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000760, OPCODE_MASK_H34C, necaputd, anyware_inst },
+ {"tnecaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007E0, OPCODE_MASK_H34C, tnecaputd, anyware_inst },
+ {"", 0, 0, 0, 0, 0, 0, 0, 0},
+};
+
+/* prefix for register names */
+char register_prefix[] = "r";
+char special_register_prefix[] = "spr";
+char fsl_register_prefix[] = "rfsl";
+char pvr_register_prefix[] = "rpvr";
+
+
+/* #defines for valid immediate range */
+#define MIN_IMM ((int) 0x80000000)
+#define MAX_IMM ((int) 0x7fffffff)
+
+#define MIN_IMM15 ((int) 0x0000)
+#define MAX_IMM15 ((int) 0x7fff)
+
+#endif /* MICROBLAZE_OPC */
+
+#include "dis-asm.h"
+#include <strings.h>
+
+#define get_field_rd(instr) get_field(instr, RD_MASK, RD_LOW)
+#define get_field_r1(instr) get_field(instr, RA_MASK, RA_LOW)
+#define get_field_r2(instr) get_field(instr, RB_MASK, RB_LOW)
+#define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW)
+#define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW)
+
+/* Local function prototypes. */
+
+static char * get_field (long instr, long mask, unsigned short low);
+static char * get_field_imm (long instr);
+static char * get_field_imm5 (long instr);
+static char * get_field_rfsl (long instr);
+static char * get_field_imm15 (long instr);
+#if 0
+static char * get_field_unsigned_imm (long instr);
+#endif
+char * get_field_special (long instr, struct op_code_struct * op);
+unsigned long read_insn_microblaze (bfd_vma memaddr,
+ struct disassemble_info *info,
+ struct op_code_struct **opr);
+enum microblaze_instr get_insn_microblaze (long inst,
+ bfd_boolean *isunsignedimm,
+ enum microblaze_instr_type *insn_type,
+ short *delay_slots);
+short get_delay_slots_microblaze (long inst);
+enum microblaze_instr microblaze_decode_insn (long insn,
+ int *rd,
+ int *ra,
+ int *rb,
+ int *imm);
+unsigned long
+microblaze_get_target_address (long inst,
+ bfd_boolean immfound,
+ int immval,
+ long pcval,
+ long r1val,
+ long r2val,
+ bfd_boolean *targetvalid,
+ bfd_boolean *unconditionalbranch);
+
+static char *
+get_field (long instr, long mask, unsigned short low)
+{
+ char tmpstr[25];
+ sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low));
+ return(strdup(tmpstr));
+}
+
+static char *
+get_field_imm (long instr)
+{
+ char tmpstr[25];
+ sprintf(tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW));
+ return(strdup(tmpstr));
+}
+
+static char *
+get_field_imm5 (long instr)
+{
+ char tmpstr[25];
+ sprintf(tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW));
+ return(strdup(tmpstr));
+}
+
+static char *
+get_field_rfsl (long instr)
+{
+ char tmpstr[25];
+ sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & RFSL_MASK) >> IMM_LOW));
+ return(strdup(tmpstr));
+}
+
+static char *
+get_field_imm15 (long instr)
+{
+ char tmpstr[25];
+ sprintf(tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW));
+ return(strdup(tmpstr));
+}
+
+#if 0
+static char *
+get_field_unsigned_imm (long instr)
+{
+ char tmpstr[25];
+ sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW));
+ return(strdup(tmpstr));
+}
+#endif
+
+/*
+ char *
+ get_field_special (instr)
+ long instr;
+ {
+ char tmpstr[25];
+
+ sprintf(tmpstr, "%s%s", register_prefix, (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr");
+
+ return(strdup(tmpstr));
+ }
+*/
+
+char *
+get_field_special (long instr, struct op_code_struct * op)
+{
+ char tmpstr[25];
+ char spr[6];
+
+ switch ( (((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ) {
+
+ case REG_MSR_MASK :
+ strcpy(spr, "msr");
+ break;
+ case REG_PC_MASK :
+ strcpy(spr, "pc");
+ break;
+ case REG_EAR_MASK :
+ strcpy(spr, "ear");
+ break;
+ case REG_ESR_MASK :
+ strcpy(spr, "esr");
+ break;
+ case REG_FSR_MASK :
+ strcpy(spr, "fsr");
+ break;
+ case REG_BTR_MASK :
+ strcpy(spr, "btr");
+ break;
+ case REG_EDR_MASK :
+ strcpy(spr, "edr");
+ break;
+ case REG_PID_MASK :
+ strcpy(spr, "pid");
+ break;
+ case REG_ZPR_MASK :
+ strcpy(spr, "zpr");
+ break;
+ case REG_TLBX_MASK :
+ strcpy(spr, "tlbx");
+ break;
+ case REG_TLBLO_MASK :
+ strcpy(spr, "tlblo");
+ break;
+ case REG_TLBHI_MASK :
+ strcpy(spr, "tlbhi");
+ break;
+ case REG_TLBSX_MASK :
+ strcpy(spr, "tlbsx");
+ break;
+ default :
+ {
+ if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) {
+ sprintf(tmpstr, "%spvr%d", register_prefix, (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ^ REG_PVR_MASK);
+ return(strdup(tmpstr));
+ } else {
+ strcpy(spr, "pc");
+ }
+ }
+ break;
+ }
+
+ sprintf(tmpstr, "%s%s", register_prefix, spr);
+ return(strdup(tmpstr));
+}
+
+unsigned long
+read_insn_microblaze (bfd_vma memaddr,
+ struct disassemble_info *info,
+ struct op_code_struct **opr)
+{
+ unsigned char ibytes[4];
+ int status;
+ struct op_code_struct * op;
+ unsigned long inst;
+
+ status = info->read_memory_func (memaddr, ibytes, 4, info);
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, memaddr, info);
+ return 0;
+ }
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ inst = (ibytes[0] << 24) | (ibytes[1] << 16) | (ibytes[2] << 8) | ibytes[3];
+ else if (info->endian == BFD_ENDIAN_LITTLE)
+ inst = (ibytes[3] << 24) | (ibytes[2] << 16) | (ibytes[1] << 8) | ibytes[0];
+ else
+ abort ();
+
+ /* Just a linear search of the table. */
+ for (op = opcodes; op->name != 0; op ++)
+ if (op->bit_sequence == (inst & op->opcode_mask))
+ break;
+
+ *opr = op;
+ return inst;
+}
+
+
+int
+print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info)
+{
+ fprintf_function fprintf_func = info->fprintf_func;
+ void * stream = info->stream;
+ unsigned long inst, prev_inst;
+ struct op_code_struct * op, *pop;
+ int immval = 0;
+ bfd_boolean immfound = FALSE;
+ static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */
+ static int prev_insn_vma = -1; /*init the prev insn vma */
+ int curr_insn_vma = info->buffer_vma;
+
+ info->bytes_per_chunk = 4;
+
+ inst = read_insn_microblaze (memaddr, info, &op);
+ if (inst == 0) {
+ return -1;
+ }
+
+ if (prev_insn_vma == curr_insn_vma) {
+ if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) {
+ prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop);
+ if (prev_inst == 0)
+ return -1;
+ if (pop->instr == imm) {
+ immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000;
+ immfound = TRUE;
+ }
+ else {
+ immval = 0;
+ immfound = FALSE;
+ }
+ }
+ }
+ /* make curr insn as prev insn */
+ prev_insn_addr = memaddr;
+ prev_insn_vma = curr_insn_vma;
+
+ if (op->name == 0) {
+ fprintf_func (stream, ".short 0x%04lx", inst);
+ }
+ else
+ {
+ fprintf_func (stream, "%s", op->name);
+
+ switch (op->inst_type)
+ {
+ case INST_TYPE_RD_R1_R2:
+ fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst));
+ break;
+ case INST_TYPE_RD_R1_IMM:
+ fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst));
+ if (info->print_address_func && get_int_field_r1(inst) == 0 && info->symbol_at_address_func) {
+ if (immfound)
+ immval |= (get_int_field_imm(inst) & 0x0000ffff);
+ else {
+ immval = get_int_field_imm(inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ if (immval > 0 && info->symbol_at_address_func(immval, info)) {
+ fprintf_func (stream, "\t// ");
+ info->print_address_func (immval, info);
+ }
+ }
+ break;
+ case INST_TYPE_RD_R1_IMM5:
+ fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst));
+ break;
+ case INST_TYPE_RD_RFSL:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_rfsl(inst));
+ break;
+ case INST_TYPE_R1_RFSL:
+ fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_rfsl(inst));
+ break;
+ case INST_TYPE_RD_SPECIAL:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op));
+ break;
+ case INST_TYPE_SPECIAL_R1:
+ fprintf_func(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst));
+ break;
+ case INST_TYPE_RD_R1:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst));
+ break;
+ case INST_TYPE_R1_R2:
+ fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst));
+ break;
+ case INST_TYPE_R1_IMM:
+ fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst));
+ /* The non-pc relative instructions are returns, which shouldn't
+ have a label printed */
+ if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET && info->symbol_at_address_func) {
+ if (immfound)
+ immval |= (get_int_field_imm(inst) & 0x0000ffff);
+ else {
+ immval = get_int_field_imm(inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ immval += memaddr;
+ if (immval > 0 && info->symbol_at_address_func(immval, info)) {
+ fprintf_func (stream, "\t// ");
+ info->print_address_func (immval, info);
+ } else {
+ fprintf_func (stream, "\t\t// ");
+ fprintf_func (stream, "%x", immval);
+ }
+ }
+ break;
+ case INST_TYPE_RD_IMM:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst));
+ if (info->print_address_func && info->symbol_at_address_func) {
+ if (immfound)
+ immval |= (get_int_field_imm(inst) & 0x0000ffff);
+ else {
+ immval = get_int_field_imm(inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ immval += (int) memaddr;
+ if (info->symbol_at_address_func(immval, info)) {
+ fprintf_func (stream, "\t// ");
+ info->print_address_func (immval, info);
+ }
+ }
+ break;
+ case INST_TYPE_IMM:
+ fprintf_func(stream, "\t%s", get_field_imm(inst));
+ if (info->print_address_func && info->symbol_at_address_func && op->instr != imm) {
+ if (immfound)
+ immval |= (get_int_field_imm(inst) & 0x0000ffff);
+ else {
+ immval = get_int_field_imm(inst);
+ if (immval & 0x8000)
+ immval |= 0xFFFF0000;
+ }
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ immval += (int) memaddr;
+ if (immval > 0 && info->symbol_at_address_func(immval, info)) {
+ fprintf_func (stream, "\t// ");
+ info->print_address_func (immval, info);
+ } else if (op->inst_offset_type == INST_PC_OFFSET) {
+ fprintf_func (stream, "\t\t// ");
+ fprintf_func (stream, "%x", immval);
+ }
+ }
+ break;
+ case INST_TYPE_RD_R2:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst));
+ break;
+ case INST_TYPE_R2:
+ fprintf_func(stream, "\t%s", get_field_r2(inst));
+ break;
+ case INST_TYPE_R1:
+ fprintf_func(stream, "\t%s", get_field_r1(inst));
+ break;
+ case INST_TYPE_RD_R1_SPECIAL:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst));
+ break;
+ case INST_TYPE_RD_IMM15:
+ fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm15(inst));
+ break;
+ /* For tuqula instruction */
+ case INST_TYPE_RD:
+ fprintf_func(stream, "\t%s", get_field_rd(inst));
+ break;
+ case INST_TYPE_RFSL:
+ fprintf_func(stream, "\t%s", get_field_rfsl(inst));
+ break;
+ default:
+ /* if the disassembler lags the instruction set */
+ fprintf_func (stream, "\tundecoded operands, inst is 0x%04lx", inst);
+ break;
+ }
+ }
+
+ /* Say how many bytes we consumed? */
+ return 4;
+}
+
+enum microblaze_instr
+get_insn_microblaze (long inst,
+ bfd_boolean *isunsignedimm,
+ enum microblaze_instr_type *insn_type,
+ short *delay_slots)
+{
+ struct op_code_struct * op;
+ *isunsignedimm = FALSE;
+
+ /* Just a linear search of the table. */
+ for (op = opcodes; op->name != 0; op ++)
+ if (op->bit_sequence == (inst & op->opcode_mask))
+ break;
+
+ if (op->name == 0)
+ return invalid_inst;
+ else {
+ *isunsignedimm = (op->inst_type == INST_TYPE_RD_R1_UNSIGNED_IMM);
+ *insn_type = op->instr_type;
+ *delay_slots = op->delay_slots;
+ return op->instr;
+ }
+}
+
+short
+get_delay_slots_microblaze (long inst)
+{
+ bfd_boolean isunsignedimm;
+ enum microblaze_instr_type insn_type;
+ enum microblaze_instr op;
+ short delay_slots;
+
+ op = get_insn_microblaze( inst, &isunsignedimm, &insn_type, &delay_slots);
+ if (op == invalid_inst)
+ return 0;
+ else
+ return delay_slots;
+}
+
+enum microblaze_instr
+microblaze_decode_insn (long insn,
+ int *rd,
+ int *ra,
+ int *rb,
+ int *imm)
+{
+ enum microblaze_instr op;
+ bfd_boolean t1;
+ enum microblaze_instr_type t2;
+ short t3;
+
+ op = get_insn_microblaze(insn, &t1, &t2, &t3);
+ *rd = (insn & RD_MASK) >> RD_LOW;
+ *ra = (insn & RA_MASK) >> RA_LOW;
+ *rb = (insn & RB_MASK) >> RB_LOW;
+ t3 = (insn & IMM_MASK) >> IMM_LOW;
+ *imm = (int) t3;
+ return (op);
+}
+
+unsigned long
+microblaze_get_target_address (long inst,
+ bfd_boolean immfound,
+ int immval,
+ long pcval,
+ long r1val,
+ long r2val,
+ bfd_boolean *targetvalid,
+ bfd_boolean *unconditionalbranch)
+{
+ struct op_code_struct * op;
+ long targetaddr = 0;
+
+ *unconditionalbranch = FALSE;
+ /* Just a linear search of the table. */
+ for (op = opcodes; op->name != 0; op ++)
+ if (op->bit_sequence == (inst & op->opcode_mask))
+ break;
+
+ if (op->name == 0) {
+ *targetvalid = FALSE;
+ } else if (op->instr_type == branch_inst) {
+ switch (op->inst_type) {
+ case INST_TYPE_R2:
+ *unconditionalbranch = TRUE;
+ /* fallthru */
+ case INST_TYPE_RD_R2:
+ case INST_TYPE_R1_R2:
+ targetaddr = r2val;
+ *targetvalid = TRUE;
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ targetaddr += pcval;
+ break;
+ case INST_TYPE_IMM:
+ *unconditionalbranch = TRUE;
+ /* fallthru */
+ case INST_TYPE_RD_IMM:
+ case INST_TYPE_R1_IMM:
+ if (immfound) {
+ targetaddr = (immval << 16) & 0xffff0000;
+ targetaddr |= (get_int_field_imm(inst) & 0x0000ffff);
+ } else {
+ targetaddr = get_int_field_imm(inst);
+ if (targetaddr & 0x8000)
+ targetaddr |= 0xFFFF0000;
+ }
+ if (op->inst_offset_type == INST_PC_OFFSET)
+ targetaddr += pcval;
+ *targetvalid = TRUE;
+ break;
+ default:
+ *targetvalid = FALSE;
+ break;
+ }
+ } else if (op->instr_type == return_inst) {
+ if (immfound) {
+ targetaddr = (immval << 16) & 0xffff0000;
+ targetaddr |= (get_int_field_imm(inst) & 0x0000ffff);
+ } else {
+ targetaddr = get_int_field_imm(inst);
+ if (targetaddr & 0x8000)
+ targetaddr |= 0xFFFF0000;
+ }
+ targetaddr += r1val;
+ *targetvalid = TRUE;
+ } else {
+ *targetvalid = FALSE;
+ }
+ return targetaddr;
+}
diff --git a/migration-exec.c b/migration-exec.c
new file mode 100644
index 0000000..14718dd
--- /dev/null
+++ b/migration-exec.c
@@ -0,0 +1,145 @@
+/*
+ * QEMU live migration
+ *
+ * Copyright IBM, Corp. 2008
+ * Copyright Dell MessageOne 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Charles Duffy <charles_duffy@messageone.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "migration.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "buffered_file.h"
+#include "block.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+
+//#define DEBUG_MIGRATION_EXEC
+
+#ifdef DEBUG_MIGRATION_EXEC
+#define DPRINTF(fmt, ...) \
+ do { printf("migration-exec: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static int file_errno(FdMigrationState *s)
+{
+ return errno;
+}
+
+static int file_write(FdMigrationState *s, const void * buf, size_t size)
+{
+ return write(s->fd, buf, size);
+}
+
+static int exec_close(FdMigrationState *s)
+{
+ int ret = 0;
+ DPRINTF("exec_close\n");
+ if (s->opaque) {
+ ret = qemu_fclose(s->opaque);
+ s->opaque = NULL;
+ s->fd = -1;
+ if (ret != -1 &&
+ WIFEXITED(ret)
+ && WEXITSTATUS(ret) == 0) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+MigrationState *exec_start_outgoing_migration(Monitor *mon,
+ const char *command,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc)
+{
+ FdMigrationState *s;
+ FILE *f;
+
+ s = qemu_mallocz(sizeof(*s));
+
+ f = popen(command, "w");
+ if (f == NULL) {
+ DPRINTF("Unable to popen exec target\n");
+ goto err_after_alloc;
+ }
+
+ s->fd = fileno(f);
+ if (s->fd == -1) {
+ DPRINTF("Unable to retrieve file descriptor for popen'd handle\n");
+ goto err_after_open;
+ }
+
+ socket_set_nonblock(s->fd);
+
+ s->opaque = qemu_popen(f, "w");
+
+ s->close = exec_close;
+ s->get_error = file_errno;
+ s->write = file_write;
+ s->mig_state.cancel = migrate_fd_cancel;
+ s->mig_state.get_status = migrate_fd_get_status;
+ s->mig_state.release = migrate_fd_release;
+
+ s->mig_state.blk = blk;
+ s->mig_state.shared = inc;
+
+ s->state = MIG_STATE_ACTIVE;
+ s->mon = NULL;
+ s->bandwidth_limit = bandwidth_limit;
+
+ if (!detach) {
+ migrate_fd_monitor_suspend(s, mon);
+ }
+
+ migrate_fd_connect(s);
+ return &s->mig_state;
+
+err_after_open:
+ pclose(f);
+err_after_alloc:
+ qemu_free(s);
+ return NULL;
+}
+
+static void exec_accept_incoming_migration(void *opaque)
+{
+ QEMUFile *f = opaque;
+
+ process_incoming_migration(f);
+ qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL);
+ qemu_fclose(f);
+}
+
+int exec_start_incoming_migration(const char *command)
+{
+ QEMUFile *f;
+
+ DPRINTF("Attempting to start an incoming migration\n");
+ f = qemu_popen_cmd(command, "r");
+ if(f == NULL) {
+ DPRINTF("Unable to apply qemu wrapper to popen file\n");
+ return -errno;
+ }
+
+ qemu_set_fd_handler2(qemu_stdio_fd(f), NULL,
+ exec_accept_incoming_migration, NULL, f);
+
+ return 0;
+}
diff --git a/migration-fd.c b/migration-fd.c
new file mode 100644
index 0000000..6d14505
--- /dev/null
+++ b/migration-fd.c
@@ -0,0 +1,130 @@
+/*
+ * QEMU live migration via generic fd
+ *
+ * Copyright Red Hat, Inc. 2009
+ *
+ * Authors:
+ * Chris Lalancette <clalance@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "migration.h"
+#include "monitor.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "buffered_file.h"
+#include "block.h"
+#include "qemu_socket.h"
+
+//#define DEBUG_MIGRATION_FD
+
+#ifdef DEBUG_MIGRATION_FD
+#define DPRINTF(fmt, ...) \
+ do { printf("migration-fd: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static int fd_errno(FdMigrationState *s)
+{
+ return errno;
+}
+
+static int fd_write(FdMigrationState *s, const void * buf, size_t size)
+{
+ return write(s->fd, buf, size);
+}
+
+static int fd_close(FdMigrationState *s)
+{
+ DPRINTF("fd_close\n");
+ if (s->fd != -1) {
+ close(s->fd);
+ s->fd = -1;
+ }
+ return 0;
+}
+
+MigrationState *fd_start_outgoing_migration(Monitor *mon,
+ const char *fdname,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc)
+{
+ FdMigrationState *s;
+
+ s = qemu_mallocz(sizeof(*s));
+
+ s->fd = monitor_get_fd(mon, fdname);
+ if (s->fd == -1) {
+ DPRINTF("fd_migration: invalid file descriptor identifier\n");
+ goto err_after_alloc;
+ }
+
+ if (fcntl(s->fd, F_SETFL, O_NONBLOCK) == -1) {
+ DPRINTF("Unable to set nonblocking mode on file descriptor\n");
+ goto err_after_open;
+ }
+
+ s->get_error = fd_errno;
+ s->write = fd_write;
+ s->close = fd_close;
+ s->mig_state.cancel = migrate_fd_cancel;
+ s->mig_state.get_status = migrate_fd_get_status;
+ s->mig_state.release = migrate_fd_release;
+
+ s->mig_state.blk = blk;
+ s->mig_state.shared = inc;
+
+ s->state = MIG_STATE_ACTIVE;
+ s->mon = NULL;
+ s->bandwidth_limit = bandwidth_limit;
+
+ if (!detach) {
+ migrate_fd_monitor_suspend(s, mon);
+ }
+
+ migrate_fd_connect(s);
+ return &s->mig_state;
+
+err_after_open:
+ close(s->fd);
+err_after_alloc:
+ qemu_free(s);
+ return NULL;
+}
+
+static void fd_accept_incoming_migration(void *opaque)
+{
+ QEMUFile *f = opaque;
+
+ process_incoming_migration(f);
+ qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL);
+ qemu_fclose(f);
+}
+
+int fd_start_incoming_migration(const char *infd)
+{
+ int fd;
+ QEMUFile *f;
+
+ DPRINTF("Attempting to start an incoming migration via fd\n");
+
+ fd = strtol(infd, NULL, 0);
+ f = qemu_fdopen(fd, "rb");
+ if(f == NULL) {
+ DPRINTF("Unable to apply qemu wrapper to file descriptor\n");
+ return -errno;
+ }
+
+ qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f);
+
+ return 0;
+}
diff --git a/migration-tcp.c b/migration-tcp.c
new file mode 100644
index 0000000..b55f419
--- /dev/null
+++ b/migration-tcp.c
@@ -0,0 +1,204 @@
+/*
+ * QEMU live migration
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "migration.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "buffered_file.h"
+#include "block.h"
+
+//#define DEBUG_MIGRATION_TCP
+
+#ifdef DEBUG_MIGRATION_TCP
+#define DPRINTF(fmt, ...) \
+ do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static int socket_errno(FdMigrationState *s)
+{
+ return socket_error();
+}
+
+static int socket_write(FdMigrationState *s, const void * buf, size_t size)
+{
+ return send(s->fd, buf, size, 0);
+}
+
+static int tcp_close(FdMigrationState *s)
+{
+ DPRINTF("tcp_close\n");
+ if (s->fd != -1) {
+ close(s->fd);
+ s->fd = -1;
+ }
+ return 0;
+}
+
+
+static void tcp_wait_for_connect(void *opaque)
+{
+ FdMigrationState *s = opaque;
+ int val, ret;
+ socklen_t valsize = sizeof(val);
+
+ DPRINTF("connect completed\n");
+ do {
+ ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
+ } while (ret == -1 && (s->get_error(s)) == EINTR);
+
+ if (ret < 0) {
+ migrate_fd_error(s);
+ return;
+ }
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+
+ if (val == 0)
+ migrate_fd_connect(s);
+ else {
+ DPRINTF("error connecting %d\n", val);
+ migrate_fd_error(s);
+ }
+}
+
+MigrationState *tcp_start_outgoing_migration(Monitor *mon,
+ const char *host_port,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc)
+{
+ struct sockaddr_in addr;
+ FdMigrationState *s;
+ int ret;
+
+ if (parse_host_port(&addr, host_port) < 0)
+ return NULL;
+
+ s = qemu_mallocz(sizeof(*s));
+
+ s->get_error = socket_errno;
+ s->write = socket_write;
+ s->close = tcp_close;
+ s->mig_state.cancel = migrate_fd_cancel;
+ s->mig_state.get_status = migrate_fd_get_status;
+ s->mig_state.release = migrate_fd_release;
+
+ s->mig_state.blk = blk;
+ s->mig_state.shared = inc;
+
+ s->state = MIG_STATE_ACTIVE;
+ s->mon = NULL;
+ s->bandwidth_limit = bandwidth_limit;
+ s->fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ if (s->fd == -1) {
+ qemu_free(s);
+ return NULL;
+ }
+
+ socket_set_nonblock(s->fd);
+
+ if (!detach) {
+ migrate_fd_monitor_suspend(s, mon);
+ }
+
+ do {
+ ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret == -1)
+ ret = -(s->get_error(s));
+
+ if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
+ qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s);
+ } while (ret == -EINTR);
+
+ if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
+ DPRINTF("connect failed\n");
+ migrate_fd_error(s);
+ } else if (ret >= 0)
+ migrate_fd_connect(s);
+
+ return &s->mig_state;
+}
+
+static void tcp_accept_incoming_migration(void *opaque)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int s = (unsigned long)opaque;
+ QEMUFile *f;
+ int c;
+
+ do {
+ c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
+ } while (c == -1 && socket_error() == EINTR);
+
+ DPRINTF("accepted migration\n");
+
+ if (c == -1) {
+ fprintf(stderr, "could not accept migration connection\n");
+ goto out2;
+ }
+
+ f = qemu_fopen_socket(c);
+ if (f == NULL) {
+ fprintf(stderr, "could not qemu_fopen socket\n");
+ goto out;
+ }
+
+ process_incoming_migration(f);
+ qemu_fclose(f);
+out:
+ close(c);
+out2:
+ qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
+ close(s);
+}
+
+int tcp_start_incoming_migration(const char *host_port)
+{
+ struct sockaddr_in addr;
+ int val;
+ int s;
+
+ if (parse_host_port(&addr, host_port) < 0) {
+ fprintf(stderr, "invalid host/port combination: %s\n", host_port);
+ return -EINVAL;
+ }
+
+ s = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ if (s == -1)
+ return -socket_error();
+
+ val = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+ goto err;
+
+ if (listen(s, 1) == -1)
+ goto err;
+
+ qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL,
+ (void *)(unsigned long)s);
+
+ return 0;
+
+err:
+ close(s);
+ return -socket_error();
+}
diff --git a/migration-unix.c b/migration-unix.c
new file mode 100644
index 0000000..57232c0
--- /dev/null
+++ b/migration-unix.c
@@ -0,0 +1,215 @@
+/*
+ * QEMU live migration via Unix Domain Sockets
+ *
+ * Copyright Red Hat, Inc. 2009
+ *
+ * Authors:
+ * Chris Lalancette <clalance@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "migration.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "buffered_file.h"
+#include "block.h"
+
+//#define DEBUG_MIGRATION_UNIX
+
+#ifdef DEBUG_MIGRATION_UNIX
+#define DPRINTF(fmt, ...) \
+ do { printf("migration-unix: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+static int unix_errno(FdMigrationState *s)
+{
+ return errno;
+}
+
+static int unix_write(FdMigrationState *s, const void * buf, size_t size)
+{
+ return write(s->fd, buf, size);
+}
+
+static int unix_close(FdMigrationState *s)
+{
+ DPRINTF("unix_close\n");
+ if (s->fd != -1) {
+ close(s->fd);
+ s->fd = -1;
+ }
+ return 0;
+}
+
+static void unix_wait_for_connect(void *opaque)
+{
+ FdMigrationState *s = opaque;
+ int val, ret;
+ socklen_t valsize = sizeof(val);
+
+ DPRINTF("connect completed\n");
+ do {
+ ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize);
+ } while (ret == -1 && (s->get_error(s)) == EINTR);
+
+ if (ret < 0) {
+ migrate_fd_error(s);
+ return;
+ }
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+
+ if (val == 0)
+ migrate_fd_connect(s);
+ else {
+ DPRINTF("error connecting %d\n", val);
+ migrate_fd_error(s);
+ }
+}
+
+MigrationState *unix_start_outgoing_migration(Monitor *mon,
+ const char *path,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc)
+{
+ FdMigrationState *s;
+ struct sockaddr_un addr;
+ int ret;
+
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
+
+ s = qemu_mallocz(sizeof(*s));
+
+ s->get_error = unix_errno;
+ s->write = unix_write;
+ s->close = unix_close;
+ s->mig_state.cancel = migrate_fd_cancel;
+ s->mig_state.get_status = migrate_fd_get_status;
+ s->mig_state.release = migrate_fd_release;
+
+ s->mig_state.blk = blk;
+ s->mig_state.shared = inc;
+
+ s->state = MIG_STATE_ACTIVE;
+ s->mon = NULL;
+ s->bandwidth_limit = bandwidth_limit;
+ s->fd = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s->fd < 0) {
+ DPRINTF("Unable to open socket");
+ goto err_after_alloc;
+ }
+
+ socket_set_nonblock(s->fd);
+
+ do {
+ ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (ret == -1)
+ ret = -(s->get_error(s));
+
+ if (ret == -EINPROGRESS || ret == -EWOULDBLOCK)
+ qemu_set_fd_handler2(s->fd, NULL, NULL, unix_wait_for_connect, s);
+ } while (ret == -EINTR);
+
+ if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) {
+ DPRINTF("connect failed\n");
+ goto err_after_open;
+ }
+
+ if (!detach) {
+ migrate_fd_monitor_suspend(s, mon);
+ }
+
+ if (ret >= 0)
+ migrate_fd_connect(s);
+
+ return &s->mig_state;
+
+err_after_open:
+ close(s->fd);
+
+err_after_alloc:
+ qemu_free(s);
+ return NULL;
+}
+
+static void unix_accept_incoming_migration(void *opaque)
+{
+ struct sockaddr_un addr;
+ socklen_t addrlen = sizeof(addr);
+ int s = (unsigned long)opaque;
+ QEMUFile *f;
+ int c;
+
+ do {
+ c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen);
+ } while (c == -1 && socket_error() == EINTR);
+
+ DPRINTF("accepted migration\n");
+
+ if (c == -1) {
+ fprintf(stderr, "could not accept migration connection\n");
+ return;
+ }
+
+ f = qemu_fopen_socket(c);
+ if (f == NULL) {
+ fprintf(stderr, "could not qemu_fopen socket\n");
+ goto out;
+ }
+
+ process_incoming_migration(f);
+ qemu_fclose(f);
+out:
+ qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL);
+ close(s);
+ close(c);
+}
+
+int unix_start_incoming_migration(const char *path)
+{
+ struct sockaddr_un un;
+ int sock;
+
+ DPRINTF("Attempting to start an incoming migration\n");
+
+ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ fprintf(stderr, "Could not open unix socket: %s\n", strerror(errno));
+ return -EINVAL;
+ }
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+
+ unlink(un.sun_path);
+ if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
+ fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+ if (listen(sock, 1) < 0) {
+ fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+
+ qemu_set_fd_handler2(sock, NULL, unix_accept_incoming_migration, NULL,
+ (void *)(unsigned long)sock);
+
+ return 0;
+
+err:
+ close(sock);
+
+ return -EINVAL;
+}
diff --git a/migration.c b/migration.c
new file mode 100644
index 0000000..3612572
--- /dev/null
+++ b/migration.c
@@ -0,0 +1,482 @@
+/*
+ * QEMU live migration
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "migration.h"
+#include "monitor.h"
+#include "buffered_file.h"
+#include "sysemu.h"
+#include "block.h"
+#include "qemu_socket.h"
+#include "block-migration.h"
+#include "qemu-objects.h"
+
+//#define DEBUG_MIGRATION
+
+#ifdef DEBUG_MIGRATION
+#define DPRINTF(fmt, ...) \
+ do { printf("migration: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+/* Migration speed throttling */
+static int64_t max_throttle = (32 << 20);
+
+static MigrationState *current_migration;
+
+static NotifierList migration_state_notifiers =
+ NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);
+
+int qemu_start_incoming_migration(const char *uri)
+{
+ const char *p;
+ int ret;
+
+ if (strstart(uri, "tcp:", &p))
+ ret = tcp_start_incoming_migration(p);
+#if !defined(WIN32)
+ else if (strstart(uri, "exec:", &p))
+ ret = exec_start_incoming_migration(p);
+ else if (strstart(uri, "unix:", &p))
+ ret = unix_start_incoming_migration(p);
+ else if (strstart(uri, "fd:", &p))
+ ret = fd_start_incoming_migration(p);
+#endif
+ else {
+ fprintf(stderr, "unknown migration protocol: %s\n", uri);
+ ret = -EPROTONOSUPPORT;
+ }
+ return ret;
+}
+
+void process_incoming_migration(QEMUFile *f)
+{
+ if (qemu_loadvm_state(f) < 0) {
+ fprintf(stderr, "load of migration failed\n");
+ exit(0);
+ }
+ qemu_announce_self();
+ DPRINTF("successfully loaded vm state\n");
+
+ incoming_expected = false;
+
+ if (autostart)
+ vm_start();
+}
+
+int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ MigrationState *s = NULL;
+ const char *p;
+ int detach = qdict_get_try_bool(qdict, "detach", 0);
+ int blk = qdict_get_try_bool(qdict, "blk", 0);
+ int inc = qdict_get_try_bool(qdict, "inc", 0);
+ const char *uri = qdict_get_str(qdict, "uri");
+
+ if (current_migration &&
+ current_migration->get_status(current_migration) == MIG_STATE_ACTIVE) {
+ monitor_printf(mon, "migration already in progress\n");
+ return -1;
+ }
+
+ if (qemu_savevm_state_blocked(mon)) {
+ return -1;
+ }
+
+ if (strstart(uri, "tcp:", &p)) {
+ s = tcp_start_outgoing_migration(mon, p, max_throttle, detach,
+ blk, inc);
+#if !defined(WIN32)
+ } else if (strstart(uri, "exec:", &p)) {
+ s = exec_start_outgoing_migration(mon, p, max_throttle, detach,
+ blk, inc);
+ } else if (strstart(uri, "unix:", &p)) {
+ s = unix_start_outgoing_migration(mon, p, max_throttle, detach,
+ blk, inc);
+ } else if (strstart(uri, "fd:", &p)) {
+ s = fd_start_outgoing_migration(mon, p, max_throttle, detach,
+ blk, inc);
+#endif
+ } else {
+ monitor_printf(mon, "unknown migration protocol: %s\n", uri);
+ return -1;
+ }
+
+ if (s == NULL) {
+ monitor_printf(mon, "migration failed\n");
+ return -1;
+ }
+
+ if (current_migration) {
+ current_migration->release(current_migration);
+ }
+
+ current_migration = s;
+ notifier_list_notify(&migration_state_notifiers);
+ return 0;
+}
+
+int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ MigrationState *s = current_migration;
+
+ if (s)
+ s->cancel(s);
+
+ return 0;
+}
+
+int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ int64_t d;
+ FdMigrationState *s;
+
+ d = qdict_get_int(qdict, "value");
+ if (d < 0) {
+ d = 0;
+ }
+ max_throttle = d;
+
+ s = migrate_to_fms(current_migration);
+ if (s && s->file) {
+ qemu_file_set_rate_limit(s->file, max_throttle);
+ }
+
+ return 0;
+}
+
+/* amount of nanoseconds we are willing to wait for migration to be down.
+ * the choice of nanoseconds is because it is the maximum resolution that
+ * get_clock() can achieve. It is an internal measure. All user-visible
+ * units must be in seconds */
+static uint64_t max_downtime = 30000000;
+
+uint64_t migrate_max_downtime(void)
+{
+ return max_downtime;
+}
+
+int do_migrate_set_downtime(Monitor *mon, const QDict *qdict,
+ QObject **ret_data)
+{
+ double d;
+
+ d = qdict_get_double(qdict, "value") * 1e9;
+ d = MAX(0, MIN(UINT64_MAX, d));
+ max_downtime = (uint64_t)d;
+
+ return 0;
+}
+
+static void migrate_print_status(Monitor *mon, const char *name,
+ const QDict *status_dict)
+{
+ QDict *qdict;
+
+ qdict = qobject_to_qdict(qdict_get(status_dict, name));
+
+ monitor_printf(mon, "transferred %s: %" PRIu64 " kbytes\n", name,
+ qdict_get_int(qdict, "transferred") >> 10);
+ monitor_printf(mon, "remaining %s: %" PRIu64 " kbytes\n", name,
+ qdict_get_int(qdict, "remaining") >> 10);
+ monitor_printf(mon, "total %s: %" PRIu64 " kbytes\n", name,
+ qdict_get_int(qdict, "total") >> 10);
+}
+
+void do_info_migrate_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+
+ qdict = qobject_to_qdict(data);
+
+ monitor_printf(mon, "Migration status: %s\n",
+ qdict_get_str(qdict, "status"));
+
+ if (qdict_haskey(qdict, "ram")) {
+ migrate_print_status(mon, "ram", qdict);
+ }
+
+ if (qdict_haskey(qdict, "disk")) {
+ migrate_print_status(mon, "disk", qdict);
+ }
+}
+
+static void migrate_put_status(QDict *qdict, const char *name,
+ uint64_t trans, uint64_t rem, uint64_t total)
+{
+ QObject *obj;
+
+ obj = qobject_from_jsonf("{ 'transferred': %" PRId64 ", "
+ "'remaining': %" PRId64 ", "
+ "'total': %" PRId64 " }", trans, rem, total);
+ qdict_put_obj(qdict, name, obj);
+}
+
+void do_info_migrate(Monitor *mon, QObject **ret_data)
+{
+ QDict *qdict;
+ MigrationState *s = current_migration;
+
+ if (s) {
+ switch (s->get_status(s)) {
+ case MIG_STATE_ACTIVE:
+ qdict = qdict_new();
+ qdict_put(qdict, "status", qstring_from_str("active"));
+
+ migrate_put_status(qdict, "ram", ram_bytes_transferred(),
+ ram_bytes_remaining(), ram_bytes_total());
+
+ if (blk_mig_active()) {
+ migrate_put_status(qdict, "disk", blk_mig_bytes_transferred(),
+ blk_mig_bytes_remaining(),
+ blk_mig_bytes_total());
+ }
+
+ *ret_data = QOBJECT(qdict);
+ break;
+ case MIG_STATE_COMPLETED:
+ *ret_data = qobject_from_jsonf("{ 'status': 'completed' }");
+ break;
+ case MIG_STATE_ERROR:
+ *ret_data = qobject_from_jsonf("{ 'status': 'failed' }");
+ break;
+ case MIG_STATE_CANCELLED:
+ *ret_data = qobject_from_jsonf("{ 'status': 'cancelled' }");
+ break;
+ }
+ }
+}
+
+/* shared migration helpers */
+
+void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon)
+{
+ s->mon = mon;
+ if (monitor_suspend(mon) == 0) {
+ DPRINTF("suspending monitor\n");
+ } else {
+ monitor_printf(mon, "terminal does not allow synchronous "
+ "migration, continuing detached\n");
+ }
+}
+
+void migrate_fd_error(FdMigrationState *s)
+{
+ DPRINTF("setting error state\n");
+ s->state = MIG_STATE_ERROR;
+ notifier_list_notify(&migration_state_notifiers);
+ migrate_fd_cleanup(s);
+}
+
+int migrate_fd_cleanup(FdMigrationState *s)
+{
+ int ret = 0;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+
+ if (s->file) {
+ DPRINTF("closing file\n");
+ if (qemu_fclose(s->file) != 0) {
+ ret = -1;
+ }
+ s->file = NULL;
+ }
+
+ if (s->fd != -1)
+ close(s->fd);
+
+ /* Don't resume monitor until we've flushed all of the buffers */
+ if (s->mon) {
+ monitor_resume(s->mon);
+ }
+
+ s->fd = -1;
+
+ return ret;
+}
+
+void migrate_fd_put_notify(void *opaque)
+{
+ FdMigrationState *s = opaque;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ qemu_file_put_notify(s->file);
+}
+
+ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
+{
+ FdMigrationState *s = opaque;
+ ssize_t ret;
+
+ do {
+ ret = s->write(s, data, size);
+ } while (ret == -1 && ((s->get_error(s)) == EINTR));
+
+ if (ret == -1)
+ ret = -(s->get_error(s));
+
+ if (ret == -EAGAIN) {
+ qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s);
+ } else if (ret < 0) {
+ if (s->mon) {
+ monitor_resume(s->mon);
+ }
+ s->state = MIG_STATE_ERROR;
+ notifier_list_notify(&migration_state_notifiers);
+ }
+
+ return ret;
+}
+
+void migrate_fd_connect(FdMigrationState *s)
+{
+ int ret;
+
+ s->file = qemu_fopen_ops_buffered(s,
+ s->bandwidth_limit,
+ migrate_fd_put_buffer,
+ migrate_fd_put_ready,
+ migrate_fd_wait_for_unfreeze,
+ migrate_fd_close);
+
+ DPRINTF("beginning savevm\n");
+ ret = qemu_savevm_state_begin(s->mon, s->file, s->mig_state.blk,
+ s->mig_state.shared);
+ if (ret < 0) {
+ DPRINTF("failed, %d\n", ret);
+ migrate_fd_error(s);
+ return;
+ }
+
+ migrate_fd_put_ready(s);
+}
+
+void migrate_fd_put_ready(void *opaque)
+{
+ FdMigrationState *s = opaque;
+
+ if (s->state != MIG_STATE_ACTIVE) {
+ DPRINTF("put_ready returning because of non-active state\n");
+ return;
+ }
+
+ DPRINTF("iterate\n");
+ if (qemu_savevm_state_iterate(s->mon, s->file) == 1) {
+ int state;
+ int old_vm_running = vm_running;
+
+ DPRINTF("done iterating\n");
+ vm_stop(0);
+
+ if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) {
+ if (old_vm_running) {
+ vm_start();
+ }
+ state = MIG_STATE_ERROR;
+ } else {
+ state = MIG_STATE_COMPLETED;
+ }
+ if (migrate_fd_cleanup(s) < 0) {
+ if (old_vm_running) {
+ vm_start();
+ }
+ state = MIG_STATE_ERROR;
+ }
+ s->state = state;
+ notifier_list_notify(&migration_state_notifiers);
+ }
+}
+
+int migrate_fd_get_status(MigrationState *mig_state)
+{
+ FdMigrationState *s = migrate_to_fms(mig_state);
+ return s->state;
+}
+
+void migrate_fd_cancel(MigrationState *mig_state)
+{
+ FdMigrationState *s = migrate_to_fms(mig_state);
+
+ if (s->state != MIG_STATE_ACTIVE)
+ return;
+
+ DPRINTF("cancelling migration\n");
+
+ s->state = MIG_STATE_CANCELLED;
+ notifier_list_notify(&migration_state_notifiers);
+ qemu_savevm_state_cancel(s->mon, s->file);
+
+ migrate_fd_cleanup(s);
+}
+
+void migrate_fd_release(MigrationState *mig_state)
+{
+ FdMigrationState *s = migrate_to_fms(mig_state);
+
+ DPRINTF("releasing state\n");
+
+ if (s->state == MIG_STATE_ACTIVE) {
+ s->state = MIG_STATE_CANCELLED;
+ notifier_list_notify(&migration_state_notifiers);
+ migrate_fd_cleanup(s);
+ }
+ qemu_free(s);
+}
+
+void migrate_fd_wait_for_unfreeze(void *opaque)
+{
+ FdMigrationState *s = opaque;
+ int ret;
+
+ DPRINTF("wait for unfreeze\n");
+ if (s->state != MIG_STATE_ACTIVE)
+ return;
+
+ do {
+ fd_set wfds;
+
+ FD_ZERO(&wfds);
+ FD_SET(s->fd, &wfds);
+
+ ret = select(s->fd + 1, NULL, &wfds, NULL, NULL);
+ } while (ret == -1 && (s->get_error(s)) == EINTR);
+}
+
+int migrate_fd_close(void *opaque)
+{
+ FdMigrationState *s = opaque;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ return s->close(s);
+}
+
+void add_migration_state_change_notifier(Notifier *notify)
+{
+ notifier_list_add(&migration_state_notifiers, notify);
+}
+
+void remove_migration_state_change_notifier(Notifier *notify)
+{
+ notifier_list_remove(&migration_state_notifiers, notify);
+}
+
+int get_migration_state(void)
+{
+ if (current_migration) {
+ return migrate_fd_get_status(current_migration);
+ } else {
+ return MIG_STATE_ERROR;
+ }
+}
diff --git a/migration.h b/migration.h
new file mode 100644
index 0000000..2170792
--- /dev/null
+++ b/migration.h
@@ -0,0 +1,142 @@
+/*
+ * QEMU live migration
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_MIGRATION_H
+#define QEMU_MIGRATION_H
+
+#include "qdict.h"
+#include "qemu-common.h"
+#include "notify.h"
+
+#define MIG_STATE_ERROR -1
+#define MIG_STATE_COMPLETED 0
+#define MIG_STATE_CANCELLED 1
+#define MIG_STATE_ACTIVE 2
+
+typedef struct MigrationState MigrationState;
+
+struct MigrationState
+{
+ /* FIXME: add more accessors to print migration info */
+ void (*cancel)(MigrationState *s);
+ int (*get_status)(MigrationState *s);
+ void (*release)(MigrationState *s);
+ int blk;
+ int shared;
+};
+
+typedef struct FdMigrationState FdMigrationState;
+
+struct FdMigrationState
+{
+ MigrationState mig_state;
+ int64_t bandwidth_limit;
+ QEMUFile *file;
+ int fd;
+ Monitor *mon;
+ int state;
+ int (*get_error)(struct FdMigrationState*);
+ int (*close)(struct FdMigrationState*);
+ int (*write)(struct FdMigrationState*, const void *, size_t);
+ void *opaque;
+};
+
+void process_incoming_migration(QEMUFile *f);
+
+int qemu_start_incoming_migration(const char *uri);
+
+int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+uint64_t migrate_max_downtime(void);
+
+int do_migrate_set_downtime(Monitor *mon, const QDict *qdict,
+ QObject **ret_data);
+
+void do_info_migrate_print(Monitor *mon, const QObject *data);
+
+void do_info_migrate(Monitor *mon, QObject **ret_data);
+
+int exec_start_incoming_migration(const char *host_port);
+
+MigrationState *exec_start_outgoing_migration(Monitor *mon,
+ const char *host_port,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc);
+
+int tcp_start_incoming_migration(const char *host_port);
+
+MigrationState *tcp_start_outgoing_migration(Monitor *mon,
+ const char *host_port,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc);
+
+int unix_start_incoming_migration(const char *path);
+
+MigrationState *unix_start_outgoing_migration(Monitor *mon,
+ const char *path,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc);
+
+int fd_start_incoming_migration(const char *path);
+
+MigrationState *fd_start_outgoing_migration(Monitor *mon,
+ const char *fdname,
+ int64_t bandwidth_limit,
+ int detach,
+ int blk,
+ int inc);
+
+void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon);
+
+void migrate_fd_error(FdMigrationState *s);
+
+int migrate_fd_cleanup(FdMigrationState *s);
+
+void migrate_fd_put_notify(void *opaque);
+
+ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size);
+
+void migrate_fd_connect(FdMigrationState *s);
+
+void migrate_fd_put_ready(void *opaque);
+
+int migrate_fd_get_status(MigrationState *mig_state);
+
+void migrate_fd_cancel(MigrationState *mig_state);
+
+void migrate_fd_release(MigrationState *mig_state);
+
+void migrate_fd_wait_for_unfreeze(void *opaque);
+
+int migrate_fd_close(void *opaque);
+
+static inline FdMigrationState *migrate_to_fms(MigrationState *mig_state)
+{
+ return container_of(mig_state, FdMigrationState, mig_state);
+}
+
+void add_migration_state_change_notifier(Notifier *notify);
+void remove_migration_state_change_notifier(Notifier *notify);
+int get_migration_state(void);
+
+#endif
diff --git a/mips-dis.c b/mips-dis.c
new file mode 100644
index 0000000..4d8e85b
--- /dev/null
+++ b/mips-dis.c
@@ -0,0 +1,4873 @@
+/* Print mips instructions for GDB, the GNU debugger, or for objdump.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 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/>. */
+
+#include "dis-asm.h"
+
+/* mips.h. Mips opcode list for GDB, the GNU debugger.
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Contributed by Ralph Campbell and OSF
+ Commented and modified by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not,
+see <http://www.gnu.org/licenses/>. */
+
+/* These are bit masks and shift counts to use to access the various
+ fields of an instruction. To retrieve the X field of an
+ instruction, use the expression
+ (i >> OP_SH_X) & OP_MASK_X
+ To set the same field (to j), use
+ i = (i &~ (OP_MASK_X << OP_SH_X)) | (j << OP_SH_X)
+
+ Make sure you use fields that are appropriate for the instruction,
+ of course.
+
+ The 'i' format uses OP, RS, RT and IMMEDIATE.
+
+ The 'j' format uses OP and TARGET.
+
+ The 'r' format uses OP, RS, RT, RD, SHAMT and FUNCT.
+
+ The 'b' format uses OP, RS, RT and DELTA.
+
+ The floating point 'i' format uses OP, RS, RT and IMMEDIATE.
+
+ The floating point 'r' format uses OP, FMT, FT, FS, FD and FUNCT.
+
+ A breakpoint instruction uses OP, CODE and SPEC (10 bits of the
+ breakpoint instruction are not defined; Kane says the breakpoint
+ code field in BREAK is 20 bits; yet MIPS assemblers and debuggers
+ only use ten bits). An optional two-operand form of break/sdbbp
+ allows the lower ten bits to be set too, and MIPS32 and later
+ architectures allow 20 bits to be set with a signal operand
+ (using CODE20).
+
+ The syscall instruction uses CODE20.
+
+ The general coprocessor instructions use COPZ. */
+
+#define OP_MASK_OP 0x3f
+#define OP_SH_OP 26
+#define OP_MASK_RS 0x1f
+#define OP_SH_RS 21
+#define OP_MASK_FR 0x1f
+#define OP_SH_FR 21
+#define OP_MASK_FMT 0x1f
+#define OP_SH_FMT 21
+#define OP_MASK_BCC 0x7
+#define OP_SH_BCC 18
+#define OP_MASK_CODE 0x3ff
+#define OP_SH_CODE 16
+#define OP_MASK_CODE2 0x3ff
+#define OP_SH_CODE2 6
+#define OP_MASK_RT 0x1f
+#define OP_SH_RT 16
+#define OP_MASK_FT 0x1f
+#define OP_SH_FT 16
+#define OP_MASK_CACHE 0x1f
+#define OP_SH_CACHE 16
+#define OP_MASK_RD 0x1f
+#define OP_SH_RD 11
+#define OP_MASK_FS 0x1f
+#define OP_SH_FS 11
+#define OP_MASK_PREFX 0x1f
+#define OP_SH_PREFX 11
+#define OP_MASK_CCC 0x7
+#define OP_SH_CCC 8
+#define OP_MASK_CODE20 0xfffff /* 20 bit syscall/breakpoint code. */
+#define OP_SH_CODE20 6
+#define OP_MASK_SHAMT 0x1f
+#define OP_SH_SHAMT 6
+#define OP_MASK_FD 0x1f
+#define OP_SH_FD 6
+#define OP_MASK_TARGET 0x3ffffff
+#define OP_SH_TARGET 0
+#define OP_MASK_COPZ 0x1ffffff
+#define OP_SH_COPZ 0
+#define OP_MASK_IMMEDIATE 0xffff
+#define OP_SH_IMMEDIATE 0
+#define OP_MASK_DELTA 0xffff
+#define OP_SH_DELTA 0
+#define OP_MASK_FUNCT 0x3f
+#define OP_SH_FUNCT 0
+#define OP_MASK_SPEC 0x3f
+#define OP_SH_SPEC 0
+#define OP_SH_LOCC 8 /* FP condition code. */
+#define OP_SH_HICC 18 /* FP condition code. */
+#define OP_MASK_CC 0x7
+#define OP_SH_COP1NORM 25 /* Normal COP1 encoding. */
+#define OP_MASK_COP1NORM 0x1 /* a single bit. */
+#define OP_SH_COP1SPEC 21 /* COP1 encodings. */
+#define OP_MASK_COP1SPEC 0xf
+#define OP_MASK_COP1SCLR 0x4
+#define OP_MASK_COP1CMP 0x3
+#define OP_SH_COP1CMP 4
+#define OP_SH_FORMAT 21 /* FP short format field. */
+#define OP_MASK_FORMAT 0x7
+#define OP_SH_TRUE 16
+#define OP_MASK_TRUE 0x1
+#define OP_SH_GE 17
+#define OP_MASK_GE 0x01
+#define OP_SH_UNSIGNED 16
+#define OP_MASK_UNSIGNED 0x1
+#define OP_SH_HINT 16
+#define OP_MASK_HINT 0x1f
+#define OP_SH_MMI 0 /* Multimedia (parallel) op. */
+#define OP_MASK_MMI 0x3f
+#define OP_SH_MMISUB 6
+#define OP_MASK_MMISUB 0x1f
+#define OP_MASK_PERFREG 0x1f /* Performance monitoring. */
+#define OP_SH_PERFREG 1
+#define OP_SH_SEL 0 /* Coprocessor select field. */
+#define OP_MASK_SEL 0x7 /* The sel field of mfcZ and mtcZ. */
+#define OP_SH_CODE19 6 /* 19 bit wait code. */
+#define OP_MASK_CODE19 0x7ffff
+#define OP_SH_ALN 21
+#define OP_MASK_ALN 0x7
+#define OP_SH_VSEL 21
+#define OP_MASK_VSEL 0x1f
+#define OP_MASK_VECBYTE 0x7 /* Selector field is really 4 bits,
+ but 0x8-0xf don't select bytes. */
+#define OP_SH_VECBYTE 22
+#define OP_MASK_VECALIGN 0x7 /* Vector byte-align (alni.ob) op. */
+#define OP_SH_VECALIGN 21
+#define OP_MASK_INSMSB 0x1f /* "ins" MSB. */
+#define OP_SH_INSMSB 11
+#define OP_MASK_EXTMSBD 0x1f /* "ext" MSBD. */
+#define OP_SH_EXTMSBD 11
+
+#define OP_OP_COP0 0x10
+#define OP_OP_COP1 0x11
+#define OP_OP_COP2 0x12
+#define OP_OP_COP3 0x13
+#define OP_OP_LWC1 0x31
+#define OP_OP_LWC2 0x32
+#define OP_OP_LWC3 0x33 /* a.k.a. pref */
+#define OP_OP_LDC1 0x35
+#define OP_OP_LDC2 0x36
+#define OP_OP_LDC3 0x37 /* a.k.a. ld */
+#define OP_OP_SWC1 0x39
+#define OP_OP_SWC2 0x3a
+#define OP_OP_SWC3 0x3b
+#define OP_OP_SDC1 0x3d
+#define OP_OP_SDC2 0x3e
+#define OP_OP_SDC3 0x3f /* a.k.a. sd */
+
+/* MIPS DSP ASE */
+#define OP_SH_DSPACC 11
+#define OP_MASK_DSPACC 0x3
+#define OP_SH_DSPACC_S 21
+#define OP_MASK_DSPACC_S 0x3
+#define OP_SH_DSPSFT 20
+#define OP_MASK_DSPSFT 0x3f
+#define OP_SH_DSPSFT_7 19
+#define OP_MASK_DSPSFT_7 0x7f
+#define OP_SH_SA3 21
+#define OP_MASK_SA3 0x7
+#define OP_SH_SA4 21
+#define OP_MASK_SA4 0xf
+#define OP_SH_IMM8 16
+#define OP_MASK_IMM8 0xff
+#define OP_SH_IMM10 16
+#define OP_MASK_IMM10 0x3ff
+#define OP_SH_WRDSP 11
+#define OP_MASK_WRDSP 0x3f
+#define OP_SH_RDDSP 16
+#define OP_MASK_RDDSP 0x3f
+#define OP_SH_BP 11
+#define OP_MASK_BP 0x3
+
+/* MIPS MT ASE */
+#define OP_SH_MT_U 5
+#define OP_MASK_MT_U 0x1
+#define OP_SH_MT_H 4
+#define OP_MASK_MT_H 0x1
+#define OP_SH_MTACC_T 18
+#define OP_MASK_MTACC_T 0x3
+#define OP_SH_MTACC_D 13
+#define OP_MASK_MTACC_D 0x3
+
+#define OP_OP_COP0 0x10
+#define OP_OP_COP1 0x11
+#define OP_OP_COP2 0x12
+#define OP_OP_COP3 0x13
+#define OP_OP_LWC1 0x31
+#define OP_OP_LWC2 0x32
+#define OP_OP_LWC3 0x33 /* a.k.a. pref */
+#define OP_OP_LDC1 0x35
+#define OP_OP_LDC2 0x36
+#define OP_OP_LDC3 0x37 /* a.k.a. ld */
+#define OP_OP_SWC1 0x39
+#define OP_OP_SWC2 0x3a
+#define OP_OP_SWC3 0x3b
+#define OP_OP_SDC1 0x3d
+#define OP_OP_SDC2 0x3e
+#define OP_OP_SDC3 0x3f /* a.k.a. sd */
+
+/* Values in the 'VSEL' field. */
+#define MDMX_FMTSEL_IMM_QH 0x1d
+#define MDMX_FMTSEL_IMM_OB 0x1e
+#define MDMX_FMTSEL_VEC_QH 0x15
+#define MDMX_FMTSEL_VEC_OB 0x16
+
+/* UDI */
+#define OP_SH_UDI1 6
+#define OP_MASK_UDI1 0x1f
+#define OP_SH_UDI2 6
+#define OP_MASK_UDI2 0x3ff
+#define OP_SH_UDI3 6
+#define OP_MASK_UDI3 0x7fff
+#define OP_SH_UDI4 6
+#define OP_MASK_UDI4 0xfffff
+/* This structure holds information for a particular instruction. */
+
+struct mips_opcode
+{
+ /* The name of the instruction. */
+ const char *name;
+ /* A string describing the arguments for this instruction. */
+ const char *args;
+ /* The basic opcode for the instruction. When assembling, this
+ opcode is modified by the arguments to produce the actual opcode
+ that is used. If pinfo is INSN_MACRO, then this is 0. */
+ unsigned long match;
+ /* If pinfo is not INSN_MACRO, then this is a bit mask for the
+ relevant portions of the opcode when disassembling. If the
+ actual opcode anded with the match field equals the opcode field,
+ then we have found the correct instruction. If pinfo is
+ INSN_MACRO, then this field is the macro identifier. */
+ unsigned long mask;
+ /* For a macro, this is INSN_MACRO. Otherwise, it is a collection
+ of bits describing the instruction, notably any relevant hazard
+ information. */
+ unsigned long pinfo;
+ /* A collection of additional bits describing the instruction. */
+ unsigned long pinfo2;
+ /* A collection of bits describing the instruction sets of which this
+ instruction or macro is a member. */
+ unsigned long membership;
+};
+
+/* These are the characters which may appear in the args field of an
+ instruction. They appear in the order in which the fields appear
+ when the instruction is used. Commas and parentheses in the args
+ string are ignored when assembling, and written into the output
+ when disassembling.
+
+ Each of these characters corresponds to a mask field defined above.
+
+ "<" 5 bit shift amount (OP_*_SHAMT)
+ ">" shift amount between 32 and 63, stored after subtracting 32 (OP_*_SHAMT)
+ "a" 26 bit target address (OP_*_TARGET)
+ "b" 5 bit base register (OP_*_RS)
+ "c" 10 bit breakpoint code (OP_*_CODE)
+ "d" 5 bit destination register specifier (OP_*_RD)
+ "h" 5 bit prefx hint (OP_*_PREFX)
+ "i" 16 bit unsigned immediate (OP_*_IMMEDIATE)
+ "j" 16 bit signed immediate (OP_*_DELTA)
+ "k" 5 bit cache opcode in target register position (OP_*_CACHE)
+ Also used for immediate operands in vr5400 vector insns.
+ "o" 16 bit signed offset (OP_*_DELTA)
+ "p" 16 bit PC relative branch target address (OP_*_DELTA)
+ "q" 10 bit extra breakpoint code (OP_*_CODE2)
+ "r" 5 bit same register used as both source and target (OP_*_RS)
+ "s" 5 bit source register specifier (OP_*_RS)
+ "t" 5 bit target register (OP_*_RT)
+ "u" 16 bit upper 16 bits of address (OP_*_IMMEDIATE)
+ "v" 5 bit same register used as both source and destination (OP_*_RS)
+ "w" 5 bit same register used as both target and destination (OP_*_RT)
+ "U" 5 bit same destination register in both OP_*_RD and OP_*_RT
+ (used by clo and clz)
+ "C" 25 bit coprocessor function code (OP_*_COPZ)
+ "B" 20 bit syscall/breakpoint function code (OP_*_CODE20)
+ "J" 19 bit wait function code (OP_*_CODE19)
+ "x" accept and ignore register name
+ "z" must be zero register
+ "K" 5 bit Hardware Register (rdhwr instruction) (OP_*_RD)
+ "+A" 5 bit ins/ext/dins/dext/dinsm/dextm position, which becomes
+ LSB (OP_*_SHAMT).
+ Enforces: 0 <= pos < 32.
+ "+B" 5 bit ins/dins size, which becomes MSB (OP_*_INSMSB).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 0 < (pos+size) <= 32.
+ "+C" 5 bit ext/dext size, which becomes MSBD (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 0 < (pos+size) <= 32.
+ (Also used by "dext" w/ different limits, but limits for
+ that are checked by the M_DEXT macro.)
+ "+E" 5 bit dinsu/dextu position, which becomes LSB-32 (OP_*_SHAMT).
+ Enforces: 32 <= pos < 64.
+ "+F" 5 bit "dinsm/dinsu" size, which becomes MSB-32 (OP_*_INSMSB).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+ "+G" 5 bit "dextm" size, which becomes MSBD-32 (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+ "+H" 5 bit "dextu" size, which becomes MSBD (OP_*_EXTMSBD).
+ Requires that "+A" or "+E" occur first to set position.
+ Enforces: 32 < (pos+size) <= 64.
+
+ Floating point instructions:
+ "D" 5 bit destination register (OP_*_FD)
+ "M" 3 bit compare condition code (OP_*_CCC) (only used for mips4 and up)
+ "N" 3 bit branch condition code (OP_*_BCC) (only used for mips4 and up)
+ "S" 5 bit fs source 1 register (OP_*_FS)
+ "T" 5 bit ft source 2 register (OP_*_FT)
+ "R" 5 bit fr source 3 register (OP_*_FR)
+ "V" 5 bit same register used as floating source and destination (OP_*_FS)
+ "W" 5 bit same register used as floating target and destination (OP_*_FT)
+
+ Coprocessor instructions:
+ "E" 5 bit target register (OP_*_RT)
+ "G" 5 bit destination register (OP_*_RD)
+ "H" 3 bit sel field for (d)mtc* and (d)mfc* (OP_*_SEL)
+ "P" 5 bit performance-monitor register (OP_*_PERFREG)
+ "e" 5 bit vector register byte specifier (OP_*_VECBYTE)
+ "%" 3 bit immediate vr5400 vector alignment operand (OP_*_VECALIGN)
+ see also "k" above
+ "+D" Combined destination register ("G") and sel ("H") for CP0 ops,
+ for pretty-printing in disassembly only.
+
+ Macro instructions:
+ "A" General 32 bit expression
+ "I" 32 bit immediate (value placed in imm_expr).
+ "+I" 32 bit immediate (value placed in imm2_expr).
+ "F" 64 bit floating point constant in .rdata
+ "L" 64 bit floating point constant in .lit8
+ "f" 32 bit floating point constant
+ "l" 32 bit floating point constant in .lit4
+
+ MDMX instruction operands (note that while these use the FP register
+ fields, they accept both $fN and $vN names for the registers):
+ "O" MDMX alignment offset (OP_*_ALN)
+ "Q" MDMX vector/scalar/immediate source (OP_*_VSEL and OP_*_FT)
+ "X" MDMX destination register (OP_*_FD)
+ "Y" MDMX source register (OP_*_FS)
+ "Z" MDMX source register (OP_*_FT)
+
+ DSP ASE usage:
+ "2" 2 bit unsigned immediate for byte align (OP_*_BP)
+ "3" 3 bit unsigned immediate (OP_*_SA3)
+ "4" 4 bit unsigned immediate (OP_*_SA4)
+ "5" 8 bit unsigned immediate (OP_*_IMM8)
+ "6" 5 bit unsigned immediate (OP_*_RS)
+ "7" 2 bit dsp accumulator register (OP_*_DSPACC)
+ "8" 6 bit unsigned immediate (OP_*_WRDSP)
+ "9" 2 bit dsp accumulator register (OP_*_DSPACC_S)
+ "0" 6 bit signed immediate (OP_*_DSPSFT)
+ ":" 7 bit signed immediate (OP_*_DSPSFT_7)
+ "'" 6 bit unsigned immediate (OP_*_RDDSP)
+ "@" 10 bit signed immediate (OP_*_IMM10)
+
+ MT ASE usage:
+ "!" 1 bit usermode flag (OP_*_MT_U)
+ "$" 1 bit load high flag (OP_*_MT_H)
+ "*" 2 bit dsp/smartmips accumulator register (OP_*_MTACC_T)
+ "&" 2 bit dsp/smartmips accumulator register (OP_*_MTACC_D)
+ "g" 5 bit coprocessor 1 and 2 destination register (OP_*_RD)
+ "+t" 5 bit coprocessor 0 destination register (OP_*_RT)
+ "+T" 5 bit coprocessor 0 destination register (OP_*_RT) - disassembly only
+
+ UDI immediates:
+ "+1" UDI immediate bits 6-10
+ "+2" UDI immediate bits 6-15
+ "+3" UDI immediate bits 6-20
+ "+4" UDI immediate bits 6-25
+
+ Other:
+ "()" parens surrounding optional value
+ "," separates operands
+ "[]" brackets around index for vector-op scalar operand specifier (vr5400)
+ "+" Start of extension sequence.
+
+ Characters used so far, for quick reference when adding more:
+ "234567890"
+ "%[]<>(),+:'@!$*&"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklopqrstuvwxz"
+
+ Extension character sequences used so far ("+" followed by the
+ following), for quick reference when adding more:
+ "1234"
+ "ABCDEFGHIT"
+ "t"
+*/
+
+/* These are the bits which may be set in the pinfo field of an
+ instructions, if it is not equal to INSN_MACRO. */
+
+/* Modifies the general purpose register in OP_*_RD. */
+#define INSN_WRITE_GPR_D 0x00000001
+/* Modifies the general purpose register in OP_*_RT. */
+#define INSN_WRITE_GPR_T 0x00000002
+/* Modifies general purpose register 31. */
+#define INSN_WRITE_GPR_31 0x00000004
+/* Modifies the floating point register in OP_*_FD. */
+#define INSN_WRITE_FPR_D 0x00000008
+/* Modifies the floating point register in OP_*_FS. */
+#define INSN_WRITE_FPR_S 0x00000010
+/* Modifies the floating point register in OP_*_FT. */
+#define INSN_WRITE_FPR_T 0x00000020
+/* Reads the general purpose register in OP_*_RS. */
+#define INSN_READ_GPR_S 0x00000040
+/* Reads the general purpose register in OP_*_RT. */
+#define INSN_READ_GPR_T 0x00000080
+/* Reads the floating point register in OP_*_FS. */
+#define INSN_READ_FPR_S 0x00000100
+/* Reads the floating point register in OP_*_FT. */
+#define INSN_READ_FPR_T 0x00000200
+/* Reads the floating point register in OP_*_FR. */
+#define INSN_READ_FPR_R 0x00000400
+/* Modifies coprocessor condition code. */
+#define INSN_WRITE_COND_CODE 0x00000800
+/* Reads coprocessor condition code. */
+#define INSN_READ_COND_CODE 0x00001000
+/* TLB operation. */
+#define INSN_TLB 0x00002000
+/* Reads coprocessor register other than floating point register. */
+#define INSN_COP 0x00004000
+/* Instruction loads value from memory, requiring delay. */
+#define INSN_LOAD_MEMORY_DELAY 0x00008000
+/* Instruction loads value from coprocessor, requiring delay. */
+#define INSN_LOAD_COPROC_DELAY 0x00010000
+/* Instruction has unconditional branch delay slot. */
+#define INSN_UNCOND_BRANCH_DELAY 0x00020000
+/* Instruction has conditional branch delay slot. */
+#define INSN_COND_BRANCH_DELAY 0x00040000
+/* Conditional branch likely: if branch not taken, insn nullified. */
+#define INSN_COND_BRANCH_LIKELY 0x00080000
+/* Moves to coprocessor register, requiring delay. */
+#define INSN_COPROC_MOVE_DELAY 0x00100000
+/* Loads coprocessor register from memory, requiring delay. */
+#define INSN_COPROC_MEMORY_DELAY 0x00200000
+/* Reads the HI register. */
+#define INSN_READ_HI 0x00400000
+/* Reads the LO register. */
+#define INSN_READ_LO 0x00800000
+/* Modifies the HI register. */
+#define INSN_WRITE_HI 0x01000000
+/* Modifies the LO register. */
+#define INSN_WRITE_LO 0x02000000
+/* Takes a trap (easier to keep out of delay slot). */
+#define INSN_TRAP 0x04000000
+/* Instruction stores value into memory. */
+#define INSN_STORE_MEMORY 0x08000000
+/* Instruction uses single precision floating point. */
+#define FP_S 0x10000000
+/* Instruction uses double precision floating point. */
+#define FP_D 0x20000000
+/* Instruction is part of the tx39's integer multiply family. */
+#define INSN_MULT 0x40000000
+/* Instruction synchronize shared memory. */
+#define INSN_SYNC 0x80000000
+
+/* These are the bits which may be set in the pinfo2 field of an
+ instruction. */
+
+/* Instruction is a simple alias (I.E. "move" for daddu/addu/or) */
+#define INSN2_ALIAS 0x00000001
+/* Instruction reads MDMX accumulator. */
+#define INSN2_READ_MDMX_ACC 0x00000002
+/* Instruction writes MDMX accumulator. */
+#define INSN2_WRITE_MDMX_ACC 0x00000004
+
+/* Instruction is actually a macro. It should be ignored by the
+ disassembler, and requires special treatment by the assembler. */
+#define INSN_MACRO 0xffffffff
+
+/* Masks used to mark instructions to indicate which MIPS ISA level
+ they were introduced in. ISAs, as defined below, are logical
+ ORs of these bits, indicating that they support the instructions
+ defined at the given level. */
+
+#define INSN_ISA_MASK 0x00000fff
+#define INSN_ISA1 0x00000001
+#define INSN_ISA2 0x00000002
+#define INSN_ISA3 0x00000004
+#define INSN_ISA4 0x00000008
+#define INSN_ISA5 0x00000010
+#define INSN_ISA32 0x00000020
+#define INSN_ISA64 0x00000040
+#define INSN_ISA32R2 0x00000080
+#define INSN_ISA64R2 0x00000100
+
+/* Masks used for MIPS-defined ASEs. */
+#define INSN_ASE_MASK 0x0000f000
+
+/* DSP ASE */
+#define INSN_DSP 0x00001000
+#define INSN_DSP64 0x00002000
+/* MIPS 16 ASE */
+#define INSN_MIPS16 0x00004000
+/* MIPS-3D ASE */
+#define INSN_MIPS3D 0x00008000
+
+/* Chip specific instructions. These are bitmasks. */
+
+/* MIPS R4650 instruction. */
+#define INSN_4650 0x00010000
+/* LSI R4010 instruction. */
+#define INSN_4010 0x00020000
+/* NEC VR4100 instruction. */
+#define INSN_4100 0x00040000
+/* Toshiba R3900 instruction. */
+#define INSN_3900 0x00080000
+/* MIPS R10000 instruction. */
+#define INSN_10000 0x00100000
+/* Broadcom SB-1 instruction. */
+#define INSN_SB1 0x00200000
+/* NEC VR4111/VR4181 instruction. */
+#define INSN_4111 0x00400000
+/* NEC VR4120 instruction. */
+#define INSN_4120 0x00800000
+/* NEC VR5400 instruction. */
+#define INSN_5400 0x01000000
+/* NEC VR5500 instruction. */
+#define INSN_5500 0x02000000
+
+/* MDMX ASE */
+#define INSN_MDMX 0x04000000
+/* MT ASE */
+#define INSN_MT 0x08000000
+/* SmartMIPS ASE */
+#define INSN_SMARTMIPS 0x10000000
+/* DSP R2 ASE */
+#define INSN_DSPR2 0x20000000
+
+/* ST Microelectronics Loongson 2E. */
+#define INSN_LOONGSON_2E 0x40000000
+/* ST Microelectronics Loongson 2F. */
+#define INSN_LOONGSON_2F 0x80000000
+
+/* MIPS ISA defines, use instead of hardcoding ISA level. */
+
+#define ISA_UNKNOWN 0 /* Gas internal use. */
+#define ISA_MIPS1 (INSN_ISA1)
+#define ISA_MIPS2 (ISA_MIPS1 | INSN_ISA2)
+#define ISA_MIPS3 (ISA_MIPS2 | INSN_ISA3)
+#define ISA_MIPS4 (ISA_MIPS3 | INSN_ISA4)
+#define ISA_MIPS5 (ISA_MIPS4 | INSN_ISA5)
+
+#define ISA_MIPS32 (ISA_MIPS2 | INSN_ISA32)
+#define ISA_MIPS64 (ISA_MIPS5 | INSN_ISA32 | INSN_ISA64)
+
+#define ISA_MIPS32R2 (ISA_MIPS32 | INSN_ISA32R2)
+#define ISA_MIPS64R2 (ISA_MIPS64 | INSN_ISA32R2 | INSN_ISA64R2)
+
+
+/* CPU defines, use instead of hardcoding processor number. Keep this
+ in sync with bfd/archures.c in order for machine selection to work. */
+#define CPU_UNKNOWN 0 /* Gas internal use. */
+#define CPU_R3000 3000
+#define CPU_R3900 3900
+#define CPU_R4000 4000
+#define CPU_R4010 4010
+#define CPU_VR4100 4100
+#define CPU_R4111 4111
+#define CPU_VR4120 4120
+#define CPU_R4300 4300
+#define CPU_R4400 4400
+#define CPU_R4600 4600
+#define CPU_R4650 4650
+#define CPU_R5000 5000
+#define CPU_VR5400 5400
+#define CPU_VR5500 5500
+#define CPU_R6000 6000
+#define CPU_RM7000 7000
+#define CPU_R8000 8000
+#define CPU_R10000 10000
+#define CPU_R12000 12000
+#define CPU_MIPS16 16
+#define CPU_MIPS32 32
+#define CPU_MIPS32R2 33
+#define CPU_MIPS5 5
+#define CPU_MIPS64 64
+#define CPU_MIPS64R2 65
+#define CPU_SB1 12310201 /* octal 'SB', 01. */
+
+/* Test for membership in an ISA including chip specific ISAs. INSN
+ is pointer to an element of the opcode table; ISA is the specified
+ ISA/ASE bitmask to test against; and CPU is the CPU specific ISA to
+ test, or zero if no CPU specific ISA test is desired. */
+
+#if 0
+#define OPCODE_IS_MEMBER(insn, isa, cpu) \
+ (((insn)->membership & isa) != 0 \
+ || (cpu == CPU_R4650 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_RM7000 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_RM9000 && ((insn)->membership & INSN_4650) != 0) \
+ || (cpu == CPU_R4010 && ((insn)->membership & INSN_4010) != 0) \
+ || (cpu == CPU_VR4100 && ((insn)->membership & INSN_4100) != 0) \
+ || (cpu == CPU_R3900 && ((insn)->membership & INSN_3900) != 0) \
+ || ((cpu == CPU_R10000 || cpu == CPU_R12000) \
+ && ((insn)->membership & INSN_10000) != 0) \
+ || (cpu == CPU_SB1 && ((insn)->membership & INSN_SB1) != 0) \
+ || (cpu == CPU_R4111 && ((insn)->membership & INSN_4111) != 0) \
+ || (cpu == CPU_VR4120 && ((insn)->membership & INSN_4120) != 0) \
+ || (cpu == CPU_VR5400 && ((insn)->membership & INSN_5400) != 0) \
+ || (cpu == CPU_VR5500 && ((insn)->membership & INSN_5500) != 0) \
+ || 0) /* Please keep this term for easier source merging. */
+#else
+#define OPCODE_IS_MEMBER(insn, isa, cpu) \
+ (1 != 0)
+#endif
+
+/* This is a list of macro expanded instructions.
+
+ _I appended means immediate
+ _A appended means address
+ _AB appended means address with base register
+ _D appended means 64 bit floating point constant
+ _S appended means 32 bit floating point constant. */
+
+enum
+{
+ M_ABS,
+ M_ADD_I,
+ M_ADDU_I,
+ M_AND_I,
+ M_BALIGN,
+ M_BEQ,
+ M_BEQ_I,
+ M_BEQL_I,
+ M_BGE,
+ M_BGEL,
+ M_BGE_I,
+ M_BGEL_I,
+ M_BGEU,
+ M_BGEUL,
+ M_BGEU_I,
+ M_BGEUL_I,
+ M_BGT,
+ M_BGTL,
+ M_BGT_I,
+ M_BGTL_I,
+ M_BGTU,
+ M_BGTUL,
+ M_BGTU_I,
+ M_BGTUL_I,
+ M_BLE,
+ M_BLEL,
+ M_BLE_I,
+ M_BLEL_I,
+ M_BLEU,
+ M_BLEUL,
+ M_BLEU_I,
+ M_BLEUL_I,
+ M_BLT,
+ M_BLTL,
+ M_BLT_I,
+ M_BLTL_I,
+ M_BLTU,
+ M_BLTUL,
+ M_BLTU_I,
+ M_BLTUL_I,
+ M_BNE,
+ M_BNE_I,
+ M_BNEL_I,
+ M_CACHE_AB,
+ M_DABS,
+ M_DADD_I,
+ M_DADDU_I,
+ M_DDIV_3,
+ M_DDIV_3I,
+ M_DDIVU_3,
+ M_DDIVU_3I,
+ M_DEXT,
+ M_DINS,
+ M_DIV_3,
+ M_DIV_3I,
+ M_DIVU_3,
+ M_DIVU_3I,
+ M_DLA_AB,
+ M_DLCA_AB,
+ M_DLI,
+ M_DMUL,
+ M_DMUL_I,
+ M_DMULO,
+ M_DMULO_I,
+ M_DMULOU,
+ M_DMULOU_I,
+ M_DREM_3,
+ M_DREM_3I,
+ M_DREMU_3,
+ M_DREMU_3I,
+ M_DSUB_I,
+ M_DSUBU_I,
+ M_DSUBU_I_2,
+ M_J_A,
+ M_JAL_1,
+ M_JAL_2,
+ M_JAL_A,
+ M_L_DOB,
+ M_L_DAB,
+ M_LA_AB,
+ M_LB_A,
+ M_LB_AB,
+ M_LBU_A,
+ M_LBU_AB,
+ M_LCA_AB,
+ M_LD_A,
+ M_LD_OB,
+ M_LD_AB,
+ M_LDC1_AB,
+ M_LDC2_AB,
+ M_LDC3_AB,
+ M_LDL_AB,
+ M_LDR_AB,
+ M_LH_A,
+ M_LH_AB,
+ M_LHU_A,
+ M_LHU_AB,
+ M_LI,
+ M_LI_D,
+ M_LI_DD,
+ M_LI_S,
+ M_LI_SS,
+ M_LL_AB,
+ M_LLD_AB,
+ M_LS_A,
+ M_LW_A,
+ M_LW_AB,
+ M_LWC0_A,
+ M_LWC0_AB,
+ M_LWC1_A,
+ M_LWC1_AB,
+ M_LWC2_A,
+ M_LWC2_AB,
+ M_LWC3_A,
+ M_LWC3_AB,
+ M_LWL_A,
+ M_LWL_AB,
+ M_LWR_A,
+ M_LWR_AB,
+ M_LWU_AB,
+ M_MOVE,
+ M_MUL,
+ M_MUL_I,
+ M_MULO,
+ M_MULO_I,
+ M_MULOU,
+ M_MULOU_I,
+ M_NOR_I,
+ M_OR_I,
+ M_REM_3,
+ M_REM_3I,
+ M_REMU_3,
+ M_REMU_3I,
+ M_DROL,
+ M_ROL,
+ M_DROL_I,
+ M_ROL_I,
+ M_DROR,
+ M_ROR,
+ M_DROR_I,
+ M_ROR_I,
+ M_S_DA,
+ M_S_DOB,
+ M_S_DAB,
+ M_S_S,
+ M_SC_AB,
+ M_SCD_AB,
+ M_SD_A,
+ M_SD_OB,
+ M_SD_AB,
+ M_SDC1_AB,
+ M_SDC2_AB,
+ M_SDC3_AB,
+ M_SDL_AB,
+ M_SDR_AB,
+ M_SEQ,
+ M_SEQ_I,
+ M_SGE,
+ M_SGE_I,
+ M_SGEU,
+ M_SGEU_I,
+ M_SGT,
+ M_SGT_I,
+ M_SGTU,
+ M_SGTU_I,
+ M_SLE,
+ M_SLE_I,
+ M_SLEU,
+ M_SLEU_I,
+ M_SLT_I,
+ M_SLTU_I,
+ M_SNE,
+ M_SNE_I,
+ M_SB_A,
+ M_SB_AB,
+ M_SH_A,
+ M_SH_AB,
+ M_SW_A,
+ M_SW_AB,
+ M_SWC0_A,
+ M_SWC0_AB,
+ M_SWC1_A,
+ M_SWC1_AB,
+ M_SWC2_A,
+ M_SWC2_AB,
+ M_SWC3_A,
+ M_SWC3_AB,
+ M_SWL_A,
+ M_SWL_AB,
+ M_SWR_A,
+ M_SWR_AB,
+ M_SUB_I,
+ M_SUBU_I,
+ M_SUBU_I_2,
+ M_TEQ_I,
+ M_TGE_I,
+ M_TGEU_I,
+ M_TLT_I,
+ M_TLTU_I,
+ M_TNE_I,
+ M_TRUNCWD,
+ M_TRUNCWS,
+ M_ULD,
+ M_ULD_A,
+ M_ULH,
+ M_ULH_A,
+ M_ULHU,
+ M_ULHU_A,
+ M_ULW,
+ M_ULW_A,
+ M_USH,
+ M_USH_A,
+ M_USW,
+ M_USW_A,
+ M_USD,
+ M_USD_A,
+ M_XOR_I,
+ M_COP0,
+ M_COP1,
+ M_COP2,
+ M_COP3,
+ M_NUM_MACROS
+};
+
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+extern const struct mips_opcode mips_builtin_opcodes[];
+extern const int bfd_mips_num_builtin_opcodes;
+extern struct mips_opcode *mips_opcodes;
+extern int bfd_mips_num_opcodes;
+#define NUMOPCODES bfd_mips_num_opcodes
+
+
+/* The rest of this file adds definitions for the mips16 TinyRISC
+ processor. */
+
+/* These are the bitmasks and shift counts used for the different
+ fields in the instruction formats. Other than OP, no masks are
+ provided for the fixed portions of an instruction, since they are
+ not needed.
+
+ The I format uses IMM11.
+
+ The RI format uses RX and IMM8.
+
+ The RR format uses RX, and RY.
+
+ The RRI format uses RX, RY, and IMM5.
+
+ The RRR format uses RX, RY, and RZ.
+
+ The RRI_A format uses RX, RY, and IMM4.
+
+ The SHIFT format uses RX, RY, and SHAMT.
+
+ The I8 format uses IMM8.
+
+ The I8_MOVR32 format uses RY and REGR32.
+
+ The IR_MOV32R format uses REG32R and MOV32Z.
+
+ The I64 format uses IMM8.
+
+ The RI64 format uses RY and IMM5.
+ */
+
+#define MIPS16OP_MASK_OP 0x1f
+#define MIPS16OP_SH_OP 11
+#define MIPS16OP_MASK_IMM11 0x7ff
+#define MIPS16OP_SH_IMM11 0
+#define MIPS16OP_MASK_RX 0x7
+#define MIPS16OP_SH_RX 8
+#define MIPS16OP_MASK_IMM8 0xff
+#define MIPS16OP_SH_IMM8 0
+#define MIPS16OP_MASK_RY 0x7
+#define MIPS16OP_SH_RY 5
+#define MIPS16OP_MASK_IMM5 0x1f
+#define MIPS16OP_SH_IMM5 0
+#define MIPS16OP_MASK_RZ 0x7
+#define MIPS16OP_SH_RZ 2
+#define MIPS16OP_MASK_IMM4 0xf
+#define MIPS16OP_SH_IMM4 0
+#define MIPS16OP_MASK_REGR32 0x1f
+#define MIPS16OP_SH_REGR32 0
+#define MIPS16OP_MASK_REG32R 0x1f
+#define MIPS16OP_SH_REG32R 3
+#define MIPS16OP_EXTRACT_REG32R(i) ((((i) >> 5) & 7) | ((i) & 0x18))
+#define MIPS16OP_MASK_MOVE32Z 0x7
+#define MIPS16OP_SH_MOVE32Z 0
+#define MIPS16OP_MASK_IMM6 0x3f
+#define MIPS16OP_SH_IMM6 5
+
+/* These are the characters which may appears in the args field of an
+ instruction. They appear in the order in which the fields appear
+ when the instruction is used. Commas and parentheses in the args
+ string are ignored when assembling, and written into the output
+ when disassembling.
+
+ "y" 3 bit register (MIPS16OP_*_RY)
+ "x" 3 bit register (MIPS16OP_*_RX)
+ "z" 3 bit register (MIPS16OP_*_RZ)
+ "Z" 3 bit register (MIPS16OP_*_MOVE32Z)
+ "v" 3 bit same register as source and destination (MIPS16OP_*_RX)
+ "w" 3 bit same register as source and destination (MIPS16OP_*_RY)
+ "0" zero register ($0)
+ "S" stack pointer ($sp or $29)
+ "P" program counter
+ "R" return address register ($ra or $31)
+ "X" 5 bit MIPS register (MIPS16OP_*_REGR32)
+ "Y" 5 bit MIPS register (MIPS16OP_*_REG32R)
+ "6" 6 bit unsigned break code (MIPS16OP_*_IMM6)
+ "a" 26 bit jump address
+ "e" 11 bit extension value
+ "l" register list for entry instruction
+ "L" register list for exit instruction
+
+ The remaining codes may be extended. Except as otherwise noted,
+ the full extended operand is a 16 bit signed value.
+ "<" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 5 bit unsigned)
+ ">" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 5 bit unsigned)
+ "[" 3 bit unsigned shift count * 0 (MIPS16OP_*_RZ) (full 6 bit unsigned)
+ "]" 3 bit unsigned shift count * 0 (MIPS16OP_*_RX) (full 6 bit unsigned)
+ "4" 4 bit signed immediate * 0 (MIPS16OP_*_IMM4) (full 15 bit signed)
+ "5" 5 bit unsigned immediate * 0 (MIPS16OP_*_IMM5)
+ "H" 5 bit unsigned immediate * 2 (MIPS16OP_*_IMM5)
+ "W" 5 bit unsigned immediate * 4 (MIPS16OP_*_IMM5)
+ "D" 5 bit unsigned immediate * 8 (MIPS16OP_*_IMM5)
+ "j" 5 bit signed immediate * 0 (MIPS16OP_*_IMM5)
+ "8" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8)
+ "V" 8 bit unsigned immediate * 4 (MIPS16OP_*_IMM8)
+ "C" 8 bit unsigned immediate * 8 (MIPS16OP_*_IMM8)
+ "U" 8 bit unsigned immediate * 0 (MIPS16OP_*_IMM8) (full 16 bit unsigned)
+ "k" 8 bit signed immediate * 0 (MIPS16OP_*_IMM8)
+ "K" 8 bit signed immediate * 8 (MIPS16OP_*_IMM8)
+ "p" 8 bit conditional branch address (MIPS16OP_*_IMM8)
+ "q" 11 bit branch address (MIPS16OP_*_IMM11)
+ "A" 8 bit PC relative address * 4 (MIPS16OP_*_IMM8)
+ "B" 5 bit PC relative address * 8 (MIPS16OP_*_IMM5)
+ "E" 5 bit PC relative address * 4 (MIPS16OP_*_IMM5)
+ */
+
+/* Save/restore encoding for the args field when all 4 registers are
+ either saved as arguments or saved/restored as statics. */
+#define MIPS16_ALL_ARGS 0xe
+#define MIPS16_ALL_STATICS 0xb
+
+/* For the mips16, we use the same opcode table format and a few of
+ the same flags. However, most of the flags are different. */
+
+/* Modifies the register in MIPS16OP_*_RX. */
+#define MIPS16_INSN_WRITE_X 0x00000001
+/* Modifies the register in MIPS16OP_*_RY. */
+#define MIPS16_INSN_WRITE_Y 0x00000002
+/* Modifies the register in MIPS16OP_*_RZ. */
+#define MIPS16_INSN_WRITE_Z 0x00000004
+/* Modifies the T ($24) register. */
+#define MIPS16_INSN_WRITE_T 0x00000008
+/* Modifies the SP ($29) register. */
+#define MIPS16_INSN_WRITE_SP 0x00000010
+/* Modifies the RA ($31) register. */
+#define MIPS16_INSN_WRITE_31 0x00000020
+/* Modifies the general purpose register in MIPS16OP_*_REG32R. */
+#define MIPS16_INSN_WRITE_GPR_Y 0x00000040
+/* Reads the register in MIPS16OP_*_RX. */
+#define MIPS16_INSN_READ_X 0x00000080
+/* Reads the register in MIPS16OP_*_RY. */
+#define MIPS16_INSN_READ_Y 0x00000100
+/* Reads the register in MIPS16OP_*_MOVE32Z. */
+#define MIPS16_INSN_READ_Z 0x00000200
+/* Reads the T ($24) register. */
+#define MIPS16_INSN_READ_T 0x00000400
+/* Reads the SP ($29) register. */
+#define MIPS16_INSN_READ_SP 0x00000800
+/* Reads the RA ($31) register. */
+#define MIPS16_INSN_READ_31 0x00001000
+/* Reads the program counter. */
+#define MIPS16_INSN_READ_PC 0x00002000
+/* Reads the general purpose register in MIPS16OP_*_REGR32. */
+#define MIPS16_INSN_READ_GPR_X 0x00004000
+/* Is a branch insn. */
+#define MIPS16_INSN_BRANCH 0x00010000
+
+/* The following flags have the same value for the mips16 opcode
+ table:
+ INSN_UNCOND_BRANCH_DELAY
+ INSN_COND_BRANCH_DELAY
+ INSN_COND_BRANCH_LIKELY (never used)
+ INSN_READ_HI
+ INSN_READ_LO
+ INSN_WRITE_HI
+ INSN_WRITE_LO
+ INSN_TRAP
+ INSN_ISA3
+ */
+
+extern const struct mips_opcode mips16_opcodes[];
+extern const int bfd_mips16_num_opcodes;
+
+/* Short hand so the lines aren't too long. */
+
+#define LDD INSN_LOAD_MEMORY_DELAY
+#define LCD INSN_LOAD_COPROC_DELAY
+#define UBD INSN_UNCOND_BRANCH_DELAY
+#define CBD INSN_COND_BRANCH_DELAY
+#define COD INSN_COPROC_MOVE_DELAY
+#define CLD INSN_COPROC_MEMORY_DELAY
+#define CBL INSN_COND_BRANCH_LIKELY
+#define TRAP INSN_TRAP
+#define SM INSN_STORE_MEMORY
+
+#define WR_d INSN_WRITE_GPR_D
+#define WR_t INSN_WRITE_GPR_T
+#define WR_31 INSN_WRITE_GPR_31
+#define WR_D INSN_WRITE_FPR_D
+#define WR_T INSN_WRITE_FPR_T
+#define WR_S INSN_WRITE_FPR_S
+#define RD_s INSN_READ_GPR_S
+#define RD_b INSN_READ_GPR_S
+#define RD_t INSN_READ_GPR_T
+#define RD_S INSN_READ_FPR_S
+#define RD_T INSN_READ_FPR_T
+#define RD_R INSN_READ_FPR_R
+#define WR_CC INSN_WRITE_COND_CODE
+#define RD_CC INSN_READ_COND_CODE
+#define RD_C0 INSN_COP
+#define RD_C1 INSN_COP
+#define RD_C2 INSN_COP
+#define RD_C3 INSN_COP
+#define WR_C0 INSN_COP
+#define WR_C1 INSN_COP
+#define WR_C2 INSN_COP
+#define WR_C3 INSN_COP
+
+#define WR_HI INSN_WRITE_HI
+#define RD_HI INSN_READ_HI
+#define MOD_HI WR_HI|RD_HI
+
+#define WR_LO INSN_WRITE_LO
+#define RD_LO INSN_READ_LO
+#define MOD_LO WR_LO|RD_LO
+
+#define WR_HILO WR_HI|WR_LO
+#define RD_HILO RD_HI|RD_LO
+#define MOD_HILO WR_HILO|RD_HILO
+
+#define IS_M INSN_MULT
+
+#define WR_MACC INSN2_WRITE_MDMX_ACC
+#define RD_MACC INSN2_READ_MDMX_ACC
+
+#define I1 INSN_ISA1
+#define I2 INSN_ISA2
+#define I3 INSN_ISA3
+#define I4 INSN_ISA4
+#define I5 INSN_ISA5
+#define I32 INSN_ISA32
+#define I64 INSN_ISA64
+#define I33 INSN_ISA32R2
+#define I65 INSN_ISA64R2
+
+/* MIPS64 MIPS-3D ASE support. */
+#define I16 INSN_MIPS16
+
+/* MIPS32 SmartMIPS ASE support. */
+#define SMT INSN_SMARTMIPS
+
+/* MIPS64 MIPS-3D ASE support. */
+#define M3D INSN_MIPS3D
+
+/* MIPS64 MDMX ASE support. */
+#define MX INSN_MDMX
+
+#define IL2E (INSN_LOONGSON_2E)
+#define IL2F (INSN_LOONGSON_2F)
+
+#define P3 INSN_4650
+#define L1 INSN_4010
+#define V1 (INSN_4100 | INSN_4111 | INSN_4120)
+#define T3 INSN_3900
+#define M1 INSN_10000
+#define SB1 INSN_SB1
+#define N411 INSN_4111
+#define N412 INSN_4120
+#define N5 (INSN_5400 | INSN_5500)
+#define N54 INSN_5400
+#define N55 INSN_5500
+
+#define G1 (T3 \
+ )
+
+#define G2 (T3 \
+ )
+
+#define G3 (I4 \
+ )
+
+/* MIPS DSP ASE support.
+ NOTE:
+ 1. MIPS DSP ASE includes 4 accumulators ($ac0 - $ac3). $ac0 is the pair
+ of original HI and LO. $ac1, $ac2 and $ac3 are new registers, and have
+ the same structure as $ac0 (HI + LO). For DSP instructions that write or
+ read accumulators (that may be $ac0), we add WR_a (WR_HILO) or RD_a
+ (RD_HILO) attributes, such that HILO dependencies are maintained
+ conservatively.
+
+ 2. For some mul. instructions that use integer registers as destinations
+ but destroy HI+LO as side-effect, we add WR_HILO to their attributes.
+
+ 3. MIPS DSP ASE includes a new DSP control register, which has 6 fields
+ (ccond, outflag, EFI, c, scount, pos). Many DSP instructions read or write
+ certain fields of the DSP control register. For simplicity, we decide not
+ to track dependencies of these fields.
+ However, "bposge32" is a branch instruction that depends on the "pos"
+ field. In order to make sure that GAS does not reorder DSP instructions
+ that writes the "pos" field and "bposge32", we add DSP_VOLA (INSN_TRAP)
+ attribute to those instructions that write the "pos" field. */
+
+#define WR_a WR_HILO /* Write dsp accumulators (reuse WR_HILO) */
+#define RD_a RD_HILO /* Read dsp accumulators (reuse RD_HILO) */
+#define MOD_a WR_a|RD_a
+#define DSP_VOLA INSN_TRAP
+#define D32 INSN_DSP
+#define D33 INSN_DSPR2
+#define D64 INSN_DSP64
+
+/* MIPS MT ASE support. */
+#define MT32 INSN_MT
+
+/* The order of overloaded instructions matters. Label arguments and
+ register arguments look the same. Instructions that can have either
+ for arguments must apear in the correct order in this table for the
+ assembler to pick the right one. In other words, entries with
+ immediate operands must apear after the same instruction with
+ registers.
+
+ Because of the lookup algorithm used, entries with the same opcode
+ name must be contiguous.
+
+ Many instructions are short hand for other instructions (i.e., The
+ jal <register> instruction is short for jalr <register>). */
+
+const struct mips_opcode mips_builtin_opcodes[] =
+{
+/* These instructions appear first so that the disassembler will find
+ them first. The assemblers uses a hash table based on the
+ instruction name anyhow. */
+/* name, args, match, mask, pinfo, membership */
+{"pref", "k,o(b)", 0xcc000000, 0xfc000000, RD_b, 0, I4|I32|G3 },
+{"prefx", "h,t(b)", 0x4c00000f, 0xfc0007ff, RD_b|RD_t, 0, I4|I33 },
+{"nop", "", 0x00000000, 0xffffffff, 0, INSN2_ALIAS, I1 }, /* sll */
+{"ssnop", "", 0x00000040, 0xffffffff, 0, INSN2_ALIAS, I32|N55 }, /* sll */
+{"ehb", "", 0x000000c0, 0xffffffff, 0, INSN2_ALIAS, I33 }, /* sll */
+{"li", "t,j", 0x24000000, 0xffe00000, WR_t, INSN2_ALIAS, I1 }, /* addiu */
+{"li", "t,i", 0x34000000, 0xffe00000, WR_t, INSN2_ALIAS, I1 }, /* ori */
+{"li", "t,I", 0, (int) M_LI, INSN_MACRO, 0, I1 },
+{"move", "d,s", 0, (int) M_MOVE, INSN_MACRO, 0, I1 },
+{"move", "d,s", 0x0000002d, 0xfc1f07ff, WR_d|RD_s, INSN2_ALIAS, I3 },/* daddu */
+{"move", "d,s", 0x00000021, 0xfc1f07ff, WR_d|RD_s, INSN2_ALIAS, I1 },/* addu */
+{"move", "d,s", 0x00000025, 0xfc1f07ff, WR_d|RD_s, INSN2_ALIAS, I1 },/* or */
+{"b", "p", 0x10000000, 0xffff0000, UBD, INSN2_ALIAS, I1 },/* beq 0,0 */
+{"b", "p", 0x04010000, 0xffff0000, UBD, INSN2_ALIAS, I1 },/* bgez 0 */
+{"bal", "p", 0x04110000, 0xffff0000, UBD|WR_31, INSN2_ALIAS, I1 },/* bgezal 0*/
+
+{"abs", "d,v", 0, (int) M_ABS, INSN_MACRO, 0, I1 },
+{"abs.s", "D,V", 0x46000005, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 },
+{"abs.d", "D,V", 0x46200005, 0xffff003f, WR_D|RD_S|FP_D, 0, I1 },
+{"abs.ps", "D,V", 0x46c00005, 0xffff003f, WR_D|RD_S|FP_D, 0, I5|I33 },
+{"add", "d,v,t", 0x00000020, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"add", "t,r,I", 0, (int) M_ADD_I, INSN_MACRO, 0, I1 },
+{"add.s", "D,V,T", 0x46000000, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 },
+{"add.d", "D,V,T", 0x46200000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 },
+{"add.ob", "X,Y,Q", 0x7800000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"add.ob", "D,S,T", 0x4ac0000b, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"add.ob", "D,S,T[e]", 0x4800000b, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"add.ob", "D,S,k", 0x4bc0000b, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"add.ps", "D,V,T", 0x46c00000, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"add.qh", "X,Y,Q", 0x7820000b, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"adda.ob", "Y,Q", 0x78000037, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"adda.qh", "Y,Q", 0x78200037, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"addi", "t,r,j", 0x20000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+{"addiu", "t,r,j", 0x24000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+{"addl.ob", "Y,Q", 0x78000437, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"addl.qh", "Y,Q", 0x78200437, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"addr.ps", "D,S,T", 0x46c00018, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D },
+{"addu", "d,v,t", 0x00000021, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"addu", "t,r,I", 0, (int) M_ADDU_I, INSN_MACRO, 0, I1 },
+{"alni.ob", "X,Y,Z,O", 0x78000018, 0xff00003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"alni.ob", "D,S,T,%", 0x48000018, 0xff00003f, WR_D|RD_S|RD_T, 0, N54 },
+{"alni.qh", "X,Y,Z,O", 0x7800001a, 0xff00003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"alnv.ps", "D,V,T,s", 0x4c00001e, 0xfc00003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"alnv.ob", "X,Y,Z,s", 0x78000019, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, 0, MX|SB1 },
+{"alnv.qh", "X,Y,Z,s", 0x7800001b, 0xfc00003f, WR_D|RD_S|RD_T|RD_s|FP_D, 0, MX },
+{"and", "d,v,t", 0x00000024, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"and", "t,r,I", 0, (int) M_AND_I, INSN_MACRO, 0, I1 },
+{"and.ob", "X,Y,Q", 0x7800000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"and.ob", "D,S,T", 0x4ac0000c, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"and.ob", "D,S,T[e]", 0x4800000c, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"and.ob", "D,S,k", 0x4bc0000c, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"and.qh", "X,Y,Q", 0x7820000c, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"andi", "t,r,i", 0x30000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+/* b is at the top of the table. */
+/* bal is at the top of the table. */
+/* bc0[tf]l? are at the bottom of the table. */
+{"bc1any2f", "N,p", 0x45200000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D },
+{"bc1any2t", "N,p", 0x45210000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D },
+{"bc1any4f", "N,p", 0x45400000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D },
+{"bc1any4t", "N,p", 0x45410000, 0xffe30000, CBD|RD_CC|FP_S, 0, M3D },
+{"bc1f", "p", 0x45000000, 0xffff0000, CBD|RD_CC|FP_S, 0, I1 },
+{"bc1f", "N,p", 0x45000000, 0xffe30000, CBD|RD_CC|FP_S, 0, I4|I32 },
+{"bc1fl", "p", 0x45020000, 0xffff0000, CBL|RD_CC|FP_S, 0, I2|T3 },
+{"bc1fl", "N,p", 0x45020000, 0xffe30000, CBL|RD_CC|FP_S, 0, I4|I32 },
+{"bc1t", "p", 0x45010000, 0xffff0000, CBD|RD_CC|FP_S, 0, I1 },
+{"bc1t", "N,p", 0x45010000, 0xffe30000, CBD|RD_CC|FP_S, 0, I4|I32 },
+{"bc1tl", "p", 0x45030000, 0xffff0000, CBL|RD_CC|FP_S, 0, I2|T3 },
+{"bc1tl", "N,p", 0x45030000, 0xffe30000, CBL|RD_CC|FP_S, 0, I4|I32 },
+/* bc2* are at the bottom of the table. */
+/* bc3* are at the bottom of the table. */
+{"beqz", "s,p", 0x10000000, 0xfc1f0000, CBD|RD_s, 0, I1 },
+{"beqzl", "s,p", 0x50000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 },
+{"beq", "s,t,p", 0x10000000, 0xfc000000, CBD|RD_s|RD_t, 0, I1 },
+{"beq", "s,I,p", 0, (int) M_BEQ_I, INSN_MACRO, 0, I1 },
+{"beql", "s,t,p", 0x50000000, 0xfc000000, CBL|RD_s|RD_t, 0, I2|T3 },
+{"beql", "s,I,p", 0, (int) M_BEQL_I, INSN_MACRO, 0, I2|T3 },
+{"bge", "s,t,p", 0, (int) M_BGE, INSN_MACRO, 0, I1 },
+{"bge", "s,I,p", 0, (int) M_BGE_I, INSN_MACRO, 0, I1 },
+{"bgel", "s,t,p", 0, (int) M_BGEL, INSN_MACRO, 0, I2|T3 },
+{"bgel", "s,I,p", 0, (int) M_BGEL_I, INSN_MACRO, 0, I2|T3 },
+{"bgeu", "s,t,p", 0, (int) M_BGEU, INSN_MACRO, 0, I1 },
+{"bgeu", "s,I,p", 0, (int) M_BGEU_I, INSN_MACRO, 0, I1 },
+{"bgeul", "s,t,p", 0, (int) M_BGEUL, INSN_MACRO, 0, I2|T3 },
+{"bgeul", "s,I,p", 0, (int) M_BGEUL_I, INSN_MACRO, 0, I2|T3 },
+{"bgez", "s,p", 0x04010000, 0xfc1f0000, CBD|RD_s, 0, I1 },
+{"bgezl", "s,p", 0x04030000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 },
+{"bgezal", "s,p", 0x04110000, 0xfc1f0000, CBD|RD_s|WR_31, 0, I1 },
+{"bgezall", "s,p", 0x04130000, 0xfc1f0000, CBL|RD_s|WR_31, 0, I2|T3 },
+{"bgt", "s,t,p", 0, (int) M_BGT, INSN_MACRO, 0, I1 },
+{"bgt", "s,I,p", 0, (int) M_BGT_I, INSN_MACRO, 0, I1 },
+{"bgtl", "s,t,p", 0, (int) M_BGTL, INSN_MACRO, 0, I2|T3 },
+{"bgtl", "s,I,p", 0, (int) M_BGTL_I, INSN_MACRO, 0, I2|T3 },
+{"bgtu", "s,t,p", 0, (int) M_BGTU, INSN_MACRO, 0, I1 },
+{"bgtu", "s,I,p", 0, (int) M_BGTU_I, INSN_MACRO, 0, I1 },
+{"bgtul", "s,t,p", 0, (int) M_BGTUL, INSN_MACRO, 0, I2|T3 },
+{"bgtul", "s,I,p", 0, (int) M_BGTUL_I, INSN_MACRO, 0, I2|T3 },
+{"bgtz", "s,p", 0x1c000000, 0xfc1f0000, CBD|RD_s, 0, I1 },
+{"bgtzl", "s,p", 0x5c000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 },
+{"ble", "s,t,p", 0, (int) M_BLE, INSN_MACRO, 0, I1 },
+{"ble", "s,I,p", 0, (int) M_BLE_I, INSN_MACRO, 0, I1 },
+{"blel", "s,t,p", 0, (int) M_BLEL, INSN_MACRO, 0, I2|T3 },
+{"blel", "s,I,p", 0, (int) M_BLEL_I, INSN_MACRO, 0, I2|T3 },
+{"bleu", "s,t,p", 0, (int) M_BLEU, INSN_MACRO, 0, I1 },
+{"bleu", "s,I,p", 0, (int) M_BLEU_I, INSN_MACRO, 0, I1 },
+{"bleul", "s,t,p", 0, (int) M_BLEUL, INSN_MACRO, 0, I2|T3 },
+{"bleul", "s,I,p", 0, (int) M_BLEUL_I, INSN_MACRO, 0, I2|T3 },
+{"blez", "s,p", 0x18000000, 0xfc1f0000, CBD|RD_s, 0, I1 },
+{"blezl", "s,p", 0x58000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 },
+{"blt", "s,t,p", 0, (int) M_BLT, INSN_MACRO, 0, I1 },
+{"blt", "s,I,p", 0, (int) M_BLT_I, INSN_MACRO, 0, I1 },
+{"bltl", "s,t,p", 0, (int) M_BLTL, INSN_MACRO, 0, I2|T3 },
+{"bltl", "s,I,p", 0, (int) M_BLTL_I, INSN_MACRO, 0, I2|T3 },
+{"bltu", "s,t,p", 0, (int) M_BLTU, INSN_MACRO, 0, I1 },
+{"bltu", "s,I,p", 0, (int) M_BLTU_I, INSN_MACRO, 0, I1 },
+{"bltul", "s,t,p", 0, (int) M_BLTUL, INSN_MACRO, 0, I2|T3 },
+{"bltul", "s,I,p", 0, (int) M_BLTUL_I, INSN_MACRO, 0, I2|T3 },
+{"bltz", "s,p", 0x04000000, 0xfc1f0000, CBD|RD_s, 0, I1 },
+{"bltzl", "s,p", 0x04020000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 },
+{"bltzal", "s,p", 0x04100000, 0xfc1f0000, CBD|RD_s|WR_31, 0, I1 },
+{"bltzall", "s,p", 0x04120000, 0xfc1f0000, CBL|RD_s|WR_31, 0, I2|T3 },
+{"bnez", "s,p", 0x14000000, 0xfc1f0000, CBD|RD_s, 0, I1 },
+{"bnezl", "s,p", 0x54000000, 0xfc1f0000, CBL|RD_s, 0, I2|T3 },
+{"bne", "s,t,p", 0x14000000, 0xfc000000, CBD|RD_s|RD_t, 0, I1 },
+{"bne", "s,I,p", 0, (int) M_BNE_I, INSN_MACRO, 0, I1 },
+{"bnel", "s,t,p", 0x54000000, 0xfc000000, CBL|RD_s|RD_t, 0, I2|T3 },
+{"bnel", "s,I,p", 0, (int) M_BNEL_I, INSN_MACRO, 0, I2|T3 },
+{"break", "", 0x0000000d, 0xffffffff, TRAP, 0, I1 },
+{"break", "c", 0x0000000d, 0xfc00ffff, TRAP, 0, I1 },
+{"break", "c,q", 0x0000000d, 0xfc00003f, TRAP, 0, I1 },
+{"c.f.d", "S,T", 0x46200030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.f.d", "M,S,T", 0x46200030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.f.s", "S,T", 0x46000030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.f.s", "M,S,T", 0x46000030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.f.ps", "S,T", 0x46c00030, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.f.ps", "M,S,T", 0x46c00030, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.un.d", "S,T", 0x46200031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.un.d", "M,S,T", 0x46200031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.un.s", "S,T", 0x46000031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.un.s", "M,S,T", 0x46000031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.un.ps", "S,T", 0x46c00031, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.un.ps", "M,S,T", 0x46c00031, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.eq.d", "S,T", 0x46200032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.eq.d", "M,S,T", 0x46200032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.eq.s", "S,T", 0x46000032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.eq.s", "M,S,T", 0x46000032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.eq.ob", "Y,Q", 0x78000001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"c.eq.ob", "S,T", 0x4ac00001, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.eq.ob", "S,T[e]", 0x48000001, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.eq.ob", "S,k", 0x4bc00001, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.eq.ps", "S,T", 0x46c00032, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.eq.ps", "M,S,T", 0x46c00032, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.eq.qh", "Y,Q", 0x78200001, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX },
+{"c.ueq.d", "S,T", 0x46200033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ueq.d", "M,S,T", 0x46200033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ueq.s", "S,T", 0x46000033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ueq.s", "M,S,T", 0x46000033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ueq.ps","S,T", 0x46c00033, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ueq.ps","M,S,T", 0x46c00033, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.olt.d", "S,T", 0x46200034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.olt.d", "M,S,T", 0x46200034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.olt.s", "S,T", 0x46000034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.olt.s", "M,S,T", 0x46000034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.olt.ps","S,T", 0x46c00034, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.olt.ps","M,S,T", 0x46c00034, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ult.d", "S,T", 0x46200035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ult.d", "M,S,T", 0x46200035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ult.s", "S,T", 0x46000035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ult.s", "M,S,T", 0x46000035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ult.ps","S,T", 0x46c00035, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ult.ps","M,S,T", 0x46c00035, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ole.d", "S,T", 0x46200036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ole.d", "M,S,T", 0x46200036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ole.s", "S,T", 0x46000036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ole.s", "M,S,T", 0x46000036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ole.ps","S,T", 0x46c00036, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ole.ps","M,S,T", 0x46c00036, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ule.d", "S,T", 0x46200037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ule.d", "M,S,T", 0x46200037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ule.s", "S,T", 0x46000037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ule.s", "M,S,T", 0x46000037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ule.ps","S,T", 0x46c00037, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ule.ps","M,S,T", 0x46c00037, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.sf.d", "S,T", 0x46200038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.sf.d", "M,S,T", 0x46200038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.sf.s", "S,T", 0x46000038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.sf.s", "M,S,T", 0x46000038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.sf.ps", "S,T", 0x46c00038, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.sf.ps", "M,S,T", 0x46c00038, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ngle.d","S,T", 0x46200039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ngle.d","M,S,T", 0x46200039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ngle.s","S,T", 0x46000039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ngle.s","M,S,T", 0x46000039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ngle.ps","S,T", 0x46c00039, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ngle.ps","M,S,T", 0x46c00039, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.seq.d", "S,T", 0x4620003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.seq.d", "M,S,T", 0x4620003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.seq.s", "S,T", 0x4600003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.seq.s", "M,S,T", 0x4600003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.seq.ps","S,T", 0x46c0003a, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.seq.ps","M,S,T", 0x46c0003a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ngl.d", "S,T", 0x4620003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ngl.d", "M,S,T", 0x4620003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ngl.s", "S,T", 0x4600003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ngl.s", "M,S,T", 0x4600003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ngl.ps","S,T", 0x46c0003b, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ngl.ps","M,S,T", 0x46c0003b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.lt.d", "S,T", 0x4620003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.lt.d", "M,S,T", 0x4620003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.lt.s", "S,T", 0x4600003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.lt.s", "M,S,T", 0x4600003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.lt.ob", "Y,Q", 0x78000004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"c.lt.ob", "S,T", 0x4ac00004, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.lt.ob", "S,T[e]", 0x48000004, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.lt.ob", "S,k", 0x4bc00004, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.lt.ps", "S,T", 0x46c0003c, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.lt.ps", "M,S,T", 0x46c0003c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.lt.qh", "Y,Q", 0x78200004, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX },
+{"c.nge.d", "S,T", 0x4620003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.nge.d", "M,S,T", 0x4620003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.nge.s", "S,T", 0x4600003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.nge.s", "M,S,T", 0x4600003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.nge.ps","S,T", 0x46c0003d, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.nge.ps","M,S,T", 0x46c0003d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.le.d", "S,T", 0x4620003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.le.d", "M,S,T", 0x4620003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.le.s", "S,T", 0x4600003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.le.s", "M,S,T", 0x4600003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.le.ob", "Y,Q", 0x78000005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"c.le.ob", "S,T", 0x4ac00005, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.le.ob", "S,T[e]", 0x48000005, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.le.ob", "S,k", 0x4bc00005, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"c.le.ps", "S,T", 0x46c0003e, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.le.ps", "M,S,T", 0x46c0003e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.le.qh", "Y,Q", 0x78200005, 0xfc2007ff, WR_CC|RD_S|RD_T|FP_D, 0, MX },
+{"c.ngt.d", "S,T", 0x4620003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I1 },
+{"c.ngt.d", "M,S,T", 0x4620003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I4|I32 },
+{"c.ngt.s", "S,T", 0x4600003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_S, 0, I1 },
+{"c.ngt.s", "M,S,T", 0x4600003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, I4|I32 },
+{"c.ngt.ps","S,T", 0x46c0003f, 0xffe007ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"c.ngt.ps","M,S,T", 0x46c0003f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, I5|I33 },
+{"cabs.eq.d", "M,S,T", 0x46200072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.eq.ps", "M,S,T", 0x46c00072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.eq.s", "M,S,T", 0x46000072, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.f.d", "M,S,T", 0x46200070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.f.ps", "M,S,T", 0x46c00070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.f.s", "M,S,T", 0x46000070, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.le.d", "M,S,T", 0x4620007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.le.ps", "M,S,T", 0x46c0007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.le.s", "M,S,T", 0x4600007e, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.lt.d", "M,S,T", 0x4620007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.lt.ps", "M,S,T", 0x46c0007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.lt.s", "M,S,T", 0x4600007c, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.nge.d", "M,S,T", 0x4620007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.nge.ps","M,S,T", 0x46c0007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.nge.s", "M,S,T", 0x4600007d, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ngl.d", "M,S,T", 0x4620007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ngl.ps","M,S,T", 0x46c0007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ngl.s", "M,S,T", 0x4600007b, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ngle.d","M,S,T", 0x46200079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ngle.ps","M,S,T",0x46c00079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ngle.s","M,S,T", 0x46000079, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ngt.d", "M,S,T", 0x4620007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ngt.ps","M,S,T", 0x46c0007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ngt.s", "M,S,T", 0x4600007f, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ole.d", "M,S,T", 0x46200076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ole.ps","M,S,T", 0x46c00076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ole.s", "M,S,T", 0x46000076, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.olt.d", "M,S,T", 0x46200074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.olt.ps","M,S,T", 0x46c00074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.olt.s", "M,S,T", 0x46000074, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.seq.d", "M,S,T", 0x4620007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.seq.ps","M,S,T", 0x46c0007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.seq.s", "M,S,T", 0x4600007a, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.sf.d", "M,S,T", 0x46200078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.sf.ps", "M,S,T", 0x46c00078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.sf.s", "M,S,T", 0x46000078, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ueq.d", "M,S,T", 0x46200073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ueq.ps","M,S,T", 0x46c00073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ueq.s", "M,S,T", 0x46000073, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ule.d", "M,S,T", 0x46200077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ule.ps","M,S,T", 0x46c00077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ule.s", "M,S,T", 0x46000077, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.ult.d", "M,S,T", 0x46200075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ult.ps","M,S,T", 0x46c00075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.ult.s", "M,S,T", 0x46000075, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+{"cabs.un.d", "M,S,T", 0x46200071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.un.ps", "M,S,T", 0x46c00071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_D, 0, M3D },
+{"cabs.un.s", "M,S,T", 0x46000071, 0xffe000ff, RD_S|RD_T|WR_CC|FP_S, 0, M3D },
+/* CW4010 instructions which are aliases for the cache instruction. */
+{"flushi", "", 0xbc010000, 0xffffffff, 0, 0, L1 },
+{"flushd", "", 0xbc020000, 0xffffffff, 0, 0, L1 },
+{"flushid", "", 0xbc030000, 0xffffffff, 0, 0, L1 },
+{"wb", "o(b)", 0xbc040000, 0xfc1f0000, SM|RD_b, 0, L1 },
+{"cache", "k,o(b)", 0xbc000000, 0xfc000000, RD_b, 0, I3|I32|T3},
+{"cache", "k,A(b)", 0, (int) M_CACHE_AB, INSN_MACRO, 0, I3|I32|T3},
+{"ceil.l.d", "D,S", 0x4620000a, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 },
+{"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
+{"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
+{"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 },
+{"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
+{"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 },
+/* cfc2 is at the bottom of the table. */
+/* cfc3 is at the bottom of the table. */
+{"cftc1", "d,E", 0x41000023, 0xffe007ff, TRAP|LCD|WR_d|RD_C1|FP_S, 0, MT32 },
+{"cftc1", "d,T", 0x41000023, 0xffe007ff, TRAP|LCD|WR_d|RD_C1|FP_S, 0, MT32 },
+{"cftc2", "d,E", 0x41000025, 0xffe007ff, TRAP|LCD|WR_d|RD_C2, 0, MT32 },
+{"clo", "U,s", 0x70000021, 0xfc0007ff, WR_d|WR_t|RD_s, 0, I32|N55 },
+{"clz", "U,s", 0x70000020, 0xfc0007ff, WR_d|WR_t|RD_s, 0, I32|N55 },
+{"ctc0", "t,G", 0x40c00000, 0xffe007ff, COD|RD_t|WR_CC, 0, I1 },
+{"ctc1", "t,G", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, 0, I1 },
+{"ctc1", "t,S", 0x44c00000, 0xffe007ff, COD|RD_t|WR_CC|FP_S, 0, I1 },
+/* ctc2 is at the bottom of the table. */
+/* ctc3 is at the bottom of the table. */
+{"cttc1", "t,g", 0x41800023, 0xffe007ff, TRAP|COD|RD_t|WR_CC|FP_S, 0, MT32 },
+{"cttc1", "t,S", 0x41800023, 0xffe007ff, TRAP|COD|RD_t|WR_CC|FP_S, 0, MT32 },
+{"cttc2", "t,g", 0x41800025, 0xffe007ff, TRAP|COD|RD_t|WR_CC, 0, MT32 },
+{"cvt.d.l", "D,S", 0x46a00021, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 },
+{"cvt.d.s", "D,S", 0x46000021, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 },
+{"cvt.d.w", "D,S", 0x46800021, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 },
+{"cvt.l.d", "D,S", 0x46200025, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 },
+{"cvt.l.s", "D,S", 0x46000025, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
+{"cvt.s.l", "D,S", 0x46a00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
+{"cvt.s.d", "D,S", 0x46200020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 },
+{"cvt.s.w", "D,S", 0x46800020, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 },
+{"cvt.s.pl","D,S", 0x46c00028, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I5|I33 },
+{"cvt.s.pu","D,S", 0x46c00020, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I5|I33 },
+{"cvt.w.d", "D,S", 0x46200024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I1 },
+{"cvt.w.s", "D,S", 0x46000024, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 },
+{"cvt.ps.pw", "D,S", 0x46800026, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, M3D },
+{"cvt.ps.s","D,V,T", 0x46000026, 0xffe0003f, WR_D|RD_S|RD_T|FP_S|FP_D, 0, I5|I33 },
+{"cvt.pw.ps", "D,S", 0x46c00024, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, M3D },
+{"dabs", "d,v", 0, (int) M_DABS, INSN_MACRO, 0, I3 },
+{"dadd", "d,v,t", 0x0000002c, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 },
+{"dadd", "t,r,I", 0, (int) M_DADD_I, INSN_MACRO, 0, I3 },
+{"daddi", "t,r,j", 0x60000000, 0xfc000000, WR_t|RD_s, 0, I3 },
+{"daddiu", "t,r,j", 0x64000000, 0xfc000000, WR_t|RD_s, 0, I3 },
+{"daddu", "d,v,t", 0x0000002d, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 },
+{"daddu", "t,r,I", 0, (int) M_DADDU_I, INSN_MACRO, 0, I3 },
+{"dbreak", "", 0x7000003f, 0xffffffff, 0, 0, N5 },
+{"dclo", "U,s", 0x70000025, 0xfc0007ff, RD_s|WR_d|WR_t, 0, I64|N55 },
+{"dclz", "U,s", 0x70000024, 0xfc0007ff, RD_s|WR_d|WR_t, 0, I64|N55 },
+/* dctr and dctw are used on the r5000. */
+{"dctr", "o(b)", 0xbc050000, 0xfc1f0000, RD_b, 0, I3 },
+{"dctw", "o(b)", 0xbc090000, 0xfc1f0000, RD_b, 0, I3 },
+{"deret", "", 0x4200001f, 0xffffffff, 0, 0, I32|G2 },
+{"dext", "t,r,I,+I", 0, (int) M_DEXT, INSN_MACRO, 0, I65 },
+{"dext", "t,r,+A,+C", 0x7c000003, 0xfc00003f, WR_t|RD_s, 0, I65 },
+{"dextm", "t,r,+A,+G", 0x7c000001, 0xfc00003f, WR_t|RD_s, 0, I65 },
+{"dextu", "t,r,+E,+H", 0x7c000002, 0xfc00003f, WR_t|RD_s, 0, I65 },
+/* For ddiv, see the comments about div. */
+{"ddiv", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 },
+{"ddiv", "d,v,t", 0, (int) M_DDIV_3, INSN_MACRO, 0, I3 },
+{"ddiv", "d,v,I", 0, (int) M_DDIV_3I, INSN_MACRO, 0, I3 },
+/* For ddivu, see the comments about div. */
+{"ddivu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 },
+{"ddivu", "d,v,t", 0, (int) M_DDIVU_3, INSN_MACRO, 0, I3 },
+{"ddivu", "d,v,I", 0, (int) M_DDIVU_3I, INSN_MACRO, 0, I3 },
+{"di", "", 0x41606000, 0xffffffff, WR_t|WR_C0, 0, I33 },
+{"di", "t", 0x41606000, 0xffe0ffff, WR_t|WR_C0, 0, I33 },
+{"dins", "t,r,I,+I", 0, (int) M_DINS, INSN_MACRO, 0, I65 },
+{"dins", "t,r,+A,+B", 0x7c000007, 0xfc00003f, WR_t|RD_s, 0, I65 },
+{"dinsm", "t,r,+A,+F", 0x7c000005, 0xfc00003f, WR_t|RD_s, 0, I65 },
+{"dinsu", "t,r,+E,+F", 0x7c000006, 0xfc00003f, WR_t|RD_s, 0, I65 },
+/* The MIPS assembler treats the div opcode with two operands as
+ though the first operand appeared twice (the first operand is both
+ a source and a destination). To get the div machine instruction,
+ you must use an explicit destination of $0. */
+{"div", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 },
+{"div", "z,t", 0x0000001a, 0xffe0ffff, RD_s|RD_t|WR_HILO, 0, I1 },
+{"div", "d,v,t", 0, (int) M_DIV_3, INSN_MACRO, 0, I1 },
+{"div", "d,v,I", 0, (int) M_DIV_3I, INSN_MACRO, 0, I1 },
+{"div.d", "D,V,T", 0x46200003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 },
+{"div.s", "D,V,T", 0x46000003, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 },
+{"div.ps", "D,V,T", 0x46c00003, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, SB1 },
+/* For divu, see the comments about div. */
+{"divu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 },
+{"divu", "z,t", 0x0000001b, 0xffe0ffff, RD_s|RD_t|WR_HILO, 0, I1 },
+{"divu", "d,v,t", 0, (int) M_DIVU_3, INSN_MACRO, 0, I1 },
+{"divu", "d,v,I", 0, (int) M_DIVU_3I, INSN_MACRO, 0, I1 },
+{"dla", "t,A(b)", 0, (int) M_DLA_AB, INSN_MACRO, 0, I3 },
+{"dlca", "t,A(b)", 0, (int) M_DLCA_AB, INSN_MACRO, 0, I3 },
+{"dli", "t,j", 0x24000000, 0xffe00000, WR_t, 0, I3 }, /* addiu */
+{"dli", "t,i", 0x34000000, 0xffe00000, WR_t, 0, I3 }, /* ori */
+{"dli", "t,I", 0, (int) M_DLI, INSN_MACRO, 0, I3 },
+{"dmacc", "d,s,t", 0x00000029, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmacchi", "d,s,t", 0x00000229, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmacchis", "d,s,t", 0x00000629, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmacchiu", "d,s,t", 0x00000269, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmacchius", "d,s,t", 0x00000669, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmaccs", "d,s,t", 0x00000429, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmaccu", "d,s,t", 0x00000069, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmaccus", "d,s,t", 0x00000469, 0xfc0007ff, RD_s|RD_t|WR_LO|WR_d, 0, N412 },
+{"dmadd16", "s,t", 0x00000029, 0xfc00ffff, RD_s|RD_t|MOD_LO, 0, N411 },
+{"dmfc0", "t,G", 0x40200000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I3 },
+{"dmfc0", "t,+D", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I64 },
+{"dmfc0", "t,G,H", 0x40200000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I64 },
+{"dmt", "", 0x41600bc1, 0xffffffff, TRAP, 0, MT32 },
+{"dmt", "t", 0x41600bc1, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
+{"dmtc0", "t,G", 0x40a00000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, 0, I3 },
+{"dmtc0", "t,+D", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I64 },
+{"dmtc0", "t,G,H", 0x40a00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I64 },
+{"dmfc1", "t,S", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I3 },
+{"dmfc1", "t,G", 0x44200000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I3 },
+{"dmtc1", "t,S", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I3 },
+{"dmtc1", "t,G", 0x44a00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I3 },
+/* dmfc2 is at the bottom of the table. */
+/* dmtc2 is at the bottom of the table. */
+/* dmfc3 is at the bottom of the table. */
+/* dmtc3 is at the bottom of the table. */
+{"dmul", "d,v,t", 0, (int) M_DMUL, INSN_MACRO, 0, I3 },
+{"dmul", "d,v,I", 0, (int) M_DMUL_I, INSN_MACRO, 0, I3 },
+{"dmulo", "d,v,t", 0, (int) M_DMULO, INSN_MACRO, 0, I3 },
+{"dmulo", "d,v,I", 0, (int) M_DMULO_I, INSN_MACRO, 0, I3 },
+{"dmulou", "d,v,t", 0, (int) M_DMULOU, INSN_MACRO, 0, I3 },
+{"dmulou", "d,v,I", 0, (int) M_DMULOU_I, INSN_MACRO, 0, I3 },
+{"dmult", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 },
+{"dmultu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 },
+{"dneg", "d,w", 0x0000002e, 0xffe007ff, WR_d|RD_t, 0, I3 }, /* dsub 0 */
+{"dnegu", "d,w", 0x0000002f, 0xffe007ff, WR_d|RD_t, 0, I3 }, /* dsubu 0*/
+{"drem", "z,s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 },
+{"drem", "d,v,t", 3, (int) M_DREM_3, INSN_MACRO, 0, I3 },
+{"drem", "d,v,I", 3, (int) M_DREM_3I, INSN_MACRO, 0, I3 },
+{"dremu", "z,s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I3 },
+{"dremu", "d,v,t", 3, (int) M_DREMU_3, INSN_MACRO, 0, I3 },
+{"dremu", "d,v,I", 3, (int) M_DREMU_3I, INSN_MACRO, 0, I3 },
+{"dret", "", 0x7000003e, 0xffffffff, 0, 0, N5 },
+{"drol", "d,v,t", 0, (int) M_DROL, INSN_MACRO, 0, I3 },
+{"drol", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, 0, I3 },
+{"dror", "d,v,t", 0, (int) M_DROR, INSN_MACRO, 0, I3 },
+{"dror", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, 0, I3 },
+{"dror", "d,w,<", 0x0020003a, 0xffe0003f, WR_d|RD_t, 0, N5|I65 },
+{"drorv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, 0, N5|I65 },
+{"dror32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, 0, N5|I65 },
+{"drotl", "d,v,t", 0, (int) M_DROL, INSN_MACRO, 0, I65 },
+{"drotl", "d,v,I", 0, (int) M_DROL_I, INSN_MACRO, 0, I65 },
+{"drotr", "d,v,t", 0, (int) M_DROR, INSN_MACRO, 0, I65 },
+{"drotr", "d,v,I", 0, (int) M_DROR_I, INSN_MACRO, 0, I65 },
+{"drotrv", "d,t,s", 0x00000056, 0xfc0007ff, RD_t|RD_s|WR_d, 0, I65 },
+{"drotr32", "d,w,<", 0x0020003e, 0xffe0003f, WR_d|RD_t, 0, I65 },
+{"dsbh", "d,w", 0x7c0000a4, 0xffe007ff, WR_d|RD_t, 0, I65 },
+{"dshd", "d,w", 0x7c000164, 0xffe007ff, WR_d|RD_t, 0, I65 },
+{"dsllv", "d,t,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 },
+{"dsll32", "d,w,<", 0x0000003c, 0xffe0003f, WR_d|RD_t, 0, I3 },
+{"dsll", "d,w,s", 0x00000014, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, /* dsllv */
+{"dsll", "d,w,>", 0x0000003c, 0xffe0003f, WR_d|RD_t, 0, I3 }, /* dsll32 */
+{"dsll", "d,w,<", 0x00000038, 0xffe0003f, WR_d|RD_t, 0, I3 },
+{"dsrav", "d,t,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 },
+{"dsra32", "d,w,<", 0x0000003f, 0xffe0003f, WR_d|RD_t, 0, I3 },
+{"dsra", "d,w,s", 0x00000017, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, /* dsrav */
+{"dsra", "d,w,>", 0x0000003f, 0xffe0003f, WR_d|RD_t, 0, I3 }, /* dsra32 */
+{"dsra", "d,w,<", 0x0000003b, 0xffe0003f, WR_d|RD_t, 0, I3 },
+{"dsrlv", "d,t,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 },
+{"dsrl32", "d,w,<", 0x0000003e, 0xffe0003f, WR_d|RD_t, 0, I3 },
+{"dsrl", "d,w,s", 0x00000016, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I3 }, /* dsrlv */
+{"dsrl", "d,w,>", 0x0000003e, 0xffe0003f, WR_d|RD_t, 0, I3 }, /* dsrl32 */
+{"dsrl", "d,w,<", 0x0000003a, 0xffe0003f, WR_d|RD_t, 0, I3 },
+{"dsub", "d,v,t", 0x0000002e, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 },
+{"dsub", "d,v,I", 0, (int) M_DSUB_I, INSN_MACRO, 0, I3 },
+{"dsubu", "d,v,t", 0x0000002f, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I3 },
+{"dsubu", "d,v,I", 0, (int) M_DSUBU_I, INSN_MACRO, 0, I3 },
+{"dvpe", "", 0x41600001, 0xffffffff, TRAP, 0, MT32 },
+{"dvpe", "t", 0x41600001, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
+{"ei", "", 0x41606020, 0xffffffff, WR_t|WR_C0, 0, I33 },
+{"ei", "t", 0x41606020, 0xffe0ffff, WR_t|WR_C0, 0, I33 },
+{"emt", "", 0x41600be1, 0xffffffff, TRAP, 0, MT32 },
+{"emt", "t", 0x41600be1, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
+{"eret", "", 0x42000018, 0xffffffff, 0, 0, I3|I32 },
+{"evpe", "", 0x41600021, 0xffffffff, TRAP, 0, MT32 },
+{"evpe", "t", 0x41600021, 0xffe0ffff, TRAP|WR_t, 0, MT32 },
+{"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, 0, I33 },
+{"floor.l.d", "D,S", 0x4620000b, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 },
+{"floor.l.s", "D,S", 0x4600000b, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
+{"floor.w.d", "D,S", 0x4620000f, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
+{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"hibernate","", 0x42000023, 0xffffffff, 0, 0, V1 },
+{"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, 0, I33 },
+{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 },
+/* jr.hb is officially MIPS{32,64}R2, but it works on R1 as jr with
+ the same hazard barrier effect. */
+{"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, 0, I32 },
+{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 }, /* jr */
+/* SVR4 PIC code requires special handling for j, so it must be a
+ macro. */
+{"j", "a", 0, (int) M_J_A, INSN_MACRO, 0, I1 },
+/* This form of j is used by the disassembler and internally by the
+ assembler, but will never match user input (because the line above
+ will match first). */
+{"j", "a", 0x08000000, 0xfc000000, UBD, 0, I1 },
+{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, 0, I1 },
+{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, 0, I1 },
+/* jalr.hb is officially MIPS{32,64}R2, but it works on R1 as jalr
+ with the same hazard barrier effect. */
+{"jalr.hb", "s", 0x0000fc09, 0xfc1fffff, UBD|RD_s|WR_d, 0, I32 },
+{"jalr.hb", "d,s", 0x00000409, 0xfc1f07ff, UBD|RD_s|WR_d, 0, I32 },
+/* SVR4 PIC code requires special handling for jal, so it must be a
+ macro. */
+{"jal", "d,s", 0, (int) M_JAL_2, INSN_MACRO, 0, I1 },
+{"jal", "s", 0, (int) M_JAL_1, INSN_MACRO, 0, I1 },
+{"jal", "a", 0, (int) M_JAL_A, INSN_MACRO, 0, I1 },
+/* This form of jal is used by the disassembler and internally by the
+ assembler, but will never match user input (because the line above
+ will match first). */
+{"jal", "a", 0x0c000000, 0xfc000000, UBD|WR_31, 0, I1 },
+{"jalx", "a", 0x74000000, 0xfc000000, UBD|WR_31, 0, I16 },
+{"la", "t,A(b)", 0, (int) M_LA_AB, INSN_MACRO, 0, I1 },
+{"lb", "t,o(b)", 0x80000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lb", "t,A(b)", 0, (int) M_LB_AB, INSN_MACRO, 0, I1 },
+{"lbu", "t,o(b)", 0x90000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lbu", "t,A(b)", 0, (int) M_LBU_AB, INSN_MACRO, 0, I1 },
+{"lca", "t,A(b)", 0, (int) M_LCA_AB, INSN_MACRO, 0, I1 },
+{"ld", "t,o(b)", 0xdc000000, 0xfc000000, WR_t|RD_b, 0, I3 },
+{"ld", "t,o(b)", 0, (int) M_LD_OB, INSN_MACRO, 0, I1 },
+{"ld", "t,A(b)", 0, (int) M_LD_AB, INSN_MACRO, 0, I1 },
+{"ldc1", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, 0, I2 },
+{"ldc1", "E,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, 0, I2 },
+{"ldc1", "T,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, 0, I2 },
+{"ldc1", "E,A(b)", 0, (int) M_LDC1_AB, INSN_MACRO, 0, I2 },
+{"l.d", "T,o(b)", 0xd4000000, 0xfc000000, CLD|RD_b|WR_T|FP_D, 0, I2 }, /* ldc1 */
+{"l.d", "T,o(b)", 0, (int) M_L_DOB, INSN_MACRO, 0, I1 },
+{"l.d", "T,A(b)", 0, (int) M_L_DAB, INSN_MACRO, 0, I1 },
+{"ldc2", "E,o(b)", 0xd8000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I2 },
+{"ldc2", "E,A(b)", 0, (int) M_LDC2_AB, INSN_MACRO, 0, I2 },
+{"ldc3", "E,o(b)", 0xdc000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I2 },
+{"ldc3", "E,A(b)", 0, (int) M_LDC3_AB, INSN_MACRO, 0, I2 },
+{"ldl", "t,o(b)", 0x68000000, 0xfc000000, LDD|WR_t|RD_b, 0, I3 },
+{"ldl", "t,A(b)", 0, (int) M_LDL_AB, INSN_MACRO, 0, I3 },
+{"ldr", "t,o(b)", 0x6c000000, 0xfc000000, LDD|WR_t|RD_b, 0, I3 },
+{"ldr", "t,A(b)", 0, (int) M_LDR_AB, INSN_MACRO, 0, I3 },
+{"ldxc1", "D,t(b)", 0x4c000001, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I4|I33 },
+{"lh", "t,o(b)", 0x84000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lh", "t,A(b)", 0, (int) M_LH_AB, INSN_MACRO, 0, I1 },
+{"lhu", "t,o(b)", 0x94000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lhu", "t,A(b)", 0, (int) M_LHU_AB, INSN_MACRO, 0, I1 },
+/* li is at the start of the table. */
+{"li.d", "t,F", 0, (int) M_LI_D, INSN_MACRO, 0, I1 },
+{"li.d", "T,L", 0, (int) M_LI_DD, INSN_MACRO, 0, I1 },
+{"li.s", "t,f", 0, (int) M_LI_S, INSN_MACRO, 0, I1 },
+{"li.s", "T,l", 0, (int) M_LI_SS, INSN_MACRO, 0, I1 },
+{"ll", "t,o(b)", 0xc0000000, 0xfc000000, LDD|RD_b|WR_t, 0, I2 },
+{"ll", "t,A(b)", 0, (int) M_LL_AB, INSN_MACRO, 0, I2 },
+{"lld", "t,o(b)", 0xd0000000, 0xfc000000, LDD|RD_b|WR_t, 0, I3 },
+{"lld", "t,A(b)", 0, (int) M_LLD_AB, INSN_MACRO, 0, I3 },
+{"lui", "t,u", 0x3c000000, 0xffe00000, WR_t, 0, I1 },
+{"luxc1", "D,t(b)", 0x4c000005, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I5|I33|N55},
+{"lw", "t,o(b)", 0x8c000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lw", "t,A(b)", 0, (int) M_LW_AB, INSN_MACRO, 0, I1 },
+{"lwc0", "E,o(b)", 0xc0000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I1 },
+{"lwc0", "E,A(b)", 0, (int) M_LWC0_AB, INSN_MACRO, 0, I1 },
+{"lwc1", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, 0, I1 },
+{"lwc1", "E,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, 0, I1 },
+{"lwc1", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, 0, I1 },
+{"lwc1", "E,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, 0, I1 },
+{"l.s", "T,o(b)", 0xc4000000, 0xfc000000, CLD|RD_b|WR_T|FP_S, 0, I1 }, /* lwc1 */
+{"l.s", "T,A(b)", 0, (int) M_LWC1_AB, INSN_MACRO, 0, I1 },
+{"lwc2", "E,o(b)", 0xc8000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I1 },
+{"lwc2", "E,A(b)", 0, (int) M_LWC2_AB, INSN_MACRO, 0, I1 },
+{"lwc3", "E,o(b)", 0xcc000000, 0xfc000000, CLD|RD_b|WR_CC, 0, I1 },
+{"lwc3", "E,A(b)", 0, (int) M_LWC3_AB, INSN_MACRO, 0, I1 },
+{"lwl", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lwl", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, 0, I1 },
+{"lcache", "t,o(b)", 0x88000000, 0xfc000000, LDD|RD_b|WR_t, 0, I2 }, /* same */
+{"lcache", "t,A(b)", 0, (int) M_LWL_AB, INSN_MACRO, 0, I2 }, /* as lwl */
+{"lwr", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, 0, I1 },
+{"lwr", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, 0, I1 },
+{"flush", "t,o(b)", 0x98000000, 0xfc000000, LDD|RD_b|WR_t, 0, I2 }, /* same */
+{"flush", "t,A(b)", 0, (int) M_LWR_AB, INSN_MACRO, 0, I2 }, /* as lwr */
+{"fork", "d,s,t", 0x7c000008, 0xfc0007ff, TRAP|WR_d|RD_s|RD_t, 0, MT32 },
+{"lwu", "t,o(b)", 0x9c000000, 0xfc000000, LDD|RD_b|WR_t, 0, I3 },
+{"lwu", "t,A(b)", 0, (int) M_LWU_AB, INSN_MACRO, 0, I3 },
+{"lwxc1", "D,t(b)", 0x4c000000, 0xfc00f83f, LDD|WR_D|RD_t|RD_b|FP_D, 0, I4|I33 },
+{"lwxs", "d,t(b)", 0x70000088, 0xfc0007ff, LDD|RD_b|RD_t|WR_d, 0, SMT },
+{"macc", "d,s,t", 0x00000028, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"macc", "d,s,t", 0x00000158, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"maccs", "d,s,t", 0x00000428, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"macchi", "d,s,t", 0x00000228, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"macchi", "d,s,t", 0x00000358, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"macchis", "d,s,t", 0x00000628, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"macchiu", "d,s,t", 0x00000268, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"macchiu", "d,s,t", 0x00000359, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"macchius","d,s,t", 0x00000668, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"maccu", "d,s,t", 0x00000068, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"maccu", "d,s,t", 0x00000159, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"maccus", "d,s,t", 0x00000468, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N412 },
+{"mad", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, P3 },
+{"madu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, P3 },
+{"madd.d", "D,R,S,T", 0x4c000021, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 },
+{"madd.s", "D,R,S,T", 0x4c000020, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 },
+{"madd.ps", "D,R,S,T", 0x4c000026, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 },
+{"madd", "s,t", 0x0000001c, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 },
+{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 },
+{"madd", "s,t", 0x70000000, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, G1 },
+{"madd", "7,s,t", 0x70000000, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"madd", "d,s,t", 0x70000000, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 },
+{"maddp", "s,t", 0x70000441, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, SMT },
+{"maddu", "s,t", 0x0000001d, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 },
+{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 },
+{"maddu", "s,t", 0x70000001, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, G1 },
+{"maddu", "7,s,t", 0x70000001, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"maddu", "d,s,t", 0x70000001, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 },
+{"madd16", "s,t", 0x00000028, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, N411 },
+{"max.ob", "X,Y,Q", 0x78000007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"max.ob", "D,S,T", 0x4ac00007, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"max.ob", "D,S,T[e]", 0x48000007, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"max.ob", "D,S,k", 0x4bc00007, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"max.qh", "X,Y,Q", 0x78200007, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"mfpc", "t,P", 0x4000c801, 0xffe0ffc1, LCD|WR_t|RD_C0, 0, M1|N5 },
+{"mfps", "t,P", 0x4000c800, 0xffe0ffc1, LCD|WR_t|RD_C0, 0, M1|N5 },
+{"mftacx", "d", 0x41020021, 0xffff07ff, TRAP|WR_d|RD_a, 0, MT32 },
+{"mftacx", "d,*", 0x41020021, 0xfff307ff, TRAP|WR_d|RD_a, 0, MT32 },
+{"mftc0", "d,+t", 0x41000000, 0xffe007ff, TRAP|LCD|WR_d|RD_C0, 0, MT32 },
+{"mftc0", "d,+T", 0x41000000, 0xffe007f8, TRAP|LCD|WR_d|RD_C0, 0, MT32 },
+{"mftc0", "d,E,H", 0x41000000, 0xffe007f8, TRAP|LCD|WR_d|RD_C0, 0, MT32 },
+{"mftc1", "d,T", 0x41000022, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_S, 0, MT32 },
+{"mftc1", "d,E", 0x41000022, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_S, 0, MT32 },
+{"mftc2", "d,E", 0x41000024, 0xffe007ff, TRAP|LCD|WR_d|RD_C2, 0, MT32 },
+{"mftdsp", "d", 0x41100021, 0xffff07ff, TRAP|WR_d, 0, MT32 },
+{"mftgpr", "d,t", 0x41000020, 0xffe007ff, TRAP|WR_d|RD_t, 0, MT32 },
+{"mfthc1", "d,T", 0x41000032, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_D, 0, MT32 },
+{"mfthc1", "d,E", 0x41000032, 0xffe007ff, TRAP|LCD|WR_d|RD_T|FP_D, 0, MT32 },
+{"mfthc2", "d,E", 0x41000034, 0xffe007ff, TRAP|LCD|WR_d|RD_C2, 0, MT32 },
+{"mfthi", "d", 0x41010021, 0xffff07ff, TRAP|WR_d|RD_a, 0, MT32 },
+{"mfthi", "d,*", 0x41010021, 0xfff307ff, TRAP|WR_d|RD_a, 0, MT32 },
+{"mftlo", "d", 0x41000021, 0xffff07ff, TRAP|WR_d|RD_a, 0, MT32 },
+{"mftlo", "d,*", 0x41000021, 0xfff307ff, TRAP|WR_d|RD_a, 0, MT32 },
+{"mftr", "d,t,!,H,$", 0x41000000, 0xffe007c8, TRAP|WR_d, 0, MT32 },
+{"mfc0", "t,G", 0x40000000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 },
+{"mfc0", "t,+D", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I32 },
+{"mfc0", "t,G,H", 0x40000000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I32 },
+{"mfc1", "t,S", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, 0, I1 },
+{"mfc1", "t,G", 0x44000000, 0xffe007ff, LCD|WR_t|RD_S|FP_S, 0, I1 },
+{"mfhc1", "t,S", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I33 },
+{"mfhc1", "t,G", 0x44600000, 0xffe007ff, LCD|WR_t|RD_S|FP_D, 0, I33 },
+/* mfc2 is at the bottom of the table. */
+/* mfhc2 is at the bottom of the table. */
+/* mfc3 is at the bottom of the table. */
+{"mfdr", "t,G", 0x7000003d, 0xffe007ff, LCD|WR_t|RD_C0, 0, N5 },
+{"mfhi", "d", 0x00000010, 0xffff07ff, WR_d|RD_HI, 0, I1 },
+{"mfhi", "d,9", 0x00000010, 0xff9f07ff, WR_d|RD_HI, 0, D32 },
+{"mflo", "d", 0x00000012, 0xffff07ff, WR_d|RD_LO, 0, I1 },
+{"mflo", "d,9", 0x00000012, 0xff9f07ff, WR_d|RD_LO, 0, D32 },
+{"mflhxu", "d", 0x00000052, 0xffff07ff, WR_d|MOD_HILO, 0, SMT },
+{"min.ob", "X,Y,Q", 0x78000006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"min.ob", "D,S,T", 0x4ac00006, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"min.ob", "D,S,T[e]", 0x48000006, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"min.ob", "D,S,k", 0x4bc00006, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"min.qh", "X,Y,Q", 0x78200006, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"mov.d", "D,S", 0x46200006, 0xffff003f, WR_D|RD_S|FP_D, 0, I1 },
+{"mov.s", "D,S", 0x46000006, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 },
+{"mov.ps", "D,S", 0x46c00006, 0xffff003f, WR_D|RD_S|FP_D, 0, I5|I33 },
+{"movf", "d,s,N", 0x00000001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_S|FP_D, 0, I4|I32 },
+{"movf.d", "D,S,N", 0x46200011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I4|I32 },
+{"movf.l", "D,S,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 },
+{"movf.l", "X,Y,N", 0x46a00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 },
+{"movf.s", "D,S,N", 0x46000011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, 0, I4|I32 },
+{"movf.ps", "D,S,N", 0x46c00011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I5|I33 },
+{"movn", "d,v,t", 0x0000000b, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I4|I32 },
+{"ffc", "d,v", 0x0000000b, 0xfc1f07ff, WR_d|RD_s, 0, L1 },
+{"movn.d", "D,S,t", 0x46200013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I4|I32 },
+{"movn.l", "D,S,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 },
+{"movn.l", "X,Y,t", 0x46a00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 },
+{"movn.s", "D,S,t", 0x46000013, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, 0, I4|I32 },
+{"movn.ps", "D,S,t", 0x46c00013, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I5|I33 },
+{"movt", "d,s,N", 0x00010001, 0xfc0307ff, WR_d|RD_s|RD_CC|FP_S|FP_D, 0, I4|I32 },
+{"movt.d", "D,S,N", 0x46210011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I4|I32 },
+{"movt.l", "D,S,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 },
+{"movt.l", "X,Y,N", 0x46a10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, MX|SB1 },
+{"movt.s", "D,S,N", 0x46010011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_S, 0, I4|I32 },
+{"movt.ps", "D,S,N", 0x46c10011, 0xffe3003f, WR_D|RD_S|RD_CC|FP_D, 0, I5|I33 },
+{"movz", "d,v,t", 0x0000000a, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I4|I32 },
+{"ffs", "d,v", 0x0000000a, 0xfc1f07ff, WR_d|RD_s, 0, L1 },
+{"movz.d", "D,S,t", 0x46200012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I4|I32 },
+{"movz.l", "D,S,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 },
+{"movz.l", "X,Y,t", 0x46a00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, MX|SB1 },
+{"movz.s", "D,S,t", 0x46000012, 0xffe0003f, WR_D|RD_S|RD_t|FP_S, 0, I4|I32 },
+{"movz.ps", "D,S,t", 0x46c00012, 0xffe0003f, WR_D|RD_S|RD_t|FP_D, 0, I5|I33 },
+{"msac", "d,s,t", 0x000001d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"msacu", "d,s,t", 0x000001d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"msachi", "d,s,t", 0x000003d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"msachiu", "d,s,t", 0x000003d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+/* move is at the top of the table. */
+{"msgn.qh", "X,Y,Q", 0x78200000, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"msub.d", "D,R,S,T", 0x4c000029, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 },
+{"msub.s", "D,R,S,T", 0x4c000028, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 },
+{"msub.ps", "D,R,S,T", 0x4c00002e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 },
+{"msub", "s,t", 0x0000001e, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 },
+{"msub", "s,t", 0x70000004, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 },
+{"msub", "7,s,t", 0x70000004, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"msubu", "s,t", 0x0000001f, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, L1 },
+{"msubu", "s,t", 0x70000005, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, I32|N55 },
+{"msubu", "7,s,t", 0x70000005, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"mtpc", "t,P", 0x4080c801, 0xffe0ffc1, COD|RD_t|WR_C0, 0, M1|N5 },
+{"mtps", "t,P", 0x4080c800, 0xffe0ffc1, COD|RD_t|WR_C0, 0, M1|N5 },
+{"mtc0", "t,G", 0x40800000, 0xffe007ff, COD|RD_t|WR_C0|WR_CC, 0, I1 },
+{"mtc0", "t,+D", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I32 },
+{"mtc0", "t,G,H", 0x40800000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I32 },
+{"mtc1", "t,S", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, 0, I1 },
+{"mtc1", "t,G", 0x44800000, 0xffe007ff, COD|RD_t|WR_S|FP_S, 0, I1 },
+{"mthc1", "t,S", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I33 },
+{"mthc1", "t,G", 0x44e00000, 0xffe007ff, COD|RD_t|WR_S|FP_D, 0, I33 },
+/* mtc2 is at the bottom of the table. */
+/* mthc2 is at the bottom of the table. */
+/* mtc3 is at the bottom of the table. */
+{"mtdr", "t,G", 0x7080003d, 0xffe007ff, COD|RD_t|WR_C0, 0, N5 },
+{"mthi", "s", 0x00000011, 0xfc1fffff, RD_s|WR_HI, 0, I1 },
+{"mthi", "s,7", 0x00000011, 0xfc1fe7ff, RD_s|WR_HI, 0, D32 },
+{"mtlo", "s", 0x00000013, 0xfc1fffff, RD_s|WR_LO, 0, I1 },
+{"mtlo", "s,7", 0x00000013, 0xfc1fe7ff, RD_s|WR_LO, 0, D32 },
+{"mtlhx", "s", 0x00000053, 0xfc1fffff, RD_s|MOD_HILO, 0, SMT },
+{"mttc0", "t,G", 0x41800000, 0xffe007ff, TRAP|COD|RD_t|WR_C0|WR_CC, 0, MT32 },
+{"mttc0", "t,+D", 0x41800000, 0xffe007f8, TRAP|COD|RD_t|WR_C0|WR_CC, 0, MT32 },
+{"mttc0", "t,G,H", 0x41800000, 0xffe007f8, TRAP|COD|RD_t|WR_C0|WR_CC, 0, MT32 },
+{"mttc1", "t,S", 0x41800022, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_S, 0, MT32 },
+{"mttc1", "t,G", 0x41800022, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_S, 0, MT32 },
+{"mttc2", "t,g", 0x41800024, 0xffe007ff, TRAP|COD|RD_t|WR_C2|WR_CC, 0, MT32 },
+{"mttacx", "t", 0x41801021, 0xffe0ffff, TRAP|WR_a|RD_t, 0, MT32 },
+{"mttacx", "t,&", 0x41801021, 0xffe09fff, TRAP|WR_a|RD_t, 0, MT32 },
+{"mttdsp", "t", 0x41808021, 0xffe0ffff, TRAP|RD_t, 0, MT32 },
+{"mttgpr", "t,d", 0x41800020, 0xffe007ff, TRAP|WR_d|RD_t, 0, MT32 },
+{"mtthc1", "t,S", 0x41800032, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_D, 0, MT32 },
+{"mtthc1", "t,G", 0x41800032, 0xffe007ff, TRAP|COD|RD_t|WR_S|FP_D, 0, MT32 },
+{"mtthc2", "t,g", 0x41800034, 0xffe007ff, TRAP|COD|RD_t|WR_C2|WR_CC, 0, MT32 },
+{"mtthi", "t", 0x41800821, 0xffe0ffff, TRAP|WR_a|RD_t, 0, MT32 },
+{"mtthi", "t,&", 0x41800821, 0xffe09fff, TRAP|WR_a|RD_t, 0, MT32 },
+{"mttlo", "t", 0x41800021, 0xffe0ffff, TRAP|WR_a|RD_t, 0, MT32 },
+{"mttlo", "t,&", 0x41800021, 0xffe09fff, TRAP|WR_a|RD_t, 0, MT32 },
+{"mttr", "t,d,!,H,$", 0x41800000, 0xffe007c8, TRAP|RD_t, 0, MT32 },
+{"mul.d", "D,V,T", 0x46200002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 },
+{"mul.s", "D,V,T", 0x46000002, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 },
+{"mul.ob", "X,Y,Q", 0x78000030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"mul.ob", "D,S,T", 0x4ac00030, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"mul.ob", "D,S,T[e]", 0x48000030, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"mul.ob", "D,S,k", 0x4bc00030, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"mul.ps", "D,V,T", 0x46c00002, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"mul.qh", "X,Y,Q", 0x78200030, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"mul", "d,v,t", 0x70000002, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, I32|P3|N55},
+{"mul", "d,s,t", 0x00000058, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N54 },
+{"mul", "d,v,t", 0, (int) M_MUL, INSN_MACRO, 0, I1 },
+{"mul", "d,v,I", 0, (int) M_MUL_I, INSN_MACRO, 0, I1 },
+{"mula.ob", "Y,Q", 0x78000033, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"mula.ob", "S,T", 0x4ac00033, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mula.ob", "S,T[e]", 0x48000033, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mula.ob", "S,k", 0x4bc00033, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mula.qh", "Y,Q", 0x78200033, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"mulhi", "d,s,t", 0x00000258, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"mulhiu", "d,s,t", 0x00000259, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"mull.ob", "Y,Q", 0x78000433, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"mull.ob", "S,T", 0x4ac00433, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mull.ob", "S,T[e]", 0x48000433, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mull.ob", "S,k", 0x4bc00433, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mull.qh", "Y,Q", 0x78200433, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"mulo", "d,v,t", 0, (int) M_MULO, INSN_MACRO, 0, I1 },
+{"mulo", "d,v,I", 0, (int) M_MULO_I, INSN_MACRO, 0, I1 },
+{"mulou", "d,v,t", 0, (int) M_MULOU, INSN_MACRO, 0, I1 },
+{"mulou", "d,v,I", 0, (int) M_MULOU_I, INSN_MACRO, 0, I1 },
+{"mulr.ps", "D,S,T", 0x46c0001a, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D },
+{"muls", "d,s,t", 0x000000d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"mulsu", "d,s,t", 0x000000d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"mulshi", "d,s,t", 0x000002d8, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"mulshiu", "d,s,t", 0x000002d9, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"muls.ob", "Y,Q", 0x78000032, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"muls.ob", "S,T", 0x4ac00032, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"muls.ob", "S,T[e]", 0x48000032, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"muls.ob", "S,k", 0x4bc00032, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"muls.qh", "Y,Q", 0x78200032, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"mulsl.ob", "Y,Q", 0x78000432, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"mulsl.ob", "S,T", 0x4ac00432, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mulsl.ob", "S,T[e]", 0x48000432, 0xfe2007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mulsl.ob", "S,k", 0x4bc00432, 0xffe007ff, WR_CC|RD_S|RD_T, 0, N54 },
+{"mulsl.qh", "Y,Q", 0x78200432, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"mult", "s,t", 0x00000018, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, I1 },
+{"mult", "7,s,t", 0x00000018, 0xfc00e7ff, WR_a|RD_s|RD_t, 0, D33 },
+{"mult", "d,s,t", 0x00000018, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 },
+{"multp", "s,t", 0x00000459, 0xfc00ffff, RD_s|RD_t|MOD_HILO, 0, SMT },
+{"multu", "s,t", 0x00000019, 0xfc00ffff, RD_s|RD_t|WR_HILO|IS_M, 0, I1 },
+{"multu", "7,s,t", 0x00000019, 0xfc00e7ff, WR_a|RD_s|RD_t, 0, D33 },
+{"multu", "d,s,t", 0x00000019, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d|IS_M, 0, G1 },
+{"mulu", "d,s,t", 0x00000059, 0xfc0007ff, RD_s|RD_t|WR_HILO|WR_d, 0, N5 },
+{"neg", "d,w", 0x00000022, 0xffe007ff, WR_d|RD_t, 0, I1 }, /* sub 0 */
+{"negu", "d,w", 0x00000023, 0xffe007ff, WR_d|RD_t, 0, I1 }, /* subu 0 */
+{"neg.d", "D,V", 0x46200007, 0xffff003f, WR_D|RD_S|FP_D, 0, I1 },
+{"neg.s", "D,V", 0x46000007, 0xffff003f, WR_D|RD_S|FP_S, 0, I1 },
+{"neg.ps", "D,V", 0x46c00007, 0xffff003f, WR_D|RD_S|FP_D, 0, I5|I33 },
+{"nmadd.d", "D,R,S,T", 0x4c000031, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 },
+{"nmadd.s", "D,R,S,T", 0x4c000030, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 },
+{"nmadd.ps","D,R,S,T", 0x4c000036, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 },
+{"nmsub.d", "D,R,S,T", 0x4c000039, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I4|I33 },
+{"nmsub.s", "D,R,S,T", 0x4c000038, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_S, 0, I4|I33 },
+{"nmsub.ps","D,R,S,T", 0x4c00003e, 0xfc00003f, RD_R|RD_S|RD_T|WR_D|FP_D, 0, I5|I33 },
+/* nop is at the start of the table. */
+{"nor", "d,v,t", 0x00000027, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"nor", "t,r,I", 0, (int) M_NOR_I, INSN_MACRO, 0, I1 },
+{"nor.ob", "X,Y,Q", 0x7800000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"nor.ob", "D,S,T", 0x4ac0000f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"nor.ob", "D,S,T[e]", 0x4800000f, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"nor.ob", "D,S,k", 0x4bc0000f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"nor.qh", "X,Y,Q", 0x7820000f, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"not", "d,v", 0x00000027, 0xfc1f07ff, WR_d|RD_s|RD_t, 0, I1 },/*nor d,s,0*/
+{"or", "d,v,t", 0x00000025, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"or", "t,r,I", 0, (int) M_OR_I, INSN_MACRO, 0, I1 },
+{"or.ob", "X,Y,Q", 0x7800000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"or.ob", "D,S,T", 0x4ac0000e, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"or.ob", "D,S,T[e]", 0x4800000e, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"or.ob", "D,S,k", 0x4bc0000e, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"or.qh", "X,Y,Q", 0x7820000e, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"ori", "t,r,i", 0x34000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+{"pabsdiff.ob", "X,Y,Q",0x78000009, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, SB1 },
+{"pabsdiffc.ob", "Y,Q", 0x78000035, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, SB1 },
+{"pavg.ob", "X,Y,Q", 0x78000008, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, SB1 },
+{"pickf.ob", "X,Y,Q", 0x78000002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"pickf.ob", "D,S,T", 0x4ac00002, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"pickf.ob", "D,S,T[e]",0x48000002, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"pickf.ob", "D,S,k", 0x4bc00002, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"pickf.qh", "X,Y,Q", 0x78200002, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"pickt.ob", "X,Y,Q", 0x78000003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"pickt.ob", "D,S,T", 0x4ac00003, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"pickt.ob", "D,S,T[e]",0x48000003, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"pickt.ob", "D,S,k", 0x4bc00003, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"pickt.qh", "X,Y,Q", 0x78200003, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"pll.ps", "D,V,T", 0x46c0002c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"plu.ps", "D,V,T", 0x46c0002d, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+ /* pref and prefx are at the start of the table. */
+{"pul.ps", "D,V,T", 0x46c0002e, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"puu.ps", "D,V,T", 0x46c0002f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"pperm", "s,t", 0x70000481, 0xfc00ffff, MOD_HILO|RD_s|RD_t, 0, SMT },
+{"rach.ob", "X", 0x7a00003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX|SB1 },
+{"rach.ob", "D", 0x4a00003f, 0xfffff83f, WR_D, 0, N54 },
+{"rach.qh", "X", 0x7a20003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX },
+{"racl.ob", "X", 0x7800003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX|SB1 },
+{"racl.ob", "D", 0x4800003f, 0xfffff83f, WR_D, 0, N54 },
+{"racl.qh", "X", 0x7820003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX },
+{"racm.ob", "X", 0x7900003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX|SB1 },
+{"racm.ob", "D", 0x4900003f, 0xfffff83f, WR_D, 0, N54 },
+{"racm.qh", "X", 0x7920003f, 0xfffff83f, WR_D|FP_D, RD_MACC, MX },
+{"recip.d", "D,S", 0x46200015, 0xffff003f, WR_D|RD_S|FP_D, 0, I4|I33 },
+{"recip.ps","D,S", 0x46c00015, 0xffff003f, WR_D|RD_S|FP_D, 0, SB1 },
+{"recip.s", "D,S", 0x46000015, 0xffff003f, WR_D|RD_S|FP_S, 0, I4|I33 },
+{"recip1.d", "D,S", 0x4620001d, 0xffff003f, WR_D|RD_S|FP_D, 0, M3D },
+{"recip1.ps", "D,S", 0x46c0001d, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D },
+{"recip1.s", "D,S", 0x4600001d, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D },
+{"recip2.d", "D,S,T", 0x4620001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D },
+{"recip2.ps", "D,S,T", 0x46c0001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D },
+{"recip2.s", "D,S,T", 0x4600001c, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D },
+{"rem", "z,s,t", 0x0000001a, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 },
+{"rem", "d,v,t", 0, (int) M_REM_3, INSN_MACRO, 0, I1 },
+{"rem", "d,v,I", 0, (int) M_REM_3I, INSN_MACRO, 0, I1 },
+{"remu", "z,s,t", 0x0000001b, 0xfc00ffff, RD_s|RD_t|WR_HILO, 0, I1 },
+{"remu", "d,v,t", 0, (int) M_REMU_3, INSN_MACRO, 0, I1 },
+{"remu", "d,v,I", 0, (int) M_REMU_3I, INSN_MACRO, 0, I1 },
+{"rdhwr", "t,K", 0x7c00003b, 0xffe007ff, WR_t, 0, I33 },
+{"rdpgpr", "d,w", 0x41400000, 0xffe007ff, WR_d, 0, I33 },
+{"rfe", "", 0x42000010, 0xffffffff, 0, 0, I1|T3 },
+{"rnas.qh", "X,Q", 0x78200025, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX },
+{"rnau.ob", "X,Q", 0x78000021, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX|SB1 },
+{"rnau.qh", "X,Q", 0x78200021, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX },
+{"rnes.qh", "X,Q", 0x78200026, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX },
+{"rneu.ob", "X,Q", 0x78000022, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX|SB1 },
+{"rneu.qh", "X,Q", 0x78200022, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX },
+{"rol", "d,v,t", 0, (int) M_ROL, INSN_MACRO, 0, I1 },
+{"rol", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, 0, I1 },
+{"ror", "d,v,t", 0, (int) M_ROR, INSN_MACRO, 0, I1 },
+{"ror", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, 0, I1 },
+{"ror", "d,w,<", 0x00200002, 0xffe0003f, WR_d|RD_t, 0, N5|I33|SMT },
+{"rorv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, 0, N5|I33|SMT },
+{"rotl", "d,v,t", 0, (int) M_ROL, INSN_MACRO, 0, I33|SMT },
+{"rotl", "d,v,I", 0, (int) M_ROL_I, INSN_MACRO, 0, I33|SMT },
+{"rotr", "d,v,t", 0, (int) M_ROR, INSN_MACRO, 0, I33|SMT },
+{"rotr", "d,v,I", 0, (int) M_ROR_I, INSN_MACRO, 0, I33|SMT },
+{"rotrv", "d,t,s", 0x00000046, 0xfc0007ff, RD_t|RD_s|WR_d, 0, I33|SMT },
+{"round.l.d", "D,S", 0x46200008, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 },
+{"round.l.s", "D,S", 0x46000008, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
+{"round.w.d", "D,S", 0x4620000c, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
+{"round.w.s", "D,S", 0x4600000c, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"rsqrt.d", "D,S", 0x46200016, 0xffff003f, WR_D|RD_S|FP_D, 0, I4|I33 },
+{"rsqrt.ps","D,S", 0x46c00016, 0xffff003f, WR_D|RD_S|FP_D, 0, SB1 },
+{"rsqrt.s", "D,S", 0x46000016, 0xffff003f, WR_D|RD_S|FP_S, 0, I4|I33 },
+{"rsqrt1.d", "D,S", 0x4620001e, 0xffff003f, WR_D|RD_S|FP_D, 0, M3D },
+{"rsqrt1.ps", "D,S", 0x46c0001e, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D },
+{"rsqrt1.s", "D,S", 0x4600001e, 0xffff003f, WR_D|RD_S|FP_S, 0, M3D },
+{"rsqrt2.d", "D,S,T", 0x4620001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, M3D },
+{"rsqrt2.ps", "D,S,T", 0x46c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D },
+{"rsqrt2.s", "D,S,T", 0x4600001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, M3D },
+{"rzs.qh", "X,Q", 0x78200024, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX },
+{"rzu.ob", "X,Q", 0x78000020, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX|SB1 },
+{"rzu.ob", "D,k", 0x4bc00020, 0xffe0f83f, WR_D|RD_S|RD_T, 0, N54 },
+{"rzu.qh", "X,Q", 0x78200020, 0xfc20f83f, WR_D|RD_T|FP_D, RD_MACC, MX },
+{"sb", "t,o(b)", 0xa0000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 },
+{"sb", "t,A(b)", 0, (int) M_SB_AB, INSN_MACRO, 0, I1 },
+{"sc", "t,o(b)", 0xe0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, 0, I2 },
+{"sc", "t,A(b)", 0, (int) M_SC_AB, INSN_MACRO, 0, I2 },
+{"scd", "t,o(b)", 0xf0000000, 0xfc000000, SM|RD_t|WR_t|RD_b, 0, I3 },
+{"scd", "t,A(b)", 0, (int) M_SCD_AB, INSN_MACRO, 0, I3 },
+{"sd", "t,o(b)", 0xfc000000, 0xfc000000, SM|RD_t|RD_b, 0, I3 },
+{"sd", "t,o(b)", 0, (int) M_SD_OB, INSN_MACRO, 0, I1 },
+{"sd", "t,A(b)", 0, (int) M_SD_AB, INSN_MACRO, 0, I1 },
+{"sdbbp", "", 0x0000000e, 0xffffffff, TRAP, 0, G2 },
+{"sdbbp", "c", 0x0000000e, 0xfc00ffff, TRAP, 0, G2 },
+{"sdbbp", "c,q", 0x0000000e, 0xfc00003f, TRAP, 0, G2 },
+{"sdbbp", "", 0x7000003f, 0xffffffff, TRAP, 0, I32 },
+{"sdbbp", "B", 0x7000003f, 0xfc00003f, TRAP, 0, I32 },
+{"sdc1", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, 0, I2 },
+{"sdc1", "E,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, 0, I2 },
+{"sdc1", "T,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, 0, I2 },
+{"sdc1", "E,A(b)", 0, (int) M_SDC1_AB, INSN_MACRO, 0, I2 },
+{"sdc2", "E,o(b)", 0xf8000000, 0xfc000000, SM|RD_C2|RD_b, 0, I2 },
+{"sdc2", "E,A(b)", 0, (int) M_SDC2_AB, INSN_MACRO, 0, I2 },
+{"sdc3", "E,o(b)", 0xfc000000, 0xfc000000, SM|RD_C3|RD_b, 0, I2 },
+{"sdc3", "E,A(b)", 0, (int) M_SDC3_AB, INSN_MACRO, 0, I2 },
+{"s.d", "T,o(b)", 0xf4000000, 0xfc000000, SM|RD_T|RD_b|FP_D, 0, I2 },
+{"s.d", "T,o(b)", 0, (int) M_S_DOB, INSN_MACRO, 0, I1 },
+{"s.d", "T,A(b)", 0, (int) M_S_DAB, INSN_MACRO, 0, I1 },
+{"sdl", "t,o(b)", 0xb0000000, 0xfc000000, SM|RD_t|RD_b, 0, I3 },
+{"sdl", "t,A(b)", 0, (int) M_SDL_AB, INSN_MACRO, 0, I3 },
+{"sdr", "t,o(b)", 0xb4000000, 0xfc000000, SM|RD_t|RD_b, 0, I3 },
+{"sdr", "t,A(b)", 0, (int) M_SDR_AB, INSN_MACRO, 0, I3 },
+{"sdxc1", "S,t(b)", 0x4c000009, 0xfc0007ff, SM|RD_S|RD_t|RD_b|FP_D, 0, I4|I33 },
+{"seb", "d,w", 0x7c000420, 0xffe007ff, WR_d|RD_t, 0, I33 },
+{"seh", "d,w", 0x7c000620, 0xffe007ff, WR_d|RD_t, 0, I33 },
+{"selsl", "d,v,t", 0x00000005, 0xfc0007ff, WR_d|RD_s|RD_t, 0, L1 },
+{"selsr", "d,v,t", 0x00000001, 0xfc0007ff, WR_d|RD_s|RD_t, 0, L1 },
+{"seq", "d,v,t", 0, (int) M_SEQ, INSN_MACRO, 0, I1 },
+{"seq", "d,v,I", 0, (int) M_SEQ_I, INSN_MACRO, 0, I1 },
+{"sge", "d,v,t", 0, (int) M_SGE, INSN_MACRO, 0, I1 },
+{"sge", "d,v,I", 0, (int) M_SGE_I, INSN_MACRO, 0, I1 },
+{"sgeu", "d,v,t", 0, (int) M_SGEU, INSN_MACRO, 0, I1 },
+{"sgeu", "d,v,I", 0, (int) M_SGEU_I, INSN_MACRO, 0, I1 },
+{"sgt", "d,v,t", 0, (int) M_SGT, INSN_MACRO, 0, I1 },
+{"sgt", "d,v,I", 0, (int) M_SGT_I, INSN_MACRO, 0, I1 },
+{"sgtu", "d,v,t", 0, (int) M_SGTU, INSN_MACRO, 0, I1 },
+{"sgtu", "d,v,I", 0, (int) M_SGTU_I, INSN_MACRO, 0, I1 },
+{"sh", "t,o(b)", 0xa4000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 },
+{"sh", "t,A(b)", 0, (int) M_SH_AB, INSN_MACRO, 0, I1 },
+{"shfl.bfla.qh", "X,Y,Z", 0x7a20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"shfl.mixh.ob", "X,Y,Z", 0x7980001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"shfl.mixh.ob", "D,S,T", 0x4980001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"shfl.mixh.qh", "X,Y,Z", 0x7820001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"shfl.mixl.ob", "X,Y,Z", 0x79c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"shfl.mixl.ob", "D,S,T", 0x49c0001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"shfl.mixl.qh", "X,Y,Z", 0x78a0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"shfl.pach.ob", "X,Y,Z", 0x7900001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"shfl.pach.ob", "D,S,T", 0x4900001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"shfl.pach.qh", "X,Y,Z", 0x7920001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"shfl.pacl.ob", "D,S,T", 0x4940001f, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"shfl.repa.qh", "X,Y,Z", 0x7b20001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"shfl.repb.qh", "X,Y,Z", 0x7ba0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"shfl.upsl.ob", "X,Y,Z", 0x78c0001f, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"sle", "d,v,t", 0, (int) M_SLE, INSN_MACRO, 0, I1 },
+{"sle", "d,v,I", 0, (int) M_SLE_I, INSN_MACRO, 0, I1 },
+{"sleu", "d,v,t", 0, (int) M_SLEU, INSN_MACRO, 0, I1 },
+{"sleu", "d,v,I", 0, (int) M_SLEU_I, INSN_MACRO, 0, I1 },
+{"sllv", "d,t,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 },
+{"sll", "d,w,s", 0x00000004, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, /* sllv */
+{"sll", "d,w,<", 0x00000000, 0xffe0003f, WR_d|RD_t, 0, I1 },
+{"sll.ob", "X,Y,Q", 0x78000010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"sll.ob", "D,S,T[e]", 0x48000010, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"sll.ob", "D,S,k", 0x4bc00010, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"sll.qh", "X,Y,Q", 0x78200010, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"slt", "d,v,t", 0x0000002a, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"slt", "d,v,I", 0, (int) M_SLT_I, INSN_MACRO, 0, I1 },
+{"slti", "t,r,j", 0x28000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+{"sltiu", "t,r,j", 0x2c000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+{"sltu", "d,v,t", 0x0000002b, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"sltu", "d,v,I", 0, (int) M_SLTU_I, INSN_MACRO, 0, I1 },
+{"sne", "d,v,t", 0, (int) M_SNE, INSN_MACRO, 0, I1 },
+{"sne", "d,v,I", 0, (int) M_SNE_I, INSN_MACRO, 0, I1 },
+{"sqrt.d", "D,S", 0x46200004, 0xffff003f, WR_D|RD_S|FP_D, 0, I2 },
+{"sqrt.s", "D,S", 0x46000004, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"sqrt.ps", "D,S", 0x46c00004, 0xffff003f, WR_D|RD_S|FP_D, 0, SB1 },
+{"srav", "d,t,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 },
+{"sra", "d,w,s", 0x00000007, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, /* srav */
+{"sra", "d,w,<", 0x00000003, 0xffe0003f, WR_d|RD_t, 0, I1 },
+{"sra.qh", "X,Y,Q", 0x78200013, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"srlv", "d,t,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 },
+{"srl", "d,w,s", 0x00000006, 0xfc0007ff, WR_d|RD_t|RD_s, 0, I1 }, /* srlv */
+{"srl", "d,w,<", 0x00000002, 0xffe0003f, WR_d|RD_t, 0, I1 },
+{"srl.ob", "X,Y,Q", 0x78000012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"srl.ob", "D,S,T[e]", 0x48000012, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"srl.ob", "D,S,k", 0x4bc00012, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"srl.qh", "X,Y,Q", 0x78200012, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+/* ssnop is at the start of the table. */
+{"standby", "", 0x42000021, 0xffffffff, 0, 0, V1 },
+{"sub", "d,v,t", 0x00000022, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"sub", "d,v,I", 0, (int) M_SUB_I, INSN_MACRO, 0, I1 },
+{"sub.d", "D,V,T", 0x46200001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I1 },
+{"sub.s", "D,V,T", 0x46000001, 0xffe0003f, WR_D|RD_S|RD_T|FP_S, 0, I1 },
+{"sub.ob", "X,Y,Q", 0x7800000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"sub.ob", "D,S,T", 0x4ac0000a, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"sub.ob", "D,S,T[e]", 0x4800000a, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"sub.ob", "D,S,k", 0x4bc0000a, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"sub.ps", "D,V,T", 0x46c00001, 0xffe0003f, WR_D|RD_S|RD_T|FP_D, 0, I5|I33 },
+{"sub.qh", "X,Y,Q", 0x7820000a, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"suba.ob", "Y,Q", 0x78000036, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"suba.qh", "Y,Q", 0x78200036, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"subl.ob", "Y,Q", 0x78000436, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"subl.qh", "Y,Q", 0x78200436, 0xfc2007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"subu", "d,v,t", 0x00000023, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"subu", "d,v,I", 0, (int) M_SUBU_I, INSN_MACRO, 0, I1 },
+{"suspend", "", 0x42000022, 0xffffffff, 0, 0, V1 },
+{"suxc1", "S,t(b)", 0x4c00000d, 0xfc0007ff, SM|RD_S|RD_t|RD_b, 0, I5|I33|N55},
+{"sw", "t,o(b)", 0xac000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 },
+{"sw", "t,A(b)", 0, (int) M_SW_AB, INSN_MACRO, 0, I1 },
+{"swc0", "E,o(b)", 0xe0000000, 0xfc000000, SM|RD_C0|RD_b, 0, I1 },
+{"swc0", "E,A(b)", 0, (int) M_SWC0_AB, INSN_MACRO, 0, I1 },
+{"swc1", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, 0, I1 },
+{"swc1", "E,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, 0, I1 },
+{"swc1", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, 0, I1 },
+{"swc1", "E,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, 0, I1 },
+{"s.s", "T,o(b)", 0xe4000000, 0xfc000000, SM|RD_T|RD_b|FP_S, 0, I1 }, /* swc1 */
+{"s.s", "T,A(b)", 0, (int) M_SWC1_AB, INSN_MACRO, 0, I1 },
+{"swc2", "E,o(b)", 0xe8000000, 0xfc000000, SM|RD_C2|RD_b, 0, I1 },
+{"swc2", "E,A(b)", 0, (int) M_SWC2_AB, INSN_MACRO, 0, I1 },
+{"swc3", "E,o(b)", 0xec000000, 0xfc000000, SM|RD_C3|RD_b, 0, I1 },
+{"swc3", "E,A(b)", 0, (int) M_SWC3_AB, INSN_MACRO, 0, I1 },
+{"swl", "t,o(b)", 0xa8000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 },
+{"swl", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, 0, I1 },
+{"scache", "t,o(b)", 0xa8000000, 0xfc000000, RD_t|RD_b, 0, I2 }, /* same */
+{"scache", "t,A(b)", 0, (int) M_SWL_AB, INSN_MACRO, 0, I2 }, /* as swl */
+{"swr", "t,o(b)", 0xb8000000, 0xfc000000, SM|RD_t|RD_b, 0, I1 },
+{"swr", "t,A(b)", 0, (int) M_SWR_AB, INSN_MACRO, 0, I1 },
+{"invalidate", "t,o(b)",0xb8000000, 0xfc000000, RD_t|RD_b, 0, I2 }, /* same */
+{"invalidate", "t,A(b)",0, (int) M_SWR_AB, INSN_MACRO, 0, I2 }, /* as swr */
+{"swxc1", "S,t(b)", 0x4c000008, 0xfc0007ff, SM|RD_S|RD_t|RD_b|FP_S, 0, I4|I33 },
+{"sync", "", 0x0000000f, 0xffffffff, INSN_SYNC, 0, I2|G1 },
+{"sync.p", "", 0x0000040f, 0xffffffff, INSN_SYNC, 0, I2 },
+{"sync.l", "", 0x0000000f, 0xffffffff, INSN_SYNC, 0, I2 },
+{"synci", "o(b)", 0x041f0000, 0xfc1f0000, SM|RD_b, 0, I33 },
+{"syscall", "", 0x0000000c, 0xffffffff, TRAP, 0, I1 },
+{"syscall", "B", 0x0000000c, 0xfc00003f, TRAP, 0, I1 },
+{"teqi", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
+{"teq", "s,t", 0x00000034, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
+{"teq", "s,t,q", 0x00000034, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 },
+{"teq", "s,j", 0x040c0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* teqi */
+{"teq", "s,I", 0, (int) M_TEQ_I, INSN_MACRO, 0, I2 },
+{"tgei", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
+{"tge", "s,t", 0x00000030, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
+{"tge", "s,t,q", 0x00000030, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 },
+{"tge", "s,j", 0x04080000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tgei */
+{"tge", "s,I", 0, (int) M_TGE_I, INSN_MACRO, 0, I2 },
+{"tgeiu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
+{"tgeu", "s,t", 0x00000031, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
+{"tgeu", "s,t,q", 0x00000031, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 },
+{"tgeu", "s,j", 0x04090000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tgeiu */
+{"tgeu", "s,I", 0, (int) M_TGEU_I, INSN_MACRO, 0, I2 },
+{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, 0, I1 },
+{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, 0, I1 },
+{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, 0, I1 },
+{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, 0, I1 },
+{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
+{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
+{"tlt", "s,t,q", 0x00000032, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 },
+{"tlt", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tlti */
+{"tlt", "s,I", 0, (int) M_TLT_I, INSN_MACRO, 0, I2 },
+{"tltiu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
+{"tltu", "s,t", 0x00000033, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
+{"tltu", "s,t,q", 0x00000033, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 },
+{"tltu", "s,j", 0x040b0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tltiu */
+{"tltu", "s,I", 0, (int) M_TLTU_I, INSN_MACRO, 0, I2 },
+{"tnei", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
+{"tne", "s,t", 0x00000036, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
+{"tne", "s,t,q", 0x00000036, 0xfc00003f, RD_s|RD_t|TRAP, 0, I2 },
+{"tne", "s,j", 0x040e0000, 0xfc1f0000, RD_s|TRAP, 0, I2 }, /* tnei */
+{"tne", "s,I", 0, (int) M_TNE_I, INSN_MACRO, 0, I2 },
+{"trunc.l.d", "D,S", 0x46200009, 0xffff003f, WR_D|RD_S|FP_D, 0, I3|I33 },
+{"trunc.l.s", "D,S", 0x46000009, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 },
+{"trunc.w.d", "D,S", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
+{"trunc.w.d", "D,S,x", 0x4620000d, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 },
+{"trunc.w.d", "D,S,t", 0, (int) M_TRUNCWD, INSN_MACRO, 0, I1 },
+{"trunc.w.s", "D,S", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"trunc.w.s", "D,S,x", 0x4600000d, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
+{"trunc.w.s", "D,S,t", 0, (int) M_TRUNCWS, INSN_MACRO, 0, I1 },
+{"uld", "t,o(b)", 0, (int) M_ULD, INSN_MACRO, 0, I3 },
+{"uld", "t,A(b)", 0, (int) M_ULD_A, INSN_MACRO, 0, I3 },
+{"ulh", "t,o(b)", 0, (int) M_ULH, INSN_MACRO, 0, I1 },
+{"ulh", "t,A(b)", 0, (int) M_ULH_A, INSN_MACRO, 0, I1 },
+{"ulhu", "t,o(b)", 0, (int) M_ULHU, INSN_MACRO, 0, I1 },
+{"ulhu", "t,A(b)", 0, (int) M_ULHU_A, INSN_MACRO, 0, I1 },
+{"ulw", "t,o(b)", 0, (int) M_ULW, INSN_MACRO, 0, I1 },
+{"ulw", "t,A(b)", 0, (int) M_ULW_A, INSN_MACRO, 0, I1 },
+{"usd", "t,o(b)", 0, (int) M_USD, INSN_MACRO, 0, I3 },
+{"usd", "t,A(b)", 0, (int) M_USD_A, INSN_MACRO, 0, I3 },
+{"ush", "t,o(b)", 0, (int) M_USH, INSN_MACRO, 0, I1 },
+{"ush", "t,A(b)", 0, (int) M_USH_A, INSN_MACRO, 0, I1 },
+{"usw", "t,o(b)", 0, (int) M_USW, INSN_MACRO, 0, I1 },
+{"usw", "t,A(b)", 0, (int) M_USW_A, INSN_MACRO, 0, I1 },
+{"wach.ob", "Y", 0x7a00003e, 0xffff07ff, RD_S|FP_D, WR_MACC, MX|SB1 },
+{"wach.ob", "S", 0x4a00003e, 0xffff07ff, RD_S, 0, N54 },
+{"wach.qh", "Y", 0x7a20003e, 0xffff07ff, RD_S|FP_D, WR_MACC, MX },
+{"wacl.ob", "Y,Z", 0x7800003e, 0xffe007ff, RD_S|RD_T|FP_D, WR_MACC, MX|SB1 },
+{"wacl.ob", "S,T", 0x4800003e, 0xffe007ff, RD_S|RD_T, 0, N54 },
+{"wacl.qh", "Y,Z", 0x7820003e, 0xffe007ff, RD_S|RD_T|FP_D, WR_MACC, MX },
+{"wait", "", 0x42000020, 0xffffffff, TRAP, 0, I3|I32 },
+{"wait", "J", 0x42000020, 0xfe00003f, TRAP, 0, I32|N55 },
+{"waiti", "", 0x42000020, 0xffffffff, TRAP, 0, L1 },
+{"wrpgpr", "d,w", 0x41c00000, 0xffe007ff, RD_t, 0, I33 },
+{"wsbh", "d,w", 0x7c0000a0, 0xffe007ff, WR_d|RD_t, 0, I33 },
+{"xor", "d,v,t", 0x00000026, 0xfc0007ff, WR_d|RD_s|RD_t, 0, I1 },
+{"xor", "t,r,I", 0, (int) M_XOR_I, INSN_MACRO, 0, I1 },
+{"xor.ob", "X,Y,Q", 0x7800000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX|SB1 },
+{"xor.ob", "D,S,T", 0x4ac0000d, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"xor.ob", "D,S,T[e]", 0x4800000d, 0xfe20003f, WR_D|RD_S|RD_T, 0, N54 },
+{"xor.ob", "D,S,k", 0x4bc0000d, 0xffe0003f, WR_D|RD_S|RD_T, 0, N54 },
+{"xor.qh", "X,Y,Q", 0x7820000d, 0xfc20003f, WR_D|RD_S|RD_T|FP_D, 0, MX },
+{"xori", "t,r,i", 0x38000000, 0xfc000000, WR_t|RD_s, 0, I1 },
+{"yield", "s", 0x7c000009, 0xfc1fffff, TRAP|RD_s, 0, MT32 },
+{"yield", "d,s", 0x7c000009, 0xfc1f07ff, TRAP|WR_d|RD_s, 0, MT32 },
+
+/* User Defined Instruction. */
+{"udi0", "s,t,d,+1",0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi0", "s,t,+2", 0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi0", "s,+3", 0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi0", "+4", 0x70000010, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi1", "s,t,d,+1",0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi1", "s,t,+2", 0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi1", "s,+3", 0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi1", "+4", 0x70000011, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi2", "s,t,d,+1",0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi2", "s,t,+2", 0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi2", "s,+3", 0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi2", "+4", 0x70000012, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi3", "s,t,d,+1",0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi3", "s,t,+2", 0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi3", "s,+3", 0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi3", "+4", 0x70000013, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi4", "s,t,d,+1",0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi4", "s,t,+2", 0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi4", "s,+3", 0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi4", "+4", 0x70000014, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi5", "s,t,d,+1",0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi5", "s,t,+2", 0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi5", "s,+3", 0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi5", "+4", 0x70000015, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi6", "s,t,d,+1",0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi6", "s,t,+2", 0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi6", "s,+3", 0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi6", "+4", 0x70000016, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi7", "s,t,d,+1",0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi7", "s,t,+2", 0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi7", "s,+3", 0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi7", "+4", 0x70000017, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi8", "s,t,d,+1",0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi8", "s,t,+2", 0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi8", "s,+3", 0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi8", "+4", 0x70000018, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi9", "s,t,d,+1",0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi9", "s,t,+2", 0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi9", "s,+3", 0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi9", "+4", 0x70000019, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi10", "s,t,d,+1",0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi10", "s,t,+2", 0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi10", "s,+3", 0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi10", "+4", 0x7000001a, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi11", "s,t,d,+1",0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi11", "s,t,+2", 0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi11", "s,+3", 0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi11", "+4", 0x7000001b, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi12", "s,t,d,+1",0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi12", "s,t,+2", 0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi12", "s,+3", 0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi12", "+4", 0x7000001c, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi13", "s,t,d,+1",0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi13", "s,t,+2", 0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi13", "s,+3", 0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi13", "+4", 0x7000001d, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi14", "s,t,d,+1",0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi14", "s,t,+2", 0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi14", "s,+3", 0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi14", "+4", 0x7000001e, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi15", "s,t,d,+1",0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi15", "s,t,+2", 0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi15", "s,+3", 0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+{"udi15", "+4", 0x7000001f, 0xfc00003f, WR_d|RD_s|RD_t, 0, I33 },
+
+/* Coprocessor 2 move/branch operations overlap with VR5400 .ob format
+ instructions so they are here for the latters to take precedence. */
+{"bc2f", "p", 0x49000000, 0xffff0000, CBD|RD_CC, 0, I1 },
+{"bc2f", "N,p", 0x49000000, 0xffe30000, CBD|RD_CC, 0, I32 },
+{"bc2fl", "p", 0x49020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 },
+{"bc2fl", "N,p", 0x49020000, 0xffe30000, CBL|RD_CC, 0, I32 },
+{"bc2t", "p", 0x49010000, 0xffff0000, CBD|RD_CC, 0, I1 },
+{"bc2t", "N,p", 0x49010000, 0xffe30000, CBD|RD_CC, 0, I32 },
+{"bc2tl", "p", 0x49030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 },
+{"bc2tl", "N,p", 0x49030000, 0xffe30000, CBL|RD_CC, 0, I32 },
+{"cfc2", "t,G", 0x48400000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I1 },
+{"ctc2", "t,G", 0x48c00000, 0xffe007ff, COD|RD_t|WR_CC, 0, I1 },
+{"dmfc2", "t,G", 0x48200000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I3 },
+{"dmfc2", "t,G,H", 0x48200000, 0xffe007f8, LCD|WR_t|RD_C2, 0, I64 },
+{"dmtc2", "t,G", 0x48a00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, 0, I3 },
+{"dmtc2", "t,G,H", 0x48a00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, 0, I64 },
+{"mfc2", "t,G", 0x48000000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I1 },
+{"mfc2", "t,G,H", 0x48000000, 0xffe007f8, LCD|WR_t|RD_C2, 0, I32 },
+{"mfhc2", "t,G", 0x48600000, 0xffe007ff, LCD|WR_t|RD_C2, 0, I33 },
+{"mfhc2", "t,G,H", 0x48600000, 0xffe007f8, LCD|WR_t|RD_C2, 0, I33 },
+{"mfhc2", "t,i", 0x48600000, 0xffe00000, LCD|WR_t|RD_C2, 0, I33 },
+{"mtc2", "t,G", 0x48800000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, 0, I1 },
+{"mtc2", "t,G,H", 0x48800000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, 0, I32 },
+{"mthc2", "t,G", 0x48e00000, 0xffe007ff, COD|RD_t|WR_C2|WR_CC, 0, I33 },
+{"mthc2", "t,G,H", 0x48e00000, 0xffe007f8, COD|RD_t|WR_C2|WR_CC, 0, I33 },
+{"mthc2", "t,i", 0x48e00000, 0xffe00000, COD|RD_t|WR_C2|WR_CC, 0, I33 },
+
+/* Coprocessor 3 move/branch operations overlap with MIPS IV COP1X
+ instructions, so they are here for the latters to take precedence. */
+{"bc3f", "p", 0x4d000000, 0xffff0000, CBD|RD_CC, 0, I1 },
+{"bc3fl", "p", 0x4d020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 },
+{"bc3t", "p", 0x4d010000, 0xffff0000, CBD|RD_CC, 0, I1 },
+{"bc3tl", "p", 0x4d030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 },
+{"cfc3", "t,G", 0x4c400000, 0xffe007ff, LCD|WR_t|RD_C3, 0, I1 },
+{"ctc3", "t,G", 0x4cc00000, 0xffe007ff, COD|RD_t|WR_CC, 0, I1 },
+{"dmfc3", "t,G", 0x4c200000, 0xffe007ff, LCD|WR_t|RD_C3, 0, I3 },
+{"dmtc3", "t,G", 0x4ca00000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, 0, I3 },
+{"mfc3", "t,G", 0x4c000000, 0xffe007ff, LCD|WR_t|RD_C3, 0, I1 },
+{"mfc3", "t,G,H", 0x4c000000, 0xffe007f8, LCD|WR_t|RD_C3, 0, I32 },
+{"mtc3", "t,G", 0x4c800000, 0xffe007ff, COD|RD_t|WR_C3|WR_CC, 0, I1 },
+{"mtc3", "t,G,H", 0x4c800000, 0xffe007f8, COD|RD_t|WR_C3|WR_CC, 0, I32 },
+
+/* No hazard protection on coprocessor instructions--they shouldn't
+ change the state of the processor and if they do it's up to the
+ user to put in nops as necessary. These are at the end so that the
+ disassembler recognizes more specific versions first. */
+{"c0", "C", 0x42000000, 0xfe000000, 0, 0, I1 },
+{"c1", "C", 0x46000000, 0xfe000000, 0, 0, I1 },
+{"c2", "C", 0x4a000000, 0xfe000000, 0, 0, I1 },
+{"c3", "C", 0x4e000000, 0xfe000000, 0, 0, I1 },
+{"cop0", "C", 0, (int) M_COP0, INSN_MACRO, 0, I1 },
+{"cop1", "C", 0, (int) M_COP1, INSN_MACRO, 0, I1 },
+{"cop2", "C", 0, (int) M_COP2, INSN_MACRO, 0, I1 },
+{"cop3", "C", 0, (int) M_COP3, INSN_MACRO, 0, I1 },
+ /* Conflicts with the 4650's "mul" instruction. Nobody's using the
+ 4010 any more, so move this insn out of the way. If the object
+ format gave us more info, we could do this right. */
+{"addciu", "t,r,j", 0x70000000, 0xfc000000, WR_t|RD_s, 0, L1 },
+/* MIPS DSP ASE */
+{"absq_s.ph", "d,t", 0x7c000252, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"absq_s.pw", "d,t", 0x7c000456, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"absq_s.qh", "d,t", 0x7c000256, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"absq_s.w", "d,t", 0x7c000452, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"addq.ph", "d,s,t", 0x7c000290, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"addq.pw", "d,s,t", 0x7c000494, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"addq.qh", "d,s,t", 0x7c000294, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"addq_s.ph", "d,s,t", 0x7c000390, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"addq_s.pw", "d,s,t", 0x7c000594, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"addq_s.qh", "d,s,t", 0x7c000394, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"addq_s.w", "d,s,t", 0x7c000590, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"addsc", "d,s,t", 0x7c000410, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"addu.ob", "d,s,t", 0x7c000014, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"addu.qb", "d,s,t", 0x7c000010, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"addu_s.ob", "d,s,t", 0x7c000114, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"addu_s.qb", "d,s,t", 0x7c000110, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"addwc", "d,s,t", 0x7c000450, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"bitrev", "d,t", 0x7c0006d2, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"bposge32", "p", 0x041c0000, 0xffff0000, CBD, 0, D32 },
+{"bposge64", "p", 0x041d0000, 0xffff0000, CBD, 0, D64 },
+{"cmp.eq.ph", "s,t", 0x7c000211, 0xfc00ffff, RD_s|RD_t, 0, D32 },
+{"cmp.eq.pw", "s,t", 0x7c000415, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmp.eq.qh", "s,t", 0x7c000215, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmpgu.eq.ob", "d,s,t", 0x7c000115, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"cmpgu.eq.qb", "d,s,t", 0x7c000111, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"cmpgu.le.ob", "d,s,t", 0x7c000195, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"cmpgu.le.qb", "d,s,t", 0x7c000191, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"cmpgu.lt.ob", "d,s,t", 0x7c000155, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"cmpgu.lt.qb", "d,s,t", 0x7c000151, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"cmp.le.ph", "s,t", 0x7c000291, 0xfc00ffff, RD_s|RD_t, 0, D32 },
+{"cmp.le.pw", "s,t", 0x7c000495, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmp.le.qh", "s,t", 0x7c000295, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmp.lt.ph", "s,t", 0x7c000251, 0xfc00ffff, RD_s|RD_t, 0, D32 },
+{"cmp.lt.pw", "s,t", 0x7c000455, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmp.lt.qh", "s,t", 0x7c000255, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmpu.eq.ob", "s,t", 0x7c000015, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmpu.eq.qb", "s,t", 0x7c000011, 0xfc00ffff, RD_s|RD_t, 0, D32 },
+{"cmpu.le.ob", "s,t", 0x7c000095, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmpu.le.qb", "s,t", 0x7c000091, 0xfc00ffff, RD_s|RD_t, 0, D32 },
+{"cmpu.lt.ob", "s,t", 0x7c000055, 0xfc00ffff, RD_s|RD_t, 0, D64 },
+{"cmpu.lt.qb", "s,t", 0x7c000051, 0xfc00ffff, RD_s|RD_t, 0, D32 },
+{"dextpdp", "t,7,6", 0x7c0002bc, 0xfc00e7ff, WR_t|RD_a|DSP_VOLA, 0, D64 },
+{"dextpdpv", "t,7,s", 0x7c0002fc, 0xfc00e7ff, WR_t|RD_a|RD_s|DSP_VOLA, 0, D64 },
+{"dextp", "t,7,6", 0x7c0000bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextpv", "t,7,s", 0x7c0000fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextr.l", "t,7,6", 0x7c00043c, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextr_r.l", "t,7,6", 0x7c00053c, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextr_rs.l", "t,7,6", 0x7c0005bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextr_rs.w", "t,7,6", 0x7c0001bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextr_r.w", "t,7,6", 0x7c00013c, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextr_s.h", "t,7,6", 0x7c0003bc, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dextrv.l", "t,7,s", 0x7c00047c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextrv_r.l", "t,7,s", 0x7c00057c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextrv_rs.l", "t,7,s", 0x7c0005fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextrv_rs.w", "t,7,s", 0x7c0001fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextrv_r.w", "t,7,s", 0x7c00017c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextrv_s.h", "t,7,s", 0x7c0003fc, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextrv.w", "t,7,s", 0x7c00007c, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D64 },
+{"dextr.w", "t,7,6", 0x7c00003c, 0xfc00e7ff, WR_t|RD_a, 0, D64 },
+{"dinsv", "t,s", 0x7c00000d, 0xfc00ffff, WR_t|RD_s, 0, D64 },
+{"dmadd", "7,s,t", 0x7c000674, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dmaddu", "7,s,t", 0x7c000774, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dmsub", "7,s,t", 0x7c0006f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dmsubu", "7,s,t", 0x7c0007f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dmthlip", "s,7", 0x7c0007fc, 0xfc1fe7ff, RD_s|MOD_a|DSP_VOLA, 0, D64 },
+{"dpaq_sa.l.pw", "7,s,t", 0x7c000334, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpaq_sa.l.w", "7,s,t", 0x7c000330, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpaq_s.w.ph", "7,s,t", 0x7c000130, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpaq_s.w.qh", "7,s,t", 0x7c000134, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpau.h.obl", "7,s,t", 0x7c0000f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpau.h.obr", "7,s,t", 0x7c0001f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpau.h.qbl", "7,s,t", 0x7c0000f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpau.h.qbr", "7,s,t", 0x7c0001f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpsq_sa.l.pw", "7,s,t", 0x7c000374, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpsq_sa.l.w", "7,s,t", 0x7c000370, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpsq_s.w.ph", "7,s,t", 0x7c000170, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpsq_s.w.qh", "7,s,t", 0x7c000174, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpsu.h.obl", "7,s,t", 0x7c0002f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpsu.h.obr", "7,s,t", 0x7c0003f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"dpsu.h.qbl", "7,s,t", 0x7c0002f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dpsu.h.qbr", "7,s,t", 0x7c0003f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"dshilo", "7,:", 0x7c0006bc, 0xfc07e7ff, MOD_a, 0, D64 },
+{"dshilov", "7,s", 0x7c0006fc, 0xfc1fe7ff, MOD_a|RD_s, 0, D64 },
+{"extpdp", "t,7,6", 0x7c0002b8, 0xfc00e7ff, WR_t|RD_a|DSP_VOLA, 0, D32 },
+{"extpdpv", "t,7,s", 0x7c0002f8, 0xfc00e7ff, WR_t|RD_a|RD_s|DSP_VOLA, 0, D32 },
+{"extp", "t,7,6", 0x7c0000b8, 0xfc00e7ff, WR_t|RD_a, 0, D32 },
+{"extpv", "t,7,s", 0x7c0000f8, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 },
+{"extr_rs.w", "t,7,6", 0x7c0001b8, 0xfc00e7ff, WR_t|RD_a, 0, D32 },
+{"extr_r.w", "t,7,6", 0x7c000138, 0xfc00e7ff, WR_t|RD_a, 0, D32 },
+{"extr_s.h", "t,7,6", 0x7c0003b8, 0xfc00e7ff, WR_t|RD_a, 0, D32 },
+{"extrv_rs.w", "t,7,s", 0x7c0001f8, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 },
+{"extrv_r.w", "t,7,s", 0x7c000178, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 },
+{"extrv_s.h", "t,7,s", 0x7c0003f8, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 },
+{"extrv.w", "t,7,s", 0x7c000078, 0xfc00e7ff, WR_t|RD_a|RD_s, 0, D32 },
+{"extr.w", "t,7,6", 0x7c000038, 0xfc00e7ff, WR_t|RD_a, 0, D32 },
+{"insv", "t,s", 0x7c00000c, 0xfc00ffff, WR_t|RD_s, 0, D32 },
+{"lbux", "d,t(b)", 0x7c00018a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D32 },
+{"ldx", "d,t(b)", 0x7c00020a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D64 },
+{"lhx", "d,t(b)", 0x7c00010a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D32 },
+{"lwx", "d,t(b)", 0x7c00000a, 0xfc0007ff, LDD|WR_d|RD_t|RD_b, 0, D32 },
+{"maq_sa.w.phl", "7,s,t", 0x7c000430, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"maq_sa.w.phr", "7,s,t", 0x7c0004b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"maq_sa.w.qhll", "7,s,t", 0x7c000434, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_sa.w.qhlr", "7,s,t", 0x7c000474, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_sa.w.qhrl", "7,s,t", 0x7c0004b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_sa.w.qhrr", "7,s,t", 0x7c0004f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_s.l.pwl", "7,s,t", 0x7c000734, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_s.l.pwr", "7,s,t", 0x7c0007b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_s.w.phl", "7,s,t", 0x7c000530, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"maq_s.w.phr", "7,s,t", 0x7c0005b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"maq_s.w.qhll", "7,s,t", 0x7c000534, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_s.w.qhlr", "7,s,t", 0x7c000574, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_s.w.qhrl", "7,s,t", 0x7c0005b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"maq_s.w.qhrr", "7,s,t", 0x7c0005f4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"modsub", "d,s,t", 0x7c000490, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"mthlip", "s,7", 0x7c0007f8, 0xfc1fe7ff, RD_s|MOD_a|DSP_VOLA, 0, D32 },
+{"muleq_s.pw.qhl", "d,s,t", 0x7c000714, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 },
+{"muleq_s.pw.qhr", "d,s,t", 0x7c000754, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 },
+{"muleq_s.w.phl", "d,s,t", 0x7c000710, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 },
+{"muleq_s.w.phr", "d,s,t", 0x7c000750, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 },
+{"muleu_s.ph.qbl", "d,s,t", 0x7c000190, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 },
+{"muleu_s.ph.qbr", "d,s,t", 0x7c0001d0, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 },
+{"muleu_s.qh.obl", "d,s,t", 0x7c000194, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 },
+{"muleu_s.qh.obr", "d,s,t", 0x7c0001d4, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 },
+{"mulq_rs.ph", "d,s,t", 0x7c0007d0, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D32 },
+{"mulq_rs.qh", "d,s,t", 0x7c0007d4, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D64 },
+{"mulsaq_s.l.pw", "7,s,t", 0x7c0003b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"mulsaq_s.w.ph", "7,s,t", 0x7c0001b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D32 },
+{"mulsaq_s.w.qh", "7,s,t", 0x7c0001b4, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D64 },
+{"packrl.ph", "d,s,t", 0x7c000391, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"packrl.pw", "d,s,t", 0x7c000395, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"pick.ob", "d,s,t", 0x7c0000d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"pick.ph", "d,s,t", 0x7c0002d1, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"pick.pw", "d,s,t", 0x7c0004d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"pick.qb", "d,s,t", 0x7c0000d1, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"pick.qh", "d,s,t", 0x7c0002d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"preceq.pw.qhla", "d,t", 0x7c000396, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceq.pw.qhl", "d,t", 0x7c000316, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceq.pw.qhra", "d,t", 0x7c0003d6, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceq.pw.qhr", "d,t", 0x7c000356, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceq.s.l.pwl", "d,t", 0x7c000516, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceq.s.l.pwr", "d,t", 0x7c000556, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"precequ.ph.qbla", "d,t", 0x7c000192, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"precequ.ph.qbl", "d,t", 0x7c000112, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"precequ.ph.qbra", "d,t", 0x7c0001d2, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"precequ.ph.qbr", "d,t", 0x7c000152, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"precequ.pw.qhla", "d,t", 0x7c000196, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"precequ.pw.qhl", "d,t", 0x7c000116, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"precequ.pw.qhra", "d,t", 0x7c0001d6, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"precequ.pw.qhr", "d,t", 0x7c000156, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceq.w.phl", "d,t", 0x7c000312, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"preceq.w.phr", "d,t", 0x7c000352, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"preceu.ph.qbla", "d,t", 0x7c000792, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"preceu.ph.qbl", "d,t", 0x7c000712, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"preceu.ph.qbra", "d,t", 0x7c0007d2, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"preceu.ph.qbr", "d,t", 0x7c000752, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"preceu.qh.obla", "d,t", 0x7c000796, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceu.qh.obl", "d,t", 0x7c000716, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceu.qh.obra", "d,t", 0x7c0007d6, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"preceu.qh.obr", "d,t", 0x7c000756, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"precrq.ob.qh", "d,s,t", 0x7c000315, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"precrq.ph.w", "d,s,t", 0x7c000511, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"precrq.pw.l", "d,s,t", 0x7c000715, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"precrq.qb.ph", "d,s,t", 0x7c000311, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"precrq.qh.pw", "d,s,t", 0x7c000515, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"precrq_rs.ph.w", "d,s,t", 0x7c000551, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"precrq_rs.qh.pw", "d,s,t", 0x7c000555, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"precrqu_s.ob.qh", "d,s,t", 0x7c0003d5, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"precrqu_s.qb.ph", "d,s,t", 0x7c0003d1, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"raddu.l.ob", "d,s", 0x7c000514, 0xfc1f07ff, WR_d|RD_s, 0, D64 },
+{"raddu.w.qb", "d,s", 0x7c000510, 0xfc1f07ff, WR_d|RD_s, 0, D32 },
+{"rddsp", "d", 0x7fff04b8, 0xffff07ff, WR_d, 0, D32 },
+{"rddsp", "d,'", 0x7c0004b8, 0xffc007ff, WR_d, 0, D32 },
+{"repl.ob", "d,5", 0x7c000096, 0xff0007ff, WR_d, 0, D64 },
+{"repl.ph", "d,@", 0x7c000292, 0xfc0007ff, WR_d, 0, D32 },
+{"repl.pw", "d,@", 0x7c000496, 0xfc0007ff, WR_d, 0, D64 },
+{"repl.qb", "d,5", 0x7c000092, 0xff0007ff, WR_d, 0, D32 },
+{"repl.qh", "d,@", 0x7c000296, 0xfc0007ff, WR_d, 0, D64 },
+{"replv.ob", "d,t", 0x7c0000d6, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"replv.ph", "d,t", 0x7c0002d2, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"replv.pw", "d,t", 0x7c0004d6, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"replv.qb", "d,t", 0x7c0000d2, 0xffe007ff, WR_d|RD_t, 0, D32 },
+{"replv.qh", "d,t", 0x7c0002d6, 0xffe007ff, WR_d|RD_t, 0, D64 },
+{"shilo", "7,0", 0x7c0006b8, 0xfc0fe7ff, MOD_a, 0, D32 },
+{"shilov", "7,s", 0x7c0006f8, 0xfc1fe7ff, MOD_a|RD_s, 0, D32 },
+{"shll.ob", "d,t,3", 0x7c000017, 0xff0007ff, WR_d|RD_t, 0, D64 },
+{"shll.ph", "d,t,4", 0x7c000213, 0xfe0007ff, WR_d|RD_t, 0, D32 },
+{"shll.pw", "d,t,6", 0x7c000417, 0xfc0007ff, WR_d|RD_t, 0, D64 },
+{"shll.qb", "d,t,3", 0x7c000013, 0xff0007ff, WR_d|RD_t, 0, D32 },
+{"shll.qh", "d,t,4", 0x7c000217, 0xfe0007ff, WR_d|RD_t, 0, D64 },
+{"shll_s.ph", "d,t,4", 0x7c000313, 0xfe0007ff, WR_d|RD_t, 0, D32 },
+{"shll_s.pw", "d,t,6", 0x7c000517, 0xfc0007ff, WR_d|RD_t, 0, D64 },
+{"shll_s.qh", "d,t,4", 0x7c000317, 0xfe0007ff, WR_d|RD_t, 0, D64 },
+{"shll_s.w", "d,t,6", 0x7c000513, 0xfc0007ff, WR_d|RD_t, 0, D32 },
+{"shllv.ob", "d,t,s", 0x7c000097, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shllv.ph", "d,t,s", 0x7c000293, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shllv.pw", "d,t,s", 0x7c000497, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shllv.qb", "d,t,s", 0x7c000093, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shllv.qh", "d,t,s", 0x7c000297, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shllv_s.ph", "d,t,s", 0x7c000393, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shllv_s.pw", "d,t,s", 0x7c000597, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shllv_s.qh", "d,t,s", 0x7c000397, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shllv_s.w", "d,t,s", 0x7c000593, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shra.ph", "d,t,4", 0x7c000253, 0xfe0007ff, WR_d|RD_t, 0, D32 },
+{"shra.pw", "d,t,6", 0x7c000457, 0xfc0007ff, WR_d|RD_t, 0, D64 },
+{"shra.qh", "d,t,4", 0x7c000257, 0xfe0007ff, WR_d|RD_t, 0, D64 },
+{"shra_r.ph", "d,t,4", 0x7c000353, 0xfe0007ff, WR_d|RD_t, 0, D32 },
+{"shra_r.pw", "d,t,6", 0x7c000557, 0xfc0007ff, WR_d|RD_t, 0, D64 },
+{"shra_r.qh", "d,t,4", 0x7c000357, 0xfe0007ff, WR_d|RD_t, 0, D64 },
+{"shra_r.w", "d,t,6", 0x7c000553, 0xfc0007ff, WR_d|RD_t, 0, D32 },
+{"shrav.ph", "d,t,s", 0x7c0002d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shrav.pw", "d,t,s", 0x7c0004d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shrav.qh", "d,t,s", 0x7c0002d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shrav_r.ph", "d,t,s", 0x7c0003d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shrav_r.pw", "d,t,s", 0x7c0005d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shrav_r.qh", "d,t,s", 0x7c0003d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shrav_r.w", "d,t,s", 0x7c0005d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"shrl.ob", "d,t,3", 0x7c000057, 0xff0007ff, WR_d|RD_t, 0, D64 },
+{"shrl.qb", "d,t,3", 0x7c000053, 0xff0007ff, WR_d|RD_t, 0, D32 },
+{"shrlv.ob", "d,t,s", 0x7c0000d7, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"shrlv.qb", "d,t,s", 0x7c0000d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"subq.ph", "d,s,t", 0x7c0002d0, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"subq.pw", "d,s,t", 0x7c0004d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"subq.qh", "d,s,t", 0x7c0002d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"subq_s.ph", "d,s,t", 0x7c0003d0, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"subq_s.pw", "d,s,t", 0x7c0005d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"subq_s.qh", "d,s,t", 0x7c0003d4, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"subq_s.w", "d,s,t", 0x7c0005d0, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"subu.ob", "d,s,t", 0x7c000054, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"subu.qb", "d,s,t", 0x7c000050, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"subu_s.ob", "d,s,t", 0x7c000154, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D64 },
+{"subu_s.qb", "d,s,t", 0x7c000150, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D32 },
+{"wrdsp", "s", 0x7c1ffcf8, 0xfc1fffff, RD_s|DSP_VOLA, 0, D32 },
+{"wrdsp", "s,8", 0x7c0004f8, 0xfc1e07ff, RD_s|DSP_VOLA, 0, D32 },
+/* MIPS DSP ASE Rev2 */
+{"absq_s.qb", "d,t", 0x7c000052, 0xffe007ff, WR_d|RD_t, 0, D33 },
+{"addu.ph", "d,s,t", 0x7c000210, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"addu_s.ph", "d,s,t", 0x7c000310, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"adduh.qb", "d,s,t", 0x7c000018, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"adduh_r.qb", "d,s,t", 0x7c000098, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"append", "t,s,h", 0x7c000031, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 },
+{"balign", "t,s,I", 0, (int) M_BALIGN, INSN_MACRO, 0, D33 },
+{"balign", "t,s,2", 0x7c000431, 0xfc00e7ff, WR_t|RD_t|RD_s, 0, D33 },
+{"cmpgdu.eq.qb", "d,s,t", 0x7c000611, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"cmpgdu.lt.qb", "d,s,t", 0x7c000651, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"cmpgdu.le.qb", "d,s,t", 0x7c000691, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"dpa.w.ph", "7,s,t", 0x7c000030, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"dps.w.ph", "7,s,t", 0x7c000070, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"mul.ph", "d,s,t", 0x7c000318, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 },
+{"mul_s.ph", "d,s,t", 0x7c000398, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 },
+{"mulq_rs.w", "d,s,t", 0x7c0005d8, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 },
+{"mulq_s.ph", "d,s,t", 0x7c000790, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 },
+{"mulq_s.w", "d,s,t", 0x7c000598, 0xfc0007ff, WR_d|RD_s|RD_t|WR_HILO, 0, D33 },
+{"mulsa.w.ph", "7,s,t", 0x7c0000b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"precr.qb.ph", "d,s,t", 0x7c000351, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"precr_sra.ph.w", "t,s,h", 0x7c000791, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 },
+{"precr_sra_r.ph.w", "t,s,h", 0x7c0007d1, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 },
+{"prepend", "t,s,h", 0x7c000071, 0xfc0007ff, WR_t|RD_t|RD_s, 0, D33 },
+{"shra.qb", "d,t,3", 0x7c000113, 0xff0007ff, WR_d|RD_t, 0, D33 },
+{"shra_r.qb", "d,t,3", 0x7c000153, 0xff0007ff, WR_d|RD_t, 0, D33 },
+{"shrav.qb", "d,t,s", 0x7c000193, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"shrav_r.qb", "d,t,s", 0x7c0001d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"shrl.ph", "d,t,4", 0x7c000653, 0xfe0007ff, WR_d|RD_t, 0, D33 },
+{"shrlv.ph", "d,t,s", 0x7c0006d3, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subu.ph", "d,s,t", 0x7c000250, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subu_s.ph", "d,s,t", 0x7c000350, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subuh.qb", "d,s,t", 0x7c000058, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subuh_r.qb", "d,s,t", 0x7c0000d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"addqh.ph", "d,s,t", 0x7c000218, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"addqh_r.ph", "d,s,t", 0x7c000298, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"addqh.w", "d,s,t", 0x7c000418, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"addqh_r.w", "d,s,t", 0x7c000498, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subqh.ph", "d,s,t", 0x7c000258, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subqh_r.ph", "d,s,t", 0x7c0002d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subqh.w", "d,s,t", 0x7c000458, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"subqh_r.w", "d,s,t", 0x7c0004d8, 0xfc0007ff, WR_d|RD_s|RD_t, 0, D33 },
+{"dpax.w.ph", "7,s,t", 0x7c000230, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"dpsx.w.ph", "7,s,t", 0x7c000270, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"dpaqx_s.w.ph", "7,s,t", 0x7c000630, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"dpaqx_sa.w.ph", "7,s,t", 0x7c0006b0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"dpsqx_s.w.ph", "7,s,t", 0x7c000670, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+{"dpsqx_sa.w.ph", "7,s,t", 0x7c0006f0, 0xfc00e7ff, MOD_a|RD_s|RD_t, 0, D33 },
+/* Move bc0* after mftr and mttr to avoid opcode collision. */
+{"bc0f", "p", 0x41000000, 0xffff0000, CBD|RD_CC, 0, I1 },
+{"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 },
+{"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, 0, I1 },
+{"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 },
+/* ST Microelectronics Loongson-2E and -2F. */
+{"mult.g", "d,s,t", 0x7c000018, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"mult.g", "d,s,t", 0x70000010, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"multu.g", "d,s,t", 0x7c000019, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"multu.g", "d,s,t", 0x70000012, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"dmult.g", "d,s,t", 0x7c00001c, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"dmult.g", "d,s,t", 0x70000011, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"dmultu.g", "d,s,t", 0x7c00001d, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"dmultu.g", "d,s,t", 0x70000013, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"div.g", "d,s,t", 0x7c00001a, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"div.g", "d,s,t", 0x70000014, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"divu.g", "d,s,t", 0x7c00001b, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"divu.g", "d,s,t", 0x70000016, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"ddiv.g", "d,s,t", 0x7c00001e, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"ddiv.g", "d,s,t", 0x70000015, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"ddivu.g", "d,s,t", 0x7c00001f, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"ddivu.g", "d,s,t", 0x70000017, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"mod.g", "d,s,t", 0x7c000022, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"mod.g", "d,s,t", 0x7000001c, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"modu.g", "d,s,t", 0x7c000023, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"modu.g", "d,s,t", 0x7000001e, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"dmod.g", "d,s,t", 0x7c000026, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"dmod.g", "d,s,t", 0x7000001d, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+{"dmodu.g", "d,s,t", 0x7c000027, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E },
+{"dmodu.g", "d,s,t", 0x7000001f, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F },
+};
+
+#define MIPS_NUM_OPCODES \
+ ((sizeof mips_builtin_opcodes) / (sizeof (mips_builtin_opcodes[0])))
+const int bfd_mips_num_builtin_opcodes = MIPS_NUM_OPCODES;
+
+/* const removed from the following to allow for dynamic extensions to the
+ * built-in instruction set. */
+struct mips_opcode *mips_opcodes =
+ (struct mips_opcode *) mips_builtin_opcodes;
+int bfd_mips_num_opcodes = MIPS_NUM_OPCODES;
+#undef MIPS_NUM_OPCODES
+
+/* Mips instructions are at maximum this many bytes long. */
+#define INSNLEN 4
+
+
+/* FIXME: These should be shared with gdb somehow. */
+
+struct mips_cp0sel_name
+{
+ unsigned int cp0reg;
+ unsigned int sel;
+ const char * const name;
+};
+
+/* The mips16 registers. */
+static const unsigned int mips16_to_32_reg_map[] =
+{
+ 16, 17, 2, 3, 4, 5, 6, 7
+};
+
+#define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]]
+
+
+static const char * const mips_gpr_names_numeric[32] =
+{
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_gpr_names_oldabi[32] =
+{
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char * const mips_gpr_names_newabi[32] =
+{
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
+};
+
+static const char * const mips_fpr_names_numeric[32] =
+{
+ "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+ "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31"
+};
+
+static const char * const mips_fpr_names_32[32] =
+{
+ "fv0", "fv0f", "fv1", "fv1f", "ft0", "ft0f", "ft1", "ft1f",
+ "ft2", "ft2f", "ft3", "ft3f", "fa0", "fa0f", "fa1", "fa1f",
+ "ft4", "ft4f", "ft5", "ft5f", "fs0", "fs0f", "fs1", "fs1f",
+ "fs2", "fs2f", "fs3", "fs3f", "fs4", "fs4f", "fs5", "fs5f"
+};
+
+static const char * const mips_fpr_names_n32[32] =
+{
+ "fv0", "ft14", "fv1", "ft15", "ft0", "ft1", "ft2", "ft3",
+ "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3",
+ "fa4", "fa5", "fa6", "fa7", "fs0", "ft8", "fs1", "ft9",
+ "fs2", "ft10", "fs3", "ft11", "fs4", "ft12", "fs5", "ft13"
+};
+
+static const char * const mips_fpr_names_64[32] =
+{
+ "fv0", "ft12", "fv1", "ft13", "ft0", "ft1", "ft2", "ft3",
+ "ft4", "ft5", "ft6", "ft7", "fa0", "fa1", "fa2", "fa3",
+ "fa4", "fa5", "fa6", "fa7", "ft8", "ft9", "ft10", "ft11",
+ "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7"
+};
+
+static const char * const mips_cp0_names_numeric[32] =
+{
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_cp0_names_mips3264[32] =
+{
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "$7",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
+ "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] =
+{
+ { 4, 1, "c0_contextconfig" },
+ { 0, 1, "c0_mvpcontrol" },
+ { 0, 2, "c0_mvpconf0" },
+ { 0, 3, "c0_mvpconf1" },
+ { 1, 1, "c0_vpecontrol" },
+ { 1, 2, "c0_vpeconf0" },
+ { 1, 3, "c0_vpeconf1" },
+ { 1, 4, "c0_yqmask" },
+ { 1, 5, "c0_vpeschedule" },
+ { 1, 6, "c0_vpeschefback" },
+ { 2, 1, "c0_tcstatus" },
+ { 2, 2, "c0_tcbind" },
+ { 2, 3, "c0_tcrestart" },
+ { 2, 4, "c0_tchalt" },
+ { 2, 5, "c0_tccontext" },
+ { 2, 6, "c0_tcschedule" },
+ { 2, 7, "c0_tcschefback" },
+ { 5, 1, "c0_pagegrain" },
+ { 6, 1, "c0_srsconf0" },
+ { 6, 2, "c0_srsconf1" },
+ { 6, 3, "c0_srsconf2" },
+ { 6, 4, "c0_srsconf3" },
+ { 6, 5, "c0_srsconf4" },
+ { 12, 1, "c0_intctl" },
+ { 12, 2, "c0_srsctl" },
+ { 12, 3, "c0_srsmap" },
+ { 15, 1, "c0_ebase" },
+ { 16, 1, "c0_config1" },
+ { 16, 2, "c0_config2" },
+ { 16, 3, "c0_config3" },
+ { 18, 1, "c0_watchlo,1" },
+ { 18, 2, "c0_watchlo,2" },
+ { 18, 3, "c0_watchlo,3" },
+ { 18, 4, "c0_watchlo,4" },
+ { 18, 5, "c0_watchlo,5" },
+ { 18, 6, "c0_watchlo,6" },
+ { 18, 7, "c0_watchlo,7" },
+ { 19, 1, "c0_watchhi,1" },
+ { 19, 2, "c0_watchhi,2" },
+ { 19, 3, "c0_watchhi,3" },
+ { 19, 4, "c0_watchhi,4" },
+ { 19, 5, "c0_watchhi,5" },
+ { 19, 6, "c0_watchhi,6" },
+ { 19, 7, "c0_watchhi,7" },
+ { 23, 1, "c0_tracecontrol" },
+ { 23, 2, "c0_tracecontrol2" },
+ { 23, 3, "c0_usertracedata" },
+ { 23, 4, "c0_tracebpc" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 27, 1, "c0_cacheerr,1" },
+ { 27, 2, "c0_cacheerr,2" },
+ { 27, 3, "c0_cacheerr,3" },
+ { 28, 1, "c0_datalo" },
+ { 28, 2, "c0_taglo1" },
+ { 28, 3, "c0_datalo1" },
+ { 28, 4, "c0_taglo2" },
+ { 28, 5, "c0_datalo2" },
+ { 28, 6, "c0_taglo3" },
+ { 28, 7, "c0_datalo3" },
+ { 29, 1, "c0_datahi" },
+ { 29, 2, "c0_taghi1" },
+ { 29, 3, "c0_datahi1" },
+ { 29, 4, "c0_taghi2" },
+ { 29, 5, "c0_datahi2" },
+ { 29, 6, "c0_taghi3" },
+ { 29, 7, "c0_datahi3" },
+};
+
+static const char * const mips_cp0_names_mips3264r2[32] =
+{
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "c0_hwrena",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr",
+ "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_mips3264r2[] =
+{
+ { 4, 1, "c0_contextconfig" },
+ { 5, 1, "c0_pagegrain" },
+ { 12, 1, "c0_intctl" },
+ { 12, 2, "c0_srsctl" },
+ { 12, 3, "c0_srsmap" },
+ { 15, 1, "c0_ebase" },
+ { 16, 1, "c0_config1" },
+ { 16, 2, "c0_config2" },
+ { 16, 3, "c0_config3" },
+ { 18, 1, "c0_watchlo,1" },
+ { 18, 2, "c0_watchlo,2" },
+ { 18, 3, "c0_watchlo,3" },
+ { 18, 4, "c0_watchlo,4" },
+ { 18, 5, "c0_watchlo,5" },
+ { 18, 6, "c0_watchlo,6" },
+ { 18, 7, "c0_watchlo,7" },
+ { 19, 1, "c0_watchhi,1" },
+ { 19, 2, "c0_watchhi,2" },
+ { 19, 3, "c0_watchhi,3" },
+ { 19, 4, "c0_watchhi,4" },
+ { 19, 5, "c0_watchhi,5" },
+ { 19, 6, "c0_watchhi,6" },
+ { 19, 7, "c0_watchhi,7" },
+ { 23, 1, "c0_tracecontrol" },
+ { 23, 2, "c0_tracecontrol2" },
+ { 23, 3, "c0_usertracedata" },
+ { 23, 4, "c0_tracebpc" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 27, 1, "c0_cacheerr,1" },
+ { 27, 2, "c0_cacheerr,2" },
+ { 27, 3, "c0_cacheerr,3" },
+ { 28, 1, "c0_datalo" },
+ { 28, 2, "c0_taglo1" },
+ { 28, 3, "c0_datalo1" },
+ { 28, 4, "c0_taglo2" },
+ { 28, 5, "c0_datalo2" },
+ { 28, 6, "c0_taglo3" },
+ { 28, 7, "c0_datalo3" },
+ { 29, 1, "c0_datahi" },
+ { 29, 2, "c0_taghi1" },
+ { 29, 3, "c0_datahi1" },
+ { 29, 4, "c0_taghi2" },
+ { 29, 5, "c0_datahi2" },
+ { 29, 6, "c0_taghi3" },
+ { 29, 7, "c0_datahi3" },
+};
+
+/* SB-1: MIPS64 (mips_cp0_names_mips3264) with minor mods. */
+static const char * const mips_cp0_names_sb1[32] =
+{
+ "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
+ "c0_context", "c0_pagemask", "c0_wired", "$7",
+ "c0_badvaddr", "c0_count", "c0_entryhi", "c0_compare",
+ "c0_status", "c0_cause", "c0_epc", "c0_prid",
+ "c0_config", "c0_lladdr", "c0_watchlo", "c0_watchhi",
+ "c0_xcontext", "$21", "$22", "c0_debug",
+ "c0_depc", "c0_perfcnt", "c0_errctl", "c0_cacheerr_i",
+ "c0_taglo_i", "c0_taghi_i", "c0_errorepc", "c0_desave",
+};
+
+static const struct mips_cp0sel_name mips_cp0sel_names_sb1[] =
+{
+ { 16, 1, "c0_config1" },
+ { 18, 1, "c0_watchlo,1" },
+ { 19, 1, "c0_watchhi,1" },
+ { 22, 0, "c0_perftrace" },
+ { 23, 3, "c0_edebug" },
+ { 25, 1, "c0_perfcnt,1" },
+ { 25, 2, "c0_perfcnt,2" },
+ { 25, 3, "c0_perfcnt,3" },
+ { 25, 4, "c0_perfcnt,4" },
+ { 25, 5, "c0_perfcnt,5" },
+ { 25, 6, "c0_perfcnt,6" },
+ { 25, 7, "c0_perfcnt,7" },
+ { 26, 1, "c0_buserr_pa" },
+ { 27, 1, "c0_cacheerr_d" },
+ { 27, 3, "c0_cacheerr_d_pa" },
+ { 28, 1, "c0_datalo_i" },
+ { 28, 2, "c0_taglo_d" },
+ { 28, 3, "c0_datalo_d" },
+ { 29, 1, "c0_datahi_i" },
+ { 29, 2, "c0_taghi_d" },
+ { 29, 3, "c0_datahi_d" },
+};
+
+static const char * const mips_hwr_names_numeric[32] =
+{
+ "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+static const char * const mips_hwr_names_mips3264r2[32] =
+{
+ "hwr_cpunum", "hwr_synci_step", "hwr_cc", "hwr_ccres",
+ "$4", "$5", "$6", "$7",
+ "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
+ "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31"
+};
+
+struct mips_abi_choice
+{
+ const char *name;
+ const char * const *gpr_names;
+ const char * const *fpr_names;
+};
+
+static struct mips_abi_choice mips_abi_choices[] =
+{
+ { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric },
+ { "32", mips_gpr_names_oldabi, mips_fpr_names_32 },
+ { "n32", mips_gpr_names_newabi, mips_fpr_names_n32 },
+ { "64", mips_gpr_names_newabi, mips_fpr_names_64 },
+};
+
+struct mips_arch_choice
+{
+ const char *name;
+ int bfd_mach_valid;
+ unsigned long bfd_mach;
+ int processor;
+ int isa;
+ const char * const *cp0_names;
+ const struct mips_cp0sel_name *cp0sel_names;
+ unsigned int cp0sel_names_len;
+ const char * const *hwr_names;
+};
+
+#define bfd_mach_mips3000 3000
+#define bfd_mach_mips3900 3900
+#define bfd_mach_mips4000 4000
+#define bfd_mach_mips4010 4010
+#define bfd_mach_mips4100 4100
+#define bfd_mach_mips4111 4111
+#define bfd_mach_mips4120 4120
+#define bfd_mach_mips4300 4300
+#define bfd_mach_mips4400 4400
+#define bfd_mach_mips4600 4600
+#define bfd_mach_mips4650 4650
+#define bfd_mach_mips5000 5000
+#define bfd_mach_mips5400 5400
+#define bfd_mach_mips5500 5500
+#define bfd_mach_mips6000 6000
+#define bfd_mach_mips7000 7000
+#define bfd_mach_mips8000 8000
+#define bfd_mach_mips9000 9000
+#define bfd_mach_mips10000 10000
+#define bfd_mach_mips12000 12000
+#define bfd_mach_mips16 16
+#define bfd_mach_mips5 5
+#define bfd_mach_mips_sb1 12310201 /* octal 'SB', 01 */
+#define bfd_mach_mipsisa32 32
+#define bfd_mach_mipsisa32r2 33
+#define bfd_mach_mipsisa64 64
+#define bfd_mach_mipsisa64r2 65
+
+static const struct mips_arch_choice mips_arch_choices[] =
+{
+ { "numeric", 0, 0, 0, 0,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+
+ { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+ { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+
+ /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs.
+ Note that MIPS-3D and MDMX are not applicable to MIPS32. (See
+ _MIPS32 Architecture For Programmers Volume I: Introduction to the
+ MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95),
+ page 1. */
+ { "mips32", 1, bfd_mach_mipsisa32, CPU_MIPS32,
+ ISA_MIPS32 | INSN_MIPS16 | INSN_SMARTMIPS,
+ mips_cp0_names_mips3264,
+ mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
+ mips_hwr_names_numeric },
+
+ { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2,
+ (ISA_MIPS32R2 | INSN_MIPS16 | INSN_SMARTMIPS | INSN_DSP | INSN_DSPR2
+ | INSN_MIPS3D | INSN_MT),
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_hwr_names_mips3264r2 },
+
+ /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */
+ { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64,
+ ISA_MIPS64 | INSN_MIPS16 | INSN_MIPS3D | INSN_MDMX,
+ mips_cp0_names_mips3264,
+ mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
+ mips_hwr_names_numeric },
+
+ { "mips64r2", 1, bfd_mach_mipsisa64r2, CPU_MIPS64R2,
+ (ISA_MIPS64R2 | INSN_MIPS16 | INSN_MIPS3D | INSN_DSP | INSN_DSPR2
+ | INSN_DSP64 | INSN_MT | INSN_MDMX),
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_hwr_names_mips3264r2 },
+
+ { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1,
+ ISA_MIPS64 | INSN_MIPS3D | INSN_SB1,
+ mips_cp0_names_sb1,
+ mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1),
+ mips_hwr_names_numeric },
+
+ /* This entry, mips16, is here only for ISA/processor selection; do
+ not print its name. */
+ { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3 | INSN_MIPS16,
+ mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+};
+
+/* ISA and processor type to disassemble for, and register names to use.
+ set_default_mips_dis_options and parse_mips_dis_options fill in these
+ values. */
+static int mips_processor;
+static int mips_isa;
+static const char * const *mips_gpr_names;
+static const char * const *mips_fpr_names;
+static const char * const *mips_cp0_names;
+static const struct mips_cp0sel_name *mips_cp0sel_names;
+static int mips_cp0sel_names_len;
+static const char * const *mips_hwr_names;
+
+/* Other options */
+static int no_aliases; /* If set disassemble as most general inst. */
+
+static const struct mips_abi_choice *
+choose_abi_by_name (const char *name, unsigned int namelen)
+{
+ const struct mips_abi_choice *c;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_abi_choices) && c == NULL; i++)
+ if (strncmp (mips_abi_choices[i].name, name, namelen) == 0
+ && strlen (mips_abi_choices[i].name) == namelen)
+ c = &mips_abi_choices[i];
+
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_name (const char *name, unsigned int namelen)
+{
+ const struct mips_arch_choice *c = NULL;
+ unsigned int i;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ if (strncmp (mips_arch_choices[i].name, name, namelen) == 0
+ && strlen (mips_arch_choices[i].name) == namelen)
+ c = &mips_arch_choices[i];
+
+ return c;
+}
+
+static const struct mips_arch_choice *
+choose_arch_by_number (unsigned long mach)
+{
+ static unsigned long hint_bfd_mach;
+ static const struct mips_arch_choice *hint_arch_choice;
+ const struct mips_arch_choice *c;
+ unsigned int i;
+
+ /* We optimize this because even if the user specifies no
+ flags, this will be done for every instruction! */
+ if (hint_bfd_mach == mach
+ && hint_arch_choice != NULL
+ && hint_arch_choice->bfd_mach == hint_bfd_mach)
+ return hint_arch_choice;
+
+ for (i = 0, c = NULL; i < ARRAY_SIZE (mips_arch_choices) && c == NULL; i++)
+ {
+ if (mips_arch_choices[i].bfd_mach_valid
+ && mips_arch_choices[i].bfd_mach == mach)
+ {
+ c = &mips_arch_choices[i];
+ hint_bfd_mach = mach;
+ hint_arch_choice = c;
+ }
+ }
+ return c;
+}
+
+static void
+set_default_mips_dis_options (struct disassemble_info *info)
+{
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Defaults: mipsIII/r3000 (?!), (o)32-style ("oldabi") GPR names,
+ and numeric FPR, CP0 register, and HWR names. */
+ mips_isa = ISA_MIPS3;
+ mips_processor = CPU_R3000;
+ mips_gpr_names = mips_gpr_names_oldabi;
+ mips_fpr_names = mips_fpr_names_numeric;
+ mips_cp0_names = mips_cp0_names_numeric;
+ mips_cp0sel_names = NULL;
+ mips_cp0sel_names_len = 0;
+ mips_hwr_names = mips_hwr_names_numeric;
+ no_aliases = 0;
+
+ /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */
+#if 0
+ if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
+ {
+ Elf_Internal_Ehdr *header;
+
+ header = elf_elfheader (info->section->owner);
+ if (is_newabi (header))
+ mips_gpr_names = mips_gpr_names_newabi;
+ }
+#endif
+
+ /* Set ISA, architecture, and cp0 register names as best we can. */
+#if !defined(SYMTAB_AVAILABLE) && 0
+ /* This is running out on a target machine, not in a host tool.
+ FIXME: Where does mips_target_info come from? */
+ target_processor = mips_target_info.processor;
+ mips_isa = mips_target_info.isa;
+#else
+ chosen_arch = choose_arch_by_number (info->mach);
+ if (chosen_arch != NULL)
+ {
+ mips_processor = chosen_arch->processor;
+ mips_isa = chosen_arch->isa;
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ mips_hwr_names = chosen_arch->hwr_names;
+ }
+#endif
+}
+
+static void
+parse_mips_dis_option (const char *option, unsigned int len)
+{
+ unsigned int i, optionlen, vallen;
+ const char *val;
+ const struct mips_abi_choice *chosen_abi;
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Look for the = that delimits the end of the option name. */
+ for (i = 0; i < len; i++)
+ {
+ if (option[i] == '=')
+ break;
+ }
+ if (i == 0) /* Invalid option: no name before '='. */
+ return;
+ if (i == len) /* Invalid option: no '='. */
+ return;
+ if (i == (len - 1)) /* Invalid option: no value after '='. */
+ return;
+
+ optionlen = i;
+ val = option + (optionlen + 1);
+ vallen = len - (optionlen + 1);
+
+ if (strncmp("gpr-names", option, optionlen) == 0
+ && strlen("gpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_gpr_names = chosen_abi->gpr_names;
+ return;
+ }
+
+ if (strncmp("fpr-names", option, optionlen) == 0
+ && strlen("fpr-names") == optionlen)
+ {
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ mips_fpr_names = chosen_abi->fpr_names;
+ return;
+ }
+
+ if (strncmp("cp0-names", option, optionlen) == 0
+ && strlen("cp0-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ }
+ return;
+ }
+
+ if (strncmp("hwr-names", option, optionlen) == 0
+ && strlen("hwr-names") == optionlen)
+ {
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ mips_hwr_names = chosen_arch->hwr_names;
+ return;
+ }
+
+ if (strncmp("reg-names", option, optionlen) == 0
+ && strlen("reg-names") == optionlen)
+ {
+ /* We check both ABI and ARCH here unconditionally, so
+ that "numeric" will do the desirable thing: select
+ numeric register names for all registers. Other than
+ that, a given name probably won't match both. */
+ chosen_abi = choose_abi_by_name (val, vallen);
+ if (chosen_abi != NULL)
+ {
+ mips_gpr_names = chosen_abi->gpr_names;
+ mips_fpr_names = chosen_abi->fpr_names;
+ }
+ chosen_arch = choose_arch_by_name (val, vallen);
+ if (chosen_arch != NULL)
+ {
+ mips_cp0_names = chosen_arch->cp0_names;
+ mips_cp0sel_names = chosen_arch->cp0sel_names;
+ mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+ mips_hwr_names = chosen_arch->hwr_names;
+ }
+ return;
+ }
+
+ /* Invalid option. */
+}
+
+static void
+parse_mips_dis_options (const char *options)
+{
+ const char *option_end;
+
+ if (options == NULL)
+ return;
+
+ while (*options != '\0')
+ {
+ /* Skip empty options. */
+ if (*options == ',')
+ {
+ options++;
+ continue;
+ }
+
+ /* We know that *options is neither NUL or a comma. */
+ option_end = options + 1;
+ while (*option_end != ',' && *option_end != '\0')
+ option_end++;
+
+ parse_mips_dis_option (options, option_end - options);
+
+ /* Go on to the next one. If option_end points to a comma, it
+ will be skipped above. */
+ options = option_end;
+ }
+}
+
+static const struct mips_cp0sel_name *
+lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names,
+ unsigned int len,
+ unsigned int cp0reg,
+ unsigned int sel)
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ if (names[i].cp0reg == cp0reg && names[i].sel == sel)
+ return &names[i];
+ return NULL;
+}
+
+/* Print insn arguments for 32/64-bit code. */
+
+static void
+print_insn_args (const char *d,
+ register unsigned long int l,
+ bfd_vma pc,
+ struct disassemble_info *info,
+ const struct mips_opcode *opp)
+{
+ int op, delta;
+ unsigned int lsb, msb, msbd;
+
+ lsb = 0;
+
+ for (; *d != '\0'; d++)
+ {
+ switch (*d)
+ {
+ case ',':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ (*info->fprintf_func) (info->stream, "%c", *d);
+ break;
+
+ case '+':
+ /* Extension character; switch for second char. */
+ d++;
+ switch (*d)
+ {
+ case '\0':
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, incomplete extension sequence (+)"));
+ return;
+
+ case 'A':
+ lsb = (l >> OP_SH_SHAMT) & OP_MASK_SHAMT;
+ (*info->fprintf_func) (info->stream, "0x%x", lsb);
+ break;
+
+ case 'B':
+ msb = (l >> OP_SH_INSMSB) & OP_MASK_INSMSB;
+ (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1);
+ break;
+
+ case '1':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_UDI1) & OP_MASK_UDI1);
+ break;
+
+ case '2':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_UDI2) & OP_MASK_UDI2);
+ break;
+
+ case '3':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_UDI3) & OP_MASK_UDI3);
+ break;
+
+ case '4':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_UDI4) & OP_MASK_UDI4);
+ break;
+
+ case 'C':
+ case 'H':
+ msbd = (l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD;
+ (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
+ break;
+
+ case 'D':
+ {
+ const struct mips_cp0sel_name *n;
+ unsigned int cp0reg, sel;
+
+ cp0reg = (l >> OP_SH_RD) & OP_MASK_RD;
+ sel = (l >> OP_SH_SEL) & OP_MASK_SEL;
+
+ /* CP0 register including 'sel' code for mtcN (et al.), to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name(mips_cp0sel_names,
+ mips_cp0sel_names_len, cp0reg, sel);
+ if (n != NULL)
+ (*info->fprintf_func) (info->stream, "%s", n->name);
+ else
+ (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel);
+ break;
+ }
+
+ case 'E':
+ lsb = ((l >> OP_SH_SHAMT) & OP_MASK_SHAMT) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", lsb);
+ break;
+
+ case 'F':
+ msb = ((l >> OP_SH_INSMSB) & OP_MASK_INSMSB) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", msb - lsb + 1);
+ break;
+
+ case 'G':
+ msbd = ((l >> OP_SH_EXTMSBD) & OP_MASK_EXTMSBD) + 32;
+ (*info->fprintf_func) (info->stream, "0x%x", msbd + 1);
+ break;
+
+ case 't': /* Coprocessor 0 reg name */
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_cp0_names[(l >> OP_SH_RT) &
+ OP_MASK_RT]);
+ break;
+
+ case 'T': /* Coprocessor 0 reg name */
+ {
+ const struct mips_cp0sel_name *n;
+ unsigned int cp0reg, sel;
+
+ cp0reg = (l >> OP_SH_RT) & OP_MASK_RT;
+ sel = (l >> OP_SH_SEL) & OP_MASK_SEL;
+
+ /* CP0 register including 'sel' code for mftc0, to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name(mips_cp0sel_names,
+ mips_cp0sel_names_len, cp0reg, sel);
+ if (n != NULL)
+ (*info->fprintf_func) (info->stream, "%s", n->name);
+ else
+ (*info->fprintf_func) (info->stream, "$%d,%d", cp0reg, sel);
+ break;
+ }
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined extension sequence (+%c)"),
+ *d);
+ return;
+ }
+ break;
+
+ case '2':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_BP) & OP_MASK_BP);
+ break;
+
+ case '3':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_SA3) & OP_MASK_SA3);
+ break;
+
+ case '4':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_SA4) & OP_MASK_SA4);
+ break;
+
+ case '5':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_IMM8) & OP_MASK_IMM8);
+ break;
+
+ case '6':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_RS) & OP_MASK_RS);
+ break;
+
+ case '7':
+ (*info->fprintf_func) (info->stream, "$ac%ld",
+ (l >> OP_SH_DSPACC) & OP_MASK_DSPACC);
+ break;
+
+ case '8':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_WRDSP) & OP_MASK_WRDSP);
+ break;
+
+ case '9':
+ (*info->fprintf_func) (info->stream, "$ac%ld",
+ (l >> OP_SH_DSPACC_S) & OP_MASK_DSPACC_S);
+ break;
+
+ case '0': /* dsp 6-bit signed immediate in bit 20 */
+ delta = ((l >> OP_SH_DSPSFT) & OP_MASK_DSPSFT);
+ if (delta & 0x20) /* test sign bit */
+ delta |= ~OP_MASK_DSPSFT;
+ (*info->fprintf_func) (info->stream, "%d", delta);
+ break;
+
+ case ':': /* dsp 7-bit signed immediate in bit 19 */
+ delta = ((l >> OP_SH_DSPSFT_7) & OP_MASK_DSPSFT_7);
+ if (delta & 0x40) /* test sign bit */
+ delta |= ~OP_MASK_DSPSFT_7;
+ (*info->fprintf_func) (info->stream, "%d", delta);
+ break;
+
+ case '\'':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_RDDSP) & OP_MASK_RDDSP);
+ break;
+
+ case '@': /* dsp 10-bit signed immediate in bit 16 */
+ delta = ((l >> OP_SH_IMM10) & OP_MASK_IMM10);
+ if (delta & 0x200) /* test sign bit */
+ delta |= ~OP_MASK_IMM10;
+ (*info->fprintf_func) (info->stream, "%d", delta);
+ break;
+
+ case '!':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_MT_U) & OP_MASK_MT_U);
+ break;
+
+ case '$':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_MT_H) & OP_MASK_MT_H);
+ break;
+
+ case '*':
+ (*info->fprintf_func) (info->stream, "$ac%ld",
+ (l >> OP_SH_MTACC_T) & OP_MASK_MTACC_T);
+ break;
+
+ case '&':
+ (*info->fprintf_func) (info->stream, "$ac%ld",
+ (l >> OP_SH_MTACC_D) & OP_MASK_MTACC_D);
+ break;
+
+ case 'g':
+ /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */
+ (*info->fprintf_func) (info->stream, "$%ld",
+ (l >> OP_SH_RD) & OP_MASK_RD);
+ break;
+
+ case 's':
+ case 'b':
+ case 'r':
+ case 'v':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RS) & OP_MASK_RS]);
+ break;
+
+ case 't':
+ case 'w':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ break;
+
+ case 'i':
+ case 'u':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE);
+ break;
+
+ case 'j': /* Same as i, but sign-extended. */
+ case 'o':
+ delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+ if (delta & 0x8000)
+ delta |= ~0xffff;
+ (*info->fprintf_func) (info->stream, "%d",
+ delta);
+ break;
+
+ case 'h':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (unsigned int) ((l >> OP_SH_PREFX)
+ & OP_MASK_PREFX));
+ break;
+
+ case 'k':
+ (*info->fprintf_func) (info->stream, "0x%x",
+ (unsigned int) ((l >> OP_SH_CACHE)
+ & OP_MASK_CACHE));
+ break;
+
+ case 'a':
+ info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
+ | (((l >> OP_SH_TARGET) & OP_MASK_TARGET) << 2));
+ /* For gdb disassembler, force odd address on jalx. */
+ if (info->flavour == bfd_target_unknown_flavour
+ && strcmp (opp->name, "jalx") == 0)
+ info->target |= 1;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'p':
+ /* Sign extend the displacement. */
+ delta = (l >> OP_SH_DELTA) & OP_MASK_DELTA;
+ if (delta & 0x8000)
+ delta |= ~0xffff;
+ info->target = (delta << 2) + pc + INSNLEN;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'd':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'U':
+ {
+ /* First check for both rd and rt being equal. */
+ unsigned int reg = (l >> OP_SH_RD) & OP_MASK_RD;
+ if (reg == ((l >> OP_SH_RT) & OP_MASK_RT))
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[reg]);
+ else
+ {
+ /* If one is zero use the other. */
+ if (reg == 0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ else if (((l >> OP_SH_RT) & OP_MASK_RT) == 0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[reg]);
+ else /* Bogus, result depends on processor. */
+ (*info->fprintf_func) (info->stream, "%s or %s",
+ mips_gpr_names[reg],
+ mips_gpr_names[(l >> OP_SH_RT) & OP_MASK_RT]);
+ }
+ }
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case '<':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_SHAMT) & OP_MASK_SHAMT);
+ break;
+
+ case 'c':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_CODE) & OP_MASK_CODE);
+ break;
+
+ case 'q':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_CODE2) & OP_MASK_CODE2);
+ break;
+
+ case 'C':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_COPZ) & OP_MASK_COPZ);
+ break;
+
+ case 'B':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+
+ (l >> OP_SH_CODE20) & OP_MASK_CODE20);
+ break;
+
+ case 'J':
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_CODE19) & OP_MASK_CODE19);
+ break;
+
+ case 'S':
+ case 'V':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FS) & OP_MASK_FS]);
+ break;
+
+ case 'T':
+ case 'W':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FT) & OP_MASK_FT]);
+ break;
+
+ case 'D':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FD) & OP_MASK_FD]);
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_fpr_names[(l >> OP_SH_FR) & OP_MASK_FR]);
+ break;
+
+ case 'E':
+ /* Coprocessor register for lwcN instructions, et al.
+
+ Note that there is no load/store cp0 instructions, and
+ that FPU (cp1) instructions disassemble this field using
+ 'T' format. Therefore, until we gain understanding of
+ cp2 register names, we can simply print the register
+ numbers. */
+ (*info->fprintf_func) (info->stream, "$%ld",
+ (l >> OP_SH_RT) & OP_MASK_RT);
+ break;
+
+ case 'G':
+ /* Coprocessor register for mtcN instructions, et al. Note
+ that FPU (cp1) instructions disassemble this field using
+ 'S' format. Therefore, we only need to worry about cp0,
+ cp2, and cp3. */
+ op = (l >> OP_SH_OP) & OP_MASK_OP;
+ if (op == OP_OP_COP0)
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_cp0_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ else
+ (*info->fprintf_func) (info->stream, "$%ld",
+ (l >> OP_SH_RD) & OP_MASK_RD);
+ break;
+
+ case 'K':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_hwr_names[(l >> OP_SH_RD) & OP_MASK_RD]);
+ break;
+
+ case 'N':
+ (*info->fprintf_func) (info->stream,
+ ((opp->pinfo & (FP_D | FP_S)) != 0
+ ? "$fcc%ld" : "$cc%ld"),
+ (l >> OP_SH_BCC) & OP_MASK_BCC);
+ break;
+
+ case 'M':
+ (*info->fprintf_func) (info->stream, "$fcc%ld",
+ (l >> OP_SH_CCC) & OP_MASK_CCC);
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_PERFREG) & OP_MASK_PERFREG);
+ break;
+
+ case 'e':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_VECBYTE) & OP_MASK_VECBYTE);
+ break;
+
+ case '%':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_VECALIGN) & OP_MASK_VECALIGN);
+ break;
+
+ case 'H':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_SEL) & OP_MASK_SEL);
+ break;
+
+ case 'O':
+ (*info->fprintf_func) (info->stream, "%ld",
+ (l >> OP_SH_ALN) & OP_MASK_ALN);
+ break;
+
+ case 'Q':
+ {
+ unsigned int vsel = (l >> OP_SH_VSEL) & OP_MASK_VSEL;
+
+ if ((vsel & 0x10) == 0)
+ {
+ int fmt;
+
+ vsel &= 0x0f;
+ for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
+ if ((vsel & 1) == 0)
+ break;
+ (*info->fprintf_func) (info->stream, "$v%ld[%d]",
+ (l >> OP_SH_FT) & OP_MASK_FT,
+ vsel >> 1);
+ }
+ else if ((vsel & 0x08) == 0)
+ {
+ (*info->fprintf_func) (info->stream, "$v%ld",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ }
+ else
+ {
+ (*info->fprintf_func) (info->stream, "0x%lx",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ }
+ }
+ break;
+
+ case 'X':
+ (*info->fprintf_func) (info->stream, "$v%ld",
+ (l >> OP_SH_FD) & OP_MASK_FD);
+ break;
+
+ case 'Y':
+ (*info->fprintf_func) (info->stream, "$v%ld",
+ (l >> OP_SH_FS) & OP_MASK_FS);
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (info->stream, "$v%ld",
+ (l >> OP_SH_FT) & OP_MASK_FT);
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func) (info->stream,
+ _("# internal error, undefined modifier(%c)"),
+ *d);
+ return;
+ }
+ }
+}
+
+/* Check if the object uses NewABI conventions. */
+#if 0
+static int
+is_newabi (header)
+ Elf_Internal_Ehdr *header;
+{
+ /* There are no old-style ABIs which use 64-bit ELF. */
+ if (header->e_ident[EI_CLASS] == ELFCLASS64)
+ return 1;
+
+ /* If a 32-bit ELF file, n32 is a new-style ABI. */
+ if ((header->e_flags & EF_MIPS_ABI2) != 0)
+ return 1;
+
+ return 0;
+}
+#endif
+
+/* Print the mips instruction at address MEMADDR in debugged memory,
+ on using INFO. Returns length of the instruction, in bytes, which is
+ always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if
+ this is little-endian code. */
+
+static int
+print_insn_mips (bfd_vma memaddr,
+ unsigned long int word,
+ struct disassemble_info *info)
+{
+ const struct mips_opcode *op;
+ static bfd_boolean init = 0;
+ static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
+
+ /* Build a hash table to shorten the search time. */
+ if (! init)
+ {
+ unsigned int i;
+
+ for (i = 0; i <= OP_MASK_OP; i++)
+ {
+ for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo == INSN_MACRO
+ || (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
+ continue;
+ if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP))
+ {
+ mips_hash[i] = op;
+ break;
+ }
+ }
+ }
+
+ init = 1;
+ }
+
+ info->bytes_per_chunk = INSNLEN;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ op = mips_hash[(word >> OP_SH_OP) & OP_MASK_OP];
+ if (op != NULL)
+ {
+ for (; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo != INSN_MACRO
+ && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
+ && (word & op->mask) == op->match)
+ {
+ const char *d;
+
+ /* We always allow to disassemble the jalx instruction. */
+ if (! OPCODE_IS_MEMBER (op, mips_isa, mips_processor)
+ && strcmp (op->name, "jalx"))
+ continue;
+
+ /* Figure out instruction type and branch delay information. */
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ if ((info->insn_type & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_jsr;
+ else
+ info->insn_type = dis_branch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)) != 0)
+ {
+ if ((info->insn_type & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_condjsr;
+ else
+ info->insn_type = dis_condbranch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_STORE_MEMORY
+ | INSN_LOAD_MEMORY_DELAY)) != 0)
+ info->insn_type = dis_dref;
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+
+ d = op->args;
+ if (d != NULL && *d != '\0')
+ {
+ (*info->fprintf_func) (info->stream, "\t");
+ print_insn_args (d, word, memaddr, info, op);
+ }
+
+ return INSNLEN;
+ }
+ }
+ }
+
+ /* Handle undefined instructions. */
+ info->insn_type = dis_noninsn;
+ (*info->fprintf_func) (info->stream, "0x%lx", word);
+ return INSNLEN;
+}
+
+/* In an environment where we do not know the symbol type of the
+ instruction we are forced to assume that the low order bit of the
+ instructions' address may mark it as a mips16 instruction. If we
+ are single stepping, or the pc is within the disassembled function,
+ this works. Otherwise, we need a clue. Sometimes. */
+
+static int
+_print_insn_mips (bfd_vma memaddr,
+ struct disassemble_info *info,
+ enum bfd_endian endianness)
+{
+ bfd_byte buffer[INSNLEN];
+ int status;
+
+ set_default_mips_dis_options (info);
+ parse_mips_dis_options (info->disassembler_options);
+
+#if 0
+#if 1
+ /* FIXME: If odd address, this is CLEARLY a mips 16 instruction. */
+ /* Only a few tools will work this way. */
+ if (memaddr & 0x01)
+ return print_insn_mips16 (memaddr, info);
+#endif
+
+#if SYMTAB_AVAILABLE
+ if (info->mach == bfd_mach_mips16
+ || (info->flavour == bfd_target_elf_flavour
+ && info->symbols != NULL
+ && ((*(elf_symbol_type **) info->symbols)->internal_elf_sym.st_other
+ == STO_MIPS16)))
+ return print_insn_mips16 (memaddr, info);
+#endif
+#endif
+
+ status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info);
+ if (status == 0)
+ {
+ unsigned long insn;
+
+ if (endianness == BFD_ENDIAN_BIG)
+ insn = (unsigned long) bfd_getb32 (buffer);
+ else
+ insn = (unsigned long) bfd_getl32 (buffer);
+
+ return print_insn_mips (memaddr, insn, info);
+ }
+ else
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+}
+
+int
+print_insn_big_mips (bfd_vma memaddr, struct disassemble_info *info)
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_BIG);
+}
+
+int
+print_insn_little_mips (bfd_vma memaddr, struct disassemble_info *info)
+{
+ return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
+}
+
+/* Disassemble mips16 instructions. */
+#if 0
+static int
+print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
+{
+ int status;
+ bfd_byte buffer[2];
+ int length;
+ int insn;
+ bfd_boolean use_extend;
+ int extend = 0;
+ const struct mips_opcode *op, *opend;
+
+ info->bytes_per_chunk = 2;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ length = 2;
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ insn = bfd_getb16 (buffer);
+ else
+ insn = bfd_getl16 (buffer);
+
+ /* Handle the extend opcode specially. */
+ use_extend = FALSE;
+ if ((insn & 0xf800) == 0xf000)
+ {
+ use_extend = TRUE;
+ extend = insn & 0x7ff;
+
+ memaddr += 2;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+ if (status != 0)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_BIG)
+ insn = bfd_getb16 (buffer);
+ else
+ insn = bfd_getl16 (buffer);
+
+ /* Check for an extend opcode followed by an extend opcode. */
+ if ((insn & 0xf800) == 0xf000)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ info->insn_type = dis_noninsn;
+ return length;
+ }
+
+ length += 2;
+ }
+
+ /* FIXME: Should probably use a hash table on the major opcode here. */
+
+ opend = mips16_opcodes + bfd_mips16_num_opcodes;
+ for (op = mips16_opcodes; op < opend; op++)
+ {
+ if (op->pinfo != INSN_MACRO
+ && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
+ && (insn & op->mask) == op->match)
+ {
+ const char *s;
+
+ if (strchr (op->args, 'a') != NULL)
+ {
+ if (use_extend)
+ {
+ (*info->fprintf_func) (info->stream, "extend 0x%x",
+ (unsigned int) extend);
+ info->insn_type = dis_noninsn;
+ return length - 2;
+ }
+
+ use_extend = FALSE;
+
+ memaddr += 2;
+
+ status = (*info->read_memory_func) (memaddr, buffer, 2,
+ info);
+ if (status == 0)
+ {
+ use_extend = TRUE;
+ if (info->endian == BFD_ENDIAN_BIG)
+ extend = bfd_getb16 (buffer);
+ else
+ extend = bfd_getl16 (buffer);
+ length += 2;
+ }
+ }
+
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+ if (op->args[0] != '\0')
+ (*info->fprintf_func) (info->stream, "\t");
+
+ for (s = op->args; *s != '\0'; s++)
+ {
+ if (*s == ','
+ && s[1] == 'w'
+ && (((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)
+ == ((insn >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY)))
+ {
+ /* Skip the register and the comma. */
+ ++s;
+ continue;
+ }
+ if (*s == ','
+ && s[1] == 'v'
+ && (((insn >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ)
+ == ((insn >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX)))
+ {
+ /* Skip the register and the comma. */
+ ++s;
+ continue;
+ }
+ print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr,
+ info);
+ }
+
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ info->branch_delay_insns = 1;
+ if (info->insn_type != dis_jsr)
+ info->insn_type = dis_branch;
+ }
+
+ return length;
+ }
+ }
+
+ if (use_extend)
+ (*info->fprintf_func) (info->stream, "0x%x", extend | 0xf000);
+ (*info->fprintf_func) (info->stream, "0x%x", insn);
+ info->insn_type = dis_noninsn;
+
+ return length;
+}
+
+/* Disassemble an operand for a mips16 instruction. */
+
+static void
+print_mips16_insn_arg (char type,
+ const struct mips_opcode *op,
+ int l,
+ bfd_boolean use_extend,
+ int extend,
+ bfd_vma memaddr,
+ struct disassemble_info *info)
+{
+ switch (type)
+ {
+ case ',':
+ case '(':
+ case ')':
+ (*info->fprintf_func) (info->stream, "%c", type);
+ break;
+
+ case 'y':
+ case 'w':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names(((l >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY)));
+ break;
+
+ case 'x':
+ case 'v':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names(((l >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX)));
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names(((l >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ)));
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips16_reg_names(((l >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z)));
+ break;
+
+ case '0':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[0]);
+ break;
+
+ case 'S':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[29]);
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (info->stream, "$pc");
+ break;
+
+ case 'R':
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[31]);
+ break;
+
+ case 'X':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[((l >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32)]);
+ break;
+
+ case 'Y':
+ (*info->fprintf_func) (info->stream, "%s",
+ mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]);
+ break;
+
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '6':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ {
+ int immed, nbits, shift, signedp, extbits, pcrel, extu, branch;
+
+ shift = 0;
+ signedp = 0;
+ extbits = 16;
+ pcrel = 0;
+ extu = 0;
+ branch = 0;
+ switch (type)
+ {
+ case '<':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+ extbits = 5;
+ extu = 1;
+ break;
+ case '>':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ extbits = 5;
+ extu = 1;
+ break;
+ case '[':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+ extbits = 6;
+ extu = 1;
+ break;
+ case ']':
+ nbits = 3;
+ immed = (l >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ extbits = 6;
+ extu = 1;
+ break;
+ case '4':
+ nbits = 4;
+ immed = (l >> MIPS16OP_SH_IMM4) & MIPS16OP_MASK_IMM4;
+ signedp = 1;
+ extbits = 15;
+ break;
+ case '5':
+ nbits = 5;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 1;
+ break;
+ case 'H':
+ nbits = 5;
+ shift = 1;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 2;
+ break;
+ case 'W':
+ nbits = 5;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ if ((op->pinfo & MIPS16_INSN_READ_PC) == 0
+ && (op->pinfo & MIPS16_INSN_READ_SP) == 0)
+ {
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ }
+ break;
+ case 'D':
+ nbits = 5;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'j':
+ nbits = 5;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ signedp = 1;
+ break;
+ case '6':
+ nbits = 6;
+ immed = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
+ break;
+ case '8':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ break;
+ case 'V':
+ nbits = 8;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ /* FIXME: This might be lw, or it might be addiu to $sp or
+ $pc. We assume it's load. */
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ break;
+ case 'C':
+ nbits = 8;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'U':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ extu = 1;
+ break;
+ case 'k':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ break;
+ case 'K':
+ nbits = 8;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ break;
+ case 'p':
+ nbits = 8;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ signedp = 1;
+ pcrel = 1;
+ branch = 1;
+ info->insn_type = dis_condbranch;
+ break;
+ case 'q':
+ nbits = 11;
+ immed = (l >> MIPS16OP_SH_IMM11) & MIPS16OP_MASK_IMM11;
+ signedp = 1;
+ pcrel = 1;
+ branch = 1;
+ info->insn_type = dis_branch;
+ break;
+ case 'A':
+ nbits = 8;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM8) & MIPS16OP_MASK_IMM8;
+ pcrel = 1;
+ /* FIXME: This can be lw or la. We assume it is lw. */
+ info->insn_type = dis_dref;
+ info->data_size = 4;
+ break;
+ case 'B':
+ nbits = 5;
+ shift = 3;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ pcrel = 1;
+ info->insn_type = dis_dref;
+ info->data_size = 8;
+ break;
+ case 'E':
+ nbits = 5;
+ shift = 2;
+ immed = (l >> MIPS16OP_SH_IMM5) & MIPS16OP_MASK_IMM5;
+ pcrel = 1;
+ break;
+ default:
+ abort ();
+ }
+
+ if (! use_extend)
+ {
+ if (signedp && immed >= (1 << (nbits - 1)))
+ immed -= 1 << nbits;
+ immed <<= shift;
+ if ((type == '<' || type == '>' || type == '[' || type == ']')
+ && immed == 0)
+ immed = 8;
+ }
+ else
+ {
+ if (extbits == 16)
+ immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0);
+ else if (extbits == 15)
+ immed |= ((extend & 0xf) << 11) | (extend & 0x7f0);
+ else
+ immed = ((extend >> 6) & 0x1f) | (extend & 0x20);
+ immed &= (1 << extbits) - 1;
+ if (! extu && immed >= (1 << (extbits - 1)))
+ immed -= 1 << extbits;
+ }
+
+ if (! pcrel)
+ (*info->fprintf_func) (info->stream, "%d", immed);
+ else
+ {
+ bfd_vma baseaddr;
+
+ if (branch)
+ {
+ immed *= 2;
+ baseaddr = memaddr + 2;
+ }
+ else if (use_extend)
+ baseaddr = memaddr - 2;
+ else
+ {
+ int status;
+ bfd_byte buffer[2];
+
+ baseaddr = memaddr;
+
+ /* If this instruction is in the delay slot of a jr
+ instruction, the base address is the address of the
+ jr instruction. If it is in the delay slot of jalr
+ instruction, the base address is the address of the
+ jalr instruction. This test is unreliable: we have
+ no way of knowing whether the previous word is
+ instruction or data. */
+ status = (*info->read_memory_func) (memaddr - 4, buffer, 2,
+ info);
+ if (status == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf800) == 0x1800))
+ baseaddr = memaddr - 4;
+ else
+ {
+ status = (*info->read_memory_func) (memaddr - 2, buffer,
+ 2, info);
+ if (status == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf81f) == 0xe800))
+ baseaddr = memaddr - 2;
+ }
+ }
+ info->target = (baseaddr & ~((1 << shift) - 1)) + immed;
+ if (pcrel && branch
+ && info->flavour == bfd_target_unknown_flavour)
+ /* For gdb disassembler, maintain odd address. */
+ info->target |= 1;
+ (*info->print_address_func) (info->target, info);
+ }
+ }
+ break;
+
+ case 'a':
+ {
+ int jalx = l & 0x400;
+
+ if (! use_extend)
+ extend = 0;
+ l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2);
+ if (!jalx && info->flavour == bfd_target_unknown_flavour)
+ /* For gdb disassembler, maintain odd address. */
+ l |= 1;
+ }
+ info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l;
+ (*info->print_address_func) (info->target, info);
+ info->insn_type = dis_jsr;
+ info->branch_delay_insns = 1;
+ break;
+
+ case 'l':
+ case 'L':
+ {
+ int need_comma, amask, smask;
+
+ need_comma = 0;
+
+ l = (l >> MIPS16OP_SH_IMM6) & MIPS16OP_MASK_IMM6;
+
+ amask = (l >> 3) & 7;
+
+ if (amask > 0 && amask < 5)
+ {
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]);
+ if (amask > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[amask + 3]);
+ need_comma = 1;
+ }
+
+ smask = (l >> 1) & 3;
+ if (smask == 3)
+ {
+ (*info->fprintf_func) (info->stream, "%s??",
+ need_comma ? "," : "");
+ need_comma = 1;
+ }
+ else if (smask > 0)
+ {
+ (*info->fprintf_func) (info->stream, "%s%s",
+ need_comma ? "," : "",
+ mips_gpr_names[16]);
+ if (smask > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[smask + 15]);
+ need_comma = 1;
+ }
+
+ if (l & 1)
+ {
+ (*info->fprintf_func) (info->stream, "%s%s",
+ need_comma ? "," : "",
+ mips_gpr_names[31]);
+ need_comma = 1;
+ }
+
+ if (amask == 5 || amask == 6)
+ {
+ (*info->fprintf_func) (info->stream, "%s$f0",
+ need_comma ? "," : "");
+ if (amask == 6)
+ (*info->fprintf_func) (info->stream, "-$f1");
+ }
+ }
+ break;
+
+ case 'm':
+ case 'M':
+ /* MIPS16e save/restore. */
+ {
+ int need_comma = 0;
+ int amask, args, statics;
+ int nsreg, smask;
+ int framesz;
+ int i, j;
+
+ l = l & 0x7f;
+ if (use_extend)
+ l |= extend << 16;
+
+ amask = (l >> 16) & 0xf;
+ if (amask == MIPS16_ALL_ARGS)
+ {
+ args = 4;
+ statics = 0;
+ }
+ else if (amask == MIPS16_ALL_STATICS)
+ {
+ args = 0;
+ statics = 4;
+ }
+ else
+ {
+ args = amask >> 2;
+ statics = amask & 3;
+ }
+
+ if (args > 0) {
+ (*info->fprintf_func) (info->stream, "%s", mips_gpr_names[4]);
+ if (args > 1)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[4 + args - 1]);
+ need_comma = 1;
+ }
+
+ framesz = (((l >> 16) & 0xf0) | (l & 0x0f)) * 8;
+ if (framesz == 0 && !use_extend)
+ framesz = 128;
+
+ (*info->fprintf_func) (info->stream, "%s%d",
+ need_comma ? "," : "",
+ framesz);
+
+ if (l & 0x40) /* $ra */
+ (*info->fprintf_func) (info->stream, ",%s", mips_gpr_names[31]);
+
+ nsreg = (l >> 24) & 0x7;
+ smask = 0;
+ if (l & 0x20) /* $s0 */
+ smask |= 1 << 0;
+ if (l & 0x10) /* $s1 */
+ smask |= 1 << 1;
+ if (nsreg > 0) /* $s2-$s8 */
+ smask |= ((1 << nsreg) - 1) << 2;
+
+ /* Find first set static reg bit. */
+ for (i = 0; i < 9; i++)
+ {
+ if (smask & (1 << i))
+ {
+ (*info->fprintf_func) (info->stream, ",%s",
+ mips_gpr_names[i == 8 ? 30 : (16 + i)]);
+ /* Skip over string of set bits. */
+ for (j = i; smask & (2 << j); j++)
+ continue;
+ if (j > i)
+ (*info->fprintf_func) (info->stream, "-%s",
+ mips_gpr_names[j == 8 ? 30 : (16 + j)]);
+ i = j + 1;
+ }
+ }
+
+ /* Statics $ax - $a3. */
+ if (statics == 1)
+ (*info->fprintf_func) (info->stream, ",%s", mips_gpr_names[7]);
+ else if (statics > 0)
+ (*info->fprintf_func) (info->stream, ",%s-%s",
+ mips_gpr_names[7 - statics + 1],
+ mips_gpr_names[7]);
+ }
+ break;
+
+ default:
+ /* xgettext:c-format */
+ (*info->fprintf_func)
+ (info->stream,
+ _("# internal disassembler error, unrecognised modifier (%c)"),
+ type);
+ abort ();
+ }
+}
+
+void
+print_mips_disassembler_options (FILE *stream)
+{
+ unsigned int i;
+
+ fprintf (stream, _("\n\
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n"));
+
+ fprintf (stream, _("\n\
+ gpr-names=ABI Print GPR names according to specified ABI.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ fpr-names=ABI Print FPR names according to specified ABI.\n\
+ Default: numeric.\n"));
+
+ fprintf (stream, _("\n\
+ cp0-names=ARCH Print CP0 register names according to\n\
+ specified architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ hwr-names=ARCH Print HWR names according to specified \n\
+ architecture.\n\
+ Default: based on binary being disassembled.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ABI Print GPR and FPR names according to\n\
+ specified ABI.\n"));
+
+ fprintf (stream, _("\n\
+ reg-names=ARCH Print CP0 register and HWR names according to\n\
+ specified architecture.\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, the following values are supported for \"ABI\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+ fprintf (stream, " %s", mips_abi_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n\
+ For the options above, The following values are supported for \"ARCH\":\n\
+ "));
+ for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+ if (*mips_arch_choices[i].name != '\0')
+ fprintf (stream, " %s", mips_arch_choices[i].name);
+ fprintf (stream, _("\n"));
+
+ fprintf (stream, _("\n"));
+}
+#endif
diff --git a/mips.ld b/mips.ld
new file mode 100644
index 0000000..4294761
--- /dev/null
+++ b/mips.ld
@@ -0,0 +1,224 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips",
+ "elf32-tradlittlemips")
+OUTPUT_ARCH(mips)
+ENTRY(__start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = 0x0400000); . = 0x0400000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .reginfo : { *(.reginfo) }
+ .dynamic : { *(.dynamic) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data.rel.ro : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) }
+ .rela.data.rel.ro : { *(.rela.data.rel.ro* .rela.gnu.linkonce.d.rel.ro.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+ .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+ .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+ .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+ .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+ .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+ .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+ .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x47ff041f
+ .plt : { *(.plt) }
+ .text :
+ {
+ _ftext = . ;
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.mips16.fn.*) *(.mips16.call.*)
+ } =0x47ff041f
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x47ff041f
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 :
+ {
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN (0x40000) - ((0x40000 - .) & (0x40000 - 1)); . = DATA_SEGMENT_ALIGN (0x40000, 0x1000);
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .preinit_array :
+ {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ }
+ .init_array :
+ {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+ . = DATA_SEGMENT_RELRO_END (0, .);
+ .data :
+ {
+ _fdata = . ;
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ . = .;
+ _gp = ALIGN(16) + 0x7ff0;
+ .got : { *(.got.plt) *(.got) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ .lit8 : { *(.lit8) }
+ .lit4 : { *(.lit4) }
+ _edata = .; PROVIDE (edata = .);
+ __bss_start = .;
+ _fbss = .;
+ .sbss :
+ {
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections.
+ FIXME: Why do we need it? When there is no .bss section, we don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ . = ALIGN(32 / 8);
+ . = ALIGN(32 / 8);
+ _end = .; PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+ .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/module.c b/module.c
new file mode 100644
index 0000000..e77d569
--- /dev/null
+++ b/module.c
@@ -0,0 +1,80 @@
+/*
+ * QEMU Module Infrastructure
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "module.h"
+
+typedef struct ModuleEntry
+{
+ module_init_type type;
+ void (*init)(void);
+ QTAILQ_ENTRY(ModuleEntry) node;
+} ModuleEntry;
+
+typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList;
+
+static ModuleTypeList init_type_list[MODULE_INIT_MAX];
+
+static void init_types(void)
+{
+ static int inited;
+ int i;
+
+ if (inited) {
+ return;
+ }
+
+ for (i = 0; i < MODULE_INIT_MAX; i++) {
+ QTAILQ_INIT(&init_type_list[i]);
+ }
+
+ inited = 1;
+}
+
+
+static ModuleTypeList *find_type(module_init_type type)
+{
+ ModuleTypeList *l;
+
+ init_types();
+
+ l = &init_type_list[type];
+
+ return l;
+}
+
+void register_module_init(void (*fn)(void), module_init_type type)
+{
+ ModuleEntry *e;
+ ModuleTypeList *l;
+
+ e = qemu_mallocz(sizeof(*e));
+ e->init = fn;
+
+ l = find_type(type);
+
+ QTAILQ_INSERT_TAIL(l, e, node);
+}
+
+void module_call_init(module_init_type type)
+{
+ ModuleTypeList *l;
+ ModuleEntry *e;
+
+ l = find_type(type);
+
+ QTAILQ_FOREACH(e, l, node) {
+ e->init();
+ }
+}
diff --git a/module.h b/module.h
new file mode 100644
index 0000000..9263f1c
--- /dev/null
+++ b/module.h
@@ -0,0 +1,38 @@
+/*
+ * QEMU Module Infrastructure
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_MODULE_H
+#define QEMU_MODULE_H
+
+/* This should not be used directly. Use block_init etc. instead. */
+#define module_init(function, type) \
+static void __attribute__((constructor)) do_qemu_init_ ## function(void) { \
+ register_module_init(function, type); \
+}
+
+typedef enum {
+ MODULE_INIT_BLOCK,
+ MODULE_INIT_DEVICE,
+ MODULE_INIT_MACHINE,
+ MODULE_INIT_MAX
+} module_init_type;
+
+#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
+#define device_init(function) module_init(function, MODULE_INIT_DEVICE)
+#define machine_init(function) module_init(function, MODULE_INIT_MACHINE)
+
+void register_module_init(void (*fn)(void), module_init_type type);
+
+void module_call_init(module_init_type type);
+
+#endif
diff --git a/monitor.c b/monitor.c
new file mode 100644
index 0000000..096d42b
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,5261 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <dirent.h>
+#include "hw/hw.h"
+#include "hw/qdev.h"
+#include "hw/usb.h"
+#include "hw/pcmcia.h"
+#include "hw/pc.h"
+#include "hw/pci.h"
+#include "hw/watchdog.h"
+#include "hw/loader.h"
+#include "gdbstub.h"
+#include "net.h"
+#include "net/slirp.h"
+#include "qemu-char.h"
+#include "ui/qemu-spice.h"
+#include "sysemu.h"
+#include "monitor.h"
+#include "readline.h"
+#include "console.h"
+#include "blockdev.h"
+#include "audio/audio.h"
+#include "disas.h"
+#include "balloon.h"
+#include "qemu-timer.h"
+#include "migration.h"
+#include "kvm.h"
+#include "acl.h"
+#include "qint.h"
+#include "qfloat.h"
+#include "qlist.h"
+#include "qbool.h"
+#include "qstring.h"
+#include "qjson.h"
+#include "json-streamer.h"
+#include "json-parser.h"
+#include "osdep.h"
+#include "exec-all.h"
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
+#include "ui/qemu-spice.h"
+
+//#define DEBUG
+//#define DEBUG_COMPLETION
+
+/*
+ * Supported types:
+ *
+ * 'F' filename
+ * 'B' block device name
+ * 's' string (accept optional quote)
+ * 'O' option string of the form NAME=VALUE,...
+ * parsed according to QemuOptsList given by its name
+ * Example: 'device:O' uses qemu_device_opts.
+ * Restriction: only lists with empty desc are supported
+ * TODO lift the restriction
+ * 'i' 32 bit integer
+ * 'l' target long (32 or 64 bit)
+ * 'M' just like 'l', except in user mode the value is
+ * multiplied by 2^20 (think Mebibyte)
+ * 'o' octets (aka bytes)
+ * user mode accepts an optional T, t, G, g, M, m, K, k
+ * suffix, which multiplies the value by 2^40 for
+ * suffixes T and t, 2^30 for suffixes G and g, 2^20 for
+ * M and m, 2^10 for K and k
+ * 'T' double
+ * user mode accepts an optional ms, us, ns suffix,
+ * which divides the value by 1e3, 1e6, 1e9, respectively
+ * '/' optional gdb-like print format (like "/10x")
+ *
+ * '?' optional type (for all types, except '/')
+ * '.' other form of optional type (for 'i' and 'l')
+ * 'b' boolean
+ * user mode accepts "on" or "off"
+ * '-' optional parameter (eg. '-f')
+ *
+ */
+
+typedef struct MonitorCompletionData MonitorCompletionData;
+struct MonitorCompletionData {
+ Monitor *mon;
+ void (*user_print)(Monitor *mon, const QObject *data);
+};
+
+typedef struct mon_cmd_t {
+ const char *name;
+ const char *args_type;
+ const char *params;
+ const char *help;
+ void (*user_print)(Monitor *mon, const QObject *data);
+ union {
+ void (*info)(Monitor *mon);
+ void (*info_new)(Monitor *mon, QObject **ret_data);
+ int (*info_async)(Monitor *mon, MonitorCompletion *cb, void *opaque);
+ void (*cmd)(Monitor *mon, const QDict *qdict);
+ int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
+ int (*cmd_async)(Monitor *mon, const QDict *params,
+ MonitorCompletion *cb, void *opaque);
+ } mhandler;
+ int flags;
+} mon_cmd_t;
+
+/* file descriptors passed via SCM_RIGHTS */
+typedef struct mon_fd_t mon_fd_t;
+struct mon_fd_t {
+ char *name;
+ int fd;
+ QLIST_ENTRY(mon_fd_t) next;
+};
+
+typedef struct MonitorControl {
+ QObject *id;
+ JSONMessageParser parser;
+ int command_mode;
+} MonitorControl;
+
+struct Monitor {
+ CharDriverState *chr;
+ int mux_out;
+ int reset_seen;
+ int flags;
+ int suspend_cnt;
+ uint8_t outbuf[1024];
+ int outbuf_index;
+ ReadLineState *rs;
+ MonitorControl *mc;
+ CPUState *mon_cpu;
+ BlockDriverCompletionFunc *password_completion_cb;
+ void *password_opaque;
+#ifdef CONFIG_DEBUG_MONITOR
+ int print_calls_nr;
+#endif
+ QError *error;
+ QLIST_HEAD(,mon_fd_t) fds;
+ QLIST_ENTRY(Monitor) entry;
+};
+
+#ifdef CONFIG_DEBUG_MONITOR
+#define MON_DEBUG(fmt, ...) do { \
+ fprintf(stderr, "Monitor: "); \
+ fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+
+static inline void mon_print_count_inc(Monitor *mon)
+{
+ mon->print_calls_nr++;
+}
+
+static inline void mon_print_count_init(Monitor *mon)
+{
+ mon->print_calls_nr = 0;
+}
+
+static inline int mon_print_count_get(const Monitor *mon)
+{
+ return mon->print_calls_nr;
+}
+
+#else /* !CONFIG_DEBUG_MONITOR */
+#define MON_DEBUG(fmt, ...) do { } while (0)
+static inline void mon_print_count_inc(Monitor *mon) { }
+static inline void mon_print_count_init(Monitor *mon) { }
+static inline int mon_print_count_get(const Monitor *mon) { return 0; }
+#endif /* CONFIG_DEBUG_MONITOR */
+
+/* QMP checker flags */
+#define QMP_ACCEPT_UNKNOWNS 1
+
+static QLIST_HEAD(mon_list, Monitor) mon_list;
+
+static const mon_cmd_t mon_cmds[];
+static const mon_cmd_t info_cmds[];
+
+static const mon_cmd_t qmp_cmds[];
+static const mon_cmd_t qmp_query_cmds[];
+
+Monitor *cur_mon;
+Monitor *default_mon;
+
+static void monitor_command_cb(Monitor *mon, const char *cmdline,
+ void *opaque);
+
+static inline int qmp_cmd_mode(const Monitor *mon)
+{
+ return (mon->mc ? mon->mc->command_mode : 0);
+}
+
+/* Return true if in control mode, false otherwise */
+static inline int monitor_ctrl_mode(const Monitor *mon)
+{
+ return (mon->flags & MONITOR_USE_CONTROL);
+}
+
+/* Return non-zero iff we have a current monitor, and it is in QMP mode. */
+int monitor_cur_is_qmp(void)
+{
+ return cur_mon && monitor_ctrl_mode(cur_mon);
+}
+
+static void monitor_read_command(Monitor *mon, int show_prompt)
+{
+ if (!mon->rs)
+ return;
+
+ readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL);
+ if (show_prompt)
+ readline_show_prompt(mon->rs);
+}
+
+static int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
+ void *opaque)
+{
+ if (monitor_ctrl_mode(mon)) {
+ qerror_report(QERR_MISSING_PARAMETER, "password");
+ return -EINVAL;
+ } else if (mon->rs) {
+ readline_start(mon->rs, "Password: ", 1, readline_func, opaque);
+ /* prompt is printed on return from the command handler */
+ return 0;
+ } else {
+ monitor_printf(mon, "terminal does not support password prompting\n");
+ return -ENOTTY;
+ }
+}
+
+void monitor_flush(Monitor *mon)
+{
+ if (mon && mon->outbuf_index != 0 && !mon->mux_out) {
+ qemu_chr_write(mon->chr, mon->outbuf, mon->outbuf_index);
+ mon->outbuf_index = 0;
+ }
+}
+
+/* flush at every end of line or if the buffer is full */
+static void monitor_puts(Monitor *mon, const char *str)
+{
+ char c;
+
+ for(;;) {
+ c = *str++;
+ if (c == '\0')
+ break;
+ if (c == '\n')
+ mon->outbuf[mon->outbuf_index++] = '\r';
+ mon->outbuf[mon->outbuf_index++] = c;
+ if (mon->outbuf_index >= (sizeof(mon->outbuf) - 1)
+ || c == '\n')
+ monitor_flush(mon);
+ }
+}
+
+void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+{
+ char buf[4096];
+
+ if (!mon)
+ return;
+
+ mon_print_count_inc(mon);
+
+ if (monitor_ctrl_mode(mon)) {
+ return;
+ }
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ monitor_puts(mon, buf);
+}
+
+void monitor_printf(Monitor *mon, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ monitor_vprintf(mon, fmt, ap);
+ va_end(ap);
+}
+
+void monitor_print_filename(Monitor *mon, const char *filename)
+{
+ int i;
+
+ for (i = 0; filename[i]; i++) {
+ switch (filename[i]) {
+ case ' ':
+ case '"':
+ case '\\':
+ monitor_printf(mon, "\\%c", filename[i]);
+ break;
+ case '\t':
+ monitor_printf(mon, "\\t");
+ break;
+ case '\r':
+ monitor_printf(mon, "\\r");
+ break;
+ case '\n':
+ monitor_printf(mon, "\\n");
+ break;
+ default:
+ monitor_printf(mon, "%c", filename[i]);
+ break;
+ }
+ }
+}
+
+static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ monitor_vprintf((Monitor *)stream, fmt, ap);
+ va_end(ap);
+ return 0;
+}
+
+static void monitor_user_noop(Monitor *mon, const QObject *data) { }
+
+static inline int handler_is_qobject(const mon_cmd_t *cmd)
+{
+ return cmd->user_print != NULL;
+}
+
+static inline bool handler_is_async(const mon_cmd_t *cmd)
+{
+ return cmd->flags & MONITOR_CMD_ASYNC;
+}
+
+static inline int monitor_has_error(const Monitor *mon)
+{
+ return mon->error != NULL;
+}
+
+static void monitor_json_emitter(Monitor *mon, const QObject *data)
+{
+ QString *json;
+
+ json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) :
+ qobject_to_json(data);
+ assert(json != NULL);
+
+ qstring_append_chr(json, '\n');
+ monitor_puts(mon, qstring_get_str(json));
+
+ QDECREF(json);
+}
+
+static void monitor_protocol_emitter(Monitor *mon, QObject *data)
+{
+ QDict *qmp;
+
+ qmp = qdict_new();
+
+ if (!monitor_has_error(mon)) {
+ /* success response */
+ if (data) {
+ qobject_incref(data);
+ qdict_put_obj(qmp, "return", data);
+ } else {
+ /* return an empty QDict by default */
+ qdict_put(qmp, "return", qdict_new());
+ }
+ } else {
+ /* error response */
+ qdict_put(mon->error->error, "desc", qerror_human(mon->error));
+ qdict_put(qmp, "error", mon->error->error);
+ QINCREF(mon->error->error);
+ QDECREF(mon->error);
+ mon->error = NULL;
+ }
+
+ if (mon->mc->id) {
+ qdict_put_obj(qmp, "id", mon->mc->id);
+ mon->mc->id = NULL;
+ }
+
+ monitor_json_emitter(mon, QOBJECT(qmp));
+ QDECREF(qmp);
+}
+
+static void timestamp_put(QDict *qdict)
+{
+ int err;
+ QObject *obj;
+ qemu_timeval tv;
+
+ err = qemu_gettimeofday(&tv);
+ if (err < 0)
+ return;
+
+ obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", "
+ "'microseconds': %" PRId64 " }",
+ (int64_t) tv.tv_sec, (int64_t) tv.tv_usec);
+ qdict_put_obj(qdict, "timestamp", obj);
+}
+
+/**
+ * monitor_protocol_event(): Generate a Monitor event
+ *
+ * Event-specific data can be emitted through the (optional) 'data' parameter.
+ */
+void monitor_protocol_event(MonitorEvent event, QObject *data)
+{
+ QDict *qmp;
+ const char *event_name;
+ Monitor *mon;
+
+ assert(event < QEVENT_MAX);
+
+ switch (event) {
+ case QEVENT_SHUTDOWN:
+ event_name = "SHUTDOWN";
+ break;
+ case QEVENT_RESET:
+ event_name = "RESET";
+ break;
+ case QEVENT_POWERDOWN:
+ event_name = "POWERDOWN";
+ break;
+ case QEVENT_STOP:
+ event_name = "STOP";
+ break;
+ case QEVENT_RESUME:
+ event_name = "RESUME";
+ break;
+ case QEVENT_VNC_CONNECTED:
+ event_name = "VNC_CONNECTED";
+ break;
+ case QEVENT_VNC_INITIALIZED:
+ event_name = "VNC_INITIALIZED";
+ break;
+ case QEVENT_VNC_DISCONNECTED:
+ event_name = "VNC_DISCONNECTED";
+ break;
+ case QEVENT_BLOCK_IO_ERROR:
+ event_name = "BLOCK_IO_ERROR";
+ break;
+ case QEVENT_RTC_CHANGE:
+ event_name = "RTC_CHANGE";
+ break;
+ case QEVENT_WATCHDOG:
+ event_name = "WATCHDOG";
+ break;
+ case QEVENT_SPICE_CONNECTED:
+ event_name = "SPICE_CONNECTED";
+ break;
+ case QEVENT_SPICE_INITIALIZED:
+ event_name = "SPICE_INITIALIZED";
+ break;
+ case QEVENT_SPICE_DISCONNECTED:
+ event_name = "SPICE_DISCONNECTED";
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ qmp = qdict_new();
+ timestamp_put(qmp);
+ qdict_put(qmp, "event", qstring_from_str(event_name));
+ if (data) {
+ qobject_incref(data);
+ qdict_put_obj(qmp, "data", data);
+ }
+
+ QLIST_FOREACH(mon, &mon_list, entry) {
+ if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) {
+ monitor_json_emitter(mon, QOBJECT(qmp));
+ }
+ }
+ QDECREF(qmp);
+}
+
+static int do_qmp_capabilities(Monitor *mon, const QDict *params,
+ QObject **ret_data)
+{
+ /* Will setup QMP capabilities in the future */
+ if (monitor_ctrl_mode(mon)) {
+ mon->mc->command_mode = 1;
+ }
+
+ return 0;
+}
+
+static int mon_set_cpu(int cpu_index);
+static void handle_user_command(Monitor *mon, const char *cmdline);
+
+static int do_hmp_passthrough(Monitor *mon, const QDict *params,
+ QObject **ret_data)
+{
+ int ret = 0;
+ Monitor *old_mon, hmp;
+ CharDriverState mchar;
+
+ memset(&hmp, 0, sizeof(hmp));
+ qemu_chr_init_mem(&mchar);
+ hmp.chr = &mchar;
+
+ old_mon = cur_mon;
+ cur_mon = &hmp;
+
+ if (qdict_haskey(params, "cpu-index")) {
+ ret = mon_set_cpu(qdict_get_int(params, "cpu-index"));
+ if (ret < 0) {
+ cur_mon = old_mon;
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number");
+ goto out;
+ }
+ }
+
+ handle_user_command(&hmp, qdict_get_str(params, "command-line"));
+ cur_mon = old_mon;
+
+ if (qemu_chr_mem_osize(hmp.chr) > 0) {
+ *ret_data = QOBJECT(qemu_chr_mem_to_qs(hmp.chr));
+ }
+
+out:
+ qemu_chr_close_mem(hmp.chr);
+ return ret;
+}
+
+static int compare_cmd(const char *name, const char *list)
+{
+ const char *p, *pstart;
+ int len;
+ len = strlen(name);
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = strchr(p, '|');
+ if (!p)
+ p = pstart + strlen(pstart);
+ if ((p - pstart) == len && !memcmp(pstart, name, len))
+ return 1;
+ if (*p == '\0')
+ break;
+ p++;
+ }
+ return 0;
+}
+
+static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
+ const char *prefix, const char *name)
+{
+ const mon_cmd_t *cmd;
+
+ for(cmd = cmds; cmd->name != NULL; cmd++) {
+ if (!name || !strcmp(name, cmd->name))
+ monitor_printf(mon, "%s%s %s -- %s\n", prefix, cmd->name,
+ cmd->params, cmd->help);
+ }
+}
+
+static void help_cmd(Monitor *mon, const char *name)
+{
+ if (name && !strcmp(name, "info")) {
+ help_cmd_dump(mon, info_cmds, "info ", NULL);
+ } else {
+ help_cmd_dump(mon, mon_cmds, "", name);
+ if (name && !strcmp(name, "log")) {
+ const CPULogItem *item;
+ monitor_printf(mon, "Log items (comma separated):\n");
+ monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
+ for(item = cpu_log_items; item->mask != 0; item++) {
+ monitor_printf(mon, "%-10s %s\n", item->name, item->help);
+ }
+ }
+ }
+}
+
+static void do_help_cmd(Monitor *mon, const QDict *qdict)
+{
+ help_cmd(mon, qdict_get_try_str(qdict, "name"));
+}
+
+#ifdef CONFIG_SIMPLE_TRACE
+static void do_change_trace_event_state(Monitor *mon, const QDict *qdict)
+{
+ const char *tp_name = qdict_get_str(qdict, "name");
+ bool new_state = qdict_get_bool(qdict, "option");
+ int ret = st_change_trace_event_state(tp_name, new_state);
+
+ if (!ret) {
+ monitor_printf(mon, "unknown event name \"%s\"\n", tp_name);
+ }
+}
+
+static void do_trace_file(Monitor *mon, const QDict *qdict)
+{
+ const char *op = qdict_get_try_str(qdict, "op");
+ const char *arg = qdict_get_try_str(qdict, "arg");
+
+ if (!op) {
+ st_print_trace_file_status((FILE *)mon, &monitor_fprintf);
+ } else if (!strcmp(op, "on")) {
+ st_set_trace_file_enabled(true);
+ } else if (!strcmp(op, "off")) {
+ st_set_trace_file_enabled(false);
+ } else if (!strcmp(op, "flush")) {
+ st_flush_trace_buffer();
+ } else if (!strcmp(op, "set")) {
+ if (arg) {
+ st_set_trace_file(arg);
+ }
+ } else {
+ monitor_printf(mon, "unexpected argument \"%s\"\n", op);
+ help_cmd(mon, "trace-file");
+ }
+}
+#endif
+
+static void user_monitor_complete(void *opaque, QObject *ret_data)
+{
+ MonitorCompletionData *data = (MonitorCompletionData *)opaque;
+
+ if (ret_data) {
+ data->user_print(data->mon, ret_data);
+ }
+ monitor_resume(data->mon);
+ qemu_free(data);
+}
+
+static void qmp_monitor_complete(void *opaque, QObject *ret_data)
+{
+ monitor_protocol_emitter(opaque, ret_data);
+}
+
+static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
+ const QDict *params)
+{
+ return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
+}
+
+static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
+{
+ cmd->mhandler.info_async(mon, qmp_monitor_complete, mon);
+}
+
+static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
+ const QDict *params)
+{
+ int ret;
+
+ MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data));
+ cb_data->mon = mon;
+ cb_data->user_print = cmd->user_print;
+ monitor_suspend(mon);
+ ret = cmd->mhandler.cmd_async(mon, params,
+ user_monitor_complete, cb_data);
+ if (ret < 0) {
+ monitor_resume(mon);
+ qemu_free(cb_data);
+ }
+}
+
+static void user_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
+{
+ int ret;
+
+ MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data));
+ cb_data->mon = mon;
+ cb_data->user_print = cmd->user_print;
+ monitor_suspend(mon);
+ ret = cmd->mhandler.info_async(mon, user_monitor_complete, cb_data);
+ if (ret < 0) {
+ monitor_resume(mon);
+ qemu_free(cb_data);
+ }
+}
+
+static void do_info(Monitor *mon, const QDict *qdict)
+{
+ const mon_cmd_t *cmd;
+ const char *item = qdict_get_try_str(qdict, "item");
+
+ if (!item) {
+ goto help;
+ }
+
+ for (cmd = info_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(item, cmd->name))
+ break;
+ }
+
+ if (cmd->name == NULL) {
+ goto help;
+ }
+
+ if (handler_is_async(cmd)) {
+ user_async_info_handler(mon, cmd);
+ } else if (handler_is_qobject(cmd)) {
+ QObject *info_data = NULL;
+
+ cmd->mhandler.info_new(mon, &info_data);
+ if (info_data) {
+ cmd->user_print(mon, info_data);
+ qobject_decref(info_data);
+ }
+ } else {
+ cmd->mhandler.info(mon);
+ }
+
+ return;
+
+help:
+ help_cmd(mon, "info");
+}
+
+static void do_info_version_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+ QDict *qemu;
+
+ qdict = qobject_to_qdict(data);
+ qemu = qdict_get_qdict(qdict, "qemu");
+
+ monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
+ qdict_get_int(qemu, "major"),
+ qdict_get_int(qemu, "minor"),
+ qdict_get_int(qemu, "micro"),
+ qdict_get_str(qdict, "package"));
+}
+
+static void do_info_version(Monitor *mon, QObject **ret_data)
+{
+ const char *version = QEMU_VERSION;
+ int major = 0, minor = 0, micro = 0;
+ char *tmp;
+
+ major = strtol(version, &tmp, 10);
+ tmp++;
+ minor = strtol(tmp, &tmp, 10);
+ tmp++;
+ micro = strtol(tmp, &tmp, 10);
+
+ *ret_data = qobject_from_jsonf("{ 'qemu': { 'major': %d, 'minor': %d, \
+ 'micro': %d }, 'package': %s }", major, minor, micro, QEMU_PKGVERSION);
+}
+
+static void do_info_name_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+
+ qdict = qobject_to_qdict(data);
+ if (qdict_size(qdict) == 0) {
+ return;
+ }
+
+ monitor_printf(mon, "%s\n", qdict_get_str(qdict, "name"));
+}
+
+static void do_info_name(Monitor *mon, QObject **ret_data)
+{
+ *ret_data = qemu_name ? qobject_from_jsonf("{'name': %s }", qemu_name) :
+ qobject_from_jsonf("{}");
+}
+
+static QObject *get_cmd_dict(const char *name)
+{
+ const char *p;
+
+ /* Remove '|' from some commands */
+ p = strchr(name, '|');
+ if (p) {
+ p++;
+ } else {
+ p = name;
+ }
+
+ return qobject_from_jsonf("{ 'name': %s }", p);
+}
+
+static void do_info_commands(Monitor *mon, QObject **ret_data)
+{
+ QList *cmd_list;
+ const mon_cmd_t *cmd;
+
+ cmd_list = qlist_new();
+
+ for (cmd = qmp_cmds; cmd->name != NULL; cmd++) {
+ qlist_append_obj(cmd_list, get_cmd_dict(cmd->name));
+ }
+
+ for (cmd = qmp_query_cmds; cmd->name != NULL; cmd++) {
+ char buf[128];
+ snprintf(buf, sizeof(buf), "query-%s", cmd->name);
+ qlist_append_obj(cmd_list, get_cmd_dict(buf));
+ }
+
+ *ret_data = QOBJECT(cmd_list);
+}
+
+static void do_info_uuid_print(Monitor *mon, const QObject *data)
+{
+ monitor_printf(mon, "%s\n", qdict_get_str(qobject_to_qdict(data), "UUID"));
+}
+
+static void do_info_uuid(Monitor *mon, QObject **ret_data)
+{
+ char uuid[64];
+
+ snprintf(uuid, sizeof(uuid), UUID_FMT, qemu_uuid[0], qemu_uuid[1],
+ qemu_uuid[2], qemu_uuid[3], qemu_uuid[4], qemu_uuid[5],
+ qemu_uuid[6], qemu_uuid[7], qemu_uuid[8], qemu_uuid[9],
+ qemu_uuid[10], qemu_uuid[11], qemu_uuid[12], qemu_uuid[13],
+ qemu_uuid[14], qemu_uuid[15]);
+ *ret_data = qobject_from_jsonf("{ 'UUID': %s }", uuid);
+}
+
+/* get the current CPU defined by the user */
+static int mon_set_cpu(int cpu_index)
+{
+ CPUState *env;
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->cpu_index == cpu_index) {
+ cur_mon->mon_cpu = env;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static CPUState *mon_get_cpu(void)
+{
+ if (!cur_mon->mon_cpu) {
+ mon_set_cpu(0);
+ }
+ cpu_synchronize_state(cur_mon->mon_cpu);
+ return cur_mon->mon_cpu;
+}
+
+static void do_info_registers(Monitor *mon)
+{
+ CPUState *env;
+ env = mon_get_cpu();
+#ifdef TARGET_I386
+ cpu_dump_state(env, (FILE *)mon, monitor_fprintf,
+ X86_DUMP_FPU);
+#else
+ cpu_dump_state(env, (FILE *)mon, monitor_fprintf,
+ 0);
+#endif
+}
+
+static void print_cpu_iter(QObject *obj, void *opaque)
+{
+ QDict *cpu;
+ int active = ' ';
+ Monitor *mon = opaque;
+
+ assert(qobject_type(obj) == QTYPE_QDICT);
+ cpu = qobject_to_qdict(obj);
+
+ if (qdict_get_bool(cpu, "current")) {
+ active = '*';
+ }
+
+ monitor_printf(mon, "%c CPU #%d: ", active, (int)qdict_get_int(cpu, "CPU"));
+
+#if defined(TARGET_I386)
+ monitor_printf(mon, "pc=0x" TARGET_FMT_lx,
+ (target_ulong) qdict_get_int(cpu, "pc"));
+#elif defined(TARGET_PPC)
+ monitor_printf(mon, "nip=0x" TARGET_FMT_lx,
+ (target_long) qdict_get_int(cpu, "nip"));
+#elif defined(TARGET_SPARC)
+ monitor_printf(mon, "pc=0x " TARGET_FMT_lx,
+ (target_long) qdict_get_int(cpu, "pc"));
+ monitor_printf(mon, "npc=0x" TARGET_FMT_lx,
+ (target_long) qdict_get_int(cpu, "npc"));
+#elif defined(TARGET_MIPS)
+ monitor_printf(mon, "PC=0x" TARGET_FMT_lx,
+ (target_long) qdict_get_int(cpu, "PC"));
+#endif
+
+ if (qdict_get_bool(cpu, "halted")) {
+ monitor_printf(mon, " (halted)");
+ }
+
+ monitor_printf(mon, " thread_id=%" PRId64 " ",
+ qdict_get_int(cpu, "thread_id"));
+
+ monitor_printf(mon, "\n");
+}
+
+static void monitor_print_cpus(Monitor *mon, const QObject *data)
+{
+ QList *cpu_list;
+
+ assert(qobject_type(data) == QTYPE_QLIST);
+ cpu_list = qobject_to_qlist(data);
+ qlist_iter(cpu_list, print_cpu_iter, mon);
+}
+
+static void do_info_cpus(Monitor *mon, QObject **ret_data)
+{
+ CPUState *env;
+ QList *cpu_list;
+
+ cpu_list = qlist_new();
+
+ /* just to set the default cpu if not already done */
+ mon_get_cpu();
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ QDict *cpu;
+ QObject *obj;
+
+ cpu_synchronize_state(env);
+
+ obj = qobject_from_jsonf("{ 'CPU': %d, 'current': %i, 'halted': %i }",
+ env->cpu_index, env == mon->mon_cpu,
+ env->halted);
+
+ cpu = qobject_to_qdict(obj);
+
+#if defined(TARGET_I386)
+ qdict_put(cpu, "pc", qint_from_int(env->eip + env->segs[R_CS].base));
+#elif defined(TARGET_PPC)
+ qdict_put(cpu, "nip", qint_from_int(env->nip));
+#elif defined(TARGET_SPARC)
+ qdict_put(cpu, "pc", qint_from_int(env->pc));
+ qdict_put(cpu, "npc", qint_from_int(env->npc));
+#elif defined(TARGET_MIPS)
+ qdict_put(cpu, "PC", qint_from_int(env->active_tc.PC));
+#endif
+ qdict_put(cpu, "thread_id", qint_from_int(env->thread_id));
+
+ qlist_append(cpu_list, cpu);
+ }
+
+ *ret_data = QOBJECT(cpu_list);
+}
+
+static int do_cpu_set(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ int index = qdict_get_int(qdict, "index");
+ if (mon_set_cpu(index) < 0) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "index",
+ "a CPU number");
+ return -1;
+ }
+ return 0;
+}
+
+static void do_cpu_set_nr(Monitor *mon, const QDict *qdict)
+{
+ int state, value;
+ const char *status;
+
+ status = qdict_get_str(qdict, "state");
+ value = qdict_get_int(qdict, "cpu");
+
+ if (!strcmp(status, "online"))
+ state = 1;
+ else if (!strcmp(status, "offline"))
+ state = 0;
+ else {
+ monitor_printf(mon, "invalid status: %s\n", status);
+ return;
+ }
+#if defined(TARGET_I386) || defined(TARGET_X86_64)
+ qemu_system_cpu_hot_add(value, state);
+#endif
+}
+
+static void do_info_jit(Monitor *mon)
+{
+ dump_exec_info((FILE *)mon, monitor_fprintf);
+}
+
+static void do_info_history(Monitor *mon)
+{
+ int i;
+ const char *str;
+
+ if (!mon->rs)
+ return;
+ i = 0;
+ for(;;) {
+ str = readline_get_history(mon->rs, i);
+ if (!str)
+ break;
+ monitor_printf(mon, "%d: '%s'\n", i, str);
+ i++;
+ }
+}
+
+#if defined(TARGET_PPC)
+/* XXX: not implemented in other targets */
+static void do_info_cpu_stats(Monitor *mon)
+{
+ CPUState *env;
+
+ env = mon_get_cpu();
+ cpu_dump_statistics(env, (FILE *)mon, &monitor_fprintf, 0);
+}
+#endif
+
+#if defined(CONFIG_SIMPLE_TRACE)
+static void do_info_trace(Monitor *mon)
+{
+ st_print_trace((FILE *)mon, &monitor_fprintf);
+}
+
+static void do_info_trace_events(Monitor *mon)
+{
+ st_print_trace_events((FILE *)mon, &monitor_fprintf);
+}
+#endif
+
+/**
+ * do_quit(): Quit QEMU execution
+ */
+static int do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ monitor_suspend(mon);
+ no_shutdown = 0;
+ qemu_system_shutdown_request();
+
+ return 0;
+}
+
+static int change_vnc_password(const char *password)
+{
+ if (!password || !password[0]) {
+ if (vnc_display_disable_login(NULL)) {
+ qerror_report(QERR_SET_PASSWD_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (vnc_display_password(NULL, password) < 0) {
+ qerror_report(QERR_SET_PASSWD_FAILED);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void change_vnc_password_cb(Monitor *mon, const char *password,
+ void *opaque)
+{
+ change_vnc_password(password);
+ monitor_read_command(mon, 1);
+}
+
+static int do_change_vnc(Monitor *mon, const char *target, const char *arg)
+{
+ if (strcmp(target, "passwd") == 0 ||
+ strcmp(target, "password") == 0) {
+ if (arg) {
+ char password[9];
+ strncpy(password, arg, sizeof(password));
+ password[sizeof(password) - 1] = '\0';
+ return change_vnc_password(password);
+ } else {
+ return monitor_read_password(mon, change_vnc_password_cb, NULL);
+ }
+ } else {
+ if (vnc_display_open(NULL, target) < 0) {
+ qerror_report(QERR_VNC_SERVER_FAILED, target);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * do_change(): Change a removable medium, or VNC configuration
+ */
+static int do_change(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ const char *target = qdict_get_str(qdict, "target");
+ const char *arg = qdict_get_try_str(qdict, "arg");
+ int ret;
+
+ if (strcmp(device, "vnc") == 0) {
+ ret = do_change_vnc(mon, target, arg);
+ } else {
+ ret = do_change_block(mon, device, target, arg);
+ }
+
+ return ret;
+}
+
+static int set_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *password = qdict_get_str(qdict, "password");
+ const char *connected = qdict_get_try_str(qdict, "connected");
+ int disconnect_if_connected = 0;
+ int fail_if_connected = 0;
+ int rc;
+
+ if (connected) {
+ if (strcmp(connected, "fail") == 0) {
+ fail_if_connected = 1;
+ } else if (strcmp(connected, "disconnect") == 0) {
+ disconnect_if_connected = 1;
+ } else if (strcmp(connected, "keep") == 0) {
+ /* nothing */
+ } else {
+ qerror_report(QERR_INVALID_PARAMETER, "connected");
+ return -1;
+ }
+ }
+
+ if (strcmp(protocol, "spice") == 0) {
+ if (!using_spice) {
+ /* correct one? spice isn't a device ,,, */
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+ return -1;
+ }
+ rc = qemu_spice_set_passwd(password, fail_if_connected,
+ disconnect_if_connected);
+ if (rc != 0) {
+ qerror_report(QERR_SET_PASSWD_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (strcmp(protocol, "vnc") == 0) {
+ if (fail_if_connected || disconnect_if_connected) {
+ /* vnc supports "connected=keep" only */
+ qerror_report(QERR_INVALID_PARAMETER, "connected");
+ return -1;
+ }
+ /* Note that setting an empty password will not disable login through
+ * this interface. */
+ rc = vnc_display_password(NULL, password);
+ if (rc != 0) {
+ qerror_report(QERR_SET_PASSWD_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ qerror_report(QERR_INVALID_PARAMETER, "protocol");
+ return -1;
+}
+
+static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *whenstr = qdict_get_str(qdict, "time");
+ time_t when;
+ int rc;
+
+ if (strcmp(whenstr, "now") == 0) {
+ when = 0;
+ } else if (strcmp(whenstr, "never") == 0) {
+ when = TIME_MAX;
+ } else if (whenstr[0] == '+') {
+ when = time(NULL) + strtoull(whenstr+1, NULL, 10);
+ } else {
+ when = strtoull(whenstr, NULL, 10);
+ }
+
+ if (strcmp(protocol, "spice") == 0) {
+ if (!using_spice) {
+ /* correct one? spice isn't a device ,,, */
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+ return -1;
+ }
+ rc = qemu_spice_set_pw_expire(when);
+ if (rc != 0) {
+ qerror_report(QERR_SET_PASSWD_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ if (strcmp(protocol, "vnc") == 0) {
+ rc = vnc_display_pw_expire(NULL, when);
+ if (rc != 0) {
+ qerror_report(QERR_SET_PASSWD_FAILED);
+ return -1;
+ }
+ return 0;
+ }
+
+ qerror_report(QERR_INVALID_PARAMETER, "protocol");
+ return -1;
+}
+
+static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *protocol = qdict_get_str(qdict, "protocol");
+ const char *hostname = qdict_get_str(qdict, "hostname");
+ const char *subject = qdict_get_try_str(qdict, "cert-subject");
+ int port = qdict_get_try_int(qdict, "port", -1);
+ int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
+ int ret;
+
+ if (strcmp(protocol, "spice") == 0) {
+ if (!using_spice) {
+ qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
+ return -1;
+ }
+
+ ret = qemu_spice_migrate_info(hostname, port, tls_port, subject);
+ if (ret != 0) {
+ qerror_report(QERR_UNDEFINED_ERROR);
+ return -1;
+ }
+ return 0;
+ }
+
+ qerror_report(QERR_INVALID_PARAMETER, "protocol");
+ return -1;
+}
+
+static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ vga_hw_screen_dump(qdict_get_str(qdict, "filename"));
+ return 0;
+}
+
+static void do_logfile(Monitor *mon, const QDict *qdict)
+{
+ cpu_set_log_filename(qdict_get_str(qdict, "filename"));
+}
+
+static void do_log(Monitor *mon, const QDict *qdict)
+{
+ int mask;
+ const char *items = qdict_get_str(qdict, "items");
+
+ if (!strcmp(items, "none")) {
+ mask = 0;
+ } else {
+ mask = cpu_str_to_log_mask(items);
+ if (!mask) {
+ help_cmd(mon, "log");
+ return;
+ }
+ }
+ cpu_set_log(mask);
+}
+
+static void do_singlestep(Monitor *mon, const QDict *qdict)
+{
+ const char *option = qdict_get_try_str(qdict, "option");
+ if (!option || !strcmp(option, "on")) {
+ singlestep = 1;
+ } else if (!strcmp(option, "off")) {
+ singlestep = 0;
+ } else {
+ monitor_printf(mon, "unexpected option %s\n", option);
+ }
+}
+
+/**
+ * do_stop(): Stop VM execution
+ */
+static int do_stop(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ vm_stop(EXCP_INTERRUPT);
+ return 0;
+}
+
+static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs);
+
+struct bdrv_iterate_context {
+ Monitor *mon;
+ int err;
+};
+
+/**
+ * do_cont(): Resume emulation.
+ */
+static int do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ struct bdrv_iterate_context context = { mon, 0 };
+
+ if (incoming_expected) {
+ qerror_report(QERR_MIGRATION_EXPECTED);
+ return -1;
+ }
+ bdrv_iterate(encrypted_bdrv_it, &context);
+ /* only resume the vm if all keys are set and valid */
+ if (!context.err) {
+ vm_start();
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void bdrv_key_cb(void *opaque, int err)
+{
+ Monitor *mon = opaque;
+
+ /* another key was set successfully, retry to continue */
+ if (!err)
+ do_cont(mon, NULL, NULL);
+}
+
+static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs)
+{
+ struct bdrv_iterate_context *context = opaque;
+
+ if (!context->err && bdrv_key_required(bs)) {
+ context->err = -EBUSY;
+ monitor_read_bdrv_key_start(context->mon, bs, bdrv_key_cb,
+ context->mon);
+ }
+}
+
+static void do_gdbserver(Monitor *mon, const QDict *qdict)
+{
+ const char *device = qdict_get_try_str(qdict, "device");
+ if (!device)
+ device = "tcp::" DEFAULT_GDBSTUB_PORT;
+ if (gdbserver_start(device) < 0) {
+ monitor_printf(mon, "Could not open gdbserver on device '%s'\n",
+ device);
+ } else if (strcmp(device, "none") == 0) {
+ monitor_printf(mon, "Disabled gdbserver\n");
+ } else {
+ monitor_printf(mon, "Waiting for gdb connection on device '%s'\n",
+ device);
+ }
+}
+
+static void do_watchdog_action(Monitor *mon, const QDict *qdict)
+{
+ const char *action = qdict_get_str(qdict, "action");
+ if (select_watchdog_action(action) == -1) {
+ monitor_printf(mon, "Unknown watchdog action '%s'\n", action);
+ }
+}
+
+static void monitor_printc(Monitor *mon, int c)
+{
+ monitor_printf(mon, "'");
+ switch(c) {
+ case '\'':
+ monitor_printf(mon, "\\'");
+ break;
+ case '\\':
+ monitor_printf(mon, "\\\\");
+ break;
+ case '\n':
+ monitor_printf(mon, "\\n");
+ break;
+ case '\r':
+ monitor_printf(mon, "\\r");
+ break;
+ default:
+ if (c >= 32 && c <= 126) {
+ monitor_printf(mon, "%c", c);
+ } else {
+ monitor_printf(mon, "\\x%02x", c);
+ }
+ break;
+ }
+ monitor_printf(mon, "'");
+}
+
+static void memory_dump(Monitor *mon, int count, int format, int wsize,
+ target_phys_addr_t addr, int is_physical)
+{
+ CPUState *env;
+ int l, line_size, i, max_digits, len;
+ uint8_t buf[16];
+ uint64_t v;
+
+ if (format == 'i') {
+ int flags;
+ flags = 0;
+ env = mon_get_cpu();
+#ifdef TARGET_I386
+ if (wsize == 2) {
+ flags = 1;
+ } else if (wsize == 4) {
+ flags = 0;
+ } else {
+ /* as default we use the current CS size */
+ flags = 0;
+ if (env) {
+#ifdef TARGET_X86_64
+ if ((env->efer & MSR_EFER_LMA) &&
+ (env->segs[R_CS].flags & DESC_L_MASK))
+ flags = 2;
+ else
+#endif
+ if (!(env->segs[R_CS].flags & DESC_B_MASK))
+ flags = 1;
+ }
+ }
+#endif
+ monitor_disas(mon, env, addr, count, is_physical, flags);
+ return;
+ }
+
+ len = wsize * count;
+ if (wsize == 1)
+ line_size = 8;
+ else
+ line_size = 16;
+ max_digits = 0;
+
+ switch(format) {
+ case 'o':
+ max_digits = (wsize * 8 + 2) / 3;
+ break;
+ default:
+ case 'x':
+ max_digits = (wsize * 8) / 4;
+ break;
+ case 'u':
+ case 'd':
+ max_digits = (wsize * 8 * 10 + 32) / 33;
+ break;
+ case 'c':
+ wsize = 1;
+ break;
+ }
+
+ while (len > 0) {
+ if (is_physical)
+ monitor_printf(mon, TARGET_FMT_plx ":", addr);
+ else
+ monitor_printf(mon, TARGET_FMT_lx ":", (target_ulong)addr);
+ l = len;
+ if (l > line_size)
+ l = line_size;
+ if (is_physical) {
+ cpu_physical_memory_rw(addr, buf, l, 0);
+ } else {
+ env = mon_get_cpu();
+ if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) {
+ monitor_printf(mon, " Cannot access memory\n");
+ break;
+ }
+ }
+ i = 0;
+ while (i < l) {
+ switch(wsize) {
+ default:
+ case 1:
+ v = ldub_raw(buf + i);
+ break;
+ case 2:
+ v = lduw_raw(buf + i);
+ break;
+ case 4:
+ v = (uint32_t)ldl_raw(buf + i);
+ break;
+ case 8:
+ v = ldq_raw(buf + i);
+ break;
+ }
+ monitor_printf(mon, " ");
+ switch(format) {
+ case 'o':
+ monitor_printf(mon, "%#*" PRIo64, max_digits, v);
+ break;
+ case 'x':
+ monitor_printf(mon, "0x%0*" PRIx64, max_digits, v);
+ break;
+ case 'u':
+ monitor_printf(mon, "%*" PRIu64, max_digits, v);
+ break;
+ case 'd':
+ monitor_printf(mon, "%*" PRId64, max_digits, v);
+ break;
+ case 'c':
+ monitor_printc(mon, v);
+ break;
+ }
+ i += wsize;
+ }
+ monitor_printf(mon, "\n");
+ addr += l;
+ len -= l;
+ }
+}
+
+static void do_memory_dump(Monitor *mon, const QDict *qdict)
+{
+ int count = qdict_get_int(qdict, "count");
+ int format = qdict_get_int(qdict, "format");
+ int size = qdict_get_int(qdict, "size");
+ target_long addr = qdict_get_int(qdict, "addr");
+
+ memory_dump(mon, count, format, size, addr, 0);
+}
+
+static void do_physical_memory_dump(Monitor *mon, const QDict *qdict)
+{
+ int count = qdict_get_int(qdict, "count");
+ int format = qdict_get_int(qdict, "format");
+ int size = qdict_get_int(qdict, "size");
+ target_phys_addr_t addr = qdict_get_int(qdict, "addr");
+
+ memory_dump(mon, count, format, size, addr, 1);
+}
+
+static void do_print(Monitor *mon, const QDict *qdict)
+{
+ int format = qdict_get_int(qdict, "format");
+ target_phys_addr_t val = qdict_get_int(qdict, "val");
+
+#if TARGET_PHYS_ADDR_BITS == 32
+ switch(format) {
+ case 'o':
+ monitor_printf(mon, "%#o", val);
+ break;
+ case 'x':
+ monitor_printf(mon, "%#x", val);
+ break;
+ case 'u':
+ monitor_printf(mon, "%u", val);
+ break;
+ default:
+ case 'd':
+ monitor_printf(mon, "%d", val);
+ break;
+ case 'c':
+ monitor_printc(mon, val);
+ break;
+ }
+#else
+ switch(format) {
+ case 'o':
+ monitor_printf(mon, "%#" PRIo64, val);
+ break;
+ case 'x':
+ monitor_printf(mon, "%#" PRIx64, val);
+ break;
+ case 'u':
+ monitor_printf(mon, "%" PRIu64, val);
+ break;
+ default:
+ case 'd':
+ monitor_printf(mon, "%" PRId64, val);
+ break;
+ case 'c':
+ monitor_printc(mon, val);
+ break;
+ }
+#endif
+ monitor_printf(mon, "\n");
+}
+
+static int do_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ FILE *f;
+ uint32_t size = qdict_get_int(qdict, "size");
+ const char *filename = qdict_get_str(qdict, "filename");
+ target_long addr = qdict_get_int(qdict, "val");
+ uint32_t l;
+ CPUState *env;
+ uint8_t buf[1024];
+ int ret = -1;
+
+ env = mon_get_cpu();
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ qerror_report(QERR_OPEN_FILE_FAILED, filename);
+ return -1;
+ }
+ while (size != 0) {
+ l = sizeof(buf);
+ if (l > size)
+ l = size;
+ cpu_memory_rw_debug(env, addr, buf, l, 0);
+ if (fwrite(buf, 1, l, f) != l) {
+ monitor_printf(mon, "fwrite() error in do_memory_save\n");
+ goto exit;
+ }
+ addr += l;
+ size -= l;
+ }
+
+ ret = 0;
+
+exit:
+ fclose(f);
+ return ret;
+}
+
+static int do_physical_memory_save(Monitor *mon, const QDict *qdict,
+ QObject **ret_data)
+{
+ FILE *f;
+ uint32_t l;
+ uint8_t buf[1024];
+ uint32_t size = qdict_get_int(qdict, "size");
+ const char *filename = qdict_get_str(qdict, "filename");
+ target_phys_addr_t addr = qdict_get_int(qdict, "val");
+ int ret = -1;
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ qerror_report(QERR_OPEN_FILE_FAILED, filename);
+ return -1;
+ }
+ while (size != 0) {
+ l = sizeof(buf);
+ if (l > size)
+ l = size;
+ cpu_physical_memory_rw(addr, buf, l, 0);
+ if (fwrite(buf, 1, l, f) != l) {
+ monitor_printf(mon, "fwrite() error in do_physical_memory_save\n");
+ goto exit;
+ }
+ fflush(f);
+ addr += l;
+ size -= l;
+ }
+
+ ret = 0;
+
+exit:
+ fclose(f);
+ return ret;
+}
+
+static void do_sum(Monitor *mon, const QDict *qdict)
+{
+ uint32_t addr;
+ uint8_t buf[1];
+ uint16_t sum;
+ uint32_t start = qdict_get_int(qdict, "start");
+ uint32_t size = qdict_get_int(qdict, "size");
+
+ sum = 0;
+ for(addr = start; addr < (start + size); addr++) {
+ cpu_physical_memory_rw(addr, buf, 1, 0);
+ /* BSD sum algorithm ('sum' Unix command) */
+ sum = (sum >> 1) | (sum << 15);
+ sum += buf[0];
+ }
+ monitor_printf(mon, "%05d\n", sum);
+}
+
+typedef struct {
+ int keycode;
+ const char *name;
+} KeyDef;
+
+static const KeyDef key_defs[] = {
+ { 0x2a, "shift" },
+ { 0x36, "shift_r" },
+
+ { 0x38, "alt" },
+ { 0xb8, "alt_r" },
+ { 0x64, "altgr" },
+ { 0xe4, "altgr_r" },
+ { 0x1d, "ctrl" },
+ { 0x9d, "ctrl_r" },
+
+ { 0xdd, "menu" },
+
+ { 0x01, "esc" },
+
+ { 0x02, "1" },
+ { 0x03, "2" },
+ { 0x04, "3" },
+ { 0x05, "4" },
+ { 0x06, "5" },
+ { 0x07, "6" },
+ { 0x08, "7" },
+ { 0x09, "8" },
+ { 0x0a, "9" },
+ { 0x0b, "0" },
+ { 0x0c, "minus" },
+ { 0x0d, "equal" },
+ { 0x0e, "backspace" },
+
+ { 0x0f, "tab" },
+ { 0x10, "q" },
+ { 0x11, "w" },
+ { 0x12, "e" },
+ { 0x13, "r" },
+ { 0x14, "t" },
+ { 0x15, "y" },
+ { 0x16, "u" },
+ { 0x17, "i" },
+ { 0x18, "o" },
+ { 0x19, "p" },
+ { 0x1a, "bracket_left" },
+ { 0x1b, "bracket_right" },
+ { 0x1c, "ret" },
+
+ { 0x1e, "a" },
+ { 0x1f, "s" },
+ { 0x20, "d" },
+ { 0x21, "f" },
+ { 0x22, "g" },
+ { 0x23, "h" },
+ { 0x24, "j" },
+ { 0x25, "k" },
+ { 0x26, "l" },
+ { 0x27, "semicolon" },
+ { 0x28, "apostrophe" },
+ { 0x29, "grave_accent" },
+
+ { 0x2b, "backslash" },
+ { 0x2c, "z" },
+ { 0x2d, "x" },
+ { 0x2e, "c" },
+ { 0x2f, "v" },
+ { 0x30, "b" },
+ { 0x31, "n" },
+ { 0x32, "m" },
+ { 0x33, "comma" },
+ { 0x34, "dot" },
+ { 0x35, "slash" },
+
+ { 0x37, "asterisk" },
+
+ { 0x39, "spc" },
+ { 0x3a, "caps_lock" },
+ { 0x3b, "f1" },
+ { 0x3c, "f2" },
+ { 0x3d, "f3" },
+ { 0x3e, "f4" },
+ { 0x3f, "f5" },
+ { 0x40, "f6" },
+ { 0x41, "f7" },
+ { 0x42, "f8" },
+ { 0x43, "f9" },
+ { 0x44, "f10" },
+ { 0x45, "num_lock" },
+ { 0x46, "scroll_lock" },
+
+ { 0xb5, "kp_divide" },
+ { 0x37, "kp_multiply" },
+ { 0x4a, "kp_subtract" },
+ { 0x4e, "kp_add" },
+ { 0x9c, "kp_enter" },
+ { 0x53, "kp_decimal" },
+ { 0x54, "sysrq" },
+
+ { 0x52, "kp_0" },
+ { 0x4f, "kp_1" },
+ { 0x50, "kp_2" },
+ { 0x51, "kp_3" },
+ { 0x4b, "kp_4" },
+ { 0x4c, "kp_5" },
+ { 0x4d, "kp_6" },
+ { 0x47, "kp_7" },
+ { 0x48, "kp_8" },
+ { 0x49, "kp_9" },
+
+ { 0x56, "<" },
+
+ { 0x57, "f11" },
+ { 0x58, "f12" },
+
+ { 0xb7, "print" },
+
+ { 0xc7, "home" },
+ { 0xc9, "pgup" },
+ { 0xd1, "pgdn" },
+ { 0xcf, "end" },
+
+ { 0xcb, "left" },
+ { 0xc8, "up" },
+ { 0xd0, "down" },
+ { 0xcd, "right" },
+
+ { 0xd2, "insert" },
+ { 0xd3, "delete" },
+#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
+ { 0xf0, "stop" },
+ { 0xf1, "again" },
+ { 0xf2, "props" },
+ { 0xf3, "undo" },
+ { 0xf4, "front" },
+ { 0xf5, "copy" },
+ { 0xf6, "open" },
+ { 0xf7, "paste" },
+ { 0xf8, "find" },
+ { 0xf9, "cut" },
+ { 0xfa, "lf" },
+ { 0xfb, "help" },
+ { 0xfc, "meta_l" },
+ { 0xfd, "meta_r" },
+ { 0xfe, "compose" },
+#endif
+ { 0, NULL },
+};
+
+static int get_keycode(const char *key)
+{
+ const KeyDef *p;
+ char *endp;
+ int ret;
+
+ for(p = key_defs; p->name != NULL; p++) {
+ if (!strcmp(key, p->name))
+ return p->keycode;
+ }
+ if (strstart(key, "0x", NULL)) {
+ ret = strtoul(key, &endp, 0);
+ if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
+ return ret;
+ }
+ return -1;
+}
+
+#define MAX_KEYCODES 16
+static uint8_t keycodes[MAX_KEYCODES];
+static int nb_pending_keycodes;
+static QEMUTimer *key_timer;
+
+static void release_keys(void *opaque)
+{
+ int keycode;
+
+ while (nb_pending_keycodes > 0) {
+ nb_pending_keycodes--;
+ keycode = keycodes[nb_pending_keycodes];
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode | 0x80);
+ }
+}
+
+static void do_sendkey(Monitor *mon, const QDict *qdict)
+{
+ char keyname_buf[16];
+ char *separator;
+ int keyname_len, keycode, i;
+ const char *string = qdict_get_str(qdict, "string");
+ int has_hold_time = qdict_haskey(qdict, "hold_time");
+ int hold_time = qdict_get_try_int(qdict, "hold_time", -1);
+
+ if (nb_pending_keycodes > 0) {
+ qemu_del_timer(key_timer);
+ release_keys(NULL);
+ }
+ if (!has_hold_time)
+ hold_time = 100;
+ i = 0;
+ while (1) {
+ separator = strchr(string, '-');
+ keyname_len = separator ? separator - string : strlen(string);
+ if (keyname_len > 0) {
+ pstrcpy(keyname_buf, sizeof(keyname_buf), string);
+ if (keyname_len > sizeof(keyname_buf) - 1) {
+ monitor_printf(mon, "invalid key: '%s...'\n", keyname_buf);
+ return;
+ }
+ if (i == MAX_KEYCODES) {
+ monitor_printf(mon, "too many keys\n");
+ return;
+ }
+ keyname_buf[keyname_len] = 0;
+ keycode = get_keycode(keyname_buf);
+ if (keycode < 0) {
+ monitor_printf(mon, "unknown key: '%s'\n", keyname_buf);
+ return;
+ }
+ keycodes[i++] = keycode;
+ }
+ if (!separator)
+ break;
+ string = separator + 1;
+ }
+ nb_pending_keycodes = i;
+ /* key down events */
+ for (i = 0; i < nb_pending_keycodes; i++) {
+ keycode = keycodes[i];
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode & 0x7f);
+ }
+ /* delayed key up events */
+ qemu_mod_timer(key_timer, qemu_get_clock(vm_clock) +
+ muldiv64(get_ticks_per_sec(), hold_time, 1000));
+}
+
+static int mouse_button_state;
+
+static void do_mouse_move(Monitor *mon, const QDict *qdict)
+{
+ int dx, dy, dz;
+ const char *dx_str = qdict_get_str(qdict, "dx_str");
+ const char *dy_str = qdict_get_str(qdict, "dy_str");
+ const char *dz_str = qdict_get_try_str(qdict, "dz_str");
+ dx = strtol(dx_str, NULL, 0);
+ dy = strtol(dy_str, NULL, 0);
+ dz = 0;
+ if (dz_str)
+ dz = strtol(dz_str, NULL, 0);
+ kbd_mouse_event(dx, dy, dz, mouse_button_state);
+}
+
+static void do_mouse_button(Monitor *mon, const QDict *qdict)
+{
+ int button_state = qdict_get_int(qdict, "button_state");
+ mouse_button_state = button_state;
+ kbd_mouse_event(0, 0, 0, mouse_button_state);
+}
+
+static void do_ioport_read(Monitor *mon, const QDict *qdict)
+{
+ int size = qdict_get_int(qdict, "size");
+ int addr = qdict_get_int(qdict, "addr");
+ int has_index = qdict_haskey(qdict, "index");
+ uint32_t val;
+ int suffix;
+
+ if (has_index) {
+ int index = qdict_get_int(qdict, "index");
+ cpu_outb(addr & IOPORTS_MASK, index & 0xff);
+ addr++;
+ }
+ addr &= 0xffff;
+
+ switch(size) {
+ default:
+ case 1:
+ val = cpu_inb(addr);
+ suffix = 'b';
+ break;
+ case 2:
+ val = cpu_inw(addr);
+ suffix = 'w';
+ break;
+ case 4:
+ val = cpu_inl(addr);
+ suffix = 'l';
+ break;
+ }
+ monitor_printf(mon, "port%c[0x%04x] = %#0*x\n",
+ suffix, addr, size * 2, val);
+}
+
+static void do_ioport_write(Monitor *mon, const QDict *qdict)
+{
+ int size = qdict_get_int(qdict, "size");
+ int addr = qdict_get_int(qdict, "addr");
+ int val = qdict_get_int(qdict, "val");
+
+ addr &= IOPORTS_MASK;
+
+ switch (size) {
+ default:
+ case 1:
+ cpu_outb(addr, val);
+ break;
+ case 2:
+ cpu_outw(addr, val);
+ break;
+ case 4:
+ cpu_outl(addr, val);
+ break;
+ }
+}
+
+static void do_boot_set(Monitor *mon, const QDict *qdict)
+{
+ int res;
+ const char *bootdevice = qdict_get_str(qdict, "bootdevice");
+
+ res = qemu_boot_set(bootdevice);
+ if (res == 0) {
+ monitor_printf(mon, "boot device list now set to %s\n", bootdevice);
+ } else if (res > 0) {
+ monitor_printf(mon, "setting boot device list failed\n");
+ } else {
+ monitor_printf(mon, "no function defined to set boot device list for "
+ "this architecture\n");
+ }
+}
+
+/**
+ * do_system_reset(): Issue a machine reset
+ */
+static int do_system_reset(Monitor *mon, const QDict *qdict,
+ QObject **ret_data)
+{
+ qemu_system_reset_request();
+ return 0;
+}
+
+/**
+ * do_system_powerdown(): Issue a machine powerdown
+ */
+static int do_system_powerdown(Monitor *mon, const QDict *qdict,
+ QObject **ret_data)
+{
+ qemu_system_powerdown_request();
+ return 0;
+}
+
+#if defined(TARGET_I386)
+static void print_pte(Monitor *mon, target_phys_addr_t addr,
+ target_phys_addr_t pte,
+ target_phys_addr_t mask)
+{
+#ifdef TARGET_X86_64
+ if (addr & (1ULL << 47)) {
+ addr |= -1LL << 48;
+ }
+#endif
+ monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx
+ " %c%c%c%c%c%c%c%c%c\n",
+ addr,
+ pte & mask,
+ pte & PG_NX_MASK ? 'X' : '-',
+ pte & PG_GLOBAL_MASK ? 'G' : '-',
+ pte & PG_PSE_MASK ? 'P' : '-',
+ pte & PG_DIRTY_MASK ? 'D' : '-',
+ pte & PG_ACCESSED_MASK ? 'A' : '-',
+ pte & PG_PCD_MASK ? 'C' : '-',
+ pte & PG_PWT_MASK ? 'T' : '-',
+ pte & PG_USER_MASK ? 'U' : '-',
+ pte & PG_RW_MASK ? 'W' : '-');
+}
+
+static void tlb_info_32(Monitor *mon, CPUState *env)
+{
+ int l1, l2;
+ uint32_t pgd, pde, pte;
+
+ pgd = env->cr[3] & ~0xfff;
+ for(l1 = 0; l1 < 1024; l1++) {
+ cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
+ pde = le32_to_cpu(pde);
+ if (pde & PG_PRESENT_MASK) {
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ /* 4M pages */
+ print_pte(mon, (l1 << 22), pde, ~((1 << 21) - 1));
+ } else {
+ for(l2 = 0; l2 < 1024; l2++) {
+ cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
+ (uint8_t *)&pte, 4);
+ pte = le32_to_cpu(pte);
+ if (pte & PG_PRESENT_MASK) {
+ print_pte(mon, (l1 << 22) + (l2 << 12),
+ pte & ~PG_PSE_MASK,
+ ~0xfff);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void tlb_info_pae32(Monitor *mon, CPUState *env)
+{
+ int l1, l2, l3;
+ uint64_t pdpe, pde, pte;
+ uint64_t pdp_addr, pd_addr, pt_addr;
+
+ pdp_addr = env->cr[3] & ~0x1f;
+ for (l1 = 0; l1 < 4; l1++) {
+ cpu_physical_memory_read(pdp_addr + l1 * 8, (uint8_t *)&pdpe, 8);
+ pdpe = le64_to_cpu(pdpe);
+ if (pdpe & PG_PRESENT_MASK) {
+ pd_addr = pdpe & 0x3fffffffff000ULL;
+ for (l2 = 0; l2 < 512; l2++) {
+ cpu_physical_memory_read(pd_addr + l2 * 8,
+ (uint8_t *)&pde, 8);
+ pde = le64_to_cpu(pde);
+ if (pde & PG_PRESENT_MASK) {
+ if (pde & PG_PSE_MASK) {
+ /* 2M pages with PAE, CR4.PSE is ignored */
+ print_pte(mon, (l1 << 30 ) + (l2 << 21), pde,
+ ~((target_phys_addr_t)(1 << 20) - 1));
+ } else {
+ pt_addr = pde & 0x3fffffffff000ULL;
+ for (l3 = 0; l3 < 512; l3++) {
+ cpu_physical_memory_read(pt_addr + l3 * 8,
+ (uint8_t *)&pte, 8);
+ pte = le64_to_cpu(pte);
+ if (pte & PG_PRESENT_MASK) {
+ print_pte(mon, (l1 << 30 ) + (l2 << 21)
+ + (l3 << 12),
+ pte & ~PG_PSE_MASK,
+ ~(target_phys_addr_t)0xfff);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#ifdef TARGET_X86_64
+static void tlb_info_64(Monitor *mon, CPUState *env)
+{
+ uint64_t l1, l2, l3, l4;
+ uint64_t pml4e, pdpe, pde, pte;
+ uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr;
+
+ pml4_addr = env->cr[3] & 0x3fffffffff000ULL;
+ for (l1 = 0; l1 < 512; l1++) {
+ cpu_physical_memory_read(pml4_addr + l1 * 8, (uint8_t *)&pml4e, 8);
+ pml4e = le64_to_cpu(pml4e);
+ if (pml4e & PG_PRESENT_MASK) {
+ pdp_addr = pml4e & 0x3fffffffff000ULL;
+ for (l2 = 0; l2 < 512; l2++) {
+ cpu_physical_memory_read(pdp_addr + l2 * 8, (uint8_t *)&pdpe,
+ 8);
+ pdpe = le64_to_cpu(pdpe);
+ if (pdpe & PG_PRESENT_MASK) {
+ if (pdpe & PG_PSE_MASK) {
+ /* 1G pages, CR4.PSE is ignored */
+ print_pte(mon, (l1 << 39) + (l2 << 30), pdpe,
+ 0x3ffffc0000000ULL);
+ } else {
+ pd_addr = pdpe & 0x3fffffffff000ULL;
+ for (l3 = 0; l3 < 512; l3++) {
+ cpu_physical_memory_read(pd_addr + l3 * 8,
+ (uint8_t *)&pde, 8);
+ pde = le64_to_cpu(pde);
+ if (pde & PG_PRESENT_MASK) {
+ if (pde & PG_PSE_MASK) {
+ /* 2M pages, CR4.PSE is ignored */
+ print_pte(mon, (l1 << 39) + (l2 << 30) +
+ (l3 << 21), pde,
+ 0x3ffffffe00000ULL);
+ } else {
+ pt_addr = pde & 0x3fffffffff000ULL;
+ for (l4 = 0; l4 < 512; l4++) {
+ cpu_physical_memory_read(pt_addr
+ + l4 * 8,
+ (uint8_t *)&pte,
+ 8);
+ pte = le64_to_cpu(pte);
+ if (pte & PG_PRESENT_MASK) {
+ print_pte(mon, (l1 << 39) +
+ (l2 << 30) +
+ (l3 << 21) + (l4 << 12),
+ pte & ~PG_PSE_MASK,
+ 0x3fffffffff000ULL);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+static void tlb_info(Monitor *mon)
+{
+ CPUState *env;
+
+ env = mon_get_cpu();
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ monitor_printf(mon, "PG disabled\n");
+ return;
+ }
+ if (env->cr[4] & CR4_PAE_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ tlb_info_64(mon, env);
+ } else
+#endif
+ {
+ tlb_info_pae32(mon, env);
+ }
+ } else {
+ tlb_info_32(mon, env);
+ }
+}
+
+static void mem_print(Monitor *mon, target_phys_addr_t *pstart,
+ int *plast_prot,
+ target_phys_addr_t end, int prot)
+{
+ int prot1;
+ prot1 = *plast_prot;
+ if (prot != prot1) {
+ if (*pstart != -1) {
+ monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " "
+ TARGET_FMT_plx " %c%c%c\n",
+ *pstart, end, end - *pstart,
+ prot1 & PG_USER_MASK ? 'u' : '-',
+ 'r',
+ prot1 & PG_RW_MASK ? 'w' : '-');
+ }
+ if (prot != 0)
+ *pstart = end;
+ else
+ *pstart = -1;
+ *plast_prot = prot;
+ }
+}
+
+static void mem_info_32(Monitor *mon, CPUState *env)
+{
+ int l1, l2, prot, last_prot;
+ uint32_t pgd, pde, pte;
+ target_phys_addr_t start, end;
+
+ pgd = env->cr[3] & ~0xfff;
+ last_prot = 0;
+ start = -1;
+ for(l1 = 0; l1 < 1024; l1++) {
+ cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
+ pde = le32_to_cpu(pde);
+ end = l1 << 22;
+ if (pde & PG_PRESENT_MASK) {
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
+ mem_print(mon, &start, &last_prot, end, prot);
+ } else {
+ for(l2 = 0; l2 < 1024; l2++) {
+ cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
+ (uint8_t *)&pte, 4);
+ pte = le32_to_cpu(pte);
+ end = (l1 << 22) + (l2 << 12);
+ if (pte & PG_PRESENT_MASK) {
+ prot = pte & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
+ } else {
+ prot = 0;
+ }
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+}
+
+static void mem_info_pae32(Monitor *mon, CPUState *env)
+{
+ int l1, l2, l3, prot, last_prot;
+ uint64_t pdpe, pde, pte;
+ uint64_t pdp_addr, pd_addr, pt_addr;
+ target_phys_addr_t start, end;
+
+ pdp_addr = env->cr[3] & ~0x1f;
+ last_prot = 0;
+ start = -1;
+ for (l1 = 0; l1 < 4; l1++) {
+ cpu_physical_memory_read(pdp_addr + l1 * 8, (uint8_t *)&pdpe, 8);
+ pdpe = le64_to_cpu(pdpe);
+ end = l1 << 30;
+ if (pdpe & PG_PRESENT_MASK) {
+ pd_addr = pdpe & 0x3fffffffff000ULL;
+ for (l2 = 0; l2 < 512; l2++) {
+ cpu_physical_memory_read(pd_addr + l2 * 8,
+ (uint8_t *)&pde, 8);
+ pde = le64_to_cpu(pde);
+ end = (l1 << 30) + (l2 << 21);
+ if (pde & PG_PRESENT_MASK) {
+ if (pde & PG_PSE_MASK) {
+ prot = pde & (PG_USER_MASK | PG_RW_MASK |
+ PG_PRESENT_MASK);
+ mem_print(mon, &start, &last_prot, end, prot);
+ } else {
+ pt_addr = pde & 0x3fffffffff000ULL;
+ for (l3 = 0; l3 < 512; l3++) {
+ cpu_physical_memory_read(pt_addr + l3 * 8,
+ (uint8_t *)&pte, 8);
+ pte = le64_to_cpu(pte);
+ end = (l1 << 30) + (l2 << 21) + (l3 << 12);
+ if (pte & PG_PRESENT_MASK) {
+ prot = pte & (PG_USER_MASK | PG_RW_MASK |
+ PG_PRESENT_MASK);
+ } else {
+ prot = 0;
+ }
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+}
+
+
+#ifdef TARGET_X86_64
+static void mem_info_64(Monitor *mon, CPUState *env)
+{
+ int prot, last_prot;
+ uint64_t l1, l2, l3, l4;
+ uint64_t pml4e, pdpe, pde, pte;
+ uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr, start, end;
+
+ pml4_addr = env->cr[3] & 0x3fffffffff000ULL;
+ last_prot = 0;
+ start = -1;
+ for (l1 = 0; l1 < 512; l1++) {
+ cpu_physical_memory_read(pml4_addr + l1 * 8, (uint8_t *)&pml4e, 8);
+ pml4e = le64_to_cpu(pml4e);
+ end = l1 << 39;
+ if (pml4e & PG_PRESENT_MASK) {
+ pdp_addr = pml4e & 0x3fffffffff000ULL;
+ for (l2 = 0; l2 < 512; l2++) {
+ cpu_physical_memory_read(pdp_addr + l2 * 8, (uint8_t *)&pdpe,
+ 8);
+ pdpe = le64_to_cpu(pdpe);
+ end = (l1 << 39) + (l2 << 30);
+ if (pdpe & PG_PRESENT_MASK) {
+ if (pdpe & PG_PSE_MASK) {
+ prot = pdpe & (PG_USER_MASK | PG_RW_MASK |
+ PG_PRESENT_MASK);
+ mem_print(mon, &start, &last_prot, end, prot);
+ } else {
+ pd_addr = pdpe & 0x3fffffffff000ULL;
+ for (l3 = 0; l3 < 512; l3++) {
+ cpu_physical_memory_read(pd_addr + l3 * 8,
+ (uint8_t *)&pde, 8);
+ pde = le64_to_cpu(pde);
+ end = (l1 << 39) + (l2 << 30) + (l3 << 21);
+ if (pde & PG_PRESENT_MASK) {
+ if (pde & PG_PSE_MASK) {
+ prot = pde & (PG_USER_MASK | PG_RW_MASK |
+ PG_PRESENT_MASK);
+ mem_print(mon, &start, &last_prot, end, prot);
+ } else {
+ pt_addr = pde & 0x3fffffffff000ULL;
+ for (l4 = 0; l4 < 512; l4++) {
+ cpu_physical_memory_read(pt_addr
+ + l4 * 8,
+ (uint8_t *)&pte,
+ 8);
+ pte = le64_to_cpu(pte);
+ end = (l1 << 39) + (l2 << 30) +
+ (l3 << 21) + (l4 << 12);
+ if (pte & PG_PRESENT_MASK) {
+ prot = pte & (PG_USER_MASK | PG_RW_MASK |
+ PG_PRESENT_MASK);
+ } else {
+ prot = 0;
+ }
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+ } else {
+ prot = 0;
+ mem_print(mon, &start, &last_prot, end, prot);
+ }
+ }
+}
+#endif
+
+static void mem_info(Monitor *mon)
+{
+ CPUState *env;
+
+ env = mon_get_cpu();
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ monitor_printf(mon, "PG disabled\n");
+ return;
+ }
+ if (env->cr[4] & CR4_PAE_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ mem_info_64(mon, env);
+ } else
+#endif
+ {
+ mem_info_pae32(mon, env);
+ }
+ } else {
+ mem_info_32(mon, env);
+ }
+}
+#endif
+
+#if defined(TARGET_SH4)
+
+static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
+{
+ monitor_printf(mon, " tlb%i:\t"
+ "asid=%hhu vpn=%x\tppn=%x\tsz=%hhu size=%u\t"
+ "v=%hhu shared=%hhu cached=%hhu prot=%hhu "
+ "dirty=%hhu writethrough=%hhu\n",
+ idx,
+ tlb->asid, tlb->vpn, tlb->ppn, tlb->sz, tlb->size,
+ tlb->v, tlb->sh, tlb->c, tlb->pr,
+ tlb->d, tlb->wt);
+}
+
+static void tlb_info(Monitor *mon)
+{
+ CPUState *env = mon_get_cpu();
+ int i;
+
+ monitor_printf (mon, "ITLB:\n");
+ for (i = 0 ; i < ITLB_SIZE ; i++)
+ print_tlb (mon, i, &env->itlb[i]);
+ monitor_printf (mon, "UTLB:\n");
+ for (i = 0 ; i < UTLB_SIZE ; i++)
+ print_tlb (mon, i, &env->utlb[i]);
+}
+
+#endif
+
+#if defined(TARGET_SPARC)
+static void tlb_info(Monitor *mon)
+{
+ CPUState *env1 = mon_get_cpu();
+
+ dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1);
+}
+#endif
+
+static void do_info_kvm_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+
+ qdict = qobject_to_qdict(data);
+
+ monitor_printf(mon, "kvm support: ");
+ if (qdict_get_bool(qdict, "present")) {
+ monitor_printf(mon, "%s\n", qdict_get_bool(qdict, "enabled") ?
+ "enabled" : "disabled");
+ } else {
+ monitor_printf(mon, "not compiled\n");
+ }
+}
+
+static void do_info_kvm(Monitor *mon, QObject **ret_data)
+{
+#ifdef CONFIG_KVM
+ *ret_data = qobject_from_jsonf("{ 'enabled': %i, 'present': true }",
+ kvm_enabled());
+#else
+ *ret_data = qobject_from_jsonf("{ 'enabled': false, 'present': false }");
+#endif
+}
+
+static void do_info_numa(Monitor *mon)
+{
+ int i;
+ CPUState *env;
+
+ monitor_printf(mon, "%d nodes\n", nb_numa_nodes);
+ for (i = 0; i < nb_numa_nodes; i++) {
+ monitor_printf(mon, "node %d cpus:", i);
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (env->numa_node == i) {
+ monitor_printf(mon, " %d", env->cpu_index);
+ }
+ }
+ monitor_printf(mon, "\n");
+ monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i,
+ node_mem[i] >> 20);
+ }
+}
+
+#ifdef CONFIG_PROFILER
+
+int64_t qemu_time;
+int64_t dev_time;
+
+static void do_info_profile(Monitor *mon)
+{
+ int64_t total;
+ total = qemu_time;
+ if (total == 0)
+ total = 1;
+ monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
+ dev_time, dev_time / (double)get_ticks_per_sec());
+ monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
+ qemu_time, qemu_time / (double)get_ticks_per_sec());
+ qemu_time = 0;
+ dev_time = 0;
+}
+#else
+static void do_info_profile(Monitor *mon)
+{
+ monitor_printf(mon, "Internal profiler not compiled\n");
+}
+#endif
+
+/* Capture support */
+static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
+
+static void do_info_capture(Monitor *mon)
+{
+ int i;
+ CaptureState *s;
+
+ for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+ monitor_printf(mon, "[%d]: ", i);
+ s->ops.info (s->opaque);
+ }
+}
+
+#ifdef HAS_AUDIO
+static void do_stop_capture(Monitor *mon, const QDict *qdict)
+{
+ int i;
+ int n = qdict_get_int(qdict, "n");
+ CaptureState *s;
+
+ for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+ if (i == n) {
+ s->ops.destroy (s->opaque);
+ QLIST_REMOVE (s, entries);
+ qemu_free (s);
+ return;
+ }
+ }
+}
+
+static void do_wav_capture(Monitor *mon, const QDict *qdict)
+{
+ const char *path = qdict_get_str(qdict, "path");
+ int has_freq = qdict_haskey(qdict, "freq");
+ int freq = qdict_get_try_int(qdict, "freq", -1);
+ int has_bits = qdict_haskey(qdict, "bits");
+ int bits = qdict_get_try_int(qdict, "bits", -1);
+ int has_channels = qdict_haskey(qdict, "nchannels");
+ int nchannels = qdict_get_try_int(qdict, "nchannels", -1);
+ CaptureState *s;
+
+ s = qemu_mallocz (sizeof (*s));
+
+ freq = has_freq ? freq : 44100;
+ bits = has_bits ? bits : 16;
+ nchannels = has_channels ? nchannels : 2;
+
+ if (wav_start_capture (s, path, freq, bits, nchannels)) {
+ monitor_printf(mon, "Failed to add wave capture\n");
+ qemu_free (s);
+ return;
+ }
+ QLIST_INSERT_HEAD (&capture_head, s, entries);
+}
+#endif
+
+#if defined(TARGET_I386)
+static void do_inject_nmi(Monitor *mon, const QDict *qdict)
+{
+ CPUState *env;
+ int cpu_index = qdict_get_int(qdict, "cpu_index");
+
+ for (env = first_cpu; env != NULL; env = env->next_cpu)
+ if (env->cpu_index == cpu_index) {
+ if (kvm_enabled())
+ kvm_inject_interrupt(env, CPU_INTERRUPT_NMI);
+ else
+ cpu_interrupt(env, CPU_INTERRUPT_NMI);
+ break;
+ }
+}
+#endif
+
+static void do_info_status_print(Monitor *mon, const QObject *data)
+{
+ QDict *qdict;
+
+ qdict = qobject_to_qdict(data);
+
+ monitor_printf(mon, "VM status: ");
+ if (qdict_get_bool(qdict, "running")) {
+ monitor_printf(mon, "running");
+ if (qdict_get_bool(qdict, "singlestep")) {
+ monitor_printf(mon, " (single step mode)");
+ }
+ } else {
+ monitor_printf(mon, "paused");
+ }
+
+ monitor_printf(mon, "\n");
+}
+
+static void do_info_status(Monitor *mon, QObject **ret_data)
+{
+ *ret_data = qobject_from_jsonf("{ 'running': %i, 'singlestep': %i }",
+ vm_running, singlestep);
+}
+
+static qemu_acl *find_acl(Monitor *mon, const char *name)
+{
+ qemu_acl *acl = qemu_acl_find(name);
+
+ if (!acl) {
+ monitor_printf(mon, "acl: unknown list '%s'\n", name);
+ }
+ return acl;
+}
+
+static void do_acl_show(Monitor *mon, const QDict *qdict)
+{
+ const char *aclname = qdict_get_str(qdict, "aclname");
+ qemu_acl *acl = find_acl(mon, aclname);
+ qemu_acl_entry *entry;
+ int i = 0;
+
+ if (acl) {
+ monitor_printf(mon, "policy: %s\n",
+ acl->defaultDeny ? "deny" : "allow");
+ QTAILQ_FOREACH(entry, &acl->entries, next) {
+ i++;
+ monitor_printf(mon, "%d: %s %s\n", i,
+ entry->deny ? "deny" : "allow", entry->match);
+ }
+ }
+}
+
+static void do_acl_reset(Monitor *mon, const QDict *qdict)
+{
+ const char *aclname = qdict_get_str(qdict, "aclname");
+ qemu_acl *acl = find_acl(mon, aclname);
+
+ if (acl) {
+ qemu_acl_reset(acl);
+ monitor_printf(mon, "acl: removed all rules\n");
+ }
+}
+
+static void do_acl_policy(Monitor *mon, const QDict *qdict)
+{
+ const char *aclname = qdict_get_str(qdict, "aclname");
+ const char *policy = qdict_get_str(qdict, "policy");
+ qemu_acl *acl = find_acl(mon, aclname);
+
+ if (acl) {
+ if (strcmp(policy, "allow") == 0) {
+ acl->defaultDeny = 0;
+ monitor_printf(mon, "acl: policy set to 'allow'\n");
+ } else if (strcmp(policy, "deny") == 0) {
+ acl->defaultDeny = 1;
+ monitor_printf(mon, "acl: policy set to 'deny'\n");
+ } else {
+ monitor_printf(mon, "acl: unknown policy '%s', "
+ "expected 'deny' or 'allow'\n", policy);
+ }
+ }
+}
+
+static void do_acl_add(Monitor *mon, const QDict *qdict)
+{
+ const char *aclname = qdict_get_str(qdict, "aclname");
+ const char *match = qdict_get_str(qdict, "match");
+ const char *policy = qdict_get_str(qdict, "policy");
+ int has_index = qdict_haskey(qdict, "index");
+ int index = qdict_get_try_int(qdict, "index", -1);
+ qemu_acl *acl = find_acl(mon, aclname);
+ int deny, ret;
+
+ if (acl) {
+ if (strcmp(policy, "allow") == 0) {
+ deny = 0;
+ } else if (strcmp(policy, "deny") == 0) {
+ deny = 1;
+ } else {
+ monitor_printf(mon, "acl: unknown policy '%s', "
+ "expected 'deny' or 'allow'\n", policy);
+ return;
+ }
+ if (has_index)
+ ret = qemu_acl_insert(acl, deny, match, index);
+ else
+ ret = qemu_acl_append(acl, deny, match);
+ if (ret < 0)
+ monitor_printf(mon, "acl: unable to add acl entry\n");
+ else
+ monitor_printf(mon, "acl: added rule at position %d\n", ret);
+ }
+}
+
+static void do_acl_remove(Monitor *mon, const QDict *qdict)
+{
+ const char *aclname = qdict_get_str(qdict, "aclname");
+ const char *match = qdict_get_str(qdict, "match");
+ qemu_acl *acl = find_acl(mon, aclname);
+ int ret;
+
+ if (acl) {
+ ret = qemu_acl_remove(acl, match);
+ if (ret < 0)
+ monitor_printf(mon, "acl: no matching acl entry\n");
+ else
+ monitor_printf(mon, "acl: removed rule at position %d\n", ret);
+ }
+}
+
+#if defined(TARGET_I386)
+static void do_inject_mce(Monitor *mon, const QDict *qdict)
+{
+ CPUState *cenv;
+ int cpu_index = qdict_get_int(qdict, "cpu_index");
+ int bank = qdict_get_int(qdict, "bank");
+ uint64_t status = qdict_get_int(qdict, "status");
+ uint64_t mcg_status = qdict_get_int(qdict, "mcg_status");
+ uint64_t addr = qdict_get_int(qdict, "addr");
+ uint64_t misc = qdict_get_int(qdict, "misc");
+ int broadcast = qdict_get_try_bool(qdict, "broadcast", 0);
+
+ for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
+ if (cenv->cpu_index == cpu_index && cenv->mcg_cap) {
+ cpu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc,
+ broadcast);
+ break;
+ }
+ }
+}
+#endif
+
+static int do_getfd(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *fdname = qdict_get_str(qdict, "fdname");
+ mon_fd_t *monfd;
+ int fd;
+
+ fd = qemu_chr_get_msgfd(mon->chr);
+ if (fd == -1) {
+ qerror_report(QERR_FD_NOT_SUPPLIED);
+ return -1;
+ }
+
+ if (qemu_isdigit(fdname[0])) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "fdname",
+ "a name not starting with a digit");
+ return -1;
+ }
+
+ QLIST_FOREACH(monfd, &mon->fds, next) {
+ if (strcmp(monfd->name, fdname) != 0) {
+ continue;
+ }
+
+ close(monfd->fd);
+ monfd->fd = fd;
+ return 0;
+ }
+
+ monfd = qemu_mallocz(sizeof(mon_fd_t));
+ monfd->name = qemu_strdup(fdname);
+ monfd->fd = fd;
+
+ QLIST_INSERT_HEAD(&mon->fds, monfd, next);
+ return 0;
+}
+
+static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *fdname = qdict_get_str(qdict, "fdname");
+ mon_fd_t *monfd;
+
+ QLIST_FOREACH(monfd, &mon->fds, next) {
+ if (strcmp(monfd->name, fdname) != 0) {
+ continue;
+ }
+
+ QLIST_REMOVE(monfd, next);
+ close(monfd->fd);
+ qemu_free(monfd->name);
+ qemu_free(monfd);
+ return 0;
+ }
+
+ qerror_report(QERR_FD_NOT_FOUND, fdname);
+ return -1;
+}
+
+static void do_loadvm(Monitor *mon, const QDict *qdict)
+{
+ int saved_vm_running = vm_running;
+ const char *name = qdict_get_str(qdict, "name");
+
+ vm_stop(0);
+
+ if (load_vmstate(name) == 0 && saved_vm_running) {
+ vm_start();
+ }
+}
+
+int monitor_get_fd(Monitor *mon, const char *fdname)
+{
+ mon_fd_t *monfd;
+
+ QLIST_FOREACH(monfd, &mon->fds, next) {
+ int fd;
+
+ if (strcmp(monfd->name, fdname) != 0) {
+ continue;
+ }
+
+ fd = monfd->fd;
+
+ /* caller takes ownership of fd */
+ QLIST_REMOVE(monfd, next);
+ qemu_free(monfd->name);
+ qemu_free(monfd);
+
+ return fd;
+ }
+
+ return -1;
+}
+
+static const mon_cmd_t mon_cmds[] = {
+#include "hmp-commands.h"
+ { NULL, NULL, },
+};
+
+/* Please update hmp-commands.hx when adding or changing commands */
+static const mon_cmd_t info_cmds[] = {
+ {
+ .name = "version",
+ .args_type = "",
+ .params = "",
+ .help = "show the version of QEMU",
+ .user_print = do_info_version_print,
+ .mhandler.info_new = do_info_version,
+ },
+ {
+ .name = "network",
+ .args_type = "",
+ .params = "",
+ .help = "show the network state",
+ .mhandler.info = do_info_network,
+ },
+ {
+ .name = "chardev",
+ .args_type = "",
+ .params = "",
+ .help = "show the character devices",
+ .user_print = qemu_chr_info_print,
+ .mhandler.info_new = qemu_chr_info,
+ },
+ {
+ .name = "block",
+ .args_type = "",
+ .params = "",
+ .help = "show the block devices",
+ .user_print = bdrv_info_print,
+ .mhandler.info_new = bdrv_info,
+ },
+ {
+ .name = "blockstats",
+ .args_type = "",
+ .params = "",
+ .help = "show block device statistics",
+ .user_print = bdrv_stats_print,
+ .mhandler.info_new = bdrv_info_stats,
+ },
+ {
+ .name = "registers",
+ .args_type = "",
+ .params = "",
+ .help = "show the cpu registers",
+ .mhandler.info = do_info_registers,
+ },
+ {
+ .name = "cpus",
+ .args_type = "",
+ .params = "",
+ .help = "show infos for each CPU",
+ .user_print = monitor_print_cpus,
+ .mhandler.info_new = do_info_cpus,
+ },
+ {
+ .name = "history",
+ .args_type = "",
+ .params = "",
+ .help = "show the command line history",
+ .mhandler.info = do_info_history,
+ },
+ {
+ .name = "irq",
+ .args_type = "",
+ .params = "",
+ .help = "show the interrupts statistics (if available)",
+ .mhandler.info = irq_info,
+ },
+ {
+ .name = "pic",
+ .args_type = "",
+ .params = "",
+ .help = "show i8259 (PIC) state",
+ .mhandler.info = pic_info,
+ },
+ {
+ .name = "pci",
+ .args_type = "",
+ .params = "",
+ .help = "show PCI info",
+ .user_print = do_pci_info_print,
+ .mhandler.info_new = do_pci_info,
+ },
+#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC)
+ {
+ .name = "tlb",
+ .args_type = "",
+ .params = "",
+ .help = "show virtual to physical memory mappings",
+ .mhandler.info = tlb_info,
+ },
+#endif
+#if defined(TARGET_I386)
+ {
+ .name = "mem",
+ .args_type = "",
+ .params = "",
+ .help = "show the active virtual memory mappings",
+ .mhandler.info = mem_info,
+ },
+#endif
+ {
+ .name = "jit",
+ .args_type = "",
+ .params = "",
+ .help = "show dynamic compiler info",
+ .mhandler.info = do_info_jit,
+ },
+ {
+ .name = "kvm",
+ .args_type = "",
+ .params = "",
+ .help = "show KVM information",
+ .user_print = do_info_kvm_print,
+ .mhandler.info_new = do_info_kvm,
+ },
+ {
+ .name = "numa",
+ .args_type = "",
+ .params = "",
+ .help = "show NUMA information",
+ .mhandler.info = do_info_numa,
+ },
+ {
+ .name = "usb",
+ .args_type = "",
+ .params = "",
+ .help = "show guest USB devices",
+ .mhandler.info = usb_info,
+ },
+ {
+ .name = "usbhost",
+ .args_type = "",
+ .params = "",
+ .help = "show host USB devices",
+ .mhandler.info = usb_host_info,
+ },
+ {
+ .name = "profile",
+ .args_type = "",
+ .params = "",
+ .help = "show profiling information",
+ .mhandler.info = do_info_profile,
+ },
+ {
+ .name = "capture",
+ .args_type = "",
+ .params = "",
+ .help = "show capture information",
+ .mhandler.info = do_info_capture,
+ },
+ {
+ .name = "snapshots",
+ .args_type = "",
+ .params = "",
+ .help = "show the currently saved VM snapshots",
+ .mhandler.info = do_info_snapshots,
+ },
+ {
+ .name = "status",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM status (running|paused)",
+ .user_print = do_info_status_print,
+ .mhandler.info_new = do_info_status,
+ },
+ {
+ .name = "pcmcia",
+ .args_type = "",
+ .params = "",
+ .help = "show guest PCMCIA status",
+ .mhandler.info = pcmcia_info,
+ },
+ {
+ .name = "mice",
+ .args_type = "",
+ .params = "",
+ .help = "show which guest mouse is receiving events",
+ .user_print = do_info_mice_print,
+ .mhandler.info_new = do_info_mice,
+ },
+ {
+ .name = "vnc",
+ .args_type = "",
+ .params = "",
+ .help = "show the vnc server status",
+ .user_print = do_info_vnc_print,
+ .mhandler.info_new = do_info_vnc,
+ },
+#if defined(CONFIG_SPICE)
+ {
+ .name = "spice",
+ .args_type = "",
+ .params = "",
+ .help = "show the spice server status",
+ .user_print = do_info_spice_print,
+ .mhandler.info_new = do_info_spice,
+ },
+#endif
+ {
+ .name = "name",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM name",
+ .user_print = do_info_name_print,
+ .mhandler.info_new = do_info_name,
+ },
+ {
+ .name = "uuid",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM UUID",
+ .user_print = do_info_uuid_print,
+ .mhandler.info_new = do_info_uuid,
+ },
+#if defined(TARGET_PPC)
+ {
+ .name = "cpustats",
+ .args_type = "",
+ .params = "",
+ .help = "show CPU statistics",
+ .mhandler.info = do_info_cpu_stats,
+ },
+#endif
+#if defined(CONFIG_SLIRP)
+ {
+ .name = "usernet",
+ .args_type = "",
+ .params = "",
+ .help = "show user network stack connection states",
+ .mhandler.info = do_info_usernet,
+ },
+#endif
+ {
+ .name = "migrate",
+ .args_type = "",
+ .params = "",
+ .help = "show migration status",
+ .user_print = do_info_migrate_print,
+ .mhandler.info_new = do_info_migrate,
+ },
+ {
+ .name = "balloon",
+ .args_type = "",
+ .params = "",
+ .help = "show balloon information",
+ .user_print = monitor_print_balloon,
+ .mhandler.info_async = do_info_balloon,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+ {
+ .name = "qtree",
+ .args_type = "",
+ .params = "",
+ .help = "show device tree",
+ .mhandler.info = do_info_qtree,
+ },
+ {
+ .name = "qdm",
+ .args_type = "",
+ .params = "",
+ .help = "show qdev device model list",
+ .mhandler.info = do_info_qdm,
+ },
+ {
+ .name = "roms",
+ .args_type = "",
+ .params = "",
+ .help = "show roms",
+ .mhandler.info = do_info_roms,
+ },
+#if defined(CONFIG_SIMPLE_TRACE)
+ {
+ .name = "trace",
+ .args_type = "",
+ .params = "",
+ .help = "show current contents of trace buffer",
+ .mhandler.info = do_info_trace,
+ },
+ {
+ .name = "trace-events",
+ .args_type = "",
+ .params = "",
+ .help = "show available trace-events & their state",
+ .mhandler.info = do_info_trace_events,
+ },
+#endif
+ {
+ .name = NULL,
+ },
+};
+
+static const mon_cmd_t qmp_cmds[] = {
+#include "qmp-commands.h"
+ { /* NULL */ },
+};
+
+static const mon_cmd_t qmp_query_cmds[] = {
+ {
+ .name = "version",
+ .args_type = "",
+ .params = "",
+ .help = "show the version of QEMU",
+ .user_print = do_info_version_print,
+ .mhandler.info_new = do_info_version,
+ },
+ {
+ .name = "commands",
+ .args_type = "",
+ .params = "",
+ .help = "list QMP available commands",
+ .user_print = monitor_user_noop,
+ .mhandler.info_new = do_info_commands,
+ },
+ {
+ .name = "chardev",
+ .args_type = "",
+ .params = "",
+ .help = "show the character devices",
+ .user_print = qemu_chr_info_print,
+ .mhandler.info_new = qemu_chr_info,
+ },
+ {
+ .name = "block",
+ .args_type = "",
+ .params = "",
+ .help = "show the block devices",
+ .user_print = bdrv_info_print,
+ .mhandler.info_new = bdrv_info,
+ },
+ {
+ .name = "blockstats",
+ .args_type = "",
+ .params = "",
+ .help = "show block device statistics",
+ .user_print = bdrv_stats_print,
+ .mhandler.info_new = bdrv_info_stats,
+ },
+ {
+ .name = "cpus",
+ .args_type = "",
+ .params = "",
+ .help = "show infos for each CPU",
+ .user_print = monitor_print_cpus,
+ .mhandler.info_new = do_info_cpus,
+ },
+ {
+ .name = "pci",
+ .args_type = "",
+ .params = "",
+ .help = "show PCI info",
+ .user_print = do_pci_info_print,
+ .mhandler.info_new = do_pci_info,
+ },
+ {
+ .name = "kvm",
+ .args_type = "",
+ .params = "",
+ .help = "show KVM information",
+ .user_print = do_info_kvm_print,
+ .mhandler.info_new = do_info_kvm,
+ },
+ {
+ .name = "status",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM status (running|paused)",
+ .user_print = do_info_status_print,
+ .mhandler.info_new = do_info_status,
+ },
+ {
+ .name = "mice",
+ .args_type = "",
+ .params = "",
+ .help = "show which guest mouse is receiving events",
+ .user_print = do_info_mice_print,
+ .mhandler.info_new = do_info_mice,
+ },
+ {
+ .name = "vnc",
+ .args_type = "",
+ .params = "",
+ .help = "show the vnc server status",
+ .user_print = do_info_vnc_print,
+ .mhandler.info_new = do_info_vnc,
+ },
+#if defined(CONFIG_SPICE)
+ {
+ .name = "spice",
+ .args_type = "",
+ .params = "",
+ .help = "show the spice server status",
+ .user_print = do_info_spice_print,
+ .mhandler.info_new = do_info_spice,
+ },
+#endif
+ {
+ .name = "name",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM name",
+ .user_print = do_info_name_print,
+ .mhandler.info_new = do_info_name,
+ },
+ {
+ .name = "uuid",
+ .args_type = "",
+ .params = "",
+ .help = "show the current VM UUID",
+ .user_print = do_info_uuid_print,
+ .mhandler.info_new = do_info_uuid,
+ },
+ {
+ .name = "migrate",
+ .args_type = "",
+ .params = "",
+ .help = "show migration status",
+ .user_print = do_info_migrate_print,
+ .mhandler.info_new = do_info_migrate,
+ },
+ {
+ .name = "balloon",
+ .args_type = "",
+ .params = "",
+ .help = "show balloon information",
+ .user_print = monitor_print_balloon,
+ .mhandler.info_async = do_info_balloon,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+ { /* NULL */ },
+};
+
+/*******************************************************************/
+
+static const char *pch;
+static jmp_buf expr_env;
+
+#define MD_TLONG 0
+#define MD_I32 1
+
+typedef struct MonitorDef {
+ const char *name;
+ int offset;
+ target_long (*get_value)(const struct MonitorDef *md, int val);
+ int type;
+} MonitorDef;
+
+#if defined(TARGET_I386)
+static target_long monitor_get_pc (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return env->eip + env->segs[R_CS].base;
+}
+#endif
+
+#if defined(TARGET_PPC)
+static target_long monitor_get_ccr (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ unsigned int u;
+ int i;
+
+ u = 0;
+ for (i = 0; i < 8; i++)
+ u |= env->crf[i] << (32 - (4 * i));
+
+ return u;
+}
+
+static target_long monitor_get_msr (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return env->msr;
+}
+
+static target_long monitor_get_xer (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return env->xer;
+}
+
+static target_long monitor_get_decr (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return cpu_ppc_load_decr(env);
+}
+
+static target_long monitor_get_tbu (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return cpu_ppc_load_tbu(env);
+}
+
+static target_long monitor_get_tbl (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return cpu_ppc_load_tbl(env);
+}
+#endif
+
+#if defined(TARGET_SPARC)
+#ifndef TARGET_SPARC64
+static target_long monitor_get_psr (const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+
+ return cpu_get_psr(env);
+}
+#endif
+
+static target_long monitor_get_reg(const struct MonitorDef *md, int val)
+{
+ CPUState *env = mon_get_cpu();
+ return env->regwptr[val];
+}
+#endif
+
+static const MonitorDef monitor_defs[] = {
+#ifdef TARGET_I386
+
+#define SEG(name, seg) \
+ { name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\
+ { name ".base", offsetof(CPUState, segs[seg].base) },\
+ { name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 },
+
+ { "eax", offsetof(CPUState, regs[0]) },
+ { "ecx", offsetof(CPUState, regs[1]) },
+ { "edx", offsetof(CPUState, regs[2]) },
+ { "ebx", offsetof(CPUState, regs[3]) },
+ { "esp|sp", offsetof(CPUState, regs[4]) },
+ { "ebp|fp", offsetof(CPUState, regs[5]) },
+ { "esi", offsetof(CPUState, regs[6]) },
+ { "edi", offsetof(CPUState, regs[7]) },
+#ifdef TARGET_X86_64
+ { "r8", offsetof(CPUState, regs[8]) },
+ { "r9", offsetof(CPUState, regs[9]) },
+ { "r10", offsetof(CPUState, regs[10]) },
+ { "r11", offsetof(CPUState, regs[11]) },
+ { "r12", offsetof(CPUState, regs[12]) },
+ { "r13", offsetof(CPUState, regs[13]) },
+ { "r14", offsetof(CPUState, regs[14]) },
+ { "r15", offsetof(CPUState, regs[15]) },
+#endif
+ { "eflags", offsetof(CPUState, eflags) },
+ { "eip", offsetof(CPUState, eip) },
+ SEG("cs", R_CS)
+ SEG("ds", R_DS)
+ SEG("es", R_ES)
+ SEG("ss", R_SS)
+ SEG("fs", R_FS)
+ SEG("gs", R_GS)
+ { "pc", 0, monitor_get_pc, },
+#elif defined(TARGET_PPC)
+ /* General purpose registers */
+ { "r0", offsetof(CPUState, gpr[0]) },
+ { "r1", offsetof(CPUState, gpr[1]) },
+ { "r2", offsetof(CPUState, gpr[2]) },
+ { "r3", offsetof(CPUState, gpr[3]) },
+ { "r4", offsetof(CPUState, gpr[4]) },
+ { "r5", offsetof(CPUState, gpr[5]) },
+ { "r6", offsetof(CPUState, gpr[6]) },
+ { "r7", offsetof(CPUState, gpr[7]) },
+ { "r8", offsetof(CPUState, gpr[8]) },
+ { "r9", offsetof(CPUState, gpr[9]) },
+ { "r10", offsetof(CPUState, gpr[10]) },
+ { "r11", offsetof(CPUState, gpr[11]) },
+ { "r12", offsetof(CPUState, gpr[12]) },
+ { "r13", offsetof(CPUState, gpr[13]) },
+ { "r14", offsetof(CPUState, gpr[14]) },
+ { "r15", offsetof(CPUState, gpr[15]) },
+ { "r16", offsetof(CPUState, gpr[16]) },
+ { "r17", offsetof(CPUState, gpr[17]) },
+ { "r18", offsetof(CPUState, gpr[18]) },
+ { "r19", offsetof(CPUState, gpr[19]) },
+ { "r20", offsetof(CPUState, gpr[20]) },
+ { "r21", offsetof(CPUState, gpr[21]) },
+ { "r22", offsetof(CPUState, gpr[22]) },
+ { "r23", offsetof(CPUState, gpr[23]) },
+ { "r24", offsetof(CPUState, gpr[24]) },
+ { "r25", offsetof(CPUState, gpr[25]) },
+ { "r26", offsetof(CPUState, gpr[26]) },
+ { "r27", offsetof(CPUState, gpr[27]) },
+ { "r28", offsetof(CPUState, gpr[28]) },
+ { "r29", offsetof(CPUState, gpr[29]) },
+ { "r30", offsetof(CPUState, gpr[30]) },
+ { "r31", offsetof(CPUState, gpr[31]) },
+ /* Floating point registers */
+ { "f0", offsetof(CPUState, fpr[0]) },
+ { "f1", offsetof(CPUState, fpr[1]) },
+ { "f2", offsetof(CPUState, fpr[2]) },
+ { "f3", offsetof(CPUState, fpr[3]) },
+ { "f4", offsetof(CPUState, fpr[4]) },
+ { "f5", offsetof(CPUState, fpr[5]) },
+ { "f6", offsetof(CPUState, fpr[6]) },
+ { "f7", offsetof(CPUState, fpr[7]) },
+ { "f8", offsetof(CPUState, fpr[8]) },
+ { "f9", offsetof(CPUState, fpr[9]) },
+ { "f10", offsetof(CPUState, fpr[10]) },
+ { "f11", offsetof(CPUState, fpr[11]) },
+ { "f12", offsetof(CPUState, fpr[12]) },
+ { "f13", offsetof(CPUState, fpr[13]) },
+ { "f14", offsetof(CPUState, fpr[14]) },
+ { "f15", offsetof(CPUState, fpr[15]) },
+ { "f16", offsetof(CPUState, fpr[16]) },
+ { "f17", offsetof(CPUState, fpr[17]) },
+ { "f18", offsetof(CPUState, fpr[18]) },
+ { "f19", offsetof(CPUState, fpr[19]) },
+ { "f20", offsetof(CPUState, fpr[20]) },
+ { "f21", offsetof(CPUState, fpr[21]) },
+ { "f22", offsetof(CPUState, fpr[22]) },
+ { "f23", offsetof(CPUState, fpr[23]) },
+ { "f24", offsetof(CPUState, fpr[24]) },
+ { "f25", offsetof(CPUState, fpr[25]) },
+ { "f26", offsetof(CPUState, fpr[26]) },
+ { "f27", offsetof(CPUState, fpr[27]) },
+ { "f28", offsetof(CPUState, fpr[28]) },
+ { "f29", offsetof(CPUState, fpr[29]) },
+ { "f30", offsetof(CPUState, fpr[30]) },
+ { "f31", offsetof(CPUState, fpr[31]) },
+ { "fpscr", offsetof(CPUState, fpscr) },
+ /* Next instruction pointer */
+ { "nip|pc", offsetof(CPUState, nip) },
+ { "lr", offsetof(CPUState, lr) },
+ { "ctr", offsetof(CPUState, ctr) },
+ { "decr", 0, &monitor_get_decr, },
+ { "ccr", 0, &monitor_get_ccr, },
+ /* Machine state register */
+ { "msr", 0, &monitor_get_msr, },
+ { "xer", 0, &monitor_get_xer, },
+ { "tbu", 0, &monitor_get_tbu, },
+ { "tbl", 0, &monitor_get_tbl, },
+#if defined(TARGET_PPC64)
+ /* Address space register */
+ { "asr", offsetof(CPUState, asr) },
+#endif
+ /* Segment registers */
+ { "sdr1", offsetof(CPUState, sdr1) },
+ { "sr0", offsetof(CPUState, sr[0]) },
+ { "sr1", offsetof(CPUState, sr[1]) },
+ { "sr2", offsetof(CPUState, sr[2]) },
+ { "sr3", offsetof(CPUState, sr[3]) },
+ { "sr4", offsetof(CPUState, sr[4]) },
+ { "sr5", offsetof(CPUState, sr[5]) },
+ { "sr6", offsetof(CPUState, sr[6]) },
+ { "sr7", offsetof(CPUState, sr[7]) },
+ { "sr8", offsetof(CPUState, sr[8]) },
+ { "sr9", offsetof(CPUState, sr[9]) },
+ { "sr10", offsetof(CPUState, sr[10]) },
+ { "sr11", offsetof(CPUState, sr[11]) },
+ { "sr12", offsetof(CPUState, sr[12]) },
+ { "sr13", offsetof(CPUState, sr[13]) },
+ { "sr14", offsetof(CPUState, sr[14]) },
+ { "sr15", offsetof(CPUState, sr[15]) },
+ /* Too lazy to put BATs and SPRs ... */
+#elif defined(TARGET_SPARC)
+ { "g0", offsetof(CPUState, gregs[0]) },
+ { "g1", offsetof(CPUState, gregs[1]) },
+ { "g2", offsetof(CPUState, gregs[2]) },
+ { "g3", offsetof(CPUState, gregs[3]) },
+ { "g4", offsetof(CPUState, gregs[4]) },
+ { "g5", offsetof(CPUState, gregs[5]) },
+ { "g6", offsetof(CPUState, gregs[6]) },
+ { "g7", offsetof(CPUState, gregs[7]) },
+ { "o0", 0, monitor_get_reg },
+ { "o1", 1, monitor_get_reg },
+ { "o2", 2, monitor_get_reg },
+ { "o3", 3, monitor_get_reg },
+ { "o4", 4, monitor_get_reg },
+ { "o5", 5, monitor_get_reg },
+ { "o6", 6, monitor_get_reg },
+ { "o7", 7, monitor_get_reg },
+ { "l0", 8, monitor_get_reg },
+ { "l1", 9, monitor_get_reg },
+ { "l2", 10, monitor_get_reg },
+ { "l3", 11, monitor_get_reg },
+ { "l4", 12, monitor_get_reg },
+ { "l5", 13, monitor_get_reg },
+ { "l6", 14, monitor_get_reg },
+ { "l7", 15, monitor_get_reg },
+ { "i0", 16, monitor_get_reg },
+ { "i1", 17, monitor_get_reg },
+ { "i2", 18, monitor_get_reg },
+ { "i3", 19, monitor_get_reg },
+ { "i4", 20, monitor_get_reg },
+ { "i5", 21, monitor_get_reg },
+ { "i6", 22, monitor_get_reg },
+ { "i7", 23, monitor_get_reg },
+ { "pc", offsetof(CPUState, pc) },
+ { "npc", offsetof(CPUState, npc) },
+ { "y", offsetof(CPUState, y) },
+#ifndef TARGET_SPARC64
+ { "psr", 0, &monitor_get_psr, },
+ { "wim", offsetof(CPUState, wim) },
+#endif
+ { "tbr", offsetof(CPUState, tbr) },
+ { "fsr", offsetof(CPUState, fsr) },
+ { "f0", offsetof(CPUState, fpr[0]) },
+ { "f1", offsetof(CPUState, fpr[1]) },
+ { "f2", offsetof(CPUState, fpr[2]) },
+ { "f3", offsetof(CPUState, fpr[3]) },
+ { "f4", offsetof(CPUState, fpr[4]) },
+ { "f5", offsetof(CPUState, fpr[5]) },
+ { "f6", offsetof(CPUState, fpr[6]) },
+ { "f7", offsetof(CPUState, fpr[7]) },
+ { "f8", offsetof(CPUState, fpr[8]) },
+ { "f9", offsetof(CPUState, fpr[9]) },
+ { "f10", offsetof(CPUState, fpr[10]) },
+ { "f11", offsetof(CPUState, fpr[11]) },
+ { "f12", offsetof(CPUState, fpr[12]) },
+ { "f13", offsetof(CPUState, fpr[13]) },
+ { "f14", offsetof(CPUState, fpr[14]) },
+ { "f15", offsetof(CPUState, fpr[15]) },
+ { "f16", offsetof(CPUState, fpr[16]) },
+ { "f17", offsetof(CPUState, fpr[17]) },
+ { "f18", offsetof(CPUState, fpr[18]) },
+ { "f19", offsetof(CPUState, fpr[19]) },
+ { "f20", offsetof(CPUState, fpr[20]) },
+ { "f21", offsetof(CPUState, fpr[21]) },
+ { "f22", offsetof(CPUState, fpr[22]) },
+ { "f23", offsetof(CPUState, fpr[23]) },
+ { "f24", offsetof(CPUState, fpr[24]) },
+ { "f25", offsetof(CPUState, fpr[25]) },
+ { "f26", offsetof(CPUState, fpr[26]) },
+ { "f27", offsetof(CPUState, fpr[27]) },
+ { "f28", offsetof(CPUState, fpr[28]) },
+ { "f29", offsetof(CPUState, fpr[29]) },
+ { "f30", offsetof(CPUState, fpr[30]) },
+ { "f31", offsetof(CPUState, fpr[31]) },
+#ifdef TARGET_SPARC64
+ { "f32", offsetof(CPUState, fpr[32]) },
+ { "f34", offsetof(CPUState, fpr[34]) },
+ { "f36", offsetof(CPUState, fpr[36]) },
+ { "f38", offsetof(CPUState, fpr[38]) },
+ { "f40", offsetof(CPUState, fpr[40]) },
+ { "f42", offsetof(CPUState, fpr[42]) },
+ { "f44", offsetof(CPUState, fpr[44]) },
+ { "f46", offsetof(CPUState, fpr[46]) },
+ { "f48", offsetof(CPUState, fpr[48]) },
+ { "f50", offsetof(CPUState, fpr[50]) },
+ { "f52", offsetof(CPUState, fpr[52]) },
+ { "f54", offsetof(CPUState, fpr[54]) },
+ { "f56", offsetof(CPUState, fpr[56]) },
+ { "f58", offsetof(CPUState, fpr[58]) },
+ { "f60", offsetof(CPUState, fpr[60]) },
+ { "f62", offsetof(CPUState, fpr[62]) },
+ { "asi", offsetof(CPUState, asi) },
+ { "pstate", offsetof(CPUState, pstate) },
+ { "cansave", offsetof(CPUState, cansave) },
+ { "canrestore", offsetof(CPUState, canrestore) },
+ { "otherwin", offsetof(CPUState, otherwin) },
+ { "wstate", offsetof(CPUState, wstate) },
+ { "cleanwin", offsetof(CPUState, cleanwin) },
+ { "fprs", offsetof(CPUState, fprs) },
+#endif
+#endif
+ { NULL },
+};
+
+static void expr_error(Monitor *mon, const char *msg)
+{
+ monitor_printf(mon, "%s\n", msg);
+ longjmp(expr_env, 1);
+}
+
+/* return 0 if OK, -1 if not found */
+static int get_monitor_def(target_long *pval, const char *name)
+{
+ const MonitorDef *md;
+ void *ptr;
+
+ for(md = monitor_defs; md->name != NULL; md++) {
+ if (compare_cmd(name, md->name)) {
+ if (md->get_value) {
+ *pval = md->get_value(md, md->offset);
+ } else {
+ CPUState *env = mon_get_cpu();
+ ptr = (uint8_t *)env + md->offset;
+ switch(md->type) {
+ case MD_I32:
+ *pval = *(int32_t *)ptr;
+ break;
+ case MD_TLONG:
+ *pval = *(target_long *)ptr;
+ break;
+ default:
+ *pval = 0;
+ break;
+ }
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void next(void)
+{
+ if (*pch != '\0') {
+ pch++;
+ while (qemu_isspace(*pch))
+ pch++;
+ }
+}
+
+static int64_t expr_sum(Monitor *mon);
+
+static int64_t expr_unary(Monitor *mon)
+{
+ int64_t n;
+ char *p;
+ int ret;
+
+ switch(*pch) {
+ case '+':
+ next();
+ n = expr_unary(mon);
+ break;
+ case '-':
+ next();
+ n = -expr_unary(mon);
+ break;
+ case '~':
+ next();
+ n = ~expr_unary(mon);
+ break;
+ case '(':
+ next();
+ n = expr_sum(mon);
+ if (*pch != ')') {
+ expr_error(mon, "')' expected");
+ }
+ next();
+ break;
+ case '\'':
+ pch++;
+ if (*pch == '\0')
+ expr_error(mon, "character constant expected");
+ n = *pch;
+ pch++;
+ if (*pch != '\'')
+ expr_error(mon, "missing terminating \' character");
+ next();
+ break;
+ case '$':
+ {
+ char buf[128], *q;
+ target_long reg=0;
+
+ pch++;
+ q = buf;
+ while ((*pch >= 'a' && *pch <= 'z') ||
+ (*pch >= 'A' && *pch <= 'Z') ||
+ (*pch >= '0' && *pch <= '9') ||
+ *pch == '_' || *pch == '.') {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = *pch;
+ pch++;
+ }
+ while (qemu_isspace(*pch))
+ pch++;
+ *q = 0;
+ ret = get_monitor_def(&reg, buf);
+ if (ret < 0)
+ expr_error(mon, "unknown register");
+ n = reg;
+ }
+ break;
+ case '\0':
+ expr_error(mon, "unexpected end of expression");
+ n = 0;
+ break;
+ default:
+#if TARGET_PHYS_ADDR_BITS > 32
+ n = strtoull(pch, &p, 0);
+#else
+ n = strtoul(pch, &p, 0);
+#endif
+ if (pch == p) {
+ expr_error(mon, "invalid char in expression");
+ }
+ pch = p;
+ while (qemu_isspace(*pch))
+ pch++;
+ break;
+ }
+ return n;
+}
+
+
+static int64_t expr_prod(Monitor *mon)
+{
+ int64_t val, val2;
+ int op;
+
+ val = expr_unary(mon);
+ for(;;) {
+ op = *pch;
+ if (op != '*' && op != '/' && op != '%')
+ break;
+ next();
+ val2 = expr_unary(mon);
+ switch(op) {
+ default:
+ case '*':
+ val *= val2;
+ break;
+ case '/':
+ case '%':
+ if (val2 == 0)
+ expr_error(mon, "division by zero");
+ if (op == '/')
+ val /= val2;
+ else
+ val %= val2;
+ break;
+ }
+ }
+ return val;
+}
+
+static int64_t expr_logic(Monitor *mon)
+{
+ int64_t val, val2;
+ int op;
+
+ val = expr_prod(mon);
+ for(;;) {
+ op = *pch;
+ if (op != '&' && op != '|' && op != '^')
+ break;
+ next();
+ val2 = expr_prod(mon);
+ switch(op) {
+ default:
+ case '&':
+ val &= val2;
+ break;
+ case '|':
+ val |= val2;
+ break;
+ case '^':
+ val ^= val2;
+ break;
+ }
+ }
+ return val;
+}
+
+static int64_t expr_sum(Monitor *mon)
+{
+ int64_t val, val2;
+ int op;
+
+ val = expr_logic(mon);
+ for(;;) {
+ op = *pch;
+ if (op != '+' && op != '-')
+ break;
+ next();
+ val2 = expr_logic(mon);
+ if (op == '+')
+ val += val2;
+ else
+ val -= val2;
+ }
+ return val;
+}
+
+static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
+{
+ pch = *pp;
+ if (setjmp(expr_env)) {
+ *pp = pch;
+ return -1;
+ }
+ while (qemu_isspace(*pch))
+ pch++;
+ *pval = expr_sum(mon);
+ *pp = pch;
+ return 0;
+}
+
+static int get_double(Monitor *mon, double *pval, const char **pp)
+{
+ const char *p = *pp;
+ char *tailp;
+ double d;
+
+ d = strtod(p, &tailp);
+ if (tailp == p) {
+ monitor_printf(mon, "Number expected\n");
+ return -1;
+ }
+ if (d != d || d - d != 0) {
+ /* NaN or infinity */
+ monitor_printf(mon, "Bad number\n");
+ return -1;
+ }
+ *pval = d;
+ *pp = tailp;
+ return 0;
+}
+
+static int get_str(char *buf, int buf_size, const char **pp)
+{
+ const char *p;
+ char *q;
+ int c;
+
+ q = buf;
+ p = *pp;
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '\0') {
+ fail:
+ *q = '\0';
+ *pp = p;
+ return -1;
+ }
+ if (*p == '\"') {
+ p++;
+ while (*p != '\0' && *p != '\"') {
+ if (*p == '\\') {
+ p++;
+ c = *p++;
+ switch(c) {
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case '\\':
+ case '\'':
+ case '\"':
+ break;
+ default:
+ qemu_printf("unsupported escape code: '\\%c'\n", c);
+ goto fail;
+ }
+ if ((q - buf) < buf_size - 1) {
+ *q++ = c;
+ }
+ } else {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ if (*p != '\"') {
+ qemu_printf("unterminated string\n");
+ goto fail;
+ }
+ p++;
+ } else {
+ while (*p != '\0' && !qemu_isspace(*p)) {
+ if ((q - buf) < buf_size - 1) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ }
+ *q = '\0';
+ *pp = p;
+ return 0;
+}
+
+/*
+ * Store the command-name in cmdname, and return a pointer to
+ * the remaining of the command string.
+ */
+static const char *get_command_name(const char *cmdline,
+ char *cmdname, size_t nlen)
+{
+ size_t len;
+ const char *p, *pstart;
+
+ p = cmdline;
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '\0')
+ return NULL;
+ pstart = p;
+ while (*p != '\0' && *p != '/' && !qemu_isspace(*p))
+ p++;
+ len = p - pstart;
+ if (len > nlen - 1)
+ len = nlen - 1;
+ memcpy(cmdname, pstart, len);
+ cmdname[len] = '\0';
+ return p;
+}
+
+/**
+ * Read key of 'type' into 'key' and return the current
+ * 'type' pointer.
+ */
+static char *key_get_info(const char *type, char **key)
+{
+ size_t len;
+ char *p, *str;
+
+ if (*type == ',')
+ type++;
+
+ p = strchr(type, ':');
+ if (!p) {
+ *key = NULL;
+ return NULL;
+ }
+ len = p - type;
+
+ str = qemu_malloc(len + 1);
+ memcpy(str, type, len);
+ str[len] = '\0';
+
+ *key = str;
+ return ++p;
+}
+
+static int default_fmt_format = 'x';
+static int default_fmt_size = 4;
+
+#define MAX_ARGS 16
+
+static int is_valid_option(const char *c, const char *typestr)
+{
+ char option[3];
+
+ option[0] = '-';
+ option[1] = *c;
+ option[2] = '\0';
+
+ typestr = strstr(typestr, option);
+ return (typestr != NULL);
+}
+
+static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table,
+ const char *cmdname)
+{
+ const mon_cmd_t *cmd;
+
+ for (cmd = disp_table; cmd->name != NULL; cmd++) {
+ if (compare_cmd(cmdname, cmd->name)) {
+ return cmd;
+ }
+ }
+
+ return NULL;
+}
+
+static const mon_cmd_t *monitor_find_command(const char *cmdname)
+{
+ return search_dispatch_table(mon_cmds, cmdname);
+}
+
+static const mon_cmd_t *qmp_find_query_cmd(const char *info_item)
+{
+ return search_dispatch_table(qmp_query_cmds, info_item);
+}
+
+static const mon_cmd_t *qmp_find_cmd(const char *cmdname)
+{
+ return search_dispatch_table(qmp_cmds, cmdname);
+}
+
+static const mon_cmd_t *monitor_parse_command(Monitor *mon,
+ const char *cmdline,
+ QDict *qdict)
+{
+ const char *p, *typestr;
+ int c;
+ const mon_cmd_t *cmd;
+ char cmdname[256];
+ char buf[1024];
+ char *key;
+
+#ifdef DEBUG
+ monitor_printf(mon, "command='%s'\n", cmdline);
+#endif
+
+ /* extract the command name */
+ p = get_command_name(cmdline, cmdname, sizeof(cmdname));
+ if (!p)
+ return NULL;
+
+ cmd = monitor_find_command(cmdname);
+ if (!cmd) {
+ monitor_printf(mon, "unknown command: '%s'\n", cmdname);
+ return NULL;
+ }
+
+ /* parse the parameters */
+ typestr = cmd->args_type;
+ for(;;) {
+ typestr = key_get_info(typestr, &key);
+ if (!typestr)
+ break;
+ c = *typestr;
+ typestr++;
+ switch(c) {
+ case 'F':
+ case 'B':
+ case 's':
+ {
+ int ret;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ /* no optional string: NULL argument */
+ break;
+ }
+ }
+ ret = get_str(buf, sizeof(buf), &p);
+ if (ret < 0) {
+ switch(c) {
+ case 'F':
+ monitor_printf(mon, "%s: filename expected\n",
+ cmdname);
+ break;
+ case 'B':
+ monitor_printf(mon, "%s: block device name expected\n",
+ cmdname);
+ break;
+ default:
+ monitor_printf(mon, "%s: string expected\n", cmdname);
+ break;
+ }
+ goto fail;
+ }
+ qdict_put(qdict, key, qstring_from_str(buf));
+ }
+ break;
+ case 'O':
+ {
+ QemuOptsList *opts_list;
+ QemuOpts *opts;
+
+ opts_list = qemu_find_opts(key);
+ if (!opts_list || opts_list->desc->name) {
+ goto bad_type;
+ }
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (!*p)
+ break;
+ if (get_str(buf, sizeof(buf), &p) < 0) {
+ goto fail;
+ }
+ opts = qemu_opts_parse(opts_list, buf, 1);
+ if (!opts) {
+ goto fail;
+ }
+ qemu_opts_to_qdict(opts, qdict);
+ qemu_opts_del(opts);
+ }
+ break;
+ case '/':
+ {
+ int count, format, size;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '/') {
+ /* format found */
+ p++;
+ count = 1;
+ if (qemu_isdigit(*p)) {
+ count = 0;
+ while (qemu_isdigit(*p)) {
+ count = count * 10 + (*p - '0');
+ p++;
+ }
+ }
+ size = -1;
+ format = -1;
+ for(;;) {
+ switch(*p) {
+ case 'o':
+ case 'd':
+ case 'u':
+ case 'x':
+ case 'i':
+ case 'c':
+ format = *p++;
+ break;
+ case 'b':
+ size = 1;
+ p++;
+ break;
+ case 'h':
+ size = 2;
+ p++;
+ break;
+ case 'w':
+ size = 4;
+ p++;
+ break;
+ case 'g':
+ case 'L':
+ size = 8;
+ p++;
+ break;
+ default:
+ goto next;
+ }
+ }
+ next:
+ if (*p != '\0' && !qemu_isspace(*p)) {
+ monitor_printf(mon, "invalid char in format: '%c'\n",
+ *p);
+ goto fail;
+ }
+ if (format < 0)
+ format = default_fmt_format;
+ if (format != 'i') {
+ /* for 'i', not specifying a size gives -1 as size */
+ if (size < 0)
+ size = default_fmt_size;
+ default_fmt_size = size;
+ }
+ default_fmt_format = format;
+ } else {
+ count = 1;
+ format = default_fmt_format;
+ if (format != 'i') {
+ size = default_fmt_size;
+ } else {
+ size = -1;
+ }
+ }
+ qdict_put(qdict, "count", qint_from_int(count));
+ qdict_put(qdict, "format", qint_from_int(format));
+ qdict_put(qdict, "size", qint_from_int(size));
+ }
+ break;
+ case 'i':
+ case 'l':
+ case 'M':
+ {
+ int64_t val;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*typestr == '?' || *typestr == '.') {
+ if (*typestr == '?') {
+ if (*p == '\0') {
+ typestr++;
+ break;
+ }
+ } else {
+ if (*p == '.') {
+ p++;
+ while (qemu_isspace(*p))
+ p++;
+ } else {
+ typestr++;
+ break;
+ }
+ }
+ typestr++;
+ }
+ if (get_expr(mon, &val, &p))
+ goto fail;
+ /* Check if 'i' is greater than 32-bit */
+ if ((c == 'i') && ((val >> 32) & 0xffffffff)) {
+ monitor_printf(mon, "\'%s\' has failed: ", cmdname);
+ monitor_printf(mon, "integer is for 32-bit values\n");
+ goto fail;
+ } else if (c == 'M') {
+ val <<= 20;
+ }
+ qdict_put(qdict, key, qint_from_int(val));
+ }
+ break;
+ case 'o':
+ {
+ int64_t val;
+ char *end;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ break;
+ }
+ }
+ val = strtosz(p, &end);
+ if (val < 0) {
+ monitor_printf(mon, "invalid size\n");
+ goto fail;
+ }
+ qdict_put(qdict, key, qint_from_int(val));
+ p = end;
+ }
+ break;
+ case 'T':
+ {
+ double val;
+
+ while (qemu_isspace(*p))
+ p++;
+ if (*typestr == '?') {
+ typestr++;
+ if (*p == '\0') {
+ break;
+ }
+ }
+ if (get_double(mon, &val, &p) < 0) {
+ goto fail;
+ }
+ if (p[0] && p[1] == 's') {
+ switch (*p) {
+ case 'm':
+ val /= 1e3; p += 2; break;
+ case 'u':
+ val /= 1e6; p += 2; break;
+ case 'n':
+ val /= 1e9; p += 2; break;
+ }
+ }
+ if (*p && !qemu_isspace(*p)) {
+ monitor_printf(mon, "Unknown unit suffix\n");
+ goto fail;
+ }
+ qdict_put(qdict, key, qfloat_from_double(val));
+ }
+ break;
+ case 'b':
+ {
+ const char *beg;
+ int val;
+
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ beg = p;
+ while (qemu_isgraph(*p)) {
+ p++;
+ }
+ if (p - beg == 2 && !memcmp(beg, "on", p - beg)) {
+ val = 1;
+ } else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) {
+ val = 0;
+ } else {
+ monitor_printf(mon, "Expected 'on' or 'off'\n");
+ goto fail;
+ }
+ qdict_put(qdict, key, qbool_from_int(val));
+ }
+ break;
+ case '-':
+ {
+ const char *tmp = p;
+ int skip_key = 0;
+ /* option */
+
+ c = *typestr++;
+ if (c == '\0')
+ goto bad_type;
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '-') {
+ p++;
+ if(c != *p) {
+ if(!is_valid_option(p, typestr)) {
+
+ monitor_printf(mon, "%s: unsupported option -%c\n",
+ cmdname, *p);
+ goto fail;
+ } else {
+ skip_key = 1;
+ }
+ }
+ if(skip_key) {
+ p = tmp;
+ } else {
+ /* has option */
+ p++;
+ qdict_put(qdict, key, qbool_from_int(1));
+ }
+ }
+ }
+ break;
+ default:
+ bad_type:
+ monitor_printf(mon, "%s: unknown type '%c'\n", cmdname, c);
+ goto fail;
+ }
+ qemu_free(key);
+ key = NULL;
+ }
+ /* check that all arguments were parsed */
+ while (qemu_isspace(*p))
+ p++;
+ if (*p != '\0') {
+ monitor_printf(mon, "%s: extraneous characters at the end of line\n",
+ cmdname);
+ goto fail;
+ }
+
+ return cmd;
+
+fail:
+ qemu_free(key);
+ return NULL;
+}
+
+void monitor_set_error(Monitor *mon, QError *qerror)
+{
+ /* report only the first error */
+ if (!mon->error) {
+ mon->error = qerror;
+ } else {
+ MON_DEBUG("Additional error report at %s:%d\n",
+ qerror->file, qerror->linenr);
+ QDECREF(qerror);
+ }
+}
+
+static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret)
+{
+ if (ret && !monitor_has_error(mon)) {
+ /*
+ * If it returns failure, it must have passed on error.
+ *
+ * Action: Report an internal error to the client if in QMP.
+ */
+ qerror_report(QERR_UNDEFINED_ERROR);
+ MON_DEBUG("command '%s' returned failure but did not pass an error\n",
+ cmd->name);
+ }
+
+#ifdef CONFIG_DEBUG_MONITOR
+ if (!ret && monitor_has_error(mon)) {
+ /*
+ * If it returns success, it must not have passed an error.
+ *
+ * Action: Report the passed error to the client.
+ */
+ MON_DEBUG("command '%s' returned success but passed an error\n",
+ cmd->name);
+ }
+
+ if (mon_print_count_get(mon) > 0 && strcmp(cmd->name, "info") != 0) {
+ /*
+ * Handlers should not call Monitor print functions.
+ *
+ * Action: Ignore them in QMP.
+ *
+ * (XXX: we don't check any 'info' or 'query' command here
+ * because the user print function _is_ called by do_info(), hence
+ * we will trigger this check. This problem will go away when we
+ * make 'query' commands real and kill do_info())
+ */
+ MON_DEBUG("command '%s' called print functions %d time(s)\n",
+ cmd->name, mon_print_count_get(mon));
+ }
+#endif
+}
+
+static void handle_user_command(Monitor *mon, const char *cmdline)
+{
+ QDict *qdict;
+ const mon_cmd_t *cmd;
+
+ qdict = qdict_new();
+
+ cmd = monitor_parse_command(mon, cmdline, qdict);
+ if (!cmd)
+ goto out;
+
+ if (handler_is_async(cmd)) {
+ user_async_cmd_handler(mon, cmd, qdict);
+ } else if (handler_is_qobject(cmd)) {
+ QObject *data = NULL;
+
+ /* XXX: ignores the error code */
+ cmd->mhandler.cmd_new(mon, qdict, &data);
+ assert(!monitor_has_error(mon));
+ if (data) {
+ cmd->user_print(mon, data);
+ qobject_decref(data);
+ }
+ } else {
+ cmd->mhandler.cmd(mon, qdict);
+ }
+
+out:
+ QDECREF(qdict);
+}
+
+static void cmd_completion(const char *name, const char *list)
+{
+ const char *p, *pstart;
+ char cmd[128];
+ int len;
+
+ p = list;
+ for(;;) {
+ pstart = p;
+ p = strchr(p, '|');
+ if (!p)
+ p = pstart + strlen(pstart);
+ len = p - pstart;
+ if (len > sizeof(cmd) - 2)
+ len = sizeof(cmd) - 2;
+ memcpy(cmd, pstart, len);
+ cmd[len] = '\0';
+ if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
+ readline_add_completion(cur_mon->rs, cmd);
+ }
+ if (*p == '\0')
+ break;
+ p++;
+ }
+}
+
+static void file_completion(const char *input)
+{
+ DIR *ffs;
+ struct dirent *d;
+ char path[1024];
+ char file[1024], file_prefix[1024];
+ int input_path_len;
+ const char *p;
+
+ p = strrchr(input, '/');
+ if (!p) {
+ input_path_len = 0;
+ pstrcpy(file_prefix, sizeof(file_prefix), input);
+ pstrcpy(path, sizeof(path), ".");
+ } else {
+ input_path_len = p - input + 1;
+ memcpy(path, input, input_path_len);
+ if (input_path_len > sizeof(path) - 1)
+ input_path_len = sizeof(path) - 1;
+ path[input_path_len] = '\0';
+ pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
+ }
+#ifdef DEBUG_COMPLETION
+ monitor_printf(cur_mon, "input='%s' path='%s' prefix='%s'\n",
+ input, path, file_prefix);
+#endif
+ ffs = opendir(path);
+ if (!ffs)
+ return;
+ for(;;) {
+ struct stat sb;
+ d = readdir(ffs);
+ if (!d)
+ break;
+
+ if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
+ continue;
+ }
+
+ if (strstart(d->d_name, file_prefix, NULL)) {
+ memcpy(file, input, input_path_len);
+ if (input_path_len < sizeof(file))
+ pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
+ d->d_name);
+ /* stat the file to find out if it's a directory.
+ * In that case add a slash to speed up typing long paths
+ */
+ stat(file, &sb);
+ if(S_ISDIR(sb.st_mode))
+ pstrcat(file, sizeof(file), "/");
+ readline_add_completion(cur_mon->rs, file);
+ }
+ }
+ closedir(ffs);
+}
+
+static void block_completion_it(void *opaque, BlockDriverState *bs)
+{
+ const char *name = bdrv_get_device_name(bs);
+ const char *input = opaque;
+
+ if (input[0] == '\0' ||
+ !strncmp(name, (char *)input, strlen(input))) {
+ readline_add_completion(cur_mon->rs, name);
+ }
+}
+
+/* NOTE: this parser is an approximate form of the real command parser */
+static void parse_cmdline(const char *cmdline,
+ int *pnb_args, char **args)
+{
+ const char *p;
+ int nb_args, ret;
+ char buf[1024];
+
+ p = cmdline;
+ nb_args = 0;
+ for(;;) {
+ while (qemu_isspace(*p))
+ p++;
+ if (*p == '\0')
+ break;
+ if (nb_args >= MAX_ARGS)
+ break;
+ ret = get_str(buf, sizeof(buf), &p);
+ args[nb_args] = qemu_strdup(buf);
+ nb_args++;
+ if (ret < 0)
+ break;
+ }
+ *pnb_args = nb_args;
+}
+
+static const char *next_arg_type(const char *typestr)
+{
+ const char *p = strchr(typestr, ':');
+ return (p != NULL ? ++p : typestr);
+}
+
+static void monitor_find_completion(const char *cmdline)
+{
+ const char *cmdname;
+ char *args[MAX_ARGS];
+ int nb_args, i, len;
+ const char *ptype, *str;
+ const mon_cmd_t *cmd;
+ const KeyDef *key;
+
+ parse_cmdline(cmdline, &nb_args, args);
+#ifdef DEBUG_COMPLETION
+ for(i = 0; i < nb_args; i++) {
+ monitor_printf(cur_mon, "arg%d = '%s'\n", i, (char *)args[i]);
+ }
+#endif
+
+ /* if the line ends with a space, it means we want to complete the
+ next arg */
+ len = strlen(cmdline);
+ if (len > 0 && qemu_isspace(cmdline[len - 1])) {
+ if (nb_args >= MAX_ARGS) {
+ goto cleanup;
+ }
+ args[nb_args++] = qemu_strdup("");
+ }
+ if (nb_args <= 1) {
+ /* command completion */
+ if (nb_args == 0)
+ cmdname = "";
+ else
+ cmdname = args[0];
+ readline_set_completion_index(cur_mon->rs, strlen(cmdname));
+ for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
+ cmd_completion(cmdname, cmd->name);
+ }
+ } else {
+ /* find the command */
+ for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
+ if (compare_cmd(args[0], cmd->name)) {
+ break;
+ }
+ }
+ if (!cmd->name) {
+ goto cleanup;
+ }
+
+ ptype = next_arg_type(cmd->args_type);
+ for(i = 0; i < nb_args - 2; i++) {
+ if (*ptype != '\0') {
+ ptype = next_arg_type(ptype);
+ while (*ptype == '?')
+ ptype = next_arg_type(ptype);
+ }
+ }
+ str = args[nb_args - 1];
+ if (*ptype == '-' && ptype[1] != '\0') {
+ ptype = next_arg_type(ptype);
+ }
+ switch(*ptype) {
+ case 'F':
+ /* file completion */
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ file_completion(str);
+ break;
+ case 'B':
+ /* block device name completion */
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ bdrv_iterate(block_completion_it, (void *)str);
+ break;
+ case 's':
+ /* XXX: more generic ? */
+ if (!strcmp(cmd->name, "info")) {
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ for(cmd = info_cmds; cmd->name != NULL; cmd++) {
+ cmd_completion(str, cmd->name);
+ }
+ } else if (!strcmp(cmd->name, "sendkey")) {
+ char *sep = strrchr(str, '-');
+ if (sep)
+ str = sep + 1;
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ for(key = key_defs; key->name != NULL; key++) {
+ cmd_completion(str, key->name);
+ }
+ } else if (!strcmp(cmd->name, "help|?")) {
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
+ cmd_completion(str, cmd->name);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+cleanup:
+ for (i = 0; i < nb_args; i++) {
+ qemu_free(args[i]);
+ }
+}
+
+static int monitor_can_read(void *opaque)
+{
+ Monitor *mon = opaque;
+
+ return (mon->suspend_cnt == 0) ? 1 : 0;
+}
+
+static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name)
+{
+ int is_cap = compare_cmd(cmd_name, "qmp_capabilities");
+ return (qmp_cmd_mode(mon) ? is_cap : !is_cap);
+}
+
+/*
+ * Argument validation rules:
+ *
+ * 1. The argument must exist in cmd_args qdict
+ * 2. The argument type must be the expected one
+ *
+ * Special case: If the argument doesn't exist in cmd_args and
+ * the QMP_ACCEPT_UNKNOWNS flag is set, then the
+ * checking is skipped for it.
+ */
+static int check_client_args_type(const QDict *client_args,
+ const QDict *cmd_args, int flags)
+{
+ const QDictEntry *ent;
+
+ for (ent = qdict_first(client_args); ent;ent = qdict_next(client_args,ent)){
+ QObject *obj;
+ QString *arg_type;
+ const QObject *client_arg = qdict_entry_value(ent);
+ const char *client_arg_name = qdict_entry_key(ent);
+
+ obj = qdict_get(cmd_args, client_arg_name);
+ if (!obj) {
+ if (flags & QMP_ACCEPT_UNKNOWNS) {
+ /* handler accepts unknowns */
+ continue;
+ }
+ /* client arg doesn't exist */
+ qerror_report(QERR_INVALID_PARAMETER, client_arg_name);
+ return -1;
+ }
+
+ arg_type = qobject_to_qstring(obj);
+ assert(arg_type != NULL);
+
+ /* check if argument's type is correct */
+ switch (qstring_get_str(arg_type)[0]) {
+ case 'F':
+ case 'B':
+ case 's':
+ if (qobject_type(client_arg) != QTYPE_QSTRING) {
+ qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
+ "string");
+ return -1;
+ }
+ break;
+ case 'i':
+ case 'l':
+ case 'M':
+ case 'o':
+ if (qobject_type(client_arg) != QTYPE_QINT) {
+ qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
+ "int");
+ return -1;
+ }
+ break;
+ case 'T':
+ if (qobject_type(client_arg) != QTYPE_QINT &&
+ qobject_type(client_arg) != QTYPE_QFLOAT) {
+ qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
+ "number");
+ return -1;
+ }
+ break;
+ case 'b':
+ case '-':
+ if (qobject_type(client_arg) != QTYPE_QBOOL) {
+ qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
+ "bool");
+ return -1;
+ }
+ break;
+ case 'O':
+ assert(flags & QMP_ACCEPT_UNKNOWNS);
+ break;
+ case '/':
+ case '.':
+ /*
+ * These types are not supported by QMP and thus are not
+ * handled here. Fall through.
+ */
+ default:
+ abort();
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * - Check if the client has passed all mandatory args
+ * - Set special flags for argument validation
+ */
+static int check_mandatory_args(const QDict *cmd_args,
+ const QDict *client_args, int *flags)
+{
+ const QDictEntry *ent;
+
+ for (ent = qdict_first(cmd_args); ent; ent = qdict_next(cmd_args, ent)) {
+ const char *cmd_arg_name = qdict_entry_key(ent);
+ QString *type = qobject_to_qstring(qdict_entry_value(ent));
+ assert(type != NULL);
+
+ if (qstring_get_str(type)[0] == 'O') {
+ assert((*flags & QMP_ACCEPT_UNKNOWNS) == 0);
+ *flags |= QMP_ACCEPT_UNKNOWNS;
+ } else if (qstring_get_str(type)[0] != '-' &&
+ qstring_get_str(type)[1] != '?' &&
+ !qdict_haskey(client_args, cmd_arg_name)) {
+ qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static QDict *qdict_from_args_type(const char *args_type)
+{
+ int i;
+ QDict *qdict;
+ QString *key, *type, *cur_qs;
+
+ assert(args_type != NULL);
+
+ qdict = qdict_new();
+
+ if (args_type == NULL || args_type[0] == '\0') {
+ /* no args, empty qdict */
+ goto out;
+ }
+
+ key = qstring_new();
+ type = qstring_new();
+
+ cur_qs = key;
+
+ for (i = 0;; i++) {
+ switch (args_type[i]) {
+ case ',':
+ case '\0':
+ qdict_put(qdict, qstring_get_str(key), type);
+ QDECREF(key);
+ if (args_type[i] == '\0') {
+ goto out;
+ }
+ type = qstring_new(); /* qdict has ref */
+ cur_qs = key = qstring_new();
+ break;
+ case ':':
+ cur_qs = type;
+ break;
+ default:
+ qstring_append_chr(cur_qs, args_type[i]);
+ break;
+ }
+ }
+
+out:
+ return qdict;
+}
+
+/*
+ * Client argument checking rules:
+ *
+ * 1. Client must provide all mandatory arguments
+ * 2. Each argument provided by the client must be expected
+ * 3. Each argument provided by the client must have the type expected
+ * by the command
+ */
+static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args)
+{
+ int flags, err;
+ QDict *cmd_args;
+
+ cmd_args = qdict_from_args_type(cmd->args_type);
+
+ flags = 0;
+ err = check_mandatory_args(cmd_args, client_args, &flags);
+ if (err) {
+ goto out;
+ }
+
+ err = check_client_args_type(client_args, cmd_args, flags);
+
+out:
+ QDECREF(cmd_args);
+ return err;
+}
+
+/*
+ * Input object checking rules
+ *
+ * 1. Input object must be a dict
+ * 2. The "execute" key must exist
+ * 3. The "execute" key must be a string
+ * 4. If the "arguments" key exists, it must be a dict
+ * 5. If the "id" key exists, it can be anything (ie. json-value)
+ * 6. Any argument not listed above is considered invalid
+ */
+static QDict *qmp_check_input_obj(QObject *input_obj)
+{
+ const QDictEntry *ent;
+ int has_exec_key = 0;
+ QDict *input_dict;
+
+ if (qobject_type(input_obj) != QTYPE_QDICT) {
+ qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object");
+ return NULL;
+ }
+
+ input_dict = qobject_to_qdict(input_obj);
+
+ for (ent = qdict_first(input_dict); ent; ent = qdict_next(input_dict, ent)){
+ const char *arg_name = qdict_entry_key(ent);
+ const QObject *arg_obj = qdict_entry_value(ent);
+
+ if (!strcmp(arg_name, "execute")) {
+ if (qobject_type(arg_obj) != QTYPE_QSTRING) {
+ qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
+ "string");
+ return NULL;
+ }
+ has_exec_key = 1;
+ } else if (!strcmp(arg_name, "arguments")) {
+ if (qobject_type(arg_obj) != QTYPE_QDICT) {
+ qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments",
+ "object");
+ return NULL;
+ }
+ } else if (!strcmp(arg_name, "id")) {
+ /* FIXME: check duplicated IDs for async commands */
+ } else {
+ qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name);
+ return NULL;
+ }
+ }
+
+ if (!has_exec_key) {
+ qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute");
+ return NULL;
+ }
+
+ return input_dict;
+}
+
+static void qmp_call_query_cmd(Monitor *mon, const mon_cmd_t *cmd)
+{
+ QObject *ret_data = NULL;
+
+ if (handler_is_async(cmd)) {
+ qmp_async_info_handler(mon, cmd);
+ if (monitor_has_error(mon)) {
+ monitor_protocol_emitter(mon, NULL);
+ }
+ } else {
+ cmd->mhandler.info_new(mon, &ret_data);
+ monitor_protocol_emitter(mon, ret_data);
+ qobject_decref(ret_data);
+ }
+}
+
+static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd,
+ const QDict *params)
+{
+ int ret;
+ QObject *data = NULL;
+
+ mon_print_count_init(mon);
+
+ ret = cmd->mhandler.cmd_new(mon, params, &data);
+ handler_audit(mon, cmd, ret);
+ monitor_protocol_emitter(mon, data);
+ qobject_decref(data);
+}
+
+static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
+{
+ int err;
+ QObject *obj;
+ QDict *input, *args;
+ const mon_cmd_t *cmd;
+ Monitor *mon = cur_mon;
+ const char *cmd_name, *query_cmd;
+
+ query_cmd = NULL;
+ args = input = NULL;
+
+ obj = json_parser_parse(tokens, NULL);
+ if (!obj) {
+ // FIXME: should be triggered in json_parser_parse()
+ qerror_report(QERR_JSON_PARSING);
+ goto err_out;
+ }
+
+ input = qmp_check_input_obj(obj);
+ if (!input) {
+ qobject_decref(obj);
+ goto err_out;
+ }
+
+ mon->mc->id = qdict_get(input, "id");
+ qobject_incref(mon->mc->id);
+
+ cmd_name = qdict_get_str(input, "execute");
+ if (invalid_qmp_mode(mon, cmd_name)) {
+ qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
+ goto err_out;
+ }
+
+ if (strstart(cmd_name, "query-", &query_cmd)) {
+ cmd = qmp_find_query_cmd(query_cmd);
+ } else {
+ cmd = qmp_find_cmd(cmd_name);
+ }
+
+ if (!cmd) {
+ qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
+ goto err_out;
+ }
+
+ obj = qdict_get(input, "arguments");
+ if (!obj) {
+ args = qdict_new();
+ } else {
+ args = qobject_to_qdict(obj);
+ QINCREF(args);
+ }
+
+ err = qmp_check_client_args(cmd, args);
+ if (err < 0) {
+ goto err_out;
+ }
+
+ if (query_cmd) {
+ qmp_call_query_cmd(mon, cmd);
+ } else if (handler_is_async(cmd)) {
+ err = qmp_async_cmd_handler(mon, cmd, args);
+ if (err) {
+ /* emit the error response */
+ goto err_out;
+ }
+ } else {
+ qmp_call_cmd(mon, cmd, args);
+ }
+
+ goto out;
+
+err_out:
+ monitor_protocol_emitter(mon, NULL);
+out:
+ QDECREF(input);
+ QDECREF(args);
+}
+
+/**
+ * monitor_control_read(): Read and handle QMP input
+ */
+static void monitor_control_read(void *opaque, const uint8_t *buf, int size)
+{
+ Monitor *old_mon = cur_mon;
+
+ cur_mon = opaque;
+
+ json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size);
+
+ cur_mon = old_mon;
+}
+
+static void monitor_read(void *opaque, const uint8_t *buf, int size)
+{
+ Monitor *old_mon = cur_mon;
+ int i;
+
+ cur_mon = opaque;
+
+ if (cur_mon->rs) {
+ for (i = 0; i < size; i++)
+ readline_handle_byte(cur_mon->rs, buf[i]);
+ } else {
+ if (size == 0 || buf[size - 1] != 0)
+ monitor_printf(cur_mon, "corrupted command\n");
+ else
+ handle_user_command(cur_mon, (char *)buf);
+ }
+
+ cur_mon = old_mon;
+}
+
+static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque)
+{
+ monitor_suspend(mon);
+ handle_user_command(mon, cmdline);
+ monitor_resume(mon);
+}
+
+int monitor_suspend(Monitor *mon)
+{
+ if (!mon->rs)
+ return -ENOTTY;
+ mon->suspend_cnt++;
+ return 0;
+}
+
+void monitor_resume(Monitor *mon)
+{
+ if (!mon->rs)
+ return;
+ if (--mon->suspend_cnt == 0)
+ readline_show_prompt(mon->rs);
+}
+
+static QObject *get_qmp_greeting(void)
+{
+ QObject *ver;
+
+ do_info_version(NULL, &ver);
+ return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
+}
+
+/**
+ * monitor_control_event(): Print QMP gretting
+ */
+static void monitor_control_event(void *opaque, int event)
+{
+ QObject *data;
+ Monitor *mon = opaque;
+
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ mon->mc->command_mode = 0;
+ json_message_parser_init(&mon->mc->parser, handle_qmp_command);
+ data = get_qmp_greeting();
+ monitor_json_emitter(mon, data);
+ qobject_decref(data);
+ break;
+ case CHR_EVENT_CLOSED:
+ json_message_parser_destroy(&mon->mc->parser);
+ break;
+ }
+}
+
+static void monitor_event(void *opaque, int event)
+{
+ Monitor *mon = opaque;
+
+ switch (event) {
+ case CHR_EVENT_MUX_IN:
+ mon->mux_out = 0;
+ if (mon->reset_seen) {
+ readline_restart(mon->rs);
+ monitor_resume(mon);
+ monitor_flush(mon);
+ } else {
+ mon->suspend_cnt = 0;
+ }
+ break;
+
+ case CHR_EVENT_MUX_OUT:
+ if (mon->reset_seen) {
+ if (mon->suspend_cnt == 0) {
+ monitor_printf(mon, "\n");
+ }
+ monitor_flush(mon);
+ monitor_suspend(mon);
+ } else {
+ mon->suspend_cnt++;
+ }
+ mon->mux_out = 1;
+ break;
+
+ case CHR_EVENT_OPENED:
+ monitor_printf(mon, "QEMU %s monitor - type 'help' for more "
+ "information\n", QEMU_VERSION);
+ if (!mon->mux_out) {
+ readline_show_prompt(mon->rs);
+ }
+ mon->reset_seen = 1;
+ break;
+ }
+}
+
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 8
+ * End:
+ */
+
+void monitor_init(CharDriverState *chr, int flags)
+{
+ static int is_first_init = 1;
+ Monitor *mon;
+
+ if (is_first_init) {
+ key_timer = qemu_new_timer(vm_clock, release_keys, NULL);
+ is_first_init = 0;
+ }
+
+ mon = qemu_mallocz(sizeof(*mon));
+
+ mon->chr = chr;
+ mon->flags = flags;
+ if (flags & MONITOR_USE_READLINE) {
+ mon->rs = readline_init(mon, monitor_find_completion);
+ monitor_read_command(mon, 0);
+ }
+
+ if (monitor_ctrl_mode(mon)) {
+ mon->mc = qemu_mallocz(sizeof(MonitorControl));
+ /* Control mode requires special handlers */
+ qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read,
+ monitor_control_event, mon);
+ qemu_chr_set_echo(chr, true);
+ } else {
+ qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
+ monitor_event, mon);
+ }
+
+ QLIST_INSERT_HEAD(&mon_list, mon, entry);
+ if (!default_mon || (flags & MONITOR_IS_DEFAULT))
+ default_mon = mon;
+}
+
+static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
+{
+ BlockDriverState *bs = opaque;
+ int ret = 0;
+
+ if (bdrv_set_key(bs, password) != 0) {
+ monitor_printf(mon, "invalid password\n");
+ ret = -EPERM;
+ }
+ if (mon->password_completion_cb)
+ mon->password_completion_cb(mon->password_opaque, ret);
+
+ monitor_read_command(mon, 1);
+}
+
+int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
+ BlockDriverCompletionFunc *completion_cb,
+ void *opaque)
+{
+ int err;
+
+ if (!bdrv_key_required(bs)) {
+ if (completion_cb)
+ completion_cb(opaque, 0);
+ return 0;
+ }
+
+ if (monitor_ctrl_mode(mon)) {
+ qerror_report(QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs));
+ return -1;
+ }
+
+ monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs),
+ bdrv_get_encrypted_filename(bs));
+
+ mon->password_completion_cb = completion_cb;
+ mon->password_opaque = opaque;
+
+ err = monitor_read_password(mon, bdrv_password_cb, bs);
+
+ if (err && completion_cb)
+ completion_cb(opaque, err);
+
+ return err;
+}
diff --git a/monitor.h b/monitor.h
new file mode 100644
index 0000000..4f2d328
--- /dev/null
+++ b/monitor.h
@@ -0,0 +1,65 @@
+#ifndef MONITOR_H
+#define MONITOR_H
+
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "qerror.h"
+#include "qdict.h"
+#include "block.h"
+
+extern Monitor *cur_mon;
+extern Monitor *default_mon;
+
+/* flags for monitor_init */
+#define MONITOR_IS_DEFAULT 0x01
+#define MONITOR_USE_READLINE 0x02
+#define MONITOR_USE_CONTROL 0x04
+#define MONITOR_USE_PRETTY 0x08
+
+/* flags for monitor commands */
+#define MONITOR_CMD_ASYNC 0x0001
+
+/* QMP events */
+typedef enum MonitorEvent {
+ QEVENT_SHUTDOWN,
+ QEVENT_RESET,
+ QEVENT_POWERDOWN,
+ QEVENT_STOP,
+ QEVENT_RESUME,
+ QEVENT_VNC_CONNECTED,
+ QEVENT_VNC_INITIALIZED,
+ QEVENT_VNC_DISCONNECTED,
+ QEVENT_BLOCK_IO_ERROR,
+ QEVENT_RTC_CHANGE,
+ QEVENT_WATCHDOG,
+ QEVENT_SPICE_CONNECTED,
+ QEVENT_SPICE_INITIALIZED,
+ QEVENT_SPICE_DISCONNECTED,
+ QEVENT_MAX,
+} MonitorEvent;
+
+int monitor_cur_is_qmp(void);
+
+void monitor_protocol_event(MonitorEvent event, QObject *data);
+void monitor_init(CharDriverState *chr, int flags);
+
+int monitor_suspend(Monitor *mon);
+void monitor_resume(Monitor *mon);
+
+int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
+ BlockDriverCompletionFunc *completion_cb,
+ void *opaque);
+
+int monitor_get_fd(Monitor *mon, const char *fdname);
+
+void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+ GCC_FMT_ATTR(2, 0);
+void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
+void monitor_print_filename(Monitor *mon, const char *filename);
+void monitor_flush(Monitor *mon);
+
+typedef void (MonitorCompletion)(void *opaque, QObject *ret_data);
+
+void monitor_set_error(Monitor *mon, QError *qerror);
+
+#endif /* !MONITOR_H */
diff --git a/nbd.c b/nbd.c
new file mode 100644
index 0000000..d8ebc42
--- /dev/null
+++ b/nbd.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Network Block Device
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "nbd.h"
+
+#include <errno.h>
+#include <string.h>
+#ifndef _WIN32
+#include <sys/ioctl.h>
+#endif
+#if defined(__sun__) || defined(__HAIKU__)
+#include <sys/ioccom.h>
+#endif
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "qemu_socket.h"
+
+//#define DEBUG_NBD
+
+#ifdef DEBUG_NBD
+#define TRACE(msg, ...) do { \
+ LOG(msg, ## __VA_ARGS__); \
+} while(0)
+#else
+#define TRACE(msg, ...) \
+ do { } while (0)
+#endif
+
+#define LOG(msg, ...) do { \
+ fprintf(stderr, "%s:%s():L%d: " msg "\n", \
+ __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
+} while(0)
+
+/* This is all part of the "official" NBD API */
+
+#define NBD_REPLY_SIZE (4 + 4 + 8)
+#define NBD_REQUEST_MAGIC 0x25609513
+#define NBD_REPLY_MAGIC 0x67446698
+
+#define NBD_SET_SOCK _IO(0xab, 0)
+#define NBD_SET_BLKSIZE _IO(0xab, 1)
+#define NBD_SET_SIZE _IO(0xab, 2)
+#define NBD_DO_IT _IO(0xab, 3)
+#define NBD_CLEAR_SOCK _IO(0xab, 4)
+#define NBD_CLEAR_QUE _IO(0xab, 5)
+#define NBD_PRINT_DEBUG _IO(0xab, 6)
+#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7)
+#define NBD_DISCONNECT _IO(0xab, 8)
+
+#define NBD_OPT_EXPORT_NAME (1 << 0)
+
+/* That's all folks */
+
+#define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
+#define write_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, false)
+
+size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
+{
+ size_t offset = 0;
+
+ while (offset < size) {
+ ssize_t len;
+
+ if (do_read) {
+ len = recv(fd, buffer + offset, size - offset, 0);
+ } else {
+ len = send(fd, buffer + offset, size - offset, 0);
+ }
+
+ if (len == -1)
+ errno = socket_error();
+
+ /* recoverable error */
+ if (len == -1 && (errno == EAGAIN || errno == EINTR)) {
+ continue;
+ }
+
+ /* eof */
+ if (len == 0) {
+ break;
+ }
+
+ /* unrecoverable error */
+ if (len == -1) {
+ return 0;
+ }
+
+ offset += len;
+ }
+
+ return offset;
+}
+
+int tcp_socket_outgoing(const char *address, uint16_t port)
+{
+ int s;
+ struct in_addr in;
+ struct sockaddr_in addr;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s == -1) {
+ return -1;
+ }
+
+ if (inet_aton(address, &in) == 0) {
+ struct hostent *ent;
+
+ ent = gethostbyname(address);
+ if (ent == NULL) {
+ goto error;
+ }
+
+ memcpy(&in, ent->h_addr, sizeof(in));
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
+
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ goto error;
+ }
+
+ return s;
+error:
+ closesocket(s);
+ return -1;
+}
+
+int tcp_socket_incoming(const char *address, uint16_t port)
+{
+ int s;
+ struct in_addr in;
+ struct sockaddr_in addr;
+ int opt;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ if (s == -1) {
+ return -1;
+ }
+
+ if (inet_aton(address, &in) == 0) {
+ struct hostent *ent;
+
+ ent = gethostbyname(address);
+ if (ent == NULL) {
+ goto error;
+ }
+
+ memcpy(&in, ent->h_addr, sizeof(in));
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
+
+ opt = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (const void *) &opt, sizeof(opt)) == -1) {
+ goto error;
+ }
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ goto error;
+ }
+
+ if (listen(s, 128) == -1) {
+ goto error;
+ }
+
+ return s;
+error:
+ closesocket(s);
+ return -1;
+}
+
+#ifndef _WIN32
+int unix_socket_incoming(const char *path)
+{
+ int s;
+ struct sockaddr_un addr;
+
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s == -1) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ pstrcpy(addr.sun_path, sizeof(addr.sun_path), path);
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ goto error;
+ }
+
+ if (listen(s, 128) == -1) {
+ goto error;
+ }
+
+ return s;
+error:
+ closesocket(s);
+ return -1;
+}
+
+int unix_socket_outgoing(const char *path)
+{
+ int s;
+ struct sockaddr_un addr;
+
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s == -1) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ pstrcpy(addr.sun_path, sizeof(addr.sun_path), path);
+
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ goto error;
+ }
+
+ return s;
+error:
+ closesocket(s);
+ return -1;
+}
+#else
+int unix_socket_incoming(const char *path)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+int unix_socket_outgoing(const char *path)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+#endif
+
+
+/* Basic flow
+
+ Server Client
+
+ Negotiate
+ Request
+ Response
+ Request
+ Response
+ ...
+ ...
+ Request (type == 2)
+*/
+
+int nbd_negotiate(int csock, off_t size)
+{
+ char buf[8 + 8 + 8 + 128];
+
+ /* Negotiate
+ [ 0 .. 7] passwd ("NBDMAGIC")
+ [ 8 .. 15] magic (0x00420281861253)
+ [16 .. 23] size
+ [24 .. 151] reserved (0)
+ */
+
+ TRACE("Beginning negotiation.");
+ memcpy(buf, "NBDMAGIC", 8);
+ cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL);
+ cpu_to_be64w((uint64_t*)(buf + 16), size);
+ memset(buf + 24, 0, 128);
+
+ if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ LOG("write failed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ TRACE("Negotation succeeded.");
+
+ return 0;
+}
+
+int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+ off_t *size, size_t *blocksize)
+{
+ char buf[256];
+ uint64_t magic, s;
+ uint16_t tmp;
+
+ TRACE("Receiving negotation.");
+
+ if (read_sync(csock, buf, 8) != 8) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ buf[8] = '\0';
+ if (strlen(buf) == 0) {
+ LOG("server connection closed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ TRACE("Magic is %c%c%c%c%c%c%c%c",
+ qemu_isprint(buf[0]) ? buf[0] : '.',
+ qemu_isprint(buf[1]) ? buf[1] : '.',
+ qemu_isprint(buf[2]) ? buf[2] : '.',
+ qemu_isprint(buf[3]) ? buf[3] : '.',
+ qemu_isprint(buf[4]) ? buf[4] : '.',
+ qemu_isprint(buf[5]) ? buf[5] : '.',
+ qemu_isprint(buf[6]) ? buf[6] : '.',
+ qemu_isprint(buf[7]) ? buf[7] : '.');
+
+ if (memcmp(buf, "NBDMAGIC", 8) != 0) {
+ LOG("Invalid magic received");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+ magic = be64_to_cpu(magic);
+ TRACE("Magic is 0x%" PRIx64, magic);
+
+ if (name) {
+ uint32_t reserved = 0;
+ uint32_t opt;
+ uint32_t namesize;
+
+ TRACE("Checking magic (opts_magic)");
+ if (magic != 0x49484156454F5054LL) {
+ LOG("Bad magic received");
+ errno = EINVAL;
+ return -1;
+ }
+ if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+ LOG("flags read failed");
+ errno = EINVAL;
+ return -1;
+ }
+ *flags = be16_to_cpu(tmp) << 16;
+ /* reserved for future use */
+ if (write_sync(csock, &reserved, sizeof(reserved)) !=
+ sizeof(reserved)) {
+ LOG("write failed (reserved)");
+ errno = EINVAL;
+ return -1;
+ }
+ /* write the export name */
+ magic = cpu_to_be64(magic);
+ if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+ LOG("write failed (magic)");
+ errno = EINVAL;
+ return -1;
+ }
+ opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
+ if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+ LOG("write failed (opt)");
+ errno = EINVAL;
+ return -1;
+ }
+ namesize = cpu_to_be32(strlen(name));
+ if (write_sync(csock, &namesize, sizeof(namesize)) !=
+ sizeof(namesize)) {
+ LOG("write failed (namesize)");
+ errno = EINVAL;
+ return -1;
+ }
+ if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
+ LOG("write failed (name)");
+ errno = EINVAL;
+ return -1;
+ }
+ } else {
+ TRACE("Checking magic (cli_magic)");
+
+ if (magic != 0x00420281861253LL) {
+ LOG("Bad magic received");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+ *size = be64_to_cpu(s);
+ *blocksize = 1024;
+ TRACE("Size is %" PRIu64, *size);
+
+ if (!name) {
+ if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
+ LOG("read failed (flags)");
+ errno = EINVAL;
+ return -1;
+ }
+ *flags = be32_to_cpup(flags);
+ } else {
+ if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+ LOG("read failed (tmp)");
+ errno = EINVAL;
+ return -1;
+ }
+ *flags |= be32_to_cpu(tmp);
+ }
+ if (read_sync(csock, &buf, 124) != 124) {
+ LOG("read failed (buf)");
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+#ifndef _WIN32
+int nbd_init(int fd, int csock, off_t size, size_t blocksize)
+{
+ TRACE("Setting block size to %lu", (unsigned long)blocksize);
+
+ if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) {
+ int serrno = errno;
+ LOG("Failed setting NBD block size");
+ errno = serrno;
+ return -1;
+ }
+
+ TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize));
+
+ if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) {
+ int serrno = errno;
+ LOG("Failed setting size (in blocks)");
+ errno = serrno;
+ return -1;
+ }
+
+ TRACE("Clearing NBD socket");
+
+ if (ioctl(fd, NBD_CLEAR_SOCK) == -1) {
+ int serrno = errno;
+ LOG("Failed clearing NBD socket");
+ errno = serrno;
+ return -1;
+ }
+
+ TRACE("Setting NBD socket");
+
+ if (ioctl(fd, NBD_SET_SOCK, csock) == -1) {
+ int serrno = errno;
+ LOG("Failed to set NBD socket");
+ errno = serrno;
+ return -1;
+ }
+
+ TRACE("Negotiation ended");
+
+ return 0;
+}
+
+int nbd_disconnect(int fd)
+{
+ ioctl(fd, NBD_CLEAR_QUE);
+ ioctl(fd, NBD_DISCONNECT);
+ ioctl(fd, NBD_CLEAR_SOCK);
+ return 0;
+}
+
+int nbd_client(int fd)
+{
+ int ret;
+ int serrno;
+
+ TRACE("Doing NBD loop");
+
+ ret = ioctl(fd, NBD_DO_IT);
+ serrno = errno;
+
+ TRACE("NBD loop returned %d: %s", ret, strerror(serrno));
+
+ TRACE("Clearing NBD queue");
+ ioctl(fd, NBD_CLEAR_QUE);
+
+ TRACE("Clearing NBD socket");
+ ioctl(fd, NBD_CLEAR_SOCK);
+
+ errno = serrno;
+ return ret;
+}
+#else
+int nbd_init(int fd, int csock, off_t size, size_t blocksize)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+int nbd_disconnect(int fd)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+int nbd_client(int fd)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+#endif
+
+int nbd_send_request(int csock, struct nbd_request *request)
+{
+ uint8_t buf[4 + 4 + 8 + 8 + 4];
+
+ cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC);
+ cpu_to_be32w((uint32_t*)(buf + 4), request->type);
+ cpu_to_be64w((uint64_t*)(buf + 8), request->handle);
+ cpu_to_be64w((uint64_t*)(buf + 16), request->from);
+ cpu_to_be32w((uint32_t*)(buf + 24), request->len);
+
+ TRACE("Sending request to client");
+
+ if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ LOG("writing to socket failed");
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int nbd_receive_request(int csock, struct nbd_request *request)
+{
+ uint8_t buf[4 + 4 + 8 + 8 + 4];
+ uint32_t magic;
+
+ if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Request
+ [ 0 .. 3] magic (NBD_REQUEST_MAGIC)
+ [ 4 .. 7] type (0 == READ, 1 == WRITE)
+ [ 8 .. 15] handle
+ [16 .. 23] from
+ [24 .. 27] len
+ */
+
+ magic = be32_to_cpup((uint32_t*)buf);
+ request->type = be32_to_cpup((uint32_t*)(buf + 4));
+ request->handle = be64_to_cpup((uint64_t*)(buf + 8));
+ request->from = be64_to_cpup((uint64_t*)(buf + 16));
+ request->len = be32_to_cpup((uint32_t*)(buf + 24));
+
+ TRACE("Got request: "
+ "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }",
+ magic, request->type, request->from, request->len);
+
+ if (magic != NBD_REQUEST_MAGIC) {
+ LOG("invalid magic (got 0x%x)", magic);
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int nbd_receive_reply(int csock, struct nbd_reply *reply)
+{
+ uint8_t buf[NBD_REPLY_SIZE];
+ uint32_t magic;
+
+ memset(buf, 0xAA, sizeof(buf));
+
+ if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ LOG("read failed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Reply
+ [ 0 .. 3] magic (NBD_REPLY_MAGIC)
+ [ 4 .. 7] error (0 == no error)
+ [ 7 .. 15] handle
+ */
+
+ magic = be32_to_cpup((uint32_t*)buf);
+ reply->error = be32_to_cpup((uint32_t*)(buf + 4));
+ reply->handle = be64_to_cpup((uint64_t*)(buf + 8));
+
+ TRACE("Got reply: "
+ "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }",
+ magic, reply->error, reply->handle);
+
+ if (magic != NBD_REPLY_MAGIC) {
+ LOG("invalid magic (got 0x%x)", magic);
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+static int nbd_send_reply(int csock, struct nbd_reply *reply)
+{
+ uint8_t buf[4 + 4 + 8];
+
+ /* Reply
+ [ 0 .. 3] magic (NBD_REPLY_MAGIC)
+ [ 4 .. 7] error (0 == no error)
+ [ 7 .. 15] handle
+ */
+ cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC);
+ cpu_to_be32w((uint32_t*)(buf + 4), reply->error);
+ cpu_to_be64w((uint64_t*)(buf + 8), reply->handle);
+
+ TRACE("Sending response to client");
+
+ if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+ LOG("writing to socket failed");
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
+ off_t *offset, bool readonly, uint8_t *data, int data_size)
+{
+ struct nbd_request request;
+ struct nbd_reply reply;
+
+ TRACE("Reading request.");
+
+ if (nbd_receive_request(csock, &request) == -1)
+ return -1;
+
+ if (request.len + NBD_REPLY_SIZE > data_size) {
+ LOG("len (%u) is larger than max len (%u)",
+ request.len + NBD_REPLY_SIZE, data_size);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((request.from + request.len) < request.from) {
+ LOG("integer overflow detected! "
+ "you're probably being attacked");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((request.from + request.len) > size) {
+ LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64
+ ", Offset: %" PRIu64 "\n",
+ request.from, request.len, (uint64_t)size, dev_offset);
+ LOG("requested operation past EOF--bad client?");
+ errno = EINVAL;
+ return -1;
+ }
+
+ TRACE("Decoding type");
+
+ reply.handle = request.handle;
+ reply.error = 0;
+
+ switch (request.type) {
+ case NBD_CMD_READ:
+ TRACE("Request type is READ");
+
+ if (bdrv_read(bs, (request.from + dev_offset) / 512,
+ data + NBD_REPLY_SIZE,
+ request.len / 512) == -1) {
+ LOG("reading from file failed");
+ errno = EINVAL;
+ return -1;
+ }
+ *offset += request.len;
+
+ TRACE("Read %u byte(s)", request.len);
+
+ /* Reply
+ [ 0 .. 3] magic (NBD_REPLY_MAGIC)
+ [ 4 .. 7] error (0 == no error)
+ [ 7 .. 15] handle
+ */
+
+ cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC);
+ cpu_to_be32w((uint32_t*)(data + 4), reply.error);
+ cpu_to_be64w((uint64_t*)(data + 8), reply.handle);
+
+ TRACE("Sending data to client");
+
+ if (write_sync(csock, data,
+ request.len + NBD_REPLY_SIZE) !=
+ request.len + NBD_REPLY_SIZE) {
+ LOG("writing to socket failed");
+ errno = EINVAL;
+ return -1;
+ }
+ break;
+ case NBD_CMD_WRITE:
+ TRACE("Request type is WRITE");
+
+ TRACE("Reading %u byte(s)", request.len);
+
+ if (read_sync(csock, data, request.len) != request.len) {
+ LOG("reading from socket failed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (readonly) {
+ TRACE("Server is read-only, return error");
+ reply.error = 1;
+ } else {
+ TRACE("Writing to device");
+
+ if (bdrv_write(bs, (request.from + dev_offset) / 512,
+ data, request.len / 512) == -1) {
+ LOG("writing to file failed");
+ errno = EINVAL;
+ return -1;
+ }
+
+ *offset += request.len;
+ }
+
+ if (nbd_send_reply(csock, &reply) == -1)
+ return -1;
+ break;
+ case NBD_CMD_DISC:
+ TRACE("Request type is DISCONNECT");
+ errno = 0;
+ return 1;
+ default:
+ LOG("invalid request type (%u) received", request.type);
+ errno = EINVAL;
+ return -1;
+ }
+
+ TRACE("Request/Reply complete");
+
+ return 0;
+}
diff --git a/nbd.h b/nbd.h
new file mode 100644
index 0000000..fc3a594
--- /dev/null
+++ b/nbd.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Network Block Device
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef NBD_H
+#define NBD_H
+
+#include <sys/types.h>
+
+#include <qemu-common.h>
+#include "block_int.h"
+
+struct nbd_request {
+ uint32_t type;
+ uint64_t handle;
+ uint64_t from;
+ uint32_t len;
+};
+
+struct nbd_reply {
+ uint32_t error;
+ uint64_t handle;
+};
+
+enum {
+ NBD_CMD_READ = 0,
+ NBD_CMD_WRITE = 1,
+ NBD_CMD_DISC = 2
+};
+
+#define NBD_DEFAULT_PORT 10809
+
+size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read);
+int tcp_socket_outgoing(const char *address, uint16_t port);
+int tcp_socket_incoming(const char *address, uint16_t port);
+int unix_socket_outgoing(const char *path);
+int unix_socket_incoming(const char *path);
+
+int nbd_negotiate(int csock, off_t size);
+int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+ off_t *size, size_t *blocksize);
+int nbd_init(int fd, int csock, off_t size, size_t blocksize);
+int nbd_send_request(int csock, struct nbd_request *request);
+int nbd_receive_reply(int csock, struct nbd_reply *reply);
+int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
+ off_t *offset, bool readonly, uint8_t *data, int data_size);
+int nbd_client(int fd);
+int nbd_disconnect(int fd);
+
+#endif
diff --git a/net-checksum.c b/net-checksum.c
new file mode 100644
index 0000000..4956c5c
--- /dev/null
+++ b/net-checksum.c
@@ -0,0 +1,86 @@
+/*
+ * IP checksumming functions.
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "hw/hw.h"
+#include "net.h"
+
+#define PROTO_TCP 6
+#define PROTO_UDP 17
+
+uint32_t net_checksum_add(int len, uint8_t *buf)
+{
+ uint32_t sum = 0;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i & 1)
+ sum += (uint32_t)buf[i];
+ else
+ sum += (uint32_t)buf[i] << 8;
+ }
+ return sum;
+}
+
+uint16_t net_checksum_finish(uint32_t sum)
+{
+ while (sum>>16)
+ sum = (sum & 0xFFFF)+(sum >> 16);
+ return ~sum;
+}
+
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+ uint8_t *addrs, uint8_t *buf)
+{
+ uint32_t sum = 0;
+
+ sum += net_checksum_add(length, buf); // payload
+ sum += net_checksum_add(8, addrs); // src + dst address
+ sum += proto + length; // protocol & length
+ return net_checksum_finish(sum);
+}
+
+void net_checksum_calculate(uint8_t *data, int length)
+{
+ int hlen, plen, proto, csum_offset;
+ uint16_t csum;
+
+ if ((data[14] & 0xf0) != 0x40)
+ return; /* not IPv4 */
+ hlen = (data[14] & 0x0f) * 4;
+ plen = (data[16] << 8 | data[17]) - hlen;
+ proto = data[23];
+
+ switch (proto) {
+ case PROTO_TCP:
+ csum_offset = 16;
+ break;
+ case PROTO_UDP:
+ csum_offset = 6;
+ break;
+ default:
+ return;
+ }
+
+ if (plen < csum_offset+2)
+ return;
+
+ data[14+hlen+csum_offset] = 0;
+ data[14+hlen+csum_offset+1] = 0;
+ csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
+ data[14+hlen+csum_offset] = csum >> 8;
+ data[14+hlen+csum_offset+1] = csum & 0xff;
+}
diff --git a/net.c b/net.c
new file mode 100644
index 0000000..21d4443
--- /dev/null
+++ b/net.c
@@ -0,0 +1,1439 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "net.h"
+
+#include "config-host.h"
+
+#include "net/tap.h"
+#include "net/socket.h"
+#include "net/dump.h"
+#include "net/slirp.h"
+#include "net/vde.h"
+#include "net/util.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "qemu_socket.h"
+#include "hw/qdev.h"
+
+static QTAILQ_HEAD(, VLANState) vlans;
+static QTAILQ_HEAD(, VLANClientState) non_vlan_clients;
+
+int default_net = 1;
+
+/***********************************************************/
+/* network device redirectors */
+
+#if defined(DEBUG_NET)
+static void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+ int len, i, j, c;
+
+ for(i=0;i<size;i+=16) {
+ len = size - i;
+ if (len > 16)
+ len = 16;
+ fprintf(f, "%08x ", i);
+ for(j=0;j<16;j++) {
+ if (j < len)
+ fprintf(f, " %02x", buf[i+j]);
+ else
+ fprintf(f, " ");
+ }
+ fprintf(f, " ");
+ for(j=0;j<len;j++) {
+ c = buf[i+j];
+ if (c < ' ' || c > '~')
+ c = '.';
+ fprintf(f, "%c", c);
+ }
+ fprintf(f, "\n");
+ }
+}
+#endif
+
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+ const char *p, *p1;
+ int len;
+ p = *pp;
+ p1 = strchr(p, sep);
+ if (!p1)
+ return -1;
+ len = p1 - p;
+ p1++;
+ if (buf_size > 0) {
+ if (len > buf_size - 1)
+ len = buf_size - 1;
+ memcpy(buf, p, len);
+ buf[len] = '\0';
+ }
+ *pp = p1;
+ return 0;
+}
+
+int parse_host_src_port(struct sockaddr_in *haddr,
+ struct sockaddr_in *saddr,
+ const char *input_str)
+{
+ char *str = qemu_strdup(input_str);
+ char *host_str = str;
+ char *src_str;
+ const char *src_str2;
+ char *ptr;
+
+ /*
+ * Chop off any extra arguments at the end of the string which
+ * would start with a comma, then fill in the src port information
+ * if it was provided else use the "any address" and "any port".
+ */
+ if ((ptr = strchr(str,',')))
+ *ptr = '\0';
+
+ if ((src_str = strchr(input_str,'@'))) {
+ *src_str = '\0';
+ src_str++;
+ }
+
+ if (parse_host_port(haddr, host_str) < 0)
+ goto fail;
+
+ src_str2 = src_str;
+ if (!src_str || *src_str == '\0')
+ src_str2 = ":0";
+
+ if (parse_host_port(saddr, src_str2) < 0)
+ goto fail;
+
+ free(str);
+ return(0);
+
+fail:
+ free(str);
+ return -1;
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str)
+{
+ char buf[512];
+ struct hostent *he;
+ const char *p, *r;
+ int port;
+
+ p = str;
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ return -1;
+ saddr->sin_family = AF_INET;
+ if (buf[0] == '\0') {
+ saddr->sin_addr.s_addr = 0;
+ } else {
+ if (qemu_isdigit(buf[0])) {
+ if (!inet_aton(buf, &saddr->sin_addr))
+ return -1;
+ } else {
+ if ((he = gethostbyname(buf)) == NULL)
+ return - 1;
+ saddr->sin_addr = *(struct in_addr *)he->h_addr;
+ }
+ }
+ port = strtol(p, (char **)&r, 0);
+ if (r == p)
+ return -1;
+ saddr->sin_port = htons(port);
+ return 0;
+}
+
+void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6])
+{
+ snprintf(vc->info_str, sizeof(vc->info_str),
+ "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ vc->model,
+ macaddr[0], macaddr[1], macaddr[2],
+ macaddr[3], macaddr[4], macaddr[5]);
+}
+
+void qemu_macaddr_default_if_unset(MACAddr *macaddr)
+{
+ static int index = 0;
+ static const MACAddr zero = { .a = { 0,0,0,0,0,0 } };
+
+ if (memcmp(macaddr, &zero, sizeof(zero)) != 0)
+ return;
+ macaddr->a[0] = 0x52;
+ macaddr->a[1] = 0x54;
+ macaddr->a[2] = 0x00;
+ macaddr->a[3] = 0x12;
+ macaddr->a[4] = 0x34;
+ macaddr->a[5] = 0x56 + index++;
+}
+
+static char *assign_name(VLANClientState *vc1, const char *model)
+{
+ VLANState *vlan;
+ char buf[256];
+ int id = 0;
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ VLANClientState *vc;
+
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ if (vc != vc1 && strcmp(vc->model, model) == 0) {
+ id++;
+ }
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s.%d", model, id);
+
+ return qemu_strdup(buf);
+}
+
+static ssize_t qemu_deliver_packet(VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ void *opaque);
+static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque);
+
+VLANClientState *qemu_new_net_client(NetClientInfo *info,
+ VLANState *vlan,
+ VLANClientState *peer,
+ const char *model,
+ const char *name)
+{
+ VLANClientState *vc;
+
+ assert(info->size >= sizeof(VLANClientState));
+
+ vc = qemu_mallocz(info->size);
+
+ vc->info = info;
+ vc->model = qemu_strdup(model);
+ if (name) {
+ vc->name = qemu_strdup(name);
+ } else {
+ vc->name = assign_name(vc, model);
+ }
+
+ if (vlan) {
+ assert(!peer);
+ vc->vlan = vlan;
+ QTAILQ_INSERT_TAIL(&vc->vlan->clients, vc, next);
+ } else {
+ if (peer) {
+ assert(!peer->peer);
+ vc->peer = peer;
+ peer->peer = vc;
+ }
+ QTAILQ_INSERT_TAIL(&non_vlan_clients, vc, next);
+
+ vc->send_queue = qemu_new_net_queue(qemu_deliver_packet,
+ qemu_deliver_packet_iov,
+ vc);
+ }
+
+ return vc;
+}
+
+NICState *qemu_new_nic(NetClientInfo *info,
+ NICConf *conf,
+ const char *model,
+ const char *name,
+ void *opaque)
+{
+ VLANClientState *nc;
+ NICState *nic;
+
+ assert(info->type == NET_CLIENT_TYPE_NIC);
+ assert(info->size >= sizeof(NICState));
+
+ nc = qemu_new_net_client(info, conf->vlan, conf->peer, model, name);
+
+ nic = DO_UPCAST(NICState, nc, nc);
+ nic->conf = conf;
+ nic->opaque = opaque;
+
+ return nic;
+}
+
+static void qemu_cleanup_vlan_client(VLANClientState *vc)
+{
+ if (vc->vlan) {
+ QTAILQ_REMOVE(&vc->vlan->clients, vc, next);
+ } else {
+ QTAILQ_REMOVE(&non_vlan_clients, vc, next);
+ }
+
+ if (vc->info->cleanup) {
+ vc->info->cleanup(vc);
+ }
+}
+
+static void qemu_free_vlan_client(VLANClientState *vc)
+{
+ if (!vc->vlan) {
+ if (vc->send_queue) {
+ qemu_del_net_queue(vc->send_queue);
+ }
+ if (vc->peer) {
+ vc->peer->peer = NULL;
+ }
+ }
+ qemu_free(vc->name);
+ qemu_free(vc->model);
+ qemu_free(vc);
+}
+
+void qemu_del_vlan_client(VLANClientState *vc)
+{
+ /* If there is a peer NIC, delete and cleanup client, but do not free. */
+ if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_TYPE_NIC) {
+ NICState *nic = DO_UPCAST(NICState, nc, vc->peer);
+ if (nic->peer_deleted) {
+ return;
+ }
+ nic->peer_deleted = true;
+ /* Let NIC know peer is gone. */
+ vc->peer->link_down = true;
+ if (vc->peer->info->link_status_changed) {
+ vc->peer->info->link_status_changed(vc->peer);
+ }
+ qemu_cleanup_vlan_client(vc);
+ return;
+ }
+
+ /* If this is a peer NIC and peer has already been deleted, free it now. */
+ if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_TYPE_NIC) {
+ NICState *nic = DO_UPCAST(NICState, nc, vc);
+ if (nic->peer_deleted) {
+ qemu_free_vlan_client(vc->peer);
+ }
+ }
+
+ qemu_cleanup_vlan_client(vc);
+ qemu_free_vlan_client(vc);
+}
+
+VLANClientState *
+qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
+ const char *client_str)
+{
+ VLANState *vlan;
+ VLANClientState *vc;
+
+ vlan = qemu_find_vlan(vlan_id, 0);
+ if (!vlan) {
+ monitor_printf(mon, "unknown VLAN %d\n", vlan_id);
+ return NULL;
+ }
+
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ if (!strcmp(vc->name, client_str)) {
+ break;
+ }
+ }
+ if (!vc) {
+ monitor_printf(mon, "can't find device %s on VLAN %d\n",
+ client_str, vlan_id);
+ }
+
+ return vc;
+}
+
+void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
+{
+ VLANClientState *nc;
+ VLANState *vlan;
+
+ QTAILQ_FOREACH(nc, &non_vlan_clients, next) {
+ if (nc->info->type == NET_CLIENT_TYPE_NIC) {
+ func(DO_UPCAST(NICState, nc, nc), opaque);
+ }
+ }
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ QTAILQ_FOREACH(nc, &vlan->clients, next) {
+ if (nc->info->type == NET_CLIENT_TYPE_NIC) {
+ func(DO_UPCAST(NICState, nc, nc), opaque);
+ }
+ }
+ }
+}
+
+int qemu_can_send_packet(VLANClientState *sender)
+{
+ VLANState *vlan = sender->vlan;
+ VLANClientState *vc;
+
+ if (sender->peer) {
+ if (sender->peer->receive_disabled) {
+ return 0;
+ } else if (sender->peer->info->can_receive &&
+ !sender->peer->info->can_receive(sender->peer)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (!sender->vlan) {
+ return 1;
+ }
+
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ if (vc == sender) {
+ continue;
+ }
+
+ /* no can_receive() handler, they can always receive */
+ if (!vc->info->can_receive || vc->info->can_receive(vc)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static ssize_t qemu_deliver_packet(VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ void *opaque)
+{
+ VLANClientState *vc = opaque;
+ ssize_t ret;
+
+ if (vc->link_down) {
+ return size;
+ }
+
+ if (vc->receive_disabled) {
+ return 0;
+ }
+
+ if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
+ ret = vc->info->receive_raw(vc, data, size);
+ } else {
+ ret = vc->info->receive(vc, data, size);
+ }
+
+ if (ret == 0) {
+ vc->receive_disabled = 1;
+ };
+
+ return ret;
+}
+
+static ssize_t qemu_vlan_deliver_packet(VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *buf,
+ size_t size,
+ void *opaque)
+{
+ VLANState *vlan = opaque;
+ VLANClientState *vc;
+ ssize_t ret = -1;
+
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ ssize_t len;
+
+ if (vc == sender) {
+ continue;
+ }
+
+ if (vc->link_down) {
+ ret = size;
+ continue;
+ }
+
+ if (vc->receive_disabled) {
+ ret = 0;
+ continue;
+ }
+
+ if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
+ len = vc->info->receive_raw(vc, buf, size);
+ } else {
+ len = vc->info->receive(vc, buf, size);
+ }
+
+ if (len == 0) {
+ vc->receive_disabled = 1;
+ }
+
+ ret = (ret >= 0) ? ret : len;
+
+ }
+
+ return ret;
+}
+
+void qemu_purge_queued_packets(VLANClientState *vc)
+{
+ NetQueue *queue;
+
+ if (!vc->peer && !vc->vlan) {
+ return;
+ }
+
+ if (vc->peer) {
+ queue = vc->peer->send_queue;
+ } else {
+ queue = vc->vlan->send_queue;
+ }
+
+ qemu_net_queue_purge(queue, vc);
+}
+
+void qemu_flush_queued_packets(VLANClientState *vc)
+{
+ NetQueue *queue;
+
+ vc->receive_disabled = 0;
+
+ if (vc->vlan) {
+ queue = vc->vlan->send_queue;
+ } else {
+ queue = vc->send_queue;
+ }
+
+ qemu_net_queue_flush(queue);
+}
+
+static ssize_t qemu_send_packet_async_with_flags(VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *buf, int size,
+ NetPacketSent *sent_cb)
+{
+ NetQueue *queue;
+
+#ifdef DEBUG_NET
+ printf("qemu_send_packet_async:\n");
+ hex_dump(stdout, buf, size);
+#endif
+
+ if (sender->link_down || (!sender->peer && !sender->vlan)) {
+ return size;
+ }
+
+ if (sender->peer) {
+ queue = sender->peer->send_queue;
+ } else {
+ queue = sender->vlan->send_queue;
+ }
+
+ return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
+}
+
+ssize_t qemu_send_packet_async(VLANClientState *sender,
+ const uint8_t *buf, int size,
+ NetPacketSent *sent_cb)
+{
+ return qemu_send_packet_async_with_flags(sender, QEMU_NET_PACKET_FLAG_NONE,
+ buf, size, sent_cb);
+}
+
+void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size)
+{
+ qemu_send_packet_async(vc, buf, size, NULL);
+}
+
+ssize_t qemu_send_packet_raw(VLANClientState *vc, const uint8_t *buf, int size)
+{
+ return qemu_send_packet_async_with_flags(vc, QEMU_NET_PACKET_FLAG_RAW,
+ buf, size, NULL);
+}
+
+static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
+ int iovcnt)
+{
+ uint8_t buffer[4096];
+ size_t offset = 0;
+ int i;
+
+ for (i = 0; i < iovcnt; i++) {
+ size_t len;
+
+ len = MIN(sizeof(buffer) - offset, iov[i].iov_len);
+ memcpy(buffer + offset, iov[i].iov_base, len);
+ offset += len;
+ }
+
+ return vc->info->receive(vc, buffer, offset);
+}
+
+static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt)
+{
+ size_t offset = 0;
+ int i;
+
+ for (i = 0; i < iovcnt; i++)
+ offset += iov[i].iov_len;
+ return offset;
+}
+
+static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque)
+{
+ VLANClientState *vc = opaque;
+
+ if (vc->link_down) {
+ return calc_iov_length(iov, iovcnt);
+ }
+
+ if (vc->info->receive_iov) {
+ return vc->info->receive_iov(vc, iov, iovcnt);
+ } else {
+ return vc_sendv_compat(vc, iov, iovcnt);
+ }
+}
+
+static ssize_t qemu_vlan_deliver_packet_iov(VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque)
+{
+ VLANState *vlan = opaque;
+ VLANClientState *vc;
+ ssize_t ret = -1;
+
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ ssize_t len;
+
+ if (vc == sender) {
+ continue;
+ }
+
+ if (vc->link_down) {
+ ret = calc_iov_length(iov, iovcnt);
+ continue;
+ }
+
+ assert(!(flags & QEMU_NET_PACKET_FLAG_RAW));
+
+ if (vc->info->receive_iov) {
+ len = vc->info->receive_iov(vc, iov, iovcnt);
+ } else {
+ len = vc_sendv_compat(vc, iov, iovcnt);
+ }
+
+ ret = (ret >= 0) ? ret : len;
+ }
+
+ return ret;
+}
+
+ssize_t qemu_sendv_packet_async(VLANClientState *sender,
+ const struct iovec *iov, int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ NetQueue *queue;
+
+ if (sender->link_down || (!sender->peer && !sender->vlan)) {
+ return calc_iov_length(iov, iovcnt);
+ }
+
+ if (sender->peer) {
+ queue = sender->peer->send_queue;
+ } else {
+ queue = sender->vlan->send_queue;
+ }
+
+ return qemu_net_queue_send_iov(queue, sender,
+ QEMU_NET_PACKET_FLAG_NONE,
+ iov, iovcnt, sent_cb);
+}
+
+ssize_t
+qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt)
+{
+ return qemu_sendv_packet_async(vc, iov, iovcnt, NULL);
+}
+
+/* find or alloc a new VLAN */
+VLANState *qemu_find_vlan(int id, int allocate)
+{
+ VLANState *vlan;
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ if (vlan->id == id) {
+ return vlan;
+ }
+ }
+
+ if (!allocate) {
+ return NULL;
+ }
+
+ vlan = qemu_mallocz(sizeof(VLANState));
+ vlan->id = id;
+ QTAILQ_INIT(&vlan->clients);
+
+ vlan->send_queue = qemu_new_net_queue(qemu_vlan_deliver_packet,
+ qemu_vlan_deliver_packet_iov,
+ vlan);
+
+ QTAILQ_INSERT_TAIL(&vlans, vlan, next);
+
+ return vlan;
+}
+
+VLANClientState *qemu_find_netdev(const char *id)
+{
+ VLANClientState *vc;
+
+ QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+ if (!strcmp(vc->name, id)) {
+ return vc;
+ }
+ }
+
+ return NULL;
+}
+
+static int nic_get_free_idx(void)
+{
+ int index;
+
+ for (index = 0; index < MAX_NICS; index++)
+ if (!nd_table[index].used)
+ return index;
+ return -1;
+}
+
+int qemu_show_nic_models(const char *arg, const char *const *models)
+{
+ int i;
+
+ if (!arg || strcmp(arg, "?"))
+ return 0;
+
+ fprintf(stderr, "qemu: Supported NIC models: ");
+ for (i = 0 ; models[i]; i++)
+ fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n');
+ return 1;
+}
+
+void qemu_check_nic_model(NICInfo *nd, const char *model)
+{
+ const char *models[2];
+
+ models[0] = model;
+ models[1] = NULL;
+
+ if (qemu_show_nic_models(nd->model, models))
+ exit(0);
+ if (qemu_find_nic_model(nd, models, model) < 0)
+ exit(1);
+}
+
+int qemu_find_nic_model(NICInfo *nd, const char * const *models,
+ const char *default_model)
+{
+ int i;
+
+ if (!nd->model)
+ nd->model = qemu_strdup(default_model);
+
+ for (i = 0 ; models[i]; i++) {
+ if (strcmp(nd->model, models[i]) == 0)
+ return i;
+ }
+
+ error_report("qemu: Unsupported NIC model: %s", nd->model);
+ return -1;
+}
+
+int net_handle_fd_param(Monitor *mon, const char *param)
+{
+ int fd;
+
+ if (!qemu_isdigit(param[0]) && mon) {
+
+ fd = monitor_get_fd(mon, param);
+ if (fd == -1) {
+ error_report("No file descriptor named %s found", param);
+ return -1;
+ }
+ } else {
+ char *endptr = NULL;
+
+ fd = strtol(param, &endptr, 10);
+ if (*endptr || (fd == 0 && param == endptr)) {
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+static int net_init_nic(QemuOpts *opts,
+ Monitor *mon,
+ const char *name,
+ VLANState *vlan)
+{
+ int idx;
+ NICInfo *nd;
+ const char *netdev;
+
+ idx = nic_get_free_idx();
+ if (idx == -1 || nb_nics >= MAX_NICS) {
+ error_report("Too Many NICs");
+ return -1;
+ }
+
+ nd = &nd_table[idx];
+
+ memset(nd, 0, sizeof(*nd));
+
+ if ((netdev = qemu_opt_get(opts, "netdev"))) {
+ nd->netdev = qemu_find_netdev(netdev);
+ if (!nd->netdev) {
+ error_report("netdev '%s' not found", netdev);
+ return -1;
+ }
+ } else {
+ assert(vlan);
+ nd->vlan = vlan;
+ }
+ if (name) {
+ nd->name = qemu_strdup(name);
+ }
+ if (qemu_opt_get(opts, "model")) {
+ nd->model = qemu_strdup(qemu_opt_get(opts, "model"));
+ }
+ if (qemu_opt_get(opts, "addr")) {
+ nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr"));
+ }
+
+ nd->macaddr[0] = 0x52;
+ nd->macaddr[1] = 0x54;
+ nd->macaddr[2] = 0x00;
+ nd->macaddr[3] = 0x12;
+ nd->macaddr[4] = 0x34;
+ nd->macaddr[5] = 0x56 + idx;
+
+ if (qemu_opt_get(opts, "macaddr") &&
+ net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) {
+ error_report("invalid syntax for ethernet address");
+ return -1;
+ }
+
+ nd->nvectors = qemu_opt_get_number(opts, "vectors",
+ DEV_NVECTORS_UNSPECIFIED);
+ if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
+ (nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
+ error_report("invalid # of vectors: %d", nd->nvectors);
+ return -1;
+ }
+
+ nd->used = 1;
+ nb_nics++;
+
+ return idx;
+}
+
+#define NET_COMMON_PARAMS_DESC \
+ { \
+ .name = "type", \
+ .type = QEMU_OPT_STRING, \
+ .help = "net client type (nic, tap etc.)", \
+ }, { \
+ .name = "vlan", \
+ .type = QEMU_OPT_NUMBER, \
+ .help = "vlan number", \
+ }, { \
+ .name = "name", \
+ .type = QEMU_OPT_STRING, \
+ .help = "identifier for monitor commands", \
+ }
+
+typedef int (*net_client_init_func)(QemuOpts *opts,
+ Monitor *mon,
+ const char *name,
+ VLANState *vlan);
+
+/* magic number, but compiler will warn if too small */
+#define NET_MAX_DESC 20
+
+static const struct {
+ const char *type;
+ net_client_init_func init;
+ QemuOptDesc desc[NET_MAX_DESC];
+} net_client_types[] = {
+ {
+ .type = "none",
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ { /* end of list */ }
+ },
+ }, {
+ .type = "nic",
+ .init = net_init_nic,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "netdev",
+ .type = QEMU_OPT_STRING,
+ .help = "id of -netdev to connect to",
+ },
+ {
+ .name = "macaddr",
+ .type = QEMU_OPT_STRING,
+ .help = "MAC address",
+ }, {
+ .name = "model",
+ .type = QEMU_OPT_STRING,
+ .help = "device model (e1000, rtl8139, virtio etc.)",
+ }, {
+ .name = "addr",
+ .type = QEMU_OPT_STRING,
+ .help = "PCI device address",
+ }, {
+ .name = "vectors",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of MSI-x vectors, 0 to disable MSI-X",
+ },
+ { /* end of list */ }
+ },
+#ifdef CONFIG_SLIRP
+ }, {
+ .type = "user",
+ .init = net_init_slirp,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "hostname",
+ .type = QEMU_OPT_STRING,
+ .help = "client hostname reported by the builtin DHCP server",
+ }, {
+ .name = "restrict",
+ .type = QEMU_OPT_STRING,
+ .help = "isolate the guest from the host (y|yes|n|no)",
+ }, {
+ .name = "ip",
+ .type = QEMU_OPT_STRING,
+ .help = "legacy parameter, use net= instead",
+ }, {
+ .name = "net",
+ .type = QEMU_OPT_STRING,
+ .help = "IP address and optional netmask",
+ }, {
+ .name = "host",
+ .type = QEMU_OPT_STRING,
+ .help = "guest-visible address of the host",
+ }, {
+ .name = "tftp",
+ .type = QEMU_OPT_STRING,
+ .help = "root directory of the built-in TFTP server",
+ }, {
+ .name = "bootfile",
+ .type = QEMU_OPT_STRING,
+ .help = "BOOTP filename, for use with tftp=",
+ }, {
+ .name = "dhcpstart",
+ .type = QEMU_OPT_STRING,
+ .help = "the first of the 16 IPs the built-in DHCP server can assign",
+ }, {
+ .name = "dns",
+ .type = QEMU_OPT_STRING,
+ .help = "guest-visible address of the virtual nameserver",
+ }, {
+ .name = "smb",
+ .type = QEMU_OPT_STRING,
+ .help = "root directory of the built-in SMB server",
+ }, {
+ .name = "smbserver",
+ .type = QEMU_OPT_STRING,
+ .help = "IP address of the built-in SMB server",
+ }, {
+ .name = "hostfwd",
+ .type = QEMU_OPT_STRING,
+ .help = "guest port number to forward incoming TCP or UDP connections",
+ }, {
+ .name = "guestfwd",
+ .type = QEMU_OPT_STRING,
+ .help = "IP address and port to forward guest TCP connections",
+ },
+ { /* end of list */ }
+ },
+#endif
+ }, {
+ .type = "tap",
+ .init = net_init_tap,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "ifname",
+ .type = QEMU_OPT_STRING,
+ .help = "interface name",
+ },
+#ifndef _WIN32
+ {
+ .name = "fd",
+ .type = QEMU_OPT_STRING,
+ .help = "file descriptor of an already opened tap",
+ }, {
+ .name = "script",
+ .type = QEMU_OPT_STRING,
+ .help = "script to initialize the interface",
+ }, {
+ .name = "downscript",
+ .type = QEMU_OPT_STRING,
+ .help = "script to shut down the interface",
+ }, {
+ .name = "sndbuf",
+ .type = QEMU_OPT_SIZE,
+ .help = "send buffer limit"
+ }, {
+ .name = "vnet_hdr",
+ .type = QEMU_OPT_BOOL,
+ .help = "enable the IFF_VNET_HDR flag on the tap interface"
+ }, {
+ .name = "vhost",
+ .type = QEMU_OPT_BOOL,
+ .help = "enable vhost-net network accelerator",
+ }, {
+ .name = "vhostfd",
+ .type = QEMU_OPT_STRING,
+ .help = "file descriptor of an already opened vhost net device",
+ }, {
+ .name = "vhostforce",
+ .type = QEMU_OPT_BOOL,
+ .help = "force vhost on for non-MSIX virtio guests",
+ },
+#endif /* _WIN32 */
+ { /* end of list */ }
+ },
+ }, {
+ .type = "socket",
+ .init = net_init_socket,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "fd",
+ .type = QEMU_OPT_STRING,
+ .help = "file descriptor of an already opened socket",
+ }, {
+ .name = "listen",
+ .type = QEMU_OPT_STRING,
+ .help = "port number, and optional hostname, to listen on",
+ }, {
+ .name = "connect",
+ .type = QEMU_OPT_STRING,
+ .help = "port number, and optional hostname, to connect to",
+ }, {
+ .name = "mcast",
+ .type = QEMU_OPT_STRING,
+ .help = "UDP multicast address and port number",
+ }, {
+ .name = "localaddr",
+ .type = QEMU_OPT_STRING,
+ .help = "source address for multicast packets",
+ },
+ { /* end of list */ }
+ },
+#ifdef CONFIG_VDE
+ }, {
+ .type = "vde",
+ .init = net_init_vde,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "sock",
+ .type = QEMU_OPT_STRING,
+ .help = "socket path",
+ }, {
+ .name = "port",
+ .type = QEMU_OPT_NUMBER,
+ .help = "port number",
+ }, {
+ .name = "group",
+ .type = QEMU_OPT_STRING,
+ .help = "group owner of socket",
+ }, {
+ .name = "mode",
+ .type = QEMU_OPT_NUMBER,
+ .help = "permissions for socket",
+ },
+ { /* end of list */ }
+ },
+#endif
+ }, {
+ .type = "dump",
+ .init = net_init_dump,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "len",
+ .type = QEMU_OPT_SIZE,
+ .help = "per-packet size limit (64k default)",
+ }, {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "dump file path (default is qemu-vlan0.pcap)",
+ },
+ { /* end of list */ }
+ },
+ },
+ { /* end of list */ }
+};
+
+int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
+{
+ const char *name;
+ const char *type;
+ int i;
+
+ type = qemu_opt_get(opts, "type");
+ if (!type) {
+ qerror_report(QERR_MISSING_PARAMETER, "type");
+ return -1;
+ }
+
+ if (is_netdev) {
+ if (strcmp(type, "tap") != 0 &&
+#ifdef CONFIG_SLIRP
+ strcmp(type, "user") != 0 &&
+#endif
+#ifdef CONFIG_VDE
+ strcmp(type, "vde") != 0 &&
+#endif
+ strcmp(type, "socket") != 0) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+ "a netdev backend type");
+ return -1;
+ }
+
+ if (qemu_opt_get(opts, "vlan")) {
+ qerror_report(QERR_INVALID_PARAMETER, "vlan");
+ return -1;
+ }
+ if (qemu_opt_get(opts, "name")) {
+ qerror_report(QERR_INVALID_PARAMETER, "name");
+ return -1;
+ }
+ if (!qemu_opts_id(opts)) {
+ qerror_report(QERR_MISSING_PARAMETER, "id");
+ return -1;
+ }
+ }
+
+ name = qemu_opts_id(opts);
+ if (!name) {
+ name = qemu_opt_get(opts, "name");
+ }
+
+ for (i = 0; net_client_types[i].type != NULL; i++) {
+ if (!strcmp(net_client_types[i].type, type)) {
+ VLANState *vlan = NULL;
+ int ret;
+
+ if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) {
+ return -1;
+ }
+
+ /* Do not add to a vlan if it's a -netdev or a nic with a
+ * netdev= parameter. */
+ if (!(is_netdev ||
+ (strcmp(type, "nic") == 0 && qemu_opt_get(opts, "netdev")))) {
+ vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
+ }
+
+ ret = 0;
+ if (net_client_types[i].init) {
+ ret = net_client_types[i].init(opts, mon, name, vlan);
+ if (ret < 0) {
+ /* TODO push error reporting into init() methods */
+ qerror_report(QERR_DEVICE_INIT_FAILED, type);
+ return -1;
+ }
+ }
+ return ret;
+ }
+ }
+
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+ "a network client type");
+ return -1;
+}
+
+static int net_host_check_device(const char *device)
+{
+ int i;
+ const char *valid_param_list[] = { "tap", "socket", "dump"
+#ifdef CONFIG_SLIRP
+ ,"user"
+#endif
+#ifdef CONFIG_VDE
+ ,"vde"
+#endif
+ };
+ for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
+ if (!strncmp(valid_param_list[i], device,
+ strlen(valid_param_list[i])))
+ return 1;
+ }
+
+ return 0;
+}
+
+void net_host_device_add(Monitor *mon, const QDict *qdict)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ const char *opts_str = qdict_get_try_str(qdict, "opts");
+ QemuOpts *opts;
+
+ if (!net_host_check_device(device)) {
+ monitor_printf(mon, "invalid host network device %s\n", device);
+ return;
+ }
+
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
+ if (!opts) {
+ return;
+ }
+
+ qemu_opt_set(opts, "type", device);
+
+ if (net_client_init(mon, opts, 0) < 0) {
+ monitor_printf(mon, "adding host network device %s failed\n", device);
+ }
+}
+
+void net_host_device_remove(Monitor *mon, const QDict *qdict)
+{
+ VLANClientState *vc;
+ int vlan_id = qdict_get_int(qdict, "vlan_id");
+ const char *device = qdict_get_str(qdict, "device");
+
+ vc = qemu_find_vlan_client_by_name(mon, vlan_id, device);
+ if (!vc) {
+ return;
+ }
+ if (!net_host_check_device(vc->model)) {
+ monitor_printf(mon, "invalid host network device %s\n", device);
+ return;
+ }
+ qemu_del_vlan_client(vc);
+}
+
+int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ QemuOpts *opts;
+ int res;
+
+ opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
+ if (!opts) {
+ return -1;
+ }
+
+ res = net_client_init(mon, opts, 1);
+ if (res < 0) {
+ qemu_opts_del(opts);
+ }
+
+ return res;
+}
+
+int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ VLANClientState *vc;
+
+ vc = qemu_find_netdev(id);
+ if (!vc || vc->info->type == NET_CLIENT_TYPE_NIC) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, id);
+ return -1;
+ }
+ qemu_del_vlan_client(vc);
+ qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id));
+ return 0;
+}
+
+void do_info_network(Monitor *mon)
+{
+ VLANState *vlan;
+ VLANClientState *vc;
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
+
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str);
+ }
+ }
+ monitor_printf(mon, "Devices not on any VLAN:\n");
+ QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+ monitor_printf(mon, " %s: %s", vc->name, vc->info_str);
+ if (vc->peer) {
+ monitor_printf(mon, " peer=%s", vc->peer->name);
+ }
+ monitor_printf(mon, "\n");
+ }
+}
+
+int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ VLANState *vlan;
+ VLANClientState *vc = NULL;
+ const char *name = qdict_get_str(qdict, "name");
+ int up = qdict_get_bool(qdict, "up");
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ if (strcmp(vc->name, name) == 0) {
+ goto done;
+ }
+ }
+ }
+ vc = qemu_find_netdev(name);
+done:
+
+ if (!vc) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, name);
+ return -1;
+ }
+
+ vc->link_down = !up;
+
+ if (vc->info->link_status_changed) {
+ vc->info->link_status_changed(vc);
+ }
+ return 0;
+}
+
+void net_cleanup(void)
+{
+ VLANState *vlan;
+ VLANClientState *vc, *next_vc;
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ QTAILQ_FOREACH_SAFE(vc, &vlan->clients, next, next_vc) {
+ qemu_del_vlan_client(vc);
+ }
+ }
+
+ QTAILQ_FOREACH_SAFE(vc, &non_vlan_clients, next, next_vc) {
+ qemu_del_vlan_client(vc);
+ }
+}
+
+void net_check_clients(void)
+{
+ VLANState *vlan;
+ VLANClientState *vc;
+ int has_nic = 0, has_host_dev = 0;
+
+ QTAILQ_FOREACH(vlan, &vlans, next) {
+ QTAILQ_FOREACH(vc, &vlan->clients, next) {
+ switch (vc->info->type) {
+ case NET_CLIENT_TYPE_NIC:
+ has_nic = 1;
+ break;
+ case NET_CLIENT_TYPE_SLIRP:
+ case NET_CLIENT_TYPE_TAP:
+ case NET_CLIENT_TYPE_SOCKET:
+ case NET_CLIENT_TYPE_VDE:
+ has_host_dev = 1;
+ break;
+ default: ;
+ }
+ }
+ if (has_host_dev && !has_nic)
+ fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
+ if (has_nic && !has_host_dev)
+ fprintf(stderr,
+ "Warning: vlan %d is not connected to host network\n",
+ vlan->id);
+ }
+ QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+ if (!vc->peer) {
+ fprintf(stderr, "Warning: %s %s has no peer\n",
+ vc->info->type == NET_CLIENT_TYPE_NIC ? "nic" : "netdev",
+ vc->name);
+ }
+ }
+}
+
+static int net_init_client(QemuOpts *opts, void *dummy)
+{
+ if (net_client_init(NULL, opts, 0) < 0)
+ return -1;
+ return 0;
+}
+
+static int net_init_netdev(QemuOpts *opts, void *dummy)
+{
+ return net_client_init(NULL, opts, 1);
+}
+
+int net_init_clients(void)
+{
+ QemuOptsList *net = qemu_find_opts("net");
+
+ if (default_net) {
+ /* if no clients, we use a default config */
+ qemu_opts_set(net, NULL, "type", "nic");
+#ifdef CONFIG_SLIRP
+ qemu_opts_set(net, NULL, "type", "user");
+#endif
+ }
+
+ QTAILQ_INIT(&vlans);
+ QTAILQ_INIT(&non_vlan_clients);
+
+ if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
+ return -1;
+
+ if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int net_client_parse(QemuOptsList *opts_list, const char *optarg)
+{
+#if defined(CONFIG_SLIRP)
+ int ret;
+ if (net_slirp_parse_legacy(opts_list, optarg, &ret)) {
+ return ret;
+ }
+#endif
+
+ if (!qemu_opts_parse(opts_list, optarg, 1)) {
+ return -1;
+ }
+
+ default_net = 0;
+ return 0;
+}
diff --git a/net.h b/net.h
new file mode 100644
index 0000000..6ceca50
--- /dev/null
+++ b/net.h
@@ -0,0 +1,184 @@
+#ifndef QEMU_NET_H
+#define QEMU_NET_H
+
+#include "qemu-queue.h"
+#include "qemu-common.h"
+#include "qdict.h"
+#include "qemu-option.h"
+#include "net/queue.h"
+
+struct MACAddr {
+ uint8_t a[6];
+};
+
+/* qdev nic properties */
+
+typedef struct NICConf {
+ MACAddr macaddr;
+ VLANState *vlan;
+ VLANClientState *peer;
+ int32_t bootindex;
+} NICConf;
+
+#define DEFINE_NIC_PROPERTIES(_state, _conf) \
+ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \
+ DEFINE_PROP_VLAN("vlan", _state, _conf.vlan), \
+ DEFINE_PROP_NETDEV("netdev", _state, _conf.peer), \
+ DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1)
+
+/* VLANs support */
+
+typedef enum {
+ NET_CLIENT_TYPE_NONE,
+ NET_CLIENT_TYPE_NIC,
+ NET_CLIENT_TYPE_SLIRP,
+ NET_CLIENT_TYPE_TAP,
+ NET_CLIENT_TYPE_SOCKET,
+ NET_CLIENT_TYPE_VDE,
+ NET_CLIENT_TYPE_DUMP
+} net_client_type;
+
+typedef void (NetPoll)(VLANClientState *, bool enable);
+typedef int (NetCanReceive)(VLANClientState *);
+typedef ssize_t (NetReceive)(VLANClientState *, const uint8_t *, size_t);
+typedef ssize_t (NetReceiveIOV)(VLANClientState *, const struct iovec *, int);
+typedef void (NetCleanup) (VLANClientState *);
+typedef void (LinkStatusChanged)(VLANClientState *);
+
+typedef struct NetClientInfo {
+ net_client_type type;
+ size_t size;
+ NetReceive *receive;
+ NetReceive *receive_raw;
+ NetReceiveIOV *receive_iov;
+ NetCanReceive *can_receive;
+ NetCleanup *cleanup;
+ LinkStatusChanged *link_status_changed;
+ NetPoll *poll;
+} NetClientInfo;
+
+struct VLANClientState {
+ NetClientInfo *info;
+ int link_down;
+ QTAILQ_ENTRY(VLANClientState) next;
+ struct VLANState *vlan;
+ VLANClientState *peer;
+ NetQueue *send_queue;
+ char *model;
+ char *name;
+ char info_str[256];
+ unsigned receive_disabled : 1;
+};
+
+typedef struct NICState {
+ VLANClientState nc;
+ NICConf *conf;
+ void *opaque;
+ bool peer_deleted;
+} NICState;
+
+struct VLANState {
+ int id;
+ QTAILQ_HEAD(, VLANClientState) clients;
+ QTAILQ_ENTRY(VLANState) next;
+ NetQueue *send_queue;
+};
+
+VLANState *qemu_find_vlan(int id, int allocate);
+VLANClientState *qemu_find_netdev(const char *id);
+VLANClientState *qemu_new_net_client(NetClientInfo *info,
+ VLANState *vlan,
+ VLANClientState *peer,
+ const char *model,
+ const char *name);
+NICState *qemu_new_nic(NetClientInfo *info,
+ NICConf *conf,
+ const char *model,
+ const char *name,
+ void *opaque);
+void qemu_del_vlan_client(VLANClientState *vc);
+VLANClientState *qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
+ const char *client_str);
+typedef void (*qemu_nic_foreach)(NICState *nic, void *opaque);
+void qemu_foreach_nic(qemu_nic_foreach func, void *opaque);
+int qemu_can_send_packet(VLANClientState *vc);
+ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov,
+ int iovcnt);
+ssize_t qemu_sendv_packet_async(VLANClientState *vc, const struct iovec *iov,
+ int iovcnt, NetPacketSent *sent_cb);
+void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+ssize_t qemu_send_packet_raw(VLANClientState *vc, const uint8_t *buf, int size);
+ssize_t qemu_send_packet_async(VLANClientState *vc, const uint8_t *buf,
+ int size, NetPacketSent *sent_cb);
+void qemu_purge_queued_packets(VLANClientState *vc);
+void qemu_flush_queued_packets(VLANClientState *vc);
+void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6]);
+void qemu_macaddr_default_if_unset(MACAddr *macaddr);
+int qemu_show_nic_models(const char *arg, const char *const *models);
+void qemu_check_nic_model(NICInfo *nd, const char *model);
+int qemu_find_nic_model(NICInfo *nd, const char * const *models,
+ const char *default_model);
+
+void do_info_network(Monitor *mon);
+int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+/* NIC info */
+
+#define MAX_NICS 8
+
+struct NICInfo {
+ uint8_t macaddr[6];
+ char *model;
+ char *name;
+ char *devaddr;
+ VLANState *vlan;
+ VLANClientState *netdev;
+ int used;
+ int nvectors;
+};
+
+extern int nb_nics;
+extern NICInfo nd_table[MAX_NICS];
+extern int default_net;
+
+/* BT HCI info */
+
+struct HCIInfo {
+ int (*bdaddr_set)(struct HCIInfo *hci, const uint8_t *bd_addr);
+ void (*cmd_send)(struct HCIInfo *hci, const uint8_t *data, int len);
+ void (*sco_send)(struct HCIInfo *hci, const uint8_t *data, int len);
+ void (*acl_send)(struct HCIInfo *hci, const uint8_t *data, int len);
+ void *opaque;
+ void (*evt_recv)(void *opaque, const uint8_t *data, int len);
+ void (*acl_recv)(void *opaque, const uint8_t *data, int len);
+};
+
+struct HCIInfo *qemu_next_hci(void);
+
+/* from net.c */
+extern const char *legacy_tftp_prefix;
+extern const char *legacy_bootp_filename;
+
+int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev);
+int net_client_parse(QemuOptsList *opts_list, const char *str);
+int net_init_clients(void);
+void net_check_clients(void);
+void net_cleanup(void);
+void net_host_device_add(Monitor *mon, const QDict *qdict);
+void net_host_device_remove(Monitor *mon, const QDict *qdict);
+int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+
+#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
+#ifdef __sun__
+#define SMBD_COMMAND "/usr/sfw/sbin/smbd"
+#else
+#define SMBD_COMMAND "/usr/sbin/smbd"
+#endif
+
+void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd);
+
+int net_handle_fd_param(Monitor *mon, const char *param);
+
+#endif
diff --git a/net/checksum.c b/net/checksum.c
new file mode 100644
index 0000000..4046932
--- /dev/null
+++ b/net/checksum.c
@@ -0,0 +1,85 @@
+/*
+ * IP checksumming functions.
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include "net/checksum.h"
+
+#define PROTO_TCP 6
+#define PROTO_UDP 17
+
+uint32_t net_checksum_add(int len, uint8_t *buf)
+{
+ uint32_t sum = 0;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i & 1)
+ sum += (uint32_t)buf[i];
+ else
+ sum += (uint32_t)buf[i] << 8;
+ }
+ return sum;
+}
+
+uint16_t net_checksum_finish(uint32_t sum)
+{
+ while (sum>>16)
+ sum = (sum & 0xFFFF)+(sum >> 16);
+ return ~sum;
+}
+
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+ uint8_t *addrs, uint8_t *buf)
+{
+ uint32_t sum = 0;
+
+ sum += net_checksum_add(length, buf); // payload
+ sum += net_checksum_add(8, addrs); // src + dst address
+ sum += proto + length; // protocol & length
+ return net_checksum_finish(sum);
+}
+
+void net_checksum_calculate(uint8_t *data, int length)
+{
+ int hlen, plen, proto, csum_offset;
+ uint16_t csum;
+
+ if ((data[14] & 0xf0) != 0x40)
+ return; /* not IPv4 */
+ hlen = (data[14] & 0x0f) * 4;
+ plen = (data[16] << 8 | data[17]) - hlen;
+ proto = data[23];
+
+ switch (proto) {
+ case PROTO_TCP:
+ csum_offset = 16;
+ break;
+ case PROTO_UDP:
+ csum_offset = 6;
+ break;
+ default:
+ return;
+ }
+
+ if (plen < csum_offset+2)
+ return;
+
+ data[14+hlen+csum_offset] = 0;
+ data[14+hlen+csum_offset+1] = 0;
+ csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
+ data[14+hlen+csum_offset] = csum >> 8;
+ data[14+hlen+csum_offset+1] = csum & 0xff;
+}
diff --git a/net/checksum.h b/net/checksum.h
new file mode 100644
index 0000000..1f05298
--- /dev/null
+++ b/net/checksum.h
@@ -0,0 +1,29 @@
+/*
+ * IP checksumming functions.
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef QEMU_NET_CHECKSUM_H
+#define QEMU_NET_CHECKSUM_H
+
+#include <stdint.h>
+
+uint32_t net_checksum_add(int len, uint8_t *buf);
+uint16_t net_checksum_finish(uint32_t sum);
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+ uint8_t *addrs, uint8_t *buf);
+void net_checksum_calculate(uint8_t *data, int length);
+
+#endif /* QEMU_NET_CHECKSUM_H */
diff --git a/net/dump.c b/net/dump.c
new file mode 100644
index 0000000..6db7ecf
--- /dev/null
+++ b/net/dump.c
@@ -0,0 +1,159 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "dump.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "qemu-error.h"
+#include "qemu-log.h"
+
+typedef struct DumpState {
+ VLANClientState nc;
+ int fd;
+ int pcap_caplen;
+} DumpState;
+
+#define PCAP_MAGIC 0xa1b2c3d4
+
+struct pcap_file_hdr {
+ uint32_t magic;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t thiszone;
+ uint32_t sigfigs;
+ uint32_t snaplen;
+ uint32_t linktype;
+};
+
+struct pcap_sf_pkthdr {
+ struct {
+ int32_t tv_sec;
+ int32_t tv_usec;
+ } ts;
+ uint32_t caplen;
+ uint32_t len;
+};
+
+static ssize_t dump_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ DumpState *s = DO_UPCAST(DumpState, nc, nc);
+ struct pcap_sf_pkthdr hdr;
+ int64_t ts;
+ int caplen;
+
+ /* Early return in case of previous error. */
+ if (s->fd < 0) {
+ return size;
+ }
+
+ ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec());
+ caplen = size > s->pcap_caplen ? s->pcap_caplen : size;
+
+ hdr.ts.tv_sec = ts / 1000000;
+ hdr.ts.tv_usec = ts % 1000000;
+ hdr.caplen = caplen;
+ hdr.len = size;
+ if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) ||
+ write(s->fd, buf, caplen) != caplen) {
+ qemu_log("-net dump write error - stop dump\n");
+ close(s->fd);
+ s->fd = -1;
+ }
+
+ return size;
+}
+
+static void dump_cleanup(VLANClientState *nc)
+{
+ DumpState *s = DO_UPCAST(DumpState, nc, nc);
+
+ close(s->fd);
+}
+
+static NetClientInfo net_dump_info = {
+ .type = NET_CLIENT_TYPE_DUMP,
+ .size = sizeof(DumpState),
+ .receive = dump_receive,
+ .cleanup = dump_cleanup,
+};
+
+static int net_dump_init(VLANState *vlan, const char *device,
+ const char *name, const char *filename, int len)
+{
+ struct pcap_file_hdr hdr;
+ VLANClientState *nc;
+ DumpState *s;
+ int fd;
+
+ fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644);
+ if (fd < 0) {
+ error_report("-net dump: can't open %s", filename);
+ return -1;
+ }
+
+ hdr.magic = PCAP_MAGIC;
+ hdr.version_major = 2;
+ hdr.version_minor = 4;
+ hdr.thiszone = 0;
+ hdr.sigfigs = 0;
+ hdr.snaplen = len;
+ hdr.linktype = 1;
+
+ if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
+ error_report("-net dump write error: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_dump_info, vlan, NULL, device, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "dump to %s (len=%d)", filename, len);
+
+ s = DO_UPCAST(DumpState, nc, nc);
+
+ s->fd = fd;
+ s->pcap_caplen = len;
+
+ return 0;
+}
+
+int net_init_dump(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+{
+ int len;
+ const char *file;
+ char def_file[128];
+
+ assert(vlan);
+
+ file = qemu_opt_get(opts, "file");
+ if (!file) {
+ snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id);
+ file = def_file;
+ }
+
+ len = qemu_opt_get_size(opts, "len", 65536);
+
+ return net_dump_init(vlan, "dump", name, file, len);
+}
diff --git a/net/dump.h b/net/dump.h
new file mode 100644
index 0000000..fdc91ad
--- /dev/null
+++ b/net/dump.h
@@ -0,0 +1,33 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_NET_DUMP_H
+#define QEMU_NET_DUMP_H
+
+#include "net.h"
+#include "qemu-common.h"
+
+int net_init_dump(QemuOpts *opts, Monitor *mon,
+ const char *name, VLANState *vlan);
+
+#endif /* QEMU_NET_DUMP_H */
diff --git a/net/queue.c b/net/queue.c
new file mode 100644
index 0000000..2ea6cd0
--- /dev/null
+++ b/net/queue.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/queue.h"
+#include "qemu-queue.h"
+
+/* The delivery handler may only return zero if it will call
+ * qemu_net_queue_flush() when it determines that it is once again able
+ * to deliver packets. It must also call qemu_net_queue_purge() in its
+ * cleanup path.
+ *
+ * If a sent callback is provided to send(), the caller must handle a
+ * zero return from the delivery handler by not sending any more packets
+ * until we have invoked the callback. Only in that case will we queue
+ * the packet.
+ *
+ * If a sent callback isn't provided, we just drop the packet to avoid
+ * unbounded queueing.
+ */
+
+struct NetPacket {
+ QTAILQ_ENTRY(NetPacket) entry;
+ VLANClientState *sender;
+ unsigned flags;
+ int size;
+ NetPacketSent *sent_cb;
+ uint8_t data[0];
+};
+
+struct NetQueue {
+ NetPacketDeliver *deliver;
+ NetPacketDeliverIOV *deliver_iov;
+ void *opaque;
+
+ QTAILQ_HEAD(packets, NetPacket) packets;
+
+ unsigned delivering : 1;
+};
+
+NetQueue *qemu_new_net_queue(NetPacketDeliver *deliver,
+ NetPacketDeliverIOV *deliver_iov,
+ void *opaque)
+{
+ NetQueue *queue;
+
+ queue = qemu_mallocz(sizeof(NetQueue));
+
+ queue->deliver = deliver;
+ queue->deliver_iov = deliver_iov;
+ queue->opaque = opaque;
+
+ QTAILQ_INIT(&queue->packets);
+
+ queue->delivering = 0;
+
+ return queue;
+}
+
+void qemu_del_net_queue(NetQueue *queue)
+{
+ NetPacket *packet, *next;
+
+ QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
+ QTAILQ_REMOVE(&queue->packets, packet, entry);
+ qemu_free(packet);
+ }
+
+ qemu_free(queue);
+}
+
+static ssize_t qemu_net_queue_append(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *buf,
+ size_t size,
+ NetPacketSent *sent_cb)
+{
+ NetPacket *packet;
+
+ packet = qemu_malloc(sizeof(NetPacket) + size);
+ packet->sender = sender;
+ packet->flags = flags;
+ packet->size = size;
+ packet->sent_cb = sent_cb;
+ memcpy(packet->data, buf, size);
+
+ QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
+
+ return size;
+}
+
+static ssize_t qemu_net_queue_append_iov(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ NetPacket *packet;
+ size_t max_len = 0;
+ int i;
+
+ for (i = 0; i < iovcnt; i++) {
+ max_len += iov[i].iov_len;
+ }
+
+ packet = qemu_malloc(sizeof(NetPacket) + max_len);
+ packet->sender = sender;
+ packet->sent_cb = sent_cb;
+ packet->flags = flags;
+ packet->size = 0;
+
+ for (i = 0; i < iovcnt; i++) {
+ size_t len = iov[i].iov_len;
+
+ memcpy(packet->data + packet->size, iov[i].iov_base, len);
+ packet->size += len;
+ }
+
+ QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
+
+ return packet->size;
+}
+
+static ssize_t qemu_net_queue_deliver(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size)
+{
+ ssize_t ret = -1;
+
+ queue->delivering = 1;
+ ret = queue->deliver(sender, flags, data, size, queue->opaque);
+ queue->delivering = 0;
+
+ return ret;
+}
+
+static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt)
+{
+ ssize_t ret = -1;
+
+ queue->delivering = 1;
+ ret = queue->deliver_iov(sender, flags, iov, iovcnt, queue->opaque);
+ queue->delivering = 0;
+
+ return ret;
+}
+
+ssize_t qemu_net_queue_send(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ NetPacketSent *sent_cb)
+{
+ ssize_t ret;
+
+ if (queue->delivering) {
+ return qemu_net_queue_append(queue, sender, flags, data, size, NULL);
+ }
+
+ ret = qemu_net_queue_deliver(queue, sender, flags, data, size);
+ if (ret == 0) {
+ qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
+ return 0;
+ }
+
+ qemu_net_queue_flush(queue);
+
+ return ret;
+}
+
+ssize_t qemu_net_queue_send_iov(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ ssize_t ret;
+
+ if (queue->delivering) {
+ return qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, NULL);
+ }
+
+ ret = qemu_net_queue_deliver_iov(queue, sender, flags, iov, iovcnt);
+ if (ret == 0) {
+ qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb);
+ return 0;
+ }
+
+ qemu_net_queue_flush(queue);
+
+ return ret;
+}
+
+void qemu_net_queue_purge(NetQueue *queue, VLANClientState *from)
+{
+ NetPacket *packet, *next;
+
+ QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
+ if (packet->sender == from) {
+ QTAILQ_REMOVE(&queue->packets, packet, entry);
+ qemu_free(packet);
+ }
+ }
+}
+
+void qemu_net_queue_flush(NetQueue *queue)
+{
+ while (!QTAILQ_EMPTY(&queue->packets)) {
+ NetPacket *packet;
+ int ret;
+
+ packet = QTAILQ_FIRST(&queue->packets);
+ QTAILQ_REMOVE(&queue->packets, packet, entry);
+
+ ret = qemu_net_queue_deliver(queue,
+ packet->sender,
+ packet->flags,
+ packet->data,
+ packet->size);
+ if (ret == 0) {
+ QTAILQ_INSERT_HEAD(&queue->packets, packet, entry);
+ break;
+ }
+
+ if (packet->sent_cb) {
+ packet->sent_cb(packet->sender, ret);
+ }
+
+ qemu_free(packet);
+ }
+}
diff --git a/net/queue.h b/net/queue.h
new file mode 100644
index 0000000..a31958e
--- /dev/null
+++ b/net/queue.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_NET_QUEUE_H
+#define QEMU_NET_QUEUE_H
+
+#include "qemu-common.h"
+
+typedef struct NetPacket NetPacket;
+typedef struct NetQueue NetQueue;
+
+typedef void (NetPacketSent) (VLANClientState *sender, ssize_t ret);
+
+typedef ssize_t (NetPacketDeliver) (VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *buf,
+ size_t size,
+ void *opaque);
+
+typedef ssize_t (NetPacketDeliverIOV) (VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque);
+
+#define QEMU_NET_PACKET_FLAG_NONE 0
+#define QEMU_NET_PACKET_FLAG_RAW (1<<0)
+
+NetQueue *qemu_new_net_queue(NetPacketDeliver *deliver,
+ NetPacketDeliverIOV *deliver_iov,
+ void *opaque);
+void qemu_del_net_queue(NetQueue *queue);
+
+ssize_t qemu_net_queue_send(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ NetPacketSent *sent_cb);
+
+ssize_t qemu_net_queue_send_iov(NetQueue *queue,
+ VLANClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb);
+
+void qemu_net_queue_purge(NetQueue *queue, VLANClientState *from);
+void qemu_net_queue_flush(NetQueue *queue);
+
+#endif /* QEMU_NET_QUEUE_H */
diff --git a/net/slirp.c b/net/slirp.c
new file mode 100644
index 0000000..b41c60a
--- /dev/null
+++ b/net/slirp.c
@@ -0,0 +1,771 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "net/slirp.h"
+
+#include "config-host.h"
+
+#ifndef _WIN32
+#include <sys/wait.h>
+#endif
+#include "net.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "qemu_socket.h"
+#include "slirp/libslirp.h"
+
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+ const char *p, *p1;
+ int len;
+ p = *pp;
+ p1 = strchr(p, sep);
+ if (!p1)
+ return -1;
+ len = p1 - p;
+ p1++;
+ if (buf_size > 0) {
+ if (len > buf_size - 1)
+ len = buf_size - 1;
+ memcpy(buf, p, len);
+ buf[len] = '\0';
+ }
+ *pp = p1;
+ return 0;
+}
+
+/* slirp network adapter */
+
+#define SLIRP_CFG_HOSTFWD 1
+#define SLIRP_CFG_LEGACY 2
+
+struct slirp_config_str {
+ struct slirp_config_str *next;
+ int flags;
+ char str[1024];
+ int legacy_format;
+};
+
+typedef struct SlirpState {
+ VLANClientState nc;
+ QTAILQ_ENTRY(SlirpState) entry;
+ Slirp *slirp;
+#ifndef _WIN32
+ char smb_dir[128];
+#endif
+} SlirpState;
+
+static struct slirp_config_str *slirp_configs;
+const char *legacy_tftp_prefix;
+const char *legacy_bootp_filename;
+static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
+ QTAILQ_HEAD_INITIALIZER(slirp_stacks);
+
+static int slirp_hostfwd(SlirpState *s, const char *redir_str,
+ int legacy_format);
+static int slirp_guestfwd(SlirpState *s, const char *config_str,
+ int legacy_format);
+
+#ifndef _WIN32
+static const char *legacy_smb_export;
+
+static int slirp_smb(SlirpState *s, const char *exported_dir,
+ struct in_addr vserver_addr);
+static void slirp_smb_cleanup(SlirpState *s);
+#else
+static inline void slirp_smb_cleanup(SlirpState *s) { }
+#endif
+
+int slirp_can_output(void *opaque)
+{
+ SlirpState *s = opaque;
+
+ return qemu_can_send_packet(&s->nc);
+}
+
+void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
+{
+ SlirpState *s = opaque;
+
+ qemu_send_packet(&s->nc, pkt, pkt_len);
+}
+
+static ssize_t net_slirp_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
+
+ slirp_input(s->slirp, buf, size);
+
+ return size;
+}
+
+static void net_slirp_cleanup(VLANClientState *nc)
+{
+ SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
+
+ slirp_cleanup(s->slirp);
+ slirp_smb_cleanup(s);
+ QTAILQ_REMOVE(&slirp_stacks, s, entry);
+}
+
+static NetClientInfo net_slirp_info = {
+ .type = NET_CLIENT_TYPE_SLIRP,
+ .size = sizeof(SlirpState),
+ .receive = net_slirp_receive,
+ .cleanup = net_slirp_cleanup,
+};
+
+static int net_slirp_init(VLANState *vlan, const char *model,
+ const char *name, int restricted,
+ const char *vnetwork, const char *vhost,
+ const char *vhostname, const char *tftp_export,
+ const char *bootfile, const char *vdhcp_start,
+ const char *vnameserver, const char *smb_export,
+ const char *vsmbserver)
+{
+ /* default settings according to historic slirp */
+ struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
+ struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
+ struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
+ struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
+ struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
+#ifndef _WIN32
+ struct in_addr smbsrv = { .s_addr = 0 };
+#endif
+ VLANClientState *nc;
+ SlirpState *s;
+ char buf[20];
+ uint32_t addr;
+ int shift;
+ char *end;
+ struct slirp_config_str *config;
+
+ if (!tftp_export) {
+ tftp_export = legacy_tftp_prefix;
+ }
+ if (!bootfile) {
+ bootfile = legacy_bootp_filename;
+ }
+
+ if (vnetwork) {
+ if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
+ if (!inet_aton(vnetwork, &net)) {
+ return -1;
+ }
+ addr = ntohl(net.s_addr);
+ if (!(addr & 0x80000000)) {
+ mask.s_addr = htonl(0xff000000); /* class A */
+ } else if ((addr & 0xfff00000) == 0xac100000) {
+ mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
+ } else if ((addr & 0xc0000000) == 0x80000000) {
+ mask.s_addr = htonl(0xffff0000); /* class B */
+ } else if ((addr & 0xffff0000) == 0xc0a80000) {
+ mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
+ } else if ((addr & 0xffff0000) == 0xc6120000) {
+ mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
+ } else if ((addr & 0xe0000000) == 0xe0000000) {
+ mask.s_addr = htonl(0xffffff00); /* class C */
+ } else {
+ mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
+ }
+ } else {
+ if (!inet_aton(buf, &net)) {
+ return -1;
+ }
+ shift = strtol(vnetwork, &end, 10);
+ if (*end != '\0') {
+ if (!inet_aton(vnetwork, &mask)) {
+ return -1;
+ }
+ } else if (shift < 4 || shift > 32) {
+ return -1;
+ } else {
+ mask.s_addr = htonl(0xffffffff << (32 - shift));
+ }
+ }
+ net.s_addr &= mask.s_addr;
+ host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
+ dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
+ dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
+ }
+
+ if (vhost && !inet_aton(vhost, &host)) {
+ return -1;
+ }
+ if ((host.s_addr & mask.s_addr) != net.s_addr) {
+ return -1;
+ }
+
+ if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
+ return -1;
+ }
+ if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
+ dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
+ return -1;
+ }
+
+ if (vnameserver && !inet_aton(vnameserver, &dns)) {
+ return -1;
+ }
+ if ((dns.s_addr & mask.s_addr) != net.s_addr ||
+ dns.s_addr == host.s_addr) {
+ return -1;
+ }
+
+#ifndef _WIN32
+ if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
+ return -1;
+ }
+#endif
+
+ nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n');
+
+ s = DO_UPCAST(SlirpState, nc, nc);
+
+ s->slirp = slirp_init(restricted, net, mask, host, vhostname,
+ tftp_export, bootfile, dhcp, dns, s);
+ QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
+
+ for (config = slirp_configs; config; config = config->next) {
+ if (config->flags & SLIRP_CFG_HOSTFWD) {
+ if (slirp_hostfwd(s, config->str,
+ config->flags & SLIRP_CFG_LEGACY) < 0)
+ goto error;
+ } else {
+ if (slirp_guestfwd(s, config->str,
+ config->flags & SLIRP_CFG_LEGACY) < 0)
+ goto error;
+ }
+ }
+#ifndef _WIN32
+ if (!smb_export) {
+ smb_export = legacy_smb_export;
+ }
+ if (smb_export) {
+ if (slirp_smb(s, smb_export, smbsrv) < 0)
+ goto error;
+ }
+#endif
+
+ return 0;
+
+error:
+ qemu_del_vlan_client(nc);
+ return -1;
+}
+
+static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
+ const char *stack)
+{
+
+ if (vlan) {
+ VLANClientState *nc;
+ nc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack);
+ if (!nc) {
+ return NULL;
+ }
+ if (strcmp(nc->model, "user")) {
+ monitor_printf(mon, "invalid device specified\n");
+ return NULL;
+ }
+ return DO_UPCAST(SlirpState, nc, nc);
+ } else {
+ if (QTAILQ_EMPTY(&slirp_stacks)) {
+ monitor_printf(mon, "user mode network stack not in use\n");
+ return NULL;
+ }
+ return QTAILQ_FIRST(&slirp_stacks);
+ }
+}
+
+void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict)
+{
+ struct in_addr host_addr = { .s_addr = INADDR_ANY };
+ int host_port;
+ char buf[256] = "";
+ const char *src_str, *p;
+ SlirpState *s;
+ int is_udp = 0;
+ int err;
+ const char *arg1 = qdict_get_str(qdict, "arg1");
+ const char *arg2 = qdict_get_try_str(qdict, "arg2");
+ const char *arg3 = qdict_get_try_str(qdict, "arg3");
+
+ if (arg2) {
+ s = slirp_lookup(mon, arg1, arg2);
+ src_str = arg3;
+ } else {
+ s = slirp_lookup(mon, NULL, NULL);
+ src_str = arg1;
+ }
+ if (!s) {
+ return;
+ }
+
+ if (!src_str || !src_str[0])
+ goto fail_syntax;
+
+ p = src_str;
+ get_str_sep(buf, sizeof(buf), &p, ':');
+
+ if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+ is_udp = 0;
+ } else if (!strcmp(buf, "udp")) {
+ is_udp = 1;
+ } else {
+ goto fail_syntax;
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
+ goto fail_syntax;
+ }
+
+ host_port = atoi(p);
+
+ err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp,
+ host_addr, host_port);
+
+ monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
+ err ? "removed" : "not found");
+ return;
+
+ fail_syntax:
+ monitor_printf(mon, "invalid format\n");
+}
+
+static int slirp_hostfwd(SlirpState *s, const char *redir_str,
+ int legacy_format)
+{
+ struct in_addr host_addr = { .s_addr = INADDR_ANY };
+ struct in_addr guest_addr = { .s_addr = 0 };
+ int host_port, guest_port;
+ const char *p;
+ char buf[256];
+ int is_udp;
+ char *end;
+
+ p = redir_str;
+ if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+ is_udp = 0;
+ } else if (!strcmp(buf, "udp")) {
+ is_udp = 1;
+ } else {
+ goto fail_syntax;
+ }
+
+ if (!legacy_format) {
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
+ goto fail_syntax;
+ }
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
+ goto fail_syntax;
+ }
+ host_port = strtol(buf, &end, 0);
+ if (*end != '\0' || host_port < 1 || host_port > 65535) {
+ goto fail_syntax;
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
+ goto fail_syntax;
+ }
+
+ guest_port = strtol(p, &end, 0);
+ if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
+ goto fail_syntax;
+ }
+
+ if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
+ guest_port) < 0) {
+ error_report("could not set up host forwarding rule '%s'",
+ redir_str);
+ return -1;
+ }
+ return 0;
+
+ fail_syntax:
+ error_report("invalid host forwarding rule '%s'", redir_str);
+ return -1;
+}
+
+void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict)
+{
+ const char *redir_str;
+ SlirpState *s;
+ const char *arg1 = qdict_get_str(qdict, "arg1");
+ const char *arg2 = qdict_get_try_str(qdict, "arg2");
+ const char *arg3 = qdict_get_try_str(qdict, "arg3");
+
+ if (arg2) {
+ s = slirp_lookup(mon, arg1, arg2);
+ redir_str = arg3;
+ } else {
+ s = slirp_lookup(mon, NULL, NULL);
+ redir_str = arg1;
+ }
+ if (s) {
+ slirp_hostfwd(s, redir_str, 0);
+ }
+
+}
+
+int net_slirp_redir(const char *redir_str)
+{
+ struct slirp_config_str *config;
+
+ if (QTAILQ_EMPTY(&slirp_stacks)) {
+ config = qemu_malloc(sizeof(*config));
+ pstrcpy(config->str, sizeof(config->str), redir_str);
+ config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
+ config->next = slirp_configs;
+ slirp_configs = config;
+ return 0;
+ }
+
+ return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
+}
+
+#ifndef _WIN32
+
+/* automatic user mode samba server configuration */
+static void slirp_smb_cleanup(SlirpState *s)
+{
+ char cmd[128];
+ int ret;
+
+ if (s->smb_dir[0] != '\0') {
+ snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
+ ret = system(cmd);
+ if (ret == -1 || !WIFEXITED(ret)) {
+ error_report("'%s' failed.", cmd);
+ } else if (WEXITSTATUS(ret)) {
+ error_report("'%s' failed. Error code: %d",
+ cmd, WEXITSTATUS(ret));
+ }
+ s->smb_dir[0] = '\0';
+ }
+}
+
+static int slirp_smb(SlirpState* s, const char *exported_dir,
+ struct in_addr vserver_addr)
+{
+ static int instance;
+ char smb_conf[128];
+ char smb_cmdline[128];
+ FILE *f;
+
+ snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d",
+ (long)getpid(), instance++);
+ if (mkdir(s->smb_dir, 0700) < 0) {
+ error_report("could not create samba server dir '%s'", s->smb_dir);
+ return -1;
+ }
+ snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
+
+ f = fopen(smb_conf, "w");
+ if (!f) {
+ slirp_smb_cleanup(s);
+ error_report("could not create samba server configuration file '%s'",
+ smb_conf);
+ return -1;
+ }
+ fprintf(f,
+ "[global]\n"
+ "private dir=%s\n"
+ "smb ports=0\n"
+ "socket address=127.0.0.1\n"
+ "pid directory=%s\n"
+ "lock directory=%s\n"
+ "log file=%s/log.smbd\n"
+ "smb passwd file=%s/smbpasswd\n"
+ "security = share\n"
+ "[qemu]\n"
+ "path=%s\n"
+ "read only=no\n"
+ "guest ok=yes\n",
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ exported_dir
+ );
+ fclose(f);
+
+ snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
+ SMBD_COMMAND, smb_conf);
+
+ if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) {
+ slirp_smb_cleanup(s);
+ error_report("conflicting/invalid smbserver address");
+ return -1;
+ }
+ return 0;
+}
+
+/* automatic user mode samba server configuration (legacy interface) */
+int net_slirp_smb(const char *exported_dir)
+{
+ struct in_addr vserver_addr = { .s_addr = 0 };
+
+ if (legacy_smb_export) {
+ fprintf(stderr, "-smb given twice\n");
+ return -1;
+ }
+ legacy_smb_export = exported_dir;
+ if (!QTAILQ_EMPTY(&slirp_stacks)) {
+ return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
+ vserver_addr);
+ }
+ return 0;
+}
+
+#endif /* !defined(_WIN32) */
+
+struct GuestFwd {
+ CharDriverState *hd;
+ struct in_addr server;
+ int port;
+ Slirp *slirp;
+};
+
+static int guestfwd_can_read(void *opaque)
+{
+ struct GuestFwd *fwd = opaque;
+ return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
+}
+
+static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
+{
+ struct GuestFwd *fwd = opaque;
+ slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
+}
+
+static int slirp_guestfwd(SlirpState *s, const char *config_str,
+ int legacy_format)
+{
+ struct in_addr server = { .s_addr = 0 };
+ struct GuestFwd *fwd;
+ const char *p;
+ char buf[128];
+ char *end;
+ int port;
+
+ p = config_str;
+ if (legacy_format) {
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ } else {
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (strcmp(buf, "tcp") && buf[0] != '\0') {
+ goto fail_syntax;
+ }
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &server)) {
+ goto fail_syntax;
+ }
+ if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
+ goto fail_syntax;
+ }
+ }
+ port = strtol(buf, &end, 10);
+ if (*end != '\0' || port < 1 || port > 65535) {
+ goto fail_syntax;
+ }
+
+ fwd = qemu_malloc(sizeof(struct GuestFwd));
+ snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port);
+ fwd->hd = qemu_chr_open(buf, p, NULL);
+ if (!fwd->hd) {
+ error_report("could not open guest forwarding device '%s'", buf);
+ qemu_free(fwd);
+ return -1;
+ }
+
+ if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
+ error_report("conflicting/invalid host:port in guest forwarding "
+ "rule '%s'", config_str);
+ qemu_free(fwd);
+ return -1;
+ }
+ fwd->server = server;
+ fwd->port = port;
+ fwd->slirp = s->slirp;
+
+ qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
+ NULL, fwd);
+ return 0;
+
+ fail_syntax:
+ error_report("invalid guest forwarding rule '%s'", config_str);
+ return -1;
+}
+
+void do_info_usernet(Monitor *mon)
+{
+ SlirpState *s;
+
+ QTAILQ_FOREACH(s, &slirp_stacks, entry) {
+ monitor_printf(mon, "VLAN %d (%s):\n",
+ s->nc.vlan ? s->nc.vlan->id : -1,
+ s->nc.name);
+ slirp_connection_info(s->slirp, mon);
+ }
+}
+
+static int net_init_slirp_configs(const char *name, const char *value, void *opaque)
+{
+ struct slirp_config_str *config;
+
+ if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) {
+ return 0;
+ }
+
+ config = qemu_mallocz(sizeof(*config));
+
+ pstrcpy(config->str, sizeof(config->str), value);
+
+ if (!strcmp(name, "hostfwd")) {
+ config->flags = SLIRP_CFG_HOSTFWD;
+ }
+
+ config->next = slirp_configs;
+ slirp_configs = config;
+
+ return 0;
+}
+
+int net_init_slirp(QemuOpts *opts,
+ Monitor *mon,
+ const char *name,
+ VLANState *vlan)
+{
+ struct slirp_config_str *config;
+ const char *vhost;
+ const char *vhostname;
+ const char *vdhcp_start;
+ const char *vnamesrv;
+ const char *tftp_export;
+ const char *bootfile;
+ const char *smb_export;
+ const char *vsmbsrv;
+ char *vnet = NULL;
+ int restricted = 0;
+ int ret;
+
+ vhost = qemu_opt_get(opts, "host");
+ vhostname = qemu_opt_get(opts, "hostname");
+ vdhcp_start = qemu_opt_get(opts, "dhcpstart");
+ vnamesrv = qemu_opt_get(opts, "dns");
+ tftp_export = qemu_opt_get(opts, "tftp");
+ bootfile = qemu_opt_get(opts, "bootfile");
+ smb_export = qemu_opt_get(opts, "smb");
+ vsmbsrv = qemu_opt_get(opts, "smbserver");
+
+ if (qemu_opt_get(opts, "ip")) {
+ const char *ip = qemu_opt_get(opts, "ip");
+ int l = strlen(ip) + strlen("/24") + 1;
+
+ vnet = qemu_malloc(l);
+
+ /* emulate legacy ip= parameter */
+ pstrcpy(vnet, l, ip);
+ pstrcat(vnet, l, "/24");
+ }
+
+ if (qemu_opt_get(opts, "net")) {
+ if (vnet) {
+ qemu_free(vnet);
+ }
+ vnet = qemu_strdup(qemu_opt_get(opts, "net"));
+ }
+
+ if (qemu_opt_get(opts, "restrict") &&
+ qemu_opt_get(opts, "restrict")[0] == 'y') {
+ restricted = 1;
+ }
+
+ qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
+
+ ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
+ vhostname, tftp_export, bootfile, vdhcp_start,
+ vnamesrv, smb_export, vsmbsrv);
+
+ while (slirp_configs) {
+ config = slirp_configs;
+ slirp_configs = config->next;
+ qemu_free(config);
+ }
+
+ qemu_free(vnet);
+
+ return ret;
+}
+
+int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
+{
+ if (strcmp(opts_list->name, "net") != 0 ||
+ strncmp(optarg, "channel,", strlen("channel,")) != 0) {
+ return 0;
+ }
+
+ /* handle legacy -net channel,port:chr */
+ optarg += strlen("channel,");
+
+ if (QTAILQ_EMPTY(&slirp_stacks)) {
+ struct slirp_config_str *config;
+
+ config = qemu_malloc(sizeof(*config));
+ pstrcpy(config->str, sizeof(config->str), optarg);
+ config->flags = SLIRP_CFG_LEGACY;
+ config->next = slirp_configs;
+ slirp_configs = config;
+ *ret = 0;
+ } else {
+ *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
+ }
+
+ return 1;
+}
+
diff --git a/net/slirp.h b/net/slirp.h
new file mode 100644
index 0000000..c17de8e
--- /dev/null
+++ b/net/slirp.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_NET_SLIRP_H
+#define QEMU_NET_SLIRP_H
+
+#include "qemu-common.h"
+#include "qdict.h"
+#include "qemu-option.h"
+
+#ifdef CONFIG_SLIRP
+
+int net_init_slirp(QemuOpts *opts,
+ Monitor *mon,
+ const char *name,
+ VLANState *vlan);
+
+void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict);
+void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict);
+
+int net_slirp_redir(const char *redir_str);
+
+int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret);
+
+int net_slirp_smb(const char *exported_dir);
+
+void do_info_usernet(Monitor *mon);
+
+#endif
+
+#endif /* QEMU_NET_SLIRP_H */
diff --git a/net/socket.c b/net/socket.c
new file mode 100644
index 0000000..3182b37
--- /dev/null
+++ b/net/socket.c
@@ -0,0 +1,600 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "net/socket.h"
+
+#include "config-host.h"
+
+#include "net.h"
+#include "qemu-char.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "qemu-option.h"
+#include "qemu_socket.h"
+
+typedef struct NetSocketState {
+ VLANClientState nc;
+ int fd;
+ int state; /* 0 = getting length, 1 = getting data */
+ unsigned int index;
+ unsigned int packet_len;
+ uint8_t buf[4096];
+ struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
+} NetSocketState;
+
+typedef struct NetSocketListenState {
+ VLANState *vlan;
+ char *model;
+ char *name;
+ int fd;
+} NetSocketListenState;
+
+/* XXX: we consider we can send the whole packet without blocking */
+static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+ uint32_t len;
+ len = htonl(size);
+
+ send_all(s->fd, (const uint8_t *)&len, sizeof(len));
+ return send_all(s->fd, buf, size);
+}
+
+static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+
+ return sendto(s->fd, (const void *)buf, size, 0,
+ (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
+}
+
+static void net_socket_send(void *opaque)
+{
+ NetSocketState *s = opaque;
+ int size, err;
+ unsigned l;
+ uint8_t buf1[4096];
+ const uint8_t *buf;
+
+ size = recv(s->fd, (void *)buf1, sizeof(buf1), 0);
+ if (size < 0) {
+ err = socket_error();
+ if (err != EWOULDBLOCK)
+ goto eoc;
+ } else if (size == 0) {
+ /* end of connection */
+ eoc:
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ return;
+ }
+ buf = buf1;
+ while (size > 0) {
+ /* reassemble a packet from the network */
+ switch(s->state) {
+ case 0:
+ l = 4 - s->index;
+ if (l > size)
+ l = size;
+ memcpy(s->buf + s->index, buf, l);
+ buf += l;
+ size -= l;
+ s->index += l;
+ if (s->index == 4) {
+ /* got length */
+ s->packet_len = ntohl(*(uint32_t *)s->buf);
+ s->index = 0;
+ s->state = 1;
+ }
+ break;
+ case 1:
+ l = s->packet_len - s->index;
+ if (l > size)
+ l = size;
+ if (s->index + l <= sizeof(s->buf)) {
+ memcpy(s->buf + s->index, buf, l);
+ } else {
+ fprintf(stderr, "serious error: oversized packet received,"
+ "connection terminated.\n");
+ s->state = 0;
+ goto eoc;
+ }
+
+ s->index += l;
+ buf += l;
+ size -= l;
+ if (s->index >= s->packet_len) {
+ qemu_send_packet(&s->nc, s->buf, s->packet_len);
+ s->index = 0;
+ s->state = 0;
+ }
+ break;
+ }
+ }
+}
+
+static void net_socket_send_dgram(void *opaque)
+{
+ NetSocketState *s = opaque;
+ int size;
+
+ size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
+ if (size < 0)
+ return;
+ if (size == 0) {
+ /* end of connection */
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ return;
+ }
+ qemu_send_packet(&s->nc, s->buf, size);
+}
+
+static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
+{
+ struct ip_mreq imr;
+ int fd;
+ int val, ret;
+ if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
+ fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
+ inet_ntoa(mcastaddr->sin_addr),
+ (int)ntohl(mcastaddr->sin_addr.s_addr));
+ return -1;
+
+ }
+ fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ return -1;
+ }
+
+ val = 1;
+ ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
+ goto fail;
+ }
+
+ ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
+ if (ret < 0) {
+ perror("bind");
+ goto fail;
+ }
+
+ /* Add host to multicast group */
+ imr.imr_multiaddr = mcastaddr->sin_addr;
+ if (localaddr) {
+ imr.imr_interface = *localaddr;
+ } else {
+ imr.imr_interface.s_addr = htonl(INADDR_ANY);
+ }
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (const char *)&imr, sizeof(struct ip_mreq));
+ if (ret < 0) {
+ perror("setsockopt(IP_ADD_MEMBERSHIP)");
+ goto fail;
+ }
+
+ /* Force mcast msgs to loopback (eg. several QEMUs in same host */
+ val = 1;
+ ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
+ goto fail;
+ }
+
+ /* If a bind address is given, only send packets from that address */
+ if (localaddr != NULL) {
+ ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+ (const char *)localaddr, sizeof(*localaddr));
+ if (ret < 0) {
+ perror("setsockopt(IP_MULTICAST_IF)");
+ goto fail;
+ }
+ }
+
+ socket_set_nonblock(fd);
+ return fd;
+fail:
+ if (fd >= 0)
+ closesocket(fd);
+ return -1;
+}
+
+static void net_socket_cleanup(VLANClientState *nc)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ close(s->fd);
+}
+
+static NetClientInfo net_dgram_socket_info = {
+ .type = NET_CLIENT_TYPE_SOCKET,
+ .size = sizeof(NetSocketState),
+ .receive = net_socket_receive_dgram,
+ .cleanup = net_socket_cleanup,
+};
+
+static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
+ const char *model,
+ const char *name,
+ int fd, int is_connected)
+{
+ struct sockaddr_in saddr;
+ int newfd;
+ socklen_t saddr_len;
+ VLANClientState *nc;
+ NetSocketState *s;
+
+ /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
+ * Because this may be "shared" socket from a "master" process, datagrams would be recv()
+ * by ONLY ONE process: we must "clone" this dgram socket --jjo
+ */
+
+ if (is_connected) {
+ if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
+ /* must be bound */
+ if (saddr.sin_addr.s_addr==0) {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
+ fd);
+ return NULL;
+ }
+ /* clone dgram socket */
+ newfd = net_socket_mcast_create(&saddr, NULL);
+ if (newfd < 0) {
+ /* error already reported by net_socket_mcast_create() */
+ close(fd);
+ return NULL;
+ }
+ /* clone newfd to fd, close newfd */
+ dup2(newfd, fd);
+ close(newfd);
+
+ } else {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
+ fd, strerror(errno));
+ return NULL;
+ }
+ }
+
+ nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "socket: fd=%d (%s mcast=%s:%d)",
+ fd, is_connected ? "cloned" : "",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+
+ s = DO_UPCAST(NetSocketState, nc, nc);
+
+ s->fd = fd;
+
+ qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
+
+ /* mcast: save bound address as dst */
+ if (is_connected) s->dgram_dst=saddr;
+
+ return s;
+}
+
+static void net_socket_connect(void *opaque)
+{
+ NetSocketState *s = opaque;
+ qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
+}
+
+static NetClientInfo net_socket_info = {
+ .type = NET_CLIENT_TYPE_SOCKET,
+ .size = sizeof(NetSocketState),
+ .receive = net_socket_receive,
+ .cleanup = net_socket_cleanup,
+};
+
+static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
+ const char *model,
+ const char *name,
+ int fd, int is_connected)
+{
+ VLANClientState *nc;
+ NetSocketState *s;
+
+ nc = qemu_new_net_client(&net_socket_info, vlan, NULL, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
+
+ s = DO_UPCAST(NetSocketState, nc, nc);
+
+ s->fd = fd;
+
+ if (is_connected) {
+ net_socket_connect(s);
+ } else {
+ qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
+ }
+ return s;
+}
+
+static NetSocketState *net_socket_fd_init(VLANState *vlan,
+ const char *model, const char *name,
+ int fd, int is_connected)
+{
+ int so_type = -1, optlen=sizeof(so_type);
+
+ if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
+ (socklen_t *)&optlen)< 0) {
+ fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd);
+ return NULL;
+ }
+ switch(so_type) {
+ case SOCK_DGRAM:
+ return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected);
+ case SOCK_STREAM:
+ return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
+ default:
+ /* who knows ... this could be a eg. a pty, do warn and continue as stream */
+ fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
+ return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
+ }
+ return NULL;
+}
+
+static void net_socket_accept(void *opaque)
+{
+ NetSocketListenState *s = opaque;
+ NetSocketState *s1;
+ struct sockaddr_in saddr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ break;
+ }
+ }
+ s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1);
+ if (!s1) {
+ closesocket(fd);
+ } else {
+ snprintf(s1->nc.info_str, sizeof(s1->nc.info_str),
+ "socket: connection from %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ }
+}
+
+static int net_socket_listen_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ const char *host_str)
+{
+ NetSocketListenState *s;
+ int fd, val, ret;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ s = qemu_mallocz(sizeof(NetSocketListenState));
+
+ fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ return -1;
+ }
+ ret = listen(fd, 0);
+ if (ret < 0) {
+ perror("listen");
+ return -1;
+ }
+ s->vlan = vlan;
+ s->model = qemu_strdup(model);
+ s->name = name ? qemu_strdup(name) : NULL;
+ s->fd = fd;
+ qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
+ return 0;
+}
+
+static int net_socket_connect_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ const char *host_str)
+{
+ NetSocketState *s;
+ int fd, connected, ret, err;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ connected = 0;
+ for(;;) {
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ err = socket_error();
+ if (err == EINTR || err == EWOULDBLOCK) {
+ } else if (err == EINPROGRESS) {
+ break;
+#ifdef _WIN32
+ } else if (err == WSAEALREADY) {
+ break;
+#endif
+ } else {
+ perror("connect");
+ closesocket(fd);
+ return -1;
+ }
+ } else {
+ connected = 1;
+ break;
+ }
+ }
+ s = net_socket_fd_init(vlan, model, name, fd, connected);
+ if (!s)
+ return -1;
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: connect to %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+}
+
+static int net_socket_mcast_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ const char *host_str,
+ const char *localaddr_str)
+{
+ NetSocketState *s;
+ int fd;
+ struct sockaddr_in saddr;
+ struct in_addr localaddr, *param_localaddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ if (localaddr_str != NULL) {
+ if (inet_aton(localaddr_str, &localaddr) == 0)
+ return -1;
+ param_localaddr = &localaddr;
+ } else {
+ param_localaddr = NULL;
+ }
+
+ fd = net_socket_mcast_create(&saddr, param_localaddr);
+ if (fd < 0)
+ return -1;
+
+ s = net_socket_fd_init(vlan, model, name, fd, 0);
+ if (!s)
+ return -1;
+
+ s->dgram_dst = saddr;
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: mcast=%s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+
+}
+
+int net_init_socket(QemuOpts *opts,
+ Monitor *mon,
+ const char *name,
+ VLANState *vlan)
+{
+ if (qemu_opt_get(opts, "fd")) {
+ int fd;
+
+ if (qemu_opt_get(opts, "listen") ||
+ qemu_opt_get(opts, "connect") ||
+ qemu_opt_get(opts, "mcast") ||
+ qemu_opt_get(opts, "localaddr")) {
+ error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n");
+ return -1;
+ }
+
+ fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
+ if (fd == -1) {
+ return -1;
+ }
+
+ if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) {
+ close(fd);
+ return -1;
+ }
+ } else if (qemu_opt_get(opts, "listen")) {
+ const char *listen;
+
+ if (qemu_opt_get(opts, "fd") ||
+ qemu_opt_get(opts, "connect") ||
+ qemu_opt_get(opts, "mcast") ||
+ qemu_opt_get(opts, "localaddr")) {
+ error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n");
+ return -1;
+ }
+
+ listen = qemu_opt_get(opts, "listen");
+
+ if (net_socket_listen_init(vlan, "socket", name, listen) == -1) {
+ return -1;
+ }
+ } else if (qemu_opt_get(opts, "connect")) {
+ const char *connect;
+
+ if (qemu_opt_get(opts, "fd") ||
+ qemu_opt_get(opts, "listen") ||
+ qemu_opt_get(opts, "mcast") ||
+ qemu_opt_get(opts, "localaddr")) {
+ error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n");
+ return -1;
+ }
+
+ connect = qemu_opt_get(opts, "connect");
+
+ if (net_socket_connect_init(vlan, "socket", name, connect) == -1) {
+ return -1;
+ }
+ } else if (qemu_opt_get(opts, "mcast")) {
+ const char *mcast, *localaddr;
+
+ if (qemu_opt_get(opts, "fd") ||
+ qemu_opt_get(opts, "connect") ||
+ qemu_opt_get(opts, "listen")) {
+ error_report("fd=, connect= and listen= is invalid with mcast=");
+ return -1;
+ }
+
+ mcast = qemu_opt_get(opts, "mcast");
+ localaddr = qemu_opt_get(opts, "localaddr");
+
+ if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) {
+ return -1;
+ }
+ } else {
+ error_report("-socket requires fd=, listen=, connect= or mcast=");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/net/socket.h b/net/socket.h
new file mode 100644
index 0000000..ea46f02
--- /dev/null
+++ b/net/socket.h
@@ -0,0 +1,33 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_NET_SOCKET_H
+#define QEMU_NET_SOCKET_H
+
+#include "net.h"
+#include "qemu-common.h"
+
+int net_init_socket(QemuOpts *opts, Monitor *mon,
+ const char *name, VLANState *vlan);
+
+#endif /* QEMU_NET_SOCKET_H */
diff --git a/net/tap-aix.c b/net/tap-aix.c
new file mode 100644
index 0000000..e19aaba
--- /dev/null
+++ b/net/tap-aix.c
@@ -0,0 +1,61 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+#include <stdio.h>
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ fprintf(stderr, "no tap on AIX\n");
+ return -1;
+}
+
+int tap_set_sndbuf(int fd, QemuOpts *opts)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
new file mode 100644
index 0000000..2f3efde
--- /dev/null
+++ b/net/tap-bsd.c
@@ -0,0 +1,131 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "qemu-error.h"
+
+#ifdef __NetBSD__
+#include <net/if_tap.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#include <libutil.h>
+#else
+#include <util.h>
+#endif
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ int fd;
+ char *dev;
+ struct stat s;
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
+ /* if no ifname is given, always start the search from tap0/tun0. */
+ int i;
+ char dname[100];
+
+ for (i = 0; i < 10; i++) {
+ if (*ifname) {
+ snprintf(dname, sizeof dname, "/dev/%s", ifname);
+ } else {
+#if defined(__OpenBSD__)
+ snprintf(dname, sizeof dname, "/dev/tun%d", i);
+#else
+ snprintf(dname, sizeof dname, "/dev/tap%d", i);
+#endif
+ }
+ TFR(fd = open(dname, O_RDWR));
+ if (fd >= 0) {
+ break;
+ }
+ else if (errno == ENXIO || errno == ENOENT) {
+ break;
+ }
+ if (*ifname) {
+ break;
+ }
+ }
+ if (fd < 0) {
+ error_report("warning: could not open %s (%s): no virtual network emulation",
+ dname, strerror(errno));
+ return -1;
+ }
+#else
+ TFR(fd = open("/dev/tap", O_RDWR));
+ if (fd < 0) {
+ fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n");
+ return -1;
+ }
+#endif
+
+ fstat(fd, &s);
+ dev = devname(s.st_rdev, S_IFCHR);
+ pstrcpy(ifname, ifname_size, dev);
+
+ if (*vnet_hdr) {
+ /* BSD doesn't have IFF_VNET_HDR */
+ *vnet_hdr = 0;
+
+ if (vnet_hdr_required && !*vnet_hdr) {
+ error_report("vnet_hdr=1 requested, but no kernel "
+ "support for IFF_VNET_HDR available");
+ close(fd);
+ return -1;
+ }
+ }
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+
+int tap_set_sndbuf(int fd, QemuOpts *opts)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
new file mode 100644
index 0000000..91dda8e
--- /dev/null
+++ b/net/tap-haiku.c
@@ -0,0 +1,61 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+#include <stdio.h>
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ fprintf(stderr, "no tap on Haiku\n");
+ return -1;
+}
+
+int tap_set_sndbuf(int fd, QemuOpts *opts)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-linux.c b/net/tap-linux.c
new file mode 100644
index 0000000..ff8cad0
--- /dev/null
+++ b/net/tap-linux.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+#include "net/tap-linux.h"
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+
+#define PATH_NET_TUN "/dev/net/tun"
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ struct ifreq ifr;
+ int fd, ret;
+
+ TFR(fd = open(PATH_NET_TUN, O_RDWR));
+ if (fd < 0) {
+ error_report("could not open %s: %m", PATH_NET_TUN);
+ return -1;
+ }
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ if (*vnet_hdr) {
+ unsigned int features;
+
+ if (ioctl(fd, TUNGETFEATURES, &features) == 0 &&
+ features & IFF_VNET_HDR) {
+ *vnet_hdr = 1;
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ } else {
+ *vnet_hdr = 0;
+ }
+
+ if (vnet_hdr_required && !*vnet_hdr) {
+ error_report("vnet_hdr=1 requested, but no kernel "
+ "support for IFF_VNET_HDR available");
+ close(fd);
+ return -1;
+ }
+ }
+
+ if (ifname[0] != '\0')
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
+ else
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret != 0) {
+ error_report("could not configure %s (%s): %m", PATH_NET_TUN, ifr.ifr_name);
+ close(fd);
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, ifr.ifr_name);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+
+/* sndbuf implements a kind of flow control for tap.
+ * Unfortunately when it's enabled, and packets are sent
+ * to other guests on the same host, the receiver
+ * can lock up the transmitter indefinitely.
+ *
+ * To avoid packet loss, sndbuf should be set to a value lower than the tx
+ * queue capacity of any destination network interface.
+ * Ethernet NICs generally have txqueuelen=1000, so 1Mb is
+ * a good value, given a 1500 byte MTU.
+ */
+#define TAP_DEFAULT_SNDBUF 0
+
+int tap_set_sndbuf(int fd, QemuOpts *opts)
+{
+ int sndbuf;
+
+ sndbuf = qemu_opt_get_size(opts, "sndbuf", TAP_DEFAULT_SNDBUF);
+ if (!sndbuf) {
+ sndbuf = INT_MAX;
+ }
+
+ if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && qemu_opt_get(opts, "sndbuf")) {
+ error_report("TUNSETSNDBUF ioctl failed: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
+ error_report("TUNGETIFF ioctl() failed: %s", strerror(errno));
+ return 0;
+ }
+
+ return ifr.ifr_flags & IFF_VNET_HDR;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ unsigned offload;
+
+ offload = TUN_F_CSUM | TUN_F_UFO;
+
+ if (ioctl(fd, TUNSETOFFLOAD, offload) < 0)
+ return 0;
+
+ return 1;
+}
+
+/* Verify that we can assign given length */
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ int orig;
+ if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) {
+ return 0;
+ }
+ if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
+ return 0;
+ }
+ /* Restore original length: we can't handle failure. */
+ if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) {
+ fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
+ strerror(errno));
+ assert(0);
+ return -errno;
+ }
+ return 1;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+ if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
+ fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
+ strerror(errno));
+ assert(0);
+ }
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+ unsigned int offload = 0;
+
+ /* Check if our kernel supports TUNSETOFFLOAD */
+ if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) {
+ return;
+ }
+
+ if (csum) {
+ offload |= TUN_F_CSUM;
+ if (tso4)
+ offload |= TUN_F_TSO4;
+ if (tso6)
+ offload |= TUN_F_TSO6;
+ if ((tso4 || tso6) && ecn)
+ offload |= TUN_F_TSO_ECN;
+ if (ufo)
+ offload |= TUN_F_UFO;
+ }
+
+ if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+ offload &= ~TUN_F_UFO;
+ if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+ fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n",
+ strerror(errno));
+ }
+ }
+}
diff --git a/net/tap-linux.h b/net/tap-linux.h
new file mode 100644
index 0000000..659e981
--- /dev/null
+++ b/net/tap-linux.h
@@ -0,0 +1,63 @@
+/*
+ * Universal TUN/TAP device driver.
+ * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ */
+
+#ifndef QEMU_TAP_H
+#define QEMU_TAP_H
+
+#include <stdint.h>
+#ifdef __linux__
+
+#include <linux/ioctl.h>
+
+/* Ioctl defines */
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+
+#endif
+
+/* TUNSETIFF ifr flags */
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_VNET_HDR 0x4000
+
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
+#define TUN_F_UFO 0x10 /* I can handle UFO packets */
+
+struct virtio_net_hdr
+{
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+};
+
+struct virtio_net_hdr_mrg_rxbuf
+{
+ struct virtio_net_hdr hdr;
+ uint16_t num_buffers; /* Number of merged rx buffers */
+};
+
+#endif /* QEMU_TAP_H */
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
new file mode 100644
index 0000000..c216d28
--- /dev/null
+++ b/net/tap-solaris.c
@@ -0,0 +1,227 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+#include "sysemu.h"
+
+#include <sys/stat.h>
+#include <sys/ethernet.h>
+#include <sys/sockio.h>
+#include <netinet/arp.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h> // must come after ip.h
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <syslog.h>
+#include <stropts.h>
+#include "qemu-error.h"
+
+ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
+{
+ struct strbuf sbuf;
+ int f = 0;
+
+ sbuf.maxlen = maxlen;
+ sbuf.buf = (char *)buf;
+
+ return getmsg(tapfd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
+}
+
+#define TUNNEWPPA (('T'<<16) | 0x0001)
+/*
+ * Allocate TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+static int tap_alloc(char *dev, size_t dev_size)
+{
+ int tap_fd, if_fd, ppa = -1;
+ static int ip_fd = 0;
+ char *ptr;
+
+ static int arp_fd = 0;
+ int ip_muxid, arp_muxid;
+ struct strioctl strioc_if, strioc_ppa;
+ int link_type = I_PLINK;;
+ struct lifreq ifr;
+ char actual_name[32] = "";
+
+ memset(&ifr, 0x0, sizeof(ifr));
+
+ if( *dev ){
+ ptr = dev;
+ while( *ptr && !qemu_isdigit((int)*ptr) ) ptr++;
+ ppa = atoi(ptr);
+ }
+
+ /* Check if IP device was opened */
+ if( ip_fd )
+ close(ip_fd);
+
+ TFR(ip_fd = open("/dev/udp", O_RDWR, 0));
+ if (ip_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)");
+ return -1;
+ }
+
+ TFR(tap_fd = open("/dev/tap", O_RDWR, 0));
+ if (tap_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/tap");
+ return -1;
+ }
+
+ /* Assign a new PPA and get its unit number. */
+ strioc_ppa.ic_cmd = TUNNEWPPA;
+ strioc_ppa.ic_timout = 0;
+ strioc_ppa.ic_len = sizeof(ppa);
+ strioc_ppa.ic_dp = (char *)&ppa;
+ if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0)
+ syslog (LOG_ERR, "Can't assign new interface");
+
+ TFR(if_fd = open("/dev/tap", O_RDWR, 0));
+ if (if_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/tap (2)");
+ return -1;
+ }
+ if(ioctl(if_fd, I_PUSH, "ip") < 0){
+ syslog(LOG_ERR, "Can't push IP module");
+ return -1;
+ }
+
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
+ syslog(LOG_ERR, "Can't get flags\n");
+
+ snprintf (actual_name, 32, "tap%d", ppa);
+ pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
+
+ ifr.lifr_ppa = ppa;
+ /* Assign ppa according to the unit number returned by tun device */
+
+ if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
+ syslog (LOG_ERR, "Can't set PPA %d", ppa);
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
+ syslog (LOG_ERR, "Can't get flags\n");
+ /* Push arp module to if_fd */
+ if (ioctl (if_fd, I_PUSH, "arp") < 0)
+ syslog (LOG_ERR, "Can't push ARP module (2)");
+
+ /* Push arp module to ip_fd */
+ if (ioctl (ip_fd, I_POP, NULL) < 0)
+ syslog (LOG_ERR, "I_POP failed\n");
+ if (ioctl (ip_fd, I_PUSH, "arp") < 0)
+ syslog (LOG_ERR, "Can't push ARP module (3)\n");
+ /* Open arp_fd */
+ TFR(arp_fd = open ("/dev/tap", O_RDWR, 0));
+ if (arp_fd < 0)
+ syslog (LOG_ERR, "Can't open %s\n", "/dev/tap");
+
+ /* Set ifname to arp */
+ strioc_if.ic_cmd = SIOCSLIFNAME;
+ strioc_if.ic_timout = 0;
+ strioc_if.ic_len = sizeof(ifr);
+ strioc_if.ic_dp = (char *)&ifr;
+ if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
+ syslog (LOG_ERR, "Can't set ifname to arp\n");
+ }
+
+ if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){
+ syslog(LOG_ERR, "Can't link TAP device to IP");
+ return -1;
+ }
+
+ if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0)
+ syslog (LOG_ERR, "Can't link TAP device to ARP");
+
+ close (if_fd);
+
+ memset(&ifr, 0x0, sizeof(ifr));
+ pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
+ ifr.lifr_ip_muxid = ip_muxid;
+ ifr.lifr_arp_muxid = arp_muxid;
+
+ if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0)
+ {
+ ioctl (ip_fd, I_PUNLINK , arp_muxid);
+ ioctl (ip_fd, I_PUNLINK, ip_muxid);
+ syslog (LOG_ERR, "Can't set multiplexor id");
+ }
+
+ snprintf(dev, dev_size, "tap%d", ppa);
+ return tap_fd;
+}
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ char dev[10]="";
+ int fd;
+ if( (fd = tap_alloc(dev, sizeof(dev))) < 0 ){
+ fprintf(stderr, "Cannot allocate TAP device\n");
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, dev);
+ if (*vnet_hdr) {
+ /* Solaris doesn't have IFF_VNET_HDR */
+ *vnet_hdr = 0;
+
+ if (vnet_hdr_required && !*vnet_hdr) {
+ error_report("vnet_hdr=1 requested, but no kernel "
+ "support for IFF_VNET_HDR available");
+ close(fd);
+ return -1;
+ }
+ }
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+
+int tap_set_sndbuf(int fd, QemuOpts *opts)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-win32.c b/net/tap-win32.c
new file mode 100644
index 0000000..081904e
--- /dev/null
+++ b/net/tap-win32.c
@@ -0,0 +1,751 @@
+/*
+ * TAP-Win32 -- A kernel driver to provide virtual tap device functionality
+ * on Windows. Originally derived from the CIPE-Win32
+ * project by Damion K. Wilson, with extensive modifications by
+ * James Yonan.
+ *
+ * All source code which derives from the CIPE-Win32 project is
+ * Copyright (C) Damion K. Wilson, 2003, and is released under the
+ * GPL version 2 (see below).
+ *
+ * All other source code is Copyright (C) James Yonan, 2003-2004,
+ * and is released under the GPL version 2 (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/tap.h"
+
+#include "qemu-common.h"
+#include "net.h"
+#include "sysemu.h"
+#include "qemu-error.h"
+#include <stdio.h>
+#include <windows.h>
+#include <winioctl.h>
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+ CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define TAPSUFFIX ".tap"
+
+
+//======================
+// Compile time configuration
+//======================
+
+//#define DEBUG_TAP_WIN32
+
+#define TUN_ASYNCHRONOUS_WRITES 1
+
+#define TUN_BUFFER_SIZE 1560
+#define TUN_MAX_BUFFER_COUNT 32
+
+/*
+ * The data member "buffer" must be the first element in the tun_buffer
+ * structure. See the function, tap_win32_free_buffer.
+ */
+typedef struct tun_buffer_s {
+ unsigned char buffer [TUN_BUFFER_SIZE];
+ unsigned long read_size;
+ struct tun_buffer_s* next;
+} tun_buffer_t;
+
+typedef struct tap_win32_overlapped {
+ HANDLE handle;
+ HANDLE read_event;
+ HANDLE write_event;
+ HANDLE output_queue_semaphore;
+ HANDLE free_list_semaphore;
+ HANDLE tap_semaphore;
+ CRITICAL_SECTION output_queue_cs;
+ CRITICAL_SECTION free_list_cs;
+ OVERLAPPED read_overlapped;
+ OVERLAPPED write_overlapped;
+ tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
+ tun_buffer_t* free_list;
+ tun_buffer_t* output_queue_front;
+ tun_buffer_t* output_queue_back;
+} tap_win32_overlapped_t;
+
+static tap_win32_overlapped_t tap_overlapped;
+
+static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped)
+{
+ tun_buffer_t* buffer = NULL;
+ WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
+ EnterCriticalSection(&overlapped->free_list_cs);
+ buffer = overlapped->free_list;
+// assert(buffer != NULL);
+ overlapped->free_list = buffer->next;
+ LeaveCriticalSection(&overlapped->free_list_cs);
+ buffer->next = NULL;
+ return buffer;
+}
+
+static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+ EnterCriticalSection(&overlapped->free_list_cs);
+ buffer->next = overlapped->free_list;
+ overlapped->free_list = buffer;
+ LeaveCriticalSection(&overlapped->free_list_cs);
+ ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
+}
+
+static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block)
+{
+ tun_buffer_t* buffer = NULL;
+ DWORD result, timeout = block ? INFINITE : 0L;
+
+ // Non-blocking call
+ result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout);
+
+ switch (result)
+ {
+ // The semaphore object was signaled.
+ case WAIT_OBJECT_0:
+ EnterCriticalSection(&overlapped->output_queue_cs);
+
+ buffer = overlapped->output_queue_front;
+ overlapped->output_queue_front = buffer->next;
+
+ if(overlapped->output_queue_front == NULL) {
+ overlapped->output_queue_back = NULL;
+ }
+
+ LeaveCriticalSection(&overlapped->output_queue_cs);
+ break;
+
+ // Semaphore was nonsignaled, so a time-out occurred.
+ case WAIT_TIMEOUT:
+ // Cannot open another window.
+ break;
+ }
+
+ return buffer;
+}
+
+static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped)
+{
+ return get_buffer_from_output_queue(overlapped, 0);
+}
+
+static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+ EnterCriticalSection(&overlapped->output_queue_cs);
+
+ if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
+ overlapped->output_queue_front = overlapped->output_queue_back = buffer;
+ } else {
+ buffer->next = NULL;
+ overlapped->output_queue_back->next = buffer;
+ overlapped->output_queue_back = buffer;
+ }
+
+ LeaveCriticalSection(&overlapped->output_queue_cs);
+
+ ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
+}
+
+
+static int is_tap_win32_dev(const char *guid)
+{
+ HKEY netcard_key;
+ LONG status;
+ DWORD len;
+ int i = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ ADAPTER_KEY,
+ 0,
+ KEY_READ,
+ &netcard_key);
+
+ if (status != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ for (;;) {
+ char enum_name[256];
+ char unit_string[256];
+ HKEY unit_key;
+ char component_id_string[] = "ComponentId";
+ char component_id[256];
+ char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+ char net_cfg_instance_id[256];
+ DWORD data_type;
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ netcard_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ snprintf (unit_string, sizeof(unit_string), "%s\\%s",
+ ADAPTER_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ unit_string,
+ 0,
+ KEY_READ,
+ &unit_key);
+
+ if (status != ERROR_SUCCESS) {
+ return FALSE;
+ } else {
+ len = sizeof (component_id);
+ status = RegQueryValueEx(
+ unit_key,
+ component_id_string,
+ NULL,
+ &data_type,
+ (LPBYTE)component_id,
+ &len);
+
+ if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
+ len = sizeof (net_cfg_instance_id);
+ status = RegQueryValueEx(
+ unit_key,
+ net_cfg_instance_id_string,
+ NULL,
+ &data_type,
+ (LPBYTE)net_cfg_instance_id,
+ &len);
+
+ if (status == ERROR_SUCCESS && data_type == REG_SZ) {
+ if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
+ !strcmp (net_cfg_instance_id, guid)) {
+ RegCloseKey (unit_key);
+ RegCloseKey (netcard_key);
+ return TRUE;
+ }
+ }
+ }
+ RegCloseKey (unit_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (netcard_key);
+ return FALSE;
+}
+
+static int get_device_guid(
+ char *name,
+ int name_size,
+ char *actual_name,
+ int actual_name_size)
+{
+ LONG status;
+ HKEY control_net_key;
+ DWORD len;
+ int i = 0;
+ int stop = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ NETWORK_CONNECTIONS_KEY,
+ 0,
+ KEY_READ,
+ &control_net_key);
+
+ if (status != ERROR_SUCCESS) {
+ return -1;
+ }
+
+ while (!stop)
+ {
+ char enum_name[256];
+ char connection_string[256];
+ HKEY connection_key;
+ char name_data[256];
+ DWORD name_type;
+ const char name_string[] = "Name";
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ control_net_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS) {
+ return -1;
+ }
+
+ snprintf(connection_string,
+ sizeof(connection_string),
+ "%s\\%s\\Connection",
+ NETWORK_CONNECTIONS_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ connection_string,
+ 0,
+ KEY_READ,
+ &connection_key);
+
+ if (status == ERROR_SUCCESS) {
+ len = sizeof (name_data);
+ status = RegQueryValueEx(
+ connection_key,
+ name_string,
+ NULL,
+ &name_type,
+ (LPBYTE)name_data,
+ &len);
+
+ if (status != ERROR_SUCCESS || name_type != REG_SZ) {
+ return -1;
+ }
+ else {
+ if (is_tap_win32_dev(enum_name)) {
+ snprintf(name, name_size, "%s", enum_name);
+ if (actual_name) {
+ if (strcmp(actual_name, "") != 0) {
+ if (strcmp(name_data, actual_name) != 0) {
+ RegCloseKey (connection_key);
+ ++i;
+ continue;
+ }
+ }
+ else {
+ snprintf(actual_name, actual_name_size, "%s", name_data);
+ }
+ }
+ stop = 1;
+ }
+ }
+
+ RegCloseKey (connection_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (control_net_key);
+
+ if (stop == 0)
+ return -1;
+
+ return 0;
+}
+
+static int tap_win32_set_status(HANDLE handle, int status)
+{
+ unsigned long len = 0;
+
+ return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+ &status, sizeof (status),
+ &status, sizeof (status), &len, NULL);
+}
+
+static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
+{
+ overlapped->handle = handle;
+
+ overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ overlapped->read_overlapped.Offset = 0;
+ overlapped->read_overlapped.OffsetHigh = 0;
+ overlapped->read_overlapped.hEvent = overlapped->read_event;
+
+ overlapped->write_overlapped.Offset = 0;
+ overlapped->write_overlapped.OffsetHigh = 0;
+ overlapped->write_overlapped.hEvent = overlapped->write_event;
+
+ InitializeCriticalSection(&overlapped->output_queue_cs);
+ InitializeCriticalSection(&overlapped->free_list_cs);
+
+ overlapped->output_queue_semaphore = CreateSemaphore(
+ NULL, // default security attributes
+ 0, // initial count
+ TUN_MAX_BUFFER_COUNT, // maximum count
+ NULL); // unnamed semaphore
+
+ if(!overlapped->output_queue_semaphore) {
+ fprintf(stderr, "error creating output queue semaphore!\n");
+ }
+
+ overlapped->free_list_semaphore = CreateSemaphore(
+ NULL, // default security attributes
+ TUN_MAX_BUFFER_COUNT, // initial count
+ TUN_MAX_BUFFER_COUNT, // maximum count
+ NULL); // unnamed semaphore
+
+ if(!overlapped->free_list_semaphore) {
+ fprintf(stderr, "error creating free list semaphore!\n");
+ }
+
+ overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
+
+ {
+ unsigned index;
+ for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
+ tun_buffer_t* element = &overlapped->buffers[index];
+ element->next = overlapped->free_list;
+ overlapped->free_list = element;
+ }
+ }
+ /* To count buffers, initially no-signal. */
+ overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL);
+ if(!overlapped->tap_semaphore)
+ fprintf(stderr, "error creating tap_semaphore.\n");
+}
+
+static int tap_win32_write(tap_win32_overlapped_t *overlapped,
+ const void *buffer, unsigned long size)
+{
+ unsigned long write_size;
+ BOOL result;
+ DWORD error;
+
+ result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
+ &write_size, FALSE);
+
+ if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
+ WaitForSingleObject(overlapped->write_event, INFINITE);
+
+ result = WriteFile(overlapped->handle, buffer, size,
+ &write_size, &overlapped->write_overlapped);
+
+ if (!result) {
+ switch (error = GetLastError())
+ {
+ case ERROR_IO_PENDING:
+#ifndef TUN_ASYNCHRONOUS_WRITES
+ WaitForSingleObject(overlapped->write_event, INFINITE);
+#endif
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
+{
+ tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
+ unsigned long read_size;
+ BOOL result;
+ DWORD dwError;
+ tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
+
+
+ for (;;) {
+ result = ReadFile(overlapped->handle,
+ buffer->buffer,
+ sizeof(buffer->buffer),
+ &read_size,
+ &overlapped->read_overlapped);
+ if (!result) {
+ dwError = GetLastError();
+ if (dwError == ERROR_IO_PENDING) {
+ WaitForSingleObject(overlapped->read_event, INFINITE);
+ result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
+ &read_size, FALSE);
+ if (!result) {
+#ifdef DEBUG_TAP_WIN32
+ LPVOID lpBuffer;
+ dwError = GetLastError();
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpBuffer, 0, NULL );
+ fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
+ LocalFree( lpBuffer );
+#endif
+ }
+ } else {
+#ifdef DEBUG_TAP_WIN32
+ LPVOID lpBuffer;
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpBuffer, 0, NULL );
+ fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
+ LocalFree( lpBuffer );
+#endif
+ }
+ }
+
+ if(read_size > 0) {
+ buffer->read_size = read_size;
+ put_buffer_on_output_queue(overlapped, buffer);
+ ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL);
+ buffer = get_buffer_from_free_list(overlapped);
+ }
+ }
+
+ return 0;
+}
+
+static int tap_win32_read(tap_win32_overlapped_t *overlapped,
+ uint8_t **pbuf, int max_size)
+{
+ int size = 0;
+
+ tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
+
+ if(buffer != NULL) {
+ *pbuf = buffer->buffer;
+ size = (int)buffer->read_size;
+ if(size > max_size) {
+ size = max_size;
+ }
+ }
+
+ return size;
+}
+
+static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
+ uint8_t *pbuf)
+{
+ tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
+ put_buffer_on_free_list(overlapped, buffer);
+}
+
+static int tap_win32_open(tap_win32_overlapped_t **phandle,
+ const char *prefered_name)
+{
+ char device_path[256];
+ char device_guid[0x100];
+ int rc;
+ HANDLE handle;
+ BOOL bret;
+ char name_buffer[0x100] = {0, };
+ struct {
+ unsigned long major;
+ unsigned long minor;
+ unsigned long debug;
+ } version;
+ DWORD version_len;
+ DWORD idThread;
+
+ if (prefered_name != NULL)
+ snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
+
+ rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
+ if (rc)
+ return -1;
+
+ snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+
+ handle = CreateFile (
+ device_path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0 );
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+ &version, sizeof (version),
+ &version, sizeof (version), &version_len, NULL);
+
+ if (bret == FALSE) {
+ CloseHandle(handle);
+ return -1;
+ }
+
+ if (!tap_win32_set_status(handle, TRUE)) {
+ return -1;
+ }
+
+ tap_win32_overlapped_init(&tap_overlapped, handle);
+
+ *phandle = &tap_overlapped;
+
+ CreateThread(NULL, 0, tap_win32_thread_entry,
+ (LPVOID)&tap_overlapped, 0, &idThread);
+ return 0;
+}
+
+/********************************************/
+
+ typedef struct TAPState {
+ VLANClientState nc;
+ tap_win32_overlapped_t *handle;
+ } TAPState;
+
+static void tap_cleanup(VLANClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL);
+
+ /* FIXME: need to kill thread and close file handle:
+ tap_win32_close(s);
+ */
+}
+
+static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ return tap_win32_write(s->handle, buf, size);
+}
+
+static void tap_win32_send(void *opaque)
+{
+ TAPState *s = opaque;
+ uint8_t *buf;
+ int max_size = 4096;
+ int size;
+
+ size = tap_win32_read(s->handle, &buf, max_size);
+ if (size > 0) {
+ qemu_send_packet(&s->nc, buf, size);
+ tap_win32_free_buffer(s->handle, buf);
+ }
+}
+
+static NetClientInfo net_tap_win32_info = {
+ .type = NET_CLIENT_TYPE_TAP,
+ .size = sizeof(TAPState),
+ .receive = tap_receive,
+ .cleanup = tap_cleanup,
+};
+
+static int tap_win32_init(VLANState *vlan, const char *model,
+ const char *name, const char *ifname)
+{
+ VLANClientState *nc;
+ TAPState *s;
+ tap_win32_overlapped_t *handle;
+
+ if (tap_win32_open(&handle, ifname) < 0) {
+ printf("tap: Could not open '%s'\n", ifname);
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_tap_win32_info, vlan, NULL, model, name);
+
+ s = DO_UPCAST(TAPState, nc, nc);
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "tap: ifname=%s", ifname);
+
+ s->handle = handle;
+
+ qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
+
+ return 0;
+}
+
+int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+{
+ const char *ifname;
+
+ ifname = qemu_opt_get(opts, "ifname");
+
+ if (!ifname) {
+ error_report("tap: no interface name");
+ return -1;
+ }
+
+ if (tap_win32_init(vlan, "tap", name, ifname) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int tap_has_ufo(VLANClientState *vc)
+{
+ return 0;
+}
+
+int tap_has_vnet_hdr(VLANClientState *vc)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr)
+{
+}
+
+void tap_set_offload(VLANClientState *vc, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
+
+struct vhost_net *tap_get_vhost_net(VLANClientState *nc)
+{
+ return NULL;
+}
diff --git a/net/tap.c b/net/tap.c
new file mode 100644
index 0000000..b8cd252
--- /dev/null
+++ b/net/tap.c
@@ -0,0 +1,525 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/tap.h"
+
+#include "config-host.h"
+
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "net.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+
+#include "net/tap-linux.h"
+
+#include "hw/vhost_net.h"
+
+/* Maximum GSO packet size (64k) plus plenty of room for
+ * the ethernet and virtio_net headers
+ */
+#define TAP_BUFSIZE (4096 + 65536)
+
+typedef struct TAPState {
+ VLANClientState nc;
+ int fd;
+ char down_script[1024];
+ char down_script_arg[128];
+ uint8_t buf[TAP_BUFSIZE];
+ unsigned int read_poll : 1;
+ unsigned int write_poll : 1;
+ unsigned int using_vnet_hdr : 1;
+ unsigned int has_ufo: 1;
+ VHostNetState *vhost_net;
+ unsigned host_vnet_hdr_len;
+} TAPState;
+
+static int launch_script(const char *setup_script, const char *ifname, int fd);
+
+static int tap_can_send(void *opaque);
+static void tap_send(void *opaque);
+static void tap_writable(void *opaque);
+
+static void tap_update_fd_handler(TAPState *s)
+{
+ qemu_set_fd_handler2(s->fd,
+ s->read_poll ? tap_can_send : NULL,
+ s->read_poll ? tap_send : NULL,
+ s->write_poll ? tap_writable : NULL,
+ s);
+}
+
+static void tap_read_poll(TAPState *s, int enable)
+{
+ s->read_poll = !!enable;
+ tap_update_fd_handler(s);
+}
+
+static void tap_write_poll(TAPState *s, int enable)
+{
+ s->write_poll = !!enable;
+ tap_update_fd_handler(s);
+}
+
+static void tap_writable(void *opaque)
+{
+ TAPState *s = opaque;
+
+ tap_write_poll(s, 0);
+
+ qemu_flush_queued_packets(&s->nc);
+}
+
+static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)
+{
+ ssize_t len;
+
+ do {
+ len = writev(s->fd, iov, iovcnt);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN) {
+ tap_write_poll(s, 1);
+ return 0;
+ }
+
+ return len;
+}
+
+static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
+ int iovcnt)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ const struct iovec *iovp = iov;
+ struct iovec iov_copy[iovcnt + 1];
+ struct virtio_net_hdr_mrg_rxbuf hdr = { };
+
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ iov_copy[0].iov_base = &hdr;
+ iov_copy[0].iov_len = s->host_vnet_hdr_len;
+ memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
+ iovp = iov_copy;
+ iovcnt++;
+ }
+
+ return tap_write_packet(s, iovp, iovcnt);
+}
+
+static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ struct iovec iov[2];
+ int iovcnt = 0;
+ struct virtio_net_hdr_mrg_rxbuf hdr = { };
+
+ if (s->host_vnet_hdr_len) {
+ iov[iovcnt].iov_base = &hdr;
+ iov[iovcnt].iov_len = s->host_vnet_hdr_len;
+ iovcnt++;
+ }
+
+ iov[iovcnt].iov_base = (char *)buf;
+ iov[iovcnt].iov_len = size;
+ iovcnt++;
+
+ return tap_write_packet(s, iov, iovcnt);
+}
+
+static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ struct iovec iov[1];
+
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ return tap_receive_raw(nc, buf, size);
+ }
+
+ iov[0].iov_base = (char *)buf;
+ iov[0].iov_len = size;
+
+ return tap_write_packet(s, iov, 1);
+}
+
+static int tap_can_send(void *opaque)
+{
+ TAPState *s = opaque;
+
+ return qemu_can_send_packet(&s->nc);
+}
+
+#ifndef __sun__
+ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
+{
+ return read(tapfd, buf, maxlen);
+}
+#endif
+
+static void tap_send_completed(VLANClientState *nc, ssize_t len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ tap_read_poll(s, 1);
+}
+
+static void tap_send(void *opaque)
+{
+ TAPState *s = opaque;
+ int size;
+
+ do {
+ uint8_t *buf = s->buf;
+
+ size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
+ if (size <= 0) {
+ break;
+ }
+
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ buf += s->host_vnet_hdr_len;
+ size -= s->host_vnet_hdr_len;
+ }
+
+ size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
+ if (size == 0) {
+ tap_read_poll(s, 0);
+ }
+ } while (size > 0 && qemu_can_send_packet(&s->nc));
+}
+
+int tap_has_ufo(VLANClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+
+ return s->has_ufo;
+}
+
+int tap_has_vnet_hdr(VLANClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+
+ return !!s->host_vnet_hdr_len;
+}
+
+int tap_has_vnet_hdr_len(VLANClientState *nc, int len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+
+ return tap_probe_vnet_hdr_len(s->fd, len);
+}
+
+void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
+ len == sizeof(struct virtio_net_hdr));
+
+ tap_fd_set_vnet_hdr_len(s->fd, len);
+ s->host_vnet_hdr_len = len;
+}
+
+void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ using_vnet_hdr = using_vnet_hdr != 0;
+
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
+
+ s->using_vnet_hdr = using_vnet_hdr;
+}
+
+void tap_set_offload(VLANClientState *nc, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ if (s->fd < 0) {
+ return;
+ }
+
+ tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
+}
+
+static void tap_cleanup(VLANClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ if (s->vhost_net) {
+ vhost_net_cleanup(s->vhost_net);
+ s->vhost_net = NULL;
+ }
+
+ qemu_purge_queued_packets(nc);
+
+ if (s->down_script[0])
+ launch_script(s->down_script, s->down_script_arg, s->fd);
+
+ tap_read_poll(s, 0);
+ tap_write_poll(s, 0);
+ close(s->fd);
+ s->fd = -1;
+}
+
+static void tap_poll(VLANClientState *nc, bool enable)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ tap_read_poll(s, enable);
+ tap_write_poll(s, enable);
+}
+
+int tap_get_fd(VLANClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ return s->fd;
+}
+
+/* fd support */
+
+static NetClientInfo net_tap_info = {
+ .type = NET_CLIENT_TYPE_TAP,
+ .size = sizeof(TAPState),
+ .receive = tap_receive,
+ .receive_raw = tap_receive_raw,
+ .receive_iov = tap_receive_iov,
+ .poll = tap_poll,
+ .cleanup = tap_cleanup,
+};
+
+static TAPState *net_tap_fd_init(VLANState *vlan,
+ const char *model,
+ const char *name,
+ int fd,
+ int vnet_hdr)
+{
+ VLANClientState *nc;
+ TAPState *s;
+
+ nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name);
+
+ s = DO_UPCAST(TAPState, nc, nc);
+
+ s->fd = fd;
+ s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
+ s->using_vnet_hdr = 0;
+ s->has_ufo = tap_probe_has_ufo(s->fd);
+ tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
+ tap_read_poll(s, 1);
+ s->vhost_net = NULL;
+ return s;
+}
+
+static int launch_script(const char *setup_script, const char *ifname, int fd)
+{
+ sigset_t oldmask, mask;
+ int pid, status;
+ char *args[3];
+ char **parg;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+
+ /* try to launch network script */
+ pid = fork();
+ if (pid == 0) {
+ int open_max = sysconf(_SC_OPEN_MAX), i;
+
+ for (i = 0; i < open_max; i++) {
+ if (i != STDIN_FILENO &&
+ i != STDOUT_FILENO &&
+ i != STDERR_FILENO &&
+ i != fd) {
+ close(i);
+ }
+ }
+ parg = args;
+ *parg++ = (char *)setup_script;
+ *parg++ = (char *)ifname;
+ *parg = NULL;
+ execv(setup_script, args);
+ _exit(1);
+ } else if (pid > 0) {
+ while (waitpid(pid, &status, 0) != pid) {
+ /* loop */
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return 0;
+ }
+ }
+ fprintf(stderr, "%s: could not launch network script\n", setup_script);
+ return -1;
+}
+
+static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
+{
+ int fd, vnet_hdr_required;
+ char ifname[128] = {0,};
+ const char *setup_script;
+
+ if (qemu_opt_get(opts, "ifname")) {
+ pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
+ }
+
+ *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
+ if (qemu_opt_get(opts, "vnet_hdr")) {
+ vnet_hdr_required = *vnet_hdr;
+ } else {
+ vnet_hdr_required = 0;
+ }
+
+ TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required));
+ if (fd < 0) {
+ return -1;
+ }
+
+ setup_script = qemu_opt_get(opts, "script");
+ if (setup_script &&
+ setup_script[0] != '\0' &&
+ strcmp(setup_script, "no") != 0 &&
+ launch_script(setup_script, ifname, fd)) {
+ close(fd);
+ return -1;
+ }
+
+ qemu_opt_set(opts, "ifname", ifname);
+
+ return fd;
+}
+
+int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+{
+ TAPState *s;
+ int fd, vnet_hdr = 0;
+
+ if (qemu_opt_get(opts, "fd")) {
+ if (qemu_opt_get(opts, "ifname") ||
+ qemu_opt_get(opts, "script") ||
+ qemu_opt_get(opts, "downscript") ||
+ qemu_opt_get(opts, "vnet_hdr")) {
+ error_report("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=");
+ return -1;
+ }
+
+ fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+ } else {
+ if (!qemu_opt_get(opts, "script")) {
+ qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
+ }
+
+ if (!qemu_opt_get(opts, "downscript")) {
+ qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT);
+ }
+
+ fd = net_tap_init(opts, &vnet_hdr);
+ if (fd == -1) {
+ return -1;
+ }
+ }
+
+ s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
+ if (!s) {
+ close(fd);
+ return -1;
+ }
+
+ if (tap_set_sndbuf(s->fd, opts) < 0) {
+ return -1;
+ }
+
+ if (qemu_opt_get(opts, "fd")) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
+ } else {
+ const char *ifname, *script, *downscript;
+
+ ifname = qemu_opt_get(opts, "ifname");
+ script = qemu_opt_get(opts, "script");
+ downscript = qemu_opt_get(opts, "downscript");
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "ifname=%s,script=%s,downscript=%s",
+ ifname, script, downscript);
+
+ if (strcmp(downscript, "no") != 0) {
+ snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
+ snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
+ }
+ }
+
+ if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd") ||
+ qemu_opt_get_bool(opts, "vhostforce", false))) {
+ int vhostfd, r;
+ bool force = qemu_opt_get_bool(opts, "vhostforce", false);
+ if (qemu_opt_get(opts, "vhostfd")) {
+ r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd"));
+ if (r == -1) {
+ return -1;
+ }
+ vhostfd = r;
+ } else {
+ vhostfd = -1;
+ }
+ s->vhost_net = vhost_net_init(&s->nc, vhostfd, force);
+ if (!s->vhost_net) {
+ error_report("vhost-net requested but could not be initialized");
+ return -1;
+ }
+ } else if (qemu_opt_get(opts, "vhostfd")) {
+ error_report("vhostfd= is not valid without vhost");
+ return -1;
+ }
+
+ return 0;
+}
+
+VHostNetState *tap_get_vhost_net(VLANClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ return s->vhost_net;
+}
diff --git a/net/tap.h b/net/tap.h
new file mode 100644
index 0000000..e44bd2b
--- /dev/null
+++ b/net/tap.h
@@ -0,0 +1,60 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_NET_TAP_H
+#define QEMU_NET_TAP_H
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+
+#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
+
+int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan);
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
+
+ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
+
+int tap_has_ufo(VLANClientState *vc);
+int tap_has_vnet_hdr(VLANClientState *vc);
+int tap_has_vnet_hdr_len(VLANClientState *vc, int len);
+void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr);
+void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, int ecn, int ufo);
+void tap_set_vnet_hdr_len(VLANClientState *vc, int len);
+
+int tap_set_sndbuf(int fd, QemuOpts *opts);
+int tap_probe_vnet_hdr(int fd);
+int tap_probe_vnet_hdr_len(int fd, int len);
+int tap_probe_has_ufo(int fd);
+void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo);
+void tap_fd_set_vnet_hdr_len(int fd, int len);
+
+int tap_get_fd(VLANClientState *vc);
+
+struct vhost_net;
+struct vhost_net *tap_get_vhost_net(VLANClientState *vc);
+
+#endif /* QEMU_NET_TAP_H */
diff --git a/net/util.c b/net/util.c
new file mode 100644
index 0000000..1e9afbc
--- /dev/null
+++ b/net/util.c
@@ -0,0 +1,60 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "net/util.h"
+#include <errno.h>
+#include <stdlib.h>
+
+int net_parse_macaddr(uint8_t *macaddr, const char *p)
+{
+ int i;
+ char *last_char;
+ long int offset;
+
+ errno = 0;
+ offset = strtol(p, &last_char, 0);
+ if (errno == 0 && *last_char == '\0' &&
+ offset >= 0 && offset <= 0xFFFFFF) {
+ macaddr[3] = (offset & 0xFF0000) >> 16;
+ macaddr[4] = (offset & 0xFF00) >> 8;
+ macaddr[5] = offset & 0xFF;
+ return 0;
+ }
+
+ for (i = 0; i < 6; i++) {
+ macaddr[i] = strtol(p, (char **)&p, 16);
+ if (i == 5) {
+ if (*p != '\0') {
+ return -1;
+ }
+ } else {
+ if (*p != ':' && *p != '-') {
+ return -1;
+ }
+ p++;
+ }
+ }
+
+ return 0;
+}
diff --git a/net/util.h b/net/util.h
new file mode 100644
index 0000000..10c7da9
--- /dev/null
+++ b/net/util.h
@@ -0,0 +1,32 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_NET_UTIL_H
+#define QEMU_NET_UTIL_H
+
+#include <stdint.h>
+
+int net_parse_macaddr(uint8_t *macaddr, const char *p);
+
+#endif /* QEMU_NET_UTIL_H */
diff --git a/net/vde.c b/net/vde.c
new file mode 100644
index 0000000..0b46fa6
--- /dev/null
+++ b/net/vde.c
@@ -0,0 +1,131 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "net/vde.h"
+
+#include "config-host.h"
+
+#include <libvdeplug.h>
+
+#include "net.h"
+#include "qemu-char.h"
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "sysemu.h"
+
+typedef struct VDEState {
+ VLANClientState nc;
+ VDECONN *vde;
+} VDEState;
+
+static void vde_to_qemu(void *opaque)
+{
+ VDEState *s = opaque;
+ uint8_t buf[4096];
+ int size;
+
+ size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0);
+ if (size > 0) {
+ qemu_send_packet(&s->nc, buf, size);
+ }
+}
+
+static ssize_t vde_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+{
+ VDEState *s = DO_UPCAST(VDEState, nc, nc);
+ ssize_t ret;
+
+ do {
+ ret = vde_send(s->vde, (const char *)buf, size, 0);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static void vde_cleanup(VLANClientState *nc)
+{
+ VDEState *s = DO_UPCAST(VDEState, nc, nc);
+ qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL);
+ vde_close(s->vde);
+}
+
+static NetClientInfo net_vde_info = {
+ .type = NET_CLIENT_TYPE_VDE,
+ .size = sizeof(VDEState),
+ .receive = vde_receive,
+ .cleanup = vde_cleanup,
+};
+
+static int net_vde_init(VLANState *vlan, const char *model,
+ const char *name, const char *sock,
+ int port, const char *group, int mode)
+{
+ VLANClientState *nc;
+ VDEState *s;
+ VDECONN *vde;
+ char *init_group = (char *)group;
+ char *init_sock = (char *)sock;
+
+ struct vde_open_args args = {
+ .port = port,
+ .group = init_group,
+ .mode = mode,
+ };
+
+ vde = vde_open(init_sock, (char *)"QEMU", &args);
+ if (!vde){
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_vde_info, vlan, NULL, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d",
+ sock, vde_datafd(vde));
+
+ s = DO_UPCAST(VDEState, nc, nc);
+
+ s->vde = vde;
+
+ qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
+
+ return 0;
+}
+
+int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+{
+ const char *sock;
+ const char *group;
+ int port, mode;
+
+ sock = qemu_opt_get(opts, "sock");
+ group = qemu_opt_get(opts, "group");
+
+ port = qemu_opt_get_number(opts, "port", 0);
+ mode = qemu_opt_get_number(opts, "mode", 0700);
+
+ if (net_vde_init(vlan, "vde", name, sock, port, group, mode) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/net/vde.h b/net/vde.h
new file mode 100644
index 0000000..3e6ca3e
--- /dev/null
+++ b/net/vde.h
@@ -0,0 +1,36 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_NET_VDE_H
+#define QEMU_NET_VDE_H
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+
+#ifdef CONFIG_VDE
+
+int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan);
+
+#endif /* CONFIG_VDE */
+
+#endif /* QEMU_NET_VDE_H */
diff --git a/notify.c b/notify.c
new file mode 100644
index 0000000..bcd3fc5
--- /dev/null
+++ b/notify.c
@@ -0,0 +1,39 @@
+/*
+ * Notifier lists
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "notify.h"
+
+void notifier_list_init(NotifierList *list)
+{
+ QTAILQ_INIT(&list->notifiers);
+}
+
+void notifier_list_add(NotifierList *list, Notifier *notifier)
+{
+ QTAILQ_INSERT_HEAD(&list->notifiers, notifier, node);
+}
+
+void notifier_list_remove(NotifierList *list, Notifier *notifier)
+{
+ QTAILQ_REMOVE(&list->notifiers, notifier, node);
+}
+
+void notifier_list_notify(NotifierList *list)
+{
+ Notifier *notifier, *next;
+
+ QTAILQ_FOREACH_SAFE(notifier, &list->notifiers, node, next) {
+ notifier->notify(notifier);
+ }
+}
diff --git a/notify.h b/notify.h
new file mode 100644
index 0000000..b40522f
--- /dev/null
+++ b/notify.h
@@ -0,0 +1,43 @@
+/*
+ * Notifier lists
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_NOTIFY_H
+#define QEMU_NOTIFY_H
+
+#include "qemu-queue.h"
+
+typedef struct Notifier Notifier;
+
+struct Notifier
+{
+ void (*notify)(Notifier *notifier);
+ QTAILQ_ENTRY(Notifier) node;
+};
+
+typedef struct NotifierList
+{
+ QTAILQ_HEAD(, Notifier) notifiers;
+} NotifierList;
+
+#define NOTIFIER_LIST_INITIALIZER(head) \
+ { QTAILQ_HEAD_INITIALIZER((head).notifiers) }
+
+void notifier_list_init(NotifierList *list);
+
+void notifier_list_add(NotifierList *list, Notifier *notifier);
+
+void notifier_list_remove(NotifierList *list, Notifier *notifier);
+
+void notifier_list_notify(NotifierList *list);
+
+#endif
diff --git a/os-posix.c b/os-posix.c
new file mode 100644
index 0000000..38c29d1
--- /dev/null
+++ b/os-posix.c
@@ -0,0 +1,384 @@
+/*
+ * os-posix.c
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+/*needed for MAP_POPULATE before including qemu-options.h */
+#include <sys/mman.h>
+#include <pwd.h>
+#include <libgen.h>
+
+/* Needed early for CONFIG_BSD etc. */
+#include "config-host.h"
+#include "sysemu.h"
+#include "net/slirp.h"
+#include "qemu-options.h"
+
+#ifdef CONFIG_LINUX
+#include <sys/prctl.h>
+#endif
+
+#ifdef CONFIG_EVENTFD
+#include <sys/eventfd.h>
+#endif
+
+static struct passwd *user_pwd;
+static const char *chroot_dir;
+static int daemonize;
+static int fds[2];
+
+void os_setup_early_signal_handling(void)
+{
+ struct sigaction act;
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &act, NULL);
+}
+
+static void termsig_handler(int signal)
+{
+ qemu_system_shutdown_request();
+}
+
+static void sigchld_handler(int signal)
+{
+ waitpid(-1, NULL, WNOHANG);
+}
+
+void os_setup_signal_handling(void)
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = termsig_handler;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = sigchld_handler;
+ act.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, NULL);
+}
+
+/* Find a likely location for support files using the location of the binary.
+ For installed binaries this will be "$bindir/../share/qemu". When
+ running from the build tree this will be "$bindir/../pc-bios". */
+#define SHARE_SUFFIX "/share/qemu"
+#define BUILD_SUFFIX "/pc-bios"
+char *os_find_datadir(const char *argv0)
+{
+ char *dir;
+ char *p = NULL;
+ char *res;
+ char buf[PATH_MAX];
+ size_t max_len;
+
+#if defined(__linux__)
+ {
+ int len;
+ len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+ if (len > 0) {
+ buf[len] = 0;
+ p = buf;
+ }
+ }
+#elif defined(__FreeBSD__)
+ {
+ static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+ size_t len = sizeof(buf) - 1;
+
+ *buf = '\0';
+ if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) &&
+ *buf) {
+ buf[sizeof(buf) - 1] = '\0';
+ p = buf;
+ }
+ }
+#endif
+ /* If we don't have any way of figuring out the actual executable
+ location then try argv[0]. */
+ if (!p) {
+ p = realpath(argv0, buf);
+ if (!p) {
+ return NULL;
+ }
+ }
+ dir = dirname(p);
+ dir = dirname(dir);
+
+ max_len = strlen(dir) +
+ MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1;
+ res = qemu_mallocz(max_len);
+ snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX);
+ if (access(res, R_OK)) {
+ snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX);
+ if (access(res, R_OK)) {
+ qemu_free(res);
+ res = NULL;
+ }
+ }
+
+ return res;
+}
+#undef SHARE_SUFFIX
+#undef BUILD_SUFFIX
+
+void os_set_proc_name(const char *s)
+{
+#if defined(PR_SET_NAME)
+ char name[16];
+ if (!s)
+ return;
+ name[sizeof(name) - 1] = 0;
+ strncpy(name, s, sizeof(name));
+ /* Could rewrite argv[0] too, but that's a bit more complicated.
+ This simple way is enough for `top'. */
+ if (prctl(PR_SET_NAME, name)) {
+ perror("unable to change process name");
+ exit(1);
+ }
+#else
+ fprintf(stderr, "Change of process name not supported by your OS\n");
+ exit(1);
+#endif
+}
+
+/*
+ * Parse OS specific command line options.
+ * return 0 if option handled, -1 otherwise
+ */
+void os_parse_cmd_args(int index, const char *optarg)
+{
+ switch (index) {
+#ifdef CONFIG_SLIRP
+ case QEMU_OPTION_smb:
+ if (net_slirp_smb(optarg) < 0)
+ exit(1);
+ break;
+#endif
+ case QEMU_OPTION_runas:
+ user_pwd = getpwnam(optarg);
+ if (!user_pwd) {
+ fprintf(stderr, "User \"%s\" doesn't exist\n", optarg);
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_chroot:
+ chroot_dir = optarg;
+ break;
+ case QEMU_OPTION_daemonize:
+ daemonize = 1;
+ break;
+ }
+ return;
+}
+
+static void change_process_uid(void)
+{
+ if (user_pwd) {
+ if (setgid(user_pwd->pw_gid) < 0) {
+ fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid);
+ exit(1);
+ }
+ if (setuid(user_pwd->pw_uid) < 0) {
+ fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid);
+ exit(1);
+ }
+ if (setuid(0) != -1) {
+ fprintf(stderr, "Dropping privileges failed\n");
+ exit(1);
+ }
+ }
+}
+
+static void change_root(void)
+{
+ if (chroot_dir) {
+ if (chroot(chroot_dir) < 0) {
+ fprintf(stderr, "chroot failed\n");
+ exit(1);
+ }
+ if (chdir("/")) {
+ perror("not able to chdir to /");
+ exit(1);
+ }
+ }
+
+}
+
+void os_daemonize(void)
+{
+ if (daemonize) {
+ pid_t pid;
+
+ if (pipe(fds) == -1)
+ exit(1);
+
+ pid = fork();
+ if (pid > 0) {
+ uint8_t status;
+ ssize_t len;
+
+ close(fds[1]);
+
+ again:
+ len = read(fds[0], &status, 1);
+ if (len == -1 && (errno == EINTR))
+ goto again;
+
+ if (len != 1)
+ exit(1);
+ else if (status == 1) {
+ fprintf(stderr, "Could not acquire pidfile: %s\n", strerror(errno));
+ exit(1);
+ } else
+ exit(0);
+ } else if (pid < 0)
+ exit(1);
+
+ close(fds[0]);
+ qemu_set_cloexec(fds[1]);
+
+ setsid();
+
+ pid = fork();
+ if (pid > 0)
+ exit(0);
+ else if (pid < 0)
+ exit(1);
+
+ umask(027);
+
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ }
+}
+
+void os_setup_post(void)
+{
+ int fd = 0;
+
+ if (daemonize) {
+ uint8_t status = 0;
+ ssize_t len;
+
+ again1:
+ len = write(fds[1], &status, 1);
+ if (len == -1 && (errno == EINTR))
+ goto again1;
+
+ if (len != 1)
+ exit(1);
+
+ if (chdir("/")) {
+ perror("not able to chdir to /");
+ exit(1);
+ }
+ TFR(fd = qemu_open("/dev/null", O_RDWR));
+ if (fd == -1)
+ exit(1);
+ }
+
+ change_root();
+ change_process_uid();
+
+ if (daemonize) {
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+
+ close(fd);
+ }
+}
+
+void os_pidfile_error(void)
+{
+ if (daemonize) {
+ uint8_t status = 1;
+ if (write(fds[1], &status, 1) != 1) {
+ perror("daemonize. Writing to pipe\n");
+ }
+ } else
+ fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno));
+}
+
+void os_set_line_buffering(void)
+{
+ setvbuf(stdout, NULL, _IOLBF, 0);
+}
+
+/*
+ * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set.
+ */
+int qemu_eventfd(int fds[2])
+{
+#ifdef CONFIG_EVENTFD
+ int ret;
+
+ ret = eventfd(0, 0);
+ if (ret >= 0) {
+ fds[0] = ret;
+ qemu_set_cloexec(ret);
+ if ((fds[1] = dup(ret)) == -1) {
+ close(ret);
+ return -1;
+ }
+ qemu_set_cloexec(fds[1]);
+ return 0;
+ }
+
+ if (errno != ENOSYS) {
+ return -1;
+ }
+#endif
+
+ return qemu_pipe(fds);
+}
+
+int qemu_create_pidfile(const char *filename)
+{
+ char buffer[128];
+ int len;
+ int fd;
+
+ fd = qemu_open(filename, O_RDWR | O_CREAT, 0600);
+ if (fd == -1) {
+ return -1;
+ }
+ if (lockf(fd, F_TLOCK, 0) == -1) {
+ return -1;
+ }
+ len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid());
+ if (write(fd, buffer, len) != len) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/os-win32.c b/os-win32.c
new file mode 100644
index 0000000..566d5e9
--- /dev/null
+++ b/os-win32.c
@@ -0,0 +1,266 @@
+/*
+ * os-win32.c
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <windows.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+#include "config-host.h"
+#include "sysemu.h"
+#include "qemu-options.h"
+
+/***********************************************************/
+/* Functions missing in mingw */
+
+int setenv(const char *name, const char *value, int overwrite)
+{
+ int result = 0;
+ if (overwrite || !getenv(name)) {
+ size_t length = strlen(name) + strlen(value) + 2;
+ char *string = qemu_malloc(length);
+ snprintf(string, length, "%s=%s", name, value);
+ result = putenv(string);
+ }
+ return result;
+}
+
+/***********************************************************/
+/* Polling handling */
+
+typedef struct PollingEntry {
+ PollingFunc *func;
+ void *opaque;
+ struct PollingEntry *next;
+} PollingEntry;
+
+static PollingEntry *first_polling_entry;
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque)
+{
+ PollingEntry **ppe, *pe;
+ pe = qemu_mallocz(sizeof(PollingEntry));
+ pe->func = func;
+ pe->opaque = opaque;
+ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next);
+ *ppe = pe;
+ return 0;
+}
+
+void qemu_del_polling_cb(PollingFunc *func, void *opaque)
+{
+ PollingEntry **ppe, *pe;
+ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) {
+ pe = *ppe;
+ if (pe->func == func && pe->opaque == opaque) {
+ *ppe = pe->next;
+ qemu_free(pe);
+ break;
+ }
+ }
+}
+
+/***********************************************************/
+/* Wait objects support */
+typedef struct WaitObjects {
+ int num;
+ HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
+ WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1];
+ void *opaque[MAXIMUM_WAIT_OBJECTS + 1];
+} WaitObjects;
+
+static WaitObjects wait_objects = {0};
+
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+ WaitObjects *w = &wait_objects;
+
+ if (w->num >= MAXIMUM_WAIT_OBJECTS)
+ return -1;
+ w->events[w->num] = handle;
+ w->func[w->num] = func;
+ w->opaque[w->num] = opaque;
+ w->num++;
+ return 0;
+}
+
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+ int i, found;
+ WaitObjects *w = &wait_objects;
+
+ found = 0;
+ for (i = 0; i < w->num; i++) {
+ if (w->events[i] == handle)
+ found = 1;
+ if (found) {
+ w->events[i] = w->events[i + 1];
+ w->func[i] = w->func[i + 1];
+ w->opaque[i] = w->opaque[i + 1];
+ }
+ }
+ if (found)
+ w->num--;
+}
+
+void os_host_main_loop_wait(int *timeout)
+{
+ int ret, ret2, i;
+ PollingEntry *pe;
+
+ /* XXX: need to suppress polling by better using win32 events */
+ ret = 0;
+ for(pe = first_polling_entry; pe != NULL; pe = pe->next) {
+ ret |= pe->func(pe->opaque);
+ }
+ if (ret == 0) {
+ int err;
+ WaitObjects *w = &wait_objects;
+
+ ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout);
+ if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) {
+ if (w->func[ret - WAIT_OBJECT_0])
+ w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]);
+
+ /* Check for additional signaled events */
+ for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) {
+
+ /* Check if event is signaled */
+ ret2 = WaitForSingleObject(w->events[i], 0);
+ if(ret2 == WAIT_OBJECT_0) {
+ if (w->func[i])
+ w->func[i](w->opaque[i]);
+ } else if (ret2 == WAIT_TIMEOUT) {
+ } else {
+ err = GetLastError();
+ fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err);
+ }
+ }
+ } else if (ret == WAIT_TIMEOUT) {
+ } else {
+ err = GetLastError();
+ fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err);
+ }
+ }
+
+ *timeout = 0;
+}
+
+static BOOL WINAPI qemu_ctrl_handler(DWORD type)
+{
+ exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+
+void os_setup_early_signal_handling(void)
+{
+ /* Note: cpu_interrupt() is currently not SMP safe, so we force
+ QEMU to run on a single CPU */
+ HANDLE h;
+ DWORD mask, smask;
+ int i;
+
+ SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE);
+
+ h = GetCurrentProcess();
+ if (GetProcessAffinityMask(h, &mask, &smask)) {
+ for(i = 0; i < 32; i++) {
+ if (mask & (1 << i))
+ break;
+ }
+ if (i != 32) {
+ mask = 1 << i;
+ SetProcessAffinityMask(h, mask);
+ }
+ }
+}
+
+/* Look for support files in the same directory as the executable. */
+char *os_find_datadir(const char *argv0)
+{
+ char *p;
+ char buf[MAX_PATH];
+ DWORD len;
+
+ len = GetModuleFileName(NULL, buf, sizeof(buf) - 1);
+ if (len == 0) {
+ return NULL;
+ }
+
+ buf[len] = 0;
+ p = buf + len - 1;
+ while (p != buf && *p != '\\')
+ p--;
+ *p = 0;
+ if (access(buf, R_OK) == 0) {
+ return qemu_strdup(buf);
+ }
+ return NULL;
+}
+
+void os_set_line_buffering(void)
+{
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+}
+
+/*
+ * Parse OS specific command line options.
+ * return 0 if option handled, -1 otherwise
+ */
+void os_parse_cmd_args(int index, const char *optarg)
+{
+ return;
+}
+
+void os_pidfile_error(void)
+{
+ fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno));
+}
+
+int qemu_create_pidfile(const char *filename)
+{
+ char buffer[128];
+ int len;
+ HANDLE file;
+ OVERLAPPED overlap;
+ BOOL ret;
+ memset(&overlap, 0, sizeof(overlap));
+
+ file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+ len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid());
+ ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len,
+ &overlap, NULL);
+ if (ret == 0) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/osdep.c b/osdep.c
new file mode 100644
index 0000000..327583b
--- /dev/null
+++ b/osdep.c
@@ -0,0 +1,169 @@
+/*
+ * QEMU low level functions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+/* Needed early for CONFIG_BSD etc. */
+#include "config-host.h"
+
+#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE)
+#include <sys/mman.h>
+#endif
+
+#ifdef CONFIG_SOLARIS
+#include <sys/types.h>
+#include <sys/statvfs.h>
+/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for
+ discussion about Solaris header problems */
+extern int madvise(caddr_t, size_t, int);
+#endif
+
+#include "qemu-common.h"
+#include "trace.h"
+#include "sysemu.h"
+#include "qemu_socket.h"
+
+int qemu_madvise(void *addr, size_t len, int advice)
+{
+ if (advice == QEMU_MADV_INVALID) {
+ errno = EINVAL;
+ return -1;
+ }
+#if defined(CONFIG_MADVISE)
+ return madvise(addr, len, advice);
+#elif defined(CONFIG_POSIX_MADVISE)
+ return posix_madvise(addr, len, advice);
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+
+/*
+ * Opens a file with FD_CLOEXEC set
+ */
+int qemu_open(const char *name, int flags, ...)
+{
+ int ret;
+ int mode = 0;
+
+ if (flags & O_CREAT) {
+ va_list ap;
+
+ va_start(ap, flags);
+ mode = va_arg(ap, int);
+ va_end(ap);
+ }
+
+#ifdef O_CLOEXEC
+ ret = open(name, flags | O_CLOEXEC, mode);
+#else
+ ret = open(name, flags, mode);
+ if (ret >= 0) {
+ qemu_set_cloexec(ret);
+ }
+#endif
+
+ return ret;
+}
+
+/*
+ * A variant of write(2) which handles partial write.
+ *
+ * Return the number of bytes transferred.
+ * Set errno if fewer than `count' bytes are written.
+ *
+ * This function don't work with non-blocking fd's.
+ * Any of the possibilities with non-bloking fd's is bad:
+ * - return a short write (then name is wrong)
+ * - busy wait adding (errno == EAGAIN) to the loop
+ */
+ssize_t qemu_write_full(int fd, const void *buf, size_t count)
+{
+ ssize_t ret = 0;
+ ssize_t total = 0;
+
+ while (count) {
+ ret = write(fd, buf, count);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+
+ count -= ret;
+ buf += ret;
+ total += ret;
+ }
+
+ return total;
+}
+
+/*
+ * Opens a socket with FD_CLOEXEC set
+ */
+int qemu_socket(int domain, int type, int protocol)
+{
+ int ret;
+
+#ifdef SOCK_CLOEXEC
+ ret = socket(domain, type | SOCK_CLOEXEC, protocol);
+ if (ret != -1 || errno != EINVAL) {
+ return ret;
+ }
+#endif
+ ret = socket(domain, type, protocol);
+ if (ret >= 0) {
+ qemu_set_cloexec(ret);
+ }
+
+ return ret;
+}
+
+/*
+ * Accept a connection and set FD_CLOEXEC
+ */
+int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ int ret;
+
+#ifdef CONFIG_ACCEPT4
+ ret = accept4(s, addr, addrlen, SOCK_CLOEXEC);
+ if (ret != -1 || errno != ENOSYS) {
+ return ret;
+ }
+#endif
+ ret = accept(s, addr, addrlen);
+ if (ret >= 0) {
+ qemu_set_cloexec(ret);
+ }
+
+ return ret;
+}
diff --git a/osdep.h b/osdep.h
new file mode 100644
index 0000000..8bd30d7
--- /dev/null
+++ b/osdep.h
@@ -0,0 +1,130 @@
+#ifndef QEMU_OSDEP_H
+#define QEMU_OSDEP_H
+
+#include <stdarg.h>
+#include <stddef.h>
+#ifdef __OpenBSD__
+#include <sys/types.h>
+#include <sys/signal.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+
+#ifndef glue
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+#endif
+
+#ifndef likely
+#if __GNUC__ < 3
+#define __builtin_expect(x, n) (x)
+#endif
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
+#ifdef CONFIG_NEED_OFFSETOF
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER)
+#endif
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *) 0)->member) *__mptr = (ptr); \
+ (type *) ((char *) __mptr - offsetof(type, member));})
+#endif
+
+/* Convert from a base type to a parent type, with compile time checking. */
+#ifdef __GNUC__
+#define DO_UPCAST(type, field, dev) ( __extension__ ( { \
+ char __attribute__((unused)) offset_must_be_zero[ \
+ -offsetof(type, field)]; \
+ container_of(dev, type, field);}))
+#else
+#define DO_UPCAST(type, field, dev) container_of(dev, type, field)
+#endif
+
+#define typeof_field(type, field) typeof(((type *)0)->field)
+#define type_check(t1,t2) ((t1*)0 - (t2*)0)
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#ifndef always_inline
+#if !((__GNUC__ < 3) || defined(__APPLE__))
+#ifdef __OPTIMIZE__
+#define inline __attribute__ (( always_inline )) __inline__
+#endif
+#endif
+#else
+#define inline always_inline
+#endif
+
+#ifdef __i386__
+#define REGPARM __attribute((regparm(3)))
+#else
+#define REGPARM
+#endif
+
+#define qemu_printf printf
+
+#if defined (__GNUC__) && defined (__GNUC_MINOR__)
+# define QEMU_GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define QEMU_GNUC_PREREQ(maj, min) 0
+#endif
+
+void *qemu_memalign(size_t alignment, size_t size);
+void *qemu_vmalloc(size_t size);
+void qemu_vfree(void *ptr);
+
+#define QEMU_MADV_INVALID -1
+
+#if defined(CONFIG_MADVISE)
+
+#define QEMU_MADV_WILLNEED MADV_WILLNEED
+#define QEMU_MADV_DONTNEED MADV_DONTNEED
+#ifdef MADV_DONTFORK
+#define QEMU_MADV_DONTFORK MADV_DONTFORK
+#else
+#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
+#endif
+#ifdef MADV_MERGEABLE
+#define QEMU_MADV_MERGEABLE MADV_MERGEABLE
+#else
+#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
+#endif
+
+#elif defined(CONFIG_POSIX_MADVISE)
+
+#define QEMU_MADV_WILLNEED POSIX_MADV_WILLNEED
+#define QEMU_MADV_DONTNEED POSIX_MADV_DONTNEED
+#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
+#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
+
+#else /* no-op */
+
+#define QEMU_MADV_WILLNEED QEMU_MADV_INVALID
+#define QEMU_MADV_DONTNEED QEMU_MADV_INVALID
+#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID
+#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID
+
+#endif
+
+int qemu_madvise(void *addr, size_t len, int advice);
+
+int qemu_create_pidfile(const char *filename);
+
+#endif
diff --git a/oslib-posix.c b/oslib-posix.c
new file mode 100644
index 0000000..edd4ddf
--- /dev/null
+++ b/oslib-posix.c
@@ -0,0 +1,161 @@
+/*
+ * os-posix-lib.c
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * QEMU library functions on POSIX which are shared between QEMU and
+ * the QEMU tools.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+#include "sysemu.h"
+#include "trace.h"
+#include "qemu_socket.h"
+
+void *qemu_oom_check(void *ptr)
+{
+ if (ptr == NULL) {
+ fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno));
+ abort();
+ }
+ return ptr;
+}
+
+void *qemu_memalign(size_t alignment, size_t size)
+{
+ void *ptr;
+#if defined(_POSIX_C_SOURCE) && !defined(__sun__)
+ int ret;
+ ret = posix_memalign(&ptr, alignment, size);
+ if (ret != 0) {
+ fprintf(stderr, "Failed to allocate %zu B: %s\n",
+ size, strerror(ret));
+ abort();
+ }
+#elif defined(CONFIG_BSD)
+ ptr = qemu_oom_check(valloc(size));
+#else
+ ptr = qemu_oom_check(memalign(alignment, size));
+#endif
+ trace_qemu_memalign(alignment, size, ptr);
+ return ptr;
+}
+
+/* alloc shared memory pages */
+void *qemu_vmalloc(size_t size)
+{
+#ifndef __ia64__
+ return qemu_memalign(getpagesize(), size);
+#else
+ return qemu_memalign(65536, size);
+#endif
+}
+
+void qemu_vfree(void *ptr)
+{
+ trace_qemu_vfree(ptr);
+ free(ptr);
+}
+
+void socket_set_nonblock(int fd)
+{
+ int f;
+ f = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, f | O_NONBLOCK);
+}
+
+void qemu_set_cloexec(int fd)
+{
+ int f;
+ f = fcntl(fd, F_GETFD);
+ fcntl(fd, F_SETFD, f | FD_CLOEXEC);
+}
+
+/*
+ * Creates a pipe with FD_CLOEXEC set on both file descriptors
+ */
+int qemu_pipe(int pipefd[2])
+{
+ int ret;
+
+#ifdef CONFIG_PIPE2
+ ret = pipe2(pipefd, O_CLOEXEC);
+ if (ret != -1 || errno != ENOSYS) {
+ return ret;
+ }
+#endif
+ ret = pipe(pipefd);
+ if (ret == 0) {
+ qemu_set_cloexec(pipefd[0]);
+ qemu_set_cloexec(pipefd[1]);
+ }
+
+ return ret;
+}
+
+int qemu_utimensat(int dirfd, const char *path, const struct timespec *times,
+ int flags)
+{
+ struct timeval tv[2], tv_now;
+ struct stat st;
+ int i;
+#ifdef CONFIG_UTIMENSAT
+ int ret;
+
+ ret = utimensat(dirfd, path, times, flags);
+ if (ret != -1 || errno != ENOSYS) {
+ return ret;
+ }
+#endif
+ /* Fallback: use utimes() instead of utimensat() */
+
+ /* happy if special cases */
+ if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) {
+ return 0;
+ }
+ if (times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) {
+ return utimes(path, NULL);
+ }
+
+ /* prepare for hard cases */
+ if (times[0].tv_nsec == UTIME_NOW || times[1].tv_nsec == UTIME_NOW) {
+ gettimeofday(&tv_now, NULL);
+ }
+ if (times[0].tv_nsec == UTIME_OMIT || times[1].tv_nsec == UTIME_OMIT) {
+ stat(path, &st);
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (times[i].tv_nsec == UTIME_NOW) {
+ tv[i].tv_sec = tv_now.tv_sec;
+ tv[i].tv_usec = tv_now.tv_usec;
+ } else if (times[i].tv_nsec == UTIME_OMIT) {
+ tv[i].tv_sec = (i == 0) ? st.st_atime : st.st_mtime;
+ tv[i].tv_usec = 0;
+ } else {
+ tv[i].tv_sec = times[i].tv_sec;
+ tv[i].tv_usec = times[i].tv_nsec / 1000;
+ }
+ }
+
+ return utimes(path, &tv[0]);
+}
diff --git a/oslib-win32.c b/oslib-win32.c
new file mode 100644
index 0000000..ab29eae
--- /dev/null
+++ b/oslib-win32.c
@@ -0,0 +1,121 @@
+/*
+ * os-win32.c
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Red Hat, Inc.
+ *
+ * QEMU library functions for win32 which are shared between QEMU and
+ * the QEMU tools.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <windows.h>
+#include "config-host.h"
+#include "sysemu.h"
+#include "trace.h"
+#include "qemu_socket.h"
+
+void *qemu_oom_check(void *ptr)
+{
+ if (ptr == NULL) {
+ fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError());
+ abort();
+ }
+ return ptr;
+}
+
+void *qemu_memalign(size_t alignment, size_t size)
+{
+ void *ptr;
+
+ if (!size) {
+ abort();
+ }
+ ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+ trace_qemu_memalign(alignment, size, ptr);
+ return ptr;
+}
+
+void *qemu_vmalloc(size_t size)
+{
+ void *ptr;
+
+ /* FIXME: this is not exactly optimal solution since VirtualAlloc
+ has 64Kb granularity, but at least it guarantees us that the
+ memory is page aligned. */
+ if (!size) {
+ abort();
+ }
+ ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE));
+ trace_qemu_vmalloc(size, ptr);
+ return ptr;
+}
+
+void qemu_vfree(void *ptr)
+{
+ trace_qemu_vfree(ptr);
+ VirtualFree(ptr, 0, MEM_RELEASE);
+}
+
+void socket_set_nonblock(int fd)
+{
+ unsigned long opt = 1;
+ ioctlsocket(fd, FIONBIO, &opt);
+}
+
+int inet_aton(const char *cp, struct in_addr *ia)
+{
+ uint32_t addr = inet_addr(cp);
+ if (addr == 0xffffffff) {
+ return 0;
+ }
+ ia->s_addr = addr;
+ return 1;
+}
+
+void qemu_set_cloexec(int fd)
+{
+}
+
+/* mingw32 needs ffs for compilations without optimization. */
+int ffs(int i)
+{
+ /* Use gcc's builtin ffs. */
+ return __builtin_ffs(i);
+}
+
+/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
+#define _W32_FT_OFFSET (116444736000000000ULL)
+
+int qemu_gettimeofday(qemu_timeval *tp)
+{
+ union {
+ unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
+ FILETIME ft;
+ } _now;
+
+ if(tp) {
+ GetSystemTimeAsFileTime (&_now.ft);
+ tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL );
+ tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL);
+ }
+ /* Always return 0 as per Open Group Base Specifications Issue 6.
+ Do not set errno on error. */
+ return 0;
+}
diff --git a/path.c b/path.c
new file mode 100644
index 0000000..0d2bf14
--- /dev/null
+++ b/path.c
@@ -0,0 +1,165 @@
+/* Code to mangle pathnames into those matching a given prefix.
+ eg. open("/lib/foo.so") => open("/usr/gnemul/i386-linux/lib/foo.so");
+
+ The assumption is that this area does not change.
+*/
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include "qemu-common.h"
+
+struct pathelem
+{
+ /* Name of this, eg. lib */
+ char *name;
+ /* Full path name, eg. /usr/gnemul/x86-linux/lib. */
+ char *pathname;
+ struct pathelem *parent;
+ /* Children */
+ unsigned int num_entries;
+ struct pathelem *entries[0];
+};
+
+static struct pathelem *base;
+
+/* First N chars of S1 match S2, and S2 is N chars long. */
+static int strneq(const char *s1, unsigned int n, const char *s2)
+{
+ unsigned int i;
+
+ for (i = 0; i < n; i++)
+ if (s1[i] != s2[i])
+ return 0;
+ return s2[i] == 0;
+}
+
+static struct pathelem *add_entry(struct pathelem *root, const char *name);
+
+static struct pathelem *new_entry(const char *root,
+ struct pathelem *parent,
+ const char *name)
+{
+ struct pathelem *new = malloc(sizeof(*new));
+ new->name = strdup(name);
+ if (asprintf(&new->pathname, "%s/%s", root, name) == -1) {
+ printf("Cannot allocate memory\n");
+ exit(1);
+ }
+ new->num_entries = 0;
+ return new;
+}
+
+#define streq(a,b) (strcmp((a), (b)) == 0)
+
+static struct pathelem *add_dir_maybe(struct pathelem *path)
+{
+ DIR *dir;
+
+ if ((dir = opendir(path->pathname)) != NULL) {
+ struct dirent *dirent;
+
+ while ((dirent = readdir(dir)) != NULL) {
+ if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){
+ path = add_entry(path, dirent->d_name);
+ }
+ }
+ closedir(dir);
+ }
+ return path;
+}
+
+static struct pathelem *add_entry(struct pathelem *root, const char *name)
+{
+ root->num_entries++;
+
+ root = realloc(root, sizeof(*root)
+ + sizeof(root->entries[0])*root->num_entries);
+
+ root->entries[root->num_entries-1] = new_entry(root->pathname, root, name);
+ root->entries[root->num_entries-1]
+ = add_dir_maybe(root->entries[root->num_entries-1]);
+ return root;
+}
+
+/* This needs to be done after tree is stabilized (ie. no more reallocs!). */
+static void set_parents(struct pathelem *child, struct pathelem *parent)
+{
+ unsigned int i;
+
+ child->parent = parent;
+ for (i = 0; i < child->num_entries; i++)
+ set_parents(child->entries[i], child);
+}
+
+/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */
+static const char *
+follow_path(const struct pathelem *cursor, const char *name)
+{
+ unsigned int i, namelen;
+
+ name += strspn(name, "/");
+ namelen = strcspn(name, "/");
+
+ if (namelen == 0)
+ return cursor->pathname;
+
+ if (strneq(name, namelen, ".."))
+ return follow_path(cursor->parent, name + namelen);
+
+ if (strneq(name, namelen, "."))
+ return follow_path(cursor, name + namelen);
+
+ for (i = 0; i < cursor->num_entries; i++)
+ if (strneq(name, namelen, cursor->entries[i]->name))
+ return follow_path(cursor->entries[i], name + namelen);
+
+ /* Not found */
+ return NULL;
+}
+
+void init_paths(const char *prefix)
+{
+ char pref_buf[PATH_MAX];
+
+ if (prefix[0] == '\0' ||
+ !strcmp(prefix, "/"))
+ return;
+
+ if (prefix[0] != '/') {
+ char *cwd = getcwd(NULL, 0);
+ size_t pref_buf_len = sizeof(pref_buf);
+
+ if (!cwd)
+ abort();
+ pstrcpy(pref_buf, sizeof(pref_buf), cwd);
+ pstrcat(pref_buf, pref_buf_len, "/");
+ pstrcat(pref_buf, pref_buf_len, prefix);
+ free(cwd);
+ } else
+ pstrcpy(pref_buf, sizeof(pref_buf), prefix + 1);
+
+ base = new_entry("", NULL, pref_buf);
+ base = add_dir_maybe(base);
+ if (base->num_entries == 0) {
+ free (base);
+ base = NULL;
+ } else {
+ set_parents(base, base);
+ }
+}
+
+/* Look for path in emulation dir, otherwise return name. */
+const char *path(const char *name)
+{
+ /* Only do absolute paths: quick and dirty, but should mostly be OK.
+ Could do relative by tracking cwd. */
+ if (!base || !name || name[0] != '/')
+ return name;
+
+ return follow_path(base, name) ?: name;
+}
diff --git a/pc-bios/Makefile b/pc-bios/Makefile
new file mode 100644
index 0000000..315288d
--- /dev/null
+++ b/pc-bios/Makefile
@@ -0,0 +1,19 @@
+#
+# NOTE: only compilable with x86 cross compile tools
+#
+include ../config-host.mak
+
+DEFINES=
+
+TARGETS=
+
+all: $(TARGETS)
+
+%.o: %.S
+ $(CC) $(DEFINES) -c -o $@ $<
+
+%.dtb: %.dts
+ dtc -I dts -O dtb -o $@ $<
+
+clean:
+ rm -f $(TARGETS) *.o *~
diff --git a/pc-bios/README b/pc-bios/README
new file mode 100644
index 0000000..3fc0944
--- /dev/null
+++ b/pc-bios/README
@@ -0,0 +1,29 @@
+- SeaBIOS (bios.bin) is the successor of pc bios.
+ See http://www.seabios.org/ for more information.
+
+- The VGA BIOS and the Cirrus VGA BIOS come from the LGPL VGA bios
+ project (http://www.nongnu.org/vgabios/).
+
+- The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is
+ available at http://perso.magic.fr/l_indien/OpenHackWare/index.htm.
+
+- OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable
+ firmware implementation. The goal is to implement a 100% IEEE
+ 1275-1994 (referred to as Open Firmware) compliant firmware.
+ The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32
+ and Sparc64 are built from OpenBIOS SVN revision 1018.
+
+- The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0
+
+ e1000 8086:100E
+ eepro100 8086:1209 (also used for 8086:1229 and 8086:2449)
+ ns8390 1050:0940
+ pcnet32 1022:2000
+ rtl8139 10ec:8139
+ virtio 1af4:1000
+
+ http://rom-o-matic.net/
+
+- The S390 zipl loader is an addition to the official IBM s390-tools
+ package. That fork is maintained in its own git repository at:
+ git://repo.or.cz/s390-tools.git
diff --git a/pc-bios/bamboo.dtb b/pc-bios/bamboo.dtb
new file mode 100644
index 0000000..c78e254
--- /dev/null
+++ b/pc-bios/bamboo.dtb
Binary files differ
diff --git a/pc-bios/bamboo.dts b/pc-bios/bamboo.dts
new file mode 100644
index 0000000..655442c
--- /dev/null
+++ b/pc-bios/bamboo.dts
@@ -0,0 +1,234 @@
+/*
+ * Device Tree Source for AMCC Bamboo
+ *
+ * Copyright (c) 2006, 2007 IBM Corp.
+ * Josh Boyer <jwboyer@linux.vnet.ibm.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without
+ * any warranty of any kind, whether express or implied.
+ */
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <1>;
+ model = "amcc,bamboo";
+ compatible = "amcc,bamboo";
+ dcr-parent = <&/cpus/cpu@0>;
+
+ aliases {
+ serial0 = &UART0;
+ serial1 = &UART1;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ model = "PowerPC,440EP";
+ reg = <0>;
+ clock-frequency = <1fca0550>;
+ timebase-frequency = <017d7840>;
+ i-cache-line-size = <20>;
+ d-cache-line-size = <20>;
+ i-cache-size = <8000>;
+ d-cache-size = <8000>;
+ dcr-controller;
+ dcr-access-method = "native";
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0 0 9000000>;
+ };
+
+ UIC0: interrupt-controller0 {
+ compatible = "ibm,uic-440ep","ibm,uic";
+ interrupt-controller;
+ cell-index = <0>;
+ dcr-reg = <0c0 009>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ #interrupt-cells = <2>;
+ };
+/*
+ UIC1: interrupt-controller1 {
+ compatible = "ibm,uic-440ep","ibm,uic";
+ interrupt-controller;
+ cell-index = <1>;
+ dcr-reg = <0d0 009>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ #interrupt-cells = <2>;
+ interrupts = <1e 4 1f 4>;
+ interrupt-parent = <&UIC0>;
+ };
+*/
+
+ SDR0: sdr {
+ compatible = "ibm,sdr-440ep";
+ dcr-reg = <00e 002>;
+ };
+
+ CPR0: cpr {
+ compatible = "ibm,cpr-440ep";
+ dcr-reg = <00c 002>;
+ };
+
+ plb {
+ compatible = "ibm,plb-440ep", "ibm,plb-440gp", "ibm,plb4";
+ #address-cells = <2>;
+ #size-cells = <1>;
+ ranges;
+ clock-frequency = <07f28154>;
+
+ SDRAM0: sdram {
+ compatible = "ibm,sdram-440ep", "ibm,sdram-405gp";
+ dcr-reg = <010 2>;
+ };
+
+ DMA0: dma {
+ compatible = "ibm,dma-440ep", "ibm,dma-440gp";
+ dcr-reg = <100 027>;
+ };
+
+ POB0: opb {
+ compatible = "ibm,opb-440ep", "ibm,opb-440gp", "ibm,opb";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ /* Bamboo is oddball in the 44x world and doesn't use the ERPN
+ * bits.
+ */
+ ranges = <00000000 0 00000000 80000000
+ 80000000 0 80000000 80000000>;
+ /* interrupt-parent = <&UIC1>; */
+ interrupts = <7 4>;
+ clock-frequency = <03f940aa>;
+
+ EBC0: ebc {
+ compatible = "ibm,ebc-440ep", "ibm,ebc-440gp", "ibm,ebc";
+ dcr-reg = <012 2>;
+ #address-cells = <2>;
+ #size-cells = <1>;
+ clock-frequency = <03f940aa>;
+ interrupts = <5 1>;
+ /* interrupt-parent = <&UIC1>; */
+ };
+
+ UART0: serial@ef600300 {
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <ef600300 8>;
+ virtual-reg = <ef600300>;
+ clock-frequency = <00a8c000>;
+ current-speed = <1c200>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <0 4>;
+ };
+
+ UART1: serial@ef600400 {
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <ef600400 8>;
+ virtual-reg = <ef600400>;
+ clock-frequency = <00a8c000>;
+ current-speed = <0>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <1 4>;
+ };
+/*
+ UART2: serial@ef600500 {
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <ef600500 8>;
+ virtual-reg = <ef600500>;
+ clock-frequency = <0>;
+ current-speed = <0>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <3 4>;
+ };
+
+ UART3: serial@ef600600 {
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <ef600600 8>;
+ virtual-reg = <ef600600>;
+ clock-frequency = <0>;
+ current-speed = <0>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <4 4>;
+ };
+
+*/
+ IIC0: i2c@ef600700 {
+ device_type = "i2c";
+ compatible = "ibm,iic-440ep", "ibm,iic-440gp", "ibm,iic";
+ reg = <ef600700 14>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <2 4>;
+ };
+
+ IIC1: i2c@ef600800 {
+ device_type = "i2c";
+ compatible = "ibm,iic-440ep", "ibm,iic-440gp", "ibm,iic";
+ reg = <ef600800 14>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <7 4>;
+ };
+
+ ZMII0: emac-zmii@ef600d00 {
+ device_type = "zmii-interface";
+ compatible = "ibm,zmii-440ep", "ibm,zmii-440gp", "ibm,zmii";
+ reg = <ef600d00 c>;
+ };
+
+ };
+
+ PCI0: pci@ec000000 {
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ compatible = "ibm,plb440ep-pci", "ibm,plb-pci";
+ primary;
+ reg = <0 eec00000 8 /* Config space access */
+ 0 eed00000 4 /* IACK */
+ 0 eed00000 4 /* Special cycle */
+ 0 ef400000 40>; /* Internal registers */
+
+ /* Outbound ranges, one memory and one IO,
+ * later cannot be changed. Chip supports a second
+ * IO range but we don't use it for now
+ */
+ ranges = <02000000 0 a0000000 0 a0000000 0 20000000
+ 01000000 0 00000000 0 e8000000 0 00010000>;
+
+ /* Inbound 2GB range starting at 0 */
+ dma-ranges = <42000000 0 0 0 0 0 80000000>;
+
+ /* Bamboo has all 4 IRQ pins tied together per slot */
+ interrupt-map-mask = <f800 0 0 0>;
+ interrupt-map = <
+ /* IDSEL 1 */
+ 0800 0 0 0 &UIC0 1c 8
+
+ /* IDSEL 2 */
+ 1000 0 0 0 &UIC0 1b 8
+
+ /* IDSEL 3 */
+ 1800 0 0 0 &UIC0 1a 8
+
+ /* IDSEL 4 */
+ 2000 0 0 0 &UIC0 19 8
+ >;
+ };
+
+ };
+
+ chosen {
+ linux,stdout-path = "/plb/opb/serial@ef600300";
+ };
+};
diff --git a/pc-bios/bios-vista.diff b/pc-bios/bios-vista.diff
new file mode 100644
index 0000000..684a310
--- /dev/null
+++ b/pc-bios/bios-vista.diff
@@ -0,0 +1,17 @@
+Index: rombios32.c
+===================================================================
+RCS file: /cvsroot/bochs/bochs/bios/rombios32.c,v
+retrieving revision 1.9
+diff -u -w -r1.9 rombios32.c
+--- rombios32.c 20 Feb 2007 09:36:55 -0000 1.9
++++ rombios32.c 2 May 2007 06:07:31 -0000
+@@ -1191,7 +1191,7 @@
+ {
+ memcpy(h->signature, sig, 4);
+ h->length = cpu_to_le32(len);
+- h->revision = 0;
++ h->revision = 1;
+ #ifdef BX_QEMU
+ memcpy(h->oem_id, "QEMU ", 6);
+ memcpy(h->oem_table_id, "QEMU", 4);
+
diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
new file mode 100644
index 0000000..e191908
--- /dev/null
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/bochs-manifest b/pc-bios/bochs-manifest
new file mode 100644
index 0000000..1b25aa4
--- /dev/null
+++ b/pc-bios/bochs-manifest
@@ -0,0 +1,24 @@
+.cvsignore 1.2
+BIOS-bochs-latest 1.145
+BIOS-bochs-legacy 1.9
+Makefile.in 1.26
+VGABIOS-elpin-2.40 1.4
+VGABIOS-elpin-LICENSE 1.3
+VGABIOS-lgpl-README 1.9
+VGABIOS-lgpl-latest 1.13
+VGABIOS-lgpl-latest-cirrus 1.5
+VGABIOS-lgpl-latest-cirrus-debug 1.5
+VGABIOS-lgpl-latest-debug 1.9
+acpi-dsdt.dsl 1.1
+acpi-dsdt.hex 1.1
+apmbios.S 1.5
+bios_usage 1.1
+biossums.c 1.3
+makesym.perl 1.1
+notes 1.1
+rombios.c 1.178
+rombios.h 1.4
+rombios32.c 1.9
+rombios32.ld 1.1
+rombios32start.S 1.3
+usage.cc 1.4
diff --git a/pc-bios/gpxe-eepro100-80861209.rom b/pc-bios/gpxe-eepro100-80861209.rom
new file mode 100644
index 0000000..2ca59ec
--- /dev/null
+++ b/pc-bios/gpxe-eepro100-80861209.rom
Binary files differ
diff --git a/pc-bios/keymaps/ar b/pc-bios/keymaps/ar
new file mode 100644
index 0000000..c430c03
--- /dev/null
+++ b/pc-bios/keymaps/ar
@@ -0,0 +1,98 @@
+# generated from XKB map ar
+include common
+map 0x401
+exclam 0x02 shift
+at 0x03 shift
+numbersign 0x04 shift
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Arabic_dad 0x10 altgr
+Arabic_fatha 0x10 shift altgr
+Arabic_sad 0x11 altgr
+Arabic_fathatan 0x11 shift altgr
+Arabic_theh 0x12 altgr
+Arabic_damma 0x12 shift altgr
+Arabic_qaf 0x13 altgr
+Arabic_dammatan 0x13 shift altgr
+Arabic_feh 0x14 altgr
+UFEF9 0x14 shift altgr
+Arabic_ghain 0x15 altgr
+Arabic_hamzaunderalef 0x15 shift altgr
+Arabic_ain 0x16 altgr
+grave 0x16 shift altgr
+Arabic_ha 0x17 altgr
+division 0x17 shift altgr
+Arabic_khah 0x18 altgr
+multiply 0x18 shift altgr
+Arabic_hah 0x19 altgr
+Arabic_semicolon 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Arabic_jeem 0x1a altgr
+bracketright 0x1b
+braceright 0x1b shift
+Arabic_dal 0x1b altgr
+Arabic_sheen 0x1e altgr
+backslash 0x1e shift altgr
+Arabic_seen 0x1f altgr
+Arabic_yeh 0x20 altgr
+bracketleft 0x20 shift altgr
+Arabic_beh 0x21 altgr
+bracketright 0x21 shift altgr
+Arabic_lam 0x22 altgr
+UFEF7 0x22 shift altgr
+Arabic_alef 0x23 altgr
+Arabic_hamzaonalef 0x23 shift altgr
+Arabic_teh 0x24 altgr
+Arabic_tatweel 0x24 shift altgr
+Arabic_noon 0x25 altgr
+Arabic_comma 0x25 shift altgr
+Arabic_meem 0x26 altgr
+slash 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Arabic_kaf 0x27 altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Arabic_tah 0x28 altgr
+grave 0x29
+asciitilde 0x29 shift
+Arabic_thal 0x29 altgr
+Arabic_shadda 0x29 shift altgr
+backslash 0x2b
+bar 0x2b shift
+less 0x2b altgr
+greater 0x2b shift altgr
+Arabic_hamzaonyeh 0x2c altgr
+asciitilde 0x2c shift altgr
+Arabic_hamza 0x2d altgr
+Arabic_sukun 0x2d shift altgr
+Arabic_hamzaonwaw 0x2e altgr
+Arabic_kasra 0x2e shift altgr
+Arabic_ra 0x2f altgr
+Arabic_kasratan 0x2f shift altgr
+UFEFB 0x30 altgr
+UFEF5 0x30 shift altgr
+Arabic_alefmaksura 0x31 altgr
+Arabic_maddaonalef 0x31 shift altgr
+Arabic_tehmarbuta 0x32 altgr
+apostrophe 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Arabic_waw 0x33 altgr
+period 0x34
+greater 0x34 shift
+Arabic_zain 0x34 altgr
+slash 0x35
+question 0x35 shift
+Arabic_zah 0x35 altgr
+Arabic_question_mark 0x35 shift altgr
diff --git a/pc-bios/keymaps/bepo b/pc-bios/keymaps/bepo
new file mode 100644
index 0000000..d40041a
--- /dev/null
+++ b/pc-bios/keymaps/bepo
@@ -0,0 +1,333 @@
+include common
+
+# Bépo : Improved ergonomic french keymap using Dvorak method.
+# Built by community on 'Dvorak Fr / Bépo' :
+# see http://www.clavier-dvorak.org/wiki/ to join and help.
+#
+# Bépo layout (1.0rc2 version) for a pc105 keyboard (french) :
+# ┌────â”
+# │ S A│ S = Shift, A = AltGr + Shift
+# │ s a│ s = normal, a = AltGr
+# └────┘
+#
+# ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┲â”â”â”â”â”â”â”â”â”┓
+# │ # ¶ │ 1 „ │ 2 “ │ 3 †│ 4 ≤ │ 5 ≥ │ 6 │ 7 ¬ │ 8 ¼ │ 9 ½ │ 0 ¾ │ ° ′ │ ` ″ ┃ ⌫ Retour┃
+# │ $ – │ " — │ « < │ » > │ ( [ │ ) ] │ @ ^ │ + ± │ - − │ / ÷ │ * × │ = ≠ │ % ‰ ┃ arrière┃
+# ┢â”â”â”â”â”â”·â”┱───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┺â”┳â”â”â”â”â”â”â”┫
+# ┃ ┃ B ¦ │ É Ë â”‚ P § │ O Å’ │ È ` │ ! │ V │ D à │ L │ J IJ │ Z Æ â”‚ W ┃Entrée ┃
+# ┃Tab ↹ ┃ b | │ é ˊ │ p & │ o œ │ è ` │ ˆ ¡ │ v ˇ │ d ð │ l / │ j ij │ z ə │ w ̆ ┃ ⎠┃
+# ┣â”â”â”â”â”â”â”┻┱────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┺┓ ┃
+# ┃ ┃ A Æ │ U Ù │ I ˙ │ E ¤ │ ; ̛ │ C ſ │ T Þ │ S ẞ │ R ™ │ N │ M º │ Ç , ┃ ┃
+# ┃Maj ⇬ ┃ a æ │ u ù │ i ̈ │ e € │ , ’ │ c © │ t þ │ s ß │ r ® │ n ˜ │ m ¯ │ ç ¸ ┃ ┃
+# ┣â”â”â”â”â”â”â”┳┹────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┲┷â”â”â”â”â”â”»â”â”â”â”â”â”┫
+# ┃ ┃ Ê │ À │ Y ‘ │ X ’ │ : · │ K │ ? ̉ │ Q ̣ │ G │ H ‡ │ F ª ┃ ┃
+# ┃Shift ⇧┃ ê / │ à \ │ y { │ x } │ . … │ k ~ │ ' ¿ │ q ˚ │ g µ │ h † │ f ˛ ┃Shift ⇧ ┃
+# ┣â”â”â”â”â”â”â”â•‹â”â”â”â”â”â”·â”┳â”â”â”â”·â”â”â”┱─┴─────┴─────┴─────┴─────┴─────┴───┲â”â”·â”â”â”â”â”╈â”â”â”â”â”â”»â”┳â”â”â”â”â”â”â”┳â”â”â”â”›
+# ┃ ┃ ┃ ┃ Espace inséc. Espace inséc. fin ┃ ┃ ┃ ┃
+# ┃Ctrl ┃Meta ┃Alt ┃ ⣠(Espace) _ ⣠┃AltGr ⇮┃Menu ┃Ctrl ┃
+# â”—â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”┹───────────────────────────────────┺â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”â”›
+
+
+# First row
+## keycode 41 = dollar numbersign U+2013 U+00b6
+dollar 0x29
+numbersign 0x29 shift
+U2013 0x29 altgr
+U00b6 0x29 shift altgr
+
+## keycode 2 = +quotedbl +one U+2014 U+201e
+quotedbl 0x2
+one 0x2 shift
+U2014 0x2 altgr
+U201e 0x2 shift altgr
+
+## keycode 3 = +guillemotleft +two less U+201c
+guillemotleft 0x3
+two 0x3 shift
+less 0x3 altgr
+U201c 0x3 shift altgr
+
+## keycode 4 = +guillemotright +three greater U+201d
+guillemotright 0x4
+three 0x4 shift
+greater 0x4 altgr
+U201d 0x4 shift altgr
+
+## keycode 5 = +parenleft +four bracketleft U+2264
+parenleft 0x5
+four 0x5 shift
+bracketleft 0x5 altgr
+U2264 0x5 shift altgr
+
+## keycode 6 = +parenright +five bracketright U+2265
+parenright 0x6
+five 0x6 shift
+bracketright 0x6 altgr
+U2265 0x6 shift altgr
+
+## keycode 7 = +at +six asciicircum
+at 0x7
+six 0x7 shift
+asciicircum 0x7 altgr
+
+## keycode 8 = +plus +seven U+00b1 U+00ac
+plus 0x8
+seven 0x8 shift
+U00b1 0x8 altgr
+U00ac 0x8 shift altgr
+
+## keycode 9 = +minus +eight U+2212 U+00bc
+minus 0x9
+eight 0x9 shift
+U2212 0x9 altgr
+U00bc 0x9 shift altgr
+
+## keycode 10 = +slash +nine U+00f7 U+00bd
+slash 0xa
+nine 0xa shift
+U00f7 0xa altgr
+U00bd 0xa shift altgr
+
+## keycode 11 = +asterisk +zero U+00d7 U+00be
+asterisk 0xb
+zero 0xb shift
+U00d7 0xb altgr
+U00be 0xb shift altgr
+
+## keycode 12 = equal U+00b0 U+2260 U+2032
+equal 0xc
+U00b0 0xc shift
+U2260 0xc altgr
+U2032 0xc shift altgr
+
+## keycode 13 = percent grave U+2030 U+2033
+percent 0xd
+grave 0xd shift
+U2030 0xd altgr
+U2033 0xd shift altgr
+
+
+# Second row
+
+# simplified letter definitions notation :
+## keycode 16 = b
+b 0x10 addupper
+## keycode 18 = p
+p 0x12 addupper
+## keycode 19 = o
+o 0x13 addupper
+## keycode 22 = v
+v 0x16 addupper
+## keycode 23 = d
+d 0x17 addupper
+## keycode 24 = l
+l 0x18 addupper
+## keycode 25 = j
+j 0x19 addupper
+## keycode 26 = z
+z 0x1a addupper
+## keycode 27 = w
+w 0x1b addupper
+
+# then, add specific definitions
+## AltGr keycode 16 = bar
+bar 0x10 altgr
+## Shift AltGr keycode 16 = brokenbar
+brokenbar 0x10 shift altgr
+
+## keycode 17 = +eacute +Eacute dead_acute
+eacute 0x11
+Eacute 0x11 shift
+dead_acute 0x11 altgr
+
+## AltGr keycode 18 = ampersand
+ampersand 0x12 altgr
+## Shift AltGr keycode 18 = U+00a7
+U00a7 0x12 shift altgr
+
+## AltGr keycode 19 = +U+0153
+U+0153 0x13 altgr
+## Shift AltGr keycode 19 = +U+0152
+U+0152 0x13 shift altgr
+
+## keycode 20 = +egrave +Egrave dead_grave grave # no Meta !
+egrave 0x14
+Egrave 0x14 shift
+dead_grave 0x14 altgr
+
+## keycode 21 = dead_circumflex exclam exclamdown
+dead_circumflex 0x15
+exclam 0x15 shift
+exclamdown 0x15 altgr
+
+## AltGr keycode 22 = dead_caron
+dead_caron 0x16 altgr
+
+## AltGr keycode 23 = eth
+eth 0x17 altgr
+## Shift AltGr keycode 23 = ETH
+ETH 0x17 shift altgr
+
+## AltGr keycode 25 = +U+0133
+U+0133 0x19 altgr
+## Shift AltGr keycode 25 = +U+0132
+U+0132 0x19 shift altgr
+
+## AltGr keycode 26 = +U+0259
+U+0259 0x1a altgr
+## Shift AltGr keycode 26 = +U+018f
+U+018f 0x1a shift altgr
+
+
+
+# Third row
+
+# simplified letter definitions notation :
+## keycode 30 = a
+a 0x1e addupper
+## keycode 31 = u
+u 0x1f addupper
+## keycode 32 = i
+i 0x20 addupper
+## keycode 33 = e
+e 0x21 addupper
+## keycode 35 = c
+c 0x23 addupper
+## keycode 36 = t
+t 0x24 addupper
+## keycode 37 = s
+s 0x25 addupper
+## keycode 38 = r
+r 0x26 addupper
+## keycode 39 = n
+n 0x27 addupper
+## keycode 40 = m
+m 0x28 addupper
+
+# then, add specific definitions
+## AltGr keycode 30 = +ae
+ae 0x1e altgr
+## Shift AltGr keycode 30 = +AE
+AE 0x1e shift altgr
+
+## AltGr keycode 31 = +ugrave
+ugrave 0x1f altgr
+## Shift AltGr keycode 31 = +Ugrave
+Ugrave 0x1f shift altgr
+
+## AltGr keycode 32 = dead_diaeresis
+dead_diaeresis 0x20 altgr
+
+
+## AltGr keycode 33 = U+20ac
+U20ac 0x21 altgr
+
+## keycode 34 = comma semicolon U+2019 +U+031b
+comma 0x22
+semicolon 0x22 shift
+U2019 0x22 altgr
+U+031b 0x22 shift altgr
+
+## AltGr keycode 35 = copyright
+copyright 0x23 altgr
+## Shift AltGr keycode 35 = U+017f
+U017f 0x23 shift altgr
+
+## AltGr keycode 36 = +thorn
+thorn 0x24 altgr
+## Shift AltGr keycode 36 = +THORN
+THORN 0x24 shift altgr
+
+## AltGr keycode 37 = +ssharp
+ssharp 0x25 altgr
+## Shift AltGr keycode 37 = U+1e9e
+U1e9e 0x25 shift altgr
+
+## AltGr keycode 38 = registered
+registered 0x26 altgr
+## Shift AltGr keycode 38 = U+2122
+U2122 0x26 shift altgr
+
+## AltGr keycode 39 = dead_tilde
+dead_tilde 0x27 altgr
+
+## Shift AltGr keycode 40 = masculine
+masculine 0x28 shift altgr
+
+## keycode 43 = +ccedilla +Ccedilla dead_cedilla
+ccedilla 0x2b
+Ccedilla 0x2b shift
+dead_cedilla 0x2b altgr
+
+
+# Fourth row
+
+# simplified letter definitions notation :
+## keycode 45 = y
+y 0x2d addupper
+## keycode 46 = x
+x 0x2e addupper
+## keycode 48 = k
+k 0x30 addupper
+## keycode 50 = q
+q 0x32 addupper
+## keycode 51 = g
+g 0x33 addupper
+## keycode 52 = h
+h 0x34 addupper
+## keycode 53 = f
+f 0x35 addupper
+
+# then, add specific definitions
+## keycode 86 = +ecircumflex +Ecircumflex slash slash
+ecircumflex 0x56
+Ecircumflex 0x56 shift
+
+## keycode 44 = +agrave +Agrave backslash
+agrave 0x2c
+Agrave 0x2c shift
+backslash 0x2c altgr
+
+## AltGr keycode 45 = braceleft
+braceleft 0x2d altgr
+## Shift AltGr keycode 45 = U+2018
+U2018 0x2d shift altgr
+
+## AltGr keycode 46 = braceright
+braceright 0x2e altgr
+
+## keycode 47 = period colon U+2026 periodcentered
+period 0x2f
+colon 0x2f shift
+U2026 0x2f altgr
+periodcentered 0x2f shift altgr
+
+## AltGr keycode 48 = asciitilde
+asciitilde 0x30 altgr
+## Shift AltGr keycode 48 = U+2328
+U2328 0x30 shift altgr
+
+## keycode 49 = apostrophe question questiondown +U+0309
+apostrophe 0x31
+question 0x31 shift
+questiondown 0x31 altgr
+U+0309 0x31 shift altgr
+
+## AltGr keycode 51 = mu
+mu 0x33 altgr
+
+## AltGr keycode 52 = U+2020
+U2020 0x34 altgr
+## Shift AltGr keycode 52 = U+2021
+U2021 0x34 shift altgr
+
+## Shift AltGr keycode 53 = ordfeminine
+ordfeminine 0x35 shift altgr
+
+
+
+## keycode 57 = space nobreakspace underscore U+202f
+space 0x39
+nobreakspace 0x39 shift
+underscore 0x39 altgr
+U202f 0x39 shift altgr
diff --git a/pc-bios/keymaps/common b/pc-bios/keymaps/common
new file mode 100644
index 0000000..adc56c7
--- /dev/null
+++ b/pc-bios/keymaps/common
@@ -0,0 +1,157 @@
+include modifiers
+
+#
+# Top row
+#
+1 0x2
+2 0x3
+3 0x4
+4 0x5
+5 0x6
+6 0x7
+7 0x8
+8 0x9
+9 0xa
+0 0xb
+BackSpace 0xe
+
+#
+# QWERTY first row
+#
+Tab 0xf localstate
+ISO_Left_Tab 0xf shift
+q 0x10 addupper
+w 0x11 addupper
+e 0x12 addupper
+r 0x13 addupper
+t 0x14 addupper
+y 0x15 addupper
+u 0x16 addupper
+i 0x17 addupper
+o 0x18 addupper
+p 0x19 addupper
+
+#
+# QWERTY second row
+#
+a 0x1e addupper
+s 0x1f addupper
+d 0x20 addupper
+f 0x21 addupper
+g 0x22 addupper
+h 0x23 addupper
+j 0x24 addupper
+k 0x25 addupper
+l 0x26 addupper
+Return 0x1c localstate
+
+#
+# QWERTY third row
+#
+z 0x2c addupper
+x 0x2d addupper
+c 0x2e addupper
+v 0x2f addupper
+b 0x30 addupper
+n 0x31 addupper
+m 0x32 addupper
+
+space 0x39 localstate
+
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+brokenbar 0x56 shift altgr
+
+#
+# Esc and Function keys
+#
+Escape 0x1 localstate
+F1 0x3b localstate
+F2 0x3c localstate
+F3 0x3d localstate
+F4 0x3e localstate
+F5 0x3f localstate
+F6 0x40 localstate
+F7 0x41 localstate
+F8 0x42 localstate
+F9 0x43 localstate
+F10 0x44 localstate
+F11 0x57 localstate
+F12 0x58 localstate
+
+# Printscreen, Scrollock and Pause
+# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37),
+# but (0xe0, 0x37) seems to work.
+Print 0xb7 localstate
+Sys_Req 0xb7 localstate
+Execute 0xb7 localstate
+Scroll_Lock 0x46
+
+#
+# Insert - PgDown
+#
+Insert 0xd2 localstate
+Delete 0xd3 localstate
+Home 0xc7 localstate
+End 0xcf localstate
+Page_Up 0xc9 localstate
+Page_Down 0xd1 localstate
+
+#
+# Arrow keys
+#
+Left 0xcb localstate
+Up 0xc8 localstate
+Down 0xd0 localstate
+Right 0xcd localstate
+
+#
+# Numpad
+#
+Num_Lock 0x45
+KP_Divide 0xb5
+KP_Multiply 0x37
+KP_Subtract 0x4a
+KP_Add 0x4e
+KP_Enter 0x9c
+
+KP_Decimal 0x53 numlock
+KP_Separator 0x53 numlock
+KP_Delete 0x53
+
+KP_0 0x52 numlock
+KP_Insert 0x52
+
+KP_1 0x4f numlock
+KP_End 0x4f
+
+KP_2 0x50 numlock
+KP_Down 0x50
+
+KP_3 0x51 numlock
+KP_Next 0x51
+
+KP_4 0x4b numlock
+KP_Left 0x4b
+
+KP_5 0x4c numlock
+KP_Begin 0x4c
+
+KP_6 0x4d numlock
+KP_Right 0x4d
+
+KP_7 0x47 numlock
+KP_Home 0x47
+
+KP_8 0x48 numlock
+KP_Up 0x48
+
+KP_9 0x49 numlock
+KP_Prior 0x49
+
+Caps_Lock 0x3a
+#
+# Inhibited keys
+#
+Multi_key 0x0 inhibit
diff --git a/pc-bios/keymaps/da b/pc-bios/keymaps/da
new file mode 100644
index 0000000..3884dcf
--- /dev/null
+++ b/pc-bios/keymaps/da
@@ -0,0 +1,120 @@
+# generated from XKB map dk
+include common
+map 0x406
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+plusminus 0x0c altgr
+questiondown 0x0c shift altgr
+dead_acute 0x0d
+dead_grave 0x0d shift
+bar 0x0d altgr
+brokenbar 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ae 0x27
+AE 0x27 shift
+oslash 0x28
+Ooblique 0x28 shift
+dead_caron 0x28 shift altgr
+onehalf 0x29
+section 0x29 shift
+threequarters 0x29 altgr
+paragraph 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+dead_doubleacute 0x2b altgr
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
+less 0x56
+greater 0x56 shift
+backslash 0x56 altgr
+notsign 0x56 shift altgr
diff --git a/pc-bios/keymaps/de b/pc-bios/keymaps/de
new file mode 100644
index 0000000..ed929c7
--- /dev/null
+++ b/pc-bios/keymaps/de
@@ -0,0 +1,114 @@
+# generated from XKB map de
+include common
+map 0x407
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+section 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+ssharp 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+acute 0x0d
+dead_acute 0x0d
+grave 0x0d shift
+dead_grave 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+asciitilde 0x1b altgr
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+dead_doubleacute 0x27 altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+dead_caron 0x28 shift altgr
+asciicircum 0x29
+dead_circumflex 0x29
+degree 0x29 shift
+notsign 0x29 altgr
+numbersign 0x2b
+apostrophe 0x2b shift
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/de-ch b/pc-bios/keymaps/de-ch
new file mode 100644
index 0000000..852f8b8
--- /dev/null
+++ b/pc-bios/keymaps/de-ch
@@ -0,0 +1,169 @@
+# rdesktop Swiss-German (de-ch) keymap file
+# 2003-06-03 by noldi@tristar.ch
+#
+include common
+map 0x00000807
+#
+# Scan Code 1
+section 0x29
+degree 0x29 shift
+notsign 0x29 altgr inhibit
+#
+# Scan Code 2
+plus 0x2 shift
+brokenbar 0x02 altgr
+#
+# Scan Code 3
+quotedbl 0x03 shift
+at 0x03 altgr
+#
+# Scan Code 4
+asterisk 0x04 shift
+numbersign 0x04 altgr
+#
+# Scan Code 5
+ccedilla 0x05 shift
+onequarter 0x05 altgr inhibit
+#
+# Scan Code 6
+percent 0x06 shift
+onehalf 0x06 altgr inhibit
+#
+# Scan Code 7
+ampersand 0x07 shift
+notsign 0x07 altgr
+#
+# Scan Code 8
+slash 0x08 shift
+bar 0x08 altgr
+#
+# Scan Code 9
+parenleft 0x09 shift
+cent 0x09 altgr
+#
+# Scan Code 10
+parenright 0x0a shift
+#
+# Scan Code 11
+equal 0x0b shift
+braceright 0x0b altgr inhibit
+#
+# Scan Code 12
+apostrophe 0x0c
+question 0x0c shift
+dead_acute 0x0c altgr
+#
+# Scan Code 13
+dead_circumflex 0x0d
+dead_grave 0x0d shift
+dead_tilde 0x0d altgr
+#
+# Scan Code 19
+EuroSign 0x12 altgr
+#
+# Scan Code 22
+z 0x15 addupper
+#
+# Scan Code 27
+udiaeresis 0x1a
+egrave 0x1a shift
+bracketleft 0x1a altgr
+#
+# Scan Code 28
+dead_diaeresis 0x1b
+exclam 0x1b shift
+bracketright 0x1b altgr
+#
+# Scan Code 40
+odiaeresis 0x27
+eacute 0x27 shift
+#
+# Scan Code 41
+adiaeresis 0x28
+agrave 0x28 shift
+braceleft 0x28 altgr
+#
+# Scan Code 42 (only on international keyboards)
+dollar 0x2b
+sterling 0x2b shift
+braceright 0x2b altgr
+#
+# Scan Code 45 (only on international keyboards)
+backslash 0x56 altgr
+#
+# Scan Code 46
+y 0x2c addupper
+#
+# Scan Code 53
+comma 0x33
+semicolon 0x33 shift
+#
+# Scan Code 54
+period 0x34
+colon 0x34 shift
+#
+# Scan Code 55
+minus 0x35
+underscore 0x35 shift
+#
+# Suppress Windows unsupported AltGr keys
+#
+# Scan Code 17
+paragraph 0x10 altgr inhibit
+#
+# Scan Code 21
+tslash 0x14 altgr inhibit
+#
+# Scan Code 22
+leftarrow 0x15 altgr inhibit
+#
+# Scan Code 23
+downarrow 0x16 altgr inhibit
+#
+# Scan Code 24
+rightarrow 0x17 altgr inhibit
+#
+# Scan Code 25
+oslash 0x18 altgr inhibit
+#
+# Scan Code 26
+thorn 0x19 altgr inhibit
+#
+# Scan Code 31
+ae 0x1e altgr inhibit
+#
+# Scan Code 32
+ssharp 0x1f altgr inhibit
+#
+# Scan Code 33
+eth 0x20 altgr inhibit
+#
+# Scan Code 34
+dstroke 0x21 altgr inhibit
+#
+# Scan Code 35
+eng 0x22 altgr inhibit
+#
+# Scan Code 36
+hstroke 0x23 altgr inhibit
+#
+# Scan Code 38
+kra 0x25 altgr inhibit
+#
+# Scan Code 39
+lstroke 0x26 altgr inhibit
+#
+# Scan Code 46
+guillemotleft 0x2c altgr inhibit
+#
+# Scan Code 47
+guillemotright 0x2d altgr inhibit
+#
+# Scan Code 49
+leftdoublequotemark 0x2f altgr inhibit
+#
+# Scan Code 50
+rightdoublequotemark 0x30 altgr inhibit
+#
+# Scan Code 52
+mu 0x32 altgr inhibit
diff --git a/pc-bios/keymaps/en-gb b/pc-bios/keymaps/en-gb
new file mode 100644
index 0000000..b45f06c
--- /dev/null
+++ b/pc-bios/keymaps/en-gb
@@ -0,0 +1,119 @@
+# generated from XKB map gb
+include common
+map 0x809
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+sterling 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+EuroSign 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+at 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+grave 0x29
+notsign 0x29 shift
+bar 0x29 altgr
+numbersign 0x2b
+asciitilde 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+backslash 0x56
+bar 0x56 shift
diff --git a/pc-bios/keymaps/en-us b/pc-bios/keymaps/en-us
new file mode 100644
index 0000000..f5784bb
--- /dev/null
+++ b/pc-bios/keymaps/en-us
@@ -0,0 +1,35 @@
+# generated from XKB map us
+include common
+map 0x409
+exclam 0x02 shift
+at 0x03 shift
+numbersign 0x04 shift
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+bracketleft 0x1a
+braceleft 0x1a shift
+bracketright 0x1b
+braceright 0x1b shift
+semicolon 0x27
+colon 0x27 shift
+apostrophe 0x28
+quotedbl 0x28 shift
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+slash 0x35
+question 0x35 shift
diff --git a/pc-bios/keymaps/es b/pc-bios/keymaps/es
new file mode 100644
index 0000000..0c29eec
--- /dev/null
+++ b/pc-bios/keymaps/es
@@ -0,0 +1,105 @@
+# generated from XKB map es
+include common
+map 0x40a
+exclam 0x02 shift
+bar 0x02 altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+periodcentered 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+asciitilde 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+notsign 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+trademark 0x09 shift altgr
+parenright 0x0a shift
+plusminus 0x0a shift altgr
+equal 0x0b shift
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+exclamdown 0x0d
+questiondown 0x0d shift
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+dead_grave 0x1a
+dead_circumflex 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ntilde 0x27
+Ntilde 0x27 shift
+dead_doubleacute 0x27 shift altgr
+dead_acute 0x28
+dead_diaeresis 0x28 shift
+braceleft 0x28 altgr
+masculine 0x29
+ordfeminine 0x29 shift
+backslash 0x29 altgr
+ccedilla 0x2b
+Ccedilla 0x2b shift
+braceright 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x56
+greater 0x56 shift
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/et b/pc-bios/keymaps/et
new file mode 100644
index 0000000..85541a3
--- /dev/null
+++ b/pc-bios/keymaps/et
@@ -0,0 +1,85 @@
+map 0x00000425
+include common
+
+#
+# Top row
+#
+dead_caron 0x29
+dead_tilde 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+backslash 0xc altgr
+
+acute 0xd
+dead_acute 0xd
+grave 0xd shift
+dead_grave 0xd shift
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+otilde 0x1b
+Otilde 0x1b shift
+section 0x1b altgr
+
+#
+# QWERTY second row
+#
+scaron 0x1f altgr
+Scaron 0x1f altgr shift
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+asciicircum 0x28 altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+onehalf 0x2b altgr
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+zcaron 0x2c altgr
+Zcaron 0x2c altgr shift
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
diff --git a/pc-bios/keymaps/fi b/pc-bios/keymaps/fi
new file mode 100644
index 0000000..2a4e0f0
--- /dev/null
+++ b/pc-bios/keymaps/fi
@@ -0,0 +1,124 @@
+# generated from XKB map se_FI
+include common
+map 0x40b
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+dead_acute 0x0d
+dead_grave 0x0d shift
+plusminus 0x0d altgr
+notsign 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+oslash 0x27 altgr
+Ooblique 0x27 shift altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+ae 0x28 altgr
+AE 0x28 shift altgr
+section 0x29
+onehalf 0x29 shift
+paragraph 0x29 altgr
+threequarters 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+acute 0x2b altgr
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
diff --git a/pc-bios/keymaps/fo b/pc-bios/keymaps/fo
new file mode 100644
index 0000000..c00d9d4
--- /dev/null
+++ b/pc-bios/keymaps/fo
@@ -0,0 +1,76 @@
+map 0x438
+include common
+
+#
+# Top row
+#
+onehalf 0x29
+section 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+plusminus 0xc altgr
+
+bar 0xd altgr
+dead_acute 0xd
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+aring 0x1a
+Aring 0x1a shift
+eth 0x1b addupper
+asciitilde 0x1b altgr
+
+#
+# QWERTY second row
+#
+ae 0x27 addupper
+oslash 0x28
+Ooblique 0x28 shift
+apostrophe 0x2b
+asterisk 0x2b shift
+
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+backslash 0x56 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
diff --git a/pc-bios/keymaps/fr b/pc-bios/keymaps/fr
new file mode 100644
index 0000000..ba5a176
--- /dev/null
+++ b/pc-bios/keymaps/fr
@@ -0,0 +1,181 @@
+include common
+map 0x40c
+#
+# Top row
+#
+twosuperior 0x29
+notsign 0x29 altgr
+
+ampersand 0x02
+1 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+
+eacute 0x03
+2 0x03 shift
+asciitilde 0x03 altgr
+oneeighth 0x03 shift altgr
+
+quotedbl 0x04
+3 0x04 shift
+numbersign 0x04 altgr
+
+apostrophe 0x05
+4 0x05 shift
+braceleft 0x05 altgr
+
+parenleft 0x06
+5 0x06 shift
+bracketleft 0x06 altgr
+threeeighths 0x06 shift altgr
+
+minus 0x07
+6 0x07 shift
+bar 0x07 altgr
+fiveeighths 0x07 shift altgr
+
+egrave 0x08
+7 0x08 shift
+grave 0x08 altgr
+seveneighths 0x08 shift altgr
+
+underscore 0x09
+8 0x09 shift
+backslash 0x09 altgr
+trademark 0x09 shift altgr
+
+ccedilla 0x0a
+9 0x0a shift
+asciicircum 0x0a altgr
+plusminus 0x0a shift altgr
+
+agrave 0x0b
+0 0x0b shift
+at 0x0b altgr
+
+parenright 0x0c
+degree 0x0c shift
+bracketright 0x0c altgr
+questiondown 0x0c shift altgr
+
+equal 0x0d
+plus 0x0d shift
+braceright 0x0d altgr
+dead_ogonek 0x0d shift altgr
+
+#
+# AZERTY first row
+#
+
+a 0x10 addupper
+ae 0x10 altgr
+AE 0x10 shift altgr
+
+z 0x11 addupper
+guillemotleft 0x11 altgr
+
+EuroSign 0x12 altgr
+
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+
+dead_circumflex 0x1a
+dead_diaeresis 0x1a shift
+dead_abovering 0x1a shift altgr
+
+dollar 0x1b
+sterling 0x1b shift
+currency 0x1b altgr
+dead_macron 0x1b shift altgr
+
+#
+# AZERTY second row
+#
+q 0x1e addupper
+Greek_OMEGA 0x1e shift altgr
+
+ssharp 0x1f altgr
+
+eth 0x20 altgr
+ETH 0x20 shift altgr
+
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+
+eng 0x22 altgr
+ENG 0x22 shift altgr
+
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+
+kra 0x25 altgr
+
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+
+m 0x27 addupper
+masculine 0x27 shift altgr
+
+ugrave 0x28
+percent 0x28 shift
+dead_caron 0x28 shift altgr
+
+asterisk 0x2b
+mu 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+
+#
+# AZERTY third row
+#
+less 0x56
+greater 0x56 shift
+
+w 0x2c addupper
+
+guillemotright 0x2d altgr
+
+cent 0x2e altgr
+copyright 0x2e shift altgr
+
+leftdoublequotemark 0x2f altgr
+
+rightdoublequotemark 0x30 altgr
+
+comma 0x32
+question 0x32 shift
+dead_acute 0x32 altgr
+dead_doubleacute 0x32 shift altgr
+
+semicolon 0x33
+period 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+
+colon 0x34
+slash 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+
+exclam 0x35
+section 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/fr-be b/pc-bios/keymaps/fr-be
new file mode 100644
index 0000000..62f7128
--- /dev/null
+++ b/pc-bios/keymaps/fr-be
@@ -0,0 +1,134 @@
+# generated from XKB map be
+include common
+map 0x80c
+ampersand 0x02
+1 0x02 shift
+bar 0x02 altgr
+exclamdown 0x02 shift altgr
+eacute 0x03
+2 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+quotedbl 0x04
+3 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+apostrophe 0x05
+4 0x05 shift
+onequarter 0x05 altgr
+dollar 0x05 shift altgr
+parenleft 0x06
+5 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+section 0x07
+6 0x07 shift
+asciicircum 0x07 altgr
+fiveeighths 0x07 shift altgr
+egrave 0x08
+7 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+exclam 0x09
+8 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+ccedilla 0x0a
+9 0x0a shift
+braceleft 0x0a altgr
+plusminus 0x0a shift altgr
+agrave 0x0b
+0 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+parenright 0x0c
+degree 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+minus 0x0d
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+a 0x10 addupper
+Greek_OMEGA 0x10 shift altgr
+z 0x11 addupper
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+dead_circumflex 0x1a
+dead_diaeresis 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+dollar 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+q 0x1e addupper
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+m 0x27 addupper
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+ugrave 0x28
+percent 0x28 shift
+dead_acute 0x28 altgr
+dead_caron 0x28 shift altgr
+twosuperior 0x29
+threesuperior 0x29 shift
+notsign 0x29 altgr
+mu 0x2b
+sterling 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+w 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+comma 0x32
+question 0x32 shift
+dead_cedilla 0x32 altgr
+masculine 0x32 shift altgr
+semicolon 0x33
+period 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+colon 0x34
+slash 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+equal 0x35
+plus 0x35 shift
+dead_tilde 0x35 altgr
+dead_abovedot 0x35 shift altgr
+backslash 0x56 altgr
diff --git a/pc-bios/keymaps/fr-ca b/pc-bios/keymaps/fr-ca
new file mode 100644
index 0000000..b645208
--- /dev/null
+++ b/pc-bios/keymaps/fr-ca
@@ -0,0 +1,50 @@
+# Canadian French
+# By Simon Germain
+include common
+map 0xc0c
+
+backslash 0x29 altgr
+plusminus 0x2 altgr
+at 0x3 altgr
+sterling 0x4 altgr
+cent 0x5 altgr
+currency 0x6 altgr
+notsign 0x7 altgr
+bar 0x29 shift
+twosuperior 0x9 altgr
+threesuperior 0xa altgr
+onequarter 0xb altgr
+onehalf 0xc altgr
+threequarters 0xd altgr
+section 0x18 altgr
+paragraph 0x19 altgr
+bracketleft 0x1a altgr
+bracketright 0x1b altgr
+asciitilde 0x27 altgr
+braceleft 0x28 altgr
+braceright 0x2b altgr
+less 0x2b
+greater 0x2b shift
+guillemotleft 0x56
+guillemotright 0x56 shift
+degree 0x56 altgr
+mu 0x32 altgr
+eacute 0x35
+dead_acute 0x35 altgr
+dead_grave 0x28
+dead_circumflex 0x1a
+dead_circumflex 0x1a shift
+dead_cedilla 0x1b
+dead_diaeresis 0x1b shift
+exclam 0x2 shift
+quotedbl 0x3 shift
+slash 0x4 shift
+dollar 0x5 shift
+percent 0x6 shift
+question 0x7 shift
+ampersand 0x8 shift
+asterisk 0x9 shift
+parenleft 0xa shift
+parenright 0xb shift
+underscore 0xc shift
+plus 0xd shift
diff --git a/pc-bios/keymaps/fr-ch b/pc-bios/keymaps/fr-ch
new file mode 100644
index 0000000..4620d20
--- /dev/null
+++ b/pc-bios/keymaps/fr-ch
@@ -0,0 +1,114 @@
+# generated from XKB map fr_CH
+include common
+map 0x100c
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+section 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+ssharp 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+acute 0x0d
+dead_acute 0x0d
+grave 0x0d shift
+dead_grave 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+asciitilde 0x1b altgr
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+dead_doubleacute 0x27 altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+dead_caron 0x28 shift altgr
+asciicircum 0x29
+dead_circumflex 0x29
+degree 0x29 shift
+notsign 0x29 altgr
+numbersign 0x2b
+apostrophe 0x2b shift
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/hr b/pc-bios/keymaps/hr
new file mode 100644
index 0000000..613aa69
--- /dev/null
+++ b/pc-bios/keymaps/hr
@@ -0,0 +1,125 @@
+# generated from XKB map hr
+include common
+map 0x41a
+exclam 0x02 shift
+asciitilde 0x02 altgr
+dead_tilde 0x02 shift altgr
+quotedbl 0x03 shift
+dead_caron 0x03 altgr
+caron 0x03 shift altgr
+numbersign 0x04 shift
+asciicircum 0x04 altgr
+dead_circumflex 0x04 shift altgr
+dollar 0x05 shift
+dead_breve 0x05 altgr
+breve 0x05 shift altgr
+percent 0x06 shift
+degree 0x06 altgr
+dead_abovering 0x06 shift altgr
+ampersand 0x07 shift
+dead_ogonek 0x07 altgr
+ogonek 0x07 shift altgr
+slash 0x08 shift
+grave 0x08 altgr
+dead_grave 0x08 shift altgr
+parenleft 0x09 shift
+dead_abovedot 0x09 altgr
+abovedot 0x09 shift altgr
+parenright 0x0a shift
+dead_acute 0x0a altgr
+apostrophe 0x0a shift altgr
+equal 0x0b shift
+dead_doubleacute 0x0b altgr
+doubleacute 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+dead_diaeresis 0x0c altgr
+diaeresis 0x0c shift altgr
+plus 0x0d
+asterisk 0x0d shift
+dead_cedilla 0x0d altgr
+cedilla 0x0d shift altgr
+backslash 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+bar 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+scaron 0x1a
+Scaron 0x1a shift
+division 0x1a altgr
+dead_abovering 0x1a shift altgr
+dstroke 0x1b
+Dstroke 0x1b shift
+multiply 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+bracketleft 0x21 altgr
+ordfeminine 0x21 shift altgr
+bracketright 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+lstroke 0x25 altgr
+ampersand 0x25 shift altgr
+Lstroke 0x26 altgr
+ccaron 0x27
+Ccaron 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+cacute 0x28
+Cacute 0x28 shift
+ssharp 0x28 altgr
+dead_caron 0x28 shift altgr
+dead_cedilla 0x29
+dead_diaeresis 0x29 shift
+notsign 0x29 altgr
+zcaron 0x2b
+Zcaron 0x2b shift
+currency 0x2b altgr
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+at 0x2f altgr
+grave 0x2f shift altgr
+braceleft 0x30 altgr
+apostrophe 0x30 shift altgr
+braceright 0x31 altgr
+section 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/hu b/pc-bios/keymaps/hu
new file mode 100644
index 0000000..8aba444
--- /dev/null
+++ b/pc-bios/keymaps/hu
@@ -0,0 +1,115 @@
+# Hungarian keyboard layout (QWERTZ)
+# Created by: The NeverGone <never@delfin.klte.hu>
+
+include common
+map 0x40e
+
+
+# AltGr keys:
+notsign 0x29 altgr
+asciitilde 0x02 altgr
+caron 0x03 altgr
+asciicircum 0x04 altgr
+breve 0x05 altgr
+degree 0x06 altgr
+ogonek 0x07 altgr
+grave 0x08 altgr
+abovedot 0x09 altgr
+acute 0x0a altgr
+doubleacute 0x0b altgr
+diaeresis 0x0c altgr
+cedilla 0x0d altgr
+backslash 0x10 altgr
+bar 0x11 altgr
+EuroSign 0x12 altgr
+Iacute 0x17 altgr
+division 0x1a altgr
+multiply 0x1b altgr
+dstroke 0x1f altgr
+Dstroke 0x20 altgr
+bracketleft 0x21 altgr
+bracketright 0x22 altgr
+iacute 0x24 altgr
+lstroke 0x25 altgr
+Lstroke 0x26 altgr
+dollar 0x27 altgr
+ssharp 0x28 altgr
+currency 0x2b altgr
+less 0x56 altgr
+greater 0x2c altgr
+numbersign 0x2d altgr
+ampersand 0x2e altgr
+at 0x2f altgr
+braceleft 0x30 altgr
+braceright 0x31 altgr
+semicolon 0x33 altgr
+asterisk 0x35 altgr
+
+
+# Shift keys:
+section 0x29 shift
+apostrophe 0x02 shift
+quotedbl 0x03 shift
+plus 0x04 shift
+exclam 0x05 shift
+percent 0x06 shift
+slash 0x07 shift
+equal 0x08 shift
+parenleft 0x09 shift
+parenright 0x0a shift
+Odiaeresis 0x0b shift
+Udiaeresis 0x0c shift
+Oacute 0x0d shift
+Z 0x15 shift
+Odoubleacute 0x1a shift
+Uacute 0x1b shift
+Eacute 0x27 shift
+Aacute 0x28 shift
+Udoubleacute 0x2b shift
+Y 0x2c shift
+question 0x33 shift
+colon 0x34 shift
+underscore 0x35 shift
+F13 0x3b shift
+F14 0x3c shift
+F15 0x3d shift
+F16 0x3e shift
+F17 0x3f shift
+F18 0x40 shift
+F19 0x41 shift
+F20 0x42 shift
+F21 0x43 shift
+F22 0x44 shift
+F23 0x57 shift
+F24 0x58 shift
+
+
+# Ctrl keys:
+F25 0x3b ctrl
+F26 0x3c ctrl
+F27 0x3d ctrl
+F28 0x3e ctrl
+F29 0x3f ctrl
+F30 0x40 ctrl
+F31 0x41 ctrl
+F32 0x42 ctrl
+F33 0x43 ctrl
+F34 0x44 ctrl
+F35 0x57 ctrl
+#NoSymbol 0x58 ctrl
+
+
+0 0x29
+odiaeresis 0x0b
+udiaeresis 0x0c
+oacute 0x0d
+z 0x15
+odoubleacute 0x1a
+uacute 0x1b
+eacute 0x27
+aacute 0x28
+udoubleacute 0x2b
+y 0x2c
+comma 0x33
+period 0x34
+minus 0x35
diff --git a/pc-bios/keymaps/is b/pc-bios/keymaps/is
new file mode 100644
index 0000000..21dc1fd
--- /dev/null
+++ b/pc-bios/keymaps/is
@@ -0,0 +1,139 @@
+# 2004-03-16 Halldór Guðmundsson and Morten Lange
+# Keyboard definition file for the Icelandic keyboard
+# to be used in rdesktop 1.3.x ( See rdesktop.org)
+# generated from XKB map de, and changed manually
+# Location for example /usr/local/share/rdesktop/keymaps/is
+include common
+map 0x40f
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+#section 0x04 shift
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+#ssharp 0x0c
+odiaeresis 0x0c
+#question 0x0c shift
+Odiaeresis 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+#acute 0x0d
+minus 0x0d
+#dead_acute 0x0d
+#grave 0x0d shift
+#dead_grave 0x0d shift
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+#z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+#thorn 0x19 altgr
+#THORN 0x19 shift altgr
+#udiaeresis 0x1a
+#Udiaeresis 0x1a shift
+#dead_diaeresis 0x1a altgr
+#dead_abovering 0x1a shift altgr
+eth 0x1a
+ETH 0x1a shift
+apostrophe 0x1b
+question 0x1b shift
+#plus 0x1b
+#asterisk 0x1b shift
+asciitilde 0x1b altgr
+#grave 0x1b altgr
+#dead_tilde 0x1b altgr
+#dead_macron 0x1b shift altgr
+#ae 0x1e altgr
+#AE 0x1e shift altgr
+#eth 0x20 altgr
+#eth 0x20
+#ETH 0x20 shift altgr
+#ETH 0x20 shift
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+#adiaeresis 0x27
+#Adiaeresis 0x27 shift
+ae 0x27
+AE 0x27 shift
+dead_doubleacute 0x27 altgr
+#adiaeresis 0x28
+#Adiaeresis 0x28 shift
+#dead_caron 0x28 shift altgr
+#asciicircum 0x29
+acute 0x28
+dead_acute 0x28
+#dead_circumflex 0x29
+#degree 0x29 shift
+#notsign 0x29 altgr
+plus 0x2b
+asterisk 0x2b shift
+grave 0x2b altgr
+#numbersign 0x2b
+#apostrophe 0x2b shift
+#dead_breve 0x2b shift altgr
+#y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+#minus 0x35
+#underscore 0x35 shift
+thorn 0x35
+THORN 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/it b/pc-bios/keymaps/it
new file mode 100644
index 0000000..00ca73a
--- /dev/null
+++ b/pc-bios/keymaps/it
@@ -0,0 +1,115 @@
+# generated from XKB map it
+include common
+map 0x410
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+sterling 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+trademark 0x09 shift altgr
+parenright 0x0a shift
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+grave 0x0c altgr
+questiondown 0x0c shift altgr
+igrave 0x0d
+asciicircum 0x0d shift
+asciitilde 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+egrave 0x1a
+eacute 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ograve 0x27
+ccedilla 0x27 shift
+at 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+agrave 0x28
+degree 0x28 shift
+numbersign 0x28 altgr
+backslash 0x29
+bar 0x29 shift
+notsign 0x29 altgr
+ugrave 0x2b
+section 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/ja b/pc-bios/keymaps/ja
new file mode 100644
index 0000000..9d90a78
--- /dev/null
+++ b/pc-bios/keymaps/ja
@@ -0,0 +1,109 @@
+# generated from XKB map jp106
+include common
+map 0x411
+exclam 0x02 shift
+kana_NU 0x02 altgr
+quotedbl 0x03 shift
+kana_FU 0x03 altgr
+numbersign 0x04 shift
+kana_A 0x04 altgr
+kana_a 0x04 shift altgr
+dollar 0x05 shift
+kana_U 0x05 altgr
+kana_u 0x05 shift altgr
+percent 0x06 shift
+kana_E 0x06 altgr
+kana_e 0x06 shift altgr
+ampersand 0x07 shift
+kana_O 0x07 altgr
+kana_o 0x07 shift altgr
+apostrophe 0x08 shift
+kana_YA 0x08 altgr
+kana_ya 0x08 shift altgr
+parenleft 0x09 shift
+kana_YU 0x09 altgr
+kana_yu 0x09 shift altgr
+parenright 0x0a shift
+kana_YO 0x0a altgr
+kana_yo 0x0a shift altgr
+asciitilde 0x0b shift
+kana_WA 0x0b altgr
+kana_WO 0x0b shift altgr
+minus 0x0c
+equal 0x0c shift
+kana_HO 0x0c altgr
+asciicircum 0x0d
+asciitilde 0x0d shift
+kana_HE 0x0d altgr
+kana_TA 0x10 altgr
+kana_TE 0x11 altgr
+kana_I 0x12 altgr
+kana_i 0x12 shift altgr
+kana_SU 0x13 altgr
+kana_KA 0x14 altgr
+kana_N 0x15 altgr
+kana_NA 0x16 altgr
+kana_NI 0x17 altgr
+kana_RA 0x18 altgr
+kana_SE 0x19 altgr
+at 0x1a
+grave 0x1a shift
+voicedsound 0x1a altgr
+bracketleft 0x1b
+braceleft 0x1b shift
+semivoicedsound 0x1b altgr
+kana_openingbracket 0x1b shift altgr
+kana_CHI 0x1e altgr
+kana_TO 0x1f altgr
+kana_SHI 0x20 altgr
+kana_HA 0x21 altgr
+kana_KI 0x22 altgr
+kana_KU 0x23 altgr
+kana_MA 0x24 altgr
+kana_NO 0x25 altgr
+kana_RI 0x26 altgr
+semicolon 0x27
+plus 0x27 shift
+kana_RE 0x27 altgr
+colon 0x28
+asterisk 0x28 shift
+kana_KE 0x28 altgr
+Zenkaku_Hankaku 0x29
+bracketright 0x2b
+braceright 0x2b shift
+kana_MU 0x2b altgr
+kana_closingbracket 0x2b shift altgr
+kana_TSU 0x2c altgr
+kana_tsu 0x2c shift altgr
+kana_SA 0x2d altgr
+kana_SO 0x2e altgr
+kana_HI 0x2f altgr
+kana_KO 0x30 altgr
+kana_MI 0x31 altgr
+kana_MO 0x32 altgr
+comma 0x33
+less 0x33 shift
+kana_NE 0x33 altgr
+kana_comma 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+kana_RU 0x34 altgr
+kana_fullstop 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+kana_ME 0x35 altgr
+kana_conjunctive 0x35 shift altgr
+Eisu_toggle 0x3a shift
+Execute 0x54 shift
+Kanji 0x70
+backslash 0x73
+yen 0x7d
+bar 0x7d shift
+underscore 0x73 shift
+Henkan_Mode 0x79
+Katakana_Real 0x70
+Katakana 0x70
+Muhenkan 0x7b
+Henkan_Mode_Real 0x79
+Henkan_Mode_Ultra 0x79
+backslash_ja 0x73
diff --git a/pc-bios/keymaps/lt b/pc-bios/keymaps/lt
new file mode 100644
index 0000000..3d9d619
--- /dev/null
+++ b/pc-bios/keymaps/lt
@@ -0,0 +1,57 @@
+# generated from XKB map lt
+include common
+map 0x427
+exclam 0x02 shift
+aogonek 0x02 altgr
+Aogonek 0x02 shift altgr
+at 0x03 shift
+ccaron 0x03 altgr
+Ccaron 0x03 shift altgr
+numbersign 0x04 shift
+eogonek 0x04 altgr
+Eogonek 0x04 shift altgr
+dollar 0x05 shift
+eabovedot 0x05 altgr
+Eabovedot 0x05 shift altgr
+percent 0x06 shift
+iogonek 0x06 altgr
+Iogonek 0x06 shift altgr
+asciicircum 0x07 shift
+scaron 0x07 altgr
+Scaron 0x07 shift altgr
+ampersand 0x08 shift
+uogonek 0x08 altgr
+Uogonek 0x08 shift altgr
+asterisk 0x09 shift
+umacron 0x09 altgr
+Umacron 0x09 shift altgr
+parenleft 0x0a shift
+doublelowquotemark 0x0a altgr
+parenright 0x0b shift
+leftdoublequotemark 0x0b altgr
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+zcaron 0x0d altgr
+Zcaron 0x0d shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+bracketright 0x1b
+braceright 0x1b shift
+semicolon 0x27
+colon 0x27 shift
+apostrophe 0x28
+quotedbl 0x28 shift
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+slash 0x35
+question 0x35 shift
+endash 0x56
+EuroSign 0x56 shift
diff --git a/pc-bios/keymaps/lv b/pc-bios/keymaps/lv
new file mode 100644
index 0000000..1d91727
--- /dev/null
+++ b/pc-bios/keymaps/lv
@@ -0,0 +1,128 @@
+# generated from XKB map lv
+include common
+map 0x426
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+EuroSign 0x05 altgr
+cent 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+emacron 0x12 altgr
+Emacron 0x12 shift altgr
+rcedilla 0x13 altgr
+Rcedilla 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+umacron 0x16 altgr
+Umacron 0x16 shift altgr
+imacron 0x17 altgr
+Imacron 0x17 shift altgr
+omacron 0x18 altgr
+Omacron 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ISO_Next_Group 0x1c shift
+amacron 0x1e altgr
+Amacron 0x1e shift altgr
+scaron 0x1f altgr
+Scaron 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+gcedilla 0x22 altgr
+Gcedilla 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kcedilla 0x25 altgr
+Kcedilla 0x25 shift altgr
+lcedilla 0x26 altgr
+Lcedilla 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+leftdoublequotemark 0x28 altgr
+doublelowquotemark 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+notsign 0x29 altgr
+backslash 0x2b
+bar 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+zcaron 0x2c altgr
+Zcaron 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+ccaron 0x2e altgr
+Ccaron 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+ncedilla 0x31 altgr
+Ncedilla 0x31 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+nobreakspace 0x39 altgr
diff --git a/pc-bios/keymaps/mk b/pc-bios/keymaps/mk
new file mode 100644
index 0000000..18c1504
--- /dev/null
+++ b/pc-bios/keymaps/mk
@@ -0,0 +1,101 @@
+# generated from XKB map mk
+include common
+map 0x42f
+exclam 0x02 shift
+at 0x03 shift
+doublelowquotemark 0x03 shift altgr
+numbersign 0x04 shift
+leftdoublequotemark 0x04 shift altgr
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Cyrillic_lje 0x10 altgr
+Cyrillic_LJE 0x10 shift altgr
+Cyrillic_nje 0x11 altgr
+Cyrillic_NJE 0x11 shift altgr
+Cyrillic_ie 0x12 altgr
+Cyrillic_IE 0x12 shift altgr
+Cyrillic_er 0x13 altgr
+Cyrillic_ER 0x13 shift altgr
+Cyrillic_te 0x14 altgr
+Cyrillic_TE 0x14 shift altgr
+Macedonia_dse 0x15 altgr
+Macedonia_DSE 0x15 shift altgr
+Cyrillic_u 0x16 altgr
+Cyrillic_U 0x16 shift altgr
+Cyrillic_i 0x17 altgr
+Cyrillic_I 0x17 shift altgr
+Cyrillic_o 0x18 altgr
+Cyrillic_O 0x18 shift altgr
+Cyrillic_pe 0x19 altgr
+Cyrillic_PE 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Cyrillic_sha 0x1a altgr
+Cyrillic_SHA 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Macedonia_gje 0x1b altgr
+Macedonia_GJE 0x1b shift altgr
+Cyrillic_a 0x1e altgr
+Cyrillic_A 0x1e shift altgr
+Cyrillic_es 0x1f altgr
+Cyrillic_ES 0x1f shift altgr
+Cyrillic_de 0x20 altgr
+Cyrillic_DE 0x20 shift altgr
+Cyrillic_ef 0x21 altgr
+Cyrillic_EF 0x21 shift altgr
+Cyrillic_ghe 0x22 altgr
+Cyrillic_GHE 0x22 shift altgr
+Cyrillic_ha 0x23 altgr
+Cyrillic_HA 0x23 shift altgr
+Cyrillic_je 0x24 altgr
+Cyrillic_JE 0x24 shift altgr
+Cyrillic_ka 0x25 altgr
+Cyrillic_KA 0x25 shift altgr
+Cyrillic_el 0x26 altgr
+Cyrillic_EL 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Cyrillic_che 0x27 altgr
+Cyrillic_CHE 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Macedonia_kje 0x28 altgr
+Macedonia_KJE 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+Cyrillic_zhe 0x2b altgr
+Cyrillic_ZHE 0x2b shift altgr
+Cyrillic_ze 0x2c altgr
+Cyrillic_ZE 0x2c shift altgr
+Cyrillic_dzhe 0x2d altgr
+Cyrillic_DZHE 0x2d shift altgr
+Cyrillic_tse 0x2e altgr
+Cyrillic_TSE 0x2e shift altgr
+Cyrillic_ve 0x2f altgr
+Cyrillic_VE 0x2f shift altgr
+Cyrillic_be 0x30 altgr
+Cyrillic_BE 0x30 shift altgr
+Cyrillic_en 0x31 altgr
+Cyrillic_EN 0x31 shift altgr
+Cyrillic_em 0x32 altgr
+Cyrillic_EM 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+semicolon 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+colon 0x34 shift altgr
+slash 0x35
+question 0x35 shift
diff --git a/pc-bios/keymaps/modifiers b/pc-bios/keymaps/modifiers
new file mode 100644
index 0000000..d73b7a6
--- /dev/null
+++ b/pc-bios/keymaps/modifiers
@@ -0,0 +1,18 @@
+Shift_R 0x36
+Shift_L 0x2a
+
+Alt_R 0xb8
+Mode_switch 0xb8
+ISO_Level3_Shift 0xb8
+Alt_L 0x38
+
+Control_R 0x9d
+Control_L 0x1d
+
+# Translate Super to Windows keys.
+# This is hardcoded. See documentation for details.
+Super_R 0xdc
+Super_L 0xdb
+
+# Translate Menu to the Windows Application key.
+Menu 0xdd
diff --git a/pc-bios/keymaps/nl b/pc-bios/keymaps/nl
new file mode 100644
index 0000000..b4892f9
--- /dev/null
+++ b/pc-bios/keymaps/nl
@@ -0,0 +1,59 @@
+# Dutch (Netherlands)
+include common
+map 0x413
+
+exclam 0x02 shift
+onesuperior 0x02 altgr
+quotebl 0x03 shift
+twosuperior 0x03 altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+underscore 0x08 shift
+sterling 0x08 altgr
+parenleft 0x09 shift
+braceleft 0x09 altgr
+parenright 0x0a shift
+braceright 0x0a altgr
+apostrophe 0x0b shift
+slash 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+degree 0x0d
+dead_tilde 0x0d shift
+dead_cedilla 0x0d altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+dead_diaeresis 0x1a
+dead_circumflex 0x1a shift
+asterisk 0x1b
+bar 0x1b shift
+ssharp 0x1f altgr
+plus 0x27
+plusminus 0x27 shift
+dead_acute 0x28
+dead_grave 0x28 shift
+at 0x29
+section 0x29 shift
+notsign 0x29 altgr
+less 0x2b
+greater 0x2b shift
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+hyphen 0x35
+equal 0x35 shift
+bracketright 0x56
+bracketleft 0x56 shift
+brokenbar 0x56 altgr
diff --git a/pc-bios/keymaps/nl-be b/pc-bios/keymaps/nl-be
new file mode 100644
index 0000000..34fc881
--- /dev/null
+++ b/pc-bios/keymaps/nl-be
@@ -0,0 +1,3 @@
+# Dutch (Belgium)
+map 0x813
+include common
diff --git a/pc-bios/keymaps/no b/pc-bios/keymaps/no
new file mode 100644
index 0000000..40a6479
--- /dev/null
+++ b/pc-bios/keymaps/no
@@ -0,0 +1,119 @@
+# generated from XKB map no
+include common
+map 0x414
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+plusminus 0x0c altgr
+questiondown 0x0c shift altgr
+backslash 0x0d
+dead_grave 0x0d shift
+dead_acute 0x0d altgr
+notsign 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+asciicircum 0x01b shift
+dead_tilde 0x1b altgr
+asciitilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+oslash 0x27
+Ooblique 0x27 shift
+dead_doubleacute 0x27 shift altgr
+ae 0x28
+AE 0x28 shift
+dead_caron 0x28 shift altgr
+bar 0x29
+section 0x29 shift
+brokenbar 0x29 altgr
+paragraph 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
+onehalf 0x56 altgr
+threequarters 0x56 shift altgr
diff --git a/pc-bios/keymaps/pl b/pc-bios/keymaps/pl
new file mode 100644
index 0000000..09c600d
--- /dev/null
+++ b/pc-bios/keymaps/pl
@@ -0,0 +1,122 @@
+# generated from XKB map pl
+include common
+map 0x415
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+eogonek 0x12 altgr
+Eogonek 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+EuroSign 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oacute 0x18 altgr
+Oacute 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+aogonek 0x1e altgr
+Aogonek 0x1e shift altgr
+sacute 0x1f altgr
+Sacute 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+notsign 0x29 altgr
+backslash 0x2b
+bar 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+zabovedot 0x2c altgr
+Zabovedot 0x2c shift altgr
+zacute 0x2d altgr
+Zacute 0x2d shift altgr
+cacute 0x2e altgr
+Cacute 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+nacute 0x31 altgr
+Nacute 0x31 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/pt b/pc-bios/keymaps/pt
new file mode 100644
index 0000000..c6941f6
--- /dev/null
+++ b/pc-bios/keymaps/pt
@@ -0,0 +1,113 @@
+# generated from XKB map pt
+include common
+map 0x816
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+dollar 0x05 shift
+section 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+guillemotleft 0x0d
+guillemotright 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+plus 0x1a
+asterisk 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_acute 0x1b
+dead_grave 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ccedilla 0x27
+Ccedilla 0x27 shift
+dead_doubleacute 0x27 shift altgr
+masculine 0x28
+ordfeminine 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+backslash 0x29
+bar 0x29 shift
+notsign 0x29 altgr
+dead_tilde 0x2b
+dead_circumflex 0x2b shift
+dead_breve 0x2b shift altgr
+less 0x56
+greater 0x56 shift
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/keymaps/pt-br b/pc-bios/keymaps/pt-br
new file mode 100644
index 0000000..54bafc5
--- /dev/null
+++ b/pc-bios/keymaps/pt-br
@@ -0,0 +1,69 @@
+# generated from XKB map br
+include common
+map 0x416
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+onehalf 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+threequarters 0x04 shift altgr
+dollar 0x05 shift
+sterling 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+cent 0x06 altgr
+dead_diaeresis 0x07 shift
+notsign 0x07 altgr
+diaeresis 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+equal 0x0d
+plus 0x0d shift
+section 0x0d altgr
+EuroSign 0x12 altgr
+registered 0x13 altgr
+dead_acute 0x1a
+dead_grave 0x1a shift
+acute 0x1a altgr
+grave 0x1a shift altgr
+bracketleft 0x1b
+braceleft 0x1b shift
+ordfeminine 0x1b altgr
+ccedilla 0x27
+Ccedilla 0x27 shift
+dead_tilde 0x28
+dead_circumflex 0x28 shift
+asciitilde 0x28 altgr
+asciicircum 0x28 shift altgr
+apostrophe 0x29
+quotedbl 0x29 shift
+bracketright 0x2b
+braceright 0x2b shift
+masculine 0x2b altgr
+copyright 0x2e altgr
+mu 0x32 altgr
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+semicolon 0x35
+colon 0x35 shift
+comma 0x53 numlock
+backslash 0x56
+bar 0x56 shift
+slash 0x73
+question 0x73 shift
+degree 0x73 altgr
+KP_Decimal 0x34
diff --git a/pc-bios/keymaps/ru b/pc-bios/keymaps/ru
new file mode 100644
index 0000000..b3e7d24
--- /dev/null
+++ b/pc-bios/keymaps/ru
@@ -0,0 +1,109 @@
+# generated from XKB map ru
+include common
+map 0x419
+exclam 0x02 shift
+at 0x03 shift
+quotedbl 0x03 shift altgr
+numbersign 0x04 shift
+dollar 0x05 shift
+asterisk 0x05 shift altgr
+percent 0x06 shift
+colon 0x06 shift altgr
+asciicircum 0x07 shift
+comma 0x07 shift altgr
+ampersand 0x08 shift
+period 0x08 shift altgr
+asterisk 0x09 shift
+semicolon 0x09 shift altgr
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Cyrillic_shorti 0x10 altgr
+Cyrillic_SHORTI 0x10 shift altgr
+Cyrillic_tse 0x11 altgr
+Cyrillic_TSE 0x11 shift altgr
+Cyrillic_u 0x12 altgr
+Cyrillic_U 0x12 shift altgr
+Cyrillic_ka 0x13 altgr
+Cyrillic_KA 0x13 shift altgr
+Cyrillic_ie 0x14 altgr
+Cyrillic_IE 0x14 shift altgr
+Cyrillic_en 0x15 altgr
+Cyrillic_EN 0x15 shift altgr
+Cyrillic_ghe 0x16 altgr
+Cyrillic_GHE 0x16 shift altgr
+Cyrillic_sha 0x17 altgr
+Cyrillic_SHA 0x17 shift altgr
+Cyrillic_shcha 0x18 altgr
+Cyrillic_SHCHA 0x18 shift altgr
+Cyrillic_ze 0x19 altgr
+Cyrillic_ZE 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Cyrillic_ha 0x1a altgr
+Cyrillic_HA 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Cyrillic_hardsign 0x1b altgr
+Cyrillic_HARDSIGN 0x1b shift altgr
+Cyrillic_ef 0x1e altgr
+Cyrillic_EF 0x1e shift altgr
+Cyrillic_yeru 0x1f altgr
+Cyrillic_YERU 0x1f shift altgr
+Cyrillic_ve 0x20 altgr
+Cyrillic_VE 0x20 shift altgr
+Cyrillic_a 0x21 altgr
+Cyrillic_A 0x21 shift altgr
+Cyrillic_pe 0x22 altgr
+Cyrillic_PE 0x22 shift altgr
+Cyrillic_er 0x23 altgr
+Cyrillic_ER 0x23 shift altgr
+Cyrillic_o 0x24 altgr
+Cyrillic_O 0x24 shift altgr
+Cyrillic_el 0x25 altgr
+Cyrillic_EL 0x25 shift altgr
+Cyrillic_de 0x26 altgr
+Cyrillic_DE 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Cyrillic_zhe 0x27 altgr
+Cyrillic_ZHE 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Cyrillic_e 0x28 altgr
+Cyrillic_E 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+Cyrillic_io 0x29 altgr
+Cyrillic_IO 0x29 shift altgr
+backslash 0x2b
+bar 0x2b shift
+Cyrillic_ya 0x2c altgr
+Cyrillic_YA 0x2c shift altgr
+Cyrillic_che 0x2d altgr
+Cyrillic_CHE 0x2d shift altgr
+Cyrillic_es 0x2e altgr
+Cyrillic_ES 0x2e shift altgr
+Cyrillic_em 0x2f altgr
+Cyrillic_EM 0x2f shift altgr
+Cyrillic_i 0x30 altgr
+Cyrillic_I 0x30 shift altgr
+Cyrillic_te 0x31 altgr
+Cyrillic_TE 0x31 shift altgr
+Cyrillic_softsign 0x32 altgr
+Cyrillic_SOFTSIGN 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Cyrillic_be 0x33 altgr
+Cyrillic_BE 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+Cyrillic_yu 0x34 altgr
+Cyrillic_YU 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+slash 0x56 altgr
+bar 0x56 shift altgr
diff --git a/pc-bios/keymaps/sl b/pc-bios/keymaps/sl
new file mode 100644
index 0000000..56835a9
--- /dev/null
+++ b/pc-bios/keymaps/sl
@@ -0,0 +1,110 @@
+# generated from XKB map sl
+include common
+map 0x424
+exclam 0x02 shift
+asciitilde 0x02 altgr
+dead_tilde 0x02 shift altgr
+quotedbl 0x03 shift
+dead_caron 0x03 altgr
+caron 0x03 shift altgr
+numbersign 0x04 shift
+asciicircum 0x04 altgr
+dead_circumflex 0x04 shift altgr
+dollar 0x05 shift
+dead_breve 0x05 altgr
+breve 0x05 shift altgr
+percent 0x06 shift
+degree 0x06 altgr
+dead_abovering 0x06 shift altgr
+ampersand 0x07 shift
+dead_ogonek 0x07 altgr
+ogonek 0x07 shift altgr
+slash 0x08 shift
+grave 0x08 altgr
+dead_grave 0x08 shift altgr
+parenleft 0x09 shift
+dead_abovedot 0x09 altgr
+abovedot 0x09 shift altgr
+parenright 0x0a shift
+dead_acute 0x0a altgr
+equal 0x0b shift
+dead_doubleacute 0x0b altgr
+doubleacute 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+dead_diaeresis 0x0c altgr
+diaeresis 0x0c shift altgr
+plus 0x0d
+asterisk 0x0d shift
+dead_cedilla 0x0d altgr
+cedilla 0x0d shift altgr
+backslash 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+bar 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+scaron 0x1a
+Scaron 0x1a shift
+division 0x1a altgr
+dstroke 0x1b
+Dstroke 0x1b shift
+multiply 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+bracketleft 0x21 altgr
+ordfeminine 0x21 shift altgr
+bracketright 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+lstroke 0x25 altgr
+Lstroke 0x26 altgr
+ccaron 0x27
+Ccaron 0x27 shift
+cacute 0x28
+Cacute 0x28 shift
+ssharp 0x28 altgr
+dead_cedilla 0x29
+notsign 0x29 altgr
+zcaron 0x2b
+Zcaron 0x2b shift
+currency 0x2b altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+at 0x2f altgr
+braceleft 0x30 altgr
+braceright 0x31 altgr
+section 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
diff --git a/pc-bios/keymaps/sv b/pc-bios/keymaps/sv
new file mode 100644
index 0000000..5d9080e
--- /dev/null
+++ b/pc-bios/keymaps/sv
@@ -0,0 +1,81 @@
+map 0x0000041d
+include common
+
+#
+# Top row
+#
+section 0x29
+onehalf 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+backslash 0xc altgr
+
+acute 0xd
+dead_acute 0xd
+grave 0xd shift
+dead_grave 0xd shift
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+
+#
+# QWERTY second row
+#
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+apostrophe 0x2b
+asterisk 0x2b shift
+
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
diff --git a/pc-bios/keymaps/th b/pc-bios/keymaps/th
new file mode 100644
index 0000000..b65b6da
--- /dev/null
+++ b/pc-bios/keymaps/th
@@ -0,0 +1,131 @@
+# generated from XKB map th
+include common
+map 0x41e
+exclam 0x02 shift
+Thai_lakkhangyao 0x02 altgr
+plus 0x02 shift altgr
+at 0x03 shift
+slash 0x03 altgr
+Thai_leknung 0x03 shift altgr
+numbersign 0x04 shift
+minus 0x04 altgr
+Thai_leksong 0x04 shift altgr
+dollar 0x05 shift
+Thai_phosamphao 0x05 altgr
+Thai_leksam 0x05 shift altgr
+percent 0x06 shift
+Thai_thothung 0x06 altgr
+Thai_leksi 0x06 shift altgr
+asciicircum 0x07 shift
+Thai_sarau 0x07 altgr
+Thai_sarauu 0x07 shift altgr
+ampersand 0x08 shift
+Thai_saraue 0x08 altgr
+Thai_baht 0x08 shift altgr
+asterisk 0x09 shift
+Thai_khokhwai 0x09 altgr
+Thai_lekha 0x09 shift altgr
+parenleft 0x0a shift
+Thai_totao 0x0a altgr
+Thai_lekhok 0x0a shift altgr
+parenright 0x0b shift
+Thai_chochan 0x0b altgr
+Thai_lekchet 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+Thai_khokhai 0x0c altgr
+Thai_lekpaet 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+Thai_chochang 0x0d altgr
+Thai_lekkao 0x0d shift altgr
+Thai_maiyamok 0x10 altgr
+Thai_leksun 0x10 shift altgr
+Thai_saraaimaimalai 0x11 altgr
+quotedbl 0x11 shift altgr
+Thai_saraam 0x12 altgr
+Thai_dochada 0x12 shift altgr
+Thai_phophan 0x13 altgr
+Thai_thonangmontho 0x13 shift altgr
+Thai_saraa 0x14 altgr
+Thai_thothong 0x14 shift altgr
+Thai_maihanakat 0x15 altgr
+Thai_nikhahit 0x15 shift altgr
+Thai_saraii 0x16 altgr
+Thai_maitri 0x16 shift altgr
+Thai_rorua 0x17 altgr
+Thai_nonen 0x17 shift altgr
+Thai_nonu 0x18 altgr
+Thai_paiyannoi 0x18 shift altgr
+Thai_yoyak 0x19 altgr
+Thai_yoying 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Thai_bobaimai 0x1a altgr
+Thai_thothan 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Thai_loling 0x1b altgr
+comma 0x1b shift altgr
+Thai_fofan 0x1e altgr
+Thai_ru 0x1e shift altgr
+Thai_hohip 0x1f altgr
+Thai_khorakhang 0x1f shift altgr
+Thai_kokai 0x20 altgr
+Thai_topatak 0x20 shift altgr
+Thai_dodek 0x21 altgr
+Thai_sarao 0x21 shift altgr
+Thai_sarae 0x22 altgr
+Thai_chochoe 0x22 shift altgr
+Thai_maitho 0x23 altgr
+Thai_maitaikhu 0x23 shift altgr
+Thai_maiek 0x24 altgr
+Thai_maichattawa 0x24 shift altgr
+Thai_saraaa 0x25 altgr
+Thai_sorusi 0x25 shift altgr
+Thai_sosua 0x26 altgr
+Thai_sosala 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Thai_wowaen 0x27 altgr
+Thai_soso 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Thai_ngongu 0x28 altgr
+period 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+underscore 0x29 altgr
+percent 0x29 shift altgr
+ISO_First_Group 0x2a shift
+backslash 0x2b
+bar 0x2b shift
+Thai_khokhuat 0x2b altgr
+Thai_khokhon 0x2b shift altgr
+Thai_phophung 0x2c altgr
+parenleft 0x2c shift altgr
+Thai_popla 0x2d altgr
+parenright 0x2d shift altgr
+Thai_saraae 0x2e altgr
+Thai_choching 0x2e shift altgr
+Thai_oang 0x2f altgr
+Thai_honokhuk 0x2f shift altgr
+Thai_sarai 0x30 altgr
+Thai_phinthu 0x30 shift altgr
+Thai_sarauee 0x31 altgr
+Thai_thanthakhat 0x31 shift altgr
+Thai_thothahan 0x32 altgr
+question 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Thai_moma 0x33 altgr
+Thai_thophuthao 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+Thai_saraaimaimuan 0x34 altgr
+Thai_lochula 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+Thai_fofa 0x35 altgr
+Thai_lu 0x35 shift altgr
+ISO_Last_Group 0x36 shift
diff --git a/pc-bios/keymaps/tr b/pc-bios/keymaps/tr
new file mode 100644
index 0000000..5650e1e
--- /dev/null
+++ b/pc-bios/keymaps/tr
@@ -0,0 +1,123 @@
+# generated from XKB map tr
+include common
+map 0x41f
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+apostrophe 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+dead_circumflex 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+plus 0x05 shift
+dollar 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+asciicircum 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+asterisk 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+minus 0x0d
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+idotless 0x17
+I 0x17 shift
+rightarrow 0x17 altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+gbreve 0x1a
+Gbreve 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+udiaeresis 0x1b
+Udiaeresis 0x1b shift
+asciitilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+scedilla 0x27
+Scedilla 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+i 0x28
+Iabovedot 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+backslash 0x29
+quotedbl 0x29 shift
+asciitilde 0x29 altgr
+comma 0x2b
+semicolon 0x2b shift
+bar 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+odiaeresis 0x33
+Odiaeresis 0x33 shift
+less 0x33 altgr
+multiply 0x33 shift altgr
+ccedilla 0x34
+Ccedilla 0x34 shift
+greater 0x34 altgr
+division 0x34 shift altgr
+period 0x35
+colon 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin
new file mode 100644
index 0000000..d8f0ea8
--- /dev/null
+++ b/pc-bios/linuxboot.bin
Binary files differ
diff --git a/pc-bios/mpc8544ds.dtb b/pc-bios/mpc8544ds.dtb
new file mode 100644
index 0000000..3299546
--- /dev/null
+++ b/pc-bios/mpc8544ds.dtb
Binary files differ
diff --git a/pc-bios/mpc8544ds.dts b/pc-bios/mpc8544ds.dts
new file mode 100644
index 0000000..872152d
--- /dev/null
+++ b/pc-bios/mpc8544ds.dts
@@ -0,0 +1,122 @@
+/*
+ * MPC8544 DS Device Tree Source
+ *
+ * Copyright 2007, 2008 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/dts-v1/;
+/ {
+ model = "MPC8544DS";
+ compatible = "MPC8544DS", "MPC85xxDS";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ aliases {
+ serial0 = &serial0;
+ serial1 = &serial1;
+ pci0 = &pci0;
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ PowerPC,8544@0 {
+ device_type = "cpu";
+ reg = <0x0>;
+ d-cache-line-size = <32>; // 32 bytes
+ i-cache-line-size = <32>; // 32 bytes
+ d-cache-size = <0x8000>; // L1, 32K
+ i-cache-size = <0x8000>; // L1, 32K
+ timebase-frequency = <0>;
+ bus-frequency = <0>;
+ clock-frequency = <0>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x0 0x0>; // Filled by U-Boot
+ };
+
+ soc8544@e0000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ device_type = "soc";
+ compatible = "simple-bus";
+
+ ranges = <0x0 0xe0000000 0x100000>;
+ reg = <0xe0000000 0x1000>; // CCSRBAR 1M
+ bus-frequency = <0>; // Filled out by uboot.
+
+ serial0: serial@4500 {
+ cell-index = <0>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4500 0x100>;
+ clock-frequency = <0>;
+ interrupts = <42 2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ serial1: serial@4600 {
+ cell-index = <1>;
+ device_type = "serial";
+ compatible = "ns16550";
+ reg = <0x4600 0x100>;
+ clock-frequency = <0>;
+ interrupts = <42 2>;
+ interrupt-parent = <&mpic>;
+ };
+
+ mpic: pic@40000 {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x40000 0x40000>;
+ compatible = "chrp,open-pic";
+ device_type = "open-pic";
+ };
+ };
+
+ pci0: pci@e0008000 {
+ cell-index = <0>;
+ compatible = "fsl,mpc8540-pci";
+ device_type = "pci";
+ interrupt-map-mask = <0xf800 0x0 0x0 0x7>;
+ interrupt-map = <
+
+ /* IDSEL 0x11 J17 Slot 1 */
+ 0x8800 0x0 0x0 0x1 &mpic 0x2 0x1
+ 0x8800 0x0 0x0 0x2 &mpic 0x3 0x1
+ 0x8800 0x0 0x0 0x3 &mpic 0x4 0x1
+ 0x8800 0x0 0x0 0x4 &mpic 0x1 0x1
+
+ /* IDSEL 0x12 J16 Slot 2 */
+
+ 0x9000 0x0 0x0 0x1 &mpic 0x3 0x1
+ 0x9000 0x0 0x0 0x2 &mpic 0x4 0x1
+ 0x9000 0x0 0x0 0x3 &mpic 0x2 0x1
+ 0x9000 0x0 0x0 0x4 &mpic 0x1 0x1>;
+
+ interrupt-parent = <&mpic>;
+ interrupts = <24 2>;
+ bus-range = <0 255>;
+ ranges = <0x2000000 0x0 0xc0000000 0xc0000000 0x0 0x20000000
+ 0x1000000 0x0 0x0 0xe1000000 0x0 0x10000>;
+ clock-frequency = <66666666>;
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0xe0008000 0x1000>;
+ };
+
+ chosen {
+ linux,stdout-path = "/soc8544@e0000000/serial@4500";
+ };
+};
diff --git a/pc-bios/multiboot.bin b/pc-bios/multiboot.bin
new file mode 100644
index 0000000..d7da6e0
--- /dev/null
+++ b/pc-bios/multiboot.bin
Binary files differ
diff --git a/pc-bios/ohw.diff b/pc-bios/ohw.diff
new file mode 100644
index 0000000..4fb5422
--- /dev/null
+++ b/pc-bios/ohw.diff
@@ -0,0 +1,1843 @@
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bios.h OpenHackWare-release-0.4/src/bios.h
+--- OpenHackWare-release-0.4.org/src/bios.h 2005-04-06 23:20:22.000000000 +0200
++++ OpenHackWare-release-0.4/src/bios.h 2005-07-07 01:10:20.000000000 +0200
+@@ -64,6 +64,7 @@
+ ARCH_CHRP,
+ ARCH_MAC99,
+ ARCH_POP,
++ ARCH_HEATHROW,
+ };
+
+ /* Hardware definition(s) */
+@@ -174,6 +175,7 @@
+ int bd_ioctl (bloc_device_t *bd, int func, void *args);
+ uint32_t bd_seclen (bloc_device_t *bd);
+ void bd_close (bloc_device_t *bd);
++void bd_reset_all(void);
+ uint32_t bd_seclen (bloc_device_t *bd);
+ uint32_t bd_maxbloc (bloc_device_t *bd);
+ void bd_sect2CHS (bloc_device_t *bd, uint32_t secnum,
+@@ -183,12 +185,12 @@
+ part_t *bd_probe (int boot_device);
+ bloc_device_t *bd_get (int device);
+ void bd_put (bloc_device_t *bd);
+-void bd_set_boot_part (bloc_device_t *bd, part_t *partition);
++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum);
+ part_t **_bd_parts (bloc_device_t *bd);
+
+ void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1,
+ uint32_t io_base2, uint32_t io_base3,
+- void *OF_private);
++ void *OF_private0, void *OF_private1);
+ void ide_pci_pmac_register (uint32_t io_base0, uint32_t io_base1,
+ void *OF_private);
+
+@@ -399,17 +401,23 @@
+ uint16_t min_grant, uint16_t max_latency);
+ void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses);
+ void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn,
+- uint32_t *regions, uint32_t *sizes);
++ uint32_t *regions, uint32_t *sizes,
++ int irq_line);
+ void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size,
+ void *private_data);
++void OF_finalize_pci_ide (void *dev,
++ uint32_t io_base0, uint32_t io_base1,
++ uint32_t io_base2, uint32_t io_base3);
+ int OF_register_bus (const unsigned char *name, uint32_t address,
+ const unsigned char *type);
+ int OF_register_serial (const unsigned char *bus, const unsigned char *name,
+ uint32_t io_base, int irq);
+ int OF_register_stdio (const unsigned char *dev_in,
+ const unsigned char *dev_out);
+-void OF_vga_register (const unsigned char *name, uint32_t address,
+- int width, int height, int depth);
++void OF_vga_register (const unsigned char *name, unused uint32_t address,
++ int width, int height, int depth,
++ unsigned long vga_bios_addr,
++ unsigned long vga_bios_size);
+ void *OF_blockdev_register (void *parent, void *private,
+ const unsigned char *type,
+ const unsigned char *name, int devnum,
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/bloc.c OpenHackWare-release-0.4/src/bloc.c
+--- OpenHackWare-release-0.4.org/src/bloc.c 2005-04-06 23:21:00.000000000 +0200
++++ OpenHackWare-release-0.4/src/bloc.c 2005-07-08 00:28:26.000000000 +0200
+@@ -55,6 +55,7 @@
+ /* Partitions */
+ part_t *parts, *bparts;
+ part_t *boot_part;
++ int bpartnum;
+ /* Chain */
+ bloc_device_t *next;
+ };
+@@ -66,6 +67,7 @@
+
+ static int ide_initialize (bloc_device_t *bd, int device);
+ static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum);
++static int ide_reset (bloc_device_t *bd);
+
+ static int mem_initialize (bloc_device_t *bd, int device);
+ static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum);
+@@ -212,6 +214,17 @@
+ {
+ }
+
++void bd_reset_all(void)
++{
++ bloc_device_t *bd;
++ for (bd = bd_list; bd != NULL; bd = bd->next) {
++ if (bd->init == &ide_initialize) {
++ /* reset IDE drive because Darwin wants all IDE devices to be reset */
++ ide_reset(bd);
++ }
++ }
++}
++
+ uint32_t bd_seclen (bloc_device_t *bd)
+ {
+ return bd->seclen;
+@@ -223,10 +236,12 @@
+ }
+
+ /* XXX: to be suppressed */
+-void bd_set_boot_part (bloc_device_t *bd, part_t *partition)
++void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum)
+ {
++ dprintf("%s: part %p (%p) %d\n", __func__, partition, bd->boot_part, partnum);
+ if (bd->boot_part == NULL) {
+ bd->boot_part = partition;
++ bd->bpartnum = partnum;
+ }
+ }
+
+@@ -240,6 +255,13 @@
+ return &bd->bparts;
+ }
+
++void bd_set_boot_device (bloc_device_t *bd)
++{
++#if defined (USE_OPENFIRMWARE)
++ OF_blockdev_set_boot_device(bd->OF_private, bd->bpartnum, "\\\\ofwboot");
++#endif
++}
++
+ part_t *bd_probe (int boot_device)
+ {
+ char devices[] = { /*'a', 'b',*/ 'c', 'd', 'e', 'f', 'm', '\0', };
+@@ -272,9 +294,7 @@
+ tmp = part_probe(bd, force_raw);
+ if (boot_device == bd->device) {
+ boot_part = tmp;
+-#if defined (USE_OPENFIRMWARE)
+- OF_blockdev_set_boot_device(bd->OF_private, 2, "\\\\ofwboot");
+-#endif
++ bd_set_boot_device(bd);
+ }
+ }
+
+@@ -717,34 +737,29 @@
+ /* IDE PCI access for pc */
+ static uint8_t ide_pci_port_read (bloc_device_t *bd, int port)
+ {
+- eieio();
+-
+- return *(uint8_t *)(bd->io_base + port);
++ uint8_t value;
++ value = inb(bd->io_base + port);
++ return value;
+ }
+
+ static void ide_pci_port_write (bloc_device_t *bd, int port, uint8_t value)
+ {
+- *(uint8_t *)(bd->io_base + port) = value;
+- eieio();
++ outb(bd->io_base + port, value);
+ }
+
+ static uint32_t ide_pci_data_readl (bloc_device_t *bd)
+ {
+- eieio();
+-
+- return *((uint32_t *)bd->io_base);
++ return inl(bd->io_base);
+ }
+
+ static void ide_pci_data_writel (bloc_device_t *bd, uint32_t val)
+ {
+- *(uint32_t *)(bd->io_base) = val;
+- eieio();
++ outl(bd->io_base, val);
+ }
+
+ static void ide_pci_control_write (bloc_device_t *bd, uint32_t val)
+ {
+- *((uint8_t *)bd->tmp) = val;
+- eieio();
++ outb(bd->tmp + 2, val);
+ }
+
+ static ide_ops_t ide_pci_pc_ops = {
+@@ -761,7 +776,7 @@
+
+ void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1,
+ uint32_t io_base2, uint32_t io_base3,
+- unused void *OF_private)
++ void *OF_private0, void *OF_private1)
+ {
+ if (ide_pci_ops == NULL) {
+ ide_pci_ops = malloc(sizeof(ide_ops_t));
+@@ -770,19 +785,19 @@
+ memcpy(ide_pci_ops, &ide_pci_pc_ops, sizeof(ide_ops_t));
+ }
+ if ((io_base0 != 0 || io_base1 != 0) &&
+- ide_pci_ops->base[0] == 0 && ide_pci_ops->base[1] == 0) {
++ ide_pci_ops->base[0] == 0 && ide_pci_ops->base[2] == 0) {
+ ide_pci_ops->base[0] = io_base0;
+- ide_pci_ops->base[1] = io_base1;
++ ide_pci_ops->base[2] = io_base1;
+ #ifdef USE_OPENFIRMWARE
+- ide_pci_ops->OF_private[0] = OF_private;
++ ide_pci_ops->OF_private[0] = OF_private0;
+ #endif
+ }
+ if ((io_base2 != 0 || io_base3 != 0) &&
+- ide_pci_ops->base[2] == 0 && ide_pci_ops->base[3] == 0) {
+- ide_pci_ops->base[2] = io_base2;
++ ide_pci_ops->base[1] == 0 && ide_pci_ops->base[3] == 0) {
++ ide_pci_ops->base[1] = io_base2;
+ ide_pci_ops->base[3] = io_base3;
+ #ifdef USE_OPENFIRMWARE
+- ide_pci_ops->OF_private[1] = OF_private;
++ ide_pci_ops->OF_private[1] = OF_private1;
+ #endif
+ }
+ }
+@@ -935,6 +950,8 @@
+ }
+
+ static void atapi_pad_req (void *buffer, int len);
++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer,
++ int maxlen);
+ static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum);
+
+ static int ide_initialize (bloc_device_t *bd, int device)
+@@ -1035,9 +1052,7 @@
+ DPRINTF("INQUIRY\n");
+ len = spc_inquiry_req(&atapi_buffer, 36);
+ atapi_pad_req(&atapi_buffer, len);
+- ide_port_write(bd, 0x07, 0xA0);
+- for (i = 0; i < 3; i++)
+- ide_data_writel(bd, ldswap32(&atapi_buffer[i]));
++ atapi_make_req(bd, atapi_buffer, 36);
+ status = ide_port_read(bd, 0x07);
+ if (status != 0x48) {
+ ERROR("ATAPI INQUIRY : status %0x != 0x48\n", status);
+@@ -1053,9 +1068,7 @@
+ DPRINTF("READ_CAPACITY\n");
+ len = mmc_read_capacity_req(&atapi_buffer);
+ atapi_pad_req(&atapi_buffer, len);
+- ide_port_write(bd, 0x07, 0xA0);
+- for (i = 0; i < 3; i++)
+- ide_data_writel(bd, ldswap32(&atapi_buffer[i]));
++ atapi_make_req(bd, atapi_buffer, 8);
+ status = ide_port_read(bd, 0x07);
+ if (status != 0x48) {
+ ERROR("ATAPI READ_CAPACITY : status %0x != 0x48\n", status);
+@@ -1105,6 +1118,22 @@
+ memset(p + len, 0, 12 - len);
+ }
+
++static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer,
++ int maxlen)
++{
++ int i;
++ /* select drive */
++ if (bd->drv == 0)
++ ide_port_write(bd, 0x06, 0x40);
++ else
++ ide_port_write(bd, 0x06, 0x50);
++ ide_port_write(bd, 0x04, maxlen & 0xff);
++ ide_port_write(bd, 0x05, (maxlen >> 8) & 0xff);
++ ide_port_write(bd, 0x07, 0xA0);
++ for (i = 0; i < 3; i++)
++ ide_data_writel(bd, ldswap32(&buffer[i]));
++}
++
+ static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum)
+ {
+ uint32_t atapi_buffer[4];
+@@ -1112,16 +1141,9 @@
+ uint32_t status, value;
+ int i, len;
+
+- /* select drive */
+- if (bd->drv == 0)
+- ide_port_write(bd, 0x06, 0x40);
+- else
+- ide_port_write(bd, 0x06, 0x50);
+ len = mmc_read12_req(atapi_buffer, secnum, 1);
+ atapi_pad_req(&atapi_buffer, len);
+- ide_port_write(bd, 0x07, 0xA0);
+- for (i = 0; i < 3; i++)
+- ide_data_writel(bd, ldswap32(&atapi_buffer[i]));
++ atapi_make_req(bd, atapi_buffer, bd->seclen);
+ status = ide_port_read(bd, 0x07);
+ if (status != 0x48) {
+ ERROR("ATAPI READ12 : status %0x != 0x48\n", status);
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/apple.c OpenHackWare-release-0.4/src/libpart/apple.c
+--- OpenHackWare-release-0.4.org/src/libpart/apple.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/apple.c 2005-07-03 16:17:41.000000000 +0200
+@@ -199,14 +199,18 @@
+ if (len == 0) {
+ /* Place holder. Skip it */
+ DPRINTF("%s placeholder part\t%d\n", __func__, i);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Void", type, 32) == 0) {
+ /* Void partition. Skip it */
+ DPRINTF("%s Void part\t%d [%s]\n", __func__, i, type);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Free", type, 32) == 0) {
+ /* Free space. Skip it */
+ DPRINTF("%s Free part (%d)\n", __func__, i);
+ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
+- part_register(bd, part, name);
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_partition_map", type, 32) == 0 ||
+ strncmp("Apple_Partition_Map", type, 32) == 0
+ #if 0 // Is this really used or is it just a mistake ?
+@@ -226,7 +230,7 @@
+ */
+ }
+ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
+- part_register(bd, part, name);
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Driver", type, 32) == 0 ||
+ strncmp("Apple_Driver43", type, 32) == 0 ||
+ strncmp("Apple_Driver43_CD", type, 32) == 0 ||
+@@ -236,8 +240,12 @@
+ strncmp("Apple_Driver_IOKit", type, 32) == 0) {
+ /* Drivers. don't care for now */
+ DPRINTF("%s Drivers part\t%d [%s]\n", __func__, i, type);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DRIVER;
++ part_register(bd, part, name, i);
+ } else if (strncmp("Apple_Patches", type, 32) == 0) {
+ /* Patches: don't care for now */
++ part->flags = PART_TYPE_APPLE | PART_FLAG_PATCH;
++ part_register(bd, part, name, i);
+ DPRINTF("%s Patches part\t%d [%s]\n", __func__, i, type);
+ } else if (strncmp("Apple_HFS", type, 32) == 0 ||
+ strncmp("Apple_MFS", type, 32) == 0 ||
+@@ -256,9 +264,8 @@
+ count = partmap->bloc_cnt * HFS_BLOCSIZE;
+ if (partmap->boot_size == 0 || partmap->boot_load == 0) {
+ printf("Not a bootable partition %d %d (%p %p)\n",
+- partmap->boot_size, partmap->boot_load,boot_part, part);
+- if (boot_part == NULL)
+- boot_part = part;
++ partmap->boot_size, partmap->boot_load,
++ boot_part, part);
+ part->flags = PART_TYPE_APPLE | PART_FLAG_FS;
+ } else {
+ part->boot_start.bloc = partmap->boot_start;
+@@ -278,8 +285,8 @@
+ boot_part = part;
+ part->flags = PART_TYPE_APPLE | PART_FLAG_FS | PART_FLAG_BOOT;
+ }
+- printf("Partition: %d %s st %0x size %0x",
+- i, name, partmap->start_bloc, partmap->bloc_cnt);
++ printf("Partition: %d '%s' '%s' st %0x size %0x",
++ i, name, type, partmap->start_bloc, partmap->bloc_cnt);
+ #ifndef DEBUG
+ printf("\n");
+ #endif
+@@ -290,11 +297,13 @@
+ part->boot_load, part->boot_entry);
+ DPRINTF(" load %0x entry %0x %0x\n",
+ partmap->boot_load2, partmap->boot_entry2, HFS_BLOCSIZE);
+- part_register(bd, part, name);
++ part_register(bd, part, name, i);
+ } else {
+ memcpy(tmp, type, 32);
+ tmp[32] = '\0';
+ ERROR("Unknown partition type [%s]\n", tmp);
++ part->flags = PART_TYPE_APPLE | PART_FLAG_DUMMY;
++ part_register(bd, part, name, i);
+ }
+ }
+ error:
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/core.c OpenHackWare-release-0.4/src/libpart/core.c
+--- OpenHackWare-release-0.4.org/src/libpart/core.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/core.c 2005-07-03 16:17:41.000000000 +0200
+@@ -126,7 +126,7 @@
+ }
+
+ int part_register (bloc_device_t *bd, part_t *partition,
+- const unsigned char *name)
++ const unsigned char *name, int partnum)
+ {
+ part_t **cur;
+
+@@ -134,6 +134,7 @@
+ partition->bd = bd;
+ partition->next = NULL;
+ partition->name = strdup(name);
++ partition->partnum = partnum;
+ for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next)
+ continue;
+ *cur = partition;
+@@ -141,29 +142,15 @@
+ return 0;
+ }
+
+-static inline int set_boot_part (bloc_device_t *bd, int partnum)
+-{
+- part_t *cur;
+-
+- cur = part_get(bd, partnum);
+- if (cur == NULL)
+- return -1;
+- bd_set_boot_part(bd, cur);
+-
+- return 0;
+-}
+-
+ part_t *part_get (bloc_device_t *bd, int partnum)
+ {
+ part_t **listp, *cur;
+- int i;
+
+ listp = _bd_parts(bd);
+- cur = *listp;
+- for (i = 0; i != partnum; i++) {
+- if (cur == NULL)
++
++ for (cur = *listp; cur != NULL; cur = cur->next) {
++ if (cur->partnum == partnum)
+ break;
+- cur = cur->next;
+ }
+
+ return cur;
+@@ -192,17 +179,20 @@
+ part_set_blocsize(bd, part, 512);
+ part->bd = bd;
+ part->flags = PART_TYPE_RAW | PART_FLAG_BOOT;
+- part_register(bd, part, "Raw");
++ part_register(bd, part, "Raw", 0);
+
+ return part;
+ }
+
++bloc_device_t *part_get_bd (part_t *part)
++{
++ return part->bd;
++}
++
+ part_t *part_probe (bloc_device_t *bd, int set_raw)
+ {
+- part_t *part0, *boot_part, **cur;
++ part_t *part0 = NULL, *boot_part, **cur;
+
+- /* Register the 0 partition: raw partition containing the whole disk */
+- part0 = part_get_raw(bd);
+ /* Try to find a valid boot partition */
+ boot_part = Apple_probe_partitions(bd);
+ if (boot_part == NULL) {
+@@ -210,10 +200,13 @@
+ if (boot_part == NULL && arch == ARCH_PREP)
+ boot_part = PREP_find_partition(bd);
+ if (boot_part == NULL && set_raw != 0) {
+- boot_part = part0;
+- set_boot_part(bd, 0);
++ dprintf("Use bloc device as raw partition\n");
+ }
+ }
++ if (_bd_parts(bd) == NULL) {
++ /* Register the 0 partition: raw partition containing the whole disk */
++ part0 = part_get_raw(bd);
++ }
+ /* Probe filesystem on each found partition */
+ for (cur = _bd_parts(bd); *cur != NULL; cur = &(*cur)->next) {
+ const unsigned char *map, *type;
+@@ -248,23 +241,28 @@
+ type = "unknown";
+ break;
+ }
+- DPRINTF("Probe filesystem on %s %s partition '%s' %s\n",
++ dprintf("Probe filesystem on %s %s partition '%s' %s %p\n",
+ type, map, (*cur)->name,
+- ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : "");
++ ((*cur)->flags) & PART_FLAG_BOOT ? "(bootable)" : "", *cur);
+ if (((*cur)->flags) & PART_FLAG_FS) {
+ if (((*cur)->flags) & PART_FLAG_BOOT)
+ (*cur)->fs = fs_probe(*cur, 1);
+ else
+ (*cur)->fs = fs_probe(*cur, 0);
++ } else if (((*cur)->flags) & PART_TYPE_RAW) {
++ (*cur)->fs = fs_probe(*cur, 2);
+ } else {
+ (*cur)->fs = fs_probe(*cur, 2);
+ }
+- if (((*cur)->flags) & PART_FLAG_BOOT) {
+- bd_set_boot_part(bd, *cur);
+ fs_get_bootfile((*cur)->fs);
++ if (((*cur)->flags) & PART_FLAG_BOOT) {
++ dprintf("Partition is bootable (%d)\n", (*cur)->partnum);
++ bd_set_boot_part(bd, *cur, (*cur)->partnum);
++ if (boot_part == NULL)
++ boot_part = *cur;
+ }
+ }
+- DPRINTF("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs,
++ dprintf("Boot partition: %p %p %p %p\n", boot_part, boot_part->fs,
+ part_fs(boot_part), part0);
+
+ return boot_part;
+@@ -279,6 +277,7 @@
+ part->boot_size.offset = 0;
+ part->boot_load = 0;
+ part->boot_entry = 0;
++ part->flags |= PART_FLAG_BOOT;
+
+ return 0;
+ }
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/isofs.c OpenHackWare-release-0.4/src/libpart/isofs.c
+--- OpenHackWare-release-0.4.org/src/libpart/isofs.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/isofs.c 2005-07-03 16:17:41.000000000 +0200
+@@ -242,7 +242,7 @@
+ part->boot_start.bloc, part->boot_size.bloc,
+ part->boot_load, part->boot_entry);
+ part->flags = PART_TYPE_ISO9660 | PART_FLAG_BOOT;
+- part_register(bd, part, name);
++ part_register(bd, part, name, i + 1);
+ fs_raw_set_bootfile(part, part->boot_start.bloc,
+ part->boot_start.offset,
+ part->boot_size.bloc,
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/libpart.h OpenHackWare-release-0.4/src/libpart/libpart.h
+--- OpenHackWare-release-0.4.org/src/libpart/libpart.h 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/libpart.h 2005-07-03 16:17:41.000000000 +0200
+@@ -30,6 +30,7 @@
+
+ struct part_t {
+ bloc_device_t *bd;
++ int partnum;
+ uint32_t start; /* Partition first bloc */
+ uint32_t size; /* Partition size, in blocs */
+ uint32_t spb;
+@@ -54,7 +55,7 @@
+ };
+
+ int part_register (bloc_device_t *bd, part_t *partition,
+- const unsigned char *name);
++ const unsigned char *name, int partnum);
+ void part_set_blocsize (bloc_device_t *bd, part_t *part, uint32_t blocsize);
+ void part_private_set (part_t *part, void *private);
+ void *part_private_get (part_t *part);
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/libpart/prep.c OpenHackWare-release-0.4/src/libpart/prep.c
+--- OpenHackWare-release-0.4.org/src/libpart/prep.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/libpart/prep.c 2005-07-03 16:17:41.000000000 +0200
+@@ -164,7 +164,7 @@
+ part->boot_load = 0;
+ part->boot_entry = boot_offset - part->bloc_size;
+ part->flags = PART_TYPE_PREP | PART_FLAG_BOOT;
+- part_register(bd, part, "PREP boot");
++ part_register(bd, part, "PREP boot", i);
+ fs_raw_set_bootfile(part, part->boot_start.bloc,
+ part->boot_start.offset,
+ part->boot_size.bloc,
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/main.c OpenHackWare-release-0.4/src/main.c
+--- OpenHackWare-release-0.4.org/src/main.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/main.c 2005-06-07 23:48:39.000000000 +0200
+@@ -364,20 +364,24 @@
+ void *load_base, *load_entry, *last_alloc, *load_end;
+ uint32_t memsize, boot_image_size, cmdline_size, ramdisk_size;
+ uint32_t boot_base, boot_nb;
+- int boot_device;
++ int boot_device, i;
++ static const uint32_t isa_base_tab[3] = {
++ 0x80000000, /* PREP */
++ 0xFE000000, /* Grackle (Heathrow) */
++ 0xF2000000, /* UniNorth (Mac99) */
++ };
+
+ /* Retrieve NVRAM configuration */
+- nvram_retry:
++ for(i = 0; i < 3; i++) {
++ isa_io_base = isa_base_tab[i];
+ nvram = NVRAM_get_config(&memsize, &boot_device,
+ &boot_image, &boot_image_size,
+ &cmdline, &cmdline_size,
+ &ramdisk, &ramdisk_size);
+- if (nvram == NULL) {
+- /* Retry with another isa_io_base */
+- if (isa_io_base == 0x80000000) {
+- isa_io_base = 0xF2000000;
+- goto nvram_retry;
++ if (nvram)
++ break;
+ }
++ if (i == 3) {
+ ERROR("Unable to load configuration from NVRAM. Aborting...\n");
+ return -1;
+ }
+@@ -402,7 +406,7 @@
+ cpu_name = CPU_get_name(pvr);
+ OF_register_cpu(cpu_name, 0, pvr,
+ 200 * 1000 * 1000, 200 * 1000 * 1000,
+- 100 * 1000 * 1000, 10 * 1000 * 1000,
++ 100 * 1000 * 1000, 100 * 1000 * 1000,
+ 0x0092);
+ }
+ OF_register_memory(memsize, 512 * 1024 /* TOFIX */);
+@@ -433,9 +437,12 @@
+ vga_puts(copyright);
+ vga_puts("\n");
+
++#if 0
+ /* QEMU is quite incoherent: d is cdrom, not second drive */
++ /* XXX: should probe CD-ROM position */
+ if (boot_device == 'd')
+ boot_device = 'e';
++#endif
+ /* Open boot device */
+ boot_part = bd_probe(boot_device);
+ if (boot_device == 'm') {
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/nvram.c OpenHackWare-release-0.4/src/nvram.c
+--- OpenHackWare-release-0.4.org/src/nvram.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/nvram.c 2005-06-04 23:44:03.000000000 +0200
+@@ -334,6 +334,7 @@
+ ret = NVRAM_chrp_format(nvram);
+ break;
+ case ARCH_MAC99:
++ case ARCH_HEATHROW: /* XXX: may be incorrect */
+ ret = NVRAM_mac99_format(nvram);
+ break;
+ case ARCH_POP:
+@@ -409,13 +410,12 @@
+ arch = ARCH_MAC99;
+ } else if (strcmp(sign, "POP") == 0) {
+ arch = ARCH_POP;
++ } else if (strcmp(sign, "HEATHROW") == 0) {
++ arch = ARCH_HEATHROW;
+ } else {
+ ERROR("Unknown PPC architecture: '%s'\n", sign);
+ return NULL;
+ }
+- /* HACK */
+- if (arch == ARCH_CHRP)
+- arch = ARCH_MAC99;
+ lword = NVRAM_get_lword(nvram, 0x30);
+ *RAM_size = lword;
+ byte = NVRAM_get_byte(nvram, 0x34);
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/of.c OpenHackWare-release-0.4/src/of.c
+--- OpenHackWare-release-0.4.org/src/of.c 2005-04-06 23:17:26.000000000 +0200
++++ OpenHackWare-release-0.4/src/of.c 2005-07-07 23:30:08.000000000 +0200
+@@ -489,7 +489,7 @@
+ ERROR("%s can't alloc new node '%s' name\n", __func__, name);
+ return NULL;
+ }
+- new->prop_address = OF_prop_int_new(env, new, "address", address);
++ new->prop_address = OF_prop_int_new(env, new, "unit-address", address);
+ if (new->prop_address == NULL) {
+ free(new->prop_name->value);
+ free(new->prop_name);
+@@ -1017,6 +1017,33 @@
+ string, strlen(string) + 1);
+ }
+
++/* convert '\1' char to '\0' */
++static OF_prop_t *OF_prop_string_new1 (OF_env_t *env, OF_node_t *node,
++ const unsigned char *name,
++ const unsigned char *string)
++{
++ int len, i;
++ OF_prop_t *ret;
++ unsigned char *str;
++
++ if (strchr(string, '\1') == NULL) {
++ return OF_prop_string_new(env, node, name, string);
++ } else {
++ len = strlen(string) + 1;
++ str = malloc(len);
++ if (!str)
++ return NULL;
++ memcpy(str, string, len);
++ for(i = 0; i < len; i++)
++ if (str[i] == '\1')
++ str[i] = '\0';
++ ret = OF_property_new(env, node, name,
++ str, len);
++ free(str);
++ return ret;
++ }
++}
++
+ __attribute__ (( section (".OpenFirmware") ))
+ static OF_prop_t *OF_prop_int_new (OF_env_t *env, OF_node_t *node,
+ const unsigned char *name, uint32_t value)
+@@ -1421,15 +1448,12 @@
+ __attribute__ (( section (".OpenFirmware") ))
+ int OF_init (void)
+ {
+- const unsigned char compat_str[] =
+ #if 0
+ "PowerMac3,1\0MacRISC\0Power Macintosh\0";
+ "PowerMac1,2\0MacRISC\0Power Macintosh\0";
+ "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0";
+ "AAPL,PowerMac3,0\0MacRISC\0Power Macintosh\0";
+ "AAPL,Gossamer\0MacRISC\0Power Macintosh\0";
+-#else
+- "AAPL,PowerMac G3\0PowerMac G3\0MacRISC\0Power Macintosh\0";
+ #endif
+ OF_env_t *OF_env;
+ OF_node_t *als, *opt, *chs, *pks;
+@@ -1455,15 +1479,21 @@
+ return -1;
+ }
+ OF_prop_string_new(OF_env, OF_node_root, "device_type", "bootrom");
+-#if 0
+- OF_prop_string_new(OF_env, OF_node_root,
+- "model", "PPC Open Hack'Ware " BIOS_VERSION);
+-#else
++ if (arch == ARCH_HEATHROW) {
++ const unsigned char compat_str[] =
++ "PowerMac1,1\0MacRISC\0Power Macintosh";
++ OF_property_new(OF_env, OF_node_root, "compatible",
++ compat_str, sizeof(compat_str));
+ OF_prop_string_new(OF_env, OF_node_root,
+- "model", compat_str);
+-#endif
++ "model", "Power Macintosh");
++ } else {
++ const unsigned char compat_str[] =
++ "PowerMac3,1\0MacRISC\0Power Macintosh";
+ OF_property_new(OF_env, OF_node_root, "compatible",
+ compat_str, sizeof(compat_str));
++ OF_prop_string_new(OF_env, OF_node_root,
++ "model", "PowerMac3,1");
++ }
+ #if 0
+ OF_prop_string_new(OF_env, OF_node_root, "copyright", copyright);
+ #else
+@@ -1561,14 +1591,15 @@
+ range.size = 0x00800000;
+ OF_property_new(OF_env, rom, "ranges", &range, sizeof(OF_range_t));
+ OF_prop_int_new(OF_env, rom, "#address-cells", 1);
++
+ /* "/rom/boot-rom@fff00000" node */
+- brom = OF_node_new(OF_env, OF_node_root, "boot-rom", 0xfff00000);
++ brom = OF_node_new(OF_env, rom, "boot-rom", 0xfff00000);
+ if (brom == NULL) {
+ ERROR("Cannot create 'boot-rom'\n");
+ return -1;
+ }
+ regs.address = 0xFFF00000;
+- regs.size = 0x00010000;
++ regs.size = 0x00100000;
+ OF_property_new(OF_env, brom, "reg", &regs, sizeof(OF_regprop_t));
+ OF_prop_string_new(OF_env, brom, "write-characteristic", "flash");
+ OF_prop_string_new(OF_env, brom, "BootROM-build-date",
+@@ -1577,7 +1608,7 @@
+ OF_prop_string_new(OF_env, brom, "copyright", copyright);
+ OF_prop_string_new(OF_env, brom, "model", BIOS_str);
+ OF_prop_int_new(OF_env, brom, "result", 0);
+-#if 0
++#if 1
+ {
+ /* Hack taken 'as-is' from PearPC */
+ unsigned char info[] = {
+@@ -1596,7 +1627,9 @@
+ OF_node_put(OF_env, brom);
+ OF_node_put(OF_env, rom);
+ }
++#if 0
+ /* From here, hardcoded hacks to get a Mac-like machine */
++ /* XXX: Core99 does not seem to like this NVRAM tree */
+ /* "/nvram@fff04000" node */
+ {
+ OF_regprop_t regs;
+@@ -1617,6 +1650,7 @@
+ OF_prop_int_new(OF_env, chs, "nvram", OF_pack_handle(OF_env, nvr));
+ OF_node_put(OF_env, nvr);
+ }
++#endif
+ /* "/pseudo-hid" : hid emulation as Apple does */
+ {
+ OF_node_t *hid;
+@@ -1663,7 +1697,27 @@
+ }
+ OF_node_put(OF_env, hid);
+ }
++ if (arch == ARCH_MAC99) {
++ OF_node_t *unin;
++ OF_regprop_t regs;
+
++ unin = OF_node_new(OF_env, OF_node_root,
++ "uni-n", 0xf8000000);
++ if (unin == NULL) {
++ ERROR("Cannot create 'uni-n'\n");
++ return -1;
++ }
++ OF_prop_string_new(OF_env, unin, "device-type", "memory-controller");
++ OF_prop_string_new(OF_env, unin, "model", "AAPL,UniNorth");
++ OF_prop_string_new(OF_env, unin, "compatible", "uni-north");
++ regs.address = 0xf8000000;
++ regs.size = 0x01000000;
++ OF_property_new(OF_env, unin, "reg", &regs, sizeof(regs));
++ OF_prop_int_new(OF_env, unin, "#address-cells", 1);
++ OF_prop_int_new(OF_env, unin, "#size-cells", 1);
++ OF_prop_int_new(OF_env, unin, "device-rev", 3);
++ OF_node_put(OF_env, unin);
++ }
+
+ #if 1 /* This is mandatory for claim to work
+ * but I don't know where it should really be (in cpu ?)
+@@ -1693,7 +1747,9 @@
+
+ /* "/options/boot-args" node */
+ {
+- const unsigned char *args = "-v rootdev cdrom";
++ // const unsigned char *args = "-v rootdev cdrom";
++ //const unsigned char *args = "-v io=0xffffffff";
++ const unsigned char *args = "-v";
+ /* Ask MacOS X to print debug messages */
+ // OF_prop_string_new(OF_env, chs, "machargs", args);
+ // OF_prop_string_new(OF_env, opt, "boot-command", args);
+@@ -2013,17 +2069,17 @@
+ OF_prop_int_new(OF_env, node, "min-grant", min_grant);
+ OF_prop_int_new(OF_env, node, "max-latency", max_latency);
+ if (dev->type != NULL)
+- OF_prop_string_new(OF_env, node, "device_type", dev->type);
++ OF_prop_string_new1(OF_env, node, "device_type", dev->type);
+ if (dev->compat != NULL)
+- OF_prop_string_new(OF_env, node, "compatible", dev->compat);
++ OF_prop_string_new1(OF_env, node, "compatible", dev->compat);
+ if (dev->model != NULL)
+- OF_prop_string_new(OF_env, node, "model", dev->model);
++ OF_prop_string_new1(OF_env, node, "model", dev->model);
+ if (dev->acells != 0)
+ OF_prop_int_new(OF_env, node, "#address-cells", dev->acells);
+ if (dev->scells != 0)
+- OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->acells);
++ OF_prop_int_new(OF_env, node, "#size-cells", dev->scells);
+ if (dev->icells != 0)
+- OF_prop_int_new(OF_env, node, "#size-cells", dev->acells);
++ OF_prop_int_new(OF_env, node, "#interrupt-cells", dev->icells);
+ dprintf("Done %p %p\n", parent, node);
+
+ return node;
+@@ -2040,8 +2096,9 @@
+ OF_env_t *OF_env;
+ pci_range_t ranges[3];
+ OF_regprop_t regs[1];
+- OF_node_t *pci_host;
++ OF_node_t *pci_host, *als;
+ int nranges;
++ unsigned char buffer[OF_NAMELEN_MAX];
+
+ OF_env = OF_env_main;
+ dprintf("register PCI host '%s' '%s' '%s' '%s'\n",
+@@ -2052,6 +2109,17 @@
+ ERROR("Cannot create pci host\n");
+ return NULL;
+ }
++
++ als = OF_node_get(OF_env, "aliases");
++ if (als == NULL) {
++ ERROR("Cannot get 'aliases'\n");
++ return NULL;
++ }
++ sprintf(buffer, "/%s", dev->name);
++ OF_prop_string_set(OF_env, als, "pci", buffer);
++ OF_node_put(OF_env, als);
++
++
+ regs[0].address = cfg_base;
+ regs[0].size = cfg_len;
+ OF_property_new(OF_env, pci_host, "reg", regs, sizeof(OF_regprop_t));
+@@ -2136,6 +2204,11 @@
+ return pci_dev;
+ }
+
++/* XXX: suppress that, used for interrupt map init */
++OF_node_t *pci_host_node;
++uint32_t pci_host_interrupt_map[7 * 32];
++int pci_host_interrupt_map_len = 0;
++
+ void OF_finalize_pci_host (void *dev, int first_bus, int nb_busses)
+ {
+ OF_env_t *OF_env;
+@@ -2145,10 +2218,12 @@
+ regs[0].address = first_bus;
+ regs[0].size = nb_busses;
+ OF_property_new(OF_env, dev, "bus-range", regs, sizeof(OF_regprop_t));
++ pci_host_node = dev;
+ }
+
+ void OF_finalize_pci_device (void *dev, uint8_t bus, uint8_t devfn,
+- uint32_t *regions, uint32_t *sizes)
++ uint32_t *regions, uint32_t *sizes,
++ int irq_line)
+ {
+ OF_env_t *OF_env;
+ pci_reg_prop_t pregs[6], rregs[6];
+@@ -2156,6 +2231,7 @@
+ int i, j, k;
+
+ OF_env = OF_env_main;
++ /* XXX: only useful for VGA card in fact */
+ if (regions[0] != 0x00000000)
+ OF_prop_int_set(OF_env, dev, "address", regions[0] & ~0x0000000F);
+ for (i = 0, j = 0, k = 0; i < 6; i++) {
+@@ -2222,7 +2298,22 @@
+ } else {
+ OF_property_new(OF_env, dev, "assigned-addresses", NULL, 0);
+ }
+-#if 0
++ if (irq_line >= 0) {
++ int i;
++ OF_prop_int_new(OF_env, dev, "interrupts", 1);
++ i = pci_host_interrupt_map_len;
++ pci_host_interrupt_map[i++] = (devfn << 8) & 0xf800;
++ pci_host_interrupt_map[i++] = 0;
++ pci_host_interrupt_map[i++] = 0;
++ pci_host_interrupt_map[i++] = 0;
++ pci_host_interrupt_map[i++] = 0; /* pic handle will be patched later */
++ pci_host_interrupt_map[i++] = irq_line;
++ if (arch != ARCH_HEATHROW) {
++ pci_host_interrupt_map[i++] = 1;
++ }
++ pci_host_interrupt_map_len = i;
++ }
++#if 1
+ {
+ OF_prop_t *prop_name = ((OF_node_t *)dev)->prop_name;
+
+@@ -2390,6 +2481,54 @@
+ return 0;
+ }
+
++static void keylargo_ata(OF_node_t *mio, uint32_t base_address,
++ uint32_t base, int irq1, int irq2,
++ uint16_t pic_phandle)
++{
++ OF_env_t *OF_env = OF_env_main;
++ OF_node_t *ata;
++ OF_regprop_t regs[2];
++
++ ata = OF_node_new(OF_env, mio, "ata-4", base);
++ if (ata == NULL) {
++ ERROR("Cannot create 'ata-4'\n");
++ return;
++ }
++ OF_prop_string_new(OF_env, ata, "device_type", "ata");
++#if 1
++ OF_prop_string_new(OF_env, ata, "compatible", "key2largo-ata");
++ OF_prop_string_new(OF_env, ata, "model", "ata-4");
++ OF_prop_string_new(OF_env, ata, "cable-type", "80-conductor");
++#else
++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
++ OF_prop_string_new(OF_env, ata, "model", "ata-4");
++#endif
++ OF_prop_int_new(OF_env, ata, "#address-cells", 1);
++ OF_prop_int_new(OF_env, ata, "#size-cells", 0);
++ regs[0].address = base;
++ regs[0].size = 0x00001000;
++#if 0 // HACK: Don't set up DMA registers
++ regs[1].address = 0x00008A00;
++ regs[1].size = 0x00001000;
++ OF_property_new(OF_env, ata, "reg",
++ regs, 2 * sizeof(OF_regprop_t));
++#else
++ OF_property_new(OF_env, ata, "reg",
++ regs, sizeof(OF_regprop_t));
++#endif
++ OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle);
++ regs[0].address = irq1;
++ regs[0].size = 0x00000001;
++ regs[1].address = irq2;
++ regs[1].size = 0x00000000;
++ OF_property_new(OF_env, ata, "interrupts",
++ regs, 2 * sizeof(OF_regprop_t));
++ if (base == 0x1f000)
++ ide_pci_pmac_register(base_address + base, 0x00000000, ata);
++ else
++ ide_pci_pmac_register(0x00000000, base_address + base, ata);
++}
++
+ void OF_finalize_pci_macio (void *dev, uint32_t base_address, uint32_t size,
+ void *private_data)
+ {
+@@ -2398,6 +2537,8 @@
+ pci_reg_prop_t pregs[2];
+ OF_node_t *mio, *chs, *als;
+ uint16_t pic_phandle;
++ int rec_len;
++ OF_prop_t *mio_reg;
+
+ OF_DPRINTF("mac-io: %p\n", dev);
+ OF_env = OF_env_main;
+@@ -2416,10 +2557,14 @@
+ mio = dev;
+ mio->private_data = private_data;
+ pregs[0].addr.hi = 0x00000000;
+- pregs[0].addr.mid = 0x82013810;
++ pregs[0].addr.mid = 0x00000000;
+ pregs[0].addr.lo = 0x00000000;
+ pregs[0].size_hi = base_address;
+ pregs[0].size_lo = size;
++ mio_reg = OF_property_get(OF_env, mio, "reg");
++ if (mio_reg && mio_reg->vlen >= 5 * 4) {
++ pregs[0].addr.mid = ((pci_reg_prop_t *)mio_reg->value)->addr.hi;
++ }
+ OF_property_new(OF_env, mio, "ranges",
+ &pregs, sizeof(pci_reg_prop_t));
+ #if 0
+@@ -2431,8 +2576,32 @@
+ OF_property_new(OF_env, mio, "assigned-addresses",
+ &pregs, sizeof(pci_reg_prop_t));
+ #endif
++
++ if (arch == ARCH_HEATHROW) {
++ /* Heathrow PIC */
++ OF_regprop_t regs;
++ OF_node_t *mpic;
++ const char compat_str[] = "heathrow\0mac-risc";
++
++ mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x10);
++ if (mpic == NULL) {
++ ERROR("Cannot create 'mpic'\n");
++ goto out;
++ }
++ OF_prop_string_new(OF_env, mpic, "device_type", "interrupt-controller");
++ OF_property_new(OF_env, mpic, "compatible", compat_str, sizeof(compat_str));
++ OF_prop_int_new(OF_env, mpic, "#interrupt-cells", 1);
++ regs.address = 0x10;
++ regs.size = 0x20;
++ OF_property_new(OF_env, mpic, "reg",
++ &regs, sizeof(regs));
++ OF_property_new(OF_env, mpic, "interrupt-controller", NULL, 0);
++ pic_phandle = OF_pack_handle(OF_env, mpic);
++ OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle);
++ OF_node_put(OF_env, mpic);
++ rec_len = 6;
++ } else {
+ /* OpenPIC */
+- {
+ OF_regprop_t regs[4];
+ OF_node_t *mpic;
+ mpic = OF_node_new(OF_env, mio, "interrupt-controller", 0x40000);
+@@ -2455,8 +2624,37 @@
+ pic_phandle = OF_pack_handle(OF_env, mpic);
+ OF_prop_int_new(OF_env, chs, "interrupt-controller", pic_phandle);
+ OF_node_put(OF_env, mpic);
++ rec_len = 7;
+ }
+-#if 1
++
++ /* patch pci host table */
++ /* XXX: do it after the PCI init */
++ {
++ int i;
++ uint32_t tab[4];
++
++ for(i = 0; i < pci_host_interrupt_map_len; i += rec_len)
++ pci_host_interrupt_map[i + 4] = pic_phandle;
++#if 0
++ dprintf("interrupt-map:\n");
++ for(i = 0; i < pci_host_interrupt_map_len; i++) {
++ dprintf(" %08x", pci_host_interrupt_map[i]);
++ if ((i % rec_len) == (rec_len - 1))
++ dprintf("\n");
++ }
++ dprintf("\n");
++#endif
++ OF_property_new(OF_env, pci_host_node, "interrupt-map",
++ pci_host_interrupt_map,
++ pci_host_interrupt_map_len * sizeof(uint32_t));
++ tab[0] = 0xf800;
++ tab[1] = 0;
++ tab[2] = 0;
++ tab[3] = 0;
++ OF_property_new(OF_env, pci_host_node, "interrupt-map-mask",
++ tab, 4 * sizeof(uint32_t));
++ }
++#if 0
+ /* escc is usefull to get MacOS X debug messages */
+ {
+ OF_regprop_t regs[8];
+@@ -2645,85 +2843,12 @@
+ OF_node_put(OF_env, scc);
+ }
+ #endif
+- /* IDE controller */
+- {
+- OF_node_t *ata;
+- OF_regprop_t regs[2];
+- ata = OF_node_new(OF_env, mio, "ata-4", 0x1f000);
+- if (ata == NULL) {
+- ERROR("Cannot create 'ata-4'\n");
+- goto out;
+- }
+- OF_prop_string_new(OF_env, ata, "device_type", "ata");
+-#if 1
+- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#else
+- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#endif
+- OF_prop_int_new(OF_env, ata, "#address-cells", 1);
+- OF_prop_int_new(OF_env, ata, "#size-cells", 0);
+- regs[0].address = 0x0001F000;
+- regs[0].size = 0x00001000;
+-#if 0 // HACK: Don't set up DMA registers
+- regs[1].address = 0x00008A00;
+- regs[1].size = 0x00001000;
+- OF_property_new(OF_env, ata, "reg",
+- regs, 2 * sizeof(OF_regprop_t));
+-#else
+- OF_property_new(OF_env, ata, "reg",
+- regs, sizeof(OF_regprop_t));
+-#endif
+- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle);
+- regs[0].address = 0x00000013;
+- regs[0].size = 0x00000001;
+- regs[1].address = 0x0000000B;
+- regs[1].size = 0x00000000;
+- OF_property_new(OF_env, ata, "interrupts",
+- regs, 2 * sizeof(OF_regprop_t));
+- ide_pci_pmac_register(base_address + 0x1f000, 0x00000000, ata);
+-
+- }
+- {
+- OF_node_t *ata;
+- OF_regprop_t regs[2];
+- ata = OF_node_new(OF_env, mio, "ata-4", 0x20000);
+- if (ata == NULL) {
+- ERROR("Cannot create 'ata-4'\n");
+- goto out;
+- }
+- OF_prop_string_new(OF_env, ata, "device_type", "ata");
+-#if 1
+- OF_prop_string_new(OF_env, ata, "compatible", "keylargo-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#else
+- OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
+- OF_prop_string_new(OF_env, ata, "model", "ata-4");
+-#endif
+- OF_prop_int_new(OF_env, ata, "#address-cells", 1);
+- OF_prop_int_new(OF_env, ata, "#size-cells", 0);
+- regs[0].address = 0x00020000;
+- regs[0].size = 0x00001000;
+-#if 0 // HACK: Don't set up DMA registers
+- regs[1].address = 0x00008A00;
+- regs[1].size = 0x00001000;
+- OF_property_new(OF_env, ata, "reg",
+- regs, 2 * sizeof(OF_regprop_t));
+-#else
+- OF_property_new(OF_env, ata, "reg",
+- regs, sizeof(OF_regprop_t));
+-#endif
+- OF_prop_int_new(OF_env, ata, "interrupt-parent", pic_phandle);
+- regs[0].address = 0x00000014;
+- regs[0].size = 0x00000001;
+- regs[1].address = 0x0000000B;
+- regs[1].size = 0x00000000;
+- OF_property_new(OF_env, ata, "interrupts",
+- regs, 2 * sizeof(OF_regprop_t));
+- ide_pci_pmac_register(0x00000000, base_address + 0x20000, ata);
+-
++ /* Keylargo IDE controller: need some work (DMA problem ?) */
++ if (arch == ARCH_MAC99) {
++ keylargo_ata(mio, base_address, 0x1f000, 0x13, 0xb, pic_phandle);
++ keylargo_ata(mio, base_address, 0x20000, 0x14, 0xb, pic_phandle);
+ }
++#if 0
+ /* Timer */
+ {
+ OF_node_t *tmr;
+@@ -2746,10 +2871,11 @@
+ regs, sizeof(OF_regprop_t));
+ OF_node_put(OF_env, tmr);
+ }
++#endif
+ /* VIA-PMU */
+ {
+ /* Controls adb, RTC and power-mgt (forget it !) */
+- OF_node_t *via, *adb, *rtc;
++ OF_node_t *via, *adb;
+ OF_regprop_t regs[1];
+ #if 0 // THIS IS A HACK AND IS COMPLETELY ABSURD !
+ // (but needed has Qemu doesn't emulate via-pmu).
+@@ -2773,14 +2899,21 @@
+ regs[0].size = 0x00002000;
+ OF_property_new(OF_env, via, "reg", regs, sizeof(OF_regprop_t));
+ OF_prop_int_new(OF_env, via, "interrupt-parent", pic_phandle);
++ if (arch == ARCH_HEATHROW) {
++ OF_prop_int_new(OF_env, via, "interrupts", 0x12);
++ } else {
+ regs[0].address = 0x00000019;
+ regs[0].size = 0x00000001;
+ OF_property_new(OF_env, via, "interrupts",
+ regs, sizeof(OF_regprop_t));
++ }
++ /* force usage of OF bus speeds */
++ OF_prop_int_new(OF_env, via, "BusSpeedCorrect", 1);
+ #if 0
+ OF_prop_int_new(OF_env, via, "pmu-version", 0x00D0740C);
+ #endif
+-#if 1
++ {
++ OF_node_t *kbd, *mouse;
+ /* ADB pseudo-device */
+ adb = OF_node_new(OF_env, via, "adb", OF_ADDRESS_NONE);
+ if (adb == NULL) {
+@@ -2797,9 +2930,26 @@
+ OF_prop_int_new(OF_env, adb, "#size-cells", 0);
+ OF_pack_get_path(OF_env, tmp, 512, adb);
+ OF_prop_string_new(OF_env, als, "adb", tmp);
+- /* XXX: add "keyboard@2" and "mouse@3" */
+- OF_node_put(OF_env, adb);
+-#endif
++
++ kbd = OF_node_new(OF_env, adb, "keyboard", 2);
++ if (kbd == NULL) {
++ ERROR("Cannot create 'kbd'\n");
++ goto out;
++ }
++ OF_prop_string_new(OF_env, kbd, "device_type", "keyboard");
++ OF_prop_int_new(OF_env, kbd, "reg", 2);
++
++ mouse = OF_node_new(OF_env, adb, "mouse", 3);
++ if (mouse == NULL) {
++ ERROR("Cannot create 'mouse'\n");
++ goto out;
++ }
++ OF_prop_string_new(OF_env, mouse, "device_type", "mouse");
++ OF_prop_int_new(OF_env, mouse, "reg", 3);
++ OF_prop_int_new(OF_env, mouse, "#buttons", 3);
++ }
++ {
++ OF_node_t *rtc;
+
+ rtc = OF_node_new(OF_env, via, "rtc", OF_ADDRESS_NONE);
+ if (rtc == NULL) {
+@@ -2813,14 +2963,68 @@
+ OF_prop_string_new(OF_env, rtc, "compatible", "rtc");
+ #endif
+ OF_node_put(OF_env, rtc);
+- OF_node_put(OF_env, via);
+ }
++ // OF_node_put(OF_env, via);
++ }
++ {
++ OF_node_t *pmgt;
++ pmgt = OF_node_new(OF_env, mio, "power-mgt", OF_ADDRESS_NONE);
++ OF_prop_string_new(OF_env, pmgt, "device_type", "power-mgt");
++ OF_prop_string_new(OF_env, pmgt, "compatible", "cuda");
++ OF_prop_string_new(OF_env, pmgt, "mgt-kind", "min-consumption-pwm-led");
++ OF_node_put(OF_env, pmgt);
++ }
++
++ if (arch == ARCH_HEATHROW) {
++ /* NVRAM */
++ OF_node_t *nvr;
++ OF_regprop_t regs;
++ nvr = OF_node_new(OF_env, mio, "nvram", 0x60000);
++ OF_prop_string_new(OF_env, nvr, "device_type", "nvram");
++ regs.address = 0x60000;
++ regs.size = 0x00020000;
++ OF_property_new(OF_env, nvr, "reg", &regs, sizeof(regs));
++ OF_prop_int_new(OF_env, nvr, "#bytes", 0x2000);
++ OF_node_put(OF_env, nvr);
++ }
++
+ out:
+ // OF_node_put(OF_env, mio);
+ OF_node_put(OF_env, chs);
+ OF_node_put(OF_env, als);
+ }
+
++void OF_finalize_pci_ide (void *dev,
++ uint32_t io_base0, uint32_t io_base1,
++ uint32_t io_base2, uint32_t io_base3)
++{
++ OF_env_t *OF_env = OF_env_main;
++ OF_node_t *pci_ata = dev;
++ OF_node_t *ata, *atas[2];
++ int i;
++
++ OF_prop_int_new(OF_env, pci_ata, "#address-cells", 1);
++ OF_prop_int_new(OF_env, pci_ata, "#size-cells", 0);
++
++ /* XXX: Darwin handles only one device */
++ for(i = 0; i < 1; i++) {
++ ata = OF_node_new(OF_env, pci_ata, "ata-4", i);
++ if (ata == NULL) {
++ ERROR("Cannot create 'ata-4'\n");
++ return;
++ }
++ OF_prop_string_new(OF_env, ata, "device_type", "ata");
++ OF_prop_string_new(OF_env, ata, "compatible", "cmd646-ata");
++ OF_prop_string_new(OF_env, ata, "model", "ata-4");
++ OF_prop_int_new(OF_env, ata, "#address-cells", 1);
++ OF_prop_int_new(OF_env, ata, "#size-cells", 0);
++ OF_prop_int_new(OF_env, ata, "reg", i);
++ atas[i] = ata;
++ }
++ ide_pci_pc_register(io_base0, io_base1, io_base2, io_base3,
++ atas[0], atas[1]);
++}
++
+ /*****************************************************************************/
+ /* Fake package */
+ static void OF_method_fake (OF_env_t *OF_env)
+@@ -2862,11 +3066,11 @@
+ /* As we get a 1:1 mapping, do nothing */
+ ihandle = popd(OF_env);
+ args = (void *)popd(OF_env);
+- address = popd(OF_env);
+- virt = popd(OF_env);
+- size = popd(OF_env);
+ popd(OF_env);
+- OF_DPRINTF("Translate address %0x %0x %0x %0x\n", ihandle, address,
++ size = popd(OF_env);
++ virt = popd(OF_env);
++ address = popd(OF_env);
++ OF_DPRINTF("Map %0x %0x %0x %0x\n", ihandle, address,
+ virt, size);
+ pushd(OF_env, 0);
+ }
+@@ -3270,7 +3474,7 @@
+ OF_prop_string_new(OF_env, dsk, "device_type", "block");
+ OF_prop_string_new(OF_env, dsk, "category", type);
+ OF_prop_int_new(OF_env, dsk, "device_id", devnum);
+- OF_prop_int_new(OF_env, dsk, "reg", 0);
++ OF_prop_int_new(OF_env, dsk, "reg", devnum);
+ OF_method_new(OF_env, dsk, "open", &OF_blockdev_open);
+ OF_method_new(OF_env, dsk, "seek", &OF_blockdev_seek);
+ OF_method_new(OF_env, dsk, "read", &OF_blockdev_read);
+@@ -3432,7 +3636,8 @@
+ }
+
+ void OF_vga_register (const unsigned char *name, unused uint32_t address,
+- int width, int height, int depth)
++ int width, int height, int depth,
++ unsigned long vga_bios_addr, unsigned long vga_bios_size)
+ {
+ OF_env_t *OF_env;
+ unsigned char tmp[OF_NAMELEN_MAX];
+@@ -3504,6 +3709,18 @@
+ OF_prop_string_new(OF_env, als, "display", tmp);
+ OF_node_put(OF_env, als);
+ /* XXX: may also need read-rectangle */
++
++ if (vga_bios_size >= 8) {
++ const uint8_t *p;
++ int size;
++ /* check the QEMU VGA BIOS header */
++ p = (const uint8_t *)vga_bios_addr;
++ if (p[0] == 'N' && p[1] == 'D' && p[2] == 'R' && p[3] == 'V') {
++ size = *(uint32_t *)(p + 4);
++ OF_property_new(OF_env, disp, "driver,AAPL,MacOS,PowerPC",
++ p + 8, size);
++ }
++ }
+ out:
+ OF_node_put(OF_env, disp);
+ }
+@@ -4451,7 +4668,10 @@
+ break;
+ case 0x233441d3: /* MacOS X 10.2 and OpenDarwin 1.41 */
+ /* Create "memory-map" pseudo device */
+- popd(OF_env);
++ {
++ OF_node_t *map;
++ uint32_t phandle;
++
+ /* Find "/packages" */
+ chs = OF_pack_find_by_name(OF_env, OF_node_root, "/chosen");
+ if (chs == NULL) {
+@@ -4459,10 +4679,6 @@
+ ERROR("Cannot get '/chosen'\n");
+ break;
+ }
+- {
+-#if 1
+- OF_node_t *map;
+- uint32_t phandle;
+ map = OF_node_new(OF_env, chs, "memory-map", OF_ADDRESS_NONE);
+ if (map == NULL) {
+ pushd(OF_env, -1);
+@@ -4473,11 +4689,8 @@
+ OF_node_put(OF_env, map);
+ OF_node_put(OF_env, chs);
+ pushd(OF_env, phandle);
+- }
+-#else
+- pushd(OF_env, 0);
+-#endif
+ pushd(OF_env, 0);
++ }
+ break;
+ case 0x32a2d18e: /* MacOS X 10.2 and OpenDarwin 6.02 */
+ /* Return screen ihandle */
+@@ -4540,9 +4753,10 @@
+ case 0x4ad41f2d:
+ /* Yaboot: wait 10 ms: sure ! */
+ break;
++
+ default:
+ /* ERROR */
+- printf("Script:\n%s\n", FString);
++ printf("Script: len=%d\n%s\n", (int)strlen(FString), FString);
+ printf("Call %0x NOT IMPLEMENTED !\n", crc);
+ bug();
+ break;
+@@ -4581,6 +4795,7 @@
+ {
+ OF_CHECK_NBARGS(OF_env, 0);
+ /* Should free all OF resources */
++ bd_reset_all();
+ #if defined (DEBUG_BIOS)
+ {
+ uint16_t loglevel = 0x02 | 0x10 | 0x80;
+diff -wruN --exclude '*~' --exclude '*.o' --exclude '*.bin' --exclude '*.out' --exclude mkdiff OpenHackWare-release-0.4.org/src/pci.c OpenHackWare-release-0.4/src/pci.c
+--- OpenHackWare-release-0.4.org/src/pci.c 2005-03-31 09:23:33.000000000 +0200
++++ OpenHackWare-release-0.4/src/pci.c 2005-07-07 23:27:37.000000000 +0200
+@@ -99,8 +99,8 @@
+ uint16_t min_grant;
+ uint16_t max_latency;
+ uint8_t irq_line;
+- uint32_t regions[6];
+- uint32_t sizes[6];
++ uint32_t regions[7]; /* the region 6 is the PCI ROM */
++ uint32_t sizes[7];
+ pci_device_t *next;
+ };
+
+@@ -158,6 +158,7 @@
+
+ /* IRQ numbers assigned to PCI IRQs */
+ static uint8_t prep_pci_irqs[4] = { 9, 11, 9, 11 };
++static uint8_t heathrow_pci_irqs[4] = { 0x15, 0x16, 0x17, 0x18 };
+ static uint8_t pmac_pci_irqs[4] = { 8, 9, 10, 11 };
+
+ /* PREP PCI host */
+@@ -399,6 +400,79 @@
+ &uninorth_config_readl, &uninorth_config_writel,
+ };
+
++/* Grackle PCI host */
++
++static uint32_t grackle_cfg_address (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = 0x80000000 | (bus << 16) | (devfn << 8) | (offset & 0xfc);
++ stswap32((uint32_t *)bridge->cfg_addr, addr);
++ return bridge->cfg_data + (offset & 3);
++}
++
++static uint8_t grackle_config_readb (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ return *((uint8_t *)addr);
++}
++
++static void grackle_config_writeb (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset, uint8_t val)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ *((uint8_t *)addr) = val;
++}
++
++static uint16_t grackle_config_readw (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ return ldswap16((uint16_t *)addr);
++}
++
++static void grackle_config_writew (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset, uint16_t val)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ stswap16((uint16_t *)addr, val);
++}
++
++static uint32_t grackle_config_readl (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset)
++{
++ uint32_t addr;
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ return ldswap32((uint32_t *)addr);
++}
++
++static void grackle_config_writel (pci_bridge_t *bridge,
++ uint8_t bus, uint8_t devfn,
++ uint8_t offset, uint32_t val)
++{
++ uint32_t addr;
++
++ addr = grackle_cfg_address(bridge, bus, devfn, offset);
++ stswap32((uint32_t *)addr, val);
++}
++
++static pci_ops_t grackle_pci_ops = {
++ &grackle_config_readb, &grackle_config_writeb,
++ &grackle_config_readw, &grackle_config_writew,
++ &grackle_config_readl, &grackle_config_writel,
++};
++
+ static inline uint8_t pci_config_readb (pci_bridge_t *bridge,
+ uint8_t bus, uint8_t devfn,
+ uint8_t offset)
+@@ -466,12 +540,22 @@
+ },
+ };
+
++static int ide_config_cb2 (pci_device_t *device)
++{
++ OF_finalize_pci_ide(device->common.OF_private,
++ device->regions[0] & ~0x0000000F,
++ device->regions[1] & ~0x0000000F,
++ device->regions[2] & ~0x0000000F,
++ device->regions[3] & ~0x0000000F);
++ return 0;
++}
++
+ static pci_dev_t ide_devices[] = {
+ {
+- 0x8086, 0x0100,
+- NULL, "Qemu IDE", "Qemu IDE", "ide",
++ 0x1095, 0x0646, /* CMD646 IDE controller */
++ "pci-ide", "pci-ata", NULL, NULL,
+ 0, 0, 0,
+- NULL, NULL,
++ ide_config_cb2, NULL,
+ },
+ {
+ 0xFFFF, 0xFFFF,
+@@ -481,7 +565,9 @@
+ },
+ };
+
+-static int ide_config_cb (pci_device_t *device)
++#if 0
++/* should base it on PCI ID, not on arch */
++static int ide_config_cb (unused pci_device_t *device)
+ {
+ printf("Register IDE controller\n");
+ switch (arch) {
+@@ -491,14 +577,8 @@
+ device->common.OF_private);
+ break;
+ default:
+- ide_pci_pc_register(device->regions[0] & ~0x0000000F,
+- device->regions[1] & ~0x0000000F,
+- device->regions[2] & ~0x0000000F,
+- device->regions[3] & ~0x0000000F,
+- device->common.OF_private);
+ break;
+ }
+-
+ return 0;
+ }
+
+@@ -512,16 +592,12 @@
+ device->common.OF_private);
+ break;
+ default:
+- ide_pci_pc_register(device->regions[0] & ~0x0000000F,
+- device->regions[1] & ~0x0000000F,
+- device->regions[2] & ~0x0000000F,
+- device->regions[3] & ~0x0000000F,
+- device->common.OF_private);
+ break;
+ }
+
+ return 0;
+ }
++#endif
+
+ static pci_subclass_t mass_subclass[] = {
+ {
+@@ -530,7 +606,7 @@
+ },
+ {
+ 0x01, "IDE controller", "ide", ide_devices, NULL,
+- &ide_config_cb, NULL,
++ NULL, NULL,
+ },
+ {
+ 0x02, "Floppy disk controller", NULL, NULL, NULL,
+@@ -546,7 +622,7 @@
+ },
+ {
+ 0x05, "ATA controller", "ata", NULL, NULL,
+- &ata_config_cb, NULL,
++ NULL, NULL,
+ },
+ {
+ 0x80, "misc mass-storage controller", NULL, NULL, NULL,
+@@ -646,7 +722,9 @@
+ /* VGA 640x480x16 */
+ OF_vga_register(device->common.device->name,
+ device->regions[0] & ~0x0000000F,
+- vga_width, vga_height, vga_depth);
++ vga_width, vga_height, vga_depth,
++ device->regions[6] & ~0x0000000F,
++ device->sizes[6]);
+ }
+ vga_console_register();
+
+@@ -750,6 +828,13 @@
+ NULL, &PREP_pci_ops,
+ };
+
++pci_dev_t grackle_fake_bridge = {
++ 0xFFFF, 0xFFFF,
++ "pci", "pci-bridge", "DEC,21154", "DEC,21154.pci-bridge",
++ -1, -1, -1,
++ NULL, &grackle_pci_ops,
++};
++
+ static pci_dev_t hbrg_devices[] = {
+ {
+ 0x106B, 0x0020, NULL,
+@@ -758,8 +843,8 @@
+ NULL, &uninorth_agp_fake_bridge,
+ },
+ {
+- 0x106B, 0x001F,
+- NULL, "pci", "AAPL,UniNorth", "uni-north",
++ 0x106B, 0x001F, NULL,
++ "pci", "AAPL,UniNorth", "uni-north",
+ 3, 2, 1,
+ NULL, &uninorth_fake_bridge,
+ },
+@@ -770,10 +855,10 @@
+ NULL, &uninorth_fake_bridge,
+ },
+ {
+- 0x1011, 0x0026, NULL,
+- "pci-bridge", NULL, NULL,
++ 0x1057, 0x0002, "pci",
++ "pci", "MOT,MPC106", "grackle",
+ 3, 2, 1,
+- NULL, &PREP_pci_ops,
++ NULL, &grackle_fake_bridge,
+ },
+ {
+ 0x1057, 0x4801, NULL,
+@@ -1443,7 +1528,14 @@
+ }
+
+ static const pci_dev_t misc_pci[] = {
+- /* Apple Mac-io controller */
++ /* Paddington Mac I/O */
++ {
++ 0x106B, 0x0017,
++ "mac-io", "mac-io", "AAPL,343S1211", "paddington\1heathrow",
++ 1, 1, 1,
++ &macio_config_cb, NULL,
++ },
++ /* KeyLargo Mac I/O */
+ {
+ 0x106B, 0x0022,
+ "mac-io", "mac-io", "AAPL,Keylargo", "Keylargo",
+@@ -1599,7 +1691,7 @@
+ uint8_t min_grant, uint8_t max_latency,
+ int irq_line)
+ {
+- uint32_t cmd;
++ uint32_t cmd, addr;
+ int i;
+
+ device->min_grant = min_grant;
+@@ -1611,22 +1703,28 @@
+ printf("MAP PCI device %d:%d to IRQ %d\n",
+ device->bus, device->devfn, irq_line);
+ }
+- for (i = 0; i < 6; i++) {
++ for (i = 0; i < 7; i++) {
+ if ((device->regions[i] & ~0xF) != 0x00000000 &&
+ (device->regions[i] & ~0xF) != 0xFFFFFFF0) {
+ printf("Map PCI device %d:%d %d to %0x %0x (%s)\n",
+ device->bus, device->devfn, i,
+ device->regions[i], device->sizes[i],
+- device->regions[i] & 0x00000001 ? "I/O" : "memory");
++ (device->regions[i] & 0x00000001) && i != 6 ? "I/O" :
++ "memory");
++ if (i != 6) {
+ cmd = pci_config_readl(bridge, device->bus, device->devfn, 0x04);
+ if (device->regions[i] & 0x00000001)
+ cmd |= 0x00000001;
+ else
+ cmd |= 0x00000002;
+ pci_config_writel(bridge, device->bus, device->devfn, 0x04, cmd);
++ }
++ if (i == 6)
++ addr = 0x30; /* PCI ROM */
++ else
++ addr = 0x10 + (i * sizeof(uint32_t));
+ pci_config_writel(bridge, device->bus, device->devfn,
+- 0x10 + (i * sizeof(uint32_t)),
+- device->regions[i]);
++ addr, device->regions[i]);
+ }
+ }
+ }
+@@ -1900,7 +1998,7 @@
+ goto out;
+ }
+ ret = (pci_u_t *)newd;
+- max_areas = 6;
++ max_areas = 7;
+ /* register PCI device in OF tree */
+ if (bridge->dev.common.type == PCI_FAKE_BRIDGE) {
+ newd->common.OF_private =
+@@ -1927,6 +2025,9 @@
+ /* Handle 64 bits memory mapping */
+ continue;
+ }
++ if (i == 6)
++ addr = 0x30; /* PCI ROM */
++ else
+ addr = 0x10 + (i * sizeof(uint32_t));
+ /* Get region size
+ * Note: we assume it's always a power of 2
+@@ -1935,7 +2036,7 @@
+ smask = pci_config_readl(bridge, bus, devfn, addr);
+ if (smask == 0x00000000 || smask == 0xFFFFFFFF)
+ continue;
+- if (smask & 0x00000001) {
++ if ((smask & 0x00000001) != 0 && i != 6) {
+ /* I/O space */
+ base = io_base;
+ /* Align to a minimum of 256 bytes (arbitrary) */
+@@ -1947,6 +2048,8 @@
+ /* Align to a minimum of 64 kB (arbitrary) */
+ min_align = 1 << 16;
+ amask = 0x0000000F;
++ if (i == 6)
++ smask |= 1; /* PCI ROM enable */
+ }
+ omask = smask & amask;
+ smask &= ~amask;
+@@ -1980,7 +2083,10 @@
+ if (irq_pin > 0) {
+ /* assign the IRQ */
+ irq_pin = ((devfn >> 3) + irq_pin - 1) & 3;
+- if (arch == ARCH_PREP) {
++ /* XXX: should base it on the PCI bridge type, not the arch */
++ switch(arch) {
++ case ARCH_PREP:
++ {
+ int elcr_port, val;
+ irq_line = prep_pci_irqs[irq_pin];
+ /* set the IRQ to level-sensitive */
+@@ -1988,14 +2094,22 @@
+ val = inb(elcr_port);
+ val |= 1 << (irq_line & 7);
+ outb(elcr_port, val);
+- } else {
++ }
++ break;
++ case ARCH_MAC99:
+ irq_line = pmac_pci_irqs[irq_pin];
++ break;
++ case ARCH_HEATHROW:
++ irq_line = heathrow_pci_irqs[irq_pin];
++ break;
++ default:
++ break;
+ }
+ }
+ update_device:
+ pci_update_device(bridge, newd, min_grant, max_latency, irq_line);
+ OF_finalize_pci_device(newd->common.OF_private, bus, devfn,
+- newd->regions, newd->sizes);
++ newd->regions, newd->sizes, irq_line);
+ /* Call special inits if needed */
+ if (dev->config_cb != NULL)
+ (*dev->config_cb)(newd);
+@@ -2049,6 +2163,32 @@
+ case ARCH_CHRP:
+ /* TODO */
+ break;
++ case ARCH_HEATHROW:
++ dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp);
++ if (dev == NULL)
++ return -1;
++ fake_host = pci_add_host(hostp, dev,
++ (0x06 << 24) | (0x00 << 16) | (0xFF << 8));
++ if (fake_host == NULL)
++ return -1;
++ fake_host->dev.common.type = PCI_FAKE_HOST;
++ dev = &grackle_fake_bridge;
++ if (dev == NULL)
++ goto free_fake_host;
++ fake_bridge = pci_add_bridge(fake_host, 0, 0, dev,
++ (0x06 << 24) | (0x04 << 16) | (0xFF << 8),
++ cfg_base, cfg_len,
++ cfg_base + 0x7ec00000,
++ cfg_base + 0x7ee00000,
++ mem_base, mem_len,
++ io_base, io_len,
++ rbase, rlen,
++ 0,
++ &grackle_pci_ops);
++ if (fake_bridge == NULL)
++ goto free_fake_host;
++ fake_bridge->dev.common.type = PCI_FAKE_BRIDGE;
++ break;
+ case ARCH_MAC99:
+ dev = pci_find_device(0x06, 0x00, 0xFF, checkv, checkp);
+ if (dev == NULL)
+@@ -2167,6 +2307,30 @@
+ case ARCH_CHRP:
+ /* TODO */
+ break;
++ case ARCH_HEATHROW:
++ cfg_base = 0x80000000;
++ cfg_len = 0x7f000000;
++ mem_base = 0x80000000;
++ mem_len = 0x01000000;
++ io_base = 0xfe000000;
++ io_len = 0x00800000;
++#if 1
++ rbase = 0xfd000000;
++ rlen = 0x01000000;
++#else
++ rbase = 0x00000000;
++ rlen = 0x01000000;
++#endif
++ if (pci_check_host(&pci_main, cfg_base, cfg_len,
++ mem_base, mem_len, io_base, io_len, rbase, rlen,
++ 0x1057, 0x0002) == 0) {
++ isa_io_base = io_base;
++ busnum++;
++ }
++ for (curh = pci_main; curh->next != NULL; curh = curh->next)
++ continue;
++ pci_check_devices(curh);
++ break;
+ case ARCH_MAC99:
+ /* We are supposed to have 3 host bridges:
+ * - the uninorth AGP bridge at 0xF0000000
diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc
new file mode 100644
index 0000000..ee6f5ae
--- /dev/null
+++ b/pc-bios/openbios-ppc
Binary files differ
diff --git a/pc-bios/openbios-sparc b/pc-bios/openbios-sparc
new file mode 100644
index 0000000..7a729aa
--- /dev/null
+++ b/pc-bios/openbios-sparc
Binary files differ
diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32
new file mode 100644
index 0000000..b2dc5c5
--- /dev/null
+++ b/pc-bios/openbios-sparc32
Binary files differ
diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64
new file mode 100644
index 0000000..70a223d
--- /dev/null
+++ b/pc-bios/openbios-sparc64
Binary files differ
diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile
new file mode 100644
index 0000000..da1a8b8
--- /dev/null
+++ b/pc-bios/optionrom/Makefile
@@ -0,0 +1,31 @@
+all: build-all
+# Dummy command so that make thinks it has done something
+ @true
+
+include ../../config-host.mak
+include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH)/pc-bios/optionrom)
+
+.PHONY : all clean build-all
+
+CFLAGS := -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin
+CFLAGS += -I$(SRC_PATH)
+CFLAGS += $(call cc-option, $(CFLAGS), -fno-stack-protector)
+QEMU_CFLAGS = $(CFLAGS)
+
+build-all: multiboot.bin linuxboot.bin
+
+build-all: extboot.bin vapic.bin
+
+%.img: %.o
+ $(call quiet-command,$(LD) -Ttext 0 -e _start -s -o $@ $<," Building $(TARGET_DIR)$@")
+
+%.raw: %.img
+ $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@")
+
+%.bin: %.raw
+ $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/signrom.sh $< $@," Signing $(TARGET_DIR)$@")
+
+clean:
+ rm -f *.o *.d *.raw *.img *.bin *~
diff --git a/pc-bios/optionrom/extboot.S b/pc-bios/optionrom/extboot.S
new file mode 100644
index 0000000..db6c2b6
--- /dev/null
+++ b/pc-bios/optionrom/extboot.S
@@ -0,0 +1,691 @@
+/*
+ * Extended Boot Option ROM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ * Authors: Anthony Liguori <aliguori@us.ibm.com>
+ */
+
+#define OLD_INT19 (0x80 * 4) /* re-use INT 0x80 BASIC vector */
+#define OLD_INT13 (0x81 * 4) /* re-use INT 0x81 BASIC vector */
+
+.code16
+.text
+ .global _start
+_start:
+ .short 0xaa55
+ .byte (_end - _start) / 512
+ push %eax
+ push %ds
+
+ /* setup ds so we can access the IVT */
+ xor %ax, %ax
+ mov %ax, %ds
+
+ /* there is one more bootable HD */
+ incb 0x0475
+
+ /* save old int 19 */
+ mov (0x19*4), %eax
+ mov %eax, (OLD_INT19)
+
+ /* install out int 19 handler */
+ movw $int19_handler, (0x19*4)
+ mov %cs, (0x19*4+2)
+
+ pop %ds
+ pop %eax
+ lret
+
+int19_handler:
+ push %eax /* reserve space for lret */
+ push %eax
+ push %bx
+ push %cx
+ push %dx
+ push %ds
+
+ /* setup ds to access IVT */
+ xor %ax, %ax
+ mov %ax, %ds
+
+ /* save old int 13 to int 2c */
+ mov (0x13*4), %eax
+ mov %eax, (OLD_INT13)
+
+ /* install our int 13 handler */
+ movw $int13_handler, (0x13*4)
+ mov %cs, (0x13*4+2)
+
+ /* restore previous int $0x19 handler */
+ mov (OLD_INT19),%eax
+ mov %eax,(0x19*4)
+
+ /* write old handler as return address onto stack */
+ push %bp
+ mov %sp, %bp
+ mov %eax, 14(%bp)
+ pop %bp
+
+ pop %ds
+ pop %dx
+ pop %cx
+ pop %bx
+ pop %eax
+ lret
+
+#define FLAGS_CF 0x01
+
+/* The two macro below clear/set the carry flag to indicate the status
+ * of the interrupt execution. It is not enough to issue a clc/stc instruction,
+ * since the value of the flags register will be overwritten by whatever is
+ * in the stack frame
+ */
+.macro clc_stack
+ push %bp
+ mov %sp, %bp
+ /* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */
+ and $(~FLAGS_CF), 8(%bp)
+ pop %bp
+.endm
+
+.macro stc_stack
+ push %bp
+ /* 8 = 2 (bp, just pushed) + 2 (ip) + 3 (real mode interrupt frame) */
+ or $(FLAGS_CF), 8(%bp)
+ pop %bp
+.endm
+
+/* we clobber %bx */
+.macro alloca size
+ push %ds
+ push %bp
+ mov %sp, %bp /* remember the current stack position */
+
+ mov %ss, %bx
+ mov %bx, %ds
+
+ sub \size, %sp
+ and $(~0x0F), %sp
+ mov %sp, %bx
+
+ push %bp
+ mov 0(%bp), %bp
+.endm
+
+/* we clobber %bp */
+.macro allocbpa size
+ mov %sp, %bp /* remember the current stack position */
+ sub \size, %sp
+ and $(~0x0F), %sp
+ push %bp
+ mov %sp, %bp
+ add $2, %bp
+.endm
+
+.macro freea
+ pop %sp
+ add $2, %sp
+ pop %ds
+.endm
+
+.macro freebpa
+ pop %sp
+.endm
+
+.macro dump reg
+ push %ax
+ push %dx
+
+ mov \reg, %ax
+ mov $0x406, %dx
+ outw %ax, %dx
+
+ pop %dx
+ pop %ax
+.endm
+
+.macro callout value
+ push %bp
+ push %bx
+ mov %sp, %bp
+ alloca $16
+ push %ax
+ push %dx
+
+ mov %ax, 0(%bx) /* ax */
+ mov 0(%bp), %ax /* bx */
+ mov %ax, 2(%bx)
+ mov %cx, 4(%bx) /* cx */
+ mov %dx, 6(%bx) /* dx */
+ mov %si, 8(%bx) /* si */
+ mov %ds, 10(%bx) /* ds */
+ mov %es, 12(%bx) /* ds */
+ movw \value, 14(%bx) /* value */
+
+ mov %bx, %ax
+ shr $4, %ax
+ mov %ds, %dx
+ add %dx, %ax
+
+ mov $0x407, %dx
+ outw %ax, %dx
+
+ pop %dx
+ pop %ax
+ freea
+ pop %bx
+ pop %bp
+.endm
+
+send_command:
+ push %bp
+ mov %sp, %bp
+ push %ax
+ push %bx
+ push %dx
+
+ mov 4(%bp), %ax
+ shr $4, %ax
+ and $0x0FFF, %ax
+ mov %ss, %bx
+ add %bx, %ax
+
+ mov $0x405, %dx
+ outw %ax, %dx
+
+ pop %dx
+ pop %bx
+ pop %ax
+ pop %bp
+
+ push %ax
+ mov 2(%bx), %ax
+ pop %ax
+
+ ret
+
+add32: /* lo, hi, lo, hi */
+ push %bp
+ mov %sp, %bp
+
+ movw 4(%bp), %cx /* hi */
+ movw 6(%bp), %dx /* lo */
+
+ add 10(%bp), %dx
+ jnc 1f
+ add $1, %cx
+1: add 8(%bp), %cx
+
+ pop %bp
+ ret
+
+mul32: /* lo, hi, lo, hi */
+ /* 10(%bp), 8(%bp), 6(%bp), 4(%bp) */
+ push %bp
+ mov %sp, %bp
+ push %ax
+ push %bx
+
+ xor %cx, %cx
+ xor %dx, %dx
+
+ /* for (i = 0; i < 16;) */
+ xor %bx, %bx
+0:
+ cmp $16, %bx
+ jge 2f
+
+ mov 6(%bp), %ax
+ and $1, %ax
+ cmp $1, %ax
+ jne 1f
+ push 10(%bp)
+ push 8(%bp)
+ push %dx
+ push %cx
+ call add32
+ add $8, %sp
+1:
+ shlw $1, 8(%bp)
+ movw 10(%bp), %ax
+ and $0x8000, %ax
+ cmp $0x8000, %ax
+ jne 1f
+ orw $1, 8(%bp)
+1:
+ shlw $1, 10(%bp)
+ shrw $1, 6(%bp)
+
+ /* i++) { */
+ add $1, %bx
+ jmp 0b
+
+2:
+ pop %bx
+ pop %ax
+ pop %bp
+ ret
+
+disk_reset:
+ movb $0, %ah
+ clc_stack
+ ret
+
+/* this really should be a function, not a macro but i'm lazy */
+.macro read_write_disk_sectors cmd
+ push %ax
+ push %bx
+ push %cx
+ push %dx
+ push %si
+
+ push %bp
+ sub $10, %sp
+ mov %sp, %bp
+
+ /* save nb_sectors */
+ mov %al, 6(%bp)
+ movb $0, 7(%bp)
+
+ /* save buffer */
+ mov %bx, 8(%bp)
+
+ /* cylinders */
+ xor %ax, %ax
+ mov %cl, %al
+ shl $2, %ax
+ and $0x300, %ax
+ mov %ch, %al
+ mov %ax, 0(%bp)
+
+ /* heads */
+ xor %ax, %ax
+ mov %dh, %al
+ mov %ax, 2(%bp)
+
+ /* sectors - 1 */
+ xor %ax, %ax
+ mov %cl, %al
+ and $0x3F, %al
+ sub $1, %ax
+ mov %ax, 4(%bp)
+
+ alloca $16
+
+ movw $0, 0(%bx) /* read c,h,s */
+ push %bx
+ call send_command
+ add $2, %sp
+
+ mov 6(%bx), %ax /* total_sectors */
+ mov 2(%bp), %si /* *= heads */
+ mul %si
+ add 4(%bp), %ax /* += sectors - 1 */
+
+ push 4(%bx) /* total_heads */
+ push $0
+ push 6(%bx) /* total_sectors */
+ push $0
+ call mul32
+ add $8, %sp
+
+ push 0(%bp) /* cylinders */
+ push $0
+ push %dx
+ push %cx
+ call mul32
+ add $8, %sp
+
+ add %ax, %dx
+ jnc 1f
+ add $1, %cx
+1:
+ freea
+
+ alloca $16
+
+ movw \cmd, 0(%bx) /* read */
+ movw 6(%bp), %ax /* nb_sectors */
+ movw %ax, 2(%bx)
+ movw %es, 4(%bx) /* segment */
+ movw 8(%bp), %ax /* offset */
+ mov %ax, 6(%bx)
+ movw %dx, 8(%bx) /* sector */
+ movw %cx, 10(%bx)
+ movw $0, 12(%bx)
+ movw $0, 14(%bx)
+
+ push %bx
+ call send_command
+ add $2, %sp
+
+ freea
+
+ add $10, %sp
+ pop %bp
+
+ pop %si
+ pop %dx
+ pop %cx
+ pop %bx
+ pop %ax
+
+ mov $0, %ah
+ clc_stack
+ ret
+.endm
+
+read_disk_sectors:
+ read_write_disk_sectors $0x01
+
+write_disk_sectors:
+ read_write_disk_sectors $0x02
+
+read_disk_drive_parameters:
+ push %bx
+
+ /* allocate memory for packet, pointer gets returned in bx */
+ alloca $16
+
+ /* issue command */
+ movw $0, 0(%bx) /* cmd = 0, read c,h,s */
+ push %bx
+ call send_command
+ add $2, %sp
+
+ /* normalize sector value */
+ movb 6(%bx), %cl
+ andb $0x3F, %cl
+ movb %cl, 6(%bx)
+
+ /* normalize cylinders */
+ subw $2, 2(%bx)
+
+ /* normalize heads */
+ subw $1, 4(%bx)
+
+ /* return code */
+ mov $0, %ah
+
+ /* cylinders */
+ movb 2(%bx), %ch
+ movb 3(%bx), %cl
+ shlb $6, %cl
+ andb $0xC0, %cl
+
+ /* sectors */
+ orb 6(%bx), %cl
+
+ /* heads */
+ movb 4(%bx), %dh
+
+ /* drives */
+ movb $1, %dl
+
+ /* status */
+ mov $0, %ah
+
+ freea
+
+ pop %bx
+
+ /* do this last since it's the most sensitive */
+ clc_stack
+ ret
+
+alternate_disk_reset:
+ movb $0, %ah
+ clc_stack
+ ret
+
+read_disk_drive_size:
+ push %bx
+ alloca $16
+
+ movw $0, 0(%bx) /* cmd = 0, read c,h,s */
+ push %bx
+ call send_command
+ add $2, %sp
+
+ /* cylinders - 1 to cx:dx */
+ mov 2(%bx), %dx
+ xor %cx, %cx
+ sub $1, %dx
+
+ /* heads */
+ push 4(%bx)
+ push $0
+ push %dx
+ push %cx
+ call mul32
+ add $8, %sp
+
+ /* sectors */
+ push 6(%bx)
+ push $0
+ push %dx
+ push %cx
+ call mul32
+ add $8, %sp
+
+ /* status */
+ mov $3, %ah
+
+ freea
+ pop %bx
+
+ clc_stack
+ ret
+
+check_if_extensions_present:
+ mov $0x30, %ah
+ mov $0xAA55, %bx
+ mov $0x07, %cx
+ clc_stack
+ ret
+
+.macro extended_read_write_sectors cmd
+ cmpb $10, 0(%si)
+ jg 1f
+ mov $1, %ah
+ stc_stack
+ ret
+1:
+ push %ax
+ push %bp
+ allocbpa $16
+
+ movw \cmd, 0(%bp) /* read */
+ movw 2(%si), %ax /* nb_sectors */
+ movw %ax, 2(%bp)
+ movw 4(%si), %ax /* offset */
+ movw %ax, 6(%bp)
+ movw 6(%si), %ax /* segment */
+ movw %ax, 4(%bp)
+ movw 8(%si), %ax /* block */
+ movw %ax, 8(%bp)
+ movw 10(%si), %ax
+ movw %ax, 10(%bp)
+ movw 12(%si), %ax
+ movw %ax, 12(%bp)
+ movw 14(%si), %ax
+ movw %ax, 14(%bp)
+
+ push %bp
+ call send_command
+ add $2, %sp
+
+ freebpa
+ pop %bp
+ pop %ax
+
+ mov $0, %ah
+ clc_stack
+ ret
+.endm
+
+extended_read_sectors:
+ extended_read_write_sectors $0x01
+
+extended_write_sectors:
+ extended_read_write_sectors $0x02
+
+get_extended_drive_parameters:
+ push %ax
+ push %bp
+ push %cx
+ push %dx
+
+ allocbpa $16
+
+ movw $0, 0(%bp) /* read c,h,s */
+ push %bp
+ call send_command
+ add $2, %sp
+
+ /* write size */
+ movw $26, 0(%si)
+
+ /* set flags to 2 */
+ movw $2, 2(%si)
+
+ /* cylinders */
+ mov 2(%bp), %ax
+ mov %ax, 4(%si)
+ xor %ax, %ax
+ mov %ax, 6(%si)
+
+ /* heads */
+ mov 4(%bp), %ax
+ mov %ax, 8(%si)
+ xor %ax, %ax
+ mov %ax, 10(%si)
+
+ /* sectors */
+ mov 6(%bp), %ax
+ mov %ax, 12(%si)
+ xor %ax, %ax
+ mov %ax, 14(%si)
+
+ /* set total number of sectors */
+ mov 8(%bp), %ax
+ mov %ax, 16(%si)
+ mov 10(%bp), %ax
+ mov %ax, 18(%si)
+ mov 12(%bp), %ax
+ mov %ax, 20(%si)
+ mov 14(%bp), %ax
+ mov %ax, 22(%si)
+
+ /* number of bytes per sector */
+ movw $512, 24(%si)
+
+ freebpa
+
+ pop %dx
+ pop %cx
+ pop %bp
+ pop %ax
+
+ mov $0, %ah
+ clc_stack
+ ret
+
+terminate_disk_emulation:
+ mov $1, %ah
+ stc_stack
+ ret
+
+int13_handler:
+ cmp $0x80, %dl
+ je 1f
+
+ /* write old handler as return address onto stack */
+ push %eax
+ push %eax
+ push %ds
+ push %bp
+ mov %sp, %bp
+ xor %ax, %ax
+ mov %ax, %ds
+ mov (OLD_INT13), %eax
+ mov %eax, 8(%bp)
+ pop %bp
+ pop %ds
+ pop %eax
+ lret
+1:
+ cmp $0x0, %ah
+ jne 1f
+ call disk_reset
+ iret
+1:
+ cmp $0x2, %ah
+ jne 1f
+ call read_disk_sectors
+ iret
+1:
+ cmp $0x8, %ah
+ jne 1f
+ call read_disk_drive_parameters
+ iret
+1:
+ cmp $0x15, %ah
+ jne 1f
+ call read_disk_drive_size
+ iret
+1:
+ cmp $0x41, %ah
+ jne 1f
+ call check_if_extensions_present
+ iret
+1:
+ cmp $0x42, %ah
+ jne 1f
+ call extended_read_sectors
+ iret
+1:
+ cmp $0x48, %ah
+ jne 1f
+ call get_extended_drive_parameters
+ iret
+1:
+ cmp $0x4b, %ah
+ jne 1f
+ call terminate_disk_emulation
+ iret
+1:
+ cmp $0x0d, %ah
+ jne 1f
+ call alternate_disk_reset
+ iret
+1:
+ cmp $0x03, %ah
+ jne 1f
+ call write_disk_sectors
+ iret
+1:
+ cmp $0x43, %ah
+ jne 1f
+ call extended_write_sectors
+ iret
+1:
+ int $0x18 /* boot failed */
+ iret
+
+.align 512, 0
+_end:
diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S
new file mode 100644
index 0000000..c109363
--- /dev/null
+++ b/pc-bios/optionrom/linuxboot.S
@@ -0,0 +1,137 @@
+/*
+ * Linux Boot Option ROM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * Copyright Novell Inc, 2009
+ * Authors: Alexander Graf <agraf@suse.de>
+ *
+ * Based on code in hw/pc.c.
+ */
+
+#include "optionrom.h"
+
+BOOT_ROM_START
+
+run_linuxboot:
+
+ cli
+ cld
+
+ jmp copy_kernel
+boot_kernel:
+
+ read_fw FW_CFG_SETUP_ADDR
+
+ mov %eax, %ebx
+ shr $4, %ebx
+
+ /* All segments contain real_addr */
+ mov %bx, %ds
+ mov %bx, %es
+ mov %bx, %fs
+ mov %bx, %gs
+ mov %bx, %ss
+
+ /* CX = CS we want to jump to */
+ add $0x20, %bx
+ mov %bx, %cx
+
+ /* SP = cmdline_addr-real_addr-16 */
+ read_fw FW_CFG_CMDLINE_ADDR
+ mov %eax, %ebx
+ read_fw FW_CFG_SETUP_ADDR
+ sub %eax, %ebx
+ sub $16, %ebx
+ mov %ebx, %esp
+
+ /* Build indirect lret descriptor */
+ pushw %cx /* CS */
+ xor %ax, %ax
+ pushw %ax /* IP = 0 */
+
+ /* Clear registers */
+ xor %eax, %eax
+ xor %ebx, %ebx
+ xor %ecx, %ecx
+ xor %edx, %edx
+ xor %edi, %edi
+ xor %ebp, %ebp
+
+ /* Jump to Linux */
+ lret
+
+
+copy_kernel:
+
+ /* We need to load the kernel into memory we can't access in 16 bit
+ mode, so let's get into 32 bit mode, write the kernel and jump
+ back again. */
+
+ /* Reserve space on the stack for our GDT descriptor. */
+ mov %esp, %ebp
+ sub $16, %esp
+
+ /* Now create the GDT descriptor */
+ movw $((3 * 8) - 1), -16(%bp)
+ mov %cs, %eax
+ movzwl %ax, %eax
+ shl $4, %eax
+ addl $gdt, %eax
+ movl %eax, -14(%bp)
+
+ /* And load the GDT */
+ data32 lgdt -16(%bp)
+ mov %ebp, %esp
+
+ /* Get us to protected mode now */
+ mov $1, %eax
+ mov %eax, %cr0
+
+ /* So we can set ES to a 32-bit segment */
+ mov $0x10, %eax
+ mov %eax, %es
+
+ /* We're now running in 16-bit CS, but 32-bit ES! */
+
+ /* Load kernel and initrd */
+ read_fw_blob_addr32(FW_CFG_KERNEL)
+ read_fw_blob_addr32(FW_CFG_INITRD)
+ read_fw_blob_addr32(FW_CFG_CMDLINE)
+ read_fw_blob_addr32(FW_CFG_SETUP)
+
+ /* And now jump into Linux! */
+ mov $0, %eax
+ mov %eax, %cr0
+
+ /* ES = CS */
+ mov %cs, %ax
+ mov %ax, %es
+
+ jmp boot_kernel
+
+/* Variables */
+
+.align 4, 0
+gdt:
+ /* 0x00 */
+.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+ /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */
+.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00
+
+ /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */
+.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00
+
+BOOT_ROM_END
diff --git a/pc-bios/optionrom/multiboot.S b/pc-bios/optionrom/multiboot.S
new file mode 100644
index 0000000..9131837
--- /dev/null
+++ b/pc-bios/optionrom/multiboot.S
@@ -0,0 +1,184 @@
+/*
+ * Multiboot Option ROM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * Copyright Novell Inc, 2009
+ * Authors: Alexander Graf <agraf@suse.de>
+ */
+
+#include "optionrom.h"
+
+#define MULTIBOOT_MAGIC 0x2badb002
+
+#define GS_PROT_JUMP 0
+#define GS_GDT_DESC 6
+
+
+BOOT_ROM_START
+
+run_multiboot:
+
+ cli
+ cld
+
+ mov %cs, %eax
+ shl $0x4, %eax
+
+ /* set up a long jump descriptor that is PC relative */
+
+ /* move stack memory to %gs */
+ mov %ss, %ecx
+ shl $0x4, %ecx
+ mov %esp, %ebx
+ add %ebx, %ecx
+ sub $0x20, %ecx
+ sub $0x30, %esp
+ shr $0x4, %ecx
+ mov %cx, %gs
+
+ /* now push the indirect jump decriptor there */
+ mov (prot_jump), %ebx
+ add %eax, %ebx
+ movl %ebx, %gs:GS_PROT_JUMP
+ mov $8, %bx
+ movw %bx, %gs:GS_PROT_JUMP + 4
+
+ /* fix the gdt descriptor to be PC relative */
+ movw (gdt_desc), %bx
+ movw %bx, %gs:GS_GDT_DESC
+ movl (gdt_desc+2), %ebx
+ add %eax, %ebx
+ movl %ebx, %gs:GS_GDT_DESC + 2
+
+ xor %eax, %eax
+ mov %eax, %es
+
+ /* Read the bootinfo struct into RAM */
+ read_fw_blob(FW_CFG_INITRD)
+
+ /* FS = bootinfo_struct */
+ read_fw FW_CFG_INITRD_ADDR
+ shr $4, %eax
+ mov %ax, %fs
+
+ /* ES = mmap_addr */
+ mov %fs:48, %eax
+ shr $4, %eax
+ mov %ax, %es
+
+ /* Initialize multiboot mmap structs using int 0x15(e820) */
+ xor %ebx, %ebx
+ /* mmap start after first size */
+ movl $4, %edi
+
+mmap_loop:
+ /* entry size (mmap struct) & max buffer size (int15) */
+ movl $20, %ecx
+ /* store entry size */
+ /* old as(1) doesn't like this insn so emit the bytes instead:
+ movl %ecx, %es:-4(%edi)
+ */
+ .dc.b 0x26,0x67,0x66,0x89,0x4f,0xfc
+ /* e820 */
+ movl $0x0000e820, %eax
+ /* 'SMAP' magic */
+ movl $0x534d4150, %edx
+ int $0x15
+
+mmap_check_entry:
+ /* last entry? then we're done */
+ jb mmap_done
+ and %bx, %bx
+ jz mmap_done
+ /* valid entry, so let's loop on */
+
+mmap_store_entry:
+ /* %ax = entry_number * 24 */
+ mov $24, %ax
+ mul %bx
+ mov %ax, %di
+ movw %di, %fs:0x2c
+ /* %di = 4 + (entry_number * 24) */
+ add $4, %di
+ jmp mmap_loop
+
+mmap_done:
+real_to_prot:
+ /* Load the GDT before going into protected mode */
+lgdt:
+ data32 lgdt %gs:GS_GDT_DESC
+
+ /* get us to protected mode now */
+ movl $1, %eax
+ movl %eax, %cr0
+
+ /* the LJMP sets CS for us and gets us to 32-bit */
+ljmp:
+ data32 ljmp *%gs:GS_PROT_JUMP
+
+prot_mode:
+.code32
+
+ /* initialize all other segments */
+ movl $0x10, %eax
+ movl %eax, %ss
+ movl %eax, %ds
+ movl %eax, %es
+ movl %eax, %fs
+ movl %eax, %gs
+
+ /* Read the kernel and modules into RAM */
+ read_fw_blob(FW_CFG_KERNEL)
+
+ /* Jump off to the kernel */
+ read_fw FW_CFG_KERNEL_ENTRY
+ mov %eax, %ecx
+
+ /* EBX contains a pointer to the bootinfo struct */
+ read_fw FW_CFG_INITRD_ADDR
+ movl %eax, %ebx
+
+ /* EAX has to contain the magic */
+ movl $MULTIBOOT_MAGIC, %eax
+ljmp2:
+ jmp *%ecx
+
+/* Variables */
+.align 4, 0
+prot_jump: .long prot_mode
+ .short 8
+
+.align 4, 0
+gdt:
+ /* 0x00 */
+.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+ /* 0x08: code segment (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) */
+.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00
+
+ /* 0x10: data segment (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) */
+.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00
+
+ /* 0x18: code segment (base=0, limit=0x0ffff, type=16bit code exec/read/conf, DPL=0, 1b) */
+.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00
+
+ /* 0x20: data segment (base=0, limit=0x0ffff, type=16bit data read/write, DPL=0, 1b) */
+.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00
+
+gdt_desc:
+.short (5 * 8) - 1
+.long gdt
+
+BOOT_ROM_END
diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h
new file mode 100644
index 0000000..fbdd48a
--- /dev/null
+++ b/pc-bios/optionrom/optionrom.h
@@ -0,0 +1,126 @@
+/*
+ * Common Option ROM Functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ * Copyright Novell Inc, 2009
+ * Authors: Alexander Graf <agraf@suse.de>
+ */
+
+
+#define NO_QEMU_PROTOS
+#include "../../hw/fw_cfg.h"
+
+#define BIOS_CFG_IOPORT_CFG 0x510
+#define BIOS_CFG_IOPORT_DATA 0x511
+
+/* Break the translation block flow so -d cpu shows us values */
+#define DEBUG_HERE \
+ jmp 1f; \
+ 1:
+
+/*
+ * Read a variable from the fw_cfg device.
+ * Clobbers: %edx
+ * Out: %eax
+ */
+.macro read_fw VAR
+ mov $\VAR, %ax
+ mov $BIOS_CFG_IOPORT_CFG, %dx
+ outw %ax, (%dx)
+ mov $BIOS_CFG_IOPORT_DATA, %dx
+ inb (%dx), %al
+ shl $8, %eax
+ inb (%dx), %al
+ shl $8, %eax
+ inb (%dx), %al
+ shl $8, %eax
+ inb (%dx), %al
+ bswap %eax
+.endm
+
+#define read_fw_blob_pre(var) \
+ read_fw var ## _ADDR; \
+ mov %eax, %edi; \
+ read_fw var ## _SIZE; \
+ mov %eax, %ecx; \
+ mov $var ## _DATA, %ax; \
+ mov $BIOS_CFG_IOPORT_CFG, %edx; \
+ outw %ax, (%dx); \
+ mov $BIOS_CFG_IOPORT_DATA, %dx; \
+ cld
+
+/*
+ * Read a blob from the fw_cfg device.
+ * Requires _ADDR, _SIZE and _DATA values for the parameter.
+ *
+ * Clobbers: %eax, %edx, %es, %ecx, %edi
+ */
+#define read_fw_blob(var) \
+ read_fw_blob_pre(var); \
+ /* old as(1) doesn't like this insn so emit the bytes instead: \
+ rep insb (%dx), %es:(%edi); \
+ */ \
+ .dc.b 0xf3,0x6c
+
+/*
+ * Read a blob from the fw_cfg device in forced addr32 mode.
+ * Requires _ADDR, _SIZE and _DATA values for the parameter.
+ *
+ * Clobbers: %eax, %edx, %es, %ecx, %edi
+ */
+#define read_fw_blob_addr32(var) \
+ read_fw_blob_pre(var); \
+ /* old as(1) doesn't like this insn so emit the bytes instead: \
+ addr32 rep insb (%dx), %es:(%edi); \
+ */ \
+ .dc.b 0x67,0xf3,0x6c
+
+#define OPTION_ROM_START \
+ .code16; \
+ .text; \
+ .global _start; \
+ _start:; \
+ .short 0xaa55; \
+ .byte (_end - _start) / 512;
+
+#define BOOT_ROM_START \
+ OPTION_ROM_START \
+ push %eax; \
+ push %ds; \
+ \
+ /* setup ds so we can access the IVT */ \
+ xor %ax, %ax; \
+ mov %ax, %ds; \
+ \
+ /* install our int 19 handler */ \
+ movw $int19_handler, (0x19*4); \
+ mov %cs, (0x19*4+2); \
+ \
+ pop %ds; \
+ pop %eax; \
+ lret; \
+ \
+ int19_handler:; \
+ /* DS = CS */ \
+ movw %cs, %ax; \
+ movw %ax, %ds;
+
+#define OPTION_ROM_END \
+ .align 512, 0; \
+ _end:
+
+#define BOOT_ROM_END \
+ OPTION_ROM_END
+
diff --git a/pc-bios/optionrom/vapic.S b/pc-bios/optionrom/vapic.S
new file mode 100644
index 0000000..97fa19c
--- /dev/null
+++ b/pc-bios/optionrom/vapic.S
@@ -0,0 +1,330 @@
+ .text 0
+ .code16
+.global _start
+_start:
+ .short 0xaa55
+ .byte (_end - _start) / 512
+ # clear vapic area: firmware load using rep insb may cause
+ # stale tpr/isr/irr data to corrupt the vapic area.
+ push %es
+ push %cs
+ pop %es
+ xor %ax, %ax
+ mov $vapic_size/2, %cx
+ lea vapic, %di
+ cld
+ rep stosw
+ pop %es
+ mov $vapic_base, %ax
+ out %ax, $0x7e
+ lret
+
+ .code32
+vapic_size = 2*4096
+
+.macro fixup delta=-4
+777:
+ .text 1
+ .long 777b + \delta - vapic_base
+ .text 0
+.endm
+
+.macro reenable_vtpr
+ out %al, $0x7e
+.endm
+
+.text 1
+ fixup_start = .
+.text 0
+
+.align 16
+
+vapic_base:
+ .ascii "kvm aPiC"
+
+ /* relocation data */
+ .long vapic_base ; fixup
+ .long fixup_start ; fixup
+ .long fixup_end ; fixup
+
+ .long vapic ; fixup
+ .long vapic_size
+vcpu_shift:
+ .long 0
+real_tpr:
+ .long 0
+ .long up_set_tpr ; fixup
+ .long up_set_tpr_eax ; fixup
+ .long up_get_tpr_eax ; fixup
+ .long up_get_tpr_ecx ; fixup
+ .long up_get_tpr_edx ; fixup
+ .long up_get_tpr_ebx ; fixup
+ .long 0 /* esp. won't work. */
+ .long up_get_tpr_ebp ; fixup
+ .long up_get_tpr_esi ; fixup
+ .long up_get_tpr_edi ; fixup
+ .long up_get_tpr_stack ; fixup
+ .long mp_set_tpr ; fixup
+ .long mp_set_tpr_eax ; fixup
+ .long mp_get_tpr_eax ; fixup
+ .long mp_get_tpr_ecx ; fixup
+ .long mp_get_tpr_edx ; fixup
+ .long mp_get_tpr_ebx ; fixup
+ .long 0 /* esp. won't work. */
+ .long mp_get_tpr_ebp ; fixup
+ .long mp_get_tpr_esi ; fixup
+ .long mp_get_tpr_edi ; fixup
+ .long mp_get_tpr_stack ; fixup
+
+.macro kvm_hypercall
+ .byte 0x0f, 0x01, 0xc1
+.endm
+
+kvm_hypercall_vapic_poll_irq = 1
+
+pcr_cpu = 0x51
+
+.align 64
+
+mp_get_tpr_eax:
+ pushf
+ cli
+ reenable_vtpr
+ push %ecx
+
+ fs/movzbl pcr_cpu, %eax
+
+ mov vcpu_shift, %ecx ; fixup
+ shl %cl, %eax
+ testb $1, vapic+4(%eax) ; fixup delta=-5
+ jz mp_get_tpr_bad
+ movzbl vapic(%eax), %eax ; fixup
+
+mp_get_tpr_out:
+ pop %ecx
+ popf
+ ret
+
+mp_get_tpr_bad:
+ mov real_tpr, %eax ; fixup
+ mov (%eax), %eax
+ jmp mp_get_tpr_out
+
+mp_get_tpr_ebx:
+ mov %eax, %ebx
+ call mp_get_tpr_eax
+ xchg %eax, %ebx
+ ret
+
+mp_get_tpr_ecx:
+ mov %eax, %ecx
+ call mp_get_tpr_eax
+ xchg %eax, %ecx
+ ret
+
+mp_get_tpr_edx:
+ mov %eax, %edx
+ call mp_get_tpr_eax
+ xchg %eax, %edx
+ ret
+
+mp_get_tpr_esi:
+ mov %eax, %esi
+ call mp_get_tpr_eax
+ xchg %eax, %esi
+ ret
+
+mp_get_tpr_edi:
+ mov %eax, %edi
+ call mp_get_tpr_edi
+ xchg %eax, %edi
+ ret
+
+mp_get_tpr_ebp:
+ mov %eax, %ebp
+ call mp_get_tpr_eax
+ xchg %eax, %ebp
+ ret
+
+mp_get_tpr_stack:
+ call mp_get_tpr_eax
+ xchg %eax, 4(%esp)
+ ret
+
+mp_set_tpr_eax:
+ push %eax
+ call mp_set_tpr
+ ret
+
+mp_set_tpr:
+ pushf
+ push %eax
+ push %ecx
+ push %edx
+ push %ebx
+ cli
+ reenable_vtpr
+
+mp_set_tpr_failed:
+ fs/movzbl pcr_cpu, %edx
+
+ mov vcpu_shift, %ecx ; fixup
+ shl %cl, %edx
+
+ testb $1, vapic+4(%edx) ; fixup delta=-5
+ jz mp_set_tpr_bad
+
+ mov vapic(%edx), %eax ; fixup
+
+ mov %eax, %ebx
+ mov 24(%esp), %bl
+
+ /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
+
+ lock cmpxchg %ebx, vapic(%edx) ; fixup
+ jnz mp_set_tpr_failed
+
+ /* compute ppr */
+ cmp %bh, %bl
+ jae mp_tpr_is_bigger
+mp_isr_is_bigger:
+ mov %bh, %bl
+mp_tpr_is_bigger:
+ /* %bl = ppr */
+ mov %bl, %ch /* ch = ppr */
+ rol $8, %ebx
+ /* now: %bl = irr, %bh = ppr */
+ cmp %bh, %bl
+ ja mp_set_tpr_poll_irq
+
+mp_set_tpr_out:
+ pop %ebx
+ pop %edx
+ pop %ecx
+ pop %eax
+ popf
+ ret $4
+
+mp_set_tpr_poll_irq:
+ mov $kvm_hypercall_vapic_poll_irq, %eax
+ kvm_hypercall
+ jmp mp_set_tpr_out
+
+mp_set_tpr_bad:
+ mov 24(%esp), %ecx
+ mov real_tpr, %eax ; fixup
+ mov %ecx, (%eax)
+ jmp mp_set_tpr_out
+
+up_get_tpr_eax:
+ reenable_vtpr
+ movzbl vapic, %eax ; fixup
+ ret
+
+up_get_tpr_ebx:
+ reenable_vtpr
+ movzbl vapic, %ebx ; fixup
+ ret
+
+up_get_tpr_ecx:
+ reenable_vtpr
+ movzbl vapic, %ecx ; fixup
+ ret
+
+up_get_tpr_edx:
+ reenable_vtpr
+ movzbl vapic, %edx ; fixup
+ ret
+
+up_get_tpr_esi:
+ reenable_vtpr
+ movzbl vapic, %esi ; fixup
+ ret
+
+up_get_tpr_edi:
+ reenable_vtpr
+ movzbl vapic, %edi ; fixup
+ ret
+
+up_get_tpr_ebp:
+ reenable_vtpr
+ movzbl vapic, %ebp ; fixup
+ ret
+
+up_get_tpr_stack:
+ reenable_vtpr
+ movzbl vapic, %eax ; fixup
+ xchg %eax, 4(%esp)
+ ret
+
+up_set_tpr_eax:
+ push %eax
+ call up_set_tpr
+ ret
+
+up_set_tpr:
+ pushf
+ push %eax
+ push %ecx
+ push %ebx
+ reenable_vtpr
+
+up_set_tpr_failed:
+ mov vapic, %eax ; fixup
+
+ mov %eax, %ebx
+ mov 20(%esp), %bl
+
+ /* %ebx = new vapic (%bl = tpr, %bh = isr, %b3 = irr) */
+
+ lock cmpxchg %ebx, vapic ; fixup
+ jnz up_set_tpr_failed
+
+ /* compute ppr */
+ cmp %bh, %bl
+ jae up_tpr_is_bigger
+up_isr_is_bigger:
+ mov %bh, %bl
+up_tpr_is_bigger:
+ /* %bl = ppr */
+ mov %bl, %ch /* ch = ppr */
+ rol $8, %ebx
+ /* now: %bl = irr, %bh = ppr */
+ cmp %bh, %bl
+ ja up_set_tpr_poll_irq
+
+up_set_tpr_out:
+ pop %ebx
+ pop %ecx
+ pop %eax
+ popf
+ ret $4
+
+up_set_tpr_poll_irq:
+ mov $kvm_hypercall_vapic_poll_irq, %eax
+ kvm_hypercall
+ jmp up_set_tpr_out
+
+.text 1
+ fixup_end = .
+.text 0
+
+/*
+ * vapic format:
+ * per-vcpu records of size 2^vcpu shift.
+ * byte 0: tpr (r/w)
+ * byte 1: highest in-service interrupt (isr) (r/o); bits 3:0 are zero
+ * byte 2: zero (r/o)
+ * byte 3: highest pending interrupt (irr) (r/o)
+ */
+.text 2
+
+.align 128
+
+vapic:
+. = . + vapic_size
+
+.byte 0 # reserve space for signature
+.align 512, 0
+
+_end:
diff --git a/pc-bios/petalogix-s3adsp1800.dtb b/pc-bios/petalogix-s3adsp1800.dtb
new file mode 100644
index 0000000..93c5973
--- /dev/null
+++ b/pc-bios/petalogix-s3adsp1800.dtb
Binary files differ
diff --git a/pc-bios/ppc_rom.bin b/pc-bios/ppc_rom.bin
new file mode 100644
index 0000000..0ad0282
--- /dev/null
+++ b/pc-bios/ppc_rom.bin
Binary files differ
diff --git a/pc-bios/pxe-e1000.bin b/pc-bios/pxe-e1000.bin
new file mode 100644
index 0000000..7ac744e
--- /dev/null
+++ b/pc-bios/pxe-e1000.bin
Binary files differ
diff --git a/pc-bios/pxe-ne2k_pci.bin b/pc-bios/pxe-ne2k_pci.bin
new file mode 100644
index 0000000..5cb68ab
--- /dev/null
+++ b/pc-bios/pxe-ne2k_pci.bin
Binary files differ
diff --git a/pc-bios/pxe-pcnet.bin b/pc-bios/pxe-pcnet.bin
new file mode 100644
index 0000000..7a54bab
--- /dev/null
+++ b/pc-bios/pxe-pcnet.bin
Binary files differ
diff --git a/pc-bios/pxe-rtl8139.bin b/pc-bios/pxe-rtl8139.bin
new file mode 100644
index 0000000..db7d76d
--- /dev/null
+++ b/pc-bios/pxe-rtl8139.bin
Binary files differ
diff --git a/pc-bios/pxe-virtio.bin b/pc-bios/pxe-virtio.bin
new file mode 100644
index 0000000..6dde514
--- /dev/null
+++ b/pc-bios/pxe-virtio.bin
Binary files differ
diff --git a/pc-bios/s390-zipl.rom b/pc-bios/s390-zipl.rom
new file mode 100644
index 0000000..f7af9b1
--- /dev/null
+++ b/pc-bios/s390-zipl.rom
Binary files differ
diff --git a/pc-bios/vapic.bin b/pc-bios/vapic.bin
new file mode 100755
index 0000000..35f0cf2
--- /dev/null
+++ b/pc-bios/vapic.bin
Binary files differ
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
new file mode 100644
index 0000000..7626ea5
--- /dev/null
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin
new file mode 100644
index 0000000..3156c6e
--- /dev/null
+++ b/pc-bios/vgabios-qxl.bin
Binary files differ
diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin
new file mode 100644
index 0000000..1e4fc33
--- /dev/null
+++ b/pc-bios/vgabios-stdvga.bin
Binary files differ
diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin
new file mode 100644
index 0000000..9633d9a
--- /dev/null
+++ b/pc-bios/vgabios-vmware.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
new file mode 100644
index 0000000..d04033e
--- /dev/null
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/pci-ids.txt b/pci-ids.txt
new file mode 100644
index 0000000..73125a8
--- /dev/null
+++ b/pci-ids.txt
@@ -0,0 +1,31 @@
+
+PCI IDs for qemu
+================
+
+Red Hat, Inc. donates a part of its device ID range to qemu, to be used for
+virtual devices. The vendor ID is 1af4 (formerly Qumranet ID).
+
+The 1000 -> 10ff device ID range is used for VirtIO devices.
+
+The 1100 device ID is used as PCI Subsystem ID for existing hardware
+devices emulated by qemu.
+
+All other device IDs are reserved.
+
+
+VirtIO Device IDs
+-----------------
+
+1af4:1000 network device
+1af4:1001 block device
+1af4:1002 balloon device
+1af4:1003 console device
+
+1af4:1004 Reserved.
+ to Contact Gerd Hoffmann <kraxel@redhat.com> to get a
+1af4:10ef device ID assigned for your new virtio device.
+
+1af4:10f0 Available for experimental usage without registration. Must get
+ to official ID when the code leaves the test lab (i.e. when seeking
+1af4:10ff upstream merge or shipping a distro/product) to avoid conflicts.
+
diff --git a/pflib.c b/pflib.c
new file mode 100644
index 0000000..1154d0c
--- /dev/null
+++ b/pflib.c
@@ -0,0 +1,213 @@
+/*
+ * PixelFormat conversion library.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "qemu-common.h"
+#include "console.h"
+#include "pflib.h"
+
+typedef struct QemuPixel QemuPixel;
+
+typedef void (*pf_convert)(QemuPfConv *conv,
+ void *dst, void *src, uint32_t cnt);
+typedef void (*pf_convert_from)(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt);
+typedef void (*pf_convert_to)(PixelFormat *pf,
+ void *dst, QemuPixel *src, uint32_t cnt);
+
+struct QemuPfConv {
+ pf_convert convert;
+ PixelFormat src;
+ PixelFormat dst;
+
+ /* for copy_generic() */
+ pf_convert_from conv_from;
+ pf_convert_to conv_to;
+ QemuPixel *conv_buf;
+ uint32_t conv_cnt;
+};
+
+struct QemuPixel {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ uint8_t alpha;
+};
+
+/* ----------------------------------------------------------------------- */
+/* PixelFormat -> QemuPixel conversions */
+
+static void conv_16_to_pixel(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt)
+{
+ uint16_t *src16 = src;
+
+ while (cnt > 0) {
+ dst->red = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
+ dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
+ dst->blue = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
+ dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
+ dst++, src16++, cnt--;
+ }
+}
+
+/* assumes pf->{r,g,b,a}bits == 8 */
+static void conv_32_to_pixel_fast(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt)
+{
+ uint32_t *src32 = src;
+
+ while (cnt > 0) {
+ dst->red = (*src32 & pf->rmask) >> pf->rshift;
+ dst->green = (*src32 & pf->gmask) >> pf->gshift;
+ dst->blue = (*src32 & pf->bmask) >> pf->bshift;
+ dst->alpha = (*src32 & pf->amask) >> pf->ashift;
+ dst++, src32++, cnt--;
+ }
+}
+
+static void conv_32_to_pixel_generic(PixelFormat *pf,
+ QemuPixel *dst, void *src, uint32_t cnt)
+{
+ uint32_t *src32 = src;
+
+ while (cnt > 0) {
+ if (pf->rbits < 8) {
+ dst->red = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
+ } else {
+ dst->red = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
+ }
+ if (pf->gbits < 8) {
+ dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
+ } else {
+ dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
+ }
+ if (pf->bbits < 8) {
+ dst->blue = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
+ } else {
+ dst->blue = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
+ }
+ if (pf->abits < 8) {
+ dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
+ } else {
+ dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
+ }
+ dst++, src32++, cnt--;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* QemuPixel -> PixelFormat conversions */
+
+static void conv_pixel_to_16(PixelFormat *pf,
+ void *dst, QemuPixel *src, uint32_t cnt)
+{
+ uint16_t *dst16 = dst;
+
+ while (cnt > 0) {
+ *dst16 = ((uint16_t)src->red >> (8 - pf->rbits)) << pf->rshift;
+ *dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
+ *dst16 |= ((uint16_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
+ *dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
+ dst16++, src++, cnt--;
+ }
+}
+
+static void conv_pixel_to_32(PixelFormat *pf,
+ void *dst, QemuPixel *src, uint32_t cnt)
+{
+ uint32_t *dst32 = dst;
+
+ while (cnt > 0) {
+ *dst32 = ((uint32_t)src->red >> (8 - pf->rbits)) << pf->rshift;
+ *dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
+ *dst32 |= ((uint32_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
+ *dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
+ dst32++, src++, cnt--;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+/* PixelFormat -> PixelFormat conversions */
+
+static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+ uint32_t bytes = cnt * conv->src.bytes_per_pixel;
+ memcpy(dst, src, bytes);
+}
+
+static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+ if (conv->conv_cnt < cnt) {
+ conv->conv_cnt = cnt;
+ conv->conv_buf = qemu_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
+ }
+ conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
+ conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
+}
+
+/* ----------------------------------------------------------------------- */
+/* public interface */
+
+QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
+{
+ QemuPfConv *conv = qemu_mallocz(sizeof(QemuPfConv));
+
+ conv->src = *src;
+ conv->dst = *dst;
+
+ if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
+ /* formats identical, can simply copy */
+ conv->convert = convert_copy;
+ } else {
+ /* generic two-step conversion: src -> QemuPixel -> dst */
+ switch (conv->src.bytes_per_pixel) {
+ case 2:
+ conv->conv_from = conv_16_to_pixel;
+ break;
+ case 4:
+ if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
+ conv->conv_from = conv_32_to_pixel_fast;
+ } else {
+ conv->conv_from = conv_32_to_pixel_generic;
+ }
+ break;
+ default:
+ goto err;
+ }
+ switch (conv->dst.bytes_per_pixel) {
+ case 2:
+ conv->conv_to = conv_pixel_to_16;
+ break;
+ case 4:
+ conv->conv_to = conv_pixel_to_32;
+ break;
+ default:
+ goto err;
+ }
+ conv->convert = convert_generic;
+ }
+ return conv;
+
+err:
+ qemu_free(conv);
+ return NULL;
+}
+
+void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+ conv->convert(conv, dst, src, cnt);
+}
+
+void qemu_pf_conv_put(QemuPfConv *conv)
+{
+ if (conv) {
+ qemu_free(conv->conv_buf);
+ qemu_free(conv);
+ }
+}
diff --git a/pflib.h b/pflib.h
new file mode 100644
index 0000000..b70c313
--- /dev/null
+++ b/pflib.h
@@ -0,0 +1,20 @@
+#ifndef __QEMU_PFLIB_H
+#define __QEMU_PFLIB_H
+
+/*
+ * PixelFormat conversion library.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+typedef struct QemuPfConv QemuPfConv;
+
+QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
+void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
+void qemu_pf_conv_put(QemuPfConv *conv);
+
+#endif
diff --git a/poison.h b/poison.h
new file mode 100644
index 0000000..d7db7f4
--- /dev/null
+++ b/poison.h
@@ -0,0 +1,50 @@
+/* Poison identifiers that should not be used when building
+ target independent device code. */
+
+#ifndef HW_POISON_H
+#define HW_POISON_H
+#ifdef __GNUC__
+
+#pragma GCC poison TARGET_I386
+#pragma GCC poison TARGET_X86_64
+#pragma GCC poison TARGET_ALPHA
+#pragma GCC poison TARGET_ARM
+#pragma GCC poison TARGET_CRIS
+#pragma GCC poison TARGET_M68K
+#pragma GCC poison TARGET_MIPS
+#pragma GCC poison TARGET_MIPS64
+#pragma GCC poison TARGET_PPC
+#pragma GCC poison TARGET_PPCEMB
+#pragma GCC poison TARGET_PPC64
+#pragma GCC poison TARGET_ABI32
+#pragma GCC poison TARGET_SH4
+#pragma GCC poison TARGET_SPARC
+#pragma GCC poison TARGET_SPARC64
+
+#pragma GCC poison TARGET_WORDS_BIGENDIAN
+#pragma GCC poison BSWAP_NEEDED
+
+#pragma GCC poison TARGET_LONG_BITS
+#pragma GCC poison TARGET_FMT_lx
+#pragma GCC poison TARGET_FMT_ld
+
+#pragma GCC poison TARGET_PAGE_SIZE
+#pragma GCC poison TARGET_PAGE_MASK
+#pragma GCC poison TARGET_PAGE_BITS
+#pragma GCC poison TARGET_PAGE_ALIGN
+
+#pragma GCC poison CPUState
+#pragma GCC poison env
+
+#pragma GCC poison CPU_INTERRUPT_HARD
+#pragma GCC poison CPU_INTERRUPT_EXITTB
+#pragma GCC poison CPU_INTERRUPT_TIMER
+#pragma GCC poison CPU_INTERRUPT_FIQ
+#pragma GCC poison CPU_INTERRUPT_HALT
+#pragma GCC poison CPU_INTERRUPT_SMI
+#pragma GCC poison CPU_INTERRUPT_DEBUG
+#pragma GCC poison CPU_INTERRUPT_VIRQ
+#pragma GCC poison CPU_INTERRUPT_NMI
+
+#endif
+#endif
diff --git a/posix-aio-compat.c b/posix-aio-compat.c
new file mode 100644
index 0000000..0704064
--- /dev/null
+++ b/posix-aio-compat.c
@@ -0,0 +1,655 @@
+/*
+ * QEMU posix-aio emulation
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "qemu-queue.h"
+#include "osdep.h"
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "trace.h"
+#include "block_int.h"
+#include "compatfd.h"
+
+#include "block/raw-posix-aio.h"
+
+
+struct qemu_paiocb {
+ BlockDriverAIOCB common;
+ int aio_fildes;
+ union {
+ struct iovec *aio_iov;
+ void *aio_ioctl_buf;
+ };
+ int aio_niov;
+ size_t aio_nbytes;
+#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
+ int ev_signo;
+ off_t aio_offset;
+
+ QTAILQ_ENTRY(qemu_paiocb) node;
+ int aio_type;
+ ssize_t ret;
+ int active;
+ struct qemu_paiocb *next;
+
+ int async_context_id;
+};
+
+typedef struct PosixAioState {
+ int fd;
+ struct qemu_paiocb *first_aio;
+} PosixAioState;
+
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static pthread_t thread_id;
+static pthread_attr_t attr;
+static int max_threads = 64;
+static int cur_threads = 0;
+static int idle_threads = 0;
+static QTAILQ_HEAD(, qemu_paiocb) request_list;
+
+#ifdef CONFIG_PREADV
+static int preadv_present = 1;
+#else
+static int preadv_present = 0;
+#endif
+
+static void die2(int err, const char *what)
+{
+ fprintf(stderr, "%s failed: %s\n", what, strerror(err));
+ abort();
+}
+
+static void die(const char *what)
+{
+ die2(errno, what);
+}
+
+static void mutex_lock(pthread_mutex_t *mutex)
+{
+ int ret = pthread_mutex_lock(mutex);
+ if (ret) die2(ret, "pthread_mutex_lock");
+}
+
+static void mutex_unlock(pthread_mutex_t *mutex)
+{
+ int ret = pthread_mutex_unlock(mutex);
+ if (ret) die2(ret, "pthread_mutex_unlock");
+}
+
+static int cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
+ struct timespec *ts)
+{
+ int ret = pthread_cond_timedwait(cond, mutex, ts);
+ if (ret && ret != ETIMEDOUT) die2(ret, "pthread_cond_timedwait");
+ return ret;
+}
+
+static void cond_signal(pthread_cond_t *cond)
+{
+ int ret = pthread_cond_signal(cond);
+ if (ret) die2(ret, "pthread_cond_signal");
+}
+
+static void thread_create(pthread_t *thread, pthread_attr_t *attr,
+ void *(*start_routine)(void*), void *arg)
+{
+ int ret = pthread_create(thread, attr, start_routine, arg);
+ if (ret) die2(ret, "pthread_create");
+}
+
+static ssize_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb)
+{
+ int ret;
+
+ ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf);
+ if (ret == -1)
+ return -errno;
+
+ /*
+ * This looks weird, but the aio code only consideres a request
+ * successful if it has written the number full number of bytes.
+ *
+ * Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command,
+ * so in fact we return the ioctl command here to make posix_aio_read()
+ * happy..
+ */
+ return aiocb->aio_nbytes;
+}
+
+static ssize_t handle_aiocb_flush(struct qemu_paiocb *aiocb)
+{
+ int ret;
+
+ ret = qemu_fdatasync(aiocb->aio_fildes);
+ if (ret == -1)
+ return -errno;
+ return 0;
+}
+
+#ifdef CONFIG_PREADV
+
+static ssize_t
+qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset)
+{
+ return preadv(fd, iov, nr_iov, offset);
+}
+
+static ssize_t
+qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset)
+{
+ return pwritev(fd, iov, nr_iov, offset);
+}
+
+#else
+
+static ssize_t
+qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset)
+{
+ return -ENOSYS;
+}
+
+static ssize_t
+qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset)
+{
+ return -ENOSYS;
+}
+
+#endif
+
+static ssize_t handle_aiocb_rw_vector(struct qemu_paiocb *aiocb)
+{
+ size_t offset = 0;
+ ssize_t len;
+
+ do {
+ if (aiocb->aio_type & QEMU_AIO_WRITE)
+ len = qemu_pwritev(aiocb->aio_fildes,
+ aiocb->aio_iov,
+ aiocb->aio_niov,
+ aiocb->aio_offset + offset);
+ else
+ len = qemu_preadv(aiocb->aio_fildes,
+ aiocb->aio_iov,
+ aiocb->aio_niov,
+ aiocb->aio_offset + offset);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1)
+ return -errno;
+ return len;
+}
+
+static ssize_t handle_aiocb_rw_linear(struct qemu_paiocb *aiocb, char *buf)
+{
+ ssize_t offset = 0;
+ ssize_t len;
+
+ while (offset < aiocb->aio_nbytes) {
+ if (aiocb->aio_type & QEMU_AIO_WRITE)
+ len = pwrite(aiocb->aio_fildes,
+ (const char *)buf + offset,
+ aiocb->aio_nbytes - offset,
+ aiocb->aio_offset + offset);
+ else
+ len = pread(aiocb->aio_fildes,
+ buf + offset,
+ aiocb->aio_nbytes - offset,
+ aiocb->aio_offset + offset);
+
+ if (len == -1 && errno == EINTR)
+ continue;
+ else if (len == -1) {
+ offset = -errno;
+ break;
+ } else if (len == 0)
+ break;
+
+ offset += len;
+ }
+
+ return offset;
+}
+
+static ssize_t handle_aiocb_rw(struct qemu_paiocb *aiocb)
+{
+ ssize_t nbytes;
+ char *buf;
+
+ if (!(aiocb->aio_type & QEMU_AIO_MISALIGNED)) {
+ /*
+ * If there is just a single buffer, and it is properly aligned
+ * we can just use plain pread/pwrite without any problems.
+ */
+ if (aiocb->aio_niov == 1)
+ return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base);
+
+ /*
+ * We have more than one iovec, and all are properly aligned.
+ *
+ * Try preadv/pwritev first and fall back to linearizing the
+ * buffer if it's not supported.
+ */
+ if (preadv_present) {
+ nbytes = handle_aiocb_rw_vector(aiocb);
+ if (nbytes == aiocb->aio_nbytes)
+ return nbytes;
+ if (nbytes < 0 && nbytes != -ENOSYS)
+ return nbytes;
+ preadv_present = 0;
+ }
+
+ /*
+ * XXX(hch): short read/write. no easy way to handle the reminder
+ * using these interfaces. For now retry using plain
+ * pread/pwrite?
+ */
+ }
+
+ /*
+ * Ok, we have to do it the hard way, copy all segments into
+ * a single aligned buffer.
+ */
+ buf = qemu_blockalign(aiocb->common.bs, aiocb->aio_nbytes);
+ if (aiocb->aio_type & QEMU_AIO_WRITE) {
+ char *p = buf;
+ int i;
+
+ for (i = 0; i < aiocb->aio_niov; ++i) {
+ memcpy(p, aiocb->aio_iov[i].iov_base, aiocb->aio_iov[i].iov_len);
+ p += aiocb->aio_iov[i].iov_len;
+ }
+ }
+
+ nbytes = handle_aiocb_rw_linear(aiocb, buf);
+ if (!(aiocb->aio_type & QEMU_AIO_WRITE)) {
+ char *p = buf;
+ size_t count = aiocb->aio_nbytes, copy;
+ int i;
+
+ for (i = 0; i < aiocb->aio_niov && count; ++i) {
+ copy = count;
+ if (copy > aiocb->aio_iov[i].iov_len)
+ copy = aiocb->aio_iov[i].iov_len;
+ memcpy(aiocb->aio_iov[i].iov_base, p, copy);
+ p += copy;
+ count -= copy;
+ }
+ }
+ qemu_vfree(buf);
+
+ return nbytes;
+}
+
+static void *aio_thread(void *unused)
+{
+ pid_t pid;
+
+ pid = getpid();
+
+ while (1) {
+ struct qemu_paiocb *aiocb;
+ ssize_t ret = 0;
+ qemu_timeval tv;
+ struct timespec ts;
+
+ qemu_gettimeofday(&tv);
+ ts.tv_sec = tv.tv_sec + 10;
+ ts.tv_nsec = 0;
+
+ mutex_lock(&lock);
+
+ while (QTAILQ_EMPTY(&request_list) &&
+ !(ret == ETIMEDOUT)) {
+ ret = cond_timedwait(&cond, &lock, &ts);
+ }
+
+ if (QTAILQ_EMPTY(&request_list))
+ break;
+
+ 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) {
+ case QEMU_AIO_READ:
+ case QEMU_AIO_WRITE:
+ ret = handle_aiocb_rw(aiocb);
+ break;
+ case QEMU_AIO_FLUSH:
+ ret = handle_aiocb_flush(aiocb);
+ break;
+ case QEMU_AIO_IOCTL:
+ ret = handle_aiocb_ioctl(aiocb);
+ break;
+ default:
+ fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ 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);
+
+ return NULL;
+}
+
+static void spawn_thread(void)
+{
+ sigset_t set, oldset;
+
+ cur_threads++;
+ idle_threads++;
+
+ /* block all signals */
+ if (sigfillset(&set)) die("sigfillset");
+ if (sigprocmask(SIG_SETMASK, &set, &oldset)) die("sigprocmask");
+
+ thread_create(&thread_id, &attr, aio_thread, NULL);
+
+ if (sigprocmask(SIG_SETMASK, &oldset, NULL)) die("sigprocmask restore");
+}
+
+static void qemu_paio_submit(struct qemu_paiocb *aiocb)
+{
+ aiocb->ret = -EINPROGRESS;
+ aiocb->active = 0;
+ mutex_lock(&lock);
+ if (idle_threads == 0 && cur_threads < max_threads)
+ spawn_thread();
+ QTAILQ_INSERT_TAIL(&request_list, aiocb, node);
+ mutex_unlock(&lock);
+ cond_signal(&cond);
+}
+
+static ssize_t qemu_paio_return(struct qemu_paiocb *aiocb)
+{
+ ssize_t ret;
+
+ mutex_lock(&lock);
+ ret = aiocb->ret;
+ mutex_unlock(&lock);
+
+ return ret;
+}
+
+static int qemu_paio_error(struct qemu_paiocb *aiocb)
+{
+ ssize_t ret = qemu_paio_return(aiocb);
+
+ if (ret < 0)
+ ret = -ret;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int posix_aio_process_queue(void *opaque)
+{
+ PosixAioState *s = opaque;
+ struct qemu_paiocb *acb, **pacb;
+ int ret;
+ int result = 0;
+ int async_context_id = get_async_context_id();
+
+ for(;;) {
+ pacb = &s->first_aio;
+ for(;;) {
+ acb = *pacb;
+ if (!acb)
+ return result;
+
+ /* we're only interested in requests in the right context */
+ if (acb->async_context_id != async_context_id) {
+ pacb = &acb->next;
+ continue;
+ }
+
+ ret = qemu_paio_error(acb);
+ if (ret == ECANCELED) {
+ /* remove the request */
+ *pacb = acb->next;
+ qemu_aio_release(acb);
+ result = 1;
+ } else if (ret != EINPROGRESS) {
+ /* end of aio */
+ if (ret == 0) {
+ ret = qemu_paio_return(acb);
+ if (ret == acb->aio_nbytes)
+ ret = 0;
+ else
+ ret = -EINVAL;
+ } else {
+ ret = -ret;
+ }
+ /* remove the request */
+ *pacb = acb->next;
+ /* call the callback */
+ acb->common.cb(acb->common.opaque, ret);
+ qemu_aio_release(acb);
+ result = 1;
+ break;
+ } else {
+ pacb = &acb->next;
+ }
+ }
+ }
+
+ return result;
+}
+
+static void posix_aio_read(void *opaque)
+{
+ PosixAioState *s = opaque;
+ union {
+ struct qemu_signalfd_siginfo siginfo;
+ char buf[128];
+ } sig;
+ size_t offset;
+
+ /* try to read from signalfd, don't freak out if we can't read anything */
+ offset = 0;
+ while (offset < 128) {
+ ssize_t len;
+
+ len = read(s->fd, sig.buf + offset, 128 - offset);
+ if (len == -1 && errno == EINTR)
+ continue;
+ if (len == -1 && errno == EAGAIN) {
+ /* there is no natural reason for this to happen,
+ * so we'll spin hard until we get everything just
+ * to be on the safe side. */
+ if (offset > 0)
+ continue;
+ }
+
+ offset += len;
+ }
+
+ posix_aio_process_queue(s);
+}
+
+static int posix_aio_flush(void *opaque)
+{
+ PosixAioState *s = opaque;
+ return !!s->first_aio;
+}
+
+static PosixAioState *posix_aio_state;
+
+static void paio_remove(struct qemu_paiocb *acb)
+{
+ struct qemu_paiocb **pacb;
+
+ /* remove the callback from the queue */
+ pacb = &posix_aio_state->first_aio;
+ for(;;) {
+ if (*pacb == NULL) {
+ fprintf(stderr, "paio_remove: aio request not found!\n");
+ break;
+ } else if (*pacb == acb) {
+ *pacb = acb->next;
+ qemu_aio_release(acb);
+ break;
+ }
+ pacb = &(*pacb)->next;
+ }
+}
+
+static void paio_cancel(BlockDriverAIOCB *blockacb)
+{
+ struct qemu_paiocb *acb = (struct qemu_paiocb *)blockacb;
+ int active = 0;
+
+ mutex_lock(&lock);
+ if (!acb->active) {
+ QTAILQ_REMOVE(&request_list, acb, node);
+ acb->ret = -ECANCELED;
+ } else if (acb->ret == -EINPROGRESS) {
+ active = 1;
+ }
+ mutex_unlock(&lock);
+
+ if (active) {
+ /* fail safe: if the aio could not be canceled, we wait for
+ it */
+ while (qemu_paio_error(acb) == EINPROGRESS)
+ ;
+ }
+
+ paio_remove(acb);
+}
+
+static AIOPool raw_aio_pool = {
+ .aiocb_size = sizeof(struct qemu_paiocb),
+ .cancel = paio_cancel,
+};
+
+BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb, void *opaque, int type)
+{
+ struct qemu_paiocb *acb;
+
+ acb = qemu_aio_get(&raw_aio_pool, bs, cb, opaque);
+ if (!acb)
+ return NULL;
+ acb->aio_type = type;
+ acb->aio_fildes = fd;
+ acb->ev_signo = SIGUSR2;
+ acb->async_context_id = get_async_context_id();
+
+ if (qiov) {
+ acb->aio_iov = qiov->iov;
+ acb->aio_niov = qiov->niov;
+ }
+ acb->aio_nbytes = nb_sectors * 512;
+ acb->aio_offset = sector_num * 512;
+
+ acb->next = posix_aio_state->first_aio;
+ posix_aio_state->first_aio = acb;
+
+ trace_paio_submit(acb, opaque, sector_num, nb_sectors, type);
+ qemu_paio_submit(acb);
+ return &acb->common;
+}
+
+BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
+ unsigned long int req, void *buf,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ struct qemu_paiocb *acb;
+
+ acb = qemu_aio_get(&raw_aio_pool, bs, cb, opaque);
+ if (!acb)
+ return NULL;
+ acb->aio_type = QEMU_AIO_IOCTL;
+ acb->aio_fildes = fd;
+ acb->ev_signo = SIGUSR2;
+ acb->async_context_id = get_async_context_id();
+ acb->aio_offset = 0;
+ acb->aio_ioctl_buf = buf;
+ acb->aio_ioctl_cmd = req;
+
+ acb->next = posix_aio_state->first_aio;
+ posix_aio_state->first_aio = acb;
+
+ qemu_paio_submit(acb);
+ return &acb->common;
+}
+
+int paio_init(void)
+{
+ sigset_t mask;
+ PosixAioState *s;
+ int ret;
+
+ if (posix_aio_state)
+ return 0;
+
+ s = qemu_malloc(sizeof(PosixAioState));
+
+ /* Make sure to block AIO signal */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR2);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ s->first_aio = NULL;
+ s->fd = qemu_signalfd(&mask);
+ if (s->fd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
+ return -1;
+ }
+
+ fcntl(s->fd, F_SETFL, O_NONBLOCK);
+
+ qemu_aio_set_fd_handler(s->fd, posix_aio_read, NULL, posix_aio_flush,
+ posix_aio_process_queue, s);
+
+ ret = pthread_attr_init(&attr);
+ if (ret)
+ die2(ret, "pthread_attr_init");
+
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret)
+ die2(ret, "pthread_attr_setdetachstate");
+
+ QTAILQ_INIT(&request_list);
+
+ posix_aio_state = s;
+ return 0;
+}
diff --git a/ppc-dis.c b/ppc-dis.c
new file mode 100644
index 0000000..ffdbec1
--- /dev/null
+++ b/ppc-dis.c
@@ -0,0 +1,5412 @@
+/* ppc-dis.c -- Disassemble PowerPC instructions
+ Copyright 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not,
+see <http://www.gnu.org/licenses/>. */
+#include "dis-asm.h"
+#define BFD_DEFAULT_TARGET_SIZE 64
+
+/* ppc.h -- Header file for PowerPC opcode table
+ Copyright 1994, 1995, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ 2007 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not,
+see <http://www.gnu.org/licenses/>. */
+
+/* The opcode table is an array of struct powerpc_opcode. */
+
+struct powerpc_opcode
+{
+ /* The opcode name. */
+ const char *name;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ unsigned long opcode;
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ unsigned long mask;
+
+ /* One bit flags for the opcode. These are used to indicate which
+ specific processors support the instructions. The defined values
+ are listed below. */
+ unsigned long flags;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ unsigned char operands[8];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+ in the order in which the disassembler should consider
+ instructions. */
+extern const struct powerpc_opcode powerpc_opcodes[];
+extern const int powerpc_num_opcodes;
+
+/* Values defined for the flags field of a struct powerpc_opcode. */
+
+/* Opcode is defined for the PowerPC architecture. */
+#define PPC_OPCODE_PPC 1
+
+/* Opcode is defined for the POWER (RS/6000) architecture. */
+#define PPC_OPCODE_POWER 2
+
+/* Opcode is defined for the POWER2 (Rios 2) architecture. */
+#define PPC_OPCODE_POWER2 4
+
+/* Opcode is only defined on 32 bit architectures. */
+#define PPC_OPCODE_32 8
+
+/* Opcode is only defined on 64 bit architectures. */
+#define PPC_OPCODE_64 0x10
+
+/* Opcode is supported by the Motorola PowerPC 601 processor. The 601
+ is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions,
+ but it also supports many additional POWER instructions. */
+#define PPC_OPCODE_601 0x20
+
+/* Opcode is supported in both the Power and PowerPC architectures
+ (ie, compiler's -mcpu=common or assembler's -mcom). */
+#define PPC_OPCODE_COMMON 0x40
+
+/* Opcode is supported for any Power or PowerPC platform (this is
+ for the assembler's -many option, and it eliminates duplicates). */
+#define PPC_OPCODE_ANY 0x80
+
+/* Opcode is supported as part of the 64-bit bridge. */
+#define PPC_OPCODE_64_BRIDGE 0x100
+
+/* Opcode is supported by Altivec Vector Unit */
+#define PPC_OPCODE_ALTIVEC 0x200
+
+/* Opcode is supported by PowerPC 403 processor. */
+#define PPC_OPCODE_403 0x400
+
+/* Opcode is supported by PowerPC BookE processor. */
+#define PPC_OPCODE_BOOKE 0x800
+
+/* Opcode is only supported by 64-bit PowerPC BookE processor. */
+#define PPC_OPCODE_BOOKE64 0x1000
+
+/* Opcode is supported by PowerPC 440 processor. */
+#define PPC_OPCODE_440 0x2000
+
+/* Opcode is only supported by Power4 architecture. */
+#define PPC_OPCODE_POWER4 0x4000
+
+/* Opcode isn't supported by Power4 architecture. */
+#define PPC_OPCODE_NOPOWER4 0x8000
+
+/* Opcode is only supported by POWERPC Classic architecture. */
+#define PPC_OPCODE_CLASSIC 0x10000
+
+/* Opcode is only supported by e500x2 Core. */
+#define PPC_OPCODE_SPE 0x20000
+
+/* Opcode is supported by e500x2 Integer select APU. */
+#define PPC_OPCODE_ISEL 0x40000
+
+/* Opcode is an e500 SPE floating point instruction. */
+#define PPC_OPCODE_EFS 0x80000
+
+/* Opcode is supported by branch locking APU. */
+#define PPC_OPCODE_BRLOCK 0x100000
+
+/* Opcode is supported by performance monitor APU. */
+#define PPC_OPCODE_PMR 0x200000
+
+/* Opcode is supported by cache locking APU. */
+#define PPC_OPCODE_CACHELCK 0x400000
+
+/* Opcode is supported by machine check APU. */
+#define PPC_OPCODE_RFMCI 0x800000
+
+/* Opcode is only supported by Power5 architecture. */
+#define PPC_OPCODE_POWER5 0x1000000
+
+/* Opcode is supported by PowerPC e300 family. */
+#define PPC_OPCODE_E300 0x2000000
+
+/* Opcode is only supported by Power6 architecture. */
+#define PPC_OPCODE_POWER6 0x4000000
+
+/* Opcode is only supported by PowerPC Cell family. */
+#define PPC_OPCODE_CELL 0x8000000
+
+/* A macro to extract the major opcode from an instruction. */
+#define PPC_OP(i) (((i) >> 26) & 0x3f)
+
+/* The operands table is an array of struct powerpc_operand. */
+
+struct powerpc_operand
+{
+ /* A bitmask of bits in the operand. */
+ unsigned int bitm;
+
+ /* How far the operand is left shifted in the instruction.
+ -1 to indicate that BITM and SHIFT cannot be used to determine
+ where the operand goes in the insn. */
+ int shift;
+
+ /* Insertion function. This is used by the assembler. To insert an
+ operand value into an instruction, check this field.
+
+ If it is NULL, execute
+ i |= (op & o->bitm) << o->shift;
+ (i is the instruction which we are filling in, o is a pointer to
+ this structure, and op is the operand value).
+
+ If this field is not NULL, then simply call it with the
+ instruction and the operand value. It will return the new value
+ of the instruction. If the ERRMSG argument is not NULL, then if
+ the operand value is illegal, *ERRMSG will be set to a warning
+ string (the operand will be inserted in any case). If the
+ operand value is legal, *ERRMSG will be unchanged (most operands
+ can accept any value). */
+ unsigned long (*insert)
+ (unsigned long instruction, long op, int dialect, const char **errmsg);
+
+ /* Extraction function. This is used by the disassembler. To
+ extract this operand type from an instruction, check this field.
+
+ If it is NULL, compute
+ op = (i >> o->shift) & o->bitm;
+ if ((o->flags & PPC_OPERAND_SIGNED) != 0)
+ sign_extend (op);
+ (i is the instruction, o is a pointer to this structure, and op
+ is the result).
+
+ If this field is not NULL, then simply call it with the
+ instruction value. It will return the value of the operand. If
+ the INVALID argument is not NULL, *INVALID will be set to
+ non-zero if this operand type can not actually be extracted from
+ this operand (i.e., the instruction does not match). If the
+ operand is valid, *INVALID will not be changed. */
+ long (*extract) (unsigned long instruction, int dialect, int *invalid);
+
+ /* One bit syntax flags. */
+ unsigned long flags;
+};
+
+/* Elements in the table are retrieved by indexing with values from
+ the operands field of the powerpc_opcodes table. */
+
+extern const struct powerpc_operand powerpc_operands[];
+extern const unsigned int num_powerpc_operands;
+
+/* Values defined for the flags field of a struct powerpc_operand. */
+
+/* This operand takes signed values. */
+#define PPC_OPERAND_SIGNED (0x1)
+
+/* This operand takes signed values, but also accepts a full positive
+ range of values when running in 32 bit mode. That is, if bits is
+ 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode,
+ this flag is ignored. */
+#define PPC_OPERAND_SIGNOPT (0x2)
+
+/* This operand does not actually exist in the assembler input. This
+ is used to support extended mnemonics such as mr, for which two
+ operands fields are identical. The assembler should call the
+ insert function with any op value. The disassembler should call
+ the extract function, ignore the return value, and check the value
+ placed in the valid argument. */
+#define PPC_OPERAND_FAKE (0x4)
+
+/* The next operand should be wrapped in parentheses rather than
+ separated from this one by a comma. This is used for the load and
+ store instructions which want their operands to look like
+ reg,displacement(reg)
+ */
+#define PPC_OPERAND_PARENS (0x8)
+
+/* This operand may use the symbolic names for the CR fields, which
+ are
+ lt 0 gt 1 eq 2 so 3 un 3
+ cr0 0 cr1 1 cr2 2 cr3 3
+ cr4 4 cr5 5 cr6 6 cr7 7
+ These may be combined arithmetically, as in cr2*4+gt. These are
+ only supported on the PowerPC, not the POWER. */
+#define PPC_OPERAND_CR (0x10)
+
+/* This operand names a register. The disassembler uses this to print
+ register names with a leading 'r'. */
+#define PPC_OPERAND_GPR (0x20)
+
+/* Like PPC_OPERAND_GPR, but don't print a leading 'r' for r0. */
+#define PPC_OPERAND_GPR_0 (0x40)
+
+/* This operand names a floating point register. The disassembler
+ prints these with a leading 'f'. */
+#define PPC_OPERAND_FPR (0x80)
+
+/* This operand is a relative branch displacement. The disassembler
+ prints these symbolically if possible. */
+#define PPC_OPERAND_RELATIVE (0x100)
+
+/* This operand is an absolute branch address. The disassembler
+ prints these symbolically if possible. */
+#define PPC_OPERAND_ABSOLUTE (0x200)
+
+/* This operand is optional, and is zero if omitted. This is used for
+ example, in the optional BF field in the comparison instructions. The
+ assembler must count the number of operands remaining on the line,
+ and the number of operands remaining for the opcode, and decide
+ whether this operand is present or not. The disassembler should
+ print this operand out only if it is not zero. */
+#define PPC_OPERAND_OPTIONAL (0x400)
+
+/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand
+ is omitted, then for the next operand use this operand value plus
+ 1, ignoring the next operand field for the opcode. This wretched
+ hack is needed because the Power rotate instructions can take
+ either 4 or 5 operands. The disassembler should print this operand
+ out regardless of the PPC_OPERAND_OPTIONAL field. */
+#define PPC_OPERAND_NEXT (0x800)
+
+/* This operand should be regarded as a negative number for the
+ purposes of overflow checking (i.e., the normal most negative
+ number is disallowed and one more than the normal most positive
+ number is allowed). This flag will only be set for a signed
+ operand. */
+#define PPC_OPERAND_NEGATIVE (0x1000)
+
+/* This operand names a vector unit register. The disassembler
+ prints these with a leading 'v'. */
+#define PPC_OPERAND_VR (0x2000)
+
+/* This operand is for the DS field in a DS form instruction. */
+#define PPC_OPERAND_DS (0x4000)
+
+/* This operand is for the DQ field in a DQ form instruction. */
+#define PPC_OPERAND_DQ (0x8000)
+
+/* Valid range of operand is 0..n rather than 0..n-1. */
+#define PPC_OPERAND_PLUS1 (0x10000)
+
+/* The POWER and PowerPC assemblers use a few macros. We keep them
+ with the operands table for simplicity. The macro table is an
+ array of struct powerpc_macro. */
+
+struct powerpc_macro
+{
+ /* The macro name. */
+ const char *name;
+
+ /* The number of operands the macro takes. */
+ unsigned int operands;
+
+ /* One bit flags for the opcode. These are used to indicate which
+ specific processors support the instructions. The values are the
+ same as those for the struct powerpc_opcode flags field. */
+ unsigned long flags;
+
+ /* A format string to turn the macro into a normal instruction.
+ Each %N in the string is replaced with operand number N (zero
+ based). */
+ const char *format;
+};
+
+extern const struct powerpc_macro powerpc_macros[];
+extern const int powerpc_num_macros;
+
+/* ppc-opc.c -- PowerPC opcode list
+ Copyright 1994, 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ GDB, GAS, and the GNU binutils are free software; you can redistribute
+ them and/or modify them under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either version
+ 2, or (at your option) any later version.
+
+ GDB, GAS, and the GNU binutils are distributed in the hope that they
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this file; see the file COPYING.
+ If not, see <http://www.gnu.org/licenses/>. */
+
+/* This file holds the PowerPC opcode table. The opcode table
+ includes almost all of the extended instruction mnemonics. This
+ permits the disassembler to use them, and simplifies the assembler
+ logic, at the cost of increasing the table size. The table is
+ strictly constant data, so the compiler should be able to put it in
+ the .text section.
+
+ This file also holds the operand table. All knowledge about
+ inserting operands into instructions and vice-versa is kept in this
+ file. */
+
+/* Local insertion and extraction functions. */
+
+static unsigned long insert_bat (unsigned long, long, int, const char **);
+static long extract_bat (unsigned long, int, int *);
+static unsigned long insert_bba (unsigned long, long, int, const char **);
+static long extract_bba (unsigned long, int, int *);
+static unsigned long insert_bdm (unsigned long, long, int, const char **);
+static long extract_bdm (unsigned long, int, int *);
+static unsigned long insert_bdp (unsigned long, long, int, const char **);
+static long extract_bdp (unsigned long, int, int *);
+static unsigned long insert_bo (unsigned long, long, int, const char **);
+static long extract_bo (unsigned long, int, int *);
+static unsigned long insert_boe (unsigned long, long, int, const char **);
+static long extract_boe (unsigned long, int, int *);
+static unsigned long insert_fxm (unsigned long, long, int, const char **);
+static long extract_fxm (unsigned long, int, int *);
+static unsigned long insert_mbe (unsigned long, long, int, const char **);
+static long extract_mbe (unsigned long, int, int *);
+static unsigned long insert_mb6 (unsigned long, long, int, const char **);
+static long extract_mb6 (unsigned long, int, int *);
+static long extract_nb (unsigned long, int, int *);
+static unsigned long insert_nsi (unsigned long, long, int, const char **);
+static long extract_nsi (unsigned long, int, int *);
+static unsigned long insert_ral (unsigned long, long, int, const char **);
+static unsigned long insert_ram (unsigned long, long, int, const char **);
+static unsigned long insert_raq (unsigned long, long, int, const char **);
+static unsigned long insert_ras (unsigned long, long, int, const char **);
+static unsigned long insert_rbs (unsigned long, long, int, const char **);
+static long extract_rbs (unsigned long, int, int *);
+static unsigned long insert_sh6 (unsigned long, long, int, const char **);
+static long extract_sh6 (unsigned long, int, int *);
+static unsigned long insert_spr (unsigned long, long, int, const char **);
+static long extract_spr (unsigned long, int, int *);
+static unsigned long insert_sprg (unsigned long, long, int, const char **);
+static long extract_sprg (unsigned long, int, int *);
+static unsigned long insert_tbr (unsigned long, long, int, const char **);
+static long extract_tbr (unsigned long, int, int *);
+
+/* The operands table.
+
+ The fields are bitm, shift, insert, extract, flags.
+
+ We used to put parens around the various additions, like the one
+ for BA just below. However, that caused trouble with feeble
+ compilers with a limit on depth of a parenthesized expression, like
+ (reportedly) the compiler in Microsoft Developer Studio 5. So we
+ omit the parens, since the macros are never used in a context where
+ the addition will be ambiguous. */
+
+const struct powerpc_operand powerpc_operands[] =
+{
+ /* The zero index is used to indicate the end of the list of
+ operands. */
+#define UNUSED 0
+ { 0, 0, NULL, NULL, 0 },
+
+ /* The BA field in an XL form instruction. */
+#define BA UNUSED + 1
+ /* The BI field in a B form or XL form instruction. */
+#define BI BA
+#define BI_MASK (0x1f << 16)
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_CR },
+
+ /* The BA field in an XL form instruction when it must be the same
+ as the BT field in the same instruction. */
+#define BAT BA + 1
+ { 0x1f, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE },
+
+ /* The BB field in an XL form instruction. */
+#define BB BAT + 1
+#define BB_MASK (0x1f << 11)
+ { 0x1f, 11, NULL, NULL, PPC_OPERAND_CR },
+
+ /* The BB field in an XL form instruction when it must be the same
+ as the BA field in the same instruction. */
+#define BBA BB + 1
+ { 0x1f, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE },
+
+ /* The BD field in a B form instruction. The lower two bits are
+ forced to zero. */
+#define BD BBA + 1
+ { 0xfffc, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when absolute addressing is
+ used. */
+#define BDA BD + 1
+ { 0xfffc, 0, NULL, NULL, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the - modifier is used.
+ This sets the y bit of the BO field appropriately. */
+#define BDM BDA + 1
+ { 0xfffc, 0, insert_bdm, extract_bdm,
+ PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the - modifier is used
+ and absolute address is used. */
+#define BDMA BDM + 1
+ { 0xfffc, 0, insert_bdm, extract_bdm,
+ PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the + modifier is used.
+ This sets the y bit of the BO field appropriately. */
+#define BDP BDMA + 1
+ { 0xfffc, 0, insert_bdp, extract_bdp,
+ PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the + modifier is used
+ and absolute addressing is used. */
+#define BDPA BDP + 1
+ { 0xfffc, 0, insert_bdp, extract_bdp,
+ PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BF field in an X or XL form instruction. */
+#define BF BDPA + 1
+ /* The CRFD field in an X form instruction. */
+#define CRFD BF
+ { 0x7, 23, NULL, NULL, PPC_OPERAND_CR },
+
+ /* The BF field in an X or XL form instruction. */
+#define BFF BF + 1
+ { 0x7, 23, NULL, NULL, 0 },
+
+ /* An optional BF field. This is used for comparison instructions,
+ in which an omitted BF field is taken as zero. */
+#define OBF BFF + 1
+ { 0x7, 23, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+ /* The BFA field in an X or XL form instruction. */
+#define BFA OBF + 1
+ { 0x7, 18, NULL, NULL, PPC_OPERAND_CR },
+
+ /* The BO field in a B form instruction. Certain values are
+ illegal. */
+#define BO BFA + 1
+#define BO_MASK (0x1f << 21)
+ { 0x1f, 21, insert_bo, extract_bo, 0 },
+
+ /* The BO field in a B form instruction when the + or - modifier is
+ used. This is like the BO field, but it must be even. */
+#define BOE BO + 1
+ { 0x1e, 21, insert_boe, extract_boe, 0 },
+
+#define BH BOE + 1
+ { 0x3, 11, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The BT field in an X or XL form instruction. */
+#define BT BH + 1
+ { 0x1f, 21, NULL, NULL, PPC_OPERAND_CR },
+
+ /* The condition register number portion of the BI field in a B form
+ or XL form instruction. This is used for the extended
+ conditional branch mnemonics, which set the lower two bits of the
+ BI field. This field is optional. */
+#define CR BT + 1
+ { 0x7, 18, NULL, NULL, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+ /* The CRB field in an X form instruction. */
+#define CRB CR + 1
+ /* The MB field in an M form instruction. */
+#define MB CRB
+#define MB_MASK (0x1f << 6)
+ { 0x1f, 6, NULL, NULL, 0 },
+
+ /* The CRFS field in an X form instruction. */
+#define CRFS CRB + 1
+ { 0x7, 0, NULL, NULL, PPC_OPERAND_CR },
+
+ /* The CT field in an X form instruction. */
+#define CT CRFS + 1
+ /* The MO field in an mbar instruction. */
+#define MO CT
+ { 0x1f, 21, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The D field in a D form instruction. This is a displacement off
+ a register, and implies that the next operand is a register in
+ parentheses. */
+#define D CT + 1
+ { 0xffff, 0, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The DE field in a DE form instruction. This is like D, but is 12
+ bits only. */
+#define DE D + 1
+ { 0xfff, 4, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The DES field in a DES form instruction. This is like DS, but is 14
+ bits only (12 stored.) */
+#define DES DE + 1
+ { 0x3ffc, 2, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The DQ field in a DQ form instruction. This is like D, but the
+ lower four bits are forced to zero. */
+#define DQ DES + 1
+ { 0xfff0, 0, NULL, NULL,
+ PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DQ },
+
+ /* The DS field in a DS form instruction. This is like D, but the
+ lower two bits are forced to zero. */
+#undef DS
+#define DS DQ + 1
+ { 0xfffc, 0, NULL, NULL,
+ PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DS },
+
+ /* The E field in a wrteei instruction. */
+#define E DS + 1
+ { 0x1, 15, NULL, NULL, 0 },
+
+ /* The FL1 field in a POWER SC form instruction. */
+#define FL1 E + 1
+ /* The U field in an X form instruction. */
+#define U FL1
+ { 0xf, 12, NULL, NULL, 0 },
+
+ /* The FL2 field in a POWER SC form instruction. */
+#define FL2 FL1 + 1
+ { 0x7, 2, NULL, NULL, 0 },
+
+ /* The FLM field in an XFL form instruction. */
+#define FLM FL2 + 1
+ { 0xff, 17, NULL, NULL, 0 },
+
+ /* The FRA field in an X or A form instruction. */
+#define FRA FLM + 1
+#define FRA_MASK (0x1f << 16)
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_FPR },
+
+ /* The FRB field in an X or A form instruction. */
+#define FRB FRA + 1
+#define FRB_MASK (0x1f << 11)
+ { 0x1f, 11, NULL, NULL, PPC_OPERAND_FPR },
+
+ /* The FRC field in an A form instruction. */
+#define FRC FRB + 1
+#define FRC_MASK (0x1f << 6)
+ { 0x1f, 6, NULL, NULL, PPC_OPERAND_FPR },
+
+ /* The FRS field in an X form instruction or the FRT field in a D, X
+ or A form instruction. */
+#define FRS FRC + 1
+#define FRT FRS
+ { 0x1f, 21, NULL, NULL, PPC_OPERAND_FPR },
+
+ /* The FXM field in an XFX instruction. */
+#define FXM FRS + 1
+ { 0xff, 12, insert_fxm, extract_fxm, 0 },
+
+ /* Power4 version for mfcr. */
+#define FXM4 FXM + 1
+ { 0xff, 12, insert_fxm, extract_fxm, PPC_OPERAND_OPTIONAL },
+
+ /* The L field in a D or X form instruction. */
+#define L FXM4 + 1
+ { 0x1, 21, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The LEV field in a POWER SVC form instruction. */
+#define SVC_LEV L + 1
+ { 0x7f, 5, NULL, NULL, 0 },
+
+ /* The LEV field in an SC form instruction. */
+#define LEV SVC_LEV + 1
+ { 0x7f, 5, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The LI field in an I form instruction. The lower two bits are
+ forced to zero. */
+#define LI LEV + 1
+ { 0x3fffffc, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The LI field in an I form instruction when used as an absolute
+ address. */
+#define LIA LI + 1
+ { 0x3fffffc, 0, NULL, NULL, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The LS field in an X (sync) form instruction. */
+#define LS LIA + 1
+ { 0x3, 21, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The ME field in an M form instruction. */
+#define ME LS + 1
+#define ME_MASK (0x1f << 1)
+ { 0x1f, 1, NULL, NULL, 0 },
+
+ /* The MB and ME fields in an M form instruction expressed a single
+ operand which is a bitmask indicating which bits to select. This
+ is a two operand form using PPC_OPERAND_NEXT. See the
+ description in opcode/ppc.h for what this means. */
+#define MBE ME + 1
+ { 0x1f, 6, NULL, NULL, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
+ { -1, 0, insert_mbe, extract_mbe, 0 },
+
+ /* The MB or ME field in an MD or MDS form instruction. The high
+ bit is wrapped to the low end. */
+#define MB6 MBE + 2
+#define ME6 MB6
+#define MB6_MASK (0x3f << 5)
+ { 0x3f, 5, insert_mb6, extract_mb6, 0 },
+
+ /* The NB field in an X form instruction. The value 32 is stored as
+ 0. */
+#define NB MB6 + 1
+ { 0x1f, 11, NULL, extract_nb, PPC_OPERAND_PLUS1 },
+
+ /* The NSI field in a D form instruction. This is the same as the
+ SI field, only negated. */
+#define NSI NB + 1
+ { 0xffff, 0, insert_nsi, extract_nsi,
+ PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
+
+ /* The RA field in an D, DS, DQ, X, XO, M, or MDS form instruction. */
+#define RA NSI + 1
+#define RA_MASK (0x1f << 16)
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR },
+
+ /* As above, but 0 in the RA field means zero, not r0. */
+#define RA0 RA + 1
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR_0 },
+
+ /* The RA field in the DQ form lq instruction, which has special
+ value restrictions. */
+#define RAQ RA0 + 1
+ { 0x1f, 16, insert_raq, NULL, PPC_OPERAND_GPR_0 },
+
+ /* The RA field in a D or X form instruction which is an updating
+ load, which means that the RA field may not be zero and may not
+ equal the RT field. */
+#define RAL RAQ + 1
+ { 0x1f, 16, insert_ral, NULL, PPC_OPERAND_GPR_0 },
+
+ /* The RA field in an lmw instruction, which has special value
+ restrictions. */
+#define RAM RAL + 1
+ { 0x1f, 16, insert_ram, NULL, PPC_OPERAND_GPR_0 },
+
+ /* The RA field in a D or X form instruction which is an updating
+ store or an updating floating point load, which means that the RA
+ field may not be zero. */
+#define RAS RAM + 1
+ { 0x1f, 16, insert_ras, NULL, PPC_OPERAND_GPR_0 },
+
+ /* The RA field of the tlbwe instruction, which is optional. */
+#define RAOPT RAS + 1
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_GPR | PPC_OPERAND_OPTIONAL },
+
+ /* The RB field in an X, XO, M, or MDS form instruction. */
+#define RB RAOPT + 1
+#define RB_MASK (0x1f << 11)
+ { 0x1f, 11, NULL, NULL, PPC_OPERAND_GPR },
+
+ /* The RB field in an X form instruction when it must be the same as
+ the RS field in the instruction. This is used for extended
+ mnemonics like mr. */
+#define RBS RB + 1
+ { 0x1f, 11, insert_rbs, extract_rbs, PPC_OPERAND_FAKE },
+
+ /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form
+ instruction or the RT field in a D, DS, X, XFX or XO form
+ instruction. */
+#define RS RBS + 1
+#define RT RS
+#define RT_MASK (0x1f << 21)
+ { 0x1f, 21, NULL, NULL, PPC_OPERAND_GPR },
+
+ /* The RS and RT fields of the DS form stq instruction, which have
+ special value restrictions. */
+#define RSQ RS + 1
+#define RTQ RSQ
+ { 0x1e, 21, NULL, NULL, PPC_OPERAND_GPR_0 },
+
+ /* The RS field of the tlbwe instruction, which is optional. */
+#define RSO RSQ + 1
+#define RTO RSO
+ { 0x1f, 21, NULL, NULL, PPC_OPERAND_GPR | PPC_OPERAND_OPTIONAL },
+
+ /* The SH field in an X or M form instruction. */
+#define SH RSO + 1
+#define SH_MASK (0x1f << 11)
+ /* The other UIMM field in a EVX form instruction. */
+#define EVUIMM SH
+ { 0x1f, 11, NULL, NULL, 0 },
+
+ /* The SH field in an MD form instruction. This is split. */
+#define SH6 SH + 1
+#define SH6_MASK ((0x1f << 11) | (1 << 1))
+ { 0x3f, -1, insert_sh6, extract_sh6, 0 },
+
+ /* The SH field of the tlbwe instruction, which is optional. */
+#define SHO SH6 + 1
+ { 0x1f, 11, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The SI field in a D form instruction. */
+#define SI SHO + 1
+ { 0xffff, 0, NULL, NULL, PPC_OPERAND_SIGNED },
+
+ /* The SI field in a D form instruction when we accept a wide range
+ of positive values. */
+#define SISIGNOPT SI + 1
+ { 0xffff, 0, NULL, NULL, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
+
+ /* The SPR field in an XFX form instruction. This is flipped--the
+ lower 5 bits are stored in the upper 5 and vice- versa. */
+#define SPR SISIGNOPT + 1
+#define PMR SPR
+#define SPR_MASK (0x3ff << 11)
+ { 0x3ff, 11, insert_spr, extract_spr, 0 },
+
+ /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */
+#define SPRBAT SPR + 1
+#define SPRBAT_MASK (0x3 << 17)
+ { 0x3, 17, NULL, NULL, 0 },
+
+ /* The SPRG register number in an XFX form m[ft]sprg instruction. */
+#define SPRG SPRBAT + 1
+ { 0x1f, 16, insert_sprg, extract_sprg, 0 },
+
+ /* The SR field in an X form instruction. */
+#define SR SPRG + 1
+ { 0xf, 16, NULL, NULL, 0 },
+
+ /* The STRM field in an X AltiVec form instruction. */
+#define STRM SR + 1
+ { 0x3, 21, NULL, NULL, 0 },
+
+ /* The SV field in a POWER SC form instruction. */
+#define SV STRM + 1
+ { 0x3fff, 2, NULL, NULL, 0 },
+
+ /* The TBR field in an XFX form instruction. This is like the SPR
+ field, but it is optional. */
+#define TBR SV + 1
+ { 0x3ff, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL },
+
+ /* The TO field in a D or X form instruction. */
+#define TO TBR + 1
+#define TO_MASK (0x1f << 21)
+ { 0x1f, 21, NULL, NULL, 0 },
+
+ /* The UI field in a D form instruction. */
+#define UI TO + 1
+ { 0xffff, 0, NULL, NULL, 0 },
+
+ /* The VA field in a VA, VX or VXR form instruction. */
+#define VA UI + 1
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_VR },
+
+ /* The VB field in a VA, VX or VXR form instruction. */
+#define VB VA + 1
+ { 0x1f, 11, NULL, NULL, PPC_OPERAND_VR },
+
+ /* The VC field in a VA form instruction. */
+#define VC VB + 1
+ { 0x1f, 6, NULL, NULL, PPC_OPERAND_VR },
+
+ /* The VD or VS field in a VA, VX, VXR or X form instruction. */
+#define VD VC + 1
+#define VS VD
+ { 0x1f, 21, NULL, NULL, PPC_OPERAND_VR },
+
+ /* The SIMM field in a VX form instruction. */
+#define SIMM VD + 1
+ { 0x1f, 16, NULL, NULL, PPC_OPERAND_SIGNED},
+
+ /* The UIMM field in a VX form instruction, and TE in Z form. */
+#define UIMM SIMM + 1
+#define TE UIMM
+ { 0x1f, 16, NULL, NULL, 0 },
+
+ /* The SHB field in a VA form instruction. */
+#define SHB UIMM + 1
+ { 0xf, 6, NULL, NULL, 0 },
+
+ /* The other UIMM field in a half word EVX form instruction. */
+#define EVUIMM_2 SHB + 1
+ { 0x3e, 10, NULL, NULL, PPC_OPERAND_PARENS },
+
+ /* The other UIMM field in a word EVX form instruction. */
+#define EVUIMM_4 EVUIMM_2 + 1
+ { 0x7c, 9, NULL, NULL, PPC_OPERAND_PARENS },
+
+ /* The other UIMM field in a double EVX form instruction. */
+#define EVUIMM_8 EVUIMM_4 + 1
+ { 0xf8, 8, NULL, NULL, PPC_OPERAND_PARENS },
+
+ /* The WS field. */
+#define WS EVUIMM_8 + 1
+ { 0x7, 11, NULL, NULL, 0 },
+
+ /* The L field in an mtmsrd or A form instruction or W in an X form. */
+#define A_L WS + 1
+#define W A_L
+ { 0x1, 16, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+#define RMC A_L + 1
+ { 0x3, 9, NULL, NULL, 0 },
+
+#define R RMC + 1
+ { 0x1, 16, NULL, NULL, 0 },
+
+#define SP R + 1
+ { 0x3, 19, NULL, NULL, 0 },
+
+#define S SP + 1
+ { 0x1, 20, NULL, NULL, 0 },
+
+ /* SH field starting at bit position 16. */
+#define SH16 S + 1
+ /* The DCM and DGM fields in a Z form instruction. */
+#define DCM SH16
+#define DGM DCM
+ { 0x3f, 10, NULL, NULL, 0 },
+
+ /* The EH field in larx instruction. */
+#define EH SH16 + 1
+ { 0x1, 0, NULL, NULL, PPC_OPERAND_OPTIONAL },
+
+ /* The L field in an mtfsf or XFL form instruction. */
+#define XFL_L EH + 1
+ { 0x1, 25, NULL, NULL, PPC_OPERAND_OPTIONAL},
+};
+
+const unsigned int num_powerpc_operands = (sizeof (powerpc_operands)
+ / sizeof (powerpc_operands[0]));
+
+/* The functions used to insert and extract complicated operands. */
+
+/* The BA field in an XL form instruction when it must be the same as
+ the BT field in the same instruction. This operand is marked FAKE.
+ The insertion function just copies the BT field into the BA field,
+ and the extraction function just checks that the fields are the
+ same. */
+
+static unsigned long
+insert_bat (unsigned long insn,
+ long value ATTRIBUTE_UNUSED,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static long
+extract_bat (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid)
+{
+ if (((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The BB field in an XL form instruction when it must be the same as
+ the BA field in the same instruction. This operand is marked FAKE.
+ The insertion function just copies the BA field into the BB field,
+ and the extraction function just checks that the fields are the
+ same. */
+
+static unsigned long
+insert_bba (unsigned long insn,
+ long value ATTRIBUTE_UNUSED,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (((insn >> 16) & 0x1f) << 11);
+}
+
+static long
+extract_bba (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid)
+{
+ if (((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The BD field in a B form instruction when the - modifier is used.
+ This modifier means that the branch is not expected to be taken.
+ For chips built to versions of the architecture prior to version 2
+ (ie. not Power4 compatible), we set the y bit of the BO field to 1
+ if the offset is negative. When extracting, we require that the y
+ bit be 1 and that the offset be positive, since if the y bit is 0
+ we just want to print the normal form of the instruction.
+ Power4 compatible targets use two bits, "a", and "t", instead of
+ the "y" bit. "at" == 00 => no hint, "at" == 01 => unpredictable,
+ "at" == 10 => not taken, "at" == 11 => taken. The "t" bit is 00001
+ in BO field, the "a" bit is 00010 for branch on CR(BI) and 01000
+ for branch on CTR. We only handle the taken/not-taken hint here.
+ Note that we don't relax the conditions tested here when
+ disassembling with -Many because insns using extract_bdm and
+ extract_bdp always occur in pairs. One or the other will always
+ be valid. */
+
+static unsigned long
+insert_bdm (unsigned long insn,
+ long value,
+ int dialect,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ if ((dialect & PPC_OPCODE_POWER4) == 0)
+ {
+ if ((value & 0x8000) != 0)
+ insn |= 1 << 21;
+ }
+ else
+ {
+ if ((insn & (0x14 << 21)) == (0x04 << 21))
+ insn |= 0x02 << 21;
+ else if ((insn & (0x14 << 21)) == (0x10 << 21))
+ insn |= 0x08 << 21;
+ }
+ return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdm (unsigned long insn,
+ int dialect,
+ int *invalid)
+{
+ if ((dialect & PPC_OPCODE_POWER4) == 0)
+ {
+ if (((insn & (1 << 21)) == 0) != ((insn & (1 << 15)) == 0))
+ *invalid = 1;
+ }
+ else
+ {
+ if ((insn & (0x17 << 21)) != (0x06 << 21)
+ && (insn & (0x1d << 21)) != (0x18 << 21))
+ *invalid = 1;
+ }
+
+ return ((insn & 0xfffc) ^ 0x8000) - 0x8000;
+}
+
+/* The BD field in a B form instruction when the + modifier is used.
+ This is like BDM, above, except that the branch is expected to be
+ taken. */
+
+static unsigned long
+insert_bdp (unsigned long insn,
+ long value,
+ int dialect,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ if ((dialect & PPC_OPCODE_POWER4) == 0)
+ {
+ if ((value & 0x8000) == 0)
+ insn |= 1 << 21;
+ }
+ else
+ {
+ if ((insn & (0x14 << 21)) == (0x04 << 21))
+ insn |= 0x03 << 21;
+ else if ((insn & (0x14 << 21)) == (0x10 << 21))
+ insn |= 0x09 << 21;
+ }
+ return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdp (unsigned long insn,
+ int dialect,
+ int *invalid)
+{
+ if ((dialect & PPC_OPCODE_POWER4) == 0)
+ {
+ if (((insn & (1 << 21)) == 0) == ((insn & (1 << 15)) == 0))
+ *invalid = 1;
+ }
+ else
+ {
+ if ((insn & (0x17 << 21)) != (0x07 << 21)
+ && (insn & (0x1d << 21)) != (0x19 << 21))
+ *invalid = 1;
+ }
+
+ return ((insn & 0xfffc) ^ 0x8000) - 0x8000;
+}
+
+/* Check for legal values of a BO field. */
+
+static int
+valid_bo (long value, int dialect, int extract)
+{
+ if ((dialect & PPC_OPCODE_POWER4) == 0)
+ {
+ int valid;
+ /* Certain encodings have bits that are required to be zero.
+ These are (z must be zero, y may be anything):
+ 001zy
+ 011zy
+ 1z00y
+ 1z01y
+ 1z1zz
+ */
+ switch (value & 0x14)
+ {
+ default:
+ case 0:
+ valid = 1;
+ break;
+ case 0x4:
+ valid = (value & 0x2) == 0;
+ break;
+ case 0x10:
+ valid = (value & 0x8) == 0;
+ break;
+ case 0x14:
+ valid = value == 0x14;
+ break;
+ }
+ /* When disassembling with -Many, accept power4 encodings too. */
+ if (valid
+ || (dialect & PPC_OPCODE_ANY) == 0
+ || !extract)
+ return valid;
+ }
+
+ /* Certain encodings have bits that are required to be zero.
+ These are (z must be zero, a & t may be anything):
+ 0000z
+ 0001z
+ 0100z
+ 0101z
+ 001at
+ 011at
+ 1a00t
+ 1a01t
+ 1z1zz
+ */
+ if ((value & 0x14) == 0)
+ return (value & 0x1) == 0;
+ else if ((value & 0x14) == 0x14)
+ return value == 0x14;
+ else
+ return 1;
+}
+
+/* The BO field in a B form instruction. Warn about attempts to set
+ the field to an illegal value. */
+
+static unsigned long
+insert_bo (unsigned long insn,
+ long value,
+ int dialect,
+ const char **errmsg)
+{
+ if (!valid_bo (value, dialect, 0))
+ *errmsg = _("invalid conditional option");
+ return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_bo (unsigned long insn,
+ int dialect,
+ int *invalid)
+{
+ long value;
+
+ value = (insn >> 21) & 0x1f;
+ if (!valid_bo (value, dialect, 1))
+ *invalid = 1;
+ return value;
+}
+
+/* The BO field in a B form instruction when the + or - modifier is
+ used. This is like the BO field, but it must be even. When
+ extracting it, we force it to be even. */
+
+static unsigned long
+insert_boe (unsigned long insn,
+ long value,
+ int dialect,
+ const char **errmsg)
+{
+ if (!valid_bo (value, dialect, 0))
+ *errmsg = _("invalid conditional option");
+ else if ((value & 1) != 0)
+ *errmsg = _("attempt to set y bit when using + or - modifier");
+
+ return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_boe (unsigned long insn,
+ int dialect,
+ int *invalid)
+{
+ long value;
+
+ value = (insn >> 21) & 0x1f;
+ if (!valid_bo (value, dialect, 1))
+ *invalid = 1;
+ return value & 0x1e;
+}
+
+/* FXM mask in mfcr and mtcrf instructions. */
+
+static unsigned long
+insert_fxm (unsigned long insn,
+ long value,
+ int dialect,
+ const char **errmsg)
+{
+ /* If we're handling the mfocrf and mtocrf insns ensure that exactly
+ one bit of the mask field is set. */
+ if ((insn & (1 << 20)) != 0)
+ {
+ if (value == 0 || (value & -value) != value)
+ {
+ *errmsg = _("invalid mask field");
+ value = 0;
+ }
+ }
+
+ /* If the optional field on mfcr is missing that means we want to use
+ the old form of the instruction that moves the whole cr. In that
+ case we'll have VALUE zero. There doesn't seem to be a way to
+ distinguish this from the case where someone writes mfcr %r3,0. */
+ else if (value == 0)
+ ;
+
+ /* If only one bit of the FXM field is set, we can use the new form
+ of the instruction, which is faster. Unlike the Power4 branch hint
+ encoding, this is not backward compatible. Do not generate the
+ new form unless -mpower4 has been given, or -many and the two
+ operand form of mfcr was used. */
+ else if ((value & -value) == value
+ && ((dialect & PPC_OPCODE_POWER4) != 0
+ || ((dialect & PPC_OPCODE_ANY) != 0
+ && (insn & (0x3ff << 1)) == 19 << 1)))
+ insn |= 1 << 20;
+
+ /* Any other value on mfcr is an error. */
+ else if ((insn & (0x3ff << 1)) == 19 << 1)
+ {
+ *errmsg = _("ignoring invalid mfcr mask");
+ value = 0;
+ }
+
+ return insn | ((value & 0xff) << 12);
+}
+
+static long
+extract_fxm (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid)
+{
+ long mask = (insn >> 12) & 0xff;
+
+ /* Is this a Power4 insn? */
+ if ((insn & (1 << 20)) != 0)
+ {
+ /* Exactly one bit of MASK should be set. */
+ if (mask == 0 || (mask & -mask) != mask)
+ *invalid = 1;
+ }
+
+ /* Check that non-power4 form of mfcr has a zero MASK. */
+ else if ((insn & (0x3ff << 1)) == 19 << 1)
+ {
+ if (mask != 0)
+ *invalid = 1;
+ }
+
+ return mask;
+}
+
+/* The MB and ME fields in an M form instruction expressed as a single
+ operand which is itself a bitmask. The extraction function always
+ marks it as invalid, since we never want to recognize an
+ instruction which uses a field of this type. */
+
+static unsigned long
+insert_mbe (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg)
+{
+ unsigned long uval, mask;
+ int mb, me, mx, count, last;
+
+ uval = value;
+
+ if (uval == 0)
+ {
+ *errmsg = _("illegal bitmask");
+ return insn;
+ }
+
+ mb = 0;
+ me = 32;
+ if ((uval & 1) != 0)
+ last = 1;
+ else
+ last = 0;
+ count = 0;
+
+ /* mb: location of last 0->1 transition */
+ /* me: location of last 1->0 transition */
+ /* count: # transitions */
+
+ for (mx = 0, mask = 1L << 31; mx < 32; ++mx, mask >>= 1)
+ {
+ if ((uval & mask) && !last)
+ {
+ ++count;
+ mb = mx;
+ last = 1;
+ }
+ else if (!(uval & mask) && last)
+ {
+ ++count;
+ me = mx;
+ last = 0;
+ }
+ }
+ if (me == 0)
+ me = 32;
+
+ if (count != 2 && (count != 0 || ! last))
+ *errmsg = _("illegal bitmask");
+
+ return insn | (mb << 6) | ((me - 1) << 1);
+}
+
+static long
+extract_mbe (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid)
+{
+ long ret;
+ int mb, me;
+ int i;
+
+ *invalid = 1;
+
+ mb = (insn >> 6) & 0x1f;
+ me = (insn >> 1) & 0x1f;
+ if (mb < me + 1)
+ {
+ ret = 0;
+ for (i = mb; i <= me; i++)
+ ret |= 1L << (31 - i);
+ }
+ else if (mb == me + 1)
+ ret = ~0;
+ else /* (mb > me + 1) */
+ {
+ ret = ~0;
+ for (i = me + 1; i < mb; i++)
+ ret &= ~(1L << (31 - i));
+ }
+ return ret;
+}
+
+/* The MB or ME field in an MD or MDS form instruction. The high bit
+ is wrapped to the low end. */
+
+static unsigned long
+insert_mb6 (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | ((value & 0x1f) << 6) | (value & 0x20);
+}
+
+static long
+extract_mb6 (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid ATTRIBUTE_UNUSED)
+{
+ return ((insn >> 6) & 0x1f) | (insn & 0x20);
+}
+
+/* The NB field in an X form instruction. The value 32 is stored as
+ 0. */
+
+static long
+extract_nb (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid ATTRIBUTE_UNUSED)
+{
+ long ret;
+
+ ret = (insn >> 11) & 0x1f;
+ if (ret == 0)
+ ret = 32;
+ return ret;
+}
+
+/* The NSI field in a D form instruction. This is the same as the SI
+ field, only negated. The extraction function always marks it as
+ invalid, since we never want to recognize an instruction which uses
+ a field of this type. */
+
+static unsigned long
+insert_nsi (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (-value & 0xffff);
+}
+
+static long
+extract_nsi (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid)
+{
+ *invalid = 1;
+ return -(((insn & 0xffff) ^ 0x8000) - 0x8000);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+ load, which means that the RA field may not be zero and may not
+ equal the RT field. */
+
+static unsigned long
+insert_ral (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg)
+{
+ if (value == 0
+ || (unsigned long) value == ((insn >> 21) & 0x1f))
+ *errmsg = "invalid register operand when updating";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in an lmw instruction, which has special value
+ restrictions. */
+
+static unsigned long
+insert_ram (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg)
+{
+ if ((unsigned long) value >= ((insn >> 21) & 0x1f))
+ *errmsg = _("index register in load range");
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in the DQ form lq instruction, which has special
+ value restrictions. */
+
+static unsigned long
+insert_raq (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg)
+{
+ long rtvalue = (insn & RT_MASK) >> 21;
+
+ if (value == rtvalue)
+ *errmsg = _("source and target register operands must be different");
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+ store or an updating floating point load, which means that the RA
+ field may not be zero. */
+
+static unsigned long
+insert_ras (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg)
+{
+ if (value == 0)
+ *errmsg = _("invalid register operand when updating");
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RB field in an X form instruction when it must be the same as
+ the RS field in the instruction. This is used for extended
+ mnemonics like mr. This operand is marked FAKE. The insertion
+ function just copies the BT field into the BA field, and the
+ extraction function just checks that the fields are the same. */
+
+static unsigned long
+insert_rbs (unsigned long insn,
+ long value ATTRIBUTE_UNUSED,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | (((insn >> 21) & 0x1f) << 11);
+}
+
+static long
+extract_rbs (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid)
+{
+ if (((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The SH field in an MD form instruction. This is split. */
+
+static unsigned long
+insert_sh6 (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
+}
+
+static long
+extract_sh6 (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid ATTRIBUTE_UNUSED)
+{
+ return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20);
+}
+
+/* The SPR field in an XFX form instruction. This is flipped--the
+ lower 5 bits are stored in the upper 5 and vice- versa. */
+
+static unsigned long
+insert_spr (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_spr (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid ATTRIBUTE_UNUSED)
+{
+ return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+}
+
+/* Some dialects have 8 SPRG registers instead of the standard 4. */
+
+static unsigned long
+insert_sprg (unsigned long insn,
+ long value,
+ int dialect,
+ const char **errmsg)
+{
+ /* This check uses PPC_OPCODE_403 because PPC405 is later defined
+ as a synonym. If ever a 405 specific dialect is added this
+ check should use that instead. */
+ if (value > 7
+ || (value > 3
+ && (dialect & (PPC_OPCODE_BOOKE | PPC_OPCODE_403)) == 0))
+ *errmsg = _("invalid sprg number");
+
+ /* If this is mfsprg4..7 then use spr 260..263 which can be read in
+ user mode. Anything else must use spr 272..279. */
+ if (value <= 3 || (insn & 0x100) != 0)
+ value |= 0x10;
+
+ return insn | ((value & 0x17) << 16);
+}
+
+static long
+extract_sprg (unsigned long insn,
+ int dialect,
+ int *invalid)
+{
+ unsigned long val = (insn >> 16) & 0x1f;
+
+ /* mfsprg can use 260..263 and 272..279. mtsprg only uses spr 272..279
+ If not BOOKE or 405, then both use only 272..275. */
+ if (val <= 3
+ || (val < 0x10 && (insn & 0x100) != 0)
+ || (val - 0x10 > 3
+ && (dialect & (PPC_OPCODE_BOOKE | PPC_OPCODE_403)) == 0))
+ *invalid = 1;
+ return val & 7;
+}
+
+/* The TBR field in an XFX instruction. This is just like SPR, but it
+ is optional. When TBR is omitted, it must be inserted as 268 (the
+ magic number of the TB register). These functions treat 0
+ (indicating an omitted optional operand) as 268. This means that
+ ``mftb 4,0'' is not handled correctly. This does not matter very
+ much, since the architecture manual does not define mftb as
+ accepting any values other than 268 or 269. */
+
+#define TB (268)
+
+static unsigned long
+insert_tbr (unsigned long insn,
+ long value,
+ int dialect ATTRIBUTE_UNUSED,
+ const char **errmsg ATTRIBUTE_UNUSED)
+{
+ if (value == 0)
+ value = TB;
+ return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_tbr (unsigned long insn,
+ int dialect ATTRIBUTE_UNUSED,
+ int *invalid ATTRIBUTE_UNUSED)
+{
+ long ret;
+
+ ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+ if (ret == TB)
+ ret = 0;
+ return ret;
+}
+
+/* Macros used to form opcodes. */
+
+/* The main opcode. */
+#define OP(x) ((((unsigned long)(x)) & 0x3f) << 26)
+#define OP_MASK OP (0x3f)
+
+/* The main opcode combined with a trap code in the TO field of a D
+ form instruction. Used for extended mnemonics for the trap
+ instructions. */
+#define OPTO(x,to) (OP (x) | ((((unsigned long)(to)) & 0x1f) << 21))
+#define OPTO_MASK (OP_MASK | TO_MASK)
+
+/* The main opcode combined with a comparison size bit in the L field
+ of a D form or X form instruction. Used for extended mnemonics for
+ the comparison instructions. */
+#define OPL(x,l) (OP (x) | ((((unsigned long)(l)) & 1) << 21))
+#define OPL_MASK OPL (0x3f,1)
+
+/* An A form instruction. */
+#define A(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1) | (((unsigned long)(rc)) & 1))
+#define A_MASK A (0x3f, 0x1f, 1)
+
+/* An A_MASK with the FRB field fixed. */
+#define AFRB_MASK (A_MASK | FRB_MASK)
+
+/* An A_MASK with the FRC field fixed. */
+#define AFRC_MASK (A_MASK | FRC_MASK)
+
+/* An A_MASK with the FRA and FRC fields fixed. */
+#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK)
+
+/* An AFRAFRC_MASK, but with L bit clear. */
+#define AFRALFRC_MASK (AFRAFRC_MASK & ~((unsigned long) 1 << 16))
+
+/* A B form instruction. */
+#define B(op, aa, lk) (OP (op) | ((((unsigned long)(aa)) & 1) << 1) | ((lk) & 1))
+#define B_MASK B (0x3f, 1, 1)
+
+/* A B form instruction setting the BO field. */
+#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21))
+#define BBO_MASK BBO (0x3f, 0x1f, 1, 1)
+
+/* A BBO_MASK with the y bit of the BO field removed. This permits
+ matching a conditional branch regardless of the setting of the y
+ bit. Similarly for the 'at' bits used for power4 branch hints. */
+#define Y_MASK (((unsigned long) 1) << 21)
+#define AT1_MASK (((unsigned long) 3) << 21)
+#define AT2_MASK (((unsigned long) 9) << 21)
+#define BBOY_MASK (BBO_MASK &~ Y_MASK)
+#define BBOAT_MASK (BBO_MASK &~ AT1_MASK)
+
+/* A B form instruction setting the BO field and the condition bits of
+ the BI field. */
+#define BBOCB(op, bo, cb, aa, lk) \
+ (BBO ((op), (bo), (aa), (lk)) | ((((unsigned long)(cb)) & 0x3) << 16))
+#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1)
+
+/* A BBOCB_MASK with the y bit of the BO field removed. */
+#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK)
+#define BBOATCB_MASK (BBOCB_MASK &~ AT1_MASK)
+#define BBOAT2CB_MASK (BBOCB_MASK &~ AT2_MASK)
+
+/* A BBOYCB_MASK in which the BI field is fixed. */
+#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
+#define BBOATBI_MASK (BBOAT2CB_MASK | BI_MASK)
+
+/* An Context form instruction. */
+#define CTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7))
+#define CTX_MASK CTX(0x3f, 0x7)
+
+/* An User Context form instruction. */
+#define UCTX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f))
+#define UCTX_MASK UCTX(0x3f, 0x1f)
+
+/* The main opcode mask with the RA field clear. */
+#define DRA_MASK (OP_MASK | RA_MASK)
+
+/* A DS form instruction. */
+#define DSO(op, xop) (OP (op) | ((xop) & 0x3))
+#define DS_MASK DSO (0x3f, 3)
+
+/* A DE form instruction. */
+#define DEO(op, xop) (OP (op) | ((xop) & 0xf))
+#define DE_MASK DEO (0x3e, 0xf)
+
+/* An EVSEL form instruction. */
+#define EVSEL(op, xop) (OP (op) | (((unsigned long)(xop)) & 0xff) << 3)
+#define EVSEL_MASK EVSEL(0x3f, 0xff)
+
+/* An M form instruction. */
+#define M(op, rc) (OP (op) | ((rc) & 1))
+#define M_MASK M (0x3f, 1)
+
+/* An M form instruction with the ME field specified. */
+#define MME(op, me, rc) (M ((op), (rc)) | ((((unsigned long)(me)) & 0x1f) << 1))
+
+/* An M_MASK with the MB and ME fields fixed. */
+#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK)
+
+/* An M_MASK with the SH and ME fields fixed. */
+#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK)
+
+/* An MD form instruction. */
+#define MD(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x7) << 2) | ((rc) & 1))
+#define MD_MASK MD (0x3f, 0x7, 1)
+
+/* An MD_MASK with the MB field fixed. */
+#define MDMB_MASK (MD_MASK | MB6_MASK)
+
+/* An MD_MASK with the SH field fixed. */
+#define MDSH_MASK (MD_MASK | SH6_MASK)
+
+/* An MDS form instruction. */
+#define MDS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0xf) << 1) | ((rc) & 1))
+#define MDS_MASK MDS (0x3f, 0xf, 1)
+
+/* An MDS_MASK with the MB field fixed. */
+#define MDSMB_MASK (MDS_MASK | MB6_MASK)
+
+/* An SC form instruction. */
+#define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1))
+#define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1)
+
+/* An VX form instruction. */
+#define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff))
+
+/* The mask for an VX form instruction. */
+#define VX_MASK VX(0x3f, 0x7ff)
+
+/* An VA form instruction. */
+#define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x03f))
+
+/* The mask for an VA form instruction. */
+#define VXA_MASK VXA(0x3f, 0x3f)
+
+/* An VXR form instruction. */
+#define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff))
+
+/* The mask for a VXR form instruction. */
+#define VXR_MASK VXR(0x3f, 0x3ff, 1)
+
+/* An X form instruction. */
+#define X(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1))
+
+/* A Z form instruction. */
+#define Z(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1))
+
+/* An X form instruction with the RC bit specified. */
+#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1))
+
+/* A Z form instruction with the RC bit specified. */
+#define ZRC(op, xop, rc) (Z ((op), (xop)) | ((rc) & 1))
+
+/* The mask for an X form instruction. */
+#define X_MASK XRC (0x3f, 0x3ff, 1)
+
+/* The mask for a Z form instruction. */
+#define Z_MASK ZRC (0x3f, 0x1ff, 1)
+#define Z2_MASK ZRC (0x3f, 0xff, 1)
+
+/* An X_MASK with the RA field fixed. */
+#define XRA_MASK (X_MASK | RA_MASK)
+
+/* An XRA_MASK with the W field clear. */
+#define XWRA_MASK (XRA_MASK & ~((unsigned long) 1 << 16))
+
+/* An X_MASK with the RB field fixed. */
+#define XRB_MASK (X_MASK | RB_MASK)
+
+/* An X_MASK with the RT field fixed. */
+#define XRT_MASK (X_MASK | RT_MASK)
+
+/* An XRT_MASK mask with the L bits clear. */
+#define XLRT_MASK (XRT_MASK & ~((unsigned long) 0x3 << 21))
+
+/* An X_MASK with the RA and RB fields fixed. */
+#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK)
+
+/* An XRARB_MASK, but with the L bit clear. */
+#define XRLARB_MASK (XRARB_MASK & ~((unsigned long) 1 << 16))
+
+/* An X_MASK with the RT and RA fields fixed. */
+#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK)
+
+/* An XRTRA_MASK, but with L bit clear. */
+#define XRTLRA_MASK (XRTRA_MASK & ~((unsigned long) 1 << 21))
+
+/* An X form instruction with the L bit specified. */
+#define XOPL(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 1) << 21))
+
+/* The mask for an X form comparison instruction. */
+#define XCMP_MASK (X_MASK | (((unsigned long)1) << 22))
+
+/* The mask for an X form comparison instruction with the L field
+ fixed. */
+#define XCMPL_MASK (XCMP_MASK | (((unsigned long)1) << 21))
+
+/* An X form trap instruction with the TO field specified. */
+#define XTO(op, xop, to) (X ((op), (xop)) | ((((unsigned long)(to)) & 0x1f) << 21))
+#define XTO_MASK (X_MASK | TO_MASK)
+
+/* An X form tlb instruction with the SH field specified. */
+#define XTLB(op, xop, sh) (X ((op), (xop)) | ((((unsigned long)(sh)) & 0x1f) << 11))
+#define XTLB_MASK (X_MASK | SH_MASK)
+
+/* An X form sync instruction. */
+#define XSYNC(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 3) << 21))
+
+/* An X form sync instruction with everything filled in except the LS field. */
+#define XSYNC_MASK (0xff9fffff)
+
+/* An X_MASK, but with the EH bit clear. */
+#define XEH_MASK (X_MASK & ~((unsigned long )1))
+
+/* An X form AltiVec dss instruction. */
+#define XDSS(op, xop, a) (X ((op), (xop)) | ((((unsigned long)(a)) & 1) << 25))
+#define XDSS_MASK XDSS(0x3f, 0x3ff, 1)
+
+/* An XFL form instruction. */
+#define XFL(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1) | (((unsigned long)(rc)) & 1))
+#define XFL_MASK XFL (0x3f, 0x3ff, 1)
+
+/* An X form isel instruction. */
+#define XISEL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1))
+#define XISEL_MASK XISEL(0x3f, 0x1f)
+
+/* An XL form instruction with the LK field set to 0. */
+#define XL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1))
+
+/* An XL form instruction which uses the LK field. */
+#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1))
+
+/* The mask for an XL form instruction. */
+#define XL_MASK XLLK (0x3f, 0x3ff, 1)
+
+/* An XL form instruction which explicitly sets the BO field. */
+#define XLO(op, bo, xop, lk) \
+ (XLLK ((op), (xop), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21))
+#define XLO_MASK (XL_MASK | BO_MASK)
+
+/* An XL form instruction which explicitly sets the y bit of the BO
+ field. */
+#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | ((((unsigned long)(y)) & 1) << 21))
+#define XLYLK_MASK (XL_MASK | Y_MASK)
+
+/* An XL form instruction which sets the BO field and the condition
+ bits of the BI field. */
+#define XLOCB(op, bo, cb, xop, lk) \
+ (XLO ((op), (bo), (xop), (lk)) | ((((unsigned long)(cb)) & 3) << 16))
+#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1)
+
+/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */
+#define XLBB_MASK (XL_MASK | BB_MASK)
+#define XLYBB_MASK (XLYLK_MASK | BB_MASK)
+#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK)
+
+/* A mask for branch instructions using the BH field. */
+#define XLBH_MASK (XL_MASK | (0x1c << 11))
+
+/* An XL_MASK with the BO and BB fields fixed. */
+#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK)
+
+/* An XL_MASK with the BO, BI and BB fields fixed. */
+#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK)
+
+/* An XO form instruction. */
+#define XO(op, xop, oe, rc) \
+ (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1) | ((((unsigned long)(oe)) & 1) << 10) | (((unsigned long)(rc)) & 1))
+#define XO_MASK XO (0x3f, 0x1ff, 1, 1)
+
+/* An XO_MASK with the RB field fixed. */
+#define XORB_MASK (XO_MASK | RB_MASK)
+
+/* An XS form instruction. */
+#define XS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 2) | (((unsigned long)(rc)) & 1))
+#define XS_MASK XS (0x3f, 0x1ff, 1)
+
+/* A mask for the FXM version of an XFX form instruction. */
+#define XFXFXM_MASK (X_MASK | (1 << 11) | (1 << 20))
+
+/* An XFX form instruction with the FXM field filled in. */
+#define XFXM(op, xop, fxm, p4) \
+ (X ((op), (xop)) | ((((unsigned long)(fxm)) & 0xff) << 12) \
+ | ((unsigned long)(p4) << 20))
+
+/* An XFX form instruction with the SPR field filled in. */
+#define XSPR(op, xop, spr) \
+ (X ((op), (xop)) | ((((unsigned long)(spr)) & 0x1f) << 16) | ((((unsigned long)(spr)) & 0x3e0) << 6))
+#define XSPR_MASK (X_MASK | SPR_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+ SPRBAT field. */
+#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+ SPRG field. */
+#define XSPRG_MASK (XSPR_MASK & ~(0x1f << 16))
+
+/* An X form instruction with everything filled in except the E field. */
+#define XE_MASK (0xffff7fff)
+
+/* An X form user context instruction. */
+#define XUC(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x1f))
+#define XUC_MASK XUC(0x3f, 0x1f)
+
+/* The BO encodings used in extended conditional branch mnemonics. */
+#define BODNZF (0x0)
+#define BODNZFP (0x1)
+#define BODZF (0x2)
+#define BODZFP (0x3)
+#define BODNZT (0x8)
+#define BODNZTP (0x9)
+#define BODZT (0xa)
+#define BODZTP (0xb)
+
+#define BOF (0x4)
+#define BOFP (0x5)
+#define BOFM4 (0x6)
+#define BOFP4 (0x7)
+#define BOT (0xc)
+#define BOTP (0xd)
+#define BOTM4 (0xe)
+#define BOTP4 (0xf)
+
+#define BODNZ (0x10)
+#define BODNZP (0x11)
+#define BODZ (0x12)
+#define BODZP (0x13)
+#define BODNZM4 (0x18)
+#define BODNZP4 (0x19)
+#define BODZM4 (0x1a)
+#define BODZP4 (0x1b)
+
+#define BOU (0x14)
+
+/* The BI condition bit encodings used in extended conditional branch
+ mnemonics. */
+#define CBLT (0)
+#define CBGT (1)
+#define CBEQ (2)
+#define CBSO (3)
+
+/* The TO encodings used in extended trap mnemonics. */
+#define TOLGT (0x1)
+#define TOLLT (0x2)
+#define TOEQ (0x4)
+#define TOLGE (0x5)
+#define TOLNL (0x5)
+#define TOLLE (0x6)
+#define TOLNG (0x6)
+#define TOGT (0x8)
+#define TOGE (0xc)
+#define TONL (0xc)
+#define TOLT (0x10)
+#define TOLE (0x14)
+#define TONG (0x14)
+#define TONE (0x18)
+#define TOU (0x1f)
+
+/* Smaller names for the flags so each entry in the opcodes table will
+ fit on a single line. */
+#undef PPC
+#define PPC PPC_OPCODE_PPC
+#define PPCCOM PPC_OPCODE_PPC | PPC_OPCODE_COMMON
+#define NOPOWER4 PPC_OPCODE_NOPOWER4 | PPCCOM
+#define POWER4 PPC_OPCODE_POWER4
+#define POWER5 PPC_OPCODE_POWER5
+#define POWER6 PPC_OPCODE_POWER6
+#define CELL PPC_OPCODE_CELL
+#define PPC32 PPC_OPCODE_32 | PPC_OPCODE_PPC
+#define PPC64 PPC_OPCODE_64 | PPC_OPCODE_PPC
+#define PPC403 PPC_OPCODE_403
+#define PPC405 PPC403
+#define PPC440 PPC_OPCODE_440
+#define PPC750 PPC
+#define PPC860 PPC
+#define PPCVEC PPC_OPCODE_ALTIVEC
+#define POWER PPC_OPCODE_POWER
+#define POWER2 PPC_OPCODE_POWER | PPC_OPCODE_POWER2
+#define PPCPWR2 PPC_OPCODE_PPC | PPC_OPCODE_POWER | PPC_OPCODE_POWER2
+#define POWER32 PPC_OPCODE_POWER | PPC_OPCODE_32
+#define COM PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON
+#define COM32 PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_32
+#define M601 PPC_OPCODE_POWER | PPC_OPCODE_601
+#define PWRCOM PPC_OPCODE_POWER | PPC_OPCODE_601 | PPC_OPCODE_COMMON
+#define MFDEC1 PPC_OPCODE_POWER
+#define MFDEC2 PPC_OPCODE_PPC | PPC_OPCODE_601 | PPC_OPCODE_BOOKE
+#define BOOKE PPC_OPCODE_BOOKE
+#define BOOKE64 PPC_OPCODE_BOOKE64
+#define CLASSIC PPC_OPCODE_CLASSIC
+#define PPCE300 PPC_OPCODE_E300
+#define PPCSPE PPC_OPCODE_SPE
+#define PPCISEL PPC_OPCODE_ISEL
+#define PPCEFS PPC_OPCODE_EFS
+#define PPCBRLK PPC_OPCODE_BRLOCK
+#define PPCPMR PPC_OPCODE_PMR
+#define PPCCHLK PPC_OPCODE_CACHELCK
+#define PPCCHLK64 PPC_OPCODE_CACHELCK | PPC_OPCODE_BOOKE64
+#define PPCRFMCI PPC_OPCODE_RFMCI
+
+/* The opcode table.
+
+ The format of the opcode table is:
+
+ NAME OPCODE MASK FLAGS { OPERANDS }
+
+ NAME is the name of the instruction.
+ OPCODE is the instruction opcode.
+ MASK is the opcode mask; this is used to tell the disassembler
+ which bits in the actual opcode must match OPCODE.
+ FLAGS are flags indicated what processors support the instruction.
+ OPERANDS is the list of operands.
+
+ The disassembler reads the table in order and prints the first
+ instruction which matches, so this table is sorted to put more
+ specific instructions before more general instructions. It is also
+ sorted by major opcode. */
+
+const struct powerpc_opcode powerpc_opcodes[] = {
+{ "attn", X(0,256), X_MASK, POWER4, { 0 } },
+{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC64, { RA, SI } },
+{ "tdi", OP(2), OP_MASK, PPC64, { TO, RA, SI } },
+
+{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tllti", OPTO(3,TOLLT), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "teqi", OPTO(3,TOEQ), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tllei", OPTO(3,TOLLE), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tgti", OPTO(3,TOGT), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tgei", OPTO(3,TOGE), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twnli", OPTO(3,TONL), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tnli", OPTO(3,TONL), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tlti", OPTO(3,TOLT), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tlei", OPTO(3,TOLE), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twngi", OPTO(3,TONG), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tngi", OPTO(3,TONG), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twnei", OPTO(3,TONE), OPTO_MASK, PPCCOM, { RA, SI } },
+{ "tnei", OPTO(3,TONE), OPTO_MASK, PWRCOM, { RA, SI } },
+{ "twi", OP(3), OP_MASK, PPCCOM, { TO, RA, SI } },
+{ "ti", OP(3), OP_MASK, PWRCOM, { TO, RA, SI } },
+
+{ "macchw", XO(4,172,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchw.", XO(4,172,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwo", XO(4,172,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwo.", XO(4,172,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchws", XO(4,236,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchws.", XO(4,236,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwso", XO(4,236,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwso.", XO(4,236,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwsu", XO(4,204,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwsu.", XO(4,204,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwsuo", XO(4,204,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwsuo.", XO(4,204,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwu", XO(4,140,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwu.", XO(4,140,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwuo", XO(4,140,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "macchwuo.", XO(4,140,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhw", XO(4,44,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhw.", XO(4,44,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwo", XO(4,44,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwo.", XO(4,44,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhws", XO(4,108,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhws.", XO(4,108,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwso", XO(4,108,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwso.", XO(4,108,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwsu", XO(4,76,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwsu.", XO(4,76,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwsuo", XO(4,76,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwsuo.", XO(4,76,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwu", XO(4,12,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwu.", XO(4,12,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwuo", XO(4,12,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "machhwuo.", XO(4,12,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhw", XO(4,428,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhw.", XO(4,428,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwo", XO(4,428,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwo.", XO(4,428,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhws", XO(4,492,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhws.", XO(4,492,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwso", XO(4,492,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwso.", XO(4,492,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwsu", XO(4,460,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwsu.", XO(4,460,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwsuo", XO(4,460,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwsuo.", XO(4,460,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwu", XO(4,396,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwu.", XO(4,396,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwuo", XO(4,396,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "maclhwuo.", XO(4,396,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulchw", XRC(4,168,0), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulchw.", XRC(4,168,1), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulchwu", XRC(4,136,0), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulchwu.", XRC(4,136,1), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulhhw", XRC(4,40,0), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulhhw.", XRC(4,40,1), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulhhwu", XRC(4,8,0), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mulhhwu.", XRC(4,8,1), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mullhw", XRC(4,424,0), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mullhw.", XRC(4,424,1), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mullhwu", XRC(4,392,0), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mullhwu.", XRC(4,392,1), X_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchw", XO(4,174,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchw.", XO(4,174,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchwo", XO(4,174,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchwo.", XO(4,174,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchws", XO(4,238,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchws.", XO(4,238,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchwso", XO(4,238,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmacchwso.", XO(4,238,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhw", XO(4,46,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhw.", XO(4,46,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhwo", XO(4,46,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhwo.", XO(4,46,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhws", XO(4,110,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhws.", XO(4,110,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhwso", XO(4,110,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmachhwso.", XO(4,110,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhw", XO(4,430,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhw.", XO(4,430,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhwo", XO(4,430,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhwo.", XO(4,430,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhws", XO(4,494,0,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhws.", XO(4,494,0,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhwso", XO(4,494,1,0), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "nmaclhwso.", XO(4,494,1,1), XO_MASK, PPC405|PPC440, { RT, RA, RB } },
+{ "mfvscr", VX(4, 1540), VX_MASK, PPCVEC, { VD } },
+{ "mtvscr", VX(4, 1604), VX_MASK, PPCVEC, { VB } },
+
+ /* Double-precision opcodes. */
+ /* Some of these conflict with AltiVec, so move them before, since
+ PPCVEC includes the PPC_OPCODE_PPC set. */
+{ "efscfd", VX(4, 719), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdabs", VX(4, 740), VX_MASK, PPCEFS, { RS, RA } },
+{ "efdnabs", VX(4, 741), VX_MASK, PPCEFS, { RS, RA } },
+{ "efdneg", VX(4, 742), VX_MASK, PPCEFS, { RS, RA } },
+{ "efdadd", VX(4, 736), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efdsub", VX(4, 737), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efdmul", VX(4, 744), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efddiv", VX(4, 745), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efdcmpgt", VX(4, 748), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efdcmplt", VX(4, 749), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efdcmpeq", VX(4, 750), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efdtstgt", VX(4, 764), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efdtstlt", VX(4, 765), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efdtsteq", VX(4, 766), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efdcfsi", VX(4, 753), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdcfsid", VX(4, 739), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdcfui", VX(4, 752), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdcfuid", VX(4, 738), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdcfsf", VX(4, 755), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdcfuf", VX(4, 754), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctsi", VX(4, 757), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctsidz",VX(4, 747), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctsiz", VX(4, 762), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctui", VX(4, 756), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctuidz",VX(4, 746), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctuiz", VX(4, 760), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctsf", VX(4, 759), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdctuf", VX(4, 758), VX_MASK, PPCEFS, { RS, RB } },
+{ "efdcfs", VX(4, 751), VX_MASK, PPCEFS, { RS, RB } },
+ /* End of double-precision opcodes. */
+
+{ "vaddcuw", VX(4, 384), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vaddfp", VX(4, 10), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vaddsbs", VX(4, 768), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vaddshs", VX(4, 832), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vaddsws", VX(4, 896), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vaddubm", VX(4, 0), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vaddubs", VX(4, 512), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vadduhm", VX(4, 64), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vadduhs", VX(4, 576), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vadduwm", VX(4, 128), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vadduws", VX(4, 640), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vand", VX(4, 1028), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vandc", VX(4, 1092), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vavgsb", VX(4, 1282), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vavgsh", VX(4, 1346), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vavgsw", VX(4, 1410), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vavgub", VX(4, 1026), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vavguh", VX(4, 1090), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vavguw", VX(4, 1154), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcfsx", VX(4, 842), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vcfux", VX(4, 778), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vcmpbfp", VXR(4, 966, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpbfp.", VXR(4, 966, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpeqfp", VXR(4, 198, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpeqfp.", VXR(4, 198, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpequb", VXR(4, 6, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpequb.", VXR(4, 6, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpequh", VXR(4, 70, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpequh.", VXR(4, 70, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpequw", VXR(4, 134, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpequw.", VXR(4, 134, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgefp", VXR(4, 454, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgefp.", VXR(4, 454, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtfp", VXR(4, 710, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtfp.", VXR(4, 710, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtsb", VXR(4, 774, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtsb.", VXR(4, 774, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtsh", VXR(4, 838, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtsh.", VXR(4, 838, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtsw", VXR(4, 902, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtsw.", VXR(4, 902, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtub", VXR(4, 518, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtub.", VXR(4, 518, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtuh", VXR(4, 582, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtuh.", VXR(4, 582, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtuw", VXR(4, 646, 0), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vcmpgtuw.", VXR(4, 646, 1), VXR_MASK, PPCVEC, { VD, VA, VB } },
+{ "vctsxs", VX(4, 970), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vctuxs", VX(4, 906), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vexptefp", VX(4, 394), VX_MASK, PPCVEC, { VD, VB } },
+{ "vlogefp", VX(4, 458), VX_MASK, PPCVEC, { VD, VB } },
+{ "vmaddfp", VXA(4, 46), VXA_MASK, PPCVEC, { VD, VA, VC, VB } },
+{ "vmaxfp", VX(4, 1034), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmaxsb", VX(4, 258), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmaxsh", VX(4, 322), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmaxsw", VX(4, 386), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmaxub", VX(4, 2), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmaxuh", VX(4, 66), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmaxuw", VX(4, 130), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmhaddshs", VXA(4, 32), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmhraddshs", VXA(4, 33), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vminfp", VX(4, 1098), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vminsb", VX(4, 770), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vminsh", VX(4, 834), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vminsw", VX(4, 898), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vminub", VX(4, 514), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vminuh", VX(4, 578), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vminuw", VX(4, 642), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmladduhm", VXA(4, 34), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmrghb", VX(4, 12), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmrghh", VX(4, 76), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmrghw", VX(4, 140), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmrglb", VX(4, 268), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmrglh", VX(4, 332), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmrglw", VX(4, 396), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmsummbm", VXA(4, 37), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmsumshm", VXA(4, 40), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmsumshs", VXA(4, 41), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmsumubm", VXA(4, 36), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmsumuhm", VXA(4, 38), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmsumuhs", VXA(4, 39), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vmulesb", VX(4, 776), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmulesh", VX(4, 840), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmuleub", VX(4, 520), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmuleuh", VX(4, 584), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmulosb", VX(4, 264), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmulosh", VX(4, 328), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmuloub", VX(4, 8), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vmulouh", VX(4, 72), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vnmsubfp", VXA(4, 47), VXA_MASK, PPCVEC, { VD, VA, VC, VB } },
+{ "vnor", VX(4, 1284), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vor", VX(4, 1156), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vperm", VXA(4, 43), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vpkpx", VX(4, 782), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkshss", VX(4, 398), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkshus", VX(4, 270), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkswss", VX(4, 462), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkswus", VX(4, 334), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkuhum", VX(4, 14), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkuhus", VX(4, 142), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkuwum", VX(4, 78), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vpkuwus", VX(4, 206), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vrefp", VX(4, 266), VX_MASK, PPCVEC, { VD, VB } },
+{ "vrfim", VX(4, 714), VX_MASK, PPCVEC, { VD, VB } },
+{ "vrfin", VX(4, 522), VX_MASK, PPCVEC, { VD, VB } },
+{ "vrfip", VX(4, 650), VX_MASK, PPCVEC, { VD, VB } },
+{ "vrfiz", VX(4, 586), VX_MASK, PPCVEC, { VD, VB } },
+{ "vrlb", VX(4, 4), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vrlh", VX(4, 68), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vrlw", VX(4, 132), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vrsqrtefp", VX(4, 330), VX_MASK, PPCVEC, { VD, VB } },
+{ "vsel", VXA(4, 42), VXA_MASK, PPCVEC, { VD, VA, VB, VC } },
+{ "vsl", VX(4, 452), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vslb", VX(4, 260), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsldoi", VXA(4, 44), VXA_MASK, PPCVEC, { VD, VA, VB, SHB } },
+{ "vslh", VX(4, 324), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vslo", VX(4, 1036), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vslw", VX(4, 388), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vspltb", VX(4, 524), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vsplth", VX(4, 588), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vspltisb", VX(4, 780), VX_MASK, PPCVEC, { VD, SIMM } },
+{ "vspltish", VX(4, 844), VX_MASK, PPCVEC, { VD, SIMM } },
+{ "vspltisw", VX(4, 908), VX_MASK, PPCVEC, { VD, SIMM } },
+{ "vspltw", VX(4, 652), VX_MASK, PPCVEC, { VD, VB, UIMM } },
+{ "vsr", VX(4, 708), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsrab", VX(4, 772), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsrah", VX(4, 836), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsraw", VX(4, 900), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsrb", VX(4, 516), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsrh", VX(4, 580), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsro", VX(4, 1100), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsrw", VX(4, 644), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubcuw", VX(4, 1408), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubfp", VX(4, 74), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubsbs", VX(4, 1792), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubshs", VX(4, 1856), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubsws", VX(4, 1920), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsububm", VX(4, 1024), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsububs", VX(4, 1536), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubuhm", VX(4, 1088), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubuhs", VX(4, 1600), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubuwm", VX(4, 1152), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsubuws", VX(4, 1664), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsumsws", VX(4, 1928), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsum2sws", VX(4, 1672), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsum4sbs", VX(4, 1800), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsum4shs", VX(4, 1608), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vsum4ubs", VX(4, 1544), VX_MASK, PPCVEC, { VD, VA, VB } },
+{ "vupkhpx", VX(4, 846), VX_MASK, PPCVEC, { VD, VB } },
+{ "vupkhsb", VX(4, 526), VX_MASK, PPCVEC, { VD, VB } },
+{ "vupkhsh", VX(4, 590), VX_MASK, PPCVEC, { VD, VB } },
+{ "vupklpx", VX(4, 974), VX_MASK, PPCVEC, { VD, VB } },
+{ "vupklsb", VX(4, 654), VX_MASK, PPCVEC, { VD, VB } },
+{ "vupklsh", VX(4, 718), VX_MASK, PPCVEC, { VD, VB } },
+{ "vxor", VX(4, 1220), VX_MASK, PPCVEC, { VD, VA, VB } },
+
+{ "evaddw", VX(4, 512), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evaddiw", VX(4, 514), VX_MASK, PPCSPE, { RS, RB, UIMM } },
+{ "evsubfw", VX(4, 516), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evsubw", VX(4, 516), VX_MASK, PPCSPE, { RS, RB, RA } },
+{ "evsubifw", VX(4, 518), VX_MASK, PPCSPE, { RS, UIMM, RB } },
+{ "evsubiw", VX(4, 518), VX_MASK, PPCSPE, { RS, RB, UIMM } },
+{ "evabs", VX(4, 520), VX_MASK, PPCSPE, { RS, RA } },
+{ "evneg", VX(4, 521), VX_MASK, PPCSPE, { RS, RA } },
+{ "evextsb", VX(4, 522), VX_MASK, PPCSPE, { RS, RA } },
+{ "evextsh", VX(4, 523), VX_MASK, PPCSPE, { RS, RA } },
+{ "evrndw", VX(4, 524), VX_MASK, PPCSPE, { RS, RA } },
+{ "evcntlzw", VX(4, 525), VX_MASK, PPCSPE, { RS, RA } },
+{ "evcntlsw", VX(4, 526), VX_MASK, PPCSPE, { RS, RA } },
+
+{ "brinc", VX(4, 527), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evand", VX(4, 529), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evandc", VX(4, 530), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmr", VX(4, 535), VX_MASK, PPCSPE, { RS, RA, BBA } },
+{ "evor", VX(4, 535), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evorc", VX(4, 539), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evxor", VX(4, 534), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "eveqv", VX(4, 537), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evnand", VX(4, 542), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evnot", VX(4, 536), VX_MASK, PPCSPE, { RS, RA, BBA } },
+{ "evnor", VX(4, 536), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evrlw", VX(4, 552), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evrlwi", VX(4, 554), VX_MASK, PPCSPE, { RS, RA, EVUIMM } },
+{ "evslw", VX(4, 548), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evslwi", VX(4, 550), VX_MASK, PPCSPE, { RS, RA, EVUIMM } },
+{ "evsrws", VX(4, 545), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evsrwu", VX(4, 544), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evsrwis", VX(4, 547), VX_MASK, PPCSPE, { RS, RA, EVUIMM } },
+{ "evsrwiu", VX(4, 546), VX_MASK, PPCSPE, { RS, RA, EVUIMM } },
+{ "evsplati", VX(4, 553), VX_MASK, PPCSPE, { RS, SIMM } },
+{ "evsplatfi", VX(4, 555), VX_MASK, PPCSPE, { RS, SIMM } },
+{ "evmergehi", VX(4, 556), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmergelo", VX(4, 557), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmergehilo",VX(4,558), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmergelohi",VX(4,559), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evcmpgts", VX(4, 561), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evcmpgtu", VX(4, 560), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evcmplts", VX(4, 563), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evcmpltu", VX(4, 562), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evcmpeq", VX(4, 564), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evsel", EVSEL(4,79),EVSEL_MASK, PPCSPE, { RS, RA, RB, CRFS } },
+
+{ "evldd", VX(4, 769), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } },
+{ "evlddx", VX(4, 768), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evldw", VX(4, 771), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } },
+{ "evldwx", VX(4, 770), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evldh", VX(4, 773), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } },
+{ "evldhx", VX(4, 772), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlwhe", VX(4, 785), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evlwhex", VX(4, 784), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlwhou", VX(4, 789), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evlwhoux", VX(4, 788), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlwhos", VX(4, 791), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evlwhosx", VX(4, 790), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlwwsplat",VX(4, 793), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evlwwsplatx",VX(4, 792), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlwhsplat",VX(4, 797), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evlwhsplatx",VX(4, 796), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlhhesplat",VX(4, 777), VX_MASK, PPCSPE, { RS, EVUIMM_2, RA } },
+{ "evlhhesplatx",VX(4, 776), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlhhousplat",VX(4, 781), VX_MASK, PPCSPE, { RS, EVUIMM_2, RA } },
+{ "evlhhousplatx",VX(4, 780), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evlhhossplat",VX(4, 783), VX_MASK, PPCSPE, { RS, EVUIMM_2, RA } },
+{ "evlhhossplatx",VX(4, 782), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evstdd", VX(4, 801), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } },
+{ "evstddx", VX(4, 800), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evstdw", VX(4, 803), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } },
+{ "evstdwx", VX(4, 802), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evstdh", VX(4, 805), VX_MASK, PPCSPE, { RS, EVUIMM_8, RA } },
+{ "evstdhx", VX(4, 804), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evstwwe", VX(4, 825), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evstwwex", VX(4, 824), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evstwwo", VX(4, 829), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evstwwox", VX(4, 828), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evstwhe", VX(4, 817), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evstwhex", VX(4, 816), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evstwho", VX(4, 821), VX_MASK, PPCSPE, { RS, EVUIMM_4, RA } },
+{ "evstwhox", VX(4, 820), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evfsabs", VX(4, 644), VX_MASK, PPCSPE, { RS, RA } },
+{ "evfsnabs", VX(4, 645), VX_MASK, PPCSPE, { RS, RA } },
+{ "evfsneg", VX(4, 646), VX_MASK, PPCSPE, { RS, RA } },
+{ "evfsadd", VX(4, 640), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evfssub", VX(4, 641), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evfsmul", VX(4, 648), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evfsdiv", VX(4, 649), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evfscmpgt", VX(4, 652), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evfscmplt", VX(4, 653), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evfscmpeq", VX(4, 654), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evfststgt", VX(4, 668), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evfststlt", VX(4, 669), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evfststeq", VX(4, 670), VX_MASK, PPCSPE, { CRFD, RA, RB } },
+{ "evfscfui", VX(4, 656), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfsctuiz", VX(4, 664), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfscfsi", VX(4, 657), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfscfuf", VX(4, 658), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfscfsf", VX(4, 659), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfsctui", VX(4, 660), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfsctsi", VX(4, 661), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfsctsiz", VX(4, 666), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfsctuf", VX(4, 662), VX_MASK, PPCSPE, { RS, RB } },
+{ "evfsctsf", VX(4, 663), VX_MASK, PPCSPE, { RS, RB } },
+
+{ "efsabs", VX(4, 708), VX_MASK, PPCEFS, { RS, RA } },
+{ "efsnabs", VX(4, 709), VX_MASK, PPCEFS, { RS, RA } },
+{ "efsneg", VX(4, 710), VX_MASK, PPCEFS, { RS, RA } },
+{ "efsadd", VX(4, 704), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efssub", VX(4, 705), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efsmul", VX(4, 712), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efsdiv", VX(4, 713), VX_MASK, PPCEFS, { RS, RA, RB } },
+{ "efscmpgt", VX(4, 716), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efscmplt", VX(4, 717), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efscmpeq", VX(4, 718), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efststgt", VX(4, 732), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efststlt", VX(4, 733), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efststeq", VX(4, 734), VX_MASK, PPCEFS, { CRFD, RA, RB } },
+{ "efscfui", VX(4, 720), VX_MASK, PPCEFS, { RS, RB } },
+{ "efsctuiz", VX(4, 728), VX_MASK, PPCEFS, { RS, RB } },
+{ "efscfsi", VX(4, 721), VX_MASK, PPCEFS, { RS, RB } },
+{ "efscfuf", VX(4, 722), VX_MASK, PPCEFS, { RS, RB } },
+{ "efscfsf", VX(4, 723), VX_MASK, PPCEFS, { RS, RB } },
+{ "efsctui", VX(4, 724), VX_MASK, PPCEFS, { RS, RB } },
+{ "efsctsi", VX(4, 725), VX_MASK, PPCEFS, { RS, RB } },
+{ "efsctsiz", VX(4, 730), VX_MASK, PPCEFS, { RS, RB } },
+{ "efsctuf", VX(4, 726), VX_MASK, PPCEFS, { RS, RB } },
+{ "efsctsf", VX(4, 727), VX_MASK, PPCEFS, { RS, RB } },
+
+{ "evmhossf", VX(4, 1031), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhossfa", VX(4, 1063), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmf", VX(4, 1039), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmfa", VX(4, 1071), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmi", VX(4, 1037), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmia", VX(4, 1069), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhoumi", VX(4, 1036), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhoumia", VX(4, 1068), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhessf", VX(4, 1027), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhessfa", VX(4, 1059), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmf", VX(4, 1035), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmfa", VX(4, 1067), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmi", VX(4, 1033), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmia", VX(4, 1065), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmheumi", VX(4, 1032), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmheumia", VX(4, 1064), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmhossfaaw",VX(4, 1287), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhossiaaw",VX(4, 1285), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmfaaw",VX(4, 1295), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmiaaw",VX(4, 1293), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhousiaaw",VX(4, 1284), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhoumiaaw",VX(4, 1292), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhessfaaw",VX(4, 1283), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhessiaaw",VX(4, 1281), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmfaaw",VX(4, 1291), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmiaaw",VX(4, 1289), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmheusiaaw",VX(4, 1280), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmheumiaaw",VX(4, 1288), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmhossfanw",VX(4, 1415), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhossianw",VX(4, 1413), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmfanw",VX(4, 1423), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhosmianw",VX(4, 1421), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhousianw",VX(4, 1412), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhoumianw",VX(4, 1420), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhessfanw",VX(4, 1411), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhessianw",VX(4, 1409), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmfanw",VX(4, 1419), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhesmianw",VX(4, 1417), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmheusianw",VX(4, 1408), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmheumianw",VX(4, 1416), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmhogsmfaa",VX(4, 1327), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhogsmiaa",VX(4, 1325), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhogumiaa",VX(4, 1324), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhegsmfaa",VX(4, 1323), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhegsmiaa",VX(4, 1321), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhegumiaa",VX(4, 1320), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmhogsmfan",VX(4, 1455), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhogsmian",VX(4, 1453), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhogumian",VX(4, 1452), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhegsmfan",VX(4, 1451), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhegsmian",VX(4, 1449), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmhegumian",VX(4, 1448), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwhssf", VX(4, 1095), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhssfa", VX(4, 1127), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhsmf", VX(4, 1103), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhsmfa", VX(4, 1135), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhsmi", VX(4, 1101), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhsmia", VX(4, 1133), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhumi", VX(4, 1100), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwhumia", VX(4, 1132), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwlumi", VX(4, 1096), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlumia", VX(4, 1128), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwlssiaaw",VX(4, 1345), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlsmiaaw",VX(4, 1353), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlusiaaw",VX(4, 1344), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlumiaaw",VX(4, 1352), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwlssianw",VX(4, 1473), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlsmianw",VX(4, 1481), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlusianw",VX(4, 1472), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwlumianw",VX(4, 1480), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwssf", VX(4, 1107), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwssfa", VX(4, 1139), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmf", VX(4, 1115), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmfa", VX(4, 1147), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmi", VX(4, 1113), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmia", VX(4, 1145), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwumi", VX(4, 1112), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwumia", VX(4, 1144), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwssfaa", VX(4, 1363), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmfaa", VX(4, 1371), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmiaa", VX(4, 1369), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwumiaa", VX(4, 1368), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evmwssfan", VX(4, 1491), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmfan", VX(4, 1499), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwsmian", VX(4, 1497), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evmwumian", VX(4, 1496), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "evaddssiaaw",VX(4, 1217), VX_MASK, PPCSPE, { RS, RA } },
+{ "evaddsmiaaw",VX(4, 1225), VX_MASK, PPCSPE, { RS, RA } },
+{ "evaddusiaaw",VX(4, 1216), VX_MASK, PPCSPE, { RS, RA } },
+{ "evaddumiaaw",VX(4, 1224), VX_MASK, PPCSPE, { RS, RA } },
+
+{ "evsubfssiaaw",VX(4, 1219), VX_MASK, PPCSPE, { RS, RA } },
+{ "evsubfsmiaaw",VX(4, 1227), VX_MASK, PPCSPE, { RS, RA } },
+{ "evsubfusiaaw",VX(4, 1218), VX_MASK, PPCSPE, { RS, RA } },
+{ "evsubfumiaaw",VX(4, 1226), VX_MASK, PPCSPE, { RS, RA } },
+
+{ "evmra", VX(4, 1220), VX_MASK, PPCSPE, { RS, RA } },
+
+{ "evdivws", VX(4, 1222), VX_MASK, PPCSPE, { RS, RA, RB } },
+{ "evdivwu", VX(4, 1223), VX_MASK, PPCSPE, { RS, RA, RB } },
+
+{ "mulli", OP(7), OP_MASK, PPCCOM, { RT, RA, SI } },
+{ "muli", OP(7), OP_MASK, PWRCOM, { RT, RA, SI } },
+
+{ "subfic", OP(8), OP_MASK, PPCCOM, { RT, RA, SI } },
+{ "sfi", OP(8), OP_MASK, PWRCOM, { RT, RA, SI } },
+
+{ "dozi", OP(9), OP_MASK, M601, { RT, RA, SI } },
+
+{ "bce", B(9,0,0), B_MASK, BOOKE64, { BO, BI, BD } },
+{ "bcel", B(9,0,1), B_MASK, BOOKE64, { BO, BI, BD } },
+{ "bcea", B(9,1,0), B_MASK, BOOKE64, { BO, BI, BDA } },
+{ "bcela", B(9,1,1), B_MASK, BOOKE64, { BO, BI, BDA } },
+
+{ "cmplwi", OPL(10,0), OPL_MASK, PPCCOM, { OBF, RA, UI } },
+{ "cmpldi", OPL(10,1), OPL_MASK, PPC64, { OBF, RA, UI } },
+{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } },
+{ "cmpli", OP(10), OP_MASK, PWRCOM, { BF, RA, UI } },
+
+{ "cmpwi", OPL(11,0), OPL_MASK, PPCCOM, { OBF, RA, SI } },
+{ "cmpdi", OPL(11,1), OPL_MASK, PPC64, { OBF, RA, SI } },
+{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } },
+{ "cmpi", OP(11), OP_MASK, PWRCOM, { BF, RA, SI } },
+
+{ "addic", OP(12), OP_MASK, PPCCOM, { RT, RA, SI } },
+{ "ai", OP(12), OP_MASK, PWRCOM, { RT, RA, SI } },
+{ "subic", OP(12), OP_MASK, PPCCOM, { RT, RA, NSI } },
+
+{ "addic.", OP(13), OP_MASK, PPCCOM, { RT, RA, SI } },
+{ "ai.", OP(13), OP_MASK, PWRCOM, { RT, RA, SI } },
+{ "subic.", OP(13), OP_MASK, PPCCOM, { RT, RA, NSI } },
+
+{ "li", OP(14), DRA_MASK, PPCCOM, { RT, SI } },
+{ "lil", OP(14), DRA_MASK, PWRCOM, { RT, SI } },
+{ "addi", OP(14), OP_MASK, PPCCOM, { RT, RA0, SI } },
+{ "cal", OP(14), OP_MASK, PWRCOM, { RT, D, RA0 } },
+{ "subi", OP(14), OP_MASK, PPCCOM, { RT, RA0, NSI } },
+{ "la", OP(14), OP_MASK, PPCCOM, { RT, D, RA0 } },
+
+{ "lis", OP(15), DRA_MASK, PPCCOM, { RT, SISIGNOPT } },
+{ "liu", OP(15), DRA_MASK, PWRCOM, { RT, SISIGNOPT } },
+{ "addis", OP(15), OP_MASK, PPCCOM, { RT,RA0,SISIGNOPT } },
+{ "cau", OP(15), OP_MASK, PWRCOM, { RT,RA0,SISIGNOPT } },
+{ "subis", OP(15), OP_MASK, PPCCOM, { RT, RA0, NSI } },
+
+{ "bdnz-", BBO(16,BODNZ,0,0), BBOATBI_MASK, PPCCOM, { BDM } },
+{ "bdnz+", BBO(16,BODNZ,0,0), BBOATBI_MASK, PPCCOM, { BDP } },
+{ "bdnz", BBO(16,BODNZ,0,0), BBOATBI_MASK, PPCCOM, { BD } },
+{ "bdn", BBO(16,BODNZ,0,0), BBOATBI_MASK, PWRCOM, { BD } },
+{ "bdnzl-", BBO(16,BODNZ,0,1), BBOATBI_MASK, PPCCOM, { BDM } },
+{ "bdnzl+", BBO(16,BODNZ,0,1), BBOATBI_MASK, PPCCOM, { BDP } },
+{ "bdnzl", BBO(16,BODNZ,0,1), BBOATBI_MASK, PPCCOM, { BD } },
+{ "bdnl", BBO(16,BODNZ,0,1), BBOATBI_MASK, PWRCOM, { BD } },
+{ "bdnza-", BBO(16,BODNZ,1,0), BBOATBI_MASK, PPCCOM, { BDMA } },
+{ "bdnza+", BBO(16,BODNZ,1,0), BBOATBI_MASK, PPCCOM, { BDPA } },
+{ "bdnza", BBO(16,BODNZ,1,0), BBOATBI_MASK, PPCCOM, { BDA } },
+{ "bdna", BBO(16,BODNZ,1,0), BBOATBI_MASK, PWRCOM, { BDA } },
+{ "bdnzla-", BBO(16,BODNZ,1,1), BBOATBI_MASK, PPCCOM, { BDMA } },
+{ "bdnzla+", BBO(16,BODNZ,1,1), BBOATBI_MASK, PPCCOM, { BDPA } },
+{ "bdnzla", BBO(16,BODNZ,1,1), BBOATBI_MASK, PPCCOM, { BDA } },
+{ "bdnla", BBO(16,BODNZ,1,1), BBOATBI_MASK, PWRCOM, { BDA } },
+{ "bdz-", BBO(16,BODZ,0,0), BBOATBI_MASK, PPCCOM, { BDM } },
+{ "bdz+", BBO(16,BODZ,0,0), BBOATBI_MASK, PPCCOM, { BDP } },
+{ "bdz", BBO(16,BODZ,0,0), BBOATBI_MASK, COM, { BD } },
+{ "bdzl-", BBO(16,BODZ,0,1), BBOATBI_MASK, PPCCOM, { BDM } },
+{ "bdzl+", BBO(16,BODZ,0,1), BBOATBI_MASK, PPCCOM, { BDP } },
+{ "bdzl", BBO(16,BODZ,0,1), BBOATBI_MASK, COM, { BD } },
+{ "bdza-", BBO(16,BODZ,1,0), BBOATBI_MASK, PPCCOM, { BDMA } },
+{ "bdza+", BBO(16,BODZ,1,0), BBOATBI_MASK, PPCCOM, { BDPA } },
+{ "bdza", BBO(16,BODZ,1,0), BBOATBI_MASK, COM, { BDA } },
+{ "bdzla-", BBO(16,BODZ,1,1), BBOATBI_MASK, PPCCOM, { BDMA } },
+{ "bdzla+", BBO(16,BODZ,1,1), BBOATBI_MASK, PPCCOM, { BDPA } },
+{ "bdzla", BBO(16,BODZ,1,1), BBOATBI_MASK, COM, { BDA } },
+{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BD } },
+{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BD } },
+{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDA } },
+{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDA } },
+{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, COM, { CR, BD } },
+{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, COM, { CR, BDA } },
+{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOATCB_MASK, PPCCOM, { CR, BD } },
+{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDM } },
+{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BDP } },
+{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOATCB_MASK, PPCCOM, { CR, BD } },
+{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOATCB_MASK, PPCCOM, { CR, BDA } },
+{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDMA } },
+{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDPA } },
+{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOATCB_MASK, PPCCOM, { CR, BDA } },
+{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bt-", BBO(16,BOT,0,0), BBOAT_MASK, PPCCOM, { BI, BDM } },
+{ "bt+", BBO(16,BOT,0,0), BBOAT_MASK, PPCCOM, { BI, BDP } },
+{ "bt", BBO(16,BOT,0,0), BBOAT_MASK, PPCCOM, { BI, BD } },
+{ "bbt", BBO(16,BOT,0,0), BBOAT_MASK, PWRCOM, { BI, BD } },
+{ "btl-", BBO(16,BOT,0,1), BBOAT_MASK, PPCCOM, { BI, BDM } },
+{ "btl+", BBO(16,BOT,0,1), BBOAT_MASK, PPCCOM, { BI, BDP } },
+{ "btl", BBO(16,BOT,0,1), BBOAT_MASK, PPCCOM, { BI, BD } },
+{ "bbtl", BBO(16,BOT,0,1), BBOAT_MASK, PWRCOM, { BI, BD } },
+{ "bta-", BBO(16,BOT,1,0), BBOAT_MASK, PPCCOM, { BI, BDMA } },
+{ "bta+", BBO(16,BOT,1,0), BBOAT_MASK, PPCCOM, { BI, BDPA } },
+{ "bta", BBO(16,BOT,1,0), BBOAT_MASK, PPCCOM, { BI, BDA } },
+{ "bbta", BBO(16,BOT,1,0), BBOAT_MASK, PWRCOM, { BI, BDA } },
+{ "btla-", BBO(16,BOT,1,1), BBOAT_MASK, PPCCOM, { BI, BDMA } },
+{ "btla+", BBO(16,BOT,1,1), BBOAT_MASK, PPCCOM, { BI, BDPA } },
+{ "btla", BBO(16,BOT,1,1), BBOAT_MASK, PPCCOM, { BI, BDA } },
+{ "bbtla", BBO(16,BOT,1,1), BBOAT_MASK, PWRCOM, { BI, BDA } },
+{ "bf-", BBO(16,BOF,0,0), BBOAT_MASK, PPCCOM, { BI, BDM } },
+{ "bf+", BBO(16,BOF,0,0), BBOAT_MASK, PPCCOM, { BI, BDP } },
+{ "bf", BBO(16,BOF,0,0), BBOAT_MASK, PPCCOM, { BI, BD } },
+{ "bbf", BBO(16,BOF,0,0), BBOAT_MASK, PWRCOM, { BI, BD } },
+{ "bfl-", BBO(16,BOF,0,1), BBOAT_MASK, PPCCOM, { BI, BDM } },
+{ "bfl+", BBO(16,BOF,0,1), BBOAT_MASK, PPCCOM, { BI, BDP } },
+{ "bfl", BBO(16,BOF,0,1), BBOAT_MASK, PPCCOM, { BI, BD } },
+{ "bbfl", BBO(16,BOF,0,1), BBOAT_MASK, PWRCOM, { BI, BD } },
+{ "bfa-", BBO(16,BOF,1,0), BBOAT_MASK, PPCCOM, { BI, BDMA } },
+{ "bfa+", BBO(16,BOF,1,0), BBOAT_MASK, PPCCOM, { BI, BDPA } },
+{ "bfa", BBO(16,BOF,1,0), BBOAT_MASK, PPCCOM, { BI, BDA } },
+{ "bbfa", BBO(16,BOF,1,0), BBOAT_MASK, PWRCOM, { BI, BDA } },
+{ "bfla-", BBO(16,BOF,1,1), BBOAT_MASK, PPCCOM, { BI, BDMA } },
+{ "bfla+", BBO(16,BOF,1,1), BBOAT_MASK, PPCCOM, { BI, BDPA } },
+{ "bfla", BBO(16,BOF,1,1), BBOAT_MASK, PPCCOM, { BI, BDA } },
+{ "bbfla", BBO(16,BOF,1,1), BBOAT_MASK, PWRCOM, { BI, BDA } },
+{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDM } },
+{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, NOPOWER4, { BI, BDP } },
+{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } },
+{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDMA } },
+{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, NOPOWER4, { BI, BDPA } },
+{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } },
+{ "bc-", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDM } },
+{ "bc+", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDP } },
+{ "bc", B(16,0,0), B_MASK, COM, { BO, BI, BD } },
+{ "bcl-", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDM } },
+{ "bcl+", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDP } },
+{ "bcl", B(16,0,1), B_MASK, COM, { BO, BI, BD } },
+{ "bca-", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDMA } },
+{ "bca+", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDPA } },
+{ "bca", B(16,1,0), B_MASK, COM, { BO, BI, BDA } },
+{ "bcla-", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDMA } },
+{ "bcla+", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDPA } },
+{ "bcla", B(16,1,1), B_MASK, COM, { BO, BI, BDA } },
+
+{ "sc", SC(17,1,0), SC_MASK, PPC, { LEV } },
+{ "svc", SC(17,0,0), SC_MASK, POWER, { SVC_LEV, FL1, FL2 } },
+{ "svcl", SC(17,0,1), SC_MASK, POWER, { SVC_LEV, FL1, FL2 } },
+{ "svca", SC(17,1,0), SC_MASK, PWRCOM, { SV } },
+{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } },
+
+{ "b", B(18,0,0), B_MASK, COM, { LI } },
+{ "bl", B(18,0,1), B_MASK, COM, { LI } },
+{ "ba", B(18,1,0), B_MASK, COM, { LIA } },
+{ "bla", B(18,1,1), B_MASK, COM, { LIA } },
+
+{ "mcrf", XL(19,0), XLBB_MASK|(3 << 21)|(3 << 16), COM, { BF, BFA } },
+
+{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } },
+{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, PWRCOM, { 0 } },
+{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } },
+{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PWRCOM, { 0 } },
+{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } },
+{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdnzlr-", XLO(19,BODNZM4,16,0), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdnzlr+", XLO(19,BODNZP4,16,0), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } },
+{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdnzlrl-",XLO(19,BODNZM4,16,1), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdnzlrl+",XLO(19,BODNZP4,16,1), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } },
+{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdzlr-", XLO(19,BODZM4,16,0), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdzlr+", XLO(19,BODZP4,16,0), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } },
+{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdzlrl-", XLO(19,BODZM4,16,1), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, NOPOWER4, { 0 } },
+{ "bdzlrl+", XLO(19,BODZP4,16,1), XLBOBIBB_MASK, POWER4, { 0 } },
+{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltlr-", XLOCB(19,BOTM4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltlr+", XLOCB(19,BOTP4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltlrl-", XLOCB(19,BOTM4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltlrl+", XLOCB(19,BOTP4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtlr-", XLOCB(19,BOTM4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtlr+", XLOCB(19,BOTP4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtlrl-", XLOCB(19,BOTM4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtlrl+", XLOCB(19,BOTP4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqlr-", XLOCB(19,BOTM4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqlr+", XLOCB(19,BOTP4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqlrl-", XLOCB(19,BOTM4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqlrl+", XLOCB(19,BOTP4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsolr-", XLOCB(19,BOTM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsolr+", XLOCB(19,BOTP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsolrl-", XLOCB(19,BOTM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsolrl+", XLOCB(19,BOTP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunlr-", XLOCB(19,BOTM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunlr+", XLOCB(19,BOTP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunlrl-", XLOCB(19,BOTM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunlrl+", XLOCB(19,BOTP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgelr-", XLOCB(19,BOFM4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgelr+", XLOCB(19,BOFP4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgelrl-", XLOCB(19,BOFM4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgelrl+", XLOCB(19,BOFP4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnllr-", XLOCB(19,BOFM4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnllr+", XLOCB(19,BOFP4,CBLT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnllrl-", XLOCB(19,BOFM4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnllrl+", XLOCB(19,BOFP4,CBLT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blelr-", XLOCB(19,BOFM4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blelr+", XLOCB(19,BOFP4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blelrl-", XLOCB(19,BOFM4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blelrl+", XLOCB(19,BOFP4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnglr-", XLOCB(19,BOFM4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnglr+", XLOCB(19,BOFP4,CBGT,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnglrl-", XLOCB(19,BOFM4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnglrl+", XLOCB(19,BOFP4,CBGT,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnelr-", XLOCB(19,BOFM4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnelr+", XLOCB(19,BOFP4,CBEQ,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnelrl-", XLOCB(19,BOFM4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnelrl+", XLOCB(19,BOFP4,CBEQ,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnslr-", XLOCB(19,BOFM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnslr+", XLOCB(19,BOFP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnslrl-", XLOCB(19,BOFM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnslrl+", XLOCB(19,BOFP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } },
+{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnulr-", XLOCB(19,BOFM4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnulr+", XLOCB(19,BOFP4,CBSO,16,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnulrl-", XLOCB(19,BOFM4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnulrl+", XLOCB(19,BOFP4,CBSO,16,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btlr-", XLO(19,BOTM4,16,0), XLBOBB_MASK, POWER4, { BI } },
+{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btlr+", XLO(19,BOTP4,16,0), XLBOBB_MASK, POWER4, { BI } },
+{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, PWRCOM, { BI } },
+{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btlrl-", XLO(19,BOTM4,16,1), XLBOBB_MASK, POWER4, { BI } },
+{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btlrl+", XLO(19,BOTP4,16,1), XLBOBB_MASK, POWER4, { BI } },
+{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, PWRCOM, { BI } },
+{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bflr-", XLO(19,BOFM4,16,0), XLBOBB_MASK, POWER4, { BI } },
+{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bflr+", XLO(19,BOFP4,16,0), XLBOBB_MASK, POWER4, { BI } },
+{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, PWRCOM, { BI } },
+{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bflrl-", XLO(19,BOFM4,16,1), XLBOBB_MASK, POWER4, { BI } },
+{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bflrl+", XLO(19,BOFP4,16,1), XLBOBB_MASK, POWER4, { BI } },
+{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, PWRCOM, { BI } },
+{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bclr", XLLK(19,16,0), XLBH_MASK, PPCCOM, { BO, BI, BH } },
+{ "bclrl", XLLK(19,16,1), XLBH_MASK, PPCCOM, { BO, BI, BH } },
+{ "bcr", XLLK(19,16,0), XLBB_MASK, PWRCOM, { BO, BI } },
+{ "bcrl", XLLK(19,16,1), XLBB_MASK, PWRCOM, { BO, BI } },
+{ "bclre", XLLK(19,17,0), XLBB_MASK, BOOKE64, { BO, BI } },
+{ "bclrel", XLLK(19,17,1), XLBB_MASK, BOOKE64, { BO, BI } },
+
+{ "rfid", XL(19,18), 0xffffffff, PPC64, { 0 } },
+
+{ "crnot", XL(19,33), XL_MASK, PPCCOM, { BT, BA, BBA } },
+{ "crnor", XL(19,33), XL_MASK, COM, { BT, BA, BB } },
+{ "rfmci", X(19,38), 0xffffffff, PPCRFMCI, { 0 } },
+
+{ "rfi", XL(19,50), 0xffffffff, COM, { 0 } },
+{ "rfci", XL(19,51), 0xffffffff, PPC403 | BOOKE, { 0 } },
+
+{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } },
+
+{ "crandc", XL(19,129), XL_MASK, COM, { BT, BA, BB } },
+
+{ "isync", XL(19,150), 0xffffffff, PPCCOM, { 0 } },
+{ "ics", XL(19,150), 0xffffffff, PWRCOM, { 0 } },
+
+{ "crclr", XL(19,193), XL_MASK, PPCCOM, { BT, BAT, BBA } },
+{ "crxor", XL(19,193), XL_MASK, COM, { BT, BA, BB } },
+
+{ "crnand", XL(19,225), XL_MASK, COM, { BT, BA, BB } },
+
+{ "crand", XL(19,257), XL_MASK, COM, { BT, BA, BB } },
+
+{ "hrfid", XL(19,274), 0xffffffff, POWER5 | CELL, { 0 } },
+
+{ "crset", XL(19,289), XL_MASK, PPCCOM, { BT, BAT, BBA } },
+{ "creqv", XL(19,289), XL_MASK, COM, { BT, BA, BB } },
+
+{ "doze", XL(19,402), 0xffffffff, POWER6, { 0 } },
+
+{ "crorc", XL(19,417), XL_MASK, COM, { BT, BA, BB } },
+
+{ "nap", XL(19,434), 0xffffffff, POWER6, { 0 } },
+
+{ "crmove", XL(19,449), XL_MASK, PPCCOM, { BT, BA, BBA } },
+{ "cror", XL(19,449), XL_MASK, COM, { BT, BA, BB } },
+
+{ "sleep", XL(19,466), 0xffffffff, POWER6, { 0 } },
+{ "rvwinkle", XL(19,498), 0xffffffff, POWER6, { 0 } },
+
+{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, COM, { 0 } },
+{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, COM, { 0 } },
+{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltctr-", XLOCB(19,BOTM4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltctr+", XLOCB(19,BOTP4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltctrl-",XLOCB(19,BOTM4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bltctrl+",XLOCB(19,BOTP4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtctr-", XLOCB(19,BOTM4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtctr+", XLOCB(19,BOTP4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtctrl-",XLOCB(19,BOTM4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgtctrl+",XLOCB(19,BOTP4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqctr-", XLOCB(19,BOTM4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqctr+", XLOCB(19,BOTP4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqctrl-",XLOCB(19,BOTM4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "beqctrl+",XLOCB(19,BOTP4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsoctr-", XLOCB(19,BOTM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsoctr+", XLOCB(19,BOTP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsoctrl-",XLOCB(19,BOTM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bsoctrl+",XLOCB(19,BOTP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunctr-", XLOCB(19,BOTM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunctr+", XLOCB(19,BOTP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunctrl-",XLOCB(19,BOTM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bunctrl+",XLOCB(19,BOTP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgectr-", XLOCB(19,BOFM4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgectr+", XLOCB(19,BOFP4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgectrl-",XLOCB(19,BOFM4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bgectrl+",XLOCB(19,BOFP4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnlctr-", XLOCB(19,BOFM4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnlctr+", XLOCB(19,BOFP4,CBLT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnlctrl-",XLOCB(19,BOFM4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnlctrl+",XLOCB(19,BOFP4,CBLT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blectr-", XLOCB(19,BOFM4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blectr+", XLOCB(19,BOFP4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blectrl-",XLOCB(19,BOFM4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "blectrl+",XLOCB(19,BOFP4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bngctr-", XLOCB(19,BOFM4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bngctr+", XLOCB(19,BOFP4,CBGT,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bngctrl-",XLOCB(19,BOFM4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bngctrl+",XLOCB(19,BOFP4,CBGT,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnectr-", XLOCB(19,BOFM4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnectr+", XLOCB(19,BOFP4,CBEQ,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnectrl-",XLOCB(19,BOFM4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnectrl+",XLOCB(19,BOFP4,CBEQ,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnsctr-", XLOCB(19,BOFM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnsctr+", XLOCB(19,BOFP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnsctrl-",XLOCB(19,BOFM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnsctrl+",XLOCB(19,BOFP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnuctr-", XLOCB(19,BOFM4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnuctr+", XLOCB(19,BOFP4,CBSO,528,0), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } },
+{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnuctrl-",XLOCB(19,BOFM4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, NOPOWER4, { CR } },
+{ "bnuctrl+",XLOCB(19,BOFP4,CBSO,528,1), XLBOCBBB_MASK, POWER4, { CR } },
+{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btctr-", XLO(19,BOTM4,528,0), XLBOBB_MASK, POWER4, { BI } },
+{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btctr+", XLO(19,BOTP4,528,0), XLBOBB_MASK, POWER4, { BI } },
+{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btctrl-", XLO(19,BOTM4,528,1), XLBOBB_MASK, POWER4, { BI } },
+{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "btctrl+", XLO(19,BOTP4,528,1), XLBOBB_MASK, POWER4, { BI } },
+{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bfctr-", XLO(19,BOFM4,528,0), XLBOBB_MASK, POWER4, { BI } },
+{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bfctr+", XLO(19,BOFP4,528,0), XLBOBB_MASK, POWER4, { BI } },
+{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPCCOM, { BI } },
+{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bfctrl-", XLO(19,BOFM4,528,1), XLBOBB_MASK, POWER4, { BI } },
+{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, NOPOWER4, { BI } },
+{ "bfctrl+", XLO(19,BOFP4,528,1), XLBOBB_MASK, POWER4, { BI } },
+{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } },
+{ "bcctr", XLLK(19,528,0), XLBH_MASK, PPCCOM, { BO, BI, BH } },
+{ "bcctrl", XLLK(19,528,1), XLBH_MASK, PPCCOM, { BO, BI, BH } },
+{ "bcc", XLLK(19,528,0), XLBB_MASK, PWRCOM, { BO, BI } },
+{ "bccl", XLLK(19,528,1), XLBB_MASK, PWRCOM, { BO, BI } },
+{ "bcctre", XLLK(19,529,0), XLBB_MASK, BOOKE64, { BO, BI } },
+{ "bcctrel", XLLK(19,529,1), XLBB_MASK, BOOKE64, { BO, BI } },
+
+{ "rlwimi", M(20,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } },
+{ "rlimi", M(20,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } },
+
+{ "rlwimi.", M(20,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } },
+{ "rlimi.", M(20,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } },
+
+{ "rotlwi", MME(21,31,0), MMBME_MASK, PPCCOM, { RA, RS, SH } },
+{ "clrlwi", MME(21,31,0), MSHME_MASK, PPCCOM, { RA, RS, MB } },
+{ "rlwinm", M(21,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } },
+{ "rlinm", M(21,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } },
+{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPCCOM, { RA,RS,SH } },
+{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPCCOM, { RA, RS, MB } },
+{ "rlwinm.", M(21,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } },
+{ "rlinm.", M(21,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } },
+
+{ "rlmi", M(22,0), M_MASK, M601, { RA,RS,RB,MBE,ME } },
+{ "rlmi.", M(22,1), M_MASK, M601, { RA,RS,RB,MBE,ME } },
+
+{ "be", B(22,0,0), B_MASK, BOOKE64, { LI } },
+{ "bel", B(22,0,1), B_MASK, BOOKE64, { LI } },
+{ "bea", B(22,1,0), B_MASK, BOOKE64, { LIA } },
+{ "bela", B(22,1,1), B_MASK, BOOKE64, { LIA } },
+
+{ "rotlw", MME(23,31,0), MMBME_MASK, PPCCOM, { RA, RS, RB } },
+{ "rlwnm", M(23,0), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } },
+{ "rlnm", M(23,0), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } },
+{ "rotlw.", MME(23,31,1), MMBME_MASK, PPCCOM, { RA, RS, RB } },
+{ "rlwnm.", M(23,1), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } },
+{ "rlnm.", M(23,1), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } },
+
+{ "nop", OP(24), 0xffffffff, PPCCOM, { 0 } },
+{ "ori", OP(24), OP_MASK, PPCCOM, { RA, RS, UI } },
+{ "oril", OP(24), OP_MASK, PWRCOM, { RA, RS, UI } },
+
+{ "oris", OP(25), OP_MASK, PPCCOM, { RA, RS, UI } },
+{ "oriu", OP(25), OP_MASK, PWRCOM, { RA, RS, UI } },
+
+{ "xori", OP(26), OP_MASK, PPCCOM, { RA, RS, UI } },
+{ "xoril", OP(26), OP_MASK, PWRCOM, { RA, RS, UI } },
+
+{ "xoris", OP(27), OP_MASK, PPCCOM, { RA, RS, UI } },
+{ "xoriu", OP(27), OP_MASK, PWRCOM, { RA, RS, UI } },
+
+{ "andi.", OP(28), OP_MASK, PPCCOM, { RA, RS, UI } },
+{ "andil.", OP(28), OP_MASK, PWRCOM, { RA, RS, UI } },
+
+{ "andis.", OP(29), OP_MASK, PPCCOM, { RA, RS, UI } },
+{ "andiu.", OP(29), OP_MASK, PWRCOM, { RA, RS, UI } },
+
+{ "rotldi", MD(30,0,0), MDMB_MASK, PPC64, { RA, RS, SH6 } },
+{ "clrldi", MD(30,0,0), MDSH_MASK, PPC64, { RA, RS, MB6 } },
+{ "rldicl", MD(30,0,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } },
+{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC64, { RA, RS, SH6 } },
+{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC64, { RA, RS, MB6 } },
+{ "rldicl.", MD(30,0,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } },
+
+{ "rldicr", MD(30,1,0), MD_MASK, PPC64, { RA, RS, SH6, ME6 } },
+{ "rldicr.", MD(30,1,1), MD_MASK, PPC64, { RA, RS, SH6, ME6 } },
+
+{ "rldic", MD(30,2,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } },
+{ "rldic.", MD(30,2,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } },
+
+{ "rldimi", MD(30,3,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } },
+{ "rldimi.", MD(30,3,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } },
+
+{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC64, { RA, RS, RB } },
+{ "rldcl", MDS(30,8,0), MDS_MASK, PPC64, { RA, RS, RB, MB6 } },
+{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC64, { RA, RS, RB } },
+{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC64, { RA, RS, RB, MB6 } },
+
+{ "rldcr", MDS(30,9,0), MDS_MASK, PPC64, { RA, RS, RB, ME6 } },
+{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC64, { RA, RS, RB, ME6 } },
+
+{ "cmpw", XOPL(31,0,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } },
+{ "cmpd", XOPL(31,0,1), XCMPL_MASK, PPC64, { OBF, RA, RB } },
+{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } },
+{ "cmp", X(31,0), XCMPL_MASK, PWRCOM, { BF, RA, RB } },
+
+{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tllt", XTO(31,4,TOLLT), XTO_MASK, PWRCOM, { RA, RB } },
+{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPCCOM, { RA, RB } },
+{ "teq", XTO(31,4,TOEQ), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tlge", XTO(31,4,TOLGE), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tlle", XTO(31,4,TOLLE), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tlng", XTO(31,4,TOLNG), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tgt", XTO(31,4,TOGT), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twge", XTO(31,4,TOGE), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tge", XTO(31,4,TOGE), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twnl", XTO(31,4,TONL), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tnl", XTO(31,4,TONL), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tlt", XTO(31,4,TOLT), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twle", XTO(31,4,TOLE), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tle", XTO(31,4,TOLE), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twng", XTO(31,4,TONG), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tng", XTO(31,4,TONG), XTO_MASK, PWRCOM, { RA, RB } },
+{ "twne", XTO(31,4,TONE), XTO_MASK, PPCCOM, { RA, RB } },
+{ "tne", XTO(31,4,TONE), XTO_MASK, PWRCOM, { RA, RB } },
+{ "trap", XTO(31,4,TOU), 0xffffffff, PPCCOM, { 0 } },
+{ "tw", X(31,4), X_MASK, PPCCOM, { TO, RA, RB } },
+{ "t", X(31,4), X_MASK, PWRCOM, { TO, RA, RB } },
+
+{ "subfc", XO(31,8,0,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sf", XO(31,8,0,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sf.", XO(31,8,0,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RB, RA } },
+{ "subfco", XO(31,8,1,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sfo", XO(31,8,1,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfco.", XO(31,8,1,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sfo.", XO(31,8,1,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } },
+
+{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC64, { RT, RA, RB } },
+
+{ "addc", XO(31,10,0,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "a", XO(31,10,0,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addc.", XO(31,10,0,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "a.", XO(31,10,0,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addco", XO(31,10,1,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "ao", XO(31,10,1,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addco.", XO(31,10,1,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "ao.", XO(31,10,1,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "isellt", X(31,15), X_MASK, PPCISEL, { RT, RA, RB } },
+{ "iselgt", X(31,47), X_MASK, PPCISEL, { RT, RA, RB } },
+{ "iseleq", X(31,79), X_MASK, PPCISEL, { RT, RA, RB } },
+{ "isel", XISEL(31,15), XISEL_MASK, PPCISEL, { RT, RA, RB, CRB } },
+
+{ "mfocrf", XFXM(31,19,0,1), XFXFXM_MASK, COM, { RT, FXM } },
+{ "mfcr", X(31,19), XRARB_MASK, NOPOWER4 | COM, { RT } },
+{ "mfcr", X(31,19), XFXFXM_MASK, POWER4, { RT, FXM4 } },
+
+{ "lwarx", X(31,20), XEH_MASK, PPC, { RT, RA0, RB, EH } },
+
+{ "ldx", X(31,21), X_MASK, PPC64, { RT, RA0, RB } },
+
+{ "icbt", X(31,22), X_MASK, BOOKE|PPCE300, { CT, RA, RB } },
+{ "icbt", X(31,262), XRT_MASK, PPC403, { RA, RB } },
+
+{ "lwzx", X(31,23), X_MASK, PPCCOM, { RT, RA0, RB } },
+{ "lx", X(31,23), X_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "slw", XRC(31,24,0), X_MASK, PPCCOM, { RA, RS, RB } },
+{ "sl", XRC(31,24,0), X_MASK, PWRCOM, { RA, RS, RB } },
+{ "slw.", XRC(31,24,1), X_MASK, PPCCOM, { RA, RS, RB } },
+{ "sl.", XRC(31,24,1), X_MASK, PWRCOM, { RA, RS, RB } },
+
+{ "cntlzw", XRC(31,26,0), XRB_MASK, PPCCOM, { RA, RS } },
+{ "cntlz", XRC(31,26,0), XRB_MASK, PWRCOM, { RA, RS } },
+{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPCCOM, { RA, RS } },
+{ "cntlz.", XRC(31,26,1), XRB_MASK, PWRCOM, { RA, RS } },
+
+{ "sld", XRC(31,27,0), X_MASK, PPC64, { RA, RS, RB } },
+{ "sld.", XRC(31,27,1), X_MASK, PPC64, { RA, RS, RB } },
+
+{ "and", XRC(31,28,0), X_MASK, COM, { RA, RS, RB } },
+{ "and.", XRC(31,28,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "maskg", XRC(31,29,0), X_MASK, M601, { RA, RS, RB } },
+{ "maskg.", XRC(31,29,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "icbte", X(31,30), X_MASK, BOOKE64, { CT, RA, RB } },
+
+{ "lwzxe", X(31,31), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "cmplw", XOPL(31,32,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } },
+{ "cmpld", XOPL(31,32,1), XCMPL_MASK, PPC64, { OBF, RA, RB } },
+{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } },
+{ "cmpl", X(31,32), XCMPL_MASK, PWRCOM, { BF, RA, RB } },
+
+{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } },
+
+{ "ldux", X(31,53), X_MASK, PPC64, { RT, RAL, RB } },
+
+{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } },
+
+{ "lwzux", X(31,55), X_MASK, PPCCOM, { RT, RAL, RB } },
+{ "lux", X(31,55), X_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "dcbste", X(31,62), XRT_MASK, BOOKE64, { RA, RB } },
+
+{ "lwzuxe", X(31,63), X_MASK, BOOKE64, { RT, RAL, RB } },
+
+{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC64, { RA, RS } },
+{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC64, { RA, RS } },
+
+{ "andc", XRC(31,60,0), X_MASK, COM, { RA, RS, RB } },
+{ "andc.", XRC(31,60,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC64, { RA, RB } },
+{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC64, { RA, RB } },
+{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC64, { RA, RB } },
+{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC64, { RA, RB } },
+{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC64, { RA, RB } },
+{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC64, { RA, RB } },
+{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC64, { RA, RB } },
+{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC64, { RA, RB } },
+{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC64, { RA, RB } },
+{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC64, { RA, RB } },
+{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC64, { RA, RB } },
+{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC64, { RA, RB } },
+{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC64, { RA, RB } },
+{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC64, { RA, RB } },
+{ "td", X(31,68), X_MASK, PPC64, { TO, RA, RB } },
+
+{ "mulhd", XO(31,73,0,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC64, { RT, RA, RB } },
+
+{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "dlmzb", XRC(31,78,0), X_MASK, PPC403|PPC440, { RA, RS, RB } },
+{ "dlmzb.", XRC(31,78,1), X_MASK, PPC403|PPC440, { RA, RS, RB } },
+
+{ "mtsrd", X(31,82), XRB_MASK|(1<<20), PPC64, { SR, RS } },
+
+{ "mfmsr", X(31,83), XRARB_MASK, COM, { RT } },
+
+{ "ldarx", X(31,84), XEH_MASK, PPC64, { RT, RA0, RB, EH } },
+
+{ "dcbfl", XOPL(31,86,1), XRT_MASK, POWER5, { RA, RB } },
+{ "dcbf", X(31,86), XLRT_MASK, PPC, { RA, RB, L } },
+
+{ "lbzx", X(31,87), X_MASK, COM, { RT, RA0, RB } },
+
+{ "dcbfe", X(31,94), XRT_MASK, BOOKE64, { RA, RB } },
+
+{ "lbzxe", X(31,95), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "neg", XO(31,104,0,0), XORB_MASK, COM, { RT, RA } },
+{ "neg.", XO(31,104,0,1), XORB_MASK, COM, { RT, RA } },
+{ "nego", XO(31,104,1,0), XORB_MASK, COM, { RT, RA } },
+{ "nego.", XO(31,104,1,1), XORB_MASK, COM, { RT, RA } },
+
+{ "mul", XO(31,107,0,0), XO_MASK, M601, { RT, RA, RB } },
+{ "mul.", XO(31,107,0,1), XO_MASK, M601, { RT, RA, RB } },
+{ "mulo", XO(31,107,1,0), XO_MASK, M601, { RT, RA, RB } },
+{ "mulo.", XO(31,107,1,1), XO_MASK, M601, { RT, RA, RB } },
+
+{ "mtsrdin", X(31,114), XRA_MASK, PPC64, { RS, RB } },
+
+{ "clf", X(31,118), XTO_MASK, POWER, { RA, RB } },
+
+{ "lbzux", X(31,119), X_MASK, COM, { RT, RAL, RB } },
+
+{ "popcntb", X(31,122), XRB_MASK, POWER5, { RA, RS } },
+
+{ "not", XRC(31,124,0), X_MASK, COM, { RA, RS, RBS } },
+{ "nor", XRC(31,124,0), X_MASK, COM, { RA, RS, RB } },
+{ "not.", XRC(31,124,1), X_MASK, COM, { RA, RS, RBS } },
+{ "nor.", XRC(31,124,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "lwarxe", X(31,126), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "lbzuxe", X(31,127), X_MASK, BOOKE64, { RT, RAL, RB } },
+
+{ "wrtee", X(31,131), XRARB_MASK, PPC403 | BOOKE, { RS } },
+
+{ "dcbtstls",X(31,134), X_MASK, PPCCHLK, { CT, RA, RB }},
+
+{ "subfe", XO(31,136,0,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sfe", XO(31,136,0,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subfe.", XO(31,136,0,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sfe.", XO(31,136,0,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subfeo", XO(31,136,1,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sfeo", XO(31,136,1,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "subfeo.", XO(31,136,1,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "sfeo.", XO(31,136,1,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "adde", XO(31,138,0,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "ae", XO(31,138,0,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "adde.", XO(31,138,0,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "ae.", XO(31,138,0,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addeo", XO(31,138,1,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "aeo", XO(31,138,1,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addeo.", XO(31,138,1,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "aeo.", XO(31,138,1,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "dcbtstlse",X(31,142),X_MASK, PPCCHLK64, { CT, RA, RB }},
+
+{ "mtocrf", XFXM(31,144,0,1), XFXFXM_MASK, COM, { FXM, RS } },
+{ "mtcr", XFXM(31,144,0xff,0), XRARB_MASK, COM, { RS }},
+{ "mtcrf", X(31,144), XFXFXM_MASK, COM, { FXM, RS } },
+
+{ "mtmsr", X(31,146), XRARB_MASK, COM, { RS } },
+
+{ "stdx", X(31,149), X_MASK, PPC64, { RS, RA0, RB } },
+
+{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA0, RB } },
+
+{ "stwx", X(31,151), X_MASK, PPCCOM, { RS, RA0, RB } },
+{ "stx", X(31,151), X_MASK, PWRCOM, { RS, RA, RB } },
+
+{ "stwcxe.", XRC(31,158,1), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "stwxe", X(31,159), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "slq", XRC(31,152,0), X_MASK, M601, { RA, RS, RB } },
+{ "slq.", XRC(31,152,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "sle", XRC(31,153,0), X_MASK, M601, { RA, RS, RB } },
+{ "sle.", XRC(31,153,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "prtyw", X(31,154), XRB_MASK, POWER6, { RA, RS } },
+
+{ "wrteei", X(31,163), XE_MASK, PPC403 | BOOKE, { E } },
+
+{ "dcbtls", X(31,166), X_MASK, PPCCHLK, { CT, RA, RB }},
+{ "dcbtlse", X(31,174), X_MASK, PPCCHLK64, { CT, RA, RB }},
+
+{ "mtmsrd", X(31,178), XRLARB_MASK, PPC64, { RS, A_L } },
+
+{ "stdux", X(31,181), X_MASK, PPC64, { RS, RAS, RB } },
+
+{ "stwux", X(31,183), X_MASK, PPCCOM, { RS, RAS, RB } },
+{ "stux", X(31,183), X_MASK, PWRCOM, { RS, RA0, RB } },
+
+{ "sliq", XRC(31,184,0), X_MASK, M601, { RA, RS, SH } },
+{ "sliq.", XRC(31,184,1), X_MASK, M601, { RA, RS, SH } },
+
+{ "prtyd", X(31,186), XRB_MASK, POWER6, { RA, RS } },
+
+{ "stwuxe", X(31,191), X_MASK, BOOKE64, { RS, RAS, RB } },
+
+{ "subfze", XO(31,200,0,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfze", XO(31,200,0,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "subfze.", XO(31,200,0,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfze.", XO(31,200,0,1), XORB_MASK, PWRCOM, { RT, RA } },
+{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfzeo", XO(31,200,1,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfzeo.", XO(31,200,1,1), XORB_MASK, PWRCOM, { RT, RA } },
+
+{ "addze", XO(31,202,0,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "aze", XO(31,202,0,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "addze.", XO(31,202,0,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "aze.", XO(31,202,0,1), XORB_MASK, PWRCOM, { RT, RA } },
+{ "addzeo", XO(31,202,1,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "azeo", XO(31,202,1,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "azeo.", XO(31,202,1,1), XORB_MASK, PWRCOM, { RT, RA } },
+
+{ "mtsr", X(31,210), XRB_MASK|(1<<20), COM32, { SR, RS } },
+
+{ "stdcx.", XRC(31,214,1), X_MASK, PPC64, { RS, RA0, RB } },
+
+{ "stbx", X(31,215), X_MASK, COM, { RS, RA0, RB } },
+
+{ "sllq", XRC(31,216,0), X_MASK, M601, { RA, RS, RB } },
+{ "sllq.", XRC(31,216,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "sleq", XRC(31,217,0), X_MASK, M601, { RA, RS, RB } },
+{ "sleq.", XRC(31,217,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "stbxe", X(31,223), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "icblc", X(31,230), X_MASK, PPCCHLK, { CT, RA, RB }},
+
+{ "subfme", XO(31,232,0,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfme", XO(31,232,0,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "subfme.", XO(31,232,0,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfme.", XO(31,232,0,1), XORB_MASK, PWRCOM, { RT, RA } },
+{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfmeo", XO(31,232,1,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "sfmeo.", XO(31,232,1,1), XORB_MASK, PWRCOM, { RT, RA } },
+
+{ "mulld", XO(31,233,0,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "mulld.", XO(31,233,0,1), XO_MASK, PPC64, { RT, RA, RB } },
+{ "mulldo", XO(31,233,1,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC64, { RT, RA, RB } },
+
+{ "addme", XO(31,234,0,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "ame", XO(31,234,0,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "addme.", XO(31,234,0,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "ame.", XO(31,234,0,1), XORB_MASK, PWRCOM, { RT, RA } },
+{ "addmeo", XO(31,234,1,0), XORB_MASK, PPCCOM, { RT, RA } },
+{ "ameo", XO(31,234,1,0), XORB_MASK, PWRCOM, { RT, RA } },
+{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPCCOM, { RT, RA } },
+{ "ameo.", XO(31,234,1,1), XORB_MASK, PWRCOM, { RT, RA } },
+
+{ "mullw", XO(31,235,0,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "muls", XO(31,235,0,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "mullw.", XO(31,235,0,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "muls.", XO(31,235,0,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "mullwo", XO(31,235,1,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "mulso", XO(31,235,1,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "mullwo.", XO(31,235,1,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "mulso.", XO(31,235,1,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "icblce", X(31,238), X_MASK, PPCCHLK64, { CT, RA, RB }},
+{ "mtsrin", X(31,242), XRA_MASK, PPC32, { RS, RB } },
+{ "mtsri", X(31,242), XRA_MASK, POWER32, { RS, RB } },
+
+{ "dcbtst", X(31,246), X_MASK, PPC, { CT, RA, RB } },
+
+{ "stbux", X(31,247), X_MASK, COM, { RS, RAS, RB } },
+
+{ "slliq", XRC(31,248,0), X_MASK, M601, { RA, RS, SH } },
+{ "slliq.", XRC(31,248,1), X_MASK, M601, { RA, RS, SH } },
+
+{ "dcbtste", X(31,253), X_MASK, BOOKE64, { CT, RA, RB } },
+
+{ "stbuxe", X(31,255), X_MASK, BOOKE64, { RS, RAS, RB } },
+
+{ "mfdcrx", X(31,259), X_MASK, BOOKE, { RS, RA } },
+
+{ "doz", XO(31,264,0,0), XO_MASK, M601, { RT, RA, RB } },
+{ "doz.", XO(31,264,0,1), XO_MASK, M601, { RT, RA, RB } },
+{ "dozo", XO(31,264,1,0), XO_MASK, M601, { RT, RA, RB } },
+{ "dozo.", XO(31,264,1,1), XO_MASK, M601, { RT, RA, RB } },
+
+{ "add", XO(31,266,0,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "cax", XO(31,266,0,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "add.", XO(31,266,0,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "cax.", XO(31,266,0,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addo", XO(31,266,1,0), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "caxo", XO(31,266,1,0), XO_MASK, PWRCOM, { RT, RA, RB } },
+{ "addo.", XO(31,266,1,1), XO_MASK, PPCCOM, { RT, RA, RB } },
+{ "caxo.", XO(31,266,1,1), XO_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "tlbiel", X(31,274), XRTLRA_MASK, POWER4, { RB, L } },
+
+{ "mfapidi", X(31,275), X_MASK, BOOKE, { RT, RA } },
+
+{ "lscbx", XRC(31,277,0), X_MASK, M601, { RT, RA, RB } },
+{ "lscbx.", XRC(31,277,1), X_MASK, M601, { RT, RA, RB } },
+
+{ "dcbt", X(31,278), X_MASK, PPC, { CT, RA, RB } },
+
+{ "lhzx", X(31,279), X_MASK, COM, { RT, RA0, RB } },
+
+{ "eqv", XRC(31,284,0), X_MASK, COM, { RA, RS, RB } },
+{ "eqv.", XRC(31,284,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "dcbte", X(31,286), X_MASK, BOOKE64, { CT, RA, RB } },
+
+{ "lhzxe", X(31,287), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "tlbie", X(31,306), XRTLRA_MASK, PPC, { RB, L } },
+{ "tlbi", X(31,306), XRT_MASK, POWER, { RA0, RB } },
+
+{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } },
+
+{ "lhzux", X(31,311), X_MASK, COM, { RT, RAL, RB } },
+
+{ "xor", XRC(31,316,0), X_MASK, COM, { RA, RS, RB } },
+{ "xor.", XRC(31,316,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "lhzuxe", X(31,319), X_MASK, BOOKE64, { RT, RAL, RB } },
+
+{ "mfexisr", XSPR(31,323,64), XSPR_MASK, PPC403, { RT } },
+{ "mfexier", XSPR(31,323,66), XSPR_MASK, PPC403, { RT } },
+{ "mfbr0", XSPR(31,323,128), XSPR_MASK, PPC403, { RT } },
+{ "mfbr1", XSPR(31,323,129), XSPR_MASK, PPC403, { RT } },
+{ "mfbr2", XSPR(31,323,130), XSPR_MASK, PPC403, { RT } },
+{ "mfbr3", XSPR(31,323,131), XSPR_MASK, PPC403, { RT } },
+{ "mfbr4", XSPR(31,323,132), XSPR_MASK, PPC403, { RT } },
+{ "mfbr5", XSPR(31,323,133), XSPR_MASK, PPC403, { RT } },
+{ "mfbr6", XSPR(31,323,134), XSPR_MASK, PPC403, { RT } },
+{ "mfbr7", XSPR(31,323,135), XSPR_MASK, PPC403, { RT } },
+{ "mfbear", XSPR(31,323,144), XSPR_MASK, PPC403, { RT } },
+{ "mfbesr", XSPR(31,323,145), XSPR_MASK, PPC403, { RT } },
+{ "mfiocr", XSPR(31,323,160), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacr0", XSPR(31,323,192), XSPR_MASK, PPC403, { RT } },
+{ "mfdmact0", XSPR(31,323,193), XSPR_MASK, PPC403, { RT } },
+{ "mfdmada0", XSPR(31,323,194), XSPR_MASK, PPC403, { RT } },
+{ "mfdmasa0", XSPR(31,323,195), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacc0", XSPR(31,323,196), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacr1", XSPR(31,323,200), XSPR_MASK, PPC403, { RT } },
+{ "mfdmact1", XSPR(31,323,201), XSPR_MASK, PPC403, { RT } },
+{ "mfdmada1", XSPR(31,323,202), XSPR_MASK, PPC403, { RT } },
+{ "mfdmasa1", XSPR(31,323,203), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacc1", XSPR(31,323,204), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacr2", XSPR(31,323,208), XSPR_MASK, PPC403, { RT } },
+{ "mfdmact2", XSPR(31,323,209), XSPR_MASK, PPC403, { RT } },
+{ "mfdmada2", XSPR(31,323,210), XSPR_MASK, PPC403, { RT } },
+{ "mfdmasa2", XSPR(31,323,211), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacc2", XSPR(31,323,212), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacr3", XSPR(31,323,216), XSPR_MASK, PPC403, { RT } },
+{ "mfdmact3", XSPR(31,323,217), XSPR_MASK, PPC403, { RT } },
+{ "mfdmada3", XSPR(31,323,218), XSPR_MASK, PPC403, { RT } },
+{ "mfdmasa3", XSPR(31,323,219), XSPR_MASK, PPC403, { RT } },
+{ "mfdmacc3", XSPR(31,323,220), XSPR_MASK, PPC403, { RT } },
+{ "mfdmasr", XSPR(31,323,224), XSPR_MASK, PPC403, { RT } },
+{ "mfdcr", X(31,323), X_MASK, PPC403 | BOOKE, { RT, SPR } },
+
+{ "div", XO(31,331,0,0), XO_MASK, M601, { RT, RA, RB } },
+{ "div.", XO(31,331,0,1), XO_MASK, M601, { RT, RA, RB } },
+{ "divo", XO(31,331,1,0), XO_MASK, M601, { RT, RA, RB } },
+{ "divo.", XO(31,331,1,1), XO_MASK, M601, { RT, RA, RB } },
+
+{ "mfpmr", X(31,334), X_MASK, PPCPMR, { RT, PMR }},
+
+{ "mfmq", XSPR(31,339,0), XSPR_MASK, M601, { RT } },
+{ "mfxer", XSPR(31,339,1), XSPR_MASK, COM, { RT } },
+{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, COM, { RT } },
+{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, COM, { RT } },
+{ "mfdec", XSPR(31,339,6), XSPR_MASK, MFDEC1, { RT } },
+{ "mfdec", XSPR(31,339,22), XSPR_MASK, MFDEC2, { RT } },
+{ "mflr", XSPR(31,339,8), XSPR_MASK, COM, { RT } },
+{ "mfctr", XSPR(31,339,9), XSPR_MASK, COM, { RT } },
+{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } },
+{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, COM, { RT } },
+{ "mfdar", XSPR(31,339,19), XSPR_MASK, COM, { RT } },
+{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } },
+{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, COM, { RT } },
+{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, COM, { RT } },
+{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, COM, { RT } },
+{ "mfcfar", XSPR(31,339,28), XSPR_MASK, POWER6, { RT } },
+{ "mfpid", XSPR(31,339,48), XSPR_MASK, BOOKE, { RT } },
+{ "mfpid", XSPR(31,339,945), XSPR_MASK, PPC403, { RT } },
+{ "mfcsrr0", XSPR(31,339,58), XSPR_MASK, BOOKE, { RT } },
+{ "mfcsrr1", XSPR(31,339,59), XSPR_MASK, BOOKE, { RT } },
+{ "mfdear", XSPR(31,339,61), XSPR_MASK, BOOKE, { RT } },
+{ "mfdear", XSPR(31,339,981), XSPR_MASK, PPC403, { RT } },
+{ "mfesr", XSPR(31,339,62), XSPR_MASK, BOOKE, { RT } },
+{ "mfesr", XSPR(31,339,980), XSPR_MASK, PPC403, { RT } },
+{ "mfivpr", XSPR(31,339,63), XSPR_MASK, BOOKE, { RT } },
+{ "mfcmpa", XSPR(31,339,144), XSPR_MASK, PPC860, { RT } },
+{ "mfcmpb", XSPR(31,339,145), XSPR_MASK, PPC860, { RT } },
+{ "mfcmpc", XSPR(31,339,146), XSPR_MASK, PPC860, { RT } },
+{ "mfcmpd", XSPR(31,339,147), XSPR_MASK, PPC860, { RT } },
+{ "mficr", XSPR(31,339,148), XSPR_MASK, PPC860, { RT } },
+{ "mfder", XSPR(31,339,149), XSPR_MASK, PPC860, { RT } },
+{ "mfcounta", XSPR(31,339,150), XSPR_MASK, PPC860, { RT } },
+{ "mfcountb", XSPR(31,339,151), XSPR_MASK, PPC860, { RT } },
+{ "mfcmpe", XSPR(31,339,152), XSPR_MASK, PPC860, { RT } },
+{ "mfcmpf", XSPR(31,339,153), XSPR_MASK, PPC860, { RT } },
+{ "mfcmpg", XSPR(31,339,154), XSPR_MASK, PPC860, { RT } },
+{ "mfcmph", XSPR(31,339,155), XSPR_MASK, PPC860, { RT } },
+{ "mflctrl1", XSPR(31,339,156), XSPR_MASK, PPC860, { RT } },
+{ "mflctrl2", XSPR(31,339,157), XSPR_MASK, PPC860, { RT } },
+{ "mfictrl", XSPR(31,339,158), XSPR_MASK, PPC860, { RT } },
+{ "mfbar", XSPR(31,339,159), XSPR_MASK, PPC860, { RT } },
+{ "mfvrsave", XSPR(31,339,256), XSPR_MASK, PPCVEC, { RT } },
+{ "mfusprg0", XSPR(31,339,256), XSPR_MASK, BOOKE, { RT } },
+{ "mftb", X(31,371), X_MASK, CLASSIC, { RT, TBR } },
+{ "mftb", XSPR(31,339,268), XSPR_MASK, BOOKE, { RT } },
+{ "mftbl", XSPR(31,371,268), XSPR_MASK, CLASSIC, { RT } },
+{ "mftbl", XSPR(31,339,268), XSPR_MASK, BOOKE, { RT } },
+{ "mftbu", XSPR(31,371,269), XSPR_MASK, CLASSIC, { RT } },
+{ "mftbu", XSPR(31,339,269), XSPR_MASK, BOOKE, { RT } },
+{ "mfsprg", XSPR(31,339,256), XSPRG_MASK, PPC, { RT, SPRG } },
+{ "mfsprg0", XSPR(31,339,272), XSPR_MASK, PPC, { RT } },
+{ "mfsprg1", XSPR(31,339,273), XSPR_MASK, PPC, { RT } },
+{ "mfsprg2", XSPR(31,339,274), XSPR_MASK, PPC, { RT } },
+{ "mfsprg3", XSPR(31,339,275), XSPR_MASK, PPC, { RT } },
+{ "mfsprg4", XSPR(31,339,260), XSPR_MASK, PPC405 | BOOKE, { RT } },
+{ "mfsprg5", XSPR(31,339,261), XSPR_MASK, PPC405 | BOOKE, { RT } },
+{ "mfsprg6", XSPR(31,339,262), XSPR_MASK, PPC405 | BOOKE, { RT } },
+{ "mfsprg7", XSPR(31,339,263), XSPR_MASK, PPC405 | BOOKE, { RT } },
+{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC64, { RT } },
+{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } },
+{ "mfpir", XSPR(31,339,286), XSPR_MASK, BOOKE, { RT } },
+{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } },
+{ "mfdbsr", XSPR(31,339,304), XSPR_MASK, BOOKE, { RT } },
+{ "mfdbsr", XSPR(31,339,1008), XSPR_MASK, PPC403, { RT } },
+{ "mfdbcr0", XSPR(31,339,308), XSPR_MASK, BOOKE, { RT } },
+{ "mfdbcr0", XSPR(31,339,1010), XSPR_MASK, PPC405, { RT } },
+{ "mfdbcr1", XSPR(31,339,309), XSPR_MASK, BOOKE, { RT } },
+{ "mfdbcr1", XSPR(31,339,957), XSPR_MASK, PPC405, { RT } },
+{ "mfdbcr2", XSPR(31,339,310), XSPR_MASK, BOOKE, { RT } },
+{ "mfiac1", XSPR(31,339,312), XSPR_MASK, BOOKE, { RT } },
+{ "mfiac1", XSPR(31,339,1012), XSPR_MASK, PPC403, { RT } },
+{ "mfiac2", XSPR(31,339,313), XSPR_MASK, BOOKE, { RT } },
+{ "mfiac2", XSPR(31,339,1013), XSPR_MASK, PPC403, { RT } },
+{ "mfiac3", XSPR(31,339,314), XSPR_MASK, BOOKE, { RT } },
+{ "mfiac3", XSPR(31,339,948), XSPR_MASK, PPC405, { RT } },
+{ "mfiac4", XSPR(31,339,315), XSPR_MASK, BOOKE, { RT } },
+{ "mfiac4", XSPR(31,339,949), XSPR_MASK, PPC405, { RT } },
+{ "mfdac1", XSPR(31,339,316), XSPR_MASK, BOOKE, { RT } },
+{ "mfdac1", XSPR(31,339,1014), XSPR_MASK, PPC403, { RT } },
+{ "mfdac2", XSPR(31,339,317), XSPR_MASK, BOOKE, { RT } },
+{ "mfdac2", XSPR(31,339,1015), XSPR_MASK, PPC403, { RT } },
+{ "mfdvc1", XSPR(31,339,318), XSPR_MASK, BOOKE, { RT } },
+{ "mfdvc1", XSPR(31,339,950), XSPR_MASK, PPC405, { RT } },
+{ "mfdvc2", XSPR(31,339,319), XSPR_MASK, BOOKE, { RT } },
+{ "mfdvc2", XSPR(31,339,951), XSPR_MASK, PPC405, { RT } },
+{ "mftsr", XSPR(31,339,336), XSPR_MASK, BOOKE, { RT } },
+{ "mftsr", XSPR(31,339,984), XSPR_MASK, PPC403, { RT } },
+{ "mftcr", XSPR(31,339,340), XSPR_MASK, BOOKE, { RT } },
+{ "mftcr", XSPR(31,339,986), XSPR_MASK, PPC403, { RT } },
+{ "mfivor0", XSPR(31,339,400), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor1", XSPR(31,339,401), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor2", XSPR(31,339,402), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor3", XSPR(31,339,403), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor4", XSPR(31,339,404), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor5", XSPR(31,339,405), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor6", XSPR(31,339,406), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor7", XSPR(31,339,407), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor8", XSPR(31,339,408), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor9", XSPR(31,339,409), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor10", XSPR(31,339,410), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor11", XSPR(31,339,411), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor12", XSPR(31,339,412), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor13", XSPR(31,339,413), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor14", XSPR(31,339,414), XSPR_MASK, BOOKE, { RT } },
+{ "mfivor15", XSPR(31,339,415), XSPR_MASK, BOOKE, { RT } },
+{ "mfspefscr", XSPR(31,339,512), XSPR_MASK, PPCSPE, { RT } },
+{ "mfbbear", XSPR(31,339,513), XSPR_MASK, PPCBRLK, { RT } },
+{ "mfbbtar", XSPR(31,339,514), XSPR_MASK, PPCBRLK, { RT } },
+{ "mfivor32", XSPR(31,339,528), XSPR_MASK, PPCSPE, { RT } },
+{ "mfivor33", XSPR(31,339,529), XSPR_MASK, PPCSPE, { RT } },
+{ "mfivor34", XSPR(31,339,530), XSPR_MASK, PPCSPE, { RT } },
+{ "mfivor35", XSPR(31,339,531), XSPR_MASK, PPCPMR, { RT } },
+{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfic_cst", XSPR(31,339,560), XSPR_MASK, PPC860, { RT } },
+{ "mfic_adr", XSPR(31,339,561), XSPR_MASK, PPC860, { RT } },
+{ "mfic_dat", XSPR(31,339,562), XSPR_MASK, PPC860, { RT } },
+{ "mfdc_cst", XSPR(31,339,568), XSPR_MASK, PPC860, { RT } },
+{ "mfdc_adr", XSPR(31,339,569), XSPR_MASK, PPC860, { RT } },
+{ "mfmcsrr0", XSPR(31,339,570), XSPR_MASK, PPCRFMCI, { RT } },
+{ "mfdc_dat", XSPR(31,339,570), XSPR_MASK, PPC860, { RT } },
+{ "mfmcsrr1", XSPR(31,339,571), XSPR_MASK, PPCRFMCI, { RT } },
+{ "mfmcsr", XSPR(31,339,572), XSPR_MASK, PPCRFMCI, { RT } },
+{ "mfmcar", XSPR(31,339,573), XSPR_MASK, PPCRFMCI, { RT } },
+{ "mfdpdr", XSPR(31,339,630), XSPR_MASK, PPC860, { RT } },
+{ "mfdpir", XSPR(31,339,631), XSPR_MASK, PPC860, { RT } },
+{ "mfimmr", XSPR(31,339,638), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_ctr", XSPR(31,339,784), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_ap", XSPR(31,339,786), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_epn", XSPR(31,339,787), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_twc", XSPR(31,339,789), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_rpn", XSPR(31,339,790), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_ctr", XSPR(31,339,792), XSPR_MASK, PPC860, { RT } },
+{ "mfm_casid", XSPR(31,339,793), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_ap", XSPR(31,339,794), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_epn", XSPR(31,339,795), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_twb", XSPR(31,339,796), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_twc", XSPR(31,339,797), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_rpn", XSPR(31,339,798), XSPR_MASK, PPC860, { RT } },
+{ "mfm_tw", XSPR(31,339,799), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_dbcam", XSPR(31,339,816), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_dbram0",XSPR(31,339,817), XSPR_MASK, PPC860, { RT } },
+{ "mfmi_dbram1",XSPR(31,339,818), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_dbcam", XSPR(31,339,824), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_dbram0",XSPR(31,339,825), XSPR_MASK, PPC860, { RT } },
+{ "mfmd_dbram1",XSPR(31,339,826), XSPR_MASK, PPC860, { RT } },
+{ "mfummcr0", XSPR(31,339,936), XSPR_MASK, PPC750, { RT } },
+{ "mfupmc1", XSPR(31,339,937), XSPR_MASK, PPC750, { RT } },
+{ "mfupmc2", XSPR(31,339,938), XSPR_MASK, PPC750, { RT } },
+{ "mfusia", XSPR(31,339,939), XSPR_MASK, PPC750, { RT } },
+{ "mfummcr1", XSPR(31,339,940), XSPR_MASK, PPC750, { RT } },
+{ "mfupmc3", XSPR(31,339,941), XSPR_MASK, PPC750, { RT } },
+{ "mfupmc4", XSPR(31,339,942), XSPR_MASK, PPC750, { RT } },
+{ "mfzpr", XSPR(31,339,944), XSPR_MASK, PPC403, { RT } },
+{ "mfccr0", XSPR(31,339,947), XSPR_MASK, PPC405, { RT } },
+{ "mfmmcr0", XSPR(31,339,952), XSPR_MASK, PPC750, { RT } },
+{ "mfpmc1", XSPR(31,339,953), XSPR_MASK, PPC750, { RT } },
+{ "mfsgr", XSPR(31,339,953), XSPR_MASK, PPC403, { RT } },
+{ "mfpmc2", XSPR(31,339,954), XSPR_MASK, PPC750, { RT } },
+{ "mfdcwr", XSPR(31,339,954), XSPR_MASK, PPC403, { RT } },
+{ "mfsia", XSPR(31,339,955), XSPR_MASK, PPC750, { RT } },
+{ "mfsler", XSPR(31,339,955), XSPR_MASK, PPC405, { RT } },
+{ "mfmmcr1", XSPR(31,339,956), XSPR_MASK, PPC750, { RT } },
+{ "mfsu0r", XSPR(31,339,956), XSPR_MASK, PPC405, { RT } },
+{ "mfpmc3", XSPR(31,339,957), XSPR_MASK, PPC750, { RT } },
+{ "mfpmc4", XSPR(31,339,958), XSPR_MASK, PPC750, { RT } },
+{ "mficdbdr", XSPR(31,339,979), XSPR_MASK, PPC403, { RT } },
+{ "mfevpr", XSPR(31,339,982), XSPR_MASK, PPC403, { RT } },
+{ "mfcdbcr", XSPR(31,339,983), XSPR_MASK, PPC403, { RT } },
+{ "mfpit", XSPR(31,339,987), XSPR_MASK, PPC403, { RT } },
+{ "mftbhi", XSPR(31,339,988), XSPR_MASK, PPC403, { RT } },
+{ "mftblo", XSPR(31,339,989), XSPR_MASK, PPC403, { RT } },
+{ "mfsrr2", XSPR(31,339,990), XSPR_MASK, PPC403, { RT } },
+{ "mfsrr3", XSPR(31,339,991), XSPR_MASK, PPC403, { RT } },
+{ "mfl2cr", XSPR(31,339,1017), XSPR_MASK, PPC750, { RT } },
+{ "mfdccr", XSPR(31,339,1018), XSPR_MASK, PPC403, { RT } },
+{ "mficcr", XSPR(31,339,1019), XSPR_MASK, PPC403, { RT } },
+{ "mfictc", XSPR(31,339,1019), XSPR_MASK, PPC750, { RT } },
+{ "mfpbl1", XSPR(31,339,1020), XSPR_MASK, PPC403, { RT } },
+{ "mfthrm1", XSPR(31,339,1020), XSPR_MASK, PPC750, { RT } },
+{ "mfpbu1", XSPR(31,339,1021), XSPR_MASK, PPC403, { RT } },
+{ "mfthrm2", XSPR(31,339,1021), XSPR_MASK, PPC750, { RT } },
+{ "mfpbl2", XSPR(31,339,1022), XSPR_MASK, PPC403, { RT } },
+{ "mfthrm3", XSPR(31,339,1022), XSPR_MASK, PPC750, { RT } },
+{ "mfpbu2", XSPR(31,339,1023), XSPR_MASK, PPC403, { RT } },
+{ "mfspr", X(31,339), X_MASK, COM, { RT, SPR } },
+
+{ "lwax", X(31,341), X_MASK, PPC64, { RT, RA0, RB } },
+
+{ "dst", XDSS(31,342,0), XDSS_MASK, PPCVEC, { RA, RB, STRM } },
+{ "dstt", XDSS(31,342,1), XDSS_MASK, PPCVEC, { RA, RB, STRM } },
+
+{ "lhax", X(31,343), X_MASK, COM, { RT, RA0, RB } },
+
+{ "lhaxe", X(31,351), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "dstst", XDSS(31,374,0), XDSS_MASK, PPCVEC, { RA, RB, STRM } },
+{ "dststt", XDSS(31,374,1), XDSS_MASK, PPCVEC, { RA, RB, STRM } },
+
+{ "dccci", X(31,454), XRT_MASK, PPC403|PPC440, { RA, RB } },
+
+{ "abs", XO(31,360,0,0), XORB_MASK, M601, { RT, RA } },
+{ "abs.", XO(31,360,0,1), XORB_MASK, M601, { RT, RA } },
+{ "abso", XO(31,360,1,0), XORB_MASK, M601, { RT, RA } },
+{ "abso.", XO(31,360,1,1), XORB_MASK, M601, { RT, RA } },
+
+{ "divs", XO(31,363,0,0), XO_MASK, M601, { RT, RA, RB } },
+{ "divs.", XO(31,363,0,1), XO_MASK, M601, { RT, RA, RB } },
+{ "divso", XO(31,363,1,0), XO_MASK, M601, { RT, RA, RB } },
+{ "divso.", XO(31,363,1,1), XO_MASK, M601, { RT, RA, RB } },
+
+{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } },
+
+{ "lwaux", X(31,373), X_MASK, PPC64, { RT, RAL, RB } },
+
+{ "lhaux", X(31,375), X_MASK, COM, { RT, RAL, RB } },
+
+{ "lhauxe", X(31,383), X_MASK, BOOKE64, { RT, RAL, RB } },
+
+{ "mtdcrx", X(31,387), X_MASK, BOOKE, { RA, RS } },
+
+{ "dcblc", X(31,390), X_MASK, PPCCHLK, { CT, RA, RB }},
+
+{ "subfe64", XO(31,392,0,0), XO_MASK, BOOKE64, { RT, RA, RB } },
+{ "subfe64o",XO(31,392,1,0), XO_MASK, BOOKE64, { RT, RA, RB } },
+
+{ "adde64", XO(31,394,0,0), XO_MASK, BOOKE64, { RT, RA, RB } },
+{ "adde64o", XO(31,394,1,0), XO_MASK, BOOKE64, { RT, RA, RB } },
+
+{ "dcblce", X(31,398), X_MASK, PPCCHLK64, { CT, RA, RB }},
+
+{ "slbmte", X(31,402), XRA_MASK, PPC64, { RS, RB } },
+
+{ "sthx", X(31,407), X_MASK, COM, { RS, RA0, RB } },
+
+{ "cmpb", X(31,508), X_MASK, POWER6, { RA, RS, RB } },
+
+{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } },
+
+{ "lfdpx", X(31,791), X_MASK, POWER6, { FRT, RA, RB } },
+
+{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } },
+
+{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } },
+
+{ "stfdpx", X(31,919), X_MASK, POWER6, { FRS, RA, RB } },
+
+{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } },
+
+{ "orc", XRC(31,412,0), X_MASK, COM, { RA, RS, RB } },
+{ "orc.", XRC(31,412,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "sradi", XS(31,413,0), XS_MASK, PPC64, { RA, RS, SH6 } },
+{ "sradi.", XS(31,413,1), XS_MASK, PPC64, { RA, RS, SH6 } },
+
+{ "sthxe", X(31,415), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "slbie", X(31,434), XRTRA_MASK, PPC64, { RB } },
+
+{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } },
+
+{ "sthux", X(31,439), X_MASK, COM, { RS, RAS, RB } },
+
+{ "sthuxe", X(31,447), X_MASK, BOOKE64, { RS, RAS, RB } },
+
+{ "cctpl", 0x7c210b78, 0xffffffff, CELL, { 0 }},
+{ "cctpm", 0x7c421378, 0xffffffff, CELL, { 0 }},
+{ "cctph", 0x7c631b78, 0xffffffff, CELL, { 0 }},
+{ "db8cyc", 0x7f9ce378, 0xffffffff, CELL, { 0 }},
+{ "db10cyc", 0x7fbdeb78, 0xffffffff, CELL, { 0 }},
+{ "db12cyc", 0x7fdef378, 0xffffffff, CELL, { 0 }},
+{ "db16cyc", 0x7ffffb78, 0xffffffff, CELL, { 0 }},
+{ "mr", XRC(31,444,0), X_MASK, COM, { RA, RS, RBS } },
+{ "or", XRC(31,444,0), X_MASK, COM, { RA, RS, RB } },
+{ "mr.", XRC(31,444,1), X_MASK, COM, { RA, RS, RBS } },
+{ "or.", XRC(31,444,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "mtexisr", XSPR(31,451,64), XSPR_MASK, PPC403, { RS } },
+{ "mtexier", XSPR(31,451,66), XSPR_MASK, PPC403, { RS } },
+{ "mtbr0", XSPR(31,451,128), XSPR_MASK, PPC403, { RS } },
+{ "mtbr1", XSPR(31,451,129), XSPR_MASK, PPC403, { RS } },
+{ "mtbr2", XSPR(31,451,130), XSPR_MASK, PPC403, { RS } },
+{ "mtbr3", XSPR(31,451,131), XSPR_MASK, PPC403, { RS } },
+{ "mtbr4", XSPR(31,451,132), XSPR_MASK, PPC403, { RS } },
+{ "mtbr5", XSPR(31,451,133), XSPR_MASK, PPC403, { RS } },
+{ "mtbr6", XSPR(31,451,134), XSPR_MASK, PPC403, { RS } },
+{ "mtbr7", XSPR(31,451,135), XSPR_MASK, PPC403, { RS } },
+{ "mtbear", XSPR(31,451,144), XSPR_MASK, PPC403, { RS } },
+{ "mtbesr", XSPR(31,451,145), XSPR_MASK, PPC403, { RS } },
+{ "mtiocr", XSPR(31,451,160), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacr0", XSPR(31,451,192), XSPR_MASK, PPC403, { RS } },
+{ "mtdmact0", XSPR(31,451,193), XSPR_MASK, PPC403, { RS } },
+{ "mtdmada0", XSPR(31,451,194), XSPR_MASK, PPC403, { RS } },
+{ "mtdmasa0", XSPR(31,451,195), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacc0", XSPR(31,451,196), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacr1", XSPR(31,451,200), XSPR_MASK, PPC403, { RS } },
+{ "mtdmact1", XSPR(31,451,201), XSPR_MASK, PPC403, { RS } },
+{ "mtdmada1", XSPR(31,451,202), XSPR_MASK, PPC403, { RS } },
+{ "mtdmasa1", XSPR(31,451,203), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacc1", XSPR(31,451,204), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacr2", XSPR(31,451,208), XSPR_MASK, PPC403, { RS } },
+{ "mtdmact2", XSPR(31,451,209), XSPR_MASK, PPC403, { RS } },
+{ "mtdmada2", XSPR(31,451,210), XSPR_MASK, PPC403, { RS } },
+{ "mtdmasa2", XSPR(31,451,211), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacc2", XSPR(31,451,212), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacr3", XSPR(31,451,216), XSPR_MASK, PPC403, { RS } },
+{ "mtdmact3", XSPR(31,451,217), XSPR_MASK, PPC403, { RS } },
+{ "mtdmada3", XSPR(31,451,218), XSPR_MASK, PPC403, { RS } },
+{ "mtdmasa3", XSPR(31,451,219), XSPR_MASK, PPC403, { RS } },
+{ "mtdmacc3", XSPR(31,451,220), XSPR_MASK, PPC403, { RS } },
+{ "mtdmasr", XSPR(31,451,224), XSPR_MASK, PPC403, { RS } },
+{ "mtdcr", X(31,451), X_MASK, PPC403 | BOOKE, { SPR, RS } },
+
+{ "subfze64",XO(31,456,0,0), XORB_MASK, BOOKE64, { RT, RA } },
+{ "subfze64o",XO(31,456,1,0), XORB_MASK, BOOKE64, { RT, RA } },
+
+{ "divdu", XO(31,457,0,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "divdu.", XO(31,457,0,1), XO_MASK, PPC64, { RT, RA, RB } },
+{ "divduo", XO(31,457,1,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "divduo.", XO(31,457,1,1), XO_MASK, PPC64, { RT, RA, RB } },
+
+{ "addze64", XO(31,458,0,0), XORB_MASK, BOOKE64, { RT, RA } },
+{ "addze64o",XO(31,458,1,0), XORB_MASK, BOOKE64, { RT, RA } },
+
+{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mtmq", XSPR(31,467,0), XSPR_MASK, M601, { RS } },
+{ "mtxer", XSPR(31,467,1), XSPR_MASK, COM, { RS } },
+{ "mtlr", XSPR(31,467,8), XSPR_MASK, COM, { RS } },
+{ "mtctr", XSPR(31,467,9), XSPR_MASK, COM, { RS } },
+{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } },
+{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, COM, { RS } },
+{ "mtdar", XSPR(31,467,19), XSPR_MASK, COM, { RS } },
+{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, COM, { RS } },
+{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, COM, { RS } },
+{ "mtdec", XSPR(31,467,22), XSPR_MASK, COM, { RS } },
+{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } },
+{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, COM, { RS } },
+{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, COM, { RS } },
+{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, COM, { RS } },
+{ "mtcfar", XSPR(31,467,28), XSPR_MASK, POWER6, { RS } },
+{ "mtpid", XSPR(31,467,48), XSPR_MASK, BOOKE, { RS } },
+{ "mtpid", XSPR(31,467,945), XSPR_MASK, PPC403, { RS } },
+{ "mtdecar", XSPR(31,467,54), XSPR_MASK, BOOKE, { RS } },
+{ "mtcsrr0", XSPR(31,467,58), XSPR_MASK, BOOKE, { RS } },
+{ "mtcsrr1", XSPR(31,467,59), XSPR_MASK, BOOKE, { RS } },
+{ "mtdear", XSPR(31,467,61), XSPR_MASK, BOOKE, { RS } },
+{ "mtdear", XSPR(31,467,981), XSPR_MASK, PPC403, { RS } },
+{ "mtesr", XSPR(31,467,62), XSPR_MASK, BOOKE, { RS } },
+{ "mtesr", XSPR(31,467,980), XSPR_MASK, PPC403, { RS } },
+{ "mtivpr", XSPR(31,467,63), XSPR_MASK, BOOKE, { RS } },
+{ "mtcmpa", XSPR(31,467,144), XSPR_MASK, PPC860, { RS } },
+{ "mtcmpb", XSPR(31,467,145), XSPR_MASK, PPC860, { RS } },
+{ "mtcmpc", XSPR(31,467,146), XSPR_MASK, PPC860, { RS } },
+{ "mtcmpd", XSPR(31,467,147), XSPR_MASK, PPC860, { RS } },
+{ "mticr", XSPR(31,467,148), XSPR_MASK, PPC860, { RS } },
+{ "mtder", XSPR(31,467,149), XSPR_MASK, PPC860, { RS } },
+{ "mtcounta", XSPR(31,467,150), XSPR_MASK, PPC860, { RS } },
+{ "mtcountb", XSPR(31,467,151), XSPR_MASK, PPC860, { RS } },
+{ "mtcmpe", XSPR(31,467,152), XSPR_MASK, PPC860, { RS } },
+{ "mtcmpf", XSPR(31,467,153), XSPR_MASK, PPC860, { RS } },
+{ "mtcmpg", XSPR(31,467,154), XSPR_MASK, PPC860, { RS } },
+{ "mtcmph", XSPR(31,467,155), XSPR_MASK, PPC860, { RS } },
+{ "mtlctrl1", XSPR(31,467,156), XSPR_MASK, PPC860, { RS } },
+{ "mtlctrl2", XSPR(31,467,157), XSPR_MASK, PPC860, { RS } },
+{ "mtictrl", XSPR(31,467,158), XSPR_MASK, PPC860, { RS } },
+{ "mtbar", XSPR(31,467,159), XSPR_MASK, PPC860, { RS } },
+{ "mtvrsave", XSPR(31,467,256), XSPR_MASK, PPCVEC, { RS } },
+{ "mtusprg0", XSPR(31,467,256), XSPR_MASK, BOOKE, { RS } },
+{ "mtsprg", XSPR(31,467,256), XSPRG_MASK,PPC, { SPRG, RS } },
+{ "mtsprg0", XSPR(31,467,272), XSPR_MASK, PPC, { RS } },
+{ "mtsprg1", XSPR(31,467,273), XSPR_MASK, PPC, { RS } },
+{ "mtsprg2", XSPR(31,467,274), XSPR_MASK, PPC, { RS } },
+{ "mtsprg3", XSPR(31,467,275), XSPR_MASK, PPC, { RS } },
+{ "mtsprg4", XSPR(31,467,276), XSPR_MASK, PPC405 | BOOKE, { RS } },
+{ "mtsprg5", XSPR(31,467,277), XSPR_MASK, PPC405 | BOOKE, { RS } },
+{ "mtsprg6", XSPR(31,467,278), XSPR_MASK, PPC405 | BOOKE, { RS } },
+{ "mtsprg7", XSPR(31,467,279), XSPR_MASK, PPC405 | BOOKE, { RS } },
+{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC64, { RS } },
+{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } },
+{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } },
+{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } },
+{ "mtdbsr", XSPR(31,467,304), XSPR_MASK, BOOKE, { RS } },
+{ "mtdbsr", XSPR(31,467,1008), XSPR_MASK, PPC403, { RS } },
+{ "mtdbcr0", XSPR(31,467,308), XSPR_MASK, BOOKE, { RS } },
+{ "mtdbcr0", XSPR(31,467,1010), XSPR_MASK, PPC405, { RS } },
+{ "mtdbcr1", XSPR(31,467,309), XSPR_MASK, BOOKE, { RS } },
+{ "mtdbcr1", XSPR(31,467,957), XSPR_MASK, PPC405, { RS } },
+{ "mtdbcr2", XSPR(31,467,310), XSPR_MASK, BOOKE, { RS } },
+{ "mtiac1", XSPR(31,467,312), XSPR_MASK, BOOKE, { RS } },
+{ "mtiac1", XSPR(31,467,1012), XSPR_MASK, PPC403, { RS } },
+{ "mtiac2", XSPR(31,467,313), XSPR_MASK, BOOKE, { RS } },
+{ "mtiac2", XSPR(31,467,1013), XSPR_MASK, PPC403, { RS } },
+{ "mtiac3", XSPR(31,467,314), XSPR_MASK, BOOKE, { RS } },
+{ "mtiac3", XSPR(31,467,948), XSPR_MASK, PPC405, { RS } },
+{ "mtiac4", XSPR(31,467,315), XSPR_MASK, BOOKE, { RS } },
+{ "mtiac4", XSPR(31,467,949), XSPR_MASK, PPC405, { RS } },
+{ "mtdac1", XSPR(31,467,316), XSPR_MASK, BOOKE, { RS } },
+{ "mtdac1", XSPR(31,467,1014), XSPR_MASK, PPC403, { RS } },
+{ "mtdac2", XSPR(31,467,317), XSPR_MASK, BOOKE, { RS } },
+{ "mtdac2", XSPR(31,467,1015), XSPR_MASK, PPC403, { RS } },
+{ "mtdvc1", XSPR(31,467,318), XSPR_MASK, BOOKE, { RS } },
+{ "mtdvc1", XSPR(31,467,950), XSPR_MASK, PPC405, { RS } },
+{ "mtdvc2", XSPR(31,467,319), XSPR_MASK, BOOKE, { RS } },
+{ "mtdvc2", XSPR(31,467,951), XSPR_MASK, PPC405, { RS } },
+{ "mttsr", XSPR(31,467,336), XSPR_MASK, BOOKE, { RS } },
+{ "mttsr", XSPR(31,467,984), XSPR_MASK, PPC403, { RS } },
+{ "mttcr", XSPR(31,467,340), XSPR_MASK, BOOKE, { RS } },
+{ "mttcr", XSPR(31,467,986), XSPR_MASK, PPC403, { RS } },
+{ "mtivor0", XSPR(31,467,400), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor1", XSPR(31,467,401), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor2", XSPR(31,467,402), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor3", XSPR(31,467,403), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor4", XSPR(31,467,404), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor5", XSPR(31,467,405), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor6", XSPR(31,467,406), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor7", XSPR(31,467,407), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor8", XSPR(31,467,408), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor9", XSPR(31,467,409), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor10", XSPR(31,467,410), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor11", XSPR(31,467,411), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor12", XSPR(31,467,412), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor13", XSPR(31,467,413), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor14", XSPR(31,467,414), XSPR_MASK, BOOKE, { RS } },
+{ "mtivor15", XSPR(31,467,415), XSPR_MASK, BOOKE, { RS } },
+{ "mtspefscr", XSPR(31,467,512), XSPR_MASK, PPCSPE, { RS } },
+{ "mtbbear", XSPR(31,467,513), XSPR_MASK, PPCBRLK, { RS } },
+{ "mtbbtar", XSPR(31,467,514), XSPR_MASK, PPCBRLK, { RS } },
+{ "mtivor32", XSPR(31,467,528), XSPR_MASK, PPCSPE, { RS } },
+{ "mtivor33", XSPR(31,467,529), XSPR_MASK, PPCSPE, { RS } },
+{ "mtivor34", XSPR(31,467,530), XSPR_MASK, PPCSPE, { RS } },
+{ "mtivor35", XSPR(31,467,531), XSPR_MASK, PPCPMR, { RS } },
+{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtmcsrr0", XSPR(31,467,570), XSPR_MASK, PPCRFMCI, { RS } },
+{ "mtmcsrr1", XSPR(31,467,571), XSPR_MASK, PPCRFMCI, { RS } },
+{ "mtmcsr", XSPR(31,467,572), XSPR_MASK, PPCRFMCI, { RS } },
+{ "mtummcr0", XSPR(31,467,936), XSPR_MASK, PPC750, { RS } },
+{ "mtupmc1", XSPR(31,467,937), XSPR_MASK, PPC750, { RS } },
+{ "mtupmc2", XSPR(31,467,938), XSPR_MASK, PPC750, { RS } },
+{ "mtusia", XSPR(31,467,939), XSPR_MASK, PPC750, { RS } },
+{ "mtummcr1", XSPR(31,467,940), XSPR_MASK, PPC750, { RS } },
+{ "mtupmc3", XSPR(31,467,941), XSPR_MASK, PPC750, { RS } },
+{ "mtupmc4", XSPR(31,467,942), XSPR_MASK, PPC750, { RS } },
+{ "mtzpr", XSPR(31,467,944), XSPR_MASK, PPC403, { RS } },
+{ "mtccr0", XSPR(31,467,947), XSPR_MASK, PPC405, { RS } },
+{ "mtmmcr0", XSPR(31,467,952), XSPR_MASK, PPC750, { RS } },
+{ "mtsgr", XSPR(31,467,953), XSPR_MASK, PPC403, { RS } },
+{ "mtpmc1", XSPR(31,467,953), XSPR_MASK, PPC750, { RS } },
+{ "mtdcwr", XSPR(31,467,954), XSPR_MASK, PPC403, { RS } },
+{ "mtpmc2", XSPR(31,467,954), XSPR_MASK, PPC750, { RS } },
+{ "mtsler", XSPR(31,467,955), XSPR_MASK, PPC405, { RS } },
+{ "mtsia", XSPR(31,467,955), XSPR_MASK, PPC750, { RS } },
+{ "mtsu0r", XSPR(31,467,956), XSPR_MASK, PPC405, { RS } },
+{ "mtmmcr1", XSPR(31,467,956), XSPR_MASK, PPC750, { RS } },
+{ "mtpmc3", XSPR(31,467,957), XSPR_MASK, PPC750, { RS } },
+{ "mtpmc4", XSPR(31,467,958), XSPR_MASK, PPC750, { RS } },
+{ "mticdbdr", XSPR(31,467,979), XSPR_MASK, PPC403, { RS } },
+{ "mtevpr", XSPR(31,467,982), XSPR_MASK, PPC403, { RS } },
+{ "mtcdbcr", XSPR(31,467,983), XSPR_MASK, PPC403, { RS } },
+{ "mtpit", XSPR(31,467,987), XSPR_MASK, PPC403, { RS } },
+{ "mttbhi", XSPR(31,467,988), XSPR_MASK, PPC403, { RS } },
+{ "mttblo", XSPR(31,467,989), XSPR_MASK, PPC403, { RS } },
+{ "mtsrr2", XSPR(31,467,990), XSPR_MASK, PPC403, { RS } },
+{ "mtsrr3", XSPR(31,467,991), XSPR_MASK, PPC403, { RS } },
+{ "mtl2cr", XSPR(31,467,1017), XSPR_MASK, PPC750, { RS } },
+{ "mtdccr", XSPR(31,467,1018), XSPR_MASK, PPC403, { RS } },
+{ "mticcr", XSPR(31,467,1019), XSPR_MASK, PPC403, { RS } },
+{ "mtictc", XSPR(31,467,1019), XSPR_MASK, PPC750, { RS } },
+{ "mtpbl1", XSPR(31,467,1020), XSPR_MASK, PPC403, { RS } },
+{ "mtthrm1", XSPR(31,467,1020), XSPR_MASK, PPC750, { RS } },
+{ "mtpbu1", XSPR(31,467,1021), XSPR_MASK, PPC403, { RS } },
+{ "mtthrm2", XSPR(31,467,1021), XSPR_MASK, PPC750, { RS } },
+{ "mtpbl2", XSPR(31,467,1022), XSPR_MASK, PPC403, { RS } },
+{ "mtthrm3", XSPR(31,467,1022), XSPR_MASK, PPC750, { RS } },
+{ "mtpbu2", XSPR(31,467,1023), XSPR_MASK, PPC403, { RS } },
+{ "mtspr", X(31,467), X_MASK, COM, { SPR, RS } },
+
+{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } },
+
+{ "nand", XRC(31,476,0), X_MASK, COM, { RA, RS, RB } },
+{ "nand.", XRC(31,476,1), X_MASK, COM, { RA, RS, RB } },
+
+{ "dcbie", X(31,478), XRT_MASK, BOOKE64, { RA, RB } },
+
+{ "dcread", X(31,486), X_MASK, PPC403|PPC440, { RT, RA, RB }},
+
+{ "mtpmr", X(31,462), X_MASK, PPCPMR, { PMR, RS }},
+
+{ "icbtls", X(31,486), X_MASK, PPCCHLK, { CT, RA, RB }},
+
+{ "nabs", XO(31,488,0,0), XORB_MASK, M601, { RT, RA } },
+{ "subfme64",XO(31,488,0,0), XORB_MASK, BOOKE64, { RT, RA } },
+{ "nabs.", XO(31,488,0,1), XORB_MASK, M601, { RT, RA } },
+{ "nabso", XO(31,488,1,0), XORB_MASK, M601, { RT, RA } },
+{ "subfme64o",XO(31,488,1,0), XORB_MASK, BOOKE64, { RT, RA } },
+{ "nabso.", XO(31,488,1,1), XORB_MASK, M601, { RT, RA } },
+
+{ "divd", XO(31,489,0,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "divd.", XO(31,489,0,1), XO_MASK, PPC64, { RT, RA, RB } },
+{ "divdo", XO(31,489,1,0), XO_MASK, PPC64, { RT, RA, RB } },
+{ "divdo.", XO(31,489,1,1), XO_MASK, PPC64, { RT, RA, RB } },
+
+{ "addme64", XO(31,490,0,0), XORB_MASK, BOOKE64, { RT, RA } },
+{ "addme64o",XO(31,490,1,0), XORB_MASK, BOOKE64, { RT, RA } },
+
+{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "icbtlse", X(31,494), X_MASK, PPCCHLK64, { CT, RA, RB }},
+
+{ "slbia", X(31,498), 0xffffffff, PPC64, { 0 } },
+
+{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } },
+
+{ "stdcxe.", XRC(31,511,1), X_MASK, BOOKE64, { RS, RA, RB } },
+
+{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), COM, { BF } },
+
+{ "bblels", X(31,518), X_MASK, PPCBRLK, { 0 }},
+{ "mcrxr64", X(31,544), XRARB_MASK|(3<<21), BOOKE64, { BF } },
+
+{ "clcs", X(31,531), XRB_MASK, M601, { RT, RA } },
+
+{ "ldbrx", X(31,532), X_MASK, CELL, { RT, RA0, RB } },
+
+{ "lswx", X(31,533), X_MASK, PPCCOM, { RT, RA0, RB } },
+{ "lsx", X(31,533), X_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "lwbrx", X(31,534), X_MASK, PPCCOM, { RT, RA0, RB } },
+{ "lbrx", X(31,534), X_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "lfsx", X(31,535), X_MASK, COM, { FRT, RA0, RB } },
+
+{ "srw", XRC(31,536,0), X_MASK, PPCCOM, { RA, RS, RB } },
+{ "sr", XRC(31,536,0), X_MASK, PWRCOM, { RA, RS, RB } },
+{ "srw.", XRC(31,536,1), X_MASK, PPCCOM, { RA, RS, RB } },
+{ "sr.", XRC(31,536,1), X_MASK, PWRCOM, { RA, RS, RB } },
+
+{ "rrib", XRC(31,537,0), X_MASK, M601, { RA, RS, RB } },
+{ "rrib.", XRC(31,537,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "srd", XRC(31,539,0), X_MASK, PPC64, { RA, RS, RB } },
+{ "srd.", XRC(31,539,1), X_MASK, PPC64, { RA, RS, RB } },
+
+{ "maskir", XRC(31,541,0), X_MASK, M601, { RA, RS, RB } },
+{ "maskir.", XRC(31,541,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "lwbrxe", X(31,542), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "lfsxe", X(31,543), X_MASK, BOOKE64, { FRT, RA0, RB } },
+
+{ "bbelr", X(31,550), X_MASK, PPCBRLK, { 0 }},
+
+{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } },
+
+{ "lfsux", X(31,567), X_MASK, COM, { FRT, RAS, RB } },
+
+{ "lfsuxe", X(31,575), X_MASK, BOOKE64, { FRT, RAS, RB } },
+
+{ "mfsr", X(31,595), XRB_MASK|(1<<20), COM32, { RT, SR } },
+
+{ "lswi", X(31,597), X_MASK, PPCCOM, { RT, RA0, NB } },
+{ "lsi", X(31,597), X_MASK, PWRCOM, { RT, RA0, NB } },
+
+{ "lwsync", XSYNC(31,598,1), 0xffffffff, PPC, { 0 } },
+{ "ptesync", XSYNC(31,598,2), 0xffffffff, PPC64, { 0 } },
+{ "msync", X(31,598), 0xffffffff, BOOKE, { 0 } },
+{ "sync", X(31,598), XSYNC_MASK, PPCCOM, { LS } },
+{ "dcs", X(31,598), 0xffffffff, PWRCOM, { 0 } },
+
+{ "lfdx", X(31,599), X_MASK, COM, { FRT, RA0, RB } },
+
+{ "lfdxe", X(31,607), X_MASK, BOOKE64, { FRT, RA0, RB } },
+
+{ "mffgpr", XRC(31,607,0), XRA_MASK, POWER6, { FRT, RB } },
+
+{ "mfsri", X(31,627), X_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "dclst", X(31,630), XRB_MASK, PWRCOM, { RS, RA } },
+
+{ "lfdux", X(31,631), X_MASK, COM, { FRT, RAS, RB } },
+
+{ "lfduxe", X(31,639), X_MASK, BOOKE64, { FRT, RAS, RB } },
+
+{ "mfsrin", X(31,659), XRA_MASK, PPC32, { RT, RB } },
+
+{ "stdbrx", X(31,660), X_MASK, CELL, { RS, RA0, RB } },
+
+{ "stswx", X(31,661), X_MASK, PPCCOM, { RS, RA0, RB } },
+{ "stsx", X(31,661), X_MASK, PWRCOM, { RS, RA0, RB } },
+
+{ "stwbrx", X(31,662), X_MASK, PPCCOM, { RS, RA0, RB } },
+{ "stbrx", X(31,662), X_MASK, PWRCOM, { RS, RA0, RB } },
+
+{ "stfsx", X(31,663), X_MASK, COM, { FRS, RA0, RB } },
+
+{ "srq", XRC(31,664,0), X_MASK, M601, { RA, RS, RB } },
+{ "srq.", XRC(31,664,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "sre", XRC(31,665,0), X_MASK, M601, { RA, RS, RB } },
+{ "sre.", XRC(31,665,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "stwbrxe", X(31,670), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "stfsxe", X(31,671), X_MASK, BOOKE64, { FRS, RA0, RB } },
+
+{ "stfsux", X(31,695), X_MASK, COM, { FRS, RAS, RB } },
+
+{ "sriq", XRC(31,696,0), X_MASK, M601, { RA, RS, SH } },
+{ "sriq.", XRC(31,696,1), X_MASK, M601, { RA, RS, SH } },
+
+{ "stfsuxe", X(31,703), X_MASK, BOOKE64, { FRS, RAS, RB } },
+
+{ "stswi", X(31,725), X_MASK, PPCCOM, { RS, RA0, NB } },
+{ "stsi", X(31,725), X_MASK, PWRCOM, { RS, RA0, NB } },
+
+{ "stfdx", X(31,727), X_MASK, COM, { FRS, RA0, RB } },
+
+{ "srlq", XRC(31,728,0), X_MASK, M601, { RA, RS, RB } },
+{ "srlq.", XRC(31,728,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "sreq", XRC(31,729,0), X_MASK, M601, { RA, RS, RB } },
+{ "sreq.", XRC(31,729,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "stfdxe", X(31,735), X_MASK, BOOKE64, { FRS, RA0, RB } },
+
+{ "mftgpr", XRC(31,735,0), XRA_MASK, POWER6, { RT, FRB } },
+
+{ "dcba", X(31,758), XRT_MASK, PPC405 | BOOKE, { RA, RB } },
+
+{ "stfdux", X(31,759), X_MASK, COM, { FRS, RAS, RB } },
+
+{ "srliq", XRC(31,760,0), X_MASK, M601, { RA, RS, SH } },
+{ "srliq.", XRC(31,760,1), X_MASK, M601, { RA, RS, SH } },
+
+{ "dcbae", X(31,766), XRT_MASK, BOOKE64, { RA, RB } },
+
+{ "stfduxe", X(31,767), X_MASK, BOOKE64, { FRS, RAS, RB } },
+
+{ "tlbivax", X(31,786), XRT_MASK, BOOKE, { RA, RB } },
+{ "tlbivaxe",X(31,787), XRT_MASK, BOOKE64, { RA, RB } },
+
+{ "lwzcix", X(31,789), X_MASK, POWER6, { RT, RA0, RB } },
+
+{ "lhbrx", X(31,790), X_MASK, COM, { RT, RA0, RB } },
+
+{ "sraw", XRC(31,792,0), X_MASK, PPCCOM, { RA, RS, RB } },
+{ "sra", XRC(31,792,0), X_MASK, PWRCOM, { RA, RS, RB } },
+{ "sraw.", XRC(31,792,1), X_MASK, PPCCOM, { RA, RS, RB } },
+{ "sra.", XRC(31,792,1), X_MASK, PWRCOM, { RA, RS, RB } },
+
+{ "srad", XRC(31,794,0), X_MASK, PPC64, { RA, RS, RB } },
+{ "srad.", XRC(31,794,1), X_MASK, PPC64, { RA, RS, RB } },
+
+{ "lhbrxe", X(31,798), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "ldxe", X(31,799), X_MASK, BOOKE64, { RT, RA0, RB } },
+{ "lduxe", X(31,831), X_MASK, BOOKE64, { RT, RA0, RB } },
+
+{ "rac", X(31,818), X_MASK, PWRCOM, { RT, RA, RB } },
+
+{ "lhzcix", X(31,821), X_MASK, POWER6, { RT, RA0, RB } },
+
+{ "dss", XDSS(31,822,0), XDSS_MASK, PPCVEC, { STRM } },
+{ "dssall", XDSS(31,822,1), XDSS_MASK, PPCVEC, { 0 } },
+
+{ "srawi", XRC(31,824,0), X_MASK, PPCCOM, { RA, RS, SH } },
+{ "srai", XRC(31,824,0), X_MASK, PWRCOM, { RA, RS, SH } },
+{ "srawi.", XRC(31,824,1), X_MASK, PPCCOM, { RA, RS, SH } },
+{ "srai.", XRC(31,824,1), X_MASK, PWRCOM, { RA, RS, SH } },
+
+{ "slbmfev", X(31,851), XRA_MASK, PPC64, { RT, RB } },
+
+{ "lbzcix", X(31,853), X_MASK, POWER6, { RT, RA0, RB } },
+
+{ "mbar", X(31,854), X_MASK, BOOKE, { MO } },
+{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } },
+
+{ "lfiwax", X(31,855), X_MASK, POWER6, { FRT, RA0, RB } },
+
+{ "ldcix", X(31,885), X_MASK, POWER6, { RT, RA0, RB } },
+
+{ "tlbsx", XRC(31,914,0), X_MASK, PPC403|BOOKE, { RTO, RA, RB } },
+{ "tlbsx.", XRC(31,914,1), X_MASK, PPC403|BOOKE, { RTO, RA, RB } },
+{ "tlbsxe", XRC(31,915,0), X_MASK, BOOKE64, { RTO, RA, RB } },
+{ "tlbsxe.", XRC(31,915,1), X_MASK, BOOKE64, { RTO, RA, RB } },
+
+{ "slbmfee", X(31,915), XRA_MASK, PPC64, { RT, RB } },
+
+{ "stwcix", X(31,917), X_MASK, POWER6, { RS, RA0, RB } },
+
+{ "sthbrx", X(31,918), X_MASK, COM, { RS, RA0, RB } },
+
+{ "sraq", XRC(31,920,0), X_MASK, M601, { RA, RS, RB } },
+{ "sraq.", XRC(31,920,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "srea", XRC(31,921,0), X_MASK, M601, { RA, RS, RB } },
+{ "srea.", XRC(31,921,1), X_MASK, M601, { RA, RS, RB } },
+
+{ "extsh", XRC(31,922,0), XRB_MASK, PPCCOM, { RA, RS } },
+{ "exts", XRC(31,922,0), XRB_MASK, PWRCOM, { RA, RS } },
+{ "extsh.", XRC(31,922,1), XRB_MASK, PPCCOM, { RA, RS } },
+{ "exts.", XRC(31,922,1), XRB_MASK, PWRCOM, { RA, RS } },
+
+{ "sthbrxe", X(31,926), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "stdxe", X(31,927), X_MASK, BOOKE64, { RS, RA0, RB } },
+
+{ "tlbrehi", XTLB(31,946,0), XTLB_MASK, PPC403, { RT, RA } },
+{ "tlbrelo", XTLB(31,946,1), XTLB_MASK, PPC403, { RT, RA } },
+{ "tlbre", X(31,946), X_MASK, PPC403|BOOKE, { RSO, RAOPT, SHO } },
+
+{ "sthcix", X(31,949), X_MASK, POWER6, { RS, RA0, RB } },
+
+{ "sraiq", XRC(31,952,0), X_MASK, M601, { RA, RS, SH } },
+{ "sraiq.", XRC(31,952,1), X_MASK, M601, { RA, RS, SH } },
+
+{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} },
+{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} },
+
+{ "stduxe", X(31,959), X_MASK, BOOKE64, { RS, RAS, RB } },
+
+{ "iccci", X(31,966), XRT_MASK, PPC403|PPC440, { RA, RB } },
+
+{ "tlbwehi", XTLB(31,978,0), XTLB_MASK, PPC403, { RT, RA } },
+{ "tlbwelo", XTLB(31,978,1), XTLB_MASK, PPC403, { RT, RA } },
+{ "tlbwe", X(31,978), X_MASK, PPC403|BOOKE, { RSO, RAOPT, SHO } },
+{ "tlbld", X(31,978), XRTRA_MASK, PPC, { RB } },
+
+{ "stbcix", X(31,981), X_MASK, POWER6, { RS, RA0, RB } },
+
+{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } },
+
+{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA0, RB } },
+
+{ "extsw", XRC(31,986,0), XRB_MASK, PPC64 | BOOKE64,{ RA, RS } },
+{ "extsw.", XRC(31,986,1), XRB_MASK, PPC64, { RA, RS } },
+
+{ "icread", X(31,998), XRT_MASK, PPC403|PPC440, { RA, RB } },
+
+{ "icbie", X(31,990), XRT_MASK, BOOKE64, { RA, RB } },
+{ "stfiwxe", X(31,991), X_MASK, BOOKE64, { FRS, RA0, RB } },
+
+{ "tlbli", X(31,1010), XRTRA_MASK, PPC, { RB } },
+
+{ "stdcix", X(31,1013), X_MASK, POWER6, { RS, RA0, RB } },
+
+{ "dcbzl", XOPL(31,1014,1), XRT_MASK,POWER4, { RA, RB } },
+{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } },
+{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } },
+
+{ "dcbze", X(31,1022), XRT_MASK, BOOKE64, { RA, RB } },
+
+{ "lvebx", X(31, 7), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "lvehx", X(31, 39), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "lvewx", X(31, 71), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "lvsl", X(31, 6), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "lvsr", X(31, 38), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "lvx", X(31, 103), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "lvxl", X(31, 359), X_MASK, PPCVEC, { VD, RA, RB } },
+{ "stvebx", X(31, 135), X_MASK, PPCVEC, { VS, RA, RB } },
+{ "stvehx", X(31, 167), X_MASK, PPCVEC, { VS, RA, RB } },
+{ "stvewx", X(31, 199), X_MASK, PPCVEC, { VS, RA, RB } },
+{ "stvx", X(31, 231), X_MASK, PPCVEC, { VS, RA, RB } },
+{ "stvxl", X(31, 487), X_MASK, PPCVEC, { VS, RA, RB } },
+
+/* New load/store left/right index vector instructions that are in the Cell only. */
+{ "lvlx", X(31, 519), X_MASK, CELL, { VD, RA0, RB } },
+{ "lvlxl", X(31, 775), X_MASK, CELL, { VD, RA0, RB } },
+{ "lvrx", X(31, 551), X_MASK, CELL, { VD, RA0, RB } },
+{ "lvrxl", X(31, 807), X_MASK, CELL, { VD, RA0, RB } },
+{ "stvlx", X(31, 647), X_MASK, CELL, { VS, RA0, RB } },
+{ "stvlxl", X(31, 903), X_MASK, CELL, { VS, RA0, RB } },
+{ "stvrx", X(31, 679), X_MASK, CELL, { VS, RA0, RB } },
+{ "stvrxl", X(31, 935), X_MASK, CELL, { VS, RA0, RB } },
+
+{ "lwz", OP(32), OP_MASK, PPCCOM, { RT, D, RA0 } },
+{ "l", OP(32), OP_MASK, PWRCOM, { RT, D, RA0 } },
+
+{ "lwzu", OP(33), OP_MASK, PPCCOM, { RT, D, RAL } },
+{ "lu", OP(33), OP_MASK, PWRCOM, { RT, D, RA0 } },
+
+{ "lbz", OP(34), OP_MASK, COM, { RT, D, RA0 } },
+
+{ "lbzu", OP(35), OP_MASK, COM, { RT, D, RAL } },
+
+{ "stw", OP(36), OP_MASK, PPCCOM, { RS, D, RA0 } },
+{ "st", OP(36), OP_MASK, PWRCOM, { RS, D, RA0 } },
+
+{ "stwu", OP(37), OP_MASK, PPCCOM, { RS, D, RAS } },
+{ "stu", OP(37), OP_MASK, PWRCOM, { RS, D, RA0 } },
+
+{ "stb", OP(38), OP_MASK, COM, { RS, D, RA0 } },
+
+{ "stbu", OP(39), OP_MASK, COM, { RS, D, RAS } },
+
+{ "lhz", OP(40), OP_MASK, COM, { RT, D, RA0 } },
+
+{ "lhzu", OP(41), OP_MASK, COM, { RT, D, RAL } },
+
+{ "lha", OP(42), OP_MASK, COM, { RT, D, RA0 } },
+
+{ "lhau", OP(43), OP_MASK, COM, { RT, D, RAL } },
+
+{ "sth", OP(44), OP_MASK, COM, { RS, D, RA0 } },
+
+{ "sthu", OP(45), OP_MASK, COM, { RS, D, RAS } },
+
+{ "lmw", OP(46), OP_MASK, PPCCOM, { RT, D, RAM } },
+{ "lm", OP(46), OP_MASK, PWRCOM, { RT, D, RA0 } },
+
+{ "stmw", OP(47), OP_MASK, PPCCOM, { RS, D, RA0 } },
+{ "stm", OP(47), OP_MASK, PWRCOM, { RS, D, RA0 } },
+
+{ "lfs", OP(48), OP_MASK, COM, { FRT, D, RA0 } },
+
+{ "lfsu", OP(49), OP_MASK, COM, { FRT, D, RAS } },
+
+{ "lfd", OP(50), OP_MASK, COM, { FRT, D, RA0 } },
+
+{ "lfdu", OP(51), OP_MASK, COM, { FRT, D, RAS } },
+
+{ "stfs", OP(52), OP_MASK, COM, { FRS, D, RA0 } },
+
+{ "stfsu", OP(53), OP_MASK, COM, { FRS, D, RAS } },
+
+{ "stfd", OP(54), OP_MASK, COM, { FRS, D, RA0 } },
+
+{ "stfdu", OP(55), OP_MASK, COM, { FRS, D, RAS } },
+
+{ "lq", OP(56), OP_MASK, POWER4, { RTQ, DQ, RAQ } },
+
+{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA0 } },
+
+{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA0 } },
+
+{ "lfdp", OP(57), OP_MASK, POWER6, { FRT, D, RA0 } },
+
+{ "lbze", DEO(58,0), DE_MASK, BOOKE64, { RT, DE, RA0 } },
+{ "lbzue", DEO(58,1), DE_MASK, BOOKE64, { RT, DE, RAL } },
+{ "lhze", DEO(58,2), DE_MASK, BOOKE64, { RT, DE, RA0 } },
+{ "lhzue", DEO(58,3), DE_MASK, BOOKE64, { RT, DE, RAL } },
+{ "lhae", DEO(58,4), DE_MASK, BOOKE64, { RT, DE, RA0 } },
+{ "lhaue", DEO(58,5), DE_MASK, BOOKE64, { RT, DE, RAL } },
+{ "lwze", DEO(58,6), DE_MASK, BOOKE64, { RT, DE, RA0 } },
+{ "lwzue", DEO(58,7), DE_MASK, BOOKE64, { RT, DE, RAL } },
+{ "stbe", DEO(58,8), DE_MASK, BOOKE64, { RS, DE, RA0 } },
+{ "stbue", DEO(58,9), DE_MASK, BOOKE64, { RS, DE, RAS } },
+{ "sthe", DEO(58,10), DE_MASK, BOOKE64, { RS, DE, RA0 } },
+{ "sthue", DEO(58,11), DE_MASK, BOOKE64, { RS, DE, RAS } },
+{ "stwe", DEO(58,14), DE_MASK, BOOKE64, { RS, DE, RA0 } },
+{ "stwue", DEO(58,15), DE_MASK, BOOKE64, { RS, DE, RAS } },
+
+{ "ld", DSO(58,0), DS_MASK, PPC64, { RT, DS, RA0 } },
+
+{ "ldu", DSO(58,1), DS_MASK, PPC64, { RT, DS, RAL } },
+
+{ "lwa", DSO(58,2), DS_MASK, PPC64, { RT, DS, RA0 } },
+
+{ "dadd", XRC(59,2,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "dadd.", XRC(59,2,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "dqua", ZRC(59,3,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+{ "dqua.", ZRC(59,3,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+
+{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fres", A(59,24,0), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } },
+{ "fres.", A(59,24,1), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } },
+
+{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+
+{ "frsqrtes", A(59,26,0), AFRALFRC_MASK,POWER5, { FRT, FRB, A_L } },
+{ "frsqrtes.",A(59,26,1), AFRALFRC_MASK,POWER5, { FRT, FRB, A_L } },
+
+{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "dmul", XRC(59,34,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "dmul.", XRC(59,34,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "drrnd", ZRC(59,35,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+{ "drrnd.", ZRC(59,35,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+
+{ "dscli", ZRC(59,66,0), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+{ "dscli.", ZRC(59,66,1), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+
+{ "dquai", ZRC(59,67,0), Z2_MASK, POWER6, { TE, FRT, FRB, RMC } },
+{ "dquai.", ZRC(59,67,1), Z2_MASK, POWER6, { TE, FRT, FRB, RMC } },
+
+{ "dscri", ZRC(59,98,0), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+{ "dscri.", ZRC(59,98,1), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+
+{ "drintx", ZRC(59,99,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+{ "drintx.", ZRC(59,99,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+
+{ "dcmpo", X(59,130), X_MASK, POWER6, { BF, FRA, FRB } },
+
+{ "dtstex", X(59,162), X_MASK, POWER6, { BF, FRA, FRB } },
+{ "dtstdc", Z(59,194), Z_MASK, POWER6, { BF, FRA, DCM } },
+{ "dtstdg", Z(59,226), Z_MASK, POWER6, { BF, FRA, DGM } },
+
+{ "drintn", ZRC(59,227,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+{ "drintn.", ZRC(59,227,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+
+{ "dctdp", XRC(59,258,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dctdp.", XRC(59,258,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "dctfix", XRC(59,290,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dctfix.", XRC(59,290,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "ddedpd", XRC(59,322,0), X_MASK, POWER6, { SP, FRT, FRB } },
+{ "ddedpd.", XRC(59,322,1), X_MASK, POWER6, { SP, FRT, FRB } },
+
+{ "dxex", XRC(59,354,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dxex.", XRC(59,354,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "dsub", XRC(59,514,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "dsub.", XRC(59,514,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "ddiv", XRC(59,546,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "ddiv.", XRC(59,546,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "dcmpu", X(59,642), X_MASK, POWER6, { BF, FRA, FRB } },
+
+{ "dtstsf", X(59,674), X_MASK, POWER6, { BF, FRA, FRB } },
+
+{ "drsp", XRC(59,770,0), X_MASK, POWER6, { FRT, FRB } },
+{ "drsp.", XRC(59,770,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "dcffix", XRC(59,802,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dcffix.", XRC(59,802,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "denbcd", XRC(59,834,0), X_MASK, POWER6, { S, FRT, FRB } },
+{ "denbcd.", XRC(59,834,1), X_MASK, POWER6, { S, FRT, FRB } },
+
+{ "diex", XRC(59,866,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "diex.", XRC(59,866,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } },
+
+{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } },
+
+{ "stfdp", OP(61), OP_MASK, POWER6, { FRT, D, RA0 } },
+
+{ "lde", DEO(62,0), DE_MASK, BOOKE64, { RT, DES, RA0 } },
+{ "ldue", DEO(62,1), DE_MASK, BOOKE64, { RT, DES, RA0 } },
+{ "lfse", DEO(62,4), DE_MASK, BOOKE64, { FRT, DES, RA0 } },
+{ "lfsue", DEO(62,5), DE_MASK, BOOKE64, { FRT, DES, RAS } },
+{ "lfde", DEO(62,6), DE_MASK, BOOKE64, { FRT, DES, RA0 } },
+{ "lfdue", DEO(62,7), DE_MASK, BOOKE64, { FRT, DES, RAS } },
+{ "stde", DEO(62,8), DE_MASK, BOOKE64, { RS, DES, RA0 } },
+{ "stdue", DEO(62,9), DE_MASK, BOOKE64, { RS, DES, RAS } },
+{ "stfse", DEO(62,12), DE_MASK, BOOKE64, { FRS, DES, RA0 } },
+{ "stfsue", DEO(62,13), DE_MASK, BOOKE64, { FRS, DES, RAS } },
+{ "stfde", DEO(62,14), DE_MASK, BOOKE64, { FRS, DES, RA0 } },
+{ "stfdue", DEO(62,15), DE_MASK, BOOKE64, { FRS, DES, RAS } },
+
+{ "std", DSO(62,0), DS_MASK, PPC64, { RS, DS, RA0 } },
+
+{ "stdu", DSO(62,1), DS_MASK, PPC64, { RS, DS, RAS } },
+
+{ "stq", DSO(62,2), DS_MASK, POWER4, { RSQ, DS, RA0 } },
+
+{ "fcmpu", X(63,0), X_MASK|(3<<21), COM, { BF, FRA, FRB } },
+
+{ "daddq", XRC(63,2,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "daddq.", XRC(63,2,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "dquaq", ZRC(63,3,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+{ "dquaq.", ZRC(63,3,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+
+{ "fcpsgn", XRC(63,8,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "fcpsgn.", XRC(63,8,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "frsp", XRC(63,12,0), XRA_MASK, COM, { FRT, FRB } },
+{ "frsp.", XRC(63,12,1), XRA_MASK, COM, { FRT, FRB } },
+
+{ "fctiw", XRC(63,14,0), XRA_MASK, PPCCOM, { FRT, FRB } },
+{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } },
+{ "fctiw.", XRC(63,14,1), XRA_MASK, PPCCOM, { FRT, FRB } },
+{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } },
+
+{ "fctiwz", XRC(63,15,0), XRA_MASK, PPCCOM, { FRT, FRB } },
+{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } },
+{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPCCOM, { FRT, FRB } },
+{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } },
+
+{ "fdiv", A(63,18,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } },
+{ "fd", A(63,18,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } },
+{ "fdiv.", A(63,18,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } },
+{ "fd.", A(63,18,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } },
+
+{ "fsub", A(63,20,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } },
+{ "fs", A(63,20,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } },
+{ "fsub.", A(63,20,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } },
+{ "fs.", A(63,20,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } },
+
+{ "fadd", A(63,21,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } },
+{ "fa", A(63,21,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } },
+{ "fadd.", A(63,21,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } },
+{ "fa.", A(63,21,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } },
+
+{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } },
+{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } },
+
+{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fre", A(63,24,0), AFRALFRC_MASK, POWER5, { FRT, FRB, A_L } },
+{ "fre.", A(63,24,1), AFRALFRC_MASK, POWER5, { FRT, FRB, A_L } },
+
+{ "fmul", A(63,25,0), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } },
+{ "fm", A(63,25,0), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } },
+{ "fmul.", A(63,25,1), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } },
+{ "fm.", A(63,25,1), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } },
+
+{ "frsqrte", A(63,26,0), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } },
+{ "frsqrte.",A(63,26,1), AFRALFRC_MASK, PPC, { FRT, FRB, A_L } },
+
+{ "fmsub", A(63,28,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fms", A(63,28,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+{ "fmsub.", A(63,28,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fms.", A(63,28,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+
+{ "fmadd", A(63,29,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fma", A(63,29,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+{ "fmadd.", A(63,29,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fma.", A(63,29,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+
+{ "fnmsub", A(63,30,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fnms", A(63,30,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+{ "fnmsub.", A(63,30,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fnms.", A(63,30,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+
+{ "fnmadd", A(63,31,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fnma", A(63,31,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+{ "fnmadd.", A(63,31,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } },
+{ "fnma.", A(63,31,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } },
+
+{ "fcmpo", X(63,32), X_MASK|(3<<21), COM, { BF, FRA, FRB } },
+
+{ "dmulq", XRC(63,34,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "dmulq.", XRC(63,34,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "drrndq", ZRC(63,35,0), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+{ "drrndq.", ZRC(63,35,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+
+{ "mtfsb1", XRC(63,38,0), XRARB_MASK, COM, { BT } },
+{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, COM, { BT } },
+
+{ "fneg", XRC(63,40,0), XRA_MASK, COM, { FRT, FRB } },
+{ "fneg.", XRC(63,40,1), XRA_MASK, COM, { FRT, FRB } },
+
+{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), COM, { BF, BFA } },
+
+{ "dscliq", ZRC(63,66,0), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+{ "dscliq.", ZRC(63,66,1), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+
+{ "dquaiq", ZRC(63,67,0), Z2_MASK, POWER6, { TE, FRT, FRB, RMC } },
+{ "dquaiq.", ZRC(63,67,1), Z2_MASK, POWER6, { FRT, FRA, FRB, RMC } },
+
+{ "mtfsb0", XRC(63,70,0), XRARB_MASK, COM, { BT } },
+{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, COM, { BT } },
+
+{ "fmr", XRC(63,72,0), XRA_MASK, COM, { FRT, FRB } },
+{ "fmr.", XRC(63,72,1), XRA_MASK, COM, { FRT, FRB } },
+
+{ "dscriq", ZRC(63,98,0), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+{ "dscriq.", ZRC(63,98,1), Z_MASK, POWER6, { FRT, FRA, SH16 } },
+
+{ "drintxq", ZRC(63,99,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+{ "drintxq.",ZRC(63,99,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+
+{ "dcmpoq", X(63,130), X_MASK, POWER6, { BF, FRA, FRB } },
+
+{ "mtfsfi", XRC(63,134,0), XWRA_MASK|(3<<21)|(1<<11), COM, { BFF, U, W } },
+{ "mtfsfi.", XRC(63,134,1), XWRA_MASK|(3<<21)|(1<<11), COM, { BFF, U, W } },
+
+{ "fnabs", XRC(63,136,0), XRA_MASK, COM, { FRT, FRB } },
+{ "fnabs.", XRC(63,136,1), XRA_MASK, COM, { FRT, FRB } },
+
+{ "dtstexq", X(63,162), X_MASK, POWER6, { BF, FRA, FRB } },
+{ "dtstdcq", Z(63,194), Z_MASK, POWER6, { BF, FRA, DCM } },
+{ "dtstdgq", Z(63,226), Z_MASK, POWER6, { BF, FRA, DGM } },
+
+{ "drintnq", ZRC(63,227,0), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+{ "drintnq.",ZRC(63,227,1), Z2_MASK, POWER6, { R, FRT, FRB, RMC } },
+
+{ "dctqpq", XRC(63,258,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dctqpq.", XRC(63,258,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "fabs", XRC(63,264,0), XRA_MASK, COM, { FRT, FRB } },
+{ "fabs.", XRC(63,264,1), XRA_MASK, COM, { FRT, FRB } },
+
+{ "dctfixq", XRC(63,290,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dctfixq.",XRC(63,290,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "ddedpdq", XRC(63,322,0), X_MASK, POWER6, { SP, FRT, FRB } },
+{ "ddedpdq.",XRC(63,322,1), X_MASK, POWER6, { SP, FRT, FRB } },
+
+{ "dxexq", XRC(63,354,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dxexq.", XRC(63,354,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "frin", XRC(63,392,0), XRA_MASK, POWER5, { FRT, FRB } },
+{ "frin.", XRC(63,392,1), XRA_MASK, POWER5, { FRT, FRB } },
+{ "friz", XRC(63,424,0), XRA_MASK, POWER5, { FRT, FRB } },
+{ "friz.", XRC(63,424,1), XRA_MASK, POWER5, { FRT, FRB } },
+{ "frip", XRC(63,456,0), XRA_MASK, POWER5, { FRT, FRB } },
+{ "frip.", XRC(63,456,1), XRA_MASK, POWER5, { FRT, FRB } },
+{ "frim", XRC(63,488,0), XRA_MASK, POWER5, { FRT, FRB } },
+{ "frim.", XRC(63,488,1), XRA_MASK, POWER5, { FRT, FRB } },
+
+{ "dsubq", XRC(63,514,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "dsubq.", XRC(63,514,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "ddivq", XRC(63,546,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "ddivq.", XRC(63,546,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+{ "mffs", XRC(63,583,0), XRARB_MASK, COM, { FRT } },
+{ "mffs.", XRC(63,583,1), XRARB_MASK, COM, { FRT } },
+
+{ "dcmpuq", X(63,642), X_MASK, POWER6, { BF, FRA, FRB } },
+
+{ "dtstsfq", X(63,674), X_MASK, POWER6, { BF, FRA, FRB } },
+
+{ "mtfsf", XFL(63,711,0), XFL_MASK, COM, { FLM, FRB, XFL_L, W } },
+{ "mtfsf.", XFL(63,711,1), XFL_MASK, COM, { FLM, FRB, XFL_L, W } },
+
+{ "drdpq", XRC(63,770,0), X_MASK, POWER6, { FRT, FRB } },
+{ "drdpq.", XRC(63,770,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "dcffixq", XRC(63,802,0), X_MASK, POWER6, { FRT, FRB } },
+{ "dcffixq.",XRC(63,802,1), X_MASK, POWER6, { FRT, FRB } },
+
+{ "fctid", XRC(63,814,0), XRA_MASK, PPC64, { FRT, FRB } },
+{ "fctid.", XRC(63,814,1), XRA_MASK, PPC64, { FRT, FRB } },
+
+{ "fctidz", XRC(63,815,0), XRA_MASK, PPC64, { FRT, FRB } },
+{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC64, { FRT, FRB } },
+
+{ "denbcdq", XRC(63,834,0), X_MASK, POWER6, { S, FRT, FRB } },
+{ "denbcdq.",XRC(63,834,1), X_MASK, POWER6, { S, FRT, FRB } },
+
+{ "fcfid", XRC(63,846,0), XRA_MASK, PPC64, { FRT, FRB } },
+{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC64, { FRT, FRB } },
+
+{ "diexq", XRC(63,866,0), X_MASK, POWER6, { FRT, FRA, FRB } },
+{ "diexq.", XRC(63,866,1), X_MASK, POWER6, { FRT, FRA, FRB } },
+
+};
+
+const int powerpc_num_opcodes =
+ sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
+
+/* The macro table. This is only used by the assembler. */
+
+/* The expressions of the form (-x ! 31) & (x | 31) have the value 0
+ when x=0; 32-x when x is between 1 and 31; are negative if x is
+ negative; and are 32 or more otherwise. This is what you want
+ when, for instance, you are emulating a right shift by a
+ rotate-left-and-mask, because the underlying instructions support
+ shifts of size 0 but not shifts of size 32. By comparison, when
+ extracting x bits from some word you want to use just 32-x, because
+ the underlying instructions don't support extracting 0 bits but do
+ support extracting the whole word (32 bits in this case). */
+
+const struct powerpc_macro powerpc_macros[] = {
+{ "extldi", 4, PPC64, "rldicr %0,%1,%3,(%2)-1" },
+{ "extldi.", 4, PPC64, "rldicr. %0,%1,%3,(%2)-1" },
+{ "extrdi", 4, PPC64, "rldicl %0,%1,(%2)+(%3),64-(%2)" },
+{ "extrdi.", 4, PPC64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" },
+{ "insrdi", 4, PPC64, "rldimi %0,%1,64-((%2)+(%3)),%3" },
+{ "insrdi.", 4, PPC64, "rldimi. %0,%1,64-((%2)+(%3)),%3" },
+{ "rotrdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),0" },
+{ "rotrdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),0" },
+{ "sldi", 3, PPC64, "rldicr %0,%1,%2,63-(%2)" },
+{ "sldi.", 3, PPC64, "rldicr. %0,%1,%2,63-(%2)" },
+{ "srdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),%2" },
+{ "srdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),%2" },
+{ "clrrdi", 3, PPC64, "rldicr %0,%1,0,63-(%2)" },
+{ "clrrdi.", 3, PPC64, "rldicr. %0,%1,0,63-(%2)" },
+{ "clrlsldi",4, PPC64, "rldic %0,%1,%3,(%2)-(%3)" },
+{ "clrlsldi.",4, PPC64, "rldic. %0,%1,%3,(%2)-(%3)" },
+
+{ "extlwi", 4, PPCCOM, "rlwinm %0,%1,%3,0,(%2)-1" },
+{ "extlwi.", 4, PPCCOM, "rlwinm. %0,%1,%3,0,(%2)-1" },
+{ "extrwi", 4, PPCCOM, "rlwinm %0,%1,((%2)+(%3))&((%2)+(%3)<>32),32-(%2),31" },
+{ "extrwi.", 4, PPCCOM, "rlwinm. %0,%1,((%2)+(%3))&((%2)+(%3)<>32),32-(%2),31" },
+{ "inslwi", 4, PPCCOM, "rlwimi %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1" },
+{ "inslwi.", 4, PPCCOM, "rlwimi. %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1"},
+{ "insrwi", 4, PPCCOM, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" },
+{ "insrwi.", 4, PPCCOM, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"},
+{ "rotrwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),0,31" },
+{ "rotrwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),0,31" },
+{ "slwi", 3, PPCCOM, "rlwinm %0,%1,%2,0,31-(%2)" },
+{ "sli", 3, PWRCOM, "rlinm %0,%1,%2,0,31-(%2)" },
+{ "slwi.", 3, PPCCOM, "rlwinm. %0,%1,%2,0,31-(%2)" },
+{ "sli.", 3, PWRCOM, "rlinm. %0,%1,%2,0,31-(%2)" },
+{ "srwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" },
+{ "sri", 3, PWRCOM, "rlinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" },
+{ "srwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" },
+{ "sri.", 3, PWRCOM, "rlinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" },
+{ "clrrwi", 3, PPCCOM, "rlwinm %0,%1,0,0,31-(%2)" },
+{ "clrrwi.", 3, PPCCOM, "rlwinm. %0,%1,0,0,31-(%2)" },
+{ "clrlslwi",4, PPCCOM, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" },
+{ "clrlslwi.",4, PPCCOM, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" },
+};
+
+const int powerpc_num_macros =
+ sizeof (powerpc_macros) / sizeof (powerpc_macros[0]);
+
+
+/* This file provides several disassembler functions, all of which use
+ the disassembler interface defined in dis-asm.h. Several functions
+ are provided because this file handles disassembly for the PowerPC
+ in both big and little endian mode and also for the POWER (RS/6000)
+ chip. */
+
+static int print_insn_powerpc (bfd_vma, struct disassemble_info *, int, int);
+
+/* Determine which set of machines to disassemble for. PPC403/601 or
+ BookE. For convenience, also disassemble instructions supported
+ by the AltiVec vector unit. */
+
+static int
+powerpc_dialect (struct disassemble_info *info)
+{
+ int dialect = PPC_OPCODE_PPC;
+
+ if (BFD_DEFAULT_TARGET_SIZE == 64)
+ dialect |= PPC_OPCODE_64;
+
+ if (info->disassembler_options
+ && strstr (info->disassembler_options, "booke") != NULL)
+ dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_BOOKE64;
+ else if ((info->mach == bfd_mach_ppc_e500)
+ || (info->disassembler_options
+ && strstr (info->disassembler_options, "e500") != NULL))
+ dialect |= (PPC_OPCODE_BOOKE
+ | PPC_OPCODE_SPE | PPC_OPCODE_ISEL
+ | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK
+ | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK
+ | PPC_OPCODE_RFMCI);
+ else if (info->disassembler_options
+ && strstr (info->disassembler_options, "efs") != NULL)
+ dialect |= PPC_OPCODE_EFS;
+ else if (info->disassembler_options
+ && strstr (info->disassembler_options, "e300") != NULL)
+ dialect |= PPC_OPCODE_E300 | PPC_OPCODE_CLASSIC | PPC_OPCODE_COMMON;
+ else if (info->disassembler_options
+ && strstr (info->disassembler_options, "440") != NULL)
+ dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_32
+ | PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI;
+ else
+ dialect |= (PPC_OPCODE_403 | PPC_OPCODE_601 | PPC_OPCODE_CLASSIC
+ | PPC_OPCODE_COMMON | PPC_OPCODE_ALTIVEC);
+
+ if (info->disassembler_options
+ && strstr (info->disassembler_options, "power4") != NULL)
+ dialect |= PPC_OPCODE_POWER4;
+
+ if (info->disassembler_options
+ && strstr (info->disassembler_options, "power5") != NULL)
+ dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5;
+
+ if (info->disassembler_options
+ && strstr (info->disassembler_options, "cell") != NULL)
+ dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC;
+
+ if (info->disassembler_options
+ && strstr (info->disassembler_options, "power6") != NULL)
+ dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC;
+
+ if (info->disassembler_options
+ && strstr (info->disassembler_options, "any") != NULL)
+ dialect |= PPC_OPCODE_ANY;
+
+ if (info->disassembler_options)
+ {
+ if (strstr (info->disassembler_options, "32") != NULL)
+ dialect &= ~PPC_OPCODE_64;
+ else if (strstr (info->disassembler_options, "64") != NULL)
+ dialect |= PPC_OPCODE_64;
+ }
+
+ info->private_data = (char *) 0 + dialect;
+ return dialect;
+}
+
+/* Qemu default */
+int
+print_insn_ppc (bfd_vma memaddr, struct disassemble_info *info)
+{
+ int dialect = (char *) info->private_data - (char *) 0;
+ return print_insn_powerpc (memaddr, info, 1, dialect);
+}
+
+/* Print a big endian PowerPC instruction. */
+
+int
+print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info)
+{
+ int dialect = (char *) info->private_data - (char *) 0;
+ return print_insn_powerpc (memaddr, info, 1, dialect);
+}
+
+/* Print a little endian PowerPC instruction. */
+
+int
+print_insn_little_powerpc (bfd_vma memaddr, struct disassemble_info *info)
+{
+ int dialect = (char *) info->private_data - (char *) 0;
+ return print_insn_powerpc (memaddr, info, 0, dialect);
+}
+
+/* Print a POWER (RS/6000) instruction. */
+
+int
+print_insn_rs6000 (bfd_vma memaddr, struct disassemble_info *info)
+{
+ return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER);
+}
+
+/* Extract the operand value from the PowerPC or POWER instruction. */
+
+static long
+operand_value_powerpc (const struct powerpc_operand *operand,
+ unsigned long insn, int dialect)
+{
+ long value;
+ int invalid;
+ /* Extract the value from the instruction. */
+ if (operand->extract)
+ value = (*operand->extract) (insn, dialect, &invalid);
+ else
+ {
+ value = (insn >> operand->shift) & operand->bitm;
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ /* BITM is always some number of zeros followed by some
+ number of ones, followed by some numer of zeros. */
+ unsigned long top = operand->bitm;
+ /* top & -top gives the rightmost 1 bit, so this
+ fills in any trailing zeros. */
+ top |= (top & -top) - 1;
+ top &= ~(top >> 1);
+ value = (value ^ top) - top;
+ }
+ }
+
+ return value;
+}
+
+/* Determine whether the optional operand(s) should be printed. */
+
+static int
+skip_optional_operands (const unsigned char *opindex,
+ unsigned long insn, int dialect)
+{
+ const struct powerpc_operand *operand;
+
+ for (; *opindex != 0; opindex++)
+ {
+ operand = &powerpc_operands[*opindex];
+ if ((operand->flags & PPC_OPERAND_NEXT) != 0
+ || ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+ && operand_value_powerpc (operand, insn, dialect) != 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Print a PowerPC or POWER instruction. */
+
+static int
+print_insn_powerpc (bfd_vma memaddr,
+ struct disassemble_info *info,
+ int bigendian,
+ int dialect)
+{
+ bfd_byte buffer[4];
+ int status;
+ unsigned long insn;
+ const struct powerpc_opcode *opcode;
+ const struct powerpc_opcode *opcode_end;
+ unsigned long op;
+
+ if (dialect == 0)
+ dialect = powerpc_dialect (info);
+
+ status = (*info->read_memory_func) (memaddr, buffer, 4, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ if (bigendian)
+ insn = bfd_getb32 (buffer);
+ else
+ insn = bfd_getl32 (buffer);
+
+ /* Get the major opcode of the instruction. */
+ op = PPC_OP (insn);
+
+ /* Find the first match in the opcode table. We could speed this up
+ a bit by doing a binary search on the major opcode. */
+ opcode_end = powerpc_opcodes + powerpc_num_opcodes;
+ again:
+ for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
+ {
+ unsigned long table_op;
+ const unsigned char *opindex;
+ const struct powerpc_operand *operand;
+ int invalid;
+ int need_comma;
+ int need_paren;
+ int skip_optional;
+
+ table_op = PPC_OP (opcode->opcode);
+ if (op < table_op)
+ break;
+ if (op > table_op)
+ continue;
+
+ if ((insn & opcode->mask) != opcode->opcode
+ || (opcode->flags & dialect) == 0)
+ continue;
+
+ /* Make two passes over the operands. First see if any of them
+ have extraction functions, and, if they do, make sure the
+ instruction is valid. */
+ invalid = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ operand = powerpc_operands + *opindex;
+ if (operand->extract)
+ (*operand->extract) (insn, dialect, &invalid);
+ }
+ if (invalid)
+ continue;
+
+ /* The instruction is valid. */
+ if (opcode->operands[0] != 0)
+ (*info->fprintf_func) (info->stream, "%-7s ", opcode->name);
+ else
+ (*info->fprintf_func) (info->stream, "%s", opcode->name);
+
+ /* Now extract and print the operands. */
+ need_comma = 0;
+ need_paren = 0;
+ skip_optional = -1;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ long value;
+
+ operand = powerpc_operands + *opindex;
+
+ /* Operands that are marked FAKE are simply ignored. We
+ already made sure that the extract function considered
+ the instruction to be valid. */
+ if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+ continue;
+
+ /* If all of the optional operands have the value zero,
+ then don't print any of them. */
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
+ {
+ if (skip_optional < 0)
+ skip_optional = skip_optional_operands (opindex, insn,
+ dialect);
+ if (skip_optional)
+ continue;
+ }
+
+ value = operand_value_powerpc (operand, insn, dialect);
+
+ if (need_comma)
+ {
+ (*info->fprintf_func) (info->stream, ",");
+ need_comma = 0;
+ }
+
+ /* Print the operand as directed by the flags. */
+ if ((operand->flags & PPC_OPERAND_GPR) != 0
+ || ((operand->flags & PPC_OPERAND_GPR_0) != 0 && value != 0))
+ (*info->fprintf_func) (info->stream, "r%ld", value);
+ else if ((operand->flags & PPC_OPERAND_FPR) != 0)
+ (*info->fprintf_func) (info->stream, "f%ld", value);
+ else if ((operand->flags & PPC_OPERAND_VR) != 0)
+ (*info->fprintf_func) (info->stream, "v%ld", value);
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
+ (*info->print_address_func) (memaddr + value, info);
+ else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+ (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
+ else if ((operand->flags & PPC_OPERAND_CR) == 0
+ || (dialect & PPC_OPCODE_PPC) == 0)
+ (*info->fprintf_func) (info->stream, "%ld", value);
+ else
+ {
+ if (operand->bitm == 7)
+ (*info->fprintf_func) (info->stream, "cr%ld", value);
+ else
+ {
+ static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
+ int cr;
+ int cc;
+
+ cr = value >> 2;
+ if (cr != 0)
+ (*info->fprintf_func) (info->stream, "4*cr%d+", cr);
+ cc = value & 3;
+ (*info->fprintf_func) (info->stream, "%s", cbnames[cc]);
+ }
+ }
+
+ if (need_paren)
+ {
+ (*info->fprintf_func) (info->stream, ")");
+ need_paren = 0;
+ }
+
+ if ((operand->flags & PPC_OPERAND_PARENS) == 0)
+ need_comma = 1;
+ else
+ {
+ (*info->fprintf_func) (info->stream, "(");
+ need_paren = 1;
+ }
+ }
+
+ /* We have found and printed an instruction; return. */
+ return 4;
+ }
+
+ if ((dialect & PPC_OPCODE_ANY) != 0)
+ {
+ dialect = ~PPC_OPCODE_ANY;
+ goto again;
+ }
+
+ /* We could not find a match. */
+ (*info->fprintf_func) (info->stream, ".long 0x%lx", insn);
+
+ return 4;
+}
diff --git a/ppc.ld b/ppc.ld
new file mode 100644
index 0000000..5248ef1
--- /dev/null
+++ b/ppc.ld
@@ -0,0 +1,227 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
+OUTPUT_ARCH(powerpc:common)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data.rel.ro : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) }
+ .rela.data.rel.ro : { *(.rela.data.rel.ro* .rela.gnu.linkonce.d.rel.ro.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rela.got1 : { *(.rela.got1) }
+ .rela.got2 : { *(.rela.got2) }
+ .rel.sdata : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+ .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+ .rel.sbss : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+ .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+ .rel.sdata2 : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+ .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+ .rel.sbss2 : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+ .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.glink)
+ } =0x47ff041f
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x47ff041f
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 :
+ {
+ PROVIDE (_SDA2_BASE_ = 32768);
+ *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+ }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN (0x10000) - ((0x10000 - .) & (0x10000 - 1)); . = DATA_SEGMENT_ALIGN (0x10000, 0x1000);
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .preinit_array :
+ {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ }
+ .init_array :
+ {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+ .got1 : { *(.got1) }
+ .got2 : { *(.got2) }
+ .dynamic : { *(.dynamic) }
+ .got : SPECIAL { *(.got) }
+ . = DATA_SEGMENT_RELRO_END (0, .);
+ .plt : SPECIAL { *(.plt) }
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .got : SPECIAL { *(.got) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ PROVIDE (_SDA_BASE_ = 32768);
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .; PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .); PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ PROVIDE (__sbss_end = .); PROVIDE (___sbss_end = .);
+ }
+ .plt : SPECIAL { *(.plt) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections.
+ FIXME: Why do we need it? When there is no .bss section, we don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ . = ALIGN(32 / 8);
+ . = ALIGN(32 / 8);
+ _end = .; PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+ /DISCARD/ : { *(.fixup) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/ppc64.ld b/ppc64.ld
new file mode 100644
index 0000000..dea0dbd
--- /dev/null
+++ b/ppc64.ld
@@ -0,0 +1,220 @@
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf64-powerpc", "elf64-powerpc",
+ "elf64-powerpc")
+OUTPUT_ARCH(powerpc:common64)
+ENTRY(_start)
+/* __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ PROVIDE (__executable_start = 0x60000000); . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data.rel.ro*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+ *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+ *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+ *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.toc)
+ *(.rela.opd)
+ *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+ *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+ *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+ *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .rela.tocbss : { *(.rela.tocbss) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x60000000
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.sfpr .glink)
+ } =0x60000000
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x60000000
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table))
+*(.gcc_except_table.*) } /* Adjust the address for the data segment. We want to
+adjust up to + the same address within the page on the next page up. */
+ . = ALIGN (0x10000) - ((0x10000 - .) & (0x10000 - 1)); . = DATA_SEGMENT_ALIGN
+(0x10000, 0x1000); /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table))
+*(.gcc_except_table.*) } /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(64 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { KEEP (*(.preinit_array)) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { KEEP (*(.init_array)) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { KEEP (*(.fini_array)) }
+ PROVIDE (__fini_array_end = .);
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
+ .dynamic : { *(.dynamic) }
+ . = DATA_SEGMENT_RELRO_END (0, .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .toc1 ALIGN(8) : { *(.toc1) }
+ .opd ALIGN(8) : { KEEP (*(.opd)) }
+ .got ALIGN(8) : { *(.got .toc) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .tocbss ALIGN(8) : { *(.tocbss)}
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ PROVIDE (__sbss_end = .);
+ PROVIDE (___sbss_end = .);
+ }
+ .plt : { *(.plt) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(64 / 8);
+ }
+ . = ALIGN(64 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/qbool.c b/qbool.c
new file mode 100644
index 0000000..ad4873f
--- /dev/null
+++ b/qbool.c
@@ -0,0 +1,68 @@
+/*
+ * QBool Module
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qbool.h"
+#include "qobject.h"
+#include "qemu-common.h"
+
+static void qbool_destroy_obj(QObject *obj);
+
+static const QType qbool_type = {
+ .code = QTYPE_QBOOL,
+ .destroy = qbool_destroy_obj,
+};
+
+/**
+ * qbool_from_int(): Create a new QBool from an int
+ *
+ * Return strong reference.
+ */
+QBool *qbool_from_int(int value)
+{
+ QBool *qb;
+
+ qb = qemu_malloc(sizeof(*qb));
+ qb->value = value;
+ QOBJECT_INIT(qb, &qbool_type);
+
+ return qb;
+}
+
+/**
+ * qbool_get_int(): Get the stored int
+ */
+int qbool_get_int(const QBool *qb)
+{
+ return qb->value;
+}
+
+/**
+ * qobject_to_qbool(): Convert a QObject into a QBool
+ */
+QBool *qobject_to_qbool(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QBOOL)
+ return NULL;
+
+ return container_of(obj, QBool, base);
+}
+
+/**
+ * qbool_destroy_obj(): Free all memory allocated by a
+ * QBool object
+ */
+static void qbool_destroy_obj(QObject *obj)
+{
+ assert(obj != NULL);
+ qemu_free(qobject_to_qbool(obj));
+}
diff --git a/qbool.h b/qbool.h
new file mode 100644
index 0000000..fe66fcd
--- /dev/null
+++ b/qbool.h
@@ -0,0 +1,29 @@
+/*
+ * QBool Module
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QBOOL_H
+#define QBOOL_H
+
+#include <stdint.h>
+#include "qobject.h"
+
+typedef struct QBool {
+ QObject_HEAD;
+ int value;
+} QBool;
+
+QBool *qbool_from_int(int value);
+int qbool_get_int(const QBool *qb);
+QBool *qobject_to_qbool(const QObject *obj);
+
+#endif /* QBOOL_H */
diff --git a/qdict-test-data.txt b/qdict-test-data.txt
new file mode 100644
index 0000000..122fda4
--- /dev/null
+++ b/qdict-test-data.txt
@@ -0,0 +1,4999 @@
+00-INDEX: 333
+07: 4096
+1040.bin.ihex: 92633
+11d.c: 17874
+11d.h: 2386
+1200.bin.ihex: 14717
+12160.bin.ihex: 77829
+1232ea962bbaf0e909365f4964f6cceb2ba8ce: 298
+1280.bin.ihex: 88220
+15562512ca6cf14c1b8f08e09d5907118deaf0: 297
+17: 4096
+1d: 4096
+1.Intro: 14968
+21142.c: 8591
+21285.c: 11721
+2860_main_dev.c: 33854
+2860_rtmp_init.c: 26170
+2870_main_dev.c: 39352
+2870_rtmp_init.c: 51247
+2.Process: 22356
+300vtbl.h: 43771
+310vtbl.h: 59655
+3270.ChangeLog: 1889
+3270.txt: 10964
+3550.bin.ihex: 13916
+3780i.c: 21485
+3780i.h: 14180
+38C0800.bin.ihex: 14772
+38C1600.bin.ihex: 17504
+3C359.bin.ihex: 69044
+3c359.c: 60161
+3c359.h: 7264
+3c359.txt: 2463
+3c501.c: 23869
+3c501.h: 2623
+3c503.c: 22491
+3c503.h: 3880
+3c505.c: 48605
+3c505.h: 6535
+3c505.txt: 1831
+3c507.c: 28758
+3c509.c: 42879
+3c509.txt: 9006
+3c515.c: 49671
+3c523.c: 39251
+3c523.h: 11169
+3c527.c: 43065
+3c527.h: 1482
+3c574_cs.c: 35859
+3c589_cs.c: 30110
+3c59x.c: 103272
+3CCFEM556.cis.ihex: 469
+3com: 4096
+3CXEM556.cis.ihex: 463
+3.Early-stage: 9993
+3w-9xxx.c: 77318
+3w-9xxx.h: 26357
+3w-xxxx.c: 85227
+3w-xxxx.h: 16846
+40x: 4096
+40x_mmu.c: 4082
+42: 4096
+44x: 4096
+44x.c: 3615
+44x_emulate.c: 4512
+44x.h: 448
+44x_mmu.c: 2966
+44x_tlb.c: 13997
+44x_tlb.h: 2465
+4.Coding: 20212
+4level-fixup.h: 1028
+4xx: 4096
+4xx.c: 16297
+4xx.h: 1174
+512x: 4096
+5206: 4096
+5206e: 4096
+520x: 4096
+523x: 4096
+5249: 4096
+5272: 4096
+527x: 4096
+528x: 4096
+52xx: 4096
+5307: 4096
+532x: 4096
+53c700.c: 71188
+53c700_d.h_shipped: 28887
+53c700.h: 16652
+53c700.scr: 10894
+53c700.txt: 5042
+5407: 4096
+57xx_iscsi_constants.h: 7004
+57xx_iscsi_hsi.h: 36895
+5d: 4096
+5.Posting: 15211
+66b00c9dc3e1e071bde0ebfeadc40bbc1e8316: 298
+68328: 4096
+68328fb.c: 13570
+68328serial.c: 35841
+68328serial.h: 6237
+68360: 4096
+68360serial.c: 76262
+68EZ328: 4096
+68VZ328: 4096
+6c5e45fe4f1c83df9429b7c2668b41446baac2: 297
+6.Followthrough: 11745
+6pack.c: 24715
+6pack.txt: 7940
+6xx-suspend.S: 1086
+712_defconfig: 25531
+7206: 4096
+73: 4096
+7343: 4096
+770x: 4096
+7721: 4096
+7722: 4096
+7724: 4096
+7751: 4096
+7780: 4096
+7990.c: 22036
+7990.h: 10421
+7.AdvancedTopics: 9648
+7c: 4096
+7segled.c: 2698
+802: 4096
+80211core: 4096
+80211hdr.h: 12326
+80211mgr.c: 28179
+80211mgr.h: 23006
+8021q: 4096
+8139cp.c: 56138
+8139too.c: 71384
+8250_accent.c: 1071
+8250_acorn.c: 3194
+8250_boca.c: 1301
+8250.c: 83003
+8250_early.c: 6636
+8250_exar_st16c554.c: 1191
+8250_fourport.c: 1216
+8250_gsc.c: 3620
+8250.h: 2432
+8250_hp300.c: 7797
+8250_hub6.c: 1224
+8250_mca.c: 1375
+8250_pci.c: 93521
+8250_pci.h: 999
+8250-platform.c: 1091
+8250_pnp.c: 14783
+8253.h: 10999
+8253pit.h: 48
+8255.c: 10692
+8255.h: 1805
+82571.c: 48377
+82596.c: 41209
+82xx: 4096
+8390.c: 2094
+8390.h: 9629
+8390p.c: 2248
+83xx: 4096
+83xx-512x-pci.txt: 1323
+85xx: 4096
+86xx: 4096
+87: 4096
+8b: 4096
+8.Conclusion: 3137
+8xx: 4096
+8xx_immap.h: 14089
+8xxx_gpio.txt: 1343
+941a7798a5169ee0dd69a9e8d5c40ceb702023: 298
+9600.bin.ihex: 14715
+9p: 4096
+9p.h: 13443
+9p.txt: 5113
+a100u2w.c: 36919
+a100u2w.h: 16936
+a2065.c: 20730
+a2065.h: 5135
+a2091.c: 6445
+a2091.h: 1712
+a20.c: 3548
+a20r.c: 5261
+a3000.c: 6528
+a3000.h: 1807
+a3d.c: 11335
+a4000t.c: 3579
+a500_defconfig: 31440
+a800.c: 5950
+aachba.c: 84424
+aaci.c: 27297
+aaci.h: 7022
+aacraid: 4096
+aacraid.h: 53671
+aacraid.txt: 6849
+aaec2000.h: 8936
+aaed2000.c: 2298
+aaed2000.h: 1354
+aaed2000_kbd.c: 5059
+aarp.c: 25379
+ab3100-core.c: 22600
+ab3100.h: 3510
+abdac.c: 15469
+ABI: 4096
+abi.h: 782
+abituguru: 3731
+abituguru3: 2493
+abituguru3.c: 41974
+abituguru.c: 53440
+abituguru-datasheet: 12294
+abi.txt: 1413
+ABI.txt: 1413
+ablkcipher.c: 9484
+abort-ev4.S: 882
+abort-ev4t.S: 913
+abort-ev5tj.S: 987
+abort-ev5t.S: 912
+abort-ev6.S: 1320
+abort-ev7.S: 774
+abort-lv4t.S: 6546
+abort-macro.S: 1144
+abort-nommu.S: 462
+abs_addr.h: 1800
+abspath.c: 2808
+abs.S: 238
+abyss.c: 11197
+abyss.h: 1550
+ac3200.c: 11838
+ac97: 4096
+ac97_bus.c: 1681
+ac97.c: 4489
+ac97c.c: 26095
+ac97c.h: 2045
+ac97_codec.c: 36925
+ac97_codec.h: 15115
+ac97.h: 507
+ac97_id.h: 2422
+ac97_local.h: 1689
+ac97_patch.c: 128568
+ac97_patch.h: 4386
+ac97_pcm.c: 21244
+ac97_proc.c: 18243
+acadia_defconfig: 23272
+acadia.dts: 5188
+ac.c: 9202
+accel.c: 8625
+accel.h: 5627
+access.c: 10521
+accessibility: 4096
+access_ok.h: 1305
+accommon.h: 2825
+acconfig.h: 7688
+accounting: 4096
+acct.c: 17700
+acct.h: 5949
+acdebug.h: 6836
+acdispat.h: 10988
+acecad.c: 7982
+acenic: 4096
+acenic.c: 88166
+acenic.h: 16029
+acenv.h: 11821
+acerhdf.c: 14909
+acer-wmi.c: 30530
+acer-wmi.txt: 6480
+acevents.h: 6692
+acexcep.h: 12764
+acgcc.h: 2843
+acglobal.h: 14485
+achware.h: 4355
+acinterp.h: 16329
+ackvec.c: 12985
+ackvec.h: 3426
+acl7225b.c: 3794
+acl.c: 5321
+acl.h: 1548
+aclinux.h: 5049
+aclocal.h: 34614
+acmacros.h: 22966
+acm.txt: 4974
+acnames.h: 3482
+acnamesp.h: 10211
+acobject.h: 16573
+acopcode.h: 22365
+acorn.c: 12206
+acornfb.c: 35783
+acornfb.h: 4809
+acorn.h: 623
+acornscsi.c: 87770
+acornscsi.h: 9694
+acornscsi-io.S: 3320
+acoutput.h: 10925
+acparser.h: 7436
+acpi: 4096
+acpi_bus.h: 11195
+acpi.c: 6529
+acpica: 12288
+acpi-cpufreq.c: 21588
+acpi_drivers.h: 5322
+acpi-ext.c: 2774
+acpi-ext.h: 590
+acpi.h: 4768
+acpi_memhotplug.c: 14832
+acpi_numa.h: 471
+acpiosxf.h: 7880
+acpi_pcihp.c: 14898
+acpiphp_core.c: 10949
+acpiphp_glue.c: 44065
+acpiphp.h: 6325
+acpiphp_ibm.c: 14740
+acpi_pm.c: 6723
+acpi_pmtmr.h: 672
+acpi-processor.c: 1893
+acpixf.h: 12045
+acpredef.h: 18909
+acquirewdt.c: 8920
+acresrc.h: 11064
+acrestyp.h: 11267
+acs5k_defconfig: 28786
+acs5k_tiny_defconfig: 21413
+acstruct.h: 7738
+act2000: 4096
+act2000.h: 6271
+act2000_isa.c: 11978
+act2000_isa.h: 7615
+act200l-sir.c: 7142
+actables.h: 3945
+act_api.c: 23712
+act_api.h: 4195
+actbl1.h: 35761
+actbl.h: 15492
+act_gact.c: 5493
+action.c: 40
+action.h: 40
+act_ipt.c: 7564
+actisys-sir.c: 7692
+active_mm.txt: 3805
+act_mirred.c: 6285
+act_nat.c: 7265
+act_pedit.c: 6148
+act_police.c: 9833
+act_simple.c: 5238
+act_skbedit.c: 5510
+actypes.h: 34263
+acutils.h: 16898
+acx.c: 16590
+acx.h: 33552
+ad1816a: 4096
+ad1816a.c: 8825
+ad1816a.h: 5363
+ad1816a_lib.c: 30656
+ad1843.c: 16402
+ad1843.h: 1516
+ad1848: 4096
+ad1848.c: 75959
+ad1848.h: 982
+ad1848_mixer.h: 10957
+ad1889.c: 27048
+ad1889.h: 8073
+ad1980.c: 8427
+ad1980.h: 468
+ad73311.c: 2765
+ad73311.h: 2404
+ad7414.c: 7164
+ad7418.c: 8304
+ad7877.c: 20856
+ad7877.h: 796
+ad7879.c: 19019
+ad7879.h: 1004
+adaptec: 4096
+adapter.h: 400
+adb.c: 19706
+adb.h: 2770
+adbhid.c: 35627
+adb-iop.c: 6325
+adb_iop.h: 1081
+adc.c: 8573
+adc.h: 886
+ADC-LH7-Touchscreen: 2187
+adcxx.c: 8256
+adder875.c: 3142
+adder875_defconfig: 21956
+adder875-redboot.dts: 4102
+adder875-uboot.dts: 4072
+addi_amcc_S5920.c: 7563
+addi_amcc_S5920.h: 1016
+addi_amcc_s5933.h: 13296
+addi_apci_035.c: 128
+addi_apci_1032.c: 63
+addi_apci_1500.c: 63
+addi_apci_1516.c: 63
+addi_apci_1564.c: 63
+addi_apci_16xx.c: 63
+addi_apci_1710.c: 63
+addi_apci_2016.c: 63
+addi_apci_2032.c: 63
+addi_apci_2200.c: 63
+addi_apci_3001.c: 63
+addi_apci_3120.c: 63
+addi_apci_3200.c: 63
+addi_apci_3300.c: 63
+addi_apci_3501.c: 63
+addi_apci_3xxx.c: 63
+addi_apci_all.c: 468
+addi_common.c: 57084
+addi_common.h: 15536
+addi-data: 4096
+addi_eeprom.c: 35657
+addinitrd.c: 3837
+addnote.c: 5117
+addon_cpuid_features.c: 3269
+addRamDisk.c: 9120
+addr.c: 13176
+addrconf.c: 112491
+addrconf_core.c: 2484
+addrconf.h: 7261
+address.c: 10451
+addr.h: 615
+addrlabel.c: 14012
+addr-map.c: 3725
+addr-map.h: 1054
+addrs.h: 14810
+addrspace.h: 4233
+adfs: 4096
+adfs_fs.h: 1337
+adfs.h: 5064
+adfs.txt: 1816
+adi.c: 14310
+adlib.c: 3006
+adl_pci6208.c: 11803
+adl_pci7296.c: 4671
+adl_pci7432.c: 5617
+adl_pci8164.c: 9991
+adl_pci9111.c: 38266
+adl_pci9118.c: 69079
+adm1021: 4184
+adm1021.c: 14057
+adm1025: 2364
+adm1025.c: 19865
+adm1026: 4578
+adm1026.c: 60096
+adm1029.c: 13774
+adm1031: 1193
+adm1031.c: 31209
+adm8211.c: 56115
+adm8211.h: 18093
+adm9240: 6804
+adm9240.c: 24112
+adma.c: 5497
+adma.h: 13803
+adq12b.c: 12147
+ads7828: 1146
+ads7828.c: 8084
+ads7846.c: 31008
+ads7846.h: 1844
+ADSBitsy: 1396
+adssphere.c: 1810
+adt7462: 2515
+adt7462.c: 60645
+adt7470: 2759
+adt7470.c: 42397
+adt7473: 2757
+adt7473.c: 35779
+adt7475: 2578
+adt7475.c: 35318
+adummy.c: 3035
+adutux.c: 25427
+adv7170.c: 9645
+adv7175.c: 10457
+adv7343.c: 12608
+adv7343.h: 730
+adv7343_regs.h: 5764
+advansys: 4096
+advansys.c: 382572
+advansys.txt: 9492
+advantechwdt.c: 8222
+adv_pci1710.c: 46220
+adv_pci1723.c: 12975
+adv_pci_dio.c: 34130
+ae7400074d449189d41fceb6d6f871490d7842: 298
+aead.c: 12873
+aead.h: 2027
+aec62xx.c: 9392
+aedsp16.c: 36369
+ael1002.c: 41054
+aer: 4096
+aerdrv_acpi.c: 1347
+aerdrv.c: 8935
+aerdrv_core.c: 22958
+aerdrv_errprint.c: 5915
+aerdrv.h: 3468
+aer.h: 773
+aer_inject.c: 10715
+aes.c: 11973
+aes_ccm.c: 3589
+aes_ccm.h: 810
+aes_ccmp.c: 14024
+aes_ccmp.h: 1469
+aes_cmac.c: 2737
+aes_cmac.h: 587
+aes_generic.c: 63316
+aes_glue.c: 1691
+aes.h: 279
+aes-i586-asm_32.S: 10637
+aesni-intel_asm.S: 21991
+aesni-intel_glue.c: 19525
+aes_s390.c: 13814
+aes-x86_64-asm_64.S: 4820
+af802154.h: 1271
+af9005.c: 28215
+af9005-fe.c: 36514
+af9005.h: 120579
+af9005-remote.c: 4403
+af9005-script.h: 5193
+af9013.c: 38083
+af9013.h: 3095
+af9013_priv.h: 21508
+af9015.c: 43153
+af9015.h: 28439
+af_ax25.c: 45160
+af_bluetooth.c: 10089
+af_can.c: 23294
+af_can.h: 4053
+af_decnet.c: 54945
+afeb9260_defconfig: 29783
+af_econet.c: 25853
+affs: 4096
+affs.h: 10590
+affs_hardblocks.h: 1481
+affs.txt: 8188
+af_ieee802154.c: 8683
+af_ieee802154.h: 1620
+af_inet6.c: 29847
+af_inet.c: 40041
+af_ipx.c: 50967
+af_irda.c: 67827
+af_irda.h: 2964
+af_iucv.c: 41285
+af_iucv.h: 2495
+af_key.c: 102229
+af_llc.c: 29863
+af_netlink.c: 46590
+af_netrom.c: 33634
+af_packet.c: 55964
+af_phonet.c: 10845
+af_rds.c: 14309
+af_rose.c: 39337
+af_rxrpc.c: 20908
+af_rxrpc.h: 2017
+afs: 4096
+afs.c: 6434
+afs_cm.h: 1213
+afs_fs.h: 2342
+afs.h: 6110
+afs.txt: 7976
+afs_vl.h: 3680
+af_unix.c: 53673
+af_unix.h: 1891
+af_x25.c: 38538
+agent.c: 6214
+agent.h: 2106
+agg-rx.c: 10061
+agg-tx.c: 19983
+agnx: 4096
+agnx.h: 4414
+agp: 4096
+agp_backend.h: 909
+agpgart.h: 6567
+agp.h: 11594
+ah4.c: 7802
+ah6.c: 13438
+aha152x.c: 100462
+aha152x_core.c: 61
+aha152x.h: 10175
+aha152x_stub.c: 7843
+aha152x.txt: 6540
+aha1542.c: 50376
+aha1542.h: 4776
+aha1740.c: 19608
+aha1740.h: 4954
+ahash.c: 5607
+ahb.c: 4740
+ah.c: 3493
+ahci.c: 82058
+ah.h: 894
+aic7770.c: 9851
+aic7770_osm.c: 4437
+aic79xx_core.c: 299904
+aic79xx.h: 46803
+aic79xx_inline.h: 5997
+aic79xx_osm.c: 79947
+aic79xx_osm.h: 21300
+aic79xx_osm_pci.c: 10626
+aic79xx_pci.c: 27694
+aic79xx_pci.h: 3106
+aic79xx_proc.c: 10559
+aic79xx.reg: 73912
+aic79xx_reg.h_shipped: 71718
+aic79xx_reg_print.c_shipped: 19691
+aic79xx.seq: 72735
+aic79xx_seq.h_shipped: 29327
+aic79xx.txt: 24091
+aic7xxx: 4096
+aic7xxx_93cx6.c: 9761
+aic7xxx_93cx6.h: 3670
+aic7xxx_core.c: 216583
+aic7xxx.h: 42160
+aic7xxx_inline.h: 3774
+aic7xxx_old: 4096
+aic7xxx_old.c: 366880
+aic7xxx_old.txt: 24500
+aic7xxx_osm.c: 73118
+aic7xxx_osm.h: 21353
+aic7xxx_osm_pci.c: 12355
+aic7xxx_pci.c: 61842
+aic7xxx_pci.h: 5296
+aic7xxx_proc.c: 10950
+aic7xxx.reg: 38137
+aic7xxx_reg.h: 15305
+aic7xxx_reg.h_shipped: 23099
+aic7xxx_reg_print.c_shipped: 10818
+aic7xxx.seq: 70499
+aic7xxx_seq.c: 20747
+aic7xxx_seq.h_shipped: 32908
+aic7xxx.txt: 19701
+aic94xx: 4096
+aic94xx_dev.c: 11236
+aic94xx_dump.c: 36929
+aic94xx_dump.h: 1451
+aic94xx.h: 3007
+aic94xx_hwi.c: 38875
+aic94xx_hwi.h: 10646
+aic94xx_init.c: 28607
+aic94xx_reg.c: 10895
+aic94xx_reg_def.h: 74147
+aic94xx_reg.h: 10470
+aic94xx_sas.h: 22044
+aic94xx_scb.c: 27220
+aic94xx_sds.c: 37304
+aic94xx_sds.h: 4644
+aic94xx_seq.c: 47415
+aic94xx_seq.h: 2011
+aic94xx_task.c: 17414
+aic94xx_tmf.c: 20129
+aica.c: 18912
+aica.h: 2322
+aicasm: 4096
+aicasm.c: 20235
+aicasm_gram.y: 41937
+aicasm.h: 3213
+aicasm_insformat.h: 5394
+aicasm_macro_gram.y: 4072
+aicasm_macro_scan.l: 4166
+aicasm_scan.l: 15538
+aicasm_symbol.c: 16089
+aicasm_symbol.h: 4889
+aiclib.c: 1599
+aiclib.h: 6327
+aio_abi.h: 3060
+aio_aio12_8.c: 5597
+aio.c: 9345
+aio.h: 530
+aio_iiro_16.c: 4521
+aiptek.c: 63719
+aircable.c: 16905
+airo.c: 224549
+airo_cs.c: 16214
+airo.h: 272
+aironet.c: 38
+aironet.h: 31
+airport.c: 6741
+airq.c: 3567
+airq.h: 530
+ak4104.c: 8783
+ak4104.h: 143
+ak4114.c: 18943
+ak4114.h: 10191
+ak4117.c: 16871
+ak4117.h: 9193
+ak4396.h: 1065
+ak4531_codec.c: 17543
+ak4531_codec.h: 3161
+ak4535.c: 18361
+ak4535.h: 1042
+ak4xxx-adda.c: 25308
+ak4xxx-adda.h: 3333
+ak4xxx.c: 5039
+aki3068net: 4096
+alauda.c: 34485
+alaw_main.csp.ihex: 3714
+alchemy: 4096
+alchemy-flash.c: 4191
+algapi.c: 16668
+algapi.h: 8721
+algboss.c: 6296
+algos: 4096
+ali14xx.c: 6564
+ali5451: 4096
+ali5451.c: 59400
+ali-agp.c: 10369
+alias.c: 1447
+aliasing-test.c: 6103
+aliasing.txt: 8753
+alias.txt: 1181
+align.c: 24429
+alignment.c: 23757
+align.S: 11650
+ali-ircc.c: 57570
+ali-ircc.h: 7748
+alim1535_wdt.c: 10224
+alim15x3.c: 14978
+alim7101_wdt.c: 11048
+allocator.c: 10151
+alloc.c: 10261
+alloc.h: 9219
+allocpercpu.c: 4150
+alpaca.h: 1209
+alpha: 4096
+alpha-agp.c: 5476
+alpha_ksyms.c: 2696
+alphatrack.c: 23326
+alphatrack.h: 2005
+alps.c: 15813
+alps.h: 1047
+ALS: 3770
+als100.c: 9384
+als300.c: 23789
+als4000.c: 32136
+alsa: 4096
+alsa.c: 2430
+ALSA-Configuration.txt: 75006
+alsa-driver-api.tmpl: 3216
+alsa.h: 370
+alternative-asm.h: 289
+alternative.c: 14237
+alternative.h: 5895
+altpciechdma: 4096
+altpciechdma.c: 37598
+am200epd.c: 9652
+am200epdkit_defconfig: 26949
+am300epd.c: 6564
+am79c961a.c: 18502
+am79c961a.h: 3109
+am9513.h: 1940
+amba: 4096
+amba-clcd.c: 12494
+ambakmi.c: 4554
+amba-pl010.c: 19490
+amba-pl011.c: 21509
+amba-pl022.c: 53346
+ambassador.c: 68387
+ambassador.h: 16192
+amcc: 4096
+amcc_s5933_58.h: 14263
+amcc_s5933.h: 6603
+amd5536udc.c: 86854
+amd5536udc.h: 17276
+amd64-agp.c: 20128
+amd64_edac.c: 98492
+amd64_edac_dbg.c: 5401
+amd64_edac_err_types.c: 3628
+amd64_edac.h: 19221
+amd64_edac_inj.c: 4304
+amd74xx.c: 10198
+amd76x_edac.c: 9371
+amd76xrom.c: 9404
+amd7930.c: 31102
+amd7930_fn.c: 23626
+amd7930_fn.h: 1162
+amd8111e.c: 53690
+amd8111_edac.c: 16756
+amd8111_edac.h: 4244
+amd8111e.h: 21006
+amd8131_edac.c: 10985
+amd8131_edac.h: 3772
+amd_bus.c: 13843
+amd.c: 3180
+amd_iommu.c: 52618
+amd_iommu.h: 1421
+amd_iommu_init.c: 34814
+amd_iommu_types.h: 13377
+amd-k7-agp.c: 15673
+amd-powernow.txt: 1673
+amd-rng.c: 3458
+amifb.c: 103212
+amifd.h: 1995
+amifdreg.h: 2674
+amiflop.c: 46962
+amiga: 4096
+amiga.c: 3445
+amiga_defconfig: 28613
+amigaffs.c: 11549
+amigaffs.h: 2927
+amiga.h: 116
+amigahw.h: 11232
+amigaints.h: 3583
+amigaone: 4096
+amigaone_defconfig: 40433
+amigaone.dts: 4189
+amigayle.h: 3135
+amiints.c: 6489
+amijoy.c: 4591
+amijoy.txt: 7701
+amikbd.c: 6468
+amimouse.c: 3296
+amipcmcia.h: 2568
+amiserial.c: 54254
+amisound.c: 2828
+amix86.c: 23331
+amlcode.h: 18976
+aml_nfw.c: 5630
+amlresrc.h: 9975
+amon.h: 141
+amp.c: 2736
+amp.h: 1594
+amplc_dio200.c: 38731
+amplc_pc236.c: 16962
+amplc_pc263.c: 11680
+amplc_pci224.c: 44174
+amplc_pci230.c: 92910
+ams: 4096
+ams-core.c: 6046
+ams-delta.c: 5974
+ams_delta_defconfig: 28481
+amsdu.c: 5428
+ams.h: 1354
+ams-i2c.c: 6365
+ams-input.c: 3542
+amso1100: 4096
+ams-pmu.c: 4469
+analog.c: 20366
+anchor.h: 3882
+anchors.txt: 2625
+android: 4096
+ani.c: 23441
+ani.h: 4062
+anode.c: 14491
+anomaly.h: 11228
+anon_inodes.c: 5477
+anon_inodes.h: 312
+ansi_cprng.c: 10011
+ansidecl.h: 4393
+ans-lcd.c: 3847
+ans-lcd.h: 206
+anubis.c: 28484
+anubis-cpld.h: 652
+anubis-irq.h: 596
+anubis-map.h: 1219
+anycast.c: 11884
+anysee.c: 15434
+anysee.h: 14174
+aoa: 4096
+aoa-gpio.h: 2434
+aoa.h: 3816
+aoe: 4096
+aoeblk.c: 7321
+aoechr.c: 6092
+aoecmd.c: 23023
+aoedev.c: 5392
+aoe.h: 4748
+aoemain.c: 2032
+aoenet.c: 3237
+aoe.txt: 4600
+aops.c: 49318
+aops.h: 4017
+a.out-core.h: 2444
+a.out.h: 2438
+ap325rxa_defconfig: 31462
+apanel.c: 8043
+apb.h: 1043
+ap_bus.c: 45317
+ap_bus.h: 6091
+apc.c: 4412
+apc.h: 1682
+APCI1710_82x54.c: 55614
+APCI1710_82x54.h: 2408
+APCI1710_Chrono.c: 75530
+APCI1710_Chrono.h: 2397
+APCI1710_Dig_io.c: 35243
+APCI1710_Dig_io.h: 1437
+APCI1710_INCCPT.c: 206966
+APCI1710_INCCPT.h: 10615
+APCI1710_Inp_cpt.c: 32511
+APCI1710_Inp_cpt.h: 1414
+APCI1710_Pwm.c: 110859
+APCI1710_Pwm.h: 2722
+APCI1710_Ssi.c: 30348
+APCI1710_Ssi.h: 1343
+APCI1710_Tor.c: 70692
+APCI1710_Tor.h: 1684
+APCI1710_Ttl.c: 35072
+APCI1710_Ttl.h: 1338
+apdbg.c: 12729
+aperture_64.c: 13850
+ap.h: 26
+apic: 4096
+api.c: 4766
+apic.c: 56323
+apicdef.h: 10790
+apic_flat_64.c: 9680
+apic.h: 48
+apicnum.h: 229
+API.html: 738
+api-intro.txt: 6569
+apm_32.c: 71310
+apm-acpi.txt: 1541
+apm_bios.h: 5647
+apm.c: 1961
+apm_emu.c: 3225
+apm-emulation.c: 17959
+apm-emulation.h: 1575
+apm.h: 1722
+apm_power.c: 10334
+apm-power.c: 2659
+apne.c: 17115
+apollo: 4096
+apollo_defconfig: 25574
+apollodma.h: 9409
+apollohw.h: 2876
+appldata: 4096
+appldata_base.c: 16414
+appldata.h: 2269
+appldata_mem.c: 4196
+appldata_net_sum.c: 4240
+appldata_os.c: 6134
+appledisplay.c: 9793
+applesmc.c: 46139
+appletalk: 4096
+appletouch.c: 25051
+appletouch.txt: 3199
+applicom.c: 24465
+applicom.h: 2558
+applying-patches.txt: 19961
+applypatch-msg.sample: 452
+aq100x.c: 8974
+ar7: 4096
+ar7_defconfig: 29419
+ar7.h: 4432
+ar7part.c: 4186
+ar7_wdt.c: 8576
+ar9170: 4096
+ar9170.h: 7335
+ar-accept.c: 12406
+ar-ack.c: 33274
+arbiter.c: 11549
+arbiter.h: 607
+arc: 4096
+arc4.c: 2066
+ar-call.c: 21724
+arc_con.c: 973
+arcdevice.h: 12708
+arcfb.c: 16908
+arcfb.h: 150
+arch: 4096
+arch_checks.c: 3045
+arches_defconfig: 19397
+arches.dts: 7694
+arch.h: 1650
+arch-kev7a400.c: 2933
+arch-lpd7a40x.c: 10208
+archparam.h: 246
+archsetjmp.h: 363
+arch-v10: 4096
+arch-v32: 4096
+arcmsr: 4096
+arcmsr_attr.c: 12953
+arcmsr.h: 22540
+arcmsr_hba.c: 68471
+arcmsr_spec.txt: 22891
+arcnet: 4096
+arcnet.c: 30476
+arcnet-hardware.txt: 112635
+arcnet.txt: 23983
+arcofi.c: 3664
+arcofi.h: 721
+ar-connection.c: 23383
+ar-connevent.c: 9295
+arc-rawmode.c: 5274
+arc-rimi.c: 10949
+ar-error.c: 5949
+argv_split.c: 1893
+ariadne.c: 24224
+ariadne.h: 15566
+ar-input.c: 20380
+ar-internal.h: 28881
+ark3116.c: 12365
+ar-key.c: 8236
+arkfb.c: 31427
+arkfb.txt: 2024
+arlan.h: 16773
+arlan-main.c: 51969
+arlan-proc.c: 30009
+ar-local.c: 7670
+arm: 4096
+armadillo5x0.c: 7149
+ARM-gcc.h: 4503
+armksyms.c: 4758
+arm_timer.h: 576
+ar-output.c: 18343
+arp.c: 35296
+ar-peer.c: 7599
+arp.h: 1028
+ar-proc.c: 5159
+arptable_filter.c: 3496
+arp_tables.c: 47085
+arp_tables.h: 8551
+arpt_mangle.c: 2391
+arpt_mangle.h: 547
+array.c: 13244
+arrayRCU.txt: 4475
+ar-recvmsg.c: 11003
+ar-security.c: 5588
+ar-skbuff.c: 3506
+arthur.c: 2275
+artpec_3_defconfig: 13781
+ar-transport.c: 7196
+arv.c: 22235
+arxescsi.c: 9967
+asb100: 2093
+asb100.c: 29220
+asb2303_defconfig: 13559
+asciidoc.conf: 2238
+asequencer.h: 24733
+ashiftrt.S: 3468
+ashldi3.c: 1516
+__ashldi3.S: 1340
+ashldi3.S: 1594
+ashlsi3.S: 3814
+ashrdi3.c: 1578
+__ashrdi3.S: 1359
+ashrdi3.S: 1594
+ashrsi3.S: 3765
+ashxdi3.S: 5014
+asic3.c: 23429
+asic3.h: 11966
+asids-debugfs.c: 1875
+asi.h: 13855
+asiliantfb.c: 17057
+as-iosched.c: 39676
+as-iosched.txt: 8697
+asix.c: 39119
+as-layout.h: 1666
+asl.c: 9101
+asm: 4096
+asm-compat.h: 1951
+asm-generic: 4096
+asm.h: 9481
+asmmacro-32.h: 4815
+asmmacro-64.h: 3961
+asmmacro.h: 2767
+asm-offsets_32.c: 5194
+asm-offsets_64.c: 3637
+asm-offsets.c: 4611
+asmregs.h: 3115
+asn1.c: 15700
+asoundef.h: 14263
+asound_fm.h: 4313
+asound.h: 39901
+asp8347_defconfig: 35011
+asp834x.c: 2108
+asp834x-redboot.dts: 6941
+asp.c: 3631
+aspenite.c: 2671
+asp.h: 608
+aspm.c: 25790
+Assabet: 9201
+assabet.c: 11283
+assabet_defconfig: 17595
+assabet.h: 4477
+assembler.h: 2834
+assembly.h: 12972
+assoc.c: 36
+assoc.h: 408
+association.h: 4661
+associola.c: 43957
+ast.c: 3630
+ast.h: 878
+asus_acpi.c: 39676
+asus_atk0110.c: 24097
+asuscom.c: 11573
+asus-laptop.c: 36214
+asus_oled: 4096
+asus_oled.c: 18945
+async.c: 11128
+asyncdata.c: 15045
+async.h: 973
+async_memcpy.c: 3227
+async_memset.c: 2994
+async-thread.c: 13350
+async-thread.h: 3400
+async_tx: 4096
+async-tx-api.txt: 8950
+async_tx.c: 8062
+async_tx.h: 5062
+async_xor.c: 9790
+at1700.c: 25461
+at24.c: 17172
+at24.h: 1084
+at25.c: 10286
+at32ap700x.c: 54284
+at32ap700x.h: 9017
+at32ap700x_wdt.c: 10216
+at32psif.c: 8545
+at73c213.c: 28442
+at73c213.h: 3187
+at76c50x-usb.c: 69577
+at76c50x-usb.h: 11556
+at76_usb: 4096
+at76_usb.c: 159931
+at76_usb.h: 18785
+at91_adc.h: 2447
+at91_aic.h: 2578
+at91cap9adk_defconfig: 28118
+at91cap9.c: 9260
+at91cap9_ddrsdr.h: 4803
+at91cap9_devices.c: 32852
+at91cap9.h: 5120
+at91cap9_matrix.h: 8438
+at91_cf.c: 10319
+at91_dbgu.h: 2878
+at91_ether.c: 37656
+at91_ether.h: 3116
+at91_ide.c: 10538
+at91_mci.c: 31894
+at91_mci.h: 5220
+at91_pio.h: 2085
+at91_pit.h: 1189
+at91_pmc.h: 6822
+at91rm9200.c: 8544
+at91rm9200_devices.c: 32118
+at91rm9200dk_defconfig: 19334
+at91rm9200ek_defconfig: 19319
+at91rm9200_emac.h: 6334
+at91rm9200.h: 4634
+at91rm9200_mc.h: 7552
+at91rm9200_time.c: 6137
+at91rm9200_wdt.c: 6655
+at91_rstc.h: 1684
+at91_rtc.h: 3381
+at91_rtt.h: 1335
+at91sam9260.c: 9719
+at91sam9260_devices.c: 28830
+at91sam9260ek_defconfig: 23675
+at91sam9260.h: 5601
+at91sam9260_matrix.h: 4360
+at91sam9261.c: 7811
+at91sam9261_devices.c: 27547
+at91sam9261ek_defconfig: 27131
+at91sam9261.h: 3985
+at91sam9261_matrix.h: 3171
+at91sam9263.c: 8693
+at91sam9263_devices.c: 36564
+at91sam9263ek_defconfig: 27888
+at91sam9263.h: 5088
+at91sam9263_matrix.h: 7531
+at91sam926x_time.c: 4812
+at91sam9g20ek_defconfig: 26082
+at91sam9rl.c: 8144
+at91sam9rl_devices.c: 28643
+at91sam9rlek_defconfig: 21184
+at91sam9rl.h: 4332
+at91sam9rl_matrix.h: 5086
+at91sam9_sdramc.h: 3914
+at91sam9_smc.h: 3570
+at91sam9_wdt.c: 7764
+at91_shdwc.h: 1497
+at91_spi.h: 3738
+at91_ssc.h: 4846
+at91_st.h: 1983
+at91_tc.h: 6897
+at91_twi.h: 3075
+at91_udc.c: 48229
+at91_udc.h: 6283
+at91_wdt.h: 1464
+at91x40.c: 1914
+at91x40.h: 2025
+at91x40_time.c: 2547
+at93c.c: 2613
+at93c.h: 292
+ata: 12288
+ata.c: 4208
+ata_defs_asm.h: 8736
+ata_defs.h: 6434
+atafb.c: 91174
+atafb.h: 1708
+atafb_iplan2p2.c: 6818
+atafb_iplan2p4.c: 7301
+atafb_iplan2p8.c: 8377
+atafb_mfb.c: 2554
+atafb_utils.h: 11235
+atafd.h: 261
+atafdreg.h: 2704
+ataflop.c: 52286
+ata_generic.c: 6543
+atags.c: 1588
+atags.h: 132
+ata.h: 787
+ataints.c: 14727
+atakbd.c: 6402
+atakeyb.c: 15982
+atalk.h: 5240
+atalk_proc.c: 7290
+ata_piix.c: 45848
+ata_platform.h: 910
+atari: 4096
+atari.c: 3911
+atari_defconfig: 26788
+atari.h: 1077
+atarihw.h: 20620
+atariints.h: 5520
+atari_joystick.h: 418
+atarikbd.txt: 26109
+atarikb.h: 1514
+atarilance.c: 34168
+atarimouse.c: 3865
+atari_NCR5380.c: 92608
+atari_scsi.c: 35928
+atari_scsi.h: 5820
+atari_stdma.h: 458
+atari_stram.h: 429
+atasound.c: 2705
+ateb9200_defconfig: 26557
+aten2011.c: 65277
+aten.c: 3343
+ath: 4096
+ath5k: 4096
+ath5k.h: 45994
+ath9k: 4096
+ath9k.h: 20541
+ath9k_platform.h: 1116
+athr_common.h: 4056
+ati-agp.c: 14653
+ati_ids.h: 8172
+atiixp.c: 5592
+atiixp_modem.c: 36739
+ati_pcigart.c: 5516
+ati_remote2.c: 23391
+ati_remote.c: 27913
+atkbd.c: 40410
+atl1c: 4096
+atl1.c: 101403
+atl1c_ethtool.c: 9188
+atl1c.h: 20395
+atl1c_hw.c: 13975
+atl1c_hw.h: 31069
+atl1c_main.c: 77505
+atl1e: 4096
+atl1e_ethtool.c: 11679
+atl1e.h: 17562
+atl1e_hw.c: 16680
+atl1e_hw.h: 38581
+atl1e_main.c: 71172
+atl1e_param.c: 7245
+atl1.h: 24023
+atl2.c: 82456
+atl2.h: 16349
+atlas_btns.c: 4752
+atlx: 4096
+atlx.c: 7198
+atlx.h: 18176
+atm: 4096
+atmapi.h: 889
+atmarp.h: 1233
+atmbr2684.h: 3208
+atmclip.h: 513
+atmdev.h: 16681
+atmel: 4096
+atmel-abdac.h: 626
+atmel-ac97c.h: 1409
+atmel.c: 132756
+atmel_cs.c: 17914
+atmel.h: 1533
+atmel_lcdc.h: 7265
+atmel_lcdfb.c: 31305
+atmel-mci.c: 43139
+atmel-mci.h: 1269
+atmel-mci-regs.h: 6476
+atmel_nand.c: 15137
+atmel_nand_ecc.h: 1412
+atmel_pci.c: 2533
+atmel-pcm.c: 14083
+atmel-pcm.h: 2987
+atmel_pdc.h: 1410
+atmel-pwm-bl.c: 6152
+atmel-pwm-bl.h: 1550
+atmel_pwm.c: 9359
+atmel_pwm.h: 2724
+atmel_read_eeprom.c: 3671
+atmel_read_eeprom.h: 2413
+atmel_serial.c: 40894
+atmel_serial.h: 6087
+atmel_spi.c: 23923
+atmel_spi.h: 4446
+atmel-ssc.c: 3605
+atmel_ssc_dai.c: 20632
+atmel_ssc_dai.h: 3265
+atmel-ssc.h: 9369
+atmel_tc.h: 11075
+atmel_tclib.c: 3626
+atmel_tsadcc.c: 10816
+atmel_usba_udc.c: 50940
+atmel_usba_udc.h: 10031
+atmel-wm97xx.c: 11848
+atm_eni.h: 585
+atm.h: 7995
+atm_he.h: 343
+atm_idt77105.h: 892
+atmioc.h: 1583
+atmlec.h: 2561
+atm_misc.c: 2638
+atmmpc.h: 4163
+atm_nicstar.h: 1215
+atmppp.h: 576
+atmsap.h: 4907
+atmsar11.HEX: 19191
+atm_suni.h: 253
+atmsvc.h: 1790
+atm_sysfs.c: 3945
+atmtcp.c: 11650
+atm_tcp.h: 1768
+atm.txt: 426
+atm_zatm.h: 1606
+atngw100: 4096
+atngw100_defconfig: 28242
+atngw100_evklcd100_defconfig: 29353
+atngw100_evklcd101_defconfig: 29353
+atngw100_mrmt_defconfig: 33751
+atombios_crtc.c: 23254
+atombios.h: 221852
+atom-bits.h: 1882
+atom.c: 29689
+atom.h: 4494
+atomic32.c: 2859
+atomic_32.h: 11086
+atomic_32.S: 2663
+atomic64.c: 3863
+atomic_64.h: 10903
+atomic64.h: 1840
+atomic64-ops.S: 4827
+atomic_64.S: 2925
+atomic-grb.h: 4642
+atomic.h: 7333
+atomic-irq.h: 1375
+atomic-llsc.h: 2813
+atomic-long.h: 5244
+atomic_mm.h: 4061
+atomic_no.h: 3556
+atomic-ops.S: 5339
+atomic_ops.txt: 19503
+atomic-ops.txt: 4632
+atomic.S: 15917
+atom-names.h: 4758
+atom-types.h: 1430
+atp870u.c: 86512
+atp870u.h: 1487
+atp.c: 29816
+atp.h: 8737
+atstk1000: 4096
+atstk1000.h: 535
+atstk1002.c: 8012
+atstk1002_defconfig: 30107
+atstk1003.c: 3765
+atstk1003_defconfig: 25774
+atstk1004.c: 3756
+atstk1004_defconfig: 15379
+atstk1006_defconfig: 32583
+attach.c: 9647
+attr.c: 2666
+attrib.c: 91896
+attrib.h: 4321
+attribute_container.c: 12242
+attribute_container.h: 2530
+atxp1.c: 9417
+aty: 4096
+aty128fb.c: 67277
+aty128fb.txt: 2119
+aty128.h: 13554
+atyfb_base.c: 111634
+atyfb.h: 8973
+au0828: 4096
+au0828-cards.c: 9773
+au0828-cards.h: 1060
+au0828-core.c: 7629
+au0828-dvb.c: 11151
+au0828.h: 7385
+au0828-i2c.c: 9287
+au0828-reg.h: 2134
+au0828-video.c: 41940
+au1000_db1x00.c: 7294
+au1000_dma.h: 11212
+au1000_eth.c: 33708
+au1000_eth.h: 3012
+au1000_generic.c: 14674
+au1000_generic.h: 4280
+au1000.h: 53188
+au1000_ircc.h: 3758
+au1000_pb1x00.c: 9234
+au1000_xxs1500.c: 4378
+au1100fb.c: 20752
+au1100fb.h: 14620
+au1100_mmc.h: 5888
+au1200fb.c: 52491
+au1200fb.h: 18704
+au1550_ac97.c: 51655
+au1550nd.c: 14821
+au1550_spi.c: 26531
+au1550_spi.h: 433
+au1k_ir.c: 20249
+au1x: 4096
+au1x00.c: 19173
+au1xmmc.c: 28453
+au1xxx_dbdma.h: 13134
+au1xxx.h: 1723
+au1xxx-ide.c: 15369
+au1xxx_ide.h: 6491
+AU1xxx_IDE.README: 4039
+au1xxx_psc.h: 15895
+au6610.c: 6091
+au6610.h: 1286
+au8522_decoder.c: 27416
+au8522_dig.c: 21506
+au8522.h: 2387
+au8522_priv.h: 17128
+au8810.c: 379
+au8810.h: 7007
+au8820.c: 331
+au8820.h: 6479
+au8830.c: 404
+au8830.h: 8629
+au88x0: 4096
+au88x0_a3d.c: 25257
+au88x0_a3ddata.c: 2908
+au88x0_a3d.h: 4336
+au88x0.c: 10316
+au88x0_core.c: 78891
+au88x0_eq.c: 23045
+au88x0_eqdata.c: 3952
+au88x0_eq.h: 1287
+au88x0_game.c: 3655
+au88x0.h: 8929
+au88x0_mixer.c: 773
+au88x0_mpu401.c: 3619
+au88x0_pcm.c: 15858
+au88x0_synth.c: 11076
+au88x0_wt.h: 2347
+au88x0_xtalk.c: 23426
+au88x0_xtalk.h: 2304
+Audigy-mixer.txt: 13956
+audio.c: 7519
+AudioExcelDSP16: 3886
+audio.h: 525
+Audiophile-Usb.txt: 17915
+audit_64.c: 1870
+audit.c: 1888
+audit_change_attr.h: 320
+audit_dir_write.h: 238
+auditfilter.c: 34110
+audit.h: 394
+audit_read.h: 127
+auditsc.c: 67706
+audit_signal.h: 36
+audit_tree.c: 22256
+audit_watch.c: 14765
+audit_write.h: 255
+aureon.c: 64017
+aureon.h: 2419
+autcpu12.c: 5831
+autcpu12.h: 2590
+autcpu12-nvram.c: 3124
+auth.c: 35
+authenc.c: 13429
+authenc.h: 609
+auth_generic.c: 4800
+auth_gss: 4096
+auth_gss.c: 40950
+auth_gss.h: 2256
+auth.h: 540
+auth_null.c: 2607
+authorization.txt: 2640
+authors: 38
+AUTHORS: 38
+auth_rsp.c: 39
+auth_unix.c: 5607
+auto_dev-ioctl.h: 5186
+autofs: 4096
+autofs4: 4096
+auto_fs4.h: 4095
+autofs4-mount-control.txt: 17706
+auto_fs.h: 2467
+autofs_i.h: 7355
+autoload.c: 967
+autoload.sh: 339
+automount-support.txt: 4694
+autoprobe.c: 4772
+auxdisplay: 4096
+auxio_32.c: 3721
+auxio_32.h: 2622
+auxio_64.c: 3214
+auxio_64.h: 3266
+auxio.h: 176
+aux_reg.h: 646
+auxvec.h: 60
+av7110: 4096
+av7110_av.c: 40033
+av7110_av.h: 1214
+av7110.c: 82729
+av7110_ca.c: 9180
+av7110_ca.h: 441
+av7110.h: 7155
+av7110_hw.c: 31995
+av7110_hw.h: 12512
+av7110_ipack.c: 7991
+av7110_ipack.h: 402
+av7110_ir.c: 11196
+av7110_v4l.c: 27884
+avc.c: 24534
+avc.h: 3102
+avc_ss.h: 617
+avermedia.txt: 13922
+avila.h: 917
+avila-pci.c: 1817
+avila-setup.c: 4610
+av_inherit.h: 1693
+avm: 4096
+avm_a1.c: 8303
+avma1_cs.c: 9468
+avm_a1p.c: 7146
+avmcard.h: 13933
+avm_cs.c: 9107
+avm_pci.c: 23187
+av_permissions.h: 53693
+av_perm_to_string.h: 10893
+avr32: 4096
+__avr32_asr64.S: 560
+avr32_ksyms.c: 1843
+__avr32_lsl64.S: 559
+__avr32_lsr64.S: 559
+avtab.c: 12842
+avtab.h: 2874
+aw2: 4096
+aw2-alsa.c: 22536
+aw2-saa7146.c: 12816
+aw2-saa7146.h: 3650
+aw2-tsl.c: 4092
+awacs.c: 32895
+awacs.h: 8192
+ax25: 4096
+ax25_addr.c: 6217
+ax25_dev.c: 4867
+ax25_ds_in.c: 7261
+ax25_ds_subr.c: 5218
+ax25_ds_timer.c: 5960
+ax25.h: 2752
+ax25_iface.c: 5067
+ax25_in.c: 10789
+ax25_ip.c: 5405
+ax25_out.c: 8933
+ax25_route.c: 11432
+ax25_std_in.c: 11343
+ax25_std_subr.c: 2329
+ax25_std_timer.c: 4335
+ax25_subr.c: 7113
+ax25_timer.c: 5261
+ax25.txt: 610
+ax25_uid.c: 5032
+ax88796.c: 25354
+ax88796.h: 751
+axisflashmap.c: 11854
+axisflashmap.h: 1932
+axnet_cs.c: 55656
+axon_msi.c: 11768
+axonram.c: 9510
+azt2320.c: 9995
+azt3328.c: 74771
+azt3328.h: 16466
+b128ops.h: 2471
+b180_defconfig: 30806
+b1.c: 20917
+b1dma.c: 24682
+b1isa.c: 6013
+b1lli.h: 1654
+b1pci.c: 10903
+b1pcmcia.c: 5718
+b1pcmcia.h: 666
+b2: 4096
+b2c2: 4096
+b3dfg: 4096
+b3dfg.c: 29136
+b43: 4096
+b43.h: 34564
+b43legacy: 4096
+b43legacy.h: 26792
+b43_pci_bridge.c: 1563
+b44.c: 58926
+b44.h: 17577
+ba_action.c: 43
+baboon.c: 2869
+backchannel_rqst.c: 9002
+backend-api.txt: 23417
+backend.c: 8969
+background.c: 4246
+backing-dev.c: 7753
+backing-dev.h: 7625
+backing_ops.c: 11152
+backlight: 4096
+backlight.c: 8488
+backlight.h: 1076
+backoff.h: 514
+backtrace.c: 2048
+backtrace.S: 3769
+backtracetest.c: 2135
+badge4.c: 7278
+badge4_defconfig: 25237
+badge4.h: 2530
+bad_inode.c: 8103
+bad_memory.txt: 1113
+balance: 5103
+balloc.c: 28466
+balloon.c: 15823
+bamboo.c: 1219
+bamboo_defconfig: 23088
+bamboo.dts: 7833
+barrier.h: 751
+barrier.txt: 10841
+base: 4096
+baseband.c: 69836
+baseband.h: 5142
+base.c: 17017
+base.h: 5692
+base.S: 2976
+bas-gigaset.c: 70245
+basic: 4096
+basic-pm-debugging.txt: 10323
+basic_profiling.txt: 1707
+basler: 4096
+bast-cpld.h: 1556
+bast-ide.c: 2522
+bast-irq.c: 3575
+bast-irq.h: 842
+bast-map.h: 5001
+bast-pmu.h: 1140
+battery.c: 26147
+baycom_epp.c: 34930
+baycom.h: 820
+baycom_par.c: 16899
+baycom_ser_fdx.c: 21220
+baycom_ser_hdx.c: 21005
+baycom.txt: 7038
+bbc_envctrl.c: 15825
+bbc.h: 9957
+bbc_i2c.c: 9865
+bbc_i2c.h: 1987
+bbm.h: 4408
+bcache.h: 1450
+bcast.c: 21337
+bcast.h: 5594
+bcd.c: 257
+bcd.h: 195
+bcm1480: 4096
+bcm1480_int.h: 18356
+bcm1480_l2c.h: 8696
+bcm1480_mc.h: 51843
+bcm1480_regs.h: 42338
+bcm1480_scd.h: 15457
+bcm203x.c: 7066
+bcm3510.c: 21894
+bcm3510.h: 1667
+bcm3510_priv.h: 9194
+bcm47xx: 4096
+bcm47xx_defconfig: 44162
+bcm47xx.h: 914
+bcm47xx_wdt.c: 6278
+bcm5974.c: 22743
+bcm5974.txt: 2275
+bcm.c: 39855
+bcm.h: 2057
+bcom_ata_task.c: 1873
+bcom_fec_rx_task.c: 2688
+bcom_fec_tx_task.c: 3548
+bcom_gen_bd_rx_task.c: 1901
+bcom_gen_bd_tx_task.c: 2118
+bc_svc.c: 2387
+bcu.c: 5507
+bc_xprt.h: 1873
+bdx.bin.ihex: 117765
+beacon.c: 16577
+bearer.c: 17977
+bearer.h: 6277
+beat.c: 5680
+beat.h: 1477
+beat_htab.c: 12023
+beat_hvCall.S: 4654
+beat_interrupt.c: 7398
+beat_interrupt.h: 1147
+beat_iommu.c: 3142
+beat_smp.c: 2934
+beat_spu_priv1.c: 5295
+beat_syscall.h: 9338
+beat_udbg.c: 2419
+beat_wrapper.h: 7284
+be_byteshift.h: 1424
+be_cmds.c: 28134
+be_cmds.h: 21092
+beep.c: 7937
+be_ethtool.c: 10213
+befs: 4096
+befs_fs_types.h: 5040
+befs.h: 3265
+befs.txt: 3636
+be.h: 9794
+be_hw.h: 6736
+belkin_sa.c: 17581
+belkin_sa.h: 5114
+be_main.c: 53536
+be_memmove.h: 770
+Benchmark.h: 14912
+benet: 4096
+berry_charge.c: 5094
+bestcomm: 4096
+bestcomm.c: 13444
+bestcomm.h: 5696
+bestcomm_priv.h: 10178
+be_struct.h: 749
+BF518F-EZBRD_defconfig: 30487
+bf518.h: 3233
+BF526-EZBRD_defconfig: 37925
+BF527-EZKIT_defconfig: 39344
+bf527.h: 3368
+BF533-EZKIT_defconfig: 27699
+bf533.h: 3987
+BF533-STAMP_defconfig: 31991
+bf537.h: 3679
+BF537-STAMP_defconfig: 33322
+BF538-EZKIT_defconfig: 31824
+bf538.h: 3110
+BF548-EZKIT_defconfig: 41918
+bf548.h: 3408
+bf54x-keys.c: 10383
+bf54x_keys.h: 373
+bf54x-lq043fb.c: 19521
+bf54x-lq043.h: 486
+BF561-EZKIT_defconfig: 27483
+bf561.h: 5899
+bf5xx-ac97.c: 10719
+bf5xx-ac97.h: 1698
+bf5xx-ac97-pcm.c: 13449
+bf5xx-ac97-pcm.h: 595
+bf5xx-ad1980.c: 3043
+bf5xx-ad73311.c: 6491
+bf5xx-i2s.c: 8397
+bf5xx-i2s.h: 321
+bf5xx-i2s-pcm.c: 7810
+bf5xx-i2s-pcm.h: 591
+bf5xx_nand.c: 19911
+bf5xx-sport.c: 26015
+bf5xx-sport.h: 5191
+bf5xx-ssm2602.c: 4846
+bfin_5xx.c: 37574
+bfin5xx_spi.h: 3282
+bfin-async-flash.c: 5961
+bfin_cf_pcmcia.c: 8045
+bfind.c: 4484
+bfin_dma_5xx.c: 13838
+bfin-global.h: 3579
+bfin_gpio.c: 31670
+bfin-gpio-notes.txt: 2459
+bfin_jtag_comm.c: 9699
+bfin_ksyms.c: 3302
+bfin_mac.c: 29136
+bfin_mac.h: 1654
+bfin_oprofile.c: 303
+bfin-otp.c: 4874
+bfin_sdh.h: 273
+bfin_serial_5xx.h: 5124
+bfin_simple_timer.h: 446
+bfin_sir.c: 18943
+bfin_sir.h: 5300
+bfin_sport.h: 3839
+bfin_sport_uart.c: 16463
+bfin_sport_uart.h: 3726
+bfin-t350mcqb-fb.c: 16570
+bfin_wdt.c: 11813
+bfrom.h: 3443
+bfs: 4096
+bfs_fs.h: 1830
+bfs.h: 1424
+bfs.txt: 2118
+bfusb.c: 17036
+bif_core_defs_asm.h: 15240
+bif_core_defs.h: 9672
+bif_dma_defs_asm.h: 22539
+bif_dma_defs.h: 14998
+bif_slave_defs_asm.h: 11664
+bif_slave_defs.h: 8256
+big_endian.h: 3729
+big-endian.S: 304
+BIG.FAT.WARNING: 234
+bigsmp_32.c: 6414
+bigsur_defconfig: 32018
+bigsur.h: 1540
+bin2c.c: 702
+bin2hex.c: 755
+bin.c: 11164
+bind_addr.c: 13678
+bind.c: 7327
+bindec.S: 28113
+binder.c: 105864
+binder.h: 9073
+bind.h: 1305
+binding.txt: 3687
+binfmt_aout.c: 13156
+binfmt_elf32.c: 3565
+binfmt_elf.c: 55465
+binfmt_elf_fdpic.c: 48887
+binfmt_elfn32.c: 3368
+binfmt_elfo32.c: 4508
+binfmt_em86.c: 2848
+binfmt_flat.c: 27410
+binfmt_loader.c: 1116
+binfmt_misc.c: 15436
+binfmt_misc.txt: 6108
+binfmt_script.c: 2829
+binfmts.h: 4146
+binfmt_som.c: 7567
+binoffset.c: 4039
+binstr.S: 4302
+bio.c: 39950
+biodoc.txt: 55452
+bio.h: 20583
+bio-integrity.c: 21385
+bios32.c: 17673
+bios.c: 2953
+bioscall.S: 1566
+bioscalls.c: 13235
+bios_ebda.h: 794
+bios.h: 3282
+bios_uv.c: 4629
+bitblit.c: 10909
+bitext.c: 2979
+bitext.h: 613
+bitfield.h: 19835
+bitmap.c: 45202
+bitmap.h: 10515
+bit_operations.h: 5627
+bitops: 4096
+bitops_32.h: 2988
+bitops_64.h: 2428
+bitops.c: 1091
+bitops-grb.h: 6325
+bitops.h: 3153
+bitops-llsc.h: 2819
+bitops_mm.h: 11048
+bitops_no.h: 8097
+bitops-op32.h: 3832
+bitops.S: 2667
+bitrev.c: 2157
+bitrev.h: 270
+bits.h: 2634
+bitsperlong.h: 37
+bit_spinlock.h: 2210
+bitstream.bin.ihex: 33395
+bitstream.HEX: 192281
+bkm_a4t.c: 9149
+bkm_a8.c: 11765
+bkm_ax.h: 4575
+blackfin: 4096
+blackfin.c: 7552
+blackfin.h: 1089
+blackfin_sram.h: 1207
+blacklist.c: 8658
+blacklist.h: 108
+blackstamp.c: 9838
+BlackStamp_defconfig: 27434
+blinken.h: 617
+blizzard.c: 41338
+blizzard.h: 249
+blk-barrier.c: 9923
+blkcipher.c: 19013
+blk-core.c: 65533
+blkdev.h: 38233
+blk-exec.c: 2683
+blk.h: 4565
+blkif.h: 3398
+blk-integrity.c: 10137
+blk-ioc.c: 4083
+blk-map.c: 8314
+blk-merge.c: 9996
+blkpg.h: 1569
+blk-settings.c: 21842
+blk-softirq.c: 4192
+blk-sysfs.c: 12022
+blk-tag.c: 10090
+blk-timeout.c: 5850
+blktrace_api.h: 7347
+blktrace.c: 40749
+blktrans.h: 1935
+bloat-o-meter: 1711
+block: 4096
+block2mtd.c: 10757
+block.c: 15974
+blockcheck.c: 16817
+blockcheck.h: 3928
+blockdev: 4096
+block_dev.c: 37737
+blockgroup_lock.h: 1164
+block.h: 12870
+blockops.S: 2573
+block_validity.c: 6187
+blowfish.c: 17890
+bluecard_cs.c: 21854
+bluetooth: 4096
+bluetooth.h: 4823
+bmac.c: 42545
+bmac.h: 8220
+bmap.c: 17082
+bmap.h: 8896
+bmap_union.h: 1274
+bnep: 4096
+bnep.h: 4313
+bnode.c: 15448
+bnx2: 4096
+bnx2.c: 204472
+bnx2_fw.h: 2909
+bnx2.h: 328128
+bnx2i: 4096
+bnx2i.h: 23900
+bnx2i_hwi.c: 73780
+bnx2i_init.c: 11868
+bnx2i_iscsi.c: 56395
+bnx2i_sysfs.c: 3632
+bnx2-mips-06-4.6.16.fw.ihex: 255140
+bnx2-mips-09-4.6.17.fw.ihex: 255536
+bnx2-rv2p-06-4.6.16.fw.ihex: 19256
+bnx2-rv2p-09-4.6.15.fw.ihex: 21444
+bnx2x_dump.h: 28960
+bnx2x-e1-4.8.53.0.fw.ihex: 455912
+bnx2x-e1h-4.8.53.0.fw.ihex: 529144
+bnx2x_fw_defs.h: 15968
+bnx2x_fw_file_hdr.h: 1226
+bnx2x.h: 36495
+bnx2x_hsi.h: 87970
+bnx2x_init.h: 12172
+bnx2x_init_ops.h: 12435
+bnx2x_link.c: 162411
+bnx2x_link.h: 6119
+bnx2x_main.c: 321334
+bnx2x_reg.h: 308654
+board-1arm.c: 2699
+board-2430sdp.c: 5365
+board-3430sdp.c: 12788
+board-4430sdp.c: 2343
+board-a9m9750dev.c: 3624
+board-a9m9750dev.h: 478
+board-acs5k.c: 5495
+board-afeb-9260v1.c: 5234
+board-ams-delta.c: 6109
+board-ams-delta.h: 3023
+board-ap325rxa.c: 13583
+board-apollon.c: 8566
+board-armadillo5x0.h: 564
+board.c: 3934
+board-cam60.c: 4681
+board-cap9adk.c: 9718
+board-carmeva.c: 4435
+board-csb337.c: 6265
+board-csb637.c: 3642
+board-dk.c: 5862
+board-dm355-evm.c: 7541
+board-dm355-leopard.c: 7584
+board-dm644x-evm.c: 17303
+board-dm646x-evm.c: 5912
+board-dsm320.c: 2919
+board-eb01.c: 1385
+board-eb9200.c: 3461
+board-eb.h: 3843
+board-ecbat91.c: 4404
+board-edosk7760.c: 4611
+board-ek.c: 5054
+boardergo.c: 16200
+boardergo.h: 4069
+board-espt.c: 2426
+board-fsample.c: 8474
+board-generic.c: 1918
+board.h: 3611
+board-h2.c: 10497
+board-h2.h: 1585
+board-h2-mmc.c: 1862
+board-h3.c: 9698
+board-h3.h: 1550
+board-h3-mmc.c: 1668
+board-h4.c: 9524
+board-halibut.c: 2112
+board-innovator.c: 10872
+board-jscc9p9360.c: 457
+board-jscc9p9360.h: 390
+board-kafa.c: 2778
+board-kb9202.c: 3738
+board-ldp.c: 9161
+board-magicpanelr2.c: 11382
+board-micrel.c: 1450
+board-mx21ads.h: 1998
+board-mx27ads.h: 10858
+board-mx27lite.h: 544
+board-mx27pdk.h: 541
+board-mx31ads.h: 3850
+board-mx31lilly.h: 1430
+board-mx31lite.h: 508
+board-mx31moboard.h: 1408
+board-mx31pdk.h: 2120
+board-mx35pdk.h: 1062
+board-neocore926.c: 9196
+board-nokia770.c: 9780
+board-omap3beagle.c: 10647
+board-omap3evm.c: 7649
+board-omap3pandora.c: 10350
+board-osk.c: 15556
+board-overo.c: 11768
+board-palmte.c: 9495
+board-palmtt.c: 7148
+board-palmz71.c: 8139
+board-pb1176.h: 3534
+board-pb11mp.h: 3933
+board-pba8.h: 3279
+board-pbx.h: 4735
+board-pcm037.h: 1039
+board-pcm038.h: 1428
+board-pcm043.h: 1039
+board-perseus2.c: 7106
+board-picotux200.c: 4544
+board-polaris.c: 3273
+board-qil-a9260.c: 6417
+board-qong.h: 590
+board-rut1xx.c: 2111
+board-rx51.c: 2237
+board-rx51-peripherals.c: 9630
+boards: 4096
+board-sam9260ek.c: 8166
+board-sam9261ek.c: 14425
+board-sam9263ek.c: 10154
+board-sam9g20ek.c: 7036
+board-sam9-l9260.c: 5065
+board-sam9rlek.c: 5294
+boards.c: 9634
+board-se7619.c: 368
+board_setup.c: 991
+board-sffsdr.c: 5298
+boards.h: 874
+board-sh7785lcr.c: 8051
+board-shmin.c: 715
+board-sx1.c: 10319
+board-sx1.h: 1472
+board-sx1-mmc.c: 1614
+board.txt: 1400
+board-urquell.c: 4834
+board-usb-a9260.c: 5382
+board-usb-a9263.c: 5692
+board-voiceblue.c: 6718
+board-voiceblue.h: 552
+board-yl-9200.c: 19259
+board-zoom2.c: 2568
+board-zoom-debugboard.c: 3833
+bond_3ad.c: 81880
+bond_3ad.h: 9440
+bond_alb.c: 45790
+bond_alb.h: 4802
+bonding: 4096
+bonding.h: 10602
+bonding.txt: 98243
+bond_ipv6.c: 5432
+bond_main.c: 137243
+bond_sysfs.c: 42050
+bonito64.h: 16247
+bonito-irq.c: 2465
+booke.c: 16034
+booke_emulate.c: 6991
+booke.h: 2429
+booke_interrupts.S: 11644
+booke_wdt.c: 4745
+boot: 4096
+boot2.H16: 14566
+boot.c: 8143
+bootcode.bin.ihex: 604
+boot-elf: 4096
+bootflag.c: 1698
+bootgraph.pl: 5753
+boot.h: 1195
+boot.H16: 14980
+boot_head.S: 4023
+bootinfo.h: 3250
+Booting: 4704
+booting.txt: 5755
+booting-without-of.txt: 61721
+boot.ld: 812
+boot.lds.S: 927
+bootloader.c: 3823
+bootloader.lds: 501
+bootlogo.h: 236895
+bootlogo.pl: 165
+bootmem.c: 19242
+bootmem.h: 4841
+boot-options.txt: 12803
+bootp: 4096
+bootparam.h: 1530
+bootp.c: 5689
+bootp.lds: 695
+bootpz.c: 13415
+boot-redboot: 4096
+Boot.S: 2820
+bootstd.h: 4709
+bootstr_32.c: 1207
+bootstr_64.c: 1055
+bootstrap.asm: 2924
+bootstrap.bin.ihex: 1080
+bootstrap.S: 4829
+boot.txt: 35058
+bootwrapper.txt: 7768
+bootx.h: 5211
+bootx_init.c: 16762
+bottom_half.h: 224
+bounce.c: 6616
+bounds.c: 526
+bpa10x.c: 10967
+bpck6.c: 6457
+bpck.c: 9505
+bpqether.c: 14923
+bpqether.h: 952
+bq24022.c: 4006
+bq24022.h: 728
+bq27x00_battery.c: 8343
+br2684.c: 20774
+braille: 4096
+braille_console.c: 8194
+braille-console.txt: 1458
+branch.c: 5609
+branches: 4096
+branch.h: 794
+br.c: 2297
+brcmphy.h: 277
+brd.c: 13994
+br_device.c: 4305
+break.h: 1512
+break.S: 20777
+brec.c: 13153
+br_fdb.c: 10129
+br_forward.c: 3296
+brg.txt: 450
+bridge: 4096
+bridge.h: 29862
+bridge-regs.h: 1121
+bridge.txt: 382
+br_if.c: 9454
+br_input.c: 4146
+br_ioctl.c: 9353
+briq_panel.c: 5383
+brl_emu.c: 5592
+br_netfilter.c: 28764
+br_netlink.c: 4931
+br_notify.c: 2202
+broadcom.c: 17870
+broadsheetfb.c: 13698
+broadsheetfb.h: 1727
+br_private.h: 7906
+br_private_stp.h: 1654
+br_stp_bpdu.c: 5251
+br_stp.c: 11134
+br_stp_if.c: 7340
+br_stp_timer.c: 4651
+br_sysfs_br.c: 12447
+br_sysfs_if.c: 6540
+Brutus: 1927
+bsbe1.h: 3346
+bsd_comp.c: 29587
+bsg.c: 23964
+bsg.h: 3096
+bsr.c: 8971
+bsru6.h: 3855
+bssdb.c: 59681
+bssdb.h: 9978
+bt3c_cs.c: 16786
+bt431.h: 5854
+bt455.h: 2096
+bt819.c: 14549
+bt819.h: 1082
+bt848.h: 11788
+bt856.c: 7085
+bt866.c: 6222
+bt878.c: 16114
+bt878.h: 4113
+bt87x.c: 30530
+Bt87x.txt: 2597
+bt8xx: 4096
+bt8xxgpio.c: 8476
+bt8xxgpio.txt: 4402
+bt8xx.txt: 3940
+btaudio: 3104
+btcx-risc.c: 6470
+btcx-risc.h: 863
+bte.c: 12913
+bte_error.c: 7665
+bte.h: 7765
+btext.c: 38463
+btext.h: 858
+btfixup.c: 10268
+btfixup.h: 7304
+btfixupprep.c: 11589
+btnode.c: 8474
+btnode.h: 2067
+btree.c: 8032
+btree.h: 5488
+btrfs: 4096
+btrfs_inode.h: 4339
+btrfs.txt: 2918
+btsdio.c: 8446
+bttv: 4096
+bttv-audio-hook.c: 9617
+bttv-audio-hook.h: 1126
+bttv-cards.c: 151213
+bttv-driver.c: 121604
+bttv-gpio.c: 4858
+bttv.h: 14877
+bttv-i2c.c: 10677
+bttv-if.c: 2894
+bttv-input.c: 10597
+bttvp.h: 14108
+bttv-risc.c: 25965
+bttv-vbi.c: 12835
+btuart_cs.c: 15857
+btusb.c: 24625
+buddha.c: 5673
+budget-av.c: 45832
+budget.c: 23026
+budget-ci.c: 46442
+budget-core.c: 17319
+budget.h: 3030
+budget-patch.c: 20598
+buffer.c: 3308
+buffer-format.txt: 4606
+buffer_head.h: 11463
+buffer_head_io.c: 10961
+buffer_head_io.h: 2416
+buffer_icap.c: 10726
+buffer_icap.h: 2297
+buffer_sync.c: 13744
+buffer_sync.h: 432
+bug.c: 423
+bugfix.S: 14062
+bug.h: 400
+BUG-HUNTING: 8326
+BUGS: 235
+bugs_64.c: 778
+bugs.c: 1893
+bugs.h: 451
+BUGS-parport: 319
+build.c: 39439
+builddeb: 5872
+buildtar: 2659
+builtin-annotate.c: 29512
+builtin.h: 1035
+builtin-help.c: 11520
+builtin-list.c: 408
+builtin-record.c: 14786
+builtin-report.c: 35243
+builtin-stat.c: 13788
+builtin-top.c: 17061
+bunzip2.h: 258
+burgundy.c: 25045
+burgundy.h: 4105
+bus.c: 8958
+busctl-regs.h: 2071
+bus.h: 950
+BusLogic.c: 151498
+BusLogic.h: 40976
+BusLogic.txt: 26440
+bus-osm.c: 4125
+busses: 4096
+bust_spinlocks.c: 636
+bus.txt: 4536
+bus_watcher.c: 7864
+butterfly: 2990
+button.c: 12060
+buttons.c: 1516
+bvme6000: 4096
+bvme6000_defconfig: 24611
+bvme6000hw.h: 3490
+bvme6000_scsi.c: 3343
+bw2.c: 9440
+bw-qcam.c: 25715
+bw-qcam.h: 2049
+byteorder: 4096
+byteorder.h: 277
+bzero.S: 3581
+c101.c: 11218
+c16e9e8bb74f14f4504305957e4346e7fc46ea: 296
+c2_ae.c: 9166
+c2_ae.h: 3338
+c2_alloc.c: 4106
+c2.c: 33727
+c2_cm.c: 9984
+c2_cq.c: 10526
+c2.h: 13957
+c2_intr.c: 5630
+c2k.c: 3705
+c2k_defconfig: 48649
+c2k.dts: 8777
+c2_mm.c: 8862
+c2_mq.c: 4622
+c2_mq.h: 3238
+c2p_core.h: 2688
+c2_pd.c: 3012
+c2p.h: 631
+c2p_iplan2.c: 3576
+c2port: 4096
+c2port-duramar2150.c: 3241
+c2port.h: 1788
+c2port.txt: 2848
+c2p_planar.c: 3697
+c2_provider.c: 21747
+c2_provider.h: 4101
+c2_qp.c: 25022
+c2_rnic.c: 16782
+c2_status.h: 5103
+c2_user.h: 2415
+c2_vq.c: 7741
+c2_vq.h: 2530
+c2_wr.h: 35475
+c3000_defconfig: 37418
+c4.c: 33871
+c67x00: 4096
+c67x00-drv.c: 6069
+c67x00.h: 9392
+c67x00-hcd.c: 10012
+c67x00-hcd.h: 4298
+c67x00-ll-hpi.c: 12221
+c67x00-sched.c: 30459
+c6xdigio.c: 13668
+ca0106: 4096
+ca0106.h: 37992
+ca0106_main.c: 59396
+ca0106_mixer.c: 27767
+ca0106_proc.c: 14457
+cacheasm.h: 3274
+cache.c: 10301
+cache-c.c: 733
+cachectl.h: 752
+cache-debugfs.c: 3861
+cache-fa.S: 5726
+cachefeatures.txt: 1538
+cache-feroceon-l2.c: 8394
+cache-feroceon-l2.h: 358
+cachefiles: 4096
+cachefiles.txt: 17132
+cacheflush_32.h: 3544
+cacheflush_64.h: 2543
+cacheflush.h: 7154
+cacheflush_mm.h: 4194
+cache-flush-mn10300.S: 5528
+cacheflush_no.h: 2672
+cacheflush.S: 1897
+cache.h: 4660
+cacheinfo.c: 20221
+cacheinfo.h: 240
+cacheinit.c: 1960
+cache-l2x0.c: 3064
+cache-l2x0.h: 1931
+cache-lock.txt: 1222
+cache-mn10300.S: 6477
+cacheops.h: 2183
+cache-page.c: 1964
+cache.S: 2858
+cache-sh2a.c: 3241
+cache-sh2.c: 2044
+cache-sh3.c: 2486
+cache-sh4.c: 20825
+cache-sh5.c: 27270
+cache-sh7705.c: 5050
+cachetlb.txt: 16265
+cachetype.h: 1643
+cache-v3.S: 3174
+cache-v4.S: 3364
+cache-v4wb.S: 5731
+cache-v4wt.S: 4411
+cache-v6.S: 6450
+cache-v7.S: 7015
+cache-xsc3l2.c: 5806
+caching: 4096
+cafe_ccic: 2424
+cafe_ccic.c: 53700
+cafe_ccic-regs.h: 6845
+cafe_nand.c: 25500
+cagg.c: 110445
+cagg.h: 15067
+ca.h: 3022
+caiaq: 4096
+caleb.c: 3138
+caleb.h: 636
+calgary.h: 2452
+calib.c: 28905
+calib.h: 3835
+calibrate.c: 5121
+callback.c: 9965
+callback.h: 2994
+callback_proc.c: 6012
+callbacks.c: 7878
+callbacks.h: 1495
+callback_srm.S: 2840
+callbacks.txt: 4860
+callback_xdr.c: 18050
+callc.c: 48919
+callchain.c: 3746
+callchain.h: 711
+call_hpt.h: 3078
+calling.h: 5596
+call_o32.S: 2534
+call_pci.h: 7934
+call_sm.h: 1270
+calls.S: 10619
+cam60_defconfig: 28038
+camellia.c: 35950
+camera.h: 1504
+cam.h: 4590
+ca_midi.c: 8689
+ca_midi.h: 2024
+can: 4096
+can.h: 3326
+can.txt: 35254
+canyonlands_defconfig: 25711
+canyonlands.dts: 14369
+capability.c: 8203
+capability.h: 17864
+capability.txt: 618
+capcella_defconfig: 18424
+capcella.h: 1486
+capi: 4096
+capi20.h: 29357
+capi.c: 33038
+capicmd.h: 4663
+capidrv.c: 64650
+capidrv.h: 4862
+capidtmf.c: 26932
+capidtmf.h: 3726
+capifs.c: 5266
+capifs.h: 360
+capifunc.c: 31377
+capifunc.h: 903
+capi.h: 10360
+capilib.c: 4606
+capilli.h: 3567
+capimain.c: 3336
+capiutil.c: 30023
+capiutil.h: 14649
+capmode.c: 8041
+caps.c: 5160
+capture.c: 9562
+capture.h: 702
+card: 4096
+cardbus.c: 6323
+card.c: 10228
+card.h: 4366
+CARDLIST.au0828: 527
+CARDLIST.bttv: 9061
+CARDLIST.cx23885: 1693
+CARDLIST.cx88: 5876
+CARDLIST.em28xx: 4880
+CARDLIST.ivtv: 1163
+CARDLIST.saa7134: 9829
+CARDLIST.tuner: 2995
+CARDLIST.usbvision: 4940
+Cards: 26708
+cards.txt: 4752
+cardtype.h: 39894
+carmel.h: 1969
+carmeva_defconfig: 13180
+carminefb.c: 22690
+carminefb.h: 2251
+carminefb_regs.h: 6940
+carta_random.S: 1032
+casio-e55: 4096
+cassini.bin.ihex: 6246
+cassini.c: 145494
+cassini.h: 125724
+cast5.c: 34939
+cast6.c: 22021
+catalog.c: 10749
+catas.c: 4281
+catc.c: 24085
+cats-hw.c: 2052
+cats-pci.c: 1313
+cavium-octeon: 4096
+cavium-octeon_defconfig: 22786
+cayman_defconfig: 31979
+cb710: 4096
+cb710.h: 6584
+cb710-mmc.c: 22406
+cb710-mmc.h: 3337
+cbaf.c: 20405
+cbc.c: 7621
+cb_das16_cs.c: 25977
+cbe_cpufreq.c: 4980
+cbe_cpufreq.h: 578
+cbe_cpufreq_pervasive.c: 2924
+cbe_cpufreq_pmi.c: 3695
+cbe_powerbutton.c: 3019
+cbe_regs.c: 6692
+cbe_thermal.c: 10724
+cb_pcidas64.c: 122559
+cb_pcidas.c: 54058
+cb_pcidda.c: 24298
+cb_pcidio.c: 9309
+cb_pcimdas.c: 14161
+cb_pcimdda.c: 15282
+cchips: 4096
+ccid2.c: 21699
+ccid2.h: 2480
+ccid3.c: 29348
+ccid3.h: 6401
+ccid.c: 5450
+ccid.h: 7568
+ccids: 4096
+ccio-dma.c: 48900
+ccio-rm-dma.c: 5203
+cciss.c: 121676
+cciss_cmd.h: 8834
+cciss.h: 7343
+cciss_ioctl.h: 6805
+cciss_scsi.c: 49769
+cciss_scsi.h: 3184
+cciss.txt: 6396
+ccm.c: 22059
+ccmd.c: 47173
+ccwdev.h: 6880
+ccwgroup.c: 16375
+ccwgroup.h: 2479
+cd1400.h: 7059
+cd1865.h: 13309
+cd32.txt: 535
+cda2df87ba4ecc7988be7a45d01645e11c9f4c: 307
+cdb89712.c: 5870
+cdc2.c: 6912
+cdc-acm.c: 43081
+cdc-acm.h: 3737
+cdc_eem.c: 9394
+cdc_ether.c: 17432
+cdc.h: 7100
+cdc_subset.c: 10970
+cdc-wdm.c: 19634
+cdefBF512.h: 1386
+cdefBF514.h: 5819
+cdefBF516.h: 16666
+cdefBF518.h: 16668
+cdefBF51x_base.h: 71524
+cdefBF522.h: 1386
+cdefBF525.h: 26762
+cdefBF527.h: 37609
+cdefBF52x_base.h: 71524
+cdefBF532.h: 48301
+cdefBF534.h: 124785
+cdefBF537.h: 13187
+cdefBF538.h: 148152
+cdefBF539.h: 16826
+cdefBF542.h: 35078
+cdefBF544.h: 59758
+cdefBF547.h: 48769
+cdefBF548.h: 98262
+cdefBF549.h: 115636
+cdefBF54x_base.h: 169306
+cdefBF561.h: 117485
+cdef_LPBlackfin.h: 20055
+cdev.c: 25135
+cdev.h: 677
+cdk.h: 12770
+cdrom: 4096
+cdrom.c: 99243
+cdrom.h: 36224
+cdrom-standard.tex: 51371
+cdrom.txt: 19223
+cds.txt: 21022
+ce6230.c: 8126
+ce6230.h: 2306
+ceiva.c: 7563
+cell: 4096
+cell.c: 9326
+cell_defconfig: 37867
+celleb_defconfig: 32091
+celleb_pci.c: 12369
+celleb_pci.h: 1402
+celleb_scc_epci.c: 10052
+celleb_scc.h: 7871
+celleb_scc_pciex.c: 15023
+celleb_scc_sio.c: 2600
+celleb_scc_uhc.c: 2518
+celleb_setup.c: 6097
+cell_edac.c: 7394
+cell-pmu.h: 4133
+cell-regs.h: 9211
+centaur.c: 5432
+central.c: 6181
+CERF: 1215
+cerf.c: 3449
+cerfcube_defconfig: 17978
+cerf.h: 810
+cerr-sb1.c: 16742
+cevt-bcm1480.c: 4444
+cevt-ds1287.c: 2833
+cevt-gt641xx.c: 3662
+cevt-r4k.c: 5115
+cevt-r4k.h: 1421
+cevt-sb1250.c: 4380
+cevt-smtc.c: 8920
+cevt-txx9.c: 5694
+cex-gen.S: 1009
+cex-oct.S: 1574
+cex-sb1.S: 4707
+cfag12864b: 3173
+cfag12864b.c: 8350
+cfag12864b-example.c: 5897
+cfag12864bfb.c: 4656
+cfag12864b.h: 2147
+cfbcopyarea.c: 11312
+cfbfillrect.c: 8940
+cfbimgblt.c: 8396
+cfe: 4096
+cfe_api.c: 11219
+cfe_api.h: 3833
+cfe_api_int.h: 4031
+cfe.c: 8423
+cfe_console.c: 1737
+cfe_error.h: 2197
+cfg80211.c: 10563
+cfg80211.h: 1079
+cfg.c: 35379
+cfg.h: 155
+cfi_cmdset_0001.c: 73671
+cfi_cmdset_0002.c: 53117
+cfi_cmdset_0020.c: 38615
+cfi_endian.h: 1238
+cfi_flagadm.c: 3947
+cfi.h: 13714
+cfi_probe.c: 11670
+cfi_util.c: 5934
+cfm.c: 16482
+cfq-iosched.c: 65456
+cfunc.c: 33674
+cfunc.h: 23021
+cg14.c: 15044
+cg3.c: 11707
+cg6.c: 22284
+cgroup.c: 95997
+cgroup_debug.c: 2057
+cgroup_freezer.c: 9146
+cgroup.h: 15604
+cgroups: 4096
+cgroupstats.h: 2155
+cgroupstats.txt: 1307
+cgroups.txt: 21831
+cgroup_subsys.h: 697
+ch341.c: 15479
+ch9.h: 24667
+chafsr.h: 9671
+chainiv.c: 8763
+changebit.S: 617
+ChangeLog: 18049
+CHANGELOG: 18049
+ChangeLog.1992-1997: 59954
+ChangeLog.arcmsr: 6754
+ChangeLog.history: 25741
+ChangeLog.ide-cd.1994-2004: 15586
+ChangeLog.ide-floppy.1996-2002: 4068
+ChangeLog.ide-tape.1995-2002: 16249
+ChangeLog.ips: 5304
+ChangeLog.lpfc: 89671
+ChangeLog.megaraid: 23975
+ChangeLog.megaraid_sas: 12624
+ChangeLog.ncr53c8xx: 22786
+ChangeLog.sym53c8xx: 29464
+ChangeLog.sym53c8xx_2: 6011
+Changes: 4725
+CHANGES: 4725
+chan_kern.c: 13142
+chan_kern.h: 1587
+chan_user.c: 7101
+chan_user.h: 1710
+char: 4096
+char_dev.c: 13712
+chb.c: 5975
+ch.c: 24601
+check-all.sh: 434
+check.c: 3835
+check-gas: 277
+check-gas-asm.S: 37
+check.h: 551
+checkincludes.pl: 529
+checkkconfigsymbols.sh: 1851
+checklist.c: 8211
+checklist.txt: 14315
+check-lxdialog.sh: 1653
+check-model.c: 47
+checkpatch.pl: 70566
+checkpoint.c: 21921
+checks.c: 15663
+check-segrel.lds: 203
+check-segrel.S: 45
+check-serialize.S: 41
+check.sh: 214
+check_signature.c: 599
+checkstack.pl: 5332
+checksum_32.h: 4895
+checksum_32.S: 4739
+checksum_64.h: 5349
+checksum_64.S: 6192
+checksum.c: 4610
+checksumcopy.S: 2455
+checksum.h: 6169
+checksum_mm.h: 3391
+checksum_no.h: 3074
+checksum.S: 9073
+checksyscalls.sh: 5661
+check-text-align.S: 69
+checkversion.pl: 1867
+chelsio: 4096
+cherrs.S: 15233
+chio.h: 5287
+chip.c: 15518
+chip.h: 5099
+chipram.c: 3350
+chipreg.c: 2405
+chips: 4096
+chipsfb.c: 12752
+chlist.h: 30
+chmc.c: 20666
+chmctrl.h: 8057
+chp.c: 17406
+chp.h: 1489
+chpid.h: 1040
+chrp: 4096
+chrp32_defconfig: 40160
+chrp.h: 308
+chsc.c: 22534
+chsc.h: 2470
+chsc_sch.c: 19838
+chsc_sch.h: 179
+chunk.c: 8172
+ci13xxx_udc.c: 69484
+ci13xxx_udc.h: 6227
+cia.c: 4343
+cicada.c: 3994
+cic.c: 14355
+cifs: 4096
+cif.S: 1000
+cifsacl.c: 21375
+cifsacl.h: 2405
+cifs_debug.c: 21856
+cifs_debug.h: 2329
+cifs_dfs_ref.c: 9982
+cifsencrypt.c: 12795
+cifsencrypt.h: 1256
+cifsfs.c: 31585
+cifsfs.h: 4854
+cifs_fs_sb.h: 2432
+cifsglob.h: 23265
+cifspdu.h: 81322
+cifsproto.h: 16968
+cifssmb.c: 173618
+cifs_spnego.c: 4357
+cifs_spnego.h: 1628
+cifs.txt: 2406
+cifs_unicode.c: 7190
+cifs_unicode.h: 8878
+cifs_uniupr.h: 12874
+cimax2.c: 11227
+cimax2.h: 1921
+cinergyT2-core.c: 6717
+cinergyT2-fe.c: 8456
+cinergyT2.h: 3001
+cinit.c: 59044
+cio: 4096
+cio.c: 27748
+cio_debug.h: 849
+cio.h: 4879
+cipher.c: 7783
+cipso_ipv4.c: 64343
+cipso_ipv4.h: 7616
+cipso_ipv4.txt: 2252
+circ_buf.h: 1000
+cirrusfb.c: 77607
+cirrusfb.txt: 1934
+cirrus.h: 8939
+cis: 4096
+ciscode.h: 3364
+cisreg.h: 2804
+cistpl.c: 38770
+cistpl.h: 14596
+ci.txt: 6753
+ck804xrom.c: 10948
+class: 4096
+class.c: 12662
+class_to_string.h: 1508
+class.txt: 4758
+claw.c: 115821
+claw.h: 14619
+clcd.c: 5668
+clcd.h: 6601
+cleanfile: 3492
+cleanpatch: 5132
+cleanup.c: 28008
+clearbit.S: 613
+clear_page_64.S: 969
+clear_page.S: 401
+clear_user.S: 2687
+clep7312.c: 1478
+client.c: 45575
+client.h: 7071
+clinkage.h: 27
+clip.c: 24997
+clk.c: 774
+clkdev.c: 3488
+clkdev.h: 122
+clkgen_defs_asm.h: 7393
+clkgen_defs.h: 5421
+clk.h: 4465
+clk_interface.h: 683
+clksupport.h: 844
+clk.txt: 1173
+clnt.c: 42336
+clnt.h: 5095
+clntlock.c: 6865
+clntproc.c: 21263
+clock24xx.c: 22943
+clock24xx.h: 82888
+clock34xx.c: 31225
+clock34xx.h: 84973
+clock-7x01a.c: 4770
+clock.c: 6387
+clockchips.h: 4535
+clock-cpg.c: 5839
+clock-dclk.c: 4423
+clockdomain.c: 16366
+clockdomain.h: 3231
+clockdomains.h: 9108
+clockevents.c: 6244
+clock_getres.S: 1020
+clock_gettime.S: 2948
+clock.h: 1320
+clock_imx21.c: 23302
+clock_imx27.c: 22042
+clock-imx35.c: 13016
+clocking.txt: 1582
+clocks.c: 2153
+clocks.h: 2092
+clock-sh3.c: 2204
+clock-sh4-202.c: 3818
+clock-sh4.c: 1967
+clock-sh5.c: 1848
+clock-sh7201.c: 1982
+clock-sh7203.c: 1936
+clock-sh7206.c: 1931
+clock-sh7343.c: 6574
+clock-sh7366.c: 6500
+clock-sh7619.c: 1741
+clock-sh7705.c: 2140
+clock-sh7706.c: 2055
+clock-sh7709.c: 2302
+clock-sh7710.c: 1874
+clock-sh7712.c: 1592
+clock-sh7722.c: 5683
+clock-sh7723.c: 7223
+clock-sh7724.c: 7654
+clock-sh7763.c: 2503
+clock-sh7770.c: 1749
+clock-sh7780.c: 2630
+clock-sh7785.c: 3792
+clock-sh7786.c: 3252
+clock-shx3.c: 2959
+clocks-init.c: 2524
+clocksource: 4096
+clocksource.c: 16881
+clocksource.h: 11531
+clock.txt: 2443
+clone.c: 1295
+clps7111.h: 5605
+clps711x.c: 13340
+clps711xfb.c: 10751
+cls_api.c: 14078
+cls_basic.c: 6522
+cls_cgroup.c: 6506
+cls_flow.c: 15621
+cls_fw.c: 8499
+cls_route.c: 12482
+cls_rsvp6.c: 768
+cls_rsvp.c: 761
+cls_rsvp.h: 14927
+cls_tcindex.c: 11961
+cls_u32.c: 16562
+cluster: 4096
+cluster.c: 14916
+cluster.h: 3649
+clut_vga16.ppm: 230
+cm109.c: 24186
+cm4000_cs.c: 50839
+cm4000_cs.h: 1822
+cm4040_cs.c: 17240
+cm4040_cs.h: 1435
+cm5200_defconfig: 30374
+cm5200.dts: 5770
+cm9780.h: 1648
+cma.c: 76031
+cmap_xfbdev.txt: 1927
+cm_bf527.c: 24357
+CM-BF527_defconfig: 33449
+cm_bf533.c: 10441
+CM-BF533_defconfig: 17784
+cm_bf537.c: 15825
+CM-BF537E_defconfig: 22519
+CM-BF537U_defconfig: 18739
+cm_bf548.c: 20079
+CM-BF548_defconfig: 31514
+cm_bf561.c: 11463
+CM-BF561_defconfig: 19376
+cmb.h: 2128
+cm.c: 108467
+cmd640.c: 22942
+cmd64x.c: 13588
+cmdblk.h: 1843
+cmd.c: 12040
+cmd.h: 3292
+cmdline.c: 774
+cmdlinepart.c: 9405
+cmdpkt.h: 4387
+cmdresp.c: 16004
+cmf.c: 34004
+cm.h: 3735
+CMI8330: 3229
+cmi8330.c: 22466
+cmipci.c: 104710
+CMIPCI.txt: 9357
+cmmap.c: 75502
+cmm.c: 74564
+cmm_data_2860.c: 34802
+cmm_data_2870.c: 28944
+cmm_data.c: 42
+cmm_info.c: 42
+cmm_sanity.c: 44
+cm_msgs.h: 21469
+cmmsta.c: 182636
+cmm_sync.c: 42
+cmm_wpa.c: 41
+cmode.S: 4744
+cmpdi2.c: 435
+cmp.h: 486
+cmpxchg_32.h: 9438
+cmpxchg_64.h: 4582
+cmpxchg.c: 1500
+cmpxchg-grb.h: 2053
+cmpxchg.h: 3173
+cmpxchg-irq.h: 796
+cmpxchg-llsc.h: 1420
+cmpxchg-local.h: 1369
+cm-regbits-24xx.h: 14321
+cm-regbits-34xx.h: 26454
+cm_sbs.c: 3007
+cmservice.c: 13501
+cmtdef.h: 23113
+cmt.h: 1786
+cmtp: 4096
+cmtp.h: 3099
+cmu.c: 5791
+cm-x255.c: 5295
+cm-x270.c: 7295
+cmx270_nand.c: 6063
+cm-x2xx.c: 11698
+cm_x2xx_defconfig: 48725
+cm-x2xx-pci.c: 5404
+cm-x2xx-pci.h: 460
+cm-x300.c: 11712
+cm_x300_defconfig: 39363
+cn_cifs.h: 1323
+cnic.c: 66079
+cnic_defs.h: 16086
+cnic.h: 7097
+cnic_if.h: 7270
+cnode.c: 4219
+cn_proc.c: 7019
+cn_proc.h: 3206
+cn_queue.c: 6122
+cnt32_to_63.h: 3193
+cn_test.c: 4483
+cnv_float.h: 13001
+coalesced_mmio.c: 3435
+coalesced_mmio.h: 652
+cobalt: 4096
+cobalt_btns.c: 4645
+cobalt_defconfig: 29161
+cobalt.h: 614
+cobalt_lcdfb.c: 7871
+cobra.c: 6747
+c-octeon.c: 7206
+coda: 4096
+coda_cache.h: 673
+coda_fs_i.h: 1700
+coda.h: 17708
+coda_int.h: 434
+coda_linux.c: 5125
+coda_linux.h: 2883
+coda_psdev.h: 3200
+coda.txt: 49725
+code16gcc.h: 388
+codecs: 12288
+codec.txt: 5826
+code-patching.c: 12715
+code-patching.h: 1744
+CodingStyle: 29820
+coff.h: 12413
+coh901327_wdt.c: 14160
+coid.c: 75657
+coldfire: 4096
+coldfire.h: 1492
+colibri.h: 1059
+colibri-pxa270.c: 3375
+colibri_pxa270_defconfig: 42182
+colibri-pxa300.c: 4795
+colibri_pxa300_defconfig: 27217
+colibri-pxa320.c: 4735
+colibri-pxa3xx.c: 3817
+collate.c: 3675
+collate.h: 1705
+collie.c: 7329
+collie_defconfig: 18902
+collie.h: 3779
+color.c: 4817
+color.h: 1187
+com20020.c: 10296
+com20020_cs.c: 10301
+com20020.h: 3624
+com20020-isa.c: 5185
+com20020-pci.c: 5701
+com90io.c: 11118
+com90xx.c: 18469
+comedi: 4096
+comedi_bond.c: 17048
+comedi_compat32.c: 17423
+comedi_compat32.h: 1861
+comedidev.h: 16882
+comedi_fc.c: 3317
+comedi_fc.h: 2462
+comedi_fops.c: 60764
+comedi_fops.h: 191
+comedi.h: 29999
+comedi_ksyms.c: 2407
+comedilib.h: 7998
+comedi_parport.c: 9181
+comedi_pci.h: 1605
+comedi_test.c: 14760
+command.c: 8449
+command.h: 8829
+command-list.txt: 288
+commands.c: 25775
+commands.h: 11936
+comm.c: 4950
+commctrl.c: 23882
+comminit.c: 13072
+commit.c: 32855
+commit-msg.sample: 894
+common: 4096
+common.c: 25712
+commoncap.c: 27617
+common_defconfig: 11971
+common.h: 12299
+CommonIO: 4651
+common.lds.S: 2212
+common-offsets.h: 1451
+common-pci.c: 12979
+common_perm_to_string.h: 1100
+common-smdk.c: 4474
+common-smdk.h: 451
+commproc.c: 8070
+commproc.h: 25607
+commsup.c: 51665
+CompactFlash: 1735
+compal-laptop.c: 8886
+comparator.h: 5271
+compartmentalisation.txt: 1944
+compat_audit.c: 664
+compat_binfmt_elf.c: 3496
+compat.c: 2196
+compat_exec_domain.c: 741
+compat.h: 369
+compatibility-list.txt: 1345
+compat_ioctl.c: 7581
+compat_ioctl.h: 4449
+compat_linux.c: 21127
+compat_linux.h: 7320
+compatmac.h: 332
+compat_mq.c: 4132
+compat_ptrace.h: 2572
+compat_rt_sigframe.h: 1846
+compat_signal.c: 17351
+compat_signal.h: 57
+compat-signal.h: 2664
+compat_ucontext.h: 552
+compat_wrapper.S: 48644
+compiler-gcc3.h: 823
+compiler-gcc4.h: 1407
+compiler-gcc.h: 3118
+compiler.h: 4524
+compiler-intel.h: 746
+completion.h: 3226
+composite.c: 30121
+composite.h: 14982
+compr.c: 10363
+compress.c: 1839
+compressed: 4096
+compress.h: 974
+compression.c: 18461
+compression.h: 1742
+compr.h: 3025
+compr_lzo.c: 2312
+compr_rtime.c: 2867
+compr_rubin.c: 8932
+compr_zlib.c: 5850
+computone.txt: 17570
+comstats.h: 3120
+con3215.c: 30319
+con3270.c: 15947
+concap.h: 3778
+concat.h: 454
+conditional.c: 11225
+conditional.h: 617
+conex.c: 32880
+conf.c: 11969
+confdata.c: 19322
+config: 2695
+config3270.sh: 2009
+config.c: 24033
+config.c.in: 496
+config_defs_asm.h: 5405
+config_defs.h: 4370
+configfs: 4096
+configfs_example_explicit.c: 12619
+configfs_example_macros.c: 11555
+configfs.h: 8837
+configfs_internal.h: 5080
+configfs.txt: 21464
+config.h: 1142
+config.mk: 7917
+config-osm.c: 2170
+config_roms.c: 4579
+config_roms.h: 588
+configs: 4096
+configs.c: 2818
+configuring.txt: 4139
+cong.c: 12223
+conmakehash.c: 6121
+connect.c: 38
+connection.c: 13263
+connector: 4096
+connector.c: 11327
+connector.h: 4405
+connector.txt: 6503
+conntrack.h: 810
+consistent.c: 3927
+console: 4096
+console_32.c: 2071
+console_64.c: 1575
+console.c: 7199
+console.h: 2080
+consolemap.c: 22885
+consolemap.h: 1029
+console_struct.h: 5109
+console.txt: 5814
+constants.c: 49479
+constants.h: 2939
+const.h: 596
+constraint.h: 2093
+consumer.h: 8894
+consumer.txt: 6869
+container.c: 7095
+container.h: 198
+contec_pci_dio.c: 5763
+context.c: 1678
+context.h: 3703
+context.S: 6412
+contig.c: 7791
+contregs.h: 3351
+CONTRIBUTORS: 495
+contributors.txt: 3035
+control.c: 43620
+control_compat.c: 11584
+controlfb.c: 27775
+controlfb.h: 4696
+control.h: 9928
+ControlNames.txt: 2085
+control_w.h: 1820
+cookie.c: 13149
+coprocessor.h: 5194
+coprocessor.S: 7277
+coproc.h: 362
+cops.c: 29453
+cops_ffdrv.h: 21384
+cops.h: 1400
+cops_ltdrv.h: 9491
+cops.txt: 2768
+copy_32.S: 9822
+copy.c: 2883
+copy_from_user.S: 1986
+COPYING: 18693
+COPYING.LIB: 25265
+copy_in_user.S: 1659
+copy_page_64.S: 2337
+copypage_64.S: 2011
+copypage-fa.c: 2349
+copypage-feroceon.c: 3145
+copy_page_mck.S: 5871
+copy_page.S: 532
+copypage-v3.c: 2110
+copypage-v4mc.c: 3649
+copypage-v4wb.c: 2820
+copypage-v4wt.c: 2403
+copypage-v6.c: 3822
+copypage-xsc3.c: 2840
+copypage-xscale.c: 3881
+copy.S: 1321
+copy_template.S: 5586
+copy_to_user.S: 2016
+copy_user_64.S: 5420
+copyuser_64.S: 9936
+copy_user_memcpy.S: 5245
+copy_user_nocache_64.S: 2468
+copy_user.S: 2573
+core: 4096
+core_apecs.c: 10176
+core_apecs.h: 17281
+coreb.c: 1844
+core.c: 24656
+core-card.c: 15330
+core-cdev.c: 37224
+core_cia.c: 33365
+core_cia.h: 15760
+core-device.c: 32375
+coredump.c: 5613
+core.h: 5921
+core_irongate.c: 10486
+core_irongate.h: 6754
+core-iso.c: 8971
+core_lca.c: 14096
+core_lca.h: 11596
+core_locking.txt: 4081
+core_marvel.c: 25082
+core_marvel.h: 9350
+core_mcpcia.c: 16238
+core_mcpcia.h: 11691
+core_polaris.c: 4523
+core_polaris.h: 2948
+core_priv.h: 1817
+core_t2.c: 16416
+core_t2.h: 20353
+coretemp: 1672
+coretemp.c: 11947
+core_titan.c: 20020
+core_titan.h: 11455
+core-topology.c: 15332
+core-transaction.c: 27218
+core_tsunami.c: 13148
+core_tsunami.h: 8469
+core.txt: 804
+core_wildfire.c: 17607
+core_wildfire.h: 8616
+corgi.c: 9471
+corgi_defconfig: 47365
+corgi.h: 4831
+corgikbd.c: 12406
+corgi_lcd.c: 16845
+corgi_lcd.h: 421
+corgi_pm.c: 7155
+corgi_ssp.c: 7384
+corgi_ts.c: 9648
+cosa.c: 59334
+cosa.h: 4349
+country.h: 4726
+cow.h: 1883
+cow_sys.h: 692
+cow_user.c: 12166
+coyote.h: 949
+coyote-pci.c: 1459
+coyote-setup.c: 3158
+cp1emu.c: 28793
+cp210x.c: 24866
+cp437.uni: 4426
+cp6.c: 1398
+cpc925_edac.c: 30540
+cpc.h: 16904
+cpci_hotplug_core.c: 17267
+cpci_hotplug.h: 3193
+cpci_hotplug_pci.c: 7823
+cpcihp_generic.c: 6519
+cpcihp_zt5550.c: 8619
+cpcihp_zt5550.h: 2730
+cpc_int.h: 2284
+cpcmd.c: 3087
+cpcmd.h: 1259
+cpc-usb: 4096
+cpc-usb_drv.c: 28935
+cpcusb.h: 2856
+cpfile.c: 24964
+cpfile.h: 1680
+cphy.h: 6427
+cpia2: 4096
+cpia2_core.c: 77737
+cpia2dev.h: 1919
+cpia2.h: 13192
+cpia2_overview.txt: 2357
+cpia2_registers.h: 17890
+cpia2_usb.c: 25426
+cpia2_v4l.c: 50779
+cpia.c: 114212
+cpia.h: 11791
+cpia_pp.c: 22139
+cpia_usb.c: 16084
+cp_intc.c: 3984
+cp_intc.h: 2302
+cpl5_cmd.h: 12577
+cplb.h: 4597
+cplbinfo.c: 3920
+cplbinit.c: 3353
+cplbinit.h: 2491
+cplbmgr.c: 9975
+cplb-mpu: 4096
+cplb-nompu: 4096
+cpm: 4096
+cpm1.c: 19027
+cpm1.h: 23000
+cpm2.c: 8657
+cpm2.h: 51335
+cpm2_pic.c: 7028
+cpm2_pic.h: 177
+cpmac.c: 35475
+cpm_common.c: 8715
+cpm.h: 3601
+cpm_qe: 4096
+cpm-serial.c: 5276
+cpm.txt: 2279
+cpm_uart: 4096
+cpm_uart_core.c: 34184
+cpm_uart_cpm1.c: 4062
+cpm_uart_cpm1.h: 630
+cpm_uart_cpm2.c: 4728
+cpm_uart_cpm2.h: 692
+cpm_uart.h: 3846
+cppi_dma.c: 44672
+cppi_dma.h: 3257
+cpqarray.c: 48267
+cpqarray.h: 3023
+cpqarray.txt: 2224
+cpqphp_core.c: 37277
+cpqphp_ctrl.c: 77860
+cpqphp.h: 22122
+cpqphp_nvram.c: 13820
+cpqphp_nvram.h: 1534
+cpqphp_pci.c: 39788
+cpqphp_sysfs.c: 5682
+cprecomp.h: 1072
+cpsmgr.c: 18525
+cpu: 4096
+cpu5wdt.c: 6940
+cpuacct.txt: 1941
+cpu_buffer.c: 11402
+cpu_buffer.h: 2876
+cpu-bugs64.c: 7627
+cpu.c: 5945
+cpucheck.c: 6054
+cpu-common: 4096
+cpudata_32.h: 574
+cpudata_64.h: 1041
+cpudata.h: 184
+cpu_debug.c: 17937
+cpu_debug.h: 3759
+cpu-drivers.txt: 7387
+cpufeature.h: 13827
+cpu-feature-overrides.h: 1176
+cpu-features.h: 7307
+cpu_features.txt: 2681
+cpufreq: 4096
+cpu-freq: 4096
+cpufreq_32.c: 18804
+cpufreq_64.c: 20116
+cpufreq.c: 49742
+cpufreq_conservative.c: 18026
+cpu-freq.h: 2392
+cpufreq.h: 12164
+cpufreq-nforce2.c: 9537
+cpufreq-nforce2.txt: 597
+cpufreq_ondemand.c: 19342
+cpufreq_performance.c: 1553
+cpufreq_powersave.c: 1625
+cpufreq-pxa2xx.c: 14833
+cpufreq-pxa3xx.c: 6493
+cpufreq_spudemand.c: 4449
+cpufreq_stats.c: 9729
+cpufreq-stats.txt: 5021
+cpufreq_userspace.c: 6109
+cpu.h: 1297
+cpu-h7201.c: 1510
+cpu-h7202.c: 5243
+cpu_hotplug.c: 2052
+cpu-hotplug-spec: 1155
+cpu-hotplug.txt: 14935
+cpuid.c: 5549
+cpuid.h: 617
+cpuidle: 4096
+cpuidle.c: 8664
+cpuidle.h: 1058
+cpu_imx27.c: 1739
+cpuinfo.c: 2141
+cpu-info.h: 2924
+cpuinfo.h: 2135
+cpuinfo-pvr-full.c: 2822
+cpuinfo-static.c: 4940
+cpu-irqs.h: 2654
+cpu-load.txt: 3110
+cpumap.c: 10894
+cpumap.h: 320
+cpumask.c: 4643
+cpumask.h: 381
+cpu-multi32.h: 1743
+cpu-omap.c: 4053
+cpu-probe.c: 23180
+cpu-regs.h: 14601
+cpu-sa1100.c: 7866
+cpu-sa1110.c: 9518
+cpuset.c: 72697
+cpuset.h: 4740
+cpusets.txt: 36191
+cpu_setup_44x.S: 1639
+cpu_setup_6xx.S: 11167
+cpu_setup_fsl_booke.S: 1766
+cpu_setup_pa6t.S: 1218
+cpu_setup_ppc970.S: 3704
+cpu-sh2: 4096
+cpu-sh2a: 4096
+cpu-sh3: 4096
+cpu-sh4: 4096
+cpu-sh5: 4096
+cpu-single.h: 1429
+cputable.c: 57765
+cputable.h: 19753
+cputhreads.h: 1572
+cputime.h: 118
+cputopology.txt: 3013
+cputype.h: 840
+cpu_vect.h: 1321
+cp_vers.h: 933
+cpwd.c: 16512
+cq.c: 20469
+c-qcam.c: 19812
+CQcam.txt: 7069
+cq_desc.h: 2507
+cq_enet_desc.h: 6342
+cq_exch_desc.h: 5837
+cq.h: 4130
+c-r3k.c: 8056
+c-r4k.c: 37720
+cramfs: 4096
+cramfs_fs.h: 2930
+cramfs_fs_sb.h: 343
+cramfs.txt: 2599
+crash.c: 9898
+crash_dump_32.c: 2149
+crash_dump_64.c: 1417
+crash_dump.c: 3851
+crash_dump.h: 2002
+cr_bllcd.c: 7402
+crc16.c: 2838
+crc16.h: 622
+crc32.c: 15299
+crc32c.c: 8200
+crc32c.h: 254
+crc32c-intel.c: 4975
+crc32defs.h: 1072
+crc32.h: 880
+crc32hash.c: 654
+crc7.c: 2329
+crc7.h: 272
+crc-ccitt.c: 3052
+crc-ccitt.h: 330
+crc.h: 880
+crc-itu-t.c: 2892
+crc-itu-t.h: 615
+crc-t10dif.c: 2965
+crc-t10dif.h: 140
+cred.c: 14888
+credentials.txt: 20932
+cred.h: 10905
+cred-internals.h: 559
+CREDITS: 603
+crime.c: 2833
+crime.h: 5271
+cris: 4096
+cris_defs_asm.h: 3805
+crisksyms.c: 472
+cris_supp_reg.h: 198
+crisv10.c: 129158
+crisv10.h: 4289
+crm_regs.h: 1700
+cr_pll.c: 4842
+crt0_ram.S: 2152
+crt0_rom.S: 3157
+crt0.S: 1979
+crtsavres.S: 6123
+crunch-bits.S: 8521
+crunch.c: 2001
+crw.c: 4007
+crw.h: 2025
+cryptd.c: 16974
+cryptd.h: 650
+crypto: 4096
+crypto4xx_alg.c: 8383
+crypto4xx_core.c: 34847
+crypto4xx_core.h: 5634
+crypto4xx_reg_def.h: 7646
+crypto4xx_sa.c: 2984
+crypto4xx_sa.h: 5742
+crypto.c: 16089
+crypto_compat.h: 2294
+cryptocop.c: 111134
+cryptocop.h: 7614
+crypto_des.h: 516
+crypto.h: 35141
+cryptohash.h: 264
+cryptoloop.c: 5005
+crypto_null.c: 5051
+crypto_wq.c: 896
+crypto_wq.h: 122
+crypt_s390.h: 10190
+cs4231.c: 6021
+cs4231-regs.h: 8547
+cs4236.c: 22848
+cs4236_lib.c: 35470
+cs423x: 4096
+cs4270.c: 26832
+cs4270.h: 804
+cs4281.c: 66411
+cs4362a.h: 2039
+cs4398.h: 1968
+cs461x.txt: 2184
+cs46xx: 4096
+cs46xx.c: 5301
+cs46xx_dsp_scb_types.h: 28137
+cs46xx_dsp_spos.h: 6198
+cs46xx_dsp_task_types.h: 7293
+cs46xx.h: 73681
+cs46xx_image.h: 155782
+cs46xx_lib.c: 107227
+cs46xx_lib.h: 8586
+cs5345.c: 5839
+cs5345.h: 1210
+cs53l32a.c: 5932
+cs53l32a.h: 1196
+cs5520.c: 4777
+cs5530.c: 8332
+cs5535audio: 4096
+cs5535audio.c: 10538
+cs5535audio.h: 4162
+cs5535audio_olpc.c: 4595
+cs5535audio_pcm.c: 13419
+cs5535audio_pm.c: 4043
+cs5535.c: 6298
+cs5535_gpio.c: 6000
+cs5536.c: 7861
+cs553x_nand.c: 10268
+cs8403.h: 8833
+cs8420.h: 1681
+cs8427.c: 18610
+cs8427.h: 10572
+cs89712.h: 1848
+cs89x0.c: 59868
+cs89x0.h: 16242
+cs89x0.txt: 25508
+csb337_defconfig: 28177
+csb637_defconfig: 29026
+csb701.c: 1321
+csb726.c: 7527
+csb726.h: 658
+cs.c: 21706
+cscanmgr.c: 14014
+cs.h: 6099
+cs_internal.h: 7245
+csr1212.c: 38810
+csr1212.h: 14571
+csr.c: 26430
+csrc-bcm1480.c: 1705
+csrc-ioasic.c: 1796
+csrc-octeon.c: 1473
+csrc-r4k.c: 885
+csrc-sb1250.c: 2176
+csr.h: 2690
+css.c: 26134
+css.h: 4460
+cstate.c: 4904
+cs_types.h: 934
+csum-copy_64.S: 4160
+csum_copy_from_user.S: 439
+csum_copy.S: 7018
+csum_copy_to_user.S: 431
+csumcpfruser.S: 1663
+csum_ipv6_magic.S: 2885
+csumipv6.S: 696
+csum-partial_64.c: 3530
+csum_partial_copy.c: 8909
+csum_partial_copy_generic.S: 1729
+csumpartialcopygeneric.S: 6850
+csumpartialcopy.S: 1151
+csumpartialcopyuser.S: 2378
+csum_partial.S: 16737
+csumpartial.S: 3126
+csum-wrappers_64.c: 3926
+ct20k1reg.h: 20603
+ct20k2reg.h: 3088
+ct82c710.c: 6778
+ctamixer.c: 10033
+ctamixer.h: 2795
+ctatc.c: 42227
+ctatc.h: 5058
+ctcm_dbug.c: 1932
+ctcm_dbug.h: 3316
+ctcm_fsms.c: 75291
+ctcm_fsms.h: 8271
+ctcm_main.c: 45768
+ctcm_main.h: 6938
+ctcm_mpc.c: 59919
+ctcm_mpc.h: 5324
+ctcm_sysfs.c: 5171
+ctdaio.c: 17904
+ctdaio.h: 3291
+cthardware.c: 1688
+cthardware.h: 7286
+cthw20k1.c: 49680
+cthw20k1.h: 598
+cthw20k2.c: 49114
+cthw20k2.h: 598
+ctimap.c: 2516
+ctimap.h: 1167
+ctkip.c: 17399
+ctl_unnumbered.txt: 962
+ctmixer.c: 28786
+ctmixer.h: 1691
+ctpcm.c: 11244
+ctpcm.h: 612
+ctr.c: 11026
+ctree.c: 111552
+ctree.h: 75158
+ctresource.c: 5825
+ctresource.h: 2267
+ctr.h: 524
+ctrlchar.c: 1752
+ctrlchar.h: 484
+cts.c: 10045
+ctsrc.c: 19870
+ctsrc.h: 4527
+cttimer.c: 11342
+cttimer.h: 686
+ctvmem.c: 5666
+ctvmem.h: 1787
+c-tx39.c: 10852
+ctxfi: 4096
+ctxrx.c: 147975
+ctype.c: 1041
+ctype.h: 1399
+cu3088.c: 3577
+cu3088.h: 848
+cuboot-52xx.c: 1660
+cuboot-824x.c: 1224
+cuboot-83xx.c: 1525
+cuboot-85xx.c: 1673
+cuboot-85xx-cpm2.c: 1764
+cuboot-8xx.c: 1149
+cuboot-acadia.c: 4964
+cuboot-amigaone.c: 868
+cuboot-bamboo.c: 689
+cuboot.c: 944
+cuboot-c2k.c: 4884
+cuboot-ebony.c: 782
+cuboot.h: 365
+cuboot-katmai.c: 1294
+cuboot-mpc7448hpc2.c: 1279
+cuboot-pq2.c: 7138
+cuboot-rainier.c: 1453
+cuboot-sam440ep.c: 1254
+cuboot-sequoia.c: 1453
+cuboot-taishan.c: 1361
+cuboot-warp.c: 916
+cuboot-yosemite.c: 1095
+cuda.h: 978
+cumana_1.c: 7960
+cumana_2.c: 14690
+current.h: 677
+cuse.c: 14893
+cvisionppc.h: 1577
+cvmx-address.h: 8499
+cvmx-asm.h: 4526
+cvmx-asxx-defs.h: 14223
+cvmx-bootinfo.h: 9081
+cvmx-bootmem.c: 20524
+cvmx-bootmem.h: 12982
+cvmx-ciu-defs.h: 39090
+cvmx-cmd-queue.c: 9400
+cvmx-cmd-queue.h: 18993
+cvmx-config.h: 6486
+cvmx-dbg-defs.h: 2123
+cvmx-fau.h: 19011
+cvmx-fpa.c: 4975
+cvmx-fpa-defs.h: 11938
+cvmx-fpa.h: 8284
+cvmx-gmxx-defs.h: 79305
+cvmx-gpio-defs.h: 6346
+cvmx.h: 14240
+cvmx-helper-board.c: 21950
+cvmx-helper-board.h: 6656
+cvmx-helper.c: 32770
+cvmx-helper-errata.c: 2592
+cvmx-helper-errata.h: 1277
+cvmx-helper-fpa.c: 7545
+cvmx-helper-fpa.h: 2360
+cvmx-helper.h: 7482
+cvmx-helper-jtag.c: 4203
+cvmx-helper-jtag.h: 1530
+cvmx-helper-loop.c: 2679
+cvmx-helper-loop.h: 1901
+cvmx-helper-npi.c: 3413
+cvmx-helper-npi.h: 1897
+cvmx-helper-rgmii.c: 17448
+cvmx-helper-rgmii.h: 3560
+cvmx-helper-sgmii.c: 17037
+cvmx-helper-sgmii.h: 3417
+cvmx-helper-spi.c: 6088
+cvmx-helper-spi.h: 2785
+cvmx-helper-util.c: 12555
+cvmx-helper-util.h: 6164
+cvmx-helper-xaui.c: 11997
+cvmx-helper-xaui.h: 3409
+cvmx-interrupt-decodes.c: 13959
+cvmx-interrupt-rsl.c: 3787
+cvmx-iob-defs.h: 16589
+cvmx-ipd-defs.h: 27302
+cvmx-ipd.h: 10763
+cvmx-l2c.c: 19669
+cvmx-l2c-defs.h: 23395
+cvmx-l2c.h: 10068
+cvmx-l2d-defs.h: 9721
+cvmx-l2t-defs.h: 3720
+cvmx-led-defs.h: 6731
+cvmx-mdio.h: 13085
+cvmx-mio-defs.h: 54538
+cvmx-npei-defs.h: 61528
+cvmx-npi-defs.h: 46348
+cvmx-packet.h: 1964
+cvmx-pci-defs.h: 40401
+cvmx-pcieep-defs.h: 32743
+cvmx-pciercx-defs.h: 36139
+cvmx-pcsx-defs.h: 11497
+cvmx-pcsxx-defs.h: 9562
+cvmx-pescx-defs.h: 11439
+cvmx-pexp-defs.h: 9365
+cvmx-pip-defs.h: 34993
+cvmx-pip.h: 16579
+cvmx-pko.c: 14987
+cvmx-pko-defs.h: 31966
+cvmx-pko.h: 18946
+cvmx-pow-defs.h: 19069
+cvmx-pow.h: 59817
+cvmx-scratch.h: 3875
+cvmx-smix-defs.h: 5173
+cvmx-spi.c: 22343
+cvmx-spi.h: 9516
+cvmx-spinlock.h: 6516
+cvmx-spxx-defs.h: 9687
+cvmx-srxx-defs.h: 3849
+cvmx-stxx-defs.h: 8491
+cvmx-sysinfo.c: 3570
+cvmx-sysinfo.h: 4893
+cvmx-wqe.h: 11943
+cwc4630.h: 15478
+cwcasync.h: 7969
+cwcbinhack.h: 1566
+cwcdma.asp: 4526
+cwcdma.h: 2174
+cwcsnoop.h: 1513
+cwep.c: 9662
+cwm.c: 3787
+cwm.h: 2421
+cx18: 4096
+cx18-audio.c: 2589
+cx18-audio.h: 905
+cx18-av-audio.c: 14932
+cx18-av-core.c: 41048
+cx18-av-core.h: 13381
+cx18-av-firmware.c: 7225
+cx18-av-vbi.c: 9043
+cx18-cards.c: 16165
+cx18-cards.h: 4838
+cx18-controls.c: 9435
+cx18-controls.h: 1257
+cx18-driver.c: 35409
+cx18-driver.h: 23311
+cx18-dvb.c: 9950
+cx18-dvb.h: 946
+cx18-fileops.c: 21082
+cx18-fileops.h: 1421
+cx18-firmware.c: 14987
+cx18-firmware.h: 1001
+cx18-gpio.c: 9268
+cx18-gpio.h: 1226
+cx18-i2c.c: 8776
+cx18-i2c.h: 1083
+cx18-io.c: 2807
+cx18-ioctl.c: 27533
+cx18-ioctl.h: 1423
+cx18-io.h: 4951
+cx18-irq.c: 2319
+cx18-irq.h: 1417
+cx18-mailbox.c: 21623
+cx18-mailbox.h: 3643
+cx18-queue.c: 6955
+cx18-queue.h: 2294
+cx18-scb.c: 5787
+cx18-scb.h: 7441
+cx18-streams.c: 21499
+cx18-streams.h: 1889
+cx18.txt: 811
+cx18-vbi.c: 7419
+cx18-vbi.h: 1031
+cx18-version.h: 1319
+cx18-video.c: 1080
+cx18-video.h: 875
+cx22700.c: 11029
+cx22700.h: 1488
+cx22702.c: 14516
+cx22702.h: 1673
+cx231xx: 4096
+cx231xx-audio.c: 14952
+cx231xx-avcore.c: 77231
+cx231xx-cards.c: 23257
+cx231xx-conf-reg.h: 25445
+cx231xx-core.c: 30779
+cx231xx-dvb.c: 13339
+cx231xx.h: 22018
+cx231xx-i2c.c: 12912
+cx231xx-input.c: 6423
+cx231xx-pcb-cfg.c: 20991
+cx231xx-pcb-cfg.h: 6456
+cx231xx-reg.h: 67462
+cx231xx-vbi.c: 17757
+cx231xx-vbi.h: 2261
+cx231xx-video.c: 58825
+cx23418.h: 17951
+cx2341x: 4096
+cx2341x.c: 38287
+cx2341x.h: 7383
+cx23885: 4096
+cx23885-417.c: 48981
+cx23885-cards.c: 25645
+cx23885-core.c: 54422
+cx23885-dvb.c: 26635
+cx23885.h: 16143
+cx23885-i2c.c: 9910
+cx23885-reg.h: 12999
+cx23885-vbi.c: 6762
+cx23885-video.c: 40258
+cx24110.c: 20853
+cx24110.h: 1810
+cx24113.c: 14661
+cx24113.h: 1673
+cx24116.c: 40768
+cx24116.h: 1706
+cx24123.c: 30243
+cx24123.h: 1979
+cx25840: 4096
+cx25840-audio.c: 11073
+cx25840-core.c: 45706
+cx25840-core.h: 3605
+cx25840-firmware.c: 3889
+cx25840.h: 3250
+cx25840-vbi.c: 7108
+cx88: 4096
+cx88-alsa.c: 23036
+cx88-blackbird.c: 39031
+cx88-cards.c: 89130
+cx88-core.c: 31343
+cx88-dsp.c: 8787
+cx88-dvb.c: 39095
+cx88.h: 22857
+cx88-i2c.c: 5568
+cx88-input.c: 14002
+cx88-mpeg.c: 24270
+cx88-reg.h: 34334
+cx88-tvaudio.c: 28674
+cx88-vbi.c: 6438
+cx88-video.c: 56637
+cx88-vp3054-i2c.c: 4322
+cx88-vp3054-i2c.h: 1559
+cxacru.c: 37008
+cxacru.txt: 2325
+cxgb2.c: 38481
+cxgb3: 4096
+cxgb3_ctl_defs.h: 5067
+cxgb3_defs.h: 3489
+cxgb3i: 4096
+cxgb3i_ddp.c: 20593
+cxgb3i_ddp.h: 8254
+cxgb3i.h: 4401
+cxgb3i_init.c: 3040
+cxgb3i_iscsi.c: 27545
+cxgb3_ioctl.h: 3847
+cxgb3i_offload.c: 50331
+cxgb3i_offload.h: 7277
+cxgb3i_pdu.c: 12368
+cxgb3i_pdu.h: 1710
+cxgb3i.txt: 3295
+cxgb3_main.c: 84313
+cxgb3_offload.c: 37599
+cxgb3_offload.h: 6272
+cxgb.txt: 13829
+cxio_dbg.c: 5114
+cxio_hal.c: 38094
+cxio_hal.h: 7482
+cxio_resource.c: 9188
+cxio_resource.h: 3116
+cxio_wr.h: 20826
+cxusb.c: 48202
+cxusb.h: 724
+cy82c693.c: 10524
+cyber2000fb.c: 43356
+cyber2000fb.h: 16008
+cyberjack.c: 14547
+cyclades.c: 158213
+cyclades.h: 24974
+cyclomx.h: 2544
+cyclone.c: 3125
+cyclone.h: 403
+cycx_cfm.h: 2926
+cycx_drv.c: 15649
+cycx_drv.h: 2183
+cycx_main.c: 9413
+cycx_x25.c: 45814
+cycx_x25.h: 3727
+cylinder.c: 5848
+cypress_atacb.c: 8391
+cypress_cy7c63.c: 7689
+cypress.h: 2856
+cypress_m8.c: 47071
+cypress_m8.h: 2367
+cyrix.c: 5962
+cytherm.c: 11260
+d101m_ucode.bin.ihex: 1675
+d101s_ucode.bin.ihex: 1675
+d102e_ucode.bin.ihex: 1675
+da9030_battery.c: 16256
+da9034-ts.c: 9057
+da903x_bl.c: 4988
+da903x.c: 13455
+da903x.h: 7048
+dabusb: 4096
+dabusb.c: 22084
+dabusb.h: 1873
+DAC960.c: 265715
+DAC960.h: 151800
+daca.c: 7036
+dac.h: 752
+dadapter.c: 14853
+dadapter.h: 1070
+daemon.c: 16879
+daemon.h: 8435
+daemon_kern.c: 2419
+daemon_user.c: 4368
+daisy.c: 12538
+DAI.txt: 2270
+dapm.txt: 10267
+daqboard2000.c: 25750
+darla20.c: 2889
+darla20_dsp.c: 3479
+darla24.c: 3153
+darla24_dsp.c: 3845
+dart.h: 2235
+dart_iommu.c: 10816
+das08.c: 26325
+das08_cs.c: 14893
+das08.h: 2561
+das16.c: 44853
+das16m1.c: 21006
+das1800.c: 48894
+das6402.c: 8510
+das800.c: 24454
+DASD: 3388
+dasd_3990_erp.c: 68376
+dasd_alias.c: 26580
+dasd.c: 72767
+dasd_devmap.c: 29631
+dasd_diag.c: 17829
+dasd_diag.h: 2628
+dasd_eckd.c: 95322
+dasd_eckd.h: 11295
+dasd_eer.c: 20468
+dasd_erp.c: 4866
+dasd_fba.c: 17801
+dasd_fba.h: 1816
+dasd_genhd.c: 4783
+dasd.h: 11124
+dasd_int.h: 21917
+dasd_ioctl.c: 11160
+dasd_proc.c: 9802
+data.c: 2497
+datafab.c: 20690
+datagram.c: 4449
+data-integrity.txt: 13815
+datalink.h: 482
+datapage.S: 2018
+datarate.c: 12551
+datarate.h: 2494
+datastream.c: 15931
+datastream.h: 514
+dat.c: 11476
+dat.h: 2048
+davicom.c: 4985
+davinci: 4096
+davinci_all_defconfig: 43899
+davinci.c: 13768
+davinci_emac.c: 84968
+davinci-evm.c: 6719
+davinci.h: 3430
+davinci-i2s.c: 17146
+davinci-i2s.h: 495
+davinci_nand.c: 23119
+davinci-pcm.c: 10560
+davinci-pcm.h: 783
+davinci-sffsdr.c: 4374
+davinci_wdt.c: 6690
+db1000_defconfig: 23355
+db1100_defconfig: 23611
+db1200_defconfig: 25180
+db1200.h: 6115
+db1500_defconfig: 30453
+db1550_defconfig: 26655
+db1x00: 4096
+db1x00.h: 5353
+db78x00-bp-setup.c: 2591
+db88f5281-setup.c: 9805
+db88f6281-bp-setup.c: 2227
+db9.c: 21255
+dbdma2.c: 10987
+dbdma.c: 29134
+dbdma.h: 3834
+dbell.c: 1075
+dbell.h: 1308
+dbg.c: 7231
+dbg_current.S: 402
+dbg.h: 144
+dbg_io.c: 5088
+dbg_stackcheck.S: 427
+dbg_stackkill.S: 575
+dbl_float.h: 36770
+dbox2-flash.c: 2719
+dbri.c: 80387
+dbus_contexts: 195
+dc21285.c: 5936
+dc21285-timer.c: 1332
+dc232b: 4096
+dc395x.c: 145696
+dc395x.h: 25842
+dc395x.txt: 3337
+dca: 4096
+dcache.c: 60424
+dcache.h: 2009
+dca-core.c: 6864
+dca.h: 2530
+dca-sysfs.c: 2700
+dcb: 4096
+dcbnl.c: 29597
+dcbnl.h: 11558
+dccp: 4096
+dccp.h: 15873
+dccp.txt: 7484
+dcdbas.c: 16163
+dcdbas.h: 2821
+dcdbas.txt: 3709
+dcookies.c: 6936
+dcookies.h: 1289
+dcore.c: 21748
+dcr.c: 6132
+dcr-generic.h: 1621
+dcr.h: 6308
+dcr-low.S: 983
+dcr-mmio.h: 1838
+dcr-native.h: 4500
+dcr-regs.h: 5140
+dcssblk.c: 27372
+dcu.h: 1481
+dd.c: 9413
+ddp.c: 49018
+ddr2_defs_asm.h: 11790
+ddr2_defs.h: 9835
+ddr.h: 4564
+de2104x.c: 54503
+de4x5.c: 169230
+de4x5.h: 49229
+de4x5.txt: 8677
+de600.c: 13275
+de600.h: 5588
+de620.c: 26654
+de620.h: 4970
+deadline-iosched.c: 11700
+deadline-iosched.txt: 2841
+debug-8250.S: 719
+debug.c: 7221
+debug-cmd.h: 1653
+debug-devices.c: 1975
+debugfs: 4096
+debug_fs.c: 16621
+debugfs.c: 6192
+debugfs.h: 2563
+debugfs_key.c: 9097
+debugfs_key.h: 1348
+debugfs-kmemtrace: 2769
+debugfs_netdev.c: 14895
+debugfs_netdev.h: 739
+debugfs-pktcdvd: 448
+debugfs_sta.c: 7063
+debugfs_sta.h: 427
+debugfs.txt: 7128
+debugging: 1594
+Debugging390.txt: 97243
+debugging-modules.txt: 954
+debugging-via-ohci1394.txt: 7635
+debug.h: 1195
+Debug.h: 1195
+debug_if.h: 3572
+debug-leds.c: 7120
+debug-levels.h: 1482
+debuglib.c: 4523
+debuglib.h: 11740
+debug_locks.c: 1127
+debug_locks.h: 1663
+debug-macro.S: 619
+debugobjects.c: 24352
+debugobjects.h: 2960
+debugobjects.tmpl: 14144
+debug-pagealloc.c: 2607
+debug-pl01x.S: 693
+debugport.c: 12453
+debugreg.h: 2904
+debug.S: 2650
+debug-stub.c: 6089
+debugtraps.S: 1210
+debug.txt: 5755
+dec: 4096
+dec21285.h: 5572
+dec_and_lock.c: 809
+decbin.S: 15728
+declance.c: 35842
+decl.h: 2409
+decnet: 4096
+decnet.txt: 10805
+decodecode: 1702
+decode_exc.c: 11585
+decode_rs.c: 6959
+decompress: 4096
+decompress_bunzip2.c: 23556
+decompress.c: 1126
+decompress_inflate.c: 3544
+decompress_unlzma.c: 15528
+decompress_v10.lds: 342
+decompress_v32.lds: 337
+decstation_defconfig: 17956
+dectypes.h: 427
+default_defconfig: 39014
+defBF512.h: 1271
+defBF514.h: 13406
+defBF516.h: 46402
+defBF518.h: 48358
+defBF51x_base.h: 113540
+defBF522.h: 1271
+defBF525.h: 44639
+defBF527.h: 77635
+defBF52x_base.h: 116393
+defBF532.h: 74150
+defBF534.h: 189790
+defBF537.h: 35002
+defBF539.h: 212777
+defBF542.h: 58060
+defBF544.h: 57371
+defBF547.h: 78333
+defBF548.h: 117603
+defBF549.h: 180941
+defBF54x_base.h: 251535
+defBF561.h: 109277
+defconfig: 20414
+deferred_io.txt: 3031
+defines.h: 5063
+define_trace.h: 1988
+defkeymap.c: 6243
+defkeymap.c_shipped: 11007
+defkeymap.map: 12183
+deflate.c: 5615
+deflate_syms.c: 377
+def_LPBlackfin.h: 29239
+defpalo.conf: 789
+defs.h: 12522
+deftree.c: 40510
+defutil.h: 11891
+defxx.c: 116343
+defxx.h: 54496
+delay_32.h: 882
+delay_64.h: 378
+delay-accounting.txt: 3837
+delayacct.c: 5038
+delayacct.h: 4108
+delay.c: 581
+delayed-ref.c: 24860
+delayed-ref.h: 6619
+delay.h: 1166
+delay_mm.h: 1362
+delay_no.h: 2314
+delay.S: 1279
+delay.txt: 694
+delegation.c: 14100
+delegation.h: 2261
+delkin_cb.c: 4687
+dell-laptop.c: 9519
+dell_rbu.c: 19420
+dell_rbu.txt: 4973
+dell-wmi.c: 6724
+delta.c: 23513
+delta.h: 5823
+demo_main.c: 29367
+demux.h: 8791
+denormal.c: 3335
+dentry.c: 2934
+dentry-locking.txt: 8359
+depca.c: 61475
+depca.h: 6823
+depca.txt: 1245
+desc.c: 19546
+desc_defs.h: 2385
+desc.h: 20526
+des_check_key.c: 4011
+descore-readme.txt: 17200
+description: 73
+des_generic.c: 36210
+des.h: 403
+design_notes.txt: 4626
+design.txt: 17558
+des_s390.c: 17966
+dev-audio.c: 1656
+devboards: 4096
+dev.c: 16990
+devconnect.c: 33409
+devdma.c: 2008
+devdma.h: 271
+development-process: 4096
+dev-fb.c: 1681
+devfs: 459
+dev.h: 9111
+dev-hsmmc1.c: 1647
+dev-hsmmc.c: 1640
+dev-i2c0.c: 1622
+dev-i2c1.c: 1587
+device.c: 3904
+device_cfg.h: 2924
+device_cgroup.c: 12005
+device_cgroup.h: 380
+device-drivers.tmpl: 13872
+device_fsm.c: 34071
+device.h: 31245
+device_handler: 4096
+device_id.c: 8750
+device_id.h: 9158
+device-init.c: 23541
+deviceiobook.tmpl: 11288
+device_main.c: 128828
+device-mapper: 4096
+device-mapper.h: 10956
+device_ops.c: 22502
+device_pgid.c: 16031
+devices: 4096
+devices.c: 19482
+devices.h: 918
+devices-rsk7203.c: 2587
+device_status.c: 12596
+devices.txt: 118144
+devicetable.txt: 1298
+device.txt: 6393
+devinet.c: 40642
+dev-interface: 8905
+devio.c: 45517
+dev-ioctl.c: 18582
+devmap.c: 1464
+dev_mcast.c: 5650
+devops_32.c: 1879
+devops_64.c: 1006
+devpts: 4096
+devpts_fs.h: 1454
+devpts.txt: 5085
+devres.c: 16191
+devres.txt: 7761
+devs.c: 9766
+devs.h: 1960
+dev-sysfs.c: 3852
+dev_table.c: 5563
+dev_table.h: 10908
+devtree.c: 8324
+dev-uart.c: 3521
+dev-usb.c: 1134
+dev-usb-hsotg.c: 992
+dfadd.c: 15801
+dfcmp.c: 5306
+dfdiv.c: 12636
+dfifo.h: 2160
+dfmpy.c: 11736
+dfrem.c: 8997
+dfs.c: 37
+dfs.h: 27
+dfsqrt.c: 5530
+dfsub.c: 15897
+dfu: 4096
+dfu.c: 6025
+dgram.c: 8393
+diag.c: 1739
+diag.h: 1050
+dialog.h: 6696
+dib0070.c: 13441
+dib0070.h: 1691
+dib0700_core.c: 11979
+dib0700_devices.c: 54526
+dib0700.h: 2680
+dib07x0.h: 266
+dib3000.h: 1813
+dib3000mb.c: 23970
+dib3000mb_priv.h: 16232
+dib3000mc.c: 26706
+dib3000mc.h: 2337
+dib7000m.c: 40754
+dib7000m.h: 2102
+dib7000p.c: 40757
+dib7000p.h: 2849
+dibusb-common.c: 12123
+dibusb.h: 3337
+dibusb-mb.c: 14485
+dibusb-mc.c: 5124
+dibx000_common.c: 4156
+dibx000_common.h: 2896
+di.c: 31214
+di_dbg.h: 1102
+diddfunc.c: 2703
+di_defs.h: 8217
+did_vers.h: 933
+diffconfig: 3642
+dig: 4096
+digest.c: 2574
+digi1.h: 3665
+digi_acceleport.c: 58641
+digiepca.txt: 3789
+digiFep1.h: 1928
+digiPCI.h: 1382
+digitv.c: 9329
+digitv.h: 1457
+digsy_mtc.dts: 5933
+di.h: 4386
+dilnetpc.c: 13589
+dino.c: 31503
+dio: 4096
+dio.c: 8573
+dio-driver.c: 3552
+dio.h: 11200
+dio-sysfs.c: 2250
+dir.c: 25683
+direct.c: 6527
+direct.h: 1640
+direct-io.c: 35225
+directory.c: 7736
+directory-locking: 5153
+dirent.h: 177
+dir_f.c: 10085
+dir_f.h: 1267
+dir_fplus.c: 4598
+dir_fplus.h: 1014
+dir.h: 1246
+dirhash.c: 6496
+dir-item.c: 11583
+disable-tsc-ctxt-sw-stress-test.c: 1724
+disable-tsc-on-off-stress-test.c: 1717
+disable-tsc-test.c: 2121
+dis-asm.h: 895
+disassemble.c: 19044
+disassemble.h: 1766
+dis.c: 41703
+discontig.c: 1271
+discover.c: 9895
+discover.h: 2378
+discovery.c: 12785
+discovery.h: 3572
+disk-io.c: 67604
+disk-io.h: 4751
+diskonchip.c: 50636
+disk-shock-protection.txt: 6881
+dispc.c: 37465
+dispc.h: 1183
+display: 4096
+display7seg.c: 6503
+display7seg.h: 1882
+display.c: 1573
+display_gx1.c: 6318
+display_gx1.h: 4598
+display_gx.c: 4945
+display.h: 2117
+display-sysfs.c: 6099
+diu.txt: 470
+div64.c: 3889
+div64-generic.c: 283
+div64.h: 371
+div64.S: 3866
+diva.c: 34488
+divacapi.h: 50994
+diva_didd.c: 3571
+diva_dma.c: 2843
+diva_dma.h: 1993
+diva.h: 1010
+divamnt.c: 5616
+diva_pci.h: 631
+divasfunc.c: 5512
+divasi.c: 12379
+divasmain.c: 21787
+divasproc.c: 10764
+divasync.h: 20126
+divdi3.S: 6280
+divert: 4096
+divert_init.c: 2399
+divert_procfs.c: 8581
+divide.S: 4293
+divsi3.S: 6392
+div_small.S: 1546
+div_Xsig.S: 10096
+dl2k.c: 48901
+dl2k.h: 14949
+dl2k.txt: 9387
+dlci.c: 11723
+DLINK.txt: 7386
+dlm: 4096
+dlmapi.h: 9492
+dlmast.c: 13019
+dlmcommon.h: 29686
+dlmconstants.h: 5013
+dlmconvert.c: 15590
+dlmconvert.h: 1218
+dlmdebug.c: 28761
+dlmdebug.h: 2109
+dlm_device.h: 2536
+dlmdomain.c: 49292
+dlmdomain.h: 1165
+dlmfs.c: 15242
+dlmfs.txt: 4319
+dlmfsver.c: 1211
+dlmfsver.h: 997
+dlmglue.c: 110475
+dlmglue.h: 5587
+dlm.h: 5602
+dlm_internal.h: 16633
+dlmlock.c: 19997
+dlmmaster.c: 96511
+dlm_netlink.h: 1064
+dlm_plock.h: 1135
+dlmrecovery.c: 85132
+dlmthread.c: 21013
+dlmunlock.c: 19250
+dlmver.c: 1203
+dlmver.h: 991
+dm1105: 4096
+dm1105.c: 22399
+dm355.c: 17263
+dm355evm_keys.c: 8928
+dm355evm_msp.c: 10717
+dm355evm_msp.h: 2879
+dm355.h: 586
+dm644x.c: 14946
+dm644x.h: 1316
+dm646x.c: 15438
+dm646x.h: 757
+dm9000.c: 34291
+dm9000.h: 4167
+dm9000.txt: 5137
+dm9601.c: 15941
+dma: 4096
+dma-alloc.c: 4571
+dma-api.c: 9315
+DMA-API.txt: 28886
+DMA-attributes.txt: 1376
+dma-attrs.h: 1758
+dmabounce.c: 14340
+dmabrg.c: 5279
+dmabrg.h: 497
+dmabuf.c: 35686
+dma.c: 6328
+dmac.c: 4805
+dmac.h: 11517
+dma-coherence.h: 1492
+dma-coherent.c: 3667
+dma-coherent.h: 891
+dmacopy.c: 909
+dma-core.h: 639
+dmactl-regs.h: 4663
+dma-debug.c: 31988
+dma-debug.h: 4915
+dma-default.c: 8575
+dma_defs_asm.h: 14594
+dma_defs.h: 14110
+dmaengine.c: 28040
+dmaengine.h: 15831
+dmaengine.txt: 42
+dma-g2.c: 4776
+dma.h: 2547
+dma-iommu.c: 3128
+dma-isa.c: 5163
+DMA-ISA-LPC.txt: 5333
+dma_lib.c: 16254
+dma-m2p.c: 9922
+dma-mapping-broken.h: 2488
+dma-mapping.c: 5434
+dma-mapping-common.h: 5646
+dma-mapping.h: 4368
+DMA-mapping.txt: 27929
+dma_mm.h: 455
+dma-mx1-mx2.c: 23615
+dma-mx1-mx2.h: 2711
+dma_no.h: 16955
+dma-noncoherent.c: 10156
+dma-octeon.c: 10532
+dma-plat.h: 2025
+dmapool.c: 13226
+dmapool.h: 923
+dma-pvr2.c: 2423
+dmar.c: 31385
+dma_remapping.h: 988
+dmar.h: 6143
+dmascc.c: 37724
+dma-sh4a.h: 2469
+dma-sh7760.c: 10216
+dma-sh.c: 7913
+dma-sh.h: 2862
+dmasound: 4096
+dmasound_atari.c: 42606
+dmasound_core.c: 44456
+dmasound.h: 8131
+dmasound_paula.c: 19160
+dmasound_q40.c: 14354
+dma-swiotlb.c: 4748
+dma-sysfs.c: 4348
+dmatest.c: 15129
+dma_timer.c: 2282
+dma.txt: 6352
+DMA.txt: 6352
+dma_v.h: 1177
+dm-bio-record.h: 1629
+dm.c: 60780
+dm-crypt.c: 32243
+dm-crypt.txt: 1485
+dm-delay.c: 8567
+dm-dirty-log.h: 3944
+dme1737: 11748
+dme1737.c: 74868
+dmesg.c: 1657
+dm-exception-store.c: 6745
+dm-exception-store.h: 4659
+dmfe.c: 60351
+dmfe.txt: 2168
+dm.h: 3933
+dmi.h: 411
+dmi-id.c: 6586
+dm-io.c: 11366
+dm-ioctl.c: 33212
+dm-ioctl.h: 9109
+dm-io.h: 2051
+dm-io.txt: 3298
+dmi_scan.c: 15130
+dm-kcopyd.c: 14244
+dm-kcopyd.h: 1335
+dm-linear.c: 3614
+dm-log.c: 19367
+dm-log.txt: 2397
+dm-log-userspace-base.c: 16497
+dm-log-userspace.h: 12944
+dm-log-userspace-transfer.c: 6936
+dm-log-userspace-transfer.h: 452
+dmm32at.c: 30666
+dm-mpath.c: 37626
+dm-mpath.h: 415
+dm-path-selector.c: 2473
+dm-path-selector.h: 2533
+dm-queue-length.c: 5495
+dm-queue-length.txt: 1218
+dm-raid1.c: 32159
+dm-region-hash.c: 18043
+dm-region-hash.h: 3217
+dm-round-robin.c: 4673
+dm-service-time.c: 8360
+dm-service-time.txt: 3243
+dm-snap.c: 33940
+dm-snap-persistent.c: 17550
+dm-snap-transient.c: 3687
+dm-stripe.c: 8013
+dm-sysfs.c: 2204
+dm-table.c: 27836
+dm-target.c: 2600
+dmtimer.c: 22824
+dmtimer.h: 3450
+dm-uevent.c: 5482
+dm-uevent.h: 1678
+dm-uevent.txt: 2650
+dmv182.c: 3833
+dmx3191d.c: 4547
+dmxdev.c: 28366
+dmxdev.h: 2409
+dmx.h: 3848
+dm-zero.c: 1495
+dn_dev.c: 33824
+dn_dev.h: 5493
+dnet.c: 25715
+dnet.h: 7221
+dnfb.c: 8139
+dn_fib.c: 18396
+dn_fib.h: 4894
+dn.h: 4527
+dn_ints.c: 1049
+dn_neigh.c: 15875
+dn_neigh.h: 824
+dn_nsp.h: 6229
+dn_nsp_in.c: 21566
+dn_nsp_out.c: 17942
+dnode.c: 30235
+dnotify: 4096
+dnotify.c: 12385
+dnotify.h: 978
+dnotify.txt: 3565
+dn_route.c: 44768
+dn_route.h: 4165
+dn_rtmsg.c: 3778
+dn_rules.c: 5755
+dns323-setup.c: 10831
+dns_resolve.c: 3678
+dns_resolve.h: 1294
+dn_table.c: 20482
+dn_timer.c: 3167
+do_balan.c: 58466
+doc2000.c: 32664
+doc2000.h: 5486
+doc2001.c: 25107
+doc2001plus.c: 31825
+DocBook: 4096
+docecc.c: 15968
+dock.c: 31358
+docprobe.c: 10340
+docproc.c: 11716
+do_csum.S: 2991
+Documentation: 4096
+do_func.S: 13813
+domain.c: 29471
+domain.h: 2107
+do_mounts.c: 9465
+do_mounts.h: 1396
+do_mounts_initrd.c: 3256
+do_mounts_md.c: 8071
+do_mounts_rd.c: 8166
+donauboe.c: 48207
+donauboe.h: 14213
+dontdiff: 1964
+doorbell.c: 2900
+doorbell.h: 2894
+dot11d.c: 5434
+dot11d.h: 2916
+dot_command.c: 4062
+dot_command.h: 2259
+dot.gdbinit: 5372
+dot.gdbinit_200MHz_16MB: 5763
+dot.gdbinit_300MHz_32MB: 5763
+dot.gdbinit_400MHz_32MB: 5764
+dot.gdbinit.nommu: 3891
+dot.gdbinit.smp: 8938
+dot.gdbinit.vdec2: 5442
+do_timer.h: 349
+double_cpdo.c: 4122
+doublefault_32.c: 1732
+double.h: 6303
+down2.H16: 33610
+down3.bin.ihex: 35892
+down.H16: 36678
+dp_add.c: 4700
+dpc.c: 58689
+dpc.h: 1771
+dp_cmp.c: 1842
+dpcsup.c: 9553
+dp_div.c: 4369
+dp_fint.c: 1932
+dp_flong.c: 1912
+dp_frexp.c: 1450
+dp_fsp.c: 1879
+dp_logb.c: 1446
+dpmc.c: 3024
+dpmc.h: 1247
+dpmc_modes.S: 14539
+dp_modf.c: 2064
+dp_mul.c: 4812
+dp_scalb.c: 1508
+dp_simple.c: 2057
+dp_sqrt.c: 4372
+dp_sub.c: 4977
+dpt: 4096
+dpt_i2o.c: 96500
+dpti.h: 11787
+dpti_i2o.h: 13449
+dpti_ioctl.h: 5365
+dp_tint.c: 3021
+dpti.txt: 3614
+dp_tlong.c: 3061
+dptsig.h: 14986
+dqblk_qtree.h: 2108
+dqblk_v1.h: 342
+dqblk_v2.h: 367
+dqblk_xfs.h: 6607
+dqueue.c: 2180
+dqueue.h: 1007
+dquot.c: 72530
+draft-ietf-cipso-ipsecurity-01.txt: 28638
+dram_init.S: 3913
+draw_functrace.py: 3560
+dreamcast_defconfig: 25856
+driver: 11995
+driver.c: 54722
+driver-changes.txt: 4022
+driver_chipcommon.c: 13262
+driver_chipcommon_pmu.c: 17510
+driver_extif.c: 3829
+driver_gige.c: 7415
+driver.h: 4405
+driver_mipscore.c: 6999
+driver-model: 4096
+driver-model.txt: 10127
+driver-ops.h: 5035
+driver_pcicore.c: 16619
+drivers: 4096
+drivers.c: 22567
+drivers-testing.txt: 2173
+driver.txt: 8049
+drm: 4096
+drm_agpsupport.c: 13149
+drm_auth.c: 5698
+drm_bufs.c: 44350
+drm_cache.c: 2119
+drm_context.c: 11821
+drm_core.h: 1468
+drm_crtc.c: 63999
+drm_crtc.h: 25347
+drm_crtc_helper.c: 29207
+drm_crtc_helper.h: 4951
+drm_debugfs.c: 6275
+drm_dma.c: 4147
+drm_drawable.c: 5166
+drm_drv.c: 16709
+drm_edid.c: 24730
+drm_edid.h: 5722
+drm_fops.c: 14460
+drm_gem.c: 14905
+drm.h: 23018
+drm_hashtab.c: 5440
+drm_hashtab.h: 2589
+drm_info.c: 9606
+drm_ioc32.c: 33632
+drm_ioctl.c: 9402
+drm_irq.c: 17068
+drm_lock.c: 11107
+drm_memory.c: 5012
+drm_memory.h: 1936
+drm_mm.c: 9213
+drm_mm.h: 3301
+drm_mode.h: 6855
+drm_modes.c: 14802
+drm_os_linux.h: 4047
+drm_pci.c: 3839
+drm_pciids.h: 40171
+drmP.h: 49714
+drm_proc.c: 6339
+drm_sarea.h: 2655
+drm_scatter.c: 5431
+drm_sman.c: 8782
+drm_sman.h: 5975
+drm_stub.c: 13017
+drm_sysfs.c: 13561
+drm_vm.c: 18670
+drop_caches.c: 1684
+drop_monitor.c: 8677
+drp-avail.c: 8822
+drp.c: 24684
+drp-ie.c: 9743
+drv.c: 22503
+drvfbi.c: 12647
+drx397xD.c: 30335
+drx397xD_fw.h: 1525
+drx397xD.h: 3015
+ds1286.h: 1223
+ds1287.h: 1019
+ds1302.c: 7604
+ds1305.h: 1068
+ds1603.c: 3162
+ds1603.h: 566
+ds1620.c: 8486
+ds1621: 2670
+ds1621.c: 9932
+ds1682.c: 7160
+ds17287rtc.h: 2676
+ds1_ctrl.fw.ihex: 33804
+ds1_dsp.fw.ihex: 364
+ds1e_ctrl.fw.ihex: 33804
+ds1wm.c: 11691
+ds1wm.h: 114
+ds2482: 743
+ds2482.c: 14041
+ds2490: 3576
+ds2490.c: 24260
+ds2760_battery.c: 13485
+dsa: 4096
+dsa.c: 9418
+dsa.h: 1617
+dsa_priv.h: 4060
+dsbr100.c: 17605
+ds.c: 38706
+dsc.c: 1195
+dscc4.c: 55052
+dsdt-override.txt: 247
+dsemul.c: 4501
+dsfield.c: 19154
+dsfield.h: 1127
+ds.h: 8003
+dsinit.c: 6745
+dsmethod.c: 19169
+dsmg600.h: 1216
+dsmg600-pci.c: 1898
+dsmg600-setup.c: 7030
+dsmthdat.c: 21585
+dsobject.c: 23900
+dsopcode.c: 39305
+dsp56k: 4096
+dsp56k.c: 12404
+dsp56k.h: 1269
+dsp_audio.c: 11056
+dsp_biquad.h: 1622
+dsp_blowfish.c: 23738
+dspbootcode.bin.ihex: 36048
+dsp_cmx.c: 52826
+dsp_common.h: 1309
+dsp_core.c: 33551
+dsp_defs.h: 12149
+dspdids.h: 2695
+dsp_dtmf.c: 7570
+dsp_ecdis.h: 3527
+dsp.h: 7893
+dsp_hwec.c: 3049
+dsp_hwec.h: 242
+dsp_pipeline.c: 8348
+dsp_spos.c: 55617
+dsp_spos.h: 7631
+dsp_spos_scb_lib.c: 49424
+dsp_tones.c: 17264
+dsp_tst.h: 1486
+dsrv4bri.h: 1644
+dsrv_bri.h: 1178
+dsrv_pri.h: 1247
+ds_selftest.c: 9393
+ds_selftest.h: 363
+dst: 4096
+dst.c: 49967
+dst_ca.c: 21634
+dst_ca.h: 1591
+dst_common.h: 4277
+dst.h: 14556
+dst_priv.h: 598
+dstr.c: 5143
+dsutils.c: 25450
+dswexec.c: 19731
+dswload.c: 31066
+dswscope.c: 6750
+dswstate.c: 21428
+dt019x.c: 9062
+dt2801.c: 15251
+dt2811.c: 14680
+dt2814.c: 8589
+dt2815.c: 7337
+dt2817.c: 4517
+dt282x.c: 35248
+dt3000.c: 23494
+dt9812.c: 28480
+dtc: 4096
+dt.c: 16907
+dtc2278.c: 3867
+dtc3x80.txt: 1952
+dtc.c: 13348
+dtc.h: 2644
+dtc-lexer.l: 6979
+dtc-lexer.lex.c_shipped: 57920
+dtc-parser.tab.c_shipped: 55472
+dtc-parser.tab.h_shipped: 3233
+dtc-parser.y: 6540
+dtc-src: 4096
+dtl1_cs.c: 14325
+dtlb_miss.S: 747
+dtlb_prot.S: 1267
+dtl.c: 6493
+dtlk.c: 16644
+dtlk.h: 3768
+dts: 4096
+dts-bindings: 4096
+dtt200u.c: 9848
+dtt200u-fe.c: 5300
+dtt200u.h: 1606
+dtv5100.c: 5845
+dtv5100.h: 1534
+dum.h: 7581
+dummy.c: 3850
+dummycon.c: 1805
+dummy_hcd.c: 50629
+dump_pagetables.c: 8879
+dumprequest.c: 3343
+dumprequest.h: 2117
+dumpstack_32.c: 3323
+dumpstack_64.c: 7276
+dump_stack.c: 290
+dumpstack.c: 8067
+dumpstack.h: 1003
+dump_tlb.c: 2643
+dv1394: 390
+dv1394.c: 74226
+dv1394.h: 10409
+dv1394-private.h: 17471
+dvb: 4096
+dvb-bt8xx.c: 28096
+dvb-bt8xx.h: 1816
+dvb_ca_en50221.c: 45737
+dvb_ca_en50221.h: 4082
+dvb-core: 4096
+dvb_demux.c: 30507
+dvb_demux.h: 3579
+dvbdev.c: 11761
+dvbdev.h: 4065
+dvb_dummy_fe.c: 7049
+dvb_dummy_fe.h: 1698
+dvb_filter.c: 12922
+dvb_filter.h: 6064
+dvb_frontend.c: 54720
+dvb_frontend.h: 11618
+dvb_math.c: 5423
+dvb_math.h: 1974
+dvb_net.c: 42892
+dvb_net.h: 1379
+dvb-pll.c: 17175
+dvb-pll.h: 1617
+dvb_ringbuffer.c: 7188
+dvb_ringbuffer.h: 6340
+dvb-ttusb-budget.c: 44105
+dvb-usb: 12288
+dvb-usb-common.h: 2150
+dvb-usb-dvb.c: 5850
+dvb-usb-firmware.c: 3956
+dvb-usb.h: 12194
+dvb-usb-i2c.c: 1061
+dvb-usb-ids.h: 11042
+dvb-usb-init.c: 8477
+dvb-usb-remote.c: 5519
+dvb-usb-urb.c: 2585
+dvi.c: 18260
+dvi.h: 2461
+dvma.c: 1265
+dvma.h: 9864
+dvo_ch7017.c: 14698
+dvo_ch7xxx.c: 9179
+dvo.h: 4860
+dvo_ivch.c: 10628
+dvo_sil164.c: 7534
+dvo_tfp410.c: 8848
+dw2102.c: 27129
+dw2102.h: 240
+dwarf2.h: 2393
+dw_dmac.c: 37849
+dw_dmac.h: 3078
+dw_dmac_regs.h: 6137
+dynamic_debug.c: 18386
+dynamic_debug.h: 2666
+dynamic-debug-howto.txt: 8633
+dyn.lds.S: 5092
+dz.c: 23133
+dz.h: 5439
+e00154b8e949bf4b89ac198aef9a247532ac2d: 297
+e100: 4096
+e1000: 4096
+e1000_82575.c: 42179
+e1000_82575.h: 8176
+e1000_defines.h: 28796
+e1000e: 4096
+e1000_ethtool.c: 56605
diff --git a/qdict.c b/qdict.c
new file mode 100644
index 0000000..dee0fb4
--- /dev/null
+++ b/qdict.c
@@ -0,0 +1,456 @@
+/*
+ * QDict Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qint.h"
+#include "qfloat.h"
+#include "qdict.h"
+#include "qbool.h"
+#include "qstring.h"
+#include "qobject.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+
+static void qdict_destroy_obj(QObject *obj);
+
+static const QType qdict_type = {
+ .code = QTYPE_QDICT,
+ .destroy = qdict_destroy_obj,
+};
+
+/**
+ * qdict_new(): Create a new QDict
+ *
+ * Return strong reference.
+ */
+QDict *qdict_new(void)
+{
+ QDict *qdict;
+
+ qdict = qemu_mallocz(sizeof(*qdict));
+ QOBJECT_INIT(qdict, &qdict_type);
+
+ return qdict;
+}
+
+/**
+ * qobject_to_qdict(): Convert a QObject into a QDict
+ */
+QDict *qobject_to_qdict(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QDICT)
+ return NULL;
+
+ return container_of(obj, QDict, base);
+}
+
+/**
+ * tdb_hash(): based on the hash agorithm from gdbm, via tdb
+ * (from module-init-tools)
+ */
+static unsigned int tdb_hash(const char *name)
+{
+ unsigned value; /* Used to compute the hash value. */
+ unsigned i; /* Used to cycle through random values. */
+
+ /* Set the initial value from the key size. */
+ for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++)
+ value = (value + (((const unsigned char *)name)[i] << (i*5 % 24)));
+
+ return (1103515243 * value + 12345);
+}
+
+/**
+ * alloc_entry(): allocate a new QDictEntry
+ */
+static QDictEntry *alloc_entry(const char *key, QObject *value)
+{
+ QDictEntry *entry;
+
+ entry = qemu_mallocz(sizeof(*entry));
+ entry->key = qemu_strdup(key);
+ entry->value = value;
+
+ return entry;
+}
+
+/**
+ * qdict_entry_value(): Return qdict entry value
+ *
+ * Return weak reference.
+ */
+QObject *qdict_entry_value(const QDictEntry *entry)
+{
+ return entry->value;
+}
+
+/**
+ * qdict_entry_key(): Return qdict entry key
+ *
+ * Return a *pointer* to the string, it has to be duplicated before being
+ * stored.
+ */
+const char *qdict_entry_key(const QDictEntry *entry)
+{
+ return entry->key;
+}
+
+/**
+ * qdict_find(): List lookup function
+ */
+static QDictEntry *qdict_find(const QDict *qdict,
+ const char *key, unsigned int bucket)
+{
+ QDictEntry *entry;
+
+ QLIST_FOREACH(entry, &qdict->table[bucket], next)
+ if (!strcmp(entry->key, key))
+ return entry;
+
+ return NULL;
+}
+
+/**
+ * qdict_put_obj(): Put a new QObject into the dictionary
+ *
+ * Insert the pair 'key:value' into 'qdict', if 'key' already exists
+ * its 'value' will be replaced.
+ *
+ * This is done by freeing the reference to the stored QObject and
+ * storing the new one in the same entry.
+ *
+ * NOTE: ownership of 'value' is transferred to the QDict
+ */
+void qdict_put_obj(QDict *qdict, const char *key, QObject *value)
+{
+ unsigned int bucket;
+ QDictEntry *entry;
+
+ bucket = tdb_hash(key) % QDICT_BUCKET_MAX;
+ entry = qdict_find(qdict, key, bucket);
+ if (entry) {
+ /* replace key's value */
+ qobject_decref(entry->value);
+ entry->value = value;
+ } else {
+ /* allocate a new entry */
+ entry = alloc_entry(key, value);
+ QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next);
+ qdict->size++;
+ }
+}
+
+/**
+ * qdict_get(): Lookup for a given 'key'
+ *
+ * Return a weak reference to the QObject associated with 'key' if
+ * 'key' is present in the dictionary, NULL otherwise.
+ */
+QObject *qdict_get(const QDict *qdict, const char *key)
+{
+ QDictEntry *entry;
+
+ entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX);
+ return (entry == NULL ? NULL : entry->value);
+}
+
+/**
+ * qdict_haskey(): Check if 'key' exists
+ *
+ * Return 1 if 'key' exists in the dict, 0 otherwise
+ */
+int qdict_haskey(const QDict *qdict, const char *key)
+{
+ unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX;
+ return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1);
+}
+
+/**
+ * qdict_size(): Return the size of the dictionary
+ */
+size_t qdict_size(const QDict *qdict)
+{
+ return qdict->size;
+}
+
+/**
+ * qdict_get_obj(): Get a QObject of a specific type
+ */
+static QObject *qdict_get_obj(const QDict *qdict, const char *key,
+ qtype_code type)
+{
+ QObject *obj;
+
+ obj = qdict_get(qdict, key);
+ assert(obj != NULL);
+ assert(qobject_type(obj) == type);
+
+ return obj;
+}
+
+/**
+ * qdict_get_double(): Get an number mapped by 'key'
+ *
+ * This function assumes that 'key' exists and it stores a
+ * QFloat or QInt object.
+ *
+ * Return number mapped by 'key'.
+ */
+double qdict_get_double(const QDict *qdict, const char *key)
+{
+ QObject *obj = qdict_get(qdict, key);
+
+ assert(obj);
+ switch (qobject_type(obj)) {
+ case QTYPE_QFLOAT:
+ return qfloat_get_double(qobject_to_qfloat(obj));
+ case QTYPE_QINT:
+ return qint_get_int(qobject_to_qint(obj));
+ default:
+ abort();
+ }
+}
+
+/**
+ * qdict_get_int(): Get an integer mapped by 'key'
+ *
+ * This function assumes that 'key' exists and it stores a
+ * QInt object.
+ *
+ * Return integer mapped by 'key'.
+ */
+int64_t qdict_get_int(const QDict *qdict, const char *key)
+{
+ QObject *obj = qdict_get_obj(qdict, key, QTYPE_QINT);
+ return qint_get_int(qobject_to_qint(obj));
+}
+
+/**
+ * qdict_get_bool(): Get a bool mapped by 'key'
+ *
+ * This function assumes that 'key' exists and it stores a
+ * QBool object.
+ *
+ * Return bool mapped by 'key'.
+ */
+int qdict_get_bool(const QDict *qdict, const char *key)
+{
+ QObject *obj = qdict_get_obj(qdict, key, QTYPE_QBOOL);
+ return qbool_get_int(qobject_to_qbool(obj));
+}
+
+/**
+ * qdict_get_qlist(): Get the QList mapped by 'key'
+ *
+ * This function assumes that 'key' exists and it stores a
+ * QList object.
+ *
+ * Return QList mapped by 'key'.
+ */
+QList *qdict_get_qlist(const QDict *qdict, const char *key)
+{
+ return qobject_to_qlist(qdict_get_obj(qdict, key, QTYPE_QLIST));
+}
+
+/**
+ * qdict_get_qdict(): Get the QDict mapped by 'key'
+ *
+ * This function assumes that 'key' exists and it stores a
+ * QDict object.
+ *
+ * Return QDict mapped by 'key'.
+ */
+QDict *qdict_get_qdict(const QDict *qdict, const char *key)
+{
+ return qobject_to_qdict(qdict_get_obj(qdict, key, QTYPE_QDICT));
+}
+
+/**
+ * qdict_get_str(): Get a pointer to the stored string mapped
+ * by 'key'
+ *
+ * This function assumes that 'key' exists and it stores a
+ * QString object.
+ *
+ * Return pointer to the string mapped by 'key'.
+ */
+const char *qdict_get_str(const QDict *qdict, const char *key)
+{
+ QObject *obj = qdict_get_obj(qdict, key, QTYPE_QSTRING);
+ return qstring_get_str(qobject_to_qstring(obj));
+}
+
+/**
+ * qdict_get_try_int(): Try to get integer mapped by 'key'
+ *
+ * Return integer mapped by 'key', if it is not present in
+ * the dictionary or if the stored object is not of QInt type
+ * 'def_value' will be returned.
+ */
+int64_t qdict_get_try_int(const QDict *qdict, const char *key,
+ int64_t def_value)
+{
+ QObject *obj;
+
+ obj = qdict_get(qdict, key);
+ if (!obj || qobject_type(obj) != QTYPE_QINT)
+ return def_value;
+
+ return qint_get_int(qobject_to_qint(obj));
+}
+
+/**
+ * qdict_get_try_bool(): Try to get a bool mapped by 'key'
+ *
+ * Return bool mapped by 'key', if it is not present in the
+ * dictionary or if the stored object is not of QBool type
+ * 'def_value' will be returned.
+ */
+int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value)
+{
+ QObject *obj;
+
+ obj = qdict_get(qdict, key);
+ if (!obj || qobject_type(obj) != QTYPE_QBOOL)
+ return def_value;
+
+ return qbool_get_int(qobject_to_qbool(obj));
+}
+
+/**
+ * qdict_get_try_str(): Try to get a pointer to the stored string
+ * mapped by 'key'
+ *
+ * Return a pointer to the string mapped by 'key', if it is not present
+ * in the dictionary or if the stored object is not of QString type
+ * NULL will be returned.
+ */
+const char *qdict_get_try_str(const QDict *qdict, const char *key)
+{
+ QObject *obj;
+
+ obj = qdict_get(qdict, key);
+ if (!obj || qobject_type(obj) != QTYPE_QSTRING)
+ return NULL;
+
+ return qstring_get_str(qobject_to_qstring(obj));
+}
+
+/**
+ * qdict_iter(): Iterate over all the dictionary's stored values.
+ *
+ * This function allows the user to provide an iterator, which will be
+ * called for each stored value in the dictionary.
+ */
+void qdict_iter(const QDict *qdict,
+ void (*iter)(const char *key, QObject *obj, void *opaque),
+ void *opaque)
+{
+ int i;
+ QDictEntry *entry;
+
+ for (i = 0; i < QDICT_BUCKET_MAX; i++) {
+ QLIST_FOREACH(entry, &qdict->table[i], next)
+ iter(entry->key, entry->value, opaque);
+ }
+}
+
+static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket)
+{
+ int i;
+
+ for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) {
+ if (!QLIST_EMPTY(&qdict->table[i])) {
+ return QLIST_FIRST(&qdict->table[i]);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * qdict_first(): Return first qdict entry for iteration.
+ */
+const QDictEntry *qdict_first(const QDict *qdict)
+{
+ return qdict_next_entry(qdict, 0);
+}
+
+/**
+ * qdict_next(): Return next qdict entry in an iteration.
+ */
+const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry)
+{
+ QDictEntry *ret;
+
+ ret = QLIST_NEXT(entry, next);
+ if (!ret) {
+ unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX;
+ ret = qdict_next_entry(qdict, bucket + 1);
+ }
+
+ return ret;
+}
+
+/**
+ * qentry_destroy(): Free all the memory allocated by a QDictEntry
+ */
+static void qentry_destroy(QDictEntry *e)
+{
+ assert(e != NULL);
+ assert(e->key != NULL);
+ assert(e->value != NULL);
+
+ qobject_decref(e->value);
+ qemu_free(e->key);
+ qemu_free(e);
+}
+
+/**
+ * qdict_del(): Delete a 'key:value' pair from the dictionary
+ *
+ * This will destroy all data allocated by this entry.
+ */
+void qdict_del(QDict *qdict, const char *key)
+{
+ QDictEntry *entry;
+
+ entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX);
+ if (entry) {
+ QLIST_REMOVE(entry, next);
+ qentry_destroy(entry);
+ qdict->size--;
+ }
+}
+
+/**
+ * qdict_destroy_obj(): Free all the memory allocated by a QDict
+ */
+static void qdict_destroy_obj(QObject *obj)
+{
+ int i;
+ QDict *qdict;
+
+ assert(obj != NULL);
+ qdict = qobject_to_qdict(obj);
+
+ for (i = 0; i < QDICT_BUCKET_MAX; i++) {
+ QDictEntry *entry = QLIST_FIRST(&qdict->table[i]);
+ while (entry) {
+ QDictEntry *tmp = QLIST_NEXT(entry, next);
+ QLIST_REMOVE(entry, next);
+ qentry_destroy(entry);
+ entry = tmp;
+ }
+ }
+
+ qemu_free(qdict);
+}
diff --git a/qdict.h b/qdict.h
new file mode 100644
index 0000000..929d8d2
--- /dev/null
+++ b/qdict.h
@@ -0,0 +1,67 @@
+/*
+ * QDict Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef QDICT_H
+#define QDICT_H
+
+#include "qobject.h"
+#include "qlist.h"
+#include "qemu-queue.h"
+#include <stdint.h>
+
+#define QDICT_BUCKET_MAX 512
+
+typedef struct QDictEntry {
+ char *key;
+ QObject *value;
+ QLIST_ENTRY(QDictEntry) next;
+} QDictEntry;
+
+typedef struct QDict {
+ QObject_HEAD;
+ size_t size;
+ QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX];
+} QDict;
+
+/* Object API */
+QDict *qdict_new(void);
+const char *qdict_entry_key(const QDictEntry *entry);
+QObject *qdict_entry_value(const QDictEntry *entry);
+size_t qdict_size(const QDict *qdict);
+void qdict_put_obj(QDict *qdict, const char *key, QObject *value);
+void qdict_del(QDict *qdict, const char *key);
+int qdict_haskey(const QDict *qdict, const char *key);
+QObject *qdict_get(const QDict *qdict, const char *key);
+QDict *qobject_to_qdict(const QObject *obj);
+void qdict_iter(const QDict *qdict,
+ void (*iter)(const char *key, QObject *obj, void *opaque),
+ void *opaque);
+const QDictEntry *qdict_first(const QDict *qdict);
+const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry);
+
+/* Helper to qdict_put_obj(), accepts any object */
+#define qdict_put(qdict, key, obj) \
+ qdict_put_obj(qdict, key, QOBJECT(obj))
+
+/* High level helpers */
+double qdict_get_double(const QDict *qdict, const char *key);
+int64_t qdict_get_int(const QDict *qdict, const char *key);
+int qdict_get_bool(const QDict *qdict, const char *key);
+QList *qdict_get_qlist(const QDict *qdict, const char *key);
+QDict *qdict_get_qdict(const QDict *qdict, const char *key);
+const char *qdict_get_str(const QDict *qdict, const char *key);
+int64_t qdict_get_try_int(const QDict *qdict, const char *key,
+ int64_t def_value);
+int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value);
+const char *qdict_get_try_str(const QDict *qdict, const char *key);
+
+#endif /* QDICT_H */
diff --git a/qemu-aio.h b/qemu-aio.h
new file mode 100644
index 0000000..3bdd749
--- /dev/null
+++ b/qemu-aio.h
@@ -0,0 +1,59 @@
+/*
+ * QEMU aio implementation
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_AIO_H
+#define QEMU_AIO_H
+
+#include "qemu-common.h"
+#include "qemu-char.h"
+
+/* Returns 1 if there are still outstanding AIO requests; 0 otherwise */
+typedef int (AioFlushHandler)(void *opaque);
+
+/* Runs all currently allowed AIO callbacks of completed requests in the
+ * respective AIO backend. Returns 0 if no requests was handled, non-zero
+ * if at least one queued request was handled. */
+typedef int (AioProcessQueue)(void *opaque);
+
+/* Flush any pending AIO operation. This function will block until all
+ * outstanding AIO operations have been completed or cancelled. */
+void qemu_aio_flush(void);
+
+/* Wait for a single AIO completion to occur. This function will wait
+ * until a single AIO event has completed and it will ensure something
+ * has moved before returning. This can issue new pending aio as
+ * result of executing I/O completion or bh callbacks. */
+void qemu_aio_wait(void);
+
+/*
+ * Runs all currently allowed AIO callbacks of completed requests. Returns 0
+ * if no requests were handled, non-zero if at least one request was
+ * processed.
+ */
+int qemu_aio_process_queue(void);
+
+/* Register a file descriptor and associated callbacks. Behaves very similarly
+ * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will
+ * be invoked when using either qemu_aio_wait() or qemu_aio_flush().
+ *
+ * Code that invokes AIO completion functions should rely on this function
+ * instead of qemu_set_fd_handler[2].
+ */
+int qemu_aio_set_fd_handler(int fd,
+ IOHandler *io_read,
+ IOHandler *io_write,
+ AioFlushHandler *io_flush,
+ AioProcessQueue *io_process_queue,
+ void *opaque);
+
+#endif
diff --git a/qemu-barrier.h b/qemu-barrier.h
new file mode 100644
index 0000000..b77fce2
--- /dev/null
+++ b/qemu-barrier.h
@@ -0,0 +1,10 @@
+#ifndef __QEMU_BARRIER_H
+#define __QEMU_BARRIER_H 1
+
+/* FIXME: arch dependant, x86 version */
+#define smp_wmb() asm volatile("" ::: "memory")
+
+/* Compiler barrier */
+#define barrier() asm volatile("" ::: "memory")
+
+#endif
diff --git a/qemu-char.c b/qemu-char.c
new file mode 100644
index 0000000..bd4e944
--- /dev/null
+++ b/qemu-char.c
@@ -0,0 +1,2629 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "net.h"
+#include "monitor.h"
+#include "console.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "hw/usb.h"
+#include "hw/baum.h"
+#include "hw/msmouse.h"
+#include "qemu-objects.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <zlib.h>
+
+#ifndef _WIN32
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <sys/select.h>
+#ifdef CONFIG_BSD
+#include <sys/stat.h>
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <libutil.h>
+#include <dev/ppbus/ppi.h>
+#include <dev/ppbus/ppbconf.h>
+#if defined(__GLIBC__)
+#include <pty.h>
+#endif
+#elif defined(__DragonFly__)
+#include <libutil.h>
+#include <dev/misc/ppi/ppi.h>
+#include <bus/ppbus/ppbconf.h>
+#else
+#include <util.h>
+#endif
+#else
+#ifdef __linux__
+#include <pty.h>
+
+#include <linux/ppdev.h>
+#include <linux/parport.h>
+#endif
+#ifdef __sun__
+#include <sys/stat.h>
+#include <sys/ethernet.h>
+#include <sys/sockio.h>
+#include <netinet/arp.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h> // must come after ip.h
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <syslog.h>
+#include <stropts.h>
+#endif
+#endif
+#endif
+
+#include "qemu_socket.h"
+#include "ui/qemu-spice.h"
+
+#define READ_BUF_LEN 4096
+
+/***********************************************************/
+/* character device */
+
+static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
+ QTAILQ_HEAD_INITIALIZER(chardevs);
+
+static void qemu_chr_event(CharDriverState *s, int event)
+{
+ /* Keep track if the char device is open */
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ s->opened = 1;
+ break;
+ case CHR_EVENT_CLOSED:
+ s->opened = 0;
+ break;
+ }
+
+ if (!s->chr_event)
+ return;
+ s->chr_event(s->handler_opaque, event);
+}
+
+static void qemu_chr_generic_open_bh(void *opaque)
+{
+ CharDriverState *s = opaque;
+ qemu_chr_event(s, CHR_EVENT_OPENED);
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
+}
+
+void qemu_chr_generic_open(CharDriverState *s)
+{
+ if (s->bh == NULL) {
+ s->bh = qemu_bh_new(qemu_chr_generic_open_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
+int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
+{
+ return s->chr_write(s, buf, len);
+}
+
+int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
+{
+ if (!s->chr_ioctl)
+ return -ENOTSUP;
+ return s->chr_ioctl(s, cmd, arg);
+}
+
+int qemu_chr_can_read(CharDriverState *s)
+{
+ if (!s->chr_can_read)
+ return 0;
+ return s->chr_can_read(s->handler_opaque);
+}
+
+void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len)
+{
+ s->chr_read(s->handler_opaque, buf, len);
+}
+
+int qemu_chr_get_msgfd(CharDriverState *s)
+{
+ return s->get_msgfd ? s->get_msgfd(s) : -1;
+}
+
+void qemu_chr_accept_input(CharDriverState *s)
+{
+ if (s->chr_accept_input)
+ s->chr_accept_input(s);
+}
+
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
+{
+ char buf[READ_BUF_LEN];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ qemu_chr_write(s, (uint8_t *)buf, strlen(buf));
+ va_end(ap);
+}
+
+void qemu_chr_send_event(CharDriverState *s, int event)
+{
+ if (s->chr_send_event)
+ s->chr_send_event(s, event);
+}
+
+void qemu_chr_add_handlers(CharDriverState *s,
+ IOCanReadHandler *fd_can_read,
+ IOReadHandler *fd_read,
+ IOEventHandler *fd_event,
+ void *opaque)
+{
+ s->chr_can_read = fd_can_read;
+ s->chr_read = fd_read;
+ s->chr_event = fd_event;
+ s->handler_opaque = opaque;
+ if (s->chr_update_read_handler)
+ s->chr_update_read_handler(s);
+
+ /* We're connecting to an already opened device, so let's make sure we
+ also get the open event */
+ if (s->opened) {
+ qemu_chr_generic_open(s);
+ }
+}
+
+static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ return len;
+}
+
+static CharDriverState *qemu_chr_open_null(QemuOpts *opts)
+{
+ CharDriverState *chr;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ chr->chr_write = null_chr_write;
+ return chr;
+}
+
+/* MUX driver for serial I/O splitting */
+#define MAX_MUX 4
+#define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */
+#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
+typedef struct {
+ IOCanReadHandler *chr_can_read[MAX_MUX];
+ IOReadHandler *chr_read[MAX_MUX];
+ IOEventHandler *chr_event[MAX_MUX];
+ void *ext_opaque[MAX_MUX];
+ CharDriverState *drv;
+ int focus;
+ int mux_cnt;
+ int term_got_escape;
+ int max_size;
+ /* Intermediate input buffer allows to catch escape sequences even if the
+ currently active device is not accepting any input - but only until it
+ is full as well. */
+ unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
+ int prod[MAX_MUX];
+ int cons[MAX_MUX];
+ int timestamps;
+ int linestart;
+ int64_t timestamps_start;
+} MuxDriver;
+
+
+static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ MuxDriver *d = chr->opaque;
+ int ret;
+ if (!d->timestamps) {
+ ret = d->drv->chr_write(d->drv, buf, len);
+ } else {
+ int i;
+
+ ret = 0;
+ for (i = 0; i < len; i++) {
+ if (d->linestart) {
+ char buf1[64];
+ int64_t ti;
+ int secs;
+
+ ti = qemu_get_clock(rt_clock);
+ if (d->timestamps_start == -1)
+ d->timestamps_start = ti;
+ ti -= d->timestamps_start;
+ secs = ti / 1000;
+ snprintf(buf1, sizeof(buf1),
+ "[%02d:%02d:%02d.%03d] ",
+ secs / 3600,
+ (secs / 60) % 60,
+ secs % 60,
+ (int)(ti % 1000));
+ d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1));
+ d->linestart = 0;
+ }
+ ret += d->drv->chr_write(d->drv, buf+i, 1);
+ if (buf[i] == '\n') {
+ d->linestart = 1;
+ }
+ }
+ }
+ return ret;
+}
+
+static const char * const mux_help[] = {
+ "% h print this help\n\r",
+ "% x exit emulator\n\r",
+ "% s save disk data back to file (if -snapshot)\n\r",
+ "% t toggle console timestamps\n\r"
+ "% b send break (magic sysrq)\n\r",
+ "% c switch between console and monitor\n\r",
+ "% % sends %\n\r",
+ NULL
+};
+
+int term_escape_char = 0x01; /* ctrl-a is used for escape */
+static void mux_print_help(CharDriverState *chr)
+{
+ int i, j;
+ char ebuf[15] = "Escape-Char";
+ char cbuf[50] = "\n\r";
+
+ if (term_escape_char > 0 && term_escape_char < 26) {
+ snprintf(cbuf, sizeof(cbuf), "\n\r");
+ snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
+ } else {
+ snprintf(cbuf, sizeof(cbuf),
+ "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
+ term_escape_char);
+ }
+ chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf));
+ for (i = 0; mux_help[i] != NULL; i++) {
+ for (j=0; mux_help[i][j] != '\0'; j++) {
+ if (mux_help[i][j] == '%')
+ chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf));
+ else
+ chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1);
+ }
+ }
+}
+
+static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event)
+{
+ if (d->chr_event[mux_nr])
+ d->chr_event[mux_nr](d->ext_opaque[mux_nr], event);
+}
+
+static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+{
+ if (d->term_got_escape) {
+ d->term_got_escape = 0;
+ if (ch == term_escape_char)
+ goto send_char;
+ switch(ch) {
+ case '?':
+ case 'h':
+ mux_print_help(chr);
+ break;
+ case 'x':
+ {
+ const char *term = "QEMU: Terminated\n\r";
+ chr->chr_write(chr,(uint8_t *)term,strlen(term));
+ exit(0);
+ break;
+ }
+ case 's':
+ bdrv_commit_all();
+ break;
+ case 'b':
+ qemu_chr_event(chr, CHR_EVENT_BREAK);
+ break;
+ case 'c':
+ /* Switch to the next registered device */
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+ d->focus++;
+ if (d->focus >= d->mux_cnt)
+ d->focus = 0;
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+ break;
+ case 't':
+ d->timestamps = !d->timestamps;
+ d->timestamps_start = -1;
+ d->linestart = 0;
+ break;
+ }
+ } else if (ch == term_escape_char) {
+ d->term_got_escape = 1;
+ } else {
+ send_char:
+ return 1;
+ }
+ return 0;
+}
+
+static void mux_chr_accept_input(CharDriverState *chr)
+{
+ MuxDriver *d = chr->opaque;
+ int m = d->focus;
+
+ while (d->prod[m] != d->cons[m] &&
+ d->chr_can_read[m] &&
+ d->chr_can_read[m](d->ext_opaque[m])) {
+ d->chr_read[m](d->ext_opaque[m],
+ &d->buffer[m][d->cons[m]++ & MUX_BUFFER_MASK], 1);
+ }
+}
+
+static int mux_chr_can_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int m = d->focus;
+
+ if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
+ return 1;
+ if (d->chr_can_read[m])
+ return d->chr_can_read[m](d->ext_opaque[m]);
+ return 0;
+}
+
+static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int m = d->focus;
+ int i;
+
+ mux_chr_accept_input (opaque);
+
+ for(i = 0; i < size; i++)
+ if (mux_proc_byte(chr, d, buf[i])) {
+ if (d->prod[m] == d->cons[m] &&
+ d->chr_can_read[m] &&
+ d->chr_can_read[m](d->ext_opaque[m]))
+ d->chr_read[m](d->ext_opaque[m], &buf[i], 1);
+ else
+ d->buffer[m][d->prod[m]++ & MUX_BUFFER_MASK] = buf[i];
+ }
+}
+
+static void mux_chr_event(void *opaque, int event)
+{
+ CharDriverState *chr = opaque;
+ MuxDriver *d = chr->opaque;
+ int i;
+
+ /* Send the event to all registered listeners */
+ for (i = 0; i < d->mux_cnt; i++)
+ mux_chr_send_event(d, i, event);
+}
+
+static void mux_chr_update_read_handler(CharDriverState *chr)
+{
+ MuxDriver *d = chr->opaque;
+
+ if (d->mux_cnt >= MAX_MUX) {
+ fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
+ return;
+ }
+ d->ext_opaque[d->mux_cnt] = chr->handler_opaque;
+ d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
+ d->chr_read[d->mux_cnt] = chr->chr_read;
+ d->chr_event[d->mux_cnt] = chr->chr_event;
+ /* Fix up the real driver with mux routines */
+ if (d->mux_cnt == 0) {
+ qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
+ mux_chr_event, chr);
+ }
+ if (d->focus != -1) {
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+ }
+ d->focus = d->mux_cnt;
+ d->mux_cnt++;
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
+}
+
+static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
+{
+ CharDriverState *chr;
+ MuxDriver *d;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ d = qemu_mallocz(sizeof(MuxDriver));
+
+ chr->opaque = d;
+ d->drv = drv;
+ d->focus = -1;
+ chr->chr_write = mux_chr_write;
+ chr->chr_update_read_handler = mux_chr_update_read_handler;
+ chr->chr_accept_input = mux_chr_accept_input;
+
+ /* Muxes are always open on creation */
+ qemu_chr_generic_open(chr);
+
+ return chr;
+}
+
+
+#ifdef _WIN32
+int send_all(int fd, const void *buf, int len1)
+{
+ int ret, len;
+
+ len = len1;
+ while (len > 0) {
+ ret = send(fd, buf, len, 0);
+ if (ret < 0) {
+ errno = WSAGetLastError();
+ if (errno != WSAEWOULDBLOCK) {
+ return -1;
+ }
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+
+#else
+
+int send_all(int fd, const void *_buf, int len1)
+{
+ int ret, len;
+ const uint8_t *buf = _buf;
+
+ len = len1;
+ while (len > 0) {
+ ret = write(fd, buf, len);
+ if (ret < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ } else if (ret == 0) {
+ break;
+ } else {
+ buf += ret;
+ len -= ret;
+ }
+ }
+ return len1 - len;
+}
+#endif /* !_WIN32 */
+
+#ifndef _WIN32
+
+typedef struct {
+ int fd_in, fd_out;
+ int max_size;
+} FDCharDriver;
+
+#define STDIO_MAX_CLIENTS 1
+static int stdio_nb_clients = 0;
+
+static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ FDCharDriver *s = chr->opaque;
+ return send_all(s->fd_out, buf, len);
+}
+
+static int fd_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+
+ s->max_size = qemu_chr_can_read(chr);
+ return s->max_size;
+}
+
+static void fd_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ FDCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[READ_BUF_LEN];
+
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ if (len == 0)
+ return;
+ size = read(s->fd_in, buf, len);
+ if (size == 0) {
+ /* FD has been closed. Remove it from the active list. */
+ qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+ return;
+ }
+ if (size > 0) {
+ qemu_chr_read(chr, buf, size);
+ }
+}
+
+static void fd_chr_update_read_handler(CharDriverState *chr)
+{
+ FDCharDriver *s = chr->opaque;
+
+ if (s->fd_in >= 0) {
+ if (display_type == DT_NOGRAPHIC && s->fd_in == 0) {
+ } else {
+ qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll,
+ fd_chr_read, NULL, chr);
+ }
+ }
+}
+
+static void fd_chr_close(struct CharDriverState *chr)
+{
+ FDCharDriver *s = chr->opaque;
+
+ if (s->fd_in >= 0) {
+ if (display_type == DT_NOGRAPHIC && s->fd_in == 0) {
+ } else {
+ qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+ }
+ }
+
+ qemu_free(s);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+}
+
+/* open a character device to a unix fd */
+static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
+{
+ CharDriverState *chr;
+ FDCharDriver *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(FDCharDriver));
+ s->fd_in = fd_in;
+ s->fd_out = fd_out;
+ chr->opaque = s;
+ chr->chr_write = fd_chr_write;
+ chr->chr_update_read_handler = fd_chr_update_read_handler;
+ chr->chr_close = fd_chr_close;
+
+ qemu_chr_generic_open(chr);
+
+ return chr;
+}
+
+static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts)
+{
+ int fd_out;
+
+ TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"),
+ O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666));
+ if (fd_out < 0)
+ return NULL;
+ return qemu_chr_open_fd(-1, fd_out);
+}
+
+static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts)
+{
+ int fd_in, fd_out;
+ char filename_in[256], filename_out[256];
+ const char *filename = qemu_opt_get(opts, "path");
+
+ if (filename == NULL) {
+ fprintf(stderr, "chardev: pipe: no filename given\n");
+ return NULL;
+ }
+
+ snprintf(filename_in, 256, "%s.in", filename);
+ snprintf(filename_out, 256, "%s.out", filename);
+ TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
+ TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
+ if (fd_in < 0 || fd_out < 0) {
+ if (fd_in >= 0)
+ close(fd_in);
+ if (fd_out >= 0)
+ close(fd_out);
+ TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY));
+ if (fd_in < 0)
+ return NULL;
+ }
+ return qemu_chr_open_fd(fd_in, fd_out);
+}
+
+
+/* for STDIO, we handle the case where several clients use it
+ (nographic mode) */
+
+#define TERM_FIFO_MAX_SIZE 1
+
+static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
+static int term_fifo_size;
+
+static int stdio_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+
+ /* try to flush the queue if needed */
+ if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
+ qemu_chr_read(chr, term_fifo, 1);
+ term_fifo_size = 0;
+ }
+ /* see if we can absorb more chars */
+ if (term_fifo_size == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static void stdio_read(void *opaque)
+{
+ int size;
+ uint8_t buf[1];
+ CharDriverState *chr = opaque;
+
+ size = read(0, buf, 1);
+ if (size == 0) {
+ /* stdin has been closed. Remove it from the active list. */
+ qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+ return;
+ }
+ if (size > 0) {
+ if (qemu_chr_can_read(chr) > 0) {
+ qemu_chr_read(chr, buf, 1);
+ } else if (term_fifo_size == 0) {
+ term_fifo[term_fifo_size++] = buf[0];
+ }
+ }
+}
+
+/* init terminal so that we can grab keys */
+static struct termios oldtty;
+static int old_fd0_flags;
+static bool stdio_allow_signal;
+
+static void term_exit(void)
+{
+ tcsetattr (0, TCSANOW, &oldtty);
+ fcntl(0, F_SETFL, old_fd0_flags);
+}
+
+static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo)
+{
+ struct termios tty;
+
+ tty = oldtty;
+ if (!echo) {
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+ }
+ /* if graphical mode, we allow Ctrl-C handling */
+ if (!stdio_allow_signal)
+ tty.c_lflag &= ~ISIG;
+
+ tcsetattr (0, TCSANOW, &tty);
+}
+
+static void qemu_chr_close_stdio(struct CharDriverState *chr)
+{
+ term_exit();
+ stdio_nb_clients--;
+ qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+ fd_chr_close(chr);
+}
+
+static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts)
+{
+ CharDriverState *chr;
+
+ if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
+ return NULL;
+ if (stdio_nb_clients == 0) {
+ old_fd0_flags = fcntl(0, F_GETFL);
+ tcgetattr (0, &oldtty);
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ atexit(term_exit);
+ }
+
+ chr = qemu_chr_open_fd(0, 1);
+ chr->chr_close = qemu_chr_close_stdio;
+ chr->chr_set_echo = qemu_chr_set_echo_stdio;
+ qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
+ stdio_nb_clients++;
+ stdio_allow_signal = qemu_opt_get_bool(opts, "signal",
+ display_type != DT_NOGRAPHIC);
+ qemu_chr_set_echo(chr, false);
+
+ return chr;
+}
+
+#ifdef __sun__
+/* Once Solaris has openpty(), this is going to be removed. */
+static int openpty(int *amaster, int *aslave, char *name,
+ struct termios *termp, struct winsize *winp)
+{
+ const char *slave;
+ int mfd = -1, sfd = -1;
+
+ *amaster = *aslave = -1;
+
+ mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
+ if (mfd < 0)
+ goto err;
+
+ if (grantpt(mfd) == -1 || unlockpt(mfd) == -1)
+ goto err;
+
+ if ((slave = ptsname(mfd)) == NULL)
+ goto err;
+
+ if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1)
+ goto err;
+
+ if (ioctl(sfd, I_PUSH, "ptem") == -1 ||
+ (termp != NULL && tcgetattr(sfd, termp) < 0))
+ goto err;
+
+ if (amaster)
+ *amaster = mfd;
+ if (aslave)
+ *aslave = sfd;
+ if (winp)
+ ioctl(sfd, TIOCSWINSZ, winp);
+
+ return 0;
+
+err:
+ if (sfd != -1)
+ close(sfd);
+ close(mfd);
+ return -1;
+}
+
+static void cfmakeraw (struct termios *termios_p)
+{
+ termios_p->c_iflag &=
+ ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ termios_p->c_oflag &= ~OPOST;
+ termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+ termios_p->c_cflag &= ~(CSIZE|PARENB);
+ termios_p->c_cflag |= CS8;
+
+ termios_p->c_cc[VMIN] = 0;
+ termios_p->c_cc[VTIME] = 0;
+}
+#endif
+
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+ || defined(__GLIBC__)
+
+typedef struct {
+ int fd;
+ int connected;
+ int polling;
+ int read_bytes;
+ QEMUTimer *timer;
+} PtyCharDriver;
+
+static void pty_chr_update_read_handler(CharDriverState *chr);
+static void pty_chr_state(CharDriverState *chr, int connected);
+
+static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ if (!s->connected) {
+ /* guest sends data, check for (re-)connect */
+ pty_chr_update_read_handler(chr);
+ return 0;
+ }
+ return send_all(s->fd, buf, len);
+}
+
+static int pty_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ s->read_bytes = qemu_chr_can_read(chr);
+ return s->read_bytes;
+}
+
+static void pty_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+ int size, len;
+ uint8_t buf[READ_BUF_LEN];
+
+ len = sizeof(buf);
+ if (len > s->read_bytes)
+ len = s->read_bytes;
+ if (len == 0)
+ return;
+ size = read(s->fd, buf, len);
+ if ((size == -1 && errno == EIO) ||
+ (size == 0)) {
+ pty_chr_state(chr, 0);
+ return;
+ }
+ if (size > 0) {
+ pty_chr_state(chr, 1);
+ qemu_chr_read(chr, buf, size);
+ }
+}
+
+static void pty_chr_update_read_handler(CharDriverState *chr)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, pty_chr_read_poll,
+ pty_chr_read, NULL, chr);
+ s->polling = 1;
+ /*
+ * Short timeout here: just need wait long enougth that qemu makes
+ * it through the poll loop once. When reconnected we want a
+ * short timeout so we notice it almost instantly. Otherwise
+ * read() gives us -EIO instantly, making pty_chr_state() reset the
+ * timeout to the normal (much longer) poll interval before the
+ * timer triggers.
+ */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10);
+}
+
+static void pty_chr_state(CharDriverState *chr, int connected)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ if (!connected) {
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ s->connected = 0;
+ s->polling = 0;
+ /* (re-)connect poll interval for idle guests: once per second.
+ * We check more frequently in case the guests sends data to
+ * the virtual device linked to our pty. */
+ qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
+ } else {
+ if (!s->connected)
+ qemu_chr_generic_open(chr);
+ s->connected = 1;
+ }
+}
+
+static void pty_chr_timer(void *opaque)
+{
+ struct CharDriverState *chr = opaque;
+ PtyCharDriver *s = chr->opaque;
+
+ if (s->connected)
+ return;
+ if (s->polling) {
+ /* If we arrive here without polling being cleared due
+ * read returning -EIO, then we are (re-)connected */
+ pty_chr_state(chr, 1);
+ return;
+ }
+
+ /* Next poll ... */
+ pty_chr_update_read_handler(chr);
+}
+
+static void pty_chr_close(struct CharDriverState *chr)
+{
+ PtyCharDriver *s = chr->opaque;
+
+ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+ close(s->fd);
+ qemu_del_timer(s->timer);
+ qemu_free_timer(s->timer);
+ qemu_free(s);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+}
+
+static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
+{
+ CharDriverState *chr;
+ PtyCharDriver *s;
+ struct termios tty;
+ int slave_fd, len;
+#if defined(__OpenBSD__) || defined(__DragonFly__)
+ char pty_name[PATH_MAX];
+#define q_ptsname(x) pty_name
+#else
+ char *pty_name = NULL;
+#define q_ptsname(x) ptsname(x)
+#endif
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(PtyCharDriver));
+
+ if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) {
+ return NULL;
+ }
+
+ /* Set raw attributes on the pty. */
+ tcgetattr(slave_fd, &tty);
+ cfmakeraw(&tty);
+ tcsetattr(slave_fd, TCSAFLUSH, &tty);
+ close(slave_fd);
+
+ len = strlen(q_ptsname(s->fd)) + 5;
+ chr->filename = qemu_malloc(len);
+ snprintf(chr->filename, len, "pty:%s", q_ptsname(s->fd));
+ qemu_opt_set(opts, "path", q_ptsname(s->fd));
+ fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd));
+
+ chr->opaque = s;
+ chr->chr_write = pty_chr_write;
+ chr->chr_update_read_handler = pty_chr_update_read_handler;
+ chr->chr_close = pty_chr_close;
+
+ s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr);
+
+ return chr;
+}
+
+static void tty_serial_init(int fd, int speed,
+ int parity, int data_bits, int stop_bits)
+{
+ struct termios tty;
+ speed_t spd;
+
+#if 0
+ printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
+ speed, parity, data_bits, stop_bits);
+#endif
+ tcgetattr (fd, &tty);
+
+#define check_speed(val) if (speed <= val) { spd = B##val; break; }
+ speed = speed * 10 / 11;
+ do {
+ check_speed(50);
+ check_speed(75);
+ check_speed(110);
+ check_speed(134);
+ check_speed(150);
+ check_speed(200);
+ check_speed(300);
+ check_speed(600);
+ check_speed(1200);
+ check_speed(1800);
+ check_speed(2400);
+ check_speed(4800);
+ check_speed(9600);
+ check_speed(19200);
+ check_speed(38400);
+ /* Non-Posix values follow. They may be unsupported on some systems. */
+ check_speed(57600);
+ check_speed(115200);
+#ifdef B230400
+ check_speed(230400);
+#endif
+#ifdef B460800
+ check_speed(460800);
+#endif
+#ifdef B500000
+ check_speed(500000);
+#endif
+#ifdef B576000
+ check_speed(576000);
+#endif
+#ifdef B921600
+ check_speed(921600);
+#endif
+#ifdef B1000000
+ check_speed(1000000);
+#endif
+#ifdef B1152000
+ check_speed(1152000);
+#endif
+#ifdef B1500000
+ check_speed(1500000);
+#endif
+#ifdef B2000000
+ check_speed(2000000);
+#endif
+#ifdef B2500000
+ check_speed(2500000);
+#endif
+#ifdef B3000000
+ check_speed(3000000);
+#endif
+#ifdef B3500000
+ check_speed(3500000);
+#endif
+#ifdef B4000000
+ check_speed(4000000);
+#endif
+ spd = B115200;
+ } while (0);
+
+ cfsetispeed(&tty, spd);
+ cfsetospeed(&tty, spd);
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
+ tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
+ switch(data_bits) {
+ default:
+ case 8:
+ tty.c_cflag |= CS8;
+ break;
+ case 7:
+ tty.c_cflag |= CS7;
+ break;
+ case 6:
+ tty.c_cflag |= CS6;
+ break;
+ case 5:
+ tty.c_cflag |= CS5;
+ break;
+ }
+ switch(parity) {
+ default:
+ case 'N':
+ break;
+ case 'E':
+ tty.c_cflag |= PARENB;
+ break;
+ case 'O':
+ tty.c_cflag |= PARENB | PARODD;
+ break;
+ }
+ if (stop_bits == 2)
+ tty.c_cflag |= CSTOPB;
+
+ tcsetattr (fd, TCSANOW, &tty);
+}
+
+static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+ FDCharDriver *s = chr->opaque;
+
+ switch(cmd) {
+ case CHR_IOCTL_SERIAL_SET_PARAMS:
+ {
+ QEMUSerialSetParams *ssp = arg;
+ tty_serial_init(s->fd_in, ssp->speed, ssp->parity,
+ ssp->data_bits, ssp->stop_bits);
+ }
+ break;
+ case CHR_IOCTL_SERIAL_SET_BREAK:
+ {
+ int enable = *(int *)arg;
+ if (enable)
+ tcsendbreak(s->fd_in, 1);
+ }
+ break;
+ case CHR_IOCTL_SERIAL_GET_TIOCM:
+ {
+ int sarg = 0;
+ int *targ = (int *)arg;
+ ioctl(s->fd_in, TIOCMGET, &sarg);
+ *targ = 0;
+ if (sarg & TIOCM_CTS)
+ *targ |= CHR_TIOCM_CTS;
+ if (sarg & TIOCM_CAR)
+ *targ |= CHR_TIOCM_CAR;
+ if (sarg & TIOCM_DSR)
+ *targ |= CHR_TIOCM_DSR;
+ if (sarg & TIOCM_RI)
+ *targ |= CHR_TIOCM_RI;
+ if (sarg & TIOCM_DTR)
+ *targ |= CHR_TIOCM_DTR;
+ if (sarg & TIOCM_RTS)
+ *targ |= CHR_TIOCM_RTS;
+ }
+ break;
+ case CHR_IOCTL_SERIAL_SET_TIOCM:
+ {
+ int sarg = *(int *)arg;
+ int targ = 0;
+ ioctl(s->fd_in, TIOCMGET, &targ);
+ targ &= ~(CHR_TIOCM_CTS | CHR_TIOCM_CAR | CHR_TIOCM_DSR
+ | CHR_TIOCM_RI | CHR_TIOCM_DTR | CHR_TIOCM_RTS);
+ if (sarg & CHR_TIOCM_CTS)
+ targ |= TIOCM_CTS;
+ if (sarg & CHR_TIOCM_CAR)
+ targ |= TIOCM_CAR;
+ if (sarg & CHR_TIOCM_DSR)
+ targ |= TIOCM_DSR;
+ if (sarg & CHR_TIOCM_RI)
+ targ |= TIOCM_RI;
+ if (sarg & CHR_TIOCM_DTR)
+ targ |= TIOCM_DTR;
+ if (sarg & CHR_TIOCM_RTS)
+ targ |= TIOCM_RTS;
+ ioctl(s->fd_in, TIOCMSET, &targ);
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+static void qemu_chr_close_tty(CharDriverState *chr)
+{
+ FDCharDriver *s = chr->opaque;
+ int fd = -1;
+
+ if (s) {
+ fd = s->fd_in;
+ }
+
+ fd_chr_close(chr);
+
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+static CharDriverState *qemu_chr_open_tty(QemuOpts *opts)
+{
+ const char *filename = qemu_opt_get(opts, "path");
+ CharDriverState *chr;
+ int fd;
+
+ TFR(fd = open(filename, O_RDWR | O_NONBLOCK));
+ if (fd < 0) {
+ return NULL;
+ }
+ tty_serial_init(fd, 115200, 'N', 8, 1);
+ chr = qemu_chr_open_fd(fd, fd);
+ if (!chr) {
+ close(fd);
+ return NULL;
+ }
+ chr->chr_ioctl = tty_serial_ioctl;
+ chr->chr_close = qemu_chr_close_tty;
+ return chr;
+}
+#else /* ! __linux__ && ! __sun__ */
+static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
+{
+ return NULL;
+}
+#endif /* __linux__ || __sun__ */
+
+#if defined(__linux__)
+typedef struct {
+ int fd;
+ int mode;
+} ParallelCharDriver;
+
+static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
+{
+ if (s->mode != mode) {
+ int m = mode;
+ if (ioctl(s->fd, PPSETMODE, &m) < 0)
+ return 0;
+ s->mode = mode;
+ }
+ return 1;
+}
+
+static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+ ParallelCharDriver *drv = chr->opaque;
+ int fd = drv->fd;
+ uint8_t b;
+
+ switch(cmd) {
+ case CHR_IOCTL_PP_READ_DATA:
+ if (ioctl(fd, PPRDATA, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_DATA:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPWDATA, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_CONTROL:
+ if (ioctl(fd, PPRCONTROL, &b) < 0)
+ return -ENOTSUP;
+ /* Linux gives only the lowest bits, and no way to know data
+ direction! For better compatibility set the fixed upper
+ bits. */
+ *(uint8_t *)arg = b | 0xc0;
+ break;
+ case CHR_IOCTL_PP_WRITE_CONTROL:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPWCONTROL, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_STATUS:
+ if (ioctl(fd, PPRSTATUS, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_DATA_DIR:
+ if (ioctl(fd, PPDATADIR, (int *)arg) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_EPP_READ_ADDR:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+ struct ParallelIOArg *parg = arg;
+ int n = read(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ case CHR_IOCTL_PP_EPP_READ:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+ struct ParallelIOArg *parg = arg;
+ int n = read(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ case CHR_IOCTL_PP_EPP_WRITE_ADDR:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+ struct ParallelIOArg *parg = arg;
+ int n = write(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ case CHR_IOCTL_PP_EPP_WRITE:
+ if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+ struct ParallelIOArg *parg = arg;
+ int n = write(fd, parg->buffer, parg->count);
+ if (n != parg->count) {
+ return -EIO;
+ }
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+static void pp_close(CharDriverState *chr)
+{
+ ParallelCharDriver *drv = chr->opaque;
+ int fd = drv->fd;
+
+ pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
+ ioctl(fd, PPRELEASE);
+ close(fd);
+ qemu_free(drv);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+}
+
+static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
+{
+ const char *filename = qemu_opt_get(opts, "path");
+ CharDriverState *chr;
+ ParallelCharDriver *drv;
+ int fd;
+
+ TFR(fd = open(filename, O_RDWR));
+ if (fd < 0)
+ return NULL;
+
+ if (ioctl(fd, PPCLAIM) < 0) {
+ close(fd);
+ return NULL;
+ }
+
+ drv = qemu_mallocz(sizeof(ParallelCharDriver));
+ drv->fd = fd;
+ drv->mode = IEEE1284_MODE_COMPAT;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ chr->chr_write = null_chr_write;
+ chr->chr_ioctl = pp_ioctl;
+ chr->chr_close = pp_close;
+ chr->opaque = drv;
+
+ qemu_chr_generic_open(chr);
+
+ return chr;
+}
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+ int fd = (int)(long)chr->opaque;
+ uint8_t b;
+
+ switch(cmd) {
+ case CHR_IOCTL_PP_READ_DATA:
+ if (ioctl(fd, PPIGDATA, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_DATA:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPISDATA, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_CONTROL:
+ if (ioctl(fd, PPIGCTRL, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ case CHR_IOCTL_PP_WRITE_CONTROL:
+ b = *(uint8_t *)arg;
+ if (ioctl(fd, PPISCTRL, &b) < 0)
+ return -ENOTSUP;
+ break;
+ case CHR_IOCTL_PP_READ_STATUS:
+ if (ioctl(fd, PPIGSTATUS, &b) < 0)
+ return -ENOTSUP;
+ *(uint8_t *)arg = b;
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
+{
+ const char *filename = qemu_opt_get(opts, "path");
+ CharDriverState *chr;
+ int fd;
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ return NULL;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ chr->opaque = (void *)(long)fd;
+ chr->chr_write = null_chr_write;
+ chr->chr_ioctl = pp_ioctl;
+ return chr;
+}
+#endif
+
+#else /* _WIN32 */
+
+typedef struct {
+ int max_size;
+ HANDLE hcom, hrecv, hsend;
+ OVERLAPPED orecv, osend;
+ BOOL fpipe;
+ DWORD len;
+} WinCharState;
+
+#define NSENDBUF 2048
+#define NRECVBUF 2048
+#define MAXCONNECT 1
+#define NTIMEOUT 5000
+
+static int win_chr_poll(void *opaque);
+static int win_chr_pipe_poll(void *opaque);
+
+static void win_chr_close(CharDriverState *chr)
+{
+ WinCharState *s = chr->opaque;
+
+ if (s->hsend) {
+ CloseHandle(s->hsend);
+ s->hsend = NULL;
+ }
+ if (s->hrecv) {
+ CloseHandle(s->hrecv);
+ s->hrecv = NULL;
+ }
+ if (s->hcom) {
+ CloseHandle(s->hcom);
+ s->hcom = NULL;
+ }
+ if (s->fpipe)
+ qemu_del_polling_cb(win_chr_pipe_poll, chr);
+ else
+ qemu_del_polling_cb(win_chr_poll, chr);
+
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+}
+
+static int win_chr_init(CharDriverState *chr, const char *filename)
+{
+ WinCharState *s = chr->opaque;
+ COMMCONFIG comcfg;
+ COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
+ COMSTAT comstat;
+ DWORD size;
+ DWORD err;
+
+ s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hsend) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+ s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hrecv) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+
+ s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+ if (s->hcom == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
+ s->hcom = NULL;
+ goto fail;
+ }
+
+ if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
+ fprintf(stderr, "Failed SetupComm\n");
+ goto fail;
+ }
+
+ ZeroMemory(&comcfg, sizeof(COMMCONFIG));
+ size = sizeof(COMMCONFIG);
+ GetDefaultCommConfig(filename, &comcfg, &size);
+ comcfg.dcb.DCBlength = sizeof(DCB);
+ CommConfigDialog(filename, NULL, &comcfg);
+
+ if (!SetCommState(s->hcom, &comcfg.dcb)) {
+ fprintf(stderr, "Failed SetCommState\n");
+ goto fail;
+ }
+
+ if (!SetCommMask(s->hcom, EV_ERR)) {
+ fprintf(stderr, "Failed SetCommMask\n");
+ goto fail;
+ }
+
+ cto.ReadIntervalTimeout = MAXDWORD;
+ if (!SetCommTimeouts(s->hcom, &cto)) {
+ fprintf(stderr, "Failed SetCommTimeouts\n");
+ goto fail;
+ }
+
+ if (!ClearCommError(s->hcom, &err, &comstat)) {
+ fprintf(stderr, "Failed ClearCommError\n");
+ goto fail;
+ }
+ qemu_add_polling_cb(win_chr_poll, chr);
+ return 0;
+
+ fail:
+ win_chr_close(chr);
+ return -1;
+}
+
+static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
+{
+ WinCharState *s = chr->opaque;
+ DWORD len, ret, size, err;
+
+ len = len1;
+ ZeroMemory(&s->osend, sizeof(s->osend));
+ s->osend.hEvent = s->hsend;
+ while (len > 0) {
+ if (s->hsend)
+ ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
+ else
+ ret = WriteFile(s->hcom, buf, len, &size, NULL);
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
+ if (ret) {
+ buf += size;
+ len -= size;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } else {
+ buf += size;
+ len -= size;
+ }
+ }
+ return len1 - len;
+}
+
+static int win_chr_read_poll(CharDriverState *chr)
+{
+ WinCharState *s = chr->opaque;
+
+ s->max_size = qemu_chr_can_read(chr);
+ return s->max_size;
+}
+
+static void win_chr_readfile(CharDriverState *chr)
+{
+ WinCharState *s = chr->opaque;
+ int ret, err;
+ uint8_t buf[READ_BUF_LEN];
+ DWORD size;
+
+ ZeroMemory(&s->orecv, sizeof(s->orecv));
+ s->orecv.hEvent = s->hrecv;
+ ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
+ if (!ret) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
+ }
+ }
+
+ if (size > 0) {
+ qemu_chr_read(chr, buf, size);
+ }
+}
+
+static void win_chr_read(CharDriverState *chr)
+{
+ WinCharState *s = chr->opaque;
+
+ if (s->len > s->max_size)
+ s->len = s->max_size;
+ if (s->len == 0)
+ return;
+
+ win_chr_readfile(chr);
+}
+
+static int win_chr_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ WinCharState *s = chr->opaque;
+ COMSTAT status;
+ DWORD comerr;
+
+ ClearCommError(s->hcom, &comerr, &status);
+ if (status.cbInQue > 0) {
+ s->len = status.cbInQue;
+ win_chr_read_poll(chr);
+ win_chr_read(chr);
+ return 1;
+ }
+ return 0;
+}
+
+static CharDriverState *qemu_chr_open_win(QemuOpts *opts)
+{
+ const char *filename = qemu_opt_get(opts, "path");
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(WinCharState));
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_close = win_chr_close;
+
+ if (win_chr_init(chr, filename) < 0) {
+ free(s);
+ free(chr);
+ return NULL;
+ }
+ qemu_chr_generic_open(chr);
+ return chr;
+}
+
+static int win_chr_pipe_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ WinCharState *s = chr->opaque;
+ DWORD size;
+
+ PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
+ if (size > 0) {
+ s->len = size;
+ win_chr_read_poll(chr);
+ win_chr_read(chr);
+ return 1;
+ }
+ return 0;
+}
+
+static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
+{
+ WinCharState *s = chr->opaque;
+ OVERLAPPED ov;
+ int ret;
+ DWORD size;
+ char openname[256];
+
+ s->fpipe = TRUE;
+
+ s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hsend) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+ s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!s->hrecv) {
+ fprintf(stderr, "Failed CreateEvent\n");
+ goto fail;
+ }
+
+ snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
+ s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+ if (s->hcom == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
+ s->hcom = NULL;
+ goto fail;
+ }
+
+ ZeroMemory(&ov, sizeof(ov));
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ ret = ConnectNamedPipe(s->hcom, &ov);
+ if (ret) {
+ fprintf(stderr, "Failed ConnectNamedPipe\n");
+ goto fail;
+ }
+
+ ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
+ if (!ret) {
+ fprintf(stderr, "Failed GetOverlappedResult\n");
+ if (ov.hEvent) {
+ CloseHandle(ov.hEvent);
+ ov.hEvent = NULL;
+ }
+ goto fail;
+ }
+
+ if (ov.hEvent) {
+ CloseHandle(ov.hEvent);
+ ov.hEvent = NULL;
+ }
+ qemu_add_polling_cb(win_chr_pipe_poll, chr);
+ return 0;
+
+ fail:
+ win_chr_close(chr);
+ return -1;
+}
+
+
+static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts)
+{
+ const char *filename = qemu_opt_get(opts, "path");
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(WinCharState));
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ chr->chr_close = win_chr_close;
+
+ if (win_chr_pipe_init(chr, filename) < 0) {
+ free(s);
+ free(chr);
+ return NULL;
+ }
+ qemu_chr_generic_open(chr);
+ return chr;
+}
+
+static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
+{
+ CharDriverState *chr;
+ WinCharState *s;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(WinCharState));
+ s->hcom = fd_out;
+ chr->opaque = s;
+ chr->chr_write = win_chr_write;
+ qemu_chr_generic_open(chr);
+ return chr;
+}
+
+static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts)
+{
+ return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
+}
+
+static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts)
+{
+ const char *file_out = qemu_opt_get(opts, "path");
+ HANDLE fd_out;
+
+ fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (fd_out == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ return qemu_chr_open_win_file(fd_out);
+}
+#endif /* !_WIN32 */
+
+/***********************************************************/
+/* UDP Net console */
+
+typedef struct {
+ int fd;
+ uint8_t buf[READ_BUF_LEN];
+ int bufcnt;
+ int bufptr;
+ int max_size;
+} NetCharDriver;
+
+static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ NetCharDriver *s = chr->opaque;
+
+ return send(s->fd, (const void *)buf, len, 0);
+}
+
+static int udp_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ s->max_size = qemu_chr_can_read(chr);
+
+ /* If there were any stray characters in the queue process them
+ * first
+ */
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = qemu_chr_can_read(chr);
+ }
+ return s->max_size;
+}
+
+static void udp_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ NetCharDriver *s = chr->opaque;
+
+ if (s->max_size == 0)
+ return;
+ s->bufcnt = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
+ s->bufptr = s->bufcnt;
+ if (s->bufcnt <= 0)
+ return;
+
+ s->bufptr = 0;
+ while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+ qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+ s->bufptr++;
+ s->max_size = qemu_chr_can_read(chr);
+ }
+}
+
+static void udp_chr_update_read_handler(CharDriverState *chr)
+{
+ NetCharDriver *s = chr->opaque;
+
+ if (s->fd >= 0) {
+ qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
+ udp_chr_read, NULL, chr);
+ }
+}
+
+static void udp_chr_close(CharDriverState *chr)
+{
+ NetCharDriver *s = chr->opaque;
+ if (s->fd >= 0) {
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ }
+ qemu_free(s);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+}
+
+static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
+{
+ CharDriverState *chr = NULL;
+ NetCharDriver *s = NULL;
+ int fd = -1;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(NetCharDriver));
+
+ fd = inet_dgram_opts(opts);
+ if (fd < 0) {
+ fprintf(stderr, "inet_dgram_opts failed\n");
+ goto return_err;
+ }
+
+ s->fd = fd;
+ s->bufcnt = 0;
+ s->bufptr = 0;
+ chr->opaque = s;
+ chr->chr_write = udp_chr_write;
+ chr->chr_update_read_handler = udp_chr_update_read_handler;
+ chr->chr_close = udp_chr_close;
+ return chr;
+
+return_err:
+ if (chr)
+ free(chr);
+ if (s)
+ free(s);
+ if (fd >= 0)
+ closesocket(fd);
+ return NULL;
+}
+
+/***********************************************************/
+/* TCP Net console */
+
+typedef struct {
+ int fd, listen_fd;
+ int connected;
+ int max_size;
+ int do_telnetopt;
+ int do_nodelay;
+ int is_unix;
+ int msgfd;
+} TCPCharDriver;
+
+static void tcp_chr_accept(void *opaque);
+
+static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->connected) {
+ return send_all(s->fd, buf, len);
+ } else {
+ /* XXX: indicate an error ? */
+ return len;
+ }
+}
+
+static int tcp_chr_read_poll(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ if (!s->connected)
+ return 0;
+ s->max_size = qemu_chr_can_read(chr);
+ return s->max_size;
+}
+
+#define IAC 255
+#define IAC_BREAK 243
+static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
+ TCPCharDriver *s,
+ uint8_t *buf, int *size)
+{
+ /* Handle any telnet client's basic IAC options to satisfy char by
+ * char mode with no echo. All IAC options will be removed from
+ * the buf and the do_telnetopt variable will be used to track the
+ * state of the width of the IAC information.
+ *
+ * IAC commands come in sets of 3 bytes with the exception of the
+ * "IAC BREAK" command and the double IAC.
+ */
+
+ int i;
+ int j = 0;
+
+ for (i = 0; i < *size; i++) {
+ if (s->do_telnetopt > 1) {
+ if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+ /* Double IAC means send an IAC */
+ if (j != i)
+ buf[j] = buf[i];
+ j++;
+ s->do_telnetopt = 1;
+ } else {
+ if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
+ /* Handle IAC break commands by sending a serial break */
+ qemu_chr_event(chr, CHR_EVENT_BREAK);
+ s->do_telnetopt++;
+ }
+ s->do_telnetopt++;
+ }
+ if (s->do_telnetopt >= 4) {
+ s->do_telnetopt = 1;
+ }
+ } else {
+ if ((unsigned char)buf[i] == IAC) {
+ s->do_telnetopt = 2;
+ } else {
+ if (j != i)
+ buf[j] = buf[i];
+ j++;
+ }
+ }
+ }
+ *size = j;
+}
+
+static int tcp_get_msgfd(CharDriverState *chr)
+{
+ TCPCharDriver *s = chr->opaque;
+ int fd = s->msgfd;
+ s->msgfd = -1;
+ return fd;
+}
+
+#ifndef _WIN32
+static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg)
+{
+ TCPCharDriver *s = chr->opaque;
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ int fd;
+
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS)
+ continue;
+
+ fd = *((int *)CMSG_DATA(cmsg));
+ if (fd < 0)
+ continue;
+
+ if (s->msgfd != -1)
+ close(s->msgfd);
+ s->msgfd = fd;
+ }
+}
+
+static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
+{
+ TCPCharDriver *s = chr->opaque;
+ struct msghdr msg = { NULL, };
+ struct iovec iov[1];
+ union {
+ struct cmsghdr cmsg;
+ char control[CMSG_SPACE(sizeof(int))];
+ } msg_control;
+ ssize_t ret;
+
+ iov[0].iov_base = buf;
+ iov[0].iov_len = len;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &msg_control;
+ msg.msg_controllen = sizeof(msg_control);
+
+ ret = recvmsg(s->fd, &msg, 0);
+ if (ret > 0 && s->is_unix)
+ unix_process_msgfd(chr, &msg);
+
+ return ret;
+}
+#else
+static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
+{
+ TCPCharDriver *s = chr->opaque;
+ return recv(s->fd, buf, len, 0);
+}
+#endif
+
+static void tcp_chr_read(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ uint8_t buf[READ_BUF_LEN];
+ int len, size;
+
+ if (!s->connected || s->max_size <= 0)
+ return;
+ len = sizeof(buf);
+ if (len > s->max_size)
+ len = s->max_size;
+ size = tcp_chr_recv(chr, (void *)buf, len);
+ if (size == 0) {
+ /* connection closed */
+ s->connected = 0;
+ if (s->listen_fd >= 0) {
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ }
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ s->fd = -1;
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+ } else if (size > 0) {
+ if (s->do_telnetopt)
+ tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+ if (size > 0)
+ qemu_chr_read(chr, buf, size);
+ }
+}
+
+#ifndef _WIN32
+CharDriverState *qemu_chr_open_eventfd(int eventfd)
+{
+ return qemu_chr_open_fd(eventfd, eventfd);
+}
+#endif
+
+static void tcp_chr_connect(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+
+ s->connected = 1;
+ qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+ tcp_chr_read, NULL, chr);
+ qemu_chr_generic_open(chr);
+}
+
+#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
+static void tcp_chr_telnet_init(int fd)
+{
+ char buf[3];
+ /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
+ IACSET(buf, 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfb, 0x00); /* IAC WILL Binary */
+ send(fd, (char *)buf, 3, 0);
+ IACSET(buf, 0xff, 0xfd, 0x00); /* IAC DO Binary */
+ send(fd, (char *)buf, 3, 0);
+}
+
+static void socket_set_nodelay(int fd)
+{
+ int val = 1;
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
+}
+
+static void tcp_chr_accept(void *opaque)
+{
+ CharDriverState *chr = opaque;
+ TCPCharDriver *s = chr->opaque;
+ struct sockaddr_in saddr;
+#ifndef _WIN32
+ struct sockaddr_un uaddr;
+#endif
+ struct sockaddr *addr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+#ifndef _WIN32
+ if (s->is_unix) {
+ len = sizeof(uaddr);
+ addr = (struct sockaddr *)&uaddr;
+ } else
+#endif
+ {
+ len = sizeof(saddr);
+ addr = (struct sockaddr *)&saddr;
+ }
+ fd = qemu_accept(s->listen_fd, addr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ if (s->do_telnetopt)
+ tcp_chr_telnet_init(fd);
+ break;
+ }
+ }
+ socket_set_nonblock(fd);
+ if (s->do_nodelay)
+ socket_set_nodelay(fd);
+ s->fd = fd;
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ tcp_chr_connect(chr);
+}
+
+static void tcp_chr_close(CharDriverState *chr)
+{
+ TCPCharDriver *s = chr->opaque;
+ if (s->fd >= 0) {
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ closesocket(s->fd);
+ }
+ if (s->listen_fd >= 0) {
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ closesocket(s->listen_fd);
+ }
+ qemu_free(s);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
+}
+
+static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
+{
+ CharDriverState *chr = NULL;
+ TCPCharDriver *s = NULL;
+ int fd = -1;
+ int is_listen;
+ int is_waitconnect;
+ int do_nodelay;
+ int is_unix;
+ int is_telnet;
+
+ is_listen = qemu_opt_get_bool(opts, "server", 0);
+ is_waitconnect = qemu_opt_get_bool(opts, "wait", 1);
+ is_telnet = qemu_opt_get_bool(opts, "telnet", 0);
+ do_nodelay = !qemu_opt_get_bool(opts, "delay", 1);
+ is_unix = qemu_opt_get(opts, "path") != NULL;
+ if (!is_listen)
+ is_waitconnect = 0;
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(TCPCharDriver));
+
+ if (is_unix) {
+ if (is_listen) {
+ fd = unix_listen_opts(opts);
+ } else {
+ fd = unix_connect_opts(opts);
+ }
+ } else {
+ if (is_listen) {
+ fd = inet_listen_opts(opts, 0);
+ } else {
+ fd = inet_connect_opts(opts);
+ }
+ }
+ if (fd < 0)
+ goto fail;
+
+ if (!is_waitconnect)
+ socket_set_nonblock(fd);
+
+ s->connected = 0;
+ s->fd = -1;
+ s->listen_fd = -1;
+ s->msgfd = -1;
+ s->is_unix = is_unix;
+ s->do_nodelay = do_nodelay && !is_unix;
+
+ chr->opaque = s;
+ chr->chr_write = tcp_chr_write;
+ chr->chr_close = tcp_chr_close;
+ chr->get_msgfd = tcp_get_msgfd;
+
+ if (is_listen) {
+ s->listen_fd = fd;
+ qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+ if (is_telnet)
+ s->do_telnetopt = 1;
+
+ } else {
+ s->connected = 1;
+ s->fd = fd;
+ socket_set_nodelay(fd);
+ tcp_chr_connect(chr);
+ }
+
+ /* for "info chardev" monitor command */
+ chr->filename = qemu_malloc(256);
+ if (is_unix) {
+ snprintf(chr->filename, 256, "unix:%s%s",
+ qemu_opt_get(opts, "path"),
+ qemu_opt_get_bool(opts, "server", 0) ? ",server" : "");
+ } else if (is_telnet) {
+ snprintf(chr->filename, 256, "telnet:%s:%s%s",
+ qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"),
+ qemu_opt_get_bool(opts, "server", 0) ? ",server" : "");
+ } else {
+ snprintf(chr->filename, 256, "tcp:%s:%s%s",
+ qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"),
+ qemu_opt_get_bool(opts, "server", 0) ? ",server" : "");
+ }
+
+ if (is_listen && is_waitconnect) {
+ printf("QEMU waiting for connection on: %s\n",
+ chr->filename);
+ tcp_chr_accept(chr);
+ socket_set_nonblock(s->listen_fd);
+ }
+ return chr;
+
+ fail:
+ if (fd >= 0)
+ closesocket(fd);
+ qemu_free(s);
+ qemu_free(chr);
+ return NULL;
+}
+
+/***********************************************************/
+/* Memory chardev */
+typedef struct {
+ size_t outbuf_size;
+ size_t outbuf_capacity;
+ uint8_t *outbuf;
+} MemoryDriver;
+
+static int mem_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ MemoryDriver *d = chr->opaque;
+
+ /* TODO: the QString implementation has the same code, we should
+ * introduce a generic way to do this in cutils.c */
+ if (d->outbuf_capacity < d->outbuf_size + len) {
+ /* grow outbuf */
+ d->outbuf_capacity += len;
+ d->outbuf_capacity *= 2;
+ d->outbuf = qemu_realloc(d->outbuf, d->outbuf_capacity);
+ }
+
+ memcpy(d->outbuf + d->outbuf_size, buf, len);
+ d->outbuf_size += len;
+
+ return len;
+}
+
+void qemu_chr_init_mem(CharDriverState *chr)
+{
+ MemoryDriver *d;
+
+ d = qemu_malloc(sizeof(*d));
+ d->outbuf_size = 0;
+ d->outbuf_capacity = 4096;
+ d->outbuf = qemu_mallocz(d->outbuf_capacity);
+
+ memset(chr, 0, sizeof(*chr));
+ chr->opaque = d;
+ chr->chr_write = mem_chr_write;
+}
+
+QString *qemu_chr_mem_to_qs(CharDriverState *chr)
+{
+ MemoryDriver *d = chr->opaque;
+ return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1);
+}
+
+/* NOTE: this driver can not be closed with qemu_chr_close()! */
+void qemu_chr_close_mem(CharDriverState *chr)
+{
+ MemoryDriver *d = chr->opaque;
+
+ qemu_free(d->outbuf);
+ qemu_free(chr->opaque);
+ chr->opaque = NULL;
+ chr->chr_write = NULL;
+}
+
+size_t qemu_chr_mem_osize(const CharDriverState *chr)
+{
+ const MemoryDriver *d = chr->opaque;
+ return d->outbuf_size;
+}
+
+QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
+{
+ char host[65], port[33], width[8], height[8];
+ int pos;
+ const char *p;
+ QemuOpts *opts;
+
+ opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1);
+ if (NULL == opts)
+ return NULL;
+
+ if (strstart(filename, "mon:", &p)) {
+ filename = p;
+ qemu_opt_set(opts, "mux", "on");
+ }
+
+ if (strcmp(filename, "null") == 0 ||
+ strcmp(filename, "pty") == 0 ||
+ strcmp(filename, "msmouse") == 0 ||
+ strcmp(filename, "braille") == 0 ||
+ strcmp(filename, "stdio") == 0) {
+ qemu_opt_set(opts, "backend", filename);
+ return opts;
+ }
+ if (strstart(filename, "vc", &p)) {
+ qemu_opt_set(opts, "backend", "vc");
+ if (*p == ':') {
+ if (sscanf(p+1, "%8[0-9]x%8[0-9]", width, height) == 2) {
+ /* pixels */
+ qemu_opt_set(opts, "width", width);
+ qemu_opt_set(opts, "height", height);
+ } else if (sscanf(p+1, "%8[0-9]Cx%8[0-9]C", width, height) == 2) {
+ /* chars */
+ qemu_opt_set(opts, "cols", width);
+ qemu_opt_set(opts, "rows", height);
+ } else {
+ goto fail;
+ }
+ }
+ return opts;
+ }
+ if (strcmp(filename, "con:") == 0) {
+ qemu_opt_set(opts, "backend", "console");
+ return opts;
+ }
+ if (strstart(filename, "COM", NULL)) {
+ qemu_opt_set(opts, "backend", "serial");
+ qemu_opt_set(opts, "path", filename);
+ return opts;
+ }
+ if (strstart(filename, "file:", &p)) {
+ qemu_opt_set(opts, "backend", "file");
+ qemu_opt_set(opts, "path", p);
+ return opts;
+ }
+ if (strstart(filename, "pipe:", &p)) {
+ qemu_opt_set(opts, "backend", "pipe");
+ qemu_opt_set(opts, "path", p);
+ return opts;
+ }
+ if (strstart(filename, "tcp:", &p) ||
+ strstart(filename, "telnet:", &p)) {
+ if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
+ host[0] = 0;
+ if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
+ goto fail;
+ }
+ qemu_opt_set(opts, "backend", "socket");
+ qemu_opt_set(opts, "host", host);
+ qemu_opt_set(opts, "port", port);
+ if (p[pos] == ',') {
+ if (qemu_opts_do_parse(opts, p+pos+1, NULL) != 0)
+ goto fail;
+ }
+ if (strstart(filename, "telnet:", &p))
+ qemu_opt_set(opts, "telnet", "on");
+ return opts;
+ }
+ if (strstart(filename, "udp:", &p)) {
+ qemu_opt_set(opts, "backend", "udp");
+ if (sscanf(p, "%64[^:]:%32[^@,]%n", host, port, &pos) < 2) {
+ host[0] = 0;
+ if (sscanf(p, ":%32[^@,]%n", port, &pos) < 1) {
+ goto fail;
+ }
+ }
+ qemu_opt_set(opts, "host", host);
+ qemu_opt_set(opts, "port", port);
+ if (p[pos] == '@') {
+ p += pos + 1;
+ if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
+ host[0] = 0;
+ if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) {
+ goto fail;
+ }
+ }
+ qemu_opt_set(opts, "localaddr", host);
+ qemu_opt_set(opts, "localport", port);
+ }
+ return opts;
+ }
+ if (strstart(filename, "unix:", &p)) {
+ qemu_opt_set(opts, "backend", "socket");
+ if (qemu_opts_do_parse(opts, p, "path") != 0)
+ goto fail;
+ return opts;
+ }
+ if (strstart(filename, "/dev/parport", NULL) ||
+ strstart(filename, "/dev/ppi", NULL)) {
+ qemu_opt_set(opts, "backend", "parport");
+ qemu_opt_set(opts, "path", filename);
+ return opts;
+ }
+ if (strstart(filename, "/dev/", NULL)) {
+ qemu_opt_set(opts, "backend", "tty");
+ qemu_opt_set(opts, "path", filename);
+ return opts;
+ }
+
+fail:
+ qemu_opts_del(opts);
+ return NULL;
+}
+
+static const struct {
+ const char *name;
+ CharDriverState *(*open)(QemuOpts *opts);
+} backend_table[] = {
+ { .name = "null", .open = qemu_chr_open_null },
+ { .name = "socket", .open = qemu_chr_open_socket },
+ { .name = "udp", .open = qemu_chr_open_udp },
+ { .name = "msmouse", .open = qemu_chr_open_msmouse },
+ { .name = "vc", .open = text_console_init },
+#ifdef _WIN32
+ { .name = "file", .open = qemu_chr_open_win_file_out },
+ { .name = "pipe", .open = qemu_chr_open_win_pipe },
+ { .name = "console", .open = qemu_chr_open_win_con },
+ { .name = "serial", .open = qemu_chr_open_win },
+#else
+ { .name = "file", .open = qemu_chr_open_file_out },
+ { .name = "pipe", .open = qemu_chr_open_pipe },
+ { .name = "pty", .open = qemu_chr_open_pty },
+ { .name = "stdio", .open = qemu_chr_open_stdio },
+#endif
+#ifdef CONFIG_BRLAPI
+ { .name = "braille", .open = chr_baum_init },
+#endif
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+ || defined(__FreeBSD_kernel__)
+ { .name = "tty", .open = qemu_chr_open_tty },
+#endif
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) \
+ || defined(__FreeBSD_kernel__)
+ { .name = "parport", .open = qemu_chr_open_pp },
+#endif
+#ifdef CONFIG_SPICE
+ { .name = "spicevmc", .open = qemu_chr_open_spice },
+#endif
+};
+
+CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
+ void (*init)(struct CharDriverState *s))
+{
+ CharDriverState *chr;
+ int i;
+
+ if (qemu_opts_id(opts) == NULL) {
+ fprintf(stderr, "chardev: no id specified\n");
+ return NULL;
+ }
+
+ if (qemu_opt_get(opts, "backend") == NULL) {
+ fprintf(stderr, "chardev: \"%s\" missing backend\n",
+ qemu_opts_id(opts));
+ return NULL;
+ }
+ for (i = 0; i < ARRAY_SIZE(backend_table); i++) {
+ if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0)
+ break;
+ }
+ if (i == ARRAY_SIZE(backend_table)) {
+ fprintf(stderr, "chardev: backend \"%s\" not found\n",
+ qemu_opt_get(opts, "backend"));
+ return NULL;
+ }
+
+ chr = backend_table[i].open(opts);
+ if (!chr) {
+ fprintf(stderr, "chardev: opening backend \"%s\" failed\n",
+ qemu_opt_get(opts, "backend"));
+ return NULL;
+ }
+
+ if (!chr->filename)
+ chr->filename = qemu_strdup(qemu_opt_get(opts, "backend"));
+ chr->init = init;
+ QTAILQ_INSERT_TAIL(&chardevs, chr, next);
+
+ if (qemu_opt_get_bool(opts, "mux", 0)) {
+ CharDriverState *base = chr;
+ int len = strlen(qemu_opts_id(opts)) + 6;
+ base->label = qemu_malloc(len);
+ snprintf(base->label, len, "%s-base", qemu_opts_id(opts));
+ chr = qemu_chr_open_mux(base);
+ chr->filename = base->filename;
+ QTAILQ_INSERT_TAIL(&chardevs, chr, next);
+ }
+ chr->label = qemu_strdup(qemu_opts_id(opts));
+ return chr;
+}
+
+CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
+{
+ const char *p;
+ CharDriverState *chr;
+ QemuOpts *opts;
+
+ if (strstart(filename, "chardev:", &p)) {
+ return qemu_chr_find(p);
+ }
+
+ opts = qemu_chr_parse_compat(label, filename);
+ if (!opts)
+ return NULL;
+
+ chr = qemu_chr_open_opts(opts, init);
+ if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
+ monitor_init(chr, MONITOR_USE_READLINE);
+ }
+ qemu_opts_del(opts);
+ return chr;
+}
+
+void qemu_chr_set_echo(struct CharDriverState *chr, bool echo)
+{
+ if (chr->chr_set_echo) {
+ chr->chr_set_echo(chr, echo);
+ }
+}
+
+void qemu_chr_close(CharDriverState *chr)
+{
+ QTAILQ_REMOVE(&chardevs, chr, next);
+ if (chr->chr_close)
+ chr->chr_close(chr);
+ qemu_free(chr->filename);
+ qemu_free(chr->label);
+ qemu_free(chr);
+}
+
+static void qemu_chr_qlist_iter(QObject *obj, void *opaque)
+{
+ QDict *chr_dict;
+ Monitor *mon = opaque;
+
+ chr_dict = qobject_to_qdict(obj);
+ monitor_printf(mon, "%s: filename=%s\n", qdict_get_str(chr_dict, "label"),
+ qdict_get_str(chr_dict, "filename"));
+}
+
+void qemu_chr_info_print(Monitor *mon, const QObject *ret_data)
+{
+ qlist_iter(qobject_to_qlist(ret_data), qemu_chr_qlist_iter, mon);
+}
+
+void qemu_chr_info(Monitor *mon, QObject **ret_data)
+{
+ QList *chr_list;
+ CharDriverState *chr;
+
+ chr_list = qlist_new();
+
+ QTAILQ_FOREACH(chr, &chardevs, next) {
+ QObject *obj = qobject_from_jsonf("{ 'label': %s, 'filename': %s }",
+ chr->label, chr->filename);
+ qlist_append_obj(chr_list, obj);
+ }
+
+ *ret_data = QOBJECT(chr_list);
+}
+
+CharDriverState *qemu_chr_find(const char *name)
+{
+ CharDriverState *chr;
+
+ QTAILQ_FOREACH(chr, &chardevs, next) {
+ if (strcmp(chr->label, name) != 0)
+ continue;
+ return chr;
+ }
+ return NULL;
+}
diff --git a/qemu-char.h b/qemu-char.h
new file mode 100644
index 0000000..56d9954
--- /dev/null
+++ b/qemu-char.h
@@ -0,0 +1,123 @@
+#ifndef QEMU_CHAR_H
+#define QEMU_CHAR_H
+
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "qobject.h"
+#include "qstring.h"
+
+/* character device */
+
+#define CHR_EVENT_BREAK 0 /* serial break char */
+#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */
+#define CHR_EVENT_OPENED 2 /* new connection established */
+#define CHR_EVENT_MUX_IN 3 /* mux-focus was set to this terminal */
+#define CHR_EVENT_MUX_OUT 4 /* mux-focus will move on */
+#define CHR_EVENT_CLOSED 5 /* connection closed */
+
+
+#define CHR_IOCTL_SERIAL_SET_PARAMS 1
+typedef struct {
+ int speed;
+ int parity;
+ int data_bits;
+ int stop_bits;
+} QEMUSerialSetParams;
+
+#define CHR_IOCTL_SERIAL_SET_BREAK 2
+
+#define CHR_IOCTL_PP_READ_DATA 3
+#define CHR_IOCTL_PP_WRITE_DATA 4
+#define CHR_IOCTL_PP_READ_CONTROL 5
+#define CHR_IOCTL_PP_WRITE_CONTROL 6
+#define CHR_IOCTL_PP_READ_STATUS 7
+#define CHR_IOCTL_PP_EPP_READ_ADDR 8
+#define CHR_IOCTL_PP_EPP_READ 9
+#define CHR_IOCTL_PP_EPP_WRITE_ADDR 10
+#define CHR_IOCTL_PP_EPP_WRITE 11
+#define CHR_IOCTL_PP_DATA_DIR 12
+
+#define CHR_IOCTL_SERIAL_SET_TIOCM 13
+#define CHR_IOCTL_SERIAL_GET_TIOCM 14
+
+#define CHR_TIOCM_CTS 0x020
+#define CHR_TIOCM_CAR 0x040
+#define CHR_TIOCM_DSR 0x100
+#define CHR_TIOCM_RI 0x080
+#define CHR_TIOCM_DTR 0x002
+#define CHR_TIOCM_RTS 0x004
+
+typedef void IOEventHandler(void *opaque, int event);
+
+struct CharDriverState {
+ void (*init)(struct CharDriverState *s);
+ int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
+ void (*chr_update_read_handler)(struct CharDriverState *s);
+ int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
+ int (*get_msgfd)(struct CharDriverState *s);
+ IOEventHandler *chr_event;
+ IOCanReadHandler *chr_can_read;
+ IOReadHandler *chr_read;
+ void *handler_opaque;
+ void (*chr_send_event)(struct CharDriverState *chr, int event);
+ void (*chr_close)(struct CharDriverState *chr);
+ void (*chr_accept_input)(struct CharDriverState *chr);
+ void (*chr_set_echo)(struct CharDriverState *chr, bool echo);
+ void *opaque;
+ QEMUBH *bh;
+ char *label;
+ char *filename;
+ int opened;
+ QTAILQ_ENTRY(CharDriverState) next;
+};
+
+QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
+CharDriverState *qemu_chr_open_opts(QemuOpts *opts,
+ void (*init)(struct CharDriverState *s));
+CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s));
+void qemu_chr_set_echo(struct CharDriverState *chr, bool echo);
+void qemu_chr_close(CharDriverState *chr);
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
+ GCC_FMT_ATTR(2, 3);
+int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
+void qemu_chr_send_event(CharDriverState *s, int event);
+void qemu_chr_add_handlers(CharDriverState *s,
+ IOCanReadHandler *fd_can_read,
+ IOReadHandler *fd_read,
+ IOEventHandler *fd_event,
+ void *opaque);
+int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg);
+void qemu_chr_generic_open(CharDriverState *s);
+int qemu_chr_can_read(CharDriverState *s);
+void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len);
+int qemu_chr_get_msgfd(CharDriverState *s);
+void qemu_chr_accept_input(CharDriverState *s);
+void qemu_chr_info_print(Monitor *mon, const QObject *ret_data);
+void qemu_chr_info(Monitor *mon, QObject **ret_data);
+CharDriverState *qemu_chr_find(const char *name);
+
+/* add an eventfd to the qemu devices that are polled */
+CharDriverState *qemu_chr_open_eventfd(int eventfd);
+
+extern int term_escape_char;
+
+/* memory chardev */
+void qemu_chr_init_mem(CharDriverState *chr);
+void qemu_chr_close_mem(CharDriverState *chr);
+QString *qemu_chr_mem_to_qs(CharDriverState *chr);
+size_t qemu_chr_mem_osize(const CharDriverState *chr);
+
+/* async I/O support */
+
+int qemu_set_fd_handler2(int fd,
+ IOCanReadHandler *fd_read_poll,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque);
+int qemu_set_fd_handler(int fd,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque);
+#endif
diff --git a/qemu-common.h b/qemu-common.h
new file mode 100644
index 0000000..cb4b7e0
--- /dev/null
+++ b/qemu-common.h
@@ -0,0 +1,368 @@
+/* Common header file that is included by all of qemu. */
+#ifndef QEMU_COMMON_H
+#define QEMU_COMMON_H
+
+#include "config-host.h"
+
+#define QEMU_NORETURN __attribute__ ((__noreturn__))
+#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT
+#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define QEMU_WARN_UNUSED_RESULT
+#endif
+
+#define QEMU_BUILD_BUG_ON(x) typedef char __build_bug_on__##__LINE__[(x)?-1:1];
+
+typedef struct QEMUTimer QEMUTimer;
+typedef struct QEMUFile QEMUFile;
+typedef struct QEMUBH QEMUBH;
+typedef struct DeviceState DeviceState;
+
+/* we put basic includes here to avoid repeating them in device drivers */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+#ifndef ENOMEDIUM
+#define ENOMEDIUM ENODEV
+#endif
+#if !defined(ENOTSUP)
+#define ENOTSUP 4096
+#endif
+#ifndef TIME_MAX
+#define TIME_MAX LONG_MAX
+#endif
+
+#ifndef CONFIG_IOVEC
+#define CONFIG_IOVEC
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+/*
+ * Use the same value as Linux for now.
+ */
+#define IOV_MAX 1024
+#else
+#include <sys/uio.h>
+#endif
+
+#if defined __GNUC__
+# if (__GNUC__ < 4) || \
+ defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4)
+ /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */
+# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2)))
+# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m)))
+# else
+ /* Use gnu_printf when supported (qemu uses standard format strings). */
+# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
+# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
+# endif
+#else
+#define GCC_ATTR /**/
+#define GCC_FMT_ATTR(n, m)
+#endif
+
+typedef int (*fprintf_function)(FILE *f, const char *fmt, ...)
+ GCC_FMT_ATTR(2, 3);
+
+#ifdef _WIN32
+#define fsync _commit
+#define lseek _lseeki64
+int qemu_ftruncate64(int, int64_t);
+#define ftruncate qemu_ftruncate64
+
+static inline char *realpath(const char *path, char *resolved_path)
+{
+ _fullpath(resolved_path, path, _MAX_PATH);
+ return resolved_path;
+}
+
+#define PRId64 "I64d"
+#define PRIx64 "I64x"
+#define PRIu64 "I64u"
+#define PRIo64 "I64o"
+#endif
+
+/* FIXME: Remove NEED_CPU_H. */
+#ifndef NEED_CPU_H
+
+#include <setjmp.h>
+#include "osdep.h"
+#include "bswap.h"
+
+#else
+
+#include "cpu.h"
+
+#endif /* !defined(NEED_CPU_H) */
+
+/* bottom halves */
+typedef void QEMUBHFunc(void *opaque);
+
+void async_context_push(void);
+void async_context_pop(void);
+int get_async_context_id(void);
+
+QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
+void qemu_bh_schedule(QEMUBH *bh);
+/* Bottom halfs that are scheduled from a bottom half handler are instantly
+ * invoked. This can create an infinite loop if a bottom half handler
+ * schedules itself. qemu_bh_schedule_idle() avoids this infinite loop by
+ * ensuring that the bottom half isn't executed until the next main loop
+ * iteration.
+ */
+void qemu_bh_schedule_idle(QEMUBH *bh);
+void qemu_bh_cancel(QEMUBH *bh);
+void qemu_bh_delete(QEMUBH *bh);
+int qemu_bh_poll(void);
+void qemu_bh_update_timeout(int *timeout);
+
+void qemu_get_timedate(struct tm *tm, int offset);
+int qemu_timedate_diff(struct tm *tm);
+
+/* cutils.c */
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+int strstart(const char *str, const char *val, const char **ptr);
+int stristart(const char *str, const char *val, const char **ptr);
+int qemu_strnlen(const char *s, int max_len);
+time_t mktimegm(struct tm *tm);
+int qemu_fls(int i);
+int qemu_fdatasync(int fd);
+int fcntl_setfl(int fd, int flag);
+
+/*
+ * strtosz() suffixes used to specify the default treatment of an
+ * argument passed to strtosz() without an explicit suffix.
+ * These should be defined using upper case characters in the range
+ * A-Z, as strtosz() will use qemu_toupper() on the given argument
+ * prior to comparison.
+ */
+#define STRTOSZ_DEFSUFFIX_TB 'T'
+#define STRTOSZ_DEFSUFFIX_GB 'G'
+#define STRTOSZ_DEFSUFFIX_MB 'M'
+#define STRTOSZ_DEFSUFFIX_KB 'K'
+#define STRTOSZ_DEFSUFFIX_B 'B'
+int64_t strtosz(const char *nptr, char **end);
+int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
+
+/* path.c */
+void init_paths(const char *prefix);
+const char *path(const char *pathname);
+
+#define qemu_isalnum(c) isalnum((unsigned char)(c))
+#define qemu_isalpha(c) isalpha((unsigned char)(c))
+#define qemu_iscntrl(c) iscntrl((unsigned char)(c))
+#define qemu_isdigit(c) isdigit((unsigned char)(c))
+#define qemu_isgraph(c) isgraph((unsigned char)(c))
+#define qemu_islower(c) islower((unsigned char)(c))
+#define qemu_isprint(c) isprint((unsigned char)(c))
+#define qemu_ispunct(c) ispunct((unsigned char)(c))
+#define qemu_isspace(c) isspace((unsigned char)(c))
+#define qemu_isupper(c) isupper((unsigned char)(c))
+#define qemu_isxdigit(c) isxdigit((unsigned char)(c))
+#define qemu_tolower(c) tolower((unsigned char)(c))
+#define qemu_toupper(c) toupper((unsigned char)(c))
+#define qemu_isascii(c) isascii((unsigned char)(c))
+#define qemu_toascii(c) toascii((unsigned char)(c))
+
+#ifdef _WIN32
+/* ffs() in oslib-win32.c for WIN32, strings.h for the rest of the world */
+int ffs(int i);
+#endif
+
+void *qemu_oom_check(void *ptr);
+void *qemu_malloc(size_t size);
+void *qemu_realloc(void *ptr, size_t size);
+void *qemu_mallocz(size_t size);
+void qemu_free(void *ptr);
+char *qemu_strdup(const char *str);
+char *qemu_strndup(const char *str, size_t size);
+
+void qemu_mutex_lock_iothread(void);
+void qemu_mutex_unlock_iothread(void);
+
+int qemu_open(const char *name, int flags, ...);
+ssize_t qemu_write_full(int fd, const void *buf, size_t count)
+ QEMU_WARN_UNUSED_RESULT;
+void qemu_set_cloexec(int fd);
+
+#ifndef _WIN32
+int qemu_eventfd(int pipefd[2]);
+int qemu_pipe(int pipefd[2]);
+#endif
+
+/* Error handling. */
+
+void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+
+/* IO callbacks. */
+typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
+typedef int IOCanReadHandler(void *opaque);
+typedef void IOHandler(void *opaque);
+
+struct ParallelIOArg {
+ void *buffer;
+ int count;
+};
+
+typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size);
+
+/* A load of opaque types so that device init declarations don't have to
+ pull in all the real definitions. */
+typedef struct NICInfo NICInfo;
+typedef struct HCIInfo HCIInfo;
+typedef struct AudioState AudioState;
+typedef struct BlockDriverState BlockDriverState;
+typedef struct DriveInfo DriveInfo;
+typedef struct DisplayState DisplayState;
+typedef struct DisplayChangeListener DisplayChangeListener;
+typedef struct DisplaySurface DisplaySurface;
+typedef struct DisplayAllocator DisplayAllocator;
+typedef struct PixelFormat PixelFormat;
+typedef struct TextConsole TextConsole;
+typedef TextConsole QEMUConsole;
+typedef struct CharDriverState CharDriverState;
+typedef struct MACAddr MACAddr;
+typedef struct VLANState VLANState;
+typedef struct VLANClientState VLANClientState;
+typedef struct i2c_bus i2c_bus;
+typedef struct i2c_slave i2c_slave;
+typedef struct SMBusDevice SMBusDevice;
+typedef struct PCIHostState PCIHostState;
+typedef struct PCIExpressHost PCIExpressHost;
+typedef struct PCIBus PCIBus;
+typedef struct PCIDevice PCIDevice;
+typedef struct PCIExpressDevice PCIExpressDevice;
+typedef struct PCIBridge PCIBridge;
+typedef struct PCIEAERMsg PCIEAERMsg;
+typedef struct PCIEAERLog PCIEAERLog;
+typedef struct PCIEAERErr PCIEAERErr;
+typedef struct PCIEPort PCIEPort;
+typedef struct PCIESlot PCIESlot;
+typedef struct SerialState SerialState;
+typedef struct IRQState *qemu_irq;
+typedef struct PCMCIACardState PCMCIACardState;
+typedef struct MouseTransformInfo MouseTransformInfo;
+typedef struct uWireSlave uWireSlave;
+typedef struct I2SCodec I2SCodec;
+typedef struct SSIBus SSIBus;
+typedef struct EventNotifier EventNotifier;
+typedef struct VirtIODevice VirtIODevice;
+
+typedef uint64_t pcibus_t;
+
+void cpu_exec_init_all(unsigned long tb_size);
+
+/* CPU save/load. */
+void cpu_save(QEMUFile *f, void *opaque);
+int cpu_load(QEMUFile *f, void *opaque, int version_id);
+
+/* Force QEMU to stop what it's doing and service IO */
+void qemu_service_io(void);
+
+/* Force QEMU to process pending events */
+void qemu_notify_event(void);
+
+/* Unblock cpu */
+void qemu_cpu_kick(void *env);
+int qemu_cpu_self(void *env);
+
+/* work queue */
+struct qemu_work_item {
+ struct qemu_work_item *next;
+ void (*func)(void *data);
+ void *data;
+ int done;
+};
+
+#ifdef CONFIG_USER_ONLY
+#define qemu_init_vcpu(env) do { } while (0)
+#else
+void qemu_init_vcpu(void *env);
+#endif
+
+typedef struct QEMUIOVector {
+ struct iovec *iov;
+ int niov;
+ int nalloc;
+ size_t size;
+} QEMUIOVector;
+
+void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
+void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
+void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
+void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip,
+ size_t size);
+void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size);
+void qemu_iovec_destroy(QEMUIOVector *qiov);
+void qemu_iovec_reset(QEMUIOVector *qiov);
+void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf);
+void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count);
+void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count);
+void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count,
+ size_t skip);
+
+struct Monitor;
+typedef struct Monitor Monitor;
+
+/* Convert a byte between binary and BCD. */
+static inline uint8_t to_bcd(uint8_t val)
+{
+ return ((val / 10) << 4) | (val % 10);
+}
+
+static inline uint8_t from_bcd(uint8_t val)
+{
+ return ((val >> 4) * 10) + (val & 0x0f);
+}
+
+/* compute with 96 bit intermediate result: (a*b)/c */
+static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+{
+ union {
+ uint64_t ll;
+ struct {
+#ifdef HOST_WORDS_BIGENDIAN
+ uint32_t high, low;
+#else
+ uint32_t low, high;
+#endif
+ } l;
+ } u, res;
+ uint64_t rl, rh;
+
+ u.ll = a;
+ rl = (uint64_t)u.l.low * (uint64_t)b;
+ rh = (uint64_t)u.l.high * (uint64_t)b;
+ rh += (rl >> 32);
+ res.l.high = rh / c;
+ res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
+ return res.ll;
+}
+
+#include "module.h"
+
+#endif
diff --git a/qemu-config.c b/qemu-config.c
new file mode 100644
index 0000000..6d9c238
--- /dev/null
+++ b/qemu-config.c
@@ -0,0 +1,677 @@
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "sysemu.h"
+#include "hw/qdev.h"
+
+static QemuOptsList qemu_drive_opts = {
+ .name = "drive",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
+ .desc = {
+ {
+ .name = "bus",
+ .type = QEMU_OPT_NUMBER,
+ .help = "bus number",
+ },{
+ .name = "unit",
+ .type = QEMU_OPT_NUMBER,
+ .help = "unit number (i.e. lun for scsi)",
+ },{
+ .name = "if",
+ .type = QEMU_OPT_STRING,
+ .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
+ },{
+ .name = "index",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "cyls",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of cylinders (ide disk geometry)",
+ },{
+ .name = "heads",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of heads (ide disk geometry)",
+ },{
+ .name = "secs",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of sectors (ide disk geometry)",
+ },{
+ .name = "trans",
+ .type = QEMU_OPT_STRING,
+ .help = "chs translation (auto, lba. none)",
+ },{
+ .name = "media",
+ .type = QEMU_OPT_STRING,
+ .help = "media type (disk, cdrom)",
+ },{
+ .name = "snapshot",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "disk image",
+ },{
+ .name = "cache",
+ .type = QEMU_OPT_STRING,
+ .help = "host cache usage (none, writeback, writethrough, unsafe)",
+ },{
+ .name = "aio",
+ .type = QEMU_OPT_STRING,
+ .help = "host AIO implementation (threads, native)",
+ },{
+ .name = "format",
+ .type = QEMU_OPT_STRING,
+ .help = "disk format (raw, qcow2, ...)",
+ },{
+ .name = "serial",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "rerror",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "werror",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "addr",
+ .type = QEMU_OPT_STRING,
+ .help = "pci address (virtio only)",
+ },{
+ .name = "readonly",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "boot",
+ .type = QEMU_OPT_BOOL,
+ .help = "make this a boot drive",
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList qemu_chardev_opts = {
+ .name = "chardev",
+ .implied_opt_name = "backend",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
+ .desc = {
+ {
+ .name = "backend",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "host",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "port",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "localaddr",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "localport",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "to",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "ipv4",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "ipv6",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "wait",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "server",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "delay",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "telnet",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "width",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "height",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "cols",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "rows",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "mux",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "signal",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "name",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "debug",
+ .type = QEMU_OPT_NUMBER,
+ },
+ { /* end of list */ }
+ },
+};
+
+QemuOptsList qemu_fsdev_opts = {
+ .name = "fsdev",
+ .implied_opt_name = "fstype",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_fsdev_opts.head),
+ .desc = {
+ {
+ .name = "fstype",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "security_model",
+ .type = QEMU_OPT_STRING,
+ },
+ { /*End of list */ }
+ },
+};
+
+QemuOptsList qemu_virtfs_opts = {
+ .name = "virtfs",
+ .implied_opt_name = "fstype",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_virtfs_opts.head),
+ .desc = {
+ {
+ .name = "fstype",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "mount_tag",
+ .type = QEMU_OPT_STRING,
+ }, {
+ .name = "security_model",
+ .type = QEMU_OPT_STRING,
+ },
+
+ { /*End of list */ }
+ },
+};
+
+static QemuOptsList qemu_device_opts = {
+ .name = "device",
+ .implied_opt_name = "driver",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
+ .desc = {
+ /*
+ * no elements => accept any
+ * sanity checking will happen later
+ * when setting device properties
+ */
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList qemu_netdev_opts = {
+ .name = "netdev",
+ .implied_opt_name = "type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
+ .desc = {
+ /*
+ * no elements => accept any params
+ * validation will happen later
+ */
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList qemu_net_opts = {
+ .name = "net",
+ .implied_opt_name = "type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
+ .desc = {
+ /*
+ * no elements => accept any params
+ * validation will happen later
+ */
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList qemu_rtc_opts = {
+ .name = "rtc",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
+ .desc = {
+ {
+ .name = "base",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "clock",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "driftfix",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList qemu_global_opts = {
+ .name = "global",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
+ .desc = {
+ {
+ .name = "driver",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "property",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "value",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+static QemuOptsList qemu_mon_opts = {
+ .name = "mon",
+ .implied_opt_name = "chardev",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
+ .desc = {
+ {
+ .name = "mode",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "chardev",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "default",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "pretty",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
+
+#ifdef CONFIG_SIMPLE_TRACE
+static QemuOptsList qemu_trace_opts = {
+ .name = "trace",
+ .implied_opt_name = "trace",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head),
+ .desc = {
+ {
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end if list */ }
+ },
+};
+#endif
+
+static QemuOptsList qemu_cpudef_opts = {
+ .name = "cpudef",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head),
+ .desc = {
+ {
+ .name = "name",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "level",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "vendor",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "family",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "model",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "stepping",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "feature_edx", /* cpuid 0000_0001.edx */
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "feature_ecx", /* cpuid 0000_0001.ecx */
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "extfeature_edx", /* cpuid 8000_0001.edx */
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "extfeature_ecx", /* cpuid 8000_0001.ecx */
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "xlevel",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "model_id",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "vendor_override",
+ .type = QEMU_OPT_NUMBER,
+ },
+ { /* end of list */ }
+ },
+};
+
+QemuOptsList qemu_spice_opts = {
+ .name = "spice",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
+ .desc = {
+ {
+ .name = "port",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "tls-port",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "addr",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "ipv4",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "ipv6",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "password",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "disable-ticketing",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "x509-dir",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "x509-key-file",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "x509-key-password",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "x509-cert-file",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "x509-cacert-file",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "x509-dh-key-file",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "tls-ciphers",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "tls-channel",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "plaintext-channel",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "image-compression",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "jpeg-wan-compression",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "zlib-glz-wan-compression",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "streaming-video",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "agent-mouse",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "playback-compression",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end if list */ }
+ },
+};
+
+QemuOptsList qemu_option_rom_opts = {
+ .name = "option-rom",
+ .implied_opt_name = "romfile",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_option_rom_opts.head),
+ .desc = {
+ {
+ .name = "bootindex",
+ .type = QEMU_OPT_NUMBER,
+ }, {
+ .name = "romfile",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end if list */ }
+ },
+};
+
+static QemuOptsList *vm_config_groups[32] = {
+ &qemu_drive_opts,
+ &qemu_chardev_opts,
+ &qemu_device_opts,
+ &qemu_netdev_opts,
+ &qemu_net_opts,
+ &qemu_rtc_opts,
+ &qemu_global_opts,
+ &qemu_mon_opts,
+ &qemu_cpudef_opts,
+#ifdef CONFIG_SIMPLE_TRACE
+ &qemu_trace_opts,
+#endif
+ &qemu_option_rom_opts,
+ NULL,
+};
+
+static QemuOptsList *find_list(QemuOptsList **lists, const char *group)
+{
+ int i;
+
+ for (i = 0; lists[i] != NULL; i++) {
+ if (strcmp(lists[i]->name, group) == 0)
+ break;
+ }
+ if (lists[i] == NULL) {
+ error_report("there is no option group \"%s\"", group);
+ }
+ return lists[i];
+}
+
+QemuOptsList *qemu_find_opts(const char *group)
+{
+ return find_list(vm_config_groups, group);
+}
+
+void qemu_add_opts(QemuOptsList *list)
+{
+ int entries, i;
+
+ entries = ARRAY_SIZE(vm_config_groups);
+ entries--; /* keep list NULL terminated */
+ for (i = 0; i < entries; i++) {
+ if (vm_config_groups[i] == NULL) {
+ vm_config_groups[i] = list;
+ return;
+ }
+ }
+ fprintf(stderr, "ran out of space in vm_config_groups");
+ abort();
+}
+
+int qemu_set_option(const char *str)
+{
+ char group[64], id[64], arg[64];
+ QemuOptsList *list;
+ QemuOpts *opts;
+ int rc, offset;
+
+ rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset);
+ if (rc < 3 || str[offset] != '=') {
+ error_report("can't parse: \"%s\"", str);
+ return -1;
+ }
+
+ list = qemu_find_opts(group);
+ if (list == NULL) {
+ return -1;
+ }
+
+ opts = qemu_opts_find(list, id);
+ if (!opts) {
+ error_report("there is no %s \"%s\" defined",
+ list->name, id);
+ return -1;
+ }
+
+ if (qemu_opt_set(opts, arg, str+offset+1) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+int qemu_global_option(const char *str)
+{
+ char driver[64], property[64];
+ QemuOpts *opts;
+ int rc, offset;
+
+ rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset);
+ if (rc < 2 || str[offset] != '=') {
+ error_report("can't parse: \"%s\"", str);
+ return -1;
+ }
+
+ opts = qemu_opts_create(&qemu_global_opts, NULL, 0);
+ qemu_opt_set(opts, "driver", driver);
+ qemu_opt_set(opts, "property", property);
+ qemu_opt_set(opts, "value", str+offset+1);
+ return 0;
+}
+
+struct ConfigWriteData {
+ QemuOptsList *list;
+ FILE *fp;
+};
+
+static int config_write_opt(const char *name, const char *value, void *opaque)
+{
+ struct ConfigWriteData *data = opaque;
+
+ fprintf(data->fp, " %s = \"%s\"\n", name, value);
+ return 0;
+}
+
+static int config_write_opts(QemuOpts *opts, void *opaque)
+{
+ struct ConfigWriteData *data = opaque;
+ const char *id = qemu_opts_id(opts);
+
+ if (id) {
+ fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id);
+ } else {
+ fprintf(data->fp, "[%s]\n", data->list->name);
+ }
+ qemu_opt_foreach(opts, config_write_opt, data, 0);
+ fprintf(data->fp, "\n");
+ return 0;
+}
+
+void qemu_config_write(FILE *fp)
+{
+ struct ConfigWriteData data = { .fp = fp };
+ QemuOptsList **lists = vm_config_groups;
+ int i;
+
+ fprintf(fp, "# qemu config file\n\n");
+ for (i = 0; lists[i] != NULL; i++) {
+ data.list = lists[i];
+ qemu_opts_foreach(data.list, config_write_opts, &data, 0);
+ }
+}
+
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname)
+{
+ char line[1024], group[64], id[64], arg[64], value[1024];
+ Location loc;
+ QemuOptsList *list = NULL;
+ QemuOpts *opts = NULL;
+ int res = -1, lno = 0;
+
+ loc_push_none(&loc);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ loc_set_file(fname, ++lno);
+ if (line[0] == '\n') {
+ /* skip empty lines */
+ continue;
+ }
+ if (line[0] == '#') {
+ /* comment */
+ continue;
+ }
+ if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) {
+ /* group with id */
+ list = find_list(lists, group);
+ if (list == NULL)
+ goto out;
+ opts = qemu_opts_create(list, id, 1);
+ continue;
+ }
+ if (sscanf(line, "[%63[^]]]", group) == 1) {
+ /* group without id */
+ list = find_list(lists, group);
+ if (list == NULL)
+ goto out;
+ opts = qemu_opts_create(list, NULL, 0);
+ continue;
+ }
+ if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {
+ /* arg = value */
+ if (opts == NULL) {
+ error_report("no group defined");
+ goto out;
+ }
+ if (qemu_opt_set(opts, arg, value) != 0) {
+ goto out;
+ }
+ continue;
+ }
+ error_report("parse error");
+ goto out;
+ }
+ if (ferror(fp)) {
+ error_report("error reading file");
+ goto out;
+ }
+ res = 0;
+out:
+ loc_pop(&loc);
+ return res;
+}
+
+int qemu_read_config_file(const char *filename)
+{
+ FILE *f = fopen(filename, "r");
+ int ret;
+
+ if (f == NULL) {
+ return -errno;
+ }
+
+ ret = qemu_config_parse(f, vm_config_groups, filename);
+ fclose(f);
+
+ if (ret == 0) {
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
diff --git a/qemu-config.h b/qemu-config.h
new file mode 100644
index 0000000..20d707f
--- /dev/null
+++ b/qemu-config.h
@@ -0,0 +1,19 @@
+#ifndef QEMU_CONFIG_H
+#define QEMU_CONFIG_H
+
+extern QemuOptsList qemu_fsdev_opts;
+extern QemuOptsList qemu_virtfs_opts;
+extern QemuOptsList qemu_spice_opts;
+
+QemuOptsList *qemu_find_opts(const char *group);
+void qemu_add_opts(QemuOptsList *list);
+int qemu_set_option(const char *str);
+int qemu_global_option(const char *str);
+void qemu_add_globals(void);
+
+void qemu_config_write(FILE *fp);
+int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
+
+int qemu_read_config_file(const char *filename);
+
+#endif /* QEMU_CONFIG_H */
diff --git a/qemu-doc.texi b/qemu-doc.texi
new file mode 100644
index 0000000..86e017c
--- /dev/null
+++ b/qemu-doc.texi
@@ -0,0 +1,2713 @@
+\input texinfo @c -*- texinfo -*-
+@c %**start of header
+@setfilename qemu-doc.info
+
+@documentlanguage en
+@documentencoding UTF-8
+
+@settitle QEMU Emulator User Documentation
+@exampleindent 0
+@paragraphindent 0
+@c %**end of header
+
+@ifinfo
+@direntry
+* QEMU: (qemu-doc). The QEMU Emulator User Documentation.
+@end direntry
+@end ifinfo
+
+@iftex
+@titlepage
+@sp 7
+@center @titlefont{QEMU Emulator}
+@sp 1
+@center @titlefont{User Documentation}
+@sp 3
+@end titlepage
+@end iftex
+
+@ifnottex
+@node Top
+@top
+
+@menu
+* Introduction::
+* Installation::
+* QEMU PC System emulator::
+* QEMU System emulator for non PC targets::
+* QEMU User space emulator::
+* compilation:: Compilation from the sources
+* License::
+* Index::
+@end menu
+@end ifnottex
+
+@contents
+
+@node Introduction
+@chapter Introduction
+
+@menu
+* intro_features:: Features
+@end menu
+
+@node intro_features
+@section Features
+
+QEMU is a FAST! processor emulator using dynamic translation to
+achieve good emulation speed.
+
+QEMU has two operating modes:
+
+@itemize
+@cindex operating modes
+
+@item
+@cindex system emulation
+Full system emulation. In this mode, QEMU emulates a full system (for
+example a PC), including one or several processors and various
+peripherals. It can be used to launch different Operating Systems
+without rebooting the PC or to debug system code.
+
+@item
+@cindex user mode emulation
+User mode emulation. In this mode, QEMU can launch
+processes compiled for one CPU on another CPU. It can be used to
+launch the Wine Windows API emulator (@url{http://www.winehq.org}) or
+to ease cross-compilation and cross-debugging.
+
+@end itemize
+
+QEMU can run without an host kernel driver and yet gives acceptable
+performance.
+
+For system emulation, the following hardware targets are supported:
+@itemize
+@cindex emulated target systems
+@cindex supported target systems
+@item PC (x86 or x86_64 processor)
+@item ISA PC (old style PC without PCI bus)
+@item PREP (PowerPC processor)
+@item G3 Beige PowerMac (PowerPC processor)
+@item Mac99 PowerMac (PowerPC processor, in progress)
+@item Sun4m/Sun4c/Sun4d (32-bit Sparc processor)
+@item Sun4u/Sun4v (64-bit Sparc processor, in progress)
+@item Malta board (32-bit and 64-bit MIPS processors)
+@item MIPS Magnum (64-bit MIPS processor)
+@item ARM Integrator/CP (ARM)
+@item ARM Versatile baseboard (ARM)
+@item ARM RealView Emulation/Platform baseboard (ARM)
+@item Spitz, Akita, Borzoi, Terrier and Tosa PDAs (PXA270 processor)
+@item Luminary Micro LM3S811EVB (ARM Cortex-M3)
+@item Luminary Micro LM3S6965EVB (ARM Cortex-M3)
+@item Freescale MCF5208EVB (ColdFire V2).
+@item Arnewsh MCF5206 evaluation board (ColdFire V2).
+@item Palm Tungsten|E PDA (OMAP310 processor)
+@item N800 and N810 tablets (OMAP2420 processor)
+@item MusicPal (MV88W8618 ARM processor)
+@item Gumstix "Connex" and "Verdex" motherboards (PXA255/270).
+@item Siemens SX1 smartphone (OMAP310 processor)
+@item Syborg SVP base model (ARM Cortex-A8).
+@item AXIS-Devboard88 (CRISv32 ETRAX-FS).
+@item Petalogix Spartan 3aDSP1800 MMU ref design (MicroBlaze).
+@end itemize
+
+@cindex supported user mode targets
+For user emulation, x86 (32 and 64 bit), PowerPC (32 and 64 bit),
+ARM, MIPS (32 bit only), Sparc (32 and 64 bit),
+Alpha, ColdFire(m68k), CRISv32 and MicroBlaze CPUs are supported.
+
+@node Installation
+@chapter Installation
+
+If you want to compile QEMU yourself, see @ref{compilation}.
+
+@menu
+* install_linux:: Linux
+* install_windows:: Windows
+* install_mac:: Macintosh
+@end menu
+
+@node install_linux
+@section Linux
+@cindex installation (Linux)
+
+If a precompiled package is available for your distribution - you just
+have to install it. Otherwise, see @ref{compilation}.
+
+@node install_windows
+@section Windows
+@cindex installation (Windows)
+
+Download the experimental binary installer at
+@url{http://www.free.oszoo.org/@/download.html}.
+TODO (no longer available)
+
+@node install_mac
+@section Mac OS X
+
+Download the experimental binary installer at
+@url{http://www.free.oszoo.org/@/download.html}.
+TODO (no longer available)
+
+@node QEMU PC System emulator
+@chapter QEMU PC System emulator
+@cindex system emulation (PC)
+
+@menu
+* pcsys_introduction:: Introduction
+* pcsys_quickstart:: Quick Start
+* sec_invocation:: Invocation
+* pcsys_keys:: Keys
+* pcsys_monitor:: QEMU Monitor
+* disk_images:: Disk Images
+* pcsys_network:: Network emulation
+* pcsys_other_devs:: Other Devices
+* direct_linux_boot:: Direct Linux Boot
+* pcsys_usb:: USB emulation
+* vnc_security:: VNC security
+* gdb_usage:: GDB usage
+* pcsys_os_specific:: Target OS specific information
+@end menu
+
+@node pcsys_introduction
+@section Introduction
+
+@c man begin DESCRIPTION
+
+The QEMU PC System emulator simulates the
+following peripherals:
+
+@itemize @minus
+@item
+i440FX host PCI bridge and PIIX3 PCI to ISA bridge
+@item
+Cirrus CLGD 5446 PCI VGA card or dummy VGA card with Bochs VESA
+extensions (hardware level, including all non standard modes).
+@item
+PS/2 mouse and keyboard
+@item
+2 PCI IDE interfaces with hard disk and CD-ROM support
+@item
+Floppy disk
+@item
+PCI and ISA network adapters
+@item
+Serial ports
+@item
+Creative SoundBlaster 16 sound card
+@item
+ENSONIQ AudioPCI ES1370 sound card
+@item
+Intel 82801AA AC97 Audio compatible sound card
+@item
+Intel HD Audio Controller and HDA codec
+@item
+Adlib (OPL2) - Yamaha YM3812 compatible chip
+@item
+Gravis Ultrasound GF1 sound card
+@item
+CS4231A compatible sound card
+@item
+PCI UHCI USB controller and a virtual USB hub.
+@end itemize
+
+SMP is supported with up to 255 CPUs.
+
+Note that adlib, gus and cs4231a are only available when QEMU was
+configured with --audio-card-list option containing the name(s) of
+required card(s).
+
+QEMU uses the PC BIOS from the Bochs project and the Plex86/Bochs LGPL
+VGA BIOS.
+
+QEMU uses YM3812 emulation by Tatsuyuki Satoh.
+
+QEMU uses GUS emulation (GUSEMU32 @url{http://www.deinmeister.de/gusemu/})
+by Tibor "TS" Schütz.
+
+Not that, by default, GUS shares IRQ(7) with parallel ports and so
+qemu must be told to not have parallel ports to have working GUS
+
+@example
+qemu dos.img -soundhw gus -parallel none
+@end example
+
+Alternatively:
+@example
+qemu dos.img -device gus,irq=5
+@end example
+
+Or some other unclaimed IRQ.
+
+CS4231A is the chip used in Windows Sound System and GUSMAX products
+
+@c man end
+
+@node pcsys_quickstart
+@section Quick Start
+@cindex quick start
+
+Download and uncompress the linux image (@file{linux.img}) and type:
+
+@example
+qemu linux.img
+@end example
+
+Linux should boot and give you a prompt.
+
+@node sec_invocation
+@section Invocation
+
+@example
+@c man begin SYNOPSIS
+usage: qemu [options] [@var{disk_image}]
+@c man end
+@end example
+
+@c man begin OPTIONS
+@var{disk_image} is a raw hard disk image for IDE hard disk 0. Some
+targets do not need a disk image.
+
+@include qemu-options.texi
+
+@c man end
+
+@node pcsys_keys
+@section Keys
+
+@c man begin OPTIONS
+
+During the graphical emulation, you can use the following keys:
+@table @key
+@item Ctrl-Alt-f
+@kindex Ctrl-Alt-f
+Toggle full screen
+
+@item Ctrl-Alt-u
+@kindex Ctrl-Alt-u
+Restore the screen's un-scaled dimensions
+
+@item Ctrl-Alt-n
+@kindex Ctrl-Alt-n
+Switch to virtual console 'n'. Standard console mappings are:
+@table @emph
+@item 1
+Target system display
+@item 2
+Monitor
+@item 3
+Serial port
+@end table
+
+@item Ctrl-Alt
+@kindex Ctrl-Alt
+Toggle mouse and keyboard grab.
+@end table
+
+@kindex Ctrl-Up
+@kindex Ctrl-Down
+@kindex Ctrl-PageUp
+@kindex Ctrl-PageDown
+In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down},
+@key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log.
+
+@kindex Ctrl-a h
+During emulation, if you are using the @option{-nographic} option, use
+@key{Ctrl-a h} to get terminal commands:
+
+@table @key
+@item Ctrl-a h
+@kindex Ctrl-a h
+@item Ctrl-a ?
+@kindex Ctrl-a ?
+Print this help
+@item Ctrl-a x
+@kindex Ctrl-a x
+Exit emulator
+@item Ctrl-a s
+@kindex Ctrl-a s
+Save disk data back to file (if -snapshot)
+@item Ctrl-a t
+@kindex Ctrl-a t
+Toggle console timestamps
+@item Ctrl-a b
+@kindex Ctrl-a b
+Send break (magic sysrq in Linux)
+@item Ctrl-a c
+@kindex Ctrl-a c
+Switch between console and monitor
+@item Ctrl-a Ctrl-a
+@kindex Ctrl-a a
+Send Ctrl-a
+@end table
+@c man end
+
+@ignore
+
+@c man begin SEEALSO
+The HTML documentation of QEMU for more precise information and Linux
+user mode emulator invocation.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
+
+@node pcsys_monitor
+@section QEMU Monitor
+@cindex QEMU monitor
+
+The QEMU monitor is used to give complex commands to the QEMU
+emulator. You can use it to:
+
+@itemize @minus
+
+@item
+Remove or insert removable media images
+(such as CD-ROM or floppies).
+
+@item
+Freeze/unfreeze the Virtual Machine (VM) and save or restore its state
+from a disk file.
+
+@item Inspect the VM state without an external debugger.
+
+@end itemize
+
+@subsection Commands
+
+The following commands are available:
+
+@include qemu-monitor.texi
+
+@subsection Integer expressions
+
+The monitor understands integers expressions for every integer
+argument. You can use register names to get the value of specifics
+CPU registers by prefixing them with @emph{$}.
+
+@node disk_images
+@section Disk Images
+
+Since version 0.6.1, QEMU supports many disk image formats, including
+growable disk images (their size increase as non empty sectors are
+written), compressed and encrypted disk images. Version 0.8.3 added
+the new qcow2 disk image format which is essential to support VM
+snapshots.
+
+@menu
+* disk_images_quickstart:: Quick start for disk image creation
+* disk_images_snapshot_mode:: Snapshot mode
+* vm_snapshots:: VM snapshots
+* qemu_img_invocation:: qemu-img Invocation
+* qemu_nbd_invocation:: qemu-nbd Invocation
+* host_drives:: Using host drives
+* disk_images_fat_images:: Virtual FAT disk images
+* disk_images_nbd:: NBD access
+* disk_images_sheepdog:: Sheepdog disk images
+@end menu
+
+@node disk_images_quickstart
+@subsection Quick start for disk image creation
+
+You can create a disk image with the command:
+@example
+qemu-img create myimage.img mysize
+@end example
+where @var{myimage.img} is the disk image filename and @var{mysize} is its
+size in kilobytes. You can add an @code{M} suffix to give the size in
+megabytes and a @code{G} suffix for gigabytes.
+
+See @ref{qemu_img_invocation} for more information.
+
+@node disk_images_snapshot_mode
+@subsection Snapshot mode
+
+If you use the option @option{-snapshot}, all disk images are
+considered as read only. When sectors in written, they are written in
+a temporary file created in @file{/tmp}. You can however force the
+write back to the raw disk images by using the @code{commit} monitor
+command (or @key{C-a s} in the serial console).
+
+@node vm_snapshots
+@subsection VM snapshots
+
+VM snapshots are snapshots of the complete virtual machine including
+CPU state, RAM, device state and the content of all the writable
+disks. In order to use VM snapshots, you must have at least one non
+removable and writable block device using the @code{qcow2} disk image
+format. Normally this device is the first virtual hard drive.
+
+Use the monitor command @code{savevm} to create a new VM snapshot or
+replace an existing one. A human readable name can be assigned to each
+snapshot in addition to its numerical ID.
+
+Use @code{loadvm} to restore a VM snapshot and @code{delvm} to remove
+a VM snapshot. @code{info snapshots} lists the available snapshots
+with their associated information:
+
+@example
+(qemu) info snapshots
+Snapshot devices: hda
+Snapshot list (from hda):
+ID TAG VM SIZE DATE VM CLOCK
+1 start 41M 2006-08-06 12:38:02 00:00:14.954
+2 40M 2006-08-06 12:43:29 00:00:18.633
+3 msys 40M 2006-08-06 12:44:04 00:00:23.514
+@end example
+
+A VM snapshot is made of a VM state info (its size is shown in
+@code{info snapshots}) and a snapshot of every writable disk image.
+The VM state info is stored in the first @code{qcow2} non removable
+and writable block device. The disk image snapshots are stored in
+every disk image. The size of a snapshot in a disk image is difficult
+to evaluate and is not shown by @code{info snapshots} because the
+associated disk sectors are shared among all the snapshots to save
+disk space (otherwise each snapshot would need a full copy of all the
+disk images).
+
+When using the (unrelated) @code{-snapshot} option
+(@ref{disk_images_snapshot_mode}), you can always make VM snapshots,
+but they are deleted as soon as you exit QEMU.
+
+VM snapshots currently have the following known limitations:
+@itemize
+@item
+They cannot cope with removable devices if they are removed or
+inserted after a snapshot is done.
+@item
+A few device drivers still have incomplete snapshot support so their
+state is not saved or restored properly (in particular USB).
+@end itemize
+
+@node qemu_img_invocation
+@subsection @code{qemu-img} Invocation
+
+@include qemu-img.texi
+
+@node qemu_nbd_invocation
+@subsection @code{qemu-nbd} Invocation
+
+@include qemu-nbd.texi
+
+@node host_drives
+@subsection Using host drives
+
+In addition to disk image files, QEMU can directly access host
+devices. We describe here the usage for QEMU version >= 0.8.3.
+
+@subsubsection Linux
+
+On Linux, you can directly use the host device filename instead of a
+disk image filename provided you have enough privileges to access
+it. For example, use @file{/dev/cdrom} to access to the CDROM or
+@file{/dev/fd0} for the floppy.
+
+@table @code
+@item CD
+You can specify a CDROM device even if no CDROM is loaded. QEMU has
+specific code to detect CDROM insertion or removal. CDROM ejection by
+the guest OS is supported. Currently only data CDs are supported.
+@item Floppy
+You can specify a floppy device even if no floppy is loaded. Floppy
+removal is currently not detected accurately (if you change floppy
+without doing floppy access while the floppy is not loaded, the guest
+OS will think that the same floppy is loaded).
+@item Hard disks
+Hard disks can be used. Normally you must specify the whole disk
+(@file{/dev/hdb} instead of @file{/dev/hdb1}) so that the guest OS can
+see it as a partitioned disk. WARNING: unless you know what you do, it
+is better to only make READ-ONLY accesses to the hard disk otherwise
+you may corrupt your host data (use the @option{-snapshot} command
+line option or modify the device permissions accordingly).
+@end table
+
+@subsubsection Windows
+
+@table @code
+@item CD
+The preferred syntax is the drive letter (e.g. @file{d:}). The
+alternate syntax @file{\\.\d:} is supported. @file{/dev/cdrom} is
+supported as an alias to the first CDROM drive.
+
+Currently there is no specific code to handle removable media, so it
+is better to use the @code{change} or @code{eject} monitor commands to
+change or eject media.
+@item Hard disks
+Hard disks can be used with the syntax: @file{\\.\PhysicalDrive@var{N}}
+where @var{N} is the drive number (0 is the first hard disk).
+
+WARNING: unless you know what you do, it is better to only make
+READ-ONLY accesses to the hard disk otherwise you may corrupt your
+host data (use the @option{-snapshot} command line so that the
+modifications are written in a temporary file).
+@end table
+
+
+@subsubsection Mac OS X
+
+@file{/dev/cdrom} is an alias to the first CDROM.
+
+Currently there is no specific code to handle removable media, so it
+is better to use the @code{change} or @code{eject} monitor commands to
+change or eject media.
+
+@node disk_images_fat_images
+@subsection Virtual FAT disk images
+
+QEMU can automatically create a virtual FAT disk image from a
+directory tree. In order to use it, just type:
+
+@example
+qemu linux.img -hdb fat:/my_directory
+@end example
+
+Then you access access to all the files in the @file{/my_directory}
+directory without having to copy them in a disk image or to export
+them via SAMBA or NFS. The default access is @emph{read-only}.
+
+Floppies can be emulated with the @code{:floppy:} option:
+
+@example
+qemu linux.img -fda fat:floppy:/my_directory
+@end example
+
+A read/write support is available for testing (beta stage) with the
+@code{:rw:} option:
+
+@example
+qemu linux.img -fda fat:floppy:rw:/my_directory
+@end example
+
+What you should @emph{never} do:
+@itemize
+@item use non-ASCII filenames ;
+@item use "-snapshot" together with ":rw:" ;
+@item expect it to work when loadvm'ing ;
+@item write to the FAT directory on the host system while accessing it with the guest system.
+@end itemize
+
+@node disk_images_nbd
+@subsection NBD access
+
+QEMU can access directly to block device exported using the Network Block Device
+protocol.
+
+@example
+qemu linux.img -hdb nbd:my_nbd_server.mydomain.org:1024
+@end example
+
+If the NBD server is located on the same host, you can use an unix socket instead
+of an inet socket:
+
+@example
+qemu linux.img -hdb nbd:unix:/tmp/my_socket
+@end example
+
+In this case, the block device must be exported using qemu-nbd:
+
+@example
+qemu-nbd --socket=/tmp/my_socket my_disk.qcow2
+@end example
+
+The use of qemu-nbd allows to share a disk between several guests:
+@example
+qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2
+@end example
+
+and then you can use it with two guests:
+@example
+qemu linux1.img -hdb nbd:unix:/tmp/my_socket
+qemu linux2.img -hdb nbd:unix:/tmp/my_socket
+@end example
+
+If the nbd-server uses named exports (since NBD 2.9.18), you must use the
+"exportname" option:
+@example
+qemu -cdrom nbd:localhost:exportname=debian-500-ppc-netinst
+qemu -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst
+@end example
+
+@node disk_images_sheepdog
+@subsection Sheepdog disk images
+
+Sheepdog is a distributed storage system for QEMU. It provides highly
+available block level storage volumes that can be attached to
+QEMU-based virtual machines.
+
+You can create a Sheepdog disk image with the command:
+@example
+qemu-img create sheepdog:@var{image} @var{size}
+@end example
+where @var{image} is the Sheepdog image name and @var{size} is its
+size.
+
+To import the existing @var{filename} to Sheepdog, you can use a
+convert command.
+@example
+qemu-img convert @var{filename} sheepdog:@var{image}
+@end example
+
+You can boot from the Sheepdog disk image with the command:
+@example
+qemu sheepdog:@var{image}
+@end example
+
+You can also create a snapshot of the Sheepdog image like qcow2.
+@example
+qemu-img snapshot -c @var{tag} sheepdog:@var{image}
+@end example
+where @var{tag} is a tag name of the newly created snapshot.
+
+To boot from the Sheepdog snapshot, specify the tag name of the
+snapshot.
+@example
+qemu sheepdog:@var{image}:@var{tag}
+@end example
+
+You can create a cloned image from the existing snapshot.
+@example
+qemu-img create -b sheepdog:@var{base}:@var{tag} sheepdog:@var{image}
+@end example
+where @var{base} is a image name of the source snapshot and @var{tag}
+is its tag name.
+
+If the Sheepdog daemon doesn't run on the local host, you need to
+specify one of the Sheepdog servers to connect to.
+@example
+qemu-img create sheepdog:@var{hostname}:@var{port}:@var{image} @var{size}
+qemu sheepdog:@var{hostname}:@var{port}:@var{image}
+@end example
+
+@node pcsys_network
+@section Network emulation
+
+QEMU can simulate several network cards (PCI or ISA cards on the PC
+target) and can connect them to an arbitrary number of Virtual Local
+Area Networks (VLANs). Host TAP devices can be connected to any QEMU
+VLAN. VLAN can be connected between separate instances of QEMU to
+simulate large networks. For simpler usage, a non privileged user mode
+network stack can replace the TAP device to have a basic network
+connection.
+
+@subsection VLANs
+
+QEMU simulates several VLANs. A VLAN can be symbolised as a virtual
+connection between several network devices. These devices can be for
+example QEMU virtual Ethernet cards or virtual Host ethernet devices
+(TAP devices).
+
+@subsection Using TAP network interfaces
+
+This is the standard way to connect QEMU to a real network. QEMU adds
+a virtual network device on your host (called @code{tapN}), and you
+can then configure it as if it was a real ethernet card.
+
+@subsubsection Linux host
+
+As an example, you can download the @file{linux-test-xxx.tar.gz}
+archive and copy the script @file{qemu-ifup} in @file{/etc} and
+configure properly @code{sudo} so that the command @code{ifconfig}
+contained in @file{qemu-ifup} can be executed as root. You must verify
+that your host kernel supports the TAP network interfaces: the
+device @file{/dev/net/tun} must be present.
+
+See @ref{sec_invocation} to have examples of command lines using the
+TAP network interfaces.
+
+@subsubsection Windows host
+
+There is a virtual ethernet driver for Windows 2000/XP systems, called
+TAP-Win32. But it is not included in standard QEMU for Windows,
+so you will need to get it separately. It is part of OpenVPN package,
+so download OpenVPN from : @url{http://openvpn.net/}.
+
+@subsection Using the user mode network stack
+
+By using the option @option{-net user} (default configuration if no
+@option{-net} option is specified), QEMU uses a completely user mode
+network stack (you don't need root privilege to use the virtual
+network). The virtual network configuration is the following:
+
+@example
+
+ QEMU VLAN <------> Firewall/DHCP server <-----> Internet
+ | (10.0.2.2)
+ |
+ ----> DNS server (10.0.2.3)
+ |
+ ----> SMB server (10.0.2.4)
+@end example
+
+The QEMU VM behaves as if it was behind a firewall which blocks all
+incoming connections. You can use a DHCP client to automatically
+configure the network in the QEMU VM. The DHCP server assign addresses
+to the hosts starting from 10.0.2.15.
+
+In order to check that the user mode network is working, you can ping
+the address 10.0.2.2 and verify that you got an address in the range
+10.0.2.x from the QEMU virtual DHCP server.
+
+Note that @code{ping} is not supported reliably to the internet as it
+would require root privileges. It means you can only ping the local
+router (10.0.2.2).
+
+When using the built-in TFTP server, the router is also the TFTP
+server.
+
+When using the @option{-redir} option, TCP or UDP connections can be
+redirected from the host to the guest. It allows for example to
+redirect X11, telnet or SSH connections.
+
+@subsection Connecting VLANs between QEMU instances
+
+Using the @option{-net socket} option, it is possible to make VLANs
+that span several QEMU instances. See @ref{sec_invocation} to have a
+basic example.
+
+@node pcsys_other_devs
+@section Other Devices
+
+@subsection Inter-VM Shared Memory device
+
+With KVM enabled on a Linux host, a shared memory device is available. Guests
+map a POSIX shared memory region into the guest as a PCI device that enables
+zero-copy communication to the application level of the guests. The basic
+syntax is:
+
+@example
+qemu -device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
+@end example
+
+If desired, interrupts can be sent between guest VMs accessing the same shared
+memory region. Interrupt support requires using a shared memory server and
+using a chardev socket to connect to it. The code for the shared memory server
+is qemu.git/contrib/ivshmem-server. An example syntax when using the shared
+memory server is:
+
+@example
+qemu -device ivshmem,size=<size in format accepted by -m>[,chardev=<id>]
+ [,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master]
+qemu -chardev socket,path=<path>,id=<id>
+@end example
+
+When using the server, the guest will be assigned a VM ID (>=0) that allows guests
+using the same server to communicate via interrupts. Guests can read their
+VM ID from a device register (see example code). Since receiving the shared
+memory region from the server is asynchronous, there is a (small) chance the
+guest may boot before the shared memory is attached. To allow an application
+to ensure shared memory is attached, the VM ID register will return -1 (an
+invalid VM ID) until the memory is attached. Once the shared memory is
+attached, the VM ID will return the guest's valid VM ID. With these semantics,
+the guest application can check to ensure the shared memory is attached to the
+guest before proceeding.
+
+The @option{role} argument can be set to either master or peer and will affect
+how the shared memory is migrated. With @option{role=master}, the guest will
+copy the shared memory on migration to the destination host. With
+@option{role=peer}, the guest will not be able to migrate with the device attached.
+With the @option{peer} case, the device should be detached and then reattached
+after migration using the PCI hotplug support.
+
+@node direct_linux_boot
+@section Direct Linux Boot
+
+This section explains how to launch a Linux kernel inside QEMU without
+having to make a full bootable image. It is very useful for fast Linux
+kernel testing.
+
+The syntax is:
+@example
+qemu -kernel arch/i386/boot/bzImage -hda root-2.4.20.img -append "root=/dev/hda"
+@end example
+
+Use @option{-kernel} to provide the Linux kernel image and
+@option{-append} to give the kernel command line arguments. The
+@option{-initrd} option can be used to provide an INITRD image.
+
+When using the direct Linux boot, a disk image for the first hard disk
+@file{hda} is required because its boot sector is used to launch the
+Linux kernel.
+
+If you do not need graphical output, you can disable it and redirect
+the virtual serial port and the QEMU monitor to the console with the
+@option{-nographic} option. The typical command line is:
+@example
+qemu -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \
+ -append "root=/dev/hda console=ttyS0" -nographic
+@end example
+
+Use @key{Ctrl-a c} to switch between the serial console and the
+monitor (@pxref{pcsys_keys}).
+
+@node pcsys_usb
+@section USB emulation
+
+QEMU emulates a PCI UHCI USB controller. You can virtually plug
+virtual USB devices or real host USB devices (experimental, works only
+on Linux hosts). Qemu will automatically create and connect virtual USB hubs
+as necessary to connect multiple USB devices.
+
+@menu
+* usb_devices::
+* host_usb_devices::
+@end menu
+@node usb_devices
+@subsection Connecting USB devices
+
+USB devices can be connected with the @option{-usbdevice} commandline option
+or the @code{usb_add} monitor command. Available devices are:
+
+@table @code
+@item mouse
+Virtual Mouse. This will override the PS/2 mouse emulation when activated.
+@item tablet
+Pointer device that uses absolute coordinates (like a touchscreen).
+This means qemu is able to report the mouse position without having
+to grab the mouse. Also overrides the PS/2 mouse emulation when activated.
+@item disk:@var{file}
+Mass storage device based on @var{file} (@pxref{disk_images})
+@item host:@var{bus.addr}
+Pass through the host device identified by @var{bus.addr}
+(Linux only)
+@item host:@var{vendor_id:product_id}
+Pass through the host device identified by @var{vendor_id:product_id}
+(Linux only)
+@item wacom-tablet
+Virtual Wacom PenPartner tablet. This device is similar to the @code{tablet}
+above but it can be used with the tslib library because in addition to touch
+coordinates it reports touch pressure.
+@item keyboard
+Standard USB keyboard. Will override the PS/2 keyboard (if present).
+@item serial:[vendorid=@var{vendor_id}][,product_id=@var{product_id}]:@var{dev}
+Serial converter. This emulates an FTDI FT232BM chip connected to host character
+device @var{dev}. The available character devices are the same as for the
+@code{-serial} option. The @code{vendorid} and @code{productid} options can be
+used to override the default 0403:6001. For instance,
+@example
+usb_add serial:productid=FA00:tcp:192.168.0.2:4444
+@end example
+will connect to tcp port 4444 of ip 192.168.0.2, and plug that to the virtual
+serial converter, faking a Matrix Orbital LCD Display (USB ID 0403:FA00).
+@item braille
+Braille device. This will use BrlAPI to display the braille output on a real
+or fake device.
+@item net:@var{options}
+Network adapter that supports CDC ethernet and RNDIS protocols. @var{options}
+specifies NIC options as with @code{-net nic,}@var{options} (see description).
+For instance, user-mode networking can be used with
+@example
+qemu [...OPTIONS...] -net user,vlan=0 -usbdevice net:vlan=0
+@end example
+Currently this cannot be used in machines that support PCI NICs.
+@item bt[:@var{hci-type}]
+Bluetooth dongle whose type is specified in the same format as with
+the @option{-bt hci} option, @pxref{bt-hcis,,allowed HCI types}. If
+no type is given, the HCI logic corresponds to @code{-bt hci,vlan=0}.
+This USB device implements the USB Transport Layer of HCI. Example
+usage:
+@example
+qemu [...OPTIONS...] -usbdevice bt:hci,vlan=3 -bt device:keyboard,vlan=3
+@end example
+@end table
+
+@node host_usb_devices
+@subsection Using host USB devices on a Linux host
+
+WARNING: this is an experimental feature. QEMU will slow down when
+using it. USB devices requiring real time streaming (i.e. USB Video
+Cameras) are not supported yet.
+
+@enumerate
+@item If you use an early Linux 2.4 kernel, verify that no Linux driver
+is actually using the USB device. A simple way to do that is simply to
+disable the corresponding kernel module by renaming it from @file{mydriver.o}
+to @file{mydriver.o.disabled}.
+
+@item Verify that @file{/proc/bus/usb} is working (most Linux distributions should enable it by default). You should see something like that:
+@example
+ls /proc/bus/usb
+001 devices drivers
+@end example
+
+@item Since only root can access to the USB devices directly, you can either launch QEMU as root or change the permissions of the USB devices you want to use. For testing, the following suffices:
+@example
+chown -R myuid /proc/bus/usb
+@end example
+
+@item Launch QEMU and do in the monitor:
+@example
+info usbhost
+ Device 1.2, speed 480 Mb/s
+ Class 00: USB device 1234:5678, USB DISK
+@end example
+You should see the list of the devices you can use (Never try to use
+hubs, it won't work).
+
+@item Add the device in QEMU by using:
+@example
+usb_add host:1234:5678
+@end example
+
+Normally the guest OS should report that a new USB device is
+plugged. You can use the option @option{-usbdevice} to do the same.
+
+@item Now you can try to use the host USB device in QEMU.
+
+@end enumerate
+
+When relaunching QEMU, you may have to unplug and plug again the USB
+device to make it work again (this is a bug).
+
+@node vnc_security
+@section VNC security
+
+The VNC server capability provides access to the graphical console
+of the guest VM across the network. This has a number of security
+considerations depending on the deployment scenarios.
+
+@menu
+* vnc_sec_none::
+* vnc_sec_password::
+* vnc_sec_certificate::
+* vnc_sec_certificate_verify::
+* vnc_sec_certificate_pw::
+* vnc_sec_sasl::
+* vnc_sec_certificate_sasl::
+* vnc_generate_cert::
+* vnc_setup_sasl::
+@end menu
+@node vnc_sec_none
+@subsection Without passwords
+
+The simplest VNC server setup does not include any form of authentication.
+For this setup it is recommended to restrict it to listen on a UNIX domain
+socket only. For example
+
+@example
+qemu [...OPTIONS...] -vnc unix:/home/joebloggs/.qemu-myvm-vnc
+@end example
+
+This ensures that only users on local box with read/write access to that
+path can access the VNC server. To securely access the VNC server from a
+remote machine, a combination of netcat+ssh can be used to provide a secure
+tunnel.
+
+@node vnc_sec_password
+@subsection With passwords
+
+The VNC protocol has limited support for password based authentication. Since
+the protocol limits passwords to 8 characters it should not be considered
+to provide high security. The password can be fairly easily brute-forced by
+a client making repeat connections. For this reason, a VNC server using password
+authentication should be restricted to only listen on the loopback interface
+or UNIX domain sockets. Password authentication is requested with the @code{password}
+option, and then once QEMU is running the password is set with the monitor. Until
+the monitor is used to set the password all clients will be rejected.
+
+@example
+qemu [...OPTIONS...] -vnc :1,password -monitor stdio
+(qemu) change vnc password
+Password: ********
+(qemu)
+@end example
+
+@node vnc_sec_certificate
+@subsection With x509 certificates
+
+The QEMU VNC server also implements the VeNCrypt extension allowing use of
+TLS for encryption of the session, and x509 certificates for authentication.
+The use of x509 certificates is strongly recommended, because TLS on its
+own is susceptible to man-in-the-middle attacks. Basic x509 certificate
+support provides a secure session, but no authentication. This allows any
+client to connect, and provides an encrypted session.
+
+@example
+qemu [...OPTIONS...] -vnc :1,tls,x509=/etc/pki/qemu -monitor stdio
+@end example
+
+In the above example @code{/etc/pki/qemu} should contain at least three files,
+@code{ca-cert.pem}, @code{server-cert.pem} and @code{server-key.pem}. Unprivileged
+users will want to use a private directory, for example @code{$HOME/.pki/qemu}.
+NB the @code{server-key.pem} file should be protected with file mode 0600 to
+only be readable by the user owning it.
+
+@node vnc_sec_certificate_verify
+@subsection With x509 certificates and client verification
+
+Certificates can also provide a means to authenticate the client connecting.
+The server will request that the client provide a certificate, which it will
+then validate against the CA certificate. This is a good choice if deploying
+in an environment with a private internal certificate authority.
+
+@example
+qemu [...OPTIONS...] -vnc :1,tls,x509verify=/etc/pki/qemu -monitor stdio
+@end example
+
+
+@node vnc_sec_certificate_pw
+@subsection With x509 certificates, client verification and passwords
+
+Finally, the previous method can be combined with VNC password authentication
+to provide two layers of authentication for clients.
+
+@example
+qemu [...OPTIONS...] -vnc :1,password,tls,x509verify=/etc/pki/qemu -monitor stdio
+(qemu) change vnc password
+Password: ********
+(qemu)
+@end example
+
+
+@node vnc_sec_sasl
+@subsection With SASL authentication
+
+The SASL authentication method is a VNC extension, that provides an
+easily extendable, pluggable authentication method. This allows for
+integration with a wide range of authentication mechanisms, such as
+PAM, GSSAPI/Kerberos, LDAP, SQL databases, one-time keys and more.
+The strength of the authentication depends on the exact mechanism
+configured. If the chosen mechanism also provides a SSF layer, then
+it will encrypt the datastream as well.
+
+Refer to the later docs on how to choose the exact SASL mechanism
+used for authentication, but assuming use of one supporting SSF,
+then QEMU can be launched with:
+
+@example
+qemu [...OPTIONS...] -vnc :1,sasl -monitor stdio
+@end example
+
+@node vnc_sec_certificate_sasl
+@subsection With x509 certificates and SASL authentication
+
+If the desired SASL authentication mechanism does not supported
+SSF layers, then it is strongly advised to run it in combination
+with TLS and x509 certificates. This provides securely encrypted
+data stream, avoiding risk of compromising of the security
+credentials. This can be enabled, by combining the 'sasl' option
+with the aforementioned TLS + x509 options:
+
+@example
+qemu [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio
+@end example
+
+
+@node vnc_generate_cert
+@subsection Generating certificates for VNC
+
+The GNU TLS packages provides a command called @code{certtool} which can
+be used to generate certificates and keys in PEM format. At a minimum it
+is necessary to setup a certificate authority, and issue certificates to
+each server. If using certificates for authentication, then each client
+will also need to be issued a certificate. The recommendation is for the
+server to keep its certificates in either @code{/etc/pki/qemu} or for
+unprivileged users in @code{$HOME/.pki/qemu}.
+
+@menu
+* vnc_generate_ca::
+* vnc_generate_server::
+* vnc_generate_client::
+@end menu
+@node vnc_generate_ca
+@subsubsection Setup the Certificate Authority
+
+This step only needs to be performed once per organization / organizational
+unit. First the CA needs a private key. This key must be kept VERY secret
+and secure. If this key is compromised the entire trust chain of the certificates
+issued with it is lost.
+
+@example
+# certtool --generate-privkey > ca-key.pem
+@end example
+
+A CA needs to have a public certificate. For simplicity it can be a self-signed
+certificate, or one issue by a commercial certificate issuing authority. To
+generate a self-signed certificate requires one core piece of information, the
+name of the organization.
+
+@example
+# cat > ca.info <<EOF
+cn = Name of your organization
+ca
+cert_signing_key
+EOF
+# certtool --generate-self-signed \
+ --load-privkey ca-key.pem
+ --template ca.info \
+ --outfile ca-cert.pem
+@end example
+
+The @code{ca-cert.pem} file should be copied to all servers and clients wishing to utilize
+TLS support in the VNC server. The @code{ca-key.pem} must not be disclosed/copied at all.
+
+@node vnc_generate_server
+@subsubsection Issuing server certificates
+
+Each server (or host) needs to be issued with a key and certificate. When connecting
+the certificate is sent to the client which validates it against the CA certificate.
+The core piece of information for a server certificate is the hostname. This should
+be the fully qualified hostname that the client will connect with, since the client
+will typically also verify the hostname in the certificate. On the host holding the
+secure CA private key:
+
+@example
+# cat > server.info <<EOF
+organization = Name of your organization
+cn = server.foo.example.com
+tls_www_server
+encryption_key
+signing_key
+EOF
+# certtool --generate-privkey > server-key.pem
+# certtool --generate-certificate \
+ --load-ca-certificate ca-cert.pem \
+ --load-ca-privkey ca-key.pem \
+ --load-privkey server server-key.pem \
+ --template server.info \
+ --outfile server-cert.pem
+@end example
+
+The @code{server-key.pem} and @code{server-cert.pem} files should now be securely copied
+to the server for which they were generated. The @code{server-key.pem} is security
+sensitive and should be kept protected with file mode 0600 to prevent disclosure.
+
+@node vnc_generate_client
+@subsubsection Issuing client certificates
+
+If the QEMU VNC server is to use the @code{x509verify} option to validate client
+certificates as its authentication mechanism, each client also needs to be issued
+a certificate. The client certificate contains enough metadata to uniquely identify
+the client, typically organization, state, city, building, etc. On the host holding
+the secure CA private key:
+
+@example
+# cat > client.info <<EOF
+country = GB
+state = London
+locality = London
+organiazation = Name of your organization
+cn = client.foo.example.com
+tls_www_client
+encryption_key
+signing_key
+EOF
+# certtool --generate-privkey > client-key.pem
+# certtool --generate-certificate \
+ --load-ca-certificate ca-cert.pem \
+ --load-ca-privkey ca-key.pem \
+ --load-privkey client-key.pem \
+ --template client.info \
+ --outfile client-cert.pem
+@end example
+
+The @code{client-key.pem} and @code{client-cert.pem} files should now be securely
+copied to the client for which they were generated.
+
+
+@node vnc_setup_sasl
+
+@subsection Configuring SASL mechanisms
+
+The following documentation assumes use of the Cyrus SASL implementation on a
+Linux host, but the principals should apply to any other SASL impl. When SASL
+is enabled, the mechanism configuration will be loaded from system default
+SASL service config /etc/sasl2/qemu.conf. If running QEMU as an
+unprivileged user, an environment variable SASL_CONF_PATH can be used
+to make it search alternate locations for the service config.
+
+The default configuration might contain
+
+@example
+mech_list: digest-md5
+sasldb_path: /etc/qemu/passwd.db
+@end example
+
+This says to use the 'Digest MD5' mechanism, which is similar to the HTTP
+Digest-MD5 mechanism. The list of valid usernames & passwords is maintained
+in the /etc/qemu/passwd.db file, and can be updated using the saslpasswd2
+command. While this mechanism is easy to configure and use, it is not
+considered secure by modern standards, so only suitable for developers /
+ad-hoc testing.
+
+A more serious deployment might use Kerberos, which is done with the 'gssapi'
+mechanism
+
+@example
+mech_list: gssapi
+keytab: /etc/qemu/krb5.tab
+@end example
+
+For this to work the administrator of your KDC must generate a Kerberos
+principal for the server, with a name of 'qemu/somehost.example.com@@EXAMPLE.COM'
+replacing 'somehost.example.com' with the fully qualified host name of the
+machine running QEMU, and 'EXAMPLE.COM' with the Kerberos Realm.
+
+Other configurations will be left as an exercise for the reader. It should
+be noted that only Digest-MD5 and GSSAPI provides a SSF layer for data
+encryption. For all other mechanisms, VNC should always be configured to
+use TLS and x509 certificates to protect security credentials from snooping.
+
+@node gdb_usage
+@section GDB usage
+
+QEMU has a primitive support to work with gdb, so that you can do
+'Ctrl-C' while the virtual machine is running and inspect its state.
+
+In order to use gdb, launch qemu with the '-s' option. It will wait for a
+gdb connection:
+@example
+> qemu -s -kernel arch/i386/boot/bzImage -hda root-2.4.20.img \
+ -append "root=/dev/hda"
+Connected to host network interface: tun0
+Waiting gdb connection on port 1234
+@end example
+
+Then launch gdb on the 'vmlinux' executable:
+@example
+> gdb vmlinux
+@end example
+
+In gdb, connect to QEMU:
+@example
+(gdb) target remote localhost:1234
+@end example
+
+Then you can use gdb normally. For example, type 'c' to launch the kernel:
+@example
+(gdb) c
+@end example
+
+Here are some useful tips in order to use gdb on system code:
+
+@enumerate
+@item
+Use @code{info reg} to display all the CPU registers.
+@item
+Use @code{x/10i $eip} to display the code at the PC position.
+@item
+Use @code{set architecture i8086} to dump 16 bit code. Then use
+@code{x/10i $cs*16+$eip} to dump the code at the PC position.
+@end enumerate
+
+Advanced debugging options:
+
+The default single stepping behavior is step with the IRQs and timer service routines off. It is set this way because when gdb executes a single step it expects to advance beyond the current instruction. With the IRQs and and timer service routines on, a single step might jump into the one of the interrupt or exception vectors instead of executing the current instruction. This means you may hit the same breakpoint a number of times before executing the instruction gdb wants to have executed. Because there are rare circumstances where you want to single step into an interrupt vector the behavior can be controlled from GDB. There are three commands you can query and set the single step behavior:
+@table @code
+@item maintenance packet qqemu.sstepbits
+
+This will display the MASK bits used to control the single stepping IE:
+@example
+(gdb) maintenance packet qqemu.sstepbits
+sending: "qqemu.sstepbits"
+received: "ENABLE=1,NOIRQ=2,NOTIMER=4"
+@end example
+@item maintenance packet qqemu.sstep
+
+This will display the current value of the mask used when single stepping IE:
+@example
+(gdb) maintenance packet qqemu.sstep
+sending: "qqemu.sstep"
+received: "0x7"
+@end example
+@item maintenance packet Qqemu.sstep=HEX_VALUE
+
+This will change the single step mask, so if wanted to enable IRQs on the single step, but not timers, you would use:
+@example
+(gdb) maintenance packet Qqemu.sstep=0x5
+sending: "qemu.sstep=0x5"
+received: "OK"
+@end example
+@end table
+
+@node pcsys_os_specific
+@section Target OS specific information
+
+@subsection Linux
+
+To have access to SVGA graphic modes under X11, use the @code{vesa} or
+the @code{cirrus} X11 driver. For optimal performances, use 16 bit
+color depth in the guest and the host OS.
+
+When using a 2.6 guest Linux kernel, you should add the option
+@code{clock=pit} on the kernel command line because the 2.6 Linux
+kernels make very strict real time clock checks by default that QEMU
+cannot simulate exactly.
+
+When using a 2.6 guest Linux kernel, verify that the 4G/4G patch is
+not activated because QEMU is slower with this patch. The QEMU
+Accelerator Module is also much slower in this case. Earlier Fedora
+Core 3 Linux kernel (< 2.6.9-1.724_FC3) were known to incorporate this
+patch by default. Newer kernels don't have it.
+
+@subsection Windows
+
+If you have a slow host, using Windows 95 is better as it gives the
+best speed. Windows 2000 is also a good choice.
+
+@subsubsection SVGA graphic modes support
+
+QEMU emulates a Cirrus Logic GD5446 Video
+card. All Windows versions starting from Windows 95 should recognize
+and use this graphic card. For optimal performances, use 16 bit color
+depth in the guest and the host OS.
+
+If you are using Windows XP as guest OS and if you want to use high
+resolution modes which the Cirrus Logic BIOS does not support (i.e. >=
+1280x1024x16), then you should use the VESA VBE virtual graphic card
+(option @option{-std-vga}).
+
+@subsubsection CPU usage reduction
+
+Windows 9x does not correctly use the CPU HLT
+instruction. The result is that it takes host CPU cycles even when
+idle. You can install the utility from
+@url{http://www.user.cityline.ru/~maxamn/amnhltm.zip} to solve this
+problem. Note that no such tool is needed for NT, 2000 or XP.
+
+@subsubsection Windows 2000 disk full problem
+
+Windows 2000 has a bug which gives a disk full problem during its
+installation. When installing it, use the @option{-win2k-hack} QEMU
+option to enable a specific workaround. After Windows 2000 is
+installed, you no longer need this option (this option slows down the
+IDE transfers).
+
+@subsubsection Windows 2000 shutdown
+
+Windows 2000 cannot automatically shutdown in QEMU although Windows 98
+can. It comes from the fact that Windows 2000 does not automatically
+use the APM driver provided by the BIOS.
+
+In order to correct that, do the following (thanks to Struan
+Bartlett): go to the Control Panel => Add/Remove Hardware & Next =>
+Add/Troubleshoot a device => Add a new device & Next => No, select the
+hardware from a list & Next => NT Apm/Legacy Support & Next => Next
+(again) a few times. Now the driver is installed and Windows 2000 now
+correctly instructs QEMU to shutdown at the appropriate moment.
+
+@subsubsection Share a directory between Unix and Windows
+
+See @ref{sec_invocation} about the help of the option @option{-smb}.
+
+@subsubsection Windows XP security problem
+
+Some releases of Windows XP install correctly but give a security
+error when booting:
+@example
+A problem is preventing Windows from accurately checking the
+license for this computer. Error code: 0x800703e6.
+@end example
+
+The workaround is to install a service pack for XP after a boot in safe
+mode. Then reboot, and the problem should go away. Since there is no
+network while in safe mode, its recommended to download the full
+installation of SP1 or SP2 and transfer that via an ISO or using the
+vvfat block device ("-hdb fat:directory_which_holds_the_SP").
+
+@subsection MS-DOS and FreeDOS
+
+@subsubsection CPU usage reduction
+
+DOS does not correctly use the CPU HLT instruction. The result is that
+it takes host CPU cycles even when idle. You can install the utility
+from @url{http://www.vmware.com/software/dosidle210.zip} to solve this
+problem.
+
+@node QEMU System emulator for non PC targets
+@chapter QEMU System emulator for non PC targets
+
+QEMU is a generic emulator and it emulates many non PC
+machines. Most of the options are similar to the PC emulator. The
+differences are mentioned in the following sections.
+
+@menu
+* PowerPC System emulator::
+* Sparc32 System emulator::
+* Sparc64 System emulator::
+* MIPS System emulator::
+* ARM System emulator::
+* ColdFire System emulator::
+* Cris System emulator::
+* Microblaze System emulator::
+* SH4 System emulator::
+@end menu
+
+@node PowerPC System emulator
+@section PowerPC System emulator
+@cindex system emulation (PowerPC)
+
+Use the executable @file{qemu-system-ppc} to simulate a complete PREP
+or PowerMac PowerPC system.
+
+QEMU emulates the following PowerMac peripherals:
+
+@itemize @minus
+@item
+UniNorth or Grackle PCI Bridge
+@item
+PCI VGA compatible card with VESA Bochs Extensions
+@item
+2 PMAC IDE interfaces with hard disk and CD-ROM support
+@item
+NE2000 PCI adapters
+@item
+Non Volatile RAM
+@item
+VIA-CUDA with ADB keyboard and mouse.
+@end itemize
+
+QEMU emulates the following PREP peripherals:
+
+@itemize @minus
+@item
+PCI Bridge
+@item
+PCI VGA compatible card with VESA Bochs Extensions
+@item
+2 IDE interfaces with hard disk and CD-ROM support
+@item
+Floppy disk
+@item
+NE2000 network adapters
+@item
+Serial port
+@item
+PREP Non Volatile RAM
+@item
+PC compatible keyboard and mouse.
+@end itemize
+
+QEMU uses the Open Hack'Ware Open Firmware Compatible BIOS available at
+@url{http://perso.magic.fr/l_indien/OpenHackWare/index.htm}.
+
+Since version 0.9.1, QEMU uses OpenBIOS @url{http://www.openbios.org/}
+for the g3beige and mac99 PowerMac machines. OpenBIOS is a free (GPL
+v2) portable firmware implementation. The goal is to implement a 100%
+IEEE 1275-1994 (referred to as Open Firmware) compliant firmware.
+
+@c man begin OPTIONS
+
+The following options are specific to the PowerPC emulation:
+
+@table @option
+
+@item -g @var{W}x@var{H}[x@var{DEPTH}]
+
+Set the initial VGA graphic mode. The default is 800x600x15.
+
+@item -prom-env @var{string}
+
+Set OpenBIOS variables in NVRAM, for example:
+
+@example
+qemu-system-ppc -prom-env 'auto-boot?=false' \
+ -prom-env 'boot-device=hd:2,\yaboot' \
+ -prom-env 'boot-args=conf=hd:2,\yaboot.conf'
+@end example
+
+These variables are not used by Open Hack'Ware.
+
+@end table
+
+@c man end
+
+
+More information is available at
+@url{http://perso.magic.fr/l_indien/qemu-ppc/}.
+
+@node Sparc32 System emulator
+@section Sparc32 System emulator
+@cindex system emulation (Sparc32)
+
+Use the executable @file{qemu-system-sparc} to simulate the following
+Sun4m architecture machines:
+@itemize @minus
+@item
+SPARCstation 4
+@item
+SPARCstation 5
+@item
+SPARCstation 10
+@item
+SPARCstation 20
+@item
+SPARCserver 600MP
+@item
+SPARCstation LX
+@item
+SPARCstation Voyager
+@item
+SPARCclassic
+@item
+SPARCbook
+@end itemize
+
+The emulation is somewhat complete. SMP up to 16 CPUs is supported,
+but Linux limits the number of usable CPUs to 4.
+
+It's also possible to simulate a SPARCstation 2 (sun4c architecture),
+SPARCserver 1000, or SPARCcenter 2000 (sun4d architecture), but these
+emulators are not usable yet.
+
+QEMU emulates the following sun4m/sun4c/sun4d peripherals:
+
+@itemize @minus
+@item
+IOMMU or IO-UNITs
+@item
+TCX Frame buffer
+@item
+Lance (Am7990) Ethernet
+@item
+Non Volatile RAM M48T02/M48T08
+@item
+Slave I/O: timers, interrupt controllers, Zilog serial ports, keyboard
+and power/reset logic
+@item
+ESP SCSI controller with hard disk and CD-ROM support
+@item
+Floppy drive (not on SS-600MP)
+@item
+CS4231 sound device (only on SS-5, not working yet)
+@end itemize
+
+The number of peripherals is fixed in the architecture. Maximum
+memory size depends on the machine type, for SS-5 it is 256MB and for
+others 2047MB.
+
+Since version 0.8.2, QEMU uses OpenBIOS
+@url{http://www.openbios.org/}. OpenBIOS is a free (GPL v2) portable
+firmware implementation. The goal is to implement a 100% IEEE
+1275-1994 (referred to as Open Firmware) compliant firmware.
+
+A sample Linux 2.6 series kernel and ram disk image are available on
+the QEMU web site. There are still issues with NetBSD and OpenBSD, but
+some kernel versions work. Please note that currently Solaris kernels
+don't work probably due to interface issues between OpenBIOS and
+Solaris.
+
+@c man begin OPTIONS
+
+The following options are specific to the Sparc32 emulation:
+
+@table @option
+
+@item -g @var{W}x@var{H}x[x@var{DEPTH}]
+
+Set the initial TCX graphic mode. The default is 1024x768x8, currently
+the only other possible mode is 1024x768x24.
+
+@item -prom-env @var{string}
+
+Set OpenBIOS variables in NVRAM, for example:
+
+@example
+qemu-system-sparc -prom-env 'auto-boot?=false' \
+ -prom-env 'boot-device=sd(0,2,0):d' -prom-env 'boot-args=linux single'
+@end example
+
+@item -M [SS-4|SS-5|SS-10|SS-20|SS-600MP|LX|Voyager|SPARCClassic] [|SPARCbook|SS-2|SS-1000|SS-2000]
+
+Set the emulated machine type. Default is SS-5.
+
+@end table
+
+@c man end
+
+@node Sparc64 System emulator
+@section Sparc64 System emulator
+@cindex system emulation (Sparc64)
+
+Use the executable @file{qemu-system-sparc64} to simulate a Sun4u
+(UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic
+Niagara (T1) machine. The emulator is not usable for anything yet, but
+it can launch some kernels.
+
+QEMU emulates the following peripherals:
+
+@itemize @minus
+@item
+UltraSparc IIi APB PCI Bridge
+@item
+PCI VGA compatible card with VESA Bochs Extensions
+@item
+PS/2 mouse and keyboard
+@item
+Non Volatile RAM M48T59
+@item
+PC-compatible serial ports
+@item
+2 PCI IDE interfaces with hard disk and CD-ROM support
+@item
+Floppy disk
+@end itemize
+
+@c man begin OPTIONS
+
+The following options are specific to the Sparc64 emulation:
+
+@table @option
+
+@item -prom-env @var{string}
+
+Set OpenBIOS variables in NVRAM, for example:
+
+@example
+qemu-system-sparc64 -prom-env 'auto-boot?=false'
+@end example
+
+@item -M [sun4u|sun4v|Niagara]
+
+Set the emulated machine type. The default is sun4u.
+
+@end table
+
+@c man end
+
+@node MIPS System emulator
+@section MIPS System emulator
+@cindex system emulation (MIPS)
+
+Four executables cover simulation of 32 and 64-bit MIPS systems in
+both endian options, @file{qemu-system-mips}, @file{qemu-system-mipsel}
+@file{qemu-system-mips64} and @file{qemu-system-mips64el}.
+Five different machine types are emulated:
+
+@itemize @minus
+@item
+A generic ISA PC-like machine "mips"
+@item
+The MIPS Malta prototype board "malta"
+@item
+An ACER Pica "pica61". This machine needs the 64-bit emulator.
+@item
+MIPS emulator pseudo board "mipssim"
+@item
+A MIPS Magnum R4000 machine "magnum". This machine needs the 64-bit emulator.
+@end itemize
+
+The generic emulation is supported by Debian 'Etch' and is able to
+install Debian into a virtual disk image. The following devices are
+emulated:
+
+@itemize @minus
+@item
+A range of MIPS CPUs, default is the 24Kf
+@item
+PC style serial port
+@item
+PC style IDE disk
+@item
+NE2000 network card
+@end itemize
+
+The Malta emulation supports the following devices:
+
+@itemize @minus
+@item
+Core board with MIPS 24Kf CPU and Galileo system controller
+@item
+PIIX4 PCI/USB/SMbus controller
+@item
+The Multi-I/O chip's serial device
+@item
+PCI network cards (PCnet32 and others)
+@item
+Malta FPGA serial device
+@item
+Cirrus (default) or any other PCI VGA graphics card
+@end itemize
+
+The ACER Pica emulation supports:
+
+@itemize @minus
+@item
+MIPS R4000 CPU
+@item
+PC-style IRQ and DMA controllers
+@item
+PC Keyboard
+@item
+IDE controller
+@end itemize
+
+The mipssim pseudo board emulation provides an environment similiar
+to what the proprietary MIPS emulator uses for running Linux.
+It supports:
+
+@itemize @minus
+@item
+A range of MIPS CPUs, default is the 24Kf
+@item
+PC style serial port
+@item
+MIPSnet network emulation
+@end itemize
+
+The MIPS Magnum R4000 emulation supports:
+
+@itemize @minus
+@item
+MIPS R4000 CPU
+@item
+PC-style IRQ controller
+@item
+PC Keyboard
+@item
+SCSI controller
+@item
+G364 framebuffer
+@end itemize
+
+
+@node ARM System emulator
+@section ARM System emulator
+@cindex system emulation (ARM)
+
+Use the executable @file{qemu-system-arm} to simulate a ARM
+machine. The ARM Integrator/CP board is emulated with the following
+devices:
+
+@itemize @minus
+@item
+ARM926E, ARM1026E, ARM946E, ARM1136 or Cortex-A8 CPU
+@item
+Two PL011 UARTs
+@item
+SMC 91c111 Ethernet adapter
+@item
+PL110 LCD controller
+@item
+PL050 KMI with PS/2 keyboard and mouse.
+@item
+PL181 MultiMedia Card Interface with SD card.
+@end itemize
+
+The ARM Versatile baseboard is emulated with the following devices:
+
+@itemize @minus
+@item
+ARM926E, ARM1136 or Cortex-A8 CPU
+@item
+PL190 Vectored Interrupt Controller
+@item
+Four PL011 UARTs
+@item
+SMC 91c111 Ethernet adapter
+@item
+PL110 LCD controller
+@item
+PL050 KMI with PS/2 keyboard and mouse.
+@item
+PCI host bridge. Note the emulated PCI bridge only provides access to
+PCI memory space. It does not provide access to PCI IO space.
+This means some devices (eg. ne2k_pci NIC) are not usable, and others
+(eg. rtl8139 NIC) are only usable when the guest drivers use the memory
+mapped control registers.
+@item
+PCI OHCI USB controller.
+@item
+LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM devices.
+@item
+PL181 MultiMedia Card Interface with SD card.
+@end itemize
+
+Several variants of the ARM RealView baseboard are emulated,
+including the EB, PB-A8 and PBX-A9. Due to interactions with the
+bootloader, only certain Linux kernel configurations work out
+of the box on these boards.
+
+Kernels for the PB-A8 board should have CONFIG_REALVIEW_HIGH_PHYS_OFFSET
+enabled in the kernel, and expect 512M RAM. Kernels for The PBX-A9 board
+should have CONFIG_SPARSEMEM enabled, CONFIG_REALVIEW_HIGH_PHYS_OFFSET
+disabled and expect 1024M RAM.
+
+The following devices are emulated:
+
+@itemize @minus
+@item
+ARM926E, ARM1136, ARM11MPCore, Cortex-A8 or Cortex-A9 MPCore CPU
+@item
+ARM AMBA Generic/Distributed Interrupt Controller
+@item
+Four PL011 UARTs
+@item
+SMC 91c111 or SMSC LAN9118 Ethernet adapter
+@item
+PL110 LCD controller
+@item
+PL050 KMI with PS/2 keyboard and mouse
+@item
+PCI host bridge
+@item
+PCI OHCI USB controller
+@item
+LSI53C895A PCI SCSI Host Bus Adapter with hard disk and CD-ROM devices
+@item
+PL181 MultiMedia Card Interface with SD card.
+@end itemize
+
+The XScale-based clamshell PDA models ("Spitz", "Akita", "Borzoi"
+and "Terrier") emulation includes the following peripherals:
+
+@itemize @minus
+@item
+Intel PXA270 System-on-chip (ARM V5TE core)
+@item
+NAND Flash memory
+@item
+IBM/Hitachi DSCM microdrive in a PXA PCMCIA slot - not in "Akita"
+@item
+On-chip OHCI USB controller
+@item
+On-chip LCD controller
+@item
+On-chip Real Time Clock
+@item
+TI ADS7846 touchscreen controller on SSP bus
+@item
+Maxim MAX1111 analog-digital converter on I@math{^2}C bus
+@item
+GPIO-connected keyboard controller and LEDs
+@item
+Secure Digital card connected to PXA MMC/SD host
+@item
+Three on-chip UARTs
+@item
+WM8750 audio CODEC on I@math{^2}C and I@math{^2}S busses
+@end itemize
+
+The Palm Tungsten|E PDA (codename "Cheetah") emulation includes the
+following elements:
+
+@itemize @minus
+@item
+Texas Instruments OMAP310 System-on-chip (ARM 925T core)
+@item
+ROM and RAM memories (ROM firmware image can be loaded with -option-rom)
+@item
+On-chip LCD controller
+@item
+On-chip Real Time Clock
+@item
+TI TSC2102i touchscreen controller / analog-digital converter / Audio
+CODEC, connected through MicroWire and I@math{^2}S busses
+@item
+GPIO-connected matrix keypad
+@item
+Secure Digital card connected to OMAP MMC/SD host
+@item
+Three on-chip UARTs
+@end itemize
+
+Nokia N800 and N810 internet tablets (known also as RX-34 and RX-44 / 48)
+emulation supports the following elements:
+
+@itemize @minus
+@item
+Texas Instruments OMAP2420 System-on-chip (ARM 1136 core)
+@item
+RAM and non-volatile OneNAND Flash memories
+@item
+Display connected to EPSON remote framebuffer chip and OMAP on-chip
+display controller and a LS041y3 MIPI DBI-C controller
+@item
+TI TSC2301 (in N800) and TI TSC2005 (in N810) touchscreen controllers
+driven through SPI bus
+@item
+National Semiconductor LM8323-controlled qwerty keyboard driven
+through I@math{^2}C bus
+@item
+Secure Digital card connected to OMAP MMC/SD host
+@item
+Three OMAP on-chip UARTs and on-chip STI debugging console
+@item
+A Bluetooth(R) transceiver and HCI connected to an UART
+@item
+Mentor Graphics "Inventra" dual-role USB controller embedded in a TI
+TUSB6010 chip - only USB host mode is supported
+@item
+TI TMP105 temperature sensor driven through I@math{^2}C bus
+@item
+TI TWL92230C power management companion with an RTC on I@math{^2}C bus
+@item
+Nokia RETU and TAHVO multi-purpose chips with an RTC, connected
+through CBUS
+@end itemize
+
+The Luminary Micro Stellaris LM3S811EVB emulation includes the following
+devices:
+
+@itemize @minus
+@item
+Cortex-M3 CPU core.
+@item
+64k Flash and 8k SRAM.
+@item
+Timers, UARTs, ADC and I@math{^2}C interface.
+@item
+OSRAM Pictiva 96x16 OLED with SSD0303 controller on I@math{^2}C bus.
+@end itemize
+
+The Luminary Micro Stellaris LM3S6965EVB emulation includes the following
+devices:
+
+@itemize @minus
+@item
+Cortex-M3 CPU core.
+@item
+256k Flash and 64k SRAM.
+@item
+Timers, UARTs, ADC, I@math{^2}C and SSI interfaces.
+@item
+OSRAM Pictiva 128x64 OLED with SSD0323 controller connected via SSI.
+@end itemize
+
+The Freecom MusicPal internet radio emulation includes the following
+elements:
+
+@itemize @minus
+@item
+Marvell MV88W8618 ARM core.
+@item
+32 MB RAM, 256 KB SRAM, 8 MB flash.
+@item
+Up to 2 16550 UARTs
+@item
+MV88W8xx8 Ethernet controller
+@item
+MV88W8618 audio controller, WM8750 CODEC and mixer
+@item
+128×64 display with brightness control
+@item
+2 buttons, 2 navigation wheels with button function
+@end itemize
+
+The Siemens SX1 models v1 and v2 (default) basic emulation.
+The emulation includes the following elements:
+
+@itemize @minus
+@item
+Texas Instruments OMAP310 System-on-chip (ARM 925T core)
+@item
+ROM and RAM memories (ROM firmware image can be loaded with -pflash)
+V1
+1 Flash of 16MB and 1 Flash of 8MB
+V2
+1 Flash of 32MB
+@item
+On-chip LCD controller
+@item
+On-chip Real Time Clock
+@item
+Secure Digital card connected to OMAP MMC/SD host
+@item
+Three on-chip UARTs
+@end itemize
+
+The "Syborg" Symbian Virtual Platform base model includes the following
+elements:
+
+@itemize @minus
+@item
+ARM Cortex-A8 CPU
+@item
+Interrupt controller
+@item
+Timer
+@item
+Real Time Clock
+@item
+Keyboard
+@item
+Framebuffer
+@item
+Touchscreen
+@item
+UARTs
+@end itemize
+
+A Linux 2.6 test image is available on the QEMU web site. More
+information is available in the QEMU mailing-list archive.
+
+@c man begin OPTIONS
+
+The following options are specific to the ARM emulation:
+
+@table @option
+
+@item -semihosting
+Enable semihosting syscall emulation.
+
+On ARM this implements the "Angel" interface.
+
+Note that this allows guest direct access to the host filesystem,
+so should only be used with trusted guest OS.
+
+@end table
+
+@node ColdFire System emulator
+@section ColdFire System emulator
+@cindex system emulation (ColdFire)
+@cindex system emulation (M68K)
+
+Use the executable @file{qemu-system-m68k} to simulate a ColdFire machine.
+The emulator is able to boot a uClinux kernel.
+
+The M5208EVB emulation includes the following devices:
+
+@itemize @minus
+@item
+MCF5208 ColdFire V2 Microprocessor (ISA A+ with EMAC).
+@item
+Three Two on-chip UARTs.
+@item
+Fast Ethernet Controller (FEC)
+@end itemize
+
+The AN5206 emulation includes the following devices:
+
+@itemize @minus
+@item
+MCF5206 ColdFire V2 Microprocessor.
+@item
+Two on-chip UARTs.
+@end itemize
+
+@c man begin OPTIONS
+
+The following options are specific to the ColdFire emulation:
+
+@table @option
+
+@item -semihosting
+Enable semihosting syscall emulation.
+
+On M68K this implements the "ColdFire GDB" interface used by libgloss.
+
+Note that this allows guest direct access to the host filesystem,
+so should only be used with trusted guest OS.
+
+@end table
+
+@node Cris System emulator
+@section Cris System emulator
+@cindex system emulation (Cris)
+
+TODO
+
+@node Microblaze System emulator
+@section Microblaze System emulator
+@cindex system emulation (Microblaze)
+
+TODO
+
+@node SH4 System emulator
+@section SH4 System emulator
+@cindex system emulation (SH4)
+
+TODO
+
+@node QEMU User space emulator
+@chapter QEMU User space emulator
+
+@menu
+* Supported Operating Systems ::
+* Linux User space emulator::
+* Mac OS X/Darwin User space emulator ::
+* BSD User space emulator ::
+@end menu
+
+@node Supported Operating Systems
+@section Supported Operating Systems
+
+The following OS are supported in user space emulation:
+
+@itemize @minus
+@item
+Linux (referred as qemu-linux-user)
+@item
+Mac OS X/Darwin (referred as qemu-darwin-user)
+@item
+BSD (referred as qemu-bsd-user)
+@end itemize
+
+@node Linux User space emulator
+@section Linux User space emulator
+
+@menu
+* Quick Start::
+* Wine launch::
+* Command line options::
+* Other binaries::
+@end menu
+
+@node Quick Start
+@subsection Quick Start
+
+In order to launch a Linux process, QEMU needs the process executable
+itself and all the target (x86) dynamic libraries used by it.
+
+@itemize
+
+@item On x86, you can just try to launch any process by using the native
+libraries:
+
+@example
+qemu-i386 -L / /bin/ls
+@end example
+
+@code{-L /} tells that the x86 dynamic linker must be searched with a
+@file{/} prefix.
+
+@item Since QEMU is also a linux process, you can launch qemu with
+qemu (NOTE: you can only do that if you compiled QEMU from the sources):
+
+@example
+qemu-i386 -L / qemu-i386 -L / /bin/ls
+@end example
+
+@item On non x86 CPUs, you need first to download at least an x86 glibc
+(@file{qemu-runtime-i386-XXX-.tar.gz} on the QEMU web page). Ensure that
+@code{LD_LIBRARY_PATH} is not set:
+
+@example
+unset LD_LIBRARY_PATH
+@end example
+
+Then you can launch the precompiled @file{ls} x86 executable:
+
+@example
+qemu-i386 tests/i386/ls
+@end example
+You can look at @file{scripts/qemu-binfmt-conf.sh} so that
+QEMU is automatically launched by the Linux kernel when you try to
+launch x86 executables. It requires the @code{binfmt_misc} module in the
+Linux kernel.
+
+@item The x86 version of QEMU is also included. You can try weird things such as:
+@example
+qemu-i386 /usr/local/qemu-i386/bin/qemu-i386 \
+ /usr/local/qemu-i386/bin/ls-i386
+@end example
+
+@end itemize
+
+@node Wine launch
+@subsection Wine launch
+
+@itemize
+
+@item Ensure that you have a working QEMU with the x86 glibc
+distribution (see previous section). In order to verify it, you must be
+able to do:
+
+@example
+qemu-i386 /usr/local/qemu-i386/bin/ls-i386
+@end example
+
+@item Download the binary x86 Wine install
+(@file{qemu-XXX-i386-wine.tar.gz} on the QEMU web page).
+
+@item Configure Wine on your account. Look at the provided script
+@file{/usr/local/qemu-i386/@/bin/wine-conf.sh}. Your previous
+@code{$@{HOME@}/.wine} directory is saved to @code{$@{HOME@}/.wine.org}.
+
+@item Then you can try the example @file{putty.exe}:
+
+@example
+qemu-i386 /usr/local/qemu-i386/wine/bin/wine \
+ /usr/local/qemu-i386/wine/c/Program\ Files/putty.exe
+@end example
+
+@end itemize
+
+@node Command line options
+@subsection Command line options
+
+@example
+usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...]
+@end example
+
+@table @option
+@item -h
+Print the help
+@item -L path
+Set the x86 elf interpreter prefix (default=/usr/local/qemu-i386)
+@item -s size
+Set the x86 stack size in bytes (default=524288)
+@item -cpu model
+Select CPU model (-cpu ? for list and additional feature selection)
+@item -ignore-environment
+Start with an empty environment. Without this option,
+the initial environment is a copy of the caller's environment.
+@item -E @var{var}=@var{value}
+Set environment @var{var} to @var{value}.
+@item -U @var{var}
+Remove @var{var} from the environment.
+@item -B offset
+Offset guest address by the specified number of bytes. This is useful when
+the address region required by guest applications is reserved on the host.
+This option is currently only supported on some hosts.
+@item -R size
+Pre-allocate a guest virtual address space of the given size (in bytes).
+"G", "M", and "k" suffixes may be used when specifying the size.
+@end table
+
+Debug options:
+
+@table @option
+@item -d
+Activate log (logfile=/tmp/qemu.log)
+@item -p pagesize
+Act as if the host page size was 'pagesize' bytes
+@item -g port
+Wait gdb connection to port
+@item -singlestep
+Run the emulation in single step mode.
+@end table
+
+Environment variables:
+
+@table @env
+@item QEMU_STRACE
+Print system calls and arguments similar to the 'strace' program
+(NOTE: the actual 'strace' program will not work because the user
+space emulator hasn't implemented ptrace). At the moment this is
+incomplete. All system calls that don't have a specific argument
+format are printed with information for six arguments. Many
+flag-style arguments don't have decoders and will show up as numbers.
+@end table
+
+@node Other binaries
+@subsection Other binaries
+
+@cindex user mode (Alpha)
+@command{qemu-alpha} TODO.
+
+@cindex user mode (ARM)
+@command{qemu-armeb} TODO.
+
+@cindex user mode (ARM)
+@command{qemu-arm} is also capable of running ARM "Angel" semihosted ELF
+binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB
+configurations), and arm-uclinux bFLT format binaries.
+
+@cindex user mode (ColdFire)
+@cindex user mode (M68K)
+@command{qemu-m68k} is capable of running semihosted binaries using the BDM
+(m5xxx-ram-hosted.ld) or m68k-sim (sim.ld) syscall interfaces, and
+coldfire uClinux bFLT format binaries.
+
+The binary format is detected automatically.
+
+@cindex user mode (Cris)
+@command{qemu-cris} TODO.
+
+@cindex user mode (i386)
+@command{qemu-i386} TODO.
+@command{qemu-x86_64} TODO.
+
+@cindex user mode (Microblaze)
+@command{qemu-microblaze} TODO.
+
+@cindex user mode (MIPS)
+@command{qemu-mips} TODO.
+@command{qemu-mipsel} TODO.
+
+@cindex user mode (PowerPC)
+@command{qemu-ppc64abi32} TODO.
+@command{qemu-ppc64} TODO.
+@command{qemu-ppc} TODO.
+
+@cindex user mode (SH4)
+@command{qemu-sh4eb} TODO.
+@command{qemu-sh4} TODO.
+
+@cindex user mode (SPARC)
+@command{qemu-sparc} can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI).
+
+@command{qemu-sparc32plus} can execute Sparc32 and SPARC32PLUS binaries
+(Sparc64 CPU, 32 bit ABI).
+
+@command{qemu-sparc64} can execute some Sparc64 (Sparc64 CPU, 64 bit ABI) and
+SPARC32PLUS binaries (Sparc64 CPU, 32 bit ABI).
+
+@node Mac OS X/Darwin User space emulator
+@section Mac OS X/Darwin User space emulator
+
+@menu
+* Mac OS X/Darwin Status::
+* Mac OS X/Darwin Quick Start::
+* Mac OS X/Darwin Command line options::
+@end menu
+
+@node Mac OS X/Darwin Status
+@subsection Mac OS X/Darwin Status
+
+@itemize @minus
+@item
+target x86 on x86: Most apps (Cocoa and Carbon too) works. [1]
+@item
+target PowerPC on x86: Not working as the ppc commpage can't be mapped (yet!)
+@item
+target PowerPC on PowerPC: Most apps (Cocoa and Carbon too) works. [1]
+@item
+target x86 on PowerPC: most utilities work. Cocoa and Carbon apps are not yet supported.
+@end itemize
+
+[1] If you're host commpage can be executed by qemu.
+
+@node Mac OS X/Darwin Quick Start
+@subsection Quick Start
+
+In order to launch a Mac OS X/Darwin process, QEMU needs the process executable
+itself and all the target dynamic libraries used by it. If you don't have the FAT
+libraries (you're running Mac OS X/ppc) you'll need to obtain it from a Mac OS X
+CD or compile them by hand.
+
+@itemize
+
+@item On x86, you can just try to launch any process by using the native
+libraries:
+
+@example
+qemu-i386 /bin/ls
+@end example
+
+or to run the ppc version of the executable:
+
+@example
+qemu-ppc /bin/ls
+@end example
+
+@item On ppc, you'll have to tell qemu where your x86 libraries (and dynamic linker)
+are installed:
+
+@example
+qemu-i386 -L /opt/x86_root/ /bin/ls
+@end example
+
+@code{-L /opt/x86_root/} tells that the dynamic linker (dyld) path is in
+@file{/opt/x86_root/usr/bin/dyld}.
+
+@end itemize
+
+@node Mac OS X/Darwin Command line options
+@subsection Command line options
+
+@example
+usage: qemu-i386 [-h] [-d] [-L path] [-s size] program [arguments...]
+@end example
+
+@table @option
+@item -h
+Print the help
+@item -L path
+Set the library root path (default=/)
+@item -s size
+Set the stack size in bytes (default=524288)
+@end table
+
+Debug options:
+
+@table @option
+@item -d
+Activate log (logfile=/tmp/qemu.log)
+@item -p pagesize
+Act as if the host page size was 'pagesize' bytes
+@item -singlestep
+Run the emulation in single step mode.
+@end table
+
+@node BSD User space emulator
+@section BSD User space emulator
+
+@menu
+* BSD Status::
+* BSD Quick Start::
+* BSD Command line options::
+@end menu
+
+@node BSD Status
+@subsection BSD Status
+
+@itemize @minus
+@item
+target Sparc64 on Sparc64: Some trivial programs work.
+@end itemize
+
+@node BSD Quick Start
+@subsection Quick Start
+
+In order to launch a BSD process, QEMU needs the process executable
+itself and all the target dynamic libraries used by it.
+
+@itemize
+
+@item On Sparc64, you can just try to launch any process by using the native
+libraries:
+
+@example
+qemu-sparc64 /bin/ls
+@end example
+
+@end itemize
+
+@node BSD Command line options
+@subsection Command line options
+
+@example
+usage: qemu-sparc64 [-h] [-d] [-L path] [-s size] [-bsd type] program [arguments...]
+@end example
+
+@table @option
+@item -h
+Print the help
+@item -L path
+Set the library root path (default=/)
+@item -s size
+Set the stack size in bytes (default=524288)
+@item -ignore-environment
+Start with an empty environment. Without this option,
+the initial environment is a copy of the caller's environment.
+@item -E @var{var}=@var{value}
+Set environment @var{var} to @var{value}.
+@item -U @var{var}
+Remove @var{var} from the environment.
+@item -bsd type
+Set the type of the emulated BSD Operating system. Valid values are
+FreeBSD, NetBSD and OpenBSD (default).
+@end table
+
+Debug options:
+
+@table @option
+@item -d
+Activate log (logfile=/tmp/qemu.log)
+@item -p pagesize
+Act as if the host page size was 'pagesize' bytes
+@item -singlestep
+Run the emulation in single step mode.
+@end table
+
+@node compilation
+@chapter Compilation from the sources
+
+@menu
+* Linux/Unix::
+* Windows::
+* Cross compilation for Windows with Linux::
+* Mac OS X::
+* Make targets::
+@end menu
+
+@node Linux/Unix
+@section Linux/Unix
+
+@subsection Compilation
+
+First you must decompress the sources:
+@example
+cd /tmp
+tar zxvf qemu-x.y.z.tar.gz
+cd qemu-x.y.z
+@end example
+
+Then you configure QEMU and build it (usually no options are needed):
+@example
+./configure
+make
+@end example
+
+Then type as root user:
+@example
+make install
+@end example
+to install QEMU in @file{/usr/local}.
+
+@node Windows
+@section Windows
+
+@itemize
+@item Install the current versions of MSYS and MinGW from
+@url{http://www.mingw.org/}. You can find detailed installation
+instructions in the download section and the FAQ.
+
+@item Download
+the MinGW development library of SDL 1.2.x
+(@file{SDL-devel-1.2.x-@/mingw32.tar.gz}) from
+@url{http://www.libsdl.org}. Unpack it in a temporary place and
+edit the @file{sdl-config} script so that it gives the
+correct SDL directory when invoked.
+
+@item Install the MinGW version of zlib and make sure
+@file{zlib.h} and @file{libz.dll.a} are in
+MinGW's default header and linker search paths.
+
+@item Extract the current version of QEMU.
+
+@item Start the MSYS shell (file @file{msys.bat}).
+
+@item Change to the QEMU directory. Launch @file{./configure} and
+@file{make}. If you have problems using SDL, verify that
+@file{sdl-config} can be launched from the MSYS command line.
+
+@item You can install QEMU in @file{Program Files/Qemu} by typing
+@file{make install}. Don't forget to copy @file{SDL.dll} in
+@file{Program Files/Qemu}.
+
+@end itemize
+
+@node Cross compilation for Windows with Linux
+@section Cross compilation for Windows with Linux
+
+@itemize
+@item
+Install the MinGW cross compilation tools available at
+@url{http://www.mingw.org/}.
+
+@item Download
+the MinGW development library of SDL 1.2.x
+(@file{SDL-devel-1.2.x-@/mingw32.tar.gz}) from
+@url{http://www.libsdl.org}. Unpack it in a temporary place and
+edit the @file{sdl-config} script so that it gives the
+correct SDL directory when invoked. Set up the @code{PATH} environment
+variable so that @file{sdl-config} can be launched by
+the QEMU configuration script.
+
+@item Install the MinGW version of zlib and make sure
+@file{zlib.h} and @file{libz.dll.a} are in
+MinGW's default header and linker search paths.
+
+@item
+Configure QEMU for Windows cross compilation:
+@example
+PATH=/usr/i686-pc-mingw32/sys-root/mingw/bin:$PATH ./configure --cross-prefix='i686-pc-mingw32-'
+@end example
+The example assumes @file{sdl-config} is installed under @file{/usr/i686-pc-mingw32/sys-root/mingw/bin} and
+MinGW cross compilation tools have names like @file{i686-pc-mingw32-gcc} and @file{i686-pc-mingw32-strip}.
+We set the @code{PATH} environment variable to ensure the MinGW version of @file{sdl-config} is used and
+use --cross-prefix to specify the name of the cross compiler.
+You can also use --prefix to set the Win32 install path which defaults to @file{c:/Program Files/Qemu}.
+
+Under Fedora Linux, you can run:
+@example
+yum -y install mingw32-gcc mingw32-SDL mingw32-zlib
+@end example
+to get a suitable cross compilation environment.
+
+@item You can install QEMU in the installation directory by typing
+@code{make install}. Don't forget to copy @file{SDL.dll} and @file{zlib1.dll} into the
+installation directory.
+
+@end itemize
+
+Wine can be used to launch the resulting qemu.exe compiled for Win32.
+
+@node Mac OS X
+@section Mac OS X
+
+The Mac OS X patches are not fully merged in QEMU, so you should look
+at the QEMU mailing list archive to have all the necessary
+information.
+
+@node Make targets
+@section Make targets
+
+@table @code
+
+@item make
+@item make all
+Make everything which is typically needed.
+
+@item install
+TODO
+
+@item install-doc
+TODO
+
+@item make clean
+Remove most files which were built during make.
+
+@item make distclean
+Remove everything which was built during make.
+
+@item make dvi
+@item make html
+@item make info
+@item make pdf
+Create documentation in dvi, html, info or pdf format.
+
+@item make cscope
+TODO
+
+@item make defconfig
+(Re-)create some build configuration files.
+User made changes will be overwritten.
+
+@item tar
+@item tarbin
+TODO
+
+@end table
+
+@node License
+@appendix License
+
+QEMU is a trademark of Fabrice Bellard.
+
+QEMU is released under the GNU General Public License (TODO: add link).
+Parts of QEMU have specific licenses, see file LICENSE.
+
+TODO (refer to file LICENSE, include it, include the GPL?)
+
+@node Index
+@appendix Index
+@menu
+* Concept Index::
+* Function Index::
+* Keystroke Index::
+* Program Index::
+* Data Type Index::
+* Variable Index::
+@end menu
+
+@node Concept Index
+@section Concept Index
+This is the main index. Should we combine all keywords in one index? TODO
+@printindex cp
+
+@node Function Index
+@section Function Index
+This index could be used for command line options and monitor functions.
+@printindex fn
+
+@node Keystroke Index
+@section Keystroke Index
+
+This is a list of all keystrokes which have a special function
+in system emulation.
+
+@printindex ky
+
+@node Program Index
+@section Program Index
+@printindex pg
+
+@node Data Type Index
+@section Data Type Index
+
+This index could be used for qdev device names and options.
+
+@printindex tp
+
+@node Variable Index
+@section Variable Index
+@printindex vr
+
+@bye
diff --git a/qemu-error.c b/qemu-error.c
new file mode 100644
index 0000000..5a35e7c
--- /dev/null
+++ b/qemu-error.c
@@ -0,0 +1,209 @@
+/*
+ * Error reporting
+ *
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * Authors:
+ * Markus Armbruster <armbru@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdio.h>
+#include "monitor.h"
+#include "sysemu.h"
+
+/*
+ * Print to current monitor if we have one, else to stderr.
+ * TODO should return int, so callers can calculate width, but that
+ * requires surgery to monitor_vprintf(). Left for another day.
+ */
+void error_vprintf(const char *fmt, va_list ap)
+{
+ if (cur_mon) {
+ monitor_vprintf(cur_mon, fmt, ap);
+ } else {
+ vfprintf(stderr, fmt, ap);
+ }
+}
+
+/*
+ * Print to current monitor if we have one, else to stderr.
+ * TODO just like error_vprintf()
+ */
+void error_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ error_vprintf(fmt, ap);
+ va_end(ap);
+}
+
+void error_printf_unless_qmp(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!monitor_cur_is_qmp()) {
+ va_start(ap, fmt);
+ error_vprintf(fmt, ap);
+ va_end(ap);
+ }
+}
+
+static Location std_loc = {
+ .kind = LOC_NONE
+};
+static Location *cur_loc = &std_loc;
+
+/*
+ * Push location saved in LOC onto the location stack, return it.
+ * The top of that stack is the current location.
+ * Needs a matching loc_pop().
+ */
+Location *loc_push_restore(Location *loc)
+{
+ assert(!loc->prev);
+ loc->prev = cur_loc;
+ cur_loc = loc;
+ return loc;
+}
+
+/*
+ * Initialize *LOC to "nowhere", push it onto the location stack.
+ * The top of that stack is the current location.
+ * Needs a matching loc_pop().
+ * Return LOC.
+ */
+Location *loc_push_none(Location *loc)
+{
+ loc->kind = LOC_NONE;
+ loc->prev = NULL;
+ return loc_push_restore(loc);
+}
+
+/*
+ * Pop the location stack.
+ * LOC must be the current location, i.e. the top of the stack.
+ */
+Location *loc_pop(Location *loc)
+{
+ assert(cur_loc == loc && loc->prev);
+ cur_loc = loc->prev;
+ loc->prev = NULL;
+ return loc;
+}
+
+/*
+ * Save the current location in LOC, return LOC.
+ */
+Location *loc_save(Location *loc)
+{
+ *loc = *cur_loc;
+ loc->prev = NULL;
+ return loc;
+}
+
+/*
+ * Change the current location to the one saved in LOC.
+ */
+void loc_restore(Location *loc)
+{
+ Location *prev = cur_loc->prev;
+ assert(!loc->prev);
+ *cur_loc = *loc;
+ cur_loc->prev = prev;
+}
+
+/*
+ * Change the current location to "nowhere in particular".
+ */
+void loc_set_none(void)
+{
+ cur_loc->kind = LOC_NONE;
+}
+
+/*
+ * Change the current location to argument ARGV[IDX..IDX+CNT-1].
+ */
+void loc_set_cmdline(char **argv, int idx, int cnt)
+{
+ cur_loc->kind = LOC_CMDLINE;
+ cur_loc->num = cnt;
+ cur_loc->ptr = argv + idx;
+}
+
+/*
+ * Change the current location to file FNAME, line LNO.
+ */
+void loc_set_file(const char *fname, int lno)
+{
+ assert (fname || cur_loc->kind == LOC_FILE);
+ cur_loc->kind = LOC_FILE;
+ cur_loc->num = lno;
+ if (fname) {
+ cur_loc->ptr = fname;
+ }
+}
+
+static const char *progname;
+
+/*
+ * Set the program name for error_print_loc().
+ */
+void error_set_progname(const char *argv0)
+{
+ const char *p = strrchr(argv0, '/');
+ progname = p ? p + 1 : argv0;
+}
+
+/*
+ * Print current location to current monitor if we have one, else to stderr.
+ */
+void error_print_loc(void)
+{
+ const char *sep = "";
+ int i;
+ const char *const *argp;
+
+ if (!cur_mon && progname) {
+ fprintf(stderr, "%s:", progname);
+ sep = " ";
+ }
+ switch (cur_loc->kind) {
+ case LOC_CMDLINE:
+ argp = cur_loc->ptr;
+ for (i = 0; i < cur_loc->num; i++) {
+ error_printf("%s%s", sep, argp[i]);
+ sep = " ";
+ }
+ error_printf(": ");
+ break;
+ case LOC_FILE:
+ error_printf("%s:", (const char *)cur_loc->ptr);
+ if (cur_loc->num) {
+ error_printf("%d:", cur_loc->num);
+ }
+ error_printf(" ");
+ break;
+ default:
+ error_printf("%s", sep);
+ }
+}
+
+/*
+ * Print an error message to current monitor if we have one, else to stderr.
+ * Prepend the current location and append a newline.
+ * It's wrong to call this in a QMP monitor. Use qerror_report() there.
+ */
+void error_report(const char *fmt, ...)
+{
+ va_list ap;
+
+ error_print_loc();
+ va_start(ap, fmt);
+ error_vprintf(fmt, ap);
+ va_end(ap);
+ error_printf("\n");
+}
diff --git a/qemu-error.h b/qemu-error.h
new file mode 100644
index 0000000..4d5c537
--- /dev/null
+++ b/qemu-error.h
@@ -0,0 +1,40 @@
+/*
+ * Error reporting
+ *
+ * Copyright (C) 2010 Red Hat Inc.
+ *
+ * Authors:
+ * Markus Armbruster <armbru@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_ERROR_H
+#define QEMU_ERROR_H
+
+typedef struct Location {
+ /* all members are private to qemu-error.c */
+ enum { LOC_NONE, LOC_CMDLINE, LOC_FILE } kind;
+ int num;
+ const void *ptr;
+ struct Location *prev;
+} Location;
+
+Location *loc_push_restore(Location *loc);
+Location *loc_push_none(Location *loc);
+Location *loc_pop(Location *loc);
+Location *loc_save(Location *loc);
+void loc_restore(Location *loc);
+void loc_set_none(void);
+void loc_set_cmdline(char **argv, int idx, int cnt);
+void loc_set_file(const char *fname, int lno);
+
+void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0);
+void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+void error_print_loc(void);
+void error_set_progname(const char *argv0);
+void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
+
+#endif
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
new file mode 100644
index 0000000..6c7176f
--- /dev/null
+++ b/qemu-img-cmds.hx
@@ -0,0 +1,59 @@
+HXCOMM Use DEFHEADING() to define headings in both help text and texi
+HXCOMM Text between STEXI and ETEXI are copied to texi version and
+HXCOMM discarded from C version
+HXCOMM DEF(command, callback, arg_string) is used to construct
+HXCOMM command structures and help message.
+HXCOMM HXCOMM can be used for comments, discarded from both texi and C
+
+STEXI
+@table @option
+ETEXI
+
+DEF("check", img_check,
+ "check [-f fmt] filename")
+STEXI
+@item check [-f @var{fmt}] @var{filename}
+ETEXI
+
+DEF("create", img_create,
+ "create [-f fmt] [-o options] filename [size]")
+STEXI
+@item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
+ETEXI
+
+DEF("commit", img_commit,
+ "commit [-f fmt] filename")
+STEXI
+@item commit [-f @var{fmt}] @var{filename}
+ETEXI
+
+DEF("convert", img_convert,
+ "convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename")
+STEXI
+@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+ETEXI
+
+DEF("info", img_info,
+ "info [-f fmt] filename")
+STEXI
+@item info [-f @var{fmt}] @var{filename}
+ETEXI
+
+DEF("snapshot", img_snapshot,
+ "snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename")
+STEXI
+@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
+ETEXI
+
+DEF("rebase", img_rebase,
+ "rebase [-f fmt] [-u] -b backing_file [-F backing_fmt] filename")
+STEXI
+@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+ETEXI
+
+DEF("resize", img_resize,
+ "resize filename [+ | -]size")
+STEXI
+@item resize @var{filename} [+ | -]@var{size}
+@end table
+ETEXI
diff --git a/qemu-img.c b/qemu-img.c
new file mode 100644
index 0000000..7e3cc4c
--- /dev/null
+++ b/qemu-img.c
@@ -0,0 +1,1528 @@
+/*
+ * QEMU disk image utility
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-error.h"
+#include "osdep.h"
+#include "sysemu.h"
+#include "block_int.h"
+#include <stdio.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+typedef struct img_cmd_t {
+ const char *name;
+ int (*handler)(int argc, char **argv);
+} img_cmd_t;
+
+/* Default to cache=writeback as data integrity is not important for qemu-tcg. */
+#define BDRV_O_FLAGS BDRV_O_CACHE_WB
+
+static void format_print(void *opaque, const char *name)
+{
+ printf(" %s", name);
+}
+
+/* Please keep in synch with qemu-img.texi */
+static void help(void)
+{
+ const char *help_msg =
+ "qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n"
+ "usage: qemu-img command [command options]\n"
+ "QEMU disk image utility\n"
+ "\n"
+ "Command syntax:\n"
+#define DEF(option, callback, arg_string) \
+ " " arg_string "\n"
+#include "qemu-img-cmds.h"
+#undef DEF
+#undef GEN_DOCS
+ "\n"
+ "Command parameters:\n"
+ " 'filename' is a disk image filename\n"
+ " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
+ " 'size' is the disk image size in bytes. Optional suffixes\n"
+ " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n"
+ " and T (terabyte, 1024G) are supported. 'b' is ignored.\n"
+ " 'output_filename' is the destination disk image filename\n"
+ " 'output_fmt' is the destination format\n"
+ " 'options' is a comma separated list of format specific options in a\n"
+ " name=value format. Use -o ? for an overview of the options supported by the\n"
+ " used format\n"
+ " '-c' indicates that target image must be compressed (qcow format only)\n"
+ " '-u' enables unsafe rebasing. It is assumed that old and new backing file\n"
+ " match exactly. The image doesn't need a working backing file before\n"
+ " rebasing in this case (useful for renaming the backing file)\n"
+ " '-h' with or without a command shows this help and lists the supported formats\n"
+ "\n"
+ "Parameters to snapshot subcommand:\n"
+ " 'snapshot' is the name of the snapshot to create, apply or delete\n"
+ " '-a' applies a snapshot (revert disk to saved state)\n"
+ " '-c' creates a snapshot\n"
+ " '-d' deletes a snapshot\n"
+ " '-l' lists all snapshots in the given image\n";
+
+ printf("%s\nSupported formats:", help_msg);
+ bdrv_iterate_format(format_print, NULL);
+ printf("\n");
+ exit(1);
+}
+
+#if defined(WIN32)
+/* XXX: put correct support for win32 */
+static int read_password(char *buf, int buf_size)
+{
+ int c, i;
+ printf("Password: ");
+ fflush(stdout);
+ i = 0;
+ for(;;) {
+ c = getchar();
+ if (c == '\n')
+ break;
+ if (i < (buf_size - 1))
+ buf[i++] = c;
+ }
+ buf[i] = '\0';
+ return 0;
+}
+
+#else
+
+#include <termios.h>
+
+static struct termios oldtty;
+
+static void term_exit(void)
+{
+ tcsetattr (0, TCSANOW, &oldtty);
+}
+
+static void term_init(void)
+{
+ struct termios tty;
+
+ tcgetattr (0, &tty);
+ oldtty = tty;
+
+ tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+ |INLCR|IGNCR|ICRNL|IXON);
+ tty.c_oflag |= OPOST;
+ tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+ tty.c_cflag &= ~(CSIZE|PARENB);
+ tty.c_cflag |= CS8;
+ tty.c_cc[VMIN] = 1;
+ tty.c_cc[VTIME] = 0;
+
+ tcsetattr (0, TCSANOW, &tty);
+
+ atexit(term_exit);
+}
+
+static int read_password(char *buf, int buf_size)
+{
+ uint8_t ch;
+ int i, ret;
+
+ printf("password: ");
+ fflush(stdout);
+ term_init();
+ i = 0;
+ for(;;) {
+ ret = read(0, &ch, 1);
+ if (ret == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ } else {
+ ret = -1;
+ break;
+ }
+ } else if (ret == 0) {
+ ret = -1;
+ break;
+ } else {
+ if (ch == '\r') {
+ ret = 0;
+ break;
+ }
+ if (i < (buf_size - 1))
+ buf[i++] = ch;
+ }
+ }
+ term_exit();
+ buf[i] = '\0';
+ printf("\n");
+ return ret;
+}
+#endif
+
+static int print_block_option_help(const char *filename, const char *fmt)
+{
+ BlockDriver *drv, *proto_drv;
+ QEMUOptionParameter *create_options = NULL;
+
+ /* Find driver and parse its options */
+ drv = bdrv_find_format(fmt);
+ if (!drv) {
+ error_report("Unknown file format '%s'", fmt);
+ return 1;
+ }
+
+ proto_drv = bdrv_find_protocol(filename);
+ if (!proto_drv) {
+ error_report("Unknown protocol '%s'", filename);
+ return 1;
+ }
+
+ create_options = append_option_parameters(create_options,
+ drv->create_options);
+ create_options = append_option_parameters(create_options,
+ proto_drv->create_options);
+ print_option_help(create_options);
+ free_option_parameters(create_options);
+ return 0;
+}
+
+static BlockDriverState *bdrv_new_open(const char *filename,
+ const char *fmt,
+ int flags)
+{
+ BlockDriverState *bs;
+ BlockDriver *drv;
+ char password[256];
+ int ret;
+
+ bs = bdrv_new("image");
+
+ if (fmt) {
+ drv = bdrv_find_format(fmt);
+ if (!drv) {
+ error_report("Unknown file format '%s'", fmt);
+ goto fail;
+ }
+ } else {
+ drv = NULL;
+ }
+
+ ret = bdrv_open(bs, filename, flags, drv);
+ if (ret < 0) {
+ error_report("Could not open '%s': %s", filename, strerror(-ret));
+ goto fail;
+ }
+
+ if (bdrv_is_encrypted(bs)) {
+ printf("Disk image '%s' is encrypted.\n", filename);
+ if (read_password(password, sizeof(password)) < 0) {
+ error_report("No password given");
+ goto fail;
+ }
+ if (bdrv_set_key(bs, password) < 0) {
+ error_report("invalid password");
+ goto fail;
+ }
+ }
+ return bs;
+fail:
+ if (bs) {
+ bdrv_delete(bs);
+ }
+ return NULL;
+}
+
+static int add_old_style_options(const char *fmt, QEMUOptionParameter *list,
+ const char *base_filename,
+ const char *base_fmt)
+{
+ if (base_filename) {
+ if (set_option_parameter(list, BLOCK_OPT_BACKING_FILE, base_filename)) {
+ error_report("Backing file not supported for file format '%s'",
+ fmt);
+ return -1;
+ }
+ }
+ if (base_fmt) {
+ if (set_option_parameter(list, BLOCK_OPT_BACKING_FMT, base_fmt)) {
+ error_report("Backing file format not supported for file "
+ "format '%s'", fmt);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int img_create(int argc, char **argv)
+{
+ int c, ret = 0;
+ uint64_t img_size = -1;
+ const char *fmt = "raw";
+ const char *base_fmt = NULL;
+ const char *filename;
+ const char *base_filename = NULL;
+ char *options = NULL;
+
+ for(;;) {
+ c = getopt(argc, argv, "F:b:f:he6o:");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'F':
+ base_fmt = optarg;
+ break;
+ case 'b':
+ base_filename = optarg;
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'e':
+ error_report("qemu-img: option -e is deprecated, please use \'-o "
+ "encryption\' instead!");
+ return 1;
+ case '6':
+ error_report("qemu-img: option -6 is deprecated, please use \'-o "
+ "compat6\' instead!");
+ return 1;
+ case 'o':
+ options = optarg;
+ break;
+ }
+ }
+
+ /* Get the filename */
+ if (optind >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+
+ /* Get image size, if specified */
+ if (optind < argc) {
+ int64_t sval;
+ sval = strtosz_suffix(argv[optind++], NULL, STRTOSZ_DEFSUFFIX_B);
+ if (sval < 0) {
+ error_report("Invalid image size specified! You may use k, M, G or "
+ "T suffixes for ");
+ error_report("kilobytes, megabytes, gigabytes and terabytes.");
+ ret = -1;
+ goto out;
+ }
+ img_size = (uint64_t)sval;
+ }
+
+ if (options && !strcmp(options, "?")) {
+ ret = print_block_option_help(filename, fmt);
+ goto out;
+ }
+
+ ret = bdrv_img_create(filename, fmt, base_filename, base_fmt,
+ options, img_size, BDRV_O_FLAGS);
+out:
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Checks an image for consistency. Exit codes:
+ *
+ * 0 - Check completed, image is good
+ * 1 - Check not completed because of internal errors
+ * 2 - Check completed, image is corrupted
+ * 3 - Check completed, image has leaked clusters, but is good otherwise
+ */
+static int img_check(int argc, char **argv)
+{
+ int c, ret;
+ const char *filename, *fmt;
+ BlockDriverState *bs;
+ BdrvCheckResult result;
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+
+ bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS);
+ if (!bs) {
+ return 1;
+ }
+ ret = bdrv_check(bs, &result);
+
+ if (ret == -ENOTSUP) {
+ error_report("This image format does not support checks");
+ bdrv_delete(bs);
+ return 1;
+ }
+
+ if (!(result.corruptions || result.leaks || result.check_errors)) {
+ printf("No errors were found on the image.\n");
+ } else {
+ if (result.corruptions) {
+ printf("\n%d errors were found on the image.\n"
+ "Data may be corrupted, or further writes to the image "
+ "may corrupt it.\n",
+ result.corruptions);
+ }
+
+ if (result.leaks) {
+ printf("\n%d leaked clusters were found on the image.\n"
+ "This means waste of disk space, but no harm to data.\n",
+ result.leaks);
+ }
+
+ if (result.check_errors) {
+ printf("\n%d internal errors have occurred during the check.\n",
+ result.check_errors);
+ }
+ }
+
+ bdrv_delete(bs);
+
+ if (ret < 0 || result.check_errors) {
+ printf("\nAn error has occurred during the check: %s\n"
+ "The check is not complete and may have missed error.\n",
+ strerror(-ret));
+ return 1;
+ }
+
+ if (result.corruptions) {
+ return 2;
+ } else if (result.leaks) {
+ return 3;
+ } else {
+ return 0;
+ }
+}
+
+static int img_commit(int argc, char **argv)
+{
+ int c, ret;
+ const char *filename, *fmt;
+ BlockDriverState *bs;
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+
+ bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
+ if (!bs) {
+ return 1;
+ }
+ ret = bdrv_commit(bs);
+ switch(ret) {
+ case 0:
+ printf("Image committed.\n");
+ break;
+ case -ENOENT:
+ error_report("No disk inserted");
+ break;
+ case -EACCES:
+ error_report("Image is read-only");
+ break;
+ case -ENOTSUP:
+ error_report("Image is already committed");
+ break;
+ default:
+ error_report("Error while committing image");
+ break;
+ }
+
+ bdrv_delete(bs);
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+static int is_not_zero(const uint8_t *sector, int len)
+{
+ int i;
+ len >>= 2;
+ for(i = 0;i < len; i++) {
+ if (((uint32_t *)sector)[i] != 0)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Returns true iff the first sector pointed to by 'buf' contains at least
+ * a non-NUL byte.
+ *
+ * 'pnum' is set to the number of sectors (including and immediately following
+ * the first one) that are known to be in the same allocated/unallocated state.
+ */
+static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
+{
+ int v, i;
+
+ if (n <= 0) {
+ *pnum = 0;
+ return 0;
+ }
+ v = is_not_zero(buf, 512);
+ for(i = 1; i < n; i++) {
+ buf += 512;
+ if (v != is_not_zero(buf, 512))
+ break;
+ }
+ *pnum = i;
+ return v;
+}
+
+/*
+ * Compares two buffers sector by sector. Returns 0 if the first sector of both
+ * buffers matches, non-zero otherwise.
+ *
+ * pnum is set to the number of sectors (including and immediately following
+ * the first one) that are known to have the same comparison result
+ */
+static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n,
+ int *pnum)
+{
+ int res, i;
+
+ if (n <= 0) {
+ *pnum = 0;
+ return 0;
+ }
+
+ res = !!memcmp(buf1, buf2, 512);
+ for(i = 1; i < n; i++) {
+ buf1 += 512;
+ buf2 += 512;
+
+ if (!!memcmp(buf1, buf2, 512) != res) {
+ break;
+ }
+ }
+
+ *pnum = i;
+ return res;
+}
+
+#define IO_BUF_SIZE (2 * 1024 * 1024)
+
+static int img_convert(int argc, char **argv)
+{
+ int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors;
+ const char *fmt, *out_fmt, *out_baseimg, *out_filename;
+ BlockDriver *drv, *proto_drv;
+ BlockDriverState **bs = NULL, *out_bs = NULL;
+ int64_t total_sectors, nb_sectors, sector_num, bs_offset;
+ uint64_t bs_sectors;
+ uint8_t * buf = NULL;
+ const uint8_t *buf1;
+ BlockDriverInfo bdi;
+ QEMUOptionParameter *param = NULL, *create_options = NULL;
+ QEMUOptionParameter *out_baseimg_param;
+ char *options = NULL;
+ const char *snapshot_name = NULL;
+
+ fmt = NULL;
+ out_fmt = "raw";
+ out_baseimg = NULL;
+ compress = 0;
+ for(;;) {
+ c = getopt(argc, argv, "f:O:B:s:hce6o:");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'O':
+ out_fmt = optarg;
+ break;
+ case 'B':
+ out_baseimg = optarg;
+ break;
+ case 'c':
+ compress = 1;
+ break;
+ case 'e':
+ error_report("qemu-img: option -e is deprecated, please use \'-o "
+ "encryption\' instead!");
+ return 1;
+ case '6':
+ error_report("qemu-img: option -6 is deprecated, please use \'-o "
+ "compat6\' instead!");
+ return 1;
+ case 'o':
+ options = optarg;
+ break;
+ case 's':
+ snapshot_name = optarg;
+ break;
+ }
+ }
+
+ bs_n = argc - optind - 1;
+ if (bs_n < 1) {
+ help();
+ }
+
+ out_filename = argv[argc - 1];
+
+ if (options && !strcmp(options, "?")) {
+ ret = print_block_option_help(out_filename, out_fmt);
+ goto out;
+ }
+
+ if (bs_n > 1 && out_baseimg) {
+ error_report("-B makes no sense when concatenating multiple input "
+ "images");
+ ret = -1;
+ goto out;
+ }
+
+ bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *));
+
+ total_sectors = 0;
+ for (bs_i = 0; bs_i < bs_n; bs_i++) {
+ bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt, BDRV_O_FLAGS);
+ if (!bs[bs_i]) {
+ error_report("Could not open '%s'", argv[optind + bs_i]);
+ ret = -1;
+ goto out;
+ }
+ bdrv_get_geometry(bs[bs_i], &bs_sectors);
+ total_sectors += bs_sectors;
+ }
+
+ if (snapshot_name != NULL) {
+ if (bs_n > 1) {
+ error_report("No support for concatenating multiple snapshot\n");
+ ret = -1;
+ goto out;
+ }
+ if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) {
+ error_report("Failed to load snapshot\n");
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* Find driver and parse its options */
+ drv = bdrv_find_format(out_fmt);
+ if (!drv) {
+ error_report("Unknown file format '%s'", out_fmt);
+ ret = -1;
+ goto out;
+ }
+
+ proto_drv = bdrv_find_protocol(out_filename);
+ if (!proto_drv) {
+ error_report("Unknown protocol '%s'", out_filename);
+ ret = -1;
+ goto out;
+ }
+
+ create_options = append_option_parameters(create_options,
+ drv->create_options);
+ create_options = append_option_parameters(create_options,
+ proto_drv->create_options);
+
+ if (options) {
+ param = parse_option_parameters(options, create_options, param);
+ if (param == NULL) {
+ error_report("Invalid options for file format '%s'.", out_fmt);
+ ret = -1;
+ goto out;
+ }
+ } else {
+ param = parse_option_parameters("", create_options, param);
+ }
+
+ set_option_parameter_int(param, BLOCK_OPT_SIZE, total_sectors * 512);
+ ret = add_old_style_options(out_fmt, param, out_baseimg, NULL);
+ if (ret < 0) {
+ goto out;
+ }
+
+ /* Get backing file name if -o backing_file was used */
+ out_baseimg_param = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
+ if (out_baseimg_param) {
+ out_baseimg = out_baseimg_param->value.s;
+ }
+
+ /* Check if compression is supported */
+ if (compress) {
+ QEMUOptionParameter *encryption =
+ get_option_parameter(param, BLOCK_OPT_ENCRYPT);
+
+ if (!drv->bdrv_write_compressed) {
+ error_report("Compression not supported for this file format");
+ ret = -1;
+ goto out;
+ }
+
+ if (encryption && encryption->value.n) {
+ error_report("Compression and encryption not supported at "
+ "the same time");
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* Create the new image */
+ ret = bdrv_create(drv, out_filename, param);
+ if (ret < 0) {
+ if (ret == -ENOTSUP) {
+ error_report("Formatting not supported for file format '%s'",
+ out_fmt);
+ } else if (ret == -EFBIG) {
+ error_report("The image size is too large for file format '%s'",
+ out_fmt);
+ } else {
+ error_report("%s: error while converting %s: %s",
+ out_filename, out_fmt, strerror(-ret));
+ }
+ goto out;
+ }
+
+ out_bs = bdrv_new_open(out_filename, out_fmt,
+ BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH);
+ if (!out_bs) {
+ ret = -1;
+ goto out;
+ }
+
+ bs_i = 0;
+ bs_offset = 0;
+ bdrv_get_geometry(bs[0], &bs_sectors);
+ buf = qemu_malloc(IO_BUF_SIZE);
+
+ if (compress) {
+ ret = bdrv_get_info(out_bs, &bdi);
+ if (ret < 0) {
+ error_report("could not get block driver info");
+ goto out;
+ }
+ cluster_size = bdi.cluster_size;
+ if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) {
+ error_report("invalid cluster size");
+ ret = -1;
+ goto out;
+ }
+ cluster_sectors = cluster_size >> 9;
+ sector_num = 0;
+ for(;;) {
+ int64_t bs_num;
+ int remainder;
+ uint8_t *buf2;
+
+ nb_sectors = total_sectors - sector_num;
+ if (nb_sectors <= 0)
+ break;
+ if (nb_sectors >= cluster_sectors)
+ n = cluster_sectors;
+ else
+ n = nb_sectors;
+
+ bs_num = sector_num - bs_offset;
+ assert (bs_num >= 0);
+ remainder = n;
+ buf2 = buf;
+ while (remainder > 0) {
+ int nlow;
+ while (bs_num == bs_sectors) {
+ bs_i++;
+ assert (bs_i < bs_n);
+ bs_offset += bs_sectors;
+ bdrv_get_geometry(bs[bs_i], &bs_sectors);
+ bs_num = 0;
+ /* printf("changing part: sector_num=%" PRId64 ", "
+ "bs_i=%d, bs_offset=%" PRId64 ", bs_sectors=%" PRId64
+ "\n", sector_num, bs_i, bs_offset, bs_sectors); */
+ }
+ assert (bs_num < bs_sectors);
+
+ nlow = (remainder > bs_sectors - bs_num) ? bs_sectors - bs_num : remainder;
+
+ ret = bdrv_read(bs[bs_i], bs_num, buf2, nlow);
+ if (ret < 0) {
+ error_report("error while reading");
+ goto out;
+ }
+
+ buf2 += nlow * 512;
+ bs_num += nlow;
+
+ remainder -= nlow;
+ }
+ assert (remainder == 0);
+
+ if (n < cluster_sectors) {
+ memset(buf + n * 512, 0, cluster_size - n * 512);
+ }
+ if (is_not_zero(buf, cluster_size)) {
+ ret = bdrv_write_compressed(out_bs, sector_num, buf,
+ cluster_sectors);
+ if (ret != 0) {
+ error_report("error while compressing sector %" PRId64,
+ sector_num);
+ goto out;
+ }
+ }
+ sector_num += n;
+ }
+ /* signal EOF to align */
+ bdrv_write_compressed(out_bs, 0, NULL, 0);
+ } else {
+ int has_zero_init = bdrv_has_zero_init(out_bs);
+
+ sector_num = 0; // total number of sectors converted so far
+ for(;;) {
+ nb_sectors = total_sectors - sector_num;
+ if (nb_sectors <= 0) {
+ break;
+ }
+ if (nb_sectors >= (IO_BUF_SIZE / 512)) {
+ n = (IO_BUF_SIZE / 512);
+ } else {
+ n = nb_sectors;
+ }
+
+ while (sector_num - bs_offset >= bs_sectors) {
+ bs_i ++;
+ assert (bs_i < bs_n);
+ bs_offset += bs_sectors;
+ bdrv_get_geometry(bs[bs_i], &bs_sectors);
+ /* printf("changing part: sector_num=%" PRId64 ", bs_i=%d, "
+ "bs_offset=%" PRId64 ", bs_sectors=%" PRId64 "\n",
+ sector_num, bs_i, bs_offset, bs_sectors); */
+ }
+
+ if (n > bs_offset + bs_sectors - sector_num) {
+ n = bs_offset + bs_sectors - sector_num;
+ }
+
+ if (has_zero_init) {
+ /* If the output image is being created as a copy on write image,
+ assume that sectors which are unallocated in the input image
+ are present in both the output's and input's base images (no
+ need to copy them). */
+ if (out_baseimg) {
+ if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset,
+ n, &n1)) {
+ sector_num += n1;
+ continue;
+ }
+ /* The next 'n1' sectors are allocated in the input image. Copy
+ only those as they may be followed by unallocated sectors. */
+ n = n1;
+ }
+ } else {
+ n1 = n;
+ }
+
+ ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
+ if (ret < 0) {
+ error_report("error while reading");
+ goto out;
+ }
+ /* NOTE: at the same time we convert, we do not write zero
+ sectors to have a chance to compress the image. Ideally, we
+ should add a specific call to have the info to go faster */
+ buf1 = buf;
+ while (n > 0) {
+ /* If the output image is being created as a copy on write image,
+ copy all sectors even the ones containing only NUL bytes,
+ because they may differ from the sectors in the base image.
+
+ If the output is to a host device, we also write out
+ sectors that are entirely 0, since whatever data was
+ already there is garbage, not 0s. */
+ if (!has_zero_init || out_baseimg ||
+ is_allocated_sectors(buf1, n, &n1)) {
+ ret = bdrv_write(out_bs, sector_num, buf1, n1);
+ if (ret < 0) {
+ error_report("error while writing");
+ goto out;
+ }
+ }
+ sector_num += n1;
+ n -= n1;
+ buf1 += n1 * 512;
+ }
+ }
+ }
+out:
+ free_option_parameters(create_options);
+ free_option_parameters(param);
+ qemu_free(buf);
+ if (out_bs) {
+ bdrv_delete(out_bs);
+ }
+ if (bs) {
+ for (bs_i = 0; bs_i < bs_n; bs_i++) {
+ if (bs[bs_i]) {
+ bdrv_delete(bs[bs_i]);
+ }
+ }
+ qemu_free(bs);
+ }
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef _WIN32
+static int64_t get_allocated_file_size(const char *filename)
+{
+ typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
+ get_compressed_t get_compressed;
+ struct _stati64 st;
+
+ /* WinNT support GetCompressedFileSize to determine allocate size */
+ get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
+ if (get_compressed) {
+ DWORD high, low;
+ low = get_compressed(filename, &high);
+ if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
+ return (((int64_t) high) << 32) + low;
+ }
+
+ if (_stati64(filename, &st) < 0)
+ return -1;
+ return st.st_size;
+}
+#else
+static int64_t get_allocated_file_size(const char *filename)
+{
+ struct stat st;
+ if (stat(filename, &st) < 0)
+ return -1;
+ return (int64_t)st.st_blocks * 512;
+}
+#endif
+
+static void dump_snapshots(BlockDriverState *bs)
+{
+ QEMUSnapshotInfo *sn_tab, *sn;
+ int nb_sns, i;
+ char buf[256];
+
+ nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+ if (nb_sns <= 0)
+ return;
+ printf("Snapshot list:\n");
+ printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+ for(i = 0; i < nb_sns; i++) {
+ sn = &sn_tab[i];
+ printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+ }
+ qemu_free(sn_tab);
+}
+
+static int img_info(int argc, char **argv)
+{
+ int c;
+ const char *filename, *fmt;
+ BlockDriverState *bs;
+ char fmt_name[128], size_buf[128], dsize_buf[128];
+ uint64_t total_sectors;
+ int64_t allocated_size;
+ char backing_filename[1024];
+ char backing_filename2[1024];
+ BlockDriverInfo bdi;
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+
+ bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING);
+ if (!bs) {
+ return 1;
+ }
+ bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
+ bdrv_get_geometry(bs, &total_sectors);
+ get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
+ allocated_size = get_allocated_file_size(filename);
+ if (allocated_size < 0) {
+ snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
+ } else {
+ get_human_readable_size(dsize_buf, sizeof(dsize_buf),
+ allocated_size);
+ }
+ printf("image: %s\n"
+ "file format: %s\n"
+ "virtual size: %s (%" PRId64 " bytes)\n"
+ "disk size: %s\n",
+ filename, fmt_name, size_buf,
+ (total_sectors * 512),
+ dsize_buf);
+ if (bdrv_is_encrypted(bs)) {
+ printf("encrypted: yes\n");
+ }
+ if (bdrv_get_info(bs, &bdi) >= 0) {
+ if (bdi.cluster_size != 0) {
+ printf("cluster_size: %d\n", bdi.cluster_size);
+ }
+ }
+ bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
+ if (backing_filename[0] != '\0') {
+ path_combine(backing_filename2, sizeof(backing_filename2),
+ filename, backing_filename);
+ printf("backing file: %s (actual path: %s)\n",
+ backing_filename,
+ backing_filename2);
+ }
+ dump_snapshots(bs);
+ bdrv_delete(bs);
+ return 0;
+}
+
+#define SNAPSHOT_LIST 1
+#define SNAPSHOT_CREATE 2
+#define SNAPSHOT_APPLY 3
+#define SNAPSHOT_DELETE 4
+
+static int img_snapshot(int argc, char **argv)
+{
+ BlockDriverState *bs;
+ QEMUSnapshotInfo sn;
+ char *filename, *snapshot_name = NULL;
+ int c, ret = 0, bdrv_oflags;
+ int action = 0;
+ qemu_timeval tv;
+
+ bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR;
+ /* Parse commandline parameters */
+ for(;;) {
+ c = getopt(argc, argv, "la:c:d:h");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ return 0;
+ case 'l':
+ if (action) {
+ help();
+ return 0;
+ }
+ action = SNAPSHOT_LIST;
+ bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */
+ break;
+ case 'a':
+ if (action) {
+ help();
+ return 0;
+ }
+ action = SNAPSHOT_APPLY;
+ snapshot_name = optarg;
+ break;
+ case 'c':
+ if (action) {
+ help();
+ return 0;
+ }
+ action = SNAPSHOT_CREATE;
+ snapshot_name = optarg;
+ break;
+ case 'd':
+ if (action) {
+ help();
+ return 0;
+ }
+ action = SNAPSHOT_DELETE;
+ snapshot_name = optarg;
+ break;
+ }
+ }
+
+ if (optind >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+
+ /* Open the image */
+ bs = bdrv_new_open(filename, NULL, bdrv_oflags);
+ if (!bs) {
+ return 1;
+ }
+
+ /* Perform the requested action */
+ switch(action) {
+ case SNAPSHOT_LIST:
+ dump_snapshots(bs);
+ break;
+
+ case SNAPSHOT_CREATE:
+ memset(&sn, 0, sizeof(sn));
+ pstrcpy(sn.name, sizeof(sn.name), snapshot_name);
+
+ qemu_gettimeofday(&tv);
+ sn.date_sec = tv.tv_sec;
+ sn.date_nsec = tv.tv_usec * 1000;
+
+ ret = bdrv_snapshot_create(bs, &sn);
+ if (ret) {
+ error_report("Could not create snapshot '%s': %d (%s)",
+ snapshot_name, ret, strerror(-ret));
+ }
+ break;
+
+ case SNAPSHOT_APPLY:
+ ret = bdrv_snapshot_goto(bs, snapshot_name);
+ if (ret) {
+ error_report("Could not apply snapshot '%s': %d (%s)",
+ snapshot_name, ret, strerror(-ret));
+ }
+ break;
+
+ case SNAPSHOT_DELETE:
+ ret = bdrv_snapshot_delete(bs, snapshot_name);
+ if (ret) {
+ error_report("Could not delete snapshot '%s': %d (%s)",
+ snapshot_name, ret, strerror(-ret));
+ }
+ break;
+ }
+
+ /* Cleanup */
+ bdrv_delete(bs);
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+static int img_rebase(int argc, char **argv)
+{
+ BlockDriverState *bs, *bs_old_backing = NULL, *bs_new_backing = NULL;
+ BlockDriver *old_backing_drv, *new_backing_drv;
+ char *filename;
+ const char *fmt, *out_basefmt, *out_baseimg;
+ int c, flags, ret;
+ int unsafe = 0;
+
+ /* Parse commandline parameters */
+ fmt = NULL;
+ out_baseimg = NULL;
+ out_basefmt = NULL;
+
+ for(;;) {
+ c = getopt(argc, argv, "uhf:F:b:");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ return 0;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'F':
+ out_basefmt = optarg;
+ break;
+ case 'b':
+ out_baseimg = optarg;
+ break;
+ case 'u':
+ unsafe = 1;
+ break;
+ }
+ }
+
+ if ((optind >= argc) || !out_baseimg) {
+ help();
+ }
+ filename = argv[optind++];
+
+ /*
+ * Open the images.
+ *
+ * Ignore the old backing file for unsafe rebase in case we want to correct
+ * the reference to a renamed or moved backing file.
+ */
+ flags = BDRV_O_FLAGS | BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0);
+ bs = bdrv_new_open(filename, fmt, flags);
+ if (!bs) {
+ return 1;
+ }
+
+ /* Find the right drivers for the backing files */
+ old_backing_drv = NULL;
+ new_backing_drv = NULL;
+
+ if (!unsafe && bs->backing_format[0] != '\0') {
+ old_backing_drv = bdrv_find_format(bs->backing_format);
+ if (old_backing_drv == NULL) {
+ error_report("Invalid format name: '%s'", bs->backing_format);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if (out_basefmt != NULL) {
+ new_backing_drv = bdrv_find_format(out_basefmt);
+ if (new_backing_drv == NULL) {
+ error_report("Invalid format name: '%s'", out_basefmt);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* For safe rebasing we need to compare old and new backing file */
+ if (unsafe) {
+ /* Make the compiler happy */
+ bs_old_backing = NULL;
+ bs_new_backing = NULL;
+ } else {
+ char backing_name[1024];
+
+ bs_old_backing = bdrv_new("old_backing");
+ bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
+ ret = bdrv_open(bs_old_backing, backing_name, BDRV_O_FLAGS,
+ old_backing_drv);
+ if (ret) {
+ error_report("Could not open old backing file '%s'", backing_name);
+ goto out;
+ }
+
+ bs_new_backing = bdrv_new("new_backing");
+ ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS,
+ new_backing_drv);
+ if (ret) {
+ error_report("Could not open new backing file '%s'", out_baseimg);
+ goto out;
+ }
+ }
+
+ /*
+ * Check each unallocated cluster in the COW file. If it is unallocated,
+ * accesses go to the backing file. We must therefore compare this cluster
+ * in the old and new backing file, and if they differ we need to copy it
+ * from the old backing file into the COW file.
+ *
+ * If qemu-img crashes during this step, no harm is done. The content of
+ * the image is the same as the original one at any time.
+ */
+ if (!unsafe) {
+ uint64_t num_sectors;
+ uint64_t sector;
+ int n;
+ uint8_t * buf_old;
+ uint8_t * buf_new;
+
+ buf_old = qemu_malloc(IO_BUF_SIZE);
+ buf_new = qemu_malloc(IO_BUF_SIZE);
+
+ bdrv_get_geometry(bs, &num_sectors);
+
+ for (sector = 0; sector < num_sectors; sector += n) {
+
+ /* How many sectors can we handle with the next read? */
+ if (sector + (IO_BUF_SIZE / 512) <= num_sectors) {
+ n = (IO_BUF_SIZE / 512);
+ } else {
+ n = num_sectors - sector;
+ }
+
+ /* If the cluster is allocated, we don't need to take action */
+ ret = bdrv_is_allocated(bs, sector, n, &n);
+ if (ret) {
+ continue;
+ }
+
+ /* Read old and new backing file */
+ ret = bdrv_read(bs_old_backing, sector, buf_old, n);
+ if (ret < 0) {
+ error_report("error while reading from old backing file");
+ goto out;
+ }
+ ret = bdrv_read(bs_new_backing, sector, buf_new, n);
+ if (ret < 0) {
+ error_report("error while reading from new backing file");
+ goto out;
+ }
+
+ /* If they differ, we need to write to the COW file */
+ uint64_t written = 0;
+
+ while (written < n) {
+ int pnum;
+
+ if (compare_sectors(buf_old + written * 512,
+ buf_new + written * 512, n - written, &pnum))
+ {
+ ret = bdrv_write(bs, sector + written,
+ buf_old + written * 512, pnum);
+ if (ret < 0) {
+ error_report("Error while writing to COW image: %s",
+ strerror(-ret));
+ goto out;
+ }
+ }
+
+ written += pnum;
+ }
+ }
+
+ qemu_free(buf_old);
+ qemu_free(buf_new);
+ }
+
+ /*
+ * Change the backing file. All clusters that are different from the old
+ * backing file are overwritten in the COW file now, so the visible content
+ * doesn't change when we switch the backing file.
+ */
+ ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt);
+ if (ret == -ENOSPC) {
+ error_report("Could not change the backing file to '%s': No "
+ "space left in the file header", out_baseimg);
+ } else if (ret < 0) {
+ error_report("Could not change the backing file to '%s': %s",
+ out_baseimg, strerror(-ret));
+ }
+
+ /*
+ * TODO At this point it is possible to check if any clusters that are
+ * allocated in the COW file are the same in the backing file. If so, they
+ * could be dropped from the COW file. Don't do this before switching the
+ * backing file, in case of a crash this would lead to corruption.
+ */
+out:
+ /* Cleanup */
+ if (!unsafe) {
+ bdrv_delete(bs_old_backing);
+ bdrv_delete(bs_new_backing);
+ }
+
+ bdrv_delete(bs);
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+static int img_resize(int argc, char **argv)
+{
+ int c, ret, relative;
+ const char *filename, *fmt, *size;
+ int64_t n, total_size;
+ BlockDriverState *bs = NULL;
+ QEMUOptionParameter *param;
+ QEMUOptionParameter resize_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+ };
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind + 1 >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+ size = argv[optind++];
+
+ /* Choose grow, shrink, or absolute resize mode */
+ switch (size[0]) {
+ case '+':
+ relative = 1;
+ size++;
+ break;
+ case '-':
+ relative = -1;
+ size++;
+ break;
+ default:
+ relative = 0;
+ break;
+ }
+
+ /* Parse size */
+ param = parse_option_parameters("", resize_options, NULL);
+ if (set_option_parameter(param, BLOCK_OPT_SIZE, size)) {
+ /* Error message already printed when size parsing fails */
+ ret = -1;
+ goto out;
+ }
+ n = get_option_parameter(param, BLOCK_OPT_SIZE)->value.n;
+ free_option_parameters(param);
+
+ bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
+ if (!bs) {
+ ret = -1;
+ goto out;
+ }
+
+ if (relative) {
+ total_size = bdrv_getlength(bs) + n * relative;
+ } else {
+ total_size = n;
+ }
+ if (total_size <= 0) {
+ error_report("New image size must be positive");
+ ret = -1;
+ goto out;
+ }
+
+ ret = bdrv_truncate(bs, total_size);
+ switch (ret) {
+ case 0:
+ printf("Image resized.\n");
+ break;
+ case -ENOTSUP:
+ error_report("This image format does not support resize");
+ break;
+ case -EACCES:
+ error_report("Image is read-only");
+ break;
+ default:
+ error_report("Error resizing image (%d)", -ret);
+ break;
+ }
+out:
+ if (bs) {
+ bdrv_delete(bs);
+ }
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+static const img_cmd_t img_cmds[] = {
+#define DEF(option, callback, arg_string) \
+ { option, callback },
+#include "qemu-img-cmds.h"
+#undef DEF
+#undef GEN_DOCS
+ { NULL, NULL, },
+};
+
+int main(int argc, char **argv)
+{
+ const img_cmd_t *cmd;
+ const char *cmdname;
+
+ error_set_progname(argv[0]);
+
+ bdrv_init();
+ if (argc < 2)
+ help();
+ cmdname = argv[1];
+ argc--; argv++;
+
+ /* find the command */
+ for(cmd = img_cmds; cmd->name != NULL; cmd++) {
+ if (!strcmp(cmdname, cmd->name)) {
+ return cmd->handler(argc, argv);
+ }
+ }
+
+ /* not found */
+ help();
+ return 0;
+}
diff --git a/qemu-img.texi b/qemu-img.texi
new file mode 100644
index 0000000..ced64a4
--- /dev/null
+++ b/qemu-img.texi
@@ -0,0 +1,266 @@
+@example
+@c man begin SYNOPSIS
+usage: qemu-img command [command options]
+@c man end
+@end example
+
+@c man begin OPTIONS
+
+The following commands are supported:
+
+@include qemu-img-cmds.texi
+
+Command parameters:
+@table @var
+@item filename
+ is a disk image filename
+@item fmt
+is the disk image format. It is guessed automatically in most cases. See below
+for a description of the supported disk formats.
+
+@item size
+is the disk image size in bytes. Optional suffixes @code{k} or @code{K}
+(kilobyte, 1024) @code{M} (megabyte, 1024k) and @code{G} (gigabyte, 1024M)
+and T (terabyte, 1024G) are supported. @code{b} is ignored.
+
+@item output_filename
+is the destination disk image filename
+
+@item output_fmt
+ is the destination format
+@item options
+is a comma separated list of format specific options in a
+name=value format. Use @code{-o ?} for an overview of the options supported
+by the used format or see the format descriptions below for details.
+
+
+@item -c
+indicates that target image must be compressed (qcow format only)
+@item -h
+with or without a command shows help and lists the supported formats
+@end table
+
+Parameters to snapshot subcommand:
+
+@table @option
+
+@item snapshot
+is the name of the snapshot to create, apply or delete
+@item -a
+applies a snapshot (revert disk to saved state)
+@item -c
+creates a snapshot
+@item -d
+deletes a snapshot
+@item -l
+lists all snapshots in the given image
+@end table
+
+Command description:
+
+@table @option
+@item check [-f @var{fmt}] @var{filename}
+
+Perform a consistency check on the disk image @var{filename}.
+
+Only the formats @code{qcow2}, @code{qed} and @code{vdi} support
+consistency checks.
+
+@item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
+
+Create the new disk image @var{filename} of size @var{size} and format
+@var{fmt}. Depending on the file format, you can add one or more @var{options}
+that enable additional features of this format.
+
+If the option @var{backing_file} is specified, then the image will record
+only the differences from @var{backing_file}. No size needs to be specified in
+this case. @var{backing_file} will never be modified unless you use the
+@code{commit} monitor command (or qemu-img commit).
+
+The size can also be specified using the @var{size} option with @code{-o},
+it doesn't need to be specified separately in this case.
+
+@item commit [-f @var{fmt}] @var{filename}
+
+Commit the changes recorded in @var{filename} in its base image.
+
+@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename}
+
+Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
+using format @var{output_fmt}. It can be optionally compressed (@code{-c}
+option) or use any format specific options like encryption (@code{-o} option).
+
+Only the formats @code{qcow} and @code{qcow2} support compression. The
+compression is read-only. It means that if a compressed sector is
+rewritten, then it is rewritten as uncompressed data.
+
+Image conversion is also useful to get smaller image when using a
+growable format such as @code{qcow} or @code{cow}: the empty sectors
+are detected and suppressed from the destination image.
+
+You can use the @var{backing_file} option to force the output image to be
+created as a copy on write image of the specified base image; the
+@var{backing_file} should have the same content as the input's base image,
+however the path, image format, etc may differ.
+
+@item info [-f @var{fmt}] @var{filename}
+
+Give information about the disk image @var{filename}. Use it in
+particular to know the size reserved on disk which can be different
+from the displayed size. If VM snapshots are stored in the disk image,
+they are displayed too.
+
+@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename}
+
+List, apply, create or delete snapshots in image @var{filename}.
+
+@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
+
+Changes the backing file of an image. Only the formats @code{qcow2} and
+@code{qed} support changing the backing file.
+
+The backing file is changed to @var{backing_file} and (if the image format of
+@var{filename} supports this) the backing file format is changed to
+@var{backing_fmt}.
+
+There are two different modes in which @code{rebase} can operate:
+@table @option
+@item Safe mode
+This is the default mode and performs a real rebase operation. The new backing
+file may differ from the old one and qemu-img rebase will take care of keeping
+the guest-visible content of @var{filename} unchanged.
+
+In order to achieve this, any clusters that differ between @var{backing_file}
+and the old backing file of @var{filename} are merged into @var{filename}
+before actually changing the backing file.
+
+Note that the safe mode is an expensive operation, comparable to converting
+an image. It only works if the old backing file still exists.
+
+@item Unsafe mode
+qemu-img uses the unsafe mode if @code{-u} is specified. In this mode, only the
+backing file name and format of @var{filename} is changed without any checks
+on the file contents. The user must take care of specifying the correct new
+backing file, or the guest-visible content of the image will be corrupted.
+
+This mode is useful for renaming or moving the backing file to somewhere else.
+It can be used without an accessible old backing file, i.e. you can use it to
+fix an image whose backing file has already been moved/renamed.
+@end table
+
+@item resize @var{filename} [+ | -]@var{size}
+
+Change the disk image as if it had been created with @var{size}.
+
+Before using this command to shrink a disk image, you MUST use file system and
+partitioning tools inside the VM to reduce allocated file systems and partition
+sizes accordingly. Failure to do so will result in data loss!
+
+After using this command to grow a disk image, you must use file system and
+partitioning tools inside the VM to actually begin using the new space on the
+device.
+@end table
+
+Supported image file formats:
+
+@table @option
+@item raw
+
+Raw disk image format (default). This format has the advantage of
+being simple and easily exportable to all other emulators. If your
+file system supports @emph{holes} (for example in ext2 or ext3 on
+Linux or NTFS on Windows), then only the written sectors will reserve
+space. Use @code{qemu-img info} to know the real size used by the
+image or @code{ls -ls} on Unix/Linux.
+
+@item host_device
+
+Host device format. This format should be used instead of raw when
+converting to block devices or other devices where "holes" are not
+supported.
+
+@item qcow2
+QEMU image format, the most versatile format. Use it to have smaller
+images (useful if your filesystem does not supports holes, for example
+on Windows), optional AES encryption, zlib based compression and
+support of multiple VM snapshots.
+
+Supported options:
+@table @code
+@item backing_file
+File name of a base image (see @option{create} subcommand)
+@item backing_fmt
+Image format of the base image
+@item encryption
+If this option is set to @code{on}, the image is encrypted.
+
+Encryption uses the AES format which is very secure (128 bit keys). Use
+a long password (16 characters) to get maximum protection.
+
+@item cluster_size
+Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster
+sizes can improve the image file size whereas larger cluster sizes generally
+provide better performance.
+
+@item preallocation
+Preallocation mode (allowed values: off, metadata). An image with preallocated
+metadata is initially larger but can improve performance when the image needs
+to grow.
+
+@end table
+
+
+@item qcow
+Old QEMU image format. Left for compatibility.
+
+Supported options:
+@table @code
+@item backing_file
+File name of a base image (see @option{create} subcommand)
+@item encryption
+If this option is set to @code{on}, the image is encrypted.
+@end table
+
+@item cow
+User Mode Linux Copy On Write image format. Used to be the only growable
+image format in QEMU. It is supported only for compatibility with
+previous versions. It does not work on win32.
+@item vdi
+VirtualBox 1.1 compatible image format.
+@item vmdk
+VMware 3 and 4 compatible image format.
+
+Supported options:
+@table @code
+@item backing_fmt
+Image format of the base image
+@item compat6
+Create a VMDK version 6 image (instead of version 4)
+@end table
+
+@item vpc
+VirtualPC compatible image format (VHD).
+
+@item cloop
+Linux Compressed Loop image, useful only to reuse directly compressed
+CD-ROM images present for example in the Knoppix CD-ROMs.
+@end table
+
+
+@c man end
+
+@ignore
+
+@setfilename qemu-img
+@settitle QEMU disk image utility
+
+@c man begin SEEALSO
+The HTML documentation of QEMU for more precise information and Linux
+user mode emulator invocation.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
diff --git a/qemu-io.c b/qemu-io.c
new file mode 100644
index 0000000..4470e49
--- /dev/null
+++ b/qemu-io.c
@@ -0,0 +1,1831 @@
+/*
+ * Command line utility to exercise the QEMU I/O path.
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "cmd.h"
+
+#define VERSION "0.0.1"
+
+#define CMD_NOFILE_OK 0x01
+
+char *progname;
+static BlockDriverState *bs;
+
+static int misalign;
+
+/*
+ * Parse the pattern argument to various sub-commands.
+ *
+ * Because the pattern is used as an argument to memset it must evaluate
+ * to an unsigned integer that fits into a single byte.
+ */
+static int parse_pattern(const char *arg)
+{
+ char *endptr = NULL;
+ long pattern;
+
+ pattern = strtol(arg, &endptr, 0);
+ if (pattern < 0 || pattern > UCHAR_MAX || *endptr != '\0') {
+ printf("%s is not a valid pattern byte\n", arg);
+ return -1;
+ }
+
+ return pattern;
+}
+
+/*
+ * Memory allocation helpers.
+ *
+ * Make sure memory is aligned by default, or purposefully misaligned if
+ * that is specified on the command line.
+ */
+
+#define MISALIGN_OFFSET 16
+static void *qemu_io_alloc(size_t len, int pattern)
+{
+ void *buf;
+
+ if (misalign)
+ len += MISALIGN_OFFSET;
+ buf = qemu_blockalign(bs, len);
+ memset(buf, pattern, len);
+ if (misalign)
+ buf += MISALIGN_OFFSET;
+ return buf;
+}
+
+static void qemu_io_free(void *p)
+{
+ if (misalign)
+ p -= MISALIGN_OFFSET;
+ qemu_vfree(p);
+}
+
+static void
+dump_buffer(const void *buffer, int64_t offset, int len)
+{
+ int i, j;
+ const uint8_t *p;
+
+ for (i = 0, p = buffer; i < len; i += 16) {
+ const uint8_t *s = p;
+
+ printf("%08" PRIx64 ": ", offset + i);
+ for (j = 0; j < 16 && i + j < len; j++, p++)
+ printf("%02x ", *p);
+ printf(" ");
+ for (j = 0; j < 16 && i + j < len; j++, s++) {
+ if (isalnum(*s))
+ printf("%c", *s);
+ else
+ printf(".");
+ }
+ printf("\n");
+ }
+}
+
+static void
+print_report(const char *op, struct timeval *t, int64_t offset,
+ int count, int total, int cnt, int Cflag)
+{
+ char s1[64], s2[64], ts[64];
+
+ timestr(t, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
+ if (!Cflag) {
+ cvtstr((double)total, s1, sizeof(s1));
+ cvtstr(tdiv((double)total, *t), s2, sizeof(s2));
+ printf("%s %d/%d bytes at offset %" PRId64 "\n",
+ op, total, count, offset);
+ printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n",
+ s1, cnt, ts, s2, tdiv((double)cnt, *t));
+ } else {/* bytes,ops,time,bytes/sec,ops/sec */
+ printf("%d,%d,%s,%.3f,%.3f\n",
+ total, cnt, ts,
+ tdiv((double)total, *t),
+ tdiv((double)cnt, *t));
+ }
+}
+
+/*
+ * Parse multiple length statements for vectored I/O, and construct an I/O
+ * vector matching it.
+ */
+static void *
+create_iovec(QEMUIOVector *qiov, char **argv, int nr_iov, int pattern)
+{
+ size_t *sizes = calloc(nr_iov, sizeof(size_t));
+ size_t count = 0;
+ void *buf = NULL;
+ void *p;
+ int i;
+
+ for (i = 0; i < nr_iov; i++) {
+ char *arg = argv[i];
+ int64_t len;
+
+ len = cvtnum(arg);
+ if (len < 0) {
+ printf("non-numeric length argument -- %s\n", arg);
+ goto fail;
+ }
+
+ /* should be SIZE_T_MAX, but that doesn't exist */
+ if (len > INT_MAX) {
+ printf("too large length argument -- %s\n", arg);
+ goto fail;
+ }
+
+ if (len & 0x1ff) {
+ printf("length argument %" PRId64
+ " is not sector aligned\n", len);
+ goto fail;
+ }
+
+ sizes[i] = len;
+ count += len;
+ }
+
+ qemu_iovec_init(qiov, nr_iov);
+
+ buf = p = qemu_io_alloc(count, pattern);
+
+ for (i = 0; i < nr_iov; i++) {
+ qemu_iovec_add(qiov, p, sizes[i]);
+ p += sizes[i];
+ }
+
+fail:
+ free(sizes);
+ return buf;
+}
+
+static int do_read(char *buf, int64_t offset, int count, int *total)
+{
+ int ret;
+
+ ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+ if (ret < 0)
+ return ret;
+ *total = count;
+ return 1;
+}
+
+static int do_write(char *buf, int64_t offset, int count, int *total)
+{
+ int ret;
+
+ ret = bdrv_write(bs, offset >> 9, (uint8_t *)buf, count >> 9);
+ if (ret < 0)
+ return ret;
+ *total = count;
+ return 1;
+}
+
+static int do_pread(char *buf, int64_t offset, int count, int *total)
+{
+ *total = bdrv_pread(bs, offset, (uint8_t *)buf, count);
+ if (*total < 0)
+ return *total;
+ return 1;
+}
+
+static int do_pwrite(char *buf, int64_t offset, int count, int *total)
+{
+ *total = bdrv_pwrite(bs, offset, (uint8_t *)buf, count);
+ if (*total < 0)
+ return *total;
+ return 1;
+}
+
+static int do_load_vmstate(char *buf, int64_t offset, int count, int *total)
+{
+ *total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count);
+ if (*total < 0)
+ return *total;
+ return 1;
+}
+
+static int do_save_vmstate(char *buf, int64_t offset, int count, int *total)
+{
+ *total = bdrv_save_vmstate(bs, (uint8_t *)buf, offset, count);
+ if (*total < 0)
+ return *total;
+ return 1;
+}
+
+#define NOT_DONE 0x7fffffff
+static void aio_rw_done(void *opaque, int ret)
+{
+ *(int *)opaque = ret;
+}
+
+static int do_aio_readv(QEMUIOVector *qiov, int64_t offset, int *total)
+{
+ BlockDriverAIOCB *acb;
+ int async_ret = NOT_DONE;
+
+ acb = bdrv_aio_readv(bs, offset >> 9, qiov, qiov->size >> 9,
+ aio_rw_done, &async_ret);
+ if (!acb)
+ return -EIO;
+
+ while (async_ret == NOT_DONE)
+ qemu_aio_wait();
+
+ *total = qiov->size;
+ return async_ret < 0 ? async_ret : 1;
+}
+
+static int do_aio_writev(QEMUIOVector *qiov, int64_t offset, int *total)
+{
+ BlockDriverAIOCB *acb;
+ int async_ret = NOT_DONE;
+
+ acb = bdrv_aio_writev(bs, offset >> 9, qiov, qiov->size >> 9,
+ aio_rw_done, &async_ret);
+ if (!acb)
+ return -EIO;
+
+ while (async_ret == NOT_DONE)
+ qemu_aio_wait();
+
+ *total = qiov->size;
+ return async_ret < 0 ? async_ret : 1;
+}
+
+struct multiwrite_async_ret {
+ int num_done;
+ int error;
+};
+
+static void multiwrite_cb(void *opaque, int ret)
+{
+ struct multiwrite_async_ret *async_ret = opaque;
+
+ async_ret->num_done++;
+ if (ret < 0) {
+ async_ret->error = ret;
+ }
+}
+
+static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total)
+{
+ int i, ret;
+ struct multiwrite_async_ret async_ret = {
+ .num_done = 0,
+ .error = 0,
+ };
+
+ *total = 0;
+ for (i = 0; i < num_reqs; i++) {
+ reqs[i].cb = multiwrite_cb;
+ reqs[i].opaque = &async_ret;
+ *total += reqs[i].qiov->size;
+ }
+
+ ret = bdrv_aio_multiwrite(bs, reqs, num_reqs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ while (async_ret.num_done < num_reqs) {
+ qemu_aio_wait();
+ }
+
+ return async_ret.error < 0 ? async_ret.error : 1;
+}
+
+static void
+read_help(void)
+{
+ printf(
+"\n"
+" reads a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'read -v 512 1k' - dumps 1 kilobyte read from 512 bytes into the file\n"
+"\n"
+" Reads a segment of the currently open file, optionally dumping it to the\n"
+" standard output stream (with -v option) for subsequent inspection.\n"
+" -b, -- read from the VM state rather than the virtual disk\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -l, -- length for pattern verification (only with -P)\n"
+" -p, -- use bdrv_pread to read the file\n"
+" -P, -- use a pattern to verify read data\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+" -s, -- start offset for pattern verification (only with -P)\n"
+" -v, -- dump buffer to standard output\n"
+"\n");
+}
+
+static int read_f(int argc, char **argv);
+
+static const cmdinfo_t read_cmd = {
+ .name = "read",
+ .altname = "r",
+ .cfunc = read_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-abCpqv] [-P pattern [-s off] [-l len]] off len",
+ .oneline = "reads a number of bytes at a specified offset",
+ .help = read_help,
+};
+
+static int
+read_f(int argc, char **argv)
+{
+ struct timeval t1, t2;
+ int Cflag = 0, pflag = 0, qflag = 0, vflag = 0;
+ int Pflag = 0, sflag = 0, lflag = 0, bflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ int count;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int pattern = 0, pattern_offset = 0, pattern_count = 0;
+
+ while ((c = getopt(argc, argv, "bCl:pP:qs:v")) != EOF) {
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ pattern_count = cvtnum(optarg);
+ if (pattern_count < 0) {
+ printf("non-numeric length argument -- %s\n", optarg);
+ return 0;
+ }
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ pattern = parse_pattern(optarg);
+ if (pattern < 0)
+ return 0;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ pattern_offset = cvtnum(optarg);
+ if (pattern_offset < 0) {
+ printf("non-numeric length argument -- %s\n", optarg);
+ return 0;
+ }
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ return command_usage(&read_cmd);
+ }
+ }
+
+ if (optind != argc - 2)
+ return command_usage(&read_cmd);
+
+ if (bflag && pflag) {
+ printf("-b and -p cannot be specified at the same time\n");
+ return 0;
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ if (!Pflag && (lflag || sflag)) {
+ return command_usage(&read_cmd);
+ }
+
+ if (!lflag) {
+ pattern_count = count - pattern_offset;
+ }
+
+ if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) {
+ printf("pattern verfication range exceeds end of read data\n");
+ return 0;
+ }
+
+ if (!pflag)
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+
+ if (count & 0x1ff) {
+ printf("count %d is not sector aligned\n",
+ count);
+ return 0;
+ }
+ }
+
+ buf = qemu_io_alloc(count, 0xab);
+
+ gettimeofday(&t1, NULL);
+ if (pflag)
+ cnt = do_pread(buf, offset, count, &total);
+ else if (bflag)
+ cnt = do_load_vmstate(buf, offset, count, &total);
+ else
+ cnt = do_read(buf, offset, count, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("read failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (Pflag) {
+ void* cmp_buf = malloc(pattern_count);
+ memset(cmp_buf, pattern, pattern_count);
+ if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %d bytes\n",
+ offset + pattern_offset, pattern_count);
+ }
+ free(cmp_buf);
+ }
+
+ if (qflag)
+ goto out;
+
+ if (vflag)
+ dump_buffer(buf, offset, count);
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("read", &t2, offset, count, total, cnt, Cflag);
+
+out:
+ qemu_io_free(buf);
+
+ return 0;
+}
+
+static void
+readv_help(void)
+{
+ printf(
+"\n"
+" reads a range of bytes from the given offset into multiple buffers\n"
+"\n"
+" Example:\n"
+" 'readv -v 512 1k 1k ' - dumps 2 kilobytes read from 512 bytes into the file\n"
+"\n"
+" Reads a segment of the currently open file, optionally dumping it to the\n"
+" standard output stream (with -v option) for subsequent inspection.\n"
+" Uses multiple iovec buffers if more than one byte range is specified.\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -P, -- use a pattern to verify read data\n"
+" -v, -- dump buffer to standard output\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int readv_f(int argc, char **argv);
+
+static const cmdinfo_t readv_cmd = {
+ .name = "readv",
+ .cfunc = readv_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cqv] [-P pattern ] off len [len..]",
+ .oneline = "reads a number of bytes at a specified offset",
+ .help = readv_help,
+};
+
+static int
+readv_f(int argc, char **argv)
+{
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0, vflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ QEMUIOVector qiov;
+ int pattern = 0;
+ int Pflag = 0;
+
+ while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'P':
+ Pflag = 1;
+ pattern = parse_pattern(optarg);
+ if (pattern < 0)
+ return 0;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ return command_usage(&readv_cmd);
+ }
+ }
+
+ if (optind > argc - 2)
+ return command_usage(&readv_cmd);
+
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ buf = create_iovec(&qiov, &argv[optind], nr_iov, 0xab);
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_readv(&qiov, offset, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("readv failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (Pflag) {
+ void* cmp_buf = malloc(qiov.size);
+ memset(cmp_buf, pattern, qiov.size);
+ if (memcmp(buf, cmp_buf, qiov.size)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %zd bytes\n",
+ offset, qiov.size);
+ }
+ free(cmp_buf);
+ }
+
+ if (qflag)
+ goto out;
+
+ if (vflag)
+ dump_buffer(buf, offset, qiov.size);
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("read", &t2, offset, qiov.size, total, cnt, Cflag);
+
+out:
+ qemu_io_free(buf);
+ return 0;
+}
+
+static void
+write_help(void)
+{
+ printf(
+"\n"
+" writes a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'write 512 1k' - writes 1 kilobyte at 512 bytes into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd).\n"
+" -b, -- write to the VM state rather than the virtual disk\n"
+" -p, -- use bdrv_pwrite to write the file\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int write_f(int argc, char **argv);
+
+static const cmdinfo_t write_cmd = {
+ .name = "write",
+ .altname = "w",
+ .cfunc = write_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-abCpq] [-P pattern ] off len",
+ .oneline = "writes a number of bytes at a specified offset",
+ .help = write_help,
+};
+
+static int
+write_f(int argc, char **argv)
+{
+ struct timeval t1, t2;
+ int Cflag = 0, pflag = 0, qflag = 0, bflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ int count;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int pattern = 0xcd;
+
+ while ((c = getopt(argc, argv, "bCpP:q")) != EOF) {
+ switch (c) {
+ case 'b':
+ bflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'p':
+ pflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0)
+ return 0;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ return command_usage(&write_cmd);
+ }
+ }
+
+ if (optind != argc - 2)
+ return command_usage(&write_cmd);
+
+ if (bflag && pflag) {
+ printf("-b and -p cannot be specified at the same time\n");
+ return 0;
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ if (!pflag) {
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ if (count & 0x1ff) {
+ printf("count %d is not sector aligned\n",
+ count);
+ return 0;
+ }
+ }
+
+ buf = qemu_io_alloc(count, pattern);
+
+ gettimeofday(&t1, NULL);
+ if (pflag)
+ cnt = do_pwrite(buf, offset, count, &total);
+ else if (bflag)
+ cnt = do_save_vmstate(buf, offset, count, &total);
+ else
+ cnt = do_write(buf, offset, count, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("write failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag)
+ goto out;
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, offset, count, total, cnt, Cflag);
+
+out:
+ qemu_io_free(buf);
+
+ return 0;
+}
+
+static void
+writev_help(void)
+{
+ printf(
+"\n"
+" writes a range of bytes from the given offset source from multiple buffers\n"
+"\n"
+" Example:\n"
+" 'write 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd).\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int writev_f(int argc, char **argv);
+
+static const cmdinfo_t writev_cmd = {
+ .name = "writev",
+ .cfunc = writev_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..]",
+ .oneline = "writes a number of bytes at a specified offset",
+ .help = writev_help,
+};
+
+static int
+writev_f(int argc, char **argv)
+{
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, cnt;
+ char *buf;
+ int64_t offset;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ int pattern = 0xcd;
+ QEMUIOVector qiov;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0)
+ return 0;
+ break;
+ default:
+ return command_usage(&writev_cmd);
+ }
+ }
+
+ if (optind > argc - 2)
+ return command_usage(&writev_cmd);
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ buf = create_iovec(&qiov, &argv[optind], nr_iov, pattern);
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_writev(&qiov, offset, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("writev failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag)
+ goto out;
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag);
+out:
+ qemu_io_free(buf);
+ return 0;
+}
+
+static void
+multiwrite_help(void)
+{
+ printf(
+"\n"
+" writes a range of bytes from the given offset source from multiple buffers,\n"
+" in a batch of requests that may be merged by qemu\n"
+"\n"
+" Example:\n"
+" 'multiwrite 512 1k 1k ; 4k 1k' \n"
+" writes 2 kB at 512 bytes and 1 kB at 4 kB into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd). The pattern byte is increased\n"
+" by one for each request contained in the multiwrite command.\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int multiwrite_f(int argc, char **argv);
+
+static const cmdinfo_t multiwrite_cmd = {
+ .name = "multiwrite",
+ .cfunc = multiwrite_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]",
+ .oneline = "issues multiple write requests at once",
+ .help = multiwrite_help,
+};
+
+static int
+multiwrite_f(int argc, char **argv)
+{
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, cnt;
+ char **buf;
+ int64_t offset, first_offset = 0;
+ /* Some compilers get confused and warn if this is not initialized. */
+ int total = 0;
+ int nr_iov;
+ int nr_reqs;
+ int pattern = 0xcd;
+ QEMUIOVector *qiovs;
+ int i;
+ BlockRequest *reqs;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0)
+ return 0;
+ break;
+ default:
+ return command_usage(&writev_cmd);
+ }
+ }
+
+ if (optind > argc - 2)
+ return command_usage(&writev_cmd);
+
+ nr_reqs = 1;
+ for (i = optind; i < argc; i++) {
+ if (!strcmp(argv[i], ";")) {
+ nr_reqs++;
+ }
+ }
+
+ reqs = qemu_malloc(nr_reqs * sizeof(*reqs));
+ buf = qemu_malloc(nr_reqs * sizeof(*buf));
+ qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs));
+
+ for (i = 0; i < nr_reqs; i++) {
+ int j;
+
+ /* Read the offset of the request */
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric offset argument -- %s\n", argv[optind]);
+ return 0;
+ }
+ optind++;
+
+ if (offset & 0x1ff) {
+ printf("offset %lld is not sector aligned\n",
+ (long long)offset);
+ return 0;
+ }
+
+ if (i == 0) {
+ first_offset = offset;
+ }
+
+ /* Read lengths for qiov entries */
+ for (j = optind; j < argc; j++) {
+ if (!strcmp(argv[j], ";")) {
+ break;
+ }
+ }
+
+ nr_iov = j - optind;
+
+ /* Build request */
+ reqs[i].qiov = &qiovs[i];
+ buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern);
+ reqs[i].sector = offset >> 9;
+ reqs[i].nb_sectors = reqs[i].qiov->size >> 9;
+
+ optind = j + 1;
+
+ offset += reqs[i].qiov->size;
+ pattern++;
+ }
+
+ gettimeofday(&t1, NULL);
+ cnt = do_aio_multiwrite(reqs, nr_reqs, &total);
+ gettimeofday(&t2, NULL);
+
+ if (cnt < 0) {
+ printf("aio_multiwrite failed: %s\n", strerror(-cnt));
+ goto out;
+ }
+
+ if (qflag)
+ goto out;
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, t1);
+ print_report("wrote", &t2, first_offset, total, total, cnt, Cflag);
+out:
+ for (i = 0; i < nr_reqs; i++) {
+ qemu_io_free(buf[i]);
+ qemu_iovec_destroy(&qiovs[i]);
+ }
+ qemu_free(buf);
+ qemu_free(reqs);
+ qemu_free(qiovs);
+ return 0;
+}
+
+struct aio_ctx {
+ QEMUIOVector qiov;
+ int64_t offset;
+ char *buf;
+ int qflag;
+ int vflag;
+ int Cflag;
+ int Pflag;
+ int pattern;
+ struct timeval t1;
+};
+
+static void
+aio_write_done(void *opaque, int ret)
+{
+ struct aio_ctx *ctx = opaque;
+ struct timeval t2;
+
+ gettimeofday(&t2, NULL);
+
+
+ if (ret < 0) {
+ printf("aio_write failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ if (ctx->qflag) {
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, ctx->t1);
+ print_report("wrote", &t2, ctx->offset, ctx->qiov.size,
+ ctx->qiov.size, 1, ctx->Cflag);
+out:
+ qemu_io_free(ctx->buf);
+ free(ctx);
+}
+
+static void
+aio_read_done(void *opaque, int ret)
+{
+ struct aio_ctx *ctx = opaque;
+ struct timeval t2;
+
+ gettimeofday(&t2, NULL);
+
+ if (ret < 0) {
+ printf("readv failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ if (ctx->Pflag) {
+ void *cmp_buf = malloc(ctx->qiov.size);
+
+ memset(cmp_buf, ctx->pattern, ctx->qiov.size);
+ if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) {
+ printf("Pattern verification failed at offset %"
+ PRId64 ", %zd bytes\n",
+ ctx->offset, ctx->qiov.size);
+ }
+ free(cmp_buf);
+ }
+
+ if (ctx->qflag) {
+ goto out;
+ }
+
+ if (ctx->vflag) {
+ dump_buffer(ctx->buf, ctx->offset, ctx->qiov.size);
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ t2 = tsub(t2, ctx->t1);
+ print_report("read", &t2, ctx->offset, ctx->qiov.size,
+ ctx->qiov.size, 1, ctx->Cflag);
+out:
+ qemu_io_free(ctx->buf);
+ free(ctx);
+}
+
+static void
+aio_read_help(void)
+{
+ printf(
+"\n"
+" asynchronously reads a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'aio_read -v 512 1k 1k ' - dumps 2 kilobytes read from 512 bytes into the file\n"
+"\n"
+" Reads a segment of the currently open file, optionally dumping it to the\n"
+" standard output stream (with -v option) for subsequent inspection.\n"
+" The read is performed asynchronously and the aio_flush command must be\n"
+" used to ensure all outstanding aio requests have been completed\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -P, -- use a pattern to verify read data\n"
+" -v, -- dump buffer to standard output\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int aio_read_f(int argc, char **argv);
+
+static const cmdinfo_t aio_read_cmd = {
+ .name = "aio_read",
+ .cfunc = aio_read_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cqv] [-P pattern ] off len [len..]",
+ .oneline = "asynchronously reads a number of bytes",
+ .help = aio_read_help,
+};
+
+static int
+aio_read_f(int argc, char **argv)
+{
+ int nr_iov, c;
+ struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+ BlockDriverAIOCB *acb;
+
+ while ((c = getopt(argc, argv, "CP:qv")) != EOF) {
+ switch (c) {
+ case 'C':
+ ctx->Cflag = 1;
+ break;
+ case 'P':
+ ctx->Pflag = 1;
+ ctx->pattern = parse_pattern(optarg);
+ if (ctx->pattern < 0) {
+ free(ctx);
+ return 0;
+ }
+ break;
+ case 'q':
+ ctx->qflag = 1;
+ break;
+ case 'v':
+ ctx->vflag = 1;
+ break;
+ default:
+ free(ctx);
+ return command_usage(&aio_read_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ free(ctx);
+ return command_usage(&aio_read_cmd);
+ }
+
+ ctx->offset = cvtnum(argv[optind]);
+ if (ctx->offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ free(ctx);
+ return 0;
+ }
+ optind++;
+
+ if (ctx->offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ ctx->offset);
+ free(ctx);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, 0xab);
+
+ gettimeofday(&ctx->t1, NULL);
+ acb = bdrv_aio_readv(bs, ctx->offset >> 9, &ctx->qiov,
+ ctx->qiov.size >> 9, aio_read_done, ctx);
+ if (!acb) {
+ free(ctx->buf);
+ free(ctx);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void
+aio_write_help(void)
+{
+ printf(
+"\n"
+" asynchronously writes a range of bytes from the given offset source \n"
+" from multiple buffers\n"
+"\n"
+" Example:\n"
+" 'aio_write 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file\n"
+"\n"
+" Writes into a segment of the currently open file, using a buffer\n"
+" filled with a set pattern (0xcdcdcdcd).\n"
+" The write is performed asynchronously and the aio_flush command must be\n"
+" used to ensure all outstanding aio requests have been completed\n"
+" -P, -- use different pattern to fill file\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int aio_write_f(int argc, char **argv);
+
+static const cmdinfo_t aio_write_cmd = {
+ .name = "aio_write",
+ .cfunc = aio_write_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] [-P pattern ] off len [len..]",
+ .oneline = "asynchronously writes a number of bytes",
+ .help = aio_write_help,
+};
+
+static int
+aio_write_f(int argc, char **argv)
+{
+ int nr_iov, c;
+ int pattern = 0xcd;
+ struct aio_ctx *ctx = calloc(1, sizeof(struct aio_ctx));
+ BlockDriverAIOCB *acb;
+
+ while ((c = getopt(argc, argv, "CqP:")) != EOF) {
+ switch (c) {
+ case 'C':
+ ctx->Cflag = 1;
+ break;
+ case 'q':
+ ctx->qflag = 1;
+ break;
+ case 'P':
+ pattern = parse_pattern(optarg);
+ if (pattern < 0)
+ return 0;
+ break;
+ default:
+ free(ctx);
+ return command_usage(&aio_write_cmd);
+ }
+ }
+
+ if (optind > argc - 2) {
+ free(ctx);
+ return command_usage(&aio_write_cmd);
+ }
+
+ ctx->offset = cvtnum(argv[optind]);
+ if (ctx->offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ free(ctx);
+ return 0;
+ }
+ optind++;
+
+ if (ctx->offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ ctx->offset);
+ free(ctx);
+ return 0;
+ }
+
+ nr_iov = argc - optind;
+ ctx->buf = create_iovec(&ctx->qiov, &argv[optind], nr_iov, pattern);
+
+ gettimeofday(&ctx->t1, NULL);
+ acb = bdrv_aio_writev(bs, ctx->offset >> 9, &ctx->qiov,
+ ctx->qiov.size >> 9, aio_write_done, ctx);
+ if (!acb) {
+ free(ctx->buf);
+ free(ctx);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+aio_flush_f(int argc, char **argv)
+{
+ qemu_aio_flush();
+ return 0;
+}
+
+static const cmdinfo_t aio_flush_cmd = {
+ .name = "aio_flush",
+ .cfunc = aio_flush_f,
+ .oneline = "completes all outstanding aio requests"
+};
+
+static int
+flush_f(int argc, char **argv)
+{
+ bdrv_flush(bs);
+ return 0;
+}
+
+static const cmdinfo_t flush_cmd = {
+ .name = "flush",
+ .altname = "f",
+ .cfunc = flush_f,
+ .oneline = "flush all in-core file state to disk",
+};
+
+static int
+truncate_f(int argc, char **argv)
+{
+ int64_t offset;
+ int ret;
+
+ offset = cvtnum(argv[1]);
+ if (offset < 0) {
+ printf("non-numeric truncate argument -- %s\n", argv[1]);
+ return 0;
+ }
+
+ ret = bdrv_truncate(bs, offset);
+ if (ret < 0) {
+ printf("truncate: %s\n", strerror(-ret));
+ return 0;
+ }
+
+ return 0;
+}
+
+static const cmdinfo_t truncate_cmd = {
+ .name = "truncate",
+ .altname = "t",
+ .cfunc = truncate_f,
+ .argmin = 1,
+ .argmax = 1,
+ .args = "off",
+ .oneline = "truncates the current file at the given offset",
+};
+
+static int
+length_f(int argc, char **argv)
+{
+ int64_t size;
+ char s1[64];
+
+ size = bdrv_getlength(bs);
+ if (size < 0) {
+ printf("getlength: %s\n", strerror(-size));
+ return 0;
+ }
+
+ cvtstr(size, s1, sizeof(s1));
+ printf("%s\n", s1);
+ return 0;
+}
+
+
+static const cmdinfo_t length_cmd = {
+ .name = "length",
+ .altname = "l",
+ .cfunc = length_f,
+ .oneline = "gets the length of the current file",
+};
+
+
+static int
+info_f(int argc, char **argv)
+{
+ BlockDriverInfo bdi;
+ char s1[64], s2[64];
+ int ret;
+
+ if (bs->drv && bs->drv->format_name)
+ printf("format name: %s\n", bs->drv->format_name);
+ if (bs->drv && bs->drv->protocol_name)
+ printf("format name: %s\n", bs->drv->protocol_name);
+
+ ret = bdrv_get_info(bs, &bdi);
+ if (ret)
+ return 0;
+
+ cvtstr(bdi.cluster_size, s1, sizeof(s1));
+ cvtstr(bdi.vm_state_offset, s2, sizeof(s2));
+
+ printf("cluster size: %s\n", s1);
+ printf("vm state offset: %s\n", s2);
+
+ return 0;
+}
+
+
+
+static const cmdinfo_t info_cmd = {
+ .name = "info",
+ .altname = "i",
+ .cfunc = info_f,
+ .oneline = "prints information about the current file",
+};
+
+static void
+discard_help(void)
+{
+ printf(
+"\n"
+" discards a range of bytes from the given offset\n"
+"\n"
+" Example:\n"
+" 'discard 512 1k' - discards 1 kilobyte from 512 bytes into the file\n"
+"\n"
+" Discards a segment of the currently open file.\n"
+" -C, -- report statistics in a machine parsable format\n"
+" -q, -- quiet mode, do not show I/O statistics\n"
+"\n");
+}
+
+static int discard_f(int argc, char **argv);
+
+static const cmdinfo_t discard_cmd = {
+ .name = "discard",
+ .altname = "d",
+ .cfunc = discard_f,
+ .argmin = 2,
+ .argmax = -1,
+ .args = "[-Cq] off len",
+ .oneline = "discards a number of bytes at a specified offset",
+ .help = discard_help,
+};
+
+static int
+discard_f(int argc, char **argv)
+{
+ struct timeval t1, t2;
+ int Cflag = 0, qflag = 0;
+ int c, ret;
+ int64_t offset;
+ int count;
+
+ while ((c = getopt(argc, argv, "Cq")) != EOF) {
+ switch (c) {
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ default:
+ return command_usage(&discard_cmd);
+ }
+ }
+
+ if (optind != argc - 2) {
+ return command_usage(&discard_cmd);
+ }
+
+ offset = cvtnum(argv[optind]);
+ if (offset < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ optind++;
+ count = cvtnum(argv[optind]);
+ if (count < 0) {
+ printf("non-numeric length argument -- %s\n", argv[optind]);
+ return 0;
+ }
+
+ gettimeofday(&t1, NULL);
+ ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS);
+ gettimeofday(&t2, NULL);
+
+ if (ret < 0) {
+ printf("discard failed: %s\n", strerror(-ret));
+ goto out;
+ }
+
+ /* Finally, report back -- -C gives a parsable format */
+ if (!qflag) {
+ t2 = tsub(t2, t1);
+ print_report("discard", &t2, offset, count, count, 1, Cflag);
+ }
+
+out:
+ return 0;
+}
+
+static int
+alloc_f(int argc, char **argv)
+{
+ int64_t offset;
+ int nb_sectors, remaining;
+ char s1[64];
+ int num, sum_alloc;
+ int ret;
+
+ offset = cvtnum(argv[1]);
+ if (offset & 0x1ff) {
+ printf("offset %" PRId64 " is not sector aligned\n",
+ offset);
+ return 0;
+ }
+
+ if (argc == 3)
+ nb_sectors = cvtnum(argv[2]);
+ else
+ nb_sectors = 1;
+
+ remaining = nb_sectors;
+ sum_alloc = 0;
+ while (remaining) {
+ ret = bdrv_is_allocated(bs, offset >> 9, nb_sectors, &num);
+ remaining -= num;
+ if (ret) {
+ sum_alloc += num;
+ }
+ }
+
+ cvtstr(offset, s1, sizeof(s1));
+
+ printf("%d/%d sectors allocated at offset %s\n",
+ sum_alloc, nb_sectors, s1);
+ return 0;
+}
+
+static const cmdinfo_t alloc_cmd = {
+ .name = "alloc",
+ .altname = "a",
+ .argmin = 1,
+ .argmax = 2,
+ .cfunc = alloc_f,
+ .args = "off [sectors]",
+ .oneline = "checks if a sector is present in the file",
+};
+
+static int
+map_f(int argc, char **argv)
+{
+ int64_t offset;
+ int64_t nb_sectors;
+ char s1[64];
+ int num, num_checked;
+ int ret;
+ const char *retstr;
+
+ offset = 0;
+ nb_sectors = bs->total_sectors;
+
+ do {
+ num_checked = MIN(nb_sectors, INT_MAX);
+ ret = bdrv_is_allocated(bs, offset, num_checked, &num);
+ retstr = ret ? " allocated" : "not allocated";
+ cvtstr(offset << 9ULL, s1, sizeof(s1));
+ printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n",
+ offset << 9ULL, num, num_checked, retstr, s1, ret);
+
+ offset += num;
+ nb_sectors -= num;
+ } while(offset < bs->total_sectors);
+
+ return 0;
+}
+
+static const cmdinfo_t map_cmd = {
+ .name = "map",
+ .argmin = 0,
+ .argmax = 0,
+ .cfunc = map_f,
+ .args = "",
+ .oneline = "prints the allocated areas of a file",
+};
+
+
+static int
+close_f(int argc, char **argv)
+{
+ bdrv_close(bs);
+ bs = NULL;
+ return 0;
+}
+
+static const cmdinfo_t close_cmd = {
+ .name = "close",
+ .altname = "c",
+ .cfunc = close_f,
+ .oneline = "close the current open file",
+};
+
+static int openfile(char *name, int flags, int growable)
+{
+ if (bs) {
+ fprintf(stderr, "file open already, try 'help close'\n");
+ return 1;
+ }
+
+ if (growable) {
+ if (bdrv_file_open(&bs, name, flags)) {
+ fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ return 1;
+ }
+ } else {
+ bs = bdrv_new("hda");
+
+ if (bdrv_open(bs, name, flags, NULL) < 0) {
+ fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ bs = NULL;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+open_help(void)
+{
+ printf(
+"\n"
+" opens a new file in the requested mode\n"
+"\n"
+" Example:\n"
+" 'open -Cn /tmp/data' - creates/opens data file read-write and uncached\n"
+"\n"
+" Opens a file for subsequent use by all of the other qemu-io commands.\n"
+" -r, -- open file read-only\n"
+" -s, -- use snapshot file\n"
+" -n, -- disable host cache\n"
+" -g, -- allow file to grow (only applies to protocols)"
+"\n");
+}
+
+static int open_f(int argc, char **argv);
+
+static const cmdinfo_t open_cmd = {
+ .name = "open",
+ .altname = "o",
+ .cfunc = open_f,
+ .argmin = 1,
+ .argmax = -1,
+ .flags = CMD_NOFILE_OK,
+ .args = "[-Crsn] [path]",
+ .oneline = "open the file specified by path",
+ .help = open_help,
+};
+
+static int
+open_f(int argc, char **argv)
+{
+ int flags = 0;
+ int readonly = 0;
+ int growable = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "snrg")) != EOF) {
+ switch (c) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE;
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'g':
+ growable = 1;
+ break;
+ default:
+ return command_usage(&open_cmd);
+ }
+ }
+
+ if (!readonly) {
+ flags |= BDRV_O_RDWR;
+ }
+
+ if (optind != argc - 1)
+ return command_usage(&open_cmd);
+
+ return openfile(argv[optind], flags, growable);
+}
+
+static int
+init_args_command(
+ int index)
+{
+ /* only one device allowed so far */
+ if (index >= 1)
+ return 0;
+ return ++index;
+}
+
+static int
+init_check_command(
+ const cmdinfo_t *ct)
+{
+ if (ct->flags & CMD_FLAG_GLOBAL)
+ return 1;
+ if (!(ct->flags & CMD_NOFILE_OK) && !bs) {
+ fprintf(stderr, "no file open, try 'help open'\n");
+ return 0;
+ }
+ return 1;
+}
+
+static void usage(const char *name)
+{
+ printf(
+"Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n"
+"QEMU Disk exerciser\n"
+"\n"
+" -c, --cmd command to execute\n"
+" -r, --read-only export read-only\n"
+" -s, --snapshot use snapshot file\n"
+" -n, --nocache disable host cache\n"
+" -g, --growable allow file to grow (only applies to protocols)\n"
+" -m, --misalign misalign allocations for O_DIRECT\n"
+" -k, --native-aio use kernel AIO implementation (on Linux only)\n"
+" -h, --help display this help and exit\n"
+" -V, --version output version information and exit\n"
+"\n",
+ name);
+}
+
+
+int main(int argc, char **argv)
+{
+ int readonly = 0;
+ int growable = 0;
+ const char *sopt = "hVc:rsnmgk";
+ const struct option lopt[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "offset", 1, NULL, 'o' },
+ { "cmd", 1, NULL, 'c' },
+ { "read-only", 0, NULL, 'r' },
+ { "snapshot", 0, NULL, 's' },
+ { "nocache", 0, NULL, 'n' },
+ { "misalign", 0, NULL, 'm' },
+ { "growable", 0, NULL, 'g' },
+ { "native-aio", 0, NULL, 'k' },
+ { NULL, 0, NULL, 0 }
+ };
+ int c;
+ int opt_index = 0;
+ int flags = 0;
+
+ progname = basename(argv[0]);
+
+ while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) {
+ switch (c) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE;
+ break;
+ case 'c':
+ add_user_command(optarg);
+ break;
+ case 'r':
+ readonly = 1;
+ break;
+ case 'm':
+ misalign = 1;
+ break;
+ case 'g':
+ growable = 1;
+ break;
+ case 'k':
+ flags |= BDRV_O_NATIVE_AIO;
+ break;
+ case 'V':
+ printf("%s version %s\n", progname, VERSION);
+ exit(0);
+ case 'h':
+ usage(progname);
+ exit(0);
+ default:
+ usage(progname);
+ exit(1);
+ }
+ }
+
+ if ((argc - optind) > 1) {
+ usage(progname);
+ exit(1);
+ }
+
+ bdrv_init();
+
+ /* initialize commands */
+ quit_init();
+ help_init();
+ add_command(&open_cmd);
+ add_command(&close_cmd);
+ add_command(&read_cmd);
+ add_command(&readv_cmd);
+ add_command(&write_cmd);
+ add_command(&writev_cmd);
+ add_command(&multiwrite_cmd);
+ add_command(&aio_read_cmd);
+ add_command(&aio_write_cmd);
+ add_command(&aio_flush_cmd);
+ add_command(&flush_cmd);
+ add_command(&truncate_cmd);
+ add_command(&length_cmd);
+ add_command(&info_cmd);
+ add_command(&discard_cmd);
+ add_command(&alloc_cmd);
+ add_command(&map_cmd);
+
+ add_args_command(init_args_command);
+ add_check_command(init_check_command);
+
+ /* open the device */
+ if (!readonly) {
+ flags |= BDRV_O_RDWR;
+ }
+
+ if ((argc - optind) == 1)
+ openfile(argv[optind], flags, growable);
+ command_loop();
+
+ /*
+ * Make sure all outstanding requests get flushed the program exits.
+ */
+ qemu_aio_flush();
+
+ if (bs)
+ bdrv_close(bs);
+ return 0;
+}
diff --git a/qemu-kvm-ia64.c b/qemu-kvm-ia64.c
new file mode 100644
index 0000000..39bcbeb
--- /dev/null
+++ b/qemu-kvm-ia64.c
@@ -0,0 +1,145 @@
+#include "config.h"
+#include "config-host.h"
+
+#include <string.h>
+
+#include "hw/hw.h"
+#include "qemu-kvm.h"
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/io.h>
+
+
+
+int kvm_arch_qemu_create_context(void)
+{
+ return 0;
+}
+
+void kvm_arch_load_regs(CPUState *env, int level)
+{
+}
+
+
+void kvm_arch_save_regs(CPUState *env)
+{
+}
+
+int kvm_arch_init_vcpu(CPUState *cenv)
+{
+ return 0;
+}
+
+int kvm_arch_halt(kvm_vcpu_context_t vcpu)
+{
+ CPUState *env = cpu_single_env;
+ env->hflags |= HF_HALTED_MASK;
+ return 1;
+}
+
+void kvm_arch_pre_kvm_run(void *opaque, CPUState *env)
+{
+}
+
+void kvm_arch_post_kvm_run(void *opaque, CPUState *env)
+{
+}
+
+int kvm_arch_has_work(CPUState *env)
+{
+ return 1;
+}
+
+int kvm_arch_try_push_interrupts(void *opaque)
+{
+ return 1;
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *current_env,
+ struct kvm_sw_breakpoint *bp)
+{
+ return -EINVAL;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ return -ENOSYS;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+}
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ return 0;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+}
+
+void kvm_arch_save_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ int r;
+ struct kvm_mp_state mp_state;
+
+ r = kvm_get_mpstate(env->kvm_cpu_state.vcpu_ctx, &mp_state);
+ if (r < 0)
+ env->mp_state = -1;
+ else
+ env->mp_state = mp_state.mp_state;
+#endif
+}
+
+void kvm_arch_load_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ struct kvm_mp_state mp_state = { .mp_state = env->mp_state };
+
+ /*
+ * -1 indicates that the host did not support GET_MP_STATE ioctl,
+ * so don't touch it.
+ */
+ if (env->mp_state != -1)
+ kvm_set_mpstate(env->kvm_cpu_state.vcpu_ctx, &mp_state);
+#endif
+}
+
+void kvm_arch_cpu_reset(CPUState *env)
+{
+ if (kvm_irqchip_in_kernel(kvm_context)) {
+#ifdef KVM_CAP_MP_STATE
+ struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED
+ };
+ kvm_set_mpstate(env, &mp_state);
+#endif
+ } else {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ env->halted = 1;
+ }
+}
+
+void kvm_arch_do_ioperm(void *_data)
+{
+ struct ioperm_data *data = _data;
+ ioperm(data->start_port, data->num, data->turn_on);
+}
+
+void kvm_arch_process_irqchip_events(CPUState *env)
+{
+}
diff --git a/qemu-kvm-x86.c b/qemu-kvm-x86.c
new file mode 100644
index 0000000..4be5e32
--- /dev/null
+++ b/qemu-kvm-x86.c
@@ -0,0 +1,814 @@
+/*
+ * qemu/kvm integration, x86 specific code
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+
+#include "config.h"
+#include "config-host.h"
+
+#include <string.h>
+#include "hw/hw.h"
+#include "gdbstub.h"
+#include <sys/io.h>
+
+#include "qemu-kvm.h"
+#include "libkvm.h"
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <linux/kvm_para.h>
+#include <sys/ioctl.h>
+
+#include "kvm.h"
+#include "hw/apic.h"
+
+#define MSR_IA32_TSC 0x10
+
+static struct kvm_msr_list *kvm_msr_list;
+extern unsigned int kvm_shadow_memory;
+
+int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr)
+{
+ int r;
+
+ r = kvm_vm_ioctl(kvm_state, KVM_SET_TSS_ADDR, addr);
+ if (r < 0) {
+ fprintf(stderr, "kvm_set_tss_addr: %m\n");
+ return r;
+ }
+ return 0;
+}
+
+static int kvm_init_tss(kvm_context_t kvm)
+{
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR);
+ if (r > 0) {
+ /*
+ * this address is 3 pages before the bios, and the bios should present
+ * as unavaible memory
+ */
+ r = kvm_set_tss_addr(kvm, 0xfeffd000);
+ if (r < 0) {
+ fprintf(stderr, "kvm_init_tss: unable to set tss addr\n");
+ return r;
+ }
+ } else {
+ fprintf(stderr, "kvm does not support KVM_CAP_SET_TSS_ADDR\n");
+ }
+ return 0;
+}
+
+static int kvm_set_identity_map_addr(kvm_context_t kvm, uint64_t addr)
+{
+#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR);
+ if (r > 0) {
+ r = kvm_vm_ioctl(kvm_state, KVM_SET_IDENTITY_MAP_ADDR, &addr);
+ if (r == -1) {
+ fprintf(stderr, "kvm_set_identity_map_addr: %m\n");
+ return -errno;
+ }
+ return 0;
+ }
+#endif
+ return -ENOSYS;
+}
+
+static int kvm_init_identity_map_page(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR);
+ if (r > 0) {
+ /*
+ * this address is 4 pages before the bios, and the bios should present
+ * as unavaible memory
+ */
+ r = kvm_set_identity_map_addr(kvm, 0xfeffc000);
+ if (r < 0) {
+ fprintf(stderr, "kvm_init_identity_map_page: "
+ "unable to set identity mapping addr\n");
+ return r;
+ }
+ }
+#endif
+ return 0;
+}
+
+static int kvm_create_pit(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_PIT
+ int r;
+
+ kvm_state->pit_in_kernel = 0;
+ if (!kvm->no_pit_creation) {
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_PIT);
+ if (r > 0) {
+ r = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT);
+ if (r >= 0) {
+ kvm_state->pit_in_kernel = 1;
+ } else {
+ fprintf(stderr, "Create kernel PIC irqchip failed\n");
+ return r;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r = 0;
+
+ r = kvm_init_tss(kvm);
+ if (r < 0) {
+ return r;
+ }
+
+ r = kvm_init_identity_map_page(kvm);
+ if (r < 0) {
+ return r;
+ }
+
+ /*
+ * Tell fw_cfg to notify the BIOS to reserve the range.
+ */
+ if (e820_add_entry(0xfeffc000, 0x4000, E820_RESERVED) < 0) {
+ perror("e820_add_entry() table is full");
+ exit(1);
+ }
+
+ r = kvm_create_pit(kvm);
+ if (r < 0) {
+ return r;
+ }
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0) {
+ return r;
+ }
+
+ return 0;
+}
+
+#ifdef KVM_EXIT_TPR_ACCESS
+
+static int kvm_handle_tpr_access(CPUState *env)
+{
+ struct kvm_run *run = env->kvm_run;
+ kvm_tpr_access_report(env,
+ run->tpr_access.rip,
+ run->tpr_access.is_write);
+ return 0;
+}
+
+
+int kvm_enable_vapic(CPUState *env, uint64_t vapic)
+{
+ struct kvm_vapic_addr va = {
+ .vapic_addr = vapic,
+ };
+
+ return kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va);
+}
+
+#endif
+
+int kvm_arch_run(CPUState *env)
+{
+ int r = 0;
+ struct kvm_run *run = env->kvm_run;
+
+ switch (run->exit_reason) {
+#ifdef KVM_EXIT_SET_TPR
+ case KVM_EXIT_SET_TPR:
+ break;
+#endif
+#ifdef KVM_EXIT_TPR_ACCESS
+ case KVM_EXIT_TPR_ACCESS:
+ r = kvm_handle_tpr_access(env);
+ break;
+#endif
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s)
+{
+ int r = 0;
+
+ if (!kvm_irqchip_in_kernel()) {
+ return r;
+ }
+
+ r = kvm_vcpu_ioctl(env, KVM_GET_LAPIC, s);
+ if (r < 0) {
+ fprintf(stderr, "KVM_GET_LAPIC failed\n");
+ }
+ return r;
+}
+
+int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s)
+{
+ int r = 0;
+
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+
+ r = kvm_vcpu_ioctl(env, KVM_SET_LAPIC, s);
+
+ if (r < 0) {
+ fprintf(stderr, "KVM_SET_LAPIC failed\n");
+ }
+ return r;
+}
+
+#endif
+
+#ifdef KVM_CAP_PIT
+
+int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s)
+{
+ if (!kvm_pit_in_kernel()) {
+ return 0;
+ }
+ return kvm_vm_ioctl(kvm_state, KVM_GET_PIT, s);
+}
+
+int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s)
+{
+ if (!kvm_pit_in_kernel()) {
+ return 0;
+ }
+ return kvm_vm_ioctl(kvm_state, KVM_SET_PIT, s);
+}
+
+#ifdef KVM_CAP_PIT_STATE2
+int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2)
+{
+ if (!kvm_pit_in_kernel()) {
+ return 0;
+ }
+ return kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, ps2);
+}
+
+int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2)
+{
+ if (!kvm_pit_in_kernel()) {
+ return 0;
+ }
+ return kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, ps2);
+}
+
+#endif
+#endif
+
+int kvm_has_pit_state2(kvm_context_t kvm)
+{
+ int r = 0;
+
+#ifdef KVM_CAP_PIT_STATE2
+ r = kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2);
+#endif
+ return r;
+}
+
+void kvm_show_code(CPUState *env)
+{
+#define SHOW_CODE_LEN 50
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r, n;
+ int back_offset;
+ unsigned char code;
+ char code_str[SHOW_CODE_LEN * 3 + 1];
+ unsigned long rip;
+
+ r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
+ if (r < 0 ) {
+ perror("KVM_GET_SREGS");
+ return;
+ }
+ r = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (r < 0) {
+ perror("KVM_GET_REGS");
+ return;
+ }
+ rip = sregs.cs.base + regs.rip;
+ back_offset = regs.rip;
+ if (back_offset > 20) {
+ back_offset = 20;
+ }
+ *code_str = 0;
+ for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) {
+ if (n == 0) {
+ strcat(code_str, " -->");
+ }
+ cpu_physical_memory_rw(rip + n, &code, 1, 1);
+ sprintf(code_str + strlen(code_str), " %02x", code);
+ }
+ fprintf(stderr, "code:%s\n", code_str);
+}
+
+
+/*
+ * Returns available msr list. User must free.
+ */
+static struct kvm_msr_list *kvm_get_msr_list(void)
+{
+ struct kvm_msr_list sizer, *msrs;
+ int r;
+
+ sizer.nmsrs = 0;
+ r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, &sizer);
+ if (r < 0 && r != -E2BIG) {
+ return NULL;
+ }
+ /* Old kernel modules had a bug and could write beyond the provided
+ memory. Allocate at least a safe amount of 1K. */
+ msrs = qemu_malloc(MAX(1024, sizeof(*msrs) +
+ sizer.nmsrs * sizeof(*msrs->indices)));
+
+ msrs->nmsrs = sizer.nmsrs;
+ r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, msrs);
+ if (r < 0) {
+ free(msrs);
+ errno = r;
+ return NULL;
+ }
+ return msrs;
+}
+
+static void print_seg(FILE *file, const char *name, struct kvm_segment *seg)
+{
+ fprintf(stderr,
+ "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d"
+ " g %d avl %d)\n",
+ name, seg->selector, seg->base, seg->limit, seg->present,
+ seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g,
+ seg->avl);
+}
+
+static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt)
+{
+ fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit);
+}
+
+void kvm_show_regs(CPUState *env)
+{
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int r;
+
+ r = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (r < 0) {
+ perror("KVM_GET_REGS");
+ return;
+ }
+ fprintf(stderr,
+ "rax %016llx rbx %016llx rcx %016llx rdx %016llx\n"
+ "rsi %016llx rdi %016llx rsp %016llx rbp %016llx\n"
+ "r8 %016llx r9 %016llx r10 %016llx r11 %016llx\n"
+ "r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n"
+ "rip %016llx rflags %08llx\n",
+ regs.rax, regs.rbx, regs.rcx, regs.rdx,
+ regs.rsi, regs.rdi, regs.rsp, regs.rbp,
+ regs.r8, regs.r9, regs.r10, regs.r11,
+ regs.r12, regs.r13, regs.r14, regs.r15,
+ regs.rip, regs.rflags);
+ r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
+ if (r < 0) {
+ perror("KVM_GET_SREGS");
+ return;
+ }
+ print_seg(stderr, "cs", &sregs.cs);
+ print_seg(stderr, "ds", &sregs.ds);
+ print_seg(stderr, "es", &sregs.es);
+ print_seg(stderr, "ss", &sregs.ss);
+ print_seg(stderr, "fs", &sregs.fs);
+ print_seg(stderr, "gs", &sregs.gs);
+ print_seg(stderr, "tr", &sregs.tr);
+ print_seg(stderr, "ldt", &sregs.ldt);
+ print_dt(stderr, "gdt", &sregs.gdt);
+ print_dt(stderr, "idt", &sregs.idt);
+ fprintf(stderr, "cr0 %llx cr2 %llx cr3 %llx cr4 %llx cr8 %llx"
+ " efer %llx\n",
+ sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4, sregs.cr8,
+ sregs.efer);
+}
+
+static void kvm_set_cr8(CPUState *env, uint64_t cr8)
+{
+ env->kvm_run->cr8 = cr8;
+}
+
+int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages)
+{
+#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION,
+ KVM_CAP_MMU_SHADOW_CACHE_CONTROL);
+ if (r > 0) {
+ r = kvm_vm_ioctl(kvm_state, KVM_SET_NR_MMU_PAGES, nrshadow_pages);
+ if (r < 0) {
+ fprintf(stderr, "kvm_set_shadow_pages: %m\n");
+ return r;
+ }
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages)
+{
+#ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION,
+ KVM_CAP_MMU_SHADOW_CACHE_CONTROL);
+ if (r > 0) {
+ *nrshadow_pages = kvm_vm_ioctl(kvm_state, KVM_GET_NR_MMU_PAGES);
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+#ifdef KVM_CAP_VAPIC
+static int kvm_enable_tpr_access_reporting(CPUState *env)
+{
+ int r;
+ struct kvm_tpr_access_ctl tac = { .enabled = 1 };
+
+ r = kvm_ioctl(env->kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC);
+ if (r <= 0) {
+ return -ENOSYS;
+ }
+ return kvm_vcpu_ioctl(env, KVM_TPR_ACCESS_REPORTING, &tac);
+}
+#endif
+
+#ifdef KVM_CAP_ADJUST_CLOCK
+static struct kvm_clock_data kvmclock_data;
+
+static void kvmclock_pre_save(void *opaque)
+{
+ struct kvm_clock_data *cl = opaque;
+
+ kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, cl);
+}
+
+static int kvmclock_post_load(void *opaque, int version_id)
+{
+ struct kvm_clock_data *cl = opaque;
+
+ return kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, cl);
+}
+
+static const VMStateDescription vmstate_kvmclock= {
+ .name = "kvmclock",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .pre_save = kvmclock_pre_save,
+ .post_load = kvmclock_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_U64(clock, struct kvm_clock_data),
+ VMSTATE_END_OF_LIST()
+ }
+};
+#endif
+
+int kvm_arch_qemu_create_context(void)
+{
+ int r;
+ struct utsname utsname;
+
+ uname(&utsname);
+ lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
+
+ if (kvm_shadow_memory) {
+ kvm_set_shadow_pages(kvm_context, kvm_shadow_memory);
+ }
+
+ /* initialize has_msr_star/has_msr_hsave_pa */
+ r = kvm_get_supported_msrs(kvm_state);
+ if (r < 0) {
+ return r;
+ }
+
+ kvm_msr_list = kvm_get_msr_list();
+ if (!kvm_msr_list) {
+ return -1;
+ }
+
+#ifdef KVM_CAP_ADJUST_CLOCK
+ if (kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK)) {
+ vmstate_register(NULL, 0, &vmstate_kvmclock, &kvmclock_data);
+ }
+#endif
+
+ r = kvm_set_boot_cpu_id(0);
+ if (r < 0 && r != -ENOSYS) {
+ return r;
+ }
+
+ return 0;
+}
+
+static void kvm_arch_save_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ int r;
+ struct kvm_mp_state mp_state;
+
+ r = kvm_get_mpstate(env, &mp_state);
+ if (r < 0) {
+ env->mp_state = -1;
+ } else {
+ env->mp_state = mp_state.mp_state;
+ if (kvm_irqchip_in_kernel()) {
+ env->halted = (env->mp_state == KVM_MP_STATE_HALTED);
+ }
+ }
+#else
+ env->mp_state = -1;
+#endif
+}
+
+static void kvm_arch_load_mpstate(CPUState *env)
+{
+#ifdef KVM_CAP_MP_STATE
+ struct kvm_mp_state mp_state;
+
+ /*
+ * -1 indicates that the host did not support GET_MP_STATE ioctl,
+ * so don't touch it.
+ */
+ if (env->mp_state != -1) {
+ mp_state.mp_state = env->mp_state;
+ kvm_set_mpstate(env, &mp_state);
+ }
+#endif
+}
+
+#define XSAVE_CWD_RIP 2
+#define XSAVE_CWD_RDP 4
+#define XSAVE_MXCSR 6
+#define XSAVE_ST_SPACE 8
+#define XSAVE_XMM_SPACE 40
+#define XSAVE_XSTATE_BV 128
+#define XSAVE_YMMH_SPACE 144
+
+void kvm_arch_load_regs(CPUState *env, int level)
+{
+ int rc;
+
+ assert(kvm_cpu_is_stopped(env) || env->thread_id == kvm_get_thread_id());
+
+ kvm_getput_regs(env, 1);
+
+ kvm_put_xsave(env);
+ kvm_put_xcrs(env);
+
+ kvm_put_sregs(env);
+
+ rc = kvm_put_msrs(env, level);
+ if (rc < 0) {
+ perror("kvm__msrs FAILED");
+ }
+
+ if (level >= KVM_PUT_RESET_STATE) {
+ kvm_arch_load_mpstate(env);
+ kvm_load_lapic(env);
+ }
+ if (level == KVM_PUT_FULL_STATE) {
+ if (env->kvm_vcpu_update_vapic) {
+ kvm_tpr_enable_vapic(env);
+ }
+ }
+
+ kvm_put_vcpu_events(env, level);
+ kvm_put_debugregs(env);
+
+ /* must be last */
+ kvm_guest_debug_workarounds(env);
+}
+
+void kvm_arch_save_regs(CPUState *env)
+{
+ int rc;
+
+ assert(kvm_cpu_is_stopped(env) || env->thread_id == kvm_get_thread_id());
+
+ kvm_getput_regs(env, 0);
+
+ kvm_get_xsave(env);
+ kvm_get_xcrs(env);
+
+ kvm_get_sregs(env);
+
+ rc = kvm_get_msrs(env);
+ if (rc < 0) {
+ perror("kvm_get_msrs FAILED");
+ }
+
+ kvm_arch_save_mpstate(env);
+ kvm_save_lapic(env);
+ kvm_get_vcpu_events(env);
+ kvm_get_debugregs(env);
+}
+
+static int _kvm_arch_init_vcpu(CPUState *env)
+{
+ kvm_arch_reset_vcpu(env);
+
+#ifdef KVM_EXIT_TPR_ACCESS
+ kvm_enable_tpr_access_reporting(env);
+#endif
+ return 0;
+}
+
+int kvm_arch_halt(CPUState *env)
+{
+
+ if (!((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) &&
+ !(env->interrupt_request & CPU_INTERRUPT_NMI)) {
+ env->halted = 1;
+ }
+ return 1;
+}
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+{
+ if (!kvm_irqchip_in_kernel()) {
+ kvm_set_cr8(env, cpu_get_apic_tpr(env->apic_state));
+ }
+ return 0;
+}
+
+int kvm_arch_has_work(CPUState *env)
+{
+ if (((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) ||
+ (env->interrupt_request & CPU_INTERRUPT_NMI)) {
+ return 1;
+ }
+ return 0;
+}
+
+int kvm_arch_try_push_interrupts(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r, irq;
+
+ if (kvm_is_ready_for_interrupt_injection(env) &&
+ (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) {
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ irq = cpu_get_pic_interrupt(env);
+ if (irq >= 0) {
+ r = kvm_inject_irq(env, irq);
+ if (r < 0) {
+ printf("cpu %d fail inject %x\n", env->cpu_index, irq);
+ }
+ }
+ }
+
+ return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0;
+}
+
+#ifdef KVM_CAP_USER_NMI
+void kvm_arch_push_nmi(void *opaque)
+{
+ CPUState *env = cpu_single_env;
+ int r;
+
+ if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI))) {
+ return;
+ }
+
+ env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ r = kvm_inject_nmi(env);
+ if (r < 0) {
+ printf("cpu %d fail inject NMI\n", env->cpu_index);
+ }
+}
+#endif /* KVM_CAP_USER_NMI */
+
+static int kvm_reset_msrs(CPUState *env)
+{
+ struct {
+ struct kvm_msrs info;
+ struct kvm_msr_entry entries[100];
+ } msr_data;
+ int n;
+ struct kvm_msr_entry *msrs = msr_data.entries;
+ uint32_t index;
+ uint64_t data;
+
+ if (!kvm_msr_list) {
+ return -1;
+ }
+
+ for (n = 0; n < kvm_msr_list->nmsrs; n++) {
+ index = kvm_msr_list->indices[n];
+ switch (index) {
+ case MSR_PAT:
+ data = 0x0007040600070406ULL;
+ break;
+ default:
+ data = 0;
+ }
+ kvm_msr_entry_set(&msrs[n], kvm_msr_list->indices[n], data);
+ }
+
+ msr_data.info.nmsrs = n;
+
+ return kvm_vcpu_ioctl(env, KVM_SET_MSRS, &msr_data);
+}
+
+
+void kvm_arch_cpu_reset(CPUState *env)
+{
+ kvm_reset_msrs(env);
+ kvm_arch_reset_vcpu(env);
+}
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+void kvm_arch_do_ioperm(void *_data)
+{
+ struct ioperm_data *data = _data;
+ ioperm(data->start_port, data->num, data->turn_on);
+}
+#endif
+
+/*
+ * Setup x86 specific IRQ routing
+ */
+int kvm_arch_init_irq_routing(void)
+{
+ int i, r;
+
+ if (kvm_irqchip && kvm_has_gsi_routing()) {
+ kvm_clear_gsi_routes();
+ for (i = 0; i < 8; ++i) {
+ if (i == 2) {
+ continue;
+ }
+ r = kvm_add_irq_route(i, KVM_IRQCHIP_PIC_MASTER, i);
+ if (r < 0) {
+ return r;
+ }
+ }
+ for (i = 8; i < 16; ++i) {
+ r = kvm_add_irq_route(i, KVM_IRQCHIP_PIC_SLAVE, i - 8);
+ if (r < 0) {
+ return r;
+ }
+ }
+ for (i = 0; i < 24; ++i) {
+ if (i == 0 && irq0override) {
+ r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, 2);
+ } else if (i != 2 || !irq0override) {
+ r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, i);
+ }
+ if (r < 0) {
+ return r;
+ }
+ }
+ kvm_commit_irq_routes();
+ }
+ return 0;
+}
+
+void kvm_arch_process_irqchip_events(CPUState *env)
+{
+ if (env->interrupt_request & CPU_INTERRUPT_INIT) {
+ kvm_cpu_synchronize_state(env);
+ do_cpu_init(env);
+ }
+ if (env->interrupt_request & CPU_INTERRUPT_SIPI) {
+ kvm_cpu_synchronize_state(env);
+ do_cpu_sipi(env);
+ }
+}
diff --git a/qemu-kvm.c b/qemu-kvm.c
new file mode 100644
index 0000000..49cd683
--- /dev/null
+++ b/qemu-kvm.c
@@ -0,0 +1,1784 @@
+/*
+ * qemu/kvm integration
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+#include "config.h"
+#include "config-host.h"
+
+#include <assert.h>
+#include <string.h>
+#include "hw/hw.h"
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "console.h"
+#include "block.h"
+#include "compatfd.h"
+#include "gdbstub.h"
+#include "monitor.h"
+
+#include "qemu-kvm.h"
+#include "libkvm.h"
+
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include "compatfd.h"
+#include <sys/prctl.h>
+
+#define false 0
+#define true 1
+
+#ifndef PR_MCE_KILL
+#define PR_MCE_KILL 33
+#endif
+
+#ifndef BUS_MCEERR_AR
+#define BUS_MCEERR_AR 4
+#endif
+#ifndef BUS_MCEERR_AO
+#define BUS_MCEERR_AO 5
+#endif
+
+#define EXPECTED_KVM_API_VERSION 12
+
+#if EXPECTED_KVM_API_VERSION != KVM_API_VERSION
+#error libkvm: userspace and kernel version mismatch
+#endif
+
+int kvm_irqchip = 1;
+int kvm_pit = 1;
+int kvm_pit_reinject = 1;
+int kvm_nested = 0;
+
+
+KVMState *kvm_state;
+kvm_context_t kvm_context;
+
+pthread_mutex_t qemu_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t qemu_vcpu_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t qemu_system_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t qemu_pause_cond = PTHREAD_COND_INITIALIZER;
+pthread_cond_t qemu_work_cond = PTHREAD_COND_INITIALIZER;
+__thread CPUState *current_env;
+
+static int qemu_system_ready;
+
+#define SIG_IPI (SIGRTMIN+4)
+
+pthread_t io_thread;
+static int io_thread_sigfd = -1;
+
+static CPUState *kvm_debug_cpu_requested;
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+/* The list of ioperm_data */
+static QLIST_HEAD(, ioperm_data) ioperm_head;
+#endif
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+
+int kvm_abi = EXPECTED_KVM_API_VERSION;
+int kvm_page_size;
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+static int kvm_debug(CPUState *env,
+ struct kvm_debug_exit_arch *arch_info)
+{
+ int handle = kvm_arch_debug(arch_info);
+
+ if (handle) {
+ kvm_debug_cpu_requested = env;
+ env->stopped = 1;
+ }
+ return handle;
+}
+#endif
+
+static int handle_unhandled(uint64_t reason)
+{
+ fprintf(stderr, "kvm: unhandled exit %" PRIx64 "\n", reason);
+ return -EINVAL;
+}
+
+#define VMX_INVALID_GUEST_STATE 0x80000021
+
+static int handle_failed_vmentry(uint64_t reason)
+{
+ fprintf(stderr, "kvm: vm entry failed with error 0x%" PRIx64 "\n\n", reason);
+
+ /* Perhaps we will need to check if this machine is intel since exit reason 0x21
+ has a different interpretation on SVM */
+ if (reason == VMX_INVALID_GUEST_STATE) {
+ fprintf(stderr, "If you're runnning a guest on an Intel machine without\n");
+ fprintf(stderr, "unrestricted mode support, the failure can be most likely\n");
+ fprintf(stderr, "due to the guest entering an invalid state for Intel VT.\n");
+ fprintf(stderr, "For example, the guest maybe running in big real mode\n");
+ fprintf(stderr, "which is not supported on less recent Intel processors.\n\n");
+ }
+
+ return -EINVAL;
+}
+
+static inline void set_gsi(kvm_context_t kvm, unsigned int gsi)
+{
+ uint32_t *bitmap = kvm->used_gsi_bitmap;
+
+ if (gsi < kvm->max_gsi)
+ bitmap[gsi / 32] |= 1U << (gsi % 32);
+ else
+ DPRINTF("Invalid GSI %u\n", gsi);
+}
+
+static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)
+{
+ uint32_t *bitmap = kvm->used_gsi_bitmap;
+
+ if (gsi < kvm->max_gsi)
+ bitmap[gsi / 32] &= ~(1U << (gsi % 32));
+ else
+ DPRINTF("Invalid GSI %u\n", gsi);
+}
+
+static int kvm_create_context(void);
+
+int kvm_init(void)
+{
+ int fd;
+ int r, gsi_count;
+
+
+ fd = open("/dev/kvm", O_RDWR);
+ if (fd == -1) {
+ perror("open /dev/kvm");
+ return -1;
+ }
+ r = ioctl(fd, KVM_GET_API_VERSION, 0);
+ if (r == -1) {
+ fprintf(stderr,
+ "kvm kernel version too old: "
+ "KVM_GET_API_VERSION ioctl not supported\n");
+ goto out_close;
+ }
+ if (r < EXPECTED_KVM_API_VERSION) {
+ fprintf(stderr, "kvm kernel version too old: "
+ "We expect API version %d or newer, but got "
+ "version %d\n", EXPECTED_KVM_API_VERSION, r);
+ goto out_close;
+ }
+ if (r > EXPECTED_KVM_API_VERSION) {
+ fprintf(stderr, "kvm userspace version too old\n");
+ goto out_close;
+ }
+ kvm_abi = r;
+ kvm_page_size = getpagesize();
+ kvm_state = qemu_mallocz(sizeof(*kvm_state));
+ kvm_context = &kvm_state->kvm_context;
+
+ kvm_state->fd = fd;
+ kvm_state->vmfd = -1;
+ kvm_context->opaque = cpu_single_env;
+ kvm_context->dirty_pages_log_all = 0;
+ kvm_context->no_irqchip_creation = 0;
+ kvm_context->no_pit_creation = 0;
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ QTAILQ_INIT(&kvm_state->kvm_sw_breakpoints);
+#endif
+
+ gsi_count = kvm_get_gsi_count(kvm_context);
+ if (gsi_count > 0) {
+ int gsi_bits, i;
+
+ /* Round up so we can search ints using ffs */
+ gsi_bits = ALIGN(gsi_count, 32);
+ kvm_context->used_gsi_bitmap = qemu_mallocz(gsi_bits / 8);
+ kvm_context->max_gsi = gsi_bits;
+
+ /* Mark any over-allocated bits as already in use */
+ for (i = gsi_count; i < gsi_bits; i++) {
+ set_gsi(kvm_context, i);
+ }
+ }
+
+ kvm_cpu_register_phys_memory_client();
+
+ pthread_mutex_lock(&qemu_mutex);
+ return kvm_create_context();
+
+ out_close:
+ close(fd);
+ return -1;
+}
+
+static void kvm_finalize(KVMState *s)
+{
+ /* FIXME
+ if (kvm->vcpu_fd[0] != -1)
+ close(kvm->vcpu_fd[0]);
+ if (kvm->vm_fd != -1)
+ close(kvm->vm_fd);
+ */
+ close(s->fd);
+ free(s);
+}
+
+void kvm_disable_irqchip_creation(kvm_context_t kvm)
+{
+ kvm->no_irqchip_creation = 1;
+}
+
+void kvm_disable_pit_creation(kvm_context_t kvm)
+{
+ kvm->no_pit_creation = 1;
+}
+
+static void kvm_reset_vcpu(void *opaque)
+{
+ CPUState *env = opaque;
+
+ kvm_arch_cpu_reset(env);
+}
+
+static void kvm_create_vcpu(CPUState *env, int id)
+{
+ long mmap_size;
+ int r;
+ KVMState *s = kvm_state;
+
+ r = kvm_vm_ioctl(kvm_state, KVM_CREATE_VCPU, id);
+ if (r < 0) {
+ fprintf(stderr, "kvm_create_vcpu: %m\n");
+ fprintf(stderr, "Failed to create vCPU. Check the -smp parameter.\n");
+ goto err;
+ }
+
+ env->kvm_fd = r;
+ env->kvm_state = kvm_state;
+
+ mmap_size = kvm_ioctl(kvm_state, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (mmap_size < 0) {
+ fprintf(stderr, "get vcpu mmap size: %m\n");
+ goto err_fd;
+ }
+ env->kvm_run =
+ mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, env->kvm_fd,
+ 0);
+ if (env->kvm_run == MAP_FAILED) {
+ fprintf(stderr, "mmap vcpu area: %m\n");
+ goto err_fd;
+ }
+
+#ifdef KVM_CAP_COALESCED_MMIO
+ if (s->coalesced_mmio && !s->coalesced_mmio_ring)
+ s->coalesced_mmio_ring = (void *) env->kvm_run +
+ s->coalesced_mmio * PAGE_SIZE;
+#endif
+
+ r = kvm_arch_init_vcpu(env);
+ if (r == 0) {
+ qemu_register_reset(kvm_reset_vcpu, env);
+ }
+
+ return;
+ err_fd:
+ close(env->kvm_fd);
+ err:
+ /* We're no good with semi-broken states. */
+ abort();
+}
+
+static int kvm_set_boot_vcpu_id(kvm_context_t kvm, uint32_t id)
+{
+#ifdef KVM_CAP_SET_BOOT_CPU_ID
+ int r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_BOOT_CPU_ID);
+ if (r > 0) {
+ return kvm_vm_ioctl(kvm_state, KVM_SET_BOOT_CPU_ID, id);
+ }
+ return -ENOSYS;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_create_vm(kvm_context_t kvm)
+{
+ int fd;
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm->irq_routes = qemu_mallocz(sizeof(*kvm->irq_routes));
+ kvm->nr_allocated_irq_routes = 0;
+#endif
+
+ fd = kvm_ioctl(kvm_state, KVM_CREATE_VM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "kvm_create_vm: %m\n");
+ return -1;
+ }
+ kvm_state->vmfd = fd;
+ return 0;
+}
+
+static int kvm_create_default_phys_mem(kvm_context_t kvm,
+ unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+#ifdef KVM_CAP_USER_MEMORY
+ int r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
+ if (r > 0)
+ return 0;
+ fprintf(stderr,
+ "Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported\n");
+#else
+#error Hypervisor too old: KVM_CAP_USER_MEMORY extension not supported
+#endif
+ return -1;
+}
+
+void kvm_create_irqchip(kvm_context_t kvm)
+{
+ int r;
+
+ kvm->irqchip_in_kernel = 0;
+#ifdef KVM_CAP_IRQCHIP
+ if (!kvm->no_irqchip_creation) {
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_IRQCHIP);
+ if (r > 0) { /* kernel irqchip supported */
+ r = kvm_vm_ioctl(kvm_state, KVM_CREATE_IRQCHIP);
+ if (r >= 0) {
+ kvm->irqchip_inject_ioctl = KVM_IRQ_LINE;
+#if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS)
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION,
+ KVM_CAP_IRQ_INJECT_STATUS);
+ if (r > 0) {
+ kvm->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS;
+ }
+#endif
+ kvm->irqchip_in_kernel = 1;
+ } else
+ fprintf(stderr, "Create kernel PIC irqchip failed\n");
+ }
+ }
+#endif
+ kvm_state->irqchip_in_kernel = kvm->irqchip_in_kernel;
+}
+
+int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem)
+{
+ int r, i;
+
+ r = kvm_create_vm(kvm);
+ if (r < 0) {
+ return r;
+ }
+ r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0) {
+ return r;
+ }
+ for (i = 0; i < ARRAY_SIZE(kvm_state->slots); i++) {
+ kvm_state->slots[i].slot = i;
+ }
+
+ r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem);
+ if (r < 0) {
+ return r;
+ }
+
+ kvm_create_irqchip(kvm);
+
+ return 0;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status)
+{
+ struct kvm_irq_level event;
+ int r;
+
+ if (!kvm->irqchip_in_kernel) {
+ return 0;
+ }
+ event.level = level;
+ event.irq = irq;
+ r = kvm_vm_ioctl(kvm_state, kvm->irqchip_inject_ioctl, &event);
+ if (r < 0) {
+ perror("kvm_set_irq_level");
+ }
+
+ if (status) {
+#ifdef KVM_CAP_IRQ_INJECT_STATUS
+ *status =
+ (kvm->irqchip_inject_ioctl == KVM_IRQ_LINE) ? 1 : event.status;
+#else
+ *status = 1;
+#endif
+ }
+
+ return 1;
+}
+
+int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip)
+{
+ int r;
+
+ if (!kvm->irqchip_in_kernel) {
+ return 0;
+ }
+ r = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip);
+ if (r < 0) {
+ perror("kvm_get_irqchip\n");
+ }
+ return r;
+}
+
+int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip)
+{
+ int r;
+
+ if (!kvm->irqchip_in_kernel) {
+ return 0;
+ }
+ r = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip);
+ if (r < 0) {
+ perror("kvm_set_irqchip\n");
+ }
+ return r;
+}
+
+#endif
+
+static int handle_debug(CPUState *env)
+{
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ struct kvm_run *run = env->kvm_run;
+
+ return kvm_debug(env, &run->debug.arch);
+#else
+ return 0;
+#endif
+}
+
+int kvm_get_regs(CPUState *env, struct kvm_regs *regs)
+{
+ return kvm_vcpu_ioctl(env, KVM_GET_REGS, regs);
+}
+
+int kvm_set_regs(CPUState *env, struct kvm_regs *regs)
+{
+ return kvm_vcpu_ioctl(env, KVM_SET_REGS, regs);
+}
+
+#ifdef KVM_CAP_MP_STATE
+int kvm_get_mpstate(CPUState *env, struct kvm_mp_state *mp_state)
+{
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE);
+ if (r > 0) {
+ return kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, mp_state);
+ }
+ return -ENOSYS;
+}
+
+int kvm_set_mpstate(CPUState *env, struct kvm_mp_state *mp_state)
+{
+ int r;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE);
+ if (r > 0) {
+ return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, mp_state);
+ }
+ return -ENOSYS;
+}
+#endif
+
+static int handle_mmio(CPUState *env)
+{
+ unsigned long addr = env->kvm_run->mmio.phys_addr;
+ struct kvm_run *kvm_run = env->kvm_run;
+ void *data = kvm_run->mmio.data;
+
+ /* hack: Red Hat 7.1 generates these weird accesses. */
+ if ((addr > 0xa0000 - 4 && addr <= 0xa0000) && kvm_run->mmio.len == 3) {
+ return 0;
+ }
+
+ cpu_physical_memory_rw(addr, data, kvm_run->mmio.len, kvm_run->mmio.is_write);
+ return 0;
+}
+
+int handle_io_window(kvm_context_t kvm)
+{
+ return 1;
+}
+
+int handle_shutdown(kvm_context_t kvm, CPUState *env)
+{
+ /* stop the current vcpu from going back to guest mode */
+ env->stopped = 1;
+
+ qemu_system_reset_request();
+ return 1;
+}
+
+static inline void push_nmi(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_USER_NMI
+ kvm_arch_push_nmi(kvm->opaque);
+#endif /* KVM_CAP_USER_NMI */
+}
+
+void post_kvm_run(kvm_context_t kvm, CPUState *env)
+{
+ pthread_mutex_lock(&qemu_mutex);
+ kvm_arch_post_run(env, env->kvm_run);
+ cpu_single_env = env;
+}
+
+int pre_kvm_run(kvm_context_t kvm, CPUState *env)
+{
+ kvm_arch_pre_run(env, env->kvm_run);
+
+ pthread_mutex_unlock(&qemu_mutex);
+ return 0;
+}
+
+int kvm_is_ready_for_interrupt_injection(CPUState *env)
+{
+ return env->kvm_run->ready_for_interrupt_injection;
+}
+
+int kvm_run(CPUState *env)
+{
+ int r;
+ kvm_context_t kvm = &env->kvm_state->kvm_context;
+ struct kvm_run *run = env->kvm_run;
+ int fd = env->kvm_fd;
+
+ again:
+ if (env->kvm_vcpu_dirty) {
+ kvm_arch_load_regs(env, KVM_PUT_RUNTIME_STATE);
+ env->kvm_vcpu_dirty = 0;
+ }
+ push_nmi(kvm);
+#if !defined(__s390__)
+ if (!kvm->irqchip_in_kernel) {
+ run->request_interrupt_window = kvm_arch_try_push_interrupts(env);
+ }
+#endif
+
+ r = pre_kvm_run(kvm, env);
+ if (r) {
+ return r;
+ }
+ if (env->exit_request) {
+ env->exit_request = 0;
+ pthread_kill(env->kvm_cpu_state.thread, SIG_IPI);
+ }
+ r = ioctl(fd, KVM_RUN, 0);
+
+ if (r == -1 && errno != EINTR && errno != EAGAIN) {
+ r = -errno;
+ post_kvm_run(kvm, env);
+ fprintf(stderr, "kvm_run: %s\n", strerror(-r));
+ return r;
+ }
+
+ post_kvm_run(kvm, env);
+
+ kvm_flush_coalesced_mmio_buffer();
+
+#if !defined(__s390__)
+ if (r == -1) {
+ r = handle_io_window(kvm);
+ goto more;
+ }
+#endif
+ if (1) {
+ switch (run->exit_reason) {
+ case KVM_EXIT_UNKNOWN:
+ r = handle_unhandled(run->hw.hardware_exit_reason);
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ r = handle_failed_vmentry(run->fail_entry.hardware_entry_failure_reason);
+ break;
+ case KVM_EXIT_EXCEPTION:
+ fprintf(stderr, "exception %d (%x)\n", run->ex.exception,
+ run->ex.error_code);
+ kvm_show_regs(env);
+ kvm_show_code(env);
+ abort();
+ break;
+ case KVM_EXIT_IO:
+ r = kvm_handle_io(run->io.port,
+ (uint8_t *)run + run->io.data_offset,
+ run->io.direction,
+ run->io.size,
+ run->io.count);
+ r = 0;
+ break;
+ case KVM_EXIT_DEBUG:
+ r = handle_debug(env);
+ break;
+ case KVM_EXIT_MMIO:
+ r = handle_mmio(env);
+ break;
+ case KVM_EXIT_HLT:
+ r = kvm_arch_halt(env);
+ break;
+ case KVM_EXIT_IRQ_WINDOW_OPEN:
+ break;
+ case KVM_EXIT_SHUTDOWN:
+ r = handle_shutdown(kvm, env);
+ break;
+#if defined(__s390__)
+ case KVM_EXIT_S390_SIEIC:
+ r = kvm_s390_handle_intercept(kvm, env, run);
+ break;
+ case KVM_EXIT_S390_RESET:
+ r = kvm_s390_handle_reset(kvm, env, run);
+ break;
+#endif
+ case KVM_EXIT_INTERNAL_ERROR:
+ kvm_handle_internal_error(env, run);
+ r = 1;
+ break;
+ default:
+ if (kvm_arch_run(env)) {
+ fprintf(stderr, "unhandled vm exit: 0x%x\n", run->exit_reason);
+ kvm_show_regs(env);
+ abort();
+ }
+ break;
+ }
+ }
+more:
+ if (!r) {
+ goto again;
+ }
+ return r;
+}
+
+int kvm_inject_irq(CPUState *env, unsigned irq)
+{
+ struct kvm_interrupt intr;
+
+ intr.irq = irq;
+ return kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr);
+}
+
+int kvm_inject_nmi(CPUState *env)
+{
+#ifdef KVM_CAP_USER_NMI
+ return kvm_vcpu_ioctl(env, KVM_NMI);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_init_coalesced_mmio(kvm_context_t kvm)
+{
+ int r = 0;
+ kvm_state->coalesced_mmio = 0;
+#ifdef KVM_CAP_COALESCED_MMIO
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_COALESCED_MMIO);
+ if (r > 0) {
+ kvm_state->coalesced_mmio = r;
+ return 0;
+ }
+#endif
+ return r;
+}
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+int kvm_assign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_PCI_DEVICE, assigned_dev);
+}
+
+static int kvm_old_assign_irq(kvm_context_t kvm,
+ struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_IRQ, assigned_irq);
+}
+
+#ifdef KVM_CAP_ASSIGN_DEV_IRQ
+int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq)
+{
+ int ret;
+
+ ret = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_ASSIGN_DEV_IRQ);
+ if (ret > 0) {
+ return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_DEV_IRQ, assigned_irq);
+ }
+
+ return kvm_old_assign_irq(kvm, assigned_irq);
+}
+
+int kvm_deassign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_vm_ioctl(kvm_state, KVM_DEASSIGN_DEV_IRQ, assigned_irq);
+}
+#else
+int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq)
+{
+ return kvm_old_assign_irq(kvm, assigned_irq);
+}
+#endif
+#endif
+
+#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
+int kvm_deassign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev)
+{
+ return kvm_vm_ioctl(kvm_state, KVM_DEASSIGN_PCI_DEVICE, assigned_dev);
+}
+#endif
+
+int kvm_reinject_control(kvm_context_t kvm, int pit_reinject)
+{
+#ifdef KVM_CAP_REINJECT_CONTROL
+ int r;
+ struct kvm_reinject_control control;
+
+ control.pit_reinject = pit_reinject;
+
+ r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_REINJECT_CONTROL);
+ if (r > 0) {
+ return kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control);
+ }
+#endif
+ return -ENOSYS;
+}
+
+int kvm_has_gsi_routing(void)
+{
+ int r = 0;
+
+#ifdef KVM_CAP_IRQ_ROUTING
+ r = kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING);
+#endif
+ return r;
+}
+
+int kvm_get_gsi_count(kvm_context_t kvm)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ return kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING);
+#else
+ return -EINVAL;
+#endif
+}
+
+int kvm_clear_gsi_routes(void)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm_context_t kvm = kvm_context;
+
+ kvm->irq_routes->nr = 0;
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
+int kvm_add_routing_entry(struct kvm_irq_routing_entry *entry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm_context_t kvm = kvm_context;
+ struct kvm_irq_routing *z;
+ struct kvm_irq_routing_entry *new;
+ int n, size;
+
+ if (kvm->irq_routes->nr == kvm->nr_allocated_irq_routes) {
+ n = kvm->nr_allocated_irq_routes * 2;
+ if (n < 64) {
+ n = 64;
+ }
+ size = sizeof(struct kvm_irq_routing);
+ size += n * sizeof(*new);
+ z = realloc(kvm->irq_routes, size);
+ if (!z) {
+ return -ENOMEM;
+ }
+ kvm->nr_allocated_irq_routes = n;
+ kvm->irq_routes = z;
+ }
+ n = kvm->irq_routes->nr++;
+ new = &kvm->irq_routes->entries[n];
+ memset(new, 0, sizeof(*new));
+ new->gsi = entry->gsi;
+ new->type = entry->type;
+ new->flags = entry->flags;
+ new->u = entry->u;
+
+ set_gsi(kvm, entry->gsi);
+
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_add_irq_route(int gsi, int irqchip, int pin)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e;
+
+ e.gsi = gsi;
+ e.type = KVM_IRQ_ROUTING_IRQCHIP;
+ e.flags = 0;
+ e.u.irqchip.irqchip = irqchip;
+ e.u.irqchip.pin = pin;
+ return kvm_add_routing_entry(&e);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm_context_t kvm = kvm_context;
+ struct kvm_irq_routing_entry *e, *p;
+ int i, gsi, found = 0;
+
+ gsi = entry->gsi;
+
+ for (i = 0; i < kvm->irq_routes->nr; ++i) {
+ e = &kvm->irq_routes->entries[i];
+ if (e->type == entry->type && e->gsi == gsi) {
+ switch (e->type) {
+ case KVM_IRQ_ROUTING_IRQCHIP:{
+ if (e->u.irqchip.irqchip ==
+ entry->u.irqchip.irqchip
+ && e->u.irqchip.pin == entry->u.irqchip.pin) {
+ p = &kvm->irq_routes->entries[--kvm->irq_routes->nr];
+ *e = *p;
+ found = 1;
+ }
+ break;
+ }
+ case KVM_IRQ_ROUTING_MSI:{
+ if (e->u.msi.address_lo ==
+ entry->u.msi.address_lo
+ && e->u.msi.address_hi ==
+ entry->u.msi.address_hi
+ && e->u.msi.data == entry->u.msi.data) {
+ p = &kvm->irq_routes->entries[--kvm->irq_routes->nr];
+ *e = *p;
+ found = 1;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (found) {
+ /* If there are no other users of this GSI
+ * mark it available in the bitmap */
+ for (i = 0; i < kvm->irq_routes->nr; i++) {
+ e = &kvm->irq_routes->entries[i];
+ if (e->gsi == gsi)
+ break;
+ }
+ if (i == kvm->irq_routes->nr) {
+ clear_gsi(kvm, gsi);
+ }
+
+ return 0;
+ }
+ }
+ }
+ return -ESRCH;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry,
+ struct kvm_irq_routing_entry *newentry)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm_context_t kvm = kvm_context;
+ struct kvm_irq_routing_entry *e;
+ int i;
+
+ if (entry->gsi != newentry->gsi || entry->type != newentry->type) {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < kvm->irq_routes->nr; ++i) {
+ e = &kvm->irq_routes->entries[i];
+ if (e->type != entry->type || e->gsi != entry->gsi) {
+ continue;
+ }
+ switch (e->type) {
+ case KVM_IRQ_ROUTING_IRQCHIP:
+ if (e->u.irqchip.irqchip == entry->u.irqchip.irqchip &&
+ e->u.irqchip.pin == entry->u.irqchip.pin) {
+ memcpy(&e->u.irqchip, &newentry->u.irqchip,
+ sizeof e->u.irqchip);
+ return 0;
+ }
+ break;
+ case KVM_IRQ_ROUTING_MSI:
+ if (e->u.msi.address_lo == entry->u.msi.address_lo &&
+ e->u.msi.address_hi == entry->u.msi.address_hi &&
+ e->u.msi.data == entry->u.msi.data) {
+ memcpy(&e->u.msi, &newentry->u.msi, sizeof e->u.msi);
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return -ESRCH;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_del_irq_route(int gsi, int irqchip, int pin)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing_entry e;
+
+ e.gsi = gsi;
+ e.type = KVM_IRQ_ROUTING_IRQCHIP;
+ e.flags = 0;
+ e.u.irqchip.irqchip = irqchip;
+ e.u.irqchip.pin = pin;
+ return kvm_del_routing_entry(&e);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_commit_irq_routes(void)
+{
+#ifdef KVM_CAP_IRQ_ROUTING
+ kvm_context_t kvm = kvm_context;
+
+ kvm->irq_routes->flags = 0;
+ return kvm_vm_ioctl(kvm_state, KVM_SET_GSI_ROUTING, kvm->irq_routes);
+#else
+ return -ENOSYS;
+#endif
+}
+
+int kvm_get_irq_route_gsi(void)
+{
+ kvm_context_t kvm = kvm_context;
+ int i, bit;
+ uint32_t *buf = kvm->used_gsi_bitmap;
+
+ /* Return the lowest unused GSI in the bitmap */
+ for (i = 0; i < kvm->max_gsi / 32; i++) {
+ bit = ffs(~buf[i]);
+ if (!bit) {
+ continue;
+ }
+
+ return bit - 1 + i * 32;
+ }
+
+ return -ENOSPC;
+}
+
+static void kvm_msix_routing_entry(struct kvm_irq_routing_entry *e,
+ uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data)
+
+{
+ e->gsi = gsi;
+ e->type = KVM_IRQ_ROUTING_MSI;
+ e->flags = 0;
+ e->u.msi.address_lo = addr_lo;
+ e->u.msi.address_hi = addr_hi;
+ e->u.msi.data = data;
+}
+
+int kvm_add_msix(uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data)
+{
+ struct kvm_irq_routing_entry e;
+
+ kvm_msix_routing_entry(&e, gsi, addr_lo, addr_hi, data);
+ return kvm_add_routing_entry(&e);
+}
+
+int kvm_del_msix(uint32_t gsi, uint32_t addr_lo,
+ uint32_t addr_hi, uint32_t data)
+{
+ struct kvm_irq_routing_entry e;
+
+ kvm_msix_routing_entry(&e, gsi, addr_lo, addr_hi, data);
+ return kvm_del_routing_entry(&e);
+}
+
+int kvm_update_msix(uint32_t old_gsi, uint32_t old_addr_lo,
+ uint32_t old_addr_hi, uint32_t old_data,
+ uint32_t new_gsi, uint32_t new_addr_lo,
+ uint32_t new_addr_hi, uint32_t new_data)
+{
+ struct kvm_irq_routing_entry e1, e2;
+
+ kvm_msix_routing_entry(&e1, old_gsi, old_addr_lo, old_addr_hi, old_data);
+ kvm_msix_routing_entry(&e2, new_gsi, new_addr_lo, new_addr_hi, new_data);
+ return kvm_update_routing_entry(&e1, &e2);
+}
+
+
+#ifdef KVM_CAP_DEVICE_MSIX
+int kvm_assign_set_msix_nr(kvm_context_t kvm,
+ struct kvm_assigned_msix_nr *msix_nr)
+{
+ return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_NR, msix_nr);
+}
+
+int kvm_assign_set_msix_entry(kvm_context_t kvm,
+ struct kvm_assigned_msix_entry *entry)
+{
+ return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_ENTRY, entry);
+}
+#endif
+
+#if defined(KVM_CAP_IRQFD) && defined(CONFIG_EVENTFD)
+
+#include <sys/eventfd.h>
+
+static int _kvm_irqfd(kvm_context_t kvm, int fd, int gsi, int flags)
+{
+ struct kvm_irqfd data = {
+ .fd = fd,
+ .gsi = gsi,
+ .flags = flags,
+ };
+
+ return kvm_vm_ioctl(kvm_state, KVM_IRQFD, &data);
+}
+
+int kvm_irqfd(kvm_context_t kvm, int gsi, int flags)
+{
+ int r;
+ int fd;
+
+ if (!kvm_check_extension(kvm_state, KVM_CAP_IRQFD))
+ return -ENOENT;
+
+ fd = eventfd(0, 0);
+ if (fd < 0) {
+ return -errno;
+ }
+
+ r = _kvm_irqfd(kvm, fd, gsi, 0);
+ if (r < 0) {
+ close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
+#else /* KVM_CAP_IRQFD */
+
+int kvm_irqfd(kvm_context_t kvm, int gsi, int flags)
+{
+ return -ENOSYS;
+}
+
+#endif /* KVM_CAP_IRQFD */
+unsigned long kvm_get_thread_id(void)
+{
+ return syscall(SYS_gettid);
+}
+
+static void qemu_cond_wait(pthread_cond_t *cond)
+{
+ CPUState *env = cpu_single_env;
+
+ pthread_cond_wait(cond, &qemu_mutex);
+ cpu_single_env = env;
+}
+
+static void sig_ipi_handler(int n)
+{
+}
+
+static void sigbus_reraise(void)
+{
+ sigset_t set;
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = SIG_DFL;
+ if (!sigaction(SIGBUS, &action, NULL)) {
+ raise(SIGBUS);
+ sigemptyset(&set);
+ sigaddset(&set, SIGBUS);
+ sigprocmask(SIG_UNBLOCK, &set, NULL);
+ }
+ perror("Failed to re-raise SIGBUS!\n");
+ abort();
+}
+
+static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo,
+ void *ctx)
+{
+ if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr))
+ sigbus_reraise();
+}
+
+void on_vcpu(CPUState *env, void (*func)(void *data), void *data)
+{
+ struct qemu_work_item wi;
+
+ if (env == current_env) {
+ func(data);
+ return;
+ }
+
+ wi.func = func;
+ wi.data = data;
+ if (!env->kvm_cpu_state.queued_work_first) {
+ env->kvm_cpu_state.queued_work_first = &wi;
+ } else {
+ env->kvm_cpu_state.queued_work_last->next = &wi;
+ }
+ env->kvm_cpu_state.queued_work_last = &wi;
+ wi.next = NULL;
+ wi.done = false;
+
+ pthread_kill(env->kvm_cpu_state.thread, SIG_IPI);
+ while (!wi.done) {
+ qemu_cond_wait(&qemu_work_cond);
+ }
+}
+
+static void do_kvm_cpu_synchronize_state(void *_env)
+{
+ CPUState *env = _env;
+
+ if (!env->kvm_vcpu_dirty) {
+ kvm_arch_save_regs(env);
+ env->kvm_vcpu_dirty = 1;
+ }
+}
+
+void kvm_cpu_synchronize_state(CPUState *env)
+{
+ if (!env->kvm_vcpu_dirty) {
+ on_vcpu(env, do_kvm_cpu_synchronize_state, env);
+ }
+}
+
+void kvm_cpu_synchronize_post_reset(CPUState *env)
+{
+ kvm_arch_load_regs(env, KVM_PUT_RESET_STATE);
+ env->kvm_vcpu_dirty = 0;
+}
+
+void kvm_cpu_synchronize_post_init(CPUState *env)
+{
+ kvm_arch_load_regs(env, KVM_PUT_FULL_STATE);
+ env->kvm_vcpu_dirty = 0;
+}
+
+static void inject_interrupt(void *data)
+{
+ cpu_interrupt(current_env, (long) data);
+}
+
+void kvm_inject_interrupt(CPUState *env, int mask)
+{
+ on_vcpu(env, inject_interrupt, (void *) (long) mask);
+}
+
+void kvm_update_interrupt_request(CPUState *env)
+{
+ int signal = 0;
+
+ if (env) {
+ if (!current_env || !current_env->created) {
+ signal = 1;
+ }
+ /*
+ * Testing for created here is really redundant
+ */
+ if (current_env && current_env->created &&
+ env != current_env && !env->kvm_cpu_state.signalled) {
+ signal = 1;
+ }
+
+ if (signal) {
+ env->kvm_cpu_state.signalled = 1;
+ if (env->kvm_cpu_state.thread) {
+ pthread_kill(env->kvm_cpu_state.thread, SIG_IPI);
+ }
+ }
+ }
+}
+
+int kvm_cpu_exec(CPUState *env)
+{
+ int r;
+
+ r = kvm_run(env);
+ if (r < 0) {
+ printf("kvm_run returned %d\n", r);
+ vm_stop(0);
+ }
+
+ return 0;
+}
+
+int kvm_cpu_is_stopped(CPUState *env)
+{
+ return !vm_running || env->stopped;
+}
+
+static void flush_queued_work(CPUState *env)
+{
+ struct qemu_work_item *wi;
+
+ if (!env->kvm_cpu_state.queued_work_first) {
+ return;
+ }
+
+ while ((wi = env->kvm_cpu_state.queued_work_first)) {
+ env->kvm_cpu_state.queued_work_first = wi->next;
+ wi->func(wi->data);
+ wi->done = true;
+ }
+ env->kvm_cpu_state.queued_work_last = NULL;
+ pthread_cond_broadcast(&qemu_work_cond);
+}
+
+static void kvm_main_loop_wait(CPUState *env, int timeout)
+{
+ struct timespec ts;
+ int r, e;
+ siginfo_t siginfo;
+ sigset_t waitset;
+ sigset_t chkset;
+
+ ts.tv_sec = timeout / 1000;
+ ts.tv_nsec = (timeout % 1000) * 1000000;
+ sigemptyset(&waitset);
+ sigaddset(&waitset, SIG_IPI);
+ sigaddset(&waitset, SIGBUS);
+
+ do {
+ pthread_mutex_unlock(&qemu_mutex);
+
+ r = sigtimedwait(&waitset, &siginfo, &ts);
+ e = errno;
+
+ pthread_mutex_lock(&qemu_mutex);
+
+ if (r == -1 && !(e == EAGAIN || e == EINTR)) {
+ printf("sigtimedwait: %s\n", strerror(e));
+ exit(1);
+ }
+
+ switch (r) {
+ case SIGBUS:
+ if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr))
+ sigbus_reraise();
+ break;
+ default:
+ break;
+ }
+
+ r = sigpending(&chkset);
+ if (r == -1) {
+ printf("sigpending: %s\n", strerror(e));
+ exit(1);
+ }
+ } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS));
+
+ cpu_single_env = env;
+ flush_queued_work(env);
+
+ if (env->stop) {
+ env->stop = 0;
+ env->stopped = 1;
+ pthread_cond_signal(&qemu_pause_cond);
+ }
+
+ env->kvm_cpu_state.signalled = 0;
+}
+
+static int all_threads_paused(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ if (penv->stop) {
+ return 0;
+ }
+ penv = (CPUState *) penv->next_cpu;
+ }
+
+ return 1;
+}
+
+static void pause_all_threads(void)
+{
+ CPUState *penv = first_cpu;
+
+ while (penv) {
+ if (penv != cpu_single_env) {
+ penv->stop = 1;
+ pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI);
+ } else {
+ penv->stop = 0;
+ penv->stopped = 1;
+ cpu_exit(penv);
+ }
+ penv = (CPUState *) penv->next_cpu;
+ }
+
+ while (!all_threads_paused()) {
+ qemu_cond_wait(&qemu_pause_cond);
+ }
+}
+
+static void resume_all_threads(void)
+{
+ CPUState *penv = first_cpu;
+
+ assert(!cpu_single_env);
+
+ while (penv) {
+ penv->stop = 0;
+ penv->stopped = 0;
+ pthread_kill(penv->kvm_cpu_state.thread, SIG_IPI);
+ penv = (CPUState *) penv->next_cpu;
+ }
+}
+
+static void kvm_vm_state_change_handler(void *context, int running, int reason)
+{
+ if (running) {
+ resume_all_threads();
+ } else {
+ pause_all_threads();
+ }
+}
+
+static void setup_kernel_sigmask(CPUState *env)
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR2);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ sigprocmask(SIG_BLOCK, NULL, &set);
+ sigdelset(&set, SIG_IPI);
+ sigdelset(&set, SIGBUS);
+
+ kvm_set_signal_mask(env, &set);
+}
+
+static void qemu_kvm_system_reset(void)
+{
+ pause_all_threads();
+
+ qemu_system_reset();
+
+ resume_all_threads();
+}
+
+static void process_irqchip_events(CPUState *env)
+{
+ kvm_arch_process_irqchip_events(env);
+ if (kvm_arch_has_work(env))
+ env->halted = 0;
+}
+
+static int kvm_main_loop_cpu(CPUState *env)
+{
+ while (1) {
+ int run_cpu = !kvm_cpu_is_stopped(env);
+ if (run_cpu && !kvm_irqchip_in_kernel()) {
+ process_irqchip_events(env);
+ run_cpu = !env->halted;
+ }
+ if (run_cpu) {
+ kvm_cpu_exec(env);
+ kvm_main_loop_wait(env, 0);
+ } else {
+ kvm_main_loop_wait(env, 1000);
+ }
+ }
+ pthread_mutex_unlock(&qemu_mutex);
+ return 0;
+}
+
+static void *ap_main_loop(void *_env)
+{
+ CPUState *env = _env;
+ sigset_t signals;
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+ struct ioperm_data *data = NULL;
+#endif
+
+ current_env = env;
+ env->thread_id = kvm_get_thread_id();
+ sigfillset(&signals);
+ sigprocmask(SIG_BLOCK, &signals, NULL);
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+ /* do ioperm for io ports of assigned devices */
+ QLIST_FOREACH(data, &ioperm_head, entries)
+ on_vcpu(env, kvm_arch_do_ioperm, data);
+#endif
+
+ pthread_mutex_lock(&qemu_mutex);
+ cpu_single_env = env;
+
+ kvm_create_vcpu(env, env->cpu_index);
+ setup_kernel_sigmask(env);
+
+ /* signal VCPU creation */
+ current_env->created = 1;
+ pthread_cond_signal(&qemu_vcpu_cond);
+
+ /* and wait for machine initialization */
+ while (!qemu_system_ready) {
+ qemu_cond_wait(&qemu_system_cond);
+ }
+
+ /* re-initialize cpu_single_env after re-acquiring qemu_mutex */
+ cpu_single_env = env;
+
+ kvm_main_loop_cpu(env);
+ return NULL;
+}
+
+int kvm_init_vcpu(CPUState *env)
+{
+ pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env);
+
+ while (env->created == 0) {
+ qemu_cond_wait(&qemu_vcpu_cond);
+ }
+
+ return 0;
+}
+
+int kvm_vcpu_inited(CPUState *env)
+{
+ return env->created;
+}
+
+#ifdef TARGET_I386
+void kvm_hpet_disable_kpit(void)
+{
+ struct kvm_pit_state2 ps2;
+
+ kvm_get_pit2(kvm_context, &ps2);
+ ps2.flags |= KVM_PIT_FLAGS_HPET_LEGACY;
+ kvm_set_pit2(kvm_context, &ps2);
+}
+
+void kvm_hpet_enable_kpit(void)
+{
+ struct kvm_pit_state2 ps2;
+
+ kvm_get_pit2(kvm_context, &ps2);
+ ps2.flags &= ~KVM_PIT_FLAGS_HPET_LEGACY;
+ kvm_set_pit2(kvm_context, &ps2);
+}
+#endif
+
+int kvm_init_ap(void)
+{
+ struct sigaction action;
+
+ qemu_add_vm_change_state_handler(kvm_vm_state_change_handler, NULL);
+
+ signal(SIG_IPI, sig_ipi_handler);
+
+ memset(&action, 0, sizeof(action));
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler;
+ sigaction(SIGBUS, &action, NULL);
+ prctl(PR_MCE_KILL, 1, 1, 0, 0);
+ return 0;
+}
+
+/* If we have signalfd, we mask out the signals we want to handle and then
+ * use signalfd to listen for them. We rely on whatever the current signal
+ * handler is to dispatch the signals when we receive them.
+ */
+
+static void sigfd_handler(void *opaque)
+{
+ int fd = (unsigned long) opaque;
+ struct qemu_signalfd_siginfo info;
+ struct sigaction action;
+ ssize_t len;
+
+ while (1) {
+ do {
+ len = read(fd, &info, sizeof(info));
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN) {
+ break;
+ }
+
+ if (len != sizeof(info)) {
+ printf("read from sigfd returned %zd: %m\n", len);
+ return;
+ }
+
+ sigaction(info.ssi_signo, NULL, &action);
+ if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) {
+ action.sa_sigaction(info.ssi_signo,
+ (siginfo_t *)&info, NULL);
+ } else if (action.sa_handler) {
+ action.sa_handler(info.ssi_signo);
+ }
+ }
+}
+
+int kvm_main_loop(void)
+{
+ sigset_t mask;
+ int sigfd;
+
+ io_thread = pthread_self();
+ qemu_system_ready = 1;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGIO);
+ sigaddset(&mask, SIGALRM);
+ sigaddset(&mask, SIGBUS);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ sigfd = qemu_signalfd(&mask);
+ if (sigfd == -1) {
+ fprintf(stderr, "failed to create signalfd\n");
+ return -errno;
+ }
+
+ fcntl(sigfd, F_SETFL, O_NONBLOCK);
+
+ qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL,
+ (void *)(unsigned long) sigfd);
+
+ pthread_cond_broadcast(&qemu_system_cond);
+
+ io_thread_sigfd = sigfd;
+ cpu_single_env = NULL;
+
+ while (1) {
+ main_loop_wait(0);
+ if (qemu_shutdown_requested()) {
+ monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
+ if (qemu_no_shutdown()) {
+ vm_stop(0);
+ } else {
+ break;
+ }
+ } else if (qemu_powerdown_requested()) {
+ monitor_protocol_event(QEVENT_POWERDOWN, NULL);
+ qemu_irq_raise(qemu_system_powerdown);
+ } else if (qemu_reset_requested()) {
+ qemu_kvm_system_reset();
+ } else if (kvm_debug_cpu_requested) {
+ gdb_set_stop_cpu(kvm_debug_cpu_requested);
+ vm_stop(EXCP_DEBUG);
+ kvm_debug_cpu_requested = NULL;
+ }
+ }
+
+ bdrv_close_all();
+ pause_all_threads();
+ pthread_mutex_unlock(&qemu_mutex);
+
+ return 0;
+}
+
+#if !defined(TARGET_I386)
+int kvm_arch_init_irq_routing(void)
+{
+ return 0;
+}
+#endif
+
+extern int no_hpet;
+
+static int kvm_create_context(void)
+{
+ static const char upgrade_note[] =
+ "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n"
+ "(see http://sourceforge.net/projects/kvm).\n";
+
+ int r;
+
+ if (!kvm_irqchip) {
+ kvm_disable_irqchip_creation(kvm_context);
+ }
+ if (!kvm_pit) {
+ kvm_disable_pit_creation(kvm_context);
+ }
+ if (kvm_create(kvm_context, 0, NULL) < 0) {
+ kvm_finalize(kvm_state);
+ return -1;
+ }
+ r = kvm_arch_qemu_create_context();
+ if (r < 0) {
+ kvm_finalize(kvm_state);
+ return -1;
+ }
+ if (kvm_pit && !kvm_pit_reinject) {
+ if (kvm_reinject_control(kvm_context, 0)) {
+ fprintf(stderr, "failure to disable in-kernel PIT reinjection\n");
+ return -1;
+ }
+ }
+
+ /* There was a nasty bug in < kvm-80 that prevents memory slots from being
+ * destroyed properly. Since we rely on this capability, refuse to work
+ * with any kernel without this capability. */
+ if (!kvm_check_extension(kvm_state, KVM_CAP_DESTROY_MEMORY_REGION_WORKS)) {
+ fprintf(stderr,
+ "KVM kernel module broken (DESTROY_MEMORY_REGION).\n%s",
+ upgrade_note);
+ return -EINVAL;
+ }
+
+ r = kvm_arch_init_irq_routing();
+ if (r < 0) {
+ return r;
+ }
+
+ kvm_state->vcpu_events = 0;
+#ifdef KVM_CAP_VCPU_EVENTS
+ kvm_state->vcpu_events = kvm_check_extension(kvm_state, KVM_CAP_VCPU_EVENTS);
+#endif
+
+ kvm_state->debugregs = 0;
+#ifdef KVM_CAP_DEBUGREGS
+ kvm_state->debugregs = kvm_check_extension(kvm_state, KVM_CAP_DEBUGREGS);
+#endif
+
+ kvm_state->xsave = 0;
+#ifdef KVM_CAP_XSAVE
+ kvm_state->xsave = kvm_check_extension(kvm_state, KVM_CAP_XSAVE);
+#endif
+
+ kvm_state->xcrs = 0;
+#ifdef KVM_CAP_XCRS
+ kvm_state->xcrs = kvm_check_extension(kvm_state, KVM_CAP_XCRS);
+#endif
+
+ kvm_state->many_ioeventfds = kvm_check_many_ioeventfds();
+
+ kvm_init_ap();
+ if (kvm_irqchip) {
+ if (!qemu_kvm_has_gsi_routing()) {
+ irq0override = 0;
+#ifdef TARGET_I386
+ /* if kernel can't do irq routing, interrupt source
+ * override 0->2 can not be set up as required by hpet,
+ * so disable hpet.
+ */
+ no_hpet = 1;
+ } else if (!qemu_kvm_has_pit_state2()) {
+ no_hpet = 1;
+ }
+#else
+ }
+#endif
+ }
+
+ return 0;
+}
+
+#ifdef KVM_CAP_IRQCHIP
+
+int kvm_set_irq(int irq, int level, int *status)
+{
+ return kvm_set_irq_level(kvm_context, irq, level, status);
+}
+
+#endif
+
+static void kvm_mutex_unlock(void)
+{
+ assert(!cpu_single_env);
+ pthread_mutex_unlock(&qemu_mutex);
+}
+
+static void kvm_mutex_lock(void)
+{
+ pthread_mutex_lock(&qemu_mutex);
+ cpu_single_env = NULL;
+}
+
+void qemu_mutex_unlock_iothread(void)
+{
+ if (kvm_enabled()) {
+ kvm_mutex_unlock();
+ }
+}
+
+void qemu_mutex_lock_iothread(void)
+{
+ if (kvm_enabled()) {
+ kvm_mutex_lock();
+ }
+}
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+void kvm_add_ioperm_data(struct ioperm_data *data)
+{
+ QLIST_INSERT_HEAD(&ioperm_head, data, entries);
+}
+
+void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num)
+{
+ struct ioperm_data *data;
+
+ data = QLIST_FIRST(&ioperm_head);
+ while (data) {
+ struct ioperm_data *next = QLIST_NEXT(data, entries);
+
+ if (data->start_port == start_port && data->num == num) {
+ QLIST_REMOVE(data, entries);
+ qemu_free(data);
+ }
+
+ data = next;
+ }
+}
+
+void kvm_ioperm(CPUState *env, void *data)
+{
+ if (kvm_enabled() && qemu_system_ready) {
+ on_vcpu(env, kvm_arch_do_ioperm, data);
+ }
+}
+
+#endif
+
+int kvm_set_boot_cpu_id(uint32_t id)
+{
+ return kvm_set_boot_vcpu_id(kvm_context, id);
+}
+
diff --git a/qemu-kvm.h b/qemu-kvm.h
new file mode 100644
index 0000000..88cf276
--- /dev/null
+++ b/qemu-kvm.h
@@ -0,0 +1,771 @@
+/*
+ * qemu/kvm integration
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ *
+ * Licensed under the terms of the GNU GPL version 2 or higher.
+ */
+#ifndef THE_ORIGINAL_AND_TRUE_QEMU_KVM_H
+#define THE_ORIGINAL_AND_TRUE_QEMU_KVM_H
+
+#include "cpu.h"
+
+#include <signal.h>
+#include <stdlib.h>
+
+#ifdef CONFIG_KVM
+
+#if defined(__s390__)
+#include <asm/ptrace.h>
+#endif
+
+#include <stdint.h>
+
+#ifndef __user
+#define __user /* temporary, until installed via make headers_install */
+#endif
+
+#include <linux/kvm.h>
+
+#include <signal.h>
+
+/* FIXME: share this number with kvm */
+/* FIXME: or dynamically alloc/realloc regions */
+#ifdef __s390__
+#define KVM_MAX_NUM_MEM_REGIONS 1u
+#define MAX_VCPUS 64
+#define LIBKVM_S390_ORIGIN (0UL)
+#elif defined(__ia64__)
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 256
+#else
+#define KVM_MAX_NUM_MEM_REGIONS 32u
+#define MAX_VCPUS 16
+#endif
+
+/* kvm abi verison variable */
+extern int kvm_abi;
+
+/**
+ * \brief The KVM context
+ *
+ * The verbose KVM context
+ */
+
+struct kvm_context {
+ void *opaque;
+ /// is dirty pages logging enabled for all regions or not
+ int dirty_pages_log_all;
+ /// do not create in-kernel irqchip if set
+ int no_irqchip_creation;
+ /// in-kernel irqchip status
+ int irqchip_in_kernel;
+ /// ioctl to use to inject interrupts
+ int irqchip_inject_ioctl;
+ /// do not create in-kernel pit if set
+ int no_pit_creation;
+#ifdef KVM_CAP_IRQ_ROUTING
+ struct kvm_irq_routing *irq_routes;
+ int nr_allocated_irq_routes;
+#endif
+ void *used_gsi_bitmap;
+ int max_gsi;
+};
+
+typedef struct kvm_context *kvm_context_t;
+
+#include "kvm.h"
+int kvm_alloc_kernel_memory(kvm_context_t kvm, unsigned long memory,
+ void **vm_mem);
+int kvm_alloc_userspace_memory(kvm_context_t kvm, unsigned long memory,
+ void **vm_mem);
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem);
+
+int kvm_arch_run(CPUState *env);
+
+
+void kvm_show_code(CPUState *env);
+
+int handle_halt(CPUState *env);
+
+int handle_shutdown(kvm_context_t kvm, CPUState *env);
+void post_kvm_run(kvm_context_t kvm, CPUState *env);
+int pre_kvm_run(kvm_context_t kvm, CPUState *env);
+int handle_io_window(kvm_context_t kvm);
+int try_push_interrupts(kvm_context_t kvm);
+
+#if defined(__x86_64__) || defined(__i386__)
+struct kvm_x86_mce;
+#endif
+
+/*!
+ * \brief Disable the in-kernel IRQCHIP creation
+ *
+ * In-kernel irqchip is enabled by default. If userspace irqchip is to be used,
+ * this should be called prior to kvm_create().
+ *
+ * \param kvm Pointer to the kvm_context
+ */
+void kvm_disable_irqchip_creation(kvm_context_t kvm);
+
+/*!
+ * \brief Disable the in-kernel PIT creation
+ *
+ * In-kernel pit is enabled by default. If userspace pit is to be used,
+ * this should be called prior to kvm_create().
+ *
+ * \param kvm Pointer to the kvm_context
+ */
+void kvm_disable_pit_creation(kvm_context_t kvm);
+
+/*!
+ * \brief Create new virtual machine
+ *
+ * This creates a new virtual machine, maps physical RAM to it, and creates a
+ * virtual CPU for it.\n
+ * \n
+ * Memory gets mapped for addresses 0->0xA0000, 0xC0000->phys_mem_bytes
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param phys_mem_bytes The amount of physical ram you want the VM to have
+ * \param phys_mem This pointer will be set to point to the memory that
+ * kvm_create allocates for physical RAM
+ * \return 0 on success
+ */
+int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **phys_mem);
+int kvm_create_vm(kvm_context_t kvm);
+void kvm_create_irqchip(kvm_context_t kvm);
+
+/*!
+ * \brief Start the VCPU
+ *
+ * This starts the VCPU and virtualization is started.\n
+ * \n
+ * This function will not return until any of these conditions are met:
+ * - An IO/MMIO handler does not return "0"
+ * - An exception that neither the guest OS, nor KVM can handle occurs
+ *
+ * \note This function will call the callbacks registered in kvm_init()
+ * to emulate those functions
+ * \note If you at any point want to interrupt the VCPU, kvm_run() will
+ * listen to the EINTR signal. This allows you to simulate external interrupts
+ * and asyncronous IO.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be started
+ * \return 0 on success, but you really shouldn't expect this function to
+ * return except for when an error has occured, or when you have sent it
+ * an EINTR signal.
+ */
+int kvm_run(CPUState *env);
+
+/*!
+ * \brief Check if a vcpu is ready for interrupt injection
+ *
+ * This checks if vcpu interrupts are not masked by mov ss or sti.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return boolean indicating interrupt injection readiness
+ */
+int kvm_is_ready_for_interrupt_injection(CPUState *env);
+
+/*!
+ * \brief Read VCPU registers
+ *
+ * This gets the GP registers from the VCPU and outputs them
+ * into a kvm_regs structure
+ *
+ * \note This function returns a \b copy of the VCPUs registers.\n
+ * If you wish to modify the VCPUs GP registers, you should call kvm_set_regs()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_regs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_get_regs(CPUState *env, struct kvm_regs *regs);
+
+/*!
+ * \brief Write VCPU registers
+ *
+ * This sets the GP registers on the VCPU from a kvm_regs structure
+ *
+ * \note When this function returns, the regs pointer and the data it points to
+ * can be discarded
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param regs Pointer to a kvm_regs which will be populated with the VCPUs
+ * registers values
+ * \return 0 on success
+ */
+int kvm_set_regs(CPUState *env, struct kvm_regs *regs);
+
+#ifdef KVM_CAP_MP_STATE
+/*!
+ * * \brief Read VCPU MP state
+ *
+ */
+int kvm_get_mpstate(CPUState *env, struct kvm_mp_state *mp_state);
+
+/*!
+ * * \brief Write VCPU MP state
+ *
+ */
+int kvm_set_mpstate(CPUState *env, struct kvm_mp_state *mp_state);
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Simulate an external vectored interrupt
+ *
+ * This allows you to simulate an external vectored interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \param irq Vector number
+ * \return 0 on success
+ */
+int kvm_inject_irq(CPUState *env, unsigned irq);
+
+
+/*!
+ * \brief Setting the number of shadow pages to be allocated to the vm
+ *
+ * \param kvm pointer to kvm_context
+ * \param nrshadow_pages number of pages to be allocated
+ */
+int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages);
+
+/*!
+ * \brief Getting the number of shadow pages that are allocated to the vm
+ *
+ * \param kvm pointer to kvm_context
+ * \param nrshadow_pages number of pages to be allocated
+ */
+int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages);
+
+#endif
+
+/*!
+ * \brief Dump VCPU registers
+ *
+ * This dumps some of the information that KVM has about a virtual CPU, namely:
+ * - GP Registers
+ *
+ * A much more verbose version of this is available as kvm_dump_vcpu()
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+void kvm_show_regs(CPUState *env);
+
+
+void *kvm_create_phys_mem(kvm_context_t, unsigned long phys_start,
+ unsigned long len, int log, int writable);
+void kvm_destroy_phys_mem(kvm_context_t, unsigned long phys_start,
+ unsigned long len);
+
+int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_start,
+ unsigned long size);
+int kvm_register_phys_mem(kvm_context_t kvm, unsigned long phys_start,
+ void *userspace_addr, unsigned long len, int log);
+int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long end_addr, void *opaque,
+ int (*cb)(unsigned long start,
+ unsigned long len, void *bitmap,
+ void *opaque));
+int kvm_register_coalesced_mmio(kvm_context_t kvm, uint64_t addr,
+ uint32_t size);
+int kvm_unregister_coalesced_mmio(kvm_context_t kvm, uint64_t addr,
+ uint32_t size);
+
+/*!
+ * \brief Get a bitmap of guest ram pages which are allocated to the guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param phys_addr Memory slot phys addr
+ * \param bitmap Long aligned address of a big enough bitmap (one bit per page)
+ */
+int kvm_get_mem_map(kvm_context_t kvm, unsigned long phys_addr, void *bitmap);
+int kvm_get_mem_map_range(kvm_context_t kvm, unsigned long phys_addr,
+ unsigned long len, void *buf, void *opaque,
+ int (*cb)(unsigned long start,
+ unsigned long len, void *bitmap,
+ void *opaque));
+int kvm_set_irq_level(kvm_context_t kvm, int irq, int level, int *status);
+
+int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, uint64_t phys_start,
+ uint64_t len);
+int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, uint64_t phys_start,
+ uint64_t len);
+/*!
+ * \brief Enable dirty-pages-logging for all memory regions
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_dirty_pages_log_enable_all(kvm_context_t kvm);
+
+/*!
+ * \brief Disable dirty-page-logging for some memory regions
+ *
+ * Disable dirty-pages-logging for those memory regions that were
+ * created with dirty-page-logging disabled.
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_dirty_pages_log_reset(kvm_context_t kvm);
+
+#ifdef KVM_CAP_IRQCHIP
+/*!
+ * \brief Dump in kernel IRQCHIP contents
+ *
+ * Dump one of the in kernel irq chip devices, including PIC (master/slave)
+ * and IOAPIC into a kvm_irqchip structure
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param chip The irq chip device to be dumped
+ */
+int kvm_get_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip);
+
+/*!
+ * \brief Set in kernel IRQCHIP contents
+ *
+ * Write one of the in kernel irq chip devices, including PIC (master/slave)
+ * and IOAPIC
+ *
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param chip THe irq chip device to be written
+ */
+int kvm_set_irqchip(kvm_context_t kvm, struct kvm_irqchip *chip);
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Get in kernel local APIC for vcpu
+ *
+ * Save the local apic state including the timer of a virtual CPU
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be accessed
+ * \param s Local apic state of the specific virtual CPU
+ */
+int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s);
+
+/*!
+ * \brief Set in kernel local APIC for vcpu
+ *
+ * Restore the local apic state including the timer of a virtual CPU
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be accessed
+ * \param s Local apic state of the specific virtual CPU
+ */
+int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s);
+
+#endif
+
+/*!
+ * \brief Simulate an NMI
+ *
+ * This allows you to simulate a non-maskable interrupt.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should get dumped
+ * \return 0 on success
+ */
+int kvm_inject_nmi(CPUState *env);
+
+#endif
+
+/*!
+ * \brief Initialize coalesced MMIO
+ *
+ * Check for coalesced MMIO capability and store in context
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_init_coalesced_mmio(kvm_context_t kvm);
+
+#ifdef KVM_CAP_PIT
+
+#if defined(__i386__) || defined(__x86_64__)
+/*!
+ * \brief Get in kernel PIT of the virtual domain
+ *
+ * Save the PIT state.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param s PIT state of the virtual domain
+ */
+int kvm_get_pit(kvm_context_t kvm, struct kvm_pit_state *s);
+
+/*!
+ * \brief Set in kernel PIT of the virtual domain
+ *
+ * Restore the PIT state.
+ * Timer would be retriggerred after restored.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param s PIT state of the virtual domain
+ */
+int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s);
+
+int kvm_reinject_control(kvm_context_t kvm, int pit_reinject);
+
+#ifdef KVM_CAP_PIT_STATE2
+/*!
+ * \brief Check for kvm support of kvm_pit_state2
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \return 0 on success
+ */
+int kvm_has_pit_state2(kvm_context_t kvm);
+
+/*!
+ * \brief Set in kernel PIT state2 of the virtual domain
+ *
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param ps2 PIT state2 of the virtual domain
+ * \return 0 on success
+ */
+int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2);
+
+/*!
+ * \brief Get in kernel PIT state2 of the virtual domain
+ *
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param ps2 PIT state2 of the virtual domain
+ * \return 0 on success
+ */
+int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2);
+
+#endif
+#endif
+#endif
+
+#ifdef KVM_CAP_VAPIC
+
+int kvm_enable_vapic(CPUState *env, uint64_t vapic);
+
+#endif
+
+#if defined(__s390__)
+int kvm_s390_initial_reset(kvm_context_t kvm, int slot);
+int kvm_s390_interrupt(kvm_context_t kvm, int slot,
+ struct kvm_s390_interrupt *kvmint);
+int kvm_s390_set_initial_psw(kvm_context_t kvm, int slot, psw_t psw);
+int kvm_s390_store_status(kvm_context_t kvm, int slot, unsigned long addr);
+#endif
+
+#ifdef KVM_CAP_DEVICE_ASSIGNMENT
+/*!
+ * \brief Notifies host kernel about a PCI device to be assigned to a guest
+ *
+ * Used for PCI device assignment, this function notifies the host
+ * kernel about the assigning of the physical PCI device to a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_assign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev);
+
+/*!
+ * \brief Assign IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function assigns IRQ numbers for
+ * an physical device and guest IRQ handling.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_assign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq);
+
+#ifdef KVM_CAP_ASSIGN_DEV_IRQ
+/*!
+ * \brief Deassign IRQ for an assigned device
+ *
+ * Used for PCI device assignment, this function deassigns IRQ numbers
+ * for an assigned device.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_irq Parameters, like dev id, host irq, guest irq, etc
+ */
+int kvm_deassign_irq(kvm_context_t kvm, struct kvm_assigned_irq *assigned_irq);
+#endif
+#endif
+
+#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
+/*!
+ * \brief Notifies host kernel about a PCI device to be deassigned from a guest
+ *
+ * Used for hot remove PCI device, this function notifies the host
+ * kernel about the deassigning of the physical PCI device from a guest.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param assigned_dev Parameters, like bus, devfn number, etc
+ */
+int kvm_deassign_pci_device(kvm_context_t kvm,
+ struct kvm_assigned_pci_dev *assigned_dev);
+#endif
+
+/*!
+ * \brief Determines the number of gsis that can be routed
+ *
+ * Returns the number of distinct gsis that can be routed by kvm. This is
+ * also the number of distinct routes (if a gsi has two routes, than another
+ * gsi cannot be used...)
+ *
+ * \param kvm Pointer to the current kvm_context
+ */
+int kvm_get_gsi_count(kvm_context_t kvm);
+
+/*!
+ * \brief Clears the temporary irq routing table
+ *
+ * Clears the temporary irq routing table. Nothing is committed to the
+ * running VM.
+ *
+ */
+int kvm_clear_gsi_routes(void);
+
+/*!
+ * \brief Adds an irq route to the temporary irq routing table
+ *
+ * Adds an irq route to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ */
+int kvm_add_irq_route(int gsi, int irqchip, int pin);
+
+/*!
+ * \brief Removes an irq route from the temporary irq routing table
+ *
+ * Adds an irq route to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ */
+int kvm_del_irq_route(int gsi, int irqchip, int pin);
+
+struct kvm_irq_routing_entry;
+/*!
+ * \brief Adds a routing entry to the temporary irq routing table
+ *
+ * Adds a filled routing entry to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ */
+int kvm_add_routing_entry(struct kvm_irq_routing_entry *entry);
+
+/*!
+ * \brief Removes a routing from the temporary irq routing table
+ *
+ * Remove a routing to the temporary irq routing table. Nothing is
+ * committed to the running VM.
+ */
+int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry);
+
+/*!
+ * \brief Updates a routing in the temporary irq routing table
+ *
+ * Update a routing in the temporary irq routing table
+ * with a new value. entry type and GSI can not be changed.
+ * Nothing is committed to the running VM.
+ */
+int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry,
+ struct kvm_irq_routing_entry *newentry);
+
+
+/*!
+ * \brief Create a file descriptor for injecting interrupts
+ *
+ * Creates an eventfd based file-descriptor that maps to a specific GSI
+ * in the guest. eventfd compliant signaling (write() from userspace, or
+ * eventfd_signal() from kernelspace) will cause the GSI to inject
+ * itself into the guest at the next available window.
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param gsi GSI to assign to this fd
+ * \param flags reserved, must be zero
+ */
+int kvm_irqfd(kvm_context_t kvm, int gsi, int flags);
+
+#ifdef KVM_CAP_DEVICE_MSIX
+int kvm_assign_set_msix_nr(kvm_context_t kvm,
+ struct kvm_assigned_msix_nr *msix_nr);
+int kvm_assign_set_msix_entry(kvm_context_t kvm,
+ struct kvm_assigned_msix_entry *entry);
+#endif
+
+#else /* !CONFIG_KVM */
+
+typedef struct kvm_context *kvm_context_t;
+typedef struct kvm_vcpu_context *kvm_vcpu_context_t;
+
+struct kvm_pit_state {
+};
+
+#endif /* !CONFIG_KVM */
+
+
+/*!
+ * \brief Create new KVM context
+ *
+ * This creates a new kvm_context. A KVM context is a small area of data that
+ * holds information about the KVM instance that gets created by this call.\n
+ * This should always be your first call to KVM.
+ *
+ * \param opaque Not used
+ * \return NULL on failure
+ */
+int kvm_init(void);
+
+int kvm_main_loop(void);
+int kvm_init_ap(void);
+int kvm_vcpu_inited(CPUState *env);
+void kvm_save_lapic(CPUState *env);
+void kvm_load_lapic(CPUState *env);
+
+void kvm_hpet_enable_kpit(void);
+void kvm_hpet_disable_kpit(void);
+
+int kvm_physical_memory_set_dirty_tracking(int enable);
+
+void on_vcpu(CPUState *env, void (*func)(void *data), void *data);
+void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *env);
+void qemu_kvm_cpuid_on_env(CPUState *env);
+void kvm_inject_interrupt(CPUState *env, int mask);
+void kvm_update_after_sipi(CPUState *env);
+void kvm_update_interrupt_request(CPUState *env);
+#ifndef CONFIG_USER_ONLY
+void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr, unsigned long size,
+ int log, int writable);
+
+void kvm_cpu_destroy_phys_mem(target_phys_addr_t start_addr,
+ unsigned long size);
+void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size,
+ int log);
+#endif
+int kvm_qemu_create_memory_alias(uint64_t phys_start, uint64_t len,
+ uint64_t target_phys);
+int kvm_qemu_destroy_memory_alias(uint64_t phys_start);
+
+int kvm_arch_qemu_create_context(void);
+
+void kvm_arch_save_regs(CPUState *env);
+void kvm_arch_load_regs(CPUState *env, int level);
+int kvm_arch_has_work(CPUState *env);
+void kvm_arch_process_irqchip_events(CPUState *env);
+int kvm_arch_try_push_interrupts(void *opaque);
+void kvm_arch_push_nmi(void *opaque);
+void kvm_arch_cpu_reset(CPUState *env);
+int kvm_set_boot_cpu_id(uint32_t id);
+
+void qemu_kvm_aio_wait_start(void);
+void qemu_kvm_aio_wait(void);
+void qemu_kvm_aio_wait_end(void);
+
+void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write);
+
+int kvm_arch_init_irq_routing(void);
+
+int kvm_mmio_read(void *opaque, uint64_t addr, uint8_t * data, int len);
+int kvm_mmio_write(void *opaque, uint64_t addr, uint8_t * data, int len);
+
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+struct ioperm_data;
+
+void kvm_ioperm(CPUState *env, void *data);
+void kvm_add_ioperm_data(struct ioperm_data *data);
+void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num);
+void kvm_arch_do_ioperm(void *_data);
+#endif
+
+#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
+#define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8)
+
+#ifdef CONFIG_KVM
+#include "qemu-queue.h"
+
+extern int kvm_irqchip;
+extern int kvm_pit;
+extern int kvm_pit_reinject;
+extern int kvm_nested;
+extern kvm_context_t kvm_context;
+
+struct ioperm_data {
+ unsigned long start_port;
+ unsigned long num;
+ int turn_on;
+ QLIST_ENTRY(ioperm_data) entries;
+};
+
+void qemu_kvm_cpu_stop(CPUState *env);
+int kvm_arch_halt(CPUState *env);
+int handle_tpr_access(void *opaque, CPUState *env, uint64_t rip,
+ int is_write);
+
+#define qemu_kvm_has_gsi_routing() kvm_has_gsi_routing()
+#ifdef TARGET_I386
+#define qemu_kvm_has_pit_state2() kvm_has_pit_state2(kvm_context)
+#endif
+#else
+#define kvm_nested 0
+#define qemu_kvm_has_gsi_routing() (0)
+#ifdef TARGET_I386
+#define qemu_kvm_has_pit_state2() (0)
+#endif
+#define qemu_kvm_cpu_stop(env) do {} while(0)
+#endif
+
+#ifdef CONFIG_KVM
+
+typedef struct KVMSlot {
+ target_phys_addr_t start_addr;
+ ram_addr_t memory_size;
+ ram_addr_t phys_offset;
+ int slot;
+ int flags;
+} KVMSlot;
+
+typedef struct kvm_dirty_log KVMDirtyLog;
+
+struct KVMState {
+ KVMSlot slots[32];
+ int fd;
+ int vmfd;
+ int coalesced_mmio;
+#ifdef KVM_CAP_COALESCED_MMIO
+ struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
+#endif
+ int broken_set_mem_region;
+ int migration_log;
+ int vcpu_events;
+ int robust_singlestep;
+ int debugregs;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints;
+#endif
+ int irqchip_in_kernel;
+ int pit_in_kernel;
+ int xsave, xcrs;
+ int many_ioeventfds;
+
+ struct kvm_context kvm_context;
+};
+
+extern struct KVMState *kvm_state;
+
+int kvm_tpr_enable_vapic(CPUState *env);
+
+unsigned long kvm_get_thread_id(void);
+int kvm_cpu_is_stopped(CPUState *env);
+
+#endif
+
+#endif
diff --git a/qemu-lock.h b/qemu-lock.h
new file mode 100644
index 0000000..0f9dd9b
--- /dev/null
+++ b/qemu-lock.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2003 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/>
+ */
+
+/* Locking primitives. Most of this code should be redundant -
+ system emulation doesn't need/use locking, NPTL userspace uses
+ pthread mutexes, and non-NPTL userspace isn't threadsafe anyway.
+ In either case a spinlock is probably the wrong kind of lock.
+ Spinlocks are only good if you know annother CPU has the lock and is
+ likely to release it soon. In environments where you have more threads
+ than physical CPUs (the extreme case being a single CPU host) a spinlock
+ simply wastes CPU until the OS decides to preempt it. */
+#if defined(CONFIG_USE_NPTL)
+
+#include <pthread.h>
+#define spin_lock pthread_mutex_lock
+#define spin_unlock pthread_mutex_unlock
+#define spinlock_t pthread_mutex_t
+#define SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER
+
+#else
+
+#if defined(__hppa__)
+
+typedef int spinlock_t[4];
+
+#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 }
+
+static inline void resetlock (spinlock_t *p)
+{
+ (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1;
+}
+
+#else
+
+typedef int spinlock_t;
+
+#define SPIN_LOCK_UNLOCKED 0
+
+static inline void resetlock (spinlock_t *p)
+{
+ *p = SPIN_LOCK_UNLOCKED;
+}
+
+#endif
+
+#if defined(_ARCH_PPC)
+static inline int testandset (int *p)
+{
+ int ret;
+ __asm__ __volatile__ (
+ " lwarx %0,0,%1\n"
+ " xor. %0,%3,%0\n"
+ " bne $+12\n"
+ " stwcx. %2,0,%1\n"
+ " bne- $-16\n"
+ : "=&r" (ret)
+ : "r" (p), "r" (1), "r" (0)
+ : "cr0", "memory");
+ return ret;
+}
+#elif defined(__i386__)
+static inline int testandset (int *p)
+{
+ long int readval = 0;
+
+ __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+ : "+m" (*p), "+a" (readval)
+ : "r" (1)
+ : "cc");
+ return readval;
+}
+#elif defined(__x86_64__)
+static inline int testandset (int *p)
+{
+ long int readval = 0;
+
+ __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+ : "+m" (*p), "+a" (readval)
+ : "r" (1)
+ : "cc");
+ return readval;
+}
+#elif defined(__s390__)
+static inline int testandset (int *p)
+{
+ int ret;
+
+ __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n"
+ " jl 0b"
+ : "=&d" (ret)
+ : "r" (1), "a" (p), "0" (*p)
+ : "cc", "memory" );
+ return ret;
+}
+#elif defined(__alpha__)
+static inline int testandset (int *p)
+{
+ int ret;
+ unsigned long one;
+
+ __asm__ __volatile__ ("0: mov 1,%2\n"
+ " ldl_l %0,%1\n"
+ " stl_c %2,%1\n"
+ " beq %2,1f\n"
+ ".subsection 2\n"
+ "1: br 0b\n"
+ ".previous"
+ : "=r" (ret), "=m" (*p), "=r" (one)
+ : "m" (*p));
+ return ret;
+}
+#elif defined(__sparc__)
+static inline int testandset (int *p)
+{
+ int ret;
+
+ __asm__ __volatile__("ldstub [%1], %0"
+ : "=r" (ret)
+ : "r" (p)
+ : "memory");
+
+ return (ret ? 1 : 0);
+}
+#elif defined(__arm__)
+static inline int testandset (int *spinlock)
+{
+ register unsigned int ret;
+ __asm__ __volatile__("swp %0, %1, [%2]"
+ : "=r"(ret)
+ : "0"(1), "r"(spinlock));
+
+ return ret;
+}
+#elif defined(__mc68000)
+static inline int testandset (int *p)
+{
+ char ret;
+ __asm__ __volatile__("tas %1; sne %0"
+ : "=r" (ret)
+ : "m" (p)
+ : "cc","memory");
+ return ret;
+}
+#elif defined(__hppa__)
+
+/* Because malloc only guarantees 8-byte alignment for malloc'd data,
+ and GCC only guarantees 8-byte alignment for stack locals, we can't
+ be assured of 16-byte alignment for atomic lock data even if we
+ specify "__attribute ((aligned(16)))" in the type declaration. So,
+ we use a struct containing an array of four ints for the atomic lock
+ type and dynamically select the 16-byte aligned int from the array
+ for the semaphore. */
+#define __PA_LDCW_ALIGNMENT 16
+static inline void *ldcw_align (void *p) {
+ unsigned long a = (unsigned long)p;
+ a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1);
+ return (void *)a;
+}
+
+static inline int testandset (spinlock_t *p)
+{
+ unsigned int ret;
+ p = ldcw_align(p);
+ __asm__ __volatile__("ldcw 0(%1),%0"
+ : "=r" (ret)
+ : "r" (p)
+ : "memory" );
+ return !ret;
+}
+
+#elif defined(__ia64)
+
+#include "ia64intrin.h"
+
+static inline int testandset (int *p)
+{
+ return (int)cmpxchg_acq(p,0,1);
+}
+#elif defined(__mips__)
+static inline int testandset (int *p)
+{
+ int ret;
+
+ __asm__ __volatile__ (
+ " .set push \n"
+ " .set noat \n"
+ " .set mips2 \n"
+ "1: li $1, 1 \n"
+ " ll %0, %1 \n"
+ " sc $1, %1 \n"
+ " beqz $1, 1b \n"
+ " .set pop "
+ : "=r" (ret), "+R" (*p)
+ :
+ : "memory");
+
+ return ret;
+}
+#else
+#error unimplemented CPU support
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+static inline void spin_lock(spinlock_t *lock)
+{
+ while (testandset(lock));
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+ resetlock(lock);
+}
+#else
+static inline void spin_lock(spinlock_t *lock)
+{
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+}
+#endif
+
+#endif
diff --git a/qemu-log.h b/qemu-log.h
new file mode 100644
index 0000000..fccfb11
--- /dev/null
+++ b/qemu-log.h
@@ -0,0 +1,93 @@
+#ifndef QEMU_LOG_H
+#define QEMU_LOG_H
+
+/* The deprecated global variables: */
+extern FILE *logfile;
+extern int loglevel;
+
+
+/*
+ * The new API:
+ *
+ */
+
+/* Log settings checking macros: */
+
+/* Returns true if qemu_log() will really write somewhere
+ */
+#define qemu_log_enabled() (logfile != NULL)
+
+/* Returns true if a bit is set in the current loglevel mask
+ */
+#define qemu_loglevel_mask(b) ((loglevel & (b)) != 0)
+
+
+/* Logging functions: */
+
+/* main logging function
+ */
+#define qemu_log(...) do { \
+ if (logfile) \
+ fprintf(logfile, ## __VA_ARGS__); \
+ } while (0)
+
+/* vfprintf-like logging function
+ */
+#define qemu_log_vprintf(fmt, va) do { \
+ if (logfile) \
+ vfprintf(logfile, fmt, va); \
+ } while (0)
+
+/* log only if a bit is set on the current loglevel mask
+ */
+#define qemu_log_mask(b, ...) do { \
+ if (loglevel & (b)) \
+ fprintf(logfile, ## __VA_ARGS__); \
+ } while (0)
+
+
+
+
+/* Special cases: */
+
+/* cpu_dump_state() logging functions: */
+#define log_cpu_state(env, f) cpu_dump_state((env), logfile, fprintf, (f));
+#define log_cpu_state_mask(b, env, f) do { \
+ if (loglevel & (b)) log_cpu_state((env), (f)); \
+ } while (0)
+
+/* disas() and target_disas() to logfile: */
+#define log_target_disas(start, len, flags) \
+ target_disas(logfile, (start), (len), (flags))
+#define log_disas(start, len) \
+ disas(logfile, (start), (len))
+
+/* page_dump() output to the log file: */
+#define log_page_dump() page_dump(logfile)
+
+
+
+/* Maintenance: */
+
+/* fflush() the log file */
+#define qemu_log_flush() fflush(logfile)
+
+/* Close the log file */
+#define qemu_log_close() do { \
+ fclose(logfile); \
+ logfile = NULL; \
+ } while (0)
+
+/* Set up a new log file */
+#define qemu_log_set_file(f) do { \
+ logfile = (f); \
+ } while (0)
+
+/* Set up a new log file, only if none is set */
+#define qemu_log_try_set_file(f) do { \
+ if (!logfile) \
+ logfile = (f); \
+ } while (0)
+
+
+#endif
diff --git a/qemu-malloc.c b/qemu-malloc.c
new file mode 100644
index 0000000..b9b3851
--- /dev/null
+++ b/qemu-malloc.c
@@ -0,0 +1,98 @@
+/*
+ * malloc-like functions for system emulation.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "trace.h"
+#include <stdlib.h>
+
+void qemu_free(void *ptr)
+{
+ trace_qemu_free(ptr);
+ free(ptr);
+}
+
+static int allow_zero_malloc(void)
+{
+#if defined(CONFIG_ZERO_MALLOC)
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+void *qemu_malloc(size_t size)
+{
+ void *ptr;
+ if (!size && !allow_zero_malloc()) {
+ abort();
+ }
+ ptr = qemu_oom_check(malloc(size ? size : 1));
+ trace_qemu_malloc(size, ptr);
+ return ptr;
+}
+
+void *qemu_realloc(void *ptr, size_t size)
+{
+ void *newptr;
+ if (!size && !allow_zero_malloc()) {
+ abort();
+ }
+ newptr = qemu_oom_check(realloc(ptr, size ? size : 1));
+ trace_qemu_realloc(ptr, size, newptr);
+ return newptr;
+}
+
+void *qemu_mallocz(size_t size)
+{
+ void *ptr;
+ if (!size && !allow_zero_malloc()) {
+ abort();
+ }
+ ptr = qemu_oom_check(calloc(1, size ? size : 1));
+ trace_qemu_malloc(size, ptr);
+ return ptr;
+}
+
+char *qemu_strdup(const char *str)
+{
+ char *ptr;
+ size_t len = strlen(str);
+ ptr = qemu_malloc(len + 1);
+ memcpy(ptr, str, len + 1);
+ return ptr;
+}
+
+char *qemu_strndup(const char *str, size_t size)
+{
+ const char *end = memchr(str, 0, size);
+ char *new;
+
+ if (end) {
+ size = end - str;
+ }
+
+ new = qemu_malloc(size + 1);
+ new[size] = 0;
+
+ return memcpy(new, str, size);
+}
diff --git a/qemu-nbd.c b/qemu-nbd.c
new file mode 100644
index 0000000..e858033
--- /dev/null
+++ b/qemu-nbd.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Network Block Device
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <qemu-common.h>
+#include "block_int.h"
+#include "nbd.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <libgen.h>
+
+#define SOCKET_PATH "/var/lock/qemu-nbd-%s"
+
+#define NBD_BUFFER_SIZE (1024*1024)
+
+static int verbose;
+
+static void usage(const char *name)
+{
+ printf(
+"Usage: %s [OPTIONS] FILE\n"
+"QEMU Disk Network Block Device Server\n"
+"\n"
+" -p, --port=PORT port to listen on (default `%d')\n"
+" -o, --offset=OFFSET offset into the image\n"
+" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n"
+" -k, --socket=PATH path to the unix socket\n"
+" (default '"SOCKET_PATH"')\n"
+" -r, --read-only export read-only\n"
+" -P, --partition=NUM only expose partition NUM\n"
+" -s, --snapshot use snapshot file\n"
+" -n, --nocache disable host cache\n"
+" -c, --connect=DEV connect FILE to the local NBD device DEV\n"
+" -d, --disconnect disconnect the specified device\n"
+" -e, --shared=NUM device can be shared by NUM clients (default '1')\n"
+" -t, --persistent don't exit on the last connection\n"
+" -v, --verbose display extra debugging information\n"
+" -h, --help display this help and exit\n"
+" -V, --version output version information and exit\n"
+"\n"
+"Report bugs to <anthony@codemonkey.ws>\n"
+ , name, NBD_DEFAULT_PORT, "DEVICE");
+}
+
+static void version(const char *name)
+{
+ printf(
+"%s version 0.0.1\n"
+"Written by Anthony Liguori.\n"
+"\n"
+"Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.\n"
+"This is free software; see the source for copying conditions. There is NO\n"
+"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
+ , name);
+}
+
+struct partition_record
+{
+ uint8_t bootable;
+ uint8_t start_head;
+ uint32_t start_cylinder;
+ uint8_t start_sector;
+ uint8_t system;
+ uint8_t end_head;
+ uint8_t end_cylinder;
+ uint8_t end_sector;
+ uint32_t start_sector_abs;
+ uint32_t nb_sectors_abs;
+};
+
+static void read_partition(uint8_t *p, struct partition_record *r)
+{
+ r->bootable = p[0];
+ r->start_head = p[1];
+ r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300);
+ r->start_sector = p[2] & 0x3f;
+ r->system = p[4];
+ r->end_head = p[5];
+ r->end_cylinder = p[7] | ((p[6] << 2) & 0x300);
+ r->end_sector = p[6] & 0x3f;
+ r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24;
+ r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24;
+}
+
+static int find_partition(BlockDriverState *bs, int partition,
+ off_t *offset, off_t *size)
+{
+ struct partition_record mbr[4];
+ uint8_t data[512];
+ int i;
+ int ext_partnum = 4;
+ int ret;
+
+ if ((ret = bdrv_read(bs, 0, data, 1)) < 0) {
+ errno = -ret;
+ err(EXIT_FAILURE, "error while reading");
+ }
+
+ if (data[510] != 0x55 || data[511] != 0xaa) {
+ errno = -EINVAL;
+ return -1;
+ }
+
+ for (i = 0; i < 4; i++) {
+ read_partition(&data[446 + 16 * i], &mbr[i]);
+
+ if (!mbr[i].nb_sectors_abs)
+ continue;
+
+ if (mbr[i].system == 0xF || mbr[i].system == 0x5) {
+ struct partition_record ext[4];
+ uint8_t data1[512];
+ int j;
+
+ if ((ret = bdrv_read(bs, mbr[i].start_sector_abs, data1, 1)) < 0) {
+ errno = -ret;
+ err(EXIT_FAILURE, "error while reading");
+ }
+
+ for (j = 0; j < 4; j++) {
+ read_partition(&data1[446 + 16 * j], &ext[j]);
+ if (!ext[j].nb_sectors_abs)
+ continue;
+
+ if ((ext_partnum + j + 1) == partition) {
+ *offset = (uint64_t)ext[j].start_sector_abs << 9;
+ *size = (uint64_t)ext[j].nb_sectors_abs << 9;
+ return 0;
+ }
+ }
+ ext_partnum += 4;
+ } else if ((i + 1) == partition) {
+ *offset = (uint64_t)mbr[i].start_sector_abs << 9;
+ *size = (uint64_t)mbr[i].nb_sectors_abs << 9;
+ return 0;
+ }
+ }
+
+ errno = -ENOENT;
+ return -1;
+}
+
+static void show_parts(const char *device)
+{
+ if (fork() == 0) {
+ int nbd;
+
+ /* linux just needs an open() to trigger
+ * the partition table update
+ * but remember to load the module with max_part != 0 :
+ * modprobe nbd max_part=63
+ */
+ nbd = open(device, O_RDWR);
+ if (nbd != -1)
+ close(nbd);
+ exit(0);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ BlockDriverState *bs;
+ off_t dev_offset = 0;
+ off_t offset = 0;
+ bool readonly = false;
+ bool disconnect = false;
+ const char *bindto = "0.0.0.0";
+ int port = NBD_DEFAULT_PORT;
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ off_t fd_size;
+ char *device = NULL;
+ char *socket = NULL;
+ char sockpath[128];
+ const char *sopt = "hVb:o:p:rsnP:c:dvk:e:t";
+ struct option lopt[] = {
+ { "help", 0, NULL, 'h' },
+ { "version", 0, NULL, 'V' },
+ { "bind", 1, NULL, 'b' },
+ { "port", 1, NULL, 'p' },
+ { "socket", 1, NULL, 'k' },
+ { "offset", 1, NULL, 'o' },
+ { "read-only", 0, NULL, 'r' },
+ { "partition", 1, NULL, 'P' },
+ { "connect", 1, NULL, 'c' },
+ { "disconnect", 0, NULL, 'd' },
+ { "snapshot", 0, NULL, 's' },
+ { "nocache", 0, NULL, 'n' },
+ { "shared", 1, NULL, 'e' },
+ { "persistent", 0, NULL, 't' },
+ { "verbose", 0, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+ };
+ int ch;
+ int opt_ind = 0;
+ int li;
+ char *end;
+ int flags = BDRV_O_RDWR;
+ int partition = -1;
+ int ret;
+ int shared = 1;
+ uint8_t *data;
+ fd_set fds;
+ int *sharing_fds;
+ int fd;
+ int i;
+ int nb_fds = 0;
+ int max_fd;
+ int persistent = 0;
+ uint32_t nbdflags;
+
+ while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
+ switch (ch) {
+ case 's':
+ flags |= BDRV_O_SNAPSHOT;
+ break;
+ case 'n':
+ flags |= BDRV_O_NOCACHE;
+ break;
+ case 'b':
+ bindto = optarg;
+ break;
+ case 'p':
+ li = strtol(optarg, &end, 0);
+ if (*end) {
+ errx(EXIT_FAILURE, "Invalid port `%s'", optarg);
+ }
+ if (li < 1 || li > 65535) {
+ errx(EXIT_FAILURE, "Port out of range `%s'", optarg);
+ }
+ port = (uint16_t)li;
+ break;
+ case 'o':
+ dev_offset = strtoll (optarg, &end, 0);
+ if (*end) {
+ errx(EXIT_FAILURE, "Invalid offset `%s'", optarg);
+ }
+ if (dev_offset < 0) {
+ errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg);
+ }
+ break;
+ case 'r':
+ readonly = true;
+ flags &= ~BDRV_O_RDWR;
+ break;
+ case 'P':
+ partition = strtol(optarg, &end, 0);
+ if (*end)
+ errx(EXIT_FAILURE, "Invalid partition `%s'", optarg);
+ if (partition < 1 || partition > 8)
+ errx(EXIT_FAILURE, "Invalid partition %d", partition);
+ break;
+ case 'k':
+ socket = optarg;
+ if (socket[0] != '/')
+ errx(EXIT_FAILURE, "socket path must be absolute\n");
+ break;
+ case 'd':
+ disconnect = true;
+ break;
+ case 'c':
+ device = optarg;
+ break;
+ case 'e':
+ shared = strtol(optarg, &end, 0);
+ if (*end) {
+ errx(EXIT_FAILURE, "Invalid shared device number '%s'", optarg);
+ }
+ if (shared < 1) {
+ errx(EXIT_FAILURE, "Shared device number must be greater than 0\n");
+ }
+ break;
+ case 't':
+ persistent = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ version(argv[0]);
+ exit(0);
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ case '?':
+ errx(EXIT_FAILURE, "Try `%s --help' for more information.",
+ argv[0]);
+ }
+ }
+
+ if ((argc - optind) != 1) {
+ errx(EXIT_FAILURE, "Invalid number of argument.\n"
+ "Try `%s --help' for more information.",
+ argv[0]);
+ }
+
+ if (disconnect) {
+ fd = open(argv[optind], O_RDWR);
+ if (fd == -1)
+ err(EXIT_FAILURE, "Cannot open %s", argv[optind]);
+
+ nbd_disconnect(fd);
+
+ close(fd);
+
+ printf("%s disconnected\n", argv[optind]);
+
+ return 0;
+ }
+
+ bdrv_init();
+
+ bs = bdrv_new("hda");
+
+ if ((ret = bdrv_open(bs, argv[optind], flags, NULL)) < 0) {
+ errno = -ret;
+ err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]);
+ }
+
+ fd_size = bs->total_sectors * 512;
+
+ if (partition != -1 &&
+ find_partition(bs, partition, &dev_offset, &fd_size))
+ err(EXIT_FAILURE, "Could not find partition %d", partition);
+
+ if (device) {
+ pid_t pid;
+ int sock;
+
+ /* want to fail before daemonizing */
+ if (access(device, R_OK|W_OK) == -1) {
+ err(EXIT_FAILURE, "Could not access '%s'", device);
+ }
+
+ if (!verbose) {
+ /* detach client and server */
+ if (daemon(0, 0) == -1) {
+ err(EXIT_FAILURE, "Failed to daemonize");
+ }
+ }
+
+ if (socket == NULL) {
+ snprintf(sockpath, sizeof(sockpath), SOCKET_PATH,
+ basename(device));
+ socket = sockpath;
+ }
+
+ pid = fork();
+ if (pid < 0)
+ return 1;
+ if (pid != 0) {
+ off_t size;
+ size_t blocksize;
+
+ ret = 0;
+ bdrv_close(bs);
+
+ do {
+ sock = unix_socket_outgoing(socket);
+ if (sock == -1) {
+ if (errno != ENOENT && errno != ECONNREFUSED) {
+ ret = 1;
+ goto out;
+ }
+ sleep(1); /* wait children */
+ }
+ } while (sock == -1);
+
+ fd = open(device, O_RDWR);
+ if (fd == -1) {
+ ret = 1;
+ goto out;
+ }
+
+ ret = nbd_receive_negotiate(sock, NULL, &nbdflags,
+ &size, &blocksize);
+ if (ret == -1) {
+ ret = 1;
+ goto out;
+ }
+
+ ret = nbd_init(fd, sock, size, blocksize);
+ if (ret == -1) {
+ ret = 1;
+ goto out;
+ }
+
+ printf("NBD device %s is now connected to file %s\n",
+ device, argv[optind]);
+
+ /* update partition table */
+
+ show_parts(device);
+
+ ret = nbd_client(fd);
+ if (ret) {
+ ret = 1;
+ }
+ close(fd);
+ out:
+ kill(pid, SIGTERM);
+ unlink(socket);
+
+ return ret;
+ }
+ /* children */
+ }
+
+ sharing_fds = qemu_malloc((shared + 1) * sizeof(int));
+
+ if (socket) {
+ sharing_fds[0] = unix_socket_incoming(socket);
+ } else {
+ sharing_fds[0] = tcp_socket_incoming(bindto, port);
+ }
+
+ if (sharing_fds[0] == -1)
+ return 1;
+ max_fd = sharing_fds[0];
+ nb_fds++;
+
+ data = qemu_blockalign(bs, NBD_BUFFER_SIZE);
+ if (data == NULL)
+ errx(EXIT_FAILURE, "Cannot allocate data buffer");
+
+ do {
+
+ FD_ZERO(&fds);
+ for (i = 0; i < nb_fds; i++)
+ FD_SET(sharing_fds[i], &fds);
+
+ ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
+ if (ret == -1)
+ break;
+
+ if (FD_ISSET(sharing_fds[0], &fds))
+ ret--;
+ for (i = 1; i < nb_fds && ret; i++) {
+ if (FD_ISSET(sharing_fds[i], &fds)) {
+ if (nbd_trip(bs, sharing_fds[i], fd_size, dev_offset,
+ &offset, readonly, data, NBD_BUFFER_SIZE) != 0) {
+ close(sharing_fds[i]);
+ nb_fds--;
+ sharing_fds[i] = sharing_fds[nb_fds];
+ i--;
+ }
+ ret--;
+ }
+ }
+ /* new connection ? */
+ if (FD_ISSET(sharing_fds[0], &fds)) {
+ if (nb_fds < shared + 1) {
+ sharing_fds[nb_fds] = accept(sharing_fds[0],
+ (struct sockaddr *)&addr,
+ &addr_len);
+ if (sharing_fds[nb_fds] != -1 &&
+ nbd_negotiate(sharing_fds[nb_fds], fd_size) != -1) {
+ if (sharing_fds[nb_fds] > max_fd)
+ max_fd = sharing_fds[nb_fds];
+ nb_fds++;
+ }
+ }
+ }
+ } while (persistent || nb_fds > 1);
+ qemu_vfree(data);
+
+ close(sharing_fds[0]);
+ bdrv_close(bs);
+ qemu_free(sharing_fds);
+ if (socket)
+ unlink(socket);
+
+ return 0;
+}
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
new file mode 100644
index 0000000..44996cc
--- /dev/null
+++ b/qemu-nbd.texi
@@ -0,0 +1,66 @@
+@example
+@c man begin SYNOPSIS
+usage: qemu-nbd [OPTION]... @var{filename}
+@c man end
+@end example
+
+@c man begin DESCRIPTION
+
+Export Qemu disk image using NBD protocol.
+
+@c man end
+
+@c man begin OPTIONS
+@table @option
+@item @var{filename}
+ is a disk image filename
+@item -p, --port=@var{port}
+ port to listen on (default @samp{1024})
+@item -o, --offset=@var{offset}
+ offset into the image
+@item -b, --bind=@var{iface}
+ interface to bind to (default @samp{0.0.0.0})
+@item -k, --socket=@var{path}
+ Use a unix socket with path @var{path}
+@item -r, --read-only
+ export read-only
+@item -P, --partition=@var{num}
+ only expose partition @var{num}
+@item -s, --snapshot
+ use snapshot file
+@item -n, --nocache
+ disable host cache
+@item -c, --connect=@var{dev}
+ connect @var{filename} to NBD device @var{dev}
+@item -d, --disconnect
+ disconnect the specified device
+@item -e, --shared=@var{num}
+ device can be shared by @var{num} clients (default @samp{1})
+@item -t, --persistent
+ don't exit on the last connection
+@item -v, --verbose
+ display extra debugging information
+@item -h, --help
+ display this help and exit
+@item -V, --version
+ output version information and exit
+@end table
+
+@c man end
+
+@ignore
+
+@setfilename qemu-nbd
+@settitle QEMU Disk Network Block Device Server
+
+@c man begin AUTHOR
+Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+@c man end
+
+@c man begin SEEALSO
+qemu-img(1)
+@c man end
+
+@end ignore
diff --git a/qemu-objects.h b/qemu-objects.h
new file mode 100644
index 0000000..c53fbaa
--- /dev/null
+++ b/qemu-objects.h
@@ -0,0 +1,25 @@
+/*
+ * Include all QEMU objects.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef QEMU_OBJECTS_H
+#define QEMU_OBJECTS_H
+
+#include "qobject.h"
+#include "qint.h"
+#include "qfloat.h"
+#include "qbool.h"
+#include "qstring.h"
+#include "qdict.h"
+#include "qlist.h"
+#include "qjson.h"
+
+#endif /* QEMU_OBJECTS_H */
diff --git a/qemu-option.c b/qemu-option.c
new file mode 100644
index 0000000..65db542
--- /dev/null
+++ b/qemu-option.c
@@ -0,0 +1,977 @@
+/*
+ * Commandline option parsing functions
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "qemu-objects.h"
+#include "qemu-option.h"
+#include "qerror.h"
+
+/*
+ * Extracts the name of an option from the parameter string (p points at the
+ * first byte of the option name)
+ *
+ * The option name is delimited by delim (usually , or =) or the string end
+ * and is copied into buf. If the option name is longer than buf_size, it is
+ * truncated. buf is always zero terminated.
+ *
+ * The return value is the position of the delimiter/zero byte after the option
+ * name in p.
+ */
+const char *get_opt_name(char *buf, int buf_size, const char *p, char delim)
+{
+ char *q;
+
+ q = buf;
+ while (*p != '\0' && *p != delim) {
+ if (q && (q - buf) < buf_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ if (q)
+ *q = '\0';
+
+ return p;
+}
+
+/*
+ * Extracts the value of an option from the parameter string p (p points at the
+ * first byte of the option value)
+ *
+ * This function is comparable to get_opt_name with the difference that the
+ * delimiter is fixed to be comma which starts a new option. To specify an
+ * option value that contains commas, double each comma.
+ */
+const char *get_opt_value(char *buf, int buf_size, const char *p)
+{
+ char *q;
+
+ q = buf;
+ while (*p != '\0') {
+ if (*p == ',') {
+ if (*(p + 1) != ',')
+ break;
+ p++;
+ }
+ if (q && (q - buf) < buf_size - 1)
+ *q++ = *p;
+ p++;
+ }
+ if (q)
+ *q = '\0';
+
+ return p;
+}
+
+int get_next_param_value(char *buf, int buf_size,
+ const char *tag, const char **pstr)
+{
+ const char *p;
+ char option[128];
+
+ p = *pstr;
+ for(;;) {
+ p = get_opt_name(option, sizeof(option), p, '=');
+ if (*p != '=')
+ break;
+ p++;
+ if (!strcmp(tag, option)) {
+ *pstr = get_opt_value(buf, buf_size, p);
+ if (**pstr == ',') {
+ (*pstr)++;
+ }
+ return strlen(buf);
+ } else {
+ p = get_opt_value(NULL, 0, p);
+ }
+ if (*p != ',')
+ break;
+ p++;
+ }
+ return 0;
+}
+
+int get_param_value(char *buf, int buf_size,
+ const char *tag, const char *str)
+{
+ return get_next_param_value(buf, buf_size, tag, &str);
+}
+
+int check_params(char *buf, int buf_size,
+ const char * const *params, const char *str)
+{
+ const char *p;
+ int i;
+
+ p = str;
+ while (*p != '\0') {
+ p = get_opt_name(buf, buf_size, p, '=');
+ if (*p != '=') {
+ return -1;
+ }
+ p++;
+ for (i = 0; params[i] != NULL; i++) {
+ if (!strcmp(params[i], buf)) {
+ break;
+ }
+ }
+ if (params[i] == NULL) {
+ return -1;
+ }
+ p = get_opt_value(NULL, 0, p);
+ if (*p != ',') {
+ break;
+ }
+ p++;
+ }
+ return 0;
+}
+
+/*
+ * Searches an option list for an option with the given name
+ */
+QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list,
+ const char *name)
+{
+ while (list && list->name) {
+ if (!strcmp(list->name, name)) {
+ return list;
+ }
+ list++;
+ }
+
+ return NULL;
+}
+
+static int parse_option_bool(const char *name, const char *value, int *ret)
+{
+ if (value != NULL) {
+ if (!strcmp(value, "on")) {
+ *ret = 1;
+ } else if (!strcmp(value, "off")) {
+ *ret = 0;
+ } else {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'");
+ return -1;
+ }
+ } else {
+ *ret = 1;
+ }
+ return 0;
+}
+
+static int parse_option_number(const char *name, const char *value, uint64_t *ret)
+{
+ char *postfix;
+ uint64_t number;
+
+ if (value != NULL) {
+ number = strtoull(value, &postfix, 0);
+ if (*postfix != '\0') {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number");
+ return -1;
+ }
+ *ret = number;
+ } else {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number");
+ return -1;
+ }
+ return 0;
+}
+
+static int parse_option_size(const char *name, const char *value, uint64_t *ret)
+{
+ char *postfix;
+ double sizef;
+
+ if (value != NULL) {
+ sizef = strtod(value, &postfix);
+ switch (*postfix) {
+ case 'T':
+ sizef *= 1024;
+ case 'G':
+ sizef *= 1024;
+ case 'M':
+ sizef *= 1024;
+ case 'K':
+ case 'k':
+ sizef *= 1024;
+ case 'b':
+ case '\0':
+ *ret = (uint64_t) sizef;
+ break;
+ default:
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size");
+ error_printf_unless_qmp("You may use k, M, G or T suffixes for "
+ "kilobytes, megabytes, gigabytes and terabytes.\n");
+ return -1;
+ }
+ } else {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Sets the value of a parameter in a given option list. The parsing of the
+ * value depends on the type of option:
+ *
+ * OPT_FLAG (uses value.n):
+ * If no value is given, the flag is set to 1.
+ * Otherwise the value must be "on" (set to 1) or "off" (set to 0)
+ *
+ * OPT_STRING (uses value.s):
+ * value is strdup()ed and assigned as option value
+ *
+ * OPT_SIZE (uses value.n):
+ * The value is converted to an integer. Suffixes for kilobytes etc. are
+ * allowed (powers of 1024).
+ *
+ * Returns 0 on succes, -1 in error cases
+ */
+int set_option_parameter(QEMUOptionParameter *list, const char *name,
+ const char *value)
+{
+ int flag;
+
+ // Find a matching parameter
+ list = get_option_parameter(list, name);
+ if (list == NULL) {
+ fprintf(stderr, "Unknown option '%s'\n", name);
+ return -1;
+ }
+
+ // Process parameter
+ switch (list->type) {
+ case OPT_FLAG:
+ if (parse_option_bool(name, value, &flag) == -1)
+ return -1;
+ list->value.n = flag;
+ break;
+
+ case OPT_STRING:
+ if (value != NULL) {
+ list->value.s = qemu_strdup(value);
+ } else {
+ fprintf(stderr, "Option '%s' needs a parameter\n", name);
+ return -1;
+ }
+ break;
+
+ case OPT_SIZE:
+ if (parse_option_size(name, value, &list->value.n) == -1)
+ return -1;
+ break;
+
+ default:
+ fprintf(stderr, "Bug: Option '%s' has an unknown type\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Sets the given parameter to an integer instead of a string.
+ * This function cannot be used to set string options.
+ *
+ * Returns 0 on success, -1 in error cases
+ */
+int set_option_parameter_int(QEMUOptionParameter *list, const char *name,
+ uint64_t value)
+{
+ // Find a matching parameter
+ list = get_option_parameter(list, name);
+ if (list == NULL) {
+ fprintf(stderr, "Unknown option '%s'\n", name);
+ return -1;
+ }
+
+ // Process parameter
+ switch (list->type) {
+ case OPT_FLAG:
+ case OPT_NUMBER:
+ case OPT_SIZE:
+ list->value.n = value;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Frees a option list. If it contains strings, the strings are freed as well.
+ */
+void free_option_parameters(QEMUOptionParameter *list)
+{
+ QEMUOptionParameter *cur = list;
+
+ while (cur && cur->name) {
+ if (cur->type == OPT_STRING) {
+ qemu_free(cur->value.s);
+ }
+ cur++;
+ }
+
+ qemu_free(list);
+}
+
+/*
+ * Count valid options in list
+ */
+static size_t count_option_parameters(QEMUOptionParameter *list)
+{
+ size_t num_options = 0;
+
+ while (list && list->name) {
+ num_options++;
+ list++;
+ }
+
+ return num_options;
+}
+
+/*
+ * Append an option list (list) to an option list (dest).
+ *
+ * If dest is NULL, a new copy of list is created.
+ *
+ * Returns a pointer to the first element of dest (or the newly allocated copy)
+ */
+QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest,
+ QEMUOptionParameter *list)
+{
+ size_t num_options, num_dest_options;
+
+ num_options = count_option_parameters(dest);
+ num_dest_options = num_options;
+
+ num_options += count_option_parameters(list);
+
+ dest = qemu_realloc(dest, (num_options + 1) * sizeof(QEMUOptionParameter));
+ dest[num_dest_options].name = NULL;
+
+ while (list && list->name) {
+ if (get_option_parameter(dest, list->name) == NULL) {
+ dest[num_dest_options++] = *list;
+ dest[num_dest_options].name = NULL;
+ }
+ list++;
+ }
+
+ return dest;
+}
+
+/*
+ * Parses a parameter string (param) into an option list (dest).
+ *
+ * list is the template option list. If dest is NULL, a new copy of list is
+ * created. If list is NULL, this function fails.
+ *
+ * A parameter string consists of one or more parameters, separated by commas.
+ * Each parameter consists of its name and possibly of a value. In the latter
+ * case, the value is delimited by an = character. To specify a value which
+ * contains commas, double each comma so it won't be recognized as the end of
+ * the parameter.
+ *
+ * For more details of the parsing see above.
+ *
+ * Returns a pointer to the first element of dest (or the newly allocated copy)
+ * or NULL in error cases
+ */
+QEMUOptionParameter *parse_option_parameters(const char *param,
+ QEMUOptionParameter *list, QEMUOptionParameter *dest)
+{
+ QEMUOptionParameter *allocated = NULL;
+ char name[256];
+ char value[256];
+ char *param_delim, *value_delim;
+ char next_delim;
+
+ if (list == NULL) {
+ return NULL;
+ }
+
+ if (dest == NULL) {
+ dest = allocated = append_option_parameters(NULL, list);
+ }
+
+ while (*param) {
+
+ // Find parameter name and value in the string
+ param_delim = strchr(param, ',');
+ value_delim = strchr(param, '=');
+
+ if (value_delim && (value_delim < param_delim || !param_delim)) {
+ next_delim = '=';
+ } else {
+ next_delim = ',';
+ value_delim = NULL;
+ }
+
+ param = get_opt_name(name, sizeof(name), param, next_delim);
+ if (value_delim) {
+ param = get_opt_value(value, sizeof(value), param + 1);
+ }
+ if (*param != '\0') {
+ param++;
+ }
+
+ // Set the parameter
+ if (set_option_parameter(dest, name, value_delim ? value : NULL)) {
+ goto fail;
+ }
+ }
+
+ return dest;
+
+fail:
+ // Only free the list if it was newly allocated
+ free_option_parameters(allocated);
+ return NULL;
+}
+
+/*
+ * Prints all options of a list that have a value to stdout
+ */
+void print_option_parameters(QEMUOptionParameter *list)
+{
+ while (list && list->name) {
+ switch (list->type) {
+ case OPT_STRING:
+ if (list->value.s != NULL) {
+ printf("%s='%s' ", list->name, list->value.s);
+ }
+ break;
+ case OPT_FLAG:
+ printf("%s=%s ", list->name, list->value.n ? "on" : "off");
+ break;
+ case OPT_SIZE:
+ case OPT_NUMBER:
+ printf("%s=%" PRId64 " ", list->name, list->value.n);
+ break;
+ default:
+ printf("%s=(unkown type) ", list->name);
+ break;
+ }
+ list++;
+ }
+}
+
+/*
+ * Prints an overview of all available options
+ */
+void print_option_help(QEMUOptionParameter *list)
+{
+ printf("Supported options:\n");
+ while (list && list->name) {
+ printf("%-16s %s\n", list->name,
+ list->help ? list->help : "No description available");
+ list++;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+struct QemuOpt {
+ const char *name;
+ const char *str;
+
+ const QemuOptDesc *desc;
+ union {
+ int boolean;
+ uint64_t uint;
+ } value;
+
+ QemuOpts *opts;
+ QTAILQ_ENTRY(QemuOpt) next;
+};
+
+struct QemuOpts {
+ char *id;
+ QemuOptsList *list;
+ Location loc;
+ QTAILQ_HEAD(QemuOptHead, QemuOpt) head;
+ QTAILQ_ENTRY(QemuOpts) next;
+};
+
+static QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name)
+{
+ QemuOpt *opt;
+
+ QTAILQ_FOREACH_REVERSE(opt, &opts->head, QemuOptHead, next) {
+ if (strcmp(opt->name, name) != 0)
+ continue;
+ return opt;
+ }
+ return NULL;
+}
+
+const char *qemu_opt_get(QemuOpts *opts, const char *name)
+{
+ QemuOpt *opt = qemu_opt_find(opts, name);
+ return opt ? opt->str : NULL;
+}
+
+int qemu_opt_get_bool(QemuOpts *opts, const char *name, int defval)
+{
+ QemuOpt *opt = qemu_opt_find(opts, name);
+
+ if (opt == NULL)
+ return defval;
+ assert(opt->desc && opt->desc->type == QEMU_OPT_BOOL);
+ return opt->value.boolean;
+}
+
+uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval)
+{
+ QemuOpt *opt = qemu_opt_find(opts, name);
+
+ if (opt == NULL)
+ return defval;
+ assert(opt->desc && opt->desc->type == QEMU_OPT_NUMBER);
+ return opt->value.uint;
+}
+
+uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval)
+{
+ QemuOpt *opt = qemu_opt_find(opts, name);
+
+ if (opt == NULL)
+ return defval;
+ assert(opt->desc && opt->desc->type == QEMU_OPT_SIZE);
+ return opt->value.uint;
+}
+
+static int qemu_opt_parse(QemuOpt *opt)
+{
+ if (opt->desc == NULL)
+ return 0;
+ switch (opt->desc->type) {
+ case QEMU_OPT_STRING:
+ /* nothing */
+ return 0;
+ case QEMU_OPT_BOOL:
+ return parse_option_bool(opt->name, opt->str, &opt->value.boolean);
+ case QEMU_OPT_NUMBER:
+ return parse_option_number(opt->name, opt->str, &opt->value.uint);
+ case QEMU_OPT_SIZE:
+ return parse_option_size(opt->name, opt->str, &opt->value.uint);
+ default:
+ abort();
+ }
+}
+
+static void qemu_opt_del(QemuOpt *opt)
+{
+ QTAILQ_REMOVE(&opt->opts->head, opt, next);
+ qemu_free((/* !const */ char*)opt->name);
+ qemu_free((/* !const */ char*)opt->str);
+ qemu_free(opt);
+}
+
+int qemu_opt_set(QemuOpts *opts, const char *name, const char *value)
+{
+ QemuOpt *opt;
+ const QemuOptDesc *desc = opts->list->desc;
+ int i;
+
+ for (i = 0; desc[i].name != NULL; i++) {
+ if (strcmp(desc[i].name, name) == 0) {
+ break;
+ }
+ }
+ if (desc[i].name == NULL) {
+ if (i == 0) {
+ /* empty list -> allow any */;
+ } else {
+ qerror_report(QERR_INVALID_PARAMETER, name);
+ return -1;
+ }
+ }
+
+ opt = qemu_mallocz(sizeof(*opt));
+ opt->name = qemu_strdup(name);
+ opt->opts = opts;
+ QTAILQ_INSERT_TAIL(&opts->head, opt, next);
+ if (desc[i].name != NULL) {
+ opt->desc = desc+i;
+ }
+ if (value) {
+ opt->str = qemu_strdup(value);
+ }
+ if (qemu_opt_parse(opt) < 0) {
+ qemu_opt_del(opt);
+ return -1;
+ }
+ return 0;
+}
+
+int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
+ int abort_on_failure)
+{
+ QemuOpt *opt;
+ int rc = 0;
+
+ QTAILQ_FOREACH(opt, &opts->head, next) {
+ rc = func(opt->name, opt->str, opaque);
+ if (abort_on_failure && rc != 0)
+ break;
+ }
+ return rc;
+}
+
+QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id)
+{
+ QemuOpts *opts;
+
+ QTAILQ_FOREACH(opts, &list->head, next) {
+ if (!opts->id) {
+ continue;
+ }
+ if (strcmp(opts->id, id) != 0) {
+ continue;
+ }
+ return opts;
+ }
+ return NULL;
+}
+
+static int id_wellformed(const char *id)
+{
+ int i;
+
+ if (!qemu_isalpha(id[0])) {
+ return 0;
+ }
+ for (i = 1; id[i]; i++) {
+ if (!qemu_isalnum(id[i]) && !strchr("-._", id[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists)
+{
+ QemuOpts *opts = NULL;
+
+ if (id) {
+ if (!id_wellformed(id)) {
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "id", "an identifier");
+ error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n");
+ return NULL;
+ }
+ opts = qemu_opts_find(list, id);
+ if (opts != NULL) {
+ if (fail_if_exists) {
+ qerror_report(QERR_DUPLICATE_ID, id, list->name);
+ return NULL;
+ } else {
+ return opts;
+ }
+ }
+ }
+ opts = qemu_mallocz(sizeof(*opts));
+ if (id) {
+ opts->id = qemu_strdup(id);
+ }
+ opts->list = list;
+ loc_save(&opts->loc);
+ QTAILQ_INIT(&opts->head);
+ QTAILQ_INSERT_TAIL(&list->head, opts, next);
+ return opts;
+}
+
+void qemu_opts_reset(QemuOptsList *list)
+{
+ QemuOpts *opts, *next_opts;
+
+ QTAILQ_FOREACH_SAFE(opts, &list->head, next, next_opts) {
+ qemu_opts_del(opts);
+ }
+}
+
+void qemu_opts_loc_restore(QemuOpts *opts)
+{
+ loc_restore(&opts->loc);
+}
+
+int qemu_opts_set(QemuOptsList *list, const char *id,
+ const char *name, const char *value)
+{
+ QemuOpts *opts;
+
+ opts = qemu_opts_create(list, id, 1);
+ if (opts == NULL) {
+ return -1;
+ }
+ return qemu_opt_set(opts, name, value);
+}
+
+const char *qemu_opts_id(QemuOpts *opts)
+{
+ return opts->id;
+}
+
+void qemu_opts_del(QemuOpts *opts)
+{
+ QemuOpt *opt;
+
+ for (;;) {
+ opt = QTAILQ_FIRST(&opts->head);
+ if (opt == NULL)
+ break;
+ qemu_opt_del(opt);
+ }
+ QTAILQ_REMOVE(&opts->list->head, opts, next);
+ qemu_free(opts->id);
+ qemu_free(opts);
+}
+
+int qemu_opts_print(QemuOpts *opts, void *dummy)
+{
+ QemuOpt *opt;
+
+ fprintf(stderr, "%s: %s:", opts->list->name,
+ opts->id ? opts->id : "<noid>");
+ QTAILQ_FOREACH(opt, &opts->head, next) {
+ fprintf(stderr, " %s=\"%s\"", opt->name, opt->str);
+ }
+ fprintf(stderr, "\n");
+ return 0;
+}
+
+int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname)
+{
+ char option[128], value[1024];
+ const char *p,*pe,*pc;
+
+ for (p = params; *p != '\0'; p++) {
+ pe = strchr(p, '=');
+ pc = strchr(p, ',');
+ if (!pe || (pc && pc < pe)) {
+ /* found "foo,more" */
+ if (p == params && firstname) {
+ /* implicitly named first option */
+ pstrcpy(option, sizeof(option), firstname);
+ p = get_opt_value(value, sizeof(value), p);
+ } else {
+ /* option without value, probably a flag */
+ p = get_opt_name(option, sizeof(option), p, ',');
+ if (strncmp(option, "no", 2) == 0) {
+ memmove(option, option+2, strlen(option+2)+1);
+ pstrcpy(value, sizeof(value), "off");
+ } else {
+ pstrcpy(value, sizeof(value), "on");
+ }
+ }
+ } else {
+ /* found "foo=bar,more" */
+ p = get_opt_name(option, sizeof(option), p, '=');
+ if (*p != '=') {
+ break;
+ }
+ p++;
+ p = get_opt_value(value, sizeof(value), p);
+ }
+ if (strcmp(option, "id") != 0) {
+ /* store and parse */
+ if (qemu_opt_set(opts, option, value) == -1) {
+ return -1;
+ }
+ }
+ if (*p != ',') {
+ break;
+ }
+ }
+ return 0;
+}
+
+QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
+ int permit_abbrev)
+{
+ const char *firstname;
+ char value[1024], *id = NULL;
+ const char *p;
+ QemuOpts *opts;
+
+ assert(!permit_abbrev || list->implied_opt_name);
+ firstname = permit_abbrev ? list->implied_opt_name : NULL;
+
+ if (strncmp(params, "id=", 3) == 0) {
+ get_opt_value(value, sizeof(value), params+3);
+ id = value;
+ } else if ((p = strstr(params, ",id=")) != NULL) {
+ get_opt_value(value, sizeof(value), p+4);
+ id = value;
+ }
+ opts = qemu_opts_create(list, id, 1);
+ if (opts == NULL)
+ return NULL;
+
+ if (qemu_opts_do_parse(opts, params, firstname) != 0) {
+ qemu_opts_del(opts);
+ return NULL;
+ }
+
+ return opts;
+}
+
+static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
+{
+ char buf[32];
+ const char *value;
+ int n;
+
+ if (!strcmp(key, "id")) {
+ return;
+ }
+
+ switch (qobject_type(obj)) {
+ case QTYPE_QSTRING:
+ value = qstring_get_str(qobject_to_qstring(obj));
+ break;
+ case QTYPE_QINT:
+ n = snprintf(buf, sizeof(buf), "%" PRId64,
+ qint_get_int(qobject_to_qint(obj)));
+ assert(n < sizeof(buf));
+ value = buf;
+ break;
+ case QTYPE_QFLOAT:
+ n = snprintf(buf, sizeof(buf), "%.17g",
+ qfloat_get_double(qobject_to_qfloat(obj)));
+ assert(n < sizeof(buf));
+ value = buf;
+ break;
+ case QTYPE_QBOOL:
+ pstrcpy(buf, sizeof(buf),
+ qbool_get_int(qobject_to_qbool(obj)) ? "on" : "off");
+ value = buf;
+ break;
+ default:
+ return;
+ }
+ qemu_opt_set(opaque, key, value);
+}
+
+/*
+ * Create QemuOpts from a QDict.
+ * Use value of key "id" as ID if it exists and is a QString.
+ * Only QStrings, QInts, QFloats and QBools are copied. Entries with
+ * other types are silently ignored.
+ */
+QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict)
+{
+ QemuOpts *opts;
+
+ opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1);
+ if (opts == NULL)
+ return NULL;
+
+ qdict_iter(qdict, qemu_opts_from_qdict_1, opts);
+ return opts;
+}
+
+/*
+ * Convert from QemuOpts to QDict.
+ * The QDict values are of type QString.
+ * TODO We'll want to use types appropriate for opt->desc->type, but
+ * this is enough for now.
+ */
+QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict)
+{
+ QemuOpt *opt;
+ QObject *val;
+
+ if (!qdict) {
+ qdict = qdict_new();
+ }
+ if (opts->id) {
+ qdict_put(qdict, "id", qstring_from_str(opts->id));
+ }
+ QTAILQ_FOREACH(opt, &opts->head, next) {
+ val = QOBJECT(qstring_from_str(opt->str));
+ qdict_put_obj(qdict, opt->name, val);
+ }
+ return qdict;
+}
+
+/* Validate parsed opts against descriptions where no
+ * descriptions were provided in the QemuOptsList.
+ */
+int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc)
+{
+ QemuOpt *opt;
+
+ assert(opts->list->desc[0].name == NULL);
+
+ QTAILQ_FOREACH(opt, &opts->head, next) {
+ int i;
+
+ for (i = 0; desc[i].name != NULL; i++) {
+ if (strcmp(desc[i].name, opt->name) == 0) {
+ break;
+ }
+ }
+ if (desc[i].name == NULL) {
+ qerror_report(QERR_INVALID_PARAMETER, opt->name);
+ return -1;
+ }
+
+ opt->desc = &desc[i];
+
+ if (qemu_opt_parse(opt) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque,
+ int abort_on_failure)
+{
+ Location loc;
+ QemuOpts *opts;
+ int rc = 0;
+
+ loc_push_none(&loc);
+ QTAILQ_FOREACH(opts, &list->head, next) {
+ loc_restore(&opts->loc);
+ rc |= func(opts, opaque);
+ if (abort_on_failure && rc != 0)
+ break;
+ }
+ loc_pop(&loc);
+ return rc;
+}
diff --git a/qemu-option.h b/qemu-option.h
new file mode 100644
index 0000000..b515813
--- /dev/null
+++ b/qemu-option.h
@@ -0,0 +1,135 @@
+/*
+ * Commandline option parsing functions
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2009 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_OPTIONS_H
+#define QEMU_OPTIONS_H
+
+#include <stdint.h>
+#include "qemu-queue.h"
+#include "qdict.h"
+
+enum QEMUOptionParType {
+ OPT_FLAG,
+ OPT_NUMBER,
+ OPT_SIZE,
+ OPT_STRING,
+};
+
+typedef struct QEMUOptionParameter {
+ const char *name;
+ enum QEMUOptionParType type;
+ union {
+ uint64_t n;
+ char* s;
+ } value;
+ const char *help;
+} QEMUOptionParameter;
+
+
+const char *get_opt_name(char *buf, int buf_size, const char *p, char delim);
+const char *get_opt_value(char *buf, int buf_size, const char *p);
+int get_next_param_value(char *buf, int buf_size,
+ const char *tag, const char **pstr);
+int get_param_value(char *buf, int buf_size,
+ const char *tag, const char *str);
+int check_params(char *buf, int buf_size,
+ const char * const *params, const char *str);
+
+
+/*
+ * The following functions take a parameter list as input. This is a pointer to
+ * the first element of a QEMUOptionParameter array which is terminated by an
+ * entry with entry->name == NULL.
+ */
+
+QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list,
+ const char *name);
+int set_option_parameter(QEMUOptionParameter *list, const char *name,
+ const char *value);
+int set_option_parameter_int(QEMUOptionParameter *list, const char *name,
+ uint64_t value);
+QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest,
+ QEMUOptionParameter *list);
+QEMUOptionParameter *parse_option_parameters(const char *param,
+ QEMUOptionParameter *list, QEMUOptionParameter *dest);
+void free_option_parameters(QEMUOptionParameter *list);
+void print_option_parameters(QEMUOptionParameter *list);
+void print_option_help(QEMUOptionParameter *list);
+
+/* ------------------------------------------------------------------ */
+
+typedef struct QemuOpt QemuOpt;
+typedef struct QemuOpts QemuOpts;
+typedef struct QemuOptsList QemuOptsList;
+
+enum QemuOptType {
+ QEMU_OPT_STRING = 0, /* no parsing (use string as-is) */
+ QEMU_OPT_BOOL, /* on/off */
+ QEMU_OPT_NUMBER, /* simple number */
+ QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */
+};
+
+typedef struct QemuOptDesc {
+ const char *name;
+ enum QemuOptType type;
+ const char *help;
+} QemuOptDesc;
+
+struct QemuOptsList {
+ const char *name;
+ const char *implied_opt_name;
+ QTAILQ_HEAD(, QemuOpts) head;
+ QemuOptDesc desc[];
+};
+
+const char *qemu_opt_get(QemuOpts *opts, const char *name);
+int qemu_opt_get_bool(QemuOpts *opts, const char *name, int defval);
+uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval);
+uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval);
+int qemu_opt_set(QemuOpts *opts, const char *name, const char *value);
+typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque);
+int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
+ int abort_on_failure);
+
+QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id);
+QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists);
+void qemu_opts_reset(QemuOptsList *list);
+void qemu_opts_loc_restore(QemuOpts *opts);
+int qemu_opts_set(QemuOptsList *list, const char *id,
+ const char *name, const char *value);
+const char *qemu_opts_id(QemuOpts *opts);
+void qemu_opts_del(QemuOpts *opts);
+int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc);
+int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname);
+QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, int permit_abbrev);
+QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict);
+QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict);
+
+typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque);
+int qemu_opts_print(QemuOpts *opts, void *dummy);
+int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque,
+ int abort_on_failure);
+
+#endif
diff --git a/qemu-options.h b/qemu-options.h
new file mode 100644
index 0000000..c96f994
--- /dev/null
+++ b/qemu-options.h
@@ -0,0 +1,41 @@
+/*
+ * qemu-options.h
+ *
+ * Defines needed for command line argument processing.
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef _QEMU_OPTIONS_H_
+#define _QEMU_OPTIONS_H_
+
+enum {
+#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
+ opt_enum,
+#define DEFHEADING(text)
+#include "qemu-options.def"
+#undef DEF
+#undef DEFHEADING
+#undef GEN_DOCS
+};
+
+#endif
diff --git a/qemu-options.hx b/qemu-options.hx
new file mode 100644
index 0000000..328a1f9
--- /dev/null
+++ b/qemu-options.hx
@@ -0,0 +1,2372 @@
+HXCOMM Use DEFHEADING() to define headings in both help text and texi
+HXCOMM Text between STEXI and ETEXI are copied to texi version and
+HXCOMM discarded from C version
+HXCOMM DEF(option, HAS_ARG/0, opt_enum, opt_help, arch_mask) is used to
+HXCOMM construct option structures, enums and help message for specified
+HXCOMM architectures.
+HXCOMM HXCOMM can be used for comments, discarded from both texi and C
+
+DEFHEADING(Standard options:)
+STEXI
+@table @option
+ETEXI
+
+DEF("help", 0, QEMU_OPTION_h,
+ "-h or -help display this help and exit\n", QEMU_ARCH_ALL)
+STEXI
+@item -h
+@findex -h
+Display help and exit
+ETEXI
+
+DEF("version", 0, QEMU_OPTION_version,
+ "-version display version information and exit\n", QEMU_ARCH_ALL)
+STEXI
+@item -version
+@findex -version
+Display version information and exit
+ETEXI
+
+DEF("M", HAS_ARG, QEMU_OPTION_M,
+ "-M machine select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL)
+STEXI
+@item -M @var{machine}
+@findex -M
+Select the emulated @var{machine} (@code{-M ?} for list)
+ETEXI
+
+DEF("cpu", HAS_ARG, QEMU_OPTION_cpu,
+ "-cpu cpu select CPU (-cpu ? for list)\n", QEMU_ARCH_ALL)
+STEXI
+@item -cpu @var{model}
+@findex -cpu
+Select CPU model (-cpu ? for list and additional feature selection)
+ETEXI
+
+DEF("smp", HAS_ARG, QEMU_OPTION_smp,
+ "-smp n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]\n"
+ " set the number of CPUs to 'n' [default=1]\n"
+ " maxcpus= maximum number of total cpus, including\n"
+ " offline CPUs for hotplug, etc\n"
+ " cores= number of CPU cores on one socket\n"
+ " threads= number of threads on one CPU core\n"
+ " sockets= number of discrete sockets in the system\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -smp @var{n}[,cores=@var{cores}][,threads=@var{threads}][,sockets=@var{sockets}][,maxcpus=@var{maxcpus}]
+@findex -smp
+Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255
+CPUs are supported. On Sparc32 target, Linux limits the number of usable CPUs
+to 4.
+For the PC target, the number of @var{cores} per socket, the number
+of @var{threads} per cores and the total number of @var{sockets} can be
+specified. Missing values will be computed. If any on the three values is
+given, the total number of CPUs @var{n} can be omitted. @var{maxcpus}
+specifies the maximum number of hotpluggable CPUs.
+ETEXI
+
+DEF("numa", HAS_ARG, QEMU_OPTION_numa,
+ "-numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node]\n", QEMU_ARCH_ALL)
+STEXI
+@item -numa @var{opts}
+@findex -numa
+Simulate a multi node NUMA system. If mem and cpus are omitted, resources
+are split equally.
+ETEXI
+
+DEF("fda", HAS_ARG, QEMU_OPTION_fda,
+ "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL)
+DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL)
+STEXI
+@item -fda @var{file}
+@item -fdb @var{file}
+@findex -fda
+@findex -fdb
+Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can
+use the host floppy by using @file{/dev/fd0} as filename (@pxref{host_drives}).
+ETEXI
+
+DEF("hda", HAS_ARG, QEMU_OPTION_hda,
+ "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n", QEMU_ARCH_ALL)
+DEF("hdb", HAS_ARG, QEMU_OPTION_hdb, "", QEMU_ARCH_ALL)
+DEF("hdc", HAS_ARG, QEMU_OPTION_hdc,
+ "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n", QEMU_ARCH_ALL)
+DEF("hdd", HAS_ARG, QEMU_OPTION_hdd, "", QEMU_ARCH_ALL)
+STEXI
+@item -hda @var{file}
+@item -hdb @var{file}
+@item -hdc @var{file}
+@item -hdd @var{file}
+@findex -hda
+@findex -hdb
+@findex -hdc
+@findex -hdd
+Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}).
+ETEXI
+
+DEF("cdrom", HAS_ARG, QEMU_OPTION_cdrom,
+ "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -cdrom @var{file}
+@findex -cdrom
+Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and
+@option{-cdrom} at the same time). You can use the host CD-ROM by
+using @file{/dev/cdrom} as filename (@pxref{host_drives}).
+ETEXI
+
+DEF("drive", HAS_ARG, QEMU_OPTION_drive,
+ "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
+ " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n"
+ " [,cache=writethrough|writeback|none|unsafe][,format=f]\n"
+ " [,serial=s][,addr=A][,id=name][,aio=threads|native]\n"
+ " [,readonly=on|off][,boot=on|off]\n"
+ " use 'file' as a drive image\n", QEMU_ARCH_ALL)
+STEXI
+@item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
+@findex -drive
+
+Define a new drive. Valid options are:
+
+@table @option
+@item file=@var{file}
+This option defines which disk image (@pxref{disk_images}) to use with
+this drive. If the filename contains comma, you must double it
+(for instance, "file=my,,file" to use file "my,file").
+@item if=@var{interface}
+This option defines on which type on interface the drive is connected.
+Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio.
+@item bus=@var{bus},unit=@var{unit}
+These options define where is connected the drive by defining the bus number and
+the unit id.
+@item index=@var{index}
+This option defines where is connected the drive by using an index in the list
+of available connectors of a given interface type.
+@item media=@var{media}
+This option defines the type of the media: disk or cdrom.
+@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
+These options have the same definition as they have in @option{-hdachs}.
+@item snapshot=@var{snapshot}
+@var{snapshot} is "on" or "off" and allows to enable snapshot for given drive (see @option{-snapshot}).
+@item cache=@var{cache}
+@var{cache} is "none", "writeback", "unsafe", or "writethrough" and controls how the host cache is used to access block data.
+@item aio=@var{aio}
+@var{aio} is "threads", or "native" and selects between pthread based disk I/O and native Linux AIO.
+@item format=@var{format}
+Specify which disk @var{format} will be used rather than detecting
+the format. Can be used to specifiy format=raw to avoid interpreting
+an untrusted format header.
+@item serial=@var{serial}
+This option specifies the serial number to assign to the device.
+@item addr=@var{addr}
+Specify the controller's PCI address (if=virtio only).
+@item boot=@var{boot}
+@var{boot} is "on" or "off" and allows for booting from non-traditional interfaces, such as virtio.
+@end table
+
+By default, writethrough caching is used for all block device. This means that
+the host page cache will be used to read and write data but write notification
+will be sent to the guest only when the data has been reported as written by
+the storage subsystem.
+
+Writeback caching will report data writes as completed as soon as the data is
+present in the host page cache. This is safe as long as you trust your host.
+If your host crashes or loses power, then the guest may experience data
+corruption.
+
+The host page cache can be avoided entirely with @option{cache=none}. This will
+attempt to do disk IO directly to the guests memory. QEMU may still perform
+an internal copy of the data.
+
+Some block drivers perform badly with @option{cache=writethrough}, most notably,
+qcow2. If performance is more important than correctness,
+@option{cache=writeback} should be used with qcow2.
+
+In case you don't care about data integrity over host failures, use
+cache=unsafe. This option tells qemu that it never needs to write any data
+to the disk but can instead keeps things in cache. If anything goes wrong,
+like your host losing power, the disk storage getting disconnected accidently,
+etc. you're image will most probably be rendered unusable. When using
+the @option{-snapshot} option, unsafe caching is always used.
+
+Instead of @option{-cdrom} you can use:
+@example
+qemu -drive file=file,index=2,media=cdrom
+@end example
+
+Instead of @option{-hda}, @option{-hdb}, @option{-hdc}, @option{-hdd}, you can
+use:
+@example
+qemu -drive file=file,index=0,media=disk
+qemu -drive file=file,index=1,media=disk
+qemu -drive file=file,index=2,media=disk
+qemu -drive file=file,index=3,media=disk
+@end example
+
+You can connect a CDROM to the slave of ide0:
+@example
+qemu -drive file=file,if=ide,index=1,media=cdrom
+@end example
+
+If you don't specify the "file=" argument, you define an empty drive:
+@example
+qemu -drive if=ide,index=1,media=cdrom
+@end example
+
+You can connect a SCSI disk with unit ID 6 on the bus #0:
+@example
+qemu -drive file=file,if=scsi,bus=0,unit=6
+@end example
+
+Instead of @option{-fda}, @option{-fdb}, you can use:
+@example
+qemu -drive file=file,index=0,if=floppy
+qemu -drive file=file,index=1,if=floppy
+@end example
+
+By default, @var{interface} is "ide" and @var{index} is automatically
+incremented:
+@example
+qemu -drive file=a -drive file=b"
+@end example
+is interpreted like:
+@example
+qemu -hda a -hdb b
+@end example
+ETEXI
+
+DEF("set", HAS_ARG, QEMU_OPTION_set,
+ "-set group.id.arg=value\n"
+ " set <arg> parameter for item <id> of type <group>\n"
+ " i.e. -set drive.$id.file=/path/to/image\n", QEMU_ARCH_ALL)
+STEXI
+@item -set
+@findex -set
+TODO
+ETEXI
+
+DEF("global", HAS_ARG, QEMU_OPTION_global,
+ "-global driver.property=value\n"
+ " set a global default for a driver property\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -global
+@findex -global
+TODO
+ETEXI
+
+DEF("mtdblock", HAS_ARG, QEMU_OPTION_mtdblock,
+ "-mtdblock file use 'file' as on-board Flash memory image\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -mtdblock @var{file}
+@findex -mtdblock
+Use @var{file} as on-board Flash memory image.
+ETEXI
+
+DEF("sd", HAS_ARG, QEMU_OPTION_sd,
+ "-sd file use 'file' as SecureDigital card image\n", QEMU_ARCH_ALL)
+STEXI
+@item -sd @var{file}
+@findex -sd
+Use @var{file} as SecureDigital card image.
+ETEXI
+
+DEF("pflash", HAS_ARG, QEMU_OPTION_pflash,
+ "-pflash file use 'file' as a parallel flash image\n", QEMU_ARCH_ALL)
+STEXI
+@item -pflash @var{file}
+@findex -pflash
+Use @var{file} as a parallel flash image.
+ETEXI
+
+DEF("boot", HAS_ARG, QEMU_OPTION_boot,
+ "-boot [order=drives][,once=drives][,menu=on|off]\n"
+ " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off]
+@findex -boot
+Specify boot order @var{drives} as a string of drive letters. Valid
+drive letters depend on the target achitecture. The x86 PC uses: a, b
+(floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p (Etherboot
+from network adapter 1-4), hard disk boot is the default. To apply a
+particular boot order only on the first startup, specify it via
+@option{once}.
+
+Interactive boot menus/prompts can be enabled via @option{menu=on} as far
+as firmware/BIOS supports them. The default is non-interactive boot.
+
+@example
+# try to boot from network first, then from hard disk
+qemu -boot order=nc
+# boot from CD-ROM first, switch back to default order after reboot
+qemu -boot once=d
+@end example
+
+Note: The legacy format '-boot @var{drives}' is still supported but its
+use is discouraged as it may be removed from future versions.
+ETEXI
+
+DEF("snapshot", 0, QEMU_OPTION_snapshot,
+ "-snapshot write to temporary files instead of disk image files\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -snapshot
+@findex -snapshot
+Write to temporary files instead of disk image files. In this case,
+the raw disk image you use is not written back. You can however force
+the write back by pressing @key{C-a s} (@pxref{disk_images}).
+ETEXI
+
+DEF("m", HAS_ARG, QEMU_OPTION_m,
+ "-m megs set virtual RAM size to megs MB [default="
+ stringify(DEFAULT_RAM_SIZE) "]\n", QEMU_ARCH_ALL)
+STEXI
+@item -m @var{megs}
+@findex -m
+Set virtual RAM size to @var{megs} megabytes. Default is 128 MiB. Optionally,
+a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or
+gigabytes respectively.
+ETEXI
+
+DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath,
+ "-mem-path FILE provide backing storage for guest RAM\n", QEMU_ARCH_ALL)
+STEXI
+@item -mem-path @var{path}
+Allocate guest RAM from a temporarily created file in @var{path}.
+ETEXI
+
+#ifdef MAP_POPULATE
+DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc,
+ "-mem-prealloc preallocate guest memory (use with -mem-path)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -mem-prealloc
+Preallocate memory when using -mem-path.
+ETEXI
+#endif
+
+DEF("k", HAS_ARG, QEMU_OPTION_k,
+ "-k language use keyboard layout (for example 'fr' for French)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -k @var{language}
+@findex -k
+Use keyboard layout @var{language} (for example @code{fr} for
+French). This option is only needed where it is not easy to get raw PC
+keycodes (e.g. on Macs, with some X11 servers or with a VNC
+display). You don't normally need to use it on PC/Linux or PC/Windows
+hosts.
+
+The available layouts are:
+@example
+ar de-ch es fo fr-ca hu ja mk no pt-br sv
+da en-gb et fr fr-ch is lt nl pl ru th
+de en-us fi fr-be hr it lv nl-be pt sl tr
+@end example
+
+The default is @code{en-us}.
+ETEXI
+
+
+DEF("audio-help", 0, QEMU_OPTION_audio_help,
+ "-audio-help print list of audio drivers and their options\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -audio-help
+@findex -audio-help
+Will show the audio subsystem help: list of drivers, tunable
+parameters.
+ETEXI
+
+DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
+ "-soundhw c1,... enable audio support\n"
+ " and only specified sound cards (comma separated list)\n"
+ " use -soundhw ? to get the list of supported cards\n"
+ " use -soundhw all to enable all of them\n", QEMU_ARCH_ALL)
+STEXI
+@item -soundhw @var{card1}[,@var{card2},...] or -soundhw all
+@findex -soundhw
+Enable audio and selected sound hardware. Use ? to print all
+available sound hardware.
+
+@example
+qemu -soundhw sb16,adlib disk.img
+qemu -soundhw es1370 disk.img
+qemu -soundhw ac97 disk.img
+qemu -soundhw hda disk.img
+qemu -soundhw all disk.img
+qemu -soundhw ?
+@end example
+
+Note that Linux's i810_audio OSS kernel (for AC97) module might
+require manually specifying clocking.
+
+@example
+modprobe i810_audio clocking=48000
+@end example
+ETEXI
+
+STEXI
+@end table
+ETEXI
+
+DEF("usb", 0, QEMU_OPTION_usb,
+ "-usb enable the USB driver (will be the default soon)\n",
+ QEMU_ARCH_ALL)
+STEXI
+USB options:
+@table @option
+
+@item -usb
+@findex -usb
+Enable the USB driver (will be the default soon)
+ETEXI
+
+DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice,
+ "-usbdevice name add the host or guest USB device 'name'\n",
+ QEMU_ARCH_ALL)
+STEXI
+
+@item -usbdevice @var{devname}
+@findex -usbdevice
+Add the USB device @var{devname}. @xref{usb_devices}.
+
+@table @option
+
+@item mouse
+Virtual Mouse. This will override the PS/2 mouse emulation when activated.
+
+@item tablet
+Pointer device that uses absolute coordinates (like a touchscreen). This
+means qemu is able to report the mouse position without having to grab the
+mouse. Also overrides the PS/2 mouse emulation when activated.
+
+@item disk:[format=@var{format}]:@var{file}
+Mass storage device based on file. The optional @var{format} argument
+will be used rather than detecting the format. Can be used to specifiy
+@code{format=raw} to avoid interpreting an untrusted format header.
+
+@item host:@var{bus}.@var{addr}
+Pass through the host device identified by @var{bus}.@var{addr} (Linux only).
+
+@item host:@var{vendor_id}:@var{product_id}
+Pass through the host device identified by @var{vendor_id}:@var{product_id}
+(Linux only).
+
+@item serial:[vendorid=@var{vendor_id}][,productid=@var{product_id}]:@var{dev}
+Serial converter to host character device @var{dev}, see @code{-serial} for the
+available devices.
+
+@item braille
+Braille device. This will use BrlAPI to display the braille output on a real
+or fake device.
+
+@item net:@var{options}
+Network adapter that supports CDC ethernet and RNDIS protocols.
+
+@end table
+ETEXI
+
+DEF("device", HAS_ARG, QEMU_OPTION_device,
+ "-device driver[,prop[=value][,...]]\n"
+ " add device (based on driver)\n"
+ " prop=value,... sets driver properties\n"
+ " use -device ? to print all possible drivers\n"
+ " use -device driver,? to print all possible properties\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -device @var{driver}[,@var{prop}[=@var{value}][,...]]
+@findex -device
+Add device @var{driver}. @var{prop}=@var{value} sets driver
+properties. Valid properties depend on the driver. To get help on
+possible drivers and properties, use @code{-device ?} and
+@code{-device @var{driver},?}.
+ETEXI
+
+DEFHEADING(File system options:)
+
+DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
+ "-fsdev local,id=id,path=path,security_model=[mapped|passthrough|none]\n",
+ QEMU_ARCH_ALL)
+
+STEXI
+
+The general form of a File system device option is:
+@table @option
+
+@item -fsdev @var{fstype} ,id=@var{id} [,@var{options}]
+@findex -fsdev
+Fstype is one of:
+@option{local},
+The specific Fstype will determine the applicable options.
+
+Options to each backend are described below.
+
+@item -fsdev local ,id=@var{id} ,path=@var{path} ,security_model=@var{security_model}
+
+Create a file-system-"device" for local-filesystem.
+
+@option{local} is only available on Linux.
+
+@option{path} specifies the path to be exported. @option{path} is required.
+
+@option{security_model} specifies the security model to be followed.
+@option{security_model} is required.
+
+@end table
+ETEXI
+
+DEFHEADING(Virtual File system pass-through options:)
+
+DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
+ "-virtfs local,path=path,mount_tag=tag,security_model=[mapped|passthrough|none]\n",
+ QEMU_ARCH_ALL)
+
+STEXI
+
+The general form of a Virtual File system pass-through option is:
+@table @option
+
+@item -virtfs @var{fstype} [,@var{options}]
+@findex -virtfs
+Fstype is one of:
+@option{local},
+The specific Fstype will determine the applicable options.
+
+Options to each backend are described below.
+
+@item -virtfs local ,path=@var{path} ,mount_tag=@var{mount_tag} ,security_model=@var{security_model}
+
+Create a Virtual file-system-pass through for local-filesystem.
+
+@option{local} is only available on Linux.
+
+@option{path} specifies the path to be exported. @option{path} is required.
+
+@option{security_model} specifies the security model to be followed.
+@option{security_model} is required.
+
+
+@option{mount_tag} specifies the tag with which the exported file is mounted.
+@option{mount_tag} is required.
+
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEF("name", HAS_ARG, QEMU_OPTION_name,
+ "-name string1[,process=string2]\n"
+ " set the name of the guest\n"
+ " string1 sets the window title and string2 the process name (on Linux)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -name @var{name}
+@findex -name
+Sets the @var{name} of the guest.
+This name will be displayed in the SDL window caption.
+The @var{name} will also be used for the VNC server.
+Also optionally set the top visible process name in Linux.
+ETEXI
+
+DEF("uuid", HAS_ARG, QEMU_OPTION_uuid,
+ "-uuid %08x-%04x-%04x-%04x-%012x\n"
+ " specify machine UUID\n", QEMU_ARCH_ALL)
+STEXI
+@item -uuid @var{uuid}
+@findex -uuid
+Set system UUID.
+ETEXI
+
+STEXI
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEFHEADING(Display options:)
+
+STEXI
+@table @option
+ETEXI
+
+DEF("nographic", 0, QEMU_OPTION_nographic,
+ "-nographic disable graphical output and redirect serial I/Os to console\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -nographic
+@findex -nographic
+Normally, QEMU uses SDL to display the VGA output. With this option,
+you can totally disable graphical output so that QEMU is a simple
+command line application. The emulated serial port is redirected on
+the console. Therefore, you can still use QEMU to debug a Linux kernel
+with a serial console.
+ETEXI
+
+#ifdef CONFIG_CURSES
+DEF("curses", 0, QEMU_OPTION_curses,
+ "-curses use a curses/ncurses interface instead of SDL\n",
+ QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -curses
+@findex curses
+Normally, QEMU uses SDL to display the VGA output. With this option,
+QEMU can display the VGA output when in text mode using a
+curses/ncurses interface. Nothing is displayed in graphical mode.
+ETEXI
+
+#ifdef CONFIG_SDL
+DEF("no-frame", 0, QEMU_OPTION_no_frame,
+ "-no-frame open SDL window without a frame and window decorations\n",
+ QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -no-frame
+@findex -no-frame
+Do not use decorations for SDL windows and start them using the whole
+available screen space. This makes the using QEMU in a dedicated desktop
+workspace more convenient.
+ETEXI
+
+#ifdef CONFIG_SDL
+DEF("alt-grab", 0, QEMU_OPTION_alt_grab,
+ "-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n",
+ QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -alt-grab
+@findex -alt-grab
+Use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt).
+ETEXI
+
+#ifdef CONFIG_SDL
+DEF("ctrl-grab", 0, QEMU_OPTION_ctrl_grab,
+ "-ctrl-grab use Right-Ctrl to grab mouse (instead of Ctrl-Alt)\n",
+ QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -ctrl-grab
+@findex -ctrl-grab
+Use Right-Ctrl to grab mouse (instead of Ctrl-Alt).
+ETEXI
+
+#ifdef CONFIG_SDL
+DEF("no-quit", 0, QEMU_OPTION_no_quit,
+ "-no-quit disable SDL window close capability\n", QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -no-quit
+@findex -no-quit
+Disable SDL window close capability.
+ETEXI
+
+#ifdef CONFIG_SDL
+DEF("sdl", 0, QEMU_OPTION_sdl,
+ "-sdl enable SDL\n", QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -sdl
+@findex -sdl
+Enable SDL.
+ETEXI
+
+DEF("spice", HAS_ARG, QEMU_OPTION_spice,
+ "-spice <args> enable spice\n", QEMU_ARCH_ALL)
+STEXI
+@item -spice @var{option}[,@var{option}[,...]]
+@findex -spice
+Enable the spice remote desktop protocol. Valid options are
+
+@table @option
+
+@item port=<nr>
+Set the TCP port spice is listening on for plaintext channels.
+
+@item addr=<addr>
+Set the IP address spice is listening on. Default is any address.
+
+@item ipv4
+@item ipv6
+Force using the specified IP version.
+
+@item password=<secret>
+Set the password you need to authenticate.
+
+@item disable-ticketing
+Allow client connects without authentication.
+
+@item tls-port=<nr>
+Set the TCP port spice is listening on for encrypted channels.
+
+@item x509-dir=<dir>
+Set the x509 file directory. Expects same filenames as -vnc $display,x509=$dir
+
+@item x509-key-file=<file>
+@item x509-key-password=<file>
+@item x509-cert-file=<file>
+@item x509-cacert-file=<file>
+@item x509-dh-key-file=<file>
+The x509 file names can also be configured individually.
+
+@item tls-ciphers=<list>
+Specify which ciphers to use.
+
+@item tls-channel=[main|display|inputs|record|playback|tunnel]
+@item plaintext-channel=[main|display|inputs|record|playback|tunnel]
+Force specific channel to be used with or without TLS encryption. The
+options can be specified multiple times to configure multiple
+channels. The special name "default" can be used to set the default
+mode. For channels which are not explicitly forced into one mode the
+spice client is allowed to pick tls/plaintext as he pleases.
+
+@item image-compression=[auto_glz|auto_lz|quic|glz|lz|off]
+Configure image compression (lossless).
+Default is auto_glz.
+
+@item jpeg-wan-compression=[auto|never|always]
+@item zlib-glz-wan-compression=[auto|never|always]
+Configure wan image compression (lossy for slow links).
+Default is auto.
+
+@item streaming-video=[off|all|filter]
+Configure video stream detection. Default is filter.
+
+@item agent-mouse=[on|off]
+Enable/disable passing mouse events via vdagent. Default is on.
+
+@item playback-compression=[on|off]
+Enable/disable audio stream compression (using celt 0.5.1). Default is on.
+
+@end table
+ETEXI
+
+DEF("portrait", 0, QEMU_OPTION_portrait,
+ "-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -portrait
+@findex -portrait
+Rotate graphical output 90 deg left (only PXA LCD).
+ETEXI
+
+DEF("vga", HAS_ARG, QEMU_OPTION_vga,
+ "-vga [std|cirrus|vmware|qxl|xenfb|none]\n"
+ " select video card type\n", QEMU_ARCH_ALL)
+STEXI
+@item -vga @var{type}
+@findex -vga
+Select type of VGA card to emulate. Valid values for @var{type} are
+@table @option
+@item cirrus
+Cirrus Logic GD5446 Video card. All Windows versions starting from
+Windows 95 should recognize and use this graphic card. For optimal
+performances, use 16 bit color depth in the guest and the host OS.
+(This one is the default)
+@item std
+Standard VGA card with Bochs VBE extensions. If your guest OS
+supports the VESA 2.0 VBE extensions (e.g. Windows XP) and if you want
+to use high resolution modes (>= 1280x1024x16) then you should use
+this option.
+@item vmware
+VMWare SVGA-II compatible adapter. Use it if you have sufficiently
+recent XFree86/XOrg server or Windows guest with a driver for this
+card.
+@item qxl
+QXL paravirtual graphic card. It is VGA compatible (including VESA
+2.0 VBE support). Works best with qxl guest drivers installed though.
+Recommended choice when using the spice protocol.
+@item none
+Disable VGA card.
+@end table
+ETEXI
+
+DEF("full-screen", 0, QEMU_OPTION_full_screen,
+ "-full-screen start in full screen\n", QEMU_ARCH_ALL)
+STEXI
+@item -full-screen
+@findex -full-screen
+Start in full screen.
+ETEXI
+
+DEF("g", 1, QEMU_OPTION_g ,
+ "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n",
+ QEMU_ARCH_PPC | QEMU_ARCH_SPARC)
+STEXI
+@item -g @var{width}x@var{height}[x@var{depth}]
+@findex -g
+Set the initial graphical resolution and depth (PPC, SPARC only).
+ETEXI
+
+DEF("vnc", HAS_ARG, QEMU_OPTION_vnc ,
+ "-vnc display start a VNC server on display\n", QEMU_ARCH_ALL)
+STEXI
+@item -vnc @var{display}[,@var{option}[,@var{option}[,...]]]
+@findex -vnc
+Normally, QEMU uses SDL to display the VGA output. With this option,
+you can have QEMU listen on VNC display @var{display} and redirect the VGA
+display over the VNC session. It is very useful to enable the usb
+tablet device when using this option (option @option{-usbdevice
+tablet}). When using the VNC display, you must use the @option{-k}
+parameter to set the keyboard layout if you are not using en-us. Valid
+syntax for the @var{display} is
+
+@table @option
+
+@item @var{host}:@var{d}
+
+TCP connections will only be allowed from @var{host} on display @var{d}.
+By convention the TCP port is 5900+@var{d}. Optionally, @var{host} can
+be omitted in which case the server will accept connections from any host.
+
+@item unix:@var{path}
+
+Connections will be allowed over UNIX domain sockets where @var{path} is the
+location of a unix socket to listen for connections on.
+
+@item none
+
+VNC is initialized but not started. The monitor @code{change} command
+can be used to later start the VNC server.
+
+@end table
+
+Following the @var{display} value there may be one or more @var{option} flags
+separated by commas. Valid options are
+
+@table @option
+
+@item reverse
+
+Connect to a listening VNC client via a ``reverse'' connection. The
+client is specified by the @var{display}. For reverse network
+connections (@var{host}:@var{d},@code{reverse}), the @var{d} argument
+is a TCP port number, not a display number.
+
+@item password
+
+Require that password based authentication is used for client connections.
+The password must be set separately using the @code{change} command in the
+@ref{pcsys_monitor}
+
+@item tls
+
+Require that client use TLS when communicating with the VNC server. This
+uses anonymous TLS credentials so is susceptible to a man-in-the-middle
+attack. It is recommended that this option be combined with either the
+@option{x509} or @option{x509verify} options.
+
+@item x509=@var{/path/to/certificate/dir}
+
+Valid if @option{tls} is specified. Require that x509 credentials are used
+for negotiating the TLS session. The server will send its x509 certificate
+to the client. It is recommended that a password be set on the VNC server
+to provide authentication of the client when this is used. The path following
+this option specifies where the x509 certificates are to be loaded from.
+See the @ref{vnc_security} section for details on generating certificates.
+
+@item x509verify=@var{/path/to/certificate/dir}
+
+Valid if @option{tls} is specified. Require that x509 credentials are used
+for negotiating the TLS session. The server will send its x509 certificate
+to the client, and request that the client send its own x509 certificate.
+The server will validate the client's certificate against the CA certificate,
+and reject clients when validation fails. If the certificate authority is
+trusted, this is a sufficient authentication mechanism. You may still wish
+to set a password on the VNC server as a second authentication layer. The
+path following this option specifies where the x509 certificates are to
+be loaded from. See the @ref{vnc_security} section for details on generating
+certificates.
+
+@item sasl
+
+Require that the client use SASL to authenticate with the VNC server.
+The exact choice of authentication method used is controlled from the
+system / user's SASL configuration file for the 'qemu' service. This
+is typically found in /etc/sasl2/qemu.conf. If running QEMU as an
+unprivileged user, an environment variable SASL_CONF_PATH can be used
+to make it search alternate locations for the service config.
+While some SASL auth methods can also provide data encryption (eg GSSAPI),
+it is recommended that SASL always be combined with the 'tls' and
+'x509' settings to enable use of SSL and server certificates. This
+ensures a data encryption preventing compromise of authentication
+credentials. See the @ref{vnc_security} section for details on using
+SASL authentication.
+
+@item acl
+
+Turn on access control lists for checking of the x509 client certificate
+and SASL party. For x509 certs, the ACL check is made against the
+certificate's distinguished name. This is something that looks like
+@code{C=GB,O=ACME,L=Boston,CN=bob}. For SASL party, the ACL check is
+made against the username, which depending on the SASL plugin, may
+include a realm component, eg @code{bob} or @code{bob@@EXAMPLE.COM}.
+When the @option{acl} flag is set, the initial access list will be
+empty, with a @code{deny} policy. Thus no one will be allowed to
+use the VNC server until the ACLs have been loaded. This can be
+achieved using the @code{acl} monitor command.
+
+@item lossy
+
+Enable lossy compression methods (gradient, JPEG, ...). If this
+option is set, VNC client may receive lossy framebuffer updates
+depending on its encoding settings. Enabling this option can save
+a lot of bandwidth at the expense of quality.
+
+@end table
+ETEXI
+
+STEXI
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEFHEADING(i386 target only:)
+STEXI
+@table @option
+ETEXI
+
+DEF("win2k-hack", 0, QEMU_OPTION_win2k_hack,
+ "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n",
+ QEMU_ARCH_I386)
+STEXI
+@item -win2k-hack
+@findex -win2k-hack
+Use it when installing Windows 2000 to avoid a disk full bug. After
+Windows 2000 is installed, you no longer need this option (this option
+slows down the IDE transfers).
+ETEXI
+
+HXCOMM Deprecated by -rtc
+DEF("rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack, "", QEMU_ARCH_I386)
+
+DEF("no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk,
+ "-no-fd-bootchk disable boot signature checking for floppy disks\n",
+ QEMU_ARCH_I386)
+STEXI
+@item -no-fd-bootchk
+@findex -no-fd-bootchk
+Disable boot signature checking for floppy disks in Bochs BIOS. It may
+be needed to boot from old floppy disks.
+TODO: check reference to Bochs BIOS.
+ETEXI
+
+DEF("no-acpi", 0, QEMU_OPTION_no_acpi,
+ "-no-acpi disable ACPI\n", QEMU_ARCH_I386)
+STEXI
+@item -no-acpi
+@findex -no-acpi
+Disable ACPI (Advanced Configuration and Power Interface) support. Use
+it if your guest OS complains about ACPI problems (PC target machine
+only).
+ETEXI
+
+DEF("no-hpet", 0, QEMU_OPTION_no_hpet,
+ "-no-hpet disable HPET\n", QEMU_ARCH_I386)
+STEXI
+@item -no-hpet
+@findex -no-hpet
+Disable HPET support.
+ETEXI
+
+DEF("balloon", HAS_ARG, QEMU_OPTION_balloon,
+ "-balloon none disable balloon device\n"
+ "-balloon virtio[,addr=str]\n"
+ " enable virtio balloon device (default)\n", QEMU_ARCH_ALL)
+STEXI
+@item -balloon none
+@findex -balloon
+Disable balloon device.
+@item -balloon virtio[,addr=@var{addr}]
+Enable virtio balloon device (default), optionally with PCI address
+@var{addr}.
+ETEXI
+
+DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable,
+ "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,data=file1[:file2]...]\n"
+ " ACPI table description\n", QEMU_ARCH_I386)
+STEXI
+@item -acpitable [sig=@var{str}][,rev=@var{n}][,oem_id=@var{str}][,oem_table_id=@var{str}][,oem_rev=@var{n}] [,asl_compiler_id=@var{str}][,asl_compiler_rev=@var{n}][,data=@var{file1}[:@var{file2}]...]
+@findex -acpitable
+Add ACPI table with specified header fields and context from specified files.
+ETEXI
+
+DEF("smbios", HAS_ARG, QEMU_OPTION_smbios,
+ "-smbios file=binary\n"
+ " load SMBIOS entry from binary file\n"
+ "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]\n"
+ " specify SMBIOS type 0 fields\n"
+ "-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]\n"
+ " [,uuid=uuid][,sku=str][,family=str]\n"
+ " specify SMBIOS type 1 fields\n", QEMU_ARCH_I386)
+STEXI
+@item -smbios file=@var{binary}
+@findex -smbios
+Load SMBIOS entry from binary file.
+
+@item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}]
+@findex -smbios
+Specify SMBIOS type 0 fields
+
+@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}] [,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}] [,family=@var{str}]
+Specify SMBIOS type 1 fields
+ETEXI
+
+DEFHEADING()
+STEXI
+@end table
+ETEXI
+
+DEFHEADING(Network options:)
+STEXI
+@table @option
+ETEXI
+
+HXCOMM Legacy slirp options (now moved to -net user):
+#ifdef CONFIG_SLIRP
+DEF("tftp", HAS_ARG, QEMU_OPTION_tftp, "", QEMU_ARCH_ALL)
+DEF("bootp", HAS_ARG, QEMU_OPTION_bootp, "", QEMU_ARCH_ALL)
+DEF("redir", HAS_ARG, QEMU_OPTION_redir, "", QEMU_ARCH_ALL)
+#ifndef _WIN32
+DEF("smb", HAS_ARG, QEMU_OPTION_smb, "", QEMU_ARCH_ALL)
+#endif
+#endif
+
+DEF("net", HAS_ARG, QEMU_OPTION_net,
+ "-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n"
+ " create a new Network Interface Card and connect it to VLAN 'n'\n"
+#ifdef CONFIG_SLIRP
+ "-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n]\n"
+ " [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f]\n"
+ " [,hostfwd=rule][,guestfwd=rule]"
+#ifndef _WIN32
+ "[,smb=dir[,smbserver=addr]]\n"
+#endif
+ " connect the user mode network stack to VLAN 'n', configure its\n"
+ " DHCP server and enabled optional services\n"
+#endif
+#ifdef _WIN32
+ "-net tap[,vlan=n][,name=str],ifname=name\n"
+ " connect the host TAP network interface to VLAN 'n'\n"
+#else
+ "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]\n"
+ " connect the host TAP network interface to VLAN 'n' and use the\n"
+ " network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n"
+ " and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n"
+ " use '[down]script=no' to disable script execution\n"
+ " use 'fd=h' to connect to an already opened TAP interface\n"
+ " use 'sndbuf=nbytes' to limit the size of the send buffer (the\n"
+ " default is disabled 'sndbuf=0' to enable flow control set 'sndbuf=1048576')\n"
+ " use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag\n"
+ " use vnet_hdr=on to make the lack of IFF_VNET_HDR support an error condition\n"
+ " use vhost=on to enable experimental in kernel accelerator\n"
+ " (only has effect for virtio guests which use MSIX)\n"
+ " use vhostforce=on to force vhost on for non-MSIX virtio guests\n"
+ " use 'vhostfd=h' to connect to an already opened vhost net device\n"
+#endif
+ "-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n"
+ " connect the vlan 'n' to another VLAN using a socket connection\n"
+ "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port[,localaddr=addr]]\n"
+ " connect the vlan 'n' to multicast maddr and port\n"
+ " use 'localaddr=addr' to specify the host address to send packets from\n"
+#ifdef CONFIG_VDE
+ "-net vde[,vlan=n][,name=str][,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n"
+ " connect the vlan 'n' to port 'n' of a vde switch running\n"
+ " on host and listening for incoming connections on 'socketpath'.\n"
+ " Use group 'groupname' and mode 'octalmode' to change default\n"
+ " ownership and permissions for communication port.\n"
+#endif
+ "-net dump[,vlan=n][,file=f][,len=n]\n"
+ " dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\n"
+ "-net none use it alone to have zero network devices. If no -net option\n"
+ " is provided, the default is '-net nic -net user'\n", QEMU_ARCH_ALL)
+DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
+ "-netdev ["
+#ifdef CONFIG_SLIRP
+ "user|"
+#endif
+ "tap|"
+#ifdef CONFIG_VDE
+ "vde|"
+#endif
+ "socket],id=str[,option][,option][,...]\n", QEMU_ARCH_ALL)
+STEXI
+@item -net nic[,vlan=@var{n}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}]
+@findex -net
+Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n}
+= 0 is the default). The NIC is an e1000 by default on the PC
+target. Optionally, the MAC address can be changed to @var{mac}, the
+device address set to @var{addr} (PCI cards only),
+and a @var{name} can be assigned for use in monitor commands.
+Optionally, for PCI cards, you can specify the number @var{v} of MSI-X vectors
+that the card should have; this option currently only affects virtio cards; set
+@var{v} = 0 to disable MSI-X. If no @option{-net} option is specified, a single
+NIC is created. Qemu can emulate several different models of network card.
+Valid values for @var{type} are
+@code{virtio}, @code{i82551}, @code{i82557b}, @code{i82559er},
+@code{ne2k_pci}, @code{ne2k_isa}, @code{pcnet}, @code{rtl8139},
+@code{e1000}, @code{smc91c111}, @code{lance} and @code{mcf_fec}.
+Not all devices are supported on all targets. Use -net nic,model=?
+for a list of available devices for your target.
+
+@item -net user[,@var{option}][,@var{option}][,...]
+Use the user mode network stack which requires no administrator
+privilege to run. Valid options are:
+
+@table @option
+@item vlan=@var{n}
+Connect user mode stack to VLAN @var{n} (@var{n} = 0 is the default).
+
+@item name=@var{name}
+Assign symbolic name for use in monitor commands.
+
+@item net=@var{addr}[/@var{mask}]
+Set IP network address the guest will see. Optionally specify the netmask,
+either in the form a.b.c.d or as number of valid top-most bits. Default is
+10.0.2.0/8.
+
+@item host=@var{addr}
+Specify the guest-visible address of the host. Default is the 2nd IP in the
+guest network, i.e. x.x.x.2.
+
+@item restrict=y|yes|n|no
+If this options is enabled, the guest will be isolated, i.e. it will not be
+able to contact the host and no guest IP packets will be routed over the host
+to the outside. This option does not affect explicitly set forwarding rule.
+
+@item hostname=@var{name}
+Specifies the client hostname reported by the builtin DHCP server.
+
+@item dhcpstart=@var{addr}
+Specify the first of the 16 IPs the built-in DHCP server can assign. Default
+is the 16th to 31st IP in the guest network, i.e. x.x.x.16 to x.x.x.31.
+
+@item dns=@var{addr}
+Specify the guest-visible address of the virtual nameserver. The address must
+be different from the host address. Default is the 3rd IP in the guest network,
+i.e. x.x.x.3.
+
+@item tftp=@var{dir}
+When using the user mode network stack, activate a built-in TFTP
+server. The files in @var{dir} will be exposed as the root of a TFTP server.
+The TFTP client on the guest must be configured in binary mode (use the command
+@code{bin} of the Unix TFTP client).
+
+@item bootfile=@var{file}
+When using the user mode network stack, broadcast @var{file} as the BOOTP
+filename. In conjunction with @option{tftp}, this can be used to network boot
+a guest from a local directory.
+
+Example (using pxelinux):
+@example
+qemu -hda linux.img -boot n -net user,tftp=/path/to/tftp/files,bootfile=/pxelinux.0
+@end example
+
+@item smb=@var{dir}[,smbserver=@var{addr}]
+When using the user mode network stack, activate a built-in SMB
+server so that Windows OSes can access to the host files in @file{@var{dir}}
+transparently. The IP address of the SMB server can be set to @var{addr}. By
+default the 4th IP in the guest network is used, i.e. x.x.x.4.
+
+In the guest Windows OS, the line:
+@example
+10.0.2.4 smbserver
+@end example
+must be added in the file @file{C:\WINDOWS\LMHOSTS} (for windows 9x/Me)
+or @file{C:\WINNT\SYSTEM32\DRIVERS\ETC\LMHOSTS} (Windows NT/2000).
+
+Then @file{@var{dir}} can be accessed in @file{\\smbserver\qemu}.
+
+Note that a SAMBA server must be installed on the host OS in
+@file{/usr/sbin/smbd}. QEMU was tested successfully with smbd versions from
+Red Hat 9, Fedora Core 3 and OpenSUSE 11.x.
+
+@item hostfwd=[tcp|udp]:[@var{hostaddr}]:@var{hostport}-[@var{guestaddr}]:@var{guestport}
+Redirect incoming TCP or UDP connections to the host port @var{hostport} to
+the guest IP address @var{guestaddr} on guest port @var{guestport}. If
+@var{guestaddr} is not specified, its value is x.x.x.15 (default first address
+given by the built-in DHCP server). By specifying @var{hostaddr}, the rule can
+be bound to a specific host interface. If no connection type is set, TCP is
+used. This option can be given multiple times.
+
+For example, to redirect host X11 connection from screen 1 to guest
+screen 0, use the following:
+
+@example
+# on the host
+qemu -net user,hostfwd=tcp:127.0.0.1:6001-:6000 [...]
+# this host xterm should open in the guest X11 server
+xterm -display :1
+@end example
+
+To redirect telnet connections from host port 5555 to telnet port on
+the guest, use the following:
+
+@example
+# on the host
+qemu -net user,hostfwd=tcp::5555-:23 [...]
+telnet localhost 5555
+@end example
+
+Then when you use on the host @code{telnet localhost 5555}, you
+connect to the guest telnet server.
+
+@item guestfwd=[tcp]:@var{server}:@var{port}-@var{dev}
+Forward guest TCP connections to the IP address @var{server} on port @var{port}
+to the character device @var{dev}. This option can be given multiple times.
+
+@end table
+
+Note: Legacy stand-alone options -tftp, -bootp, -smb and -redir are still
+processed and applied to -net user. Mixing them with the new configuration
+syntax gives undefined results. Their use for new applications is discouraged
+as they will be removed from future versions.
+
+@item -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}] [,script=@var{file}][,downscript=@var{dfile}]
+Connect the host TAP network interface @var{name} to VLAN @var{n}, use
+the network script @var{file} to configure it and the network script
+@var{dfile} to deconfigure it. If @var{name} is not provided, the OS
+automatically provides one. @option{fd}=@var{h} can be used to specify
+the handle of an already opened host TAP interface. The default network
+configure script is @file{/etc/qemu-ifup} and the default network
+deconfigure script is @file{/etc/qemu-ifdown}. Use @option{script=no}
+or @option{downscript=no} to disable script execution. Example:
+
+@example
+qemu linux.img -net nic -net tap
+@end example
+
+More complicated example (two NICs, each one connected to a TAP device)
+@example
+qemu linux.img -net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \
+ -net nic,vlan=1 -net tap,vlan=1,ifname=tap1
+@end example
+
+@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}]
+
+Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual
+machine using a TCP socket connection. If @option{listen} is
+specified, QEMU waits for incoming connections on @var{port}
+(@var{host} is optional). @option{connect} is used to connect to
+another QEMU instance using the @option{listen} option. @option{fd}=@var{h}
+specifies an already opened TCP socket.
+
+Example:
+@example
+# launch a first QEMU instance
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,listen=:1234
+# connect the VLAN 0 of this instance to the VLAN 0
+# of the first instance
+qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \
+ -net socket,connect=127.0.0.1:1234
+@end example
+
+@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]]
+
+Create a VLAN @var{n} shared with another QEMU virtual
+machines using a UDP multicast socket, effectively making a bus for
+every QEMU with same multicast address @var{maddr} and @var{port}.
+NOTES:
+@enumerate
+@item
+Several QEMU can be running on different hosts and share same bus (assuming
+correct multicast setup for these hosts).
+@item
+mcast support is compatible with User Mode Linux (argument @option{eth@var{N}=mcast}), see
+@url{http://user-mode-linux.sf.net}.
+@item
+Use @option{fd=h} to specify an already opened UDP multicast socket.
+@end enumerate
+
+Example:
+@example
+# launch one QEMU instance
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,mcast=230.0.0.1:1234
+# launch another QEMU instance on same "bus"
+qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \
+ -net socket,mcast=230.0.0.1:1234
+# launch yet another QEMU instance on same "bus"
+qemu linux.img -net nic,macaddr=52:54:00:12:34:58 \
+ -net socket,mcast=230.0.0.1:1234
+@end example
+
+Example (User Mode Linux compat.):
+@example
+# launch QEMU instance (note mcast address selected
+# is UML's default)
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,mcast=239.192.168.1:1102
+# launch UML
+/path/to/linux ubd0=/path/to/root_fs eth0=mcast
+@end example
+
+Example (send packets from host's 1.2.3.4):
+@example
+qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \
+ -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4
+@end example
+
+@item -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}] [,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}]
+Connect VLAN @var{n} to PORT @var{n} of a vde switch running on host and
+listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname}
+and MODE @var{octalmode} to change default ownership and permissions for
+communication port. This option is available only if QEMU has been compiled
+with vde support enabled.
+
+Example:
+@example
+# launch vde switch
+vde_switch -F -sock /tmp/myswitch
+# launch QEMU instance
+qemu linux.img -net nic -net vde,sock=/tmp/myswitch
+@end example
+
+@item -net dump[,vlan=@var{n}][,file=@var{file}][,len=@var{len}]
+Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default).
+At most @var{len} bytes (64k by default) per packet are stored. The file format is
+libpcap, so it can be analyzed with tools such as tcpdump or Wireshark.
+
+@item -net none
+Indicate that no network devices should be configured. It is used to
+override the default configuration (@option{-net nic -net user}) which
+is activated if no @option{-net} options are provided.
+
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEFHEADING(Character device options:)
+
+DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
+ "-chardev null,id=id[,mux=on|off]\n"
+ "-chardev socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay]\n"
+ " [,server][,nowait][,telnet][,mux=on|off] (tcp)\n"
+ "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n"
+ "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
+ " [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
+ "-chardev msmouse,id=id[,mux=on|off]\n"
+ "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n"
+ " [,mux=on|off]\n"
+ "-chardev file,id=id,path=path[,mux=on|off]\n"
+ "-chardev pipe,id=id,path=path[,mux=on|off]\n"
+#ifdef _WIN32
+ "-chardev console,id=id[,mux=on|off]\n"
+ "-chardev serial,id=id,path=path[,mux=on|off]\n"
+#else
+ "-chardev pty,id=id[,mux=on|off]\n"
+ "-chardev stdio,id=id[,mux=on|off][,signal=on|off]\n"
+#endif
+#ifdef CONFIG_BRLAPI
+ "-chardev braille,id=id[,mux=on|off]\n"
+#endif
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+ "-chardev tty,id=id,path=path[,mux=on|off]\n"
+#endif
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
+ "-chardev parport,id=id,path=path[,mux=on|off]\n"
+#endif
+#if defined(CONFIG_SPICE)
+ "-chardev spicevmc,id=id,name=name[,debug=debug]\n"
+#endif
+ , QEMU_ARCH_ALL
+)
+
+STEXI
+
+The general form of a character device option is:
+@table @option
+
+@item -chardev @var{backend} ,id=@var{id} [,mux=on|off] [,@var{options}]
+@findex -chardev
+Backend is one of:
+@option{null},
+@option{socket},
+@option{udp},
+@option{msmouse},
+@option{vc},
+@option{file},
+@option{pipe},
+@option{console},
+@option{serial},
+@option{pty},
+@option{stdio},
+@option{braille},
+@option{tty},
+@option{parport},
+@option{spicevmc}.
+The specific backend will determine the applicable options.
+
+All devices must have an id, which can be any string up to 127 characters long.
+It is used to uniquely identify this device in other command line directives.
+
+A character device may be used in multiplexing mode by multiple front-ends.
+The key sequence of @key{Control-a} and @key{c} will rotate the input focus
+between attached front-ends. Specify @option{mux=on} to enable this mode.
+
+Options to each backend are described below.
+
+@item -chardev null ,id=@var{id}
+A void device. This device will not emit any data, and will drop any data it
+receives. The null backend does not take any options.
+
+@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet]
+
+Create a two-way stream socket, which can be either a TCP or a unix socket. A
+unix socket will be created if @option{path} is specified. Behaviour is
+undefined if TCP options are specified for a unix socket.
+
+@option{server} specifies that the socket shall be a listening socket.
+
+@option{nowait} specifies that QEMU should not block waiting for a client to
+connect to a listening socket.
+
+@option{telnet} specifies that traffic on the socket should interpret telnet
+escape sequences.
+
+TCP and unix socket options are given below:
+
+@table @option
+
+@item TCP options: port=@var{port} [,host=@var{host}] [,to=@var{to}] [,ipv4] [,ipv6] [,nodelay]
+
+@option{host} for a listening socket specifies the local address to be bound.
+For a connecting socket species the remote host to connect to. @option{host} is
+optional for listening sockets. If not specified it defaults to @code{0.0.0.0}.
+
+@option{port} for a listening socket specifies the local port to be bound. For a
+connecting socket specifies the port on the remote host to connect to.
+@option{port} can be given as either a port number or a service name.
+@option{port} is required.
+
+@option{to} is only relevant to listening sockets. If it is specified, and
+@option{port} cannot be bound, QEMU will attempt to bind to subsequent ports up
+to and including @option{to} until it succeeds. @option{to} must be specified
+as a port number.
+
+@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must be used.
+If neither is specified the socket may use either protocol.
+
+@option{nodelay} disables the Nagle algorithm.
+
+@item unix options: path=@var{path}
+
+@option{path} specifies the local path of the unix socket. @option{path} is
+required.
+
+@end table
+
+@item -chardev udp ,id=@var{id} [,host=@var{host}] ,port=@var{port} [,localaddr=@var{localaddr}] [,localport=@var{localport}] [,ipv4] [,ipv6]
+
+Sends all traffic from the guest to a remote host over UDP.
+
+@option{host} specifies the remote host to connect to. If not specified it
+defaults to @code{localhost}.
+
+@option{port} specifies the port on the remote host to connect to. @option{port}
+is required.
+
+@option{localaddr} specifies the local address to bind to. If not specified it
+defaults to @code{0.0.0.0}.
+
+@option{localport} specifies the local port to bind to. If not specified any
+available local port will be used.
+
+@option{ipv4} and @option{ipv6} specify that either IPv4 or IPv6 must be used.
+If neither is specified the device may use either protocol.
+
+@item -chardev msmouse ,id=@var{id}
+
+Forward QEMU's emulated msmouse events to the guest. @option{msmouse} does not
+take any options.
+
+@item -chardev vc ,id=@var{id} [[,width=@var{width}] [,height=@var{height}]] [[,cols=@var{cols}] [,rows=@var{rows}]]
+
+Connect to a QEMU text console. @option{vc} may optionally be given a specific
+size.
+
+@option{width} and @option{height} specify the width and height respectively of
+the console, in pixels.
+
+@option{cols} and @option{rows} specify that the console be sized to fit a text
+console with the given dimensions.
+
+@item -chardev file ,id=@var{id} ,path=@var{path}
+
+Log all traffic received from the guest to a file.
+
+@option{path} specifies the path of the file to be opened. This file will be
+created if it does not already exist, and overwritten if it does. @option{path}
+is required.
+
+@item -chardev pipe ,id=@var{id} ,path=@var{path}
+
+Create a two-way connection to the guest. The behaviour differs slightly between
+Windows hosts and other hosts:
+
+On Windows, a single duplex pipe will be created at
+@file{\\.pipe\@option{path}}.
+
+On other hosts, 2 pipes will be created called @file{@option{path}.in} and
+@file{@option{path}.out}. Data written to @file{@option{path}.in} will be
+received by the guest. Data written by the guest can be read from
+@file{@option{path}.out}. QEMU will not create these fifos, and requires them to
+be present.
+
+@option{path} forms part of the pipe path as described above. @option{path} is
+required.
+
+@item -chardev console ,id=@var{id}
+
+Send traffic from the guest to QEMU's standard output. @option{console} does not
+take any options.
+
+@option{console} is only available on Windows hosts.
+
+@item -chardev serial ,id=@var{id} ,path=@option{path}
+
+Send traffic from the guest to a serial device on the host.
+
+@option{serial} is
+only available on Windows hosts.
+
+@option{path} specifies the name of the serial device to open.
+
+@item -chardev pty ,id=@var{id}
+
+Create a new pseudo-terminal on the host and connect to it. @option{pty} does
+not take any options.
+
+@option{pty} is not available on Windows hosts.
+
+@item -chardev stdio ,id=@var{id} [,signal=on|off]
+Connect to standard input and standard output of the qemu process.
+
+@option{signal} controls if signals are enabled on the terminal, that includes
+exiting QEMU with the key sequence @key{Control-c}. This option is enabled by
+default, use @option{signal=off} to disable it.
+
+@option{stdio} is not available on Windows hosts.
+
+@item -chardev braille ,id=@var{id}
+
+Connect to a local BrlAPI server. @option{braille} does not take any options.
+
+@item -chardev tty ,id=@var{id} ,path=@var{path}
+
+Connect to a local tty device.
+
+@option{tty} is only available on Linux, Sun, FreeBSD, NetBSD, OpenBSD and
+DragonFlyBSD hosts.
+
+@option{path} specifies the path to the tty. @option{path} is required.
+
+@item -chardev parport ,id=@var{id} ,path=@var{path}
+
+@option{parport} is only available on Linux, FreeBSD and DragonFlyBSD hosts.
+
+Connect to a local parallel port.
+
+@option{path} specifies the path to the parallel port device. @option{path} is
+required.
+
+#if defined(CONFIG_SPICE)
+@item -chardev spicevmc ,id=@var{id} ,debug=@var{debug}, name=@var{name}
+
+@option{debug} debug level for spicevmc
+
+@option{name} name of spice channel to connect to
+
+Connect to a spice virtual machine channel, such as vdiport.
+#endif
+
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEFHEADING(Bluetooth(R) options:)
+
+DEF("bt", HAS_ARG, QEMU_OPTION_bt, \
+ "-bt hci,null dumb bluetooth HCI - doesn't respond to commands\n" \
+ "-bt hci,host[:id]\n" \
+ " use host's HCI with the given name\n" \
+ "-bt hci[,vlan=n]\n" \
+ " emulate a standard HCI in virtual scatternet 'n'\n" \
+ "-bt vhci[,vlan=n]\n" \
+ " add host computer to virtual scatternet 'n' using VHCI\n" \
+ "-bt device:dev[,vlan=n]\n" \
+ " emulate a bluetooth device 'dev' in scatternet 'n'\n",
+ QEMU_ARCH_ALL)
+STEXI
+@table @option
+
+@item -bt hci[...]
+@findex -bt
+Defines the function of the corresponding Bluetooth HCI. -bt options
+are matched with the HCIs present in the chosen machine type. For
+example when emulating a machine with only one HCI built into it, only
+the first @code{-bt hci[...]} option is valid and defines the HCI's
+logic. The Transport Layer is decided by the machine type. Currently
+the machines @code{n800} and @code{n810} have one HCI and all other
+machines have none.
+
+@anchor{bt-hcis}
+The following three types are recognized:
+
+@table @option
+@item -bt hci,null
+(default) The corresponding Bluetooth HCI assumes no internal logic
+and will not respond to any HCI commands or emit events.
+
+@item -bt hci,host[:@var{id}]
+(@code{bluez} only) The corresponding HCI passes commands / events
+to / from the physical HCI identified by the name @var{id} (default:
+@code{hci0}) on the computer running QEMU. Only available on @code{bluez}
+capable systems like Linux.
+
+@item -bt hci[,vlan=@var{n}]
+Add a virtual, standard HCI that will participate in the Bluetooth
+scatternet @var{n} (default @code{0}). Similarly to @option{-net}
+VLANs, devices inside a bluetooth network @var{n} can only communicate
+with other devices in the same network (scatternet).
+@end table
+
+@item -bt vhci[,vlan=@var{n}]
+(Linux-host only) Create a HCI in scatternet @var{n} (default 0) attached
+to the host bluetooth stack instead of to the emulated target. This
+allows the host and target machines to participate in a common scatternet
+and communicate. Requires the Linux @code{vhci} driver installed. Can
+be used as following:
+
+@example
+qemu [...OPTIONS...] -bt hci,vlan=5 -bt vhci,vlan=5
+@end example
+
+@item -bt device:@var{dev}[,vlan=@var{n}]
+Emulate a bluetooth device @var{dev} and place it in network @var{n}
+(default @code{0}). QEMU can only emulate one type of bluetooth devices
+currently:
+
+@table @option
+@item keyboard
+Virtual wireless keyboard implementing the HIDP bluetooth profile.
+@end table
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEFHEADING(Linux/Multiboot boot specific:)
+STEXI
+
+When using these options, you can use a given Linux or Multiboot
+kernel without installing it in the disk image. It can be useful
+for easier testing of various kernels.
+
+@table @option
+ETEXI
+
+DEF("kernel", HAS_ARG, QEMU_OPTION_kernel, \
+ "-kernel bzImage use 'bzImage' as kernel image\n", QEMU_ARCH_ALL)
+STEXI
+@item -kernel @var{bzImage}
+@findex -kernel
+Use @var{bzImage} as kernel image. The kernel can be either a Linux kernel
+or in multiboot format.
+ETEXI
+
+DEF("append", HAS_ARG, QEMU_OPTION_append, \
+ "-append cmdline use 'cmdline' as kernel command line\n", QEMU_ARCH_ALL)
+STEXI
+@item -append @var{cmdline}
+@findex -append
+Use @var{cmdline} as kernel command line
+ETEXI
+
+DEF("initrd", HAS_ARG, QEMU_OPTION_initrd, \
+ "-initrd file use 'file' as initial ram disk\n", QEMU_ARCH_ALL)
+STEXI
+@item -initrd @var{file}
+@findex -initrd
+Use @var{file} as initial ram disk.
+
+@item -initrd "@var{file1} arg=foo,@var{file2}"
+
+This syntax is only available with multiboot.
+
+Use @var{file1} and @var{file2} as modules and pass arg=foo as parameter to the
+first module.
+ETEXI
+
+STEXI
+@end table
+ETEXI
+
+DEFHEADING()
+
+DEFHEADING(Debug/Expert options:)
+
+STEXI
+@table @option
+ETEXI
+
+DEF("serial", HAS_ARG, QEMU_OPTION_serial, \
+ "-serial dev redirect the serial port to char device 'dev'\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -serial @var{dev}
+@findex -serial
+Redirect the virtual serial port to host character device
+@var{dev}. The default device is @code{vc} in graphical mode and
+@code{stdio} in non graphical mode.
+
+This option can be used several times to simulate up to 4 serial
+ports.
+
+Use @code{-serial none} to disable all serial ports.
+
+Available character devices are:
+@table @option
+@item vc[:@var{W}x@var{H}]
+Virtual console. Optionally, a width and height can be given in pixel with
+@example
+vc:800x600
+@end example
+It is also possible to specify width or height in characters:
+@example
+vc:80Cx24C
+@end example
+@item pty
+[Linux only] Pseudo TTY (a new PTY is automatically allocated)
+@item none
+No device is allocated.
+@item null
+void device
+@item /dev/XXX
+[Linux only] Use host tty, e.g. @file{/dev/ttyS0}. The host serial port
+parameters are set according to the emulated ones.
+@item /dev/parport@var{N}
+[Linux only, parallel port only] Use host parallel port
+@var{N}. Currently SPP and EPP parallel port features can be used.
+@item file:@var{filename}
+Write output to @var{filename}. No character can be read.
+@item stdio
+[Unix only] standard input/output
+@item pipe:@var{filename}
+name pipe @var{filename}
+@item COM@var{n}
+[Windows only] Use host serial port @var{n}
+@item udp:[@var{remote_host}]:@var{remote_port}[@@[@var{src_ip}]:@var{src_port}]
+This implements UDP Net Console.
+When @var{remote_host} or @var{src_ip} are not specified
+they default to @code{0.0.0.0}.
+When not using a specified @var{src_port} a random port is automatically chosen.
+
+If you just want a simple readonly console you can use @code{netcat} or
+@code{nc}, by starting qemu with: @code{-serial udp::4555} and nc as:
+@code{nc -u -l -p 4555}. Any time qemu writes something to that port it
+will appear in the netconsole session.
+
+If you plan to send characters back via netconsole or you want to stop
+and start qemu a lot of times, you should have qemu use the same
+source port each time by using something like @code{-serial
+udp::4555@@:4556} to qemu. Another approach is to use a patched
+version of netcat which can listen to a TCP port and send and receive
+characters via udp. If you have a patched version of netcat which
+activates telnet remote echo and single char transfer, then you can
+use the following options to step up a netcat redirector to allow
+telnet on port 5555 to access the qemu port.
+@table @code
+@item Qemu Options:
+-serial udp::4555@@:4556
+@item netcat options:
+-u -P 4555 -L 0.0.0.0:4556 -t -p 5555 -I -T
+@item telnet options:
+localhost 5555
+@end table
+
+@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay]
+The TCP Net Console has two modes of operation. It can send the serial
+I/O to a location or wait for a connection from a location. By default
+the TCP Net Console is sent to @var{host} at the @var{port}. If you use
+the @var{server} option QEMU will wait for a client socket application
+to connect to the port before continuing, unless the @code{nowait}
+option was specified. The @code{nodelay} option disables the Nagle buffering
+algorithm. If @var{host} is omitted, 0.0.0.0 is assumed. Only
+one TCP connection at a time is accepted. You can use @code{telnet} to
+connect to the corresponding character device.
+@table @code
+@item Example to send tcp console to 192.168.0.2 port 4444
+-serial tcp:192.168.0.2:4444
+@item Example to listen and wait on port 4444 for connection
+-serial tcp::4444,server
+@item Example to not wait and listen on ip 192.168.0.100 port 4444
+-serial tcp:192.168.0.100:4444,server,nowait
+@end table
+
+@item telnet:@var{host}:@var{port}[,server][,nowait][,nodelay]
+The telnet protocol is used instead of raw tcp sockets. The options
+work the same as if you had specified @code{-serial tcp}. The
+difference is that the port acts like a telnet server or client using
+telnet option negotiation. This will also allow you to send the
+MAGIC_SYSRQ sequence if you use a telnet that supports sending the break
+sequence. Typically in unix telnet you do it with Control-] and then
+type "send break" followed by pressing the enter key.
+
+@item unix:@var{path}[,server][,nowait]
+A unix domain socket is used instead of a tcp socket. The option works the
+same as if you had specified @code{-serial tcp} except the unix domain socket
+@var{path} is used for connections.
+
+@item mon:@var{dev_string}
+This is a special option to allow the monitor to be multiplexed onto
+another serial port. The monitor is accessed with key sequence of
+@key{Control-a} and then pressing @key{c}. See monitor access
+@ref{pcsys_keys} in the -nographic section for more keys.
+@var{dev_string} should be any one of the serial devices specified
+above. An example to multiplex the monitor onto a telnet server
+listening on port 4444 would be:
+@table @code
+@item -serial mon:telnet::4444,server,nowait
+@end table
+
+@item braille
+Braille device. This will use BrlAPI to display the braille output on a real
+or fake device.
+
+@item msmouse
+Three button serial mouse. Configure the guest to use Microsoft protocol.
+@end table
+ETEXI
+
+DEF("parallel", HAS_ARG, QEMU_OPTION_parallel, \
+ "-parallel dev redirect the parallel port to char device 'dev'\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -parallel @var{dev}
+@findex -parallel
+Redirect the virtual parallel port to host device @var{dev} (same
+devices as the serial port). On Linux hosts, @file{/dev/parportN} can
+be used to use hardware devices connected on the corresponding host
+parallel port.
+
+This option can be used several times to simulate up to 3 parallel
+ports.
+
+Use @code{-parallel none} to disable all parallel ports.
+ETEXI
+
+DEF("monitor", HAS_ARG, QEMU_OPTION_monitor, \
+ "-monitor dev redirect the monitor to char device 'dev'\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -monitor @var{dev}
+@findex -monitor
+Redirect the monitor to host device @var{dev} (same devices as the
+serial port).
+The default device is @code{vc} in graphical mode and @code{stdio} in
+non graphical mode.
+ETEXI
+DEF("qmp", HAS_ARG, QEMU_OPTION_qmp, \
+ "-qmp dev like -monitor but opens in 'control' mode\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -qmp @var{dev}
+@findex -qmp
+Like -monitor but opens in 'control' mode.
+ETEXI
+
+DEF("mon", HAS_ARG, QEMU_OPTION_mon, \
+ "-mon chardev=[name][,mode=readline|control][,default]\n", QEMU_ARCH_ALL)
+STEXI
+@item -mon chardev=[name][,mode=readline|control][,default]
+@findex -mon
+Setup monitor on chardev @var{name}.
+ETEXI
+
+DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \
+ "-debugcon dev redirect the debug console to char device 'dev'\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -debugcon @var{dev}
+@findex -debugcon
+Redirect the debug console to host device @var{dev} (same devices as the
+serial port). The debug console is an I/O port which is typically port
+0xe9; writing to that I/O port sends output to this device.
+The default device is @code{vc} in graphical mode and @code{stdio} in
+non graphical mode.
+ETEXI
+
+DEF("pidfile", HAS_ARG, QEMU_OPTION_pidfile, \
+ "-pidfile file write PID to 'file'\n", QEMU_ARCH_ALL)
+STEXI
+@item -pidfile @var{file}
+@findex -pidfile
+Store the QEMU process PID in @var{file}. It is useful if you launch QEMU
+from a script.
+ETEXI
+
+DEF("singlestep", 0, QEMU_OPTION_singlestep, \
+ "-singlestep always run in singlestep mode\n", QEMU_ARCH_ALL)
+STEXI
+@item -singlestep
+@findex -singlestep
+Run the emulation in single step mode.
+ETEXI
+
+DEF("S", 0, QEMU_OPTION_S, \
+ "-S freeze CPU at startup (use 'c' to start execution)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -S
+@findex -S
+Do not start CPU at startup (you must type 'c' in the monitor).
+ETEXI
+
+DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \
+ "-gdb dev wait for gdb connection on 'dev'\n", QEMU_ARCH_ALL)
+STEXI
+@item -gdb @var{dev}
+@findex -gdb
+Wait for gdb connection on device @var{dev} (@pxref{gdb_usage}). Typical
+connections will likely be TCP-based, but also UDP, pseudo TTY, or even
+stdio are reasonable use case. The latter is allowing to start qemu from
+within gdb and establish the connection via a pipe:
+@example
+(gdb) target remote | exec qemu -gdb stdio ...
+@end example
+ETEXI
+
+DEF("s", 0, QEMU_OPTION_s, \
+ "-s shorthand for -gdb tcp::" DEFAULT_GDBSTUB_PORT "\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -s
+@findex -s
+Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234
+(@pxref{gdb_usage}).
+ETEXI
+
+DEF("d", HAS_ARG, QEMU_OPTION_d, \
+ "-d item1,... output log to /tmp/qemu.log (use -d ? for a list of log items)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -d
+@findex -d
+Output log in /tmp/qemu.log
+ETEXI
+
+DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
+ "-hdachs c,h,s[,t]\n" \
+ " force hard disk 0 physical geometry and the optional BIOS\n" \
+ " translation (t=none or lba) (usually qemu can guess them)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
+@findex -hdachs
+Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
+@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
+translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
+all those parameters. This option is useful for old MS-DOS disk
+images.
+ETEXI
+
+DEF("L", HAS_ARG, QEMU_OPTION_L, \
+ "-L path set the directory for the BIOS, VGA BIOS and keymaps\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -L @var{path}
+@findex -L
+Set the directory for the BIOS, VGA BIOS and keymaps.
+ETEXI
+
+DEF("bios", HAS_ARG, QEMU_OPTION_bios, \
+ "-bios file set the filename for the BIOS\n", QEMU_ARCH_ALL)
+STEXI
+@item -bios @var{file}
+@findex -bios
+Set the filename for the BIOS.
+ETEXI
+
+DEF("enable-kvm", 0, QEMU_OPTION_enable_kvm, \
+ "-enable-kvm enable KVM full virtualization support\n", QEMU_ARCH_ALL)
+STEXI
+@item -enable-kvm
+@findex -enable-kvm
+Enable KVM full virtualization support. This option is only available
+if KVM support is enabled when compiling.
+ETEXI
+
+DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid,
+ "-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL)
+DEF("xen-create", 0, QEMU_OPTION_xen_create,
+ "-xen-create create domain using xen hypercalls, bypassing xend\n"
+ " warning: should not be used when xend is in use\n",
+ QEMU_ARCH_ALL)
+DEF("xen-attach", 0, QEMU_OPTION_xen_attach,
+ "-xen-attach attach to existing xen domain\n"
+ " xend will use this when starting qemu\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -xen-domid @var{id}
+@findex -xen-domid
+Specify xen guest domain @var{id} (XEN only).
+@item -xen-create
+@findex -xen-create
+Create domain using xen hypercalls, bypassing xend.
+Warning: should not be used when xend is in use (XEN only).
+@item -xen-attach
+@findex -xen-attach
+Attach to existing xen domain.
+xend will use this when starting qemu (XEN only).
+ETEXI
+
+DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \
+ "-no-reboot exit instead of rebooting\n", QEMU_ARCH_ALL)
+STEXI
+@item -no-reboot
+@findex -no-reboot
+Exit instead of rebooting.
+ETEXI
+
+DEF("no-shutdown", 0, QEMU_OPTION_no_shutdown, \
+ "-no-shutdown stop before shutdown\n", QEMU_ARCH_ALL)
+STEXI
+@item -no-shutdown
+@findex -no-shutdown
+Don't exit QEMU on guest shutdown, but instead only stop the emulation.
+This allows for instance switching to monitor to commit changes to the
+disk image.
+ETEXI
+
+DEF("loadvm", HAS_ARG, QEMU_OPTION_loadvm, \
+ "-loadvm [tag|id]\n" \
+ " start right away with a saved state (loadvm in monitor)\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -loadvm @var{file}
+@findex -loadvm
+Start right away with a saved state (@code{loadvm} in monitor)
+ETEXI
+
+#ifndef _WIN32
+DEF("daemonize", 0, QEMU_OPTION_daemonize, \
+ "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -daemonize
+@findex -daemonize
+Daemonize the QEMU process after initialization. QEMU will not detach from
+standard IO until it is ready to receive connections on any of its devices.
+This option is a useful way for external programs to launch QEMU without having
+to cope with initialization race conditions.
+ETEXI
+
+DEF("option-rom", HAS_ARG, QEMU_OPTION_option_rom, \
+ "-option-rom rom load a file, rom, into the option ROM space\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -option-rom @var{file}
+@findex -option-rom
+Load the contents of @var{file} as an option ROM.
+This option is useful to load things like EtherBoot.
+ETEXI
+
+DEF("clock", HAS_ARG, QEMU_OPTION_clock, \
+ "-clock force the use of the given methods for timer alarm.\n" \
+ " To see what timers are available use -clock ?\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -clock @var{method}
+@findex -clock
+Force the use of the given methods for timer alarm. To see what timers
+are available use -clock ?.
+ETEXI
+
+HXCOMM Options deprecated by -rtc
+DEF("localtime", 0, QEMU_OPTION_localtime, "", QEMU_ARCH_ALL)
+DEF("startdate", HAS_ARG, QEMU_OPTION_startdate, "", QEMU_ARCH_ALL)
+
+DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \
+ "-rtc [base=utc|localtime|date][,clock=host|vm][,driftfix=none|slew]\n" \
+ " set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n",
+ QEMU_ARCH_ALL)
+
+STEXI
+
+@item -rtc [base=utc|localtime|@var{date}][,clock=host|vm][,driftfix=none|slew]
+@findex -rtc
+Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current
+UTC or local time, respectively. @code{localtime} is required for correct date in
+MS-DOS or Windows. To start at a specific point in time, provide @var{date} in the
+format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC.
+
+By default the RTC is driven by the host system time. This allows to use the
+RTC as accurate reference clock inside the guest, specifically if the host
+time is smoothly following an accurate external reference clock, e.g. via NTP.
+If you want to isolate the guest time from the host, even prevent it from
+progressing during suspension, you can set @option{clock} to @code{vm} instead.
+
+Enable @option{driftfix} (i386 targets only) if you experience time drift problems,
+specifically with Windows' ACPI HAL. This option will try to figure out how
+many timer interrupts were not processed by the Windows guest and will
+re-inject them.
+ETEXI
+
+DEF("icount", HAS_ARG, QEMU_OPTION_icount, \
+ "-icount [N|auto]\n" \
+ " enable virtual instruction counter with 2^N clock ticks per\n" \
+ " instruction\n", QEMU_ARCH_ALL)
+STEXI
+@item -icount [@var{N}|auto]
+@findex -icount
+Enable virtual instruction counter. The virtual cpu will execute one
+instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified
+then the virtual cpu speed will be automatically adjusted to keep virtual
+time within a few seconds of real time.
+
+Note that while this option can give deterministic behavior, it does not
+provide cycle accurate emulation. Modern CPUs contain superscalar out of
+order cores with complex cache hierarchies. The number of instructions
+executed often has little or no correlation with actual performance.
+ETEXI
+
+DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
+ "-watchdog i6300esb|ib700\n" \
+ " enable virtual hardware watchdog [default=none]\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -watchdog @var{model}
+@findex -watchdog
+Create a virtual hardware watchdog device. Once enabled (by a guest
+action), the watchdog must be periodically polled by an agent inside
+the guest or else the guest will be restarted.
+
+The @var{model} is the model of hardware watchdog to emulate. Choices
+for model are: @code{ib700} (iBASE 700) which is a very simple ISA
+watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O
+controller hub) which is a much more featureful PCI-based dual-timer
+watchdog. Choose a model for which your guest has drivers.
+
+Use @code{-watchdog ?} to list available hardware models. Only one
+watchdog can be enabled for a guest.
+ETEXI
+
+DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \
+ "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n" \
+ " action when watchdog fires [default=reset]\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -watchdog-action @var{action}
+
+The @var{action} controls what QEMU will do when the watchdog timer
+expires.
+The default is
+@code{reset} (forcefully reset the guest).
+Other possible actions are:
+@code{shutdown} (attempt to gracefully shutdown the guest),
+@code{poweroff} (forcefully poweroff the guest),
+@code{pause} (pause the guest),
+@code{debug} (print a debug message and continue), or
+@code{none} (do nothing).
+
+Note that the @code{shutdown} action requires that the guest responds
+to ACPI signals, which it may not be able to do in the sort of
+situations where the watchdog would have expired, and thus
+@code{-watchdog-action shutdown} is not recommended for production use.
+
+Examples:
+
+@table @code
+@item -watchdog i6300esb -watchdog-action pause
+@item -watchdog ib700
+@end table
+ETEXI
+
+DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
+ "-echr chr set terminal escape character instead of ctrl-a\n",
+ QEMU_ARCH_ALL)
+STEXI
+
+@item -echr @var{numeric_ascii_value}
+@findex -echr
+Change the escape character used for switching to the monitor when using
+monitor and serial sharing. The default is @code{0x01} when using the
+@code{-nographic} option. @code{0x01} is equal to pressing
+@code{Control-a}. You can select a different character from the ascii
+control keys where 1 through 26 map to Control-a through Control-z. For
+instance you could use the either of the following to change the escape
+character to Control-t.
+@table @code
+@item -echr 0x14
+@item -echr 20
+@end table
+ETEXI
+
+DEF("virtioconsole", HAS_ARG, QEMU_OPTION_virtiocon, \
+ "-virtioconsole c\n" \
+ " set virtio console\n", QEMU_ARCH_ALL)
+STEXI
+@item -virtioconsole @var{c}
+@findex -virtioconsole
+Set virtio console.
+
+This option is maintained for backward compatibility.
+
+Please use @code{-device virtconsole} for the new way of invocation.
+ETEXI
+
+DEF("show-cursor", 0, QEMU_OPTION_show_cursor, \
+ "-show-cursor show cursor\n", QEMU_ARCH_ALL)
+STEXI
+@item -show-cursor
+@findex -show-cursor
+Show cursor.
+ETEXI
+
+DEF("tb-size", HAS_ARG, QEMU_OPTION_tb_size, \
+ "-tb-size n set TB size\n", QEMU_ARCH_ALL)
+STEXI
+@item -tb-size @var{n}
+@findex -tb-size
+Set TB size.
+ETEXI
+
+DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \
+ "-incoming p prepare for incoming migration, listen on port p\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -incoming @var{port}
+@findex -incoming
+Prepare for incoming migration, listen on @var{port}.
+ETEXI
+
+DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \
+ "-nodefaults don't create default devices\n", QEMU_ARCH_ALL)
+STEXI
+@item -nodefaults
+@findex -nodefaults
+Don't create default devices.
+ETEXI
+
+#ifndef _WIN32
+DEF("chroot", HAS_ARG, QEMU_OPTION_chroot, \
+ "-chroot dir chroot to dir just before starting the VM\n",
+ QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -chroot @var{dir}
+@findex -chroot
+Immediately before starting guest execution, chroot to the specified
+directory. Especially useful in combination with -runas.
+ETEXI
+
+#ifndef _WIN32
+DEF("runas", HAS_ARG, QEMU_OPTION_runas, \
+ "-runas user change to user id user just before starting the VM\n",
+ QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -runas @var{user}
+@findex -runas
+Immediately before starting guest execution, drop root privileges, switching
+to the specified user.
+ETEXI
+
+DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env,
+ "-prom-env variable=value\n"
+ " set OpenBIOS nvram variables\n",
+ QEMU_ARCH_PPC | QEMU_ARCH_SPARC)
+STEXI
+@item -prom-env @var{variable}=@var{value}
+@findex -prom-env
+Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only).
+ETEXI
+DEF("semihosting", 0, QEMU_OPTION_semihosting,
+ "-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K)
+STEXI
+@item -semihosting
+@findex -semihosting
+Semihosting mode (ARM, M68K only).
+ETEXI
+DEF("old-param", 0, QEMU_OPTION_old_param,
+ "-old-param old param mode\n", QEMU_ARCH_ARM)
+STEXI
+@item -old-param
+@findex -old-param (ARM)
+Old param mode (ARM only).
+ETEXI
+
+DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig,
+ "-readconfig <file>\n", QEMU_ARCH_ALL)
+STEXI
+@item -readconfig @var{file}
+@findex -readconfig
+Read device configuration from @var{file}.
+ETEXI
+DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig,
+ "-writeconfig <file>\n"
+ " read/write config file\n", QEMU_ARCH_ALL)
+STEXI
+@item -writeconfig @var{file}
+@findex -writeconfig
+Write device configuration to @var{file}.
+ETEXI
+DEF("nodefconfig", 0, QEMU_OPTION_nodefconfig,
+ "-nodefconfig\n"
+ " do not load default config files at startup\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -nodefconfig
+@findex -nodefconfig
+Normally QEMU loads a configuration file from @var{sysconfdir}/qemu.conf and
+@var{sysconfdir}/target-@var{ARCH}.conf on startup. The @code{-nodefconfig}
+option will prevent QEMU from loading these configuration files at startup.
+ETEXI
+#ifdef CONFIG_SIMPLE_TRACE
+DEF("trace", HAS_ARG, QEMU_OPTION_trace,
+ "-trace\n"
+ " Specify a trace file to log traces to\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -trace
+@findex -trace
+Specify a trace file to log output traces to.
+ETEXI
+#endif
+
+DEF("no-kvm", 0, QEMU_OPTION_no_kvm,
+ "-no-kvm disable KVM hardware virtualization\n",
+ QEMU_ARCH_ALL)
+DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip,
+ "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n",
+ QEMU_ARCH_I386)
+DEF("no-kvm-pit", 0, QEMU_OPTION_no_kvm_pit,
+ "-no-kvm-pit disable KVM kernel mode PIT\n",
+ QEMU_ARCH_I386)
+DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection,
+ "-no-kvm-pit-reinjection\n"
+ " disable KVM kernel mode PIT interrupt reinjection\n",
+ QEMU_ARCH_I386)
+DEF("enable-nesting", 0, QEMU_OPTION_enable_nesting,
+ "-enable-nesting enable support for running a VM inside the VM (AMD only)\n", QEMU_ARCH_I386)
+DEF("nvram", HAS_ARG, QEMU_OPTION_nvram,
+ "-nvram FILE provide ia64 nvram contents\n", QEMU_ARCH_ALL)
+DEF("tdf", 0, QEMU_OPTION_tdf,
+ "-tdf enable guest time drift compensation\n", QEMU_ARCH_ALL)
+DEF("kvm-shadow-memory", HAS_ARG, QEMU_OPTION_kvm_shadow_memory,
+ "-kvm-shadow-memory MEGABYTES\n"
+ " allocate MEGABYTES for kvm mmu shadowing\n",
+ QEMU_ARCH_I386)
+
+HXCOMM This is the last statement. Insert new options before this line!
+STEXI
+@end table
+ETEXI
diff --git a/qemu-os-posix.h b/qemu-os-posix.h
new file mode 100644
index 0000000..81fd9ab
--- /dev/null
+++ b/qemu-os-posix.h
@@ -0,0 +1,54 @@
+/*
+ * posix specific declarations
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_OS_POSIX_H
+#define QEMU_OS_POSIX_H
+
+static inline void os_host_main_loop_wait(int *timeout)
+{
+}
+
+void os_set_line_buffering(void);
+void os_set_proc_name(const char *s);
+void os_setup_signal_handling(void);
+void os_daemonize(void);
+void os_setup_post(void);
+
+typedef struct timeval qemu_timeval;
+#define qemu_gettimeofday(tp) gettimeofday(tp, NULL)
+
+#ifndef CONFIG_UTIMENSAT
+#ifndef UTIME_NOW
+# define UTIME_NOW ((1l << 30) - 1l)
+#endif
+#ifndef UTIME_OMIT
+# define UTIME_OMIT ((1l << 30) - 2l)
+#endif
+#endif
+typedef struct timespec qemu_timespec;
+int qemu_utimensat(int dirfd, const char *path, const qemu_timespec *times,
+ int flags);
+
+#endif
diff --git a/qemu-os-win32.h b/qemu-os-win32.h
new file mode 100644
index 0000000..1a07e5e
--- /dev/null
+++ b/qemu-os-win32.h
@@ -0,0 +1,63 @@
+/*
+ * win32 specific declarations
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ * Copyright (c) 2010 Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_OS_WIN32_H
+#define QEMU_OS_WIN32_H
+
+/* Polling handling */
+
+/* return TRUE if no sleep should be done afterwards */
+typedef int PollingFunc(void *opaque);
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque);
+void qemu_del_polling_cb(PollingFunc *func, void *opaque);
+
+/* Wait objects handling */
+typedef void WaitObjectFunc(void *opaque);
+
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque);
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque);
+
+void os_host_main_loop_wait(int *timeout);
+
+static inline void os_setup_signal_handling(void) {}
+static inline void os_daemonize(void) {}
+static inline void os_setup_post(void) {}
+void os_set_line_buffering(void);
+static inline void os_set_proc_name(const char *dummy) {}
+
+#if !defined(EPROTONOSUPPORT)
+# define EPROTONOSUPPORT EINVAL
+#endif
+
+int setenv(const char *name, const char *value, int overwrite);
+
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} qemu_timeval;
+int qemu_gettimeofday(qemu_timeval *tp);
+
+#endif
diff --git a/qemu-queue.h b/qemu-queue.h
new file mode 100644
index 0000000..1d07745
--- /dev/null
+++ b/qemu-queue.h
@@ -0,0 +1,449 @@
+/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */
+
+/*
+ * Qemu version: Copy from netbsd, removed debug code, removed some of
+ * the implementations. Left in lists, simple queues, tail queues and
+ * circular queues.
+ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef QEMU_SYS_QUEUE_H_
+#define QEMU_SYS_QUEUE_H_
+
+/*
+ * This file defines four types of data structures:
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define QLIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define QLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define QLIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#define QLIST_INIT(head) do { \
+ (head)->lh_first = NULL; \
+} while (/*CONSTCOND*/0)
+
+#define QLIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define QLIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define QLIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define QLIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var); \
+ (var) = ((var)->field.le_next))
+
+#define QLIST_FOREACH_SAFE(var, head, field, next_var) \
+ for ((var) = ((head)->lh_first); \
+ (var) && ((next_var) = ((var)->field.le_next), 1); \
+ (var) = (next_var))
+
+/*
+ * List access methods.
+ */
+#define QLIST_EMPTY(head) ((head)->lh_first == NULL)
+#define QLIST_FIRST(head) ((head)->lh_first)
+#define QLIST_NEXT(elm, field) ((elm)->field.le_next)
+
+
+/*
+ * Simple queue definitions.
+ */
+#define QSIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define QSIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define QSIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue functions.
+ */
+#define QSIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ QSIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var); \
+ (var) = ((var)->field.sqe_next))
+
+#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->sqh_first); \
+ (var) && ((next = ((var)->field.sqe_next)), 1); \
+ (var) = (next))
+
+#define QSIMPLEQ_CONCAT(head1, head2) do { \
+ if (!QSIMPLEQ_EMPTY((head2))) { \
+ *(head1)->sqh_last = (head2)->sqh_first; \
+ (head1)->sqh_last = (head2)->sqh_last; \
+ QSIMPLEQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_LAST(head, type, field) \
+ (QSIMPLEQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->sqh_last) - offsetof(struct type, field))))
+
+/*
+ * Simple queue access methods.
+ */
+#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL)
+#define QSIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+
+/*
+ * Tail queue definitions.
+ */
+#define Q_TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define QTAILQ_HEAD(name, type) Q_TAILQ_HEAD(name, struct type,)
+
+#define QTAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define Q_TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define QTAILQ_ENTRY(type) Q_TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue functions.
+ */
+#define QTAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+
+#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \
+ for ((var) = ((head)->tqh_first); \
+ (var) && ((next_var) = ((var)->field.tqe_next), 1); \
+ (var) = (next_var))
+
+#define QTAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+/*
+ * Tail queue access methods.
+ */
+#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define QTAILQ_FIRST(head) ((head)->tqh_first)
+#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define QTAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define QTAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+/*
+ * Circular queue definitions.
+ */
+#define QCIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define QCIRCLEQ_HEAD_INITIALIZER(head) \
+ { (void *)&head, (void *)&head }
+
+#define QCIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define QCIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (/*CONSTCOND*/0)
+
+#define QCIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define QCIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define QCIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define QCIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define QCIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define QCIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->cqh_first); \
+ (var) != (const void *)(head); \
+ (var) = ((var)->field.cqe_next))
+
+#define QCIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = ((head)->cqh_last); \
+ (var) != (const void *)(head); \
+ (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define QCIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+#define QCIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define QCIRCLEQ_LAST(head) ((head)->cqh_last)
+#define QCIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define QCIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+
+#define QCIRCLEQ_LOOP_NEXT(head, elm, field) \
+ (((elm)->field.cqe_next == (void *)(head)) \
+ ? ((head)->cqh_first) \
+ : (elm->field.cqe_next))
+#define QCIRCLEQ_LOOP_PREV(head, elm, field) \
+ (((elm)->field.cqe_prev == (void *)(head)) \
+ ? ((head)->cqh_last) \
+ : (elm->field.cqe_prev))
+
+#endif /* !QEMU_SYS_QUEUE_H_ */
diff --git a/qemu-sockets.c b/qemu-sockets.c
new file mode 100644
index 0000000..c526324
--- /dev/null
+++ b/qemu-sockets.c
@@ -0,0 +1,675 @@
+/*
+ * inet and unix socket functions for qemu
+ *
+ * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program 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 General Public License for more details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "qemu_socket.h"
+#include "qemu-common.h" /* for qemu_isdigit */
+
+#ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0
+#endif
+
+static int sockets_debug = 0;
+static const int on=1, off=0;
+
+/* used temporarely until all users are converted to QemuOpts */
+static QemuOptsList dummy_opts = {
+ .name = "dummy",
+ .head = QTAILQ_HEAD_INITIALIZER(dummy_opts.head),
+ .desc = {
+ {
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "host",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "port",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "to",
+ .type = QEMU_OPT_NUMBER,
+ },{
+ .name = "ipv4",
+ .type = QEMU_OPT_BOOL,
+ },{
+ .name = "ipv6",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end if list */ }
+ },
+};
+
+static int inet_getport(struct addrinfo *e)
+{
+ struct sockaddr_in *i4;
+ struct sockaddr_in6 *i6;
+
+ switch (e->ai_family) {
+ case PF_INET6:
+ i6 = (void*)e->ai_addr;
+ return ntohs(i6->sin6_port);
+ case PF_INET:
+ i4 = (void*)e->ai_addr;
+ return ntohs(i4->sin_port);
+ default:
+ return 0;
+ }
+}
+
+static void inet_setport(struct addrinfo *e, int port)
+{
+ struct sockaddr_in *i4;
+ struct sockaddr_in6 *i6;
+
+ switch (e->ai_family) {
+ case PF_INET6:
+ i6 = (void*)e->ai_addr;
+ i6->sin6_port = htons(port);
+ break;
+ case PF_INET:
+ i4 = (void*)e->ai_addr;
+ i4->sin_port = htons(port);
+ break;
+ }
+}
+
+const char *inet_strfamily(int family)
+{
+ switch (family) {
+ case PF_INET6: return "ipv6";
+ case PF_INET: return "ipv4";
+ case PF_UNIX: return "unix";
+ }
+ return "unknown";
+}
+
+static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
+{
+ struct addrinfo *e;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
+ tag, inet_strfamily(e->ai_family), uaddr, uport);
+ }
+}
+
+int inet_listen_opts(QemuOpts *opts, int port_offset)
+{
+ struct addrinfo ai,*res,*e;
+ const char *addr;
+ char port[33];
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int slisten,rc,to,try_next;
+
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ ai.ai_family = PF_UNSPEC;
+ ai.ai_socktype = SOCK_STREAM;
+
+ if ((qemu_opt_get(opts, "host") == NULL) ||
+ (qemu_opt_get(opts, "port") == NULL)) {
+ fprintf(stderr, "%s: host and/or port not specified\n", __FUNCTION__);
+ return -1;
+ }
+ pstrcpy(port, sizeof(port), qemu_opt_get(opts, "port"));
+ addr = qemu_opt_get(opts, "host");
+
+ to = qemu_opt_get_number(opts, "to", 0);
+ if (qemu_opt_get_bool(opts, "ipv4", 0))
+ ai.ai_family = PF_INET;
+ if (qemu_opt_get_bool(opts, "ipv6", 0))
+ ai.ai_family = PF_INET6;
+
+ /* lookup */
+ if (port_offset)
+ snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
+ rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
+ if (rc != 0) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
+ gai_strerror(rc));
+ return -1;
+ }
+ if (sockets_debug)
+ inet_print_addrinfo(__FUNCTION__, res);
+
+ /* create socket + bind */
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
+ if (slisten < 0) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), strerror(errno));
+ continue;
+ }
+
+ setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+#ifdef IPV6_V6ONLY
+ if (e->ai_family == PF_INET6) {
+ /* listen on both ipv4 and ipv6 */
+ setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,
+ sizeof(off));
+ }
+#endif
+
+ for (;;) {
+ if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
+ if (sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), uaddr, inet_getport(e));
+ goto listen;
+ }
+ try_next = to && (inet_getport(e) <= to + port_offset);
+ if (!try_next || sockets_debug)
+ fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), uaddr, inet_getport(e),
+ strerror(errno));
+ if (try_next) {
+ inet_setport(e, inet_getport(e) + 1);
+ continue;
+ }
+ break;
+ }
+ closesocket(slisten);
+ }
+ fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
+ freeaddrinfo(res);
+ return -1;
+
+listen:
+ if (listen(slisten,1) != 0) {
+ perror("listen");
+ closesocket(slisten);
+ freeaddrinfo(res);
+ return -1;
+ }
+ snprintf(uport, sizeof(uport), "%d", inet_getport(e) - port_offset);
+ qemu_opt_set(opts, "host", uaddr);
+ qemu_opt_set(opts, "port", uport);
+ qemu_opt_set(opts, "ipv6", (e->ai_family == PF_INET6) ? "on" : "off");
+ qemu_opt_set(opts, "ipv4", (e->ai_family != PF_INET6) ? "on" : "off");
+ freeaddrinfo(res);
+ return slisten;
+}
+
+int inet_connect_opts(QemuOpts *opts)
+{
+ struct addrinfo ai,*res,*e;
+ const char *addr;
+ const char *port;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int sock,rc;
+
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+ ai.ai_family = PF_UNSPEC;
+ ai.ai_socktype = SOCK_STREAM;
+
+ addr = qemu_opt_get(opts, "host");
+ port = qemu_opt_get(opts, "port");
+ if (addr == NULL || port == NULL) {
+ fprintf(stderr, "inet_connect: host and/or port not specified\n");
+ return -1;
+ }
+
+ if (qemu_opt_get_bool(opts, "ipv4", 0))
+ ai.ai_family = PF_INET;
+ if (qemu_opt_get_bool(opts, "ipv6", 0))
+ ai.ai_family = PF_INET6;
+
+ /* lookup */
+ if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
+ gai_strerror(rc));
+ return -1;
+ }
+ if (sockets_debug)
+ inet_print_addrinfo(__FUNCTION__, res);
+
+ for (e = res; e != NULL; e = e->ai_next) {
+ if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
+ continue;
+ }
+ sock = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol);
+ if (sock < 0) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family), strerror(errno));
+ continue;
+ }
+ setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+
+ /* connect to peer */
+ if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) {
+ if (sockets_debug || NULL == e->ai_next)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
+ inet_strfamily(e->ai_family),
+ e->ai_canonname, uaddr, uport, strerror(errno));
+ closesocket(sock);
+ continue;
+ }
+ if (sockets_debug)
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
+ inet_strfamily(e->ai_family),
+ e->ai_canonname, uaddr, uport);
+ freeaddrinfo(res);
+ return sock;
+ }
+ freeaddrinfo(res);
+ return -1;
+}
+
+int inet_dgram_opts(QemuOpts *opts)
+{
+ struct addrinfo ai, *peer = NULL, *local = NULL;
+ const char *addr;
+ const char *port;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int sock = -1, rc;
+
+ /* lookup peer addr */
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+ ai.ai_family = PF_UNSPEC;
+ ai.ai_socktype = SOCK_DGRAM;
+
+ addr = qemu_opt_get(opts, "host");
+ port = qemu_opt_get(opts, "port");
+ if (addr == NULL || strlen(addr) == 0) {
+ addr = "localhost";
+ }
+ if (port == NULL || strlen(port) == 0) {
+ fprintf(stderr, "inet_dgram: port not specified\n");
+ return -1;
+ }
+
+ if (qemu_opt_get_bool(opts, "ipv4", 0))
+ ai.ai_family = PF_INET;
+ if (qemu_opt_get_bool(opts, "ipv6", 0))
+ ai.ai_family = PF_INET6;
+
+ if (0 != (rc = getaddrinfo(addr, port, &ai, &peer))) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
+ gai_strerror(rc));
+ return -1;
+ }
+ if (sockets_debug) {
+ fprintf(stderr, "%s: peer (%s:%s)\n", __FUNCTION__, addr, port);
+ inet_print_addrinfo(__FUNCTION__, peer);
+ }
+
+ /* lookup local addr */
+ memset(&ai,0, sizeof(ai));
+ ai.ai_flags = AI_PASSIVE;
+ ai.ai_family = peer->ai_family;
+ ai.ai_socktype = SOCK_DGRAM;
+
+ addr = qemu_opt_get(opts, "localaddr");
+ port = qemu_opt_get(opts, "localport");
+ if (addr == NULL || strlen(addr) == 0) {
+ addr = NULL;
+ }
+ if (!port || strlen(port) == 0)
+ port = "0";
+
+ if (0 != (rc = getaddrinfo(addr, port, &ai, &local))) {
+ fprintf(stderr,"getaddrinfo(%s,%s): %s\n", addr, port,
+ gai_strerror(rc));
+ return -1;
+ }
+ if (sockets_debug) {
+ fprintf(stderr, "%s: local (%s:%s)\n", __FUNCTION__, addr, port);
+ inet_print_addrinfo(__FUNCTION__, local);
+ }
+
+ /* create socket */
+ sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol);
+ if (sock < 0) {
+ fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
+ inet_strfamily(peer->ai_family), strerror(errno));
+ goto err;
+ }
+ setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
+
+ /* bind socket */
+ if (getnameinfo((struct sockaddr*)local->ai_addr,local->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__);
+ goto err;
+ }
+ if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) {
+ fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
+ inet_strfamily(local->ai_family), uaddr, inet_getport(local));
+ goto err;
+ }
+
+ /* connect to peer */
+ if (getnameinfo((struct sockaddr*)peer->ai_addr, peer->ai_addrlen,
+ uaddr, INET6_ADDRSTRLEN, uport, 32,
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+ fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__);
+ goto err;
+ }
+ if (connect(sock,peer->ai_addr,peer->ai_addrlen) < 0) {
+ fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
+ inet_strfamily(peer->ai_family),
+ peer->ai_canonname, uaddr, uport, strerror(errno));
+ goto err;
+ }
+
+ freeaddrinfo(local);
+ freeaddrinfo(peer);
+ return sock;
+
+err:
+ if (-1 != sock)
+ closesocket(sock);
+ if (local)
+ freeaddrinfo(local);
+ if (peer)
+ freeaddrinfo(peer);
+ return -1;
+}
+
+/* compatibility wrapper */
+static int inet_parse(QemuOpts *opts, const char *str)
+{
+ const char *optstr, *h;
+ char addr[64];
+ char port[33];
+ int pos;
+
+ /* parse address */
+ if (str[0] == ':') {
+ /* no host given */
+ addr[0] = '\0';
+ if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
+ fprintf(stderr, "%s: portonly parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ } else if (str[0] == '[') {
+ /* IPv6 addr */
+ if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: ipv6 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ qemu_opt_set(opts, "ipv6", "on");
+ } else if (qemu_isdigit(str[0])) {
+ /* IPv4 addr */
+ if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: ipv4 parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ qemu_opt_set(opts, "ipv4", "on");
+ } else {
+ /* hostname */
+ if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
+ fprintf(stderr, "%s: hostname parse error (%s)\n",
+ __FUNCTION__, str);
+ return -1;
+ }
+ }
+ qemu_opt_set(opts, "host", addr);
+ qemu_opt_set(opts, "port", port);
+
+ /* parse options */
+ optstr = str + pos;
+ h = strstr(optstr, ",to=");
+ if (h)
+ qemu_opt_set(opts, "to", h+4);
+ if (strstr(optstr, ",ipv4"))
+ qemu_opt_set(opts, "ipv4", "on");
+ if (strstr(optstr, ",ipv6"))
+ qemu_opt_set(opts, "ipv6", "on");
+ return 0;
+}
+
+int inet_listen(const char *str, char *ostr, int olen,
+ int socktype, int port_offset)
+{
+ QemuOpts *opts;
+ char *optstr;
+ int sock = -1;
+
+ opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ if (inet_parse(opts, str) == 0) {
+ sock = inet_listen_opts(opts, port_offset);
+ if (sock != -1 && ostr) {
+ optstr = strchr(str, ',');
+ if (qemu_opt_get_bool(opts, "ipv6", 0)) {
+ snprintf(ostr, olen, "[%s]:%s%s",
+ qemu_opt_get(opts, "host"),
+ qemu_opt_get(opts, "port"),
+ optstr ? optstr : "");
+ } else {
+ snprintf(ostr, olen, "%s:%s%s",
+ qemu_opt_get(opts, "host"),
+ qemu_opt_get(opts, "port"),
+ optstr ? optstr : "");
+ }
+ }
+ }
+ qemu_opts_del(opts);
+ return sock;
+}
+
+int inet_connect(const char *str, int socktype)
+{
+ QemuOpts *opts;
+ int sock = -1;
+
+ opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ if (inet_parse(opts, str) == 0)
+ sock = inet_connect_opts(opts);
+ qemu_opts_del(opts);
+ return sock;
+}
+
+#ifndef _WIN32
+
+int unix_listen_opts(QemuOpts *opts)
+{
+ struct sockaddr_un un;
+ const char *path = qemu_opt_get(opts, "path");
+ int sock, fd;
+
+ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ if (path && strlen(path)) {
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ } else {
+ char *tmpdir = getenv("TMPDIR");
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
+ tmpdir ? tmpdir : "/tmp");
+ /*
+ * This dummy fd usage silences the mktemp() unsecure warning.
+ * Using mkstemp() doesn't make things more secure here
+ * though. bind() complains about existing files, so we have
+ * to unlink first and thus re-open the race window. The
+ * worst case possible is bind() failing, i.e. a DoS attack.
+ */
+ fd = mkstemp(un.sun_path); close(fd);
+ qemu_opt_set(opts, "path", un.sun_path);
+ }
+
+ unlink(un.sun_path);
+ if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
+ fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+ if (listen(sock, 1) < 0) {
+ fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
+ goto err;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
+ return sock;
+
+err:
+ closesocket(sock);
+ return -1;
+}
+
+int unix_connect_opts(QemuOpts *opts)
+{
+ struct sockaddr_un un;
+ const char *path = qemu_opt_get(opts, "path");
+ int sock;
+
+ if (NULL == path) {
+ fprintf(stderr, "unix connect: no path specified\n");
+ return -1;
+ }
+
+ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket(unix)");
+ return -1;
+ }
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
+ fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ if (sockets_debug)
+ fprintf(stderr, "connect(unix:%s): OK\n", path);
+ return sock;
+}
+
+/* compatibility wrapper */
+int unix_listen(const char *str, char *ostr, int olen)
+{
+ QemuOpts *opts;
+ char *path, *optstr;
+ int sock, len;
+
+ opts = qemu_opts_create(&dummy_opts, NULL, 0);
+
+ optstr = strchr(str, ',');
+ if (optstr) {
+ len = optstr - str;
+ if (len) {
+ path = qemu_malloc(len+1);
+ snprintf(path, len+1, "%.*s", len, str);
+ qemu_opt_set(opts, "path", path);
+ qemu_free(path);
+ }
+ } else {
+ qemu_opt_set(opts, "path", str);
+ }
+
+ sock = unix_listen_opts(opts);
+
+ if (sock != -1 && ostr)
+ snprintf(ostr, olen, "%s%s", qemu_opt_get(opts, "path"), optstr ? optstr : "");
+ qemu_opts_del(opts);
+ return sock;
+}
+
+int unix_connect(const char *path)
+{
+ QemuOpts *opts;
+ int sock;
+
+ opts = qemu_opts_create(&dummy_opts, NULL, 0);
+ qemu_opt_set(opts, "path", path);
+ sock = unix_connect_opts(opts);
+ qemu_opts_del(opts);
+ return sock;
+}
+
+#else
+
+int unix_listen_opts(QemuOpts *opts)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+int unix_connect_opts(QemuOpts *opts)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+int unix_listen(const char *path, char *ostr, int olen)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+int unix_connect(const char *path)
+{
+ fprintf(stderr, "unix sockets are not available on windows\n");
+ return -1;
+}
+
+#endif
+
+#ifdef _WIN32
+static void socket_cleanup(void)
+{
+ WSACleanup();
+}
+#endif
+
+int socket_init(void)
+{
+#ifdef _WIN32
+ WSADATA Data;
+ int ret, err;
+
+ ret = WSAStartup(MAKEWORD(2,2), &Data);
+ if (ret != 0) {
+ err = WSAGetLastError();
+ fprintf(stderr, "WSAStartup: %d\n", err);
+ return -1;
+ }
+ atexit(socket_cleanup);
+#endif
+ return 0;
+}
diff --git a/qemu-tech.texi b/qemu-tech.texi
new file mode 100644
index 0000000..138e3ce
--- /dev/null
+++ b/qemu-tech.texi
@@ -0,0 +1,697 @@
+\input texinfo @c -*- texinfo -*-
+@c %**start of header
+@setfilename qemu-tech.info
+
+@documentlanguage en
+@documentencoding UTF-8
+
+@settitle QEMU Internals
+@exampleindent 0
+@paragraphindent 0
+@c %**end of header
+
+@ifinfo
+@direntry
+* QEMU Internals: (qemu-tech). The QEMU Emulator Internals.
+@end direntry
+@end ifinfo
+
+@iftex
+@titlepage
+@sp 7
+@center @titlefont{QEMU Internals}
+@sp 3
+@end titlepage
+@end iftex
+
+@ifnottex
+@node Top
+@top
+
+@menu
+* Introduction::
+* QEMU Internals::
+* Regression Tests::
+* Index::
+@end menu
+@end ifnottex
+
+@contents
+
+@node Introduction
+@chapter Introduction
+
+@menu
+* intro_features:: Features
+* intro_x86_emulation:: x86 and x86-64 emulation
+* intro_arm_emulation:: ARM emulation
+* intro_mips_emulation:: MIPS emulation
+* intro_ppc_emulation:: PowerPC emulation
+* intro_sparc_emulation:: Sparc32 and Sparc64 emulation
+* intro_other_emulation:: Other CPU emulation
+@end menu
+
+@node intro_features
+@section Features
+
+QEMU is a FAST! processor emulator using a portable dynamic
+translator.
+
+QEMU has two operating modes:
+
+@itemize @minus
+
+@item
+Full system emulation. In this mode (full platform virtualization),
+QEMU emulates a full system (usually a PC), including a processor and
+various peripherals. It can be used to launch several different
+Operating Systems at once without rebooting the host machine or to
+debug system code.
+
+@item
+User mode emulation. In this mode (application level virtualization),
+QEMU can launch processes compiled for one CPU on another CPU, however
+the Operating Systems must match. This can be used for example to ease
+cross-compilation and cross-debugging.
+@end itemize
+
+As QEMU requires no host kernel driver to run, it is very safe and
+easy to use.
+
+QEMU generic features:
+
+@itemize
+
+@item User space only or full system emulation.
+
+@item Using dynamic translation to native code for reasonable speed.
+
+@item
+Working on x86, x86_64 and PowerPC32/64 hosts. Being tested on ARM,
+HPPA, Sparc32 and Sparc64. Previous versions had some support for
+Alpha and S390 hosts, but TCG (see below) doesn't support those yet.
+
+@item Self-modifying code support.
+
+@item Precise exceptions support.
+
+@item The virtual CPU is a library (@code{libqemu}) which can be used
+in other projects (look at @file{qemu/tests/qruncom.c} to have an
+example of user mode @code{libqemu} usage).
+
+@item
+Floating point library supporting both full software emulation and
+native host FPU instructions.
+
+@end itemize
+
+QEMU user mode emulation features:
+@itemize
+@item Generic Linux system call converter, including most ioctls.
+
+@item clone() emulation using native CPU clone() to use Linux scheduler for threads.
+
+@item Accurate signal handling by remapping host signals to target signals.
+@end itemize
+
+Linux user emulator (Linux host only) can be used to launch the Wine
+Windows API emulator (@url{http://www.winehq.org}). A Darwin user
+emulator (Darwin hosts only) exists and a BSD user emulator for BSD
+hosts is under development. It would also be possible to develop a
+similar user emulator for Solaris.
+
+QEMU full system emulation features:
+@itemize
+@item
+QEMU uses a full software MMU for maximum portability.
+
+@item
+QEMU can optionally use an in-kernel accelerator, like kvm. The accelerators
+execute some of the guest code natively, while
+continuing to emulate the rest of the machine.
+
+@item
+Various hardware devices can be emulated and in some cases, host
+devices (e.g. serial and parallel ports, USB, drives) can be used
+transparently by the guest Operating System. Host device passthrough
+can be used for talking to external physical peripherals (e.g. a
+webcam, modem or tape drive).
+
+@item
+Symmetric multiprocessing (SMP) even on a host with a single CPU. On a
+SMP host system, QEMU can use only one CPU fully due to difficulty in
+implementing atomic memory accesses efficiently.
+
+@end itemize
+
+@node intro_x86_emulation
+@section x86 and x86-64 emulation
+
+QEMU x86 target features:
+
+@itemize
+
+@item The virtual x86 CPU supports 16 bit and 32 bit addressing with segmentation.
+LDT/GDT and IDT are emulated. VM86 mode is also supported to run
+DOSEMU. There is some support for MMX/3DNow!, SSE, SSE2, SSE3, SSSE3,
+and SSE4 as well as x86-64 SVM.
+
+@item Support of host page sizes bigger than 4KB in user mode emulation.
+
+@item QEMU can emulate itself on x86.
+
+@item An extensive Linux x86 CPU test program is included @file{tests/test-i386}.
+It can be used to test other x86 virtual CPUs.
+
+@end itemize
+
+Current QEMU limitations:
+
+@itemize
+
+@item Limited x86-64 support.
+
+@item IPC syscalls are missing.
+
+@item The x86 segment limits and access rights are not tested at every
+memory access (yet). Hopefully, very few OSes seem to rely on that for
+normal use.
+
+@end itemize
+
+@node intro_arm_emulation
+@section ARM emulation
+
+@itemize
+
+@item Full ARM 7 user emulation.
+
+@item NWFPE FPU support included in user Linux emulation.
+
+@item Can run most ARM Linux binaries.
+
+@end itemize
+
+@node intro_mips_emulation
+@section MIPS emulation
+
+@itemize
+
+@item The system emulation allows full MIPS32/MIPS64 Release 2 emulation,
+including privileged instructions, FPU and MMU, in both little and big
+endian modes.
+
+@item The Linux userland emulation can run many 32 bit MIPS Linux binaries.
+
+@end itemize
+
+Current QEMU limitations:
+
+@itemize
+
+@item Self-modifying code is not always handled correctly.
+
+@item 64 bit userland emulation is not implemented.
+
+@item The system emulation is not complete enough to run real firmware.
+
+@item The watchpoint debug facility is not implemented.
+
+@end itemize
+
+@node intro_ppc_emulation
+@section PowerPC emulation
+
+@itemize
+
+@item Full PowerPC 32 bit emulation, including privileged instructions,
+FPU and MMU.
+
+@item Can run most PowerPC Linux binaries.
+
+@end itemize
+
+@node intro_sparc_emulation
+@section Sparc32 and Sparc64 emulation
+
+@itemize
+
+@item Full SPARC V8 emulation, including privileged
+instructions, FPU and MMU. SPARC V9 emulation includes most privileged
+and VIS instructions, FPU and I/D MMU. Alignment is fully enforced.
+
+@item Can run most 32-bit SPARC Linux binaries, SPARC32PLUS Linux binaries and
+some 64-bit SPARC Linux binaries.
+
+@end itemize
+
+Current QEMU limitations:
+
+@itemize
+
+@item IPC syscalls are missing.
+
+@item Floating point exception support is buggy.
+
+@item Atomic instructions are not correctly implemented.
+
+@item There are still some problems with Sparc64 emulators.
+
+@end itemize
+
+@node intro_other_emulation
+@section Other CPU emulation
+
+In addition to the above, QEMU supports emulation of other CPUs with
+varying levels of success. These are:
+
+@itemize
+
+@item
+Alpha
+@item
+CRIS
+@item
+M68k
+@item
+SH4
+@end itemize
+
+@node QEMU Internals
+@chapter QEMU Internals
+
+@menu
+* QEMU compared to other emulators::
+* Portable dynamic translation::
+* Condition code optimisations::
+* CPU state optimisations::
+* Translation cache::
+* Direct block chaining::
+* Self-modifying code and translated code invalidation::
+* Exception support::
+* MMU emulation::
+* Device emulation::
+* Hardware interrupts::
+* User emulation specific details::
+* Bibliography::
+@end menu
+
+@node QEMU compared to other emulators
+@section QEMU compared to other emulators
+
+Like bochs [3], QEMU emulates an x86 CPU. But QEMU is much faster than
+bochs as it uses dynamic compilation. Bochs is closely tied to x86 PC
+emulation while QEMU can emulate several processors.
+
+Like Valgrind [2], QEMU does user space emulation and dynamic
+translation. Valgrind is mainly a memory debugger while QEMU has no
+support for it (QEMU could be used to detect out of bound memory
+accesses as Valgrind, but it has no support to track uninitialised data
+as Valgrind does). The Valgrind dynamic translator generates better code
+than QEMU (in particular it does register allocation) but it is closely
+tied to an x86 host and target and has no support for precise exceptions
+and system emulation.
+
+EM86 [4] is the closest project to user space QEMU (and QEMU still uses
+some of its code, in particular the ELF file loader). EM86 was limited
+to an alpha host and used a proprietary and slow interpreter (the
+interpreter part of the FX!32 Digital Win32 code translator [5]).
+
+TWIN [6] is a Windows API emulator like Wine. It is less accurate than
+Wine but includes a protected mode x86 interpreter to launch x86 Windows
+executables. Such an approach has greater potential because most of the
+Windows API is executed natively but it is far more difficult to develop
+because all the data structures and function parameters exchanged
+between the API and the x86 code must be converted.
+
+User mode Linux [7] was the only solution before QEMU to launch a
+Linux kernel as a process while not needing any host kernel
+patches. However, user mode Linux requires heavy kernel patches while
+QEMU accepts unpatched Linux kernels. The price to pay is that QEMU is
+slower.
+
+The Plex86 [8] PC virtualizer is done in the same spirit as the now
+obsolete qemu-fast system emulator. It requires a patched Linux kernel
+to work (you cannot launch the same kernel on your PC), but the
+patches are really small. As it is a PC virtualizer (no emulation is
+done except for some privileged instructions), it has the potential of
+being faster than QEMU. The downside is that a complicated (and
+potentially unsafe) host kernel patch is needed.
+
+The commercial PC Virtualizers (VMWare [9], VirtualPC [10], TwoOStwo
+[11]) are faster than QEMU, but they all need specific, proprietary
+and potentially unsafe host drivers. Moreover, they are unable to
+provide cycle exact simulation as an emulator can.
+
+VirtualBox [12], Xen [13] and KVM [14] are based on QEMU. QEMU-SystemC
+[15] uses QEMU to simulate a system where some hardware devices are
+developed in SystemC.
+
+@node Portable dynamic translation
+@section Portable dynamic translation
+
+QEMU is a dynamic translator. When it first encounters a piece of code,
+it converts it to the host instruction set. Usually dynamic translators
+are very complicated and highly CPU dependent. QEMU uses some tricks
+which make it relatively easily portable and simple while achieving good
+performances.
+
+After the release of version 0.9.1, QEMU switched to a new method of
+generating code, Tiny Code Generator or TCG. TCG relaxes the
+dependency on the exact version of the compiler used. The basic idea
+is to split every target instruction into a couple of RISC-like TCG
+ops (see @code{target-i386/translate.c}). Some optimizations can be
+performed at this stage, including liveness analysis and trivial
+constant expression evaluation. TCG ops are then implemented in the
+host CPU back end, also known as TCG target (see
+@code{tcg/i386/tcg-target.c}). For more information, please take a
+look at @code{tcg/README}.
+
+@node Condition code optimisations
+@section Condition code optimisations
+
+Lazy evaluation of CPU condition codes (@code{EFLAGS} register on x86)
+is important for CPUs where every instruction sets the condition
+codes. It tends to be less important on conventional RISC systems
+where condition codes are only updated when explicitly requested. On
+Sparc64, costly update of both 32 and 64 bit condition codes can be
+avoided with lazy evaluation.
+
+Instead of computing the condition codes after each x86 instruction,
+QEMU just stores one operand (called @code{CC_SRC}), the result
+(called @code{CC_DST}) and the type of operation (called
+@code{CC_OP}). When the condition codes are needed, the condition
+codes can be calculated using this information. In addition, an
+optimized calculation can be performed for some instruction types like
+conditional branches.
+
+@code{CC_OP} is almost never explicitly set in the generated code
+because it is known at translation time.
+
+The lazy condition code evaluation is used on x86, m68k, cris and
+Sparc. ARM uses a simplified variant for the N and Z flags.
+
+@node CPU state optimisations
+@section CPU state optimisations
+
+The target CPUs have many internal states which change the way it
+evaluates instructions. In order to achieve a good speed, the
+translation phase considers that some state information of the virtual
+CPU cannot change in it. The state is recorded in the Translation
+Block (TB). If the state changes (e.g. privilege level), a new TB will
+be generated and the previous TB won't be used anymore until the state
+matches the state recorded in the previous TB. For example, if the SS,
+DS and ES segments have a zero base, then the translator does not even
+generate an addition for the segment base.
+
+[The FPU stack pointer register is not handled that way yet].
+
+@node Translation cache
+@section Translation cache
+
+A 16 MByte cache holds the most recently used translations. For
+simplicity, it is completely flushed when it is full. A translation unit
+contains just a single basic block (a block of x86 instructions
+terminated by a jump or by a virtual CPU state change which the
+translator cannot deduce statically).
+
+@node Direct block chaining
+@section Direct block chaining
+
+After each translated basic block is executed, QEMU uses the simulated
+Program Counter (PC) and other cpu state informations (such as the CS
+segment base value) to find the next basic block.
+
+In order to accelerate the most common cases where the new simulated PC
+is known, QEMU can patch a basic block so that it jumps directly to the
+next one.
+
+The most portable code uses an indirect jump. An indirect jump makes
+it easier to make the jump target modification atomic. On some host
+architectures (such as x86 or PowerPC), the @code{JUMP} opcode is
+directly patched so that the block chaining has no overhead.
+
+@node Self-modifying code and translated code invalidation
+@section Self-modifying code and translated code invalidation
+
+Self-modifying code is a special challenge in x86 emulation because no
+instruction cache invalidation is signaled by the application when code
+is modified.
+
+When translated code is generated for a basic block, the corresponding
+host page is write protected if it is not already read-only. Then, if
+a write access is done to the page, Linux raises a SEGV signal. QEMU
+then invalidates all the translated code in the page and enables write
+accesses to the page.
+
+Correct translated code invalidation is done efficiently by maintaining
+a linked list of every translated block contained in a given page. Other
+linked lists are also maintained to undo direct block chaining.
+
+On RISC targets, correctly written software uses memory barriers and
+cache flushes, so some of the protection above would not be
+necessary. However, QEMU still requires that the generated code always
+matches the target instructions in memory in order to handle
+exceptions correctly.
+
+@node Exception support
+@section Exception support
+
+longjmp() is used when an exception such as division by zero is
+encountered.
+
+The host SIGSEGV and SIGBUS signal handlers are used to get invalid
+memory accesses. The simulated program counter is found by
+retranslating the corresponding basic block and by looking where the
+host program counter was at the exception point.
+
+The virtual CPU cannot retrieve the exact @code{EFLAGS} register because
+in some cases it is not computed because of condition code
+optimisations. It is not a big concern because the emulated code can
+still be restarted in any cases.
+
+@node MMU emulation
+@section MMU emulation
+
+For system emulation QEMU supports a soft MMU. In that mode, the MMU
+virtual to physical address translation is done at every memory
+access. QEMU uses an address translation cache to speed up the
+translation.
+
+In order to avoid flushing the translated code each time the MMU
+mappings change, QEMU uses a physically indexed translation cache. It
+means that each basic block is indexed with its physical address.
+
+When MMU mappings change, only the chaining of the basic blocks is
+reset (i.e. a basic block can no longer jump directly to another one).
+
+@node Device emulation
+@section Device emulation
+
+Systems emulated by QEMU are organized by boards. At initialization
+phase, each board instantiates a number of CPUs, devices, RAM and
+ROM. Each device in turn can assign I/O ports or memory areas (for
+MMIO) to its handlers. When the emulation starts, an access to the
+ports or MMIO memory areas assigned to the device causes the
+corresponding handler to be called.
+
+RAM and ROM are handled more optimally, only the offset to the host
+memory needs to be added to the guest address.
+
+The video RAM of VGA and other display cards is special: it can be
+read or written directly like RAM, but write accesses cause the memory
+to be marked with VGA_DIRTY flag as well.
+
+QEMU supports some device classes like serial and parallel ports, USB,
+drives and network devices, by providing APIs for easier connection to
+the generic, higher level implementations. The API hides the
+implementation details from the devices, like native device use or
+advanced block device formats like QCOW.
+
+Usually the devices implement a reset method and register support for
+saving and loading of the device state. The devices can also use
+timers, especially together with the use of bottom halves (BHs).
+
+@node Hardware interrupts
+@section Hardware interrupts
+
+In order to be faster, QEMU does not check at every basic block if an
+hardware interrupt is pending. Instead, the user must asynchronously
+call a specific function to tell that an interrupt is pending. This
+function resets the chaining of the currently executing basic
+block. It ensures that the execution will return soon in the main loop
+of the CPU emulator. Then the main loop can test if the interrupt is
+pending and handle it.
+
+@node User emulation specific details
+@section User emulation specific details
+
+@subsection Linux system call translation
+
+QEMU includes a generic system call translator for Linux. It means that
+the parameters of the system calls can be converted to fix the
+endianness and 32/64 bit issues. The IOCTLs are converted with a generic
+type description system (see @file{ioctls.h} and @file{thunk.c}).
+
+QEMU supports host CPUs which have pages bigger than 4KB. It records all
+the mappings the process does and try to emulated the @code{mmap()}
+system calls in cases where the host @code{mmap()} call would fail
+because of bad page alignment.
+
+@subsection Linux signals
+
+Normal and real-time signals are queued along with their information
+(@code{siginfo_t}) as it is done in the Linux kernel. Then an interrupt
+request is done to the virtual CPU. When it is interrupted, one queued
+signal is handled by generating a stack frame in the virtual CPU as the
+Linux kernel does. The @code{sigreturn()} system call is emulated to return
+from the virtual signal handler.
+
+Some signals (such as SIGALRM) directly come from the host. Other
+signals are synthesized from the virtual CPU exceptions such as SIGFPE
+when a division by zero is done (see @code{main.c:cpu_loop()}).
+
+The blocked signal mask is still handled by the host Linux kernel so
+that most signal system calls can be redirected directly to the host
+Linux kernel. Only the @code{sigaction()} and @code{sigreturn()} system
+calls need to be fully emulated (see @file{signal.c}).
+
+@subsection clone() system call and threads
+
+The Linux clone() system call is usually used to create a thread. QEMU
+uses the host clone() system call so that real host threads are created
+for each emulated thread. One virtual CPU instance is created for each
+thread.
+
+The virtual x86 CPU atomic operations are emulated with a global lock so
+that their semantic is preserved.
+
+Note that currently there are still some locking issues in QEMU. In
+particular, the translated cache flush is not protected yet against
+reentrancy.
+
+@subsection Self-virtualization
+
+QEMU was conceived so that ultimately it can emulate itself. Although
+it is not very useful, it is an important test to show the power of the
+emulator.
+
+Achieving self-virtualization is not easy because there may be address
+space conflicts. QEMU user emulators solve this problem by being an
+executable ELF shared object as the ld-linux.so ELF interpreter. That
+way, it can be relocated at load time.
+
+@node Bibliography
+@section Bibliography
+
+@table @asis
+
+@item [1]
+@url{http://citeseer.nj.nec.com/piumarta98optimizing.html}, Optimizing
+direct threaded code by selective inlining (1998) by Ian Piumarta, Fabio
+Riccardi.
+
+@item [2]
+@url{http://developer.kde.org/~sewardj/}, Valgrind, an open-source
+memory debugger for x86-GNU/Linux, by Julian Seward.
+
+@item [3]
+@url{http://bochs.sourceforge.net/}, the Bochs IA-32 Emulator Project,
+by Kevin Lawton et al.
+
+@item [4]
+@url{http://www.cs.rose-hulman.edu/~donaldlf/em86/index.html}, the EM86
+x86 emulator on Alpha-Linux.
+
+@item [5]
+@url{http://www.usenix.org/publications/library/proceedings/usenix-nt97/@/full_papers/chernoff/chernoff.pdf},
+DIGITAL FX!32: Running 32-Bit x86 Applications on Alpha NT, by Anton
+Chernoff and Ray Hookway.
+
+@item [6]
+@url{http://www.willows.com/}, Windows API library emulation from
+Willows Software.
+
+@item [7]
+@url{http://user-mode-linux.sourceforge.net/},
+The User-mode Linux Kernel.
+
+@item [8]
+@url{http://www.plex86.org/},
+The new Plex86 project.
+
+@item [9]
+@url{http://www.vmware.com/},
+The VMWare PC virtualizer.
+
+@item [10]
+@url{http://www.microsoft.com/windowsxp/virtualpc/},
+The VirtualPC PC virtualizer.
+
+@item [11]
+@url{http://www.twoostwo.org/},
+The TwoOStwo PC virtualizer.
+
+@item [12]
+@url{http://virtualbox.org/},
+The VirtualBox PC virtualizer.
+
+@item [13]
+@url{http://www.xen.org/},
+The Xen hypervisor.
+
+@item [14]
+@url{http://kvm.qumranet.com/kvmwiki/Front_Page},
+Kernel Based Virtual Machine (KVM).
+
+@item [15]
+@url{http://www.greensocs.com/projects/QEMUSystemC},
+QEMU-SystemC, a hardware co-simulator.
+
+@end table
+
+@node Regression Tests
+@chapter Regression Tests
+
+In the directory @file{tests/}, various interesting testing programs
+are available. They are used for regression testing.
+
+@menu
+* test-i386::
+* linux-test::
+* qruncom.c::
+@end menu
+
+@node test-i386
+@section @file{test-i386}
+
+This program executes most of the 16 bit and 32 bit x86 instructions and
+generates a text output. It can be compared with the output obtained with
+a real CPU or another emulator. The target @code{make test} runs this
+program and a @code{diff} on the generated output.
+
+The Linux system call @code{modify_ldt()} is used to create x86 selectors
+to test some 16 bit addressing and 32 bit with segmentation cases.
+
+The Linux system call @code{vm86()} is used to test vm86 emulation.
+
+Various exceptions are raised to test most of the x86 user space
+exception reporting.
+
+@node linux-test
+@section @file{linux-test}
+
+This program tests various Linux system calls. It is used to verify
+that the system call parameters are correctly converted between target
+and host CPUs.
+
+@node qruncom.c
+@section @file{qruncom.c}
+
+Example of usage of @code{libqemu} to emulate a user mode i386 CPU.
+
+@node Index
+@chapter Index
+@printindex cp
+
+@bye
diff --git a/qemu-thread.c b/qemu-thread.c
new file mode 100644
index 0000000..fbc78fe
--- /dev/null
+++ b/qemu-thread.c
@@ -0,0 +1,192 @@
+/*
+ * Wrappers around mutex/cond/thread functions
+ *
+ * Copyright Red Hat, Inc. 2009
+ *
+ * Author:
+ * Marcelo Tosatti <mtosatti@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include "qemu-thread.h"
+
+static void error_exit(int err, const char *msg)
+{
+ fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
+ exit(1);
+}
+
+void qemu_mutex_init(QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_mutex_init(&mutex->lock, NULL);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_mutex_destroy(QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_mutex_destroy(&mutex->lock);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_mutex_lock(QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_mutex_lock(&mutex->lock);
+ if (err)
+ error_exit(err, __func__);
+}
+
+int qemu_mutex_trylock(QemuMutex *mutex)
+{
+ return pthread_mutex_trylock(&mutex->lock);
+}
+
+static void timespec_add_ms(struct timespec *ts, uint64_t msecs)
+{
+ ts->tv_sec = ts->tv_sec + (long)(msecs / 1000);
+ ts->tv_nsec = (ts->tv_nsec + ((long)msecs % 1000) * 1000000);
+ if (ts->tv_nsec >= 1000000000) {
+ ts->tv_nsec -= 1000000000;
+ ts->tv_sec++;
+ }
+}
+
+int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs)
+{
+ int err;
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ timespec_add_ms(&ts, msecs);
+
+ err = pthread_mutex_timedlock(&mutex->lock, &ts);
+ if (err && err != ETIMEDOUT)
+ error_exit(err, __func__);
+ return err;
+}
+
+void qemu_mutex_unlock(QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_mutex_unlock(&mutex->lock);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_cond_init(QemuCond *cond)
+{
+ int err;
+
+ err = pthread_cond_init(&cond->cond, NULL);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_cond_destroy(QemuCond *cond)
+{
+ int err;
+
+ err = pthread_cond_destroy(&cond->cond);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_cond_signal(QemuCond *cond)
+{
+ int err;
+
+ err = pthread_cond_signal(&cond->cond);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_cond_broadcast(QemuCond *cond)
+{
+ int err;
+
+ err = pthread_cond_broadcast(&cond->cond);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
+{
+ int err;
+
+ err = pthread_cond_wait(&cond->cond, &mutex->lock);
+ if (err)
+ error_exit(err, __func__);
+}
+
+int qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex, uint64_t msecs)
+{
+ struct timespec ts;
+ int err;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ timespec_add_ms(&ts, msecs);
+
+ err = pthread_cond_timedwait(&cond->cond, &mutex->lock, &ts);
+ if (err && err != ETIMEDOUT)
+ error_exit(err, __func__);
+ return err;
+}
+
+void qemu_thread_create(QemuThread *thread,
+ void *(*start_routine)(void*),
+ void *arg)
+{
+ int err;
+
+ /* Leave signal handling to the iothread. */
+ sigset_t set, oldset;
+
+ sigfillset(&set);
+ pthread_sigmask(SIG_SETMASK, &set, &oldset);
+ err = pthread_create(&thread->thread, NULL, start_routine, arg);
+ if (err)
+ error_exit(err, __func__);
+
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+}
+
+void qemu_thread_signal(QemuThread *thread, int sig)
+{
+ int err;
+
+ err = pthread_kill(thread->thread, sig);
+ if (err)
+ error_exit(err, __func__);
+}
+
+void qemu_thread_self(QemuThread *thread)
+{
+ thread->thread = pthread_self();
+}
+
+int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2)
+{
+ return pthread_equal(thread1->thread, thread2->thread);
+}
+
+void qemu_thread_exit(void *retval)
+{
+ pthread_exit(retval);
+}
diff --git a/qemu-thread.h b/qemu-thread.h
new file mode 100644
index 0000000..19bb30c
--- /dev/null
+++ b/qemu-thread.h
@@ -0,0 +1,44 @@
+#ifndef __QEMU_THREAD_H
+#define __QEMU_THREAD_H 1
+#include "semaphore.h"
+#include "pthread.h"
+
+struct QemuMutex {
+ pthread_mutex_t lock;
+};
+
+struct QemuCond {
+ pthread_cond_t cond;
+};
+
+struct QemuThread {
+ pthread_t thread;
+};
+
+typedef struct QemuMutex QemuMutex;
+typedef struct QemuCond QemuCond;
+typedef struct QemuThread QemuThread;
+
+void qemu_mutex_init(QemuMutex *mutex);
+void qemu_mutex_destroy(QemuMutex *mutex);
+void qemu_mutex_lock(QemuMutex *mutex);
+int qemu_mutex_trylock(QemuMutex *mutex);
+int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs);
+void qemu_mutex_unlock(QemuMutex *mutex);
+
+void qemu_cond_init(QemuCond *cond);
+void qemu_cond_destroy(QemuCond *cond);
+void qemu_cond_signal(QemuCond *cond);
+void qemu_cond_broadcast(QemuCond *cond);
+void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
+int qemu_cond_timedwait(QemuCond *cond, QemuMutex *mutex, uint64_t msecs);
+
+void qemu_thread_create(QemuThread *thread,
+ void *(*start_routine)(void*),
+ void *arg);
+void qemu_thread_signal(QemuThread *thread, int sig);
+void qemu_thread_self(QemuThread *thread);
+int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2);
+void qemu_thread_exit(void *retval);
+
+#endif
diff --git a/qemu-timer-common.c b/qemu-timer-common.c
new file mode 100644
index 0000000..755e300
--- /dev/null
+++ b/qemu-timer-common.c
@@ -0,0 +1,63 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-timer.h"
+
+/***********************************************************/
+/* real time host monotonic timer */
+
+#ifdef _WIN32
+
+int64_t clock_freq;
+
+static void __attribute__((constructor)) init_get_clock(void)
+{
+ LARGE_INTEGER freq;
+ int ret;
+ ret = QueryPerformanceFrequency(&freq);
+ if (ret == 0) {
+ fprintf(stderr, "Could not calibrate ticks\n");
+ exit(1);
+ }
+ clock_freq = freq.QuadPart;
+}
+
+#else
+
+int use_rt_clock;
+
+static void __attribute__((constructor)) init_get_clock(void)
+{
+ use_rt_clock = 0;
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
+ || defined(__DragonFly__) || defined(__FreeBSD_kernel__) \
+ || defined(__OpenBSD__)
+ {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ use_rt_clock = 1;
+ }
+ }
+#endif
+}
+#endif
diff --git a/qemu-timer.c b/qemu-timer.c
new file mode 100644
index 0000000..218a2a3
--- /dev/null
+++ b/qemu-timer.c
@@ -0,0 +1,1122 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysemu.h"
+#include "net.h"
+#include "monitor.h"
+#include "console.h"
+
+#include "hw/hw.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <signal.h>
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#endif
+
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/rtc.h>
+/* For the benefit of older linux systems which don't supply it,
+ we use a local copy of hpet.h. */
+/* #include <linux/hpet.h> */
+#include "hpet.h"
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
+#include "qemu-timer.h"
+
+/* Conversion factor from emulated instructions to virtual clock ticks. */
+int icount_time_shift;
+/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
+#define MAX_ICOUNT_SHIFT 10
+/* Compensate for varying guest execution speed. */
+int64_t qemu_icount_bias;
+static QEMUTimer *icount_rt_timer;
+static QEMUTimer *icount_vm_timer;
+
+/***********************************************************/
+/* guest cycle counter */
+
+typedef struct TimersState {
+ int64_t cpu_ticks_prev;
+ int64_t cpu_ticks_offset;
+ int64_t cpu_clock_offset;
+ int32_t cpu_ticks_enabled;
+ int64_t dummy;
+} TimersState;
+
+TimersState timers_state;
+
+/* return the host CPU cycle counter and handle stop/restart */
+int64_t cpu_get_ticks(void)
+{
+ if (use_icount) {
+ return cpu_get_icount();
+ }
+ if (!timers_state.cpu_ticks_enabled) {
+ return timers_state.cpu_ticks_offset;
+ } else {
+ int64_t ticks;
+ ticks = cpu_get_real_ticks();
+ if (timers_state.cpu_ticks_prev > ticks) {
+ /* Note: non increasing ticks may happen if the host uses
+ software suspend */
+ timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
+ }
+ timers_state.cpu_ticks_prev = ticks;
+ return ticks + timers_state.cpu_ticks_offset;
+ }
+}
+
+/* return the host CPU monotonic timer and handle stop/restart */
+static int64_t cpu_get_clock(void)
+{
+ int64_t ti;
+ if (!timers_state.cpu_ticks_enabled) {
+ return timers_state.cpu_clock_offset;
+ } else {
+ ti = get_clock();
+ return ti + timers_state.cpu_clock_offset;
+ }
+}
+
+static int64_t qemu_icount_delta(void)
+{
+ if (!use_icount) {
+ return 5000 * (int64_t) 1000000;
+ } else if (use_icount == 1) {
+ /* When not using an adaptive execution frequency
+ we tend to get badly out of sync with real time,
+ so just delay for a reasonable amount of time. */
+ return 0;
+ } else {
+ return cpu_get_icount() - cpu_get_clock();
+ }
+}
+
+/* enable cpu_get_ticks() */
+void cpu_enable_ticks(void)
+{
+ if (!timers_state.cpu_ticks_enabled) {
+ timers_state.cpu_ticks_offset -= cpu_get_real_ticks();
+ timers_state.cpu_clock_offset -= get_clock();
+ timers_state.cpu_ticks_enabled = 1;
+ }
+}
+
+/* disable cpu_get_ticks() : the clock is stopped. You must not call
+ cpu_get_ticks() after that. */
+void cpu_disable_ticks(void)
+{
+ if (timers_state.cpu_ticks_enabled) {
+ timers_state.cpu_ticks_offset = cpu_get_ticks();
+ timers_state.cpu_clock_offset = cpu_get_clock();
+ timers_state.cpu_ticks_enabled = 0;
+ }
+}
+
+/***********************************************************/
+/* timers */
+
+#define QEMU_CLOCK_REALTIME 0
+#define QEMU_CLOCK_VIRTUAL 1
+#define QEMU_CLOCK_HOST 2
+
+struct QEMUClock {
+ int type;
+ int enabled;
+ /* XXX: add frequency */
+};
+
+struct QEMUTimer {
+ QEMUClock *clock;
+ int64_t expire_time;
+ QEMUTimerCB *cb;
+ void *opaque;
+ struct QEMUTimer *next;
+};
+
+struct qemu_alarm_timer {
+ char const *name;
+ int (*start)(struct qemu_alarm_timer *t);
+ void (*stop)(struct qemu_alarm_timer *t);
+ void (*rearm)(struct qemu_alarm_timer *t);
+ void *priv;
+
+ char expired;
+ char pending;
+};
+
+static struct qemu_alarm_timer *alarm_timer;
+
+int qemu_alarm_pending(void)
+{
+ return alarm_timer->pending;
+}
+
+static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
+{
+ return !!t->rearm;
+}
+
+static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
+{
+ if (!alarm_has_dynticks(t))
+ return;
+
+ t->rearm(t);
+}
+
+/* TODO: MIN_TIMER_REARM_NS should be optimized */
+#define MIN_TIMER_REARM_NS 250000
+
+#ifdef _WIN32
+
+struct qemu_alarm_win32 {
+ MMRESULT timerId;
+ unsigned int period;
+} alarm_win32_data = {0, 0};
+
+static int win32_start_timer(struct qemu_alarm_timer *t);
+static void win32_stop_timer(struct qemu_alarm_timer *t);
+static void win32_rearm_timer(struct qemu_alarm_timer *t);
+
+#else
+
+static int unix_start_timer(struct qemu_alarm_timer *t);
+static void unix_stop_timer(struct qemu_alarm_timer *t);
+
+#ifdef __linux__
+
+static int dynticks_start_timer(struct qemu_alarm_timer *t);
+static void dynticks_stop_timer(struct qemu_alarm_timer *t);
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t);
+
+static int hpet_start_timer(struct qemu_alarm_timer *t);
+static void hpet_stop_timer(struct qemu_alarm_timer *t);
+
+static int rtc_start_timer(struct qemu_alarm_timer *t);
+static void rtc_stop_timer(struct qemu_alarm_timer *t);
+
+#endif /* __linux__ */
+
+#endif /* _WIN32 */
+
+/* Correlation between real and virtual time is always going to be
+ fairly approximate, so ignore small variation.
+ When the guest is idle real and virtual time will be aligned in
+ the IO wait loop. */
+#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10)
+
+static void icount_adjust(void)
+{
+ int64_t cur_time;
+ int64_t cur_icount;
+ int64_t delta;
+ static int64_t last_delta;
+ /* If the VM is not running, then do nothing. */
+ if (!vm_running)
+ return;
+
+ cur_time = cpu_get_clock();
+ cur_icount = qemu_get_clock(vm_clock);
+ delta = cur_icount - cur_time;
+ /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
+ if (delta > 0
+ && last_delta + ICOUNT_WOBBLE < delta * 2
+ && icount_time_shift > 0) {
+ /* The guest is getting too far ahead. Slow time down. */
+ icount_time_shift--;
+ }
+ if (delta < 0
+ && last_delta - ICOUNT_WOBBLE > delta * 2
+ && icount_time_shift < MAX_ICOUNT_SHIFT) {
+ /* The guest is getting too far behind. Speed time up. */
+ icount_time_shift++;
+ }
+ last_delta = delta;
+ qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift);
+}
+
+static void icount_adjust_rt(void * opaque)
+{
+ qemu_mod_timer(icount_rt_timer,
+ qemu_get_clock(rt_clock) + 1000);
+ icount_adjust();
+}
+
+static void icount_adjust_vm(void * opaque)
+{
+ qemu_mod_timer(icount_vm_timer,
+ qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10);
+ icount_adjust();
+}
+
+int64_t qemu_icount_round(int64_t count)
+{
+ return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
+}
+
+static struct qemu_alarm_timer alarm_timers[] = {
+#ifndef _WIN32
+#ifdef __linux__
+ {"dynticks", dynticks_start_timer,
+ dynticks_stop_timer, dynticks_rearm_timer, NULL},
+ /* HPET - if available - is preferred */
+ {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL},
+ /* ...otherwise try RTC */
+ {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL},
+#endif
+ {"unix", unix_start_timer, unix_stop_timer, NULL, NULL},
+#else
+ {"dynticks", win32_start_timer,
+ win32_stop_timer, win32_rearm_timer, &alarm_win32_data},
+ {"win32", win32_start_timer,
+ win32_stop_timer, NULL, &alarm_win32_data},
+#endif
+ {NULL, }
+};
+
+static void show_available_alarms(void)
+{
+ int i;
+
+ printf("Available alarm timers, in order of precedence:\n");
+ for (i = 0; alarm_timers[i].name; i++)
+ printf("%s\n", alarm_timers[i].name);
+}
+
+void configure_alarms(char const *opt)
+{
+ int i;
+ int cur = 0;
+ int count = ARRAY_SIZE(alarm_timers) - 1;
+ char *arg;
+ char *name;
+ struct qemu_alarm_timer tmp;
+
+ if (!strcmp(opt, "?")) {
+ show_available_alarms();
+ exit(0);
+ }
+
+ arg = qemu_strdup(opt);
+
+ /* Reorder the array */
+ name = strtok(arg, ",");
+ while (name) {
+ for (i = 0; i < count && alarm_timers[i].name; i++) {
+ if (!strcmp(alarm_timers[i].name, name))
+ break;
+ }
+
+ if (i == count) {
+ fprintf(stderr, "Unknown clock %s\n", name);
+ goto next;
+ }
+
+ if (i < cur)
+ /* Ignore */
+ goto next;
+
+ /* Swap */
+ tmp = alarm_timers[i];
+ alarm_timers[i] = alarm_timers[cur];
+ alarm_timers[cur] = tmp;
+
+ cur++;
+next:
+ name = strtok(NULL, ",");
+ }
+
+ qemu_free(arg);
+
+ if (cur) {
+ /* Disable remaining timers */
+ for (i = cur; i < count; i++)
+ alarm_timers[i].name = NULL;
+ } else {
+ show_available_alarms();
+ exit(1);
+ }
+}
+
+#define QEMU_NUM_CLOCKS 3
+
+QEMUClock *rt_clock;
+QEMUClock *vm_clock;
+QEMUClock *host_clock;
+
+static QEMUTimer *active_timers[QEMU_NUM_CLOCKS];
+
+static QEMUClock *qemu_new_clock(int type)
+{
+ QEMUClock *clock;
+ clock = qemu_mallocz(sizeof(QEMUClock));
+ clock->type = type;
+ clock->enabled = 1;
+ return clock;
+}
+
+void qemu_clock_enable(QEMUClock *clock, int enabled)
+{
+ clock->enabled = enabled;
+}
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque)
+{
+ QEMUTimer *ts;
+
+ ts = qemu_mallocz(sizeof(QEMUTimer));
+ ts->clock = clock;
+ ts->cb = cb;
+ ts->opaque = opaque;
+ return ts;
+}
+
+void qemu_free_timer(QEMUTimer *ts)
+{
+ qemu_free(ts);
+}
+
+/* stop a timer, but do not dealloc it */
+void qemu_del_timer(QEMUTimer *ts)
+{
+ QEMUTimer **pt, *t;
+
+ /* NOTE: this code must be signal safe because
+ qemu_timer_expired() can be called from a signal. */
+ pt = &active_timers[ts->clock->type];
+ for(;;) {
+ t = *pt;
+ if (!t)
+ break;
+ if (t == ts) {
+ *pt = t->next;
+ break;
+ }
+ pt = &t->next;
+ }
+}
+
+/* modify the current timer so that it will be fired when current_time
+ >= expire_time. The corresponding callback will be called. */
+void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
+{
+ QEMUTimer **pt, *t;
+
+ qemu_del_timer(ts);
+
+ /* add the timer in the sorted list */
+ /* NOTE: this code must be signal safe because
+ qemu_timer_expired() can be called from a signal. */
+ pt = &active_timers[ts->clock->type];
+ for(;;) {
+ t = *pt;
+ if (!t)
+ break;
+ if (t->expire_time > expire_time)
+ break;
+ pt = &t->next;
+ }
+ ts->expire_time = expire_time;
+ ts->next = *pt;
+ *pt = ts;
+
+ /* Rearm if necessary */
+ if (pt == &active_timers[ts->clock->type]) {
+ if (!alarm_timer->pending) {
+ qemu_rearm_alarm_timer(alarm_timer);
+ }
+ /* Interrupt execution to force deadline recalculation. */
+ if (use_icount)
+ qemu_notify_event();
+ }
+}
+
+int qemu_timer_pending(QEMUTimer *ts)
+{
+ QEMUTimer *t;
+ for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) {
+ if (t == ts)
+ return 1;
+ }
+ return 0;
+}
+
+int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time)
+{
+ if (!timer_head)
+ return 0;
+ return (timer_head->expire_time <= current_time);
+}
+
+static void qemu_run_timers(QEMUClock *clock)
+{
+ QEMUTimer **ptimer_head, *ts;
+ int64_t current_time;
+
+ if (!clock->enabled)
+ return;
+
+ current_time = qemu_get_clock (clock);
+ ptimer_head = &active_timers[clock->type];
+ for(;;) {
+ ts = *ptimer_head;
+ if (!ts || ts->expire_time > current_time)
+ break;
+ /* remove timer from the list before calling the callback */
+ *ptimer_head = ts->next;
+ ts->next = NULL;
+
+ /* run the callback (the timer list can be modified) */
+ ts->cb(ts->opaque);
+ }
+}
+
+int64_t qemu_get_clock(QEMUClock *clock)
+{
+ switch(clock->type) {
+ case QEMU_CLOCK_REALTIME:
+ return get_clock() / 1000000;
+ default:
+ case QEMU_CLOCK_VIRTUAL:
+ if (use_icount) {
+ return cpu_get_icount();
+ } else {
+ return cpu_get_clock();
+ }
+ case QEMU_CLOCK_HOST:
+ return get_clock_realtime();
+ }
+}
+
+int64_t qemu_get_clock_ns(QEMUClock *clock)
+{
+ switch(clock->type) {
+ case QEMU_CLOCK_REALTIME:
+ return get_clock();
+ default:
+ case QEMU_CLOCK_VIRTUAL:
+ if (use_icount) {
+ return cpu_get_icount();
+ } else {
+ return cpu_get_clock();
+ }
+ case QEMU_CLOCK_HOST:
+ return get_clock_realtime();
+ }
+}
+
+void init_clocks(void)
+{
+ rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME);
+ vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL);
+ host_clock = qemu_new_clock(QEMU_CLOCK_HOST);
+
+ rtc_clock = host_clock;
+}
+
+/* save a timer */
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
+{
+ uint64_t expire_time;
+
+ if (qemu_timer_pending(ts)) {
+ expire_time = ts->expire_time;
+ } else {
+ expire_time = -1;
+ }
+ qemu_put_be64(f, expire_time);
+}
+
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts)
+{
+ uint64_t expire_time;
+
+ expire_time = qemu_get_be64(f);
+ if (expire_time != -1) {
+ qemu_mod_timer(ts, expire_time);
+ } else {
+ qemu_del_timer(ts);
+ }
+}
+
+static const VMStateDescription vmstate_timers = {
+ .name = "timer",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT64(cpu_ticks_offset, TimersState),
+ VMSTATE_INT64(dummy, TimersState),
+ VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+void configure_icount(const char *option)
+{
+ vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
+ if (!option)
+ return;
+
+ if (strcmp(option, "auto") != 0) {
+ icount_time_shift = strtol(option, NULL, 0);
+ use_icount = 1;
+ return;
+ }
+
+ use_icount = 2;
+
+ /* 125MIPS seems a reasonable initial guess at the guest speed.
+ It will be corrected fairly quickly anyway. */
+ icount_time_shift = 3;
+
+ /* Have both realtime and virtual time triggers for speed adjustment.
+ The realtime trigger catches emulated time passing too slowly,
+ the virtual time trigger catches emulated time passing too fast.
+ Realtime triggers occur even when idle, so use them less frequently
+ than VM triggers. */
+ icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL);
+ qemu_mod_timer(icount_rt_timer,
+ qemu_get_clock(rt_clock) + 1000);
+ icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL);
+ qemu_mod_timer(icount_vm_timer,
+ qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10);
+}
+
+void qemu_run_all_timers(void)
+{
+ alarm_timer->pending = 0;
+
+ /* rearm timer, if not periodic */
+ if (alarm_timer->expired) {
+ alarm_timer->expired = 0;
+ qemu_rearm_alarm_timer(alarm_timer);
+ }
+
+ /* vm time timers */
+ if (vm_running) {
+ qemu_run_timers(vm_clock);
+ }
+
+ qemu_run_timers(rt_clock);
+ qemu_run_timers(host_clock);
+}
+
+static int64_t qemu_next_alarm_deadline(void);
+
+#ifdef _WIN32
+static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg,
+ DWORD_PTR dwUser, DWORD_PTR dw1,
+ DWORD_PTR dw2)
+#else
+static void host_alarm_handler(int host_signum)
+#endif
+{
+ struct qemu_alarm_timer *t = alarm_timer;
+ if (!t)
+ return;
+
+#if 0
+#define DISP_FREQ 1000
+ {
+ static int64_t delta_min = INT64_MAX;
+ static int64_t delta_max, delta_cum, last_clock, delta, ti;
+ static int count;
+ ti = qemu_get_clock(vm_clock);
+ if (last_clock != 0) {
+ delta = ti - last_clock;
+ if (delta < delta_min)
+ delta_min = delta;
+ if (delta > delta_max)
+ delta_max = delta;
+ delta_cum += delta;
+ if (++count == DISP_FREQ) {
+ printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n",
+ muldiv64(delta_min, 1000000, get_ticks_per_sec()),
+ muldiv64(delta_max, 1000000, get_ticks_per_sec()),
+ muldiv64(delta_cum, 1000000 / DISP_FREQ, get_ticks_per_sec()),
+ (double)get_ticks_per_sec() / ((double)delta_cum / DISP_FREQ));
+ count = 0;
+ delta_min = INT64_MAX;
+ delta_max = 0;
+ delta_cum = 0;
+ }
+ }
+ last_clock = ti;
+ }
+#endif
+ if (alarm_has_dynticks(t) ||
+ qemu_next_alarm_deadline () <= 0) {
+ t->expired = alarm_has_dynticks(t);
+ t->pending = 1;
+ qemu_notify_event();
+ }
+}
+
+int64_t qemu_next_deadline(void)
+{
+ /* To avoid problems with overflow limit this to 2^32. */
+ int64_t delta = INT32_MAX;
+
+ if (active_timers[QEMU_CLOCK_VIRTUAL]) {
+ delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time -
+ qemu_get_clock_ns(vm_clock);
+ }
+ if (active_timers[QEMU_CLOCK_HOST]) {
+ int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time -
+ qemu_get_clock_ns(host_clock);
+ if (hdelta < delta)
+ delta = hdelta;
+ }
+
+ if (delta < 0)
+ delta = 0;
+
+ return delta;
+}
+
+static int64_t qemu_next_alarm_deadline(void)
+{
+ int64_t delta;
+ int64_t rtdelta;
+
+ if (!use_icount && active_timers[QEMU_CLOCK_VIRTUAL]) {
+ delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time -
+ qemu_get_clock(vm_clock);
+ } else {
+ delta = INT32_MAX;
+ }
+ if (active_timers[QEMU_CLOCK_HOST]) {
+ int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time -
+ qemu_get_clock_ns(host_clock);
+ if (hdelta < delta)
+ delta = hdelta;
+ }
+ if (active_timers[QEMU_CLOCK_REALTIME]) {
+ rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time * 1000000 -
+ qemu_get_clock_ns(rt_clock));
+ if (rtdelta < delta)
+ delta = rtdelta;
+ }
+
+ return delta;
+}
+
+#if defined(__linux__)
+
+#define RTC_FREQ 1024
+
+static void enable_sigio_timer(int fd)
+{
+ struct sigaction act;
+
+ /* timer signal */
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = host_alarm_handler;
+
+ sigaction(SIGIO, &act, NULL);
+ fcntl_setfl(fd, O_ASYNC);
+ fcntl(fd, F_SETOWN, getpid());
+}
+
+static int hpet_start_timer(struct qemu_alarm_timer *t)
+{
+ struct hpet_info info;
+ int r, fd;
+
+ fd = qemu_open("/dev/hpet", O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ /* Set frequency */
+ r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ);
+ if (r < 0) {
+ fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n"
+ "error, but for better emulation accuracy type:\n"
+ "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n");
+ goto fail;
+ }
+
+ /* Check capabilities */
+ r = ioctl(fd, HPET_INFO, &info);
+ if (r < 0)
+ goto fail;
+
+ /* Enable periodic mode */
+ r = ioctl(fd, HPET_EPI, 0);
+ if (info.hi_flags && (r < 0))
+ goto fail;
+
+ /* Enable interrupt */
+ r = ioctl(fd, HPET_IE_ON, 0);
+ if (r < 0)
+ goto fail;
+
+ enable_sigio_timer(fd);
+ t->priv = (void *)(long)fd;
+
+ return 0;
+fail:
+ close(fd);
+ return -1;
+}
+
+static void hpet_stop_timer(struct qemu_alarm_timer *t)
+{
+ int fd = (long)t->priv;
+
+ close(fd);
+}
+
+static int rtc_start_timer(struct qemu_alarm_timer *t)
+{
+ int rtc_fd;
+ unsigned long current_rtc_freq = 0;
+
+ TFR(rtc_fd = qemu_open("/dev/rtc", O_RDONLY));
+ if (rtc_fd < 0)
+ return -1;
+ ioctl(rtc_fd, RTC_IRQP_READ, &current_rtc_freq);
+ if (current_rtc_freq != RTC_FREQ &&
+ ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) {
+ fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n"
+ "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n"
+ "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n");
+ goto fail;
+ }
+ if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) {
+ fail:
+ close(rtc_fd);
+ return -1;
+ }
+
+ enable_sigio_timer(rtc_fd);
+
+ t->priv = (void *)(long)rtc_fd;
+
+ return 0;
+}
+
+static void rtc_stop_timer(struct qemu_alarm_timer *t)
+{
+ int rtc_fd = (long)t->priv;
+
+ close(rtc_fd);
+}
+
+static int dynticks_start_timer(struct qemu_alarm_timer *t)
+{
+ struct sigevent ev;
+ timer_t host_timer;
+ struct sigaction act;
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = host_alarm_handler;
+
+ sigaction(SIGALRM, &act, NULL);
+
+ /*
+ * Initialize ev struct to 0 to avoid valgrind complaining
+ * about uninitialized data in timer_create call
+ */
+ memset(&ev, 0, sizeof(ev));
+ ev.sigev_value.sival_int = 0;
+ ev.sigev_notify = SIGEV_SIGNAL;
+ ev.sigev_signo = SIGALRM;
+
+ if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
+ perror("timer_create");
+
+ /* disable dynticks */
+ fprintf(stderr, "Dynamic Ticks disabled\n");
+
+ return -1;
+ }
+
+ t->priv = (void *)(long)host_timer;
+
+ return 0;
+}
+
+static void dynticks_stop_timer(struct qemu_alarm_timer *t)
+{
+ timer_t host_timer = (timer_t)(long)t->priv;
+
+ timer_delete(host_timer);
+}
+
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
+{
+ timer_t host_timer = (timer_t)(long)t->priv;
+ struct itimerspec timeout;
+ int64_t nearest_delta_ns = INT64_MAX;
+ int64_t current_ns;
+
+ assert(alarm_has_dynticks(t));
+ if (!active_timers[QEMU_CLOCK_REALTIME] &&
+ !active_timers[QEMU_CLOCK_VIRTUAL] &&
+ !active_timers[QEMU_CLOCK_HOST])
+ return;
+
+ nearest_delta_ns = qemu_next_alarm_deadline();
+ if (nearest_delta_ns < MIN_TIMER_REARM_NS)
+ nearest_delta_ns = MIN_TIMER_REARM_NS;
+
+ /* check whether a timer is already running */
+ if (timer_gettime(host_timer, &timeout)) {
+ perror("gettime");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
+ current_ns = timeout.it_value.tv_sec * 1000000000LL + timeout.it_value.tv_nsec;
+ if (current_ns && current_ns <= nearest_delta_ns)
+ return;
+
+ timeout.it_interval.tv_sec = 0;
+ timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
+ timeout.it_value.tv_sec = nearest_delta_ns / 1000000000;
+ timeout.it_value.tv_nsec = nearest_delta_ns % 1000000000;
+ if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) {
+ perror("settime");
+ fprintf(stderr, "Internal timer error: aborting\n");
+ exit(1);
+ }
+}
+
+#endif /* defined(__linux__) */
+
+#if !defined(_WIN32)
+
+static int unix_start_timer(struct qemu_alarm_timer *t)
+{
+ struct sigaction act;
+ struct itimerval itv;
+ int err;
+
+ /* timer signal */
+ sigfillset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = host_alarm_handler;
+
+ sigaction(SIGALRM, &act, NULL);
+
+ itv.it_interval.tv_sec = 0;
+ /* for i386 kernel 2.6 to get 1 ms */
+ itv.it_interval.tv_usec = 999;
+ itv.it_value.tv_sec = 0;
+ itv.it_value.tv_usec = 10 * 1000;
+
+ err = setitimer(ITIMER_REAL, &itv, NULL);
+ if (err)
+ return -1;
+
+ return 0;
+}
+
+static void unix_stop_timer(struct qemu_alarm_timer *t)
+{
+ struct itimerval itv;
+
+ memset(&itv, 0, sizeof(itv));
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+#endif /* !defined(_WIN32) */
+
+
+#ifdef _WIN32
+
+static int win32_start_timer(struct qemu_alarm_timer *t)
+{
+ TIMECAPS tc;
+ struct qemu_alarm_win32 *data = t->priv;
+ UINT flags;
+
+ memset(&tc, 0, sizeof(tc));
+ timeGetDevCaps(&tc, sizeof(tc));
+
+ data->period = tc.wPeriodMin;
+ timeBeginPeriod(data->period);
+
+ flags = TIME_CALLBACK_FUNCTION;
+ if (alarm_has_dynticks(t))
+ flags |= TIME_ONESHOT;
+ else
+ flags |= TIME_PERIODIC;
+
+ data->timerId = timeSetEvent(1, // interval (ms)
+ data->period, // resolution
+ host_alarm_handler, // function
+ (DWORD)t, // parameter
+ flags);
+
+ if (!data->timerId) {
+ fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n",
+ GetLastError());
+ timeEndPeriod(data->period);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void win32_stop_timer(struct qemu_alarm_timer *t)
+{
+ struct qemu_alarm_win32 *data = t->priv;
+
+ timeKillEvent(data->timerId);
+ timeEndPeriod(data->period);
+}
+
+static void win32_rearm_timer(struct qemu_alarm_timer *t)
+{
+ struct qemu_alarm_win32 *data = t->priv;
+
+ assert(alarm_has_dynticks(t));
+ if (!active_timers[QEMU_CLOCK_REALTIME] &&
+ !active_timers[QEMU_CLOCK_VIRTUAL] &&
+ !active_timers[QEMU_CLOCK_HOST])
+ return;
+
+ timeKillEvent(data->timerId);
+
+ data->timerId = timeSetEvent(1,
+ data->period,
+ host_alarm_handler,
+ (DWORD)t,
+ TIME_ONESHOT | TIME_CALLBACK_FUNCTION);
+
+ if (!data->timerId) {
+ fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n",
+ GetLastError());
+
+ timeEndPeriod(data->period);
+ exit(1);
+ }
+}
+
+#endif /* _WIN32 */
+
+static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason)
+{
+ if (running)
+ qemu_rearm_alarm_timer((struct qemu_alarm_timer *) opaque);
+}
+
+int init_timer_alarm(void)
+{
+ struct qemu_alarm_timer *t = NULL;
+ int i, err = -1;
+
+ for (i = 0; alarm_timers[i].name; i++) {
+ t = &alarm_timers[i];
+
+ err = t->start(t);
+ if (!err)
+ break;
+ }
+
+ if (err) {
+ err = -ENOENT;
+ goto fail;
+ }
+
+ /* first event is at time 0 */
+ t->pending = 1;
+ alarm_timer = t;
+ qemu_add_vm_change_state_handler(alarm_timer_on_change_state_rearm, t);
+
+ return 0;
+
+fail:
+ return err;
+}
+
+void quit_timers(void)
+{
+ struct qemu_alarm_timer *t = alarm_timer;
+ alarm_timer = NULL;
+ t->stop(t);
+}
+
+int qemu_calculate_timeout(void)
+{
+ int timeout;
+
+#define QEMUKVM 1
+#if defined (CONFIG_IOTHREAD) || defined (QEMUKVM)
+ /* When using icount, making forward progress with qemu_icount when the
+ guest CPU is idle is critical. We only use the static io-thread timeout
+ for non icount runs. */
+ if (!use_icount) {
+ return 1000;
+ }
+#endif
+
+ if (!vm_running)
+ timeout = 5000;
+ else {
+ /* XXX: use timeout computed from timers */
+ int64_t add;
+ int64_t delta;
+ /* Advance virtual time to the next event. */
+ delta = qemu_icount_delta();
+ if (delta > 0) {
+ /* If virtual time is ahead of real time then just
+ wait for IO. */
+ timeout = (delta + 999999) / 1000000;
+ } else {
+ /* Wait for either IO to occur or the next
+ timer event. */
+ add = qemu_next_deadline();
+ /* We advance the timer before checking for IO.
+ Limit the amount we advance so that early IO
+ activity won't get the guest too far ahead. */
+ if (add > 10000000)
+ add = 10000000;
+ delta += add;
+ qemu_icount += qemu_icount_round (add);
+ timeout = delta / 1000000;
+ if (timeout < 0)
+ timeout = 0;
+ }
+ }
+
+ return timeout;
+}
+
diff --git a/qemu-timer.h b/qemu-timer.h
new file mode 100644
index 0000000..8cd8f83
--- /dev/null
+++ b/qemu-timer.h
@@ -0,0 +1,313 @@
+#ifndef QEMU_TIMER_H
+#define QEMU_TIMER_H
+
+#include "qemu-common.h"
+#include <time.h>
+#include <sys/time.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
+/* timers */
+
+typedef struct QEMUClock QEMUClock;
+typedef void QEMUTimerCB(void *opaque);
+
+/* The real time clock should be used only for stuff which does not
+ change the virtual machine state, as it is run even if the virtual
+ machine is stopped. The real time clock has a frequency of 1000
+ Hz. */
+extern QEMUClock *rt_clock;
+
+/* The virtual clock is only run during the emulation. It is stopped
+ when the virtual machine is stopped. Virtual timers use a high
+ precision clock, usually cpu cycles (use ticks_per_sec). */
+extern QEMUClock *vm_clock;
+
+/* The host clock should be use for device models that emulate accurate
+ real time sources. It will continue to run when the virtual machine
+ is suspended, and it will reflect system time changes the host may
+ undergo (e.g. due to NTP). The host clock has the same precision as
+ the virtual clock. */
+extern QEMUClock *host_clock;
+
+int64_t qemu_get_clock(QEMUClock *clock);
+int64_t qemu_get_clock_ns(QEMUClock *clock);
+void qemu_clock_enable(QEMUClock *clock, int enabled);
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, 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);
+int qemu_timer_pending(QEMUTimer *ts);
+int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time);
+
+void qemu_run_all_timers(void);
+int qemu_alarm_pending(void);
+int64_t qemu_next_deadline(void);
+void configure_alarms(char const *opt);
+void configure_icount(const char *option);
+int qemu_calculate_timeout(void);
+void init_clocks(void);
+int init_timer_alarm(void);
+void quit_timers(void);
+
+static inline int64_t get_ticks_per_sec(void)
+{
+ return 1000000000LL;
+}
+
+/* real time host monotonic timer */
+static inline int64_t get_clock_realtime(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
+}
+
+/* Warning: don't insert tracepoints into these functions, they are
+ also used by simpletrace backend and tracepoints would cause
+ an infinite recursion! */
+#ifdef _WIN32
+extern int64_t clock_freq;
+
+static inline int64_t get_clock(void)
+{
+ LARGE_INTEGER ti;
+ QueryPerformanceCounter(&ti);
+ return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq);
+}
+
+#else
+
+extern int use_rt_clock;
+
+static inline int64_t get_clock(void)
+{
+#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \
+ || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+ if (use_rt_clock) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+ } else
+#endif
+ {
+ /* XXX: using gettimeofday leads to problems if the date
+ changes, so it should be avoided. */
+ return get_clock_realtime();
+ }
+}
+#endif
+
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts);
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts);
+
+/* ptimer.c */
+typedef struct ptimer_state ptimer_state;
+typedef void (*ptimer_cb)(void *opaque);
+
+ptimer_state *ptimer_init(QEMUBH *bh);
+void ptimer_set_period(ptimer_state *s, int64_t period);
+void ptimer_set_freq(ptimer_state *s, uint32_t freq);
+void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload);
+uint64_t ptimer_get_count(ptimer_state *s);
+void ptimer_set_count(ptimer_state *s, uint64_t count);
+void ptimer_run(ptimer_state *s, int oneshot);
+void ptimer_stop(ptimer_state *s);
+void qemu_put_ptimer(QEMUFile *f, ptimer_state *s);
+void qemu_get_ptimer(QEMUFile *f, ptimer_state *s);
+
+/* icount */
+int64_t qemu_icount_round(int64_t count);
+extern int64_t qemu_icount;
+extern int use_icount;
+extern int icount_time_shift;
+extern int64_t qemu_icount_bias;
+int64_t cpu_get_icount(void);
+
+/*******************************************/
+/* host CPU ticks (if available) */
+
+#if defined(_ARCH_PPC)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t retval;
+#ifdef _ARCH_PPC64
+ /* This reads timebase in one 64bit go and includes Cell workaround from:
+ http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html
+ */
+ __asm__ __volatile__ ("mftb %0\n\t"
+ "cmpwi %0,0\n\t"
+ "beq- $-8"
+ : "=r" (retval));
+#else
+ /* http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html */
+ unsigned long junk;
+ __asm__ __volatile__ ("mfspr %1,269\n\t" /* mftbu */
+ "mfspr %L0,268\n\t" /* mftb */
+ "mfspr %0,269\n\t" /* mftbu */
+ "cmpw %0,%1\n\t"
+ "bne $-16"
+ : "=r" (retval), "=r" (junk));
+#endif
+ return retval;
+}
+
+#elif defined(__i386__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t val;
+ asm volatile ("rdtsc" : "=A" (val));
+ return val;
+}
+
+#elif defined(__x86_64__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ uint32_t low,high;
+ int64_t val;
+ asm volatile("rdtsc" : "=a" (low), "=d" (high));
+ val = high;
+ val <<= 32;
+ val |= low;
+ return val;
+}
+
+#elif defined(__hppa__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int val;
+ asm volatile ("mfctl %%cr16, %0" : "=r"(val));
+ return val;
+}
+
+#elif defined(__ia64)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t val;
+ asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory");
+ return val;
+}
+
+#elif defined(__s390__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ int64_t val;
+ asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc");
+ return val;
+}
+
+#elif defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || defined(__sparc_v9__)
+
+static inline int64_t cpu_get_real_ticks (void)
+{
+#if defined(_LP64)
+ uint64_t rval;
+ asm volatile("rd %%tick,%0" : "=r"(rval));
+ return rval;
+#else
+ union {
+ uint64_t i64;
+ struct {
+ uint32_t high;
+ uint32_t low;
+ } i32;
+ } rval;
+ asm volatile("rd %%tick,%1; srlx %1,32,%0"
+ : "=r"(rval.i32.high), "=r"(rval.i32.low));
+ return rval.i64;
+#endif
+}
+
+#elif defined(__mips__) && \
+ ((defined(__mips_isa_rev) && __mips_isa_rev >= 2) || defined(__linux__))
+/*
+ * binutils wants to use rdhwr only on mips32r2
+ * but as linux kernel emulate it, it's fine
+ * to use it.
+ *
+ */
+#define MIPS_RDHWR(rd, value) { \
+ __asm__ __volatile__ (".set push\n\t" \
+ ".set mips32r2\n\t" \
+ "rdhwr %0, "rd"\n\t" \
+ ".set pop" \
+ : "=r" (value)); \
+ }
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ /* On kernels >= 2.6.25 rdhwr <reg>, $2 and $3 are emulated */
+ uint32_t count;
+ static uint32_t cyc_per_count = 0;
+
+ if (!cyc_per_count) {
+ MIPS_RDHWR("$3", cyc_per_count);
+ }
+
+ MIPS_RDHWR("$2", count);
+ return (int64_t)(count * cyc_per_count);
+}
+
+#elif defined(__alpha__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+ uint64_t cc;
+ uint32_t cur, ofs;
+
+ asm volatile("rpcc %0" : "=r"(cc));
+ cur = cc;
+ ofs = cc >> 32;
+ return cur - ofs;
+}
+
+#else
+/* The host CPU doesn't have an easily accessible cycle counter.
+ Just return a monotonically increasing value. This will be
+ totally wrong, but hopefully better than nothing. */
+static inline int64_t cpu_get_real_ticks (void)
+{
+ static int64_t ticks = 0;
+ return ticks++;
+}
+#endif
+
+#ifdef NEED_CPU_H
+/* Deterministic execution requires that IO only be performed on the last
+ instruction of a TB so that interrupts take effect immediately. */
+static inline int can_do_io(CPUState *env)
+{
+ if (!use_icount)
+ return 1;
+
+ /* If not executing code then assume we are ok. */
+ if (!env->current_tb)
+ return 1;
+
+ return env->can_do_io != 0;
+}
+#endif
+
+#ifdef CONFIG_PROFILER
+static inline int64_t profile_getclock(void)
+{
+ return cpu_get_real_ticks();
+}
+
+extern int64_t qemu_time, qemu_time_start;
+extern int64_t tlb_flush_time;
+extern int64_t dev_time;
+#endif
+
+#endif
diff --git a/qemu-tool.c b/qemu-tool.c
new file mode 100644
index 0000000..392e1c9
--- /dev/null
+++ b/qemu-tool.c
@@ -0,0 +1,113 @@
+/*
+ * Compatibility for qemu-img/qemu-nbd
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "monitor.h"
+#include "qemu-timer.h"
+#include "qemu-log.h"
+#include "sysemu.h"
+
+#include <sys/time.h>
+
+QEMUClock *rt_clock;
+
+FILE *logfile;
+
+struct QEMUBH
+{
+ QEMUBHFunc *cb;
+ void *opaque;
+};
+
+void qemu_service_io(void)
+{
+}
+
+Monitor *cur_mon;
+
+int monitor_cur_is_qmp(void)
+{
+ return 0;
+}
+
+void monitor_set_error(Monitor *mon, QError *qerror)
+{
+}
+
+void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+{
+}
+
+void monitor_printf(Monitor *mon, const char *fmt, ...)
+{
+}
+
+void monitor_print_filename(Monitor *mon, const char *filename)
+{
+}
+
+void async_context_push(void)
+{
+}
+
+void async_context_pop(void)
+{
+}
+
+int get_async_context_id(void)
+{
+ return 0;
+}
+
+void monitor_protocol_event(MonitorEvent event, QObject *data)
+{
+}
+
+QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
+{
+ QEMUBH *bh;
+
+ bh = qemu_malloc(sizeof(*bh));
+ bh->cb = cb;
+ bh->opaque = opaque;
+
+ return bh;
+}
+
+int qemu_bh_poll(void)
+{
+ return 0;
+}
+
+void qemu_bh_schedule(QEMUBH *bh)
+{
+ bh->cb(bh->opaque);
+}
+
+void qemu_bh_cancel(QEMUBH *bh)
+{
+}
+
+void qemu_bh_delete(QEMUBH *bh)
+{
+ qemu_free(bh);
+}
+
+int qemu_set_fd_handler2(int fd,
+ IOCanReadHandler *fd_read_poll,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque)
+{
+ return 0;
+}
diff --git a/qemu-x509.h b/qemu-x509.h
new file mode 100644
index 0000000..095aec1
--- /dev/null
+++ b/qemu-x509.h
@@ -0,0 +1,9 @@
+#ifndef QEMU_X509_H
+#define QEMU_X509_H
+
+#define X509_CA_CERT_FILE "ca-cert.pem"
+#define X509_CA_CRL_FILE "ca-crl.pem"
+#define X509_SERVER_KEY_FILE "server-key.pem"
+#define X509_SERVER_CERT_FILE "server-cert.pem"
+
+#endif /* QEMU_X509_H */
diff --git a/qemu.sasl b/qemu.sasl
new file mode 100644
index 0000000..cf19cf8
--- /dev/null
+++ b/qemu.sasl
@@ -0,0 +1,34 @@
+# If you want to use the non-TLS socket, then you *must* include
+# the GSSAPI or DIGEST-MD5 mechanisms, because they are the only
+# ones that can offer session encryption as well as authentication.
+#
+# If you're only using TLS, then you can turn on any mechanisms
+# you like for authentication, because TLS provides the encryption
+#
+# Default to a simple username+password mechanism
+# NB digest-md5 is no longer considered secure by current standards
+mech_list: digest-md5
+
+# Before you can use GSSAPI, you need a service principle on the
+# KDC server for libvirt, and that to be exported to the keytab
+# file listed below
+#mech_list: gssapi
+#
+# You can also list many mechanisms at once, then the user can choose
+# by adding '?auth=sasl.gssapi' to their libvirt URI, eg
+# qemu+tcp://hostname/system?auth=sasl.gssapi
+#mech_list: digest-md5 gssapi
+
+# Some older builds of MIT kerberos on Linux ignore this option &
+# instead need KRB5_KTNAME env var.
+# For modern Linux, and other OS, this should be sufficient
+keytab: /etc/qemu/krb5.tab
+
+# If using digest-md5 for username/passwds, then this is the file
+# containing the passwds. Use 'saslpasswd2 -a qemu [username]'
+# to add entries, and 'sasldblistusers2 -a qemu' to browse it
+sasldb_path: /etc/qemu/passwd.db
+
+
+auxprop_plugin: sasldb
+
diff --git a/qemu_socket.h b/qemu_socket.h
new file mode 100644
index 0000000..897a8ae
--- /dev/null
+++ b/qemu_socket.h
@@ -0,0 +1,62 @@
+/* headers to use the BSD sockets */
+#ifndef QEMU_SOCKET_H
+#define QEMU_SOCKET_H
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#define socket_error() WSAGetLastError()
+#undef EINTR
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINTR WSAEINTR
+#define EINPROGRESS WSAEINPROGRESS
+
+int inet_aton(const char *cp, struct in_addr *ia);
+
+#else
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#define socket_error() errno
+#define closesocket(s) close(s)
+
+#endif /* !_WIN32 */
+
+#include "qemu-option.h"
+
+/* misc helpers */
+int qemu_socket(int domain, int type, int protocol);
+int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+void socket_set_nonblock(int fd);
+int send_all(int fd, const void *buf, int len1);
+
+/* New, ipv6-ready socket helper functions, see qemu-sockets.c */
+int inet_listen_opts(QemuOpts *opts, int port_offset);
+int inet_listen(const char *str, char *ostr, int olen,
+ int socktype, int port_offset);
+int inet_connect_opts(QemuOpts *opts);
+int inet_connect(const char *str, int socktype);
+int inet_dgram_opts(QemuOpts *opts);
+const char *inet_strfamily(int family);
+
+int unix_listen_opts(QemuOpts *opts);
+int unix_listen(const char *path, char *ostr, int olen);
+int unix_connect_opts(QemuOpts *opts);
+int unix_connect(const char *path);
+
+/* Old, ipv4 only bits. Don't use for new code. */
+int parse_host_port(struct sockaddr_in *saddr, const char *str);
+int parse_host_src_port(struct sockaddr_in *haddr,
+ struct sockaddr_in *saddr,
+ const char *str);
+int socket_init(void);
+
+#endif /* QEMU_SOCKET_H */
diff --git a/qerror.c b/qerror.c
new file mode 100644
index 0000000..4855604
--- /dev/null
+++ b/qerror.c
@@ -0,0 +1,463 @@
+/*
+ * QError Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "monitor.h"
+#include "qjson.h"
+#include "qerror.h"
+#include "qemu-common.h"
+
+static void qerror_destroy_obj(QObject *obj);
+
+static const QType qerror_type = {
+ .code = QTYPE_QERROR,
+ .destroy = qerror_destroy_obj,
+};
+
+/**
+ * The 'desc' parameter is a printf-like string, the format of the format
+ * string is:
+ *
+ * %(KEY)
+ *
+ * Where KEY is a QDict key, which has to be passed to qerror_from_info().
+ *
+ * Example:
+ *
+ * "foo error on device: %(device) slot: %(slot_nr)"
+ *
+ * A single percent sign can be printed if followed by a second one,
+ * for example:
+ *
+ * "running out of foo: %(foo)%%"
+ *
+ * Please keep the entries in alphabetical order.
+ * Use "sed -n '/^static.*qerror_table\[\]/,/^};/s/QERR_/&/gp' qerror.c | sort -c"
+ * to check.
+ */
+static const QErrorStringTable qerror_table[] = {
+ {
+ .error_fmt = QERR_BAD_BUS_FOR_DEVICE,
+ .desc = "Device '%(device)' can't go on a %(bad_bus_type) bus",
+ },
+ {
+ .error_fmt = QERR_BUS_NOT_FOUND,
+ .desc = "Bus '%(bus)' not found",
+ },
+ {
+ .error_fmt = QERR_BUS_NO_HOTPLUG,
+ .desc = "Bus '%(bus)' does not support hotplugging",
+ },
+ {
+ .error_fmt = QERR_COMMAND_NOT_FOUND,
+ .desc = "The command %(name) has not been found",
+ },
+ {
+ .error_fmt = QERR_DEVICE_ENCRYPTED,
+ .desc = "Device '%(device)' is encrypted",
+ },
+ {
+ .error_fmt = QERR_DEVICE_INIT_FAILED,
+ .desc = "Device '%(device)' could not be initialized",
+ },
+ {
+ .error_fmt = QERR_DEVICE_IN_USE,
+ .desc = "Device '%(device)' is in use",
+ },
+ {
+ .error_fmt = QERR_DEVICE_LOCKED,
+ .desc = "Device '%(device)' is locked",
+ },
+ {
+ .error_fmt = QERR_DEVICE_MULTIPLE_BUSSES,
+ .desc = "Device '%(device)' has multiple child busses",
+ },
+ {
+ .error_fmt = QERR_DEVICE_NOT_ACTIVE,
+ .desc = "Device '%(device)' has not been activated",
+ },
+ {
+ .error_fmt = QERR_DEVICE_NOT_ENCRYPTED,
+ .desc = "Device '%(device)' is not encrypted",
+ },
+ {
+ .error_fmt = QERR_DEVICE_NOT_FOUND,
+ .desc = "Device '%(device)' not found",
+ },
+ {
+ .error_fmt = QERR_DEVICE_NOT_REMOVABLE,
+ .desc = "Device '%(device)' is not removable",
+ },
+ {
+ .error_fmt = QERR_DEVICE_NO_BUS,
+ .desc = "Device '%(device)' has no child bus",
+ },
+ {
+ .error_fmt = QERR_DEVICE_NO_HOTPLUG,
+ .desc = "Device '%(device)' does not support hotplugging",
+ },
+ {
+ .error_fmt = QERR_DUPLICATE_ID,
+ .desc = "Duplicate ID '%(id)' for %(object)",
+ },
+ {
+ .error_fmt = QERR_FD_NOT_FOUND,
+ .desc = "File descriptor named '%(name)' not found",
+ },
+ {
+ .error_fmt = QERR_FD_NOT_SUPPLIED,
+ .desc = "No file descriptor supplied via SCM_RIGHTS",
+ },
+ {
+ .error_fmt = QERR_INVALID_BLOCK_FORMAT,
+ .desc = "Invalid block format '%(name)'",
+ },
+ {
+ .error_fmt = QERR_INVALID_PARAMETER,
+ .desc = "Invalid parameter '%(name)'",
+ },
+ {
+ .error_fmt = QERR_INVALID_PARAMETER_TYPE,
+ .desc = "Invalid parameter type, expected: %(expected)",
+ },
+ {
+ .error_fmt = QERR_INVALID_PARAMETER_VALUE,
+ .desc = "Parameter '%(name)' expects %(expected)",
+ },
+ {
+ .error_fmt = QERR_INVALID_PASSWORD,
+ .desc = "Password incorrect",
+ },
+ {
+ .error_fmt = QERR_JSON_PARSING,
+ .desc = "Invalid JSON syntax",
+ },
+ {
+ .error_fmt = QERR_KVM_MISSING_CAP,
+ .desc = "Using KVM without %(capability), %(feature) unavailable",
+ },
+ {
+ .error_fmt = QERR_MIGRATION_EXPECTED,
+ .desc = "An incoming migration is expected before this command can be executed",
+ },
+ {
+ .error_fmt = QERR_MISSING_PARAMETER,
+ .desc = "Parameter '%(name)' is missing",
+ },
+ {
+ .error_fmt = QERR_NO_BUS_FOR_DEVICE,
+ .desc = "No '%(bus)' bus found for device '%(device)'",
+ },
+ {
+ .error_fmt = QERR_OPEN_FILE_FAILED,
+ .desc = "Could not open '%(filename)'",
+ },
+ {
+ .error_fmt = QERR_PROPERTY_NOT_FOUND,
+ .desc = "Property '%(device).%(property)' not found",
+ },
+ {
+ .error_fmt = QERR_PROPERTY_VALUE_BAD,
+ .desc = "Property '%(device).%(property)' doesn't take value '%(value)'",
+ },
+ {
+ .error_fmt = QERR_PROPERTY_VALUE_IN_USE,
+ .desc = "Property '%(device).%(property)' can't take value '%(value)', it's in use",
+ },
+ {
+ .error_fmt = QERR_PROPERTY_VALUE_NOT_FOUND,
+ .desc = "Property '%(device).%(property)' can't find value '%(value)'",
+ },
+ {
+ .error_fmt = QERR_QMP_BAD_INPUT_OBJECT,
+ .desc = "Expected '%(expected)' in QMP input",
+ },
+ {
+ .error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
+ .desc = "QMP input object member '%(member)' expects '%(expected)'",
+ },
+ {
+ .error_fmt = QERR_QMP_EXTRA_MEMBER,
+ .desc = "QMP input object member '%(member)' is unexpected",
+ },
+ {
+ .error_fmt = QERR_SET_PASSWD_FAILED,
+ .desc = "Could not set password",
+ },
+ {
+ .error_fmt = QERR_TOO_MANY_FILES,
+ .desc = "Too many open files",
+ },
+ {
+ .error_fmt = QERR_UNDEFINED_ERROR,
+ .desc = "An undefined error has ocurred",
+ },
+ {
+ .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ .desc = "'%(device)' uses a %(format) feature which is not "
+ "supported by this qemu version: %(feature)",
+ },
+ {
+ .error_fmt = QERR_VNC_SERVER_FAILED,
+ .desc = "Could not start VNC server on %(target)",
+ },
+ {}
+};
+
+/**
+ * qerror_new(): Create a new QError
+ *
+ * Return strong reference.
+ */
+QError *qerror_new(void)
+{
+ QError *qerr;
+
+ qerr = qemu_mallocz(sizeof(*qerr));
+ QOBJECT_INIT(qerr, &qerror_type);
+
+ return qerr;
+}
+
+static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func);
+ fprintf(stderr, "qerror: -> ");
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr);
+ abort();
+}
+
+static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr,
+ const char *fmt, va_list *va)
+{
+ QObject *obj;
+
+ obj = qobject_from_jsonv(fmt, va);
+ if (!obj) {
+ qerror_abort(qerr, "invalid format '%s'", fmt);
+ }
+ if (qobject_type(obj) != QTYPE_QDICT) {
+ qerror_abort(qerr, "error format is not a QDict '%s'", fmt);
+ }
+
+ qerr->error = qobject_to_qdict(obj);
+
+ obj = qdict_get(qerr->error, "class");
+ if (!obj) {
+ qerror_abort(qerr, "missing 'class' key in '%s'", fmt);
+ }
+ if (qobject_type(obj) != QTYPE_QSTRING) {
+ qerror_abort(qerr, "'class' key value should be a QString");
+ }
+
+ obj = qdict_get(qerr->error, "data");
+ if (!obj) {
+ qerror_abort(qerr, "missing 'data' key in '%s'", fmt);
+ }
+ if (qobject_type(obj) != QTYPE_QDICT) {
+ qerror_abort(qerr, "'data' key value should be a QDICT");
+ }
+}
+
+static void qerror_set_desc(QError *qerr, const char *fmt)
+{
+ int i;
+
+ // FIXME: inefficient loop
+
+ for (i = 0; qerror_table[i].error_fmt; i++) {
+ if (strcmp(qerror_table[i].error_fmt, fmt) == 0) {
+ qerr->entry = &qerror_table[i];
+ return;
+ }
+ }
+
+ qerror_abort(qerr, "error format '%s' not found", fmt);
+}
+
+/**
+ * qerror_from_info(): Create a new QError from error information
+ *
+ * The information consists of:
+ *
+ * - file the file name of where the error occurred
+ * - linenr the line number of where the error occurred
+ * - func the function name of where the error occurred
+ * - fmt JSON printf-like dictionary, there must exist keys 'class' and
+ * 'data'
+ * - va va_list of all arguments specified by fmt
+ *
+ * Return strong reference.
+ */
+QError *qerror_from_info(const char *file, int linenr, const char *func,
+ const char *fmt, va_list *va)
+{
+ QError *qerr;
+
+ qerr = qerror_new();
+ loc_save(&qerr->loc);
+ qerr->linenr = linenr;
+ qerr->file = file;
+ qerr->func = func;
+
+ if (!fmt) {
+ qerror_abort(qerr, "QDict not specified");
+ }
+
+ qerror_set_data(qerr, fmt, va);
+ qerror_set_desc(qerr, fmt);
+
+ return qerr;
+}
+
+static void parse_error(const QError *qerror, int c)
+{
+ qerror_abort(qerror, "expected '%c' in '%s'", c, qerror->entry->desc);
+}
+
+static const char *append_field(QString *outstr, const QError *qerror,
+ const char *start)
+{
+ QObject *obj;
+ QDict *qdict;
+ QString *key_qs;
+ const char *end, *key;
+
+ if (*start != '%')
+ parse_error(qerror, '%');
+ start++;
+ if (*start != '(')
+ parse_error(qerror, '(');
+ start++;
+
+ end = strchr(start, ')');
+ if (!end)
+ parse_error(qerror, ')');
+
+ key_qs = qstring_from_substr(start, 0, end - start - 1);
+ key = qstring_get_str(key_qs);
+
+ qdict = qobject_to_qdict(qdict_get(qerror->error, "data"));
+ obj = qdict_get(qdict, key);
+ if (!obj) {
+ qerror_abort(qerror, "key '%s' not found in QDict", key);
+ }
+
+ switch (qobject_type(obj)) {
+ case QTYPE_QSTRING:
+ qstring_append(outstr, qdict_get_str(qdict, key));
+ break;
+ case QTYPE_QINT:
+ qstring_append_int(outstr, qdict_get_int(qdict, key));
+ break;
+ default:
+ qerror_abort(qerror, "invalid type '%c'", qobject_type(obj));
+ }
+
+ QDECREF(key_qs);
+ return ++end;
+}
+
+/**
+ * qerror_human(): Format QError data into human-readable string.
+ *
+ * Formats according to member 'desc' of the specified QError object.
+ */
+QString *qerror_human(const QError *qerror)
+{
+ const char *p;
+ QString *qstring;
+
+ assert(qerror->entry != NULL);
+
+ qstring = qstring_new();
+
+ for (p = qerror->entry->desc; *p != '\0';) {
+ if (*p != '%') {
+ qstring_append_chr(qstring, *p++);
+ } else if (*(p + 1) == '%') {
+ qstring_append_chr(qstring, '%');
+ p += 2;
+ } else {
+ p = append_field(qstring, qerror, p);
+ }
+ }
+
+ return qstring;
+}
+
+/**
+ * qerror_print(): Print QError data
+ *
+ * This function will print the member 'desc' of the specified QError object,
+ * it uses error_report() for this, so that the output is routed to the right
+ * place (ie. stderr or Monitor's device).
+ */
+void qerror_print(QError *qerror)
+{
+ QString *qstring = qerror_human(qerror);
+ loc_push_restore(&qerror->loc);
+ error_report("%s", qstring_get_str(qstring));
+ loc_pop(&qerror->loc);
+ QDECREF(qstring);
+}
+
+void qerror_report_internal(const char *file, int linenr, const char *func,
+ const char *fmt, ...)
+{
+ va_list va;
+ QError *qerror;
+
+ va_start(va, fmt);
+ qerror = qerror_from_info(file, linenr, func, fmt, &va);
+ va_end(va);
+
+ if (monitor_cur_is_qmp()) {
+ monitor_set_error(cur_mon, qerror);
+ } else {
+ qerror_print(qerror);
+ QDECREF(qerror);
+ }
+}
+
+/**
+ * qobject_to_qerror(): Convert a QObject into a QError
+ */
+QError *qobject_to_qerror(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QERROR) {
+ return NULL;
+ }
+
+ return container_of(obj, QError, base);
+}
+
+/**
+ * qerror_destroy_obj(): Free all memory allocated by a QError
+ */
+static void qerror_destroy_obj(QObject *obj)
+{
+ QError *qerr;
+
+ assert(obj != NULL);
+ qerr = qobject_to_qerror(obj);
+
+ QDECREF(qerr->error);
+ qemu_free(qerr);
+}
diff --git a/qerror.h b/qerror.h
new file mode 100644
index 0000000..f732d45
--- /dev/null
+++ b/qerror.h
@@ -0,0 +1,174 @@
+/*
+ * QError Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+#ifndef QERROR_H
+#define QERROR_H
+
+#include "qdict.h"
+#include "qstring.h"
+#include "qemu-error.h"
+#include <stdarg.h>
+
+typedef struct QErrorStringTable {
+ const char *desc;
+ const char *error_fmt;
+} QErrorStringTable;
+
+typedef struct QError {
+ QObject_HEAD;
+ QDict *error;
+ Location loc;
+ int linenr;
+ const char *file;
+ const char *func;
+ const QErrorStringTable *entry;
+} QError;
+
+QError *qerror_new(void);
+QError *qerror_from_info(const char *file, int linenr, const char *func,
+ const char *fmt, va_list *va) GCC_FMT_ATTR(4, 0);
+QString *qerror_human(const QError *qerror);
+void qerror_print(QError *qerror);
+void qerror_report_internal(const char *file, int linenr, const char *func,
+ const char *fmt, ...) GCC_FMT_ATTR(4, 5);
+#define qerror_report(fmt, ...) \
+ qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__)
+QError *qobject_to_qerror(const QObject *obj);
+
+/*
+ * QError class list
+ * Please keep the definitions in alphabetical order.
+ * Use "grep '^#define QERR_' qerror.h | sort -c" to check.
+ */
+#define QERR_BAD_BUS_FOR_DEVICE \
+ "{ 'class': 'BadBusForDevice', 'data': { 'device': %s, 'bad_bus_type': %s } }"
+
+#define QERR_BUS_NOT_FOUND \
+ "{ 'class': 'BusNotFound', 'data': { 'bus': %s } }"
+
+#define QERR_BUS_NO_HOTPLUG \
+ "{ 'class': 'BusNoHotplug', 'data': { 'bus': %s } }"
+
+#define QERR_COMMAND_NOT_FOUND \
+ "{ 'class': 'CommandNotFound', 'data': { 'name': %s } }"
+
+#define QERR_DEVICE_ENCRYPTED \
+ "{ 'class': 'DeviceEncrypted', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_INIT_FAILED \
+ "{ 'class': 'DeviceInitFailed', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_IN_USE \
+ "{ 'class': 'DeviceInUse', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_LOCKED \
+ "{ 'class': 'DeviceLocked', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_MULTIPLE_BUSSES \
+ "{ 'class': 'DeviceMultipleBusses', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_NOT_ACTIVE \
+ "{ 'class': 'DeviceNotActive', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_NOT_ENCRYPTED \
+ "{ 'class': 'DeviceNotEncrypted', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_NOT_FOUND \
+ "{ 'class': 'DeviceNotFound', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_NOT_REMOVABLE \
+ "{ 'class': 'DeviceNotRemovable', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_NO_BUS \
+ "{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }"
+
+#define QERR_DEVICE_NO_HOTPLUG \
+ "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }"
+
+#define QERR_DUPLICATE_ID \
+ "{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }"
+
+#define QERR_FD_NOT_FOUND \
+ "{ 'class': 'FdNotFound', 'data': { 'name': %s } }"
+
+#define QERR_FD_NOT_SUPPLIED \
+ "{ 'class': 'FdNotSupplied', 'data': {} }"
+
+#define QERR_INVALID_BLOCK_FORMAT \
+ "{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }"
+
+#define QERR_INVALID_PARAMETER \
+ "{ 'class': 'InvalidParameter', 'data': { 'name': %s } }"
+
+#define QERR_INVALID_PARAMETER_TYPE \
+ "{ 'class': 'InvalidParameterType', 'data': { 'name': %s,'expected': %s } }"
+
+#define QERR_INVALID_PARAMETER_VALUE \
+ "{ 'class': 'InvalidParameterValue', 'data': { 'name': %s, 'expected': %s } }"
+
+#define QERR_INVALID_PASSWORD \
+ "{ 'class': 'InvalidPassword', 'data': {} }"
+
+#define QERR_JSON_PARSING \
+ "{ 'class': 'JSONParsing', 'data': {} }"
+
+#define QERR_KVM_MISSING_CAP \
+ "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }"
+
+#define QERR_MIGRATION_EXPECTED \
+ "{ 'class': 'MigrationExpected', 'data': {} }"
+
+#define QERR_MISSING_PARAMETER \
+ "{ 'class': 'MissingParameter', 'data': { 'name': %s } }"
+
+#define QERR_NO_BUS_FOR_DEVICE \
+ "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }"
+
+#define QERR_OPEN_FILE_FAILED \
+ "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
+
+#define QERR_PROPERTY_NOT_FOUND \
+ "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }"
+
+#define QERR_PROPERTY_VALUE_BAD \
+ "{ 'class': 'PropertyValueBad', 'data': { 'device': %s, 'property': %s, 'value': %s } }"
+
+#define QERR_PROPERTY_VALUE_IN_USE \
+ "{ 'class': 'PropertyValueInUse', 'data': { 'device': %s, 'property': %s, 'value': %s } }"
+
+#define QERR_PROPERTY_VALUE_NOT_FOUND \
+ "{ 'class': 'PropertyValueNotFound', 'data': { 'device': %s, 'property': %s, 'value': %s } }"
+
+#define QERR_QMP_BAD_INPUT_OBJECT \
+ "{ 'class': 'QMPBadInputObject', 'data': { 'expected': %s } }"
+
+#define QERR_QMP_BAD_INPUT_OBJECT_MEMBER \
+ "{ 'class': 'QMPBadInputObjectMember', 'data': { 'member': %s, 'expected': %s } }"
+
+#define QERR_QMP_EXTRA_MEMBER \
+ "{ 'class': 'QMPExtraInputObjectMember', 'data': { 'member': %s } }"
+
+#define QERR_SET_PASSWD_FAILED \
+ "{ 'class': 'SetPasswdFailed', 'data': {} }"
+
+#define QERR_TOO_MANY_FILES \
+ "{ 'class': 'TooManyFiles', 'data': {} }"
+
+#define QERR_UNDEFINED_ERROR \
+ "{ 'class': 'UndefinedError', 'data': {} }"
+
+#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \
+ "{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }"
+
+#define QERR_VNC_SERVER_FAILED \
+ "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"
+
+#endif /* QERROR_H */
diff --git a/qfloat.c b/qfloat.c
new file mode 100644
index 0000000..f8c8a2e
--- /dev/null
+++ b/qfloat.c
@@ -0,0 +1,68 @@
+/*
+ * QFloat Module
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qfloat.h"
+#include "qobject.h"
+#include "qemu-common.h"
+
+static void qfloat_destroy_obj(QObject *obj);
+
+static const QType qfloat_type = {
+ .code = QTYPE_QFLOAT,
+ .destroy = qfloat_destroy_obj,
+};
+
+/**
+ * qfloat_from_int(): Create a new QFloat from a float
+ *
+ * Return strong reference.
+ */
+QFloat *qfloat_from_double(double value)
+{
+ QFloat *qf;
+
+ qf = qemu_malloc(sizeof(*qf));
+ qf->value = value;
+ QOBJECT_INIT(qf, &qfloat_type);
+
+ return qf;
+}
+
+/**
+ * qfloat_get_double(): Get the stored float
+ */
+double qfloat_get_double(const QFloat *qf)
+{
+ return qf->value;
+}
+
+/**
+ * qobject_to_qfloat(): Convert a QObject into a QFloat
+ */
+QFloat *qobject_to_qfloat(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QFLOAT)
+ return NULL;
+
+ return container_of(obj, QFloat, base);
+}
+
+/**
+ * qfloat_destroy_obj(): Free all memory allocated by a
+ * QFloat object
+ */
+static void qfloat_destroy_obj(QObject *obj)
+{
+ assert(obj != NULL);
+ qemu_free(qobject_to_qfloat(obj));
+}
diff --git a/qfloat.h b/qfloat.h
new file mode 100644
index 0000000..9d67876
--- /dev/null
+++ b/qfloat.h
@@ -0,0 +1,29 @@
+/*
+ * QFloat Module
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QFLOAT_H
+#define QFLOAT_H
+
+#include <stdint.h>
+#include "qobject.h"
+
+typedef struct QFloat {
+ QObject_HEAD;
+ double value;
+} QFloat;
+
+QFloat *qfloat_from_double(double value);
+double qfloat_get_double(const QFloat *qi);
+QFloat *qobject_to_qfloat(const QObject *obj);
+
+#endif /* QFLOAT_H */
diff --git a/qint.c b/qint.c
new file mode 100644
index 0000000..fb3823a
--- /dev/null
+++ b/qint.c
@@ -0,0 +1,67 @@
+/*
+ * QInt Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qint.h"
+#include "qobject.h"
+#include "qemu-common.h"
+
+static void qint_destroy_obj(QObject *obj);
+
+static const QType qint_type = {
+ .code = QTYPE_QINT,
+ .destroy = qint_destroy_obj,
+};
+
+/**
+ * qint_from_int(): Create a new QInt from an int64_t
+ *
+ * Return strong reference.
+ */
+QInt *qint_from_int(int64_t value)
+{
+ QInt *qi;
+
+ qi = qemu_malloc(sizeof(*qi));
+ qi->value = value;
+ QOBJECT_INIT(qi, &qint_type);
+
+ return qi;
+}
+
+/**
+ * qint_get_int(): Get the stored integer
+ */
+int64_t qint_get_int(const QInt *qi)
+{
+ return qi->value;
+}
+
+/**
+ * qobject_to_qint(): Convert a QObject into a QInt
+ */
+QInt *qobject_to_qint(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QINT)
+ return NULL;
+
+ return container_of(obj, QInt, base);
+}
+
+/**
+ * qint_destroy_obj(): Free all memory allocated by a
+ * QInt object
+ */
+static void qint_destroy_obj(QObject *obj)
+{
+ assert(obj != NULL);
+ qemu_free(qobject_to_qint(obj));
+}
diff --git a/qint.h b/qint.h
new file mode 100644
index 0000000..6b1a15c
--- /dev/null
+++ b/qint.h
@@ -0,0 +1,28 @@
+/*
+ * QInt Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef QINT_H
+#define QINT_H
+
+#include <stdint.h>
+#include "qobject.h"
+
+typedef struct QInt {
+ QObject_HEAD;
+ int64_t value;
+} QInt;
+
+QInt *qint_from_int(int64_t value);
+int64_t qint_get_int(const QInt *qi);
+QInt *qobject_to_qint(const QObject *obj);
+
+#endif /* QINT_H */
diff --git a/qjson.c b/qjson.c
new file mode 100644
index 0000000..f9c8e77
--- /dev/null
+++ b/qjson.c
@@ -0,0 +1,294 @@
+/*
+ * QObject JSON integration
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "json-lexer.h"
+#include "json-parser.h"
+#include "json-streamer.h"
+#include "qjson.h"
+#include "qint.h"
+#include "qlist.h"
+#include "qbool.h"
+#include "qfloat.h"
+#include "qdict.h"
+
+typedef struct JSONParsingState
+{
+ JSONMessageParser parser;
+ va_list *ap;
+ QObject *result;
+} JSONParsingState;
+
+static void parse_json(JSONMessageParser *parser, QList *tokens)
+{
+ JSONParsingState *s = container_of(parser, JSONParsingState, parser);
+ s->result = json_parser_parse(tokens, s->ap);
+}
+
+QObject *qobject_from_jsonv(const char *string, va_list *ap)
+{
+ JSONParsingState state = {};
+
+ state.ap = ap;
+
+ json_message_parser_init(&state.parser, parse_json);
+ json_message_parser_feed(&state.parser, string, strlen(string));
+ json_message_parser_flush(&state.parser);
+ json_message_parser_destroy(&state.parser);
+
+ return state.result;
+}
+
+QObject *qobject_from_json(const char *string)
+{
+ return qobject_from_jsonv(string, NULL);
+}
+
+/*
+ * IMPORTANT: This function aborts on error, thus it must not
+ * be used with untrusted arguments.
+ */
+QObject *qobject_from_jsonf(const char *string, ...)
+{
+ QObject *obj;
+ va_list ap;
+
+ va_start(ap, string);
+ obj = qobject_from_jsonv(string, &ap);
+ va_end(ap);
+
+ assert(obj != NULL);
+ return obj;
+}
+
+typedef struct ToJsonIterState
+{
+ int indent;
+ int pretty;
+ int count;
+ QString *str;
+} ToJsonIterState;
+
+static void to_json(const QObject *obj, QString *str, int pretty, int indent);
+
+static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
+{
+ ToJsonIterState *s = opaque;
+ QString *qkey;
+ int j;
+
+ if (s->count)
+ qstring_append(s->str, ", ");
+
+ if (s->pretty) {
+ qstring_append(s->str, "\n");
+ for (j = 0 ; j < s->indent ; j++)
+ qstring_append(s->str, " ");
+ }
+
+ qkey = qstring_from_str(key);
+ to_json(QOBJECT(qkey), s->str, s->pretty, s->indent);
+ QDECREF(qkey);
+
+ qstring_append(s->str, ": ");
+ to_json(obj, s->str, s->pretty, s->indent);
+ s->count++;
+}
+
+static void to_json_list_iter(QObject *obj, void *opaque)
+{
+ ToJsonIterState *s = opaque;
+ int j;
+
+ if (s->count)
+ qstring_append(s->str, ", ");
+
+ if (s->pretty) {
+ qstring_append(s->str, "\n");
+ for (j = 0 ; j < s->indent ; j++)
+ qstring_append(s->str, " ");
+ }
+
+ to_json(obj, s->str, s->pretty, s->indent);
+ s->count++;
+}
+
+static void to_json(const QObject *obj, QString *str, int pretty, int indent)
+{
+ switch (qobject_type(obj)) {
+ case QTYPE_QINT: {
+ QInt *val = qobject_to_qint(obj);
+ char buffer[1024];
+
+ snprintf(buffer, sizeof(buffer), "%" PRId64, qint_get_int(val));
+ qstring_append(str, buffer);
+ break;
+ }
+ case QTYPE_QSTRING: {
+ QString *val = qobject_to_qstring(obj);
+ const char *ptr;
+
+ ptr = qstring_get_str(val);
+ qstring_append(str, "\"");
+ while (*ptr) {
+ if ((ptr[0] & 0xE0) == 0xE0 &&
+ (ptr[1] & 0x80) && (ptr[2] & 0x80)) {
+ uint16_t wchar;
+ char escape[7];
+
+ wchar = (ptr[0] & 0x0F) << 12;
+ wchar |= (ptr[1] & 0x3F) << 6;
+ wchar |= (ptr[2] & 0x3F);
+ ptr += 2;
+
+ snprintf(escape, sizeof(escape), "\\u%04X", wchar);
+ qstring_append(str, escape);
+ } else if ((ptr[0] & 0xE0) == 0xC0 && (ptr[1] & 0x80)) {
+ uint16_t wchar;
+ char escape[7];
+
+ wchar = (ptr[0] & 0x1F) << 6;
+ wchar |= (ptr[1] & 0x3F);
+ ptr++;
+
+ snprintf(escape, sizeof(escape), "\\u%04X", wchar);
+ qstring_append(str, escape);
+ } else switch (ptr[0]) {
+ case '\"':
+ qstring_append(str, "\\\"");
+ break;
+ case '\\':
+ qstring_append(str, "\\\\");
+ break;
+ case '\b':
+ qstring_append(str, "\\b");
+ break;
+ case '\f':
+ qstring_append(str, "\\f");
+ break;
+ case '\n':
+ qstring_append(str, "\\n");
+ break;
+ case '\r':
+ qstring_append(str, "\\r");
+ break;
+ case '\t':
+ qstring_append(str, "\\t");
+ break;
+ default: {
+ if (ptr[0] <= 0x1F) {
+ char escape[7];
+ snprintf(escape, sizeof(escape), "\\u%04X", ptr[0]);
+ qstring_append(str, escape);
+ } else {
+ char buf[2] = { ptr[0], 0 };
+ qstring_append(str, buf);
+ }
+ break;
+ }
+ }
+ ptr++;
+ }
+ qstring_append(str, "\"");
+ break;
+ }
+ case QTYPE_QDICT: {
+ ToJsonIterState s;
+ QDict *val = qobject_to_qdict(obj);
+
+ s.count = 0;
+ s.str = str;
+ s.indent = indent + 1;
+ s.pretty = pretty;
+ qstring_append(str, "{");
+ qdict_iter(val, to_json_dict_iter, &s);
+ if (pretty) {
+ int j;
+ qstring_append(str, "\n");
+ for (j = 0 ; j < indent ; j++)
+ qstring_append(str, " ");
+ }
+ qstring_append(str, "}");
+ break;
+ }
+ case QTYPE_QLIST: {
+ ToJsonIterState s;
+ QList *val = qobject_to_qlist(obj);
+
+ s.count = 0;
+ s.str = str;
+ s.indent = indent + 1;
+ s.pretty = pretty;
+ qstring_append(str, "[");
+ qlist_iter(val, (void *)to_json_list_iter, &s);
+ if (pretty) {
+ int j;
+ qstring_append(str, "\n");
+ for (j = 0 ; j < indent ; j++)
+ qstring_append(str, " ");
+ }
+ qstring_append(str, "]");
+ break;
+ }
+ case QTYPE_QFLOAT: {
+ QFloat *val = qobject_to_qfloat(obj);
+ char buffer[1024];
+ int len;
+
+ len = snprintf(buffer, sizeof(buffer), "%f", qfloat_get_double(val));
+ while (len > 0 && buffer[len - 1] == '0') {
+ len--;
+ }
+
+ if (len && buffer[len - 1] == '.') {
+ buffer[len - 1] = 0;
+ } else {
+ buffer[len] = 0;
+ }
+
+ qstring_append(str, buffer);
+ break;
+ }
+ case QTYPE_QBOOL: {
+ QBool *val = qobject_to_qbool(obj);
+
+ if (qbool_get_int(val)) {
+ qstring_append(str, "true");
+ } else {
+ qstring_append(str, "false");
+ }
+ break;
+ }
+ case QTYPE_QERROR:
+ /* XXX: should QError be emitted? */
+ case QTYPE_NONE:
+ break;
+ }
+}
+
+QString *qobject_to_json(const QObject *obj)
+{
+ QString *str = qstring_new();
+
+ to_json(obj, str, 0, 0);
+
+ return str;
+}
+
+QString *qobject_to_json_pretty(const QObject *obj)
+{
+ QString *str = qstring_new();
+
+ to_json(obj, str, 1, 0);
+
+ return str;
+}
diff --git a/qjson.h b/qjson.h
new file mode 100644
index 0000000..65b10ea
--- /dev/null
+++ b/qjson.h
@@ -0,0 +1,28 @@
+/*
+ * QObject JSON integration
+ *
+ * Copyright IBM, Corp. 2009
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QJSON_H
+#define QJSON_H
+
+#include <stdarg.h>
+#include "qobject.h"
+#include "qstring.h"
+
+QObject *qobject_from_json(const char *string) GCC_FMT_ATTR(1, 0);
+QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
+QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0);
+
+QString *qobject_to_json(const QObject *obj);
+QString *qobject_to_json_pretty(const QObject *obj);
+
+#endif /* QJSON_H */
diff --git a/qlist.c b/qlist.c
new file mode 100644
index 0000000..5730fb8
--- /dev/null
+++ b/qlist.c
@@ -0,0 +1,157 @@
+/*
+ * QList Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qlist.h"
+#include "qobject.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+
+static void qlist_destroy_obj(QObject *obj);
+
+static const QType qlist_type = {
+ .code = QTYPE_QLIST,
+ .destroy = qlist_destroy_obj,
+};
+
+/**
+ * qlist_new(): Create a new QList
+ *
+ * Return strong reference.
+ */
+QList *qlist_new(void)
+{
+ QList *qlist;
+
+ qlist = qemu_malloc(sizeof(*qlist));
+ QTAILQ_INIT(&qlist->head);
+ QOBJECT_INIT(qlist, &qlist_type);
+
+ return qlist;
+}
+
+static void qlist_copy_elem(QObject *obj, void *opaque)
+{
+ QList *dst = opaque;
+
+ qobject_incref(obj);
+ qlist_append_obj(dst, obj);
+}
+
+QList *qlist_copy(QList *src)
+{
+ QList *dst = qlist_new();
+
+ qlist_iter(src, qlist_copy_elem, dst);
+
+ return dst;
+}
+
+/**
+ * qlist_append_obj(): Append an QObject into QList
+ *
+ * NOTE: ownership of 'value' is transferred to the QList
+ */
+void qlist_append_obj(QList *qlist, QObject *value)
+{
+ QListEntry *entry;
+
+ entry = qemu_malloc(sizeof(*entry));
+ entry->value = value;
+
+ QTAILQ_INSERT_TAIL(&qlist->head, entry, next);
+}
+
+/**
+ * qlist_iter(): Iterate over all the list's stored values.
+ *
+ * This function allows the user to provide an iterator, which will be
+ * called for each stored value in the list.
+ */
+void qlist_iter(const QList *qlist,
+ void (*iter)(QObject *obj, void *opaque), void *opaque)
+{
+ QListEntry *entry;
+
+ QTAILQ_FOREACH(entry, &qlist->head, next)
+ iter(entry->value, opaque);
+}
+
+QObject *qlist_pop(QList *qlist)
+{
+ QListEntry *entry;
+ QObject *ret;
+
+ if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) {
+ return NULL;
+ }
+
+ entry = QTAILQ_FIRST(&qlist->head);
+ QTAILQ_REMOVE(&qlist->head, entry, next);
+
+ ret = entry->value;
+ qemu_free(entry);
+
+ return ret;
+}
+
+QObject *qlist_peek(QList *qlist)
+{
+ QListEntry *entry;
+ QObject *ret;
+
+ if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) {
+ return NULL;
+ }
+
+ entry = QTAILQ_FIRST(&qlist->head);
+
+ ret = entry->value;
+
+ return ret;
+}
+
+int qlist_empty(const QList *qlist)
+{
+ return QTAILQ_EMPTY(&qlist->head);
+}
+
+/**
+ * qobject_to_qlist(): Convert a QObject into a QList
+ */
+QList *qobject_to_qlist(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QLIST) {
+ return NULL;
+ }
+
+ return container_of(obj, QList, base);
+}
+
+/**
+ * qlist_destroy_obj(): Free all the memory allocated by a QList
+ */
+static void qlist_destroy_obj(QObject *obj)
+{
+ QList *qlist;
+ QListEntry *entry, *next_entry;
+
+ assert(obj != NULL);
+ qlist = qobject_to_qlist(obj);
+
+ QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) {
+ QTAILQ_REMOVE(&qlist->head, entry, next);
+ qobject_decref(entry->value);
+ qemu_free(entry);
+ }
+
+ qemu_free(qlist);
+}
diff --git a/qlist.h b/qlist.h
new file mode 100644
index 0000000..dbe7b92
--- /dev/null
+++ b/qlist.h
@@ -0,0 +1,53 @@
+/*
+ * QList Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef QLIST_H
+#define QLIST_H
+
+#include "qobject.h"
+#include "qemu-queue.h"
+#include "qemu-common.h"
+
+typedef struct QListEntry {
+ QObject *value;
+ QTAILQ_ENTRY(QListEntry) next;
+} QListEntry;
+
+typedef struct QList {
+ QObject_HEAD;
+ QTAILQ_HEAD(,QListEntry) head;
+} QList;
+
+#define qlist_append(qlist, obj) \
+ qlist_append_obj(qlist, QOBJECT(obj))
+
+#define QLIST_FOREACH_ENTRY(qlist, var) \
+ for ((var) = ((qlist)->head.tqh_first); \
+ (var); \
+ (var) = ((var)->next.tqe_next))
+
+static inline QObject *qlist_entry_obj(const QListEntry *entry)
+{
+ return entry->value;
+}
+
+QList *qlist_new(void);
+QList *qlist_copy(QList *src);
+void qlist_append_obj(QList *qlist, QObject *obj);
+void qlist_iter(const QList *qlist,
+ void (*iter)(QObject *obj, void *opaque), void *opaque);
+QObject *qlist_pop(QList *qlist);
+QObject *qlist_peek(QList *qlist);
+int qlist_empty(const QList *qlist);
+QList *qobject_to_qlist(const QObject *obj);
+
+#endif /* QLIST_H */
diff --git a/qmp-commands.hx b/qmp-commands.hx
new file mode 100644
index 0000000..df40a3d
--- /dev/null
+++ b/qmp-commands.hx
@@ -0,0 +1,1776 @@
+HXCOMM QMP dispatch table and documentation
+HXCOMM Text between SQMP and EQMP is copied to the QMP documention file and
+HXCOMM does not show up in the other formats.
+
+SQMP
+ QMP Supported Commands
+ ----------------------
+
+This document describes all commands currently supported by QMP.
+
+Most of the time their usage is exactly the same as in the user Monitor, this
+means that any other document which also describe commands (the manpage,
+QEMU's manual, etc) can and should be consulted.
+
+QMP has two types of commands: regular and query commands. Regular commands
+usually change the Virtual Machine's state someway, while query commands just
+return information. The sections below are divided accordingly.
+
+It's important to observe that all communication examples are formatted in
+a reader-friendly way, so that they're easier to understand. However, in real
+protocol usage, they're emitted as a single line.
+
+Also, the following notation is used to denote data flow:
+
+-> data issued by the Client
+<- Server data response
+
+Please, refer to the QMP specification (QMP/qmp-spec.txt) for detailed
+information on the Server command and response formats.
+
+NOTE: This document is temporary and will be replaced soon.
+
+1. Stability Considerations
+===========================
+
+The current QMP command set (described in this file) may be useful for a
+number of use cases, however it's limited and several commands have bad
+defined semantics, specially with regard to command completion.
+
+These problems are going to be solved incrementally in the next QEMU releases
+and we're going to establish a deprecation policy for badly defined commands.
+
+If you're planning to adopt QMP, please observe the following:
+
+ 1. The deprecation policy will take efect and be documented soon, please
+ check the documentation of each used command as soon as a new release of
+ QEMU is available
+
+ 2. DO NOT rely on anything which is not explicit documented
+
+ 3. Errors, in special, are not documented. Applications should NOT check
+ for specific errors classes or data (it's strongly recommended to only
+ check for the "error" key)
+
+2. Regular Commands
+===================
+
+Server's responses in the examples below are always a success response, please
+refer to the QMP specification for more details on error responses.
+
+EQMP
+
+ {
+ .name = "quit",
+ .args_type = "",
+ .params = "",
+ .help = "quit the emulator",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_quit,
+ },
+
+SQMP
+quit
+----
+
+Quit the emulator.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "quit" }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "eject",
+ .args_type = "force:-f,device:B",
+ .params = "[-f] device",
+ .help = "eject a removable medium (use -f to force it)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_eject,
+ },
+
+SQMP
+eject
+-----
+
+Eject a removable medium.
+
+Arguments:
+
+- force: force ejection (json-bool, optional)
+- device: device name (json-string)
+
+Example:
+
+-> { "execute": "eject", "arguments": { "device": "ide1-cd0" } }
+<- { "return": {} }
+
+Note: The "force" argument defaults to false.
+
+EQMP
+
+ {
+ .name = "change",
+ .args_type = "device:B,target:F,arg:s?",
+ .params = "device filename [format]",
+ .help = "change a removable medium, optional format",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_change,
+ },
+
+SQMP
+change
+------
+
+Change a removable medium or VNC configuration.
+
+Arguments:
+
+- "device": device name (json-string)
+- "target": filename or item (json-string)
+- "arg": additional argument (json-string, optional)
+
+Examples:
+
+1. Change a removable medium
+
+-> { "execute": "change",
+ "arguments": { "device": "ide1-cd0",
+ "target": "/srv/images/Fedora-12-x86_64-DVD.iso" } }
+<- { "return": {} }
+
+2. Change VNC password
+
+-> { "execute": "change",
+ "arguments": { "device": "vnc", "target": "password",
+ "arg": "foobar1" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "screendump",
+ .args_type = "filename:F",
+ .params = "filename",
+ .help = "save screen into PPM image 'filename'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_screen_dump,
+ },
+
+SQMP
+screendump
+----------
+
+Save screen into PPM image.
+
+Arguments:
+
+- "filename": file path (json-string)
+
+Example:
+
+-> { "execute": "screendump", "arguments": { "filename": "/tmp/image" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "stop",
+ .args_type = "",
+ .params = "",
+ .help = "stop emulation",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_stop,
+ },
+
+SQMP
+stop
+----
+
+Stop the emulator.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "stop" }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "cont",
+ .args_type = "",
+ .params = "",
+ .help = "resume emulation",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_cont,
+ },
+
+SQMP
+cont
+----
+
+Resume emulation.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "cont" }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "system_reset",
+ .args_type = "",
+ .params = "",
+ .help = "reset the system",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_system_reset,
+ },
+
+SQMP
+system_reset
+------------
+
+Reset the system.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "system_reset" }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "system_powerdown",
+ .args_type = "",
+ .params = "",
+ .help = "send system power down event",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_system_powerdown,
+ },
+
+SQMP
+system_powerdown
+----------------
+
+Send system power down event.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "system_powerdown" }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "device_add",
+ .args_type = "device:O",
+ .params = "driver[,prop=value][,...]",
+ .help = "add device, like -device on the command line",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_device_add,
+ },
+
+SQMP
+device_add
+----------
+
+Add a device.
+
+Arguments:
+
+- "driver": the name of the new device's driver (json-string)
+- "bus": the device's parent bus (device tree path, json-string, optional)
+- "id": the device's ID, must be unique (json-string)
+- device properties
+
+Example:
+
+-> { "execute": "device_add", "arguments": { "driver": "e1000", "id": "net1" } }
+<- { "return": {} }
+
+Notes:
+
+(1) For detailed information about this command, please refer to the
+ 'docs/qdev-device-use.txt' file.
+
+(2) It's possible to list device properties by running QEMU with the
+ "-device DEVICE,\?" command-line argument, where DEVICE is the device's name
+
+EQMP
+
+ {
+ .name = "device_del",
+ .args_type = "id:s",
+ .params = "device",
+ .help = "remove device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_device_del,
+ },
+
+SQMP
+device_del
+----------
+
+Remove a device.
+
+Arguments:
+
+- "id": the device's ID (json-string)
+
+Example:
+
+-> { "execute": "device_del", "arguments": { "id": "net1" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "cpu",
+ .args_type = "index:i",
+ .params = "index",
+ .help = "set the default CPU",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_cpu_set,
+ },
+
+SQMP
+cpu
+---
+
+Set the default CPU.
+
+Arguments:
+
+- "index": the CPU's index (json-int)
+
+Example:
+
+-> { "execute": "cpu", "arguments": { "index": 0 } }
+<- { "return": {} }
+
+Note: CPUs' indexes are obtained with the 'query-cpus' command.
+
+EQMP
+
+ {
+ .name = "memsave",
+ .args_type = "val:l,size:i,filename:s",
+ .params = "addr size file",
+ .help = "save to disk virtual memory dump starting at 'addr' of size 'size'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_memory_save,
+ },
+
+SQMP
+memsave
+-------
+
+Save to disk virtual memory dump starting at 'val' of size 'size'.
+
+Arguments:
+
+- "val": the starting address (json-int)
+- "size": the memory size, in bytes (json-int)
+- "filename": file path (json-string)
+
+Example:
+
+-> { "execute": "memsave",
+ "arguments": { "val": 10,
+ "size": 100,
+ "filename": "/tmp/virtual-mem-dump" } }
+<- { "return": {} }
+
+Note: Depends on the current CPU.
+
+EQMP
+
+ {
+ .name = "pmemsave",
+ .args_type = "val:l,size:i,filename:s",
+ .params = "addr size file",
+ .help = "save to disk physical memory dump starting at 'addr' of size 'size'",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_physical_memory_save,
+ },
+
+SQMP
+pmemsave
+--------
+
+Save to disk physical memory dump starting at 'val' of size 'size'.
+
+Arguments:
+
+- "val": the starting address (json-int)
+- "size": the memory size, in bytes (json-int)
+- "filename": file path (json-string)
+
+Example:
+
+-> { "execute": "pmemsave",
+ "arguments": { "val": 10,
+ "size": 100,
+ "filename": "/tmp/physical-mem-dump" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "migrate",
+ .args_type = "detach:-d,blk:-b,inc:-i,uri:s",
+ .params = "[-d] [-b] [-i] uri",
+ .help = "migrate to URI (using -d to not wait for completion)"
+ "\n\t\t\t -b for migration without shared storage with"
+ " full copy of disk\n\t\t\t -i for migration without "
+ "shared storage with incremental copy of disk "
+ "(base image shared between src and destination)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate,
+ },
+
+SQMP
+migrate
+-------
+
+Migrate to URI.
+
+Arguments:
+
+- "blk": block migration, full disk copy (json-bool, optional)
+- "inc": incremental disk copy (json-bool, optional)
+- "uri": Destination URI (json-string)
+
+Example:
+
+-> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
+<- { "return": {} }
+
+Notes:
+
+(1) The 'query-migrate' command should be used to check migration's progress
+ and final result (this information is provided by the 'status' member)
+(2) All boolean arguments default to false
+(3) The user Monitor's "detach" argument is invalid in QMP and should not
+ be used
+
+EQMP
+
+ {
+ .name = "migrate_cancel",
+ .args_type = "",
+ .params = "",
+ .help = "cancel the current VM migration",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_cancel,
+ },
+
+SQMP
+migrate_cancel
+--------------
+
+Cancel the current migration.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "migrate_cancel" }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "migrate_set_speed",
+ .args_type = "value:o",
+ .params = "value",
+ .help = "set maximum speed (in bytes) for migrations",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_set_speed,
+ },
+
+SQMP
+client_migrate_info
+------------------
+
+Set the spice/vnc connection info for the migration target. The spice/vnc
+server will ask the spice/vnc client to automatically reconnect using the
+new parameters (if specified) once the vm migration finished successfully.
+
+Arguments:
+
+- "protocol": protocol: "spice" or "vnc" (json-string)
+- "hostname": migration target hostname (json-string)
+- "port": spice/vnc tcp port for plaintext channels (json-int, optional)
+- "tls-port": spice tcp port for tls-secured channels (json-int, optional)
+- "cert-subject": server certificate subject (json-string, optional)
+
+Example:
+
+-> { "execute": "client_migrate_info",
+ "arguments": { "protocol": "spice",
+ "hostname": "virt42.lab.kraxel.org",
+ "port": 1234 } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "client_migrate_info",
+ .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
+ .params = "protocol hostname port tls-port cert-subject",
+ .help = "send migration info to spice/vnc client",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = client_migrate_info,
+ },
+
+SQMP
+migrate_set_speed
+-----------------
+
+Set maximum speed for migrations.
+
+Arguments:
+
+- "value": maximum speed, in bytes per second (json-int)
+
+Example:
+
+-> { "execute": "migrate_set_speed", "arguments": { "value": 1024 } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "migrate_set_downtime",
+ .args_type = "value:T",
+ .params = "value",
+ .help = "set maximum tolerated downtime (in seconds) for migrations",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_migrate_set_downtime,
+ },
+
+SQMP
+migrate_set_downtime
+--------------------
+
+Set maximum tolerated downtime (in seconds) for migrations.
+
+Arguments:
+
+- "value": maximum downtime (json-number)
+
+Example:
+
+-> { "execute": "migrate_set_downtime", "arguments": { "value": 0.1 } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "netdev_add",
+ .args_type = "netdev:O",
+ .params = "[user|tap|socket],id=str[,prop=value][,...]",
+ .help = "add host network device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_netdev_add,
+ },
+
+SQMP
+netdev_add
+----------
+
+Add host network device.
+
+Arguments:
+
+- "type": the device type, "tap", "user", ... (json-string)
+- "id": the device's ID, must be unique (json-string)
+- device options
+
+Example:
+
+-> { "execute": "netdev_add", "arguments": { "type": "user", "id": "netdev1" } }
+<- { "return": {} }
+
+Note: The supported device options are the same ones supported by the '-net'
+ command-line argument, which are listed in the '-help' output or QEMU's
+ manual
+
+EQMP
+
+ {
+ .name = "netdev_del",
+ .args_type = "id:s",
+ .params = "id",
+ .help = "remove host network device",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_netdev_del,
+ },
+
+SQMP
+netdev_del
+----------
+
+Remove host network device.
+
+Arguments:
+
+- "id": the device's ID, must be unique (json-string)
+
+Example:
+
+-> { "execute": "netdev_del", "arguments": { "id": "netdev1" } }
+<- { "return": {} }
+
+
+EQMP
+
+ {
+ .name = "block_resize",
+ .args_type = "device:B,size:o",
+ .params = "device size",
+ .help = "resize a block image",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_block_resize,
+ },
+
+SQMP
+block_resize
+------------
+
+Resize a block image while a guest is running.
+
+Arguments:
+
+- "device": the device's ID, must be unique (json-string)
+- "size": new size
+
+Example:
+
+-> { "execute": "block_resize", "arguments": { "device": "scratch", "size": 1073741824 } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "balloon",
+ .args_type = "value:M",
+ .params = "target",
+ .help = "request VM to change its memory allocation (in MB)",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_async = do_balloon,
+ .flags = MONITOR_CMD_ASYNC,
+ },
+
+SQMP
+balloon
+-------
+
+Request VM to change its memory allocation (in bytes).
+
+Arguments:
+
+- "value": New memory allocation (json-int)
+
+Example:
+
+-> { "execute": "balloon", "arguments": { "value": 536870912 } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "set_link",
+ .args_type = "name:s,up:b",
+ .params = "name on|off",
+ .help = "change the link status of a network adapter",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_set_link,
+ },
+
+SQMP
+set_link
+--------
+
+Change the link status of a network adapter.
+
+Arguments:
+
+- "name": network device name (json-string)
+- "up": status is up (json-bool)
+
+Example:
+
+-> { "execute": "set_link", "arguments": { "name": "e1000.0", "up": false } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "getfd",
+ .args_type = "fdname:s",
+ .params = "getfd name",
+ .help = "receive a file descriptor via SCM rights and assign it a name",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_getfd,
+ },
+
+SQMP
+getfd
+-----
+
+Receive a file descriptor via SCM rights and assign it a name.
+
+Arguments:
+
+- "fdname": file descriptor name (json-string)
+
+Example:
+
+-> { "execute": "getfd", "arguments": { "fdname": "fd1" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "closefd",
+ .args_type = "fdname:s",
+ .params = "closefd name",
+ .help = "close a file descriptor previously passed via SCM rights",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_closefd,
+ },
+
+SQMP
+closefd
+-------
+
+Close a file descriptor previously passed via SCM rights.
+
+Arguments:
+
+- "fdname": file descriptor name (json-string)
+
+Example:
+
+-> { "execute": "closefd", "arguments": { "fdname": "fd1" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "block_passwd",
+ .args_type = "device:B,password:s",
+ .params = "block_passwd device password",
+ .help = "set the password of encrypted block devices",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_block_set_passwd,
+ },
+
+SQMP
+block_passwd
+------------
+
+Set the password of encrypted block devices.
+
+Arguments:
+
+- "device": device name (json-string)
+- "password": password (json-string)
+
+Example:
+
+-> { "execute": "block_passwd", "arguments": { "device": "ide0-hd0",
+ "password": "12345" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "set_password",
+ .args_type = "protocol:s,password:s,connected:s?",
+ .params = "protocol password action-if-connected",
+ .help = "set spice/vnc password",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = set_password,
+ },
+
+SQMP
+set_password
+------------
+
+Set the password for vnc/spice protocols.
+
+Arguments:
+
+- "protocol": protocol name (json-string)
+- "password": password (json-string)
+- "connected": [ keep | disconnect | fail ] (josn-string, optional)
+
+Example:
+
+-> { "execute": "set_password", "arguments": { "protocol": "vnc",
+ "password": "secret" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "expire_password",
+ .args_type = "protocol:s,time:s",
+ .params = "protocol time",
+ .help = "set spice/vnc password expire-time",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = expire_password,
+ },
+
+SQMP
+expire_password
+---------------
+
+Set the password expire time for vnc/spice protocols.
+
+Arguments:
+
+- "protocol": protocol name (json-string)
+- "time": [ now | never | +secs | secs ] (json-string)
+
+Example:
+
+-> { "execute": "expire_password", "arguments": { "protocol": "vnc",
+ "time": "+60" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "qmp_capabilities",
+ .args_type = "",
+ .params = "",
+ .help = "enable QMP capabilities",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_qmp_capabilities,
+ },
+
+SQMP
+qmp_capabilities
+----------------
+
+Enable QMP capabilities.
+
+Arguments: None.
+
+Example:
+
+-> { "execute": "qmp_capabilities" }
+<- { "return": {} }
+
+Note: This command must be issued before issuing any other command.
+
+EQMP
+
+ {
+ .name = "human-monitor-command",
+ .args_type = "command-line:s,cpu-index:i?",
+ .params = "",
+ .help = "",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_hmp_passthrough,
+ },
+
+SQMP
+human-monitor-command
+---------------------
+
+Execute a Human Monitor command.
+
+Arguments:
+
+- command-line: the command name and its arguments, just like the
+ Human Monitor's shell (json-string)
+- cpu-index: select the CPU number to be used by commands which access CPU
+ data, like 'info registers'. The Monitor selects CPU 0 if this
+ argument is not provided (json-int, optional)
+
+Example:
+
+-> { "execute": "human-monitor-command", "arguments": { "command-line": "info kvm" } }
+<- { "return": "kvm support: enabled\r\n" }
+
+Notes:
+
+(1) The Human Monitor is NOT an stable interface, this means that command
+ names, arguments and responses can change or be removed at ANY time.
+ Applications that rely on long term stability guarantees should NOT
+ use this command
+
+(2) Limitations:
+
+ o This command is stateless, this means that commands that depend
+ on state information (such as getfd) might not work
+
+ o Commands that prompt the user for data (eg. 'cont' when the block
+ device is encrypted) don't currently work
+
+3. Query Commands
+=================
+
+HXCOMM Each query command below is inside a SQMP/EQMP section, do NOT change
+HXCOMM this! We will possibly move query commands definitions inside those
+HXCOMM sections, just like regular commands.
+
+EQMP
+
+SQMP
+query-version
+-------------
+
+Show QEMU version.
+
+Return a json-object with the following information:
+
+- "qemu": A json-object containing three integer values:
+ - "major": QEMU's major version (json-int)
+ - "minor": QEMU's minor version (json-int)
+ - "micro": QEMU's micro version (json-int)
+- "package": package's version (json-string)
+
+Example:
+
+-> { "execute": "query-version" }
+<- {
+ "return":{
+ "qemu":{
+ "major":0,
+ "minor":11,
+ "micro":5
+ },
+ "package":""
+ }
+ }
+
+EQMP
+
+SQMP
+query-commands
+--------------
+
+List QMP available commands.
+
+Each command is represented by a json-object, the returned value is a json-array
+of all commands.
+
+Each json-object contain:
+
+- "name": command's name (json-string)
+
+Example:
+
+-> { "execute": "query-commands" }
+<- {
+ "return":[
+ {
+ "name":"query-balloon"
+ },
+ {
+ "name":"system_powerdown"
+ }
+ ]
+ }
+
+Note: This example has been shortened as the real response is too long.
+
+EQMP
+
+SQMP
+query-chardev
+-------------
+
+Each device is represented by a json-object. The returned value is a json-array
+of all devices.
+
+Each json-object contain the following:
+
+- "label": device's label (json-string)
+- "filename": device's file (json-string)
+
+Example:
+
+-> { "execute": "query-chardev" }
+<- {
+ "return":[
+ {
+ "label":"monitor",
+ "filename":"stdio"
+ },
+ {
+ "label":"serial0",
+ "filename":"vc"
+ }
+ ]
+ }
+
+EQMP
+
+SQMP
+query-block
+-----------
+
+Show the block devices.
+
+Each block device information is stored in a json-object and the returned value
+is a json-array of all devices.
+
+Each json-object contain the following:
+
+- "device": device name (json-string)
+- "type": device type (json-string)
+ - Possible values: "hd", "cdrom", "floppy", "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
+ containing the following:
+ - "file": device file name (json-string)
+ - "ro": true if read-only, false otherwise (json-bool)
+ - "drv": driver format name (json-string)
+ - Possible values: "blkdebug", "bochs", "cloop", "cow", "dmg",
+ "file", "file", "ftp", "ftps", "host_cdrom",
+ "host_device", "host_floppy", "http", "https",
+ "nbd", "parallels", "qcow", "qcow2", "raw",
+ "tftp", "vdi", "vmdk", "vpc", "vvfat"
+ - "backing_file": backing file name (json-string, optional)
+ - "encrypted": true if encrypted, false otherwise (json-bool)
+
+Example:
+
+-> { "execute": "query-block" }
+<- {
+ "return":[
+ {
+ "device":"ide0-hd0",
+ "locked":false,
+ "removable":false,
+ "inserted":{
+ "ro":false,
+ "drv":"qcow2",
+ "encrypted":false,
+ "file":"disks/test.img"
+ },
+ "type":"hd"
+ },
+ {
+ "device":"ide1-cd0",
+ "locked":false,
+ "removable":true,
+ "type":"cdrom"
+ },
+ {
+ "device":"floppy0",
+ "locked":false,
+ "removable":true,
+ "type": "floppy"
+ },
+ {
+ "device":"sd0",
+ "locked":false,
+ "removable":true,
+ "type":"floppy"
+ }
+ ]
+ }
+
+EQMP
+
+SQMP
+query-blockstats
+----------------
+
+Show block device statistics.
+
+Each device statistic information is stored in a json-object and the returned
+value is a json-array of all devices.
+
+Each json-object contain the following:
+
+- "device": device name (json-string)
+- "stats": A json-object with the statistics information, it contains:
+ - "rd_bytes": bytes read (json-int)
+ - "wr_bytes": bytes written (json-int)
+ - "rd_operations": read operations (json-int)
+ - "wr_operations": write operations (json-int)
+ - "wr_highest_offset": Highest offset of a sector written since the
+ BlockDriverState has been opened (json-int)
+- "parent": Contains recursively the statistics of the underlying
+ protocol (e.g. the host file for a qcow2 image). If there is
+ no underlying protocol, this field is omitted
+ (json-object, optional)
+
+Example:
+
+-> { "execute": "query-blockstats" }
+<- {
+ "return":[
+ {
+ "device":"ide0-hd0",
+ "parent":{
+ "stats":{
+ "wr_highest_offset":3686448128,
+ "wr_bytes":9786368,
+ "wr_operations":751,
+ "rd_bytes":122567168,
+ "rd_operations":36772
+ }
+ },
+ "stats":{
+ "wr_highest_offset":2821110784,
+ "wr_bytes":9786368,
+ "wr_operations":692,
+ "rd_bytes":122739200,
+ "rd_operations":36604
+ }
+ },
+ {
+ "device":"ide1-cd0",
+ "stats":{
+ "wr_highest_offset":0,
+ "wr_bytes":0,
+ "wr_operations":0,
+ "rd_bytes":0,
+ "rd_operations":0
+ }
+ },
+ {
+ "device":"floppy0",
+ "stats":{
+ "wr_highest_offset":0,
+ "wr_bytes":0,
+ "wr_operations":0,
+ "rd_bytes":0,
+ "rd_operations":0
+ }
+ },
+ {
+ "device":"sd0",
+ "stats":{
+ "wr_highest_offset":0,
+ "wr_bytes":0,
+ "wr_operations":0,
+ "rd_bytes":0,
+ "rd_operations":0
+ }
+ }
+ ]
+ }
+
+EQMP
+
+SQMP
+query-cpus
+----------
+
+Show CPU information.
+
+Return a json-array. Each CPU is represented by a json-object, which contains:
+
+- "CPU": CPU index (json-int)
+- "current": true if this is the current CPU, false otherwise (json-bool)
+- "halted": true if the cpu is halted, false otherwise (json-bool)
+- Current program counter. The key's name depends on the architecture:
+ "pc": i386/x86_64 (json-int)
+ "nip": PPC (json-int)
+ "pc" and "npc": sparc (json-int)
+ "PC": mips (json-int)
+
+Example:
+
+-> { "execute": "query-cpus" }
+<- {
+ "return":[
+ {
+ "CPU":0,
+ "current":true,
+ "halted":false,
+ "pc":3227107138
+ },
+ {
+ "CPU":1,
+ "current":false,
+ "halted":true,
+ "pc":7108165
+ }
+ ]
+ }
+
+EQMP
+
+SQMP
+query-pci
+---------
+
+PCI buses and devices information.
+
+The returned value is a json-array of all buses. Each bus is represented by
+a json-object, which has a key with a json-array of all PCI devices attached
+to it. Each device is represented by a json-object.
+
+The bus json-object contains the following:
+
+- "bus": bus number (json-int)
+- "devices": a json-array of json-objects, each json-object represents a
+ PCI device
+
+The PCI device json-object contains the following:
+
+- "bus": identical to the parent's bus number (json-int)
+- "slot": slot number (json-int)
+- "function": function number (json-int)
+- "class_info": a json-object containing:
+ - "desc": device class description (json-string, optional)
+ - "class": device class number (json-int)
+- "id": a json-object containing:
+ - "device": device ID (json-int)
+ - "vendor": vendor ID (json-int)
+- "irq": device's IRQ if assigned (json-int, optional)
+- "qdev_id": qdev id string (json-string)
+- "pci_bridge": It's a json-object, only present if this device is a
+ PCI bridge, contains:
+ - "bus": bus number (json-int)
+ - "secondary": secondary bus number (json-int)
+ - "subordinate": subordinate bus number (json-int)
+ - "io_range": I/O memory range information, a json-object with the
+ following members:
+ - "base": base address, in bytes (json-int)
+ - "limit": limit address, in bytes (json-int)
+ - "memory_range": memory range information, a json-object with the
+ following members:
+ - "base": base address, in bytes (json-int)
+ - "limit": limit address, in bytes (json-int)
+ - "prefetchable_range": Prefetchable memory range information, a
+ json-object with the following members:
+ - "base": base address, in bytes (json-int)
+ - "limit": limit address, in bytes (json-int)
+ - "devices": a json-array of PCI devices if there's any attached, each
+ each element is represented by a json-object, which contains
+ the same members of the 'PCI device json-object' described
+ above (optional)
+- "regions": a json-array of json-objects, each json-object represents a
+ memory region of this device
+
+The memory range json-object contains the following:
+
+- "base": base memory address (json-int)
+- "limit": limit value (json-int)
+
+The region json-object can be an I/O region or a memory region, an I/O region
+json-object contains the following:
+
+- "type": "io" (json-string, fixed)
+- "bar": BAR number (json-int)
+- "address": memory address (json-int)
+- "size": memory size (json-int)
+
+A memory region json-object contains the following:
+
+- "type": "memory" (json-string, fixed)
+- "bar": BAR number (json-int)
+- "address": memory address (json-int)
+- "size": memory size (json-int)
+- "mem_type_64": true or false (json-bool)
+- "prefetch": true or false (json-bool)
+
+Example:
+
+-> { "execute": "query-pci" }
+<- {
+ "return":[
+ {
+ "bus":0,
+ "devices":[
+ {
+ "bus":0,
+ "qdev_id":"",
+ "slot":0,
+ "class_info":{
+ "class":1536,
+ "desc":"Host bridge"
+ },
+ "id":{
+ "device":32902,
+ "vendor":4663
+ },
+ "function":0,
+ "regions":[
+
+ ]
+ },
+ {
+ "bus":0,
+ "qdev_id":"",
+ "slot":1,
+ "class_info":{
+ "class":1537,
+ "desc":"ISA bridge"
+ },
+ "id":{
+ "device":32902,
+ "vendor":28672
+ },
+ "function":0,
+ "regions":[
+
+ ]
+ },
+ {
+ "bus":0,
+ "qdev_id":"",
+ "slot":1,
+ "class_info":{
+ "class":257,
+ "desc":"IDE controller"
+ },
+ "id":{
+ "device":32902,
+ "vendor":28688
+ },
+ "function":1,
+ "regions":[
+ {
+ "bar":4,
+ "size":16,
+ "address":49152,
+ "type":"io"
+ }
+ ]
+ },
+ {
+ "bus":0,
+ "qdev_id":"",
+ "slot":2,
+ "class_info":{
+ "class":768,
+ "desc":"VGA controller"
+ },
+ "id":{
+ "device":4115,
+ "vendor":184
+ },
+ "function":0,
+ "regions":[
+ {
+ "prefetch":true,
+ "mem_type_64":false,
+ "bar":0,
+ "size":33554432,
+ "address":4026531840,
+ "type":"memory"
+ },
+ {
+ "prefetch":false,
+ "mem_type_64":false,
+ "bar":1,
+ "size":4096,
+ "address":4060086272,
+ "type":"memory"
+ },
+ {
+ "prefetch":false,
+ "mem_type_64":false,
+ "bar":6,
+ "size":65536,
+ "address":-1,
+ "type":"memory"
+ }
+ ]
+ },
+ {
+ "bus":0,
+ "qdev_id":"",
+ "irq":11,
+ "slot":4,
+ "class_info":{
+ "class":1280,
+ "desc":"RAM controller"
+ },
+ "id":{
+ "device":6900,
+ "vendor":4098
+ },
+ "function":0,
+ "regions":[
+ {
+ "bar":0,
+ "size":32,
+ "address":49280,
+ "type":"io"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+Note: This example has been shortened as the real response is too long.
+
+EQMP
+
+SQMP
+query-kvm
+---------
+
+Show KVM information.
+
+Return a json-object with the following information:
+
+- "enabled": true if KVM support is enabled, false otherwise (json-bool)
+- "present": true if QEMU has KVM support, false otherwise (json-bool)
+
+Example:
+
+-> { "execute": "query-kvm" }
+<- { "return": { "enabled": true, "present": true } }
+
+EQMP
+
+SQMP
+query-status
+------------
+
+Return a json-object with the following information:
+
+- "running": true if the VM is running, or false if it is paused (json-bool)
+- "singlestep": true if the VM is in single step mode,
+ false otherwise (json-bool)
+
+Example:
+
+-> { "execute": "query-status" }
+<- { "return": { "running": true, "singlestep": false } }
+
+EQMP
+
+SQMP
+query-mice
+----------
+
+Show VM mice information.
+
+Each mouse is represented by a json-object, the returned value is a json-array
+of all mice.
+
+The mouse json-object contains the following:
+
+- "name": mouse's name (json-string)
+- "index": mouse's index (json-int)
+- "current": true if this mouse is receiving events, false otherwise (json-bool)
+- "absolute": true if the mouse generates absolute input events (json-bool)
+
+Example:
+
+-> { "execute": "query-mice" }
+<- {
+ "return":[
+ {
+ "name":"QEMU Microsoft Mouse",
+ "index":0,
+ "current":false,
+ "absolute":false
+ },
+ {
+ "name":"QEMU PS/2 Mouse",
+ "index":1,
+ "current":true,
+ "absolute":true
+ }
+ ]
+ }
+
+EQMP
+
+SQMP
+query-vnc
+---------
+
+Show VNC server information.
+
+Return a json-object with server information. Connected clients are returned
+as a json-array of json-objects.
+
+The main json-object contains the following:
+
+- "enabled": true or false (json-bool)
+- "host": server's IP address (json-string)
+- "family": address family (json-string)
+ - Possible values: "ipv4", "ipv6", "unix", "unknown"
+- "service": server's port number (json-string)
+- "auth": authentication method (json-string)
+ - Possible values: "invalid", "none", "ra2", "ra2ne", "sasl", "tight",
+ "tls", "ultra", "unknown", "vencrypt", "vencrypt",
+ "vencrypt+plain", "vencrypt+tls+none",
+ "vencrypt+tls+plain", "vencrypt+tls+sasl",
+ "vencrypt+tls+vnc", "vencrypt+x509+none",
+ "vencrypt+x509+plain", "vencrypt+x509+sasl",
+ "vencrypt+x509+vnc", "vnc"
+- "clients": a json-array of all connected clients
+
+Clients are described by a json-object, each one contain the following:
+
+- "host": client's IP address (json-string)
+- "family": address family (json-string)
+ - Possible values: "ipv4", "ipv6", "unix", "unknown"
+- "service": client's port number (json-string)
+- "x509_dname": TLS dname (json-string, optional)
+- "sasl_username": SASL username (json-string, optional)
+
+Example:
+
+-> { "execute": "query-vnc" }
+<- {
+ "return":{
+ "enabled":true,
+ "host":"0.0.0.0",
+ "service":"50402",
+ "auth":"vnc",
+ "family":"ipv4",
+ "clients":[
+ {
+ "host":"127.0.0.1",
+ "service":"50401",
+ "family":"ipv4"
+ }
+ ]
+ }
+ }
+
+EQMP
+
+SQMP
+query-spice
+-----------
+
+Show SPICE server information.
+
+Return a json-object with server information. Connected clients are returned
+as a json-array of json-objects.
+
+The main json-object contains the following:
+
+- "enabled": true or false (json-bool)
+- "host": server's IP address (json-string)
+- "port": server's port number (json-int, optional)
+- "tls-port": server's port number (json-int, optional)
+- "auth": authentication method (json-string)
+ - Possible values: "none", "spice"
+- "channels": a json-array of all active channels clients
+
+Channels are described by a json-object, each one contain the following:
+
+- "host": client's IP address (json-string)
+- "family": address family (json-string)
+ - Possible values: "ipv4", "ipv6", "unix", "unknown"
+- "port": client's port number (json-string)
+- "connection-id": spice connection id. All channels with the same id
+ belong to the same spice session (json-int)
+- "channel-type": channel type. "1" is the main control channel, filter for
+ this one if you want track spice sessions only (json-int)
+- "channel-id": channel id. Usually "0", might be different needed when
+ multiple channels of the same type exist, such as multiple
+ display channels in a multihead setup (json-int)
+- "tls": whevener the channel is encrypted (json-bool)
+
+Example:
+
+-> { "execute": "query-spice" }
+<- {
+ "return": {
+ "enabled": true,
+ "auth": "spice",
+ "port": 5920,
+ "tls-port": 5921,
+ "host": "0.0.0.0",
+ "channels": [
+ {
+ "port": "54924",
+ "family": "ipv4",
+ "channel-type": 1,
+ "connection-id": 1804289383,
+ "host": "127.0.0.1",
+ "channel-id": 0,
+ "tls": true
+ },
+ {
+ "port": "36710",
+ "family": "ipv4",
+ "channel-type": 4,
+ "connection-id": 1804289383,
+ "host": "127.0.0.1",
+ "channel-id": 0,
+ "tls": false
+ },
+ [ ... more channels follow ... ]
+ ]
+ }
+ }
+
+EQMP
+
+SQMP
+query-name
+----------
+
+Show VM name.
+
+Return a json-object with the following information:
+
+- "name": VM's name (json-string, optional)
+
+Example:
+
+-> { "execute": "query-name" }
+<- { "return": { "name": "qemu-name" } }
+
+EQMP
+
+SQMP
+query-uuid
+----------
+
+Show VM UUID.
+
+Return a json-object with the following information:
+
+- "UUID": Universally Unique Identifier (json-string)
+
+Example:
+
+-> { "execute": "query-uuid" }
+<- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } }
+
+EQMP
+
+SQMP
+query-migrate
+-------------
+
+Migration status.
+
+Return a json-object. If migration is active there will be another json-object
+with RAM migration status and if block migration is active another one with
+block migration status.
+
+The main json-object contains the following:
+
+- "status": migration status (json-string)
+ - Possible values: "active", "completed", "failed", "cancelled"
+- "ram": only present if "status" is "active", it is a json-object with the
+ following RAM information (in bytes):
+ - "transferred": amount transferred (json-int)
+ - "remaining": amount remaining (json-int)
+ - "total": total (json-int)
+- "disk": only present if "status" is "active" and it is a block migration,
+ it is a json-object with the following disk information (in bytes):
+ - "transferred": amount transferred (json-int)
+ - "remaining": amount remaining (json-int)
+ - "total": total (json-int)
+
+Examples:
+
+1. Before the first migration
+
+-> { "execute": "query-migrate" }
+<- { "return": {} }
+
+2. Migration is done and has succeeded
+
+-> { "execute": "query-migrate" }
+<- { "return": { "status": "completed" } }
+
+3. Migration is done and has failed
+
+-> { "execute": "query-migrate" }
+<- { "return": { "status": "failed" } }
+
+4. Migration is being performed and is not a block migration:
+
+-> { "execute": "query-migrate" }
+<- {
+ "return":{
+ "status":"active",
+ "ram":{
+ "transferred":123,
+ "remaining":123,
+ "total":246
+ }
+ }
+ }
+
+5. Migration is being performed and is a block migration:
+
+-> { "execute": "query-migrate" }
+<- {
+ "return":{
+ "status":"active",
+ "ram":{
+ "total":1057024,
+ "remaining":1053304,
+ "transferred":3720
+ },
+ "disk":{
+ "total":20971520,
+ "remaining":20880384,
+ "transferred":91136
+ }
+ }
+ }
+
+EQMP
+
+SQMP
+query-balloon
+-------------
+
+Show balloon information.
+
+Make an asynchronous request for balloon info. When the request completes a
+json-object will be returned containing the following data:
+
+- "actual": current balloon value in bytes (json-int)
+- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional)
+- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional)
+- "major_page_faults": Number of major faults (json-int, optional)
+- "minor_page_faults": Number of minor faults (json-int, optional)
+- "free_mem": Total amount of free and unused memory in
+ bytes (json-int, optional)
+- "total_mem": Total amount of available memory in bytes (json-int, optional)
+
+Example:
+
+-> { "execute": "query-balloon" }
+<- {
+ "return":{
+ "actual":1073741824,
+ "mem_swapped_in":0,
+ "mem_swapped_out":0,
+ "major_page_faults":142,
+ "minor_page_faults":239245,
+ "free_mem":1014185984,
+ "total_mem":1044668416
+ }
+ }
+
+EQMP
+
diff --git a/qobject.h b/qobject.h
new file mode 100644
index 0000000..d42386d
--- /dev/null
+++ b/qobject.h
@@ -0,0 +1,112 @@
+/*
+ * QEMU Object Model.
+ *
+ * Based on ideas by Avi Kivity <avi@redhat.com>
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ * QObject Reference Counts Terminology
+ * ------------------------------------
+ *
+ * - Returning references: A function that returns an object may
+ * return it as either a weak or a strong reference. If the reference
+ * is strong, you are responsible for calling QDECREF() on the reference
+ * when you are done.
+ *
+ * If the reference is weak, the owner of the reference may free it at
+ * any time in the future. Before storing the reference anywhere, you
+ * should call QINCREF() to make the reference strong.
+ *
+ * - Transferring ownership: when you transfer ownership of a reference
+ * by calling a function, you are no longer responsible for calling
+ * QDECREF() when the reference is no longer needed. In other words,
+ * when the function returns you must behave as if the reference to the
+ * passed object was weak.
+ */
+#ifndef QOBJECT_H
+#define QOBJECT_H
+
+#include <stddef.h>
+#include <assert.h>
+
+typedef enum {
+ QTYPE_NONE,
+ QTYPE_QINT,
+ QTYPE_QSTRING,
+ QTYPE_QDICT,
+ QTYPE_QLIST,
+ QTYPE_QFLOAT,
+ QTYPE_QBOOL,
+ QTYPE_QERROR,
+} qtype_code;
+
+struct QObject;
+
+typedef struct QType {
+ qtype_code code;
+ void (*destroy)(struct QObject *);
+} QType;
+
+typedef struct QObject {
+ const QType *type;
+ size_t refcnt;
+} QObject;
+
+/* Objects definitions must include this */
+#define QObject_HEAD \
+ QObject base
+
+/* Get the 'base' part of an object */
+#define QOBJECT(obj) (&(obj)->base)
+
+/* High-level interface for qobject_incref() */
+#define QINCREF(obj) \
+ qobject_incref(QOBJECT(obj))
+
+/* High-level interface for qobject_decref() */
+#define QDECREF(obj) \
+ qobject_decref(QOBJECT(obj))
+
+/* Initialize an object to default values */
+#define QOBJECT_INIT(obj, qtype_type) \
+ obj->base.refcnt = 1; \
+ obj->base.type = qtype_type
+
+/**
+ * qobject_incref(): Increment QObject's reference count
+ */
+static inline void qobject_incref(QObject *obj)
+{
+ if (obj)
+ obj->refcnt++;
+}
+
+/**
+ * qobject_decref(): Decrement QObject's reference count, deallocate
+ * when it reaches zero
+ */
+static inline void qobject_decref(QObject *obj)
+{
+ if (obj && --obj->refcnt == 0) {
+ assert(obj->type != NULL);
+ assert(obj->type->destroy != NULL);
+ obj->type->destroy(obj);
+ }
+}
+
+/**
+ * qobject_type(): Return the QObject's type
+ */
+static inline qtype_code qobject_type(const QObject *obj)
+{
+ assert(obj->type != NULL);
+ return obj->type->code;
+}
+
+#endif /* QOBJECT_H */
diff --git a/qstring.c b/qstring.c
new file mode 100644
index 0000000..4e2ba08
--- /dev/null
+++ b/qstring.c
@@ -0,0 +1,141 @@
+/*
+ * QString Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#include "qobject.h"
+#include "qstring.h"
+#include "qemu-common.h"
+
+static void qstring_destroy_obj(QObject *obj);
+
+static const QType qstring_type = {
+ .code = QTYPE_QSTRING,
+ .destroy = qstring_destroy_obj,
+};
+
+/**
+ * qstring_new(): Create a new empty QString
+ *
+ * Return strong reference.
+ */
+QString *qstring_new(void)
+{
+ return qstring_from_str("");
+}
+
+/**
+ * qstring_from_substr(): Create a new QString from a C string substring
+ *
+ * Return string reference
+ */
+QString *qstring_from_substr(const char *str, int start, int end)
+{
+ QString *qstring;
+
+ qstring = qemu_malloc(sizeof(*qstring));
+
+ qstring->length = end - start + 1;
+ qstring->capacity = qstring->length;
+
+ qstring->string = qemu_malloc(qstring->capacity + 1);
+ memcpy(qstring->string, str + start, qstring->length);
+ qstring->string[qstring->length] = 0;
+
+ QOBJECT_INIT(qstring, &qstring_type);
+
+ return qstring;
+}
+
+/**
+ * qstring_from_str(): Create a new QString from a regular C string
+ *
+ * Return strong reference.
+ */
+QString *qstring_from_str(const char *str)
+{
+ return qstring_from_substr(str, 0, strlen(str) - 1);
+}
+
+static void capacity_increase(QString *qstring, size_t len)
+{
+ if (qstring->capacity < (qstring->length + len)) {
+ qstring->capacity += len;
+ qstring->capacity *= 2; /* use exponential growth */
+
+ qstring->string = qemu_realloc(qstring->string, qstring->capacity + 1);
+ }
+}
+
+/* qstring_append(): Append a C string to a QString
+ */
+void qstring_append(QString *qstring, const char *str)
+{
+ size_t len = strlen(str);
+
+ capacity_increase(qstring, len);
+ memcpy(qstring->string + qstring->length, str, len);
+ qstring->length += len;
+ qstring->string[qstring->length] = 0;
+}
+
+void qstring_append_int(QString *qstring, int64_t value)
+{
+ char num[32];
+
+ snprintf(num, sizeof(num), "%" PRId64, value);
+ qstring_append(qstring, num);
+}
+
+/**
+ * qstring_append_chr(): Append a C char to a QString
+ */
+void qstring_append_chr(QString *qstring, int c)
+{
+ capacity_increase(qstring, 1);
+ qstring->string[qstring->length++] = c;
+ qstring->string[qstring->length] = 0;
+}
+
+/**
+ * qobject_to_qstring(): Convert a QObject to a QString
+ */
+QString *qobject_to_qstring(const QObject *obj)
+{
+ if (qobject_type(obj) != QTYPE_QSTRING)
+ return NULL;
+
+ return container_of(obj, QString, base);
+}
+
+/**
+ * qstring_get_str(): Return a pointer to the stored string
+ *
+ * NOTE: Should be used with caution, if the object is deallocated
+ * this pointer becomes invalid.
+ */
+const char *qstring_get_str(const QString *qstring)
+{
+ return qstring->string;
+}
+
+/**
+ * qstring_destroy_obj(): Free all memory allocated by a QString
+ * object
+ */
+static void qstring_destroy_obj(QObject *obj)
+{
+ QString *qs;
+
+ assert(obj != NULL);
+ qs = qobject_to_qstring(obj);
+ qemu_free(qs->string);
+ qemu_free(qs);
+}
diff --git a/qstring.h b/qstring.h
new file mode 100644
index 0000000..84ccd96
--- /dev/null
+++ b/qstring.h
@@ -0,0 +1,35 @@
+/*
+ * QString Module
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * Authors:
+ * Luiz Capitulino <lcapitulino@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ */
+
+#ifndef QSTRING_H
+#define QSTRING_H
+
+#include <stdint.h>
+#include "qobject.h"
+
+typedef struct QString {
+ QObject_HEAD;
+ char *string;
+ size_t length;
+ size_t capacity;
+} QString;
+
+QString *qstring_new(void);
+QString *qstring_from_str(const char *str);
+QString *qstring_from_substr(const char *str, int start, int end);
+const char *qstring_get_str(const QString *qstring);
+void qstring_append_int(QString *qstring, int64_t value);
+void qstring_append(QString *qstring, const char *str);
+void qstring_append_chr(QString *qstring, int c);
+QString *qobject_to_qstring(const QObject *obj);
+
+#endif /* QSTRING_H */
diff --git a/range.h b/range.h
new file mode 100644
index 0000000..3502372
--- /dev/null
+++ b/range.h
@@ -0,0 +1,29 @@
+#ifndef QEMU_RANGE_H
+#define QEMU_RANGE_H
+
+/* Get last byte of a range from offset + length.
+ * Undefined for ranges that wrap around 0. */
+static inline uint64_t range_get_last(uint64_t offset, uint64_t len)
+{
+ return offset + len - 1;
+}
+
+/* Check whether a given range covers a given byte. */
+static inline int range_covers_byte(uint64_t offset, uint64_t len,
+ uint64_t byte)
+{
+ return offset <= byte && byte <= range_get_last(offset, len);
+}
+
+/* Check whether 2 given ranges overlap.
+ * Undefined if ranges that wrap around 0. */
+static inline int ranges_overlap(uint64_t first1, uint64_t len1,
+ uint64_t first2, uint64_t len2)
+{
+ uint64_t last1 = range_get_last(first1, len1);
+ uint64_t last2 = range_get_last(first2, len2);
+
+ return !(last2 < first1 || last1 < first2);
+}
+
+#endif
diff --git a/readline.c b/readline.c
new file mode 100644
index 0000000..92f9cd1
--- /dev/null
+++ b/readline.c
@@ -0,0 +1,476 @@
+/*
+ * QEMU readline utility
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "readline.h"
+#include "monitor.h"
+
+#define IS_NORM 0
+#define IS_ESC 1
+#define IS_CSI 2
+
+#undef printf
+#define printf do_not_use_printf
+
+void readline_show_prompt(ReadLineState *rs)
+{
+ monitor_printf(rs->mon, "%s", rs->prompt);
+ monitor_flush(rs->mon);
+ rs->last_cmd_buf_index = 0;
+ rs->last_cmd_buf_size = 0;
+ rs->esc_state = IS_NORM;
+}
+
+/* update the displayed command line */
+static void readline_update(ReadLineState *rs)
+{
+ int i, delta, len;
+
+ if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
+ memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
+ for(i = 0; i < rs->last_cmd_buf_index; i++) {
+ monitor_printf(rs->mon, "\033[D");
+ }
+ rs->cmd_buf[rs->cmd_buf_size] = '\0';
+ if (rs->read_password) {
+ len = strlen(rs->cmd_buf);
+ for(i = 0; i < len; i++)
+ monitor_printf(rs->mon, "*");
+ } else {
+ monitor_printf(rs->mon, "%s", rs->cmd_buf);
+ }
+ monitor_printf(rs->mon, "\033[K");
+ memcpy(rs->last_cmd_buf, rs->cmd_buf, rs->cmd_buf_size);
+ rs->last_cmd_buf_size = rs->cmd_buf_size;
+ rs->last_cmd_buf_index = rs->cmd_buf_size;
+ }
+ if (rs->cmd_buf_index != rs->last_cmd_buf_index) {
+ delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
+ if (delta > 0) {
+ for(i = 0;i < delta; i++) {
+ monitor_printf(rs->mon, "\033[C");
+ }
+ } else {
+ delta = -delta;
+ for(i = 0;i < delta; i++) {
+ monitor_printf(rs->mon, "\033[D");
+ }
+ }
+ rs->last_cmd_buf_index = rs->cmd_buf_index;
+ }
+ monitor_flush(rs->mon);
+}
+
+static void readline_insert_char(ReadLineState *rs, int ch)
+{
+ if (rs->cmd_buf_index < READLINE_CMD_BUF_SIZE) {
+ memmove(rs->cmd_buf + rs->cmd_buf_index + 1,
+ rs->cmd_buf + rs->cmd_buf_index,
+ rs->cmd_buf_size - rs->cmd_buf_index);
+ rs->cmd_buf[rs->cmd_buf_index] = ch;
+ rs->cmd_buf_size++;
+ rs->cmd_buf_index++;
+ }
+}
+
+static void readline_backward_char(ReadLineState *rs)
+{
+ if (rs->cmd_buf_index > 0) {
+ rs->cmd_buf_index--;
+ }
+}
+
+static void readline_forward_char(ReadLineState *rs)
+{
+ if (rs->cmd_buf_index < rs->cmd_buf_size) {
+ rs->cmd_buf_index++;
+ }
+}
+
+static void readline_delete_char(ReadLineState *rs)
+{
+ if (rs->cmd_buf_index < rs->cmd_buf_size) {
+ memmove(rs->cmd_buf + rs->cmd_buf_index,
+ rs->cmd_buf + rs->cmd_buf_index + 1,
+ rs->cmd_buf_size - rs->cmd_buf_index - 1);
+ rs->cmd_buf_size--;
+ }
+}
+
+static void readline_backspace(ReadLineState *rs)
+{
+ if (rs->cmd_buf_index > 0) {
+ readline_backward_char(rs);
+ readline_delete_char(rs);
+ }
+}
+
+static void readline_backword(ReadLineState *rs)
+{
+ int start;
+
+ if (rs->cmd_buf_index == 0 || rs->cmd_buf_index > rs->cmd_buf_size) {
+ return;
+ }
+
+ start = rs->cmd_buf_index - 1;
+
+ /* find first word (backwards) */
+ while (start > 0) {
+ if (!qemu_isspace(rs->cmd_buf[start])) {
+ break;
+ }
+
+ --start;
+ }
+
+ /* find first space (backwards) */
+ while (start > 0) {
+ if (qemu_isspace(rs->cmd_buf[start])) {
+ ++start;
+ break;
+ }
+
+ --start;
+ }
+
+ /* remove word */
+ if (start < rs->cmd_buf_index) {
+ memmove(rs->cmd_buf + start,
+ rs->cmd_buf + rs->cmd_buf_index,
+ rs->cmd_buf_size - rs->cmd_buf_index);
+ rs->cmd_buf_size -= rs->cmd_buf_index - start;
+ rs->cmd_buf_index = start;
+ }
+}
+
+static void readline_bol(ReadLineState *rs)
+{
+ rs->cmd_buf_index = 0;
+}
+
+static void readline_eol(ReadLineState *rs)
+{
+ rs->cmd_buf_index = rs->cmd_buf_size;
+}
+
+static void readline_up_char(ReadLineState *rs)
+{
+ int idx;
+
+ if (rs->hist_entry == 0)
+ return;
+ if (rs->hist_entry == -1) {
+ /* Find latest entry */
+ for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
+ if (rs->history[idx] == NULL)
+ break;
+ }
+ rs->hist_entry = idx;
+ }
+ rs->hist_entry--;
+ if (rs->hist_entry >= 0) {
+ pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
+ rs->history[rs->hist_entry]);
+ rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
+ }
+}
+
+static void readline_down_char(ReadLineState *rs)
+{
+ if (rs->hist_entry == -1)
+ return;
+ if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
+ rs->history[++rs->hist_entry] != NULL) {
+ pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
+ rs->history[rs->hist_entry]);
+ } else {
+ rs->cmd_buf[0] = 0;
+ rs->hist_entry = -1;
+ }
+ rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
+}
+
+static void readline_hist_add(ReadLineState *rs, const char *cmdline)
+{
+ char *hist_entry, *new_entry;
+ int idx;
+
+ if (cmdline[0] == '\0')
+ return;
+ new_entry = NULL;
+ if (rs->hist_entry != -1) {
+ /* We were editing an existing history entry: replace it */
+ hist_entry = rs->history[rs->hist_entry];
+ idx = rs->hist_entry;
+ if (strcmp(hist_entry, cmdline) == 0) {
+ goto same_entry;
+ }
+ }
+ /* Search cmdline in history buffers */
+ for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
+ hist_entry = rs->history[idx];
+ if (hist_entry == NULL)
+ break;
+ if (strcmp(hist_entry, cmdline) == 0) {
+ same_entry:
+ new_entry = hist_entry;
+ /* Put this entry at the end of history */
+ memmove(&rs->history[idx], &rs->history[idx + 1],
+ (READLINE_MAX_CMDS - idx + 1) * sizeof(char *));
+ rs->history[READLINE_MAX_CMDS - 1] = NULL;
+ for (; idx < READLINE_MAX_CMDS; idx++) {
+ if (rs->history[idx] == NULL)
+ break;
+ }
+ break;
+ }
+ }
+ if (idx == READLINE_MAX_CMDS) {
+ /* Need to get one free slot */
+ free(rs->history[0]);
+ memcpy(rs->history, &rs->history[1],
+ (READLINE_MAX_CMDS - 1) * sizeof(char *));
+ rs->history[READLINE_MAX_CMDS - 1] = NULL;
+ idx = READLINE_MAX_CMDS - 1;
+ }
+ if (new_entry == NULL)
+ new_entry = strdup(cmdline);
+ rs->history[idx] = new_entry;
+ rs->hist_entry = -1;
+}
+
+/* completion support */
+
+void readline_add_completion(ReadLineState *rs, const char *str)
+{
+ if (rs->nb_completions < READLINE_MAX_COMPLETIONS) {
+ rs->completions[rs->nb_completions++] = qemu_strdup(str);
+ }
+}
+
+void readline_set_completion_index(ReadLineState *rs, int index)
+{
+ rs->completion_index = index;
+}
+
+static void readline_completion(ReadLineState *rs)
+{
+ Monitor *mon = cur_mon;
+ int len, i, j, max_width, nb_cols, max_prefix;
+ char *cmdline;
+
+ rs->nb_completions = 0;
+
+ cmdline = qemu_malloc(rs->cmd_buf_index + 1);
+ memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
+ cmdline[rs->cmd_buf_index] = '\0';
+ rs->completion_finder(cmdline);
+ qemu_free(cmdline);
+
+ /* no completion found */
+ if (rs->nb_completions <= 0)
+ return;
+ if (rs->nb_completions == 1) {
+ len = strlen(rs->completions[0]);
+ for(i = rs->completion_index; i < len; i++) {
+ readline_insert_char(rs, rs->completions[0][i]);
+ }
+ /* extra space for next argument. XXX: make it more generic */
+ if (len > 0 && rs->completions[0][len - 1] != '/')
+ readline_insert_char(rs, ' ');
+ } else {
+ monitor_printf(mon, "\n");
+ max_width = 0;
+ max_prefix = 0;
+ for(i = 0; i < rs->nb_completions; i++) {
+ len = strlen(rs->completions[i]);
+ if (i==0) {
+ max_prefix = len;
+ } else {
+ if (len < max_prefix)
+ max_prefix = len;
+ for(j=0; j<max_prefix; j++) {
+ if (rs->completions[i][j] != rs->completions[0][j])
+ max_prefix = j;
+ }
+ }
+ if (len > max_width)
+ max_width = len;
+ }
+ if (max_prefix > 0)
+ for(i = rs->completion_index; i < max_prefix; i++) {
+ readline_insert_char(rs, rs->completions[0][i]);
+ }
+ max_width += 2;
+ if (max_width < 10)
+ max_width = 10;
+ else if (max_width > 80)
+ max_width = 80;
+ nb_cols = 80 / max_width;
+ j = 0;
+ for(i = 0; i < rs->nb_completions; i++) {
+ monitor_printf(rs->mon, "%-*s", max_width, rs->completions[i]);
+ if (++j == nb_cols || i == (rs->nb_completions - 1)) {
+ monitor_printf(rs->mon, "\n");
+ j = 0;
+ }
+ }
+ readline_show_prompt(rs);
+ }
+}
+
+/* return true if command handled */
+void readline_handle_byte(ReadLineState *rs, int ch)
+{
+ switch(rs->esc_state) {
+ case IS_NORM:
+ switch(ch) {
+ case 1:
+ readline_bol(rs);
+ break;
+ case 4:
+ readline_delete_char(rs);
+ break;
+ case 5:
+ readline_eol(rs);
+ break;
+ case 9:
+ readline_completion(rs);
+ break;
+ case 10:
+ case 13:
+ rs->cmd_buf[rs->cmd_buf_size] = '\0';
+ if (!rs->read_password)
+ readline_hist_add(rs, rs->cmd_buf);
+ monitor_printf(rs->mon, "\n");
+ rs->cmd_buf_index = 0;
+ rs->cmd_buf_size = 0;
+ rs->last_cmd_buf_index = 0;
+ rs->last_cmd_buf_size = 0;
+ rs->readline_func(rs->mon, rs->cmd_buf, rs->readline_opaque);
+ break;
+ case 23:
+ /* ^W */
+ readline_backword(rs);
+ break;
+ case 27:
+ rs->esc_state = IS_ESC;
+ break;
+ case 127:
+ case 8:
+ readline_backspace(rs);
+ break;
+ case 155:
+ rs->esc_state = IS_CSI;
+ break;
+ default:
+ if (ch >= 32) {
+ readline_insert_char(rs, ch);
+ }
+ break;
+ }
+ break;
+ case IS_ESC:
+ if (ch == '[') {
+ rs->esc_state = IS_CSI;
+ rs->esc_param = 0;
+ } else {
+ rs->esc_state = IS_NORM;
+ }
+ break;
+ case IS_CSI:
+ switch(ch) {
+ case 'A':
+ case 'F':
+ readline_up_char(rs);
+ break;
+ case 'B':
+ case 'E':
+ readline_down_char(rs);
+ break;
+ case 'D':
+ readline_backward_char(rs);
+ break;
+ case 'C':
+ readline_forward_char(rs);
+ break;
+ case '0' ... '9':
+ rs->esc_param = rs->esc_param * 10 + (ch - '0');
+ goto the_end;
+ case '~':
+ switch(rs->esc_param) {
+ case 1:
+ readline_bol(rs);
+ break;
+ case 3:
+ readline_delete_char(rs);
+ break;
+ case 4:
+ readline_eol(rs);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ rs->esc_state = IS_NORM;
+ the_end:
+ break;
+ }
+ readline_update(rs);
+}
+
+void readline_start(ReadLineState *rs, const char *prompt, int read_password,
+ ReadLineFunc *readline_func, void *opaque)
+{
+ pstrcpy(rs->prompt, sizeof(rs->prompt), prompt);
+ rs->readline_func = readline_func;
+ rs->readline_opaque = opaque;
+ rs->read_password = read_password;
+ readline_restart(rs);
+}
+
+void readline_restart(ReadLineState *rs)
+{
+ rs->cmd_buf_index = 0;
+ rs->cmd_buf_size = 0;
+}
+
+const char *readline_get_history(ReadLineState *rs, unsigned int index)
+{
+ if (index >= READLINE_MAX_CMDS)
+ return NULL;
+ return rs->history[index];
+}
+
+ReadLineState *readline_init(Monitor *mon,
+ ReadLineCompletionFunc *completion_finder)
+{
+ ReadLineState *rs = qemu_mallocz(sizeof(*rs));
+
+ rs->hist_entry = -1;
+ rs->mon = mon;
+ rs->completion_finder = completion_finder;
+
+ return rs;
+}
diff --git a/readline.h b/readline.h
new file mode 100644
index 0000000..fc9806e
--- /dev/null
+++ b/readline.h
@@ -0,0 +1,55 @@
+#ifndef READLINE_H
+#define READLINE_H
+
+#include "qemu-common.h"
+
+#define READLINE_CMD_BUF_SIZE 4095
+#define READLINE_MAX_CMDS 64
+#define READLINE_MAX_COMPLETIONS 256
+
+typedef void ReadLineFunc(Monitor *mon, const char *str, void *opaque);
+typedef void ReadLineCompletionFunc(const char *cmdline);
+
+typedef struct ReadLineState {
+ char cmd_buf[READLINE_CMD_BUF_SIZE + 1];
+ int cmd_buf_index;
+ int cmd_buf_size;
+
+ char last_cmd_buf[READLINE_CMD_BUF_SIZE + 1];
+ int last_cmd_buf_index;
+ int last_cmd_buf_size;
+
+ int esc_state;
+ int esc_param;
+
+ char *history[READLINE_MAX_CMDS];
+ int hist_entry;
+
+ ReadLineCompletionFunc *completion_finder;
+ char *completions[READLINE_MAX_COMPLETIONS];
+ int nb_completions;
+ int completion_index;
+
+ ReadLineFunc *readline_func;
+ void *readline_opaque;
+ int read_password;
+ char prompt[256];
+ Monitor *mon;
+} ReadLineState;
+
+void readline_add_completion(ReadLineState *rs, const char *str);
+void readline_set_completion_index(ReadLineState *rs, int completion_index);
+
+const char *readline_get_history(ReadLineState *rs, unsigned int index);
+
+void readline_handle_byte(ReadLineState *rs, int ch);
+
+void readline_start(ReadLineState *rs, const char *prompt, int read_password,
+ ReadLineFunc *readline_func, void *opaque);
+void readline_restart(ReadLineState *rs);
+void readline_show_prompt(ReadLineState *rs);
+
+ReadLineState *readline_init(Monitor *mon,
+ ReadLineCompletionFunc *completion_finder);
+
+#endif /* !READLINE_H */
diff --git a/roms/seabios/config.mak b/roms/seabios/config.mak
new file mode 100644
index 0000000..5640c74
--- /dev/null
+++ b/roms/seabios/config.mak
@@ -0,0 +1,8 @@
+# Automatically generated by configure - do not modify
+SRC_PATH=/home/rm/src/kvm/projects/local/qemu-kvm/roms/seabios
+CC=gcc
+BCC=bcc
+CPP=cpp
+OBJCOPY=objcopy
+IASL=iasl
+LD=gld
diff --git a/roms/vgabios/config.mak b/roms/vgabios/config.mak
new file mode 100644
index 0000000..d437853
--- /dev/null
+++ b/roms/vgabios/config.mak
@@ -0,0 +1,8 @@
+# Automatically generated by configure - do not modify
+SRC_PATH=/home/rm/src/kvm/projects/local/qemu-kvm/roms/vgabios
+CC=gcc
+BCC=bcc
+CPP=cpp
+OBJCOPY=objcopy
+IASL=iasl
+LD=gld
diff --git a/rules.mak b/rules.mak
new file mode 100644
index 0000000..ed59c9e
--- /dev/null
+++ b/rules.mak
@@ -0,0 +1,64 @@
+
+# Don't use implicit rules or variables
+# we have explicit rules for everything
+MAKEFLAGS += -rR
+
+# Files with this suffixes are final, don't try to generate them
+# using implicit rules
+%.d:
+%.h:
+%.c:
+%.m:
+%.mak:
+
+# Flags for dependency generation
+QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d
+
+%.o: %.c
+ $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@")
+
+%.o: %.S
+ $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@")
+
+%.o: %.m
+ $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@")
+
+LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(1) $(LIBS)," LINK $(TARGET_DIR)$@")
+
+%$(EXESUF): %.o
+ $(call LINK,$^)
+
+%.a:
+ $(call quiet-command,rm -f $@ && $(AR) rcs $@ $^," AR $(TARGET_DIR)$@")
+
+quiet-command = $(if $(V),$1,$(if $(2),@echo $2 && $1, @$1))
+
+# cc-option
+# Usage: CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
+
+cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
+ >/dev/null 2>&1 && echo OK), $2, $3)
+
+VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi
+set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
+
+# find-in-path
+# Usage: $(call find-in-path, prog)
+# Looks in the PATH if the argument contains no slash, else only considers one
+# specific directory. Returns an # empty string if the program doesn't exist
+# there.
+find-in-path = $(if $(find-string /, $1), \
+ $(wildcard $1), \
+ $(wildcard $(patsubst %, %/$1, $(subst :, ,$(PATH)))))
+
+# Generate timestamp files for .h include files
+
+%.h: %.h-timestamp
+ @test -f $@ || cp $< $@
+
+%.h-timestamp: %.mak
+ $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $*.h")
+ @cmp $@ $*.h >/dev/null 2>&1 || cp $@ $*.h
+
+# will delete the target of a rule if commands exit with a nonzero exit status
+.DELETE_ON_ERROR:
diff --git a/rwhandler.c b/rwhandler.c
new file mode 100644
index 0000000..bb2238f
--- /dev/null
+++ b/rwhandler.c
@@ -0,0 +1,87 @@
+#include "rwhandler.h"
+#include "ioport.h"
+#include "cpu-all.h"
+
+#define RWHANDLER_WRITE(name, len, type) \
+static void name(void *opaque, type addr, uint32_t value) \
+{\
+ struct ReadWriteHandler *handler = opaque;\
+ handler->write(handler, addr, value, len);\
+}
+
+#define RWHANDLER_READ(name, len, type) \
+static uint32_t name(void *opaque, type addr) \
+{ \
+ struct ReadWriteHandler *handler = opaque; \
+ return handler->read(handler, addr, len); \
+}
+
+RWHANDLER_WRITE(cpu_io_memory_simple_writeb, 1, target_phys_addr_t);
+RWHANDLER_READ(cpu_io_memory_simple_readb, 1, target_phys_addr_t);
+RWHANDLER_WRITE(cpu_io_memory_simple_writew, 2, target_phys_addr_t);
+RWHANDLER_READ(cpu_io_memory_simple_readw, 2, target_phys_addr_t);
+RWHANDLER_WRITE(cpu_io_memory_simple_writel, 4, target_phys_addr_t);
+RWHANDLER_READ(cpu_io_memory_simple_readl, 4, target_phys_addr_t);
+
+static CPUWriteMemoryFunc * const cpu_io_memory_simple_write[] = {
+ &cpu_io_memory_simple_writeb,
+ &cpu_io_memory_simple_writew,
+ &cpu_io_memory_simple_writel,
+};
+
+static CPUReadMemoryFunc * const cpu_io_memory_simple_read[] = {
+ &cpu_io_memory_simple_readb,
+ &cpu_io_memory_simple_readw,
+ &cpu_io_memory_simple_readl,
+};
+
+int cpu_register_io_memory_simple(struct ReadWriteHandler *handler, int endian)
+{
+ if (!handler->read || !handler->write) {
+ return -1;
+ }
+ return cpu_register_io_memory(cpu_io_memory_simple_read,
+ cpu_io_memory_simple_write,
+ handler, endian);
+}
+
+RWHANDLER_WRITE(ioport_simple_writeb, 1, uint32_t);
+RWHANDLER_READ(ioport_simple_readb, 1, uint32_t);
+RWHANDLER_WRITE(ioport_simple_writew, 2, uint32_t);
+RWHANDLER_READ(ioport_simple_readw, 2, uint32_t);
+RWHANDLER_WRITE(ioport_simple_writel, 4, uint32_t);
+RWHANDLER_READ(ioport_simple_readl, 4, uint32_t);
+
+int register_ioport_simple(ReadWriteHandler* handler,
+ pio_addr_t start, int length, int size)
+{
+ IOPortWriteFunc *write;
+ IOPortReadFunc *read;
+ int r;
+ switch (size) {
+ case 1:
+ write = ioport_simple_writeb;
+ read = ioport_simple_readb;
+ break;
+ case 2:
+ write = ioport_simple_writew;
+ read = ioport_simple_readw;
+ break;
+ default:
+ write = ioport_simple_writel;
+ read = ioport_simple_readl;
+ }
+ if (handler->write) {
+ r = register_ioport_write(start, length, size, write, handler);
+ if (r < 0) {
+ return r;
+ }
+ }
+ if (handler->read) {
+ r = register_ioport_read(start, length, size, read, handler);
+ if (r < 0) {
+ return r;
+ }
+ }
+ return 0;
+}
diff --git a/rwhandler.h b/rwhandler.h
new file mode 100644
index 0000000..b2a5790
--- /dev/null
+++ b/rwhandler.h
@@ -0,0 +1,27 @@
+#ifndef READ_WRITE_HANDLER_H
+#define READ_WRITE_HANDLER_H
+
+#include "qemu-common.h"
+#include "ioport.h"
+
+typedef struct ReadWriteHandler ReadWriteHandler;
+
+/* len is guaranteed to be one of 1, 2 or 4, addr is guaranteed to fit in an
+ * appropriate type (io/memory/etc). They do not need to be range checked. */
+typedef void WriteHandlerFunc(ReadWriteHandler *, pcibus_t addr,
+ uint32_t value, int len);
+typedef uint32_t ReadHandlerFunc(ReadWriteHandler *, pcibus_t addr, int len);
+
+struct ReadWriteHandler {
+ WriteHandlerFunc *write;
+ ReadHandlerFunc *read;
+};
+
+/* Helpers for when we want to use a single routine with length. */
+/* CPU memory handler: both read and write must be present. */
+int cpu_register_io_memory_simple(ReadWriteHandler *, int endian);
+/* io port handler: can supply only read or write handlers. */
+int register_ioport_simple(ReadWriteHandler *,
+ pio_addr_t start, int length, int size);
+
+#endif
diff --git a/s390-dis.c b/s390-dis.c
new file mode 100644
index 0000000..8abcdf0
--- /dev/null
+++ b/s390-dis.c
@@ -0,0 +1,1796 @@
+/* opcodes/s390-dis.c revision 1.12 */
+/* s390-dis.c -- Disassemble S390 instructions
+ Copyright 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+ Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
+
+ This file is part of GDB, GAS and the GNU binutils.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "qemu-common.h"
+#include "dis-asm.h"
+
+/* include/opcode/s390.h revision 1.9 */
+/* s390.h -- Header file for S390 opcode table
+ Copyright 2000, 2001, 2003 Free Software Foundation, Inc.
+ Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef S390_H
+#define S390_H
+
+/* List of instruction sets variations. */
+
+enum s390_opcode_mode_val
+ {
+ S390_OPCODE_ESA = 0,
+ S390_OPCODE_ZARCH
+ };
+
+enum s390_opcode_cpu_val
+ {
+ S390_OPCODE_G5 = 0,
+ S390_OPCODE_G6,
+ S390_OPCODE_Z900,
+ S390_OPCODE_Z990,
+ S390_OPCODE_Z9_109,
+ S390_OPCODE_Z9_EC,
+ S390_OPCODE_Z10
+ };
+
+/* The opcode table is an array of struct s390_opcode. */
+
+struct s390_opcode
+ {
+ /* The opcode name. */
+ const char * name;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ unsigned char opcode[6];
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ unsigned char mask[6];
+
+ /* The opcode length in bytes. */
+ int oplen;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ unsigned char operands[6];
+
+ /* Bitmask of execution modes this opcode is available for. */
+ unsigned int modes;
+
+ /* First cpu this opcode is available for. */
+ enum s390_opcode_cpu_val min_cpu;
+ };
+
+/* The table itself is sorted by major opcode number, and is otherwise
+ in the order in which the disassembler should consider
+ instructions. */
+/* QEMU: Mark these static. */
+static const struct s390_opcode s390_opcodes[];
+static const int s390_num_opcodes;
+
+/* A opcode format table for the .insn pseudo mnemonic. */
+static const struct s390_opcode s390_opformats[];
+static const int s390_num_opformats;
+
+/* Values defined for the flags field of a struct powerpc_opcode. */
+
+/* The operands table is an array of struct s390_operand. */
+
+struct s390_operand
+ {
+ /* The number of bits in the operand. */
+ int bits;
+
+ /* How far the operand is left shifted in the instruction. */
+ int shift;
+
+ /* One bit syntax flags. */
+ unsigned long flags;
+ };
+
+/* Elements in the table are retrieved by indexing with values from
+ the operands field of the powerpc_opcodes table. */
+
+static const struct s390_operand s390_operands[];
+
+/* Values defined for the flags field of a struct s390_operand. */
+
+/* This operand names a register. The disassembler uses this to print
+ register names with a leading 'r'. */
+#define S390_OPERAND_GPR 0x1
+
+/* This operand names a floating point register. The disassembler
+ prints these with a leading 'f'. */
+#define S390_OPERAND_FPR 0x2
+
+/* This operand names an access register. The disassembler
+ prints these with a leading 'a'. */
+#define S390_OPERAND_AR 0x4
+
+/* This operand names a control register. The disassembler
+ prints these with a leading 'c'. */
+#define S390_OPERAND_CR 0x8
+
+/* This operand is a displacement. */
+#define S390_OPERAND_DISP 0x10
+
+/* This operand names a base register. */
+#define S390_OPERAND_BASE 0x20
+
+/* This operand names an index register, it can be skipped. */
+#define S390_OPERAND_INDEX 0x40
+
+/* This operand is a relative branch displacement. The disassembler
+ prints these symbolically if possible. */
+#define S390_OPERAND_PCREL 0x80
+
+/* This operand takes signed values. */
+#define S390_OPERAND_SIGNED 0x100
+
+/* This operand is a length. */
+#define S390_OPERAND_LENGTH 0x200
+
+/* This operand is optional. Only a single operand at the end of
+ the instruction may be optional. */
+#define S390_OPERAND_OPTIONAL 0x400
+
+/* QEMU-ADD */
+/* ??? Not quite the format the assembler takes, but easy to implement
+ without recourse to the table generator. */
+#define S390_OPERAND_CCODE 0x800
+
+static const char s390_ccode_name[16][4] = {
+ "n", /* 0000 */
+ "o", /* 0001 */
+ "h", /* 0010 */
+ "nle", /* 0011 */
+ "l", /* 0100 */
+ "nhe", /* 0101 */
+ "lh", /* 0110 */
+ "ne", /* 0111 */
+ "e", /* 1000 */
+ "nlh", /* 1001 */
+ "he", /* 1010 */
+ "nl", /* 1011 */
+ "le", /* 1100 */
+ "nh", /* 1101 */
+ "no", /* 1110 */
+ "a" /* 1111 */
+};
+/* QEMU-END */
+
+#endif /* S390_H */
+
+static int init_flag = 0;
+static int opc_index[256];
+
+/* QEMU: We've disabled the architecture check below. */
+/* static int current_arch_mask = 0; */
+
+/* Set up index table for first opcode byte. */
+
+static void
+init_disasm (struct disassemble_info *info)
+{
+ const struct s390_opcode *opcode;
+ const struct s390_opcode *opcode_end;
+
+ memset (opc_index, 0, sizeof (opc_index));
+ opcode_end = s390_opcodes + s390_num_opcodes;
+ for (opcode = s390_opcodes; opcode < opcode_end; opcode++)
+ {
+ opc_index[(int) opcode->opcode[0]] = opcode - s390_opcodes;
+ while ((opcode < opcode_end) &&
+ (opcode[1].opcode[0] == opcode->opcode[0]))
+ opcode++;
+ }
+
+#ifdef QEMU_DISABLE
+ switch (info->mach)
+ {
+ case bfd_mach_s390_31:
+ current_arch_mask = 1 << S390_OPCODE_ESA;
+ break;
+ case bfd_mach_s390_64:
+ current_arch_mask = 1 << S390_OPCODE_ZARCH;
+ break;
+ default:
+ abort ();
+ }
+#endif /* QEMU_DISABLE */
+
+ init_flag = 1;
+}
+
+/* Extracts an operand value from an instruction. */
+
+static inline unsigned int
+s390_extract_operand (unsigned char *insn, const struct s390_operand *operand)
+{
+ unsigned int val;
+ int bits;
+
+ /* Extract fragments of the operand byte for byte. */
+ insn += operand->shift / 8;
+ bits = (operand->shift & 7) + operand->bits;
+ val = 0;
+ do
+ {
+ val <<= 8;
+ val |= (unsigned int) *insn++;
+ bits -= 8;
+ }
+ while (bits > 0);
+ val >>= -bits;
+ val &= ((1U << (operand->bits - 1)) << 1) - 1;
+
+ /* Check for special long displacement case. */
+ if (operand->bits == 20 && operand->shift == 20)
+ val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
+
+ /* Sign extend value if the operand is signed or pc relative. */
+ if ((operand->flags & (S390_OPERAND_SIGNED | S390_OPERAND_PCREL))
+ && (val & (1U << (operand->bits - 1))))
+ val |= (-1U << (operand->bits - 1)) << 1;
+
+ /* Double value if the operand is pc relative. */
+ if (operand->flags & S390_OPERAND_PCREL)
+ val <<= 1;
+
+ /* Length x in an instructions has real length x + 1. */
+ if (operand->flags & S390_OPERAND_LENGTH)
+ val++;
+ return val;
+}
+
+/* Print a S390 instruction. */
+
+int
+print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
+{
+ bfd_byte buffer[6];
+ const struct s390_opcode *opcode;
+ const struct s390_opcode *opcode_end;
+ unsigned int value;
+ int status, opsize, bufsize;
+ char separator;
+
+ if (init_flag == 0)
+ init_disasm (info);
+
+ /* The output looks better if we put 6 bytes on a line. */
+ info->bytes_per_line = 6;
+
+ /* Every S390 instruction is max 6 bytes long. */
+ memset (buffer, 0, 6);
+ status = (*info->read_memory_func) (memaddr, buffer, 6, info);
+ if (status != 0)
+ {
+ for (bufsize = 0; bufsize < 6; bufsize++)
+ if ((*info->read_memory_func) (memaddr, buffer, bufsize + 1, info) != 0)
+ break;
+ if (bufsize <= 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ /* Opsize calculation looks strange but it works
+ 00xxxxxx -> 2 bytes, 01xxxxxx/10xxxxxx -> 4 bytes,
+ 11xxxxxx -> 6 bytes. */
+ opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1;
+ status = opsize > bufsize;
+ }
+ else
+ {
+ bufsize = 6;
+ opsize = ((((buffer[0] >> 6) + 1) >> 1) + 1) << 1;
+ }
+
+ if (status == 0)
+ {
+ /* Find the first match in the opcode table. */
+ opcode_end = s390_opcodes + s390_num_opcodes;
+ for (opcode = s390_opcodes + opc_index[(int) buffer[0]];
+ (opcode < opcode_end) && (buffer[0] == opcode->opcode[0]);
+ opcode++)
+ {
+ const struct s390_operand *operand;
+ const unsigned char *opindex;
+
+#ifdef QEMU_DISABLE
+ /* Check architecture. */
+ if (!(opcode->modes & current_arch_mask))
+ continue;
+#endif /* QEMU_DISABLE */
+
+ /* Check signature of the opcode. */
+ if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1]
+ || (buffer[2] & opcode->mask[2]) != opcode->opcode[2]
+ || (buffer[3] & opcode->mask[3]) != opcode->opcode[3]
+ || (buffer[4] & opcode->mask[4]) != opcode->opcode[4]
+ || (buffer[5] & opcode->mask[5]) != opcode->opcode[5])
+ continue;
+
+ /* The instruction is valid. */
+/* QEMU-MOD */
+ (*info->fprintf_func) (info->stream, "%s", opcode->name);
+
+ if (s390_operands[opcode->operands[0]].flags & S390_OPERAND_CCODE)
+ separator = 0;
+ else
+ separator = '\t';
+/* QEMU-END */
+
+ /* Extract the operands. */
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ unsigned int value;
+
+ operand = s390_operands + *opindex;
+ value = s390_extract_operand (buffer, operand);
+
+ if ((operand->flags & S390_OPERAND_INDEX) && value == 0)
+ continue;
+ if ((operand->flags & S390_OPERAND_BASE) &&
+ value == 0 && separator == '(')
+ {
+ separator = ',';
+ continue;
+ }
+
+ if (separator)
+ (*info->fprintf_func) (info->stream, "%c", separator);
+
+ if (operand->flags & S390_OPERAND_GPR)
+ (*info->fprintf_func) (info->stream, "%%r%i", value);
+ else if (operand->flags & S390_OPERAND_FPR)
+ (*info->fprintf_func) (info->stream, "%%f%i", value);
+ else if (operand->flags & S390_OPERAND_AR)
+ (*info->fprintf_func) (info->stream, "%%a%i", value);
+ else if (operand->flags & S390_OPERAND_CR)
+ (*info->fprintf_func) (info->stream, "%%c%i", value);
+ else if (operand->flags & S390_OPERAND_PCREL)
+ (*info->print_address_func) (memaddr + (int) value, info);
+ else if (operand->flags & S390_OPERAND_SIGNED)
+ (*info->fprintf_func) (info->stream, "%i", (int) value);
+/* QEMU-ADD */
+ else if (operand->flags & S390_OPERAND_CCODE)
+ {
+ (*info->fprintf_func) (info->stream, "%s",
+ s390_ccode_name[(int) value]);
+ separator = '\t';
+ continue;
+ }
+/* QEMU-END */
+ else
+ (*info->fprintf_func) (info->stream, "%u", value);
+
+ if (operand->flags & S390_OPERAND_DISP)
+ {
+ separator = '(';
+ }
+ else if (operand->flags & S390_OPERAND_BASE)
+ {
+ (*info->fprintf_func) (info->stream, ")");
+ separator = ',';
+ }
+ else
+ separator = ',';
+ }
+
+ /* Found instruction, printed it, return its size. */
+ return opsize;
+ }
+ /* No matching instruction found, fall through to hex print. */
+ }
+
+ if (bufsize >= 4)
+ {
+ value = (unsigned int) buffer[0];
+ value = (value << 8) + (unsigned int) buffer[1];
+ value = (value << 8) + (unsigned int) buffer[2];
+ value = (value << 8) + (unsigned int) buffer[3];
+ (*info->fprintf_func) (info->stream, ".long\t0x%08x", value);
+ return 4;
+ }
+ else if (bufsize >= 2)
+ {
+ value = (unsigned int) buffer[0];
+ value = (value << 8) + (unsigned int) buffer[1];
+ (*info->fprintf_func) (info->stream, ".short\t0x%04x", value);
+ return 2;
+ }
+ else
+ {
+ value = (unsigned int) buffer[0];
+ (*info->fprintf_func) (info->stream, ".byte\t0x%02x", value);
+ return 1;
+ }
+}
+
+/* opcodes/s390-opc.c revision 1.16 */
+/* s390-opc.c -- S390 opcode list
+ Copyright 2000, 2001, 2003 Free Software Foundation, Inc.
+ Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
+
+ This file is part of GDB, GAS, and the GNU binutils.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file holds the S390 opcode table. The opcode table
+ includes almost all of the extended instruction mnemonics. This
+ permits the disassembler to use them, and simplifies the assembler
+ logic, at the cost of increasing the table size. The table is
+ strictly constant data, so the compiler should be able to put it in
+ the .text section.
+
+ This file also holds the operand table. All knowledge about
+ inserting operands into instructions and vice-versa is kept in this
+ file. */
+
+/* The operands table.
+ The fields are bits, shift, insert, extract, flags. */
+
+static const struct s390_operand s390_operands[] =
+{
+#define UNUSED 0
+ { 0, 0, 0 }, /* Indicates the end of the operand list */
+
+#define R_8 1 /* GPR starting at position 8 */
+ { 4, 8, S390_OPERAND_GPR },
+#define R_12 2 /* GPR starting at position 12 */
+ { 4, 12, S390_OPERAND_GPR },
+#define R_16 3 /* GPR starting at position 16 */
+ { 4, 16, S390_OPERAND_GPR },
+#define R_20 4 /* GPR starting at position 20 */
+ { 4, 20, S390_OPERAND_GPR },
+#define R_24 5 /* GPR starting at position 24 */
+ { 4, 24, S390_OPERAND_GPR },
+#define R_28 6 /* GPR starting at position 28 */
+ { 4, 28, S390_OPERAND_GPR },
+#define R_32 7 /* GPR starting at position 32 */
+ { 4, 32, S390_OPERAND_GPR },
+
+#define F_8 8 /* FPR starting at position 8 */
+ { 4, 8, S390_OPERAND_FPR },
+#define F_12 9 /* FPR starting at position 12 */
+ { 4, 12, S390_OPERAND_FPR },
+#define F_16 10 /* FPR starting at position 16 */
+ { 4, 16, S390_OPERAND_FPR },
+#define F_20 11 /* FPR starting at position 16 */
+ { 4, 16, S390_OPERAND_FPR },
+#define F_24 12 /* FPR starting at position 24 */
+ { 4, 24, S390_OPERAND_FPR },
+#define F_28 13 /* FPR starting at position 28 */
+ { 4, 28, S390_OPERAND_FPR },
+#define F_32 14 /* FPR starting at position 32 */
+ { 4, 32, S390_OPERAND_FPR },
+
+#define A_8 15 /* Access reg. starting at position 8 */
+ { 4, 8, S390_OPERAND_AR },
+#define A_12 16 /* Access reg. starting at position 12 */
+ { 4, 12, S390_OPERAND_AR },
+#define A_24 17 /* Access reg. starting at position 24 */
+ { 4, 24, S390_OPERAND_AR },
+#define A_28 18 /* Access reg. starting at position 28 */
+ { 4, 28, S390_OPERAND_AR },
+
+#define C_8 19 /* Control reg. starting at position 8 */
+ { 4, 8, S390_OPERAND_CR },
+#define C_12 20 /* Control reg. starting at position 12 */
+ { 4, 12, S390_OPERAND_CR },
+
+#define B_16 21 /* Base register starting at position 16 */
+ { 4, 16, S390_OPERAND_BASE|S390_OPERAND_GPR },
+#define B_32 22 /* Base register starting at position 32 */
+ { 4, 32, S390_OPERAND_BASE|S390_OPERAND_GPR },
+
+#define X_12 23 /* Index register starting at position 12 */
+ { 4, 12, S390_OPERAND_INDEX|S390_OPERAND_GPR },
+
+#define D_20 24 /* Displacement starting at position 20 */
+ { 12, 20, S390_OPERAND_DISP },
+#define D_36 25 /* Displacement starting at position 36 */
+ { 12, 36, S390_OPERAND_DISP },
+#define D20_20 26 /* 20 bit displacement starting at 20 */
+ { 20, 20, S390_OPERAND_DISP|S390_OPERAND_SIGNED },
+
+#define L4_8 27 /* 4 bit length starting at position 8 */
+ { 4, 8, S390_OPERAND_LENGTH },
+#define L4_12 28 /* 4 bit length starting at position 12 */
+ { 4, 12, S390_OPERAND_LENGTH },
+#define L8_8 29 /* 8 bit length starting at position 8 */
+ { 8, 8, S390_OPERAND_LENGTH },
+
+#define U4_8 30 /* 4 bit unsigned value starting at 8 */
+ { 4, 8, 0 },
+#define U4_12 31 /* 4 bit unsigned value starting at 12 */
+ { 4, 12, 0 },
+#define U4_16 32 /* 4 bit unsigned value starting at 16 */
+ { 4, 16, 0 },
+#define U4_20 33 /* 4 bit unsigned value starting at 20 */
+ { 4, 20, 0 },
+#define U8_8 34 /* 8 bit unsigned value starting at 8 */
+ { 8, 8, 0 },
+#define U8_16 35 /* 8 bit unsigned value starting at 16 */
+ { 8, 16, 0 },
+#define I16_16 36 /* 16 bit signed value starting at 16 */
+ { 16, 16, S390_OPERAND_SIGNED },
+#define U16_16 37 /* 16 bit unsigned value starting at 16 */
+ { 16, 16, 0 },
+#define J16_16 38 /* PC relative jump offset at 16 */
+ { 16, 16, S390_OPERAND_PCREL },
+#define J32_16 39 /* PC relative long offset at 16 */
+ { 32, 16, S390_OPERAND_PCREL },
+#define I32_16 40 /* 32 bit signed value starting at 16 */
+ { 32, 16, S390_OPERAND_SIGNED },
+#define U32_16 41 /* 32 bit unsigned value starting at 16 */
+ { 32, 16, 0 },
+#define M_16 42 /* 4 bit optional mask starting at 16 */
+ { 4, 16, S390_OPERAND_OPTIONAL },
+#define RO_28 43 /* optional GPR starting at position 28 */
+ { 4, 28, (S390_OPERAND_GPR | S390_OPERAND_OPTIONAL) },
+
+/* QEMU-ADD: */
+#define M4_12 44 /* 4-bit condition-code starting at 12 */
+ { 4, 12, S390_OPERAND_CCODE },
+#define M4_32 45 /* 4-bit condition-code starting at 32 */
+ { 4, 32, S390_OPERAND_CCODE },
+#define I8_32 46 /* 8 bit signed value starting at 32 */
+ { 8, 32, S390_OPERAND_SIGNED },
+/* QEMU-END */
+};
+
+
+/* Macros used to form opcodes. */
+
+/* 8/16/48 bit opcodes. */
+#define OP8(x) { x, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define OP16(x) { x >> 8, x & 255, 0x00, 0x00, 0x00, 0x00 }
+#define OP48(x) { x >> 40, (x >> 32) & 255, (x >> 24) & 255, \
+ (x >> 16) & 255, (x >> 8) & 255, x & 255}
+
+/* The new format of the INSTR_x_y and MASK_x_y defines is based
+ on the following rules:
+ 1) the middle part of the definition (x in INSTR_x_y) is the official
+ names of the instruction format that you can find in the principals
+ of operation.
+ 2) the last part of the definition (y in INSTR_x_y) gives you an idea
+ which operands the binary represenation of the instruction has.
+ The meanings of the letters in y are:
+ a - access register
+ c - control register
+ d - displacement, 12 bit
+ f - floating pointer register
+ i - signed integer, 4, 8, 16 or 32 bit
+ l - length, 4 or 8 bit
+ p - pc relative
+ r - general purpose register
+ u - unsigned integer, 4, 8, 16 or 32 bit
+ m - mode field, 4 bit
+ 0 - operand skipped.
+ The order of the letters reflects the layout of the format in
+ storage and not the order of the paramaters of the instructions.
+ The use of the letters is not a 100% match with the PoP but it is
+ quite close.
+
+ For example the instruction "mvo" is defined in the PoP as follows:
+
+ MVO D1(L1,B1),D2(L2,B2) [SS]
+
+ --------------------------------------
+ | 'F1' | L1 | L2 | B1 | D1 | B2 | D2 |
+ --------------------------------------
+ 0 8 12 16 20 32 36
+
+ The instruction format is: INSTR_SS_LLRDRD / MASK_SS_LLRDRD. */
+
+#define INSTR_E 2, { 0,0,0,0,0,0 } /* e.g. pr */
+#define INSTR_RIE_RRP 6, { R_8,R_12,J16_16,0,0,0 } /* e.g. brxhg */
+#define INSTR_RIL_0P 6, { J32_16,0,0,0,0 } /* e.g. jg */
+#define INSTR_RIL_RP 6, { R_8,J32_16,0,0,0,0 } /* e.g. brasl */
+#define INSTR_RIL_UP 6, { U4_8,J32_16,0,0,0,0 } /* e.g. brcl */
+#define INSTR_RIL_RI 6, { R_8,I32_16,0,0,0,0 } /* e.g. afi */
+#define INSTR_RIL_RU 6, { R_8,U32_16,0,0,0,0 } /* e.g. alfi */
+#define INSTR_RI_0P 4, { J16_16,0,0,0,0,0 } /* e.g. j */
+#define INSTR_RI_RI 4, { R_8,I16_16,0,0,0,0 } /* e.g. ahi */
+#define INSTR_RI_RP 4, { R_8,J16_16,0,0,0,0 } /* e.g. brct */
+#define INSTR_RI_RU 4, { R_8,U16_16,0,0,0,0 } /* e.g. tml */
+#define INSTR_RI_UP 4, { U4_8,J16_16,0,0,0,0 } /* e.g. brc */
+#define INSTR_RRE_00 4, { 0,0,0,0,0,0 } /* e.g. palb */
+#define INSTR_RRE_0R 4, { R_28,0,0,0,0,0 } /* e.g. tb */
+#define INSTR_RRE_AA 4, { A_24,A_28,0,0,0,0 } /* e.g. cpya */
+#define INSTR_RRE_AR 4, { A_24,R_28,0,0,0,0 } /* e.g. sar */
+#define INSTR_RRE_F0 4, { F_24,0,0,0,0,0 } /* e.g. sqer */
+#define INSTR_RRE_FF 4, { F_24,F_28,0,0,0,0 } /* e.g. debr */
+#define INSTR_RRE_R0 4, { R_24,0,0,0,0,0 } /* e.g. ipm */
+#define INSTR_RRE_RA 4, { R_24,A_28,0,0,0,0 } /* e.g. ear */
+#define INSTR_RRE_RF 4, { R_24,F_28,0,0,0,0 } /* e.g. cefbr */
+#define INSTR_RRE_RR 4, { R_24,R_28,0,0,0,0 } /* e.g. lura */
+#define INSTR_RRE_FR 4, { F_24,R_28,0,0,0,0 } /* e.g. ldgr */
+/* Actually efpc and sfpc do not take an optional operand.
+ This is just a workaround for existing code e.g. glibc. */
+#define INSTR_RRE_RR_OPT 4, { R_24,RO_28,0,0,0,0 } /* efpc, sfpc */
+#define INSTR_RRF_F0FF 4, { F_16,F_24,F_28,0,0,0 } /* e.g. madbr */
+#define INSTR_RRF_F0FF2 4, { F_24,F_16,F_28,0,0,0 } /* e.g. cpsdr */
+#define INSTR_RRF_F0FR 4, { F_24,F_16,R_28,0,0,0 } /* e.g. iedtr */
+#define INSTR_RRF_FUFF 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. didbr */
+#define INSTR_RRF_RURR 4, { R_24,R_28,R_16,U4_20,0,0 } /* e.g. .insn */
+#define INSTR_RRF_R0RR 4, { R_24,R_28,R_16,0,0,0 } /* e.g. idte */
+#define INSTR_RRF_U0FF 4, { F_24,U4_16,F_28,0,0,0 } /* e.g. fixr */
+#define INSTR_RRF_U0RF 4, { R_24,U4_16,F_28,0,0,0 } /* e.g. cfebr */
+#define INSTR_RRF_UUFF 4, { F_24,U4_16,F_28,U4_20,0,0 } /* e.g. fidtr */
+#define INSTR_RRF_0UFF 4, { F_24,F_28,U4_20,0,0,0 } /* e.g. ldetr */
+#define INSTR_RRF_FFFU 4, { F_24,F_16,F_28,U4_20,0,0 } /* e.g. qadtr */
+#define INSTR_RRF_M0RR 4, { R_24,R_28,M_16,0,0,0 } /* e.g. sske */
+#define INSTR_RR_0R 2, { R_12, 0,0,0,0,0 } /* e.g. br */
+#define INSTR_RR_FF 2, { F_8,F_12,0,0,0,0 } /* e.g. adr */
+#define INSTR_RR_R0 2, { R_8, 0,0,0,0,0 } /* e.g. spm */
+#define INSTR_RR_RR 2, { R_8,R_12,0,0,0,0 } /* e.g. lr */
+#define INSTR_RR_U0 2, { U8_8, 0,0,0,0,0 } /* e.g. svc */
+#define INSTR_RR_UR 2, { U4_8,R_12,0,0,0,0 } /* e.g. bcr */
+#define INSTR_RRR_F0FF 4, { F_24,F_28,F_16,0,0,0 } /* e.g. ddtr */
+#define INSTR_RSE_RRRD 6, { R_8,R_12,D_20,B_16,0,0 } /* e.g. lmh */
+#define INSTR_RSE_CCRD 6, { C_8,C_12,D_20,B_16,0,0 } /* e.g. lmh */
+#define INSTR_RSE_RURD 6, { R_8,U4_12,D_20,B_16,0,0 } /* e.g. icmh */
+#define INSTR_RSL_R0RD 6, { R_8,D_20,B_16,0,0,0 } /* e.g. tp */
+#define INSTR_RSI_RRP 4, { R_8,R_12,J16_16,0,0,0 } /* e.g. brxh */
+#define INSTR_RSY_RRRD 6, { R_8,R_12,D20_20,B_16,0,0 } /* e.g. stmy */
+#define INSTR_RSY_RURD 6, { R_8,U4_12,D20_20,B_16,0,0 } /* e.g. icmh */
+#define INSTR_RSY_AARD 6, { A_8,A_12,D20_20,B_16,0,0 } /* e.g. lamy */
+#define INSTR_RSY_CCRD 6, { C_8,C_12,D20_20,B_16,0,0 } /* e.g. lamy */
+#define INSTR_RS_AARD 4, { A_8,A_12,D_20,B_16,0,0 } /* e.g. lam */
+#define INSTR_RS_CCRD 4, { C_8,C_12,D_20,B_16,0,0 } /* e.g. lctl */
+#define INSTR_RS_R0RD 4, { R_8,D_20,B_16,0,0,0 } /* e.g. sll */
+#define INSTR_RS_RRRD 4, { R_8,R_12,D_20,B_16,0,0 } /* e.g. cs */
+#define INSTR_RS_RURD 4, { R_8,U4_12,D_20,B_16,0,0 } /* e.g. icm */
+#define INSTR_RXE_FRRD 6, { F_8,D_20,X_12,B_16,0,0 } /* e.g. axbr */
+#define INSTR_RXE_RRRD 6, { R_8,D_20,X_12,B_16,0,0 } /* e.g. lg */
+#define INSTR_RXF_FRRDF 6, { F_32,F_8,D_20,X_12,B_16,0 } /* e.g. madb */
+#define INSTR_RXF_RRRDR 6, { R_32,R_8,D_20,X_12,B_16,0 } /* e.g. .insn */
+#define INSTR_RXY_RRRD 6, { R_8,D20_20,X_12,B_16,0,0 } /* e.g. ly */
+#define INSTR_RXY_FRRD 6, { F_8,D20_20,X_12,B_16,0,0 } /* e.g. ley */
+#define INSTR_RX_0RRD 4, { D_20,X_12,B_16,0,0,0 } /* e.g. be */
+#define INSTR_RX_FRRD 4, { F_8,D_20,X_12,B_16,0,0 } /* e.g. ae */
+#define INSTR_RX_RRRD 4, { R_8,D_20,X_12,B_16,0,0 } /* e.g. l */
+#define INSTR_RX_URRD 4, { U4_8,D_20,X_12,B_16,0,0 } /* e.g. bc */
+#define INSTR_SI_URD 4, { D_20,B_16,U8_8,0,0,0 } /* e.g. cli */
+#define INSTR_SIY_URD 6, { D20_20,B_16,U8_8,0,0,0 } /* e.g. tmy */
+#define INSTR_SSE_RDRD 6, { D_20,B_16,D_36,B_32,0,0 } /* e.g. mvsdk */
+#define INSTR_SS_L0RDRD 6, { D_20,L8_8,B_16,D_36,B_32,0 } /* e.g. mvc */
+#define INSTR_SS_L2RDRD 6, { D_20,B_16,D_36,L8_8,B_32,0 } /* e.g. pka */
+#define INSTR_SS_LIRDRD 6, { D_20,L4_8,B_16,D_36,B_32,U4_12 } /* e.g. srp */
+#define INSTR_SS_LLRDRD 6, { D_20,L4_8,B_16,D_36,L4_12,B_32 } /* e.g. pack */
+#define INSTR_SS_RRRDRD 6, { D_20,R_8,B_16,D_36,B_32,R_12 } /* e.g. mvck */
+#define INSTR_SS_RRRDRD2 6, { R_8,D_20,B_16,R_12,D_36,B_32 } /* e.g. plo */
+#define INSTR_SS_RRRDRD3 6, { R_8,R_12,D_20,B_16,D_36,B_32 } /* e.g. lmd */
+#define INSTR_S_00 4, { 0,0,0,0,0,0 } /* e.g. hsch */
+#define INSTR_S_RD 4, { D_20,B_16,0,0,0,0 } /* e.g. lpsw */
+#define INSTR_SSF_RRDRD 6, { D_20,B_16,D_36,B_32,R_8,0 } /* e.g. mvcos */
+
+#define MASK_E { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RIE_RRP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RIL_0P { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RIL_RP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RIL_UP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RIL_RI { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RIL_RU { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RI_0P { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RI_RI { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RI_RP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RI_RU { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RI_UP { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRE_00 { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
+#define MASK_RRE_0R { 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00 }
+#define MASK_RRE_AA { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_AR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_F0 { 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00 }
+#define MASK_RRE_FF { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_R0 { 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00 }
+#define MASK_RRE_RA { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_RF { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_RR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_FR { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRE_RR_OPT { 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 }
+#define MASK_RRF_F0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RRF_F0FF2 { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RRF_F0FR { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RRF_FUFF { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRF_RURR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRF_R0RR { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRF_U0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RRF_U0RF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RRF_UUFF { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRF_0UFF { 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00 }
+#define MASK_RRF_FFFU { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRF_M0RR { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RR_0R { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RR_FF { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RR_R0 { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RR_RR { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RR_U0 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RR_UR { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RRR_F0FF { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00 }
+#define MASK_RSE_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSE_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSE_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSL_R0RD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSI_RRP { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RS_AARD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RS_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RS_R0RD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RS_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RS_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RSY_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSY_RURD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSY_AARD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RSY_CCRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RXE_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RXE_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RXF_FRRDF { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RXF_RRRDR { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RXY_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RXY_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_RX_0RRD { 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RX_FRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RX_RRRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_RX_URRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SI_URD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SIY_URD { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+#define MASK_SSE_RDRD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_L0RDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_L2RDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_LIRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_LLRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_RRRDRD { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_RRRDRD2 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SS_RRRDRD3 { 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_S_00 { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 }
+#define MASK_S_RD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }
+#define MASK_SSF_RRDRD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 }
+
+/* QEMU-ADD: */
+#define INSTR_RIE_MRRP 6, { M4_32,R_8,R_12,J16_16,0,0 } /* e.g. crj */
+#define MASK_RIE_MRRP { 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff }
+
+#define INSTR_RIE_MRIP 6, { M4_12,R_8,I8_32,J16_16,0,0 } /* e.g. cij */
+#define MASK_RIE_MRIP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff }
+/* QEMU-END */
+
+/* The opcode formats table (blueprints for .insn pseudo mnemonic). */
+
+static const struct s390_opcode s390_opformats[] =
+ {
+ { "e", OP8(0x00LL), MASK_E, INSTR_E, 3, 0 },
+ { "ri", OP8(0x00LL), MASK_RI_RI, INSTR_RI_RI, 3, 0 },
+ { "rie", OP8(0x00LL), MASK_RIE_RRP, INSTR_RIE_RRP, 3, 0 },
+ { "ril", OP8(0x00LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 0 },
+ { "rilu", OP8(0x00LL), MASK_RIL_RU, INSTR_RIL_RU, 3, 0 },
+ { "rr", OP8(0x00LL), MASK_RR_RR, INSTR_RR_RR, 3, 0 },
+ { "rre", OP8(0x00LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0 },
+ { "rrf", OP8(0x00LL), MASK_RRF_RURR, INSTR_RRF_RURR, 3, 0 },
+ { "rs", OP8(0x00LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0 },
+ { "rse", OP8(0x00LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 0 },
+ { "rsi", OP8(0x00LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0 },
+ { "rsy", OP8(0x00LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3 },
+ { "rx", OP8(0x00LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0 },
+ { "rxe", OP8(0x00LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 0 },
+ { "rxf", OP8(0x00LL), MASK_RXF_RRRDR, INSTR_RXF_RRRDR,3, 0 },
+ { "rxy", OP8(0x00LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3 },
+ { "s", OP8(0x00LL), MASK_S_RD, INSTR_S_RD, 3, 0 },
+ { "si", OP8(0x00LL), MASK_SI_URD, INSTR_SI_URD, 3, 0 },
+ { "siy", OP8(0x00LL), MASK_SIY_URD, INSTR_SIY_URD, 3, 3 },
+ { "ss", OP8(0x00LL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD,3, 0 },
+ { "sse", OP8(0x00LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0 },
+ { "ssf", OP8(0x00LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD,3, 0 },
+};
+
+static const int s390_num_opformats =
+ sizeof (s390_opformats) / sizeof (s390_opformats[0]);
+
+/* include "s390-opc.tab" generated from opcodes/s390-opc.txt rev 1.17 */
+/* The opcode table. This file was generated by s390-mkopc.
+
+ The format of the opcode table is:
+
+ NAME OPCODE MASK OPERANDS
+
+ Name is the name of the instruction.
+ OPCODE is the instruction opcode.
+ MASK is the opcode mask; this is used to tell the disassembler
+ which bits in the actual opcode must match OPCODE.
+ OPERANDS is the list of operands.
+
+ The disassembler reads the table in order and prints the first
+ instruction which matches. */
+
+static const struct s390_opcode s390_opcodes[] =
+ {
+ { "dp", OP8(0xfdLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "mp", OP8(0xfcLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "sp", OP8(0xfbLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "ap", OP8(0xfaLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "cp", OP8(0xf9LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "zap", OP8(0xf8LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "unpk", OP8(0xf3LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "pack", OP8(0xf2LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "mvo", OP8(0xf1LL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0},
+ { "srp", OP8(0xf0LL), MASK_SS_LIRDRD, INSTR_SS_LIRDRD, 3, 0},
+ { "lmd", OP8(0xefLL), MASK_SS_RRRDRD3, INSTR_SS_RRRDRD3, 2, 2},
+ { "plo", OP8(0xeeLL), MASK_SS_RRRDRD2, INSTR_SS_RRRDRD2, 3, 0},
+ { "stdy", OP48(0xed0000000067LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3},
+ { "stey", OP48(0xed0000000066LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3},
+ { "ldy", OP48(0xed0000000065LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3},
+ { "ley", OP48(0xed0000000064LL), MASK_RXY_FRRD, INSTR_RXY_FRRD, 2, 3},
+ { "tgxt", OP48(0xed0000000059LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5},
+ { "tcxt", OP48(0xed0000000058LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5},
+ { "tgdt", OP48(0xed0000000055LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5},
+ { "tcdt", OP48(0xed0000000054LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5},
+ { "tget", OP48(0xed0000000051LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5},
+ { "tcet", OP48(0xed0000000050LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 2, 5},
+ { "srxt", OP48(0xed0000000049LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5},
+ { "slxt", OP48(0xed0000000048LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5},
+ { "srdt", OP48(0xed0000000041LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5},
+ { "sldt", OP48(0xed0000000040LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 5},
+ { "msd", OP48(0xed000000003fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3},
+ { "mad", OP48(0xed000000003eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3},
+ { "myh", OP48(0xed000000003dLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4},
+ { "mayh", OP48(0xed000000003cLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4},
+ { "my", OP48(0xed000000003bLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4},
+ { "may", OP48(0xed000000003aLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4},
+ { "myl", OP48(0xed0000000039LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4},
+ { "mayl", OP48(0xed0000000038LL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 2, 4},
+ { "mee", OP48(0xed0000000037LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "sqe", OP48(0xed0000000034LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "mse", OP48(0xed000000002fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3},
+ { "mae", OP48(0xed000000002eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 3},
+ { "lxe", OP48(0xed0000000026LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "lxd", OP48(0xed0000000025LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "lde", OP48(0xed0000000024LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "msdb", OP48(0xed000000001fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0},
+ { "madb", OP48(0xed000000001eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0},
+ { "ddb", OP48(0xed000000001dLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "mdb", OP48(0xed000000001cLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "sdb", OP48(0xed000000001bLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "adb", OP48(0xed000000001aLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "cdb", OP48(0xed0000000019LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "kdb", OP48(0xed0000000018LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "meeb", OP48(0xed0000000017LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "sqdb", OP48(0xed0000000015LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "sqeb", OP48(0xed0000000014LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "tcxb", OP48(0xed0000000012LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "tcdb", OP48(0xed0000000011LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "tceb", OP48(0xed0000000010LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "mseb", OP48(0xed000000000fLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0},
+ { "maeb", OP48(0xed000000000eLL), MASK_RXF_FRRDF, INSTR_RXF_FRRDF, 3, 0},
+ { "deb", OP48(0xed000000000dLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "mdeb", OP48(0xed000000000cLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "seb", OP48(0xed000000000bLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "aeb", OP48(0xed000000000aLL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "ceb", OP48(0xed0000000009LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "keb", OP48(0xed0000000008LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "mxdb", OP48(0xed0000000007LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "lxeb", OP48(0xed0000000006LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "lxdb", OP48(0xed0000000005LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "ldeb", OP48(0xed0000000004LL), MASK_RXE_FRRD, INSTR_RXE_FRRD, 3, 0},
+ { "brxlg", OP48(0xec0000000045LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2},
+ { "brxhg", OP48(0xec0000000044LL), MASK_RIE_RRP, INSTR_RIE_RRP, 2, 2},
+ { "tp", OP48(0xeb00000000c0LL), MASK_RSL_R0RD, INSTR_RSL_R0RD, 3, 0},
+ { "stamy", OP48(0xeb000000009bLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3},
+ { "lamy", OP48(0xeb000000009aLL), MASK_RSY_AARD, INSTR_RSY_AARD, 2, 3},
+ { "lmy", OP48(0xeb0000000098LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "lmh", OP48(0xeb0000000096LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "lmh", OP48(0xeb0000000096LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "stmy", OP48(0xeb0000000090LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "clclu", OP48(0xeb000000008fLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "mvclu", OP48(0xeb000000008eLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3},
+ { "mvclu", OP48(0xeb000000008eLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 0},
+ { "icmy", OP48(0xeb0000000081LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3},
+ { "icmh", OP48(0xeb0000000080LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3},
+ { "icmh", OP48(0xeb0000000080LL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2},
+ { "xiy", OP48(0xeb0000000057LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3},
+ { "oiy", OP48(0xeb0000000056LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3},
+ { "cliy", OP48(0xeb0000000055LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3},
+ { "niy", OP48(0xeb0000000054LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3},
+ { "mviy", OP48(0xeb0000000052LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3},
+ { "tmy", OP48(0xeb0000000051LL), MASK_SIY_URD, INSTR_SIY_URD, 2, 3},
+ { "bxleg", OP48(0xeb0000000045LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "bxleg", OP48(0xeb0000000045LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "bxhg", OP48(0xeb0000000044LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "bxhg", OP48(0xeb0000000044LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "cdsg", OP48(0xeb000000003eLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "cdsg", OP48(0xeb000000003eLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "cdsy", OP48(0xeb0000000031LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "csg", OP48(0xeb0000000030LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "csg", OP48(0xeb0000000030LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "lctlg", OP48(0xeb000000002fLL), MASK_RSY_CCRD, INSTR_RSY_CCRD, 2, 3},
+ { "lctlg", OP48(0xeb000000002fLL), MASK_RSE_CCRD, INSTR_RSE_CCRD, 2, 2},
+ { "stcmy", OP48(0xeb000000002dLL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3},
+ { "stcmh", OP48(0xeb000000002cLL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3},
+ { "stcmh", OP48(0xeb000000002cLL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2},
+ { "stmh", OP48(0xeb0000000026LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "stmh", OP48(0xeb0000000026LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "stctg", OP48(0xeb0000000025LL), MASK_RSY_CCRD, INSTR_RSY_CCRD, 2, 3},
+ { "stctg", OP48(0xeb0000000025LL), MASK_RSE_CCRD, INSTR_RSE_CCRD, 2, 2},
+ { "stmg", OP48(0xeb0000000024LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "stmg", OP48(0xeb0000000024LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "clmy", OP48(0xeb0000000021LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3},
+ { "clmh", OP48(0xeb0000000020LL), MASK_RSY_RURD, INSTR_RSY_RURD, 2, 3},
+ { "clmh", OP48(0xeb0000000020LL), MASK_RSE_RURD, INSTR_RSE_RURD, 2, 2},
+ { "rll", OP48(0xeb000000001dLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 3, 3},
+ { "rll", OP48(0xeb000000001dLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 3, 2},
+ { "rllg", OP48(0xeb000000001cLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "rllg", OP48(0xeb000000001cLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "csy", OP48(0xeb0000000014LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "tracg", OP48(0xeb000000000fLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "tracg", OP48(0xeb000000000fLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "sllg", OP48(0xeb000000000dLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "sllg", OP48(0xeb000000000dLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "srlg", OP48(0xeb000000000cLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "srlg", OP48(0xeb000000000cLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "slag", OP48(0xeb000000000bLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "slag", OP48(0xeb000000000bLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "srag", OP48(0xeb000000000aLL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "srag", OP48(0xeb000000000aLL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "lmg", OP48(0xeb0000000004LL), MASK_RSY_RRRD, INSTR_RSY_RRRD, 2, 3},
+ { "lmg", OP48(0xeb0000000004LL), MASK_RSE_RRRD, INSTR_RSE_RRRD, 2, 2},
+ { "unpka", OP8(0xeaLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "pka", OP8(0xe9LL), MASK_SS_L2RDRD, INSTR_SS_L2RDRD, 3, 0},
+ { "mvcin", OP8(0xe8LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "mvcdk", OP16(0xe50fLL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0},
+ { "mvcsk", OP16(0xe50eLL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0},
+ { "tprot", OP16(0xe501LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0},
+ { "strag", OP48(0xe50000000002LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 2, 2},
+ { "lasp", OP16(0xe500LL), MASK_SSE_RDRD, INSTR_SSE_RDRD, 3, 0},
+ { "slb", OP48(0xe30000000099LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "slb", OP48(0xe30000000099LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "alc", OP48(0xe30000000098LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "alc", OP48(0xe30000000098LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "dl", OP48(0xe30000000097LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "dl", OP48(0xe30000000097LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "ml", OP48(0xe30000000096LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "ml", OP48(0xe30000000096LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "llh", OP48(0xe30000000095LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4},
+ { "llc", OP48(0xe30000000094LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4},
+ { "llgh", OP48(0xe30000000091LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "llgh", OP48(0xe30000000091LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "llgc", OP48(0xe30000000090LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "llgc", OP48(0xe30000000090LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "lpq", OP48(0xe3000000008fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lpq", OP48(0xe3000000008fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "stpq", OP48(0xe3000000008eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "stpq", OP48(0xe3000000008eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "slbg", OP48(0xe30000000089LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "slbg", OP48(0xe30000000089LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "alcg", OP48(0xe30000000088LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "alcg", OP48(0xe30000000088LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "dlg", OP48(0xe30000000087LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "dlg", OP48(0xe30000000087LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "mlg", OP48(0xe30000000086LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "mlg", OP48(0xe30000000086LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "xg", OP48(0xe30000000082LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "xg", OP48(0xe30000000082LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "og", OP48(0xe30000000081LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "og", OP48(0xe30000000081LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "ng", OP48(0xe30000000080LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "ng", OP48(0xe30000000080LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "shy", OP48(0xe3000000007bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "ahy", OP48(0xe3000000007aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "chy", OP48(0xe30000000079LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lhy", OP48(0xe30000000078LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lgb", OP48(0xe30000000077LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lb", OP48(0xe30000000076LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "icy", OP48(0xe30000000073LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "stcy", OP48(0xe30000000072LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lay", OP48(0xe30000000071LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "sthy", OP48(0xe30000000070LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "sly", OP48(0xe3000000005fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "aly", OP48(0xe3000000005eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "sy", OP48(0xe3000000005bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "ay", OP48(0xe3000000005aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "cy", OP48(0xe30000000059LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "ly", OP48(0xe30000000058LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "xy", OP48(0xe30000000057LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "oy", OP48(0xe30000000056LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "cly", OP48(0xe30000000055LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "ny", OP48(0xe30000000054LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "msy", OP48(0xe30000000051LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "sty", OP48(0xe30000000050LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "bctg", OP48(0xe30000000046LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "bctg", OP48(0xe30000000046LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "strvh", OP48(0xe3000000003fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "strvh", OP48(0xe3000000003fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "strv", OP48(0xe3000000003eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "strv", OP48(0xe3000000003eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "clgf", OP48(0xe30000000031LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "clgf", OP48(0xe30000000031LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "cgf", OP48(0xe30000000030LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "cgf", OP48(0xe30000000030LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "strvg", OP48(0xe3000000002fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "strvg", OP48(0xe3000000002fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "cvdg", OP48(0xe3000000002eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "cvdg", OP48(0xe3000000002eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "cvdy", OP48(0xe30000000026LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "stg", OP48(0xe30000000024LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "stg", OP48(0xe30000000024LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "clg", OP48(0xe30000000021LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "clg", OP48(0xe30000000021LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "cg", OP48(0xe30000000020LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "cg", OP48(0xe30000000020LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "lrvh", OP48(0xe3000000001fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "lrvh", OP48(0xe3000000001fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "lrv", OP48(0xe3000000001eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 3, 3},
+ { "lrv", OP48(0xe3000000001eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 3, 2},
+ { "dsgf", OP48(0xe3000000001dLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "dsgf", OP48(0xe3000000001dLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "msgf", OP48(0xe3000000001cLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "msgf", OP48(0xe3000000001cLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "slgf", OP48(0xe3000000001bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "slgf", OP48(0xe3000000001bLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "algf", OP48(0xe3000000001aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "algf", OP48(0xe3000000001aLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "sgf", OP48(0xe30000000019LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "sgf", OP48(0xe30000000019LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "agf", OP48(0xe30000000018LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "agf", OP48(0xe30000000018LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "llgt", OP48(0xe30000000017LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "llgt", OP48(0xe30000000017LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "llgf", OP48(0xe30000000016LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "llgf", OP48(0xe30000000016LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "lgh", OP48(0xe30000000015LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lgh", OP48(0xe30000000015LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "lgf", OP48(0xe30000000014LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lgf", OP48(0xe30000000014LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "lray", OP48(0xe30000000013LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lt", OP48(0xe30000000012LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4},
+ { "lrvg", OP48(0xe3000000000fLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lrvg", OP48(0xe3000000000fLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "cvbg", OP48(0xe3000000000eLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "cvbg", OP48(0xe3000000000eLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "dsg", OP48(0xe3000000000dLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "dsg", OP48(0xe3000000000dLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "msg", OP48(0xe3000000000cLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "msg", OP48(0xe3000000000cLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "slg", OP48(0xe3000000000bLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "slg", OP48(0xe3000000000bLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "alg", OP48(0xe3000000000aLL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "alg", OP48(0xe3000000000aLL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "sg", OP48(0xe30000000009LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "sg", OP48(0xe30000000009LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "ag", OP48(0xe30000000008LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "ag", OP48(0xe30000000008LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "cvby", OP48(0xe30000000006LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lg", OP48(0xe30000000004LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lg", OP48(0xe30000000004LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "lrag", OP48(0xe30000000003LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 3},
+ { "lrag", OP48(0xe30000000003LL), MASK_RXE_RRRD, INSTR_RXE_RRRD, 2, 2},
+ { "ltg", OP48(0xe30000000002LL), MASK_RXY_RRRD, INSTR_RXY_RRRD, 2, 4},
+ { "unpku", OP8(0xe2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "pku", OP8(0xe1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "edmk", OP8(0xdfLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "ed", OP8(0xdeLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "trt", OP8(0xddLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "tr", OP8(0xdcLL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "mvcs", OP8(0xdbLL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0},
+ { "mvcp", OP8(0xdaLL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0},
+ { "mvck", OP8(0xd9LL), MASK_SS_RRRDRD, INSTR_SS_RRRDRD, 3, 0},
+ { "xc", OP8(0xd7LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "oc", OP8(0xd6LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "clc", OP8(0xd5LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "nc", OP8(0xd4LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "mvz", OP8(0xd3LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "mvc", OP8(0xd2LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "mvn", OP8(0xd1LL), MASK_SS_L0RDRD, INSTR_SS_L0RDRD, 3, 0},
+ { "csst", OP16(0xc802LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5},
+ { "ectg", OP16(0xc801LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 5},
+ { "mvcos", OP16(0xc800LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD, 2, 4},
+ { "clfi", OP16(0xc20fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "clgfi", OP16(0xc20eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "cfi", OP16(0xc20dLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4},
+ { "cgfi", OP16(0xc20cLL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4},
+ { "alfi", OP16(0xc20bLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "algfi", OP16(0xc20aLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "afi", OP16(0xc209LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4},
+ { "agfi", OP16(0xc208LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4},
+ { "slfi", OP16(0xc205LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "slgfi", OP16(0xc204LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+/* QEMU-ADD: */
+ { "msfi", OP16(0xc201ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6},
+ { "msgfi", OP16(0xc200ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6},
+/* QEMU-END */
+ { "jg", OP16(0xc0f4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgno", OP16(0xc0e4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnh", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnp", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgle", OP16(0xc0c4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnl", OP16(0xc0b4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnm", OP16(0xc0b4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jghe", OP16(0xc0a4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnlh", OP16(0xc094LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jge", OP16(0xc084LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgz", OP16(0xc084LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgne", OP16(0xc074LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnz", OP16(0xc074LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jglh", OP16(0xc064LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnhe", OP16(0xc054LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgl", OP16(0xc044LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgm", OP16(0xc044LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgnle", OP16(0xc034LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgh", OP16(0xc024LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgp", OP16(0xc024LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "jgo", OP16(0xc014LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2},
+ { "llilf", OP16(0xc00fLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "llihf", OP16(0xc00eLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "oilf", OP16(0xc00dLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "oihf", OP16(0xc00cLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "nilf", OP16(0xc00bLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "nihf", OP16(0xc00aLL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "iilf", OP16(0xc009LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "iihf", OP16(0xc008LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "xilf", OP16(0xc007LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "xihf", OP16(0xc006LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4},
+ { "brasl", OP16(0xc005LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 2},
+ { "brcl", OP16(0xc004LL), MASK_RIL_UP, INSTR_RIL_UP, 3, 2},
+ { "lgfi", OP16(0xc001LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4},
+ { "larl", OP16(0xc000LL), MASK_RIL_RP, INSTR_RIL_RP, 3, 2},
+ { "icm", OP8(0xbfLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0},
+ { "stcm", OP8(0xbeLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0},
+ { "clm", OP8(0xbdLL), MASK_RS_RURD, INSTR_RS_RURD, 3, 0},
+ { "cds", OP8(0xbbLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "cs", OP8(0xbaLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "cu42", OP16(0xb9b3LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cu41", OP16(0xb9b2LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cu24", OP16(0xb9b1LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cu14", OP16(0xb9b0LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "lptea", OP16(0xb9aaLL), MASK_RRF_RURR, INSTR_RRF_RURR, 2, 4},
+ { "esea", OP16(0xb99dLL), MASK_RRE_R0, INSTR_RRE_R0, 2, 2},
+ { "slbr", OP16(0xb999LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2},
+ { "alcr", OP16(0xb998LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2},
+ { "dlr", OP16(0xb997LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2},
+ { "mlr", OP16(0xb996LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2},
+ { "llhr", OP16(0xb995LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "llcr", OP16(0xb994LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "troo", OP16(0xb993LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4},
+ { "troo", OP16(0xb993LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "trot", OP16(0xb992LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4},
+ { "trot", OP16(0xb992LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "trto", OP16(0xb991LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4},
+ { "trto", OP16(0xb991LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "trtt", OP16(0xb990LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 3, 4},
+ { "trtt", OP16(0xb990LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "idte", OP16(0xb98eLL), MASK_RRF_R0RR, INSTR_RRF_R0RR, 2, 3},
+ { "epsw", OP16(0xb98dLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2},
+ { "cspg", OP16(0xb98aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 3},
+ { "slbgr", OP16(0xb989LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "alcgr", OP16(0xb988LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "dlgr", OP16(0xb987LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "mlgr", OP16(0xb986LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "llghr", OP16(0xb985LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "llgcr", OP16(0xb984LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "flogr", OP16(0xb983LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "xgr", OP16(0xb982LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "ogr", OP16(0xb981LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "ngr", OP16(0xb980LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "bctgr", OP16(0xb946LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "klmd", OP16(0xb93fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3},
+ { "kimd", OP16(0xb93eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3},
+ { "clgfr", OP16(0xb931LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cgfr", OP16(0xb930LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "kmc", OP16(0xb92fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3},
+ { "km", OP16(0xb92eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3},
+ { "lhr", OP16(0xb927LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "lbr", OP16(0xb926LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "sturg", OP16(0xb925LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "clgr", OP16(0xb921LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cgr", OP16(0xb920LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lrvr", OP16(0xb91fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 2},
+ { "kmac", OP16(0xb91eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 3},
+ { "dsgfr", OP16(0xb91dLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "msgfr", OP16(0xb91cLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "slgfr", OP16(0xb91bLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "algfr", OP16(0xb91aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "sgfr", OP16(0xb919LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "agfr", OP16(0xb918LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "llgtr", OP16(0xb917LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "llgfr", OP16(0xb916LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lgfr", OP16(0xb914LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lcgfr", OP16(0xb913LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "ltgfr", OP16(0xb912LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lngfr", OP16(0xb911LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lpgfr", OP16(0xb910LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lrvgr", OP16(0xb90fLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "eregg", OP16(0xb90eLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "dsgr", OP16(0xb90dLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "msgr", OP16(0xb90cLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "slgr", OP16(0xb90bLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "algr", OP16(0xb90aLL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "sgr", OP16(0xb909LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "agr", OP16(0xb908LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lghr", OP16(0xb907LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "lgbr", OP16(0xb906LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 4},
+ { "lurag", OP16(0xb905LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lgr", OP16(0xb904LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lcgr", OP16(0xb903LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "ltgr", OP16(0xb902LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lngr", OP16(0xb901LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lpgr", OP16(0xb900LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "lctl", OP8(0xb7LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0},
+ { "stctl", OP8(0xb6LL), MASK_RS_CCRD, INSTR_RS_CCRD, 3, 0},
+ { "rrxtr", OP16(0xb3ffLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5},
+ { "iextr", OP16(0xb3feLL), MASK_RRF_F0FR, INSTR_RRF_F0FR, 2, 5},
+ { "qaxtr", OP16(0xb3fdLL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5},
+ { "cextr", OP16(0xb3fcLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "cxstr", OP16(0xb3fbLL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "cxutr", OP16(0xb3faLL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "cxgtr", OP16(0xb3f9LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "rrdtr", OP16(0xb3f7LL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5},
+ { "iedtr", OP16(0xb3f6LL), MASK_RRF_F0FR, INSTR_RRF_F0FR, 2, 5},
+ { "qadtr", OP16(0xb3f5LL), MASK_RRF_FFFU, INSTR_RRF_FFFU, 2, 5},
+ { "cedtr", OP16(0xb3f4LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "cdstr", OP16(0xb3f3LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "cdutr", OP16(0xb3f2LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "cdgtr", OP16(0xb3f1LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "esxtr", OP16(0xb3efLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "eextr", OP16(0xb3edLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cxtr", OP16(0xb3ecLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "csxtr", OP16(0xb3ebLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cuxtr", OP16(0xb3eaLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cgxtr", OP16(0xb3e9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 5},
+ { "kxtr", OP16(0xb3e8LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "esdtr", OP16(0xb3e7LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "eedtr", OP16(0xb3e5LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cdtr", OP16(0xb3e4LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "csdtr", OP16(0xb3e3LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cudtr", OP16(0xb3e2LL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cgdtr", OP16(0xb3e1LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 5},
+ { "kdtr", OP16(0xb3e0LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "fixtr", OP16(0xb3dfLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5},
+ { "ltxtr", OP16(0xb3deLL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "ldxtr", OP16(0xb3ddLL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5},
+ { "lxdtr", OP16(0xb3dcLL), MASK_RRF_0UFF, INSTR_RRF_0UFF, 2, 5},
+ { "sxtr", OP16(0xb3dbLL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "axtr", OP16(0xb3daLL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "dxtr", OP16(0xb3d9LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "mxtr", OP16(0xb3d8LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "fidtr", OP16(0xb3d7LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5},
+ { "ltdtr", OP16(0xb3d6LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "ledtr", OP16(0xb3d5LL), MASK_RRF_UUFF, INSTR_RRF_UUFF, 2, 5},
+ { "ldetr", OP16(0xb3d4LL), MASK_RRF_0UFF, INSTR_RRF_0UFF, 2, 5},
+ { "sdtr", OP16(0xb3d3LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "adtr", OP16(0xb3d2LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "ddtr", OP16(0xb3d1LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "mdtr", OP16(0xb3d0LL), MASK_RRR_F0FF, INSTR_RRR_F0FF, 2, 5},
+ { "lgdr", OP16(0xb3cdLL), MASK_RRE_RF, INSTR_RRE_RF, 2, 5},
+ { "cgxr", OP16(0xb3caLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cgdr", OP16(0xb3c9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cger", OP16(0xb3c8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cxgr", OP16(0xb3c6LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cdgr", OP16(0xb3c5LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cegr", OP16(0xb3c4LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "ldgr", OP16(0xb3c1LL), MASK_RRE_FR, INSTR_RRE_FR, 2, 5},
+ { "cfxr", OP16(0xb3baLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cfdr", OP16(0xb3b9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cfer", OP16(0xb3b8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cxfr", OP16(0xb3b6LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0},
+ { "cdfr", OP16(0xb3b5LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0},
+ { "cefr", OP16(0xb3b4LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0},
+ { "cgxbr", OP16(0xb3aaLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cgdbr", OP16(0xb3a9LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cgebr", OP16(0xb3a8LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 2, 2},
+ { "cxgbr", OP16(0xb3a6LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cdgbr", OP16(0xb3a5LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cegbr", OP16(0xb3a4LL), MASK_RRE_RR, INSTR_RRE_RR, 2, 2},
+ { "cfxbr", OP16(0xb39aLL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0},
+ { "cfdbr", OP16(0xb399LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0},
+ { "cfebr", OP16(0xb398LL), MASK_RRF_U0RF, INSTR_RRF_U0RF, 3, 0},
+ { "cxfbr", OP16(0xb396LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0},
+ { "cdfbr", OP16(0xb395LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0},
+ { "cefbr", OP16(0xb394LL), MASK_RRE_RF, INSTR_RRE_RF, 3, 0},
+ { "efpc", OP16(0xb38cLL), MASK_RRE_RR_OPT, INSTR_RRE_RR_OPT, 3, 0},
+ { "sfasr", OP16(0xb385LL), MASK_RRE_R0, INSTR_RRE_R0, 2, 5},
+ { "sfpc", OP16(0xb384LL), MASK_RRE_RR_OPT, INSTR_RRE_RR_OPT, 3, 0},
+ { "fidr", OP16(0xb37fLL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "fier", OP16(0xb377LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "lzxr", OP16(0xb376LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "lzdr", OP16(0xb375LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "lzer", OP16(0xb374LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "lcdfr", OP16(0xb373LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "cpsdr", OP16(0xb372LL), MASK_RRF_F0FF2, INSTR_RRF_F0FF2, 2, 5},
+ { "lndfr", OP16(0xb371LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "lpdfr", OP16(0xb370LL), MASK_RRE_FF, INSTR_RRE_FF, 2, 5},
+ { "cxr", OP16(0xb369LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "fixr", OP16(0xb367LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "lexr", OP16(0xb366LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lxr", OP16(0xb365LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "lcxr", OP16(0xb363LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ltxr", OP16(0xb362LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lnxr", OP16(0xb361LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lpxr", OP16(0xb360LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "fidbr", OP16(0xb35fLL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "didbr", OP16(0xb35bLL), MASK_RRF_FUFF, INSTR_RRF_FUFF, 3, 0},
+ { "thdr", OP16(0xb359LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "thder", OP16(0xb358LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "fiebr", OP16(0xb357LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "diebr", OP16(0xb353LL), MASK_RRF_FUFF, INSTR_RRF_FUFF, 3, 0},
+ { "tbdr", OP16(0xb351LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "tbedr", OP16(0xb350LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "dxbr", OP16(0xb34dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "mxbr", OP16(0xb34cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sxbr", OP16(0xb34bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "axbr", OP16(0xb34aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "cxbr", OP16(0xb349LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "kxbr", OP16(0xb348LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "fixbr", OP16(0xb347LL), MASK_RRF_U0FF, INSTR_RRF_U0FF, 3, 0},
+ { "lexbr", OP16(0xb346LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ldxbr", OP16(0xb345LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ledbr", OP16(0xb344LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lcxbr", OP16(0xb343LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ltxbr", OP16(0xb342LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lnxbr", OP16(0xb341LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lpxbr", OP16(0xb340LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "msdr", OP16(0xb33fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3},
+ { "madr", OP16(0xb33eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3},
+ { "myhr", OP16(0xb33dLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4},
+ { "mayhr", OP16(0xb33cLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4},
+ { "myr", OP16(0xb33bLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4},
+ { "mayr", OP16(0xb33aLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4},
+ { "mylr", OP16(0xb339LL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4},
+ { "maylr", OP16(0xb338LL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 2, 4},
+ { "meer", OP16(0xb337LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sqxr", OP16(0xb336LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "mser", OP16(0xb32fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3},
+ { "maer", OP16(0xb32eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 3},
+ { "lxer", OP16(0xb326LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lxdr", OP16(0xb325LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lder", OP16(0xb324LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "msdbr", OP16(0xb31fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0},
+ { "madbr", OP16(0xb31eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0},
+ { "ddbr", OP16(0xb31dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "mdbr", OP16(0xb31cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sdbr", OP16(0xb31bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "adbr", OP16(0xb31aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "cdbr", OP16(0xb319LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "kdbr", OP16(0xb318LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "meebr", OP16(0xb317LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sqxbr", OP16(0xb316LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sqdbr", OP16(0xb315LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sqebr", OP16(0xb314LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lcdbr", OP16(0xb313LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ltdbr", OP16(0xb312LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lndbr", OP16(0xb311LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lpdbr", OP16(0xb310LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "msebr", OP16(0xb30fLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0},
+ { "maebr", OP16(0xb30eLL), MASK_RRF_F0FF, INSTR_RRF_F0FF, 3, 0},
+ { "debr", OP16(0xb30dLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "mdebr", OP16(0xb30cLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "sebr", OP16(0xb30bLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "aebr", OP16(0xb30aLL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "cebr", OP16(0xb309LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "kebr", OP16(0xb308LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "mxdbr", OP16(0xb307LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lxebr", OP16(0xb306LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lxdbr", OP16(0xb305LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ldebr", OP16(0xb304LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lcebr", OP16(0xb303LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "ltebr", OP16(0xb302LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lnebr", OP16(0xb301LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "lpebr", OP16(0xb300LL), MASK_RRE_FF, INSTR_RRE_FF, 3, 0},
+ { "trap4", OP16(0xb2ffLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "lfas", OP16(0xb2bdLL), MASK_S_RD, INSTR_S_RD, 2, 5},
+ { "srnmt", OP16(0xb2b9LL), MASK_S_RD, INSTR_S_RD, 2, 5},
+ { "lpswe", OP16(0xb2b2LL), MASK_S_RD, INSTR_S_RD, 2, 2},
+ { "stfl", OP16(0xb2b1LL), MASK_S_RD, INSTR_S_RD, 3, 2},
+ { "stfle", OP16(0xb2b0LL), MASK_S_RD, INSTR_S_RD, 2, 4},
+ { "cu12", OP16(0xb2a7LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cutfu", OP16(0xb2a7LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cutfu", OP16(0xb2a7LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "cu21", OP16(0xb2a6LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cuutf", OP16(0xb2a6LL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "cuutf", OP16(0xb2a6LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "tre", OP16(0xb2a5LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "lfpc", OP16(0xb29dLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stfpc", OP16(0xb29cLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "srnm", OP16(0xb299LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stsi", OP16(0xb27dLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stckf", OP16(0xb27cLL), MASK_S_RD, INSTR_S_RD, 2, 4},
+ { "sacf", OP16(0xb279LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stcke", OP16(0xb278LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "rp", OP16(0xb277LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "xsch", OP16(0xb276LL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "siga", OP16(0xb274LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "cmpsc", OP16(0xb263LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "cmpsc", OP16(0xb263LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "srst", OP16(0xb25eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "clst", OP16(0xb25dLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "bsa", OP16(0xb25aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "bsg", OP16(0xb258LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "cuse", OP16(0xb257LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "mvst", OP16(0xb255LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "mvpg", OP16(0xb254LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "msr", OP16(0xb252LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "csp", OP16(0xb250LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "ear", OP16(0xb24fLL), MASK_RRE_RA, INSTR_RRE_RA, 3, 0},
+ { "sar", OP16(0xb24eLL), MASK_RRE_AR, INSTR_RRE_AR, 3, 0},
+ { "cpya", OP16(0xb24dLL), MASK_RRE_AA, INSTR_RRE_AA, 3, 0},
+ { "tar", OP16(0xb24cLL), MASK_RRE_AR, INSTR_RRE_AR, 3, 0},
+ { "lura", OP16(0xb24bLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "esta", OP16(0xb24aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "ereg", OP16(0xb249LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "palb", OP16(0xb248LL), MASK_RRE_00, INSTR_RRE_00, 3, 0},
+ { "msta", OP16(0xb247LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "stura", OP16(0xb246LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "sqer", OP16(0xb245LL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0},
+ { "sqdr", OP16(0xb244LL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0},
+ { "cksm", OP16(0xb241LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "bakr", OP16(0xb240LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "schm", OP16(0xb23cLL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "rchp", OP16(0xb23bLL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "stcps", OP16(0xb23aLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stcrw", OP16(0xb239LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "rsch", OP16(0xb238LL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "sal", OP16(0xb237LL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "tpi", OP16(0xb236LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "tsch", OP16(0xb235LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stsch", OP16(0xb234LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "ssch", OP16(0xb233LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "msch", OP16(0xb232LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "hsch", OP16(0xb231LL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "csch", OP16(0xb230LL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "pgout", OP16(0xb22fLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "pgin", OP16(0xb22eLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "dxr", OP16(0xb22dLL), MASK_RRE_F0, INSTR_RRE_F0, 3, 0},
+ { "tb", OP16(0xb22cLL), MASK_RRE_0R, INSTR_RRE_0R, 3, 0},
+ { "sske", OP16(0xb22bLL), MASK_RRF_M0RR, INSTR_RRF_M0RR, 2, 4},
+ { "sske", OP16(0xb22bLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "rrbe", OP16(0xb22aLL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "iske", OP16(0xb229LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "pt", OP16(0xb228LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "esar", OP16(0xb227LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "epar", OP16(0xb226LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "ssar", OP16(0xb225LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "iac", OP16(0xb224LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "ivsk", OP16(0xb223LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "ipm", OP16(0xb222LL), MASK_RRE_R0, INSTR_RRE_R0, 3, 0},
+ { "ipte", OP16(0xb221LL), MASK_RRE_RR, INSTR_RRE_RR, 3, 0},
+ { "cfc", OP16(0xb21aLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "sac", OP16(0xb219LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "pc", OP16(0xb218LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "sie", OP16(0xb214LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stap", OP16(0xb212LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stpx", OP16(0xb211LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "spx", OP16(0xb210LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "ptlb", OP16(0xb20dLL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "ipk", OP16(0xb20bLL), MASK_S_00, INSTR_S_00, 3, 0},
+ { "spka", OP16(0xb20aLL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stpt", OP16(0xb209LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "spt", OP16(0xb208LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stckc", OP16(0xb207LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "sckc", OP16(0xb206LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stck", OP16(0xb205LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "sck", OP16(0xb204LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "stidp", OP16(0xb202LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "lra", OP8(0xb1LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "mc", OP8(0xafLL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "sigp", OP8(0xaeLL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "stosm", OP8(0xadLL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "stnsm", OP8(0xacLL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "clcle", OP8(0xa9LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "mvcle", OP8(0xa8LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "j", OP16(0xa7f4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jno", OP16(0xa7e4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnh", OP16(0xa7d4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnp", OP16(0xa7d4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jle", OP16(0xa7c4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnl", OP16(0xa7b4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnm", OP16(0xa7b4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jhe", OP16(0xa7a4LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnlh", OP16(0xa794LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "je", OP16(0xa784LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jz", OP16(0xa784LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jne", OP16(0xa774LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnz", OP16(0xa774LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jlh", OP16(0xa764LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnhe", OP16(0xa754LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jl", OP16(0xa744LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jm", OP16(0xa744LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jnle", OP16(0xa734LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jh", OP16(0xa724LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jp", OP16(0xa724LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "jo", OP16(0xa714LL), MASK_RI_0P, INSTR_RI_0P, 3, 0},
+ { "cghi", OP16(0xa70fLL), MASK_RI_RI, INSTR_RI_RI, 2, 2},
+ { "chi", OP16(0xa70eLL), MASK_RI_RI, INSTR_RI_RI, 3, 0},
+ { "mghi", OP16(0xa70dLL), MASK_RI_RI, INSTR_RI_RI, 2, 2},
+ { "mhi", OP16(0xa70cLL), MASK_RI_RI, INSTR_RI_RI, 3, 0},
+ { "aghi", OP16(0xa70bLL), MASK_RI_RI, INSTR_RI_RI, 2, 2},
+ { "ahi", OP16(0xa70aLL), MASK_RI_RI, INSTR_RI_RI, 3, 0},
+ { "lghi", OP16(0xa709LL), MASK_RI_RI, INSTR_RI_RI, 2, 2},
+ { "lhi", OP16(0xa708LL), MASK_RI_RI, INSTR_RI_RI, 3, 0},
+ { "brctg", OP16(0xa707LL), MASK_RI_RP, INSTR_RI_RP, 2, 2},
+ { "brct", OP16(0xa706LL), MASK_RI_RP, INSTR_RI_RP, 3, 0},
+ { "bras", OP16(0xa705LL), MASK_RI_RP, INSTR_RI_RP, 3, 0},
+ { "brc", OP16(0xa704LL), MASK_RI_UP, INSTR_RI_UP, 3, 0},
+ { "tmhl", OP16(0xa703LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "tmhh", OP16(0xa702LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "tml", OP16(0xa701LL), MASK_RI_RU, INSTR_RI_RU, 3, 0},
+ { "tmll", OP16(0xa701LL), MASK_RI_RU, INSTR_RI_RU, 3, 0},
+ { "tmh", OP16(0xa700LL), MASK_RI_RU, INSTR_RI_RU, 3, 0},
+ { "tmlh", OP16(0xa700LL), MASK_RI_RU, INSTR_RI_RU, 3, 0},
+ { "llill", OP16(0xa50fLL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "llilh", OP16(0xa50eLL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "llihl", OP16(0xa50dLL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "llihh", OP16(0xa50cLL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "oill", OP16(0xa50bLL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "oilh", OP16(0xa50aLL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "oihl", OP16(0xa509LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "oihh", OP16(0xa508LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "nill", OP16(0xa507LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "nilh", OP16(0xa506LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "nihl", OP16(0xa505LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "nihh", OP16(0xa504LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "iill", OP16(0xa503LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "iilh", OP16(0xa502LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "iihl", OP16(0xa501LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "iihh", OP16(0xa500LL), MASK_RI_RU, INSTR_RI_RU, 2, 2},
+ { "stam", OP8(0x9bLL), MASK_RS_AARD, INSTR_RS_AARD, 3, 0},
+ { "lam", OP8(0x9aLL), MASK_RS_AARD, INSTR_RS_AARD, 3, 0},
+ { "trace", OP8(0x99LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "lm", OP8(0x98LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "xi", OP8(0x97LL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "oi", OP8(0x96LL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "cli", OP8(0x95LL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "ni", OP8(0x94LL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "ts", OP8(0x93LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "mvi", OP8(0x92LL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "tm", OP8(0x91LL), MASK_SI_URD, INSTR_SI_URD, 3, 0},
+ { "stm", OP8(0x90LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "slda", OP8(0x8fLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "srda", OP8(0x8eLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "sldl", OP8(0x8dLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "srdl", OP8(0x8cLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "sla", OP8(0x8bLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "sra", OP8(0x8aLL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "sll", OP8(0x89LL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "srl", OP8(0x88LL), MASK_RS_R0RD, INSTR_RS_R0RD, 3, 0},
+ { "bxle", OP8(0x87LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "bxh", OP8(0x86LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "brxle", OP8(0x85LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0},
+ { "brxh", OP8(0x84LL), MASK_RSI_RRP, INSTR_RSI_RRP, 3, 0},
+ { "diag", OP8(0x83LL), MASK_RS_RRRD, INSTR_RS_RRRD, 3, 0},
+ { "lpsw", OP8(0x82LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "ssm", OP8(0x80LL), MASK_S_RD, INSTR_S_RD, 3, 0},
+ { "su", OP8(0x7fLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "au", OP8(0x7eLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "de", OP8(0x7dLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "me", OP8(0x7cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "mde", OP8(0x7cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "se", OP8(0x7bLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "ae", OP8(0x7aLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "ce", OP8(0x79LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "le", OP8(0x78LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "ms", OP8(0x71LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "ste", OP8(0x70LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "sw", OP8(0x6fLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "aw", OP8(0x6eLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "dd", OP8(0x6dLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "md", OP8(0x6cLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "sd", OP8(0x6bLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "ad", OP8(0x6aLL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "cd", OP8(0x69LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "ld", OP8(0x68LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "mxd", OP8(0x67LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "std", OP8(0x60LL), MASK_RX_FRRD, INSTR_RX_FRRD, 3, 0},
+ { "sl", OP8(0x5fLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "al", OP8(0x5eLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "d", OP8(0x5dLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "m", OP8(0x5cLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "s", OP8(0x5bLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "a", OP8(0x5aLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "c", OP8(0x59LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "l", OP8(0x58LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "x", OP8(0x57LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "o", OP8(0x56LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "cl", OP8(0x55LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "n", OP8(0x54LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "lae", OP8(0x51LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "st", OP8(0x50LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "cvb", OP8(0x4fLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "cvd", OP8(0x4eLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "bas", OP8(0x4dLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "mh", OP8(0x4cLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "sh", OP8(0x4bLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "ah", OP8(0x4aLL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "ch", OP8(0x49LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "lh", OP8(0x48LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "b", OP16(0x47f0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bno", OP16(0x47e0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnh", OP16(0x47d0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnp", OP16(0x47d0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "ble", OP16(0x47c0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnl", OP16(0x47b0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnm", OP16(0x47b0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bhe", OP16(0x47a0LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnlh", OP16(0x4790LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "be", OP16(0x4780LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bz", OP16(0x4780LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bne", OP16(0x4770LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnz", OP16(0x4770LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "blh", OP16(0x4760LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnhe", OP16(0x4750LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bl", OP16(0x4740LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bm", OP16(0x4740LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bnle", OP16(0x4730LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bh", OP16(0x4720LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bp", OP16(0x4720LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bo", OP16(0x4710LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bc", OP8(0x47LL), MASK_RX_URRD, INSTR_RX_URRD, 3, 0},
+ { "nop", OP16(0x4700LL), MASK_RX_0RRD, INSTR_RX_0RRD, 3, 0},
+ { "bct", OP8(0x46LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "bal", OP8(0x45LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "ex", OP8(0x44LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "ic", OP8(0x43LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "stc", OP8(0x42LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "la", OP8(0x41LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "sth", OP8(0x40LL), MASK_RX_RRRD, INSTR_RX_RRRD, 3, 0},
+ { "sur", OP8(0x3fLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "aur", OP8(0x3eLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "der", OP8(0x3dLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "mer", OP8(0x3cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "mder", OP8(0x3cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ser", OP8(0x3bLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "aer", OP8(0x3aLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "cer", OP8(0x39LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ler", OP8(0x38LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "sxr", OP8(0x37LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "axr", OP8(0x36LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lrer", OP8(0x35LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ledr", OP8(0x35LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "her", OP8(0x34LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lcer", OP8(0x33LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lter", OP8(0x32LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lner", OP8(0x31LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lper", OP8(0x30LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "swr", OP8(0x2fLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "awr", OP8(0x2eLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ddr", OP8(0x2dLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "mdr", OP8(0x2cLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "sdr", OP8(0x2bLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "adr", OP8(0x2aLL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "cdr", OP8(0x29LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ldr", OP8(0x28LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "mxdr", OP8(0x27LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "mxr", OP8(0x26LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lrdr", OP8(0x25LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ldxr", OP8(0x25LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "hdr", OP8(0x24LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lcdr", OP8(0x23LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "ltdr", OP8(0x22LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lndr", OP8(0x21LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "lpdr", OP8(0x20LL), MASK_RR_FF, INSTR_RR_FF, 3, 0},
+ { "slr", OP8(0x1fLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "alr", OP8(0x1eLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "dr", OP8(0x1dLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "mr", OP8(0x1cLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "sr", OP8(0x1bLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "ar", OP8(0x1aLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "cr", OP8(0x19LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "lr", OP8(0x18LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "xr", OP8(0x17LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "or", OP8(0x16LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "clr", OP8(0x15LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "nr", OP8(0x14LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "lcr", OP8(0x13LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "ltr", OP8(0x12LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "lnr", OP8(0x11LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "lpr", OP8(0x10LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "clcl", OP8(0x0fLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "mvcl", OP8(0x0eLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "basr", OP8(0x0dLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "bassm", OP8(0x0cLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "bsm", OP8(0x0bLL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "svc", OP8(0x0aLL), MASK_RR_U0, INSTR_RR_U0, 3, 0},
+ { "br", OP16(0x07f0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnor", OP16(0x07e0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnhr", OP16(0x07d0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnpr", OP16(0x07d0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bler", OP16(0x07c0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnlr", OP16(0x07b0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnmr", OP16(0x07b0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bher", OP16(0x07a0LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnlhr", OP16(0x0790LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "ber", OP16(0x0780LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bzr", OP16(0x0780LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bner", OP16(0x0770LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnzr", OP16(0x0770LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "blhr", OP16(0x0760LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnher", OP16(0x0750LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "blr", OP16(0x0740LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bmr", OP16(0x0740LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bnler", OP16(0x0730LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bhr", OP16(0x0720LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bpr", OP16(0x0720LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bor", OP16(0x0710LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bcr", OP8(0x07LL), MASK_RR_UR, INSTR_RR_UR, 3, 0},
+ { "nopr", OP16(0x0700LL), MASK_RR_0R, INSTR_RR_0R, 3, 0},
+ { "bctr", OP8(0x06LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "balr", OP8(0x05LL), MASK_RR_RR, INSTR_RR_RR, 3, 0},
+ { "spm", OP8(0x04LL), MASK_RR_R0, INSTR_RR_R0, 3, 0},
+ { "trap2", OP16(0x01ffLL), MASK_E, INSTR_E, 3, 0},
+ { "sam64", OP16(0x010eLL), MASK_E, INSTR_E, 2, 2},
+ { "sam31", OP16(0x010dLL), MASK_E, INSTR_E, 3, 2},
+ { "sam24", OP16(0x010cLL), MASK_E, INSTR_E, 3, 2},
+ { "tam", OP16(0x010bLL), MASK_E, INSTR_E, 3, 2},
+ { "pfpo", OP16(0x010aLL), MASK_E, INSTR_E, 2, 5},
+ { "sckpf", OP16(0x0107LL), MASK_E, INSTR_E, 3, 0},
+ { "upt", OP16(0x0102LL), MASK_E, INSTR_E, 3, 0},
+ { "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0},
+
+/* QEMU-ADD: */
+ { "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
+ { "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
+ { "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
+ { "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6},
+
+ { "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
+ { "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
+ { "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
+ { "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6},
+
+ { "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
+ { "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
+ { "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6},
+/* QEMU-END */
+};
+
+static const int s390_num_opcodes =
+ sizeof (s390_opcodes) / sizeof (s390_opcodes[0]);
diff --git a/s390.ld b/s390.ld
new file mode 100644
index 0000000..a9c5370
--- /dev/null
+++ b/s390.ld
@@ -0,0 +1,201 @@
+OUTPUT_FORMAT("elf32-s390", "elf32-s390",
+ "elf32-s390")
+OUTPUT_ARCH(s390:31-bit)
+ENTRY(_start)
+/* __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*)
+ *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*)
+ *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*)
+ *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*)
+ *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+ *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+ *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x07070707
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x07070707
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x07070707
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+ .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x1000) + (. & (0x1000 - 1));
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .got : { *(.got.plt) *(.got) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata :
+ {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ PROVIDE (__sbss_end = .);
+ PROVIDE (___sbss_end = .);
+ }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(32 / 8);
+ }
+ . = ALIGN(32 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}
diff --git a/savevm.c b/savevm.c
new file mode 100644
index 0000000..d4c25c7
--- /dev/null
+++ b/savevm.c
@@ -0,0 +1,2172 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <zlib.h>
+
+/* Needed early for CONFIG_BSD etc. */
+#include "config-host.h"
+
+#ifndef _WIN32
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <sys/select.h>
+#ifdef CONFIG_BSD
+#include <sys/stat.h>
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#include <libutil.h>
+#else
+#include <util.h>
+#endif
+#ifdef __linux__
+#include <pty.h>
+#include <malloc.h>
+#include <linux/rtc.h>
+#endif
+#endif
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <malloc.h>
+#include <sys/timeb.h>
+#include <mmsystem.h>
+#define getopt_long_only getopt_long
+#define memalign(align, size) malloc(size)
+#endif
+
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "hw/qdev.h"
+#include "net.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "audio/audio.h"
+#include "migration.h"
+#include "qemu_socket.h"
+#include "qemu-queue.h"
+
+#define SELF_ANNOUNCE_ROUNDS 5
+
+#ifndef ETH_P_RARP
+#define ETH_P_RARP 0x8035
+#endif
+#define ARP_HTYPE_ETH 0x0001
+#define ARP_PTYPE_IP 0x0800
+#define ARP_OP_REQUEST_REV 0x3
+
+static int announce_self_create(uint8_t *buf,
+ uint8_t *mac_addr)
+{
+ /* Ethernet header. */
+ memset(buf, 0xff, 6); /* destination MAC addr */
+ memcpy(buf + 6, mac_addr, 6); /* source MAC addr */
+ *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */
+
+ /* RARP header. */
+ *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */
+ *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */
+ *(buf + 18) = 6; /* hardware addr length (ethernet) */
+ *(buf + 19) = 4; /* protocol addr length (IPv4) */
+ *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */
+ memcpy(buf + 22, mac_addr, 6); /* source hw addr */
+ memset(buf + 28, 0x00, 4); /* source protocol addr */
+ memcpy(buf + 32, mac_addr, 6); /* target hw addr */
+ memset(buf + 38, 0x00, 4); /* target protocol addr */
+
+ /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */
+ memset(buf + 42, 0x00, 18);
+
+ return 60; /* len (FCS will be added by hardware) */
+}
+
+static void qemu_announce_self_iter(NICState *nic, void *opaque)
+{
+ uint8_t buf[60];
+ int len;
+
+ len = announce_self_create(buf, nic->conf->macaddr.a);
+
+ qemu_send_packet_raw(&nic->nc, buf, len);
+}
+
+
+static void qemu_announce_self_once(void *opaque)
+{
+ static int count = SELF_ANNOUNCE_ROUNDS;
+ QEMUTimer *timer = *(QEMUTimer **)opaque;
+
+ qemu_foreach_nic(qemu_announce_self_iter, NULL);
+
+ if (--count) {
+ /* delay 50ms, 150ms, 250ms, ... */
+ qemu_mod_timer(timer, qemu_get_clock(rt_clock) +
+ 50 + (SELF_ANNOUNCE_ROUNDS - count - 1) * 100);
+ } else {
+ qemu_del_timer(timer);
+ qemu_free_timer(timer);
+ }
+}
+
+void qemu_announce_self(void)
+{
+ static QEMUTimer *timer;
+ timer = qemu_new_timer(rt_clock, qemu_announce_self_once, &timer);
+ qemu_announce_self_once(&timer);
+}
+
+/***********************************************************/
+/* savevm/loadvm support */
+
+#define IO_BUF_SIZE 32768
+
+struct QEMUFile {
+ QEMUFilePutBufferFunc *put_buffer;
+ QEMUFileGetBufferFunc *get_buffer;
+ QEMUFileCloseFunc *close;
+ QEMUFileRateLimit *rate_limit;
+ QEMUFileSetRateLimit *set_rate_limit;
+ QEMUFileGetRateLimit *get_rate_limit;
+ void *opaque;
+ int is_write;
+
+ int64_t buf_offset; /* start of buffer when writing, end of buffer
+ when reading */
+ int buf_index;
+ int buf_size; /* 0 when writing */
+ uint8_t buf[IO_BUF_SIZE];
+
+ int has_error;
+};
+
+typedef struct QEMUFileStdio
+{
+ FILE *stdio_file;
+ QEMUFile *file;
+} QEMUFileStdio;
+
+typedef struct QEMUFileSocket
+{
+ int fd;
+ QEMUFile *file;
+} QEMUFileSocket;
+
+static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ do {
+ len = recv(s->fd, (void *)buf, size, 0);
+ } while (len == -1 && socket_error() == EINTR);
+
+ if (len == -1)
+ len = -socket_error();
+
+ return len;
+}
+
+static int socket_close(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+ qemu_free(s);
+ return 0;
+}
+
+static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ FILE *fp = s->stdio_file;
+ int bytes;
+
+ do {
+ clearerr(fp);
+ bytes = fread(buf, 1, size, fp);
+ } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
+ return bytes;
+}
+
+static int stdio_pclose(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+ int ret;
+ ret = pclose(s->stdio_file);
+ qemu_free(s);
+ return ret;
+}
+
+static int stdio_fclose(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+ fclose(s->stdio_file);
+ qemu_free(s);
+ return 0;
+}
+
+QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
+{
+ QEMUFileStdio *s;
+
+ if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+ fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = qemu_mallocz(sizeof(QEMUFileStdio));
+
+ s->stdio_file = stdio_file;
+
+ if(mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_pclose,
+ NULL, NULL, NULL);
+ } else {
+ s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_pclose,
+ NULL, NULL, NULL);
+ }
+ return s->file;
+}
+
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+{
+ FILE *popen_file;
+
+ popen_file = popen(command, mode);
+ if(popen_file == NULL) {
+ return NULL;
+ }
+
+ return qemu_popen(popen_file, mode);
+}
+
+int qemu_stdio_fd(QEMUFile *f)
+{
+ QEMUFileStdio *p;
+ int fd;
+
+ p = (QEMUFileStdio *)f->opaque;
+ fd = fileno(p->stdio_file);
+
+ return fd;
+}
+
+QEMUFile *qemu_fdopen(int fd, const char *mode)
+{
+ QEMUFileStdio *s;
+
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = qemu_mallocz(sizeof(QEMUFileStdio));
+ s->stdio_file = fdopen(fd, mode);
+ if (!s->stdio_file)
+ goto fail;
+
+ if(mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, NULL, stdio_get_buffer, stdio_fclose,
+ NULL, NULL, NULL);
+ } else {
+ s->file = qemu_fopen_ops(s, stdio_put_buffer, NULL, stdio_fclose,
+ NULL, NULL, NULL);
+ }
+ return s->file;
+
+fail:
+ qemu_free(s);
+ return NULL;
+}
+
+QEMUFile *qemu_fopen_socket(int fd)
+{
+ QEMUFileSocket *s = qemu_mallocz(sizeof(QEMUFileSocket));
+
+ s->fd = fd;
+ s->file = qemu_fopen_ops(s, NULL, socket_get_buffer, socket_close,
+ NULL, NULL, NULL);
+ return s->file;
+}
+
+static int file_put_buffer(void *opaque, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ fseek(s->stdio_file, pos, SEEK_SET);
+ return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ fseek(s->stdio_file, pos, SEEK_SET);
+ return fread(buf, 1, size, s->stdio_file);
+}
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+ QEMUFileStdio *s;
+
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = qemu_mallocz(sizeof(QEMUFileStdio));
+
+ s->stdio_file = fopen(filename, mode);
+ if (!s->stdio_file)
+ goto fail;
+
+ if(mode[0] == 'w') {
+ s->file = qemu_fopen_ops(s, file_put_buffer, NULL, stdio_fclose,
+ NULL, NULL, NULL);
+ } else {
+ s->file = qemu_fopen_ops(s, NULL, file_get_buffer, stdio_fclose,
+ NULL, NULL, NULL);
+ }
+ return s->file;
+fail:
+ qemu_free(s);
+ return NULL;
+}
+
+static int block_put_buffer(void *opaque, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ bdrv_save_vmstate(opaque, buf, pos, size);
+ return size;
+}
+
+static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ return bdrv_load_vmstate(opaque, buf, pos, size);
+}
+
+static int bdrv_fclose(void *opaque)
+{
+ return 0;
+}
+
+static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
+{
+ if (is_writable)
+ return qemu_fopen_ops(bs, block_put_buffer, NULL, bdrv_fclose,
+ NULL, NULL, NULL);
+ return qemu_fopen_ops(bs, NULL, block_get_buffer, bdrv_fclose, NULL, NULL, NULL);
+}
+
+QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
+ QEMUFileGetBufferFunc *get_buffer,
+ QEMUFileCloseFunc *close,
+ QEMUFileRateLimit *rate_limit,
+ QEMUFileSetRateLimit *set_rate_limit,
+ QEMUFileGetRateLimit *get_rate_limit)
+{
+ QEMUFile *f;
+
+ f = qemu_mallocz(sizeof(QEMUFile));
+
+ f->opaque = opaque;
+ f->put_buffer = put_buffer;
+ f->get_buffer = get_buffer;
+ f->close = close;
+ f->rate_limit = rate_limit;
+ f->set_rate_limit = set_rate_limit;
+ f->get_rate_limit = get_rate_limit;
+ f->is_write = 0;
+
+ return f;
+}
+
+int qemu_file_has_error(QEMUFile *f)
+{
+ return f->has_error;
+}
+
+void qemu_file_set_error(QEMUFile *f)
+{
+ f->has_error = 1;
+}
+
+void qemu_fflush(QEMUFile *f)
+{
+ if (!f->put_buffer)
+ return;
+
+ if (f->is_write && f->buf_index > 0) {
+ int len;
+
+ len = f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
+ if (len > 0)
+ f->buf_offset += f->buf_index;
+ else
+ f->has_error = 1;
+ f->buf_index = 0;
+ }
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+ int len;
+
+ if (!f->get_buffer)
+ return;
+
+ if (f->is_write)
+ abort();
+
+ len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+ if (len > 0) {
+ f->buf_index = 0;
+ f->buf_size = len;
+ f->buf_offset += len;
+ } else if (len != -EAGAIN)
+ f->has_error = 1;
+}
+
+int qemu_fclose(QEMUFile *f)
+{
+ int ret = 0;
+ qemu_fflush(f);
+ if (f->close)
+ ret = f->close(f->opaque);
+ qemu_free(f);
+ return ret;
+}
+
+void qemu_file_put_notify(QEMUFile *f)
+{
+ f->put_buffer(f->opaque, NULL, 0, 0);
+}
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+ int l;
+
+ if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ fprintf(stderr,
+ "Attempted to write to buffer while read buffer is not empty\n");
+ abort();
+ }
+
+ while (!f->has_error && size > 0) {
+ l = IO_BUF_SIZE - f->buf_index;
+ if (l > size)
+ l = size;
+ memcpy(f->buf + f->buf_index, buf, l);
+ f->is_write = 1;
+ f->buf_index += l;
+ buf += l;
+ size -= l;
+ if (f->buf_index >= IO_BUF_SIZE)
+ qemu_fflush(f);
+ }
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+ if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ fprintf(stderr,
+ "Attempted to write to buffer while read buffer is not empty\n");
+ abort();
+ }
+
+ f->buf[f->buf_index++] = v;
+ f->is_write = 1;
+ if (f->buf_index >= IO_BUF_SIZE)
+ qemu_fflush(f);
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+{
+ int size, l;
+
+ if (f->is_write)
+ abort();
+
+ size = size1;
+ while (size > 0) {
+ l = f->buf_size - f->buf_index;
+ if (l == 0) {
+ qemu_fill_buffer(f);
+ l = f->buf_size - f->buf_index;
+ if (l == 0)
+ break;
+ }
+ if (l > size)
+ l = size;
+ memcpy(buf, f->buf + f->buf_index, l);
+ f->buf_index += l;
+ buf += l;
+ size -= l;
+ }
+ return size1 - size;
+}
+
+static int qemu_peek_byte(QEMUFile *f)
+{
+ if (f->is_write)
+ abort();
+
+ if (f->buf_index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ if (f->buf_index >= f->buf_size)
+ return 0;
+ }
+ return f->buf[f->buf_index];
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+ if (f->is_write)
+ abort();
+
+ if (f->buf_index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ if (f->buf_index >= f->buf_size)
+ return 0;
+ }
+ return f->buf[f->buf_index++];
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+ return f->buf_offset - f->buf_size + f->buf_index;
+}
+
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
+{
+ if (whence == SEEK_SET) {
+ /* nothing to do */
+ } else if (whence == SEEK_CUR) {
+ pos += qemu_ftell(f);
+ } else {
+ /* SEEK_END not supported */
+ return -1;
+ }
+ if (f->put_buffer) {
+ qemu_fflush(f);
+ f->buf_offset = pos;
+ } else {
+ f->buf_offset = pos;
+ f->buf_index = 0;
+ f->buf_size = 0;
+ }
+ return pos;
+}
+
+int qemu_file_rate_limit(QEMUFile *f)
+{
+ if (f->rate_limit)
+ return f->rate_limit(f->opaque);
+
+ return 0;
+}
+
+int64_t qemu_file_get_rate_limit(QEMUFile *f)
+{
+ if (f->get_rate_limit)
+ return f->get_rate_limit(f->opaque);
+
+ return 0;
+}
+
+int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
+{
+ /* any failed or completed migration keeps its state to allow probing of
+ * migration data, but has no associated file anymore */
+ if (f && f->set_rate_limit)
+ return f->set_rate_limit(f->opaque, new_rate);
+
+ return 0;
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 24);
+ qemu_put_byte(f, v >> 16);
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+ qemu_put_be32(f, v >> 32);
+ qemu_put_be32(f, v);
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 24;
+ v |= qemu_get_byte(f) << 16;
+ v |= qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+ uint64_t v;
+ v = (uint64_t)qemu_get_be32(f) << 32;
+ v |= qemu_get_be32(f);
+ return v;
+}
+
+/* bool */
+
+static int get_bool(QEMUFile *f, void *pv, size_t size)
+{
+ bool *v = pv;
+ *v = qemu_get_byte(f);
+ return 0;
+}
+
+static void put_bool(QEMUFile *f, void *pv, size_t size)
+{
+ bool *v = pv;
+ qemu_put_byte(f, *v);
+}
+
+const VMStateInfo vmstate_info_bool = {
+ .name = "bool",
+ .get = get_bool,
+ .put = put_bool,
+};
+
+/* 8 bit int */
+
+static int get_int8(QEMUFile *f, void *pv, size_t size)
+{
+ int8_t *v = pv;
+ qemu_get_s8s(f, v);
+ return 0;
+}
+
+static void put_int8(QEMUFile *f, void *pv, size_t size)
+{
+ int8_t *v = pv;
+ qemu_put_s8s(f, v);
+}
+
+const VMStateInfo vmstate_info_int8 = {
+ .name = "int8",
+ .get = get_int8,
+ .put = put_int8,
+};
+
+/* 16 bit int */
+
+static int get_int16(QEMUFile *f, void *pv, size_t size)
+{
+ int16_t *v = pv;
+ qemu_get_sbe16s(f, v);
+ return 0;
+}
+
+static void put_int16(QEMUFile *f, void *pv, size_t size)
+{
+ int16_t *v = pv;
+ qemu_put_sbe16s(f, v);
+}
+
+const VMStateInfo vmstate_info_int16 = {
+ .name = "int16",
+ .get = get_int16,
+ .put = put_int16,
+};
+
+/* 32 bit int */
+
+static int get_int32(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *v = pv;
+ qemu_get_sbe32s(f, v);
+ return 0;
+}
+
+static void put_int32(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *v = pv;
+ qemu_put_sbe32s(f, v);
+}
+
+const VMStateInfo vmstate_info_int32 = {
+ .name = "int32",
+ .get = get_int32,
+ .put = put_int32,
+};
+
+/* 32 bit int. See that the received value is the same than the one
+ in the field */
+
+static int get_int32_equal(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *v = pv;
+ int32_t v2;
+ qemu_get_sbe32s(f, &v2);
+
+ if (*v == v2)
+ return 0;
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_int32_equal = {
+ .name = "int32 equal",
+ .get = get_int32_equal,
+ .put = put_int32,
+};
+
+/* 32 bit int. See that the received value is the less or the same
+ than the one in the field */
+
+static int get_int32_le(QEMUFile *f, void *pv, size_t size)
+{
+ int32_t *old = pv;
+ int32_t new;
+ qemu_get_sbe32s(f, &new);
+
+ if (*old <= new)
+ return 0;
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_int32_le = {
+ .name = "int32 equal",
+ .get = get_int32_le,
+ .put = put_int32,
+};
+
+/* 64 bit int */
+
+static int get_int64(QEMUFile *f, void *pv, size_t size)
+{
+ int64_t *v = pv;
+ qemu_get_sbe64s(f, v);
+ return 0;
+}
+
+static void put_int64(QEMUFile *f, void *pv, size_t size)
+{
+ int64_t *v = pv;
+ qemu_put_sbe64s(f, v);
+}
+
+const VMStateInfo vmstate_info_int64 = {
+ .name = "int64",
+ .get = get_int64,
+ .put = put_int64,
+};
+
+/* 8 bit unsigned int */
+
+static int get_uint8(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_get_8s(f, v);
+ return 0;
+}
+
+static void put_uint8(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_put_8s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint8 = {
+ .name = "uint8",
+ .get = get_uint8,
+ .put = put_uint8,
+};
+
+/* 16 bit unsigned int */
+
+static int get_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ qemu_get_be16s(f, v);
+ return 0;
+}
+
+static void put_uint16(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ qemu_put_be16s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint16 = {
+ .name = "uint16",
+ .get = get_uint16,
+ .put = put_uint16,
+};
+
+/* 32 bit unsigned int */
+
+static int get_uint32(QEMUFile *f, void *pv, size_t size)
+{
+ uint32_t *v = pv;
+ qemu_get_be32s(f, v);
+ return 0;
+}
+
+static void put_uint32(QEMUFile *f, void *pv, size_t size)
+{
+ uint32_t *v = pv;
+ qemu_put_be32s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint32 = {
+ .name = "uint32",
+ .get = get_uint32,
+ .put = put_uint32,
+};
+
+/* 64 bit unsigned int */
+
+static int get_uint64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_get_be64s(f, v);
+ return 0;
+}
+
+static void put_uint64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_put_be64s(f, v);
+}
+
+const VMStateInfo vmstate_info_uint64 = {
+ .name = "uint64",
+ .get = get_uint64,
+ .put = put_uint64,
+};
+
+/* 64 bit linux kernel unsigned int */
+
+#ifdef __linux__
+static int get_u64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_get_be64s(f, v);
+ return 0;
+}
+
+static void put_u64(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_put_be64s(f, v);
+}
+
+const VMStateInfo vmstate_info_u64 = {
+ .name = "__u64",
+ .get = get_u64,
+ .put = put_u64,
+};
+#endif /* __linux__ */
+
+/* 8 bit int. See that the received value is the same than the one
+ in the field */
+
+static int get_uint8_equal(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ uint8_t v2;
+ qemu_get_8s(f, &v2);
+
+ if (*v == v2)
+ return 0;
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_uint8_equal = {
+ .name = "uint8 equal",
+ .get = get_uint8_equal,
+ .put = put_uint8,
+};
+
+/* 16 bit unsigned int int. See that the received value is the same than the one
+ in the field */
+
+static int get_uint16_equal(QEMUFile *f, void *pv, size_t size)
+{
+ uint16_t *v = pv;
+ uint16_t v2;
+ qemu_get_be16s(f, &v2);
+
+ if (*v == v2)
+ return 0;
+ return -EINVAL;
+}
+
+const VMStateInfo vmstate_info_uint16_equal = {
+ .name = "uint16 equal",
+ .get = get_uint16_equal,
+ .put = put_uint16,
+};
+
+/* timers */
+
+static int get_timer(QEMUFile *f, void *pv, size_t size)
+{
+ QEMUTimer *v = pv;
+ qemu_get_timer(f, v);
+ return 0;
+}
+
+static void put_timer(QEMUFile *f, void *pv, size_t size)
+{
+ QEMUTimer *v = pv;
+ qemu_put_timer(f, v);
+}
+
+const VMStateInfo vmstate_info_timer = {
+ .name = "timer",
+ .get = get_timer,
+ .put = put_timer,
+};
+
+/* uint8_t buffers */
+
+static int get_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_get_buffer(f, v, size);
+ return 0;
+}
+
+static void put_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t *v = pv;
+ qemu_put_buffer(f, v, size);
+}
+
+const VMStateInfo vmstate_info_buffer = {
+ .name = "buffer",
+ .get = get_buffer,
+ .put = put_buffer,
+};
+
+/* unused buffers: space that was used for some fields that are
+ not usefull anymore */
+
+static int get_unused_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ uint8_t buf[1024];
+ int block_len;
+
+ while (size > 0) {
+ block_len = MIN(sizeof(buf), size);
+ size -= block_len;
+ qemu_get_buffer(f, buf, block_len);
+ }
+ return 0;
+}
+
+static void put_unused_buffer(QEMUFile *f, void *pv, size_t size)
+{
+ static const uint8_t buf[1024];
+ int block_len;
+
+ while (size > 0) {
+ block_len = MIN(sizeof(buf), size);
+ size -= block_len;
+ qemu_put_buffer(f, buf, block_len);
+ }
+}
+
+const VMStateInfo vmstate_info_unused_buffer = {
+ .name = "unused_buffer",
+ .get = get_unused_buffer,
+ .put = put_unused_buffer,
+};
+
+typedef struct CompatEntry {
+ char idstr[256];
+ int instance_id;
+} CompatEntry;
+
+typedef struct SaveStateEntry {
+ QTAILQ_ENTRY(SaveStateEntry) entry;
+ char idstr[256];
+ int instance_id;
+ int alias_id;
+ int version_id;
+ int section_id;
+ SaveSetParamsHandler *set_params;
+ SaveLiveStateHandler *save_live_state;
+ SaveStateHandler *save_state;
+ LoadStateHandler *load_state;
+ const VMStateDescription *vmsd;
+ void *opaque;
+ CompatEntry *compat;
+ int no_migrate;
+} SaveStateEntry;
+
+
+static QTAILQ_HEAD(savevm_handlers, SaveStateEntry) savevm_handlers =
+ QTAILQ_HEAD_INITIALIZER(savevm_handlers);
+static int global_section_id;
+
+static int calculate_new_instance_id(const char *idstr)
+{
+ SaveStateEntry *se;
+ int instance_id = 0;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (strcmp(idstr, se->idstr) == 0
+ && instance_id <= se->instance_id) {
+ instance_id = se->instance_id + 1;
+ }
+ }
+ return instance_id;
+}
+
+static int calculate_compat_instance_id(const char *idstr)
+{
+ SaveStateEntry *se;
+ int instance_id = 0;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (!se->compat)
+ continue;
+
+ if (strcmp(idstr, se->compat->idstr) == 0
+ && instance_id <= se->compat->instance_id) {
+ instance_id = se->compat->instance_id + 1;
+ }
+ }
+ return instance_id;
+}
+
+/* TODO: Individual devices generally have very little idea about the rest
+ of the system, so instance_id should be removed/replaced.
+ Meanwhile pass -1 as instance_id if you do not already have a clearly
+ distinguishing id for all instances of your device class. */
+int register_savevm_live(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveSetParamsHandler *set_params,
+ SaveLiveStateHandler *save_live_state,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque)
+{
+ SaveStateEntry *se;
+
+ se = qemu_mallocz(sizeof(SaveStateEntry));
+ se->version_id = version_id;
+ se->section_id = global_section_id++;
+ se->set_params = set_params;
+ se->save_live_state = save_live_state;
+ se->save_state = save_state;
+ se->load_state = load_state;
+ se->opaque = opaque;
+ se->vmsd = NULL;
+ se->no_migrate = 0;
+
+ if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
+ char *id = dev->parent_bus->info->get_dev_path(dev);
+ if (id) {
+ pstrcpy(se->idstr, sizeof(se->idstr), id);
+ pstrcat(se->idstr, sizeof(se->idstr), "/");
+ qemu_free(id);
+
+ se->compat = qemu_mallocz(sizeof(CompatEntry));
+ pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr);
+ se->compat->instance_id = instance_id == -1 ?
+ calculate_compat_instance_id(idstr) : instance_id;
+ instance_id = -1;
+ }
+ }
+ pstrcat(se->idstr, sizeof(se->idstr), idstr);
+
+ if (instance_id == -1) {
+ se->instance_id = calculate_new_instance_id(se->idstr);
+ } else {
+ se->instance_id = instance_id;
+ }
+ assert(!se->compat || se->instance_id == 0);
+ /* add at the end of list */
+ QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry);
+ return 0;
+}
+
+int register_savevm(DeviceState *dev,
+ const char *idstr,
+ int instance_id,
+ int version_id,
+ SaveStateHandler *save_state,
+ LoadStateHandler *load_state,
+ void *opaque)
+{
+ return register_savevm_live(dev, idstr, instance_id, version_id,
+ NULL, NULL, save_state, load_state, opaque);
+}
+
+void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque)
+{
+ SaveStateEntry *se, *new_se;
+ char id[256] = "";
+
+ if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
+ char *path = dev->parent_bus->info->get_dev_path(dev);
+ if (path) {
+ pstrcpy(id, sizeof(id), path);
+ pstrcat(id, sizeof(id), "/");
+ qemu_free(path);
+ }
+ }
+ pstrcat(id, sizeof(id), idstr);
+
+ QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) {
+ if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
+ QTAILQ_REMOVE(&savevm_handlers, se, entry);
+ if (se->compat) {
+ qemu_free(se->compat);
+ }
+ qemu_free(se);
+ }
+ }
+}
+
+/* mark a device as not to be migrated, that is the device should be
+ unplugged before migration */
+void register_device_unmigratable(DeviceState *dev, const char *idstr,
+ void *opaque)
+{
+ SaveStateEntry *se;
+ char id[256] = "";
+
+ if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
+ char *path = dev->parent_bus->info->get_dev_path(dev);
+ if (path) {
+ pstrcpy(id, sizeof(id), path);
+ pstrcat(id, sizeof(id), "/");
+ qemu_free(path);
+ }
+ }
+ pstrcat(id, sizeof(id), idstr);
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) {
+ se->no_migrate = 1;
+ }
+ }
+}
+
+int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
+ const VMStateDescription *vmsd,
+ void *opaque, int alias_id,
+ int required_for_version)
+{
+ SaveStateEntry *se;
+
+ /* If this triggers, alias support can be dropped for the vmsd. */
+ assert(alias_id == -1 || required_for_version >= vmsd->minimum_version_id);
+
+ se = qemu_mallocz(sizeof(SaveStateEntry));
+ se->version_id = vmsd->version_id;
+ se->section_id = global_section_id++;
+ se->save_live_state = NULL;
+ se->save_state = NULL;
+ se->load_state = NULL;
+ se->opaque = opaque;
+ se->vmsd = vmsd;
+ se->alias_id = alias_id;
+
+ if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) {
+ char *id = dev->parent_bus->info->get_dev_path(dev);
+ if (id) {
+ pstrcpy(se->idstr, sizeof(se->idstr), id);
+ pstrcat(se->idstr, sizeof(se->idstr), "/");
+ qemu_free(id);
+
+ se->compat = qemu_mallocz(sizeof(CompatEntry));
+ pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name);
+ se->compat->instance_id = instance_id == -1 ?
+ calculate_compat_instance_id(vmsd->name) : instance_id;
+ instance_id = -1;
+ }
+ }
+ pstrcat(se->idstr, sizeof(se->idstr), vmsd->name);
+
+ if (instance_id == -1) {
+ se->instance_id = calculate_new_instance_id(se->idstr);
+ } else {
+ se->instance_id = instance_id;
+ }
+ assert(!se->compat || se->instance_id == 0);
+ /* add at the end of list */
+ QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry);
+ return 0;
+}
+
+int vmstate_register(DeviceState *dev, int instance_id,
+ const VMStateDescription *vmsd, void *opaque)
+{
+ return vmstate_register_with_alias_id(dev, instance_id, vmsd,
+ opaque, -1, 0);
+}
+
+void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ SaveStateEntry *se, *new_se;
+
+ QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) {
+ if (se->vmsd == vmsd && se->opaque == opaque) {
+ QTAILQ_REMOVE(&savevm_handlers, se, entry);
+ if (se->compat) {
+ qemu_free(se->compat);
+ }
+ qemu_free(se);
+ }
+ }
+}
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque);
+
+int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque, int version_id)
+{
+ VMStateField *field = vmsd->fields;
+ int ret;
+
+ if (version_id > vmsd->version_id) {
+ return -EINVAL;
+ }
+ if (version_id < vmsd->minimum_version_id_old) {
+ return -EINVAL;
+ }
+ if (version_id < vmsd->minimum_version_id) {
+ return vmsd->load_state_old(f, opaque, version_id);
+ }
+ if (vmsd->pre_load) {
+ int ret = vmsd->pre_load(opaque);
+ if (ret)
+ return ret;
+ }
+ while(field->name) {
+ if ((field->field_exists &&
+ field->field_exists(opaque, version_id)) ||
+ (!field->field_exists &&
+ field->version_id <= version_id)) {
+ void *base_addr = opaque + field->offset;
+ int i, n_elems = 1;
+ int size = field->size;
+
+ if (field->flags & VMS_VBUFFER) {
+ size = *(int32_t *)(opaque+field->size_offset);
+ if (field->flags & VMS_MULTIPLY) {
+ size *= field->size;
+ }
+ }
+ if (field->flags & VMS_ARRAY) {
+ n_elems = field->num;
+ } else if (field->flags & VMS_VARRAY_INT32) {
+ n_elems = *(int32_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT16) {
+ n_elems = *(uint16_t *)(opaque+field->num_offset);
+ }
+ if (field->flags & VMS_POINTER) {
+ base_addr = *(void **)base_addr + field->start;
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *addr = base_addr + size * i;
+
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ addr = *(void **)addr;
+ }
+ if (field->flags & VMS_STRUCT) {
+ ret = vmstate_load_state(f, field->vmsd, addr, field->vmsd->version_id);
+ } else {
+ ret = field->info->get(f, addr, size);
+
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ }
+ field++;
+ }
+ ret = vmstate_subsection_load(f, vmsd, opaque);
+ if (ret != 0) {
+ return ret;
+ }
+ if (vmsd->post_load) {
+ return vmsd->post_load(opaque, version_id);
+ }
+ return 0;
+}
+
+void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ VMStateField *field = vmsd->fields;
+
+ if (vmsd->pre_save) {
+ vmsd->pre_save(opaque);
+ }
+ while(field->name) {
+ if (!field->field_exists ||
+ field->field_exists(opaque, vmsd->version_id)) {
+ void *base_addr = opaque + field->offset;
+ int i, n_elems = 1;
+ int size = field->size;
+
+ if (field->flags & VMS_VBUFFER) {
+ size = *(int32_t *)(opaque+field->size_offset);
+ if (field->flags & VMS_MULTIPLY) {
+ size *= field->size;
+ }
+ }
+ if (field->flags & VMS_ARRAY) {
+ n_elems = field->num;
+ } else if (field->flags & VMS_VARRAY_INT32) {
+ n_elems = *(int32_t *)(opaque+field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT16) {
+ n_elems = *(uint16_t *)(opaque+field->num_offset);
+ }
+ if (field->flags & VMS_POINTER) {
+ base_addr = *(void **)base_addr + field->start;
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *addr = base_addr + size * i;
+
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ addr = *(void **)addr;
+ }
+ if (field->flags & VMS_STRUCT) {
+ vmstate_save_state(f, field->vmsd, addr);
+ } else {
+ field->info->put(f, addr, size);
+ }
+ }
+ }
+ field++;
+ }
+ vmstate_subsection_save(f, vmsd, opaque);
+}
+
+static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
+{
+ if (!se->vmsd) { /* Old style */
+ return se->load_state(f, se->opaque, version_id);
+ }
+ return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
+}
+
+static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
+{
+ if (!se->vmsd) { /* Old style */
+ se->save_state(f, se->opaque);
+ return;
+ }
+ vmstate_save_state(f,se->vmsd, se->opaque);
+}
+
+#define QEMU_VM_FILE_MAGIC 0x5145564d
+#define QEMU_VM_FILE_VERSION_COMPAT 0x00000002
+#define QEMU_VM_FILE_VERSION 0x00000003
+
+#define QEMU_VM_EOF 0x00
+#define QEMU_VM_SECTION_START 0x01
+#define QEMU_VM_SECTION_PART 0x02
+#define QEMU_VM_SECTION_END 0x03
+#define QEMU_VM_SECTION_FULL 0x04
+#define QEMU_VM_SUBSECTION 0x05
+
+bool qemu_savevm_state_blocked(Monitor *mon)
+{
+ SaveStateEntry *se;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (se->no_migrate) {
+ monitor_printf(mon, "state blocked by non-migratable device '%s'\n",
+ se->idstr);
+ return true;
+ }
+ }
+ return false;
+}
+
+int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
+ int shared)
+{
+ SaveStateEntry *se;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if(se->set_params == NULL) {
+ continue;
+ }
+ se->set_params(blk_enable, shared, se->opaque);
+ }
+
+ qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+ qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ int len;
+
+ if (se->save_live_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_START);
+ qemu_put_be32(f, se->section_id);
+
+ /* ID string */
+ len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
+ }
+
+ if (qemu_file_has_error(f)) {
+ qemu_savevm_state_cancel(mon, f);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
+{
+ SaveStateEntry *se;
+ int ret = 1;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (se->save_live_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_PART);
+ qemu_put_be32(f, se->section_id);
+
+ ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque);
+ if (!ret) {
+ /* Do not proceed to the next vmstate before this one reported
+ completion of the current stage. This serializes the migration
+ and reduces the probability that a faster changing state is
+ synchronized over and over again. */
+ break;
+ }
+ }
+
+ if (ret)
+ return 1;
+
+ if (qemu_file_has_error(f)) {
+ qemu_savevm_state_cancel(mon, f);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
+{
+ SaveStateEntry *se;
+
+ cpu_synchronize_all_states();
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (se->save_live_state == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_END);
+ qemu_put_be32(f, se->section_id);
+
+ se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
+ }
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ int len;
+
+ if (se->save_state == NULL && se->vmsd == NULL)
+ continue;
+
+ /* Section type */
+ qemu_put_byte(f, QEMU_VM_SECTION_FULL);
+ qemu_put_be32(f, se->section_id);
+
+ /* ID string */
+ len = strlen(se->idstr);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+ qemu_put_be32(f, se->instance_id);
+ qemu_put_be32(f, se->version_id);
+
+ vmstate_save(f, se);
+ }
+
+ qemu_put_byte(f, QEMU_VM_EOF);
+
+ if (qemu_file_has_error(f))
+ return -EIO;
+
+ return 0;
+}
+
+void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f)
+{
+ SaveStateEntry *se;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (se->save_live_state) {
+ se->save_live_state(mon, f, -1, se->opaque);
+ }
+ }
+}
+
+static int qemu_savevm_state(Monitor *mon, QEMUFile *f)
+{
+ int saved_vm_running;
+ int ret;
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ if (qemu_savevm_state_blocked(mon)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = qemu_savevm_state_begin(mon, f, 0, 0);
+ if (ret < 0)
+ goto out;
+
+ do {
+ ret = qemu_savevm_state_iterate(mon, f);
+ if (ret < 0)
+ goto out;
+ } while (ret == 0);
+
+ ret = qemu_savevm_state_complete(mon, f);
+
+out:
+ if (qemu_file_has_error(f))
+ ret = -EIO;
+
+ if (!ret && saved_vm_running)
+ vm_start();
+
+ return ret;
+}
+
+static SaveStateEntry *find_se(const char *idstr, int instance_id)
+{
+ SaveStateEntry *se;
+
+ QTAILQ_FOREACH(se, &savevm_handlers, entry) {
+ if (!strcmp(se->idstr, idstr) &&
+ (instance_id == se->instance_id ||
+ instance_id == se->alias_id))
+ return se;
+ /* Migrating from an older version? */
+ if (strstr(se->idstr, idstr) && se->compat) {
+ if (!strcmp(se->compat->idstr, idstr) &&
+ (instance_id == se->compat->instance_id ||
+ instance_id == se->alias_id))
+ return se;
+ }
+ }
+ return NULL;
+}
+
+static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr)
+{
+ while(sub && sub->needed) {
+ if (strcmp(idstr, sub->vmsd->name) == 0) {
+ return sub->vmsd;
+ }
+ sub++;
+ }
+ return NULL;
+}
+
+static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ const VMStateSubsection *sub = vmsd->subsections;
+
+ if (!sub || !sub->needed) {
+ return 0;
+ }
+
+ while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) {
+ char idstr[256];
+ int ret;
+ uint8_t version_id, len;
+ const VMStateDescription *sub_vmsd;
+
+ qemu_get_byte(f); /* subsection */
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)idstr, len);
+ idstr[len] = 0;
+ version_id = qemu_get_be32(f);
+
+ sub_vmsd = vmstate_get_subsection(sub, idstr);
+ if (sub_vmsd == NULL) {
+ return -ENOENT;
+ }
+ assert(!sub_vmsd->subsections);
+ ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ const VMStateSubsection *sub = vmsd->subsections;
+
+ while (sub && sub->needed) {
+ if (sub->needed(opaque)) {
+ const VMStateDescription *vmsd = sub->vmsd;
+ uint8_t len;
+
+ qemu_put_byte(f, QEMU_VM_SUBSECTION);
+ len = strlen(vmsd->name);
+ qemu_put_byte(f, len);
+ qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
+ qemu_put_be32(f, vmsd->version_id);
+ assert(!vmsd->subsections);
+ vmstate_save_state(f, vmsd, opaque);
+ }
+ sub++;
+ }
+}
+
+typedef struct LoadStateEntry {
+ QLIST_ENTRY(LoadStateEntry) entry;
+ SaveStateEntry *se;
+ int section_id;
+ int version_id;
+} LoadStateEntry;
+
+int qemu_loadvm_state(QEMUFile *f)
+{
+ QLIST_HEAD(, LoadStateEntry) loadvm_handlers =
+ QLIST_HEAD_INITIALIZER(loadvm_handlers);
+ LoadStateEntry *le, *new_le;
+ uint8_t section_type;
+ unsigned int v;
+ int ret;
+
+ if (qemu_savevm_state_blocked(default_mon)) {
+ return -EINVAL;
+ }
+
+ v = qemu_get_be32(f);
+ if (v != QEMU_VM_FILE_MAGIC)
+ return -EINVAL;
+
+ v = qemu_get_be32(f);
+ if (v == QEMU_VM_FILE_VERSION_COMPAT) {
+ fprintf(stderr, "SaveVM v2 format is obsolete and don't work anymore\n");
+ return -ENOTSUP;
+ }
+ if (v != QEMU_VM_FILE_VERSION)
+ return -ENOTSUP;
+
+ while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) {
+ uint32_t instance_id, version_id, section_id;
+ SaveStateEntry *se;
+ char idstr[257];
+ int len;
+
+ switch (section_type) {
+ case QEMU_VM_SECTION_START:
+ case QEMU_VM_SECTION_FULL:
+ /* Read section start */
+ section_id = qemu_get_be32(f);
+ len = qemu_get_byte(f);
+ qemu_get_buffer(f, (uint8_t *)idstr, len);
+ idstr[len] = 0;
+ instance_id = qemu_get_be32(f);
+ version_id = qemu_get_be32(f);
+
+ /* Find savevm section */
+ se = find_se(idstr, instance_id);
+ if (se == NULL) {
+ fprintf(stderr, "Unknown savevm section or instance '%s' %d\n", idstr, instance_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Validate version */
+ if (version_id > se->version_id) {
+ fprintf(stderr, "savevm: unsupported version %d for '%s' v%d\n",
+ version_id, idstr, se->version_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Add entry */
+ le = qemu_mallocz(sizeof(*le));
+
+ le->se = se;
+ le->section_id = section_id;
+ le->version_id = version_id;
+ QLIST_INSERT_HEAD(&loadvm_handlers, le, entry);
+
+ ret = vmstate_load(f, le->se, le->version_id);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
+ instance_id, idstr);
+ goto out;
+ }
+ break;
+ case QEMU_VM_SECTION_PART:
+ case QEMU_VM_SECTION_END:
+ section_id = qemu_get_be32(f);
+
+ QLIST_FOREACH(le, &loadvm_handlers, entry) {
+ if (le->section_id == section_id) {
+ break;
+ }
+ }
+ if (le == NULL) {
+ fprintf(stderr, "Unknown savevm section %d\n", section_id);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = vmstate_load(f, le->se, le->version_id);
+ if (ret < 0) {
+ fprintf(stderr, "qemu: warning: error while loading state section id %d\n",
+ section_id);
+ goto out;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown savevm section type %d\n", section_type);
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ cpu_synchronize_all_post_init();
+
+ ret = 0;
+
+out:
+ QLIST_FOREACH_SAFE(le, &loadvm_handlers, entry, new_le) {
+ QLIST_REMOVE(le, entry);
+ qemu_free(le);
+ }
+
+ if (qemu_file_has_error(f))
+ ret = -EIO;
+
+ return ret;
+}
+
+static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
+ const char *name)
+{
+ QEMUSnapshotInfo *sn_tab, *sn;
+ int nb_sns, i, ret;
+
+ ret = -ENOENT;
+ nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+ if (nb_sns < 0)
+ return ret;
+ for(i = 0; i < nb_sns; i++) {
+ sn = &sn_tab[i];
+ if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
+ *sn_info = *sn;
+ ret = 0;
+ break;
+ }
+ }
+ qemu_free(sn_tab);
+ return ret;
+}
+
+/*
+ * Deletes snapshots of a given name in all opened images.
+ */
+static int del_existing_snapshots(Monitor *mon, const char *name)
+{
+ BlockDriverState *bs;
+ QEMUSnapshotInfo sn1, *snapshot = &sn1;
+ int ret;
+
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+ if (bdrv_can_snapshot(bs) &&
+ bdrv_snapshot_find(bs, snapshot, name) >= 0)
+ {
+ ret = bdrv_snapshot_delete(bs, name);
+ if (ret < 0) {
+ monitor_printf(mon,
+ "Error while deleting snapshot on '%s'\n",
+ bdrv_get_device_name(bs));
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void do_savevm(Monitor *mon, const QDict *qdict)
+{
+ BlockDriverState *bs, *bs1;
+ QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
+ int ret;
+ QEMUFile *f;
+ int saved_vm_running;
+ uint32_t vm_state_size;
+#ifdef _WIN32
+ struct _timeb tb;
+ struct tm *ptm;
+#else
+ struct timeval tv;
+ struct tm tm;
+#endif
+ const char *name = qdict_get_try_str(qdict, "name");
+
+ /* Verify if there is a device that doesn't support snapshots and is writable */
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+
+ if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ continue;
+ }
+
+ if (!bdrv_can_snapshot(bs)) {
+ monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n",
+ bdrv_get_device_name(bs));
+ return;
+ }
+ }
+
+ bs = bdrv_snapshots();
+ if (!bs) {
+ monitor_printf(mon, "No block device can accept snapshots\n");
+ return;
+ }
+
+ saved_vm_running = vm_running;
+ vm_stop(0);
+
+ memset(sn, 0, sizeof(*sn));
+
+ /* fill auxiliary fields */
+#ifdef _WIN32
+ _ftime(&tb);
+ sn->date_sec = tb.time;
+ sn->date_nsec = tb.millitm * 1000000;
+#else
+ gettimeofday(&tv, NULL);
+ sn->date_sec = tv.tv_sec;
+ sn->date_nsec = tv.tv_usec * 1000;
+#endif
+ sn->vm_clock_nsec = qemu_get_clock(vm_clock);
+
+ if (name) {
+ ret = bdrv_snapshot_find(bs, old_sn, name);
+ if (ret >= 0) {
+ pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
+ pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
+ } else {
+ pstrcpy(sn->name, sizeof(sn->name), name);
+ }
+ } else {
+#ifdef _WIN32
+ ptm = localtime(&tb.time);
+ strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", ptm);
+#else
+ /* cast below needed for OpenBSD where tv_sec is still 'long' */
+ localtime_r((const time_t *)&tv.tv_sec, &tm);
+ strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm);
+#endif
+ }
+
+ /* Delete old snapshots of the same name */
+ if (name && del_existing_snapshots(mon, name) < 0) {
+ goto the_end;
+ }
+
+ /* save the VM state */
+ f = qemu_fopen_bdrv(bs, 1);
+ if (!f) {
+ monitor_printf(mon, "Could not open VM state file\n");
+ goto the_end;
+ }
+ ret = qemu_savevm_state(mon, f);
+ vm_state_size = qemu_ftell(f);
+ qemu_fclose(f);
+ if (ret < 0) {
+ monitor_printf(mon, "Error %d while writing VM\n", ret);
+ goto the_end;
+ }
+
+ /* create the snapshots */
+
+ bs1 = NULL;
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1)) {
+ /* Write VM state size only to the image that contains the state */
+ sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
+ ret = bdrv_snapshot_create(bs1, sn);
+ if (ret < 0) {
+ monitor_printf(mon, "Error while creating snapshot on '%s'\n",
+ bdrv_get_device_name(bs1));
+ }
+ }
+ }
+
+ the_end:
+ if (saved_vm_running)
+ vm_start();
+}
+
+int load_vmstate(const char *name)
+{
+ BlockDriverState *bs, *bs_vm_state;
+ QEMUSnapshotInfo sn;
+ QEMUFile *f;
+ int ret;
+
+ bs_vm_state = bdrv_snapshots();
+ if (!bs_vm_state) {
+ error_report("No block device supports snapshots");
+ return -ENOTSUP;
+ }
+
+ /* Don't even try to load empty VM states */
+ ret = bdrv_snapshot_find(bs_vm_state, &sn, name);
+ if (ret < 0) {
+ return ret;
+ } else if (sn.vm_state_size == 0) {
+ return -EINVAL;
+ }
+
+ /* Verify if there is any device that doesn't support snapshots and is
+ writable and check if the requested snapshot is available too. */
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+
+ if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
+ continue;
+ }
+
+ if (!bdrv_can_snapshot(bs)) {
+ error_report("Device '%s' is writable but does not support snapshots.",
+ bdrv_get_device_name(bs));
+ return -ENOTSUP;
+ }
+
+ ret = bdrv_snapshot_find(bs, &sn, name);
+ if (ret < 0) {
+ error_report("Device '%s' does not have the requested snapshot '%s'",
+ bdrv_get_device_name(bs), name);
+ return ret;
+ }
+ }
+
+ /* Flush all IO requests so they don't interfere with the new state. */
+ qemu_aio_flush();
+
+ bs = NULL;
+ while ((bs = bdrv_next(bs))) {
+ if (bdrv_can_snapshot(bs)) {
+ ret = bdrv_snapshot_goto(bs, name);
+ if (ret < 0) {
+ error_report("Error %d while activating snapshot '%s' on '%s'",
+ ret, name, bdrv_get_device_name(bs));
+ return ret;
+ }
+ }
+ }
+
+ /* restore the VM state */
+ f = qemu_fopen_bdrv(bs_vm_state, 0);
+ if (!f) {
+ error_report("Could not open VM state file");
+ return -EINVAL;
+ }
+
+ ret = qemu_loadvm_state(f);
+
+ qemu_fclose(f);
+ if (ret < 0) {
+ error_report("Error %d while loading VM state", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void do_delvm(Monitor *mon, const QDict *qdict)
+{
+ BlockDriverState *bs, *bs1;
+ int ret;
+ const char *name = qdict_get_str(qdict, "name");
+
+ bs = bdrv_snapshots();
+ if (!bs) {
+ monitor_printf(mon, "No block device supports snapshots\n");
+ return;
+ }
+
+ bs1 = NULL;
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1)) {
+ ret = bdrv_snapshot_delete(bs1, name);
+ if (ret < 0) {
+ if (ret == -ENOTSUP)
+ monitor_printf(mon,
+ "Snapshots not supported on device '%s'\n",
+ bdrv_get_device_name(bs1));
+ else
+ monitor_printf(mon, "Error %d while deleting snapshot on "
+ "'%s'\n", ret, bdrv_get_device_name(bs1));
+ }
+ }
+ }
+}
+
+void do_info_snapshots(Monitor *mon)
+{
+ BlockDriverState *bs, *bs1;
+ QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s;
+ int nb_sns, i, ret, available;
+ int total;
+ int *available_snapshots;
+ char buf[256];
+
+ bs = bdrv_snapshots();
+ if (!bs) {
+ monitor_printf(mon, "No available block device supports snapshots\n");
+ return;
+ }
+
+ nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+ if (nb_sns < 0) {
+ monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
+ return;
+ }
+
+ if (nb_sns == 0) {
+ monitor_printf(mon, "There is no snapshot available.\n");
+ return;
+ }
+
+ available_snapshots = qemu_mallocz(sizeof(int) * nb_sns);
+ total = 0;
+ for (i = 0; i < nb_sns; i++) {
+ sn = &sn_tab[i];
+ available = 1;
+ bs1 = NULL;
+
+ while ((bs1 = bdrv_next(bs1))) {
+ if (bdrv_can_snapshot(bs1) && bs1 != bs) {
+ ret = bdrv_snapshot_find(bs1, sn_info, sn->id_str);
+ if (ret < 0) {
+ available = 0;
+ break;
+ }
+ }
+ }
+
+ if (available) {
+ available_snapshots[total] = i;
+ total++;
+ }
+ }
+
+ if (total > 0) {
+ monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+ for (i = 0; i < total; i++) {
+ sn = &sn_tab[available_snapshots[i]];
+ monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+ }
+ } else {
+ monitor_printf(mon, "There is no suitable snapshot available\n");
+ }
+
+ qemu_free(sn_tab);
+ qemu_free(available_snapshots);
+
+}
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
new file mode 100755
index 0000000..4fa06c0
--- /dev/null
+++ b/scripts/checkpatch.pl
@@ -0,0 +1,2910 @@
+#!/usr/bin/perl -w
+# (c) 2001, Dave Jones. (the file handling bit)
+# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
+# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
+# (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+use strict;
+
+my $P = $0;
+$P =~ s@.*/@@g;
+
+my $V = '0.31';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+
+my $quiet = 0;
+my $tree = 1;
+my $chk_signoff = 1;
+my $chk_patch = 1;
+my $tst_only;
+my $emacs = 0;
+my $terse = 0;
+my $file = 0;
+my $check = 0;
+my $summary = 1;
+my $mailback = 0;
+my $summary_file = 0;
+my $root;
+my %debug;
+my $help = 0;
+
+sub help {
+ my ($exitcode) = @_;
+
+ print << "EOM";
+Usage: $P [OPTION]... [FILE]...
+Version: $V
+
+Options:
+ -q, --quiet quiet
+ --no-tree run without a kernel tree
+ --no-signoff do not check for 'Signed-off-by' line
+ --patch treat FILE as patchfile (default)
+ --emacs emacs compile window format
+ --terse one line per report
+ -f, --file treat FILE as regular source file
+ --subjective, --strict enable more subjective tests
+ --root=PATH PATH to the kernel tree root
+ --no-summary suppress the per-file summary
+ --mailback only produce a report in case of warnings/errors
+ --summary-file include the filename in summary
+ --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of
+ 'values', 'possible', 'type', and 'attr' (default
+ is all off)
+ --test-only=WORD report only warnings/errors containing WORD
+ literally
+ -h, --help, --version display this help and exit
+
+When FILE is - read standard input.
+EOM
+
+ exit($exitcode);
+}
+
+GetOptions(
+ 'q|quiet+' => \$quiet,
+ 'tree!' => \$tree,
+ 'signoff!' => \$chk_signoff,
+ 'patch!' => \$chk_patch,
+ 'emacs!' => \$emacs,
+ 'terse!' => \$terse,
+ 'f|file!' => \$file,
+ 'subjective!' => \$check,
+ 'strict!' => \$check,
+ 'root=s' => \$root,
+ 'summary!' => \$summary,
+ 'mailback!' => \$mailback,
+ 'summary-file!' => \$summary_file,
+
+ 'debug=s' => \%debug,
+ 'test-only=s' => \$tst_only,
+ 'h|help' => \$help,
+ 'version' => \$help
+) or help(1);
+
+help(0) if ($help);
+
+my $exit = 0;
+
+if ($#ARGV < 0) {
+ print "$P: no input files\n";
+ exit(1);
+}
+
+my $dbg_values = 0;
+my $dbg_possible = 0;
+my $dbg_type = 0;
+my $dbg_attr = 0;
+for my $key (keys %debug) {
+ ## no critic
+ eval "\${dbg_$key} = '$debug{$key}';";
+ die "$@" if ($@);
+}
+
+my $rpt_cleaners = 0;
+
+if ($terse) {
+ $emacs = 1;
+ $quiet++;
+}
+
+if ($tree) {
+ if (defined $root) {
+ if (!top_of_kernel_tree($root)) {
+ die "$P: $root: --root does not point at a valid tree\n";
+ }
+ } else {
+ if (top_of_kernel_tree('.')) {
+ $root = '.';
+ } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ &&
+ top_of_kernel_tree($1)) {
+ $root = $1;
+ }
+ }
+
+ if (!defined $root) {
+ print "Must be run from the top-level dir. of a kernel tree\n";
+ exit(2);
+ }
+}
+
+my $emitted_corrupt = 0;
+
+our $Ident = qr{
+ [A-Za-z_][A-Za-z\d_]*
+ (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
+ }x;
+our $Storage = qr{extern|static|asmlinkage};
+our $Sparse = qr{
+ __user|
+ __kernel|
+ __force|
+ __iomem|
+ __must_check|
+ __init_refok|
+ __kprobes|
+ __ref
+ }x;
+
+# Notes to $Attribute:
+# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
+our $Attribute = qr{
+ const|
+ __percpu|
+ __nocast|
+ __safe|
+ __bitwise__|
+ __packed__|
+ __packed2__|
+ __naked|
+ __maybe_unused|
+ __always_unused|
+ __noreturn|
+ __used|
+ __cold|
+ __noclone|
+ __deprecated|
+ __read_mostly|
+ __kprobes|
+ __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)|
+ ____cacheline_aligned|
+ ____cacheline_aligned_in_smp|
+ ____cacheline_internodealigned_in_smp|
+ __weak
+ }x;
+our $Modifier;
+our $Inline = qr{inline|__always_inline|noinline};
+our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]};
+our $Lval = qr{$Ident(?:$Member)*};
+
+our $Constant = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*};
+our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)};
+our $Compare = qr{<=|>=|==|!=|<|>};
+our $Operators = qr{
+ <=|>=|==|!=|
+ =>|->|<<|>>|<|>|!|~|
+ &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%
+ }x;
+
+our $NonptrType;
+our $Type;
+our $Declare;
+
+our $UTF8 = qr {
+ [\x09\x0A\x0D\x20-\x7E] # ASCII
+ | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
+ | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
+ | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
+ | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
+ | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
+ | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
+}x;
+
+our $typeTypedefs = qr{(?x:
+ (?:__)?(?:u|s|be|le)(?:8|16|32|64)|
+ atomic_t
+)};
+
+our $logFunctions = qr{(?x:
+ printk|
+ pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)|
+ (dev|netdev|netif)_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)|
+ WARN|
+ panic
+)};
+
+our @typeList = (
+ qr{void},
+ qr{(?:unsigned\s+)?char},
+ qr{(?:unsigned\s+)?short},
+ qr{(?:unsigned\s+)?int},
+ qr{(?:unsigned\s+)?long},
+ qr{(?:unsigned\s+)?long\s+int},
+ qr{(?:unsigned\s+)?long\s+long},
+ qr{(?:unsigned\s+)?long\s+long\s+int},
+ qr{unsigned},
+ qr{float},
+ qr{double},
+ qr{bool},
+ qr{struct\s+$Ident},
+ qr{union\s+$Ident},
+ qr{enum\s+$Ident},
+ qr{${Ident}_t},
+ qr{${Ident}_handler},
+ qr{${Ident}_handler_fn},
+);
+our @modifierList = (
+ qr{fastcall},
+);
+
+our $allowed_asm_includes = qr{(?x:
+ irq|
+ memory
+)};
+# memory.h: ARM has a custom one
+
+sub build_types {
+ my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)";
+ my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)";
+ $Modifier = qr{(?:$Attribute|$Sparse|$mods)};
+ $NonptrType = qr{
+ (?:$Modifier\s+|const\s+)*
+ (?:
+ (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)|
+ (?:$typeTypedefs\b)|
+ (?:${all}\b)
+ )
+ (?:\s+$Modifier|\s+const)*
+ }x;
+ $Type = qr{
+ $NonptrType
+ (?:[\s\*]+\s*const|[\s\*]+|(?:\s*\[\s*\])+)?
+ (?:\s+$Inline|\s+$Modifier)*
+ }x;
+ $Declare = qr{(?:$Storage\s+)?$Type};
+}
+build_types();
+
+$chk_signoff = 0 if ($file);
+
+my @dep_includes = ();
+my @dep_functions = ();
+my $removal = "Documentation/feature-removal-schedule.txt";
+if ($tree && -f "$root/$removal") {
+ open(my $REMOVE, '<', "$root/$removal") ||
+ die "$P: $removal: open failed - $!\n";
+ while (<$REMOVE>) {
+ if (/^Check:\s+(.*\S)/) {
+ for my $entry (split(/[, ]+/, $1)) {
+ if ($entry =~ m@include/(.*)@) {
+ push(@dep_includes, $1);
+
+ } elsif ($entry !~ m@/@) {
+ push(@dep_functions, $entry);
+ }
+ }
+ }
+ }
+ close($REMOVE);
+}
+
+my @rawlines = ();
+my @lines = ();
+my $vname;
+for my $filename (@ARGV) {
+ my $FILE;
+ if ($file) {
+ open($FILE, '-|', "diff -u /dev/null $filename") ||
+ die "$P: $filename: diff failed - $!\n";
+ } elsif ($filename eq '-') {
+ open($FILE, '<&STDIN');
+ } else {
+ open($FILE, '<', "$filename") ||
+ die "$P: $filename: open failed - $!\n";
+ }
+ if ($filename eq '-') {
+ $vname = 'Your patch';
+ } else {
+ $vname = $filename;
+ }
+ while (<$FILE>) {
+ chomp;
+ push(@rawlines, $_);
+ }
+ close($FILE);
+ if (!process($filename)) {
+ $exit = 1;
+ }
+ @rawlines = ();
+ @lines = ();
+}
+
+exit($exit);
+
+sub top_of_kernel_tree {
+ my ($root) = @_;
+
+ my @tree_check = (
+ "COPYING", "MAINTAINERS", "Makefile",
+ "README", "docs", "VERSION",
+ "vl.c"
+ );
+
+ foreach my $check (@tree_check) {
+ if (! -e $root . '/' . $check) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+sub expand_tabs {
+ my ($str) = @_;
+
+ my $res = '';
+ my $n = 0;
+ for my $c (split(//, $str)) {
+ if ($c eq "\t") {
+ $res .= ' ';
+ $n++;
+ for (; ($n % 8) != 0; $n++) {
+ $res .= ' ';
+ }
+ next;
+ }
+ $res .= $c;
+ $n++;
+ }
+
+ return $res;
+}
+sub copy_spacing {
+ (my $res = shift) =~ tr/\t/ /c;
+ return $res;
+}
+
+sub line_stats {
+ my ($line) = @_;
+
+ # Drop the diff line leader and expand tabs
+ $line =~ s/^.//;
+ $line = expand_tabs($line);
+
+ # Pick the indent from the front of the line.
+ my ($white) = ($line =~ /^(\s*)/);
+
+ return (length($line), length($white));
+}
+
+my $sanitise_quote = '';
+
+sub sanitise_line_reset {
+ my ($in_comment) = @_;
+
+ if ($in_comment) {
+ $sanitise_quote = '*/';
+ } else {
+ $sanitise_quote = '';
+ }
+}
+sub sanitise_line {
+ my ($line) = @_;
+
+ my $res = '';
+ my $l = '';
+
+ my $qlen = 0;
+ my $off = 0;
+ my $c;
+
+ # Always copy over the diff marker.
+ $res = substr($line, 0, 1);
+
+ for ($off = 1; $off < length($line); $off++) {
+ $c = substr($line, $off, 1);
+
+ # Comments we are wacking completly including the begin
+ # and end, all to $;.
+ if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
+ $sanitise_quote = '*/';
+
+ substr($res, $off, 2, "$;$;");
+ $off++;
+ next;
+ }
+ if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
+ $sanitise_quote = '';
+ substr($res, $off, 2, "$;$;");
+ $off++;
+ next;
+ }
+ if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
+ $sanitise_quote = '//';
+
+ substr($res, $off, 2, $sanitise_quote);
+ $off++;
+ next;
+ }
+
+ # A \ in a string means ignore the next character.
+ if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
+ $c eq "\\") {
+ substr($res, $off, 2, 'XX');
+ $off++;
+ next;
+ }
+ # Regular quotes.
+ if ($c eq "'" || $c eq '"') {
+ if ($sanitise_quote eq '') {
+ $sanitise_quote = $c;
+
+ substr($res, $off, 1, $c);
+ next;
+ } elsif ($sanitise_quote eq $c) {
+ $sanitise_quote = '';
+ }
+ }
+
+ #print "c<$c> SQ<$sanitise_quote>\n";
+ if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
+ substr($res, $off, 1, $;);
+ } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
+ substr($res, $off, 1, $;);
+ } elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
+ substr($res, $off, 1, 'X');
+ } else {
+ substr($res, $off, 1, $c);
+ }
+ }
+
+ if ($sanitise_quote eq '//') {
+ $sanitise_quote = '';
+ }
+
+ # The pathname on a #include may be surrounded by '<' and '>'.
+ if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
+ my $clean = 'X' x length($1);
+ $res =~ s@\<.*\>@<$clean>@;
+
+ # The whole of a #error is a string.
+ } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
+ my $clean = 'X' x length($1);
+ $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
+ }
+
+ return $res;
+}
+
+sub ctx_statement_block {
+ my ($linenr, $remain, $off) = @_;
+ my $line = $linenr - 1;
+ my $blk = '';
+ my $soff = $off;
+ my $coff = $off - 1;
+ my $coff_set = 0;
+
+ my $loff = 0;
+
+ my $type = '';
+ my $level = 0;
+ my @stack = ();
+ my $p;
+ my $c;
+ my $len = 0;
+
+ my $remainder;
+ while (1) {
+ @stack = (['', 0]) if ($#stack == -1);
+
+ #warn "CSB: blk<$blk> remain<$remain>\n";
+ # If we are about to drop off the end, pull in more
+ # context.
+ if ($off >= $len) {
+ for (; $remain > 0; $line++) {
+ last if (!defined $lines[$line]);
+ next if ($lines[$line] =~ /^-/);
+ $remain--;
+ $loff = $len;
+ $blk .= $lines[$line] . "\n";
+ $len = length($blk);
+ $line++;
+ last;
+ }
+ # Bail if there is no further context.
+ #warn "CSB: blk<$blk> off<$off> len<$len>\n";
+ if ($off >= $len) {
+ last;
+ }
+ }
+ $p = $c;
+ $c = substr($blk, $off, 1);
+ $remainder = substr($blk, $off);
+
+ #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
+
+ # Handle nested #if/#else.
+ if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
+ push(@stack, [ $type, $level ]);
+ } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
+ ($type, $level) = @{$stack[$#stack - 1]};
+ } elsif ($remainder =~ /^#\s*endif\b/) {
+ ($type, $level) = @{pop(@stack)};
+ }
+
+ # Statement ends at the ';' or a close '}' at the
+ # outermost level.
+ if ($level == 0 && $c eq ';') {
+ last;
+ }
+
+ # An else is really a conditional as long as its not else if
+ if ($level == 0 && $coff_set == 0 &&
+ (!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
+ $remainder =~ /^(else)(?:\s|{)/ &&
+ $remainder !~ /^else\s+if\b/) {
+ $coff = $off + length($1) - 1;
+ $coff_set = 1;
+ #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
+ #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
+ }
+
+ if (($type eq '' || $type eq '(') && $c eq '(') {
+ $level++;
+ $type = '(';
+ }
+ if ($type eq '(' && $c eq ')') {
+ $level--;
+ $type = ($level != 0)? '(' : '';
+
+ if ($level == 0 && $coff < $soff) {
+ $coff = $off;
+ $coff_set = 1;
+ #warn "CSB: mark coff<$coff>\n";
+ }
+ }
+ if (($type eq '' || $type eq '{') && $c eq '{') {
+ $level++;
+ $type = '{';
+ }
+ if ($type eq '{' && $c eq '}') {
+ $level--;
+ $type = ($level != 0)? '{' : '';
+
+ if ($level == 0) {
+ if (substr($blk, $off + 1, 1) eq ';') {
+ $off++;
+ }
+ last;
+ }
+ }
+ $off++;
+ }
+ # We are truly at the end, so shuffle to the next line.
+ if ($off == $len) {
+ $loff = $len + 1;
+ $line++;
+ $remain--;
+ }
+
+ my $statement = substr($blk, $soff, $off - $soff + 1);
+ my $condition = substr($blk, $soff, $coff - $soff + 1);
+
+ #warn "STATEMENT<$statement>\n";
+ #warn "CONDITION<$condition>\n";
+
+ #print "coff<$coff> soff<$off> loff<$loff>\n";
+
+ return ($statement, $condition,
+ $line, $remain + 1, $off - $loff + 1, $level);
+}
+
+sub statement_lines {
+ my ($stmt) = @_;
+
+ # Strip the diff line prefixes and rip blank lines at start and end.
+ $stmt =~ s/(^|\n)./$1/g;
+ $stmt =~ s/^\s*//;
+ $stmt =~ s/\s*$//;
+
+ my @stmt_lines = ($stmt =~ /\n/g);
+
+ return $#stmt_lines + 2;
+}
+
+sub statement_rawlines {
+ my ($stmt) = @_;
+
+ my @stmt_lines = ($stmt =~ /\n/g);
+
+ return $#stmt_lines + 2;
+}
+
+sub statement_block_size {
+ my ($stmt) = @_;
+
+ $stmt =~ s/(^|\n)./$1/g;
+ $stmt =~ s/^\s*{//;
+ $stmt =~ s/}\s*$//;
+ $stmt =~ s/^\s*//;
+ $stmt =~ s/\s*$//;
+
+ my @stmt_lines = ($stmt =~ /\n/g);
+ my @stmt_statements = ($stmt =~ /;/g);
+
+ my $stmt_lines = $#stmt_lines + 2;
+ my $stmt_statements = $#stmt_statements + 1;
+
+ if ($stmt_lines > $stmt_statements) {
+ return $stmt_lines;
+ } else {
+ return $stmt_statements;
+ }
+}
+
+sub ctx_statement_full {
+ my ($linenr, $remain, $off) = @_;
+ my ($statement, $condition, $level);
+
+ my (@chunks);
+
+ # Grab the first conditional/block pair.
+ ($statement, $condition, $linenr, $remain, $off, $level) =
+ ctx_statement_block($linenr, $remain, $off);
+ #print "F: c<$condition> s<$statement> remain<$remain>\n";
+ push(@chunks, [ $condition, $statement ]);
+ if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) {
+ return ($level, $linenr, @chunks);
+ }
+
+ # Pull in the following conditional/block pairs and see if they
+ # could continue the statement.
+ for (;;) {
+ ($statement, $condition, $linenr, $remain, $off, $level) =
+ ctx_statement_block($linenr, $remain, $off);
+ #print "C: c<$condition> s<$statement> remain<$remain>\n";
+ last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s));
+ #print "C: push\n";
+ push(@chunks, [ $condition, $statement ]);
+ }
+
+ return ($level, $linenr, @chunks);
+}
+
+sub ctx_block_get {
+ my ($linenr, $remain, $outer, $open, $close, $off) = @_;
+ my $line;
+ my $start = $linenr - 1;
+ my $blk = '';
+ my @o;
+ my @c;
+ my @res = ();
+
+ my $level = 0;
+ my @stack = ($level);
+ for ($line = $start; $remain > 0; $line++) {
+ next if ($rawlines[$line] =~ /^-/);
+ $remain--;
+
+ $blk .= $rawlines[$line];
+
+ # Handle nested #if/#else.
+ if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
+ push(@stack, $level);
+ } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
+ $level = $stack[$#stack - 1];
+ } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) {
+ $level = pop(@stack);
+ }
+
+ foreach my $c (split(//, $lines[$line])) {
+ ##print "C<$c>L<$level><$open$close>O<$off>\n";
+ if ($off > 0) {
+ $off--;
+ next;
+ }
+
+ if ($c eq $close && $level > 0) {
+ $level--;
+ last if ($level == 0);
+ } elsif ($c eq $open) {
+ $level++;
+ }
+ }
+
+ if (!$outer || $level <= 1) {
+ push(@res, $rawlines[$line]);
+ }
+
+ last if ($level == 0);
+ }
+
+ return ($level, @res);
+}
+sub ctx_block_outer {
+ my ($linenr, $remain) = @_;
+
+ my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0);
+ return @r;
+}
+sub ctx_block {
+ my ($linenr, $remain) = @_;
+
+ my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+ return @r;
+}
+sub ctx_statement {
+ my ($linenr, $remain, $off) = @_;
+
+ my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+ return @r;
+}
+sub ctx_block_level {
+ my ($linenr, $remain) = @_;
+
+ return ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+}
+sub ctx_statement_level {
+ my ($linenr, $remain, $off) = @_;
+
+ return ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+}
+
+sub ctx_locate_comment {
+ my ($first_line, $end_line) = @_;
+
+ # Catch a comment on the end of the line itself.
+ my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
+ return $current_comment if (defined $current_comment);
+
+ # Look through the context and try and figure out if there is a
+ # comment.
+ my $in_comment = 0;
+ $current_comment = '';
+ for (my $linenr = $first_line; $linenr < $end_line; $linenr++) {
+ my $line = $rawlines[$linenr - 1];
+ #warn " $line\n";
+ if ($linenr == $first_line and $line =~ m@^.\s*\*@) {
+ $in_comment = 1;
+ }
+ if ($line =~ m@/\*@) {
+ $in_comment = 1;
+ }
+ if (!$in_comment && $current_comment ne '') {
+ $current_comment = '';
+ }
+ $current_comment .= $line . "\n" if ($in_comment);
+ if ($line =~ m@\*/@) {
+ $in_comment = 0;
+ }
+ }
+
+ chomp($current_comment);
+ return($current_comment);
+}
+sub ctx_has_comment {
+ my ($first_line, $end_line) = @_;
+ my $cmt = ctx_locate_comment($first_line, $end_line);
+
+ ##print "LINE: $rawlines[$end_line - 1 ]\n";
+ ##print "CMMT: $cmt\n";
+
+ return ($cmt ne '');
+}
+
+sub raw_line {
+ my ($linenr, $cnt) = @_;
+
+ my $offset = $linenr - 1;
+ $cnt++;
+
+ my $line;
+ while ($cnt) {
+ $line = $rawlines[$offset++];
+ next if (defined($line) && $line =~ /^-/);
+ $cnt--;
+ }
+
+ return $line;
+}
+
+sub cat_vet {
+ my ($vet) = @_;
+ my ($res, $coded);
+
+ $res = '';
+ while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) {
+ $res .= $1;
+ if ($2 ne '') {
+ $coded = sprintf("^%c", unpack('C', $2) + 64);
+ $res .= $coded;
+ }
+ }
+ $res =~ s/$/\$/;
+
+ return $res;
+}
+
+my $av_preprocessor = 0;
+my $av_pending;
+my @av_paren_type;
+my $av_pend_colon;
+
+sub annotate_reset {
+ $av_preprocessor = 0;
+ $av_pending = '_';
+ @av_paren_type = ('E');
+ $av_pend_colon = 'O';
+}
+
+sub annotate_values {
+ my ($stream, $type) = @_;
+
+ my $res;
+ my $var = '_' x length($stream);
+ my $cur = $stream;
+
+ print "$stream\n" if ($dbg_values > 1);
+
+ while (length($cur)) {
+ @av_paren_type = ('E') if ($#av_paren_type < 0);
+ print " <" . join('', @av_paren_type) .
+ "> <$type> <$av_pending>" if ($dbg_values > 1);
+ if ($cur =~ /^(\s+)/o) {
+ print "WS($1)\n" if ($dbg_values > 1);
+ if ($1 =~ /\n/ && $av_preprocessor) {
+ $type = pop(@av_paren_type);
+ $av_preprocessor = 0;
+ }
+
+ } elsif ($cur =~ /^(\(\s*$Type\s*)\)/) {
+ print "CAST($1)\n" if ($dbg_values > 1);
+ push(@av_paren_type, $type);
+ $type = 'C';
+
+ } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) {
+ print "DECLARE($1)\n" if ($dbg_values > 1);
+ $type = 'T';
+
+ } elsif ($cur =~ /^($Modifier)\s*/) {
+ print "MODIFIER($1)\n" if ($dbg_values > 1);
+ $type = 'T';
+
+ } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
+ print "DEFINE($1,$2)\n" if ($dbg_values > 1);
+ $av_preprocessor = 1;
+ push(@av_paren_type, $type);
+ if ($2 ne '') {
+ $av_pending = 'N';
+ }
+ $type = 'E';
+
+ } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) {
+ print "UNDEF($1)\n" if ($dbg_values > 1);
+ $av_preprocessor = 1;
+ push(@av_paren_type, $type);
+
+ } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) {
+ print "PRE_START($1)\n" if ($dbg_values > 1);
+ $av_preprocessor = 1;
+
+ push(@av_paren_type, $type);
+ push(@av_paren_type, $type);
+ $type = 'E';
+
+ } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) {
+ print "PRE_RESTART($1)\n" if ($dbg_values > 1);
+ $av_preprocessor = 1;
+
+ push(@av_paren_type, $av_paren_type[$#av_paren_type]);
+
+ $type = 'E';
+
+ } elsif ($cur =~ /^(\#\s*(?:endif))/o) {
+ print "PRE_END($1)\n" if ($dbg_values > 1);
+
+ $av_preprocessor = 1;
+
+ # Assume all arms of the conditional end as this
+ # one does, and continue as if the #endif was not here.
+ pop(@av_paren_type);
+ push(@av_paren_type, $type);
+ $type = 'E';
+
+ } elsif ($cur =~ /^(\\\n)/o) {
+ print "PRECONT($1)\n" if ($dbg_values > 1);
+
+ } elsif ($cur =~ /^(__attribute__)\s*\(?/o) {
+ print "ATTR($1)\n" if ($dbg_values > 1);
+ $av_pending = $type;
+ $type = 'N';
+
+ } elsif ($cur =~ /^(sizeof)\s*(\()?/o) {
+ print "SIZEOF($1)\n" if ($dbg_values > 1);
+ if (defined $2) {
+ $av_pending = 'V';
+ }
+ $type = 'N';
+
+ } elsif ($cur =~ /^(if|while|for)\b/o) {
+ print "COND($1)\n" if ($dbg_values > 1);
+ $av_pending = 'E';
+ $type = 'N';
+
+ } elsif ($cur =~/^(case)/o) {
+ print "CASE($1)\n" if ($dbg_values > 1);
+ $av_pend_colon = 'C';
+ $type = 'N';
+
+ } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
+ print "KEYWORD($1)\n" if ($dbg_values > 1);
+ $type = 'N';
+
+ } elsif ($cur =~ /^(\()/o) {
+ print "PAREN('$1')\n" if ($dbg_values > 1);
+ push(@av_paren_type, $av_pending);
+ $av_pending = '_';
+ $type = 'N';
+
+ } elsif ($cur =~ /^(\))/o) {
+ my $new_type = pop(@av_paren_type);
+ if ($new_type ne '_') {
+ $type = $new_type;
+ print "PAREN('$1') -> $type\n"
+ if ($dbg_values > 1);
+ } else {
+ print "PAREN('$1')\n" if ($dbg_values > 1);
+ }
+
+ } elsif ($cur =~ /^($Ident)\s*\(/o) {
+ print "FUNC($1)\n" if ($dbg_values > 1);
+ $type = 'V';
+ $av_pending = 'V';
+
+ } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
+ if (defined $2 && $type eq 'C' || $type eq 'T') {
+ $av_pend_colon = 'B';
+ } elsif ($type eq 'E') {
+ $av_pend_colon = 'L';
+ }
+ print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
+ $type = 'V';
+
+ } elsif ($cur =~ /^($Ident|$Constant)/o) {
+ print "IDENT($1)\n" if ($dbg_values > 1);
+ $type = 'V';
+
+ } elsif ($cur =~ /^($Assignment)/o) {
+ print "ASSIGN($1)\n" if ($dbg_values > 1);
+ $type = 'N';
+
+ } elsif ($cur =~/^(;|{|})/) {
+ print "END($1)\n" if ($dbg_values > 1);
+ $type = 'E';
+ $av_pend_colon = 'O';
+
+ } elsif ($cur =~/^(,)/) {
+ print "COMMA($1)\n" if ($dbg_values > 1);
+ $type = 'C';
+
+ } elsif ($cur =~ /^(\?)/o) {
+ print "QUESTION($1)\n" if ($dbg_values > 1);
+ $type = 'N';
+
+ } elsif ($cur =~ /^(:)/o) {
+ print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
+
+ substr($var, length($res), 1, $av_pend_colon);
+ if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
+ $type = 'E';
+ } else {
+ $type = 'N';
+ }
+ $av_pend_colon = 'O';
+
+ } elsif ($cur =~ /^(\[)/o) {
+ print "CLOSE($1)\n" if ($dbg_values > 1);
+ $type = 'N';
+
+ } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
+ my $variant;
+
+ print "OPV($1)\n" if ($dbg_values > 1);
+ if ($type eq 'V') {
+ $variant = 'B';
+ } else {
+ $variant = 'U';
+ }
+
+ substr($var, length($res), 1, $variant);
+ $type = 'N';
+
+ } elsif ($cur =~ /^($Operators)/o) {
+ print "OP($1)\n" if ($dbg_values > 1);
+ if ($1 ne '++' && $1 ne '--') {
+ $type = 'N';
+ }
+
+ } elsif ($cur =~ /(^.)/o) {
+ print "C($1)\n" if ($dbg_values > 1);
+ }
+ if (defined $1) {
+ $cur = substr($cur, length($1));
+ $res .= $type x length($1);
+ }
+ }
+
+ return ($res, $var);
+}
+
+sub possible {
+ my ($possible, $line) = @_;
+ my $notPermitted = qr{(?:
+ ^(?:
+ $Modifier|
+ $Storage|
+ $Type|
+ DEFINE_\S+
+ )$|
+ ^(?:
+ goto|
+ return|
+ case|
+ else|
+ asm|__asm__|
+ do
+ )(?:\s|$)|
+ ^(?:typedef|struct|enum)\b
+ )}x;
+ warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
+ if ($possible !~ $notPermitted) {
+ # Check for modifiers.
+ $possible =~ s/\s*$Storage\s*//g;
+ $possible =~ s/\s*$Sparse\s*//g;
+ if ($possible =~ /^\s*$/) {
+
+ } elsif ($possible =~ /\s/) {
+ $possible =~ s/\s*$Type\s*//g;
+ for my $modifier (split(' ', $possible)) {
+ if ($modifier !~ $notPermitted) {
+ warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
+ push(@modifierList, $modifier);
+ }
+ }
+
+ } else {
+ warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
+ push(@typeList, $possible);
+ }
+ build_types();
+ } else {
+ warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
+ }
+}
+
+my $prefix = '';
+
+sub report {
+ if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) {
+ return 0;
+ }
+ my $line = $prefix . $_[0];
+
+ $line = (split('\n', $line))[0] . "\n" if ($terse);
+
+ push(our @report, $line);
+
+ return 1;
+}
+sub report_dump {
+ our @report;
+}
+sub ERROR {
+ if (report("ERROR: $_[0]\n")) {
+ our $clean = 0;
+ our $cnt_error++;
+ }
+}
+sub WARN {
+ if (report("WARNING: $_[0]\n")) {
+ our $clean = 0;
+ our $cnt_warn++;
+ }
+}
+sub CHK {
+ if ($check && report("CHECK: $_[0]\n")) {
+ our $clean = 0;
+ our $cnt_chk++;
+ }
+}
+
+sub check_absolute_file {
+ my ($absolute, $herecurr) = @_;
+ my $file = $absolute;
+
+ ##print "absolute<$absolute>\n";
+
+ # See if any suffix of this path is a path within the tree.
+ while ($file =~ s@^[^/]*/@@) {
+ if (-f "$root/$file") {
+ ##print "file<$file>\n";
+ last;
+ }
+ }
+ if (! -f _) {
+ return 0;
+ }
+
+ # It is, so see if the prefix is acceptable.
+ my $prefix = $absolute;
+ substr($prefix, -length($file)) = '';
+
+ ##print "prefix<$prefix>\n";
+ if ($prefix ne ".../") {
+ WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr);
+ }
+}
+
+sub process {
+ my $filename = shift;
+
+ my $linenr=0;
+ my $prevline="";
+ my $prevrawline="";
+ my $stashline="";
+ my $stashrawline="";
+
+ my $length;
+ my $indent;
+ my $previndent=0;
+ my $stashindent=0;
+
+ our $clean = 1;
+ my $signoff = 0;
+ my $is_patch = 0;
+
+ our @report = ();
+ our $cnt_lines = 0;
+ our $cnt_error = 0;
+ our $cnt_warn = 0;
+ our $cnt_chk = 0;
+
+ # Trace the real file/line as we go.
+ my $realfile = '';
+ my $realline = 0;
+ my $realcnt = 0;
+ my $here = '';
+ my $in_comment = 0;
+ my $comment_edge = 0;
+ my $first_line = 0;
+ my $p1_prefix = '';
+
+ my $prev_values = 'E';
+
+ # suppression flags
+ my %suppress_ifbraces;
+ my %suppress_whiletrailers;
+ my %suppress_export;
+
+ # Pre-scan the patch sanitizing the lines.
+ # Pre-scan the patch looking for any __setup documentation.
+ #
+ my @setup_docs = ();
+ my $setup_docs = 0;
+
+ sanitise_line_reset();
+ my $line;
+ foreach my $rawline (@rawlines) {
+ $linenr++;
+ $line = $rawline;
+
+ if ($rawline=~/^\+\+\+\s+(\S+)/) {
+ $setup_docs = 0;
+ if ($1 =~ m@Documentation/kernel-parameters.txt$@) {
+ $setup_docs = 1;
+ }
+ #next;
+ }
+ if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+ $realline=$1-1;
+ if (defined $2) {
+ $realcnt=$3+1;
+ } else {
+ $realcnt=1+1;
+ }
+ $in_comment = 0;
+
+ # Guestimate if this is a continuing comment. Run
+ # the context looking for a comment "edge". If this
+ # edge is a close comment then we must be in a comment
+ # at context start.
+ my $edge;
+ my $cnt = $realcnt;
+ for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
+ next if (defined $rawlines[$ln - 1] &&
+ $rawlines[$ln - 1] =~ /^-/);
+ $cnt--;
+ #print "RAW<$rawlines[$ln - 1]>\n";
+ last if (!defined $rawlines[$ln - 1]);
+ if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
+ $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
+ ($edge) = $1;
+ last;
+ }
+ }
+ if (defined $edge && $edge eq '*/') {
+ $in_comment = 1;
+ }
+
+ # Guestimate if this is a continuing comment. If this
+ # is the start of a diff block and this line starts
+ # ' *' then it is very likely a comment.
+ if (!defined $edge &&
+ $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
+ {
+ $in_comment = 1;
+ }
+
+ ##print "COMMENT:$in_comment edge<$edge> $rawline\n";
+ sanitise_line_reset($in_comment);
+
+ } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) {
+ # Standardise the strings and chars within the input to
+ # simplify matching -- only bother with positive lines.
+ $line = sanitise_line($rawline);
+ }
+ push(@lines, $line);
+
+ if ($realcnt > 1) {
+ $realcnt-- if ($line =~ /^(?:\+| |$)/);
+ } else {
+ $realcnt = 0;
+ }
+
+ #print "==>$rawline\n";
+ #print "-->$line\n";
+
+ if ($setup_docs && $line =~ /^\+/) {
+ push(@setup_docs, $line);
+ }
+ }
+
+ $prefix = '';
+
+ $realcnt = 0;
+ $linenr = 0;
+ foreach my $line (@lines) {
+ $linenr++;
+
+ my $rawline = $rawlines[$linenr - 1];
+
+#extract the line range in the file after the patch is applied
+ if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+ $is_patch = 1;
+ $first_line = $linenr + 1;
+ $realline=$1-1;
+ if (defined $2) {
+ $realcnt=$3+1;
+ } else {
+ $realcnt=1+1;
+ }
+ annotate_reset();
+ $prev_values = 'E';
+
+ %suppress_ifbraces = ();
+ %suppress_whiletrailers = ();
+ %suppress_export = ();
+ next;
+
+# track the line number as we move through the hunk, note that
+# new versions of GNU diff omit the leading space on completely
+# blank context lines so we need to count that too.
+ } elsif ($line =~ /^( |\+|$)/) {
+ $realline++;
+ $realcnt-- if ($realcnt != 0);
+
+ # Measure the line length and indent.
+ ($length, $indent) = line_stats($rawline);
+
+ # Track the previous line.
+ ($prevline, $stashline) = ($stashline, $line);
+ ($previndent, $stashindent) = ($stashindent, $indent);
+ ($prevrawline, $stashrawline) = ($stashrawline, $rawline);
+
+ #warn "line<$line>\n";
+
+ } elsif ($realcnt == 1) {
+ $realcnt--;
+ }
+
+ my $hunk_line = ($realcnt != 0);
+
+#make up the handle for any error we report on this line
+ $prefix = "$filename:$realline: " if ($emacs && $file);
+ $prefix = "$filename:$linenr: " if ($emacs && !$file);
+
+ $here = "#$linenr: " if (!$file);
+ $here = "#$realline: " if ($file);
+
+ # extract the filename as it passes
+ if ($line =~ /^diff --git.*?(\S+)$/) {
+ $realfile = $1;
+ $realfile =~ s@^([^/]*)/@@;
+
+ } elsif ($line =~ /^\+\+\+\s+(\S+)/) {
+ $realfile = $1;
+ $realfile =~ s@^([^/]*)/@@;
+
+ $p1_prefix = $1;
+ if (!$file && $tree && $p1_prefix ne '' &&
+ -e "$root/$p1_prefix") {
+ WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
+ }
+
+ if ($realfile =~ m@^include/asm/@) {
+ ERROR("do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
+ }
+ next;
+ }
+
+ $here .= "FILE: $realfile:$realline:" if ($realcnt != 0);
+
+ my $hereline = "$here\n$rawline\n";
+ my $herecurr = "$here\n$rawline\n";
+ my $hereprev = "$here\n$prevrawline\n$rawline\n";
+
+ $cnt_lines++ if ($realcnt != 0);
+
+# Check for incorrect file permissions
+ if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
+ my $permhere = $here . "FILE: $realfile\n";
+ if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) {
+ ERROR("do not set execute permissions for source files\n" . $permhere);
+ }
+ }
+
+#check the patch for a signoff:
+ if ($line =~ /^\s*signed-off-by:/i) {
+ # This is a signoff, if ugly, so do not double report.
+ $signoff++;
+ if (!($line =~ /^\s*Signed-off-by:/)) {
+ WARN("Signed-off-by: is the preferred form\n" .
+ $herecurr);
+ }
+ if ($line =~ /^\s*signed-off-by:\S/i) {
+ WARN("space required after Signed-off-by:\n" .
+ $herecurr);
+ }
+ }
+
+# Check for wrappage within a valid hunk of the file
+ if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
+ ERROR("patch seems to be corrupt (line wrapped?)\n" .
+ $herecurr) if (!$emitted_corrupt++);
+ }
+
+# Check for absolute kernel paths.
+ if ($tree) {
+ while ($line =~ m{(?:^|\s)(/\S*)}g) {
+ my $file = $1;
+
+ if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
+ check_absolute_file($1, $herecurr)) {
+ #
+ } else {
+ check_absolute_file($file, $herecurr);
+ }
+ }
+ }
+
+# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
+ if (($realfile =~ /^$/ || $line =~ /^\+/) &&
+ $rawline !~ m/^$UTF8*$/) {
+ my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/);
+
+ my $blank = copy_spacing($rawline);
+ my $ptr = substr($blank, 0, length($utf8_prefix)) . "^";
+ my $hereptr = "$hereline$ptr\n";
+
+ ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
+ }
+
+# ignore non-hunk lines and lines being removed
+ next if (!$hunk_line || $line =~ /^-/);
+
+#trailing whitespace
+ if ($line =~ /^\+.*\015/) {
+ my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+ ERROR("DOS line endings\n" . $herevet);
+
+ } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) {
+ my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+ ERROR("trailing whitespace\n" . $herevet);
+ $rpt_cleaners = 1;
+ }
+
+# check for Kconfig help text having a real description
+# Only applies when adding the entry originally, after that we do not have
+# sufficient context to determine whether it is indeed long enough.
+ if ($realfile =~ /Kconfig/ &&
+ $line =~ /\+\s*(?:---)?help(?:---)?$/) {
+ my $length = 0;
+ my $cnt = $realcnt;
+ my $ln = $linenr + 1;
+ my $f;
+ my $is_end = 0;
+ while ($cnt > 0 && defined $lines[$ln - 1]) {
+ $f = $lines[$ln - 1];
+ $cnt-- if ($lines[$ln - 1] !~ /^-/);
+ $is_end = $lines[$ln - 1] =~ /^\+/;
+ $ln++;
+
+ next if ($f =~ /^-/);
+ $f =~ s/^.//;
+ $f =~ s/#.*//;
+ $f =~ s/^\s+//;
+ next if ($f =~ /^$/);
+ if ($f =~ /^\s*config\s/) {
+ $is_end = 1;
+ last;
+ }
+ $length++;
+ }
+ WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_end && $length < 4);
+ #print "is_end<$is_end> length<$length>\n";
+ }
+
+# check we are in a valid source file if not then ignore this hunk
+ next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/);
+
+#80 column limit
+ if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ &&
+ $rawline !~ /^.\s*\*\s*\@$Ident\s/ &&
+ !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ ||
+ $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) &&
+ $length > 80)
+ {
+ WARN("line over 80 characters\n" . $herecurr);
+ }
+
+# check for spaces before a quoted newline
+ if ($rawline =~ /^.*\".*\s\\n/) {
+ WARN("unnecessary whitespace before a quoted newline\n" . $herecurr);
+ }
+
+# check for adding lines without a newline.
+ if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
+ WARN("adding a line without newline at end of file\n" . $herecurr);
+ }
+
+# Blackfin: use hi/lo macros
+ if ($realfile =~ m@arch/blackfin/.*\.S$@) {
+ if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) {
+ my $herevet = "$here\n" . cat_vet($line) . "\n";
+ ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet);
+ }
+ if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) {
+ my $herevet = "$here\n" . cat_vet($line) . "\n";
+ ERROR("use the HI() macro, not (... >> 16)\n" . $herevet);
+ }
+ }
+
+# check we are in a valid source file C or perl if not then ignore this hunk
+ next if ($realfile !~ /\.(h|c|pl)$/);
+
+# in QEMU, no tabs are allowed
+ if ($rawline =~ /\t/) {
+ my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+ ERROR("code indent should never use tabs\n" . $herevet);
+ $rpt_cleaners = 1;
+ }
+
+# check we are in a valid C source file if not then ignore this hunk
+ next if ($realfile !~ /\.(h|c)$/);
+
+# check for RCS/CVS revision markers
+ if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
+ WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr);
+ }
+
+# Blackfin: don't use __builtin_bfin_[cs]sync
+ if ($line =~ /__builtin_bfin_csync/) {
+ my $herevet = "$here\n" . cat_vet($line) . "\n";
+ ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet);
+ }
+ if ($line =~ /__builtin_bfin_ssync/) {
+ my $herevet = "$here\n" . cat_vet($line) . "\n";
+ ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet);
+ }
+
+# Check for potential 'bare' types
+ my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
+ $realline_next);
+ if ($realcnt && $line =~ /.\s*\S/) {
+ ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+ ctx_statement_block($linenr, $realcnt, 0);
+ $stat =~ s/\n./\n /g;
+ $cond =~ s/\n./\n /g;
+
+ # Find the real next line.
+ $realline_next = $line_nr_next;
+ if (defined $realline_next &&
+ (!defined $lines[$realline_next - 1] ||
+ substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) {
+ $realline_next++;
+ }
+
+ my $s = $stat;
+ $s =~ s/{.*$//s;
+
+ # Ignore goto labels.
+ if ($s =~ /$Ident:\*$/s) {
+
+ # Ignore functions being called
+ } elsif ($s =~ /^.\s*$Ident\s*\(/s) {
+
+ } elsif ($s =~ /^.\s*else\b/s) {
+
+ # declarations always start with types
+ } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
+ my $type = $1;
+ $type =~ s/\s+/ /g;
+ possible($type, "A:" . $s);
+
+ # definitions in global scope can only start with types
+ } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) {
+ possible($1, "B:" . $s);
+ }
+
+ # any (foo ... *) is a pointer cast, and foo is a type
+ while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) {
+ possible($1, "C:" . $s);
+ }
+
+ # Check for any sort of function declaration.
+ # int foo(something bar, other baz);
+ # void (*store_gdt)(x86_descr_ptr *);
+ if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) {
+ my ($name_len) = length($1);
+
+ my $ctx = $s;
+ substr($ctx, 0, $name_len + 1, '');
+ $ctx =~ s/\)[^\)]*$//;
+
+ for my $arg (split(/\s*,\s*/, $ctx)) {
+ if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) {
+
+ possible($1, "D:" . $s);
+ }
+ }
+ }
+
+ }
+
+#
+# Checks which may be anchored in the context.
+#
+
+# Check for switch () and associated case and default
+# statements should be at the same indent.
+ if ($line=~/\bswitch\s*\(.*\)/) {
+ my $err = '';
+ my $sep = '';
+ my @ctx = ctx_block_outer($linenr, $realcnt);
+ shift(@ctx);
+ for my $ctx (@ctx) {
+ my ($clen, $cindent) = line_stats($ctx);
+ if ($ctx =~ /^\+\s*(case\s+|default:)/ &&
+ $indent != $cindent) {
+ $err .= "$sep$ctx\n";
+ $sep = '';
+ } else {
+ $sep = "[...]\n";
+ }
+ }
+ if ($err ne '') {
+ ERROR("switch and case should be at the same indent\n$hereline$err");
+ }
+ }
+
+# if/while/etc brace do not go on next line, unless defining a do while loop,
+# or if that brace on the next line is for something else
+ if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
+ my $pre_ctx = "$1$2";
+
+ my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
+ my $ctx_cnt = $realcnt - $#ctx - 1;
+ my $ctx = join("\n", @ctx);
+
+ my $ctx_ln = $linenr;
+ my $ctx_skip = $realcnt;
+
+ while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt &&
+ defined $lines[$ctx_ln - 1] &&
+ $lines[$ctx_ln - 1] =~ /^-/)) {
+ ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n";
+ $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/);
+ $ctx_ln++;
+ }
+
+ #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
+ #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
+
+ if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) {
+ ERROR("that open brace { should be on the previous line\n" .
+ "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+ }
+ if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ &&
+ $ctx =~ /\)\s*\;\s*$/ &&
+ defined $lines[$ctx_ln - 1])
+ {
+ my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]);
+ if ($nindent > $indent) {
+ WARN("trailing semicolon indicates no statements, indent implies otherwise\n" .
+ "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+ }
+ }
+ }
+
+# Check relative indent for conditionals and blocks.
+ if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+ my ($s, $c) = ($stat, $cond);
+
+ substr($s, 0, length($c), '');
+
+ # Make sure we remove the line prefixes as we have
+ # none on the first line, and are going to readd them
+ # where necessary.
+ $s =~ s/\n./\n/gs;
+
+ # Find out how long the conditional actually is.
+ my @newlines = ($c =~ /\n/gs);
+ my $cond_lines = 1 + $#newlines;
+
+ # We want to check the first line inside the block
+ # starting at the end of the conditional, so remove:
+ # 1) any blank line termination
+ # 2) any opening brace { on end of the line
+ # 3) any do (...) {
+ my $continuation = 0;
+ my $check = 0;
+ $s =~ s/^.*\bdo\b//;
+ $s =~ s/^\s*{//;
+ if ($s =~ s/^\s*\\//) {
+ $continuation = 1;
+ }
+ if ($s =~ s/^\s*?\n//) {
+ $check = 1;
+ $cond_lines++;
+ }
+
+ # Also ignore a loop construct at the end of a
+ # preprocessor statement.
+ if (($prevline =~ /^.\s*#\s*define\s/ ||
+ $prevline =~ /\\\s*$/) && $continuation == 0) {
+ $check = 0;
+ }
+
+ my $cond_ptr = -1;
+ $continuation = 0;
+ while ($cond_ptr != $cond_lines) {
+ $cond_ptr = $cond_lines;
+
+ # If we see an #else/#elif then the code
+ # is not linear.
+ if ($s =~ /^\s*\#\s*(?:else|elif)/) {
+ $check = 0;
+ }
+
+ # Ignore:
+ # 1) blank lines, they should be at 0,
+ # 2) preprocessor lines, and
+ # 3) labels.
+ if ($continuation ||
+ $s =~ /^\s*?\n/ ||
+ $s =~ /^\s*#\s*?/ ||
+ $s =~ /^\s*$Ident\s*:/) {
+ $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
+ if ($s =~ s/^.*?\n//) {
+ $cond_lines++;
+ }
+ }
+ }
+
+ my (undef, $sindent) = line_stats("+" . $s);
+ my $stat_real = raw_line($linenr, $cond_lines);
+
+ # Check if either of these lines are modified, else
+ # this is not this patch's fault.
+ if (!defined($stat_real) ||
+ $stat !~ /^\+/ && $stat_real !~ /^\+/) {
+ $check = 0;
+ }
+ if (defined($stat_real) && $cond_lines > 1) {
+ $stat_real = "[...]\n$stat_real";
+ }
+
+ #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
+
+ if ($check && (($sindent % 4) != 0 ||
+ ($sindent <= $indent && $s ne ''))) {
+ WARN("suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
+ }
+ }
+
+ # Track the 'values' across context and added lines.
+ my $opline = $line; $opline =~ s/^./ /;
+ my ($curr_values, $curr_vars) =
+ annotate_values($opline . "\n", $prev_values);
+ $curr_values = $prev_values . $curr_values;
+ if ($dbg_values) {
+ my $outline = $opline; $outline =~ s/\t/ /g;
+ print "$linenr > .$outline\n";
+ print "$linenr > $curr_values\n";
+ print "$linenr > $curr_vars\n";
+ }
+ $prev_values = substr($curr_values, -1);
+
+#ignore lines not being added
+ if ($line=~/^[^\+]/) {next;}
+
+# TEST: allow direct testing of the type matcher.
+ if ($dbg_type) {
+ if ($line =~ /^.\s*$Declare\s*$/) {
+ ERROR("TEST: is type\n" . $herecurr);
+ } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) {
+ ERROR("TEST: is not type ($1 is)\n". $herecurr);
+ }
+ next;
+ }
+# TEST: allow direct testing of the attribute matcher.
+ if ($dbg_attr) {
+ if ($line =~ /^.\s*$Modifier\s*$/) {
+ ERROR("TEST: is attr\n" . $herecurr);
+ } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) {
+ ERROR("TEST: is not attr ($1 is)\n". $herecurr);
+ }
+ next;
+ }
+
+# check for initialisation to aggregates open brace on the next line
+ if ($line =~ /^.\s*{/ &&
+ $prevline =~ /(?:^|[^=])=\s*$/) {
+ ERROR("that open brace { should be on the previous line\n" . $hereprev);
+ }
+
+#
+# Checks which are anchored on the added line.
+#
+
+# check for malformed paths in #include statements (uses RAW line)
+ if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) {
+ my $path = $1;
+ if ($path =~ m{//}) {
+ ERROR("malformed #include filename\n" .
+ $herecurr);
+ }
+ }
+
+# no C99 // comments
+ if ($line =~ m{//}) {
+ ERROR("do not use C99 // comments\n" . $herecurr);
+ }
+ # Remove C99 comments.
+ $line =~ s@//.*@@;
+ $opline =~ s@//.*@@;
+
+# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider
+# the whole statement.
+#print "APW <$lines[$realline_next - 1]>\n";
+ if (defined $realline_next &&
+ exists $lines[$realline_next - 1] &&
+ !defined $suppress_export{$realline_next} &&
+ ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+ $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+ # Handle definitions which produce identifiers with
+ # a prefix:
+ # XXX(foo);
+ # EXPORT_SYMBOL(something_foo);
+ my $name = $1;
+ if ($stat =~ /^.([A-Z_]+)\s*\(\s*($Ident)/ &&
+ $name =~ /^${Ident}_$2/) {
+#print "FOO C name<$name>\n";
+ $suppress_export{$realline_next} = 1;
+
+ } elsif ($stat !~ /(?:
+ \n.}\s*$|
+ ^.DEFINE_$Ident\(\Q$name\E\)|
+ ^.DECLARE_$Ident\(\Q$name\E\)|
+ ^.LIST_HEAD\(\Q$name\E\)|
+ ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(|
+ \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\()
+ )/x) {
+#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n";
+ $suppress_export{$realline_next} = 2;
+ } else {
+ $suppress_export{$realline_next} = 1;
+ }
+ }
+ if (!defined $suppress_export{$linenr} &&
+ $prevline =~ /^.\s*$/ &&
+ ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+ $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+#print "FOO B <$lines[$linenr - 1]>\n";
+ $suppress_export{$linenr} = 2;
+ }
+ if (defined $suppress_export{$linenr} &&
+ $suppress_export{$linenr} == 2) {
+ WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr);
+ }
+
+# check for global initialisers.
+ if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) {
+ ERROR("do not initialise globals to 0 or NULL\n" .
+ $herecurr);
+ }
+# check for static initialisers.
+ if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) {
+ ERROR("do not initialise statics to 0 or NULL\n" .
+ $herecurr);
+ }
+
+# * goes on variable not on type
+ # (char*[ const])
+ if ($line =~ m{\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\)}) {
+ my ($from, $to) = ($1, $1);
+
+ # Should start with a space.
+ $to =~ s/^(\S)/ $1/;
+ # Should not end with a space.
+ $to =~ s/\s+$//;
+ # '*'s should not have spaces between.
+ while ($to =~ s/\*\s+\*/\*\*/) {
+ }
+
+ #print "from<$from> to<$to>\n";
+ if ($from ne $to) {
+ ERROR("\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr);
+ }
+ } elsif ($line =~ m{\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident)}) {
+ my ($from, $to, $ident) = ($1, $1, $2);
+
+ # Should start with a space.
+ $to =~ s/^(\S)/ $1/;
+ # Should not end with a space.
+ $to =~ s/\s+$//;
+ # '*'s should not have spaces between.
+ while ($to =~ s/\*\s+\*/\*\*/) {
+ }
+ # Modifiers should have spaces.
+ $to =~ s/(\b$Modifier$)/$1 /;
+
+ #print "from<$from> to<$to> ident<$ident>\n";
+ if ($from ne $to && $ident !~ /^$Modifier$/) {
+ ERROR("\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr);
+ }
+ }
+
+# # no BUG() or BUG_ON()
+# if ($line =~ /\b(BUG|BUG_ON)\b/) {
+# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n";
+# print "$herecurr";
+# $clean = 0;
+# }
+
+ if ($line =~ /\bLINUX_VERSION_CODE\b/) {
+ WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr);
+ }
+
+# printk should use KERN_* levels. Note that follow on printk's on the
+# same line do not need a level, so we use the current block context
+# to try and find and validate the current printk. In summary the current
+# printk includes all preceeding printk's which have no newline on the end.
+# we assume the first bad printk is the one to report.
+ if ($line =~ /\bprintk\((?!KERN_)\s*"/) {
+ my $ok = 0;
+ for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) {
+ #print "CHECK<$lines[$ln - 1]\n";
+ # we have a preceeding printk if it ends
+ # with "\n" ignore it, else it is to blame
+ if ($lines[$ln - 1] =~ m{\bprintk\(}) {
+ if ($rawlines[$ln - 1] !~ m{\\n"}) {
+ $ok = 1;
+ }
+ last;
+ }
+ }
+ if ($ok == 0) {
+ WARN("printk() should include KERN_ facility level\n" . $herecurr);
+ }
+ }
+
+# function brace can't be on same line, except for #defines of do while,
+# or if closed on same line
+ if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and
+ !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) {
+ ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr);
+ }
+
+# open braces for enum, union and struct go on the same line.
+ if ($line =~ /^.\s*{/ &&
+ $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) {
+ ERROR("open brace '{' following $1 go on the same line\n" . $hereprev);
+ }
+
+# missing space after union, struct or enum definition
+ if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) {
+ WARN("missing space after $1 definition\n" . $herecurr);
+ }
+
+# check for spacing round square brackets; allowed:
+# 1. with a type on the left -- int [] a;
+# 2. at the beginning of a line for slice initialisers -- [0...10] = 5,
+# 3. inside a curly brace -- = { [0...10] = 5 }
+ while ($line =~ /(.*?\s)\[/g) {
+ my ($where, $prefix) = ($-[1], $1);
+ if ($prefix !~ /$Type\s+$/ &&
+ ($where != 0 || $prefix !~ /^.\s+$/) &&
+ $prefix !~ /{\s+$/) {
+ ERROR("space prohibited before open square bracket '['\n" . $herecurr);
+ }
+ }
+
+# check for spaces between functions and their parentheses.
+ while ($line =~ /($Ident)\s+\(/g) {
+ my $name = $1;
+ my $ctx_before = substr($line, 0, $-[1]);
+ my $ctx = "$ctx_before$name";
+
+ # Ignore those directives where spaces _are_ permitted.
+ if ($name =~ /^(?:
+ if|for|while|switch|return|case|
+ volatile|__volatile__|
+ __attribute__|format|__extension__|
+ asm|__asm__)$/x)
+ {
+
+ # cpp #define statements have non-optional spaces, ie
+ # if there is a space between the name and the open
+ # parenthesis it is simply not a parameter group.
+ } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) {
+
+ # cpp #elif statement condition may start with a (
+ } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) {
+
+ # If this whole things ends with a type its most
+ # likely a typedef for a function.
+ } elsif ($ctx =~ /$Type$/) {
+
+ } else {
+ WARN("space prohibited between function name and open parenthesis '('\n" . $herecurr);
+ }
+ }
+# Check operator spacing.
+ if (!($line=~/\#\s*include/)) {
+ my $ops = qr{
+ <<=|>>=|<=|>=|==|!=|
+ \+=|-=|\*=|\/=|%=|\^=|\|=|&=|
+ =>|->|<<|>>|<|>|=|!|~|
+ &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
+ \?|:
+ }x;
+ my @elements = split(/($ops|;)/, $opline);
+ my $off = 0;
+
+ my $blank = copy_spacing($opline);
+
+ for (my $n = 0; $n < $#elements; $n += 2) {
+ $off += length($elements[$n]);
+
+ # Pick up the preceeding and succeeding characters.
+ my $ca = substr($opline, 0, $off);
+ my $cc = '';
+ if (length($opline) >= ($off + length($elements[$n + 1]))) {
+ $cc = substr($opline, $off + length($elements[$n + 1]));
+ }
+ my $cb = "$ca$;$cc";
+
+ my $a = '';
+ $a = 'V' if ($elements[$n] ne '');
+ $a = 'W' if ($elements[$n] =~ /\s$/);
+ $a = 'C' if ($elements[$n] =~ /$;$/);
+ $a = 'B' if ($elements[$n] =~ /(\[|\()$/);
+ $a = 'O' if ($elements[$n] eq '');
+ $a = 'E' if ($ca =~ /^\s*$/);
+
+ my $op = $elements[$n + 1];
+
+ my $c = '';
+ if (defined $elements[$n + 2]) {
+ $c = 'V' if ($elements[$n + 2] ne '');
+ $c = 'W' if ($elements[$n + 2] =~ /^\s/);
+ $c = 'C' if ($elements[$n + 2] =~ /^$;/);
+ $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/);
+ $c = 'O' if ($elements[$n + 2] eq '');
+ $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/);
+ } else {
+ $c = 'E';
+ }
+
+ my $ctx = "${a}x${c}";
+
+ my $at = "(ctx:$ctx)";
+
+ my $ptr = substr($blank, 0, $off) . "^";
+ my $hereptr = "$hereline$ptr\n";
+
+ # Pull out the value of this operator.
+ my $op_type = substr($curr_values, $off + 1, 1);
+
+ # Get the full operator variant.
+ my $opv = $op . substr($curr_vars, $off, 1);
+
+ # Ignore operators passed as parameters.
+ if ($op_type ne 'V' &&
+ $ca =~ /\s$/ && $cc =~ /^\s*,/) {
+
+# # Ignore comments
+# } elsif ($op =~ /^$;+$/) {
+
+ # ; should have either the end of line or a space or \ after it
+ } elsif ($op eq ';') {
+ if ($ctx !~ /.x[WEBC]/ &&
+ $cc !~ /^\\/ && $cc !~ /^;/) {
+ ERROR("space required after that '$op' $at\n" . $hereptr);
+ }
+
+ # // is a comment
+ } elsif ($op eq '//') {
+
+ # No spaces for:
+ # ->
+ # : when part of a bitfield
+ } elsif ($op eq '->' || $opv eq ':B') {
+ if ($ctx =~ /Wx.|.xW/) {
+ ERROR("spaces prohibited around that '$op' $at\n" . $hereptr);
+ }
+
+ # , must have a space on the right.
+ } elsif ($op eq ',') {
+ if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) {
+ ERROR("space required after that '$op' $at\n" . $hereptr);
+ }
+
+ # '*' as part of a type definition -- reported already.
+ } elsif ($opv eq '*_') {
+ #warn "'*' is part of type\n";
+
+ # unary operators should have a space before and
+ # none after. May be left adjacent to another
+ # unary operator, or a cast
+ } elsif ($op eq '!' || $op eq '~' ||
+ $opv eq '*U' || $opv eq '-U' ||
+ $opv eq '&U' || $opv eq '&&U') {
+ if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) {
+ ERROR("space required before that '$op' $at\n" . $hereptr);
+ }
+ if ($op eq '*' && $cc =~/\s*$Modifier\b/) {
+ # A unary '*' may be const
+
+ } elsif ($ctx =~ /.xW/) {
+ ERROR("space prohibited after that '$op' $at\n" . $hereptr);
+ }
+
+ # unary ++ and unary -- are allowed no space on one side.
+ } elsif ($op eq '++' or $op eq '--') {
+ if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) {
+ ERROR("space required one side of that '$op' $at\n" . $hereptr);
+ }
+ if ($ctx =~ /Wx[BE]/ ||
+ ($ctx =~ /Wx./ && $cc =~ /^;/)) {
+ ERROR("space prohibited before that '$op' $at\n" . $hereptr);
+ }
+ if ($ctx =~ /ExW/) {
+ ERROR("space prohibited after that '$op' $at\n" . $hereptr);
+ }
+
+
+ # << and >> may either have or not have spaces both sides
+ } elsif ($op eq '<<' or $op eq '>>' or
+ $op eq '&' or $op eq '^' or $op eq '|' or
+ $op eq '+' or $op eq '-' or
+ $op eq '*' or $op eq '/' or
+ $op eq '%')
+ {
+ if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) {
+ ERROR("need consistent spacing around '$op' $at\n" .
+ $hereptr);
+ }
+
+ # A colon needs no spaces before when it is
+ # terminating a case value or a label.
+ } elsif ($opv eq ':C' || $opv eq ':L') {
+ if ($ctx =~ /Wx./) {
+ ERROR("space prohibited before that '$op' $at\n" . $hereptr);
+ }
+
+ # All the others need spaces both sides.
+ } elsif ($ctx !~ /[EWC]x[CWE]/) {
+ my $ok = 0;
+
+ # Ignore email addresses <foo@bar>
+ if (($op eq '<' &&
+ $cc =~ /^\S+\@\S+>/) ||
+ ($op eq '>' &&
+ $ca =~ /<\S+\@\S+$/))
+ {
+ $ok = 1;
+ }
+
+ # Ignore ?:
+ if (($opv eq ':O' && $ca =~ /\?$/) ||
+ ($op eq '?' && $cc =~ /^:/)) {
+ $ok = 1;
+ }
+
+ if ($ok == 0) {
+ ERROR("spaces required around that '$op' $at\n" . $hereptr);
+ }
+ }
+ $off += length($elements[$n + 1]);
+ }
+ }
+
+# check for multiple assignments
+ if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) {
+ CHK("multiple assignments should be avoided\n" . $herecurr);
+ }
+
+## # check for multiple declarations, allowing for a function declaration
+## # continuation.
+## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ &&
+## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
+##
+## # Remove any bracketed sections to ensure we do not
+## # falsly report the parameters of functions.
+## my $ln = $line;
+## while ($ln =~ s/\([^\(\)]*\)//g) {
+## }
+## if ($ln =~ /,/) {
+## WARN("declaring multiple variables together should be avoided\n" . $herecurr);
+## }
+## }
+
+#need space before brace following if, while, etc
+ if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) ||
+ $line =~ /do{/) {
+ ERROR("space required before the open brace '{'\n" . $herecurr);
+ }
+
+# closing brace should have a space following it when it has anything
+# on the line
+ if ($line =~ /}(?!(?:,|;|\)))\S/) {
+ ERROR("space required after that close brace '}'\n" . $herecurr);
+ }
+
+# check spacing on square brackets
+ if ($line =~ /\[\s/ && $line !~ /\[\s*$/) {
+ ERROR("space prohibited after that open square bracket '['\n" . $herecurr);
+ }
+ if ($line =~ /\s\]/) {
+ ERROR("space prohibited before that close square bracket ']'\n" . $herecurr);
+ }
+
+# check spacing on parentheses
+ if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ &&
+ $line !~ /for\s*\(\s+;/) {
+ ERROR("space prohibited after that open parenthesis '('\n" . $herecurr);
+ }
+ if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ &&
+ $line !~ /for\s*\(.*;\s+\)/ &&
+ $line !~ /:\s+\)/) {
+ ERROR("space prohibited before that close parenthesis ')'\n" . $herecurr);
+ }
+
+#goto labels aren't indented, allow a single space however
+ if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and
+ !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) {
+ WARN("labels should not be indented\n" . $herecurr);
+ }
+
+# Return is not a function.
+ if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) {
+ my $spacing = $1;
+ my $value = $2;
+
+ # Flatten any parentheses
+ $value =~ s/\(/ \(/g;
+ $value =~ s/\)/\) /g;
+ while ($value =~ s/\[[^\{\}]*\]/1/ ||
+ $value !~ /(?:$Ident|-?$Constant)\s*
+ $Compare\s*
+ (?:$Ident|-?$Constant)/x &&
+ $value =~ s/\([^\(\)]*\)/1/) {
+ }
+#print "value<$value>\n";
+ if ($value =~ /^\s*(?:$Ident|-?$Constant)\s*$/) {
+ ERROR("return is not a function, parentheses are not required\n" . $herecurr);
+
+ } elsif ($spacing !~ /\s+/) {
+ ERROR("space required before the open parenthesis '('\n" . $herecurr);
+ }
+ }
+# Return of what appears to be an errno should normally be -'ve
+ if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) {
+ my $name = $1;
+ if ($name ne 'EOF' && $name ne 'ERROR') {
+ CHK("return of an errno should typically be -ve (return -$1)\n" . $herecurr);
+ }
+ }
+
+# Need a space before open parenthesis after if, while etc
+ if ($line=~/\b(if|while|for|switch)\(/) {
+ ERROR("space required before the open parenthesis '('\n" . $herecurr);
+ }
+
+# Check for illegal assignment in if conditional -- and check for trailing
+# statements after the conditional.
+ if ($line =~ /do\s*(?!{)/) {
+ my ($stat_next) = ctx_statement_block($line_nr_next,
+ $remain_next, $off_next);
+ $stat_next =~ s/\n./\n /g;
+ ##print "stat<$stat> stat_next<$stat_next>\n";
+
+ if ($stat_next =~ /^\s*while\b/) {
+ # If the statement carries leading newlines,
+ # then count those as offsets.
+ my ($whitespace) =
+ ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s);
+ my $offset =
+ statement_rawlines($whitespace) - 1;
+
+ $suppress_whiletrailers{$line_nr_next +
+ $offset} = 1;
+ }
+ }
+ if (!defined $suppress_whiletrailers{$linenr} &&
+ $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
+ my ($s, $c) = ($stat, $cond);
+
+ if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
+ ERROR("do not use assignment in if condition\n" . $herecurr);
+ }
+
+ # Find out what is on the end of the line after the
+ # conditional.
+ substr($s, 0, length($c), '');
+ $s =~ s/\n.*//g;
+ $s =~ s/$;//g; # Remove any comments
+ if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
+ $c !~ /}\s*while\s*/)
+ {
+ # Find out how long the conditional actually is.
+ my @newlines = ($c =~ /\n/gs);
+ my $cond_lines = 1 + $#newlines;
+ my $stat_real = '';
+
+ $stat_real = raw_line($linenr, $cond_lines)
+ . "\n" if ($cond_lines);
+ if (defined($stat_real) && $cond_lines > 1) {
+ $stat_real = "[...]\n$stat_real";
+ }
+
+ ERROR("trailing statements should be on next line\n" . $herecurr . $stat_real);
+ }
+ }
+
+# Check for bitwise tests written as boolean
+ if ($line =~ /
+ (?:
+ (?:\[|\(|\&\&|\|\|)
+ \s*0[xX][0-9]+\s*
+ (?:\&\&|\|\|)
+ |
+ (?:\&\&|\|\|)
+ \s*0[xX][0-9]+\s*
+ (?:\&\&|\|\||\)|\])
+ )/x)
+ {
+ WARN("boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr);
+ }
+
+# if and else should not have general statements after it
+ if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
+ my $s = $1;
+ $s =~ s/$;//g; # Remove any comments
+ if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
+ ERROR("trailing statements should be on next line\n" . $herecurr);
+ }
+ }
+# if should not continue a brace
+ if ($line =~ /}\s*if\b/) {
+ ERROR("trailing statements should be on next line\n" .
+ $herecurr);
+ }
+# case and default should not have general statements after them
+ if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g &&
+ $line !~ /\G(?:
+ (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$|
+ \s*return\s+
+ )/xg)
+ {
+ ERROR("trailing statements should be on next line\n" . $herecurr);
+ }
+
+ # Check for }<nl>else {, these must be at the same
+ # indent level to be relevant to each other.
+ if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and
+ $previndent == $indent) {
+ ERROR("else should follow close brace '}'\n" . $hereprev);
+ }
+
+ if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and
+ $previndent == $indent) {
+ my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0);
+
+ # Find out what is on the end of the line after the
+ # conditional.
+ substr($s, 0, length($c), '');
+ $s =~ s/\n.*//g;
+
+ if ($s =~ /^\s*;/) {
+ ERROR("while should follow close brace '}'\n" . $hereprev);
+ }
+ }
+
+#studly caps, commented out until figure out how to distinguish between use of existing and adding new
+# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) {
+# print "No studly caps, use _\n";
+# print "$herecurr";
+# $clean = 0;
+# }
+
+#no spaces allowed after \ in define
+ if ($line=~/\#\s*define.*\\\s$/) {
+ WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr);
+ }
+
+#warn if <asm/foo.h> is #included and <linux/foo.h> is available (uses RAW line)
+ if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) {
+ my $file = "$1.h";
+ my $checkfile = "include/linux/$file";
+ if (-f "$root/$checkfile" &&
+ $realfile ne $checkfile &&
+ $1 !~ /$allowed_asm_includes/)
+ {
+ if ($realfile =~ m{^arch/}) {
+ CHK("Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+ } else {
+ WARN("Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+ }
+ }
+ }
+
+# multi-statement macros should be enclosed in a do while loop, grab the
+# first statement and ensure its the whole macro if its not enclosed
+# in a known good container
+ if ($realfile !~ m@/vmlinux.lds.h$@ &&
+ $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
+ my $ln = $linenr;
+ my $cnt = $realcnt;
+ my ($off, $dstat, $dcond, $rest);
+ my $ctx = '';
+
+ my $args = defined($1);
+
+ # Find the end of the macro and limit our statement
+ # search to that.
+ while ($cnt > 0 && defined $lines[$ln - 1] &&
+ $lines[$ln - 1] =~ /^(?:-|..*\\$)/)
+ {
+ $ctx .= $rawlines[$ln - 1] . "\n";
+ $cnt-- if ($lines[$ln - 1] !~ /^-/);
+ $ln++;
+ }
+ $ctx .= $rawlines[$ln - 1];
+
+ ($dstat, $dcond, $ln, $cnt, $off) =
+ ctx_statement_block($linenr, $ln - $linenr + 1, 0);
+ #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n";
+ #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
+
+ # Extract the remainder of the define (if any) and
+ # rip off surrounding spaces, and trailing \'s.
+ $rest = '';
+ while ($off != 0 || ($cnt > 0 && $rest =~ /\\\s*$/)) {
+ #print "ADDING cnt<$cnt> $off <" . substr($lines[$ln - 1], $off) . "> rest<$rest>\n";
+ if ($off != 0 || $lines[$ln - 1] !~ /^-/) {
+ $rest .= substr($lines[$ln - 1], $off) . "\n";
+ $cnt--;
+ }
+ $ln++;
+ $off = 0;
+ }
+ $rest =~ s/\\\n.//g;
+ $rest =~ s/^\s*//s;
+ $rest =~ s/\s*$//s;
+
+ # Clean up the original statement.
+ if ($args) {
+ substr($dstat, 0, length($dcond), '');
+ } else {
+ $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//;
+ }
+ $dstat =~ s/$;//g;
+ $dstat =~ s/\\\n.//g;
+ $dstat =~ s/^\s*//s;
+ $dstat =~ s/\s*$//s;
+
+ # Flatten any parentheses and braces
+ while ($dstat =~ s/\([^\(\)]*\)/1/ ||
+ $dstat =~ s/\{[^\{\}]*\}/1/ ||
+ $dstat =~ s/\[[^\{\}]*\]/1/)
+ {
+ }
+
+ my $exceptions = qr{
+ $Declare|
+ module_param_named|
+ MODULE_PARAM_DESC|
+ DECLARE_PER_CPU|
+ DEFINE_PER_CPU|
+ __typeof__\(|
+ union|
+ struct|
+ \.$Ident\s*=\s*|
+ ^\"|\"$
+ }x;
+ #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
+ if ($rest ne '' && $rest ne ',') {
+ if ($rest !~ /while\s*\(/ &&
+ $dstat !~ /$exceptions/)
+ {
+ ERROR("Macros with multiple statements should be enclosed in a do - while loop\n" . "$here\n$ctx\n");
+ }
+
+ } elsif ($ctx !~ /;/) {
+ if ($dstat ne '' &&
+ $dstat !~ /^(?:$Ident|-?$Constant)$/ &&
+ $dstat !~ /$exceptions/ &&
+ $dstat !~ /^\.$Ident\s*=/ &&
+ $dstat =~ /$Operators/)
+ {
+ ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n");
+ }
+ }
+ }
+
+# make sure symbols are always wrapped with VMLINUX_SYMBOL() ...
+# all assignments may have only one of the following with an assignment:
+# .
+# ALIGN(...)
+# VMLINUX_SYMBOL(...)
+ if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) {
+ WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr);
+ }
+
+# check for missing bracing round if etc
+ if ($line =~ /(^.*)\bif\b/ && $line !~ /\#\s*if/) {
+ my ($level, $endln, @chunks) =
+ ctx_statement_full($linenr, $realcnt, 1);
+ #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n";
+ #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n";
+ if ($#chunks >= 0 && $level == 0) {
+ my $allowed = 0;
+ my $seen = 0;
+ my $herectx = $here . "\n";
+ my $ln = $linenr - 1;
+ for my $chunk (@chunks) {
+ my ($cond, $block) = @{$chunk};
+
+ # If the condition carries leading newlines, then count those as offsets.
+ my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s);
+ my $offset = statement_rawlines($whitespace) - 1;
+
+ #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n";
+
+ # We have looked at and allowed this specific line.
+ $suppress_ifbraces{$ln + $offset} = 1;
+
+ $herectx .= "$rawlines[$ln + $offset]\n[...]\n";
+ $ln += statement_rawlines($block) - 1;
+
+ substr($block, 0, length($cond), '');
+
+ $seen++ if ($block =~ /^\s*{/);
+
+ #print "cond<$cond> block<$block> allowed<$allowed>\n";
+ if (statement_lines($cond) > 1) {
+ #print "APW: ALLOWED: cond<$cond>\n";
+ $allowed = 1;
+ }
+ if ($block =~/\b(?:if|for|while)\b/) {
+ #print "APW: ALLOWED: block<$block>\n";
+ $allowed = 1;
+ }
+ if (statement_block_size($block) > 1) {
+ #print "APW: ALLOWED: lines block<$block>\n";
+ $allowed = 1;
+ }
+ }
+ if (!$seen) {
+ WARN("braces {} are necessary for all arms of this statement\n" . $herectx);
+ }
+ }
+ }
+ if (!defined $suppress_ifbraces{$linenr - 1} &&
+ $line =~ /\b(if|while|for|else)\b/ &&
+ $line !~ /\#\s*else/) {
+ my $allowed = 0;
+
+ # Check the pre-context.
+ if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) {
+ #print "APW: ALLOWED: pre<$1>\n";
+ $allowed = 1;
+ }
+
+ my ($level, $endln, @chunks) =
+ ctx_statement_full($linenr, $realcnt, $-[0]);
+
+ # Check the condition.
+ my ($cond, $block) = @{$chunks[0]};
+ #print "CHECKING<$linenr> cond<$cond> block<$block>\n";
+ if (defined $cond) {
+ substr($block, 0, length($cond), '');
+ }
+ if (statement_lines($cond) > 1) {
+ #print "APW: ALLOWED: cond<$cond>\n";
+ $allowed = 1;
+ }
+ if ($block =~/\b(?:if|for|while)\b/) {
+ #print "APW: ALLOWED: block<$block>\n";
+ $allowed = 1;
+ }
+ if (statement_block_size($block) > 1) {
+ #print "APW: ALLOWED: lines block<$block>\n";
+ $allowed = 1;
+ }
+ # Check the post-context.
+ if (defined $chunks[1]) {
+ my ($cond, $block) = @{$chunks[1]};
+ if (defined $cond) {
+ substr($block, 0, length($cond), '');
+ }
+ if ($block =~ /^\s*\{/) {
+ #print "APW: ALLOWED: chunk-1 block<$block>\n";
+ $allowed = 1;
+ }
+ }
+ if ($level == 0 && $block !~ /^\s*\{/ && !$allowed) {
+ my $herectx = $here . "\n";;
+ my $cnt = statement_rawlines($block);
+
+ for (my $n = 0; $n < $cnt; $n++) {
+ $herectx .= raw_line($linenr, $n) . "\n";;
+ }
+
+ WARN("braces {} are necessary even for single statement blocks\n" . $herectx);
+ }
+ }
+
+# don't include deprecated include files (uses RAW line)
+ for my $inc (@dep_includes) {
+ if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) {
+ ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr);
+ }
+ }
+
+# don't use deprecated functions
+ for my $func (@dep_functions) {
+ if ($line =~ /\b$func\b/) {
+ ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr);
+ }
+ }
+
+# no volatiles please
+ my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b};
+ if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) {
+ WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr);
+ }
+
+# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated
+ if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) {
+ ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr);
+ }
+
+# warn about #if 0
+ if ($line =~ /^.\s*\#\s*if\s+0\b/) {
+ CHK("if this code is redundant consider removing it\n" .
+ $herecurr);
+ }
+
+# check for needless kfree() checks
+ if ($prevline =~ /\bif\s*\(([^\)]*)\)/) {
+ my $expr = $1;
+ if ($line =~ /\bkfree\(\Q$expr\E\);/) {
+ WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev);
+ }
+ }
+# check for needless usb_free_urb() checks
+ if ($prevline =~ /\bif\s*\(([^\)]*)\)/) {
+ my $expr = $1;
+ if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) {
+ WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev);
+ }
+ }
+
+# prefer usleep_range over udelay
+ if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) {
+ # ignore udelay's < 10, however
+ if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) {
+ CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line);
+ }
+ }
+
+# warn about unexpectedly long msleep's
+ if ($line =~ /\bmsleep\s*\((\d+)\);/) {
+ if ($1 < 20) {
+ WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line);
+ }
+ }
+
+# warn about #ifdefs in C files
+# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) {
+# print "#ifdef in C files should be avoided\n";
+# print "$herecurr";
+# $clean = 0;
+# }
+
+# warn about spacing in #ifdefs
+ if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) {
+ ERROR("exactly one space required after that #$1\n" . $herecurr);
+ }
+
+# check for spinlock_t definitions without a comment.
+ if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ ||
+ $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) {
+ my $which = $1;
+ if (!ctx_has_comment($first_line, $linenr)) {
+ CHK("$1 definition without comment\n" . $herecurr);
+ }
+ }
+# check for memory barriers without a comment.
+ if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) {
+ if (!ctx_has_comment($first_line, $linenr)) {
+ CHK("memory barrier without comment\n" . $herecurr);
+ }
+ }
+# check of hardware specific defines
+ if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) {
+ CHK("architecture specific defines should be avoided\n" . $herecurr);
+ }
+
+# Check that the storage class is at the beginning of a declaration
+ if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) {
+ WARN("storage class should be at the beginning of the declaration\n" . $herecurr)
+ }
+
+# check the location of the inline attribute, that it is between
+# storage class and type.
+ if ($line =~ /\b$Type\s+$Inline\b/ ||
+ $line =~ /\b$Inline\s+$Storage\b/) {
+ ERROR("inline keyword should sit between storage class and type\n" . $herecurr);
+ }
+
+# Check for __inline__ and __inline, prefer inline
+ if ($line =~ /\b(__inline__|__inline)\b/) {
+ WARN("plain inline is preferred over $1\n" . $herecurr);
+ }
+
+# check for sizeof(&)
+ if ($line =~ /\bsizeof\s*\(\s*\&/) {
+ WARN("sizeof(& should be avoided\n" . $herecurr);
+ }
+
+# check for new externs in .c files.
+ if ($realfile =~ /\.c$/ && defined $stat &&
+ $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
+ {
+ my $function_name = $1;
+ my $paren_space = $2;
+
+ my $s = $stat;
+ if (defined $cond) {
+ substr($s, 0, length($cond), '');
+ }
+ if ($s =~ /^\s*;/ &&
+ $function_name ne 'uninitialized_var')
+ {
+ WARN("externs should be avoided in .c files\n" . $herecurr);
+ }
+
+ if ($paren_space =~ /\n/) {
+ WARN("arguments for function declarations should follow identifier\n" . $herecurr);
+ }
+
+ } elsif ($realfile =~ /\.c$/ && defined $stat &&
+ $stat =~ /^.\s*extern\s+/)
+ {
+ WARN("externs should be avoided in .c files\n" . $herecurr);
+ }
+
+# checks for new __setup's
+ if ($rawline =~ /\b__setup\("([^"]*)"/) {
+ my $name = $1;
+
+ if (!grep(/$name/, @setup_docs)) {
+ CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr);
+ }
+ }
+
+# check for pointless casting of kmalloc return
+ if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) {
+ WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
+ }
+
+# check for gcc specific __FUNCTION__
+ if ($line =~ /__FUNCTION__/) {
+ WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr);
+ }
+
+# check for semaphores used as mutexes
+ if ($line =~ /^.\s*(DECLARE_MUTEX|init_MUTEX)\s*\(/) {
+ WARN("mutexes are preferred for single holder semaphores\n" . $herecurr);
+ }
+# check for semaphores used as mutexes
+ if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) {
+ WARN("consider using a completion\n" . $herecurr);
+
+ }
+# recommend strict_strto* over simple_strto*
+ if ($line =~ /\bsimple_(strto.*?)\s*\(/) {
+ WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr);
+ }
+# check for __initcall(), use device_initcall() explicitly please
+ if ($line =~ /^.\s*__initcall\s*\(/) {
+ WARN("please use device_initcall() instead of __initcall()\n" . $herecurr);
+ }
+# check for various ops structs, ensure they are const.
+ my $struct_ops = qr{acpi_dock_ops|
+ address_space_operations|
+ backlight_ops|
+ block_device_operations|
+ dentry_operations|
+ dev_pm_ops|
+ dma_map_ops|
+ extent_io_ops|
+ file_lock_operations|
+ file_operations|
+ hv_ops|
+ ide_dma_ops|
+ intel_dvo_dev_ops|
+ item_operations|
+ iwl_ops|
+ kgdb_arch|
+ kgdb_io|
+ kset_uevent_ops|
+ lock_manager_operations|
+ microcode_ops|
+ mtrr_ops|
+ neigh_ops|
+ nlmsvc_binding|
+ pci_raw_ops|
+ pipe_buf_operations|
+ platform_hibernation_ops|
+ platform_suspend_ops|
+ proto_ops|
+ rpc_pipe_ops|
+ seq_operations|
+ snd_ac97_build_ops|
+ soc_pcmcia_socket_ops|
+ stacktrace_ops|
+ sysfs_ops|
+ tty_operations|
+ usb_mon_operations|
+ wd_ops}x;
+ if ($line !~ /\bconst\b/ &&
+ $line =~ /\bstruct\s+($struct_ops)\b/) {
+ WARN("struct $1 should normally be const\n" .
+ $herecurr);
+ }
+
+# use of NR_CPUS is usually wrong
+# ignore definitions of NR_CPUS and usage to define arrays as likely right
+ if ($line =~ /\bNR_CPUS\b/ &&
+ $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
+ $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
+ $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
+ $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
+ $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/)
+ {
+ WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
+ }
+
+# check for %L{u,d,i} in strings
+ my $string;
+ while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
+ $string = substr($rawline, $-[1], $+[1] - $-[1]);
+ $string =~ s/%%/__/g;
+ if ($string =~ /(?<!%)%L[udi]/) {
+ WARN("\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr);
+ last;
+ }
+ }
+
+# whine mightly about in_atomic
+ if ($line =~ /\bin_atomic\s*\(/) {
+ if ($realfile =~ m@^drivers/@) {
+ ERROR("do not use in_atomic in drivers\n" . $herecurr);
+ } elsif ($realfile !~ m@^kernel/@) {
+ WARN("use of in_atomic() is incorrect outside core kernel code\n" . $herecurr);
+ }
+ }
+
+# check for lockdep_set_novalidate_class
+ if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
+ $line =~ /__lockdep_no_validate__\s*\)/ ) {
+ if ($realfile !~ m@^kernel/lockdep@ &&
+ $realfile !~ m@^include/linux/lockdep@ &&
+ $realfile !~ m@^drivers/base/core@) {
+ ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
+ }
+ }
+ }
+
+ # If we have no input at all, then there is nothing to report on
+ # so just keep quiet.
+ if ($#rawlines == -1) {
+ exit(0);
+ }
+
+ # In mailback mode only produce a report in the negative, for
+ # things that appear to be patches.
+ if ($mailback && ($clean == 1 || !$is_patch)) {
+ exit(0);
+ }
+
+ # This is not a patch, and we are are in 'no-patch' mode so
+ # just keep quiet.
+ if (!$chk_patch && !$is_patch) {
+ exit(0);
+ }
+
+ if (!$is_patch) {
+ ERROR("Does not appear to be a unified-diff format patch\n");
+ }
+ if ($is_patch && $chk_signoff && $signoff == 0) {
+ ERROR("Missing Signed-off-by: line(s)\n");
+ }
+
+ print report_dump();
+ if ($summary && !($clean == 1 && $quiet == 1)) {
+ print "$filename " if ($summary_file);
+ print "total: $cnt_error errors, $cnt_warn warnings, " .
+ (($check)? "$cnt_chk checks, " : "") .
+ "$cnt_lines lines checked\n";
+ print "\n" if ($quiet == 0);
+ }
+
+ if ($quiet == 0) {
+ # If there were whitespace errors which cleanpatch can fix
+ # then suggest that.
+# if ($rpt_cleaners) {
+# print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n";
+# print " scripts/cleanfile\n\n";
+# }
+ }
+
+ if ($clean == 1 && $quiet == 0) {
+ print "$vname has no obvious style problems and is ready for submission.\n"
+ }
+ if ($clean == 0 && $quiet == 0) {
+ print "$vname has style problems, please review. If any of these errors\n";
+ print "are false positives report them to the maintainer, see\n";
+ print "CHECKPATCH in MAINTAINERS.\n";
+ }
+
+ return $clean;
+}
diff --git a/scripts/create_config b/scripts/create_config
new file mode 100755
index 0000000..0098e68
--- /dev/null
+++ b/scripts/create_config
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+echo "/* Automatically generated by create_config - do not modify */"
+
+while read line; do
+
+case $line in
+ VERSION=*) # configuration
+ version=${line#*=}
+ echo "#define QEMU_VERSION \"$version\""
+ ;;
+ PKGVERSION=*) # configuration
+ pkgversion=${line#*=}
+ echo "#define QEMU_PKGVERSION \"$pkgversion\""
+ ;;
+ prefix=* | [a-z]*dir=*) # directory configuration
+ name=${line%=*}
+ value=${line#*=}
+ define_name=`echo $name | tr '[:lower:]' '[:upper:]'`
+ eval "define_value=\"$value\""
+ echo "#define CONFIG_QEMU_$define_name \"$define_value\""
+ # save for the next definitions
+ eval "$name=\$define_value"
+ ;;
+ CONFIG_AUDIO_DRIVERS=*)
+ drivers=${line#*=}
+ echo "#define CONFIG_AUDIO_DRIVERS \\"
+ for drv in $drivers; do
+ echo " &${drv}_audio_driver,\\"
+ done
+ echo ""
+ ;;
+ CONFIG_BDRV_WHITELIST=*)
+ echo "#define CONFIG_BDRV_WHITELIST \\"
+ for drv in ${line#*=}; do
+ echo " \"${drv}\",\\"
+ done
+ echo " NULL"
+ ;;
+ CONFIG_*=y) # configuration
+ name=${line%=*}
+ echo "#define $name 1"
+ ;;
+ CONFIG_*=*) # configuration
+ name=${line%=*}
+ value=${line#*=}
+ echo "#define $name $value"
+ ;;
+ ARCH=*) # configuration
+ arch=${line#*=}
+ arch_name=`echo $arch | tr '[:lower:]' '[:upper:]'`
+ echo "#define HOST_$arch_name 1"
+ ;;
+ HOST_USB=*)
+ # do nothing
+ ;;
+ HOST_CC=*)
+ # do nothing
+ ;;
+ HOST_*=y) # configuration
+ name=${line%=*}
+ echo "#define $name 1"
+ ;;
+ HOST_*=*) # configuration
+ name=${line%=*}
+ value=${line#*=}
+ echo "#define $name $value"
+ ;;
+ TARGET_ARCH=*) # configuration
+ target_arch=${line#*=}
+ echo "#define TARGET_ARCH \"$target_arch\""
+ ;;
+ TARGET_BASE_ARCH=*) # configuration
+ target_base_arch=${line#*=}
+ if [ "$target_base_arch" != "$target_arch" ]; then
+ base_arch_name=`echo $target_base_arch | tr '[:lower:]' '[:upper:]'`
+ echo "#define TARGET_$base_arch_name 1"
+ fi
+ ;;
+ TARGET_XML_FILES=*)
+ # do nothing
+ ;;
+ TARGET_ABI_DIR=*)
+ # do nothing
+ ;;
+ TARGET_ARCH2=*)
+ # do nothing
+ ;;
+ TARGET_DIRS=*)
+ # do nothing
+ ;;
+ TARGET_*=y) # configuration
+ name=${line%=*}
+ echo "#define $name 1"
+ ;;
+ TARGET_*=*) # configuration
+ name=${line%=*}
+ value=${line#*=}
+ echo "#define $name $value"
+ ;;
+esac
+
+done # read
diff --git a/scripts/feature_to_c.sh b/scripts/feature_to_c.sh
new file mode 100644
index 0000000..b62da8a
--- /dev/null
+++ b/scripts/feature_to_c.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# Convert text files to compilable C arrays.
+#
+# Copyright (C) 2007 Free Software Foundation, Inc.
+#
+# This file is part of GDB.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 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/>.
+
+output=$1
+shift
+
+if test -z "$output" || test -z "$1"; then
+ echo "Usage: $0 OUTPUTFILE INPUTFILE..."
+ exit 1
+fi
+
+if test -e "$output"; then
+ echo "Output file \"$output\" already exists; refusing to overwrite."
+ exit 1
+fi
+
+for input; do
+ arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'`
+
+ ${AWK:-awk} 'BEGIN { n = 0
+ printf "#include \"config.h\"\n"
+ printf "#include \"qemu-common.h\"\n"
+ printf "#include \"gdbstub.h\"\n"
+ print "static const char '$arrayname'[] = {"
+ for (i = 0; i < 255; i++)
+ _ord_[sprintf("%c", i)] = i
+ } {
+ split($0, line, "");
+ printf " "
+ for (i = 1; i <= length($0); i++) {
+ c = line[i]
+ if (c == "'\''") {
+ printf "'\''\\'\'''\'', "
+ } else if (c == "\\") {
+ printf "'\''\\\\'\'', "
+ } else if (_ord_[c] >= 32 && _ord_[c] < 127) {
+ printf "'\''%s'\'', ", c
+ } else {
+ printf "'\''\\%03o'\'', ", _ord_[c]
+ }
+ if (i % 10 == 0)
+ printf "\n "
+ }
+ printf "'\''\\n'\'', \n"
+ } END {
+ print " 0 };"
+ }' < $input >> $output
+done
+
+echo >> $output
+echo "const char *const xml_builtin[][2] = {" >> $output
+
+for input; do
+ basename=`echo $input | sed 's,.*/,,'`
+ arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'`
+ echo " { \"$basename\", $arrayname }," >> $output
+done
+
+echo " { (char *)0, (char *)0 }" >> $output
+echo "};" >> $output
diff --git a/scripts/hxtool b/scripts/hxtool
new file mode 100644
index 0000000..7ca83ed
--- /dev/null
+++ b/scripts/hxtool
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+hxtoh()
+{
+ flag=1
+ while read -r str; do
+ case $str in
+ HXCOMM*)
+ ;;
+ STEXI*|ETEXI*|SQMP*|EQMP*) flag=$(($flag^1))
+ ;;
+ *)
+ test $flag -eq 1 && printf "%s\n" "$str"
+ ;;
+ esac
+ done
+}
+
+hxtotexi()
+{
+ flag=0
+ line=1
+ while read -r str; do
+ case "$str" in
+ HXCOMM*)
+ ;;
+ STEXI*)
+ if test $flag -eq 1 ; then
+ echo "line $line: syntax error: expected ETEXI, found $str" >&2
+ exit 1
+ fi
+ flag=1
+ ;;
+ ETEXI*)
+ if test $flag -ne 1 ; then
+ echo "line $line: syntax error: expected STEXI, found $str" >&2
+ exit 1
+ fi
+ flag=0
+ ;;
+ SQMP*|EQMP*)
+ if test $flag -eq 1 ; then
+ echo "line $line: syntax error: expected ETEXI, found $str" >&2
+ exit 1
+ fi
+ ;;
+ DEFHEADING*)
+ echo "$(expr "$str" : "DEFHEADING(\(.*\))")"
+ ;;
+ *)
+ test $flag -eq 1 && echo "$str"
+ ;;
+ esac
+ line=$((line+1))
+ done
+}
+
+hxtoqmp()
+{
+ IFS=
+ flag=0
+ line=1
+ while read -r str; do
+ case "$str" in
+ HXCOMM*)
+ ;;
+ SQMP*)
+ if test $flag -eq 1 ; then
+ echo "line $line: syntax error: expected EQMP, found $str" >&2
+ exit 1
+ fi
+ flag=1
+ ;;
+ EQMP*)
+ if test $flag -ne 1 ; then
+ echo "line $line: syntax error: expected SQMP, found $str" >&2
+ exit 1
+ fi
+ flag=0
+ ;;
+ STEXI*|ETEXI*)
+ if test $flag -eq 1 ; then
+ echo "line $line: syntax error: expected EQMP, found $str" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ test $flag -eq 1 && echo "$str"
+ ;;
+ esac
+ line=$((line+1))
+ done
+}
+
+case "$1" in
+"-h") hxtoh ;;
+"-t") hxtotexi ;;
+"-q") hxtoqmp ;;
+*) exit 1 ;;
+esac
+
+exit 0
diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh
new file mode 100644
index 0000000..5d14885
--- /dev/null
+++ b/scripts/make_device_config.sh
@@ -0,0 +1,28 @@
+#! /bin/sh
+# Construct a target device config file from a default, pulling in any
+# files from include directives.
+
+dest=$1.tmp
+dep=$1.d
+src=$2
+src_dir=`dirname $src`
+all_includes=
+
+process_includes () {
+ cat $1 | grep '^include' | \
+ while read include file ; do
+ all_includes="$all_includes $src_dir/$file"
+ process_includes $src_dir/$file
+ done
+}
+
+f=$src
+while [ -n "$f" ] ; do
+ f=`tr -d '\r' < $f | awk '/^include / {printf "'$src_dir'/%s", $2}'`
+ [ $? = 0 ] || exit 1
+ all_includes="$all_includes $f"
+done
+process_includes $src > $dest
+
+cat $src $all_includes | grep -v '^include' > $dest
+echo "$1: $all_includes" > $dep
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
new file mode 100644
index 0000000..c50beb7
--- /dev/null
+++ b/scripts/qemu-binfmt-conf.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC program execution by the kernel
+
+# load the binfmt_misc module
+if [ ! -d /proc/sys/fs/binfmt_misc ]; then
+ /sbin/modprobe binfmt_misc
+fi
+if [ ! -f /proc/sys/fs/binfmt_misc/register ]; then
+ mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
+fi
+
+# probe cpu type
+cpu=`uname -m`
+case "$cpu" in
+ i386|i486|i586|i686|i86pc|BePC|x86_64)
+ cpu="i386"
+ ;;
+ m68k)
+ cpu="m68k"
+ ;;
+ mips*)
+ cpu="mips"
+ ;;
+ "Power Macintosh"|ppc|ppc64)
+ cpu="ppc"
+ ;;
+ armv[4-9]*)
+ cpu="arm"
+ ;;
+esac
+
+# register the interpreter for each cpu except for the native one
+if [ $cpu != "i386" ] ; then
+ echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
+ echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "alpha" ] ; then
+ echo ':alpha:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-alpha:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "arm" ] ; then
+ echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register
+ echo ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "sparc" ] ; then
+ echo ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "ppc" ] ; then
+ echo ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "m68k" ] ; then
+ echo 'Please check cpu value and header information for m68k!'
+ echo ':m68k:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-m68k:' > /proc/sys/fs/binfmt_misc/register
+fi
+if [ $cpu != "mips" ] ; then
+ # FIXME: We could use the other endianness on a MIPS host.
+ echo ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register
+ echo ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register
+ echo ':mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mipsn32:' > /proc/sys/fs/binfmt_misc/register
+ echo ':mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsn32el:' > /proc/sys/fs/binfmt_misc/register
+ echo ':mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips64:' > /proc/sys/fs/binfmt_misc/register
+ echo ':mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mips64el:' > /proc/sys/fs/binfmt_misc/register
+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
+fi
diff --git a/scripts/signrom.sh b/scripts/signrom.sh
new file mode 100755
index 0000000..9dc5c63
--- /dev/null
+++ b/scripts/signrom.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Option ROM Signing utility
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 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/>.
+#
+# Copyright Novell Inc, 2009
+# Authors: Alexander Graf <agraf@suse.de>
+#
+# Syntax: signrom.sh <input> <output>
+
+# did we get proper arguments?
+test "$1" -a "$2" || exit 1
+
+sum=0
+
+# find out the file size
+x=`dd if="$1" bs=1 count=1 skip=2 2>/dev/null | od -t u1 -A n`
+#size=`expr $x \* 512 - 1`
+size=$(( $x * 512 - 1 ))
+
+# now get the checksum
+nums=`od -A n -t u1 -v -N $size "$1"`
+for i in ${nums}; do
+ # add each byte's value to sum
+ sum=`expr \( $sum + $i \) % 256`
+done
+
+sum=$(( (256 - $sum) % 256 ))
+sum_octal=$( printf "%o" $sum )
+
+# and write the output file
+cp "$1" "$2"
+printf "\\$sum_octal" | dd of="$2" bs=1 count=1 seek=$size conv=notrunc 2>/dev/null
diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py
new file mode 100755
index 0000000..553a727
--- /dev/null
+++ b/scripts/simpletrace.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#
+# Pretty-printer for simple trace backend binary trace files
+#
+# Copyright IBM, Corp. 2010
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+#
+# For help see docs/tracing.txt
+
+import sys
+import struct
+import re
+
+header_event_id = 0xffffffffffffffff
+header_magic = 0xf2b177cb0aa429b4
+header_version = 0
+
+trace_fmt = '=QQQQQQQQ'
+trace_len = struct.calcsize(trace_fmt)
+event_re = re.compile(r'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*')
+
+def err(msg):
+ sys.stderr.write(msg + '\n')
+ sys.exit(1)
+
+def parse_events(fobj):
+ """Parse a trace-events file."""
+
+ def get_argnames(args):
+ """Extract argument names from a parameter list."""
+ return tuple(arg.split()[-1].lstrip('*') for arg in args.split(','))
+
+ events = {}
+ event_num = 0
+ for line in fobj:
+ m = event_re.match(line.strip())
+ if m is None:
+ continue
+
+ disable, name, args = m.groups()
+ events[event_num] = (name,) + get_argnames(args)
+ event_num += 1
+ return events
+
+def read_record(fobj):
+ """Deserialize a trace record from a file."""
+ s = fobj.read(trace_len)
+ if len(s) != trace_len:
+ return None
+ return struct.unpack(trace_fmt, s)
+
+def read_trace_file(fobj):
+ """Deserialize trace records from a file."""
+ header = read_record(fobj)
+ if header is None or \
+ header[0] != header_event_id or \
+ header[1] != header_magic or \
+ header[2] != header_version:
+ err('not a trace file or incompatible version')
+
+ while True:
+ rec = read_record(fobj)
+ if rec is None:
+ break
+
+ yield rec
+
+class Formatter(object):
+ def __init__(self, events):
+ self.events = events
+ self.last_timestamp = None
+
+ def format_record(self, rec):
+ if self.last_timestamp is None:
+ self.last_timestamp = rec[1]
+ delta_ns = rec[1] - self.last_timestamp
+ self.last_timestamp = rec[1]
+
+ event = self.events[rec[0]]
+ fields = [event[0], '%0.3f' % (delta_ns / 1000.0)]
+ for i in xrange(1, len(event)):
+ fields.append('%s=0x%x' % (event[i], rec[i + 1]))
+ return ' '.join(fields)
+
+if len(sys.argv) != 3:
+ err('usage: %s <trace-events> <trace-file>' % sys.argv[0])
+
+events = parse_events(open(sys.argv[1], 'r'))
+formatter = Formatter(events)
+for rec in read_trace_file(open(sys.argv[2], 'rb')):
+ print formatter.format_record(rec)
diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl
new file mode 100755
index 0000000..9ed056a
--- /dev/null
+++ b/scripts/texi2pod.pl
@@ -0,0 +1,477 @@
+#! /usr/bin/perl -w
+
+# Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+# This file is part of GCC.
+
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# GCC 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING. If not,
+# see <http://www.gnu.org/licenses/>.
+
+# This does trivial (and I mean _trivial_) conversion of Texinfo
+# markup to Perl POD format. It's intended to be used to extract
+# something suitable for a manpage from a Texinfo document.
+
+$output = 0;
+$skipping = 0;
+%sects = ();
+$section = "";
+@icstack = ();
+@endwstack = ();
+@skstack = ();
+@instack = ();
+$shift = "";
+%defs = ();
+$fnno = 1;
+$inf = "";
+$ibase = "";
+@ipath = ();
+
+while ($_ = shift) {
+ if (/^-D(.*)$/) {
+ if ($1 ne "") {
+ $flag = $1;
+ } else {
+ $flag = shift;
+ }
+ $value = "";
+ ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
+ die "no flag specified for -D\n"
+ unless $flag ne "";
+ die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
+ unless $flag =~ /^[a-zA-Z0-9_-]+$/;
+ $defs{$flag} = $value;
+ } elsif (/^-I(.*)$/) {
+ if ($1 ne "") {
+ $flag = $1;
+ } else {
+ $flag = shift;
+ }
+ push (@ipath, $flag);
+ } elsif (/^-/) {
+ usage();
+ } else {
+ $in = $_, next unless defined $in;
+ $out = $_, next unless defined $out;
+ usage();
+ }
+}
+
+if (defined $in) {
+ $inf = gensym();
+ open($inf, "<$in") or die "opening \"$in\": $!\n";
+ $ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
+} else {
+ $inf = \*STDIN;
+}
+
+if (defined $out) {
+ open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
+}
+
+while(defined $inf) {
+while(<$inf>) {
+ # Certain commands are discarded without further processing.
+ /^\@(?:
+ [a-z]+index # @*index: useful only in complete manual
+ |need # @need: useful only in printed manual
+ |(?:end\s+)?group # @group .. @end group: ditto
+ |page # @page: ditto
+ |node # @node: useful only in .info file
+ |(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents
+ )\b/x and next;
+
+ chomp;
+
+ # Look for filename and title markers.
+ /^\@setfilename\s+([^.]+)/ and $fn = $1, next;
+ /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
+
+ # Identify a man title but keep only the one we are interested in.
+ /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
+ if (exists $defs{$1}) {
+ $fn = $1;
+ $tl = postprocess($2);
+ }
+ next;
+ };
+
+ # Look for blocks surrounded by @c man begin SECTION ... @c man end.
+ # This really oughta be @ifman ... @end ifman and the like, but such
+ # would require rev'ing all other Texinfo translators.
+ /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
+ $output = 1 if exists $defs{$2};
+ $sect = $1;
+ next;
+ };
+ /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
+ /^\@c\s+man\s+end/ and do {
+ $sects{$sect} = "" unless exists $sects{$sect};
+ $sects{$sect} .= postprocess($section);
+ $section = "";
+ $output = 0;
+ next;
+ };
+
+ # handle variables
+ /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
+ $defs{$1} = $2;
+ next;
+ };
+ /^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
+ delete $defs{$1};
+ next;
+ };
+
+ next unless $output;
+
+ # Discard comments. (Can't do it above, because then we'd never see
+ # @c man lines.)
+ /^\@c\b/ and next;
+
+ # End-block handler goes up here because it needs to operate even
+ # if we are skipping.
+ /^\@end\s+([a-z]+)/ and do {
+ # Ignore @end foo, where foo is not an operation which may
+ # cause us to skip, if we are presently skipping.
+ my $ended = $1;
+ next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex|copying)$/;
+
+ die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
+ die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
+
+ $endw = pop @endwstack;
+
+ if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
+ $skipping = pop @skstack;
+ next;
+ } elsif ($ended =~ /^(?:example|smallexample|display)$/) {
+ $shift = "";
+ $_ = ""; # need a paragraph break
+ } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
+ $_ = "\n=back\n";
+ $ic = pop @icstack;
+ } elsif ($ended eq "multitable") {
+ $_ = "\n=back\n";
+ } else {
+ die "unknown command \@end $ended at line $.\n";
+ }
+ };
+
+ # We must handle commands which can cause skipping even while we
+ # are skipping, otherwise we will not process nested conditionals
+ # correctly.
+ /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
+ push @endwstack, $endw;
+ push @skstack, $skipping;
+ $endw = "ifset";
+ $skipping = 1 unless exists $defs{$1};
+ next;
+ };
+
+ /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
+ push @endwstack, $endw;
+ push @skstack, $skipping;
+ $endw = "ifclear";
+ $skipping = 1 if exists $defs{$1};
+ next;
+ };
+
+ /^\@(ignore|menu|iftex|copying)\b/ and do {
+ push @endwstack, $endw;
+ push @skstack, $skipping;
+ $endw = $1;
+ $skipping = 1;
+ next;
+ };
+
+ next if $skipping;
+
+ # Character entities. First the ones that can be replaced by raw text
+ # or discarded outright:
+ s/\@copyright\{\}/(c)/g;
+ s/\@dots\{\}/.../g;
+ s/\@enddots\{\}/..../g;
+ s/\@([.!? ])/$1/g;
+ s/\@[:-]//g;
+ s/\@bullet(?:\{\})?/*/g;
+ s/\@TeX\{\}/TeX/g;
+ s/\@pounds\{\}/\#/g;
+ s/\@minus(?:\{\})?/-/g;
+ s/\\,/,/g;
+
+ # Now the ones that have to be replaced by special escapes
+ # (which will be turned back into text by unmunge())
+ s/&/&amp;/g;
+ s/\@\{/&lbrace;/g;
+ s/\@\}/&rbrace;/g;
+ s/\@\@/&at;/g;
+
+ # Inside a verbatim block, handle @var specially.
+ if ($shift ne "") {
+ s/\@var\{([^\}]*)\}/<$1>/g;
+ }
+
+ # POD doesn't interpret E<> inside a verbatim block.
+ if ($shift eq "") {
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ } else {
+ s/</&LT;/g;
+ s/>/&GT;/g;
+ }
+
+ # Single line command handlers.
+
+ /^\@include\s+(.+)$/ and do {
+ push @instack, $inf;
+ $inf = gensym();
+ $file = postprocess($1);
+
+ # Try cwd and $ibase, then explicit -I paths.
+ $done = 0;
+ foreach $path ("", $ibase, @ipath) {
+ $mypath = $file;
+ $mypath = $path . "/" . $mypath if ($path ne "");
+ open($inf, "<" . $mypath) and ($done = 1, last);
+ }
+ die "cannot find $file" if !$done;
+ next;
+ };
+
+ /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
+ and $_ = "\n=head2 $1\n";
+ /^\@subsection\s+(.+)$/
+ and $_ = "\n=head3 $1\n";
+ /^\@subsubsection\s+(.+)$/
+ and $_ = "\n=head4 $1\n";
+
+ # Block command handlers:
+ /^\@itemize(?:\s+(\@[a-z]+|\*|-))?/ and do {
+ push @endwstack, $endw;
+ push @icstack, $ic;
+ if (defined $1) {
+ $ic = $1;
+ } else {
+ $ic = '*';
+ }
+ $_ = "\n=over 4\n";
+ $endw = "itemize";
+ };
+
+ /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
+ push @endwstack, $endw;
+ push @icstack, $ic;
+ if (defined $1) {
+ $ic = $1 . ".";
+ } else {
+ $ic = "1.";
+ }
+ $_ = "\n=over 4\n";
+ $endw = "enumerate";
+ };
+
+ /^\@multitable\s.*/ and do {
+ push @endwstack, $endw;
+ $endw = "multitable";
+ $_ = "\n=over 4\n";
+ };
+
+ /^\@([fv]?table)\s+(\@[a-z]+)/ and do {
+ push @endwstack, $endw;
+ push @icstack, $ic;
+ $endw = $1;
+ $ic = $2;
+ $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/;
+ $ic =~ s/\@(?:code|kbd)/C/;
+ $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
+ $ic =~ s/\@(?:file)/F/;
+ $_ = "\n=over 4\n";
+ };
+
+ /^\@((?:small)?example|display)/ and do {
+ push @endwstack, $endw;
+ $endw = $1;
+ $shift = "\t";
+ $_ = ""; # need a paragraph break
+ };
+
+ /^\@item\s+(.*\S)\s*$/ and $endw eq "multitable" and do {
+ @columns = ();
+ for $column (split (/\s*\@tab\s*/, $1)) {
+ # @strong{...} is used a @headitem work-alike
+ $column =~ s/^\@strong{(.*)}$/$1/;
+ push @columns, $column;
+ }
+ $_ = "\n=item ".join (" : ", @columns)."\n";
+ };
+
+ /^\@itemx?\s*(.+)?$/ and do {
+ if (defined $1) {
+ # Entity escapes prevent munging by the <> processing below.
+ $_ = "\n=item $ic\&LT;$1\&GT;\n";
+ } else {
+ $_ = "\n=item $ic\n";
+ $ic =~ y/A-Ya-y/B-Zb-z/;
+ $ic =~ s/(\d+)/$1 + 1/eg;
+ }
+ };
+
+ $section .= $shift.$_."\n";
+}
+# End of current file.
+close($inf);
+$inf = pop @instack;
+}
+
+die "No filename or title\n" unless defined $fn && defined $tl;
+
+$sects{NAME} = "$fn \- $tl\n";
+$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
+
+for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
+ BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
+ if(exists $sects{$sect}) {
+ $head = $sect;
+ $head =~ s/SEEALSO/SEE ALSO/;
+ print "=head1 $head\n\n";
+ print scalar unmunge ($sects{$sect});
+ print "\n";
+ }
+}
+
+sub usage
+{
+ die "usage: $0 [-D toggle...] [infile [outfile]]\n";
+}
+
+sub postprocess
+{
+ local $_ = $_[0];
+
+ # @value{foo} is replaced by whatever 'foo' is defined as.
+ while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
+ if (! exists $defs{$2}) {
+ print STDERR "Option $2 not defined\n";
+ s/\Q$1\E//;
+ } else {
+ $value = $defs{$2};
+ s/\Q$1\E/$value/;
+ }
+ }
+
+ # Formatting commands.
+ # Temporary escape for @r.
+ s/\@r\{([^\}]*)\}/R<$1>/g;
+ s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
+ s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
+ s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
+ s/\@sc\{([^\}]*)\}/\U$1/g;
+ s/\@file\{([^\}]*)\}/F<$1>/g;
+ s/\@w\{([^\}]*)\}/S<$1>/g;
+ s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
+
+ # keep references of the form @ref{...}, print them bold
+ s/\@(?:ref)\{([^\}]*)\}/B<$1>/g;
+
+ # Change double single quotes to double quotes.
+ s/''/"/g;
+ s/``/"/g;
+
+ # Cross references are thrown away, as are @noindent and @refill.
+ # (@noindent is impossible in .pod, and @refill is unnecessary.)
+ # @* is also impossible in .pod; we discard it and any newline that
+ # follows it. Similarly, our macro @gol must be discarded.
+
+ s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
+ s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
+ s/;\s+\@pxref\{(?:[^\}]*)\}//g;
+ s/\@noindent\s*//g;
+ s/\@refill//g;
+ s/\@gol//g;
+ s/\@\*\s*\n?//g;
+
+ # Anchors are thrown away
+ s/\@anchor\{(?:[^\}]*)\}//g;
+
+ # @uref can take one, two, or three arguments, with different
+ # semantics each time. @url and @email are just like @uref with
+ # one argument, for our purposes.
+ s/\@(?:uref|url|email)\{([^\},]*)\}/&lt;B<$1>&gt;/g;
+ s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
+ s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
+
+ # Un-escape <> at this point.
+ s/&LT;/</g;
+ s/&GT;/>/g;
+
+ # Now un-nest all B<>, I<>, R<>. Theoretically we could have
+ # indefinitely deep nesting; in practice, one level suffices.
+ 1 while s/([BIR])<([^<>]*)([BIR])<([^<>]*)>/$1<$2>$3<$4>$1</g;
+
+ # Replace R<...> with bare ...; eliminate empty markup, B<>;
+ # shift white space at the ends of [BI]<...> expressions outside
+ # the expression.
+ s/R<([^<>]*)>/$1/g;
+ s/[BI]<>//g;
+ s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
+ s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
+
+ # Extract footnotes. This has to be done after all other
+ # processing because otherwise the regexp will choke on formatting
+ # inside @footnote.
+ while (/\@footnote/g) {
+ s/\@footnote\{([^\}]+)\}/[$fnno]/;
+ add_footnote($1, $fnno);
+ $fnno++;
+ }
+
+ return $_;
+}
+
+sub unmunge
+{
+ # Replace escaped symbols with their equivalents.
+ local $_ = $_[0];
+
+ s/&lt;/E<lt>/g;
+ s/&gt;/E<gt>/g;
+ s/&lbrace;/\{/g;
+ s/&rbrace;/\}/g;
+ s/&at;/\@/g;
+ s/&amp;/&/g;
+ return $_;
+}
+
+sub add_footnote
+{
+ unless (exists $sects{FOOTNOTES}) {
+ $sects{FOOTNOTES} = "\n=over 4\n\n";
+ }
+
+ $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
+ $sects{FOOTNOTES} .= $_[0];
+ $sects{FOOTNOTES} .= "\n\n";
+}
+
+# stolen from Symbol.pm
+{
+ my $genseq = 0;
+ sub gensym
+ {
+ my $name = "GEN" . $genseq++;
+ my $ref = \*{$name};
+ delete $::{$name};
+ return $ref;
+ }
+}
diff --git a/scripts/tracetool b/scripts/tracetool
new file mode 100755
index 0000000..e046683
--- /dev/null
+++ b/scripts/tracetool
@@ -0,0 +1,629 @@
+#!/bin/sh
+#
+# Code generator for trace events
+#
+# Copyright IBM, Corp. 2010
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+# Disable pathname expansion, makes processing text with '*' characters simpler
+set -f
+
+usage()
+{
+ cat >&2 <<EOF
+usage: $0 [--nop | --simple | --stderr | --ust | --dtrace] [-h | -c]
+Generate tracing code for a file on stdin.
+
+Backends:
+ --nop Tracing disabled
+ --simple Simple built-in backend
+ --stderr Stderr built-in backend
+ --ust LTTng User Space Tracing backend
+ --dtrace DTrace/SystemTAP backend
+
+Output formats:
+ -h Generate .h file
+ -c Generate .c file
+ -d Generate .d file (DTrace only)
+ --stap Generate .stp file (DTrace with SystemTAP only)
+
+Options:
+ --binary [path] Full path to QEMU binary
+ --target-arch [arch] QEMU emulator target arch
+ --target-type [type] QEMU emulator target type ('system' or 'user')
+
+EOF
+ exit 1
+}
+
+# Get the name of a trace event
+get_name()
+{
+ echo ${1%%\(*}
+}
+
+# Get the argument list of a trace event, including types and names
+get_args()
+{
+ local args
+ args=${1#*\(}
+ args=${args%\)*}
+ echo "$args"
+}
+
+# Get the argument name list of a trace event
+get_argnames()
+{
+ local nfields field name sep
+ nfields=0
+ sep="$2"
+ for field in $(get_args "$1"); do
+ nfields=$((nfields + 1))
+
+ # Drop pointer star
+ field=${field#\*}
+
+ # Only argument names have commas at the end
+ name=${field%,}
+ test "$field" = "$name" && continue
+
+ printf "%s%s " $name $sep
+ done
+
+ # Last argument name
+ if [ "$nfields" -gt 1 ]
+ then
+ printf "%s" "$name"
+ fi
+}
+
+# Get the number of arguments to a trace event
+get_argc()
+{
+ local name argc
+ argc=0
+ for name in $(get_argnames "$1", ","); do
+ argc=$((argc + 1))
+ done
+ echo $argc
+}
+
+# Get the format string for a trace event
+get_fmt()
+{
+ local fmt
+ fmt=${1#*\"}
+ fmt=${fmt%\"*}
+ echo "$fmt"
+}
+
+# Get the state of a trace event
+get_state()
+{
+ local str disable state
+ str=$(get_name "$1")
+ disable=${str##disable }
+ if [ "$disable" = "$str" ] ; then
+ state=1
+ else
+ state=0
+ fi
+ echo "$state"
+}
+
+linetoh_begin_nop()
+{
+ return
+}
+
+linetoh_nop()
+{
+ local name args
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+
+ # Define an empty function for the trace event
+ cat <<EOF
+static inline void trace_$name($args)
+{
+}
+EOF
+}
+
+linetoh_end_nop()
+{
+ return
+}
+
+linetoc_begin_nop()
+{
+ return
+}
+
+linetoc_nop()
+{
+ # No need for function definitions in nop backend
+ return
+}
+
+linetoc_end_nop()
+{
+ return
+}
+
+linetoh_begin_simple()
+{
+ cat <<EOF
+#include "simpletrace.h"
+EOF
+
+ simple_event_num=0
+}
+
+cast_args_to_uint64_t()
+{
+ local arg
+ for arg in $(get_argnames "$1", ","); do
+ printf "%s" "(uint64_t)(uintptr_t)$arg"
+ done
+}
+
+linetoh_simple()
+{
+ local name args argc trace_args state
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argc=$(get_argc "$1")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ]; then
+ name=${name##disable }
+ fi
+
+ trace_args="$simple_event_num"
+ if [ "$argc" -gt 0 ]
+ then
+ trace_args="$trace_args, $(cast_args_to_uint64_t "$1")"
+ fi
+
+ cat <<EOF
+static inline void trace_$name($args)
+{
+ trace$argc($trace_args);
+}
+EOF
+
+ simple_event_num=$((simple_event_num + 1))
+}
+
+linetoh_end_simple()
+{
+ cat <<EOF
+#define NR_TRACE_EVENTS $simple_event_num
+extern TraceEvent trace_list[NR_TRACE_EVENTS];
+EOF
+}
+
+linetoc_begin_simple()
+{
+ cat <<EOF
+#include "trace.h"
+
+TraceEvent trace_list[] = {
+EOF
+ simple_event_num=0
+
+}
+
+linetoc_simple()
+{
+ local name state
+ name=$(get_name "$1")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ] ; then
+ name=${name##disable }
+ fi
+ cat <<EOF
+{.tp_name = "$name", .state=$state},
+EOF
+ simple_event_num=$((simple_event_num + 1))
+}
+
+linetoc_end_simple()
+{
+ cat <<EOF
+};
+EOF
+}
+
+#STDERR
+linetoh_begin_stderr()
+{
+ cat <<EOF
+#include <stdio.h>
+EOF
+}
+
+linetoh_stderr()
+{
+ local name args argnames argc fmt
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argnames=$(get_argnames "$1" ",")
+ argc=$(get_argc "$1")
+ fmt=$(get_fmt "$1")
+
+ if [ "$argc" -gt 0 ]; then
+ argnames=", $argnames"
+ fi
+
+ cat <<EOF
+static inline void trace_$name($args)
+{
+ fprintf(stderr, "$name $fmt\n" $argnames);
+}
+EOF
+}
+
+linetoh_end_stderr()
+{
+return
+}
+
+linetoc_begin_stderr()
+{
+return
+}
+
+linetoc_stderr()
+{
+return
+}
+
+linetoc_end_stderr()
+{
+return
+}
+#END OF STDERR
+
+# Clean up after UST headers which pollute the namespace
+ust_clean_namespace() {
+ cat <<EOF
+#undef mutex_lock
+#undef mutex_unlock
+#undef inline
+#undef wmb
+EOF
+}
+
+linetoh_begin_ust()
+{
+ echo "#include <ust/tracepoint.h>"
+ ust_clean_namespace
+}
+
+linetoh_ust()
+{
+ local name args argnames
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argnames=$(get_argnames "$1", ",")
+
+ cat <<EOF
+DECLARE_TRACE(ust_$name, TP_PROTO($args), TP_ARGS($argnames));
+#define trace_$name trace_ust_$name
+EOF
+}
+
+linetoh_end_ust()
+{
+ return
+}
+
+linetoc_begin_ust()
+{
+ cat <<EOF
+#include <ust/marker.h>
+$(ust_clean_namespace)
+#include "trace.h"
+EOF
+}
+
+linetoc_ust()
+{
+ local name args argnames fmt
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argnames=$(get_argnames "$1", ",")
+ fmt=$(get_fmt "$1")
+
+ cat <<EOF
+DEFINE_TRACE(ust_$name);
+
+static void ust_${name}_probe($args)
+{
+ trace_mark(ust, $name, "$fmt", $argnames);
+}
+EOF
+
+ # Collect names for later
+ names="$names $name"
+}
+
+linetoc_end_ust()
+{
+ cat <<EOF
+static void __attribute__((constructor)) trace_init(void)
+{
+EOF
+
+ for name in $names; do
+ cat <<EOF
+ register_trace_ust_$name(ust_${name}_probe);
+EOF
+ done
+
+ echo "}"
+}
+
+linetoh_begin_dtrace()
+{
+ cat <<EOF
+#include "trace-dtrace.h"
+EOF
+}
+
+linetoh_dtrace()
+{
+ local name args argnames state nameupper
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ argnames=$(get_argnames "$1", ",")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ] ; then
+ name=${name##disable }
+ fi
+
+ nameupper=`echo $name | tr '[:lower:]' '[:upper:]'`
+
+ # Define an empty function for the trace event
+ cat <<EOF
+static inline void trace_$name($args) {
+ if (QEMU_${nameupper}_ENABLED()) {
+ QEMU_${nameupper}($argnames);
+ }
+}
+EOF
+}
+
+linetoh_end_dtrace()
+{
+ return
+}
+
+linetoc_begin_dtrace()
+{
+ return
+}
+
+linetoc_dtrace()
+{
+ # No need for function definitions in dtrace backend
+ return
+}
+
+linetoc_end_dtrace()
+{
+ return
+}
+
+linetod_begin_dtrace()
+{
+ cat <<EOF
+provider qemu {
+EOF
+}
+
+linetod_dtrace()
+{
+ local name args state
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ] ; then
+ name=${name##disable }
+ fi
+
+ # DTrace provider syntax expects foo() for empty
+ # params, not foo(void)
+ if [ "$args" = "void" ]; then
+ args=""
+ fi
+
+ # Define prototype for probe arguments
+ cat <<EOF
+ probe $name($args);
+EOF
+}
+
+linetod_end_dtrace()
+{
+ cat <<EOF
+};
+EOF
+}
+
+linetostap_begin_dtrace()
+{
+ return
+}
+
+linetostap_dtrace()
+{
+ local i arg name args arglist state
+ name=$(get_name "$1")
+ args=$(get_args "$1")
+ arglist=$(get_argnames "$1", "")
+ state=$(get_state "$1")
+ if [ "$state" = "0" ] ; then
+ name=${name##disable }
+ fi
+
+ # Define prototype for probe arguments
+ cat <<EOF
+probe qemu.$targettype.$targetarch.$name = process("$binary").mark("$name")
+{
+EOF
+
+ i=1
+ for arg in $arglist
+ do
+ # 'limit' is a reserved keyword
+ if [ "$arg" = "limit" ]; then
+ arg="_limit"
+ fi
+ cat <<EOF
+ $arg = \$arg$i;
+EOF
+ i="$((i+1))"
+ done
+
+ cat <<EOF
+}
+EOF
+}
+
+linetostap_end_dtrace()
+{
+ return
+}
+
+# Process stdin by calling begin, line, and end functions for the backend
+convert()
+{
+ local begin process_line end str disable
+ begin="lineto$1_begin_$backend"
+ process_line="lineto$1_$backend"
+ end="lineto$1_end_$backend"
+
+ "$begin"
+
+ while read -r str; do
+ # Skip comments and empty lines
+ test -z "${str%%#*}" && continue
+
+ # Process the line. The nop backend handles disabled lines.
+ disable=${str%%disable *}
+ echo
+ if test -z "$disable"; then
+ # Pass the disabled state as an arg for the simple
+ # or DTrace backends which handle it dynamically.
+ # For all other backends, call lineto$1_nop()
+ if [ $backend = "simple" -o "$backend" = "dtrace" ]; then
+ "$process_line" "$str"
+ else
+ "lineto$1_nop" "${str##disable }"
+ fi
+ else
+ "$process_line" "$str"
+ fi
+ done
+
+ echo
+ "$end"
+}
+
+tracetoh()
+{
+ cat <<EOF
+#ifndef TRACE_H
+#define TRACE_H
+
+/* This file is autogenerated by tracetool, do not edit. */
+
+#include "qemu-common.h"
+EOF
+ convert h
+ echo "#endif /* TRACE_H */"
+}
+
+tracetoc()
+{
+ echo "/* This file is autogenerated by tracetool, do not edit. */"
+ convert c
+}
+
+tracetod()
+{
+ if [ $backend != "dtrace" ]; then
+ echo "DTrace probe generator not applicable to $backend backend"
+ exit 1
+ fi
+ echo "/* This file is autogenerated by tracetool, do not edit. */"
+ convert d
+}
+
+tracetostap()
+{
+ if [ $backend != "dtrace" ]; then
+ echo "SystemTAP tapset generator not applicable to $backend backend"
+ exit 1
+ fi
+ if [ -z "$binary" ]; then
+ echo "--binary is required for SystemTAP tapset generator"
+ exit 1
+ fi
+ if [ -z "$targettype" ]; then
+ echo "--target-type is required for SystemTAP tapset generator"
+ exit 1
+ fi
+ if [ -z "$targetarch" ]; then
+ echo "--target-arch is required for SystemTAP tapset generator"
+ exit 1
+ fi
+ echo "/* This file is autogenerated by tracetool, do not edit. */"
+ convert stap
+}
+
+
+backend=
+output=
+binary=
+targettype=
+targetarch=
+
+
+until [ -z "$1" ]
+do
+ case "$1" in
+ "--nop" | "--simple" | "--stderr" | "--ust" | "--dtrace") backend="${1#--}" ;;
+
+ "--binary") shift ; binary="$1" ;;
+ "--target-arch") shift ; targetarch="$1" ;;
+ "--target-type") shift ; targettype="$1" ;;
+
+ "-h" | "-c" | "-d") output="${1#-}" ;;
+ "--stap") output="${1#--}" ;;
+
+ "--check-backend") exit 0 ;; # used by ./configure to test for backend
+
+ "--list-backends") # used by ./configure to list available backends
+ echo "nop simple stderr ust dtrace"
+ exit 0
+ ;;
+
+ *)
+ usage;;
+ esac
+ shift
+done
+
+if [ "$backend" = "" -o "$output" = "" ]; then
+ usage
+fi
+
+gen="traceto$output"
+"$gen"
+
+exit 0
diff --git a/sh4-dis.c b/sh4-dis.c
new file mode 100644
index 0000000..673bc78
--- /dev/null
+++ b/sh4-dis.c
@@ -0,0 +1,2077 @@
+/* Disassemble SH instructions.
+ Copyright 1993, 1994, 1995, 1997, 1998, 2000, 2001, 2002, 2003, 2004
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+#include <stdio.h>
+#include "dis-asm.h"
+
+#define DEFINE_TABLE
+
+typedef enum
+ {
+ HEX_0,
+ HEX_1,
+ HEX_2,
+ HEX_3,
+ HEX_4,
+ HEX_5,
+ HEX_6,
+ HEX_7,
+ HEX_8,
+ HEX_9,
+ HEX_A,
+ HEX_B,
+ HEX_C,
+ HEX_D,
+ HEX_E,
+ HEX_F,
+ HEX_XX00,
+ HEX_00YY,
+ REG_N,
+ REG_N_D, /* nnn0 */
+ REG_N_B01, /* nn01 */
+ REG_M,
+ SDT_REG_N,
+ REG_NM,
+ REG_B,
+ BRANCH_12,
+ BRANCH_8,
+ IMM0_4,
+ IMM0_4BY2,
+ IMM0_4BY4,
+ IMM1_4,
+ IMM1_4BY2,
+ IMM1_4BY4,
+ PCRELIMM_8BY2,
+ PCRELIMM_8BY4,
+ IMM0_8,
+ IMM0_8BY2,
+ IMM0_8BY4,
+ IMM1_8,
+ IMM1_8BY2,
+ IMM1_8BY4,
+ PPI,
+ NOPX,
+ NOPY,
+ MOVX,
+ MOVY,
+ MOVX_NOPY,
+ MOVY_NOPX,
+ PSH,
+ PMUL,
+ PPI3,
+ PPI3NC,
+ PDC,
+ PPIC,
+ REPEAT,
+ IMM0_3c, /* xxxx 0iii */
+ IMM0_3s, /* xxxx 1iii */
+ IMM0_3Uc, /* 0iii xxxx */
+ IMM0_3Us, /* 1iii xxxx */
+ IMM0_20_4,
+ IMM0_20, /* follows IMM0_20_4 */
+ IMM0_20BY8, /* follows IMM0_20_4 */
+ DISP0_12,
+ DISP0_12BY2,
+ DISP0_12BY4,
+ DISP0_12BY8,
+ DISP1_12,
+ DISP1_12BY2,
+ DISP1_12BY4,
+ DISP1_12BY8
+ }
+sh_nibble_type;
+
+typedef enum
+ {
+ A_END,
+ A_BDISP12,
+ A_BDISP8,
+ A_DEC_M,
+ A_DEC_N,
+ A_DISP_GBR,
+ A_PC,
+ A_DISP_PC,
+ A_DISP_PC_ABS,
+ A_DISP_REG_M,
+ A_DISP_REG_N,
+ A_GBR,
+ A_IMM,
+ A_INC_M,
+ A_INC_N,
+ A_IND_M,
+ A_IND_N,
+ A_IND_R0_REG_M,
+ A_IND_R0_REG_N,
+ A_MACH,
+ A_MACL,
+ A_PR,
+ A_R0,
+ A_R0_GBR,
+ A_REG_M,
+ A_REG_N,
+ A_REG_B,
+ A_SR,
+ A_VBR,
+ A_TBR,
+ A_DISP_TBR,
+ A_DISP2_TBR,
+ A_DEC_R15,
+ A_INC_R15,
+ A_MOD,
+ A_RE,
+ A_RS,
+ A_DSR,
+ DSP_REG_M,
+ DSP_REG_N,
+ DSP_REG_X,
+ DSP_REG_Y,
+ DSP_REG_E,
+ DSP_REG_F,
+ DSP_REG_G,
+ DSP_REG_A_M,
+ DSP_REG_AX,
+ DSP_REG_XY,
+ DSP_REG_AY,
+ DSP_REG_YX,
+ AX_INC_N,
+ AY_INC_N,
+ AXY_INC_N,
+ AYX_INC_N,
+ AX_IND_N,
+ AY_IND_N,
+ AXY_IND_N,
+ AYX_IND_N,
+ AX_PMOD_N,
+ AXY_PMOD_N,
+ AY_PMOD_N,
+ AYX_PMOD_N,
+ AS_DEC_N,
+ AS_INC_N,
+ AS_IND_N,
+ AS_PMOD_N,
+ A_A0,
+ A_X0,
+ A_X1,
+ A_Y0,
+ A_Y1,
+ A_SSR,
+ A_SPC,
+ A_SGR,
+ A_DBR,
+ F_REG_N,
+ F_REG_M,
+ D_REG_N,
+ D_REG_M,
+ X_REG_N, /* Only used for argument parsing. */
+ X_REG_M, /* Only used for argument parsing. */
+ DX_REG_N,
+ DX_REG_M,
+ V_REG_N,
+ V_REG_M,
+ XMTRX_M4,
+ F_FR0,
+ FPUL_N,
+ FPUL_M,
+ FPSCR_N,
+ FPSCR_M
+ }
+sh_arg_type;
+
+typedef enum
+ {
+ A_A1_NUM = 5,
+ A_A0_NUM = 7,
+ A_X0_NUM, A_X1_NUM, A_Y0_NUM, A_Y1_NUM,
+ A_M0_NUM, A_A1G_NUM, A_M1_NUM, A_A0G_NUM
+ }
+sh_dsp_reg_nums;
+
+#define arch_sh1_base 0x0001
+#define arch_sh2_base 0x0002
+#define arch_sh3_base 0x0004
+#define arch_sh4_base 0x0008
+#define arch_sh4a_base 0x0010
+#define arch_sh2a_base 0x0020
+
+/* This is an annotation on instruction types, but we abuse the arch
+ field in instructions to denote it. */
+#define arch_op32 0x00100000 /* This is a 32-bit opcode. */
+
+#define arch_sh_no_mmu 0x04000000
+#define arch_sh_has_mmu 0x08000000
+#define arch_sh_no_co 0x10000000 /* neither FPU nor DSP co-processor */
+#define arch_sh_sp_fpu 0x20000000 /* single precision FPU */
+#define arch_sh_dp_fpu 0x40000000 /* double precision FPU */
+#define arch_sh_has_dsp 0x80000000
+
+
+#define arch_sh_base_mask 0x0000003f
+#define arch_opann_mask 0x00100000
+#define arch_sh_mmu_mask 0x0c000000
+#define arch_sh_co_mask 0xf0000000
+
+
+#define arch_sh1 (arch_sh1_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh2 (arch_sh2_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh2a (arch_sh2a_base|arch_sh_no_mmu|arch_sh_dp_fpu)
+#define arch_sh2a_nofpu (arch_sh2a_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh2e (arch_sh2_base|arch_sh2a_base|arch_sh_no_mmu|arch_sh_sp_fpu)
+#define arch_sh_dsp (arch_sh2_base|arch_sh_no_mmu|arch_sh_has_dsp)
+#define arch_sh3_nommu (arch_sh3_base|arch_sh_no_mmu|arch_sh_no_co)
+#define arch_sh3 (arch_sh3_base|arch_sh_has_mmu|arch_sh_no_co)
+#define arch_sh3e (arch_sh3_base|arch_sh_has_mmu|arch_sh_sp_fpu)
+#define arch_sh3_dsp (arch_sh3_base|arch_sh_has_mmu|arch_sh_has_dsp)
+#define arch_sh4 (arch_sh4_base|arch_sh_has_mmu|arch_sh_dp_fpu)
+#define arch_sh4a (arch_sh4a_base|arch_sh_has_mmu|arch_sh_dp_fpu)
+#define arch_sh4al_dsp (arch_sh4a_base|arch_sh_has_mmu|arch_sh_has_dsp)
+#define arch_sh4_nofpu (arch_sh4_base|arch_sh_has_mmu|arch_sh_no_co)
+#define arch_sh4a_nofpu (arch_sh4a_base|arch_sh_has_mmu|arch_sh_no_co)
+#define arch_sh4_nommu_nofpu (arch_sh4_base|arch_sh_no_mmu|arch_sh_no_co)
+
+#define SH_MERGE_ARCH_SET(SET1, SET2) ((SET1) & (SET2))
+#define SH_VALID_BASE_ARCH_SET(SET) (((SET) & arch_sh_base_mask) != 0)
+#define SH_VALID_MMU_ARCH_SET(SET) (((SET) & arch_sh_mmu_mask) != 0)
+#define SH_VALID_CO_ARCH_SET(SET) (((SET) & arch_sh_co_mask) != 0)
+#define SH_VALID_ARCH_SET(SET) \
+ (SH_VALID_BASE_ARCH_SET (SET) \
+ && SH_VALID_MMU_ARCH_SET (SET) \
+ && SH_VALID_CO_ARCH_SET (SET))
+#define SH_MERGE_ARCH_SET_VALID(SET1, SET2) \
+ SH_VALID_ARCH_SET (SH_MERGE_ARCH_SET (SET1, SET2))
+
+#define SH_ARCH_SET_HAS_FPU(SET) \
+ (((SET) & (arch_sh_sp_fpu | arch_sh_dp_fpu)) != 0)
+#define SH_ARCH_SET_HAS_DSP(SET) \
+ (((SET) & arch_sh_has_dsp) != 0)
+
+/* This is returned from the functions below when an error occurs
+ (in addition to a call to BFD_FAIL). The value should allow
+ the tools to continue to function in most cases - there may
+ be some confusion between DSP and FPU etc. */
+#define SH_ARCH_UNKNOWN_ARCH 0xffffffff
+
+/* These are defined in bfd/cpu-sh.c . */
+unsigned int sh_get_arch_from_bfd_mach (unsigned long mach);
+unsigned int sh_get_arch_up_from_bfd_mach (unsigned long mach);
+unsigned long sh_get_bfd_mach_from_arch_set (unsigned int arch_set);
+/* bfd_boolean sh_merge_bfd_arch (bfd *ibfd, bfd *obfd); */
+
+/* Below are the 'architecture sets'.
+ They describe the following inheritance graph:
+
+ SH1
+ |
+ SH2
+ .------------'|`--------------------.
+ / | \
+SH-DSP SH3-nommu SH2E
+ | |`--------. |
+ | | \ |
+ | SH3 SH4-nommu-nofpu |
+ | | | |
+ | .------------'|`----------+---------. |
+ |/ / \|
+ | | .-------' |
+ | |/ |
+SH3-dsp SH4-nofpu SH3E
+ | |`--------------------. |
+ | | \|
+ | SH4A-nofpu SH4
+ | .------------' `--------------------. |
+ |/ \|
+SH4AL-dsp SH4A
+
+*/
+
+/* Central branches */
+#define arch_sh1_up (arch_sh1 | arch_sh2_up)
+#define arch_sh2_up (arch_sh2 | arch_sh2e_up | arch_sh2a_nofpu_up | arch_sh3_nommu_up | arch_sh_dsp_up)
+#define arch_sh3_nommu_up (arch_sh3_nommu | arch_sh3_up | arch_sh4_nommu_nofpu_up)
+#define arch_sh3_up (arch_sh3 | arch_sh3e_up | arch_sh3_dsp_up | arch_sh4_nofp_up)
+#define arch_sh4_nommu_nofpu_up (arch_sh4_nommu_nofpu | arch_sh4_nofp_up)
+#define arch_sh4_nofp_up (arch_sh4_nofpu | arch_sh4_up | arch_sh4a_nofp_up)
+#define arch_sh4a_nofp_up (arch_sh4a_nofpu | arch_sh4a_up | arch_sh4al_dsp_up)
+
+/* Right branch */
+#define arch_sh2e_up (arch_sh2e | arch_sh2a_up | arch_sh3e_up)
+#define arch_sh3e_up (arch_sh3e | arch_sh4_up)
+#define arch_sh4_up (arch_sh4 | arch_sh4a_up)
+#define arch_sh4a_up (arch_sh4a)
+
+/* Left branch */
+#define arch_sh_dsp_up (arch_sh_dsp | arch_sh3_dsp_up)
+#define arch_sh3_dsp_up (arch_sh3_dsp | arch_sh4al_dsp_up)
+#define arch_sh4al_dsp_up (arch_sh4al_dsp)
+
+/* SH 2a branched off SH2e, adding a lot but not all of SH4 and SH4a. */
+#define arch_sh2a_up (arch_sh2a)
+#define arch_sh2a_nofpu_up (arch_sh2a_nofpu | arch_sh2a_up)
+
+
+typedef struct
+{
+ const char *name;
+ sh_arg_type arg[4];
+ sh_nibble_type nibbles[9];
+ unsigned int arch;
+} sh_opcode_info;
+
+#ifdef DEFINE_TABLE
+
+const sh_opcode_info sh_table[] =
+ {
+/* 0111nnnni8*1.... add #<imm>,<REG_N> */{"add",{A_IMM,A_REG_N},{HEX_7,REG_N,IMM0_8}, arch_sh1_up},
+
+/* 0011nnnnmmmm1100 add <REG_M>,<REG_N> */{"add",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0011nnnnmmmm1110 addc <REG_M>,<REG_N>*/{"addc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0011nnnnmmmm1111 addv <REG_M>,<REG_N>*/{"addv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 11001001i8*1.... and #<imm>,R0 */{"and",{A_IMM,A_R0},{HEX_C,HEX_9,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1001 and <REG_M>,<REG_N> */{"and",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_9}, arch_sh1_up},
+
+/* 11001101i8*1.... and.b #<imm>,@(R0,GBR)*/{"and.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_D,IMM0_8}, arch_sh1_up},
+
+/* 1010i12......... bra <bdisp12> */{"bra",{A_BDISP12},{HEX_A,BRANCH_12}, arch_sh1_up},
+
+/* 1011i12......... bsr <bdisp12> */{"bsr",{A_BDISP12},{HEX_B,BRANCH_12}, arch_sh1_up},
+
+/* 10001001i8p1.... bt <bdisp8> */{"bt",{A_BDISP8},{HEX_8,HEX_9,BRANCH_8}, arch_sh1_up},
+
+/* 10001011i8p1.... bf <bdisp8> */{"bf",{A_BDISP8},{HEX_8,HEX_B,BRANCH_8}, arch_sh1_up},
+
+/* 10001101i8p1.... bt.s <bdisp8> */{"bt.s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}, arch_sh2_up},
+
+/* 10001101i8p1.... bt/s <bdisp8> */{"bt/s",{A_BDISP8},{HEX_8,HEX_D,BRANCH_8}, arch_sh2_up},
+
+/* 10001111i8p1.... bf.s <bdisp8> */{"bf.s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}, arch_sh2_up},
+
+/* 10001111i8p1.... bf/s <bdisp8> */{"bf/s",{A_BDISP8},{HEX_8,HEX_F,BRANCH_8}, arch_sh2_up},
+
+/* 0000000010001000 clrdmxy */{"clrdmxy",{0},{HEX_0,HEX_0,HEX_8,HEX_8}, arch_sh4al_dsp_up},
+
+/* 0000000000101000 clrmac */{"clrmac",{0},{HEX_0,HEX_0,HEX_2,HEX_8}, arch_sh1_up},
+
+/* 0000000001001000 clrs */{"clrs",{0},{HEX_0,HEX_0,HEX_4,HEX_8}, arch_sh1_up},
+
+/* 0000000000001000 clrt */{"clrt",{0},{HEX_0,HEX_0,HEX_0,HEX_8}, arch_sh1_up},
+
+/* 10001000i8*1.... cmp/eq #<imm>,R0 */{"cmp/eq",{A_IMM,A_R0},{HEX_8,HEX_8,IMM0_8}, arch_sh1_up},
+
+/* 0011nnnnmmmm0000 cmp/eq <REG_M>,<REG_N>*/{"cmp/eq",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_0}, arch_sh1_up},
+
+/* 0011nnnnmmmm0011 cmp/ge <REG_M>,<REG_N>*/{"cmp/ge",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_3}, arch_sh1_up},
+
+/* 0011nnnnmmmm0111 cmp/gt <REG_M>,<REG_N>*/{"cmp/gt",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_7}, arch_sh1_up},
+
+/* 0011nnnnmmmm0110 cmp/hi <REG_M>,<REG_N>*/{"cmp/hi",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0011nnnnmmmm0010 cmp/hs <REG_M>,<REG_N>*/{"cmp/hs",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn00010101 cmp/pl <REG_N> */{"cmp/pl",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_5}, arch_sh1_up},
+
+/* 0100nnnn00010001 cmp/pz <REG_N> */{"cmp/pz",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_1}, arch_sh1_up},
+
+/* 0010nnnnmmmm1100 cmp/str <REG_M>,<REG_N>*/{"cmp/str",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0010nnnnmmmm0111 div0s <REG_M>,<REG_N>*/{"div0s",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_7}, arch_sh1_up},
+
+/* 0000000000011001 div0u */{"div0u",{0},{HEX_0,HEX_0,HEX_1,HEX_9}, arch_sh1_up},
+
+/* 0011nnnnmmmm0100 div1 <REG_M>,<REG_N>*/{"div1",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0110nnnnmmmm1110 exts.b <REG_M>,<REG_N>*/{"exts.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0110nnnnmmmm1111 exts.w <REG_M>,<REG_N>*/{"exts.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 0110nnnnmmmm1100 extu.b <REG_M>,<REG_N>*/{"extu.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0110nnnnmmmm1101 extu.w <REG_M>,<REG_N>*/{"extu.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_D}, arch_sh1_up},
+
+/* 0000nnnn11100011 icbi @<REG_N> */{"icbi",{A_IND_N},{HEX_0,REG_N,HEX_E,HEX_3}, arch_sh4a_nofp_up},
+
+/* 0100nnnn00101011 jmp @<REG_N> */{"jmp",{A_IND_N},{HEX_4,REG_N,HEX_2,HEX_B}, arch_sh1_up},
+
+/* 0100nnnn00001011 jsr @<REG_N> */{"jsr",{A_IND_N},{HEX_4,REG_N,HEX_0,HEX_B}, arch_sh1_up},
+
+/* 0100nnnn00001110 ldc <REG_N>,SR */{"ldc",{A_REG_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_E}, arch_sh1_up},
+
+/* 0100nnnn00011110 ldc <REG_N>,GBR */{"ldc",{A_REG_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_E}, arch_sh1_up},
+
+/* 0100nnnn00111010 ldc <REG_N>,SGR */{"ldc",{A_REG_N,A_SGR},{HEX_4,REG_N,HEX_3,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0100mmmm01001010 ldc <REG_M>,TBR */{"ldc",{A_REG_M,A_TBR},{HEX_4,REG_M,HEX_4,HEX_A}, arch_sh2a_nofpu_up},
+
+/* 0100nnnn00101110 ldc <REG_N>,VBR */{"ldc",{A_REG_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_E}, arch_sh1_up},
+
+/* 0100nnnn01011110 ldc <REG_N>,MOD */{"ldc",{A_REG_N,A_MOD},{HEX_4,REG_N,HEX_5,HEX_E}, arch_sh_dsp_up},
+
+/* 0100nnnn01111110 ldc <REG_N>,RE */{"ldc",{A_REG_N,A_RE},{HEX_4,REG_N,HEX_7,HEX_E}, arch_sh_dsp_up},
+
+/* 0100nnnn01101110 ldc <REG_N>,RS */{"ldc",{A_REG_N,A_RS},{HEX_4,REG_N,HEX_6,HEX_E}, arch_sh_dsp_up},
+
+/* 0100nnnn00111110 ldc <REG_N>,SSR */{"ldc",{A_REG_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_E}, arch_sh3_nommu_up},
+
+/* 0100nnnn01001110 ldc <REG_N>,SPC */{"ldc",{A_REG_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_E}, arch_sh3_nommu_up},
+
+/* 0100nnnn11111010 ldc <REG_N>,DBR */{"ldc",{A_REG_N,A_DBR},{HEX_4,REG_N,HEX_F,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn1xxx1110 ldc <REG_N>,Rn_BANK */{"ldc",{A_REG_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_E}, arch_sh3_nommu_up},
+
+/* 0100nnnn00000111 ldc.l @<REG_N>+,SR */{"ldc.l",{A_INC_N,A_SR},{HEX_4,REG_N,HEX_0,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00010111 ldc.l @<REG_N>+,GBR */{"ldc.l",{A_INC_N,A_GBR},{HEX_4,REG_N,HEX_1,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00100111 ldc.l @<REG_N>+,VBR */{"ldc.l",{A_INC_N,A_VBR},{HEX_4,REG_N,HEX_2,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00110110 ldc.l @<REG_N>+,SGR */{"ldc.l",{A_INC_N,A_SGR},{HEX_4,REG_N,HEX_3,HEX_6}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn01010111 ldc.l @<REG_N>+,MOD */{"ldc.l",{A_INC_N,A_MOD},{HEX_4,REG_N,HEX_5,HEX_7}, arch_sh_dsp_up},
+
+/* 0100nnnn01110111 ldc.l @<REG_N>+,RE */{"ldc.l",{A_INC_N,A_RE},{HEX_4,REG_N,HEX_7,HEX_7}, arch_sh_dsp_up},
+
+/* 0100nnnn01100111 ldc.l @<REG_N>+,RS */{"ldc.l",{A_INC_N,A_RS},{HEX_4,REG_N,HEX_6,HEX_7}, arch_sh_dsp_up},
+
+/* 0100nnnn00110111 ldc.l @<REG_N>+,SSR */{"ldc.l",{A_INC_N,A_SSR},{HEX_4,REG_N,HEX_3,HEX_7}, arch_sh3_nommu_up},
+
+/* 0100nnnn01000111 ldc.l @<REG_N>+,SPC */{"ldc.l",{A_INC_N,A_SPC},{HEX_4,REG_N,HEX_4,HEX_7}, arch_sh3_nommu_up},
+
+/* 0100nnnn11110110 ldc.l @<REG_N>+,DBR */{"ldc.l",{A_INC_N,A_DBR},{HEX_4,REG_N,HEX_F,HEX_6}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn1xxx0111 ldc.l <REG_N>,Rn_BANK */{"ldc.l",{A_INC_N,A_REG_B},{HEX_4,REG_N,REG_B,HEX_7}, arch_sh3_nommu_up},
+
+/* 0100mmmm00110100 ldrc <REG_M> */{"ldrc",{A_REG_M},{HEX_4,REG_M,HEX_3,HEX_4}, arch_sh4al_dsp_up},
+/* 10001010i8*1.... ldrc #<imm> */{"ldrc",{A_IMM},{HEX_8,HEX_A,IMM0_8}, arch_sh4al_dsp_up},
+
+/* 10001110i8p2.... ldre @(<disp>,PC) */{"ldre",{A_DISP_PC},{HEX_8,HEX_E,PCRELIMM_8BY2}, arch_sh_dsp_up},
+
+/* 10001100i8p2.... ldrs @(<disp>,PC) */{"ldrs",{A_DISP_PC},{HEX_8,HEX_C,PCRELIMM_8BY2}, arch_sh_dsp_up},
+
+/* 0100nnnn00001010 lds <REG_N>,MACH */{"lds",{A_REG_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_A}, arch_sh1_up},
+
+/* 0100nnnn00011010 lds <REG_N>,MACL */{"lds",{A_REG_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_A}, arch_sh1_up},
+
+/* 0100nnnn00101010 lds <REG_N>,PR */{"lds",{A_REG_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_A}, arch_sh1_up},
+
+/* 0100nnnn01101010 lds <REG_N>,DSR */{"lds",{A_REG_N,A_DSR},{HEX_4,REG_N,HEX_6,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn01111010 lds <REG_N>,A0 */{"lds",{A_REG_N,A_A0},{HEX_4,REG_N,HEX_7,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10001010 lds <REG_N>,X0 */{"lds",{A_REG_N,A_X0},{HEX_4,REG_N,HEX_8,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10011010 lds <REG_N>,X1 */{"lds",{A_REG_N,A_X1},{HEX_4,REG_N,HEX_9,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10101010 lds <REG_N>,Y0 */{"lds",{A_REG_N,A_Y0},{HEX_4,REG_N,HEX_A,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn10111010 lds <REG_N>,Y1 */{"lds",{A_REG_N,A_Y1},{HEX_4,REG_N,HEX_B,HEX_A}, arch_sh_dsp_up},
+
+/* 0100nnnn01011010 lds <REG_N>,FPUL */{"lds",{A_REG_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_A}, arch_sh2e_up},
+
+/* 0100nnnn01101010 lds <REG_M>,FPSCR */{"lds",{A_REG_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_A}, arch_sh2e_up},
+
+/* 0100nnnn00000110 lds.l @<REG_N>+,MACH*/{"lds.l",{A_INC_N,A_MACH},{HEX_4,REG_N,HEX_0,HEX_6}, arch_sh1_up},
+
+/* 0100nnnn00010110 lds.l @<REG_N>+,MACL*/{"lds.l",{A_INC_N,A_MACL},{HEX_4,REG_N,HEX_1,HEX_6}, arch_sh1_up},
+
+/* 0100nnnn00100110 lds.l @<REG_N>+,PR */{"lds.l",{A_INC_N,A_PR},{HEX_4,REG_N,HEX_2,HEX_6}, arch_sh1_up},
+
+/* 0100nnnn01100110 lds.l @<REG_N>+,DSR */{"lds.l",{A_INC_N,A_DSR},{HEX_4,REG_N,HEX_6,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn01110110 lds.l @<REG_N>+,A0 */{"lds.l",{A_INC_N,A_A0},{HEX_4,REG_N,HEX_7,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10000110 lds.l @<REG_N>+,X0 */{"lds.l",{A_INC_N,A_X0},{HEX_4,REG_N,HEX_8,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10010110 lds.l @<REG_N>+,X1 */{"lds.l",{A_INC_N,A_X1},{HEX_4,REG_N,HEX_9,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10100110 lds.l @<REG_N>+,Y0 */{"lds.l",{A_INC_N,A_Y0},{HEX_4,REG_N,HEX_A,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn10110110 lds.l @<REG_N>+,Y1 */{"lds.l",{A_INC_N,A_Y1},{HEX_4,REG_N,HEX_B,HEX_6}, arch_sh_dsp_up},
+
+/* 0100nnnn01010110 lds.l @<REG_M>+,FPUL*/{"lds.l",{A_INC_M,FPUL_N},{HEX_4,REG_M,HEX_5,HEX_6}, arch_sh2e_up},
+
+/* 0100nnnn01100110 lds.l @<REG_M>+,FPSCR*/{"lds.l",{A_INC_M,FPSCR_N},{HEX_4,REG_M,HEX_6,HEX_6}, arch_sh2e_up},
+
+/* 0000000000111000 ldtlb */{"ldtlb",{0},{HEX_0,HEX_0,HEX_3,HEX_8}, arch_sh3_up},
+
+/* 0100nnnnmmmm1111 mac.w @<REG_M>+,@<REG_N>+*/{"mac.w",{A_INC_M,A_INC_N},{HEX_4,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 1110nnnni8*1.... mov #<imm>,<REG_N> */{"mov",{A_IMM,A_REG_N},{HEX_E,REG_N,IMM0_8}, arch_sh1_up},
+
+/* 0110nnnnmmmm0011 mov <REG_M>,<REG_N> */{"mov",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_3}, arch_sh1_up},
+
+/* 0000nnnnmmmm0100 mov.b <REG_M>,@(R0,<REG_N>)*/{"mov.b",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0010nnnnmmmm0100 mov.b <REG_M>,@-<REG_N>*/{"mov.b",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0010nnnnmmmm0000 mov.b <REG_M>,@<REG_N>*/{"mov.b",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_0}, arch_sh1_up},
+
+/* 10000100mmmmi4*1 mov.b @(<disp>,<REG_M>),R0*/{"mov.b",{A_DISP_REG_M,A_R0},{HEX_8,HEX_4,REG_M,IMM0_4}, arch_sh1_up},
+
+/* 11000100i8*1.... mov.b @(<disp>,GBR),R0*/{"mov.b",{A_DISP_GBR,A_R0},{HEX_C,HEX_4,IMM0_8}, arch_sh1_up},
+
+/* 0000nnnnmmmm1100 mov.b @(R0,<REG_M>),<REG_N>*/{"mov.b",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_C}, arch_sh1_up},
+
+/* 0110nnnnmmmm0100 mov.b @<REG_M>+,<REG_N>*/{"mov.b",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_4}, arch_sh1_up},
+
+/* 0110nnnnmmmm0000 mov.b @<REG_M>,<REG_N>*/{"mov.b",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_0}, arch_sh1_up},
+
+/* 10000000mmmmi4*1 mov.b R0,@(<disp>,<REG_M>)*/{"mov.b",{A_R0,A_DISP_REG_M},{HEX_8,HEX_0,REG_M,IMM1_4}, arch_sh1_up},
+
+/* 11000000i8*1.... mov.b R0,@(<disp>,GBR)*/{"mov.b",{A_R0,A_DISP_GBR},{HEX_C,HEX_0,IMM1_8}, arch_sh1_up},
+
+/* 0100nnnn10001011 mov.b R0,@<REG_N>+ */{"mov.b",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_8,HEX_B}, arch_sh2a_nofpu_up},
+/* 0100nnnn11001011 mov.b @-<REG_M>,R0 */{"mov.b",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_C,HEX_B}, arch_sh2a_nofpu_up},
+/* 0011nnnnmmmm0001 0000dddddddddddd mov.b <REG_M>,@(<DISP12>,<REG_N>) */
+{"mov.b",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_0,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 0100dddddddddddd mov.b @(<DISP12>,<REG_M>),<REG_N> */
+{"mov.b",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_4,DISP0_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0001nnnnmmmmi4*4 mov.l <REG_M>,@(<disp>,<REG_N>)*/{"mov.l",{ A_REG_M,A_DISP_REG_N},{HEX_1,REG_N,REG_M,IMM1_4BY4}, arch_sh1_up},
+
+/* 0000nnnnmmmm0110 mov.l <REG_M>,@(R0,<REG_N>)*/{"mov.l",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0010nnnnmmmm0110 mov.l <REG_M>,@-<REG_N>*/{"mov.l",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0010nnnnmmmm0010 mov.l <REG_M>,@<REG_N>*/{"mov.l",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_2}, arch_sh1_up},
+
+/* 0101nnnnmmmmi4*4 mov.l @(<disp>,<REG_M>),<REG_N>*/{"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_5,REG_N,REG_M,IMM0_4BY4}, arch_sh1_up},
+
+/* 11000110i8*4.... mov.l @(<disp>,GBR),R0*/{"mov.l",{A_DISP_GBR,A_R0},{HEX_C,HEX_6,IMM0_8BY4}, arch_sh1_up},
+
+/* 1101nnnni8p4.... mov.l @(<disp>,PC),<REG_N>*/{"mov.l",{A_DISP_PC,A_REG_N},{HEX_D,REG_N,PCRELIMM_8BY4}, arch_sh1_up},
+
+/* 0000nnnnmmmm1110 mov.l @(R0,<REG_M>),<REG_N>*/{"mov.l",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0110nnnnmmmm0110 mov.l @<REG_M>+,<REG_N>*/{"mov.l",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_6}, arch_sh1_up},
+
+/* 0110nnnnmmmm0010 mov.l @<REG_M>,<REG_N>*/{"mov.l",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_2}, arch_sh1_up},
+
+/* 11000010i8*4.... mov.l R0,@(<disp>,GBR)*/{"mov.l",{A_R0,A_DISP_GBR},{HEX_C,HEX_2,IMM1_8BY4}, arch_sh1_up},
+
+/* 0100nnnn10101011 mov.l R0,@<REG_N>+ */{"mov.l",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_A,HEX_B}, arch_sh2a_nofpu_up},
+/* 0100nnnn11001011 mov.l @-<REG_M>,R0 */{"mov.l",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_B}, arch_sh2a_nofpu_up},
+/* 0011nnnnmmmm0001 0010dddddddddddd mov.l <REG_M>,@(<DISP12>,<REG_N>) */
+{"mov.l",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_2,DISP1_12BY4}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 0110dddddddddddd mov.l @(<DISP12>,<REG_M>),<REG_N> */
+{"mov.l",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_6,DISP0_12BY4}, arch_sh2a_nofpu_up | arch_op32},
+/* 0000nnnnmmmm0101 mov.w <REG_M>,@(R0,<REG_N>)*/{"mov.w",{ A_REG_M,A_IND_R0_REG_N},{HEX_0,REG_N,REG_M,HEX_5}, arch_sh1_up},
+
+/* 0010nnnnmmmm0101 mov.w <REG_M>,@-<REG_N>*/{"mov.w",{ A_REG_M,A_DEC_N},{HEX_2,REG_N,REG_M,HEX_5}, arch_sh1_up},
+
+/* 0010nnnnmmmm0001 mov.w <REG_M>,@<REG_N>*/{"mov.w",{ A_REG_M,A_IND_N},{HEX_2,REG_N,REG_M,HEX_1}, arch_sh1_up},
+
+/* 10000101mmmmi4*2 mov.w @(<disp>,<REG_M>),R0*/{"mov.w",{A_DISP_REG_M,A_R0},{HEX_8,HEX_5,REG_M,IMM0_4BY2}, arch_sh1_up},
+
+/* 11000101i8*2.... mov.w @(<disp>,GBR),R0*/{"mov.w",{A_DISP_GBR,A_R0},{HEX_C,HEX_5,IMM0_8BY2}, arch_sh1_up},
+
+/* 1001nnnni8p2.... mov.w @(<disp>,PC),<REG_N>*/{"mov.w",{A_DISP_PC,A_REG_N},{HEX_9,REG_N,PCRELIMM_8BY2}, arch_sh1_up},
+
+/* 0000nnnnmmmm1101 mov.w @(R0,<REG_M>),<REG_N>*/{"mov.w",{A_IND_R0_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_D}, arch_sh1_up},
+
+/* 0110nnnnmmmm0101 mov.w @<REG_M>+,<REG_N>*/{"mov.w",{A_INC_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_5}, arch_sh1_up},
+
+/* 0110nnnnmmmm0001 mov.w @<REG_M>,<REG_N>*/{"mov.w",{A_IND_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_1}, arch_sh1_up},
+
+/* 10000001mmmmi4*2 mov.w R0,@(<disp>,<REG_M>)*/{"mov.w",{A_R0,A_DISP_REG_M},{HEX_8,HEX_1,REG_M,IMM1_4BY2}, arch_sh1_up},
+
+/* 11000001i8*2.... mov.w R0,@(<disp>,GBR)*/{"mov.w",{A_R0,A_DISP_GBR},{HEX_C,HEX_1,IMM1_8BY2}, arch_sh1_up},
+
+/* 0100nnnn10011011 mov.w R0,@<REG_N>+ */{"mov.w",{A_R0,A_INC_N},{HEX_4,REG_N,HEX_9,HEX_B}, arch_sh2a_nofpu_up},
+/* 0100nnnn11011011 mov.w @-<REG_M>,R0 */{"mov.w",{A_DEC_M,A_R0},{HEX_4,REG_M,HEX_D,HEX_B}, arch_sh2a_nofpu_up},
+/* 0011nnnnmmmm0001 0001dddddddddddd mov.w <REG_M>,@(<DISP12>,<REG_N>) */
+{"mov.w",{A_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_1,DISP1_12BY2}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 0101dddddddddddd mov.w @(<DISP12>,<REG_M>),<REG_N> */
+{"mov.w",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_5,DISP0_12BY2}, arch_sh2a_nofpu_up | arch_op32},
+/* 11000111i8p4.... mova @(<disp>,PC),R0*/{"mova",{A_DISP_PC,A_R0},{HEX_C,HEX_7,PCRELIMM_8BY4}, arch_sh1_up},
+/* 0000nnnn11000011 movca.l R0,@<REG_N> */{"movca.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_C,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn01110011 movco.l r0,@<REG_N> */{"movco.l",{A_R0,A_IND_N},{HEX_0,REG_N,HEX_7,HEX_3}, arch_sh4a_nofp_up},
+/* 0000mmmm01100011 movli.l @<REG_M>,r0 */{"movli.l",{A_IND_M,A_R0},{HEX_0,REG_M,HEX_6,HEX_3}, arch_sh4a_nofp_up},
+
+/* 0000nnnn00101001 movt <REG_N> */{"movt",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_9}, arch_sh1_up},
+
+/* 0100mmmm10101001 movua.l @<REG_M>,r0 */{"movua.l",{A_IND_M,A_R0},{HEX_4,REG_M,HEX_A,HEX_9}, arch_sh4a_nofp_up},
+/* 0100mmmm11101001 movua.l @<REG_M>+,r0 */{"movua.l",{A_INC_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_9}, arch_sh4a_nofp_up},
+
+/* 0010nnnnmmmm1111 muls.w <REG_M>,<REG_N>*/{"muls.w",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}, arch_sh1_up},
+/* 0010nnnnmmmm1111 muls <REG_M>,<REG_N>*/{"muls",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_F}, arch_sh1_up},
+
+/* 0000nnnnmmmm0111 mul.l <REG_M>,<REG_N>*/{"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}, arch_sh2_up},
+
+/* 0010nnnnmmmm1110 mulu.w <REG_M>,<REG_N>*/{"mulu.w",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}, arch_sh1_up},
+/* 0010nnnnmmmm1110 mulu <REG_M>,<REG_N>*/{"mulu",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_E}, arch_sh1_up},
+
+/* 0110nnnnmmmm1011 neg <REG_M>,<REG_N> */{"neg",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_B}, arch_sh1_up},
+
+/* 0110nnnnmmmm1010 negc <REG_M>,<REG_N>*/{"negc",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_A}, arch_sh1_up},
+
+/* 0000000000001001 nop */{"nop",{0},{HEX_0,HEX_0,HEX_0,HEX_9}, arch_sh1_up},
+
+/* 0110nnnnmmmm0111 not <REG_M>,<REG_N> */{"not",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_7}, arch_sh1_up},
+/* 0000nnnn10010011 ocbi @<REG_N> */{"ocbi",{A_IND_N},{HEX_0,REG_N,HEX_9,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn10100011 ocbp @<REG_N> */{"ocbp",{A_IND_N},{HEX_0,REG_N,HEX_A,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn10110011 ocbwb @<REG_N> */{"ocbwb",{A_IND_N},{HEX_0,REG_N,HEX_B,HEX_3}, arch_sh4_nommu_nofpu_up},
+
+
+/* 11001011i8*1.... or #<imm>,R0 */{"or",{A_IMM,A_R0},{HEX_C,HEX_B,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1011 or <REG_M>,<REG_N> */{"or",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_B}, arch_sh1_up},
+
+/* 11001111i8*1.... or.b #<imm>,@(R0,GBR)*/{"or.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_F,IMM0_8}, arch_sh1_up},
+
+/* 0000nnnn10000011 pref @<REG_N> */{"pref",{A_IND_N},{HEX_0,REG_N,HEX_8,HEX_3}, arch_sh4_nommu_nofpu_up | arch_sh2a_nofpu_up},
+
+/* 0000nnnn11010011 prefi @<REG_N> */{"prefi",{A_IND_N},{HEX_0,REG_N,HEX_D,HEX_3}, arch_sh4a_nofp_up},
+
+/* 0100nnnn00100100 rotcl <REG_N> */{"rotcl",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_4}, arch_sh1_up},
+
+/* 0100nnnn00100101 rotcr <REG_N> */{"rotcr",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_5}, arch_sh1_up},
+
+/* 0100nnnn00000100 rotl <REG_N> */{"rotl",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_4}, arch_sh1_up},
+
+/* 0100nnnn00000101 rotr <REG_N> */{"rotr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_5}, arch_sh1_up},
+
+/* 0000000000101011 rte */{"rte",{0},{HEX_0,HEX_0,HEX_2,HEX_B}, arch_sh1_up},
+
+/* 0000000000001011 rts */{"rts",{0},{HEX_0,HEX_0,HEX_0,HEX_B}, arch_sh1_up},
+
+/* 0000000010011000 setdmx */{"setdmx",{0},{HEX_0,HEX_0,HEX_9,HEX_8}, arch_sh4al_dsp_up},
+/* 0000000011001000 setdmy */{"setdmy",{0},{HEX_0,HEX_0,HEX_C,HEX_8}, arch_sh4al_dsp_up},
+
+/* 0000000001011000 sets */{"sets",{0},{HEX_0,HEX_0,HEX_5,HEX_8}, arch_sh1_up},
+/* 0000000000011000 sett */{"sett",{0},{HEX_0,HEX_0,HEX_1,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00010100 setrc <REG_N> */{"setrc",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_4}, arch_sh_dsp_up},
+
+/* 10000010i8*1.... setrc #<imm> */{"setrc",{A_IMM},{HEX_8,HEX_2,IMM0_8}, arch_sh_dsp_up},
+
+/* repeat start end <REG_N> */{"repeat",{A_DISP_PC,A_DISP_PC,A_REG_N},{REPEAT,REG_N,HEX_1,HEX_4}, arch_sh_dsp_up},
+
+/* repeat start end #<imm> */{"repeat",{A_DISP_PC,A_DISP_PC,A_IMM},{REPEAT,HEX_2,IMM0_8,HEX_8}, arch_sh_dsp_up},
+
+/* 0100nnnnmmmm1100 shad <REG_M>,<REG_N>*/{"shad",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_C}, arch_sh3_nommu_up | arch_sh2a_nofpu_up},
+
+/* 0100nnnnmmmm1101 shld <REG_M>,<REG_N>*/{"shld",{ A_REG_M,A_REG_N},{HEX_4,REG_N,REG_M,HEX_D}, arch_sh3_nommu_up | arch_sh2a_nofpu_up},
+
+/* 0100nnnn00100000 shal <REG_N> */{"shal",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_0}, arch_sh1_up},
+
+/* 0100nnnn00100001 shar <REG_N> */{"shar",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_1}, arch_sh1_up},
+
+/* 0100nnnn00000000 shll <REG_N> */{"shll",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_0}, arch_sh1_up},
+
+/* 0100nnnn00101000 shll16 <REG_N> */{"shll16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00001000 shll2 <REG_N> */{"shll2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00011000 shll8 <REG_N> */{"shll8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_8}, arch_sh1_up},
+
+/* 0100nnnn00000001 shlr <REG_N> */{"shlr",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_1}, arch_sh1_up},
+
+/* 0100nnnn00101001 shlr16 <REG_N> */{"shlr16",{A_REG_N},{HEX_4,REG_N,HEX_2,HEX_9}, arch_sh1_up},
+
+/* 0100nnnn00001001 shlr2 <REG_N> */{"shlr2",{A_REG_N},{HEX_4,REG_N,HEX_0,HEX_9}, arch_sh1_up},
+
+/* 0100nnnn00011001 shlr8 <REG_N> */{"shlr8",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_9}, arch_sh1_up},
+
+/* 0000000000011011 sleep */{"sleep",{0},{HEX_0,HEX_0,HEX_1,HEX_B}, arch_sh1_up},
+
+/* 0000nnnn00000010 stc SR,<REG_N> */{"stc",{A_SR,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_2}, arch_sh1_up},
+
+/* 0000nnnn00010010 stc GBR,<REG_N> */{"stc",{A_GBR,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_2}, arch_sh1_up},
+
+/* 0000nnnn00100010 stc VBR,<REG_N> */{"stc",{A_VBR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_2}, arch_sh1_up},
+
+/* 0000nnnn01010010 stc MOD,<REG_N> */{"stc",{A_MOD,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_2}, arch_sh_dsp_up},
+
+/* 0000nnnn01110010 stc RE,<REG_N> */{"stc",{A_RE,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_2}, arch_sh_dsp_up},
+
+/* 0000nnnn01100010 stc RS,<REG_N> */{"stc",{A_RS,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_2}, arch_sh_dsp_up},
+
+/* 0000nnnn00110010 stc SSR,<REG_N> */{"stc",{A_SSR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_2}, arch_sh3_nommu_up},
+
+/* 0000nnnn01000010 stc SPC,<REG_N> */{"stc",{A_SPC,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_2}, arch_sh3_nommu_up},
+
+/* 0000nnnn00111010 stc SGR,<REG_N> */{"stc",{A_SGR,A_REG_N},{HEX_0,REG_N,HEX_3,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn11111010 stc DBR,<REG_N> */{"stc",{A_DBR,A_REG_N},{HEX_0,REG_N,HEX_F,HEX_A}, arch_sh4_nommu_nofpu_up},
+
+/* 0000nnnn1xxx0010 stc Rn_BANK,<REG_N> */{"stc",{A_REG_B,A_REG_N},{HEX_0,REG_N,REG_B,HEX_2}, arch_sh3_nommu_up},
+
+/* 0000nnnn01001010 stc TBR,<REG_N> */ {"stc",{A_TBR,A_REG_N},{HEX_0,REG_N,HEX_4,HEX_A}, arch_sh2a_nofpu_up},
+
+/* 0100nnnn00000011 stc.l SR,@-<REG_N> */{"stc.l",{A_SR,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_3}, arch_sh1_up},
+
+/* 0100nnnn00100011 stc.l VBR,@-<REG_N> */{"stc.l",{A_VBR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_3}, arch_sh1_up},
+
+/* 0100nnnn01010011 stc.l MOD,@-<REG_N> */{"stc.l",{A_MOD,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_3}, arch_sh_dsp_up},
+
+/* 0100nnnn01110011 stc.l RE,@-<REG_N> */{"stc.l",{A_RE,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_3}, arch_sh_dsp_up},
+
+/* 0100nnnn01100011 stc.l RS,@-<REG_N> */{"stc.l",{A_RS,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_3}, arch_sh_dsp_up},
+
+/* 0100nnnn00110011 stc.l SSR,@-<REG_N> */{"stc.l",{A_SSR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_3}, arch_sh3_nommu_up},
+
+/* 0100nnnn01000011 stc.l SPC,@-<REG_N> */{"stc.l",{A_SPC,A_DEC_N},{HEX_4,REG_N,HEX_4,HEX_3}, arch_sh3_nommu_up},
+
+/* 0100nnnn00010011 stc.l GBR,@-<REG_N> */{"stc.l",{A_GBR,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_3}, arch_sh1_up},
+
+/* 0100nnnn00110010 stc.l SGR,@-<REG_N> */{"stc.l",{A_SGR,A_DEC_N},{HEX_4,REG_N,HEX_3,HEX_2}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn11110010 stc.l DBR,@-<REG_N> */{"stc.l",{A_DBR,A_DEC_N},{HEX_4,REG_N,HEX_F,HEX_2}, arch_sh4_nommu_nofpu_up},
+
+/* 0100nnnn1xxx0011 stc.l Rn_BANK,@-<REG_N> */{"stc.l",{A_REG_B,A_DEC_N},{HEX_4,REG_N,REG_B,HEX_3}, arch_sh3_nommu_up},
+
+/* 0000nnnn00001010 sts MACH,<REG_N> */{"sts",{A_MACH,A_REG_N},{HEX_0,REG_N,HEX_0,HEX_A}, arch_sh1_up},
+
+/* 0000nnnn00011010 sts MACL,<REG_N> */{"sts",{A_MACL,A_REG_N},{HEX_0,REG_N,HEX_1,HEX_A}, arch_sh1_up},
+
+/* 0000nnnn00101010 sts PR,<REG_N> */{"sts",{A_PR,A_REG_N},{HEX_0,REG_N,HEX_2,HEX_A}, arch_sh1_up},
+
+/* 0000nnnn01101010 sts DSR,<REG_N> */{"sts",{A_DSR,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn01111010 sts A0,<REG_N> */{"sts",{A_A0,A_REG_N},{HEX_0,REG_N,HEX_7,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10001010 sts X0,<REG_N> */{"sts",{A_X0,A_REG_N},{HEX_0,REG_N,HEX_8,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10011010 sts X1,<REG_N> */{"sts",{A_X1,A_REG_N},{HEX_0,REG_N,HEX_9,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10101010 sts Y0,<REG_N> */{"sts",{A_Y0,A_REG_N},{HEX_0,REG_N,HEX_A,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn10111010 sts Y1,<REG_N> */{"sts",{A_Y1,A_REG_N},{HEX_0,REG_N,HEX_B,HEX_A}, arch_sh_dsp_up},
+
+/* 0000nnnn01011010 sts FPUL,<REG_N> */{"sts",{FPUL_M,A_REG_N},{HEX_0,REG_N,HEX_5,HEX_A}, arch_sh2e_up},
+
+/* 0000nnnn01101010 sts FPSCR,<REG_N> */{"sts",{FPSCR_M,A_REG_N},{HEX_0,REG_N,HEX_6,HEX_A}, arch_sh2e_up},
+
+/* 0100nnnn00000010 sts.l MACH,@-<REG_N>*/{"sts.l",{A_MACH,A_DEC_N},{HEX_4,REG_N,HEX_0,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn00010010 sts.l MACL,@-<REG_N>*/{"sts.l",{A_MACL,A_DEC_N},{HEX_4,REG_N,HEX_1,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn00100010 sts.l PR,@-<REG_N> */{"sts.l",{A_PR,A_DEC_N},{HEX_4,REG_N,HEX_2,HEX_2}, arch_sh1_up},
+
+/* 0100nnnn01100110 sts.l DSR,@-<REG_N> */{"sts.l",{A_DSR,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn01110110 sts.l A0,@-<REG_N> */{"sts.l",{A_A0,A_DEC_N},{HEX_4,REG_N,HEX_7,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10000110 sts.l X0,@-<REG_N> */{"sts.l",{A_X0,A_DEC_N},{HEX_4,REG_N,HEX_8,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10010110 sts.l X1,@-<REG_N> */{"sts.l",{A_X1,A_DEC_N},{HEX_4,REG_N,HEX_9,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10100110 sts.l Y0,@-<REG_N> */{"sts.l",{A_Y0,A_DEC_N},{HEX_4,REG_N,HEX_A,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn10110110 sts.l Y1,@-<REG_N> */{"sts.l",{A_Y1,A_DEC_N},{HEX_4,REG_N,HEX_B,HEX_2}, arch_sh_dsp_up},
+
+/* 0100nnnn01010010 sts.l FPUL,@-<REG_N>*/{"sts.l",{FPUL_M,A_DEC_N},{HEX_4,REG_N,HEX_5,HEX_2}, arch_sh2e_up},
+
+/* 0100nnnn01100010 sts.l FPSCR,@-<REG_N>*/{"sts.l",{FPSCR_M,A_DEC_N},{HEX_4,REG_N,HEX_6,HEX_2}, arch_sh2e_up},
+
+/* 0011nnnnmmmm1000 sub <REG_M>,<REG_N> */{"sub",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_8}, arch_sh1_up},
+
+/* 0011nnnnmmmm1010 subc <REG_M>,<REG_N>*/{"subc",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_A}, arch_sh1_up},
+
+/* 0011nnnnmmmm1011 subv <REG_M>,<REG_N>*/{"subv",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_B}, arch_sh1_up},
+
+/* 0110nnnnmmmm1000 swap.b <REG_M>,<REG_N>*/{"swap.b",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_8}, arch_sh1_up},
+
+/* 0110nnnnmmmm1001 swap.w <REG_M>,<REG_N>*/{"swap.w",{ A_REG_M,A_REG_N},{HEX_6,REG_N,REG_M,HEX_9}, arch_sh1_up},
+
+/* 0000000010101011 synco */{"synco",{0},{HEX_0,HEX_0,HEX_A,HEX_B}, arch_sh4a_nofp_up},
+
+/* 0100nnnn00011011 tas.b @<REG_N> */{"tas.b",{A_IND_N},{HEX_4,REG_N,HEX_1,HEX_B}, arch_sh1_up},
+
+/* 11000011i8*1.... trapa #<imm> */{"trapa",{A_IMM},{HEX_C,HEX_3,IMM0_8}, arch_sh1_up},
+
+/* 11001000i8*1.... tst #<imm>,R0 */{"tst",{A_IMM,A_R0},{HEX_C,HEX_8,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1000 tst <REG_M>,<REG_N> */{"tst",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_8}, arch_sh1_up},
+
+/* 11001100i8*1.... tst.b #<imm>,@(R0,GBR)*/{"tst.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_C,IMM0_8}, arch_sh1_up},
+
+/* 11001010i8*1.... xor #<imm>,R0 */{"xor",{A_IMM,A_R0},{HEX_C,HEX_A,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1010 xor <REG_M>,<REG_N> */{"xor",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_A}, arch_sh1_up},
+
+/* 11001110i8*1.... xor.b #<imm>,@(R0,GBR)*/{"xor.b",{A_IMM,A_R0_GBR},{HEX_C,HEX_E,IMM0_8}, arch_sh1_up},
+
+/* 0010nnnnmmmm1101 xtrct <REG_M>,<REG_N>*/{"xtrct",{ A_REG_M,A_REG_N},{HEX_2,REG_N,REG_M,HEX_D}, arch_sh1_up},
+
+/* 0000nnnnmmmm0111 mul.l <REG_M>,<REG_N>*/{"mul.l",{ A_REG_M,A_REG_N},{HEX_0,REG_N,REG_M,HEX_7}, arch_sh1_up},
+
+/* 0100nnnn00010000 dt <REG_N> */{"dt",{A_REG_N},{HEX_4,REG_N,HEX_1,HEX_0}, arch_sh2_up},
+
+/* 0011nnnnmmmm1101 dmuls.l <REG_M>,<REG_N>*/{"dmuls.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_D}, arch_sh2_up},
+
+/* 0011nnnnmmmm0101 dmulu.l <REG_M>,<REG_N>*/{"dmulu.l",{ A_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_5}, arch_sh2_up},
+
+/* 0000nnnnmmmm1111 mac.l @<REG_M>+,@<REG_N>+*/{"mac.l",{A_INC_M,A_INC_N},{HEX_0,REG_N,REG_M,HEX_F}, arch_sh2_up},
+
+/* 0000nnnn00100011 braf <REG_N> */{"braf",{A_REG_N},{HEX_0,REG_N,HEX_2,HEX_3}, arch_sh2_up},
+
+/* 0000nnnn00000011 bsrf <REG_N> */{"bsrf",{A_REG_N},{HEX_0,REG_N,HEX_0,HEX_3}, arch_sh2_up},
+
+/* 111101nnmmmm0000 movs.w @-<REG_N>,<DSP_REG_M> */ {"movs.w",{A_DEC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_0}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0001 movs.w @<REG_N>,<DSP_REG_M> */ {"movs.w",{A_IND_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_4}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0010 movs.w @<REG_N>+,<DSP_REG_M> */ {"movs.w",{A_INC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_8}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0011 movs.w @<REG_N>+r8,<DSP_REG_M> */ {"movs.w",{AS_PMOD_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_C}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0100 movs.w <DSP_REG_M>,@-<REG_N> */ {"movs.w",{DSP_REG_M,A_DEC_N},{HEX_F,SDT_REG_N,REG_M,HEX_1}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0101 movs.w <DSP_REG_M>,@<REG_N> */ {"movs.w",{DSP_REG_M,A_IND_N},{HEX_F,SDT_REG_N,REG_M,HEX_5}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0110 movs.w <DSP_REG_M>,@<REG_N>+ */ {"movs.w",{DSP_REG_M,A_INC_N},{HEX_F,SDT_REG_N,REG_M,HEX_9}, arch_sh_dsp_up},
+
+/* 111101nnmmmm0111 movs.w <DSP_REG_M>,@<REG_N>+r8 */ {"movs.w",{DSP_REG_M,AS_PMOD_N},{HEX_F,SDT_REG_N,REG_M,HEX_D}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1000 movs.l @-<REG_N>,<DSP_REG_M> */ {"movs.l",{A_DEC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_2}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1001 movs.l @<REG_N>,<DSP_REG_M> */ {"movs.l",{A_IND_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_6}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1010 movs.l @<REG_N>+,<DSP_REG_M> */ {"movs.l",{A_INC_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_A}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1011 movs.l @<REG_N>+r8,<DSP_REG_M> */ {"movs.l",{AS_PMOD_N,DSP_REG_M},{HEX_F,SDT_REG_N,REG_M,HEX_E}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1100 movs.l <DSP_REG_M>,@-<REG_N> */ {"movs.l",{DSP_REG_M,A_DEC_N},{HEX_F,SDT_REG_N,REG_M,HEX_3}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1101 movs.l <DSP_REG_M>,@<REG_N> */ {"movs.l",{DSP_REG_M,A_IND_N},{HEX_F,SDT_REG_N,REG_M,HEX_7}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1110 movs.l <DSP_REG_M>,@<REG_N>+ */ {"movs.l",{DSP_REG_M,A_INC_N},{HEX_F,SDT_REG_N,REG_M,HEX_B}, arch_sh_dsp_up},
+
+/* 111101nnmmmm1111 movs.l <DSP_REG_M>,@<REG_N>+r8 */ {"movs.l",{DSP_REG_M,AS_PMOD_N},{HEX_F,SDT_REG_N,REG_M,HEX_F}, arch_sh_dsp_up},
+
+/* 0*0*0*00** nopx */ {"nopx",{0},{PPI,NOPX}, arch_sh_dsp_up},
+/* *0*0*0**00 nopy */ {"nopy",{0},{PPI,NOPY}, arch_sh_dsp_up},
+/* n*m*0*01** movx.w @<REG_N>,<DSP_REG_X> */ {"movx.w",{AX_IND_N,DSP_REG_X},{PPI,MOVX,HEX_1}, arch_sh_dsp_up},
+/* n*m*0*10** movx.w @<REG_N>+,<DSP_REG_X> */ {"movx.w",{AX_INC_N,DSP_REG_X},{PPI,MOVX,HEX_2}, arch_sh_dsp_up},
+/* n*m*0*11** movx.w @<REG_N>+r8,<DSP_REG_X> */ {"movx.w",{AX_PMOD_N,DSP_REG_X},{PPI,MOVX,HEX_3}, arch_sh_dsp_up},
+/* n*m*1*01** movx.w <DSP_REG_M>,@<REG_N> */ {"movx.w",{DSP_REG_A_M,AX_IND_N},{PPI,MOVX,HEX_9}, arch_sh_dsp_up},
+/* n*m*1*10** movx.w <DSP_REG_M>,@<REG_N>+ */ {"movx.w",{DSP_REG_A_M,AX_INC_N},{PPI,MOVX,HEX_A}, arch_sh_dsp_up},
+/* n*m*1*11** movx.w <DSP_REG_M>,@<REG_N>+r8 */ {"movx.w",{DSP_REG_A_M,AX_PMOD_N},{PPI,MOVX,HEX_B}, arch_sh_dsp_up},
+
+/* nnmm000100 movx.w @<REG_Axy>,<DSP_REG_XY> */ {"movx.w",{AXY_IND_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm001000 movx.w @<REG_Axy>+,<DSP_REG_XY> */{"movx.w",{AXY_INC_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm001100 movx.w @<REG_Axy>+r8,<DSP_REG_XY> */{"movx.w",{AXY_PMOD_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_0,HEX_C}, arch_sh4al_dsp_up},
+/* nnmm100100 movx.w <DSP_REG_AX>,@<REG_Axy> */ {"movx.w",{DSP_REG_AX,AXY_IND_N},{PPI,MOVX_NOPY,HEX_2,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm101000 movx.w <DSP_REG_AX>,@<REG_Axy>+ */{"movx.w",{DSP_REG_AX,AXY_INC_N},{PPI,MOVX_NOPY,HEX_2,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm101100 movx.w <DSP_REG_AX>,@<REG_Axy>+r8 */{"movx.w",{DSP_REG_AX,AXY_PMOD_N},{PPI,MOVX_NOPY,HEX_2,HEX_C}, arch_sh4al_dsp_up},
+
+/* nnmm010100 movx.l @<REG_Axy>,<DSP_REG_XY> */ {"movx.l",{AXY_IND_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm011000 movx.l @<REG_Axy>+,<DSP_REG_XY> */{"movx.l",{AXY_INC_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm011100 movx.l @<REG_Axy>+r8,<DSP_REG_XY> */{"movx.l",{AXY_PMOD_N,DSP_REG_XY},{PPI,MOVX_NOPY,HEX_1,HEX_C}, arch_sh4al_dsp_up},
+/* nnmm110100 movx.l <DSP_REG_AX>,@<REG_Axy> */ {"movx.l",{DSP_REG_AX,AXY_IND_N},{PPI,MOVX_NOPY,HEX_3,HEX_4}, arch_sh4al_dsp_up},
+/* nnmm111000 movx.l <DSP_REG_AX>,@<REG_Axy>+ */{"movx.l",{DSP_REG_AX,AXY_INC_N},{PPI,MOVX_NOPY,HEX_3,HEX_8}, arch_sh4al_dsp_up},
+/* nnmm111100 movx.l <DSP_REG_AX>,@<REG_Axy>+r8 */{"movx.l",{DSP_REG_AX,AXY_PMOD_N},{PPI,MOVX_NOPY,HEX_3,HEX_C}, arch_sh4al_dsp_up},
+
+/* *n*m*0**01 movy.w @<REG_N>,<DSP_REG_Y> */ {"movy.w",{AY_IND_N,DSP_REG_Y},{PPI,MOVY,HEX_1}, arch_sh_dsp_up},
+/* *n*m*0**10 movy.w @<REG_N>+,<DSP_REG_Y> */ {"movy.w",{AY_INC_N,DSP_REG_Y},{PPI,MOVY,HEX_2}, arch_sh_dsp_up},
+/* *n*m*0**11 movy.w @<REG_N>+r9,<DSP_REG_Y> */ {"movy.w",{AY_PMOD_N,DSP_REG_Y},{PPI,MOVY,HEX_3}, arch_sh_dsp_up},
+/* *n*m*1**01 movy.w <DSP_REG_M>,@<REG_N> */ {"movy.w",{DSP_REG_A_M,AY_IND_N},{PPI,MOVY,HEX_9}, arch_sh_dsp_up},
+/* *n*m*1**10 movy.w <DSP_REG_M>,@<REG_N>+ */ {"movy.w",{DSP_REG_A_M,AY_INC_N},{PPI,MOVY,HEX_A}, arch_sh_dsp_up},
+/* *n*m*1**11 movy.w <DSP_REG_M>,@<REG_N>+r9 */ {"movy.w",{DSP_REG_A_M,AY_PMOD_N},{PPI,MOVY,HEX_B}, arch_sh_dsp_up},
+
+/* nnmm000001 movy.w @<REG_Ayx>,<DSP_REG_YX> */ {"movy.w",{AYX_IND_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm000010 movy.w @<REG_Ayx>+,<DSP_REG_YX> */{"movy.w",{AYX_INC_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm000011 movy.w @<REG_Ayx>+r8,<DSP_REG_YX> */{"movy.w",{AYX_PMOD_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_0,HEX_3}, arch_sh4al_dsp_up},
+/* nnmm010001 movy.w <DSP_REG_AY>,@<REG_Ayx> */ {"movy.w",{DSP_REG_AY,AYX_IND_N},{PPI,MOVY_NOPX,HEX_1,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm010010 movy.w <DSP_REG_AY>,@<REG_Ayx>+ */{"movy.w",{DSP_REG_AY,AYX_INC_N},{PPI,MOVY_NOPX,HEX_1,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm010011 movy.w <DSP_REG_AY>,@<REG_Ayx>+r8 */{"movy.w",{DSP_REG_AY,AYX_PMOD_N},{PPI,MOVY_NOPX,HEX_1,HEX_3}, arch_sh4al_dsp_up},
+
+/* nnmm100001 movy.l @<REG_Ayx>,<DSP_REG_YX> */ {"movy.l",{AYX_IND_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm100010 movy.l @<REG_Ayx>+,<DSP_REG_YX> */{"movy.l",{AYX_INC_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm100011 movy.l @<REG_Ayx>+r8,<DSP_REG_YX> */{"movy.l",{AYX_PMOD_N,DSP_REG_YX},{PPI,MOVY_NOPX,HEX_2,HEX_3}, arch_sh4al_dsp_up},
+/* nnmm110001 movy.l <DSP_REG_AY>,@<REG_Ayx> */ {"movy.l",{DSP_REG_AY,AYX_IND_N},{PPI,MOVY_NOPX,HEX_3,HEX_1}, arch_sh4al_dsp_up},
+/* nnmm110010 movy.l <DSP_REG_AY>,@<REG_Ayx>+ */{"movy.l",{DSP_REG_AY,AYX_INC_N},{PPI,MOVY_NOPX,HEX_3,HEX_2}, arch_sh4al_dsp_up},
+/* nnmm110011 movy.l <DSP_REG_AY>,@<REG_Ayx>+r8 */{"movy.l",{DSP_REG_AY,AYX_PMOD_N},{PPI,MOVY_NOPX,HEX_3,HEX_3}, arch_sh4al_dsp_up},
+
+/* 01aaeeffxxyyggnn pmuls Se,Sf,Dg */ {"pmuls",{DSP_REG_E,DSP_REG_F,DSP_REG_G},{PPI,PMUL}, arch_sh_dsp_up},
+/* 10100000xxyynnnn psubc <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"psubc",{DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_A,HEX_0}, arch_sh_dsp_up},
+/* 10110000xxyynnnn paddc <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"paddc",{DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_B,HEX_0}, arch_sh_dsp_up},
+/* 10000100xxyynnnn pcmp <DSP_REG_X>,<DSP_REG_Y> */
+{"pcmp", {DSP_REG_X,DSP_REG_Y},{PPI,PPI3,HEX_8,HEX_4}, arch_sh_dsp_up},
+/* 10100100xxyynnnn pwsb <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pwsb", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_A,HEX_4}, arch_sh_dsp_up},
+/* 10110100xxyynnnn pwad <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pwad", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPI3,HEX_B,HEX_4}, arch_sh_dsp_up},
+/* 10001000xxyynnnn pabs <DSP_REG_X>,<DSP_REG_N> */
+{"pabs", {DSP_REG_X,DSP_REG_N},{PPI,PPI3NC,HEX_8,HEX_8}, arch_sh_dsp_up},
+/* 1000100!xx01nnnn pabs <DSP_REG_X>,<DSP_REG_N> */
+{"pabs", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_9,HEX_1}, arch_sh4al_dsp_up},
+/* 10101000xxyynnnn pabs <DSP_REG_Y>,<DSP_REG_N> */
+{"pabs", {DSP_REG_Y,DSP_REG_N},{PPI,PPI3NC,HEX_A,HEX_8}, arch_sh_dsp_up},
+/* 1010100!01yynnnn pabs <DSP_REG_Y>,<DSP_REG_N> */
+{"pabs", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_9,HEX_4}, arch_sh4al_dsp_up},
+/* 10011000xxyynnnn prnd <DSP_REG_X>,<DSP_REG_N> */
+{"prnd", {DSP_REG_X,DSP_REG_N},{PPI,PPI3NC,HEX_9,HEX_8}, arch_sh_dsp_up},
+/* 1001100!xx01nnnn prnd <DSP_REG_X>,<DSP_REG_N> */
+{"prnd", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_9,HEX_1}, arch_sh4al_dsp_up},
+/* 10111000xxyynnnn prnd <DSP_REG_Y>,<DSP_REG_N> */
+{"prnd", {DSP_REG_Y,DSP_REG_N},{PPI,PPI3NC,HEX_B,HEX_8}, arch_sh_dsp_up},
+/* 1011100!01yynnnn prnd <DSP_REG_Y>,<DSP_REG_N> */
+{"prnd", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_9,HEX_4}, arch_sh4al_dsp_up},
+
+{"dct",{0},{PPI,PDC,HEX_1}, arch_sh_dsp_up},
+{"dcf",{0},{PPI,PDC,HEX_2}, arch_sh_dsp_up},
+
+/* 10000001xxyynnnn pshl <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pshl", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_1}, arch_sh_dsp_up},
+/* 00000iiiiiiinnnn pshl #<imm>,<DSP_REG_N> */ {"pshl",{A_IMM,DSP_REG_N},{PPI,PSH,HEX_0}, arch_sh_dsp_up},
+/* 10010001xxyynnnn psha <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"psha", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_1}, arch_sh_dsp_up},
+/* 00010iiiiiiinnnn psha #<imm>,<DSP_REG_N> */ {"psha",{A_IMM,DSP_REG_N},{PPI,PSH,HEX_1}, arch_sh_dsp_up},
+/* 10100001xxyynnnn psub <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"psub", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_1}, arch_sh_dsp_up},
+/* 10000101xxyynnnn psub <DSP_REG_Y>,<DSP_REG_X>,<DSP_REG_N> */
+{"psub", {DSP_REG_Y,DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_5}, arch_sh4al_dsp_up},
+/* 10110001xxyynnnn padd <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"padd", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_1}, arch_sh_dsp_up},
+/* 10010101xxyynnnn pand <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pand", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_5}, arch_sh_dsp_up},
+/* 10100101xxyynnnn pxor <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"pxor", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_5}, arch_sh_dsp_up},
+/* 10110101xxyynnnn por <DSP_REG_X>,<DSP_REG_Y>,<DSP_REG_N> */
+{"por", {DSP_REG_X,DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_5}, arch_sh_dsp_up},
+/* 10001001xxyynnnn pdec <DSP_REG_X>,<DSP_REG_N> */
+{"pdec", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_8,HEX_9}, arch_sh_dsp_up},
+/* 10101001xxyynnnn pdec <DSP_REG_Y>,<DSP_REG_N> */
+{"pdec", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_A,HEX_9}, arch_sh_dsp_up},
+/* 10011001xx00nnnn pinc <DSP_REG_X>,<DSP_REG_N> */
+{"pinc", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_9,HEX_XX00}, arch_sh_dsp_up},
+/* 1011100100yynnnn pinc <DSP_REG_Y>,<DSP_REG_N> */
+{"pinc", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_9,HEX_00YY}, arch_sh_dsp_up},
+/* 10001101xxyynnnn pclr <DSP_REG_N> */
+{"pclr", {DSP_REG_N},{PPI,PPIC,HEX_8,HEX_D}, arch_sh_dsp_up},
+/* 10011101xx00nnnn pdmsb <DSP_REG_X>,<DSP_REG_N> */
+{"pdmsb", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_D,HEX_XX00}, arch_sh_dsp_up},
+/* 1011110100yynnnn pdmsb <DSP_REG_Y>,<DSP_REG_N> */
+{"pdmsb", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_D,HEX_00YY}, arch_sh_dsp_up},
+/* 11001001xxyynnnn pneg <DSP_REG_X>,<DSP_REG_N> */
+{"pneg", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_C,HEX_9}, arch_sh_dsp_up},
+/* 11101001xxyynnnn pneg <DSP_REG_Y>,<DSP_REG_N> */
+{"pneg", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_E,HEX_9}, arch_sh_dsp_up},
+/* 11011001xxyynnnn pcopy <DSP_REG_X>,<DSP_REG_N> */
+{"pcopy", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_D,HEX_9}, arch_sh_dsp_up},
+/* 11111001xxyynnnn pcopy <DSP_REG_Y>,<DSP_REG_N> */
+{"pcopy", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_F,HEX_9}, arch_sh_dsp_up},
+/* 11001101xxyynnnn psts MACH,<DSP_REG_N> */
+{"psts", {A_MACH,DSP_REG_N},{PPI,PPIC,HEX_C,HEX_D}, arch_sh_dsp_up},
+/* 11011101xxyynnnn psts MACL,<DSP_REG_N> */
+{"psts", {A_MACL,DSP_REG_N},{PPI,PPIC,HEX_D,HEX_D}, arch_sh_dsp_up},
+/* 11101101xxyynnnn plds <DSP_REG_N>,MACH */
+{"plds", {DSP_REG_N,A_MACH},{PPI,PPIC,HEX_E,HEX_D}, arch_sh_dsp_up},
+/* 11111101xxyynnnn plds <DSP_REG_N>,MACL */
+{"plds", {DSP_REG_N,A_MACL},{PPI,PPIC,HEX_F,HEX_D}, arch_sh_dsp_up},
+/* 10011101xx01zzzz pswap <DSP_REG_X>,<DSP_REG_N> */
+{"pswap", {DSP_REG_X,DSP_REG_N},{PPI,PPIC,HEX_9,HEX_D,HEX_1}, arch_sh4al_dsp_up},
+/* 1011110101yyzzzz pswap <DSP_REG_Y>,<DSP_REG_N> */
+{"pswap", {DSP_REG_Y,DSP_REG_N},{PPI,PPIC,HEX_B,HEX_D,HEX_4}, arch_sh4al_dsp_up},
+
+/* 1111nnnn01011101 fabs <F_REG_N> */{"fabs",{F_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}, arch_sh2e_up},
+/* 1111nnn001011101 fabs <D_REG_N> */{"fabs",{D_REG_N},{HEX_F,REG_N,HEX_5,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0000 fadd <F_REG_M>,<F_REG_N>*/{"fadd",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_0}, arch_sh2e_up},
+/* 1111nnn0mmm00000 fadd <D_REG_M>,<D_REG_N>*/{"fadd",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_0}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0100 fcmp/eq <F_REG_M>,<F_REG_N>*/{"fcmp/eq",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_4}, arch_sh2e_up},
+/* 1111nnn0mmm00100 fcmp/eq <D_REG_M>,<D_REG_N>*/{"fcmp/eq",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_4}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0101 fcmp/gt <F_REG_M>,<F_REG_N>*/{"fcmp/gt",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_5}, arch_sh2e_up},
+/* 1111nnn0mmm00101 fcmp/gt <D_REG_M>,<D_REG_N>*/{"fcmp/gt",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_5}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn010111101 fcnvds <D_REG_N>,FPUL*/{"fcnvds",{D_REG_N,FPUL_M},{HEX_F,REG_N_D,HEX_B,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn010101101 fcnvsd FPUL,<D_REG_N>*/{"fcnvsd",{FPUL_M,D_REG_N},{HEX_F,REG_N_D,HEX_A,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0011 fdiv <F_REG_M>,<F_REG_N>*/{"fdiv",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_3}, arch_sh2e_up},
+/* 1111nnn0mmm00011 fdiv <D_REG_M>,<D_REG_N>*/{"fdiv",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_3}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnmm11101101 fipr <V_REG_M>,<V_REG_N>*/{"fipr",{V_REG_M,V_REG_N},{HEX_F,REG_NM,HEX_E,HEX_D}, arch_sh4_up},
+
+/* 1111nnnn10001101 fldi0 <F_REG_N> */{"fldi0",{F_REG_N},{HEX_F,REG_N,HEX_8,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnn10011101 fldi1 <F_REG_N> */{"fldi1",{F_REG_N},{HEX_F,REG_N,HEX_9,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnn00011101 flds <F_REG_N>,FPUL*/{"flds",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_1,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnn00101101 float FPUL,<F_REG_N>*/{"float",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}, arch_sh2e_up},
+/* 1111nnn000101101 float FPUL,<D_REG_N>*/{"float",{FPUL_M,D_REG_N},{HEX_F,REG_N,HEX_2,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1110 fmac FR0,<F_REG_M>,<F_REG_N>*/{"fmac",{F_FR0,F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_E}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1100 fmov <F_REG_M>,<F_REG_N>*/{"fmov",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_C}, arch_sh2e_up},
+/* 1111nnn1mmmm1100 fmov <DX_REG_M>,<DX_REG_N>*/{"fmov",{DX_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_C}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1000 fmov @<REG_M>,<F_REG_N>*/{"fmov",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh2e_up},
+/* 1111nnn1mmmm1000 fmov @<REG_M>,<DX_REG_N>*/{"fmov",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1010 fmov <F_REG_M>,@<REG_N>*/{"fmov",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh2e_up},
+/* 1111nnnnmmm11010 fmov <DX_REG_M>,@<REG_N>*/{"fmov",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1001 fmov @<REG_M>+,<F_REG_N>*/{"fmov",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh2e_up},
+/* 1111nnn1mmmm1001 fmov @<REG_M>+,<DX_REG_N>*/{"fmov",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm1011 fmov <F_REG_M>,@-<REG_N>*/{"fmov",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh2e_up},
+/* 1111nnnnmmm11011 fmov <DX_REG_M>,@-<REG_N>*/{"fmov",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0110 fmov @(R0,<REG_M>),<F_REG_N>*/{"fmov",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh2e_up},
+/* 1111nnn1mmmm0110 fmov @(R0,<REG_M>),<DX_REG_N>*/{"fmov",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmmm0111 fmov <F_REG_M>,@(R0,<REG_N>)*/{"fmov",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh2e_up},
+/* 1111nnnnmmm10111 fmov <DX_REG_M>,@(R0,<REG_N>)*/{"fmov",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn1mmmm1000 fmov.d @<REG_M>,<DX_REG_N>*/{"fmov.d",{A_IND_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmm11010 fmov.d <DX_REG_M>,@<REG_N>*/{"fmov.d",{DX_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn1mmmm1001 fmov.d @<REG_M>+,<DX_REG_N>*/{"fmov.d",{A_INC_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmm11011 fmov.d <DX_REG_M>,@-<REG_N>*/{"fmov.d",{DX_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnn1mmmm0110 fmov.d @(R0,<REG_M>),<DX_REG_N>*/{"fmov.d",{A_IND_R0_REG_M,DX_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnnmmm10111 fmov.d <DX_REG_M>,@(R0,<REG_N>)*/{"fmov.d",{DX_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh4_up | arch_sh2a_up},
+/* 0011nnnnmmmm0001 0011dddddddddddd fmov.d <F_REG_M>,@(<DISP12>,<REG_N>) */
+{"fmov.d",{DX_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_3,DISP1_12BY8}, arch_sh2a_up | arch_op32},
+/* 0011nnnnmmmm0001 0111dddddddddddd fmov.d @(<DISP12>,<REG_M>),F_REG_N */
+{"fmov.d",{A_DISP_REG_M,DX_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_7,DISP0_12BY8}, arch_sh2a_up | arch_op32},
+
+/* 1111nnnnmmmm1000 fmov.s @<REG_M>,<F_REG_N>*/{"fmov.s",{A_IND_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_8}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1010 fmov.s <F_REG_M>,@<REG_N>*/{"fmov.s",{F_REG_M,A_IND_N},{HEX_F,REG_N,REG_M,HEX_A}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1001 fmov.s @<REG_M>+,<F_REG_N>*/{"fmov.s",{A_INC_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_9}, arch_sh2e_up},
+
+/* 1111nnnnmmmm1011 fmov.s <F_REG_M>,@-<REG_N>*/{"fmov.s",{F_REG_M,A_DEC_N},{HEX_F,REG_N,REG_M,HEX_B}, arch_sh2e_up},
+
+/* 1111nnnnmmmm0110 fmov.s @(R0,<REG_M>),<F_REG_N>*/{"fmov.s",{A_IND_R0_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_6}, arch_sh2e_up},
+
+/* 1111nnnnmmmm0111 fmov.s <F_REG_M>,@(R0,<REG_N>)*/{"fmov.s",{F_REG_M,A_IND_R0_REG_N},{HEX_F,REG_N,REG_M,HEX_7}, arch_sh2e_up},
+/* 0011nnnnmmmm0001 0011dddddddddddd fmov.s <F_REG_M>,@(<DISP12>,<REG_N>) */
+{"fmov.s",{F_REG_M,A_DISP_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_3,DISP1_12BY4}, arch_sh2a_up | arch_op32},
+/* 0011nnnnmmmm0001 0111dddddddddddd fmov.s @(<DISP12>,<REG_M>),F_REG_N */
+{"fmov.s",{A_DISP_REG_M,F_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_7,DISP0_12BY4}, arch_sh2a_up | arch_op32},
+
+/* 1111nnnnmmmm0010 fmul <F_REG_M>,<F_REG_N>*/{"fmul",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_2}, arch_sh2e_up},
+/* 1111nnn0mmm00010 fmul <D_REG_M>,<D_REG_N>*/{"fmul",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_2}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn01001101 fneg <F_REG_N> */{"fneg",{F_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}, arch_sh2e_up},
+/* 1111nnn001001101 fneg <D_REG_N> */{"fneg",{D_REG_N},{HEX_F,REG_N,HEX_4,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111011111111101 fpchg */{"fpchg",{0},{HEX_F,HEX_7,HEX_F,HEX_D}, arch_sh4a_up},
+
+/* 1111101111111101 frchg */{"frchg",{0},{HEX_F,HEX_B,HEX_F,HEX_D}, arch_sh4_up},
+
+/* 1111nnn011111101 fsca FPUL,<D_REG_N> */{"fsca",{FPUL_M,D_REG_N},{HEX_F,REG_N_D,HEX_F,HEX_D}, arch_sh4_up},
+
+/* 1111001111111101 fschg */{"fschg",{0},{HEX_F,HEX_3,HEX_F,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn01101101 fsqrt <F_REG_N> */{"fsqrt",{F_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}, arch_sh3e_up | arch_sh2a_up},
+/* 1111nnn001101101 fsqrt <D_REG_N> */{"fsqrt",{D_REG_N},{HEX_F,REG_N,HEX_6,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn01111101 fsrra <F_REG_N> */{"fsrra",{F_REG_N},{HEX_F,REG_N,HEX_7,HEX_D}, arch_sh4_up},
+
+/* 1111nnnn00001101 fsts FPUL,<F_REG_N>*/{"fsts",{FPUL_M,F_REG_N},{HEX_F,REG_N,HEX_0,HEX_D}, arch_sh2e_up},
+
+/* 1111nnnnmmmm0001 fsub <F_REG_M>,<F_REG_N>*/{"fsub",{F_REG_M,F_REG_N},{HEX_F,REG_N,REG_M,HEX_1}, arch_sh2e_up},
+/* 1111nnn0mmm00001 fsub <D_REG_M>,<D_REG_N>*/{"fsub",{D_REG_M,D_REG_N},{HEX_F,REG_N,REG_M,HEX_1}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nnnn00111101 ftrc <F_REG_N>,FPUL*/{"ftrc",{F_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}, arch_sh2e_up},
+/* 1111nnnn00111101 ftrc <D_REG_N>,FPUL*/{"ftrc",{D_REG_N,FPUL_M},{HEX_F,REG_N,HEX_3,HEX_D}, arch_sh4_up | arch_sh2a_up},
+
+/* 1111nn0111111101 ftrv XMTRX_M4,<V_REG_n>*/{"ftrv",{XMTRX_M4,V_REG_N},{HEX_F,REG_N_B01,HEX_F,HEX_D}, arch_sh4_up},
+
+ /* 10000110nnnn0iii bclr #<imm>, <REG_N> */ {"bclr",{A_IMM, A_REG_N},{HEX_8,HEX_6,REG_N,IMM0_3c}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0000dddddddddddd bclr.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bclr.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_0,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 10000111nnnn1iii bld #<imm>, <REG_N> */ {"bld",{A_IMM, A_REG_N},{HEX_8,HEX_7,REG_N,IMM0_3s}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0011dddddddddddd bld.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bld.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_3,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 10000110nnnn1iii bset #<imm>, <REG_N> */ {"bset",{A_IMM, A_REG_N},{HEX_8,HEX_6,REG_N,IMM0_3s}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0001dddddddddddd bset.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bset.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_1,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 10000111nnnn0iii bst #<imm>, <REG_N> */ {"bst",{A_IMM, A_REG_N},{HEX_8,HEX_7,REG_N,IMM0_3c}, arch_sh2a_nofpu_up},
+ /* 0011nnnn0iii1001 0010dddddddddddd bst.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bst.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_2,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+ /* 0100nnnn10010001 clips.b <REG_N> */ {"clips.b",{A_REG_N},{HEX_4,REG_N,HEX_9,HEX_1}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10010101 clips.w <REG_N> */ {"clips.w",{A_REG_N},{HEX_4,REG_N,HEX_9,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000001 clipu.b <REG_N> */ {"clipu.b",{A_REG_N},{HEX_4,REG_N,HEX_8,HEX_1}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000101 clipu.w <REG_N> */ {"clipu.w",{A_REG_N},{HEX_4,REG_N,HEX_8,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10010100 divs R0,<REG_N> */ {"divs",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_9,HEX_4}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000100 divu R0,<REG_N> */ {"divu",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_8,HEX_4}, arch_sh2a_nofpu_up},
+ /* 0100mmmm01001011 jsr/n @<REG_M> */ {"jsr/n",{A_IND_M},{HEX_4,REG_M,HEX_4,HEX_B}, arch_sh2a_nofpu_up},
+ /* 10000011dddddddd jsr/n @@(<disp>,TBR) */ {"jsr/n",{A_DISP2_TBR},{HEX_8,HEX_3,IMM0_8BY4}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11100101 ldbank @<REG_M>,R0 */ {"ldbank",{A_IND_M,A_R0},{HEX_4,REG_M,HEX_E,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110001 movml.l <REG_M>,@-R15 */ {"movml.l",{A_REG_M,A_DEC_R15},{HEX_4,REG_M,HEX_F,HEX_1}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110101 movml.l @R15+,<REG_M> */ {"movml.l",{A_INC_R15,A_REG_M},{HEX_4,REG_M,HEX_F,HEX_5}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110000 movml.l <REG_M>,@-R15 */ {"movmu.l",{A_REG_M,A_DEC_R15},{HEX_4,REG_M,HEX_F,HEX_0}, arch_sh2a_nofpu_up},
+ /* 0100mmmm11110100 movml.l @R15+,<REG_M> */ {"movmu.l",{A_INC_R15,A_REG_M},{HEX_4,REG_M,HEX_F,HEX_4}, arch_sh2a_nofpu_up},
+ /* 0000nnnn00111001 movrt <REG_N> */ {"movrt",{A_REG_N},{HEX_0,REG_N,HEX_3,HEX_9}, arch_sh2a_nofpu_up},
+ /* 0100nnnn10000000 mulr R0,<REG_N> */ {"mulr",{A_R0,A_REG_N},{HEX_4,REG_N,HEX_8,HEX_0}, arch_sh2a_nofpu_up},
+ /* 0000000001101000 nott */ {"nott",{A_END},{HEX_0,HEX_0,HEX_6,HEX_8}, arch_sh2a_nofpu_up},
+ /* 0000000001011011 resbank */ {"resbank",{A_END},{HEX_0,HEX_0,HEX_5,HEX_B}, arch_sh2a_nofpu_up},
+ /* 0000000001101011 rts/n */ {"rts/n",{A_END},{HEX_0,HEX_0,HEX_6,HEX_B}, arch_sh2a_nofpu_up},
+ /* 0000mmmm01111011 rtv/n <REG_M>*/ {"rtv/n",{A_REG_M},{HEX_0,REG_M,HEX_7,HEX_B}, arch_sh2a_nofpu_up},
+ /* 0100nnnn11100001 stbank R0,@<REG_N>*/ {"stbank",{A_R0,A_IND_N},{HEX_4,REG_N,HEX_E,HEX_1}, arch_sh2a_nofpu_up},
+
+/* 0011nnnn0iii1001 0100dddddddddddd band.b #<imm>,@(<DISP12>,<REG_N>) */
+{"band.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_4,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 1100dddddddddddd bandnot.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bandnot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_C,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 1011dddddddddddd bldnot.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bldnot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_B,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 0101dddddddddddd bor.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bor.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_5,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 1101dddddddddddd bornot.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bornot.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_D,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnn0iii1001 0110dddddddddddd bxor.b #<imm>,@(<DISP12>,<REG_N>) */
+{"bxor.b",{A_IMM,A_DISP_REG_N},{HEX_3,REG_N,IMM0_3Uc,HEX_9,HEX_6,DISP1_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0000nnnniiii0000 iiiiiiiiiiiiiiii movi20 #<imm>,<REG_N> */
+{"movi20",{A_IMM,A_REG_N},{HEX_0,REG_N,IMM0_20_4,HEX_0,IMM0_20}, arch_sh2a_nofpu_up | arch_op32},
+/* 0000nnnniiii0001 iiiiiiiiiiiiiiii movi20s #<imm>,<REG_N> */
+{"movi20s",{A_IMM,A_REG_N},{HEX_0,REG_N,IMM0_20_4,HEX_1,IMM0_20BY8}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 1000dddddddddddd movu.b @(<DISP12>,<REG_M>),<REG_N> */
+{"movu.b",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_8,DISP0_12}, arch_sh2a_nofpu_up | arch_op32},
+/* 0011nnnnmmmm0001 1001dddddddddddd movu.w @(<DISP12>,<REG_M>),<REG_N> */
+{"movu.w",{A_DISP_REG_M,A_REG_N},{HEX_3,REG_N,REG_M,HEX_1,HEX_9,DISP0_12BY2}, arch_sh2a_nofpu_up | arch_op32},
+
+{ 0, {0}, {0}, 0 }
+};
+
+#endif
+
+#ifdef ARCH_all
+#define INCLUDE_SHMEDIA
+#endif
+
+static void
+print_movxy (const sh_opcode_info *op, int rn, int rm,
+ fprintf_function fprintf_fn, void *stream)
+{
+ int n;
+
+ fprintf_fn (stream, "%s\t", op->name);
+ for (n = 0; n < 2; n++)
+ {
+ switch (op->arg[n])
+ {
+ case A_IND_N:
+ case AX_IND_N:
+ case AXY_IND_N:
+ case AY_IND_N:
+ case AYX_IND_N:
+ fprintf_fn (stream, "@r%d", rn);
+ break;
+ case A_INC_N:
+ case AX_INC_N:
+ case AXY_INC_N:
+ case AY_INC_N:
+ case AYX_INC_N:
+ fprintf_fn (stream, "@r%d+", rn);
+ break;
+ case AX_PMOD_N:
+ case AXY_PMOD_N:
+ fprintf_fn (stream, "@r%d+r8", rn);
+ break;
+ case AY_PMOD_N:
+ case AYX_PMOD_N:
+ fprintf_fn (stream, "@r%d+r9", rn);
+ break;
+ case DSP_REG_A_M:
+ fprintf_fn (stream, "a%c", '0' + rm);
+ break;
+ case DSP_REG_X:
+ fprintf_fn (stream, "x%c", '0' + rm);
+ break;
+ case DSP_REG_Y:
+ fprintf_fn (stream, "y%c", '0' + rm);
+ break;
+ case DSP_REG_AX:
+ fprintf_fn (stream, "%c%c",
+ (rm & 1) ? 'x' : 'a',
+ (rm & 2) ? '1' : '0');
+ break;
+ case DSP_REG_XY:
+ fprintf_fn (stream, "%c%c",
+ (rm & 1) ? 'y' : 'x',
+ (rm & 2) ? '1' : '0');
+ break;
+ case DSP_REG_AY:
+ fprintf_fn (stream, "%c%c",
+ (rm & 2) ? 'y' : 'a',
+ (rm & 1) ? '1' : '0');
+ break;
+ case DSP_REG_YX:
+ fprintf_fn (stream, "%c%c",
+ (rm & 2) ? 'x' : 'y',
+ (rm & 1) ? '1' : '0');
+ break;
+ default:
+ abort ();
+ }
+ if (n == 0)
+ fprintf_fn (stream, ",");
+ }
+}
+
+/* Print a double data transfer insn. INSN is just the lower three
+ nibbles of the insn, i.e. field a and the bit that indicates if
+ a parallel processing insn follows.
+ Return nonzero if a field b of a parallel processing insns follows. */
+
+static void
+print_insn_ddt (int insn, struct disassemble_info *info)
+{
+ fprintf_function fprintf_fn = info->fprintf_func;
+ void *stream = info->stream;
+
+ /* If this is just a nop, make sure to emit something. */
+ if (insn == 0x000)
+ fprintf_fn (stream, "nopx\tnopy");
+
+ /* If a parallel processing insn was printed before,
+ and we got a non-nop, emit a tab. */
+ if ((insn & 0x800) && (insn & 0x3ff))
+ fprintf_fn (stream, "\t");
+
+ /* Check if either the x or y part is invalid. */
+ if (((insn & 0xc) == 0 && (insn & 0x2a0))
+ || ((insn & 3) == 0 && (insn & 0x150)))
+ if (info->mach != bfd_mach_sh_dsp
+ && info->mach != bfd_mach_sh3_dsp)
+ {
+ static const sh_opcode_info *first_movx, *first_movy;
+ const sh_opcode_info *op;
+ int is_movy;
+
+ if (! first_movx)
+ {
+ for (first_movx = sh_table; first_movx->nibbles[1] != MOVX_NOPY;)
+ first_movx++;
+ for (first_movy = first_movx; first_movy->nibbles[1] != MOVY_NOPX;)
+ first_movy++;
+ }
+
+ is_movy = ((insn & 3) != 0);
+
+ if (is_movy)
+ op = first_movy;
+ else
+ op = first_movx;
+
+ while (op->nibbles[2] != (unsigned) ((insn >> 4) & 3)
+ || op->nibbles[3] != (unsigned) (insn & 0xf))
+ op++;
+
+ print_movxy (op,
+ (4 * ((insn & (is_movy ? 0x200 : 0x100)) == 0)
+ + 2 * is_movy
+ + 1 * ((insn & (is_movy ? 0x100 : 0x200)) != 0)),
+ (insn >> 6) & 3,
+ fprintf_fn, stream);
+ }
+ else
+ fprintf_fn (stream, ".word 0x%x", insn);
+ else
+ {
+ static const sh_opcode_info *first_movx, *first_movy;
+ const sh_opcode_info *opx, *opy;
+ unsigned int insn_x, insn_y;
+
+ if (! first_movx)
+ {
+ for (first_movx = sh_table; first_movx->nibbles[1] != MOVX;)
+ first_movx++;
+ for (first_movy = first_movx; first_movy->nibbles[1] != MOVY;)
+ first_movy++;
+ }
+ insn_x = (insn >> 2) & 0xb;
+ if (insn_x)
+ {
+ for (opx = first_movx; opx->nibbles[2] != insn_x;)
+ opx++;
+ print_movxy (opx, ((insn >> 9) & 1) + 4, (insn >> 7) & 1,
+ fprintf_fn, stream);
+ }
+ insn_y = (insn & 3) | ((insn >> 1) & 8);
+ if (insn_y)
+ {
+ if (insn_x)
+ fprintf_fn (stream, "\t");
+ for (opy = first_movy; opy->nibbles[2] != insn_y;)
+ opy++;
+ print_movxy (opy, ((insn >> 8) & 1) + 6, (insn >> 6) & 1,
+ fprintf_fn, stream);
+ }
+ }
+}
+
+static void
+print_dsp_reg (int rm, fprintf_function fprintf_fn, void *stream)
+{
+ switch (rm)
+ {
+ case A_A1_NUM:
+ fprintf_fn (stream, "a1");
+ break;
+ case A_A0_NUM:
+ fprintf_fn (stream, "a0");
+ break;
+ case A_X0_NUM:
+ fprintf_fn (stream, "x0");
+ break;
+ case A_X1_NUM:
+ fprintf_fn (stream, "x1");
+ break;
+ case A_Y0_NUM:
+ fprintf_fn (stream, "y0");
+ break;
+ case A_Y1_NUM:
+ fprintf_fn (stream, "y1");
+ break;
+ case A_M0_NUM:
+ fprintf_fn (stream, "m0");
+ break;
+ case A_A1G_NUM:
+ fprintf_fn (stream, "a1g");
+ break;
+ case A_M1_NUM:
+ fprintf_fn (stream, "m1");
+ break;
+ case A_A0G_NUM:
+ fprintf_fn (stream, "a0g");
+ break;
+ default:
+ fprintf_fn (stream, "0x%x", rm);
+ break;
+ }
+}
+
+static void
+print_insn_ppi (int field_b, struct disassemble_info *info)
+{
+ static const char *sx_tab[] = { "x0", "x1", "a0", "a1" };
+ static const char *sy_tab[] = { "y0", "y1", "m0", "m1" };
+ fprintf_function fprintf_fn = info->fprintf_func;
+ void *stream = info->stream;
+ unsigned int nib1, nib2, nib3;
+ unsigned int altnib1, nib4;
+ const char *dc = NULL;
+ const sh_opcode_info *op;
+
+ if ((field_b & 0xe800) == 0)
+ {
+ fprintf_fn (stream, "psh%c\t#%d,",
+ field_b & 0x1000 ? 'a' : 'l',
+ (field_b >> 4) & 127);
+ print_dsp_reg (field_b & 0xf, fprintf_fn, stream);
+ return;
+ }
+ if ((field_b & 0xc000) == 0x4000 && (field_b & 0x3000) != 0x1000)
+ {
+ static const char *du_tab[] = { "x0", "y0", "a0", "a1" };
+ static const char *se_tab[] = { "x0", "x1", "y0", "a1" };
+ static const char *sf_tab[] = { "y0", "y1", "x0", "a1" };
+ static const char *sg_tab[] = { "m0", "m1", "a0", "a1" };
+
+ if (field_b & 0x2000)
+ {
+ fprintf_fn (stream, "p%s %s,%s,%s\t",
+ (field_b & 0x1000) ? "add" : "sub",
+ sx_tab[(field_b >> 6) & 3],
+ sy_tab[(field_b >> 4) & 3],
+ du_tab[(field_b >> 0) & 3]);
+ }
+ else if ((field_b & 0xf0) == 0x10
+ && info->mach != bfd_mach_sh_dsp
+ && info->mach != bfd_mach_sh3_dsp)
+ {
+ fprintf_fn (stream, "pclr %s \t", du_tab[(field_b >> 0) & 3]);
+ }
+ else if ((field_b & 0xf3) != 0)
+ {
+ fprintf_fn (stream, ".word 0x%x\t", field_b);
+ }
+ fprintf_fn (stream, "pmuls%c%s,%s,%s",
+ field_b & 0x2000 ? ' ' : '\t',
+ se_tab[(field_b >> 10) & 3],
+ sf_tab[(field_b >> 8) & 3],
+ sg_tab[(field_b >> 2) & 3]);
+ return;
+ }
+
+ nib1 = PPIC;
+ nib2 = field_b >> 12 & 0xf;
+ nib3 = field_b >> 8 & 0xf;
+ nib4 = field_b >> 4 & 0xf;
+ switch (nib3 & 0x3)
+ {
+ case 0:
+ dc = "";
+ nib1 = PPI3;
+ break;
+ case 1:
+ dc = "";
+ break;
+ case 2:
+ dc = "dct ";
+ nib3 -= 1;
+ break;
+ case 3:
+ dc = "dcf ";
+ nib3 -= 2;
+ break;
+ }
+ if (nib1 == PPI3)
+ altnib1 = PPI3NC;
+ else
+ altnib1 = nib1;
+ for (op = sh_table; op->name; op++)
+ {
+ if ((op->nibbles[1] == nib1 || op->nibbles[1] == altnib1)
+ && op->nibbles[2] == nib2
+ && op->nibbles[3] == nib3)
+ {
+ int n;
+
+ switch (op->nibbles[4])
+ {
+ case HEX_0:
+ break;
+ case HEX_XX00:
+ if ((nib4 & 3) != 0)
+ continue;
+ break;
+ case HEX_1:
+ if ((nib4 & 3) != 1)
+ continue;
+ break;
+ case HEX_00YY:
+ if ((nib4 & 0xc) != 0)
+ continue;
+ break;
+ case HEX_4:
+ if ((nib4 & 0xc) != 4)
+ continue;
+ break;
+ default:
+ abort ();
+ }
+ fprintf_fn (stream, "%s%s\t", dc, op->name);
+ for (n = 0; n < 3 && op->arg[n] != A_END; n++)
+ {
+ if (n && op->arg[1] != A_END)
+ fprintf_fn (stream, ",");
+ switch (op->arg[n])
+ {
+ case DSP_REG_N:
+ print_dsp_reg (field_b & 0xf, fprintf_fn, stream);
+ break;
+ case DSP_REG_X:
+ fprintf_fn (stream, "%s", sx_tab[(field_b >> 6) & 3]);
+ break;
+ case DSP_REG_Y:
+ fprintf_fn (stream, "%s", sy_tab[(field_b >> 4) & 3]);
+ break;
+ case A_MACH:
+ fprintf_fn (stream, "mach");
+ break;
+ case A_MACL:
+ fprintf_fn (stream, "macl");
+ break;
+ default:
+ abort ();
+ }
+ }
+ return;
+ }
+ }
+ /* Not found. */
+ fprintf_fn (stream, ".word 0x%x", field_b);
+}
+
+/* FIXME mvs: movx insns print as ".word 0x%03x", insn & 0xfff
+ (ie. the upper nibble is missing). */
+int
+print_insn_sh (bfd_vma memaddr, struct disassemble_info *info)
+{
+ fprintf_function fprintf_fn = info->fprintf_func;
+ void *stream = info->stream;
+ unsigned char insn[4];
+ unsigned char nibs[8];
+ int status;
+ bfd_vma relmask = ~(bfd_vma) 0;
+ const sh_opcode_info *op;
+ unsigned int target_arch;
+ int allow_op32;
+
+ switch (info->mach)
+ {
+ case bfd_mach_sh:
+ target_arch = arch_sh1;
+ break;
+ case bfd_mach_sh4:
+ target_arch = arch_sh4;
+ break;
+ case bfd_mach_sh5:
+#ifdef INCLUDE_SHMEDIA
+ status = print_insn_sh64 (memaddr, info);
+ if (status != -2)
+ return status;
+#endif
+ /* When we get here for sh64, it's because we want to disassemble
+ SHcompact, i.e. arch_sh4. */
+ target_arch = arch_sh4;
+ break;
+ default:
+ fprintf (stderr, "sh architecture not supported\n");
+ return -1;
+ }
+
+ status = info->read_memory_func (memaddr, insn, 2, info);
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, memaddr, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ {
+ nibs[0] = (insn[1] >> 4) & 0xf;
+ nibs[1] = insn[1] & 0xf;
+
+ nibs[2] = (insn[0] >> 4) & 0xf;
+ nibs[3] = insn[0] & 0xf;
+ }
+ else
+ {
+ nibs[0] = (insn[0] >> 4) & 0xf;
+ nibs[1] = insn[0] & 0xf;
+
+ nibs[2] = (insn[1] >> 4) & 0xf;
+ nibs[3] = insn[1] & 0xf;
+ }
+ status = info->read_memory_func (memaddr + 2, insn + 2, 2, info);
+ if (status != 0)
+ allow_op32 = 0;
+ else
+ {
+ allow_op32 = 1;
+
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ {
+ nibs[4] = (insn[3] >> 4) & 0xf;
+ nibs[5] = insn[3] & 0xf;
+
+ nibs[6] = (insn[2] >> 4) & 0xf;
+ nibs[7] = insn[2] & 0xf;
+ }
+ else
+ {
+ nibs[4] = (insn[2] >> 4) & 0xf;
+ nibs[5] = insn[2] & 0xf;
+
+ nibs[6] = (insn[3] >> 4) & 0xf;
+ nibs[7] = insn[3] & 0xf;
+ }
+ }
+
+ if (nibs[0] == 0xf && (nibs[1] & 4) == 0
+ && SH_MERGE_ARCH_SET_VALID (target_arch, arch_sh_dsp_up))
+ {
+ if (nibs[1] & 8)
+ {
+ int field_b;
+
+ status = info->read_memory_func (memaddr + 2, insn, 2, info);
+
+ if (status != 0)
+ {
+ info->memory_error_func (status, memaddr + 2, info);
+ return -1;
+ }
+
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ field_b = insn[1] << 8 | insn[0];
+ else
+ field_b = insn[0] << 8 | insn[1];
+
+ print_insn_ppi (field_b, info);
+ print_insn_ddt ((nibs[1] << 8) | (nibs[2] << 4) | nibs[3], info);
+ return 4;
+ }
+ print_insn_ddt ((nibs[1] << 8) | (nibs[2] << 4) | nibs[3], info);
+ return 2;
+ }
+ for (op = sh_table; op->name; op++)
+ {
+ int n;
+ int imm = 0;
+ int rn = 0;
+ int rm = 0;
+ int rb = 0;
+ int disp_pc;
+ bfd_vma disp_pc_addr = 0;
+ int disp = 0;
+ int has_disp = 0;
+ int max_n = SH_MERGE_ARCH_SET (op->arch, arch_op32) ? 8 : 4;
+
+ if (!allow_op32
+ && SH_MERGE_ARCH_SET (op->arch, arch_op32))
+ goto fail;
+
+ if (!SH_MERGE_ARCH_SET_VALID (op->arch, target_arch))
+ goto fail;
+ for (n = 0; n < max_n; n++)
+ {
+ int i = op->nibbles[n];
+
+ if (i < 16)
+ {
+ if (nibs[n] == i)
+ continue;
+ goto fail;
+ }
+ switch (i)
+ {
+ case BRANCH_8:
+ imm = (nibs[2] << 4) | (nibs[3]);
+ if (imm & 0x80)
+ imm |= ~0xff;
+ imm = ((char) imm) * 2 + 4;
+ goto ok;
+ case BRANCH_12:
+ imm = ((nibs[1]) << 8) | (nibs[2] << 4) | (nibs[3]);
+ if (imm & 0x800)
+ imm |= ~0xfff;
+ imm = imm * 2 + 4;
+ goto ok;
+ case IMM0_3c:
+ if (nibs[3] & 0x8)
+ goto fail;
+ imm = nibs[3] & 0x7;
+ break;
+ case IMM0_3s:
+ if (!(nibs[3] & 0x8))
+ goto fail;
+ imm = nibs[3] & 0x7;
+ break;
+ case IMM0_3Uc:
+ if (nibs[2] & 0x8)
+ goto fail;
+ imm = nibs[2] & 0x7;
+ break;
+ case IMM0_3Us:
+ if (!(nibs[2] & 0x8))
+ goto fail;
+ imm = nibs[2] & 0x7;
+ break;
+ case DISP0_12:
+ case DISP1_12:
+ disp = (nibs[5] << 8) | (nibs[6] << 4) | nibs[7];
+ has_disp = 1;
+ goto ok;
+ case DISP0_12BY2:
+ case DISP1_12BY2:
+ disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 1;
+ relmask = ~(bfd_vma) 1;
+ has_disp = 1;
+ goto ok;
+ case DISP0_12BY4:
+ case DISP1_12BY4:
+ disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 2;
+ relmask = ~(bfd_vma) 3;
+ has_disp = 1;
+ goto ok;
+ case DISP0_12BY8:
+ case DISP1_12BY8:
+ disp = ((nibs[5] << 8) | (nibs[6] << 4) | nibs[7]) << 3;
+ relmask = ~(bfd_vma) 7;
+ has_disp = 1;
+ goto ok;
+ case IMM0_20_4:
+ break;
+ case IMM0_20:
+ imm = ((nibs[2] << 16) | (nibs[4] << 12) | (nibs[5] << 8)
+ | (nibs[6] << 4) | nibs[7]);
+ if (imm & 0x80000)
+ imm -= 0x100000;
+ goto ok;
+ case IMM0_20BY8:
+ imm = ((nibs[2] << 16) | (nibs[4] << 12) | (nibs[5] << 8)
+ | (nibs[6] << 4) | nibs[7]);
+ imm <<= 8;
+ if (imm & 0x8000000)
+ imm -= 0x10000000;
+ goto ok;
+ case IMM0_4:
+ case IMM1_4:
+ imm = nibs[3];
+ goto ok;
+ case IMM0_4BY2:
+ case IMM1_4BY2:
+ imm = nibs[3] << 1;
+ goto ok;
+ case IMM0_4BY4:
+ case IMM1_4BY4:
+ imm = nibs[3] << 2;
+ goto ok;
+ case IMM0_8:
+ case IMM1_8:
+ imm = (nibs[2] << 4) | nibs[3];
+ disp = imm;
+ has_disp = 1;
+ if (imm & 0x80)
+ imm -= 0x100;
+ goto ok;
+ case PCRELIMM_8BY2:
+ imm = ((nibs[2] << 4) | nibs[3]) << 1;
+ relmask = ~(bfd_vma) 1;
+ goto ok;
+ case PCRELIMM_8BY4:
+ imm = ((nibs[2] << 4) | nibs[3]) << 2;
+ relmask = ~(bfd_vma) 3;
+ goto ok;
+ case IMM0_8BY2:
+ case IMM1_8BY2:
+ imm = ((nibs[2] << 4) | nibs[3]) << 1;
+ goto ok;
+ case IMM0_8BY4:
+ case IMM1_8BY4:
+ imm = ((nibs[2] << 4) | nibs[3]) << 2;
+ goto ok;
+ case REG_N_D:
+ if ((nibs[n] & 1) != 0)
+ goto fail;
+ /* fall through */
+ case REG_N:
+ rn = nibs[n];
+ break;
+ case REG_M:
+ rm = nibs[n];
+ break;
+ case REG_N_B01:
+ if ((nibs[n] & 0x3) != 1 /* binary 01 */)
+ goto fail;
+ rn = (nibs[n] & 0xc) >> 2;
+ break;
+ case REG_NM:
+ rn = (nibs[n] & 0xc) >> 2;
+ rm = (nibs[n] & 0x3);
+ break;
+ case REG_B:
+ rb = nibs[n] & 0x07;
+ break;
+ case SDT_REG_N:
+ /* sh-dsp: single data transfer. */
+ rn = nibs[n];
+ if ((rn & 0xc) != 4)
+ goto fail;
+ rn = rn & 0x3;
+ rn |= (!(rn & 2)) << 2;
+ break;
+ case PPI:
+ case REPEAT:
+ goto fail;
+ default:
+ abort ();
+ }
+ }
+
+ ok:
+ /* sh2a has D_REG but not X_REG. We don't know the pattern
+ doesn't match unless we check the output args to see if they
+ make sense. */
+ if (target_arch == arch_sh2a
+ && ((op->arg[0] == DX_REG_M && (rm & 1) != 0)
+ || (op->arg[1] == DX_REG_N && (rn & 1) != 0)))
+ goto fail;
+
+ fprintf_fn (stream, "%s\t", op->name);
+ disp_pc = 0;
+ for (n = 0; n < 3 && op->arg[n] != A_END; n++)
+ {
+ if (n && op->arg[1] != A_END)
+ fprintf_fn (stream, ",");
+ switch (op->arg[n])
+ {
+ case A_IMM:
+ fprintf_fn (stream, "#%d", imm);
+ break;
+ case A_R0:
+ fprintf_fn (stream, "r0");
+ break;
+ case A_REG_N:
+ fprintf_fn (stream, "r%d", rn);
+ break;
+ case A_INC_N:
+ case AS_INC_N:
+ fprintf_fn (stream, "@r%d+", rn);
+ break;
+ case A_DEC_N:
+ case AS_DEC_N:
+ fprintf_fn (stream, "@-r%d", rn);
+ break;
+ case A_IND_N:
+ case AS_IND_N:
+ fprintf_fn (stream, "@r%d", rn);
+ break;
+ case A_DISP_REG_N:
+ fprintf_fn (stream, "@(%d,r%d)", has_disp?disp:imm, rn);
+ break;
+ case AS_PMOD_N:
+ fprintf_fn (stream, "@r%d+r8", rn);
+ break;
+ case A_REG_M:
+ fprintf_fn (stream, "r%d", rm);
+ break;
+ case A_INC_M:
+ fprintf_fn (stream, "@r%d+", rm);
+ break;
+ case A_DEC_M:
+ fprintf_fn (stream, "@-r%d", rm);
+ break;
+ case A_IND_M:
+ fprintf_fn (stream, "@r%d", rm);
+ break;
+ case A_DISP_REG_M:
+ fprintf_fn (stream, "@(%d,r%d)", has_disp?disp:imm, rm);
+ break;
+ case A_REG_B:
+ fprintf_fn (stream, "r%d_bank", rb);
+ break;
+ case A_DISP_PC:
+ disp_pc = 1;
+ disp_pc_addr = imm + 4 + (memaddr & relmask);
+ (*info->print_address_func) (disp_pc_addr, info);
+ break;
+ case A_IND_R0_REG_N:
+ fprintf_fn (stream, "@(r0,r%d)", rn);
+ break;
+ case A_IND_R0_REG_M:
+ fprintf_fn (stream, "@(r0,r%d)", rm);
+ break;
+ case A_DISP_GBR:
+ fprintf_fn (stream, "@(%d,gbr)", has_disp?disp:imm);
+ break;
+ case A_TBR:
+ fprintf_fn (stream, "tbr");
+ break;
+ case A_DISP2_TBR:
+ fprintf_fn (stream, "@@(%d,tbr)", has_disp?disp:imm);
+ break;
+ case A_INC_R15:
+ fprintf_fn (stream, "@r15+");
+ break;
+ case A_DEC_R15:
+ fprintf_fn (stream, "@-r15");
+ break;
+ case A_R0_GBR:
+ fprintf_fn (stream, "@(r0,gbr)");
+ break;
+ case A_BDISP12:
+ case A_BDISP8:
+ {
+ bfd_vma addr;
+ addr = imm + memaddr;
+ (*info->print_address_func) (addr, info);
+ }
+ break;
+ case A_SR:
+ fprintf_fn (stream, "sr");
+ break;
+ case A_GBR:
+ fprintf_fn (stream, "gbr");
+ break;
+ case A_VBR:
+ fprintf_fn (stream, "vbr");
+ break;
+ case A_DSR:
+ fprintf_fn (stream, "dsr");
+ break;
+ case A_MOD:
+ fprintf_fn (stream, "mod");
+ break;
+ case A_RE:
+ fprintf_fn (stream, "re");
+ break;
+ case A_RS:
+ fprintf_fn (stream, "rs");
+ break;
+ case A_A0:
+ fprintf_fn (stream, "a0");
+ break;
+ case A_X0:
+ fprintf_fn (stream, "x0");
+ break;
+ case A_X1:
+ fprintf_fn (stream, "x1");
+ break;
+ case A_Y0:
+ fprintf_fn (stream, "y0");
+ break;
+ case A_Y1:
+ fprintf_fn (stream, "y1");
+ break;
+ case DSP_REG_M:
+ print_dsp_reg (rm, fprintf_fn, stream);
+ break;
+ case A_SSR:
+ fprintf_fn (stream, "ssr");
+ break;
+ case A_SPC:
+ fprintf_fn (stream, "spc");
+ break;
+ case A_MACH:
+ fprintf_fn (stream, "mach");
+ break;
+ case A_MACL:
+ fprintf_fn (stream, "macl");
+ break;
+ case A_PR:
+ fprintf_fn (stream, "pr");
+ break;
+ case A_SGR:
+ fprintf_fn (stream, "sgr");
+ break;
+ case A_DBR:
+ fprintf_fn (stream, "dbr");
+ break;
+ case F_REG_N:
+ fprintf_fn (stream, "fr%d", rn);
+ break;
+ case F_REG_M:
+ fprintf_fn (stream, "fr%d", rm);
+ break;
+ case DX_REG_N:
+ if (rn & 1)
+ {
+ fprintf_fn (stream, "xd%d", rn & ~1);
+ break;
+ }
+ case D_REG_N:
+ fprintf_fn (stream, "dr%d", rn);
+ break;
+ case DX_REG_M:
+ if (rm & 1)
+ {
+ fprintf_fn (stream, "xd%d", rm & ~1);
+ break;
+ }
+ case D_REG_M:
+ fprintf_fn (stream, "dr%d", rm);
+ break;
+ case FPSCR_M:
+ case FPSCR_N:
+ fprintf_fn (stream, "fpscr");
+ break;
+ case FPUL_M:
+ case FPUL_N:
+ fprintf_fn (stream, "fpul");
+ break;
+ case F_FR0:
+ fprintf_fn (stream, "fr0");
+ break;
+ case V_REG_N:
+ fprintf_fn (stream, "fv%d", rn * 4);
+ break;
+ case V_REG_M:
+ fprintf_fn (stream, "fv%d", rm * 4);
+ break;
+ case XMTRX_M4:
+ fprintf_fn (stream, "xmtrx");
+ break;
+ default:
+ abort ();
+ }
+ }
+
+#if 0
+ /* This code prints instructions in delay slots on the same line
+ as the instruction which needs the delay slots. This can be
+ confusing, since other disassembler don't work this way, and
+ it means that the instructions are not all in a line. So I
+ disabled it. Ian. */
+ if (!(info->flags & 1)
+ && (op->name[0] == 'j'
+ || (op->name[0] == 'b'
+ && (op->name[1] == 'r'
+ || op->name[1] == 's'))
+ || (op->name[0] == 'r' && op->name[1] == 't')
+ || (op->name[0] == 'b' && op->name[2] == '.')))
+ {
+ info->flags |= 1;
+ fprintf_fn (stream, "\t(slot ");
+ print_insn_sh (memaddr + 2, info);
+ info->flags &= ~1;
+ fprintf_fn (stream, ")");
+ return 4;
+ }
+#endif
+
+ if (disp_pc && strcmp (op->name, "mova") != 0)
+ {
+ int size;
+ bfd_byte bytes[4];
+
+ if (relmask == ~(bfd_vma) 1)
+ size = 2;
+ else
+ size = 4;
+ status = info->read_memory_func (disp_pc_addr, bytes, size, info);
+ if (status == 0)
+ {
+ unsigned int val;
+
+ if (size == 2)
+ {
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ val = bfd_getl16 (bytes);
+ else
+ val = bfd_getb16 (bytes);
+ }
+ else
+ {
+ if (info->endian == BFD_ENDIAN_LITTLE)
+ val = bfd_getl32 (bytes);
+ else
+ val = bfd_getb32 (bytes);
+ }
+ if ((*info->symbol_at_address_func) (val, info))
+ {
+ fprintf_fn (stream, "\t! ");
+ (*info->print_address_func) (val, info);
+ }
+ else
+ fprintf_fn (stream, "\t! 0x%x", val);
+ }
+ }
+
+ return SH_MERGE_ARCH_SET (op->arch, arch_op32) ? 4 : 2;
+ fail:
+ ;
+
+ }
+ fprintf_fn (stream, ".word 0x%x%x%x%x", nibs[0], nibs[1], nibs[2], nibs[3]);
+ return 2;
+}
diff --git a/share/qemu/bamboo.dtb b/share/qemu/bamboo.dtb
new file mode 100644
index 0000000..c78e254
--- /dev/null
+++ b/share/qemu/bamboo.dtb
Binary files differ
diff --git a/share/qemu/bios.bin b/share/qemu/bios.bin
new file mode 100644
index 0000000..e191908
--- /dev/null
+++ b/share/qemu/bios.bin
Binary files differ
diff --git a/share/qemu/gpxe-eepro100-80861209.rom b/share/qemu/gpxe-eepro100-80861209.rom
new file mode 100644
index 0000000..2ca59ec
--- /dev/null
+++ b/share/qemu/gpxe-eepro100-80861209.rom
Binary files differ
diff --git a/share/qemu/keymaps/ar b/share/qemu/keymaps/ar
new file mode 100644
index 0000000..c430c03
--- /dev/null
+++ b/share/qemu/keymaps/ar
@@ -0,0 +1,98 @@
+# generated from XKB map ar
+include common
+map 0x401
+exclam 0x02 shift
+at 0x03 shift
+numbersign 0x04 shift
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Arabic_dad 0x10 altgr
+Arabic_fatha 0x10 shift altgr
+Arabic_sad 0x11 altgr
+Arabic_fathatan 0x11 shift altgr
+Arabic_theh 0x12 altgr
+Arabic_damma 0x12 shift altgr
+Arabic_qaf 0x13 altgr
+Arabic_dammatan 0x13 shift altgr
+Arabic_feh 0x14 altgr
+UFEF9 0x14 shift altgr
+Arabic_ghain 0x15 altgr
+Arabic_hamzaunderalef 0x15 shift altgr
+Arabic_ain 0x16 altgr
+grave 0x16 shift altgr
+Arabic_ha 0x17 altgr
+division 0x17 shift altgr
+Arabic_khah 0x18 altgr
+multiply 0x18 shift altgr
+Arabic_hah 0x19 altgr
+Arabic_semicolon 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Arabic_jeem 0x1a altgr
+bracketright 0x1b
+braceright 0x1b shift
+Arabic_dal 0x1b altgr
+Arabic_sheen 0x1e altgr
+backslash 0x1e shift altgr
+Arabic_seen 0x1f altgr
+Arabic_yeh 0x20 altgr
+bracketleft 0x20 shift altgr
+Arabic_beh 0x21 altgr
+bracketright 0x21 shift altgr
+Arabic_lam 0x22 altgr
+UFEF7 0x22 shift altgr
+Arabic_alef 0x23 altgr
+Arabic_hamzaonalef 0x23 shift altgr
+Arabic_teh 0x24 altgr
+Arabic_tatweel 0x24 shift altgr
+Arabic_noon 0x25 altgr
+Arabic_comma 0x25 shift altgr
+Arabic_meem 0x26 altgr
+slash 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Arabic_kaf 0x27 altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Arabic_tah 0x28 altgr
+grave 0x29
+asciitilde 0x29 shift
+Arabic_thal 0x29 altgr
+Arabic_shadda 0x29 shift altgr
+backslash 0x2b
+bar 0x2b shift
+less 0x2b altgr
+greater 0x2b shift altgr
+Arabic_hamzaonyeh 0x2c altgr
+asciitilde 0x2c shift altgr
+Arabic_hamza 0x2d altgr
+Arabic_sukun 0x2d shift altgr
+Arabic_hamzaonwaw 0x2e altgr
+Arabic_kasra 0x2e shift altgr
+Arabic_ra 0x2f altgr
+Arabic_kasratan 0x2f shift altgr
+UFEFB 0x30 altgr
+UFEF5 0x30 shift altgr
+Arabic_alefmaksura 0x31 altgr
+Arabic_maddaonalef 0x31 shift altgr
+Arabic_tehmarbuta 0x32 altgr
+apostrophe 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Arabic_waw 0x33 altgr
+period 0x34
+greater 0x34 shift
+Arabic_zain 0x34 altgr
+slash 0x35
+question 0x35 shift
+Arabic_zah 0x35 altgr
+Arabic_question_mark 0x35 shift altgr
diff --git a/share/qemu/keymaps/common b/share/qemu/keymaps/common
new file mode 100644
index 0000000..adc56c7
--- /dev/null
+++ b/share/qemu/keymaps/common
@@ -0,0 +1,157 @@
+include modifiers
+
+#
+# Top row
+#
+1 0x2
+2 0x3
+3 0x4
+4 0x5
+5 0x6
+6 0x7
+7 0x8
+8 0x9
+9 0xa
+0 0xb
+BackSpace 0xe
+
+#
+# QWERTY first row
+#
+Tab 0xf localstate
+ISO_Left_Tab 0xf shift
+q 0x10 addupper
+w 0x11 addupper
+e 0x12 addupper
+r 0x13 addupper
+t 0x14 addupper
+y 0x15 addupper
+u 0x16 addupper
+i 0x17 addupper
+o 0x18 addupper
+p 0x19 addupper
+
+#
+# QWERTY second row
+#
+a 0x1e addupper
+s 0x1f addupper
+d 0x20 addupper
+f 0x21 addupper
+g 0x22 addupper
+h 0x23 addupper
+j 0x24 addupper
+k 0x25 addupper
+l 0x26 addupper
+Return 0x1c localstate
+
+#
+# QWERTY third row
+#
+z 0x2c addupper
+x 0x2d addupper
+c 0x2e addupper
+v 0x2f addupper
+b 0x30 addupper
+n 0x31 addupper
+m 0x32 addupper
+
+space 0x39 localstate
+
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+brokenbar 0x56 shift altgr
+
+#
+# Esc and Function keys
+#
+Escape 0x1 localstate
+F1 0x3b localstate
+F2 0x3c localstate
+F3 0x3d localstate
+F4 0x3e localstate
+F5 0x3f localstate
+F6 0x40 localstate
+F7 0x41 localstate
+F8 0x42 localstate
+F9 0x43 localstate
+F10 0x44 localstate
+F11 0x57 localstate
+F12 0x58 localstate
+
+# Printscreen, Scrollock and Pause
+# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37),
+# but (0xe0, 0x37) seems to work.
+Print 0xb7 localstate
+Sys_Req 0xb7 localstate
+Execute 0xb7 localstate
+Scroll_Lock 0x46
+
+#
+# Insert - PgDown
+#
+Insert 0xd2 localstate
+Delete 0xd3 localstate
+Home 0xc7 localstate
+End 0xcf localstate
+Page_Up 0xc9 localstate
+Page_Down 0xd1 localstate
+
+#
+# Arrow keys
+#
+Left 0xcb localstate
+Up 0xc8 localstate
+Down 0xd0 localstate
+Right 0xcd localstate
+
+#
+# Numpad
+#
+Num_Lock 0x45
+KP_Divide 0xb5
+KP_Multiply 0x37
+KP_Subtract 0x4a
+KP_Add 0x4e
+KP_Enter 0x9c
+
+KP_Decimal 0x53 numlock
+KP_Separator 0x53 numlock
+KP_Delete 0x53
+
+KP_0 0x52 numlock
+KP_Insert 0x52
+
+KP_1 0x4f numlock
+KP_End 0x4f
+
+KP_2 0x50 numlock
+KP_Down 0x50
+
+KP_3 0x51 numlock
+KP_Next 0x51
+
+KP_4 0x4b numlock
+KP_Left 0x4b
+
+KP_5 0x4c numlock
+KP_Begin 0x4c
+
+KP_6 0x4d numlock
+KP_Right 0x4d
+
+KP_7 0x47 numlock
+KP_Home 0x47
+
+KP_8 0x48 numlock
+KP_Up 0x48
+
+KP_9 0x49 numlock
+KP_Prior 0x49
+
+Caps_Lock 0x3a
+#
+# Inhibited keys
+#
+Multi_key 0x0 inhibit
diff --git a/share/qemu/keymaps/da b/share/qemu/keymaps/da
new file mode 100644
index 0000000..3884dcf
--- /dev/null
+++ b/share/qemu/keymaps/da
@@ -0,0 +1,120 @@
+# generated from XKB map dk
+include common
+map 0x406
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+plusminus 0x0c altgr
+questiondown 0x0c shift altgr
+dead_acute 0x0d
+dead_grave 0x0d shift
+bar 0x0d altgr
+brokenbar 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ae 0x27
+AE 0x27 shift
+oslash 0x28
+Ooblique 0x28 shift
+dead_caron 0x28 shift altgr
+onehalf 0x29
+section 0x29 shift
+threequarters 0x29 altgr
+paragraph 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+dead_doubleacute 0x2b altgr
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
+less 0x56
+greater 0x56 shift
+backslash 0x56 altgr
+notsign 0x56 shift altgr
diff --git a/share/qemu/keymaps/de b/share/qemu/keymaps/de
new file mode 100644
index 0000000..ed929c7
--- /dev/null
+++ b/share/qemu/keymaps/de
@@ -0,0 +1,114 @@
+# generated from XKB map de
+include common
+map 0x407
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+section 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+ssharp 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+acute 0x0d
+dead_acute 0x0d
+grave 0x0d shift
+dead_grave 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+asciitilde 0x1b altgr
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+dead_doubleacute 0x27 altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+dead_caron 0x28 shift altgr
+asciicircum 0x29
+dead_circumflex 0x29
+degree 0x29 shift
+notsign 0x29 altgr
+numbersign 0x2b
+apostrophe 0x2b shift
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/de-ch b/share/qemu/keymaps/de-ch
new file mode 100644
index 0000000..852f8b8
--- /dev/null
+++ b/share/qemu/keymaps/de-ch
@@ -0,0 +1,169 @@
+# rdesktop Swiss-German (de-ch) keymap file
+# 2003-06-03 by noldi@tristar.ch
+#
+include common
+map 0x00000807
+#
+# Scan Code 1
+section 0x29
+degree 0x29 shift
+notsign 0x29 altgr inhibit
+#
+# Scan Code 2
+plus 0x2 shift
+brokenbar 0x02 altgr
+#
+# Scan Code 3
+quotedbl 0x03 shift
+at 0x03 altgr
+#
+# Scan Code 4
+asterisk 0x04 shift
+numbersign 0x04 altgr
+#
+# Scan Code 5
+ccedilla 0x05 shift
+onequarter 0x05 altgr inhibit
+#
+# Scan Code 6
+percent 0x06 shift
+onehalf 0x06 altgr inhibit
+#
+# Scan Code 7
+ampersand 0x07 shift
+notsign 0x07 altgr
+#
+# Scan Code 8
+slash 0x08 shift
+bar 0x08 altgr
+#
+# Scan Code 9
+parenleft 0x09 shift
+cent 0x09 altgr
+#
+# Scan Code 10
+parenright 0x0a shift
+#
+# Scan Code 11
+equal 0x0b shift
+braceright 0x0b altgr inhibit
+#
+# Scan Code 12
+apostrophe 0x0c
+question 0x0c shift
+dead_acute 0x0c altgr
+#
+# Scan Code 13
+dead_circumflex 0x0d
+dead_grave 0x0d shift
+dead_tilde 0x0d altgr
+#
+# Scan Code 19
+EuroSign 0x12 altgr
+#
+# Scan Code 22
+z 0x15 addupper
+#
+# Scan Code 27
+udiaeresis 0x1a
+egrave 0x1a shift
+bracketleft 0x1a altgr
+#
+# Scan Code 28
+dead_diaeresis 0x1b
+exclam 0x1b shift
+bracketright 0x1b altgr
+#
+# Scan Code 40
+odiaeresis 0x27
+eacute 0x27 shift
+#
+# Scan Code 41
+adiaeresis 0x28
+agrave 0x28 shift
+braceleft 0x28 altgr
+#
+# Scan Code 42 (only on international keyboards)
+dollar 0x2b
+sterling 0x2b shift
+braceright 0x2b altgr
+#
+# Scan Code 45 (only on international keyboards)
+backslash 0x56 altgr
+#
+# Scan Code 46
+y 0x2c addupper
+#
+# Scan Code 53
+comma 0x33
+semicolon 0x33 shift
+#
+# Scan Code 54
+period 0x34
+colon 0x34 shift
+#
+# Scan Code 55
+minus 0x35
+underscore 0x35 shift
+#
+# Suppress Windows unsupported AltGr keys
+#
+# Scan Code 17
+paragraph 0x10 altgr inhibit
+#
+# Scan Code 21
+tslash 0x14 altgr inhibit
+#
+# Scan Code 22
+leftarrow 0x15 altgr inhibit
+#
+# Scan Code 23
+downarrow 0x16 altgr inhibit
+#
+# Scan Code 24
+rightarrow 0x17 altgr inhibit
+#
+# Scan Code 25
+oslash 0x18 altgr inhibit
+#
+# Scan Code 26
+thorn 0x19 altgr inhibit
+#
+# Scan Code 31
+ae 0x1e altgr inhibit
+#
+# Scan Code 32
+ssharp 0x1f altgr inhibit
+#
+# Scan Code 33
+eth 0x20 altgr inhibit
+#
+# Scan Code 34
+dstroke 0x21 altgr inhibit
+#
+# Scan Code 35
+eng 0x22 altgr inhibit
+#
+# Scan Code 36
+hstroke 0x23 altgr inhibit
+#
+# Scan Code 38
+kra 0x25 altgr inhibit
+#
+# Scan Code 39
+lstroke 0x26 altgr inhibit
+#
+# Scan Code 46
+guillemotleft 0x2c altgr inhibit
+#
+# Scan Code 47
+guillemotright 0x2d altgr inhibit
+#
+# Scan Code 49
+leftdoublequotemark 0x2f altgr inhibit
+#
+# Scan Code 50
+rightdoublequotemark 0x30 altgr inhibit
+#
+# Scan Code 52
+mu 0x32 altgr inhibit
diff --git a/share/qemu/keymaps/en-gb b/share/qemu/keymaps/en-gb
new file mode 100644
index 0000000..b45f06c
--- /dev/null
+++ b/share/qemu/keymaps/en-gb
@@ -0,0 +1,119 @@
+# generated from XKB map gb
+include common
+map 0x809
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+sterling 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+EuroSign 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+at 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+grave 0x29
+notsign 0x29 shift
+bar 0x29 altgr
+numbersign 0x2b
+asciitilde 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+backslash 0x56
+bar 0x56 shift
diff --git a/share/qemu/keymaps/en-us b/share/qemu/keymaps/en-us
new file mode 100644
index 0000000..f5784bb
--- /dev/null
+++ b/share/qemu/keymaps/en-us
@@ -0,0 +1,35 @@
+# generated from XKB map us
+include common
+map 0x409
+exclam 0x02 shift
+at 0x03 shift
+numbersign 0x04 shift
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+bracketleft 0x1a
+braceleft 0x1a shift
+bracketright 0x1b
+braceright 0x1b shift
+semicolon 0x27
+colon 0x27 shift
+apostrophe 0x28
+quotedbl 0x28 shift
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+slash 0x35
+question 0x35 shift
diff --git a/share/qemu/keymaps/es b/share/qemu/keymaps/es
new file mode 100644
index 0000000..0c29eec
--- /dev/null
+++ b/share/qemu/keymaps/es
@@ -0,0 +1,105 @@
+# generated from XKB map es
+include common
+map 0x40a
+exclam 0x02 shift
+bar 0x02 altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+periodcentered 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+asciitilde 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+notsign 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+trademark 0x09 shift altgr
+parenright 0x0a shift
+plusminus 0x0a shift altgr
+equal 0x0b shift
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+exclamdown 0x0d
+questiondown 0x0d shift
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+dead_grave 0x1a
+dead_circumflex 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ntilde 0x27
+Ntilde 0x27 shift
+dead_doubleacute 0x27 shift altgr
+dead_acute 0x28
+dead_diaeresis 0x28 shift
+braceleft 0x28 altgr
+masculine 0x29
+ordfeminine 0x29 shift
+backslash 0x29 altgr
+ccedilla 0x2b
+Ccedilla 0x2b shift
+braceright 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x56
+greater 0x56 shift
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/et b/share/qemu/keymaps/et
new file mode 100644
index 0000000..85541a3
--- /dev/null
+++ b/share/qemu/keymaps/et
@@ -0,0 +1,85 @@
+map 0x00000425
+include common
+
+#
+# Top row
+#
+dead_caron 0x29
+dead_tilde 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+backslash 0xc altgr
+
+acute 0xd
+dead_acute 0xd
+grave 0xd shift
+dead_grave 0xd shift
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+otilde 0x1b
+Otilde 0x1b shift
+section 0x1b altgr
+
+#
+# QWERTY second row
+#
+scaron 0x1f altgr
+Scaron 0x1f altgr shift
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+asciicircum 0x28 altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+onehalf 0x2b altgr
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+zcaron 0x2c altgr
+Zcaron 0x2c altgr shift
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
diff --git a/share/qemu/keymaps/fi b/share/qemu/keymaps/fi
new file mode 100644
index 0000000..2a4e0f0
--- /dev/null
+++ b/share/qemu/keymaps/fi
@@ -0,0 +1,124 @@
+# generated from XKB map se_FI
+include common
+map 0x40b
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+dead_acute 0x0d
+dead_grave 0x0d shift
+plusminus 0x0d altgr
+notsign 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+oslash 0x27 altgr
+Ooblique 0x27 shift altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+ae 0x28 altgr
+AE 0x28 shift altgr
+section 0x29
+onehalf 0x29 shift
+paragraph 0x29 altgr
+threequarters 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+acute 0x2b altgr
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
diff --git a/share/qemu/keymaps/fo b/share/qemu/keymaps/fo
new file mode 100644
index 0000000..c00d9d4
--- /dev/null
+++ b/share/qemu/keymaps/fo
@@ -0,0 +1,76 @@
+map 0x438
+include common
+
+#
+# Top row
+#
+onehalf 0x29
+section 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+plusminus 0xc altgr
+
+bar 0xd altgr
+dead_acute 0xd
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+aring 0x1a
+Aring 0x1a shift
+eth 0x1b addupper
+asciitilde 0x1b altgr
+
+#
+# QWERTY second row
+#
+ae 0x27 addupper
+oslash 0x28
+Ooblique 0x28 shift
+apostrophe 0x2b
+asterisk 0x2b shift
+
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+backslash 0x56 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
diff --git a/share/qemu/keymaps/fr b/share/qemu/keymaps/fr
new file mode 100644
index 0000000..ba5a176
--- /dev/null
+++ b/share/qemu/keymaps/fr
@@ -0,0 +1,181 @@
+include common
+map 0x40c
+#
+# Top row
+#
+twosuperior 0x29
+notsign 0x29 altgr
+
+ampersand 0x02
+1 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+
+eacute 0x03
+2 0x03 shift
+asciitilde 0x03 altgr
+oneeighth 0x03 shift altgr
+
+quotedbl 0x04
+3 0x04 shift
+numbersign 0x04 altgr
+
+apostrophe 0x05
+4 0x05 shift
+braceleft 0x05 altgr
+
+parenleft 0x06
+5 0x06 shift
+bracketleft 0x06 altgr
+threeeighths 0x06 shift altgr
+
+minus 0x07
+6 0x07 shift
+bar 0x07 altgr
+fiveeighths 0x07 shift altgr
+
+egrave 0x08
+7 0x08 shift
+grave 0x08 altgr
+seveneighths 0x08 shift altgr
+
+underscore 0x09
+8 0x09 shift
+backslash 0x09 altgr
+trademark 0x09 shift altgr
+
+ccedilla 0x0a
+9 0x0a shift
+asciicircum 0x0a altgr
+plusminus 0x0a shift altgr
+
+agrave 0x0b
+0 0x0b shift
+at 0x0b altgr
+
+parenright 0x0c
+degree 0x0c shift
+bracketright 0x0c altgr
+questiondown 0x0c shift altgr
+
+equal 0x0d
+plus 0x0d shift
+braceright 0x0d altgr
+dead_ogonek 0x0d shift altgr
+
+#
+# AZERTY first row
+#
+
+a 0x10 addupper
+ae 0x10 altgr
+AE 0x10 shift altgr
+
+z 0x11 addupper
+guillemotleft 0x11 altgr
+
+EuroSign 0x12 altgr
+
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+
+dead_circumflex 0x1a
+dead_diaeresis 0x1a shift
+dead_abovering 0x1a shift altgr
+
+dollar 0x1b
+sterling 0x1b shift
+currency 0x1b altgr
+dead_macron 0x1b shift altgr
+
+#
+# AZERTY second row
+#
+q 0x1e addupper
+Greek_OMEGA 0x1e shift altgr
+
+ssharp 0x1f altgr
+
+eth 0x20 altgr
+ETH 0x20 shift altgr
+
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+
+eng 0x22 altgr
+ENG 0x22 shift altgr
+
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+
+kra 0x25 altgr
+
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+
+m 0x27 addupper
+masculine 0x27 shift altgr
+
+ugrave 0x28
+percent 0x28 shift
+dead_caron 0x28 shift altgr
+
+asterisk 0x2b
+mu 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+
+#
+# AZERTY third row
+#
+less 0x56
+greater 0x56 shift
+
+w 0x2c addupper
+
+guillemotright 0x2d altgr
+
+cent 0x2e altgr
+copyright 0x2e shift altgr
+
+leftdoublequotemark 0x2f altgr
+
+rightdoublequotemark 0x30 altgr
+
+comma 0x32
+question 0x32 shift
+dead_acute 0x32 altgr
+dead_doubleacute 0x32 shift altgr
+
+semicolon 0x33
+period 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+
+colon 0x34
+slash 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+
+exclam 0x35
+section 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/fr-be b/share/qemu/keymaps/fr-be
new file mode 100644
index 0000000..62f7128
--- /dev/null
+++ b/share/qemu/keymaps/fr-be
@@ -0,0 +1,134 @@
+# generated from XKB map be
+include common
+map 0x80c
+ampersand 0x02
+1 0x02 shift
+bar 0x02 altgr
+exclamdown 0x02 shift altgr
+eacute 0x03
+2 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+quotedbl 0x04
+3 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+apostrophe 0x05
+4 0x05 shift
+onequarter 0x05 altgr
+dollar 0x05 shift altgr
+parenleft 0x06
+5 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+section 0x07
+6 0x07 shift
+asciicircum 0x07 altgr
+fiveeighths 0x07 shift altgr
+egrave 0x08
+7 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+exclam 0x09
+8 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+ccedilla 0x0a
+9 0x0a shift
+braceleft 0x0a altgr
+plusminus 0x0a shift altgr
+agrave 0x0b
+0 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+parenright 0x0c
+degree 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+minus 0x0d
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+a 0x10 addupper
+Greek_OMEGA 0x10 shift altgr
+z 0x11 addupper
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+dead_circumflex 0x1a
+dead_diaeresis 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+dollar 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+q 0x1e addupper
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+m 0x27 addupper
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+ugrave 0x28
+percent 0x28 shift
+dead_acute 0x28 altgr
+dead_caron 0x28 shift altgr
+twosuperior 0x29
+threesuperior 0x29 shift
+notsign 0x29 altgr
+mu 0x2b
+sterling 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+w 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+comma 0x32
+question 0x32 shift
+dead_cedilla 0x32 altgr
+masculine 0x32 shift altgr
+semicolon 0x33
+period 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+colon 0x34
+slash 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+equal 0x35
+plus 0x35 shift
+dead_tilde 0x35 altgr
+dead_abovedot 0x35 shift altgr
+backslash 0x56 altgr
diff --git a/share/qemu/keymaps/fr-ca b/share/qemu/keymaps/fr-ca
new file mode 100644
index 0000000..b645208
--- /dev/null
+++ b/share/qemu/keymaps/fr-ca
@@ -0,0 +1,50 @@
+# Canadian French
+# By Simon Germain
+include common
+map 0xc0c
+
+backslash 0x29 altgr
+plusminus 0x2 altgr
+at 0x3 altgr
+sterling 0x4 altgr
+cent 0x5 altgr
+currency 0x6 altgr
+notsign 0x7 altgr
+bar 0x29 shift
+twosuperior 0x9 altgr
+threesuperior 0xa altgr
+onequarter 0xb altgr
+onehalf 0xc altgr
+threequarters 0xd altgr
+section 0x18 altgr
+paragraph 0x19 altgr
+bracketleft 0x1a altgr
+bracketright 0x1b altgr
+asciitilde 0x27 altgr
+braceleft 0x28 altgr
+braceright 0x2b altgr
+less 0x2b
+greater 0x2b shift
+guillemotleft 0x56
+guillemotright 0x56 shift
+degree 0x56 altgr
+mu 0x32 altgr
+eacute 0x35
+dead_acute 0x35 altgr
+dead_grave 0x28
+dead_circumflex 0x1a
+dead_circumflex 0x1a shift
+dead_cedilla 0x1b
+dead_diaeresis 0x1b shift
+exclam 0x2 shift
+quotedbl 0x3 shift
+slash 0x4 shift
+dollar 0x5 shift
+percent 0x6 shift
+question 0x7 shift
+ampersand 0x8 shift
+asterisk 0x9 shift
+parenleft 0xa shift
+parenright 0xb shift
+underscore 0xc shift
+plus 0xd shift
diff --git a/share/qemu/keymaps/fr-ch b/share/qemu/keymaps/fr-ch
new file mode 100644
index 0000000..4620d20
--- /dev/null
+++ b/share/qemu/keymaps/fr-ch
@@ -0,0 +1,114 @@
+# generated from XKB map fr_CH
+include common
+map 0x100c
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+section 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+ssharp 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+acute 0x0d
+dead_acute 0x0d
+grave 0x0d shift
+dead_grave 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+udiaeresis 0x1a
+Udiaeresis 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+asciitilde 0x1b altgr
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+dead_doubleacute 0x27 altgr
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+dead_caron 0x28 shift altgr
+asciicircum 0x29
+dead_circumflex 0x29
+degree 0x29 shift
+notsign 0x29 altgr
+numbersign 0x2b
+apostrophe 0x2b shift
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/hr b/share/qemu/keymaps/hr
new file mode 100644
index 0000000..613aa69
--- /dev/null
+++ b/share/qemu/keymaps/hr
@@ -0,0 +1,125 @@
+# generated from XKB map hr
+include common
+map 0x41a
+exclam 0x02 shift
+asciitilde 0x02 altgr
+dead_tilde 0x02 shift altgr
+quotedbl 0x03 shift
+dead_caron 0x03 altgr
+caron 0x03 shift altgr
+numbersign 0x04 shift
+asciicircum 0x04 altgr
+dead_circumflex 0x04 shift altgr
+dollar 0x05 shift
+dead_breve 0x05 altgr
+breve 0x05 shift altgr
+percent 0x06 shift
+degree 0x06 altgr
+dead_abovering 0x06 shift altgr
+ampersand 0x07 shift
+dead_ogonek 0x07 altgr
+ogonek 0x07 shift altgr
+slash 0x08 shift
+grave 0x08 altgr
+dead_grave 0x08 shift altgr
+parenleft 0x09 shift
+dead_abovedot 0x09 altgr
+abovedot 0x09 shift altgr
+parenright 0x0a shift
+dead_acute 0x0a altgr
+apostrophe 0x0a shift altgr
+equal 0x0b shift
+dead_doubleacute 0x0b altgr
+doubleacute 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+dead_diaeresis 0x0c altgr
+diaeresis 0x0c shift altgr
+plus 0x0d
+asterisk 0x0d shift
+dead_cedilla 0x0d altgr
+cedilla 0x0d shift altgr
+backslash 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+bar 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+scaron 0x1a
+Scaron 0x1a shift
+division 0x1a altgr
+dead_abovering 0x1a shift altgr
+dstroke 0x1b
+Dstroke 0x1b shift
+multiply 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+bracketleft 0x21 altgr
+ordfeminine 0x21 shift altgr
+bracketright 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+lstroke 0x25 altgr
+ampersand 0x25 shift altgr
+Lstroke 0x26 altgr
+ccaron 0x27
+Ccaron 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+cacute 0x28
+Cacute 0x28 shift
+ssharp 0x28 altgr
+dead_caron 0x28 shift altgr
+dead_cedilla 0x29
+dead_diaeresis 0x29 shift
+notsign 0x29 altgr
+zcaron 0x2b
+Zcaron 0x2b shift
+currency 0x2b altgr
+dead_breve 0x2b shift altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+at 0x2f altgr
+grave 0x2f shift altgr
+braceleft 0x30 altgr
+apostrophe 0x30 shift altgr
+braceright 0x31 altgr
+section 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/hu b/share/qemu/keymaps/hu
new file mode 100644
index 0000000..8aba444
--- /dev/null
+++ b/share/qemu/keymaps/hu
@@ -0,0 +1,115 @@
+# Hungarian keyboard layout (QWERTZ)
+# Created by: The NeverGone <never@delfin.klte.hu>
+
+include common
+map 0x40e
+
+
+# AltGr keys:
+notsign 0x29 altgr
+asciitilde 0x02 altgr
+caron 0x03 altgr
+asciicircum 0x04 altgr
+breve 0x05 altgr
+degree 0x06 altgr
+ogonek 0x07 altgr
+grave 0x08 altgr
+abovedot 0x09 altgr
+acute 0x0a altgr
+doubleacute 0x0b altgr
+diaeresis 0x0c altgr
+cedilla 0x0d altgr
+backslash 0x10 altgr
+bar 0x11 altgr
+EuroSign 0x12 altgr
+Iacute 0x17 altgr
+division 0x1a altgr
+multiply 0x1b altgr
+dstroke 0x1f altgr
+Dstroke 0x20 altgr
+bracketleft 0x21 altgr
+bracketright 0x22 altgr
+iacute 0x24 altgr
+lstroke 0x25 altgr
+Lstroke 0x26 altgr
+dollar 0x27 altgr
+ssharp 0x28 altgr
+currency 0x2b altgr
+less 0x56 altgr
+greater 0x2c altgr
+numbersign 0x2d altgr
+ampersand 0x2e altgr
+at 0x2f altgr
+braceleft 0x30 altgr
+braceright 0x31 altgr
+semicolon 0x33 altgr
+asterisk 0x35 altgr
+
+
+# Shift keys:
+section 0x29 shift
+apostrophe 0x02 shift
+quotedbl 0x03 shift
+plus 0x04 shift
+exclam 0x05 shift
+percent 0x06 shift
+slash 0x07 shift
+equal 0x08 shift
+parenleft 0x09 shift
+parenright 0x0a shift
+Odiaeresis 0x0b shift
+Udiaeresis 0x0c shift
+Oacute 0x0d shift
+Z 0x15 shift
+Odoubleacute 0x1a shift
+Uacute 0x1b shift
+Eacute 0x27 shift
+Aacute 0x28 shift
+Udoubleacute 0x2b shift
+Y 0x2c shift
+question 0x33 shift
+colon 0x34 shift
+underscore 0x35 shift
+F13 0x3b shift
+F14 0x3c shift
+F15 0x3d shift
+F16 0x3e shift
+F17 0x3f shift
+F18 0x40 shift
+F19 0x41 shift
+F20 0x42 shift
+F21 0x43 shift
+F22 0x44 shift
+F23 0x57 shift
+F24 0x58 shift
+
+
+# Ctrl keys:
+F25 0x3b ctrl
+F26 0x3c ctrl
+F27 0x3d ctrl
+F28 0x3e ctrl
+F29 0x3f ctrl
+F30 0x40 ctrl
+F31 0x41 ctrl
+F32 0x42 ctrl
+F33 0x43 ctrl
+F34 0x44 ctrl
+F35 0x57 ctrl
+#NoSymbol 0x58 ctrl
+
+
+0 0x29
+odiaeresis 0x0b
+udiaeresis 0x0c
+oacute 0x0d
+z 0x15
+odoubleacute 0x1a
+uacute 0x1b
+eacute 0x27
+aacute 0x28
+udoubleacute 0x2b
+y 0x2c
+comma 0x33
+period 0x34
+minus 0x35
diff --git a/share/qemu/keymaps/is b/share/qemu/keymaps/is
new file mode 100644
index 0000000..21dc1fd
--- /dev/null
+++ b/share/qemu/keymaps/is
@@ -0,0 +1,139 @@
+# 2004-03-16 Halldór Guðmundsson and Morten Lange
+# Keyboard definition file for the Icelandic keyboard
+# to be used in rdesktop 1.3.x ( See rdesktop.org)
+# generated from XKB map de, and changed manually
+# Location for example /usr/local/share/rdesktop/keymaps/is
+include common
+map 0x40f
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+#section 0x04 shift
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+currency 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+#ssharp 0x0c
+odiaeresis 0x0c
+#question 0x0c shift
+Odiaeresis 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+#acute 0x0d
+minus 0x0d
+#dead_acute 0x0d
+#grave 0x0d shift
+#dead_grave 0x0d shift
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+#z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+#thorn 0x19 altgr
+#THORN 0x19 shift altgr
+#udiaeresis 0x1a
+#Udiaeresis 0x1a shift
+#dead_diaeresis 0x1a altgr
+#dead_abovering 0x1a shift altgr
+eth 0x1a
+ETH 0x1a shift
+apostrophe 0x1b
+question 0x1b shift
+#plus 0x1b
+#asterisk 0x1b shift
+asciitilde 0x1b altgr
+#grave 0x1b altgr
+#dead_tilde 0x1b altgr
+#dead_macron 0x1b shift altgr
+#ae 0x1e altgr
+#AE 0x1e shift altgr
+#eth 0x20 altgr
+#eth 0x20
+#ETH 0x20 shift altgr
+#ETH 0x20 shift
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+#adiaeresis 0x27
+#Adiaeresis 0x27 shift
+ae 0x27
+AE 0x27 shift
+dead_doubleacute 0x27 altgr
+#adiaeresis 0x28
+#Adiaeresis 0x28 shift
+#dead_caron 0x28 shift altgr
+#asciicircum 0x29
+acute 0x28
+dead_acute 0x28
+#dead_circumflex 0x29
+#degree 0x29 shift
+#notsign 0x29 altgr
+plus 0x2b
+asterisk 0x2b shift
+grave 0x2b altgr
+#numbersign 0x2b
+#apostrophe 0x2b shift
+#dead_breve 0x2b shift altgr
+#y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+#minus 0x35
+#underscore 0x35 shift
+thorn 0x35
+THORN 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/it b/share/qemu/keymaps/it
new file mode 100644
index 0000000..00ca73a
--- /dev/null
+++ b/share/qemu/keymaps/it
@@ -0,0 +1,115 @@
+# generated from XKB map it
+include common
+map 0x410
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+sterling 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+trademark 0x09 shift altgr
+parenright 0x0a shift
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+grave 0x0c altgr
+questiondown 0x0c shift altgr
+igrave 0x0d
+asciicircum 0x0d shift
+asciitilde 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+egrave 0x1a
+eacute 0x1a shift
+bracketleft 0x1a altgr
+dead_abovering 0x1a shift altgr
+plus 0x1b
+asterisk 0x1b shift
+bracketright 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ograve 0x27
+ccedilla 0x27 shift
+at 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+agrave 0x28
+degree 0x28 shift
+numbersign 0x28 altgr
+backslash 0x29
+bar 0x29 shift
+notsign 0x29 altgr
+ugrave 0x2b
+section 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/ja b/share/qemu/keymaps/ja
new file mode 100644
index 0000000..9d90a78
--- /dev/null
+++ b/share/qemu/keymaps/ja
@@ -0,0 +1,109 @@
+# generated from XKB map jp106
+include common
+map 0x411
+exclam 0x02 shift
+kana_NU 0x02 altgr
+quotedbl 0x03 shift
+kana_FU 0x03 altgr
+numbersign 0x04 shift
+kana_A 0x04 altgr
+kana_a 0x04 shift altgr
+dollar 0x05 shift
+kana_U 0x05 altgr
+kana_u 0x05 shift altgr
+percent 0x06 shift
+kana_E 0x06 altgr
+kana_e 0x06 shift altgr
+ampersand 0x07 shift
+kana_O 0x07 altgr
+kana_o 0x07 shift altgr
+apostrophe 0x08 shift
+kana_YA 0x08 altgr
+kana_ya 0x08 shift altgr
+parenleft 0x09 shift
+kana_YU 0x09 altgr
+kana_yu 0x09 shift altgr
+parenright 0x0a shift
+kana_YO 0x0a altgr
+kana_yo 0x0a shift altgr
+asciitilde 0x0b shift
+kana_WA 0x0b altgr
+kana_WO 0x0b shift altgr
+minus 0x0c
+equal 0x0c shift
+kana_HO 0x0c altgr
+asciicircum 0x0d
+asciitilde 0x0d shift
+kana_HE 0x0d altgr
+kana_TA 0x10 altgr
+kana_TE 0x11 altgr
+kana_I 0x12 altgr
+kana_i 0x12 shift altgr
+kana_SU 0x13 altgr
+kana_KA 0x14 altgr
+kana_N 0x15 altgr
+kana_NA 0x16 altgr
+kana_NI 0x17 altgr
+kana_RA 0x18 altgr
+kana_SE 0x19 altgr
+at 0x1a
+grave 0x1a shift
+voicedsound 0x1a altgr
+bracketleft 0x1b
+braceleft 0x1b shift
+semivoicedsound 0x1b altgr
+kana_openingbracket 0x1b shift altgr
+kana_CHI 0x1e altgr
+kana_TO 0x1f altgr
+kana_SHI 0x20 altgr
+kana_HA 0x21 altgr
+kana_KI 0x22 altgr
+kana_KU 0x23 altgr
+kana_MA 0x24 altgr
+kana_NO 0x25 altgr
+kana_RI 0x26 altgr
+semicolon 0x27
+plus 0x27 shift
+kana_RE 0x27 altgr
+colon 0x28
+asterisk 0x28 shift
+kana_KE 0x28 altgr
+Zenkaku_Hankaku 0x29
+bracketright 0x2b
+braceright 0x2b shift
+kana_MU 0x2b altgr
+kana_closingbracket 0x2b shift altgr
+kana_TSU 0x2c altgr
+kana_tsu 0x2c shift altgr
+kana_SA 0x2d altgr
+kana_SO 0x2e altgr
+kana_HI 0x2f altgr
+kana_KO 0x30 altgr
+kana_MI 0x31 altgr
+kana_MO 0x32 altgr
+comma 0x33
+less 0x33 shift
+kana_NE 0x33 altgr
+kana_comma 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+kana_RU 0x34 altgr
+kana_fullstop 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+kana_ME 0x35 altgr
+kana_conjunctive 0x35 shift altgr
+Eisu_toggle 0x3a shift
+Execute 0x54 shift
+Kanji 0x70
+backslash 0x73
+yen 0x7d
+bar 0x7d shift
+underscore 0x73 shift
+Henkan_Mode 0x79
+Katakana_Real 0x70
+Katakana 0x70
+Muhenkan 0x7b
+Henkan_Mode_Real 0x79
+Henkan_Mode_Ultra 0x79
+backslash_ja 0x73
diff --git a/share/qemu/keymaps/lt b/share/qemu/keymaps/lt
new file mode 100644
index 0000000..3d9d619
--- /dev/null
+++ b/share/qemu/keymaps/lt
@@ -0,0 +1,57 @@
+# generated from XKB map lt
+include common
+map 0x427
+exclam 0x02 shift
+aogonek 0x02 altgr
+Aogonek 0x02 shift altgr
+at 0x03 shift
+ccaron 0x03 altgr
+Ccaron 0x03 shift altgr
+numbersign 0x04 shift
+eogonek 0x04 altgr
+Eogonek 0x04 shift altgr
+dollar 0x05 shift
+eabovedot 0x05 altgr
+Eabovedot 0x05 shift altgr
+percent 0x06 shift
+iogonek 0x06 altgr
+Iogonek 0x06 shift altgr
+asciicircum 0x07 shift
+scaron 0x07 altgr
+Scaron 0x07 shift altgr
+ampersand 0x08 shift
+uogonek 0x08 altgr
+Uogonek 0x08 shift altgr
+asterisk 0x09 shift
+umacron 0x09 altgr
+Umacron 0x09 shift altgr
+parenleft 0x0a shift
+doublelowquotemark 0x0a altgr
+parenright 0x0b shift
+leftdoublequotemark 0x0b altgr
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+zcaron 0x0d altgr
+Zcaron 0x0d shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+bracketright 0x1b
+braceright 0x1b shift
+semicolon 0x27
+colon 0x27 shift
+apostrophe 0x28
+quotedbl 0x28 shift
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+slash 0x35
+question 0x35 shift
+endash 0x56
+EuroSign 0x56 shift
diff --git a/share/qemu/keymaps/lv b/share/qemu/keymaps/lv
new file mode 100644
index 0000000..1d91727
--- /dev/null
+++ b/share/qemu/keymaps/lv
@@ -0,0 +1,128 @@
+# generated from XKB map lv
+include common
+map 0x426
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+EuroSign 0x05 altgr
+cent 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+emacron 0x12 altgr
+Emacron 0x12 shift altgr
+rcedilla 0x13 altgr
+Rcedilla 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+umacron 0x16 altgr
+Umacron 0x16 shift altgr
+imacron 0x17 altgr
+Imacron 0x17 shift altgr
+omacron 0x18 altgr
+Omacron 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ISO_Next_Group 0x1c shift
+amacron 0x1e altgr
+Amacron 0x1e shift altgr
+scaron 0x1f altgr
+Scaron 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+gcedilla 0x22 altgr
+Gcedilla 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kcedilla 0x25 altgr
+Kcedilla 0x25 shift altgr
+lcedilla 0x26 altgr
+Lcedilla 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+leftdoublequotemark 0x28 altgr
+doublelowquotemark 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+notsign 0x29 altgr
+backslash 0x2b
+bar 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+zcaron 0x2c altgr
+Zcaron 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+ccaron 0x2e altgr
+Ccaron 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+ncedilla 0x31 altgr
+Ncedilla 0x31 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
+nobreakspace 0x39 altgr
diff --git a/share/qemu/keymaps/mk b/share/qemu/keymaps/mk
new file mode 100644
index 0000000..18c1504
--- /dev/null
+++ b/share/qemu/keymaps/mk
@@ -0,0 +1,101 @@
+# generated from XKB map mk
+include common
+map 0x42f
+exclam 0x02 shift
+at 0x03 shift
+doublelowquotemark 0x03 shift altgr
+numbersign 0x04 shift
+leftdoublequotemark 0x04 shift altgr
+dollar 0x05 shift
+percent 0x06 shift
+asciicircum 0x07 shift
+ampersand 0x08 shift
+asterisk 0x09 shift
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Cyrillic_lje 0x10 altgr
+Cyrillic_LJE 0x10 shift altgr
+Cyrillic_nje 0x11 altgr
+Cyrillic_NJE 0x11 shift altgr
+Cyrillic_ie 0x12 altgr
+Cyrillic_IE 0x12 shift altgr
+Cyrillic_er 0x13 altgr
+Cyrillic_ER 0x13 shift altgr
+Cyrillic_te 0x14 altgr
+Cyrillic_TE 0x14 shift altgr
+Macedonia_dse 0x15 altgr
+Macedonia_DSE 0x15 shift altgr
+Cyrillic_u 0x16 altgr
+Cyrillic_U 0x16 shift altgr
+Cyrillic_i 0x17 altgr
+Cyrillic_I 0x17 shift altgr
+Cyrillic_o 0x18 altgr
+Cyrillic_O 0x18 shift altgr
+Cyrillic_pe 0x19 altgr
+Cyrillic_PE 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Cyrillic_sha 0x1a altgr
+Cyrillic_SHA 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Macedonia_gje 0x1b altgr
+Macedonia_GJE 0x1b shift altgr
+Cyrillic_a 0x1e altgr
+Cyrillic_A 0x1e shift altgr
+Cyrillic_es 0x1f altgr
+Cyrillic_ES 0x1f shift altgr
+Cyrillic_de 0x20 altgr
+Cyrillic_DE 0x20 shift altgr
+Cyrillic_ef 0x21 altgr
+Cyrillic_EF 0x21 shift altgr
+Cyrillic_ghe 0x22 altgr
+Cyrillic_GHE 0x22 shift altgr
+Cyrillic_ha 0x23 altgr
+Cyrillic_HA 0x23 shift altgr
+Cyrillic_je 0x24 altgr
+Cyrillic_JE 0x24 shift altgr
+Cyrillic_ka 0x25 altgr
+Cyrillic_KA 0x25 shift altgr
+Cyrillic_el 0x26 altgr
+Cyrillic_EL 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Cyrillic_che 0x27 altgr
+Cyrillic_CHE 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Macedonia_kje 0x28 altgr
+Macedonia_KJE 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+backslash 0x2b
+bar 0x2b shift
+Cyrillic_zhe 0x2b altgr
+Cyrillic_ZHE 0x2b shift altgr
+Cyrillic_ze 0x2c altgr
+Cyrillic_ZE 0x2c shift altgr
+Cyrillic_dzhe 0x2d altgr
+Cyrillic_DZHE 0x2d shift altgr
+Cyrillic_tse 0x2e altgr
+Cyrillic_TSE 0x2e shift altgr
+Cyrillic_ve 0x2f altgr
+Cyrillic_VE 0x2f shift altgr
+Cyrillic_be 0x30 altgr
+Cyrillic_BE 0x30 shift altgr
+Cyrillic_en 0x31 altgr
+Cyrillic_EN 0x31 shift altgr
+Cyrillic_em 0x32 altgr
+Cyrillic_EM 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+semicolon 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+colon 0x34 shift altgr
+slash 0x35
+question 0x35 shift
diff --git a/share/qemu/keymaps/modifiers b/share/qemu/keymaps/modifiers
new file mode 100644
index 0000000..d73b7a6
--- /dev/null
+++ b/share/qemu/keymaps/modifiers
@@ -0,0 +1,18 @@
+Shift_R 0x36
+Shift_L 0x2a
+
+Alt_R 0xb8
+Mode_switch 0xb8
+ISO_Level3_Shift 0xb8
+Alt_L 0x38
+
+Control_R 0x9d
+Control_L 0x1d
+
+# Translate Super to Windows keys.
+# This is hardcoded. See documentation for details.
+Super_R 0xdc
+Super_L 0xdb
+
+# Translate Menu to the Windows Application key.
+Menu 0xdd
diff --git a/share/qemu/keymaps/nl b/share/qemu/keymaps/nl
new file mode 100644
index 0000000..b4892f9
--- /dev/null
+++ b/share/qemu/keymaps/nl
@@ -0,0 +1,59 @@
+# Dutch (Netherlands)
+include common
+map 0x413
+
+exclam 0x02 shift
+onesuperior 0x02 altgr
+quotebl 0x03 shift
+twosuperior 0x03 altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+underscore 0x08 shift
+sterling 0x08 altgr
+parenleft 0x09 shift
+braceleft 0x09 altgr
+parenright 0x0a shift
+braceright 0x0a altgr
+apostrophe 0x0b shift
+slash 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+degree 0x0d
+dead_tilde 0x0d shift
+dead_cedilla 0x0d altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+dead_diaeresis 0x1a
+dead_circumflex 0x1a shift
+asterisk 0x1b
+bar 0x1b shift
+ssharp 0x1f altgr
+plus 0x27
+plusminus 0x27 shift
+dead_acute 0x28
+dead_grave 0x28 shift
+at 0x29
+section 0x29 shift
+notsign 0x29 altgr
+less 0x2b
+greater 0x2b shift
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+hyphen 0x35
+equal 0x35 shift
+bracketright 0x56
+bracketleft 0x56 shift
+brokenbar 0x56 altgr
diff --git a/share/qemu/keymaps/nl-be b/share/qemu/keymaps/nl-be
new file mode 100644
index 0000000..34fc881
--- /dev/null
+++ b/share/qemu/keymaps/nl-be
@@ -0,0 +1,3 @@
+# Dutch (Belgium)
+map 0x813
+include common
diff --git a/share/qemu/keymaps/no b/share/qemu/keymaps/no
new file mode 100644
index 0000000..40a6479
--- /dev/null
+++ b/share/qemu/keymaps/no
@@ -0,0 +1,119 @@
+# generated from XKB map no
+include common
+map 0x414
+exclam 0x02 shift
+exclamdown 0x02 altgr
+onesuperior 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+twosuperior 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+threesuperior 0x04 shift altgr
+currency 0x05 shift
+dollar 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+cent 0x06 shift altgr
+ampersand 0x07 shift
+yen 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+division 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+guillemotleft 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+guillemotright 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+plus 0x0c
+question 0x0c shift
+plusminus 0x0c altgr
+questiondown 0x0c shift altgr
+backslash 0x0d
+dead_grave 0x0d shift
+dead_acute 0x0d altgr
+notsign 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+registered 0x13 altgr
+thorn 0x14 altgr
+THORN 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oe 0x18 altgr
+OE 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+asciicircum 0x01b shift
+dead_tilde 0x1b altgr
+asciitilde 0x1b altgr
+dead_caron 0x1b shift altgr
+ordfeminine 0x1e altgr
+masculine 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+oslash 0x27
+Ooblique 0x27 shift
+dead_doubleacute 0x27 shift altgr
+ae 0x28
+AE 0x28 shift
+dead_caron 0x28 shift altgr
+bar 0x29
+section 0x29 shift
+brokenbar 0x29 altgr
+paragraph 0x29 shift altgr
+apostrophe 0x2b
+asterisk 0x2b shift
+multiply 0x2b shift altgr
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+copyright 0x2e altgr
+leftdoublequotemark 0x2f altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+dead_cedilla 0x33 altgr
+dead_ogonek 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+dead_abovedot 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+hyphen 0x35 altgr
+macron 0x35 shift altgr
+nobreakspace 0x39 altgr
+onehalf 0x56 altgr
+threequarters 0x56 shift altgr
diff --git a/share/qemu/keymaps/pl b/share/qemu/keymaps/pl
new file mode 100644
index 0000000..09c600d
--- /dev/null
+++ b/share/qemu/keymaps/pl
@@ -0,0 +1,122 @@
+# generated from XKB map pl
+include common
+map 0x415
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+sterling 0x04 shift altgr
+dollar 0x05 shift
+onequarter 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+asciicircum 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+eogonek 0x12 altgr
+Eogonek 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+EuroSign 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oacute 0x18 altgr
+Oacute 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+aogonek 0x1e altgr
+Aogonek 0x1e shift altgr
+sacute 0x1f altgr
+Sacute 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+notsign 0x29 altgr
+backslash 0x2b
+bar 0x2b shift
+dead_grave 0x2b altgr
+dead_breve 0x2b shift altgr
+zabovedot 0x2c altgr
+Zabovedot 0x2c shift altgr
+zacute 0x2d altgr
+Zacute 0x2d shift altgr
+cacute 0x2e altgr
+Cacute 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+nacute 0x31 altgr
+Nacute 0x31 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/pt b/share/qemu/keymaps/pt
new file mode 100644
index 0000000..c6941f6
--- /dev/null
+++ b/share/qemu/keymaps/pt
@@ -0,0 +1,113 @@
+# generated from XKB map pt
+include common
+map 0x816
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+quotedbl 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+numbersign 0x04 shift
+sterling 0x04 altgr
+dollar 0x05 shift
+section 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+threequarters 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+guillemotleft 0x0d
+guillemotright 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+cent 0x12 shift altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+plus 0x1a
+asterisk 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+dead_acute 0x1b
+dead_grave 0x1b shift
+dead_tilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+ccedilla 0x27
+Ccedilla 0x27 shift
+dead_doubleacute 0x27 shift altgr
+masculine 0x28
+ordfeminine 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+backslash 0x29
+bar 0x29 shift
+notsign 0x29 altgr
+dead_tilde 0x2b
+dead_circumflex 0x2b shift
+dead_breve 0x2b shift altgr
+less 0x56
+greater 0x56 shift
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+multiply 0x33 shift altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+division 0x34 shift altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/keymaps/pt-br b/share/qemu/keymaps/pt-br
new file mode 100644
index 0000000..54bafc5
--- /dev/null
+++ b/share/qemu/keymaps/pt-br
@@ -0,0 +1,69 @@
+# generated from XKB map br
+include common
+map 0x416
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+at 0x03 shift
+twosuperior 0x03 altgr
+onehalf 0x03 shift altgr
+numbersign 0x04 shift
+threesuperior 0x04 altgr
+threequarters 0x04 shift altgr
+dollar 0x05 shift
+sterling 0x05 altgr
+onequarter 0x05 shift altgr
+percent 0x06 shift
+cent 0x06 altgr
+dead_diaeresis 0x07 shift
+notsign 0x07 altgr
+diaeresis 0x07 shift altgr
+ampersand 0x08 shift
+braceleft 0x08 altgr
+asterisk 0x09 shift
+bracketleft 0x09 altgr
+parenleft 0x0a shift
+bracketright 0x0a altgr
+parenright 0x0b shift
+braceright 0x0b altgr
+minus 0x0c
+underscore 0x0c shift
+backslash 0x0c altgr
+equal 0x0d
+plus 0x0d shift
+section 0x0d altgr
+EuroSign 0x12 altgr
+registered 0x13 altgr
+dead_acute 0x1a
+dead_grave 0x1a shift
+acute 0x1a altgr
+grave 0x1a shift altgr
+bracketleft 0x1b
+braceleft 0x1b shift
+ordfeminine 0x1b altgr
+ccedilla 0x27
+Ccedilla 0x27 shift
+dead_tilde 0x28
+dead_circumflex 0x28 shift
+asciitilde 0x28 altgr
+asciicircum 0x28 shift altgr
+apostrophe 0x29
+quotedbl 0x29 shift
+bracketright 0x2b
+braceright 0x2b shift
+masculine 0x2b altgr
+copyright 0x2e altgr
+mu 0x32 altgr
+comma 0x33
+less 0x33 shift
+period 0x34
+greater 0x34 shift
+semicolon 0x35
+colon 0x35 shift
+comma 0x53 numlock
+backslash 0x56
+bar 0x56 shift
+slash 0x73
+question 0x73 shift
+degree 0x73 altgr
+KP_Decimal 0x34
diff --git a/share/qemu/keymaps/ru b/share/qemu/keymaps/ru
new file mode 100644
index 0000000..b3e7d24
--- /dev/null
+++ b/share/qemu/keymaps/ru
@@ -0,0 +1,109 @@
+# generated from XKB map ru
+include common
+map 0x419
+exclam 0x02 shift
+at 0x03 shift
+quotedbl 0x03 shift altgr
+numbersign 0x04 shift
+dollar 0x05 shift
+asterisk 0x05 shift altgr
+percent 0x06 shift
+colon 0x06 shift altgr
+asciicircum 0x07 shift
+comma 0x07 shift altgr
+ampersand 0x08 shift
+period 0x08 shift altgr
+asterisk 0x09 shift
+semicolon 0x09 shift altgr
+parenleft 0x0a shift
+parenright 0x0b shift
+minus 0x0c
+underscore 0x0c shift
+equal 0x0d
+plus 0x0d shift
+Cyrillic_shorti 0x10 altgr
+Cyrillic_SHORTI 0x10 shift altgr
+Cyrillic_tse 0x11 altgr
+Cyrillic_TSE 0x11 shift altgr
+Cyrillic_u 0x12 altgr
+Cyrillic_U 0x12 shift altgr
+Cyrillic_ka 0x13 altgr
+Cyrillic_KA 0x13 shift altgr
+Cyrillic_ie 0x14 altgr
+Cyrillic_IE 0x14 shift altgr
+Cyrillic_en 0x15 altgr
+Cyrillic_EN 0x15 shift altgr
+Cyrillic_ghe 0x16 altgr
+Cyrillic_GHE 0x16 shift altgr
+Cyrillic_sha 0x17 altgr
+Cyrillic_SHA 0x17 shift altgr
+Cyrillic_shcha 0x18 altgr
+Cyrillic_SHCHA 0x18 shift altgr
+Cyrillic_ze 0x19 altgr
+Cyrillic_ZE 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Cyrillic_ha 0x1a altgr
+Cyrillic_HA 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Cyrillic_hardsign 0x1b altgr
+Cyrillic_HARDSIGN 0x1b shift altgr
+Cyrillic_ef 0x1e altgr
+Cyrillic_EF 0x1e shift altgr
+Cyrillic_yeru 0x1f altgr
+Cyrillic_YERU 0x1f shift altgr
+Cyrillic_ve 0x20 altgr
+Cyrillic_VE 0x20 shift altgr
+Cyrillic_a 0x21 altgr
+Cyrillic_A 0x21 shift altgr
+Cyrillic_pe 0x22 altgr
+Cyrillic_PE 0x22 shift altgr
+Cyrillic_er 0x23 altgr
+Cyrillic_ER 0x23 shift altgr
+Cyrillic_o 0x24 altgr
+Cyrillic_O 0x24 shift altgr
+Cyrillic_el 0x25 altgr
+Cyrillic_EL 0x25 shift altgr
+Cyrillic_de 0x26 altgr
+Cyrillic_DE 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Cyrillic_zhe 0x27 altgr
+Cyrillic_ZHE 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Cyrillic_e 0x28 altgr
+Cyrillic_E 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+Cyrillic_io 0x29 altgr
+Cyrillic_IO 0x29 shift altgr
+backslash 0x2b
+bar 0x2b shift
+Cyrillic_ya 0x2c altgr
+Cyrillic_YA 0x2c shift altgr
+Cyrillic_che 0x2d altgr
+Cyrillic_CHE 0x2d shift altgr
+Cyrillic_es 0x2e altgr
+Cyrillic_ES 0x2e shift altgr
+Cyrillic_em 0x2f altgr
+Cyrillic_EM 0x2f shift altgr
+Cyrillic_i 0x30 altgr
+Cyrillic_I 0x30 shift altgr
+Cyrillic_te 0x31 altgr
+Cyrillic_TE 0x31 shift altgr
+Cyrillic_softsign 0x32 altgr
+Cyrillic_SOFTSIGN 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Cyrillic_be 0x33 altgr
+Cyrillic_BE 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+Cyrillic_yu 0x34 altgr
+Cyrillic_YU 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+slash 0x56 altgr
+bar 0x56 shift altgr
diff --git a/share/qemu/keymaps/sl b/share/qemu/keymaps/sl
new file mode 100644
index 0000000..56835a9
--- /dev/null
+++ b/share/qemu/keymaps/sl
@@ -0,0 +1,110 @@
+# generated from XKB map sl
+include common
+map 0x424
+exclam 0x02 shift
+asciitilde 0x02 altgr
+dead_tilde 0x02 shift altgr
+quotedbl 0x03 shift
+dead_caron 0x03 altgr
+caron 0x03 shift altgr
+numbersign 0x04 shift
+asciicircum 0x04 altgr
+dead_circumflex 0x04 shift altgr
+dollar 0x05 shift
+dead_breve 0x05 altgr
+breve 0x05 shift altgr
+percent 0x06 shift
+degree 0x06 altgr
+dead_abovering 0x06 shift altgr
+ampersand 0x07 shift
+dead_ogonek 0x07 altgr
+ogonek 0x07 shift altgr
+slash 0x08 shift
+grave 0x08 altgr
+dead_grave 0x08 shift altgr
+parenleft 0x09 shift
+dead_abovedot 0x09 altgr
+abovedot 0x09 shift altgr
+parenright 0x0a shift
+dead_acute 0x0a altgr
+equal 0x0b shift
+dead_doubleacute 0x0b altgr
+doubleacute 0x0b shift altgr
+apostrophe 0x0c
+question 0x0c shift
+dead_diaeresis 0x0c altgr
+diaeresis 0x0c shift altgr
+plus 0x0d
+asterisk 0x0d shift
+dead_cedilla 0x0d altgr
+cedilla 0x0d shift altgr
+backslash 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+bar 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+z 0x15 addupper
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+rightarrow 0x17 altgr
+idotless 0x17 shift altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+scaron 0x1a
+Scaron 0x1a shift
+division 0x1a altgr
+dstroke 0x1b
+Dstroke 0x1b shift
+multiply 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+bracketleft 0x21 altgr
+ordfeminine 0x21 shift altgr
+bracketright 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+lstroke 0x25 altgr
+Lstroke 0x26 altgr
+ccaron 0x27
+Ccaron 0x27 shift
+cacute 0x28
+Cacute 0x28 shift
+ssharp 0x28 altgr
+dead_cedilla 0x29
+notsign 0x29 altgr
+zcaron 0x2b
+Zcaron 0x2b shift
+currency 0x2b altgr
+y 0x2c addupper
+guillemotleft 0x2c altgr
+guillemotright 0x2d altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+at 0x2f altgr
+braceleft 0x30 altgr
+braceright 0x31 altgr
+section 0x32 altgr
+masculine 0x32 shift altgr
+comma 0x33
+semicolon 0x33 shift
+horizconnector 0x33 altgr
+period 0x34
+colon 0x34 shift
+periodcentered 0x34 altgr
+minus 0x35
+underscore 0x35 shift
+dead_belowdot 0x35 altgr
diff --git a/share/qemu/keymaps/sv b/share/qemu/keymaps/sv
new file mode 100644
index 0000000..5d9080e
--- /dev/null
+++ b/share/qemu/keymaps/sv
@@ -0,0 +1,81 @@
+map 0x0000041d
+include common
+
+#
+# Top row
+#
+section 0x29
+onehalf 0x29 shift
+
+# 1
+exclam 0x2 shift
+
+# 2
+quotedbl 0x3 shift
+at 0x3 altgr
+
+# 3
+numbersign 0x4 shift
+sterling 0x4 altgr
+# 4
+currency 0x5 shift
+dollar 0x5 altgr
+# 5
+percent 0x6 shift
+# 6
+ampersand 0x7 shift
+# 7
+slash 0x8 shift
+braceleft 0x8 altgr
+# 8
+parenleft 0x9 shift
+bracketleft 0x9 altgr
+# 9
+parenright 0xa shift
+bracketright 0xa altgr
+# 0
+equal 0xb shift
+braceright 0xb altgr
+
+plus 0xc
+question 0xc shift
+backslash 0xc altgr
+
+acute 0xd
+dead_acute 0xd
+grave 0xd shift
+dead_grave 0xd shift
+
+#
+# QWERTY first row
+#
+EuroSign 0x12 altgr
+aring 0x1a
+Aring 0x1a shift
+dead_diaeresis 0x1b
+dead_circumflex 0x1b shift
+dead_tilde 0x1b altgr
+
+#
+# QWERTY second row
+#
+odiaeresis 0x27
+Odiaeresis 0x27 shift
+adiaeresis 0x28
+Adiaeresis 0x28 shift
+apostrophe 0x2b
+asterisk 0x2b shift
+
+#
+# QWERTY third row
+#
+less 0x56
+greater 0x56 shift
+bar 0x56 altgr
+mu 0x32 altgr
+comma 0x33
+semicolon 0x33 shift
+period 0x34
+colon 0x34 shift
+minus 0x35
+underscore 0x35 shift
diff --git a/share/qemu/keymaps/th b/share/qemu/keymaps/th
new file mode 100644
index 0000000..b65b6da
--- /dev/null
+++ b/share/qemu/keymaps/th
@@ -0,0 +1,131 @@
+# generated from XKB map th
+include common
+map 0x41e
+exclam 0x02 shift
+Thai_lakkhangyao 0x02 altgr
+plus 0x02 shift altgr
+at 0x03 shift
+slash 0x03 altgr
+Thai_leknung 0x03 shift altgr
+numbersign 0x04 shift
+minus 0x04 altgr
+Thai_leksong 0x04 shift altgr
+dollar 0x05 shift
+Thai_phosamphao 0x05 altgr
+Thai_leksam 0x05 shift altgr
+percent 0x06 shift
+Thai_thothung 0x06 altgr
+Thai_leksi 0x06 shift altgr
+asciicircum 0x07 shift
+Thai_sarau 0x07 altgr
+Thai_sarauu 0x07 shift altgr
+ampersand 0x08 shift
+Thai_saraue 0x08 altgr
+Thai_baht 0x08 shift altgr
+asterisk 0x09 shift
+Thai_khokhwai 0x09 altgr
+Thai_lekha 0x09 shift altgr
+parenleft 0x0a shift
+Thai_totao 0x0a altgr
+Thai_lekhok 0x0a shift altgr
+parenright 0x0b shift
+Thai_chochan 0x0b altgr
+Thai_lekchet 0x0b shift altgr
+minus 0x0c
+underscore 0x0c shift
+Thai_khokhai 0x0c altgr
+Thai_lekpaet 0x0c shift altgr
+equal 0x0d
+plus 0x0d shift
+Thai_chochang 0x0d altgr
+Thai_lekkao 0x0d shift altgr
+Thai_maiyamok 0x10 altgr
+Thai_leksun 0x10 shift altgr
+Thai_saraaimaimalai 0x11 altgr
+quotedbl 0x11 shift altgr
+Thai_saraam 0x12 altgr
+Thai_dochada 0x12 shift altgr
+Thai_phophan 0x13 altgr
+Thai_thonangmontho 0x13 shift altgr
+Thai_saraa 0x14 altgr
+Thai_thothong 0x14 shift altgr
+Thai_maihanakat 0x15 altgr
+Thai_nikhahit 0x15 shift altgr
+Thai_saraii 0x16 altgr
+Thai_maitri 0x16 shift altgr
+Thai_rorua 0x17 altgr
+Thai_nonen 0x17 shift altgr
+Thai_nonu 0x18 altgr
+Thai_paiyannoi 0x18 shift altgr
+Thai_yoyak 0x19 altgr
+Thai_yoying 0x19 shift altgr
+bracketleft 0x1a
+braceleft 0x1a shift
+Thai_bobaimai 0x1a altgr
+Thai_thothan 0x1a shift altgr
+bracketright 0x1b
+braceright 0x1b shift
+Thai_loling 0x1b altgr
+comma 0x1b shift altgr
+Thai_fofan 0x1e altgr
+Thai_ru 0x1e shift altgr
+Thai_hohip 0x1f altgr
+Thai_khorakhang 0x1f shift altgr
+Thai_kokai 0x20 altgr
+Thai_topatak 0x20 shift altgr
+Thai_dodek 0x21 altgr
+Thai_sarao 0x21 shift altgr
+Thai_sarae 0x22 altgr
+Thai_chochoe 0x22 shift altgr
+Thai_maitho 0x23 altgr
+Thai_maitaikhu 0x23 shift altgr
+Thai_maiek 0x24 altgr
+Thai_maichattawa 0x24 shift altgr
+Thai_saraaa 0x25 altgr
+Thai_sorusi 0x25 shift altgr
+Thai_sosua 0x26 altgr
+Thai_sosala 0x26 shift altgr
+semicolon 0x27
+colon 0x27 shift
+Thai_wowaen 0x27 altgr
+Thai_soso 0x27 shift altgr
+apostrophe 0x28
+quotedbl 0x28 shift
+Thai_ngongu 0x28 altgr
+period 0x28 shift altgr
+grave 0x29
+asciitilde 0x29 shift
+underscore 0x29 altgr
+percent 0x29 shift altgr
+ISO_First_Group 0x2a shift
+backslash 0x2b
+bar 0x2b shift
+Thai_khokhuat 0x2b altgr
+Thai_khokhon 0x2b shift altgr
+Thai_phophung 0x2c altgr
+parenleft 0x2c shift altgr
+Thai_popla 0x2d altgr
+parenright 0x2d shift altgr
+Thai_saraae 0x2e altgr
+Thai_choching 0x2e shift altgr
+Thai_oang 0x2f altgr
+Thai_honokhuk 0x2f shift altgr
+Thai_sarai 0x30 altgr
+Thai_phinthu 0x30 shift altgr
+Thai_sarauee 0x31 altgr
+Thai_thanthakhat 0x31 shift altgr
+Thai_thothahan 0x32 altgr
+question 0x32 shift altgr
+comma 0x33
+less 0x33 shift
+Thai_moma 0x33 altgr
+Thai_thophuthao 0x33 shift altgr
+period 0x34
+greater 0x34 shift
+Thai_saraaimaimuan 0x34 altgr
+Thai_lochula 0x34 shift altgr
+slash 0x35
+question 0x35 shift
+Thai_fofa 0x35 altgr
+Thai_lu 0x35 shift altgr
+ISO_Last_Group 0x36 shift
diff --git a/share/qemu/keymaps/tr b/share/qemu/keymaps/tr
new file mode 100644
index 0000000..5650e1e
--- /dev/null
+++ b/share/qemu/keymaps/tr
@@ -0,0 +1,123 @@
+# generated from XKB map tr
+include common
+map 0x41f
+exclam 0x02 shift
+onesuperior 0x02 altgr
+exclamdown 0x02 shift altgr
+apostrophe 0x03 shift
+at 0x03 altgr
+oneeighth 0x03 shift altgr
+dead_circumflex 0x04 shift
+numbersign 0x04 altgr
+sterling 0x04 shift altgr
+plus 0x05 shift
+dollar 0x05 altgr
+percent 0x06 shift
+onehalf 0x06 altgr
+threeeighths 0x06 shift altgr
+ampersand 0x07 shift
+asciicircum 0x07 altgr
+fiveeighths 0x07 shift altgr
+slash 0x08 shift
+braceleft 0x08 altgr
+seveneighths 0x08 shift altgr
+parenleft 0x09 shift
+bracketleft 0x09 altgr
+trademark 0x09 shift altgr
+parenright 0x0a shift
+bracketright 0x0a altgr
+plusminus 0x0a shift altgr
+equal 0x0b shift
+braceright 0x0b altgr
+degree 0x0b shift altgr
+asterisk 0x0c
+question 0x0c shift
+backslash 0x0c altgr
+questiondown 0x0c shift altgr
+minus 0x0d
+underscore 0x0d shift
+dead_cedilla 0x0d altgr
+dead_ogonek 0x0d shift altgr
+at 0x10 altgr
+Greek_OMEGA 0x10 shift altgr
+lstroke 0x11 altgr
+Lstroke 0x11 shift altgr
+EuroSign 0x12 altgr
+paragraph 0x13 altgr
+registered 0x13 shift altgr
+tslash 0x14 altgr
+Tslash 0x14 shift altgr
+leftarrow 0x15 altgr
+yen 0x15 shift altgr
+downarrow 0x16 altgr
+uparrow 0x16 shift altgr
+idotless 0x17
+I 0x17 shift
+rightarrow 0x17 altgr
+oslash 0x18 altgr
+Ooblique 0x18 shift altgr
+thorn 0x19 altgr
+THORN 0x19 shift altgr
+gbreve 0x1a
+Gbreve 0x1a shift
+dead_diaeresis 0x1a altgr
+dead_abovering 0x1a shift altgr
+udiaeresis 0x1b
+Udiaeresis 0x1b shift
+asciitilde 0x1b altgr
+dead_macron 0x1b shift altgr
+ae 0x1e altgr
+AE 0x1e shift altgr
+ssharp 0x1f altgr
+section 0x1f shift altgr
+eth 0x20 altgr
+ETH 0x20 shift altgr
+dstroke 0x21 altgr
+ordfeminine 0x21 shift altgr
+eng 0x22 altgr
+ENG 0x22 shift altgr
+hstroke 0x23 altgr
+Hstroke 0x23 shift altgr
+kra 0x25 altgr
+ampersand 0x25 shift altgr
+lstroke 0x26 altgr
+Lstroke 0x26 shift altgr
+scedilla 0x27
+Scedilla 0x27 shift
+dead_acute 0x27 altgr
+dead_doubleacute 0x27 shift altgr
+i 0x28
+Iabovedot 0x28 shift
+dead_circumflex 0x28 altgr
+dead_caron 0x28 shift altgr
+backslash 0x29
+quotedbl 0x29 shift
+asciitilde 0x29 altgr
+comma 0x2b
+semicolon 0x2b shift
+bar 0x2b altgr
+dead_breve 0x2b shift altgr
+guillemotleft 0x2c altgr
+less 0x2c shift altgr
+guillemotright 0x2d altgr
+greater 0x2d shift altgr
+cent 0x2e altgr
+copyright 0x2e shift altgr
+leftdoublequotemark 0x2f altgr
+grave 0x2f shift altgr
+rightdoublequotemark 0x30 altgr
+apostrophe 0x30 shift altgr
+mu 0x32 altgr
+masculine 0x32 shift altgr
+odiaeresis 0x33
+Odiaeresis 0x33 shift
+less 0x33 altgr
+multiply 0x33 shift altgr
+ccedilla 0x34
+Ccedilla 0x34 shift
+greater 0x34 altgr
+division 0x34 shift altgr
+period 0x35
+colon 0x35 shift
+dead_belowdot 0x35 altgr
+dead_abovedot 0x35 shift altgr
diff --git a/share/qemu/linuxboot.bin b/share/qemu/linuxboot.bin
new file mode 100644
index 0000000..d8f0ea8
--- /dev/null
+++ b/share/qemu/linuxboot.bin
Binary files differ
diff --git a/share/qemu/multiboot.bin b/share/qemu/multiboot.bin
new file mode 100644
index 0000000..d7da6e0
--- /dev/null
+++ b/share/qemu/multiboot.bin
Binary files differ
diff --git a/share/qemu/openbios-ppc b/share/qemu/openbios-ppc
new file mode 100644
index 0000000..ee6f5ae
--- /dev/null
+++ b/share/qemu/openbios-ppc
Binary files differ
diff --git a/share/qemu/openbios-sparc32 b/share/qemu/openbios-sparc32
new file mode 100644
index 0000000..b2dc5c5
--- /dev/null
+++ b/share/qemu/openbios-sparc32
Binary files differ
diff --git a/share/qemu/openbios-sparc64 b/share/qemu/openbios-sparc64
new file mode 100644
index 0000000..70a223d
--- /dev/null
+++ b/share/qemu/openbios-sparc64
Binary files differ
diff --git a/share/qemu/petalogix-s3adsp1800.dtb b/share/qemu/petalogix-s3adsp1800.dtb
new file mode 100644
index 0000000..93c5973
--- /dev/null
+++ b/share/qemu/petalogix-s3adsp1800.dtb
Binary files differ
diff --git a/share/qemu/ppc_rom.bin b/share/qemu/ppc_rom.bin
new file mode 100644
index 0000000..0ad0282
--- /dev/null
+++ b/share/qemu/ppc_rom.bin
Binary files differ
diff --git a/share/qemu/pxe-e1000.bin b/share/qemu/pxe-e1000.bin
new file mode 100644
index 0000000..7ac744e
--- /dev/null
+++ b/share/qemu/pxe-e1000.bin
Binary files differ
diff --git a/share/qemu/pxe-ne2k_pci.bin b/share/qemu/pxe-ne2k_pci.bin
new file mode 100644
index 0000000..5cb68ab
--- /dev/null
+++ b/share/qemu/pxe-ne2k_pci.bin
Binary files differ
diff --git a/share/qemu/pxe-pcnet.bin b/share/qemu/pxe-pcnet.bin
new file mode 100644
index 0000000..7a54bab
--- /dev/null
+++ b/share/qemu/pxe-pcnet.bin
Binary files differ
diff --git a/share/qemu/pxe-rtl8139.bin b/share/qemu/pxe-rtl8139.bin
new file mode 100644
index 0000000..db7d76d
--- /dev/null
+++ b/share/qemu/pxe-rtl8139.bin
Binary files differ
diff --git a/share/qemu/pxe-virtio.bin b/share/qemu/pxe-virtio.bin
new file mode 100644
index 0000000..6dde514
--- /dev/null
+++ b/share/qemu/pxe-virtio.bin
Binary files differ
diff --git a/share/qemu/s390-zipl.rom b/share/qemu/s390-zipl.rom
new file mode 100644
index 0000000..f7af9b1
--- /dev/null
+++ b/share/qemu/s390-zipl.rom
Binary files differ
diff --git a/share/qemu/vapic.bin b/share/qemu/vapic.bin
new file mode 100644
index 0000000..35f0cf2
--- /dev/null
+++ b/share/qemu/vapic.bin
Binary files differ
diff --git a/share/qemu/vgabios-cirrus.bin b/share/qemu/vgabios-cirrus.bin
new file mode 100644
index 0000000..7626ea5
--- /dev/null
+++ b/share/qemu/vgabios-cirrus.bin
Binary files differ
diff --git a/share/qemu/vgabios-qxl.bin b/share/qemu/vgabios-qxl.bin
new file mode 100644
index 0000000..3156c6e
--- /dev/null
+++ b/share/qemu/vgabios-qxl.bin
Binary files differ
diff --git a/share/qemu/vgabios-stdvga.bin b/share/qemu/vgabios-stdvga.bin
new file mode 100644
index 0000000..1e4fc33
--- /dev/null
+++ b/share/qemu/vgabios-stdvga.bin
Binary files differ
diff --git a/share/qemu/vgabios-vmware.bin b/share/qemu/vgabios-vmware.bin
new file mode 100644
index 0000000..9633d9a
--- /dev/null
+++ b/share/qemu/vgabios-vmware.bin
Binary files differ
diff --git a/share/qemu/vgabios.bin b/share/qemu/vgabios.bin
new file mode 100644
index 0000000..d04033e
--- /dev/null
+++ b/share/qemu/vgabios.bin
Binary files differ
diff --git a/simpletrace.c b/simpletrace.c
new file mode 100644
index 0000000..9ea0d1f
--- /dev/null
+++ b/simpletrace.c
@@ -0,0 +1,255 @@
+/*
+ * Simple trace backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include "qemu-timer.h"
+#include "trace.h"
+
+/** Trace file header event ID */
+#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
+
+/** Trace file magic number */
+#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
+
+/** Trace file version number, bump if format changes */
+#define HEADER_VERSION 0
+
+/** Trace buffer entry */
+typedef struct {
+ uint64_t event;
+ uint64_t timestamp_ns;
+ uint64_t x1;
+ uint64_t x2;
+ uint64_t x3;
+ uint64_t x4;
+ uint64_t x5;
+ uint64_t x6;
+} TraceRecord;
+
+enum {
+ TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord),
+};
+
+static TraceRecord trace_buf[TRACE_BUF_LEN];
+static unsigned int trace_idx;
+static FILE *trace_fp;
+static char *trace_file_name = NULL;
+static bool trace_file_enabled = false;
+
+void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ stream_printf(stream, "Trace file \"%s\" %s.\n",
+ trace_file_name, trace_file_enabled ? "on" : "off");
+}
+
+static bool write_header(FILE *fp)
+{
+ static const TraceRecord header = {
+ .event = HEADER_EVENT_ID,
+ .timestamp_ns = HEADER_MAGIC,
+ .x1 = HEADER_VERSION,
+ };
+
+ return fwrite(&header, sizeof header, 1, fp) == 1;
+}
+
+/**
+ * set_trace_file : To set the name of a trace file.
+ * @file : pointer to the name to be set.
+ * If NULL, set to the default name-<pid> set at config time.
+ */
+bool st_set_trace_file(const char *file)
+{
+ st_set_trace_file_enabled(false);
+
+ free(trace_file_name);
+
+ if (!file) {
+ if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
+ trace_file_name = NULL;
+ return false;
+ }
+ } else {
+ if (asprintf(&trace_file_name, "%s", file) < 0) {
+ trace_file_name = NULL;
+ return false;
+ }
+ }
+
+ st_set_trace_file_enabled(true);
+ return true;
+}
+
+static void flush_trace_file(void)
+{
+ /* If the trace file is not open yet, open it now */
+ if (!trace_fp) {
+ trace_fp = fopen(trace_file_name, "w");
+ if (!trace_fp) {
+ /* Avoid repeatedly trying to open file on failure */
+ trace_file_enabled = false;
+ return;
+ }
+ write_header(trace_fp);
+ }
+
+ if (trace_fp) {
+ size_t unused; /* for when fwrite(3) is declared warn_unused_result */
+ unused = fwrite(trace_buf, trace_idx * sizeof(trace_buf[0]), 1, trace_fp);
+ }
+}
+
+void st_flush_trace_buffer(void)
+{
+ if (trace_file_enabled) {
+ flush_trace_file();
+ }
+
+ /* Discard written trace records */
+ trace_idx = 0;
+}
+
+void st_set_trace_file_enabled(bool enable)
+{
+ if (enable == trace_file_enabled) {
+ return; /* no change */
+ }
+
+ /* Flush/discard trace buffer */
+ st_flush_trace_buffer();
+
+ /* To disable, close trace file */
+ if (!enable) {
+ fclose(trace_fp);
+ trace_fp = NULL;
+ }
+
+ trace_file_enabled = enable;
+}
+
+static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
+ uint64_t x4, uint64_t x5, uint64_t x6)
+{
+ TraceRecord *rec = &trace_buf[trace_idx];
+
+ if (!trace_list[event].state) {
+ return;
+ }
+
+ rec->event = event;
+ rec->timestamp_ns = get_clock();
+ rec->x1 = x1;
+ rec->x2 = x2;
+ rec->x3 = x3;
+ rec->x4 = x4;
+ rec->x5 = x5;
+ rec->x6 = x6;
+
+ if (++trace_idx == TRACE_BUF_LEN) {
+ st_flush_trace_buffer();
+ }
+}
+
+void trace0(TraceEventID event)
+{
+ trace(event, 0, 0, 0, 0, 0, 0);
+}
+
+void trace1(TraceEventID event, uint64_t x1)
+{
+ trace(event, x1, 0, 0, 0, 0, 0);
+}
+
+void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
+{
+ trace(event, x1, x2, 0, 0, 0, 0);
+}
+
+void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
+{
+ trace(event, x1, x2, x3, 0, 0, 0);
+}
+
+void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
+{
+ trace(event, x1, x2, x3, x4, 0, 0);
+}
+
+void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
+{
+ trace(event, x1, x2, x3, x4, x5, 0);
+}
+
+void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
+{
+ trace(event, x1, x2, x3, x4, x5, x6);
+}
+
+/**
+ * Flush the trace buffer on exit
+ */
+static void __attribute__((constructor)) st_init(void)
+{
+ atexit(st_flush_trace_buffer);
+}
+
+void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ unsigned int i;
+
+ for (i = 0; i < trace_idx; i++) {
+ stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64
+ " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n",
+ trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
+ trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5,
+ trace_buf[i].x6);
+ }
+}
+
+void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_TRACE_EVENTS; i++) {
+ stream_printf(stream, "%s [Event ID %u] : state %u\n",
+ trace_list[i].tp_name, i, trace_list[i].state);
+ }
+}
+
+static TraceEvent* find_trace_event_by_name(const char *tname)
+{
+ unsigned int i;
+
+ if (!tname) {
+ return NULL;
+ }
+
+ for (i = 0; i < NR_TRACE_EVENTS; i++) {
+ if (!strcmp(trace_list[i].tp_name, tname)) {
+ return &trace_list[i];
+ }
+ }
+ return NULL; /* indicates end of list reached without a match */
+}
+
+bool st_change_trace_event_state(const char *tname, bool tstate)
+{
+ TraceEvent *tp;
+
+ tp = find_trace_event_by_name(tname);
+ if (tp) {
+ tp->state = tstate;
+ return true;
+ }
+ return false;
+}
diff --git a/simpletrace.h b/simpletrace.h
new file mode 100644
index 0000000..2f44ed3
--- /dev/null
+++ b/simpletrace.h
@@ -0,0 +1,40 @@
+/*
+ * Simple trace backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef SIMPLETRACE_H
+#define SIMPLETRACE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+typedef uint64_t TraceEventID;
+
+typedef struct {
+ const char *tp_name;
+ bool state;
+} TraceEvent;
+
+void trace0(TraceEventID event);
+void trace1(TraceEventID event, uint64_t x1);
+void trace2(TraceEventID event, uint64_t x1, uint64_t x2);
+void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3);
+void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4);
+void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5);
+void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6);
+void st_print_trace(FILE *stream, fprintf_function stream_printf);
+void st_print_trace_events(FILE *stream, fprintf_function stream_printf);
+bool st_change_trace_event_state(const char *tname, bool tstate);
+void st_print_trace_file_status(FILE *stream, fprintf_function stream_printf);
+void st_set_trace_file_enabled(bool enable);
+bool st_set_trace_file(const char *file);
+void st_flush_trace_buffer(void);
+
+#endif /* SIMPLETRACE_H */
diff --git a/slirp/COPYRIGHT b/slirp/COPYRIGHT
new file mode 100644
index 0000000..1bc83d4
--- /dev/null
+++ b/slirp/COPYRIGHT
@@ -0,0 +1,61 @@
+Slirp was written by Danny Gasparovski.
+Copyright (c), 1995,1996 All Rights Reserved.
+
+Slirp is maintained by Kelly Price <tygris+slirp@erols.com>
+
+Slirp is free software; "free" as in you don't have to pay for it, and you
+are free to do whatever you want with it. I do not accept any donations,
+monetary or otherwise, for Slirp. Instead, I would ask you to pass this
+potential donation to your favorite charity. In fact, I encourage
+*everyone* who finds Slirp useful to make a small donation to their
+favorite charity (for example, GreenPeace). This is not a requirement, but
+a suggestion from someone who highly values the service they provide.
+
+The copyright terms and conditions:
+
+---BEGIN---
+
+ Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---END---
+
+This basically means you can do anything you want with the software, except
+1) call it your own, and 2) claim warranty on it. There is no warranty for
+this software. None. Nada. If you lose a million dollars while using
+Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***.
+
+If these conditions cannot be met due to legal restrictions (E.g. where it
+is against the law to give out Software without warranty), you must cease
+using the software and delete all copies you have.
+
+Slirp uses code that is copyrighted by the following people/organizations:
+
+Juha Pirkola.
+Gregory M. Christy.
+The Regents of the University of California.
+Carnegie Mellon University.
+The Australian National University.
+RSA Data Security, Inc.
+
+Please read the top of each source file for the details on the various
+copyrights.
diff --git a/slirp/bootp.c b/slirp/bootp.c
new file mode 100644
index 0000000..0905c6d
--- /dev/null
+++ b/slirp/bootp.c
@@ -0,0 +1,314 @@
+/*
+ * QEMU BOOTP/DHCP server
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <slirp.h>
+
+/* XXX: only DHCP is supported */
+
+#define LEASE_TIME (24 * 3600)
+
+static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) \
+do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0)
+#else
+#define DPRINTF(fmt, ...) do{}while(0)
+#endif
+
+static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ bc = &slirp->bootp_clients[i];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &slirp->bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
+ return bc;
+}
+
+static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ uint32_t req_addr = ntohl(paddr->s_addr);
+ uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr);
+ BOOTPClient *bc;
+
+ if (req_addr >= dhcp_addr &&
+ req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) {
+ bc = &slirp->bootp_clients[req_addr - dhcp_addr];
+ if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
+ bc->allocated = 1;
+ return bc;
+ }
+ }
+ return NULL;
+}
+
+static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr,
+ const uint8_t *macaddr)
+{
+ BOOTPClient *bc;
+ int i;
+
+ for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6))
+ goto found;
+ }
+ return NULL;
+ found:
+ bc = &slirp->bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
+ return bc;
+}
+
+static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
+ struct in_addr *preq_addr)
+{
+ const uint8_t *p, *p_end;
+ int len, tag;
+
+ *pmsg_type = 0;
+ preq_addr->s_addr = htonl(0L);
+
+ p = bp->bp_vend;
+ p_end = p + DHCP_OPT_LEN;
+ if (memcmp(p, rfc1533_cookie, 4) != 0)
+ return;
+ p += 4;
+ while (p < p_end) {
+ tag = p[0];
+ if (tag == RFC1533_PAD) {
+ p++;
+ } else if (tag == RFC1533_END) {
+ break;
+ } else {
+ p++;
+ if (p >= p_end)
+ break;
+ len = *p++;
+ DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
+
+ switch(tag) {
+ case RFC2132_MSG_TYPE:
+ if (len >= 1)
+ *pmsg_type = p[0];
+ break;
+ case RFC2132_REQ_ADDR:
+ if (len >= 4) {
+ memcpy(&(preq_addr->s_addr), p, 4);
+ }
+ break;
+ default:
+ break;
+ }
+ p += len;
+ }
+ }
+ if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) &&
+ bp->bp_ciaddr.s_addr) {
+ memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4);
+ }
+}
+
+static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
+{
+ BOOTPClient *bc = NULL;
+ struct mbuf *m;
+ struct bootp_t *rbp;
+ struct sockaddr_in saddr, daddr;
+ struct in_addr preq_addr;
+ int dhcp_msg_type, val;
+ uint8_t *q;
+
+ /* extract exact DHCP msg type */
+ dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
+ DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
+ if (preq_addr.s_addr != htonl(0L))
+ DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr));
+ else
+ DPRINTF("\n");
+
+ if (dhcp_msg_type == 0)
+ dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
+
+ if (dhcp_msg_type != DHCPDISCOVER &&
+ dhcp_msg_type != DHCPREQUEST)
+ return;
+ /* XXX: this is a hack to get the client mac address */
+ memcpy(slirp->client_ethaddr, bp->bp_hwaddr, 6);
+
+ m = m_get(slirp);
+ if (!m) {
+ return;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ rbp = (struct bootp_t *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+ memset(rbp, 0, sizeof(struct bootp_t));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ if (preq_addr.s_addr != htonl(0L)) {
+ bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = preq_addr;
+ }
+ }
+ if (!bc) {
+ new_addr:
+ bc = get_new_addr(slirp, &daddr.sin_addr, slirp->client_ethaddr);
+ if (!bc) {
+ DPRINTF("no address left\n");
+ return;
+ }
+ }
+ memcpy(bc->macaddr, slirp->client_ethaddr, 6);
+ } else if (preq_addr.s_addr != htonl(0L)) {
+ bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
+ if (bc) {
+ daddr.sin_addr = preq_addr;
+ memcpy(bc->macaddr, slirp->client_ethaddr, 6);
+ } else {
+ daddr.sin_addr.s_addr = 0;
+ }
+ } else {
+ bc = find_addr(slirp, &daddr.sin_addr, bp->bp_hwaddr);
+ if (!bc) {
+ /* if never assigned, behaves as if it was already
+ assigned (windows fix because it remembers its address) */
+ goto new_addr;
+ }
+ }
+
+ saddr.sin_addr = slirp->vhost_addr;
+ saddr.sin_port = htons(BOOTP_SERVER);
+
+ daddr.sin_port = htons(BOOTP_CLIENT);
+
+ rbp->bp_op = BOOTP_REPLY;
+ rbp->bp_xid = bp->bp_xid;
+ rbp->bp_htype = 1;
+ rbp->bp_hlen = 6;
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+
+ rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
+ rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
+
+ q = rbp->bp_vend;
+ memcpy(q, rfc1533_cookie, 4);
+ q += 4;
+
+ if (bc) {
+ DPRINTF("%s addr=%08x\n",
+ (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
+ ntohl(daddr.sin_addr.s_addr));
+
+ if (dhcp_msg_type == DHCPDISCOVER) {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPOFFER;
+ } else /* DHCPREQUEST */ {
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPACK;
+ }
+
+ if (slirp->bootp_filename)
+ snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
+ slirp->bootp_filename);
+
+ *q++ = RFC2132_SRV_ID;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_NETMASK;
+ *q++ = 4;
+ memcpy(q, &slirp->vnetwork_mask, 4);
+ q += 4;
+
+ if (!slirp->restricted) {
+ *q++ = RFC1533_GATEWAY;
+ *q++ = 4;
+ memcpy(q, &saddr.sin_addr, 4);
+ q += 4;
+
+ *q++ = RFC1533_DNS;
+ *q++ = 4;
+ memcpy(q, &slirp->vnameserver_addr, 4);
+ q += 4;
+ }
+
+ *q++ = RFC2132_LEASE_TIME;
+ *q++ = 4;
+ val = htonl(LEASE_TIME);
+ memcpy(q, &val, 4);
+ q += 4;
+
+ if (*slirp->client_hostname) {
+ val = strlen(slirp->client_hostname);
+ *q++ = RFC1533_HOSTNAME;
+ *q++ = val;
+ memcpy(q, slirp->client_hostname, val);
+ q += val;
+ }
+ } else {
+ static const char nak_msg[] = "requested address not available";
+
+ DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr));
+
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = DHCPNAK;
+
+ *q++ = RFC2132_MESSAGE;
+ *q++ = sizeof(nak_msg) - 1;
+ memcpy(q, nak_msg, sizeof(nak_msg) - 1);
+ q += sizeof(nak_msg) - 1;
+ }
+ *q = RFC1533_END;
+
+ daddr.sin_addr.s_addr = 0xffffffffu;
+
+ m->m_len = sizeof(struct bootp_t) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+}
+
+void bootp_input(struct mbuf *m)
+{
+ struct bootp_t *bp = mtod(m, struct bootp_t *);
+
+ if (bp->bp_op == BOOTP_REQUEST) {
+ bootp_reply(m->slirp, bp);
+ }
+}
diff --git a/slirp/bootp.h b/slirp/bootp.h
new file mode 100644
index 0000000..30c30ab
--- /dev/null
+++ b/slirp/bootp.h
@@ -0,0 +1,122 @@
+/* bootp/dhcp defines */
+
+#define BOOTP_SERVER 67
+#define BOOTP_CLIENT 68
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define RFC1533_COOKIE 99, 130, 83, 99
+#define RFC1533_PAD 0
+#define RFC1533_NETMASK 1
+#define RFC1533_TIMEOFFSET 2
+#define RFC1533_GATEWAY 3
+#define RFC1533_TIMESERVER 4
+#define RFC1533_IEN116NS 5
+#define RFC1533_DNS 6
+#define RFC1533_LOGSERVER 7
+#define RFC1533_COOKIESERVER 8
+#define RFC1533_LPRSERVER 9
+#define RFC1533_IMPRESSSERVER 10
+#define RFC1533_RESOURCESERVER 11
+#define RFC1533_HOSTNAME 12
+#define RFC1533_BOOTFILESIZE 13
+#define RFC1533_MERITDUMPFILE 14
+#define RFC1533_DOMAINNAME 15
+#define RFC1533_SWAPSERVER 16
+#define RFC1533_ROOTPATH 17
+#define RFC1533_EXTENSIONPATH 18
+#define RFC1533_IPFORWARDING 19
+#define RFC1533_IPSOURCEROUTING 20
+#define RFC1533_IPPOLICYFILTER 21
+#define RFC1533_IPMAXREASSEMBLY 22
+#define RFC1533_IPTTL 23
+#define RFC1533_IPMTU 24
+#define RFC1533_IPMTUPLATEAU 25
+#define RFC1533_INTMTU 26
+#define RFC1533_INTLOCALSUBNETS 27
+#define RFC1533_INTBROADCAST 28
+#define RFC1533_INTICMPDISCOVER 29
+#define RFC1533_INTICMPRESPOND 30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT 32
+#define RFC1533_INTSTATICROUTES 33
+#define RFC1533_LLTRAILERENCAP 34
+#define RFC1533_LLARPCACHETMO 35
+#define RFC1533_LLETHERNETENCAP 36
+#define RFC1533_TCPTTL 37
+#define RFC1533_TCPKEEPALIVETMO 38
+#define RFC1533_TCPKEEPALIVEGB 39
+#define RFC1533_NISDOMAIN 40
+#define RFC1533_NISSERVER 41
+#define RFC1533_NTPSERVER 42
+#define RFC1533_VENDOR 43
+#define RFC1533_NBNS 44
+#define RFC1533_NBDD 45
+#define RFC1533_NBNT 46
+#define RFC1533_NBSCOPE 47
+#define RFC1533_XFS 48
+#define RFC1533_XDM 49
+
+#define RFC2132_REQ_ADDR 50
+#define RFC2132_LEASE_TIME 51
+#define RFC2132_MSG_TYPE 53
+#define RFC2132_SRV_ID 54
+#define RFC2132_PARAM_LIST 55
+#define RFC2132_MESSAGE 56
+#define RFC2132_MAX_SIZE 57
+#define RFC2132_RENEWAL_TIME 58
+#define RFC2132_REBIND_TIME 59
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPACK 5
+#define DHCPNAK 6
+
+#define RFC1533_VENDOR_MAJOR 0
+#define RFC1533_VENDOR_MINOR 0
+
+#define RFC1533_VENDOR_MAGIC 128
+#define RFC1533_VENDOR_ADDPARM 129
+#define RFC1533_VENDOR_ETHDEV 130
+#define RFC1533_VENDOR_HOWTO 132
+#define RFC1533_VENDOR_MNUOPTS 160
+#define RFC1533_VENDOR_SELECTION 176
+#define RFC1533_VENDOR_MOTD 184
+#define RFC1533_VENDOR_NUMOFMOTD 8
+#define RFC1533_VENDOR_IMG 192
+#define RFC1533_VENDOR_NUMOFIMG 16
+
+#define RFC1533_END 255
+#define BOOTP_VENDOR_LEN 64
+#define DHCP_OPT_LEN 312
+
+struct bootp_t {
+ struct ip ip;
+ struct udphdr udp;
+ uint8_t bp_op;
+ uint8_t bp_htype;
+ uint8_t bp_hlen;
+ uint8_t bp_hops;
+ uint32_t bp_xid;
+ uint16_t bp_secs;
+ uint16_t unused;
+ struct in_addr bp_ciaddr;
+ struct in_addr bp_yiaddr;
+ struct in_addr bp_siaddr;
+ struct in_addr bp_giaddr;
+ uint8_t bp_hwaddr[16];
+ uint8_t bp_sname[64];
+ uint8_t bp_file[128];
+ uint8_t bp_vend[DHCP_OPT_LEN];
+};
+
+typedef struct {
+ uint16_t allocated;
+ uint8_t macaddr[6];
+} BOOTPClient;
+
+#define NB_BOOTP_CLIENTS 16
+
+void bootp_input(struct mbuf *m);
diff --git a/slirp/cksum.c b/slirp/cksum.c
new file mode 100644
index 0000000..e43867d
--- /dev/null
+++ b/slirp/cksum.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
+ */
+
+#include <slirp.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * XXX Since we will never span more than 1 mbuf, we can optimise this
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; \
+ (void)ADDCARRY(sum);}
+
+int cksum(struct mbuf *m, int len)
+{
+ register uint16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+
+ union {
+ uint8_t c[2];
+ uint16_t s;
+ } s_util;
+ union {
+ uint16_t s[2];
+ uint32_t l;
+ } l_util;
+
+ if (m->m_len == 0)
+ goto cont;
+ w = mtod(m, uint16_t *);
+
+ mlen = m->m_len;
+
+ if (len < mlen)
+ mlen = len;
+#ifdef DEBUG
+ len -= mlen;
+#endif
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(uint8_t *)w;
+ w = (uint16_t *)((int8_t *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto cont;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ if (mlen == -1) {
+ s_util.c[1] = *(uint8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(uint8_t *)w;
+
+cont:
+#ifdef DEBUG
+ if (len) {
+ DEBUG_ERROR((dfd, "cksum: out of data\n"));
+ DEBUG_ERROR((dfd, " len = %d\n", len));
+ }
+#endif
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/slirp/debug.h b/slirp/debug.h
new file mode 100644
index 0000000..6cfa61e
--- /dev/null
+++ b/slirp/debug.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+
+#define DBG_CALL 0x1
+#define DBG_MISC 0x2
+#define DBG_ERROR 0x4
+
+#define dfd stderr
+
+extern int slirp_debug;
+
+#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); }
+#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); }
+#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); }
+#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); }
+#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); }
+
+#else
+
+#define DEBUG_CALL(x)
+#define DEBUG_ARG(x, y)
+#define DEBUG_ARGS(x)
+#define DEBUG_MISC(x)
+#define DEBUG_ERROR(x)
+
+#endif
diff --git a/slirp/if.c b/slirp/if.c
new file mode 100644
index 0000000..0f04e13
--- /dev/null
+++ b/slirp/if.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+static void
+ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
+{
+ ifm->ifs_next = ifmhead->ifs_next;
+ ifmhead->ifs_next = ifm;
+ ifm->ifs_prev = ifmhead;
+ ifm->ifs_next->ifs_prev = ifm;
+}
+
+static void
+ifs_remque(struct mbuf *ifm)
+{
+ ifm->ifs_prev->ifs_next = ifm->ifs_next;
+ ifm->ifs_next->ifs_prev = ifm->ifs_prev;
+}
+
+void
+if_init(Slirp *slirp)
+{
+ slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
+ slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
+ slirp->next_m = &slirp->if_batchq;
+}
+
+/*
+ * if_output: Queue packet into an output queue.
+ * There are 2 output queue's, if_fastq and if_batchq.
+ * Each output queue is a doubly linked list of double linked lists
+ * of mbufs, each list belonging to one "session" (socket). This
+ * way, we can output packets fairly by sending one packet from each
+ * session, instead of all the packets from one session, then all packets
+ * from the next session, etc. Packets on the if_fastq get absolute
+ * priority, but if one session hogs the link, it gets "downgraded"
+ * to the batchq until it runs out of packets, then it'll return
+ * to the fastq (eg. if the user does an ls -alR in a telnet session,
+ * it'll temporarily get downgraded to the batchq)
+ */
+void
+if_output(struct socket *so, struct mbuf *ifm)
+{
+ Slirp *slirp = ifm->slirp;
+ struct mbuf *ifq;
+ int on_fastq = 1;
+
+ DEBUG_CALL("if_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ifm = %lx", (long)ifm);
+
+ /*
+ * First remove the mbuf from m_usedlist,
+ * since we're gonna use m_next and m_prev ourselves
+ * XXX Shouldn't need this, gotta change dtom() etc.
+ */
+ if (ifm->m_flags & M_USEDLIST) {
+ remque(ifm);
+ ifm->m_flags &= ~M_USEDLIST;
+ }
+
+ /*
+ * See if there's already a batchq list for this session.
+ * This can include an interactive session, which should go on fastq,
+ * but gets too greedy... hence it'll be downgraded from fastq to batchq.
+ * We mustn't put this packet back on the fastq (or we'll send it out of order)
+ * XXX add cache here?
+ */
+ for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq;
+ ifq = ifq->ifq_prev) {
+ if (so == ifq->ifq_so) {
+ /* A match! */
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ }
+
+ /* No match, check which queue to put it on */
+ if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
+ ifq = slirp->if_fastq.ifq_prev;
+ on_fastq = 1;
+ /*
+ * Check if this packet is a part of the last
+ * packet's session
+ */
+ if (ifq->ifq_so == so) {
+ ifm->ifq_so = so;
+ ifs_insque(ifm, ifq->ifs_prev);
+ goto diddit;
+ }
+ } else
+ ifq = slirp->if_batchq.ifq_prev;
+
+ /* Create a new doubly linked list for this session */
+ ifm->ifq_so = so;
+ ifs_init(ifm);
+ insque(ifm, ifq);
+
+diddit:
+ slirp->if_queued++;
+
+ if (so) {
+ /* Update *_queued */
+ so->so_queued++;
+ so->so_nqueued++;
+ /*
+ * Check if the interactive session should be downgraded to
+ * the batchq. A session is downgraded if it has queued 6
+ * packets without pausing, and at least 3 of those packets
+ * have been sent over the link
+ * (XXX These are arbitrary numbers, probably not optimal..)
+ */
+ if (on_fastq && ((so->so_nqueued >= 6) &&
+ (so->so_nqueued - so->so_queued) >= 3)) {
+
+ /* Remove from current queue... */
+ remque(ifm->ifs_next);
+
+ /* ...And insert in the new. That'll teach ya! */
+ insque(ifm->ifs_next, &slirp->if_batchq);
+ }
+ }
+
+#ifndef FULL_BOLT
+ /*
+ * This prevents us from malloc()ing too many mbufs
+ */
+ if_start(ifm->slirp);
+#endif
+}
+
+/*
+ * Send a packet
+ * We choose a packet based on it's position in the output queues;
+ * If there are packets on the fastq, they are sent FIFO, before
+ * everything else. Otherwise we choose the first packet from the
+ * batchq and send it. the next packet chosen will be from the session
+ * after this one, then the session after that one, and so on.. So,
+ * for example, if there are 3 ftp session's fighting for bandwidth,
+ * one packet will be sent from the first session, then one packet
+ * from the second session, then one packet from the third, then back
+ * to the first, etc. etc.
+ */
+void
+if_start(Slirp *slirp)
+{
+ struct mbuf *ifm, *ifqt;
+
+ DEBUG_CALL("if_start");
+
+ if (slirp->if_queued == 0)
+ return; /* Nothing to do */
+
+ again:
+ /* check if we can really output */
+ if (!slirp_can_output(slirp->opaque))
+ return;
+
+ /*
+ * See which queue to get next packet from
+ * If there's something in the fastq, select it immediately
+ */
+ if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
+ ifm = slirp->if_fastq.ifq_next;
+ } else {
+ /* Nothing on fastq, see if next_m is valid */
+ if (slirp->next_m != &slirp->if_batchq)
+ ifm = slirp->next_m;
+ else
+ ifm = slirp->if_batchq.ifq_next;
+
+ /* Set which packet to send on next iteration */
+ slirp->next_m = ifm->ifq_next;
+ }
+ /* Remove it from the queue */
+ ifqt = ifm->ifq_prev;
+ remque(ifm);
+ slirp->if_queued--;
+
+ /* If there are more packets for this session, re-queue them */
+ if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
+ insque(ifm->ifs_next, ifqt);
+ ifs_remque(ifm);
+ }
+
+ /* Update so_queued */
+ if (ifm->ifq_so) {
+ if (--ifm->ifq_so->so_queued == 0)
+ /* If there's no more queued, reset nqueued */
+ ifm->ifq_so->so_nqueued = 0;
+ }
+
+ /* Encapsulate the packet for sending */
+ if_encap(slirp, (uint8_t *)ifm->m_data, ifm->m_len);
+
+ m_free(ifm);
+
+ if (slirp->if_queued)
+ goto again;
+}
diff --git a/slirp/if.h b/slirp/if.h
new file mode 100644
index 0000000..2dac1c7
--- /dev/null
+++ b/slirp/if.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IF_H_
+#define _IF_H_
+
+#define IF_COMPRESS 0x01 /* We want compression */
+#define IF_NOCOMPRESS 0x02 /* Do not do compression */
+#define IF_AUTOCOMP 0x04 /* Autodetect (default) */
+#define IF_NOCIDCOMP 0x08 /* CID compression */
+
+#define IF_MTU 1500
+#define IF_MRU 1500
+#define IF_COMP IF_AUTOCOMP /* Flags for compression */
+
+/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
+#define IF_MAXLINKHDR (2 + 14 + 40)
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+#endif
diff --git a/slirp/ip.h b/slirp/ip.h
new file mode 100644
index 0000000..48ea38e
--- /dev/null
+++ b/slirp/ip.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
+ */
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#ifdef HOST_WORDS_BIGENDIAN
+# ifndef NTOHL
+# define NTOHL(d)
+# endif
+# ifndef NTOHS
+# define NTOHS(d)
+# endif
+# ifndef HTONL
+# define HTONL(d)
+# endif
+# ifndef HTONS
+# define HTONS(d)
+# endif
+#else
+# ifndef NTOHL
+# define NTOHL(d) ((d) = ntohl((d)))
+# endif
+# ifndef NTOHS
+# define NTOHS(d) ((d) = ntohs((uint16_t)(d)))
+# endif
+# ifndef HTONL
+# define HTONL(d) ((d) = htonl((d)))
+# endif
+# ifndef HTONS
+# define HTONS(d) ((d) = htons((uint16_t)(d)))
+# endif
+#endif
+
+typedef uint32_t n_long; /* long as received from the net */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip {
+#ifdef HOST_WORDS_BIGENDIAN
+ u_int ip_v:4, /* version */
+ ip_hl:4; /* header length */
+#else
+ u_int ip_hl:4, /* header length */
+ ip_v:4; /* version */
+#endif
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* don't fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
+ struct in_addr ip_src,ip_dst; /* source and dest address */
+} __attribute__((packed));
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ uint8_t ipt_code; /* IPOPT_TS */
+ uint8_t ipt_len; /* size of structure (variable) */
+ uint8_t ipt_ptr; /* index of current entry */
+#ifdef HOST_WORDS_BIGENDIAN
+ u_int ipt_oflw:4, /* overflow counter */
+ ipt_flg:4; /* flags, see below */
+#else
+ u_int ipt_flg:4, /* flags, see below */
+ ipt_oflw:4; /* overflow counter */
+#endif
+ union ipt_timestamp {
+ n_long ipt_time[1];
+ struct ipt_ta {
+ struct in_addr ipt_addr;
+ n_long ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+} __attribute__((packed));
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
+
+#if SIZEOF_CHAR_P == 4
+struct mbuf_ptr {
+ struct mbuf *mptr;
+ uint32_t dummy;
+} __attribute__((packed));
+#else
+struct mbuf_ptr {
+ struct mbuf *mptr;
+} __attribute__((packed));
+#endif
+struct qlink {
+ void *next, *prev;
+};
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+ struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
+ uint8_t ih_x1; /* (unused) */
+ uint8_t ih_pr; /* protocol */
+ uint16_t ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+} __attribute__((packed));
+
+/*
+ * Ip reassembly queue structure. Each fragment
+ * being reassembled is attached to one of these structures.
+ * They are timed out after ipq_ttl drops to 0, and may also
+ * be reclaimed if memory becomes tight.
+ * size 28 bytes
+ */
+struct ipq {
+ struct qlink frag_link; /* to ip headers of fragments */
+ struct qlink ip_link; /* to other reass headers */
+ uint8_t ipq_ttl; /* time for reass q to live */
+ uint8_t ipq_p; /* protocol of this fragment */
+ uint16_t ipq_id; /* sequence id for reassembly */
+ struct in_addr ipq_src,ipq_dst;
+} __attribute__((packed));
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_link must be at same offset as frag_link above
+ */
+struct ipasfrag {
+ struct qlink ipf_link;
+ struct ip ipf_ip;
+} __attribute__((packed));
+
+#define ipf_off ipf_ip.ip_off
+#define ipf_tos ipf_ip.ip_tos
+#define ipf_len ipf_ip.ip_len
+#define ipf_next ipf_link.next
+#define ipf_prev ipf_link.prev
+
+/*
+ * Structure stored in mbuf in inpcb.ip_options
+ * and passed to ip_output when ip options are in use.
+ * The actual length of the options (including ipopt_dst)
+ * is in m_len.
+ */
+#define MAX_IPOPTLEN 40
+
+struct ipoption {
+ struct in_addr ipopt_dst; /* first-hop dst if source routed */
+ int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */
+} __attribute__((packed));
+
+#endif
diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c
new file mode 100644
index 0000000..751a8e2
--- /dev/null
+++ b/slirp/ip_icmp.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+/* The message sent when emulating PING */
+/* Be nice and tell them it's just a pseudo-ping packet */
+static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
+
+/* list of actions for icmp_error() on RX of an icmp message */
+static const int icmp_flush[19] = {
+/* ECHO REPLY (0) */ 0,
+ 1,
+ 1,
+/* DEST UNREACH (3) */ 1,
+/* SOURCE QUENCH (4)*/ 1,
+/* REDIRECT (5) */ 1,
+ 1,
+ 1,
+/* ECHO (8) */ 0,
+/* ROUTERADVERT (9) */ 1,
+/* ROUTERSOLICIT (10) */ 1,
+/* TIME EXCEEDED (11) */ 1,
+/* PARAMETER PROBLEM (12) */ 1,
+/* TIMESTAMP (13) */ 0,
+/* TIMESTAMP REPLY (14) */ 0,
+/* INFO (15) */ 0,
+/* INFO REPLY (16) */ 0,
+/* ADDR MASK (17) */ 0,
+/* ADDR MASK REPLY (18) */ 0
+};
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(struct mbuf *m, int hlen)
+{
+ register struct icmp *icp;
+ register struct ip *ip=mtod(m, struct ip *);
+ int icmplen=ip->ip_len;
+ Slirp *slirp = m->slirp;
+
+ DEBUG_CALL("icmp_input");
+ DEBUG_ARG("m = %lx", (long )m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ /*
+ * Locate icmp structure in mbuf, and check
+ * that its not corrupted and of at least minimum length.
+ */
+ if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
+ freeit:
+ m_freem(m);
+ goto end_error;
+ }
+
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ icp = mtod(m, struct icmp *);
+ if (cksum(m, icmplen)) {
+ goto freeit;
+ }
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ DEBUG_ARG("icmp_type = %d", icp->icmp_type);
+ switch (icp->icmp_type) {
+ case ICMP_ECHO:
+ icp->icmp_type = ICMP_ECHOREPLY;
+ ip->ip_len += hlen; /* since ip_input subtracts this */
+ if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
+ icmp_reflect(m);
+ } else {
+ struct socket *so;
+ struct sockaddr_in addr;
+ if ((so = socreate(slirp)) == NULL) goto freeit;
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ m_free(m);
+ goto end_error;
+ }
+ so->so_m = m;
+ so->so_faddr = ip->ip_dst;
+ so->so_fport = htons(7);
+ so->so_laddr = ip->ip_src;
+ so->so_lport = htons(9);
+ so->so_iptos = ip->ip_tos;
+ so->so_type = IPPROTO_ICMP;
+ so->so_state = SS_ISFCONNECTED;
+
+ /* Send the packet */
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&addr.sin_addr) < 0)
+ addr.sin_addr = loopback_addr;
+ } else {
+ addr.sin_addr = loopback_addr;
+ }
+ } else {
+ addr.sin_addr = so->so_faddr;
+ }
+ addr.sin_port = so->so_fport;
+ if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
+ (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ udp_detach(so);
+ }
+ } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
+ break;
+ case ICMP_UNREACH:
+ /* XXX? report error? close socket? */
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TSTAMP:
+ case ICMP_MASKREQ:
+ case ICMP_REDIRECT:
+ m_freem(m);
+ break;
+
+ default:
+ m_freem(m);
+ } /* swith */
+
+end_error:
+ /* m is m_free()'d xor put in a socket xor or given to ip_send */
+ return;
+}
+
+
+/*
+ * Send an ICMP message in response to a situation
+ *
+ * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ * MUST NOT change this header information.
+ * MUST NOT reply to a multicast/broadcast IP address.
+ * MUST NOT reply to a multicast/broadcast MAC address.
+ * MUST reply to only the first fragment.
+ */
+/*
+ * Send ICMP_UNREACH back to the source regarding msrc.
+ * mbuf *msrc is used as a template, but is NOT m_free()'d.
+ * It is reported as the bad ip packet. The header should
+ * be fully correct and in host byte order.
+ * ICMP fragmentation is illegal. All machines must accept 576 bytes in one
+ * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
+ */
+
+#define ICMP_MAXDATALEN (IP_MSS-28)
+void
+icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message)
+{
+ unsigned hlen, shlen, s_ip_len;
+ register struct ip *ip;
+ register struct icmp *icp;
+ register struct mbuf *m;
+
+ DEBUG_CALL("icmp_error");
+ DEBUG_ARG("msrc = %lx", (long )msrc);
+ DEBUG_ARG("msrc_len = %d", msrc->m_len);
+
+ if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
+
+ /* check msrc */
+ if(!msrc) goto end_error;
+ ip = mtod(msrc, struct ip *);
+#ifdef DEBUG
+ { char bufa[20], bufb[20];
+ strcpy(bufa, inet_ntoa(ip->ip_src));
+ strcpy(bufb, inet_ntoa(ip->ip_dst));
+ DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb));
+ }
+#endif
+ if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */
+
+ shlen=ip->ip_hl << 2;
+ s_ip_len=ip->ip_len;
+ if(ip->ip_p == IPPROTO_ICMP) {
+ icp = (struct icmp *)((char *)ip + shlen);
+ /*
+ * Assume any unknown ICMP type is an error. This isn't
+ * specified by the RFC, but think about it..
+ */
+ if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error;
+ }
+
+ /* make a copy */
+ m = m_get(msrc->slirp);
+ if (!m) {
+ goto end_error;
+ }
+
+ { int new_m_size;
+ new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN;
+ if(new_m_size>m->m_size) m_inc(m, new_m_size);
+ }
+ memcpy(m->m_data, msrc->m_data, msrc->m_len);
+ m->m_len = msrc->m_len; /* copy msrc to m */
+
+ /* make the header of the reply packet */
+ ip = mtod(m, struct ip *);
+ hlen= sizeof(struct ip ); /* no options in reply */
+
+ /* fill in icmp */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ icp = mtod(m, struct icmp *);
+
+ if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */
+ else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */
+ s_ip_len=ICMP_MAXDATALEN;
+
+ m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */
+
+ /* min. size = 8+sizeof(struct ip)+8 */
+
+ icp->icmp_type = type;
+ icp->icmp_code = code;
+ icp->icmp_id = 0;
+ icp->icmp_seq = 0;
+
+ memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */
+ HTONS(icp->icmp_ip.ip_len);
+ HTONS(icp->icmp_ip.ip_id);
+ HTONS(icp->icmp_ip.ip_off);
+
+#ifdef DEBUG
+ if(message) { /* DEBUG : append message to ICMP packet */
+ int message_len;
+ char *cpnt;
+ message_len=strlen(message);
+ if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN;
+ cpnt=(char *)m->m_data+m->m_len;
+ memcpy(cpnt, message, message_len);
+ m->m_len+=message_len;
+ }
+#endif
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, m->m_len);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len = m->m_len;
+
+ ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
+
+ ip->ip_ttl = MAXTTL;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_dst = ip->ip_src; /* ip adresses */
+ ip->ip_src = m->slirp->vhost_addr;
+
+ (void ) ip_output((struct socket *)NULL, m);
+
+end_error:
+ return;
+}
+#undef ICMP_MAXDATALEN
+
+/*
+ * Reflect the ip packet back to the source
+ */
+void
+icmp_reflect(struct mbuf *m)
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ int optlen = hlen - sizeof(struct ip );
+ register struct icmp *icp;
+
+ /*
+ * Send an icmp packet back to the ip level,
+ * after supplying a checksum.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ /* fill in ip */
+ if (optlen > 0) {
+ /*
+ * Strip out original options by copying rest of first
+ * mbuf's data back, and adjust the IP length.
+ */
+ memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen,
+ (unsigned )(m->m_len - hlen));
+ hlen -= optlen;
+ ip->ip_hl = hlen >> 2;
+ ip->ip_len -= optlen;
+ m->m_len -= optlen;
+ }
+
+ ip->ip_ttl = MAXTTL;
+ { /* swap */
+ struct in_addr icmp_dst;
+ icmp_dst = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = icmp_dst;
+ }
+
+ (void ) ip_output((struct socket *)NULL, m);
+}
diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h
new file mode 100644
index 0000000..2692822
--- /dev/null
+++ b/slirp/ip_icmp.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
+ */
+
+#ifndef _NETINET_IP_ICMP_H_
+#define _NETINET_IP_ICMP_H_
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+typedef uint32_t n_time;
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ u_char icmp_type; /* type of message, see below */
+ u_char icmp_code; /* type sub code */
+ u_short icmp_cksum; /* ones complement cksum of struct */
+ union {
+ u_char ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ u_short icd_id;
+ u_short icd_seq;
+ } ih_idseq;
+ int ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu {
+ u_short ipm_void;
+ u_short ipm_nextmtu;
+ } ih_pmtu;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+ union {
+ struct id_ts {
+ n_time its_otime;
+ n_time its_rtime;
+ n_time its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ uint32_t id_mask;
+ char id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+void icmp_input(struct mbuf *, int);
+void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
+ const char *message);
+void icmp_reflect(struct mbuf *);
+
+#endif
diff --git a/slirp/ip_input.c b/slirp/ip_input.c
new file mode 100644
index 0000000..768ab0c
--- /dev/null
+++ b/slirp/ip_input.c
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <osdep.h>
+#include "ip_icmp.h"
+
+static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp);
+static void ip_freef(Slirp *slirp, struct ipq *fp);
+static void ip_enq(register struct ipasfrag *p,
+ register struct ipasfrag *prev);
+static void ip_deq(register struct ipasfrag *p);
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init(Slirp *slirp)
+{
+ slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;
+ udp_init(slirp);
+ tcp_init(slirp);
+}
+
+/*
+ * Ip input routine. Checksum and byte swap header. If fragmented
+ * try to reassemble. Process options. Pass to next level.
+ */
+void
+ip_input(struct mbuf *m)
+{
+ Slirp *slirp = m->slirp;
+ register struct ip *ip;
+ int hlen;
+
+ DEBUG_CALL("ip_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m_len = %d", m->m_len);
+
+ if (m->m_len < sizeof (struct ip)) {
+ return;
+ }
+
+ ip = mtod(m, struct ip *);
+
+ if (ip->ip_v != IPVERSION) {
+ goto bad;
+ }
+
+ hlen = ip->ip_hl << 2;
+ if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
+ goto bad; /* or packet too short */
+ }
+
+ /* keep ip header intact for ICMP reply
+ * ip->ip_sum = cksum(m, hlen);
+ * if (ip->ip_sum) {
+ */
+ if(cksum(m,hlen)) {
+ goto bad;
+ }
+
+ /*
+ * Convert fields to host representation.
+ */
+ NTOHS(ip->ip_len);
+ if (ip->ip_len < hlen) {
+ goto bad;
+ }
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (m->m_len < ip->ip_len) {
+ goto bad;
+ }
+
+ if (slirp->restricted) {
+ if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP)
+ goto bad;
+ } else {
+ uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
+ struct ex_list *ex_ptr;
+
+ if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) {
+ goto bad;
+ }
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr)
+ break;
+
+ if (!ex_ptr)
+ goto bad;
+ }
+ }
+
+ /* Should drop packet if mbuf too long? hmmm... */
+ if (m->m_len > ip->ip_len)
+ m_adj(m, ip->ip_len - m->m_len);
+
+ /* check ip_ttl for a correct ICMP reply */
+ if(ip->ip_ttl==0) {
+ icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
+ goto bad;
+ }
+
+ /*
+ * If offset or IP_MF are set, must reassemble.
+ * Otherwise, nothing need be done.
+ * (We could look in the reassembly queue to see
+ * if the packet was previously fragmented,
+ * but it's not worth the time; just let them time out.)
+ *
+ * XXX This should fail, don't fragment yet
+ */
+ if (ip->ip_off &~ IP_DF) {
+ register struct ipq *fp;
+ struct qlink *l;
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link;
+ l = l->next) {
+ fp = container_of(l, struct ipq, ip_link);
+ if (ip->ip_id == fp->ipq_id &&
+ ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+ ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+ ip->ip_p == fp->ipq_p)
+ goto found;
+ }
+ fp = NULL;
+ found:
+
+ /*
+ * Adjust ip_len to not reflect header,
+ * set ip_mff if more fragments are expected,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ if (ip->ip_off & IP_MF)
+ ip->ip_tos |= 1;
+ else
+ ip->ip_tos &= ~1;
+
+ ip->ip_off <<= 3;
+
+ /*
+ * If datagram marked as having more fragments
+ * or if this is not the first fragment,
+ * attempt reassembly; if it succeeds, proceed.
+ */
+ if (ip->ip_tos & 1 || ip->ip_off) {
+ ip = ip_reass(slirp, ip, fp);
+ if (ip == NULL)
+ return;
+ m = dtom(slirp, ip);
+ } else
+ if (fp)
+ ip_freef(slirp, fp);
+
+ } else
+ ip->ip_len -= hlen;
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ tcp_input(m, hlen, (struct socket *)NULL);
+ break;
+ case IPPROTO_UDP:
+ udp_input(m, hlen);
+ break;
+ case IPPROTO_ICMP:
+ icmp_input(m, hlen);
+ break;
+ default:
+ m_free(m);
+ }
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+#define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink)))
+#define fragtoip(P) ((struct ip*)(((char*)(P)) + sizeof(struct qlink)))
+/*
+ * Take incoming datagram fragment and try to
+ * reassemble it into whole datagram. If a chain for
+ * reassembly of this datagram already exists, then it
+ * is given as fp; otherwise have to make a chain.
+ */
+static struct ip *
+ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
+{
+ register struct mbuf *m = dtom(slirp, ip);
+ register struct ipasfrag *q;
+ int hlen = ip->ip_hl << 2;
+ int i, next;
+
+ DEBUG_CALL("ip_reass");
+ DEBUG_ARG("ip = %lx", (long)ip);
+ DEBUG_ARG("fp = %lx", (long)fp);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ * Fragment m_data is concatenated.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ /*
+ * If first fragment to arrive, create a reassembly queue.
+ */
+ if (fp == NULL) {
+ struct mbuf *t = m_get(slirp);
+
+ if (t == NULL) {
+ goto dropfrag;
+ }
+ fp = mtod(t, struct ipq *);
+ insque(&fp->ip_link, &slirp->ipq.ip_link);
+ fp->ipq_ttl = IPFRAGTTL;
+ fp->ipq_p = ip->ip_p;
+ fp->ipq_id = ip->ip_id;
+ fp->frag_link.next = fp->frag_link.prev = &fp->frag_link;
+ fp->ipq_src = ip->ip_src;
+ fp->ipq_dst = ip->ip_dst;
+ q = (struct ipasfrag *)fp;
+ goto insert;
+ }
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link;
+ q = q->ipf_next)
+ if (q->ipf_off > ip->ip_off)
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (q->ipf_prev != &fp->frag_link) {
+ struct ipasfrag *pq = q->ipf_prev;
+ i = pq->ipf_off + pq->ipf_len - ip->ip_off;
+ if (i > 0) {
+ if (i >= ip->ip_len)
+ goto dropfrag;
+ m_adj(dtom(slirp, ip), i);
+ ip->ip_off += i;
+ ip->ip_len -= i;
+ }
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q != (struct ipasfrag*)&fp->frag_link &&
+ ip->ip_off + ip->ip_len > q->ipf_off) {
+ i = (ip->ip_off + ip->ip_len) - q->ipf_off;
+ if (i < q->ipf_len) {
+ q->ipf_len -= i;
+ q->ipf_off += i;
+ m_adj(dtom(slirp, q), i);
+ break;
+ }
+ q = q->ipf_next;
+ m_freem(dtom(slirp, q->ipf_prev));
+ ip_deq(q->ipf_prev);
+ }
+
+insert:
+ /*
+ * Stick new segment in its place;
+ * check for complete reassembly.
+ */
+ ip_enq(iptofrag(ip), q->ipf_prev);
+ next = 0;
+ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link;
+ q = q->ipf_next) {
+ if (q->ipf_off != next)
+ return NULL;
+ next += q->ipf_len;
+ }
+ if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1)
+ return NULL;
+
+ /*
+ * Reassembly is complete; concatenate fragments.
+ */
+ q = fp->frag_link.next;
+ m = dtom(slirp, q);
+
+ q = (struct ipasfrag *) q->ipf_next;
+ while (q != (struct ipasfrag*)&fp->frag_link) {
+ struct mbuf *t = dtom(slirp, q);
+ q = (struct ipasfrag *) q->ipf_next;
+ m_cat(m, t);
+ }
+
+ /*
+ * Create header for new ip packet by
+ * modifying header of first packet;
+ * dequeue and discard fragment reassembly header.
+ * Make header visible.
+ */
+ q = fp->frag_link.next;
+
+ /*
+ * If the fragments concatenated to an mbuf that's
+ * bigger than the total size of the fragment, then and
+ * m_ext buffer was alloced. But fp->ipq_next points to
+ * the old buffer (in the mbuf), so we must point ip
+ * into the new buffer.
+ */
+ if (m->m_flags & M_EXT) {
+ int delta = (char *)q - m->m_dat;
+ q = (struct ipasfrag *)(m->m_ext + delta);
+ }
+
+ ip = fragtoip(q);
+ ip->ip_len = next;
+ ip->ip_tos &= ~1;
+ ip->ip_src = fp->ipq_src;
+ ip->ip_dst = fp->ipq_dst;
+ remque(&fp->ip_link);
+ (void) m_free(dtom(slirp, fp));
+ m->m_len += (ip->ip_hl << 2);
+ m->m_data -= (ip->ip_hl << 2);
+
+ return ip;
+
+dropfrag:
+ m_freem(m);
+ return NULL;
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+static void
+ip_freef(Slirp *slirp, struct ipq *fp)
+{
+ register struct ipasfrag *q, *p;
+
+ for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) {
+ p = q->ipf_next;
+ ip_deq(q);
+ m_freem(dtom(slirp, q));
+ }
+ remque(&fp->ip_link);
+ (void) m_free(dtom(slirp, fp));
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+static void
+ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
+{
+ DEBUG_CALL("ip_enq");
+ DEBUG_ARG("prev = %lx", (long)prev);
+ p->ipf_prev = prev;
+ p->ipf_next = prev->ipf_next;
+ ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p;
+ prev->ipf_next = p;
+}
+
+/*
+ * To ip_enq as remque is to insque.
+ */
+static void
+ip_deq(register struct ipasfrag *p)
+{
+ ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
+ ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo(Slirp *slirp)
+{
+ struct qlink *l;
+
+ DEBUG_CALL("ip_slowtimo");
+
+ l = slirp->ipq.ip_link.next;
+
+ if (l == NULL)
+ return;
+
+ while (l != &slirp->ipq.ip_link) {
+ struct ipq *fp = container_of(l, struct ipq, ip_link);
+ l = l->next;
+ if (--fp->ipq_ttl == 0) {
+ ip_freef(slirp, fp);
+ }
+ }
+}
+
+/*
+ * Do option processing on a datagram,
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
+ */
+
+#ifdef notdef
+
+int
+ip_dooptions(m)
+ struct mbuf *m;
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register u_char *cp;
+ register struct ip_timestamp *ipt;
+ register struct in_ifaddr *ia;
+ int opt, optlen, cnt, off, code, type, forward = 0;
+ struct in_addr *sin, dst;
+typedef uint32_t n_time;
+ n_time ntime;
+
+ dst = ip->ip_dst;
+ cp = (u_char *)(ip + 1);
+ cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[IPOPT_OPTVAL];
+ if (opt == IPOPT_EOL)
+ break;
+ if (opt == IPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[IPOPT_OLEN];
+ if (optlen <= 0 || optlen > cnt) {
+ code = &cp[IPOPT_OLEN] - (u_char *)ip;
+ goto bad;
+ }
+ }
+ switch (opt) {
+
+ default:
+ break;
+
+ /*
+ * Source routing with record.
+ * Find interface with current destination address.
+ * If none on this machine then drop if strictly routed,
+ * or do nothing if loosely routed.
+ * Record interface address and bring up next address
+ * component. If strictly routed make sure next
+ * address is on directly accessible net.
+ */
+ case IPOPT_LSRR:
+ case IPOPT_SSRR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ ipaddr.sin_addr = ip->ip_dst;
+ ia = (struct in_ifaddr *)
+ ifa_ifwithaddr((struct sockaddr *)&ipaddr);
+ if (ia == 0) {
+ if (opt == IPOPT_SSRR) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ /*
+ * Loose routing, and not at next destination
+ * yet; nothing to do except forward.
+ */
+ break;
+ }
+ off--; / * 0 origin * /
+ if (off > optlen - sizeof(struct in_addr)) {
+ /*
+ * End of source route. Should be for us.
+ */
+ save_rte(cp, ip->ip_src);
+ break;
+ }
+ /*
+ * locate outgoing interface
+ */
+ bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ if (opt == IPOPT_SSRR) {
+#define INA struct in_ifaddr *
+#define SA struct sockaddr *
+ if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+ ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+ } else
+ ia = ip_rtaddr(ipaddr.sin_addr);
+ if (ia == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_SRCFAIL;
+ goto bad;
+ }
+ ip->ip_dst = ipaddr.sin_addr;
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ /*
+ * Let ip_intr's mcast routing check handle mcast pkts
+ */
+ forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
+ break;
+
+ case IPOPT_RR:
+ if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+ code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+ goto bad;
+ }
+ /*
+ * If no space remains, ignore.
+ */
+ off--; * 0 origin *
+ if (off > optlen - sizeof(struct in_addr))
+ break;
+ bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
+ sizeof(ipaddr.sin_addr));
+ /*
+ * locate outgoing interface; if we're the destination,
+ * use the incoming interface (should be same).
+ */
+ if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+ (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+ type = ICMP_UNREACH;
+ code = ICMP_UNREACH_HOST;
+ goto bad;
+ }
+ bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+ (caddr_t)(cp + off), sizeof(struct in_addr));
+ cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS:
+ code = cp - (u_char *)ip;
+ ipt = (struct ip_timestamp *)cp;
+ if (ipt->ipt_len < 5)
+ goto bad;
+ if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
+ if (++ipt->ipt_oflw == 0)
+ goto bad;
+ break;
+ }
+ sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
+ switch (ipt->ipt_flg) {
+
+ case IPOPT_TS_TSONLY:
+ break;
+
+ case IPOPT_TS_TSANDADDR:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ ipaddr.sin_addr = dst;
+ ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
+ m->m_pkthdr.rcvif);
+ if (ia == 0)
+ continue;
+ bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
+ (caddr_t)sin, sizeof(struct in_addr));
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ case IPOPT_TS_PRESPEC:
+ if (ipt->ipt_ptr + sizeof(n_time) +
+ sizeof(struct in_addr) > ipt->ipt_len)
+ goto bad;
+ bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+ sizeof(struct in_addr));
+ if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+ continue;
+ ipt->ipt_ptr += sizeof(struct in_addr);
+ break;
+
+ default:
+ goto bad;
+ }
+ ntime = iptime();
+ bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+ sizeof(n_time));
+ ipt->ipt_ptr += sizeof(n_time);
+ }
+ }
+ if (forward) {
+ ip_forward(m, 1);
+ return (1);
+ }
+ return (0);
+bad:
+ icmp_error(m, type, code, 0, 0);
+
+ return (1);
+}
+
+#endif /* notdef */
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * (XXX) should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(register struct mbuf *m, struct mbuf *mopt)
+{
+ register int i;
+ struct ip *ip = mtod(m, struct ip *);
+ register caddr_t opts;
+ int olen;
+
+ olen = (ip->ip_hl<<2) - sizeof (struct ip);
+ opts = (caddr_t)(ip + 1);
+ i = m->m_len - (sizeof (struct ip) + olen);
+ memcpy(opts, opts + olen, (unsigned)i);
+ m->m_len -= olen;
+
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
diff --git a/slirp/ip_output.c b/slirp/ip_output.c
new file mode 100644
index 0000000..542f318
--- /dev/null
+++ b/slirp/ip_output.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/* Number of packets queued before we start sending
+ * (to prevent allocing too many mbufs) */
+#define IF_THRESH 10
+
+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(struct socket *so, struct mbuf *m0)
+{
+ Slirp *slirp = m0->slirp;
+ register struct ip *ip;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof(struct ip );
+ int len, off, error = 0;
+
+ DEBUG_CALL("ip_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m0 = %lx", (long)m0);
+
+ ip = mtod(m, struct ip *);
+ /*
+ * Fill in IP header.
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = htons(slirp->ip_id++);
+ ip->ip_hl = hlen >> 2;
+
+ /*
+ * If small enough for interface, can just send directly.
+ */
+ if ((uint16_t)ip->ip_len <= IF_MTU) {
+ ip->ip_len = htons((uint16_t)ip->ip_len);
+ ip->ip_off = htons((uint16_t)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+
+ if_output(so, m);
+ goto done;
+ }
+
+ /*
+ * Too large for interface; fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ if (ip->ip_off & IP_DF) {
+ error = -1;
+ goto bad;
+ }
+
+ len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */
+ if (len < 8) {
+ error = -1;
+ goto bad;
+ }
+
+ {
+ int mhlen, firstlen = len;
+ struct mbuf **mnext = &m->m_nextpkt;
+
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ mhlen = sizeof (struct ip);
+ for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) {
+ register struct ip *mhip;
+ m = m_get(slirp);
+ if (m == NULL) {
+ error = -1;
+ goto sendorfree;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ mhip = mtod(m, struct ip *);
+ *mhip = *ip;
+
+ m->m_len = mhlen;
+ mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
+ if (ip->ip_off & IP_MF)
+ mhip->ip_off |= IP_MF;
+ if (off + len >= (uint16_t)ip->ip_len)
+ len = (uint16_t)ip->ip_len - off;
+ else
+ mhip->ip_off |= IP_MF;
+ mhip->ip_len = htons((uint16_t)(len + mhlen));
+
+ if (m_copy(m, m0, off, len) < 0) {
+ error = -1;
+ goto sendorfree;
+ }
+
+ mhip->ip_off = htons((uint16_t)mhip->ip_off);
+ mhip->ip_sum = 0;
+ mhip->ip_sum = cksum(m, mhlen);
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ }
+ /*
+ * Update first fragment by trimming what's been copied out
+ * and updating header, then send each fragment (in order).
+ */
+ m = m0;
+ m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len);
+ ip->ip_len = htons((uint16_t)m->m_len);
+ ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF));
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+sendorfree:
+ for (m = m0; m; m = m0) {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = NULL;
+ if (error == 0)
+ if_output(so, m);
+ else
+ m_freem(m);
+ }
+ }
+
+done:
+ return (error);
+
+bad:
+ m_freem(m0);
+ goto done;
+}
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
new file mode 100644
index 0000000..67c70e3
--- /dev/null
+++ b/slirp/libslirp.h
@@ -0,0 +1,56 @@
+#ifndef _LIBSLIRP_H
+#define _LIBSLIRP_H
+
+#include <qemu-common.h>
+
+#ifdef CONFIG_SLIRP
+
+struct Slirp;
+typedef struct Slirp Slirp;
+
+int get_dns_addr(struct in_addr *pdns_addr);
+
+Slirp *slirp_init(int restricted, struct in_addr vnetwork,
+ struct in_addr vnetmask, struct in_addr vhost,
+ const char *vhostname, const char *tftp_path,
+ const char *bootfile, struct in_addr vdhcp_start,
+ struct in_addr vnameserver, void *opaque);
+void slirp_cleanup(Slirp *slirp);
+
+void slirp_select_fill(int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
+ int select_error);
+
+void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
+
+/* you must provide the following functions: */
+int slirp_can_output(void *opaque);
+void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len);
+
+int slirp_add_hostfwd(Slirp *slirp, int is_udp,
+ struct in_addr host_addr, int host_port,
+ struct in_addr guest_addr, int guest_port);
+int slirp_remove_hostfwd(Slirp *slirp, int is_udp,
+ struct in_addr host_addr, int host_port);
+int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
+ struct in_addr *guest_addr, int guest_port);
+
+void slirp_connection_info(Slirp *slirp, Monitor *mon);
+
+void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr,
+ int guest_port, const uint8_t *buf, int size);
+size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
+ int guest_port);
+
+#else /* !CONFIG_SLIRP */
+
+static inline void slirp_select_fill(int *pnfds, fd_set *readfds,
+ fd_set *writefds, fd_set *xfds) { }
+
+static inline void slirp_select_poll(fd_set *readfds, fd_set *writefds,
+ fd_set *xfds, int select_error) { }
+#endif /* !CONFIG_SLIRP */
+
+#endif
diff --git a/slirp/main.h b/slirp/main.h
new file mode 100644
index 0000000..0dd8d81
--- /dev/null
+++ b/slirp/main.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define TOWRITEMAX 512
+
+extern int slirp_socket;
+extern int slirp_socket_unit;
+extern int slirp_socket_port;
+extern uint32_t slirp_socket_addr;
+extern char *slirp_socket_passwd;
+extern int ctty_closed;
+
+/*
+ * Get the difference in 2 times from updtim()
+ * Allow for wraparound times, "just in case"
+ * x is the greater of the 2 (current time) and y is
+ * what it's being compared against.
+ */
+#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y)
+
+extern char *slirp_tty;
+extern char *exec_shell;
+extern u_int curtime;
+extern fd_set *global_readfds, *global_writefds, *global_xfds;
+extern struct in_addr loopback_addr;
+extern char *username;
+extern char *socket_path;
+extern int towrite_max;
+extern int ppp_exit;
+extern int tcp_keepintvl;
+
+#define PROTO_SLIP 0x1
+#ifdef USE_PPP
+#define PROTO_PPP 0x2
+#endif
+
+void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len);
+ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);
diff --git a/slirp/mbuf.c b/slirp/mbuf.c
new file mode 100644
index 0000000..ce2eb84
--- /dev/null
+++ b/slirp/mbuf.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/*
+ * mbuf's in SLiRP are much simpler than the real mbufs in
+ * FreeBSD. They are fixed size, determined by the MTU,
+ * so that one whole packet can fit. Mbuf's cannot be
+ * chained together. If there's more data than the mbuf
+ * could hold, an external malloced buffer is pointed to
+ * by m_ext (and the data pointers) and M_EXT is set in
+ * the flags
+ */
+
+#include <slirp.h>
+
+#define MBUF_THRESH 30
+
+/*
+ * Find a nice value for msize
+ * XXX if_maxlinkhdr already in mtu
+ */
+#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
+
+void
+m_init(Slirp *slirp)
+{
+ slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist;
+ slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist;
+}
+
+/*
+ * Get an mbuf from the free list, if there are none
+ * malloc one
+ *
+ * Because fragmentation can occur if we alloc new mbufs and
+ * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
+ * which tells m_free to actually free() it
+ */
+struct mbuf *
+m_get(Slirp *slirp)
+{
+ register struct mbuf *m;
+ int flags = 0;
+
+ DEBUG_CALL("m_get");
+
+ if (slirp->m_freelist.m_next == &slirp->m_freelist) {
+ m = (struct mbuf *)malloc(SLIRP_MSIZE);
+ if (m == NULL) goto end_error;
+ slirp->mbuf_alloced++;
+ if (slirp->mbuf_alloced > MBUF_THRESH)
+ flags = M_DOFREE;
+ m->slirp = slirp;
+ } else {
+ m = slirp->m_freelist.m_next;
+ remque(m);
+ }
+
+ /* Insert it in the used list */
+ insque(m,&slirp->m_usedlist);
+ m->m_flags = (flags | M_USEDLIST);
+
+ /* Initialise it */
+ m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
+ m->m_data = m->m_dat;
+ m->m_len = 0;
+ m->m_nextpkt = NULL;
+ m->m_prevpkt = NULL;
+end_error:
+ DEBUG_ARG("m = %lx", (long )m);
+ return m;
+}
+
+void
+m_free(struct mbuf *m)
+{
+
+ DEBUG_CALL("m_free");
+ DEBUG_ARG("m = %lx", (long )m);
+
+ if(m) {
+ /* Remove from m_usedlist */
+ if (m->m_flags & M_USEDLIST)
+ remque(m);
+
+ /* If it's M_EXT, free() it */
+ if (m->m_flags & M_EXT)
+ free(m->m_ext);
+
+ /*
+ * Either free() it or put it on the free list
+ */
+ if (m->m_flags & M_DOFREE) {
+ m->slirp->mbuf_alloced--;
+ free(m);
+ } else if ((m->m_flags & M_FREELIST) == 0) {
+ insque(m,&m->slirp->m_freelist);
+ m->m_flags = M_FREELIST; /* Clobber other flags */
+ }
+ } /* if(m) */
+}
+
+/*
+ * Copy data from one mbuf to the end of
+ * the other.. if result is too big for one mbuf, malloc()
+ * an M_EXT data segment
+ */
+void
+m_cat(struct mbuf *m, struct mbuf *n)
+{
+ /*
+ * If there's no room, realloc
+ */
+ if (M_FREEROOM(m) < n->m_len)
+ m_inc(m,m->m_size+MINCSIZE);
+
+ memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
+ m->m_len += n->m_len;
+
+ m_free(n);
+}
+
+
+/* make m size bytes large */
+void
+m_inc(struct mbuf *m, int size)
+{
+ int datasize;
+
+ /* some compiles throw up on gotos. This one we can fake. */
+ if(m->m_size>size) return;
+
+ if (m->m_flags & M_EXT) {
+ datasize = m->m_data - m->m_ext;
+ m->m_ext = (char *)realloc(m->m_ext,size);
+ m->m_data = m->m_ext + datasize;
+ } else {
+ char *dat;
+ datasize = m->m_data - m->m_dat;
+ dat = (char *)malloc(size);
+ memcpy(dat, m->m_dat, m->m_size);
+
+ m->m_ext = dat;
+ m->m_data = m->m_ext + datasize;
+ m->m_flags |= M_EXT;
+ }
+
+ m->m_size = size;
+
+}
+
+
+
+void
+m_adj(struct mbuf *m, int len)
+{
+ if (m == NULL)
+ return;
+ if (len >= 0) {
+ /* Trim from head */
+ m->m_data += len;
+ m->m_len -= len;
+ } else {
+ /* Trim from tail */
+ len = -len;
+ m->m_len -= len;
+ }
+}
+
+
+/*
+ * Copy len bytes from m, starting off bytes into n
+ */
+int
+m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
+{
+ if (len > M_FREEROOM(n))
+ return -1;
+
+ memcpy((n->m_data + n->m_len), (m->m_data + off), len);
+ n->m_len += len;
+ return 0;
+}
+
+
+/*
+ * Given a pointer into an mbuf, return the mbuf
+ * XXX This is a kludge, I should eliminate the need for it
+ * Fortunately, it's not used often
+ */
+struct mbuf *
+dtom(Slirp *slirp, void *dat)
+{
+ struct mbuf *m;
+
+ DEBUG_CALL("dtom");
+ DEBUG_ARG("dat = %lx", (long )dat);
+
+ /* bug corrected for M_EXT buffers */
+ for (m = slirp->m_usedlist.m_next; m != &slirp->m_usedlist;
+ m = m->m_next) {
+ if (m->m_flags & M_EXT) {
+ if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
+ return m;
+ } else {
+ if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) )
+ return m;
+ }
+ }
+
+ DEBUG_ERROR((dfd, "dtom failed"));
+
+ return (struct mbuf *)0;
+}
diff --git a/slirp/mbuf.h b/slirp/mbuf.h
new file mode 100644
index 0000000..97729e2
--- /dev/null
+++ b/slirp/mbuf.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mbuf.h 8.3 (Berkeley) 1/21/94
+ * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp
+ */
+
+#ifndef _MBUF_H_
+#define _MBUF_H_
+
+#define m_freem m_free
+
+
+#define MINCSIZE 4096 /* Amount to increase mbuf if too small */
+
+/*
+ * Macros for type conversion
+ * mtod(m,t) - convert mbuf pointer to data pointer of correct type
+ */
+#define mtod(m,t) ((t)(m)->m_data)
+
+/* XXX About mbufs for slirp:
+ * Only one mbuf is ever used in a chain, for each "cell" of data.
+ * m_nextpkt points to the next packet, if fragmented.
+ * If the data is too large, the M_EXT is used, and a larger block
+ * is alloced. Therefore, m_free[m] must check for M_EXT and if set
+ * free the m_ext. This is inefficient memory-wise, but who cares.
+ */
+
+/* XXX should union some of these! */
+/* header at beginning of each mbuf: */
+struct m_hdr {
+ struct mbuf *mh_next; /* Linked list of mbufs */
+ struct mbuf *mh_prev;
+ struct mbuf *mh_nextpkt; /* Next packet in queue/record */
+ struct mbuf *mh_prevpkt; /* Flags aren't used in the output queue */
+ int mh_flags; /* Misc flags */
+
+ int mh_size; /* Size of data */
+ struct socket *mh_so;
+
+ caddr_t mh_data; /* Location of data */
+ int mh_len; /* Amount of data in this mbuf */
+};
+
+/*
+ * How much room is in the mbuf, from m_data to the end of the mbuf
+ */
+#define M_ROOM(m) ((m->m_flags & M_EXT)? \
+ (((m)->m_ext + (m)->m_size) - (m)->m_data) \
+ : \
+ (((m)->m_dat + (m)->m_size) - (m)->m_data))
+
+/*
+ * How much free room there is
+ */
+#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
+#define M_TRAILINGSPACE M_FREEROOM
+
+struct mbuf {
+ struct m_hdr m_hdr;
+ Slirp *slirp;
+ union M_dat {
+ char m_dat_[1]; /* ANSI don't like 0 sized arrays */
+ char *m_ext_;
+ } M_dat;
+};
+
+#define m_next m_hdr.mh_next
+#define m_prev m_hdr.mh_prev
+#define m_nextpkt m_hdr.mh_nextpkt
+#define m_prevpkt m_hdr.mh_prevpkt
+#define m_flags m_hdr.mh_flags
+#define m_len m_hdr.mh_len
+#define m_data m_hdr.mh_data
+#define m_size m_hdr.mh_size
+#define m_dat M_dat.m_dat_
+#define m_ext M_dat.m_ext_
+#define m_so m_hdr.mh_so
+
+#define ifq_prev m_prev
+#define ifq_next m_next
+#define ifs_prev m_prevpkt
+#define ifs_next m_nextpkt
+#define ifq_so m_so
+
+#define M_EXT 0x01 /* m_ext points to more (malloced) data */
+#define M_FREELIST 0x02 /* mbuf is on free list */
+#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */
+#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free()
+ * it rather than putting it on the free list */
+
+void m_init(Slirp *);
+struct mbuf * m_get(Slirp *);
+void m_free(struct mbuf *);
+void m_cat(register struct mbuf *, register struct mbuf *);
+void m_inc(struct mbuf *, int);
+void m_adj(struct mbuf *, int);
+int m_copy(struct mbuf *, struct mbuf *, int, int);
+struct mbuf * dtom(Slirp *, void *);
+
+#endif
diff --git a/slirp/misc.c b/slirp/misc.c
new file mode 100644
index 0000000..19dbec4
--- /dev/null
+++ b/slirp/misc.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <libslirp.h>
+
+#include "monitor.h"
+
+#ifdef DEBUG
+int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR;
+#endif
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+inline void
+insque(void *a, void *b)
+{
+ register struct quehead *element = (struct quehead *) a;
+ register struct quehead *head = (struct quehead *) b;
+ element->qh_link = head->qh_link;
+ head->qh_link = (struct quehead *)element;
+ element->qh_rlink = (struct quehead *)head;
+ ((struct quehead *)(element->qh_link))->qh_rlink
+ = (struct quehead *)element;
+}
+
+inline void
+remque(void *a)
+{
+ register struct quehead *element = (struct quehead *) a;
+ ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
+ ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
+ element->qh_rlink = NULL;
+}
+
+int add_exec(struct ex_list **ex_ptr, int do_pty, char *exec,
+ struct in_addr addr, int port)
+{
+ struct ex_list *tmp_ptr;
+
+ /* First, check if the port is "bound" */
+ for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
+ if (port == tmp_ptr->ex_fport &&
+ addr.s_addr == tmp_ptr->ex_addr.s_addr)
+ return -1;
+ }
+
+ tmp_ptr = *ex_ptr;
+ *ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
+ (*ex_ptr)->ex_fport = port;
+ (*ex_ptr)->ex_addr = addr;
+ (*ex_ptr)->ex_pty = do_pty;
+ (*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
+ (*ex_ptr)->ex_next = tmp_ptr;
+ return 0;
+}
+
+#ifndef HAVE_STRERROR
+
+/*
+ * For systems with no strerror
+ */
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(error)
+ int error;
+{
+ if (error < sys_nerr)
+ return sys_errlist[error];
+ else
+ return "Unknown error.";
+}
+
+#endif
+
+
+#ifdef _WIN32
+
+int
+fork_exec(struct socket *so, const char *ex, int do_pty)
+{
+ /* not implemented */
+ return 0;
+}
+
+#else
+
+/*
+ * XXX This is ugly
+ * We create and bind a socket, then fork off to another
+ * process, which connects to this socket, after which we
+ * exec the wanted program. If something (strange) happens,
+ * the accept() call could block us forever.
+ *
+ * do_pty = 0 Fork/exec inetd style
+ * do_pty = 1 Fork/exec using slirp.telnetd
+ * do_ptr = 2 Fork/exec using pty
+ */
+int
+fork_exec(struct socket *so, const char *ex, int do_pty)
+{
+ int s;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int opt;
+ int master = -1;
+ const char *argv[256];
+ /* don't want to clobber the original */
+ char *bptr;
+ const char *curarg;
+ int c, i, ret;
+
+ DEBUG_CALL("fork_exec");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("ex = %lx", (long)ex);
+ DEBUG_ARG("do_pty = %lx", (long)do_pty);
+
+ if (do_pty == 2) {
+ return 0;
+ } else {
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((s = qemu_socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
+ bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
+ listen(s, 1) < 0) {
+ lprint("Error: inet socket: %s\n", strerror(errno));
+ closesocket(s);
+
+ return 0;
+ }
+ }
+
+ switch(fork()) {
+ case -1:
+ lprint("Error: fork failed: %s\n", strerror(errno));
+ close(s);
+ if (do_pty == 2)
+ close(master);
+ return 0;
+
+ case 0:
+ /* Set the DISPLAY */
+ if (do_pty == 2) {
+ (void) close(master);
+#ifdef TIOCSCTTY /* XXXXX */
+ (void) setsid();
+ ioctl(s, TIOCSCTTY, (char *)NULL);
+#endif
+ } else {
+ getsockname(s, (struct sockaddr *)&addr, &addrlen);
+ close(s);
+ /*
+ * Connect to the socket
+ * XXX If any of these fail, we're in trouble!
+ */
+ s = qemu_socket(AF_INET, SOCK_STREAM, 0);
+ addr.sin_addr = loopback_addr;
+ do {
+ ret = connect(s, (struct sockaddr *)&addr, addrlen);
+ } while (ret < 0 && errno == EINTR);
+ }
+
+ dup2(s, 0);
+ dup2(s, 1);
+ dup2(s, 2);
+ for (s = getdtablesize() - 1; s >= 3; s--)
+ close(s);
+
+ i = 0;
+ bptr = qemu_strdup(ex); /* No need to free() this */
+ if (do_pty == 1) {
+ /* Setup "slirp.telnetd -x" */
+ argv[i++] = "slirp.telnetd";
+ argv[i++] = "-x";
+ argv[i++] = bptr;
+ } else
+ do {
+ /* Change the string into argv[] */
+ curarg = bptr;
+ while (*bptr != ' ' && *bptr != (char)0)
+ bptr++;
+ c = *bptr;
+ *bptr++ = (char)0;
+ argv[i++] = strdup(curarg);
+ } while (c);
+
+ argv[i] = NULL;
+ execvp(argv[0], (char **)argv);
+
+ /* Ooops, failed, let's tell the user why */
+ fprintf(stderr, "Error: execvp of %s failed: %s\n",
+ argv[0], strerror(errno));
+ close(0); close(1); close(2); /* XXX */
+ exit(1);
+
+ default:
+ if (do_pty == 2) {
+ close(s);
+ so->s = master;
+ } else {
+ /*
+ * XXX this could block us...
+ * XXX Should set a timer here, and if accept() doesn't
+ * return after X seconds, declare it a failure
+ * The only reason this will block forever is if socket()
+ * of connect() fail in the child process
+ */
+ do {
+ so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
+ } while (so->s < 0 && errno == EINTR);
+ closesocket(s);
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ }
+ fd_nonblock(so->s);
+
+ /* Append the telnet options now */
+ if (so->so_m != NULL && do_pty == 1) {
+ sbappend(so, so->so_m);
+ so->so_m = NULL;
+ }
+
+ return 1;
+ }
+}
+#endif
+
+#ifndef HAVE_STRDUP
+char *
+strdup(str)
+ const char *str;
+{
+ char *bptr;
+
+ bptr = (char *)malloc(strlen(str)+1);
+ strcpy(bptr, str);
+
+ return bptr;
+}
+#endif
+
+#include "monitor.h"
+
+void lprint(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ monitor_vprintf(default_mon, format, args);
+ va_end(args);
+}
+
+void
+u_sleep(int usec)
+{
+ struct timeval t;
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+
+ t.tv_sec = 0;
+ t.tv_usec = usec * 1000;
+
+ select(0, &fdset, &fdset, &fdset, &t);
+}
+
+/*
+ * Set fd blocking and non-blocking
+ */
+
+void
+fd_nonblock(int fd)
+{
+#ifdef FIONBIO
+#ifdef _WIN32
+ unsigned long opt = 1;
+#else
+ int opt = 1;
+#endif
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+void
+fd_block(int fd)
+{
+#ifdef FIONBIO
+#ifdef _WIN32
+ unsigned long opt = 0;
+#else
+ int opt = 0;
+#endif
+
+ ioctlsocket(fd, FIONBIO, &opt);
+#else
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt &= ~O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+#endif
+}
+
+void slirp_connection_info(Slirp *slirp, Monitor *mon)
+{
+ const char * const tcpstates[] = {
+ [TCPS_CLOSED] = "CLOSED",
+ [TCPS_LISTEN] = "LISTEN",
+ [TCPS_SYN_SENT] = "SYN_SENT",
+ [TCPS_SYN_RECEIVED] = "SYN_RCVD",
+ [TCPS_ESTABLISHED] = "ESTABLISHED",
+ [TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
+ [TCPS_FIN_WAIT_1] = "FIN_WAIT_1",
+ [TCPS_CLOSING] = "CLOSING",
+ [TCPS_LAST_ACK] = "LAST_ACK",
+ [TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
+ [TCPS_TIME_WAIT] = "TIME_WAIT",
+ };
+ struct in_addr dst_addr;
+ struct sockaddr_in src;
+ socklen_t src_len;
+ uint16_t dst_port;
+ struct socket *so;
+ const char *state;
+ char buf[20];
+ int n;
+
+ monitor_printf(mon, " Protocol[State] FD Source Address Port "
+ "Dest. Address Port RecvQ SendQ\n");
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
+ if (so->so_state & SS_HOSTFWD) {
+ state = "HOST_FORWARD";
+ } else if (so->so_tcpcb) {
+ state = tcpstates[so->so_tcpcb->t_state];
+ } else {
+ state = "NONE";
+ }
+ if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
+ src_len = sizeof(src);
+ getsockname(so->s, (struct sockaddr *)&src, &src_len);
+ dst_addr = so->so_laddr;
+ dst_port = so->so_lport;
+ } else {
+ src.sin_addr = so->so_laddr;
+ src.sin_port = so->so_lport;
+ dst_addr = so->so_faddr;
+ dst_port = so->so_fport;
+ }
+ n = snprintf(buf, sizeof(buf), " TCP[%s]", state);
+ memset(&buf[n], ' ', 19 - n);
+ buf[19] = 0;
+ monitor_printf(mon, "%s %3d %15s %5d ", buf, so->s,
+ src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
+ ntohs(src.sin_port));
+ monitor_printf(mon, "%15s %5d %5d %5d\n",
+ inet_ntoa(dst_addr), ntohs(dst_port),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+
+ for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
+ if (so->so_state & SS_HOSTFWD) {
+ n = snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]");
+ src_len = sizeof(src);
+ getsockname(so->s, (struct sockaddr *)&src, &src_len);
+ dst_addr = so->so_laddr;
+ dst_port = so->so_lport;
+ } else {
+ n = snprintf(buf, sizeof(buf), " UDP[%d sec]",
+ (so->so_expire - curtime) / 1000);
+ src.sin_addr = so->so_laddr;
+ src.sin_port = so->so_lport;
+ dst_addr = so->so_faddr;
+ dst_port = so->so_fport;
+ }
+ memset(&buf[n], ' ', 19 - n);
+ buf[19] = 0;
+ monitor_printf(mon, "%s %3d %15s %5d ", buf, so->s,
+ src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
+ ntohs(src.sin_port));
+ monitor_printf(mon, "%15s %5d %5d %5d\n",
+ inet_ntoa(dst_addr), ntohs(dst_port),
+ so->so_rcv.sb_cc, so->so_snd.sb_cc);
+ }
+}
diff --git a/slirp/misc.h b/slirp/misc.h
new file mode 100644
index 0000000..ed40a10
--- /dev/null
+++ b/slirp/misc.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+struct ex_list {
+ int ex_pty; /* Do we want a pty? */
+ struct in_addr ex_addr; /* Server address */
+ int ex_fport; /* Port to telnet to */
+ const char *ex_exec; /* Command line of what to exec */
+ struct ex_list *ex_next;
+};
+
+#ifndef HAVE_STRDUP
+char *strdup(const char *);
+#endif
+
+void do_wait(int);
+
+#define EMU_NONE 0x0
+
+/* TCP emulations */
+#define EMU_CTL 0x1
+#define EMU_FTP 0x2
+#define EMU_KSH 0x3
+#define EMU_IRC 0x4
+#define EMU_REALAUDIO 0x5
+#define EMU_RLOGIN 0x6
+#define EMU_IDENT 0x7
+#define EMU_RSH 0x8
+
+#define EMU_NOCONNECT 0x10 /* Don't connect */
+
+struct tos_t {
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
+};
+
+struct emu_t {
+ uint16_t lport;
+ uint16_t fport;
+ uint8_t tos;
+ uint8_t emu;
+ struct emu_t *next;
+};
+
+extern int x_port, x_server, x_display;
+
+int show_x(char *, struct socket *);
+void redir_x(uint32_t, int, int, int);
+void slirp_insque(void *, void *);
+void slirp_remque(void *);
+int add_exec(struct ex_list **, int, char *, struct in_addr, int);
+int slirp_openpty(int *, int *);
+int fork_exec(struct socket *so, const char *ex, int do_pty);
+void snooze_hup(int);
+void snooze(void);
+void relay(int);
+void add_emu(char *);
+void u_sleep(int);
+void fd_nonblock(int);
+void fd_block(int);
+int rsh_exec(struct socket *, struct socket *, char *, char *, char *);
+
+#endif
diff --git a/slirp/sbuf.c b/slirp/sbuf.c
new file mode 100644
index 0000000..5a1ccbf
--- /dev/null
+++ b/slirp/sbuf.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+static void sbappendsb(struct sbuf *sb, struct mbuf *m);
+
+void
+sbfree(struct sbuf *sb)
+{
+ free(sb->sb_data);
+}
+
+void
+sbdrop(struct sbuf *sb, int num)
+{
+ /*
+ * We can only drop how much we have
+ * This should never succeed
+ */
+ if(num > sb->sb_cc)
+ num = sb->sb_cc;
+ sb->sb_cc -= num;
+ sb->sb_rptr += num;
+ if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_rptr -= sb->sb_datalen;
+
+}
+
+void
+sbreserve(struct sbuf *sb, int size)
+{
+ if (sb->sb_data) {
+ /* Already alloced, realloc if necessary */
+ if (sb->sb_datalen != size) {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+ } else {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+}
+
+/*
+ * Try and write() to the socket, whatever doesn't get written
+ * append to the buffer... for a host with a fast net connection,
+ * this prevents an unnecessary copy of the data
+ * (the socket is non-blocking, so we won't hang)
+ */
+void
+sbappend(struct socket *so, struct mbuf *m)
+{
+ int ret = 0;
+
+ DEBUG_CALL("sbappend");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("m->m_len = %d", m->m_len);
+
+ /* Shouldn't happen, but... e.g. foreign host closes connection */
+ if (m->m_len <= 0) {
+ m_free(m);
+ return;
+ }
+
+ /*
+ * If there is urgent data, call sosendoob
+ * if not all was sent, sowrite will take care of the rest
+ * (The rest of this function is just an optimisation)
+ */
+ if (so->so_urgc) {
+ sbappendsb(&so->so_rcv, m);
+ m_free(m);
+ sosendoob(so);
+ return;
+ }
+
+ /*
+ * We only write if there's nothing in the buffer,
+ * ottherwise it'll arrive out of order, and hence corrupt
+ */
+ if (!so->so_rcv.sb_cc)
+ ret = slirp_send(so, m->m_data, m->m_len, 0);
+
+ if (ret <= 0) {
+ /*
+ * Nothing was written
+ * It's possible that the socket has closed, but
+ * we don't need to check because if it has closed,
+ * it will be detected in the normal way by soread()
+ */
+ sbappendsb(&so->so_rcv, m);
+ } else if (ret != m->m_len) {
+ /*
+ * Something was written, but not everything..
+ * sbappendsb the rest
+ */
+ m->m_len -= ret;
+ m->m_data += ret;
+ sbappendsb(&so->so_rcv, m);
+ } /* else */
+ /* Whatever happened, we free the mbuf */
+ m_free(m);
+}
+
+/*
+ * Copy the data from m into sb
+ * The caller is responsible to make sure there's enough room
+ */
+static void
+sbappendsb(struct sbuf *sb, struct mbuf *m)
+{
+ int len, n, nn;
+
+ len = m->m_len;
+
+ if (sb->sb_wptr < sb->sb_rptr) {
+ n = sb->sb_rptr - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ } else {
+ /* Do the right edge first */
+ n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
+ if (n > len) n = len;
+ memcpy(sb->sb_wptr, m->m_data, n);
+ len -= n;
+ if (len) {
+ /* Now the left edge */
+ nn = sb->sb_rptr - sb->sb_data;
+ if (nn > len) nn = len;
+ memcpy(sb->sb_data,m->m_data+n,nn);
+ n += nn;
+ }
+ }
+
+ sb->sb_cc += n;
+ sb->sb_wptr += n;
+ if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_wptr -= sb->sb_datalen;
+}
+
+/*
+ * Copy data from sbuf to a normal, straight buffer
+ * Don't update the sbuf rptr, this will be
+ * done in sbdrop when the data is acked
+ */
+void
+sbcopy(struct sbuf *sb, int off, int len, char *to)
+{
+ char *from;
+
+ from = sb->sb_rptr + off;
+ if (from >= sb->sb_data + sb->sb_datalen)
+ from -= sb->sb_datalen;
+
+ if (from < sb->sb_wptr) {
+ if (len > sb->sb_cc) len = sb->sb_cc;
+ memcpy(to,from,len);
+ } else {
+ /* re-use off */
+ off = (sb->sb_data + sb->sb_datalen) - from;
+ if (off > len) off = len;
+ memcpy(to,from,off);
+ len -= off;
+ if (len)
+ memcpy(to+off,sb->sb_data,len);
+ }
+}
diff --git a/slirp/sbuf.h b/slirp/sbuf.h
new file mode 100644
index 0000000..4f22e7c
--- /dev/null
+++ b/slirp/sbuf.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SBUF_H_
+#define _SBUF_H_
+
+#define sbflush(sb) sbdrop((sb),(sb)->sb_cc)
+#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc)
+
+struct sbuf {
+ u_int sb_cc; /* actual chars in buffer */
+ u_int sb_datalen; /* Length of data */
+ char *sb_wptr; /* write pointer. points to where the next
+ * bytes should be written in the sbuf */
+ char *sb_rptr; /* read pointer. points to where the next
+ * byte should be read from the sbuf */
+ char *sb_data; /* Actual data */
+};
+
+void sbfree(struct sbuf *);
+void sbdrop(struct sbuf *, int);
+void sbreserve(struct sbuf *, int);
+void sbappend(struct socket *, struct mbuf *);
+void sbcopy(struct sbuf *, int, int, char *);
+
+#endif
diff --git a/slirp/slirp.c b/slirp/slirp.c
new file mode 100644
index 0000000..332d83b
--- /dev/null
+++ b/slirp/slirp.c
@@ -0,0 +1,1113 @@
+/*
+ * libslirp glue
+ *
+ * Copyright (c) 2004-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "slirp.h"
+#include "hw/hw.h"
+
+/* host loopback address */
+struct in_addr loopback_addr;
+
+/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
+static const uint8_t special_ethaddr[6] = {
+ 0x52, 0x55, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 };
+
+/* XXX: suppress those select globals */
+fd_set *global_readfds, *global_writefds, *global_xfds;
+
+u_int curtime;
+static u_int time_fasttimo, last_slowtimo;
+static int do_slowtimo;
+
+static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
+ QTAILQ_HEAD_INITIALIZER(slirp_instances);
+
+static struct in_addr dns_addr;
+static u_int dns_addr_time;
+
+#ifdef _WIN32
+
+int get_dns_addr(struct in_addr *pdns_addr)
+{
+ FIXED_INFO *FixedInfo=NULL;
+ ULONG BufLen;
+ DWORD ret;
+ IP_ADDR_STRING *pIPAddr;
+ struct in_addr tmp_addr;
+
+ if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) {
+ *pdns_addr = dns_addr;
+ return 0;
+ }
+
+ FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
+ BufLen = sizeof(FIXED_INFO);
+
+ if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ FixedInfo = GlobalAlloc(GPTR, BufLen);
+ }
+
+ if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
+ printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return -1;
+ }
+
+ pIPAddr = &(FixedInfo->DnsServerList);
+ inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
+ *pdns_addr = tmp_addr;
+ dns_addr = tmp_addr;
+ dns_addr_time = curtime;
+ if (FixedInfo) {
+ GlobalFree(FixedInfo);
+ FixedInfo = NULL;
+ }
+ return 0;
+}
+
+static void winsock_cleanup(void)
+{
+ WSACleanup();
+}
+
+#else
+
+static struct stat dns_addr_stat;
+
+int get_dns_addr(struct in_addr *pdns_addr)
+{
+ char buff[512];
+ char buff2[257];
+ FILE *f;
+ int found = 0;
+ struct in_addr tmp_addr;
+
+ if (dns_addr.s_addr != 0) {
+ struct stat old_stat;
+ if ((curtime - dns_addr_time) < 1000) {
+ *pdns_addr = dns_addr;
+ return 0;
+ }
+ old_stat = dns_addr_stat;
+ if (stat("/etc/resolv.conf", &dns_addr_stat) != 0)
+ return -1;
+ if ((dns_addr_stat.st_dev == old_stat.st_dev)
+ && (dns_addr_stat.st_ino == old_stat.st_ino)
+ && (dns_addr_stat.st_size == old_stat.st_size)
+ && (dns_addr_stat.st_mtime == old_stat.st_mtime)) {
+ *pdns_addr = dns_addr;
+ return 0;
+ }
+ }
+
+ f = fopen("/etc/resolv.conf", "r");
+ if (!f)
+ return -1;
+
+#ifdef DEBUG
+ lprint("IP address of your DNS(s): ");
+#endif
+ while (fgets(buff, 512, f) != NULL) {
+ if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
+ if (!inet_aton(buff2, &tmp_addr))
+ continue;
+ /* If it's the first one, set it to dns_addr */
+ if (!found) {
+ *pdns_addr = tmp_addr;
+ dns_addr = tmp_addr;
+ dns_addr_time = curtime;
+ }
+#ifdef DEBUG
+ else
+ lprint(", ");
+#endif
+ if (++found > 3) {
+#ifdef DEBUG
+ lprint("(more)");
+#endif
+ break;
+ }
+#ifdef DEBUG
+ else
+ lprint("%s", inet_ntoa(tmp_addr));
+#endif
+ }
+ }
+ fclose(f);
+ if (!found)
+ return -1;
+ return 0;
+}
+
+#endif
+
+static void slirp_init_once(void)
+{
+ static int initialized;
+#ifdef _WIN32
+ WSADATA Data;
+#endif
+
+ if (initialized) {
+ return;
+ }
+ initialized = 1;
+
+#ifdef _WIN32
+ WSAStartup(MAKEWORD(2,0), &Data);
+ atexit(winsock_cleanup);
+#endif
+
+ loopback_addr.s_addr = htonl(INADDR_LOOPBACK);
+}
+
+static void slirp_state_save(QEMUFile *f, void *opaque);
+static int slirp_state_load(QEMUFile *f, void *opaque, int version_id);
+
+Slirp *slirp_init(int restricted, struct in_addr vnetwork,
+ struct in_addr vnetmask, struct in_addr vhost,
+ const char *vhostname, const char *tftp_path,
+ const char *bootfile, struct in_addr vdhcp_start,
+ struct in_addr vnameserver, void *opaque)
+{
+ Slirp *slirp = qemu_mallocz(sizeof(Slirp));
+
+ slirp_init_once();
+
+ slirp->restricted = restricted;
+
+ if_init(slirp);
+ ip_init(slirp);
+
+ /* Initialise mbufs *after* setting the MTU */
+ m_init(slirp);
+
+ slirp->vnetwork_addr = vnetwork;
+ slirp->vnetwork_mask = vnetmask;
+ slirp->vhost_addr = vhost;
+ if (vhostname) {
+ pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
+ vhostname);
+ }
+ if (tftp_path) {
+ slirp->tftp_prefix = qemu_strdup(tftp_path);
+ }
+ if (bootfile) {
+ slirp->bootp_filename = qemu_strdup(bootfile);
+ }
+ slirp->vdhcp_startaddr = vdhcp_start;
+ slirp->vnameserver_addr = vnameserver;
+
+ slirp->opaque = opaque;
+
+ register_savevm(NULL, "slirp", 0, 3,
+ slirp_state_save, slirp_state_load, slirp);
+
+ QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry);
+
+ return slirp;
+}
+
+void slirp_cleanup(Slirp *slirp)
+{
+ QTAILQ_REMOVE(&slirp_instances, slirp, entry);
+
+ unregister_savevm(NULL, "slirp", slirp);
+
+ qemu_free(slirp->tftp_prefix);
+ qemu_free(slirp->bootp_filename);
+ qemu_free(slirp);
+}
+
+#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
+
+void slirp_select_fill(int *pnfds,
+ fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+ Slirp *slirp;
+ struct socket *so, *so_next;
+ int nfds;
+
+ if (QTAILQ_EMPTY(&slirp_instances)) {
+ return;
+ }
+
+ /* fail safe */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+
+ nfds = *pnfds;
+ /*
+ * First, TCP sockets
+ */
+ do_slowtimo = 0;
+
+ QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
+ /*
+ * *_slowtimo needs calling if there are IP fragments
+ * in the fragment queue, or there are TCP connections active
+ */
+ do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
+ (&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if we need a tcp_fasttimo
+ */
+ if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
+ time_fasttimo = curtime; /* Flag when we want a fasttimo */
+
+ /*
+ * NOFDREF can include still connecting to local-host,
+ * newly socreated() sockets etc. Don't want to select these.
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Set for reading sockets which are accepting
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing sockets which are connecting
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ continue;
+ }
+
+ /*
+ * Set for writing if we are connected, can send more, and
+ * we have something to send
+ */
+ if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
+ FD_SET(so->s, writefds);
+ UPD_NFDS(so->s);
+ }
+
+ /*
+ * Set for reading (and urgent data) if we are connected, can
+ * receive more, and we have room for it XXX /2 ?
+ */
+ if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
+ FD_SET(so->s, readfds);
+ FD_SET(so->s, xfds);
+ UPD_NFDS(so->s);
+ }
+ }
+
+ /*
+ * UDP sockets
+ */
+ for (so = slirp->udb.so_next; so != &slirp->udb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ udp_detach(so);
+ continue;
+ } else
+ do_slowtimo = 1; /* Let socket expire */
+ }
+
+ /*
+ * When UDP packets are received from over the
+ * link, they're sendto()'d straight away, so
+ * no need for setting for writing
+ * Limit the number of packets queued by this session
+ * to 4. Note that even though we try and limit this
+ * to 4 packets, the session could have more queued
+ * if the packets needed to be fragmented
+ * (XXX <= 4 ?)
+ */
+ if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
+ FD_SET(so->s, readfds);
+ UPD_NFDS(so->s);
+ }
+ }
+ }
+
+ *pnfds = nfds;
+}
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
+ int select_error)
+{
+ Slirp *slirp;
+ struct socket *so, *so_next;
+ int ret;
+
+ if (QTAILQ_EMPTY(&slirp_instances)) {
+ return;
+ }
+
+ global_readfds = readfds;
+ global_writefds = writefds;
+ global_xfds = xfds;
+
+ curtime = qemu_get_clock(rt_clock);
+
+ QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
+ /*
+ * See if anything has timed out
+ */
+ if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+ tcp_fasttimo(slirp);
+ time_fasttimo = 0;
+ }
+ if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+ ip_slowtimo(slirp);
+ tcp_slowtimo(slirp);
+ last_slowtimo = curtime;
+ }
+
+ /*
+ * Check sockets
+ */
+ if (!select_error) {
+ /*
+ * Check TCP sockets
+ */
+ for (so = slirp->tcb.so_next; so != &slirp->tcb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ /*
+ * FD_ISSET is meaningless on these sockets
+ * (and they can crash the program)
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ continue;
+
+ /*
+ * Check for URG data
+ * This will soread as well, so no need to
+ * test for readfds below if this succeeds
+ */
+ if (FD_ISSET(so->s, xfds))
+ sorecvoob(so);
+ /*
+ * Check sockets for reading
+ */
+ else if (FD_ISSET(so->s, readfds)) {
+ /*
+ * Check for incoming connections
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ tcp_connect(so);
+ continue;
+ } /* else */
+ ret = soread(so);
+
+ /* Output it if we read something */
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+ }
+
+ /*
+ * Check sockets for writing
+ */
+ if (FD_ISSET(so->s, writefds)) {
+ /*
+ * Check for non-blocking, still-connecting sockets
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ /* Connected */
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ ret = send(so->s, (const void *) &ret, 0, 0);
+ if (ret < 0) {
+ /* XXXXX Must fix, zero bytes is a NOP */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+ }
+ /* else so->so_state &= ~SS_ISFCONNECTING; */
+
+ /*
+ * Continue tcp_input
+ */
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ /* continue; */
+ } else
+ ret = sowrite(so);
+ /*
+ * XXXXX If we wrote something (a lot), there
+ * could be a need for a window update.
+ * In the worst case, the remote will send
+ * a window probe to get things going again
+ */
+ }
+
+ /*
+ * Probe a still-connecting, non-blocking socket
+ * to check if it's still alive
+ */
+#ifdef PROBE_CONN
+ if (so->so_state & SS_ISFCONNECTING) {
+ ret = recv(so->s, (char *)&ret, 0,0);
+
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue; /* Still connecting, continue */
+
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+
+ /* tcp_input will take care of it */
+ } else {
+ ret = send(so->s, &ret, 0,0);
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN)
+ continue;
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+ } else
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ }
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
+ } /* SS_ISFCONNECTING */
+#endif
+ }
+
+ /*
+ * Now UDP sockets.
+ * Incoming packets are sent straight away, they're not buffered.
+ * Incoming UDP data isn't buffered either.
+ */
+ for (so = slirp->udb.so_next; so != &slirp->udb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+ sorecvfrom(so);
+ }
+ }
+ }
+
+ /*
+ * See if we can start outputting
+ */
+ if (slirp->if_queued) {
+ if_start(slirp);
+ }
+ }
+
+ /* clear global file descriptor sets.
+ * these reside on the stack in vl.c
+ * so they're unusable if we're not in
+ * slirp_select_fill or slirp_select_poll.
+ */
+ global_readfds = NULL;
+ global_writefds = NULL;
+ global_xfds = NULL;
+}
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+
+struct ethhdr
+{
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ uint32_t ar_sip; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ uint32_t ar_tip ; /* target IP address */
+} __attribute__((packed));
+
+static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
+{
+ struct ethhdr *eh = (struct ethhdr *)pkt;
+ struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
+ uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)];
+ struct ethhdr *reh = (struct ethhdr *)arp_reply;
+ struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
+ int ar_op;
+ struct ex_list *ex_ptr;
+
+ ar_op = ntohs(ah->ar_op);
+ switch(ar_op) {
+ case ARPOP_REQUEST:
+ if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ if (ah->ar_tip == slirp->vnameserver_addr.s_addr ||
+ ah->ar_tip == slirp->vhost_addr.s_addr)
+ goto arp_ok;
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_addr.s_addr == ah->ar_tip)
+ goto arp_ok;
+ }
+ return;
+ arp_ok:
+ memset(arp_reply, 0, sizeof(arp_reply));
+ /* XXX: make an ARP request to have the client address */
+ memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN);
+
+ /* ARP request for alias/dns mac address */
+ memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&reh->h_source[2], &ah->ar_tip, 4);
+ reh->h_proto = htons(ETH_P_ARP);
+
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REPLY);
+ memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
+ rah->ar_sip = ah->ar_tip;
+ memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
+ rah->ar_tip = ah->ar_sip;
+ slirp_output(slirp->opaque, arp_reply, sizeof(arp_reply));
+ }
+ break;
+ case ARPOP_REPLY:
+ /* reply to request of client mac address ? */
+ if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN) &&
+ ah->ar_sip == slirp->client_ipaddr.s_addr) {
+ memcpy(slirp->client_ethaddr, ah->ar_sha, ETH_ALEN);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
+{
+ struct mbuf *m;
+ int proto;
+
+ if (pkt_len < ETH_HLEN)
+ return;
+
+ proto = ntohs(*(uint16_t *)(pkt + 12));
+ switch(proto) {
+ case ETH_P_ARP:
+ arp_input(slirp, pkt, pkt_len);
+ break;
+ case ETH_P_IP:
+ m = m_get(slirp);
+ if (!m)
+ return;
+ /* Note: we add to align the IP header */
+ if (M_FREEROOM(m) < pkt_len + 2) {
+ m_inc(m, pkt_len + 2);
+ }
+ m->m_len = pkt_len + 2;
+ memcpy(m->m_data + 2, pkt, pkt_len);
+
+ m->m_data += 2 + ETH_HLEN;
+ m->m_len -= 2 + ETH_HLEN;
+
+ ip_input(m);
+ break;
+ default:
+ break;
+ }
+}
+
+/* output the IP packet to the ethernet device */
+void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len)
+{
+ uint8_t buf[1600];
+ struct ethhdr *eh = (struct ethhdr *)buf;
+
+ if (ip_data_len + ETH_HLEN > sizeof(buf))
+ return;
+
+ if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN)) {
+ uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
+ struct ethhdr *reh = (struct ethhdr *)arp_req;
+ struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
+ const struct ip *iph = (const struct ip *)ip_data;
+
+ /* If the client addr is not known, there is no point in
+ sending the packet to it. Normally the sender should have
+ done an ARP request to get its MAC address. Here we do it
+ in place of sending the packet and we hope that the sender
+ will retry sending its packet. */
+ memset(reh->h_dest, 0xff, ETH_ALEN);
+ memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&reh->h_source[2], &slirp->vhost_addr, 4);
+ reh->h_proto = htons(ETH_P_ARP);
+ rah->ar_hrd = htons(1);
+ rah->ar_pro = htons(ETH_P_IP);
+ rah->ar_hln = ETH_ALEN;
+ rah->ar_pln = 4;
+ rah->ar_op = htons(ARPOP_REQUEST);
+ /* source hw addr */
+ memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4);
+ memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4);
+ /* source IP */
+ rah->ar_sip = slirp->vhost_addr.s_addr;
+ /* target hw addr (none) */
+ memset(rah->ar_tha, 0, ETH_ALEN);
+ /* target IP */
+ rah->ar_tip = iph->ip_dst.s_addr;
+ slirp->client_ipaddr = iph->ip_dst;
+ slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
+ } else {
+ memcpy(eh->h_dest, slirp->client_ethaddr, ETH_ALEN);
+ memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);
+ /* XXX: not correct */
+ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);
+ eh->h_proto = htons(ETH_P_IP);
+ memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
+ slirp_output(slirp->opaque, buf, ip_data_len + ETH_HLEN);
+ }
+}
+
+/* Drop host forwarding rule, return 0 if found. */
+int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
+ int host_port)
+{
+ struct socket *so;
+ struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb);
+ struct sockaddr_in addr;
+ int port = htons(host_port);
+ socklen_t addr_len;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ addr_len = sizeof(addr);
+ if ((so->so_state & SS_HOSTFWD) &&
+ getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
+ addr.sin_addr.s_addr == host_addr.s_addr &&
+ addr.sin_port == port) {
+ close(so->s);
+ sofree(so);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
+ int host_port, struct in_addr guest_addr, int guest_port)
+{
+ if (!guest_addr.s_addr) {
+ guest_addr = slirp->vdhcp_startaddr;
+ }
+ if (is_udp) {
+ if (!udp_listen(slirp, host_addr.s_addr, htons(host_port),
+ guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))
+ return -1;
+ } else {
+ if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port),
+ guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))
+ return -1;
+ }
+ return 0;
+}
+
+int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
+ struct in_addr *guest_addr, int guest_port)
+{
+ if (!guest_addr->s_addr) {
+ guest_addr->s_addr = slirp->vnetwork_addr.s_addr |
+ (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr);
+ }
+ if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) !=
+ slirp->vnetwork_addr.s_addr ||
+ guest_addr->s_addr == slirp->vhost_addr.s_addr ||
+ guest_addr->s_addr == slirp->vnameserver_addr.s_addr) {
+ return -1;
+ }
+ return add_exec(&slirp->exec_list, do_pty, (char *)args, *guest_addr,
+ htons(guest_port));
+}
+
+ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
+{
+ if (so->s == -1 && so->extra) {
+ qemu_chr_write(so->extra, buf, len);
+ return len;
+ }
+
+ return send(so->s, buf, len, flags);
+}
+
+static struct socket *
+slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port)
+{
+ struct socket *so;
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
+ if (so->so_faddr.s_addr == guest_addr.s_addr &&
+ htons(so->so_fport) == guest_port) {
+ return so;
+ }
+ }
+ return NULL;
+}
+
+size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
+ int guest_port)
+{
+ struct iovec iov[2];
+ struct socket *so;
+
+ so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
+
+ if (!so || so->so_state & SS_NOFDREF)
+ return 0;
+
+ if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
+ return 0;
+
+ return sopreprbuf(so, iov, NULL);
+}
+
+void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
+ const uint8_t *buf, int size)
+{
+ int ret;
+ struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
+
+ if (!so)
+ return;
+
+ ret = soreadbuf(so, (const char *)buf, size);
+
+ if (ret > 0)
+ tcp_output(sototcpcb(so));
+}
+
+static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp)
+{
+ int i;
+
+ qemu_put_sbe16(f, tp->t_state);
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ qemu_put_sbe16(f, tp->t_timer[i]);
+ qemu_put_sbe16(f, tp->t_rxtshift);
+ qemu_put_sbe16(f, tp->t_rxtcur);
+ qemu_put_sbe16(f, tp->t_dupacks);
+ qemu_put_be16(f, tp->t_maxseg);
+ qemu_put_sbyte(f, tp->t_force);
+ qemu_put_be16(f, tp->t_flags);
+ qemu_put_be32(f, tp->snd_una);
+ qemu_put_be32(f, tp->snd_nxt);
+ qemu_put_be32(f, tp->snd_up);
+ qemu_put_be32(f, tp->snd_wl1);
+ qemu_put_be32(f, tp->snd_wl2);
+ qemu_put_be32(f, tp->iss);
+ qemu_put_be32(f, tp->snd_wnd);
+ qemu_put_be32(f, tp->rcv_wnd);
+ qemu_put_be32(f, tp->rcv_nxt);
+ qemu_put_be32(f, tp->rcv_up);
+ qemu_put_be32(f, tp->irs);
+ qemu_put_be32(f, tp->rcv_adv);
+ qemu_put_be32(f, tp->snd_max);
+ qemu_put_be32(f, tp->snd_cwnd);
+ qemu_put_be32(f, tp->snd_ssthresh);
+ qemu_put_sbe16(f, tp->t_idle);
+ qemu_put_sbe16(f, tp->t_rtt);
+ qemu_put_be32(f, tp->t_rtseq);
+ qemu_put_sbe16(f, tp->t_srtt);
+ qemu_put_sbe16(f, tp->t_rttvar);
+ qemu_put_be16(f, tp->t_rttmin);
+ qemu_put_be32(f, tp->max_sndwnd);
+ qemu_put_byte(f, tp->t_oobflags);
+ qemu_put_byte(f, tp->t_iobc);
+ qemu_put_sbe16(f, tp->t_softerror);
+ qemu_put_byte(f, tp->snd_scale);
+ qemu_put_byte(f, tp->rcv_scale);
+ qemu_put_byte(f, tp->request_r_scale);
+ qemu_put_byte(f, tp->requested_s_scale);
+ qemu_put_be32(f, tp->ts_recent);
+ qemu_put_be32(f, tp->ts_recent_age);
+ qemu_put_be32(f, tp->last_ack_sent);
+}
+
+static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
+{
+ uint32_t off;
+
+ qemu_put_be32(f, sbuf->sb_cc);
+ qemu_put_be32(f, sbuf->sb_datalen);
+ off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data);
+ qemu_put_sbe32(f, off);
+ off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data);
+ qemu_put_sbe32(f, off);
+ qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
+}
+
+static void slirp_socket_save(QEMUFile *f, struct socket *so)
+{
+ qemu_put_be32(f, so->so_urgc);
+ qemu_put_be32(f, so->so_faddr.s_addr);
+ qemu_put_be32(f, so->so_laddr.s_addr);
+ qemu_put_be16(f, so->so_fport);
+ qemu_put_be16(f, so->so_lport);
+ qemu_put_byte(f, so->so_iptos);
+ qemu_put_byte(f, so->so_emu);
+ qemu_put_byte(f, so->so_type);
+ qemu_put_be32(f, so->so_state);
+ slirp_sbuf_save(f, &so->so_rcv);
+ slirp_sbuf_save(f, &so->so_snd);
+ slirp_tcp_save(f, so->so_tcpcb);
+}
+
+static void slirp_bootp_save(QEMUFile *f, Slirp *slirp)
+{
+ int i;
+
+ for (i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ qemu_put_be16(f, slirp->bootp_clients[i].allocated);
+ qemu_put_buffer(f, slirp->bootp_clients[i].macaddr, 6);
+ }
+}
+
+static void slirp_state_save(QEMUFile *f, void *opaque)
+{
+ Slirp *slirp = opaque;
+ struct ex_list *ex_ptr;
+
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
+ if (ex_ptr->ex_pty == 3) {
+ struct socket *so;
+ so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr,
+ ntohs(ex_ptr->ex_fport));
+ if (!so)
+ continue;
+
+ qemu_put_byte(f, 42);
+ slirp_socket_save(f, so);
+ }
+ qemu_put_byte(f, 0);
+
+ qemu_put_be16(f, slirp->ip_id);
+
+ slirp_bootp_save(f, slirp);
+}
+
+static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp)
+{
+ int i;
+
+ tp->t_state = qemu_get_sbe16(f);
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = qemu_get_sbe16(f);
+ tp->t_rxtshift = qemu_get_sbe16(f);
+ tp->t_rxtcur = qemu_get_sbe16(f);
+ tp->t_dupacks = qemu_get_sbe16(f);
+ tp->t_maxseg = qemu_get_be16(f);
+ tp->t_force = qemu_get_sbyte(f);
+ tp->t_flags = qemu_get_be16(f);
+ tp->snd_una = qemu_get_be32(f);
+ tp->snd_nxt = qemu_get_be32(f);
+ tp->snd_up = qemu_get_be32(f);
+ tp->snd_wl1 = qemu_get_be32(f);
+ tp->snd_wl2 = qemu_get_be32(f);
+ tp->iss = qemu_get_be32(f);
+ tp->snd_wnd = qemu_get_be32(f);
+ tp->rcv_wnd = qemu_get_be32(f);
+ tp->rcv_nxt = qemu_get_be32(f);
+ tp->rcv_up = qemu_get_be32(f);
+ tp->irs = qemu_get_be32(f);
+ tp->rcv_adv = qemu_get_be32(f);
+ tp->snd_max = qemu_get_be32(f);
+ tp->snd_cwnd = qemu_get_be32(f);
+ tp->snd_ssthresh = qemu_get_be32(f);
+ tp->t_idle = qemu_get_sbe16(f);
+ tp->t_rtt = qemu_get_sbe16(f);
+ tp->t_rtseq = qemu_get_be32(f);
+ tp->t_srtt = qemu_get_sbe16(f);
+ tp->t_rttvar = qemu_get_sbe16(f);
+ tp->t_rttmin = qemu_get_be16(f);
+ tp->max_sndwnd = qemu_get_be32(f);
+ tp->t_oobflags = qemu_get_byte(f);
+ tp->t_iobc = qemu_get_byte(f);
+ tp->t_softerror = qemu_get_sbe16(f);
+ tp->snd_scale = qemu_get_byte(f);
+ tp->rcv_scale = qemu_get_byte(f);
+ tp->request_r_scale = qemu_get_byte(f);
+ tp->requested_s_scale = qemu_get_byte(f);
+ tp->ts_recent = qemu_get_be32(f);
+ tp->ts_recent_age = qemu_get_be32(f);
+ tp->last_ack_sent = qemu_get_be32(f);
+ tcp_template(tp);
+}
+
+static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf)
+{
+ uint32_t off, sb_cc, sb_datalen;
+
+ sb_cc = qemu_get_be32(f);
+ sb_datalen = qemu_get_be32(f);
+
+ sbreserve(sbuf, sb_datalen);
+
+ if (sbuf->sb_datalen != sb_datalen)
+ return -ENOMEM;
+
+ sbuf->sb_cc = sb_cc;
+
+ off = qemu_get_sbe32(f);
+ sbuf->sb_wptr = sbuf->sb_data + off;
+ off = qemu_get_sbe32(f);
+ sbuf->sb_rptr = sbuf->sb_data + off;
+ qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen);
+
+ return 0;
+}
+
+static int slirp_socket_load(QEMUFile *f, struct socket *so)
+{
+ if (tcp_attach(so) < 0)
+ return -ENOMEM;
+
+ so->so_urgc = qemu_get_be32(f);
+ so->so_faddr.s_addr = qemu_get_be32(f);
+ so->so_laddr.s_addr = qemu_get_be32(f);
+ so->so_fport = qemu_get_be16(f);
+ so->so_lport = qemu_get_be16(f);
+ so->so_iptos = qemu_get_byte(f);
+ so->so_emu = qemu_get_byte(f);
+ so->so_type = qemu_get_byte(f);
+ so->so_state = qemu_get_be32(f);
+ if (slirp_sbuf_load(f, &so->so_rcv) < 0)
+ return -ENOMEM;
+ if (slirp_sbuf_load(f, &so->so_snd) < 0)
+ return -ENOMEM;
+ slirp_tcp_load(f, so->so_tcpcb);
+
+ return 0;
+}
+
+static void slirp_bootp_load(QEMUFile *f, Slirp *slirp)
+{
+ int i;
+
+ for (i = 0; i < NB_BOOTP_CLIENTS; i++) {
+ slirp->bootp_clients[i].allocated = qemu_get_be16(f);
+ qemu_get_buffer(f, slirp->bootp_clients[i].macaddr, 6);
+ }
+}
+
+static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
+{
+ Slirp *slirp = opaque;
+ struct ex_list *ex_ptr;
+
+ while (qemu_get_byte(f)) {
+ int ret;
+ struct socket *so = socreate(slirp);
+
+ if (!so)
+ return -ENOMEM;
+
+ ret = slirp_socket_load(f, so);
+
+ if (ret < 0)
+ return ret;
+
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) !=
+ slirp->vnetwork_addr.s_addr) {
+ return -EINVAL;
+ }
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_pty == 3 &&
+ so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr &&
+ so->so_fport == ex_ptr->ex_fport) {
+ break;
+ }
+ }
+ if (!ex_ptr)
+ return -EINVAL;
+
+ so->extra = (void *)ex_ptr->ex_exec;
+ }
+
+ if (version_id >= 2) {
+ slirp->ip_id = qemu_get_be16(f);
+ }
+
+ if (version_id >= 3) {
+ slirp_bootp_load(f, slirp);
+ }
+
+ return 0;
+}
diff --git a/slirp/slirp.h b/slirp/slirp.h
new file mode 100644
index 0000000..954289a
--- /dev/null
+++ b/slirp/slirp.h
@@ -0,0 +1,320 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include "config-host.h"
+#include "slirp_config.h"
+
+#ifdef _WIN32
+# include <inttypes.h>
+
+typedef char *caddr_t;
+
+# include <windows.h>
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <sys/timeb.h>
+# include <iphlpapi.h>
+
+# define EWOULDBLOCK WSAEWOULDBLOCK
+# define EINPROGRESS WSAEINPROGRESS
+# define ENOTCONN WSAENOTCONN
+# define EHOSTUNREACH WSAEHOSTUNREACH
+# define ENETUNREACH WSAENETUNREACH
+# define ECONNREFUSED WSAECONNREFUSED
+#else
+# define ioctlsocket ioctl
+# define closesocket(s) close(s)
+# if !defined(__HAIKU__)
+# define O_BINARY 0
+# endif
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+#include <sys/time.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_MEMMOVE
+#define memmove(x, y, z) bcopy(y, x, z)
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/uio.h>
+#endif
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+/* Systems lacking strdup() definition in <string.h>. */
+#if defined(ultrix)
+char *strdup(const char *);
+#endif
+
+/* Systems lacking malloc() definition in <stdlib.h>. */
+#if defined(ultrix) || defined(hcx)
+void *malloc(size_t arg);
+void free(void *ptr);
+#endif
+
+#ifndef HAVE_INET_ATON
+int inet_aton(const char *cp, struct in_addr *ia);
+#endif
+
+#include <fcntl.h>
+#ifndef NO_UNIX_SOCKETS
+#include <sys/un.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H)
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#ifdef USE_PPP
+#include <ppp/slirppp.h>
+#endif
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include <sys/stat.h>
+
+/* Avoid conflicting with the libc insque() and remque(), which
+ have different prototypes. */
+#define insque slirp_insque
+#define remque slirp_remque
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#endif
+
+#include "debug.h"
+
+#include "qemu-queue.h"
+
+#include "libslirp.h"
+#include "ip.h"
+#include "tcp.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "udp.h"
+#include "mbuf.h"
+#include "sbuf.h"
+#include "socket.h"
+#include "if.h"
+#include "main.h"
+#include "misc.h"
+#ifdef USE_PPP
+#include "ppp/pppd.h"
+#include "ppp/ppp.h"
+#endif
+
+#include "bootp.h"
+#include "tftp.h"
+
+/* osdep.c */
+int qemu_socket(int domain, int type, int protocol);
+
+
+struct Slirp {
+ QTAILQ_ENTRY(Slirp) entry;
+
+ /* virtual network configuration */
+ struct in_addr vnetwork_addr;
+ struct in_addr vnetwork_mask;
+ struct in_addr vhost_addr;
+ struct in_addr vdhcp_startaddr;
+ struct in_addr vnameserver_addr;
+
+ /* ARP cache for the guest IP addresses (XXX: allow many entries) */
+ uint8_t client_ethaddr[6];
+
+ struct in_addr client_ipaddr;
+ char client_hostname[33];
+
+ int restricted;
+ struct timeval tt;
+ struct ex_list *exec_list;
+
+ /* mbuf states */
+ struct mbuf m_freelist, m_usedlist;
+ int mbuf_alloced;
+
+ /* if states */
+ int if_queued; /* number of packets queued so far */
+ struct mbuf if_fastq; /* fast queue (for interactive data) */
+ struct mbuf if_batchq; /* queue for non-interactive data */
+ struct mbuf *next_m; /* pointer to next mbuf to output */
+
+ /* ip states */
+ struct ipq ipq; /* ip reass. queue */
+ uint16_t ip_id; /* ip packet ctr, for ids */
+
+ /* bootp/dhcp states */
+ BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
+ char *bootp_filename;
+
+ /* tcp states */
+ struct socket tcb;
+ struct socket *tcp_last_so;
+ tcp_seq tcp_iss; /* tcp initial send seq # */
+ uint32_t tcp_now; /* for RFC 1323 timestamps */
+
+ /* udp states */
+ struct socket udb;
+ struct socket *udp_last_so;
+
+ /* tftp states */
+ char *tftp_prefix;
+ struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+
+ void *opaque;
+};
+
+extern Slirp *slirp_instance;
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef FULL_BOLT
+void if_start(Slirp *);
+#else
+void if_start(struct ttys *);
+#endif
+
+#ifndef HAVE_STRERROR
+ char *strerror(int error);
+#endif
+
+#ifndef HAVE_INDEX
+ char *index(const char *, int);
+#endif
+
+#ifndef HAVE_GETHOSTID
+ long gethostid(void);
+#endif
+
+void lprint(const char *, ...) GCC_FMT_ATTR(1, 2);
+
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+
+#define DEFAULT_BAUD 115200
+
+#define SO_OPTIONS DO_KEEPALIVE
+#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL)
+
+/* cksum.c */
+int cksum(struct mbuf *m, int len);
+
+/* if.c */
+void if_init(Slirp *);
+void if_output(struct socket *, struct mbuf *);
+
+/* ip_input.c */
+void ip_init(Slirp *);
+void ip_input(struct mbuf *);
+void ip_slowtimo(Slirp *);
+void ip_stripoptions(register struct mbuf *, struct mbuf *);
+
+/* ip_output.c */
+int ip_output(struct socket *, struct mbuf *);
+
+/* tcp_input.c */
+void tcp_input(register struct mbuf *, int, struct socket *);
+int tcp_mss(register struct tcpcb *, u_int);
+
+/* tcp_output.c */
+int tcp_output(register struct tcpcb *);
+void tcp_setpersist(register struct tcpcb *);
+
+/* tcp_subr.c */
+void tcp_init(Slirp *);
+void tcp_template(struct tcpcb *);
+void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
+struct tcpcb * tcp_newtcpcb(struct socket *);
+struct tcpcb * tcp_close(register struct tcpcb *);
+void tcp_sockclosed(struct tcpcb *);
+int tcp_fconnect(struct socket *);
+void tcp_connect(struct socket *);
+int tcp_attach(struct socket *);
+uint8_t tcp_tos(struct socket *);
+int tcp_emu(struct socket *, struct mbuf *);
+int tcp_ctl(struct socket *);
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
+
+#ifdef USE_PPP
+#define MIN_MRU MINMRU
+#define MAX_MRU MAXMRU
+#else
+#define MIN_MRU 128
+#define MAX_MRU 16384
+#endif
+
+#ifndef _WIN32
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifdef _WIN32
+#undef errno
+#define errno (WSAGetLastError())
+#endif
+
+#endif
diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h
new file mode 100644
index 0000000..18db45c
--- /dev/null
+++ b/slirp/slirp_config.h
@@ -0,0 +1,188 @@
+/*
+ * User definable configuration options
+ */
+
+/* Define if you want the connection to be probed */
+/* XXX Not working yet, so ignore this for now */
+#undef PROBE_CONN
+
+/* Define to 1 if you want KEEPALIVE timers */
+#define DO_KEEPALIVE 0
+
+/* Define to MAX interfaces you expect to use at once */
+/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
+/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
+#define MAX_INTERFACES 1
+#define MAX_PPP_INTERFACES 1
+
+/* Define if you want slirp's socket in /tmp */
+/* XXXXXX Do this in ./configure */
+#undef USE_TMPSOCKET
+
+/* Define if you want slirp to use cfsetXspeed() on the terminal */
+#undef DO_CFSETSPEED
+
+/* Define this if you want slirp to write to the tty as fast as it can */
+/* This should only be set if you are using load-balancing, slirp does a */
+/* pretty good job on single modems already, and seting this will make */
+/* interactive sessions less responsive */
+/* XXXXX Talk about having fast modem as unit 0 */
+#undef FULL_BOLT
+
+/*
+ * Define if you want slirp to use less CPU
+ * You will notice a small lag in interactive sessions, but it's not that bad
+ * Things like Netscape/ftp/etc. are completely unaffected
+ * This is mainly for sysadmins who have many slirp users
+ */
+#undef USE_LOWCPU
+
+/* Define this if your compiler doesn't like prototypes */
+#ifndef __STDC__
+#define NO_PROTOTYPES
+#endif
+
+/*********************************************************/
+/*
+ * Autoconf defined configuration options
+ * You shouldn't need to touch any of these
+ */
+
+/* Ignore this */
+#undef DUMMY_PPP
+
+/* Define if you have unistd.h */
+#define HAVE_UNISTD_H
+
+/* Define if you have stdlib.h */
+#define HAVE_STDLIB_H
+
+/* Define if you have sys/ioctl.h */
+#undef HAVE_SYS_IOCTL_H
+#ifndef _WIN32
+#define HAVE_SYS_IOCTL_H
+#endif
+
+/* Define if you have sys/filio.h */
+#undef HAVE_SYS_FILIO_H
+#ifdef __APPLE__
+#define HAVE_SYS_FILIO_H
+#endif
+
+/* Define if you have strerror */
+#define HAVE_STRERROR
+
+/* Define if you have strdup() */
+#define HAVE_STRDUP
+
+/* Define according to how time.h should be included */
+#define TIME_WITH_SYS_TIME 0
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have sys/bitypes.h */
+#undef HAVE_SYS_BITYPES_H
+
+/* Define if the machine is big endian */
+//#undef HOST_WORDS_BIGENDIAN
+
+/* Define if you have readv */
+#undef HAVE_READV
+
+/* Define if iovec needs to be declared */
+#undef DECLARE_IOVEC
+#ifdef _WIN32
+#define DECLARE_IOVEC
+#endif
+
+/* Define if you have a POSIX.1 sys/wait.h */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have sys/select.h */
+#undef HAVE_SYS_SELECT_H
+#ifndef _WIN32
+#define HAVE_SYS_SELECT_H
+#endif
+
+/* Define if you have strings.h */
+#define HAVE_STRING_H
+
+/* Define if you have arpa/inet.h */
+#undef HAVE_ARPA_INET_H
+#ifndef _WIN32
+#define HAVE_ARPA_INET_H
+#endif
+
+/* Define if you have sys/signal.h */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have sys/stropts.h */
+#undef HAVE_SYS_STROPTS_H
+
+/* Define to whatever your compiler thinks inline should be */
+//#define inline inline
+
+/* Define to whatever your compiler thinks const should be */
+//#define const const
+
+/* Define if your compiler doesn't like prototypes */
+#undef NO_PROTOTYPES
+
+/* Define to sizeof(char) */
+#define SIZEOF_CHAR 1
+
+/* Define to sizeof(short) */
+#define SIZEOF_SHORT 2
+
+/* Define to sizeof(int) */
+#define SIZEOF_INT 4
+
+/* Define to sizeof(char *) */
+#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
+
+/* Define if you have random() */
+#undef HAVE_RANDOM
+
+/* Define if you have srandom() */
+#undef HAVE_SRANDOM
+
+/* Define if you have inet_aton */
+#undef HAVE_INET_ATON
+#ifndef _WIN32
+#define HAVE_INET_ATON
+#endif
+
+/* Define if you have setenv */
+#undef HAVE_SETENV
+
+/* Define if you have index() */
+#define HAVE_INDEX
+
+/* Define if you have bcmp() */
+#undef HAVE_BCMP
+
+/* Define if you have drand48 */
+#undef HAVE_DRAND48
+
+/* Define if you have memmove */
+#define HAVE_MEMMOVE
+
+/* Define if you have gethostid */
+#define HAVE_GETHOSTID
+
+/* Define if you DON'T have unix-domain sockets */
+#undef NO_UNIX_SOCKETS
+#ifdef _WIN32
+#define NO_UNIX_SOCKETS
+#endif
+
+/* Define if you have revoke() */
+#undef HAVE_REVOKE
+
+/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
+#undef HAVE_GRANTPT
+
+/* Define if you have fchmod */
+#undef HAVE_FCHMOD
+
+/* Define if you have <sys/type32.h> */
+#undef HAVE_SYS_TYPES32_H
diff --git a/slirp/socket.c b/slirp/socket.c
new file mode 100644
index 0000000..6119234
--- /dev/null
+++ b/slirp/socket.c
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include "qemu-common.h"
+#include <slirp.h>
+#include "ip_icmp.h"
+#ifdef __sun__
+#include <sys/filio.h>
+#endif
+
+static void sofcantrcvmore(struct socket *so);
+static void sofcantsendmore(struct socket *so);
+
+struct socket *
+solookup(struct socket *head, struct in_addr laddr, u_int lport,
+ struct in_addr faddr, u_int fport)
+{
+ struct socket *so;
+
+ for (so = head->so_next; so != head; so = so->so_next) {
+ if (so->so_lport == lport &&
+ so->so_laddr.s_addr == laddr.s_addr &&
+ so->so_faddr.s_addr == faddr.s_addr &&
+ so->so_fport == fport)
+ break;
+ }
+
+ if (so == head)
+ return (struct socket *)NULL;
+ return so;
+
+}
+
+/*
+ * Create a new socket, initialise the fields
+ * It is the responsibility of the caller to
+ * insque() it into the correct linked-list
+ */
+struct socket *
+socreate(Slirp *slirp)
+{
+ struct socket *so;
+
+ so = (struct socket *)malloc(sizeof(struct socket));
+ if(so) {
+ memset(so, 0, sizeof(struct socket));
+ so->so_state = SS_NOFDREF;
+ so->s = -1;
+ so->slirp = slirp;
+ }
+ return(so);
+}
+
+/*
+ * remque and free a socket, clobber cache
+ */
+void
+sofree(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+
+ if (so->so_emu==EMU_RSH && so->extra) {
+ sofree(so->extra);
+ so->extra=NULL;
+ }
+ if (so == slirp->tcp_last_so) {
+ slirp->tcp_last_so = &slirp->tcb;
+ } else if (so == slirp->udp_last_so) {
+ slirp->udp_last_so = &slirp->udb;
+ }
+ m_free(so->so_m);
+
+ if(so->so_next && so->so_prev)
+ remque(so); /* crashes if so is not in a queue */
+
+ free(so);
+}
+
+size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np)
+{
+ int n, lss, total;
+ struct sbuf *sb = &so->so_snd;
+ int len = sb->sb_datalen - sb->sb_cc;
+ int mss = so->so_tcpcb->t_maxseg;
+
+ DEBUG_CALL("sopreprbuf");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if (len <= 0)
+ return 0;
+
+ iov[0].iov_base = sb->sb_wptr;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ if (sb->sb_wptr < sb->sb_rptr) {
+ iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_rptr - sb->sb_data;
+ if(iov[1].iov_len > len)
+ iov[1].iov_len = len;
+ total = iov[0].iov_len + iov[1].iov_len;
+ if (total > mss) {
+ lss = total%mss;
+ if (iov[1].iov_len > lss) {
+ iov[1].iov_len -= lss;
+ n = 2;
+ } else {
+ lss -= iov[1].iov_len;
+ iov[0].iov_len -= lss;
+ n = 1;
+ }
+ } else
+ n = 2;
+ } else {
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ }
+ }
+ if (np)
+ *np = n;
+
+ return iov[0].iov_len + (n - 1) * iov[1].iov_len;
+}
+
+/*
+ * Read from so's socket into sb_snd, updating all relevant sbuf fields
+ * NOTE: This will only be called if it is select()ed for reading, so
+ * a read() of 0 (or less) means it's disconnected
+ */
+int
+soread(struct socket *so)
+{
+ int n, nn;
+ struct sbuf *sb = &so->so_snd;
+ struct iovec iov[2];
+
+ DEBUG_CALL("soread");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+ sopreprbuf(so, iov, &n);
+
+#ifdef HAVE_READV
+ nn = readv(so->s, (struct iovec *)iov, n);
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#else
+ nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ if (nn <= 0) {
+ if (nn < 0 && (errno == EINTR || errno == EAGAIN))
+ return 0;
+ else {
+ DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno)));
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+ }
+
+#ifndef HAVE_READV
+ /*
+ * If there was no error, try and read the second time round
+ * We read again if n = 2 (ie, there's another part of the buffer)
+ * and we read as much as we could in the first read
+ * We don't test for <= 0 this time, because there legitimately
+ * might not be any more data (since the socket is non-blocking),
+ * a close will be detected on next iteration.
+ * A return of -1 wont (shouldn't) happen, since it didn't happen above
+ */
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+
+ DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#endif
+
+ /* Update fields */
+ sb->sb_cc += nn;
+ sb->sb_wptr += nn;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return nn;
+}
+
+int soreadbuf(struct socket *so, const char *buf, int size)
+{
+ int n, nn, copy = size;
+ struct sbuf *sb = &so->so_snd;
+ struct iovec iov[2];
+
+ DEBUG_CALL("soreadbuf");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+ if (sopreprbuf(so, iov, &n) < size)
+ goto err;
+
+ nn = MIN(iov[0].iov_len, copy);
+ memcpy(iov[0].iov_base, buf, nn);
+
+ copy -= nn;
+ buf += nn;
+
+ if (copy == 0)
+ goto done;
+
+ memcpy(iov[1].iov_base, buf, copy);
+
+done:
+ /* Update fields */
+ sb->sb_cc += size;
+ sb->sb_wptr += size;
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_wptr -= sb->sb_datalen;
+ return size;
+err:
+
+ sofcantrcvmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ fprintf(stderr, "soreadbuf buffer to small");
+ return -1;
+}
+
+/*
+ * Get urgent data
+ *
+ * When the socket is created, we set it SO_OOBINLINE,
+ * so when OOB data arrives, we soread() it and everything
+ * in the send buffer is sent as urgent data
+ */
+void
+sorecvoob(struct socket *so)
+{
+ struct tcpcb *tp = sototcpcb(so);
+
+ DEBUG_CALL("sorecvoob");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ /*
+ * We take a guess at how much urgent data has arrived.
+ * In most situations, when urgent data arrives, the next
+ * read() should get all the urgent data. This guess will
+ * be wrong however if more data arrives just after the
+ * urgent data, or the read() doesn't return all the
+ * urgent data.
+ */
+ soread(so);
+ tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+ tp->t_force = 1;
+ tcp_output(tp);
+ tp->t_force = 0;
+}
+
+/*
+ * Send urgent data
+ * There's a lot duplicated code here, but...
+ */
+int
+sosendoob(struct socket *so)
+{
+ struct sbuf *sb = &so->so_rcv;
+ char buff[2048]; /* XXX Shouldn't be sending more oob data than this */
+
+ int n, len;
+
+ DEBUG_CALL("sosendoob");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
+
+ if (so->so_urgc > 2048)
+ so->so_urgc = 2048; /* XXXX */
+
+ if (sb->sb_rptr < sb->sb_wptr) {
+ /* We can send it directly */
+ n = slirp_send(so, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+ so->so_urgc -= n;
+
+ DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ } else {
+ /*
+ * Since there's no sendv or sendtov like writev,
+ * we must copy all data to a linear buffer then
+ * send it all
+ */
+ len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (len > so->so_urgc) len = so->so_urgc;
+ memcpy(buff, sb->sb_rptr, len);
+ so->so_urgc -= len;
+ if (so->so_urgc) {
+ n = sb->sb_wptr - sb->sb_data;
+ if (n > so->so_urgc) n = so->so_urgc;
+ memcpy((buff + len), sb->sb_data, n);
+ so->so_urgc -= n;
+ len += n;
+ }
+ n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+#ifdef DEBUG
+ if (n != len)
+ DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
+#endif
+ DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+ }
+
+ sb->sb_cc -= n;
+ sb->sb_rptr += n;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ return n;
+}
+
+/*
+ * Write data from so_rcv to so's socket,
+ * updating all sbuf field as necessary
+ */
+int
+sowrite(struct socket *so)
+{
+ int n,nn;
+ struct sbuf *sb = &so->so_rcv;
+ int len = sb->sb_cc;
+ struct iovec iov[2];
+
+ DEBUG_CALL("sowrite");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_urgc) {
+ sosendoob(so);
+ if (sb->sb_cc == 0)
+ return 0;
+ }
+
+ /*
+ * No need to check if there's something to write,
+ * sowrite wouldn't have been called otherwise
+ */
+
+ iov[0].iov_base = sb->sb_rptr;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ if (sb->sb_rptr < sb->sb_wptr) {
+ iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ n = 1;
+ } else {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (iov[0].iov_len > len) iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len) {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_wptr - sb->sb_data;
+ if (iov[1].iov_len > len) iov[1].iov_len = len;
+ n = 2;
+ } else
+ n = 1;
+ }
+ /* Check if there's urgent data to send, and if so, send it */
+
+#ifdef HAVE_READV
+ nn = writev(so->s, (const struct iovec *)iov, n);
+
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#else
+ nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len,0);
+#endif
+ /* This should never happen, but people tell me it does *shrug* */
+ if (nn < 0 && (errno == EAGAIN || errno == EINTR))
+ return 0;
+
+ if (nn <= 0) {
+ DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n",
+ so->so_state, errno));
+ sofcantsendmore(so);
+ tcp_sockclosed(sototcpcb(so));
+ return -1;
+ }
+
+#ifndef HAVE_READV
+ if (n == 2 && nn == iov[0].iov_len) {
+ int ret;
+ ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0);
+ if (ret > 0)
+ nn += ret;
+ }
+ DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
+#endif
+
+ /* Update sbuf */
+ sb->sb_cc -= nn;
+ sb->sb_rptr += nn;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ /*
+ * If in DRAIN mode, and there's no more data, set
+ * it CANTSENDMORE
+ */
+ if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
+ sofcantsendmore(so);
+
+ return nn;
+}
+
+/*
+ * recvfrom() a UDP socket
+ */
+void
+sorecvfrom(struct socket *so)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+
+ DEBUG_CALL("sorecvfrom");
+ DEBUG_ARG("so = %lx", (long)so);
+
+ if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
+ char buff[256];
+ int len;
+
+ len = recvfrom(so->s, buff, 256, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ /* XXX Check if reply is "correct"? */
+
+ if(len == -1 || len == 0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
+ errno,strerror(errno)));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ } else {
+ icmp_reflect(so->so_m);
+ so->so_m = NULL; /* Don't m_free() it again! */
+ }
+ /* No need for this socket anymore, udp_detach it */
+ udp_detach(so);
+ } else { /* A "normal" UDP packet */
+ struct mbuf *m;
+ int len;
+#ifdef _WIN32
+ unsigned long n;
+#else
+ int n;
+#endif
+
+ m = m_get(so->slirp);
+ if (!m) {
+ return;
+ }
+ m->m_data += IF_MAXLINKHDR;
+
+ /*
+ * XXX Shouldn't FIONREAD packets destined for port 53,
+ * but I don't know the max packet size for DNS lookups
+ */
+ len = M_FREEROOM(m);
+ /* if (so->so_fport != htons(53)) { */
+ ioctlsocket(so->s, FIONREAD, &n);
+
+ if (n > len) {
+ n = (m->m_data - m->m_dat) + m->m_len + n + 1;
+ m_inc(m, n);
+ len = M_FREEROOM(m);
+ }
+ /* } */
+
+ m->m_len = recvfrom(so->s, m->m_data, len, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
+ m->m_len, errno,strerror(errno)));
+ if(m->m_len<0) {
+ u_char code=ICMP_UNREACH_PORT;
+
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+ DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
+ icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
+ m_free(m);
+ } else {
+ /*
+ * Hack: domain name lookup will be used the most for UDP,
+ * and since they'll only be used once there's no need
+ * for the 4 minute (or whatever) timeout... So we time them
+ * out much quicker (10 seconds for now...)
+ */
+ if (so->so_expire) {
+ if (so->so_fport == htons(53))
+ so->so_expire = curtime + SO_EXPIREFAST;
+ else
+ so->so_expire = curtime + SO_EXPIRE;
+ }
+
+ /*
+ * If this packet was destined for CTL_ADDR,
+ * make it look like that's where it came from, done by udp_output
+ */
+ udp_output(so, m, &addr);
+ } /* rx error */
+ } /* if ping packet */
+}
+
+/*
+ * sendto() a socket
+ */
+int
+sosendto(struct socket *so, struct mbuf *m)
+{
+ Slirp *slirp = so->slirp;
+ int ret;
+ struct sockaddr_in addr;
+
+ DEBUG_CALL("sosendto");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&addr.sin_addr) < 0)
+ addr.sin_addr = loopback_addr;
+ } else {
+ addr.sin_addr = loopback_addr;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+
+ /* Don't care what port we get */
+ ret = sendto(so->s, m->m_data, m->m_len, 0,
+ (struct sockaddr *)&addr, sizeof (struct sockaddr));
+ if (ret < 0)
+ return -1;
+
+ /*
+ * Kill the socket if there's no reply in 4 minutes,
+ * but only if it's an expirable socket
+ */
+ if (so->so_expire)
+ so->so_expire = curtime + SO_EXPIRE;
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */
+ return 0;
+}
+
+/*
+ * Listen for incoming TCP connections
+ */
+struct socket *
+tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
+ u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ int s, opt = 1;
+ socklen_t addrlen = sizeof(addr);
+ memset(&addr, 0, addrlen);
+
+ DEBUG_CALL("tcp_listen");
+ DEBUG_ARG("haddr = %x", haddr);
+ DEBUG_ARG("hport = %d", hport);
+ DEBUG_ARG("laddr = %x", laddr);
+ DEBUG_ARG("lport = %d", lport);
+ DEBUG_ARG("flags = %x", flags);
+
+ so = socreate(slirp);
+ if (!so) {
+ return NULL;
+ }
+
+ /* Don't tcp_attach... we don't need so_snd nor so_rcv */
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
+ free(so);
+ return NULL;
+ }
+ insque(so, &slirp->tcb);
+
+ /*
+ * SS_FACCEPTONCE sockets must time out.
+ */
+ if (flags & SS_FACCEPTONCE)
+ so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
+
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= (SS_FACCEPTCONN | flags);
+ so->so_lport = lport; /* Kept in network format */
+ so->so_laddr.s_addr = laddr; /* Ditto */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = haddr;
+ addr.sin_port = hport;
+
+ if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) < 0) ||
+ (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) ||
+ (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) ||
+ (listen(s,1) < 0)) {
+ int tmperrno = errno; /* Don't clobber the real reason we failed */
+
+ close(s);
+ sofree(so);
+ /* Restore the real errno */
+#ifdef _WIN32
+ WSASetLastError(tmperrno);
+#else
+ errno = tmperrno;
+#endif
+ return NULL;
+ }
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+
+ getsockname(s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = slirp->vhost_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->s = s;
+ return so;
+}
+
+/*
+ * Various session state calls
+ * XXX Should be #define's
+ * The socket state stuff needs work, these often get call 2 or 3
+ * times each when only 1 was needed
+ */
+void
+soisfconnecting(struct socket *so)
+{
+ so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
+ SS_FCANTSENDMORE|SS_FWDRAIN);
+ so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
+}
+
+void
+soisfconnected(struct socket *so)
+{
+ so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
+ so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
+}
+
+static void
+sofcantrcvmore(struct socket *so)
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,0);
+ if(global_writefds) {
+ FD_CLR(so->s,global_writefds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTSENDMORE) {
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF; /* Don't select it */
+ } else {
+ so->so_state |= SS_FCANTRCVMORE;
+ }
+}
+
+static void
+sofcantsendmore(struct socket *so)
+{
+ if ((so->so_state & SS_NOFDREF) == 0) {
+ shutdown(so->s,1); /* send FIN to fhost */
+ if (global_readfds) {
+ FD_CLR(so->s,global_readfds);
+ }
+ if (global_xfds) {
+ FD_CLR(so->s,global_xfds);
+ }
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTRCVMORE) {
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF; /* as above */
+ } else {
+ so->so_state |= SS_FCANTSENDMORE;
+ }
+}
+
+/*
+ * Set write drain mode
+ * Set CANTSENDMORE once all data has been write()n
+ */
+void
+sofwdrain(struct socket *so)
+{
+ if (so->so_rcv.sb_cc)
+ so->so_state |= SS_FWDRAIN;
+ else
+ sofcantsendmore(so);
+}
diff --git a/slirp/socket.h b/slirp/socket.h
new file mode 100644
index 0000000..857b0da
--- /dev/null
+++ b/slirp/socket.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SLIRP_SOCKET_H_
+#define _SLIRP_SOCKET_H_
+
+#define SO_EXPIRE 240000
+#define SO_EXPIREFAST 10000
+
+/*
+ * Our socket structure
+ */
+
+struct socket {
+ struct socket *so_next,*so_prev; /* For a linked list of sockets */
+
+ int s; /* The actual socket */
+
+ Slirp *slirp; /* managing slirp instance */
+
+ /* XXX union these with not-yet-used sbuf params */
+ struct mbuf *so_m; /* Pointer to the original SYN packet,
+ * for non-blocking connect()'s, and
+ * PING reply's */
+ struct tcpiphdr *so_ti; /* Pointer to the original ti within
+ * so_mconn, for non-blocking connections */
+ int so_urgc;
+ struct in_addr so_faddr; /* foreign host table entry */
+ struct in_addr so_laddr; /* local host table entry */
+ uint16_t so_fport; /* foreign port */
+ uint16_t so_lport; /* local port */
+
+ uint8_t so_iptos; /* Type of service */
+ uint8_t so_emu; /* Is the socket emulated? */
+
+ u_char so_type; /* Type of socket, UDP or TCP */
+ int so_state; /* internal state flags SS_*, below */
+
+ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
+ u_int so_expire; /* When the socket will expire */
+
+ int so_queued; /* Number of packets queued from this socket */
+ int so_nqueued; /* Number of packets queued in a row
+ * Used to determine when to "downgrade" a session
+ * from fastq to batchq */
+
+ struct sbuf so_rcv; /* Receive buffer */
+ struct sbuf so_snd; /* Send buffer */
+ void * extra; /* Extra pointer */
+};
+
+
+/*
+ * Socket state bits. (peer means the host on the Internet,
+ * local host means the host on the other end of the modem)
+ */
+#define SS_NOFDREF 0x001 /* No fd reference */
+
+#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */
+#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */
+#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */
+#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */
+#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */
+
+#define SS_CTL 0x080
+#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */
+#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */
+
+#define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */
+#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
+#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */
+
+struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int);
+struct socket * socreate(Slirp *);
+void sofree(struct socket *);
+int soread(struct socket *);
+void sorecvoob(struct socket *);
+int sosendoob(struct socket *);
+int sowrite(struct socket *);
+void sorecvfrom(struct socket *);
+int sosendto(struct socket *, struct mbuf *);
+struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
+ int);
+void soisfconnecting(register struct socket *);
+void soisfconnected(register struct socket *);
+void sofwdrain(struct socket *);
+struct iovec; /* For win32 */
+size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
+int soreadbuf(struct socket *so, const char *buf, int size);
+
+#endif /* _SOCKET_H_ */
diff --git a/slirp/tcp.h b/slirp/tcp.h
new file mode 100644
index 0000000..9d06836
--- /dev/null
+++ b/slirp/tcp.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
+ */
+
+#ifndef _TCP_H_
+#define _TCP_H_
+
+typedef uint32_t tcp_seq;
+
+#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
+#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
+
+#define TCP_SNDSPACE 8192
+#define TCP_RCVSPACE 8192
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
+ tcp_seq th_seq; /* sequence number */
+ tcp_seq th_ack; /* acknowledgement number */
+#ifdef HOST_WORDS_BIGENDIAN
+ u_int th_off:4, /* data offset */
+ th_x2:4; /* (unused) */
+#else
+ u_int th_x2:4, /* (unused) */
+ th_off:4; /* data offset */
+#endif
+ uint8_t th_flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
+};
+
+#include "tcp_var.h"
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WINDOW 3
+#define TCPOLEN_WINDOW 3
+#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
+#define TCPOLEN_SACK_PERMITTED 2
+#define TCPOPT_SACK 5 /* Experimental */
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ *
+ * We make this 1460 because we only care about Ethernet in the qemu context.
+ */
+#define TCP_MSS 1460
+
+#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ *
+ * We don't use the system headers on unix because we have conflicting
+ * local structures. We can't avoid the system definitions on Windows,
+ * so we undefine them.
+ */
+#undef TCP_NODELAY
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#undef TCP_MAXSEG
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES 11
+
+#define TCPS_CLOSED 0 /* closed */
+#define TCPS_LISTEN 1 /* listening for connection */
+#define TCPS_SYN_SENT 2 /* active, have sent syn */
+#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED 4 /* established */
+#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
+#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
+#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
+#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
+
+/*
+ * TCP sequence numbers are 32 bit integers operated
+ * on with modular arithmetic. These macros can be
+ * used to compare such integers.
+ */
+#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+ (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss
+
+#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
+
+#endif
diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c
new file mode 100644
index 0000000..e4a7731
--- /dev/null
+++ b/slirp/tcp_input.c
@@ -0,0 +1,1487 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94
+ * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+#define TCPREXMTTHRESH 3
+
+#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b) ((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp. Return TH_FIN if reassembly now includes
+ * a segment with FIN. The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#ifdef TCP_ACK_HACK
+#define TCP_REASS(tp, ti, m, so, flags) {\
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ tcpfrag_list_empty(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) {\
+ if (ti->ti_flags & TH_PUSH) \
+ tp->t_flags |= TF_ACKNOW; \
+ else \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend((so), (m)); \
+ } else \
+ sbappend((so), (m)); \
+ } else {\
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#else
+#define TCP_REASS(tp, ti, m, so, flags) { \
+ if ((ti)->ti_seq == (tp)->rcv_nxt && \
+ tcpfrag_list_empty(tp) && \
+ (tp)->t_state == TCPS_ESTABLISHED) { \
+ tp->t_flags |= TF_DELACK; \
+ (tp)->rcv_nxt += (ti)->ti_len; \
+ flags = (ti)->ti_flags & TH_FIN; \
+ if (so->so_emu) { \
+ if (tcp_emu((so),(m))) sbappend(so, (m)); \
+ } else \
+ sbappend((so), (m)); \
+ } else { \
+ (flags) = tcp_reass((tp), (ti), (m)); \
+ tp->t_flags |= TF_ACKNOW; \
+ } \
+}
+#endif
+static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt,
+ struct tcpiphdr *ti);
+static void tcp_xmit_timer(register struct tcpcb *tp, int rtt);
+
+static int
+tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti,
+ struct mbuf *m)
+{
+ register struct tcpiphdr *q;
+ struct socket *so = tp->t_socket;
+ int flags;
+
+ /*
+ * Call with ti==NULL after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (ti == NULL)
+ goto present;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp);
+ q = tcpiphdr_next(q))
+ if (SEQ_GT(q->ti_seq, ti->ti_seq))
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) {
+ register int i;
+ q = tcpiphdr_prev(q);
+ /* conversion to int (in i) handles seq wraparound */
+ i = q->ti_seq + q->ti_len - ti->ti_seq;
+ if (i > 0) {
+ if (i >= ti->ti_len) {
+ m_freem(m);
+ /*
+ * Try to present any queued data
+ * at the left window edge to the user.
+ * This is needed after the 3-WHS
+ * completes.
+ */
+ goto present; /* ??? */
+ }
+ m_adj(m, i);
+ ti->ti_len -= i;
+ ti->ti_seq += i;
+ }
+ q = tcpiphdr_next(q);
+ }
+ ti->ti_mbuf = m;
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (!tcpfrag_list_end(q, tp)) {
+ register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+ if (i <= 0)
+ break;
+ if (i < q->ti_len) {
+ q->ti_seq += i;
+ q->ti_len -= i;
+ m_adj(q->ti_mbuf, i);
+ break;
+ }
+ q = tcpiphdr_next(q);
+ m = tcpiphdr_prev(q)->ti_mbuf;
+ remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
+ m_freem(m);
+ }
+
+ /*
+ * Stick new segment in its place.
+ */
+ insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q)));
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (!TCPS_HAVEESTABLISHED(tp->t_state))
+ return (0);
+ ti = tcpfrag_list_first(tp);
+ if (tcpfrag_list_end(ti, tp) || ti->ti_seq != tp->rcv_nxt)
+ return (0);
+ if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+ return (0);
+ do {
+ tp->rcv_nxt += ti->ti_len;
+ flags = ti->ti_flags & TH_FIN;
+ remque(tcpiphdr2qlink(ti));
+ m = ti->ti_mbuf;
+ ti = tcpiphdr_next(ti);
+ if (so->so_state & SS_FCANTSENDMORE)
+ m_freem(m);
+ else {
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+ }
+ } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);
+ return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
+{
+ struct ip save_ip, *ip;
+ register struct tcpiphdr *ti;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, tlen, off;
+ register struct tcpcb *tp = NULL;
+ register int tiflags;
+ struct socket *so = NULL;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+ int iss = 0;
+ u_long tiwin;
+ int ret;
+ struct ex_list *ex_ptr;
+ Slirp *slirp;
+
+ DEBUG_CALL("tcp_input");
+ DEBUG_ARGS((dfd," m = %8lx iphlen = %2d inso = %lx\n",
+ (long )m, iphlen, (long )inso ));
+
+ /*
+ * If called with m == 0, then we're continuing the connect
+ */
+ if (m == NULL) {
+ so = inso;
+ slirp = so->slirp;
+
+ /* Re-set a few variables */
+ tp = sototcpcb(so);
+ m = so->so_m;
+ so->so_m = NULL;
+ ti = so->so_ti;
+ tiwin = ti->ti_win;
+ tiflags = ti->ti_flags;
+
+ goto cont_conn;
+ }
+ slirp = m->slirp;
+
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof(struct ip )) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen=sizeof(struct ip );
+ }
+ /* XXX Check if too short */
+
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ ip=mtod(m, struct ip *);
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen;
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL;
+ memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ ti->ti_x1 = 0;
+ ti->ti_len = htons((uint16_t)tlen);
+ len = sizeof(struct ip ) + tlen;
+ if(cksum(m, len)) {
+ goto drop;
+ }
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if (off < sizeof (struct tcphdr) || off > tlen) {
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr)) {
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ if (slirp->restricted) {
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_fport == ti->ti_dport &&
+ ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) {
+ break;
+ }
+ }
+ if (!ex_ptr)
+ goto drop;
+ }
+ /*
+ * Locate pcb for segment.
+ */
+findso:
+ so = slirp->tcp_last_so;
+ if (so->so_fport != ti->ti_dport ||
+ so->so_lport != ti->ti_sport ||
+ so->so_laddr.s_addr != ti->ti_src.s_addr ||
+ so->so_faddr.s_addr != ti->ti_dst.s_addr) {
+ so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport);
+ if (so)
+ slirp->tcp_last_so = so;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ *
+ * state == CLOSED means we've done socreate() but haven't
+ * attached it to a protocol yet...
+ *
+ * XXX If a TCB does not exist, and the TH_SYN flag is
+ * the only flag set, then create a session, mark it
+ * as if it was LISTENING, and continue...
+ */
+ if (so == NULL) {
+ if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN)
+ goto dropwithreset;
+
+ if ((so = socreate(slirp)) == NULL)
+ goto dropwithreset;
+ if (tcp_attach(so) < 0) {
+ free(so); /* Not sofree (if it failed, it's not insqued) */
+ goto dropwithreset;
+ }
+
+ sbreserve(&so->so_snd, TCP_SNDSPACE);
+ sbreserve(&so->so_rcv, TCP_RCVSPACE);
+
+ so->so_laddr = ti->ti_src;
+ so->so_lport = ti->ti_sport;
+ so->so_faddr = ti->ti_dst;
+ so->so_fport = ti->ti_dport;
+
+ if ((so->so_iptos = tcp_tos(so)) == 0)
+ so->so_iptos = ((struct ip *)ti)->ip_tos;
+
+ tp = sototcpcb(so);
+ tp->t_state = TCPS_LISTEN;
+ }
+
+ /*
+ * If this is a still-connecting socket, this probably
+ * a retransmit of the SYN. Whether it's a retransmit SYN
+ * or something else, we nuke it.
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ goto drop;
+
+ tp = sototcpcb(so);
+
+ /* XXX Should never fail */
+ if (tp == NULL)
+ goto dropwithreset;
+ if (tp->t_state == TCPS_CLOSED)
+ goto drop;
+
+ tiwin = ti->ti_win;
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ if (SO_OPTIONS)
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
+ else
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ *
+ * XXX Some of these tests are not needed
+ * eg: the tiwin == tp->snd_wnd prevents many more
+ * predictions.. with no *real* advantage..
+ */
+ if (tp->t_state == TCPS_ESTABLISHED &&
+ (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+ ti->ti_seq == tp->rcv_nxt &&
+ tiwin && tiwin == tp->snd_wnd &&
+ tp->snd_nxt == tp->snd_max) {
+ if (ti->ti_len == 0) {
+ if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+ SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+ tp->snd_cwnd >= tp->snd_wnd) {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ if (tp->t_rtt &&
+ SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ /*
+ * This is called because sowwakeup might have
+ * put data into so_snd. Since we don't so sowwakeup,
+ * we don't need this.. XXX???
+ */
+ if (so->so_snd.sb_cc)
+ (void) tcp_output(tp);
+
+ return;
+ }
+ } else if (ti->ti_ack == tp->snd_una &&
+ tcpfrag_list_empty(tp) &&
+ ti->ti_len <= sbspace(&so->so_rcv)) {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ tp->rcv_nxt += ti->ti_len;
+ /*
+ * Add data to socket buffer.
+ */
+ if (so->so_emu) {
+ if (tcp_emu(so,m)) sbappend(so, m);
+ } else
+ sbappend(so, m);
+
+ /*
+ * If this is a short packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * It is better to not delay acks at all to maximize
+ * TCP throughput. See RFC 2581.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(tp);
+ return;
+ }
+ } /* header prediction */
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ { int win;
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state) {
+
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN: {
+
+ if (tiflags & TH_RST)
+ goto drop;
+ if (tiflags & TH_ACK)
+ goto dropwithreset;
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+
+ /*
+ * This has way too many gotos...
+ * But a bit of spaghetti code never hurt anybody :)
+ */
+
+ /*
+ * If this is destined for the control address, then flag to
+ * tcp_ctl once connected, otherwise connect
+ */
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr &&
+ so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) {
+ /* May be an add exec */
+ for (ex_ptr = slirp->exec_list; ex_ptr;
+ ex_ptr = ex_ptr->ex_next) {
+ if(ex_ptr->ex_fport == so->so_fport &&
+ so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
+ so->so_state |= SS_CTL;
+ break;
+ }
+ }
+ if (so->so_state & SS_CTL) {
+ goto cont_input;
+ }
+ }
+ /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */
+ }
+
+ if (so->so_emu & EMU_NOCONNECT) {
+ so->so_emu &= ~EMU_NOCONNECT;
+ goto cont_input;
+ }
+
+ if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
+ u_char code=ICMP_UNREACH_NET;
+ DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n",
+ errno,strerror(errno)));
+ if(errno == ECONNREFUSED) {
+ /* ACK the SYN, send RST to refuse the connection */
+ tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ } else {
+ if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+ HTONL(ti->ti_seq); /* restore tcp header */
+ HTONL(ti->ti_ack);
+ HTONS(ti->ti_win);
+ HTONS(ti->ti_urp);
+ m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ *ip=save_ip;
+ icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno));
+ }
+ tcp_close(tp);
+ m_free(m);
+ } else {
+ /*
+ * Haven't connected yet, save the current mbuf
+ * and ti, and return
+ * XXX Some OS's don't tell us whether the connect()
+ * succeeded or not. So we must time it out.
+ */
+ so->so_m = m;
+ so->so_ti = ti;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ }
+ return;
+
+ cont_conn:
+ /* m==NULL
+ * Check if the connect succeeded
+ */
+ if (so->so_state & SS_NOFDREF) {
+ tp = tcp_close(tp);
+ goto dropwithreset;
+ }
+ cont_input:
+ tcp_template(tp);
+
+ if (optp)
+ tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = slirp->tcp_iss;
+ slirp->tcp_iss += TCP_ISSINCR/2;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ tp->t_state = TCPS_SYN_RECEIVED;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ goto trimthenstep6;
+ } /* case TCPS_LISTEN */
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max)))
+ goto dropwithreset;
+
+ if (tiflags & TH_RST) {
+ if (tiflags & TH_ACK) {
+ tcp_drop(tp, 0); /* XXX Check t_softerror! */
+ }
+ goto drop;
+ }
+
+ if ((tiflags & TH_SYN) == 0)
+ goto drop;
+ if (tiflags & TH_ACK) {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+ soisfconnected(so);
+ tp->t_state = TCPS_ESTABLISHED;
+
+ (void) tcp_reass(tp, (struct tcpiphdr *)0,
+ (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(tp, tp->t_rtt);
+ } else
+ tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd) {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ goto step6;
+ } /* switch tp->t_state */
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * Check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ */
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0) {
+ if (tiflags & TH_SYN) {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ /*
+ * Following if statement from Stevens, vol. 2, p. 960.
+ */
+ if (todrop > ti->ti_len
+ || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) {
+ /*
+ * Any valid FIN must be to the left of the window.
+ * At this point the FIN must be a duplicate or out
+ * of sequence; drop it.
+ */
+ tiflags &= ~TH_FIN;
+
+ /*
+ * Send an ACK to resynchronize and drop any data.
+ * But keep on processing for RST or ACK.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ todrop = ti->ti_len;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ((so->so_state & SS_NOFDREF) &&
+ tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+ tp = tcp_close(tp);
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0) {
+ if (todrop >= ti->ti_len) {
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if (tiflags & TH_SYN &&
+ tp->t_state == TCPS_TIME_WAIT &&
+ SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+ iss = tp->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(tp);
+ goto findso;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+ tp->t_flags |= TF_ACKNOW;
+ } else {
+ goto dropafterack;
+ }
+ }
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST) switch (tp->t_state) {
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_CLOSED;
+ tcp_close(tp);
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tcp_close(tp);
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN) {
+ tp = tcp_drop(tp,0);
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0) goto drop;
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state) {
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST. una<=ack<=max
+ */
+ case TCPS_SYN_RECEIVED:
+
+ if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+ SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tp->t_state = TCPS_ESTABLISHED;
+ /*
+ * The sent SYN is ack'ed with our sequence number +1
+ * The first data byte already in the buffer will get
+ * lost if no correction is made. This is only needed for
+ * SS_CTL since the buffer is empty otherwise.
+ * tp->snd_una++; or:
+ */
+ tp->snd_una=ti->ti_ack;
+ if (so->so_state & SS_CTL) {
+ /* So tcp_ctl reports the right state */
+ ret = tcp_ctl(so);
+ if (ret == 1) {
+ soisfconnected(so);
+ so->so_state &= ~SS_CTL; /* success XXX */
+ } else if (ret == 2) {
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF; /* CTL_CMD */
+ } else {
+ needoutput = 1;
+ tp->t_state = TCPS_FIN_WAIT_1;
+ }
+ } else {
+ soisfconnected(so);
+ }
+
+ (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* Avoid ack processing; snd_una==ti_ack => dup ack */
+ goto synrx_to_est;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+ DEBUG_MISC((dfd," dup ack m = %lx so = %lx \n",
+ (long )m, (long )so));
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 ||
+ ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == TCPREXMTTHRESH) {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win =
+ min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+ tp->t_maxseg;
+
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ goto drop;
+ } else if (tp->t_dupacks > TCPREXMTTHRESH) {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(tp);
+ goto drop;
+ }
+ } else
+ tp->t_dupacks = 0;
+ break;
+ }
+ synrx_to_est:
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if (tp->t_dupacks > TCPREXMTTHRESH &&
+ tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+
+ /*
+ * If transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+ if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(tp,tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ } else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet).
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > so->so_snd.sb_cc) {
+ tp->snd_wnd -= so->so_snd.sb_cc;
+ sbdrop(&so->so_snd, (int )so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ } else {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state) {
+
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked) {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_FCANTRCVMORE) {
+ tp->t_timer[TCPT_2MSL] = TCP_MAXIDLE;
+ }
+ tp->t_state = TCPS_FIN_WAIT_2;
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked) {
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked) {
+ tcp_close(tp);
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ goto dropafterack;
+ }
+ } /* switch(tp->t_state) */
+
+step6:
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ((tiflags & TH_ACK) &&
+ (SEQ_LT(tp->snd_wl1, ti->ti_seq) ||
+ (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+ (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) {
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) {
+ ti->ti_urp = 0;
+ tiflags &= ~TH_URG;
+ goto dodata;
+ }
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_urgc = so->so_rcv.sb_cc +
+ (tp->rcv_up - tp->rcv_nxt); /* -1; */
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+
+ }
+ } else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata:
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ((ti->ti_len || (tiflags&TH_FIN)) &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ TCP_REASS(tp, ti, m, so, tiflags);
+ } else {
+ m_free(m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN) {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+ /*
+ * If we receive a FIN we can't send more data,
+ * set it SS_FDRAIN
+ * Shutdown the socket if there is no rx data in the
+ * buffer.
+ * soread() is called on completion of shutdown() and
+ * will got to TCPS_LAST_ACK, and use tcp_output()
+ * to send the FIN.
+ */
+ sofwdrain(so);
+
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state) {
+
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ if(so->so_emu == EMU_CTL) /* no shutdown on socket */
+ tp->t_state = TCPS_LAST_ACK;
+ else
+ tp->t_state = TCPS_CLOSE_WAIT;
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ tp->t_state = TCPS_CLOSING;
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ tp->t_state = TCPS_TIME_WAIT;
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * See above.
+ */
+ if (ti->ti_len && (unsigned)ti->ti_len <= 5 &&
+ ((struct tcpiphdr_2 *)ti)->first_char == (char)27) {
+ tp->t_flags |= TF_ACKNOW;
+ }
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW)) {
+ (void) tcp_output(tp);
+ }
+ return;
+
+dropafterack:
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ goto drop;
+ m_freem(m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ return;
+
+dropwithreset:
+ /* reuses m if m!=NULL, m_free() unnecessary */
+ if (tiflags & TH_ACK)
+ tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else {
+ if (tiflags & TH_SYN) ti->ti_len++;
+ tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+
+ return;
+
+drop:
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ m_free(m);
+
+ return;
+}
+
+static void
+tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
+{
+ uint16_t mss;
+ int opt, optlen;
+
+ DEBUG_CALL("tcp_dooptions");
+ DEBUG_ARGS((dfd," tp = %lx cnt=%i \n", (long )tp, cnt));
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen) {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt) {
+
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ memcpy((char *) &mss, (char *) cp + 2, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(tp, mss); /* sets t_maxseg */
+ break;
+ }
+ }
+}
+
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+
+#ifdef notdef
+
+void
+tcp_pulloutofband(so, ti, m)
+ struct socket *so;
+ struct tcpiphdr *ti;
+ register struct mbuf *m;
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0) {
+ if (m->m_len > cnt) {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next; /* XXX WRONG! Fix it! */
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+
+#endif /* notdef */
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+
+static void
+tcp_xmit_timer(register struct tcpcb *tp, int rtt)
+{
+ register short delta;
+
+ DEBUG_CALL("tcp_xmit_timer");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("rtt = %d", rtt);
+
+ if (tp->t_srtt != 0) {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ } else {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+
+int
+tcp_mss(struct tcpcb *tp, u_int offer)
+{
+ struct socket *so = tp->t_socket;
+ int mss;
+
+ DEBUG_CALL("tcp_mss");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("offer = %d", offer);
+
+ mss = min(IF_MTU, IF_MRU) - sizeof(struct tcpiphdr);
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32);
+ if (mss < tp->t_maxseg || offer != 0)
+ tp->t_maxseg = mss;
+
+ tp->snd_cwnd = mss;
+
+ sbreserve(&so->so_snd, TCP_SNDSPACE + ((TCP_SNDSPACE % mss) ?
+ (mss - (TCP_SNDSPACE % mss)) :
+ 0));
+ sbreserve(&so->so_rcv, TCP_RCVSPACE + ((TCP_RCVSPACE % mss) ?
+ (mss - (TCP_RCVSPACE % mss)) :
+ 0));
+
+ DEBUG_MISC((dfd, " returning mss = %d\n", mss));
+
+ return mss;
+}
diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c
new file mode 100644
index 0000000..779314b
--- /dev/null
+++ b/slirp/tcp_output.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
+ * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+static const u_char tcp_outflags[TCP_NSTATES] = {
+ TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
+ TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
+ TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+
+
+#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(struct tcpcb *tp)
+{
+ register struct socket *so = tp->t_socket;
+ register long len, win;
+ int off, flags, error;
+ register struct mbuf *m;
+ register struct tcpiphdr *ti;
+ u_char opt[MAX_TCPOPTLEN];
+ unsigned optlen, hdrlen;
+ int idle, sendalot;
+
+ DEBUG_CALL("tcp_output");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /*
+ * Determine length of data that should be transmitted,
+ * and flags that will be used.
+ * If there is some data or critical controls (SYN, RST)
+ * to send, then transmit; otherwise, investigate further.
+ */
+ idle = (tp->snd_max == tp->snd_una);
+ if (idle && tp->t_idle >= tp->t_rxtcur)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tp->snd_cwnd = tp->t_maxseg;
+again:
+ sendalot = 0;
+ off = tp->snd_nxt - tp->snd_una;
+ win = min(tp->snd_wnd, tp->snd_cwnd);
+
+ flags = tcp_outflags[tp->t_state];
+
+ DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
+
+ /*
+ * If in persist timeout with window of 0, send 1 byte.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, we will send what we can
+ * and go to transmit state.
+ */
+ if (tp->t_force) {
+ if (win == 0) {
+ /*
+ * If we still have some data to send, then
+ * clear the FIN bit. Usually this would
+ * happen below when it realizes that we
+ * aren't sending all the data. However,
+ * if we have exactly 1 byte of unset data,
+ * then it won't clear the FIN bit below,
+ * and if we are in persist state, we wind
+ * up sending the packet without recording
+ * that we sent the FIN bit.
+ *
+ * We can't just blindly clear the FIN bit,
+ * because if we don't have any more data
+ * to send then the probe will be the FIN
+ * itself.
+ */
+ if (off < so->so_snd.sb_cc)
+ flags &= ~TH_FIN;
+ win = 1;
+ } else {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+
+ len = min(so->so_snd.sb_cc, win) - off;
+
+ if (len < 0) {
+ /*
+ * If FIN has been sent but not acked,
+ * but we haven't been called to retransmit,
+ * len will be -1. Otherwise, window shrank
+ * after we sent into it. If window shrank to 0,
+ * cancel pending retransmit and pull snd_nxt
+ * back to (closed) window. We will enter persist
+ * state below. If the window didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0) {
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->snd_nxt = tp->snd_una;
+ }
+ }
+
+ if (len > tp->t_maxseg) {
+ len = tp->t_maxseg;
+ sendalot = 1;
+ }
+ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+ flags &= ~TH_FIN;
+
+ win = sbspace(&so->so_rcv);
+
+ /*
+ * Sender silly window avoidance. If connection is idle
+ * and can send all data, a maximum segment,
+ * at least a maximum default-size segment do it,
+ * or are forced, do it; otherwise don't bother.
+ * If peer's buffer is tiny, then send
+ * when window is at least half open.
+ * If retransmitting (possibly after persist timer forced us
+ * to send into a small window), then must resend.
+ */
+ if (len) {
+ if (len == tp->t_maxseg)
+ goto send;
+ if ((1 || idle || tp->t_flags & TF_NODELAY) &&
+ len + off >= so->so_snd.sb_cc)
+ goto send;
+ if (tp->t_force)
+ goto send;
+ if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
+ goto send;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ goto send;
+ }
+
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input). If the difference is at least two
+ * max size segments, or at least 50% of the maximum possible
+ * window, then want to send a window update to peer.
+ */
+ if (win > 0) {
+ /*
+ * "adv" is the amount we can increase the window,
+ * taking into account that we are limited by
+ * TCP_MAXWIN << tp->rcv_scale.
+ */
+ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+ (tp->rcv_adv - tp->rcv_nxt);
+
+ if (adv >= (long) (2 * tp->t_maxseg))
+ goto send;
+ if (2 * adv >= (long) so->so_rcv.sb_datalen)
+ goto send;
+ }
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (tp->t_flags & TF_ACKNOW)
+ goto send;
+ if (flags & (TH_SYN|TH_RST))
+ goto send;
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ goto send;
+ /*
+ * If our state indicates that FIN should be sent
+ * and we have not yet done so, or we're retransmitting the FIN,
+ * then we need to send.
+ */
+ if (flags & TH_FIN &&
+ ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+ goto send;
+
+ /*
+ * TCP window updates are not reliable, rather a polling protocol
+ * using ``persist'' packets is used to insure receipt of window
+ * updates. The three ``states'' for the output side are:
+ * idle not doing retransmits or persists
+ * persisting to move a small or zero window
+ * (re)transmitting and thereby not persisting
+ *
+ * tp->t_timer[TCPT_PERSIST]
+ * is set when we are in persist state.
+ * tp->t_force
+ * is set when we are called to send a persist packet.
+ * tp->t_timer[TCPT_REXMT]
+ * is set when we are retransmitting
+ * The output side is idle when both timers are zero.
+ *
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise force out a byte.
+ */
+ if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->t_timer[TCPT_PERSIST] == 0) {
+ tp->t_rxtshift = 0;
+ tcp_setpersist(tp);
+ }
+
+ /*
+ * No reason to send a segment, just return.
+ */
+ return (0);
+
+send:
+ /*
+ * Before ESTABLISHED, force sending of initial options
+ * unless TCP set not to do any options.
+ * NOTE: we assume that the IP/TCP header plus TCP options
+ * always fit in a single mbuf, leaving room for a maximum
+ * link header, i.e.
+ * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+ */
+ optlen = 0;
+ hdrlen = sizeof (struct tcpiphdr);
+ if (flags & TH_SYN) {
+ tp->snd_nxt = tp->iss;
+ if ((tp->t_flags & TF_NOOPT) == 0) {
+ uint16_t mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = htons((uint16_t) tcp_mss(tp, 0));
+ memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
+ optlen = 4;
+ }
+ }
+
+ hdrlen += optlen;
+
+ /*
+ * Adjust data length if insertion of options will
+ * bump the packet length beyond the t_maxseg length.
+ */
+ if (len > tp->t_maxseg - optlen) {
+ len = tp->t_maxseg - optlen;
+ sendalot = 1;
+ }
+
+ /*
+ * Grab a header mbuf, attaching a copy of data to
+ * be transmitted, and initialize the header from
+ * the template for sends on this connection.
+ */
+ if (len) {
+ m = m_get(so->slirp);
+ if (m == NULL) {
+ error = 1;
+ goto out;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ m->m_len = hdrlen;
+
+ sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
+ m->m_len += len;
+
+ /*
+ * If we're sending everything we've got, set PUSH.
+ * (This will keep happy those implementations which only
+ * give data to the user when a buffer fills or
+ * a PUSH comes in.)
+ */
+ if (off + len == so->so_snd.sb_cc)
+ flags |= TH_PUSH;
+ } else {
+ m = m_get(so->slirp);
+ if (m == NULL) {
+ error = 1;
+ goto out;
+ }
+ m->m_data += IF_MAXLINKHDR;
+ m->m_len = hdrlen;
+ }
+
+ ti = mtod(m, struct tcpiphdr *);
+
+ memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
+
+ /*
+ * Fill in fields, remembering maximum advertised
+ * window for use in delaying messages about window sizes.
+ * If resending a FIN, be sure not to use a new sequence number.
+ */
+ if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
+ tp->snd_nxt == tp->snd_max)
+ tp->snd_nxt--;
+ /*
+ * If we are doing retransmissions, then snd_nxt will
+ * not reflect the first unsent octet. For ACK only
+ * packets, we do not want the sequence number of the
+ * retransmitted packet, we want the sequence number
+ * of the next unsent octet. So, if there is no data
+ * (and no SYN or FIN), use snd_max instead of snd_nxt
+ * when filling in ti_seq. But if we are in persist
+ * state, snd_max might reflect one byte beyond the
+ * right edge of the window, so use snd_nxt in that
+ * case, since we know we aren't doing a retransmission.
+ * (retransmit and persist are mutually exclusive...)
+ */
+ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ ti->ti_seq = htonl(tp->snd_nxt);
+ else
+ ti->ti_seq = htonl(tp->snd_max);
+ ti->ti_ack = htonl(tp->rcv_nxt);
+ if (optlen) {
+ memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
+ ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
+ }
+ ti->ti_flags = flags;
+ /*
+ * Calculate receive window. Don't shrink window,
+ * but avoid silly window syndrome.
+ */
+ if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg)
+ win = 0;
+ if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+ win = (long)TCP_MAXWIN << tp->rcv_scale;
+ if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+ win = (long)(tp->rcv_adv - tp->rcv_nxt);
+ ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale));
+
+ if (SEQ_GT(tp->snd_up, tp->snd_una)) {
+ ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+ ti->ti_flags |= TH_URG;
+ } else
+ /*
+ * If no urgent pointer to send, then we pull
+ * the urgent pointer to the left edge of the send window
+ * so that it doesn't drift into the send window on sequence
+ * number wraparound.
+ */
+ tp->snd_up = tp->snd_una; /* drag it along */
+
+ /*
+ * Put TCP length in extended header, and then
+ * checksum extended header and data.
+ */
+ if (len + optlen)
+ ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) +
+ optlen + len));
+ ti->ti_sum = cksum(m, (int)(hdrlen + len));
+
+ /*
+ * In transmit state, time the transmission and arrange for
+ * the retransmit. In persist state, just set snd_max.
+ */
+ if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+ tcp_seq startseq = tp->snd_nxt;
+
+ /*
+ * Advance snd_nxt over sequence space of this segment.
+ */
+ if (flags & (TH_SYN|TH_FIN)) {
+ if (flags & TH_SYN)
+ tp->snd_nxt++;
+ if (flags & TH_FIN) {
+ tp->snd_nxt++;
+ tp->t_flags |= TF_SENTFIN;
+ }
+ }
+ tp->snd_nxt += len;
+ if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
+ tp->snd_max = tp->snd_nxt;
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tp->t_rtt == 0) {
+ tp->t_rtt = 1;
+ tp->t_rtseq = startseq;
+ }
+ }
+
+ /*
+ * Set retransmit timer if not currently set,
+ * and not doing an ack or a keep-alive probe.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if (tp->t_timer[TCPT_REXMT] == 0 &&
+ tp->snd_nxt != tp->snd_una) {
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ if (tp->t_timer[TCPT_PERSIST]) {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+ } else
+ if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+ tp->snd_max = tp->snd_nxt + len;
+
+ /*
+ * Fill in IP length and desired time to live and
+ * send to IP level. There should be a better way
+ * to handle ttl and tos; we could keep them in
+ * the template, but need a way to checksum without them.
+ */
+ m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+
+ {
+
+ ((struct ip *)ti)->ip_len = m->m_len;
+
+ ((struct ip *)ti)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ti)->ip_tos = so->so_iptos;
+
+ error = ip_output(so, m);
+ }
+ if (error) {
+out:
+ return (error);
+ }
+
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertised window.
+ * Any pending ACK has now been sent.
+ */
+ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+ tp->rcv_adv = tp->rcv_nxt + win;
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ if (sendalot)
+ goto again;
+
+ return (0);
+}
+
+void
+tcp_setpersist(struct tcpcb *tp)
+{
+ int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+ /*
+ * Start/restart persistence timer.
+ */
+ TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+ t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+ tp->t_rxtshift++;
+}
diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c
new file mode 100644
index 0000000..b661d26
--- /dev/null
+++ b/slirp/tcp_subr.c
@@ -0,0 +1,915 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
+ * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/* patchable/settable parameters for tcp */
+/* Don't do rfc1323 performance enhancements */
+#define TCP_DO_RFC1323 0
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init(Slirp *slirp)
+{
+ slirp->tcp_iss = 1; /* wrong */
+ slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb;
+ slirp->tcp_last_so = &slirp->tcb;
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+void
+tcp_template(struct tcpcb *tp)
+{
+ struct socket *so = tp->t_socket;
+ register struct tcpiphdr *n = &tp->t_template;
+
+ n->ti_mbuf = NULL;
+ n->ti_x1 = 0;
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = so->so_faddr;
+ n->ti_dst = so->so_laddr;
+ n->ti_sport = so->so_fport;
+ n->ti_dport = so->so_lport;
+
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
+ tcp_seq ack, tcp_seq seq, int flags)
+{
+ register int tlen;
+ int win = 0;
+
+ DEBUG_CALL("tcp_respond");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("ti = %lx", (long)ti);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("ack = %u", ack);
+ DEBUG_ARG("seq = %u", seq);
+ DEBUG_ARG("flags = %x", flags);
+
+ if (tp)
+ win = sbspace(&tp->t_socket->so_rcv);
+ if (m == NULL) {
+ if ((m = m_get(tp->t_socket->slirp)) == NULL)
+ return;
+ tlen = 0;
+ m->m_data += IF_MAXLINKHDR;
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ } else {
+ /*
+ * ti points into m so the next line is just making
+ * the mbuf point to ti
+ */
+ m->m_data = (caddr_t)ti;
+
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
+ xchg(ti->ti_dport, ti->ti_sport, uint16_t);
+#undef xchg
+ }
+ ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+
+ ti->ti_mbuf = NULL;
+ ti->ti_x1 = 0;
+ ti->ti_seq = htonl(seq);
+ ti->ti_ack = htonl(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale));
+ else
+ ti->ti_win = htons((uint16_t)win);
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+
+ if(flags & TH_RST)
+ ((struct ip *)ti)->ip_ttl = MAXTTL;
+ else
+ ((struct ip *)ti)->ip_ttl = IPDEFTTL;
+
+ (void) ip_output((struct socket *)0, m);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(struct socket *so)
+{
+ register struct tcpcb *tp;
+
+ tp = (struct tcpcb *)malloc(sizeof(*tp));
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+
+ memset((char *) tp, 0, sizeof(struct tcpcb));
+ tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
+ tp->t_maxseg = TCP_MSS;
+
+ tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_socket = so;
+
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = TCPTV_SRTTDFLT << 2;
+ tp->t_rttmin = TCPTV_MIN;
+
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->t_state = TCPS_CLOSED;
+
+ so->so_tcpcb = tp;
+
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
+{
+ DEBUG_CALL("tcp_drop");
+ DEBUG_ARG("tp = %lx", (long)tp);
+ DEBUG_ARG("errno = %d", errno);
+
+ if (TCPS_HAVERCVDSYN(tp->t_state)) {
+ tp->t_state = TCPS_CLOSED;
+ (void) tcp_output(tp);
+ }
+ return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(struct tcpcb *tp)
+{
+ register struct tcpiphdr *t;
+ struct socket *so = tp->t_socket;
+ Slirp *slirp = so->slirp;
+ register struct mbuf *m;
+
+ DEBUG_CALL("tcp_close");
+ DEBUG_ARG("tp = %lx", (long )tp);
+
+ /* free the reassembly queue, if any */
+ t = tcpfrag_list_first(tp);
+ while (!tcpfrag_list_end(t, tp)) {
+ t = tcpiphdr_next(t);
+ m = tcpiphdr_prev(t)->ti_mbuf;
+ remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
+ m_freem(m);
+ }
+ free(tp);
+ so->so_tcpcb = NULL;
+ /* clobber input socket cache if we're closing the cached connection */
+ if (so == slirp->tcp_last_so)
+ slirp->tcp_last_so = &slirp->tcb;
+ closesocket(so->s);
+ sbfree(&so->so_rcv);
+ sbfree(&so->so_snd);
+ sofree(so);
+ return ((struct tcpcb *)0);
+}
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+void
+tcp_sockclosed(struct tcpcb *tp)
+{
+
+ DEBUG_CALL("tcp_sockclosed");
+ DEBUG_ARG("tp = %lx", (long)tp);
+
+ switch (tp->t_state) {
+
+ case TCPS_CLOSED:
+ case TCPS_LISTEN:
+ case TCPS_SYN_SENT:
+ tp->t_state = TCPS_CLOSED;
+ tp = tcp_close(tp);
+ break;
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ tp->t_state = TCPS_FIN_WAIT_1;
+ break;
+
+ case TCPS_CLOSE_WAIT:
+ tp->t_state = TCPS_LAST_ACK;
+ break;
+ }
+ if (tp)
+ tcp_output(tp);
+}
+
+/*
+ * Connect to a host on the Internet
+ * Called by tcp_input
+ * Only do a connect, the tcp fields will be set in tcp_input
+ * return 0 if there's a result of the connect,
+ * else return -1 means we're still connecting
+ * The return value is almost always -1 since the socket is
+ * nonblocking. Connect returns after the SYN is sent, and does
+ * not wait for ACK+SYN.
+ */
+int tcp_fconnect(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+ int ret=0;
+
+ DEBUG_CALL("tcp_fconnect");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) {
+ int opt, s=so->s;
+ struct sockaddr_in addr;
+
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt ));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt ));
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ /* It's an alias */
+ if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+ if (get_dns_addr(&addr.sin_addr) < 0)
+ addr.sin_addr = loopback_addr;
+ } else {
+ addr.sin_addr = loopback_addr;
+ }
+ } else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
+ "addr.sin_addr.s_addr=%.16s\n",
+ ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+ /* We don't care what port we get */
+ ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
+
+ /*
+ * If it's not in progress, it failed, so we just return 0,
+ * without clearing SS_NOFDREF
+ */
+ soisfconnecting(so);
+ }
+
+ return(ret);
+}
+
+/*
+ * Accept the socket and connect to the local-host
+ *
+ * We have a problem. The correct thing to do would be
+ * to first connect to the local-host, and only if the
+ * connection is accepted, then do an accept() here.
+ * But, a) we need to know who's trying to connect
+ * to the socket to be able to SYN the local-host, and
+ * b) we are already connected to the foreign host by
+ * the time it gets to accept(), so... We simply accept
+ * here and SYN the local-host.
+ */
+void
+tcp_connect(struct socket *inso)
+{
+ Slirp *slirp = inso->slirp;
+ struct socket *so;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct tcpcb *tp;
+ int s, opt;
+
+ DEBUG_CALL("tcp_connect");
+ DEBUG_ARG("inso = %lx", (long)inso);
+
+ /*
+ * If it's an SS_ACCEPTONCE socket, no need to socreate()
+ * another socket, just use the accept() socket.
+ */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ /* FACCEPTONCE already have a tcpcb */
+ so = inso;
+ } else {
+ if ((so = socreate(slirp)) == NULL) {
+ /* If it failed, get rid of the pending connection */
+ closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
+ return;
+ }
+ if (tcp_attach(so) < 0) {
+ free(so); /* NOT sofree */
+ return;
+ }
+ so->so_laddr = inso->so_laddr;
+ so->so_lport = inso->so_lport;
+ }
+
+ (void) tcp_mss(sototcpcb(so), 0);
+
+ if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) {
+ tcp_close(sototcpcb(so)); /* This will sofree() as well */
+ return;
+ }
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
+ opt = 1;
+ setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int));
+
+ so->so_fport = addr.sin_port;
+ so->so_faddr = addr.sin_addr;
+ /* Translate connections from localhost to the real hostname */
+ if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = slirp->vhost_addr;
+
+ /* Close the accept() socket, set right state */
+ if (inso->so_state & SS_FACCEPTONCE) {
+ closesocket(so->s); /* If we only accept once, close the accept() socket */
+ so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
+ /* if it's not FACCEPTONCE, it's already NOFDREF */
+ }
+ so->s = s;
+ so->so_state |= SS_INCOMING;
+
+ so->so_iptos = tcp_tos(so);
+ tp = sototcpcb(so);
+
+ tcp_template(tp);
+
+ tp->t_state = TCPS_SYN_SENT;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = slirp->tcp_iss;
+ slirp->tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ tcp_output(tp);
+}
+
+/*
+ * Attach a TCPCB to a socket.
+ */
+int
+tcp_attach(struct socket *so)
+{
+ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
+ return -1;
+
+ insque(so, &so->slirp->tcb);
+
+ return 0;
+}
+
+/*
+ * Set the socket's type of service field
+ */
+static const struct tos_t tcptos[] = {
+ {0, 20, IPTOS_THROUGHPUT, 0}, /* ftp data */
+ {21, 21, IPTOS_LOWDELAY, EMU_FTP}, /* ftp control */
+ {0, 23, IPTOS_LOWDELAY, 0}, /* telnet */
+ {0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */
+ {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */
+ {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */
+ {0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */
+ {0, 543, IPTOS_LOWDELAY, 0}, /* klogin */
+ {0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */
+ {0, 6668, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC undernet */
+ {0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */
+ {0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */
+ {0, 0, 0, 0}
+};
+
+static struct emu_t *tcpemu = NULL;
+
+/*
+ * Return TOS according to the above table
+ */
+uint8_t
+tcp_tos(struct socket *so)
+{
+ int i = 0;
+ struct emu_t *emup;
+
+ while(tcptos[i].tos) {
+ if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) ||
+ (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) {
+ so->so_emu = tcptos[i].emu;
+ return tcptos[i].tos;
+ }
+ i++;
+ }
+
+ /* Nope, lets see if there's a user-added one */
+ for (emup = tcpemu; emup; emup = emup->next) {
+ if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) ||
+ (emup->lport && (ntohs(so->so_lport) == emup->lport))) {
+ so->so_emu = emup->emu;
+ return emup->tos;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Emulate programs that try and connect to us
+ * This includes ftp (the data connection is
+ * initiated by the server) and IRC (DCC CHAT and
+ * DCC SEND) for now
+ *
+ * NOTE: It's possible to crash SLiRP by sending it
+ * unstandard strings to emulate... if this is a problem,
+ * more checks are needed here
+ *
+ * XXX Assumes the whole command came in one packet
+ *
+ * XXX Some ftp clients will have their TOS set to
+ * LOWDELAY and so Nagel will kick in. Because of this,
+ * we'll get the first letter, followed by the rest, so
+ * we simply scan for ORT instead of PORT...
+ * DCC doesn't have this problem because there's other stuff
+ * in the packet before the DCC command.
+ *
+ * Return 1 if the mbuf m is still valid and should be
+ * sbappend()ed
+ *
+ * NOTE: if you return 0 you MUST m_free() the mbuf!
+ */
+int
+tcp_emu(struct socket *so, struct mbuf *m)
+{
+ Slirp *slirp = so->slirp;
+ u_int n1, n2, n3, n4, n5, n6;
+ char buff[257];
+ uint32_t laddr;
+ u_int lport;
+ char *bptr;
+
+ DEBUG_CALL("tcp_emu");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+
+ switch(so->so_emu) {
+ int x, i;
+
+ case EMU_IDENT:
+ /*
+ * Identification protocol as per rfc-1413
+ */
+
+ {
+ struct socket *tmpso;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct sbuf *so_rcv = &so->so_rcv;
+
+ memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+ so_rcv->sb_wptr += m->m_len;
+ so_rcv->sb_rptr += m->m_len;
+ m->m_data[m->m_len] = 0; /* NULL terminate */
+ if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) {
+ if (sscanf(so_rcv->sb_data, "%u%*[ ,]%u", &n1, &n2) == 2) {
+ HTONS(n1);
+ HTONS(n2);
+ /* n2 is the one on our host */
+ for (tmpso = slirp->tcb.so_next;
+ tmpso != &slirp->tcb;
+ tmpso = tmpso->so_next) {
+ if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr &&
+ tmpso->so_lport == n2 &&
+ tmpso->so_faddr.s_addr == so->so_faddr.s_addr &&
+ tmpso->so_fport == n1) {
+ if (getsockname(tmpso->s,
+ (struct sockaddr *)&addr, &addrlen) == 0)
+ n2 = ntohs(addr.sin_port);
+ break;
+ }
+ }
+ }
+ so_rcv->sb_cc = snprintf(so_rcv->sb_data,
+ so_rcv->sb_datalen,
+ "%d,%d\r\n", n1, n2);
+ so_rcv->sb_rptr = so_rcv->sb_data;
+ so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
+ }
+ m_free(m);
+ return 0;
+ }
+
+ case EMU_FTP: /* ftp */
+ *(m->m_data+m->m_len) = 0; /* NUL terminate for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {
+ /*
+ * Need to emulate the PORT command
+ */
+ x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr,
+ lport, SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
+ "ORT %d,%d,%d,%d,%d,%d\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+ return 1;
+ } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
+ /*
+ * Need to emulate the PASV response
+ */
+ x = sscanf(bptr, "27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256[^\177]",
+ &n1, &n2, &n3, &n4, &n5, &n6, buff);
+ if (x < 6)
+ return 1;
+
+ laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
+ lport = htons((n5 << 8) | (n6));
+
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr,
+ lport, SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ n6 = ntohs(so->so_fport);
+
+ n5 = (n6 >> 8) & 0xff;
+ n6 &= 0xff;
+
+ laddr = ntohl(so->so_faddr.s_addr);
+
+ n1 = ((laddr >> 24) & 0xff);
+ n2 = ((laddr >> 16) & 0xff);
+ n3 = ((laddr >> 8) & 0xff);
+ n4 = (laddr & 0xff);
+
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
+ "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
+ n1, n2, n3, n4, n5, n6, x==7?buff:"");
+
+ return 1;
+ }
+
+ return 1;
+
+ case EMU_KSH:
+ /*
+ * The kshell (Kerberos rsh) and shell services both pass
+ * a local port port number to carry signals to the server
+ * and stderr to the client. It is passed at the beginning
+ * of the connection as a NUL-terminated decimal ASCII string.
+ */
+ so->so_emu = 0;
+ for (lport = 0, i = 0; i < m->m_len-1; ++i) {
+ if (m->m_data[i] < '0' || m->m_data[i] > '9')
+ return 1; /* invalid number */
+ lport *= 10;
+ lport += m->m_data[i] - '0';
+ }
+ if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
+ (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
+ htons(lport), SS_FACCEPTONCE)) != NULL)
+ m->m_len = snprintf(m->m_data, m->m_hdr.mh_size, "%d",
+ ntohs(so->so_fport)) + 1;
+ return 1;
+
+ case EMU_IRC:
+ /*
+ * Need to emulate DCC CHAT, DCC SEND and DCC MOVE
+ */
+ *(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */
+ if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)
+ return 1;
+
+ /* The %256s is for the broken mIRC */
+ if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0,
+ htonl(laddr), htons(lport),
+ SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC CHAT chat %lu %u%c\n",
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), 1);
+ } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0,
+ htonl(laddr), htons(lport),
+ SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC SEND %s %lu %u %u%c\n", buff,
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+ if ((so = tcp_listen(slirp, INADDR_ANY, 0,
+ htonl(laddr), htons(lport),
+ SS_FACCEPTONCE)) == NULL) {
+ return 1;
+ }
+ m->m_len = bptr - m->m_data; /* Adjust length */
+ m->m_len += snprintf(bptr, m->m_hdr.mh_size,
+ "DCC MOVE %s %lu %u %u%c\n", buff,
+ (unsigned long)ntohl(so->so_faddr.s_addr),
+ ntohs(so->so_fport), n1, 1);
+ }
+ return 1;
+
+ case EMU_REALAUDIO:
+ /*
+ * RealAudio emulation - JP. We must try to parse the incoming
+ * data and try to find the two characters that contain the
+ * port number. Then we redirect an udp port and replace the
+ * number with the real port we got.
+ *
+ * The 1.0 beta versions of the player are not supported
+ * any more.
+ *
+ * A typical packet for player version 1.0 (release version):
+ *
+ * 0000:50 4E 41 00 05
+ * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 ........g.l.c..P
+ * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH
+ * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v
+ * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB
+ *
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * Now the port number 0x1BD7 is found at offset 0x04 of the
+ * second packet. This time we received five bytes first and
+ * then the rest. You never know how many bytes you get.
+ *
+ * A typical packet for player version 2.0 (beta):
+ *
+ * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA.............
+ * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .gux.c..Win2.0.0
+ * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/
+ * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas
+ * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B
+ *
+ * Port number 0x1BC1 is found at offset 0x0d.
+ *
+ * This is just a horrible switch statement. Variable ra tells
+ * us where we're going.
+ */
+
+ bptr = m->m_data;
+ while (bptr < m->m_data + m->m_len) {
+ u_short p;
+ static int ra = 0;
+ char ra_tbl[4];
+
+ ra_tbl[0] = 0x50;
+ ra_tbl[1] = 0x4e;
+ ra_tbl[2] = 0x41;
+ ra_tbl[3] = 0;
+
+ switch (ra) {
+ case 0:
+ case 2:
+ case 3:
+ if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 1:
+ /*
+ * We may get 0x50 several times, ignore them
+ */
+ if (*bptr == 0x50) {
+ ra = 1;
+ bptr++;
+ continue;
+ } else if (*bptr++ != ra_tbl[ra]) {
+ ra = 0;
+ continue;
+ }
+ break;
+
+ case 4:
+ /*
+ * skip version number
+ */
+ bptr++;
+ break;
+
+ case 5:
+ /*
+ * The difference between versions 1.0 and
+ * 2.0 is here. For future versions of
+ * the player this may need to be modified.
+ */
+ if (*(bptr + 1) == 0x02)
+ bptr += 8;
+ else
+ bptr += 4;
+ break;
+
+ case 6:
+ /* This is the field containing the port
+ * number that RA-player is listening to.
+ */
+ lport = (((u_char*)bptr)[0] << 8)
+ + ((u_char *)bptr)[1];
+ if (lport < 6970)
+ lport += 256; /* don't know why */
+ if (lport < 6970 || lport > 7170)
+ return 1; /* failed */
+
+ /* try to get udp port between 6970 - 7170 */
+ for (p = 6970; p < 7071; p++) {
+ if (udp_listen(slirp, INADDR_ANY,
+ htons(p),
+ so->so_laddr.s_addr,
+ htons(lport),
+ SS_FACCEPTONCE)) {
+ break;
+ }
+ }
+ if (p == 7071)
+ p = 0;
+ *(u_char *)bptr++ = (p >> 8) & 0xff;
+ *(u_char *)bptr = p & 0xff;
+ ra = 0;
+ return 1; /* port redirected, we're done */
+ break;
+
+ default:
+ ra = 0;
+ }
+ ra++;
+ }
+ return 1;
+
+ default:
+ /* Ooops, not emulated, won't call tcp_emu again */
+ so->so_emu = 0;
+ return 1;
+ }
+}
+
+/*
+ * Do misc. config of SLiRP while its running.
+ * Return 0 if this connections is to be closed, 1 otherwise,
+ * return 2 if this is a command-line connection
+ */
+int tcp_ctl(struct socket *so)
+{
+ Slirp *slirp = so->slirp;
+ struct sbuf *sb = &so->so_snd;
+ struct ex_list *ex_ptr;
+ int do_pty;
+
+ DEBUG_CALL("tcp_ctl");
+ DEBUG_ARG("so = %lx", (long )so);
+
+ if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
+ /* Check if it's pty_exec */
+ for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
+ if (ex_ptr->ex_fport == so->so_fport &&
+ so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
+ if (ex_ptr->ex_pty == 3) {
+ so->s = -1;
+ so->extra = (void *)ex_ptr->ex_exec;
+ return 1;
+ }
+ do_pty = ex_ptr->ex_pty;
+ DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec));
+ return fork_exec(so, ex_ptr->ex_exec, do_pty);
+ }
+ }
+ }
+ sb->sb_cc =
+ snprintf(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data),
+ "Error: No application configured.\r\n");
+ sb->sb_wptr += sb->sb_cc;
+ return 0;
+}
diff --git a/slirp/tcp_timer.c b/slirp/tcp_timer.c
new file mode 100644
index 0000000..6c5bb11
--- /dev/null
+++ b/slirp/tcp_timer.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
+ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
+ */
+
+#include <slirp.h>
+
+static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
+
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo(Slirp *slirp)
+{
+ register struct socket *so;
+ register struct tcpcb *tp;
+
+ DEBUG_CALL("tcp_fasttimo");
+
+ so = slirp->tcb.so_next;
+ if (so)
+ for (; so != &slirp->tcb; so = so->so_next)
+ if ((tp = (struct tcpcb *)so->so_tcpcb) &&
+ (tp->t_flags & TF_DELACK)) {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(tp);
+ }
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo(Slirp *slirp)
+{
+ register struct socket *ip, *ipnxt;
+ register struct tcpcb *tp;
+ register int i;
+
+ DEBUG_CALL("tcp_slowtimo");
+
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = slirp->tcb.so_next;
+ if (ip == NULL) {
+ return;
+ }
+ for (; ip != &slirp->tcb; ip = ipnxt) {
+ ipnxt = ip->so_next;
+ tp = sototcpcb(ip);
+ if (tp == NULL) {
+ continue;
+ }
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+ tcp_timers(tp,i);
+ if (ipnxt->so_prev != ip)
+ goto tpgone;
+ }
+ }
+ tp->t_idle++;
+ if (tp->t_rtt)
+ tp->t_rtt++;
+tpgone:
+ ;
+ }
+ slirp->tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
+ slirp->tcp_now++; /* for timestamps */
+}
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(struct tcpcb *tp)
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+ { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * TCP timer processing.
+ */
+static struct tcpcb *
+tcp_timers(register struct tcpcb *tp, int timer)
+{
+ register int rexmt;
+
+ DEBUG_CALL("tcp_timers");
+
+ switch (timer) {
+
+ /*
+ * 2 MSL timeout in shutdown went off. If we're closed but
+ * still waiting for peer to close and connection has been idle
+ * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+ * control block. Otherwise, check again in a bit.
+ */
+ case TCPT_2MSL:
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ tp->t_idle <= TCP_MAXIDLE)
+ tp->t_timer[TCPT_2MSL] = TCPTV_KEEPINTVL;
+ else
+ tp = tcp_close(tp);
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ case TCPT_REXMT:
+
+ /*
+ * XXXXX If a packet has timed out, then remove all the queued
+ * packets for that session.
+ */
+
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+ /*
+ * This is a hack to suit our terminal server here at the uni of canberra
+ * since they have trouble with zeroes... It usually lets them through
+ * unharmed, but under some conditions, it'll eat the zeros. If we
+ * keep retransmitting it, it'll keep eating the zeroes, so we keep
+ * retransmitting, and eventually the connection dies...
+ * (this only happens on incoming data)
+ *
+ * So, if we were gonna drop the connection from too many retransmits,
+ * don't... instead halve the t_maxseg, which might break up the NULLs and
+ * let them through
+ *
+ * *sigh*
+ */
+
+ tp->t_maxseg >>= 1;
+ if (tp->t_maxseg < 32) {
+ /*
+ * We tried our best, now the connection must die!
+ */
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tp = tcp_drop(tp, tp->t_softerror);
+ /* tp->t_softerror : ETIMEDOUT); */ /* XXX */
+ return (tp); /* XXX */
+ }
+
+ /*
+ * Set rxtshift to 6, which is still at the maximum
+ * backoff time
+ */
+ tp->t_rxtshift = 6;
+ }
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
+ }
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtt = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshold size.
+ * For a threshold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(tp);
+ break;
+
+ /*
+ * Persistence timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(tp);
+ tp->t_force = 0;
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case TCPT_KEEP:
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+
+ if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) {
+ if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE)
+ goto dropit;
+ /*
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
+ */
+ tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
+ } else
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
+ break;
+
+ dropit:
+ tp = tcp_drop(tp, 0);
+ break;
+ }
+
+ return (tp);
+}
diff --git a/slirp/tcp_timer.h b/slirp/tcp_timer.h
new file mode 100644
index 0000000..ff17914
--- /dev/null
+++ b/slirp/tcp_timer.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
+ * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
+ */
+
+#ifndef _TCP_TIMER_H_
+#define _TCP_TIMER_H_
+
+/*
+ * Definitions of the TCP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define TCPT_NTIMERS 4
+
+#define TCPT_REXMT 0 /* retransmit */
+#define TCPT_PERSIST 1 /* retransmit persistence */
+#define TCPT_KEEP 2 /* keep alive */
+#define TCPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection. Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection. We force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */
+
+#define TCPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
+#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
+#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEPCNT 8 /* max probes before drop */
+
+#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((tv) < (tvmin)) \
+ (tv) = (tvmin); \
+ else if ((tv) > (tvmax)) \
+ (tv) = (tvmax); \
+}
+
+extern const int tcp_backoff[];
+
+struct tcpcb;
+
+void tcp_fasttimo(Slirp *);
+void tcp_slowtimo(Slirp *);
+void tcp_canceltimers(struct tcpcb *);
+
+#endif
diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h
new file mode 100644
index 0000000..004193f
--- /dev/null
+++ b/slirp/tcp_var.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94
+ * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
+ */
+
+#ifndef _TCP_VAR_H_
+#define _TCP_VAR_H_
+
+#include "tcpip.h"
+#include "tcp_timer.h"
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb {
+ struct tcpiphdr *seg_next; /* sequencing queue */
+ struct tcpiphdr *seg_prev;
+ short t_state; /* state of this connection */
+ short t_timer[TCPT_NTIMERS]; /* tcp timers */
+ short t_rxtshift; /* log(2) of rexmt exp. backoff */
+ short t_rxtcur; /* current retransmit value */
+ short t_dupacks; /* consecutive dup acks recd */
+ u_short t_maxseg; /* maximum segment size */
+ char t_force; /* 1 if forcing out a byte */
+ u_short t_flags;
+#define TF_ACKNOW 0x0001 /* ack peer immediately */
+#define TF_DELACK 0x0002 /* ack, but try to delay it */
+#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
+#define TF_NOOPT 0x0008 /* don't use tcp options */
+#define TF_SENTFIN 0x0010 /* have sent FIN */
+#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
+#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
+#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
+#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
+#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
+
+ struct tcpiphdr t_template; /* static skeletal packet for xmit */
+
+ struct socket *t_socket; /* back pointer to socket */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+/* send sequence variables */
+ tcp_seq snd_una; /* send unacknowledged */
+ tcp_seq snd_nxt; /* send next */
+ tcp_seq snd_up; /* send urgent pointer */
+ tcp_seq snd_wl1; /* window update seg seq number */
+ tcp_seq snd_wl2; /* window update seg ack number */
+ tcp_seq iss; /* initial send sequence number */
+ uint32_t snd_wnd; /* send window */
+/* receive sequence variables */
+ uint32_t rcv_wnd; /* receive window */
+ tcp_seq rcv_nxt; /* receive next */
+ tcp_seq rcv_up; /* receive urgent pointer */
+ tcp_seq irs; /* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+/* receive variables */
+ tcp_seq rcv_adv; /* advertised window */
+/* retransmit variables */
+ tcp_seq snd_max; /* highest sequence number sent;
+ * used to recognize retransmits
+ */
+/* congestion control (for slow start, source quench, retransmit after loss) */
+ uint32_t snd_cwnd; /* congestion-controlled window */
+ uint32_t snd_ssthresh; /* snd_cwnd size threshold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ short t_idle; /* inactivity time */
+ short t_rtt; /* round trip time */
+ tcp_seq t_rtseq; /* sequence number being timed */
+ short t_srtt; /* smoothed round-trip time */
+ short t_rttvar; /* variance in round-trip time */
+ u_short t_rttmin; /* minimum rtt allowed */
+ uint32_t max_sndwnd; /* largest window peer has offered */
+
+/* out-of-band data */
+ char t_oobflags; /* have some */
+ char t_iobc; /* input character */
+#define TCPOOB_HAVEDATA 0x01
+#define TCPOOB_HADDATA 0x02
+ short t_softerror; /* possible error not yet reported */
+
+/* RFC 1323 variables */
+ u_char snd_scale; /* window scaling for send window */
+ u_char rcv_scale; /* window scaling for recv window */
+ u_char request_r_scale; /* pending window scaling */
+ u_char requested_s_scale;
+ uint32_t ts_recent; /* timestamp echo data */
+ uint32_t ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+
+};
+
+#define sototcpcb(so) ((so)->so_tcpcb)
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
+#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
+#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
+#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define TCP_REXMTVAL(tp) \
+ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+#endif
diff --git a/slirp/tcpip.h b/slirp/tcpip.h
new file mode 100644
index 0000000..7974ce3
--- /dev/null
+++ b/slirp/tcpip.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcpip.h 8.1 (Berkeley) 6/10/93
+ * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
+ */
+
+#ifndef _TCPIP_H_
+#define _TCPIP_H_
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr {
+ struct ipovly ti_i; /* overlaid ip structure */
+ struct tcphdr ti_t; /* tcp header */
+};
+#define ti_mbuf ti_i.ih_mbuf.mptr
+#define ti_x1 ti_i.ih_x1
+#define ti_pr ti_i.ih_pr
+#define ti_len ti_i.ih_len
+#define ti_src ti_i.ih_src
+#define ti_dst ti_i.ih_dst
+#define ti_sport ti_t.th_sport
+#define ti_dport ti_t.th_dport
+#define ti_seq ti_t.th_seq
+#define ti_ack ti_t.th_ack
+#define ti_x2 ti_t.th_x2
+#define ti_off ti_t.th_off
+#define ti_flags ti_t.th_flags
+#define ti_win ti_t.th_win
+#define ti_sum ti_t.th_sum
+#define ti_urp ti_t.th_urp
+
+#define tcpiphdr2qlink(T) ((struct qlink*)(((char*)(T)) - sizeof(struct qlink)))
+#define qlink2tcpiphdr(Q) ((struct tcpiphdr*)(((char*)(Q)) + sizeof(struct qlink)))
+#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next)
+#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev)
+#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next)
+#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
+#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
+
+/*
+ * Just a clean way to get to the first byte
+ * of the packet
+ */
+struct tcpiphdr_2 {
+ struct tcpiphdr dummy;
+ char first_char;
+};
+
+#endif
diff --git a/slirp/tftp.c b/slirp/tftp.c
new file mode 100644
index 0000000..1821648
--- /dev/null
+++ b/slirp/tftp.c
@@ -0,0 +1,422 @@
+/*
+ * tftp.c - a simple, read-only tftp server for qemu
+ *
+ * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <slirp.h>
+#include "qemu-common.h"
+
+static inline int tftp_session_in_use(struct tftp_session *spt)
+{
+ return (spt->slirp != NULL);
+}
+
+static inline void tftp_session_update(struct tftp_session *spt)
+{
+ spt->timestamp = curtime;
+}
+
+static void tftp_session_terminate(struct tftp_session *spt)
+{
+ qemu_free(spt->filename);
+ spt->slirp = NULL;
+}
+
+static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
+{
+ struct tftp_session *spt;
+ int k;
+
+ for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+ spt = &slirp->tftp_sessions[k];
+
+ if (!tftp_session_in_use(spt))
+ goto found;
+
+ /* sessions time out after 5 inactive seconds */
+ if ((int)(curtime - spt->timestamp) > 5000) {
+ qemu_free(spt->filename);
+ goto found;
+ }
+ }
+
+ return -1;
+
+ found:
+ memset(spt, 0, sizeof(*spt));
+ memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
+ spt->client_port = tp->udp.uh_sport;
+ spt->slirp = slirp;
+
+ tftp_session_update(spt);
+
+ return k;
+}
+
+static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
+{
+ struct tftp_session *spt;
+ int k;
+
+ for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+ spt = &slirp->tftp_sessions[k];
+
+ if (tftp_session_in_use(spt)) {
+ if (!memcmp(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip))) {
+ if (spt->client_port == tp->udp.uh_sport) {
+ return k;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr,
+ uint8_t *buf, int len)
+{
+ int fd;
+ int bytes_read = 0;
+
+ fd = open(spt->filename, O_RDONLY | O_BINARY);
+
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (len) {
+ lseek(fd, block_nr * 512, SEEK_SET);
+
+ bytes_read = read(fd, buf, len);
+ }
+
+ close(fd);
+
+ return bytes_read;
+}
+
+static int tftp_send_oack(struct tftp_session *spt,
+ const char *key, uint32_t value,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int n = 0;
+
+ m = m_get(spt->slirp);
+
+ if (!m)
+ return -1;
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_OACK);
+ n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
+ key) + 1;
+ n += snprintf((char *)tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
+ value) + 1;
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ m->m_len = sizeof(struct tftp_t) - 514 + n -
+ sizeof(struct ip) - sizeof(struct udphdr);
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ return 0;
+}
+
+static void tftp_send_error(struct tftp_session *spt,
+ uint16_t errorcode, const char *msg,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+
+ m = m_get(spt->slirp);
+
+ if (!m) {
+ goto out;
+ }
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_ERROR);
+ tp->x.tp_error.tp_error_code = htons(errorcode);
+ pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg);
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+out:
+ tftp_session_terminate(spt);
+}
+
+static int tftp_send_data(struct tftp_session *spt,
+ uint16_t block_nr,
+ struct tftp_t *recv_tp)
+{
+ struct sockaddr_in saddr, daddr;
+ struct mbuf *m;
+ struct tftp_t *tp;
+ int nobytes;
+
+ if (block_nr < 1) {
+ return -1;
+ }
+
+ m = m_get(spt->slirp);
+
+ if (!m) {
+ return -1;
+ }
+
+ memset(m->m_data, 0, m->m_size);
+
+ m->m_data += IF_MAXLINKHDR;
+ tp = (void *)m->m_data;
+ m->m_data += sizeof(struct udpiphdr);
+
+ tp->tp_op = htons(TFTP_DATA);
+ tp->x.tp_data.tp_block_nr = htons(block_nr);
+
+ saddr.sin_addr = recv_tp->ip.ip_dst;
+ saddr.sin_port = recv_tp->udp.uh_dport;
+
+ daddr.sin_addr = spt->client_ip;
+ daddr.sin_port = spt->client_port;
+
+ nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
+
+ if (nobytes < 0) {
+ m_free(m);
+
+ /* send "file not found" error back */
+
+ tftp_send_error(spt, 1, "File not found", tp);
+
+ return -1;
+ }
+
+ m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
+ sizeof(struct ip) - sizeof(struct udphdr);
+
+ udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+ if (nobytes == 512) {
+ tftp_session_update(spt);
+ }
+ else {
+ tftp_session_terminate(spt);
+ }
+
+ return 0;
+}
+
+static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
+{
+ struct tftp_session *spt;
+ int s, k;
+ size_t prefix_len;
+ char *req_fname;
+
+ /* check if a session already exists and if so terminate it */
+ s = tftp_session_find(slirp, tp);
+ if (s >= 0) {
+ tftp_session_terminate(&slirp->tftp_sessions[s]);
+ }
+
+ s = tftp_session_allocate(slirp, tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ spt = &slirp->tftp_sessions[s];
+
+ /* unspecifed prefix means service disabled */
+ if (!slirp->tftp_prefix) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ /* skip header fields */
+ k = 0;
+ pktlen -= ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
+
+ /* prepend tftp_prefix */
+ prefix_len = strlen(slirp->tftp_prefix);
+ spt->filename = qemu_malloc(prefix_len + TFTP_FILENAME_MAX + 2);
+ memcpy(spt->filename, slirp->tftp_prefix, prefix_len);
+ spt->filename[prefix_len] = '/';
+
+ /* get name */
+ req_fname = spt->filename + prefix_len + 1;
+
+ while (1) {
+ if (k >= TFTP_FILENAME_MAX || k >= pktlen) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+ req_fname[k] = (char)tp->x.tp_buf[k];
+ if (req_fname[k++] == '\0') {
+ break;
+ }
+ }
+
+ /* check mode */
+ if ((pktlen - k) < 6) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ if (strcasecmp((const char *)&tp->x.tp_buf[k], "octet") != 0) {
+ tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
+ return;
+ }
+
+ k += 6; /* skipping octet */
+
+ /* do sanity checks on the filename */
+ if (!strncmp(req_fname, "../", 3) ||
+ req_fname[strlen(req_fname) - 1] == '/' ||
+ strstr(req_fname, "/../")) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ /* check if the file exists */
+ if (tftp_read_data(spt, 0, NULL, 0) < 0) {
+ tftp_send_error(spt, 1, "File not found", tp);
+ return;
+ }
+
+ if (tp->x.tp_buf[pktlen - 1] != 0) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ while (k < pktlen) {
+ const char *key, *value;
+
+ key = (const char *)&tp->x.tp_buf[k];
+ k += strlen(key) + 1;
+
+ if (k >= pktlen) {
+ tftp_send_error(spt, 2, "Access violation", tp);
+ return;
+ }
+
+ value = (const char *)&tp->x.tp_buf[k];
+ k += strlen(value) + 1;
+
+ if (strcasecmp(key, "tsize") == 0) {
+ int tsize = atoi(value);
+ struct stat stat_p;
+
+ if (tsize == 0) {
+ if (stat(spt->filename, &stat_p) == 0)
+ tsize = stat_p.st_size;
+ else {
+ tftp_send_error(spt, 1, "File not found", tp);
+ return;
+ }
+ }
+
+ tftp_send_oack(spt, "tsize", tsize, tp);
+ return;
+ }
+ }
+
+ tftp_send_data(spt, 1, tp);
+}
+
+static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
+{
+ int s;
+
+ s = tftp_session_find(slirp, tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ if (tftp_send_data(&slirp->tftp_sessions[s],
+ ntohs(tp->x.tp_data.tp_block_nr) + 1,
+ tp) < 0) {
+ return;
+ }
+}
+
+static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)
+{
+ int s;
+
+ s = tftp_session_find(slirp, tp);
+
+ if (s < 0) {
+ return;
+ }
+
+ tftp_session_terminate(&slirp->tftp_sessions[s]);
+}
+
+void tftp_input(struct mbuf *m)
+{
+ struct tftp_t *tp = (struct tftp_t *)m->m_data;
+
+ switch(ntohs(tp->tp_op)) {
+ case TFTP_RRQ:
+ tftp_handle_rrq(m->slirp, tp, m->m_len);
+ break;
+
+ case TFTP_ACK:
+ tftp_handle_ack(m->slirp, tp, m->m_len);
+ break;
+
+ case TFTP_ERROR:
+ tftp_handle_error(m->slirp, tp, m->m_len);
+ break;
+ }
+}
diff --git a/slirp/tftp.h b/slirp/tftp.h
new file mode 100644
index 0000000..b9f0847
--- /dev/null
+++ b/slirp/tftp.h
@@ -0,0 +1,43 @@
+/* tftp defines */
+
+#define TFTP_SESSIONS_MAX 3
+
+#define TFTP_SERVER 69
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+#define TFTP_FILENAME_MAX 512
+
+struct tftp_t {
+ struct ip ip;
+ struct udphdr udp;
+ uint16_t tp_op;
+ union {
+ struct {
+ uint16_t tp_block_nr;
+ uint8_t tp_buf[512];
+ } tp_data;
+ struct {
+ uint16_t tp_error_code;
+ uint8_t tp_msg[512];
+ } tp_error;
+ uint8_t tp_buf[512 + 2];
+ } x;
+};
+
+struct tftp_session {
+ Slirp *slirp;
+ char *filename;
+
+ struct in_addr client_ip;
+ uint16_t client_port;
+
+ int timestamp;
+};
+
+void tftp_input(struct mbuf *m);
diff --git a/slirp/udp.c b/slirp/udp.c
new file mode 100644
index 0000000..02b3793
--- /dev/null
+++ b/slirp/udp.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
+ * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+static uint8_t udp_tos(struct socket *so);
+
+void
+udp_init(Slirp *slirp)
+{
+ slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb;
+ slirp->udp_last_so = &slirp->udb;
+}
+/* m->m_data points at ip packet header
+ * m->m_len length ip packet
+ * ip->ip_len length data (IPDU)
+ */
+void
+udp_input(register struct mbuf *m, int iphlen)
+{
+ Slirp *slirp = m->slirp;
+ register struct ip *ip;
+ register struct udphdr *uh;
+ int len;
+ struct ip save_ip;
+ struct socket *so;
+
+ DEBUG_CALL("udp_input");
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("iphlen = %d", iphlen);
+
+ /*
+ * Strip IP options, if any; should skip this,
+ * make available to user, and use on returned packets,
+ * but we don't yet have a way to check the checksum
+ * with options still present.
+ */
+ if(iphlen > sizeof(struct ip)) {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ */
+ ip = mtod(m, struct ip *);
+ uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = ntohs((uint16_t)uh->uh_ulen);
+
+ if (ip->ip_len != len) {
+ if (len > ip->ip_len) {
+ goto bad;
+ }
+ m_adj(m, len - ip->ip_len);
+ ip->ip_len = len;
+ }
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (uh->uh_sum) {
+ memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr));
+ ((struct ipovly *)ip)->ih_x1 = 0;
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+ if(cksum(m, len + sizeof(struct ip))) {
+ goto bad;
+ }
+ }
+
+ /*
+ * handle DHCP/BOOTP
+ */
+ if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
+ bootp_input(m);
+ goto bad;
+ }
+
+ if (slirp->restricted) {
+ goto bad;
+ }
+
+ /*
+ * handle TFTP
+ */
+ if (ntohs(uh->uh_dport) == TFTP_SERVER) {
+ tftp_input(m);
+ goto bad;
+ }
+
+ /*
+ * Locate pcb for datagram.
+ */
+ so = slirp->udp_last_so;
+ if (so->so_lport != uh->uh_sport ||
+ so->so_laddr.s_addr != ip->ip_src.s_addr) {
+ struct socket *tmp;
+
+ for (tmp = slirp->udb.so_next; tmp != &slirp->udb;
+ tmp = tmp->so_next) {
+ if (tmp->so_lport == uh->uh_sport &&
+ tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
+ so = tmp;
+ break;
+ }
+ }
+ if (tmp == &slirp->udb) {
+ so = NULL;
+ } else {
+ slirp->udp_last_so = so;
+ }
+ }
+
+ if (so == NULL) {
+ /*
+ * If there's no socket for this packet,
+ * create one
+ */
+ so = socreate(slirp);
+ if (!so) {
+ goto bad;
+ }
+ if(udp_attach(so) == -1) {
+ DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
+ errno,strerror(errno)));
+ sofree(so);
+ goto bad;
+ }
+
+ /*
+ * Setup fields
+ */
+ so->so_laddr = ip->ip_src;
+ so->so_lport = uh->uh_sport;
+
+ if ((so->so_iptos = udp_tos(so)) == 0)
+ so->so_iptos = ip->ip_tos;
+
+ /*
+ * XXXXX Here, check if it's in udpexec_list,
+ * and if it is, do the fork_exec() etc.
+ */
+ }
+
+ so->so_faddr = ip->ip_dst; /* XXX */
+ so->so_fport = uh->uh_dport; /* XXX */
+
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+
+ /*
+ * Now we sendto() the packet.
+ */
+ if(sosendto(so,m) == -1) {
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
+ icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
+ }
+
+ m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
+
+ /* restore the orig mbuf packet */
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip=save_ip;
+ so->so_m=m; /* ICMP backup */
+
+ return;
+bad:
+ m_freem(m);
+ return;
+}
+
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos)
+{
+ register struct udpiphdr *ui;
+ int error = 0;
+
+ DEBUG_CALL("udp_output");
+ DEBUG_ARG("so = %lx", (long)so);
+ DEBUG_ARG("m = %lx", (long)m);
+ DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
+ DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
+
+ /*
+ * Adjust for header
+ */
+ m->m_data -= sizeof(struct udpiphdr);
+ m->m_len += sizeof(struct udpiphdr);
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+ ui->ui_x1 = 0;
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = htons(m->m_len - sizeof(struct ip));
+ /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
+ ui->ui_src = saddr->sin_addr;
+ ui->ui_dst = daddr->sin_addr;
+ ui->ui_sport = saddr->sin_port;
+ ui->ui_dport = daddr->sin_port;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if ((ui->ui_sum = cksum(m, m->m_len)) == 0)
+ ui->ui_sum = 0xffff;
+ ((struct ip *)ui)->ip_len = m->m_len;
+
+ ((struct ip *)ui)->ip_ttl = IPDEFTTL;
+ ((struct ip *)ui)->ip_tos = iptos;
+
+ error = ip_output(so, m);
+
+ return (error);
+}
+
+int udp_output(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *addr)
+
+{
+ Slirp *slirp = so->slirp;
+ struct sockaddr_in saddr, daddr;
+
+ saddr = *addr;
+ if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+ slirp->vnetwork_addr.s_addr) {
+ uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
+
+ if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
+ saddr.sin_addr = slirp->vhost_addr;
+ } else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
+ so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
+ saddr.sin_addr = so->so_faddr;
+ }
+ }
+ daddr.sin_addr = so->so_laddr;
+ daddr.sin_port = so->so_lport;
+
+ return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
+}
+
+int
+udp_attach(struct socket *so)
+{
+ if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) {
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so, &so->slirp->udb);
+ }
+ return(so->s);
+}
+
+void
+udp_detach(struct socket *so)
+{
+ closesocket(so->s);
+ sofree(so);
+}
+
+static const struct tos_t udptos[] = {
+ {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
+ {0, 0, 0, 0}
+};
+
+static uint8_t
+udp_tos(struct socket *so)
+{
+ int i = 0;
+
+ while(udptos[i].tos) {
+ if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
+ (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
+ so->so_emu = udptos[i].emu;
+ return udptos[i].tos;
+ }
+ i++;
+ }
+
+ return 0;
+}
+
+struct socket *
+udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
+ u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1;
+
+ so = socreate(slirp);
+ if (!so) {
+ return NULL;
+ }
+ so->s = qemu_socket(AF_INET,SOCK_DGRAM,0);
+ so->so_expire = curtime + SO_EXPIRE;
+ insque(so, &slirp->udb);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = haddr;
+ addr.sin_port = hport;
+
+ if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
+ udp_detach(so);
+ return NULL;
+ }
+ setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
+
+ getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ if (addr.sin_addr.s_addr == 0 ||
+ addr.sin_addr.s_addr == loopback_addr.s_addr) {
+ so->so_faddr = slirp->vhost_addr;
+ } else {
+ so->so_faddr = addr.sin_addr;
+ }
+ so->so_lport = lport;
+ so->so_laddr.s_addr = laddr;
+ if (flags != SS_FACCEPTONCE)
+ so->so_expire = 0;
+
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_ISFCONNECTED | flags;
+
+ return so;
+}
diff --git a/slirp/udp.h b/slirp/udp.h
new file mode 100644
index 0000000..9b5c3cf
--- /dev/null
+++ b/slirp/udp.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
+ */
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#define UDP_TTL 0x60
+#define UDP_UDPDATALEN 16192
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+#define ui_mbuf ui_i.ih_mbuf.mptr
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
+#define UDPCTL_MAXID 2
+
+struct mbuf;
+
+void udp_init(Slirp *);
+void udp_input(register struct mbuf *, int);
+int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
+int udp_attach(struct socket *);
+void udp_detach(struct socket *);
+struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
+ int);
+int udp_output2(struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos);
+#endif
diff --git a/softmmu-semi.h b/softmmu-semi.h
new file mode 100644
index 0000000..79278cc
--- /dev/null
+++ b/softmmu-semi.h
@@ -0,0 +1,70 @@
+/*
+ * Helper routines to provide target memory access for semihosting
+ * syscalls in system emulation mode.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+
+static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
+{
+ uint32_t val;
+
+ cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 0);
+ return tswap32(val);
+}
+static inline uint32_t softmmu_tget8(CPUState *env, uint32_t addr)
+{
+ uint8_t val;
+
+ cpu_memory_rw_debug(env, addr, &val, 1, 0);
+ return val;
+}
+
+#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; })
+#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; })
+#define get_user_ual(arg, p) get_user_u32(arg, p)
+
+static inline void softmmu_tput32(CPUState *env, uint32_t addr, uint32_t val)
+{
+ val = tswap32(val);
+ cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 1);
+}
+#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; })
+#define put_user_ual(arg, p) put_user_u32(arg, p)
+
+static void *softmmu_lock_user(CPUState *env, uint32_t addr, uint32_t len,
+ int copy)
+{
+ uint8_t *p;
+ /* TODO: Make this something that isn't fixed size. */
+ p = malloc(len);
+ if (copy)
+ cpu_memory_rw_debug(env, addr, p, len, 0);
+ return p;
+}
+#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy)
+static char *softmmu_lock_user_string(CPUState *env, uint32_t addr)
+{
+ char *p;
+ char *s;
+ uint8_t c;
+ /* TODO: Make this something that isn't fixed size. */
+ s = p = malloc(1024);
+ do {
+ cpu_memory_rw_debug(env, addr, &c, 1, 0);
+ addr++;
+ *(p++) = c;
+ } while (c);
+ return s;
+}
+#define lock_user_string(p) softmmu_lock_user_string(env, p)
+static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
+ target_ulong len)
+{
+ if (len)
+ cpu_memory_rw_debug(env, addr, p, len, 1);
+ free(p);
+}
+#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
diff --git a/softmmu_defs.h b/softmmu_defs.h
new file mode 100644
index 0000000..e38bb75
--- /dev/null
+++ b/softmmu_defs.h
@@ -0,0 +1,22 @@
+#ifndef SOFTMMU_DEFS_H
+#define SOFTMMU_DEFS_H
+
+uint8_t REGPARM __ldb_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stb_mmu(target_ulong addr, uint8_t val, int mmu_idx);
+uint16_t REGPARM __ldw_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stw_mmu(target_ulong addr, uint16_t val, int mmu_idx);
+uint32_t REGPARM __ldl_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stl_mmu(target_ulong addr, uint32_t val, int mmu_idx);
+uint64_t REGPARM __ldq_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stq_mmu(target_ulong addr, uint64_t val, int mmu_idx);
+
+uint8_t REGPARM __ldb_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stb_cmmu(target_ulong addr, uint8_t val, int mmu_idx);
+uint16_t REGPARM __ldw_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stw_cmmu(target_ulong addr, uint16_t val, int mmu_idx);
+uint32_t REGPARM __ldl_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stl_cmmu(target_ulong addr, uint32_t val, int mmu_idx);
+uint64_t REGPARM __ldq_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stq_cmmu(target_ulong addr, uint64_t val, int mmu_idx);
+
+#endif
diff --git a/softmmu_exec.h b/softmmu_exec.h
new file mode 100644
index 0000000..28d1d53
--- /dev/null
+++ b/softmmu_exec.h
@@ -0,0 +1,153 @@
+/* Common softmmu definitions and inline routines. */
+
+/* XXX: find something cleaner.
+ * Furthermore, this is false for 64 bits targets
+ */
+#define ldul_user ldl_user
+#define ldul_kernel ldl_kernel
+#define ldul_hypv ldl_hypv
+#define ldul_executive ldl_executive
+#define ldul_supervisor ldl_supervisor
+
+#include "softmmu_defs.h"
+
+#define ACCESS_TYPE 0
+#define MEMSUFFIX MMU_MODE0_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#define ACCESS_TYPE 1
+#define MEMSUFFIX MMU_MODE1_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#if (NB_MMU_MODES >= 3)
+
+#define ACCESS_TYPE 2
+#define MEMSUFFIX MMU_MODE2_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 3) */
+
+#if (NB_MMU_MODES >= 4)
+
+#define ACCESS_TYPE 3
+#define MEMSUFFIX MMU_MODE3_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 4) */
+
+#if (NB_MMU_MODES >= 5)
+
+#define ACCESS_TYPE 4
+#define MEMSUFFIX MMU_MODE4_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 5) */
+
+#if (NB_MMU_MODES >= 6)
+
+#define ACCESS_TYPE 5
+#define MEMSUFFIX MMU_MODE5_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#endif /* (NB_MMU_MODES >= 6) */
+
+#if (NB_MMU_MODES > 6)
+#error "NB_MMU_MODES > 6 is not supported for now"
+#endif /* (NB_MMU_MODES > 6) */
+
+/* these access are slower, they must be as rare as possible */
+#define ACCESS_TYPE (NB_MMU_MODES)
+#define MEMSUFFIX _data
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#define ldub(p) ldub_data(p)
+#define ldsb(p) ldsb_data(p)
+#define lduw(p) lduw_data(p)
+#define ldsw(p) ldsw_data(p)
+#define ldl(p) ldl_data(p)
+#define ldq(p) ldq_data(p)
+
+#define stb(p, v) stb_data(p, v)
+#define stw(p, v) stw_data(p, v)
+#define stl(p, v) stl_data(p, v)
+#define stq(p, v) stq_data(p, v)
diff --git a/softmmu_header.h b/softmmu_header.h
new file mode 100644
index 0000000..2f95c33
--- /dev/null
+++ b/softmmu_header.h
@@ -0,0 +1,198 @@
+/*
+ * Software MMU support
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#if DATA_SIZE == 8
+#define SUFFIX q
+#define USUFFIX q
+#define DATA_TYPE uint64_t
+#elif DATA_SIZE == 4
+#define SUFFIX l
+#define USUFFIX l
+#define DATA_TYPE uint32_t
+#elif DATA_SIZE == 2
+#define SUFFIX w
+#define USUFFIX uw
+#define DATA_TYPE uint16_t
+#define DATA_STYPE int16_t
+#elif DATA_SIZE == 1
+#define SUFFIX b
+#define USUFFIX ub
+#define DATA_TYPE uint8_t
+#define DATA_STYPE int8_t
+#else
+#error unsupported data size
+#endif
+
+#if ACCESS_TYPE < (NB_MMU_MODES)
+
+#define CPU_MMU_INDEX ACCESS_TYPE
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == (NB_MMU_MODES)
+
+#define CPU_MMU_INDEX (cpu_mmu_index(env))
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == (NB_MMU_MODES + 1)
+
+#define CPU_MMU_INDEX (cpu_mmu_index(env))
+#define MMUSUFFIX _cmmu
+
+#else
+#error invalid ACCESS_TYPE
+#endif
+
+#if DATA_SIZE == 8
+#define RES_TYPE uint64_t
+#else
+#define RES_TYPE uint32_t
+#endif
+
+#if ACCESS_TYPE == (NB_MMU_MODES + 1)
+#define ADDR_READ addr_code
+#else
+#define ADDR_READ addr_read
+#endif
+
+/* generic load/store macros */
+
+static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+ int page_index;
+ RES_TYPE res;
+ target_ulong addr;
+ unsigned long physaddr;
+ int mmu_idx;
+
+ addr = ptr;
+ page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ mmu_idx = CPU_MMU_INDEX;
+ if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
+ (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
+ res = glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx);
+ } else {
+ physaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+ res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
+ }
+ return res;
+}
+
+#if DATA_SIZE <= 2
+static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+ int res, page_index;
+ target_ulong addr;
+ unsigned long physaddr;
+ int mmu_idx;
+
+ addr = ptr;
+ page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ mmu_idx = CPU_MMU_INDEX;
+ if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
+ (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
+ res = (DATA_STYPE)glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx);
+ } else {
+ physaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+ res = glue(glue(lds, SUFFIX), _raw)((uint8_t *)physaddr);
+ }
+ return res;
+}
+#endif
+
+#if ACCESS_TYPE != (NB_MMU_MODES + 1)
+
+/* generic store macro */
+
+static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v)
+{
+ int page_index;
+ target_ulong addr;
+ unsigned long physaddr;
+ int mmu_idx;
+
+ addr = ptr;
+ page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ mmu_idx = CPU_MMU_INDEX;
+ if (unlikely(env->tlb_table[mmu_idx][page_index].addr_write !=
+ (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
+ glue(glue(__st, SUFFIX), MMUSUFFIX)(addr, v, mmu_idx);
+ } else {
+ physaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+ glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, v);
+ }
+}
+
+#endif /* ACCESS_TYPE != (NB_MMU_MODES + 1) */
+
+#if ACCESS_TYPE != (NB_MMU_MODES + 1)
+
+#if DATA_SIZE == 8
+static inline float64 glue(ldfq, MEMSUFFIX)(target_ulong ptr)
+{
+ union {
+ float64 d;
+ uint64_t i;
+ } u;
+ u.i = glue(ldq, MEMSUFFIX)(ptr);
+ return u.d;
+}
+
+static inline void glue(stfq, MEMSUFFIX)(target_ulong ptr, float64 v)
+{
+ union {
+ float64 d;
+ uint64_t i;
+ } u;
+ u.d = v;
+ glue(stq, MEMSUFFIX)(ptr, u.i);
+}
+#endif /* DATA_SIZE == 8 */
+
+#if DATA_SIZE == 4
+static inline float32 glue(ldfl, MEMSUFFIX)(target_ulong ptr)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = glue(ldl, MEMSUFFIX)(ptr);
+ return u.f;
+}
+
+static inline void glue(stfl, MEMSUFFIX)(target_ulong ptr, float32 v)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = v;
+ glue(stl, MEMSUFFIX)(ptr, u.i);
+}
+#endif /* DATA_SIZE == 4 */
+
+#endif /* ACCESS_TYPE != (NB_MMU_MODES + 1) */
+
+#undef RES_TYPE
+#undef DATA_TYPE
+#undef DATA_STYPE
+#undef SUFFIX
+#undef USUFFIX
+#undef DATA_SIZE
+#undef CPU_MMU_INDEX
+#undef MMUSUFFIX
+#undef ADDR_READ
diff --git a/softmmu_template.h b/softmmu_template.h
new file mode 100644
index 0000000..c2df9ec
--- /dev/null
+++ b/softmmu_template.h
@@ -0,0 +1,332 @@
+/*
+ * Software MMU support
+ *
+ * Copyright (c) 2003 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 "qemu-timer.h"
+
+#define DATA_SIZE (1 << SHIFT)
+
+#if DATA_SIZE == 8
+#define SUFFIX q
+#define USUFFIX q
+#define DATA_TYPE uint64_t
+#elif DATA_SIZE == 4
+#define SUFFIX l
+#define USUFFIX l
+#define DATA_TYPE uint32_t
+#elif DATA_SIZE == 2
+#define SUFFIX w
+#define USUFFIX uw
+#define DATA_TYPE uint16_t
+#elif DATA_SIZE == 1
+#define SUFFIX b
+#define USUFFIX ub
+#define DATA_TYPE uint8_t
+#else
+#error unsupported data size
+#endif
+
+#ifdef SOFTMMU_CODE_ACCESS
+#define READ_ACCESS_TYPE 2
+#define ADDR_READ addr_code
+#else
+#define READ_ACCESS_TYPE 0
+#define ADDR_READ addr_read
+#endif
+
+static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int mmu_idx,
+ void *retaddr);
+static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
+ target_ulong addr,
+ void *retaddr)
+{
+ DATA_TYPE res;
+ int index;
+ index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
+ env->mem_io_pc = (unsigned long)retaddr;
+ if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
+ && !can_do_io(env)) {
+ cpu_io_recompile(env, retaddr);
+ }
+
+ env->mem_io_vaddr = addr;
+#if SHIFT <= 2
+ res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
+#else
+#ifdef TARGET_WORDS_BIGENDIAN
+ res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32;
+ res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4);
+#else
+ res = io_mem_read[index][2](io_mem_opaque[index], physaddr);
+ res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32;
+#endif
+#endif /* SHIFT > 2 */
+ return res;
+}
+
+/* handle all cases except unaligned access which span two pages */
+DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int mmu_idx)
+{
+ DATA_TYPE res;
+ int index;
+ target_ulong tlb_addr;
+ target_phys_addr_t ioaddr;
+ unsigned long addend;
+ void *retaddr;
+
+ /* test if there is match for unaligned or IO access */
+ /* XXX: could done more in memory macro in a non portable way */
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ retaddr = GETPC();
+ ioaddr = env->iotlb[mmu_idx][index];
+ res = glue(io_read, SUFFIX)(ioaddr, addr, retaddr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ /* slow unaligned access (it spans two pages or IO) */
+ do_unaligned_access:
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+#endif
+ res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr,
+ mmu_idx, retaddr);
+ } else {
+ /* unaligned/aligned access in the same page */
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ retaddr = GETPC();
+ do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+ }
+#endif
+ addend = env->tlb_table[mmu_idx][index].addend;
+ res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+#endif
+ tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+ goto redo;
+ }
+ return res;
+}
+
+/* handle all unaligned cases */
+static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ int mmu_idx,
+ void *retaddr)
+{
+ DATA_TYPE res, res1, res2;
+ int index, shift;
+ target_phys_addr_t ioaddr;
+ unsigned long addend;
+ target_ulong tlb_addr, addr1, addr2;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ ioaddr = env->iotlb[mmu_idx][index];
+ res = glue(io_read, SUFFIX)(ioaddr, addr, retaddr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ do_unaligned_access:
+ /* slow unaligned access (it spans two pages) */
+ addr1 = addr & ~(DATA_SIZE - 1);
+ addr2 = addr1 + DATA_SIZE;
+ res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1,
+ mmu_idx, retaddr);
+ res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2,
+ mmu_idx, retaddr);
+ shift = (addr & (DATA_SIZE - 1)) * 8;
+#ifdef TARGET_WORDS_BIGENDIAN
+ res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
+#else
+ res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
+#endif
+ res = (DATA_TYPE)res;
+ } else {
+ /* unaligned/aligned access in the same page */
+ addend = env->tlb_table[mmu_idx][index].addend;
+ res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+ goto redo;
+ }
+ return res;
+}
+
+#ifndef SOFTMMU_CODE_ACCESS
+
+static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ DATA_TYPE val,
+ int mmu_idx,
+ void *retaddr);
+
+static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
+ DATA_TYPE val,
+ target_ulong addr,
+ void *retaddr)
+{
+ int index;
+ index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+ physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
+ if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
+ && !can_do_io(env)) {
+ cpu_io_recompile(env, retaddr);
+ }
+
+ env->mem_io_vaddr = addr;
+ env->mem_io_pc = (unsigned long)retaddr;
+#if SHIFT <= 2
+ io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
+#else
+#ifdef TARGET_WORDS_BIGENDIAN
+ io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32);
+ io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val);
+#else
+ io_mem_write[index][2](io_mem_opaque[index], physaddr, val);
+ io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32);
+#endif
+#endif /* SHIFT > 2 */
+}
+
+void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ DATA_TYPE val,
+ int mmu_idx)
+{
+ target_phys_addr_t ioaddr;
+ unsigned long addend;
+ target_ulong tlb_addr;
+ void *retaddr;
+ int index;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ retaddr = GETPC();
+ ioaddr = env->iotlb[mmu_idx][index];
+ glue(io_write, SUFFIX)(ioaddr, val, addr, retaddr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ do_unaligned_access:
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ do_unaligned_access(addr, 1, mmu_idx, retaddr);
+#endif
+ glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val,
+ mmu_idx, retaddr);
+ } else {
+ /* aligned/unaligned access in the same page */
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0) {
+ retaddr = GETPC();
+ do_unaligned_access(addr, 1, mmu_idx, retaddr);
+ }
+#endif
+ addend = env->tlb_table[mmu_idx][index].addend;
+ glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val);
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ do_unaligned_access(addr, 1, mmu_idx, retaddr);
+#endif
+ tlb_fill(addr, 1, mmu_idx, retaddr);
+ goto redo;
+ }
+}
+
+/* handles all unaligned cases */
+static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+ DATA_TYPE val,
+ int mmu_idx,
+ void *retaddr)
+{
+ target_phys_addr_t ioaddr;
+ unsigned long addend;
+ target_ulong tlb_addr;
+ int index, i;
+
+ index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ if (tlb_addr & ~TARGET_PAGE_MASK) {
+ /* IO access */
+ if ((addr & (DATA_SIZE - 1)) != 0)
+ goto do_unaligned_access;
+ ioaddr = env->iotlb[mmu_idx][index];
+ glue(io_write, SUFFIX)(ioaddr, val, addr, retaddr);
+ } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+ do_unaligned_access:
+ /* XXX: not efficient, but simple */
+ /* Note: relies on the fact that tlb_fill() does not remove the
+ * previous page from the TLB cache. */
+ for(i = DATA_SIZE - 1; i >= 0; i--) {
+#ifdef TARGET_WORDS_BIGENDIAN
+ glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)),
+ mmu_idx, retaddr);
+#else
+ glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8),
+ mmu_idx, retaddr);
+#endif
+ }
+ } else {
+ /* aligned/unaligned access in the same page */
+ addend = env->tlb_table[mmu_idx][index].addend;
+ glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val);
+ }
+ } else {
+ /* the page is not in the TLB : fill it */
+ tlb_fill(addr, 1, mmu_idx, retaddr);
+ goto redo;
+ }
+}
+
+#endif /* !defined(SOFTMMU_CODE_ACCESS) */
+
+#undef READ_ACCESS_TYPE
+#undef SHIFT
+#undef DATA_TYPE
+#undef SUFFIX
+#undef USUFFIX
+#undef DATA_SIZE
+#undef ADDR_READ
diff --git a/sparc-dis.c b/sparc-dis.c
new file mode 100644
index 0000000..cdd337a
--- /dev/null
+++ b/sparc-dis.c
@@ -0,0 +1,3275 @@
+/*
+ * These files from binutils are concatenated:
+ * include/opcode/sparc.h, opcodes/sparc-opc.c, opcodes/sparc-dis.c
+ */
+
+/* include/opcode/sparc.h */
+
+/* Definitions for opcode table for the sparc.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000, 2002,
+ 2003, 2005 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler, GDB, the GNU debugger, and
+ the GNU Binutils.
+
+ GAS/GDB is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS/GDB 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS or GDB; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include "dis-asm.h"
+
+/* The SPARC opcode table (and other related data) is defined in
+ the opcodes library in sparc-opc.c. If you change anything here, make
+ sure you fix up that file, and vice versa. */
+
+ /* FIXME-someday: perhaps the ,a's and such should be embedded in the
+ instruction's name rather than the args. This would make gas faster, pinsn
+ slower, but would mess up some macros a bit. xoxorich. */
+
+/* List of instruction sets variations.
+ These values are such that each element is either a superset of a
+ preceding each one or they conflict in which case SPARC_OPCODE_CONFLICT_P
+ returns non-zero.
+ The values are indices into `sparc_opcode_archs' defined in sparc-opc.c.
+ Don't change this without updating sparc-opc.c. */
+
+enum sparc_opcode_arch_val
+{
+ SPARC_OPCODE_ARCH_V6 = 0,
+ SPARC_OPCODE_ARCH_V7,
+ SPARC_OPCODE_ARCH_V8,
+ SPARC_OPCODE_ARCH_SPARCLET,
+ SPARC_OPCODE_ARCH_SPARCLITE,
+ /* V9 variants must appear last. */
+ SPARC_OPCODE_ARCH_V9,
+ SPARC_OPCODE_ARCH_V9A, /* V9 with ultrasparc additions. */
+ SPARC_OPCODE_ARCH_V9B, /* V9 with ultrasparc and cheetah additions. */
+ SPARC_OPCODE_ARCH_BAD /* Error return from sparc_opcode_lookup_arch. */
+};
+
+/* The highest architecture in the table. */
+#define SPARC_OPCODE_ARCH_MAX (SPARC_OPCODE_ARCH_BAD - 1)
+
+/* Given an enum sparc_opcode_arch_val, return the bitmask to use in
+ insn encoding/decoding. */
+#define SPARC_OPCODE_ARCH_MASK(arch) (1 << (arch))
+
+/* Given a valid sparc_opcode_arch_val, return non-zero if it's v9. */
+#define SPARC_OPCODE_ARCH_V9_P(arch) ((arch) >= SPARC_OPCODE_ARCH_V9)
+
+/* Table of cpu variants. */
+
+typedef struct sparc_opcode_arch
+{
+ const char *name;
+ /* Mask of sparc_opcode_arch_val's supported.
+ EG: For v7 this would be
+ (SPARC_OPCODE_ARCH_MASK (..._V6) | SPARC_OPCODE_ARCH_MASK (..._V7)).
+ These are short's because sparc_opcode.architecture is. */
+ short supported;
+} sparc_opcode_arch;
+
+static const struct sparc_opcode_arch sparc_opcode_archs[];
+
+/* Return the bitmask of supported architectures for ARCH. */
+#define SPARC_OPCODE_SUPPORTED(ARCH) (sparc_opcode_archs[ARCH].supported)
+
+/* Non-zero if ARCH1 conflicts with ARCH2.
+ IE: ARCH1 as a supported bit set that ARCH2 doesn't, and vice versa. */
+#define SPARC_OPCODE_CONFLICT_P(ARCH1, ARCH2) \
+ (((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \
+ != SPARC_OPCODE_SUPPORTED (ARCH1)) \
+ && ((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \
+ != SPARC_OPCODE_SUPPORTED (ARCH2)))
+
+/* Structure of an opcode table entry. */
+
+typedef struct sparc_opcode
+{
+ const char *name;
+ unsigned long match; /* Bits that must be set. */
+ unsigned long lose; /* Bits that must not be set. */
+ const char *args;
+ /* This was called "delayed" in versions before the flags. */
+ char flags;
+ short architecture; /* Bitmask of sparc_opcode_arch_val's. */
+} sparc_opcode;
+
+#define F_DELAYED 1 /* Delayed branch. */
+#define F_ALIAS 2 /* Alias for a "real" instruction. */
+#define F_UNBR 4 /* Unconditional branch. */
+#define F_CONDBR 8 /* Conditional branch. */
+#define F_JSR 16 /* Subroutine call. */
+#define F_FLOAT 32 /* Floating point instruction (not a branch). */
+#define F_FBR 64 /* Floating point branch. */
+/* FIXME: Add F_ANACHRONISTIC flag for v9. */
+
+/* All sparc opcodes are 32 bits, except for the `set' instruction (really a
+ macro), which is 64 bits. It is handled as a special case.
+
+ The match component is a mask saying which bits must match a particular
+ opcode in order for an instruction to be an instance of that opcode.
+
+ The args component is a string containing one character for each operand of the
+ instruction.
+
+ Kinds of operands:
+ # Number used by optimizer. It is ignored.
+ 1 rs1 register.
+ 2 rs2 register.
+ d rd register.
+ e frs1 floating point register.
+ v frs1 floating point register (double/even).
+ V frs1 floating point register (quad/multiple of 4).
+ f frs2 floating point register.
+ B frs2 floating point register (double/even).
+ R frs2 floating point register (quad/multiple of 4).
+ g frsd floating point register.
+ H frsd floating point register (double/even).
+ J frsd floating point register (quad/multiple of 4).
+ b crs1 coprocessor register
+ c crs2 coprocessor register
+ D crsd coprocessor register
+ m alternate space register (asr) in rd
+ M alternate space register (asr) in rs1
+ h 22 high bits.
+ X 5 bit unsigned immediate
+ Y 6 bit unsigned immediate
+ 3 SIAM mode (3 bits). (v9b)
+ K MEMBAR mask (7 bits). (v9)
+ j 10 bit Immediate. (v9)
+ I 11 bit Immediate. (v9)
+ i 13 bit Immediate.
+ n 22 bit immediate.
+ k 2+14 bit PC relative immediate. (v9)
+ G 19 bit PC relative immediate. (v9)
+ l 22 bit PC relative immediate.
+ L 30 bit PC relative immediate.
+ a Annul. The annul bit is set.
+ A Alternate address space. Stored as 8 bits.
+ C Coprocessor state register.
+ F floating point state register.
+ p Processor state register.
+ N Branch predict clear ",pn" (v9)
+ T Branch predict set ",pt" (v9)
+ z %icc. (v9)
+ Z %xcc. (v9)
+ q Floating point queue.
+ r Single register that is both rs1 and rd.
+ O Single register that is both rs2 and rd.
+ Q Coprocessor queue.
+ S Special case.
+ t Trap base register.
+ w Window invalid mask register.
+ y Y register.
+ u sparclet coprocessor registers in rd position
+ U sparclet coprocessor registers in rs1 position
+ E %ccr. (v9)
+ s %fprs. (v9)
+ P %pc. (v9)
+ W %tick. (v9)
+ o %asi. (v9)
+ 6 %fcc0. (v9)
+ 7 %fcc1. (v9)
+ 8 %fcc2. (v9)
+ 9 %fcc3. (v9)
+ ! Privileged Register in rd (v9)
+ ? Privileged Register in rs1 (v9)
+ * Prefetch function constant. (v9)
+ x OPF field (v9 impdep).
+ 0 32/64 bit immediate for set or setx (v9) insns
+ _ Ancillary state register in rd (v9a)
+ / Ancillary state register in rs1 (v9a)
+
+ The following chars are unused: (note: ,[] are used as punctuation)
+ [45]. */
+
+#define OP2(x) (((x) & 0x7) << 22) /* Op2 field of format2 insns. */
+#define OP3(x) (((x) & 0x3f) << 19) /* Op3 field of format3 insns. */
+#define OP(x) ((unsigned) ((x) & 0x3) << 30) /* Op field of all insns. */
+#define OPF(x) (((x) & 0x1ff) << 5) /* Opf field of float insns. */
+#define OPF_LOW5(x) OPF ((x) & 0x1f) /* V9. */
+#define F3F(x, y, z) (OP (x) | OP3 (y) | OPF (z)) /* Format3 float insns. */
+#define F3I(x) (((x) & 0x1) << 13) /* Immediate field of format 3 insns. */
+#define F2(x, y) (OP (x) | OP2(y)) /* Format 2 insns. */
+#define F3(x, y, z) (OP (x) | OP3(y) | F3I(z)) /* Format3 insns. */
+#define F1(x) (OP (x))
+#define DISP30(x) ((x) & 0x3fffffff)
+#define ASI(x) (((x) & 0xff) << 5) /* Asi field of format3 insns. */
+#define RS2(x) ((x) & 0x1f) /* Rs2 field. */
+#define SIMM13(x) ((x) & 0x1fff) /* Simm13 field. */
+#define RD(x) (((x) & 0x1f) << 25) /* Destination register field. */
+#define RS1(x) (((x) & 0x1f) << 14) /* Rs1 field. */
+#define ASI_RS2(x) (SIMM13 (x))
+#define MEMBAR(x) ((x) & 0x7f)
+#define SLCPOP(x) (((x) & 0x7f) << 6) /* Sparclet cpop. */
+
+#define ANNUL (1 << 29)
+#define BPRED (1 << 19) /* V9. */
+#define IMMED F3I (1)
+#define RD_G0 RD (~0)
+#define RS1_G0 RS1 (~0)
+#define RS2_G0 RS2 (~0)
+
+static const struct sparc_opcode sparc_opcodes[];
+
+static const char *sparc_decode_asi_v8 (int);
+static const char *sparc_decode_asi_v9 (int);
+static const char *sparc_decode_membar (int);
+static const char *sparc_decode_prefetch (int);
+static const char *sparc_decode_sparclet_cpreg (int);
+
+/* Local Variables:
+ fill-column: 131
+ comment-column: 0
+ End: */
+
+/* opcodes/sparc-opc.c */
+
+/* Table of opcodes for the sparc.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2002, 2004, 2005
+ Free Software Foundation, Inc.
+
+ This file is part of the BFD library.
+
+ BFD is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+
+ BFD 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 General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this software; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+/* FIXME-someday: perhaps the ,a's and such should be embedded in the
+ instruction's name rather than the args. This would make gas faster, pinsn
+ slower, but would mess up some macros a bit. xoxorich. */
+
+/* Some defines to make life easy. */
+#define MASK_V6 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V6)
+#define MASK_V7 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V7)
+#define MASK_V8 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8)
+#define MASK_SPARCLET SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET)
+#define MASK_SPARCLITE SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE)
+#define MASK_V9 SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9)
+#define MASK_V9A SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A)
+#define MASK_V9B SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9B)
+
+/* Bit masks of architectures supporting the insn. */
+
+#define v6 (MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET \
+ | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B)
+/* v6 insns not supported on the sparclet. */
+#define v6notlet (MASK_V6 | MASK_V7 | MASK_V8 \
+ | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B)
+#define v7 (MASK_V7 | MASK_V8 | MASK_SPARCLET \
+ | MASK_SPARCLITE | MASK_V9 | MASK_V9A | MASK_V9B)
+/* Although not all insns are implemented in hardware, sparclite is defined
+ to be a superset of v8. Unimplemented insns trap and are then theoretically
+ implemented in software.
+ It's not clear that the same is true for sparclet, although the docs
+ suggest it is. Rather than complicating things, the sparclet assembler
+ recognizes all v8 insns. */
+#define v8 (MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE \
+ | MASK_V9 | MASK_V9A | MASK_V9B)
+#define sparclet (MASK_SPARCLET)
+#define sparclite (MASK_SPARCLITE)
+#define v9 (MASK_V9 | MASK_V9A | MASK_V9B)
+#define v9a (MASK_V9A | MASK_V9B)
+#define v9b (MASK_V9B)
+/* v6 insns not supported by v9. */
+#define v6notv9 (MASK_V6 | MASK_V7 | MASK_V8 \
+ | MASK_SPARCLET | MASK_SPARCLITE)
+/* v9a instructions which would appear to be aliases to v9's impdep's
+ otherwise. */
+#define v9notv9a (MASK_V9)
+
+/* Table of opcode architectures.
+ The order is defined in opcode/sparc.h. */
+
+static const struct sparc_opcode_arch sparc_opcode_archs[] =
+{
+ { "v6", MASK_V6 },
+ { "v7", MASK_V6 | MASK_V7 },
+ { "v8", MASK_V6 | MASK_V7 | MASK_V8 },
+ { "sparclet", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLET },
+ { "sparclite", MASK_V6 | MASK_V7 | MASK_V8 | MASK_SPARCLITE },
+ /* ??? Don't some v8 privileged insns conflict with v9? */
+ { "v9", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 },
+ /* v9 with ultrasparc additions */
+ { "v9a", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A },
+ /* v9 with cheetah additions */
+ { "v9b", MASK_V6 | MASK_V7 | MASK_V8 | MASK_V9 | MASK_V9A | MASK_V9B },
+ { NULL, 0 }
+};
+
+/* Branch condition field. */
+#define COND(x) (((x) & 0xf) << 25)
+
+/* v9: Move (MOVcc and FMOVcc) condition field. */
+#define MCOND(x,i_or_f) ((((i_or_f) & 1) << 18) | (((x) >> 11) & (0xf << 14))) /* v9 */
+
+/* v9: Move register (MOVRcc and FMOVRcc) condition field. */
+#define RCOND(x) (((x) & 0x7) << 10) /* v9 */
+
+#define CONDA (COND (0x8))
+#define CONDCC (COND (0xd))
+#define CONDCS (COND (0x5))
+#define CONDE (COND (0x1))
+#define CONDG (COND (0xa))
+#define CONDGE (COND (0xb))
+#define CONDGU (COND (0xc))
+#define CONDL (COND (0x3))
+#define CONDLE (COND (0x2))
+#define CONDLEU (COND (0x4))
+#define CONDN (COND (0x0))
+#define CONDNE (COND (0x9))
+#define CONDNEG (COND (0x6))
+#define CONDPOS (COND (0xe))
+#define CONDVC (COND (0xf))
+#define CONDVS (COND (0x7))
+
+#define CONDNZ CONDNE
+#define CONDZ CONDE
+#define CONDGEU CONDCC
+#define CONDLU CONDCS
+
+#define FCONDA (COND (0x8))
+#define FCONDE (COND (0x9))
+#define FCONDG (COND (0x6))
+#define FCONDGE (COND (0xb))
+#define FCONDL (COND (0x4))
+#define FCONDLE (COND (0xd))
+#define FCONDLG (COND (0x2))
+#define FCONDN (COND (0x0))
+#define FCONDNE (COND (0x1))
+#define FCONDO (COND (0xf))
+#define FCONDU (COND (0x7))
+#define FCONDUE (COND (0xa))
+#define FCONDUG (COND (0x5))
+#define FCONDUGE (COND (0xc))
+#define FCONDUL (COND (0x3))
+#define FCONDULE (COND (0xe))
+
+#define FCONDNZ FCONDNE
+#define FCONDZ FCONDE
+
+#define ICC (0) /* v9 */
+#define XCC (1 << 12) /* v9 */
+#define FCC(x) (((x) & 0x3) << 11) /* v9 */
+#define FBFCC(x) (((x) & 0x3) << 20) /* v9 */
+
+/* The order of the opcodes in the table is significant:
+
+ * The assembler requires that all instances of the same mnemonic must
+ be consecutive. If they aren't, the assembler will bomb at runtime.
+
+ * The disassembler should not care about the order of the opcodes. */
+
+/* Entries for commutative arithmetic operations. */
+/* ??? More entries can make use of this. */
+#define COMMUTEOP(opcode, op3, arch_mask) \
+{ opcode, F3(2, op3, 0), F3(~2, ~op3, ~0)|ASI(~0), "1,2,d", 0, arch_mask }, \
+{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "1,i,d", 0, arch_mask }, \
+{ opcode, F3(2, op3, 1), F3(~2, ~op3, ~1), "i,1,d", 0, arch_mask }
+
+static const struct sparc_opcode sparc_opcodes[] = {
+
+{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", 0, v6 },
+{ "ld", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ld", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0), "[1+2],g", 0, v6 },
+{ "ld", F3(3, 0x20, 0), F3(~3, ~0x20, ~0)|RS2_G0, "[1],g", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[1+i],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1), "[i+1],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|RS1_G0, "[i],g", 0, v6 },
+{ "ld", F3(3, 0x20, 1), F3(~3, ~0x20, ~1)|SIMM13(~0), "[1],g", 0, v6 }, /* ld [rs1+0],d */
+
+{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RD(~0), "[1+2],F", 0, v6 },
+{ "ld", F3(3, 0x21, 0), F3(~3, ~0x21, ~0)|RS2_G0|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[1+i],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RD(~0), "[i+1],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~0),"[i],F", 0, v6 },
+{ "ld", F3(3, 0x21, 1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~0),"[1],F", 0, v6 }, /* ld [rs1+0],d */
+
+{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1],D", 0, v6notv9 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i],D", 0, v6notv9 },
+{ "ld", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ld [rs1+0],d */
+{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0), "[1+2],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 0), F3(~3, ~0x31, ~0)|RS2_G0, "[1],C", 0, v6notv9 }, /* ld [rs1+%g0],d */
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[1+i],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1), "[i+1],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|RS1_G0, "[i],C", 0, v6notv9 },
+{ "ld", F3(3, 0x31, 1), F3(~3, ~0x31, ~1)|SIMM13(~0), "[1],C", 0, v6notv9 }, /* ld [rs1+0],d */
+
+/* The v9 LDUW is the same as the old 'ld' opcode, it is not the same as the
+ 'ld' pseudo-op in v9. */
+{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0), "[1+2],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 0), F3(~3, ~0x00, ~0)|RS2_G0, "[1],d", F_ALIAS, v9 }, /* ld [rs1+%g0],d */
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[1+i],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1), "[i+1],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|RS1_G0, "[i],d", F_ALIAS, v9 },
+{ "lduw", F3(3, 0x00, 1), F3(~3, ~0x00, ~1)|SIMM13(~0), "[1],d", F_ALIAS, v9 }, /* ld [rs1+0],d */
+
+{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 0), F3(~3, ~0x03, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[1+i],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1), "[i+1],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldd", F3(3, 0x03, 1), F3(~3, ~0x03, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldd [rs1+0],d */
+{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI(~0), "[1+2],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 0), F3(~3, ~0x23, ~0)|ASI_RS2(~0), "[1],H", 0, v6 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[1+i],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1), "[i+1],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|RS1_G0, "[i],H", 0, v6 },
+{ "ldd", F3(3, 0x23, 1), F3(~3, ~0x23, ~1)|SIMM13(~0), "[1],H", 0, v6 }, /* ldd [rs1+0],d */
+
+{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI(~0), "[1+2],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|ASI_RS2(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+%g0],d */
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i],D", 0, v6notv9 },
+{ "ldd", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1],D", 0, v6notv9 }, /* ldd [rs1+0],d */
+
+{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI(~0), "[1+2],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 0), F3(~3, ~0x22, ~0)|ASI_RS2(~0), "[1],J", 0, v9 }, /* ldd [rs1+%g0],d */
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[1+i],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1), "[i+1],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|RS1_G0, "[i],J", 0, v9 },
+{ "ldq", F3(3, 0x22, 1), F3(~3, ~0x22, ~1)|SIMM13(~0), "[1],J", 0, v9 }, /* ldd [rs1+0],d */
+
+{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 0), F3(~3, ~0x09, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsb [rs1+%g0],d */
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[1+i],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1), "[i+1],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldsb", F3(3, 0x09, 1), F3(~3, ~0x09, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsb [rs1+0],d */
+
+{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldsh [rs1+%g0],d */
+{ "ldsh", F3(3, 0x0a, 0), F3(~3, ~0x0a, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[1+i],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1), "[i+1],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldsh", F3(3, 0x0a, 1), F3(~3, ~0x0a, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldsh [rs1+0],d */
+
+{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 0), F3(~3, ~0x0d, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldstub [rs1+%g0],d */
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[1+i],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1), "[i+1],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldstub", F3(3, 0x0d, 1), F3(~3, ~0x0d, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldstub [rs1+0],d */
+
+{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI(~0), "[1+2],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 0), F3(~3, ~0x08, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldsw [rs1+%g0],d */
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[1+i],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1), "[i+1],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|RS1_G0, "[i],d", 0, v9 },
+{ "ldsw", F3(3, 0x08, 1), F3(~3, ~0x08, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldsw [rs1+0],d */
+
+{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 0), F3(~3, ~0x01, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* ldub [rs1+%g0],d */
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[1+i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1), "[i+1],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "ldub", F3(3, 0x01, 1), F3(~3, ~0x01, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* ldub [rs1+0],d */
+
+{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI(~0), "[1+2],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 0), F3(~3, ~0x02, ~0)|ASI_RS2(~0), "[1],d", 0, v6 }, /* lduh [rs1+%g0],d */
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[1+i],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1), "[i+1],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|RS1_G0, "[i],d", 0, v6 },
+{ "lduh", F3(3, 0x02, 1), F3(~3, ~0x02, ~1)|SIMM13(~0), "[1],d", 0, v6 }, /* lduh [rs1+0],d */
+
+{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI(~0), "[1+2],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 0), F3(~3, ~0x0b, ~0)|ASI_RS2(~0), "[1],d", 0, v9 }, /* ldx [rs1+%g0],d */
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[1+i],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1), "[i+1],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|RS1_G0, "[i],d", 0, v9 },
+{ "ldx", F3(3, 0x0b, 1), F3(~3, ~0x0b, ~1)|SIMM13(~0), "[1],d", 0, v9 }, /* ldx [rs1+0],d */
+
+{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RD(~1), "[1+2],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 0)|RD(1), F3(~3, ~0x21, ~0)|RS2_G0|RD(~1), "[1],F", 0, v9 }, /* ld [rs1+%g0],d */
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[1+i],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RD(~1), "[i+1],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|RS1_G0|RD(~1), "[i],F", 0, v9 },
+{ "ldx", F3(3, 0x21, 1)|RD(1), F3(~3, ~0x21, ~1)|SIMM13(~0)|RD(~1),"[1],F", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", 0, v6 },
+{ "lda", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lda [rs1+%g0],d */
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", 0, v9 },
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", 0, v9 },
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "lda", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0), "[1+2]A,g", 0, v9 },
+{ "lda", F3(3, 0x30, 0), F3(~3, ~0x30, ~0)|RS2_G0, "[1]A,g", 0, v9 }, /* lda [rs1+%g0],d */
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[1+i]o,g", 0, v9 },
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1), "[i+1]o,g", 0, v9 },
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|RS1_G0, "[i]o,g", 0, v9 },
+{ "lda", F3(3, 0x30, 1), F3(~3, ~0x30, ~1)|SIMM13(~0), "[1]o,g", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0), "[1+2]A,d", 0, v6 },
+{ "ldda", F3(3, 0x13, 0), F3(~3, ~0x13, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldda [rs1+%g0],d */
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[1+i]o,d", 0, v9 },
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1), "[i+1]o,d", 0, v9 },
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldda", F3(3, 0x13, 1), F3(~3, ~0x13, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0), "[1+2]A,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 0), F3(~3, ~0x33, ~0)|RS2_G0, "[1]A,H", 0, v9 }, /* ldda [rs1+%g0],d */
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[1+i]o,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1), "[i+1]o,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|RS1_G0, "[i]o,H", 0, v9 },
+{ "ldda", F3(3, 0x33, 1), F3(~3, ~0x33, ~1)|SIMM13(~0), "[1]o,H", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0), "[1+2]A,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 0), F3(~3, ~0x32, ~0)|RS2_G0, "[1]A,J", 0, v9 }, /* ldd [rs1+%g0],d */
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[1+i]o,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1), "[i+1]o,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|RS1_G0, "[i]o,J", 0, v9 },
+{ "ldqa", F3(3, 0x32, 1), F3(~3, ~0x32, ~1)|SIMM13(~0), "[1]o,J", 0, v9 }, /* ldd [rs1+0],d */
+
+{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0), "[1+2]A,d", 0, v6 },
+{ "ldsba", F3(3, 0x19, 0), F3(~3, ~0x19, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsba [rs1+%g0],d */
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[1+i]o,d", 0, v9 },
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1), "[i+1]o,d", 0, v9 },
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldsba", F3(3, 0x19, 1), F3(~3, ~0x19, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0), "[1+2]A,d", 0, v6 },
+{ "ldsha", F3(3, 0x1a, 0), F3(~3, ~0x1a, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldsha [rs1+%g0],d */
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[1+i]o,d", 0, v9 },
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1), "[i+1]o,d", 0, v9 },
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldsha", F3(3, 0x1a, 1), F3(~3, ~0x1a, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0), "[1+2]A,d", 0, v6 },
+{ "ldstuba", F3(3, 0x1d, 0), F3(~3, ~0x1d, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* ldstuba [rs1+%g0],d */
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[1+i]o,d", 0, v9 },
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1), "[i+1]o,d", 0, v9 },
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldstuba", F3(3, 0x1d, 1), F3(~3, ~0x1d, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0), "[1+2]A,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 0), F3(~3, ~0x18, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[1+i]o,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1), "[i+1]o,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldswa", F3(3, 0x18, 1), F3(~3, ~0x18, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0), "[1+2]A,d", 0, v6 },
+{ "lduba", F3(3, 0x11, 0), F3(~3, ~0x11, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduba [rs1+%g0],d */
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[1+i]o,d", 0, v9 },
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1), "[i+1]o,d", 0, v9 },
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "lduba", F3(3, 0x11, 1), F3(~3, ~0x11, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0), "[1+2]A,d", 0, v6 },
+{ "lduha", F3(3, 0x12, 0), F3(~3, ~0x12, ~0)|RS2_G0, "[1]A,d", 0, v6 }, /* lduha [rs1+%g0],d */
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[1+i]o,d", 0, v9 },
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1), "[i+1]o,d", 0, v9 },
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "lduha", F3(3, 0x12, 1), F3(~3, ~0x12, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0), "[1+2]A,d", F_ALIAS, v9 }, /* lduwa === lda */
+{ "lduwa", F3(3, 0x10, 0), F3(~3, ~0x10, ~0)|RS2_G0, "[1]A,d", F_ALIAS, v9 }, /* lda [rs1+%g0],d */
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[1+i]o,d", F_ALIAS, v9 },
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1), "[i+1]o,d", F_ALIAS, v9 },
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|RS1_G0, "[i]o,d", F_ALIAS, v9 },
+{ "lduwa", F3(3, 0x10, 1), F3(~3, ~0x10, ~1)|SIMM13(~0), "[1]o,d", F_ALIAS, v9 }, /* ld [rs1+0],d */
+
+{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0), "[1+2]A,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 0), F3(~3, ~0x1b, ~0)|RS2_G0, "[1]A,d", 0, v9 }, /* lda [rs1+%g0],d */
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[1+i]o,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1), "[i+1]o,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "ldxa", F3(3, 0x1b, 1), F3(~3, ~0x1b, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* ld [rs1+0],d */
+
+{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "st", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "st", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI(~0), "g,[1+2]", 0, v6 },
+{ "st", F3(3, 0x24, 0), F3(~3, ~0x24, ~0)|ASI_RS2(~0), "g,[1]", 0, v6 }, /* st d[rs1+%g0] */
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[1+i]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1), "g,[i+1]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|RS1_G0, "g,[i]", 0, v6 },
+{ "st", F3(3, 0x24, 1), F3(~3, ~0x24, ~1)|SIMM13(~0), "g,[1]", 0, v6 }, /* st d,[rs1+0] */
+
+{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[1+i]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "D,[i+1]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "D,[i]", 0, v6notv9 },
+{ "st", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* st d,[rs1+0] */
+{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI(~0), "C,[1+2]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 0), F3(~3, ~0x35, ~0)|ASI_RS2(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[1+i]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1), "C,[i+1]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|RS1_G0, "C,[i]", 0, v6notv9 },
+{ "st", F3(3, 0x35, 1), F3(~3, ~0x35, ~1)|SIMM13(~0), "C,[1]", 0, v6notv9 }, /* st d,[rs1+0] */
+
+{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI(~0), "F,[1+2]", 0, v6 },
+{ "st", F3(3, 0x25, 0), F3(~3, ~0x25, ~0)|RD_G0|ASI_RS2(~0), "F,[1]", 0, v6 }, /* st d,[rs1+%g0] */
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[1+i]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0, "F,[i+1]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|RS1_G0, "F,[i]", 0, v6 },
+{ "st", F3(3, 0x25, 1), F3(~3, ~0x25, ~1)|RD_G0|SIMM13(~0), "F,[1]", 0, v6 }, /* st d,[rs1+0] */
+
+{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 },
+{ "stw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stsw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 },
+{ "stsw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stuw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+%g0] */
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v9 },
+{ "stuw", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v9 }, /* st d,[rs1+0] */
+
+{ "spill", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* st d,[rs1+%g0] */
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "spill", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* st d,[rs1+0] */
+
+{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", 0, v6 },
+{ "sta", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* sta d,[rs1+%g0] */
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", 0, v9 },
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", 0, v9 },
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "sta", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* st d,[rs1+0] */
+
+{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0), "g,[1+2]A", 0, v9 },
+{ "sta", F3(3, 0x34, 0), F3(~3, ~0x34, ~0)|RS2(~0), "g,[1]A", 0, v9 }, /* sta d,[rs1+%g0] */
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[1+i]o", 0, v9 },
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1), "g,[i+1]o", 0, v9 },
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|RS1_G0, "g,[i]o", 0, v9 },
+{ "sta", F3(3, 0x34, 1), F3(~3, ~0x34, ~1)|SIMM13(~0), "g,[1]o", 0, v9 }, /* st d,[rs1+0] */
+
+{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stswa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stswa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */
+{ "stuwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0), "d,[1+2]A", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 0), F3(~3, ~0x14, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v9 }, /* sta d,[rs1+%g0] */
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stuwa", F3(3, 0x14, 1), F3(~3, ~0x14, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* st d,[rs1+0] */
+
+{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "stb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+%g0] */
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "stb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* stb d,[rs1+0] */
+
+{ "stsb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+%g0] */
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stsb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+0] */
+{ "stub", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+%g0] */
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stub", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* stb d,[rs1+0] */
+
+{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", 0, v6 },
+{ "stba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stba d,[rs1+%g0] */
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", 0, v9 },
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", 0, v9 },
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stb d,[rs1+0] */
+
+{ "stsba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stsba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stba d,[rs1+%g0] */
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stsba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* stb d,[rs1+0] */
+{ "stuba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stuba", F3(3, 0x15, 0), F3(~3, ~0x15, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stba d,[rs1+%g0] */
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stuba", F3(3, 0x15, 1), F3(~3, ~0x15, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* stb d,[rs1+0] */
+
+{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "std", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "std", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* std d,[rs1+0] */
+
+{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "q,[1+2]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[1+i]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "q,[i+1]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "q,[i]", 0, v6notv9 },
+{ "std", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI(~0), "H,[1+2]", 0, v6 },
+{ "std", F3(3, 0x27, 0), F3(~3, ~0x27, ~0)|ASI_RS2(~0), "H,[1]", 0, v6 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[1+i]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1), "H,[i+1]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|RS1_G0, "H,[i]", 0, v6 },
+{ "std", F3(3, 0x27, 1), F3(~3, ~0x27, ~1)|SIMM13(~0), "H,[1]", 0, v6 }, /* std d,[rs1+0] */
+
+{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "Q,[1+2]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[1+i]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "Q,[i+1]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "Q,[i]", 0, v6notv9 },
+{ "std", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "Q,[1]", 0, v6notv9 }, /* std d,[rs1+0] */
+{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI(~0), "D,[1+2]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|ASI_RS2(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+%g0] */
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[1+i]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "D,[i+1]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "D,[i]", 0, v6notv9 },
+{ "std", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "D,[1]", 0, v6notv9 }, /* std d,[rs1+0] */
+
+{ "spilld", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 0), F3(~3, ~0x07, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* std d,[rs1+%g0] */
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "spilld", F3(3, 0x07, 1), F3(~3, ~0x07, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* std d,[rs1+0] */
+
+{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0), "d,[1+2]A", 0, v6 },
+{ "stda", F3(3, 0x17, 0), F3(~3, ~0x17, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stda d,[rs1+%g0] */
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[1+i]o", 0, v9 },
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1), "d,[i+1]o", 0, v9 },
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stda", F3(3, 0x17, 1), F3(~3, ~0x17, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* std d,[rs1+0] */
+{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0), "H,[1+2]A", 0, v9 },
+{ "stda", F3(3, 0x37, 0), F3(~3, ~0x37, ~0)|RS2(~0), "H,[1]A", 0, v9 }, /* stda d,[rs1+%g0] */
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[1+i]o", 0, v9 },
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1), "H,[i+1]o", 0, v9 },
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|RS1_G0, "H,[i]o", 0, v9 },
+{ "stda", F3(3, 0x37, 1), F3(~3, ~0x37, ~1)|SIMM13(~0), "H,[1]o", 0, v9 }, /* std d,[rs1+0] */
+
+{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", 0, v6 },
+{ "sth", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+%g0] */
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", 0, v6 },
+{ "sth", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", 0, v6 }, /* sth d,[rs1+0] */
+
+{ "stsh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+%g0] */
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stsh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+0] */
+{ "stuh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI(~0), "d,[1+2]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|ASI_RS2(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+%g0] */
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[1+i]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1), "d,[i+1]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RS1_G0, "d,[i]", F_ALIAS, v6 },
+{ "stuh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|SIMM13(~0), "d,[1]", F_ALIAS, v6 }, /* sth d,[rs1+0] */
+
+{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", 0, v6 },
+{ "stha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", 0, v6 }, /* stha ,[rs1+%g0] */
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", 0, v9 },
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", 0, v9 },
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* sth d,[rs1+0] */
+
+{ "stsha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stsha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stha ,[rs1+%g0] */
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stsha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* sth d,[rs1+0] */
+{ "stuha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0), "d,[1+2]A", F_ALIAS, v6 },
+{ "stuha", F3(3, 0x16, 0), F3(~3, ~0x16, ~0)|RS2(~0), "d,[1]A", F_ALIAS, v6 }, /* stha ,[rs1+%g0] */
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[1+i]o", F_ALIAS, v9 },
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1), "d,[i+1]o", F_ALIAS, v9 },
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|RS1_G0, "d,[i]o", F_ALIAS, v9 },
+{ "stuha", F3(3, 0x16, 1), F3(~3, ~0x16, ~1)|SIMM13(~0), "d,[1]o", F_ALIAS, v9 }, /* sth d,[rs1+0] */
+
+{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI(~0), "d,[1+2]", 0, v9 },
+{ "stx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|ASI_RS2(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+%g0] */
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[1+i]", 0, v9 },
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1), "d,[i+1]", 0, v9 },
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RS1_G0, "d,[i]", 0, v9 },
+{ "stx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|SIMM13(~0), "d,[1]", 0, v9 }, /* stx d,[rs1+0] */
+
+{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI(~0)|RD(~1), "F,[1+2]", 0, v9 },
+{ "stx", F3(3, 0x25, 0)|RD(1), F3(~3, ~0x25, ~0)|ASI_RS2(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+%g0] */
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[1+i]", 0, v9 },
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RD(~1), "F,[i+1]", 0, v9 },
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|RS1_G0|RD(~1), "F,[i]", 0, v9 },
+{ "stx", F3(3, 0x25, 1)|RD(1), F3(~3, ~0x25, ~1)|SIMM13(~0)|RD(~1),"F,[1]", 0, v9 }, /* stx d,[rs1+0] */
+
+{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0), "d,[1+2]A", 0, v9 },
+{ "stxa", F3(3, 0x1e, 0), F3(~3, ~0x1e, ~0)|RS2(~0), "d,[1]A", 0, v9 }, /* stxa d,[rs1+%g0] */
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[1+i]o", 0, v9 },
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1), "d,[i+1]o", 0, v9 },
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|RS1_G0, "d,[i]o", 0, v9 },
+{ "stxa", F3(3, 0x1e, 1), F3(~3, ~0x1e, ~1)|SIMM13(~0), "d,[1]o", 0, v9 }, /* stx d,[rs1+0] */
+
+{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI(~0), "J,[1+2]", 0, v9 },
+{ "stq", F3(3, 0x26, 0), F3(~3, ~0x26, ~0)|ASI_RS2(~0), "J,[1]", 0, v9 }, /* stq [rs1+%g0] */
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[1+i]", 0, v9 },
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1), "J,[i+1]", 0, v9 },
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|RS1_G0, "J,[i]", 0, v9 },
+{ "stq", F3(3, 0x26, 1), F3(~3, ~0x26, ~1)|SIMM13(~0), "J,[1]", 0, v9 }, /* stq [rs1+0] */
+
+{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI(~0), "J,[1+2]A", 0, v9 },
+{ "stqa", F3(3, 0x36, 0), F3(~3, ~0x36, ~0)|ASI_RS2(~0), "J,[1]A", 0, v9 }, /* stqa [rs1+%g0] */
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[1+i]o", 0, v9 },
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1), "J,[i+1]o", 0, v9 },
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|RS1_G0, "J,[i]o", 0, v9 },
+{ "stqa", F3(3, 0x36, 1), F3(~3, ~0x36, ~1)|SIMM13(~0), "J,[1]o", 0, v9 }, /* stqa [rs1+0] */
+
+{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI(~0), "[1+2],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 0), F3(~3, ~0x0f, ~0)|ASI_RS2(~0), "[1],d", 0, v7 }, /* swap [rs1+%g0],d */
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[1+i],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1), "[i+1],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|RS1_G0, "[i],d", 0, v7 },
+{ "swap", F3(3, 0x0f, 1), F3(~3, ~0x0f, ~1)|SIMM13(~0), "[1],d", 0, v7 }, /* swap [rs1+0],d */
+
+{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0), "[1+2]A,d", 0, v7 },
+{ "swapa", F3(3, 0x1f, 0), F3(~3, ~0x1f, ~0)|RS2(~0), "[1]A,d", 0, v7 }, /* swapa [rs1+%g0],d */
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[1+i]o,d", 0, v9 },
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1), "[i+1]o,d", 0, v9 },
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|RS1_G0, "[i]o,d", 0, v9 },
+{ "swapa", F3(3, 0x1f, 1), F3(~3, ~0x1f, ~1)|SIMM13(~0), "[1]o,d", 0, v9 }, /* swap [rs1+0],d */
+
+{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "restore", F3(2, 0x3d, 0), F3(~2, ~0x3d, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v6 }, /* restore %g0,%g0,%g0 */
+{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1), "1,i,d", 0, v6 },
+{ "restore", F3(2, 0x3d, 1), F3(~2, ~0x3d, ~1)|RD_G0|RS1_G0|SIMM13(~0), "", 0, v6 }, /* restore %g0,0,%g0 */
+
+{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* rett rs1+rs2 */
+{ "rett", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1,%g0 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* rett rs1+X */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X+rs1 */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* rett X */
+{ "rett", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* rett rs1+0 */
+
+{ "save", F3(2, 0x3c, 0), F3(~2, ~0x3c, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "save", F3(2, 0x3c, 1), F3(~2, ~0x3c, ~1), "1,i,d", 0, v6 },
+{ "save", 0x81e00000, ~0x81e00000, "", F_ALIAS, v6 },
+
+{ "ret", F3(2, 0x38, 1)|RS1(0x1f)|SIMM13(8), F3(~2, ~0x38, ~1)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %i7+8,%g0 */
+{ "retl", F3(2, 0x38, 1)|RS1(0x0f)|SIMM13(8), F3(~2, ~0x38, ~1)|RS1(~0x0f)|SIMM13(~8), "", F_UNBR|F_DELAYED, v6 }, /* jmpl %o7+8,%g0 */
+
+{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI(~0), "1+2,d", F_JSR|F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|ASI_RS2(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|SIMM13(~0), "1,d", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RS1_G0, "i,d", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,d */
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "1+i,d", F_JSR|F_DELAYED, v6 },
+{ "jmpl", F3(2, 0x38, 1), F3(~2, ~0x38, ~1), "i+1,d", F_JSR|F_DELAYED, v6 },
+
+{ "done", F3(2, 0x3e, 0)|RD(0), F3(~2, ~0x3e, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "retry", F3(2, 0x3e, 0)|RD(1), F3(~2, ~0x3e, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "saved", F3(2, 0x31, 0)|RD(0), F3(~2, ~0x31, ~0)|RD(~0)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "restored", F3(2, 0x31, 0)|RD(1), F3(~2, ~0x31, ~0)|RD(~1)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "allclean", F3(2, 0x31, 0)|RD(2), F3(~2, ~0x31, ~0)|RD(~2)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "otherw", F3(2, 0x31, 0)|RD(3), F3(~2, ~0x31, ~0)|RD(~3)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "normalw", F3(2, 0x31, 0)|RD(4), F3(~2, ~0x31, ~0)|RD(~4)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "invalw", F3(2, 0x31, 0)|RD(5), F3(~2, ~0x31, ~0)|RD(~5)|RS1_G0|SIMM13(~0), "", 0, v9 },
+{ "sir", F3(2, 0x30, 1)|RD(0xf), F3(~2, ~0x30, ~1)|RD(~0xf)|RS1_G0, "i", 0, v9 },
+
+{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", 0, v8 },
+{ "flush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", 0, v8 }, /* flush rs1+%g0 */
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", 0, v8 }, /* flush rs1+0 */
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", 0, v8 }, /* flush %g0+i */
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", 0, v8 },
+{ "flush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", 0, v8 },
+
+/* IFLUSH was renamed to FLUSH in v8. */
+{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI(~0), "1+2", F_ALIAS, v6 },
+{ "iflush", F3(2, 0x3b, 0), F3(~2, ~0x3b, ~0)|ASI_RS2(~0), "1", F_ALIAS, v6 }, /* flush rs1+%g0 */
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|SIMM13(~0), "1", F_ALIAS, v6 }, /* flush rs1+0 */
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1)|RS1_G0, "i", F_ALIAS, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "1+i", F_ALIAS, v6 },
+{ "iflush", F3(2, 0x3b, 1), F3(~2, ~0x3b, ~1), "i+1", F_ALIAS, v6 },
+
+{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI(~0), "1+2", 0, v9 },
+{ "return", F3(2, 0x39, 0), F3(~2, ~0x39, ~0)|ASI_RS2(~0), "1", 0, v9 }, /* return rs1+%g0 */
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|SIMM13(~0), "1", 0, v9 }, /* return rs1+0 */
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1)|RS1_G0, "i", 0, v9 }, /* return %g0+i */
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "1+i", 0, v9 },
+{ "return", F3(2, 0x39, 1), F3(~2, ~0x39, ~1), "i+1", 0, v9 },
+
+{ "flushw", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "", 0, v9 },
+
+{ "membar", F3(2, 0x28, 1)|RS1(0xf), F3(~2, ~0x28, ~1)|RD_G0|RS1(~0xf)|SIMM13(~127), "K", 0, v9 },
+{ "stbar", F3(2, 0x28, 0)|RS1(0xf), F3(~2, ~0x28, ~0)|RD_G0|RS1(~0xf)|SIMM13(~0), "", 0, v8 },
+
+{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0), "[1+2],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 0), F3(~3, ~0x2d, ~0)|RS2_G0, "[1],*", 0, v9 }, /* prefetch [rs1+%g0],prefetch_fcn */
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[1+i],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1), "[i+1],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|RS1_G0, "[i],*", 0, v9 },
+{ "prefetch", F3(3, 0x2d, 1), F3(~3, ~0x2d, ~1)|SIMM13(~0), "[1],*", 0, v9 }, /* prefetch [rs1+0],prefetch_fcn */
+{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0), "[1+2]A,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 0), F3(~3, ~0x3d, ~0)|RS2_G0, "[1]A,*", 0, v9 }, /* prefetcha [rs1+%g0],prefetch_fcn */
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[1+i]o,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1), "[i+1]o,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|RS1_G0, "[i]o,*", 0, v9 },
+{ "prefetcha", F3(3, 0x3d, 1), F3(~3, ~0x3d, ~1)|SIMM13(~0), "[1]o,*", 0, v9 }, /* prefetcha [rs1+0],d */
+
+{ "sll", F3(2, 0x25, 0), F3(~2, ~0x25, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 },
+{ "sll", F3(2, 0x25, 1), F3(~2, ~0x25, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 },
+{ "sra", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 },
+{ "sra", F3(2, 0x27, 1), F3(~2, ~0x27, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 },
+{ "srl", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|(0x7f<<5), "1,2,d", 0, v6 },
+{ "srl", F3(2, 0x26, 1), F3(~2, ~0x26, ~1)|(1<<12)|(0x7f<<5), "1,X,d", 0, v6 },
+
+{ "sllx", F3(2, 0x25, 0)|(1<<12), F3(~2, ~0x25, ~0)|(0x7f<<5), "1,2,d", 0, v9 },
+{ "sllx", F3(2, 0x25, 1)|(1<<12), F3(~2, ~0x25, ~1)|(0x3f<<6), "1,Y,d", 0, v9 },
+{ "srax", F3(2, 0x27, 0)|(1<<12), F3(~2, ~0x27, ~0)|(0x7f<<5), "1,2,d", 0, v9 },
+{ "srax", F3(2, 0x27, 1)|(1<<12), F3(~2, ~0x27, ~1)|(0x3f<<6), "1,Y,d", 0, v9 },
+{ "srlx", F3(2, 0x26, 0)|(1<<12), F3(~2, ~0x26, ~0)|(0x7f<<5), "1,2,d", 0, v9 },
+{ "srlx", F3(2, 0x26, 1)|(1<<12), F3(~2, ~0x26, ~1)|(0x3f<<6), "1,Y,d", 0, v9 },
+
+{ "mulscc", F3(2, 0x24, 0), F3(~2, ~0x24, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "mulscc", F3(2, 0x24, 1), F3(~2, ~0x24, ~1), "1,i,d", 0, v6 },
+
+{ "divscc", F3(2, 0x1d, 0), F3(~2, ~0x1d, ~0)|ASI(~0), "1,2,d", 0, sparclite },
+{ "divscc", F3(2, 0x1d, 1), F3(~2, ~0x1d, ~1), "1,i,d", 0, sparclite },
+
+{ "scan", F3(2, 0x2c, 0), F3(~2, ~0x2c, ~0)|ASI(~0), "1,2,d", 0, sparclet|sparclite },
+{ "scan", F3(2, 0x2c, 1), F3(~2, ~0x2c, ~1), "1,i,d", 0, sparclet|sparclite },
+
+{ "popc", F3(2, 0x2e, 0), F3(~2, ~0x2e, ~0)|RS1_G0|ASI(~0),"2,d", 0, v9 },
+{ "popc", F3(2, 0x2e, 1), F3(~2, ~0x2e, ~1)|RS1_G0, "i,d", 0, v9 },
+
+{ "clr", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RD_G0|RS1_G0|ASI_RS2(~0), "d", F_ALIAS, v6 }, /* or %g0,%g0,d */
+{ "clr", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0|SIMM13(~0), "d", F_ALIAS, v6 }, /* or %g0,0,d */
+{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 0), F3(~3, ~0x04, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+%g0] */
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clr", F3(3, 0x04, 1), F3(~3, ~0x04, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* st %g0,[rs1+0] */
+
+{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 0), F3(~3, ~0x05, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+%g0] */
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clrb", F3(3, 0x05, 1), F3(~3, ~0x05, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* stb %g0,[rs1+0] */
+
+{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 0), F3(~3, ~0x06, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+%g0] */
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[1+i]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0, "[i+1]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v6 },
+{ "clrh", F3(3, 0x06, 1), F3(~3, ~0x06, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v6 }, /* sth %g0,[rs1+0] */
+
+{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI(~0), "[1+2]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 0), F3(~3, ~0x0e, ~0)|RD_G0|ASI_RS2(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+%g0] */
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[1+i]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0, "[i+1]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|RS1_G0, "[i]", F_ALIAS, v9 },
+{ "clrx", F3(3, 0x0e, 1), F3(~3, ~0x0e, ~1)|RD_G0|SIMM13(~0), "[1]", F_ALIAS, v9 }, /* stx %g0,[rs1+0] */
+
+{ "orcc", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "1,i,d", 0, v6 },
+{ "orcc", F3(2, 0x12, 1), F3(~2, ~0x12, ~1), "i,1,d", 0, v6 },
+
+/* This is not a commutative instruction. */
+{ "orncc", F3(2, 0x16, 0), F3(~2, ~0x16, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orncc", F3(2, 0x16, 1), F3(~2, ~0x16, ~1), "1,i,d", 0, v6 },
+
+/* This is not a commutative instruction. */
+{ "orn", F3(2, 0x06, 0), F3(~2, ~0x06, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "orn", F3(2, 0x06, 1), F3(~2, ~0x06, ~1), "1,i,d", 0, v6 },
+
+{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|ASI_RS2(~0), "1", 0, v6 }, /* orcc rs1, %g0, %g0 */
+{ "tst", F3(2, 0x12, 0), F3(~2, ~0x12, ~0)|RD_G0|RS1_G0|ASI(~0), "2", 0, v6 }, /* orcc %g0, rs2, %g0 */
+{ "tst", F3(2, 0x12, 1), F3(~2, ~0x12, ~1)|RD_G0|SIMM13(~0), "1", 0, v6 }, /* orcc rs1, 0, %g0 */
+
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", 0, v8 }, /* wr r,r,%asrX */
+{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", 0, v8 }, /* wr r,i,%asrX */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", 0, v6 }, /* wr r,r,%y */
+{ "wr", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", 0, v6 }, /* wr r,i,%y */
+{ "wr", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */
+{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", 0, v6notv9 }, /* wr r,r,%psr */
+{ "wr", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", 0, v6notv9 }, /* wr r,i,%psr */
+{ "wr", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */
+{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", 0, v6notv9 }, /* wr r,r,%wim */
+{ "wr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", 0, v6notv9 }, /* wr r,i,%wim */
+{ "wr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */
+{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", 0, v6notv9 }, /* wr r,r,%tbr */
+{ "wr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", 0, v6notv9 }, /* wr r,i,%tbr */
+{ "wr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */
+
+{ "wr", F3(2, 0x30, 0)|RD(2), F3(~2, ~0x30, ~0)|RD(~2)|ASI(~0), "1,2,E", 0, v9 }, /* wr r,r,%ccr */
+{ "wr", F3(2, 0x30, 1)|RD(2), F3(~2, ~0x30, ~1)|RD(~2), "1,i,E", 0, v9 }, /* wr r,i,%ccr */
+{ "wr", F3(2, 0x30, 0)|RD(3), F3(~2, ~0x30, ~0)|RD(~3)|ASI(~0), "1,2,o", 0, v9 }, /* wr r,r,%asi */
+{ "wr", F3(2, 0x30, 1)|RD(3), F3(~2, ~0x30, ~1)|RD(~3), "1,i,o", 0, v9 }, /* wr r,i,%asi */
+{ "wr", F3(2, 0x30, 0)|RD(6), F3(~2, ~0x30, ~0)|RD(~6)|ASI(~0), "1,2,s", 0, v9 }, /* wr r,r,%fprs */
+{ "wr", F3(2, 0x30, 1)|RD(6), F3(~2, ~0x30, ~1)|RD(~6), "1,i,s", 0, v9 }, /* wr r,i,%fprs */
+
+{ "wr", F3(2, 0x30, 0)|RD(16), F3(~2, ~0x30, ~0)|RD(~16)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pcr */
+{ "wr", F3(2, 0x30, 1)|RD(16), F3(~2, ~0x30, ~1)|RD(~16), "1,i,_", 0, v9a }, /* wr r,i,%pcr */
+{ "wr", F3(2, 0x30, 0)|RD(17), F3(~2, ~0x30, ~0)|RD(~17)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%pic */
+{ "wr", F3(2, 0x30, 1)|RD(17), F3(~2, ~0x30, ~1)|RD(~17), "1,i,_", 0, v9a }, /* wr r,i,%pic */
+{ "wr", F3(2, 0x30, 0)|RD(18), F3(~2, ~0x30, ~0)|RD(~18)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%dcr */
+{ "wr", F3(2, 0x30, 1)|RD(18), F3(~2, ~0x30, ~1)|RD(~18), "1,i,_", 0, v9a }, /* wr r,i,%dcr */
+{ "wr", F3(2, 0x30, 0)|RD(19), F3(~2, ~0x30, ~0)|RD(~19)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%gsr */
+{ "wr", F3(2, 0x30, 1)|RD(19), F3(~2, ~0x30, ~1)|RD(~19), "1,i,_", 0, v9a }, /* wr r,i,%gsr */
+{ "wr", F3(2, 0x30, 0)|RD(20), F3(~2, ~0x30, ~0)|RD(~20)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%set_softint */
+{ "wr", F3(2, 0x30, 1)|RD(20), F3(~2, ~0x30, ~1)|RD(~20), "1,i,_", 0, v9a }, /* wr r,i,%set_softint */
+{ "wr", F3(2, 0x30, 0)|RD(21), F3(~2, ~0x30, ~0)|RD(~21)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%clear_softint */
+{ "wr", F3(2, 0x30, 1)|RD(21), F3(~2, ~0x30, ~1)|RD(~21), "1,i,_", 0, v9a }, /* wr r,i,%clear_softint */
+{ "wr", F3(2, 0x30, 0)|RD(22), F3(~2, ~0x30, ~0)|RD(~22)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%softint */
+{ "wr", F3(2, 0x30, 1)|RD(22), F3(~2, ~0x30, ~1)|RD(~22), "1,i,_", 0, v9a }, /* wr r,i,%softint */
+{ "wr", F3(2, 0x30, 0)|RD(23), F3(~2, ~0x30, ~0)|RD(~23)|ASI(~0), "1,2,_", 0, v9a }, /* wr r,r,%tick_cmpr */
+{ "wr", F3(2, 0x30, 1)|RD(23), F3(~2, ~0x30, ~1)|RD(~23), "1,i,_", 0, v9a }, /* wr r,i,%tick_cmpr */
+{ "wr", F3(2, 0x30, 0)|RD(24), F3(~2, ~0x30, ~0)|RD(~24)|ASI(~0), "1,2,_", 0, v9b }, /* wr r,r,%sys_tick */
+{ "wr", F3(2, 0x30, 1)|RD(24), F3(~2, ~0x30, ~1)|RD(~24), "1,i,_", 0, v9b }, /* wr r,i,%sys_tick */
+{ "wr", F3(2, 0x30, 0)|RD(25), F3(~2, ~0x30, ~0)|RD(~25)|ASI(~0), "1,2,_", 0, v9b }, /* wr r,r,%sys_tick_cmpr */
+{ "wr", F3(2, 0x30, 1)|RD(25), F3(~2, ~0x30, ~1)|RD(~25), "1,i,_", 0, v9b }, /* wr r,i,%sys_tick_cmpr */
+
+{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", 0, v8 }, /* rd %asrX,r */
+{ "rd", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", 0, v6 }, /* rd %y,r */
+{ "rd", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", 0, v6notv9 }, /* rd %psr,r */
+{ "rd", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", 0, v6notv9 }, /* rd %wim,r */
+{ "rd", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", 0, v6notv9 }, /* rd %tbr,r */
+
+{ "rd", F3(2, 0x28, 0)|RS1(2), F3(~2, ~0x28, ~0)|RS1(~2)|SIMM13(~0), "E,d", 0, v9 }, /* rd %ccr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(3), F3(~2, ~0x28, ~0)|RS1(~3)|SIMM13(~0), "o,d", 0, v9 }, /* rd %asi,r */
+{ "rd", F3(2, 0x28, 0)|RS1(4), F3(~2, ~0x28, ~0)|RS1(~4)|SIMM13(~0), "W,d", 0, v9 }, /* rd %tick,r */
+{ "rd", F3(2, 0x28, 0)|RS1(5), F3(~2, ~0x28, ~0)|RS1(~5)|SIMM13(~0), "P,d", 0, v9 }, /* rd %pc,r */
+{ "rd", F3(2, 0x28, 0)|RS1(6), F3(~2, ~0x28, ~0)|RS1(~6)|SIMM13(~0), "s,d", 0, v9 }, /* rd %fprs,r */
+
+{ "rd", F3(2, 0x28, 0)|RS1(16), F3(~2, ~0x28, ~0)|RS1(~16)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pcr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(17), F3(~2, ~0x28, ~0)|RS1(~17)|SIMM13(~0), "/,d", 0, v9a }, /* rd %pic,r */
+{ "rd", F3(2, 0x28, 0)|RS1(18), F3(~2, ~0x28, ~0)|RS1(~18)|SIMM13(~0), "/,d", 0, v9a }, /* rd %dcr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(19), F3(~2, ~0x28, ~0)|RS1(~19)|SIMM13(~0), "/,d", 0, v9a }, /* rd %gsr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(22), F3(~2, ~0x28, ~0)|RS1(~22)|SIMM13(~0), "/,d", 0, v9a }, /* rd %softint,r */
+{ "rd", F3(2, 0x28, 0)|RS1(23), F3(~2, ~0x28, ~0)|RS1(~23)|SIMM13(~0), "/,d", 0, v9a }, /* rd %tick_cmpr,r */
+{ "rd", F3(2, 0x28, 0)|RS1(24), F3(~2, ~0x28, ~0)|RS1(~24)|SIMM13(~0), "/,d", 0, v9b }, /* rd %sys_tick,r */
+{ "rd", F3(2, 0x28, 0)|RS1(25), F3(~2, ~0x28, ~0)|RS1(~25)|SIMM13(~0), "/,d", 0, v9b }, /* rd %sys_tick_cmpr,r */
+
+{ "rdpr", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|SIMM13(~0), "?,d", 0, v9 }, /* rdpr %priv,r */
+{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0), "1,2,!", 0, v9 }, /* wrpr r1,r2,%priv */
+{ "wrpr", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|SIMM13(~0), "1,!", 0, v9 }, /* wrpr r1,%priv */
+{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "1,i,!", 0, v9 }, /* wrpr r1,i,%priv */
+{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1), "i,1,!", F_ALIAS, v9 }, /* wrpr i,r1,%priv */
+{ "wrpr", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RS1(~0), "i,!", 0, v9 }, /* wrpr i,%priv */
+
+{ "rdhpr", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|SIMM13(~0), "$,d", 0, v9 }, /* rdhpr %hpriv,r */
+{ "wrhpr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0), "1,2,%", 0, v9 }, /* wrhpr r1,r2,%hpriv */
+{ "wrhpr", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|SIMM13(~0), "1,%", 0, v9 }, /* wrhpr r1,%hpriv */
+{ "wrhpr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1), "1,i,%", 0, v9 }, /* wrhpr r1,i,%hpriv */
+{ "wrhpr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1), "i,1,%", F_ALIAS, v9 }, /* wrhpr i,r1,%hpriv */
+{ "wrhpr", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RS1(~0), "i,%", 0, v9 }, /* wrhpr i,%hpriv */
+
+/* ??? This group seems wrong. A three operand move? */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI(~0), "1,2,m", F_ALIAS, v8 }, /* wr r,r,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "1,i,m", F_ALIAS, v8 }, /* wr r,i,%asrX */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI(~0), "1,2,y", F_ALIAS, v6 }, /* wr r,r,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "1,i,y", F_ALIAS, v6 }, /* wr r,i,%y */
+{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI(~0), "1,2,p", F_ALIAS, v6notv9 }, /* wr r,r,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "1,i,p", F_ALIAS, v6notv9 }, /* wr r,i,%psr */
+{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI(~0), "1,2,w", F_ALIAS, v6notv9 }, /* wr r,r,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "1,i,w", F_ALIAS, v6notv9 }, /* wr r,i,%wim */
+{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI(~0), "1,2,t", F_ALIAS, v6notv9 }, /* wr r,r,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "1,i,t", F_ALIAS, v6notv9 }, /* wr r,i,%tbr */
+
+{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|SIMM13(~0), "M,d", F_ALIAS, v8 }, /* rd %asr1,r */
+{ "mov", F3(2, 0x28, 0), F3(~2, ~0x28, ~0)|RS1_G0|SIMM13(~0), "y,d", F_ALIAS, v6 }, /* rd %y,r */
+{ "mov", F3(2, 0x29, 0), F3(~2, ~0x29, ~0)|RS1_G0|SIMM13(~0), "p,d", F_ALIAS, v6notv9 }, /* rd %psr,r */
+{ "mov", F3(2, 0x2a, 0), F3(~2, ~0x2a, ~0)|RS1_G0|SIMM13(~0), "w,d", F_ALIAS, v6notv9 }, /* rd %wim,r */
+{ "mov", F3(2, 0x2b, 0), F3(~2, ~0x2b, ~0)|RS1_G0|SIMM13(~0), "t,d", F_ALIAS, v6notv9 }, /* rd %tbr,r */
+
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|ASI_RS2(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,%g0,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1), "i,m", F_ALIAS, v8 }, /* wr %g0,i,%asrX */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|SIMM13(~0), "1,m", F_ALIAS, v8 }, /* wr rs1,0,%asrX */
+{ "mov", F3(2, 0x30, 0), F3(~2, ~0x30, ~0)|RD_G0|ASI_RS2(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,%g0,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0, "i,y", F_ALIAS, v6 }, /* wr %g0,i,%y */
+{ "mov", F3(2, 0x30, 1), F3(~2, ~0x30, ~1)|RD_G0|SIMM13(~0), "1,y", F_ALIAS, v6 }, /* wr rs1,0,%y */
+{ "mov", F3(2, 0x31, 0), F3(~2, ~0x31, ~0)|RD_G0|ASI_RS2(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0, "i,p", F_ALIAS, v6notv9 }, /* wr %g0,i,%psr */
+{ "mov", F3(2, 0x31, 1), F3(~2, ~0x31, ~1)|RD_G0|SIMM13(~0), "1,p", F_ALIAS, v6notv9 }, /* wr rs1,0,%psr */
+{ "mov", F3(2, 0x32, 0), F3(~2, ~0x32, ~0)|RD_G0|ASI_RS2(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0, "i,w", F_ALIAS, v6notv9 }, /* wr %g0,i,%wim */
+{ "mov", F3(2, 0x32, 1), F3(~2, ~0x32, ~1)|RD_G0|SIMM13(~0), "1,w", F_ALIAS, v6notv9 }, /* wr rs1,0,%wim */
+{ "mov", F3(2, 0x33, 0), F3(~2, ~0x33, ~0)|RD_G0|ASI_RS2(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,%g0,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0, "i,t", F_ALIAS, v6notv9 }, /* wr %g0,i,%tbr */
+{ "mov", F3(2, 0x33, 1), F3(~2, ~0x33, ~1)|RD_G0|SIMM13(~0), "1,t", F_ALIAS, v6notv9 }, /* wr rs1,0,%tbr */
+
+{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|RS1_G0|ASI(~0), "2,d", 0, v6 }, /* or %g0,rs2,d */
+{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|RS1_G0, "i,d", 0, v6 }, /* or %g0,i,d */
+{ "mov", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI_RS2(~0), "1,d", 0, v6 }, /* or rs1,%g0,d */
+{ "mov", F3(2, 0x02, 1), F3(~2, ~0x02, ~1)|SIMM13(~0), "1,d", 0, v6 }, /* or rs1,0,d */
+
+{ "or", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "1,i,d", 0, v6 },
+{ "or", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,1,d", 0, v6 },
+
+{ "bset", F3(2, 0x02, 0), F3(~2, ~0x02, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* or rd,rs2,rd */
+{ "bset", F3(2, 0x02, 1), F3(~2, ~0x02, ~1), "i,r", F_ALIAS, v6 }, /* or rd,i,rd */
+
+/* This is not a commutative instruction. */
+{ "andn", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andn", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "1,i,d", 0, v6 },
+
+/* This is not a commutative instruction. */
+{ "andncc", F3(2, 0x15, 0), F3(~2, ~0x15, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andncc", F3(2, 0x15, 1), F3(~2, ~0x15, ~1), "1,i,d", 0, v6 },
+
+{ "bclr", F3(2, 0x05, 0), F3(~2, ~0x05, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* andn rd,rs2,rd */
+{ "bclr", F3(2, 0x05, 1), F3(~2, ~0x05, ~1), "i,r", F_ALIAS, v6 }, /* andn rd,i,rd */
+
+{ "cmp", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|RD_G0|ASI(~0), "1,2", 0, v6 }, /* subcc rs1,rs2,%g0 */
+{ "cmp", F3(2, 0x14, 1), F3(~2, ~0x14, ~1)|RD_G0, "1,i", 0, v6 }, /* subcc rs1,i,%g0 */
+
+{ "sub", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "sub", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "1,i,d", 0, v6 },
+
+{ "subcc", F3(2, 0x14, 0), F3(~2, ~0x14, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "subcc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "1,i,d", 0, v6 },
+
+{ "subx", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "subx", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v6notv9 },
+{ "subc", F3(2, 0x0c, 0), F3(~2, ~0x0c, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "subc", F3(2, 0x0c, 1), F3(~2, ~0x0c, ~1), "1,i,d", 0, v9 },
+
+{ "subxcc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "subxcc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v6notv9 },
+{ "subccc", F3(2, 0x1c, 0), F3(~2, ~0x1c, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "subccc", F3(2, 0x1c, 1), F3(~2, ~0x1c, ~1), "1,i,d", 0, v9 },
+
+{ "and", F3(2, 0x01, 0), F3(~2, ~0x01, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "1,i,d", 0, v6 },
+{ "and", F3(2, 0x01, 1), F3(~2, ~0x01, ~1), "i,1,d", 0, v6 },
+
+{ "andcc", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "1,i,d", 0, v6 },
+{ "andcc", F3(2, 0x11, 1), F3(~2, ~0x11, ~1), "i,1,d", 0, v6 },
+
+{ "dec", F3(2, 0x04, 1)|SIMM13(0x1), F3(~2, ~0x04, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* sub rd,1,rd */
+{ "dec", F3(2, 0x04, 1), F3(~2, ~0x04, ~1), "i,r", F_ALIAS, v8 }, /* sub rd,imm,rd */
+{ "deccc", F3(2, 0x14, 1)|SIMM13(0x1), F3(~2, ~0x14, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* subcc rd,1,rd */
+{ "deccc", F3(2, 0x14, 1), F3(~2, ~0x14, ~1), "i,r", F_ALIAS, v8 }, /* subcc rd,imm,rd */
+{ "inc", F3(2, 0x00, 1)|SIMM13(0x1), F3(~2, ~0x00, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* add rd,1,rd */
+{ "inc", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,r", F_ALIAS, v8 }, /* add rd,imm,rd */
+{ "inccc", F3(2, 0x10, 1)|SIMM13(0x1), F3(~2, ~0x10, ~1)|SIMM13(~0x0001), "r", F_ALIAS, v6 }, /* addcc rd,1,rd */
+{ "inccc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,r", F_ALIAS, v8 }, /* addcc rd,imm,rd */
+
+{ "btst", F3(2, 0x11, 0), F3(~2, ~0x11, ~0)|RD_G0|ASI(~0), "1,2", F_ALIAS, v6 }, /* andcc rs1,rs2,%g0 */
+{ "btst", F3(2, 0x11, 1), F3(~2, ~0x11, ~1)|RD_G0, "i,1", F_ALIAS, v6 }, /* andcc rs1,i,%g0 */
+
+{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "2,d", F_ALIAS, v6 }, /* sub %g0,rs2,rd */
+{ "neg", F3(2, 0x04, 0), F3(~2, ~0x04, ~0)|RS1_G0|ASI(~0), "O", F_ALIAS, v6 }, /* sub %g0,rd,rd */
+
+{ "add", F3(2, 0x00, 0), F3(~2, ~0x00, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "1,i,d", 0, v6 },
+{ "add", F3(2, 0x00, 1), F3(~2, ~0x00, ~1), "i,1,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 0), F3(~2, ~0x10, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "1,i,d", 0, v6 },
+{ "addcc", F3(2, 0x10, 1), F3(~2, ~0x10, ~1), "i,1,d", 0, v6 },
+
+{ "addx", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v6notv9 },
+{ "addx", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v6notv9 },
+{ "addc", F3(2, 0x08, 0), F3(~2, ~0x08, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "1,i,d", 0, v9 },
+{ "addc", F3(2, 0x08, 1), F3(~2, ~0x08, ~1), "i,1,d", 0, v9 },
+
+{ "addxcc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v6notv9 },
+{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v6notv9 },
+{ "addxcc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v6notv9 },
+{ "addccc", F3(2, 0x18, 0), F3(~2, ~0x18, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "1,i,d", 0, v9 },
+{ "addccc", F3(2, 0x18, 1), F3(~2, ~0x18, ~1), "i,1,d", 0, v9 },
+
+{ "smul", F3(2, 0x0b, 0), F3(~2, ~0x0b, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "1,i,d", 0, v8 },
+{ "smul", F3(2, 0x0b, 1), F3(~2, ~0x0b, ~1), "i,1,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 0), F3(~2, ~0x1b, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "1,i,d", 0, v8 },
+{ "smulcc", F3(2, 0x1b, 1), F3(~2, ~0x1b, ~1), "i,1,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 0), F3(~2, ~0x0a, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "1,i,d", 0, v8 },
+{ "umul", F3(2, 0x0a, 1), F3(~2, ~0x0a, ~1), "i,1,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 0), F3(~2, ~0x1a, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "1,i,d", 0, v8 },
+{ "umulcc", F3(2, 0x1a, 1), F3(~2, ~0x1a, ~1), "i,1,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 0), F3(~2, ~0x0f, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "1,i,d", 0, v8 },
+{ "sdiv", F3(2, 0x0f, 1), F3(~2, ~0x0f, ~1), "i,1,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 0), F3(~2, ~0x1f, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "1,i,d", 0, v8 },
+{ "sdivcc", F3(2, 0x1f, 1), F3(~2, ~0x1f, ~1), "i,1,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 0), F3(~2, ~0x0e, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "1,i,d", 0, v8 },
+{ "udiv", F3(2, 0x0e, 1), F3(~2, ~0x0e, ~1), "i,1,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 0), F3(~2, ~0x1e, ~0)|ASI(~0), "1,2,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "1,i,d", 0, v8 },
+{ "udivcc", F3(2, 0x1e, 1), F3(~2, ~0x1e, ~1), "i,1,d", 0, v8 },
+
+{ "mulx", F3(2, 0x09, 0), F3(~2, ~0x09, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "mulx", F3(2, 0x09, 1), F3(~2, ~0x09, ~1), "1,i,d", 0, v9 },
+{ "sdivx", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "sdivx", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, v9 },
+{ "udivx", F3(2, 0x0d, 0), F3(~2, ~0x0d, ~0)|ASI(~0), "1,2,d", 0, v9 },
+{ "udivx", F3(2, 0x0d, 1), F3(~2, ~0x0d, ~1), "1,i,d", 0, v9 },
+
+{ "call", F1(0x1), F1(~0x1), "L", F_JSR|F_DELAYED, v6 },
+{ "call", F1(0x1), F1(~0x1), "L,#", F_JSR|F_DELAYED, v6 },
+
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%o7 */
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI(~0), "1+2,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%o7 */
+{ "call", F3(2, 0x38, 0)|RD(0xf), F3(~2, ~0x38, ~0)|RD(~0xf)|ASI_RS2(~0), "1,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+i,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "1+i,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1", F_JSR|F_DELAYED, v6 }, /* jmpl i+rs1,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf), "i+1,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i", F_JSR|F_DELAYED, v6 }, /* jmpl %g0+i,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|RS1_G0, "i,#", F_JSR|F_DELAYED, v6 },
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1", F_JSR|F_DELAYED, v6 }, /* jmpl rs1+0,%o7 */
+{ "call", F3(2, 0x38, 1)|RD(0xf), F3(~2, ~0x38, ~1)|RD(~0xf)|SIMM13(~0), "1,#", F_JSR|F_DELAYED, v6 },
+
+
+/* Conditional instructions.
+
+ Because this part of the table was such a mess earlier, I have
+ macrofied it so that all the branches and traps are generated from
+ a single-line description of each condition value. John Gilmore. */
+
+/* Define branches -- one annulled, one without, etc. */
+#define br(opcode, mask, lose, flags) \
+ { opcode, (mask)|ANNUL, (lose), ",a l", (flags), v6 }, \
+ { opcode, (mask) , (lose)|ANNUL, "l", (flags), v6 }
+
+#define brx(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), "Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|BPRED, ANNUL|(lose), ",T Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|BPRED|ANNUL, (lose), ",a,T Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20), ANNUL|BPRED|(lose), ",N Z,G", (flags), v9 }, \
+ { opcode, (mask)|(2<<20)|ANNUL, BPRED|(lose), ",a,N Z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), "z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED, ANNUL|(lose)|(2<<20), ",T z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a z,G", (flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose)|(2<<20), ",a,T z,G", (flags), v9 }, \
+ { opcode, (mask), ANNUL|BPRED|(lose)|(2<<20), ",N z,G", (flags), v9 }, \
+ { opcode, (mask)|ANNUL, BPRED|(lose)|(2<<20), ",a,N z,G", (flags), v9 }
+
+/* Define four traps: reg+reg, reg + immediate, immediate alone, reg alone. */
+#define tr(opcode, mask, lose, flags) \
+ { opcode, (mask)|(2<<11)|IMMED, (lose)|RS1_G0, "Z,i", (flags), v9 }, /* %g0 + imm */ \
+ { opcode, (mask)|(2<<11)|IMMED, (lose), "Z,1+i", (flags), v9 }, /* rs1 + imm */ \
+ { opcode, (mask)|(2<<11), IMMED|(lose), "Z,1+2", (flags), v9 }, /* rs1 + rs2 */ \
+ { opcode, (mask)|(2<<11), IMMED|(lose)|RS2_G0, "Z,1", (flags), v9 }, /* rs1 + %g0 */ \
+ { opcode, (mask)|IMMED, (lose)|RS1_G0, "z,i", (flags)|F_ALIAS, v9 }, /* %g0 + imm */ \
+ { opcode, (mask)|IMMED, (lose), "z,1+i", (flags)|F_ALIAS, v9 }, /* rs1 + imm */ \
+ { opcode, (mask), IMMED|(lose), "z,1+2", (flags)|F_ALIAS, v9 }, /* rs1 + rs2 */ \
+ { opcode, (mask), IMMED|(lose)|RS2_G0, "z,1", (flags)|F_ALIAS, v9 }, /* rs1 + %g0 */ \
+ { opcode, (mask)|IMMED, (lose)|RS1_G0, "i", (flags), v6 }, /* %g0 + imm */ \
+ { opcode, (mask)|IMMED, (lose), "1+i", (flags), v6 }, /* rs1 + imm */ \
+ { opcode, (mask), IMMED|(lose), "1+2", (flags), v6 }, /* rs1 + rs2 */ \
+ { opcode, (mask), IMMED|(lose)|RS2_G0, "1", (flags), v6 } /* rs1 + %g0 */
+
+/* v9: We must put `brx' before `br', to ensure that we never match something
+ v9: against an expression unless it is an expression. Otherwise, we end
+ v9: up with undefined symbol tables entries, because they get added, but
+ v9: are not deleted if the pattern fails to match. */
+
+/* Define both branches and traps based on condition mask */
+#define cond(bop, top, mask, flags) \
+ brx(bop, F2(0, 1)|(mask), F2(~0, ~1)|((~mask)&COND(~0)), F_DELAYED|(flags)), /* v9 */ \
+ br(bop, F2(0, 2)|(mask), F2(~0, ~2)|((~mask)&COND(~0)), F_DELAYED|(flags)), \
+ tr(top, F3(2, 0x3a, 0)|(mask), F3(~2, ~0x3a, 0)|((~mask)&COND(~0)), ((flags) & ~(F_UNBR|F_CONDBR)))
+
+/* Define all the conditions, all the branches, all the traps. */
+
+/* Standard branch, trap mnemonics */
+cond ("b", "ta", CONDA, F_UNBR),
+/* Alternative form (just for assembly, not for disassembly) */
+cond ("ba", "t", CONDA, F_UNBR|F_ALIAS),
+
+cond ("bcc", "tcc", CONDCC, F_CONDBR),
+cond ("bcs", "tcs", CONDCS, F_CONDBR),
+cond ("be", "te", CONDE, F_CONDBR),
+cond ("beq", "teq", CONDE, F_CONDBR|F_ALIAS),
+cond ("bg", "tg", CONDG, F_CONDBR),
+cond ("bgt", "tgt", CONDG, F_CONDBR|F_ALIAS),
+cond ("bge", "tge", CONDGE, F_CONDBR),
+cond ("bgeu", "tgeu", CONDGEU, F_CONDBR|F_ALIAS), /* for cc */
+cond ("bgu", "tgu", CONDGU, F_CONDBR),
+cond ("bl", "tl", CONDL, F_CONDBR),
+cond ("blt", "tlt", CONDL, F_CONDBR|F_ALIAS),
+cond ("ble", "tle", CONDLE, F_CONDBR),
+cond ("bleu", "tleu", CONDLEU, F_CONDBR),
+cond ("blu", "tlu", CONDLU, F_CONDBR|F_ALIAS), /* for cs */
+cond ("bn", "tn", CONDN, F_CONDBR),
+cond ("bne", "tne", CONDNE, F_CONDBR),
+cond ("bneg", "tneg", CONDNEG, F_CONDBR),
+cond ("bnz", "tnz", CONDNZ, F_CONDBR|F_ALIAS), /* for ne */
+cond ("bpos", "tpos", CONDPOS, F_CONDBR),
+cond ("bvc", "tvc", CONDVC, F_CONDBR),
+cond ("bvs", "tvs", CONDVS, F_CONDBR),
+cond ("bz", "tz", CONDZ, F_CONDBR|F_ALIAS), /* for e */
+
+#undef cond
+#undef br
+#undef brr /* v9 */
+#undef tr
+
+#define brr(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask)|BPRED, ANNUL|(lose), "1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|BPRED, ANNUL|(lose), ",T 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose), ",a 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|BPRED|ANNUL, (lose), ",a,T 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask), ANNUL|BPRED|(lose), ",N 1,k", F_DELAYED|(flags), v9 }, \
+ { opcode, (mask)|ANNUL, BPRED|(lose), ",a,N 1,k", F_DELAYED|(flags), v9 }
+
+#define condr(bop, mask, flags) /* v9 */ \
+ brr(bop, F2(0, 3)|COND(mask), F2(~0, ~3)|COND(~(mask)), (flags)) /* v9 */
+
+/* v9 */ condr("brnz", 0x5, F_CONDBR),
+/* v9 */ condr("brz", 0x1, F_CONDBR),
+/* v9 */ condr("brgez", 0x7, F_CONDBR),
+/* v9 */ condr("brlz", 0x3, F_CONDBR),
+/* v9 */ condr("brlez", 0x2, F_CONDBR),
+/* v9 */ condr("brgz", 0x6, F_CONDBR),
+
+#undef condr /* v9 */
+#undef brr /* v9 */
+
+#define movr(opcode, mask, flags) /* v9 */ \
+ { opcode, F3(2, 0x2f, 0)|RCOND(mask), F3(~2, ~0x2f, ~0)|RCOND(~(mask)), "1,2,d", (flags), v9 }, \
+ { opcode, F3(2, 0x2f, 1)|RCOND(mask), F3(~2, ~0x2f, ~1)|RCOND(~(mask)), "1,j,d", (flags), v9 }
+
+#define fmrrs(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask), (lose), "1,f,g", (flags) | F_FLOAT, v9 }
+#define fmrrd(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask), (lose), "1,B,H", (flags) | F_FLOAT, v9 }
+#define fmrrq(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, (mask), (lose), "1,R,J", (flags) | F_FLOAT, v9 }
+
+#define fmovrs(mop, mask, flags) /* v9 */ \
+ fmrrs(mop, F3(2, 0x35, 0)|OPF_LOW5(5)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~5)|RCOND(~(mask)), (flags)) /* v9 */
+#define fmovrd(mop, mask, flags) /* v9 */ \
+ fmrrd(mop, F3(2, 0x35, 0)|OPF_LOW5(6)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~6)|RCOND(~(mask)), (flags)) /* v9 */
+#define fmovrq(mop, mask, flags) /* v9 */ \
+ fmrrq(mop, F3(2, 0x35, 0)|OPF_LOW5(7)|RCOND(mask), F3(~2, ~0x35, 0)|OPF_LOW5(~7)|RCOND(~(mask)), (flags)) /* v9 */
+
+/* v9 */ movr("movrne", 0x5, 0),
+/* v9 */ movr("movre", 0x1, 0),
+/* v9 */ movr("movrgez", 0x7, 0),
+/* v9 */ movr("movrlz", 0x3, 0),
+/* v9 */ movr("movrlez", 0x2, 0),
+/* v9 */ movr("movrgz", 0x6, 0),
+/* v9 */ movr("movrnz", 0x5, F_ALIAS),
+/* v9 */ movr("movrz", 0x1, F_ALIAS),
+
+/* v9 */ fmovrs("fmovrsne", 0x5, 0),
+/* v9 */ fmovrs("fmovrse", 0x1, 0),
+/* v9 */ fmovrs("fmovrsgez", 0x7, 0),
+/* v9 */ fmovrs("fmovrslz", 0x3, 0),
+/* v9 */ fmovrs("fmovrslez", 0x2, 0),
+/* v9 */ fmovrs("fmovrsgz", 0x6, 0),
+/* v9 */ fmovrs("fmovrsnz", 0x5, F_ALIAS),
+/* v9 */ fmovrs("fmovrsz", 0x1, F_ALIAS),
+
+/* v9 */ fmovrd("fmovrdne", 0x5, 0),
+/* v9 */ fmovrd("fmovrde", 0x1, 0),
+/* v9 */ fmovrd("fmovrdgez", 0x7, 0),
+/* v9 */ fmovrd("fmovrdlz", 0x3, 0),
+/* v9 */ fmovrd("fmovrdlez", 0x2, 0),
+/* v9 */ fmovrd("fmovrdgz", 0x6, 0),
+/* v9 */ fmovrd("fmovrdnz", 0x5, F_ALIAS),
+/* v9 */ fmovrd("fmovrdz", 0x1, F_ALIAS),
+
+/* v9 */ fmovrq("fmovrqne", 0x5, 0),
+/* v9 */ fmovrq("fmovrqe", 0x1, 0),
+/* v9 */ fmovrq("fmovrqgez", 0x7, 0),
+/* v9 */ fmovrq("fmovrqlz", 0x3, 0),
+/* v9 */ fmovrq("fmovrqlez", 0x2, 0),
+/* v9 */ fmovrq("fmovrqgz", 0x6, 0),
+/* v9 */ fmovrq("fmovrqnz", 0x5, F_ALIAS),
+/* v9 */ fmovrq("fmovrqz", 0x1, F_ALIAS),
+
+#undef movr /* v9 */
+#undef fmovr /* v9 */
+#undef fmrr /* v9 */
+
+#define movicc(opcode, cond, flags) /* v9 */ \
+ { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|XCC|(1<<11), "z,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|ICC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|XCC|(1<<11), "z,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~0)|MCOND(~cond,~1)|(1<<11), "Z,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|MCOND(cond,1)|XCC, F3(~2, ~0x2c, ~1)|MCOND(~cond,~1)|(1<<11), "Z,I,d", flags, v9 }
+
+#define movfcc(opcode, fcond, flags) /* v9 */ \
+ { opcode, F3(2, 0x2c, 0)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~0), "6,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(0)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~0)|F3(~2, ~0x2c, ~1), "6,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~0), "7,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(1)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~1)|F3(~2, ~0x2c, ~1), "7,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~0), "8,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(2)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~2)|F3(~2, ~0x2c, ~1), "8,I,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 0)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~0), "9,2,d", flags, v9 }, \
+ { opcode, F3(2, 0x2c, 1)|FCC(3)|MCOND(fcond,0), MCOND(~fcond,~0)|FCC(~3)|F3(~2, ~0x2c, ~1), "9,I,d", flags, v9 }
+
+#define movcc(opcode, cond, fcond, flags) /* v9 */ \
+ movfcc (opcode, fcond, flags), /* v9 */ \
+ movicc (opcode, cond, flags) /* v9 */
+
+/* v9 */ movcc ("mova", CONDA, FCONDA, 0),
+/* v9 */ movicc ("movcc", CONDCC, 0),
+/* v9 */ movicc ("movgeu", CONDGEU, F_ALIAS),
+/* v9 */ movicc ("movcs", CONDCS, 0),
+/* v9 */ movicc ("movlu", CONDLU, F_ALIAS),
+/* v9 */ movcc ("move", CONDE, FCONDE, 0),
+/* v9 */ movcc ("movg", CONDG, FCONDG, 0),
+/* v9 */ movcc ("movge", CONDGE, FCONDGE, 0),
+/* v9 */ movicc ("movgu", CONDGU, 0),
+/* v9 */ movcc ("movl", CONDL, FCONDL, 0),
+/* v9 */ movcc ("movle", CONDLE, FCONDLE, 0),
+/* v9 */ movicc ("movleu", CONDLEU, 0),
+/* v9 */ movfcc ("movlg", FCONDLG, 0),
+/* v9 */ movcc ("movn", CONDN, FCONDN, 0),
+/* v9 */ movcc ("movne", CONDNE, FCONDNE, 0),
+/* v9 */ movicc ("movneg", CONDNEG, 0),
+/* v9 */ movcc ("movnz", CONDNZ, FCONDNZ, F_ALIAS),
+/* v9 */ movfcc ("movo", FCONDO, 0),
+/* v9 */ movicc ("movpos", CONDPOS, 0),
+/* v9 */ movfcc ("movu", FCONDU, 0),
+/* v9 */ movfcc ("movue", FCONDUE, 0),
+/* v9 */ movfcc ("movug", FCONDUG, 0),
+/* v9 */ movfcc ("movuge", FCONDUGE, 0),
+/* v9 */ movfcc ("movul", FCONDUL, 0),
+/* v9 */ movfcc ("movule", FCONDULE, 0),
+/* v9 */ movicc ("movvc", CONDVC, 0),
+/* v9 */ movicc ("movvs", CONDVS, 0),
+/* v9 */ movcc ("movz", CONDZ, FCONDZ, F_ALIAS),
+
+#undef movicc /* v9 */
+#undef movfcc /* v9 */
+#undef movcc /* v9 */
+
+#define FM_SF 1 /* v9 - values for fpsize */
+#define FM_DF 2 /* v9 */
+#define FM_QF 3 /* v9 */
+
+#define fmoviccx(opcode, fpsize, args, cond, flags) /* v9 */ \
+{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z," args, flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z," args, flags, v9 }
+
+#define fmovfccx(opcode, fpsize, args, fcond, flags) /* v9 */ \
+{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6," args, flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7," args, flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8," args, flags, v9 }, \
+{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9," args, flags, v9 }
+
+/* FIXME: use fmovicc/fmovfcc? */ /* v9 */
+#define fmovccx(opcode, fpsize, args, cond, fcond, flags) /* v9 */ \
+{ opcode, F3F(2, 0x35, 0x100+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x100+fpsize))|MCOND(~cond,~0), "z," args, flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x000+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x000+fpsize))|MCOND(~fcond,~0), "6," args, flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x180+fpsize)|MCOND(cond,0), F3F(~2, ~0x35, ~(0x180+fpsize))|MCOND(~cond,~0), "Z," args, flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x040+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x040+fpsize))|MCOND(~fcond,~0), "7," args, flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x080+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x080+fpsize))|MCOND(~fcond,~0), "8," args, flags | F_FLOAT, v9 }, \
+{ opcode, F3F(2, 0x35, 0x0c0+fpsize)|MCOND(fcond,0), F3F(~2, ~0x35, ~(0x0c0+fpsize))|MCOND(~fcond,~0), "9," args, flags | F_FLOAT, v9 }
+
+#define fmovicc(suffix, cond, flags) /* v9 */ \
+fmoviccx("fmovd" suffix, FM_DF, "B,H", cond, flags), \
+fmoviccx("fmovq" suffix, FM_QF, "R,J", cond, flags), \
+fmoviccx("fmovs" suffix, FM_SF, "f,g", cond, flags)
+
+#define fmovfcc(suffix, fcond, flags) /* v9 */ \
+fmovfccx("fmovd" suffix, FM_DF, "B,H", fcond, flags), \
+fmovfccx("fmovq" suffix, FM_QF, "R,J", fcond, flags), \
+fmovfccx("fmovs" suffix, FM_SF, "f,g", fcond, flags)
+
+#define fmovcc(suffix, cond, fcond, flags) /* v9 */ \
+fmovccx("fmovd" suffix, FM_DF, "B,H", cond, fcond, flags), \
+fmovccx("fmovq" suffix, FM_QF, "R,J", cond, fcond, flags), \
+fmovccx("fmovs" suffix, FM_SF, "f,g", cond, fcond, flags)
+
+/* v9 */ fmovcc ("a", CONDA, FCONDA, 0),
+/* v9 */ fmovicc ("cc", CONDCC, 0),
+/* v9 */ fmovicc ("cs", CONDCS, 0),
+/* v9 */ fmovcc ("e", CONDE, FCONDE, 0),
+/* v9 */ fmovcc ("g", CONDG, FCONDG, 0),
+/* v9 */ fmovcc ("ge", CONDGE, FCONDGE, 0),
+/* v9 */ fmovicc ("geu", CONDGEU, F_ALIAS),
+/* v9 */ fmovicc ("gu", CONDGU, 0),
+/* v9 */ fmovcc ("l", CONDL, FCONDL, 0),
+/* v9 */ fmovcc ("le", CONDLE, FCONDLE, 0),
+/* v9 */ fmovicc ("leu", CONDLEU, 0),
+/* v9 */ fmovfcc ("lg", FCONDLG, 0),
+/* v9 */ fmovicc ("lu", CONDLU, F_ALIAS),
+/* v9 */ fmovcc ("n", CONDN, FCONDN, 0),
+/* v9 */ fmovcc ("ne", CONDNE, FCONDNE, 0),
+/* v9 */ fmovicc ("neg", CONDNEG, 0),
+/* v9 */ fmovcc ("nz", CONDNZ, FCONDNZ, F_ALIAS),
+/* v9 */ fmovfcc ("o", FCONDO, 0),
+/* v9 */ fmovicc ("pos", CONDPOS, 0),
+/* v9 */ fmovfcc ("u", FCONDU, 0),
+/* v9 */ fmovfcc ("ue", FCONDUE, 0),
+/* v9 */ fmovfcc ("ug", FCONDUG, 0),
+/* v9 */ fmovfcc ("uge", FCONDUGE, 0),
+/* v9 */ fmovfcc ("ul", FCONDUL, 0),
+/* v9 */ fmovfcc ("ule", FCONDULE, 0),
+/* v9 */ fmovicc ("vc", CONDVC, 0),
+/* v9 */ fmovicc ("vs", CONDVS, 0),
+/* v9 */ fmovcc ("z", CONDZ, FCONDZ, F_ALIAS),
+
+#undef fmoviccx /* v9 */
+#undef fmovfccx /* v9 */
+#undef fmovccx /* v9 */
+#undef fmovicc /* v9 */
+#undef fmovfcc /* v9 */
+#undef fmovcc /* v9 */
+#undef FM_DF /* v9 */
+#undef FM_QF /* v9 */
+#undef FM_SF /* v9 */
+
+/* Coprocessor branches. */
+#define CBR(opcode, mask, lose, flags, arch) \
+ { opcode, (mask), ANNUL | (lose), "l", flags | F_DELAYED, arch }, \
+ { opcode, (mask) | ANNUL, (lose), ",a l", flags | F_DELAYED, arch }
+
+/* Floating point branches. */
+#define FBR(opcode, mask, lose, flags) \
+ { opcode, (mask), ANNUL | (lose), "l", flags | F_DELAYED | F_FBR, v6 }, \
+ { opcode, (mask) | ANNUL, (lose), ",a l", flags | F_DELAYED | F_FBR, v6 }
+
+/* V9 extended floating point branches. */
+#define FBRX(opcode, mask, lose, flags) /* v9 */ \
+ { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), "6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|BPRED, ANNUL|FBFCC(~0)|(lose), ",T 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|BPRED|ANNUL, FBFCC(~0)|(lose), ",a,T 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask), ANNUL|BPRED|FBFCC(~0)|(lose), ",N 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(0)|(mask)|ANNUL, BPRED|FBFCC(~0)|(lose), ",a,N 6,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), "7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED, ANNUL|FBFCC(~1)|(lose), ",T 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|BPRED|ANNUL, FBFCC(~1)|(lose), ",a,T 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask), ANNUL|BPRED|FBFCC(~1)|(lose), ",N 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(1)|(mask)|ANNUL, BPRED|FBFCC(~1)|(lose), ",a,N 7,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), "8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED, ANNUL|FBFCC(~2)|(lose), ",T 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|BPRED|ANNUL, FBFCC(~2)|(lose), ",a,T 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask), ANNUL|BPRED|FBFCC(~2)|(lose), ",N 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(2)|(mask)|ANNUL, BPRED|FBFCC(~2)|(lose), ",a,N 8,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), "9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED, ANNUL|FBFCC(~3)|(lose), ",T 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|BPRED|ANNUL, FBFCC(~3)|(lose), ",a,T 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask), ANNUL|BPRED|FBFCC(~3)|(lose), ",N 9,G", flags|F_DELAYED|F_FBR, v9 }, \
+ { opcode, FBFCC(3)|(mask)|ANNUL, BPRED|FBFCC(~3)|(lose), ",a,N 9,G", flags|F_DELAYED|F_FBR, v9 }
+
+/* v9: We must put `FBRX' before `FBR', to ensure that we never match
+ v9: something against an expression unless it is an expression. Otherwise,
+ v9: we end up with undefined symbol tables entries, because they get added,
+ v9: but are not deleted if the pattern fails to match. */
+
+#define CONDFC(fop, cop, mask, flags) \
+ FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \
+ FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \
+ CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6notlet)
+
+#define CONDFCL(fop, cop, mask, flags) \
+ FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \
+ FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags), \
+ CBR(cop, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)), flags, v6)
+
+#define CONDF(fop, mask, flags) \
+ FBRX(fop, F2(0, 5)|COND(mask), F2(~0, ~5)|COND(~(mask)), flags), /* v9 */ \
+ FBR(fop, F2(0, 6)|COND(mask), F2(~0, ~6)|COND(~(mask)), flags)
+
+CONDFC ("fb", "cb", 0x8, F_UNBR),
+CONDFCL ("fba", "cba", 0x8, F_UNBR|F_ALIAS),
+CONDFC ("fbe", "cb0", 0x9, F_CONDBR),
+CONDF ("fbz", 0x9, F_CONDBR|F_ALIAS),
+CONDFC ("fbg", "cb2", 0x6, F_CONDBR),
+CONDFC ("fbge", "cb02", 0xb, F_CONDBR),
+CONDFC ("fbl", "cb1", 0x4, F_CONDBR),
+CONDFC ("fble", "cb01", 0xd, F_CONDBR),
+CONDFC ("fblg", "cb12", 0x2, F_CONDBR),
+CONDFCL ("fbn", "cbn", 0x0, F_UNBR),
+CONDFC ("fbne", "cb123", 0x1, F_CONDBR),
+CONDF ("fbnz", 0x1, F_CONDBR|F_ALIAS),
+CONDFC ("fbo", "cb012", 0xf, F_CONDBR),
+CONDFC ("fbu", "cb3", 0x7, F_CONDBR),
+CONDFC ("fbue", "cb03", 0xa, F_CONDBR),
+CONDFC ("fbug", "cb23", 0x5, F_CONDBR),
+CONDFC ("fbuge", "cb023", 0xc, F_CONDBR),
+CONDFC ("fbul", "cb13", 0x3, F_CONDBR),
+CONDFC ("fbule", "cb013", 0xe, F_CONDBR),
+
+#undef CONDFC
+#undef CONDFCL
+#undef CONDF
+#undef CBR
+#undef FBR
+#undef FBRX /* v9 */
+
+{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI(~0), "1+2", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+rs2,%g0 */
+{ "jmp", F3(2, 0x38, 0), F3(~2, ~0x38, ~0)|RD_G0|ASI_RS2(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+%g0,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "1+i", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+i,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0, "i+1", F_UNBR|F_DELAYED, v6 }, /* jmpl i+rs1,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|RS1_G0, "i", F_UNBR|F_DELAYED, v6 }, /* jmpl %g0+i,%g0 */
+{ "jmp", F3(2, 0x38, 1), F3(~2, ~0x38, ~1)|RD_G0|SIMM13(~0), "1", F_UNBR|F_DELAYED, v6 }, /* jmpl rs1+0,%g0 */
+
+{ "nop", F2(0, 4), 0xfeffffff, "", 0, v6 }, /* sethi 0, %g0 */
+
+{ "set", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v6 },
+{ "setuw", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v9 },
+{ "setsw", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,d", F_ALIAS, v9 },
+{ "setx", F2(0x0, 0x4), F2(~0x0, ~0x4), "S0,1,d", F_ALIAS, v9 },
+
+{ "sethi", F2(0x0, 0x4), F2(~0x0, ~0x4), "h,d", 0, v6 },
+
+{ "taddcc", F3(2, 0x20, 0), F3(~2, ~0x20, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "1,i,d", 0, v6 },
+{ "taddcc", F3(2, 0x20, 1), F3(~2, ~0x20, ~1), "i,1,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 0), F3(~2, ~0x22, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "1,i,d", 0, v6 },
+{ "taddcctv", F3(2, 0x22, 1), F3(~2, ~0x22, ~1), "i,1,d", 0, v6 },
+
+{ "tsubcc", F3(2, 0x21, 0), F3(~2, ~0x21, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "tsubcc", F3(2, 0x21, 1), F3(~2, ~0x21, ~1), "1,i,d", 0, v6 },
+{ "tsubcctv", F3(2, 0x23, 0), F3(~2, ~0x23, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "tsubcctv", F3(2, 0x23, 1), F3(~2, ~0x23, ~1), "1,i,d", 0, v6 },
+
+{ "unimp", F2(0x0, 0x0), 0xffc00000, "n", 0, v6notv9 },
+{ "illtrap", F2(0, 0), F2(~0, ~0)|RD_G0, "n", 0, v9 },
+
+/* This *is* a commutative instruction. */
+{ "xnor", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "1,i,d", 0, v6 },
+{ "xnor", F3(2, 0x07, 1), F3(~2, ~0x07, ~1), "i,1,d", 0, v6 },
+/* This *is* a commutative instruction. */
+{ "xnorcc", F3(2, 0x17, 0), F3(~2, ~0x17, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "1,i,d", 0, v6 },
+{ "xnorcc", F3(2, 0x17, 1), F3(~2, ~0x17, ~1), "i,1,d", 0, v6 },
+{ "xor", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "1,i,d", 0, v6 },
+{ "xor", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,1,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 0), F3(~2, ~0x13, ~0)|ASI(~0), "1,2,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "1,i,d", 0, v6 },
+{ "xorcc", F3(2, 0x13, 1), F3(~2, ~0x13, ~1), "i,1,d", 0, v6 },
+
+{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "1,d", F_ALIAS, v6 }, /* xnor rs1,%0,rd */
+{ "not", F3(2, 0x07, 0), F3(~2, ~0x07, ~0)|ASI(~0), "r", F_ALIAS, v6 }, /* xnor rd,%0,rd */
+
+{ "btog", F3(2, 0x03, 0), F3(~2, ~0x03, ~0)|ASI(~0), "2,r", F_ALIAS, v6 }, /* xor rd,rs2,rd */
+{ "btog", F3(2, 0x03, 1), F3(~2, ~0x03, ~1), "i,r", F_ALIAS, v6 }, /* xor rd,i,rd */
+
+/* FPop1 and FPop2 are not instructions. Don't accept them. */
+
+{ "fdtoi", F3F(2, 0x34, 0x0d2), F3F(~2, ~0x34, ~0x0d2)|RS1_G0, "B,g", F_FLOAT, v6 },
+{ "fstoi", F3F(2, 0x34, 0x0d1), F3F(~2, ~0x34, ~0x0d1)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fqtoi", F3F(2, 0x34, 0x0d3), F3F(~2, ~0x34, ~0x0d3)|RS1_G0, "R,g", F_FLOAT, v8 },
+
+{ "fdtox", F3F(2, 0x34, 0x082), F3F(~2, ~0x34, ~0x082)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fstox", F3F(2, 0x34, 0x081), F3F(~2, ~0x34, ~0x081)|RS1_G0, "f,H", F_FLOAT, v9 },
+{ "fqtox", F3F(2, 0x34, 0x083), F3F(~2, ~0x34, ~0x083)|RS1_G0, "R,H", F_FLOAT, v9 },
+
+{ "fitod", F3F(2, 0x34, 0x0c8), F3F(~2, ~0x34, ~0x0c8)|RS1_G0, "f,H", F_FLOAT, v6 },
+{ "fitos", F3F(2, 0x34, 0x0c4), F3F(~2, ~0x34, ~0x0c4)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fitoq", F3F(2, 0x34, 0x0cc), F3F(~2, ~0x34, ~0x0cc)|RS1_G0, "f,J", F_FLOAT, v8 },
+
+{ "fxtod", F3F(2, 0x34, 0x088), F3F(~2, ~0x34, ~0x088)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fxtos", F3F(2, 0x34, 0x084), F3F(~2, ~0x34, ~0x084)|RS1_G0, "B,g", F_FLOAT, v9 },
+{ "fxtoq", F3F(2, 0x34, 0x08c), F3F(~2, ~0x34, ~0x08c)|RS1_G0, "B,J", F_FLOAT, v9 },
+
+{ "fdtoq", F3F(2, 0x34, 0x0ce), F3F(~2, ~0x34, ~0x0ce)|RS1_G0, "B,J", F_FLOAT, v8 },
+{ "fdtos", F3F(2, 0x34, 0x0c6), F3F(~2, ~0x34, ~0x0c6)|RS1_G0, "B,g", F_FLOAT, v6 },
+{ "fqtod", F3F(2, 0x34, 0x0cb), F3F(~2, ~0x34, ~0x0cb)|RS1_G0, "R,H", F_FLOAT, v8 },
+{ "fqtos", F3F(2, 0x34, 0x0c7), F3F(~2, ~0x34, ~0x0c7)|RS1_G0, "R,g", F_FLOAT, v8 },
+{ "fstod", F3F(2, 0x34, 0x0c9), F3F(~2, ~0x34, ~0x0c9)|RS1_G0, "f,H", F_FLOAT, v6 },
+{ "fstoq", F3F(2, 0x34, 0x0cd), F3F(~2, ~0x34, ~0x0cd)|RS1_G0, "f,J", F_FLOAT, v8 },
+
+{ "fdivd", F3F(2, 0x34, 0x04e), F3F(~2, ~0x34, ~0x04e), "v,B,H", F_FLOAT, v6 },
+{ "fdivq", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT, v8 },
+{ "fdivx", F3F(2, 0x34, 0x04f), F3F(~2, ~0x34, ~0x04f), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fdivs", F3F(2, 0x34, 0x04d), F3F(~2, ~0x34, ~0x04d), "e,f,g", F_FLOAT, v6 },
+{ "fmuld", F3F(2, 0x34, 0x04a), F3F(~2, ~0x34, ~0x04a), "v,B,H", F_FLOAT, v6 },
+{ "fmulq", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT, v8 },
+{ "fmulx", F3F(2, 0x34, 0x04b), F3F(~2, ~0x34, ~0x04b), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fmuls", F3F(2, 0x34, 0x049), F3F(~2, ~0x34, ~0x049), "e,f,g", F_FLOAT, v6 },
+
+{ "fdmulq", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT, v8 },
+{ "fdmulx", F3F(2, 0x34, 0x06e), F3F(~2, ~0x34, ~0x06e), "v,B,J", F_FLOAT|F_ALIAS, v8 },
+{ "fsmuld", F3F(2, 0x34, 0x069), F3F(~2, ~0x34, ~0x069), "e,f,H", F_FLOAT, v8 },
+
+{ "fsqrtd", F3F(2, 0x34, 0x02a), F3F(~2, ~0x34, ~0x02a)|RS1_G0, "B,H", F_FLOAT, v7 },
+{ "fsqrtq", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT, v8 },
+{ "fsqrtx", F3F(2, 0x34, 0x02b), F3F(~2, ~0x34, ~0x02b)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fsqrts", F3F(2, 0x34, 0x029), F3F(~2, ~0x34, ~0x029)|RS1_G0, "f,g", F_FLOAT, v7 },
+
+{ "fabsd", F3F(2, 0x34, 0x00a), F3F(~2, ~0x34, ~0x00a)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fabsq", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT, v9 },
+{ "fabsx", F3F(2, 0x34, 0x00b), F3F(~2, ~0x34, ~0x00b)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 },
+{ "fabss", F3F(2, 0x34, 0x009), F3F(~2, ~0x34, ~0x009)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fmovd", F3F(2, 0x34, 0x002), F3F(~2, ~0x34, ~0x002)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fmovq", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT, v9 },
+{ "fmovx", F3F(2, 0x34, 0x003), F3F(~2, ~0x34, ~0x003)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 },
+{ "fmovs", F3F(2, 0x34, 0x001), F3F(~2, ~0x34, ~0x001)|RS1_G0, "f,g", F_FLOAT, v6 },
+{ "fnegd", F3F(2, 0x34, 0x006), F3F(~2, ~0x34, ~0x006)|RS1_G0, "B,H", F_FLOAT, v9 },
+{ "fnegq", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT, v9 },
+{ "fnegx", F3F(2, 0x34, 0x007), F3F(~2, ~0x34, ~0x007)|RS1_G0, "R,J", F_FLOAT|F_ALIAS, v9 },
+{ "fnegs", F3F(2, 0x34, 0x005), F3F(~2, ~0x34, ~0x005)|RS1_G0, "f,g", F_FLOAT, v6 },
+
+{ "faddd", F3F(2, 0x34, 0x042), F3F(~2, ~0x34, ~0x042), "v,B,H", F_FLOAT, v6 },
+{ "faddq", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT, v8 },
+{ "faddx", F3F(2, 0x34, 0x043), F3F(~2, ~0x34, ~0x043), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fadds", F3F(2, 0x34, 0x041), F3F(~2, ~0x34, ~0x041), "e,f,g", F_FLOAT, v6 },
+{ "fsubd", F3F(2, 0x34, 0x046), F3F(~2, ~0x34, ~0x046), "v,B,H", F_FLOAT, v6 },
+{ "fsubq", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT, v8 },
+{ "fsubx", F3F(2, 0x34, 0x047), F3F(~2, ~0x34, ~0x047), "V,R,J", F_FLOAT|F_ALIAS, v8 },
+{ "fsubs", F3F(2, 0x34, 0x045), F3F(~2, ~0x34, ~0x045), "e,f,g", F_FLOAT, v6 },
+
+#define CMPFCC(x) (((x)&0x3)<<25)
+
+{ "fcmpd", F3F(2, 0x35, 0x052), F3F(~2, ~0x35, ~0x052)|RD_G0, "v,B", F_FLOAT, v6 },
+{ "fcmpd", CMPFCC(0)|F3F(2, 0x35, 0x052), CMPFCC(~0)|F3F(~2, ~0x35, ~0x052), "6,v,B", F_FLOAT, v9 },
+{ "fcmpd", CMPFCC(1)|F3F(2, 0x35, 0x052), CMPFCC(~1)|F3F(~2, ~0x35, ~0x052), "7,v,B", F_FLOAT, v9 },
+{ "fcmpd", CMPFCC(2)|F3F(2, 0x35, 0x052), CMPFCC(~2)|F3F(~2, ~0x35, ~0x052), "8,v,B", F_FLOAT, v9 },
+{ "fcmpd", CMPFCC(3)|F3F(2, 0x35, 0x052), CMPFCC(~3)|F3F(~2, ~0x35, ~0x052), "9,v,B", F_FLOAT, v9 },
+{ "fcmped", F3F(2, 0x35, 0x056), F3F(~2, ~0x35, ~0x056)|RD_G0, "v,B", F_FLOAT, v6 },
+{ "fcmped", CMPFCC(0)|F3F(2, 0x35, 0x056), CMPFCC(~0)|F3F(~2, ~0x35, ~0x056), "6,v,B", F_FLOAT, v9 },
+{ "fcmped", CMPFCC(1)|F3F(2, 0x35, 0x056), CMPFCC(~1)|F3F(~2, ~0x35, ~0x056), "7,v,B", F_FLOAT, v9 },
+{ "fcmped", CMPFCC(2)|F3F(2, 0x35, 0x056), CMPFCC(~2)|F3F(~2, ~0x35, ~0x056), "8,v,B", F_FLOAT, v9 },
+{ "fcmped", CMPFCC(3)|F3F(2, 0x35, 0x056), CMPFCC(~3)|F3F(~2, ~0x35, ~0x056), "9,v,B", F_FLOAT, v9 },
+{ "fcmpq", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT, v8 },
+{ "fcmpq", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT, v9 },
+{ "fcmpq", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT, v9 },
+{ "fcmpq", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT, v9 },
+{ "fcmpq", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT, v9 },
+{ "fcmpeq", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT, v8 },
+{ "fcmpeq", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT, v9 },
+{ "fcmpeq", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT, v9 },
+{ "fcmpeq", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT, v9 },
+{ "fcmpeq", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT, v9 },
+{ "fcmpx", F3F(2, 0x35, 0x053), F3F(~2, ~0x35, ~0x053)|RD_G0, "V,R", F_FLOAT|F_ALIAS, v8 },
+{ "fcmpx", CMPFCC(0)|F3F(2, 0x35, 0x053), CMPFCC(~0)|F3F(~2, ~0x35, ~0x053), "6,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpx", CMPFCC(1)|F3F(2, 0x35, 0x053), CMPFCC(~1)|F3F(~2, ~0x35, ~0x053), "7,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpx", CMPFCC(2)|F3F(2, 0x35, 0x053), CMPFCC(~2)|F3F(~2, ~0x35, ~0x053), "8,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpx", CMPFCC(3)|F3F(2, 0x35, 0x053), CMPFCC(~3)|F3F(~2, ~0x35, ~0x053), "9,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", F3F(2, 0x35, 0x057), F3F(~2, ~0x35, ~0x057)|RD_G0, "V,R", F_FLOAT|F_ALIAS, v8 },
+{ "fcmpex", CMPFCC(0)|F3F(2, 0x35, 0x057), CMPFCC(~0)|F3F(~2, ~0x35, ~0x057), "6,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", CMPFCC(1)|F3F(2, 0x35, 0x057), CMPFCC(~1)|F3F(~2, ~0x35, ~0x057), "7,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", CMPFCC(2)|F3F(2, 0x35, 0x057), CMPFCC(~2)|F3F(~2, ~0x35, ~0x057), "8,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmpex", CMPFCC(3)|F3F(2, 0x35, 0x057), CMPFCC(~3)|F3F(~2, ~0x35, ~0x057), "9,V,R", F_FLOAT|F_ALIAS, v9 },
+{ "fcmps", F3F(2, 0x35, 0x051), F3F(~2, ~0x35, ~0x051)|RD_G0, "e,f", F_FLOAT, v6 },
+{ "fcmps", CMPFCC(0)|F3F(2, 0x35, 0x051), CMPFCC(~0)|F3F(~2, ~0x35, ~0x051), "6,e,f", F_FLOAT, v9 },
+{ "fcmps", CMPFCC(1)|F3F(2, 0x35, 0x051), CMPFCC(~1)|F3F(~2, ~0x35, ~0x051), "7,e,f", F_FLOAT, v9 },
+{ "fcmps", CMPFCC(2)|F3F(2, 0x35, 0x051), CMPFCC(~2)|F3F(~2, ~0x35, ~0x051), "8,e,f", F_FLOAT, v9 },
+{ "fcmps", CMPFCC(3)|F3F(2, 0x35, 0x051), CMPFCC(~3)|F3F(~2, ~0x35, ~0x051), "9,e,f", F_FLOAT, v9 },
+{ "fcmpes", F3F(2, 0x35, 0x055), F3F(~2, ~0x35, ~0x055)|RD_G0, "e,f", F_FLOAT, v6 },
+{ "fcmpes", CMPFCC(0)|F3F(2, 0x35, 0x055), CMPFCC(~0)|F3F(~2, ~0x35, ~0x055), "6,e,f", F_FLOAT, v9 },
+{ "fcmpes", CMPFCC(1)|F3F(2, 0x35, 0x055), CMPFCC(~1)|F3F(~2, ~0x35, ~0x055), "7,e,f", F_FLOAT, v9 },
+{ "fcmpes", CMPFCC(2)|F3F(2, 0x35, 0x055), CMPFCC(~2)|F3F(~2, ~0x35, ~0x055), "8,e,f", F_FLOAT, v9 },
+{ "fcmpes", CMPFCC(3)|F3F(2, 0x35, 0x055), CMPFCC(~3)|F3F(~2, ~0x35, ~0x055), "9,e,f", F_FLOAT, v9 },
+
+/* These Extended FPop (FIFO) instructions are new in the Fujitsu
+ MB86934, replacing the CPop instructions from v6 and later
+ processors. */
+
+#define EFPOP1_2(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op)|RS1_G0, args, 0, sparclite }
+#define EFPOP1_3(name, op, args) { name, F3F(2, 0x36, op), F3F(~2, ~0x36, ~op), args, 0, sparclite }
+#define EFPOP2_2(name, op, args) { name, F3F(2, 0x37, op), F3F(~2, ~0x37, ~op)|RD_G0, args, 0, sparclite }
+
+EFPOP1_2 ("efitod", 0x0c8, "f,H"),
+EFPOP1_2 ("efitos", 0x0c4, "f,g"),
+EFPOP1_2 ("efdtoi", 0x0d2, "B,g"),
+EFPOP1_2 ("efstoi", 0x0d1, "f,g"),
+EFPOP1_2 ("efstod", 0x0c9, "f,H"),
+EFPOP1_2 ("efdtos", 0x0c6, "B,g"),
+EFPOP1_2 ("efmovs", 0x001, "f,g"),
+EFPOP1_2 ("efnegs", 0x005, "f,g"),
+EFPOP1_2 ("efabss", 0x009, "f,g"),
+EFPOP1_2 ("efsqrtd", 0x02a, "B,H"),
+EFPOP1_2 ("efsqrts", 0x029, "f,g"),
+EFPOP1_3 ("efaddd", 0x042, "v,B,H"),
+EFPOP1_3 ("efadds", 0x041, "e,f,g"),
+EFPOP1_3 ("efsubd", 0x046, "v,B,H"),
+EFPOP1_3 ("efsubs", 0x045, "e,f,g"),
+EFPOP1_3 ("efdivd", 0x04e, "v,B,H"),
+EFPOP1_3 ("efdivs", 0x04d, "e,f,g"),
+EFPOP1_3 ("efmuld", 0x04a, "v,B,H"),
+EFPOP1_3 ("efmuls", 0x049, "e,f,g"),
+EFPOP1_3 ("efsmuld", 0x069, "e,f,H"),
+EFPOP2_2 ("efcmpd", 0x052, "v,B"),
+EFPOP2_2 ("efcmped", 0x056, "v,B"),
+EFPOP2_2 ("efcmps", 0x051, "e,f"),
+EFPOP2_2 ("efcmpes", 0x055, "e,f"),
+
+#undef EFPOP1_2
+#undef EFPOP1_3
+#undef EFPOP2_2
+
+/* These are marked F_ALIAS, so that they won't conflict with sparclite insns
+ present. Otherwise, the F_ALIAS flag is ignored. */
+{ "cpop1", F3(2, 0x36, 0), F3(~2, ~0x36, ~1), "[1+2],d", F_ALIAS, v6notv9 },
+{ "cpop2", F3(2, 0x37, 0), F3(~2, ~0x37, ~1), "[1+2],d", F_ALIAS, v6notv9 },
+
+/* sparclet specific insns */
+
+COMMUTEOP ("umac", 0x3e, sparclet),
+COMMUTEOP ("smac", 0x3f, sparclet),
+COMMUTEOP ("umacd", 0x2e, sparclet),
+COMMUTEOP ("smacd", 0x2f, sparclet),
+COMMUTEOP ("umuld", 0x09, sparclet),
+COMMUTEOP ("smuld", 0x0d, sparclet),
+
+{ "shuffle", F3(2, 0x2d, 0), F3(~2, ~0x2d, ~0)|ASI(~0), "1,2,d", 0, sparclet },
+{ "shuffle", F3(2, 0x2d, 1), F3(~2, ~0x2d, ~1), "1,i,d", 0, sparclet },
+
+/* The manual isn't completely accurate on these insns. The `rs2' field is
+ treated as being 6 bits to account for 6 bit immediates to cpush. It is
+ assumed that it is intended that bit 5 is 0 when rs2 contains a reg. */
+#define BIT5 (1<<5)
+{ "crdcxt", F3(2, 0x36, 0)|SLCPOP(4), F3(~2, ~0x36, ~0)|SLCPOP(~4)|BIT5|RS2(~0), "U,d", 0, sparclet },
+{ "cwrcxt", F3(2, 0x36, 0)|SLCPOP(3), F3(~2, ~0x36, ~0)|SLCPOP(~3)|BIT5|RS2(~0), "1,u", 0, sparclet },
+{ "cpush", F3(2, 0x36, 0)|SLCPOP(0), F3(~2, ~0x36, ~0)|SLCPOP(~0)|BIT5|RD(~0), "1,2", 0, sparclet },
+{ "cpush", F3(2, 0x36, 1)|SLCPOP(0), F3(~2, ~0x36, ~1)|SLCPOP(~0)|RD(~0), "1,Y", 0, sparclet },
+{ "cpusha", F3(2, 0x36, 0)|SLCPOP(1), F3(~2, ~0x36, ~0)|SLCPOP(~1)|BIT5|RD(~0), "1,2", 0, sparclet },
+{ "cpusha", F3(2, 0x36, 1)|SLCPOP(1), F3(~2, ~0x36, ~1)|SLCPOP(~1)|RD(~0), "1,Y", 0, sparclet },
+{ "cpull", F3(2, 0x36, 0)|SLCPOP(2), F3(~2, ~0x36, ~0)|SLCPOP(~2)|BIT5|RS1(~0)|RS2(~0), "d", 0, sparclet },
+#undef BIT5
+
+/* sparclet coprocessor branch insns */
+#define SLCBCC2(opcode, mask, lose) \
+ { opcode, (mask), ANNUL|(lose), "l", F_DELAYED|F_CONDBR, sparclet }, \
+ { opcode, (mask)|ANNUL, (lose), ",a l", F_DELAYED|F_CONDBR, sparclet }
+#define SLCBCC(opcode, mask) \
+ SLCBCC2(opcode, F2(0, 7)|COND(mask), F2(~0, ~7)|COND(~(mask)))
+
+/* cbn,cba can't be defined here because they're defined elsewhere and GAS
+ requires all mnemonics of the same name to be consecutive. */
+/*SLCBCC("cbn", 0), - already defined */
+SLCBCC("cbe", 1),
+SLCBCC("cbf", 2),
+SLCBCC("cbef", 3),
+SLCBCC("cbr", 4),
+SLCBCC("cber", 5),
+SLCBCC("cbfr", 6),
+SLCBCC("cbefr", 7),
+/*SLCBCC("cba", 8), - already defined */
+SLCBCC("cbne", 9),
+SLCBCC("cbnf", 10),
+SLCBCC("cbnef", 11),
+SLCBCC("cbnr", 12),
+SLCBCC("cbner", 13),
+SLCBCC("cbnfr", 14),
+SLCBCC("cbnefr", 15),
+
+#undef SLCBCC2
+#undef SLCBCC
+
+{ "casa", F3(3, 0x3c, 0), F3(~3, ~0x3c, ~0), "[1]A,2,d", 0, v9 },
+{ "casa", F3(3, 0x3c, 1), F3(~3, ~0x3c, ~1), "[1]o,2,d", 0, v9 },
+{ "casxa", F3(3, 0x3e, 0), F3(~3, ~0x3e, ~0), "[1]A,2,d", 0, v9 },
+{ "casxa", F3(3, 0x3e, 1), F3(~3, ~0x3e, ~1), "[1]o,2,d", 0, v9 },
+
+/* v9 synthetic insns */
+{ "iprefetch", F2(0, 1)|(2<<20)|BPRED, F2(~0, ~1)|(1<<20)|ANNUL|COND(~0), "G", 0, v9 }, /* bn,a,pt %xcc,label */
+{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* sra rs1,%g0,rd */
+{ "signx", F3(2, 0x27, 0), F3(~2, ~0x27, ~0)|(1<<12)|ASI(~0)|RS2_G0, "r", F_ALIAS, v9 }, /* sra rd,%g0,rd */
+{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "1,d", F_ALIAS, v9 }, /* srl rs1,%g0,rd */
+{ "clruw", F3(2, 0x26, 0), F3(~2, ~0x26, ~0)|(1<<12)|ASI(~0)|RS2_G0, "r", F_ALIAS, v9 }, /* srl rd,%g0,rd */
+{ "cas", F3(3, 0x3c, 0)|ASI(0x80), F3(~3, ~0x3c, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P,rs2,rd */
+{ "casl", F3(3, 0x3c, 0)|ASI(0x88), F3(~3, ~0x3c, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casa [rs1]ASI_P_L,rs2,rd */
+{ "casx", F3(3, 0x3e, 0)|ASI(0x80), F3(~3, ~0x3e, ~0)|ASI(~0x80), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P,rs2,rd */
+{ "casxl", F3(3, 0x3e, 0)|ASI(0x88), F3(~3, ~0x3e, ~0)|ASI(~0x88), "[1],2,d", F_ALIAS, v9 }, /* casxa [rs1]ASI_P_L,rs2,rd */
+
+/* Ultrasparc extensions */
+{ "shutdown", F3F(2, 0x36, 0x080), F3F(~2, ~0x36, ~0x080)|RD_G0|RS1_G0|RS2_G0, "", 0, v9a },
+
+/* FIXME: Do we want to mark these as F_FLOAT, or something similar? */
+{ "fpadd16", F3F(2, 0x36, 0x050), F3F(~2, ~0x36, ~0x050), "v,B,H", 0, v9a },
+{ "fpadd16s", F3F(2, 0x36, 0x051), F3F(~2, ~0x36, ~0x051), "e,f,g", 0, v9a },
+{ "fpadd32", F3F(2, 0x36, 0x052), F3F(~2, ~0x36, ~0x052), "v,B,H", 0, v9a },
+{ "fpadd32s", F3F(2, 0x36, 0x053), F3F(~2, ~0x36, ~0x053), "e,f,g", 0, v9a },
+{ "fpsub16", F3F(2, 0x36, 0x054), F3F(~2, ~0x36, ~0x054), "v,B,H", 0, v9a },
+{ "fpsub16s", F3F(2, 0x36, 0x055), F3F(~2, ~0x36, ~0x055), "e,f,g", 0, v9a },
+{ "fpsub32", F3F(2, 0x36, 0x056), F3F(~2, ~0x36, ~0x056), "v,B,H", 0, v9a },
+{ "fpsub32s", F3F(2, 0x36, 0x057), F3F(~2, ~0x36, ~0x057), "e,f,g", 0, v9a },
+
+{ "fpack32", F3F(2, 0x36, 0x03a), F3F(~2, ~0x36, ~0x03a), "v,B,H", 0, v9a },
+{ "fpack16", F3F(2, 0x36, 0x03b), F3F(~2, ~0x36, ~0x03b)|RS1_G0, "B,g", 0, v9a },
+{ "fpackfix", F3F(2, 0x36, 0x03d), F3F(~2, ~0x36, ~0x03d)|RS1_G0, "B,g", 0, v9a },
+{ "fexpand", F3F(2, 0x36, 0x04d), F3F(~2, ~0x36, ~0x04d)|RS1_G0, "f,H", 0, v9a },
+{ "fpmerge", F3F(2, 0x36, 0x04b), F3F(~2, ~0x36, ~0x04b), "e,f,H", 0, v9a },
+
+/* Note that the mixing of 32/64 bit regs is intentional. */
+{ "fmul8x16", F3F(2, 0x36, 0x031), F3F(~2, ~0x36, ~0x031), "e,B,H", 0, v9a },
+{ "fmul8x16au", F3F(2, 0x36, 0x033), F3F(~2, ~0x36, ~0x033), "e,f,H", 0, v9a },
+{ "fmul8x16al", F3F(2, 0x36, 0x035), F3F(~2, ~0x36, ~0x035), "e,f,H", 0, v9a },
+{ "fmul8sux16", F3F(2, 0x36, 0x036), F3F(~2, ~0x36, ~0x036), "v,B,H", 0, v9a },
+{ "fmul8ulx16", F3F(2, 0x36, 0x037), F3F(~2, ~0x36, ~0x037), "v,B,H", 0, v9a },
+{ "fmuld8sux16", F3F(2, 0x36, 0x038), F3F(~2, ~0x36, ~0x038), "e,f,H", 0, v9a },
+{ "fmuld8ulx16", F3F(2, 0x36, 0x039), F3F(~2, ~0x36, ~0x039), "e,f,H", 0, v9a },
+
+{ "alignaddr", F3F(2, 0x36, 0x018), F3F(~2, ~0x36, ~0x018), "1,2,d", 0, v9a },
+{ "alignaddrl", F3F(2, 0x36, 0x01a), F3F(~2, ~0x36, ~0x01a), "1,2,d", 0, v9a },
+{ "faligndata", F3F(2, 0x36, 0x048), F3F(~2, ~0x36, ~0x048), "v,B,H", 0, v9a },
+
+{ "fzero", F3F(2, 0x36, 0x060), F3F(~2, ~0x36, ~0x060), "H", 0, v9a },
+{ "fzeros", F3F(2, 0x36, 0x061), F3F(~2, ~0x36, ~0x061), "g", 0, v9a },
+{ "fone", F3F(2, 0x36, 0x07e), F3F(~2, ~0x36, ~0x07e), "H", 0, v9a },
+{ "fones", F3F(2, 0x36, 0x07f), F3F(~2, ~0x36, ~0x07f), "g", 0, v9a },
+{ "fsrc1", F3F(2, 0x36, 0x074), F3F(~2, ~0x36, ~0x074), "v,H", 0, v9a },
+{ "fsrc1s", F3F(2, 0x36, 0x075), F3F(~2, ~0x36, ~0x075), "e,g", 0, v9a },
+{ "fsrc2", F3F(2, 0x36, 0x078), F3F(~2, ~0x36, ~0x078), "B,H", 0, v9a },
+{ "fsrc2s", F3F(2, 0x36, 0x079), F3F(~2, ~0x36, ~0x079), "f,g", 0, v9a },
+{ "fnot1", F3F(2, 0x36, 0x06a), F3F(~2, ~0x36, ~0x06a), "v,H", 0, v9a },
+{ "fnot1s", F3F(2, 0x36, 0x06b), F3F(~2, ~0x36, ~0x06b), "e,g", 0, v9a },
+{ "fnot2", F3F(2, 0x36, 0x066), F3F(~2, ~0x36, ~0x066), "B,H", 0, v9a },
+{ "fnot2s", F3F(2, 0x36, 0x067), F3F(~2, ~0x36, ~0x067), "f,g", 0, v9a },
+{ "for", F3F(2, 0x36, 0x07c), F3F(~2, ~0x36, ~0x07c), "v,B,H", 0, v9a },
+{ "fors", F3F(2, 0x36, 0x07d), F3F(~2, ~0x36, ~0x07d), "e,f,g", 0, v9a },
+{ "fnor", F3F(2, 0x36, 0x062), F3F(~2, ~0x36, ~0x062), "v,B,H", 0, v9a },
+{ "fnors", F3F(2, 0x36, 0x063), F3F(~2, ~0x36, ~0x063), "e,f,g", 0, v9a },
+{ "fand", F3F(2, 0x36, 0x070), F3F(~2, ~0x36, ~0x070), "v,B,H", 0, v9a },
+{ "fands", F3F(2, 0x36, 0x071), F3F(~2, ~0x36, ~0x071), "e,f,g", 0, v9a },
+{ "fnand", F3F(2, 0x36, 0x06e), F3F(~2, ~0x36, ~0x06e), "v,B,H", 0, v9a },
+{ "fnands", F3F(2, 0x36, 0x06f), F3F(~2, ~0x36, ~0x06f), "e,f,g", 0, v9a },
+{ "fxor", F3F(2, 0x36, 0x06c), F3F(~2, ~0x36, ~0x06c), "v,B,H", 0, v9a },
+{ "fxors", F3F(2, 0x36, 0x06d), F3F(~2, ~0x36, ~0x06d), "e,f,g", 0, v9a },
+{ "fxnor", F3F(2, 0x36, 0x072), F3F(~2, ~0x36, ~0x072), "v,B,H", 0, v9a },
+{ "fxnors", F3F(2, 0x36, 0x073), F3F(~2, ~0x36, ~0x073), "e,f,g", 0, v9a },
+{ "fornot1", F3F(2, 0x36, 0x07a), F3F(~2, ~0x36, ~0x07a), "v,B,H", 0, v9a },
+{ "fornot1s", F3F(2, 0x36, 0x07b), F3F(~2, ~0x36, ~0x07b), "e,f,g", 0, v9a },
+{ "fornot2", F3F(2, 0x36, 0x076), F3F(~2, ~0x36, ~0x076), "v,B,H", 0, v9a },
+{ "fornot2s", F3F(2, 0x36, 0x077), F3F(~2, ~0x36, ~0x077), "e,f,g", 0, v9a },
+{ "fandnot1", F3F(2, 0x36, 0x068), F3F(~2, ~0x36, ~0x068), "v,B,H", 0, v9a },
+{ "fandnot1s", F3F(2, 0x36, 0x069), F3F(~2, ~0x36, ~0x069), "e,f,g", 0, v9a },
+{ "fandnot2", F3F(2, 0x36, 0x064), F3F(~2, ~0x36, ~0x064), "v,B,H", 0, v9a },
+{ "fandnot2s", F3F(2, 0x36, 0x065), F3F(~2, ~0x36, ~0x065), "e,f,g", 0, v9a },
+
+{ "fcmpgt16", F3F(2, 0x36, 0x028), F3F(~2, ~0x36, ~0x028), "v,B,d", 0, v9a },
+{ "fcmpgt32", F3F(2, 0x36, 0x02c), F3F(~2, ~0x36, ~0x02c), "v,B,d", 0, v9a },
+{ "fcmple16", F3F(2, 0x36, 0x020), F3F(~2, ~0x36, ~0x020), "v,B,d", 0, v9a },
+{ "fcmple32", F3F(2, 0x36, 0x024), F3F(~2, ~0x36, ~0x024), "v,B,d", 0, v9a },
+{ "fcmpne16", F3F(2, 0x36, 0x022), F3F(~2, ~0x36, ~0x022), "v,B,d", 0, v9a },
+{ "fcmpne32", F3F(2, 0x36, 0x026), F3F(~2, ~0x36, ~0x026), "v,B,d", 0, v9a },
+{ "fcmpeq16", F3F(2, 0x36, 0x02a), F3F(~2, ~0x36, ~0x02a), "v,B,d", 0, v9a },
+{ "fcmpeq32", F3F(2, 0x36, 0x02e), F3F(~2, ~0x36, ~0x02e), "v,B,d", 0, v9a },
+
+{ "edge8", F3F(2, 0x36, 0x000), F3F(~2, ~0x36, ~0x000), "1,2,d", 0, v9a },
+{ "edge8l", F3F(2, 0x36, 0x002), F3F(~2, ~0x36, ~0x002), "1,2,d", 0, v9a },
+{ "edge16", F3F(2, 0x36, 0x004), F3F(~2, ~0x36, ~0x004), "1,2,d", 0, v9a },
+{ "edge16l", F3F(2, 0x36, 0x006), F3F(~2, ~0x36, ~0x006), "1,2,d", 0, v9a },
+{ "edge32", F3F(2, 0x36, 0x008), F3F(~2, ~0x36, ~0x008), "1,2,d", 0, v9a },
+{ "edge32l", F3F(2, 0x36, 0x00a), F3F(~2, ~0x36, ~0x00a), "1,2,d", 0, v9a },
+
+{ "pdist", F3F(2, 0x36, 0x03e), F3F(~2, ~0x36, ~0x03e), "v,B,H", 0, v9a },
+
+{ "array8", F3F(2, 0x36, 0x010), F3F(~2, ~0x36, ~0x010), "1,2,d", 0, v9a },
+{ "array16", F3F(2, 0x36, 0x012), F3F(~2, ~0x36, ~0x012), "1,2,d", 0, v9a },
+{ "array32", F3F(2, 0x36, 0x014), F3F(~2, ~0x36, ~0x014), "1,2,d", 0, v9a },
+
+/* Cheetah instructions */
+{ "edge8n", F3F(2, 0x36, 0x001), F3F(~2, ~0x36, ~0x001), "1,2,d", 0, v9b },
+{ "edge8ln", F3F(2, 0x36, 0x003), F3F(~2, ~0x36, ~0x003), "1,2,d", 0, v9b },
+{ "edge16n", F3F(2, 0x36, 0x005), F3F(~2, ~0x36, ~0x005), "1,2,d", 0, v9b },
+{ "edge16ln", F3F(2, 0x36, 0x007), F3F(~2, ~0x36, ~0x007), "1,2,d", 0, v9b },
+{ "edge32n", F3F(2, 0x36, 0x009), F3F(~2, ~0x36, ~0x009), "1,2,d", 0, v9b },
+{ "edge32ln", F3F(2, 0x36, 0x00b), F3F(~2, ~0x36, ~0x00b), "1,2,d", 0, v9b },
+
+{ "bmask", F3F(2, 0x36, 0x019), F3F(~2, ~0x36, ~0x019), "1,2,d", 0, v9b },
+{ "bshuffle", F3F(2, 0x36, 0x04c), F3F(~2, ~0x36, ~0x04c), "v,B,H", 0, v9b },
+
+{ "siam", F3F(2, 0x36, 0x081), F3F(~2, ~0x36, ~0x081)|RD_G0|RS1_G0|RS2(~7), "3", 0, v9b },
+
+/* More v9 specific insns, these need to come last so they do not clash
+ with v9a instructions such as "edge8" which looks like impdep1. */
+
+#define IMPDEP(name, code) \
+{ name, F3(2, code, 0), F3(~2, ~code, ~0)|ASI(~0), "1,2,d", 0, v9notv9a }, \
+{ name, F3(2, code, 1), F3(~2, ~code, ~1), "1,i,d", 0, v9notv9a }, \
+{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,1,2,d", 0, v9notv9a }, \
+{ name, F3(2, code, 0), F3(~2, ~code, ~0), "x,e,f,g", 0, v9notv9a }
+
+IMPDEP ("impdep1", 0x36),
+IMPDEP ("impdep2", 0x37),
+
+#undef IMPDEP
+
+};
+
+static const int sparc_num_opcodes = ((sizeof sparc_opcodes)/(sizeof sparc_opcodes[0]));
+
+/* Utilities for argument parsing. */
+
+typedef struct
+{
+ int value;
+ const char *name;
+} arg;
+
+/* Look up VALUE in TABLE. */
+
+static const char *
+lookup_value (const arg *table, int value)
+{
+ const arg *p;
+
+ for (p = table; p->name; ++p)
+ if (value == p->value)
+ return p->name;
+
+ return NULL;
+}
+
+/* Handle ASI's. */
+
+static const arg asi_table_v8[] =
+{
+ { 0x00, "#ASI_M_RES00" },
+ { 0x01, "#ASI_M_UNA01" },
+ { 0x02, "#ASI_M_MXCC" },
+ { 0x03, "#ASI_M_FLUSH_PROBE" },
+ { 0x04, "#ASI_M_MMUREGS" },
+ { 0x05, "#ASI_M_TLBDIAG" },
+ { 0x06, "#ASI_M_DIAGS" },
+ { 0x07, "#ASI_M_IODIAG" },
+ { 0x08, "#ASI_M_USERTXT" },
+ { 0x09, "#ASI_M_KERNELTXT" },
+ { 0x0A, "#ASI_M_USERDATA" },
+ { 0x0B, "#ASI_M_KERNELDATA" },
+ { 0x0C, "#ASI_M_TXTC_TAG" },
+ { 0x0D, "#ASI_M_TXTC_DATA" },
+ { 0x0E, "#ASI_M_DATAC_TAG" },
+ { 0x0F, "#ASI_M_DATAC_DATA" },
+ { 0x10, "#ASI_M_FLUSH_PAGE" },
+ { 0x11, "#ASI_M_FLUSH_SEG" },
+ { 0x12, "#ASI_M_FLUSH_REGION" },
+ { 0x13, "#ASI_M_FLUSH_CTX" },
+ { 0x14, "#ASI_M_FLUSH_USER" },
+ { 0x17, "#ASI_M_BCOPY" },
+ { 0x18, "#ASI_M_IFLUSH_PAGE" },
+ { 0x19, "#ASI_M_IFLUSH_SEG" },
+ { 0x1A, "#ASI_M_IFLUSH_REGION" },
+ { 0x1B, "#ASI_M_IFLUSH_CTX" },
+ { 0x1C, "#ASI_M_IFLUSH_USER" },
+ { 0x1F, "#ASI_M_BFILL" },
+ { 0x20, "#ASI_M_BYPASS" },
+ { 0x29, "#ASI_M_FBMEM" },
+ { 0x2A, "#ASI_M_VMEUS" },
+ { 0x2B, "#ASI_M_VMEPS" },
+ { 0x2C, "#ASI_M_VMEUT" },
+ { 0x2D, "#ASI_M_VMEPT" },
+ { 0x2E, "#ASI_M_SBUS" },
+ { 0x2F, "#ASI_M_CTL" },
+ { 0x31, "#ASI_M_FLUSH_IWHOLE" },
+ { 0x36, "#ASI_M_IC_FLCLEAR" },
+ { 0x37, "#ASI_M_DC_FLCLEAR" },
+ { 0x39, "#ASI_M_DCDR" },
+ { 0x40, "#ASI_M_VIKING_TMP1" },
+ { 0x41, "#ASI_M_VIKING_TMP2" },
+ { 0x4c, "#ASI_M_ACTION" },
+ { 0, NULL }
+};
+
+static const arg asi_table_v9[] =
+{
+ /* These are in the v9 architecture manual. */
+ /* The shorter versions appear first, they're here because Sun's as has them.
+ Sun's as uses #ASI_P_L instead of #ASI_PL (which appears in the
+ UltraSPARC architecture manual). */
+ { 0x04, "#ASI_N" },
+ { 0x0c, "#ASI_N_L" },
+ { 0x10, "#ASI_AIUP" },
+ { 0x11, "#ASI_AIUS" },
+ { 0x18, "#ASI_AIUP_L" },
+ { 0x19, "#ASI_AIUS_L" },
+ { 0x80, "#ASI_P" },
+ { 0x81, "#ASI_S" },
+ { 0x82, "#ASI_PNF" },
+ { 0x83, "#ASI_SNF" },
+ { 0x88, "#ASI_P_L" },
+ { 0x89, "#ASI_S_L" },
+ { 0x8a, "#ASI_PNF_L" },
+ { 0x8b, "#ASI_SNF_L" },
+ { 0x04, "#ASI_NUCLEUS" },
+ { 0x0c, "#ASI_NUCLEUS_LITTLE" },
+ { 0x10, "#ASI_AS_IF_USER_PRIMARY" },
+ { 0x11, "#ASI_AS_IF_USER_SECONDARY" },
+ { 0x18, "#ASI_AS_IF_USER_PRIMARY_LITTLE" },
+ { 0x19, "#ASI_AS_IF_USER_SECONDARY_LITTLE" },
+ { 0x80, "#ASI_PRIMARY" },
+ { 0x81, "#ASI_SECONDARY" },
+ { 0x82, "#ASI_PRIMARY_NOFAULT" },
+ { 0x83, "#ASI_SECONDARY_NOFAULT" },
+ { 0x88, "#ASI_PRIMARY_LITTLE" },
+ { 0x89, "#ASI_SECONDARY_LITTLE" },
+ { 0x8a, "#ASI_PRIMARY_NOFAULT_LITTLE" },
+ { 0x8b, "#ASI_SECONDARY_NOFAULT_LITTLE" },
+ /* These are UltraSPARC extensions. */
+ { 0x14, "#ASI_PHYS_USE_EC"},
+ { 0x15, "#ASI_PHYS_BYPASS_EC_WITH_EBIT"},
+ { 0x45, "#ASI_LSU_CONTROL_REG"},
+ { 0x47, "#ASI_DCACHE_TAG"},
+ { 0x4a, "#ASI_UPA_CONFIG_REG"},
+ { 0x50, "#ASI_IMMU" },
+ { 0x51, "#ASI_IMMU_TSB_8KB_PTR_REG" },
+ { 0x52, "#ASI_IMMU_TSB_64KB_PTR_REG" },
+ /*{ 0x53, "#reserved?" },*/
+ { 0x54, "#ASI_ITLB_DATA_IN_REG" },
+ { 0x55, "#ASI_ITLB_DATA_ACCESS_REG" },
+ { 0x56, "#ASI_ITLB_TAG_READ_REG" },
+ { 0x57, "#ASI_IMMU_DEMAP" },
+ { 0x58, "#ASI_DMMU" },
+ { 0x59, "#ASI_DMMU_TSB_8KB_PTR_REG" },
+ { 0x5a, "#ASI_DMMU_TSB_64KB_PTR_REG" },
+ { 0x5b, "#ASI_DMMU_TSB_DIRECT_PTR_REG" },
+ { 0x5c, "#ASI_DTLB_DATA_IN_REG" },
+ { 0x5d, "#ASI_DTLB_DATA_ACCESS_REG" },
+ { 0x5e, "#ASI_DTLB_TAG_READ_REG" },
+ { 0x5f, "#ASI_DMMU_DEMAP" },
+ { 0x67, "#ASI_IC_TAG"},
+ /* FIXME: There are dozens of them. Not sure we want them all.
+ Most are for kernel building but some are for vis type stuff. */
+ { 0, NULL }
+};
+
+/* Return the name for ASI value VALUE or NULL if not found. */
+
+static const char *
+sparc_decode_asi_v9 (int value)
+{
+ return lookup_value (asi_table_v9, value);
+}
+
+static const char *
+sparc_decode_asi_v8 (int value)
+{
+ return lookup_value (asi_table_v8, value);
+}
+
+/* Handle membar masks. */
+
+static const arg membar_table[] =
+{
+ { 0x40, "#Sync" },
+ { 0x20, "#MemIssue" },
+ { 0x10, "#Lookaside" },
+ { 0x08, "#StoreStore" },
+ { 0x04, "#LoadStore" },
+ { 0x02, "#StoreLoad" },
+ { 0x01, "#LoadLoad" },
+ { 0, NULL }
+};
+
+/* Return the name for membar value VALUE or NULL if not found. */
+
+static const char *
+sparc_decode_membar (int value)
+{
+ return lookup_value (membar_table, value);
+}
+
+/* Handle prefetch args. */
+
+static const arg prefetch_table[] =
+{
+ { 0, "#n_reads" },
+ { 1, "#one_read" },
+ { 2, "#n_writes" },
+ { 3, "#one_write" },
+ { 4, "#page" },
+ { 16, "#invalidate" },
+ { 0, NULL }
+};
+
+/* Return the name for prefetch value VALUE or NULL if not found. */
+
+static const char *
+sparc_decode_prefetch (int value)
+{
+ return lookup_value (prefetch_table, value);
+}
+
+/* Handle sparclet coprocessor registers. */
+
+static const arg sparclet_cpreg_table[] =
+{
+ { 0, "%ccsr" },
+ { 1, "%ccfr" },
+ { 2, "%cccrcr" },
+ { 3, "%ccpr" },
+ { 4, "%ccsr2" },
+ { 5, "%cccrr" },
+ { 6, "%ccrstr" },
+ { 0, NULL }
+};
+
+/* Return the name for sparclet cpreg value VALUE or NULL if not found. */
+
+static const char *
+sparc_decode_sparclet_cpreg (int value)
+{
+ return lookup_value (sparclet_cpreg_table, value);
+}
+
+#undef MASK_V9
+
+/* opcodes/sparc-dis.c */
+
+/* Print SPARC instructions.
+ Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+ 2000, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 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/>. */
+
+/* Bitmask of v9 architectures. */
+#define MASK_V9 ((1 << SPARC_OPCODE_ARCH_V9) \
+ | (1 << SPARC_OPCODE_ARCH_V9A) \
+ | (1 << SPARC_OPCODE_ARCH_V9B))
+/* 1 if INSN is for v9 only. */
+#define V9_ONLY_P(insn) (! ((insn)->architecture & ~MASK_V9))
+/* 1 if INSN is for v9. */
+#define V9_P(insn) (((insn)->architecture & MASK_V9) != 0)
+
+/* The sorted opcode table. */
+static const sparc_opcode **sorted_opcodes;
+
+/* For faster lookup, after insns are sorted they are hashed. */
+/* ??? I think there is room for even more improvement. */
+
+#define HASH_SIZE 256
+/* It is important that we only look at insn code bits as that is how the
+ opcode table is hashed. OPCODE_BITS is a table of valid bits for each
+ of the main types (0,1,2,3). */
+static const int opcode_bits[4] = { 0x01c00000, 0x0, 0x01f80000, 0x01f80000 };
+#define HASH_INSN(INSN) \
+ ((((INSN) >> 24) & 0xc0) | (((INSN) & opcode_bits[((INSN) >> 30) & 3]) >> 19))
+typedef struct sparc_opcode_hash
+{
+ struct sparc_opcode_hash *next;
+ const sparc_opcode *opcode;
+} sparc_opcode_hash;
+
+static sparc_opcode_hash *opcode_hash_table[HASH_SIZE];
+
+/* Sign-extend a value which is N bits long. */
+#define SEX(value, bits) \
+ ((((int)(value)) << ((8 * sizeof (int)) - bits)) \
+ >> ((8 * sizeof (int)) - bits) )
+
+static const char * const reg_names[] =
+{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
+ "o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
+ "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
+ "i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7",
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+ "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39",
+ "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47",
+ "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55",
+ "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63",
+/* psr, wim, tbr, fpsr, cpsr are v8 only. */
+ "y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr"
+};
+
+#define freg_names (&reg_names[4 * 8])
+
+/* These are ordered according to there register number in
+ rdpr and wrpr insns. */
+static const char * const v9_priv_reg_names[] =
+{
+ "tpc", "tnpc", "tstate", "tt", "tick", "tba", "pstate", "tl",
+ "pil", "cwp", "cansave", "canrestore", "cleanwin", "otherwin",
+ "wstate", "fq", "gl"
+ /* "ver" - special cased */
+};
+
+/* These are ordered according to there register number in
+ rdhpr and wrhpr insns. */
+static const char * const v9_hpriv_reg_names[] =
+{
+ "hpstate", "htstate", "resv2", "hintp", "resv4", "htba", "hver",
+ "resv7", "resv8", "resv9", "resv10", "resv11", "resv12", "resv13",
+ "resv14", "resv15", "resv16", "resv17", "resv18", "resv19", "resv20",
+ "resv21", "resv22", "resv23", "resv24", "resv25", "resv26", "resv27",
+ "resv28", "resv29", "resv30", "hstick_cmpr"
+};
+
+/* These are ordered according to there register number in
+ rd and wr insns (-16). */
+static const char * const v9a_asr_reg_names[] =
+{
+ "pcr", "pic", "dcr", "gsr", "set_softint", "clear_softint",
+ "softint", "tick_cmpr", "sys_tick", "sys_tick_cmpr"
+};
+
+/* Macros used to extract instruction fields. Not all fields have
+ macros defined here, only those which are actually used. */
+
+#define X_RD(i) (((i) >> 25) & 0x1f)
+#define X_RS1(i) (((i) >> 14) & 0x1f)
+#define X_LDST_I(i) (((i) >> 13) & 1)
+#define X_ASI(i) (((i) >> 5) & 0xff)
+#define X_RS2(i) (((i) >> 0) & 0x1f)
+#define X_IMM(i,n) (((i) >> 0) & ((1 << (n)) - 1))
+#define X_SIMM(i,n) SEX (X_IMM ((i), (n)), (n))
+#define X_DISP22(i) (((i) >> 0) & 0x3fffff)
+#define X_IMM22(i) X_DISP22 (i)
+#define X_DISP30(i) (((i) >> 0) & 0x3fffffff)
+
+/* These are for v9. */
+#define X_DISP16(i) (((((i) >> 20) & 3) << 14) | (((i) >> 0) & 0x3fff))
+#define X_DISP19(i) (((i) >> 0) & 0x7ffff)
+#define X_MEMBAR(i) ((i) & 0x7f)
+
+/* Here is the union which was used to extract instruction fields
+ before the shift and mask macros were written.
+
+ union sparc_insn
+ {
+ unsigned long int code;
+ struct
+ {
+ unsigned int anop:2;
+ #define op ldst.anop
+ unsigned int anrd:5;
+ #define rd ldst.anrd
+ unsigned int op3:6;
+ unsigned int anrs1:5;
+ #define rs1 ldst.anrs1
+ unsigned int i:1;
+ unsigned int anasi:8;
+ #define asi ldst.anasi
+ unsigned int anrs2:5;
+ #define rs2 ldst.anrs2
+ #define shcnt rs2
+ } ldst;
+ struct
+ {
+ unsigned int anop:2, anrd:5, op3:6, anrs1:5, i:1;
+ unsigned int IMM13:13;
+ #define imm13 IMM13.IMM13
+ } IMM13;
+ struct
+ {
+ unsigned int anop:2;
+ unsigned int a:1;
+ unsigned int cond:4;
+ unsigned int op2:3;
+ unsigned int DISP22:22;
+ #define disp22 branch.DISP22
+ #define imm22 disp22
+ } branch;
+ struct
+ {
+ unsigned int anop:2;
+ unsigned int a:1;
+ unsigned int z:1;
+ unsigned int rcond:3;
+ unsigned int op2:3;
+ unsigned int DISP16HI:2;
+ unsigned int p:1;
+ unsigned int _rs1:5;
+ unsigned int DISP16LO:14;
+ } branch16;
+ struct
+ {
+ unsigned int anop:2;
+ unsigned int adisp30:30;
+ #define disp30 call.adisp30
+ } call;
+ }; */
+
+/* Nonzero if INSN is the opcode for a delayed branch. */
+
+static int
+is_delayed_branch (unsigned long insn)
+{
+ sparc_opcode_hash *op;
+
+ for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next)
+ {
+ const sparc_opcode *opcode = op->opcode;
+
+ if ((opcode->match & insn) == opcode->match
+ && (opcode->lose & insn) == 0)
+ return opcode->flags & F_DELAYED;
+ }
+ return 0;
+}
+
+/* extern void qsort (); */
+
+/* Records current mask of SPARC_OPCODE_ARCH_FOO values, used to pass value
+ to compare_opcodes. */
+static unsigned int current_arch_mask;
+
+/* Given BFD mach number, return a mask of SPARC_OPCODE_ARCH_FOO values. */
+
+static int
+compute_arch_mask (unsigned long mach)
+{
+ switch (mach)
+ {
+ case 0 :
+ case bfd_mach_sparc :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8);
+ case bfd_mach_sparc_sparclet :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLET);
+ case bfd_mach_sparc_sparclite :
+ case bfd_mach_sparc_sparclite_le :
+ /* sparclites insns are recognized by default (because that's how
+ they've always been treated, for better or worse). Kludge this by
+ indicating generic v8 is also selected. */
+ return (SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_SPARCLITE)
+ | SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V8));
+ case bfd_mach_sparc_v8plus :
+ case bfd_mach_sparc_v9 :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9);
+ case bfd_mach_sparc_v8plusa :
+ case bfd_mach_sparc_v9a :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A);
+ case bfd_mach_sparc_v8plusb :
+ case bfd_mach_sparc_v9b :
+ return SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9B);
+ }
+ abort ();
+}
+
+/* Compare opcodes A and B. */
+
+static int
+compare_opcodes (const void * a, const void * b)
+{
+ sparc_opcode *op0 = * (sparc_opcode **) a;
+ sparc_opcode *op1 = * (sparc_opcode **) b;
+ unsigned long int match0 = op0->match, match1 = op1->match;
+ unsigned long int lose0 = op0->lose, lose1 = op1->lose;
+ register unsigned int i;
+
+ /* If one (and only one) insn isn't supported by the current architecture,
+ prefer the one that is. If neither are supported, but they're both for
+ the same architecture, continue processing. Otherwise (both unsupported
+ and for different architectures), prefer lower numbered arch's (fudged
+ by comparing the bitmasks). */
+ if (op0->architecture & current_arch_mask)
+ {
+ if (! (op1->architecture & current_arch_mask))
+ return -1;
+ }
+ else
+ {
+ if (op1->architecture & current_arch_mask)
+ return 1;
+ else if (op0->architecture != op1->architecture)
+ return op0->architecture - op1->architecture;
+ }
+
+ /* If a bit is set in both match and lose, there is something
+ wrong with the opcode table. */
+ if (match0 & lose0)
+ {
+ fprintf
+ (stderr,
+ /* xgettext:c-format */
+ _("Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n"),
+ op0->name, match0, lose0);
+ op0->lose &= ~op0->match;
+ lose0 = op0->lose;
+ }
+
+ if (match1 & lose1)
+ {
+ fprintf
+ (stderr,
+ /* xgettext:c-format */
+ _("Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n"),
+ op1->name, match1, lose1);
+ op1->lose &= ~op1->match;
+ lose1 = op1->lose;
+ }
+
+ /* Because the bits that are variable in one opcode are constant in
+ another, it is important to order the opcodes in the right order. */
+ for (i = 0; i < 32; ++i)
+ {
+ unsigned long int x = 1 << i;
+ int x0 = (match0 & x) != 0;
+ int x1 = (match1 & x) != 0;
+
+ if (x0 != x1)
+ return x1 - x0;
+ }
+
+ for (i = 0; i < 32; ++i)
+ {
+ unsigned long int x = 1 << i;
+ int x0 = (lose0 & x) != 0;
+ int x1 = (lose1 & x) != 0;
+
+ if (x0 != x1)
+ return x1 - x0;
+ }
+
+ /* They are functionally equal. So as long as the opcode table is
+ valid, we can put whichever one first we want, on aesthetic grounds. */
+
+ /* Our first aesthetic ground is that aliases defer to real insns. */
+ {
+ int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS);
+
+ if (alias_diff != 0)
+ /* Put the one that isn't an alias first. */
+ return alias_diff;
+ }
+
+ /* Except for aliases, two "identical" instructions had
+ better have the same opcode. This is a sanity check on the table. */
+ i = strcmp (op0->name, op1->name);
+ if (i)
+ {
+ if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */
+ return i;
+ else
+ fprintf (stderr,
+ /* xgettext:c-format */
+ _("Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n"),
+ op0->name, op1->name);
+ }
+
+ /* Fewer arguments are preferred. */
+ {
+ int length_diff = strlen (op0->args) - strlen (op1->args);
+
+ if (length_diff != 0)
+ /* Put the one with fewer arguments first. */
+ return length_diff;
+ }
+
+ /* Put 1+i before i+1. */
+ {
+ char *p0 = (char *) strchr (op0->args, '+');
+ char *p1 = (char *) strchr (op1->args, '+');
+
+ if (p0 && p1)
+ {
+ /* There is a plus in both operands. Note that a plus
+ sign cannot be the first character in args,
+ so the following [-1]'s are valid. */
+ if (p0[-1] == 'i' && p1[1] == 'i')
+ /* op0 is i+1 and op1 is 1+i, so op1 goes first. */
+ return 1;
+ if (p0[1] == 'i' && p1[-1] == 'i')
+ /* op0 is 1+i and op1 is i+1, so op0 goes first. */
+ return -1;
+ }
+ }
+
+ /* Put 1,i before i,1. */
+ {
+ int i0 = strncmp (op0->args, "i,1", 3) == 0;
+ int i1 = strncmp (op1->args, "i,1", 3) == 0;
+
+ if (i0 ^ i1)
+ return i0 - i1;
+ }
+
+ /* They are, as far as we can tell, identical.
+ Since qsort may have rearranged the table partially, there is
+ no way to tell which one was first in the opcode table as
+ written, so just say there are equal. */
+ /* ??? This is no longer true now that we sort a vector of pointers,
+ not the table itself. */
+ return 0;
+}
+
+/* Build a hash table from the opcode table.
+ OPCODE_TABLE is a sorted list of pointers into the opcode table. */
+
+static void
+build_hash_table (const sparc_opcode **opcode_table,
+ sparc_opcode_hash **hash_table,
+ int num_opcodes)
+{
+ int i;
+ int hash_count[HASH_SIZE];
+ static sparc_opcode_hash *hash_buf = NULL;
+
+ /* Start at the end of the table and work backwards so that each
+ chain is sorted. */
+
+ memset (hash_table, 0, HASH_SIZE * sizeof (hash_table[0]));
+ memset (hash_count, 0, HASH_SIZE * sizeof (hash_count[0]));
+ if (hash_buf != NULL)
+ free (hash_buf);
+ hash_buf = malloc (sizeof (* hash_buf) * num_opcodes);
+ for (i = num_opcodes - 1; i >= 0; --i)
+ {
+ int hash = HASH_INSN (opcode_table[i]->match);
+ sparc_opcode_hash *h = &hash_buf[i];
+
+ h->next = hash_table[hash];
+ h->opcode = opcode_table[i];
+ hash_table[hash] = h;
+ ++hash_count[hash];
+ }
+
+#if 0 /* for debugging */
+ {
+ int min_count = num_opcodes, max_count = 0;
+ int total;
+
+ for (i = 0; i < HASH_SIZE; ++i)
+ {
+ if (hash_count[i] < min_count)
+ min_count = hash_count[i];
+ if (hash_count[i] > max_count)
+ max_count = hash_count[i];
+ total += hash_count[i];
+ }
+
+ printf ("Opcode hash table stats: min %d, max %d, ave %f\n",
+ min_count, max_count, (double) total / HASH_SIZE);
+ }
+#endif
+}
+
+/* Print one instruction from MEMADDR on INFO->STREAM.
+
+ We suffix the instruction with a comment that gives the absolute
+ address involved, as well as its symbolic form, if the instruction
+ is preceded by a findable `sethi' and it either adds an immediate
+ displacement to that register, or it is an `add' or `or' instruction
+ on that register. */
+
+int
+print_insn_sparc (bfd_vma memaddr, disassemble_info *info)
+{
+ FILE *stream = info->stream;
+ bfd_byte buffer[4];
+ unsigned long insn;
+ sparc_opcode_hash *op;
+ /* Nonzero of opcode table has been initialized. */
+ static int opcodes_initialized = 0;
+ /* bfd mach number of last call. */
+ static unsigned long current_mach = 0;
+ bfd_vma (*getword) (const unsigned char *);
+
+ if (!opcodes_initialized
+ || info->mach != current_mach)
+ {
+ int i;
+
+ current_arch_mask = compute_arch_mask (info->mach);
+
+ if (!opcodes_initialized)
+ sorted_opcodes =
+ malloc (sparc_num_opcodes * sizeof (sparc_opcode *));
+ /* Reset the sorted table so we can resort it. */
+ for (i = 0; i < sparc_num_opcodes; ++i)
+ sorted_opcodes[i] = &sparc_opcodes[i];
+ qsort ((char *) sorted_opcodes, sparc_num_opcodes,
+ sizeof (sorted_opcodes[0]), compare_opcodes);
+
+ build_hash_table (sorted_opcodes, opcode_hash_table, sparc_num_opcodes);
+ current_mach = info->mach;
+ opcodes_initialized = 1;
+ }
+
+ {
+ int status =
+ (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info);
+
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ }
+
+ /* On SPARClite variants such as DANlite (sparc86x), instructions
+ are always big-endian even when the machine is in little-endian mode. */
+ if (info->endian == BFD_ENDIAN_BIG || info->mach == bfd_mach_sparc_sparclite)
+ getword = bfd_getb32;
+ else
+ getword = bfd_getl32;
+
+ insn = getword (buffer);
+
+ info->insn_info_valid = 1; /* We do return this info. */
+ info->insn_type = dis_nonbranch; /* Assume non branch insn. */
+ info->branch_delay_insns = 0; /* Assume no delay. */
+ info->target = 0; /* Assume no target known. */
+
+ for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next)
+ {
+ const sparc_opcode *opcode = op->opcode;
+
+ /* If the insn isn't supported by the current architecture, skip it. */
+ if (! (opcode->architecture & current_arch_mask))
+ continue;
+
+ if ((opcode->match & insn) == opcode->match
+ && (opcode->lose & insn) == 0)
+ {
+ /* Nonzero means that we have found an instruction which has
+ the effect of adding or or'ing the imm13 field to rs1. */
+ int imm_added_to_rs1 = 0;
+ int imm_ored_to_rs1 = 0;
+
+ /* Nonzero means that we have found a plus sign in the args
+ field of the opcode table. */
+ int found_plus = 0;
+
+ /* Nonzero means we have an annulled branch. */
+ /* int is_annulled = 0; */ /* see FIXME below */
+
+ /* Do we have an `add' or `or' instruction combining an
+ immediate with rs1? */
+ if (opcode->match == 0x80102000) /* or */
+ imm_ored_to_rs1 = 1;
+ if (opcode->match == 0x80002000) /* add */
+ imm_added_to_rs1 = 1;
+
+ if (X_RS1 (insn) != X_RD (insn)
+ && strchr (opcode->args, 'r') != NULL)
+ /* Can't do simple format if source and dest are different. */
+ continue;
+ if (X_RS2 (insn) != X_RD (insn)
+ && strchr (opcode->args, 'O') != NULL)
+ /* Can't do simple format if source and dest are different. */
+ continue;
+
+ (*info->fprintf_func) (stream, "%s", opcode->name);
+
+ {
+ const char *s;
+
+ if (opcode->args[0] != ',')
+ (*info->fprintf_func) (stream, " ");
+
+ for (s = opcode->args; *s != '\0'; ++s)
+ {
+ while (*s == ',')
+ {
+ (*info->fprintf_func) (stream, ",");
+ ++s;
+ switch (*s)
+ {
+ case 'a':
+ (*info->fprintf_func) (stream, "a");
+ /* is_annulled = 1; */ /* see FIXME below */
+ ++s;
+ continue;
+ case 'N':
+ (*info->fprintf_func) (stream, "pn");
+ ++s;
+ continue;
+
+ case 'T':
+ (*info->fprintf_func) (stream, "pt");
+ ++s;
+ continue;
+
+ default:
+ break;
+ }
+ }
+
+ (*info->fprintf_func) (stream, " ");
+
+ switch (*s)
+ {
+ case '+':
+ found_plus = 1;
+ /* Fall through. */
+
+ default:
+ (*info->fprintf_func) (stream, "%c", *s);
+ break;
+
+ case '#':
+ (*info->fprintf_func) (stream, "0");
+ break;
+
+#define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n])
+ case '1':
+ case 'r':
+ reg (X_RS1 (insn));
+ break;
+
+ case '2':
+ case 'O':
+ reg (X_RS2 (insn));
+ break;
+
+ case 'd':
+ reg (X_RD (insn));
+ break;
+#undef reg
+
+#define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n])
+#define fregx(n) (*info->fprintf_func) (stream, "%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)])
+ case 'e':
+ freg (X_RS1 (insn));
+ break;
+ case 'v': /* Double/even. */
+ case 'V': /* Quad/multiple of 4. */
+ fregx (X_RS1 (insn));
+ break;
+
+ case 'f':
+ freg (X_RS2 (insn));
+ break;
+ case 'B': /* Double/even. */
+ case 'R': /* Quad/multiple of 4. */
+ fregx (X_RS2 (insn));
+ break;
+
+ case 'g':
+ freg (X_RD (insn));
+ break;
+ case 'H': /* Double/even. */
+ case 'J': /* Quad/multiple of 4. */
+ fregx (X_RD (insn));
+ break;
+#undef freg
+#undef fregx
+
+#define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n))
+ case 'b':
+ creg (X_RS1 (insn));
+ break;
+
+ case 'c':
+ creg (X_RS2 (insn));
+ break;
+
+ case 'D':
+ creg (X_RD (insn));
+ break;
+#undef creg
+
+ case 'h':
+ (*info->fprintf_func) (stream, "%%hi(%#x)",
+ ((unsigned) 0xFFFFFFFF
+ & ((int) X_IMM22 (insn) << 10)));
+ break;
+
+ case 'i': /* 13 bit immediate. */
+ case 'I': /* 11 bit immediate. */
+ case 'j': /* 10 bit immediate. */
+ {
+ int imm;
+
+ if (*s == 'i')
+ imm = X_SIMM (insn, 13);
+ else if (*s == 'I')
+ imm = X_SIMM (insn, 11);
+ else
+ imm = X_SIMM (insn, 10);
+
+ /* Check to see whether we have a 1+i, and take
+ note of that fact.
+
+ Note: because of the way we sort the table,
+ we will be matching 1+i rather than i+1,
+ so it is OK to assume that i is after +,
+ not before it. */
+ if (found_plus)
+ imm_added_to_rs1 = 1;
+
+ if (imm <= 9)
+ (*info->fprintf_func) (stream, "%d", imm);
+ else
+ (*info->fprintf_func) (stream, "%#x", imm);
+ }
+ break;
+
+ case 'X': /* 5 bit unsigned immediate. */
+ case 'Y': /* 6 bit unsigned immediate. */
+ {
+ int imm = X_IMM (insn, *s == 'X' ? 5 : 6);
+
+ if (imm <= 9)
+ (info->fprintf_func) (stream, "%d", imm);
+ else
+ (info->fprintf_func) (stream, "%#x", (unsigned) imm);
+ }
+ break;
+
+ case '3':
+ (info->fprintf_func) (stream, "%ld", X_IMM (insn, 3));
+ break;
+
+ case 'K':
+ {
+ int mask = X_MEMBAR (insn);
+ int bit = 0x40, printed_one = 0;
+ const char *name;
+
+ if (mask == 0)
+ (info->fprintf_func) (stream, "0");
+ else
+ while (bit)
+ {
+ if (mask & bit)
+ {
+ if (printed_one)
+ (info->fprintf_func) (stream, "|");
+ name = sparc_decode_membar (bit);
+ (info->fprintf_func) (stream, "%s", name);
+ printed_one = 1;
+ }
+ bit >>= 1;
+ }
+ break;
+ }
+
+ case 'k':
+ info->target = memaddr + SEX (X_DISP16 (insn), 16) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'G':
+ info->target = memaddr + SEX (X_DISP19 (insn), 19) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ (*info->fprintf_func) (stream, "%%fcc%c", *s - '6' + '0');
+ break;
+
+ case 'z':
+ (*info->fprintf_func) (stream, "%%icc");
+ break;
+
+ case 'Z':
+ (*info->fprintf_func) (stream, "%%xcc");
+ break;
+
+ case 'E':
+ (*info->fprintf_func) (stream, "%%ccr");
+ break;
+
+ case 's':
+ (*info->fprintf_func) (stream, "%%fprs");
+ break;
+
+ case 'o':
+ (*info->fprintf_func) (stream, "%%asi");
+ break;
+
+ case 'W':
+ (*info->fprintf_func) (stream, "%%tick");
+ break;
+
+ case 'P':
+ (*info->fprintf_func) (stream, "%%pc");
+ break;
+
+ case '?':
+ if (X_RS1 (insn) == 31)
+ (*info->fprintf_func) (stream, "%%ver");
+ else if ((unsigned) X_RS1 (insn) < 17)
+ (*info->fprintf_func) (stream, "%%%s",
+ v9_priv_reg_names[X_RS1 (insn)]);
+ else
+ (*info->fprintf_func) (stream, "%%reserved");
+ break;
+
+ case '!':
+ if ((unsigned) X_RD (insn) < 17)
+ (*info->fprintf_func) (stream, "%%%s",
+ v9_priv_reg_names[X_RD (insn)]);
+ else
+ (*info->fprintf_func) (stream, "%%reserved");
+ break;
+
+ case '$':
+ if ((unsigned) X_RS1 (insn) < 32)
+ (*info->fprintf_func) (stream, "%%%s",
+ v9_hpriv_reg_names[X_RS1 (insn)]);
+ else
+ (*info->fprintf_func) (stream, "%%reserved");
+ break;
+
+ case '%':
+ if ((unsigned) X_RD (insn) < 32)
+ (*info->fprintf_func) (stream, "%%%s",
+ v9_hpriv_reg_names[X_RD (insn)]);
+ else
+ (*info->fprintf_func) (stream, "%%reserved");
+ break;
+
+ case '/':
+ if (X_RS1 (insn) < 16 || X_RS1 (insn) > 25)
+ (*info->fprintf_func) (stream, "%%reserved");
+ else
+ (*info->fprintf_func) (stream, "%%%s",
+ v9a_asr_reg_names[X_RS1 (insn)-16]);
+ break;
+
+ case '_':
+ if (X_RD (insn) < 16 || X_RD (insn) > 25)
+ (*info->fprintf_func) (stream, "%%reserved");
+ else
+ (*info->fprintf_func) (stream, "%%%s",
+ v9a_asr_reg_names[X_RD (insn)-16]);
+ break;
+
+ case '*':
+ {
+ const char *name = sparc_decode_prefetch (X_RD (insn));
+
+ if (name)
+ (*info->fprintf_func) (stream, "%s", name);
+ else
+ (*info->fprintf_func) (stream, "%ld", X_RD (insn));
+ break;
+ }
+
+ case 'M':
+ (*info->fprintf_func) (stream, "%%asr%ld", X_RS1 (insn));
+ break;
+
+ case 'm':
+ (*info->fprintf_func) (stream, "%%asr%ld", X_RD (insn));
+ break;
+
+ case 'L':
+ info->target = memaddr + SEX (X_DISP30 (insn), 30) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'n':
+ (*info->fprintf_func)
+ (stream, "%#x", SEX (X_DISP22 (insn), 22));
+ break;
+
+ case 'l':
+ info->target = memaddr + SEX (X_DISP22 (insn), 22) * 4;
+ (*info->print_address_func) (info->target, info);
+ break;
+
+ case 'A':
+ {
+ const char *name;
+
+ if ((info->mach == bfd_mach_sparc_v8plusa) ||
+ ((info->mach >= bfd_mach_sparc_v9) &&
+ (info->mach <= bfd_mach_sparc_v9b)))
+ name = sparc_decode_asi_v9 (X_ASI (insn));
+ else
+ name = sparc_decode_asi_v8 (X_ASI (insn));
+
+ if (name)
+ (*info->fprintf_func) (stream, "%s", name);
+ else
+ (*info->fprintf_func) (stream, "(%ld)", X_ASI (insn));
+ break;
+ }
+
+ case 'C':
+ (*info->fprintf_func) (stream, "%%csr");
+ break;
+
+ case 'F':
+ (*info->fprintf_func) (stream, "%%fsr");
+ break;
+
+ case 'p':
+ (*info->fprintf_func) (stream, "%%psr");
+ break;
+
+ case 'q':
+ (*info->fprintf_func) (stream, "%%fq");
+ break;
+
+ case 'Q':
+ (*info->fprintf_func) (stream, "%%cq");
+ break;
+
+ case 't':
+ (*info->fprintf_func) (stream, "%%tbr");
+ break;
+
+ case 'w':
+ (*info->fprintf_func) (stream, "%%wim");
+ break;
+
+ case 'x':
+ (*info->fprintf_func) (stream, "%ld",
+ ((X_LDST_I (insn) << 8)
+ + X_ASI (insn)));
+ break;
+
+ case 'y':
+ (*info->fprintf_func) (stream, "%%y");
+ break;
+
+ case 'u':
+ case 'U':
+ {
+ int val = *s == 'U' ? X_RS1 (insn) : X_RD (insn);
+ const char *name = sparc_decode_sparclet_cpreg (val);
+
+ if (name)
+ (*info->fprintf_func) (stream, "%s", name);
+ else
+ (*info->fprintf_func) (stream, "%%cpreg(%d)", val);
+ break;
+ }
+ }
+ }
+ }
+
+ /* If we are adding or or'ing something to rs1, then
+ check to see whether the previous instruction was
+ a sethi to the same register as in the sethi.
+ If so, attempt to print the result of the add or
+ or (in this context add and or do the same thing)
+ and its symbolic value. */
+ if (imm_ored_to_rs1 || imm_added_to_rs1)
+ {
+ unsigned long prev_insn;
+ int errcode;
+
+ if (memaddr >= 4)
+ errcode =
+ (*info->read_memory_func)
+ (memaddr - 4, buffer, sizeof (buffer), info);
+ else
+ errcode = 1;
+
+ prev_insn = getword (buffer);
+
+ if (errcode == 0)
+ {
+ /* If it is a delayed branch, we need to look at the
+ instruction before the delayed branch. This handles
+ sequences such as:
+
+ sethi %o1, %hi(_foo), %o1
+ call _printf
+ or %o1, %lo(_foo), %o1 */
+
+ if (is_delayed_branch (prev_insn))
+ {
+ if (memaddr >= 8)
+ errcode = (*info->read_memory_func)
+ (memaddr - 8, buffer, sizeof (buffer), info);
+ else
+ errcode = 1;
+
+ prev_insn = getword (buffer);
+ }
+ }
+
+ /* If there was a problem reading memory, then assume
+ the previous instruction was not sethi. */
+ if (errcode == 0)
+ {
+ /* Is it sethi to the same register? */
+ if ((prev_insn & 0xc1c00000) == 0x01000000
+ && X_RD (prev_insn) == X_RS1 (insn))
+ {
+ (*info->fprintf_func) (stream, "\t! ");
+ info->target =
+ ((unsigned) 0xFFFFFFFF
+ & ((int) X_IMM22 (prev_insn) << 10));
+ if (imm_added_to_rs1)
+ info->target += X_SIMM (insn, 13);
+ else
+ info->target |= X_SIMM (insn, 13);
+ (*info->print_address_func) (info->target, info);
+ info->insn_type = dis_dref;
+ info->data_size = 4; /* FIXME!!! */
+ }
+ }
+ }
+
+ if (opcode->flags & (F_UNBR|F_CONDBR|F_JSR))
+ {
+ /* FIXME -- check is_annulled flag. */
+ if (opcode->flags & F_UNBR)
+ info->insn_type = dis_branch;
+ if (opcode->flags & F_CONDBR)
+ info->insn_type = dis_condbranch;
+ if (opcode->flags & F_JSR)
+ info->insn_type = dis_jsr;
+ if (opcode->flags & F_DELAYED)
+ info->branch_delay_insns = 1;
+ }
+
+ return sizeof (buffer);
+ }
+ }
+
+ info->insn_type = dis_noninsn; /* Mark as non-valid instruction. */
+ (*info->fprintf_func) (stream, _("unknown"));
+ return sizeof (buffer);
+}
diff --git a/sparc.ld b/sparc.ld
new file mode 100644
index 0000000..31321be
--- /dev/null
+++ b/sparc.ld
@@ -0,0 +1,150 @@
+OUTPUT_FORMAT("elf32-sparc", "elf32-sparc",
+ "elf32-sparc")
+OUTPUT_ARCH(sparc)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata) }
+ .tbss : { *(.tbss) }
+ .preinit_array :
+ {
+ PROVIDE_HIDDEN (__preinit_array_start = .);
+ KEEP (*(.preinit_array))
+ PROVIDE_HIDDEN (__preinit_array_end = .);
+ }
+ .init_array :
+ {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ }
+ .fini_array :
+ {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+ /DISCARD/ : { *(.note.GNU-stack) *(.note.ABI-tag) }
+}
diff --git a/sparc64.ld b/sparc64.ld
new file mode 100644
index 0000000..9ea4143
--- /dev/null
+++ b/sparc64.ld
@@ -0,0 +1,138 @@
+OUTPUT_FORMAT("elf64-sparc", "elf64-sparc",
+ "elf64-sparc")
+OUTPUT_ARCH(sparc:v9)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x47ff041f
+ .text :
+ {
+ *(.text)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x47ff041f
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x47ff041f
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ . = ALIGN(64 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .reginfo : { *(.reginfo) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x100000) + (. & (0x100000 - 1));
+ .data :
+ {
+ *(.gen_code)
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .plt : { *(.plt) }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
new file mode 100644
index 0000000..517f337
--- /dev/null
+++ b/spice-qemu-char.c
@@ -0,0 +1,190 @@
+#include "config-host.h"
+#include "trace.h"
+#include "ui/qemu-spice.h"
+#include <spice.h>
+#include <spice-experimental.h>
+
+#include "osdep.h"
+
+#define dprintf(_scd, _level, _fmt, ...) \
+ do { \
+ static unsigned __dprintf_counter = 0; \
+ if (_scd->debug >= _level) { \
+ fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
+ } \
+ } while (0)
+
+#define VMC_MAX_HOST_WRITE 2048
+
+typedef struct SpiceCharDriver {
+ CharDriverState* chr;
+ SpiceCharDeviceInstance sin;
+ char *subtype;
+ bool active;
+ uint8_t *buffer;
+ uint8_t *datapos;
+ ssize_t bufsize, datalen;
+ uint32_t debug;
+} SpiceCharDriver;
+
+static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
+{
+ SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+ ssize_t out = 0;
+ ssize_t last_out;
+ uint8_t* p = (uint8_t*)buf;
+
+ while (len > 0) {
+ last_out = MIN(len, VMC_MAX_HOST_WRITE);
+ qemu_chr_read(scd->chr, p, last_out);
+ if (last_out > 0) {
+ out += last_out;
+ len -= last_out;
+ p += last_out;
+ } else {
+ break;
+ }
+ }
+
+ dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out);
+ trace_spice_vmc_write(out, len + out);
+ return out;
+}
+
+static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
+{
+ SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+ int bytes = MIN(len, scd->datalen);
+
+ dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen);
+ if (bytes > 0) {
+ memcpy(buf, scd->datapos, bytes);
+ scd->datapos += bytes;
+ scd->datalen -= bytes;
+ assert(scd->datalen >= 0);
+ if (scd->datalen == 0) {
+ scd->datapos = 0;
+ }
+ }
+ trace_spice_vmc_read(bytes, len);
+ return bytes;
+}
+
+static SpiceCharDeviceInterface vmc_interface = {
+ .base.type = SPICE_INTERFACE_CHAR_DEVICE,
+ .base.description = "spice virtual channel char device",
+ .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
+ .write = vmc_write,
+ .read = vmc_read,
+};
+
+
+static void vmc_register_interface(SpiceCharDriver *scd)
+{
+ if (scd->active) {
+ return;
+ }
+ dprintf(scd, 1, "%s\n", __func__);
+ scd->sin.base.sif = &vmc_interface.base;
+ qemu_spice_add_interface(&scd->sin.base);
+ scd->active = true;
+ trace_spice_vmc_register_interface(scd);
+}
+
+static void vmc_unregister_interface(SpiceCharDriver *scd)
+{
+ if (!scd->active) {
+ return;
+ }
+ dprintf(scd, 1, "%s\n", __func__);
+ spice_server_remove_interface(&scd->sin.base);
+ scd->active = false;
+ trace_spice_vmc_unregister_interface(scd);
+}
+
+
+static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ SpiceCharDriver *s = chr->opaque;
+
+ dprintf(s, 2, "%s: %d\n", __func__, len);
+ vmc_register_interface(s);
+ assert(s->datalen == 0);
+ if (s->bufsize < len) {
+ s->bufsize = len;
+ s->buffer = qemu_realloc(s->buffer, s->bufsize);
+ }
+ memcpy(s->buffer, buf, len);
+ s->datapos = s->buffer;
+ s->datalen = len;
+ spice_server_char_device_wakeup(&s->sin);
+ return len;
+}
+
+static void spice_chr_close(struct CharDriverState *chr)
+{
+ SpiceCharDriver *s = chr->opaque;
+
+ printf("%s\n", __func__);
+ vmc_unregister_interface(s);
+ qemu_free(s);
+}
+
+static void print_allowed_subtypes(void)
+{
+ const char** psubtype;
+ int i;
+
+ fprintf(stderr, "allowed names: ");
+ for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
+ *psubtype != NULL; ++psubtype, ++i) {
+ if (i == 0) {
+ fprintf(stderr, "%s", *psubtype);
+ } else {
+ fprintf(stderr, ", %s", *psubtype);
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
+{
+ CharDriverState *chr;
+ SpiceCharDriver *s;
+ const char* name = qemu_opt_get(opts, "name");
+ uint32_t debug = qemu_opt_get_number(opts, "debug", 0);
+ const char** psubtype = spice_server_char_device_recognized_subtypes();
+ const char *subtype = NULL;
+
+ if (name == NULL) {
+ fprintf(stderr, "spice-qemu-char: missing name parameter\n");
+ print_allowed_subtypes();
+ return NULL;
+ }
+ for(;*psubtype != NULL; ++psubtype) {
+ if (strcmp(name, *psubtype) == 0) {
+ subtype = *psubtype;
+ break;
+ }
+ }
+ if (subtype == NULL) {
+ fprintf(stderr, "spice-qemu-char: unsupported name\n");
+ print_allowed_subtypes();
+ return NULL;
+ }
+
+ chr = qemu_mallocz(sizeof(CharDriverState));
+ s = qemu_mallocz(sizeof(SpiceCharDriver));
+ s->chr = chr;
+ s->debug = debug;
+ s->active = false;
+ s->sin.subtype = subtype;
+ chr->opaque = s;
+ chr->chr_write = spice_chr_write;
+ chr->chr_close = spice_chr_close;
+
+ qemu_chr_generic_open(chr);
+
+ return chr;
+}
diff --git a/sysconfigs/target/target-x86_64.conf b/sysconfigs/target/target-x86_64.conf
new file mode 100644
index 0000000..43ad282
--- /dev/null
+++ b/sysconfigs/target/target-x86_64.conf
@@ -0,0 +1,86 @@
+# x86 CPU MODELS
+
+[cpudef]
+ name = "Conroe"
+ level = "2"
+ vendor = "GenuineIntel"
+ family = "6"
+ model = "2"
+ stepping = "3"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 ssse3"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+ extfeature_ecx = "lahf_lm"
+ xlevel = "0x8000000A"
+ model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)"
+
+[cpudef]
+ name = "Penryn"
+ level = "2"
+ vendor = "GenuineIntel"
+ family = "6"
+ model = "2"
+ stepping = "3"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16 ssse3 sse4.1"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+ extfeature_ecx = "lahf_lm"
+ xlevel = "0x8000000A"
+ model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)"
+
+[cpudef]
+ name = "Nehalem"
+ level = "2"
+ vendor = "GenuineIntel"
+ family = "6"
+ model = "2"
+ stepping = "3"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16 ssse3 sse4.1 sse4.2 popcnt"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+ extfeature_ecx = "lahf_lm"
+ xlevel = "0x8000000A"
+ model_id = "Intel Core i7 9xx (Nehalem Class Core i7)"
+
+[cpudef]
+ name = "Opteron_G1"
+ level = "5"
+ vendor = "AuthenticAMD"
+ family = "15"
+ model = "6"
+ stepping = "1"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx"
+# extfeature_ecx = ""
+ xlevel = "0x80000008"
+ model_id = "AMD Opteron 240 (Gen 1 Class Opteron)"
+
+[cpudef]
+ name = "Opteron_G2"
+ level = "5"
+ vendor = "AuthenticAMD"
+ family = "15"
+ model = "6"
+ stepping = "1"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp"
+ extfeature_ecx = "svm lahf_lm"
+ xlevel = "0x80000008"
+ model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)"
+
+[cpudef]
+ name = "Opteron_G3"
+ level = "5"
+ vendor = "AuthenticAMD"
+ family = "15"
+ model = "6"
+ stepping = "1"
+ feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36"
+ feature_ecx = "sse3 cx16 monitor popcnt"
+ extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp"
+ extfeature_ecx = "svm sse4a abm misalignsse lahf_lm"
+ xlevel = "0x80000008"
+ model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)"
+
diff --git a/sysemu.h b/sysemu.h
new file mode 100644
index 0000000..359de42
--- /dev/null
+++ b/sysemu.h
@@ -0,0 +1,197 @@
+#ifndef SYSEMU_H
+#define SYSEMU_H
+/* Misc. things related to the system emulator. */
+
+#include "qemu-common.h"
+#include "qemu-option.h"
+#include "qemu-queue.h"
+#include "qemu-timer.h"
+#include "notify.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include "qemu-os-win32.h"
+#endif
+
+#ifdef CONFIG_POSIX
+#include "qemu-os-posix.h"
+#endif
+
+/* vl.c */
+extern const char *bios_name;
+
+#define QEMU_FILE_TYPE_BIOS 0
+#define QEMU_FILE_TYPE_KEYMAP 1
+char *qemu_find_file(int type, const char *name);
+
+extern int vm_running;
+extern const char *qemu_name;
+extern uint8_t qemu_uuid[];
+int qemu_uuid_parse(const char *str, uint8_t *uuid);
+#define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+
+typedef struct vm_change_state_entry VMChangeStateEntry;
+typedef void VMChangeStateHandler(void *opaque, int running, int reason);
+
+VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
+ void *opaque);
+void qemu_del_vm_change_state_handler(VMChangeStateEntry *e);
+
+void vm_start(void);
+void vm_stop(int reason);
+
+uint64_t ram_bytes_remaining(void);
+uint64_t ram_bytes_transferred(void);
+uint64_t ram_bytes_total(void);
+
+int64_t cpu_get_ticks(void);
+void cpu_enable_ticks(void);
+void cpu_disable_ticks(void);
+
+void qemu_system_reset_request(void);
+void qemu_system_shutdown_request(void);
+void qemu_system_powerdown_request(void);
+int qemu_no_shutdown(void);
+int qemu_shutdown_requested(void);
+int qemu_reset_requested(void);
+int qemu_powerdown_requested(void);
+extern qemu_irq qemu_system_powerdown;
+void qemu_system_reset(void);
+
+void qemu_add_exit_notifier(Notifier *notify);
+void qemu_remove_exit_notifier(Notifier *notify);
+
+void qemu_add_machine_init_done_notifier(Notifier *notify);
+
+void do_savevm(Monitor *mon, const QDict *qdict);
+int load_vmstate(const char *name);
+void do_delvm(Monitor *mon, const QDict *qdict);
+void do_info_snapshots(Monitor *mon);
+
+void cpu_synchronize_all_states(void);
+void cpu_synchronize_all_post_reset(void);
+void cpu_synchronize_all_post_init(void);
+
+void qemu_announce_self(void);
+
+void main_loop_wait(int nonblocking);
+
+bool qemu_savevm_state_blocked(Monitor *mon);
+int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
+ int shared);
+int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f);
+int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f);
+void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f);
+int qemu_loadvm_state(QEMUFile *f);
+
+/* SLIRP */
+void do_info_slirp(Monitor *mon);
+
+/* OS specific functions */
+void os_setup_early_signal_handling(void);
+char *os_find_datadir(const char *argv0);
+void os_parse_cmd_args(int index, const char *optarg);
+void os_pidfile_error(void);
+
+typedef enum DisplayType
+{
+ DT_DEFAULT,
+ DT_CURSES,
+ DT_SDL,
+ DT_NOGRAPHIC,
+} DisplayType;
+
+extern int autostart;
+extern int incoming_expected;
+extern int bios_size;
+
+typedef enum {
+ VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
+} VGAInterfaceType;
+
+extern int vga_interface_type;
+#define cirrus_vga_enabled (vga_interface_type == VGA_CIRRUS)
+#define std_vga_enabled (vga_interface_type == VGA_STD)
+#define xenfb_enabled (vga_interface_type == VGA_XENFB)
+#define vmsvga_enabled (vga_interface_type == VGA_VMWARE)
+#define qxl_enabled (vga_interface_type == VGA_QXL)
+
+extern int graphic_width;
+extern int graphic_height;
+extern int graphic_depth;
+extern uint8_t irq0override;
+extern DisplayType display_type;
+extern const char *keyboard_layout;
+extern int win2k_install_hack;
+extern int rtc_td_hack;
+extern int alt_grab;
+extern int ctrl_grab;
+extern int usb_enabled;
+extern int smp_cpus;
+extern int max_cpus;
+extern int cursor_hide;
+extern int graphic_rotate;
+extern int no_quit;
+extern int no_shutdown;
+extern int semihosting_enabled;
+extern int old_param;
+extern int boot_menu;
+extern QEMUClock *rtc_clock;
+extern long hpagesize;
+
+#define MAX_NODES 64
+extern int nb_numa_nodes;
+extern uint64_t node_mem[MAX_NODES];
+extern uint64_t node_cpumask[MAX_NODES];
+
+#define MAX_OPTION_ROMS 16
+typedef struct QEMUOptionRom {
+ const char *name;
+ int32_t bootindex;
+} QEMUOptionRom;
+extern QEMUOptionRom option_rom[MAX_OPTION_ROMS];
+extern int nb_option_roms;
+
+#define MAX_PROM_ENVS 128
+extern const char *prom_envs[MAX_PROM_ENVS];
+extern unsigned int nb_prom_envs;
+
+/* acpi */
+void qemu_system_cpu_hot_add(int cpu, int state);
+
+/* pci-hotplug */
+void pci_device_hot_add(Monitor *mon, const QDict *qdict);
+void drive_hot_add(Monitor *mon, const QDict *qdict);
+void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict);
+
+/* pcie aer error injection */
+void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
+int do_pcie_aer_inejct_error(Monitor *mon,
+ const QDict *qdict, QObject **ret_data);
+
+/* serial ports */
+
+#define MAX_SERIAL_PORTS 4
+
+extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+
+/* parallel ports */
+
+#define MAX_PARALLEL_PORTS 3
+
+extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+
+#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
+
+void do_usb_add(Monitor *mon, const QDict *qdict);
+void do_usb_del(Monitor *mon, const QDict *qdict);
+void usb_info(Monitor *mon);
+
+void rtc_change_mon_event(struct tm *tm);
+
+void register_devices(void);
+
+void add_boot_device_path(int32_t bootindex, DeviceState *dev,
+ const char *suffix);
+char *get_boot_devices_list(uint32_t *size);
+#endif
diff --git a/target-alpha/STATUS b/target-alpha/STATUS
new file mode 100644
index 0000000..353d543
--- /dev/null
+++ b/target-alpha/STATUS
@@ -0,0 +1,28 @@
+(to be completed)
+
+Alpha emulation structure:
+cpu.h : CPU definitions globally exported
+exec.h : CPU definitions used only for translated code execution
+helper.c : helpers that can be called either by the translated code
+ or the Qemu core, including the exception handler.
+op_helper.c : helpers that can be called only from TCG
+helper.h : TCG helpers prototypes
+translate.c : Alpha instructions to micro-operations translator
+
+Code translator status:
+The Alpha CPU instruction emulation should be quite complete with the
+limitation that the VAX floating-point load and stores are not tested.
+The 4 MMU modes are implemented.
+
+Linux user mode emulation status:
+a few programs start to run. Most crash at a certain point, dereferencing a
+NULL pointer. It seems that the UNIQUE register is not initialized properly.
+It may appear that old executables, not relying on TLS support, run but
+this is to be prooved...
+
+Full system emulation status:
+* Alpha PALCode emulation is in a very early stage and is not sufficient
+ to run any real OS. The alpha-softmmu target is not enabled for now.
+* no hardware platform description is implemented
+* there might be problems in the Alpha PALCode dedicated instructions
+ that would prevent to use a native PALCode image.
diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h
new file mode 100644
index 0000000..686fb4a
--- /dev/null
+++ b/target-alpha/cpu.h
@@ -0,0 +1,538 @@
+/*
+ * Alpha emulation cpu definitions for qemu.
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+#if !defined (__CPU_ALPHA_H__)
+#define __CPU_ALPHA_H__
+
+#include "config.h"
+
+#define TARGET_LONG_BITS 64
+
+#define CPUState struct CPUAlphaState
+
+#include "cpu-defs.h"
+
+#include <setjmp.h>
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE EM_ALPHA
+
+#define ICACHE_LINE_SIZE 32
+#define DCACHE_LINE_SIZE 32
+
+#define TARGET_PAGE_BITS 13
+
+/* ??? EV4 has 34 phys addr bits, EV5 has 40, EV6 has 44. */
+#define TARGET_PHYS_ADDR_SPACE_BITS 44
+#define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS)
+
+/* Alpha major type */
+enum {
+ ALPHA_EV3 = 1,
+ ALPHA_EV4 = 2,
+ ALPHA_SIM = 3,
+ ALPHA_LCA = 4,
+ ALPHA_EV5 = 5, /* 21164 */
+ ALPHA_EV45 = 6, /* 21064A */
+ ALPHA_EV56 = 7, /* 21164A */
+};
+
+/* EV4 minor type */
+enum {
+ ALPHA_EV4_2 = 0,
+ ALPHA_EV4_3 = 1,
+};
+
+/* LCA minor type */
+enum {
+ ALPHA_LCA_1 = 1, /* 21066 */
+ ALPHA_LCA_2 = 2, /* 20166 */
+ ALPHA_LCA_3 = 3, /* 21068 */
+ ALPHA_LCA_4 = 4, /* 21068 */
+ ALPHA_LCA_5 = 5, /* 21066A */
+ ALPHA_LCA_6 = 6, /* 21068A */
+};
+
+/* EV5 minor type */
+enum {
+ ALPHA_EV5_1 = 1, /* Rev BA, CA */
+ ALPHA_EV5_2 = 2, /* Rev DA, EA */
+ ALPHA_EV5_3 = 3, /* Pass 3 */
+ ALPHA_EV5_4 = 4, /* Pass 3.2 */
+ ALPHA_EV5_5 = 5, /* Pass 4 */
+};
+
+/* EV45 minor type */
+enum {
+ ALPHA_EV45_1 = 1, /* Pass 1 */
+ ALPHA_EV45_2 = 2, /* Pass 1.1 */
+ ALPHA_EV45_3 = 3, /* Pass 2 */
+};
+
+/* EV56 minor type */
+enum {
+ ALPHA_EV56_1 = 1, /* Pass 1 */
+ ALPHA_EV56_2 = 2, /* Pass 2 */
+};
+
+enum {
+ IMPLVER_2106x = 0, /* EV4, EV45 & LCA45 */
+ IMPLVER_21164 = 1, /* EV5, EV56 & PCA45 */
+ IMPLVER_21264 = 2, /* EV6, EV67 & EV68x */
+ IMPLVER_21364 = 3, /* EV7 & EV79 */
+};
+
+enum {
+ AMASK_BWX = 0x00000001,
+ AMASK_FIX = 0x00000002,
+ AMASK_CIX = 0x00000004,
+ AMASK_MVI = 0x00000100,
+ AMASK_TRAP = 0x00000200,
+ AMASK_PREFETCH = 0x00001000,
+};
+
+enum {
+ VAX_ROUND_NORMAL = 0,
+ VAX_ROUND_CHOPPED,
+};
+
+enum {
+ IEEE_ROUND_NORMAL = 0,
+ IEEE_ROUND_DYNAMIC,
+ IEEE_ROUND_PLUS,
+ IEEE_ROUND_MINUS,
+ IEEE_ROUND_CHOPPED,
+};
+
+/* IEEE floating-point operations encoding */
+/* Trap mode */
+enum {
+ FP_TRAP_I = 0x0,
+ FP_TRAP_U = 0x1,
+ FP_TRAP_S = 0x4,
+ FP_TRAP_SU = 0x5,
+ FP_TRAP_SUI = 0x7,
+};
+
+/* Rounding mode */
+enum {
+ FP_ROUND_CHOPPED = 0x0,
+ FP_ROUND_MINUS = 0x1,
+ FP_ROUND_NORMAL = 0x2,
+ FP_ROUND_DYNAMIC = 0x3,
+};
+
+/* FPCR bits */
+#define FPCR_SUM (1ULL << 63)
+#define FPCR_INED (1ULL << 62)
+#define FPCR_UNFD (1ULL << 61)
+#define FPCR_UNDZ (1ULL << 60)
+#define FPCR_DYN_SHIFT 58
+#define FPCR_DYN_CHOPPED (0ULL << FPCR_DYN_SHIFT)
+#define FPCR_DYN_MINUS (1ULL << FPCR_DYN_SHIFT)
+#define FPCR_DYN_NORMAL (2ULL << FPCR_DYN_SHIFT)
+#define FPCR_DYN_PLUS (3ULL << FPCR_DYN_SHIFT)
+#define FPCR_DYN_MASK (3ULL << FPCR_DYN_SHIFT)
+#define FPCR_IOV (1ULL << 57)
+#define FPCR_INE (1ULL << 56)
+#define FPCR_UNF (1ULL << 55)
+#define FPCR_OVF (1ULL << 54)
+#define FPCR_DZE (1ULL << 53)
+#define FPCR_INV (1ULL << 52)
+#define FPCR_OVFD (1ULL << 51)
+#define FPCR_DZED (1ULL << 50)
+#define FPCR_INVD (1ULL << 49)
+#define FPCR_DNZ (1ULL << 48)
+#define FPCR_DNOD (1ULL << 47)
+#define FPCR_STATUS_MASK (FPCR_IOV | FPCR_INE | FPCR_UNF \
+ | FPCR_OVF | FPCR_DZE | FPCR_INV)
+
+/* The silly software trap enables implemented by the kernel emulation.
+ These are more or less architecturally required, since the real hardware
+ has read-as-zero bits in the FPCR when the features aren't implemented.
+ For the purposes of QEMU, we pretend the FPCR can hold everything. */
+#define SWCR_TRAP_ENABLE_INV (1ULL << 1)
+#define SWCR_TRAP_ENABLE_DZE (1ULL << 2)
+#define SWCR_TRAP_ENABLE_OVF (1ULL << 3)
+#define SWCR_TRAP_ENABLE_UNF (1ULL << 4)
+#define SWCR_TRAP_ENABLE_INE (1ULL << 5)
+#define SWCR_TRAP_ENABLE_DNO (1ULL << 6)
+#define SWCR_TRAP_ENABLE_MASK ((1ULL << 7) - (1ULL << 1))
+
+#define SWCR_MAP_DMZ (1ULL << 12)
+#define SWCR_MAP_UMZ (1ULL << 13)
+#define SWCR_MAP_MASK (SWCR_MAP_DMZ | SWCR_MAP_UMZ)
+
+#define SWCR_STATUS_INV (1ULL << 17)
+#define SWCR_STATUS_DZE (1ULL << 18)
+#define SWCR_STATUS_OVF (1ULL << 19)
+#define SWCR_STATUS_UNF (1ULL << 20)
+#define SWCR_STATUS_INE (1ULL << 21)
+#define SWCR_STATUS_DNO (1ULL << 22)
+#define SWCR_STATUS_MASK ((1ULL << 23) - (1ULL << 17))
+
+#define SWCR_MASK (SWCR_TRAP_ENABLE_MASK | SWCR_MAP_MASK | SWCR_STATUS_MASK)
+
+/* Internal processor registers */
+/* XXX: TOFIX: most of those registers are implementation dependant */
+enum {
+#if defined(CONFIG_USER_ONLY)
+ IPR_EXC_ADDR,
+ IPR_EXC_SUM,
+ IPR_EXC_MASK,
+#else
+ /* Ebox IPRs */
+ IPR_CC = 0xC0, /* 21264 */
+ IPR_CC_CTL = 0xC1, /* 21264 */
+#define IPR_CC_CTL_ENA_SHIFT 32
+#define IPR_CC_CTL_COUNTER_MASK 0xfffffff0UL
+ IPR_VA = 0xC2, /* 21264 */
+ IPR_VA_CTL = 0xC4, /* 21264 */
+#define IPR_VA_CTL_VA_48_SHIFT 1
+#define IPR_VA_CTL_VPTB_SHIFT 30
+ IPR_VA_FORM = 0xC3, /* 21264 */
+ /* Ibox IPRs */
+ IPR_ITB_TAG = 0x00, /* 21264 */
+ IPR_ITB_PTE = 0x01, /* 21264 */
+ IPR_ITB_IAP = 0x02,
+ IPR_ITB_IA = 0x03, /* 21264 */
+ IPR_ITB_IS = 0x04, /* 21264 */
+ IPR_PMPC = 0x05,
+ IPR_EXC_ADDR = 0x06, /* 21264 */
+ IPR_IVA_FORM = 0x07, /* 21264 */
+ IPR_CM = 0x09, /* 21264 */
+#define IPR_CM_SHIFT 3
+#define IPR_CM_MASK (3ULL << IPR_CM_SHIFT) /* 21264 */
+ IPR_IER = 0x0A, /* 21264 */
+#define IPR_IER_MASK 0x0000007fffffe000ULL
+ IPR_IER_CM = 0x0B, /* 21264: = CM | IER */
+ IPR_SIRR = 0x0C, /* 21264 */
+#define IPR_SIRR_SHIFT 14
+#define IPR_SIRR_MASK 0x7fff
+ IPR_ISUM = 0x0D, /* 21264 */
+ IPR_HW_INT_CLR = 0x0E, /* 21264 */
+ IPR_EXC_SUM = 0x0F,
+ IPR_PAL_BASE = 0x10,
+ IPR_I_CTL = 0x11,
+#define IPR_I_CTL_CHIP_ID_SHIFT 24 /* 21264 */
+#define IPR_I_CTL_BIST_FAIL (1 << 23) /* 21264 */
+#define IPR_I_CTL_IC_EN_SHIFT 2 /* 21264 */
+#define IPR_I_CTL_SDE1_SHIFT 7 /* 21264 */
+#define IPR_I_CTL_HWE_SHIFT 12 /* 21264 */
+#define IPR_I_CTL_VA_48_SHIFT 15 /* 21264 */
+#define IPR_I_CTL_SPE_SHIFT 3 /* 21264 */
+#define IPR_I_CTL_CALL_PAL_R23_SHIFT 20 /* 21264 */
+ IPR_I_STAT = 0x16, /* 21264 */
+ IPR_IC_FLUSH = 0x13, /* 21264 */
+ IPR_IC_FLUSH_ASM = 0x12, /* 21264 */
+ IPR_CLR_MAP = 0x15,
+ IPR_SLEEP = 0x17,
+ IPR_PCTX = 0x40,
+ IPR_PCTX_ASN = 0x01, /* field */
+#define IPR_PCTX_ASN_SHIFT 39
+ IPR_PCTX_ASTER = 0x02, /* field */
+#define IPR_PCTX_ASTER_SHIFT 5
+ IPR_PCTX_ASTRR = 0x04, /* field */
+#define IPR_PCTX_ASTRR_SHIFT 9
+ IPR_PCTX_PPCE = 0x08, /* field */
+#define IPR_PCTX_PPCE_SHIFT 1
+ IPR_PCTX_FPE = 0x10, /* field */
+#define IPR_PCTX_FPE_SHIFT 2
+ IPR_PCTX_ALL = 0x5f, /* all fields */
+ IPR_PCTR_CTL = 0x14, /* 21264 */
+ /* Mbox IPRs */
+ IPR_DTB_TAG0 = 0x20, /* 21264 */
+ IPR_DTB_TAG1 = 0xA0, /* 21264 */
+ IPR_DTB_PTE0 = 0x21, /* 21264 */
+ IPR_DTB_PTE1 = 0xA1, /* 21264 */
+ IPR_DTB_ALTMODE = 0xA6,
+ IPR_DTB_ALTMODE0 = 0x26, /* 21264 */
+#define IPR_DTB_ALTMODE_MASK 3
+ IPR_DTB_IAP = 0xA2,
+ IPR_DTB_IA = 0xA3, /* 21264 */
+ IPR_DTB_IS0 = 0x24,
+ IPR_DTB_IS1 = 0xA4,
+ IPR_DTB_ASN0 = 0x25, /* 21264 */
+ IPR_DTB_ASN1 = 0xA5, /* 21264 */
+#define IPR_DTB_ASN_SHIFT 56
+ IPR_MM_STAT = 0x27, /* 21264 */
+ IPR_M_CTL = 0x28, /* 21264 */
+#define IPR_M_CTL_SPE_SHIFT 1
+#define IPR_M_CTL_SPE_MASK 7
+ IPR_DC_CTL = 0x29, /* 21264 */
+ IPR_DC_STAT = 0x2A, /* 21264 */
+ /* Cbox IPRs */
+ IPR_C_DATA = 0x2B,
+ IPR_C_SHIFT = 0x2C,
+
+ IPR_ASN,
+ IPR_ASTEN,
+ IPR_ASTSR,
+ IPR_DATFX,
+ IPR_ESP,
+ IPR_FEN,
+ IPR_IPIR,
+ IPR_IPL,
+ IPR_KSP,
+ IPR_MCES,
+ IPR_PERFMON,
+ IPR_PCBB,
+ IPR_PRBR,
+ IPR_PTBR,
+ IPR_SCBB,
+ IPR_SISR,
+ IPR_SSP,
+ IPR_SYSPTBR,
+ IPR_TBCHK,
+ IPR_TBIA,
+ IPR_TBIAP,
+ IPR_TBIS,
+ IPR_TBISD,
+ IPR_TBISI,
+ IPR_USP,
+ IPR_VIRBND,
+ IPR_VPTB,
+ IPR_WHAMI,
+ IPR_ALT_MODE,
+#endif
+ IPR_LAST,
+};
+
+typedef struct CPUAlphaState CPUAlphaState;
+
+typedef struct pal_handler_t pal_handler_t;
+struct pal_handler_t {
+ /* Reset */
+ void (*reset)(CPUAlphaState *env);
+ /* Uncorrectable hardware error */
+ void (*machine_check)(CPUAlphaState *env);
+ /* Arithmetic exception */
+ void (*arithmetic)(CPUAlphaState *env);
+ /* Interrupt / correctable hardware error */
+ void (*interrupt)(CPUAlphaState *env);
+ /* Data fault */
+ void (*dfault)(CPUAlphaState *env);
+ /* DTB miss pal */
+ void (*dtb_miss_pal)(CPUAlphaState *env);
+ /* DTB miss native */
+ void (*dtb_miss_native)(CPUAlphaState *env);
+ /* Unaligned access */
+ void (*unalign)(CPUAlphaState *env);
+ /* ITB miss */
+ void (*itb_miss)(CPUAlphaState *env);
+ /* Instruction stream access violation */
+ void (*itb_acv)(CPUAlphaState *env);
+ /* Reserved or privileged opcode */
+ void (*opcdec)(CPUAlphaState *env);
+ /* Floating point exception */
+ void (*fen)(CPUAlphaState *env);
+ /* Call pal instruction */
+ void (*call_pal)(CPUAlphaState *env, uint32_t palcode);
+};
+
+#define NB_MMU_MODES 4
+
+struct CPUAlphaState {
+ uint64_t ir[31];
+ float64 fir[31];
+ uint64_t pc;
+ uint64_t ipr[IPR_LAST];
+ uint64_t ps;
+ uint64_t unique;
+ uint64_t lock_addr;
+ uint64_t lock_st_addr;
+ uint64_t lock_value;
+ float_status fp_status;
+ /* The following fields make up the FPCR, but in FP_STATUS format. */
+ uint8_t fpcr_exc_status;
+ uint8_t fpcr_exc_mask;
+ uint8_t fpcr_dyn_round;
+ uint8_t fpcr_flush_to_zero;
+ uint8_t fpcr_dnz;
+ uint8_t fpcr_dnod;
+ uint8_t fpcr_undz;
+
+ /* Used for HW_LD / HW_ST */
+ uint8_t saved_mode;
+ /* For RC and RS */
+ uint8_t intr_flag;
+
+#if TARGET_LONG_BITS > HOST_LONG_BITS
+ /* temporary fixed-point registers
+ * used to emulate 64 bits target on 32 bits hosts
+ */
+ target_ulong t0, t1;
+#endif
+
+ /* Those resources are used only in Qemu core */
+ CPU_COMMON
+
+ uint32_t hflags;
+
+ int error_code;
+
+ uint32_t features;
+ uint32_t amask;
+ int implver;
+ pal_handler_t *pal_handler;
+};
+
+#define cpu_init cpu_alpha_init
+#define cpu_exec cpu_alpha_exec
+#define cpu_gen_code cpu_alpha_gen_code
+#define cpu_signal_handler cpu_alpha_signal_handler
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _executive
+#define MMU_MODE2_SUFFIX _supervisor
+#define MMU_MODE3_SUFFIX _user
+#define MMU_USER_IDX 3
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return (env->ps >> 3) & 3;
+}
+
+#include "cpu-all.h"
+
+enum {
+ FEATURE_ASN = 0x00000001,
+ FEATURE_SPS = 0x00000002,
+ FEATURE_VIRBND = 0x00000004,
+ FEATURE_TBCHK = 0x00000008,
+};
+
+enum {
+ EXCP_RESET = 0x0000,
+ EXCP_MCHK = 0x0020,
+ EXCP_ARITH = 0x0060,
+ EXCP_HW_INTERRUPT = 0x00E0,
+ EXCP_DFAULT = 0x01E0,
+ EXCP_DTB_MISS_PAL = 0x09E0,
+ EXCP_ITB_MISS = 0x03E0,
+ EXCP_ITB_ACV = 0x07E0,
+ EXCP_DTB_MISS_NATIVE = 0x08E0,
+ EXCP_UNALIGN = 0x11E0,
+ EXCP_OPCDEC = 0x13E0,
+ EXCP_FEN = 0x17E0,
+ EXCP_CALL_PAL = 0x2000,
+ EXCP_CALL_PALP = 0x3000,
+ EXCP_CALL_PALE = 0x4000,
+ /* Pseudo exception for console */
+ EXCP_CONSOLE_DISPATCH = 0x4001,
+ EXCP_CONSOLE_FIXUP = 0x4002,
+ EXCP_STL_C = 0x4003,
+ EXCP_STQ_C = 0x4004,
+};
+
+/* Arithmetic exception */
+#define EXC_M_IOV (1<<16) /* Integer Overflow */
+#define EXC_M_INE (1<<15) /* Inexact result */
+#define EXC_M_UNF (1<<14) /* Underflow */
+#define EXC_M_FOV (1<<13) /* Overflow */
+#define EXC_M_DZE (1<<12) /* Division by zero */
+#define EXC_M_INV (1<<11) /* Invalid operation */
+#define EXC_M_SWC (1<<10) /* Software completion */
+
+enum {
+ IR_V0 = 0,
+ IR_T0 = 1,
+ IR_T1 = 2,
+ IR_T2 = 3,
+ IR_T3 = 4,
+ IR_T4 = 5,
+ IR_T5 = 6,
+ IR_T6 = 7,
+ IR_T7 = 8,
+ IR_S0 = 9,
+ IR_S1 = 10,
+ IR_S2 = 11,
+ IR_S3 = 12,
+ IR_S4 = 13,
+ IR_S5 = 14,
+ IR_S6 = 15,
+ IR_FP = IR_S6,
+ IR_A0 = 16,
+ IR_A1 = 17,
+ IR_A2 = 18,
+ IR_A3 = 19,
+ IR_A4 = 20,
+ IR_A5 = 21,
+ IR_T8 = 22,
+ IR_T9 = 23,
+ IR_T10 = 24,
+ IR_T11 = 25,
+ IR_RA = 26,
+ IR_T12 = 27,
+ IR_PV = IR_T12,
+ IR_AT = 28,
+ IR_GP = 29,
+ IR_SP = 30,
+ IR_ZERO = 31,
+};
+
+CPUAlphaState * cpu_alpha_init (const char *cpu_model);
+int cpu_alpha_exec(CPUAlphaState *s);
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_alpha_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+int cpu_alpha_handle_mmu_fault (CPUState *env, uint64_t address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_alpha_handle_mmu_fault
+void do_interrupt (CPUState *env);
+
+uint64_t cpu_alpha_load_fpcr (CPUState *env);
+void cpu_alpha_store_fpcr (CPUState *env, uint64_t val);
+int cpu_alpha_mfpr (CPUState *env, int iprn, uint64_t *valp);
+int cpu_alpha_mtpr (CPUState *env, int iprn, uint64_t val, uint64_t *oldvalp);
+#if !defined (CONFIG_USER_ONLY)
+void pal_init (CPUState *env);
+void call_pal (CPUState *env);
+#endif
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = env->ps;
+}
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp) {
+ env->ir[IR_SP] = newsp;
+ }
+ env->ir[IR_V0] = 0;
+ env->ir[IR_A3] = 0;
+}
+
+static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
+{
+ env->unique = newtls;
+}
+#endif
+
+#endif /* !defined (__CPU_ALPHA_H__) */
diff --git a/target-alpha/exec.h b/target-alpha/exec.h
new file mode 100644
index 0000000..a8a38d2
--- /dev/null
+++ b/target-alpha/exec.h
@@ -0,0 +1,61 @@
+/*
+ * Alpha emulation cpu run-time definitions for qemu.
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+#if !defined (__ALPHA_EXEC_H__)
+#define __ALPHA_EXEC_H__
+
+#include "config.h"
+
+#include "dyngen-exec.h"
+
+#define TARGET_LONG_BITS 64
+
+register struct CPUAlphaState *env asm(AREG0);
+
+#define FP_STATUS (env->fp_status)
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request & CPU_INTERRUPT_HARD);
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+ if (!env->halted)
+ return 0;
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->pc = tb->pc;
+}
+
+#endif /* !defined (__ALPHA_EXEC_H__) */
diff --git a/target-alpha/helper.c b/target-alpha/helper.c
new file mode 100644
index 0000000..3ba4478
--- /dev/null
+++ b/target-alpha/helper.c
@@ -0,0 +1,570 @@
+/*
+ * Alpha emulation cpu helpers for qemu.
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "softfloat.h"
+
+uint64_t cpu_alpha_load_fpcr (CPUState *env)
+{
+ uint64_t r = 0;
+ uint8_t t;
+
+ t = env->fpcr_exc_status;
+ if (t) {
+ r = FPCR_SUM;
+ if (t & float_flag_invalid) {
+ r |= FPCR_INV;
+ }
+ if (t & float_flag_divbyzero) {
+ r |= FPCR_DZE;
+ }
+ if (t & float_flag_overflow) {
+ r |= FPCR_OVF;
+ }
+ if (t & float_flag_underflow) {
+ r |= FPCR_UNF;
+ }
+ if (t & float_flag_inexact) {
+ r |= FPCR_INE;
+ }
+ }
+
+ t = env->fpcr_exc_mask;
+ if (t & float_flag_invalid) {
+ r |= FPCR_INVD;
+ }
+ if (t & float_flag_divbyzero) {
+ r |= FPCR_DZED;
+ }
+ if (t & float_flag_overflow) {
+ r |= FPCR_OVFD;
+ }
+ if (t & float_flag_underflow) {
+ r |= FPCR_UNFD;
+ }
+ if (t & float_flag_inexact) {
+ r |= FPCR_INED;
+ }
+
+ switch (env->fpcr_dyn_round) {
+ case float_round_nearest_even:
+ r |= FPCR_DYN_NORMAL;
+ break;
+ case float_round_down:
+ r |= FPCR_DYN_MINUS;
+ break;
+ case float_round_up:
+ r |= FPCR_DYN_PLUS;
+ break;
+ case float_round_to_zero:
+ r |= FPCR_DYN_CHOPPED;
+ break;
+ }
+
+ if (env->fpcr_dnz) {
+ r |= FPCR_DNZ;
+ }
+ if (env->fpcr_dnod) {
+ r |= FPCR_DNOD;
+ }
+ if (env->fpcr_undz) {
+ r |= FPCR_UNDZ;
+ }
+
+ return r;
+}
+
+void cpu_alpha_store_fpcr (CPUState *env, uint64_t val)
+{
+ uint8_t t;
+
+ t = 0;
+ if (val & FPCR_INV) {
+ t |= float_flag_invalid;
+ }
+ if (val & FPCR_DZE) {
+ t |= float_flag_divbyzero;
+ }
+ if (val & FPCR_OVF) {
+ t |= float_flag_overflow;
+ }
+ if (val & FPCR_UNF) {
+ t |= float_flag_underflow;
+ }
+ if (val & FPCR_INE) {
+ t |= float_flag_inexact;
+ }
+ env->fpcr_exc_status = t;
+
+ t = 0;
+ if (val & FPCR_INVD) {
+ t |= float_flag_invalid;
+ }
+ if (val & FPCR_DZED) {
+ t |= float_flag_divbyzero;
+ }
+ if (val & FPCR_OVFD) {
+ t |= float_flag_overflow;
+ }
+ if (val & FPCR_UNFD) {
+ t |= float_flag_underflow;
+ }
+ if (val & FPCR_INED) {
+ t |= float_flag_inexact;
+ }
+ env->fpcr_exc_mask = t;
+
+ switch (val & FPCR_DYN_MASK) {
+ case FPCR_DYN_CHOPPED:
+ t = float_round_to_zero;
+ break;
+ case FPCR_DYN_MINUS:
+ t = float_round_down;
+ break;
+ case FPCR_DYN_NORMAL:
+ t = float_round_nearest_even;
+ break;
+ case FPCR_DYN_PLUS:
+ t = float_round_up;
+ break;
+ }
+ env->fpcr_dyn_round = t;
+
+ env->fpcr_flush_to_zero
+ = (val & (FPCR_UNDZ|FPCR_UNFD)) == (FPCR_UNDZ|FPCR_UNFD);
+
+ env->fpcr_dnz = (val & FPCR_DNZ) != 0;
+ env->fpcr_dnod = (val & FPCR_DNOD) != 0;
+ env->fpcr_undz = (val & FPCR_UNDZ) != 0;
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_alpha_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ if (rw == 2)
+ env->exception_index = EXCP_ITB_MISS;
+ else
+ env->exception_index = EXCP_DFAULT;
+ env->ipr[IPR_EXC_ADDR] = address;
+
+ return 1;
+}
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+
+#else
+
+target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
+{
+ return -1;
+}
+
+int cpu_alpha_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ uint32_t opc;
+
+ if (rw == 2) {
+ /* Instruction translation buffer miss */
+ env->exception_index = EXCP_ITB_MISS;
+ } else {
+ if (env->ipr[IPR_EXC_ADDR] & 1)
+ env->exception_index = EXCP_DTB_MISS_PAL;
+ else
+ env->exception_index = EXCP_DTB_MISS_NATIVE;
+ opc = (ldl_code(env->pc) >> 21) << 4;
+ if (rw) {
+ opc |= 0x9;
+ } else {
+ opc |= 0x4;
+ }
+ env->ipr[IPR_MM_STAT] = opc;
+ }
+
+ return 1;
+}
+
+int cpu_alpha_mfpr (CPUState *env, int iprn, uint64_t *valp)
+{
+ uint64_t hwpcb;
+ int ret = 0;
+
+ hwpcb = env->ipr[IPR_PCBB];
+ switch (iprn) {
+ case IPR_ASN:
+ if (env->features & FEATURE_ASN)
+ *valp = env->ipr[IPR_ASN];
+ else
+ *valp = 0;
+ break;
+ case IPR_ASTEN:
+ *valp = ((int64_t)(env->ipr[IPR_ASTEN] << 60)) >> 60;
+ break;
+ case IPR_ASTSR:
+ *valp = ((int64_t)(env->ipr[IPR_ASTSR] << 60)) >> 60;
+ break;
+ case IPR_DATFX:
+ /* Write only */
+ ret = -1;
+ break;
+ case IPR_ESP:
+ if (env->features & FEATURE_SPS)
+ *valp = env->ipr[IPR_ESP];
+ else
+ *valp = ldq_raw(hwpcb + 8);
+ break;
+ case IPR_FEN:
+ *valp = ((int64_t)(env->ipr[IPR_FEN] << 63)) >> 63;
+ break;
+ case IPR_IPIR:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_IPL:
+ *valp = ((int64_t)(env->ipr[IPR_IPL] << 59)) >> 59;
+ break;
+ case IPR_KSP:
+ if (!(env->ipr[IPR_EXC_ADDR] & 1)) {
+ ret = -1;
+ } else {
+ if (env->features & FEATURE_SPS)
+ *valp = env->ipr[IPR_KSP];
+ else
+ *valp = ldq_raw(hwpcb + 0);
+ }
+ break;
+ case IPR_MCES:
+ *valp = ((int64_t)(env->ipr[IPR_MCES] << 59)) >> 59;
+ break;
+ case IPR_PERFMON:
+ /* Implementation specific */
+ *valp = 0;
+ break;
+ case IPR_PCBB:
+ *valp = ((int64_t)env->ipr[IPR_PCBB] << 16) >> 16;
+ break;
+ case IPR_PRBR:
+ *valp = env->ipr[IPR_PRBR];
+ break;
+ case IPR_PTBR:
+ *valp = env->ipr[IPR_PTBR];
+ break;
+ case IPR_SCBB:
+ *valp = (int64_t)((int32_t)env->ipr[IPR_SCBB]);
+ break;
+ case IPR_SIRR:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_SISR:
+ *valp = (int64_t)((int16_t)env->ipr[IPR_SISR]);
+ case IPR_SSP:
+ if (env->features & FEATURE_SPS)
+ *valp = env->ipr[IPR_SSP];
+ else
+ *valp = ldq_raw(hwpcb + 16);
+ break;
+ case IPR_SYSPTBR:
+ if (env->features & FEATURE_VIRBND)
+ *valp = env->ipr[IPR_SYSPTBR];
+ else
+ ret = -1;
+ break;
+ case IPR_TBCHK:
+ if ((env->features & FEATURE_TBCHK)) {
+ /* XXX: TODO */
+ *valp = 0;
+ ret = -1;
+ } else {
+ ret = -1;
+ }
+ break;
+ case IPR_TBIA:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_TBIAP:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_TBIS:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_TBISD:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_TBISI:
+ /* Write-only */
+ ret = -1;
+ break;
+ case IPR_USP:
+ if (env->features & FEATURE_SPS)
+ *valp = env->ipr[IPR_USP];
+ else
+ *valp = ldq_raw(hwpcb + 24);
+ break;
+ case IPR_VIRBND:
+ if (env->features & FEATURE_VIRBND)
+ *valp = env->ipr[IPR_VIRBND];
+ else
+ ret = -1;
+ break;
+ case IPR_VPTB:
+ *valp = env->ipr[IPR_VPTB];
+ break;
+ case IPR_WHAMI:
+ *valp = env->ipr[IPR_WHAMI];
+ break;
+ default:
+ /* Invalid */
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+int cpu_alpha_mtpr (CPUState *env, int iprn, uint64_t val, uint64_t *oldvalp)
+{
+ uint64_t hwpcb, tmp64;
+ uint8_t tmp8;
+ int ret = 0;
+
+ hwpcb = env->ipr[IPR_PCBB];
+ switch (iprn) {
+ case IPR_ASN:
+ /* Read-only */
+ ret = -1;
+ break;
+ case IPR_ASTEN:
+ tmp8 = ((int8_t)(env->ipr[IPR_ASTEN] << 4)) >> 4;
+ *oldvalp = tmp8;
+ tmp8 &= val & 0xF;
+ tmp8 |= (val >> 4) & 0xF;
+ env->ipr[IPR_ASTEN] &= ~0xF;
+ env->ipr[IPR_ASTEN] |= tmp8;
+ ret = 1;
+ break;
+ case IPR_ASTSR:
+ tmp8 = ((int8_t)(env->ipr[IPR_ASTSR] << 4)) >> 4;
+ *oldvalp = tmp8;
+ tmp8 &= val & 0xF;
+ tmp8 |= (val >> 4) & 0xF;
+ env->ipr[IPR_ASTSR] &= ~0xF;
+ env->ipr[IPR_ASTSR] |= tmp8;
+ ret = 1;
+ case IPR_DATFX:
+ env->ipr[IPR_DATFX] &= ~0x1;
+ env->ipr[IPR_DATFX] |= val & 1;
+ tmp64 = ldq_raw(hwpcb + 56);
+ tmp64 &= ~0x8000000000000000ULL;
+ tmp64 |= (val & 1) << 63;
+ stq_raw(hwpcb + 56, tmp64);
+ break;
+ case IPR_ESP:
+ if (env->features & FEATURE_SPS)
+ env->ipr[IPR_ESP] = val;
+ else
+ stq_raw(hwpcb + 8, val);
+ break;
+ case IPR_FEN:
+ env->ipr[IPR_FEN] = val & 1;
+ tmp64 = ldq_raw(hwpcb + 56);
+ tmp64 &= ~1;
+ tmp64 |= val & 1;
+ stq_raw(hwpcb + 56, tmp64);
+ break;
+ case IPR_IPIR:
+ /* XXX: TODO: Send IRQ to CPU #ir[16] */
+ break;
+ case IPR_IPL:
+ *oldvalp = ((int64_t)(env->ipr[IPR_IPL] << 59)) >> 59;
+ env->ipr[IPR_IPL] &= ~0x1F;
+ env->ipr[IPR_IPL] |= val & 0x1F;
+ /* XXX: may issue an interrupt or ASR _now_ */
+ ret = 1;
+ break;
+ case IPR_KSP:
+ if (!(env->ipr[IPR_EXC_ADDR] & 1)) {
+ ret = -1;
+ } else {
+ if (env->features & FEATURE_SPS)
+ env->ipr[IPR_KSP] = val;
+ else
+ stq_raw(hwpcb + 0, val);
+ }
+ break;
+ case IPR_MCES:
+ env->ipr[IPR_MCES] &= ~((val & 0x7) | 0x18);
+ env->ipr[IPR_MCES] |= val & 0x18;
+ break;
+ case IPR_PERFMON:
+ /* Implementation specific */
+ *oldvalp = 0;
+ ret = 1;
+ break;
+ case IPR_PCBB:
+ /* Read-only */
+ ret = -1;
+ break;
+ case IPR_PRBR:
+ env->ipr[IPR_PRBR] = val;
+ break;
+ case IPR_PTBR:
+ /* Read-only */
+ ret = -1;
+ break;
+ case IPR_SCBB:
+ env->ipr[IPR_SCBB] = (uint32_t)val;
+ break;
+ case IPR_SIRR:
+ if (val & 0xF) {
+ env->ipr[IPR_SISR] |= 1 << (val & 0xF);
+ /* XXX: request a software interrupt _now_ */
+ }
+ break;
+ case IPR_SISR:
+ /* Read-only */
+ ret = -1;
+ break;
+ case IPR_SSP:
+ if (env->features & FEATURE_SPS)
+ env->ipr[IPR_SSP] = val;
+ else
+ stq_raw(hwpcb + 16, val);
+ break;
+ case IPR_SYSPTBR:
+ if (env->features & FEATURE_VIRBND)
+ env->ipr[IPR_SYSPTBR] = val;
+ else
+ ret = -1;
+ break;
+ case IPR_TBCHK:
+ /* Read-only */
+ ret = -1;
+ break;
+ case IPR_TBIA:
+ tlb_flush(env, 1);
+ break;
+ case IPR_TBIAP:
+ tlb_flush(env, 1);
+ break;
+ case IPR_TBIS:
+ tlb_flush_page(env, val);
+ break;
+ case IPR_TBISD:
+ tlb_flush_page(env, val);
+ break;
+ case IPR_TBISI:
+ tlb_flush_page(env, val);
+ break;
+ case IPR_USP:
+ if (env->features & FEATURE_SPS)
+ env->ipr[IPR_USP] = val;
+ else
+ stq_raw(hwpcb + 24, val);
+ break;
+ case IPR_VIRBND:
+ if (env->features & FEATURE_VIRBND)
+ env->ipr[IPR_VIRBND] = val;
+ else
+ ret = -1;
+ break;
+ case IPR_VPTB:
+ env->ipr[IPR_VPTB] = val;
+ break;
+ case IPR_WHAMI:
+ /* Read-only */
+ ret = -1;
+ break;
+ default:
+ /* Invalid */
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+void do_interrupt (CPUState *env)
+{
+ int excp;
+
+ env->ipr[IPR_EXC_ADDR] = env->pc | 1;
+ excp = env->exception_index;
+ env->exception_index = -1;
+ env->error_code = 0;
+ /* XXX: disable interrupts and memory mapping */
+ if (env->ipr[IPR_PAL_BASE] != -1ULL) {
+ /* We use native PALcode */
+ env->pc = env->ipr[IPR_PAL_BASE] + excp;
+ } else {
+ /* We use emulated PALcode */
+ call_pal(env);
+ /* Emulate REI */
+ env->pc = env->ipr[IPR_EXC_ADDR] & ~7;
+ env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1;
+ /* XXX: re-enable interrupts and memory mapping */
+ }
+}
+#endif
+
+void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ static const char *linux_reg_names[] = {
+ "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
+ "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
+ "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
+ "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
+ };
+ int i;
+
+ cpu_fprintf(f, " PC " TARGET_FMT_lx " PS " TARGET_FMT_lx "\n",
+ env->pc, env->ps);
+ for (i = 0; i < 31; i++) {
+ cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
+ linux_reg_names[i], env->ir[i]);
+ if ((i % 3) == 2)
+ cpu_fprintf(f, "\n");
+ }
+
+ cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
+ env->lock_addr, env->lock_value);
+
+ for (i = 0; i < 31; i++) {
+ cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i,
+ *((uint64_t *)(&env->fir[i])));
+ if ((i % 3) == 2)
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "\n");
+}
diff --git a/target-alpha/helper.h b/target-alpha/helper.h
new file mode 100644
index 0000000..ccf6a2a
--- /dev/null
+++ b/target-alpha/helper.h
@@ -0,0 +1,126 @@
+#include "def-helper.h"
+
+DEF_HELPER_2(excp, void, int, int)
+DEF_HELPER_FLAGS_0(load_pcc, TCG_CALL_CONST | TCG_CALL_PURE, i64)
+
+DEF_HELPER_2(addqv, i64, i64, i64)
+DEF_HELPER_2(addlv, i64, i64, i64)
+DEF_HELPER_2(subqv, i64, i64, i64)
+DEF_HELPER_2(sublv, i64, i64, i64)
+DEF_HELPER_2(mullv, i64, i64, i64)
+DEF_HELPER_2(mulqv, i64, i64, i64)
+DEF_HELPER_FLAGS_2(umulh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_1(ctlz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_1(cttz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+
+DEF_HELPER_FLAGS_2(zap, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(zapnot, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_2(cmpbge, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_2(minub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(minsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(minuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(minsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(maxsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(perr, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_1(pklb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_1(pkwb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_1(unpkbl, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_1(unpkbw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+
+DEF_HELPER_FLAGS_0(load_fpcr, TCG_CALL_CONST | TCG_CALL_PURE, i64)
+DEF_HELPER_FLAGS_1(store_fpcr, TCG_CALL_CONST, void, i64)
+
+DEF_HELPER_FLAGS_1(f_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64)
+DEF_HELPER_FLAGS_1(memory_to_f, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32)
+DEF_HELPER_FLAGS_2(addf, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(subf, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(mulf, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(divf, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_1(sqrtf, TCG_CALL_CONST, i64, i64)
+
+DEF_HELPER_FLAGS_1(g_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_1(memory_to_g, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64)
+DEF_HELPER_FLAGS_2(addg, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(subg, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(mulg, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(divg, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_1(sqrtg, TCG_CALL_CONST, i64, i64)
+
+DEF_HELPER_FLAGS_1(s_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64)
+DEF_HELPER_FLAGS_1(memory_to_s, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32)
+DEF_HELPER_FLAGS_2(adds, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(subs, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(muls, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(divs, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_1(sqrts, TCG_CALL_CONST, i64, i64)
+
+DEF_HELPER_FLAGS_2(addt, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(subt, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(mult, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_2(divt, TCG_CALL_CONST, i64, i64, i64)
+DEF_HELPER_FLAGS_1(sqrtt, TCG_CALL_CONST, i64, i64)
+
+DEF_HELPER_FLAGS_2(cmptun, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(cmpteq, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(cmptle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(cmptlt, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(cmpgeq, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(cmpgle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(cmpglt, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+
+DEF_HELPER_FLAGS_1(cvtts, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtst, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtqs, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtqt, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtqf, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtgf, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtgq, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvtqg, TCG_CALL_CONST, i64, i64)
+
+DEF_HELPER_FLAGS_1(cvttq, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvttq_c, TCG_CALL_CONST, i64, i64)
+DEF_HELPER_FLAGS_1(cvttq_svic, TCG_CALL_CONST, i64, i64)
+
+DEF_HELPER_FLAGS_1(setroundmode, TCG_CALL_CONST, void, i32)
+DEF_HELPER_FLAGS_1(setflushzero, TCG_CALL_CONST, void, i32)
+DEF_HELPER_FLAGS_0(fp_exc_clear, TCG_CALL_CONST, void)
+DEF_HELPER_FLAGS_0(fp_exc_get, TCG_CALL_CONST | TCG_CALL_PURE, i32)
+DEF_HELPER_2(fp_exc_raise, void, i32, i32)
+DEF_HELPER_2(fp_exc_raise_s, void, i32, i32)
+
+DEF_HELPER_1(ieee_input, i64, i64)
+DEF_HELPER_1(ieee_input_cmp, i64, i64)
+DEF_HELPER_1(ieee_input_s, i64, i64)
+
+#if !defined (CONFIG_USER_ONLY)
+DEF_HELPER_0(hw_rei, void)
+DEF_HELPER_1(hw_ret, void, i64)
+DEF_HELPER_2(mfpr, i64, int, i64)
+DEF_HELPER_2(mtpr, void, int, i64)
+DEF_HELPER_0(set_alt_mode, void)
+DEF_HELPER_0(restore_mode, void)
+
+DEF_HELPER_1(ld_virt_to_phys, i64, i64)
+DEF_HELPER_1(st_virt_to_phys, i64, i64)
+DEF_HELPER_2(ldl_raw, void, i64, i64)
+DEF_HELPER_2(ldq_raw, void, i64, i64)
+DEF_HELPER_2(ldl_l_raw, void, i64, i64)
+DEF_HELPER_2(ldq_l_raw, void, i64, i64)
+DEF_HELPER_2(ldl_kernel, void, i64, i64)
+DEF_HELPER_2(ldq_kernel, void, i64, i64)
+DEF_HELPER_2(ldl_data, void, i64, i64)
+DEF_HELPER_2(ldq_data, void, i64, i64)
+DEF_HELPER_2(stl_raw, void, i64, i64)
+DEF_HELPER_2(stq_raw, void, i64, i64)
+DEF_HELPER_2(stl_c_raw, i64, i64, i64)
+DEF_HELPER_2(stq_c_raw, i64, i64, i64)
+#endif
+
+#include "def-helper.h"
diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c
new file mode 100644
index 0000000..6c2ae20
--- /dev/null
+++ b/target-alpha/op_helper.c
@@ -0,0 +1,1385 @@
+/*
+ * Alpha emulation cpu micro-operations helpers for qemu.
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exec.h"
+#include "host-utils.h"
+#include "softfloat.h"
+#include "helper.h"
+#include "qemu-timer.h"
+
+/*****************************************************************************/
+/* Exceptions processing helpers */
+void QEMU_NORETURN helper_excp (int excp, int error)
+{
+ env->exception_index = excp;
+ env->error_code = error;
+ cpu_loop_exit();
+}
+
+uint64_t helper_load_pcc (void)
+{
+ /* ??? This isn't a timer for which we have any rate info. */
+ return (uint32_t)cpu_get_real_ticks();
+}
+
+uint64_t helper_load_fpcr (void)
+{
+ return cpu_alpha_load_fpcr (env);
+}
+
+void helper_store_fpcr (uint64_t val)
+{
+ cpu_alpha_store_fpcr (env, val);
+}
+
+uint64_t helper_addqv (uint64_t op1, uint64_t op2)
+{
+ uint64_t tmp = op1;
+ op1 += op2;
+ if (unlikely((tmp ^ op2 ^ (-1ULL)) & (tmp ^ op1) & (1ULL << 63))) {
+ helper_excp(EXCP_ARITH, EXC_M_IOV);
+ }
+ return op1;
+}
+
+uint64_t helper_addlv (uint64_t op1, uint64_t op2)
+{
+ uint64_t tmp = op1;
+ op1 = (uint32_t)(op1 + op2);
+ if (unlikely((tmp ^ op2 ^ (-1UL)) & (tmp ^ op1) & (1UL << 31))) {
+ helper_excp(EXCP_ARITH, EXC_M_IOV);
+ }
+ return op1;
+}
+
+uint64_t helper_subqv (uint64_t op1, uint64_t op2)
+{
+ uint64_t res;
+ res = op1 - op2;
+ if (unlikely((op1 ^ op2) & (res ^ op1) & (1ULL << 63))) {
+ helper_excp(EXCP_ARITH, EXC_M_IOV);
+ }
+ return res;
+}
+
+uint64_t helper_sublv (uint64_t op1, uint64_t op2)
+{
+ uint32_t res;
+ res = op1 - op2;
+ if (unlikely((op1 ^ op2) & (res ^ op1) & (1UL << 31))) {
+ helper_excp(EXCP_ARITH, EXC_M_IOV);
+ }
+ return res;
+}
+
+uint64_t helper_mullv (uint64_t op1, uint64_t op2)
+{
+ int64_t res = (int64_t)op1 * (int64_t)op2;
+
+ if (unlikely((int32_t)res != res)) {
+ helper_excp(EXCP_ARITH, EXC_M_IOV);
+ }
+ return (int64_t)((int32_t)res);
+}
+
+uint64_t helper_mulqv (uint64_t op1, uint64_t op2)
+{
+ uint64_t tl, th;
+
+ muls64(&tl, &th, op1, op2);
+ /* If th != 0 && th != -1, then we had an overflow */
+ if (unlikely((th + 1) > 1)) {
+ helper_excp(EXCP_ARITH, EXC_M_IOV);
+ }
+ return tl;
+}
+
+uint64_t helper_umulh (uint64_t op1, uint64_t op2)
+{
+ uint64_t tl, th;
+
+ mulu64(&tl, &th, op1, op2);
+ return th;
+}
+
+uint64_t helper_ctpop (uint64_t arg)
+{
+ return ctpop64(arg);
+}
+
+uint64_t helper_ctlz (uint64_t arg)
+{
+ return clz64(arg);
+}
+
+uint64_t helper_cttz (uint64_t arg)
+{
+ return ctz64(arg);
+}
+
+static inline uint64_t byte_zap(uint64_t op, uint8_t mskb)
+{
+ uint64_t mask;
+
+ mask = 0;
+ mask |= ((mskb >> 0) & 1) * 0x00000000000000FFULL;
+ mask |= ((mskb >> 1) & 1) * 0x000000000000FF00ULL;
+ mask |= ((mskb >> 2) & 1) * 0x0000000000FF0000ULL;
+ mask |= ((mskb >> 3) & 1) * 0x00000000FF000000ULL;
+ mask |= ((mskb >> 4) & 1) * 0x000000FF00000000ULL;
+ mask |= ((mskb >> 5) & 1) * 0x0000FF0000000000ULL;
+ mask |= ((mskb >> 6) & 1) * 0x00FF000000000000ULL;
+ mask |= ((mskb >> 7) & 1) * 0xFF00000000000000ULL;
+
+ return op & ~mask;
+}
+
+uint64_t helper_zap(uint64_t val, uint64_t mask)
+{
+ return byte_zap(val, mask);
+}
+
+uint64_t helper_zapnot(uint64_t val, uint64_t mask)
+{
+ return byte_zap(val, ~mask);
+}
+
+uint64_t helper_cmpbge (uint64_t op1, uint64_t op2)
+{
+ uint8_t opa, opb, res;
+ int i;
+
+ res = 0;
+ for (i = 0; i < 8; i++) {
+ opa = op1 >> (i * 8);
+ opb = op2 >> (i * 8);
+ if (opa >= opb)
+ res |= 1 << i;
+ }
+ return res;
+}
+
+uint64_t helper_minub8 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ uint8_t opa, opb, opr;
+ int i;
+
+ for (i = 0; i < 8; ++i) {
+ opa = op1 >> (i * 8);
+ opb = op2 >> (i * 8);
+ opr = opa < opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 8);
+ }
+ return res;
+}
+
+uint64_t helper_minsb8 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ int8_t opa, opb;
+ uint8_t opr;
+ int i;
+
+ for (i = 0; i < 8; ++i) {
+ opa = op1 >> (i * 8);
+ opb = op2 >> (i * 8);
+ opr = opa < opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 8);
+ }
+ return res;
+}
+
+uint64_t helper_minuw4 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ uint16_t opa, opb, opr;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ opa = op1 >> (i * 16);
+ opb = op2 >> (i * 16);
+ opr = opa < opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 16);
+ }
+ return res;
+}
+
+uint64_t helper_minsw4 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ int16_t opa, opb;
+ uint16_t opr;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ opa = op1 >> (i * 16);
+ opb = op2 >> (i * 16);
+ opr = opa < opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 16);
+ }
+ return res;
+}
+
+uint64_t helper_maxub8 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ uint8_t opa, opb, opr;
+ int i;
+
+ for (i = 0; i < 8; ++i) {
+ opa = op1 >> (i * 8);
+ opb = op2 >> (i * 8);
+ opr = opa > opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 8);
+ }
+ return res;
+}
+
+uint64_t helper_maxsb8 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ int8_t opa, opb;
+ uint8_t opr;
+ int i;
+
+ for (i = 0; i < 8; ++i) {
+ opa = op1 >> (i * 8);
+ opb = op2 >> (i * 8);
+ opr = opa > opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 8);
+ }
+ return res;
+}
+
+uint64_t helper_maxuw4 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ uint16_t opa, opb, opr;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ opa = op1 >> (i * 16);
+ opb = op2 >> (i * 16);
+ opr = opa > opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 16);
+ }
+ return res;
+}
+
+uint64_t helper_maxsw4 (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ int16_t opa, opb;
+ uint16_t opr;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ opa = op1 >> (i * 16);
+ opb = op2 >> (i * 16);
+ opr = opa > opb ? opa : opb;
+ res |= (uint64_t)opr << (i * 16);
+ }
+ return res;
+}
+
+uint64_t helper_perr (uint64_t op1, uint64_t op2)
+{
+ uint64_t res = 0;
+ uint8_t opa, opb, opr;
+ int i;
+
+ for (i = 0; i < 8; ++i) {
+ opa = op1 >> (i * 8);
+ opb = op2 >> (i * 8);
+ if (opa >= opb)
+ opr = opa - opb;
+ else
+ opr = opb - opa;
+ res += opr;
+ }
+ return res;
+}
+
+uint64_t helper_pklb (uint64_t op1)
+{
+ return (op1 & 0xff) | ((op1 >> 24) & 0xff00);
+}
+
+uint64_t helper_pkwb (uint64_t op1)
+{
+ return ((op1 & 0xff)
+ | ((op1 >> 8) & 0xff00)
+ | ((op1 >> 16) & 0xff0000)
+ | ((op1 >> 24) & 0xff000000));
+}
+
+uint64_t helper_unpkbl (uint64_t op1)
+{
+ return (op1 & 0xff) | ((op1 & 0xff00) << 24);
+}
+
+uint64_t helper_unpkbw (uint64_t op1)
+{
+ return ((op1 & 0xff)
+ | ((op1 & 0xff00) << 8)
+ | ((op1 & 0xff0000) << 16)
+ | ((op1 & 0xff000000) << 24));
+}
+
+/* Floating point helpers */
+
+void helper_setroundmode (uint32_t val)
+{
+ set_float_rounding_mode(val, &FP_STATUS);
+}
+
+void helper_setflushzero (uint32_t val)
+{
+ set_flush_to_zero(val, &FP_STATUS);
+}
+
+void helper_fp_exc_clear (void)
+{
+ set_float_exception_flags(0, &FP_STATUS);
+}
+
+uint32_t helper_fp_exc_get (void)
+{
+ return get_float_exception_flags(&FP_STATUS);
+}
+
+/* Raise exceptions for ieee fp insns without software completion.
+ In that case there are no exceptions that don't trap; the mask
+ doesn't apply. */
+void helper_fp_exc_raise(uint32_t exc, uint32_t regno)
+{
+ if (exc) {
+ uint32_t hw_exc = 0;
+
+ env->ipr[IPR_EXC_MASK] |= 1ull << regno;
+
+ if (exc & float_flag_invalid) {
+ hw_exc |= EXC_M_INV;
+ }
+ if (exc & float_flag_divbyzero) {
+ hw_exc |= EXC_M_DZE;
+ }
+ if (exc & float_flag_overflow) {
+ hw_exc |= EXC_M_FOV;
+ }
+ if (exc & float_flag_underflow) {
+ hw_exc |= EXC_M_UNF;
+ }
+ if (exc & float_flag_inexact) {
+ hw_exc |= EXC_M_INE;
+ }
+ helper_excp(EXCP_ARITH, hw_exc);
+ }
+}
+
+/* Raise exceptions for ieee fp insns with software completion. */
+void helper_fp_exc_raise_s(uint32_t exc, uint32_t regno)
+{
+ if (exc) {
+ env->fpcr_exc_status |= exc;
+
+ exc &= ~env->fpcr_exc_mask;
+ if (exc) {
+ helper_fp_exc_raise(exc, regno);
+ }
+ }
+}
+
+/* Input remapping without software completion. Handle denormal-map-to-zero
+ and trap for all other non-finite numbers. */
+uint64_t helper_ieee_input(uint64_t val)
+{
+ uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
+ uint64_t frac = val & 0xfffffffffffffull;
+
+ if (exp == 0) {
+ if (frac != 0) {
+ /* If DNZ is set flush denormals to zero on input. */
+ if (env->fpcr_dnz) {
+ val &= 1ull << 63;
+ } else {
+ helper_excp(EXCP_ARITH, EXC_M_UNF);
+ }
+ }
+ } else if (exp == 0x7ff) {
+ /* Infinity or NaN. */
+ /* ??? I'm not sure these exception bit flags are correct. I do
+ know that the Linux kernel, at least, doesn't rely on them and
+ just emulates the insn to figure out what exception to use. */
+ helper_excp(EXCP_ARITH, frac ? EXC_M_INV : EXC_M_FOV);
+ }
+ return val;
+}
+
+/* Similar, but does not trap for infinities. Used for comparisons. */
+uint64_t helper_ieee_input_cmp(uint64_t val)
+{
+ uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
+ uint64_t frac = val & 0xfffffffffffffull;
+
+ if (exp == 0) {
+ if (frac != 0) {
+ /* If DNZ is set flush denormals to zero on input. */
+ if (env->fpcr_dnz) {
+ val &= 1ull << 63;
+ } else {
+ helper_excp(EXCP_ARITH, EXC_M_UNF);
+ }
+ }
+ } else if (exp == 0x7ff && frac) {
+ /* NaN. */
+ helper_excp(EXCP_ARITH, EXC_M_INV);
+ }
+ return val;
+}
+
+/* Input remapping with software completion enabled. All we have to do
+ is handle denormal-map-to-zero; all other inputs get exceptions as
+ needed from the actual operation. */
+uint64_t helper_ieee_input_s(uint64_t val)
+{
+ if (env->fpcr_dnz) {
+ uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
+ if (exp == 0) {
+ val &= 1ull << 63;
+ }
+ }
+ return val;
+}
+
+/* F floating (VAX) */
+static inline uint64_t float32_to_f(float32 fa)
+{
+ uint64_t r, exp, mant, sig;
+ CPU_FloatU a;
+
+ a.f = fa;
+ sig = ((uint64_t)a.l & 0x80000000) << 32;
+ exp = (a.l >> 23) & 0xff;
+ mant = ((uint64_t)a.l & 0x007fffff) << 29;
+
+ if (exp == 255) {
+ /* NaN or infinity */
+ r = 1; /* VAX dirty zero */
+ } else if (exp == 0) {
+ if (mant == 0) {
+ /* Zero */
+ r = 0;
+ } else {
+ /* Denormalized */
+ r = sig | ((exp + 1) << 52) | mant;
+ }
+ } else {
+ if (exp >= 253) {
+ /* Overflow */
+ r = 1; /* VAX dirty zero */
+ } else {
+ r = sig | ((exp + 2) << 52);
+ }
+ }
+
+ return r;
+}
+
+static inline float32 f_to_float32(uint64_t a)
+{
+ uint32_t exp, mant_sig;
+ CPU_FloatU r;
+
+ exp = ((a >> 55) & 0x80) | ((a >> 52) & 0x7f);
+ mant_sig = ((a >> 32) & 0x80000000) | ((a >> 29) & 0x007fffff);
+
+ if (unlikely(!exp && mant_sig)) {
+ /* Reserved operands / Dirty zero */
+ helper_excp(EXCP_OPCDEC, 0);
+ }
+
+ if (exp < 3) {
+ /* Underflow */
+ r.l = 0;
+ } else {
+ r.l = ((exp - 2) << 23) | mant_sig;
+ }
+
+ return r.f;
+}
+
+uint32_t helper_f_to_memory (uint64_t a)
+{
+ uint32_t r;
+ r = (a & 0x00001fffe0000000ull) >> 13;
+ r |= (a & 0x07ffe00000000000ull) >> 45;
+ r |= (a & 0xc000000000000000ull) >> 48;
+ return r;
+}
+
+uint64_t helper_memory_to_f (uint32_t a)
+{
+ uint64_t r;
+ r = ((uint64_t)(a & 0x0000c000)) << 48;
+ r |= ((uint64_t)(a & 0x003fffff)) << 45;
+ r |= ((uint64_t)(a & 0xffff0000)) << 13;
+ if (!(a & 0x00004000))
+ r |= 0x7ll << 59;
+ return r;
+}
+
+/* ??? Emulating VAX arithmetic with IEEE arithmetic is wrong. We should
+ either implement VAX arithmetic properly or just signal invalid opcode. */
+
+uint64_t helper_addf (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = f_to_float32(a);
+ fb = f_to_float32(b);
+ fr = float32_add(fa, fb, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+uint64_t helper_subf (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = f_to_float32(a);
+ fb = f_to_float32(b);
+ fr = float32_sub(fa, fb, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+uint64_t helper_mulf (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = f_to_float32(a);
+ fb = f_to_float32(b);
+ fr = float32_mul(fa, fb, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+uint64_t helper_divf (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = f_to_float32(a);
+ fb = f_to_float32(b);
+ fr = float32_div(fa, fb, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+uint64_t helper_sqrtf (uint64_t t)
+{
+ float32 ft, fr;
+
+ ft = f_to_float32(t);
+ fr = float32_sqrt(ft, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+
+/* G floating (VAX) */
+static inline uint64_t float64_to_g(float64 fa)
+{
+ uint64_t r, exp, mant, sig;
+ CPU_DoubleU a;
+
+ a.d = fa;
+ sig = a.ll & 0x8000000000000000ull;
+ exp = (a.ll >> 52) & 0x7ff;
+ mant = a.ll & 0x000fffffffffffffull;
+
+ if (exp == 2047) {
+ /* NaN or infinity */
+ r = 1; /* VAX dirty zero */
+ } else if (exp == 0) {
+ if (mant == 0) {
+ /* Zero */
+ r = 0;
+ } else {
+ /* Denormalized */
+ r = sig | ((exp + 1) << 52) | mant;
+ }
+ } else {
+ if (exp >= 2045) {
+ /* Overflow */
+ r = 1; /* VAX dirty zero */
+ } else {
+ r = sig | ((exp + 2) << 52);
+ }
+ }
+
+ return r;
+}
+
+static inline float64 g_to_float64(uint64_t a)
+{
+ uint64_t exp, mant_sig;
+ CPU_DoubleU r;
+
+ exp = (a >> 52) & 0x7ff;
+ mant_sig = a & 0x800fffffffffffffull;
+
+ if (!exp && mant_sig) {
+ /* Reserved operands / Dirty zero */
+ helper_excp(EXCP_OPCDEC, 0);
+ }
+
+ if (exp < 3) {
+ /* Underflow */
+ r.ll = 0;
+ } else {
+ r.ll = ((exp - 2) << 52) | mant_sig;
+ }
+
+ return r.d;
+}
+
+uint64_t helper_g_to_memory (uint64_t a)
+{
+ uint64_t r;
+ r = (a & 0x000000000000ffffull) << 48;
+ r |= (a & 0x00000000ffff0000ull) << 16;
+ r |= (a & 0x0000ffff00000000ull) >> 16;
+ r |= (a & 0xffff000000000000ull) >> 48;
+ return r;
+}
+
+uint64_t helper_memory_to_g (uint64_t a)
+{
+ uint64_t r;
+ r = (a & 0x000000000000ffffull) << 48;
+ r |= (a & 0x00000000ffff0000ull) << 16;
+ r |= (a & 0x0000ffff00000000ull) >> 16;
+ r |= (a & 0xffff000000000000ull) >> 48;
+ return r;
+}
+
+uint64_t helper_addg (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+ fr = float64_add(fa, fb, &FP_STATUS);
+ return float64_to_g(fr);
+}
+
+uint64_t helper_subg (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+ fr = float64_sub(fa, fb, &FP_STATUS);
+ return float64_to_g(fr);
+}
+
+uint64_t helper_mulg (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+ fr = float64_mul(fa, fb, &FP_STATUS);
+ return float64_to_g(fr);
+}
+
+uint64_t helper_divg (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+ fr = float64_div(fa, fb, &FP_STATUS);
+ return float64_to_g(fr);
+}
+
+uint64_t helper_sqrtg (uint64_t a)
+{
+ float64 fa, fr;
+
+ fa = g_to_float64(a);
+ fr = float64_sqrt(fa, &FP_STATUS);
+ return float64_to_g(fr);
+}
+
+
+/* S floating (single) */
+
+/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */
+static inline uint64_t float32_to_s_int(uint32_t fi)
+{
+ uint32_t frac = fi & 0x7fffff;
+ uint32_t sign = fi >> 31;
+ uint32_t exp_msb = (fi >> 30) & 1;
+ uint32_t exp_low = (fi >> 23) & 0x7f;
+ uint32_t exp;
+
+ exp = (exp_msb << 10) | exp_low;
+ if (exp_msb) {
+ if (exp_low == 0x7f)
+ exp = 0x7ff;
+ } else {
+ if (exp_low != 0x00)
+ exp |= 0x380;
+ }
+
+ return (((uint64_t)sign << 63)
+ | ((uint64_t)exp << 52)
+ | ((uint64_t)frac << 29));
+}
+
+static inline uint64_t float32_to_s(float32 fa)
+{
+ CPU_FloatU a;
+ a.f = fa;
+ return float32_to_s_int(a.l);
+}
+
+static inline uint32_t s_to_float32_int(uint64_t a)
+{
+ return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff);
+}
+
+static inline float32 s_to_float32(uint64_t a)
+{
+ CPU_FloatU r;
+ r.l = s_to_float32_int(a);
+ return r.f;
+}
+
+uint32_t helper_s_to_memory (uint64_t a)
+{
+ return s_to_float32_int(a);
+}
+
+uint64_t helper_memory_to_s (uint32_t a)
+{
+ return float32_to_s_int(a);
+}
+
+uint64_t helper_adds (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = s_to_float32(a);
+ fb = s_to_float32(b);
+ fr = float32_add(fa, fb, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+uint64_t helper_subs (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = s_to_float32(a);
+ fb = s_to_float32(b);
+ fr = float32_sub(fa, fb, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+uint64_t helper_muls (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = s_to_float32(a);
+ fb = s_to_float32(b);
+ fr = float32_mul(fa, fb, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+uint64_t helper_divs (uint64_t a, uint64_t b)
+{
+ float32 fa, fb, fr;
+
+ fa = s_to_float32(a);
+ fb = s_to_float32(b);
+ fr = float32_div(fa, fb, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+uint64_t helper_sqrts (uint64_t a)
+{
+ float32 fa, fr;
+
+ fa = s_to_float32(a);
+ fr = float32_sqrt(fa, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+
+/* T floating (double) */
+static inline float64 t_to_float64(uint64_t a)
+{
+ /* Memory format is the same as float64 */
+ CPU_DoubleU r;
+ r.ll = a;
+ return r.d;
+}
+
+static inline uint64_t float64_to_t(float64 fa)
+{
+ /* Memory format is the same as float64 */
+ CPU_DoubleU r;
+ r.d = fa;
+ return r.ll;
+}
+
+uint64_t helper_addt (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+ fr = float64_add(fa, fb, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+uint64_t helper_subt (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+ fr = float64_sub(fa, fb, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+uint64_t helper_mult (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+ fr = float64_mul(fa, fb, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+uint64_t helper_divt (uint64_t a, uint64_t b)
+{
+ float64 fa, fb, fr;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+ fr = float64_div(fa, fb, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+uint64_t helper_sqrtt (uint64_t a)
+{
+ float64 fa, fr;
+
+ fa = t_to_float64(a);
+ fr = float64_sqrt(fa, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+/* Comparisons */
+uint64_t helper_cmptun (uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+
+ if (float64_is_quiet_nan(fa) || float64_is_quiet_nan(fb))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+uint64_t helper_cmpteq(uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+
+ if (float64_eq(fa, fb, &FP_STATUS))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+uint64_t helper_cmptle(uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+
+ if (float64_le(fa, fb, &FP_STATUS))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+uint64_t helper_cmptlt(uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = t_to_float64(a);
+ fb = t_to_float64(b);
+
+ if (float64_lt(fa, fb, &FP_STATUS))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+uint64_t helper_cmpgeq(uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+
+ if (float64_eq(fa, fb, &FP_STATUS))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+uint64_t helper_cmpgle(uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+
+ if (float64_le(fa, fb, &FP_STATUS))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+uint64_t helper_cmpglt(uint64_t a, uint64_t b)
+{
+ float64 fa, fb;
+
+ fa = g_to_float64(a);
+ fb = g_to_float64(b);
+
+ if (float64_lt(fa, fb, &FP_STATUS))
+ return 0x4000000000000000ULL;
+ else
+ return 0;
+}
+
+/* Floating point format conversion */
+uint64_t helper_cvtts (uint64_t a)
+{
+ float64 fa;
+ float32 fr;
+
+ fa = t_to_float64(a);
+ fr = float64_to_float32(fa, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+uint64_t helper_cvtst (uint64_t a)
+{
+ float32 fa;
+ float64 fr;
+
+ fa = s_to_float32(a);
+ fr = float32_to_float64(fa, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+uint64_t helper_cvtqs (uint64_t a)
+{
+ float32 fr = int64_to_float32(a, &FP_STATUS);
+ return float32_to_s(fr);
+}
+
+/* Implement float64 to uint64 conversion without saturation -- we must
+ supply the truncated result. This behaviour is used by the compiler
+ to get unsigned conversion for free with the same instruction.
+
+ The VI flag is set when overflow or inexact exceptions should be raised. */
+
+static inline uint64_t helper_cvttq_internal(uint64_t a, int roundmode, int VI)
+{
+ uint64_t frac, ret = 0;
+ uint32_t exp, sign, exc = 0;
+ int shift;
+
+ sign = (a >> 63);
+ exp = (uint32_t)(a >> 52) & 0x7ff;
+ frac = a & 0xfffffffffffffull;
+
+ if (exp == 0) {
+ if (unlikely(frac != 0)) {
+ goto do_underflow;
+ }
+ } else if (exp == 0x7ff) {
+ exc = (frac ? float_flag_invalid : VI ? float_flag_overflow : 0);
+ } else {
+ /* Restore implicit bit. */
+ frac |= 0x10000000000000ull;
+
+ shift = exp - 1023 - 52;
+ if (shift >= 0) {
+ /* In this case the number is so large that we must shift
+ the fraction left. There is no rounding to do. */
+ if (shift < 63) {
+ ret = frac << shift;
+ if (VI && (ret >> shift) != frac) {
+ exc = float_flag_overflow;
+ }
+ }
+ } else {
+ uint64_t round;
+
+ /* In this case the number is smaller than the fraction as
+ represented by the 52 bit number. Here we must think
+ about rounding the result. Handle this by shifting the
+ fractional part of the number into the high bits of ROUND.
+ This will let us efficiently handle round-to-nearest. */
+ shift = -shift;
+ if (shift < 63) {
+ ret = frac >> shift;
+ round = frac << (64 - shift);
+ } else {
+ /* The exponent is so small we shift out everything.
+ Leave a sticky bit for proper rounding below. */
+ do_underflow:
+ round = 1;
+ }
+
+ if (round) {
+ exc = (VI ? float_flag_inexact : 0);
+ switch (roundmode) {
+ case float_round_nearest_even:
+ if (round == (1ull << 63)) {
+ /* Fraction is exactly 0.5; round to even. */
+ ret += (ret & 1);
+ } else if (round > (1ull << 63)) {
+ ret += 1;
+ }
+ break;
+ case float_round_to_zero:
+ break;
+ case float_round_up:
+ ret += 1 - sign;
+ break;
+ case float_round_down:
+ ret += sign;
+ break;
+ }
+ }
+ }
+ if (sign) {
+ ret = -ret;
+ }
+ }
+ if (unlikely(exc)) {
+ float_raise(exc, &FP_STATUS);
+ }
+
+ return ret;
+}
+
+uint64_t helper_cvttq(uint64_t a)
+{
+ return helper_cvttq_internal(a, FP_STATUS.float_rounding_mode, 1);
+}
+
+uint64_t helper_cvttq_c(uint64_t a)
+{
+ return helper_cvttq_internal(a, float_round_to_zero, 0);
+}
+
+uint64_t helper_cvttq_svic(uint64_t a)
+{
+ return helper_cvttq_internal(a, float_round_to_zero, 1);
+}
+
+uint64_t helper_cvtqt (uint64_t a)
+{
+ float64 fr = int64_to_float64(a, &FP_STATUS);
+ return float64_to_t(fr);
+}
+
+uint64_t helper_cvtqf (uint64_t a)
+{
+ float32 fr = int64_to_float32(a, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+uint64_t helper_cvtgf (uint64_t a)
+{
+ float64 fa;
+ float32 fr;
+
+ fa = g_to_float64(a);
+ fr = float64_to_float32(fa, &FP_STATUS);
+ return float32_to_f(fr);
+}
+
+uint64_t helper_cvtgq (uint64_t a)
+{
+ float64 fa = g_to_float64(a);
+ return float64_to_int64_round_to_zero(fa, &FP_STATUS);
+}
+
+uint64_t helper_cvtqg (uint64_t a)
+{
+ float64 fr;
+ fr = int64_to_float64(a, &FP_STATUS);
+ return float64_to_g(fr);
+}
+
+/* PALcode support special instructions */
+#if !defined (CONFIG_USER_ONLY)
+void helper_hw_rei (void)
+{
+ env->pc = env->ipr[IPR_EXC_ADDR] & ~3;
+ env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1;
+ env->intr_flag = 0;
+ env->lock_addr = -1;
+ /* XXX: re-enable interrupts and memory mapping */
+}
+
+void helper_hw_ret (uint64_t a)
+{
+ env->pc = a & ~3;
+ env->ipr[IPR_EXC_ADDR] = a & 1;
+ env->intr_flag = 0;
+ env->lock_addr = -1;
+ /* XXX: re-enable interrupts and memory mapping */
+}
+
+uint64_t helper_mfpr (int iprn, uint64_t val)
+{
+ uint64_t tmp;
+
+ if (cpu_alpha_mfpr(env, iprn, &tmp) == 0)
+ val = tmp;
+
+ return val;
+}
+
+void helper_mtpr (int iprn, uint64_t val)
+{
+ cpu_alpha_mtpr(env, iprn, val, NULL);
+}
+
+void helper_set_alt_mode (void)
+{
+ env->saved_mode = env->ps & 0xC;
+ env->ps = (env->ps & ~0xC) | (env->ipr[IPR_ALT_MODE] & 0xC);
+}
+
+void helper_restore_mode (void)
+{
+ env->ps = (env->ps & ~0xC) | env->saved_mode;
+}
+
+#endif
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+/* XXX: the two following helpers are pure hacks.
+ * Hopefully, we emulate the PALcode, then we should never see
+ * HW_LD / HW_ST instructions.
+ */
+uint64_t helper_ld_virt_to_phys (uint64_t virtaddr)
+{
+ uint64_t tlb_addr, physaddr;
+ int index, mmu_idx;
+ void *retaddr;
+
+ mmu_idx = cpu_mmu_index(env);
+ index = (virtaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[mmu_idx][index].addr_read;
+ if ((virtaddr & TARGET_PAGE_MASK) ==
+ (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ physaddr = virtaddr + env->tlb_table[mmu_idx][index].addend;
+ } else {
+ /* the page is not in the TLB : fill it */
+ retaddr = GETPC();
+ tlb_fill(virtaddr, 0, mmu_idx, retaddr);
+ goto redo;
+ }
+ return physaddr;
+}
+
+uint64_t helper_st_virt_to_phys (uint64_t virtaddr)
+{
+ uint64_t tlb_addr, physaddr;
+ int index, mmu_idx;
+ void *retaddr;
+
+ mmu_idx = cpu_mmu_index(env);
+ index = (virtaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+ tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+ if ((virtaddr & TARGET_PAGE_MASK) ==
+ (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+ physaddr = virtaddr + env->tlb_table[mmu_idx][index].addend;
+ } else {
+ /* the page is not in the TLB : fill it */
+ retaddr = GETPC();
+ tlb_fill(virtaddr, 1, mmu_idx, retaddr);
+ goto redo;
+ }
+ return physaddr;
+}
+
+void helper_ldl_raw(uint64_t t0, uint64_t t1)
+{
+ ldl_raw(t1, t0);
+}
+
+void helper_ldq_raw(uint64_t t0, uint64_t t1)
+{
+ ldq_raw(t1, t0);
+}
+
+void helper_ldl_l_raw(uint64_t t0, uint64_t t1)
+{
+ env->lock = t1;
+ ldl_raw(t1, t0);
+}
+
+void helper_ldq_l_raw(uint64_t t0, uint64_t t1)
+{
+ env->lock = t1;
+ ldl_raw(t1, t0);
+}
+
+void helper_ldl_kernel(uint64_t t0, uint64_t t1)
+{
+ ldl_kernel(t1, t0);
+}
+
+void helper_ldq_kernel(uint64_t t0, uint64_t t1)
+{
+ ldq_kernel(t1, t0);
+}
+
+void helper_ldl_data(uint64_t t0, uint64_t t1)
+{
+ ldl_data(t1, t0);
+}
+
+void helper_ldq_data(uint64_t t0, uint64_t t1)
+{
+ ldq_data(t1, t0);
+}
+
+void helper_stl_raw(uint64_t t0, uint64_t t1)
+{
+ stl_raw(t1, t0);
+}
+
+void helper_stq_raw(uint64_t t0, uint64_t t1)
+{
+ stq_raw(t1, t0);
+}
+
+uint64_t helper_stl_c_raw(uint64_t t0, uint64_t t1)
+{
+ uint64_t ret;
+
+ if (t1 == env->lock) {
+ stl_raw(t1, t0);
+ ret = 0;
+ } else
+ ret = 1;
+
+ env->lock = 1;
+
+ return ret;
+}
+
+uint64_t helper_stq_c_raw(uint64_t t0, uint64_t t1)
+{
+ uint64_t ret;
+
+ if (t1 == env->lock) {
+ stq_raw(t1, t0);
+ ret = 0;
+ } else
+ ret = 1;
+
+ env->lock = 1;
+
+ return ret;
+}
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_alpha_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (!likely(ret == 0)) {
+ if (likely(retaddr)) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (likely(tb)) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ /* Exception index and error code are already set */
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+#endif
diff --git a/target-alpha/translate.c b/target-alpha/translate.c
new file mode 100644
index 0000000..3a1c625
--- /dev/null
+++ b/target-alpha/translate.c
@@ -0,0 +1,3374 @@
+/*
+ * Alpha emulation cpu translation for qemu.
+ *
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "host-utils.h"
+#include "tcg-op.h"
+#include "qemu-common.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+#undef ALPHA_DEBUG_DISAS
+#define CONFIG_SOFTFLOAT_INLINE
+
+#ifdef ALPHA_DEBUG_DISAS
+# define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+# define LOG_DISAS(...) do { } while (0)
+#endif
+
+typedef struct DisasContext DisasContext;
+struct DisasContext {
+ struct TranslationBlock *tb;
+ CPUAlphaState *env;
+ uint64_t pc;
+ int mem_idx;
+#if !defined (CONFIG_USER_ONLY)
+ int pal_mode;
+#endif
+ uint32_t amask;
+
+ /* Current rounding mode for this TB. */
+ int tb_rm;
+ /* Current flush-to-zero setting for this TB. */
+ int tb_ftz;
+};
+
+/* Return values from translate_one, indicating the state of the TB.
+ Note that zero indicates that we are not exiting the TB. */
+
+typedef enum {
+ NO_EXIT,
+
+ /* We have emitted one or more goto_tb. No fixup required. */
+ EXIT_GOTO_TB,
+
+ /* We are not using a goto_tb (for whatever reason), but have updated
+ the PC (for whatever reason), so there's no need to do it again on
+ exiting the TB. */
+ EXIT_PC_UPDATED,
+
+ /* We are exiting the TB, but have neither emitted a goto_tb, nor
+ updated the PC for the next instruction to be executed. */
+ EXIT_PC_STALE,
+
+ /* We are ending the TB with a noreturn function call, e.g. longjmp.
+ No following code will be executed. */
+ EXIT_NORETURN,
+} ExitStatus;
+
+/* global register indexes */
+static TCGv_ptr cpu_env;
+static TCGv cpu_ir[31];
+static TCGv cpu_fir[31];
+static TCGv cpu_pc;
+static TCGv cpu_lock_addr;
+static TCGv cpu_lock_st_addr;
+static TCGv cpu_lock_value;
+#ifdef CONFIG_USER_ONLY
+static TCGv cpu_uniq;
+#endif
+
+/* register names */
+static char cpu_reg_names[10*4+21*5 + 10*5+21*6];
+
+#include "gen-icount.h"
+
+static void alpha_translate_init(void)
+{
+ int i;
+ char *p;
+ static int done_init = 0;
+
+ if (done_init)
+ return;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ p = cpu_reg_names;
+ for (i = 0; i < 31; i++) {
+ sprintf(p, "ir%d", i);
+ cpu_ir[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, ir[i]), p);
+ p += (i < 10) ? 4 : 5;
+
+ sprintf(p, "fir%d", i);
+ cpu_fir[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, fir[i]), p);
+ p += (i < 10) ? 5 : 6;
+ }
+
+ cpu_pc = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, pc), "pc");
+
+ cpu_lock_addr = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, lock_addr),
+ "lock_addr");
+ cpu_lock_st_addr = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, lock_st_addr),
+ "lock_st_addr");
+ cpu_lock_value = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, lock_value),
+ "lock_value");
+
+#ifdef CONFIG_USER_ONLY
+ cpu_uniq = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, unique), "uniq");
+#endif
+
+ /* register helpers */
+#define GEN_HELPER 2
+#include "helper.h"
+
+ done_init = 1;
+}
+
+static ExitStatus gen_excp(DisasContext *ctx, int exception, int error_code)
+{
+ TCGv_i32 tmp1, tmp2;
+
+ tcg_gen_movi_i64(cpu_pc, ctx->pc);
+ tmp1 = tcg_const_i32(exception);
+ tmp2 = tcg_const_i32(error_code);
+ gen_helper_excp(tmp1, tmp2);
+ tcg_temp_free_i32(tmp2);
+ tcg_temp_free_i32(tmp1);
+
+ return EXIT_NORETURN;
+}
+
+static inline ExitStatus gen_invalid(DisasContext *ctx)
+{
+ return gen_excp(ctx, EXCP_OPCDEC, 0);
+}
+
+static inline void gen_qemu_ldf(TCGv t0, TCGv t1, int flags)
+{
+ TCGv tmp = tcg_temp_new();
+ TCGv_i32 tmp32 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp, t1, flags);
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ gen_helper_memory_to_f(t0, tmp32);
+ tcg_temp_free_i32(tmp32);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_qemu_ldg(TCGv t0, TCGv t1, int flags)
+{
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_qemu_ld64(tmp, t1, flags);
+ gen_helper_memory_to_g(t0, tmp);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_qemu_lds(TCGv t0, TCGv t1, int flags)
+{
+ TCGv tmp = tcg_temp_new();
+ TCGv_i32 tmp32 = tcg_temp_new_i32();
+ tcg_gen_qemu_ld32u(tmp, t1, flags);
+ tcg_gen_trunc_i64_i32(tmp32, tmp);
+ gen_helper_memory_to_s(t0, tmp32);
+ tcg_temp_free_i32(tmp32);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags)
+{
+ tcg_gen_qemu_ld32s(t0, t1, flags);
+ tcg_gen_mov_i64(cpu_lock_addr, t1);
+ tcg_gen_mov_i64(cpu_lock_value, t0);
+}
+
+static inline void gen_qemu_ldq_l(TCGv t0, TCGv t1, int flags)
+{
+ tcg_gen_qemu_ld64(t0, t1, flags);
+ tcg_gen_mov_i64(cpu_lock_addr, t1);
+ tcg_gen_mov_i64(cpu_lock_value, t0);
+}
+
+static inline void gen_load_mem(DisasContext *ctx,
+ void (*tcg_gen_qemu_load)(TCGv t0, TCGv t1,
+ int flags),
+ int ra, int rb, int32_t disp16, int fp,
+ int clear)
+{
+ TCGv addr, va;
+
+ /* LDQ_U with ra $31 is UNOP. Other various loads are forms of
+ prefetches, which we can treat as nops. No worries about
+ missed exceptions here. */
+ if (unlikely(ra == 31)) {
+ return;
+ }
+
+ addr = tcg_temp_new();
+ if (rb != 31) {
+ tcg_gen_addi_i64(addr, cpu_ir[rb], disp16);
+ if (clear) {
+ tcg_gen_andi_i64(addr, addr, ~0x7);
+ }
+ } else {
+ if (clear) {
+ disp16 &= ~0x7;
+ }
+ tcg_gen_movi_i64(addr, disp16);
+ }
+
+ va = (fp ? cpu_fir[ra] : cpu_ir[ra]);
+ tcg_gen_qemu_load(va, addr, ctx->mem_idx);
+
+ tcg_temp_free(addr);
+}
+
+static inline void gen_qemu_stf(TCGv t0, TCGv t1, int flags)
+{
+ TCGv_i32 tmp32 = tcg_temp_new_i32();
+ TCGv tmp = tcg_temp_new();
+ gen_helper_f_to_memory(tmp32, t0);
+ tcg_gen_extu_i32_i64(tmp, tmp32);
+ tcg_gen_qemu_st32(tmp, t1, flags);
+ tcg_temp_free(tmp);
+ tcg_temp_free_i32(tmp32);
+}
+
+static inline void gen_qemu_stg(TCGv t0, TCGv t1, int flags)
+{
+ TCGv tmp = tcg_temp_new();
+ gen_helper_g_to_memory(tmp, t0);
+ tcg_gen_qemu_st64(tmp, t1, flags);
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_qemu_sts(TCGv t0, TCGv t1, int flags)
+{
+ TCGv_i32 tmp32 = tcg_temp_new_i32();
+ TCGv tmp = tcg_temp_new();
+ gen_helper_s_to_memory(tmp32, t0);
+ tcg_gen_extu_i32_i64(tmp, tmp32);
+ tcg_gen_qemu_st32(tmp, t1, flags);
+ tcg_temp_free(tmp);
+ tcg_temp_free_i32(tmp32);
+}
+
+static inline void gen_store_mem(DisasContext *ctx,
+ void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1,
+ int flags),
+ int ra, int rb, int32_t disp16, int fp,
+ int clear)
+{
+ TCGv addr, va;
+
+ addr = tcg_temp_new();
+ if (rb != 31) {
+ tcg_gen_addi_i64(addr, cpu_ir[rb], disp16);
+ if (clear) {
+ tcg_gen_andi_i64(addr, addr, ~0x7);
+ }
+ } else {
+ if (clear) {
+ disp16 &= ~0x7;
+ }
+ tcg_gen_movi_i64(addr, disp16);
+ }
+
+ if (ra == 31) {
+ va = tcg_const_i64(0);
+ } else {
+ va = (fp ? cpu_fir[ra] : cpu_ir[ra]);
+ }
+ tcg_gen_qemu_store(va, addr, ctx->mem_idx);
+
+ tcg_temp_free(addr);
+ if (ra == 31) {
+ tcg_temp_free(va);
+ }
+}
+
+static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb,
+ int32_t disp16, int quad)
+{
+ TCGv addr;
+
+ if (ra == 31) {
+ /* ??? Don't bother storing anything. The user can't tell
+ the difference, since the zero register always reads zero. */
+ return NO_EXIT;
+ }
+
+#if defined(CONFIG_USER_ONLY)
+ addr = cpu_lock_st_addr;
+#else
+ addr = tcg_local_new();
+#endif
+
+ if (rb != 31) {
+ tcg_gen_addi_i64(addr, cpu_ir[rb], disp16);
+ } else {
+ tcg_gen_movi_i64(addr, disp16);
+ }
+
+#if defined(CONFIG_USER_ONLY)
+ /* ??? This is handled via a complicated version of compare-and-swap
+ in the cpu_loop. Hopefully one day we'll have a real CAS opcode
+ in TCG so that this isn't necessary. */
+ return gen_excp(ctx, quad ? EXCP_STQ_C : EXCP_STL_C, ra);
+#else
+ /* ??? In system mode we are never multi-threaded, so CAS can be
+ implemented via a non-atomic load-compare-store sequence. */
+ {
+ int lab_fail, lab_done;
+ TCGv val;
+
+ lab_fail = gen_new_label();
+ lab_done = gen_new_label();
+ tcg_gen_brcond(TCG_COND_NE, addr, cpu_lock_addr, lab_fail);
+
+ val = tcg_temp_new();
+ if (quad) {
+ tcg_gen_qemu_ld64(val, addr, ctx->mem_idx);
+ } else {
+ tcg_gen_qemu_ld32s(val, addr, ctx->mem_idx);
+ }
+ tcg_gen_brcond(TCG_COND_NE, val, cpu_lock_value, lab_fail);
+
+ if (quad) {
+ tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx);
+ } else {
+ tcg_gen_qemu_st32(cpu_ir[ra], addr, ctx->mem_idx);
+ }
+ tcg_gen_movi_i64(cpu_ir[ra], 1);
+ tcg_gen_br(lab_done);
+
+ gen_set_label(lab_fail);
+ tcg_gen_movi_i64(cpu_ir[ra], 0);
+
+ gen_set_label(lab_done);
+ tcg_gen_movi_i64(cpu_lock_addr, -1);
+
+ tcg_temp_free(addr);
+ return NO_EXIT;
+ }
+#endif
+}
+
+static int use_goto_tb(DisasContext *ctx, uint64_t dest)
+{
+ /* Check for the dest on the same page as the start of the TB. We
+ also want to suppress goto_tb in the case of single-steping and IO. */
+ return (((ctx->tb->pc ^ dest) & TARGET_PAGE_MASK) == 0
+ && !ctx->env->singlestep_enabled
+ && !(ctx->tb->cflags & CF_LAST_IO));
+}
+
+static ExitStatus gen_bdirect(DisasContext *ctx, int ra, int32_t disp)
+{
+ uint64_t dest = ctx->pc + (disp << 2);
+
+ if (ra != 31) {
+ tcg_gen_movi_i64(cpu_ir[ra], ctx->pc);
+ }
+
+ /* Notice branch-to-next; used to initialize RA with the PC. */
+ if (disp == 0) {
+ return 0;
+ } else if (use_goto_tb(ctx, dest)) {
+ tcg_gen_goto_tb(0);
+ tcg_gen_movi_i64(cpu_pc, dest);
+ tcg_gen_exit_tb((long)ctx->tb);
+ return EXIT_GOTO_TB;
+ } else {
+ tcg_gen_movi_i64(cpu_pc, dest);
+ return EXIT_PC_UPDATED;
+ }
+}
+
+static ExitStatus gen_bcond_internal(DisasContext *ctx, TCGCond cond,
+ TCGv cmp, int32_t disp)
+{
+ uint64_t dest = ctx->pc + (disp << 2);
+ int lab_true = gen_new_label();
+
+ if (use_goto_tb(ctx, dest)) {
+ tcg_gen_brcondi_i64(cond, cmp, 0, lab_true);
+
+ tcg_gen_goto_tb(0);
+ tcg_gen_movi_i64(cpu_pc, ctx->pc);
+ tcg_gen_exit_tb((long)ctx->tb);
+
+ gen_set_label(lab_true);
+ tcg_gen_goto_tb(1);
+ tcg_gen_movi_i64(cpu_pc, dest);
+ tcg_gen_exit_tb((long)ctx->tb + 1);
+
+ return EXIT_GOTO_TB;
+ } else {
+ int lab_over = gen_new_label();
+
+ /* ??? Consider using either
+ movi pc, next
+ addi tmp, pc, disp
+ movcond pc, cond, 0, tmp, pc
+ or
+ setcond tmp, cond, 0
+ movi pc, next
+ neg tmp, tmp
+ andi tmp, tmp, disp
+ add pc, pc, tmp
+ The current diamond subgraph surely isn't efficient. */
+
+ tcg_gen_brcondi_i64(cond, cmp, 0, lab_true);
+ tcg_gen_movi_i64(cpu_pc, ctx->pc);
+ tcg_gen_br(lab_over);
+ gen_set_label(lab_true);
+ tcg_gen_movi_i64(cpu_pc, dest);
+ gen_set_label(lab_over);
+
+ return EXIT_PC_UPDATED;
+ }
+}
+
+static ExitStatus gen_bcond(DisasContext *ctx, TCGCond cond, int ra,
+ int32_t disp, int mask)
+{
+ TCGv cmp_tmp;
+
+ if (unlikely(ra == 31)) {
+ cmp_tmp = tcg_const_i64(0);
+ } else {
+ cmp_tmp = tcg_temp_new();
+ if (mask) {
+ tcg_gen_andi_i64(cmp_tmp, cpu_ir[ra], 1);
+ } else {
+ tcg_gen_mov_i64(cmp_tmp, cpu_ir[ra]);
+ }
+ }
+
+ return gen_bcond_internal(ctx, cond, cmp_tmp, disp);
+}
+
+/* Fold -0.0 for comparison with COND. */
+
+static void gen_fold_mzero(TCGCond cond, TCGv dest, TCGv src)
+{
+ uint64_t mzero = 1ull << 63;
+
+ switch (cond) {
+ case TCG_COND_LE:
+ case TCG_COND_GT:
+ /* For <= or >, the -0.0 value directly compares the way we want. */
+ tcg_gen_mov_i64(dest, src);
+ break;
+
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ /* For == or !=, we can simply mask off the sign bit and compare. */
+ tcg_gen_andi_i64(dest, src, mzero - 1);
+ break;
+
+ case TCG_COND_GE:
+ case TCG_COND_LT:
+ /* For >= or <, map -0.0 to +0.0 via comparison and mask. */
+ tcg_gen_setcondi_i64(TCG_COND_NE, dest, src, mzero);
+ tcg_gen_neg_i64(dest, dest);
+ tcg_gen_and_i64(dest, dest, src);
+ break;
+
+ default:
+ abort();
+ }
+}
+
+static ExitStatus gen_fbcond(DisasContext *ctx, TCGCond cond, int ra,
+ int32_t disp)
+{
+ TCGv cmp_tmp;
+
+ if (unlikely(ra == 31)) {
+ /* Very uncommon case, but easier to optimize it to an integer
+ comparison than continuing with the floating point comparison. */
+ return gen_bcond(ctx, cond, ra, disp, 0);
+ }
+
+ cmp_tmp = tcg_temp_new();
+ gen_fold_mzero(cond, cmp_tmp, cpu_fir[ra]);
+ return gen_bcond_internal(ctx, cond, cmp_tmp, disp);
+}
+
+static void gen_cmov(TCGCond cond, int ra, int rb, int rc,
+ int islit, uint8_t lit, int mask)
+{
+ TCGCond inv_cond = tcg_invert_cond(cond);
+ int l1;
+
+ if (unlikely(rc == 31))
+ return;
+
+ l1 = gen_new_label();
+
+ if (ra != 31) {
+ if (mask) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_andi_i64(tmp, cpu_ir[ra], 1);
+ tcg_gen_brcondi_i64(inv_cond, tmp, 0, l1);
+ tcg_temp_free(tmp);
+ } else
+ tcg_gen_brcondi_i64(inv_cond, cpu_ir[ra], 0, l1);
+ } else {
+ /* Very uncommon case - Do not bother to optimize. */
+ TCGv tmp = tcg_const_i64(0);
+ tcg_gen_brcondi_i64(inv_cond, tmp, 0, l1);
+ tcg_temp_free(tmp);
+ }
+
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ gen_set_label(l1);
+}
+
+static void gen_fcmov(TCGCond cond, int ra, int rb, int rc)
+{
+ TCGv cmp_tmp;
+ int l1;
+
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ cmp_tmp = tcg_temp_new();
+ if (unlikely(ra == 31)) {
+ tcg_gen_movi_i64(cmp_tmp, 0);
+ } else {
+ gen_fold_mzero(cond, cmp_tmp, cpu_fir[ra]);
+ }
+
+ l1 = gen_new_label();
+ tcg_gen_brcondi_i64(tcg_invert_cond(cond), cmp_tmp, 0, l1);
+ tcg_temp_free(cmp_tmp);
+
+ if (rb != 31)
+ tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[rb]);
+ else
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ gen_set_label(l1);
+}
+
+#define QUAL_RM_N 0x080 /* Round mode nearest even */
+#define QUAL_RM_C 0x000 /* Round mode chopped */
+#define QUAL_RM_M 0x040 /* Round mode minus infinity */
+#define QUAL_RM_D 0x0c0 /* Round mode dynamic */
+#define QUAL_RM_MASK 0x0c0
+
+#define QUAL_U 0x100 /* Underflow enable (fp output) */
+#define QUAL_V 0x100 /* Overflow enable (int output) */
+#define QUAL_S 0x400 /* Software completion enable */
+#define QUAL_I 0x200 /* Inexact detection enable */
+
+static void gen_qual_roundmode(DisasContext *ctx, int fn11)
+{
+ TCGv_i32 tmp;
+
+ fn11 &= QUAL_RM_MASK;
+ if (fn11 == ctx->tb_rm) {
+ return;
+ }
+ ctx->tb_rm = fn11;
+
+ tmp = tcg_temp_new_i32();
+ switch (fn11) {
+ case QUAL_RM_N:
+ tcg_gen_movi_i32(tmp, float_round_nearest_even);
+ break;
+ case QUAL_RM_C:
+ tcg_gen_movi_i32(tmp, float_round_to_zero);
+ break;
+ case QUAL_RM_M:
+ tcg_gen_movi_i32(tmp, float_round_down);
+ break;
+ case QUAL_RM_D:
+ tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUState, fpcr_dyn_round));
+ break;
+ }
+
+#if defined(CONFIG_SOFTFLOAT_INLINE)
+ /* ??? The "softfloat.h" interface is to call set_float_rounding_mode.
+ With CONFIG_SOFTFLOAT that expands to an out-of-line call that just
+ sets the one field. */
+ tcg_gen_st8_i32(tmp, cpu_env,
+ offsetof(CPUState, fp_status.float_rounding_mode));
+#else
+ gen_helper_setroundmode(tmp);
+#endif
+
+ tcg_temp_free_i32(tmp);
+}
+
+static void gen_qual_flushzero(DisasContext *ctx, int fn11)
+{
+ TCGv_i32 tmp;
+
+ fn11 &= QUAL_U;
+ if (fn11 == ctx->tb_ftz) {
+ return;
+ }
+ ctx->tb_ftz = fn11;
+
+ tmp = tcg_temp_new_i32();
+ if (fn11) {
+ /* Underflow is enabled, use the FPCR setting. */
+ tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUState, fpcr_flush_to_zero));
+ } else {
+ /* Underflow is disabled, force flush-to-zero. */
+ tcg_gen_movi_i32(tmp, 1);
+ }
+
+#if defined(CONFIG_SOFTFLOAT_INLINE)
+ tcg_gen_st8_i32(tmp, cpu_env,
+ offsetof(CPUState, fp_status.flush_to_zero));
+#else
+ gen_helper_setflushzero(tmp);
+#endif
+
+ tcg_temp_free_i32(tmp);
+}
+
+static TCGv gen_ieee_input(int reg, int fn11, int is_cmp)
+{
+ TCGv val = tcg_temp_new();
+ if (reg == 31) {
+ tcg_gen_movi_i64(val, 0);
+ } else if (fn11 & QUAL_S) {
+ gen_helper_ieee_input_s(val, cpu_fir[reg]);
+ } else if (is_cmp) {
+ gen_helper_ieee_input_cmp(val, cpu_fir[reg]);
+ } else {
+ gen_helper_ieee_input(val, cpu_fir[reg]);
+ }
+ return val;
+}
+
+static void gen_fp_exc_clear(void)
+{
+#if defined(CONFIG_SOFTFLOAT_INLINE)
+ TCGv_i32 zero = tcg_const_i32(0);
+ tcg_gen_st8_i32(zero, cpu_env,
+ offsetof(CPUState, fp_status.float_exception_flags));
+ tcg_temp_free_i32(zero);
+#else
+ gen_helper_fp_exc_clear();
+#endif
+}
+
+static void gen_fp_exc_raise_ignore(int rc, int fn11, int ignore)
+{
+ /* ??? We ought to be able to do something with imprecise exceptions.
+ E.g. notice we're still in the trap shadow of something within the
+ TB and do not generate the code to signal the exception; end the TB
+ when an exception is forced to arrive, either by consumption of a
+ register value or TRAPB or EXCB. */
+ TCGv_i32 exc = tcg_temp_new_i32();
+ TCGv_i32 reg;
+
+#if defined(CONFIG_SOFTFLOAT_INLINE)
+ tcg_gen_ld8u_i32(exc, cpu_env,
+ offsetof(CPUState, fp_status.float_exception_flags));
+#else
+ gen_helper_fp_exc_get(exc);
+#endif
+
+ if (ignore) {
+ tcg_gen_andi_i32(exc, exc, ~ignore);
+ }
+
+ /* ??? Pass in the regno of the destination so that the helper can
+ set EXC_MASK, which contains a bitmask of destination registers
+ that have caused arithmetic traps. A simple userspace emulation
+ does not require this. We do need it for a guest kernel's entArith,
+ or if we were to do something clever with imprecise exceptions. */
+ reg = tcg_const_i32(rc + 32);
+
+ if (fn11 & QUAL_S) {
+ gen_helper_fp_exc_raise_s(exc, reg);
+ } else {
+ gen_helper_fp_exc_raise(exc, reg);
+ }
+
+ tcg_temp_free_i32(reg);
+ tcg_temp_free_i32(exc);
+}
+
+static inline void gen_fp_exc_raise(int rc, int fn11)
+{
+ gen_fp_exc_raise_ignore(rc, fn11, fn11 & QUAL_I ? 0 : float_flag_inexact);
+}
+
+static void gen_fcvtlq(int rb, int rc)
+{
+ if (unlikely(rc == 31)) {
+ return;
+ }
+ if (unlikely(rb == 31)) {
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ } else {
+ TCGv tmp = tcg_temp_new();
+
+ /* The arithmetic right shift here, plus the sign-extended mask below
+ yields a sign-extended result without an explicit ext32s_i64. */
+ tcg_gen_sari_i64(tmp, cpu_fir[rb], 32);
+ tcg_gen_shri_i64(cpu_fir[rc], cpu_fir[rb], 29);
+ tcg_gen_andi_i64(tmp, tmp, (int32_t)0xc0000000);
+ tcg_gen_andi_i64(cpu_fir[rc], cpu_fir[rc], 0x3fffffff);
+ tcg_gen_or_i64(cpu_fir[rc], cpu_fir[rc], tmp);
+
+ tcg_temp_free(tmp);
+ }
+}
+
+static void gen_fcvtql(int rb, int rc)
+{
+ if (unlikely(rc == 31)) {
+ return;
+ }
+ if (unlikely(rb == 31)) {
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ } else {
+ TCGv tmp = tcg_temp_new();
+
+ tcg_gen_andi_i64(tmp, cpu_fir[rb], 0xC0000000);
+ tcg_gen_andi_i64(cpu_fir[rc], cpu_fir[rb], 0x3FFFFFFF);
+ tcg_gen_shli_i64(tmp, tmp, 32);
+ tcg_gen_shli_i64(cpu_fir[rc], cpu_fir[rc], 29);
+ tcg_gen_or_i64(cpu_fir[rc], cpu_fir[rc], tmp);
+
+ tcg_temp_free(tmp);
+ }
+}
+
+static void gen_fcvtql_v(DisasContext *ctx, int rb, int rc)
+{
+ if (rb != 31) {
+ int lab = gen_new_label();
+ TCGv tmp = tcg_temp_new();
+
+ tcg_gen_ext32s_i64(tmp, cpu_fir[rb]);
+ tcg_gen_brcond_i64(TCG_COND_EQ, tmp, cpu_fir[rb], lab);
+ gen_excp(ctx, EXCP_ARITH, EXC_M_IOV);
+
+ gen_set_label(lab);
+ }
+ gen_fcvtql(rb, rc);
+}
+
+#define FARITH2(name) \
+static inline void glue(gen_f, name)(int rb, int rc) \
+{ \
+ if (unlikely(rc == 31)) { \
+ return; \
+ } \
+ if (rb != 31) { \
+ gen_helper_ ## name (cpu_fir[rc], cpu_fir[rb]); \
+ } else { \
+ TCGv tmp = tcg_const_i64(0); \
+ gen_helper_ ## name (cpu_fir[rc], tmp); \
+ tcg_temp_free(tmp); \
+ } \
+}
+
+/* ??? VAX instruction qualifiers ignored. */
+FARITH2(sqrtf)
+FARITH2(sqrtg)
+FARITH2(cvtgf)
+FARITH2(cvtgq)
+FARITH2(cvtqf)
+FARITH2(cvtqg)
+
+static void gen_ieee_arith2(DisasContext *ctx, void (*helper)(TCGv, TCGv),
+ int rb, int rc, int fn11)
+{
+ TCGv vb;
+
+ /* ??? This is wrong: the instruction is not a nop, it still may
+ raise exceptions. */
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ gen_qual_roundmode(ctx, fn11);
+ gen_qual_flushzero(ctx, fn11);
+ gen_fp_exc_clear();
+
+ vb = gen_ieee_input(rb, fn11, 0);
+ helper(cpu_fir[rc], vb);
+ tcg_temp_free(vb);
+
+ gen_fp_exc_raise(rc, fn11);
+}
+
+#define IEEE_ARITH2(name) \
+static inline void glue(gen_f, name)(DisasContext *ctx, \
+ int rb, int rc, int fn11) \
+{ \
+ gen_ieee_arith2(ctx, gen_helper_##name, rb, rc, fn11); \
+}
+IEEE_ARITH2(sqrts)
+IEEE_ARITH2(sqrtt)
+IEEE_ARITH2(cvtst)
+IEEE_ARITH2(cvtts)
+
+static void gen_fcvttq(DisasContext *ctx, int rb, int rc, int fn11)
+{
+ TCGv vb;
+ int ignore = 0;
+
+ /* ??? This is wrong: the instruction is not a nop, it still may
+ raise exceptions. */
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ /* No need to set flushzero, since we have an integer output. */
+ gen_fp_exc_clear();
+ vb = gen_ieee_input(rb, fn11, 0);
+
+ /* Almost all integer conversions use cropped rounding, and most
+ also do not have integer overflow enabled. Special case that. */
+ switch (fn11) {
+ case QUAL_RM_C:
+ gen_helper_cvttq_c(cpu_fir[rc], vb);
+ break;
+ case QUAL_V | QUAL_RM_C:
+ case QUAL_S | QUAL_V | QUAL_RM_C:
+ ignore = float_flag_inexact;
+ /* FALLTHRU */
+ case QUAL_S | QUAL_V | QUAL_I | QUAL_RM_C:
+ gen_helper_cvttq_svic(cpu_fir[rc], vb);
+ break;
+ default:
+ gen_qual_roundmode(ctx, fn11);
+ gen_helper_cvttq(cpu_fir[rc], vb);
+ ignore |= (fn11 & QUAL_V ? 0 : float_flag_overflow);
+ ignore |= (fn11 & QUAL_I ? 0 : float_flag_inexact);
+ break;
+ }
+ tcg_temp_free(vb);
+
+ gen_fp_exc_raise_ignore(rc, fn11, ignore);
+}
+
+static void gen_ieee_intcvt(DisasContext *ctx, void (*helper)(TCGv, TCGv),
+ int rb, int rc, int fn11)
+{
+ TCGv vb;
+
+ /* ??? This is wrong: the instruction is not a nop, it still may
+ raise exceptions. */
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ gen_qual_roundmode(ctx, fn11);
+
+ if (rb == 31) {
+ vb = tcg_const_i64(0);
+ } else {
+ vb = cpu_fir[rb];
+ }
+
+ /* The only exception that can be raised by integer conversion
+ is inexact. Thus we only need to worry about exceptions when
+ inexact handling is requested. */
+ if (fn11 & QUAL_I) {
+ gen_fp_exc_clear();
+ helper(cpu_fir[rc], vb);
+ gen_fp_exc_raise(rc, fn11);
+ } else {
+ helper(cpu_fir[rc], vb);
+ }
+
+ if (rb == 31) {
+ tcg_temp_free(vb);
+ }
+}
+
+#define IEEE_INTCVT(name) \
+static inline void glue(gen_f, name)(DisasContext *ctx, \
+ int rb, int rc, int fn11) \
+{ \
+ gen_ieee_intcvt(ctx, gen_helper_##name, rb, rc, fn11); \
+}
+IEEE_INTCVT(cvtqs)
+IEEE_INTCVT(cvtqt)
+
+static void gen_cpys_internal(int ra, int rb, int rc, int inv_a, uint64_t mask)
+{
+ TCGv va, vb, vmask;
+ int za = 0, zb = 0;
+
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ vmask = tcg_const_i64(mask);
+
+ TCGV_UNUSED_I64(va);
+ if (ra == 31) {
+ if (inv_a) {
+ va = vmask;
+ } else {
+ za = 1;
+ }
+ } else {
+ va = tcg_temp_new_i64();
+ tcg_gen_mov_i64(va, cpu_fir[ra]);
+ if (inv_a) {
+ tcg_gen_andc_i64(va, vmask, va);
+ } else {
+ tcg_gen_and_i64(va, va, vmask);
+ }
+ }
+
+ TCGV_UNUSED_I64(vb);
+ if (rb == 31) {
+ zb = 1;
+ } else {
+ vb = tcg_temp_new_i64();
+ tcg_gen_andc_i64(vb, cpu_fir[rb], vmask);
+ }
+
+ switch (za << 1 | zb) {
+ case 0 | 0:
+ tcg_gen_or_i64(cpu_fir[rc], va, vb);
+ break;
+ case 0 | 1:
+ tcg_gen_mov_i64(cpu_fir[rc], va);
+ break;
+ case 2 | 0:
+ tcg_gen_mov_i64(cpu_fir[rc], vb);
+ break;
+ case 2 | 1:
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ break;
+ }
+
+ tcg_temp_free(vmask);
+ if (ra != 31) {
+ tcg_temp_free(va);
+ }
+ if (rb != 31) {
+ tcg_temp_free(vb);
+ }
+}
+
+static inline void gen_fcpys(int ra, int rb, int rc)
+{
+ gen_cpys_internal(ra, rb, rc, 0, 0x8000000000000000ULL);
+}
+
+static inline void gen_fcpysn(int ra, int rb, int rc)
+{
+ gen_cpys_internal(ra, rb, rc, 1, 0x8000000000000000ULL);
+}
+
+static inline void gen_fcpyse(int ra, int rb, int rc)
+{
+ gen_cpys_internal(ra, rb, rc, 0, 0xFFF0000000000000ULL);
+}
+
+#define FARITH3(name) \
+static inline void glue(gen_f, name)(int ra, int rb, int rc) \
+{ \
+ TCGv va, vb; \
+ \
+ if (unlikely(rc == 31)) { \
+ return; \
+ } \
+ if (ra == 31) { \
+ va = tcg_const_i64(0); \
+ } else { \
+ va = cpu_fir[ra]; \
+ } \
+ if (rb == 31) { \
+ vb = tcg_const_i64(0); \
+ } else { \
+ vb = cpu_fir[rb]; \
+ } \
+ \
+ gen_helper_ ## name (cpu_fir[rc], va, vb); \
+ \
+ if (ra == 31) { \
+ tcg_temp_free(va); \
+ } \
+ if (rb == 31) { \
+ tcg_temp_free(vb); \
+ } \
+}
+
+/* ??? VAX instruction qualifiers ignored. */
+FARITH3(addf)
+FARITH3(subf)
+FARITH3(mulf)
+FARITH3(divf)
+FARITH3(addg)
+FARITH3(subg)
+FARITH3(mulg)
+FARITH3(divg)
+FARITH3(cmpgeq)
+FARITH3(cmpglt)
+FARITH3(cmpgle)
+
+static void gen_ieee_arith3(DisasContext *ctx,
+ void (*helper)(TCGv, TCGv, TCGv),
+ int ra, int rb, int rc, int fn11)
+{
+ TCGv va, vb;
+
+ /* ??? This is wrong: the instruction is not a nop, it still may
+ raise exceptions. */
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ gen_qual_roundmode(ctx, fn11);
+ gen_qual_flushzero(ctx, fn11);
+ gen_fp_exc_clear();
+
+ va = gen_ieee_input(ra, fn11, 0);
+ vb = gen_ieee_input(rb, fn11, 0);
+ helper(cpu_fir[rc], va, vb);
+ tcg_temp_free(va);
+ tcg_temp_free(vb);
+
+ gen_fp_exc_raise(rc, fn11);
+}
+
+#define IEEE_ARITH3(name) \
+static inline void glue(gen_f, name)(DisasContext *ctx, \
+ int ra, int rb, int rc, int fn11) \
+{ \
+ gen_ieee_arith3(ctx, gen_helper_##name, ra, rb, rc, fn11); \
+}
+IEEE_ARITH3(adds)
+IEEE_ARITH3(subs)
+IEEE_ARITH3(muls)
+IEEE_ARITH3(divs)
+IEEE_ARITH3(addt)
+IEEE_ARITH3(subt)
+IEEE_ARITH3(mult)
+IEEE_ARITH3(divt)
+
+static void gen_ieee_compare(DisasContext *ctx,
+ void (*helper)(TCGv, TCGv, TCGv),
+ int ra, int rb, int rc, int fn11)
+{
+ TCGv va, vb;
+
+ /* ??? This is wrong: the instruction is not a nop, it still may
+ raise exceptions. */
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ gen_fp_exc_clear();
+
+ va = gen_ieee_input(ra, fn11, 1);
+ vb = gen_ieee_input(rb, fn11, 1);
+ helper(cpu_fir[rc], va, vb);
+ tcg_temp_free(va);
+ tcg_temp_free(vb);
+
+ gen_fp_exc_raise(rc, fn11);
+}
+
+#define IEEE_CMP3(name) \
+static inline void glue(gen_f, name)(DisasContext *ctx, \
+ int ra, int rb, int rc, int fn11) \
+{ \
+ gen_ieee_compare(ctx, gen_helper_##name, ra, rb, rc, fn11); \
+}
+IEEE_CMP3(cmptun)
+IEEE_CMP3(cmpteq)
+IEEE_CMP3(cmptlt)
+IEEE_CMP3(cmptle)
+
+static inline uint64_t zapnot_mask(uint8_t lit)
+{
+ uint64_t mask = 0;
+ int i;
+
+ for (i = 0; i < 8; ++i) {
+ if ((lit >> i) & 1)
+ mask |= 0xffull << (i * 8);
+ }
+ return mask;
+}
+
+/* Implement zapnot with an immediate operand, which expands to some
+ form of immediate AND. This is a basic building block in the
+ definition of many of the other byte manipulation instructions. */
+static void gen_zapnoti(TCGv dest, TCGv src, uint8_t lit)
+{
+ switch (lit) {
+ case 0x00:
+ tcg_gen_movi_i64(dest, 0);
+ break;
+ case 0x01:
+ tcg_gen_ext8u_i64(dest, src);
+ break;
+ case 0x03:
+ tcg_gen_ext16u_i64(dest, src);
+ break;
+ case 0x0f:
+ tcg_gen_ext32u_i64(dest, src);
+ break;
+ case 0xff:
+ tcg_gen_mov_i64(dest, src);
+ break;
+ default:
+ tcg_gen_andi_i64 (dest, src, zapnot_mask (lit));
+ break;
+ }
+}
+
+static inline void gen_zapnot(int ra, int rb, int rc, int islit, uint8_t lit)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else if (islit)
+ gen_zapnoti(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ gen_helper_zapnot (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+}
+
+static inline void gen_zap(int ra, int rb, int rc, int islit, uint8_t lit)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else if (islit)
+ gen_zapnoti(cpu_ir[rc], cpu_ir[ra], ~lit);
+ else
+ gen_helper_zap (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+}
+
+
+/* EXTWH, EXTLH, EXTQH */
+static void gen_ext_h(int ra, int rb, int rc, int islit,
+ uint8_t lit, uint8_t byte_mask)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else {
+ if (islit) {
+ lit = (64 - (lit & 7) * 8) & 0x3f;
+ tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ } else {
+ TCGv tmp1 = tcg_temp_new();
+ tcg_gen_andi_i64(tmp1, cpu_ir[rb], 7);
+ tcg_gen_shli_i64(tmp1, tmp1, 3);
+ tcg_gen_neg_i64(tmp1, tmp1);
+ tcg_gen_andi_i64(tmp1, tmp1, 0x3f);
+ tcg_gen_shl_i64(cpu_ir[rc], cpu_ir[ra], tmp1);
+ tcg_temp_free(tmp1);
+ }
+ gen_zapnoti(cpu_ir[rc], cpu_ir[rc], byte_mask);
+ }
+}
+
+/* EXTBL, EXTWL, EXTLL, EXTQL */
+static void gen_ext_l(int ra, int rb, int rc, int islit,
+ uint8_t lit, uint8_t byte_mask)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else {
+ if (islit) {
+ tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[ra], (lit & 7) * 8);
+ } else {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_andi_i64(tmp, cpu_ir[rb], 7);
+ tcg_gen_shli_i64(tmp, tmp, 3);
+ tcg_gen_shr_i64(cpu_ir[rc], cpu_ir[ra], tmp);
+ tcg_temp_free(tmp);
+ }
+ gen_zapnoti(cpu_ir[rc], cpu_ir[rc], byte_mask);
+ }
+}
+
+/* INSWH, INSLH, INSQH */
+static void gen_ins_h(int ra, int rb, int rc, int islit,
+ uint8_t lit, uint8_t byte_mask)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31) || (islit && (lit & 7) == 0))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else {
+ TCGv tmp = tcg_temp_new();
+
+ /* The instruction description has us left-shift the byte mask
+ and extract bits <15:8> and apply that zap at the end. This
+ is equivalent to simply performing the zap first and shifting
+ afterward. */
+ gen_zapnoti (tmp, cpu_ir[ra], byte_mask);
+
+ if (islit) {
+ /* Note that we have handled the lit==0 case above. */
+ tcg_gen_shri_i64 (cpu_ir[rc], tmp, 64 - (lit & 7) * 8);
+ } else {
+ TCGv shift = tcg_temp_new();
+
+ /* If (B & 7) == 0, we need to shift by 64 and leave a zero.
+ Do this portably by splitting the shift into two parts:
+ shift_count-1 and 1. Arrange for the -1 by using
+ ones-complement instead of twos-complement in the negation:
+ ~((B & 7) * 8) & 63. */
+
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 7);
+ tcg_gen_shli_i64(shift, shift, 3);
+ tcg_gen_not_i64(shift, shift);
+ tcg_gen_andi_i64(shift, shift, 0x3f);
+
+ tcg_gen_shr_i64(cpu_ir[rc], tmp, shift);
+ tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[rc], 1);
+ tcg_temp_free(shift);
+ }
+ tcg_temp_free(tmp);
+ }
+}
+
+/* INSBL, INSWL, INSLL, INSQL */
+static void gen_ins_l(int ra, int rb, int rc, int islit,
+ uint8_t lit, uint8_t byte_mask)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else {
+ TCGv tmp = tcg_temp_new();
+
+ /* The instruction description has us left-shift the byte mask
+ the same number of byte slots as the data and apply the zap
+ at the end. This is equivalent to simply performing the zap
+ first and shifting afterward. */
+ gen_zapnoti (tmp, cpu_ir[ra], byte_mask);
+
+ if (islit) {
+ tcg_gen_shli_i64(cpu_ir[rc], tmp, (lit & 7) * 8);
+ } else {
+ TCGv shift = tcg_temp_new();
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 7);
+ tcg_gen_shli_i64(shift, shift, 3);
+ tcg_gen_shl_i64(cpu_ir[rc], tmp, shift);
+ tcg_temp_free(shift);
+ }
+ tcg_temp_free(tmp);
+ }
+}
+
+/* MSKWH, MSKLH, MSKQH */
+static void gen_msk_h(int ra, int rb, int rc, int islit,
+ uint8_t lit, uint8_t byte_mask)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else if (islit) {
+ gen_zapnoti (cpu_ir[rc], cpu_ir[ra], ~((byte_mask << (lit & 7)) >> 8));
+ } else {
+ TCGv shift = tcg_temp_new();
+ TCGv mask = tcg_temp_new();
+
+ /* The instruction description is as above, where the byte_mask
+ is shifted left, and then we extract bits <15:8>. This can be
+ emulated with a right-shift on the expanded byte mask. This
+ requires extra care because for an input <2:0> == 0 we need a
+ shift of 64 bits in order to generate a zero. This is done by
+ splitting the shift into two parts, the variable shift - 1
+ followed by a constant 1 shift. The code we expand below is
+ equivalent to ~((B & 7) * 8) & 63. */
+
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 7);
+ tcg_gen_shli_i64(shift, shift, 3);
+ tcg_gen_not_i64(shift, shift);
+ tcg_gen_andi_i64(shift, shift, 0x3f);
+ tcg_gen_movi_i64(mask, zapnot_mask (byte_mask));
+ tcg_gen_shr_i64(mask, mask, shift);
+ tcg_gen_shri_i64(mask, mask, 1);
+
+ tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], mask);
+
+ tcg_temp_free(mask);
+ tcg_temp_free(shift);
+ }
+}
+
+/* MSKBL, MSKWL, MSKLL, MSKQL */
+static void gen_msk_l(int ra, int rb, int rc, int islit,
+ uint8_t lit, uint8_t byte_mask)
+{
+ if (unlikely(rc == 31))
+ return;
+ else if (unlikely(ra == 31))
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else if (islit) {
+ gen_zapnoti (cpu_ir[rc], cpu_ir[ra], ~(byte_mask << (lit & 7)));
+ } else {
+ TCGv shift = tcg_temp_new();
+ TCGv mask = tcg_temp_new();
+
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 7);
+ tcg_gen_shli_i64(shift, shift, 3);
+ tcg_gen_movi_i64(mask, zapnot_mask (byte_mask));
+ tcg_gen_shl_i64(mask, mask, shift);
+
+ tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], mask);
+
+ tcg_temp_free(mask);
+ tcg_temp_free(shift);
+ }
+}
+
+/* Code to call arith3 helpers */
+#define ARITH3(name) \
+static inline void glue(gen_, name)(int ra, int rb, int rc, int islit,\
+ uint8_t lit) \
+{ \
+ if (unlikely(rc == 31)) \
+ return; \
+ \
+ if (ra != 31) { \
+ if (islit) { \
+ TCGv tmp = tcg_const_i64(lit); \
+ gen_helper_ ## name(cpu_ir[rc], cpu_ir[ra], tmp); \
+ tcg_temp_free(tmp); \
+ } else \
+ gen_helper_ ## name (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); \
+ } else { \
+ TCGv tmp1 = tcg_const_i64(0); \
+ if (islit) { \
+ TCGv tmp2 = tcg_const_i64(lit); \
+ gen_helper_ ## name (cpu_ir[rc], tmp1, tmp2); \
+ tcg_temp_free(tmp2); \
+ } else \
+ gen_helper_ ## name (cpu_ir[rc], tmp1, cpu_ir[rb]); \
+ tcg_temp_free(tmp1); \
+ } \
+}
+ARITH3(cmpbge)
+ARITH3(addlv)
+ARITH3(sublv)
+ARITH3(addqv)
+ARITH3(subqv)
+ARITH3(umulh)
+ARITH3(mullv)
+ARITH3(mulqv)
+ARITH3(minub8)
+ARITH3(minsb8)
+ARITH3(minuw4)
+ARITH3(minsw4)
+ARITH3(maxub8)
+ARITH3(maxsb8)
+ARITH3(maxuw4)
+ARITH3(maxsw4)
+ARITH3(perr)
+
+#define MVIOP2(name) \
+static inline void glue(gen_, name)(int rb, int rc) \
+{ \
+ if (unlikely(rc == 31)) \
+ return; \
+ if (unlikely(rb == 31)) \
+ tcg_gen_movi_i64(cpu_ir[rc], 0); \
+ else \
+ gen_helper_ ## name (cpu_ir[rc], cpu_ir[rb]); \
+}
+MVIOP2(pklb)
+MVIOP2(pkwb)
+MVIOP2(unpkbl)
+MVIOP2(unpkbw)
+
+static void gen_cmp(TCGCond cond, int ra, int rb, int rc,
+ int islit, uint8_t lit)
+{
+ TCGv va, vb;
+
+ if (unlikely(rc == 31)) {
+ return;
+ }
+
+ if (ra == 31) {
+ va = tcg_const_i64(0);
+ } else {
+ va = cpu_ir[ra];
+ }
+ if (islit) {
+ vb = tcg_const_i64(lit);
+ } else {
+ vb = cpu_ir[rb];
+ }
+
+ tcg_gen_setcond_i64(cond, cpu_ir[rc], va, vb);
+
+ if (ra == 31) {
+ tcg_temp_free(va);
+ }
+ if (islit) {
+ tcg_temp_free(vb);
+ }
+}
+
+static void gen_rx(int ra, int set)
+{
+ TCGv_i32 tmp;
+
+ if (ra != 31) {
+ tcg_gen_ld8u_i64(cpu_ir[ra], cpu_env, offsetof(CPUState, intr_flag));
+ }
+
+ tmp = tcg_const_i32(set);
+ tcg_gen_st8_i32(tmp, cpu_env, offsetof(CPUState, intr_flag));
+ tcg_temp_free_i32(tmp);
+}
+
+static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
+{
+ uint32_t palcode;
+ int32_t disp21, disp16, disp12;
+ uint16_t fn11;
+ uint8_t opc, ra, rb, rc, fpfn, fn7, fn2, islit, real_islit;
+ uint8_t lit;
+ ExitStatus ret;
+
+ /* Decode all instruction fields */
+ opc = insn >> 26;
+ ra = (insn >> 21) & 0x1F;
+ rb = (insn >> 16) & 0x1F;
+ rc = insn & 0x1F;
+ real_islit = islit = (insn >> 12) & 1;
+ if (rb == 31 && !islit) {
+ islit = 1;
+ lit = 0;
+ } else
+ lit = (insn >> 13) & 0xFF;
+ palcode = insn & 0x03FFFFFF;
+ disp21 = ((int32_t)((insn & 0x001FFFFF) << 11)) >> 11;
+ disp16 = (int16_t)(insn & 0x0000FFFF);
+ disp12 = (int32_t)((insn & 0x00000FFF) << 20) >> 20;
+ fn11 = (insn >> 5) & 0x000007FF;
+ fpfn = fn11 & 0x3F;
+ fn7 = (insn >> 5) & 0x0000007F;
+ fn2 = (insn >> 5) & 0x00000003;
+ LOG_DISAS("opc %02x ra %2d rb %2d rc %2d disp16 %6d\n",
+ opc, ra, rb, rc, disp16);
+
+ ret = NO_EXIT;
+ switch (opc) {
+ case 0x00:
+ /* CALL_PAL */
+#ifdef CONFIG_USER_ONLY
+ if (palcode == 0x9E) {
+ /* RDUNIQUE */
+ tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_uniq);
+ break;
+ } else if (palcode == 0x9F) {
+ /* WRUNIQUE */
+ tcg_gen_mov_i64(cpu_uniq, cpu_ir[IR_A0]);
+ break;
+ }
+#endif
+ if (palcode >= 0x80 && palcode < 0xC0) {
+ /* Unprivileged PAL call */
+ ret = gen_excp(ctx, EXCP_CALL_PAL + ((palcode & 0x3F) << 6), 0);
+ break;
+ }
+#ifndef CONFIG_USER_ONLY
+ if (palcode < 0x40) {
+ /* Privileged PAL code */
+ if (ctx->mem_idx & 1)
+ goto invalid_opc;
+ ret = gen_excp(ctx, EXCP_CALL_PALP + ((palcode & 0x3F) << 6), 0);
+ }
+#endif
+ /* Invalid PAL call */
+ goto invalid_opc;
+ case 0x01:
+ /* OPC01 */
+ goto invalid_opc;
+ case 0x02:
+ /* OPC02 */
+ goto invalid_opc;
+ case 0x03:
+ /* OPC03 */
+ goto invalid_opc;
+ case 0x04:
+ /* OPC04 */
+ goto invalid_opc;
+ case 0x05:
+ /* OPC05 */
+ goto invalid_opc;
+ case 0x06:
+ /* OPC06 */
+ goto invalid_opc;
+ case 0x07:
+ /* OPC07 */
+ goto invalid_opc;
+ case 0x08:
+ /* LDA */
+ if (likely(ra != 31)) {
+ if (rb != 31)
+ tcg_gen_addi_i64(cpu_ir[ra], cpu_ir[rb], disp16);
+ else
+ tcg_gen_movi_i64(cpu_ir[ra], disp16);
+ }
+ break;
+ case 0x09:
+ /* LDAH */
+ if (likely(ra != 31)) {
+ if (rb != 31)
+ tcg_gen_addi_i64(cpu_ir[ra], cpu_ir[rb], disp16 << 16);
+ else
+ tcg_gen_movi_i64(cpu_ir[ra], disp16 << 16);
+ }
+ break;
+ case 0x0A:
+ /* LDBU */
+ if (!(ctx->amask & AMASK_BWX))
+ goto invalid_opc;
+ gen_load_mem(ctx, &tcg_gen_qemu_ld8u, ra, rb, disp16, 0, 0);
+ break;
+ case 0x0B:
+ /* LDQ_U */
+ gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 1);
+ break;
+ case 0x0C:
+ /* LDWU */
+ if (!(ctx->amask & AMASK_BWX))
+ goto invalid_opc;
+ gen_load_mem(ctx, &tcg_gen_qemu_ld16u, ra, rb, disp16, 0, 0);
+ break;
+ case 0x0D:
+ /* STW */
+ gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0);
+ break;
+ case 0x0E:
+ /* STB */
+ gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0);
+ break;
+ case 0x0F:
+ /* STQ_U */
+ gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1);
+ break;
+ case 0x10:
+ switch (fn7) {
+ case 0x00:
+ /* ADDL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit) {
+ tcg_gen_addi_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ } else {
+ tcg_gen_add_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ }
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x02:
+ /* S4ADDL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 2);
+ if (islit)
+ tcg_gen_addi_i64(tmp, tmp, lit);
+ else
+ tcg_gen_add_i64(tmp, tmp, cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], tmp);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x09:
+ /* SUBL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_subi_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_sub_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], -lit);
+ else {
+ tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ }
+ }
+ break;
+ case 0x0B:
+ /* S4SUBL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 2);
+ if (islit)
+ tcg_gen_subi_i64(tmp, tmp, lit);
+ else
+ tcg_gen_sub_i64(tmp, tmp, cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], tmp);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], -lit);
+ else {
+ tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ }
+ }
+ }
+ break;
+ case 0x0F:
+ /* CMPBGE */
+ gen_cmpbge(ra, rb, rc, islit, lit);
+ break;
+ case 0x12:
+ /* S8ADDL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 3);
+ if (islit)
+ tcg_gen_addi_i64(tmp, tmp, lit);
+ else
+ tcg_gen_add_i64(tmp, tmp, cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], tmp);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x1B:
+ /* S8SUBL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 3);
+ if (islit)
+ tcg_gen_subi_i64(tmp, tmp, lit);
+ else
+ tcg_gen_sub_i64(tmp, tmp, cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], tmp);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], -lit);
+ else
+ tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ }
+ }
+ }
+ break;
+ case 0x1D:
+ /* CMPULT */
+ gen_cmp(TCG_COND_LTU, ra, rb, rc, islit, lit);
+ break;
+ case 0x20:
+ /* ADDQ */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_addi_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_add_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x22:
+ /* S4ADDQ */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 2);
+ if (islit)
+ tcg_gen_addi_i64(cpu_ir[rc], tmp, lit);
+ else
+ tcg_gen_add_i64(cpu_ir[rc], tmp, cpu_ir[rb]);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x29:
+ /* SUBQ */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_subi_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_sub_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], -lit);
+ else
+ tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x2B:
+ /* S4SUBQ */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 2);
+ if (islit)
+ tcg_gen_subi_i64(cpu_ir[rc], tmp, lit);
+ else
+ tcg_gen_sub_i64(cpu_ir[rc], tmp, cpu_ir[rb]);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], -lit);
+ else
+ tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x2D:
+ /* CMPEQ */
+ gen_cmp(TCG_COND_EQ, ra, rb, rc, islit, lit);
+ break;
+ case 0x32:
+ /* S8ADDQ */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 3);
+ if (islit)
+ tcg_gen_addi_i64(cpu_ir[rc], tmp, lit);
+ else
+ tcg_gen_add_i64(cpu_ir[rc], tmp, cpu_ir[rb]);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x3B:
+ /* S8SUBQ */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_shli_i64(tmp, cpu_ir[ra], 3);
+ if (islit)
+ tcg_gen_subi_i64(cpu_ir[rc], tmp, lit);
+ else
+ tcg_gen_sub_i64(cpu_ir[rc], tmp, cpu_ir[rb]);
+ tcg_temp_free(tmp);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], -lit);
+ else
+ tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x3D:
+ /* CMPULE */
+ gen_cmp(TCG_COND_LEU, ra, rb, rc, islit, lit);
+ break;
+ case 0x40:
+ /* ADDL/V */
+ gen_addlv(ra, rb, rc, islit, lit);
+ break;
+ case 0x49:
+ /* SUBL/V */
+ gen_sublv(ra, rb, rc, islit, lit);
+ break;
+ case 0x4D:
+ /* CMPLT */
+ gen_cmp(TCG_COND_LT, ra, rb, rc, islit, lit);
+ break;
+ case 0x60:
+ /* ADDQ/V */
+ gen_addqv(ra, rb, rc, islit, lit);
+ break;
+ case 0x69:
+ /* SUBQ/V */
+ gen_subqv(ra, rb, rc, islit, lit);
+ break;
+ case 0x6D:
+ /* CMPLE */
+ gen_cmp(TCG_COND_LE, ra, rb, rc, islit, lit);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x11:
+ switch (fn7) {
+ case 0x00:
+ /* AND */
+ if (likely(rc != 31)) {
+ if (ra == 31)
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else if (islit)
+ tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_and_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ }
+ break;
+ case 0x08:
+ /* BIC */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[ra], ~lit);
+ else
+ tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ }
+ break;
+ case 0x14:
+ /* CMOVLBS */
+ gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 1);
+ break;
+ case 0x16:
+ /* CMOVLBC */
+ gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 1);
+ break;
+ case 0x20:
+ /* BIS */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_ori_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_or_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x24:
+ /* CMOVEQ */
+ gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 0);
+ break;
+ case 0x26:
+ /* CMOVNE */
+ gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 0);
+ break;
+ case 0x28:
+ /* ORNOT */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_ori_i64(cpu_ir[rc], cpu_ir[ra], ~lit);
+ else
+ tcg_gen_orc_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], ~lit);
+ else
+ tcg_gen_not_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x40:
+ /* XOR */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_xori_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_xor_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x44:
+ /* CMOVLT */
+ gen_cmov(TCG_COND_LT, ra, rb, rc, islit, lit, 0);
+ break;
+ case 0x46:
+ /* CMOVGE */
+ gen_cmov(TCG_COND_GE, ra, rb, rc, islit, lit, 0);
+ break;
+ case 0x48:
+ /* EQV */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_xori_i64(cpu_ir[rc], cpu_ir[ra], ~lit);
+ else
+ tcg_gen_eqv_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ } else {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], ~lit);
+ else
+ tcg_gen_not_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ }
+ break;
+ case 0x61:
+ /* AMASK */
+ if (likely(rc != 31)) {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], lit);
+ else
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]);
+ switch (ctx->env->implver) {
+ case IMPLVER_2106x:
+ /* EV4, EV45, LCA, LCA45 & EV5 */
+ break;
+ case IMPLVER_21164:
+ case IMPLVER_21264:
+ case IMPLVER_21364:
+ tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[rc],
+ ~(uint64_t)ctx->amask);
+ break;
+ }
+ }
+ break;
+ case 0x64:
+ /* CMOVLE */
+ gen_cmov(TCG_COND_LE, ra, rb, rc, islit, lit, 0);
+ break;
+ case 0x66:
+ /* CMOVGT */
+ gen_cmov(TCG_COND_GT, ra, rb, rc, islit, lit, 0);
+ break;
+ case 0x6C:
+ /* IMPLVER */
+ if (rc != 31)
+ tcg_gen_movi_i64(cpu_ir[rc], ctx->env->implver);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x12:
+ switch (fn7) {
+ case 0x02:
+ /* MSKBL */
+ gen_msk_l(ra, rb, rc, islit, lit, 0x01);
+ break;
+ case 0x06:
+ /* EXTBL */
+ gen_ext_l(ra, rb, rc, islit, lit, 0x01);
+ break;
+ case 0x0B:
+ /* INSBL */
+ gen_ins_l(ra, rb, rc, islit, lit, 0x01);
+ break;
+ case 0x12:
+ /* MSKWL */
+ gen_msk_l(ra, rb, rc, islit, lit, 0x03);
+ break;
+ case 0x16:
+ /* EXTWL */
+ gen_ext_l(ra, rb, rc, islit, lit, 0x03);
+ break;
+ case 0x1B:
+ /* INSWL */
+ gen_ins_l(ra, rb, rc, islit, lit, 0x03);
+ break;
+ case 0x22:
+ /* MSKLL */
+ gen_msk_l(ra, rb, rc, islit, lit, 0x0f);
+ break;
+ case 0x26:
+ /* EXTLL */
+ gen_ext_l(ra, rb, rc, islit, lit, 0x0f);
+ break;
+ case 0x2B:
+ /* INSLL */
+ gen_ins_l(ra, rb, rc, islit, lit, 0x0f);
+ break;
+ case 0x30:
+ /* ZAP */
+ gen_zap(ra, rb, rc, islit, lit);
+ break;
+ case 0x31:
+ /* ZAPNOT */
+ gen_zapnot(ra, rb, rc, islit, lit);
+ break;
+ case 0x32:
+ /* MSKQL */
+ gen_msk_l(ra, rb, rc, islit, lit, 0xff);
+ break;
+ case 0x34:
+ /* SRL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[ra], lit & 0x3f);
+ else {
+ TCGv shift = tcg_temp_new();
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 0x3f);
+ tcg_gen_shr_i64(cpu_ir[rc], cpu_ir[ra], shift);
+ tcg_temp_free(shift);
+ }
+ } else
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ }
+ break;
+ case 0x36:
+ /* EXTQL */
+ gen_ext_l(ra, rb, rc, islit, lit, 0xff);
+ break;
+ case 0x39:
+ /* SLL */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], lit & 0x3f);
+ else {
+ TCGv shift = tcg_temp_new();
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 0x3f);
+ tcg_gen_shl_i64(cpu_ir[rc], cpu_ir[ra], shift);
+ tcg_temp_free(shift);
+ }
+ } else
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ }
+ break;
+ case 0x3B:
+ /* INSQL */
+ gen_ins_l(ra, rb, rc, islit, lit, 0xff);
+ break;
+ case 0x3C:
+ /* SRA */
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ if (islit)
+ tcg_gen_sari_i64(cpu_ir[rc], cpu_ir[ra], lit & 0x3f);
+ else {
+ TCGv shift = tcg_temp_new();
+ tcg_gen_andi_i64(shift, cpu_ir[rb], 0x3f);
+ tcg_gen_sar_i64(cpu_ir[rc], cpu_ir[ra], shift);
+ tcg_temp_free(shift);
+ }
+ } else
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ }
+ break;
+ case 0x52:
+ /* MSKWH */
+ gen_msk_h(ra, rb, rc, islit, lit, 0x03);
+ break;
+ case 0x57:
+ /* INSWH */
+ gen_ins_h(ra, rb, rc, islit, lit, 0x03);
+ break;
+ case 0x5A:
+ /* EXTWH */
+ gen_ext_h(ra, rb, rc, islit, lit, 0x03);
+ break;
+ case 0x62:
+ /* MSKLH */
+ gen_msk_h(ra, rb, rc, islit, lit, 0x0f);
+ break;
+ case 0x67:
+ /* INSLH */
+ gen_ins_h(ra, rb, rc, islit, lit, 0x0f);
+ break;
+ case 0x6A:
+ /* EXTLH */
+ gen_ext_h(ra, rb, rc, islit, lit, 0x0f);
+ break;
+ case 0x72:
+ /* MSKQH */
+ gen_msk_h(ra, rb, rc, islit, lit, 0xff);
+ break;
+ case 0x77:
+ /* INSQH */
+ gen_ins_h(ra, rb, rc, islit, lit, 0xff);
+ break;
+ case 0x7A:
+ /* EXTQH */
+ gen_ext_h(ra, rb, rc, islit, lit, 0xff);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x13:
+ switch (fn7) {
+ case 0x00:
+ /* MULL */
+ if (likely(rc != 31)) {
+ if (ra == 31)
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else {
+ if (islit)
+ tcg_gen_muli_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_mul_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]);
+ }
+ }
+ break;
+ case 0x20:
+ /* MULQ */
+ if (likely(rc != 31)) {
+ if (ra == 31)
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ else if (islit)
+ tcg_gen_muli_i64(cpu_ir[rc], cpu_ir[ra], lit);
+ else
+ tcg_gen_mul_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]);
+ }
+ break;
+ case 0x30:
+ /* UMULH */
+ gen_umulh(ra, rb, rc, islit, lit);
+ break;
+ case 0x40:
+ /* MULL/V */
+ gen_mullv(ra, rb, rc, islit, lit);
+ break;
+ case 0x60:
+ /* MULQ/V */
+ gen_mulqv(ra, rb, rc, islit, lit);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x14:
+ switch (fpfn) { /* fn11 & 0x3F */
+ case 0x04:
+ /* ITOFS */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv_i32 tmp = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, cpu_ir[ra]);
+ gen_helper_memory_to_s(cpu_fir[rc], tmp);
+ tcg_temp_free_i32(tmp);
+ } else
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ }
+ break;
+ case 0x0A:
+ /* SQRTF */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ gen_fsqrtf(rb, rc);
+ break;
+ case 0x0B:
+ /* SQRTS */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ gen_fsqrts(ctx, rb, rc, fn11);
+ break;
+ case 0x14:
+ /* ITOFF */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (ra != 31) {
+ TCGv_i32 tmp = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, cpu_ir[ra]);
+ gen_helper_memory_to_f(cpu_fir[rc], tmp);
+ tcg_temp_free_i32(tmp);
+ } else
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ }
+ break;
+ case 0x24:
+ /* ITOFT */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (ra != 31)
+ tcg_gen_mov_i64(cpu_fir[rc], cpu_ir[ra]);
+ else
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ }
+ break;
+ case 0x2A:
+ /* SQRTG */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ gen_fsqrtg(rb, rc);
+ break;
+ case 0x02B:
+ /* SQRTT */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ gen_fsqrtt(ctx, rb, rc, fn11);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x15:
+ /* VAX floating point */
+ /* XXX: rounding mode and trap are ignored (!) */
+ switch (fpfn) { /* fn11 & 0x3F */
+ case 0x00:
+ /* ADDF */
+ gen_faddf(ra, rb, rc);
+ break;
+ case 0x01:
+ /* SUBF */
+ gen_fsubf(ra, rb, rc);
+ break;
+ case 0x02:
+ /* MULF */
+ gen_fmulf(ra, rb, rc);
+ break;
+ case 0x03:
+ /* DIVF */
+ gen_fdivf(ra, rb, rc);
+ break;
+ case 0x1E:
+ /* CVTDG */
+#if 0 // TODO
+ gen_fcvtdg(rb, rc);
+#else
+ goto invalid_opc;
+#endif
+ break;
+ case 0x20:
+ /* ADDG */
+ gen_faddg(ra, rb, rc);
+ break;
+ case 0x21:
+ /* SUBG */
+ gen_fsubg(ra, rb, rc);
+ break;
+ case 0x22:
+ /* MULG */
+ gen_fmulg(ra, rb, rc);
+ break;
+ case 0x23:
+ /* DIVG */
+ gen_fdivg(ra, rb, rc);
+ break;
+ case 0x25:
+ /* CMPGEQ */
+ gen_fcmpgeq(ra, rb, rc);
+ break;
+ case 0x26:
+ /* CMPGLT */
+ gen_fcmpglt(ra, rb, rc);
+ break;
+ case 0x27:
+ /* CMPGLE */
+ gen_fcmpgle(ra, rb, rc);
+ break;
+ case 0x2C:
+ /* CVTGF */
+ gen_fcvtgf(rb, rc);
+ break;
+ case 0x2D:
+ /* CVTGD */
+#if 0 // TODO
+ gen_fcvtgd(rb, rc);
+#else
+ goto invalid_opc;
+#endif
+ break;
+ case 0x2F:
+ /* CVTGQ */
+ gen_fcvtgq(rb, rc);
+ break;
+ case 0x3C:
+ /* CVTQF */
+ gen_fcvtqf(rb, rc);
+ break;
+ case 0x3E:
+ /* CVTQG */
+ gen_fcvtqg(rb, rc);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x16:
+ /* IEEE floating-point */
+ switch (fpfn) { /* fn11 & 0x3F */
+ case 0x00:
+ /* ADDS */
+ gen_fadds(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x01:
+ /* SUBS */
+ gen_fsubs(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x02:
+ /* MULS */
+ gen_fmuls(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x03:
+ /* DIVS */
+ gen_fdivs(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x20:
+ /* ADDT */
+ gen_faddt(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x21:
+ /* SUBT */
+ gen_fsubt(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x22:
+ /* MULT */
+ gen_fmult(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x23:
+ /* DIVT */
+ gen_fdivt(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x24:
+ /* CMPTUN */
+ gen_fcmptun(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x25:
+ /* CMPTEQ */
+ gen_fcmpteq(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x26:
+ /* CMPTLT */
+ gen_fcmptlt(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x27:
+ /* CMPTLE */
+ gen_fcmptle(ctx, ra, rb, rc, fn11);
+ break;
+ case 0x2C:
+ if (fn11 == 0x2AC || fn11 == 0x6AC) {
+ /* CVTST */
+ gen_fcvtst(ctx, rb, rc, fn11);
+ } else {
+ /* CVTTS */
+ gen_fcvtts(ctx, rb, rc, fn11);
+ }
+ break;
+ case 0x2F:
+ /* CVTTQ */
+ gen_fcvttq(ctx, rb, rc, fn11);
+ break;
+ case 0x3C:
+ /* CVTQS */
+ gen_fcvtqs(ctx, rb, rc, fn11);
+ break;
+ case 0x3E:
+ /* CVTQT */
+ gen_fcvtqt(ctx, rb, rc, fn11);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x17:
+ switch (fn11) {
+ case 0x010:
+ /* CVTLQ */
+ gen_fcvtlq(rb, rc);
+ break;
+ case 0x020:
+ if (likely(rc != 31)) {
+ if (ra == rb) {
+ /* FMOV */
+ if (ra == 31)
+ tcg_gen_movi_i64(cpu_fir[rc], 0);
+ else
+ tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]);
+ } else {
+ /* CPYS */
+ gen_fcpys(ra, rb, rc);
+ }
+ }
+ break;
+ case 0x021:
+ /* CPYSN */
+ gen_fcpysn(ra, rb, rc);
+ break;
+ case 0x022:
+ /* CPYSE */
+ gen_fcpyse(ra, rb, rc);
+ break;
+ case 0x024:
+ /* MT_FPCR */
+ if (likely(ra != 31))
+ gen_helper_store_fpcr(cpu_fir[ra]);
+ else {
+ TCGv tmp = tcg_const_i64(0);
+ gen_helper_store_fpcr(tmp);
+ tcg_temp_free(tmp);
+ }
+ break;
+ case 0x025:
+ /* MF_FPCR */
+ if (likely(ra != 31))
+ gen_helper_load_fpcr(cpu_fir[ra]);
+ break;
+ case 0x02A:
+ /* FCMOVEQ */
+ gen_fcmov(TCG_COND_EQ, ra, rb, rc);
+ break;
+ case 0x02B:
+ /* FCMOVNE */
+ gen_fcmov(TCG_COND_NE, ra, rb, rc);
+ break;
+ case 0x02C:
+ /* FCMOVLT */
+ gen_fcmov(TCG_COND_LT, ra, rb, rc);
+ break;
+ case 0x02D:
+ /* FCMOVGE */
+ gen_fcmov(TCG_COND_GE, ra, rb, rc);
+ break;
+ case 0x02E:
+ /* FCMOVLE */
+ gen_fcmov(TCG_COND_LE, ra, rb, rc);
+ break;
+ case 0x02F:
+ /* FCMOVGT */
+ gen_fcmov(TCG_COND_GT, ra, rb, rc);
+ break;
+ case 0x030:
+ /* CVTQL */
+ gen_fcvtql(rb, rc);
+ break;
+ case 0x130:
+ /* CVTQL/V */
+ case 0x530:
+ /* CVTQL/SV */
+ /* ??? I'm pretty sure there's nothing that /sv needs to do that
+ /v doesn't do. The only thing I can think is that /sv is a
+ valid instruction merely for completeness in the ISA. */
+ gen_fcvtql_v(ctx, rb, rc);
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x18:
+ switch ((uint16_t)disp16) {
+ case 0x0000:
+ /* TRAPB */
+ /* No-op. */
+ break;
+ case 0x0400:
+ /* EXCB */
+ /* No-op. */
+ break;
+ case 0x4000:
+ /* MB */
+ /* No-op */
+ break;
+ case 0x4400:
+ /* WMB */
+ /* No-op */
+ break;
+ case 0x8000:
+ /* FETCH */
+ /* No-op */
+ break;
+ case 0xA000:
+ /* FETCH_M */
+ /* No-op */
+ break;
+ case 0xC000:
+ /* RPCC */
+ if (ra != 31)
+ gen_helper_load_pcc(cpu_ir[ra]);
+ break;
+ case 0xE000:
+ /* RC */
+ gen_rx(ra, 0);
+ break;
+ case 0xE800:
+ /* ECB */
+ break;
+ case 0xF000:
+ /* RS */
+ gen_rx(ra, 1);
+ break;
+ case 0xF800:
+ /* WH64 */
+ /* No-op */
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x19:
+ /* HW_MFPR (PALcode) */
+#if defined (CONFIG_USER_ONLY)
+ goto invalid_opc;
+#else
+ if (!ctx->pal_mode)
+ goto invalid_opc;
+ if (ra != 31) {
+ TCGv tmp = tcg_const_i32(insn & 0xFF);
+ gen_helper_mfpr(cpu_ir[ra], tmp, cpu_ir[ra]);
+ tcg_temp_free(tmp);
+ }
+ break;
+#endif
+ case 0x1A:
+ /* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch
+ prediction stack action, which of course we don't implement. */
+ if (rb != 31) {
+ tcg_gen_andi_i64(cpu_pc, cpu_ir[rb], ~3);
+ } else {
+ tcg_gen_movi_i64(cpu_pc, 0);
+ }
+ if (ra != 31) {
+ tcg_gen_movi_i64(cpu_ir[ra], ctx->pc);
+ }
+ ret = EXIT_PC_UPDATED;
+ break;
+ case 0x1B:
+ /* HW_LD (PALcode) */
+#if defined (CONFIG_USER_ONLY)
+ goto invalid_opc;
+#else
+ if (!ctx->pal_mode)
+ goto invalid_opc;
+ if (ra != 31) {
+ TCGv addr = tcg_temp_new();
+ if (rb != 31)
+ tcg_gen_addi_i64(addr, cpu_ir[rb], disp12);
+ else
+ tcg_gen_movi_i64(addr, disp12);
+ switch ((insn >> 12) & 0xF) {
+ case 0x0:
+ /* Longword physical access (hw_ldl/p) */
+ gen_helper_ldl_raw(cpu_ir[ra], addr);
+ break;
+ case 0x1:
+ /* Quadword physical access (hw_ldq/p) */
+ gen_helper_ldq_raw(cpu_ir[ra], addr);
+ break;
+ case 0x2:
+ /* Longword physical access with lock (hw_ldl_l/p) */
+ gen_helper_ldl_l_raw(cpu_ir[ra], addr);
+ break;
+ case 0x3:
+ /* Quadword physical access with lock (hw_ldq_l/p) */
+ gen_helper_ldq_l_raw(cpu_ir[ra], addr);
+ break;
+ case 0x4:
+ /* Longword virtual PTE fetch (hw_ldl/v) */
+ tcg_gen_qemu_ld32s(cpu_ir[ra], addr, 0);
+ break;
+ case 0x5:
+ /* Quadword virtual PTE fetch (hw_ldq/v) */
+ tcg_gen_qemu_ld64(cpu_ir[ra], addr, 0);
+ break;
+ case 0x6:
+ /* Incpu_ir[ra]id */
+ goto invalid_opc;
+ case 0x7:
+ /* Incpu_ir[ra]id */
+ goto invalid_opc;
+ case 0x8:
+ /* Longword virtual access (hw_ldl) */
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_ldl_raw(cpu_ir[ra], addr);
+ break;
+ case 0x9:
+ /* Quadword virtual access (hw_ldq) */
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_ldq_raw(cpu_ir[ra], addr);
+ break;
+ case 0xA:
+ /* Longword virtual access with protection check (hw_ldl/w) */
+ tcg_gen_qemu_ld32s(cpu_ir[ra], addr, 0);
+ break;
+ case 0xB:
+ /* Quadword virtual access with protection check (hw_ldq/w) */
+ tcg_gen_qemu_ld64(cpu_ir[ra], addr, 0);
+ break;
+ case 0xC:
+ /* Longword virtual access with alt access mode (hw_ldl/a)*/
+ gen_helper_set_alt_mode();
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_ldl_raw(cpu_ir[ra], addr);
+ gen_helper_restore_mode();
+ break;
+ case 0xD:
+ /* Quadword virtual access with alt access mode (hw_ldq/a) */
+ gen_helper_set_alt_mode();
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_ldq_raw(cpu_ir[ra], addr);
+ gen_helper_restore_mode();
+ break;
+ case 0xE:
+ /* Longword virtual access with alternate access mode and
+ * protection checks (hw_ldl/wa)
+ */
+ gen_helper_set_alt_mode();
+ gen_helper_ldl_data(cpu_ir[ra], addr);
+ gen_helper_restore_mode();
+ break;
+ case 0xF:
+ /* Quadword virtual access with alternate access mode and
+ * protection checks (hw_ldq/wa)
+ */
+ gen_helper_set_alt_mode();
+ gen_helper_ldq_data(cpu_ir[ra], addr);
+ gen_helper_restore_mode();
+ break;
+ }
+ tcg_temp_free(addr);
+ }
+ break;
+#endif
+ case 0x1C:
+ switch (fn7) {
+ case 0x00:
+ /* SEXTB */
+ if (!(ctx->amask & AMASK_BWX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int8_t)lit));
+ else
+ tcg_gen_ext8s_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ break;
+ case 0x01:
+ /* SEXTW */
+ if (!(ctx->amask & AMASK_BWX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int16_t)lit));
+ else
+ tcg_gen_ext16s_i64(cpu_ir[rc], cpu_ir[rb]);
+ }
+ break;
+ case 0x30:
+ /* CTPOP */
+ if (!(ctx->amask & AMASK_CIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], ctpop64(lit));
+ else
+ gen_helper_ctpop(cpu_ir[rc], cpu_ir[rb]);
+ }
+ break;
+ case 0x31:
+ /* PERR */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_perr(ra, rb, rc, islit, lit);
+ break;
+ case 0x32:
+ /* CTLZ */
+ if (!(ctx->amask & AMASK_CIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], clz64(lit));
+ else
+ gen_helper_ctlz(cpu_ir[rc], cpu_ir[rb]);
+ }
+ break;
+ case 0x33:
+ /* CTTZ */
+ if (!(ctx->amask & AMASK_CIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (islit)
+ tcg_gen_movi_i64(cpu_ir[rc], ctz64(lit));
+ else
+ gen_helper_cttz(cpu_ir[rc], cpu_ir[rb]);
+ }
+ break;
+ case 0x34:
+ /* UNPKBW */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ if (real_islit || ra != 31)
+ goto invalid_opc;
+ gen_unpkbw (rb, rc);
+ break;
+ case 0x35:
+ /* UNPKBL */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ if (real_islit || ra != 31)
+ goto invalid_opc;
+ gen_unpkbl (rb, rc);
+ break;
+ case 0x36:
+ /* PKWB */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ if (real_islit || ra != 31)
+ goto invalid_opc;
+ gen_pkwb (rb, rc);
+ break;
+ case 0x37:
+ /* PKLB */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ if (real_islit || ra != 31)
+ goto invalid_opc;
+ gen_pklb (rb, rc);
+ break;
+ case 0x38:
+ /* MINSB8 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_minsb8 (ra, rb, rc, islit, lit);
+ break;
+ case 0x39:
+ /* MINSW4 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_minsw4 (ra, rb, rc, islit, lit);
+ break;
+ case 0x3A:
+ /* MINUB8 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_minub8 (ra, rb, rc, islit, lit);
+ break;
+ case 0x3B:
+ /* MINUW4 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_minuw4 (ra, rb, rc, islit, lit);
+ break;
+ case 0x3C:
+ /* MAXUB8 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_maxub8 (ra, rb, rc, islit, lit);
+ break;
+ case 0x3D:
+ /* MAXUW4 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_maxuw4 (ra, rb, rc, islit, lit);
+ break;
+ case 0x3E:
+ /* MAXSB8 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_maxsb8 (ra, rb, rc, islit, lit);
+ break;
+ case 0x3F:
+ /* MAXSW4 */
+ if (!(ctx->amask & AMASK_MVI))
+ goto invalid_opc;
+ gen_maxsw4 (ra, rb, rc, islit, lit);
+ break;
+ case 0x70:
+ /* FTOIT */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ if (likely(rc != 31)) {
+ if (ra != 31)
+ tcg_gen_mov_i64(cpu_ir[rc], cpu_fir[ra]);
+ else
+ tcg_gen_movi_i64(cpu_ir[rc], 0);
+ }
+ break;
+ case 0x78:
+ /* FTOIS */
+ if (!(ctx->amask & AMASK_FIX))
+ goto invalid_opc;
+ if (rc != 31) {
+ TCGv_i32 tmp1 = tcg_temp_new_i32();
+ if (ra != 31)
+ gen_helper_s_to_memory(tmp1, cpu_fir[ra]);
+ else {
+ TCGv tmp2 = tcg_const_i64(0);
+ gen_helper_s_to_memory(tmp1, tmp2);
+ tcg_temp_free(tmp2);
+ }
+ tcg_gen_ext_i32_i64(cpu_ir[rc], tmp1);
+ tcg_temp_free_i32(tmp1);
+ }
+ break;
+ default:
+ goto invalid_opc;
+ }
+ break;
+ case 0x1D:
+ /* HW_MTPR (PALcode) */
+#if defined (CONFIG_USER_ONLY)
+ goto invalid_opc;
+#else
+ if (!ctx->pal_mode)
+ goto invalid_opc;
+ else {
+ TCGv tmp1 = tcg_const_i32(insn & 0xFF);
+ if (ra != 31)
+ gen_helper_mtpr(tmp1, cpu_ir[ra]);
+ else {
+ TCGv tmp2 = tcg_const_i64(0);
+ gen_helper_mtpr(tmp1, tmp2);
+ tcg_temp_free(tmp2);
+ }
+ tcg_temp_free(tmp1);
+ ret = EXIT_PC_STALE;
+ }
+ break;
+#endif
+ case 0x1E:
+ /* HW_REI (PALcode) */
+#if defined (CONFIG_USER_ONLY)
+ goto invalid_opc;
+#else
+ if (!ctx->pal_mode)
+ goto invalid_opc;
+ if (rb == 31) {
+ /* "Old" alpha */
+ gen_helper_hw_rei();
+ } else {
+ TCGv tmp;
+
+ if (ra != 31) {
+ tmp = tcg_temp_new();
+ tcg_gen_addi_i64(tmp, cpu_ir[rb], (((int64_t)insn << 51) >> 51));
+ } else
+ tmp = tcg_const_i64(((int64_t)insn << 51) >> 51);
+ gen_helper_hw_ret(tmp);
+ tcg_temp_free(tmp);
+ }
+ ret = EXIT_PC_UPDATED;
+ break;
+#endif
+ case 0x1F:
+ /* HW_ST (PALcode) */
+#if defined (CONFIG_USER_ONLY)
+ goto invalid_opc;
+#else
+ if (!ctx->pal_mode)
+ goto invalid_opc;
+ else {
+ TCGv addr, val;
+ addr = tcg_temp_new();
+ if (rb != 31)
+ tcg_gen_addi_i64(addr, cpu_ir[rb], disp12);
+ else
+ tcg_gen_movi_i64(addr, disp12);
+ if (ra != 31)
+ val = cpu_ir[ra];
+ else {
+ val = tcg_temp_new();
+ tcg_gen_movi_i64(val, 0);
+ }
+ switch ((insn >> 12) & 0xF) {
+ case 0x0:
+ /* Longword physical access */
+ gen_helper_stl_raw(val, addr);
+ break;
+ case 0x1:
+ /* Quadword physical access */
+ gen_helper_stq_raw(val, addr);
+ break;
+ case 0x2:
+ /* Longword physical access with lock */
+ gen_helper_stl_c_raw(val, val, addr);
+ break;
+ case 0x3:
+ /* Quadword physical access with lock */
+ gen_helper_stq_c_raw(val, val, addr);
+ break;
+ case 0x4:
+ /* Longword virtual access */
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_stl_raw(val, addr);
+ break;
+ case 0x5:
+ /* Quadword virtual access */
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_stq_raw(val, addr);
+ break;
+ case 0x6:
+ /* Invalid */
+ goto invalid_opc;
+ case 0x7:
+ /* Invalid */
+ goto invalid_opc;
+ case 0x8:
+ /* Invalid */
+ goto invalid_opc;
+ case 0x9:
+ /* Invalid */
+ goto invalid_opc;
+ case 0xA:
+ /* Invalid */
+ goto invalid_opc;
+ case 0xB:
+ /* Invalid */
+ goto invalid_opc;
+ case 0xC:
+ /* Longword virtual access with alternate access mode */
+ gen_helper_set_alt_mode();
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_stl_raw(val, addr);
+ gen_helper_restore_mode();
+ break;
+ case 0xD:
+ /* Quadword virtual access with alternate access mode */
+ gen_helper_set_alt_mode();
+ gen_helper_st_virt_to_phys(addr, addr);
+ gen_helper_stl_raw(val, addr);
+ gen_helper_restore_mode();
+ break;
+ case 0xE:
+ /* Invalid */
+ goto invalid_opc;
+ case 0xF:
+ /* Invalid */
+ goto invalid_opc;
+ }
+ if (ra == 31)
+ tcg_temp_free(val);
+ tcg_temp_free(addr);
+ }
+ break;
+#endif
+ case 0x20:
+ /* LDF */
+ gen_load_mem(ctx, &gen_qemu_ldf, ra, rb, disp16, 1, 0);
+ break;
+ case 0x21:
+ /* LDG */
+ gen_load_mem(ctx, &gen_qemu_ldg, ra, rb, disp16, 1, 0);
+ break;
+ case 0x22:
+ /* LDS */
+ gen_load_mem(ctx, &gen_qemu_lds, ra, rb, disp16, 1, 0);
+ break;
+ case 0x23:
+ /* LDT */
+ gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 1, 0);
+ break;
+ case 0x24:
+ /* STF */
+ gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0);
+ break;
+ case 0x25:
+ /* STG */
+ gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0);
+ break;
+ case 0x26:
+ /* STS */
+ gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0);
+ break;
+ case 0x27:
+ /* STT */
+ gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0);
+ break;
+ case 0x28:
+ /* LDL */
+ gen_load_mem(ctx, &tcg_gen_qemu_ld32s, ra, rb, disp16, 0, 0);
+ break;
+ case 0x29:
+ /* LDQ */
+ gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 0);
+ break;
+ case 0x2A:
+ /* LDL_L */
+ gen_load_mem(ctx, &gen_qemu_ldl_l, ra, rb, disp16, 0, 0);
+ break;
+ case 0x2B:
+ /* LDQ_L */
+ gen_load_mem(ctx, &gen_qemu_ldq_l, ra, rb, disp16, 0, 0);
+ break;
+ case 0x2C:
+ /* STL */
+ gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0);
+ break;
+ case 0x2D:
+ /* STQ */
+ gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0);
+ break;
+ case 0x2E:
+ /* STL_C */
+ ret = gen_store_conditional(ctx, ra, rb, disp16, 0);
+ break;
+ case 0x2F:
+ /* STQ_C */
+ ret = gen_store_conditional(ctx, ra, rb, disp16, 1);
+ break;
+ case 0x30:
+ /* BR */
+ ret = gen_bdirect(ctx, ra, disp21);
+ break;
+ case 0x31: /* FBEQ */
+ ret = gen_fbcond(ctx, TCG_COND_EQ, ra, disp21);
+ break;
+ case 0x32: /* FBLT */
+ ret = gen_fbcond(ctx, TCG_COND_LT, ra, disp21);
+ break;
+ case 0x33: /* FBLE */
+ ret = gen_fbcond(ctx, TCG_COND_LE, ra, disp21);
+ break;
+ case 0x34:
+ /* BSR */
+ ret = gen_bdirect(ctx, ra, disp21);
+ break;
+ case 0x35: /* FBNE */
+ ret = gen_fbcond(ctx, TCG_COND_NE, ra, disp21);
+ break;
+ case 0x36: /* FBGE */
+ ret = gen_fbcond(ctx, TCG_COND_GE, ra, disp21);
+ break;
+ case 0x37: /* FBGT */
+ ret = gen_fbcond(ctx, TCG_COND_GT, ra, disp21);
+ break;
+ case 0x38:
+ /* BLBC */
+ ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 1);
+ break;
+ case 0x39:
+ /* BEQ */
+ ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 0);
+ break;
+ case 0x3A:
+ /* BLT */
+ ret = gen_bcond(ctx, TCG_COND_LT, ra, disp21, 0);
+ break;
+ case 0x3B:
+ /* BLE */
+ ret = gen_bcond(ctx, TCG_COND_LE, ra, disp21, 0);
+ break;
+ case 0x3C:
+ /* BLBS */
+ ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 1);
+ break;
+ case 0x3D:
+ /* BNE */
+ ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 0);
+ break;
+ case 0x3E:
+ /* BGE */
+ ret = gen_bcond(ctx, TCG_COND_GE, ra, disp21, 0);
+ break;
+ case 0x3F:
+ /* BGT */
+ ret = gen_bcond(ctx, TCG_COND_GT, ra, disp21, 0);
+ break;
+ invalid_opc:
+ ret = gen_invalid(ctx);
+ break;
+ }
+
+ return ret;
+}
+
+static inline void gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext ctx, *ctxp = &ctx;
+ target_ulong pc_start;
+ uint32_t insn;
+ uint16_t *gen_opc_end;
+ CPUBreakpoint *bp;
+ int j, lj = -1;
+ ExitStatus ret;
+ int num_insns;
+ int max_insns;
+
+ pc_start = tb->pc;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ ctx.tb = tb;
+ ctx.env = env;
+ ctx.pc = pc_start;
+ ctx.amask = env->amask;
+#if defined (CONFIG_USER_ONLY)
+ ctx.mem_idx = 0;
+#else
+ ctx.mem_idx = ((env->ps >> 3) & 3);
+ ctx.pal_mode = env->ipr[IPR_EXC_ADDR] & 1;
+#endif
+
+ /* ??? Every TB begins with unset rounding mode, to be initialized on
+ the first fp insn of the TB. Alternately we could define a proper
+ default for every TB (e.g. QUAL_RM_N or QUAL_RM_D) and make sure
+ to reset the FP_STATUS to that default at the end of any TB that
+ changes the default. We could even (gasp) dynamiclly figure out
+ what default would be most efficient given the running program. */
+ ctx.tb_rm = -1;
+ /* Similarly for flush-to-zero. */
+ ctx.tb_ftz = -1;
+
+ 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 == ctx.pc) {
+ gen_excp(&ctx, EXCP_DEBUG, 0);
+ 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] = ctx.pc;
+ 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();
+ insn = ldl_code(ctx.pc);
+ num_insns++;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) {
+ tcg_gen_debug_insn_start(ctx.pc);
+ }
+
+ ctx.pc += 4;
+ ret = translate_one(ctxp, insn);
+
+ if (ret == NO_EXIT) {
+ /* If we reach a page boundary, are single stepping,
+ or exhaust instruction count, stop generation. */
+ if (env->singlestep_enabled) {
+ gen_excp(&ctx, EXCP_DEBUG, 0);
+ ret = EXIT_PC_UPDATED;
+ } else if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0
+ || gen_opc_ptr >= gen_opc_end
+ || num_insns >= max_insns
+ || singlestep) {
+ ret = EXIT_PC_STALE;
+ }
+ }
+ } while (ret == NO_EXIT);
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+ switch (ret) {
+ case EXIT_GOTO_TB:
+ case EXIT_NORETURN:
+ break;
+ case EXIT_PC_STALE:
+ tcg_gen_movi_i64(cpu_pc, ctx.pc);
+ /* FALLTHRU */
+ case EXIT_PC_UPDATED:
+ tcg_gen_exit_tb(0);
+ break;
+ default:
+ abort();
+ }
+
+ 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 = ctx.pc - pc_start;
+ tb->icount = num_insns;
+ }
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, ctx.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);
+}
+
+struct cpu_def_t {
+ const char *name;
+ int implver, amask;
+};
+
+static const struct cpu_def_t cpu_defs[] = {
+ { "ev4", IMPLVER_2106x, 0 },
+ { "ev5", IMPLVER_21164, 0 },
+ { "ev56", IMPLVER_21164, AMASK_BWX },
+ { "pca56", IMPLVER_21164, AMASK_BWX | AMASK_MVI },
+ { "ev6", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP },
+ { "ev67", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX
+ | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), },
+ { "ev68", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX
+ | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), },
+ { "21064", IMPLVER_2106x, 0 },
+ { "21164", IMPLVER_21164, 0 },
+ { "21164a", IMPLVER_21164, AMASK_BWX },
+ { "21164pc", IMPLVER_21164, AMASK_BWX | AMASK_MVI },
+ { "21264", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP },
+ { "21264a", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX
+ | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }
+};
+
+CPUAlphaState * cpu_alpha_init (const char *cpu_model)
+{
+ CPUAlphaState *env;
+ int implver, amask, i, max;
+
+ env = qemu_mallocz(sizeof(CPUAlphaState));
+ cpu_exec_init(env);
+ alpha_translate_init();
+ tlb_flush(env, 1);
+
+ /* Default to ev67; no reason not to emulate insns by default. */
+ implver = IMPLVER_21264;
+ amask = (AMASK_BWX | AMASK_FIX | AMASK_CIX | AMASK_MVI
+ | AMASK_TRAP | AMASK_PREFETCH);
+
+ max = ARRAY_SIZE(cpu_defs);
+ for (i = 0; i < max; i++) {
+ if (strcmp (cpu_model, cpu_defs[i].name) == 0) {
+ implver = cpu_defs[i].implver;
+ amask = cpu_defs[i].amask;
+ break;
+ }
+ }
+ env->implver = implver;
+ env->amask = amask;
+
+ env->ps = 0x1F00;
+#if defined (CONFIG_USER_ONLY)
+ env->ps |= 1 << 3;
+ cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD
+ | FPCR_UNFD | FPCR_INED | FPCR_DNOD));
+#else
+ pal_init(env);
+#endif
+ env->lock_addr = -1;
+
+ /* Initialize IPR */
+#if defined (CONFIG_USER_ONLY)
+ env->ipr[IPR_EXC_ADDR] = 0;
+ env->ipr[IPR_EXC_SUM] = 0;
+ env->ipr[IPR_EXC_MASK] = 0;
+#else
+ {
+ // uint64_t hwpcb;
+ // hwpcb = env->ipr[IPR_PCBB];
+ env->ipr[IPR_ASN] = 0;
+ env->ipr[IPR_ASTEN] = 0;
+ env->ipr[IPR_ASTSR] = 0;
+ env->ipr[IPR_DATFX] = 0;
+ /* XXX: fix this */
+ // env->ipr[IPR_ESP] = ldq_raw(hwpcb + 8);
+ // env->ipr[IPR_KSP] = ldq_raw(hwpcb + 0);
+ // env->ipr[IPR_SSP] = ldq_raw(hwpcb + 16);
+ // env->ipr[IPR_USP] = ldq_raw(hwpcb + 24);
+ env->ipr[IPR_FEN] = 0;
+ env->ipr[IPR_IPL] = 31;
+ env->ipr[IPR_MCES] = 0;
+ env->ipr[IPR_PERFMON] = 0; /* Implementation specific */
+ // env->ipr[IPR_PTBR] = ldq_raw(hwpcb + 32);
+ env->ipr[IPR_SISR] = 0;
+ env->ipr[IPR_VIRBND] = -1ULL;
+ }
+#endif
+
+ qemu_init_vcpu(env);
+ return env;
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->pc = gen_opc_pc[pc_pos];
+}
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
new file mode 100644
index 0000000..5bcd53a
--- /dev/null
+++ b/target-arm/cpu.h
@@ -0,0 +1,508 @@
+/*
+ * ARM virtual CPU header
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#ifndef CPU_ARM_H
+#define CPU_ARM_H
+
+#define TARGET_LONG_BITS 32
+
+#define ELF_MACHINE EM_ARM
+
+#define CPUState struct CPUARMState
+
+#include "config.h"
+#include "qemu-common.h"
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+#define EXCP_UDEF 1 /* undefined instruction */
+#define EXCP_SWI 2 /* software interrupt */
+#define EXCP_PREFETCH_ABORT 3
+#define EXCP_DATA_ABORT 4
+#define EXCP_IRQ 5
+#define EXCP_FIQ 6
+#define EXCP_BKPT 7
+#define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */
+#define EXCP_KERNEL_TRAP 9 /* Jumped to kernel code page. */
+#define EXCP_STREX 10
+
+#define ARMV7M_EXCP_RESET 1
+#define ARMV7M_EXCP_NMI 2
+#define ARMV7M_EXCP_HARD 3
+#define ARMV7M_EXCP_MEM 4
+#define ARMV7M_EXCP_BUS 5
+#define ARMV7M_EXCP_USAGE 6
+#define ARMV7M_EXCP_SVC 11
+#define ARMV7M_EXCP_DEBUG 12
+#define ARMV7M_EXCP_PENDSV 14
+#define ARMV7M_EXCP_SYSTICK 15
+
+typedef void ARMWriteCPFunc(void *opaque, int cp_info,
+ int srcreg, int operand, uint32_t value);
+typedef uint32_t ARMReadCPFunc(void *opaque, int cp_info,
+ int dstreg, int operand);
+
+struct arm_boot_info;
+
+#define NB_MMU_MODES 2
+
+/* We currently assume float and double are IEEE single and double
+ precision respectively.
+ Doing runtime conversions is tricky because VFP registers may contain
+ integer values (eg. as the result of a FTOSI instruction).
+ s<2n> maps to the least significant half of d<n>
+ s<2n+1> maps to the most significant half of d<n>
+ */
+
+typedef struct CPUARMState {
+ /* Regs for current mode. */
+ uint32_t regs[16];
+ /* Frequently accessed CPSR bits are stored separately for efficiently.
+ This contains all the other bits. Use cpsr_{read,write} to access
+ the whole CPSR. */
+ uint32_t uncached_cpsr;
+ uint32_t spsr;
+
+ /* Banked registers. */
+ uint32_t banked_spsr[6];
+ uint32_t banked_r13[6];
+ uint32_t banked_r14[6];
+
+ /* These hold r8-r12. */
+ uint32_t usr_regs[5];
+ uint32_t fiq_regs[5];
+
+ /* cpsr flag cache for faster execution */
+ uint32_t CF; /* 0 or 1 */
+ uint32_t VF; /* V is the bit 31. All other bits are undefined */
+ uint32_t NF; /* N is bit 31. All other bits are undefined. */
+ uint32_t ZF; /* Z set if zero. */
+ uint32_t QF; /* 0 or 1 */
+ uint32_t GE; /* cpsr[19:16] */
+ uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */
+ uint32_t condexec_bits; /* IT bits. cpsr[15:10,26:25]. */
+
+ /* System control coprocessor (cp15) */
+ struct {
+ uint32_t c0_cpuid;
+ uint32_t c0_cachetype;
+ uint32_t c0_ccsid[16]; /* Cache size. */
+ uint32_t c0_clid; /* Cache level. */
+ uint32_t c0_cssel; /* Cache size selection. */
+ uint32_t c0_c1[8]; /* Feature registers. */
+ uint32_t c0_c2[8]; /* Instruction set registers. */
+ uint32_t c1_sys; /* System control register. */
+ uint32_t c1_coproc; /* Coprocessor access register. */
+ uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */
+ uint32_t c2_base0; /* MMU translation table base 0. */
+ uint32_t c2_base1; /* MMU translation table base 1. */
+ uint32_t c2_control; /* MMU translation table base control. */
+ uint32_t c2_mask; /* MMU translation table base selection mask. */
+ uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
+ uint32_t c2_data; /* MPU data cachable bits. */
+ uint32_t c2_insn; /* MPU instruction cachable bits. */
+ uint32_t c3; /* MMU domain access control register
+ MPU write buffer control. */
+ uint32_t c5_insn; /* Fault status registers. */
+ uint32_t c5_data;
+ uint32_t c6_region[8]; /* MPU base/size registers. */
+ uint32_t c6_insn; /* Fault address registers. */
+ uint32_t c6_data;
+ uint32_t c9_insn; /* Cache lockdown registers. */
+ uint32_t c9_data;
+ uint32_t c13_fcse; /* FCSE PID. */
+ uint32_t c13_context; /* Context ID. */
+ uint32_t c13_tls1; /* User RW Thread register. */
+ uint32_t c13_tls2; /* User RO Thread register. */
+ uint32_t c13_tls3; /* Privileged Thread register. */
+ uint32_t c15_cpar; /* XScale Coprocessor Access Register */
+ uint32_t c15_ticonfig; /* TI925T configuration byte. */
+ uint32_t c15_i_max; /* Maximum D-cache dirty line index. */
+ uint32_t c15_i_min; /* Minimum D-cache dirty line index. */
+ uint32_t c15_threadid; /* TI debugger thread-ID. */
+ } cp15;
+
+ struct {
+ uint32_t other_sp;
+ uint32_t vecbase;
+ uint32_t basepri;
+ uint32_t control;
+ int current_sp;
+ int exception;
+ int pending_exception;
+ } v7m;
+
+ /* Thumb-2 EE state. */
+ uint32_t teecr;
+ uint32_t teehbr;
+
+ /* Internal CPU feature flags. */
+ uint32_t features;
+
+ /* Callback for vectored interrupt controller. */
+ int (*get_irq_vector)(struct CPUARMState *);
+ void *irq_opaque;
+
+ /* VFP coprocessor state. */
+ struct {
+ float64 regs[32];
+
+ uint32_t xregs[16];
+ /* We store these fpcsr fields separately for convenience. */
+ int vec_len;
+ int vec_stride;
+
+ /* scratch space when Tn are not sufficient. */
+ uint32_t scratch[8];
+
+ /* fp_status is the "normal" fp status. standard_fp_status retains
+ * values corresponding to the ARM "Standard FPSCR Value", ie
+ * default-NaN, flush-to-zero, round-to-nearest and is used by
+ * any operations (generally Neon) which the architecture defines
+ * as controlled by the standard FPSCR value rather than the FPSCR.
+ *
+ * To avoid having to transfer exception bits around, we simply
+ * say that the FPSCR cumulative exception flags are the logical
+ * OR of the flags in the two fp statuses. This relies on the
+ * only thing which needs to read the exception flags being
+ * an explicit FPSCR read.
+ */
+ float_status fp_status;
+ float_status standard_fp_status;
+ } vfp;
+ uint32_t exclusive_addr;
+ uint32_t exclusive_val;
+ uint32_t exclusive_high;
+#if defined(CONFIG_USER_ONLY)
+ uint32_t exclusive_test;
+ uint32_t exclusive_info;
+#endif
+
+ /* iwMMXt coprocessor state. */
+ struct {
+ uint64_t regs[16];
+ uint64_t val;
+
+ uint32_t cregs[16];
+ } iwmmxt;
+
+#if defined(CONFIG_USER_ONLY)
+ /* For usermode syscall translation. */
+ int eabi;
+#endif
+
+ CPU_COMMON
+
+ /* These fields after the common ones so they are preserved on reset. */
+
+ /* Coprocessor IO used by peripherals */
+ struct {
+ ARMReadCPFunc *cp_read;
+ ARMWriteCPFunc *cp_write;
+ void *opaque;
+ } cp[15];
+ void *nvic;
+ struct arm_boot_info *boot_info;
+} CPUARMState;
+
+CPUARMState *cpu_arm_init(const char *cpu_model);
+void arm_translate_init(void);
+int cpu_arm_exec(CPUARMState *s);
+void cpu_arm_close(CPUARMState *s);
+void do_interrupt(CPUARMState *);
+void switch_mode(CPUARMState *, int);
+uint32_t do_arm_semihosting(CPUARMState *env);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_arm_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+int cpu_arm_handle_mmu_fault (CPUARMState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmuu);
+#define cpu_handle_mmu_fault cpu_arm_handle_mmu_fault
+
+static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls)
+{
+ env->cp15.c13_tls2 = newtls;
+}
+
+#define CPSR_M (0x1f)
+#define CPSR_T (1 << 5)
+#define CPSR_F (1 << 6)
+#define CPSR_I (1 << 7)
+#define CPSR_A (1 << 8)
+#define CPSR_E (1 << 9)
+#define CPSR_IT_2_7 (0xfc00)
+#define CPSR_GE (0xf << 16)
+#define CPSR_RESERVED (0xf << 20)
+#define CPSR_J (1 << 24)
+#define CPSR_IT_0_1 (3 << 25)
+#define CPSR_Q (1 << 27)
+#define CPSR_V (1 << 28)
+#define CPSR_C (1 << 29)
+#define CPSR_Z (1 << 30)
+#define CPSR_N (1 << 31)
+#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V)
+
+#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7)
+#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV)
+/* Bits writable in user mode. */
+#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
+/* Execution state bits. MRS read as zero, MSR writes ignored. */
+#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
+
+/* Return the current CPSR value. */
+uint32_t cpsr_read(CPUARMState *env);
+/* Set the CPSR. Note that some bits of mask must be all-set or all-clear. */
+void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask);
+
+/* Return the current xPSR value. */
+static inline uint32_t xpsr_read(CPUARMState *env)
+{
+ int ZF;
+ ZF = (env->ZF == 0);
+ return (env->NF & 0x80000000) | (ZF << 30)
+ | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
+ | (env->thumb << 24) | ((env->condexec_bits & 3) << 25)
+ | ((env->condexec_bits & 0xfc) << 8)
+ | env->v7m.exception;
+}
+
+/* Set the xPSR. Note that some bits of mask must be all-set or all-clear. */
+static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
+{
+ if (mask & CPSR_NZCV) {
+ env->ZF = (~val) & CPSR_Z;
+ env->NF = val;
+ env->CF = (val >> 29) & 1;
+ env->VF = (val << 3) & 0x80000000;
+ }
+ if (mask & CPSR_Q)
+ env->QF = ((val & CPSR_Q) != 0);
+ if (mask & (1 << 24))
+ env->thumb = ((val & (1 << 24)) != 0);
+ if (mask & CPSR_IT_0_1) {
+ env->condexec_bits &= ~3;
+ env->condexec_bits |= (val >> 25) & 3;
+ }
+ if (mask & CPSR_IT_2_7) {
+ env->condexec_bits &= 3;
+ env->condexec_bits |= (val >> 8) & 0xfc;
+ }
+ if (mask & 0x1ff) {
+ env->v7m.exception = val & 0x1ff;
+ }
+}
+
+/* Return the current FPSCR value. */
+uint32_t vfp_get_fpscr(CPUARMState *env);
+void vfp_set_fpscr(CPUARMState *env, uint32_t val);
+
+enum arm_cpu_mode {
+ ARM_CPU_MODE_USR = 0x10,
+ ARM_CPU_MODE_FIQ = 0x11,
+ ARM_CPU_MODE_IRQ = 0x12,
+ ARM_CPU_MODE_SVC = 0x13,
+ ARM_CPU_MODE_ABT = 0x17,
+ ARM_CPU_MODE_UND = 0x1b,
+ ARM_CPU_MODE_SYS = 0x1f
+};
+
+/* VFP system registers. */
+#define ARM_VFP_FPSID 0
+#define ARM_VFP_FPSCR 1
+#define ARM_VFP_MVFR1 6
+#define ARM_VFP_MVFR0 7
+#define ARM_VFP_FPEXC 8
+#define ARM_VFP_FPINST 9
+#define ARM_VFP_FPINST2 10
+
+/* iwMMXt coprocessor control registers. */
+#define ARM_IWMMXT_wCID 0
+#define ARM_IWMMXT_wCon 1
+#define ARM_IWMMXT_wCSSF 2
+#define ARM_IWMMXT_wCASF 3
+#define ARM_IWMMXT_wCGR0 8
+#define ARM_IWMMXT_wCGR1 9
+#define ARM_IWMMXT_wCGR2 10
+#define ARM_IWMMXT_wCGR3 11
+
+enum arm_features {
+ ARM_FEATURE_VFP,
+ ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */
+ ARM_FEATURE_XSCALE, /* Intel XScale extensions. */
+ ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */
+ ARM_FEATURE_V6,
+ ARM_FEATURE_V6K,
+ ARM_FEATURE_V7,
+ ARM_FEATURE_THUMB2,
+ ARM_FEATURE_MPU, /* Only has Memory Protection Unit, not full MMU. */
+ ARM_FEATURE_VFP3,
+ ARM_FEATURE_VFP_FP16,
+ ARM_FEATURE_NEON,
+ ARM_FEATURE_DIV,
+ ARM_FEATURE_M, /* Microcontroller profile. */
+ ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */
+ ARM_FEATURE_THUMB2EE
+};
+
+static inline int arm_feature(CPUARMState *env, int feature)
+{
+ return (env->features & (1u << feature)) != 0;
+}
+
+void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+
+/* Interface between CPU and Interrupt controller. */
+void armv7m_nvic_set_pending(void *opaque, int irq);
+int armv7m_nvic_acknowledge_irq(void *opaque);
+void armv7m_nvic_complete_irq(void *opaque, int irq);
+
+void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+ ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
+ void *opaque);
+
+/* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3.
+ Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
+ conventional cores (ie. Application or Realtime profile). */
+
+#define IS_M(env) arm_feature(env, ARM_FEATURE_M)
+#define ARM_CPUID(env) (env->cp15.c0_cpuid)
+
+#define ARM_CPUID_ARM1026 0x4106a262
+#define ARM_CPUID_ARM926 0x41069265
+#define ARM_CPUID_ARM946 0x41059461
+#define ARM_CPUID_TI915T 0x54029152
+#define ARM_CPUID_TI925T 0x54029252
+#define ARM_CPUID_PXA250 0x69052100
+#define ARM_CPUID_PXA255 0x69052d00
+#define ARM_CPUID_PXA260 0x69052903
+#define ARM_CPUID_PXA261 0x69052d05
+#define ARM_CPUID_PXA262 0x69052d06
+#define ARM_CPUID_PXA270 0x69054110
+#define ARM_CPUID_PXA270_A0 0x69054110
+#define ARM_CPUID_PXA270_A1 0x69054111
+#define ARM_CPUID_PXA270_B0 0x69054112
+#define ARM_CPUID_PXA270_B1 0x69054113
+#define ARM_CPUID_PXA270_C0 0x69054114
+#define ARM_CPUID_PXA270_C5 0x69054117
+#define ARM_CPUID_ARM1136 0x4117b363
+#define ARM_CPUID_ARM1136_R2 0x4107b362
+#define ARM_CPUID_ARM11MPCORE 0x410fb022
+#define ARM_CPUID_CORTEXA8 0x410fc080
+#define ARM_CPUID_CORTEXA9 0x410fc090
+#define ARM_CPUID_CORTEXM3 0x410fc231
+#define ARM_CPUID_ANY 0xffffffff
+
+#if defined(CONFIG_USER_ONLY)
+#define TARGET_PAGE_BITS 12
+#else
+/* The ARM MMU allows 1k pages. */
+/* ??? Linux doesn't actually use these, and they're deprecated in recent
+ architecture revisions. Maybe a configure option to disable them. */
+#define TARGET_PAGE_BITS 10
+#endif
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define cpu_init cpu_arm_init
+#define cpu_exec cpu_arm_exec
+#define cpu_gen_code cpu_arm_gen_code
+#define cpu_signal_handler cpu_arm_signal_handler
+#define cpu_list arm_cpu_list
+
+#define CPU_SAVE_VERSION 2
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR ? 1 : 0;
+}
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regs[13] = newsp;
+ env->regs[0] = 0;
+}
+#endif
+
+#include "cpu-all.h"
+
+/* Bit usage in the TB flags field: */
+#define ARM_TBFLAG_THUMB_SHIFT 0
+#define ARM_TBFLAG_THUMB_MASK (1 << ARM_TBFLAG_THUMB_SHIFT)
+#define ARM_TBFLAG_VECLEN_SHIFT 1
+#define ARM_TBFLAG_VECLEN_MASK (0x7 << ARM_TBFLAG_VECLEN_SHIFT)
+#define ARM_TBFLAG_VECSTRIDE_SHIFT 4
+#define ARM_TBFLAG_VECSTRIDE_MASK (0x3 << ARM_TBFLAG_VECSTRIDE_SHIFT)
+#define ARM_TBFLAG_PRIV_SHIFT 6
+#define ARM_TBFLAG_PRIV_MASK (1 << ARM_TBFLAG_PRIV_SHIFT)
+#define ARM_TBFLAG_VFPEN_SHIFT 7
+#define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT)
+#define ARM_TBFLAG_CONDEXEC_SHIFT 8
+#define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT)
+/* Bits 31..16 are currently unused. */
+
+/* some convenience accessor macros */
+#define ARM_TBFLAG_THUMB(F) \
+ (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT)
+#define ARM_TBFLAG_VECLEN(F) \
+ (((F) & ARM_TBFLAG_VECLEN_MASK) >> ARM_TBFLAG_VECLEN_SHIFT)
+#define ARM_TBFLAG_VECSTRIDE(F) \
+ (((F) & ARM_TBFLAG_VECSTRIDE_MASK) >> ARM_TBFLAG_VECSTRIDE_SHIFT)
+#define ARM_TBFLAG_PRIV(F) \
+ (((F) & ARM_TBFLAG_PRIV_MASK) >> ARM_TBFLAG_PRIV_SHIFT)
+#define ARM_TBFLAG_VFPEN(F) \
+ (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT)
+#define ARM_TBFLAG_CONDEXEC(F) \
+ (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT)
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ int privmode;
+ *pc = env->regs[15];
+ *cs_base = 0;
+ *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT)
+ | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT)
+ | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT)
+ | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT);
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1));
+ } else {
+ privmode = (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR;
+ }
+ if (privmode) {
+ *flags |= ARM_TBFLAG_PRIV_MASK;
+ }
+ if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) {
+ *flags |= ARM_TBFLAG_VFPEN_MASK;
+ }
+}
+
+#endif
diff --git a/target-arm/exec.h b/target-arm/exec.h
new file mode 100644
index 0000000..e4c35a3
--- /dev/null
+++ b/target-arm/exec.h
@@ -0,0 +1,58 @@
+/*
+ * ARM execution defines
+ *
+ * Copyright (c) 2003 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 "dyngen-exec.h"
+
+register struct CPUARMState *env asm(AREG0);
+
+#define M0 env->iwmmxt.val
+
+#include "cpu.h"
+#include "exec-all.h"
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request &
+ (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB));
+}
+
+static inline int cpu_halted(CPUState *env) {
+ if (!env->halted)
+ return 0;
+ /* An interrupt wakes the CPU even if the I and F CPSR bits are
+ set. We use EXITTB to silently wake CPU without causing an
+ actual interrupt. */
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+void raise_exception(int);
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->regs[15] = tb->pc;
+}
+
diff --git a/target-arm/helper.c b/target-arm/helper.c
new file mode 100644
index 0000000..b562767
--- /dev/null
+++ b/target-arm/helper.c
@@ -0,0 +1,2687 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "gdbstub.h"
+#include "helpers.h"
+#include "qemu-common.h"
+#include "host-utils.h"
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/loader.h"
+#endif
+
+static uint32_t cortexa9_cp15_c0_c1[8] =
+{ 0x1031, 0x11, 0x000, 0, 0x00100103, 0x20000000, 0x01230000, 0x00002111 };
+
+static uint32_t cortexa9_cp15_c0_c2[8] =
+{ 0x00101111, 0x13112111, 0x21232041, 0x11112131, 0x00111142, 0, 0, 0 };
+
+static uint32_t cortexa8_cp15_c0_c1[8] =
+{ 0x1031, 0x11, 0x400, 0, 0x31100003, 0x20000000, 0x01202000, 0x11 };
+
+static uint32_t cortexa8_cp15_c0_c2[8] =
+{ 0x00101111, 0x12112111, 0x21232031, 0x11112131, 0x00111142, 0, 0, 0 };
+
+static uint32_t mpcore_cp15_c0_c1[8] =
+{ 0x111, 0x1, 0, 0x2, 0x01100103, 0x10020302, 0x01222000, 0 };
+
+static uint32_t mpcore_cp15_c0_c2[8] =
+{ 0x00100011, 0x12002111, 0x11221011, 0x01102131, 0x141, 0, 0, 0 };
+
+static uint32_t arm1136_cp15_c0_c1[8] =
+{ 0x111, 0x1, 0x2, 0x3, 0x01130003, 0x10030302, 0x01222110, 0 };
+
+static uint32_t arm1136_cp15_c0_c2[8] =
+{ 0x00140011, 0x12002111, 0x11231111, 0x01102131, 0x141, 0, 0, 0 };
+
+static uint32_t cpu_arm_find_by_name(const char *name);
+
+static inline void set_feature(CPUARMState *env, int feature)
+{
+ env->features |= 1u << feature;
+}
+
+static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
+{
+ env->cp15.c0_cpuid = id;
+ switch (id) {
+ case ARM_CPUID_ARM926:
+ set_feature(env, ARM_FEATURE_VFP);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
+ env->cp15.c0_cachetype = 0x1dd20d2;
+ env->cp15.c1_sys = 0x00090078;
+ break;
+ case ARM_CPUID_ARM946:
+ set_feature(env, ARM_FEATURE_MPU);
+ env->cp15.c0_cachetype = 0x0f004006;
+ env->cp15.c1_sys = 0x00000078;
+ break;
+ case ARM_CPUID_ARM1026:
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_AUXCR);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
+ env->cp15.c0_cachetype = 0x1dd20d2;
+ env->cp15.c1_sys = 0x00090078;
+ break;
+ case ARM_CPUID_ARM1136_R2:
+ case ARM_CPUID_ARM1136:
+ set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_AUXCR);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
+ env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
+ env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000;
+ memcpy(env->cp15.c0_c1, arm1136_cp15_c0_c1, 8 * sizeof(uint32_t));
+ memcpy(env->cp15.c0_c2, arm1136_cp15_c0_c2, 8 * sizeof(uint32_t));
+ env->cp15.c0_cachetype = 0x1dd20d2;
+ env->cp15.c1_sys = 0x00050078;
+ break;
+ case ARM_CPUID_ARM11MPCORE:
+ set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_V6K);
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_AUXCR);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
+ env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
+ env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000;
+ memcpy(env->cp15.c0_c1, mpcore_cp15_c0_c1, 8 * sizeof(uint32_t));
+ memcpy(env->cp15.c0_c2, mpcore_cp15_c0_c2, 8 * sizeof(uint32_t));
+ env->cp15.c0_cachetype = 0x1dd20d2;
+ break;
+ case ARM_CPUID_CORTEXA8:
+ set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_V6K);
+ set_feature(env, ARM_FEATURE_V7);
+ set_feature(env, ARM_FEATURE_AUXCR);
+ set_feature(env, ARM_FEATURE_THUMB2);
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_VFP3);
+ set_feature(env, ARM_FEATURE_NEON);
+ set_feature(env, ARM_FEATURE_THUMB2EE);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x410330c0;
+ env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
+ env->vfp.xregs[ARM_VFP_MVFR1] = 0x00011100;
+ memcpy(env->cp15.c0_c1, cortexa8_cp15_c0_c1, 8 * sizeof(uint32_t));
+ memcpy(env->cp15.c0_c2, cortexa8_cp15_c0_c2, 8 * sizeof(uint32_t));
+ env->cp15.c0_cachetype = 0x82048004;
+ env->cp15.c0_clid = (1 << 27) | (2 << 24) | 3;
+ env->cp15.c0_ccsid[0] = 0xe007e01a; /* 16k L1 dcache. */
+ env->cp15.c0_ccsid[1] = 0x2007e01a; /* 16k L1 icache. */
+ env->cp15.c0_ccsid[2] = 0xf0000000; /* No L2 icache. */
+ env->cp15.c1_sys = 0x00c50078;
+ break;
+ case ARM_CPUID_CORTEXA9:
+ set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_V6K);
+ set_feature(env, ARM_FEATURE_V7);
+ set_feature(env, ARM_FEATURE_AUXCR);
+ set_feature(env, ARM_FEATURE_THUMB2);
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_VFP3);
+ set_feature(env, ARM_FEATURE_VFP_FP16);
+ set_feature(env, ARM_FEATURE_NEON);
+ set_feature(env, ARM_FEATURE_THUMB2EE);
+ env->vfp.xregs[ARM_VFP_FPSID] = 0x41034000; /* Guess */
+ env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
+ env->vfp.xregs[ARM_VFP_MVFR1] = 0x01111111;
+ memcpy(env->cp15.c0_c1, cortexa9_cp15_c0_c1, 8 * sizeof(uint32_t));
+ memcpy(env->cp15.c0_c2, cortexa9_cp15_c0_c2, 8 * sizeof(uint32_t));
+ env->cp15.c0_cachetype = 0x80038003;
+ env->cp15.c0_clid = (1 << 27) | (1 << 24) | 3;
+ env->cp15.c0_ccsid[0] = 0xe00fe015; /* 16k L1 dcache. */
+ env->cp15.c0_ccsid[1] = 0x200fe015; /* 16k L1 icache. */
+ env->cp15.c1_sys = 0x00c50078;
+ break;
+ case ARM_CPUID_CORTEXM3:
+ set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_THUMB2);
+ set_feature(env, ARM_FEATURE_V7);
+ set_feature(env, ARM_FEATURE_M);
+ set_feature(env, ARM_FEATURE_DIV);
+ break;
+ case ARM_CPUID_ANY: /* For userspace emulation. */
+ set_feature(env, ARM_FEATURE_V6);
+ set_feature(env, ARM_FEATURE_V6K);
+ set_feature(env, ARM_FEATURE_V7);
+ set_feature(env, ARM_FEATURE_THUMB2);
+ set_feature(env, ARM_FEATURE_VFP);
+ set_feature(env, ARM_FEATURE_VFP3);
+ set_feature(env, ARM_FEATURE_VFP_FP16);
+ set_feature(env, ARM_FEATURE_NEON);
+ set_feature(env, ARM_FEATURE_THUMB2EE);
+ set_feature(env, ARM_FEATURE_DIV);
+ break;
+ case ARM_CPUID_TI915T:
+ case ARM_CPUID_TI925T:
+ set_feature(env, ARM_FEATURE_OMAPCP);
+ env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */
+ env->cp15.c0_cachetype = 0x5109149;
+ env->cp15.c1_sys = 0x00000070;
+ env->cp15.c15_i_max = 0x000;
+ env->cp15.c15_i_min = 0xff0;
+ break;
+ case ARM_CPUID_PXA250:
+ case ARM_CPUID_PXA255:
+ case ARM_CPUID_PXA260:
+ case ARM_CPUID_PXA261:
+ case ARM_CPUID_PXA262:
+ set_feature(env, ARM_FEATURE_XSCALE);
+ /* JTAG_ID is ((id << 28) | 0x09265013) */
+ env->cp15.c0_cachetype = 0xd172172;
+ env->cp15.c1_sys = 0x00000078;
+ break;
+ case ARM_CPUID_PXA270_A0:
+ case ARM_CPUID_PXA270_A1:
+ case ARM_CPUID_PXA270_B0:
+ case ARM_CPUID_PXA270_B1:
+ case ARM_CPUID_PXA270_C0:
+ case ARM_CPUID_PXA270_C5:
+ set_feature(env, ARM_FEATURE_XSCALE);
+ /* JTAG_ID is ((id << 28) | 0x09265013) */
+ set_feature(env, ARM_FEATURE_IWMMXT);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q';
+ env->cp15.c0_cachetype = 0xd172172;
+ env->cp15.c1_sys = 0x00000078;
+ break;
+ default:
+ cpu_abort(env, "Bad CPU ID: %x\n", id);
+ break;
+ }
+}
+
+void cpu_reset(CPUARMState *env)
+{
+ uint32_t id;
+
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ id = env->cp15.c0_cpuid;
+ memset(env, 0, offsetof(CPUARMState, breakpoints));
+ if (id)
+ cpu_reset_model_id(env, id);
+#if defined (CONFIG_USER_ONLY)
+ env->uncached_cpsr = ARM_CPU_MODE_USR;
+ /* For user mode we must enable access to coprocessors */
+ env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30;
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ env->cp15.c15_cpar = 3;
+ } else if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ env->cp15.c15_cpar = 1;
+ }
+#else
+ /* SVC mode with interrupts disabled. */
+ env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
+ /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is
+ clear at reset. Initial SP and PC are loaded from ROM. */
+ if (IS_M(env)) {
+ uint32_t pc;
+ uint8_t *rom;
+ env->uncached_cpsr &= ~CPSR_I;
+ rom = rom_ptr(0);
+ if (rom) {
+ /* We should really use ldl_phys here, in case the guest
+ modified flash and reset itself. However images
+ loaded via -kenrel have not been copied yet, so load the
+ values directly from there. */
+ env->regs[13] = ldl_p(rom);
+ pc = ldl_p(rom + 4);
+ env->thumb = pc & 1;
+ env->regs[15] = pc & ~1;
+ }
+ }
+ env->vfp.xregs[ARM_VFP_FPEXC] = 0;
+ env->cp15.c2_base_mask = 0xffffc000u;
+#endif
+ set_flush_to_zero(1, &env->vfp.standard_fp_status);
+ set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
+ set_default_nan_mode(1, &env->vfp.standard_fp_status);
+ tlb_flush(env, 1);
+}
+
+static int vfp_gdb_get_reg(CPUState *env, uint8_t *buf, int reg)
+{
+ int nregs;
+
+ /* VFP data registers are always little-endian. */
+ nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16;
+ if (reg < nregs) {
+ stfq_le_p(buf, env->vfp.regs[reg]);
+ return 8;
+ }
+ if (arm_feature(env, ARM_FEATURE_NEON)) {
+ /* Aliases for Q regs. */
+ nregs += 16;
+ if (reg < nregs) {
+ stfq_le_p(buf, env->vfp.regs[(reg - 32) * 2]);
+ stfq_le_p(buf + 8, env->vfp.regs[(reg - 32) * 2 + 1]);
+ return 16;
+ }
+ }
+ switch (reg - nregs) {
+ case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4;
+ case 1: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSCR]); return 4;
+ case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4;
+ }
+ return 0;
+}
+
+static int vfp_gdb_set_reg(CPUState *env, uint8_t *buf, int reg)
+{
+ int nregs;
+
+ nregs = arm_feature(env, ARM_FEATURE_VFP3) ? 32 : 16;
+ if (reg < nregs) {
+ env->vfp.regs[reg] = ldfq_le_p(buf);
+ return 8;
+ }
+ if (arm_feature(env, ARM_FEATURE_NEON)) {
+ nregs += 16;
+ if (reg < nregs) {
+ env->vfp.regs[(reg - 32) * 2] = ldfq_le_p(buf);
+ env->vfp.regs[(reg - 32) * 2 + 1] = ldfq_le_p(buf + 8);
+ return 16;
+ }
+ }
+ switch (reg - nregs) {
+ case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4;
+ case 1: env->vfp.xregs[ARM_VFP_FPSCR] = ldl_p(buf); return 4;
+ case 2: env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); return 4;
+ }
+ return 0;
+}
+
+CPUARMState *cpu_arm_init(const char *cpu_model)
+{
+ CPUARMState *env;
+ uint32_t id;
+ static int inited = 0;
+
+ id = cpu_arm_find_by_name(cpu_model);
+ if (id == 0)
+ return NULL;
+ env = qemu_mallocz(sizeof(CPUARMState));
+ cpu_exec_init(env);
+ if (!inited) {
+ inited = 1;
+ arm_translate_init();
+ }
+
+ env->cpu_model_str = cpu_model;
+ env->cp15.c0_cpuid = id;
+ cpu_reset(env);
+ if (arm_feature(env, ARM_FEATURE_NEON)) {
+ gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
+ 51, "arm-neon.xml", 0);
+ } else if (arm_feature(env, ARM_FEATURE_VFP3)) {
+ gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
+ 35, "arm-vfp3.xml", 0);
+ } else if (arm_feature(env, ARM_FEATURE_VFP)) {
+ gdb_register_coprocessor(env, vfp_gdb_get_reg, vfp_gdb_set_reg,
+ 19, "arm-vfp.xml", 0);
+ }
+ qemu_init_vcpu(env);
+ return env;
+}
+
+struct arm_cpu_t {
+ uint32_t id;
+ const char *name;
+};
+
+static const struct arm_cpu_t arm_cpu_names[] = {
+ { ARM_CPUID_ARM926, "arm926"},
+ { ARM_CPUID_ARM946, "arm946"},
+ { ARM_CPUID_ARM1026, "arm1026"},
+ { ARM_CPUID_ARM1136, "arm1136"},
+ { ARM_CPUID_ARM1136_R2, "arm1136-r2"},
+ { ARM_CPUID_ARM11MPCORE, "arm11mpcore"},
+ { ARM_CPUID_CORTEXM3, "cortex-m3"},
+ { ARM_CPUID_CORTEXA8, "cortex-a8"},
+ { ARM_CPUID_CORTEXA9, "cortex-a9"},
+ { ARM_CPUID_TI925T, "ti925t" },
+ { ARM_CPUID_PXA250, "pxa250" },
+ { ARM_CPUID_PXA255, "pxa255" },
+ { ARM_CPUID_PXA260, "pxa260" },
+ { ARM_CPUID_PXA261, "pxa261" },
+ { ARM_CPUID_PXA262, "pxa262" },
+ { ARM_CPUID_PXA270, "pxa270" },
+ { ARM_CPUID_PXA270_A0, "pxa270-a0" },
+ { ARM_CPUID_PXA270_A1, "pxa270-a1" },
+ { ARM_CPUID_PXA270_B0, "pxa270-b0" },
+ { ARM_CPUID_PXA270_B1, "pxa270-b1" },
+ { ARM_CPUID_PXA270_C0, "pxa270-c0" },
+ { ARM_CPUID_PXA270_C5, "pxa270-c5" },
+ { ARM_CPUID_ANY, "any"},
+ { 0, NULL}
+};
+
+void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ int i;
+
+ (*cpu_fprintf)(f, "Available CPUs:\n");
+ for (i = 0; arm_cpu_names[i].name; i++) {
+ (*cpu_fprintf)(f, " %s\n", arm_cpu_names[i].name);
+ }
+}
+
+/* return 0 if not found */
+static uint32_t cpu_arm_find_by_name(const char *name)
+{
+ int i;
+ uint32_t id;
+
+ id = 0;
+ for (i = 0; arm_cpu_names[i].name; i++) {
+ if (strcmp(name, arm_cpu_names[i].name) == 0) {
+ id = arm_cpu_names[i].id;
+ break;
+ }
+ }
+ return id;
+}
+
+void cpu_arm_close(CPUARMState *env)
+{
+ free(env);
+}
+
+uint32_t cpsr_read(CPUARMState *env)
+{
+ int ZF;
+ ZF = (env->ZF == 0);
+ return env->uncached_cpsr | (env->NF & 0x80000000) | (ZF << 30) |
+ (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
+ | (env->thumb << 5) | ((env->condexec_bits & 3) << 25)
+ | ((env->condexec_bits & 0xfc) << 8)
+ | (env->GE << 16);
+}
+
+void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
+{
+ if (mask & CPSR_NZCV) {
+ env->ZF = (~val) & CPSR_Z;
+ env->NF = val;
+ env->CF = (val >> 29) & 1;
+ env->VF = (val << 3) & 0x80000000;
+ }
+ if (mask & CPSR_Q)
+ env->QF = ((val & CPSR_Q) != 0);
+ if (mask & CPSR_T)
+ env->thumb = ((val & CPSR_T) != 0);
+ if (mask & CPSR_IT_0_1) {
+ env->condexec_bits &= ~3;
+ env->condexec_bits |= (val >> 25) & 3;
+ }
+ if (mask & CPSR_IT_2_7) {
+ env->condexec_bits &= 3;
+ env->condexec_bits |= (val >> 8) & 0xfc;
+ }
+ if (mask & CPSR_GE) {
+ env->GE = (val >> 16) & 0xf;
+ }
+
+ if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
+ switch_mode(env, val & CPSR_M);
+ }
+ mask &= ~CACHED_CPSR_BITS;
+ env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
+}
+
+/* Sign/zero extend */
+uint32_t HELPER(sxtb16)(uint32_t x)
+{
+ uint32_t res;
+ res = (uint16_t)(int8_t)x;
+ res |= (uint32_t)(int8_t)(x >> 16) << 16;
+ return res;
+}
+
+uint32_t HELPER(uxtb16)(uint32_t x)
+{
+ uint32_t res;
+ res = (uint16_t)(uint8_t)x;
+ res |= (uint32_t)(uint8_t)(x >> 16) << 16;
+ return res;
+}
+
+uint32_t HELPER(clz)(uint32_t x)
+{
+ return clz32(x);
+}
+
+int32_t HELPER(sdiv)(int32_t num, int32_t den)
+{
+ if (den == 0)
+ return 0;
+ if (num == INT_MIN && den == -1)
+ return INT_MIN;
+ return num / den;
+}
+
+uint32_t HELPER(udiv)(uint32_t num, uint32_t den)
+{
+ if (den == 0)
+ return 0;
+ return num / den;
+}
+
+uint32_t HELPER(rbit)(uint32_t x)
+{
+ x = ((x & 0xff000000) >> 24)
+ | ((x & 0x00ff0000) >> 8)
+ | ((x & 0x0000ff00) << 8)
+ | ((x & 0x000000ff) << 24);
+ x = ((x & 0xf0f0f0f0) >> 4)
+ | ((x & 0x0f0f0f0f) << 4);
+ x = ((x & 0x88888888) >> 3)
+ | ((x & 0x44444444) >> 1)
+ | ((x & 0x22222222) << 1)
+ | ((x & 0x11111111) << 3);
+ return x;
+}
+
+uint32_t HELPER(abs)(uint32_t x)
+{
+ return ((int32_t)x < 0) ? -x : x;
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ if (rw == 2) {
+ env->exception_index = EXCP_PREFETCH_ABORT;
+ env->cp15.c6_insn = address;
+ } else {
+ env->exception_index = EXCP_DATA_ABORT;
+ env->cp15.c6_data = address;
+ }
+ return 1;
+}
+
+/* These should probably raise undefined insn exceptions. */
+void HELPER(set_cp)(CPUState *env, uint32_t insn, uint32_t val)
+{
+ int op1 = (insn >> 8) & 0xf;
+ cpu_abort(env, "cp%i insn %08x\n", op1, insn);
+ return;
+}
+
+uint32_t HELPER(get_cp)(CPUState *env, uint32_t insn)
+{
+ int op1 = (insn >> 8) & 0xf;
+ cpu_abort(env, "cp%i insn %08x\n", op1, insn);
+ return 0;
+}
+
+void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
+{
+ cpu_abort(env, "cp15 insn %08x\n", insn);
+}
+
+uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
+{
+ cpu_abort(env, "cp15 insn %08x\n", insn);
+}
+
+/* These should probably raise undefined insn exceptions. */
+void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val)
+{
+ cpu_abort(env, "v7m_mrs %d\n", reg);
+}
+
+uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg)
+{
+ cpu_abort(env, "v7m_mrs %d\n", reg);
+ return 0;
+}
+
+void switch_mode(CPUState *env, int mode)
+{
+ if (mode != ARM_CPU_MODE_USR)
+ cpu_abort(env, "Tried to switch out of user mode\n");
+}
+
+void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val)
+{
+ cpu_abort(env, "banked r13 write\n");
+}
+
+uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode)
+{
+ cpu_abort(env, "banked r13 read\n");
+ return 0;
+}
+
+#else
+
+extern int semihosting_enabled;
+
+/* Map CPU modes onto saved register banks. */
+static inline int bank_number (int mode)
+{
+ switch (mode) {
+ case ARM_CPU_MODE_USR:
+ case ARM_CPU_MODE_SYS:
+ return 0;
+ case ARM_CPU_MODE_SVC:
+ return 1;
+ case ARM_CPU_MODE_ABT:
+ return 2;
+ case ARM_CPU_MODE_UND:
+ return 3;
+ case ARM_CPU_MODE_IRQ:
+ return 4;
+ case ARM_CPU_MODE_FIQ:
+ return 5;
+ }
+ cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
+ return -1;
+}
+
+void switch_mode(CPUState *env, int mode)
+{
+ int old_mode;
+ int i;
+
+ old_mode = env->uncached_cpsr & CPSR_M;
+ if (mode == old_mode)
+ return;
+
+ if (old_mode == ARM_CPU_MODE_FIQ) {
+ memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
+ } else if (mode == ARM_CPU_MODE_FIQ) {
+ memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
+ memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
+ }
+
+ i = bank_number(old_mode);
+ env->banked_r13[i] = env->regs[13];
+ env->banked_r14[i] = env->regs[14];
+ env->banked_spsr[i] = env->spsr;
+
+ i = bank_number(mode);
+ env->regs[13] = env->banked_r13[i];
+ env->regs[14] = env->banked_r14[i];
+ env->spsr = env->banked_spsr[i];
+}
+
+static void v7m_push(CPUARMState *env, uint32_t val)
+{
+ env->regs[13] -= 4;
+ stl_phys(env->regs[13], val);
+}
+
+static uint32_t v7m_pop(CPUARMState *env)
+{
+ uint32_t val;
+ val = ldl_phys(env->regs[13]);
+ env->regs[13] += 4;
+ return val;
+}
+
+/* Switch to V7M main or process stack pointer. */
+static void switch_v7m_sp(CPUARMState *env, int process)
+{
+ uint32_t tmp;
+ if (env->v7m.current_sp != process) {
+ tmp = env->v7m.other_sp;
+ env->v7m.other_sp = env->regs[13];
+ env->regs[13] = tmp;
+ env->v7m.current_sp = process;
+ }
+}
+
+static void do_v7m_exception_exit(CPUARMState *env)
+{
+ uint32_t type;
+ uint32_t xpsr;
+
+ type = env->regs[15];
+ if (env->v7m.exception != 0)
+ armv7m_nvic_complete_irq(env->nvic, env->v7m.exception);
+
+ /* Switch to the target stack. */
+ switch_v7m_sp(env, (type & 4) != 0);
+ /* Pop registers. */
+ env->regs[0] = v7m_pop(env);
+ env->regs[1] = v7m_pop(env);
+ env->regs[2] = v7m_pop(env);
+ env->regs[3] = v7m_pop(env);
+ env->regs[12] = v7m_pop(env);
+ env->regs[14] = v7m_pop(env);
+ env->regs[15] = v7m_pop(env);
+ xpsr = v7m_pop(env);
+ xpsr_write(env, xpsr, 0xfffffdff);
+ /* Undo stack alignment. */
+ if (xpsr & 0x200)
+ env->regs[13] |= 4;
+ /* ??? The exception return type specifies Thread/Handler mode. However
+ this is also implied by the xPSR value. Not sure what to do
+ if there is a mismatch. */
+ /* ??? Likewise for mismatches between the CONTROL register and the stack
+ pointer. */
+}
+
+static void do_interrupt_v7m(CPUARMState *env)
+{
+ uint32_t xpsr = xpsr_read(env);
+ uint32_t lr;
+ uint32_t addr;
+
+ lr = 0xfffffff1;
+ if (env->v7m.current_sp)
+ lr |= 4;
+ if (env->v7m.exception == 0)
+ lr |= 8;
+
+ /* For exceptions we just mark as pending on the NVIC, and let that
+ handle it. */
+ /* TODO: Need to escalate if the current priority is higher than the
+ one we're raising. */
+ switch (env->exception_index) {
+ case EXCP_UDEF:
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
+ return;
+ case EXCP_SWI:
+ env->regs[15] += 2;
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC);
+ return;
+ case EXCP_PREFETCH_ABORT:
+ case EXCP_DATA_ABORT:
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
+ return;
+ case EXCP_BKPT:
+ if (semihosting_enabled) {
+ int nr;
+ nr = lduw_code(env->regs[15]) & 0xff;
+ if (nr == 0xab) {
+ env->regs[15] += 2;
+ env->regs[0] = do_arm_semihosting(env);
+ return;
+ }
+ }
+ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
+ return;
+ case EXCP_IRQ:
+ env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic);
+ break;
+ case EXCP_EXCEPTION_EXIT:
+ do_v7m_exception_exit(env);
+ return;
+ default:
+ cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
+ return; /* Never happens. Keep compiler happy. */
+ }
+
+ /* Align stack pointer. */
+ /* ??? Should only do this if Configuration Control Register
+ STACKALIGN bit is set. */
+ if (env->regs[13] & 4) {
+ env->regs[13] -= 4;
+ xpsr |= 0x200;
+ }
+ /* Switch to the handler mode. */
+ v7m_push(env, xpsr);
+ v7m_push(env, env->regs[15]);
+ v7m_push(env, env->regs[14]);
+ v7m_push(env, env->regs[12]);
+ v7m_push(env, env->regs[3]);
+ v7m_push(env, env->regs[2]);
+ v7m_push(env, env->regs[1]);
+ v7m_push(env, env->regs[0]);
+ switch_v7m_sp(env, 0);
+ env->uncached_cpsr &= ~CPSR_IT;
+ env->regs[14] = lr;
+ addr = ldl_phys(env->v7m.vecbase + env->v7m.exception * 4);
+ env->regs[15] = addr & 0xfffffffe;
+ env->thumb = addr & 1;
+}
+
+/* Handle a CPU exception. */
+void do_interrupt(CPUARMState *env)
+{
+ uint32_t addr;
+ uint32_t mask;
+ int new_mode;
+ uint32_t offset;
+
+ if (IS_M(env)) {
+ do_interrupt_v7m(env);
+ return;
+ }
+ /* TODO: Vectored interrupt controller. */
+ switch (env->exception_index) {
+ case EXCP_UDEF:
+ new_mode = ARM_CPU_MODE_UND;
+ addr = 0x04;
+ mask = CPSR_I;
+ if (env->thumb)
+ offset = 2;
+ else
+ offset = 4;
+ break;
+ case EXCP_SWI:
+ if (semihosting_enabled) {
+ /* Check for semihosting interrupt. */
+ if (env->thumb) {
+ mask = lduw_code(env->regs[15] - 2) & 0xff;
+ } else {
+ mask = ldl_code(env->regs[15] - 4) & 0xffffff;
+ }
+ /* Only intercept calls from privileged modes, to provide some
+ semblance of security. */
+ if (((mask == 0x123456 && !env->thumb)
+ || (mask == 0xab && env->thumb))
+ && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
+ env->regs[0] = do_arm_semihosting(env);
+ return;
+ }
+ }
+ new_mode = ARM_CPU_MODE_SVC;
+ addr = 0x08;
+ mask = CPSR_I;
+ /* The PC already points to the next instruction. */
+ offset = 0;
+ break;
+ case EXCP_BKPT:
+ /* See if this is a semihosting syscall. */
+ if (env->thumb && semihosting_enabled) {
+ mask = lduw_code(env->regs[15]) & 0xff;
+ if (mask == 0xab
+ && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
+ env->regs[15] += 2;
+ env->regs[0] = do_arm_semihosting(env);
+ return;
+ }
+ }
+ /* Fall through to prefetch abort. */
+ case EXCP_PREFETCH_ABORT:
+ new_mode = ARM_CPU_MODE_ABT;
+ addr = 0x0c;
+ mask = CPSR_A | CPSR_I;
+ offset = 4;
+ break;
+ case EXCP_DATA_ABORT:
+ new_mode = ARM_CPU_MODE_ABT;
+ addr = 0x10;
+ mask = CPSR_A | CPSR_I;
+ offset = 8;
+ break;
+ case EXCP_IRQ:
+ new_mode = ARM_CPU_MODE_IRQ;
+ addr = 0x18;
+ /* Disable IRQ and imprecise data aborts. */
+ mask = CPSR_A | CPSR_I;
+ offset = 4;
+ break;
+ case EXCP_FIQ:
+ new_mode = ARM_CPU_MODE_FIQ;
+ addr = 0x1c;
+ /* Disable FIQ, IRQ and imprecise data aborts. */
+ mask = CPSR_A | CPSR_I | CPSR_F;
+ offset = 4;
+ break;
+ default:
+ cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
+ return; /* Never happens. Keep compiler happy. */
+ }
+ /* High vectors. */
+ if (env->cp15.c1_sys & (1 << 13)) {
+ addr += 0xffff0000;
+ }
+ switch_mode (env, new_mode);
+ env->spsr = cpsr_read(env);
+ /* Clear IT bits. */
+ env->condexec_bits = 0;
+ /* Switch to the new mode, and to the correct instruction set. */
+ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
+ env->uncached_cpsr |= mask;
+ env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0;
+ env->regs[14] = env->regs[15] + offset;
+ env->regs[15] = addr;
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+/* Check section/page access permissions.
+ Returns the page protection flags, or zero if the access is not
+ permitted. */
+static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
+ int is_user)
+{
+ int prot_ro;
+
+ if (domain == 3)
+ return PAGE_READ | PAGE_WRITE;
+
+ if (access_type == 1)
+ prot_ro = 0;
+ else
+ prot_ro = PAGE_READ;
+
+ switch (ap) {
+ case 0:
+ if (access_type == 1)
+ return 0;
+ switch ((env->cp15.c1_sys >> 8) & 3) {
+ case 1:
+ return is_user ? 0 : PAGE_READ;
+ case 2:
+ return PAGE_READ;
+ default:
+ return 0;
+ }
+ case 1:
+ return is_user ? 0 : PAGE_READ | PAGE_WRITE;
+ case 2:
+ if (is_user)
+ return prot_ro;
+ else
+ return PAGE_READ | PAGE_WRITE;
+ case 3:
+ return PAGE_READ | PAGE_WRITE;
+ case 4: /* Reserved. */
+ return 0;
+ case 5:
+ return is_user ? 0 : prot_ro;
+ case 6:
+ return prot_ro;
+ case 7:
+ if (!arm_feature (env, ARM_FEATURE_V7))
+ return 0;
+ return prot_ro;
+ default:
+ abort();
+ }
+}
+
+static uint32_t get_level1_table_address(CPUState *env, uint32_t address)
+{
+ uint32_t table;
+
+ if (address & env->cp15.c2_mask)
+ table = env->cp15.c2_base1 & 0xffffc000;
+ else
+ table = env->cp15.c2_base0 & env->cp15.c2_base_mask;
+
+ table |= (address >> 18) & 0x3ffc;
+ return table;
+}
+
+static int get_phys_addr_v5(CPUState *env, uint32_t address, int access_type,
+ int is_user, uint32_t *phys_ptr, int *prot,
+ target_ulong *page_size)
+{
+ int code;
+ uint32_t table;
+ uint32_t desc;
+ int type;
+ int ap;
+ int domain;
+ uint32_t phys_addr;
+
+ /* Pagetable walk. */
+ /* Lookup l1 descriptor. */
+ table = get_level1_table_address(env, address);
+ desc = ldl_phys(table);
+ type = (desc & 3);
+ domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
+ if (type == 0) {
+ /* Section translation fault. */
+ code = 5;
+ goto do_fault;
+ }
+ if (domain == 0 || domain == 2) {
+ if (type == 2)
+ code = 9; /* Section domain fault. */
+ else
+ code = 11; /* Page domain fault. */
+ goto do_fault;
+ }
+ if (type == 2) {
+ /* 1Mb section. */
+ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
+ ap = (desc >> 10) & 3;
+ code = 13;
+ *page_size = 1024 * 1024;
+ } else {
+ /* Lookup l2 entry. */
+ if (type == 1) {
+ /* Coarse pagetable. */
+ table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
+ } else {
+ /* Fine pagetable. */
+ table = (desc & 0xfffff000) | ((address >> 8) & 0xffc);
+ }
+ desc = ldl_phys(table);
+ switch (desc & 3) {
+ case 0: /* Page translation fault. */
+ code = 7;
+ goto do_fault;
+ case 1: /* 64k page. */
+ phys_addr = (desc & 0xffff0000) | (address & 0xffff);
+ ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
+ *page_size = 0x10000;
+ break;
+ case 2: /* 4k page. */
+ phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+ ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
+ *page_size = 0x1000;
+ break;
+ case 3: /* 1k page. */
+ if (type == 1) {
+ if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+ } else {
+ /* Page translation fault. */
+ code = 7;
+ goto do_fault;
+ }
+ } else {
+ phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
+ }
+ ap = (desc >> 4) & 3;
+ *page_size = 0x400;
+ break;
+ default:
+ /* Never happens, but compiler isn't smart enough to tell. */
+ abort();
+ }
+ code = 15;
+ }
+ *prot = check_ap(env, ap, domain, access_type, is_user);
+ if (!*prot) {
+ /* Access permission fault. */
+ goto do_fault;
+ }
+ *prot |= PAGE_EXEC;
+ *phys_ptr = phys_addr;
+ return 0;
+do_fault:
+ return code | (domain << 4);
+}
+
+static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type,
+ int is_user, uint32_t *phys_ptr, int *prot,
+ target_ulong *page_size)
+{
+ int code;
+ uint32_t table;
+ uint32_t desc;
+ uint32_t xn;
+ int type;
+ int ap;
+ int domain;
+ uint32_t phys_addr;
+
+ /* Pagetable walk. */
+ /* Lookup l1 descriptor. */
+ table = get_level1_table_address(env, address);
+ desc = ldl_phys(table);
+ type = (desc & 3);
+ if (type == 0) {
+ /* Section translation fault. */
+ code = 5;
+ domain = 0;
+ goto do_fault;
+ } else if (type == 2 && (desc & (1 << 18))) {
+ /* Supersection. */
+ domain = 0;
+ } else {
+ /* Section or page. */
+ domain = (desc >> 4) & 0x1e;
+ }
+ domain = (env->cp15.c3 >> domain) & 3;
+ if (domain == 0 || domain == 2) {
+ if (type == 2)
+ code = 9; /* Section domain fault. */
+ else
+ code = 11; /* Page domain fault. */
+ goto do_fault;
+ }
+ if (type == 2) {
+ if (desc & (1 << 18)) {
+ /* Supersection. */
+ phys_addr = (desc & 0xff000000) | (address & 0x00ffffff);
+ *page_size = 0x1000000;
+ } else {
+ /* Section. */
+ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
+ *page_size = 0x100000;
+ }
+ ap = ((desc >> 10) & 3) | ((desc >> 13) & 4);
+ xn = desc & (1 << 4);
+ code = 13;
+ } else {
+ /* Lookup l2 entry. */
+ table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
+ desc = ldl_phys(table);
+ ap = ((desc >> 4) & 3) | ((desc >> 7) & 4);
+ switch (desc & 3) {
+ case 0: /* Page translation fault. */
+ code = 7;
+ goto do_fault;
+ case 1: /* 64k page. */
+ phys_addr = (desc & 0xffff0000) | (address & 0xffff);
+ xn = desc & (1 << 15);
+ *page_size = 0x10000;
+ break;
+ case 2: case 3: /* 4k page. */
+ phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+ xn = desc & 1;
+ *page_size = 0x1000;
+ break;
+ default:
+ /* Never happens, but compiler isn't smart enough to tell. */
+ abort();
+ }
+ code = 15;
+ }
+ if (domain == 3) {
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ } else {
+ if (xn && access_type == 2)
+ goto do_fault;
+
+ /* The simplified model uses AP[0] as an access control bit. */
+ if ((env->cp15.c1_sys & (1 << 29)) && (ap & 1) == 0) {
+ /* Access flag fault. */
+ code = (code == 15) ? 6 : 3;
+ goto do_fault;
+ }
+ *prot = check_ap(env, ap, domain, access_type, is_user);
+ if (!*prot) {
+ /* Access permission fault. */
+ goto do_fault;
+ }
+ if (!xn) {
+ *prot |= PAGE_EXEC;
+ }
+ }
+ *phys_ptr = phys_addr;
+ return 0;
+do_fault:
+ return code | (domain << 4);
+}
+
+static int get_phys_addr_mpu(CPUState *env, uint32_t address, int access_type,
+ int is_user, uint32_t *phys_ptr, int *prot)
+{
+ int n;
+ uint32_t mask;
+ uint32_t base;
+
+ *phys_ptr = address;
+ for (n = 7; n >= 0; n--) {
+ base = env->cp15.c6_region[n];
+ if ((base & 1) == 0)
+ continue;
+ mask = 1 << ((base >> 1) & 0x1f);
+ /* Keep this shift separate from the above to avoid an
+ (undefined) << 32. */
+ mask = (mask << 1) - 1;
+ if (((base ^ address) & ~mask) == 0)
+ break;
+ }
+ if (n < 0)
+ return 2;
+
+ if (access_type == 2) {
+ mask = env->cp15.c5_insn;
+ } else {
+ mask = env->cp15.c5_data;
+ }
+ mask = (mask >> (n * 4)) & 0xf;
+ switch (mask) {
+ case 0:
+ return 1;
+ case 1:
+ if (is_user)
+ return 1;
+ *prot = PAGE_READ | PAGE_WRITE;
+ break;
+ case 2:
+ *prot = PAGE_READ;
+ if (!is_user)
+ *prot |= PAGE_WRITE;
+ break;
+ case 3:
+ *prot = PAGE_READ | PAGE_WRITE;
+ break;
+ case 5:
+ if (is_user)
+ return 1;
+ *prot = PAGE_READ;
+ break;
+ case 6:
+ *prot = PAGE_READ;
+ break;
+ default:
+ /* Bad permission. */
+ return 1;
+ }
+ *prot |= PAGE_EXEC;
+ return 0;
+}
+
+static inline int get_phys_addr(CPUState *env, uint32_t address,
+ int access_type, int is_user,
+ uint32_t *phys_ptr, int *prot,
+ target_ulong *page_size)
+{
+ /* Fast Context Switch Extension. */
+ if (address < 0x02000000)
+ address += env->cp15.c13_fcse;
+
+ if ((env->cp15.c1_sys & 1) == 0) {
+ /* MMU/MPU disabled. */
+ *phys_ptr = address;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ *page_size = TARGET_PAGE_SIZE;
+ return 0;
+ } else if (arm_feature(env, ARM_FEATURE_MPU)) {
+ *page_size = TARGET_PAGE_SIZE;
+ return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr,
+ prot);
+ } else if (env->cp15.c1_sys & (1 << 23)) {
+ return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr,
+ prot, page_size);
+ } else {
+ return get_phys_addr_v5(env, address, access_type, is_user, phys_ptr,
+ prot, page_size);
+ }
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
+ int access_type, int mmu_idx, int is_softmmu)
+{
+ uint32_t phys_addr;
+ target_ulong page_size;
+ int prot;
+ int ret, is_user;
+
+ is_user = mmu_idx == MMU_USER_IDX;
+ ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot,
+ &page_size);
+ if (ret == 0) {
+ /* Map a single [sub]page. */
+ phys_addr &= ~(uint32_t)0x3ff;
+ address &= ~(uint32_t)0x3ff;
+ tlb_set_page (env, address, phys_addr, prot, mmu_idx, page_size);
+ return 0;
+ }
+
+ if (access_type == 2) {
+ env->cp15.c5_insn = ret;
+ env->cp15.c6_insn = address;
+ env->exception_index = EXCP_PREFETCH_ABORT;
+ } else {
+ env->cp15.c5_data = ret;
+ if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6))
+ env->cp15.c5_data |= (1 << 11);
+ env->cp15.c6_data = address;
+ env->exception_index = EXCP_DATA_ABORT;
+ }
+ return 1;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ uint32_t phys_addr;
+ target_ulong page_size;
+ int prot;
+ int ret;
+
+ ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot, &page_size);
+
+ if (ret != 0)
+ return -1;
+
+ return phys_addr;
+}
+
+void HELPER(set_cp)(CPUState *env, uint32_t insn, uint32_t val)
+{
+ int cp_num = (insn >> 8) & 0xf;
+ int cp_info = (insn >> 5) & 7;
+ int src = (insn >> 16) & 0xf;
+ int operand = insn & 0xf;
+
+ if (env->cp[cp_num].cp_write)
+ env->cp[cp_num].cp_write(env->cp[cp_num].opaque,
+ cp_info, src, operand, val);
+}
+
+uint32_t HELPER(get_cp)(CPUState *env, uint32_t insn)
+{
+ int cp_num = (insn >> 8) & 0xf;
+ int cp_info = (insn >> 5) & 7;
+ int dest = (insn >> 16) & 0xf;
+ int operand = insn & 0xf;
+
+ if (env->cp[cp_num].cp_read)
+ return env->cp[cp_num].cp_read(env->cp[cp_num].opaque,
+ cp_info, dest, operand);
+ return 0;
+}
+
+/* Return basic MPU access permission bits. */
+static uint32_t simple_mpu_ap_bits(uint32_t val)
+{
+ uint32_t ret;
+ uint32_t mask;
+ int i;
+ ret = 0;
+ mask = 3;
+ for (i = 0; i < 16; i += 2) {
+ ret |= (val >> i) & mask;
+ mask <<= 2;
+ }
+ return ret;
+}
+
+/* Pad basic MPU access permission bits to extended format. */
+static uint32_t extended_mpu_ap_bits(uint32_t val)
+{
+ uint32_t ret;
+ uint32_t mask;
+ int i;
+ ret = 0;
+ mask = 3;
+ for (i = 0; i < 16; i += 2) {
+ ret |= (val & mask) << i;
+ mask <<= 2;
+ }
+ return ret;
+}
+
+void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
+{
+ int op1;
+ int op2;
+ int crm;
+
+ op1 = (insn >> 21) & 7;
+ op2 = (insn >> 5) & 7;
+ crm = insn & 0xf;
+ switch ((insn >> 16) & 0xf) {
+ case 0:
+ /* ID codes. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE))
+ break;
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ break;
+ if (arm_feature(env, ARM_FEATURE_V7)
+ && op1 == 2 && crm == 0 && op2 == 0) {
+ env->cp15.c0_cssel = val & 0xf;
+ break;
+ }
+ goto bad_reg;
+ case 1: /* System configuration. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ op2 = 0;
+ switch (op2) {
+ case 0:
+ if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0)
+ env->cp15.c1_sys = val;
+ /* ??? Lots of these bits are not implemented. */
+ /* This may enable/disable the MMU, so do a TLB flush. */
+ tlb_flush(env, 1);
+ break;
+ case 1: /* Auxiliary cotrol register. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ env->cp15.c1_xscaleauxcr = val;
+ break;
+ }
+ /* Not implemented. */
+ break;
+ case 2:
+ if (arm_feature(env, ARM_FEATURE_XSCALE))
+ goto bad_reg;
+ if (env->cp15.c1_coproc != val) {
+ env->cp15.c1_coproc = val;
+ /* ??? Is this safe when called from within a TB? */
+ tb_flush(env);
+ }
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 2: /* MMU Page table control / MPU cache control. */
+ if (arm_feature(env, ARM_FEATURE_MPU)) {
+ switch (op2) {
+ case 0:
+ env->cp15.c2_data = val;
+ break;
+ case 1:
+ env->cp15.c2_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ } else {
+ switch (op2) {
+ case 0:
+ env->cp15.c2_base0 = val;
+ break;
+ case 1:
+ env->cp15.c2_base1 = val;
+ break;
+ case 2:
+ val &= 7;
+ env->cp15.c2_control = val;
+ env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val);
+ env->cp15.c2_base_mask = ~((uint32_t)0x3fffu >> val);
+ break;
+ default:
+ goto bad_reg;
+ }
+ }
+ break;
+ case 3: /* MMU Domain access control / MPU write buffer control. */
+ env->cp15.c3 = val;
+ tlb_flush(env, 1); /* Flush TLB as domain not tracked in TLB */
+ break;
+ case 4: /* Reserved. */
+ goto bad_reg;
+ case 5: /* MMU Fault status / MPU access permission. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ op2 = 0;
+ switch (op2) {
+ case 0:
+ if (arm_feature(env, ARM_FEATURE_MPU))
+ val = extended_mpu_ap_bits(val);
+ env->cp15.c5_data = val;
+ break;
+ case 1:
+ if (arm_feature(env, ARM_FEATURE_MPU))
+ val = extended_mpu_ap_bits(val);
+ env->cp15.c5_insn = val;
+ break;
+ case 2:
+ if (!arm_feature(env, ARM_FEATURE_MPU))
+ goto bad_reg;
+ env->cp15.c5_data = val;
+ break;
+ case 3:
+ if (!arm_feature(env, ARM_FEATURE_MPU))
+ goto bad_reg;
+ env->cp15.c5_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 6: /* MMU Fault address / MPU base/size. */
+ if (arm_feature(env, ARM_FEATURE_MPU)) {
+ if (crm >= 8)
+ goto bad_reg;
+ env->cp15.c6_region[crm] = val;
+ } else {
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ op2 = 0;
+ switch (op2) {
+ case 0:
+ env->cp15.c6_data = val;
+ break;
+ case 1: /* ??? This is WFAR on armv6 */
+ case 2:
+ env->cp15.c6_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ }
+ break;
+ case 7: /* Cache control. */
+ env->cp15.c15_i_max = 0x000;
+ env->cp15.c15_i_min = 0xff0;
+ /* No cache, so nothing to do. */
+ /* ??? MPCore has VA to PA translation functions. */
+ break;
+ case 8: /* MMU TLB control. */
+ switch (op2) {
+ case 0: /* Invalidate all. */
+ tlb_flush(env, 0);
+ break;
+ case 1: /* Invalidate single TLB entry. */
+ tlb_flush_page(env, val & TARGET_PAGE_MASK);
+ break;
+ case 2: /* Invalidate on ASID. */
+ tlb_flush(env, val == 0);
+ break;
+ case 3: /* Invalidate single entry on MVA. */
+ /* ??? This is like case 1, but ignores ASID. */
+ tlb_flush(env, 1);
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 9:
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ break;
+ switch (crm) {
+ case 0: /* Cache lockdown. */
+ switch (op1) {
+ case 0: /* L1 cache. */
+ switch (op2) {
+ case 0:
+ env->cp15.c9_data = val;
+ break;
+ case 1:
+ env->cp15.c9_insn = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 1: /* L2 cache. */
+ /* Ignore writes to L2 lockdown/auxiliary registers. */
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 1: /* TCM memory region registers. */
+ /* Not implemented. */
+ goto bad_reg;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 10: /* MMU TLB lockdown. */
+ /* ??? TLB lockdown not implemented. */
+ break;
+ case 12: /* Reserved. */
+ goto bad_reg;
+ case 13: /* Process ID. */
+ switch (op2) {
+ case 0:
+ /* Unlike real hardware the qemu TLB uses virtual addresses,
+ not modified virtual addresses, so this causes a TLB flush.
+ */
+ if (env->cp15.c13_fcse != val)
+ tlb_flush(env, 1);
+ env->cp15.c13_fcse = val;
+ break;
+ case 1:
+ /* This changes the ASID, so do a TLB flush. */
+ if (env->cp15.c13_context != val
+ && !arm_feature(env, ARM_FEATURE_MPU))
+ tlb_flush(env, 0);
+ env->cp15.c13_context = val;
+ break;
+ default:
+ goto bad_reg;
+ }
+ break;
+ case 14: /* Reserved. */
+ goto bad_reg;
+ case 15: /* Implementation specific. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ if (op2 == 0 && crm == 1) {
+ if (env->cp15.c15_cpar != (val & 0x3fff)) {
+ /* Changes cp0 to cp13 behavior, so needs a TB flush. */
+ tb_flush(env);
+ env->cp15.c15_cpar = val & 0x3fff;
+ }
+ break;
+ }
+ goto bad_reg;
+ }
+ if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+ switch (crm) {
+ case 0:
+ break;
+ case 1: /* Set TI925T configuration. */
+ env->cp15.c15_ticonfig = val & 0xe7;
+ env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */
+ ARM_CPUID_TI915T : ARM_CPUID_TI925T;
+ break;
+ case 2: /* Set I_max. */
+ env->cp15.c15_i_max = val;
+ break;
+ case 3: /* Set I_min. */
+ env->cp15.c15_i_min = val;
+ break;
+ case 4: /* Set thread-ID. */
+ env->cp15.c15_threadid = val & 0xffff;
+ break;
+ case 8: /* Wait-for-interrupt (deprecated). */
+ cpu_interrupt(env, CPU_INTERRUPT_HALT);
+ break;
+ default:
+ goto bad_reg;
+ }
+ }
+ break;
+ }
+ return;
+bad_reg:
+ /* ??? For debugging only. Should raise illegal instruction exception. */
+ cpu_abort(env, "Unimplemented cp15 register write (c%d, c%d, {%d, %d})\n",
+ (insn >> 16) & 0xf, crm, op1, op2);
+}
+
+uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
+{
+ int op1;
+ int op2;
+ int crm;
+
+ op1 = (insn >> 21) & 7;
+ op2 = (insn >> 5) & 7;
+ crm = insn & 0xf;
+ switch ((insn >> 16) & 0xf) {
+ case 0: /* ID codes. */
+ switch (op1) {
+ case 0:
+ switch (crm) {
+ case 0:
+ switch (op2) {
+ case 0: /* Device ID. */
+ return env->cp15.c0_cpuid;
+ case 1: /* Cache Type. */
+ return env->cp15.c0_cachetype;
+ case 2: /* TCM status. */
+ return 0;
+ case 3: /* TLB type register. */
+ return 0; /* No lockable TLB entries. */
+ case 5: /* CPU ID */
+ if (ARM_CPUID(env) == ARM_CPUID_CORTEXA9) {
+ return env->cpu_index | 0x80000900;
+ } else {
+ return env->cpu_index;
+ }
+ default:
+ goto bad_reg;
+ }
+ case 1:
+ if (!arm_feature(env, ARM_FEATURE_V6))
+ goto bad_reg;
+ return env->cp15.c0_c1[op2];
+ case 2:
+ if (!arm_feature(env, ARM_FEATURE_V6))
+ goto bad_reg;
+ return env->cp15.c0_c2[op2];
+ case 3: case 4: case 5: case 6: case 7:
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ case 1:
+ /* These registers aren't documented on arm11 cores. However
+ Linux looks at them anyway. */
+ if (!arm_feature(env, ARM_FEATURE_V6))
+ goto bad_reg;
+ if (crm != 0)
+ goto bad_reg;
+ if (!arm_feature(env, ARM_FEATURE_V7))
+ return 0;
+
+ switch (op2) {
+ case 0:
+ return env->cp15.c0_ccsid[env->cp15.c0_cssel];
+ case 1:
+ return env->cp15.c0_clid;
+ case 7:
+ return 0;
+ }
+ goto bad_reg;
+ case 2:
+ if (op2 != 0 || crm != 0)
+ goto bad_reg;
+ return env->cp15.c0_cssel;
+ default:
+ goto bad_reg;
+ }
+ case 1: /* System configuration. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ op2 = 0;
+ switch (op2) {
+ case 0: /* Control register. */
+ return env->cp15.c1_sys;
+ case 1: /* Auxiliary control register. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE))
+ return env->cp15.c1_xscaleauxcr;
+ if (!arm_feature(env, ARM_FEATURE_AUXCR))
+ goto bad_reg;
+ switch (ARM_CPUID(env)) {
+ case ARM_CPUID_ARM1026:
+ return 1;
+ case ARM_CPUID_ARM1136:
+ case ARM_CPUID_ARM1136_R2:
+ return 7;
+ case ARM_CPUID_ARM11MPCORE:
+ return 1;
+ case ARM_CPUID_CORTEXA8:
+ return 2;
+ case ARM_CPUID_CORTEXA9:
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ case 2: /* Coprocessor access register. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE))
+ goto bad_reg;
+ return env->cp15.c1_coproc;
+ default:
+ goto bad_reg;
+ }
+ case 2: /* MMU Page table control / MPU cache control. */
+ if (arm_feature(env, ARM_FEATURE_MPU)) {
+ switch (op2) {
+ case 0:
+ return env->cp15.c2_data;
+ break;
+ case 1:
+ return env->cp15.c2_insn;
+ break;
+ default:
+ goto bad_reg;
+ }
+ } else {
+ switch (op2) {
+ case 0:
+ return env->cp15.c2_base0;
+ case 1:
+ return env->cp15.c2_base1;
+ case 2:
+ return env->cp15.c2_control;
+ default:
+ goto bad_reg;
+ }
+ }
+ case 3: /* MMU Domain access control / MPU write buffer control. */
+ return env->cp15.c3;
+ case 4: /* Reserved. */
+ goto bad_reg;
+ case 5: /* MMU Fault status / MPU access permission. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ op2 = 0;
+ switch (op2) {
+ case 0:
+ if (arm_feature(env, ARM_FEATURE_MPU))
+ return simple_mpu_ap_bits(env->cp15.c5_data);
+ return env->cp15.c5_data;
+ case 1:
+ if (arm_feature(env, ARM_FEATURE_MPU))
+ return simple_mpu_ap_bits(env->cp15.c5_data);
+ return env->cp15.c5_insn;
+ case 2:
+ if (!arm_feature(env, ARM_FEATURE_MPU))
+ goto bad_reg;
+ return env->cp15.c5_data;
+ case 3:
+ if (!arm_feature(env, ARM_FEATURE_MPU))
+ goto bad_reg;
+ return env->cp15.c5_insn;
+ default:
+ goto bad_reg;
+ }
+ case 6: /* MMU Fault address. */
+ if (arm_feature(env, ARM_FEATURE_MPU)) {
+ if (crm >= 8)
+ goto bad_reg;
+ return env->cp15.c6_region[crm];
+ } else {
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ op2 = 0;
+ switch (op2) {
+ case 0:
+ return env->cp15.c6_data;
+ case 1:
+ if (arm_feature(env, ARM_FEATURE_V6)) {
+ /* Watchpoint Fault Adrress. */
+ return 0; /* Not implemented. */
+ } else {
+ /* Instruction Fault Adrress. */
+ /* Arm9 doesn't have an IFAR, but implementing it anyway
+ shouldn't do any harm. */
+ return env->cp15.c6_insn;
+ }
+ case 2:
+ if (arm_feature(env, ARM_FEATURE_V6)) {
+ /* Instruction Fault Adrress. */
+ return env->cp15.c6_insn;
+ } else {
+ goto bad_reg;
+ }
+ default:
+ goto bad_reg;
+ }
+ }
+ case 7: /* Cache control. */
+ /* FIXME: Should only clear Z flag if destination is r15. */
+ env->ZF = 0;
+ return 0;
+ case 8: /* MMU TLB control. */
+ goto bad_reg;
+ case 9: /* Cache lockdown. */
+ switch (op1) {
+ case 0: /* L1 cache. */
+ if (arm_feature(env, ARM_FEATURE_OMAPCP))
+ return 0;
+ switch (op2) {
+ case 0:
+ return env->cp15.c9_data;
+ case 1:
+ return env->cp15.c9_insn;
+ default:
+ goto bad_reg;
+ }
+ case 1: /* L2 cache */
+ if (crm != 0)
+ goto bad_reg;
+ /* L2 Lockdown and Auxiliary control. */
+ return 0;
+ default:
+ goto bad_reg;
+ }
+ case 10: /* MMU TLB lockdown. */
+ /* ??? TLB lockdown not implemented. */
+ return 0;
+ case 11: /* TCM DMA control. */
+ case 12: /* Reserved. */
+ goto bad_reg;
+ case 13: /* Process ID. */
+ switch (op2) {
+ case 0:
+ return env->cp15.c13_fcse;
+ case 1:
+ return env->cp15.c13_context;
+ default:
+ goto bad_reg;
+ }
+ case 14: /* Reserved. */
+ goto bad_reg;
+ case 15: /* Implementation specific. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ if (op2 == 0 && crm == 1)
+ return env->cp15.c15_cpar;
+
+ goto bad_reg;
+ }
+ if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+ switch (crm) {
+ case 0:
+ return 0;
+ case 1: /* Read TI925T configuration. */
+ return env->cp15.c15_ticonfig;
+ case 2: /* Read I_max. */
+ return env->cp15.c15_i_max;
+ case 3: /* Read I_min. */
+ return env->cp15.c15_i_min;
+ case 4: /* Read thread-ID. */
+ return env->cp15.c15_threadid;
+ case 8: /* TI925T_status */
+ return 0;
+ }
+ /* TODO: Peripheral port remap register:
+ * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt
+ * controller base address at $rn & ~0xfff and map size of
+ * 0x200 << ($rn & 0xfff), when MMU is off. */
+ goto bad_reg;
+ }
+ return 0;
+ }
+bad_reg:
+ /* ??? For debugging only. Should raise illegal instruction exception. */
+ cpu_abort(env, "Unimplemented cp15 register read (c%d, c%d, {%d, %d})\n",
+ (insn >> 16) & 0xf, crm, op1, op2);
+ return 0;
+}
+
+void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val)
+{
+ if ((env->uncached_cpsr & CPSR_M) == mode) {
+ env->regs[13] = val;
+ } else {
+ env->banked_r13[bank_number(mode)] = val;
+ }
+}
+
+uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode)
+{
+ if ((env->uncached_cpsr & CPSR_M) == mode) {
+ return env->regs[13];
+ } else {
+ return env->banked_r13[bank_number(mode)];
+ }
+}
+
+uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg)
+{
+ switch (reg) {
+ case 0: /* APSR */
+ return xpsr_read(env) & 0xf8000000;
+ case 1: /* IAPSR */
+ return xpsr_read(env) & 0xf80001ff;
+ case 2: /* EAPSR */
+ return xpsr_read(env) & 0xff00fc00;
+ case 3: /* xPSR */
+ return xpsr_read(env) & 0xff00fdff;
+ case 5: /* IPSR */
+ return xpsr_read(env) & 0x000001ff;
+ case 6: /* EPSR */
+ return xpsr_read(env) & 0x0700fc00;
+ case 7: /* IEPSR */
+ return xpsr_read(env) & 0x0700edff;
+ case 8: /* MSP */
+ return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13];
+ case 9: /* PSP */
+ return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
+ case 16: /* PRIMASK */
+ return (env->uncached_cpsr & CPSR_I) != 0;
+ case 17: /* FAULTMASK */
+ return (env->uncached_cpsr & CPSR_F) != 0;
+ case 18: /* BASEPRI */
+ case 19: /* BASEPRI_MAX */
+ return env->v7m.basepri;
+ case 20: /* CONTROL */
+ return env->v7m.control;
+ default:
+ /* ??? For debugging only. */
+ cpu_abort(env, "Unimplemented system register read (%d)\n", reg);
+ return 0;
+ }
+}
+
+void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val)
+{
+ switch (reg) {
+ case 0: /* APSR */
+ xpsr_write(env, val, 0xf8000000);
+ break;
+ case 1: /* IAPSR */
+ xpsr_write(env, val, 0xf8000000);
+ break;
+ case 2: /* EAPSR */
+ xpsr_write(env, val, 0xfe00fc00);
+ break;
+ case 3: /* xPSR */
+ xpsr_write(env, val, 0xfe00fc00);
+ break;
+ case 5: /* IPSR */
+ /* IPSR bits are readonly. */
+ break;
+ case 6: /* EPSR */
+ xpsr_write(env, val, 0x0600fc00);
+ break;
+ case 7: /* IEPSR */
+ xpsr_write(env, val, 0x0600fc00);
+ break;
+ case 8: /* MSP */
+ if (env->v7m.current_sp)
+ env->v7m.other_sp = val;
+ else
+ env->regs[13] = val;
+ break;
+ case 9: /* PSP */
+ if (env->v7m.current_sp)
+ env->regs[13] = val;
+ else
+ env->v7m.other_sp = val;
+ break;
+ case 16: /* PRIMASK */
+ if (val & 1)
+ env->uncached_cpsr |= CPSR_I;
+ else
+ env->uncached_cpsr &= ~CPSR_I;
+ break;
+ case 17: /* FAULTMASK */
+ if (val & 1)
+ env->uncached_cpsr |= CPSR_F;
+ else
+ env->uncached_cpsr &= ~CPSR_F;
+ break;
+ case 18: /* BASEPRI */
+ env->v7m.basepri = val & 0xff;
+ break;
+ case 19: /* BASEPRI_MAX */
+ val &= 0xff;
+ if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0))
+ env->v7m.basepri = val;
+ break;
+ case 20: /* CONTROL */
+ env->v7m.control = val & 3;
+ switch_v7m_sp(env, (val & 2) != 0);
+ break;
+ default:
+ /* ??? For debugging only. */
+ cpu_abort(env, "Unimplemented system register write (%d)\n", reg);
+ return;
+ }
+}
+
+void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+ ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
+ void *opaque)
+{
+ if (cpnum < 0 || cpnum > 14) {
+ cpu_abort(env, "Bad coprocessor number: %i\n", cpnum);
+ return;
+ }
+
+ env->cp[cpnum].cp_read = cp_read;
+ env->cp[cpnum].cp_write = cp_write;
+ env->cp[cpnum].opaque = opaque;
+}
+
+#endif
+
+/* Note that signed overflow is undefined in C. The following routines are
+ careful to use unsigned types where modulo arithmetic is required.
+ Failure to do so _will_ break on newer gcc. */
+
+/* Signed saturating arithmetic. */
+
+/* Perform 16-bit signed saturating addition. */
+static inline uint16_t add16_sat(uint16_t a, uint16_t b)
+{
+ uint16_t res;
+
+ res = a + b;
+ if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) {
+ if (a & 0x8000)
+ res = 0x8000;
+ else
+ res = 0x7fff;
+ }
+ return res;
+}
+
+/* Perform 8-bit signed saturating addition. */
+static inline uint8_t add8_sat(uint8_t a, uint8_t b)
+{
+ uint8_t res;
+
+ res = a + b;
+ if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) {
+ if (a & 0x80)
+ res = 0x80;
+ else
+ res = 0x7f;
+ }
+ return res;
+}
+
+/* Perform 16-bit signed saturating subtraction. */
+static inline uint16_t sub16_sat(uint16_t a, uint16_t b)
+{
+ uint16_t res;
+
+ res = a - b;
+ if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) {
+ if (a & 0x8000)
+ res = 0x8000;
+ else
+ res = 0x7fff;
+ }
+ return res;
+}
+
+/* Perform 8-bit signed saturating subtraction. */
+static inline uint8_t sub8_sat(uint8_t a, uint8_t b)
+{
+ uint8_t res;
+
+ res = a - b;
+ if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) {
+ if (a & 0x80)
+ res = 0x80;
+ else
+ res = 0x7f;
+ }
+ return res;
+}
+
+#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16);
+#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16);
+#define ADD8(a, b, n) RESULT(add8_sat(a, b), n, 8);
+#define SUB8(a, b, n) RESULT(sub8_sat(a, b), n, 8);
+#define PFX q
+
+#include "op_addsub.h"
+
+/* Unsigned saturating arithmetic. */
+static inline uint16_t add16_usat(uint16_t a, uint16_t b)
+{
+ uint16_t res;
+ res = a + b;
+ if (res < a)
+ res = 0xffff;
+ return res;
+}
+
+static inline uint16_t sub16_usat(uint16_t a, uint16_t b)
+{
+ if (a > b)
+ return a - b;
+ else
+ return 0;
+}
+
+static inline uint8_t add8_usat(uint8_t a, uint8_t b)
+{
+ uint8_t res;
+ res = a + b;
+ if (res < a)
+ res = 0xff;
+ return res;
+}
+
+static inline uint8_t sub8_usat(uint8_t a, uint8_t b)
+{
+ if (a > b)
+ return a - b;
+ else
+ return 0;
+}
+
+#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16);
+#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16);
+#define ADD8(a, b, n) RESULT(add8_usat(a, b), n, 8);
+#define SUB8(a, b, n) RESULT(sub8_usat(a, b), n, 8);
+#define PFX uq
+
+#include "op_addsub.h"
+
+/* Signed modulo arithmetic. */
+#define SARITH16(a, b, n, op) do { \
+ int32_t sum; \
+ sum = (int16_t)((uint16_t)(a) op (uint16_t)(b)); \
+ RESULT(sum, n, 16); \
+ if (sum >= 0) \
+ ge |= 3 << (n * 2); \
+ } while(0)
+
+#define SARITH8(a, b, n, op) do { \
+ int32_t sum; \
+ sum = (int8_t)((uint8_t)(a) op (uint8_t)(b)); \
+ RESULT(sum, n, 8); \
+ if (sum >= 0) \
+ ge |= 1 << n; \
+ } while(0)
+
+
+#define ADD16(a, b, n) SARITH16(a, b, n, +)
+#define SUB16(a, b, n) SARITH16(a, b, n, -)
+#define ADD8(a, b, n) SARITH8(a, b, n, +)
+#define SUB8(a, b, n) SARITH8(a, b, n, -)
+#define PFX s
+#define ARITH_GE
+
+#include "op_addsub.h"
+
+/* Unsigned modulo arithmetic. */
+#define ADD16(a, b, n) do { \
+ uint32_t sum; \
+ sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \
+ RESULT(sum, n, 16); \
+ if ((sum >> 16) == 1) \
+ ge |= 3 << (n * 2); \
+ } while(0)
+
+#define ADD8(a, b, n) do { \
+ uint32_t sum; \
+ sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \
+ RESULT(sum, n, 8); \
+ if ((sum >> 8) == 1) \
+ ge |= 1 << n; \
+ } while(0)
+
+#define SUB16(a, b, n) do { \
+ uint32_t sum; \
+ sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \
+ RESULT(sum, n, 16); \
+ if ((sum >> 16) == 0) \
+ ge |= 3 << (n * 2); \
+ } while(0)
+
+#define SUB8(a, b, n) do { \
+ uint32_t sum; \
+ sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \
+ RESULT(sum, n, 8); \
+ if ((sum >> 8) == 0) \
+ ge |= 1 << n; \
+ } while(0)
+
+#define PFX u
+#define ARITH_GE
+
+#include "op_addsub.h"
+
+/* Halved signed arithmetic. */
+#define ADD16(a, b, n) \
+ RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16)
+#define SUB16(a, b, n) \
+ RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16)
+#define ADD8(a, b, n) \
+ RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8)
+#define SUB8(a, b, n) \
+ RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8)
+#define PFX sh
+
+#include "op_addsub.h"
+
+/* Halved unsigned arithmetic. */
+#define ADD16(a, b, n) \
+ RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16)
+#define SUB16(a, b, n) \
+ RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16)
+#define ADD8(a, b, n) \
+ RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8)
+#define SUB8(a, b, n) \
+ RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8)
+#define PFX uh
+
+#include "op_addsub.h"
+
+static inline uint8_t do_usad(uint8_t a, uint8_t b)
+{
+ if (a > b)
+ return a - b;
+ else
+ return b - a;
+}
+
+/* Unsigned sum of absolute byte differences. */
+uint32_t HELPER(usad8)(uint32_t a, uint32_t b)
+{
+ uint32_t sum;
+ sum = do_usad(a, b);
+ sum += do_usad(a >> 8, b >> 8);
+ sum += do_usad(a >> 16, b >>16);
+ sum += do_usad(a >> 24, b >> 24);
+ return sum;
+}
+
+/* For ARMv6 SEL instruction. */
+uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b)
+{
+ uint32_t mask;
+
+ mask = 0;
+ if (flags & 1)
+ mask |= 0xff;
+ if (flags & 2)
+ mask |= 0xff00;
+ if (flags & 4)
+ mask |= 0xff0000;
+ if (flags & 8)
+ mask |= 0xff000000;
+ return (a & mask) | (b & ~mask);
+}
+
+uint32_t HELPER(logicq_cc)(uint64_t val)
+{
+ return (val >> 32) | (val != 0);
+}
+
+/* VFP support. We follow the convention used for VFP instrunctions:
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix. */
+
+/* Convert host exception flags to vfp form. */
+static inline int vfp_exceptbits_from_host(int host_bits)
+{
+ int target_bits = 0;
+
+ if (host_bits & float_flag_invalid)
+ target_bits |= 1;
+ if (host_bits & float_flag_divbyzero)
+ target_bits |= 2;
+ if (host_bits & float_flag_overflow)
+ target_bits |= 4;
+ if (host_bits & float_flag_underflow)
+ target_bits |= 8;
+ if (host_bits & float_flag_inexact)
+ target_bits |= 0x10;
+ if (host_bits & float_flag_input_denormal)
+ target_bits |= 0x80;
+ return target_bits;
+}
+
+uint32_t HELPER(vfp_get_fpscr)(CPUState *env)
+{
+ int i;
+ uint32_t fpscr;
+
+ fpscr = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff)
+ | (env->vfp.vec_len << 16)
+ | (env->vfp.vec_stride << 20);
+ i = get_float_exception_flags(&env->vfp.fp_status);
+ i |= get_float_exception_flags(&env->vfp.standard_fp_status);
+ fpscr |= vfp_exceptbits_from_host(i);
+ return fpscr;
+}
+
+uint32_t vfp_get_fpscr(CPUState *env)
+{
+ return HELPER(vfp_get_fpscr)(env);
+}
+
+/* Convert vfp exception flags to target form. */
+static inline int vfp_exceptbits_to_host(int target_bits)
+{
+ int host_bits = 0;
+
+ if (target_bits & 1)
+ host_bits |= float_flag_invalid;
+ if (target_bits & 2)
+ host_bits |= float_flag_divbyzero;
+ if (target_bits & 4)
+ host_bits |= float_flag_overflow;
+ if (target_bits & 8)
+ host_bits |= float_flag_underflow;
+ if (target_bits & 0x10)
+ host_bits |= float_flag_inexact;
+ if (target_bits & 0x80)
+ host_bits |= float_flag_input_denormal;
+ return host_bits;
+}
+
+void HELPER(vfp_set_fpscr)(CPUState *env, uint32_t val)
+{
+ int i;
+ uint32_t changed;
+
+ changed = env->vfp.xregs[ARM_VFP_FPSCR];
+ env->vfp.xregs[ARM_VFP_FPSCR] = (val & 0xffc8ffff);
+ env->vfp.vec_len = (val >> 16) & 7;
+ env->vfp.vec_stride = (val >> 20) & 3;
+
+ changed ^= val;
+ if (changed & (3 << 22)) {
+ i = (val >> 22) & 3;
+ switch (i) {
+ case 0:
+ i = float_round_nearest_even;
+ break;
+ case 1:
+ i = float_round_up;
+ break;
+ case 2:
+ i = float_round_down;
+ break;
+ case 3:
+ i = float_round_to_zero;
+ break;
+ }
+ set_float_rounding_mode(i, &env->vfp.fp_status);
+ }
+ if (changed & (1 << 24)) {
+ set_flush_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status);
+ set_flush_inputs_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status);
+ }
+ if (changed & (1 << 25))
+ set_default_nan_mode((val & (1 << 25)) != 0, &env->vfp.fp_status);
+
+ i = vfp_exceptbits_to_host(val);
+ set_float_exception_flags(i, &env->vfp.fp_status);
+ set_float_exception_flags(0, &env->vfp.standard_fp_status);
+}
+
+void vfp_set_fpscr(CPUState *env, uint32_t val)
+{
+ HELPER(vfp_set_fpscr)(env, val);
+}
+
+#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p))
+
+#define VFP_BINOP(name) \
+float32 VFP_HELPER(name, s)(float32 a, float32 b, CPUState *env) \
+{ \
+ return float32_ ## name (a, b, &env->vfp.fp_status); \
+} \
+float64 VFP_HELPER(name, d)(float64 a, float64 b, CPUState *env) \
+{ \
+ return float64_ ## name (a, b, &env->vfp.fp_status); \
+}
+VFP_BINOP(add)
+VFP_BINOP(sub)
+VFP_BINOP(mul)
+VFP_BINOP(div)
+#undef VFP_BINOP
+
+float32 VFP_HELPER(neg, s)(float32 a)
+{
+ return float32_chs(a);
+}
+
+float64 VFP_HELPER(neg, d)(float64 a)
+{
+ return float64_chs(a);
+}
+
+float32 VFP_HELPER(abs, s)(float32 a)
+{
+ return float32_abs(a);
+}
+
+float64 VFP_HELPER(abs, d)(float64 a)
+{
+ return float64_abs(a);
+}
+
+float32 VFP_HELPER(sqrt, s)(float32 a, CPUState *env)
+{
+ return float32_sqrt(a, &env->vfp.fp_status);
+}
+
+float64 VFP_HELPER(sqrt, d)(float64 a, CPUState *env)
+{
+ return float64_sqrt(a, &env->vfp.fp_status);
+}
+
+/* XXX: check quiet/signaling case */
+#define DO_VFP_cmp(p, type) \
+void VFP_HELPER(cmp, p)(type a, type b, CPUState *env) \
+{ \
+ uint32_t flags; \
+ switch(type ## _compare_quiet(a, b, &env->vfp.fp_status)) { \
+ case 0: flags = 0x6; break; \
+ case -1: flags = 0x8; break; \
+ case 1: flags = 0x2; break; \
+ default: case 2: flags = 0x3; break; \
+ } \
+ env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28) \
+ | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
+} \
+void VFP_HELPER(cmpe, p)(type a, type b, CPUState *env) \
+{ \
+ uint32_t flags; \
+ switch(type ## _compare(a, b, &env->vfp.fp_status)) { \
+ case 0: flags = 0x6; break; \
+ case -1: flags = 0x8; break; \
+ case 1: flags = 0x2; break; \
+ default: case 2: flags = 0x3; break; \
+ } \
+ env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28) \
+ | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
+}
+DO_VFP_cmp(s, float32)
+DO_VFP_cmp(d, float64)
+#undef DO_VFP_cmp
+
+/* Helper routines to perform bitwise copies between float and int. */
+static inline float32 vfp_itos(uint32_t i)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+
+ v.i = i;
+ return v.s;
+}
+
+static inline uint32_t vfp_stoi(float32 s)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+
+ v.s = s;
+ return v.i;
+}
+
+static inline float64 vfp_itod(uint64_t i)
+{
+ union {
+ uint64_t i;
+ float64 d;
+ } v;
+
+ v.i = i;
+ return v.d;
+}
+
+static inline uint64_t vfp_dtoi(float64 d)
+{
+ union {
+ uint64_t i;
+ float64 d;
+ } v;
+
+ v.d = d;
+ return v.i;
+}
+
+/* Integer to float conversion. */
+float32 VFP_HELPER(uito, s)(float32 x, CPUState *env)
+{
+ return uint32_to_float32(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+float64 VFP_HELPER(uito, d)(float32 x, CPUState *env)
+{
+ return uint32_to_float64(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+float32 VFP_HELPER(sito, s)(float32 x, CPUState *env)
+{
+ return int32_to_float32(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+float64 VFP_HELPER(sito, d)(float32 x, CPUState *env)
+{
+ return int32_to_float64(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+/* Float to integer conversion. */
+float32 VFP_HELPER(toui, s)(float32 x, CPUState *env)
+{
+ if (float32_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float32_to_uint32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(toui, d)(float64 x, CPUState *env)
+{
+ if (float64_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float64_to_uint32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosi, s)(float32 x, CPUState *env)
+{
+ if (float32_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float32_to_int32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosi, d)(float64 x, CPUState *env)
+{
+ if (float64_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float64_to_int32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(touiz, s)(float32 x, CPUState *env)
+{
+ if (float32_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float32_to_uint32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(touiz, d)(float64 x, CPUState *env)
+{
+ if (float64_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float64_to_uint32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosiz, s)(float32 x, CPUState *env)
+{
+ if (float32_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float32_to_int32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosiz, d)(float64 x, CPUState *env)
+{
+ if (float64_is_any_nan(x)) {
+ return float32_zero;
+ }
+ return vfp_itos(float64_to_int32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+/* floating point conversion */
+float64 VFP_HELPER(fcvtd, s)(float32 x, CPUState *env)
+{
+ float64 r = float32_to_float64(x, &env->vfp.fp_status);
+ /* ARM requires that S<->D conversion of any kind of NaN generates
+ * a quiet NaN by forcing the most significant frac bit to 1.
+ */
+ return float64_maybe_silence_nan(r);
+}
+
+float32 VFP_HELPER(fcvts, d)(float64 x, CPUState *env)
+{
+ float32 r = float64_to_float32(x, &env->vfp.fp_status);
+ /* ARM requires that S<->D conversion of any kind of NaN generates
+ * a quiet NaN by forcing the most significant frac bit to 1.
+ */
+ return float32_maybe_silence_nan(r);
+}
+
+/* VFP3 fixed point conversion. */
+#define VFP_CONV_FIX(name, p, ftype, itype, sign) \
+ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \
+{ \
+ ftype tmp; \
+ tmp = sign##int32_to_##ftype ((itype##_t)vfp_##p##toi(x), \
+ &env->vfp.fp_status); \
+ return ftype##_scalbn(tmp, -(int)shift, &env->vfp.fp_status); \
+} \
+ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \
+{ \
+ ftype tmp; \
+ if (ftype##_is_any_nan(x)) { \
+ return ftype##_zero; \
+ } \
+ tmp = ftype##_scalbn(x, shift, &env->vfp.fp_status); \
+ return vfp_ito##p(ftype##_to_##itype##_round_to_zero(tmp, \
+ &env->vfp.fp_status)); \
+}
+
+VFP_CONV_FIX(sh, d, float64, int16, )
+VFP_CONV_FIX(sl, d, float64, int32, )
+VFP_CONV_FIX(uh, d, float64, uint16, u)
+VFP_CONV_FIX(ul, d, float64, uint32, u)
+VFP_CONV_FIX(sh, s, float32, int16, )
+VFP_CONV_FIX(sl, s, float32, int32, )
+VFP_CONV_FIX(uh, s, float32, uint16, u)
+VFP_CONV_FIX(ul, s, float32, uint32, u)
+#undef VFP_CONV_FIX
+
+/* Half precision conversions. */
+float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0;
+ return float16_to_float32(a, ieee, s);
+}
+
+uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0;
+ return float32_to_float16(a, ieee, s);
+}
+
+float32 HELPER(recps_f32)(float32 a, float32 b, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ float32 two = int32_to_float32(2, s);
+ return float32_sub(two, float32_mul(a, b, s), s);
+}
+
+float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env)
+{
+ float_status *s = &env->vfp.standard_fp_status;
+ float32 two = int32_to_float32(2, s);
+ float32 three = int32_to_float32(3, s);
+ float32 product;
+ if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) ||
+ (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) {
+ product = float32_zero;
+ } else {
+ product = float32_mul(a, b, s);
+ }
+ return float32_div(float32_sub(three, product, s), two, s);
+}
+
+/* NEON helpers. */
+
+/* TODO: The architecture specifies the value that the estimate functions
+ should return. We return the exact reciprocal/root instead. */
+float32 HELPER(recpe_f32)(float32 a, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ float32 one = int32_to_float32(1, s);
+ return float32_div(one, a, s);
+}
+
+float32 HELPER(rsqrte_f32)(float32 a, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ float32 one = int32_to_float32(1, s);
+ return float32_div(one, float32_sqrt(a, s), s);
+}
+
+uint32_t HELPER(recpe_u32)(uint32_t a, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ float32 tmp;
+ tmp = int32_to_float32(a, s);
+ tmp = float32_scalbn(tmp, -32, s);
+ tmp = helper_recpe_f32(tmp, env);
+ tmp = float32_scalbn(tmp, 31, s);
+ return float32_to_int32(tmp, s);
+}
+
+uint32_t HELPER(rsqrte_u32)(uint32_t a, CPUState *env)
+{
+ float_status *s = &env->vfp.fp_status;
+ float32 tmp;
+ tmp = int32_to_float32(a, s);
+ tmp = float32_scalbn(tmp, -32, s);
+ tmp = helper_rsqrte_f32(tmp, env);
+ tmp = float32_scalbn(tmp, 31, s);
+ return float32_to_int32(tmp, s);
+}
+
+void HELPER(set_teecr)(CPUState *env, uint32_t val)
+{
+ val &= 1;
+ if (env->teecr != val) {
+ env->teecr = val;
+ tb_flush(env);
+ }
+}
diff --git a/target-arm/helpers.h b/target-arm/helpers.h
new file mode 100644
index 0000000..8a2564e
--- /dev/null
+++ b/target-arm/helpers.h
@@ -0,0 +1,458 @@
+#include "def-helper.h"
+
+DEF_HELPER_1(clz, i32, i32)
+DEF_HELPER_1(sxtb16, i32, i32)
+DEF_HELPER_1(uxtb16, i32, i32)
+
+DEF_HELPER_2(add_setq, i32, i32, i32)
+DEF_HELPER_2(add_saturate, i32, i32, i32)
+DEF_HELPER_2(sub_saturate, i32, i32, i32)
+DEF_HELPER_2(add_usaturate, i32, i32, i32)
+DEF_HELPER_2(sub_usaturate, i32, i32, i32)
+DEF_HELPER_1(double_saturate, i32, s32)
+DEF_HELPER_2(sdiv, s32, s32, s32)
+DEF_HELPER_2(udiv, i32, i32, i32)
+DEF_HELPER_1(rbit, i32, i32)
+DEF_HELPER_1(abs, i32, i32)
+
+#define PAS_OP(pfx) \
+ DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \
+ DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \
+ DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \
+ DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \
+ DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \
+ DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr)
+
+PAS_OP(s)
+PAS_OP(u)
+#undef PAS_OP
+
+#define PAS_OP(pfx) \
+ DEF_HELPER_2(pfx ## add8, i32, i32, i32) \
+ DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \
+ DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \
+ DEF_HELPER_2(pfx ## add16, i32, i32, i32) \
+ DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \
+ DEF_HELPER_2(pfx ## subaddx, i32, i32, i32)
+PAS_OP(q)
+PAS_OP(sh)
+PAS_OP(uq)
+PAS_OP(uh)
+#undef PAS_OP
+
+DEF_HELPER_2(ssat, i32, i32, i32)
+DEF_HELPER_2(usat, i32, i32, i32)
+DEF_HELPER_2(ssat16, i32, i32, i32)
+DEF_HELPER_2(usat16, i32, i32, i32)
+
+DEF_HELPER_2(usad8, i32, i32, i32)
+
+DEF_HELPER_1(logicq_cc, i32, i64)
+
+DEF_HELPER_3(sel_flags, i32, i32, i32, i32)
+DEF_HELPER_1(exception, void, i32)
+DEF_HELPER_0(wfi, void)
+
+DEF_HELPER_2(cpsr_write, void, i32, i32)
+DEF_HELPER_0(cpsr_read, i32)
+
+DEF_HELPER_3(v7m_msr, void, env, i32, i32)
+DEF_HELPER_2(v7m_mrs, i32, env, i32)
+
+DEF_HELPER_3(set_cp15, void, env, i32, i32)
+DEF_HELPER_2(get_cp15, i32, env, i32)
+
+DEF_HELPER_3(set_cp, void, env, i32, i32)
+DEF_HELPER_2(get_cp, i32, env, i32)
+
+DEF_HELPER_2(get_r13_banked, i32, env, i32)
+DEF_HELPER_3(set_r13_banked, void, env, i32, i32)
+
+DEF_HELPER_1(get_user_reg, i32, i32)
+DEF_HELPER_2(set_user_reg, void, i32, i32)
+
+DEF_HELPER_1(vfp_get_fpscr, i32, env)
+DEF_HELPER_2(vfp_set_fpscr, void, env, i32)
+
+DEF_HELPER_3(vfp_adds, f32, f32, f32, env)
+DEF_HELPER_3(vfp_addd, f64, f64, f64, env)
+DEF_HELPER_3(vfp_subs, f32, f32, f32, env)
+DEF_HELPER_3(vfp_subd, f64, f64, f64, env)
+DEF_HELPER_3(vfp_muls, f32, f32, f32, env)
+DEF_HELPER_3(vfp_muld, f64, f64, f64, env)
+DEF_HELPER_3(vfp_divs, f32, f32, f32, env)
+DEF_HELPER_3(vfp_divd, f64, f64, f64, env)
+DEF_HELPER_1(vfp_negs, f32, f32)
+DEF_HELPER_1(vfp_negd, f64, f64)
+DEF_HELPER_1(vfp_abss, f32, f32)
+DEF_HELPER_1(vfp_absd, f64, f64)
+DEF_HELPER_2(vfp_sqrts, f32, f32, env)
+DEF_HELPER_2(vfp_sqrtd, f64, f64, env)
+DEF_HELPER_3(vfp_cmps, void, f32, f32, env)
+DEF_HELPER_3(vfp_cmpd, void, f64, f64, env)
+DEF_HELPER_3(vfp_cmpes, void, f32, f32, env)
+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, f32, env)
+DEF_HELPER_2(vfp_uitod, f64, f32, env)
+DEF_HELPER_2(vfp_sitos, f32, f32, env)
+DEF_HELPER_2(vfp_sitod, f64, f32, env)
+
+DEF_HELPER_2(vfp_touis, f32, f32, env)
+DEF_HELPER_2(vfp_touid, f32, f64, env)
+DEF_HELPER_2(vfp_touizs, f32, f32, env)
+DEF_HELPER_2(vfp_touizd, f32, f64, env)
+DEF_HELPER_2(vfp_tosis, f32, f32, env)
+DEF_HELPER_2(vfp_tosid, f32, f64, env)
+DEF_HELPER_2(vfp_tosizs, f32, f32, env)
+DEF_HELPER_2(vfp_tosizd, f32, f64, env)
+
+DEF_HELPER_3(vfp_toshs, f32, f32, i32, env)
+DEF_HELPER_3(vfp_tosls, f32, f32, i32, env)
+DEF_HELPER_3(vfp_touhs, f32, f32, i32, env)
+DEF_HELPER_3(vfp_touls, f32, f32, i32, env)
+DEF_HELPER_3(vfp_toshd, f64, f64, i32, env)
+DEF_HELPER_3(vfp_tosld, f64, f64, i32, env)
+DEF_HELPER_3(vfp_touhd, f64, f64, i32, env)
+DEF_HELPER_3(vfp_tould, f64, f64, i32, env)
+DEF_HELPER_3(vfp_shtos, f32, f32, i32, env)
+DEF_HELPER_3(vfp_sltos, f32, f32, i32, env)
+DEF_HELPER_3(vfp_uhtos, f32, f32, i32, env)
+DEF_HELPER_3(vfp_ultos, f32, f32, i32, env)
+DEF_HELPER_3(vfp_shtod, f64, f64, i32, env)
+DEF_HELPER_3(vfp_sltod, f64, f64, i32, env)
+DEF_HELPER_3(vfp_uhtod, f64, f64, i32, env)
+DEF_HELPER_3(vfp_ultod, f64, f64, i32, env)
+
+DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env)
+DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env)
+
+DEF_HELPER_3(recps_f32, f32, f32, f32, env)
+DEF_HELPER_3(rsqrts_f32, f32, f32, f32, env)
+DEF_HELPER_2(recpe_f32, f32, f32, env)
+DEF_HELPER_2(rsqrte_f32, f32, f32, env)
+DEF_HELPER_2(recpe_u32, i32, i32, env)
+DEF_HELPER_2(rsqrte_u32, i32, i32, env)
+DEF_HELPER_4(neon_tbl, i32, i32, i32, i32, i32)
+
+DEF_HELPER_2(add_cc, i32, i32, i32)
+DEF_HELPER_2(adc_cc, i32, i32, i32)
+DEF_HELPER_2(sub_cc, i32, i32, i32)
+DEF_HELPER_2(sbc_cc, i32, i32, i32)
+
+DEF_HELPER_2(shl, i32, i32, i32)
+DEF_HELPER_2(shr, i32, i32, i32)
+DEF_HELPER_2(sar, i32, i32, i32)
+DEF_HELPER_2(shl_cc, i32, i32, i32)
+DEF_HELPER_2(shr_cc, i32, i32, i32)
+DEF_HELPER_2(sar_cc, i32, i32, i32)
+DEF_HELPER_2(ror_cc, i32, i32, i32)
+
+/* neon_helper.c */
+DEF_HELPER_3(neon_qadd_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64)
+
+DEF_HELPER_2(neon_hadd_s8, i32, i32, i32)
+DEF_HELPER_2(neon_hadd_u8, i32, i32, i32)
+DEF_HELPER_2(neon_hadd_s16, i32, i32, i32)
+DEF_HELPER_2(neon_hadd_u16, i32, i32, i32)
+DEF_HELPER_2(neon_hadd_s32, s32, s32, s32)
+DEF_HELPER_2(neon_hadd_u32, i32, i32, i32)
+DEF_HELPER_2(neon_rhadd_s8, i32, i32, i32)
+DEF_HELPER_2(neon_rhadd_u8, i32, i32, i32)
+DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32)
+DEF_HELPER_2(neon_rhadd_u16, i32, i32, i32)
+DEF_HELPER_2(neon_rhadd_s32, s32, s32, s32)
+DEF_HELPER_2(neon_rhadd_u32, i32, i32, i32)
+DEF_HELPER_2(neon_hsub_s8, i32, i32, i32)
+DEF_HELPER_2(neon_hsub_u8, i32, i32, i32)
+DEF_HELPER_2(neon_hsub_s16, i32, i32, i32)
+DEF_HELPER_2(neon_hsub_u16, i32, i32, i32)
+DEF_HELPER_2(neon_hsub_s32, s32, s32, s32)
+DEF_HELPER_2(neon_hsub_u32, i32, i32, i32)
+
+DEF_HELPER_2(neon_cgt_u8, i32, i32, i32)
+DEF_HELPER_2(neon_cgt_s8, i32, i32, i32)
+DEF_HELPER_2(neon_cgt_u16, i32, i32, i32)
+DEF_HELPER_2(neon_cgt_s16, i32, i32, i32)
+DEF_HELPER_2(neon_cgt_u32, i32, i32, i32)
+DEF_HELPER_2(neon_cgt_s32, i32, i32, i32)
+DEF_HELPER_2(neon_cge_u8, i32, i32, i32)
+DEF_HELPER_2(neon_cge_s8, i32, i32, i32)
+DEF_HELPER_2(neon_cge_u16, i32, i32, i32)
+DEF_HELPER_2(neon_cge_s16, i32, i32, i32)
+DEF_HELPER_2(neon_cge_u32, i32, i32, i32)
+DEF_HELPER_2(neon_cge_s32, i32, i32, i32)
+
+DEF_HELPER_2(neon_min_u8, i32, i32, i32)
+DEF_HELPER_2(neon_min_s8, i32, i32, i32)
+DEF_HELPER_2(neon_min_u16, i32, i32, i32)
+DEF_HELPER_2(neon_min_s16, i32, i32, i32)
+DEF_HELPER_2(neon_min_u32, i32, i32, i32)
+DEF_HELPER_2(neon_min_s32, i32, i32, i32)
+DEF_HELPER_2(neon_max_u8, i32, i32, i32)
+DEF_HELPER_2(neon_max_s8, i32, i32, i32)
+DEF_HELPER_2(neon_max_u16, i32, i32, i32)
+DEF_HELPER_2(neon_max_s16, i32, i32, i32)
+DEF_HELPER_2(neon_max_u32, i32, i32, i32)
+DEF_HELPER_2(neon_max_s32, i32, i32, i32)
+DEF_HELPER_2(neon_pmin_u8, i32, i32, i32)
+DEF_HELPER_2(neon_pmin_s8, i32, i32, i32)
+DEF_HELPER_2(neon_pmin_u16, i32, i32, i32)
+DEF_HELPER_2(neon_pmin_s16, i32, i32, i32)
+DEF_HELPER_2(neon_pmax_u8, i32, i32, i32)
+DEF_HELPER_2(neon_pmax_s8, i32, i32, i32)
+DEF_HELPER_2(neon_pmax_u16, i32, i32, i32)
+DEF_HELPER_2(neon_pmax_s16, i32, i32, i32)
+
+DEF_HELPER_2(neon_abd_u8, i32, i32, i32)
+DEF_HELPER_2(neon_abd_s8, i32, i32, i32)
+DEF_HELPER_2(neon_abd_u16, i32, i32, i32)
+DEF_HELPER_2(neon_abd_s16, i32, i32, i32)
+DEF_HELPER_2(neon_abd_u32, i32, i32, i32)
+DEF_HELPER_2(neon_abd_s32, i32, i32, i32)
+
+DEF_HELPER_2(neon_shl_u8, i32, i32, i32)
+DEF_HELPER_2(neon_shl_s8, i32, i32, i32)
+DEF_HELPER_2(neon_shl_u16, i32, i32, i32)
+DEF_HELPER_2(neon_shl_s16, i32, i32, i32)
+DEF_HELPER_2(neon_shl_u32, i32, i32, i32)
+DEF_HELPER_2(neon_shl_s32, i32, i32, i32)
+DEF_HELPER_2(neon_shl_u64, i64, i64, i64)
+DEF_HELPER_2(neon_shl_s64, i64, i64, i64)
+DEF_HELPER_2(neon_rshl_u8, i32, i32, i32)
+DEF_HELPER_2(neon_rshl_s8, i32, i32, i32)
+DEF_HELPER_2(neon_rshl_u16, i32, i32, i32)
+DEF_HELPER_2(neon_rshl_s16, i32, i32, i32)
+DEF_HELPER_2(neon_rshl_u32, i32, i32, i32)
+DEF_HELPER_2(neon_rshl_s32, i32, i32, i32)
+DEF_HELPER_2(neon_rshl_u64, i64, i64, i64)
+DEF_HELPER_2(neon_rshl_s64, i64, i64, i64)
+DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32);
+DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64);
+DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64)
+DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64)
+
+DEF_HELPER_2(neon_add_u8, i32, i32, i32)
+DEF_HELPER_2(neon_add_u16, i32, i32, i32)
+DEF_HELPER_2(neon_padd_u8, i32, i32, i32)
+DEF_HELPER_2(neon_padd_u16, i32, i32, i32)
+DEF_HELPER_2(neon_sub_u8, i32, i32, i32)
+DEF_HELPER_2(neon_sub_u16, i32, i32, i32)
+DEF_HELPER_2(neon_mul_u8, i32, i32, i32)
+DEF_HELPER_2(neon_mul_u16, i32, i32, i32)
+DEF_HELPER_2(neon_mul_p8, i32, i32, i32)
+
+DEF_HELPER_2(neon_tst_u8, i32, i32, i32)
+DEF_HELPER_2(neon_tst_u16, i32, i32, i32)
+DEF_HELPER_2(neon_tst_u32, i32, i32, i32)
+DEF_HELPER_2(neon_ceq_u8, i32, i32, i32)
+DEF_HELPER_2(neon_ceq_u16, i32, i32, i32)
+DEF_HELPER_2(neon_ceq_u32, i32, i32, i32)
+
+DEF_HELPER_1(neon_abs_s8, i32, i32)
+DEF_HELPER_1(neon_abs_s16, i32, i32)
+DEF_HELPER_1(neon_clz_u8, i32, i32)
+DEF_HELPER_1(neon_clz_u16, i32, i32)
+DEF_HELPER_1(neon_cls_s8, i32, i32)
+DEF_HELPER_1(neon_cls_s16, i32, i32)
+DEF_HELPER_1(neon_cls_s32, i32, i32)
+DEF_HELPER_1(neon_cnt_u8, i32, i32)
+
+DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32)
+DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32)
+DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32)
+
+DEF_HELPER_1(neon_narrow_u8, i32, i64)
+DEF_HELPER_1(neon_narrow_u16, i32, i64)
+DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64)
+DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64)
+DEF_HELPER_1(neon_narrow_high_u8, i32, i64)
+DEF_HELPER_1(neon_narrow_high_u16, i32, i64)
+DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64)
+DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64)
+DEF_HELPER_1(neon_widen_u8, i64, i32)
+DEF_HELPER_1(neon_widen_s8, i64, i32)
+DEF_HELPER_1(neon_widen_u16, i64, i32)
+DEF_HELPER_1(neon_widen_s16, i64, i32)
+
+DEF_HELPER_2(neon_addl_u16, i64, i64, i64)
+DEF_HELPER_2(neon_addl_u32, i64, i64, i64)
+DEF_HELPER_2(neon_paddl_u16, i64, i64, i64)
+DEF_HELPER_2(neon_paddl_u32, i64, i64, i64)
+DEF_HELPER_2(neon_subl_u16, i64, i64, i64)
+DEF_HELPER_2(neon_subl_u32, i64, i64, i64)
+DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64)
+DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64)
+DEF_HELPER_2(neon_abdl_u16, i64, i32, i32)
+DEF_HELPER_2(neon_abdl_s16, i64, i32, i32)
+DEF_HELPER_2(neon_abdl_u32, i64, i32, i32)
+DEF_HELPER_2(neon_abdl_s32, i64, i32, i32)
+DEF_HELPER_2(neon_abdl_u64, i64, i32, i32)
+DEF_HELPER_2(neon_abdl_s64, i64, i32, i32)
+DEF_HELPER_2(neon_mull_u8, i64, i32, i32)
+DEF_HELPER_2(neon_mull_s8, i64, i32, i32)
+DEF_HELPER_2(neon_mull_u16, i64, i32, i32)
+DEF_HELPER_2(neon_mull_s16, i64, i32, i32)
+
+DEF_HELPER_1(neon_negl_u16, i64, i64)
+DEF_HELPER_1(neon_negl_u32, i64, i64)
+DEF_HELPER_1(neon_negl_u64, i64, i64)
+
+DEF_HELPER_2(neon_qabs_s8, i32, env, i32)
+DEF_HELPER_2(neon_qabs_s16, i32, env, i32)
+DEF_HELPER_2(neon_qabs_s32, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s8, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s16, i32, env, i32)
+DEF_HELPER_2(neon_qneg_s32, i32, env, i32)
+
+DEF_HELPER_2(neon_min_f32, i32, i32, i32)
+DEF_HELPER_2(neon_max_f32, i32, i32, i32)
+DEF_HELPER_2(neon_abd_f32, i32, i32, i32)
+DEF_HELPER_2(neon_add_f32, i32, i32, i32)
+DEF_HELPER_2(neon_sub_f32, i32, i32, i32)
+DEF_HELPER_2(neon_mul_f32, i32, i32, i32)
+DEF_HELPER_2(neon_ceq_f32, i32, i32, i32)
+DEF_HELPER_2(neon_cge_f32, i32, i32, i32)
+DEF_HELPER_2(neon_cgt_f32, i32, i32, i32)
+DEF_HELPER_2(neon_acge_f32, i32, i32, i32)
+DEF_HELPER_2(neon_acgt_f32, i32, i32, i32)
+
+/* iwmmxt_helper.c */
+DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64)
+DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64)
+DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64)
+
+#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \
+DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \
+DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \
+DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \
+
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackl)
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackh)
+
+DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64)
+DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(mins)
+DEF_IWMMXT_HELPER_SIZE_ENV(minu)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxs)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxu)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(subn)
+DEF_IWMMXT_HELPER_SIZE_ENV(addn)
+DEF_IWMMXT_HELPER_SIZE_ENV(subu)
+DEF_IWMMXT_HELPER_SIZE_ENV(addu)
+DEF_IWMMXT_HELPER_SIZE_ENV(subs)
+DEF_IWMMXT_HELPER_SIZE_ENV(adds)
+
+DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64)
+
+DEF_HELPER_2(iwmmxt_msadb, i64, i64, i64)
+
+DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32)
+DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32)
+
+DEF_HELPER_1(iwmmxt_bcstb, i64, i32)
+DEF_HELPER_1(iwmmxt_bcstw, i64, i32)
+DEF_HELPER_1(iwmmxt_bcstl, i64, i32)
+
+DEF_HELPER_1(iwmmxt_addcb, i64, i64)
+DEF_HELPER_1(iwmmxt_addcw, i64, i64)
+DEF_HELPER_1(iwmmxt_addcl, i64, i64)
+
+DEF_HELPER_1(iwmmxt_msbb, i32, i64)
+DEF_HELPER_1(iwmmxt_msbw, i32, i64)
+DEF_HELPER_1(iwmmxt_msbl, i32, i64)
+
+DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32)
+DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32)
+
+DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64)
+DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64)
+
+DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32)
+DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32)
+DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32)
+
+DEF_HELPER_2(set_teecr, void, env, i32)
+
+#include "def-helper.h"
diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c
new file mode 100644
index 0000000..3332f70
--- /dev/null
+++ b/target-arm/iwmmxt_helper.c
@@ -0,0 +1,681 @@
+/*
+ * iwMMXt micro operations for XScale.
+ *
+ * Copyright (c) 2007 OpenedHand, Ltd.
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "helpers.h"
+
+/* iwMMXt macros extracted from GNU gdb. */
+
+/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */
+#define SIMD8_SET( v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n)))
+#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n)))
+#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n)))
+#define SIMD64_SET(v, n) ((v != 0) << (32 + (n)))
+/* Flags to pass as "n" above. */
+#define SIMD_NBIT -1
+#define SIMD_ZBIT -2
+#define SIMD_CBIT -3
+#define SIMD_VBIT -4
+/* Various status bit macros. */
+#define NBIT8(x) ((x) & 0x80)
+#define NBIT16(x) ((x) & 0x8000)
+#define NBIT32(x) ((x) & 0x80000000)
+#define NBIT64(x) ((x) & 0x8000000000000000ULL)
+#define ZBIT8(x) (((x) & 0xff) == 0)
+#define ZBIT16(x) (((x) & 0xffff) == 0)
+#define ZBIT32(x) (((x) & 0xffffffff) == 0)
+#define ZBIT64(x) (x == 0)
+/* Sign extension macros. */
+#define EXTEND8H(a) ((uint16_t) (int8_t) (a))
+#define EXTEND8(a) ((uint32_t) (int8_t) (a))
+#define EXTEND16(a) ((uint32_t) (int16_t) (a))
+#define EXTEND16S(a) ((int32_t) (int16_t) (a))
+#define EXTEND32(a) ((uint64_t) (int32_t) (a))
+
+uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b)
+{
+ a = ((
+ EXTEND16S((a >> 0) & 0xffff) * EXTEND16S((b >> 0) & 0xffff) +
+ EXTEND16S((a >> 16) & 0xffff) * EXTEND16S((b >> 16) & 0xffff)
+ ) & 0xffffffff) | ((uint64_t) (
+ EXTEND16S((a >> 32) & 0xffff) * EXTEND16S((b >> 32) & 0xffff) +
+ EXTEND16S((a >> 48) & 0xffff) * EXTEND16S((b >> 48) & 0xffff)
+ ) << 32);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_madduq)(uint64_t a, uint64_t b)
+{
+ a = ((
+ ((a >> 0) & 0xffff) * ((b >> 0) & 0xffff) +
+ ((a >> 16) & 0xffff) * ((b >> 16) & 0xffff)
+ ) & 0xffffffff) | ((
+ ((a >> 32) & 0xffff) * ((b >> 32) & 0xffff) +
+ ((a >> 48) & 0xffff) * ((b >> 48) & 0xffff)
+ ) << 32);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_sadb)(uint64_t a, uint64_t b)
+{
+#define abs(x) (((x) >= 0) ? x : -x)
+#define SADB(SHR) abs((int) ((a >> SHR) & 0xff) - (int) ((b >> SHR) & 0xff))
+ return
+ SADB(0) + SADB(8) + SADB(16) + SADB(24) +
+ SADB(32) + SADB(40) + SADB(48) + SADB(56);
+#undef SADB
+}
+
+uint64_t HELPER(iwmmxt_sadw)(uint64_t a, uint64_t b)
+{
+#define SADW(SHR) \
+ abs((int) ((a >> SHR) & 0xffff) - (int) ((b >> SHR) & 0xffff))
+ return SADW(0) + SADW(16) + SADW(32) + SADW(48);
+#undef SADW
+}
+
+uint64_t HELPER(iwmmxt_mulslw)(uint64_t a, uint64_t b)
+{
+#define MULS(SHR) ((uint64_t) ((( \
+ EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \
+ ) >> 0) & 0xffff) << SHR)
+ return MULS(0) | MULS(16) | MULS(32) | MULS(48);
+#undef MULS
+}
+
+uint64_t HELPER(iwmmxt_mulshw)(uint64_t a, uint64_t b)
+{
+#define MULS(SHR) ((uint64_t) ((( \
+ EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \
+ ) >> 16) & 0xffff) << SHR)
+ return MULS(0) | MULS(16) | MULS(32) | MULS(48);
+#undef MULS
+}
+
+uint64_t HELPER(iwmmxt_mululw)(uint64_t a, uint64_t b)
+{
+#define MULU(SHR) ((uint64_t) ((( \
+ ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \
+ ) >> 0) & 0xffff) << SHR)
+ return MULU(0) | MULU(16) | MULU(32) | MULU(48);
+#undef MULU
+}
+
+uint64_t HELPER(iwmmxt_muluhw)(uint64_t a, uint64_t b)
+{
+#define MULU(SHR) ((uint64_t) ((( \
+ ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \
+ ) >> 16) & 0xffff) << SHR)
+ return MULU(0) | MULU(16) | MULU(32) | MULU(48);
+#undef MULU
+}
+
+uint64_t HELPER(iwmmxt_macsw)(uint64_t a, uint64_t b)
+{
+#define MACS(SHR) ( \
+ EXTEND16((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff))
+ return (int64_t) (MACS(0) + MACS(16) + MACS(32) + MACS(48));
+#undef MACS
+}
+
+uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b)
+{
+#define MACU(SHR) ( \
+ (uint32_t) ((a >> SHR) & 0xffff) * \
+ (uint32_t) ((b >> SHR) & 0xffff))
+ return MACU(0) + MACU(16) + MACU(32) + MACU(48);
+#undef MACU
+}
+
+#define NZBIT8(x, i) \
+ SIMD8_SET(NBIT8((x) & 0xff), SIMD_NBIT, i) | \
+ SIMD8_SET(ZBIT8((x) & 0xff), SIMD_ZBIT, i)
+#define NZBIT16(x, i) \
+ SIMD16_SET(NBIT16((x) & 0xffff), SIMD_NBIT, i) | \
+ SIMD16_SET(ZBIT16((x) & 0xffff), SIMD_ZBIT, i)
+#define NZBIT32(x, i) \
+ SIMD32_SET(NBIT32((x) & 0xffffffff), SIMD_NBIT, i) | \
+ SIMD32_SET(ZBIT32((x) & 0xffffffff), SIMD_ZBIT, i)
+#define NZBIT64(x) \
+ SIMD64_SET(NBIT64(x), SIMD_NBIT) | \
+ SIMD64_SET(ZBIT64(x), SIMD_ZBIT)
+#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
+{ \
+ a = \
+ (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \
+ (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \
+ (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \
+ (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \
+ NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \
+ NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \
+ NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \
+ return a; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
+{ \
+ a = \
+ (((a >> SH0) & 0xffff) << 0) | \
+ (((b >> SH0) & 0xffff) << 16) | \
+ (((a >> SH2) & 0xffff) << 32) | \
+ (((b >> SH2) & 0xffff) << 48); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \
+ NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \
+ return a; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
+{ \
+ a = \
+ (((a >> SH0) & 0xffffffff) << 0) | \
+ (((b >> SH0) & 0xffffffff) << 32); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \
+ return a; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUState *env, \
+ uint64_t x) \
+{ \
+ x = \
+ (((x >> SH0) & 0xff) << 0) | \
+ (((x >> SH1) & 0xff) << 16) | \
+ (((x >> SH2) & 0xff) << 32) | \
+ (((x >> SH3) & 0xff) << 48); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \
+ return x; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUState *env, \
+ uint64_t x) \
+{ \
+ x = \
+ (((x >> SH0) & 0xffff) << 0) | \
+ (((x >> SH2) & 0xffff) << 32); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \
+ return x; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUState *env, \
+ uint64_t x) \
+{ \
+ x = (((x >> SH0) & 0xffffffff) << 0); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \
+ return x; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUState *env, \
+ uint64_t x) \
+{ \
+ x = \
+ ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \
+ ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \
+ ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \
+ ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \
+ return x; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUState *env, \
+ uint64_t x) \
+{ \
+ x = \
+ ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \
+ ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \
+ return x; \
+} \
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUState *env, \
+ uint64_t x) \
+{ \
+ x = EXTEND32((x >> SH0) & 0xffffffff); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \
+ return x; \
+}
+IWMMXT_OP_UNPACK(l, 0, 8, 16, 24)
+IWMMXT_OP_UNPACK(h, 32, 40, 48, 56)
+
+#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
+{ \
+ a = \
+ CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \
+ CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \
+ CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \
+ CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \
+ NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \
+ NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \
+ NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \
+ return a; \
+} \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
+{ \
+ a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \
+ CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \
+ NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \
+ return a; \
+} \
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUState *env, \
+ uint64_t a, uint64_t b) \
+{ \
+ a = CMP(0, Tl, O, 0xffffffff) | \
+ CMP(32, Tl, O, 0xffffffff); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \
+ return a; \
+}
+#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \
+ (TYPE) ((b >> SHR) & MASK)) ? (uint64_t) MASK : 0) << SHR)
+IWMMXT_OP_CMP(cmpeq, uint8_t, uint16_t, uint32_t, ==)
+IWMMXT_OP_CMP(cmpgts, int8_t, int16_t, int32_t, >)
+IWMMXT_OP_CMP(cmpgtu, uint8_t, uint16_t, uint32_t, >)
+#undef CMP
+#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \
+ (TYPE) ((b >> SHR) & MASK)) ? a : b) & ((uint64_t) MASK << SHR))
+IWMMXT_OP_CMP(mins, int8_t, int16_t, int32_t, <)
+IWMMXT_OP_CMP(minu, uint8_t, uint16_t, uint32_t, <)
+IWMMXT_OP_CMP(maxs, int8_t, int16_t, int32_t, >)
+IWMMXT_OP_CMP(maxu, uint8_t, uint16_t, uint32_t, >)
+#undef CMP
+#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \
+ OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR)
+IWMMXT_OP_CMP(subn, uint8_t, uint16_t, uint32_t, -)
+IWMMXT_OP_CMP(addn, uint8_t, uint16_t, uint32_t, +)
+#undef CMP
+/* TODO Signed- and Unsigned-Saturation */
+#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \
+ OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR)
+IWMMXT_OP_CMP(subu, uint8_t, uint16_t, uint32_t, -)
+IWMMXT_OP_CMP(addu, uint8_t, uint16_t, uint32_t, +)
+IWMMXT_OP_CMP(subs, int8_t, int16_t, int32_t, -)
+IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +)
+#undef CMP
+#undef IWMMXT_OP_CMP
+
+#define AVGB(SHR) ((( \
+ ((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR)
+#define IWMMXT_OP_AVGB(r) \
+uint64_t HELPER(iwmmxt_avgb##r)(CPUState *env, uint64_t a, uint64_t b) \
+{ \
+ const int round = r; \
+ a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | \
+ AVGB(32) | AVGB(40) | AVGB(48) | AVGB(56); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ SIMD8_SET(ZBIT8((a >> 0) & 0xff), SIMD_ZBIT, 0) | \
+ SIMD8_SET(ZBIT8((a >> 8) & 0xff), SIMD_ZBIT, 1) | \
+ SIMD8_SET(ZBIT8((a >> 16) & 0xff), SIMD_ZBIT, 2) | \
+ SIMD8_SET(ZBIT8((a >> 24) & 0xff), SIMD_ZBIT, 3) | \
+ SIMD8_SET(ZBIT8((a >> 32) & 0xff), SIMD_ZBIT, 4) | \
+ SIMD8_SET(ZBIT8((a >> 40) & 0xff), SIMD_ZBIT, 5) | \
+ SIMD8_SET(ZBIT8((a >> 48) & 0xff), SIMD_ZBIT, 6) | \
+ SIMD8_SET(ZBIT8((a >> 56) & 0xff), SIMD_ZBIT, 7); \
+ return a; \
+}
+IWMMXT_OP_AVGB(0)
+IWMMXT_OP_AVGB(1)
+#undef IWMMXT_OP_AVGB
+#undef AVGB
+
+#define AVGW(SHR) ((( \
+ ((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR)
+#define IWMMXT_OP_AVGW(r) \
+uint64_t HELPER(iwmmxt_avgw##r)(CPUState *env, uint64_t a, uint64_t b) \
+{ \
+ const int round = r; \
+ a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); \
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \
+ SIMD16_SET(ZBIT16((a >> 0) & 0xffff), SIMD_ZBIT, 0) | \
+ SIMD16_SET(ZBIT16((a >> 16) & 0xffff), SIMD_ZBIT, 1) | \
+ SIMD16_SET(ZBIT16((a >> 32) & 0xffff), SIMD_ZBIT, 2) | \
+ SIMD16_SET(ZBIT16((a >> 48) & 0xffff), SIMD_ZBIT, 3); \
+ return a; \
+}
+IWMMXT_OP_AVGW(0)
+IWMMXT_OP_AVGW(1)
+#undef IWMMXT_OP_AVGW
+#undef AVGW
+
+uint64_t HELPER(iwmmxt_msadb)(uint64_t a, uint64_t b)
+{
+ a = ((((a >> 0 ) & 0xffff) * ((b >> 0) & 0xffff) +
+ ((a >> 16) & 0xffff) * ((b >> 16) & 0xffff)) & 0xffffffff) |
+ ((((a >> 32) & 0xffff) * ((b >> 32) & 0xffff) +
+ ((a >> 48) & 0xffff) * ((b >> 48) & 0xffff)) << 32);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_align)(uint64_t a, uint64_t b, uint32_t n)
+{
+ a >>= n << 3;
+ a |= b << (64 - (n << 3));
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_insr)(uint64_t x, uint32_t a, uint32_t b, uint32_t n)
+{
+ x &= ~((uint64_t) b << n);
+ x |= (uint64_t) (a & b) << n;
+ return x;
+}
+
+uint32_t HELPER(iwmmxt_setpsr_nz)(uint64_t x)
+{
+ return SIMD64_SET((x == 0), SIMD_ZBIT) |
+ SIMD64_SET((x & (1ULL << 63)), SIMD_NBIT);
+}
+
+uint64_t HELPER(iwmmxt_bcstb)(uint32_t arg)
+{
+ arg &= 0xff;
+ return
+ ((uint64_t) arg << 0 ) | ((uint64_t) arg << 8 ) |
+ ((uint64_t) arg << 16) | ((uint64_t) arg << 24) |
+ ((uint64_t) arg << 32) | ((uint64_t) arg << 40) |
+ ((uint64_t) arg << 48) | ((uint64_t) arg << 56);
+}
+
+uint64_t HELPER(iwmmxt_bcstw)(uint32_t arg)
+{
+ arg &= 0xffff;
+ return
+ ((uint64_t) arg << 0 ) | ((uint64_t) arg << 16) |
+ ((uint64_t) arg << 32) | ((uint64_t) arg << 48);
+}
+
+uint64_t HELPER(iwmmxt_bcstl)(uint32_t arg)
+{
+ return arg | ((uint64_t) arg << 32);
+}
+
+uint64_t HELPER(iwmmxt_addcb)(uint64_t x)
+{
+ return
+ ((x >> 0) & 0xff) + ((x >> 8) & 0xff) +
+ ((x >> 16) & 0xff) + ((x >> 24) & 0xff) +
+ ((x >> 32) & 0xff) + ((x >> 40) & 0xff) +
+ ((x >> 48) & 0xff) + ((x >> 56) & 0xff);
+}
+
+uint64_t HELPER(iwmmxt_addcw)(uint64_t x)
+{
+ return
+ ((x >> 0) & 0xffff) + ((x >> 16) & 0xffff) +
+ ((x >> 32) & 0xffff) + ((x >> 48) & 0xffff);
+}
+
+uint64_t HELPER(iwmmxt_addcl)(uint64_t x)
+{
+ return (x & 0xffffffff) + (x >> 32);
+}
+
+uint32_t HELPER(iwmmxt_msbb)(uint64_t x)
+{
+ return
+ ((x >> 7) & 0x01) | ((x >> 14) & 0x02) |
+ ((x >> 21) & 0x04) | ((x >> 28) & 0x08) |
+ ((x >> 35) & 0x10) | ((x >> 42) & 0x20) |
+ ((x >> 49) & 0x40) | ((x >> 56) & 0x80);
+}
+
+uint32_t HELPER(iwmmxt_msbw)(uint64_t x)
+{
+ return
+ ((x >> 15) & 0x01) | ((x >> 30) & 0x02) |
+ ((x >> 45) & 0x04) | ((x >> 52) & 0x08);
+}
+
+uint32_t HELPER(iwmmxt_msbl)(uint64_t x)
+{
+ return ((x >> 31) & 0x01) | ((x >> 62) & 0x02);
+}
+
+/* FIXME: Split wCASF setting into a separate op to avoid env use. */
+uint64_t HELPER(iwmmxt_srlw)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) |
+ (((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) |
+ (((x & (0xffffll << 32)) >> n) & (0xffffll << 32)) |
+ (((x & (0xffffll << 48)) >> n) & (0xffffll << 48));
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_srll)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = ((x & (0xffffffffll << 0)) >> n) |
+ ((x >> n) & (0xffffffffll << 32));
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_srlq)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x >>= n;
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_sllw)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) |
+ (((x & (0xffffll << 16)) << n) & (0xffffll << 16)) |
+ (((x & (0xffffll << 32)) << n) & (0xffffll << 32)) |
+ (((x & (0xffffll << 48)) << n) & (0xffffll << 48));
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_slll)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = ((x << n) & (0xffffffffll << 0)) |
+ ((x & (0xffffffffll << 32)) << n);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_sllq)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x <<= n;
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_sraw)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) |
+ ((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) |
+ ((uint64_t) ((EXTEND16(x >> 32) >> n) & 0xffff) << 32) |
+ ((uint64_t) ((EXTEND16(x >> 48) >> n) & 0xffff) << 48);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_sral)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) |
+ (((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_sraq)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = (int64_t) x >> n;
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_rorw)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = ((((x & (0xffffll << 0)) >> n) |
+ ((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) |
+ ((((x & (0xffffll << 16)) >> n) |
+ ((x & (0xffffll << 16)) << (16 - n))) & (0xffffll << 16)) |
+ ((((x & (0xffffll << 32)) >> n) |
+ ((x & (0xffffll << 32)) << (16 - n))) & (0xffffll << 32)) |
+ ((((x & (0xffffll << 48)) >> n) |
+ ((x & (0xffffll << 48)) << (16 - n))) & (0xffffll << 48));
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_rorl)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = ((x & (0xffffffffll << 0)) >> n) |
+ ((x >> n) & (0xffffffffll << 32)) |
+ ((x << (32 - n)) & (0xffffffffll << 0)) |
+ ((x & (0xffffffffll << 32)) << (32 - n));
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_rorq)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = (x >> n) | (x << (64 - n));
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+ return x;
+}
+
+uint64_t HELPER(iwmmxt_shufh)(CPUState *env, uint64_t x, uint32_t n)
+{
+ x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) |
+ (((x >> ((n << 2) & 0x30)) & 0xffff) << 16) |
+ (((x >> ((n << 0) & 0x30)) & 0xffff) << 32) |
+ (((x >> ((n >> 2) & 0x30)) & 0xffff) << 48);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+ NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+ return x;
+}
+
+/* TODO: Unsigned-Saturation */
+uint64_t HELPER(iwmmxt_packuw)(CPUState *env, uint64_t a, uint64_t b)
+{
+ a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
+ (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
+ (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) |
+ (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) |
+ NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) |
+ NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) |
+ NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_packul)(CPUState *env, uint64_t a, uint64_t b)
+{
+ a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
+ (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) |
+ NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_packuq)(CPUState *env, uint64_t a, uint64_t b)
+{
+ a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1);
+ return a;
+}
+
+/* TODO: Signed-Saturation */
+uint64_t HELPER(iwmmxt_packsw)(CPUState *env, uint64_t a, uint64_t b)
+{
+ a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
+ (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
+ (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) |
+ (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) |
+ NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) |
+ NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) |
+ NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_packsl)(CPUState *env, uint64_t a, uint64_t b)
+{
+ a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
+ (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) |
+ NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_packsq)(CPUState *env, uint64_t a, uint64_t b)
+{
+ a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
+ env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+ NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1);
+ return a;
+}
+
+uint64_t HELPER(iwmmxt_muladdsl)(uint64_t c, uint32_t a, uint32_t b)
+{
+ return c + ((int32_t) EXTEND32(a) * (int32_t) EXTEND32(b));
+}
+
+uint64_t HELPER(iwmmxt_muladdsw)(uint64_t c, uint32_t a, uint32_t b)
+{
+ c += EXTEND32(EXTEND16S((a >> 0) & 0xffff) *
+ EXTEND16S((b >> 0) & 0xffff));
+ c += EXTEND32(EXTEND16S((a >> 16) & 0xffff) *
+ EXTEND16S((b >> 16) & 0xffff));
+ return c;
+}
+
+uint64_t HELPER(iwmmxt_muladdswl)(uint64_t c, uint32_t a, uint32_t b)
+{
+ return c + (EXTEND32(EXTEND16S(a & 0xffff) *
+ EXTEND16S(b & 0xffff)));
+}
diff --git a/target-arm/machine.c b/target-arm/machine.c
new file mode 100644
index 0000000..3925d3a
--- /dev/null
+++ b/target-arm/machine.c
@@ -0,0 +1,211 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ int i;
+ CPUARMState *env = (CPUARMState *)opaque;
+
+ for (i = 0; i < 16; i++) {
+ qemu_put_be32(f, env->regs[i]);
+ }
+ qemu_put_be32(f, cpsr_read(env));
+ qemu_put_be32(f, env->spsr);
+ for (i = 0; i < 6; i++) {
+ qemu_put_be32(f, env->banked_spsr[i]);
+ qemu_put_be32(f, env->banked_r13[i]);
+ qemu_put_be32(f, env->banked_r14[i]);
+ }
+ for (i = 0; i < 5; i++) {
+ qemu_put_be32(f, env->usr_regs[i]);
+ qemu_put_be32(f, env->fiq_regs[i]);
+ }
+ qemu_put_be32(f, env->cp15.c0_cpuid);
+ qemu_put_be32(f, env->cp15.c0_cachetype);
+ qemu_put_be32(f, env->cp15.c0_cssel);
+ qemu_put_be32(f, env->cp15.c1_sys);
+ qemu_put_be32(f, env->cp15.c1_coproc);
+ qemu_put_be32(f, env->cp15.c1_xscaleauxcr);
+ qemu_put_be32(f, env->cp15.c2_base0);
+ qemu_put_be32(f, env->cp15.c2_base1);
+ qemu_put_be32(f, env->cp15.c2_control);
+ qemu_put_be32(f, env->cp15.c2_mask);
+ qemu_put_be32(f, env->cp15.c2_base_mask);
+ qemu_put_be32(f, env->cp15.c2_data);
+ qemu_put_be32(f, env->cp15.c2_insn);
+ qemu_put_be32(f, env->cp15.c3);
+ qemu_put_be32(f, env->cp15.c5_insn);
+ qemu_put_be32(f, env->cp15.c5_data);
+ for (i = 0; i < 8; i++) {
+ qemu_put_be32(f, env->cp15.c6_region[i]);
+ }
+ qemu_put_be32(f, env->cp15.c6_insn);
+ qemu_put_be32(f, env->cp15.c6_data);
+ qemu_put_be32(f, env->cp15.c9_insn);
+ qemu_put_be32(f, env->cp15.c9_data);
+ qemu_put_be32(f, env->cp15.c13_fcse);
+ qemu_put_be32(f, env->cp15.c13_context);
+ qemu_put_be32(f, env->cp15.c13_tls1);
+ qemu_put_be32(f, env->cp15.c13_tls2);
+ qemu_put_be32(f, env->cp15.c13_tls3);
+ qemu_put_be32(f, env->cp15.c15_cpar);
+
+ qemu_put_be32(f, env->features);
+
+ if (arm_feature(env, ARM_FEATURE_VFP)) {
+ for (i = 0; i < 16; i++) {
+ CPU_DoubleU u;
+ u.d = env->vfp.regs[i];
+ qemu_put_be32(f, u.l.upper);
+ qemu_put_be32(f, u.l.lower);
+ }
+ for (i = 0; i < 16; i++) {
+ qemu_put_be32(f, env->vfp.xregs[i]);
+ }
+
+ /* TODO: Should use proper FPSCR access functions. */
+ qemu_put_be32(f, env->vfp.vec_len);
+ qemu_put_be32(f, env->vfp.vec_stride);
+
+ if (arm_feature(env, ARM_FEATURE_VFP3)) {
+ for (i = 16; i < 32; i++) {
+ CPU_DoubleU u;
+ u.d = env->vfp.regs[i];
+ qemu_put_be32(f, u.l.upper);
+ qemu_put_be32(f, u.l.lower);
+ }
+ }
+ }
+
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ for (i = 0; i < 16; i++) {
+ qemu_put_be64(f, env->iwmmxt.regs[i]);
+ }
+ for (i = 0; i < 16; i++) {
+ qemu_put_be32(f, env->iwmmxt.cregs[i]);
+ }
+ }
+
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ qemu_put_be32(f, env->v7m.other_sp);
+ qemu_put_be32(f, env->v7m.vecbase);
+ qemu_put_be32(f, env->v7m.basepri);
+ qemu_put_be32(f, env->v7m.control);
+ qemu_put_be32(f, env->v7m.current_sp);
+ qemu_put_be32(f, env->v7m.exception);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
+ qemu_put_be32(f, env->teecr);
+ qemu_put_be32(f, env->teehbr);
+ }
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUARMState *env = (CPUARMState *)opaque;
+ int i;
+ uint32_t val;
+
+ if (version_id != CPU_SAVE_VERSION)
+ return -EINVAL;
+
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = qemu_get_be32(f);
+ }
+ val = qemu_get_be32(f);
+ /* Avoid mode switch when restoring CPSR. */
+ env->uncached_cpsr = val & CPSR_M;
+ cpsr_write(env, val, 0xffffffff);
+ env->spsr = qemu_get_be32(f);
+ for (i = 0; i < 6; i++) {
+ env->banked_spsr[i] = qemu_get_be32(f);
+ env->banked_r13[i] = qemu_get_be32(f);
+ env->banked_r14[i] = qemu_get_be32(f);
+ }
+ for (i = 0; i < 5; i++) {
+ env->usr_regs[i] = qemu_get_be32(f);
+ env->fiq_regs[i] = qemu_get_be32(f);
+ }
+ env->cp15.c0_cpuid = qemu_get_be32(f);
+ env->cp15.c0_cachetype = qemu_get_be32(f);
+ env->cp15.c0_cssel = qemu_get_be32(f);
+ env->cp15.c1_sys = qemu_get_be32(f);
+ env->cp15.c1_coproc = qemu_get_be32(f);
+ env->cp15.c1_xscaleauxcr = qemu_get_be32(f);
+ env->cp15.c2_base0 = qemu_get_be32(f);
+ env->cp15.c2_base1 = qemu_get_be32(f);
+ env->cp15.c2_control = qemu_get_be32(f);
+ env->cp15.c2_mask = qemu_get_be32(f);
+ env->cp15.c2_base_mask = qemu_get_be32(f);
+ env->cp15.c2_data = qemu_get_be32(f);
+ env->cp15.c2_insn = qemu_get_be32(f);
+ env->cp15.c3 = qemu_get_be32(f);
+ env->cp15.c5_insn = qemu_get_be32(f);
+ env->cp15.c5_data = qemu_get_be32(f);
+ for (i = 0; i < 8; i++) {
+ env->cp15.c6_region[i] = qemu_get_be32(f);
+ }
+ env->cp15.c6_insn = qemu_get_be32(f);
+ env->cp15.c6_data = qemu_get_be32(f);
+ env->cp15.c9_insn = qemu_get_be32(f);
+ env->cp15.c9_data = qemu_get_be32(f);
+ env->cp15.c13_fcse = qemu_get_be32(f);
+ env->cp15.c13_context = qemu_get_be32(f);
+ env->cp15.c13_tls1 = qemu_get_be32(f);
+ env->cp15.c13_tls2 = qemu_get_be32(f);
+ env->cp15.c13_tls3 = qemu_get_be32(f);
+ env->cp15.c15_cpar = qemu_get_be32(f);
+
+ env->features = qemu_get_be32(f);
+
+ if (arm_feature(env, ARM_FEATURE_VFP)) {
+ for (i = 0; i < 16; i++) {
+ CPU_DoubleU u;
+ u.l.upper = qemu_get_be32(f);
+ u.l.lower = qemu_get_be32(f);
+ env->vfp.regs[i] = u.d;
+ }
+ for (i = 0; i < 16; i++) {
+ env->vfp.xregs[i] = qemu_get_be32(f);
+ }
+
+ /* TODO: Should use proper FPSCR access functions. */
+ env->vfp.vec_len = qemu_get_be32(f);
+ env->vfp.vec_stride = qemu_get_be32(f);
+
+ if (arm_feature(env, ARM_FEATURE_VFP3)) {
+ for (i = 0; i < 16; i++) {
+ CPU_DoubleU u;
+ u.l.upper = qemu_get_be32(f);
+ u.l.lower = qemu_get_be32(f);
+ env->vfp.regs[i] = u.d;
+ }
+ }
+ }
+
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ for (i = 0; i < 16; i++) {
+ env->iwmmxt.regs[i] = qemu_get_be64(f);
+ }
+ for (i = 0; i < 16; i++) {
+ env->iwmmxt.cregs[i] = qemu_get_be32(f);
+ }
+ }
+
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ env->v7m.other_sp = qemu_get_be32(f);
+ env->v7m.vecbase = qemu_get_be32(f);
+ env->v7m.basepri = qemu_get_be32(f);
+ env->v7m.control = qemu_get_be32(f);
+ env->v7m.current_sp = qemu_get_be32(f);
+ env->v7m.exception = qemu_get_be32(f);
+ }
+
+ if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
+ env->teecr = qemu_get_be32(f);
+ env->teehbr = qemu_get_be32(f);
+ }
+
+ return 0;
+}
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
new file mode 100644
index 0000000..268af33
--- /dev/null
+++ b/target-arm/neon_helper.c
@@ -0,0 +1,1603 @@
+/*
+ * ARM NEON vector operations.
+ *
+ * Copyright (c) 2007, 2008 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "helpers.h"
+
+#define SIGNBIT (uint32_t)0x80000000
+#define SIGNBIT64 ((uint64_t)1 << 63)
+
+#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] = CPSR_Q
+
+static float_status neon_float_status;
+#define NFS &neon_float_status
+
+/* Helper routines to perform bitwise copies between float and int. */
+static inline float32 vfp_itos(uint32_t i)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+
+ v.i = i;
+ return v.s;
+}
+
+static inline uint32_t vfp_stoi(float32 s)
+{
+ union {
+ uint32_t i;
+ float32 s;
+ } v;
+
+ v.s = s;
+ return v.i;
+}
+
+#define NEON_TYPE1(name, type) \
+typedef struct \
+{ \
+ type v1; \
+} neon_##name;
+#ifdef HOST_WORDS_BIGENDIAN
+#define NEON_TYPE2(name, type) \
+typedef struct \
+{ \
+ type v2; \
+ type v1; \
+} neon_##name;
+#define NEON_TYPE4(name, type) \
+typedef struct \
+{ \
+ type v4; \
+ type v3; \
+ type v2; \
+ type v1; \
+} neon_##name;
+#else
+#define NEON_TYPE2(name, type) \
+typedef struct \
+{ \
+ type v1; \
+ type v2; \
+} neon_##name;
+#define NEON_TYPE4(name, type) \
+typedef struct \
+{ \
+ type v1; \
+ type v2; \
+ type v3; \
+ type v4; \
+} neon_##name;
+#endif
+
+NEON_TYPE4(s8, int8_t)
+NEON_TYPE4(u8, uint8_t)
+NEON_TYPE2(s16, int16_t)
+NEON_TYPE2(u16, uint16_t)
+NEON_TYPE1(s32, int32_t)
+NEON_TYPE1(u32, uint32_t)
+#undef NEON_TYPE4
+#undef NEON_TYPE2
+#undef NEON_TYPE1
+
+/* Copy from a uint32_t to a vector structure type. */
+#define NEON_UNPACK(vtype, dest, val) do { \
+ union { \
+ vtype v; \
+ uint32_t i; \
+ } conv_u; \
+ conv_u.i = (val); \
+ dest = conv_u.v; \
+ } while(0)
+
+/* Copy from a vector structure type to a uint32_t. */
+#define NEON_PACK(vtype, dest, val) do { \
+ union { \
+ vtype v; \
+ uint32_t i; \
+ } conv_u; \
+ conv_u.v = (val); \
+ dest = conv_u.i; \
+ } while(0)
+
+#define NEON_DO1 \
+ NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1);
+#define NEON_DO2 \
+ NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \
+ NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2);
+#define NEON_DO4 \
+ NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \
+ NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2); \
+ NEON_FN(vdest.v3, vsrc1.v3, vsrc2.v3); \
+ NEON_FN(vdest.v4, vsrc1.v4, vsrc2.v4);
+
+#define NEON_VOP_BODY(vtype, n) \
+{ \
+ uint32_t res; \
+ vtype vsrc1; \
+ vtype vsrc2; \
+ vtype vdest; \
+ NEON_UNPACK(vtype, vsrc1, arg1); \
+ NEON_UNPACK(vtype, vsrc2, arg2); \
+ NEON_DO##n; \
+ NEON_PACK(vtype, res, vdest); \
+ return res; \
+}
+
+#define NEON_VOP(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \
+NEON_VOP_BODY(vtype, n)
+
+#define NEON_VOP_ENV(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(CPUState *env, uint32_t arg1, uint32_t arg2) \
+NEON_VOP_BODY(vtype, n)
+
+/* Pairwise operations. */
+/* For 32-bit elements each segment only contains a single element, so
+ the elementwise and pairwise operations are the same. */
+#define NEON_PDO2 \
+ NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \
+ NEON_FN(vdest.v2, vsrc2.v1, vsrc2.v2);
+#define NEON_PDO4 \
+ NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \
+ NEON_FN(vdest.v2, vsrc1.v3, vsrc1.v4); \
+ NEON_FN(vdest.v3, vsrc2.v1, vsrc2.v2); \
+ NEON_FN(vdest.v4, vsrc2.v3, vsrc2.v4); \
+
+#define NEON_POP(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \
+{ \
+ uint32_t res; \
+ vtype vsrc1; \
+ vtype vsrc2; \
+ vtype vdest; \
+ NEON_UNPACK(vtype, vsrc1, arg1); \
+ NEON_UNPACK(vtype, vsrc2, arg2); \
+ NEON_PDO##n; \
+ NEON_PACK(vtype, res, vdest); \
+ return res; \
+}
+
+/* Unary operators. */
+#define NEON_VOP1(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(uint32_t arg) \
+{ \
+ vtype vsrc1; \
+ vtype vdest; \
+ NEON_UNPACK(vtype, vsrc1, arg); \
+ NEON_DO##n; \
+ NEON_PACK(vtype, arg, vdest); \
+ return arg; \
+}
+
+
+#define NEON_USAT(dest, src1, src2, type) do { \
+ uint32_t tmp = (uint32_t)src1 + (uint32_t)src2; \
+ if (tmp != (type)tmp) { \
+ SET_QC(); \
+ dest = ~0; \
+ } else { \
+ dest = tmp; \
+ }} while(0)
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
+NEON_VOP_ENV(qadd_u8, neon_u8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
+NEON_VOP_ENV(qadd_u16, neon_u16, 2)
+#undef NEON_FN
+#undef NEON_USAT
+
+uint32_t HELPER(neon_qadd_u32)(CPUState *env, uint32_t a, uint32_t b)
+{
+ uint32_t res = a + b;
+ if (res < a) {
+ SET_QC();
+ res = ~0;
+ }
+ return res;
+}
+
+uint64_t HELPER(neon_qadd_u64)(CPUState *env, uint64_t src1, uint64_t src2)
+{
+ uint64_t res;
+
+ res = src1 + src2;
+ if (res < src1) {
+ SET_QC();
+ res = ~(uint64_t)0;
+ }
+ return res;
+}
+
+#define NEON_SSAT(dest, src1, src2, type) do { \
+ int32_t tmp = (uint32_t)src1 + (uint32_t)src2; \
+ if (tmp != (type)tmp) { \
+ SET_QC(); \
+ if (src2 > 0) { \
+ tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \
+ } else { \
+ tmp = 1 << (sizeof(type) * 8 - 1); \
+ } \
+ } \
+ dest = tmp; \
+ } while(0)
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
+NEON_VOP_ENV(qadd_s8, neon_s8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
+NEON_VOP_ENV(qadd_s16, neon_s16, 2)
+#undef NEON_FN
+#undef NEON_SSAT
+
+uint32_t HELPER(neon_qadd_s32)(CPUState *env, uint32_t a, uint32_t b)
+{
+ uint32_t res = a + b;
+ if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
+ SET_QC();
+ res = ~(((int32_t)a >> 31) ^ SIGNBIT);
+ }
+ return res;
+}
+
+uint64_t HELPER(neon_qadd_s64)(CPUState *env, uint64_t src1, uint64_t src2)
+{
+ uint64_t res;
+
+ res = src1 + src2;
+ if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) {
+ SET_QC();
+ res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+ }
+ return res;
+}
+
+#define NEON_USAT(dest, src1, src2, type) do { \
+ uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \
+ if (tmp != (type)tmp) { \
+ SET_QC(); \
+ dest = 0; \
+ } else { \
+ dest = tmp; \
+ }} while(0)
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
+NEON_VOP_ENV(qsub_u8, neon_u8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
+NEON_VOP_ENV(qsub_u16, neon_u16, 2)
+#undef NEON_FN
+#undef NEON_USAT
+
+uint32_t HELPER(neon_qsub_u32)(CPUState *env, uint32_t a, uint32_t b)
+{
+ uint32_t res = a - b;
+ if (res > a) {
+ SET_QC();
+ res = 0;
+ }
+ return res;
+}
+
+uint64_t HELPER(neon_qsub_u64)(CPUState *env, uint64_t src1, uint64_t src2)
+{
+ uint64_t res;
+
+ if (src1 < src2) {
+ SET_QC();
+ res = 0;
+ } else {
+ res = src1 - src2;
+ }
+ return res;
+}
+
+#define NEON_SSAT(dest, src1, src2, type) do { \
+ int32_t tmp = (uint32_t)src1 - (uint32_t)src2; \
+ if (tmp != (type)tmp) { \
+ SET_QC(); \
+ if (src2 < 0) { \
+ tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \
+ } else { \
+ tmp = 1 << (sizeof(type) * 8 - 1); \
+ } \
+ } \
+ dest = tmp; \
+ } while(0)
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
+NEON_VOP_ENV(qsub_s8, neon_s8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
+NEON_VOP_ENV(qsub_s16, neon_s16, 2)
+#undef NEON_FN
+#undef NEON_SSAT
+
+uint32_t HELPER(neon_qsub_s32)(CPUState *env, uint32_t a, uint32_t b)
+{
+ uint32_t res = a - b;
+ if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
+ SET_QC();
+ res = ~(((int32_t)a >> 31) ^ SIGNBIT);
+ }
+ return res;
+}
+
+uint64_t HELPER(neon_qsub_s64)(CPUState *env, uint64_t src1, uint64_t src2)
+{
+ uint64_t res;
+
+ res = src1 - src2;
+ if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) {
+ SET_QC();
+ res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+ }
+ return res;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1
+NEON_VOP(hadd_s8, neon_s8, 4)
+NEON_VOP(hadd_u8, neon_u8, 4)
+NEON_VOP(hadd_s16, neon_s16, 2)
+NEON_VOP(hadd_u16, neon_u16, 2)
+#undef NEON_FN
+
+int32_t HELPER(neon_hadd_s32)(int32_t src1, int32_t src2)
+{
+ int32_t dest;
+
+ dest = (src1 >> 1) + (src2 >> 1);
+ if (src1 & src2 & 1)
+ dest++;
+ return dest;
+}
+
+uint32_t HELPER(neon_hadd_u32)(uint32_t src1, uint32_t src2)
+{
+ uint32_t dest;
+
+ dest = (src1 >> 1) + (src2 >> 1);
+ if (src1 & src2 & 1)
+ dest++;
+ return dest;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 + src2 + 1) >> 1
+NEON_VOP(rhadd_s8, neon_s8, 4)
+NEON_VOP(rhadd_u8, neon_u8, 4)
+NEON_VOP(rhadd_s16, neon_s16, 2)
+NEON_VOP(rhadd_u16, neon_u16, 2)
+#undef NEON_FN
+
+int32_t HELPER(neon_rhadd_s32)(int32_t src1, int32_t src2)
+{
+ int32_t dest;
+
+ dest = (src1 >> 1) + (src2 >> 1);
+ if ((src1 | src2) & 1)
+ dest++;
+ return dest;
+}
+
+uint32_t HELPER(neon_rhadd_u32)(uint32_t src1, uint32_t src2)
+{
+ uint32_t dest;
+
+ dest = (src1 >> 1) + (src2 >> 1);
+ if ((src1 | src2) & 1)
+ dest++;
+ return dest;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 - src2) >> 1
+NEON_VOP(hsub_s8, neon_s8, 4)
+NEON_VOP(hsub_u8, neon_u8, 4)
+NEON_VOP(hsub_s16, neon_s16, 2)
+NEON_VOP(hsub_u16, neon_u16, 2)
+#undef NEON_FN
+
+int32_t HELPER(neon_hsub_s32)(int32_t src1, int32_t src2)
+{
+ int32_t dest;
+
+ dest = (src1 >> 1) - (src2 >> 1);
+ if ((~src1) & src2 & 1)
+ dest--;
+ return dest;
+}
+
+uint32_t HELPER(neon_hsub_u32)(uint32_t src1, uint32_t src2)
+{
+ uint32_t dest;
+
+ dest = (src1 >> 1) - (src2 >> 1);
+ if ((~src1) & src2 & 1)
+ dest--;
+ return dest;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? ~0 : 0
+NEON_VOP(cgt_s8, neon_s8, 4)
+NEON_VOP(cgt_u8, neon_u8, 4)
+NEON_VOP(cgt_s16, neon_s16, 2)
+NEON_VOP(cgt_u16, neon_u16, 2)
+NEON_VOP(cgt_s32, neon_s32, 1)
+NEON_VOP(cgt_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 >= src2) ? ~0 : 0
+NEON_VOP(cge_s8, neon_s8, 4)
+NEON_VOP(cge_u8, neon_u8, 4)
+NEON_VOP(cge_s16, neon_s16, 2)
+NEON_VOP(cge_u16, neon_u16, 2)
+NEON_VOP(cge_s32, neon_s32, 1)
+NEON_VOP(cge_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2
+NEON_VOP(min_s8, neon_s8, 4)
+NEON_VOP(min_u8, neon_u8, 4)
+NEON_VOP(min_s16, neon_s16, 2)
+NEON_VOP(min_u16, neon_u16, 2)
+NEON_VOP(min_s32, neon_s32, 1)
+NEON_VOP(min_u32, neon_u32, 1)
+NEON_POP(pmin_s8, neon_s8, 4)
+NEON_POP(pmin_u8, neon_u8, 4)
+NEON_POP(pmin_s16, neon_s16, 2)
+NEON_POP(pmin_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? src1 : src2
+NEON_VOP(max_s8, neon_s8, 4)
+NEON_VOP(max_u8, neon_u8, 4)
+NEON_VOP(max_s16, neon_s16, 2)
+NEON_VOP(max_u16, neon_u16, 2)
+NEON_VOP(max_s32, neon_s32, 1)
+NEON_VOP(max_u32, neon_u32, 1)
+NEON_POP(pmax_s8, neon_s8, 4)
+NEON_POP(pmax_u8, neon_u8, 4)
+NEON_POP(pmax_s16, neon_s16, 2)
+NEON_POP(pmax_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) \
+ dest = (src1 > src2) ? (src1 - src2) : (src2 - src1)
+NEON_VOP(abd_s8, neon_s8, 4)
+NEON_VOP(abd_u8, neon_u8, 4)
+NEON_VOP(abd_s16, neon_s16, 2)
+NEON_VOP(abd_u16, neon_u16, 2)
+NEON_VOP(abd_s32, neon_s32, 1)
+NEON_VOP(abd_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8 || \
+ tmp <= -(ssize_t)sizeof(src1) * 8) { \
+ dest = 0; \
+ } else if (tmp < 0) { \
+ dest = src1 >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ }} while (0)
+NEON_VOP(shl_u8, neon_u8, 4)
+NEON_VOP(shl_u16, neon_u16, 2)
+NEON_VOP(shl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_shl_u64)(uint64_t val, uint64_t shiftop)
+{
+ int8_t shift = (int8_t)shiftop;
+ if (shift >= 64 || shift <= -64) {
+ val = 0;
+ } else if (shift < 0) {
+ val >>= -shift;
+ } else {
+ val <<= shift;
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8) { \
+ dest = 0; \
+ } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \
+ dest = src1 >> (sizeof(src1) * 8 - 1); \
+ } else if (tmp < 0) { \
+ dest = src1 >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ }} while (0)
+NEON_VOP(shl_s8, neon_s8, 4)
+NEON_VOP(shl_s16, neon_s16, 2)
+NEON_VOP(shl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_shl_s64)(uint64_t valop, uint64_t shiftop)
+{
+ int8_t shift = (int8_t)shiftop;
+ int64_t val = valop;
+ if (shift >= 64) {
+ val = 0;
+ } else if (shift <= -64) {
+ val >>= 63;
+ } else if (shift < 0) {
+ val >>= -shift;
+ } else {
+ val <<= shift;
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8) { \
+ dest = 0; \
+ } else if (tmp < -(ssize_t)sizeof(src1) * 8) { \
+ dest = src1 >> (sizeof(src1) * 8 - 1); \
+ } else if (tmp == -(ssize_t)sizeof(src1) * 8) { \
+ dest = src1 >> (tmp - 1); \
+ dest++; \
+ dest >>= 1; \
+ } else if (tmp < 0) { \
+ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ }} while (0)
+NEON_VOP(rshl_s8, neon_s8, 4)
+NEON_VOP(rshl_s16, neon_s16, 2)
+NEON_VOP(rshl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop)
+{
+ int8_t shift = (int8_t)shiftop;
+ int64_t val = valop;
+ if (shift >= 64) {
+ val = 0;
+ } else if (shift < -64) {
+ val >>= 63;
+ } else if (shift == -63) {
+ val >>= 63;
+ val++;
+ val >>= 1;
+ } else if (shift < 0) {
+ val = (val + ((int64_t)1 << (-1 - shift))) >> -shift;
+ } else {
+ val <<= shift;
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8 || \
+ tmp < -(ssize_t)sizeof(src1) * 8) { \
+ dest = 0; \
+ } else if (tmp == -(ssize_t)sizeof(src1) * 8) { \
+ dest = src1 >> (tmp - 1); \
+ } else if (tmp < 0) { \
+ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ }} while (0)
+NEON_VOP(rshl_u8, neon_u8, 4)
+NEON_VOP(rshl_u16, neon_u16, 2)
+NEON_VOP(rshl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop)
+{
+ int8_t shift = (uint8_t)shiftop;
+ if (shift >= 64 || shift < 64) {
+ val = 0;
+ } else if (shift == -64) {
+ /* Rounding a 1-bit result just preserves that bit. */
+ val >>= 63;
+ } if (shift < 0) {
+ val = (val + ((uint64_t)1 << (-1 - shift))) >> -shift;
+ val >>= -shift;
+ } else {
+ val <<= shift;
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8) { \
+ if (src1) { \
+ SET_QC(); \
+ dest = ~0; \
+ } else { \
+ dest = 0; \
+ } \
+ } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \
+ dest = 0; \
+ } else if (tmp < 0) { \
+ dest = src1 >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ if ((dest >> tmp) != src1) { \
+ SET_QC(); \
+ dest = ~0; \
+ } \
+ }} while (0)
+NEON_VOP_ENV(qshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qshl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
+{
+ int8_t shift = (int8_t)shiftop;
+ if (shift >= 64) {
+ if (val) {
+ val = ~(uint64_t)0;
+ SET_QC();
+ }
+ } else if (shift <= -64) {
+ val = 0;
+ } else if (shift < 0) {
+ val >>= -shift;
+ } else {
+ uint64_t tmp = val;
+ val <<= shift;
+ if ((val >> shift) != tmp) {
+ SET_QC();
+ val = ~(uint64_t)0;
+ }
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8) { \
+ if (src1) { \
+ SET_QC(); \
+ dest = (uint32_t)(1 << (sizeof(src1) * 8 - 1)); \
+ if (src1 > 0) { \
+ dest--; \
+ } \
+ } else { \
+ dest = src1; \
+ } \
+ } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \
+ dest = src1 >> 31; \
+ } else if (tmp < 0) { \
+ dest = src1 >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ if ((dest >> tmp) != src1) { \
+ SET_QC(); \
+ dest = (uint32_t)(1 << (sizeof(src1) * 8 - 1)); \
+ if (src1 > 0) { \
+ dest--; \
+ } \
+ } \
+ }} while (0)
+NEON_VOP_ENV(qshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qshl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
+{
+ int8_t shift = (uint8_t)shiftop;
+ int64_t val = valop;
+ if (shift >= 64) {
+ if (val) {
+ SET_QC();
+ val = (val >> 63) ^ ~SIGNBIT64;
+ }
+ } else if (shift <= -64) {
+ val >>= 63;
+ } else if (shift < 0) {
+ val >>= -shift;
+ } else {
+ int64_t tmp = val;
+ val <<= shift;
+ if ((val >> shift) != tmp) {
+ SET_QC();
+ val = (tmp >> 63) ^ ~SIGNBIT64;
+ }
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ if (src1 & (1 << (sizeof(src1) * 8 - 1))) { \
+ SET_QC(); \
+ dest = 0; \
+ } else { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp >= (ssize_t)sizeof(src1) * 8) { \
+ if (src1) { \
+ SET_QC(); \
+ dest = ~0; \
+ } else { \
+ dest = 0; \
+ } \
+ } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \
+ dest = 0; \
+ } else if (tmp < 0) { \
+ dest = src1 >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ if ((dest >> tmp) != src1) { \
+ SET_QC(); \
+ dest = ~0; \
+ } \
+ } \
+ }} while (0)
+NEON_VOP_ENV(qshlu_s8, neon_u8, 4)
+NEON_VOP_ENV(qshlu_s16, neon_u16, 2)
+#undef NEON_FN
+
+uint32_t HELPER(neon_qshlu_s32)(CPUState *env, uint32_t valop, uint32_t shiftop)
+{
+ if ((int32_t)valop < 0) {
+ SET_QC();
+ return 0;
+ }
+ return helper_neon_qshl_u32(env, valop, shiftop);
+}
+
+uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
+{
+ if ((int64_t)valop < 0) {
+ SET_QC();
+ return 0;
+ }
+ return helper_neon_qshl_u64(env, valop, shiftop);
+}
+
+/* FIXME: This is wrong. */
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp < 0) { \
+ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ if ((dest >> tmp) != src1) { \
+ SET_QC(); \
+ dest = ~0; \
+ } \
+ }} while (0)
+NEON_VOP_ENV(qrshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qrshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qrshl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
+{
+ int8_t shift = (int8_t)shiftop;
+ if (shift < 0) {
+ val = (val + (1 << (-1 - shift))) >> -shift;
+ } else { \
+ uint64_t tmp = val;
+ val <<= shift;
+ if ((val >> shift) != tmp) {
+ SET_QC();
+ val = ~0;
+ }
+ }
+ return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+ int8_t tmp; \
+ tmp = (int8_t)src2; \
+ if (tmp < 0) { \
+ dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+ } else { \
+ dest = src1 << tmp; \
+ if ((dest >> tmp) != src1) { \
+ SET_QC(); \
+ dest = src1 >> 31; \
+ } \
+ }} while (0)
+NEON_VOP_ENV(qrshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qrshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qrshl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
+{
+ int8_t shift = (uint8_t)shiftop;
+ int64_t val = valop;
+
+ if (shift < 0) {
+ val = (val + (1 << (-1 - shift))) >> -shift;
+ } else {
+ int64_t tmp = val;;
+ val <<= shift;
+ if ((val >> shift) != tmp) {
+ SET_QC();
+ val = tmp >> 31;
+ }
+ }
+ return val;
+}
+
+uint32_t HELPER(neon_add_u8)(uint32_t a, uint32_t b)
+{
+ uint32_t mask;
+ mask = (a ^ b) & 0x80808080u;
+ a &= ~0x80808080u;
+ b &= ~0x80808080u;
+ return (a + b) ^ mask;
+}
+
+uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b)
+{
+ uint32_t mask;
+ mask = (a ^ b) & 0x80008000u;
+ a &= ~0x80008000u;
+ b &= ~0x80008000u;
+ return (a + b) ^ mask;
+}
+
+#define NEON_FN(dest, src1, src2) dest = src1 + src2
+NEON_POP(padd_u8, neon_u8, 4)
+NEON_POP(padd_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = src1 - src2
+NEON_VOP(sub_u8, neon_u8, 4)
+NEON_VOP(sub_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = src1 * src2
+NEON_VOP(mul_u8, neon_u8, 4)
+NEON_VOP(mul_u16, neon_u16, 2)
+#undef NEON_FN
+
+/* Polynomial multiplication is like integer multiplication except the
+ partial products are XORed, not added. */
+uint32_t HELPER(neon_mul_p8)(uint32_t op1, uint32_t op2)
+{
+ uint32_t mask;
+ uint32_t result;
+ result = 0;
+ while (op1) {
+ mask = 0;
+ if (op1 & 1)
+ mask |= 0xff;
+ if (op1 & (1 << 8))
+ mask |= (0xff << 8);
+ if (op1 & (1 << 16))
+ mask |= (0xff << 16);
+ if (op1 & (1 << 24))
+ mask |= (0xff << 24);
+ result ^= op2 & mask;
+ op1 = (op1 >> 1) & 0x7f7f7f7f;
+ op2 = (op2 << 1) & 0xfefefefe;
+ }
+ return result;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0
+NEON_VOP(tst_u8, neon_u8, 4)
+NEON_VOP(tst_u16, neon_u16, 2)
+NEON_VOP(tst_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 == src2) ? -1 : 0
+NEON_VOP(ceq_u8, neon_u8, 4)
+NEON_VOP(ceq_u16, neon_u16, 2)
+NEON_VOP(ceq_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = (src < 0) ? -src : src
+NEON_VOP1(abs_s8, neon_s8, 4)
+NEON_VOP1(abs_s16, neon_s16, 2)
+#undef NEON_FN
+
+/* Count Leading Sign/Zero Bits. */
+static inline int do_clz8(uint8_t x)
+{
+ int n;
+ for (n = 8; x; n--)
+ x >>= 1;
+ return n;
+}
+
+static inline int do_clz16(uint16_t x)
+{
+ int n;
+ for (n = 16; x; n--)
+ x >>= 1;
+ return n;
+}
+
+#define NEON_FN(dest, src, dummy) dest = do_clz8(src)
+NEON_VOP1(clz_u8, neon_u8, 4)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = do_clz16(src)
+NEON_VOP1(clz_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = do_clz8((src < 0) ? ~src : src) - 1
+NEON_VOP1(cls_s8, neon_s8, 4)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = do_clz16((src < 0) ? ~src : src) - 1
+NEON_VOP1(cls_s16, neon_s16, 2)
+#undef NEON_FN
+
+uint32_t HELPER(neon_cls_s32)(uint32_t x)
+{
+ int count;
+ if ((int32_t)x < 0)
+ x = ~x;
+ for (count = 32; x; count--)
+ x = x >> 1;
+ return count - 1;
+}
+
+/* Bit count. */
+uint32_t HELPER(neon_cnt_u8)(uint32_t x)
+{
+ x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
+ return x;
+}
+
+#define NEON_QDMULH16(dest, src1, src2, round) do { \
+ uint32_t tmp = (int32_t)(int16_t) src1 * (int16_t) src2; \
+ if ((tmp ^ (tmp << 1)) & SIGNBIT) { \
+ SET_QC(); \
+ tmp = (tmp >> 31) ^ ~SIGNBIT; \
+ } else { \
+ tmp <<= 1; \
+ } \
+ if (round) { \
+ int32_t old = tmp; \
+ tmp += 1 << 15; \
+ if ((int32_t)tmp < old) { \
+ SET_QC(); \
+ tmp = SIGNBIT - 1; \
+ } \
+ } \
+ dest = tmp >> 16; \
+ } while(0)
+#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0)
+NEON_VOP_ENV(qdmulh_s16, neon_s16, 2)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1)
+NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2)
+#undef NEON_FN
+#undef NEON_QDMULH16
+
+#define NEON_QDMULH32(dest, src1, src2, round) do { \
+ uint64_t tmp = (int64_t)(int32_t) src1 * (int32_t) src2; \
+ if ((tmp ^ (tmp << 1)) & SIGNBIT64) { \
+ SET_QC(); \
+ tmp = (tmp >> 63) ^ ~SIGNBIT64; \
+ } else { \
+ tmp <<= 1; \
+ } \
+ if (round) { \
+ int64_t old = tmp; \
+ tmp += (int64_t)1 << 31; \
+ if ((int64_t)tmp < old) { \
+ SET_QC(); \
+ tmp = SIGNBIT64 - 1; \
+ } \
+ } \
+ dest = tmp >> 32; \
+ } while(0)
+#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0)
+NEON_VOP_ENV(qdmulh_s32, neon_s32, 1)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1)
+NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1)
+#undef NEON_FN
+#undef NEON_QDMULH32
+
+uint32_t HELPER(neon_narrow_u8)(uint64_t x)
+{
+ return (x & 0xffu) | ((x >> 8) & 0xff00u) | ((x >> 16) & 0xff0000u)
+ | ((x >> 24) & 0xff000000u);
+}
+
+uint32_t HELPER(neon_narrow_u16)(uint64_t x)
+{
+ return (x & 0xffffu) | ((x >> 16) & 0xffff0000u);
+}
+
+uint32_t HELPER(neon_narrow_high_u8)(uint64_t x)
+{
+ return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00)
+ | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000);
+}
+
+uint32_t HELPER(neon_narrow_high_u16)(uint64_t x)
+{
+ return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
+}
+
+uint32_t HELPER(neon_narrow_round_high_u8)(uint64_t x)
+{
+ x &= 0xff80ff80ff80ff80ull;
+ x += 0x0080008000800080ull;
+ return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00)
+ | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000);
+}
+
+uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x)
+{
+ x &= 0xffff8000ffff8000ull;
+ x += 0x0000800000008000ull;
+ return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
+}
+
+uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x)
+{
+ uint16_t s;
+ uint8_t d;
+ uint32_t res = 0;
+#define SAT8(n) \
+ s = x >> n; \
+ if (s > 0xff) { \
+ d = 0xff; \
+ SET_QC(); \
+ } else { \
+ d = s; \
+ } \
+ res |= (uint32_t)d << (n / 2);
+
+ SAT8(0);
+ SAT8(16);
+ SAT8(32);
+ SAT8(48);
+#undef SAT8
+ return res;
+}
+
+uint32_t HELPER(neon_narrow_sat_s8)(CPUState *env, uint64_t x)
+{
+ int16_t s;
+ uint8_t d;
+ uint32_t res = 0;
+#define SAT8(n) \
+ s = x >> n; \
+ if (s != (int8_t)s) { \
+ d = (s >> 15) ^ 0x7f; \
+ SET_QC(); \
+ } else { \
+ d = s; \
+ } \
+ res |= (uint32_t)d << (n / 2);
+
+ SAT8(0);
+ SAT8(16);
+ SAT8(32);
+ SAT8(48);
+#undef SAT8
+ return res;
+}
+
+uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x)
+{
+ uint32_t high;
+ uint32_t low;
+ low = x;
+ if (low > 0xffff) {
+ low = 0xffff;
+ SET_QC();
+ }
+ high = x >> 32;
+ if (high > 0xffff) {
+ high = 0xffff;
+ SET_QC();
+ }
+ return low | (high << 16);
+}
+
+uint32_t HELPER(neon_narrow_sat_s16)(CPUState *env, uint64_t x)
+{
+ int32_t low;
+ int32_t high;
+ low = x;
+ if (low != (int16_t)low) {
+ low = (low >> 31) ^ 0x7fff;
+ SET_QC();
+ }
+ high = x >> 32;
+ if (high != (int16_t)high) {
+ high = (high >> 31) ^ 0x7fff;
+ SET_QC();
+ }
+ return (uint16_t)low | (high << 16);
+}
+
+uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x)
+{
+ if (x > 0xffffffffu) {
+ SET_QC();
+ return 0xffffffffu;
+ }
+ return x;
+}
+
+uint32_t HELPER(neon_narrow_sat_s32)(CPUState *env, uint64_t x)
+{
+ if ((int64_t)x != (int32_t)x) {
+ SET_QC();
+ return (x >> 63) ^ 0x7fffffff;
+ }
+ return x;
+}
+
+uint64_t HELPER(neon_widen_u8)(uint32_t x)
+{
+ uint64_t tmp;
+ uint64_t ret;
+ ret = (uint8_t)x;
+ tmp = (uint8_t)(x >> 8);
+ ret |= tmp << 16;
+ tmp = (uint8_t)(x >> 16);
+ ret |= tmp << 32;
+ tmp = (uint8_t)(x >> 24);
+ ret |= tmp << 48;
+ return ret;
+}
+
+uint64_t HELPER(neon_widen_s8)(uint32_t x)
+{
+ uint64_t tmp;
+ uint64_t ret;
+ ret = (uint16_t)(int8_t)x;
+ tmp = (uint16_t)(int8_t)(x >> 8);
+ ret |= tmp << 16;
+ tmp = (uint16_t)(int8_t)(x >> 16);
+ ret |= tmp << 32;
+ tmp = (uint16_t)(int8_t)(x >> 24);
+ ret |= tmp << 48;
+ return ret;
+}
+
+uint64_t HELPER(neon_widen_u16)(uint32_t x)
+{
+ uint64_t high = (uint16_t)(x >> 16);
+ return ((uint16_t)x) | (high << 32);
+}
+
+uint64_t HELPER(neon_widen_s16)(uint32_t x)
+{
+ uint64_t high = (int16_t)(x >> 16);
+ return ((uint32_t)(int16_t)x) | (high << 32);
+}
+
+uint64_t HELPER(neon_addl_u16)(uint64_t a, uint64_t b)
+{
+ uint64_t mask;
+ mask = (a ^ b) & 0x8000800080008000ull;
+ a &= ~0x8000800080008000ull;
+ b &= ~0x8000800080008000ull;
+ return (a + b) ^ mask;
+}
+
+uint64_t HELPER(neon_addl_u32)(uint64_t a, uint64_t b)
+{
+ uint64_t mask;
+ mask = (a ^ b) & 0x8000000080000000ull;
+ a &= ~0x8000000080000000ull;
+ b &= ~0x8000000080000000ull;
+ return (a + b) ^ mask;
+}
+
+uint64_t HELPER(neon_paddl_u16)(uint64_t a, uint64_t b)
+{
+ uint64_t tmp;
+ uint64_t tmp2;
+
+ tmp = a & 0x0000ffff0000ffffull;
+ tmp += (a >> 16) & 0x0000ffff0000ffffull;
+ tmp2 = b & 0xffff0000ffff0000ull;
+ tmp2 += (b << 16) & 0xffff0000ffff0000ull;
+ return ( tmp & 0xffff)
+ | ((tmp >> 16) & 0xffff0000ull)
+ | ((tmp2 << 16) & 0xffff00000000ull)
+ | ( tmp2 & 0xffff000000000000ull);
+}
+
+uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b)
+{
+ uint32_t low = a + (a >> 32);
+ uint32_t high = b + (b >> 32);
+ return low + ((uint64_t)high << 32);
+}
+
+uint64_t HELPER(neon_subl_u16)(uint64_t a, uint64_t b)
+{
+ uint64_t mask;
+ mask = (a ^ ~b) & 0x8000800080008000ull;
+ a |= 0x8000800080008000ull;
+ b &= ~0x8000800080008000ull;
+ return (a - b) ^ mask;
+}
+
+uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b)
+{
+ uint64_t mask;
+ mask = (a ^ ~b) & 0x8000000080000000ull;
+ a |= 0x8000000080000000ull;
+ b &= ~0x8000000080000000ull;
+ return (a - b) ^ mask;
+}
+
+uint64_t HELPER(neon_addl_saturate_s32)(CPUState *env, uint64_t a, uint64_t b)
+{
+ uint32_t x, y;
+ uint32_t low, high;
+
+ x = a;
+ y = b;
+ low = x + y;
+ if (((low ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) {
+ SET_QC();
+ low = ((int32_t)x >> 31) ^ ~SIGNBIT;
+ }
+ x = a >> 32;
+ y = b >> 32;
+ high = x + y;
+ if (((high ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) {
+ SET_QC();
+ high = ((int32_t)x >> 31) ^ ~SIGNBIT;
+ }
+ return low | ((uint64_t)high << 32);
+}
+
+uint64_t HELPER(neon_addl_saturate_s64)(CPUState *env, uint64_t a, uint64_t b)
+{
+ uint64_t result;
+
+ result = a + b;
+ if (((result ^ a) & SIGNBIT64) && !((a ^ b) & SIGNBIT64)) {
+ SET_QC();
+ result = ((int64_t)a >> 63) ^ ~SIGNBIT64;
+ }
+ return result;
+}
+
+#define DO_ABD(dest, x, y, type) do { \
+ type tmp_x = x; \
+ type tmp_y = y; \
+ dest = ((tmp_x > tmp_y) ? tmp_x - tmp_y : tmp_y - tmp_x); \
+ } while(0)
+
+uint64_t HELPER(neon_abdl_u16)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+ DO_ABD(result, a, b, uint8_t);
+ DO_ABD(tmp, a >> 8, b >> 8, uint8_t);
+ result |= tmp << 16;
+ DO_ABD(tmp, a >> 16, b >> 16, uint8_t);
+ result |= tmp << 32;
+ DO_ABD(tmp, a >> 24, b >> 24, uint8_t);
+ result |= tmp << 48;
+ return result;
+}
+
+uint64_t HELPER(neon_abdl_s16)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+ DO_ABD(result, a, b, int8_t);
+ DO_ABD(tmp, a >> 8, b >> 8, int8_t);
+ result |= tmp << 16;
+ DO_ABD(tmp, a >> 16, b >> 16, int8_t);
+ result |= tmp << 32;
+ DO_ABD(tmp, a >> 24, b >> 24, int8_t);
+ result |= tmp << 48;
+ return result;
+}
+
+uint64_t HELPER(neon_abdl_u32)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+ DO_ABD(result, a, b, uint16_t);
+ DO_ABD(tmp, a >> 16, b >> 16, uint16_t);
+ return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_abdl_s32)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+ DO_ABD(result, a, b, int16_t);
+ DO_ABD(tmp, a >> 16, b >> 16, int16_t);
+ return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_abdl_u64)(uint32_t a, uint32_t b)
+{
+ uint64_t result;
+ DO_ABD(result, a, b, uint32_t);
+ return result;
+}
+
+uint64_t HELPER(neon_abdl_s64)(uint32_t a, uint32_t b)
+{
+ uint64_t result;
+ DO_ABD(result, a, b, int32_t);
+ return result;
+}
+#undef DO_ABD
+
+/* Widening multiply. Named type is the source type. */
+#define DO_MULL(dest, x, y, type1, type2) do { \
+ type1 tmp_x = x; \
+ type1 tmp_y = y; \
+ dest = (type2)((type2)tmp_x * (type2)tmp_y); \
+ } while(0)
+
+uint64_t HELPER(neon_mull_u8)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+
+ DO_MULL(result, a, b, uint8_t, uint16_t);
+ DO_MULL(tmp, a >> 8, b >> 8, uint8_t, uint16_t);
+ result |= tmp << 16;
+ DO_MULL(tmp, a >> 16, b >> 16, uint8_t, uint16_t);
+ result |= tmp << 32;
+ DO_MULL(tmp, a >> 24, b >> 24, uint8_t, uint16_t);
+ result |= tmp << 48;
+ return result;
+}
+
+uint64_t HELPER(neon_mull_s8)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+
+ DO_MULL(result, a, b, int8_t, uint16_t);
+ DO_MULL(tmp, a >> 8, b >> 8, int8_t, uint16_t);
+ result |= tmp << 16;
+ DO_MULL(tmp, a >> 16, b >> 16, int8_t, uint16_t);
+ result |= tmp << 32;
+ DO_MULL(tmp, a >> 24, b >> 24, int8_t, uint16_t);
+ result |= tmp << 48;
+ return result;
+}
+
+uint64_t HELPER(neon_mull_u16)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+
+ DO_MULL(result, a, b, uint16_t, uint32_t);
+ DO_MULL(tmp, a >> 16, b >> 16, uint16_t, uint32_t);
+ return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_mull_s16)(uint32_t a, uint32_t b)
+{
+ uint64_t tmp;
+ uint64_t result;
+
+ DO_MULL(result, a, b, int16_t, uint32_t);
+ DO_MULL(tmp, a >> 16, b >> 16, int16_t, uint32_t);
+ return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_negl_u16)(uint64_t x)
+{
+ uint16_t tmp;
+ uint64_t result;
+ result = (uint16_t)-x;
+ tmp = -(x >> 16);
+ result |= (uint64_t)tmp << 16;
+ tmp = -(x >> 32);
+ result |= (uint64_t)tmp << 32;
+ tmp = -(x >> 48);
+ result |= (uint64_t)tmp << 48;
+ return result;
+}
+
+#include <stdio.h>
+uint64_t HELPER(neon_negl_u32)(uint64_t x)
+{
+ uint32_t low = -x;
+ uint32_t high = -(x >> 32);
+ return low | ((uint64_t)high << 32);
+}
+
+/* FIXME: There should be a native op for this. */
+uint64_t HELPER(neon_negl_u64)(uint64_t x)
+{
+ return -x;
+}
+
+/* Saturnating sign manuipulation. */
+/* ??? Make these use NEON_VOP1 */
+#define DO_QABS8(x) do { \
+ if (x == (int8_t)0x80) { \
+ x = 0x7f; \
+ SET_QC(); \
+ } else if (x < 0) { \
+ x = -x; \
+ }} while (0)
+uint32_t HELPER(neon_qabs_s8)(CPUState *env, uint32_t x)
+{
+ neon_s8 vec;
+ NEON_UNPACK(neon_s8, vec, x);
+ DO_QABS8(vec.v1);
+ DO_QABS8(vec.v2);
+ DO_QABS8(vec.v3);
+ DO_QABS8(vec.v4);
+ NEON_PACK(neon_s8, x, vec);
+ return x;
+}
+#undef DO_QABS8
+
+#define DO_QNEG8(x) do { \
+ if (x == (int8_t)0x80) { \
+ x = 0x7f; \
+ SET_QC(); \
+ } else { \
+ x = -x; \
+ }} while (0)
+uint32_t HELPER(neon_qneg_s8)(CPUState *env, uint32_t x)
+{
+ neon_s8 vec;
+ NEON_UNPACK(neon_s8, vec, x);
+ DO_QNEG8(vec.v1);
+ DO_QNEG8(vec.v2);
+ DO_QNEG8(vec.v3);
+ DO_QNEG8(vec.v4);
+ NEON_PACK(neon_s8, x, vec);
+ return x;
+}
+#undef DO_QNEG8
+
+#define DO_QABS16(x) do { \
+ if (x == (int16_t)0x8000) { \
+ x = 0x7fff; \
+ SET_QC(); \
+ } else if (x < 0) { \
+ x = -x; \
+ }} while (0)
+uint32_t HELPER(neon_qabs_s16)(CPUState *env, uint32_t x)
+{
+ neon_s16 vec;
+ NEON_UNPACK(neon_s16, vec, x);
+ DO_QABS16(vec.v1);
+ DO_QABS16(vec.v2);
+ NEON_PACK(neon_s16, x, vec);
+ return x;
+}
+#undef DO_QABS16
+
+#define DO_QNEG16(x) do { \
+ if (x == (int16_t)0x8000) { \
+ x = 0x7fff; \
+ SET_QC(); \
+ } else { \
+ x = -x; \
+ }} while (0)
+uint32_t HELPER(neon_qneg_s16)(CPUState *env, uint32_t x)
+{
+ neon_s16 vec;
+ NEON_UNPACK(neon_s16, vec, x);
+ DO_QNEG16(vec.v1);
+ DO_QNEG16(vec.v2);
+ NEON_PACK(neon_s16, x, vec);
+ return x;
+}
+#undef DO_QNEG16
+
+uint32_t HELPER(neon_qabs_s32)(CPUState *env, uint32_t x)
+{
+ if (x == SIGNBIT) {
+ SET_QC();
+ x = ~SIGNBIT;
+ } else if ((int32_t)x < 0) {
+ x = -x;
+ }
+ return x;
+}
+
+uint32_t HELPER(neon_qneg_s32)(CPUState *env, uint32_t x)
+{
+ if (x == SIGNBIT) {
+ SET_QC();
+ x = ~SIGNBIT;
+ } else {
+ x = -x;
+ }
+ return x;
+}
+
+/* NEON Float helpers. */
+uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b)
+{
+ float32 f0 = vfp_itos(a);
+ float32 f1 = vfp_itos(b);
+ return (float32_compare_quiet(f0, f1, NFS) == -1) ? a : b;
+}
+
+uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b)
+{
+ float32 f0 = vfp_itos(a);
+ float32 f1 = vfp_itos(b);
+ return (float32_compare_quiet(f0, f1, NFS) == 1) ? a : b;
+}
+
+uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b)
+{
+ float32 f0 = vfp_itos(a);
+ float32 f1 = vfp_itos(b);
+ return vfp_stoi((float32_compare_quiet(f0, f1, NFS) == 1)
+ ? float32_sub(f0, f1, NFS)
+ : float32_sub(f1, f0, NFS));
+}
+
+uint32_t HELPER(neon_add_f32)(uint32_t a, uint32_t b)
+{
+ return vfp_stoi(float32_add(vfp_itos(a), vfp_itos(b), NFS));
+}
+
+uint32_t HELPER(neon_sub_f32)(uint32_t a, uint32_t b)
+{
+ return vfp_stoi(float32_sub(vfp_itos(a), vfp_itos(b), NFS));
+}
+
+uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b)
+{
+ return vfp_stoi(float32_mul(vfp_itos(a), vfp_itos(b), NFS));
+}
+
+/* Floating point comparisons produce an integer result. */
+#define NEON_VOP_FCMP(name, cmp) \
+uint32_t HELPER(neon_##name)(uint32_t a, uint32_t b) \
+{ \
+ if (float32_compare_quiet(vfp_itos(a), vfp_itos(b), NFS) cmp 0) \
+ return ~0; \
+ else \
+ return 0; \
+}
+
+NEON_VOP_FCMP(ceq_f32, ==)
+NEON_VOP_FCMP(cge_f32, >=)
+NEON_VOP_FCMP(cgt_f32, >)
+
+uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b)
+{
+ float32 f0 = float32_abs(vfp_itos(a));
+ float32 f1 = float32_abs(vfp_itos(b));
+ return (float32_compare_quiet(f0, f1,NFS) >= 0) ? ~0 : 0;
+}
+
+uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b)
+{
+ float32 f0 = float32_abs(vfp_itos(a));
+ float32 f1 = float32_abs(vfp_itos(b));
+ return (float32_compare_quiet(f0, f1, NFS) > 0) ? ~0 : 0;
+}
diff --git a/target-arm/op_addsub.h b/target-arm/op_addsub.h
new file mode 100644
index 0000000..c02c92a
--- /dev/null
+++ b/target-arm/op_addsub.h
@@ -0,0 +1,103 @@
+/*
+ * ARMv6 integer SIMD operations.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#ifdef ARITH_GE
+#define GE_ARG , void *gep
+#define DECLARE_GE uint32_t ge = 0
+#define SET_GE *(uint32_t *)gep = ge
+#else
+#define GE_ARG
+#define DECLARE_GE do{}while(0)
+#define SET_GE do{}while(0)
+#endif
+
+#define RESULT(val, n, width) \
+ res |= ((uint32_t)(glue(glue(uint,width),_t))(val)) << (n * width)
+
+uint32_t HELPER(glue(PFX,add16))(uint32_t a, uint32_t b GE_ARG)
+{
+ uint32_t res = 0;
+ DECLARE_GE;
+
+ ADD16(a, b, 0);
+ ADD16(a >> 16, b >> 16, 1);
+ SET_GE;
+ return res;
+}
+
+uint32_t HELPER(glue(PFX,add8))(uint32_t a, uint32_t b GE_ARG)
+{
+ uint32_t res = 0;
+ DECLARE_GE;
+
+ ADD8(a, b, 0);
+ ADD8(a >> 8, b >> 8, 1);
+ ADD8(a >> 16, b >> 16, 2);
+ ADD8(a >> 24, b >> 24, 3);
+ SET_GE;
+ return res;
+}
+
+uint32_t HELPER(glue(PFX,sub16))(uint32_t a, uint32_t b GE_ARG)
+{
+ uint32_t res = 0;
+ DECLARE_GE;
+
+ SUB16(a, b, 0);
+ SUB16(a >> 16, b >> 16, 1);
+ SET_GE;
+ return res;
+}
+
+uint32_t HELPER(glue(PFX,sub8))(uint32_t a, uint32_t b GE_ARG)
+{
+ uint32_t res = 0;
+ DECLARE_GE;
+
+ SUB8(a, b, 0);
+ SUB8(a >> 8, b >> 8, 1);
+ SUB8(a >> 16, b >> 16, 2);
+ SUB8(a >> 24, b >> 24, 3);
+ SET_GE;
+ return res;
+}
+
+uint32_t HELPER(glue(PFX,subaddx))(uint32_t a, uint32_t b GE_ARG)
+{
+ uint32_t res = 0;
+ DECLARE_GE;
+
+ ADD16(a, b >> 16, 0);
+ SUB16(a >> 16, b, 1);
+ SET_GE;
+ return res;
+}
+
+uint32_t HELPER(glue(PFX,addsubx))(uint32_t a, uint32_t b GE_ARG)
+{
+ uint32_t res = 0;
+ DECLARE_GE;
+
+ SUB16(a, b >> 16, 0);
+ ADD16(a >> 16, b, 1);
+ SET_GE;
+ return res;
+}
+
+#undef GE_ARG
+#undef DECLARE_GE
+#undef SET_GE
+#undef RESULT
+
+#undef ARITH_GE
+#undef PFX
+#undef ADD16
+#undef SUB16
+#undef ADD8
+#undef SUB8
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
new file mode 100644
index 0000000..3de2610
--- /dev/null
+++ b/target-arm/op_helper.c
@@ -0,0 +1,426 @@
+/*
+ * ARM helper routines
+ *
+ * Copyright (c) 2005-2007 CodeSourcery, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "exec.h"
+#include "helpers.h"
+
+#define SIGNBIT (uint32_t)0x80000000
+#define SIGNBIT64 ((uint64_t)1 << 63)
+
+void raise_exception(int tt)
+{
+ env->exception_index = tt;
+ cpu_loop_exit();
+}
+
+uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
+ uint32_t rn, uint32_t maxindex)
+{
+ uint32_t val;
+ uint32_t tmp;
+ int index;
+ int shift;
+ uint64_t *table;
+ table = (uint64_t *)&env->vfp.regs[rn];
+ val = 0;
+ for (shift = 0; shift < 32; shift += 8) {
+ index = (ireg >> shift) & 0xff;
+ if (index < maxindex) {
+ tmp = (table[index >> 3] >> ((index & 7) << 3)) & 0xff;
+ val |= tmp << shift;
+ } else {
+ val |= def & (0xff << shift);
+ }
+ }
+ return val;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, NULL);
+ }
+ }
+ raise_exception(env->exception_index);
+ }
+ env = saved_env;
+}
+#endif
+
+/* FIXME: Pass an axplicit pointer to QF to CPUState, and move saturating
+ instructions into helper.c */
+uint32_t HELPER(add_setq)(uint32_t a, uint32_t b)
+{
+ uint32_t res = a + b;
+ if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT))
+ env->QF = 1;
+ return res;
+}
+
+uint32_t HELPER(add_saturate)(uint32_t a, uint32_t b)
+{
+ uint32_t res = a + b;
+ if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
+ env->QF = 1;
+ res = ~(((int32_t)a >> 31) ^ SIGNBIT);
+ }
+ return res;
+}
+
+uint32_t HELPER(sub_saturate)(uint32_t a, uint32_t b)
+{
+ uint32_t res = a - b;
+ if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
+ env->QF = 1;
+ res = ~(((int32_t)a >> 31) ^ SIGNBIT);
+ }
+ return res;
+}
+
+uint32_t HELPER(double_saturate)(int32_t val)
+{
+ uint32_t res;
+ if (val >= 0x40000000) {
+ res = ~SIGNBIT;
+ env->QF = 1;
+ } else if (val <= (int32_t)0xc0000000) {
+ res = SIGNBIT;
+ env->QF = 1;
+ } else {
+ res = val << 1;
+ }
+ return res;
+}
+
+uint32_t HELPER(add_usaturate)(uint32_t a, uint32_t b)
+{
+ uint32_t res = a + b;
+ if (res < a) {
+ env->QF = 1;
+ res = ~0;
+ }
+ return res;
+}
+
+uint32_t HELPER(sub_usaturate)(uint32_t a, uint32_t b)
+{
+ uint32_t res = a - b;
+ if (res > a) {
+ env->QF = 1;
+ res = 0;
+ }
+ return res;
+}
+
+/* Signed saturation. */
+static inline uint32_t do_ssat(int32_t val, int shift)
+{
+ int32_t top;
+ uint32_t mask;
+
+ top = val >> shift;
+ mask = (1u << shift) - 1;
+ if (top > 0) {
+ env->QF = 1;
+ return mask;
+ } else if (top < -1) {
+ env->QF = 1;
+ return ~mask;
+ }
+ return val;
+}
+
+/* Unsigned saturation. */
+static inline uint32_t do_usat(int32_t val, int shift)
+{
+ uint32_t max;
+
+ max = (1u << shift) - 1;
+ if (val < 0) {
+ env->QF = 1;
+ return 0;
+ } else if (val > max) {
+ env->QF = 1;
+ return max;
+ }
+ return val;
+}
+
+/* Signed saturate. */
+uint32_t HELPER(ssat)(uint32_t x, uint32_t shift)
+{
+ return do_ssat(x, shift);
+}
+
+/* Dual halfword signed saturate. */
+uint32_t HELPER(ssat16)(uint32_t x, uint32_t shift)
+{
+ uint32_t res;
+
+ res = (uint16_t)do_ssat((int16_t)x, shift);
+ res |= do_ssat(((int32_t)x) >> 16, shift) << 16;
+ return res;
+}
+
+/* Unsigned saturate. */
+uint32_t HELPER(usat)(uint32_t x, uint32_t shift)
+{
+ return do_usat(x, shift);
+}
+
+/* Dual halfword unsigned saturate. */
+uint32_t HELPER(usat16)(uint32_t x, uint32_t shift)
+{
+ uint32_t res;
+
+ res = (uint16_t)do_usat((int16_t)x, shift);
+ res |= do_usat(((int32_t)x) >> 16, shift) << 16;
+ return res;
+}
+
+void HELPER(wfi)(void)
+{
+ env->exception_index = EXCP_HLT;
+ env->halted = 1;
+ cpu_loop_exit();
+}
+
+void HELPER(exception)(uint32_t excp)
+{
+ env->exception_index = excp;
+ cpu_loop_exit();
+}
+
+uint32_t HELPER(cpsr_read)(void)
+{
+ return cpsr_read(env) & ~CPSR_EXEC;
+}
+
+void HELPER(cpsr_write)(uint32_t val, uint32_t mask)
+{
+ cpsr_write(env, val, mask);
+}
+
+/* Access to user mode registers from privileged modes. */
+uint32_t HELPER(get_user_reg)(uint32_t regno)
+{
+ uint32_t val;
+
+ if (regno == 13) {
+ val = env->banked_r13[0];
+ } else if (regno == 14) {
+ val = env->banked_r14[0];
+ } else if (regno >= 8
+ && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+ val = env->usr_regs[regno - 8];
+ } else {
+ val = env->regs[regno];
+ }
+ return val;
+}
+
+void HELPER(set_user_reg)(uint32_t regno, uint32_t val)
+{
+ if (regno == 13) {
+ env->banked_r13[0] = val;
+ } else if (regno == 14) {
+ env->banked_r14[0] = val;
+ } else if (regno >= 8
+ && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+ env->usr_regs[regno - 8] = val;
+ } else {
+ env->regs[regno] = val;
+ }
+}
+
+/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
+ The only way to do that in TCG is a conditional branch, which clobbers
+ all our temporaries. For now implement these as helper functions. */
+
+uint32_t HELPER (add_cc)(uint32_t a, uint32_t b)
+{
+ uint32_t result;
+ result = a + b;
+ env->NF = env->ZF = result;
+ env->CF = result < a;
+ env->VF = (a ^ b ^ -1) & (a ^ result);
+ return result;
+}
+
+uint32_t HELPER(adc_cc)(uint32_t a, uint32_t b)
+{
+ uint32_t result;
+ if (!env->CF) {
+ result = a + b;
+ env->CF = result < a;
+ } else {
+ result = a + b + 1;
+ env->CF = result <= a;
+ }
+ env->VF = (a ^ b ^ -1) & (a ^ result);
+ env->NF = env->ZF = result;
+ return result;
+}
+
+uint32_t HELPER(sub_cc)(uint32_t a, uint32_t b)
+{
+ uint32_t result;
+ result = a - b;
+ env->NF = env->ZF = result;
+ env->CF = a >= b;
+ env->VF = (a ^ b) & (a ^ result);
+ return result;
+}
+
+uint32_t HELPER(sbc_cc)(uint32_t a, uint32_t b)
+{
+ uint32_t result;
+ if (!env->CF) {
+ result = a - b - 1;
+ env->CF = a > b;
+ } else {
+ result = a - b;
+ env->CF = a >= b;
+ }
+ env->VF = (a ^ b) & (a ^ result);
+ env->NF = env->ZF = result;
+ return result;
+}
+
+/* Similarly for variable shift instructions. */
+
+uint32_t HELPER(shl)(uint32_t x, uint32_t i)
+{
+ int shift = i & 0xff;
+ if (shift >= 32)
+ return 0;
+ return x << shift;
+}
+
+uint32_t HELPER(shr)(uint32_t x, uint32_t i)
+{
+ int shift = i & 0xff;
+ if (shift >= 32)
+ return 0;
+ return (uint32_t)x >> shift;
+}
+
+uint32_t HELPER(sar)(uint32_t x, uint32_t i)
+{
+ int shift = i & 0xff;
+ if (shift >= 32)
+ shift = 31;
+ return (int32_t)x >> shift;
+}
+
+uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i)
+{
+ int shift = i & 0xff;
+ if (shift >= 32) {
+ if (shift == 32)
+ env->CF = x & 1;
+ else
+ env->CF = 0;
+ return 0;
+ } else if (shift != 0) {
+ env->CF = (x >> (32 - shift)) & 1;
+ return x << shift;
+ }
+ return x;
+}
+
+uint32_t HELPER(shr_cc)(uint32_t x, uint32_t i)
+{
+ int shift = i & 0xff;
+ if (shift >= 32) {
+ if (shift == 32)
+ env->CF = (x >> 31) & 1;
+ else
+ env->CF = 0;
+ return 0;
+ } else if (shift != 0) {
+ env->CF = (x >> (shift - 1)) & 1;
+ return x >> shift;
+ }
+ return x;
+}
+
+uint32_t HELPER(sar_cc)(uint32_t x, uint32_t i)
+{
+ int shift = i & 0xff;
+ if (shift >= 32) {
+ env->CF = (x >> 31) & 1;
+ return (int32_t)x >> 31;
+ } else if (shift != 0) {
+ env->CF = (x >> (shift - 1)) & 1;
+ return (int32_t)x >> shift;
+ }
+ return x;
+}
+
+uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
+{
+ int shift1, shift;
+ shift1 = i & 0xff;
+ shift = shift1 & 0x1f;
+ if (shift == 0) {
+ if (shift1 != 0)
+ env->CF = (x >> 31) & 1;
+ return x;
+ } else {
+ env->CF = (x >> (shift - 1)) & 1;
+ return ((uint32_t)x >> shift) | (x << (32 - shift));
+ }
+}
diff --git a/target-arm/translate.c b/target-arm/translate.c
new file mode 100644
index 0000000..5149543
--- /dev/null
+++ b/target-arm/translate.c
@@ -0,0 +1,9409 @@
+/*
+ * ARM translation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2005-2007 CodeSourcery
+ * Copyright (c) 2007 OpenedHand, Ltd.
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-log.h"
+
+#include "helpers.h"
+#define GEN_HELPER 1
+#include "helpers.h"
+
+#define ENABLE_ARCH_5J 0
+#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
+#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
+#define ENABLE_ARCH_6T2 arm_feature(env, ARM_FEATURE_THUMB2)
+#define ENABLE_ARCH_7 arm_feature(env, ARM_FEATURE_V7)
+
+#define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0)
+
+/* internal defines */
+typedef struct DisasContext {
+ target_ulong pc;
+ int is_jmp;
+ /* Nonzero if this instruction has been conditionally skipped. */
+ int condjmp;
+ /* The label that will be jumped to when the instruction is skipped. */
+ int condlabel;
+ /* Thumb-2 condtional execution bits. */
+ int condexec_mask;
+ int condexec_cond;
+ struct TranslationBlock *tb;
+ int singlestep_enabled;
+ int thumb;
+#if !defined(CONFIG_USER_ONLY)
+ int user;
+#endif
+ int vfp_enabled;
+ int vec_len;
+ int vec_stride;
+} DisasContext;
+
+static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
+
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(s) 1
+#else
+#define IS_USER(s) (s->user)
+#endif
+
+/* These instructions trap after executing, so defer them until after the
+ conditional executions state has been updated. */
+#define DISAS_WFI 4
+#define DISAS_SWI 5
+
+static TCGv_ptr cpu_env;
+/* We reuse the same 64-bit temporaries for efficiency. */
+static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
+static TCGv_i32 cpu_R[16];
+static TCGv_i32 cpu_exclusive_addr;
+static TCGv_i32 cpu_exclusive_val;
+static TCGv_i32 cpu_exclusive_high;
+#ifdef CONFIG_USER_ONLY
+static TCGv_i32 cpu_exclusive_test;
+static TCGv_i32 cpu_exclusive_info;
+#endif
+
+/* FIXME: These should be removed. */
+static TCGv cpu_F0s, cpu_F1s;
+static TCGv_i64 cpu_F0d, cpu_F1d;
+
+#include "gen-icount.h"
+
+static const char *regnames[] =
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" };
+
+/* initialize TCG globals. */
+void arm_translate_init(void)
+{
+ int i;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ for (i = 0; i < 16; i++) {
+ cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[i]),
+ regnames[i]);
+ }
+ cpu_exclusive_addr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_addr), "exclusive_addr");
+ cpu_exclusive_val = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_val), "exclusive_val");
+ cpu_exclusive_high = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_high), "exclusive_high");
+#ifdef CONFIG_USER_ONLY
+ cpu_exclusive_test = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_test), "exclusive_test");
+ cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, exclusive_info), "exclusive_info");
+#endif
+
+#define GEN_HELPER 2
+#include "helpers.h"
+}
+
+static int num_temps;
+
+/* Allocate a temporary variable. */
+static TCGv_i32 new_tmp(void)
+{
+ num_temps++;
+ return tcg_temp_new_i32();
+}
+
+/* Release a temporary variable. */
+static void dead_tmp(TCGv tmp)
+{
+ tcg_temp_free(tmp);
+ num_temps--;
+}
+
+static inline TCGv load_cpu_offset(int offset)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_ld_i32(tmp, cpu_env, offset);
+ return tmp;
+}
+
+#define load_cpu_field(name) load_cpu_offset(offsetof(CPUState, name))
+
+static inline void store_cpu_offset(TCGv var, int offset)
+{
+ tcg_gen_st_i32(var, cpu_env, offset);
+ dead_tmp(var);
+}
+
+#define store_cpu_field(var, name) \
+ store_cpu_offset(var, offsetof(CPUState, name))
+
+/* Set a variable to the value of a CPU register. */
+static void load_reg_var(DisasContext *s, TCGv var, int reg)
+{
+ if (reg == 15) {
+ uint32_t addr;
+ /* normaly, since we updated PC, we need only to add one insn */
+ if (s->thumb)
+ addr = (long)s->pc + 2;
+ else
+ addr = (long)s->pc + 4;
+ tcg_gen_movi_i32(var, addr);
+ } else {
+ tcg_gen_mov_i32(var, cpu_R[reg]);
+ }
+}
+
+/* Create a new temporary and set it to the value of a CPU register. */
+static inline TCGv load_reg(DisasContext *s, int reg)
+{
+ TCGv tmp = new_tmp();
+ load_reg_var(s, tmp, reg);
+ return tmp;
+}
+
+/* Set a CPU register. The source must be a temporary and will be
+ marked as dead. */
+static void store_reg(DisasContext *s, int reg, TCGv var)
+{
+ if (reg == 15) {
+ tcg_gen_andi_i32(var, var, ~1);
+ s->is_jmp = DISAS_JUMP;
+ }
+ tcg_gen_mov_i32(cpu_R[reg], var);
+ dead_tmp(var);
+}
+
+/* Value extensions. */
+#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var)
+#define gen_uxth(var) tcg_gen_ext16u_i32(var, var)
+#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var)
+#define gen_sxth(var) tcg_gen_ext16s_i32(var, var)
+
+#define gen_sxtb16(var) gen_helper_sxtb16(var, var)
+#define gen_uxtb16(var) gen_helper_uxtb16(var, var)
+
+
+static inline void gen_set_cpsr(TCGv var, uint32_t mask)
+{
+ TCGv tmp_mask = tcg_const_i32(mask);
+ gen_helper_cpsr_write(var, tmp_mask);
+ tcg_temp_free_i32(tmp_mask);
+}
+/* Set NZCV flags from the high 4 bits of var. */
+#define gen_set_nzcv(var) gen_set_cpsr(var, CPSR_NZCV)
+
+static void gen_exception(int excp)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, excp);
+ gen_helper_exception(tmp);
+ dead_tmp(tmp);
+}
+
+static void gen_smul_dual(TCGv a, TCGv b)
+{
+ TCGv tmp1 = new_tmp();
+ TCGv tmp2 = new_tmp();
+ tcg_gen_ext16s_i32(tmp1, a);
+ tcg_gen_ext16s_i32(tmp2, b);
+ tcg_gen_mul_i32(tmp1, tmp1, tmp2);
+ dead_tmp(tmp2);
+ tcg_gen_sari_i32(a, a, 16);
+ tcg_gen_sari_i32(b, b, 16);
+ tcg_gen_mul_i32(b, b, a);
+ tcg_gen_mov_i32(a, tmp1);
+ dead_tmp(tmp1);
+}
+
+/* Byteswap each halfword. */
+static void gen_rev16(TCGv var)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_shri_i32(tmp, var, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0x00ff00ff);
+ tcg_gen_shli_i32(var, var, 8);
+ tcg_gen_andi_i32(var, var, 0xff00ff00);
+ tcg_gen_or_i32(var, var, tmp);
+ dead_tmp(tmp);
+}
+
+/* Byteswap low halfword and sign extend. */
+static void gen_revsh(TCGv var)
+{
+ tcg_gen_ext16u_i32(var, var);
+ tcg_gen_bswap16_i32(var, var);
+ tcg_gen_ext16s_i32(var, var);
+}
+
+/* Unsigned bitfield extract. */
+static void gen_ubfx(TCGv var, int shift, uint32_t mask)
+{
+ if (shift)
+ tcg_gen_shri_i32(var, var, shift);
+ tcg_gen_andi_i32(var, var, mask);
+}
+
+/* Signed bitfield extract. */
+static void gen_sbfx(TCGv var, int shift, int width)
+{
+ uint32_t signbit;
+
+ if (shift)
+ tcg_gen_sari_i32(var, var, shift);
+ if (shift + width < 32) {
+ signbit = 1u << (width - 1);
+ tcg_gen_andi_i32(var, var, (1u << width) - 1);
+ tcg_gen_xori_i32(var, var, signbit);
+ tcg_gen_subi_i32(var, var, signbit);
+ }
+}
+
+/* Bitfield insertion. Insert val into base. Clobbers base and val. */
+static void gen_bfi(TCGv dest, TCGv base, TCGv val, int shift, uint32_t mask)
+{
+ tcg_gen_andi_i32(val, val, mask);
+ tcg_gen_shli_i32(val, val, shift);
+ tcg_gen_andi_i32(base, base, ~(mask << shift));
+ tcg_gen_or_i32(dest, base, val);
+}
+
+/* Return (b << 32) + a. Mark inputs as dead */
+static TCGv_i64 gen_addq_msw(TCGv_i64 a, TCGv b)
+{
+ TCGv_i64 tmp64 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(tmp64, b);
+ dead_tmp(b);
+ tcg_gen_shli_i64(tmp64, tmp64, 32);
+ tcg_gen_add_i64(a, tmp64, a);
+
+ tcg_temp_free_i64(tmp64);
+ return a;
+}
+
+/* Return (b << 32) - a. Mark inputs as dead. */
+static TCGv_i64 gen_subq_msw(TCGv_i64 a, TCGv b)
+{
+ TCGv_i64 tmp64 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(tmp64, b);
+ dead_tmp(b);
+ tcg_gen_shli_i64(tmp64, tmp64, 32);
+ tcg_gen_sub_i64(a, tmp64, a);
+
+ tcg_temp_free_i64(tmp64);
+ return a;
+}
+
+/* FIXME: Most targets have native widening multiplication.
+ It would be good to use that instead of a full wide multiply. */
+/* 32x32->64 multiply. Marks inputs as dead. */
+static TCGv_i64 gen_mulu_i64_i32(TCGv a, TCGv b)
+{
+ TCGv_i64 tmp1 = tcg_temp_new_i64();
+ TCGv_i64 tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(tmp1, a);
+ dead_tmp(a);
+ tcg_gen_extu_i32_i64(tmp2, b);
+ dead_tmp(b);
+ tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ return tmp1;
+}
+
+static TCGv_i64 gen_muls_i64_i32(TCGv a, TCGv b)
+{
+ TCGv_i64 tmp1 = tcg_temp_new_i64();
+ TCGv_i64 tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_ext_i32_i64(tmp1, a);
+ dead_tmp(a);
+ tcg_gen_ext_i32_i64(tmp2, b);
+ dead_tmp(b);
+ tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+ tcg_temp_free_i64(tmp2);
+ return tmp1;
+}
+
+/* Swap low and high halfwords. */
+static void gen_swap_half(TCGv var)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_shri_i32(tmp, var, 16);
+ tcg_gen_shli_i32(var, var, 16);
+ tcg_gen_or_i32(var, var, tmp);
+ dead_tmp(tmp);
+}
+
+/* Dual 16-bit add. Result placed in t0 and t1 is marked as dead.
+ tmp = (t0 ^ t1) & 0x8000;
+ t0 &= ~0x8000;
+ t1 &= ~0x8000;
+ t0 = (t0 + t1) ^ tmp;
+ */
+
+static void gen_add16(TCGv t0, TCGv t1)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_xor_i32(tmp, t0, t1);
+ tcg_gen_andi_i32(tmp, tmp, 0x8000);
+ tcg_gen_andi_i32(t0, t0, ~0x8000);
+ tcg_gen_andi_i32(t1, t1, ~0x8000);
+ tcg_gen_add_i32(t0, t0, t1);
+ tcg_gen_xor_i32(t0, t0, tmp);
+ dead_tmp(tmp);
+ dead_tmp(t1);
+}
+
+#define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, CF))
+
+/* Set CF to the top bit of var. */
+static void gen_set_CF_bit31(TCGv var)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_shri_i32(tmp, var, 31);
+ gen_set_CF(tmp);
+ dead_tmp(tmp);
+}
+
+/* Set N and Z flags from var. */
+static inline void gen_logic_CC(TCGv var)
+{
+ tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, NF));
+ tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, ZF));
+}
+
+/* T0 += T1 + CF. */
+static void gen_adc(TCGv t0, TCGv t1)
+{
+ TCGv tmp;
+ tcg_gen_add_i32(t0, t0, t1);
+ tmp = load_cpu_field(CF);
+ tcg_gen_add_i32(t0, t0, tmp);
+ dead_tmp(tmp);
+}
+
+/* dest = T0 + T1 + CF. */
+static void gen_add_carry(TCGv dest, TCGv t0, TCGv t1)
+{
+ TCGv tmp;
+ tcg_gen_add_i32(dest, t0, t1);
+ tmp = load_cpu_field(CF);
+ tcg_gen_add_i32(dest, dest, tmp);
+ dead_tmp(tmp);
+}
+
+/* dest = T0 - T1 + CF - 1. */
+static void gen_sub_carry(TCGv dest, TCGv t0, TCGv t1)
+{
+ TCGv tmp;
+ tcg_gen_sub_i32(dest, t0, t1);
+ tmp = load_cpu_field(CF);
+ tcg_gen_add_i32(dest, dest, tmp);
+ tcg_gen_subi_i32(dest, dest, 1);
+ dead_tmp(tmp);
+}
+
+/* FIXME: Implement this natively. */
+#define tcg_gen_abs_i32(t0, t1) gen_helper_abs(t0, t1)
+
+static void shifter_out_im(TCGv var, int shift)
+{
+ TCGv tmp = new_tmp();
+ if (shift == 0) {
+ tcg_gen_andi_i32(tmp, var, 1);
+ } else {
+ tcg_gen_shri_i32(tmp, var, shift);
+ if (shift != 31)
+ tcg_gen_andi_i32(tmp, tmp, 1);
+ }
+ gen_set_CF(tmp);
+ dead_tmp(tmp);
+}
+
+/* Shift by immediate. Includes special handling for shift == 0. */
+static inline void gen_arm_shift_im(TCGv var, int shiftop, int shift, int flags)
+{
+ switch (shiftop) {
+ case 0: /* LSL */
+ if (shift != 0) {
+ if (flags)
+ shifter_out_im(var, 32 - shift);
+ tcg_gen_shli_i32(var, var, shift);
+ }
+ break;
+ case 1: /* LSR */
+ if (shift == 0) {
+ if (flags) {
+ tcg_gen_shri_i32(var, var, 31);
+ gen_set_CF(var);
+ }
+ tcg_gen_movi_i32(var, 0);
+ } else {
+ if (flags)
+ shifter_out_im(var, shift - 1);
+ tcg_gen_shri_i32(var, var, shift);
+ }
+ break;
+ case 2: /* ASR */
+ if (shift == 0)
+ shift = 32;
+ if (flags)
+ shifter_out_im(var, shift - 1);
+ if (shift == 32)
+ shift = 31;
+ tcg_gen_sari_i32(var, var, shift);
+ break;
+ case 3: /* ROR/RRX */
+ if (shift != 0) {
+ if (flags)
+ shifter_out_im(var, shift - 1);
+ tcg_gen_rotri_i32(var, var, shift); break;
+ } else {
+ TCGv tmp = load_cpu_field(CF);
+ if (flags)
+ shifter_out_im(var, 0);
+ tcg_gen_shri_i32(var, var, 1);
+ tcg_gen_shli_i32(tmp, tmp, 31);
+ tcg_gen_or_i32(var, var, tmp);
+ dead_tmp(tmp);
+ }
+ }
+};
+
+static inline void gen_arm_shift_reg(TCGv var, int shiftop,
+ TCGv shift, int flags)
+{
+ if (flags) {
+ switch (shiftop) {
+ case 0: gen_helper_shl_cc(var, var, shift); break;
+ case 1: gen_helper_shr_cc(var, var, shift); break;
+ case 2: gen_helper_sar_cc(var, var, shift); break;
+ case 3: gen_helper_ror_cc(var, var, shift); break;
+ }
+ } else {
+ switch (shiftop) {
+ case 0: gen_helper_shl(var, var, shift); break;
+ case 1: gen_helper_shr(var, var, shift); break;
+ case 2: gen_helper_sar(var, var, shift); break;
+ case 3: tcg_gen_andi_i32(shift, shift, 0x1f);
+ tcg_gen_rotr_i32(var, var, shift); break;
+ }
+ }
+ dead_tmp(shift);
+}
+
+#define PAS_OP(pfx) \
+ switch (op2) { \
+ case 0: gen_pas_helper(glue(pfx,add16)); break; \
+ case 1: gen_pas_helper(glue(pfx,addsubx)); break; \
+ case 2: gen_pas_helper(glue(pfx,subaddx)); break; \
+ case 3: gen_pas_helper(glue(pfx,sub16)); break; \
+ case 4: gen_pas_helper(glue(pfx,add8)); break; \
+ case 7: gen_pas_helper(glue(pfx,sub8)); break; \
+ }
+static void gen_arm_parallel_addsub(int op1, int op2, TCGv a, TCGv b)
+{
+ TCGv_ptr tmp;
+
+ switch (op1) {
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp)
+ case 1:
+ tmp = tcg_temp_new_ptr();
+ tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+ PAS_OP(s)
+ tcg_temp_free_ptr(tmp);
+ break;
+ case 5:
+ tmp = tcg_temp_new_ptr();
+ tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+ PAS_OP(u)
+ tcg_temp_free_ptr(tmp);
+ break;
+#undef gen_pas_helper
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b)
+ case 2:
+ PAS_OP(q);
+ break;
+ case 3:
+ PAS_OP(sh);
+ break;
+ case 6:
+ PAS_OP(uq);
+ break;
+ case 7:
+ PAS_OP(uh);
+ break;
+#undef gen_pas_helper
+ }
+}
+#undef PAS_OP
+
+/* For unknown reasons Arm and Thumb-2 use arbitrarily different encodings. */
+#define PAS_OP(pfx) \
+ switch (op1) { \
+ case 0: gen_pas_helper(glue(pfx,add8)); break; \
+ case 1: gen_pas_helper(glue(pfx,add16)); break; \
+ case 2: gen_pas_helper(glue(pfx,addsubx)); break; \
+ case 4: gen_pas_helper(glue(pfx,sub8)); break; \
+ case 5: gen_pas_helper(glue(pfx,sub16)); break; \
+ case 6: gen_pas_helper(glue(pfx,subaddx)); break; \
+ }
+static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv a, TCGv b)
+{
+ TCGv_ptr tmp;
+
+ switch (op2) {
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp)
+ case 0:
+ tmp = tcg_temp_new_ptr();
+ tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+ PAS_OP(s)
+ tcg_temp_free_ptr(tmp);
+ break;
+ case 4:
+ tmp = tcg_temp_new_ptr();
+ tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+ PAS_OP(u)
+ tcg_temp_free_ptr(tmp);
+ break;
+#undef gen_pas_helper
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b)
+ case 1:
+ PAS_OP(q);
+ break;
+ case 2:
+ PAS_OP(sh);
+ break;
+ case 5:
+ PAS_OP(uq);
+ break;
+ case 6:
+ PAS_OP(uh);
+ break;
+#undef gen_pas_helper
+ }
+}
+#undef PAS_OP
+
+static void gen_test_cc(int cc, int label)
+{
+ TCGv tmp;
+ TCGv tmp2;
+ int inv;
+
+ switch (cc) {
+ case 0: /* eq: Z */
+ tmp = load_cpu_field(ZF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+ break;
+ case 1: /* ne: !Z */
+ tmp = load_cpu_field(ZF);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
+ break;
+ case 2: /* cs: C */
+ tmp = load_cpu_field(CF);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
+ break;
+ case 3: /* cc: !C */
+ tmp = load_cpu_field(CF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+ break;
+ case 4: /* mi: N */
+ tmp = load_cpu_field(NF);
+ tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+ break;
+ case 5: /* pl: !N */
+ tmp = load_cpu_field(NF);
+ tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+ break;
+ case 6: /* vs: V */
+ tmp = load_cpu_field(VF);
+ tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+ break;
+ case 7: /* vc: !V */
+ tmp = load_cpu_field(VF);
+ tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+ break;
+ case 8: /* hi: C && !Z */
+ inv = gen_new_label();
+ tmp = load_cpu_field(CF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv);
+ dead_tmp(tmp);
+ tmp = load_cpu_field(ZF);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
+ gen_set_label(inv);
+ break;
+ case 9: /* ls: !C || Z */
+ tmp = load_cpu_field(CF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+ dead_tmp(tmp);
+ tmp = load_cpu_field(ZF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+ break;
+ case 10: /* ge: N == V -> N ^ V == 0 */
+ tmp = load_cpu_field(VF);
+ tmp2 = load_cpu_field(NF);
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+ break;
+ case 11: /* lt: N != V -> N ^ V != 0 */
+ tmp = load_cpu_field(VF);
+ tmp2 = load_cpu_field(NF);
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+ break;
+ case 12: /* gt: !Z && N == V */
+ inv = gen_new_label();
+ tmp = load_cpu_field(ZF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv);
+ dead_tmp(tmp);
+ tmp = load_cpu_field(VF);
+ tmp2 = load_cpu_field(NF);
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+ gen_set_label(inv);
+ break;
+ case 13: /* le: Z || N != V */
+ tmp = load_cpu_field(ZF);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+ dead_tmp(tmp);
+ tmp = load_cpu_field(VF);
+ tmp2 = load_cpu_field(NF);
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+ break;
+ default:
+ fprintf(stderr, "Bad condition code 0x%x\n", cc);
+ abort();
+ }
+ dead_tmp(tmp);
+}
+
+static const uint8_t table_logic_cc[16] = {
+ 1, /* and */
+ 1, /* xor */
+ 0, /* sub */
+ 0, /* rsb */
+ 0, /* add */
+ 0, /* adc */
+ 0, /* sbc */
+ 0, /* rsc */
+ 1, /* andl */
+ 1, /* xorl */
+ 0, /* cmp */
+ 0, /* cmn */
+ 1, /* orr */
+ 1, /* mov */
+ 1, /* bic */
+ 1, /* mvn */
+};
+
+/* Set PC and Thumb state from an immediate address. */
+static inline void gen_bx_im(DisasContext *s, uint32_t addr)
+{
+ TCGv tmp;
+
+ s->is_jmp = DISAS_UPDATE;
+ if (s->thumb != (addr & 1)) {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, addr & 1);
+ tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, thumb));
+ dead_tmp(tmp);
+ }
+ tcg_gen_movi_i32(cpu_R[15], addr & ~1);
+}
+
+/* Set PC and Thumb state from var. var is marked as dead. */
+static inline void gen_bx(DisasContext *s, TCGv var)
+{
+ s->is_jmp = DISAS_UPDATE;
+ tcg_gen_andi_i32(cpu_R[15], var, ~1);
+ tcg_gen_andi_i32(var, var, 1);
+ store_cpu_field(var, thumb);
+}
+
+/* Variant of store_reg which uses branch&exchange logic when storing
+ to r15 in ARM architecture v7 and above. The source must be a temporary
+ and will be marked as dead. */
+static inline void store_reg_bx(CPUState *env, DisasContext *s,
+ int reg, TCGv var)
+{
+ if (reg == 15 && ENABLE_ARCH_7) {
+ gen_bx(s, var);
+ } else {
+ store_reg(s, reg, var);
+ }
+}
+
+static inline TCGv gen_ld8s(TCGv addr, int index)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_qemu_ld8s(tmp, addr, index);
+ return tmp;
+}
+static inline TCGv gen_ld8u(TCGv addr, int index)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_qemu_ld8u(tmp, addr, index);
+ return tmp;
+}
+static inline TCGv gen_ld16s(TCGv addr, int index)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_qemu_ld16s(tmp, addr, index);
+ return tmp;
+}
+static inline TCGv gen_ld16u(TCGv addr, int index)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_qemu_ld16u(tmp, addr, index);
+ return tmp;
+}
+static inline TCGv gen_ld32(TCGv addr, int index)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_qemu_ld32u(tmp, addr, index);
+ return tmp;
+}
+static inline TCGv_i64 gen_ld64(TCGv addr, int index)
+{
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_qemu_ld64(tmp, addr, index);
+ return tmp;
+}
+static inline void gen_st8(TCGv val, TCGv addr, int index)
+{
+ tcg_gen_qemu_st8(val, addr, index);
+ dead_tmp(val);
+}
+static inline void gen_st16(TCGv val, TCGv addr, int index)
+{
+ tcg_gen_qemu_st16(val, addr, index);
+ dead_tmp(val);
+}
+static inline void gen_st32(TCGv val, TCGv addr, int index)
+{
+ tcg_gen_qemu_st32(val, addr, index);
+ dead_tmp(val);
+}
+static inline void gen_st64(TCGv_i64 val, TCGv addr, int index)
+{
+ tcg_gen_qemu_st64(val, addr, index);
+ tcg_temp_free_i64(val);
+}
+
+static inline void gen_set_pc_im(uint32_t val)
+{
+ tcg_gen_movi_i32(cpu_R[15], val);
+}
+
+/* Force a TB lookup after an instruction that changes the CPU state. */
+static inline void gen_lookup_tb(DisasContext *s)
+{
+ tcg_gen_movi_i32(cpu_R[15], s->pc & ~1);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+static inline void gen_add_data_offset(DisasContext *s, unsigned int insn,
+ TCGv var)
+{
+ int val, rm, shift, shiftop;
+ TCGv offset;
+
+ if (!(insn & (1 << 25))) {
+ /* immediate */
+ val = insn & 0xfff;
+ if (!(insn & (1 << 23)))
+ val = -val;
+ if (val != 0)
+ tcg_gen_addi_i32(var, var, val);
+ } else {
+ /* shift/register */
+ rm = (insn) & 0xf;
+ shift = (insn >> 7) & 0x1f;
+ shiftop = (insn >> 5) & 3;
+ offset = load_reg(s, rm);
+ gen_arm_shift_im(offset, shiftop, shift, 0);
+ if (!(insn & (1 << 23)))
+ tcg_gen_sub_i32(var, var, offset);
+ else
+ tcg_gen_add_i32(var, var, offset);
+ dead_tmp(offset);
+ }
+}
+
+static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn,
+ int extra, TCGv var)
+{
+ int val, rm;
+ TCGv offset;
+
+ if (insn & (1 << 22)) {
+ /* immediate */
+ val = (insn & 0xf) | ((insn >> 4) & 0xf0);
+ if (!(insn & (1 << 23)))
+ val = -val;
+ val += extra;
+ if (val != 0)
+ tcg_gen_addi_i32(var, var, val);
+ } else {
+ /* register */
+ if (extra)
+ tcg_gen_addi_i32(var, var, extra);
+ rm = (insn) & 0xf;
+ offset = load_reg(s, rm);
+ if (!(insn & (1 << 23)))
+ tcg_gen_sub_i32(var, var, offset);
+ else
+ tcg_gen_add_i32(var, var, offset);
+ dead_tmp(offset);
+ }
+}
+
+#define VFP_OP2(name) \
+static inline void gen_vfp_##name(int dp) \
+{ \
+ if (dp) \
+ gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \
+ else \
+ gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \
+}
+
+VFP_OP2(add)
+VFP_OP2(sub)
+VFP_OP2(mul)
+VFP_OP2(div)
+
+#undef VFP_OP2
+
+static inline void gen_vfp_abs(int dp)
+{
+ if (dp)
+ gen_helper_vfp_absd(cpu_F0d, cpu_F0d);
+ else
+ gen_helper_vfp_abss(cpu_F0s, cpu_F0s);
+}
+
+static inline void gen_vfp_neg(int dp)
+{
+ if (dp)
+ gen_helper_vfp_negd(cpu_F0d, cpu_F0d);
+ else
+ gen_helper_vfp_negs(cpu_F0s, cpu_F0s);
+}
+
+static inline void gen_vfp_sqrt(int dp)
+{
+ if (dp)
+ gen_helper_vfp_sqrtd(cpu_F0d, cpu_F0d, cpu_env);
+ else
+ gen_helper_vfp_sqrts(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_cmp(int dp)
+{
+ if (dp)
+ gen_helper_vfp_cmpd(cpu_F0d, cpu_F1d, cpu_env);
+ else
+ gen_helper_vfp_cmps(cpu_F0s, cpu_F1s, cpu_env);
+}
+
+static inline void gen_vfp_cmpe(int dp)
+{
+ if (dp)
+ gen_helper_vfp_cmped(cpu_F0d, cpu_F1d, cpu_env);
+ else
+ gen_helper_vfp_cmpes(cpu_F0s, cpu_F1s, cpu_env);
+}
+
+static inline void gen_vfp_F1_ld0(int dp)
+{
+ if (dp)
+ tcg_gen_movi_i64(cpu_F1d, 0);
+ else
+ 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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+#define VFP_GEN_FIX(name) \
+static inline void gen_vfp_##name(int dp, int shift) \
+{ \
+ 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);\
+ tcg_temp_free_i32(tmp_shift); \
+}
+VFP_GEN_FIX(tosh)
+VFP_GEN_FIX(tosl)
+VFP_GEN_FIX(touh)
+VFP_GEN_FIX(toul)
+VFP_GEN_FIX(shto)
+VFP_GEN_FIX(slto)
+VFP_GEN_FIX(uhto)
+VFP_GEN_FIX(ulto)
+#undef VFP_GEN_FIX
+
+static inline void gen_vfp_ld(DisasContext *s, int dp, TCGv addr)
+{
+ if (dp)
+ tcg_gen_qemu_ld64(cpu_F0d, addr, IS_USER(s));
+ else
+ tcg_gen_qemu_ld32u(cpu_F0s, addr, IS_USER(s));
+}
+
+static inline void gen_vfp_st(DisasContext *s, int dp, TCGv addr)
+{
+ if (dp)
+ tcg_gen_qemu_st64(cpu_F0d, addr, IS_USER(s));
+ else
+ tcg_gen_qemu_st32(cpu_F0s, addr, IS_USER(s));
+}
+
+static inline long
+vfp_reg_offset (int dp, int reg)
+{
+ if (dp)
+ return offsetof(CPUARMState, vfp.regs[reg]);
+ else if (reg & 1) {
+ return offsetof(CPUARMState, vfp.regs[reg >> 1])
+ + offsetof(CPU_DoubleU, l.upper);
+ } else {
+ return offsetof(CPUARMState, vfp.regs[reg >> 1])
+ + offsetof(CPU_DoubleU, l.lower);
+ }
+}
+
+/* Return the offset of a 32-bit piece of a NEON register.
+ zero is the least significant end of the register. */
+static inline long
+neon_reg_offset (int reg, int n)
+{
+ int sreg;
+ sreg = reg * 2 + n;
+ return vfp_reg_offset(0, sreg);
+}
+
+static TCGv neon_load_reg(int reg, int pass)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_ld_i32(tmp, cpu_env, neon_reg_offset(reg, pass));
+ return tmp;
+}
+
+static void neon_store_reg(int reg, int pass, TCGv var)
+{
+ tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass));
+ dead_tmp(var);
+}
+
+static inline void neon_load_reg64(TCGv_i64 var, int reg)
+{
+ tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg));
+}
+
+static inline void neon_store_reg64(TCGv_i64 var, int reg)
+{
+ tcg_gen_st_i64(var, cpu_env, vfp_reg_offset(1, reg));
+}
+
+#define tcg_gen_ld_f32 tcg_gen_ld_i32
+#define tcg_gen_ld_f64 tcg_gen_ld_i64
+#define tcg_gen_st_f32 tcg_gen_st_i32
+#define tcg_gen_st_f64 tcg_gen_st_i64
+
+static inline void gen_mov_F0_vreg(int dp, int reg)
+{
+ if (dp)
+ tcg_gen_ld_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg));
+ else
+ tcg_gen_ld_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg));
+}
+
+static inline void gen_mov_F1_vreg(int dp, int reg)
+{
+ if (dp)
+ tcg_gen_ld_f64(cpu_F1d, cpu_env, vfp_reg_offset(dp, reg));
+ else
+ tcg_gen_ld_f32(cpu_F1s, cpu_env, vfp_reg_offset(dp, reg));
+}
+
+static inline void gen_mov_vreg_F0(int dp, int reg)
+{
+ if (dp)
+ tcg_gen_st_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg));
+ else
+ tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg));
+}
+
+#define ARM_CP_RW_BIT (1 << 20)
+
+static inline void iwmmxt_load_reg(TCGv_i64 var, int reg)
+{
+ tcg_gen_ld_i64(var, cpu_env, offsetof(CPUState, iwmmxt.regs[reg]));
+}
+
+static inline void iwmmxt_store_reg(TCGv_i64 var, int reg)
+{
+ tcg_gen_st_i64(var, cpu_env, offsetof(CPUState, iwmmxt.regs[reg]));
+}
+
+static inline TCGv iwmmxt_load_creg(int reg)
+{
+ TCGv var = new_tmp();
+ tcg_gen_ld_i32(var, cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
+ return var;
+}
+
+static inline void iwmmxt_store_creg(int reg, TCGv var)
+{
+ tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
+ dead_tmp(var);
+}
+
+static inline void gen_op_iwmmxt_movq_wRn_M0(int rn)
+{
+ iwmmxt_store_reg(cpu_M0, rn);
+}
+
+static inline void gen_op_iwmmxt_movq_M0_wRn(int rn)
+{
+ iwmmxt_load_reg(cpu_M0, rn);
+}
+
+static inline void gen_op_iwmmxt_orq_M0_wRn(int rn)
+{
+ iwmmxt_load_reg(cpu_V1, rn);
+ tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+static inline void gen_op_iwmmxt_andq_M0_wRn(int rn)
+{
+ iwmmxt_load_reg(cpu_V1, rn);
+ tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn)
+{
+ iwmmxt_load_reg(cpu_V1, rn);
+ tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+#define IWMMXT_OP(name) \
+static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
+{ \
+ iwmmxt_load_reg(cpu_V1, rn); \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \
+}
+
+#define IWMMXT_OP_ENV(name) \
+static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
+{ \
+ iwmmxt_load_reg(cpu_V1, rn); \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \
+}
+
+#define IWMMXT_OP_ENV_SIZE(name) \
+IWMMXT_OP_ENV(name##b) \
+IWMMXT_OP_ENV(name##w) \
+IWMMXT_OP_ENV(name##l)
+
+#define IWMMXT_OP_ENV1(name) \
+static inline void gen_op_iwmmxt_##name##_M0(void) \
+{ \
+ gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \
+}
+
+IWMMXT_OP(maddsq)
+IWMMXT_OP(madduq)
+IWMMXT_OP(sadb)
+IWMMXT_OP(sadw)
+IWMMXT_OP(mulslw)
+IWMMXT_OP(mulshw)
+IWMMXT_OP(mululw)
+IWMMXT_OP(muluhw)
+IWMMXT_OP(macsw)
+IWMMXT_OP(macuw)
+
+IWMMXT_OP_ENV_SIZE(unpackl)
+IWMMXT_OP_ENV_SIZE(unpackh)
+
+IWMMXT_OP_ENV1(unpacklub)
+IWMMXT_OP_ENV1(unpackluw)
+IWMMXT_OP_ENV1(unpacklul)
+IWMMXT_OP_ENV1(unpackhub)
+IWMMXT_OP_ENV1(unpackhuw)
+IWMMXT_OP_ENV1(unpackhul)
+IWMMXT_OP_ENV1(unpacklsb)
+IWMMXT_OP_ENV1(unpacklsw)
+IWMMXT_OP_ENV1(unpacklsl)
+IWMMXT_OP_ENV1(unpackhsb)
+IWMMXT_OP_ENV1(unpackhsw)
+IWMMXT_OP_ENV1(unpackhsl)
+
+IWMMXT_OP_ENV_SIZE(cmpeq)
+IWMMXT_OP_ENV_SIZE(cmpgtu)
+IWMMXT_OP_ENV_SIZE(cmpgts)
+
+IWMMXT_OP_ENV_SIZE(mins)
+IWMMXT_OP_ENV_SIZE(minu)
+IWMMXT_OP_ENV_SIZE(maxs)
+IWMMXT_OP_ENV_SIZE(maxu)
+
+IWMMXT_OP_ENV_SIZE(subn)
+IWMMXT_OP_ENV_SIZE(addn)
+IWMMXT_OP_ENV_SIZE(subu)
+IWMMXT_OP_ENV_SIZE(addu)
+IWMMXT_OP_ENV_SIZE(subs)
+IWMMXT_OP_ENV_SIZE(adds)
+
+IWMMXT_OP_ENV(avgb0)
+IWMMXT_OP_ENV(avgb1)
+IWMMXT_OP_ENV(avgw0)
+IWMMXT_OP_ENV(avgw1)
+
+IWMMXT_OP(msadb)
+
+IWMMXT_OP_ENV(packuw)
+IWMMXT_OP_ENV(packul)
+IWMMXT_OP_ENV(packuq)
+IWMMXT_OP_ENV(packsw)
+IWMMXT_OP_ENV(packsl)
+IWMMXT_OP_ENV(packsq)
+
+static void gen_op_iwmmxt_set_mup(void)
+{
+ TCGv tmp;
+ tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]);
+ tcg_gen_ori_i32(tmp, tmp, 2);
+ store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]);
+}
+
+static void gen_op_iwmmxt_set_cup(void)
+{
+ TCGv tmp;
+ tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]);
+ tcg_gen_ori_i32(tmp, tmp, 1);
+ store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]);
+}
+
+static void gen_op_iwmmxt_setpsr_nz(void)
+{
+ TCGv tmp = new_tmp();
+ gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0);
+ store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]);
+}
+
+static inline void gen_op_iwmmxt_addl_M0_wRn(int rn)
+{
+ iwmmxt_load_reg(cpu_V1, rn);
+ tcg_gen_ext32u_i64(cpu_V1, cpu_V1);
+ tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, TCGv dest)
+{
+ int rd;
+ uint32_t offset;
+ TCGv tmp;
+
+ rd = (insn >> 16) & 0xf;
+ tmp = load_reg(s, rd);
+
+ offset = (insn & 0xff) << ((insn >> 7) & 2);
+ if (insn & (1 << 24)) {
+ /* Pre indexed */
+ if (insn & (1 << 23))
+ tcg_gen_addi_i32(tmp, tmp, offset);
+ else
+ tcg_gen_addi_i32(tmp, tmp, -offset);
+ tcg_gen_mov_i32(dest, tmp);
+ if (insn & (1 << 21))
+ store_reg(s, rd, tmp);
+ else
+ dead_tmp(tmp);
+ } else if (insn & (1 << 21)) {
+ /* Post indexed */
+ tcg_gen_mov_i32(dest, tmp);
+ if (insn & (1 << 23))
+ tcg_gen_addi_i32(tmp, tmp, offset);
+ else
+ tcg_gen_addi_i32(tmp, tmp, -offset);
+ store_reg(s, rd, tmp);
+ } else if (!(insn & (1 << 23)))
+ return 1;
+ return 0;
+}
+
+static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv dest)
+{
+ int rd = (insn >> 0) & 0xf;
+ TCGv tmp;
+
+ if (insn & (1 << 8)) {
+ if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) {
+ return 1;
+ } else {
+ tmp = iwmmxt_load_creg(rd);
+ }
+ } else {
+ tmp = new_tmp();
+ iwmmxt_load_reg(cpu_V0, rd);
+ tcg_gen_trunc_i64_i32(tmp, cpu_V0);
+ }
+ tcg_gen_andi_i32(tmp, tmp, mask);
+ tcg_gen_mov_i32(dest, tmp);
+ dead_tmp(tmp);
+ return 0;
+}
+
+/* Disassemble an iwMMXt instruction. Returns nonzero if an error occured
+ (ie. an undefined instruction). */
+static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+ int rd, wrd;
+ int rdhi, rdlo, rd0, rd1, i;
+ TCGv addr;
+ TCGv tmp, tmp2, tmp3;
+
+ if ((insn & 0x0e000e00) == 0x0c000000) {
+ if ((insn & 0x0fe00ff0) == 0x0c400000) {
+ wrd = insn & 0xf;
+ rdlo = (insn >> 12) & 0xf;
+ rdhi = (insn >> 16) & 0xf;
+ if (insn & ARM_CP_RW_BIT) { /* TMRRC */
+ iwmmxt_load_reg(cpu_V0, wrd);
+ tcg_gen_trunc_i64_i32(cpu_R[rdlo], cpu_V0);
+ tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+ tcg_gen_trunc_i64_i32(cpu_R[rdhi], cpu_V0);
+ } else { /* TMCRR */
+ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]);
+ iwmmxt_store_reg(cpu_V0, wrd);
+ gen_op_iwmmxt_set_mup();
+ }
+ return 0;
+ }
+
+ wrd = (insn >> 12) & 0xf;
+ addr = new_tmp();
+ if (gen_iwmmxt_address(s, insn, addr)) {
+ dead_tmp(addr);
+ return 1;
+ }
+ if (insn & ARM_CP_RW_BIT) {
+ if ((insn >> 28) == 0xf) { /* WLDRW wCx */
+ tmp = new_tmp();
+ tcg_gen_qemu_ld32u(tmp, addr, IS_USER(s));
+ iwmmxt_store_creg(wrd, tmp);
+ } else {
+ i = 1;
+ if (insn & (1 << 8)) {
+ if (insn & (1 << 22)) { /* WLDRD */
+ tcg_gen_qemu_ld64(cpu_M0, addr, IS_USER(s));
+ i = 0;
+ } else { /* WLDRW wRd */
+ tmp = gen_ld32(addr, IS_USER(s));
+ }
+ } else {
+ if (insn & (1 << 22)) { /* WLDRH */
+ tmp = gen_ld16u(addr, IS_USER(s));
+ } else { /* WLDRB */
+ tmp = gen_ld8u(addr, IS_USER(s));
+ }
+ }
+ if (i) {
+ tcg_gen_extu_i32_i64(cpu_M0, tmp);
+ dead_tmp(tmp);
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ }
+ } else {
+ if ((insn >> 28) == 0xf) { /* WSTRW wCx */
+ tmp = iwmmxt_load_creg(wrd);
+ gen_st32(tmp, addr, IS_USER(s));
+ } else {
+ gen_op_iwmmxt_movq_M0_wRn(wrd);
+ tmp = new_tmp();
+ if (insn & (1 << 8)) {
+ if (insn & (1 << 22)) { /* WSTRD */
+ dead_tmp(tmp);
+ tcg_gen_qemu_st64(cpu_M0, addr, IS_USER(s));
+ } else { /* WSTRW wRd */
+ tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ } else {
+ if (insn & (1 << 22)) { /* WSTRH */
+ tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+ gen_st16(tmp, addr, IS_USER(s));
+ } else { /* WSTRB */
+ tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+ gen_st8(tmp, addr, IS_USER(s));
+ }
+ }
+ }
+ }
+ dead_tmp(addr);
+ return 0;
+ }
+
+ if ((insn & 0x0f000000) != 0x0e000000)
+ return 1;
+
+ switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) {
+ case 0x000: /* WOR */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 0) & 0xf;
+ rd1 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ gen_op_iwmmxt_orq_M0_wRn(rd1);
+ gen_op_iwmmxt_setpsr_nz();
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x011: /* TMCR */
+ if (insn & 0xf)
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ wrd = (insn >> 16) & 0xf;
+ switch (wrd) {
+ case ARM_IWMMXT_wCID:
+ case ARM_IWMMXT_wCASF:
+ break;
+ case ARM_IWMMXT_wCon:
+ gen_op_iwmmxt_set_cup();
+ /* Fall through. */
+ case ARM_IWMMXT_wCSSF:
+ tmp = iwmmxt_load_creg(wrd);
+ tmp2 = load_reg(s, rd);
+ tcg_gen_andc_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ iwmmxt_store_creg(wrd, tmp);
+ break;
+ case ARM_IWMMXT_wCGR0:
+ case ARM_IWMMXT_wCGR1:
+ case ARM_IWMMXT_wCGR2:
+ case ARM_IWMMXT_wCGR3:
+ gen_op_iwmmxt_set_cup();
+ tmp = load_reg(s, rd);
+ iwmmxt_store_creg(wrd, tmp);
+ break;
+ default:
+ return 1;
+ }
+ break;
+ case 0x100: /* WXOR */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 0) & 0xf;
+ rd1 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ gen_op_iwmmxt_xorq_M0_wRn(rd1);
+ gen_op_iwmmxt_setpsr_nz();
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x111: /* TMRC */
+ if (insn & 0xf)
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ wrd = (insn >> 16) & 0xf;
+ tmp = iwmmxt_load_creg(wrd);
+ store_reg(s, rd, tmp);
+ break;
+ case 0x300: /* WANDN */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 0) & 0xf;
+ rd1 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tcg_gen_neg_i64(cpu_M0, cpu_M0);
+ gen_op_iwmmxt_andq_M0_wRn(rd1);
+ gen_op_iwmmxt_setpsr_nz();
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x200: /* WAND */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 0) & 0xf;
+ rd1 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ gen_op_iwmmxt_andq_M0_wRn(rd1);
+ gen_op_iwmmxt_setpsr_nz();
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x810: case 0xa10: /* WMADD */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 0) & 0xf;
+ rd1 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_maddsq_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_madduq_M0_wRn(rd1);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ gen_op_iwmmxt_unpacklb_M0_wRn(rd1);
+ break;
+ case 1:
+ gen_op_iwmmxt_unpacklw_M0_wRn(rd1);
+ break;
+ case 2:
+ gen_op_iwmmxt_unpackll_M0_wRn(rd1);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ gen_op_iwmmxt_unpackhb_M0_wRn(rd1);
+ break;
+ case 1:
+ gen_op_iwmmxt_unpackhw_M0_wRn(rd1);
+ break;
+ case 2:
+ gen_op_iwmmxt_unpackhl_M0_wRn(rd1);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ if (insn & (1 << 22))
+ gen_op_iwmmxt_sadw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_sadb_M0_wRn(rd1);
+ if (!(insn & (1 << 20)))
+ gen_op_iwmmxt_addl_M0_wRn(wrd);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ if (insn & (1 << 21)) {
+ if (insn & (1 << 20))
+ gen_op_iwmmxt_mulshw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_mulslw_M0_wRn(rd1);
+ } else {
+ if (insn & (1 << 20))
+ gen_op_iwmmxt_muluhw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_mululw_M0_wRn(rd1);
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_macsw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_macuw_M0_wRn(rd1);
+ if (!(insn & (1 << 20))) {
+ iwmmxt_load_reg(cpu_V1, wrd);
+ tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1);
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ gen_op_iwmmxt_cmpeqb_M0_wRn(rd1);
+ break;
+ case 1:
+ gen_op_iwmmxt_cmpeqw_M0_wRn(rd1);
+ break;
+ case 2:
+ gen_op_iwmmxt_cmpeql_M0_wRn(rd1);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ if (insn & (1 << 22)) {
+ if (insn & (1 << 20))
+ gen_op_iwmmxt_avgw1_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_avgw0_M0_wRn(rd1);
+ } else {
+ if (insn & (1 << 20))
+ gen_op_iwmmxt_avgb1_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_avgb0_M0_wRn(rd1);
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = iwmmxt_load_creg(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3));
+ tcg_gen_andi_i32(tmp, tmp, 7);
+ iwmmxt_load_reg(cpu_V1, rd1);
+ gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp);
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */
+ if (((insn >> 6) & 3) == 3)
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ wrd = (insn >> 16) & 0xf;
+ tmp = load_reg(s, rd);
+ gen_op_iwmmxt_movq_M0_wRn(wrd);
+ switch ((insn >> 6) & 3) {
+ case 0:
+ tmp2 = tcg_const_i32(0xff);
+ tmp3 = tcg_const_i32((insn & 7) << 3);
+ break;
+ case 1:
+ tmp2 = tcg_const_i32(0xffff);
+ tmp3 = tcg_const_i32((insn & 3) << 4);
+ break;
+ case 2:
+ tmp2 = tcg_const_i32(0xffffffff);
+ tmp3 = tcg_const_i32((insn & 1) << 5);
+ break;
+ default:
+ TCGV_UNUSED(tmp2);
+ TCGV_UNUSED(tmp3);
+ }
+ gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3);
+ tcg_temp_free(tmp3);
+ tcg_temp_free(tmp2);
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */
+ rd = (insn >> 12) & 0xf;
+ wrd = (insn >> 16) & 0xf;
+ if (rd == 15 || ((insn >> 22) & 3) == 3)
+ return 1;
+ gen_op_iwmmxt_movq_M0_wRn(wrd);
+ tmp = new_tmp();
+ switch ((insn >> 22) & 3) {
+ case 0:
+ tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3);
+ tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+ if (insn & 8) {
+ tcg_gen_ext8s_i32(tmp, tmp);
+ } else {
+ tcg_gen_andi_i32(tmp, tmp, 0xff);
+ }
+ break;
+ case 1:
+ tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4);
+ tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+ if (insn & 8) {
+ tcg_gen_ext16s_i32(tmp, tmp);
+ } else {
+ tcg_gen_andi_i32(tmp, tmp, 0xffff);
+ }
+ break;
+ case 2:
+ tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5);
+ tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+ break;
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */
+ if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3)
+ return 1;
+ tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ tcg_gen_shri_i32(tmp, tmp, ((insn & 7) << 2) + 0);
+ break;
+ case 1:
+ tcg_gen_shri_i32(tmp, tmp, ((insn & 3) << 3) + 4);
+ break;
+ case 2:
+ tcg_gen_shri_i32(tmp, tmp, ((insn & 1) << 4) + 12);
+ break;
+ }
+ tcg_gen_shli_i32(tmp, tmp, 28);
+ gen_set_nzcv(tmp);
+ dead_tmp(tmp);
+ break;
+ case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */
+ if (((insn >> 6) & 3) == 3)
+ return 1;
+ rd = (insn >> 12) & 0xf;
+ wrd = (insn >> 16) & 0xf;
+ tmp = load_reg(s, rd);
+ switch ((insn >> 6) & 3) {
+ case 0:
+ gen_helper_iwmmxt_bcstb(cpu_M0, tmp);
+ break;
+ case 1:
+ gen_helper_iwmmxt_bcstw(cpu_M0, tmp);
+ break;
+ case 2:
+ gen_helper_iwmmxt_bcstl(cpu_M0, tmp);
+ break;
+ }
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */
+ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3)
+ return 1;
+ tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF);
+ tmp2 = new_tmp();
+ tcg_gen_mov_i32(tmp2, tmp);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ for (i = 0; i < 7; i ++) {
+ tcg_gen_shli_i32(tmp2, tmp2, 4);
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ }
+ break;
+ case 1:
+ for (i = 0; i < 3; i ++) {
+ tcg_gen_shli_i32(tmp2, tmp2, 8);
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ }
+ break;
+ case 2:
+ tcg_gen_shli_i32(tmp2, tmp2, 16);
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ break;
+ }
+ gen_set_nzcv(tmp);
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+ break;
+ case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0);
+ break;
+ case 1:
+ gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0);
+ break;
+ case 2:
+ gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */
+ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3)
+ return 1;
+ tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF);
+ tmp2 = new_tmp();
+ tcg_gen_mov_i32(tmp2, tmp);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ for (i = 0; i < 7; i ++) {
+ tcg_gen_shli_i32(tmp2, tmp2, 4);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ }
+ break;
+ case 1:
+ for (i = 0; i < 3; i ++) {
+ tcg_gen_shli_i32(tmp2, tmp2, 8);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ }
+ break;
+ case 2:
+ tcg_gen_shli_i32(tmp2, tmp2, 16);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ break;
+ }
+ gen_set_nzcv(tmp);
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+ break;
+ case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */
+ rd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3)
+ return 1;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = new_tmp();
+ switch ((insn >> 22) & 3) {
+ case 0:
+ gen_helper_iwmmxt_msbb(tmp, cpu_M0);
+ break;
+ case 1:
+ gen_helper_iwmmxt_msbw(tmp, cpu_M0);
+ break;
+ case 2:
+ gen_helper_iwmmxt_msbl(tmp, cpu_M0);
+ break;
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */
+ case 0x906: case 0xb06: case 0xd06: case 0xf06:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_cmpgtub_M0_wRn(rd1);
+ break;
+ case 1:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1);
+ break;
+ case 2:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_cmpgtul_M0_wRn(rd1);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */
+ case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_unpacklsb_M0();
+ else
+ gen_op_iwmmxt_unpacklub_M0();
+ break;
+ case 1:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_unpacklsw_M0();
+ else
+ gen_op_iwmmxt_unpackluw_M0();
+ break;
+ case 2:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_unpacklsl_M0();
+ else
+ gen_op_iwmmxt_unpacklul_M0();
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */
+ case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_unpackhsb_M0();
+ else
+ gen_op_iwmmxt_unpackhub_M0();
+ break;
+ case 1:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_unpackhsw_M0();
+ else
+ gen_op_iwmmxt_unpackhuw_M0();
+ break;
+ case 2:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_unpackhsl_M0();
+ else
+ gen_op_iwmmxt_unpackhul_M0();
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */
+ case 0x214: case 0x614: case 0xa14: case 0xe14:
+ if (((insn >> 22) & 3) == 0)
+ return 1;
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = new_tmp();
+ if (gen_iwmmxt_shift(insn, 0xff, tmp)) {
+ dead_tmp(tmp);
+ return 1;
+ }
+ switch ((insn >> 22) & 3) {
+ case 1:
+ gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 2:
+ gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 3:
+ gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ }
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */
+ case 0x014: case 0x414: case 0x814: case 0xc14:
+ if (((insn >> 22) & 3) == 0)
+ return 1;
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = new_tmp();
+ if (gen_iwmmxt_shift(insn, 0xff, tmp)) {
+ dead_tmp(tmp);
+ return 1;
+ }
+ switch ((insn >> 22) & 3) {
+ case 1:
+ gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 2:
+ gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 3:
+ gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ }
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */
+ case 0x114: case 0x514: case 0x914: case 0xd14:
+ if (((insn >> 22) & 3) == 0)
+ return 1;
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = new_tmp();
+ if (gen_iwmmxt_shift(insn, 0xff, tmp)) {
+ dead_tmp(tmp);
+ return 1;
+ }
+ switch ((insn >> 22) & 3) {
+ case 1:
+ gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 2:
+ gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 3:
+ gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ }
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */
+ case 0x314: case 0x714: case 0xb14: case 0xf14:
+ if (((insn >> 22) & 3) == 0)
+ return 1;
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = new_tmp();
+ switch ((insn >> 22) & 3) {
+ case 1:
+ if (gen_iwmmxt_shift(insn, 0xf, tmp)) {
+ dead_tmp(tmp);
+ return 1;
+ }
+ gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 2:
+ if (gen_iwmmxt_shift(insn, 0x1f, tmp)) {
+ dead_tmp(tmp);
+ return 1;
+ }
+ gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ case 3:
+ if (gen_iwmmxt_shift(insn, 0x3f, tmp)) {
+ dead_tmp(tmp);
+ return 1;
+ }
+ gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp);
+ break;
+ }
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */
+ case 0x916: case 0xb16: case 0xd16: case 0xf16:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_minsb_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_minub_M0_wRn(rd1);
+ break;
+ case 1:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_minsw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_minuw_M0_wRn(rd1);
+ break;
+ case 2:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_minsl_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_minul_M0_wRn(rd1);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */
+ case 0x816: case 0xa16: case 0xc16: case 0xe16:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 0:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_maxsb_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_maxub_M0_wRn(rd1);
+ break;
+ case 1:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_maxsw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_maxuw_M0_wRn(rd1);
+ break;
+ case 2:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_maxsl_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_maxul_M0_wRn(rd1);
+ break;
+ case 3:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */
+ case 0x402: case 0x502: case 0x602: case 0x702:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = tcg_const_i32((insn >> 20) & 3);
+ iwmmxt_load_reg(cpu_V1, rd1);
+ gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp);
+ tcg_temp_free(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */
+ case 0x41a: case 0x51a: case 0x61a: case 0x71a:
+ case 0x81a: case 0x91a: case 0xa1a: case 0xb1a:
+ case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 20) & 0xf) {
+ case 0x0:
+ gen_op_iwmmxt_subnb_M0_wRn(rd1);
+ break;
+ case 0x1:
+ gen_op_iwmmxt_subub_M0_wRn(rd1);
+ break;
+ case 0x3:
+ gen_op_iwmmxt_subsb_M0_wRn(rd1);
+ break;
+ case 0x4:
+ gen_op_iwmmxt_subnw_M0_wRn(rd1);
+ break;
+ case 0x5:
+ gen_op_iwmmxt_subuw_M0_wRn(rd1);
+ break;
+ case 0x7:
+ gen_op_iwmmxt_subsw_M0_wRn(rd1);
+ break;
+ case 0x8:
+ gen_op_iwmmxt_subnl_M0_wRn(rd1);
+ break;
+ case 0x9:
+ gen_op_iwmmxt_subul_M0_wRn(rd1);
+ break;
+ case 0xb:
+ gen_op_iwmmxt_subsl_M0_wRn(rd1);
+ break;
+ default:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */
+ case 0x41e: case 0x51e: case 0x61e: case 0x71e:
+ case 0x81e: case 0x91e: case 0xa1e: case 0xb1e:
+ case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f));
+ gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp);
+ tcg_temp_free(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */
+ case 0x418: case 0x518: case 0x618: case 0x718:
+ case 0x818: case 0x918: case 0xa18: case 0xb18:
+ case 0xc18: case 0xd18: case 0xe18: case 0xf18:
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 20) & 0xf) {
+ case 0x0:
+ gen_op_iwmmxt_addnb_M0_wRn(rd1);
+ break;
+ case 0x1:
+ gen_op_iwmmxt_addub_M0_wRn(rd1);
+ break;
+ case 0x3:
+ gen_op_iwmmxt_addsb_M0_wRn(rd1);
+ break;
+ case 0x4:
+ gen_op_iwmmxt_addnw_M0_wRn(rd1);
+ break;
+ case 0x5:
+ gen_op_iwmmxt_adduw_M0_wRn(rd1);
+ break;
+ case 0x7:
+ gen_op_iwmmxt_addsw_M0_wRn(rd1);
+ break;
+ case 0x8:
+ gen_op_iwmmxt_addnl_M0_wRn(rd1);
+ break;
+ case 0x9:
+ gen_op_iwmmxt_addul_M0_wRn(rd1);
+ break;
+ case 0xb:
+ gen_op_iwmmxt_addsl_M0_wRn(rd1);
+ break;
+ default:
+ return 1;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */
+ case 0x408: case 0x508: case 0x608: case 0x708:
+ case 0x808: case 0x908: case 0xa08: case 0xb08:
+ case 0xc08: case 0xd08: case 0xe08: case 0xf08:
+ if (!(insn & (1 << 20)) || ((insn >> 22) & 3) == 0)
+ return 1;
+ wrd = (insn >> 12) & 0xf;
+ rd0 = (insn >> 16) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ gen_op_iwmmxt_movq_M0_wRn(rd0);
+ switch ((insn >> 22) & 3) {
+ case 1:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_packsw_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_packuw_M0_wRn(rd1);
+ break;
+ case 2:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_packsl_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_packul_M0_wRn(rd1);
+ break;
+ case 3:
+ if (insn & (1 << 21))
+ gen_op_iwmmxt_packsq_M0_wRn(rd1);
+ else
+ gen_op_iwmmxt_packuq_M0_wRn(rd1);
+ break;
+ }
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ gen_op_iwmmxt_set_cup();
+ break;
+ case 0x201: case 0x203: case 0x205: case 0x207:
+ case 0x209: case 0x20b: case 0x20d: case 0x20f:
+ case 0x211: case 0x213: case 0x215: case 0x217:
+ case 0x219: case 0x21b: case 0x21d: case 0x21f:
+ wrd = (insn >> 5) & 0xf;
+ rd0 = (insn >> 12) & 0xf;
+ rd1 = (insn >> 0) & 0xf;
+ if (rd0 == 0xf || rd1 == 0xf)
+ return 1;
+ gen_op_iwmmxt_movq_M0_wRn(wrd);
+ tmp = load_reg(s, rd0);
+ tmp2 = load_reg(s, rd1);
+ switch ((insn >> 16) & 0xf) {
+ case 0x0: /* TMIA */
+ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2);
+ break;
+ case 0x8: /* TMIAPH */
+ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2);
+ break;
+ case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */
+ if (insn & (1 << 16))
+ tcg_gen_shri_i32(tmp, tmp, 16);
+ if (insn & (1 << 17))
+ tcg_gen_shri_i32(tmp2, tmp2, 16);
+ gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2);
+ break;
+ default:
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+ return 1;
+ }
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+ gen_op_iwmmxt_movq_wRn_M0(wrd);
+ gen_op_iwmmxt_set_mup();
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Disassemble an XScale DSP instruction. Returns nonzero if an error occured
+ (ie. an undefined instruction). */
+static int disas_dsp_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+ int acc, rd0, rd1, rdhi, rdlo;
+ TCGv tmp, tmp2;
+
+ if ((insn & 0x0ff00f10) == 0x0e200010) {
+ /* Multiply with Internal Accumulate Format */
+ rd0 = (insn >> 12) & 0xf;
+ rd1 = insn & 0xf;
+ acc = (insn >> 5) & 7;
+
+ if (acc != 0)
+ return 1;
+
+ tmp = load_reg(s, rd0);
+ tmp2 = load_reg(s, rd1);
+ switch ((insn >> 16) & 0xf) {
+ case 0x0: /* MIA */
+ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2);
+ break;
+ case 0x8: /* MIAPH */
+ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2);
+ break;
+ case 0xc: /* MIABB */
+ case 0xd: /* MIABT */
+ case 0xe: /* MIATB */
+ case 0xf: /* MIATT */
+ if (insn & (1 << 16))
+ tcg_gen_shri_i32(tmp, tmp, 16);
+ if (insn & (1 << 17))
+ tcg_gen_shri_i32(tmp2, tmp2, 16);
+ gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2);
+ break;
+ default:
+ return 1;
+ }
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+
+ gen_op_iwmmxt_movq_wRn_M0(acc);
+ return 0;
+ }
+
+ if ((insn & 0x0fe00ff8) == 0x0c400000) {
+ /* Internal Accumulator Access Format */
+ rdhi = (insn >> 16) & 0xf;
+ rdlo = (insn >> 12) & 0xf;
+ acc = insn & 7;
+
+ if (acc != 0)
+ return 1;
+
+ if (insn & ARM_CP_RW_BIT) { /* MRA */
+ iwmmxt_load_reg(cpu_V0, acc);
+ tcg_gen_trunc_i64_i32(cpu_R[rdlo], cpu_V0);
+ tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+ tcg_gen_trunc_i64_i32(cpu_R[rdhi], cpu_V0);
+ tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1);
+ } else { /* MAR */
+ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]);
+ iwmmxt_store_reg(cpu_V0, acc);
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Disassemble system coprocessor instruction. Return nonzero if
+ instruction is not defined. */
+static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+ TCGv tmp, tmp2;
+ uint32_t rd = (insn >> 12) & 0xf;
+ uint32_t cp = (insn >> 8) & 0xf;
+ if (IS_USER(s)) {
+ return 1;
+ }
+
+ if (insn & ARM_CP_RW_BIT) {
+ if (!env->cp[cp].cp_read)
+ return 1;
+ gen_set_pc_im(s->pc);
+ tmp = new_tmp();
+ tmp2 = tcg_const_i32(insn);
+ gen_helper_get_cp(tmp, cpu_env, tmp2);
+ tcg_temp_free(tmp2);
+ store_reg(s, rd, tmp);
+ } else {
+ if (!env->cp[cp].cp_write)
+ return 1;
+ gen_set_pc_im(s->pc);
+ tmp = load_reg(s, rd);
+ tmp2 = tcg_const_i32(insn);
+ gen_helper_set_cp(cpu_env, tmp2, tmp);
+ tcg_temp_free(tmp2);
+ dead_tmp(tmp);
+ }
+ return 0;
+}
+
+static int cp15_user_ok(uint32_t insn)
+{
+ int cpn = (insn >> 16) & 0xf;
+ int cpm = insn & 0xf;
+ int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
+
+ if (cpn == 13 && cpm == 0) {
+ /* TLS register. */
+ if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT)))
+ return 1;
+ }
+ if (cpn == 7) {
+ /* ISB, DSB, DMB. */
+ if ((cpm == 5 && op == 4)
+ || (cpm == 10 && (op == 4 || op == 5)))
+ return 1;
+ }
+ return 0;
+}
+
+static int cp15_tls_load_store(CPUState *env, DisasContext *s, uint32_t insn, uint32_t rd)
+{
+ TCGv tmp;
+ int cpn = (insn >> 16) & 0xf;
+ int cpm = insn & 0xf;
+ int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
+
+ if (!arm_feature(env, ARM_FEATURE_V6K))
+ return 0;
+
+ if (!(cpn == 13 && cpm == 0))
+ return 0;
+
+ if (insn & ARM_CP_RW_BIT) {
+ switch (op) {
+ case 2:
+ tmp = load_cpu_field(cp15.c13_tls1);
+ break;
+ case 3:
+ tmp = load_cpu_field(cp15.c13_tls2);
+ break;
+ case 4:
+ tmp = load_cpu_field(cp15.c13_tls3);
+ break;
+ default:
+ return 0;
+ }
+ store_reg(s, rd, tmp);
+
+ } else {
+ tmp = load_reg(s, rd);
+ switch (op) {
+ case 2:
+ store_cpu_field(tmp, cp15.c13_tls1);
+ break;
+ case 3:
+ store_cpu_field(tmp, cp15.c13_tls2);
+ break;
+ case 4:
+ store_cpu_field(tmp, cp15.c13_tls3);
+ break;
+ default:
+ dead_tmp(tmp);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Disassemble system coprocessor (cp15) instruction. Return nonzero if
+ instruction is not defined. */
+static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+ uint32_t rd;
+ TCGv tmp, tmp2;
+
+ /* M profile cores use memory mapped registers instead of cp15. */
+ if (arm_feature(env, ARM_FEATURE_M))
+ return 1;
+
+ if ((insn & (1 << 25)) == 0) {
+ if (insn & (1 << 20)) {
+ /* mrrc */
+ return 1;
+ }
+ /* mcrr. Used for block cache operations, so implement as no-op. */
+ return 0;
+ }
+ if ((insn & (1 << 4)) == 0) {
+ /* cdp */
+ return 1;
+ }
+ if (IS_USER(s) && !cp15_user_ok(insn)) {
+ return 1;
+ }
+ if ((insn & 0x0fff0fff) == 0x0e070f90
+ || (insn & 0x0fff0fff) == 0x0e070f58) {
+ /* Wait for interrupt. */
+ gen_set_pc_im(s->pc);
+ s->is_jmp = DISAS_WFI;
+ return 0;
+ }
+ rd = (insn >> 12) & 0xf;
+
+ if (cp15_tls_load_store(env, s, insn, rd))
+ return 0;
+
+ tmp2 = tcg_const_i32(insn);
+ if (insn & ARM_CP_RW_BIT) {
+ tmp = new_tmp();
+ gen_helper_get_cp15(tmp, cpu_env, tmp2);
+ /* If the destination register is r15 then sets condition codes. */
+ if (rd != 15)
+ store_reg(s, rd, tmp);
+ else
+ dead_tmp(tmp);
+ } else {
+ tmp = load_reg(s, rd);
+ gen_helper_set_cp15(cpu_env, tmp2, tmp);
+ dead_tmp(tmp);
+ /* Normally we would always end the TB here, but Linux
+ * arch/arm/mach-pxa/sleep.S expects two instructions following
+ * an MMU enable to execute from cache. Imitate this behaviour. */
+ if (!arm_feature(env, ARM_FEATURE_XSCALE) ||
+ (insn & 0x0fff0fff) != 0x0e010f10)
+ gen_lookup_tb(s);
+ }
+ tcg_temp_free_i32(tmp2);
+ return 0;
+}
+
+#define VFP_REG_SHR(x, n) (((n) > 0) ? (x) >> (n) : (x) << -(n))
+#define VFP_SREG(insn, bigbit, smallbit) \
+ ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1))
+#define VFP_DREG(reg, insn, bigbit, smallbit) do { \
+ if (arm_feature(env, ARM_FEATURE_VFP3)) { \
+ reg = (((insn) >> (bigbit)) & 0x0f) \
+ | (((insn) >> ((smallbit) - 4)) & 0x10); \
+ } else { \
+ if (insn & (1 << (smallbit))) \
+ return 1; \
+ reg = ((insn) >> (bigbit)) & 0x0f; \
+ }} while (0)
+
+#define VFP_SREG_D(insn) VFP_SREG(insn, 12, 22)
+#define VFP_DREG_D(reg, insn) VFP_DREG(reg, insn, 12, 22)
+#define VFP_SREG_N(insn) VFP_SREG(insn, 16, 7)
+#define VFP_DREG_N(reg, insn) VFP_DREG(reg, insn, 16, 7)
+#define VFP_SREG_M(insn) VFP_SREG(insn, 0, 5)
+#define VFP_DREG_M(reg, insn) VFP_DREG(reg, insn, 0, 5)
+
+/* Move between integer and VFP cores. */
+static TCGv gen_vfp_mrs(void)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_mov_i32(tmp, cpu_F0s);
+ return tmp;
+}
+
+static void gen_vfp_msr(TCGv tmp)
+{
+ tcg_gen_mov_i32(cpu_F0s, tmp);
+ dead_tmp(tmp);
+}
+
+static void gen_neon_dup_u8(TCGv var, int shift)
+{
+ TCGv tmp = new_tmp();
+ if (shift)
+ tcg_gen_shri_i32(var, var, shift);
+ tcg_gen_ext8u_i32(var, var);
+ tcg_gen_shli_i32(tmp, var, 8);
+ tcg_gen_or_i32(var, var, tmp);
+ tcg_gen_shli_i32(tmp, var, 16);
+ tcg_gen_or_i32(var, var, tmp);
+ dead_tmp(tmp);
+}
+
+static void gen_neon_dup_low16(TCGv var)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_ext16u_i32(var, var);
+ tcg_gen_shli_i32(tmp, var, 16);
+ tcg_gen_or_i32(var, var, tmp);
+ dead_tmp(tmp);
+}
+
+static void gen_neon_dup_high16(TCGv var)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_andi_i32(var, var, 0xffff0000);
+ tcg_gen_shri_i32(tmp, var, 16);
+ tcg_gen_or_i32(var, var, tmp);
+ dead_tmp(tmp);
+}
+
+/* Disassemble a VFP instruction. Returns nonzero if an error occured
+ (ie. an undefined instruction). */
+static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ uint32_t rd, rn, rm, op, i, n, offset, delta_d, delta_m, bank_mask;
+ int dp, veclen;
+ TCGv addr;
+ TCGv tmp;
+ TCGv tmp2;
+
+ if (!arm_feature(env, ARM_FEATURE_VFP))
+ return 1;
+
+ if (!s->vfp_enabled) {
+ /* VFP disabled. Only allow fmxr/fmrx to/from some control regs. */
+ if ((insn & 0x0fe00fff) != 0x0ee00a10)
+ return 1;
+ rn = (insn >> 16) & 0xf;
+ if (rn != ARM_VFP_FPSID && rn != ARM_VFP_FPEXC
+ && rn != ARM_VFP_MVFR1 && rn != ARM_VFP_MVFR0)
+ return 1;
+ }
+ dp = ((insn & 0xf00) == 0xb00);
+ switch ((insn >> 24) & 0xf) {
+ case 0xe:
+ if (insn & (1 << 4)) {
+ /* single register transfer */
+ rd = (insn >> 12) & 0xf;
+ if (dp) {
+ int size;
+ int pass;
+
+ VFP_DREG_N(rn, insn);
+ if (insn & 0xf)
+ return 1;
+ if (insn & 0x00c00060
+ && !arm_feature(env, ARM_FEATURE_NEON))
+ return 1;
+
+ pass = (insn >> 21) & 1;
+ if (insn & (1 << 22)) {
+ size = 0;
+ offset = ((insn >> 5) & 3) * 8;
+ } else if (insn & (1 << 5)) {
+ size = 1;
+ offset = (insn & (1 << 6)) ? 16 : 0;
+ } else {
+ size = 2;
+ offset = 0;
+ }
+ if (insn & ARM_CP_RW_BIT) {
+ /* vfp->arm */
+ tmp = neon_load_reg(rn, pass);
+ switch (size) {
+ case 0:
+ if (offset)
+ tcg_gen_shri_i32(tmp, tmp, offset);
+ if (insn & (1 << 23))
+ gen_uxtb(tmp);
+ else
+ gen_sxtb(tmp);
+ break;
+ case 1:
+ if (insn & (1 << 23)) {
+ if (offset) {
+ tcg_gen_shri_i32(tmp, tmp, 16);
+ } else {
+ gen_uxth(tmp);
+ }
+ } else {
+ if (offset) {
+ tcg_gen_sari_i32(tmp, tmp, 16);
+ } else {
+ gen_sxth(tmp);
+ }
+ }
+ break;
+ case 2:
+ break;
+ }
+ store_reg(s, rd, tmp);
+ } else {
+ /* arm->vfp */
+ tmp = load_reg(s, rd);
+ if (insn & (1 << 23)) {
+ /* VDUP */
+ if (size == 0) {
+ gen_neon_dup_u8(tmp, 0);
+ } else if (size == 1) {
+ gen_neon_dup_low16(tmp);
+ }
+ for (n = 0; n <= pass * 2; n++) {
+ tmp2 = new_tmp();
+ tcg_gen_mov_i32(tmp2, tmp);
+ neon_store_reg(rn, n, tmp2);
+ }
+ neon_store_reg(rn, n, tmp);
+ } else {
+ /* VMOV */
+ switch (size) {
+ case 0:
+ tmp2 = neon_load_reg(rn, pass);
+ gen_bfi(tmp, tmp2, tmp, offset, 0xff);
+ dead_tmp(tmp2);
+ break;
+ case 1:
+ tmp2 = neon_load_reg(rn, pass);
+ gen_bfi(tmp, tmp2, tmp, offset, 0xffff);
+ dead_tmp(tmp2);
+ break;
+ case 2:
+ break;
+ }
+ neon_store_reg(rn, pass, tmp);
+ }
+ }
+ } else { /* !dp */
+ if ((insn & 0x6f) != 0x00)
+ return 1;
+ rn = VFP_SREG_N(insn);
+ if (insn & ARM_CP_RW_BIT) {
+ /* vfp->arm */
+ if (insn & (1 << 21)) {
+ /* system register */
+ rn >>= 1;
+
+ switch (rn) {
+ case ARM_VFP_FPSID:
+ /* VFP2 allows access to FSID from userspace.
+ VFP3 restricts all id registers to privileged
+ accesses. */
+ if (IS_USER(s)
+ && arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ tmp = load_cpu_field(vfp.xregs[rn]);
+ break;
+ case ARM_VFP_FPEXC:
+ if (IS_USER(s))
+ return 1;
+ tmp = load_cpu_field(vfp.xregs[rn]);
+ break;
+ case ARM_VFP_FPINST:
+ case ARM_VFP_FPINST2:
+ /* Not present in VFP3. */
+ if (IS_USER(s)
+ || arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ tmp = load_cpu_field(vfp.xregs[rn]);
+ break;
+ case ARM_VFP_FPSCR:
+ if (rd == 15) {
+ tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
+ tcg_gen_andi_i32(tmp, tmp, 0xf0000000);
+ } else {
+ tmp = new_tmp();
+ gen_helper_vfp_get_fpscr(tmp, cpu_env);
+ }
+ break;
+ case ARM_VFP_MVFR0:
+ case ARM_VFP_MVFR1:
+ if (IS_USER(s)
+ || !arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ tmp = load_cpu_field(vfp.xregs[rn]);
+ break;
+ default:
+ return 1;
+ }
+ } else {
+ gen_mov_F0_vreg(0, rn);
+ tmp = gen_vfp_mrs();
+ }
+ if (rd == 15) {
+ /* Set the 4 flag bits in the CPSR. */
+ gen_set_nzcv(tmp);
+ dead_tmp(tmp);
+ } else {
+ store_reg(s, rd, tmp);
+ }
+ } else {
+ /* arm->vfp */
+ tmp = load_reg(s, rd);
+ if (insn & (1 << 21)) {
+ rn >>= 1;
+ /* system register */
+ switch (rn) {
+ case ARM_VFP_FPSID:
+ case ARM_VFP_MVFR0:
+ case ARM_VFP_MVFR1:
+ /* Writes are ignored. */
+ break;
+ case ARM_VFP_FPSCR:
+ gen_helper_vfp_set_fpscr(cpu_env, tmp);
+ dead_tmp(tmp);
+ gen_lookup_tb(s);
+ break;
+ case ARM_VFP_FPEXC:
+ if (IS_USER(s))
+ return 1;
+ /* TODO: VFP subarchitecture support.
+ * For now, keep the EN bit only */
+ tcg_gen_andi_i32(tmp, tmp, 1 << 30);
+ store_cpu_field(tmp, vfp.xregs[rn]);
+ gen_lookup_tb(s);
+ break;
+ case ARM_VFP_FPINST:
+ case ARM_VFP_FPINST2:
+ store_cpu_field(tmp, vfp.xregs[rn]);
+ break;
+ default:
+ return 1;
+ }
+ } else {
+ gen_vfp_msr(tmp);
+ gen_mov_vreg_F0(0, rn);
+ }
+ }
+ }
+ } else {
+ /* data processing */
+ /* The opcode is in bits 23, 21, 20 and 6. */
+ op = ((insn >> 20) & 8) | ((insn >> 19) & 6) | ((insn >> 6) & 1);
+ if (dp) {
+ if (op == 15) {
+ /* rn is opcode */
+ rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
+ } else {
+ /* rn is register number */
+ VFP_DREG_N(rn, insn);
+ }
+
+ if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18))) {
+ /* Integer or single precision destination. */
+ rd = VFP_SREG_D(insn);
+ } else {
+ VFP_DREG_D(rd, insn);
+ }
+ if (op == 15 &&
+ (((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14))) {
+ /* VCVT from int is always from S reg regardless of dp bit.
+ * VCVT with immediate frac_bits has same format as SREG_M
+ */
+ rm = VFP_SREG_M(insn);
+ } else {
+ VFP_DREG_M(rm, insn);
+ }
+ } else {
+ rn = VFP_SREG_N(insn);
+ if (op == 15 && rn == 15) {
+ /* Double precision destination. */
+ VFP_DREG_D(rd, insn);
+ } else {
+ rd = VFP_SREG_D(insn);
+ }
+ /* NB that we implicitly rely on the encoding for the frac_bits
+ * in VCVT of fixed to float being the same as that of an SREG_M
+ */
+ rm = VFP_SREG_M(insn);
+ }
+
+ veclen = s->vec_len;
+ if (op == 15 && rn > 3)
+ veclen = 0;
+
+ /* Shut up compiler warnings. */
+ delta_m = 0;
+ delta_d = 0;
+ bank_mask = 0;
+
+ if (veclen > 0) {
+ if (dp)
+ bank_mask = 0xc;
+ else
+ bank_mask = 0x18;
+
+ /* Figure out what type of vector operation this is. */
+ if ((rd & bank_mask) == 0) {
+ /* scalar */
+ veclen = 0;
+ } else {
+ if (dp)
+ delta_d = (s->vec_stride >> 1) + 1;
+ else
+ delta_d = s->vec_stride + 1;
+
+ if ((rm & bank_mask) == 0) {
+ /* mixed scalar/vector */
+ delta_m = 0;
+ } else {
+ /* vector */
+ delta_m = delta_d;
+ }
+ }
+ }
+
+ /* Load the initial operands. */
+ if (op == 15) {
+ switch (rn) {
+ case 16:
+ case 17:
+ /* Integer source */
+ gen_mov_F0_vreg(0, rm);
+ break;
+ case 8:
+ case 9:
+ /* Compare */
+ gen_mov_F0_vreg(dp, rd);
+ gen_mov_F1_vreg(dp, rm);
+ break;
+ case 10:
+ case 11:
+ /* Compare with zero */
+ gen_mov_F0_vreg(dp, rd);
+ gen_vfp_F1_ld0(dp);
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ /* Source and destination the same. */
+ gen_mov_F0_vreg(dp, rd);
+ break;
+ default:
+ /* One source operand. */
+ gen_mov_F0_vreg(dp, rm);
+ break;
+ }
+ } else {
+ /* Two source operands. */
+ gen_mov_F0_vreg(dp, rn);
+ gen_mov_F1_vreg(dp, rm);
+ }
+
+ for (;;) {
+ /* Perform the calculation. */
+ switch (op) {
+ case 0: /* mac: fd + (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_add(dp);
+ break;
+ case 1: /* nmac: fd - (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_vfp_neg(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_add(dp);
+ break;
+ case 2: /* msc: -fd + (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_sub(dp);
+ break;
+ case 3: /* nmsc: -fd - (fn * fm) */
+ gen_vfp_mul(dp);
+ gen_vfp_neg(dp);
+ gen_mov_F1_vreg(dp, rd);
+ gen_vfp_sub(dp);
+ break;
+ case 4: /* mul: fn * fm */
+ gen_vfp_mul(dp);
+ break;
+ case 5: /* nmul: -(fn * fm) */
+ gen_vfp_mul(dp);
+ gen_vfp_neg(dp);
+ break;
+ case 6: /* add: fn + fm */
+ gen_vfp_add(dp);
+ break;
+ case 7: /* sub: fn - fm */
+ gen_vfp_sub(dp);
+ break;
+ case 8: /* div: fn / fm */
+ gen_vfp_div(dp);
+ break;
+ case 14: /* fconst */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+
+ n = (insn << 12) & 0x80000000;
+ i = ((insn >> 12) & 0x70) | (insn & 0xf);
+ if (dp) {
+ if (i & 0x40)
+ i |= 0x3f80;
+ else
+ i |= 0x4000;
+ n |= i << 16;
+ tcg_gen_movi_i64(cpu_F0d, ((uint64_t)n) << 32);
+ } else {
+ if (i & 0x40)
+ i |= 0x780;
+ else
+ i |= 0x800;
+ n |= i << 19;
+ tcg_gen_movi_i32(cpu_F0s, n);
+ }
+ break;
+ case 15: /* extension space */
+ switch (rn) {
+ case 0: /* cpy */
+ /* no-op */
+ break;
+ case 1: /* abs */
+ gen_vfp_abs(dp);
+ break;
+ case 2: /* neg */
+ gen_vfp_neg(dp);
+ break;
+ case 3: /* sqrt */
+ gen_vfp_sqrt(dp);
+ break;
+ case 4: /* vcvtb.f32.f16 */
+ if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
+ return 1;
+ tmp = gen_vfp_mrs();
+ tcg_gen_ext16u_i32(tmp, tmp);
+ gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, cpu_env);
+ dead_tmp(tmp);
+ break;
+ case 5: /* vcvtt.f32.f16 */
+ if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
+ return 1;
+ tmp = gen_vfp_mrs();
+ tcg_gen_shri_i32(tmp, tmp, 16);
+ gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, cpu_env);
+ dead_tmp(tmp);
+ break;
+ case 6: /* vcvtb.f16.f32 */
+ if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
+ return 1;
+ tmp = new_tmp();
+ gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env);
+ gen_mov_F0_vreg(0, rd);
+ tmp2 = gen_vfp_mrs();
+ tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ gen_vfp_msr(tmp);
+ break;
+ case 7: /* vcvtt.f16.f32 */
+ if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
+ return 1;
+ tmp = new_tmp();
+ gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env);
+ tcg_gen_shli_i32(tmp, tmp, 16);
+ gen_mov_F0_vreg(0, rd);
+ tmp2 = gen_vfp_mrs();
+ tcg_gen_ext16u_i32(tmp2, tmp2);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ gen_vfp_msr(tmp);
+ break;
+ case 8: /* cmp */
+ gen_vfp_cmp(dp);
+ break;
+ case 9: /* cmpe */
+ gen_vfp_cmpe(dp);
+ break;
+ case 10: /* cmpz */
+ gen_vfp_cmp(dp);
+ break;
+ case 11: /* cmpez */
+ gen_vfp_F1_ld0(dp);
+ gen_vfp_cmpe(dp);
+ break;
+ case 15: /* single<->double conversion */
+ if (dp)
+ gen_helper_vfp_fcvtsd(cpu_F0s, cpu_F0d, cpu_env);
+ else
+ gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env);
+ break;
+ case 16: /* fuito */
+ gen_vfp_uito(dp);
+ break;
+ case 17: /* fsito */
+ gen_vfp_sito(dp);
+ break;
+ case 20: /* fshto */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_shto(dp, 16 - rm);
+ break;
+ case 21: /* fslto */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_slto(dp, 32 - rm);
+ break;
+ case 22: /* fuhto */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_uhto(dp, 16 - rm);
+ break;
+ case 23: /* fulto */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_ulto(dp, 32 - rm);
+ break;
+ case 24: /* ftoui */
+ gen_vfp_toui(dp);
+ break;
+ case 25: /* ftouiz */
+ gen_vfp_touiz(dp);
+ break;
+ case 26: /* ftosi */
+ gen_vfp_tosi(dp);
+ break;
+ case 27: /* ftosiz */
+ gen_vfp_tosiz(dp);
+ break;
+ case 28: /* ftosh */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_tosh(dp, 16 - rm);
+ break;
+ case 29: /* ftosl */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_tosl(dp, 32 - rm);
+ break;
+ case 30: /* ftouh */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_touh(dp, 16 - rm);
+ break;
+ case 31: /* ftoul */
+ if (!arm_feature(env, ARM_FEATURE_VFP3))
+ return 1;
+ gen_vfp_toul(dp, 32 - rm);
+ break;
+ default: /* undefined */
+ printf ("rn:%d\n", rn);
+ return 1;
+ }
+ break;
+ default: /* undefined */
+ printf ("op:%d\n", op);
+ return 1;
+ }
+
+ /* Write back the result. */
+ if (op == 15 && (rn >= 8 && rn <= 11))
+ ; /* Comparison, do nothing. */
+ else if (op == 15 && dp && ((rn & 0x1c) == 0x18))
+ /* VCVT double to int: always integer result. */
+ gen_mov_vreg_F0(0, rd);
+ else if (op == 15 && rn == 15)
+ /* conversion */
+ gen_mov_vreg_F0(!dp, rd);
+ else
+ gen_mov_vreg_F0(dp, rd);
+
+ /* break out of the loop if we have finished */
+ if (veclen == 0)
+ break;
+
+ if (op == 15 && delta_m == 0) {
+ /* single source one-many */
+ while (veclen--) {
+ rd = ((rd + delta_d) & (bank_mask - 1))
+ | (rd & bank_mask);
+ gen_mov_vreg_F0(dp, rd);
+ }
+ break;
+ }
+ /* Setup the next operands. */
+ veclen--;
+ rd = ((rd + delta_d) & (bank_mask - 1))
+ | (rd & bank_mask);
+
+ if (op == 15) {
+ /* One source operand. */
+ rm = ((rm + delta_m) & (bank_mask - 1))
+ | (rm & bank_mask);
+ gen_mov_F0_vreg(dp, rm);
+ } else {
+ /* Two source operands. */
+ rn = ((rn + delta_d) & (bank_mask - 1))
+ | (rn & bank_mask);
+ gen_mov_F0_vreg(dp, rn);
+ if (delta_m) {
+ rm = ((rm + delta_m) & (bank_mask - 1))
+ | (rm & bank_mask);
+ gen_mov_F1_vreg(dp, rm);
+ }
+ }
+ }
+ }
+ break;
+ case 0xc:
+ case 0xd:
+ if (dp && (insn & 0x03e00000) == 0x00400000) {
+ /* two-register transfer */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ if (dp) {
+ VFP_DREG_M(rm, insn);
+ } else {
+ rm = VFP_SREG_M(insn);
+ }
+
+ if (insn & ARM_CP_RW_BIT) {
+ /* vfp->arm */
+ if (dp) {
+ gen_mov_F0_vreg(0, rm * 2);
+ tmp = gen_vfp_mrs();
+ store_reg(s, rd, tmp);
+ gen_mov_F0_vreg(0, rm * 2 + 1);
+ tmp = gen_vfp_mrs();
+ store_reg(s, rn, tmp);
+ } else {
+ gen_mov_F0_vreg(0, rm);
+ tmp = gen_vfp_mrs();
+ store_reg(s, rn, tmp);
+ gen_mov_F0_vreg(0, rm + 1);
+ tmp = gen_vfp_mrs();
+ store_reg(s, rd, tmp);
+ }
+ } else {
+ /* arm->vfp */
+ if (dp) {
+ tmp = load_reg(s, rd);
+ gen_vfp_msr(tmp);
+ gen_mov_vreg_F0(0, rm * 2);
+ tmp = load_reg(s, rn);
+ gen_vfp_msr(tmp);
+ gen_mov_vreg_F0(0, rm * 2 + 1);
+ } else {
+ tmp = load_reg(s, rn);
+ gen_vfp_msr(tmp);
+ gen_mov_vreg_F0(0, rm);
+ tmp = load_reg(s, rd);
+ gen_vfp_msr(tmp);
+ gen_mov_vreg_F0(0, rm + 1);
+ }
+ }
+ } else {
+ /* Load/store */
+ rn = (insn >> 16) & 0xf;
+ if (dp)
+ VFP_DREG_D(rd, insn);
+ else
+ rd = VFP_SREG_D(insn);
+ if (s->thumb && rn == 15) {
+ addr = new_tmp();
+ tcg_gen_movi_i32(addr, s->pc & ~2);
+ } else {
+ addr = load_reg(s, rn);
+ }
+ if ((insn & 0x01200000) == 0x01000000) {
+ /* Single load/store */
+ offset = (insn & 0xff) << 2;
+ if ((insn & (1 << 23)) == 0)
+ offset = -offset;
+ tcg_gen_addi_i32(addr, addr, offset);
+ if (insn & (1 << 20)) {
+ gen_vfp_ld(s, dp, addr);
+ gen_mov_vreg_F0(dp, rd);
+ } else {
+ gen_mov_F0_vreg(dp, rd);
+ gen_vfp_st(s, dp, addr);
+ }
+ dead_tmp(addr);
+ } else {
+ /* load/store multiple */
+ if (dp)
+ n = (insn >> 1) & 0x7f;
+ else
+ n = insn & 0xff;
+
+ if (insn & (1 << 24)) /* pre-decrement */
+ tcg_gen_addi_i32(addr, addr, -((insn & 0xff) << 2));
+
+ if (dp)
+ offset = 8;
+ else
+ offset = 4;
+ for (i = 0; i < n; i++) {
+ if (insn & ARM_CP_RW_BIT) {
+ /* load */
+ gen_vfp_ld(s, dp, addr);
+ gen_mov_vreg_F0(dp, rd + i);
+ } else {
+ /* store */
+ gen_mov_F0_vreg(dp, rd + i);
+ gen_vfp_st(s, dp, addr);
+ }
+ tcg_gen_addi_i32(addr, addr, offset);
+ }
+ if (insn & (1 << 21)) {
+ /* writeback */
+ if (insn & (1 << 24))
+ offset = -offset * n;
+ else if (dp && (insn & 1))
+ offset = 4;
+ else
+ offset = 0;
+
+ if (offset != 0)
+ tcg_gen_addi_i32(addr, addr, offset);
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ }
+ }
+ break;
+ default:
+ /* Should never happen. */
+ return 1;
+ }
+ return 0;
+}
+
+static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest)
+{
+ TranslationBlock *tb;
+
+ tb = s->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ tcg_gen_goto_tb(n);
+ gen_set_pc_im(dest);
+ tcg_gen_exit_tb((long)tb + n);
+ } else {
+ gen_set_pc_im(dest);
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static inline void gen_jmp (DisasContext *s, uint32_t dest)
+{
+ if (unlikely(s->singlestep_enabled)) {
+ /* An indirect jump so that we still trigger the debug exception. */
+ if (s->thumb)
+ dest |= 1;
+ gen_bx_im(s, dest);
+ } else {
+ gen_goto_tb(s, 0, dest);
+ s->is_jmp = DISAS_TB_JUMP;
+ }
+}
+
+static inline void gen_mulxy(TCGv t0, TCGv t1, int x, int y)
+{
+ if (x)
+ tcg_gen_sari_i32(t0, t0, 16);
+ else
+ gen_sxth(t0);
+ if (y)
+ tcg_gen_sari_i32(t1, t1, 16);
+ else
+ gen_sxth(t1);
+ tcg_gen_mul_i32(t0, t0, t1);
+}
+
+/* Return the mask of PSR bits set by a MSR instruction. */
+static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
+ uint32_t mask;
+
+ mask = 0;
+ if (flags & (1 << 0))
+ mask |= 0xff;
+ if (flags & (1 << 1))
+ mask |= 0xff00;
+ if (flags & (1 << 2))
+ mask |= 0xff0000;
+ if (flags & (1 << 3))
+ mask |= 0xff000000;
+
+ /* Mask out undefined bits. */
+ mask &= ~CPSR_RESERVED;
+ if (!arm_feature(env, ARM_FEATURE_V6))
+ mask &= ~(CPSR_E | CPSR_GE);
+ if (!arm_feature(env, ARM_FEATURE_THUMB2))
+ mask &= ~CPSR_IT;
+ /* Mask out execution state bits. */
+ if (!spsr)
+ mask &= ~CPSR_EXEC;
+ /* Mask out privileged bits. */
+ if (IS_USER(s))
+ mask &= CPSR_USER;
+ return mask;
+}
+
+/* Returns nonzero if access to the PSR is not permitted. Marks t0 as dead. */
+static int gen_set_psr(DisasContext *s, uint32_t mask, int spsr, TCGv t0)
+{
+ TCGv tmp;
+ if (spsr) {
+ /* ??? This is also undefined in system mode. */
+ if (IS_USER(s))
+ return 1;
+
+ tmp = load_cpu_field(spsr);
+ tcg_gen_andi_i32(tmp, tmp, ~mask);
+ tcg_gen_andi_i32(t0, t0, mask);
+ tcg_gen_or_i32(tmp, tmp, t0);
+ store_cpu_field(tmp, spsr);
+ } else {
+ gen_set_cpsr(t0, mask);
+ }
+ dead_tmp(t0);
+ gen_lookup_tb(s);
+ return 0;
+}
+
+/* Returns nonzero if access to the PSR is not permitted. */
+static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val)
+{
+ TCGv tmp;
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, val);
+ return gen_set_psr(s, mask, spsr, tmp);
+}
+
+/* Generate an old-style exception return. Marks pc as dead. */
+static void gen_exception_return(DisasContext *s, TCGv pc)
+{
+ TCGv tmp;
+ store_reg(s, 15, pc);
+ tmp = load_cpu_field(spsr);
+ gen_set_cpsr(tmp, 0xffffffff);
+ dead_tmp(tmp);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+/* Generate a v6 exception return. Marks both values as dead. */
+static void gen_rfe(DisasContext *s, TCGv pc, TCGv cpsr)
+{
+ gen_set_cpsr(cpsr, 0xffffffff);
+ dead_tmp(cpsr);
+ store_reg(s, 15, pc);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+static inline void
+gen_set_condexec (DisasContext *s)
+{
+ if (s->condexec_mask) {
+ uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1);
+ TCGv tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, val);
+ store_cpu_field(tmp, condexec_bits);
+ }
+}
+
+static void gen_exception_insn(DisasContext *s, int offset, int excp)
+{
+ gen_set_condexec(s);
+ gen_set_pc_im(s->pc - offset);
+ gen_exception(excp);
+ s->is_jmp = DISAS_JUMP;
+}
+
+static void gen_nop_hint(DisasContext *s, int val)
+{
+ switch (val) {
+ case 3: /* wfi */
+ gen_set_pc_im(s->pc);
+ s->is_jmp = DISAS_WFI;
+ break;
+ case 2: /* wfe */
+ case 4: /* sev */
+ /* TODO: Implement SEV and WFE. May help SMP performance. */
+ default: /* nop */
+ break;
+ }
+}
+
+#define CPU_V001 cpu_V0, cpu_V0, cpu_V1
+
+static inline int gen_neon_add(int size, TCGv t0, TCGv t1)
+{
+ switch (size) {
+ case 0: gen_helper_neon_add_u8(t0, t0, t1); break;
+ case 1: gen_helper_neon_add_u16(t0, t0, t1); break;
+ case 2: tcg_gen_add_i32(t0, t0, t1); break;
+ default: return 1;
+ }
+ return 0;
+}
+
+static inline void gen_neon_rsb(int size, TCGv t0, TCGv t1)
+{
+ switch (size) {
+ case 0: gen_helper_neon_sub_u8(t0, t1, t0); break;
+ case 1: gen_helper_neon_sub_u16(t0, t1, t0); break;
+ case 2: tcg_gen_sub_i32(t0, t1, t0); break;
+ default: return;
+ }
+}
+
+/* 32-bit pairwise ops end up the same as the elementwise versions. */
+#define gen_helper_neon_pmax_s32 gen_helper_neon_max_s32
+#define gen_helper_neon_pmax_u32 gen_helper_neon_max_u32
+#define gen_helper_neon_pmin_s32 gen_helper_neon_min_s32
+#define gen_helper_neon_pmin_u32 gen_helper_neon_min_u32
+
+#define GEN_NEON_INTEGER_OP_ENV(name) do { \
+ switch ((size << 1) | u) { \
+ case 0: \
+ gen_helper_neon_##name##_s8(tmp, cpu_env, tmp, tmp2); \
+ break; \
+ case 1: \
+ gen_helper_neon_##name##_u8(tmp, cpu_env, tmp, tmp2); \
+ break; \
+ case 2: \
+ gen_helper_neon_##name##_s16(tmp, cpu_env, tmp, tmp2); \
+ break; \
+ case 3: \
+ gen_helper_neon_##name##_u16(tmp, cpu_env, tmp, tmp2); \
+ break; \
+ case 4: \
+ gen_helper_neon_##name##_s32(tmp, cpu_env, tmp, tmp2); \
+ break; \
+ case 5: \
+ gen_helper_neon_##name##_u32(tmp, cpu_env, tmp, tmp2); \
+ break; \
+ default: return 1; \
+ }} while (0)
+
+#define GEN_NEON_INTEGER_OP(name) do { \
+ switch ((size << 1) | u) { \
+ case 0: \
+ gen_helper_neon_##name##_s8(tmp, tmp, tmp2); \
+ break; \
+ case 1: \
+ gen_helper_neon_##name##_u8(tmp, tmp, tmp2); \
+ break; \
+ case 2: \
+ gen_helper_neon_##name##_s16(tmp, tmp, tmp2); \
+ break; \
+ case 3: \
+ gen_helper_neon_##name##_u16(tmp, tmp, tmp2); \
+ break; \
+ case 4: \
+ gen_helper_neon_##name##_s32(tmp, tmp, tmp2); \
+ break; \
+ case 5: \
+ gen_helper_neon_##name##_u32(tmp, tmp, tmp2); \
+ break; \
+ default: return 1; \
+ }} while (0)
+
+static TCGv neon_load_scratch(int scratch)
+{
+ TCGv tmp = new_tmp();
+ tcg_gen_ld_i32(tmp, cpu_env, offsetof(CPUARMState, vfp.scratch[scratch]));
+ return tmp;
+}
+
+static void neon_store_scratch(int scratch, TCGv var)
+{
+ tcg_gen_st_i32(var, cpu_env, offsetof(CPUARMState, vfp.scratch[scratch]));
+ dead_tmp(var);
+}
+
+static inline TCGv neon_get_scalar(int size, int reg)
+{
+ TCGv tmp;
+ if (size == 1) {
+ tmp = neon_load_reg(reg & 7, reg >> 4);
+ if (reg & 8) {
+ gen_neon_dup_high16(tmp);
+ } else {
+ gen_neon_dup_low16(tmp);
+ }
+ } else {
+ tmp = neon_load_reg(reg & 15, reg >> 4);
+ }
+ return tmp;
+}
+
+static void gen_neon_unzip_u8(TCGv t0, TCGv t1)
+{
+ TCGv rd, rm, tmp;
+
+ rd = new_tmp();
+ rm = new_tmp();
+ tmp = new_tmp();
+
+ tcg_gen_andi_i32(rd, t0, 0xff);
+ tcg_gen_shri_i32(tmp, t0, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0xff00);
+ tcg_gen_or_i32(rd, rd, tmp);
+ tcg_gen_shli_i32(tmp, t1, 16);
+ tcg_gen_andi_i32(tmp, tmp, 0xff0000);
+ tcg_gen_or_i32(rd, rd, tmp);
+ tcg_gen_shli_i32(tmp, t1, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0xff000000);
+ tcg_gen_or_i32(rd, rd, tmp);
+
+ tcg_gen_shri_i32(rm, t0, 8);
+ tcg_gen_andi_i32(rm, rm, 0xff);
+ tcg_gen_shri_i32(tmp, t0, 16);
+ tcg_gen_andi_i32(tmp, tmp, 0xff00);
+ tcg_gen_or_i32(rm, rm, tmp);
+ tcg_gen_shli_i32(tmp, t1, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0xff0000);
+ tcg_gen_or_i32(rm, rm, tmp);
+ tcg_gen_andi_i32(tmp, t1, 0xff000000);
+ tcg_gen_or_i32(t1, rm, tmp);
+ tcg_gen_mov_i32(t0, rd);
+
+ dead_tmp(tmp);
+ dead_tmp(rm);
+ dead_tmp(rd);
+}
+
+static void gen_neon_zip_u8(TCGv t0, TCGv t1)
+{
+ TCGv rd, rm, tmp;
+
+ rd = new_tmp();
+ rm = new_tmp();
+ tmp = new_tmp();
+
+ tcg_gen_andi_i32(rd, t0, 0xff);
+ tcg_gen_shli_i32(tmp, t1, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0xff00);
+ tcg_gen_or_i32(rd, rd, tmp);
+ tcg_gen_shli_i32(tmp, t0, 16);
+ tcg_gen_andi_i32(tmp, tmp, 0xff0000);
+ tcg_gen_or_i32(rd, rd, tmp);
+ tcg_gen_shli_i32(tmp, t1, 24);
+ tcg_gen_andi_i32(tmp, tmp, 0xff000000);
+ tcg_gen_or_i32(rd, rd, tmp);
+
+ tcg_gen_andi_i32(rm, t1, 0xff000000);
+ tcg_gen_shri_i32(tmp, t0, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0xff0000);
+ tcg_gen_or_i32(rm, rm, tmp);
+ tcg_gen_shri_i32(tmp, t1, 8);
+ tcg_gen_andi_i32(tmp, tmp, 0xff00);
+ tcg_gen_or_i32(rm, rm, tmp);
+ tcg_gen_shri_i32(tmp, t0, 16);
+ tcg_gen_andi_i32(tmp, tmp, 0xff);
+ tcg_gen_or_i32(t1, rm, tmp);
+ tcg_gen_mov_i32(t0, rd);
+
+ dead_tmp(tmp);
+ dead_tmp(rm);
+ dead_tmp(rd);
+}
+
+static void gen_neon_zip_u16(TCGv t0, TCGv t1)
+{
+ TCGv tmp, tmp2;
+
+ tmp = new_tmp();
+ tmp2 = new_tmp();
+
+ tcg_gen_andi_i32(tmp, t0, 0xffff);
+ tcg_gen_shli_i32(tmp2, t1, 16);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ tcg_gen_andi_i32(t1, t1, 0xffff0000);
+ tcg_gen_shri_i32(tmp2, t0, 16);
+ tcg_gen_or_i32(t1, t1, tmp2);
+ tcg_gen_mov_i32(t0, tmp);
+
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+}
+
+static void gen_neon_unzip(int reg, int q, int tmp, int size)
+{
+ int n;
+ TCGv t0, t1;
+
+ for (n = 0; n < q + 1; n += 2) {
+ t0 = neon_load_reg(reg, n);
+ t1 = neon_load_reg(reg, n + 1);
+ switch (size) {
+ case 0: gen_neon_unzip_u8(t0, t1); break;
+ case 1: gen_neon_zip_u16(t0, t1); break; /* zip and unzip are the same. */
+ case 2: /* no-op */; break;
+ default: abort();
+ }
+ neon_store_scratch(tmp + n, t0);
+ neon_store_scratch(tmp + n + 1, t1);
+ }
+}
+
+static void gen_neon_trn_u8(TCGv t0, TCGv t1)
+{
+ TCGv rd, tmp;
+
+ rd = new_tmp();
+ tmp = new_tmp();
+
+ tcg_gen_shli_i32(rd, t0, 8);
+ tcg_gen_andi_i32(rd, rd, 0xff00ff00);
+ tcg_gen_andi_i32(tmp, t1, 0x00ff00ff);
+ tcg_gen_or_i32(rd, rd, tmp);
+
+ tcg_gen_shri_i32(t1, t1, 8);
+ tcg_gen_andi_i32(t1, t1, 0x00ff00ff);
+ tcg_gen_andi_i32(tmp, t0, 0xff00ff00);
+ tcg_gen_or_i32(t1, t1, tmp);
+ tcg_gen_mov_i32(t0, rd);
+
+ dead_tmp(tmp);
+ dead_tmp(rd);
+}
+
+static void gen_neon_trn_u16(TCGv t0, TCGv t1)
+{
+ TCGv rd, tmp;
+
+ rd = new_tmp();
+ tmp = new_tmp();
+
+ tcg_gen_shli_i32(rd, t0, 16);
+ tcg_gen_andi_i32(tmp, t1, 0xffff);
+ tcg_gen_or_i32(rd, rd, tmp);
+ tcg_gen_shri_i32(t1, t1, 16);
+ tcg_gen_andi_i32(tmp, t0, 0xffff0000);
+ tcg_gen_or_i32(t1, t1, tmp);
+ tcg_gen_mov_i32(t0, rd);
+
+ dead_tmp(tmp);
+ dead_tmp(rd);
+}
+
+
+static struct {
+ int nregs;
+ int interleave;
+ int spacing;
+} neon_ls_element_type[11] = {
+ {4, 4, 1},
+ {4, 4, 2},
+ {4, 1, 1},
+ {4, 2, 1},
+ {3, 3, 1},
+ {3, 3, 2},
+ {3, 1, 1},
+ {1, 1, 1},
+ {2, 2, 1},
+ {2, 2, 2},
+ {2, 1, 1}
+};
+
+/* Translate a NEON load/store element instruction. Return nonzero if the
+ instruction is invalid. */
+static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ int rd, rn, rm;
+ int op;
+ int nregs;
+ int interleave;
+ int spacing;
+ int stride;
+ int size;
+ int reg;
+ int pass;
+ int load;
+ int shift;
+ int n;
+ TCGv addr;
+ TCGv tmp;
+ TCGv tmp2;
+ TCGv_i64 tmp64;
+
+ if (!s->vfp_enabled)
+ return 1;
+ VFP_DREG_D(rd, insn);
+ rn = (insn >> 16) & 0xf;
+ rm = insn & 0xf;
+ load = (insn & (1 << 21)) != 0;
+ addr = new_tmp();
+ if ((insn & (1 << 23)) == 0) {
+ /* Load store all elements. */
+ op = (insn >> 8) & 0xf;
+ size = (insn >> 6) & 3;
+ if (op > 10)
+ return 1;
+ nregs = neon_ls_element_type[op].nregs;
+ interleave = neon_ls_element_type[op].interleave;
+ spacing = neon_ls_element_type[op].spacing;
+ if (size == 3 && (interleave | spacing) != 1)
+ return 1;
+ load_reg_var(s, addr, rn);
+ stride = (1 << size) * interleave;
+ for (reg = 0; reg < nregs; reg++) {
+ if (interleave > 2 || (interleave == 2 && nregs == 2)) {
+ load_reg_var(s, addr, rn);
+ tcg_gen_addi_i32(addr, addr, (1 << size) * reg);
+ } else if (interleave == 2 && nregs == 4 && reg == 2) {
+ load_reg_var(s, addr, rn);
+ tcg_gen_addi_i32(addr, addr, 1 << size);
+ }
+ if (size == 3) {
+ if (load) {
+ tmp64 = gen_ld64(addr, IS_USER(s));
+ neon_store_reg64(tmp64, rd);
+ tcg_temp_free_i64(tmp64);
+ } else {
+ tmp64 = tcg_temp_new_i64();
+ neon_load_reg64(tmp64, rd);
+ gen_st64(tmp64, addr, IS_USER(s));
+ }
+ tcg_gen_addi_i32(addr, addr, stride);
+ } else {
+ for (pass = 0; pass < 2; pass++) {
+ if (size == 2) {
+ if (load) {
+ tmp = gen_ld32(addr, IS_USER(s));
+ neon_store_reg(rd, pass, tmp);
+ } else {
+ tmp = neon_load_reg(rd, pass);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ tcg_gen_addi_i32(addr, addr, stride);
+ } else if (size == 1) {
+ if (load) {
+ tmp = gen_ld16u(addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, stride);
+ tmp2 = gen_ld16u(addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, stride);
+ tcg_gen_shli_i32(tmp2, tmp2, 16);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ neon_store_reg(rd, pass, tmp);
+ } else {
+ tmp = neon_load_reg(rd, pass);
+ tmp2 = new_tmp();
+ tcg_gen_shri_i32(tmp2, tmp, 16);
+ gen_st16(tmp, addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, stride);
+ gen_st16(tmp2, addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, stride);
+ }
+ } else /* size == 0 */ {
+ if (load) {
+ TCGV_UNUSED(tmp2);
+ for (n = 0; n < 4; n++) {
+ tmp = gen_ld8u(addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, stride);
+ if (n == 0) {
+ tmp2 = tmp;
+ } else {
+ tcg_gen_shli_i32(tmp, tmp, n * 8);
+ tcg_gen_or_i32(tmp2, tmp2, tmp);
+ dead_tmp(tmp);
+ }
+ }
+ neon_store_reg(rd, pass, tmp2);
+ } else {
+ tmp2 = neon_load_reg(rd, pass);
+ for (n = 0; n < 4; n++) {
+ tmp = new_tmp();
+ if (n == 0) {
+ tcg_gen_mov_i32(tmp, tmp2);
+ } else {
+ tcg_gen_shri_i32(tmp, tmp2, n * 8);
+ }
+ gen_st8(tmp, addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, stride);
+ }
+ dead_tmp(tmp2);
+ }
+ }
+ }
+ }
+ rd += spacing;
+ }
+ stride = nregs * 8;
+ } else {
+ size = (insn >> 10) & 3;
+ if (size == 3) {
+ /* Load single element to all lanes. */
+ if (!load)
+ return 1;
+ size = (insn >> 6) & 3;
+ nregs = ((insn >> 8) & 3) + 1;
+ stride = (insn & (1 << 5)) ? 2 : 1;
+ load_reg_var(s, addr, rn);
+ for (reg = 0; reg < nregs; reg++) {
+ switch (size) {
+ case 0:
+ tmp = gen_ld8u(addr, IS_USER(s));
+ gen_neon_dup_u8(tmp, 0);
+ break;
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ gen_neon_dup_low16(tmp);
+ break;
+ case 2:
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ case 3:
+ return 1;
+ default: /* Avoid compiler warnings. */
+ abort();
+ }
+ tcg_gen_addi_i32(addr, addr, 1 << size);
+ tmp2 = new_tmp();
+ tcg_gen_mov_i32(tmp2, tmp);
+ neon_store_reg(rd, 0, tmp2);
+ neon_store_reg(rd, 1, tmp);
+ rd += stride;
+ }
+ stride = (1 << size) * nregs;
+ } else {
+ /* Single element. */
+ pass = (insn >> 7) & 1;
+ switch (size) {
+ case 0:
+ shift = ((insn >> 5) & 3) * 8;
+ stride = 1;
+ break;
+ case 1:
+ shift = ((insn >> 6) & 1) * 16;
+ stride = (insn & (1 << 5)) ? 2 : 1;
+ break;
+ case 2:
+ shift = 0;
+ stride = (insn & (1 << 6)) ? 2 : 1;
+ break;
+ default:
+ abort();
+ }
+ nregs = ((insn >> 8) & 3) + 1;
+ load_reg_var(s, addr, rn);
+ for (reg = 0; reg < nregs; reg++) {
+ if (load) {
+ switch (size) {
+ case 0:
+ tmp = gen_ld8u(addr, IS_USER(s));
+ break;
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 2:
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ default: /* Avoid compiler warnings. */
+ abort();
+ }
+ if (size != 2) {
+ tmp2 = neon_load_reg(rd, pass);
+ gen_bfi(tmp, tmp2, tmp, shift, size ? 0xffff : 0xff);
+ dead_tmp(tmp2);
+ }
+ neon_store_reg(rd, pass, tmp);
+ } else { /* Store */
+ tmp = neon_load_reg(rd, pass);
+ if (shift)
+ tcg_gen_shri_i32(tmp, tmp, shift);
+ switch (size) {
+ case 0:
+ gen_st8(tmp, addr, IS_USER(s));
+ break;
+ case 1:
+ gen_st16(tmp, addr, IS_USER(s));
+ break;
+ case 2:
+ gen_st32(tmp, addr, IS_USER(s));
+ break;
+ }
+ }
+ rd += stride;
+ tcg_gen_addi_i32(addr, addr, 1 << size);
+ }
+ stride = nregs * (1 << size);
+ }
+ }
+ dead_tmp(addr);
+ if (rm != 15) {
+ TCGv base;
+
+ base = load_reg(s, rn);
+ if (rm == 13) {
+ tcg_gen_addi_i32(base, base, stride);
+ } else {
+ TCGv index;
+ index = load_reg(s, rm);
+ tcg_gen_add_i32(base, base, index);
+ dead_tmp(index);
+ }
+ store_reg(s, rn, base);
+ }
+ return 0;
+}
+
+/* Bitwise select. dest = c ? t : f. Clobbers T and F. */
+static void gen_neon_bsl(TCGv dest, TCGv t, TCGv f, TCGv c)
+{
+ tcg_gen_and_i32(t, t, c);
+ tcg_gen_andc_i32(f, f, c);
+ tcg_gen_or_i32(dest, t, f);
+}
+
+static inline void gen_neon_narrow(int size, TCGv dest, TCGv_i64 src)
+{
+ switch (size) {
+ case 0: gen_helper_neon_narrow_u8(dest, src); break;
+ case 1: gen_helper_neon_narrow_u16(dest, src); break;
+ case 2: tcg_gen_trunc_i64_i32(dest, src); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src)
+{
+ switch (size) {
+ case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
+{
+ switch (size) {
+ case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break;
+ case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break;
+ case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_shift_narrow(int size, TCGv var, TCGv shift,
+ int q, int u)
+{
+ if (q) {
+ if (u) {
+ switch (size) {
+ case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
+ case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
+ default: abort();
+ }
+ } else {
+ switch (size) {
+ case 1: gen_helper_neon_rshl_s16(var, var, shift); break;
+ case 2: gen_helper_neon_rshl_s32(var, var, shift); break;
+ default: abort();
+ }
+ }
+ } else {
+ if (u) {
+ switch (size) {
+ case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
+ case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
+ default: abort();
+ }
+ } else {
+ switch (size) {
+ case 1: gen_helper_neon_shl_s16(var, var, shift); break;
+ case 2: gen_helper_neon_shl_s32(var, var, shift); break;
+ default: abort();
+ }
+ }
+ }
+}
+
+static inline void gen_neon_widen(TCGv_i64 dest, TCGv src, int size, int u)
+{
+ if (u) {
+ switch (size) {
+ case 0: gen_helper_neon_widen_u8(dest, src); break;
+ case 1: gen_helper_neon_widen_u16(dest, src); break;
+ case 2: tcg_gen_extu_i32_i64(dest, src); break;
+ default: abort();
+ }
+ } else {
+ switch (size) {
+ case 0: gen_helper_neon_widen_s8(dest, src); break;
+ case 1: gen_helper_neon_widen_s16(dest, src); break;
+ case 2: tcg_gen_ext_i32_i64(dest, src); break;
+ default: abort();
+ }
+ }
+ dead_tmp(src);
+}
+
+static inline void gen_neon_addl(int size)
+{
+ switch (size) {
+ case 0: gen_helper_neon_addl_u16(CPU_V001); break;
+ case 1: gen_helper_neon_addl_u32(CPU_V001); break;
+ case 2: tcg_gen_add_i64(CPU_V001); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_subl(int size)
+{
+ switch (size) {
+ case 0: gen_helper_neon_subl_u16(CPU_V001); break;
+ case 1: gen_helper_neon_subl_u32(CPU_V001); break;
+ case 2: tcg_gen_sub_i64(CPU_V001); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_negl(TCGv_i64 var, int size)
+{
+ switch (size) {
+ case 0: gen_helper_neon_negl_u16(var, var); break;
+ case 1: gen_helper_neon_negl_u32(var, var); break;
+ case 2: gen_helper_neon_negl_u64(var, var); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_addl_saturate(TCGv_i64 op0, TCGv_i64 op1, int size)
+{
+ switch (size) {
+ case 1: gen_helper_neon_addl_saturate_s32(op0, cpu_env, op0, op1); break;
+ case 2: gen_helper_neon_addl_saturate_s64(op0, cpu_env, op0, op1); break;
+ default: abort();
+ }
+}
+
+static inline void gen_neon_mull(TCGv_i64 dest, TCGv a, TCGv b, int size, int u)
+{
+ TCGv_i64 tmp;
+
+ switch ((size << 1) | u) {
+ case 0: gen_helper_neon_mull_s8(dest, a, b); break;
+ case 1: gen_helper_neon_mull_u8(dest, a, b); break;
+ case 2: gen_helper_neon_mull_s16(dest, a, b); break;
+ case 3: gen_helper_neon_mull_u16(dest, a, b); break;
+ case 4:
+ tmp = gen_muls_i64_i32(a, b);
+ tcg_gen_mov_i64(dest, tmp);
+ break;
+ case 5:
+ tmp = gen_mulu_i64_i32(a, b);
+ tcg_gen_mov_i64(dest, tmp);
+ break;
+ default: abort();
+ }
+
+ /* gen_helper_neon_mull_[su]{8|16} do not free their parameters.
+ Don't forget to clean them now. */
+ if (size < 2) {
+ dead_tmp(a);
+ dead_tmp(b);
+ }
+}
+
+/* Translate a NEON data processing instruction. Return nonzero if the
+ instruction is invalid.
+ We process data in a mixture of 32-bit and 64-bit chunks.
+ Mostly we use 32-bit chunks so we can use normal scalar instructions. */
+
+static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ int op;
+ int q;
+ int rd, rn, rm;
+ int size;
+ int shift;
+ int pass;
+ int count;
+ int pairwise;
+ int u;
+ int n;
+ uint32_t imm, mask;
+ TCGv tmp, tmp2, tmp3, tmp4, tmp5;
+ TCGv_i64 tmp64;
+
+ if (!s->vfp_enabled)
+ return 1;
+ q = (insn & (1 << 6)) != 0;
+ u = (insn >> 24) & 1;
+ VFP_DREG_D(rd, insn);
+ VFP_DREG_N(rn, insn);
+ VFP_DREG_M(rm, insn);
+ size = (insn >> 20) & 3;
+ if ((insn & (1 << 23)) == 0) {
+ /* Three register same length. */
+ op = ((insn >> 7) & 0x1e) | ((insn >> 4) & 1);
+ if (size == 3 && (op == 1 || op == 5 || op == 8 || op == 9
+ || op == 10 || op == 11 || op == 16)) {
+ /* 64-bit element instructions. */
+ for (pass = 0; pass < (q ? 2 : 1); pass++) {
+ neon_load_reg64(cpu_V0, rn + pass);
+ neon_load_reg64(cpu_V1, rm + pass);
+ switch (op) {
+ case 1: /* VQADD */
+ if (u) {
+ gen_helper_neon_qadd_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ } else {
+ gen_helper_neon_qadd_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ }
+ break;
+ case 5: /* VQSUB */
+ if (u) {
+ gen_helper_neon_qsub_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ } else {
+ gen_helper_neon_qsub_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ }
+ break;
+ case 8: /* VSHL */
+ if (u) {
+ gen_helper_neon_shl_u64(cpu_V0, cpu_V1, cpu_V0);
+ } else {
+ gen_helper_neon_shl_s64(cpu_V0, cpu_V1, cpu_V0);
+ }
+ break;
+ case 9: /* VQSHL */
+ if (u) {
+ gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
+ } else {
+ gen_helper_neon_qshl_s64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
+ }
+ break;
+ case 10: /* VRSHL */
+ if (u) {
+ gen_helper_neon_rshl_u64(cpu_V0, cpu_V1, cpu_V0);
+ } else {
+ gen_helper_neon_rshl_s64(cpu_V0, cpu_V1, cpu_V0);
+ }
+ break;
+ case 11: /* VQRSHL */
+ if (u) {
+ gen_helper_neon_qrshl_u64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
+ } else {
+ gen_helper_neon_qrshl_s64(cpu_V0, cpu_env,
+ cpu_V1, cpu_V0);
+ }
+ break;
+ case 16:
+ if (u) {
+ tcg_gen_sub_i64(CPU_V001);
+ } else {
+ tcg_gen_add_i64(CPU_V001);
+ }
+ break;
+ default:
+ abort();
+ }
+ neon_store_reg64(cpu_V0, rd + pass);
+ }
+ return 0;
+ }
+ switch (op) {
+ case 8: /* VSHL */
+ case 9: /* VQSHL */
+ case 10: /* VRSHL */
+ case 11: /* VQRSHL */
+ {
+ int rtmp;
+ /* Shift instruction operands are reversed. */
+ rtmp = rn;
+ rn = rm;
+ rm = rtmp;
+ pairwise = 0;
+ }
+ break;
+ case 20: /* VPMAX */
+ case 21: /* VPMIN */
+ case 23: /* VPADD */
+ pairwise = 1;
+ break;
+ case 26: /* VPADD (float) */
+ pairwise = (u && size < 2);
+ break;
+ case 30: /* VPMIN/VPMAX (float) */
+ pairwise = u;
+ break;
+ default:
+ pairwise = 0;
+ break;
+ }
+
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+
+ if (pairwise) {
+ /* Pairwise. */
+ if (q)
+ n = (pass & 1) * 2;
+ else
+ n = 0;
+ if (pass < q + 1) {
+ tmp = neon_load_reg(rn, n);
+ tmp2 = neon_load_reg(rn, n + 1);
+ } else {
+ tmp = neon_load_reg(rm, n);
+ tmp2 = neon_load_reg(rm, n + 1);
+ }
+ } else {
+ /* Elementwise. */
+ tmp = neon_load_reg(rn, pass);
+ tmp2 = neon_load_reg(rm, pass);
+ }
+ switch (op) {
+ case 0: /* VHADD */
+ GEN_NEON_INTEGER_OP(hadd);
+ break;
+ case 1: /* VQADD */
+ GEN_NEON_INTEGER_OP_ENV(qadd);
+ break;
+ case 2: /* VRHADD */
+ GEN_NEON_INTEGER_OP(rhadd);
+ break;
+ case 3: /* Logic ops. */
+ switch ((u << 2) | size) {
+ case 0: /* VAND */
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ break;
+ case 1: /* BIC */
+ tcg_gen_andc_i32(tmp, tmp, tmp2);
+ break;
+ case 2: /* VORR */
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ break;
+ case 3: /* VORN */
+ tcg_gen_orc_i32(tmp, tmp, tmp2);
+ break;
+ case 4: /* VEOR */
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ break;
+ case 5: /* VBSL */
+ tmp3 = neon_load_reg(rd, pass);
+ gen_neon_bsl(tmp, tmp, tmp2, tmp3);
+ dead_tmp(tmp3);
+ break;
+ case 6: /* VBIT */
+ tmp3 = neon_load_reg(rd, pass);
+ gen_neon_bsl(tmp, tmp, tmp3, tmp2);
+ dead_tmp(tmp3);
+ break;
+ case 7: /* VBIF */
+ tmp3 = neon_load_reg(rd, pass);
+ gen_neon_bsl(tmp, tmp3, tmp, tmp2);
+ dead_tmp(tmp3);
+ break;
+ }
+ break;
+ case 4: /* VHSUB */
+ GEN_NEON_INTEGER_OP(hsub);
+ break;
+ case 5: /* VQSUB */
+ GEN_NEON_INTEGER_OP_ENV(qsub);
+ break;
+ case 6: /* VCGT */
+ GEN_NEON_INTEGER_OP(cgt);
+ break;
+ case 7: /* VCGE */
+ GEN_NEON_INTEGER_OP(cge);
+ break;
+ case 8: /* VSHL */
+ GEN_NEON_INTEGER_OP(shl);
+ break;
+ case 9: /* VQSHL */
+ GEN_NEON_INTEGER_OP_ENV(qshl);
+ break;
+ case 10: /* VRSHL */
+ GEN_NEON_INTEGER_OP(rshl);
+ break;
+ case 11: /* VQRSHL */
+ GEN_NEON_INTEGER_OP_ENV(qrshl);
+ break;
+ case 12: /* VMAX */
+ GEN_NEON_INTEGER_OP(max);
+ break;
+ case 13: /* VMIN */
+ GEN_NEON_INTEGER_OP(min);
+ break;
+ case 14: /* VABD */
+ GEN_NEON_INTEGER_OP(abd);
+ break;
+ case 15: /* VABA */
+ GEN_NEON_INTEGER_OP(abd);
+ dead_tmp(tmp2);
+ tmp2 = neon_load_reg(rd, pass);
+ gen_neon_add(size, tmp, tmp2);
+ break;
+ case 16:
+ if (!u) { /* VADD */
+ if (gen_neon_add(size, tmp, tmp2))
+ return 1;
+ } else { /* VSUB */
+ switch (size) {
+ case 0: gen_helper_neon_sub_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_sub_u16(tmp, tmp, tmp2); break;
+ case 2: tcg_gen_sub_i32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ }
+ break;
+ case 17:
+ if (!u) { /* VTST */
+ switch (size) {
+ case 0: gen_helper_neon_tst_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_tst_u16(tmp, tmp, tmp2); break;
+ case 2: gen_helper_neon_tst_u32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ } else { /* VCEQ */
+ switch (size) {
+ case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
+ case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ }
+ break;
+ case 18: /* Multiply. */
+ switch (size) {
+ case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
+ case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ dead_tmp(tmp2);
+ tmp2 = neon_load_reg(rd, pass);
+ if (u) { /* VMLS */
+ gen_neon_rsb(size, tmp, tmp2);
+ } else { /* VMLA */
+ gen_neon_add(size, tmp, tmp2);
+ }
+ break;
+ case 19: /* VMUL */
+ if (u) { /* polynomial */
+ gen_helper_neon_mul_p8(tmp, tmp, tmp2);
+ } else { /* Integer */
+ switch (size) {
+ case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
+ case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ }
+ break;
+ case 20: /* VPMAX */
+ GEN_NEON_INTEGER_OP(pmax);
+ break;
+ case 21: /* VPMIN */
+ GEN_NEON_INTEGER_OP(pmin);
+ break;
+ case 22: /* Hultiply high. */
+ if (!u) { /* VQDMULH */
+ switch (size) {
+ case 1: gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2); break;
+ case 2: gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2); break;
+ default: return 1;
+ }
+ } else { /* VQRDHMUL */
+ switch (size) {
+ case 1: gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2); break;
+ case 2: gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2); break;
+ default: return 1;
+ }
+ }
+ break;
+ case 23: /* VPADD */
+ if (u)
+ return 1;
+ switch (size) {
+ case 0: gen_helper_neon_padd_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_padd_u16(tmp, tmp, tmp2); break;
+ case 2: tcg_gen_add_i32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ break;
+ case 26: /* Floating point arithnetic. */
+ switch ((u << 2) | size) {
+ case 0: /* VADD */
+ gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ break;
+ case 2: /* VSUB */
+ gen_helper_neon_sub_f32(tmp, tmp, tmp2);
+ break;
+ case 4: /* VPADD */
+ gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ break;
+ case 6: /* VABD */
+ gen_helper_neon_abd_f32(tmp, tmp, tmp2);
+ break;
+ default:
+ return 1;
+ }
+ break;
+ case 27: /* Float multiply. */
+ gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+ if (!u) {
+ dead_tmp(tmp2);
+ tmp2 = neon_load_reg(rd, pass);
+ if (size == 0) {
+ gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ } else {
+ gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+ }
+ }
+ break;
+ case 28: /* Float compare. */
+ if (!u) {
+ gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+ } else {
+ if (size == 0)
+ gen_helper_neon_cge_f32(tmp, tmp, tmp2);
+ else
+ gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+ }
+ break;
+ case 29: /* Float compare absolute. */
+ if (!u)
+ return 1;
+ if (size == 0)
+ gen_helper_neon_acge_f32(tmp, tmp, tmp2);
+ else
+ gen_helper_neon_acgt_f32(tmp, tmp, tmp2);
+ break;
+ case 30: /* Float min/max. */
+ if (size == 0)
+ gen_helper_neon_max_f32(tmp, tmp, tmp2);
+ else
+ gen_helper_neon_min_f32(tmp, tmp, tmp2);
+ break;
+ case 31:
+ if (size == 0)
+ gen_helper_recps_f32(tmp, tmp, tmp2, cpu_env);
+ else
+ gen_helper_rsqrts_f32(tmp, tmp, tmp2, cpu_env);
+ break;
+ default:
+ abort();
+ }
+ dead_tmp(tmp2);
+
+ /* Save the result. For elementwise operations we can put it
+ straight into the destination register. For pairwise operations
+ we have to be careful to avoid clobbering the source operands. */
+ if (pairwise && rd == rm) {
+ neon_store_scratch(pass, tmp);
+ } else {
+ neon_store_reg(rd, pass, tmp);
+ }
+
+ } /* for pass */
+ if (pairwise && rd == rm) {
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ tmp = neon_load_scratch(pass);
+ neon_store_reg(rd, pass, tmp);
+ }
+ }
+ /* End of 3 register same size operations. */
+ } else if (insn & (1 << 4)) {
+ if ((insn & 0x00380080) != 0) {
+ /* Two registers and shift. */
+ op = (insn >> 8) & 0xf;
+ if (insn & (1 << 7)) {
+ /* 64-bit shift. */
+ size = 3;
+ } else {
+ size = 2;
+ while ((insn & (1 << (size + 19))) == 0)
+ size--;
+ }
+ shift = (insn >> 16) & ((1 << (3 + size)) - 1);
+ /* To avoid excessive dumplication of ops we implement shift
+ by immediate using the variable shift operations. */
+ if (op < 8) {
+ /* Shift by immediate:
+ VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VQSHL, VQSHLU. */
+ /* Right shifts are encoded as N - shift, where N is the
+ element size in bits. */
+ if (op <= 4)
+ shift = shift - (1 << (size + 3));
+ if (size == 3) {
+ count = q + 1;
+ } else {
+ count = q ? 4: 2;
+ }
+ switch (size) {
+ case 0:
+ imm = (uint8_t) shift;
+ imm |= imm << 8;
+ imm |= imm << 16;
+ break;
+ case 1:
+ imm = (uint16_t) shift;
+ imm |= imm << 16;
+ break;
+ case 2:
+ case 3:
+ imm = shift;
+ break;
+ default:
+ abort();
+ }
+
+ for (pass = 0; pass < count; pass++) {
+ if (size == 3) {
+ neon_load_reg64(cpu_V0, rm + pass);
+ tcg_gen_movi_i64(cpu_V1, imm);
+ switch (op) {
+ case 0: /* VSHR */
+ case 1: /* VSRA */
+ if (u)
+ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
+ else
+ gen_helper_neon_shl_s64(cpu_V0, cpu_V0, cpu_V1);
+ break;
+ case 2: /* VRSHR */
+ case 3: /* VRSRA */
+ if (u)
+ gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, cpu_V1);
+ else
+ gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, cpu_V1);
+ break;
+ case 4: /* VSRI */
+ if (!u)
+ return 1;
+ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
+ break;
+ case 5: /* VSHL, VSLI */
+ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
+ break;
+ case 6: /* VQSHLU */
+ if (u) {
+ gen_helper_neon_qshlu_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ } else {
+ return 1;
+ }
+ break;
+ case 7: /* VQSHL */
+ if (u) {
+ gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ } else {
+ gen_helper_neon_qshl_s64(cpu_V0, cpu_env,
+ cpu_V0, cpu_V1);
+ }
+ break;
+ }
+ if (op == 1 || op == 3) {
+ /* Accumulate. */
+ neon_load_reg64(cpu_V1, rd + pass);
+ tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1);
+ } else if (op == 4 || (op == 5 && u)) {
+ /* Insert */
+ cpu_abort(env, "VS[LR]I.64 not implemented");
+ }
+ neon_store_reg64(cpu_V0, rd + pass);
+ } else { /* size < 3 */
+ /* Operands in T0 and T1. */
+ tmp = neon_load_reg(rm, pass);
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, imm);
+ switch (op) {
+ case 0: /* VSHR */
+ case 1: /* VSRA */
+ GEN_NEON_INTEGER_OP(shl);
+ break;
+ case 2: /* VRSHR */
+ case 3: /* VRSRA */
+ GEN_NEON_INTEGER_OP(rshl);
+ break;
+ case 4: /* VSRI */
+ if (!u)
+ return 1;
+ GEN_NEON_INTEGER_OP(shl);
+ break;
+ case 5: /* VSHL, VSLI */
+ switch (size) {
+ case 0: gen_helper_neon_shl_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_shl_u16(tmp, tmp, tmp2); break;
+ case 2: gen_helper_neon_shl_u32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ break;
+ case 6: /* VQSHLU */
+ if (!u) {
+ return 1;
+ }
+ switch (size) {
+ case 0:
+ gen_helper_neon_qshlu_s8(tmp, cpu_env,
+ tmp, tmp2);
+ break;
+ case 1:
+ gen_helper_neon_qshlu_s16(tmp, cpu_env,
+ tmp, tmp2);
+ break;
+ case 2:
+ gen_helper_neon_qshlu_s32(tmp, cpu_env,
+ tmp, tmp2);
+ break;
+ default:
+ return 1;
+ }
+ break;
+ case 7: /* VQSHL */
+ GEN_NEON_INTEGER_OP_ENV(qshl);
+ break;
+ }
+ dead_tmp(tmp2);
+
+ if (op == 1 || op == 3) {
+ /* Accumulate. */
+ tmp2 = neon_load_reg(rd, pass);
+ gen_neon_add(size, tmp, tmp2);
+ dead_tmp(tmp2);
+ } else if (op == 4 || (op == 5 && u)) {
+ /* Insert */
+ switch (size) {
+ case 0:
+ if (op == 4)
+ mask = 0xff >> -shift;
+ else
+ mask = (uint8_t)(0xff << shift);
+ mask |= mask << 8;
+ mask |= mask << 16;
+ break;
+ case 1:
+ if (op == 4)
+ mask = 0xffff >> -shift;
+ else
+ mask = (uint16_t)(0xffff << shift);
+ mask |= mask << 16;
+ break;
+ case 2:
+ if (shift < -31 || shift > 31) {
+ mask = 0;
+ } else {
+ if (op == 4)
+ mask = 0xffffffffu >> -shift;
+ else
+ mask = 0xffffffffu << shift;
+ }
+ break;
+ default:
+ abort();
+ }
+ tmp2 = neon_load_reg(rd, pass);
+ tcg_gen_andi_i32(tmp, tmp, mask);
+ tcg_gen_andi_i32(tmp2, tmp2, ~mask);
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ neon_store_reg(rd, pass, tmp);
+ }
+ } /* for pass */
+ } else if (op < 10) {
+ /* Shift by immediate and narrow:
+ VSHRN, VRSHRN, VQSHRN, VQRSHRN. */
+ shift = shift - (1 << (size + 3));
+ size++;
+ switch (size) {
+ case 1:
+ imm = (uint16_t)shift;
+ imm |= imm << 16;
+ tmp2 = tcg_const_i32(imm);
+ TCGV_UNUSED_I64(tmp64);
+ break;
+ case 2:
+ imm = (uint32_t)shift;
+ tmp2 = tcg_const_i32(imm);
+ TCGV_UNUSED_I64(tmp64);
+ break;
+ case 3:
+ tmp64 = tcg_const_i64(shift);
+ TCGV_UNUSED(tmp2);
+ break;
+ default:
+ abort();
+ }
+
+ for (pass = 0; pass < 2; pass++) {
+ if (size == 3) {
+ neon_load_reg64(cpu_V0, rm + pass);
+ if (q) {
+ if (u)
+ gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, tmp64);
+ else
+ gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, tmp64);
+ } else {
+ if (u)
+ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, tmp64);
+ else
+ gen_helper_neon_shl_s64(cpu_V0, cpu_V0, tmp64);
+ }
+ } else {
+ tmp = neon_load_reg(rm + pass, 0);
+ gen_neon_shift_narrow(size, tmp, tmp2, q, u);
+ tmp3 = neon_load_reg(rm + pass, 1);
+ gen_neon_shift_narrow(size, tmp3, tmp2, q, u);
+ tcg_gen_concat_i32_i64(cpu_V0, tmp, tmp3);
+ dead_tmp(tmp);
+ dead_tmp(tmp3);
+ }
+ tmp = new_tmp();
+ if (op == 8 && !u) {
+ gen_neon_narrow(size - 1, tmp, cpu_V0);
+ } else {
+ if (op == 8)
+ gen_neon_narrow_sats(size - 1, tmp, cpu_V0);
+ else
+ gen_neon_narrow_satu(size - 1, tmp, cpu_V0);
+ }
+ neon_store_reg(rd, pass, tmp);
+ } /* for pass */
+ if (size == 3) {
+ tcg_temp_free_i64(tmp64);
+ } else {
+ tcg_temp_free_i32(tmp2);
+ }
+ } else if (op == 10) {
+ /* VSHLL */
+ if (q || size == 3)
+ return 1;
+ tmp = neon_load_reg(rm, 0);
+ tmp2 = neon_load_reg(rm, 1);
+ for (pass = 0; pass < 2; pass++) {
+ if (pass == 1)
+ tmp = tmp2;
+
+ gen_neon_widen(cpu_V0, tmp, size, u);
+
+ if (shift != 0) {
+ /* The shift is less than the width of the source
+ type, so we can just shift the whole register. */
+ tcg_gen_shli_i64(cpu_V0, cpu_V0, shift);
+ if (size < 2 || !u) {
+ uint64_t imm64;
+ if (size == 0) {
+ imm = (0xffu >> (8 - shift));
+ imm |= imm << 16;
+ } else {
+ imm = 0xffff >> (16 - shift);
+ }
+ imm64 = imm | (((uint64_t)imm) << 32);
+ tcg_gen_andi_i64(cpu_V0, cpu_V0, imm64);
+ }
+ }
+ neon_store_reg64(cpu_V0, rd + pass);
+ }
+ } else if (op >= 14) {
+ /* VCVT fixed-point. */
+ /* We have already masked out the must-be-1 top bit of imm6,
+ * hence this 32-shift where the ARM ARM has 64-imm6.
+ */
+ shift = 32 - shift;
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass));
+ if (!(op & 1)) {
+ if (u)
+ gen_vfp_ulto(0, shift);
+ else
+ gen_vfp_slto(0, shift);
+ } else {
+ if (u)
+ gen_vfp_toul(0, shift);
+ else
+ gen_vfp_tosl(0, shift);
+ }
+ tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass));
+ }
+ } else {
+ return 1;
+ }
+ } else { /* (insn & 0x00380080) == 0 */
+ int invert;
+
+ op = (insn >> 8) & 0xf;
+ /* One register and immediate. */
+ imm = (u << 7) | ((insn >> 12) & 0x70) | (insn & 0xf);
+ invert = (insn & (1 << 5)) != 0;
+ switch (op) {
+ case 0: case 1:
+ /* no-op */
+ break;
+ case 2: case 3:
+ imm <<= 8;
+ break;
+ case 4: case 5:
+ imm <<= 16;
+ break;
+ case 6: case 7:
+ imm <<= 24;
+ break;
+ case 8: case 9:
+ imm |= imm << 16;
+ break;
+ case 10: case 11:
+ imm = (imm << 8) | (imm << 24);
+ break;
+ case 12:
+ imm = (imm << 8) | 0xff;
+ break;
+ case 13:
+ imm = (imm << 16) | 0xffff;
+ break;
+ case 14:
+ imm |= (imm << 8) | (imm << 16) | (imm << 24);
+ if (invert)
+ imm = ~imm;
+ break;
+ case 15:
+ imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19)
+ | ((imm & 0x40) ? (0x1f << 25) : (1 << 30));
+ break;
+ }
+ if (invert)
+ imm = ~imm;
+
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ if (op & 1 && op < 12) {
+ tmp = neon_load_reg(rd, pass);
+ if (invert) {
+ /* The immediate value has already been inverted, so
+ BIC becomes AND. */
+ tcg_gen_andi_i32(tmp, tmp, imm);
+ } else {
+ tcg_gen_ori_i32(tmp, tmp, imm);
+ }
+ } else {
+ /* VMOV, VMVN. */
+ tmp = new_tmp();
+ if (op == 14 && invert) {
+ uint32_t val;
+ val = 0;
+ for (n = 0; n < 4; n++) {
+ if (imm & (1 << (n + (pass & 1) * 4)))
+ val |= 0xff << (n * 8);
+ }
+ tcg_gen_movi_i32(tmp, val);
+ } else {
+ tcg_gen_movi_i32(tmp, imm);
+ }
+ }
+ neon_store_reg(rd, pass, tmp);
+ }
+ }
+ } else { /* (insn & 0x00800010 == 0x00800000) */
+ if (size != 3) {
+ op = (insn >> 8) & 0xf;
+ if ((insn & (1 << 6)) == 0) {
+ /* Three registers of different lengths. */
+ int src1_wide;
+ int src2_wide;
+ int prewiden;
+ /* prewiden, src1_wide, src2_wide */
+ static const int neon_3reg_wide[16][3] = {
+ {1, 0, 0}, /* VADDL */
+ {1, 1, 0}, /* VADDW */
+ {1, 0, 0}, /* VSUBL */
+ {1, 1, 0}, /* VSUBW */
+ {0, 1, 1}, /* VADDHN */
+ {0, 0, 0}, /* VABAL */
+ {0, 1, 1}, /* VSUBHN */
+ {0, 0, 0}, /* VABDL */
+ {0, 0, 0}, /* VMLAL */
+ {0, 0, 0}, /* VQDMLAL */
+ {0, 0, 0}, /* VMLSL */
+ {0, 0, 0}, /* VQDMLSL */
+ {0, 0, 0}, /* Integer VMULL */
+ {0, 0, 0}, /* VQDMULL */
+ {0, 0, 0} /* Polynomial VMULL */
+ };
+
+ prewiden = neon_3reg_wide[op][0];
+ src1_wide = neon_3reg_wide[op][1];
+ src2_wide = neon_3reg_wide[op][2];
+
+ if (size == 0 && (op == 9 || op == 11 || op == 13))
+ return 1;
+
+ /* Avoid overlapping operands. Wide source operands are
+ always aligned so will never overlap with wide
+ destinations in problematic ways. */
+ if (rd == rm && !src2_wide) {
+ tmp = neon_load_reg(rm, 1);
+ neon_store_scratch(2, tmp);
+ } else if (rd == rn && !src1_wide) {
+ tmp = neon_load_reg(rn, 1);
+ neon_store_scratch(2, tmp);
+ }
+ TCGV_UNUSED(tmp3);
+ for (pass = 0; pass < 2; pass++) {
+ if (src1_wide) {
+ neon_load_reg64(cpu_V0, rn + pass);
+ TCGV_UNUSED(tmp);
+ } else {
+ if (pass == 1 && rd == rn) {
+ tmp = neon_load_scratch(2);
+ } else {
+ tmp = neon_load_reg(rn, pass);
+ }
+ if (prewiden) {
+ gen_neon_widen(cpu_V0, tmp, size, u);
+ }
+ }
+ if (src2_wide) {
+ neon_load_reg64(cpu_V1, rm + pass);
+ TCGV_UNUSED(tmp2);
+ } else {
+ if (pass == 1 && rd == rm) {
+ tmp2 = neon_load_scratch(2);
+ } else {
+ tmp2 = neon_load_reg(rm, pass);
+ }
+ if (prewiden) {
+ gen_neon_widen(cpu_V1, tmp2, size, u);
+ }
+ }
+ switch (op) {
+ case 0: case 1: case 4: /* VADDL, VADDW, VADDHN, VRADDHN */
+ gen_neon_addl(size);
+ break;
+ case 2: case 3: case 6: /* VSUBL, VSUBW, VSUBHN, VRSUBHN */
+ gen_neon_subl(size);
+ break;
+ case 5: case 7: /* VABAL, VABDL */
+ switch ((size << 1) | u) {
+ case 0:
+ gen_helper_neon_abdl_s16(cpu_V0, tmp, tmp2);
+ break;
+ case 1:
+ gen_helper_neon_abdl_u16(cpu_V0, tmp, tmp2);
+ break;
+ case 2:
+ gen_helper_neon_abdl_s32(cpu_V0, tmp, tmp2);
+ break;
+ case 3:
+ gen_helper_neon_abdl_u32(cpu_V0, tmp, tmp2);
+ break;
+ case 4:
+ gen_helper_neon_abdl_s64(cpu_V0, tmp, tmp2);
+ break;
+ case 5:
+ gen_helper_neon_abdl_u64(cpu_V0, tmp, tmp2);
+ break;
+ default: abort();
+ }
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+ break;
+ case 8: case 9: case 10: case 11: case 12: case 13:
+ /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */
+ gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
+ break;
+ case 14: /* Polynomial VMULL */
+ cpu_abort(env, "Polynomial VMULL not implemented");
+
+ default: /* 15 is RESERVED. */
+ return 1;
+ }
+ if (op == 5 || op == 13 || (op >= 8 && op <= 11)) {
+ /* Accumulate. */
+ if (op == 10 || op == 11) {
+ gen_neon_negl(cpu_V0, size);
+ }
+
+ if (op != 13) {
+ neon_load_reg64(cpu_V1, rd + pass);
+ }
+
+ switch (op) {
+ case 5: case 8: case 10: /* VABAL, VMLAL, VMLSL */
+ gen_neon_addl(size);
+ break;
+ case 9: case 11: /* VQDMLAL, VQDMLSL */
+ gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+ gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
+ break;
+ /* Fall through. */
+ case 13: /* VQDMULL */
+ gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+ break;
+ default:
+ abort();
+ }
+ neon_store_reg64(cpu_V0, rd + pass);
+ } else if (op == 4 || op == 6) {
+ /* Narrowing operation. */
+ tmp = new_tmp();
+ if (!u) {
+ switch (size) {
+ case 0:
+ gen_helper_neon_narrow_high_u8(tmp, cpu_V0);
+ break;
+ case 1:
+ gen_helper_neon_narrow_high_u16(tmp, cpu_V0);
+ break;
+ case 2:
+ tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+ tcg_gen_trunc_i64_i32(tmp, cpu_V0);
+ break;
+ default: abort();
+ }
+ } else {
+ switch (size) {
+ case 0:
+ gen_helper_neon_narrow_round_high_u8(tmp, cpu_V0);
+ break;
+ case 1:
+ gen_helper_neon_narrow_round_high_u16(tmp, cpu_V0);
+ break;
+ case 2:
+ tcg_gen_addi_i64(cpu_V0, cpu_V0, 1u << 31);
+ tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+ tcg_gen_trunc_i64_i32(tmp, cpu_V0);
+ break;
+ default: abort();
+ }
+ }
+ if (pass == 0) {
+ tmp3 = tmp;
+ } else {
+ neon_store_reg(rd, 0, tmp3);
+ neon_store_reg(rd, 1, tmp);
+ }
+ } else {
+ /* Write back the result. */
+ neon_store_reg64(cpu_V0, rd + pass);
+ }
+ }
+ } else {
+ /* Two registers and a scalar. */
+ switch (op) {
+ case 0: /* Integer VMLA scalar */
+ case 1: /* Float VMLA scalar */
+ case 4: /* Integer VMLS scalar */
+ case 5: /* Floating point VMLS scalar */
+ case 8: /* Integer VMUL scalar */
+ case 9: /* Floating point VMUL scalar */
+ case 12: /* VQDMULH scalar */
+ case 13: /* VQRDMULH scalar */
+ tmp = neon_get_scalar(size, rm);
+ neon_store_scratch(0, tmp);
+ for (pass = 0; pass < (u ? 4 : 2); pass++) {
+ tmp = neon_load_scratch(0);
+ tmp2 = neon_load_reg(rn, pass);
+ if (op == 12) {
+ if (size == 1) {
+ gen_helper_neon_qdmulh_s16(tmp, cpu_env, tmp, tmp2);
+ } else {
+ gen_helper_neon_qdmulh_s32(tmp, cpu_env, tmp, tmp2);
+ }
+ } else if (op == 13) {
+ if (size == 1) {
+ gen_helper_neon_qrdmulh_s16(tmp, cpu_env, tmp, tmp2);
+ } else {
+ gen_helper_neon_qrdmulh_s32(tmp, cpu_env, tmp, tmp2);
+ }
+ } else if (op & 1) {
+ gen_helper_neon_mul_f32(tmp, tmp, tmp2);
+ } else {
+ switch (size) {
+ case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break;
+ case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ }
+ dead_tmp(tmp2);
+ if (op < 8) {
+ /* Accumulate. */
+ tmp2 = neon_load_reg(rd, pass);
+ switch (op) {
+ case 0:
+ gen_neon_add(size, tmp, tmp2);
+ break;
+ case 1:
+ gen_helper_neon_add_f32(tmp, tmp, tmp2);
+ break;
+ case 4:
+ gen_neon_rsb(size, tmp, tmp2);
+ break;
+ case 5:
+ gen_helper_neon_sub_f32(tmp, tmp2, tmp);
+ break;
+ default:
+ abort();
+ }
+ dead_tmp(tmp2);
+ }
+ neon_store_reg(rd, pass, tmp);
+ }
+ break;
+ case 2: /* VMLAL sclar */
+ case 3: /* VQDMLAL scalar */
+ case 6: /* VMLSL scalar */
+ case 7: /* VQDMLSL scalar */
+ case 10: /* VMULL scalar */
+ case 11: /* VQDMULL scalar */
+ if (size == 0 && (op == 3 || op == 7 || op == 11))
+ return 1;
+
+ tmp2 = neon_get_scalar(size, rm);
+ /* We need a copy of tmp2 because gen_neon_mull
+ * deletes it during pass 0. */
+ tmp4 = new_tmp();
+ tcg_gen_mov_i32(tmp4, tmp2);
+ tmp3 = neon_load_reg(rn, 1);
+
+ for (pass = 0; pass < 2; pass++) {
+ if (pass == 0) {
+ tmp = neon_load_reg(rn, 0);
+ } else {
+ tmp = tmp3;
+ tmp2 = tmp4;
+ }
+ gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
+ if (op == 6 || op == 7) {
+ gen_neon_negl(cpu_V0, size);
+ }
+ if (op != 11) {
+ neon_load_reg64(cpu_V1, rd + pass);
+ }
+ switch (op) {
+ case 2: case 6:
+ gen_neon_addl(size);
+ break;
+ case 3: case 7:
+ gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+ gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
+ break;
+ case 10:
+ /* no-op */
+ break;
+ case 11:
+ gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+ break;
+ default:
+ abort();
+ }
+ neon_store_reg64(cpu_V0, rd + pass);
+ }
+
+
+ break;
+ default: /* 14 and 15 are RESERVED */
+ return 1;
+ }
+ }
+ } else { /* size == 3 */
+ if (!u) {
+ /* Extract. */
+ imm = (insn >> 8) & 0xf;
+
+ if (imm > 7 && !q)
+ return 1;
+
+ if (imm == 0) {
+ neon_load_reg64(cpu_V0, rn);
+ if (q) {
+ neon_load_reg64(cpu_V1, rn + 1);
+ }
+ } else if (imm == 8) {
+ neon_load_reg64(cpu_V0, rn + 1);
+ if (q) {
+ neon_load_reg64(cpu_V1, rm);
+ }
+ } else if (q) {
+ tmp64 = tcg_temp_new_i64();
+ if (imm < 8) {
+ neon_load_reg64(cpu_V0, rn);
+ neon_load_reg64(tmp64, rn + 1);
+ } else {
+ neon_load_reg64(cpu_V0, rn + 1);
+ neon_load_reg64(tmp64, rm);
+ }
+ tcg_gen_shri_i64(cpu_V0, cpu_V0, (imm & 7) * 8);
+ tcg_gen_shli_i64(cpu_V1, tmp64, 64 - ((imm & 7) * 8));
+ tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
+ if (imm < 8) {
+ neon_load_reg64(cpu_V1, rm);
+ } else {
+ neon_load_reg64(cpu_V1, rm + 1);
+ imm -= 8;
+ }
+ tcg_gen_shli_i64(cpu_V1, cpu_V1, 64 - (imm * 8));
+ tcg_gen_shri_i64(tmp64, tmp64, imm * 8);
+ tcg_gen_or_i64(cpu_V1, cpu_V1, tmp64);
+ tcg_temp_free_i64(tmp64);
+ } else {
+ /* BUGFIX */
+ neon_load_reg64(cpu_V0, rn);
+ tcg_gen_shri_i64(cpu_V0, cpu_V0, imm * 8);
+ neon_load_reg64(cpu_V1, rm);
+ tcg_gen_shli_i64(cpu_V1, cpu_V1, 64 - (imm * 8));
+ tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
+ }
+ neon_store_reg64(cpu_V0, rd);
+ if (q) {
+ neon_store_reg64(cpu_V1, rd + 1);
+ }
+ } else if ((insn & (1 << 11)) == 0) {
+ /* Two register misc. */
+ op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf);
+ size = (insn >> 18) & 3;
+ switch (op) {
+ case 0: /* VREV64 */
+ if (size == 3)
+ return 1;
+ for (pass = 0; pass < (q ? 2 : 1); pass++) {
+ tmp = neon_load_reg(rm, pass * 2);
+ tmp2 = neon_load_reg(rm, pass * 2 + 1);
+ switch (size) {
+ case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
+ case 1: gen_swap_half(tmp); break;
+ case 2: /* no-op */ break;
+ default: abort();
+ }
+ neon_store_reg(rd, pass * 2 + 1, tmp);
+ if (size == 2) {
+ neon_store_reg(rd, pass * 2, tmp2);
+ } else {
+ switch (size) {
+ case 0: tcg_gen_bswap32_i32(tmp2, tmp2); break;
+ case 1: gen_swap_half(tmp2); break;
+ default: abort();
+ }
+ neon_store_reg(rd, pass * 2, tmp2);
+ }
+ }
+ break;
+ case 4: case 5: /* VPADDL */
+ case 12: case 13: /* VPADAL */
+ if (size == 3)
+ return 1;
+ for (pass = 0; pass < q + 1; pass++) {
+ tmp = neon_load_reg(rm, pass * 2);
+ gen_neon_widen(cpu_V0, tmp, size, op & 1);
+ tmp = neon_load_reg(rm, pass * 2 + 1);
+ gen_neon_widen(cpu_V1, tmp, size, op & 1);
+ switch (size) {
+ case 0: gen_helper_neon_paddl_u16(CPU_V001); break;
+ case 1: gen_helper_neon_paddl_u32(CPU_V001); break;
+ case 2: tcg_gen_add_i64(CPU_V001); break;
+ default: abort();
+ }
+ if (op >= 12) {
+ /* Accumulate. */
+ neon_load_reg64(cpu_V1, rd + pass);
+ gen_neon_addl(size);
+ }
+ neon_store_reg64(cpu_V0, rd + pass);
+ }
+ break;
+ case 33: /* VTRN */
+ if (size == 2) {
+ for (n = 0; n < (q ? 4 : 2); n += 2) {
+ tmp = neon_load_reg(rm, n);
+ tmp2 = neon_load_reg(rd, n + 1);
+ neon_store_reg(rm, n, tmp2);
+ neon_store_reg(rd, n + 1, tmp);
+ }
+ } else {
+ goto elementwise;
+ }
+ break;
+ case 34: /* VUZP */
+ /* Reg Before After
+ Rd A3 A2 A1 A0 B2 B0 A2 A0
+ Rm B3 B2 B1 B0 B3 B1 A3 A1
+ */
+ if (size == 3)
+ return 1;
+ gen_neon_unzip(rd, q, 0, size);
+ gen_neon_unzip(rm, q, 4, size);
+ if (q) {
+ static int unzip_order_q[8] =
+ {0, 2, 4, 6, 1, 3, 5, 7};
+ for (n = 0; n < 8; n++) {
+ int reg = (n < 4) ? rd : rm;
+ tmp = neon_load_scratch(unzip_order_q[n]);
+ neon_store_reg(reg, n % 4, tmp);
+ }
+ } else {
+ static int unzip_order[4] =
+ {0, 4, 1, 5};
+ for (n = 0; n < 4; n++) {
+ int reg = (n < 2) ? rd : rm;
+ tmp = neon_load_scratch(unzip_order[n]);
+ neon_store_reg(reg, n % 2, tmp);
+ }
+ }
+ break;
+ case 35: /* VZIP */
+ /* Reg Before After
+ Rd A3 A2 A1 A0 B1 A1 B0 A0
+ Rm B3 B2 B1 B0 B3 A3 B2 A2
+ */
+ if (size == 3)
+ return 1;
+ count = (q ? 4 : 2);
+ for (n = 0; n < count; n++) {
+ tmp = neon_load_reg(rd, n);
+ tmp2 = neon_load_reg(rd, n);
+ switch (size) {
+ case 0: gen_neon_zip_u8(tmp, tmp2); break;
+ case 1: gen_neon_zip_u16(tmp, tmp2); break;
+ case 2: /* no-op */; break;
+ default: abort();
+ }
+ neon_store_scratch(n * 2, tmp);
+ neon_store_scratch(n * 2 + 1, tmp2);
+ }
+ for (n = 0; n < count * 2; n++) {
+ int reg = (n < count) ? rd : rm;
+ tmp = neon_load_scratch(n);
+ neon_store_reg(reg, n % count, tmp);
+ }
+ break;
+ case 36: case 37: /* VMOVN, VQMOVUN, VQMOVN */
+ if (size == 3)
+ return 1;
+ TCGV_UNUSED(tmp2);
+ for (pass = 0; pass < 2; pass++) {
+ neon_load_reg64(cpu_V0, rm + pass);
+ tmp = new_tmp();
+ if (op == 36 && q == 0) {
+ gen_neon_narrow(size, tmp, cpu_V0);
+ } else if (q) {
+ gen_neon_narrow_satu(size, tmp, cpu_V0);
+ } else {
+ gen_neon_narrow_sats(size, tmp, cpu_V0);
+ }
+ if (pass == 0) {
+ tmp2 = tmp;
+ } else {
+ neon_store_reg(rd, 0, tmp2);
+ neon_store_reg(rd, 1, tmp);
+ }
+ }
+ break;
+ case 38: /* VSHLL */
+ if (q || size == 3)
+ return 1;
+ tmp = neon_load_reg(rm, 0);
+ tmp2 = neon_load_reg(rm, 1);
+ for (pass = 0; pass < 2; pass++) {
+ if (pass == 1)
+ tmp = tmp2;
+ gen_neon_widen(cpu_V0, tmp, size, 1);
+ tcg_gen_shli_i64(cpu_V0, cpu_V0, 8 << size);
+ neon_store_reg64(cpu_V0, rd + pass);
+ }
+ break;
+ case 44: /* VCVT.F16.F32 */
+ if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
+ return 1;
+ tmp = new_tmp();
+ tmp2 = new_tmp();
+ tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 0));
+ gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env);
+ tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 1));
+ gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env);
+ tcg_gen_shli_i32(tmp2, tmp2, 16);
+ tcg_gen_or_i32(tmp2, tmp2, tmp);
+ tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 2));
+ gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env);
+ tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 3));
+ neon_store_reg(rd, 0, tmp2);
+ tmp2 = new_tmp();
+ gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env);
+ tcg_gen_shli_i32(tmp2, tmp2, 16);
+ tcg_gen_or_i32(tmp2, tmp2, tmp);
+ neon_store_reg(rd, 1, tmp2);
+ dead_tmp(tmp);
+ break;
+ case 46: /* VCVT.F32.F16 */
+ if (!arm_feature(env, ARM_FEATURE_VFP_FP16))
+ return 1;
+ tmp3 = new_tmp();
+ tmp = neon_load_reg(rm, 0);
+ tmp2 = neon_load_reg(rm, 1);
+ tcg_gen_ext16u_i32(tmp3, tmp);
+ gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env);
+ tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 0));
+ tcg_gen_shri_i32(tmp3, tmp, 16);
+ gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env);
+ tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 1));
+ dead_tmp(tmp);
+ tcg_gen_ext16u_i32(tmp3, tmp2);
+ gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env);
+ tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 2));
+ tcg_gen_shri_i32(tmp3, tmp2, 16);
+ gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env);
+ tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 3));
+ dead_tmp(tmp2);
+ dead_tmp(tmp3);
+ break;
+ default:
+ elementwise:
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ if (op == 30 || op == 31 || op >= 58) {
+ tcg_gen_ld_f32(cpu_F0s, cpu_env,
+ neon_reg_offset(rm, pass));
+ TCGV_UNUSED(tmp);
+ } else {
+ tmp = neon_load_reg(rm, pass);
+ }
+ switch (op) {
+ case 1: /* VREV32 */
+ switch (size) {
+ case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
+ case 1: gen_swap_half(tmp); break;
+ default: return 1;
+ }
+ break;
+ case 2: /* VREV16 */
+ if (size != 0)
+ return 1;
+ gen_rev16(tmp);
+ break;
+ case 8: /* CLS */
+ switch (size) {
+ case 0: gen_helper_neon_cls_s8(tmp, tmp); break;
+ case 1: gen_helper_neon_cls_s16(tmp, tmp); break;
+ case 2: gen_helper_neon_cls_s32(tmp, tmp); break;
+ default: return 1;
+ }
+ break;
+ case 9: /* CLZ */
+ switch (size) {
+ case 0: gen_helper_neon_clz_u8(tmp, tmp); break;
+ case 1: gen_helper_neon_clz_u16(tmp, tmp); break;
+ case 2: gen_helper_clz(tmp, tmp); break;
+ default: return 1;
+ }
+ break;
+ case 10: /* CNT */
+ if (size != 0)
+ return 1;
+ gen_helper_neon_cnt_u8(tmp, tmp);
+ break;
+ case 11: /* VNOT */
+ if (size != 0)
+ return 1;
+ tcg_gen_not_i32(tmp, tmp);
+ break;
+ case 14: /* VQABS */
+ switch (size) {
+ case 0: gen_helper_neon_qabs_s8(tmp, cpu_env, tmp); break;
+ case 1: gen_helper_neon_qabs_s16(tmp, cpu_env, tmp); break;
+ case 2: gen_helper_neon_qabs_s32(tmp, cpu_env, tmp); break;
+ default: return 1;
+ }
+ break;
+ case 15: /* VQNEG */
+ switch (size) {
+ case 0: gen_helper_neon_qneg_s8(tmp, cpu_env, tmp); break;
+ case 1: gen_helper_neon_qneg_s16(tmp, cpu_env, tmp); break;
+ case 2: gen_helper_neon_qneg_s32(tmp, cpu_env, tmp); break;
+ default: return 1;
+ }
+ break;
+ case 16: case 19: /* VCGT #0, VCLE #0 */
+ tmp2 = tcg_const_i32(0);
+ switch(size) {
+ case 0: gen_helper_neon_cgt_s8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_cgt_s16(tmp, tmp, tmp2); break;
+ case 2: gen_helper_neon_cgt_s32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ tcg_temp_free(tmp2);
+ if (op == 19)
+ tcg_gen_not_i32(tmp, tmp);
+ break;
+ case 17: case 20: /* VCGE #0, VCLT #0 */
+ tmp2 = tcg_const_i32(0);
+ switch(size) {
+ case 0: gen_helper_neon_cge_s8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_cge_s16(tmp, tmp, tmp2); break;
+ case 2: gen_helper_neon_cge_s32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ tcg_temp_free(tmp2);
+ if (op == 20)
+ tcg_gen_not_i32(tmp, tmp);
+ break;
+ case 18: /* VCEQ #0 */
+ tmp2 = tcg_const_i32(0);
+ switch(size) {
+ case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
+ case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
+ case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
+ default: return 1;
+ }
+ tcg_temp_free(tmp2);
+ break;
+ case 22: /* VABS */
+ switch(size) {
+ case 0: gen_helper_neon_abs_s8(tmp, tmp); break;
+ case 1: gen_helper_neon_abs_s16(tmp, tmp); break;
+ case 2: tcg_gen_abs_i32(tmp, tmp); break;
+ default: return 1;
+ }
+ break;
+ case 23: /* VNEG */
+ if (size == 3)
+ return 1;
+ tmp2 = tcg_const_i32(0);
+ gen_neon_rsb(size, tmp, tmp2);
+ tcg_temp_free(tmp2);
+ break;
+ case 24: case 27: /* Float VCGT #0, Float VCLE #0 */
+ tmp2 = tcg_const_i32(0);
+ gen_helper_neon_cgt_f32(tmp, tmp, tmp2);
+ tcg_temp_free(tmp2);
+ if (op == 27)
+ tcg_gen_not_i32(tmp, tmp);
+ break;
+ case 25: case 28: /* Float VCGE #0, Float VCLT #0 */
+ tmp2 = tcg_const_i32(0);
+ gen_helper_neon_cge_f32(tmp, tmp, tmp2);
+ tcg_temp_free(tmp2);
+ if (op == 28)
+ tcg_gen_not_i32(tmp, tmp);
+ break;
+ case 26: /* Float VCEQ #0 */
+ tmp2 = tcg_const_i32(0);
+ gen_helper_neon_ceq_f32(tmp, tmp, tmp2);
+ tcg_temp_free(tmp2);
+ break;
+ case 30: /* Float VABS */
+ gen_vfp_abs(0);
+ break;
+ case 31: /* Float VNEG */
+ gen_vfp_neg(0);
+ break;
+ case 32: /* VSWP */
+ tmp2 = neon_load_reg(rd, pass);
+ neon_store_reg(rm, pass, tmp2);
+ break;
+ case 33: /* VTRN */
+ tmp2 = neon_load_reg(rd, pass);
+ switch (size) {
+ case 0: gen_neon_trn_u8(tmp, tmp2); break;
+ case 1: gen_neon_trn_u16(tmp, tmp2); break;
+ case 2: abort();
+ default: return 1;
+ }
+ neon_store_reg(rm, pass, tmp2);
+ break;
+ case 56: /* Integer VRECPE */
+ gen_helper_recpe_u32(tmp, tmp, cpu_env);
+ break;
+ case 57: /* Integer VRSQRTE */
+ gen_helper_rsqrte_u32(tmp, tmp, cpu_env);
+ break;
+ case 58: /* Float VRECPE */
+ gen_helper_recpe_f32(cpu_F0s, cpu_F0s, cpu_env);
+ break;
+ case 59: /* Float VRSQRTE */
+ gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env);
+ break;
+ case 60: /* VCVT.F32.S32 */
+ gen_vfp_sito(0);
+ break;
+ case 61: /* VCVT.F32.U32 */
+ gen_vfp_uito(0);
+ break;
+ case 62: /* VCVT.S32.F32 */
+ gen_vfp_tosiz(0);
+ break;
+ case 63: /* VCVT.U32.F32 */
+ gen_vfp_touiz(0);
+ break;
+ default:
+ /* Reserved: 21, 29, 39-56 */
+ return 1;
+ }
+ if (op == 30 || op == 31 || op >= 58) {
+ tcg_gen_st_f32(cpu_F0s, cpu_env,
+ neon_reg_offset(rd, pass));
+ } else {
+ neon_store_reg(rd, pass, tmp);
+ }
+ }
+ break;
+ }
+ } else if ((insn & (1 << 10)) == 0) {
+ /* VTBL, VTBX. */
+ n = ((insn >> 5) & 0x18) + 8;
+ if (insn & (1 << 6)) {
+ tmp = neon_load_reg(rd, 0);
+ } else {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ }
+ tmp2 = neon_load_reg(rm, 0);
+ tmp4 = tcg_const_i32(rn);
+ tmp5 = tcg_const_i32(n);
+ gen_helper_neon_tbl(tmp2, tmp2, tmp, tmp4, tmp5);
+ dead_tmp(tmp);
+ if (insn & (1 << 6)) {
+ tmp = neon_load_reg(rd, 1);
+ } else {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ }
+ tmp3 = neon_load_reg(rm, 1);
+ gen_helper_neon_tbl(tmp3, tmp3, tmp, tmp4, tmp5);
+ tcg_temp_free_i32(tmp5);
+ tcg_temp_free_i32(tmp4);
+ neon_store_reg(rd, 0, tmp2);
+ neon_store_reg(rd, 1, tmp3);
+ dead_tmp(tmp);
+ } else if ((insn & 0x380) == 0) {
+ /* VDUP */
+ if (insn & (1 << 19)) {
+ tmp = neon_load_reg(rm, 1);
+ } else {
+ tmp = neon_load_reg(rm, 0);
+ }
+ if (insn & (1 << 16)) {
+ gen_neon_dup_u8(tmp, ((insn >> 17) & 3) * 8);
+ } else if (insn & (1 << 17)) {
+ if ((insn >> 18) & 1)
+ gen_neon_dup_high16(tmp);
+ else
+ gen_neon_dup_low16(tmp);
+ }
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ tmp2 = new_tmp();
+ tcg_gen_mov_i32(tmp2, tmp);
+ neon_store_reg(rd, pass, tmp2);
+ }
+ dead_tmp(tmp);
+ } else {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int disas_cp14_read(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ int crn = (insn >> 16) & 0xf;
+ int crm = insn & 0xf;
+ int op1 = (insn >> 21) & 7;
+ int op2 = (insn >> 5) & 7;
+ int rt = (insn >> 12) & 0xf;
+ TCGv tmp;
+
+ if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
+ if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) {
+ /* TEECR */
+ if (IS_USER(s))
+ return 1;
+ tmp = load_cpu_field(teecr);
+ store_reg(s, rt, tmp);
+ return 0;
+ }
+ if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) {
+ /* TEEHBR */
+ if (IS_USER(s) && (env->teecr & 1))
+ return 1;
+ tmp = load_cpu_field(teehbr);
+ store_reg(s, rt, tmp);
+ return 0;
+ }
+ }
+ fprintf(stderr, "Unknown cp14 read op1:%d crn:%d crm:%d op2:%d\n",
+ op1, crn, crm, op2);
+ return 1;
+}
+
+static int disas_cp14_write(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ int crn = (insn >> 16) & 0xf;
+ int crm = insn & 0xf;
+ int op1 = (insn >> 21) & 7;
+ int op2 = (insn >> 5) & 7;
+ int rt = (insn >> 12) & 0xf;
+ TCGv tmp;
+
+ if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
+ if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) {
+ /* TEECR */
+ if (IS_USER(s))
+ return 1;
+ tmp = load_reg(s, rt);
+ gen_helper_set_teecr(cpu_env, tmp);
+ dead_tmp(tmp);
+ return 0;
+ }
+ if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) {
+ /* TEEHBR */
+ if (IS_USER(s) && (env->teecr & 1))
+ return 1;
+ tmp = load_reg(s, rt);
+ store_cpu_field(tmp, teehbr);
+ return 0;
+ }
+ }
+ fprintf(stderr, "Unknown cp14 write op1:%d crn:%d crm:%d op2:%d\n",
+ op1, crn, crm, op2);
+ return 1;
+}
+
+static int disas_coproc_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ int cpnum;
+
+ cpnum = (insn >> 8) & 0xf;
+ if (arm_feature(env, ARM_FEATURE_XSCALE)
+ && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum)))
+ return 1;
+
+ switch (cpnum) {
+ case 0:
+ case 1:
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ return disas_iwmmxt_insn(env, s, insn);
+ } else if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ return disas_dsp_insn(env, s, insn);
+ }
+ return 1;
+ case 10:
+ case 11:
+ return disas_vfp_insn (env, s, insn);
+ case 14:
+ /* Coprocessors 7-15 are architecturally reserved by ARM.
+ Unfortunately Intel decided to ignore this. */
+ if (arm_feature(env, ARM_FEATURE_XSCALE))
+ goto board;
+ if (insn & (1 << 20))
+ return disas_cp14_read(env, s, insn);
+ else
+ return disas_cp14_write(env, s, insn);
+ case 15:
+ return disas_cp15_insn (env, s, insn);
+ default:
+ board:
+ /* Unknown coprocessor. See if the board has hooked it. */
+ return disas_cp_insn (env, s, insn);
+ }
+}
+
+
+/* Store a 64-bit value to a register pair. Clobbers val. */
+static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val)
+{
+ TCGv tmp;
+ tmp = new_tmp();
+ tcg_gen_trunc_i64_i32(tmp, val);
+ store_reg(s, rlow, tmp);
+ tmp = new_tmp();
+ tcg_gen_shri_i64(val, val, 32);
+ tcg_gen_trunc_i64_i32(tmp, val);
+ store_reg(s, rhigh, tmp);
+}
+
+/* load a 32-bit value from a register and perform a 64-bit accumulate. */
+static void gen_addq_lo(DisasContext *s, TCGv_i64 val, int rlow)
+{
+ TCGv_i64 tmp;
+ TCGv tmp2;
+
+ /* Load value and extend to 64 bits. */
+ tmp = tcg_temp_new_i64();
+ tmp2 = load_reg(s, rlow);
+ tcg_gen_extu_i32_i64(tmp, tmp2);
+ dead_tmp(tmp2);
+ tcg_gen_add_i64(val, val, tmp);
+ tcg_temp_free_i64(tmp);
+}
+
+/* load and add a 64-bit value from a register pair. */
+static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh)
+{
+ TCGv_i64 tmp;
+ TCGv tmpl;
+ TCGv tmph;
+
+ /* Load 64-bit value rd:rn. */
+ tmpl = load_reg(s, rlow);
+ tmph = load_reg(s, rhigh);
+ tmp = tcg_temp_new_i64();
+ tcg_gen_concat_i32_i64(tmp, tmpl, tmph);
+ dead_tmp(tmpl);
+ dead_tmp(tmph);
+ tcg_gen_add_i64(val, val, tmp);
+ tcg_temp_free_i64(tmp);
+}
+
+/* Set N and Z flags from a 64-bit value. */
+static void gen_logicq_cc(TCGv_i64 val)
+{
+ TCGv tmp = new_tmp();
+ gen_helper_logicq_cc(tmp, val);
+ gen_logic_CC(tmp);
+ dead_tmp(tmp);
+}
+
+/* Load/Store exclusive instructions are implemented by remembering
+ the value/address loaded, and seeing if these are the same
+ when the store is performed. This should be is sufficient to implement
+ the architecturally mandated semantics, and avoids having to monitor
+ regular stores.
+
+ In system emulation mode only one CPU will be running at once, so
+ this sequence is effectively atomic. In user emulation mode we
+ throw an exception and handle the atomic operation elsewhere. */
+static void gen_load_exclusive(DisasContext *s, int rt, int rt2,
+ TCGv addr, int size)
+{
+ TCGv tmp;
+
+ switch (size) {
+ case 0:
+ tmp = gen_ld8u(addr, IS_USER(s));
+ break;
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 2:
+ case 3:
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ default:
+ abort();
+ }
+ tcg_gen_mov_i32(cpu_exclusive_val, tmp);
+ store_reg(s, rt, tmp);
+ if (size == 3) {
+ TCGv tmp2 = new_tmp();
+ tcg_gen_addi_i32(tmp2, addr, 4);
+ tmp = gen_ld32(tmp2, IS_USER(s));
+ dead_tmp(tmp2);
+ tcg_gen_mov_i32(cpu_exclusive_high, tmp);
+ store_reg(s, rt2, tmp);
+ }
+ tcg_gen_mov_i32(cpu_exclusive_addr, addr);
+}
+
+static void gen_clrex(DisasContext *s)
+{
+ tcg_gen_movi_i32(cpu_exclusive_addr, -1);
+}
+
+#ifdef CONFIG_USER_ONLY
+static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
+ TCGv addr, int size)
+{
+ tcg_gen_mov_i32(cpu_exclusive_test, addr);
+ tcg_gen_movi_i32(cpu_exclusive_info,
+ size | (rd << 4) | (rt << 8) | (rt2 << 12));
+ gen_exception_insn(s, 4, EXCP_STREX);
+}
+#else
+static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2,
+ TCGv addr, int size)
+{
+ TCGv tmp;
+ int done_label;
+ int fail_label;
+
+ /* if (env->exclusive_addr == addr && env->exclusive_val == [addr]) {
+ [addr] = {Rt};
+ {Rd} = 0;
+ } else {
+ {Rd} = 1;
+ } */
+ fail_label = gen_new_label();
+ done_label = gen_new_label();
+ tcg_gen_brcond_i32(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label);
+ switch (size) {
+ case 0:
+ tmp = gen_ld8u(addr, IS_USER(s));
+ break;
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 2:
+ case 3:
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ default:
+ abort();
+ }
+ tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_val, fail_label);
+ dead_tmp(tmp);
+ if (size == 3) {
+ TCGv tmp2 = new_tmp();
+ tcg_gen_addi_i32(tmp2, addr, 4);
+ tmp = gen_ld32(tmp2, IS_USER(s));
+ dead_tmp(tmp2);
+ tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label);
+ dead_tmp(tmp);
+ }
+ tmp = load_reg(s, rt);
+ switch (size) {
+ case 0:
+ gen_st8(tmp, addr, IS_USER(s));
+ break;
+ case 1:
+ gen_st16(tmp, addr, IS_USER(s));
+ break;
+ case 2:
+ case 3:
+ gen_st32(tmp, addr, IS_USER(s));
+ break;
+ default:
+ abort();
+ }
+ if (size == 3) {
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = load_reg(s, rt2);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ tcg_gen_movi_i32(cpu_R[rd], 0);
+ tcg_gen_br(done_label);
+ gen_set_label(fail_label);
+ tcg_gen_movi_i32(cpu_R[rd], 1);
+ gen_set_label(done_label);
+ tcg_gen_movi_i32(cpu_exclusive_addr, -1);
+}
+#endif
+
+static void disas_arm_insn(CPUState * env, DisasContext *s)
+{
+ unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;
+ TCGv tmp;
+ TCGv tmp2;
+ TCGv tmp3;
+ TCGv addr;
+ TCGv_i64 tmp64;
+
+ insn = ldl_code(s->pc);
+ s->pc += 4;
+
+ /* M variants do not implement ARM mode. */
+ if (IS_M(env))
+ goto illegal_op;
+ cond = insn >> 28;
+ if (cond == 0xf){
+ /* Unconditional instructions. */
+ if (((insn >> 25) & 7) == 1) {
+ /* NEON Data processing. */
+ if (!arm_feature(env, ARM_FEATURE_NEON))
+ goto illegal_op;
+
+ if (disas_neon_data_insn(env, s, insn))
+ goto illegal_op;
+ return;
+ }
+ if ((insn & 0x0f100000) == 0x04000000) {
+ /* NEON load/store. */
+ if (!arm_feature(env, ARM_FEATURE_NEON))
+ goto illegal_op;
+
+ if (disas_neon_ls_insn(env, s, insn))
+ goto illegal_op;
+ return;
+ }
+ if ((insn & 0x0d70f000) == 0x0550f000)
+ return; /* PLD */
+ else if ((insn & 0x0ffffdff) == 0x01010000) {
+ ARCH(6);
+ /* setend */
+ if (insn & (1 << 9)) {
+ /* BE8 mode not implemented. */
+ goto illegal_op;
+ }
+ return;
+ } else if ((insn & 0x0fffff00) == 0x057ff000) {
+ switch ((insn >> 4) & 0xf) {
+ case 1: /* clrex */
+ ARCH(6K);
+ gen_clrex(s);
+ return;
+ case 4: /* dsb */
+ case 5: /* dmb */
+ case 6: /* isb */
+ ARCH(7);
+ /* We don't emulate caches so these are a no-op. */
+ return;
+ default:
+ goto illegal_op;
+ }
+ } else if ((insn & 0x0e5fffe0) == 0x084d0500) {
+ /* srs */
+ int32_t offset;
+ if (IS_USER(s))
+ goto illegal_op;
+ ARCH(6);
+ op1 = (insn & 0x1f);
+ addr = new_tmp();
+ tmp = tcg_const_i32(op1);
+ gen_helper_get_r13_banked(addr, cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+ i = (insn >> 23) & 3;
+ switch (i) {
+ case 0: offset = -4; break; /* DA */
+ case 1: offset = 0; break; /* IA */
+ case 2: offset = -8; break; /* DB */
+ case 3: offset = 4; break; /* IB */
+ default: abort();
+ }
+ if (offset)
+ tcg_gen_addi_i32(addr, addr, offset);
+ tmp = load_reg(s, 14);
+ gen_st32(tmp, addr, 0);
+ tmp = load_cpu_field(spsr);
+ tcg_gen_addi_i32(addr, addr, 4);
+ gen_st32(tmp, addr, 0);
+ if (insn & (1 << 21)) {
+ /* Base writeback. */
+ switch (i) {
+ case 0: offset = -8; break;
+ case 1: offset = 4; break;
+ case 2: offset = -4; break;
+ case 3: offset = 0; break;
+ default: abort();
+ }
+ if (offset)
+ tcg_gen_addi_i32(addr, addr, offset);
+ tmp = tcg_const_i32(op1);
+ gen_helper_set_r13_banked(cpu_env, tmp, addr);
+ tcg_temp_free_i32(tmp);
+ dead_tmp(addr);
+ } else {
+ dead_tmp(addr);
+ }
+ return;
+ } else if ((insn & 0x0e50ffe0) == 0x08100a00) {
+ /* rfe */
+ int32_t offset;
+ if (IS_USER(s))
+ goto illegal_op;
+ ARCH(6);
+ rn = (insn >> 16) & 0xf;
+ addr = load_reg(s, rn);
+ i = (insn >> 23) & 3;
+ switch (i) {
+ case 0: offset = -4; break; /* DA */
+ case 1: offset = 0; break; /* IA */
+ case 2: offset = -8; break; /* DB */
+ case 3: offset = 4; break; /* IB */
+ default: abort();
+ }
+ if (offset)
+ tcg_gen_addi_i32(addr, addr, offset);
+ /* Load PC into tmp and CPSR into tmp2. */
+ tmp = gen_ld32(addr, 0);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp2 = gen_ld32(addr, 0);
+ if (insn & (1 << 21)) {
+ /* Base writeback. */
+ switch (i) {
+ case 0: offset = -8; break;
+ case 1: offset = 4; break;
+ case 2: offset = -4; break;
+ case 3: offset = 0; break;
+ default: abort();
+ }
+ if (offset)
+ tcg_gen_addi_i32(addr, addr, offset);
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ gen_rfe(s, tmp, tmp2);
+ return;
+ } else if ((insn & 0x0e000000) == 0x0a000000) {
+ /* branch link and change to thumb (blx <offset>) */
+ int32_t offset;
+
+ val = (uint32_t)s->pc;
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, val);
+ store_reg(s, 14, tmp);
+ /* Sign-extend the 24-bit offset */
+ offset = (((int32_t)insn) << 8) >> 8;
+ /* offset * 4 + bit24 * 2 + (thumb bit) */
+ val += (offset << 2) | ((insn >> 23) & 2) | 1;
+ /* pipeline offset */
+ val += 4;
+ gen_bx_im(s, val);
+ return;
+ } else if ((insn & 0x0e000f00) == 0x0c000100) {
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ /* iWMMXt register transfer. */
+ if (env->cp15.c15_cpar & (1 << 1))
+ if (!disas_iwmmxt_insn(env, s, insn))
+ return;
+ }
+ } else if ((insn & 0x0fe00000) == 0x0c400000) {
+ /* Coprocessor double register transfer. */
+ } else if ((insn & 0x0f000010) == 0x0e000010) {
+ /* Additional coprocessor register transfer. */
+ } else if ((insn & 0x0ff10020) == 0x01000000) {
+ uint32_t mask;
+ uint32_t val;
+ /* cps (privileged) */
+ if (IS_USER(s))
+ return;
+ mask = val = 0;
+ if (insn & (1 << 19)) {
+ if (insn & (1 << 8))
+ mask |= CPSR_A;
+ if (insn & (1 << 7))
+ mask |= CPSR_I;
+ if (insn & (1 << 6))
+ mask |= CPSR_F;
+ if (insn & (1 << 18))
+ val |= mask;
+ }
+ if (insn & (1 << 17)) {
+ mask |= CPSR_M;
+ val |= (insn & 0x1f);
+ }
+ if (mask) {
+ gen_set_psr_im(s, mask, 0, val);
+ }
+ return;
+ }
+ goto illegal_op;
+ }
+ if (cond != 0xe) {
+ /* if not always execute, we generate a conditional jump to
+ next instruction */
+ s->condlabel = gen_new_label();
+ gen_test_cc(cond ^ 1, s->condlabel);
+ s->condjmp = 1;
+ }
+ if ((insn & 0x0f900000) == 0x03000000) {
+ if ((insn & (1 << 21)) == 0) {
+ ARCH(6T2);
+ rd = (insn >> 12) & 0xf;
+ val = ((insn >> 4) & 0xf000) | (insn & 0xfff);
+ if ((insn & (1 << 22)) == 0) {
+ /* MOVW */
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, val);
+ } else {
+ /* MOVT */
+ tmp = load_reg(s, rd);
+ tcg_gen_ext16u_i32(tmp, tmp);
+ tcg_gen_ori_i32(tmp, tmp, val << 16);
+ }
+ store_reg(s, rd, tmp);
+ } else {
+ if (((insn >> 12) & 0xf) != 0xf)
+ goto illegal_op;
+ if (((insn >> 16) & 0xf) == 0) {
+ gen_nop_hint(s, insn & 0xff);
+ } else {
+ /* CPSR = immediate */
+ val = insn & 0xff;
+ shift = ((insn >> 8) & 0xf) * 2;
+ if (shift)
+ val = (val >> shift) | (val << (32 - shift));
+ i = ((insn & (1 << 22)) != 0);
+ if (gen_set_psr_im(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i, val))
+ goto illegal_op;
+ }
+ }
+ } else if ((insn & 0x0f900000) == 0x01000000
+ && (insn & 0x00000090) != 0x00000090) {
+ /* miscellaneous instructions */
+ op1 = (insn >> 21) & 3;
+ sh = (insn >> 4) & 0xf;
+ rm = insn & 0xf;
+ switch (sh) {
+ case 0x0: /* move program status register */
+ if (op1 & 1) {
+ /* PSR = reg */
+ tmp = load_reg(s, rm);
+ i = ((op1 & 2) != 0);
+ if (gen_set_psr(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i, tmp))
+ goto illegal_op;
+ } else {
+ /* reg = PSR */
+ rd = (insn >> 12) & 0xf;
+ if (op1 & 2) {
+ if (IS_USER(s))
+ goto illegal_op;
+ tmp = load_cpu_field(spsr);
+ } else {
+ tmp = new_tmp();
+ gen_helper_cpsr_read(tmp);
+ }
+ store_reg(s, rd, tmp);
+ }
+ break;
+ case 0x1:
+ if (op1 == 1) {
+ /* branch/exchange thumb (bx). */
+ tmp = load_reg(s, rm);
+ gen_bx(s, tmp);
+ } else if (op1 == 3) {
+ /* clz */
+ rd = (insn >> 12) & 0xf;
+ tmp = load_reg(s, rm);
+ gen_helper_clz(tmp, tmp);
+ store_reg(s, rd, tmp);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x2:
+ if (op1 == 1) {
+ ARCH(5J); /* bxj */
+ /* Trivial implementation equivalent to bx. */
+ tmp = load_reg(s, rm);
+ gen_bx(s, tmp);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x3:
+ if (op1 != 1)
+ goto illegal_op;
+
+ /* branch link/exchange thumb (blx) */
+ tmp = load_reg(s, rm);
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, s->pc);
+ store_reg(s, 14, tmp2);
+ gen_bx(s, tmp);
+ break;
+ case 0x5: /* saturating add/subtract */
+ rd = (insn >> 12) & 0xf;
+ rn = (insn >> 16) & 0xf;
+ tmp = load_reg(s, rm);
+ tmp2 = load_reg(s, rn);
+ if (op1 & 2)
+ gen_helper_double_saturate(tmp2, tmp2);
+ if (op1 & 1)
+ gen_helper_sub_saturate(tmp, tmp, tmp2);
+ else
+ gen_helper_add_saturate(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ break;
+ case 7:
+ /* SMC instruction (op1 == 3)
+ and undefined instructions (op1 == 0 || op1 == 2)
+ will trap */
+ if (op1 != 1) {
+ goto illegal_op;
+ }
+ /* bkpt */
+ gen_exception_insn(s, 4, EXCP_BKPT);
+ break;
+ case 0x8: /* signed multiply */
+ case 0xa:
+ case 0xc:
+ case 0xe:
+ rs = (insn >> 8) & 0xf;
+ rn = (insn >> 12) & 0xf;
+ rd = (insn >> 16) & 0xf;
+ if (op1 == 1) {
+ /* (32 * 16) >> 16 */
+ tmp = load_reg(s, rm);
+ tmp2 = load_reg(s, rs);
+ if (sh & 4)
+ tcg_gen_sari_i32(tmp2, tmp2, 16);
+ else
+ gen_sxth(tmp2);
+ tmp64 = gen_muls_i64_i32(tmp, tmp2);
+ tcg_gen_shri_i64(tmp64, tmp64, 16);
+ tmp = new_tmp();
+ tcg_gen_trunc_i64_i32(tmp, tmp64);
+ tcg_temp_free_i64(tmp64);
+ if ((sh & 2) == 0) {
+ tmp2 = load_reg(s, rn);
+ gen_helper_add_setq(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ store_reg(s, rd, tmp);
+ } else {
+ /* 16 * 16 */
+ tmp = load_reg(s, rm);
+ tmp2 = load_reg(s, rs);
+ gen_mulxy(tmp, tmp2, sh & 2, sh & 4);
+ dead_tmp(tmp2);
+ if (op1 == 2) {
+ tmp64 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp64, tmp);
+ dead_tmp(tmp);
+ gen_addq(s, tmp64, rn, rd);
+ gen_storeq_reg(s, rn, rd, tmp64);
+ tcg_temp_free_i64(tmp64);
+ } else {
+ if (op1 == 0) {
+ tmp2 = load_reg(s, rn);
+ gen_helper_add_setq(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ store_reg(s, rd, tmp);
+ }
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else if (((insn & 0x0e000000) == 0 &&
+ (insn & 0x00000090) != 0x90) ||
+ ((insn & 0x0e000000) == (1 << 25))) {
+ int set_cc, logic_cc, shiftop;
+
+ op1 = (insn >> 21) & 0xf;
+ set_cc = (insn >> 20) & 1;
+ logic_cc = table_logic_cc[op1] & set_cc;
+
+ /* data processing instruction */
+ if (insn & (1 << 25)) {
+ /* immediate operand */
+ val = insn & 0xff;
+ shift = ((insn >> 8) & 0xf) * 2;
+ if (shift) {
+ val = (val >> shift) | (val << (32 - shift));
+ }
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, val);
+ if (logic_cc && shift) {
+ gen_set_CF_bit31(tmp2);
+ }
+ } else {
+ /* register */
+ rm = (insn) & 0xf;
+ tmp2 = load_reg(s, rm);
+ shiftop = (insn >> 5) & 3;
+ if (!(insn & (1 << 4))) {
+ shift = (insn >> 7) & 0x1f;
+ gen_arm_shift_im(tmp2, shiftop, shift, logic_cc);
+ } else {
+ rs = (insn >> 8) & 0xf;
+ tmp = load_reg(s, rs);
+ gen_arm_shift_reg(tmp2, shiftop, tmp, logic_cc);
+ }
+ }
+ if (op1 != 0x0f && op1 != 0x0d) {
+ rn = (insn >> 16) & 0xf;
+ tmp = load_reg(s, rn);
+ } else {
+ TCGV_UNUSED(tmp);
+ }
+ rd = (insn >> 12) & 0xf;
+ switch(op1) {
+ case 0x00:
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ if (logic_cc) {
+ gen_logic_CC(tmp);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x01:
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ if (logic_cc) {
+ gen_logic_CC(tmp);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x02:
+ if (set_cc && rd == 15) {
+ /* SUBS r15, ... is used for exception return. */
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ gen_exception_return(s, tmp);
+ } else {
+ if (set_cc) {
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ } else {
+ tcg_gen_sub_i32(tmp, tmp, tmp2);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ }
+ break;
+ case 0x03:
+ if (set_cc) {
+ gen_helper_sub_cc(tmp, tmp2, tmp);
+ } else {
+ tcg_gen_sub_i32(tmp, tmp2, tmp);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x04:
+ if (set_cc) {
+ gen_helper_add_cc(tmp, tmp, tmp2);
+ } else {
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x05:
+ if (set_cc) {
+ gen_helper_adc_cc(tmp, tmp, tmp2);
+ } else {
+ gen_add_carry(tmp, tmp, tmp2);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x06:
+ if (set_cc) {
+ gen_helper_sbc_cc(tmp, tmp, tmp2);
+ } else {
+ gen_sub_carry(tmp, tmp, tmp2);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x07:
+ if (set_cc) {
+ gen_helper_sbc_cc(tmp, tmp2, tmp);
+ } else {
+ gen_sub_carry(tmp, tmp2, tmp);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x08:
+ if (set_cc) {
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ gen_logic_CC(tmp);
+ }
+ dead_tmp(tmp);
+ break;
+ case 0x09:
+ if (set_cc) {
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ gen_logic_CC(tmp);
+ }
+ dead_tmp(tmp);
+ break;
+ case 0x0a:
+ if (set_cc) {
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ }
+ dead_tmp(tmp);
+ break;
+ case 0x0b:
+ if (set_cc) {
+ gen_helper_add_cc(tmp, tmp, tmp2);
+ }
+ dead_tmp(tmp);
+ break;
+ case 0x0c:
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ if (logic_cc) {
+ gen_logic_CC(tmp);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 0x0d:
+ if (logic_cc && rd == 15) {
+ /* MOVS r15, ... is used for exception return. */
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ gen_exception_return(s, tmp2);
+ } else {
+ if (logic_cc) {
+ gen_logic_CC(tmp2);
+ }
+ store_reg_bx(env, s, rd, tmp2);
+ }
+ break;
+ case 0x0e:
+ tcg_gen_andc_i32(tmp, tmp, tmp2);
+ if (logic_cc) {
+ gen_logic_CC(tmp);
+ }
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ default:
+ case 0x0f:
+ tcg_gen_not_i32(tmp2, tmp2);
+ if (logic_cc) {
+ gen_logic_CC(tmp2);
+ }
+ store_reg_bx(env, s, rd, tmp2);
+ break;
+ }
+ if (op1 != 0x0f && op1 != 0x0d) {
+ dead_tmp(tmp2);
+ }
+ } else {
+ /* other instructions */
+ op1 = (insn >> 24) & 0xf;
+ switch(op1) {
+ case 0x0:
+ case 0x1:
+ /* multiplies, extra load/stores */
+ sh = (insn >> 5) & 3;
+ if (sh == 0) {
+ if (op1 == 0x0) {
+ rd = (insn >> 16) & 0xf;
+ rn = (insn >> 12) & 0xf;
+ rs = (insn >> 8) & 0xf;
+ rm = (insn) & 0xf;
+ op1 = (insn >> 20) & 0xf;
+ switch (op1) {
+ case 0: case 1: case 2: case 3: case 6:
+ /* 32 bit mul */
+ tmp = load_reg(s, rs);
+ tmp2 = load_reg(s, rm);
+ tcg_gen_mul_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ if (insn & (1 << 22)) {
+ /* Subtract (mls) */
+ ARCH(6T2);
+ tmp2 = load_reg(s, rn);
+ tcg_gen_sub_i32(tmp, tmp2, tmp);
+ dead_tmp(tmp2);
+ } else if (insn & (1 << 21)) {
+ /* Add */
+ tmp2 = load_reg(s, rn);
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ if (insn & (1 << 20))
+ gen_logic_CC(tmp);
+ store_reg(s, rd, tmp);
+ break;
+ case 4:
+ /* 64 bit mul double accumulate (UMAAL) */
+ ARCH(6);
+ tmp = load_reg(s, rs);
+ tmp2 = load_reg(s, rm);
+ tmp64 = gen_mulu_i64_i32(tmp, tmp2);
+ gen_addq_lo(s, tmp64, rn);
+ gen_addq_lo(s, tmp64, rd);
+ gen_storeq_reg(s, rn, rd, tmp64);
+ tcg_temp_free_i64(tmp64);
+ break;
+ case 8: case 9: case 10: case 11:
+ case 12: case 13: case 14: case 15:
+ /* 64 bit mul: UMULL, UMLAL, SMULL, SMLAL. */
+ tmp = load_reg(s, rs);
+ tmp2 = load_reg(s, rm);
+ if (insn & (1 << 22)) {
+ tmp64 = gen_muls_i64_i32(tmp, tmp2);
+ } else {
+ tmp64 = gen_mulu_i64_i32(tmp, tmp2);
+ }
+ if (insn & (1 << 21)) { /* mult accumulate */
+ gen_addq(s, tmp64, rn, rd);
+ }
+ if (insn & (1 << 20)) {
+ gen_logicq_cc(tmp64);
+ }
+ gen_storeq_reg(s, rn, rd, tmp64);
+ tcg_temp_free_i64(tmp64);
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else {
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ if (insn & (1 << 23)) {
+ /* load/store exclusive */
+ op1 = (insn >> 21) & 0x3;
+ if (op1)
+ ARCH(6K);
+ else
+ ARCH(6);
+ addr = tcg_temp_local_new_i32();
+ load_reg_var(s, addr, rn);
+ if (insn & (1 << 20)) {
+ switch (op1) {
+ case 0: /* ldrex */
+ gen_load_exclusive(s, rd, 15, addr, 2);
+ break;
+ case 1: /* ldrexd */
+ gen_load_exclusive(s, rd, rd + 1, addr, 3);
+ break;
+ case 2: /* ldrexb */
+ gen_load_exclusive(s, rd, 15, addr, 0);
+ break;
+ case 3: /* ldrexh */
+ gen_load_exclusive(s, rd, 15, addr, 1);
+ break;
+ default:
+ abort();
+ }
+ } else {
+ rm = insn & 0xf;
+ switch (op1) {
+ case 0: /* strex */
+ gen_store_exclusive(s, rd, rm, 15, addr, 2);
+ break;
+ case 1: /* strexd */
+ gen_store_exclusive(s, rd, rm, rm + 1, addr, 3);
+ break;
+ case 2: /* strexb */
+ gen_store_exclusive(s, rd, rm, 15, addr, 0);
+ break;
+ case 3: /* strexh */
+ gen_store_exclusive(s, rd, rm, 15, addr, 1);
+ break;
+ default:
+ abort();
+ }
+ }
+ tcg_temp_free(addr);
+ } else {
+ /* SWP instruction */
+ rm = (insn) & 0xf;
+
+ /* ??? This is not really atomic. However we know
+ we never have multiple CPUs running in parallel,
+ so it is good enough. */
+ addr = load_reg(s, rn);
+ tmp = load_reg(s, rm);
+ if (insn & (1 << 22)) {
+ tmp2 = gen_ld8u(addr, IS_USER(s));
+ gen_st8(tmp, addr, IS_USER(s));
+ } else {
+ tmp2 = gen_ld32(addr, IS_USER(s));
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ dead_tmp(addr);
+ store_reg(s, rd, tmp2);
+ }
+ }
+ } else {
+ int address_offset;
+ int load;
+ /* Misc load/store */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ addr = load_reg(s, rn);
+ if (insn & (1 << 24))
+ gen_add_datah_offset(s, insn, 0, addr);
+ address_offset = 0;
+ if (insn & (1 << 20)) {
+ /* load */
+ switch(sh) {
+ case 1:
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 2:
+ tmp = gen_ld8s(addr, IS_USER(s));
+ break;
+ default:
+ case 3:
+ tmp = gen_ld16s(addr, IS_USER(s));
+ break;
+ }
+ load = 1;
+ } else if (sh & 2) {
+ /* doubleword */
+ if (sh & 1) {
+ /* store */
+ tmp = load_reg(s, rd);
+ gen_st32(tmp, addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = load_reg(s, rd + 1);
+ gen_st32(tmp, addr, IS_USER(s));
+ load = 0;
+ } else {
+ /* load */
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, rd, tmp);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = gen_ld32(addr, IS_USER(s));
+ rd++;
+ load = 1;
+ }
+ address_offset = -4;
+ } else {
+ /* store */
+ tmp = load_reg(s, rd);
+ gen_st16(tmp, addr, IS_USER(s));
+ load = 0;
+ }
+ /* Perform base writeback before the loaded value to
+ ensure correct behavior with overlapping index registers.
+ ldrd with base writeback is is undefined if the
+ destination and index registers overlap. */
+ if (!(insn & (1 << 24))) {
+ gen_add_datah_offset(s, insn, address_offset, addr);
+ store_reg(s, rn, addr);
+ } else if (insn & (1 << 21)) {
+ if (address_offset)
+ tcg_gen_addi_i32(addr, addr, address_offset);
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ if (load) {
+ /* Complete the load. */
+ store_reg(s, rd, tmp);
+ }
+ }
+ break;
+ case 0x4:
+ case 0x5:
+ goto do_ldst;
+ case 0x6:
+ case 0x7:
+ if (insn & (1 << 4)) {
+ ARCH(6);
+ /* Armv6 Media instructions. */
+ rm = insn & 0xf;
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ rs = (insn >> 8) & 0xf;
+ switch ((insn >> 23) & 3) {
+ case 0: /* Parallel add/subtract. */
+ op1 = (insn >> 20) & 7;
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ sh = (insn >> 5) & 7;
+ if ((op1 & 3) == 0 || sh == 5 || sh == 6)
+ goto illegal_op;
+ gen_arm_parallel_addsub(op1, sh, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ break;
+ case 1:
+ if ((insn & 0x00700020) == 0) {
+ /* Halfword pack. */
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ shift = (insn >> 7) & 0x1f;
+ if (insn & (1 << 6)) {
+ /* pkhtb */
+ if (shift == 0)
+ shift = 31;
+ tcg_gen_sari_i32(tmp2, tmp2, shift);
+ tcg_gen_andi_i32(tmp, tmp, 0xffff0000);
+ tcg_gen_ext16u_i32(tmp2, tmp2);
+ } else {
+ /* pkhbt */
+ if (shift)
+ tcg_gen_shli_i32(tmp2, tmp2, shift);
+ tcg_gen_ext16u_i32(tmp, tmp);
+ tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000);
+ }
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ } else if ((insn & 0x00200020) == 0x00200000) {
+ /* [us]sat */
+ tmp = load_reg(s, rm);
+ shift = (insn >> 7) & 0x1f;
+ if (insn & (1 << 6)) {
+ if (shift == 0)
+ shift = 31;
+ tcg_gen_sari_i32(tmp, tmp, shift);
+ } else {
+ tcg_gen_shli_i32(tmp, tmp, shift);
+ }
+ sh = (insn >> 16) & 0x1f;
+ tmp2 = tcg_const_i32(sh);
+ if (insn & (1 << 22))
+ gen_helper_usat(tmp, tmp, tmp2);
+ else
+ gen_helper_ssat(tmp, tmp, tmp2);
+ tcg_temp_free_i32(tmp2);
+ store_reg(s, rd, tmp);
+ } else if ((insn & 0x00300fe0) == 0x00200f20) {
+ /* [us]sat16 */
+ tmp = load_reg(s, rm);
+ sh = (insn >> 16) & 0x1f;
+ tmp2 = tcg_const_i32(sh);
+ if (insn & (1 << 22))
+ gen_helper_usat16(tmp, tmp, tmp2);
+ else
+ gen_helper_ssat16(tmp, tmp, tmp2);
+ tcg_temp_free_i32(tmp2);
+ store_reg(s, rd, tmp);
+ } else if ((insn & 0x00700fe0) == 0x00000fa0) {
+ /* Select bytes. */
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ tmp3 = new_tmp();
+ tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE));
+ gen_helper_sel_flags(tmp, tmp3, tmp, tmp2);
+ dead_tmp(tmp3);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ } else if ((insn & 0x000003e0) == 0x00000060) {
+ tmp = load_reg(s, rm);
+ shift = (insn >> 10) & 3;
+ /* ??? In many cases it's not neccessary to do a
+ rotate, a shift is sufficient. */
+ if (shift != 0)
+ tcg_gen_rotri_i32(tmp, tmp, shift * 8);
+ op1 = (insn >> 20) & 7;
+ switch (op1) {
+ case 0: gen_sxtb16(tmp); break;
+ case 2: gen_sxtb(tmp); break;
+ case 3: gen_sxth(tmp); break;
+ case 4: gen_uxtb16(tmp); break;
+ case 6: gen_uxtb(tmp); break;
+ case 7: gen_uxth(tmp); break;
+ default: goto illegal_op;
+ }
+ if (rn != 15) {
+ tmp2 = load_reg(s, rn);
+ if ((op1 & 3) == 0) {
+ gen_add16(tmp, tmp2);
+ } else {
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ }
+ store_reg(s, rd, tmp);
+ } else if ((insn & 0x003f0f60) == 0x003f0f20) {
+ /* rev */
+ tmp = load_reg(s, rm);
+ if (insn & (1 << 22)) {
+ if (insn & (1 << 7)) {
+ gen_revsh(tmp);
+ } else {
+ ARCH(6T2);
+ gen_helper_rbit(tmp, tmp);
+ }
+ } else {
+ if (insn & (1 << 7))
+ gen_rev16(tmp);
+ else
+ tcg_gen_bswap32_i32(tmp, tmp);
+ }
+ store_reg(s, rd, tmp);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 2: /* Multiplies (Type 3). */
+ tmp = load_reg(s, rm);
+ tmp2 = load_reg(s, rs);
+ if (insn & (1 << 20)) {
+ /* Signed multiply most significant [accumulate].
+ (SMMUL, SMMLA, SMMLS) */
+ tmp64 = gen_muls_i64_i32(tmp, tmp2);
+
+ if (rd != 15) {
+ tmp = load_reg(s, rd);
+ if (insn & (1 << 6)) {
+ tmp64 = gen_subq_msw(tmp64, tmp);
+ } else {
+ tmp64 = gen_addq_msw(tmp64, tmp);
+ }
+ }
+ if (insn & (1 << 5)) {
+ tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u);
+ }
+ tcg_gen_shri_i64(tmp64, tmp64, 32);
+ tmp = new_tmp();
+ tcg_gen_trunc_i64_i32(tmp, tmp64);
+ tcg_temp_free_i64(tmp64);
+ store_reg(s, rn, tmp);
+ } else {
+ if (insn & (1 << 5))
+ gen_swap_half(tmp2);
+ gen_smul_dual(tmp, tmp2);
+ /* This addition cannot overflow. */
+ if (insn & (1 << 6)) {
+ tcg_gen_sub_i32(tmp, tmp, tmp2);
+ } else {
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ }
+ dead_tmp(tmp2);
+ if (insn & (1 << 22)) {
+ /* smlald, smlsld */
+ tmp64 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp64, tmp);
+ dead_tmp(tmp);
+ gen_addq(s, tmp64, rd, rn);
+ gen_storeq_reg(s, rd, rn, tmp64);
+ tcg_temp_free_i64(tmp64);
+ } else {
+ /* smuad, smusd, smlad, smlsd */
+ if (rd != 15)
+ {
+ tmp2 = load_reg(s, rd);
+ gen_helper_add_setq(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ store_reg(s, rn, tmp);
+ }
+ }
+ break;
+ case 3:
+ op1 = ((insn >> 17) & 0x38) | ((insn >> 5) & 7);
+ switch (op1) {
+ case 0: /* Unsigned sum of absolute differences. */
+ ARCH(6);
+ tmp = load_reg(s, rm);
+ tmp2 = load_reg(s, rs);
+ gen_helper_usad8(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ if (rd != 15) {
+ tmp2 = load_reg(s, rd);
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ store_reg(s, rn, tmp);
+ break;
+ case 0x20: case 0x24: case 0x28: case 0x2c:
+ /* Bitfield insert/clear. */
+ ARCH(6T2);
+ shift = (insn >> 7) & 0x1f;
+ i = (insn >> 16) & 0x1f;
+ i = i + 1 - shift;
+ if (rm == 15) {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ } else {
+ tmp = load_reg(s, rm);
+ }
+ if (i != 32) {
+ tmp2 = load_reg(s, rd);
+ gen_bfi(tmp, tmp2, tmp, shift, (1u << i) - 1);
+ dead_tmp(tmp2);
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 0x12: case 0x16: case 0x1a: case 0x1e: /* sbfx */
+ case 0x32: case 0x36: case 0x3a: case 0x3e: /* ubfx */
+ ARCH(6T2);
+ tmp = load_reg(s, rm);
+ shift = (insn >> 7) & 0x1f;
+ i = ((insn >> 16) & 0x1f) + 1;
+ if (shift + i > 32)
+ goto illegal_op;
+ if (i < 32) {
+ if (op1 & 0x20) {
+ gen_ubfx(tmp, shift, (1u << i) - 1);
+ } else {
+ gen_sbfx(tmp, shift, i);
+ }
+ }
+ store_reg(s, rd, tmp);
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ }
+ break;
+ }
+ do_ldst:
+ /* Check for undefined extension instructions
+ * per the ARM Bible IE:
+ * xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
+ */
+ sh = (0xf << 20) | (0xf << 4);
+ if (op1 == 0x7 && ((insn & sh) == sh))
+ {
+ goto illegal_op;
+ }
+ /* load/store byte/word */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ tmp2 = load_reg(s, rn);
+ i = (IS_USER(s) || (insn & 0x01200000) == 0x00200000);
+ if (insn & (1 << 24))
+ gen_add_data_offset(s, insn, tmp2);
+ if (insn & (1 << 20)) {
+ /* load */
+ if (insn & (1 << 22)) {
+ tmp = gen_ld8u(tmp2, i);
+ } else {
+ tmp = gen_ld32(tmp2, i);
+ }
+ } else {
+ /* store */
+ tmp = load_reg(s, rd);
+ if (insn & (1 << 22))
+ gen_st8(tmp, tmp2, i);
+ else
+ gen_st32(tmp, tmp2, i);
+ }
+ if (!(insn & (1 << 24))) {
+ gen_add_data_offset(s, insn, tmp2);
+ store_reg(s, rn, tmp2);
+ } else if (insn & (1 << 21)) {
+ store_reg(s, rn, tmp2);
+ } else {
+ dead_tmp(tmp2);
+ }
+ if (insn & (1 << 20)) {
+ /* Complete the load. */
+ if (rd == 15)
+ gen_bx(s, tmp);
+ else
+ store_reg(s, rd, tmp);
+ }
+ break;
+ case 0x08:
+ case 0x09:
+ {
+ int j, n, user, loaded_base;
+ TCGv loaded_var;
+ /* load/store multiple words */
+ /* XXX: store correct base if write back */
+ user = 0;
+ if (insn & (1 << 22)) {
+ if (IS_USER(s))
+ goto illegal_op; /* only usable in supervisor mode */
+
+ if ((insn & (1 << 15)) == 0)
+ user = 1;
+ }
+ rn = (insn >> 16) & 0xf;
+ addr = load_reg(s, rn);
+
+ /* compute total size */
+ loaded_base = 0;
+ TCGV_UNUSED(loaded_var);
+ n = 0;
+ for(i=0;i<16;i++) {
+ if (insn & (1 << i))
+ n++;
+ }
+ /* XXX: test invalid n == 0 case ? */
+ if (insn & (1 << 23)) {
+ if (insn & (1 << 24)) {
+ /* pre increment */
+ tcg_gen_addi_i32(addr, addr, 4);
+ } else {
+ /* post increment */
+ }
+ } else {
+ if (insn & (1 << 24)) {
+ /* pre decrement */
+ tcg_gen_addi_i32(addr, addr, -(n * 4));
+ } else {
+ /* post decrement */
+ if (n != 1)
+ tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
+ }
+ }
+ j = 0;
+ for(i=0;i<16;i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 20)) {
+ /* load */
+ tmp = gen_ld32(addr, IS_USER(s));
+ if (i == 15) {
+ gen_bx(s, tmp);
+ } else if (user) {
+ tmp2 = tcg_const_i32(i);
+ gen_helper_set_user_reg(tmp2, tmp);
+ tcg_temp_free_i32(tmp2);
+ dead_tmp(tmp);
+ } else if (i == rn) {
+ loaded_var = tmp;
+ loaded_base = 1;
+ } else {
+ store_reg(s, i, tmp);
+ }
+ } else {
+ /* store */
+ if (i == 15) {
+ /* special case: r15 = PC + 8 */
+ val = (long)s->pc + 4;
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, val);
+ } else if (user) {
+ tmp = new_tmp();
+ tmp2 = tcg_const_i32(i);
+ gen_helper_get_user_reg(tmp, tmp2);
+ tcg_temp_free_i32(tmp2);
+ } else {
+ tmp = load_reg(s, i);
+ }
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ j++;
+ /* no need to add after the last transfer */
+ if (j != n)
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ }
+ if (insn & (1 << 21)) {
+ /* write back */
+ if (insn & (1 << 23)) {
+ if (insn & (1 << 24)) {
+ /* pre increment */
+ } else {
+ /* post increment */
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ } else {
+ if (insn & (1 << 24)) {
+ /* pre decrement */
+ if (n != 1)
+ tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
+ } else {
+ /* post decrement */
+ tcg_gen_addi_i32(addr, addr, -(n * 4));
+ }
+ }
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ if (loaded_base) {
+ store_reg(s, rn, loaded_var);
+ }
+ if ((insn & (1 << 22)) && !user) {
+ /* Restore CPSR from SPSR. */
+ tmp = load_cpu_field(spsr);
+ gen_set_cpsr(tmp, 0xffffffff);
+ dead_tmp(tmp);
+ s->is_jmp = DISAS_UPDATE;
+ }
+ }
+ break;
+ case 0xa:
+ case 0xb:
+ {
+ int32_t offset;
+
+ /* branch (and link) */
+ val = (int32_t)s->pc;
+ if (insn & (1 << 24)) {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, val);
+ store_reg(s, 14, tmp);
+ }
+ offset = (((int32_t)insn << 8) >> 8);
+ val += (offset << 2) + 4;
+ gen_jmp(s, val);
+ }
+ break;
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ /* Coprocessor. */
+ if (disas_coproc_insn(env, s, insn))
+ goto illegal_op;
+ break;
+ case 0xf:
+ /* swi */
+ gen_set_pc_im(s->pc);
+ s->is_jmp = DISAS_SWI;
+ break;
+ default:
+ illegal_op:
+ gen_exception_insn(s, 4, EXCP_UDEF);
+ break;
+ }
+ }
+}
+
+/* Return true if this is a Thumb-2 logical op. */
+static int
+thumb2_logic_op(int op)
+{
+ return (op < 8);
+}
+
+/* Generate code for a Thumb-2 data processing operation. If CONDS is nonzero
+ then set condition code flags based on the result of the operation.
+ If SHIFTER_OUT is nonzero then set the carry flag for logical operations
+ to the high bit of T1.
+ Returns zero if the opcode is valid. */
+
+static int
+gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out, TCGv t0, TCGv t1)
+{
+ int logic_cc;
+
+ logic_cc = 0;
+ switch (op) {
+ case 0: /* and */
+ tcg_gen_and_i32(t0, t0, t1);
+ logic_cc = conds;
+ break;
+ case 1: /* bic */
+ tcg_gen_andc_i32(t0, t0, t1);
+ logic_cc = conds;
+ break;
+ case 2: /* orr */
+ tcg_gen_or_i32(t0, t0, t1);
+ logic_cc = conds;
+ break;
+ case 3: /* orn */
+ tcg_gen_not_i32(t1, t1);
+ tcg_gen_or_i32(t0, t0, t1);
+ logic_cc = conds;
+ break;
+ case 4: /* eor */
+ tcg_gen_xor_i32(t0, t0, t1);
+ logic_cc = conds;
+ break;
+ case 8: /* add */
+ if (conds)
+ gen_helper_add_cc(t0, t0, t1);
+ else
+ tcg_gen_add_i32(t0, t0, t1);
+ break;
+ case 10: /* adc */
+ if (conds)
+ gen_helper_adc_cc(t0, t0, t1);
+ else
+ gen_adc(t0, t1);
+ break;
+ case 11: /* sbc */
+ if (conds)
+ gen_helper_sbc_cc(t0, t0, t1);
+ else
+ gen_sub_carry(t0, t0, t1);
+ break;
+ case 13: /* sub */
+ if (conds)
+ gen_helper_sub_cc(t0, t0, t1);
+ else
+ tcg_gen_sub_i32(t0, t0, t1);
+ break;
+ case 14: /* rsb */
+ if (conds)
+ gen_helper_sub_cc(t0, t1, t0);
+ else
+ tcg_gen_sub_i32(t0, t1, t0);
+ break;
+ default: /* 5, 6, 7, 9, 12, 15. */
+ return 1;
+ }
+ if (logic_cc) {
+ gen_logic_CC(t0);
+ if (shifter_out)
+ gen_set_CF_bit31(t1);
+ }
+ return 0;
+}
+
+/* Translate a 32-bit thumb instruction. Returns nonzero if the instruction
+ is not legal. */
+static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
+{
+ uint32_t insn, imm, shift, offset;
+ uint32_t rd, rn, rm, rs;
+ TCGv tmp;
+ TCGv tmp2;
+ TCGv tmp3;
+ TCGv addr;
+ TCGv_i64 tmp64;
+ int op;
+ int shiftop;
+ int conds;
+ int logic_cc;
+
+ if (!(arm_feature(env, ARM_FEATURE_THUMB2)
+ || arm_feature (env, ARM_FEATURE_M))) {
+ /* Thumb-1 cores may need to treat bl and blx as a pair of
+ 16-bit instructions to get correct prefetch abort behavior. */
+ insn = insn_hw1;
+ if ((insn & (1 << 12)) == 0) {
+ /* Second half of blx. */
+ offset = ((insn & 0x7ff) << 1);
+ tmp = load_reg(s, 14);
+ tcg_gen_addi_i32(tmp, tmp, offset);
+ tcg_gen_andi_i32(tmp, tmp, 0xfffffffc);
+
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, s->pc | 1);
+ store_reg(s, 14, tmp2);
+ gen_bx(s, tmp);
+ return 0;
+ }
+ if (insn & (1 << 11)) {
+ /* Second half of bl. */
+ offset = ((insn & 0x7ff) << 1) | 1;
+ tmp = load_reg(s, 14);
+ tcg_gen_addi_i32(tmp, tmp, offset);
+
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, s->pc | 1);
+ store_reg(s, 14, tmp2);
+ gen_bx(s, tmp);
+ return 0;
+ }
+ if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+ /* Instruction spans a page boundary. Implement it as two
+ 16-bit instructions in case the second half causes an
+ prefetch abort. */
+ offset = ((int32_t)insn << 21) >> 9;
+ tcg_gen_movi_i32(cpu_R[14], s->pc + 2 + offset);
+ return 0;
+ }
+ /* Fall through to 32-bit decode. */
+ }
+
+ insn = lduw_code(s->pc);
+ s->pc += 2;
+ insn |= (uint32_t)insn_hw1 << 16;
+
+ if ((insn & 0xf800e800) != 0xf000e800) {
+ ARCH(6T2);
+ }
+
+ rn = (insn >> 16) & 0xf;
+ rs = (insn >> 12) & 0xf;
+ rd = (insn >> 8) & 0xf;
+ rm = insn & 0xf;
+ switch ((insn >> 25) & 0xf) {
+ case 0: case 1: case 2: case 3:
+ /* 16-bit instructions. Should never happen. */
+ abort();
+ case 4:
+ if (insn & (1 << 22)) {
+ /* Other load/store, table branch. */
+ if (insn & 0x01200000) {
+ /* Load/store doubleword. */
+ if (rn == 15) {
+ addr = new_tmp();
+ tcg_gen_movi_i32(addr, s->pc & ~3);
+ } else {
+ addr = load_reg(s, rn);
+ }
+ offset = (insn & 0xff) * 4;
+ if ((insn & (1 << 23)) == 0)
+ offset = -offset;
+ if (insn & (1 << 24)) {
+ tcg_gen_addi_i32(addr, addr, offset);
+ offset = 0;
+ }
+ if (insn & (1 << 20)) {
+ /* ldrd */
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, rs, tmp);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, rd, tmp);
+ } else {
+ /* strd */
+ tmp = load_reg(s, rs);
+ gen_st32(tmp, addr, IS_USER(s));
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = load_reg(s, rd);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ if (insn & (1 << 21)) {
+ /* Base writeback. */
+ if (rn == 15)
+ goto illegal_op;
+ tcg_gen_addi_i32(addr, addr, offset - 4);
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ } else if ((insn & (1 << 23)) == 0) {
+ /* Load/store exclusive word. */
+ addr = tcg_temp_local_new();
+ load_reg_var(s, addr, rn);
+ tcg_gen_addi_i32(addr, addr, (insn & 0xff) << 2);
+ if (insn & (1 << 20)) {
+ gen_load_exclusive(s, rs, 15, addr, 2);
+ } else {
+ gen_store_exclusive(s, rd, rs, 15, addr, 2);
+ }
+ tcg_temp_free(addr);
+ } else if ((insn & (1 << 6)) == 0) {
+ /* Table Branch. */
+ if (rn == 15) {
+ addr = new_tmp();
+ tcg_gen_movi_i32(addr, s->pc);
+ } else {
+ addr = load_reg(s, rn);
+ }
+ tmp = load_reg(s, rm);
+ tcg_gen_add_i32(addr, addr, tmp);
+ if (insn & (1 << 4)) {
+ /* tbh */
+ tcg_gen_add_i32(addr, addr, tmp);
+ dead_tmp(tmp);
+ tmp = gen_ld16u(addr, IS_USER(s));
+ } else { /* tbb */
+ dead_tmp(tmp);
+ tmp = gen_ld8u(addr, IS_USER(s));
+ }
+ dead_tmp(addr);
+ tcg_gen_shli_i32(tmp, tmp, 1);
+ tcg_gen_addi_i32(tmp, tmp, s->pc);
+ store_reg(s, 15, tmp);
+ } else {
+ /* Load/store exclusive byte/halfword/doubleword. */
+ ARCH(7);
+ op = (insn >> 4) & 0x3;
+ if (op == 2) {
+ goto illegal_op;
+ }
+ addr = tcg_temp_local_new();
+ load_reg_var(s, addr, rn);
+ if (insn & (1 << 20)) {
+ gen_load_exclusive(s, rs, rd, addr, op);
+ } else {
+ gen_store_exclusive(s, rm, rs, rd, addr, op);
+ }
+ tcg_temp_free(addr);
+ }
+ } else {
+ /* Load/store multiple, RFE, SRS. */
+ if (((insn >> 23) & 1) == ((insn >> 24) & 1)) {
+ /* Not available in user mode. */
+ if (IS_USER(s))
+ goto illegal_op;
+ if (insn & (1 << 20)) {
+ /* rfe */
+ addr = load_reg(s, rn);
+ if ((insn & (1 << 24)) == 0)
+ tcg_gen_addi_i32(addr, addr, -8);
+ /* Load PC into tmp and CPSR into tmp2. */
+ tmp = gen_ld32(addr, 0);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp2 = gen_ld32(addr, 0);
+ if (insn & (1 << 21)) {
+ /* Base writeback. */
+ if (insn & (1 << 24)) {
+ tcg_gen_addi_i32(addr, addr, 4);
+ } else {
+ tcg_gen_addi_i32(addr, addr, -4);
+ }
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ gen_rfe(s, tmp, tmp2);
+ } else {
+ /* srs */
+ op = (insn & 0x1f);
+ addr = new_tmp();
+ tmp = tcg_const_i32(op);
+ gen_helper_get_r13_banked(addr, cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+ if ((insn & (1 << 24)) == 0) {
+ tcg_gen_addi_i32(addr, addr, -8);
+ }
+ tmp = load_reg(s, 14);
+ gen_st32(tmp, addr, 0);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tmp = new_tmp();
+ gen_helper_cpsr_read(tmp);
+ gen_st32(tmp, addr, 0);
+ if (insn & (1 << 21)) {
+ if ((insn & (1 << 24)) == 0) {
+ tcg_gen_addi_i32(addr, addr, -4);
+ } else {
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ tmp = tcg_const_i32(op);
+ gen_helper_set_r13_banked(cpu_env, tmp, addr);
+ tcg_temp_free_i32(tmp);
+ } else {
+ dead_tmp(addr);
+ }
+ }
+ } else {
+ int i;
+ /* Load/store multiple. */
+ addr = load_reg(s, rn);
+ offset = 0;
+ for (i = 0; i < 16; i++) {
+ if (insn & (1 << i))
+ offset += 4;
+ }
+ if (insn & (1 << 24)) {
+ tcg_gen_addi_i32(addr, addr, -offset);
+ }
+
+ for (i = 0; i < 16; i++) {
+ if ((insn & (1 << i)) == 0)
+ continue;
+ if (insn & (1 << 20)) {
+ /* Load. */
+ tmp = gen_ld32(addr, IS_USER(s));
+ if (i == 15) {
+ gen_bx(s, tmp);
+ } else {
+ store_reg(s, i, tmp);
+ }
+ } else {
+ /* Store. */
+ tmp = load_reg(s, i);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ if (insn & (1 << 21)) {
+ /* Base register writeback. */
+ if (insn & (1 << 24)) {
+ tcg_gen_addi_i32(addr, addr, -offset);
+ }
+ /* Fault if writeback register is in register list. */
+ if (insn & (1 << rn))
+ goto illegal_op;
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ }
+ }
+ break;
+ case 5:
+
+ op = (insn >> 21) & 0xf;
+ if (op == 6) {
+ /* Halfword pack. */
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ shift = ((insn >> 10) & 0x1c) | ((insn >> 6) & 0x3);
+ if (insn & (1 << 5)) {
+ /* pkhtb */
+ if (shift == 0)
+ shift = 31;
+ tcg_gen_sari_i32(tmp2, tmp2, shift);
+ tcg_gen_andi_i32(tmp, tmp, 0xffff0000);
+ tcg_gen_ext16u_i32(tmp2, tmp2);
+ } else {
+ /* pkhbt */
+ if (shift)
+ tcg_gen_shli_i32(tmp2, tmp2, shift);
+ tcg_gen_ext16u_i32(tmp, tmp);
+ tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000);
+ }
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ } else {
+ /* Data processing register constant shift. */
+ if (rn == 15) {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ } else {
+ tmp = load_reg(s, rn);
+ }
+ tmp2 = load_reg(s, rm);
+
+ shiftop = (insn >> 4) & 3;
+ shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c);
+ conds = (insn & (1 << 20)) != 0;
+ logic_cc = (conds && thumb2_logic_op(op));
+ gen_arm_shift_im(tmp2, shiftop, shift, logic_cc);
+ if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2))
+ goto illegal_op;
+ dead_tmp(tmp2);
+ if (rd != 15) {
+ store_reg(s, rd, tmp);
+ } else {
+ dead_tmp(tmp);
+ }
+ }
+ break;
+ case 13: /* Misc data processing. */
+ op = ((insn >> 22) & 6) | ((insn >> 7) & 1);
+ if (op < 4 && (insn & 0xf000) != 0xf000)
+ goto illegal_op;
+ switch (op) {
+ case 0: /* Register controlled shift. */
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ if ((insn & 0x70) != 0)
+ goto illegal_op;
+ op = (insn >> 21) & 3;
+ logic_cc = (insn & (1 << 20)) != 0;
+ gen_arm_shift_reg(tmp, op, tmp2, logic_cc);
+ if (logic_cc)
+ gen_logic_CC(tmp);
+ store_reg_bx(env, s, rd, tmp);
+ break;
+ case 1: /* Sign/zero extend. */
+ tmp = load_reg(s, rm);
+ shift = (insn >> 4) & 3;
+ /* ??? In many cases it's not neccessary to do a
+ rotate, a shift is sufficient. */
+ if (shift != 0)
+ tcg_gen_rotri_i32(tmp, tmp, shift * 8);
+ op = (insn >> 20) & 7;
+ switch (op) {
+ case 0: gen_sxth(tmp); break;
+ case 1: gen_uxth(tmp); break;
+ case 2: gen_sxtb16(tmp); break;
+ case 3: gen_uxtb16(tmp); break;
+ case 4: gen_sxtb(tmp); break;
+ case 5: gen_uxtb(tmp); break;
+ default: goto illegal_op;
+ }
+ if (rn != 15) {
+ tmp2 = load_reg(s, rn);
+ if ((op >> 1) == 1) {
+ gen_add16(tmp, tmp2);
+ } else {
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 2: /* SIMD add/subtract. */
+ op = (insn >> 20) & 7;
+ shift = (insn >> 4) & 7;
+ if ((op & 3) == 3 || (shift & 3) == 3)
+ goto illegal_op;
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ gen_thumb2_parallel_addsub(op, shift, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ break;
+ case 3: /* Other data processing. */
+ op = ((insn >> 17) & 0x38) | ((insn >> 4) & 7);
+ if (op < 4) {
+ /* Saturating add/subtract. */
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ if (op & 1)
+ gen_helper_double_saturate(tmp, tmp);
+ if (op & 2)
+ gen_helper_sub_saturate(tmp, tmp2, tmp);
+ else
+ gen_helper_add_saturate(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ } else {
+ tmp = load_reg(s, rn);
+ switch (op) {
+ case 0x0a: /* rbit */
+ gen_helper_rbit(tmp, tmp);
+ break;
+ case 0x08: /* rev */
+ tcg_gen_bswap32_i32(tmp, tmp);
+ break;
+ case 0x09: /* rev16 */
+ gen_rev16(tmp);
+ break;
+ case 0x0b: /* revsh */
+ gen_revsh(tmp);
+ break;
+ case 0x10: /* sel */
+ tmp2 = load_reg(s, rm);
+ tmp3 = new_tmp();
+ tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE));
+ gen_helper_sel_flags(tmp, tmp3, tmp, tmp2);
+ dead_tmp(tmp3);
+ dead_tmp(tmp2);
+ break;
+ case 0x18: /* clz */
+ gen_helper_clz(tmp, tmp);
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 4: case 5: /* 32-bit multiply. Sum of absolute differences. */
+ op = (insn >> 4) & 0xf;
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ switch ((insn >> 20) & 7) {
+ case 0: /* 32 x 32 -> 32 */
+ tcg_gen_mul_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ if (rs != 15) {
+ tmp2 = load_reg(s, rs);
+ if (op)
+ tcg_gen_sub_i32(tmp, tmp2, tmp);
+ else
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ break;
+ case 1: /* 16 x 16 -> 32 */
+ gen_mulxy(tmp, tmp2, op & 2, op & 1);
+ dead_tmp(tmp2);
+ if (rs != 15) {
+ tmp2 = load_reg(s, rs);
+ gen_helper_add_setq(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ break;
+ case 2: /* Dual multiply add. */
+ case 4: /* Dual multiply subtract. */
+ if (op)
+ gen_swap_half(tmp2);
+ gen_smul_dual(tmp, tmp2);
+ /* This addition cannot overflow. */
+ if (insn & (1 << 22)) {
+ tcg_gen_sub_i32(tmp, tmp, tmp2);
+ } else {
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ }
+ dead_tmp(tmp2);
+ if (rs != 15)
+ {
+ tmp2 = load_reg(s, rs);
+ gen_helper_add_setq(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ break;
+ case 3: /* 32 * 16 -> 32msb */
+ if (op)
+ tcg_gen_sari_i32(tmp2, tmp2, 16);
+ else
+ gen_sxth(tmp2);
+ tmp64 = gen_muls_i64_i32(tmp, tmp2);
+ tcg_gen_shri_i64(tmp64, tmp64, 16);
+ tmp = new_tmp();
+ tcg_gen_trunc_i64_i32(tmp, tmp64);
+ tcg_temp_free_i64(tmp64);
+ if (rs != 15)
+ {
+ tmp2 = load_reg(s, rs);
+ gen_helper_add_setq(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ break;
+ case 5: case 6: /* 32 * 32 -> 32msb (SMMUL, SMMLA, SMMLS) */
+ tmp64 = gen_muls_i64_i32(tmp, tmp2);
+ if (rs != 15) {
+ tmp = load_reg(s, rs);
+ if (insn & (1 << 20)) {
+ tmp64 = gen_addq_msw(tmp64, tmp);
+ } else {
+ tmp64 = gen_subq_msw(tmp64, tmp);
+ }
+ }
+ if (insn & (1 << 4)) {
+ tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u);
+ }
+ tcg_gen_shri_i64(tmp64, tmp64, 32);
+ tmp = new_tmp();
+ tcg_gen_trunc_i64_i32(tmp, tmp64);
+ tcg_temp_free_i64(tmp64);
+ break;
+ case 7: /* Unsigned sum of absolute differences. */
+ gen_helper_usad8(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ if (rs != 15) {
+ tmp2 = load_reg(s, rs);
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ }
+ break;
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 6: case 7: /* 64-bit multiply, Divide. */
+ op = ((insn >> 4) & 0xf) | ((insn >> 16) & 0x70);
+ tmp = load_reg(s, rn);
+ tmp2 = load_reg(s, rm);
+ if ((op & 0x50) == 0x10) {
+ /* sdiv, udiv */
+ if (!arm_feature(env, ARM_FEATURE_DIV))
+ goto illegal_op;
+ if (op & 0x20)
+ gen_helper_udiv(tmp, tmp, tmp2);
+ else
+ gen_helper_sdiv(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ } else if ((op & 0xe) == 0xc) {
+ /* Dual multiply accumulate long. */
+ if (op & 1)
+ gen_swap_half(tmp2);
+ gen_smul_dual(tmp, tmp2);
+ if (op & 0x10) {
+ tcg_gen_sub_i32(tmp, tmp, tmp2);
+ } else {
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ }
+ dead_tmp(tmp2);
+ /* BUGFIX */
+ tmp64 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp64, tmp);
+ dead_tmp(tmp);
+ gen_addq(s, tmp64, rs, rd);
+ gen_storeq_reg(s, rs, rd, tmp64);
+ tcg_temp_free_i64(tmp64);
+ } else {
+ if (op & 0x20) {
+ /* Unsigned 64-bit multiply */
+ tmp64 = gen_mulu_i64_i32(tmp, tmp2);
+ } else {
+ if (op & 8) {
+ /* smlalxy */
+ gen_mulxy(tmp, tmp2, op & 2, op & 1);
+ dead_tmp(tmp2);
+ tmp64 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(tmp64, tmp);
+ dead_tmp(tmp);
+ } else {
+ /* Signed 64-bit multiply */
+ tmp64 = gen_muls_i64_i32(tmp, tmp2);
+ }
+ }
+ if (op & 4) {
+ /* umaal */
+ gen_addq_lo(s, tmp64, rs);
+ gen_addq_lo(s, tmp64, rd);
+ } else if (op & 0x40) {
+ /* 64-bit accumulate. */
+ gen_addq(s, tmp64, rs, rd);
+ }
+ gen_storeq_reg(s, rs, rd, tmp64);
+ tcg_temp_free_i64(tmp64);
+ }
+ break;
+ }
+ break;
+ case 6: case 7: case 14: case 15:
+ /* Coprocessor. */
+ if (((insn >> 24) & 3) == 3) {
+ /* Translate into the equivalent ARM encoding. */
+ insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4);
+ if (disas_neon_data_insn(env, s, insn))
+ goto illegal_op;
+ } else {
+ if (insn & (1 << 28))
+ goto illegal_op;
+ if (disas_coproc_insn (env, s, insn))
+ goto illegal_op;
+ }
+ break;
+ case 8: case 9: case 10: case 11:
+ if (insn & (1 << 15)) {
+ /* Branches, misc control. */
+ if (insn & 0x5000) {
+ /* Unconditional branch. */
+ /* signextend(hw1[10:0]) -> offset[:12]. */
+ offset = ((int32_t)insn << 5) >> 9 & ~(int32_t)0xfff;
+ /* hw1[10:0] -> offset[11:1]. */
+ offset |= (insn & 0x7ff) << 1;
+ /* (~hw2[13, 11] ^ offset[24]) -> offset[23,22]
+ offset[24:22] already have the same value because of the
+ sign extension above. */
+ offset ^= ((~insn) & (1 << 13)) << 10;
+ offset ^= ((~insn) & (1 << 11)) << 11;
+
+ if (insn & (1 << 14)) {
+ /* Branch and link. */
+ tcg_gen_movi_i32(cpu_R[14], s->pc | 1);
+ }
+
+ offset += s->pc;
+ if (insn & (1 << 12)) {
+ /* b/bl */
+ gen_jmp(s, offset);
+ } else {
+ /* blx */
+ offset &= ~(uint32_t)2;
+ gen_bx_im(s, offset);
+ }
+ } else if (((insn >> 23) & 7) == 7) {
+ /* Misc control */
+ if (insn & (1 << 13))
+ goto illegal_op;
+
+ if (insn & (1 << 26)) {
+ /* Secure monitor call (v6Z) */
+ goto illegal_op; /* not implemented. */
+ } else {
+ op = (insn >> 20) & 7;
+ switch (op) {
+ case 0: /* msr cpsr. */
+ if (IS_M(env)) {
+ tmp = load_reg(s, rn);
+ addr = tcg_const_i32(insn & 0xff);
+ gen_helper_v7m_msr(cpu_env, addr, tmp);
+ tcg_temp_free_i32(addr);
+ dead_tmp(tmp);
+ gen_lookup_tb(s);
+ break;
+ }
+ /* fall through */
+ case 1: /* msr spsr. */
+ if (IS_M(env))
+ goto illegal_op;
+ tmp = load_reg(s, rn);
+ if (gen_set_psr(s,
+ msr_mask(env, s, (insn >> 8) & 0xf, op == 1),
+ op == 1, tmp))
+ goto illegal_op;
+ break;
+ case 2: /* cps, nop-hint. */
+ if (((insn >> 8) & 7) == 0) {
+ gen_nop_hint(s, insn & 0xff);
+ }
+ /* Implemented as NOP in user mode. */
+ if (IS_USER(s))
+ break;
+ offset = 0;
+ imm = 0;
+ if (insn & (1 << 10)) {
+ if (insn & (1 << 7))
+ offset |= CPSR_A;
+ if (insn & (1 << 6))
+ offset |= CPSR_I;
+ if (insn & (1 << 5))
+ offset |= CPSR_F;
+ if (insn & (1 << 9))
+ imm = CPSR_A | CPSR_I | CPSR_F;
+ }
+ if (insn & (1 << 8)) {
+ offset |= 0x1f;
+ imm |= (insn & 0x1f);
+ }
+ if (offset) {
+ gen_set_psr_im(s, offset, 0, imm);
+ }
+ break;
+ case 3: /* Special control operations. */
+ ARCH(7);
+ op = (insn >> 4) & 0xf;
+ switch (op) {
+ case 2: /* clrex */
+ gen_clrex(s);
+ break;
+ case 4: /* dsb */
+ case 5: /* dmb */
+ case 6: /* isb */
+ /* These execute as NOPs. */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 4: /* bxj */
+ /* Trivial implementation equivalent to bx. */
+ tmp = load_reg(s, rn);
+ gen_bx(s, tmp);
+ break;
+ case 5: /* Exception return. */
+ if (IS_USER(s)) {
+ goto illegal_op;
+ }
+ if (rn != 14 || rd != 15) {
+ goto illegal_op;
+ }
+ tmp = load_reg(s, rn);
+ tcg_gen_subi_i32(tmp, tmp, insn & 0xff);
+ gen_exception_return(s, tmp);
+ break;
+ case 6: /* mrs cpsr. */
+ tmp = new_tmp();
+ if (IS_M(env)) {
+ addr = tcg_const_i32(insn & 0xff);
+ gen_helper_v7m_mrs(tmp, cpu_env, addr);
+ tcg_temp_free_i32(addr);
+ } else {
+ gen_helper_cpsr_read(tmp);
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 7: /* mrs spsr. */
+ /* Not accessible in user mode. */
+ if (IS_USER(s) || IS_M(env))
+ goto illegal_op;
+ tmp = load_cpu_field(spsr);
+ store_reg(s, rd, tmp);
+ break;
+ }
+ }
+ } else {
+ /* Conditional branch. */
+ op = (insn >> 22) & 0xf;
+ /* Generate a conditional jump to next instruction. */
+ s->condlabel = gen_new_label();
+ gen_test_cc(op ^ 1, s->condlabel);
+ s->condjmp = 1;
+
+ /* offset[11:1] = insn[10:0] */
+ offset = (insn & 0x7ff) << 1;
+ /* offset[17:12] = insn[21:16]. */
+ offset |= (insn & 0x003f0000) >> 4;
+ /* offset[31:20] = insn[26]. */
+ offset |= ((int32_t)((insn << 5) & 0x80000000)) >> 11;
+ /* offset[18] = insn[13]. */
+ offset |= (insn & (1 << 13)) << 5;
+ /* offset[19] = insn[11]. */
+ offset |= (insn & (1 << 11)) << 8;
+
+ /* jump to the offset */
+ gen_jmp(s, s->pc + offset);
+ }
+ } else {
+ /* Data processing immediate. */
+ if (insn & (1 << 25)) {
+ if (insn & (1 << 24)) {
+ if (insn & (1 << 20))
+ goto illegal_op;
+ /* Bitfield/Saturate. */
+ op = (insn >> 21) & 7;
+ imm = insn & 0x1f;
+ shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c);
+ if (rn == 15) {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ } else {
+ tmp = load_reg(s, rn);
+ }
+ switch (op) {
+ case 2: /* Signed bitfield extract. */
+ imm++;
+ if (shift + imm > 32)
+ goto illegal_op;
+ if (imm < 32)
+ gen_sbfx(tmp, shift, imm);
+ break;
+ case 6: /* Unsigned bitfield extract. */
+ imm++;
+ if (shift + imm > 32)
+ goto illegal_op;
+ if (imm < 32)
+ gen_ubfx(tmp, shift, (1u << imm) - 1);
+ break;
+ case 3: /* Bitfield insert/clear. */
+ if (imm < shift)
+ goto illegal_op;
+ imm = imm + 1 - shift;
+ if (imm != 32) {
+ tmp2 = load_reg(s, rd);
+ gen_bfi(tmp, tmp2, tmp, shift, (1u << imm) - 1);
+ dead_tmp(tmp2);
+ }
+ break;
+ case 7:
+ goto illegal_op;
+ default: /* Saturate. */
+ if (shift) {
+ if (op & 1)
+ tcg_gen_sari_i32(tmp, tmp, shift);
+ else
+ tcg_gen_shli_i32(tmp, tmp, shift);
+ }
+ tmp2 = tcg_const_i32(imm);
+ if (op & 4) {
+ /* Unsigned. */
+ if ((op & 1) && shift == 0)
+ gen_helper_usat16(tmp, tmp, tmp2);
+ else
+ gen_helper_usat(tmp, tmp, tmp2);
+ } else {
+ /* Signed. */
+ if ((op & 1) && shift == 0)
+ gen_helper_ssat16(tmp, tmp, tmp2);
+ else
+ gen_helper_ssat(tmp, tmp, tmp2);
+ }
+ tcg_temp_free_i32(tmp2);
+ break;
+ }
+ store_reg(s, rd, tmp);
+ } else {
+ imm = ((insn & 0x04000000) >> 15)
+ | ((insn & 0x7000) >> 4) | (insn & 0xff);
+ if (insn & (1 << 22)) {
+ /* 16-bit immediate. */
+ imm |= (insn >> 4) & 0xf000;
+ if (insn & (1 << 23)) {
+ /* movt */
+ tmp = load_reg(s, rd);
+ tcg_gen_ext16u_i32(tmp, tmp);
+ tcg_gen_ori_i32(tmp, tmp, imm << 16);
+ } else {
+ /* movw */
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, imm);
+ }
+ } else {
+ /* Add/sub 12-bit immediate. */
+ if (rn == 15) {
+ offset = s->pc & ~(uint32_t)3;
+ if (insn & (1 << 23))
+ offset -= imm;
+ else
+ offset += imm;
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, offset);
+ } else {
+ tmp = load_reg(s, rn);
+ if (insn & (1 << 23))
+ tcg_gen_subi_i32(tmp, tmp, imm);
+ else
+ tcg_gen_addi_i32(tmp, tmp, imm);
+ }
+ }
+ store_reg(s, rd, tmp);
+ }
+ } else {
+ int shifter_out = 0;
+ /* modified 12-bit immediate. */
+ shift = ((insn & 0x04000000) >> 23) | ((insn & 0x7000) >> 12);
+ imm = (insn & 0xff);
+ switch (shift) {
+ case 0: /* XY */
+ /* Nothing to do. */
+ break;
+ case 1: /* 00XY00XY */
+ imm |= imm << 16;
+ break;
+ case 2: /* XY00XY00 */
+ imm |= imm << 16;
+ imm <<= 8;
+ break;
+ case 3: /* XYXYXYXY */
+ imm |= imm << 16;
+ imm |= imm << 8;
+ break;
+ default: /* Rotated constant. */
+ shift = (shift << 1) | (imm >> 7);
+ imm |= 0x80;
+ imm = imm << (32 - shift);
+ shifter_out = 1;
+ break;
+ }
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, imm);
+ rn = (insn >> 16) & 0xf;
+ if (rn == 15) {
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ } else {
+ tmp = load_reg(s, rn);
+ }
+ op = (insn >> 21) & 0xf;
+ if (gen_thumb2_data_op(s, op, (insn & (1 << 20)) != 0,
+ shifter_out, tmp, tmp2))
+ goto illegal_op;
+ dead_tmp(tmp2);
+ rd = (insn >> 8) & 0xf;
+ if (rd != 15) {
+ store_reg(s, rd, tmp);
+ } else {
+ dead_tmp(tmp);
+ }
+ }
+ }
+ break;
+ case 12: /* Load/store single data item. */
+ {
+ int postinc = 0;
+ int writeback = 0;
+ int user;
+ if ((insn & 0x01100000) == 0x01000000) {
+ if (disas_neon_ls_insn(env, s, insn))
+ goto illegal_op;
+ break;
+ }
+ user = IS_USER(s);
+ if (rn == 15) {
+ addr = new_tmp();
+ /* PC relative. */
+ /* s->pc has already been incremented by 4. */
+ imm = s->pc & 0xfffffffc;
+ if (insn & (1 << 23))
+ imm += insn & 0xfff;
+ else
+ imm -= insn & 0xfff;
+ tcg_gen_movi_i32(addr, imm);
+ } else {
+ addr = load_reg(s, rn);
+ if (insn & (1 << 23)) {
+ /* Positive offset. */
+ imm = insn & 0xfff;
+ tcg_gen_addi_i32(addr, addr, imm);
+ } else {
+ op = (insn >> 8) & 7;
+ imm = insn & 0xff;
+ switch (op) {
+ case 0: case 8: /* Shifted Register. */
+ shift = (insn >> 4) & 0xf;
+ if (shift > 3)
+ goto illegal_op;
+ tmp = load_reg(s, rm);
+ if (shift)
+ tcg_gen_shli_i32(tmp, tmp, shift);
+ tcg_gen_add_i32(addr, addr, tmp);
+ dead_tmp(tmp);
+ break;
+ case 4: /* Negative offset. */
+ tcg_gen_addi_i32(addr, addr, -imm);
+ break;
+ case 6: /* User privilege. */
+ tcg_gen_addi_i32(addr, addr, imm);
+ user = 1;
+ break;
+ case 1: /* Post-decrement. */
+ imm = -imm;
+ /* Fall through. */
+ case 3: /* Post-increment. */
+ postinc = 1;
+ writeback = 1;
+ break;
+ case 5: /* Pre-decrement. */
+ imm = -imm;
+ /* Fall through. */
+ case 7: /* Pre-increment. */
+ tcg_gen_addi_i32(addr, addr, imm);
+ writeback = 1;
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ }
+ op = ((insn >> 21) & 3) | ((insn >> 22) & 4);
+ if (insn & (1 << 20)) {
+ /* Load. */
+ if (rs == 15 && op != 2) {
+ if (op & 2)
+ goto illegal_op;
+ /* Memory hint. Implemented as NOP. */
+ } else {
+ switch (op) {
+ case 0: tmp = gen_ld8u(addr, user); break;
+ case 4: tmp = gen_ld8s(addr, user); break;
+ case 1: tmp = gen_ld16u(addr, user); break;
+ case 5: tmp = gen_ld16s(addr, user); break;
+ case 2: tmp = gen_ld32(addr, user); break;
+ default: goto illegal_op;
+ }
+ if (rs == 15) {
+ gen_bx(s, tmp);
+ } else {
+ store_reg(s, rs, tmp);
+ }
+ }
+ } else {
+ /* Store. */
+ if (rs == 15)
+ goto illegal_op;
+ tmp = load_reg(s, rs);
+ switch (op) {
+ case 0: gen_st8(tmp, addr, user); break;
+ case 1: gen_st16(tmp, addr, user); break;
+ case 2: gen_st32(tmp, addr, user); break;
+ default: goto illegal_op;
+ }
+ }
+ if (postinc)
+ tcg_gen_addi_i32(addr, addr, imm);
+ if (writeback) {
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ return 0;
+illegal_op:
+ return 1;
+}
+
+static void disas_thumb_insn(CPUState *env, DisasContext *s)
+{
+ uint32_t val, insn, op, rm, rn, rd, shift, cond;
+ int32_t offset;
+ int i;
+ TCGv tmp;
+ TCGv tmp2;
+ TCGv addr;
+
+ if (s->condexec_mask) {
+ cond = s->condexec_cond;
+ if (cond != 0x0e) { /* Skip conditional when condition is AL. */
+ s->condlabel = gen_new_label();
+ gen_test_cc(cond ^ 1, s->condlabel);
+ s->condjmp = 1;
+ }
+ }
+
+ insn = lduw_code(s->pc);
+ s->pc += 2;
+
+ switch (insn >> 12) {
+ case 0: case 1:
+
+ rd = insn & 7;
+ op = (insn >> 11) & 3;
+ if (op == 3) {
+ /* add/subtract */
+ rn = (insn >> 3) & 7;
+ tmp = load_reg(s, rn);
+ if (insn & (1 << 10)) {
+ /* immediate */
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, (insn >> 6) & 7);
+ } else {
+ /* reg */
+ rm = (insn >> 6) & 7;
+ tmp2 = load_reg(s, rm);
+ }
+ if (insn & (1 << 9)) {
+ if (s->condexec_mask)
+ tcg_gen_sub_i32(tmp, tmp, tmp2);
+ else
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ } else {
+ if (s->condexec_mask)
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ else
+ gen_helper_add_cc(tmp, tmp, tmp2);
+ }
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ } else {
+ /* shift immediate */
+ rm = (insn >> 3) & 7;
+ shift = (insn >> 6) & 0x1f;
+ tmp = load_reg(s, rm);
+ gen_arm_shift_im(tmp, op, shift, s->condexec_mask == 0);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ store_reg(s, rd, tmp);
+ }
+ break;
+ case 2: case 3:
+ /* arithmetic large immediate */
+ op = (insn >> 11) & 3;
+ rd = (insn >> 8) & 0x7;
+ if (op == 0) { /* mov */
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, insn & 0xff);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ store_reg(s, rd, tmp);
+ } else {
+ tmp = load_reg(s, rd);
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, insn & 0xff);
+ switch (op) {
+ case 1: /* cmp */
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ dead_tmp(tmp);
+ dead_tmp(tmp2);
+ break;
+ case 2: /* add */
+ if (s->condexec_mask)
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ else
+ gen_helper_add_cc(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ break;
+ case 3: /* sub */
+ if (s->condexec_mask)
+ tcg_gen_sub_i32(tmp, tmp, tmp2);
+ else
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ break;
+ }
+ }
+ break;
+ case 4:
+ if (insn & (1 << 11)) {
+ rd = (insn >> 8) & 7;
+ /* load pc-relative. Bit 1 of PC is ignored. */
+ val = s->pc + 2 + ((insn & 0xff) * 4);
+ val &= ~(uint32_t)2;
+ addr = new_tmp();
+ tcg_gen_movi_i32(addr, val);
+ tmp = gen_ld32(addr, IS_USER(s));
+ dead_tmp(addr);
+ store_reg(s, rd, tmp);
+ break;
+ }
+ if (insn & (1 << 10)) {
+ /* data processing extended or blx */
+ rd = (insn & 7) | ((insn >> 4) & 8);
+ rm = (insn >> 3) & 0xf;
+ op = (insn >> 8) & 3;
+ switch (op) {
+ case 0: /* add */
+ tmp = load_reg(s, rd);
+ tmp2 = load_reg(s, rm);
+ tcg_gen_add_i32(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ store_reg(s, rd, tmp);
+ break;
+ case 1: /* cmp */
+ tmp = load_reg(s, rd);
+ tmp2 = load_reg(s, rm);
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ dead_tmp(tmp2);
+ dead_tmp(tmp);
+ break;
+ case 2: /* mov/cpy */
+ tmp = load_reg(s, rm);
+ store_reg(s, rd, tmp);
+ break;
+ case 3:/* branch [and link] exchange thumb register */
+ tmp = load_reg(s, rm);
+ if (insn & (1 << 7)) {
+ val = (uint32_t)s->pc | 1;
+ tmp2 = new_tmp();
+ tcg_gen_movi_i32(tmp2, val);
+ store_reg(s, 14, tmp2);
+ }
+ gen_bx(s, tmp);
+ break;
+ }
+ break;
+ }
+
+ /* data processing register */
+ rd = insn & 7;
+ rm = (insn >> 3) & 7;
+ op = (insn >> 6) & 0xf;
+ if (op == 2 || op == 3 || op == 4 || op == 7) {
+ /* the shift/rotate ops want the operands backwards */
+ val = rm;
+ rm = rd;
+ rd = val;
+ val = 1;
+ } else {
+ val = 0;
+ }
+
+ if (op == 9) { /* neg */
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ } else if (op != 0xf) { /* mvn doesn't read its first operand */
+ tmp = load_reg(s, rd);
+ } else {
+ TCGV_UNUSED(tmp);
+ }
+
+ tmp2 = load_reg(s, rm);
+ switch (op) {
+ case 0x0: /* and */
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ break;
+ case 0x1: /* eor */
+ tcg_gen_xor_i32(tmp, tmp, tmp2);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ break;
+ case 0x2: /* lsl */
+ if (s->condexec_mask) {
+ gen_helper_shl(tmp2, tmp2, tmp);
+ } else {
+ gen_helper_shl_cc(tmp2, tmp2, tmp);
+ gen_logic_CC(tmp2);
+ }
+ break;
+ case 0x3: /* lsr */
+ if (s->condexec_mask) {
+ gen_helper_shr(tmp2, tmp2, tmp);
+ } else {
+ gen_helper_shr_cc(tmp2, tmp2, tmp);
+ gen_logic_CC(tmp2);
+ }
+ break;
+ case 0x4: /* asr */
+ if (s->condexec_mask) {
+ gen_helper_sar(tmp2, tmp2, tmp);
+ } else {
+ gen_helper_sar_cc(tmp2, tmp2, tmp);
+ gen_logic_CC(tmp2);
+ }
+ break;
+ case 0x5: /* adc */
+ if (s->condexec_mask)
+ gen_adc(tmp, tmp2);
+ else
+ gen_helper_adc_cc(tmp, tmp, tmp2);
+ break;
+ case 0x6: /* sbc */
+ if (s->condexec_mask)
+ gen_sub_carry(tmp, tmp, tmp2);
+ else
+ gen_helper_sbc_cc(tmp, tmp, tmp2);
+ break;
+ case 0x7: /* ror */
+ if (s->condexec_mask) {
+ tcg_gen_andi_i32(tmp, tmp, 0x1f);
+ tcg_gen_rotr_i32(tmp2, tmp2, tmp);
+ } else {
+ gen_helper_ror_cc(tmp2, tmp2, tmp);
+ gen_logic_CC(tmp2);
+ }
+ break;
+ case 0x8: /* tst */
+ tcg_gen_and_i32(tmp, tmp, tmp2);
+ gen_logic_CC(tmp);
+ rd = 16;
+ break;
+ case 0x9: /* neg */
+ if (s->condexec_mask)
+ tcg_gen_neg_i32(tmp, tmp2);
+ else
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ break;
+ case 0xa: /* cmp */
+ gen_helper_sub_cc(tmp, tmp, tmp2);
+ rd = 16;
+ break;
+ case 0xb: /* cmn */
+ gen_helper_add_cc(tmp, tmp, tmp2);
+ rd = 16;
+ break;
+ case 0xc: /* orr */
+ tcg_gen_or_i32(tmp, tmp, tmp2);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ break;
+ case 0xd: /* mul */
+ tcg_gen_mul_i32(tmp, tmp, tmp2);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ break;
+ case 0xe: /* bic */
+ tcg_gen_andc_i32(tmp, tmp, tmp2);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp);
+ break;
+ case 0xf: /* mvn */
+ tcg_gen_not_i32(tmp2, tmp2);
+ if (!s->condexec_mask)
+ gen_logic_CC(tmp2);
+ val = 1;
+ rm = rd;
+ break;
+ }
+ if (rd != 16) {
+ if (val) {
+ store_reg(s, rm, tmp2);
+ if (op != 0xf)
+ dead_tmp(tmp);
+ } else {
+ store_reg(s, rd, tmp);
+ dead_tmp(tmp2);
+ }
+ } else {
+ dead_tmp(tmp);
+ dead_tmp(tmp2);
+ }
+ break;
+
+ case 5:
+ /* load/store register offset. */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ rm = (insn >> 6) & 7;
+ op = (insn >> 9) & 7;
+ addr = load_reg(s, rn);
+ tmp = load_reg(s, rm);
+ tcg_gen_add_i32(addr, addr, tmp);
+ dead_tmp(tmp);
+
+ if (op < 3) /* store */
+ tmp = load_reg(s, rd);
+
+ switch (op) {
+ case 0: /* str */
+ gen_st32(tmp, addr, IS_USER(s));
+ break;
+ case 1: /* strh */
+ gen_st16(tmp, addr, IS_USER(s));
+ break;
+ case 2: /* strb */
+ gen_st8(tmp, addr, IS_USER(s));
+ break;
+ case 3: /* ldrsb */
+ tmp = gen_ld8s(addr, IS_USER(s));
+ break;
+ case 4: /* ldr */
+ tmp = gen_ld32(addr, IS_USER(s));
+ break;
+ case 5: /* ldrh */
+ tmp = gen_ld16u(addr, IS_USER(s));
+ break;
+ case 6: /* ldrb */
+ tmp = gen_ld8u(addr, IS_USER(s));
+ break;
+ case 7: /* ldrsh */
+ tmp = gen_ld16s(addr, IS_USER(s));
+ break;
+ }
+ if (op >= 3) /* load */
+ store_reg(s, rd, tmp);
+ dead_tmp(addr);
+ break;
+
+ case 6:
+ /* load/store word immediate offset */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ addr = load_reg(s, rn);
+ val = (insn >> 4) & 0x7c;
+ tcg_gen_addi_i32(addr, addr, val);
+
+ if (insn & (1 << 11)) {
+ /* load */
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, rd, tmp);
+ } else {
+ /* store */
+ tmp = load_reg(s, rd);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ dead_tmp(addr);
+ break;
+
+ case 7:
+ /* load/store byte immediate offset */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ addr = load_reg(s, rn);
+ val = (insn >> 6) & 0x1f;
+ tcg_gen_addi_i32(addr, addr, val);
+
+ if (insn & (1 << 11)) {
+ /* load */
+ tmp = gen_ld8u(addr, IS_USER(s));
+ store_reg(s, rd, tmp);
+ } else {
+ /* store */
+ tmp = load_reg(s, rd);
+ gen_st8(tmp, addr, IS_USER(s));
+ }
+ dead_tmp(addr);
+ break;
+
+ case 8:
+ /* load/store halfword immediate offset */
+ rd = insn & 7;
+ rn = (insn >> 3) & 7;
+ addr = load_reg(s, rn);
+ val = (insn >> 5) & 0x3e;
+ tcg_gen_addi_i32(addr, addr, val);
+
+ if (insn & (1 << 11)) {
+ /* load */
+ tmp = gen_ld16u(addr, IS_USER(s));
+ store_reg(s, rd, tmp);
+ } else {
+ /* store */
+ tmp = load_reg(s, rd);
+ gen_st16(tmp, addr, IS_USER(s));
+ }
+ dead_tmp(addr);
+ break;
+
+ case 9:
+ /* load/store from stack */
+ rd = (insn >> 8) & 7;
+ addr = load_reg(s, 13);
+ val = (insn & 0xff) * 4;
+ tcg_gen_addi_i32(addr, addr, val);
+
+ if (insn & (1 << 11)) {
+ /* load */
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, rd, tmp);
+ } else {
+ /* store */
+ tmp = load_reg(s, rd);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ dead_tmp(addr);
+ break;
+
+ case 10:
+ /* add to high reg */
+ rd = (insn >> 8) & 7;
+ if (insn & (1 << 11)) {
+ /* SP */
+ tmp = load_reg(s, 13);
+ } else {
+ /* PC. bit 1 is ignored. */
+ tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, (s->pc + 2) & ~(uint32_t)2);
+ }
+ val = (insn & 0xff) * 4;
+ tcg_gen_addi_i32(tmp, tmp, val);
+ store_reg(s, rd, tmp);
+ break;
+
+ case 11:
+ /* misc */
+ op = (insn >> 8) & 0xf;
+ switch (op) {
+ case 0:
+ /* adjust stack pointer */
+ tmp = load_reg(s, 13);
+ val = (insn & 0x7f) * 4;
+ if (insn & (1 << 7))
+ val = -(int32_t)val;
+ tcg_gen_addi_i32(tmp, tmp, val);
+ store_reg(s, 13, tmp);
+ break;
+
+ case 2: /* sign/zero extend. */
+ ARCH(6);
+ rd = insn & 7;
+ rm = (insn >> 3) & 7;
+ tmp = load_reg(s, rm);
+ switch ((insn >> 6) & 3) {
+ case 0: gen_sxth(tmp); break;
+ case 1: gen_sxtb(tmp); break;
+ case 2: gen_uxth(tmp); break;
+ case 3: gen_uxtb(tmp); break;
+ }
+ store_reg(s, rd, tmp);
+ break;
+ case 4: case 5: case 0xc: case 0xd:
+ /* push/pop */
+ addr = load_reg(s, 13);
+ if (insn & (1 << 8))
+ offset = 4;
+ else
+ offset = 0;
+ for (i = 0; i < 8; i++) {
+ if (insn & (1 << i))
+ offset += 4;
+ }
+ if ((insn & (1 << 11)) == 0) {
+ tcg_gen_addi_i32(addr, addr, -offset);
+ }
+ for (i = 0; i < 8; i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 11)) {
+ /* pop */
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, i, tmp);
+ } else {
+ /* push */
+ tmp = load_reg(s, i);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ /* advance to the next address. */
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ }
+ TCGV_UNUSED(tmp);
+ if (insn & (1 << 8)) {
+ if (insn & (1 << 11)) {
+ /* pop pc */
+ tmp = gen_ld32(addr, IS_USER(s));
+ /* don't set the pc until the rest of the instruction
+ has completed */
+ } else {
+ /* push lr */
+ tmp = load_reg(s, 14);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ if ((insn & (1 << 11)) == 0) {
+ tcg_gen_addi_i32(addr, addr, -offset);
+ }
+ /* write back the new stack pointer */
+ store_reg(s, 13, addr);
+ /* set the new PC value */
+ if ((insn & 0x0900) == 0x0900)
+ gen_bx(s, tmp);
+ break;
+
+ case 1: case 3: case 9: case 11: /* czb */
+ rm = insn & 7;
+ tmp = load_reg(s, rm);
+ s->condlabel = gen_new_label();
+ s->condjmp = 1;
+ if (insn & (1 << 11))
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel);
+ else
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, s->condlabel);
+ dead_tmp(tmp);
+ offset = ((insn & 0xf8) >> 2) | (insn & 0x200) >> 3;
+ val = (uint32_t)s->pc + 2;
+ val += offset;
+ gen_jmp(s, val);
+ break;
+
+ case 15: /* IT, nop-hint. */
+ if ((insn & 0xf) == 0) {
+ gen_nop_hint(s, (insn >> 4) & 0xf);
+ break;
+ }
+ /* If Then. */
+ s->condexec_cond = (insn >> 4) & 0xe;
+ s->condexec_mask = insn & 0x1f;
+ /* No actual code generated for this insn, just setup state. */
+ break;
+
+ case 0xe: /* bkpt */
+ gen_exception_insn(s, 2, EXCP_BKPT);
+ break;
+
+ case 0xa: /* rev */
+ ARCH(6);
+ rn = (insn >> 3) & 0x7;
+ rd = insn & 0x7;
+ tmp = load_reg(s, rn);
+ switch ((insn >> 6) & 3) {
+ case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
+ case 1: gen_rev16(tmp); break;
+ case 3: gen_revsh(tmp); break;
+ default: goto illegal_op;
+ }
+ store_reg(s, rd, tmp);
+ break;
+
+ case 6: /* cps */
+ ARCH(6);
+ if (IS_USER(s))
+ break;
+ if (IS_M(env)) {
+ tmp = tcg_const_i32((insn & (1 << 4)) != 0);
+ /* PRIMASK */
+ if (insn & 1) {
+ addr = tcg_const_i32(16);
+ gen_helper_v7m_msr(cpu_env, addr, tmp);
+ tcg_temp_free_i32(addr);
+ }
+ /* FAULTMASK */
+ if (insn & 2) {
+ addr = tcg_const_i32(17);
+ gen_helper_v7m_msr(cpu_env, addr, tmp);
+ tcg_temp_free_i32(addr);
+ }
+ tcg_temp_free_i32(tmp);
+ gen_lookup_tb(s);
+ } else {
+ if (insn & (1 << 4))
+ shift = CPSR_A | CPSR_I | CPSR_F;
+ else
+ shift = 0;
+ gen_set_psr_im(s, ((insn & 7) << 6), 0, shift);
+ }
+ break;
+
+ default:
+ goto undef;
+ }
+ break;
+
+ case 12:
+ /* load/store multiple */
+ rn = (insn >> 8) & 0x7;
+ addr = load_reg(s, rn);
+ for (i = 0; i < 8; i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 11)) {
+ /* load */
+ tmp = gen_ld32(addr, IS_USER(s));
+ store_reg(s, i, tmp);
+ } else {
+ /* store */
+ tmp = load_reg(s, i);
+ gen_st32(tmp, addr, IS_USER(s));
+ }
+ /* advance to the next address */
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ }
+ /* Base register writeback. */
+ if ((insn & (1 << rn)) == 0) {
+ store_reg(s, rn, addr);
+ } else {
+ dead_tmp(addr);
+ }
+ break;
+
+ case 13:
+ /* conditional branch or swi */
+ cond = (insn >> 8) & 0xf;
+ if (cond == 0xe)
+ goto undef;
+
+ if (cond == 0xf) {
+ /* swi */
+ gen_set_pc_im(s->pc);
+ s->is_jmp = DISAS_SWI;
+ break;
+ }
+ /* generate a conditional jump to next instruction */
+ s->condlabel = gen_new_label();
+ gen_test_cc(cond ^ 1, s->condlabel);
+ s->condjmp = 1;
+
+ /* jump to the offset */
+ val = (uint32_t)s->pc + 2;
+ offset = ((int32_t)insn << 24) >> 24;
+ val += offset << 1;
+ gen_jmp(s, val);
+ break;
+
+ case 14:
+ if (insn & (1 << 11)) {
+ if (disas_thumb2_insn(env, s, insn))
+ goto undef32;
+ break;
+ }
+ /* unconditional branch */
+ val = (uint32_t)s->pc;
+ offset = ((int32_t)insn << 21) >> 21;
+ val += (offset << 1) + 2;
+ gen_jmp(s, val);
+ break;
+
+ case 15:
+ if (disas_thumb2_insn(env, s, insn))
+ goto undef32;
+ break;
+ }
+ return;
+undef32:
+ gen_exception_insn(s, 4, EXCP_UDEF);
+ return;
+illegal_op:
+undef:
+ gen_exception_insn(s, 2, EXCP_UDEF);
+}
+
+/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
+ basic block 'tb'. If search_pc is TRUE, also generate PC
+ information for each intermediate instruction. */
+static inline void gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext dc1, *dc = &dc1;
+ CPUBreakpoint *bp;
+ uint16_t *gen_opc_end;
+ int j, lj;
+ target_ulong pc_start;
+ uint32_t next_page_start;
+ int num_insns;
+ int max_insns;
+
+ /* generate intermediate code */
+ num_temps = 0;
+
+ pc_start = tb->pc;
+
+ dc->tb = tb;
+
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ dc->is_jmp = DISAS_NEXT;
+ dc->pc = pc_start;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->condjmp = 0;
+ dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
+ dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
+ dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
+#if !defined(CONFIG_USER_ONLY)
+ dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
+#endif
+ dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
+ dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
+ dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
+ cpu_F0s = tcg_temp_new_i32();
+ cpu_F1s = tcg_temp_new_i32();
+ cpu_F0d = tcg_temp_new_i64();
+ cpu_F1d = tcg_temp_new_i64();
+ cpu_V0 = cpu_F0d;
+ cpu_V1 = cpu_F1d;
+ /* FIXME: cpu_M0 can probably be the same as cpu_V0. */
+ cpu_M0 = tcg_temp_new_i64();
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
+
+ /* A note on handling of the condexec (IT) bits:
+ *
+ * We want to avoid the overhead of having to write the updated condexec
+ * bits back to the CPUState for every instruction in an IT block. So:
+ * (1) if the condexec bits are not already zero then we write
+ * zero back into the CPUState now. This avoids complications trying
+ * to do it at the end of the block. (For example if we don't do this
+ * it's hard to identify whether we can safely skip writing condexec
+ * at the end of the TB, which we definitely want to do for the case
+ * where a TB doesn't do anything with the IT state at all.)
+ * (2) if we are going to leave the TB then we call gen_set_condexec()
+ * which will write the correct value into CPUState if zero is wrong.
+ * This is done both for leaving the TB at the end, and for leaving
+ * it because of an exception we know will happen, which is done in
+ * gen_exception_insn(). The latter is necessary because we need to
+ * leave the TB with the PC/IT state just prior to execution of the
+ * instruction which caused the exception.
+ * (3) if we leave the TB unexpectedly (eg a data abort on a load)
+ * then the CPUState will be wrong and we need to reset it.
+ * This is handled in the same way as restoration of the
+ * PC in these situations: we will be called again with search_pc=1
+ * and generate a mapping of the condexec bits for each PC in
+ * gen_opc_condexec_bits[]. gen_pc_load[] then uses this to restore
+ * the condexec bits.
+ *
+ * Note that there are no instructions which can read the condexec
+ * bits, and none which can write non-static values to them, so
+ * we don't need to care about whether CPUState is correct in the
+ * middle of a TB.
+ */
+
+ /* Reset the conditional execution bits immediately. This avoids
+ complications trying to do it at the end of the block. */
+ if (dc->condexec_mask || dc->condexec_cond)
+ {
+ TCGv tmp = new_tmp();
+ tcg_gen_movi_i32(tmp, 0);
+ store_cpu_field(tmp, condexec_bits);
+ }
+ do {
+#ifdef CONFIG_USER_ONLY
+ /* Intercept jump to the magic kernel page. */
+ if (dc->pc >= 0xffff0000) {
+ /* We always get here via a jump, so know we are not in a
+ conditional execution block. */
+ gen_exception(EXCP_KERNEL_TRAP);
+ dc->is_jmp = DISAS_UPDATE;
+ break;
+ }
+#else
+ if (dc->pc >= 0xfffffff0 && IS_M(env)) {
+ /* We always get here via a jump, so know we are not in a
+ conditional execution block. */
+ gen_exception(EXCP_EXCEPTION_EXIT);
+ dc->is_jmp = DISAS_UPDATE;
+ break;
+ }
+#endif
+
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == dc->pc) {
+ gen_exception_insn(dc, 0, EXCP_DEBUG);
+ /* Advance PC so that clearing the breakpoint will
+ invalidate this TB. */
+ dc->pc += 2;
+ goto done_generating;
+ 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_condexec_bits[lj] = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1);
+ 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 (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) {
+ tcg_gen_debug_insn_start(dc->pc);
+ }
+
+ if (dc->thumb) {
+ disas_thumb_insn(env, dc);
+ if (dc->condexec_mask) {
+ dc->condexec_cond = (dc->condexec_cond & 0xe)
+ | ((dc->condexec_mask >> 4) & 1);
+ dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f;
+ if (dc->condexec_mask == 0) {
+ dc->condexec_cond = 0;
+ }
+ }
+ } else {
+ disas_arm_insn(env, dc);
+ }
+ if (num_temps) {
+ fprintf(stderr, "Internal resource leak before %08x\n", dc->pc);
+ num_temps = 0;
+ }
+
+ if (dc->condjmp && !dc->is_jmp) {
+ gen_set_label(dc->condlabel);
+ dc->condjmp = 0;
+ }
+ /* Translation stops when a conditional branch is encountered.
+ * Otherwise the subsequent code could get translated several times.
+ * Also stop translation when a page boundary is reached. This
+ * ensures prefetch aborts occur at the right place. */
+ num_insns ++;
+ } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
+ !env->singlestep_enabled &&
+ !singlestep &&
+ dc->pc < next_page_start &&
+ num_insns < max_insns);
+
+ if (tb->cflags & CF_LAST_IO) {
+ if (dc->condjmp) {
+ /* FIXME: This can theoretically happen with self-modifying
+ code. */
+ cpu_abort(env, "IO on conditional branch instruction");
+ }
+ gen_io_end();
+ }
+
+ /* At this stage dc->condjmp will only be set when the skipped
+ instruction was a conditional branch or trap, and the PC has
+ already been written. */
+ if (unlikely(env->singlestep_enabled)) {
+ /* Make sure the pc is updated, and raise a debug exception. */
+ if (dc->condjmp) {
+ gen_set_condexec(dc);
+ if (dc->is_jmp == DISAS_SWI) {
+ gen_exception(EXCP_SWI);
+ } else {
+ gen_exception(EXCP_DEBUG);
+ }
+ gen_set_label(dc->condlabel);
+ }
+ if (dc->condjmp || !dc->is_jmp) {
+ gen_set_pc_im(dc->pc);
+ dc->condjmp = 0;
+ }
+ gen_set_condexec(dc);
+ if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
+ gen_exception(EXCP_SWI);
+ } else {
+ /* FIXME: Single stepping a WFI insn will not halt
+ the CPU. */
+ gen_exception(EXCP_DEBUG);
+ }
+ } else {
+ /* While branches must always occur at the end of an IT block,
+ there are a few other things that can cause us to terminate
+ the TB in the middel of an IT block:
+ - Exception generating instructions (bkpt, swi, undefined).
+ - Page boundaries.
+ - Hardware watchpoints.
+ Hardware breakpoints have already been handled and skip this code.
+ */
+ gen_set_condexec(dc);
+ switch(dc->is_jmp) {
+ case DISAS_NEXT:
+ gen_goto_tb(dc, 1, dc->pc);
+ break;
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ /* indicate that the hash table must be used to find the next TB */
+ tcg_gen_exit_tb(0);
+ break;
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ case DISAS_WFI:
+ gen_helper_wfi();
+ break;
+ case DISAS_SWI:
+ gen_exception(EXCP_SWI);
+ break;
+ }
+ if (dc->condjmp) {
+ gen_set_label(dc->condlabel);
+ gen_set_condexec(dc);
+ gen_goto_tb(dc, 1, dc->pc);
+ dc->condjmp = 0;
+ }
+ }
+
+done_generating:
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("----------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, dc->pc - pc_start, dc->thumb);
+ qemu_log("\n");
+ }
+#endif
+ 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;
+ }
+}
+
+void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 1);
+}
+
+static const char *cpu_mode_names[16] = {
+ "usr", "fiq", "irq", "svc", "???", "???", "???", "abt",
+ "???", "???", "???", "und", "???", "???", "???", "sys"
+};
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+#if 0
+ union {
+ uint32_t i;
+ float s;
+ } s0, s1;
+ CPU_DoubleU d;
+ /* ??? This assumes float64 and double have the same layout.
+ Oh well, it's only debug dumps. */
+ union {
+ float64 f64;
+ double d;
+ } d0;
+#endif
+ uint32_t psr;
+
+ for(i=0;i<16;i++) {
+ cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]);
+ if ((i % 4) == 3)
+ cpu_fprintf(f, "\n");
+ else
+ cpu_fprintf(f, " ");
+ }
+ psr = cpsr_read(env);
+ cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%d\n",
+ psr,
+ psr & (1 << 31) ? 'N' : '-',
+ psr & (1 << 30) ? 'Z' : '-',
+ psr & (1 << 29) ? 'C' : '-',
+ psr & (1 << 28) ? 'V' : '-',
+ psr & CPSR_T ? 'T' : 'A',
+ cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26);
+
+#if 0
+ for (i = 0; i < 16; i++) {
+ d.d = env->vfp.regs[i];
+ s0.i = d.l.lower;
+ s1.i = d.l.upper;
+ d0.f64 = d.d;
+ cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%08x%08x(%8g)\n",
+ i * 2, (int)s0.i, s0.s,
+ i * 2 + 1, (int)s1.i, s1.s,
+ i, (int)(uint32_t)d.l.upper, (int)(uint32_t)d.l.lower,
+ d0.d);
+ }
+ cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.xregs[ARM_VFP_FPSCR]);
+#endif
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->regs[15] = gen_opc_pc[pc_pos];
+ env->condexec_bits = gen_opc_condexec_bits[pc_pos];
+}
diff --git a/target-cris/cpu.h b/target-cris/cpu.h
new file mode 100644
index 0000000..d908775
--- /dev/null
+++ b/target-cris/cpu.h
@@ -0,0 +1,268 @@
+/*
+ * CRIS virtual CPU header
+ *
+ * Copyright (c) 2007 AXIS Communications AB
+ * Written by Edgar E. Iglesias
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CPU_CRIS_H
+#define CPU_CRIS_H
+
+#define TARGET_LONG_BITS 32
+
+#define CPUState struct CPUCRISState
+
+#include "cpu-defs.h"
+
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE EM_CRIS
+
+#define EXCP_NMI 1
+#define EXCP_GURU 2
+#define EXCP_BUSFAULT 3
+#define EXCP_IRQ 4
+#define EXCP_BREAK 5
+
+/* Register aliases. R0 - R15 */
+#define R_FP 8
+#define R_SP 14
+#define R_ACR 15
+
+/* Support regs, P0 - P15 */
+#define PR_BZ 0
+#define PR_VR 1
+#define PR_PID 2
+#define PR_SRS 3
+#define PR_WZ 4
+#define PR_EXS 5
+#define PR_EDA 6
+#define PR_PREFIX 6 /* On CRISv10 P6 is reserved, we use it as prefix. */
+#define PR_MOF 7
+#define PR_DZ 8
+#define PR_EBP 9
+#define PR_ERP 10
+#define PR_SRP 11
+#define PR_NRP 12
+#define PR_CCS 13
+#define PR_USP 14
+#define PR_SPC 15
+
+/* CPU flags. */
+#define Q_FLAG 0x80000000
+#define M_FLAG 0x40000000
+#define PFIX_FLAG 0x800 /* CRISv10 Only. */
+#define S_FLAG 0x200
+#define R_FLAG 0x100
+#define P_FLAG 0x80
+#define U_FLAG 0x40
+#define I_FLAG 0x20
+#define X_FLAG 0x10
+#define N_FLAG 0x08
+#define Z_FLAG 0x04
+#define V_FLAG 0x02
+#define C_FLAG 0x01
+#define ALU_FLAGS 0x1F
+
+/* Condition codes. */
+#define CC_CC 0
+#define CC_CS 1
+#define CC_NE 2
+#define CC_EQ 3
+#define CC_VC 4
+#define CC_VS 5
+#define CC_PL 6
+#define CC_MI 7
+#define CC_LS 8
+#define CC_HI 9
+#define CC_GE 10
+#define CC_LT 11
+#define CC_GT 12
+#define CC_LE 13
+#define CC_A 14
+#define CC_P 15
+
+#define NB_MMU_MODES 2
+
+typedef struct CPUCRISState {
+ uint32_t regs[16];
+ /* P0 - P15 are referred to as special registers in the docs. */
+ uint32_t pregs[16];
+
+ /* Pseudo register for the PC. Not directly accessable on CRIS. */
+ uint32_t pc;
+
+ /* Pseudo register for the kernel stack. */
+ uint32_t ksp;
+
+ /* Branch. */
+ int dslot;
+ int btaken;
+ uint32_t btarget;
+
+ /* Condition flag tracking. */
+ uint32_t cc_op;
+ uint32_t cc_mask;
+ uint32_t cc_dest;
+ uint32_t cc_src;
+ uint32_t cc_result;
+ /* size of the operation, 1 = byte, 2 = word, 4 = dword. */
+ int cc_size;
+ /* X flag at the time of cc snapshot. */
+ int cc_x;
+
+ /* CRIS has certain insns that lockout interrupts. */
+ int locked_irq;
+ int interrupt_vector;
+ int fault_vector;
+ int trap_vector;
+
+ /* FIXME: add a check in the translator to avoid writing to support
+ register sets beyond the 4th. The ISA allows up to 256! but in
+ practice there is no core that implements more than 4.
+
+ Support function registers are used to control units close to the
+ core. Accesses do not pass down the normal hierarchy.
+ */
+ uint32_t sregs[4][16];
+
+ /* Linear feedback shift reg in the mmu. Used to provide pseudo
+ randomness for the 'hint' the mmu gives to sw for chosing valid
+ sets on TLB refills. */
+ uint32_t mmu_rand_lfsr;
+
+ /*
+ * We just store the stores to the tlbset here for later evaluation
+ * when the hw needs access to them.
+ *
+ * One for I and another for D.
+ */
+ struct
+ {
+ uint32_t hi;
+ uint32_t lo;
+ } tlbsets[2][4][16];
+
+ CPU_COMMON
+
+ /* Members after CPU_COMMON are preserved across resets. */
+ void *load_info;
+} CPUCRISState;
+
+CPUCRISState *cpu_cris_init(const char *cpu_model);
+int cpu_cris_exec(CPUCRISState *s);
+void cpu_cris_close(CPUCRISState *s);
+void do_interrupt(CPUCRISState *env);
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_cris_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+
+enum {
+ CC_OP_DYNAMIC, /* Use env->cc_op */
+ CC_OP_FLAGS,
+ CC_OP_CMP,
+ CC_OP_MOVE,
+ CC_OP_ADD,
+ CC_OP_ADDC,
+ CC_OP_MCP,
+ CC_OP_ADDU,
+ CC_OP_SUB,
+ CC_OP_SUBU,
+ CC_OP_NEG,
+ CC_OP_BTST,
+ CC_OP_MULS,
+ CC_OP_MULU,
+ CC_OP_DSTEP,
+ CC_OP_MSTEP,
+ CC_OP_BOUND,
+
+ CC_OP_OR,
+ CC_OP_AND,
+ CC_OP_XOR,
+ CC_OP_LSL,
+ CC_OP_LSR,
+ CC_OP_ASR,
+ CC_OP_LZ
+};
+
+/* CRIS uses 8k pages. */
+#define TARGET_PAGE_BITS 13
+#define MMAP_SHIFT TARGET_PAGE_BITS
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define cpu_init cpu_cris_init
+#define cpu_exec cpu_cris_exec
+#define cpu_gen_code cpu_cris_gen_code
+#define cpu_signal_handler cpu_cris_signal_handler
+
+#define CPU_SAVE_VERSION 1
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return !!(env->pregs[PR_CCS] & U_FLAG);
+}
+
+int cpu_cris_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_cris_handle_mmu_fault
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regs[14] = newsp;
+ env->regs[10] = 0;
+}
+#endif
+
+static inline void cpu_set_tls(CPUCRISState *env, target_ulong newtls)
+{
+ env->pregs[PR_PID] = (env->pregs[PR_PID] & 0xff) | newtls;
+}
+
+/* Support function regs. */
+#define SFR_RW_GC_CFG 0][0
+#define SFR_RW_MM_CFG env->pregs[PR_SRS]][0
+#define SFR_RW_MM_KBASE_LO env->pregs[PR_SRS]][1
+#define SFR_RW_MM_KBASE_HI env->pregs[PR_SRS]][2
+#define SFR_R_MM_CAUSE env->pregs[PR_SRS]][3
+#define SFR_RW_MM_TLB_SEL env->pregs[PR_SRS]][4
+#define SFR_RW_MM_TLB_LO env->pregs[PR_SRS]][5
+#define SFR_RW_MM_TLB_HI env->pregs[PR_SRS]][6
+
+#include "cpu-all.h"
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = env->dslot |
+ (env->pregs[PR_CCS] & (S_FLAG | P_FLAG | U_FLAG
+ | X_FLAG | PFIX_FLAG));
+}
+
+#define cpu_list cris_cpu_list
+void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+
+#endif
diff --git a/target-cris/crisv10-decode.h b/target-cris/crisv10-decode.h
new file mode 100644
index 0000000..587fbdd
--- /dev/null
+++ b/target-cris/crisv10-decode.h
@@ -0,0 +1,107 @@
+/*
+ * CRISv10 insn decoding macros.
+ *
+ * Copyright (c) 2010 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * 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/>.
+ */
+
+#define CRISV10_MODE_QIMMEDIATE 0
+#define CRISV10_MODE_REG 1
+#define CRISV10_MODE_INDIRECT 2
+#define CRISV10_MODE_AUTOINC 3
+
+/* Quick Immediate. */
+#define CRISV10_QIMM_BCC_R0 0
+#define CRISV10_QIMM_BCC_R1 1
+#define CRISV10_QIMM_BCC_R2 2
+#define CRISV10_QIMM_BCC_R3 3
+
+#define CRISV10_QIMM_BDAP_R0 4
+#define CRISV10_QIMM_BDAP_R1 5
+#define CRISV10_QIMM_BDAP_R2 6
+#define CRISV10_QIMM_BDAP_R3 7
+
+#define CRISV10_QIMM_ADDQ 8
+#define CRISV10_QIMM_MOVEQ 9
+#define CRISV10_QIMM_SUBQ 10
+#define CRISV10_QIMM_CMPQ 11
+#define CRISV10_QIMM_ANDQ 12
+#define CRISV10_QIMM_ORQ 13
+#define CRISV10_QIMM_ASHQ 14
+#define CRISV10_QIMM_LSHQ 15
+
+
+#define CRISV10_REG_ADDX 0
+#define CRISV10_REG_MOVX 1
+#define CRISV10_REG_SUBX 2
+#define CRISV10_REG_LSL 3
+#define CRISV10_REG_ADDI 4
+#define CRISV10_REG_BIAP 5
+#define CRISV10_REG_NEG 6
+#define CRISV10_REG_BOUND 7
+#define CRISV10_REG_ADD 8
+#define CRISV10_REG_MOVE_R 9
+#define CRISV10_REG_MOVE_SPR_R 9
+#define CRISV10_REG_MOVE_R_SPR 8
+#define CRISV10_REG_SUB 10
+#define CRISV10_REG_CMP 11
+#define CRISV10_REG_AND 12
+#define CRISV10_REG_OR 13
+#define CRISV10_REG_ASR 14
+#define CRISV10_REG_LSR 15
+
+#define CRISV10_REG_BTST 3
+#define CRISV10_REG_SCC 4
+#define CRISV10_REG_SETF 6
+#define CRISV10_REG_CLEARF 7
+#define CRISV10_REG_BIAP 5
+#define CRISV10_REG_ABS 10
+#define CRISV10_REG_DSTEP 11
+#define CRISV10_REG_LZ 12
+#define CRISV10_REG_NOT 13
+#define CRISV10_REG_SWAP 13
+#define CRISV10_REG_XOR 14
+#define CRISV10_REG_MSTEP 15
+
+/* Indirect, var size. */
+#define CRISV10_IND_TEST 14
+#define CRISV10_IND_MUL 4
+#define CRISV10_IND_BDAP_M 5
+#define CRISV10_IND_ADD 8
+#define CRISV10_IND_MOVE_M_R 9
+
+
+/* indirect fixed size. */
+#define CRISV10_IND_ADDX 0
+#define CRISV10_IND_MOVX 1
+#define CRISV10_IND_SUBX 2
+#define CRISV10_IND_CMPX 3
+#define CRISV10_IND_JUMP_M 4
+#define CRISV10_IND_DIP 5
+#define CRISV10_IND_JUMP_R 6
+#define CRISV10_IND_BOUND 7
+#define CRISV10_IND_BCC_M 7
+#define CRISV10_IND_MOVE_M_SPR 8
+#define CRISV10_IND_MOVE_SPR_M 9
+#define CRISV10_IND_SUB 10
+#define CRISV10_IND_CMP 11
+#define CRISV10_IND_AND 12
+#define CRISV10_IND_OR 13
+#define CRISV10_IND_MOVE_R_M 15
+
+#define CRISV10_IND_MOVEM_M_R 14
+#define CRISV10_IND_MOVEM_R_M 15
+
diff --git a/target-cris/crisv32-decode.h b/target-cris/crisv32-decode.h
new file mode 100644
index 0000000..ed141de
--- /dev/null
+++ b/target-cris/crisv32-decode.h
@@ -0,0 +1,128 @@
+/*
+ * CRIS insn decoding macros.
+ *
+ * Copyright (c) 2007 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * 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/>.
+ */
+
+/* Convenient binary macros. */
+#define HEX__(n) 0x##n##LU
+#define B8__(x) ((x&0x0000000FLU)?1:0) \
+ + ((x&0x000000F0LU)?2:0) \
+ + ((x&0x00000F00LU)?4:0) \
+ + ((x&0x0000F000LU)?8:0) \
+ + ((x&0x000F0000LU)?16:0) \
+ + ((x&0x00F00000LU)?32:0) \
+ + ((x&0x0F000000LU)?64:0) \
+ + ((x&0xF0000000LU)?128:0)
+#define B8(d) ((unsigned char)B8__(HEX__(d)))
+
+/* Quick imm. */
+#define DEC_BCCQ {B8(00000000), B8(11110000)}
+#define DEC_ADDOQ {B8(00010000), B8(11110000)}
+#define DEC_ADDQ {B8(00100000), B8(11111100)}
+#define DEC_MOVEQ {B8(00100100), B8(11111100)}
+#define DEC_SUBQ {B8(00101000), B8(11111100)}
+#define DEC_CMPQ {B8(00101100), B8(11111100)}
+#define DEC_ANDQ {B8(00110000), B8(11111100)}
+#define DEC_ORQ {B8(00110100), B8(11111100)}
+#define DEC_BTSTQ {B8(00111000), B8(11111110)}
+#define DEC_ASRQ {B8(00111010), B8(11111110)}
+#define DEC_LSLQ {B8(00111100), B8(11111110)}
+#define DEC_LSRQ {B8(00111110), B8(11111110)}
+
+/* Register. */
+#define DEC_MOVU_R {B8(01000100), B8(11111110)}
+#define DEC_MOVU_R {B8(01000100), B8(11111110)}
+#define DEC_MOVS_R {B8(01000110), B8(11111110)}
+#define DEC_MOVE_R {B8(01100100), B8(11111100)}
+#define DEC_MOVE_RP {B8(01100011), B8(11111111)}
+#define DEC_MOVE_PR {B8(01100111), B8(11111111)}
+#define DEC_DSTEP_R {B8(01101111), B8(11111111)}
+#define DEC_MOVE_RS {B8(10110111), B8(11111111)}
+#define DEC_MOVE_SR {B8(11110111), B8(11111111)}
+#define DEC_ADDU_R {B8(01000000), B8(11111110)}
+#define DEC_ADDS_R {B8(01000010), B8(11111110)}
+#define DEC_ADD_R {B8(01100000), B8(11111100)}
+#define DEC_ADDI_R {B8(01010000), B8(11111100)}
+#define DEC_MULS_R {B8(11010000), B8(11111100)}
+#define DEC_MULU_R {B8(10010000), B8(11111100)}
+#define DEC_ADDI_ACR {B8(01010100), B8(11111100)}
+#define DEC_NEG_R {B8(01011000), B8(11111100)}
+#define DEC_BOUND_R {B8(01011100), B8(11111100)}
+#define DEC_SUBU_R {B8(01001000), B8(11111110)}
+#define DEC_SUBS_R {B8(01001010), B8(11111110)}
+#define DEC_SUB_R {B8(01101000), B8(11111100)}
+#define DEC_CMP_R {B8(01101100), B8(11111100)}
+#define DEC_AND_R {B8(01110000), B8(11111100)}
+#define DEC_ABS_R {B8(01101011), B8(11111111)}
+#define DEC_LZ_R {B8(01110011), B8(11111111)}
+#define DEC_MCP_R {B8(01111111), B8(11111111)}
+#define DEC_SWAP_R {B8(01110111), B8(11111111)}
+#define DEC_XOR_R {B8(01111011), B8(11111111)}
+#define DEC_LSL_R {B8(01001100), B8(11111100)}
+#define DEC_LSR_R {B8(01111100), B8(11111100)}
+#define DEC_ASR_R {B8(01111000), B8(11111100)}
+#define DEC_OR_R {B8(01110100), B8(11111100)}
+#define DEC_BTST_R {B8(01001111), B8(11111111)}
+
+/* Fixed. */
+#define DEC_SETF {B8(01011011), B8(11111111)}
+#define DEC_CLEARF {B8(01011111), B8(11111111)}
+
+/* Memory. */
+#define DEC_ADDU_M {B8(10000000), B8(10111110)}
+#define DEC_ADDS_M {B8(10000010), B8(10111110)}
+#define DEC_MOVU_M {B8(10000100), B8(10111110)}
+#define DEC_MOVS_M {B8(10000110), B8(10111110)}
+#define DEC_SUBU_M {B8(10001000), B8(10111110)}
+#define DEC_SUBS_M {B8(10001010), B8(10111110)}
+#define DEC_CMPU_M {B8(10001100), B8(10111110)}
+#define DEC_CMPS_M {B8(10001110), B8(10111110)}
+#define DEC_ADDO_M {B8(10010100), B8(10111100)}
+#define DEC_BOUND_M {B8(10011100), B8(10111100)}
+#define DEC_ADD_M {B8(10100000), B8(10111100)}
+#define DEC_MOVE_MR {B8(10100100), B8(10111100)}
+#define DEC_SUB_M {B8(10101000), B8(10111100)}
+#define DEC_CMP_M {B8(10101100), B8(10111100)}
+#define DEC_AND_M {B8(10110000), B8(10111100)}
+#define DEC_OR_M {B8(10110100), B8(10111100)}
+#define DEC_TEST_M {B8(10111000), B8(10111100)}
+#define DEC_MOVE_RM {B8(10111100), B8(10111100)}
+
+#define DEC_ADDC_R {B8(01010111), B8(11111111)}
+#define DEC_ADDC_MR {B8(10011010), B8(10111111)}
+#define DEC_LAPCQ {B8(10010111), B8(11111111)}
+#define DEC_LAPC_IM {B8(11010111), B8(11111111)}
+
+#define DEC_MOVE_MP {B8(10100011), B8(10111111)}
+#define DEC_MOVE_PM {B8(10100111), B8(10111111)}
+
+#define DEC_SCC_R {B8(01010011), B8(11111111)}
+#define DEC_RFE_ETC {B8(10010011), B8(11111111)}
+#define DEC_JUMP_P {B8(10011111), B8(11111111)}
+#define DEC_BCC_IM {B8(11011111), B8(11111111)}
+#define DEC_JAS_R {B8(10011011), B8(11111111)}
+#define DEC_JASC_R {B8(10110011), B8(11111111)}
+#define DEC_JAS_IM {B8(11011011), B8(11111111)}
+#define DEC_JASC_IM {B8(11110011), B8(11111111)}
+#define DEC_BAS_IM {B8(11101011), B8(11111111)}
+#define DEC_BASC_IM {B8(11101111), B8(11111111)}
+#define DEC_MOVEM_MR {B8(10111011), B8(10111111)}
+#define DEC_MOVEM_RM {B8(10111111), B8(10111111)}
+
+#define DEC_FTAG_FIDX_D_M {B8(10101011), B8(11111111)}
+#define DEC_FTAG_FIDX_I_M {B8(11010011), B8(11111111)}
diff --git a/target-cris/exec.h b/target-cris/exec.h
new file mode 100644
index 0000000..93ce768
--- /dev/null
+++ b/target-cris/exec.h
@@ -0,0 +1,53 @@
+/*
+ * CRIS execution defines
+ *
+ * Copyright (c) 2007 AXIS Communications AB
+ * Written by Edgar E. Iglesias
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "dyngen-exec.h"
+
+register struct CPUCRISState *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI));
+}
+
+static inline int cpu_halted(CPUState *env) {
+ if (!env->halted)
+ return 0;
+
+ /* IRQ, NMI and GURU execeptions wakes us up. */
+ if (env->interrupt_request
+ & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->pc = tb->pc;
+}
+
diff --git a/target-cris/helper.c b/target-cris/helper.c
new file mode 100644
index 0000000..2a4403b
--- /dev/null
+++ b/target-cris/helper.c
@@ -0,0 +1,271 @@
+/*
+ * CRIS helper routines.
+ *
+ * Copyright (c) 2007 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "mmu.h"
+#include "exec-all.h"
+#include "host-utils.h"
+
+
+//#define CRIS_HELPER_DEBUG
+
+
+#ifdef CRIS_HELPER_DEBUG
+#define D(x) x
+#define D_LOG(...) qemu_log(__VA__ARGS__)
+#else
+#define D(x)
+#define D_LOG(...) do { } while (0)
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+ env->pregs[PR_ERP] = env->pc;
+}
+
+int cpu_cris_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ env->exception_index = 0xaa;
+ env->pregs[PR_EDA] = address;
+ cpu_dump_state(env, stderr, fprintf, 0);
+ return 1;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+
+static void cris_shift_ccs(CPUState *env)
+{
+ uint32_t ccs;
+ /* Apply the ccs shift. */
+ ccs = env->pregs[PR_CCS];
+ ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff;
+ env->pregs[PR_CCS] = ccs;
+}
+
+int cpu_cris_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ struct cris_mmu_result res;
+ int prot, miss;
+ int r = -1;
+ target_ulong phy;
+
+ D(printf ("%s addr=%x pc=%x rw=%x\n", __func__, address, env->pc, rw));
+ miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK,
+ rw, mmu_idx, 0);
+ if (miss)
+ {
+ if (env->exception_index == EXCP_BUSFAULT)
+ cpu_abort(env,
+ "CRIS: Illegal recursive bus fault."
+ "addr=%x rw=%d\n",
+ address, rw);
+
+ env->pregs[PR_EDA] = address;
+ env->exception_index = EXCP_BUSFAULT;
+ env->fault_vector = res.bf_vec;
+ r = 1;
+ }
+ else
+ {
+ /*
+ * Mask off the cache selection bit. The ETRAX busses do not
+ * see the top bit.
+ */
+ phy = res.phy & ~0x80000000;
+ prot = res.prot;
+ tlb_set_page(env, address & TARGET_PAGE_MASK, phy,
+ prot, mmu_idx, TARGET_PAGE_SIZE);
+ r = 0;
+ }
+ if (r > 0)
+ D_LOG("%s returns %d irqreq=%x addr=%x"
+ " phy=%x ismmu=%d vec=%x pc=%x\n",
+ __func__, r, env->interrupt_request,
+ address, res.phy, is_softmmu, res.bf_vec, env->pc);
+ return r;
+}
+
+static void do_interruptv10(CPUState *env)
+{
+ int ex_vec = -1;
+
+ D_LOG( "exception index=%d interrupt_req=%d\n",
+ env->exception_index,
+ env->interrupt_request);
+
+ assert(!(env->pregs[PR_CCS] & PFIX_FLAG));
+ switch (env->exception_index)
+ {
+ case EXCP_BREAK:
+ /* These exceptions are genereated by the core itself.
+ ERP should point to the insn following the brk. */
+ ex_vec = env->trap_vector;
+ env->pregs[PR_ERP] = env->pc;
+ break;
+
+ case EXCP_NMI:
+ /* NMI is hardwired to vector zero. */
+ ex_vec = 0;
+ env->pregs[PR_CCS] &= ~M_FLAG;
+ env->pregs[PR_NRP] = env->pc;
+ break;
+
+ case EXCP_BUSFAULT:
+ cpu_abort(env, "Unhandled busfault");
+ break;
+
+ default:
+ /* The interrupt controller gives us the vector. */
+ ex_vec = env->interrupt_vector;
+ /* Normal interrupts are taken between
+ TB's. env->pc is valid here. */
+ env->pregs[PR_ERP] = env->pc;
+ break;
+ }
+
+ if (env->pregs[PR_CCS] & U_FLAG) {
+ /* Swap stack pointers. */
+ env->pregs[PR_USP] = env->regs[R_SP];
+ env->regs[R_SP] = env->ksp;
+ }
+
+ /* Now that we are in kernel mode, load the handlers address. */
+ env->pc = ldl_code(env->pregs[PR_EBP] + ex_vec * 4);
+ env->locked_irq = 1;
+
+ qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n",
+ __func__, env->pc, ex_vec,
+ env->pregs[PR_CCS],
+ env->pregs[PR_PID],
+ env->pregs[PR_ERP]);
+}
+
+void do_interrupt(CPUState *env)
+{
+ int ex_vec = -1;
+
+ if (env->pregs[PR_VR] < 32)
+ return do_interruptv10(env);
+
+ D_LOG( "exception index=%d interrupt_req=%d\n",
+ env->exception_index,
+ env->interrupt_request);
+
+ switch (env->exception_index)
+ {
+ case EXCP_BREAK:
+ /* These exceptions are genereated by the core itself.
+ ERP should point to the insn following the brk. */
+ ex_vec = env->trap_vector;
+ env->pregs[PR_ERP] = env->pc;
+ break;
+
+ case EXCP_NMI:
+ /* NMI is hardwired to vector zero. */
+ ex_vec = 0;
+ env->pregs[PR_CCS] &= ~M_FLAG;
+ env->pregs[PR_NRP] = env->pc;
+ break;
+
+ case EXCP_BUSFAULT:
+ ex_vec = env->fault_vector;
+ env->pregs[PR_ERP] = env->pc;
+ break;
+
+ default:
+ /* The interrupt controller gives us the vector. */
+ ex_vec = env->interrupt_vector;
+ /* Normal interrupts are taken between
+ TB's. env->pc is valid here. */
+ env->pregs[PR_ERP] = env->pc;
+ break;
+ }
+
+ /* Fill in the IDX field. */
+ env->pregs[PR_EXS] = (ex_vec & 0xff) << 8;
+
+ if (env->dslot) {
+ D_LOG("excp isr=%x PC=%x ds=%d SP=%x"
+ " ERP=%x pid=%x ccs=%x cc=%d %x\n",
+ ex_vec, env->pc, env->dslot,
+ env->regs[R_SP],
+ env->pregs[PR_ERP], env->pregs[PR_PID],
+ env->pregs[PR_CCS],
+ env->cc_op, env->cc_mask);
+ /* We loose the btarget, btaken state here so rexec the
+ branch. */
+ env->pregs[PR_ERP] -= env->dslot;
+ /* Exception starts with dslot cleared. */
+ env->dslot = 0;
+ }
+
+ if (env->pregs[PR_CCS] & U_FLAG) {
+ /* Swap stack pointers. */
+ env->pregs[PR_USP] = env->regs[R_SP];
+ env->regs[R_SP] = env->ksp;
+ }
+
+ /* Apply the CRIS CCS shift. Clears U if set. */
+ cris_shift_ccs(env);
+
+ /* Now that we are in kernel mode, load the handlers address.
+ This load may not fault, real hw leaves that behaviour as
+ undefined. */
+ env->pc = ldl_code(env->pregs[PR_EBP] + ex_vec * 4);
+
+ /* Clear the excption_index to avoid spurios hw_aborts for recursive
+ bus faults. */
+ env->exception_index = -1;
+
+ D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n",
+ __func__, env->pc, ex_vec,
+ env->pregs[PR_CCS],
+ env->pregs[PR_PID],
+ env->pregs[PR_ERP]);
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr)
+{
+ uint32_t phy = addr;
+ struct cris_mmu_result res;
+ int miss;
+
+ miss = cris_mmu_translate(&res, env, addr, 0, 0, 1);
+ /* If D TLB misses, try I TLB. */
+ if (miss) {
+ miss = cris_mmu_translate(&res, env, addr, 2, 0, 1);
+ }
+
+ if (!miss)
+ phy = res.phy;
+ D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy));
+ return phy;
+}
+#endif
diff --git a/target-cris/helper.h b/target-cris/helper.h
new file mode 100644
index 0000000..093063a
--- /dev/null
+++ b/target-cris/helper.h
@@ -0,0 +1,26 @@
+#include "def-helper.h"
+
+DEF_HELPER_1(raise_exception, void, i32)
+DEF_HELPER_1(tlb_flush_pid, void, i32)
+DEF_HELPER_1(spc_write, void, i32)
+DEF_HELPER_3(dump, void, i32, i32, i32)
+DEF_HELPER_0(rfe, void);
+DEF_HELPER_0(rfn, void);
+
+DEF_HELPER_2(movl_sreg_reg, void, i32, i32)
+DEF_HELPER_2(movl_reg_sreg, void, i32, i32)
+
+DEF_HELPER_FLAGS_1(lz, TCG_CALL_PURE, i32, i32);
+DEF_HELPER_FLAGS_3(btst, TCG_CALL_PURE, i32, i32, i32, i32);
+
+DEF_HELPER_FLAGS_3(evaluate_flags_muls, TCG_CALL_PURE, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_3(evaluate_flags_mulu, TCG_CALL_PURE, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_4(evaluate_flags_mcp, TCG_CALL_PURE, i32, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_4(evaluate_flags_alu_4, TCG_CALL_PURE, i32, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_4(evaluate_flags_sub_4, TCG_CALL_PURE, i32, i32, i32, i32, i32)
+DEF_HELPER_FLAGS_2(evaluate_flags_move_4, TCG_CALL_PURE, i32, i32, i32)
+DEF_HELPER_FLAGS_2(evaluate_flags_move_2, TCG_CALL_PURE, i32, i32, i32)
+DEF_HELPER_0(evaluate_flags, void)
+DEF_HELPER_0(top_evaluate_flags, void)
+
+#include "def-helper.h"
diff --git a/target-cris/machine.c b/target-cris/machine.c
new file mode 100644
index 0000000..8f9c0dd
--- /dev/null
+++ b/target-cris/machine.c
@@ -0,0 +1,90 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUCRISState *env = opaque;
+ int i;
+ int s;
+ int mmu;
+
+ for (i = 0; i < 16; i++)
+ qemu_put_be32(f, env->regs[i]);
+ for (i = 0; i < 16; i++)
+ qemu_put_be32(f, env->pregs[i]);
+
+ qemu_put_be32(f, env->pc);
+ qemu_put_be32(f, env->ksp);
+
+ qemu_put_be32(f, env->dslot);
+ qemu_put_be32(f, env->btaken);
+ qemu_put_be32(f, env->btarget);
+
+ qemu_put_be32(f, env->cc_op);
+ qemu_put_be32(f, env->cc_mask);
+ qemu_put_be32(f, env->cc_dest);
+ qemu_put_be32(f, env->cc_src);
+ qemu_put_be32(f, env->cc_result);
+ qemu_put_be32(f, env->cc_size);
+ qemu_put_be32(f, env->cc_x);
+
+ for (s = 0; s < 4; s++) {
+ for (i = 0; i < 16; i++)
+ qemu_put_be32(f, env->sregs[s][i]);
+ }
+
+ qemu_put_be32(f, env->mmu_rand_lfsr);
+ for (mmu = 0; mmu < 2; mmu++) {
+ for (s = 0; s < 4; s++) {
+ for (i = 0; i < 16; i++) {
+ qemu_put_be32(f, env->tlbsets[mmu][s][i].lo);
+ qemu_put_be32(f, env->tlbsets[mmu][s][i].hi);
+ }
+ }
+ }
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUCRISState *env = opaque;
+ int i;
+ int s;
+ int mmu;
+
+ for (i = 0; i < 16; i++)
+ env->regs[i] = qemu_get_be32(f);
+ for (i = 0; i < 16; i++)
+ env->pregs[i] = qemu_get_be32(f);
+
+ env->pc = qemu_get_be32(f);
+ env->ksp = qemu_get_be32(f);
+
+ env->dslot = qemu_get_be32(f);
+ env->btaken = qemu_get_be32(f);
+ env->btarget = qemu_get_be32(f);
+
+ env->cc_op = qemu_get_be32(f);
+ env->cc_mask = qemu_get_be32(f);
+ env->cc_dest = qemu_get_be32(f);
+ env->cc_src = qemu_get_be32(f);
+ env->cc_result = qemu_get_be32(f);
+ env->cc_size = qemu_get_be32(f);
+ env->cc_x = qemu_get_be32(f);
+
+ for (s = 0; s < 4; s++) {
+ for (i = 0; i < 16; i++)
+ env->sregs[s][i] = qemu_get_be32(f);
+ }
+
+ env->mmu_rand_lfsr = qemu_get_be32(f);
+ for (mmu = 0; mmu < 2; mmu++) {
+ for (s = 0; s < 4; s++) {
+ for (i = 0; i < 16; i++) {
+ env->tlbsets[mmu][s][i].lo = qemu_get_be32(f);
+ env->tlbsets[mmu][s][i].hi = qemu_get_be32(f);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/target-cris/mmu.c b/target-cris/mmu.c
new file mode 100644
index 0000000..1243745
--- /dev/null
+++ b/target-cris/mmu.c
@@ -0,0 +1,368 @@
+/*
+ * CRIS mmu emulation.
+ *
+ * Copyright (c) 2007 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONFIG_USER_ONLY
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "mmu.h"
+#include "exec-all.h"
+
+#ifdef DEBUG
+#define D(x) x
+#define D_LOG(...) qemu_log(__VA_ARGS__)
+#else
+#define D(x) do { } while (0)
+#define D_LOG(...) do { } while (0)
+#endif
+
+void cris_mmu_init(CPUState *env)
+{
+ env->mmu_rand_lfsr = 0xcccc;
+}
+
+#define SR_POLYNOM 0x8805
+static inline unsigned int compute_polynom(unsigned int sr)
+{
+ unsigned int i;
+ unsigned int f;
+
+ f = 0;
+ for (i = 0; i < 16; i++)
+ f += ((SR_POLYNOM >> i) & 1) & ((sr >> i) & 1);
+
+ return f;
+}
+
+static void cris_mmu_update_rand_lfsr(CPUState *env)
+{
+ unsigned int f;
+
+ /* Update lfsr at every fault. */
+ f = compute_polynom(env->mmu_rand_lfsr);
+ env->mmu_rand_lfsr >>= 1;
+ env->mmu_rand_lfsr |= (f << 15);
+ env->mmu_rand_lfsr &= 0xffff;
+}
+
+static inline int cris_mmu_enabled(uint32_t rw_gc_cfg)
+{
+ return (rw_gc_cfg & 12) != 0;
+}
+
+static inline int cris_mmu_segmented_addr(int seg, uint32_t rw_mm_cfg)
+{
+ return (1 << seg) & rw_mm_cfg;
+}
+
+static uint32_t cris_mmu_translate_seg(CPUState *env, int seg)
+{
+ uint32_t base;
+ int i;
+
+ if (seg < 8)
+ base = env->sregs[SFR_RW_MM_KBASE_LO];
+ else
+ base = env->sregs[SFR_RW_MM_KBASE_HI];
+
+ i = seg & 7;
+ base >>= i * 4;
+ base &= 15;
+
+ base <<= 28;
+ return base;
+}
+/* Used by the tlb decoder. */
+#define EXTRACT_FIELD(src, start, end) \
+ (((src) >> start) & ((1 << (end - start + 1)) - 1))
+
+static inline void set_field(uint32_t *dst, unsigned int val,
+ unsigned int offset, unsigned int width)
+{
+ uint32_t mask;
+
+ mask = (1 << width) - 1;
+ mask <<= offset;
+ val <<= offset;
+
+ val &= mask;
+ *dst &= ~(mask);
+ *dst |= val;
+}
+
+#ifdef DEBUG
+static void dump_tlb(CPUState *env, int mmu)
+{
+ int set;
+ int idx;
+ uint32_t hi, lo, tlb_vpn, tlb_pfn;
+
+ for (set = 0; set < 4; set++) {
+ for (idx = 0; idx < 16; idx++) {
+ lo = env->tlbsets[mmu][set][idx].lo;
+ hi = env->tlbsets[mmu][set][idx].hi;
+ tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
+ tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
+
+ printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n",
+ set, idx, hi, lo, tlb_vpn, tlb_pfn);
+ }
+ }
+}
+#endif
+
+/* rw 0 = read, 1 = write, 2 = exec. */
+static int cris_mmu_translate_page(struct cris_mmu_result *res,
+ CPUState *env, uint32_t vaddr,
+ int rw, int usermode, int debug)
+{
+ unsigned int vpage;
+ unsigned int idx;
+ uint32_t pid, lo, hi;
+ uint32_t tlb_vpn, tlb_pfn = 0;
+ int tlb_pid, tlb_g, tlb_v, tlb_k, tlb_w, tlb_x;
+ int cfg_v, cfg_k, cfg_w, cfg_x;
+ int set, match = 0;
+ uint32_t r_cause;
+ uint32_t r_cfg;
+ int rwcause;
+ int mmu = 1; /* Data mmu is default. */
+ int vect_base;
+
+ r_cause = env->sregs[SFR_R_MM_CAUSE];
+ r_cfg = env->sregs[SFR_RW_MM_CFG];
+ pid = env->pregs[PR_PID] & 0xff;
+
+ switch (rw) {
+ case 2: rwcause = CRIS_MMU_ERR_EXEC; mmu = 0; break;
+ case 1: rwcause = CRIS_MMU_ERR_WRITE; break;
+ default:
+ case 0: rwcause = CRIS_MMU_ERR_READ; break;
+ }
+
+ /* I exception vectors 4 - 7, D 8 - 11. */
+ vect_base = (mmu + 1) * 4;
+
+ vpage = vaddr >> 13;
+
+ /* We know the index which to check on each set.
+ Scan both I and D. */
+#if 0
+ for (set = 0; set < 4; set++) {
+ for (idx = 0; idx < 16; idx++) {
+ lo = env->tlbsets[mmu][set][idx].lo;
+ hi = env->tlbsets[mmu][set][idx].hi;
+ tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
+ tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
+
+ printf ("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n",
+ set, idx, hi, lo, tlb_vpn, tlb_pfn);
+ }
+ }
+#endif
+
+ idx = vpage & 15;
+ for (set = 0; set < 4; set++)
+ {
+ lo = env->tlbsets[mmu][set][idx].lo;
+ hi = env->tlbsets[mmu][set][idx].hi;
+
+ tlb_vpn = hi >> 13;
+ tlb_pid = EXTRACT_FIELD(hi, 0, 7);
+ tlb_g = EXTRACT_FIELD(lo, 4, 4);
+
+ D_LOG("TLB[%d][%d][%d] v=%x vpage=%x lo=%x hi=%x\n",
+ mmu, set, idx, tlb_vpn, vpage, lo, hi);
+ if ((tlb_g || (tlb_pid == pid))
+ && tlb_vpn == vpage) {
+ match = 1;
+ break;
+ }
+ }
+
+ res->bf_vec = vect_base;
+ if (match) {
+ cfg_w = EXTRACT_FIELD(r_cfg, 19, 19);
+ cfg_k = EXTRACT_FIELD(r_cfg, 18, 18);
+ cfg_x = EXTRACT_FIELD(r_cfg, 17, 17);
+ cfg_v = EXTRACT_FIELD(r_cfg, 16, 16);
+
+ tlb_pfn = EXTRACT_FIELD(lo, 13, 31);
+ tlb_v = EXTRACT_FIELD(lo, 3, 3);
+ tlb_k = EXTRACT_FIELD(lo, 2, 2);
+ tlb_w = EXTRACT_FIELD(lo, 1, 1);
+ tlb_x = EXTRACT_FIELD(lo, 0, 0);
+
+ /*
+ set_exception_vector(0x04, i_mmu_refill);
+ set_exception_vector(0x05, i_mmu_invalid);
+ set_exception_vector(0x06, i_mmu_access);
+ set_exception_vector(0x07, i_mmu_execute);
+ set_exception_vector(0x08, d_mmu_refill);
+ set_exception_vector(0x09, d_mmu_invalid);
+ set_exception_vector(0x0a, d_mmu_access);
+ set_exception_vector(0x0b, d_mmu_write);
+ */
+ if (cfg_k && tlb_k && usermode) {
+ D(printf ("tlb: kernel protected %x lo=%x pc=%x\n",
+ vaddr, lo, env->pc));
+ match = 0;
+ res->bf_vec = vect_base + 2;
+ } else if (rw == 1 && cfg_w && !tlb_w) {
+ D(printf ("tlb: write protected %x lo=%x pc=%x\n",
+ vaddr, lo, env->pc));
+ match = 0;
+ /* write accesses never go through the I mmu. */
+ res->bf_vec = vect_base + 3;
+ } else if (rw == 2 && cfg_x && !tlb_x) {
+ D(printf ("tlb: exec protected %x lo=%x pc=%x\n",
+ vaddr, lo, env->pc));
+ match = 0;
+ res->bf_vec = vect_base + 3;
+ } else if (cfg_v && !tlb_v) {
+ D(printf ("tlb: invalid %x\n", vaddr));
+ match = 0;
+ res->bf_vec = vect_base + 1;
+ }
+
+ res->prot = 0;
+ if (match) {
+ res->prot |= PAGE_READ;
+ if (tlb_w)
+ res->prot |= PAGE_WRITE;
+ if (mmu == 0 && (cfg_x || tlb_x))
+ res->prot |= PAGE_EXEC;
+ }
+ else
+ D(dump_tlb(env, mmu));
+ } else {
+ /* If refill, provide a randomized set. */
+ set = env->mmu_rand_lfsr & 3;
+ }
+
+ if (!match && !debug) {
+ cris_mmu_update_rand_lfsr(env);
+
+ /* Compute index. */
+ idx = vpage & 15;
+
+ /* Update RW_MM_TLB_SEL. */
+ env->sregs[SFR_RW_MM_TLB_SEL] = 0;
+ set_field(&env->sregs[SFR_RW_MM_TLB_SEL], idx, 0, 4);
+ set_field(&env->sregs[SFR_RW_MM_TLB_SEL], set, 4, 2);
+
+ /* Update RW_MM_CAUSE. */
+ set_field(&r_cause, rwcause, 8, 2);
+ set_field(&r_cause, vpage, 13, 19);
+ set_field(&r_cause, pid, 0, 8);
+ env->sregs[SFR_R_MM_CAUSE] = r_cause;
+ D(printf("refill vaddr=%x pc=%x\n", vaddr, env->pc));
+ }
+
+ D(printf ("%s rw=%d mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x"
+ " %x cause=%x sel=%x sp=%x %x %x\n",
+ __func__, rw, match, env->pc,
+ vaddr, vpage,
+ tlb_vpn, tlb_pfn, tlb_pid,
+ pid,
+ r_cause,
+ env->sregs[SFR_RW_MM_TLB_SEL],
+ env->regs[R_SP], env->pregs[PR_USP], env->ksp));
+
+ res->phy = tlb_pfn << TARGET_PAGE_BITS;
+ return !match;
+}
+
+void cris_mmu_flush_pid(CPUState *env, uint32_t pid)
+{
+ target_ulong vaddr;
+ unsigned int idx;
+ uint32_t lo, hi;
+ uint32_t tlb_vpn;
+ int tlb_pid, tlb_g, tlb_v;
+ unsigned int set;
+ unsigned int mmu;
+
+ pid &= 0xff;
+ for (mmu = 0; mmu < 2; mmu++) {
+ for (set = 0; set < 4; set++)
+ {
+ for (idx = 0; idx < 16; idx++) {
+ lo = env->tlbsets[mmu][set][idx].lo;
+ hi = env->tlbsets[mmu][set][idx].hi;
+
+ tlb_vpn = EXTRACT_FIELD(hi, 13, 31);
+ tlb_pid = EXTRACT_FIELD(hi, 0, 7);
+ tlb_g = EXTRACT_FIELD(lo, 4, 4);
+ tlb_v = EXTRACT_FIELD(lo, 3, 3);
+
+ if (tlb_v && !tlb_g && (tlb_pid == pid)) {
+ vaddr = tlb_vpn << TARGET_PAGE_BITS;
+ D_LOG("flush pid=%x vaddr=%x\n",
+ pid, vaddr);
+ tlb_flush_page(env, vaddr);
+ }
+ }
+ }
+ }
+}
+
+int cris_mmu_translate(struct cris_mmu_result *res,
+ CPUState *env, uint32_t vaddr,
+ int rw, int mmu_idx, int debug)
+{
+ int seg;
+ int miss = 0;
+ int is_user = mmu_idx == MMU_USER_IDX;
+ uint32_t old_srs;
+
+ old_srs= env->pregs[PR_SRS];
+
+ /* rw == 2 means exec, map the access to the insn mmu. */
+ env->pregs[PR_SRS] = rw == 2 ? 1 : 2;
+
+ if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) {
+ res->phy = vaddr;
+ res->prot = PAGE_BITS;
+ goto done;
+ }
+
+ seg = vaddr >> 28;
+ if (!is_user && cris_mmu_segmented_addr(seg, env->sregs[SFR_RW_MM_CFG]))
+ {
+ uint32_t base;
+
+ miss = 0;
+ base = cris_mmu_translate_seg(env, seg);
+ res->phy = base | (0x0fffffff & vaddr);
+ res->prot = PAGE_BITS;
+ } else {
+ miss = cris_mmu_translate_page(res, env, vaddr, rw,
+ is_user, debug);
+ }
+ done:
+ env->pregs[PR_SRS] = old_srs;
+ return miss;
+}
+#endif
diff --git a/target-cris/mmu.h b/target-cris/mmu.h
new file mode 100644
index 0000000..459d809
--- /dev/null
+++ b/target-cris/mmu.h
@@ -0,0 +1,17 @@
+#define CRIS_MMU_ERR_EXEC 0
+#define CRIS_MMU_ERR_READ 1
+#define CRIS_MMU_ERR_WRITE 2
+#define CRIS_MMU_ERR_FLUSH 3
+
+struct cris_mmu_result
+{
+ uint32_t phy;
+ int prot;
+ int bf_vec;
+};
+
+void cris_mmu_init(CPUState *env);
+void cris_mmu_flush_pid(CPUState *env, uint32_t pid);
+int cris_mmu_translate(struct cris_mmu_result *res,
+ CPUState *env, uint32_t vaddr,
+ int rw, int mmu_idx, int debug);
diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c
new file mode 100644
index 0000000..be9eb06
--- /dev/null
+++ b/target-cris/op_helper.c
@@ -0,0 +1,655 @@
+/*
+ * CRIS helper routines
+ *
+ * Copyright (c) 2007 AXIS Communications
+ * Written by Edgar E. Iglesias
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exec.h"
+#include "mmu.h"
+#include "helper.h"
+#include "host-utils.h"
+
+//#define CRIS_OP_HELPER_DEBUG
+
+
+#ifdef CRIS_OP_HELPER_DEBUG
+#define D(x) x
+#define D_LOG(...) qemu_log(__VA__ARGS__)
+#else
+#define D(x)
+#define D_LOG(...) do { } while (0)
+#endif
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* Try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+ D_LOG("%s pc=%x tpc=%x ra=%x\n", __func__,
+ env->pc, env->debug1, retaddr);
+ ret = cpu_cris_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, NULL);
+
+ /* Evaluate flags after retranslation. */
+ helper_top_evaluate_flags();
+ }
+ }
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+#endif
+
+void helper_raise_exception(uint32_t index)
+{
+ env->exception_index = index;
+ cpu_loop_exit();
+}
+
+void helper_tlb_flush_pid(uint32_t pid)
+{
+#if !defined(CONFIG_USER_ONLY)
+ pid &= 0xff;
+ if (pid != (env->pregs[PR_PID] & 0xff))
+ cris_mmu_flush_pid(env, env->pregs[PR_PID]);
+#endif
+}
+
+void helper_spc_write(uint32_t new_spc)
+{
+#if !defined(CONFIG_USER_ONLY)
+ tlb_flush_page(env, env->pregs[PR_SPC]);
+ tlb_flush_page(env, new_spc);
+#endif
+}
+
+void helper_dump(uint32_t a0, uint32_t a1, uint32_t a2)
+{
+ qemu_log("%s: a0=%x a1=%x\n", __func__, a0, a1);
+}
+
+/* Used by the tlb decoder. */
+#define EXTRACT_FIELD(src, start, end) \
+ (((src) >> start) & ((1 << (end - start + 1)) - 1))
+
+void helper_movl_sreg_reg (uint32_t sreg, uint32_t reg)
+{
+ uint32_t srs;
+ srs = env->pregs[PR_SRS];
+ srs &= 3;
+ env->sregs[srs][sreg] = env->regs[reg];
+
+#if !defined(CONFIG_USER_ONLY)
+ if (srs == 1 || srs == 2) {
+ if (sreg == 6) {
+ /* Writes to tlb-hi write to mm_cause as a side
+ effect. */
+ env->sregs[SFR_RW_MM_TLB_HI] = env->regs[reg];
+ env->sregs[SFR_R_MM_CAUSE] = env->regs[reg];
+ }
+ else if (sreg == 5) {
+ uint32_t set;
+ uint32_t idx;
+ uint32_t lo, hi;
+ uint32_t vaddr;
+ int tlb_v;
+
+ idx = set = env->sregs[SFR_RW_MM_TLB_SEL];
+ set >>= 4;
+ set &= 3;
+
+ idx &= 15;
+ /* We've just made a write to tlb_lo. */
+ lo = env->sregs[SFR_RW_MM_TLB_LO];
+ /* Writes are done via r_mm_cause. */
+ hi = env->sregs[SFR_R_MM_CAUSE];
+
+ vaddr = EXTRACT_FIELD(env->tlbsets[srs-1][set][idx].hi,
+ 13, 31);
+ vaddr <<= TARGET_PAGE_BITS;
+ tlb_v = EXTRACT_FIELD(env->tlbsets[srs-1][set][idx].lo,
+ 3, 3);
+ env->tlbsets[srs - 1][set][idx].lo = lo;
+ env->tlbsets[srs - 1][set][idx].hi = hi;
+
+ D_LOG("tlb flush vaddr=%x v=%d pc=%x\n",
+ vaddr, tlb_v, env->pc);
+ if (tlb_v) {
+ tlb_flush_page(env, vaddr);
+ }
+ }
+ }
+#endif
+}
+
+void helper_movl_reg_sreg (uint32_t reg, uint32_t sreg)
+{
+ uint32_t srs;
+ env->pregs[PR_SRS] &= 3;
+ srs = env->pregs[PR_SRS];
+
+#if !defined(CONFIG_USER_ONLY)
+ if (srs == 1 || srs == 2)
+ {
+ uint32_t set;
+ uint32_t idx;
+ uint32_t lo, hi;
+
+ idx = set = env->sregs[SFR_RW_MM_TLB_SEL];
+ set >>= 4;
+ set &= 3;
+ idx &= 15;
+
+ /* Update the mirror regs. */
+ hi = env->tlbsets[srs - 1][set][idx].hi;
+ lo = env->tlbsets[srs - 1][set][idx].lo;
+ env->sregs[SFR_RW_MM_TLB_HI] = hi;
+ env->sregs[SFR_RW_MM_TLB_LO] = lo;
+ }
+#endif
+ env->regs[reg] = env->sregs[srs][sreg];
+}
+
+static void cris_ccs_rshift(CPUState *env)
+{
+ uint32_t ccs;
+
+ /* Apply the ccs shift. */
+ ccs = env->pregs[PR_CCS];
+ ccs = (ccs & 0xc0000000) | ((ccs & 0x0fffffff) >> 10);
+ if (ccs & U_FLAG)
+ {
+ /* Enter user mode. */
+ env->ksp = env->regs[R_SP];
+ env->regs[R_SP] = env->pregs[PR_USP];
+ }
+
+ env->pregs[PR_CCS] = ccs;
+}
+
+void helper_rfe(void)
+{
+ int rflag = env->pregs[PR_CCS] & R_FLAG;
+
+ D_LOG("rfe: erp=%x pid=%x ccs=%x btarget=%x\n",
+ env->pregs[PR_ERP], env->pregs[PR_PID],
+ env->pregs[PR_CCS],
+ env->btarget);
+
+ cris_ccs_rshift(env);
+
+ /* RFE sets the P_FLAG only if the R_FLAG is not set. */
+ if (!rflag)
+ env->pregs[PR_CCS] |= P_FLAG;
+}
+
+void helper_rfn(void)
+{
+ int rflag = env->pregs[PR_CCS] & R_FLAG;
+
+ D_LOG("rfn: erp=%x pid=%x ccs=%x btarget=%x\n",
+ env->pregs[PR_ERP], env->pregs[PR_PID],
+ env->pregs[PR_CCS],
+ env->btarget);
+
+ cris_ccs_rshift(env);
+
+ /* Set the P_FLAG only if the R_FLAG is not set. */
+ if (!rflag)
+ env->pregs[PR_CCS] |= P_FLAG;
+
+ /* Always set the M flag. */
+ env->pregs[PR_CCS] |= M_FLAG;
+}
+
+uint32_t helper_lz(uint32_t t0)
+{
+ return clz32(t0);
+}
+
+uint32_t helper_btst(uint32_t t0, uint32_t t1, uint32_t ccs)
+{
+ /* FIXME: clean this up. */
+
+ /* des ref:
+ The N flag is set according to the selected bit in the dest reg.
+ The Z flag is set if the selected bit and all bits to the right are
+ zero.
+ The X flag is cleared.
+ Other flags are left untouched.
+ The destination reg is not affected.*/
+ unsigned int fz, sbit, bset, mask, masked_t0;
+
+ sbit = t1 & 31;
+ bset = !!(t0 & (1 << sbit));
+ mask = sbit == 31 ? -1 : (1 << (sbit + 1)) - 1;
+ masked_t0 = t0 & mask;
+ fz = !(masked_t0 | bset);
+
+ /* Clear the X, N and Z flags. */
+ ccs = ccs & ~(X_FLAG | N_FLAG | Z_FLAG);
+ if (env->pregs[PR_VR] < 32)
+ ccs &= ~(V_FLAG | C_FLAG);
+ /* Set the N and Z flags accordingly. */
+ ccs |= (bset << 3) | (fz << 2);
+ return ccs;
+}
+
+static inline uint32_t evaluate_flags_writeback(uint32_t flags, uint32_t ccs)
+{
+ unsigned int x, z, mask;
+
+ /* Extended arithmetics, leave the z flag alone. */
+ x = env->cc_x;
+ mask = env->cc_mask | X_FLAG;
+ if (x) {
+ z = flags & Z_FLAG;
+ mask = mask & ~z;
+ }
+ flags &= mask;
+
+ /* all insn clear the x-flag except setf or clrf. */
+ ccs &= ~mask;
+ ccs |= flags;
+ return ccs;
+}
+
+uint32_t helper_evaluate_flags_muls(uint32_t ccs, uint32_t res, uint32_t mof)
+{
+ uint32_t flags = 0;
+ int64_t tmp;
+ int dneg;
+
+ dneg = ((int32_t)res) < 0;
+
+ tmp = mof;
+ tmp <<= 32;
+ tmp |= res;
+ if (tmp == 0)
+ flags |= Z_FLAG;
+ else if (tmp < 0)
+ flags |= N_FLAG;
+ if ((dneg && mof != -1)
+ || (!dneg && mof != 0))
+ flags |= V_FLAG;
+ return evaluate_flags_writeback(flags, ccs);
+}
+
+uint32_t helper_evaluate_flags_mulu(uint32_t ccs, uint32_t res, uint32_t mof)
+{
+ uint32_t flags = 0;
+ uint64_t tmp;
+
+ tmp = mof;
+ tmp <<= 32;
+ tmp |= res;
+ if (tmp == 0)
+ flags |= Z_FLAG;
+ else if (tmp >> 63)
+ flags |= N_FLAG;
+ if (mof)
+ flags |= V_FLAG;
+
+ return evaluate_flags_writeback(flags, ccs);
+}
+
+uint32_t helper_evaluate_flags_mcp(uint32_t ccs,
+ uint32_t src, uint32_t dst, uint32_t res)
+{
+ uint32_t flags = 0;
+
+ src = src & 0x80000000;
+ dst = dst & 0x80000000;
+
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (!src && !dst)
+ flags |= V_FLAG;
+ else if (src & dst)
+ flags |= R_FLAG;
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (src & dst)
+ flags |= V_FLAG;
+ if (dst | src)
+ flags |= R_FLAG;
+ }
+
+ return evaluate_flags_writeback(flags, ccs);
+}
+
+uint32_t helper_evaluate_flags_alu_4(uint32_t ccs,
+ uint32_t src, uint32_t dst, uint32_t res)
+{
+ uint32_t flags = 0;
+
+ src = src & 0x80000000;
+ dst = dst & 0x80000000;
+
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (!src && !dst)
+ flags |= V_FLAG;
+ else if (src & dst)
+ flags |= C_FLAG;
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (src & dst)
+ flags |= V_FLAG;
+ if (dst | src)
+ flags |= C_FLAG;
+ }
+
+ return evaluate_flags_writeback(flags, ccs);
+}
+
+uint32_t helper_evaluate_flags_sub_4(uint32_t ccs,
+ uint32_t src, uint32_t dst, uint32_t res)
+{
+ uint32_t flags = 0;
+
+ src = (~src) & 0x80000000;
+ dst = dst & 0x80000000;
+
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (!src && !dst)
+ flags |= V_FLAG;
+ else if (src & dst)
+ flags |= C_FLAG;
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (src & dst)
+ flags |= V_FLAG;
+ if (dst | src)
+ flags |= C_FLAG;
+ }
+
+ flags ^= C_FLAG;
+ return evaluate_flags_writeback(flags, ccs);
+}
+
+uint32_t helper_evaluate_flags_move_4(uint32_t ccs, uint32_t res)
+{
+ uint32_t flags = 0;
+
+ if ((int32_t)res < 0)
+ flags |= N_FLAG;
+ else if (res == 0L)
+ flags |= Z_FLAG;
+
+ return evaluate_flags_writeback(flags, ccs);
+}
+uint32_t helper_evaluate_flags_move_2(uint32_t ccs, uint32_t res)
+{
+ uint32_t flags = 0;
+
+ if ((int16_t)res < 0L)
+ flags |= N_FLAG;
+ else if (res == 0)
+ flags |= Z_FLAG;
+
+ return evaluate_flags_writeback(flags, ccs);
+}
+
+/* TODO: This is expensive. We could split things up and only evaluate part of
+ CCR on a need to know basis. For now, we simply re-evaluate everything. */
+void helper_evaluate_flags(void)
+{
+ uint32_t src, dst, res;
+ uint32_t flags = 0;
+
+ src = env->cc_src;
+ dst = env->cc_dest;
+ res = env->cc_result;
+
+ if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP)
+ src = ~src;
+
+ /* Now, evaluate the flags. This stuff is based on
+ Per Zander's CRISv10 simulator. */
+ switch (env->cc_size)
+ {
+ case 1:
+ if ((res & 0x80L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x80L) == 0L)
+ && ((dst & 0x80L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x80L) != 0L)
+ && ((dst & 0x80L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if ((res & 0xFFL) == 0L)
+ {
+ flags |= Z_FLAG;
+ }
+ if (((src & 0x80L) != 0L)
+ && ((dst & 0x80L) != 0L))
+ {
+ flags |= V_FLAG;
+ }
+ if ((dst & 0x80L) != 0L
+ || (src & 0x80L) != 0L)
+ {
+ flags |= C_FLAG;
+ }
+ }
+ break;
+ case 2:
+ if ((res & 0x8000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x8000L) == 0L)
+ && ((dst & 0x8000L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x8000L) != 0L)
+ && ((dst & 0x8000L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if ((res & 0xFFFFL) == 0L)
+ {
+ flags |= Z_FLAG;
+ }
+ if (((src & 0x8000L) != 0L)
+ && ((dst & 0x8000L) != 0L))
+ {
+ flags |= V_FLAG;
+ }
+ if ((dst & 0x8000L) != 0L
+ || (src & 0x8000L) != 0L)
+ {
+ flags |= C_FLAG;
+ }
+ }
+ break;
+ case 4:
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x80000000L) == 0L)
+ && ((dst & 0x80000000L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x80000000L) != 0L) &&
+ ((dst & 0x80000000L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (((src & 0x80000000L) != 0L)
+ && ((dst & 0x80000000L) != 0L))
+ flags |= V_FLAG;
+ if ((dst & 0x80000000L) != 0L
+ || (src & 0x80000000L) != 0L)
+ flags |= C_FLAG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP)
+ flags ^= C_FLAG;
+
+ env->pregs[PR_CCS] = evaluate_flags_writeback(flags, env->pregs[PR_CCS]);
+}
+
+void helper_top_evaluate_flags(void)
+{
+ switch (env->cc_op)
+ {
+ case CC_OP_MCP:
+ env->pregs[PR_CCS] = helper_evaluate_flags_mcp(
+ env->pregs[PR_CCS], env->cc_src,
+ env->cc_dest, env->cc_result);
+ break;
+ case CC_OP_MULS:
+ env->pregs[PR_CCS] = helper_evaluate_flags_muls(
+ env->pregs[PR_CCS], env->cc_result,
+ env->pregs[PR_MOF]);
+ break;
+ case CC_OP_MULU:
+ env->pregs[PR_CCS] = helper_evaluate_flags_mulu(
+ env->pregs[PR_CCS], env->cc_result,
+ env->pregs[PR_MOF]);
+ break;
+ case CC_OP_MOVE:
+ case CC_OP_AND:
+ case CC_OP_OR:
+ case CC_OP_XOR:
+ case CC_OP_ASR:
+ case CC_OP_LSR:
+ case CC_OP_LSL:
+ switch (env->cc_size)
+ {
+ case 4:
+ env->pregs[PR_CCS] =
+ helper_evaluate_flags_move_4(
+ env->pregs[PR_CCS],
+ env->cc_result);
+ break;
+ case 2:
+ env->pregs[PR_CCS] =
+ helper_evaluate_flags_move_2(
+ env->pregs[PR_CCS],
+ env->cc_result);
+ break;
+ default:
+ helper_evaluate_flags();
+ break;
+ }
+ break;
+ case CC_OP_FLAGS:
+ /* live. */
+ break;
+ case CC_OP_SUB:
+ case CC_OP_CMP:
+ if (env->cc_size == 4)
+ env->pregs[PR_CCS] =
+ helper_evaluate_flags_sub_4(
+ env->pregs[PR_CCS],
+ env->cc_src, env->cc_dest,
+ env->cc_result);
+ else
+ helper_evaluate_flags();
+ break;
+ default:
+ {
+ switch (env->cc_size)
+ {
+ case 4:
+ env->pregs[PR_CCS] =
+ helper_evaluate_flags_alu_4(
+ env->pregs[PR_CCS],
+ env->cc_src, env->cc_dest,
+ env->cc_result);
+ break;
+ default:
+ helper_evaluate_flags();
+ break;
+ }
+ }
+ break;
+ }
+}
diff --git a/target-cris/opcode-cris.h b/target-cris/opcode-cris.h
new file mode 100644
index 0000000..8d4749d
--- /dev/null
+++ b/target-cris/opcode-cris.h
@@ -0,0 +1,365 @@
+/* cris.h -- Header file for CRIS opcode and register tables.
+ Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc.
+ Contributed by Axis Communications AB, Lund, Sweden.
+ Originally written for GAS 1.38.1 by Mikael Asker.
+ Updated, BFDized and GNUified by Hans-Peter Nilsson.
+
+This file is part of GAS, GDB and the GNU binutils.
+
+GAS, GDB, and GNU binutils is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2, or (at your
+option) any later version.
+
+GAS, GDB, and GNU binutils are distributed in the hope that they will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU 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/>. */
+
+#ifndef __CRIS_H_INCLUDED_
+#define __CRIS_H_INCLUDED_
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+
+/* Registers. */
+#define MAX_REG (15)
+#define REG_SP (14)
+#define REG_PC (15)
+
+/* CPU version control of disassembly and assembly of instructions.
+ May affect how the instruction is assembled, at least the size of
+ immediate operands. */
+enum cris_insn_version_usage
+{
+ /* Any version. */
+ cris_ver_version_all=0,
+
+ /* Indeterminate (intended for disassembly only, or obsolete). */
+ cris_ver_warning,
+
+ /* Only for v0..3 (Etrax 1..4). */
+ cris_ver_v0_3,
+
+ /* Only for v3 or higher (ETRAX 4 and beyond). */
+ cris_ver_v3p,
+
+ /* Only for v8 (Etrax 100). */
+ cris_ver_v8,
+
+ /* Only for v8 or higher (ETRAX 100, ETRAX 100 LX). */
+ cris_ver_v8p,
+
+ /* Only for v0..10. FIXME: Not sure what to do with this. */
+ cris_ver_sim_v0_10,
+
+ /* Only for v0..10. */
+ cris_ver_v0_10,
+
+ /* Only for v3..10. (ETRAX 4, ETRAX 100 and ETRAX 100 LX). */
+ cris_ver_v3_10,
+
+ /* Only for v8..10 (ETRAX 100 and ETRAX 100 LX). */
+ cris_ver_v8_10,
+
+ /* Only for v10 (ETRAX 100 LX) and same series. */
+ cris_ver_v10,
+
+ /* Only for v10 (ETRAX 100 LX) and same series. */
+ cris_ver_v10p,
+
+ /* Only for v32 or higher (codename GUINNESS).
+ Of course some or all these of may change to cris_ver_v32p if/when
+ there's a new revision. */
+ cris_ver_v32p
+};
+
+
+/* Special registers. */
+struct cris_spec_reg
+{
+ const char *const name;
+ unsigned int number;
+
+ /* The size of the register. */
+ unsigned int reg_size;
+
+ /* What CPU version the special register of that name is implemented
+ in. If cris_ver_warning, emit an unimplemented-warning. */
+ enum cris_insn_version_usage applicable_version;
+
+ /* There might be a specific warning for using a special register
+ here. */
+ const char *const warning;
+};
+extern const struct cris_spec_reg cris_spec_regs[];
+
+
+/* Support registers (kind of special too, but not named as such). */
+struct cris_support_reg
+{
+ const char *const name;
+ unsigned int number;
+};
+extern const struct cris_support_reg cris_support_regs[];
+
+struct cris_cond15
+{
+ /* The name of the condition. */
+ const char *const name;
+
+ /* What CPU version this condition name applies to. */
+ enum cris_insn_version_usage applicable_version;
+};
+extern const struct cris_cond15 cris_conds15[];
+
+/* Opcode-dependent constants. */
+#define AUTOINCR_BIT (0x04)
+
+/* Prefixes. */
+#define BDAP_QUICK_OPCODE (0x0100)
+#define BDAP_QUICK_Z_BITS (0x0e00)
+
+#define BIAP_OPCODE (0x0540)
+#define BIAP_Z_BITS (0x0a80)
+
+#define DIP_OPCODE (0x0970)
+#define DIP_Z_BITS (0xf280)
+
+#define BDAP_INDIR_LOW (0x40)
+#define BDAP_INDIR_LOW_Z (0x80)
+#define BDAP_INDIR_HIGH (0x09)
+#define BDAP_INDIR_HIGH_Z (0x02)
+
+#define BDAP_INDIR_OPCODE (BDAP_INDIR_HIGH * 0x0100 + BDAP_INDIR_LOW)
+#define BDAP_INDIR_Z_BITS (BDAP_INDIR_HIGH_Z * 0x100 + BDAP_INDIR_LOW_Z)
+#define BDAP_PC_LOW (BDAP_INDIR_LOW + REG_PC)
+#define BDAP_INCR_HIGH (BDAP_INDIR_HIGH + AUTOINCR_BIT)
+
+/* No prefix must have this code for its "match" bits in the
+ opcode-table. "BCC .+2" will do nicely. */
+#define NO_CRIS_PREFIX 0
+
+/* Definitions for condition codes. */
+#define CC_CC 0x0
+#define CC_HS 0x0
+#define CC_CS 0x1
+#define CC_LO 0x1
+#define CC_NE 0x2
+#define CC_EQ 0x3
+#define CC_VC 0x4
+#define CC_VS 0x5
+#define CC_PL 0x6
+#define CC_MI 0x7
+#define CC_LS 0x8
+#define CC_HI 0x9
+#define CC_GE 0xA
+#define CC_LT 0xB
+#define CC_GT 0xC
+#define CC_LE 0xD
+#define CC_A 0xE
+#define CC_EXT 0xF
+
+/* A table of strings "cc", "cs"... indexed with condition code
+ values as above. */
+extern const char *const cris_cc_strings[];
+
+/* Bcc quick. */
+#define BRANCH_QUICK_LOW (0)
+#define BRANCH_QUICK_HIGH (0)
+#define BRANCH_QUICK_OPCODE (BRANCH_QUICK_HIGH * 0x0100 + BRANCH_QUICK_LOW)
+#define BRANCH_QUICK_Z_BITS (0x0F00)
+
+/* BA quick. */
+#define BA_QUICK_HIGH (BRANCH_QUICK_HIGH + CC_A * 0x10)
+#define BA_QUICK_OPCODE (BA_QUICK_HIGH * 0x100 + BRANCH_QUICK_LOW)
+
+/* Bcc [PC+]. */
+#define BRANCH_PC_LOW (0xFF)
+#define BRANCH_INCR_HIGH (0x0D)
+#define BA_PC_INCR_OPCODE \
+ ((BRANCH_INCR_HIGH + CC_A * 0x10) * 0x0100 + BRANCH_PC_LOW)
+
+/* Jump. */
+/* Note that old versions generated special register 8 (in high bits)
+ and not-that-old versions recognized it as a jump-instruction.
+ That opcode now belongs to JUMPU. */
+#define JUMP_INDIR_OPCODE (0x0930)
+#define JUMP_INDIR_Z_BITS (0xf2c0)
+#define JUMP_PC_INCR_OPCODE \
+ (JUMP_INDIR_OPCODE + AUTOINCR_BIT * 0x0100 + REG_PC)
+
+#define MOVE_M_TO_PREG_OPCODE 0x0a30
+#define MOVE_M_TO_PREG_ZBITS 0x01c0
+
+/* BDAP.D N,PC. */
+#define MOVE_PC_INCR_OPCODE_PREFIX \
+ (((BDAP_INCR_HIGH | (REG_PC << 4)) << 8) | BDAP_PC_LOW | (2 << 4))
+#define MOVE_PC_INCR_OPCODE_SUFFIX \
+ (MOVE_M_TO_PREG_OPCODE | REG_PC | (AUTOINCR_BIT << 8))
+
+#define JUMP_PC_INCR_OPCODE_V32 (0x0DBF)
+
+/* BA DWORD (V32). */
+#define BA_DWORD_OPCODE (0x0EBF)
+
+/* Nop. */
+#define NOP_OPCODE (0x050F)
+#define NOP_Z_BITS (0xFFFF ^ NOP_OPCODE)
+
+#define NOP_OPCODE_V32 (0x05B0)
+#define NOP_Z_BITS_V32 (0xFFFF ^ NOP_OPCODE_V32)
+
+/* For the compatibility mode, let's use "MOVE R0,P0". Doesn't affect
+ registers or flags. Unfortunately shuts off interrupts for one cycle
+ for < v32, but there doesn't seem to be any alternative without that
+ effect. */
+#define NOP_OPCODE_COMMON (0x630)
+#define NOP_OPCODE_ZBITS_COMMON (0xffff & ~NOP_OPCODE_COMMON)
+
+/* LAPC.D */
+#define LAPC_DWORD_OPCODE (0x0D7F)
+#define LAPC_DWORD_Z_BITS (0x0fff & ~LAPC_DWORD_OPCODE)
+
+/* Structure of an opcode table entry. */
+enum cris_imm_oprnd_size_type
+{
+ /* No size is applicable. */
+ SIZE_NONE,
+
+ /* Always 32 bits. */
+ SIZE_FIX_32,
+
+ /* Indicated by size of special register. */
+ SIZE_SPEC_REG,
+
+ /* Indicated by size field, signed. */
+ SIZE_FIELD_SIGNED,
+
+ /* Indicated by size field, unsigned. */
+ SIZE_FIELD_UNSIGNED,
+
+ /* Indicated by size field, no sign implied. */
+ SIZE_FIELD
+};
+
+/* For GDB. FIXME: Is this the best way to handle opcode
+ interpretation? */
+enum cris_op_type
+{
+ cris_not_implemented_op = 0,
+ cris_abs_op,
+ cris_addi_op,
+ cris_asr_op,
+ cris_asrq_op,
+ cris_ax_ei_setf_op,
+ cris_bdap_prefix,
+ cris_biap_prefix,
+ cris_break_op,
+ cris_btst_nop_op,
+ cris_clearf_di_op,
+ cris_dip_prefix,
+ cris_dstep_logshift_mstep_neg_not_op,
+ cris_eight_bit_offset_branch_op,
+ cris_move_mem_to_reg_movem_op,
+ cris_move_reg_to_mem_movem_op,
+ cris_move_to_preg_op,
+ cris_muls_op,
+ cris_mulu_op,
+ cris_none_reg_mode_add_sub_cmp_and_or_move_op,
+ cris_none_reg_mode_clear_test_op,
+ cris_none_reg_mode_jump_op,
+ cris_none_reg_mode_move_from_preg_op,
+ cris_quick_mode_add_sub_op,
+ cris_quick_mode_and_cmp_move_or_op,
+ cris_quick_mode_bdap_prefix,
+ cris_reg_mode_add_sub_cmp_and_or_move_op,
+ cris_reg_mode_clear_op,
+ cris_reg_mode_jump_op,
+ cris_reg_mode_move_from_preg_op,
+ cris_reg_mode_test_op,
+ cris_scc_op,
+ cris_sixteen_bit_offset_branch_op,
+ cris_three_operand_add_sub_cmp_and_or_op,
+ cris_three_operand_bound_op,
+ cris_two_operand_bound_op,
+ cris_xor_op
+};
+
+struct cris_opcode
+{
+ /* The name of the insn. */
+ const char *name;
+
+ /* Bits that must be 1 for a match. */
+ unsigned int match;
+
+ /* Bits that must be 0 for a match. */
+ unsigned int lose;
+
+ /* See the table in "opcodes/cris-opc.c". */
+ const char *args;
+
+ /* Nonzero if this is a delayed branch instruction. */
+ char delayed;
+
+ /* Size of immediate operands. */
+ enum cris_imm_oprnd_size_type imm_oprnd_size;
+
+ /* Indicates which version this insn was first implemented in. */
+ enum cris_insn_version_usage applicable_version;
+
+ /* What kind of operation this is. */
+ enum cris_op_type op;
+};
+extern const struct cris_opcode cris_opcodes[];
+
+
+/* These macros are for the target-specific flags in disassemble_info
+ used at disassembly. */
+
+/* This insn accesses memory. This flag is more trustworthy than
+ checking insn_type for "dis_dref" which does not work for
+ e.g. "JSR [foo]". */
+#define CRIS_DIS_FLAG_MEMREF (1 << 0)
+
+/* The "target" field holds a register number. */
+#define CRIS_DIS_FLAG_MEM_TARGET_IS_REG (1 << 1)
+
+/* The "target2" field holds a register number; add it to "target". */
+#define CRIS_DIS_FLAG_MEM_TARGET2_IS_REG (1 << 2)
+
+/* Yet another add-on: the register in "target2" must be multiplied
+ by 2 before adding to "target". */
+#define CRIS_DIS_FLAG_MEM_TARGET2_MULT2 (1 << 3)
+
+/* Yet another add-on: the register in "target2" must be multiplied
+ by 4 (mutually exclusive with .._MULT2). */
+#define CRIS_DIS_FLAG_MEM_TARGET2_MULT4 (1 << 4)
+
+/* The register in "target2" is an indirect memory reference (of the
+ register there), add to "target". Assumed size is dword (mutually
+ exclusive with .._MULT[24]). */
+#define CRIS_DIS_FLAG_MEM_TARGET2_MEM (1 << 5)
+
+/* Add-on to CRIS_DIS_FLAG_MEM_TARGET2_MEM; the memory access is "byte";
+ sign-extended before adding to "target". */
+#define CRIS_DIS_FLAG_MEM_TARGET2_MEM_BYTE (1 << 6)
+
+/* Add-on to CRIS_DIS_FLAG_MEM_TARGET2_MEM; the memory access is "word";
+ sign-extended before adding to "target". */
+#define CRIS_DIS_FLAG_MEM_TARGET2_MEM_WORD (1 << 7)
+
+#endif /* __CRIS_H_INCLUDED_ */
+
+/*
+ * Local variables:
+ * eval: (c-set-style "gnu")
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/target-cris/translate.c b/target-cris/translate.c
new file mode 100644
index 0000000..b4648a0
--- /dev/null
+++ b/target-cris/translate.c
@@ -0,0 +1,3611 @@
+/*
+ * CRIS emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2008 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * 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/>.
+ */
+
+/*
+ * FIXME:
+ * The condition code translation is in need of attention.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "helper.h"
+#include "mmu.h"
+#include "crisv32-decode.h"
+#include "qemu-common.h"
+
+#define GEN_HELPER 1
+#include "helper.h"
+
+#define DISAS_CRIS 0
+#if DISAS_CRIS
+# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+# define LOG_DIS(...) do { } while (0)
+#endif
+
+#define D(x)
+#define BUG() (gen_BUG(dc, __FILE__, __LINE__))
+#define BUG_ON(x) ({if (x) BUG();})
+
+#define DISAS_SWI 5
+
+/* Used by the decoder. */
+#define EXTRACT_FIELD(src, start, end) \
+ (((src) >> start) & ((1 << (end - start + 1)) - 1))
+
+#define CC_MASK_NZ 0xc
+#define CC_MASK_NZV 0xe
+#define CC_MASK_NZVC 0xf
+#define CC_MASK_RNZV 0x10e
+
+static TCGv_ptr cpu_env;
+static TCGv cpu_R[16];
+static TCGv cpu_PR[16];
+static TCGv cc_x;
+static TCGv cc_src;
+static TCGv cc_dest;
+static TCGv cc_result;
+static TCGv cc_op;
+static TCGv cc_size;
+static TCGv cc_mask;
+
+static TCGv env_btaken;
+static TCGv env_btarget;
+static TCGv env_pc;
+
+#include "gen-icount.h"
+
+/* This is the state at translation time. */
+typedef struct DisasContext {
+ CPUState *env;
+ target_ulong pc, ppc;
+
+ /* Decoder. */
+ unsigned int (*decoder)(struct DisasContext *dc);
+ uint32_t ir;
+ uint32_t opcode;
+ unsigned int op1;
+ unsigned int op2;
+ unsigned int zsize, zzsize;
+ unsigned int mode;
+ unsigned int postinc;
+
+ unsigned int size;
+ unsigned int src;
+ unsigned int dst;
+ unsigned int cond;
+
+ int update_cc;
+ int cc_op;
+ int cc_size;
+ uint32_t cc_mask;
+
+ int cc_size_uptodate; /* -1 invalid or last written value. */
+
+ int cc_x_uptodate; /* 1 - ccs, 2 - known | X_FLAG. 0 not uptodate. */
+ int flags_uptodate; /* Wether or not $ccs is uptodate. */
+ int flagx_known; /* Wether or not flags_x has the x flag known at
+ translation time. */
+ int flags_x;
+
+ int clear_x; /* Clear x after this insn? */
+ int clear_prefix; /* Clear prefix after this insn? */
+ int clear_locked_irq; /* Clear the irq lockout. */
+ int cpustate_changed;
+ unsigned int tb_flags; /* tb dependent flags. */
+ int is_jmp;
+
+#define JMP_NOJMP 0
+#define JMP_DIRECT 1
+#define JMP_DIRECT_CC 2
+#define JMP_INDIRECT 3
+ int jmp; /* 0=nojmp, 1=direct, 2=indirect. */
+ uint32_t jmp_pc;
+
+ int delayed_branch;
+
+ struct TranslationBlock *tb;
+ int singlestep_enabled;
+} DisasContext;
+
+static void gen_BUG(DisasContext *dc, const char *file, int line)
+{
+ printf ("BUG: pc=%x %s %d\n", dc->pc, file, line);
+ qemu_log("BUG: pc=%x %s %d\n", dc->pc, file, line);
+ cpu_abort(dc->env, "%s:%d\n", file, line);
+}
+
+static const char *regnames[] =
+{
+ "$r0", "$r1", "$r2", "$r3",
+ "$r4", "$r5", "$r6", "$r7",
+ "$r8", "$r9", "$r10", "$r11",
+ "$r12", "$r13", "$sp", "$acr",
+};
+static const char *pregnames[] =
+{
+ "$bz", "$vr", "$pid", "$srs",
+ "$wz", "$exs", "$eda", "$mof",
+ "$dz", "$ebp", "$erp", "$srp",
+ "$nrp", "$ccs", "$usp", "$spc",
+};
+
+/* We need this table to handle preg-moves with implicit width. */
+static int preg_sizes[] = {
+ 1, /* bz. */
+ 1, /* vr. */
+ 4, /* pid. */
+ 1, /* srs. */
+ 2, /* wz. */
+ 4, 4, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+};
+
+#define t_gen_mov_TN_env(tn, member) \
+ _t_gen_mov_TN_env((tn), offsetof(CPUState, member))
+#define t_gen_mov_env_TN(member, tn) \
+ _t_gen_mov_env_TN(offsetof(CPUState, member), (tn))
+
+static inline void t_gen_mov_TN_reg(TCGv tn, int r)
+{
+ if (r < 0 || r > 15)
+ fprintf(stderr, "wrong register read $r%d\n", r);
+ tcg_gen_mov_tl(tn, cpu_R[r]);
+}
+static inline void t_gen_mov_reg_TN(int r, TCGv tn)
+{
+ if (r < 0 || r > 15)
+ fprintf(stderr, "wrong register write $r%d\n", r);
+ tcg_gen_mov_tl(cpu_R[r], tn);
+}
+
+static inline void _t_gen_mov_TN_env(TCGv tn, int offset)
+{
+ if (offset > sizeof (CPUState))
+ fprintf(stderr, "wrong load from env from off=%d\n", offset);
+ tcg_gen_ld_tl(tn, cpu_env, offset);
+}
+static inline void _t_gen_mov_env_TN(int offset, TCGv tn)
+{
+ if (offset > sizeof (CPUState))
+ fprintf(stderr, "wrong store to env at off=%d\n", offset);
+ tcg_gen_st_tl(tn, cpu_env, offset);
+}
+
+static inline void t_gen_mov_TN_preg(TCGv tn, int r)
+{
+ if (r < 0 || r > 15)
+ fprintf(stderr, "wrong register read $p%d\n", r);
+ if (r == PR_BZ || r == PR_WZ || r == PR_DZ)
+ tcg_gen_mov_tl(tn, tcg_const_tl(0));
+ else if (r == PR_VR)
+ tcg_gen_mov_tl(tn, tcg_const_tl(32));
+ else
+ tcg_gen_mov_tl(tn, cpu_PR[r]);
+}
+static inline void t_gen_mov_preg_TN(DisasContext *dc, int r, TCGv tn)
+{
+ if (r < 0 || r > 15)
+ fprintf(stderr, "wrong register write $p%d\n", r);
+ if (r == PR_BZ || r == PR_WZ || r == PR_DZ)
+ return;
+ else if (r == PR_SRS)
+ tcg_gen_andi_tl(cpu_PR[r], tn, 3);
+ else {
+ if (r == PR_PID)
+ gen_helper_tlb_flush_pid(tn);
+ if (dc->tb_flags & S_FLAG && r == PR_SPC)
+ gen_helper_spc_write(tn);
+ else if (r == PR_CCS)
+ dc->cpustate_changed = 1;
+ tcg_gen_mov_tl(cpu_PR[r], tn);
+ }
+}
+
+/* Sign extend at translation time. */
+static int sign_extend(unsigned int val, unsigned int width)
+{
+ int sval;
+
+ /* LSL. */
+ val <<= 31 - width;
+ sval = val;
+ /* ASR. */
+ sval >>= 31 - width;
+ return sval;
+}
+
+static int cris_fetch(DisasContext *dc, uint32_t addr,
+ unsigned int size, unsigned int sign)
+{
+ int r;
+
+ switch (size) {
+ case 4:
+ {
+ r = ldl_code(addr);
+ break;
+ }
+ case 2:
+ {
+ if (sign) {
+ r = ldsw_code(addr);
+ } else {
+ r = lduw_code(addr);
+ }
+ break;
+ }
+ case 1:
+ {
+ if (sign) {
+ r = ldsb_code(addr);
+ } else {
+ r = ldub_code(addr);
+ }
+ break;
+ }
+ default:
+ cpu_abort(dc->env, "Invalid fetch size %d\n", size);
+ break;
+ }
+ return r;
+}
+
+static void cris_lock_irq(DisasContext *dc)
+{
+ dc->clear_locked_irq = 0;
+ t_gen_mov_env_TN(locked_irq, tcg_const_tl(1));
+}
+
+static inline void t_gen_raise_exception(uint32_t index)
+{
+ TCGv_i32 tmp = tcg_const_i32(index);
+ gen_helper_raise_exception(tmp);
+ tcg_temp_free_i32(tmp);
+}
+
+static void t_gen_lsl(TCGv d, TCGv a, TCGv b)
+{
+ TCGv t0, t_31;
+
+ t0 = tcg_temp_new();
+ t_31 = tcg_const_tl(31);
+ tcg_gen_shl_tl(d, a, b);
+
+ tcg_gen_sub_tl(t0, t_31, b);
+ tcg_gen_sar_tl(t0, t0, t_31);
+ tcg_gen_and_tl(t0, t0, d);
+ tcg_gen_xor_tl(d, d, t0);
+ tcg_temp_free(t0);
+ tcg_temp_free(t_31);
+}
+
+static void t_gen_lsr(TCGv d, TCGv a, TCGv b)
+{
+ TCGv t0, t_31;
+
+ t0 = tcg_temp_new();
+ t_31 = tcg_temp_new();
+ tcg_gen_shr_tl(d, a, b);
+
+ tcg_gen_movi_tl(t_31, 31);
+ tcg_gen_sub_tl(t0, t_31, b);
+ tcg_gen_sar_tl(t0, t0, t_31);
+ tcg_gen_and_tl(t0, t0, d);
+ tcg_gen_xor_tl(d, d, t0);
+ tcg_temp_free(t0);
+ tcg_temp_free(t_31);
+}
+
+static void t_gen_asr(TCGv d, TCGv a, TCGv b)
+{
+ TCGv t0, t_31;
+
+ t0 = tcg_temp_new();
+ t_31 = tcg_temp_new();
+ tcg_gen_sar_tl(d, a, b);
+
+ tcg_gen_movi_tl(t_31, 31);
+ tcg_gen_sub_tl(t0, t_31, b);
+ tcg_gen_sar_tl(t0, t0, t_31);
+ tcg_gen_or_tl(d, d, t0);
+ tcg_temp_free(t0);
+ tcg_temp_free(t_31);
+}
+
+/* 64-bit signed mul, lower result in d and upper in d2. */
+static void t_gen_muls(TCGv d, TCGv d2, TCGv a, TCGv b)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_i32_i64(t0, a);
+ tcg_gen_ext_i32_i64(t1, b);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_trunc_i64_i32(d, t0);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(d2, t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+/* 64-bit unsigned muls, lower result in d and upper in d2. */
+static void t_gen_mulu(TCGv d, TCGv d2, TCGv a, TCGv b)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(t0, a);
+ tcg_gen_extu_i32_i64(t1, b);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_trunc_i64_i32(d, t0);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(d2, t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+static void t_gen_cris_dstep(TCGv d, TCGv a, TCGv b)
+{
+ int l1;
+
+ l1 = gen_new_label();
+
+ /*
+ * d <<= 1
+ * if (d >= s)
+ * d -= s;
+ */
+ tcg_gen_shli_tl(d, a, 1);
+ tcg_gen_brcond_tl(TCG_COND_LTU, d, b, l1);
+ tcg_gen_sub_tl(d, d, b);
+ gen_set_label(l1);
+}
+
+static void t_gen_cris_mstep(TCGv d, TCGv a, TCGv b, TCGv ccs)
+{
+ TCGv t;
+
+ /*
+ * d <<= 1
+ * if (n)
+ * d += s;
+ */
+ t = tcg_temp_new();
+ tcg_gen_shli_tl(d, a, 1);
+ tcg_gen_shli_tl(t, ccs, 31 - 3);
+ tcg_gen_sari_tl(t, t, 31);
+ tcg_gen_and_tl(t, t, b);
+ tcg_gen_add_tl(d, d, t);
+ tcg_temp_free(t);
+}
+
+/* Extended arithmetics on CRIS. */
+static inline void t_gen_add_flag(TCGv d, int flag)
+{
+ TCGv c;
+
+ c = tcg_temp_new();
+ t_gen_mov_TN_preg(c, PR_CCS);
+ /* Propagate carry into d. */
+ tcg_gen_andi_tl(c, c, 1 << flag);
+ if (flag)
+ tcg_gen_shri_tl(c, c, flag);
+ tcg_gen_add_tl(d, d, c);
+ tcg_temp_free(c);
+}
+
+static inline void t_gen_addx_carry(DisasContext *dc, TCGv d)
+{
+ if (dc->flagx_known) {
+ if (dc->flags_x) {
+ TCGv c;
+
+ c = tcg_temp_new();
+ t_gen_mov_TN_preg(c, PR_CCS);
+ /* C flag is already at bit 0. */
+ tcg_gen_andi_tl(c, c, C_FLAG);
+ tcg_gen_add_tl(d, d, c);
+ tcg_temp_free(c);
+ }
+ } else {
+ TCGv x, c;
+
+ x = tcg_temp_new();
+ c = tcg_temp_new();
+ t_gen_mov_TN_preg(x, PR_CCS);
+ tcg_gen_mov_tl(c, x);
+
+ /* Propagate carry into d if X is set. Branch free. */
+ tcg_gen_andi_tl(c, c, C_FLAG);
+ tcg_gen_andi_tl(x, x, X_FLAG);
+ tcg_gen_shri_tl(x, x, 4);
+
+ tcg_gen_and_tl(x, x, c);
+ tcg_gen_add_tl(d, d, x);
+ tcg_temp_free(x);
+ tcg_temp_free(c);
+ }
+}
+
+static inline void t_gen_subx_carry(DisasContext *dc, TCGv d)
+{
+ if (dc->flagx_known) {
+ if (dc->flags_x) {
+ TCGv c;
+
+ c = tcg_temp_new();
+ t_gen_mov_TN_preg(c, PR_CCS);
+ /* C flag is already at bit 0. */
+ tcg_gen_andi_tl(c, c, C_FLAG);
+ tcg_gen_sub_tl(d, d, c);
+ tcg_temp_free(c);
+ }
+ } else {
+ TCGv x, c;
+
+ x = tcg_temp_new();
+ c = tcg_temp_new();
+ t_gen_mov_TN_preg(x, PR_CCS);
+ tcg_gen_mov_tl(c, x);
+
+ /* Propagate carry into d if X is set. Branch free. */
+ tcg_gen_andi_tl(c, c, C_FLAG);
+ tcg_gen_andi_tl(x, x, X_FLAG);
+ tcg_gen_shri_tl(x, x, 4);
+
+ tcg_gen_and_tl(x, x, c);
+ tcg_gen_sub_tl(d, d, x);
+ tcg_temp_free(x);
+ tcg_temp_free(c);
+ }
+}
+
+/* Swap the two bytes within each half word of the s operand.
+ T0 = ((T0 << 8) & 0xff00ff00) | ((T0 >> 8) & 0x00ff00ff) */
+static inline void t_gen_swapb(TCGv d, TCGv s)
+{
+ TCGv t, org_s;
+
+ t = tcg_temp_new();
+ org_s = tcg_temp_new();
+
+ /* d and s may refer to the same object. */
+ tcg_gen_mov_tl(org_s, s);
+ tcg_gen_shli_tl(t, org_s, 8);
+ tcg_gen_andi_tl(d, t, 0xff00ff00);
+ tcg_gen_shri_tl(t, org_s, 8);
+ tcg_gen_andi_tl(t, t, 0x00ff00ff);
+ tcg_gen_or_tl(d, d, t);
+ tcg_temp_free(t);
+ tcg_temp_free(org_s);
+}
+
+/* Swap the halfwords of the s operand. */
+static inline void t_gen_swapw(TCGv d, TCGv s)
+{
+ TCGv t;
+ /* d and s refer the same object. */
+ t = tcg_temp_new();
+ tcg_gen_mov_tl(t, s);
+ tcg_gen_shli_tl(d, t, 16);
+ tcg_gen_shri_tl(t, t, 16);
+ tcg_gen_or_tl(d, d, t);
+ tcg_temp_free(t);
+}
+
+/* Reverse the within each byte.
+ T0 = (((T0 << 7) & 0x80808080) |
+ ((T0 << 5) & 0x40404040) |
+ ((T0 << 3) & 0x20202020) |
+ ((T0 << 1) & 0x10101010) |
+ ((T0 >> 1) & 0x08080808) |
+ ((T0 >> 3) & 0x04040404) |
+ ((T0 >> 5) & 0x02020202) |
+ ((T0 >> 7) & 0x01010101));
+ */
+static inline void t_gen_swapr(TCGv d, TCGv s)
+{
+ struct {
+ int shift; /* LSL when positive, LSR when negative. */
+ uint32_t mask;
+ } bitrev [] = {
+ {7, 0x80808080},
+ {5, 0x40404040},
+ {3, 0x20202020},
+ {1, 0x10101010},
+ {-1, 0x08080808},
+ {-3, 0x04040404},
+ {-5, 0x02020202},
+ {-7, 0x01010101}
+ };
+ int i;
+ TCGv t, org_s;
+
+ /* d and s refer the same object. */
+ t = tcg_temp_new();
+ org_s = tcg_temp_new();
+ tcg_gen_mov_tl(org_s, s);
+
+ tcg_gen_shli_tl(t, org_s, bitrev[0].shift);
+ tcg_gen_andi_tl(d, t, bitrev[0].mask);
+ for (i = 1; i < ARRAY_SIZE(bitrev); i++) {
+ if (bitrev[i].shift >= 0) {
+ tcg_gen_shli_tl(t, org_s, bitrev[i].shift);
+ } else {
+ tcg_gen_shri_tl(t, org_s, -bitrev[i].shift);
+ }
+ tcg_gen_andi_tl(t, t, bitrev[i].mask);
+ tcg_gen_or_tl(d, d, t);
+ }
+ tcg_temp_free(t);
+ tcg_temp_free(org_s);
+}
+
+static void t_gen_cc_jmp(TCGv pc_true, TCGv pc_false)
+{
+ int l1;
+
+ l1 = gen_new_label();
+
+ /* Conditional jmp. */
+ tcg_gen_mov_tl(env_pc, pc_false);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1);
+ tcg_gen_mov_tl(env_pc, pc_true);
+ gen_set_label(l1);
+}
+
+static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = dc->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_tl(env_pc, dest);
+ tcg_gen_exit_tb((long)tb + n);
+ } else {
+ tcg_gen_movi_tl(env_pc, dest);
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static inline void cris_clear_x_flag(DisasContext *dc)
+{
+ if (dc->flagx_known && dc->flags_x)
+ dc->flags_uptodate = 0;
+
+ dc->flagx_known = 1;
+ dc->flags_x = 0;
+}
+
+static void cris_flush_cc_state(DisasContext *dc)
+{
+ if (dc->cc_size_uptodate != dc->cc_size) {
+ tcg_gen_movi_tl(cc_size, dc->cc_size);
+ dc->cc_size_uptodate = dc->cc_size;
+ }
+ tcg_gen_movi_tl(cc_op, dc->cc_op);
+ tcg_gen_movi_tl(cc_mask, dc->cc_mask);
+}
+
+static void cris_evaluate_flags(DisasContext *dc)
+{
+ if (dc->flags_uptodate)
+ return;
+
+ cris_flush_cc_state(dc);
+
+ switch (dc->cc_op)
+ {
+ case CC_OP_MCP:
+ gen_helper_evaluate_flags_mcp(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_src,
+ cc_dest, cc_result);
+ break;
+ case CC_OP_MULS:
+ gen_helper_evaluate_flags_muls(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_result,
+ cpu_PR[PR_MOF]);
+ break;
+ case CC_OP_MULU:
+ gen_helper_evaluate_flags_mulu(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_result,
+ cpu_PR[PR_MOF]);
+ break;
+ case CC_OP_MOVE:
+ case CC_OP_AND:
+ case CC_OP_OR:
+ case CC_OP_XOR:
+ case CC_OP_ASR:
+ case CC_OP_LSR:
+ case CC_OP_LSL:
+ switch (dc->cc_size)
+ {
+ case 4:
+ gen_helper_evaluate_flags_move_4(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_result);
+ break;
+ case 2:
+ gen_helper_evaluate_flags_move_2(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_result);
+ break;
+ default:
+ gen_helper_evaluate_flags();
+ break;
+ }
+ break;
+ case CC_OP_FLAGS:
+ /* live. */
+ break;
+ case CC_OP_SUB:
+ case CC_OP_CMP:
+ if (dc->cc_size == 4)
+ gen_helper_evaluate_flags_sub_4(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_src, cc_dest, cc_result);
+ else
+ gen_helper_evaluate_flags();
+
+ break;
+ default:
+ switch (dc->cc_size)
+ {
+ case 4:
+ gen_helper_evaluate_flags_alu_4(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], cc_src, cc_dest, cc_result);
+ break;
+ default:
+ gen_helper_evaluate_flags();
+ break;
+ }
+ break;
+ }
+
+ if (dc->flagx_known) {
+ if (dc->flags_x)
+ tcg_gen_ori_tl(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], X_FLAG);
+ else if (dc->cc_op == CC_OP_FLAGS)
+ tcg_gen_andi_tl(cpu_PR[PR_CCS],
+ cpu_PR[PR_CCS], ~X_FLAG);
+ }
+ dc->flags_uptodate = 1;
+}
+
+static void cris_cc_mask(DisasContext *dc, unsigned int mask)
+{
+ uint32_t ovl;
+
+ if (!mask) {
+ dc->update_cc = 0;
+ return;
+ }
+
+ /* Check if we need to evaluate the condition codes due to
+ CC overlaying. */
+ ovl = (dc->cc_mask ^ mask) & ~mask;
+ if (ovl) {
+ /* TODO: optimize this case. It trigs all the time. */
+ cris_evaluate_flags (dc);
+ }
+ dc->cc_mask = mask;
+ dc->update_cc = 1;
+}
+
+static void cris_update_cc_op(DisasContext *dc, int op, int size)
+{
+ dc->cc_op = op;
+ dc->cc_size = size;
+ dc->flags_uptodate = 0;
+}
+
+static inline void cris_update_cc_x(DisasContext *dc)
+{
+ /* Save the x flag state at the time of the cc snapshot. */
+ if (dc->flagx_known) {
+ if (dc->cc_x_uptodate == (2 | dc->flags_x))
+ return;
+ tcg_gen_movi_tl(cc_x, dc->flags_x);
+ dc->cc_x_uptodate = 2 | dc->flags_x;
+ }
+ else {
+ tcg_gen_andi_tl(cc_x, cpu_PR[PR_CCS], X_FLAG);
+ dc->cc_x_uptodate = 1;
+ }
+}
+
+/* Update cc prior to executing ALU op. Needs source operands untouched. */
+static void cris_pre_alu_update_cc(DisasContext *dc, int op,
+ TCGv dst, TCGv src, int size)
+{
+ if (dc->update_cc) {
+ cris_update_cc_op(dc, op, size);
+ tcg_gen_mov_tl(cc_src, src);
+
+ if (op != CC_OP_MOVE
+ && op != CC_OP_AND
+ && op != CC_OP_OR
+ && op != CC_OP_XOR
+ && op != CC_OP_ASR
+ && op != CC_OP_LSR
+ && op != CC_OP_LSL)
+ tcg_gen_mov_tl(cc_dest, dst);
+
+ cris_update_cc_x(dc);
+ }
+}
+
+/* Update cc after executing ALU op. needs the result. */
+static inline void cris_update_result(DisasContext *dc, TCGv res)
+{
+ if (dc->update_cc)
+ tcg_gen_mov_tl(cc_result, res);
+}
+
+/* Returns one if the write back stage should execute. */
+static void cris_alu_op_exec(DisasContext *dc, int op,
+ TCGv dst, TCGv a, TCGv b, int size)
+{
+ /* Emit the ALU insns. */
+ switch (op)
+ {
+ case CC_OP_ADD:
+ tcg_gen_add_tl(dst, a, b);
+ /* Extended arithmetics. */
+ t_gen_addx_carry(dc, dst);
+ break;
+ case CC_OP_ADDC:
+ tcg_gen_add_tl(dst, a, b);
+ t_gen_add_flag(dst, 0); /* C_FLAG. */
+ break;
+ case CC_OP_MCP:
+ tcg_gen_add_tl(dst, a, b);
+ t_gen_add_flag(dst, 8); /* R_FLAG. */
+ break;
+ case CC_OP_SUB:
+ tcg_gen_sub_tl(dst, a, b);
+ /* Extended arithmetics. */
+ t_gen_subx_carry(dc, dst);
+ break;
+ case CC_OP_MOVE:
+ tcg_gen_mov_tl(dst, b);
+ break;
+ case CC_OP_OR:
+ tcg_gen_or_tl(dst, a, b);
+ break;
+ case CC_OP_AND:
+ tcg_gen_and_tl(dst, a, b);
+ break;
+ case CC_OP_XOR:
+ tcg_gen_xor_tl(dst, a, b);
+ break;
+ case CC_OP_LSL:
+ t_gen_lsl(dst, a, b);
+ break;
+ case CC_OP_LSR:
+ t_gen_lsr(dst, a, b);
+ break;
+ case CC_OP_ASR:
+ t_gen_asr(dst, a, b);
+ break;
+ case CC_OP_NEG:
+ tcg_gen_neg_tl(dst, b);
+ /* Extended arithmetics. */
+ t_gen_subx_carry(dc, dst);
+ break;
+ case CC_OP_LZ:
+ gen_helper_lz(dst, b);
+ break;
+ case CC_OP_MULS:
+ t_gen_muls(dst, cpu_PR[PR_MOF], a, b);
+ break;
+ case CC_OP_MULU:
+ t_gen_mulu(dst, cpu_PR[PR_MOF], a, b);
+ break;
+ case CC_OP_DSTEP:
+ t_gen_cris_dstep(dst, a, b);
+ break;
+ case CC_OP_MSTEP:
+ t_gen_cris_mstep(dst, a, b, cpu_PR[PR_CCS]);
+ break;
+ case CC_OP_BOUND:
+ {
+ int l1;
+ l1 = gen_new_label();
+ tcg_gen_mov_tl(dst, a);
+ tcg_gen_brcond_tl(TCG_COND_LEU, a, b, l1);
+ tcg_gen_mov_tl(dst, b);
+ gen_set_label(l1);
+ }
+ break;
+ case CC_OP_CMP:
+ tcg_gen_sub_tl(dst, a, b);
+ /* Extended arithmetics. */
+ t_gen_subx_carry(dc, dst);
+ break;
+ default:
+ qemu_log("illegal ALU op.\n");
+ BUG();
+ break;
+ }
+
+ if (size == 1)
+ tcg_gen_andi_tl(dst, dst, 0xff);
+ else if (size == 2)
+ tcg_gen_andi_tl(dst, dst, 0xffff);
+}
+
+static void cris_alu(DisasContext *dc, int op,
+ TCGv d, TCGv op_a, TCGv op_b, int size)
+{
+ TCGv tmp;
+ int writeback;
+
+ writeback = 1;
+
+ if (op == CC_OP_CMP) {
+ tmp = tcg_temp_new();
+ writeback = 0;
+ } else if (size == 4) {
+ tmp = d;
+ writeback = 0;
+ } else
+ tmp = tcg_temp_new();
+
+
+ cris_pre_alu_update_cc(dc, op, op_a, op_b, size);
+ cris_alu_op_exec(dc, op, tmp, op_a, op_b, size);
+ cris_update_result(dc, tmp);
+
+ /* Writeback. */
+ if (writeback) {
+ if (size == 1)
+ tcg_gen_andi_tl(d, d, ~0xff);
+ else
+ tcg_gen_andi_tl(d, d, ~0xffff);
+ tcg_gen_or_tl(d, d, tmp);
+ }
+ if (!TCGV_EQUAL(tmp, d))
+ tcg_temp_free(tmp);
+}
+
+static int arith_cc(DisasContext *dc)
+{
+ if (dc->update_cc) {
+ switch (dc->cc_op) {
+ case CC_OP_ADDC: return 1;
+ case CC_OP_ADD: return 1;
+ case CC_OP_SUB: return 1;
+ case CC_OP_DSTEP: return 1;
+ case CC_OP_LSL: return 1;
+ case CC_OP_LSR: return 1;
+ case CC_OP_ASR: return 1;
+ case CC_OP_CMP: return 1;
+ case CC_OP_NEG: return 1;
+ case CC_OP_OR: return 1;
+ case CC_OP_AND: return 1;
+ case CC_OP_XOR: return 1;
+ case CC_OP_MULU: return 1;
+ case CC_OP_MULS: return 1;
+ default:
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static void gen_tst_cc (DisasContext *dc, TCGv cc, int cond)
+{
+ int arith_opt, move_opt;
+
+ /* TODO: optimize more condition codes. */
+
+ /*
+ * If the flags are live, we've gotta look into the bits of CCS.
+ * Otherwise, if we just did an arithmetic operation we try to
+ * evaluate the condition code faster.
+ *
+ * When this function is done, T0 should be non-zero if the condition
+ * code is true.
+ */
+ arith_opt = arith_cc(dc) && !dc->flags_uptodate;
+ move_opt = (dc->cc_op == CC_OP_MOVE);
+ switch (cond) {
+ case CC_EQ:
+ if ((arith_opt || move_opt)
+ && dc->cc_x_uptodate != (2 | X_FLAG)) {
+ tcg_gen_setcond_tl(TCG_COND_EQ, cc,
+ cc_result, tcg_const_tl(0));
+ }
+ else {
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cc,
+ cpu_PR[PR_CCS], Z_FLAG);
+ }
+ break;
+ case CC_NE:
+ if ((arith_opt || move_opt)
+ && dc->cc_x_uptodate != (2 | X_FLAG)) {
+ tcg_gen_mov_tl(cc, cc_result);
+ } else {
+ cris_evaluate_flags(dc);
+ tcg_gen_xori_tl(cc, cpu_PR[PR_CCS],
+ Z_FLAG);
+ tcg_gen_andi_tl(cc, cc, Z_FLAG);
+ }
+ break;
+ case CC_CS:
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], C_FLAG);
+ break;
+ case CC_CC:
+ cris_evaluate_flags(dc);
+ tcg_gen_xori_tl(cc, cpu_PR[PR_CCS], C_FLAG);
+ tcg_gen_andi_tl(cc, cc, C_FLAG);
+ break;
+ case CC_VS:
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], V_FLAG);
+ break;
+ case CC_VC:
+ cris_evaluate_flags(dc);
+ tcg_gen_xori_tl(cc, cpu_PR[PR_CCS],
+ V_FLAG);
+ tcg_gen_andi_tl(cc, cc, V_FLAG);
+ break;
+ case CC_PL:
+ if (arith_opt || move_opt) {
+ int bits = 31;
+
+ if (dc->cc_size == 1)
+ bits = 7;
+ else if (dc->cc_size == 2)
+ bits = 15;
+
+ tcg_gen_shri_tl(cc, cc_result, bits);
+ tcg_gen_xori_tl(cc, cc, 1);
+ } else {
+ cris_evaluate_flags(dc);
+ tcg_gen_xori_tl(cc, cpu_PR[PR_CCS],
+ N_FLAG);
+ tcg_gen_andi_tl(cc, cc, N_FLAG);
+ }
+ break;
+ case CC_MI:
+ if (arith_opt || move_opt) {
+ int bits = 31;
+
+ if (dc->cc_size == 1)
+ bits = 7;
+ else if (dc->cc_size == 2)
+ bits = 15;
+
+ tcg_gen_shri_tl(cc, cc_result, bits);
+ tcg_gen_andi_tl(cc, cc, 1);
+ }
+ else {
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cc, cpu_PR[PR_CCS],
+ N_FLAG);
+ }
+ break;
+ case CC_LS:
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cc, cpu_PR[PR_CCS],
+ C_FLAG | Z_FLAG);
+ break;
+ case CC_HI:
+ cris_evaluate_flags(dc);
+ {
+ TCGv tmp;
+
+ tmp = tcg_temp_new();
+ tcg_gen_xori_tl(tmp, cpu_PR[PR_CCS],
+ C_FLAG | Z_FLAG);
+ /* Overlay the C flag on top of the Z. */
+ tcg_gen_shli_tl(cc, tmp, 2);
+ tcg_gen_and_tl(cc, tmp, cc);
+ tcg_gen_andi_tl(cc, cc, Z_FLAG);
+
+ tcg_temp_free(tmp);
+ }
+ break;
+ case CC_GE:
+ cris_evaluate_flags(dc);
+ /* Overlay the V flag on top of the N. */
+ tcg_gen_shli_tl(cc, cpu_PR[PR_CCS], 2);
+ tcg_gen_xor_tl(cc,
+ cpu_PR[PR_CCS], cc);
+ tcg_gen_andi_tl(cc, cc, N_FLAG);
+ tcg_gen_xori_tl(cc, cc, N_FLAG);
+ break;
+ case CC_LT:
+ cris_evaluate_flags(dc);
+ /* Overlay the V flag on top of the N. */
+ tcg_gen_shli_tl(cc, cpu_PR[PR_CCS], 2);
+ tcg_gen_xor_tl(cc,
+ cpu_PR[PR_CCS], cc);
+ tcg_gen_andi_tl(cc, cc, N_FLAG);
+ break;
+ case CC_GT:
+ cris_evaluate_flags(dc);
+ {
+ TCGv n, z;
+
+ n = tcg_temp_new();
+ z = tcg_temp_new();
+
+ /* To avoid a shift we overlay everything on
+ the V flag. */
+ tcg_gen_shri_tl(n, cpu_PR[PR_CCS], 2);
+ tcg_gen_shri_tl(z, cpu_PR[PR_CCS], 1);
+ /* invert Z. */
+ tcg_gen_xori_tl(z, z, 2);
+
+ tcg_gen_xor_tl(n, n, cpu_PR[PR_CCS]);
+ tcg_gen_xori_tl(n, n, 2);
+ tcg_gen_and_tl(cc, z, n);
+ tcg_gen_andi_tl(cc, cc, 2);
+
+ tcg_temp_free(n);
+ tcg_temp_free(z);
+ }
+ break;
+ case CC_LE:
+ cris_evaluate_flags(dc);
+ {
+ TCGv n, z;
+
+ n = tcg_temp_new();
+ z = tcg_temp_new();
+
+ /* To avoid a shift we overlay everything on
+ the V flag. */
+ tcg_gen_shri_tl(n, cpu_PR[PR_CCS], 2);
+ tcg_gen_shri_tl(z, cpu_PR[PR_CCS], 1);
+
+ tcg_gen_xor_tl(n, n, cpu_PR[PR_CCS]);
+ tcg_gen_or_tl(cc, z, n);
+ tcg_gen_andi_tl(cc, cc, 2);
+
+ tcg_temp_free(n);
+ tcg_temp_free(z);
+ }
+ break;
+ case CC_P:
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], P_FLAG);
+ break;
+ case CC_A:
+ tcg_gen_movi_tl(cc, 1);
+ break;
+ default:
+ BUG();
+ break;
+ };
+}
+
+static void cris_store_direct_jmp(DisasContext *dc)
+{
+ /* Store the direct jmp state into the cpu-state. */
+ if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) {
+ if (dc->jmp == JMP_DIRECT) {
+ tcg_gen_movi_tl(env_btaken, 1);
+ }
+ tcg_gen_movi_tl(env_btarget, dc->jmp_pc);
+ dc->jmp = JMP_INDIRECT;
+ }
+}
+
+static void cris_prepare_cc_branch (DisasContext *dc,
+ int offset, int cond)
+{
+ /* This helps us re-schedule the micro-code to insns in delay-slots
+ before the actual jump. */
+ dc->delayed_branch = 2;
+ dc->jmp = JMP_DIRECT_CC;
+ dc->jmp_pc = dc->pc + offset;
+
+ gen_tst_cc (dc, env_btaken, cond);
+ tcg_gen_movi_tl(env_btarget, dc->jmp_pc);
+}
+
+
+/* jumps, when the dest is in a live reg for example. Direct should be set
+ when the dest addr is constant to allow tb chaining. */
+static inline void cris_prepare_jmp (DisasContext *dc, unsigned int type)
+{
+ /* This helps us re-schedule the micro-code to insns in delay-slots
+ before the actual jump. */
+ dc->delayed_branch = 2;
+ dc->jmp = type;
+ if (type == JMP_INDIRECT) {
+ tcg_gen_movi_tl(env_btaken, 1);
+ }
+}
+
+static void gen_load64(DisasContext *dc, TCGv_i64 dst, TCGv addr)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ /* If we get a fault on a delayslot we must keep the jmp state in
+ the cpu-state to be able to re-execute the jmp. */
+ if (dc->delayed_branch == 1)
+ cris_store_direct_jmp(dc);
+
+ tcg_gen_qemu_ld64(dst, addr, mem_index);
+}
+
+static void gen_load(DisasContext *dc, TCGv dst, TCGv addr,
+ unsigned int size, int sign)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ /* If we get a fault on a delayslot we must keep the jmp state in
+ the cpu-state to be able to re-execute the jmp. */
+ if (dc->delayed_branch == 1)
+ cris_store_direct_jmp(dc);
+
+ if (size == 1) {
+ if (sign)
+ tcg_gen_qemu_ld8s(dst, addr, mem_index);
+ else
+ tcg_gen_qemu_ld8u(dst, addr, mem_index);
+ }
+ else if (size == 2) {
+ if (sign)
+ tcg_gen_qemu_ld16s(dst, addr, mem_index);
+ else
+ tcg_gen_qemu_ld16u(dst, addr, mem_index);
+ }
+ else if (size == 4) {
+ tcg_gen_qemu_ld32u(dst, addr, mem_index);
+ }
+ else {
+ abort();
+ }
+}
+
+static void gen_store (DisasContext *dc, TCGv addr, TCGv val,
+ unsigned int size)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ /* If we get a fault on a delayslot we must keep the jmp state in
+ the cpu-state to be able to re-execute the jmp. */
+ if (dc->delayed_branch == 1)
+ cris_store_direct_jmp(dc);
+
+
+ /* Conditional writes. We only support the kind were X and P are known
+ at translation time. */
+ if (dc->flagx_known && dc->flags_x && (dc->tb_flags & P_FLAG)) {
+ dc->postinc = 0;
+ cris_evaluate_flags(dc);
+ tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], C_FLAG);
+ return;
+ }
+
+ if (size == 1)
+ tcg_gen_qemu_st8(val, addr, mem_index);
+ else if (size == 2)
+ tcg_gen_qemu_st16(val, addr, mem_index);
+ else
+ tcg_gen_qemu_st32(val, addr, mem_index);
+
+ if (dc->flagx_known && dc->flags_x) {
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~C_FLAG);
+ }
+}
+
+static inline void t_gen_sext(TCGv d, TCGv s, int size)
+{
+ if (size == 1)
+ tcg_gen_ext8s_i32(d, s);
+ else if (size == 2)
+ tcg_gen_ext16s_i32(d, s);
+ else if(!TCGV_EQUAL(d, s))
+ tcg_gen_mov_tl(d, s);
+}
+
+static inline void t_gen_zext(TCGv d, TCGv s, int size)
+{
+ if (size == 1)
+ tcg_gen_ext8u_i32(d, s);
+ else if (size == 2)
+ tcg_gen_ext16u_i32(d, s);
+ else if (!TCGV_EQUAL(d, s))
+ tcg_gen_mov_tl(d, s);
+}
+
+#if DISAS_CRIS
+static char memsize_char(int size)
+{
+ switch (size)
+ {
+ case 1: return 'b'; break;
+ case 2: return 'w'; break;
+ case 4: return 'd'; break;
+ default:
+ return 'x';
+ break;
+ }
+}
+#endif
+
+static inline unsigned int memsize_z(DisasContext *dc)
+{
+ return dc->zsize + 1;
+}
+
+static inline unsigned int memsize_zz(DisasContext *dc)
+{
+ switch (dc->zzsize)
+ {
+ case 0: return 1;
+ case 1: return 2;
+ default:
+ return 4;
+ }
+}
+
+static inline void do_postinc (DisasContext *dc, int size)
+{
+ if (dc->postinc)
+ tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], size);
+}
+
+static inline void dec_prep_move_r(DisasContext *dc, int rs, int rd,
+ int size, int s_ext, TCGv dst)
+{
+ if (s_ext)
+ t_gen_sext(dst, cpu_R[rs], size);
+ else
+ t_gen_zext(dst, cpu_R[rs], size);
+}
+
+/* Prepare T0 and T1 for a register alu operation.
+ s_ext decides if the operand1 should be sign-extended or zero-extended when
+ needed. */
+static void dec_prep_alu_r(DisasContext *dc, int rs, int rd,
+ int size, int s_ext, TCGv dst, TCGv src)
+{
+ dec_prep_move_r(dc, rs, rd, size, s_ext, src);
+
+ if (s_ext)
+ t_gen_sext(dst, cpu_R[rd], size);
+ else
+ t_gen_zext(dst, cpu_R[rd], size);
+}
+
+static int dec_prep_move_m(DisasContext *dc, int s_ext, int memsize,
+ TCGv dst)
+{
+ unsigned int rs;
+ uint32_t imm;
+ int is_imm;
+ int insn_len = 2;
+
+ rs = dc->op1;
+ is_imm = rs == 15 && dc->postinc;
+
+ /* Load [$rs] onto T1. */
+ if (is_imm) {
+ insn_len = 2 + memsize;
+ if (memsize == 1)
+ insn_len++;
+
+ imm = cris_fetch(dc, dc->pc + 2, memsize, s_ext);
+ tcg_gen_movi_tl(dst, imm);
+ dc->postinc = 0;
+ } else {
+ cris_flush_cc_state(dc);
+ gen_load(dc, dst, cpu_R[rs], memsize, 0);
+ if (s_ext)
+ t_gen_sext(dst, dst, memsize);
+ else
+ t_gen_zext(dst, dst, memsize);
+ }
+ return insn_len;
+}
+
+/* Prepare T0 and T1 for a memory + alu operation.
+ s_ext decides if the operand1 should be sign-extended or zero-extended when
+ needed. */
+static int dec_prep_alu_m(DisasContext *dc, int s_ext, int memsize,
+ TCGv dst, TCGv src)
+{
+ int insn_len;
+
+ insn_len = dec_prep_move_m(dc, s_ext, memsize, src);
+ tcg_gen_mov_tl(dst, cpu_R[dc->op2]);
+ return insn_len;
+}
+
+#if DISAS_CRIS
+static const char *cc_name(int cc)
+{
+ static const char *cc_names[16] = {
+ "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi",
+ "ls", "hi", "ge", "lt", "gt", "le", "a", "p"
+ };
+ assert(cc < 16);
+ return cc_names[cc];
+}
+#endif
+
+/* Start of insn decoders. */
+
+static int dec_bccq(DisasContext *dc)
+{
+ int32_t offset;
+ int sign;
+ uint32_t cond = dc->op2;
+
+ offset = EXTRACT_FIELD (dc->ir, 1, 7);
+ sign = EXTRACT_FIELD(dc->ir, 0, 0);
+
+ offset *= 2;
+ offset |= sign << 8;
+ offset = sign_extend(offset, 8);
+
+ LOG_DIS("b%s %x\n", cc_name(cond), dc->pc + offset);
+
+ /* op2 holds the condition-code. */
+ cris_cc_mask(dc, 0);
+ cris_prepare_cc_branch (dc, offset, cond);
+ return 2;
+}
+static int dec_addoq(DisasContext *dc)
+{
+ int32_t imm;
+
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 7);
+ imm = sign_extend(dc->op1, 7);
+
+ LOG_DIS("addoq %d, $r%u\n", imm, dc->op2);
+ cris_cc_mask(dc, 0);
+ /* Fetch register operand, */
+ tcg_gen_addi_tl(cpu_R[R_ACR], cpu_R[dc->op2], imm);
+
+ return 2;
+}
+static int dec_addq(DisasContext *dc)
+{
+ LOG_DIS("addq %u, $r%u\n", dc->op1, dc->op2);
+
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+
+ cris_alu(dc, CC_OP_ADD,
+ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(dc->op1), 4);
+ return 2;
+}
+static int dec_moveq(DisasContext *dc)
+{
+ uint32_t imm;
+
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5);
+ imm = sign_extend(dc->op1, 5);
+ LOG_DIS("moveq %d, $r%u\n", imm, dc->op2);
+
+ tcg_gen_movi_tl(cpu_R[dc->op2], imm);
+ return 2;
+}
+static int dec_subq(DisasContext *dc)
+{
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5);
+
+ LOG_DIS("subq %u, $r%u\n", dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_SUB,
+ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(dc->op1), 4);
+ return 2;
+}
+static int dec_cmpq(DisasContext *dc)
+{
+ uint32_t imm;
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5);
+ imm = sign_extend(dc->op1, 5);
+
+ LOG_DIS("cmpq %d, $r%d\n", imm, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+
+ cris_alu(dc, CC_OP_CMP,
+ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(imm), 4);
+ return 2;
+}
+static int dec_andq(DisasContext *dc)
+{
+ uint32_t imm;
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5);
+ imm = sign_extend(dc->op1, 5);
+
+ LOG_DIS("andq %d, $r%d\n", imm, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ cris_alu(dc, CC_OP_AND,
+ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(imm), 4);
+ return 2;
+}
+static int dec_orq(DisasContext *dc)
+{
+ uint32_t imm;
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5);
+ imm = sign_extend(dc->op1, 5);
+ LOG_DIS("orq %d, $r%d\n", imm, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ cris_alu(dc, CC_OP_OR,
+ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(imm), 4);
+ return 2;
+}
+static int dec_btstq(DisasContext *dc)
+{
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4);
+ LOG_DIS("btstq %u, $r%d\n", dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_evaluate_flags(dc);
+ gen_helper_btst(cpu_PR[PR_CCS], cpu_R[dc->op2],
+ tcg_const_tl(dc->op1), cpu_PR[PR_CCS]);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op2], 4);
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ dc->flags_uptodate = 1;
+ return 2;
+}
+static int dec_asrq(DisasContext *dc)
+{
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4);
+ LOG_DIS("asrq %u, $r%d\n", dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ tcg_gen_sari_tl(cpu_R[dc->op2], cpu_R[dc->op2], dc->op1);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2],
+ cpu_R[dc->op2], cpu_R[dc->op2], 4);
+ return 2;
+}
+static int dec_lslq(DisasContext *dc)
+{
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4);
+ LOG_DIS("lslq %u, $r%d\n", dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ tcg_gen_shli_tl(cpu_R[dc->op2], cpu_R[dc->op2], dc->op1);
+
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2],
+ cpu_R[dc->op2], cpu_R[dc->op2], 4);
+ return 2;
+}
+static int dec_lsrq(DisasContext *dc)
+{
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4);
+ LOG_DIS("lsrq %u, $r%d\n", dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ tcg_gen_shri_tl(cpu_R[dc->op2], cpu_R[dc->op2], dc->op1);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2],
+ cpu_R[dc->op2], cpu_R[dc->op2], 4);
+ return 2;
+}
+
+static int dec_move_r(DisasContext *dc)
+{
+ int size = memsize_zz(dc);
+
+ LOG_DIS("move.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ if (size == 4) {
+ dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, cpu_R[dc->op2]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_update_cc_op(dc, CC_OP_MOVE, 4);
+ cris_update_cc_x(dc);
+ cris_update_result(dc, cpu_R[dc->op2]);
+ }
+ else {
+ TCGv t0;
+
+ t0 = tcg_temp_new();
+ dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, t0);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2],
+ cpu_R[dc->op2], t0, size);
+ tcg_temp_free(t0);
+ }
+ return 2;
+}
+
+static int dec_scc_r(DisasContext *dc)
+{
+ int cond = dc->op2;
+
+ LOG_DIS("s%s $r%u\n",
+ cc_name(cond), dc->op1);
+
+ if (cond != CC_A)
+ {
+ int l1;
+
+ gen_tst_cc (dc, cpu_R[dc->op1], cond);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->op1], 0, l1);
+ tcg_gen_movi_tl(cpu_R[dc->op1], 1);
+ gen_set_label(l1);
+ }
+ else
+ tcg_gen_movi_tl(cpu_R[dc->op1], 1);
+
+ cris_cc_mask(dc, 0);
+ return 2;
+}
+
+static inline void cris_alu_alloc_temps(DisasContext *dc, int size, TCGv *t)
+{
+ if (size == 4) {
+ t[0] = cpu_R[dc->op2];
+ t[1] = cpu_R[dc->op1];
+ } else {
+ t[0] = tcg_temp_new();
+ t[1] = tcg_temp_new();
+ }
+}
+
+static inline void cris_alu_free_temps(DisasContext *dc, int size, TCGv *t)
+{
+ if (size != 4) {
+ tcg_temp_free(t[0]);
+ tcg_temp_free(t[1]);
+ }
+}
+
+static int dec_and_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+
+ LOG_DIS("and.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+ cris_alu(dc, CC_OP_AND, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_lz_r(DisasContext *dc)
+{
+ TCGv t0;
+ LOG_DIS("lz $r%u, $r%u\n",
+ dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ t0 = tcg_temp_new();
+ dec_prep_alu_r(dc, dc->op1, dc->op2, 4, 0, cpu_R[dc->op2], t0);
+ cris_alu(dc, CC_OP_LZ, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+static int dec_lsl_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+
+ LOG_DIS("lsl.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+ tcg_gen_andi_tl(t[1], t[1], 63);
+ cris_alu(dc, CC_OP_LSL, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_alloc_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_lsr_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+
+ LOG_DIS("lsr.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+ tcg_gen_andi_tl(t[1], t[1], 63);
+ cris_alu(dc, CC_OP_LSR, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_asr_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+
+ LOG_DIS("asr.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 1, t[0], t[1]);
+ tcg_gen_andi_tl(t[1], t[1], 63);
+ cris_alu(dc, CC_OP_ASR, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_muls_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+
+ LOG_DIS("muls.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZV);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 1, t[0], t[1]);
+
+ cris_alu(dc, CC_OP_MULS, cpu_R[dc->op2], t[0], t[1], 4);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_mulu_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+
+ LOG_DIS("mulu.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZV);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+
+ cris_alu(dc, CC_OP_MULU, cpu_R[dc->op2], t[0], t[1], 4);
+ cris_alu_alloc_temps(dc, size, t);
+ return 2;
+}
+
+
+static int dec_dstep_r(DisasContext *dc)
+{
+ LOG_DIS("dstep $r%u, $r%u\n", dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_DSTEP,
+ cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op1], 4);
+ return 2;
+}
+
+static int dec_xor_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+ LOG_DIS("xor.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ BUG_ON(size != 4); /* xor is dword. */
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+
+ cris_alu(dc, CC_OP_XOR, cpu_R[dc->op2], t[0], t[1], 4);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_bound_r(DisasContext *dc)
+{
+ TCGv l0;
+ int size = memsize_zz(dc);
+ LOG_DIS("bound.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ l0 = tcg_temp_local_new();
+ dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, l0);
+ cris_alu(dc, CC_OP_BOUND, cpu_R[dc->op2], cpu_R[dc->op2], l0, 4);
+ tcg_temp_free(l0);
+ return 2;
+}
+
+static int dec_cmp_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+ LOG_DIS("cmp.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+
+ cris_alu(dc, CC_OP_CMP, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_abs_r(DisasContext *dc)
+{
+ TCGv t0;
+
+ LOG_DIS("abs $r%u, $r%u\n",
+ dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+
+ t0 = tcg_temp_new();
+ tcg_gen_sari_tl(t0, cpu_R[dc->op1], 31);
+ tcg_gen_xor_tl(cpu_R[dc->op2], cpu_R[dc->op1], t0);
+ tcg_gen_sub_tl(cpu_R[dc->op2], cpu_R[dc->op2], t0);
+ tcg_temp_free(t0);
+
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op2], 4);
+ return 2;
+}
+
+static int dec_add_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+ LOG_DIS("add.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+
+ cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_addc_r(DisasContext *dc)
+{
+ LOG_DIS("addc $r%u, $r%u\n",
+ dc->op1, dc->op2);
+ cris_evaluate_flags(dc);
+ /* Set for this insn. */
+ dc->flagx_known = 1;
+ dc->flags_x = X_FLAG;
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_ADDC,
+ cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op1], 4);
+ return 2;
+}
+
+static int dec_mcp_r(DisasContext *dc)
+{
+ LOG_DIS("mcp $p%u, $r%u\n",
+ dc->op2, dc->op1);
+ cris_evaluate_flags(dc);
+ cris_cc_mask(dc, CC_MASK_RNZV);
+ cris_alu(dc, CC_OP_MCP,
+ cpu_R[dc->op1], cpu_R[dc->op1], cpu_PR[dc->op2], 4);
+ return 2;
+}
+
+#if DISAS_CRIS
+static char * swapmode_name(int mode, char *modename) {
+ int i = 0;
+ if (mode & 8)
+ modename[i++] = 'n';
+ if (mode & 4)
+ modename[i++] = 'w';
+ if (mode & 2)
+ modename[i++] = 'b';
+ if (mode & 1)
+ modename[i++] = 'r';
+ modename[i++] = 0;
+ return modename;
+}
+#endif
+
+static int dec_swap_r(DisasContext *dc)
+{
+ TCGv t0;
+#if DISAS_CRIS
+ char modename[4];
+#endif
+ LOG_DIS("swap%s $r%u\n",
+ swapmode_name(dc->op2, modename), dc->op1);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ t0 = tcg_temp_new();
+ t_gen_mov_TN_reg(t0, dc->op1);
+ if (dc->op2 & 8)
+ tcg_gen_not_tl(t0, t0);
+ if (dc->op2 & 4)
+ t_gen_swapw(t0, t0);
+ if (dc->op2 & 2)
+ t_gen_swapb(t0, t0);
+ if (dc->op2 & 1)
+ t_gen_swapr(t0, t0);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op1], cpu_R[dc->op1], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+static int dec_or_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+ LOG_DIS("or.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+ cris_alu(dc, CC_OP_OR, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_addi_r(DisasContext *dc)
+{
+ TCGv t0;
+ LOG_DIS("addi.%c $r%u, $r%u\n",
+ memsize_char(memsize_zz(dc)), dc->op2, dc->op1);
+ cris_cc_mask(dc, 0);
+ t0 = tcg_temp_new();
+ tcg_gen_shl_tl(t0, cpu_R[dc->op2], tcg_const_tl(dc->zzsize));
+ tcg_gen_add_tl(cpu_R[dc->op1], cpu_R[dc->op1], t0);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+static int dec_addi_acr(DisasContext *dc)
+{
+ TCGv t0;
+ LOG_DIS("addi.%c $r%u, $r%u, $acr\n",
+ memsize_char(memsize_zz(dc)), dc->op2, dc->op1);
+ cris_cc_mask(dc, 0);
+ t0 = tcg_temp_new();
+ tcg_gen_shl_tl(t0, cpu_R[dc->op2], tcg_const_tl(dc->zzsize));
+ tcg_gen_add_tl(cpu_R[R_ACR], cpu_R[dc->op1], t0);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+static int dec_neg_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+ LOG_DIS("neg.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+
+ cris_alu(dc, CC_OP_NEG, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+static int dec_btst_r(DisasContext *dc)
+{
+ LOG_DIS("btst $r%u, $r%u\n",
+ dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_evaluate_flags(dc);
+ gen_helper_btst(cpu_PR[PR_CCS], cpu_R[dc->op2],
+ cpu_R[dc->op1], cpu_PR[PR_CCS]);
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2],
+ cpu_R[dc->op2], cpu_R[dc->op2], 4);
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ dc->flags_uptodate = 1;
+ return 2;
+}
+
+static int dec_sub_r(DisasContext *dc)
+{
+ TCGv t[2];
+ int size = memsize_zz(dc);
+ LOG_DIS("sub.%c $r%u, $r%u\n",
+ memsize_char(size), dc->op1, dc->op2);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu_alloc_temps(dc, size, t);
+ dec_prep_alu_r(dc, dc->op1, dc->op2, size, 0, t[0], t[1]);
+ cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], t[0], t[1], size);
+ cris_alu_free_temps(dc, size, t);
+ return 2;
+}
+
+/* Zero extension. From size to dword. */
+static int dec_movu_r(DisasContext *dc)
+{
+ TCGv t0;
+ int size = memsize_z(dc);
+ LOG_DIS("movu.%c $r%u, $r%u\n",
+ memsize_char(size),
+ dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ t0 = tcg_temp_new();
+ dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, t0);
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+/* Sign extension. From size to dword. */
+static int dec_movs_r(DisasContext *dc)
+{
+ TCGv t0;
+ int size = memsize_z(dc);
+ LOG_DIS("movs.%c $r%u, $r%u\n",
+ memsize_char(size),
+ dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZ);
+ t0 = tcg_temp_new();
+ /* Size can only be qi or hi. */
+ t_gen_sext(t0, cpu_R[dc->op1], size);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2], cpu_R[dc->op1], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+/* zero extension. From size to dword. */
+static int dec_addu_r(DisasContext *dc)
+{
+ TCGv t0;
+ int size = memsize_z(dc);
+ LOG_DIS("addu.%c $r%u, $r%u\n",
+ memsize_char(size),
+ dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ t0 = tcg_temp_new();
+ /* Size can only be qi or hi. */
+ t_gen_zext(t0, cpu_R[dc->op1], size);
+ cris_alu(dc, CC_OP_ADD,
+ cpu_R[dc->op2], cpu_R[dc->op2], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+/* Sign extension. From size to dword. */
+static int dec_adds_r(DisasContext *dc)
+{
+ TCGv t0;
+ int size = memsize_z(dc);
+ LOG_DIS("adds.%c $r%u, $r%u\n",
+ memsize_char(size),
+ dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ t0 = tcg_temp_new();
+ /* Size can only be qi or hi. */
+ t_gen_sext(t0, cpu_R[dc->op1], size);
+ cris_alu(dc, CC_OP_ADD,
+ cpu_R[dc->op2], cpu_R[dc->op2], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+/* Zero extension. From size to dword. */
+static int dec_subu_r(DisasContext *dc)
+{
+ TCGv t0;
+ int size = memsize_z(dc);
+ LOG_DIS("subu.%c $r%u, $r%u\n",
+ memsize_char(size),
+ dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ t0 = tcg_temp_new();
+ /* Size can only be qi or hi. */
+ t_gen_zext(t0, cpu_R[dc->op1], size);
+ cris_alu(dc, CC_OP_SUB,
+ cpu_R[dc->op2], cpu_R[dc->op2], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+/* Sign extension. From size to dword. */
+static int dec_subs_r(DisasContext *dc)
+{
+ TCGv t0;
+ int size = memsize_z(dc);
+ LOG_DIS("subs.%c $r%u, $r%u\n",
+ memsize_char(size),
+ dc->op1, dc->op2);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ t0 = tcg_temp_new();
+ /* Size can only be qi or hi. */
+ t_gen_sext(t0, cpu_R[dc->op1], size);
+ cris_alu(dc, CC_OP_SUB,
+ cpu_R[dc->op2], cpu_R[dc->op2], t0, 4);
+ tcg_temp_free(t0);
+ return 2;
+}
+
+static int dec_setclrf(DisasContext *dc)
+{
+ uint32_t flags;
+ int set = (~dc->opcode >> 2) & 1;
+
+
+ flags = (EXTRACT_FIELD(dc->ir, 12, 15) << 4)
+ | EXTRACT_FIELD(dc->ir, 0, 3);
+ if (set && flags == 0) {
+ LOG_DIS("nop\n");
+ return 2;
+ } else if (!set && (flags & 0x20)) {
+ LOG_DIS("di\n");
+ }
+ else {
+ LOG_DIS("%sf %x\n",
+ set ? "set" : "clr",
+ flags);
+ }
+
+ /* User space is not allowed to touch these. Silently ignore. */
+ if (dc->tb_flags & U_FLAG) {
+ flags &= ~(S_FLAG | I_FLAG | U_FLAG);
+ }
+
+ if (flags & X_FLAG) {
+ dc->flagx_known = 1;
+ if (set)
+ dc->flags_x = X_FLAG;
+ else
+ dc->flags_x = 0;
+ }
+
+ /* Break the TB if any of the SPI flag changes. */
+ if (flags & (P_FLAG | S_FLAG)) {
+ tcg_gen_movi_tl(env_pc, dc->pc + 2);
+ dc->is_jmp = DISAS_UPDATE;
+ dc->cpustate_changed = 1;
+ }
+
+ /* For the I flag, only act on posedge. */
+ if ((flags & I_FLAG)) {
+ tcg_gen_movi_tl(env_pc, dc->pc + 2);
+ dc->is_jmp = DISAS_UPDATE;
+ dc->cpustate_changed = 1;
+ }
+
+
+ /* Simply decode the flags. */
+ cris_evaluate_flags (dc);
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ cris_update_cc_x(dc);
+ tcg_gen_movi_tl(cc_op, dc->cc_op);
+
+ if (set) {
+ if (!(dc->tb_flags & U_FLAG) && (flags & U_FLAG)) {
+ /* Enter user mode. */
+ t_gen_mov_env_TN(ksp, cpu_R[R_SP]);
+ tcg_gen_mov_tl(cpu_R[R_SP], cpu_PR[PR_USP]);
+ dc->cpustate_changed = 1;
+ }
+ tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags);
+ }
+ else
+ tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~flags);
+
+ dc->flags_uptodate = 1;
+ dc->clear_x = 0;
+ return 2;
+}
+
+static int dec_move_rs(DisasContext *dc)
+{
+ LOG_DIS("move $r%u, $s%u\n", dc->op1, dc->op2);
+ cris_cc_mask(dc, 0);
+ gen_helper_movl_sreg_reg(tcg_const_tl(dc->op2), tcg_const_tl(dc->op1));
+ return 2;
+}
+static int dec_move_sr(DisasContext *dc)
+{
+ LOG_DIS("move $s%u, $r%u\n", dc->op2, dc->op1);
+ cris_cc_mask(dc, 0);
+ gen_helper_movl_reg_sreg(tcg_const_tl(dc->op1), tcg_const_tl(dc->op2));
+ return 2;
+}
+
+static int dec_move_rp(DisasContext *dc)
+{
+ TCGv t[2];
+ LOG_DIS("move $r%u, $p%u\n", dc->op1, dc->op2);
+ cris_cc_mask(dc, 0);
+
+ t[0] = tcg_temp_new();
+ if (dc->op2 == PR_CCS) {
+ cris_evaluate_flags(dc);
+ t_gen_mov_TN_reg(t[0], dc->op1);
+ if (dc->tb_flags & U_FLAG) {
+ t[1] = tcg_temp_new();
+ /* User space is not allowed to touch all flags. */
+ tcg_gen_andi_tl(t[0], t[0], 0x39f);
+ tcg_gen_andi_tl(t[1], cpu_PR[PR_CCS], ~0x39f);
+ tcg_gen_or_tl(t[0], t[1], t[0]);
+ tcg_temp_free(t[1]);
+ }
+ }
+ else
+ t_gen_mov_TN_reg(t[0], dc->op1);
+
+ t_gen_mov_preg_TN(dc, dc->op2, t[0]);
+ if (dc->op2 == PR_CCS) {
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ dc->flags_uptodate = 1;
+ }
+ tcg_temp_free(t[0]);
+ return 2;
+}
+static int dec_move_pr(DisasContext *dc)
+{
+ TCGv t0;
+ LOG_DIS("move $p%u, $r%u\n", dc->op2, dc->op1);
+ cris_cc_mask(dc, 0);
+
+ if (dc->op2 == PR_CCS)
+ cris_evaluate_flags(dc);
+
+ if (dc->op2 == PR_DZ) {
+ tcg_gen_movi_tl(cpu_R[dc->op1], 0);
+ } else {
+ t0 = tcg_temp_new();
+ t_gen_mov_TN_preg(t0, dc->op2);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op1], cpu_R[dc->op1], t0,
+ preg_sizes[dc->op2]);
+ tcg_temp_free(t0);
+ }
+ return 2;
+}
+
+static int dec_move_mr(DisasContext *dc)
+{
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("move.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ if (memsize == 4) {
+ insn_len = dec_prep_move_m(dc, 0, 4, cpu_R[dc->op2]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_update_cc_op(dc, CC_OP_MOVE, 4);
+ cris_update_cc_x(dc);
+ cris_update_result(dc, cpu_R[dc->op2]);
+ }
+ else {
+ TCGv t0;
+
+ t0 = tcg_temp_new();
+ insn_len = dec_prep_move_m(dc, 0, memsize, t0);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2], cpu_R[dc->op2], t0, memsize);
+ tcg_temp_free(t0);
+ }
+ do_postinc(dc, memsize);
+ return insn_len;
+}
+
+static inline void cris_alu_m_alloc_temps(TCGv *t)
+{
+ t[0] = tcg_temp_new();
+ t[1] = tcg_temp_new();
+}
+
+static inline void cris_alu_m_free_temps(TCGv *t)
+{
+ tcg_temp_free(t[0]);
+ tcg_temp_free(t[1]);
+}
+
+static int dec_movs_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("movs.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ /* sign extend. */
+ insn_len = dec_prep_alu_m(dc, 1, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_MOVE,
+ cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_addu_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("addu.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ /* sign extend. */
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_ADD,
+ cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_adds_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("adds.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ /* sign extend. */
+ insn_len = dec_prep_alu_m(dc, 1, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_ADD, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_subu_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("subu.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ /* sign extend. */
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_subs_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("subs.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ /* sign extend. */
+ insn_len = dec_prep_alu_m(dc, 1, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_movu_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+
+ LOG_DIS("movu.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_cmpu_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("cmpu.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_CMP, cpu_R[dc->op2], cpu_R[dc->op2], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_cmps_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_z(dc);
+ int insn_len;
+ LOG_DIS("cmps.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 1, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_CMP,
+ cpu_R[dc->op2], cpu_R[dc->op2], t[1],
+ memsize_zz(dc));
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_cmp_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("cmp.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_CMP,
+ cpu_R[dc->op2], cpu_R[dc->op2], t[1],
+ memsize_zz(dc));
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_test_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("test.%c [$r%u%s] op2=%x\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_evaluate_flags(dc);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~3);
+
+ cris_alu(dc, CC_OP_CMP,
+ cpu_R[dc->op2], t[1], tcg_const_tl(0), memsize_zz(dc));
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_and_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("and.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_AND, cpu_R[dc->op2], t[0], t[1], memsize_zz(dc));
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_add_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("add.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_ADD,
+ cpu_R[dc->op2], t[0], t[1], memsize_zz(dc));
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_addo_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("add.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 1, memsize, t[0], t[1]);
+ cris_cc_mask(dc, 0);
+ cris_alu(dc, CC_OP_ADD, cpu_R[R_ACR], t[0], t[1], 4);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_bound_m(DisasContext *dc)
+{
+ TCGv l[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("bound.%c [$r%u%s, $r%u\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ l[0] = tcg_temp_local_new();
+ l[1] = tcg_temp_local_new();
+ insn_len = dec_prep_alu_m(dc, 0, memsize, l[0], l[1]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_BOUND, cpu_R[dc->op2], l[0], l[1], 4);
+ do_postinc(dc, memsize);
+ tcg_temp_free(l[0]);
+ tcg_temp_free(l[1]);
+ return insn_len;
+}
+
+static int dec_addc_mr(DisasContext *dc)
+{
+ TCGv t[2];
+ int insn_len = 2;
+ LOG_DIS("addc [$r%u%s, $r%u\n",
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_evaluate_flags(dc);
+
+ /* Set for this insn. */
+ dc->flagx_known = 1;
+ dc->flags_x = X_FLAG;
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, 4, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_ADDC, cpu_R[dc->op2], t[0], t[1], 4);
+ do_postinc(dc, 4);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_sub_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("sub.%c [$r%u%s, $r%u ir=%x zz=%x\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2, dc->ir, dc->zzsize);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_SUB, cpu_R[dc->op2], t[0], t[1], memsize);
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_or_m(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len;
+ LOG_DIS("or.%c [$r%u%s, $r%u pc=%x\n",
+ memsize_char(memsize),
+ dc->op1, dc->postinc ? "+]" : "]",
+ dc->op2, dc->pc);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, CC_MASK_NZ);
+ cris_alu(dc, CC_OP_OR,
+ cpu_R[dc->op2], t[0], t[1], memsize_zz(dc));
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_move_mp(DisasContext *dc)
+{
+ TCGv t[2];
+ int memsize = memsize_zz(dc);
+ int insn_len = 2;
+
+ LOG_DIS("move.%c [$r%u%s, $p%u\n",
+ memsize_char(memsize),
+ dc->op1,
+ dc->postinc ? "+]" : "]",
+ dc->op2);
+
+ cris_alu_m_alloc_temps(t);
+ insn_len = dec_prep_alu_m(dc, 0, memsize, t[0], t[1]);
+ cris_cc_mask(dc, 0);
+ if (dc->op2 == PR_CCS) {
+ cris_evaluate_flags(dc);
+ if (dc->tb_flags & U_FLAG) {
+ /* User space is not allowed to touch all flags. */
+ tcg_gen_andi_tl(t[1], t[1], 0x39f);
+ tcg_gen_andi_tl(t[0], cpu_PR[PR_CCS], ~0x39f);
+ tcg_gen_or_tl(t[1], t[0], t[1]);
+ }
+ }
+
+ t_gen_mov_preg_TN(dc, dc->op2, t[1]);
+
+ do_postinc(dc, memsize);
+ cris_alu_m_free_temps(t);
+ return insn_len;
+}
+
+static int dec_move_pm(DisasContext *dc)
+{
+ TCGv t0;
+ int memsize;
+
+ memsize = preg_sizes[dc->op2];
+
+ LOG_DIS("move.%c $p%u, [$r%u%s\n",
+ memsize_char(memsize),
+ dc->op2, dc->op1, dc->postinc ? "+]" : "]");
+
+ /* prepare store. Address in T0, value in T1. */
+ if (dc->op2 == PR_CCS)
+ cris_evaluate_flags(dc);
+ t0 = tcg_temp_new();
+ t_gen_mov_TN_preg(t0, dc->op2);
+ cris_flush_cc_state(dc);
+ gen_store(dc, cpu_R[dc->op1], t0, memsize);
+ tcg_temp_free(t0);
+
+ cris_cc_mask(dc, 0);
+ if (dc->postinc)
+ tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], memsize);
+ return 2;
+}
+
+static int dec_movem_mr(DisasContext *dc)
+{
+ TCGv_i64 tmp[16];
+ TCGv tmp32;
+ TCGv addr;
+ int i;
+ int nr = dc->op2 + 1;
+
+ LOG_DIS("movem [$r%u%s, $r%u\n", dc->op1,
+ dc->postinc ? "+]" : "]", dc->op2);
+
+ addr = tcg_temp_new();
+ /* There are probably better ways of doing this. */
+ cris_flush_cc_state(dc);
+ for (i = 0; i < (nr >> 1); i++) {
+ tmp[i] = tcg_temp_new_i64();
+ tcg_gen_addi_tl(addr, cpu_R[dc->op1], i * 8);
+ gen_load64(dc, tmp[i], addr);
+ }
+ if (nr & 1) {
+ tmp32 = tcg_temp_new_i32();
+ tcg_gen_addi_tl(addr, cpu_R[dc->op1], i * 8);
+ gen_load(dc, tmp32, addr, 4, 0);
+ } else
+ TCGV_UNUSED(tmp32);
+ tcg_temp_free(addr);
+
+ for (i = 0; i < (nr >> 1); i++) {
+ tcg_gen_trunc_i64_i32(cpu_R[i * 2], tmp[i]);
+ tcg_gen_shri_i64(tmp[i], tmp[i], 32);
+ tcg_gen_trunc_i64_i32(cpu_R[i * 2 + 1], tmp[i]);
+ tcg_temp_free_i64(tmp[i]);
+ }
+ if (nr & 1) {
+ tcg_gen_mov_tl(cpu_R[dc->op2], tmp32);
+ tcg_temp_free(tmp32);
+ }
+
+ /* writeback the updated pointer value. */
+ if (dc->postinc)
+ tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], nr * 4);
+
+ /* gen_load might want to evaluate the previous insns flags. */
+ cris_cc_mask(dc, 0);
+ return 2;
+}
+
+static int dec_movem_rm(DisasContext *dc)
+{
+ TCGv tmp;
+ TCGv addr;
+ int i;
+
+ LOG_DIS("movem $r%u, [$r%u%s\n", dc->op2, dc->op1,
+ dc->postinc ? "+]" : "]");
+
+ cris_flush_cc_state(dc);
+
+ tmp = tcg_temp_new();
+ addr = tcg_temp_new();
+ tcg_gen_movi_tl(tmp, 4);
+ tcg_gen_mov_tl(addr, cpu_R[dc->op1]);
+ for (i = 0; i <= dc->op2; i++) {
+ /* Displace addr. */
+ /* Perform the store. */
+ gen_store(dc, addr, cpu_R[i], 4);
+ tcg_gen_add_tl(addr, addr, tmp);
+ }
+ if (dc->postinc)
+ tcg_gen_mov_tl(cpu_R[dc->op1], addr);
+ cris_cc_mask(dc, 0);
+ tcg_temp_free(tmp);
+ tcg_temp_free(addr);
+ return 2;
+}
+
+static int dec_move_rm(DisasContext *dc)
+{
+ int memsize;
+
+ memsize = memsize_zz(dc);
+
+ LOG_DIS("move.%c $r%u, [$r%u]\n",
+ memsize_char(memsize), dc->op2, dc->op1);
+
+ /* prepare store. */
+ cris_flush_cc_state(dc);
+ gen_store(dc, cpu_R[dc->op1], cpu_R[dc->op2], memsize);
+
+ if (dc->postinc)
+ tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], memsize);
+ cris_cc_mask(dc, 0);
+ return 2;
+}
+
+static int dec_lapcq(DisasContext *dc)
+{
+ LOG_DIS("lapcq %x, $r%u\n",
+ dc->pc + dc->op1*2, dc->op2);
+ cris_cc_mask(dc, 0);
+ tcg_gen_movi_tl(cpu_R[dc->op2], dc->pc + dc->op1 * 2);
+ return 2;
+}
+
+static int dec_lapc_im(DisasContext *dc)
+{
+ unsigned int rd;
+ int32_t imm;
+ int32_t pc;
+
+ rd = dc->op2;
+
+ cris_cc_mask(dc, 0);
+ imm = cris_fetch(dc, dc->pc + 2, 4, 0);
+ LOG_DIS("lapc 0x%x, $r%u\n", imm + dc->pc, dc->op2);
+
+ pc = dc->pc;
+ pc += imm;
+ tcg_gen_movi_tl(cpu_R[rd], pc);
+ return 6;
+}
+
+/* Jump to special reg. */
+static int dec_jump_p(DisasContext *dc)
+{
+ LOG_DIS("jump $p%u\n", dc->op2);
+
+ if (dc->op2 == PR_CCS)
+ cris_evaluate_flags(dc);
+ t_gen_mov_TN_preg(env_btarget, dc->op2);
+ /* rete will often have low bit set to indicate delayslot. */
+ tcg_gen_andi_tl(env_btarget, env_btarget, ~1);
+ cris_cc_mask(dc, 0);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ return 2;
+}
+
+/* Jump and save. */
+static int dec_jas_r(DisasContext *dc)
+{
+ LOG_DIS("jas $r%u, $p%u\n", dc->op1, dc->op2);
+ cris_cc_mask(dc, 0);
+ /* Store the return address in Pd. */
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->op1]);
+ if (dc->op2 > 15)
+ abort();
+ t_gen_mov_preg_TN(dc, dc->op2, tcg_const_tl(dc->pc + 4));
+
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ return 2;
+}
+
+static int dec_jas_im(DisasContext *dc)
+{
+ uint32_t imm;
+
+ imm = cris_fetch(dc, dc->pc + 2, 4, 0);
+
+ LOG_DIS("jas 0x%x\n", imm);
+ cris_cc_mask(dc, 0);
+ /* Store the return address in Pd. */
+ t_gen_mov_preg_TN(dc, dc->op2, tcg_const_tl(dc->pc + 8));
+
+ dc->jmp_pc = imm;
+ cris_prepare_jmp(dc, JMP_DIRECT);
+ return 6;
+}
+
+static int dec_jasc_im(DisasContext *dc)
+{
+ uint32_t imm;
+
+ imm = cris_fetch(dc, dc->pc + 2, 4, 0);
+
+ LOG_DIS("jasc 0x%x\n", imm);
+ cris_cc_mask(dc, 0);
+ /* Store the return address in Pd. */
+ t_gen_mov_preg_TN(dc, dc->op2, tcg_const_tl(dc->pc + 8 + 4));
+
+ dc->jmp_pc = imm;
+ cris_prepare_jmp(dc, JMP_DIRECT);
+ return 6;
+}
+
+static int dec_jasc_r(DisasContext *dc)
+{
+ LOG_DIS("jasc_r $r%u, $p%u\n", dc->op1, dc->op2);
+ cris_cc_mask(dc, 0);
+ /* Store the return address in Pd. */
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->op1]);
+ t_gen_mov_preg_TN(dc, dc->op2, tcg_const_tl(dc->pc + 4 + 4));
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ return 2;
+}
+
+static int dec_bcc_im(DisasContext *dc)
+{
+ int32_t offset;
+ uint32_t cond = dc->op2;
+
+ offset = cris_fetch(dc, dc->pc + 2, 2, 1);
+
+ LOG_DIS("b%s %d pc=%x dst=%x\n",
+ cc_name(cond), offset,
+ dc->pc, dc->pc + offset);
+
+ cris_cc_mask(dc, 0);
+ /* op2 holds the condition-code. */
+ cris_prepare_cc_branch (dc, offset, cond);
+ return 4;
+}
+
+static int dec_bas_im(DisasContext *dc)
+{
+ int32_t simm;
+
+
+ simm = cris_fetch(dc, dc->pc + 2, 4, 0);
+
+ LOG_DIS("bas 0x%x, $p%u\n", dc->pc + simm, dc->op2);
+ cris_cc_mask(dc, 0);
+ /* Store the return address in Pd. */
+ t_gen_mov_preg_TN(dc, dc->op2, tcg_const_tl(dc->pc + 8));
+
+ dc->jmp_pc = dc->pc + simm;
+ cris_prepare_jmp(dc, JMP_DIRECT);
+ return 6;
+}
+
+static int dec_basc_im(DisasContext *dc)
+{
+ int32_t simm;
+ simm = cris_fetch(dc, dc->pc + 2, 4, 0);
+
+ LOG_DIS("basc 0x%x, $p%u\n", dc->pc + simm, dc->op2);
+ cris_cc_mask(dc, 0);
+ /* Store the return address in Pd. */
+ t_gen_mov_preg_TN(dc, dc->op2, tcg_const_tl(dc->pc + 12));
+
+ dc->jmp_pc = dc->pc + simm;
+ cris_prepare_jmp(dc, JMP_DIRECT);
+ return 6;
+}
+
+static int dec_rfe_etc(DisasContext *dc)
+{
+ cris_cc_mask(dc, 0);
+
+ if (dc->op2 == 15) {
+ t_gen_mov_env_TN(halted, tcg_const_tl(1));
+ tcg_gen_movi_tl(env_pc, dc->pc + 2);
+ t_gen_raise_exception(EXCP_HLT);
+ return 2;
+ }
+
+ switch (dc->op2 & 7) {
+ case 2:
+ /* rfe. */
+ LOG_DIS("rfe\n");
+ cris_evaluate_flags(dc);
+ gen_helper_rfe();
+ dc->is_jmp = DISAS_UPDATE;
+ break;
+ case 5:
+ /* rfn. */
+ LOG_DIS("rfn\n");
+ cris_evaluate_flags(dc);
+ gen_helper_rfn();
+ dc->is_jmp = DISAS_UPDATE;
+ break;
+ case 6:
+ LOG_DIS("break %d\n", dc->op1);
+ cris_evaluate_flags (dc);
+ /* break. */
+ tcg_gen_movi_tl(env_pc, dc->pc + 2);
+
+ /* Breaks start at 16 in the exception vector. */
+ t_gen_mov_env_TN(trap_vector,
+ tcg_const_tl(dc->op1 + 16));
+ t_gen_raise_exception(EXCP_BREAK);
+ dc->is_jmp = DISAS_UPDATE;
+ break;
+ default:
+ printf ("op2=%x\n", dc->op2);
+ BUG();
+ break;
+
+ }
+ return 2;
+}
+
+static int dec_ftag_fidx_d_m(DisasContext *dc)
+{
+ return 2;
+}
+
+static int dec_ftag_fidx_i_m(DisasContext *dc)
+{
+ return 2;
+}
+
+static int dec_null(DisasContext *dc)
+{
+ printf ("unknown insn pc=%x opc=%x op1=%x op2=%x\n",
+ dc->pc, dc->opcode, dc->op1, dc->op2);
+ fflush(NULL);
+ BUG();
+ return 2;
+}
+
+static struct decoder_info {
+ struct {
+ uint32_t bits;
+ uint32_t mask;
+ };
+ int (*dec)(DisasContext *dc);
+} decinfo[] = {
+ /* Order matters here. */
+ {DEC_MOVEQ, dec_moveq},
+ {DEC_BTSTQ, dec_btstq},
+ {DEC_CMPQ, dec_cmpq},
+ {DEC_ADDOQ, dec_addoq},
+ {DEC_ADDQ, dec_addq},
+ {DEC_SUBQ, dec_subq},
+ {DEC_ANDQ, dec_andq},
+ {DEC_ORQ, dec_orq},
+ {DEC_ASRQ, dec_asrq},
+ {DEC_LSLQ, dec_lslq},
+ {DEC_LSRQ, dec_lsrq},
+ {DEC_BCCQ, dec_bccq},
+
+ {DEC_BCC_IM, dec_bcc_im},
+ {DEC_JAS_IM, dec_jas_im},
+ {DEC_JAS_R, dec_jas_r},
+ {DEC_JASC_IM, dec_jasc_im},
+ {DEC_JASC_R, dec_jasc_r},
+ {DEC_BAS_IM, dec_bas_im},
+ {DEC_BASC_IM, dec_basc_im},
+ {DEC_JUMP_P, dec_jump_p},
+ {DEC_LAPC_IM, dec_lapc_im},
+ {DEC_LAPCQ, dec_lapcq},
+
+ {DEC_RFE_ETC, dec_rfe_etc},
+ {DEC_ADDC_MR, dec_addc_mr},
+
+ {DEC_MOVE_MP, dec_move_mp},
+ {DEC_MOVE_PM, dec_move_pm},
+ {DEC_MOVEM_MR, dec_movem_mr},
+ {DEC_MOVEM_RM, dec_movem_rm},
+ {DEC_MOVE_PR, dec_move_pr},
+ {DEC_SCC_R, dec_scc_r},
+ {DEC_SETF, dec_setclrf},
+ {DEC_CLEARF, dec_setclrf},
+
+ {DEC_MOVE_SR, dec_move_sr},
+ {DEC_MOVE_RP, dec_move_rp},
+ {DEC_SWAP_R, dec_swap_r},
+ {DEC_ABS_R, dec_abs_r},
+ {DEC_LZ_R, dec_lz_r},
+ {DEC_MOVE_RS, dec_move_rs},
+ {DEC_BTST_R, dec_btst_r},
+ {DEC_ADDC_R, dec_addc_r},
+
+ {DEC_DSTEP_R, dec_dstep_r},
+ {DEC_XOR_R, dec_xor_r},
+ {DEC_MCP_R, dec_mcp_r},
+ {DEC_CMP_R, dec_cmp_r},
+
+ {DEC_ADDI_R, dec_addi_r},
+ {DEC_ADDI_ACR, dec_addi_acr},
+
+ {DEC_ADD_R, dec_add_r},
+ {DEC_SUB_R, dec_sub_r},
+
+ {DEC_ADDU_R, dec_addu_r},
+ {DEC_ADDS_R, dec_adds_r},
+ {DEC_SUBU_R, dec_subu_r},
+ {DEC_SUBS_R, dec_subs_r},
+ {DEC_LSL_R, dec_lsl_r},
+
+ {DEC_AND_R, dec_and_r},
+ {DEC_OR_R, dec_or_r},
+ {DEC_BOUND_R, dec_bound_r},
+ {DEC_ASR_R, dec_asr_r},
+ {DEC_LSR_R, dec_lsr_r},
+
+ {DEC_MOVU_R, dec_movu_r},
+ {DEC_MOVS_R, dec_movs_r},
+ {DEC_NEG_R, dec_neg_r},
+ {DEC_MOVE_R, dec_move_r},
+
+ {DEC_FTAG_FIDX_I_M, dec_ftag_fidx_i_m},
+ {DEC_FTAG_FIDX_D_M, dec_ftag_fidx_d_m},
+
+ {DEC_MULS_R, dec_muls_r},
+ {DEC_MULU_R, dec_mulu_r},
+
+ {DEC_ADDU_M, dec_addu_m},
+ {DEC_ADDS_M, dec_adds_m},
+ {DEC_SUBU_M, dec_subu_m},
+ {DEC_SUBS_M, dec_subs_m},
+
+ {DEC_CMPU_M, dec_cmpu_m},
+ {DEC_CMPS_M, dec_cmps_m},
+ {DEC_MOVU_M, dec_movu_m},
+ {DEC_MOVS_M, dec_movs_m},
+
+ {DEC_CMP_M, dec_cmp_m},
+ {DEC_ADDO_M, dec_addo_m},
+ {DEC_BOUND_M, dec_bound_m},
+ {DEC_ADD_M, dec_add_m},
+ {DEC_SUB_M, dec_sub_m},
+ {DEC_AND_M, dec_and_m},
+ {DEC_OR_M, dec_or_m},
+ {DEC_MOVE_RM, dec_move_rm},
+ {DEC_TEST_M, dec_test_m},
+ {DEC_MOVE_MR, dec_move_mr},
+
+ {{0, 0}, dec_null}
+};
+
+static unsigned int crisv32_decoder(DisasContext *dc)
+{
+ int insn_len = 2;
+ int i;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(dc->pc);
+
+ /* Load a halfword onto the instruction register. */
+ dc->ir = cris_fetch(dc, dc->pc, 2, 0);
+
+ /* Now decode it. */
+ dc->opcode = EXTRACT_FIELD(dc->ir, 4, 11);
+ dc->op1 = EXTRACT_FIELD(dc->ir, 0, 3);
+ dc->op2 = EXTRACT_FIELD(dc->ir, 12, 15);
+ dc->zsize = EXTRACT_FIELD(dc->ir, 4, 4);
+ dc->zzsize = EXTRACT_FIELD(dc->ir, 4, 5);
+ dc->postinc = EXTRACT_FIELD(dc->ir, 10, 10);
+
+ /* Large switch for all insns. */
+ for (i = 0; i < ARRAY_SIZE(decinfo); i++) {
+ if ((dc->opcode & decinfo[i].mask) == decinfo[i].bits)
+ {
+ insn_len = decinfo[i].dec(dc);
+ break;
+ }
+ }
+
+#if !defined(CONFIG_USER_ONLY)
+ /* Single-stepping ? */
+ if (dc->tb_flags & S_FLAG) {
+ int l1;
+
+ l1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_PR[PR_SPC], dc->pc, l1);
+ /* We treat SPC as a break with an odd trap vector. */
+ cris_evaluate_flags (dc);
+ t_gen_mov_env_TN(trap_vector, tcg_const_tl(3));
+ tcg_gen_movi_tl(env_pc, dc->pc + insn_len);
+ tcg_gen_movi_tl(cpu_PR[PR_SPC], dc->pc + insn_len);
+ t_gen_raise_exception(EXCP_BREAK);
+ gen_set_label(l1);
+ }
+#endif
+ return insn_len;
+}
+
+static void check_breakpoint(CPUState *env, DisasContext *dc)
+{
+ CPUBreakpoint *bp;
+
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == dc->pc) {
+ cris_evaluate_flags (dc);
+ tcg_gen_movi_tl(env_pc, dc->pc);
+ t_gen_raise_exception(EXCP_DEBUG);
+ dc->is_jmp = DISAS_UPDATE;
+ }
+ }
+ }
+}
+
+#include "translate_v10.c"
+
+/*
+ * Delay slots on QEMU/CRIS.
+ *
+ * If an exception hits on a delayslot, the core will let ERP (the Exception
+ * Return Pointer) point to the branch (the previous) insn and set the lsb to
+ * to give SW a hint that the exception actually hit on the dslot.
+ *
+ * CRIS expects all PC addresses to be 16-bit aligned. The lsb is ignored by
+ * the core and any jmp to an odd addresses will mask off that lsb. It is
+ * simply there to let sw know there was an exception on a dslot.
+ *
+ * When the software returns from an exception, the branch will re-execute.
+ * On QEMU care needs to be taken when a branch+delayslot sequence is broken
+ * and the branch and delayslot dont share pages.
+ *
+ * The TB contaning the branch insn will set up env->btarget and evaluate
+ * env->btaken. When the translation loop exits we will note that the branch
+ * sequence is broken and let env->dslot be the size of the branch insn (those
+ * vary in length).
+ *
+ * The TB contaning the delayslot will have the PC of its real insn (i.e no lsb
+ * set). It will also expect to have env->dslot setup with the size of the
+ * delay slot so that env->pc - env->dslot point to the branch insn. This TB
+ * will execute the dslot and take the branch, either to btarget or just one
+ * insn ahead.
+ *
+ * When exceptions occur, we check for env->dslot in do_interrupt to detect
+ * broken branch sequences and setup $erp accordingly (i.e let it point to the
+ * branch and set lsb). Then env->dslot gets cleared so that the exception
+ * handler can enter. When returning from exceptions (jump $erp) the lsb gets
+ * masked off and we will reexecute the branch insn.
+ *
+ */
+
+/* generate intermediate code for basic block 'tb'. */
+static void
+gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ uint16_t *gen_opc_end;
+ uint32_t pc_start;
+ unsigned int insn_len;
+ int j, lj;
+ struct DisasContext ctx;
+ struct DisasContext *dc = &ctx;
+ uint32_t next_page_start;
+ target_ulong npc;
+ int num_insns;
+ int max_insns;
+
+ qemu_log_try_set_file(stderr);
+
+ if (env->pregs[PR_VR] == 32) {
+ dc->decoder = crisv32_decoder;
+ dc->clear_locked_irq = 0;
+ } else {
+ dc->decoder = crisv10_decoder;
+ dc->clear_locked_irq = 1;
+ }
+
+ /* Odd PC indicates that branch is rexecuting due to exception in the
+ * delayslot, like in real hw.
+ */
+ pc_start = tb->pc & ~1;
+ dc->env = env;
+ dc->tb = tb;
+
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ dc->is_jmp = DISAS_NEXT;
+ dc->ppc = pc_start;
+ dc->pc = pc_start;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->flags_uptodate = 1;
+ dc->flagx_known = 1;
+ dc->flags_x = tb->flags & X_FLAG;
+ dc->cc_x_uptodate = 0;
+ dc->cc_mask = 0;
+ dc->update_cc = 0;
+ dc->clear_prefix = 0;
+
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ dc->cc_size_uptodate = -1;
+
+ /* Decode TB flags. */
+ dc->tb_flags = tb->flags & (S_FLAG | P_FLAG | U_FLAG \
+ | X_FLAG | PFIX_FLAG);
+ dc->delayed_branch = !!(tb->flags & 7);
+ if (dc->delayed_branch)
+ dc->jmp = JMP_INDIRECT;
+ else
+ dc->jmp = JMP_NOJMP;
+
+ dc->cpustate_changed = 0;
+
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log(
+ "srch=%d pc=%x %x flg=%" PRIx64 " bt=%x ds=%u ccs=%x\n"
+ "pid=%x usp=%x\n"
+ "%x.%x.%x.%x\n"
+ "%x.%x.%x.%x\n"
+ "%x.%x.%x.%x\n"
+ "%x.%x.%x.%x\n",
+ search_pc, dc->pc, dc->ppc,
+ (uint64_t)tb->flags,
+ env->btarget, (unsigned)tb->flags & 7,
+ env->pregs[PR_CCS],
+ env->pregs[PR_PID], env->pregs[PR_USP],
+ env->regs[0], env->regs[1], env->regs[2], env->regs[3],
+ env->regs[4], env->regs[5], env->regs[6], env->regs[7],
+ env->regs[8], env->regs[9],
+ env->regs[10], env->regs[11],
+ env->regs[12], env->regs[13],
+ env->regs[14], env->regs[15]);
+ qemu_log("--------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ }
+
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
+ do
+ {
+ check_breakpoint(env, dc);
+
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j)
+ gen_opc_instr_start[lj++] = 0;
+ }
+ if (dc->delayed_branch == 1)
+ gen_opc_pc[lj] = dc->ppc | 1;
+ else
+ gen_opc_pc[lj] = dc->pc;
+ gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
+ }
+
+ /* Pretty disas. */
+ LOG_DIS("%8.8x:\t", dc->pc);
+
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+ dc->clear_x = 1;
+
+ insn_len = dc->decoder(dc);
+ dc->ppc = dc->pc;
+ dc->pc += insn_len;
+ if (dc->clear_x)
+ cris_clear_x_flag(dc);
+
+ num_insns++;
+ /* Check for delayed branches here. If we do it before
+ actually generating any host code, the simulator will just
+ loop doing nothing for on this program location. */
+ if (dc->delayed_branch) {
+ dc->delayed_branch--;
+ if (dc->delayed_branch == 0)
+ {
+ if (tb->flags & 7)
+ t_gen_mov_env_TN(dslot,
+ tcg_const_tl(0));
+ if (dc->cpustate_changed || !dc->flagx_known
+ || (dc->flags_x != (tb->flags & X_FLAG))) {
+ cris_store_direct_jmp(dc);
+ }
+
+ if (dc->clear_locked_irq) {
+ dc->clear_locked_irq = 0;
+ t_gen_mov_env_TN(locked_irq,
+ tcg_const_tl(0));
+ }
+
+ if (dc->jmp == JMP_DIRECT_CC) {
+ int l1;
+
+ l1 = gen_new_label();
+ cris_evaluate_flags(dc);
+
+ /* Conditional jmp. */
+ tcg_gen_brcondi_tl(TCG_COND_EQ,
+ env_btaken, 0, l1);
+ gen_goto_tb(dc, 1, dc->jmp_pc);
+ gen_set_label(l1);
+ gen_goto_tb(dc, 0, dc->pc);
+ dc->is_jmp = DISAS_TB_JUMP;
+ dc->jmp = JMP_NOJMP;
+ } else if (dc->jmp == JMP_DIRECT) {
+ cris_evaluate_flags(dc);
+ gen_goto_tb(dc, 0, dc->jmp_pc);
+ dc->is_jmp = DISAS_TB_JUMP;
+ dc->jmp = JMP_NOJMP;
+ } else {
+ t_gen_cc_jmp(env_btarget,
+ tcg_const_tl(dc->pc));
+ dc->is_jmp = DISAS_JUMP;
+ }
+ break;
+ }
+ }
+
+ /* If we are rexecuting a branch due to exceptions on
+ delay slots dont break. */
+ if (!(tb->pc & 1) && env->singlestep_enabled)
+ break;
+ } while (!dc->is_jmp && !dc->cpustate_changed
+ && gen_opc_ptr < gen_opc_end
+ && !singlestep
+ && (dc->pc < next_page_start)
+ && num_insns < max_insns);
+
+ if (dc->clear_locked_irq)
+ t_gen_mov_env_TN(locked_irq, tcg_const_tl(0));
+
+ npc = dc->pc;
+
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ /* Force an update if the per-tb cpu state has changed. */
+ if (dc->is_jmp == DISAS_NEXT
+ && (dc->cpustate_changed || !dc->flagx_known
+ || (dc->flags_x != (tb->flags & X_FLAG)))) {
+ dc->is_jmp = DISAS_UPDATE;
+ tcg_gen_movi_tl(env_pc, npc);
+ }
+ /* Broken branch+delayslot sequence. */
+ if (dc->delayed_branch == 1) {
+ /* Set env->dslot to the size of the branch insn. */
+ t_gen_mov_env_TN(dslot, tcg_const_tl(dc->pc - dc->ppc));
+ cris_store_direct_jmp(dc);
+ }
+
+ cris_evaluate_flags (dc);
+
+ if (unlikely(env->singlestep_enabled)) {
+ if (dc->is_jmp == DISAS_NEXT)
+ tcg_gen_movi_tl(env_pc, npc);
+ t_gen_raise_exception(EXCP_DEBUG);
+ } else {
+ switch(dc->is_jmp) {
+ case DISAS_NEXT:
+ gen_goto_tb(dc, 1, npc);
+ break;
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ /* indicate that the hash table must be used
+ to find the next TB */
+ tcg_gen_exit_tb(0);
+ break;
+ case DISAS_SWI:
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ }
+ }
+ 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;
+ }
+
+#ifdef DEBUG_DISAS
+#if !DISAS_CRIS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ log_target_disas(pc_start, dc->pc - pc_start,
+ dc->env->pregs[PR_VR]);
+ qemu_log("\nisize=%d osize=%td\n",
+ dc->pc - pc_start, gen_opc_ptr - gen_opc_buf);
+ }
+#endif
+#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 cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+ uint32_t srs;
+
+ if (!env || !f)
+ return;
+
+ cpu_fprintf(f, "PC=%x CCS=%x btaken=%d btarget=%x\n"
+ "cc_op=%d cc_src=%d cc_dest=%d cc_result=%x cc_mask=%x\n",
+ env->pc, env->pregs[PR_CCS], env->btaken, env->btarget,
+ env->cc_op,
+ env->cc_src, env->cc_dest, env->cc_result, env->cc_mask);
+
+
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "%s=%8.8x ",regnames[i], env->regs[i]);
+ if ((i + 1) % 4 == 0)
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "\nspecial regs:\n");
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "%s=%8.8x ", pregnames[i], env->pregs[i]);
+ if ((i + 1) % 4 == 0)
+ cpu_fprintf(f, "\n");
+ }
+ srs = env->pregs[PR_SRS];
+ cpu_fprintf(f, "\nsupport function regs bank %x:\n", srs);
+ if (srs < 256) {
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "s%2.2d=%8.8x ",
+ i, env->sregs[srs][i]);
+ if ((i + 1) % 4 == 0)
+ cpu_fprintf(f, "\n");
+ }
+ }
+ cpu_fprintf(f, "\n\n");
+
+}
+
+struct
+{
+ uint32_t vr;
+ const char *name;
+} cris_cores[] = {
+ {8, "crisv8"},
+ {9, "crisv9"},
+ {10, "crisv10"},
+ {11, "crisv11"},
+ {32, "crisv32"},
+};
+
+void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ unsigned int i;
+
+ (*cpu_fprintf)(f, "Available CPUs:\n");
+ for (i = 0; i < ARRAY_SIZE(cris_cores); i++) {
+ (*cpu_fprintf)(f, " %s\n", cris_cores[i].name);
+ }
+}
+
+static uint32_t vr_by_name(const char *name)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(cris_cores); i++) {
+ if (strcmp(name, cris_cores[i].name) == 0) {
+ return cris_cores[i].vr;
+ }
+ }
+ return 32;
+}
+
+CPUCRISState *cpu_cris_init (const char *cpu_model)
+{
+ CPUCRISState *env;
+ static int tcg_initialized = 0;
+ int i;
+
+ env = qemu_mallocz(sizeof(CPUCRISState));
+
+ env->pregs[PR_VR] = vr_by_name(cpu_model);
+ cpu_exec_init(env);
+ cpu_reset(env);
+ qemu_init_vcpu(env);
+
+ if (tcg_initialized)
+ return env;
+
+ tcg_initialized = 1;
+
+#define GEN_HELPER 2
+#include "helper.h"
+
+ if (env->pregs[PR_VR] < 32) {
+ cpu_crisv10_init(env);
+ return env;
+ }
+
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+ cc_x = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_x), "cc_x");
+ cc_src = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_src), "cc_src");
+ cc_dest = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_dest),
+ "cc_dest");
+ cc_result = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_result),
+ "cc_result");
+ cc_op = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_op), "cc_op");
+ cc_size = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_size),
+ "cc_size");
+ cc_mask = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_mask),
+ "cc_mask");
+
+ env_pc = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, pc),
+ "pc");
+ env_btarget = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btarget),
+ "btarget");
+ env_btaken = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btaken),
+ "btaken");
+ for (i = 0; i < 16; i++) {
+ cpu_R[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, regs[i]),
+ regnames[i]);
+ }
+ for (i = 0; i < 16; i++) {
+ cpu_PR[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, pregs[i]),
+ pregnames[i]);
+ }
+
+ return env;
+}
+
+void cpu_reset (CPUCRISState *env)
+{
+ uint32_t vr;
+
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ vr = env->pregs[PR_VR];
+ memset(env, 0, offsetof(CPUCRISState, breakpoints));
+ env->pregs[PR_VR] = vr;
+ tlb_flush(env, 1);
+
+#if defined(CONFIG_USER_ONLY)
+ /* start in user mode with interrupts enabled. */
+ env->pregs[PR_CCS] |= U_FLAG | I_FLAG | P_FLAG;
+#else
+ cris_mmu_init(env);
+ env->pregs[PR_CCS] = 0;
+#endif
+}
+
+void gen_pc_load(CPUState *env, struct TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->pc = gen_opc_pc[pc_pos];
+}
diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c
new file mode 100644
index 0000000..41db158
--- /dev/null
+++ b/target-cris/translate_v10.c
@@ -0,0 +1,1245 @@
+/*
+ * CRISv10 emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2010 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * 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 "crisv10-decode.h"
+
+static const char *regnames_v10[] =
+{
+ "$r0", "$r1", "$r2", "$r3",
+ "$r4", "$r5", "$r6", "$r7",
+ "$r8", "$r9", "$r10", "$r11",
+ "$r12", "$r13", "$sp", "$pc",
+};
+
+static const char *pregnames_v10[] =
+{
+ "$bz", "$vr", "$p2", "$p3",
+ "$wz", "$ccr", "$p6-prefix", "$mof",
+ "$dz", "$ibr", "$irp", "$srp",
+ "$bar", "$dccr", "$brp", "$usp",
+};
+
+/* We need this table to handle preg-moves with implicit width. */
+static int preg_sizes_v10[] = {
+ 1, /* bz. */
+ 1, /* vr. */
+ 1, /* pid. */
+ 1, /* srs. */
+ 2, /* wz. */
+ 2, 2, 4,
+ 4, 4, 4, 4,
+ 4, 4, 4, 4,
+};
+
+static inline int dec10_size(unsigned int size)
+{
+ size++;
+ if (size == 3)
+ size++;
+ return size;
+}
+
+static inline void cris_illegal_insn(DisasContext *dc)
+{
+ qemu_log("illegal insn at pc=%x\n", dc->pc);
+ t_gen_raise_exception(EXCP_BREAK);
+}
+
+/* Prefix flag and register are used to handle the more complex
+ addressing modes. */
+static void cris_set_prefix(DisasContext *dc)
+{
+ dc->clear_prefix = 0;
+ dc->tb_flags |= PFIX_FLAG;
+ tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], PFIX_FLAG);
+
+ /* prefix insns dont clear the x flag. */
+ dc->clear_x = 0;
+ cris_lock_irq(dc);
+}
+
+static void crisv10_prepare_memaddr(DisasContext *dc,
+ TCGv addr, unsigned int size)
+{
+ if (dc->tb_flags & PFIX_FLAG) {
+ tcg_gen_mov_tl(addr, cpu_PR[PR_PREFIX]);
+ } else {
+ tcg_gen_mov_tl(addr, cpu_R[dc->src]);
+ }
+}
+
+static unsigned int crisv10_post_memaddr(DisasContext *dc, unsigned int size)
+{
+ unsigned int insn_len = 0;
+
+ if (dc->tb_flags & PFIX_FLAG) {
+ if (dc->mode == CRISV10_MODE_AUTOINC) {
+ tcg_gen_mov_tl(cpu_R[dc->src], cpu_PR[PR_PREFIX]);
+ }
+ } else {
+ if (dc->mode == CRISV10_MODE_AUTOINC) {
+ if (dc->src == 15) {
+ insn_len += size & ~1;
+ } else {
+ tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], size);
+ }
+ }
+ }
+ return insn_len;
+}
+
+static int dec10_prep_move_m(DisasContext *dc, int s_ext, int memsize,
+ TCGv dst)
+{
+ unsigned int rs;
+ uint32_t imm;
+ int is_imm;
+ int insn_len = 0;
+
+ rs = dc->src;
+ is_imm = rs == 15 && !(dc->tb_flags & PFIX_FLAG);
+ LOG_DIS("rs=%d rd=%d is_imm=%d mode=%d pfix=%d\n",
+ rs, dc->dst, is_imm, dc->mode, dc->tb_flags & PFIX_FLAG);
+
+ /* Load [$rs] onto T1. */
+ if (is_imm) {
+ if (memsize != 4) {
+ if (s_ext) {
+ if (memsize == 1)
+ imm = ldsb_code(dc->pc + 2);
+ else
+ imm = ldsw_code(dc->pc + 2);
+ } else {
+ if (memsize == 1)
+ imm = ldub_code(dc->pc + 2);
+ else
+ imm = lduw_code(dc->pc + 2);
+ }
+ } else
+ imm = ldl_code(dc->pc + 2);
+
+ tcg_gen_movi_tl(dst, imm);
+
+ if (dc->mode == CRISV10_MODE_AUTOINC) {
+ insn_len += memsize;
+ if (memsize == 1)
+ insn_len++;
+ tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len);
+ }
+ } else {
+ TCGv addr;
+
+ addr = tcg_temp_new();
+ cris_flush_cc_state(dc);
+ crisv10_prepare_memaddr(dc, addr, memsize);
+ gen_load(dc, dst, addr, memsize, 0);
+ if (s_ext)
+ t_gen_sext(dst, dst, memsize);
+ else
+ t_gen_zext(dst, dst, memsize);
+ insn_len += crisv10_post_memaddr(dc, memsize);
+ tcg_temp_free(addr);
+ }
+
+ if (dc->mode == CRISV10_MODE_INDIRECT && (dc->tb_flags & PFIX_FLAG)) {
+ dc->dst = dc->src;
+ }
+ return insn_len;
+}
+
+static unsigned int dec10_quick_imm(DisasContext *dc)
+{
+ int32_t imm, simm;
+ int op;
+
+ /* sign extend. */
+ imm = dc->ir & ((1 << 6) - 1);
+ simm = (int8_t) (imm << 2);
+ simm >>= 2;
+ switch (dc->opcode) {
+ case CRISV10_QIMM_BDAP_R0:
+ case CRISV10_QIMM_BDAP_R1:
+ case CRISV10_QIMM_BDAP_R2:
+ case CRISV10_QIMM_BDAP_R3:
+ simm = (int8_t)dc->ir;
+ LOG_DIS("bdap %d $r%d\n", simm, dc->dst);
+ LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n",
+ dc->pc, dc->mode, dc->opcode, dc->src, dc->dst);
+ cris_set_prefix(dc);
+ if (dc->dst == 15) {
+ tcg_gen_movi_tl(cpu_PR[PR_PREFIX], dc->pc + 2 + simm);
+ } else {
+ tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm);
+ }
+ break;
+
+ case CRISV10_QIMM_MOVEQ:
+ LOG_DIS("moveq %d, $r%d\n", simm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(simm), 4);
+ break;
+ case CRISV10_QIMM_CMPQ:
+ LOG_DIS("cmpq %d, $r%d\n", simm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(simm), 4);
+ break;
+ case CRISV10_QIMM_ADDQ:
+ LOG_DIS("addq %d, $r%d\n", imm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_ADD, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(imm), 4);
+ break;
+ case CRISV10_QIMM_ANDQ:
+ LOG_DIS("andq %d, $r%d\n", simm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_AND, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(simm), 4);
+ break;
+ case CRISV10_QIMM_ASHQ:
+ LOG_DIS("ashq %d, $r%d\n", simm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ op = imm & (1 << 5);
+ imm &= 0x1f;
+ if (op) {
+ cris_alu(dc, CC_OP_ASR, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(imm), 4);
+ } else {
+ /* BTST */
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ gen_helper_btst(cpu_PR[PR_CCS], cpu_R[dc->dst],
+ tcg_const_tl(imm), cpu_PR[PR_CCS]);
+ }
+ break;
+ case CRISV10_QIMM_LSHQ:
+ LOG_DIS("lshq %d, $r%d\n", simm, dc->dst);
+
+ op = CC_OP_LSL;
+ if (imm & (1 << 5)) {
+ op = CC_OP_LSR;
+ }
+ imm &= 0x1f;
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, op, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(imm), 4);
+ break;
+ case CRISV10_QIMM_SUBQ:
+ LOG_DIS("subq %d, $r%d\n", imm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_SUB, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(imm), 4);
+ break;
+ case CRISV10_QIMM_ORQ:
+ LOG_DIS("andq %d, $r%d\n", simm, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_OR, cpu_R[dc->dst],
+ cpu_R[dc->dst], tcg_const_tl(simm), 4);
+ break;
+
+ case CRISV10_QIMM_BCC_R0:
+ if (!dc->ir) {
+ cpu_abort(dc->env, "opcode zero\n");
+ }
+ case CRISV10_QIMM_BCC_R1:
+ case CRISV10_QIMM_BCC_R2:
+ case CRISV10_QIMM_BCC_R3:
+ imm = dc->ir & 0xff;
+ /* bit 0 is a sign bit. */
+ if (imm & 1) {
+ imm |= 0xffffff00; /* sign extend. */
+ imm &= ~1; /* get rid of the sign bit. */
+ }
+ imm += 2;
+ LOG_DIS("b%s %d\n", cc_name(dc->cond), imm);
+
+ cris_cc_mask(dc, 0);
+ cris_prepare_cc_branch(dc, imm, dc->cond);
+ break;
+
+ default:
+ LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n",
+ dc->pc, dc->mode, dc->opcode, dc->src, dc->dst);
+ cpu_abort(dc->env, "Unhandled quickimm\n");
+ break;
+ }
+ return 2;
+}
+
+static unsigned int dec10_setclrf(DisasContext *dc)
+{
+ uint32_t flags;
+ unsigned int set = ~dc->opcode & 1;
+
+ flags = EXTRACT_FIELD(dc->ir, 0, 3)
+ | (EXTRACT_FIELD(dc->ir, 12, 15) << 4);
+ LOG_DIS("%s set=%d flags=%x\n", __func__, set, flags);
+
+
+ if (flags & X_FLAG) {
+ dc->flagx_known = 1;
+ if (set)
+ dc->flags_x = X_FLAG;
+ else
+ dc->flags_x = 0;
+ }
+
+ cris_evaluate_flags (dc);
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ cris_update_cc_x(dc);
+ tcg_gen_movi_tl(cc_op, dc->cc_op);
+
+ if (set) {
+ tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags);
+ } else {
+ tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~flags);
+ }
+
+ dc->flags_uptodate = 1;
+ dc->clear_x = 0;
+ cris_lock_irq(dc);
+ return 2;
+}
+
+static inline void dec10_reg_prep_sext(DisasContext *dc, int size, int sext,
+ TCGv dd, TCGv ds, TCGv sd, TCGv ss)
+{
+ if (sext) {
+ t_gen_sext(dd, sd, size);
+ t_gen_sext(ds, ss, size);
+ } else {
+ t_gen_zext(dd, sd, size);
+ t_gen_zext(ds, ss, size);
+ }
+}
+
+static void dec10_reg_alu(DisasContext *dc, int op, int size, int sext)
+{
+ TCGv t[2];
+
+ t[0] = tcg_temp_new();
+ t[1] = tcg_temp_new();
+ dec10_reg_prep_sext(dc, size, sext,
+ t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]);
+
+ if (op == CC_OP_LSL || op == CC_OP_LSR || op == CC_OP_ASR) {
+ tcg_gen_andi_tl(t[1], t[1], 63);
+ }
+
+ assert(dc->dst != 15);
+ cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], size);
+ tcg_temp_free(t[0]);
+ tcg_temp_free(t[1]);
+}
+
+static void dec10_reg_bound(DisasContext *dc, int size)
+{
+ TCGv t;
+
+ t = tcg_temp_local_new();
+ t_gen_zext(t, cpu_R[dc->src], size);
+ cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[dc->dst], t, 4);
+ tcg_temp_free(t);
+}
+
+static void dec10_reg_mul(DisasContext *dc, int size, int sext)
+{
+ int op = sext ? CC_OP_MULS : CC_OP_MULU;
+ TCGv t[2];
+
+ t[0] = tcg_temp_new();
+ t[1] = tcg_temp_new();
+ dec10_reg_prep_sext(dc, size, sext,
+ t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]);
+
+ cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], 4);
+
+ tcg_temp_free(t[0]);
+ tcg_temp_free(t[1]);
+}
+
+
+static void dec10_reg_movs(DisasContext *dc)
+{
+ int size = (dc->size & 1) + 1;
+ TCGv t;
+
+ LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+
+ t = tcg_temp_new();
+ if (dc->ir & 32)
+ t_gen_sext(t, cpu_R[dc->src], size);
+ else
+ t_gen_zext(t, cpu_R[dc->src], size);
+
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, 4);
+ tcg_temp_free(t);
+}
+
+static void dec10_reg_alux(DisasContext *dc, int op)
+{
+ int size = (dc->size & 1) + 1;
+ TCGv t;
+
+ LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+
+ t = tcg_temp_new();
+ if (dc->ir & 32)
+ t_gen_sext(t, cpu_R[dc->src], size);
+ else
+ t_gen_zext(t, cpu_R[dc->src], size);
+
+ cris_alu(dc, op, cpu_R[dc->dst], cpu_R[dc->dst], t, 4);
+ tcg_temp_free(t);
+}
+
+static void dec10_reg_mov_pr(DisasContext *dc)
+{
+ LOG_DIS("move p%d r%d sz=%d\n", dc->dst, dc->src, preg_sizes_v10[dc->dst]);
+ cris_lock_irq(dc);
+ if (dc->src == 15) {
+ tcg_gen_mov_tl(env_btarget, cpu_PR[dc->dst]);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ return;
+ }
+ if (dc->dst == PR_CCS) {
+ cris_evaluate_flags(dc);
+ }
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src],
+ cpu_R[dc->src], cpu_PR[dc->dst], preg_sizes_v10[dc->dst]);
+}
+
+static void dec10_reg_abs(DisasContext *dc)
+{
+ TCGv t0;
+
+ LOG_DIS("abs $r%u, $r%u\n", dc->src, dc->dst);
+
+ assert(dc->dst != 15);
+ t0 = tcg_temp_new();
+ tcg_gen_sari_tl(t0, cpu_R[dc->src], 31);
+ tcg_gen_xor_tl(cpu_R[dc->dst], cpu_R[dc->src], t0);
+ tcg_gen_sub_tl(t0, cpu_R[dc->dst], t0);
+
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t0, 4);
+ tcg_temp_free(t0);
+}
+
+static void dec10_reg_swap(DisasContext *dc)
+{
+ TCGv t0;
+
+ LOG_DIS("not $r%d, $r%d\n", dc->src, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ t0 = tcg_temp_new();
+ t_gen_mov_TN_reg(t0, dc->src);
+ if (dc->dst & 8)
+ tcg_gen_not_tl(t0, t0);
+ if (dc->dst & 4)
+ t_gen_swapw(t0, t0);
+ if (dc->dst & 2)
+ t_gen_swapb(t0, t0);
+ if (dc->dst & 1)
+ t_gen_swapr(t0, t0);
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src], cpu_R[dc->src], t0, 4);
+ tcg_temp_free(t0);
+}
+
+static void dec10_reg_scc(DisasContext *dc)
+{
+ int cond = dc->dst;
+
+ LOG_DIS("s%s $r%u\n", cc_name(cond), dc->src);
+
+ if (cond != CC_A)
+ {
+ int l1;
+
+ gen_tst_cc (dc, cpu_R[dc->src], cond);
+ l1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->src], 0, l1);
+ tcg_gen_movi_tl(cpu_R[dc->src], 1);
+ gen_set_label(l1);
+ } else {
+ tcg_gen_movi_tl(cpu_R[dc->src], 1);
+ }
+
+ cris_cc_mask(dc, 0);
+}
+
+static unsigned int dec10_reg(DisasContext *dc)
+{
+ TCGv t;
+ unsigned int insn_len = 2;
+ unsigned int size = dec10_size(dc->size);
+ unsigned int tmp;
+
+ if (dc->size != 3) {
+ switch (dc->opcode) {
+ case CRISV10_REG_MOVE_R:
+ LOG_DIS("move.%d $r%d, $r%d\n", dc->size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_MOVE, size, 0);
+ if (dc->dst == 15) {
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch = 1;
+ }
+ break;
+ case CRISV10_REG_MOVX:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_movs(dc);
+ break;
+ case CRISV10_REG_ADDX:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alux(dc, CC_OP_ADD);
+ break;
+ case CRISV10_REG_SUBX:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alux(dc, CC_OP_SUB);
+ break;
+ case CRISV10_REG_ADD:
+ LOG_DIS("add $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_ADD, size, 0);
+ break;
+ case CRISV10_REG_SUB:
+ LOG_DIS("sub $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_SUB, size, 0);
+ break;
+ case CRISV10_REG_CMP:
+ LOG_DIS("cmp $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_CMP, size, 0);
+ break;
+ case CRISV10_REG_BOUND:
+ LOG_DIS("bound $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_bound(dc, size);
+ break;
+ case CRISV10_REG_AND:
+ LOG_DIS("and $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_AND, size, 0);
+ break;
+ case CRISV10_REG_ADDI:
+ if (dc->src == 15) {
+ /* nop. */
+ return 2;
+ }
+ t = tcg_temp_new();
+ LOG_DIS("addi r%d r%d size=%d\n", dc->src, dc->dst, dc->size);
+ tcg_gen_shli_tl(t, cpu_R[dc->dst], dc->size & 3);
+ tcg_gen_add_tl(cpu_R[dc->src], cpu_R[dc->src], t);
+ tcg_temp_free(t);
+ break;
+ case CRISV10_REG_LSL:
+ LOG_DIS("lsl $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_LSL, size, 0);
+ break;
+ case CRISV10_REG_LSR:
+ LOG_DIS("lsr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_LSR, size, 0);
+ break;
+ case CRISV10_REG_ASR:
+ LOG_DIS("asr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_ASR, size, 1);
+ break;
+ case CRISV10_REG_OR:
+ LOG_DIS("or $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_OR, size, 0);
+ break;
+ case CRISV10_REG_NEG:
+ LOG_DIS("neg $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_NEG, size, 0);
+ break;
+ case CRISV10_REG_BIAP:
+ LOG_DIS("BIAP pc=%x reg %d r%d r%d size=%d\n", dc->pc,
+ dc->opcode, dc->src, dc->dst, size);
+ switch (size) {
+ case 4: tmp = 2; break;
+ case 2: tmp = 1; break;
+ case 1: tmp = 0; break;
+ default:
+ cpu_abort(dc->env, "Unhandled BIAP");
+ break;
+ }
+
+ t = tcg_temp_new();
+ tcg_gen_shli_tl(t, cpu_R[dc->dst], tmp);
+ if (dc->src == 15) {
+ tcg_gen_addi_tl(cpu_PR[PR_PREFIX], t, ((dc->pc +2)| 1) + 1);
+ } else {
+ tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_R[dc->src], t);
+ }
+ tcg_temp_free(t);
+ cris_set_prefix(dc);
+ break;
+
+ default:
+ LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc,
+ dc->opcode, dc->src, dc->dst);
+ cpu_abort(dc->env, "Unhandled opcode");
+ break;
+ }
+ } else {
+ switch (dc->opcode) {
+ case CRISV10_REG_MOVX:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_movs(dc);
+ break;
+ case CRISV10_REG_ADDX:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alux(dc, CC_OP_ADD);
+ break;
+ case CRISV10_REG_SUBX:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alux(dc, CC_OP_SUB);
+ break;
+ case CRISV10_REG_MOVE_SPR_R:
+ cris_evaluate_flags(dc);
+ cris_cc_mask(dc, 0);
+ dec10_reg_mov_pr(dc);
+ break;
+ case CRISV10_REG_MOVE_R_SPR:
+ LOG_DIS("move r%d p%d\n", dc->src, dc->dst);
+ cris_evaluate_flags(dc);
+ if (dc->src != 11) /* fast for srp. */
+ dc->cpustate_changed = 1;
+ t_gen_mov_preg_TN(dc, dc->dst, cpu_R[dc->src]);
+ break;
+ case CRISV10_REG_SETF:
+ case CRISV10_REG_CLEARF:
+ dec10_setclrf(dc);
+ break;
+ case CRISV10_REG_SWAP:
+ dec10_reg_swap(dc);
+ break;
+ case CRISV10_REG_ABS:
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_abs(dc);
+ break;
+ case CRISV10_REG_LZ:
+ LOG_DIS("lz $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_LZ, 4, 0);
+ break;
+ case CRISV10_REG_XOR:
+ LOG_DIS("xor $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_alu(dc, CC_OP_XOR, 4, 0);
+ break;
+ case CRISV10_REG_BTST:
+ LOG_DIS("btst $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_update_cc_op(dc, CC_OP_FLAGS, 4);
+ gen_helper_btst(cpu_PR[PR_CCS], cpu_R[dc->dst],
+ cpu_R[dc->src], cpu_PR[PR_CCS]);
+ break;
+ case CRISV10_REG_DSTEP:
+ LOG_DIS("dstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_DSTEP, cpu_R[dc->dst],
+ cpu_R[dc->dst], cpu_R[dc->src], 4);
+ break;
+ case CRISV10_REG_MSTEP:
+ LOG_DIS("mstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size);
+ cris_evaluate_flags(dc);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu(dc, CC_OP_MSTEP, cpu_R[dc->dst],
+ cpu_R[dc->dst], cpu_R[dc->src], 4);
+ break;
+ case CRISV10_REG_SCC:
+ dec10_reg_scc(dc);
+ break;
+ default:
+ LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc,
+ dc->opcode, dc->src, dc->dst);
+ cpu_abort(dc->env, "Unhandled opcode");
+ break;
+ }
+ }
+ return insn_len;
+}
+
+static unsigned int dec10_ind_move_m_r(DisasContext *dc, unsigned int size)
+{
+ unsigned int insn_len = 2;
+ TCGv t;
+
+ LOG_DIS("%s: move.%d [$r%d], $r%d\n", __func__,
+ size, dc->src, dc->dst);
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ t = tcg_temp_new();
+ insn_len += dec10_prep_move_m(dc, 0, size, t);
+ cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, size);
+ if (dc->dst == 15) {
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch = 1;
+ return insn_len;
+ }
+
+ tcg_temp_free(t);
+ return insn_len;
+}
+
+static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size)
+{
+ unsigned int insn_len = 2;
+ TCGv addr;
+
+ LOG_DIS("move.%d $r%d, [$r%d]\n", dc->size, dc->src, dc->dst);
+ addr = tcg_temp_new();
+ crisv10_prepare_memaddr(dc, addr, size);
+ gen_store(dc, addr, cpu_R[dc->dst], size);
+ insn_len += crisv10_post_memaddr(dc, size);
+
+ return insn_len;
+}
+
+static unsigned int dec10_ind_move_m_pr(DisasContext *dc)
+{
+ unsigned int insn_len = 2, rd = dc->dst;
+ TCGv t, addr;
+
+ LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src);
+ cris_lock_irq(dc);
+
+ addr = tcg_temp_new();
+ t = tcg_temp_new();
+ insn_len += dec10_prep_move_m(dc, 0, 4, t);
+ if (rd == 15) {
+ tcg_gen_mov_tl(env_btarget, t);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch = 1;
+ return insn_len;
+ }
+
+ tcg_gen_mov_tl(cpu_PR[rd], t);
+ dc->cpustate_changed = 1;
+ tcg_temp_free(addr);
+ tcg_temp_free(t);
+ return insn_len;
+}
+
+static unsigned int dec10_ind_move_pr_m(DisasContext *dc)
+{
+ unsigned int insn_len = 2, size = preg_sizes_v10[dc->dst];
+ TCGv addr, t0;
+
+ LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src);
+
+ addr = tcg_temp_new();
+ crisv10_prepare_memaddr(dc, addr, size);
+ if (dc->dst == PR_CCS) {
+ t0 = tcg_temp_new();
+ cris_evaluate_flags(dc);
+ tcg_gen_andi_tl(t0, cpu_PR[PR_CCS], ~PFIX_FLAG);
+ gen_store(dc, addr, t0, size);
+ tcg_temp_free(t0);
+ } else {
+ gen_store(dc, addr, cpu_PR[dc->dst], size);
+ }
+ t0 = tcg_temp_new();
+ insn_len += crisv10_post_memaddr(dc, size);
+ cris_lock_irq(dc);
+
+ return insn_len;
+}
+
+static void dec10_movem_r_m(DisasContext *dc)
+{
+ int i, pfix = dc->tb_flags & PFIX_FLAG;
+ TCGv addr, t0;
+
+ LOG_DIS("%s r%d, [r%d] pi=%d ir=%x\n", __func__,
+ dc->dst, dc->src, dc->postinc, dc->ir);
+
+ addr = tcg_temp_new();
+ t0 = tcg_temp_new();
+ crisv10_prepare_memaddr(dc, addr, 4);
+ tcg_gen_mov_tl(t0, addr);
+ for (i = dc->dst; i >= 0; i--) {
+ if ((pfix && dc->mode == CRISV10_MODE_AUTOINC) && dc->src == i) {
+ gen_store(dc, addr, t0, 4);
+ } else {
+ gen_store(dc, addr, cpu_R[i], 4);
+ }
+ tcg_gen_addi_tl(addr, addr, 4);
+ }
+
+ if (pfix && dc->mode == CRISV10_MODE_AUTOINC) {
+ tcg_gen_mov_tl(cpu_R[dc->src], t0);
+ }
+
+ if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) {
+ tcg_gen_mov_tl(cpu_R[dc->src], addr);
+ }
+ tcg_temp_free(addr);
+ tcg_temp_free(t0);
+}
+
+static void dec10_movem_m_r(DisasContext *dc)
+{
+ int i, pfix = dc->tb_flags & PFIX_FLAG;
+ TCGv addr, t0;
+
+ LOG_DIS("%s [r%d], r%d pi=%d ir=%x\n", __func__,
+ dc->src, dc->dst, dc->postinc, dc->ir);
+
+ addr = tcg_temp_new();
+ t0 = tcg_temp_new();
+ crisv10_prepare_memaddr(dc, addr, 4);
+ tcg_gen_mov_tl(t0, addr);
+ for (i = dc->dst; i >= 0; i--) {
+ gen_load(dc, cpu_R[i], addr, 4, 0);
+ tcg_gen_addi_tl(addr, addr, 4);
+ }
+
+ if (pfix && dc->mode == CRISV10_MODE_AUTOINC) {
+ tcg_gen_mov_tl(cpu_R[dc->src], t0);
+ }
+
+ if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) {
+ tcg_gen_mov_tl(cpu_R[dc->src], addr);
+ }
+ tcg_temp_free(addr);
+ tcg_temp_free(t0);
+}
+
+static int dec10_ind_alu(DisasContext *dc, int op, unsigned int size)
+{
+ int insn_len = 0;
+ int rd = dc->dst;
+ TCGv t[2];
+
+ cris_alu_m_alloc_temps(t);
+ insn_len += dec10_prep_move_m(dc, 0, size, t[0]);
+ cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t[0], size);
+ if (dc->dst == 15) {
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch = 1;
+ return insn_len;
+ }
+
+ cris_alu_m_free_temps(t);
+
+ return insn_len;
+}
+
+static int dec10_ind_bound(DisasContext *dc, unsigned int size)
+{
+ int insn_len = 0;
+ int rd = dc->dst;
+ TCGv t;
+
+ t = tcg_temp_local_new();
+ insn_len += dec10_prep_move_m(dc, 0, size, t);
+ cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[rd], t, 4);
+ if (dc->dst == 15) {
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch = 1;
+ return insn_len;
+ }
+
+ tcg_temp_free(t);
+ return insn_len;
+}
+
+static int dec10_alux_m(DisasContext *dc, int op)
+{
+ unsigned int size = (dc->size & 1) ? 2 : 1;
+ unsigned int sx = !!(dc->size & 2);
+ int insn_len = 2;
+ int rd = dc->dst;
+ TCGv t;
+
+ LOG_DIS("addx size=%d sx=%d op=%d %d\n", size, sx, dc->src, dc->dst);
+
+ t = tcg_temp_new();
+
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_prep_move_m(dc, sx, size, t);
+ cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t, 4);
+ if (dc->dst == 15) {
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch = 1;
+ return insn_len;
+ }
+
+ tcg_temp_free(t);
+ return insn_len;
+}
+
+static int dec10_dip(DisasContext *dc)
+{
+ int insn_len = 2;
+ uint32_t imm;
+
+ LOG_DIS("dip pc=%x opcode=%d r%d r%d\n",
+ dc->pc, dc->opcode, dc->src, dc->dst);
+ if (dc->src == 15) {
+ imm = ldl_code(dc->pc + 2);
+ tcg_gen_movi_tl(cpu_PR[PR_PREFIX], imm);
+ if (dc->postinc)
+ insn_len += 4;
+ tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len - 2);
+ } else {
+ gen_load(dc, cpu_PR[PR_PREFIX], cpu_R[dc->src], 4, 0);
+ if (dc->postinc)
+ tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], 4);
+ }
+
+ cris_set_prefix(dc);
+ return insn_len;
+}
+
+static int dec10_bdap_m(DisasContext *dc, int size)
+{
+ int insn_len = 2;
+ int rd = dc->dst;
+
+ LOG_DIS("bdap_m pc=%x opcode=%d r%d r%d sz=%d\n",
+ dc->pc, dc->opcode, dc->src, dc->dst, size);
+
+ assert(dc->dst != 15);
+#if 0
+ /* 8bit embedded offset? */
+ if (!dc->postinc && (dc->ir & (1 << 11))) {
+ int simm = dc->ir & 0xff;
+
+ /* cpu_abort(dc->env, "Unhandled opcode"); */
+ /* sign extended. */
+ simm = (int8_t)simm;
+
+ tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm);
+
+ cris_set_prefix(dc);
+ return insn_len;
+ }
+#endif
+ /* Now the rest of the modes are truely indirect. */
+ insn_len += dec10_prep_move_m(dc, 1, size, cpu_PR[PR_PREFIX]);
+ tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_PR[PR_PREFIX], cpu_R[rd]);
+ cris_set_prefix(dc);
+ return insn_len;
+}
+
+static unsigned int dec10_ind(DisasContext *dc)
+{
+ unsigned int insn_len = 2;
+ unsigned int size = dec10_size(dc->size);
+ uint32_t imm;
+ int32_t simm;
+ TCGv t[2];
+
+ if (dc->size != 3) {
+ switch (dc->opcode) {
+ case CRISV10_IND_MOVE_M_R:
+ return dec10_ind_move_m_r(dc, size);
+ break;
+ case CRISV10_IND_MOVE_R_M:
+ return dec10_ind_move_r_m(dc, size);
+ break;
+ case CRISV10_IND_CMP:
+ LOG_DIS("cmp size=%d op=%d %d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_ind_alu(dc, CC_OP_CMP, size);
+ break;
+ case CRISV10_IND_TEST:
+ LOG_DIS("test size=%d op=%d %d\n", size, dc->src, dc->dst);
+
+ cris_evaluate_flags(dc);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ cris_alu_m_alloc_temps(t);
+ insn_len += dec10_prep_move_m(dc, 0, size, t[0]);
+ tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~3);
+ cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst],
+ t[0], tcg_const_tl(0), size);
+ cris_alu_m_free_temps(t);
+ break;
+ case CRISV10_IND_ADD:
+ LOG_DIS("add size=%d op=%d %d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_ind_alu(dc, CC_OP_ADD, size);
+ break;
+ case CRISV10_IND_SUB:
+ LOG_DIS("sub size=%d op=%d %d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_ind_alu(dc, CC_OP_SUB, size);
+ break;
+ case CRISV10_IND_BOUND:
+ LOG_DIS("bound size=%d op=%d %d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_ind_bound(dc, size);
+ break;
+ case CRISV10_IND_AND:
+ LOG_DIS("and size=%d op=%d %d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_ind_alu(dc, CC_OP_AND, size);
+ break;
+ case CRISV10_IND_OR:
+ LOG_DIS("or size=%d op=%d %d\n", size, dc->src, dc->dst);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ insn_len += dec10_ind_alu(dc, CC_OP_OR, size);
+ break;
+ case CRISV10_IND_MOVX:
+ insn_len = dec10_alux_m(dc, CC_OP_MOVE);
+ break;
+ case CRISV10_IND_ADDX:
+ insn_len = dec10_alux_m(dc, CC_OP_ADD);
+ break;
+ case CRISV10_IND_SUBX:
+ insn_len = dec10_alux_m(dc, CC_OP_SUB);
+ break;
+ case CRISV10_IND_CMPX:
+ insn_len = dec10_alux_m(dc, CC_OP_CMP);
+ break;
+ case CRISV10_IND_MUL:
+ /* This is a reg insn coded in the mem indir space. */
+ LOG_DIS("mul pc=%x opcode=%d\n", dc->pc, dc->opcode);
+ cris_cc_mask(dc, CC_MASK_NZVC);
+ dec10_reg_mul(dc, size, dc->ir & (1 << 10));
+ break;
+ case CRISV10_IND_BDAP_M:
+ insn_len = dec10_bdap_m(dc, size);
+ break;
+ default:
+ LOG_DIS("pc=%x var-ind.%d %d r%d r%d\n",
+ dc->pc, size, dc->opcode, dc->src, dc->dst);
+ cpu_abort(dc->env, "Unhandled opcode");
+ break;
+ }
+ return insn_len;
+ }
+
+ switch (dc->opcode) {
+ case CRISV10_IND_MOVE_M_SPR:
+ insn_len = dec10_ind_move_m_pr(dc);
+ break;
+ case CRISV10_IND_MOVE_SPR_M:
+ insn_len = dec10_ind_move_pr_m(dc);
+ break;
+ case CRISV10_IND_JUMP_M:
+ if (dc->src == 15) {
+ LOG_DIS("jump.%d %d r%d r%d direct\n", size,
+ dc->opcode, dc->src, dc->dst);
+ imm = ldl_code(dc->pc + 2);
+ if (dc->mode == CRISV10_MODE_AUTOINC)
+ insn_len += size;
+
+ t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len));
+ dc->jmp_pc = imm;
+ cris_prepare_jmp(dc, JMP_DIRECT);
+ dc->delayed_branch--; /* v10 has no dslot here. */
+ } else {
+ if (dc->dst == 14) {
+ LOG_DIS("break %d\n", dc->src);
+ cris_evaluate_flags(dc);
+ tcg_gen_movi_tl(env_pc, dc->pc + 2);
+ t_gen_raise_exception(EXCP_BREAK);
+ dc->is_jmp = DISAS_UPDATE;
+ return insn_len;
+ }
+ LOG_DIS("%d: jump.%d %d r%d r%d\n", __LINE__, size,
+ dc->opcode, dc->src, dc->dst);
+ t[0] = tcg_temp_new();
+ t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len));
+ crisv10_prepare_memaddr(dc, t[0], size);
+ gen_load(dc, env_btarget, t[0], 4, 0);
+ insn_len += crisv10_post_memaddr(dc, size);
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch--; /* v10 has no dslot here. */
+ tcg_temp_free(t[0]);
+ }
+ break;
+
+ case CRISV10_IND_MOVEM_R_M:
+ LOG_DIS("movem_r_m pc=%x opcode=%d r%d r%d\n",
+ dc->pc, dc->opcode, dc->dst, dc->src);
+ dec10_movem_r_m(dc);
+ break;
+ case CRISV10_IND_MOVEM_M_R:
+ LOG_DIS("movem_m_r pc=%x opcode=%d\n", dc->pc, dc->opcode);
+ dec10_movem_m_r(dc);
+ break;
+ case CRISV10_IND_JUMP_R:
+ LOG_DIS("jmp pc=%x opcode=%d r%d r%d\n",
+ dc->pc, dc->opcode, dc->dst, dc->src);
+ tcg_gen_mov_tl(env_btarget, cpu_R[dc->src]);
+ t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len));
+ cris_prepare_jmp(dc, JMP_INDIRECT);
+ dc->delayed_branch--; /* v10 has no dslot here. */
+ break;
+ case CRISV10_IND_MOVX:
+ insn_len = dec10_alux_m(dc, CC_OP_MOVE);
+ break;
+ case CRISV10_IND_ADDX:
+ insn_len = dec10_alux_m(dc, CC_OP_ADD);
+ break;
+ case CRISV10_IND_SUBX:
+ insn_len = dec10_alux_m(dc, CC_OP_SUB);
+ break;
+ case CRISV10_IND_CMPX:
+ insn_len = dec10_alux_m(dc, CC_OP_CMP);
+ break;
+ case CRISV10_IND_DIP:
+ insn_len = dec10_dip(dc);
+ break;
+ case CRISV10_IND_BCC_M:
+
+ cris_cc_mask(dc, 0);
+ imm = ldsw_code(dc->pc + 2);
+ simm = (int16_t)imm;
+ simm += 4;
+
+ LOG_DIS("bcc_m: b%s %x\n", cc_name(dc->cond), dc->pc + simm);
+ cris_prepare_cc_branch(dc, simm, dc->cond);
+ insn_len = 4;
+ break;
+ default:
+ LOG_DIS("ERROR pc=%x opcode=%d\n", dc->pc, dc->opcode);
+ cpu_abort(dc->env, "Unhandled opcode");
+ break;
+ }
+
+ return insn_len;
+}
+
+static unsigned int crisv10_decoder(DisasContext *dc)
+{
+ unsigned int insn_len = 2;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(dc->pc);
+
+ /* Load a halfword onto the instruction register. */
+ dc->ir = lduw_code(dc->pc);
+
+ /* Now decode it. */
+ dc->opcode = EXTRACT_FIELD(dc->ir, 6, 9);
+ dc->mode = EXTRACT_FIELD(dc->ir, 10, 11);
+ dc->src = EXTRACT_FIELD(dc->ir, 0, 3);
+ dc->size = EXTRACT_FIELD(dc->ir, 4, 5);
+ dc->cond = dc->dst = EXTRACT_FIELD(dc->ir, 12, 15);
+ dc->postinc = EXTRACT_FIELD(dc->ir, 10, 10);
+
+ dc->clear_prefix = 1;
+
+ /* FIXME: What if this insn insn't 2 in length?? */
+ if (dc->src == 15 || dc->dst == 15)
+ tcg_gen_movi_tl(cpu_R[15], dc->pc + 2);
+
+ switch (dc->mode) {
+ case CRISV10_MODE_QIMMEDIATE:
+ insn_len = dec10_quick_imm(dc);
+ break;
+ case CRISV10_MODE_REG:
+ insn_len = dec10_reg(dc);
+ break;
+ case CRISV10_MODE_AUTOINC:
+ case CRISV10_MODE_INDIRECT:
+ insn_len = dec10_ind(dc);
+ break;
+ }
+
+ if (dc->clear_prefix && dc->tb_flags & PFIX_FLAG) {
+ dc->tb_flags &= ~PFIX_FLAG;
+ tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~PFIX_FLAG);
+ if (dc->tb_flags != dc->tb->flags) {
+ dc->cpustate_changed = 1;
+ }
+ }
+
+ /* CRISv10 locks out interrupts on dslots. */
+ if (dc->delayed_branch == 2) {
+ cris_lock_irq(dc);
+ }
+ return insn_len;
+}
+
+static CPUCRISState *cpu_crisv10_init (CPUState *env)
+{
+ int i;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+ cc_x = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_x), "cc_x");
+ cc_src = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_src), "cc_src");
+ cc_dest = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_dest),
+ "cc_dest");
+ cc_result = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_result),
+ "cc_result");
+ cc_op = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_op), "cc_op");
+ cc_size = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_size),
+ "cc_size");
+ cc_mask = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_mask),
+ "cc_mask");
+
+ env_pc = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, pc),
+ "pc");
+ env_btarget = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btarget),
+ "btarget");
+ env_btaken = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btaken),
+ "btaken");
+ for (i = 0; i < 16; i++) {
+ cpu_R[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, regs[i]),
+ regnames_v10[i]);
+ }
+ for (i = 0; i < 16; i++) {
+ cpu_PR[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, pregs[i]),
+ pregnames_v10[i]);
+ }
+
+ return env;
+}
+
diff --git a/target-i386/TODO b/target-i386/TODO
new file mode 100644
index 0000000..c8ada07
--- /dev/null
+++ b/target-i386/TODO
@@ -0,0 +1,32 @@
+Correctness issues:
+
+- some eflags manipulation incorrectly reset the bit 0x2.
+- SVM: test, cpu save/restore, SMM save/restore.
+- x86_64: lcall/ljmp intel/amd differences ?
+- better code fetch (different exception handling + CS.limit support)
+- user/kernel PUSHL/POPL in helper.c
+- add missing cpuid tests
+- return UD exception if LOCK prefix incorrectly used
+- test ldt limit < 7 ?
+- fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret)
+- full support of segment limit/rights
+- full x87 exception support
+- improve x87 bit exactness (use bochs code ?)
+- DRx register support
+- CR0.AC emulation
+- SSE alignment checks
+- fix SSE min/max with nans
+
+Optimizations/Features:
+
+- add SVM nested paging support
+- add VMX support
+- add AVX support
+- add SSE5 support
+- fxsave/fxrstor AMD extensions
+- improve monitor/mwait support
+- faster EFLAGS update: consider SZAP, C, O can be updated separately
+ with a bit field in CC_OP and more state variables.
+- evaluate x87 stack pointer statically
+- find a way to avoid translating several time the same TB if CR0.TS
+ is set or not.
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
new file mode 100644
index 0000000..3c22e20
--- /dev/null
+++ b/target-i386/cpu.h
@@ -0,0 +1,990 @@
+/*
+ * i386 virtual CPU header
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#ifndef CPU_I386_H
+#define CPU_I386_H
+
+#include "config.h"
+#include "qemu-common.h"
+
+#ifdef TARGET_X86_64
+#define TARGET_LONG_BITS 64
+#else
+#define TARGET_LONG_BITS 32
+#endif
+
+/* target supports implicit self modifying code */
+#define TARGET_HAS_SMC
+/* support for self modifying code even if the modified instruction is
+ close to the modifying instruction */
+#define TARGET_HAS_PRECISE_SMC
+
+#define TARGET_HAS_ICE 1
+
+#ifdef TARGET_X86_64
+#define ELF_MACHINE EM_X86_64
+#else
+#define ELF_MACHINE EM_386
+#endif
+
+#define CPUState struct CPUX86State
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define R_EAX 0
+#define R_ECX 1
+#define R_EDX 2
+#define R_EBX 3
+#define R_ESP 4
+#define R_EBP 5
+#define R_ESI 6
+#define R_EDI 7
+
+#define R_AL 0
+#define R_CL 1
+#define R_DL 2
+#define R_BL 3
+#define R_AH 4
+#define R_CH 5
+#define R_DH 6
+#define R_BH 7
+
+#define R_ES 0
+#define R_CS 1
+#define R_SS 2
+#define R_DS 3
+#define R_FS 4
+#define R_GS 5
+
+/* segment descriptor fields */
+#define DESC_G_MASK (1 << 23)
+#define DESC_B_SHIFT 22
+#define DESC_B_MASK (1 << DESC_B_SHIFT)
+#define DESC_L_SHIFT 21 /* x86_64 only : 64 bit code segment */
+#define DESC_L_MASK (1 << DESC_L_SHIFT)
+#define DESC_AVL_MASK (1 << 20)
+#define DESC_P_MASK (1 << 15)
+#define DESC_DPL_SHIFT 13
+#define DESC_DPL_MASK (3 << DESC_DPL_SHIFT)
+#define DESC_S_MASK (1 << 12)
+#define DESC_TYPE_SHIFT 8
+#define DESC_TYPE_MASK (15 << DESC_TYPE_SHIFT)
+#define DESC_A_MASK (1 << 8)
+
+#define DESC_CS_MASK (1 << 11) /* 1=code segment 0=data segment */
+#define DESC_C_MASK (1 << 10) /* code: conforming */
+#define DESC_R_MASK (1 << 9) /* code: readable */
+
+#define DESC_E_MASK (1 << 10) /* data: expansion direction */
+#define DESC_W_MASK (1 << 9) /* data: writable */
+
+#define DESC_TSS_BUSY_MASK (1 << 9)
+
+/* eflags masks */
+#define CC_C 0x0001
+#define CC_P 0x0004
+#define CC_A 0x0010
+#define CC_Z 0x0040
+#define CC_S 0x0080
+#define CC_O 0x0800
+
+#define TF_SHIFT 8
+#define IOPL_SHIFT 12
+#define VM_SHIFT 17
+
+#define TF_MASK 0x00000100
+#define IF_MASK 0x00000200
+#define DF_MASK 0x00000400
+#define IOPL_MASK 0x00003000
+#define NT_MASK 0x00004000
+#define RF_MASK 0x00010000
+#define VM_MASK 0x00020000
+#define AC_MASK 0x00040000
+#define VIF_MASK 0x00080000
+#define VIP_MASK 0x00100000
+#define ID_MASK 0x00200000
+
+/* hidden flags - used internally by qemu to represent additional cpu
+ states. Only the CPL, INHIBIT_IRQ, SMM and SVMI are not
+ redundant. We avoid using the IOPL_MASK, TF_MASK and VM_MASK bit
+ position to ease oring with eflags. */
+/* current cpl */
+#define HF_CPL_SHIFT 0
+/* true if soft mmu is being used */
+#define HF_SOFTMMU_SHIFT 2
+/* true if hardware interrupts must be disabled for next instruction */
+#define HF_INHIBIT_IRQ_SHIFT 3
+/* 16 or 32 segments */
+#define HF_CS32_SHIFT 4
+#define HF_SS32_SHIFT 5
+/* zero base for DS, ES and SS : can be '0' only in 32 bit CS segment */
+#define HF_ADDSEG_SHIFT 6
+/* copy of CR0.PE (protected mode) */
+#define HF_PE_SHIFT 7
+#define HF_TF_SHIFT 8 /* must be same as eflags */
+#define HF_MP_SHIFT 9 /* the order must be MP, EM, TS */
+#define HF_EM_SHIFT 10
+#define HF_TS_SHIFT 11
+#define HF_IOPL_SHIFT 12 /* must be same as eflags */
+#define HF_LMA_SHIFT 14 /* only used on x86_64: long mode active */
+#define HF_CS64_SHIFT 15 /* only used on x86_64: 64 bit code segment */
+#define HF_RF_SHIFT 16 /* must be same as eflags */
+#define HF_VM_SHIFT 17 /* must be same as eflags */
+#define HF_SMM_SHIFT 19 /* CPU in SMM mode */
+#define HF_SVME_SHIFT 20 /* SVME enabled (copy of EFER.SVME) */
+#define HF_SVMI_SHIFT 21 /* SVM intercepts are active */
+#define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */
+
+#define HF_CPL_MASK (3 << HF_CPL_SHIFT)
+#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
+#define HF_INHIBIT_IRQ_MASK (1 << HF_INHIBIT_IRQ_SHIFT)
+#define HF_CS32_MASK (1 << HF_CS32_SHIFT)
+#define HF_SS32_MASK (1 << HF_SS32_SHIFT)
+#define HF_ADDSEG_MASK (1 << HF_ADDSEG_SHIFT)
+#define HF_PE_MASK (1 << HF_PE_SHIFT)
+#define HF_TF_MASK (1 << HF_TF_SHIFT)
+#define HF_MP_MASK (1 << HF_MP_SHIFT)
+#define HF_EM_MASK (1 << HF_EM_SHIFT)
+#define HF_TS_MASK (1 << HF_TS_SHIFT)
+#define HF_IOPL_MASK (3 << HF_IOPL_SHIFT)
+#define HF_LMA_MASK (1 << HF_LMA_SHIFT)
+#define HF_CS64_MASK (1 << HF_CS64_SHIFT)
+#define HF_RF_MASK (1 << HF_RF_SHIFT)
+#define HF_VM_MASK (1 << HF_VM_SHIFT)
+#define HF_SMM_MASK (1 << HF_SMM_SHIFT)
+#define HF_SVME_MASK (1 << HF_SVME_SHIFT)
+#define HF_SVMI_MASK (1 << HF_SVMI_SHIFT)
+#define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT)
+
+/* hflags2 */
+
+#define HF2_GIF_SHIFT 0 /* if set CPU takes interrupts */
+#define HF2_HIF_SHIFT 1 /* value of IF_MASK when entering SVM */
+#define HF2_NMI_SHIFT 2 /* CPU serving NMI */
+#define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */
+
+#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
+#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
+#define HF2_NMI_MASK (1 << HF2_NMI_SHIFT)
+#define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT)
+
+#define CR0_PE_SHIFT 0
+#define CR0_MP_SHIFT 1
+
+#define CR0_PE_MASK (1 << 0)
+#define CR0_MP_MASK (1 << 1)
+#define CR0_EM_MASK (1 << 2)
+#define CR0_TS_MASK (1 << 3)
+#define CR0_ET_MASK (1 << 4)
+#define CR0_NE_MASK (1 << 5)
+#define CR0_WP_MASK (1 << 16)
+#define CR0_AM_MASK (1 << 18)
+#define CR0_PG_MASK (1 << 31)
+
+#define CR4_VME_MASK (1 << 0)
+#define CR4_PVI_MASK (1 << 1)
+#define CR4_TSD_MASK (1 << 2)
+#define CR4_DE_MASK (1 << 3)
+#define CR4_PSE_MASK (1 << 4)
+#define CR4_PAE_MASK (1 << 5)
+#define CR4_MCE_MASK (1 << 6)
+#define CR4_PGE_MASK (1 << 7)
+#define CR4_PCE_MASK (1 << 8)
+#define CR4_OSFXSR_SHIFT 9
+#define CR4_OSFXSR_MASK (1 << CR4_OSFXSR_SHIFT)
+#define CR4_OSXMMEXCPT_MASK (1 << 10)
+
+#define DR6_BD (1 << 13)
+#define DR6_BS (1 << 14)
+#define DR6_BT (1 << 15)
+#define DR6_FIXED_1 0xffff0ff0
+
+#define DR7_GD (1 << 13)
+#define DR7_TYPE_SHIFT 16
+#define DR7_LEN_SHIFT 18
+#define DR7_FIXED_1 0x00000400
+
+#define PG_PRESENT_BIT 0
+#define PG_RW_BIT 1
+#define PG_USER_BIT 2
+#define PG_PWT_BIT 3
+#define PG_PCD_BIT 4
+#define PG_ACCESSED_BIT 5
+#define PG_DIRTY_BIT 6
+#define PG_PSE_BIT 7
+#define PG_GLOBAL_BIT 8
+#define PG_NX_BIT 63
+
+#define PG_PRESENT_MASK (1 << PG_PRESENT_BIT)
+#define PG_RW_MASK (1 << PG_RW_BIT)
+#define PG_USER_MASK (1 << PG_USER_BIT)
+#define PG_PWT_MASK (1 << PG_PWT_BIT)
+#define PG_PCD_MASK (1 << PG_PCD_BIT)
+#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT)
+#define PG_DIRTY_MASK (1 << PG_DIRTY_BIT)
+#define PG_PSE_MASK (1 << PG_PSE_BIT)
+#define PG_GLOBAL_MASK (1 << PG_GLOBAL_BIT)
+#define PG_NX_MASK (1LL << PG_NX_BIT)
+
+#define PG_ERROR_W_BIT 1
+
+#define PG_ERROR_P_MASK 0x01
+#define PG_ERROR_W_MASK (1 << PG_ERROR_W_BIT)
+#define PG_ERROR_U_MASK 0x04
+#define PG_ERROR_RSVD_MASK 0x08
+#define PG_ERROR_I_D_MASK 0x10
+
+#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */
+#define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */
+
+#define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P)
+#define MCE_BANKS_DEF 10
+
+#define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */
+#define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */
+#define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */
+
+#define MCI_STATUS_VAL (1ULL<<63) /* valid error */
+#define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */
+#define MCI_STATUS_UC (1ULL<<61) /* uncorrected error */
+#define MCI_STATUS_EN (1ULL<<60) /* error enabled */
+#define MCI_STATUS_MISCV (1ULL<<59) /* misc error reg. valid */
+#define MCI_STATUS_ADDRV (1ULL<<58) /* addr reg. valid */
+#define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */
+#define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */
+#define MCI_STATUS_AR (1ULL<<55) /* Action required */
+
+/* MISC register defines */
+#define MCM_ADDR_SEGOFF 0 /* segment offset */
+#define MCM_ADDR_LINEAR 1 /* linear address */
+#define MCM_ADDR_PHYS 2 /* physical address */
+#define MCM_ADDR_MEM 3 /* memory address */
+#define MCM_ADDR_GENERIC 7 /* generic */
+
+#define MSR_IA32_TSC 0x10
+#define MSR_IA32_APICBASE 0x1b
+#define MSR_IA32_APICBASE_BSP (1<<8)
+#define MSR_IA32_APICBASE_ENABLE (1<<11)
+#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
+
+#define MSR_MTRRcap 0xfe
+#define MSR_MTRRcap_VCNT 8
+#define MSR_MTRRcap_FIXRANGE_SUPPORT (1 << 8)
+#define MSR_MTRRcap_WC_SUPPORTED (1 << 10)
+
+#define MSR_IA32_SYSENTER_CS 0x174
+#define MSR_IA32_SYSENTER_ESP 0x175
+#define MSR_IA32_SYSENTER_EIP 0x176
+
+#define MSR_MCG_CAP 0x179
+#define MSR_MCG_STATUS 0x17a
+#define MSR_MCG_CTL 0x17b
+
+#define MSR_IA32_PERF_STATUS 0x198
+
+#define MSR_MTRRphysBase(reg) (0x200 + 2 * (reg))
+#define MSR_MTRRphysMask(reg) (0x200 + 2 * (reg) + 1)
+
+#define MSR_MTRRfix64K_00000 0x250
+#define MSR_MTRRfix16K_80000 0x258
+#define MSR_MTRRfix16K_A0000 0x259
+#define MSR_MTRRfix4K_C0000 0x268
+#define MSR_MTRRfix4K_C8000 0x269
+#define MSR_MTRRfix4K_D0000 0x26a
+#define MSR_MTRRfix4K_D8000 0x26b
+#define MSR_MTRRfix4K_E0000 0x26c
+#define MSR_MTRRfix4K_E8000 0x26d
+#define MSR_MTRRfix4K_F0000 0x26e
+#define MSR_MTRRfix4K_F8000 0x26f
+
+#define MSR_PAT 0x277
+
+#define MSR_MTRRdefType 0x2ff
+
+#define MSR_MC0_CTL 0x400
+#define MSR_MC0_STATUS 0x401
+#define MSR_MC0_ADDR 0x402
+#define MSR_MC0_MISC 0x403
+
+#define MSR_EFER 0xc0000080
+
+#define MSR_EFER_SCE (1 << 0)
+#define MSR_EFER_LME (1 << 8)
+#define MSR_EFER_LMA (1 << 10)
+#define MSR_EFER_NXE (1 << 11)
+#define MSR_EFER_SVME (1 << 12)
+#define MSR_EFER_FFXSR (1 << 14)
+
+#define MSR_STAR 0xc0000081
+#define MSR_LSTAR 0xc0000082
+#define MSR_CSTAR 0xc0000083
+#define MSR_FMASK 0xc0000084
+#define MSR_FSBASE 0xc0000100
+#define MSR_GSBASE 0xc0000101
+#define MSR_KERNELGSBASE 0xc0000102
+#define MSR_TSC_AUX 0xc0000103
+
+#define MSR_VM_HSAVE_PA 0xc0010117
+
+/* cpuid_features bits */
+#define CPUID_FP87 (1 << 0)
+#define CPUID_VME (1 << 1)
+#define CPUID_DE (1 << 2)
+#define CPUID_PSE (1 << 3)
+#define CPUID_TSC (1 << 4)
+#define CPUID_MSR (1 << 5)
+#define CPUID_PAE (1 << 6)
+#define CPUID_MCE (1 << 7)
+#define CPUID_CX8 (1 << 8)
+#define CPUID_APIC (1 << 9)
+#define CPUID_SEP (1 << 11) /* sysenter/sysexit */
+#define CPUID_MTRR (1 << 12)
+#define CPUID_PGE (1 << 13)
+#define CPUID_MCA (1 << 14)
+#define CPUID_CMOV (1 << 15)
+#define CPUID_PAT (1 << 16)
+#define CPUID_PSE36 (1 << 17)
+#define CPUID_PN (1 << 18)
+#define CPUID_CLFLUSH (1 << 19)
+#define CPUID_DTS (1 << 21)
+#define CPUID_ACPI (1 << 22)
+#define CPUID_MMX (1 << 23)
+#define CPUID_FXSR (1 << 24)
+#define CPUID_SSE (1 << 25)
+#define CPUID_SSE2 (1 << 26)
+#define CPUID_SS (1 << 27)
+#define CPUID_HT (1 << 28)
+#define CPUID_TM (1 << 29)
+#define CPUID_IA64 (1 << 30)
+#define CPUID_PBE (1 << 31)
+
+#define CPUID_EXT_SSE3 (1 << 0)
+#define CPUID_EXT_DTES64 (1 << 2)
+#define CPUID_EXT_MONITOR (1 << 3)
+#define CPUID_EXT_DSCPL (1 << 4)
+#define CPUID_EXT_VMX (1 << 5)
+#define CPUID_EXT_SMX (1 << 6)
+#define CPUID_EXT_EST (1 << 7)
+#define CPUID_EXT_TM2 (1 << 8)
+#define CPUID_EXT_SSSE3 (1 << 9)
+#define CPUID_EXT_CID (1 << 10)
+#define CPUID_EXT_CX16 (1 << 13)
+#define CPUID_EXT_XTPR (1 << 14)
+#define CPUID_EXT_PDCM (1 << 15)
+#define CPUID_EXT_DCA (1 << 18)
+#define CPUID_EXT_SSE41 (1 << 19)
+#define CPUID_EXT_SSE42 (1 << 20)
+#define CPUID_EXT_X2APIC (1 << 21)
+#define CPUID_EXT_MOVBE (1 << 22)
+#define CPUID_EXT_POPCNT (1 << 23)
+#define CPUID_EXT_XSAVE (1 << 26)
+#define CPUID_EXT_OSXSAVE (1 << 27)
+#define CPUID_EXT_HYPERVISOR (1 << 31)
+
+#define CPUID_EXT2_SYSCALL (1 << 11)
+#define CPUID_EXT2_MP (1 << 19)
+#define CPUID_EXT2_NX (1 << 20)
+#define CPUID_EXT2_MMXEXT (1 << 22)
+#define CPUID_EXT2_FFXSR (1 << 25)
+#define CPUID_EXT2_PDPE1GB (1 << 26)
+#define CPUID_EXT2_RDTSCP (1 << 27)
+#define CPUID_EXT2_LM (1 << 29)
+#define CPUID_EXT2_3DNOWEXT (1 << 30)
+#define CPUID_EXT2_3DNOW (1 << 31)
+
+#define CPUID_EXT3_LAHF_LM (1 << 0)
+#define CPUID_EXT3_CMP_LEG (1 << 1)
+#define CPUID_EXT3_SVM (1 << 2)
+#define CPUID_EXT3_EXTAPIC (1 << 3)
+#define CPUID_EXT3_CR8LEG (1 << 4)
+#define CPUID_EXT3_ABM (1 << 5)
+#define CPUID_EXT3_SSE4A (1 << 6)
+#define CPUID_EXT3_MISALIGNSSE (1 << 7)
+#define CPUID_EXT3_3DNOWPREFETCH (1 << 8)
+#define CPUID_EXT3_OSVW (1 << 9)
+#define CPUID_EXT3_IBS (1 << 10)
+#define CPUID_EXT3_SKINIT (1 << 12)
+
+#define CPUID_SVM_NPT (1 << 0)
+#define CPUID_SVM_LBRV (1 << 1)
+#define CPUID_SVM_SVMLOCK (1 << 2)
+#define CPUID_SVM_NRIPSAVE (1 << 3)
+#define CPUID_SVM_TSCSCALE (1 << 4)
+#define CPUID_SVM_VMCBCLEAN (1 << 5)
+#define CPUID_SVM_FLUSHASID (1 << 6)
+#define CPUID_SVM_DECODEASSIST (1 << 7)
+#define CPUID_SVM_PAUSEFILTER (1 << 10)
+#define CPUID_SVM_PFTHRESHOLD (1 << 12)
+
+#define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */
+#define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */
+#define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */
+
+#define CPUID_VENDOR_AMD_1 0x68747541 /* "Auth" */
+#define CPUID_VENDOR_AMD_2 0x69746e65 /* "enti" */
+#define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */
+
+#define CPUID_MWAIT_IBE (1 << 1) /* Interrupts can exit capability */
+#define CPUID_MWAIT_EMX (1 << 0) /* enumeration supported */
+
+#define EXCP00_DIVZ 0
+#define EXCP01_DB 1
+#define EXCP02_NMI 2
+#define EXCP03_INT3 3
+#define EXCP04_INTO 4
+#define EXCP05_BOUND 5
+#define EXCP06_ILLOP 6
+#define EXCP07_PREX 7
+#define EXCP08_DBLE 8
+#define EXCP09_XERR 9
+#define EXCP0A_TSS 10
+#define EXCP0B_NOSEG 11
+#define EXCP0C_STACK 12
+#define EXCP0D_GPF 13
+#define EXCP0E_PAGE 14
+#define EXCP10_COPR 16
+#define EXCP11_ALGN 17
+#define EXCP12_MCHK 18
+
+#define EXCP_SYSCALL 0x100 /* only happens in user only emulation
+ for syscall instruction */
+
+enum {
+ CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
+ CC_OP_EFLAGS, /* all cc are explicitly computed, CC_SRC = flags */
+
+ CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */
+ CC_OP_MULW,
+ CC_OP_MULL,
+ CC_OP_MULQ,
+
+ CC_OP_ADDB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_ADDW,
+ CC_OP_ADDL,
+ CC_OP_ADDQ,
+
+ CC_OP_ADCB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_ADCW,
+ CC_OP_ADCL,
+ CC_OP_ADCQ,
+
+ CC_OP_SUBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_SUBW,
+ CC_OP_SUBL,
+ CC_OP_SUBQ,
+
+ CC_OP_SBBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_SBBW,
+ CC_OP_SBBL,
+ CC_OP_SBBQ,
+
+ CC_OP_LOGICB, /* modify all flags, CC_DST = res */
+ CC_OP_LOGICW,
+ CC_OP_LOGICL,
+ CC_OP_LOGICQ,
+
+ CC_OP_INCB, /* modify all flags except, CC_DST = res, CC_SRC = C */
+ CC_OP_INCW,
+ CC_OP_INCL,
+ CC_OP_INCQ,
+
+ CC_OP_DECB, /* modify all flags except, CC_DST = res, CC_SRC = C */
+ CC_OP_DECW,
+ CC_OP_DECL,
+ CC_OP_DECQ,
+
+ CC_OP_SHLB, /* modify all flags, CC_DST = res, CC_SRC.msb = C */
+ CC_OP_SHLW,
+ CC_OP_SHLL,
+ CC_OP_SHLQ,
+
+ CC_OP_SARB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */
+ CC_OP_SARW,
+ CC_OP_SARL,
+ CC_OP_SARQ,
+
+ 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;
+ uint32_t limit;
+ uint32_t flags;
+} SegmentCache;
+
+typedef union {
+ uint8_t _b[16];
+ uint16_t _w[8];
+ uint32_t _l[4];
+ uint64_t _q[2];
+ float32 _s[4];
+ float64 _d[2];
+} XMMReg;
+
+typedef union {
+ uint8_t _b[8];
+ uint16_t _w[4];
+ uint32_t _l[2];
+ float32 _s[2];
+ uint64_t q;
+} MMXReg;
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define XMM_B(n) _b[15 - (n)]
+#define XMM_W(n) _w[7 - (n)]
+#define XMM_L(n) _l[3 - (n)]
+#define XMM_S(n) _s[3 - (n)]
+#define XMM_Q(n) _q[1 - (n)]
+#define XMM_D(n) _d[1 - (n)]
+
+#define MMX_B(n) _b[7 - (n)]
+#define MMX_W(n) _w[3 - (n)]
+#define MMX_L(n) _l[1 - (n)]
+#define MMX_S(n) _s[1 - (n)]
+#else
+#define XMM_B(n) _b[n]
+#define XMM_W(n) _w[n]
+#define XMM_L(n) _l[n]
+#define XMM_S(n) _s[n]
+#define XMM_Q(n) _q[n]
+#define XMM_D(n) _d[n]
+
+#define MMX_B(n) _b[n]
+#define MMX_W(n) _w[n]
+#define MMX_L(n) _l[n]
+#define MMX_S(n) _s[n]
+#endif
+#define MMX_Q(n) q
+
+typedef union {
+#ifdef USE_X86LDOUBLE
+ CPU86_LDouble d __attribute__((aligned(16)));
+#else
+ CPU86_LDouble d;
+#endif
+ MMXReg mmx;
+} FPReg;
+
+typedef struct {
+ uint64_t base;
+ uint64_t mask;
+} MTRRVar;
+
+#define CPU_NB_REGS64 16
+#define CPU_NB_REGS32 8
+
+#ifdef TARGET_X86_64
+#define CPU_NB_REGS CPU_NB_REGS64
+#else
+#define CPU_NB_REGS CPU_NB_REGS32
+#endif
+
+#define NB_MMU_MODES 2
+
+typedef struct CPUX86State {
+ /* standard registers */
+ target_ulong regs[CPU_NB_REGS];
+ target_ulong eip;
+ target_ulong eflags; /* eflags register. During CPU emulation, CC
+ flags and DF are set to zero because they are
+ stored elsewhere */
+
+ /* emulator internal eflags handling */
+ target_ulong cc_src;
+ target_ulong cc_dst;
+ uint32_t cc_op;
+ int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */
+ uint32_t hflags; /* TB flags, see HF_xxx constants. These flags
+ are known at translation time. */
+ uint32_t hflags2; /* various other flags, see HF2_xxx constants. */
+
+ /* segments */
+ SegmentCache segs[6]; /* selector values */
+ SegmentCache ldt;
+ SegmentCache tr;
+ SegmentCache gdt; /* only base and limit are used */
+ SegmentCache idt; /* only base and limit are used */
+
+ target_ulong cr[5]; /* NOTE: cr1 is unused */
+ int32_t a20_mask;
+
+ /* FPU state */
+ unsigned int fpstt; /* top of stack index */
+ uint16_t fpus;
+ uint16_t fpuc;
+ uint8_t fptags[8]; /* 0 = valid, 1 = empty */
+ FPReg fpregs[8];
+
+ /* emulator internal variables */
+ float_status fp_status;
+ CPU86_LDouble ft0;
+
+ float_status mmx_status; /* for 3DNow! float ops */
+ float_status sse_status;
+ uint32_t mxcsr;
+ XMMReg xmm_regs[CPU_NB_REGS];
+ XMMReg xmm_t0;
+ MMXReg mmx_t0;
+ target_ulong cc_tmp; /* temporary for rcr/rcl */
+
+ /* sysenter registers */
+ uint32_t sysenter_cs;
+ target_ulong sysenter_esp;
+ target_ulong sysenter_eip;
+ uint64_t efer;
+ uint64_t star;
+
+ uint64_t vm_hsave;
+ uint64_t vm_vmcb;
+ uint64_t tsc_offset;
+ uint64_t intercept;
+ uint16_t intercept_cr_read;
+ uint16_t intercept_cr_write;
+ uint16_t intercept_dr_read;
+ uint16_t intercept_dr_write;
+ uint32_t intercept_exceptions;
+ uint8_t v_tpr;
+
+#ifdef TARGET_X86_64
+ target_ulong lstar;
+ target_ulong cstar;
+ target_ulong fmask;
+ target_ulong kernelgsbase;
+#endif
+ uint64_t system_time_msr;
+ uint64_t wall_clock_msr;
+ uint64_t async_pf_en_msr;
+
+ uint64_t tsc;
+
+ uint64_t pat;
+
+ /* exception/interrupt handling */
+ int error_code;
+ int exception_is_int;
+ target_ulong exception_next_eip;
+ target_ulong dr[8]; /* debug registers */
+ union {
+ CPUBreakpoint *cpu_breakpoint[4];
+ CPUWatchpoint *cpu_watchpoint[4];
+ }; /* break/watchpoints for dr[0..3] */
+ uint32_t smbase;
+ int old_exception; /* exception in flight */
+
+ /* KVM states, automatically cleared on reset */
+ uint8_t nmi_injected;
+ uint8_t nmi_pending;
+
+ CPU_COMMON
+
+ /* processor features (e.g. for CPUID insn) */
+ uint32_t cpuid_level;
+ uint32_t cpuid_vendor1;
+ uint32_t cpuid_vendor2;
+ uint32_t cpuid_vendor3;
+ uint32_t cpuid_version;
+ uint32_t cpuid_features;
+ uint32_t cpuid_ext_features;
+ uint32_t cpuid_xlevel;
+ uint32_t cpuid_model[12];
+ uint32_t cpuid_ext2_features;
+ uint32_t cpuid_ext3_features;
+ uint32_t cpuid_apic_id;
+ int cpuid_vendor_override;
+
+ /* MTRRs */
+ uint64_t mtrr_fixed[11];
+ uint64_t mtrr_deftype;
+ MTRRVar mtrr_var[8];
+
+ /* For KVM */
+ uint32_t mp_state;
+ int32_t exception_injected;
+ int32_t interrupt_injected;
+ uint8_t soft_interrupt;
+ uint8_t has_error_code;
+ uint32_t sipi_vector;
+ uint32_t cpuid_kvm_features;
+ uint32_t cpuid_svm_features;
+
+ /* in order to simplify APIC support, we leave this pointer to the
+ user */
+ struct DeviceState *apic_state;
+
+ uint64_t mcg_cap;
+ uint64_t mcg_status;
+ uint64_t mcg_ctl;
+ uint64_t mce_banks[MCE_BANKS_DEF*4];
+
+ uint64_t tsc_aux;
+
+ /* vmstate */
+ uint16_t fpus_vmstate;
+ uint16_t fptag_vmstate;
+ uint16_t fpregs_format_vmstate;
+
+ int kvm_vcpu_update_vapic;
+
+ uint64_t xstate_bv;
+ XMMReg ymmh_regs[CPU_NB_REGS];
+
+ uint64_t xcr0;
+} CPUX86State;
+
+CPUX86State *cpu_x86_init(const char *cpu_model);
+int cpu_x86_exec(CPUX86State *s);
+void cpu_x86_close(CPUX86State *s);
+void x86_cpu_list (FILE *f, fprintf_function cpu_fprintf, const char *optarg);
+void x86_cpudef_setup(void);
+int cpu_x86_support_mca_broadcast(CPUState *env);
+
+int cpu_get_pic_interrupt(CPUX86State *s);
+/* MSDOS compatibility mode FPU exception support */
+void cpu_set_ferr(CPUX86State *s);
+
+/* this function must always be used to load data in the segment
+ cache: it synchronizes the hflags with the segment cache values */
+static inline void cpu_x86_load_seg_cache(CPUX86State *env,
+ int seg_reg, unsigned int selector,
+ target_ulong base,
+ unsigned int limit,
+ unsigned int flags)
+{
+ SegmentCache *sc;
+ unsigned int new_hflags;
+
+ sc = &env->segs[seg_reg];
+ sc->selector = selector;
+ sc->base = base;
+ sc->limit = limit;
+ sc->flags = flags;
+
+ /* update the hidden flags */
+ {
+ if (seg_reg == R_CS) {
+#ifdef TARGET_X86_64
+ if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) {
+ /* long mode */
+ env->hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+ env->hflags &= ~(HF_ADDSEG_MASK);
+ } else
+#endif
+ {
+ /* legacy / compatibility case */
+ new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
+ >> (DESC_B_SHIFT - HF_CS32_SHIFT);
+ env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) |
+ new_hflags;
+ }
+ }
+ new_hflags = (env->segs[R_SS].flags & DESC_B_MASK)
+ >> (DESC_B_SHIFT - HF_SS32_SHIFT);
+ if (env->hflags & HF_CS64_MASK) {
+ /* zero base assumed for DS, ES and SS in long mode */
+ } else if (!(env->cr[0] & CR0_PE_MASK) ||
+ (env->eflags & VM_MASK) ||
+ !(env->hflags & HF_CS32_MASK)) {
+ /* XXX: try to avoid this test. The problem comes from the
+ fact that is real mode or vm86 mode we only modify the
+ 'base' and 'selector' fields of the segment cache to go
+ faster. A solution may be to force addseg to one in
+ translate-i386.c. */
+ new_hflags |= HF_ADDSEG_MASK;
+ } else {
+ new_hflags |= ((env->segs[R_DS].base |
+ env->segs[R_ES].base |
+ env->segs[R_SS].base) != 0) <<
+ HF_ADDSEG_SHIFT;
+ }
+ env->hflags = (env->hflags &
+ ~(HF_SS32_MASK | HF_ADDSEG_MASK)) | new_hflags;
+ }
+}
+
+static inline void cpu_x86_load_seg_cache_sipi(CPUX86State *env,
+ int sipi_vector)
+{
+ env->eip = 0;
+ cpu_x86_load_seg_cache(env, R_CS, sipi_vector << 8,
+ sipi_vector << 12,
+ env->segs[R_CS].limit,
+ env->segs[R_CS].flags);
+ env->halted = 0;
+}
+
+int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector,
+ target_ulong *base, unsigned int *limit,
+ unsigned int *flags);
+
+/* wrapper, just in case memory mappings must be changed */
+static inline void cpu_x86_set_cpl(CPUX86State *s, int cpl)
+{
+#if HF_CPL_MASK == 3
+ s->hflags = (s->hflags & ~HF_CPL_MASK) | cpl;
+#else
+#error HF_CPL_MASK is hardcoded
+#endif
+}
+
+/* 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);
+
+/* cpu-exec.c */
+/* the following helpers are only usable in user mode simulation as
+ they can trigger unexpected exceptions */
+void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
+void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32);
+void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_x86_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+
+/* cpuid.c */
+void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
+ uint32_t *eax, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx);
+int cpu_x86_register (CPUX86State *env, const char *cpu_model);
+void cpu_clear_apic_feature(CPUX86State *env);
+void host_cpuid(uint32_t function, uint32_t count,
+ uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
+
+/* helper.c */
+int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
+ int is_write, int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_x86_handle_mmu_fault
+void cpu_x86_set_a20(CPUX86State *env, int a20_state);
+
+static inline int hw_breakpoint_enabled(unsigned long dr7, int index)
+{
+ return (dr7 >> (index * 2)) & 3;
+}
+
+static inline int hw_breakpoint_type(unsigned long dr7, int index)
+{
+ return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3;
+}
+
+static inline int hw_breakpoint_len(unsigned long dr7, int index)
+{
+ int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3);
+ return (len == 2) ? 8 : len + 1;
+}
+
+void hw_breakpoint_insert(CPUX86State *env, int index);
+void hw_breakpoint_remove(CPUX86State *env, int index);
+int check_hw_breakpoints(CPUX86State *env, int force_dr6_update);
+
+/* will be suppressed */
+void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0);
+void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3);
+void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4);
+
+/* hw/pc.c */
+void cpu_smm_update(CPUX86State *env);
+uint64_t cpu_get_tsc(CPUX86State *env);
+CPUState *pc_new_cpu(const char *cpu_model);
+
+/* used to debug */
+#define X86_DUMP_FPU 0x0001 /* dump FPU state too */
+#define X86_DUMP_CCOP 0x0002 /* dump qemu flag cache */
+
+#define TARGET_PAGE_BITS 12
+
+#ifdef TARGET_X86_64
+#define TARGET_PHYS_ADDR_SPACE_BITS 52
+/* ??? This is really 48 bits, sign-extended, but the only thing
+ accessible to userland with bit 48 set is the VSYSCALL, and that
+ is handled via other mechanisms. */
+#define TARGET_VIRT_ADDR_SPACE_BITS 47
+#else
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#endif
+
+#define cpu_init cpu_x86_init
+#define cpu_exec cpu_x86_exec
+#define cpu_gen_code cpu_x86_gen_code
+#define cpu_signal_handler cpu_x86_signal_handler
+#define cpu_list_id x86_cpu_list
+#define cpudef_setup x86_cpudef_setup
+
+#define CPU_SAVE_VERSION 12
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return (env->hflags & HF_CPL_MASK) == 3 ? 1 : 0;
+}
+
+/* translate.c */
+void optimize_flags_init(void);
+
+typedef struct CCTable {
+ int (*compute_all)(void); /* return all the flags */
+ int (*compute_c)(void); /* return the C flag */
+} CCTable;
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regs[R_ESP] = newsp;
+ env->regs[R_EAX] = 0;
+}
+#endif
+
+#include "cpu-all.h"
+#include "svm.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/apic.h"
+#endif
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *cs_base = env->segs[R_CS].base;
+ *pc = *cs_base + env->eip;
+ *flags = env->hflags |
+ (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK));
+}
+
+void do_cpu_init(CPUState *env);
+void do_cpu_sipi(CPUState *env);
+#endif /* CPU_I386_H */
diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c
new file mode 100644
index 0000000..d28de20
--- /dev/null
+++ b/target-i386/cpuid.c
@@ -0,0 +1,1266 @@
+/*
+ * i386 CPUID helper functions
+ *
+ * Copyright (c) 2003 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "kvm.h"
+
+#include "qemu-option.h"
+#include "qemu-config.h"
+
+/* feature flags taken from "Intel Processor Identification and the CPUID
+ * Instruction" and AMD's "CPUID Specification". In cases of disagreement
+ * between feature naming conventions, aliases may be added.
+ */
+static const char *feature_name[] = {
+ "fpu", "vme", "de", "pse",
+ "tsc", "msr", "pae", "mce",
+ "cx8", "apic", NULL, "sep",
+ "mtrr", "pge", "mca", "cmov",
+ "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */,
+ NULL, "ds" /* Intel dts */, "acpi", "mmx",
+ "fxsr", "sse", "sse2", "ss",
+ "ht" /* Intel htt */, "tm", "ia64", "pbe",
+};
+static const char *ext_feature_name[] = {
+ "pni|sse3" /* Intel,AMD sse3 */, "pclmuldq", "dtes64", "monitor",
+ "ds_cpl", "vmx", "smx", "est",
+ "tm2", "ssse3", "cid", NULL,
+ "fma", "cx16", "xtpr", "pdcm",
+ NULL, NULL, "dca", "sse4.1|sse4_1",
+ "sse4.2|sse4_2", "x2apic", "movbe", "popcnt",
+ NULL, "aes", "xsave", "osxsave",
+ "avx", NULL, NULL, "hypervisor",
+};
+static const char *ext2_feature_name[] = {
+ "fpu", "vme", "de", "pse",
+ "tsc", "msr", "pae", "mce",
+ "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall",
+ "mtrr", "pge", "mca", "cmov",
+ "pat", "pse36", NULL, NULL /* Linux mp */,
+ "nx" /* Intel xd */, NULL, "mmxext", "mmx",
+ "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp",
+ NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow",
+};
+static const char *ext3_feature_name[] = {
+ "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */,
+ "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse",
+ "3dnowprefetch", "osvw", "ibs", "xop",
+ "skinit", "wdt", NULL, NULL,
+ "fma4", NULL, "cvt16", "nodeid_msr",
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+};
+
+static const char *kvm_feature_name[] = {
+ "kvmclock", "kvm_nopiodelay", "kvm_mmu", NULL, "kvm_asyncpf", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static const char *svm_feature_name[] = {
+ "npt", "lbrv", "svm_lock", "nrip_save",
+ "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
+ NULL, NULL, "pause_filter", NULL,
+ "pfthreshold", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+};
+
+/* collects per-function cpuid data
+ */
+typedef struct model_features_t {
+ uint32_t *guest_feat;
+ uint32_t *host_feat;
+ uint32_t check_feat;
+ const char **flag_names;
+ uint32_t cpuid;
+ } model_features_t;
+
+int check_cpuid = 0;
+int enforce_cpuid = 0;
+
+void host_cpuid(uint32_t function, uint32_t count,
+ uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
+{
+#if defined(CONFIG_KVM)
+ uint32_t vec[4];
+
+#ifdef __x86_64__
+ asm volatile("cpuid"
+ : "=a"(vec[0]), "=b"(vec[1]),
+ "=c"(vec[2]), "=d"(vec[3])
+ : "0"(function), "c"(count) : "cc");
+#else
+ asm volatile("pusha \n\t"
+ "cpuid \n\t"
+ "mov %%eax, 0(%2) \n\t"
+ "mov %%ebx, 4(%2) \n\t"
+ "mov %%ecx, 8(%2) \n\t"
+ "mov %%edx, 12(%2) \n\t"
+ "popa"
+ : : "a"(function), "c"(count), "S"(vec)
+ : "memory", "cc");
+#endif
+
+ if (eax)
+ *eax = vec[0];
+ if (ebx)
+ *ebx = vec[1];
+ if (ecx)
+ *ecx = vec[2];
+ if (edx)
+ *edx = vec[3];
+#endif
+}
+
+#define iswhite(c) ((c) && ((c) <= ' ' || '~' < (c)))
+
+/* general substring compare of *[s1..e1) and *[s2..e2). sx is start of
+ * a substring. ex if !NULL points to the first char after a substring,
+ * otherwise the string is assumed to sized by a terminating nul.
+ * Return lexical ordering of *s1:*s2.
+ */
+static int sstrcmp(const char *s1, const char *e1, const char *s2,
+ const char *e2)
+{
+ for (;;) {
+ if (!*s1 || !*s2 || *s1 != *s2)
+ return (*s1 - *s2);
+ ++s1, ++s2;
+ if (s1 == e1 && s2 == e2)
+ return (0);
+ else if (s1 == e1)
+ return (*s2);
+ else if (s2 == e2)
+ return (*s1);
+ }
+}
+
+/* compare *[s..e) to *altstr. *altstr may be a simple string or multiple
+ * '|' delimited (possibly empty) strings in which case search for a match
+ * within the alternatives proceeds left to right. Return 0 for success,
+ * non-zero otherwise.
+ */
+static int altcmp(const char *s, const char *e, const char *altstr)
+{
+ const char *p, *q;
+
+ for (q = p = altstr; ; ) {
+ while (*p && *p != '|')
+ ++p;
+ if ((q == p && !*s) || (q != p && !sstrcmp(s, e, q, p)))
+ return (0);
+ if (!*p)
+ return (1);
+ else
+ q = ++p;
+ }
+}
+
+/* search featureset for flag *[s..e), if found set corresponding bit in
+ * *pval and return success, otherwise return zero
+ */
+static int lookup_feature(uint32_t *pval, const char *s, const char *e,
+ const char **featureset)
+{
+ uint32_t mask;
+ const char **ppc;
+
+ for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc)
+ if (*ppc && !altcmp(s, e, *ppc)) {
+ *pval |= mask;
+ break;
+ }
+ return (mask ? 1 : 0);
+}
+
+static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features,
+ uint32_t *ext_features,
+ uint32_t *ext2_features,
+ uint32_t *ext3_features,
+ uint32_t *kvm_features,
+ uint32_t *svm_features)
+{
+ if (!lookup_feature(features, flagname, NULL, feature_name) &&
+ !lookup_feature(ext_features, flagname, NULL, ext_feature_name) &&
+ !lookup_feature(ext2_features, flagname, NULL, ext2_feature_name) &&
+ !lookup_feature(ext3_features, flagname, NULL, ext3_feature_name) &&
+ !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name) &&
+ !lookup_feature(svm_features, flagname, NULL, svm_feature_name))
+ fprintf(stderr, "CPU feature %s not found\n", flagname);
+}
+
+typedef struct x86_def_t {
+ struct x86_def_t *next;
+ const char *name;
+ uint32_t level;
+ uint32_t vendor1, vendor2, vendor3;
+ int family;
+ int model;
+ int stepping;
+ uint32_t features, ext_features, ext2_features, ext3_features;
+ uint32_t kvm_features, svm_features;
+ uint32_t xlevel;
+ char model_id[48];
+ int vendor_override;
+ uint32_t flags;
+} x86_def_t;
+
+#define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE)
+#define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \
+ CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC)
+#define PENTIUM2_FEATURES (PENTIUM_FEATURES | CPUID_PAE | CPUID_SEP | \
+ CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \
+ CPUID_PSE36 | CPUID_FXSR)
+#define PENTIUM3_FEATURES (PENTIUM2_FEATURES | CPUID_SSE)
+#define PPRO_FEATURES (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | \
+ CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \
+ CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \
+ CPUID_PAE | CPUID_SEP | CPUID_APIC)
+#define EXT2_FEATURE_MASK 0x0183F3FF
+
+#define TCG_FEATURES (CPUID_FP87 | CPUID_PSE | CPUID_TSC | CPUID_MSR | \
+ CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \
+ CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \
+ CPUID_PSE36 | CPUID_CLFLUSH | CPUID_ACPI | CPUID_MMX | \
+ CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS)
+ /* partly implemented:
+ CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64)
+ CPUID_PSE36 (needed for Solaris) */
+ /* missing:
+ CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */
+#define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | \
+ CPUID_EXT_CX16 | CPUID_EXT_POPCNT | \
+ CPUID_EXT_HYPERVISOR)
+ /* missing:
+ CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST,
+ CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_XSAVE */
+#define TCG_EXT2_FEATURES ((TCG_FEATURES & EXT2_FEATURE_MASK) | \
+ CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \
+ CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT)
+ /* missing:
+ CPUID_EXT2_PDPE1GB */
+#define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \
+ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A)
+#define TCG_SVM_FEATURES 0
+
+/* maintains list of cpu model definitions
+ */
+static x86_def_t *x86_defs = {NULL};
+
+/* built-in cpu model definitions (deprecated)
+ */
+static x86_def_t builtin_x86_defs[] = {
+ {
+ .name = "qemu64",
+ .level = 4,
+ .vendor1 = CPUID_VENDOR_AMD_1,
+ .vendor2 = CPUID_VENDOR_AMD_2,
+ .vendor3 = CPUID_VENDOR_AMD_3,
+ .family = 6,
+ .model = 2,
+ .stepping = 3,
+ .features = PPRO_FEATURES |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA |
+ CPUID_PSE36,
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT,
+ .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) |
+ CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX,
+ .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM |
+ CPUID_EXT3_ABM | CPUID_EXT3_SSE4A,
+ .xlevel = 0x8000000A,
+ .model_id = "QEMU Virtual CPU version " QEMU_VERSION,
+ },
+ {
+ .name = "phenom",
+ .level = 5,
+ .vendor1 = CPUID_VENDOR_AMD_1,
+ .vendor2 = CPUID_VENDOR_AMD_2,
+ .vendor3 = CPUID_VENDOR_AMD_3,
+ .family = 16,
+ .model = 2,
+ .stepping = 3,
+ .features = PPRO_FEATURES |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA |
+ CPUID_PSE36 | CPUID_VME | CPUID_HT,
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 |
+ CPUID_EXT_POPCNT,
+ .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) |
+ CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX |
+ CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT |
+ CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP,
+ /* Missing: CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC,
+ CPUID_EXT3_CR8LEG,
+ CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH,
+ CPUID_EXT3_OSVW, CPUID_EXT3_IBS */
+ .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM |
+ CPUID_EXT3_ABM | CPUID_EXT3_SSE4A,
+ .svm_features = CPUID_SVM_NPT | CPUID_SVM_LBRV,
+ .xlevel = 0x8000001A,
+ .model_id = "AMD Phenom(tm) 9550 Quad-Core Processor"
+ },
+ {
+ .name = "core2duo",
+ .level = 10,
+ .family = 6,
+ .model = 15,
+ .stepping = 11,
+ .features = PPRO_FEATURES |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA |
+ CPUID_PSE36 | CPUID_VME | CPUID_DTS | CPUID_ACPI | CPUID_SS |
+ CPUID_HT | CPUID_TM | CPUID_PBE,
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 |
+ CPUID_EXT_DTES64 | CPUID_EXT_DSCPL | CPUID_EXT_VMX | CPUID_EXT_EST |
+ CPUID_EXT_TM2 | CPUID_EXT_CX16 | CPUID_EXT_XTPR | CPUID_EXT_PDCM,
+ .ext2_features = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX,
+ .ext3_features = CPUID_EXT3_LAHF_LM,
+ .xlevel = 0x80000008,
+ .model_id = "Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz",
+ },
+ {
+ .name = "kvm64",
+ .level = 5,
+ .vendor1 = CPUID_VENDOR_INTEL_1,
+ .vendor2 = CPUID_VENDOR_INTEL_2,
+ .vendor3 = CPUID_VENDOR_INTEL_3,
+ .family = 15,
+ .model = 6,
+ .stepping = 1,
+ /* Missing: CPUID_VME, CPUID_HT */
+ .features = PPRO_FEATURES |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA |
+ CPUID_PSE36,
+ /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16,
+ /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */
+ .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) |
+ CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX,
+ /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC,
+ CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A,
+ CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH,
+ CPUID_EXT3_OSVW, CPUID_EXT3_IBS, CPUID_EXT3_SVM */
+ .ext3_features = 0,
+ .xlevel = 0x80000008,
+ .model_id = "Common KVM processor"
+ },
+ {
+ .name = "qemu32",
+ .level = 4,
+ .family = 6,
+ .model = 3,
+ .stepping = 3,
+ .features = PPRO_FEATURES,
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_POPCNT,
+ .xlevel = 0x80000004,
+ .model_id = "QEMU Virtual CPU version " QEMU_VERSION,
+ },
+ {
+ .name = "kvm32",
+ .level = 5,
+ .family = 15,
+ .model = 6,
+ .stepping = 1,
+ .features = PPRO_FEATURES |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36,
+ .ext_features = CPUID_EXT_SSE3,
+ .ext2_features = PPRO_FEATURES & EXT2_FEATURE_MASK,
+ .ext3_features = 0,
+ .xlevel = 0x80000008,
+ .model_id = "Common 32-bit KVM processor"
+ },
+ {
+ .name = "coreduo",
+ .level = 10,
+ .family = 6,
+ .model = 14,
+ .stepping = 8,
+ .features = PPRO_FEATURES | CPUID_VME |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_DTS | CPUID_ACPI |
+ CPUID_SS | CPUID_HT | CPUID_TM | CPUID_PBE,
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_VMX |
+ CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR | CPUID_EXT_PDCM,
+ .ext2_features = CPUID_EXT2_NX,
+ .xlevel = 0x80000008,
+ .model_id = "Genuine Intel(R) CPU T2600 @ 2.16GHz",
+ },
+ {
+ .name = "486",
+ .level = 1,
+ .family = 4,
+ .model = 0,
+ .stepping = 0,
+ .features = I486_FEATURES,
+ .xlevel = 0,
+ },
+ {
+ .name = "pentium",
+ .level = 1,
+ .family = 5,
+ .model = 4,
+ .stepping = 3,
+ .features = PENTIUM_FEATURES,
+ .xlevel = 0,
+ },
+ {
+ .name = "pentium2",
+ .level = 2,
+ .family = 6,
+ .model = 5,
+ .stepping = 2,
+ .features = PENTIUM2_FEATURES,
+ .xlevel = 0,
+ },
+ {
+ .name = "pentium3",
+ .level = 2,
+ .family = 6,
+ .model = 7,
+ .stepping = 3,
+ .features = PENTIUM3_FEATURES,
+ .xlevel = 0,
+ },
+ {
+ .name = "athlon",
+ .level = 2,
+ .vendor1 = CPUID_VENDOR_AMD_1,
+ .vendor2 = CPUID_VENDOR_AMD_2,
+ .vendor3 = CPUID_VENDOR_AMD_3,
+ .family = 6,
+ .model = 2,
+ .stepping = 3,
+ .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA,
+ .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT,
+ .xlevel = 0x80000008,
+ /* XXX: put another string ? */
+ .model_id = "QEMU Virtual CPU version " QEMU_VERSION,
+ },
+ {
+ .name = "n270",
+ /* original is on level 10 */
+ .level = 5,
+ .family = 6,
+ .model = 28,
+ .stepping = 2,
+ .features = PPRO_FEATURES |
+ CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME | CPUID_DTS |
+ CPUID_ACPI | CPUID_SS | CPUID_HT | CPUID_TM | CPUID_PBE,
+ /* Some CPUs got no CPUID_SEP */
+ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 |
+ CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR,
+ .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_NX,
+ .ext3_features = CPUID_EXT3_LAHF_LM,
+ .xlevel = 0x8000000A,
+ .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz",
+ },
+};
+
+static int cpu_x86_fill_model_id(char *str)
+{
+ uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx);
+ memcpy(str + i * 16 + 0, &eax, 4);
+ memcpy(str + i * 16 + 4, &ebx, 4);
+ memcpy(str + i * 16 + 8, &ecx, 4);
+ memcpy(str + i * 16 + 12, &edx, 4);
+ }
+ return 0;
+}
+
+static int cpu_x86_fill_host(x86_def_t *x86_cpu_def)
+{
+ uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
+
+ x86_cpu_def->name = "host";
+ host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
+ x86_cpu_def->level = eax;
+ x86_cpu_def->vendor1 = ebx;
+ x86_cpu_def->vendor2 = edx;
+ x86_cpu_def->vendor3 = ecx;
+
+ host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
+ x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF);
+ x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12);
+ x86_cpu_def->stepping = eax & 0x0F;
+ x86_cpu_def->ext_features = ecx;
+ x86_cpu_def->features = edx;
+
+ host_cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
+ x86_cpu_def->xlevel = eax;
+
+ host_cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx);
+ x86_cpu_def->ext2_features = edx;
+ x86_cpu_def->ext3_features = ecx;
+ cpu_x86_fill_model_id(x86_cpu_def->model_id);
+ x86_cpu_def->vendor_override = 0;
+
+
+ /*
+ * Every SVM feature requires emulation support in KVM - so we can't just
+ * read the host features here. KVM might even support SVM features not
+ * available on the host hardware. Just set all bits and mask out the
+ * unsupported ones later.
+ */
+ x86_cpu_def->svm_features = -1;
+
+ return 0;
+}
+
+static int unavailable_host_feature(struct model_features_t *f, uint32_t mask)
+{
+ int i;
+
+ for (i = 0; i < 32; ++i)
+ if (1 << i & mask) {
+ fprintf(stderr, "warning: host cpuid %04x_%04x lacks requested"
+ " flag '%s' [0x%08x]\n",
+ f->cpuid >> 16, f->cpuid & 0xffff,
+ f->flag_names[i] ? f->flag_names[i] : "[reserved]", mask);
+ break;
+ }
+ return 0;
+}
+
+/* best effort attempt to inform user requested cpu flags aren't making
+ * their way to the guest. Note: ft[].check_feat ideally should be
+ * specified via a guest_def field to suppress report of extraneous flags.
+ */
+static int check_features_against_host(x86_def_t *guest_def)
+{
+ x86_def_t host_def;
+ uint32_t mask;
+ int rv, i;
+ struct model_features_t ft[] = {
+ {&guest_def->features, &host_def.features,
+ ~0, feature_name, 0x00000000},
+ {&guest_def->ext_features, &host_def.ext_features,
+ ~CPUID_EXT_HYPERVISOR, ext_feature_name, 0x00000001},
+ {&guest_def->ext2_features, &host_def.ext2_features,
+ ~PPRO_FEATURES, ext2_feature_name, 0x80000000},
+ {&guest_def->ext3_features, &host_def.ext3_features,
+ ~CPUID_EXT3_SVM, ext3_feature_name, 0x80000001}};
+
+ cpu_x86_fill_host(&host_def);
+ for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i)
+ for (mask = 1; mask; mask <<= 1)
+ if (ft[i].check_feat & mask && *ft[i].guest_feat & mask &&
+ !(*ft[i].host_feat & mask)) {
+ unavailable_host_feature(&ft[i], mask);
+ rv = 1;
+ }
+ return rv;
+}
+
+static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
+{
+ unsigned int i;
+ x86_def_t *def;
+
+ char *s = strdup(cpu_model);
+ char *featurestr, *name = strtok(s, ",");
+ /* Features to be added*/
+ uint32_t plus_features = 0, plus_ext_features = 0;
+ uint32_t plus_ext2_features = 0, plus_ext3_features = 0;
+ uint32_t plus_kvm_features = 0, plus_svm_features = 0;
+ /* Features to be removed */
+ uint32_t minus_features = 0, minus_ext_features = 0;
+ uint32_t minus_ext2_features = 0, minus_ext3_features = 0;
+ uint32_t minus_kvm_features = 0, minus_svm_features = 0;
+ uint32_t numvalue;
+
+ for (def = x86_defs; def; def = def->next)
+ if (!strcmp(name, def->name))
+ break;
+ if (kvm_enabled() && strcmp(name, "host") == 0) {
+ cpu_x86_fill_host(x86_cpu_def);
+ } else if (!def) {
+ goto error;
+ } else {
+ memcpy(x86_cpu_def, def, sizeof(*def));
+ }
+
+ plus_kvm_features = ~0; /* not supported bits will be filtered out later */
+
+ add_flagname_to_bitmaps("hypervisor", &plus_features,
+ &plus_ext_features, &plus_ext2_features, &plus_ext3_features,
+ &plus_kvm_features, &plus_svm_features);
+
+ featurestr = strtok(NULL, ",");
+
+ while (featurestr) {
+ char *val;
+ if (featurestr[0] == '+') {
+ add_flagname_to_bitmaps(featurestr + 1, &plus_features,
+ &plus_ext_features, &plus_ext2_features,
+ &plus_ext3_features, &plus_kvm_features,
+ &plus_svm_features);
+ } else if (featurestr[0] == '-') {
+ add_flagname_to_bitmaps(featurestr + 1, &minus_features,
+ &minus_ext_features, &minus_ext2_features,
+ &minus_ext3_features, &minus_kvm_features,
+ &minus_svm_features);
+ } else if ((val = strchr(featurestr, '='))) {
+ *val = 0; val++;
+ if (!strcmp(featurestr, "family")) {
+ char *err;
+ numvalue = strtoul(val, &err, 0);
+ if (!*val || *err) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ x86_cpu_def->family = numvalue;
+ } else if (!strcmp(featurestr, "model")) {
+ char *err;
+ numvalue = strtoul(val, &err, 0);
+ if (!*val || *err || numvalue > 0xff) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ x86_cpu_def->model = numvalue;
+ } else if (!strcmp(featurestr, "stepping")) {
+ char *err;
+ numvalue = strtoul(val, &err, 0);
+ if (!*val || *err || numvalue > 0xf) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ x86_cpu_def->stepping = numvalue ;
+ } else if (!strcmp(featurestr, "level")) {
+ char *err;
+ numvalue = strtoul(val, &err, 0);
+ if (!*val || *err) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ x86_cpu_def->level = numvalue;
+ } else if (!strcmp(featurestr, "xlevel")) {
+ char *err;
+ numvalue = strtoul(val, &err, 0);
+ if (!*val || *err) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ if (numvalue < 0x80000000) {
+ numvalue += 0x80000000;
+ }
+ x86_cpu_def->xlevel = numvalue;
+ } else if (!strcmp(featurestr, "vendor")) {
+ if (strlen(val) != 12) {
+ fprintf(stderr, "vendor string must be 12 chars long\n");
+ goto error;
+ }
+ x86_cpu_def->vendor1 = 0;
+ x86_cpu_def->vendor2 = 0;
+ x86_cpu_def->vendor3 = 0;
+ for(i = 0; i < 4; i++) {
+ x86_cpu_def->vendor1 |= ((uint8_t)val[i ]) << (8 * i);
+ x86_cpu_def->vendor2 |= ((uint8_t)val[i + 4]) << (8 * i);
+ x86_cpu_def->vendor3 |= ((uint8_t)val[i + 8]) << (8 * i);
+ }
+ x86_cpu_def->vendor_override = 1;
+ } else if (!strcmp(featurestr, "model_id")) {
+ pstrcpy(x86_cpu_def->model_id, sizeof(x86_cpu_def->model_id),
+ val);
+ } else {
+ fprintf(stderr, "unrecognized feature %s\n", featurestr);
+ goto error;
+ }
+ } else if (!strcmp(featurestr, "check")) {
+ check_cpuid = 1;
+ } else if (!strcmp(featurestr, "enforce")) {
+ check_cpuid = enforce_cpuid = 1;
+ } else {
+ fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr);
+ goto error;
+ }
+ featurestr = strtok(NULL, ",");
+ }
+ x86_cpu_def->features |= plus_features;
+ x86_cpu_def->ext_features |= plus_ext_features;
+ x86_cpu_def->ext2_features |= plus_ext2_features;
+ x86_cpu_def->ext3_features |= plus_ext3_features;
+ x86_cpu_def->kvm_features |= plus_kvm_features;
+ x86_cpu_def->svm_features |= plus_svm_features;
+ x86_cpu_def->features &= ~minus_features;
+ x86_cpu_def->ext_features &= ~minus_ext_features;
+ x86_cpu_def->ext2_features &= ~minus_ext2_features;
+ x86_cpu_def->ext3_features &= ~minus_ext3_features;
+ x86_cpu_def->kvm_features &= ~minus_kvm_features;
+ x86_cpu_def->svm_features &= ~minus_svm_features;
+ if (check_cpuid) {
+ if (check_features_against_host(x86_cpu_def) && enforce_cpuid)
+ goto error;
+ }
+ free(s);
+ return 0;
+
+error:
+ free(s);
+ return -1;
+}
+
+/* generate a composite string into buf of all cpuid names in featureset
+ * selected by fbits. indicate truncation at bufsize in the event of overflow.
+ * if flags, suppress names undefined in featureset.
+ */
+static void listflags(char *buf, int bufsize, uint32_t fbits,
+ const char **featureset, uint32_t flags)
+{
+ const char **p = &featureset[31];
+ char *q, *b, bit;
+ int nc;
+
+ b = 4 <= bufsize ? buf + (bufsize -= 3) - 1 : NULL;
+ *buf = '\0';
+ for (q = buf, bit = 31; fbits && bufsize; --p, fbits &= ~(1 << bit), --bit)
+ if (fbits & 1 << bit && (*p || !flags)) {
+ if (*p)
+ nc = snprintf(q, bufsize, "%s%s", q == buf ? "" : " ", *p);
+ else
+ nc = snprintf(q, bufsize, "%s[%d]", q == buf ? "" : " ", bit);
+ if (bufsize <= nc) {
+ if (b) {
+ memcpy(b, "...", sizeof("..."));
+ }
+ return;
+ }
+ q += nc;
+ bufsize -= nc;
+ }
+}
+
+/* generate CPU information:
+ * -? list model names
+ * -?model list model names/IDs
+ * -?dump output all model (x86_def_t) data
+ * -?cpuid list all recognized cpuid flag names
+ */
+void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
+{
+ unsigned char model = !strcmp("?model", optarg);
+ unsigned char dump = !strcmp("?dump", optarg);
+ unsigned char cpuid = !strcmp("?cpuid", optarg);
+ x86_def_t *def;
+ char buf[256];
+
+ if (cpuid) {
+ (*cpu_fprintf)(f, "Recognized CPUID flags:\n");
+ listflags(buf, sizeof (buf), (uint32_t)~0, feature_name, 1);
+ (*cpu_fprintf)(f, " f_edx: %s\n", buf);
+ listflags(buf, sizeof (buf), (uint32_t)~0, ext_feature_name, 1);
+ (*cpu_fprintf)(f, " f_ecx: %s\n", buf);
+ listflags(buf, sizeof (buf), (uint32_t)~0, ext2_feature_name, 1);
+ (*cpu_fprintf)(f, " extf_edx: %s\n", buf);
+ listflags(buf, sizeof (buf), (uint32_t)~0, ext3_feature_name, 1);
+ (*cpu_fprintf)(f, " extf_ecx: %s\n", buf);
+ return;
+ }
+ for (def = x86_defs; def; def = def->next) {
+ snprintf(buf, sizeof (buf), def->flags ? "[%s]": "%s", def->name);
+ if (model || dump) {
+ (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id);
+ } else {
+ (*cpu_fprintf)(f, "x86 %16s\n", buf);
+ }
+ if (dump) {
+ memcpy(buf, &def->vendor1, sizeof (def->vendor1));
+ memcpy(buf + 4, &def->vendor2, sizeof (def->vendor2));
+ memcpy(buf + 8, &def->vendor3, sizeof (def->vendor3));
+ buf[12] = '\0';
+ (*cpu_fprintf)(f,
+ " family %d model %d stepping %d level %d xlevel 0x%x"
+ " vendor \"%s\"\n",
+ def->family, def->model, def->stepping, def->level,
+ def->xlevel, buf);
+ listflags(buf, sizeof (buf), def->features, feature_name, 0);
+ (*cpu_fprintf)(f, " feature_edx %08x (%s)\n", def->features,
+ buf);
+ listflags(buf, sizeof (buf), def->ext_features, ext_feature_name,
+ 0);
+ (*cpu_fprintf)(f, " feature_ecx %08x (%s)\n", def->ext_features,
+ buf);
+ listflags(buf, sizeof (buf), def->ext2_features, ext2_feature_name,
+ 0);
+ (*cpu_fprintf)(f, " extfeature_edx %08x (%s)\n",
+ def->ext2_features, buf);
+ listflags(buf, sizeof (buf), def->ext3_features, ext3_feature_name,
+ 0);
+ (*cpu_fprintf)(f, " extfeature_ecx %08x (%s)\n",
+ def->ext3_features, buf);
+ (*cpu_fprintf)(f, "\n");
+ }
+ }
+ if (kvm_enabled()) {
+ (*cpu_fprintf)(f, "x86 %16s\n", "[host]");
+ }
+}
+
+int cpu_x86_register (CPUX86State *env, const char *cpu_model)
+{
+ x86_def_t def1, *def = &def1;
+
+ memset(def, 0, sizeof(*def));
+
+ if (cpu_x86_find_by_name(def, cpu_model) < 0)
+ return -1;
+ if (def->vendor1) {
+ env->cpuid_vendor1 = def->vendor1;
+ env->cpuid_vendor2 = def->vendor2;
+ env->cpuid_vendor3 = def->vendor3;
+ } else {
+ env->cpuid_vendor1 = CPUID_VENDOR_INTEL_1;
+ env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2;
+ env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3;
+ }
+ env->cpuid_vendor_override = def->vendor_override;
+ env->cpuid_level = def->level;
+ if (def->family > 0x0f)
+ env->cpuid_version = 0xf00 | ((def->family - 0x0f) << 20);
+ else
+ env->cpuid_version = def->family << 8;
+ env->cpuid_version |= ((def->model & 0xf) << 4) | ((def->model >> 4) << 16);
+ env->cpuid_version |= def->stepping;
+ env->cpuid_features = def->features;
+ env->pat = 0x0007040600070406ULL;
+ env->cpuid_ext_features = def->ext_features;
+ env->cpuid_ext2_features = def->ext2_features;
+ env->cpuid_ext3_features = def->ext3_features;
+ env->cpuid_xlevel = def->xlevel;
+ env->cpuid_kvm_features = def->kvm_features;
+ env->cpuid_svm_features = def->svm_features;
+ if (!kvm_enabled()) {
+ env->cpuid_features &= TCG_FEATURES;
+ env->cpuid_ext_features &= TCG_EXT_FEATURES;
+ env->cpuid_ext2_features &= (TCG_EXT2_FEATURES
+#ifdef TARGET_X86_64
+ | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM
+#endif
+ );
+ env->cpuid_ext3_features &= TCG_EXT3_FEATURES;
+ env->cpuid_svm_features &= TCG_SVM_FEATURES;
+ }
+ {
+ const char *model_id = def->model_id;
+ int c, len, i;
+ if (!model_id)
+ model_id = "";
+ len = strlen(model_id);
+ for(i = 0; i < 48; i++) {
+ if (i >= len)
+ c = '\0';
+ else
+ c = (uint8_t)model_id[i];
+ env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
+ }
+ }
+ return 0;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+/* copy vendor id string to 32 bit register, nul pad as needed
+ */
+static void cpyid(const char *s, uint32_t *id)
+{
+ char *d = (char *)id;
+ char i;
+
+ for (i = sizeof (*id); i--; )
+ *d++ = *s ? *s++ : '\0';
+}
+
+/* interpret radix and convert from string to arbitrary scalar,
+ * otherwise flag failure
+ */
+#define setscalar(pval, str, perr) \
+{ \
+ char *pend; \
+ unsigned long ul; \
+ \
+ ul = strtoul(str, &pend, 0); \
+ *str && !*pend ? (*pval = ul) : (*perr = 1); \
+}
+
+/* map cpuid options to feature bits, otherwise return failure
+ * (option tags in *str are delimited by whitespace)
+ */
+static void setfeatures(uint32_t *pval, const char *str,
+ const char **featureset, int *perr)
+{
+ const char *p, *q;
+
+ for (q = p = str; *p || *q; q = p) {
+ while (iswhite(*p))
+ q = ++p;
+ while (*p && !iswhite(*p))
+ ++p;
+ if (!*q && !*p)
+ return;
+ if (!lookup_feature(pval, q, p, featureset)) {
+ fprintf(stderr, "error: feature \"%.*s\" not available in set\n",
+ (int)(p - q), q);
+ *perr = 1;
+ return;
+ }
+ }
+}
+
+/* map config file options to x86_def_t form
+ */
+static int cpudef_setfield(const char *name, const char *str, void *opaque)
+{
+ x86_def_t *def = opaque;
+ int err = 0;
+
+ if (!strcmp(name, "name")) {
+ def->name = strdup(str);
+ } else if (!strcmp(name, "model_id")) {
+ strncpy(def->model_id, str, sizeof (def->model_id));
+ } else if (!strcmp(name, "level")) {
+ setscalar(&def->level, str, &err)
+ } else if (!strcmp(name, "vendor")) {
+ cpyid(&str[0], &def->vendor1);
+ cpyid(&str[4], &def->vendor2);
+ cpyid(&str[8], &def->vendor3);
+ } else if (!strcmp(name, "family")) {
+ setscalar(&def->family, str, &err)
+ } else if (!strcmp(name, "model")) {
+ setscalar(&def->model, str, &err)
+ } else if (!strcmp(name, "stepping")) {
+ setscalar(&def->stepping, str, &err)
+ } else if (!strcmp(name, "feature_edx")) {
+ setfeatures(&def->features, str, feature_name, &err);
+ } else if (!strcmp(name, "feature_ecx")) {
+ setfeatures(&def->ext_features, str, ext_feature_name, &err);
+ } else if (!strcmp(name, "extfeature_edx")) {
+ setfeatures(&def->ext2_features, str, ext2_feature_name, &err);
+ } else if (!strcmp(name, "extfeature_ecx")) {
+ setfeatures(&def->ext3_features, str, ext3_feature_name, &err);
+ } else if (!strcmp(name, "xlevel")) {
+ setscalar(&def->xlevel, str, &err)
+ } else {
+ fprintf(stderr, "error: unknown option [%s = %s]\n", name, str);
+ return (1);
+ }
+ if (err) {
+ fprintf(stderr, "error: bad option value [%s = %s]\n", name, str);
+ return (1);
+ }
+ return (0);
+}
+
+/* register config file entry as x86_def_t
+ */
+static int cpudef_register(QemuOpts *opts, void *opaque)
+{
+ x86_def_t *def = qemu_mallocz(sizeof (x86_def_t));
+
+ qemu_opt_foreach(opts, cpudef_setfield, def, 1);
+ def->next = x86_defs;
+ x86_defs = def;
+ return (0);
+}
+
+void cpu_clear_apic_feature(CPUX86State *env)
+{
+ env->cpuid_features &= ~CPUID_APIC;
+}
+
+#endif /* !CONFIG_USER_ONLY */
+
+/* register "cpudef" models defined in configuration file. Here we first
+ * preload any built-in definitions
+ */
+void x86_cpudef_setup(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) {
+ builtin_x86_defs[i].next = x86_defs;
+ builtin_x86_defs[i].flags = 1;
+ x86_defs = &builtin_x86_defs[i];
+ }
+#if !defined(CONFIG_USER_ONLY)
+ qemu_opts_foreach(qemu_find_opts("cpudef"), cpudef_register, NULL, 0);
+#endif
+}
+
+static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx)
+{
+ *ebx = env->cpuid_vendor1;
+ *edx = env->cpuid_vendor2;
+ *ecx = env->cpuid_vendor3;
+
+ /* sysenter isn't supported on compatibility mode on AMD, syscall
+ * isn't supported in compatibility mode on Intel.
+ * Normally we advertise the actual cpu vendor, but you can override
+ * this if you want to use KVM's sysenter/syscall emulation
+ * in compatibility mode and when doing cross vendor migration
+ */
+ if (kvm_enabled() && ! env->cpuid_vendor_override) {
+ host_cpuid(0, 0, NULL, ebx, ecx, edx);
+ }
+}
+
+void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
+ uint32_t *eax, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx)
+{
+ /* test if maximum index reached */
+ if (index & 0x80000000) {
+ if (index > env->cpuid_xlevel)
+ index = env->cpuid_level;
+ } else {
+ if (index > env->cpuid_level)
+ index = env->cpuid_level;
+ }
+
+ switch(index) {
+ case 0:
+ *eax = env->cpuid_level;
+ get_cpuid_vendor(env, ebx, ecx, edx);
+ break;
+ case 1:
+ *eax = env->cpuid_version;
+ *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */
+ *ecx = env->cpuid_ext_features;
+ *edx = env->cpuid_features;
+ if (env->nr_cores * env->nr_threads > 1) {
+ *ebx |= (env->nr_cores * env->nr_threads) << 16;
+ *edx |= 1 << 28; /* HTT bit */
+ }
+ break;
+ case 2:
+ /* cache info: needed for Pentium Pro compatibility */
+ *eax = 1;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0x2c307d;
+ break;
+ case 4:
+ /* cache info: needed for Core compatibility */
+ if (env->nr_cores > 1) {
+ *eax = (env->nr_cores - 1) << 26;
+ } else {
+ *eax = 0;
+ }
+ switch (count) {
+ case 0: /* L1 dcache info */
+ *eax |= 0x0000121;
+ *ebx = 0x1c0003f;
+ *ecx = 0x000003f;
+ *edx = 0x0000001;
+ break;
+ case 1: /* L1 icache info */
+ *eax |= 0x0000122;
+ *ebx = 0x1c0003f;
+ *ecx = 0x000003f;
+ *edx = 0x0000001;
+ break;
+ case 2: /* L2 cache info */
+ *eax |= 0x0000143;
+ if (env->nr_threads > 1) {
+ *eax |= (env->nr_threads - 1) << 14;
+ }
+ *ebx = 0x3c0003f;
+ *ecx = 0x0000fff;
+ *edx = 0x0000001;
+ break;
+ default: /* end of info */
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
+ }
+ break;
+ case 5:
+ /* mwait info: needed for Core compatibility */
+ *eax = 0; /* Smallest monitor-line size in bytes */
+ *ebx = 0; /* Largest monitor-line size in bytes */
+ *ecx = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE;
+ *edx = 0;
+ break;
+ case 6:
+ /* Thermal and Power Leaf */
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
+ case 9:
+ /* Direct Cache Access Information Leaf */
+ *eax = 0; /* Bits 0-31 in DCA_CAP MSR */
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
+ case 0xA:
+ /* Architectural Performance Monitoring Leaf */
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
+ case 0xD:
+ /* Processor Extended State */
+ if (!(env->cpuid_ext_features & CPUID_EXT_XSAVE)) {
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
+ }
+ if (kvm_enabled()) {
+ *eax = kvm_arch_get_supported_cpuid(env, 0xd, count, R_EAX);
+ *ebx = kvm_arch_get_supported_cpuid(env, 0xd, count, R_EBX);
+ *ecx = kvm_arch_get_supported_cpuid(env, 0xd, count, R_ECX);
+ *edx = kvm_arch_get_supported_cpuid(env, 0xd, count, R_EDX);
+ } else {
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ }
+ break;
+ case 0x80000000:
+ *eax = env->cpuid_xlevel;
+ *ebx = env->cpuid_vendor1;
+ *edx = env->cpuid_vendor2;
+ *ecx = env->cpuid_vendor3;
+ break;
+ case 0x80000001:
+ *eax = env->cpuid_version;
+ *ebx = 0;
+ *ecx = env->cpuid_ext3_features;
+ *edx = env->cpuid_ext2_features;
+
+ /* The Linux kernel checks for the CMPLegacy bit and
+ * discards multiple thread information if it is set.
+ * So dont set it here for Intel to make Linux guests happy.
+ */
+ if (env->nr_cores * env->nr_threads > 1) {
+ uint32_t tebx, tecx, tedx;
+ get_cpuid_vendor(env, &tebx, &tecx, &tedx);
+ if (tebx != CPUID_VENDOR_INTEL_1 ||
+ tedx != CPUID_VENDOR_INTEL_2 ||
+ tecx != CPUID_VENDOR_INTEL_3) {
+ *ecx |= 1 << 1; /* CmpLegacy bit */
+ }
+ }
+ if (kvm_enabled()) {
+ uint32_t h_eax, h_edx;
+
+ host_cpuid(index, 0, &h_eax, NULL, NULL, &h_edx);
+
+ /* disable CPU features that the host does not support */
+
+ /* long mode */
+ if ((h_edx & 0x20000000) == 0 /* || !lm_capable_kernel */)
+ *edx &= ~0x20000000;
+ /* syscall */
+ if ((h_edx & 0x00000800) == 0)
+ *edx &= ~0x00000800;
+ /* nx */
+ if ((h_edx & 0x00100000) == 0)
+ *edx &= ~0x00100000;
+
+ /* disable CPU features that KVM cannot support */
+
+ /* svm */
+ if (!kvm_nested)
+ *ecx &= ~CPUID_EXT3_SVM;
+ /* 3dnow */
+ *edx &= ~0xc0000000;
+ }
+ break;
+ case 0x80000002:
+ case 0x80000003:
+ case 0x80000004:
+ *eax = env->cpuid_model[(index - 0x80000002) * 4 + 0];
+ *ebx = env->cpuid_model[(index - 0x80000002) * 4 + 1];
+ *ecx = env->cpuid_model[(index - 0x80000002) * 4 + 2];
+ *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3];
+ break;
+ case 0x80000005:
+ /* cache info (L1 cache) */
+ *eax = 0x01ff01ff;
+ *ebx = 0x01ff01ff;
+ *ecx = 0x40020140;
+ *edx = 0x40020140;
+ break;
+ case 0x80000006:
+ /* cache info (L2 cache) */
+ *eax = 0;
+ *ebx = 0x42004200;
+ *ecx = 0x02008140;
+ *edx = 0;
+ break;
+ case 0x80000008:
+ /* virtual & phys address size in low 2 bytes. */
+/* XXX: This value must match the one used in the MMU code. */
+ if (env->cpuid_ext2_features & CPUID_EXT2_LM) {
+ /* 64 bit processor */
+/* XXX: The physical address space is limited to 42 bits in exec.c. */
+ *eax = 0x00003028; /* 48 bits virtual, 40 bits physical */
+ } else {
+ if (env->cpuid_features & CPUID_PSE36)
+ *eax = 0x00000024; /* 36 bits physical */
+ else
+ *eax = 0x00000020; /* 32 bits physical */
+ }
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ if (env->nr_cores * env->nr_threads > 1) {
+ *ecx |= (env->nr_cores * env->nr_threads) - 1;
+ }
+ break;
+ case 0x8000000A:
+ if (env->cpuid_ext3_features & CPUID_EXT3_SVM) {
+ *eax = 0x00000001; /* SVM Revision */
+ *ebx = 0x00000010; /* nr of ASIDs */
+ *ecx = 0;
+ *edx = env->cpuid_svm_features; /* optional features */
+ } else {
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ }
+ break;
+ default:
+ /* reserved values: zero */
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
+ }
+}
diff --git a/target-i386/exec.h b/target-i386/exec.h
new file mode 100644
index 0000000..fc8945b
--- /dev/null
+++ b/target-i386/exec.h
@@ -0,0 +1,335 @@
+/*
+ * i386 execution defines
+ *
+ * Copyright (c) 2003 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 "dyngen-exec.h"
+
+/* XXX: factorize this mess */
+#ifdef TARGET_X86_64
+#define TARGET_LONG_BITS 64
+#else
+#define TARGET_LONG_BITS 32
+#endif
+
+#include "cpu-defs.h"
+
+register struct CPUX86State *env asm(AREG0);
+
+#include "qemu-common.h"
+#include "qemu-log.h"
+
+#undef EAX
+#define EAX (env->regs[R_EAX])
+#undef ECX
+#define ECX (env->regs[R_ECX])
+#undef EDX
+#define EDX (env->regs[R_EDX])
+#undef EBX
+#define EBX (env->regs[R_EBX])
+#undef ESP
+#define ESP (env->regs[R_ESP])
+#undef EBP
+#define EBP (env->regs[R_EBP])
+#undef ESI
+#define ESI (env->regs[R_ESI])
+#undef EDI
+#define EDI (env->regs[R_EDI])
+#undef EIP
+#define EIP (env->eip)
+#define DF (env->df)
+
+#define CC_SRC (env->cc_src)
+#define CC_DST (env->cc_dst)
+#define CC_OP (env->cc_op)
+
+/* float macros */
+#define FT0 (env->ft0)
+#define ST0 (env->fpregs[env->fpstt].d)
+#define ST(n) (env->fpregs[(env->fpstt + (n)) & 7].d)
+#define ST1 ST(1)
+
+#include "cpu.h"
+#include "exec-all.h"
+
+/* op_helper.c */
+void do_interrupt(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw);
+void do_interrupt_user(int intno, int is_int, int error_code,
+ target_ulong next_eip);
+void QEMU_NORETURN raise_exception_err(int exception_index, int error_code);
+void QEMU_NORETURN raise_exception(int exception_index);
+void QEMU_NORETURN raise_exception_env(int exception_index, CPUState *nenv);
+void do_smm_enter(void);
+
+/* n must be a constant to be efficient */
+static inline target_long lshift(target_long x, int n)
+{
+ if (n >= 0)
+ return x << n;
+ else
+ return x >> (-n);
+}
+
+#include "helper.h"
+
+static inline void svm_check_intercept(uint32_t type)
+{
+ helper_svm_check_intercept_param(type, 0);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#include "softmmu_exec.h"
+
+#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_abs floatx80_abs
+#define floatx_chs floatx80_chs
+#define floatx_round_to_int floatx80_round_to_int
+#define floatx_compare floatx80_compare
+#define floatx_compare_quiet floatx80_compare_quiet
+#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_abs float64_abs
+#define floatx_chs float64_chs
+#define floatx_round_to_int float64_round_to_int
+#define floatx_compare float64_compare
+#define floatx_compare_quiet float64_compare_quiet
+#endif
+
+#define RC_MASK 0xc00
+#define RC_NEAR 0x000
+#define RC_DOWN 0x400
+#define RC_UP 0x800
+#define RC_CHOP 0xc00
+
+#define MAXTAN 9223372036854775808.0
+
+#ifdef USE_X86LDOUBLE
+
+/* only for x86 */
+typedef union {
+ long double d;
+ struct {
+ unsigned long long lower;
+ unsigned short upper;
+ } l;
+} CPU86_LDoubleU;
+
+/* the following deal with x86 long double-precision numbers */
+#define MAXEXPD 0x7fff
+#define EXPBIAS 16383
+#define EXPD(fp) (fp.l.upper & 0x7fff)
+#define SIGND(fp) ((fp.l.upper) & 0x8000)
+#define MANTD(fp) (fp.l.lower)
+#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS
+
+#else
+
+/* NOTE: arm is horrible as double 32 bit words are stored in big endian ! */
+typedef union {
+ double d;
+#if !defined(HOST_WORDS_BIGENDIAN) && !defined(__arm__)
+ struct {
+ uint32_t lower;
+ int32_t upper;
+ } l;
+#else
+ struct {
+ int32_t upper;
+ uint32_t lower;
+ } l;
+#endif
+#ifndef __arm__
+ int64_t ll;
+#endif
+} 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;
+ env->fptags[env->fpstt] = 0; /* validate stack entry */
+}
+
+static inline void fpop(void)
+{
+ env->fptags[env->fpstt] = 1; /* invvalidate stack entry */
+ 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)
+{
+ 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;
+
+ 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)
+{
+ CPU86_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)
+#define FPUS_OE (1 << 3)
+#define FPUS_UE (1 << 4)
+#define FPUS_PE (1 << 5)
+#define FPUS_SF (1 << 6)
+#define FPUS_SE (1 << 7)
+#define FPUS_B (1 << 15)
+
+#define FPUC_EM 0x3f
+
+static inline uint32_t compute_eflags(void)
+{
+ return env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK);
+}
+
+/* NOTE: CC_OP must be modified manually to CC_OP_EFLAGS */
+static inline void load_eflags(int eflags, int update_mask)
+{
+ CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+ DF = 1 - (2 * ((eflags >> 10) & 1));
+ env->eflags = (env->eflags & ~update_mask) |
+ (eflags & update_mask) | 0x2;
+}
+
+static inline int cpu_has_work(CPUState *env)
+{
+ int work;
+
+ work = (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK);
+ work |= env->interrupt_request & CPU_INTERRUPT_NMI;
+ work |= env->interrupt_request & CPU_INTERRUPT_INIT;
+ work |= env->interrupt_request & CPU_INTERRUPT_SIPI;
+
+ return work;
+}
+
+static inline int cpu_halted(CPUState *env) {
+ /* handle exit of HALTED state */
+ if (!env->halted)
+ return 0;
+ /* disable halt condition */
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+/* load efer and update the corresponding hflags. XXX: do consistency
+ checks with cpuid bits ? */
+static inline void cpu_load_efer(CPUState *env, uint64_t val)
+{
+ env->efer = val;
+ env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK);
+ if (env->efer & MSR_EFER_LMA)
+ env->hflags |= HF_LMA_MASK;
+ if (env->efer & MSR_EFER_SVME)
+ env->hflags |= HF_SVME_MASK;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->eip = tb->pc - tb->cs_base;
+}
+
diff --git a/target-i386/fake-exec.c b/target-i386/fake-exec.c
new file mode 100644
index 0000000..e6f8363
--- /dev/null
+++ b/target-i386/fake-exec.c
@@ -0,0 +1,52 @@
+/*
+ * fake-exec.c
+ *
+ * This is a file for stub functions so that compilation is possible
+ * when TCG CPU emulation is disabled during compilation.
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by & Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * This work is licensed under the GNU GPL licence version 2 or later.
+ *
+ */
+#include "exec.h"
+#include "cpu.h"
+#include "tcg.h"
+
+int code_copy_enabled = 0;
+
+CCTable cc_table[CC_OP_NB];
+
+TCGContext tcg_ctx;
+
+void cpu_dump_statistics (CPUState *env, FILE*f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+}
+
+void cpu_gen_init(void)
+{
+}
+
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+
+{
+ return 0;
+}
+
+int cpu_x86_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ return 0;
+}
+
+void optimize_flags_init(void)
+{
+}
+
+void tcg_prologue_init(TCGContext *ctx)
+{
+}
diff --git a/target-i386/helper.c b/target-i386/helper.c
new file mode 100644
index 0000000..86202e6
--- /dev/null
+++ b/target-i386/helper.c
@@ -0,0 +1,1253 @@
+/*
+ * i386 helpers (without register variable usage)
+ *
+ * Copyright (c) 2003 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "qemu-common.h"
+#include "kvm.h"
+#include "kvm_x86.h"
+
+#include "qemu-kvm.h"
+
+//#define DEBUG_MMU
+
+/* NOTE: must be called outside the CPU execute loop */
+void cpu_reset(CPUX86State *env)
+{
+ int i;
+
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, X86_DUMP_FPU | X86_DUMP_CCOP);
+ }
+
+ memset(env, 0, offsetof(CPUX86State, breakpoints));
+
+ tlb_flush(env, 1);
+
+ env->old_exception = -1;
+
+ /* init to reset state */
+
+#ifdef CONFIG_SOFTMMU
+ env->hflags |= HF_SOFTMMU_MASK;
+#endif
+ env->hflags2 |= HF2_GIF_MASK;
+
+ cpu_x86_update_cr0(env, 0x60000010);
+ env->a20_mask = ~0x0;
+ env->smbase = 0x30000;
+
+ env->idt.limit = 0xffff;
+ env->gdt.limit = 0xffff;
+ env->ldt.limit = 0xffff;
+ env->ldt.flags = DESC_P_MASK | (2 << DESC_TYPE_SHIFT);
+ env->tr.limit = 0xffff;
+ env->tr.flags = DESC_P_MASK | (11 << DESC_TYPE_SHIFT);
+
+ cpu_x86_load_seg_cache(env, R_CS, 0xf000, 0xffff0000, 0xffff,
+ DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK |
+ DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffff,
+ DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
+ DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffff,
+ DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
+ DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffff,
+ DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
+ DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffff,
+ DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
+ DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffff,
+ DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
+ DESC_A_MASK);
+
+ env->eip = 0xfff0;
+ env->regs[R_EDX] = env->cpuid_version;
+
+ env->eflags = 0x2;
+
+ /* FPU init */
+ for(i = 0;i < 8; i++)
+ env->fptags[i] = 1;
+ env->fpuc = 0x37f;
+
+ env->mxcsr = 0x1f80;
+
+ memset(env->dr, 0, sizeof(env->dr));
+ env->dr[6] = DR6_FIXED_1;
+ env->dr[7] = DR7_FIXED_1;
+ cpu_breakpoint_remove_all(env, BP_CPU);
+ cpu_watchpoint_remove_all(env, BP_CPU);
+
+ env->mcg_status = 0;
+}
+
+void cpu_x86_close(CPUX86State *env)
+{
+ qemu_free(env);
+}
+
+static void cpu_x86_version(CPUState *env, int *family, int *model)
+{
+ int cpuver = env->cpuid_version;
+
+ if (family == NULL || model == NULL) {
+ return;
+ }
+
+ *family = (cpuver >> 8) & 0x0f;
+ *model = ((cpuver >> 12) & 0xf0) + ((cpuver >> 4) & 0x0f);
+}
+
+/* Broadcast MCA signal for processor version 06H_EH and above */
+int cpu_x86_support_mca_broadcast(CPUState *env)
+{
+ int family = 0;
+ int model = 0;
+
+ cpu_x86_version(env, &family, &model);
+ if ((family == 6 && model >= 14) || family > 6) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/***********************************************************/
+/* x86 debug */
+
+static const char *cc_op_str[] = {
+ "DYNAMIC",
+ "EFLAGS",
+
+ "MULB",
+ "MULW",
+ "MULL",
+ "MULQ",
+
+ "ADDB",
+ "ADDW",
+ "ADDL",
+ "ADDQ",
+
+ "ADCB",
+ "ADCW",
+ "ADCL",
+ "ADCQ",
+
+ "SUBB",
+ "SUBW",
+ "SUBL",
+ "SUBQ",
+
+ "SBBB",
+ "SBBW",
+ "SBBL",
+ "SBBQ",
+
+ "LOGICB",
+ "LOGICW",
+ "LOGICL",
+ "LOGICQ",
+
+ "INCB",
+ "INCW",
+ "INCL",
+ "INCQ",
+
+ "DECB",
+ "DECW",
+ "DECL",
+ "DECQ",
+
+ "SHLB",
+ "SHLW",
+ "SHLL",
+ "SHLQ",
+
+ "SARB",
+ "SARW",
+ "SARL",
+ "SARQ",
+};
+
+static void
+cpu_x86_dump_seg_cache(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ const char *name, struct SegmentCache *sc)
+{
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK) {
+ cpu_fprintf(f, "%-3s=%04x %016" PRIx64 " %08x %08x", name,
+ sc->selector, sc->base, sc->limit, sc->flags & 0x00ffff00);
+ } else
+#endif
+ {
+ cpu_fprintf(f, "%-3s=%04x %08x %08x %08x", name, sc->selector,
+ (uint32_t)sc->base, sc->limit, sc->flags & 0x00ffff00);
+ }
+
+ if (!(env->hflags & HF_PE_MASK) || !(sc->flags & DESC_P_MASK))
+ goto done;
+
+ cpu_fprintf(f, " DPL=%d ", (sc->flags & DESC_DPL_MASK) >> DESC_DPL_SHIFT);
+ if (sc->flags & DESC_S_MASK) {
+ if (sc->flags & DESC_CS_MASK) {
+ cpu_fprintf(f, (sc->flags & DESC_L_MASK) ? "CS64" :
+ ((sc->flags & DESC_B_MASK) ? "CS32" : "CS16"));
+ cpu_fprintf(f, " [%c%c", (sc->flags & DESC_C_MASK) ? 'C' : '-',
+ (sc->flags & DESC_R_MASK) ? 'R' : '-');
+ } else {
+ cpu_fprintf(f, (sc->flags & DESC_B_MASK) ? "DS " : "DS16");
+ cpu_fprintf(f, " [%c%c", (sc->flags & DESC_E_MASK) ? 'E' : '-',
+ (sc->flags & DESC_W_MASK) ? 'W' : '-');
+ }
+ cpu_fprintf(f, "%c]", (sc->flags & DESC_A_MASK) ? 'A' : '-');
+ } else {
+ static const char *sys_type_name[2][16] = {
+ { /* 32 bit mode */
+ "Reserved", "TSS16-avl", "LDT", "TSS16-busy",
+ "CallGate16", "TaskGate", "IntGate16", "TrapGate16",
+ "Reserved", "TSS32-avl", "Reserved", "TSS32-busy",
+ "CallGate32", "Reserved", "IntGate32", "TrapGate32"
+ },
+ { /* 64 bit mode */
+ "<hiword>", "Reserved", "LDT", "Reserved", "Reserved",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "TSS64-avl", "Reserved", "TSS64-busy", "CallGate64",
+ "Reserved", "IntGate64", "TrapGate64"
+ }
+ };
+ cpu_fprintf(f, "%s",
+ sys_type_name[(env->hflags & HF_LMA_MASK) ? 1 : 0]
+ [(sc->flags & DESC_TYPE_MASK)
+ >> DESC_TYPE_SHIFT]);
+ }
+done:
+ cpu_fprintf(f, "\n");
+}
+
+#define DUMP_CODE_BYTES_TOTAL 50
+#define DUMP_CODE_BYTES_BACKWARD 20
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int eflags, i, nb;
+ char cc_op_name[32];
+ static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+ cpu_synchronize_state(env);
+
+ eflags = env->eflags;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK) {
+ cpu_fprintf(f,
+ "RAX=%016" PRIx64 " RBX=%016" PRIx64 " RCX=%016" PRIx64 " RDX=%016" PRIx64 "\n"
+ "RSI=%016" PRIx64 " RDI=%016" PRIx64 " RBP=%016" PRIx64 " RSP=%016" PRIx64 "\n"
+ "R8 =%016" PRIx64 " R9 =%016" PRIx64 " R10=%016" PRIx64 " R11=%016" PRIx64 "\n"
+ "R12=%016" PRIx64 " R13=%016" PRIx64 " R14=%016" PRIx64 " R15=%016" PRIx64 "\n"
+ "RIP=%016" PRIx64 " RFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d SMM=%d HLT=%d\n",
+ env->regs[R_EAX],
+ env->regs[R_EBX],
+ env->regs[R_ECX],
+ env->regs[R_EDX],
+ env->regs[R_ESI],
+ env->regs[R_EDI],
+ env->regs[R_EBP],
+ env->regs[R_ESP],
+ env->regs[8],
+ env->regs[9],
+ env->regs[10],
+ env->regs[11],
+ env->regs[12],
+ env->regs[13],
+ env->regs[14],
+ env->regs[15],
+ env->eip, eflags,
+ eflags & DF_MASK ? 'D' : '-',
+ eflags & CC_O ? 'O' : '-',
+ eflags & CC_S ? 'S' : '-',
+ eflags & CC_Z ? 'Z' : '-',
+ eflags & CC_A ? 'A' : '-',
+ eflags & CC_P ? 'P' : '-',
+ eflags & CC_C ? 'C' : '-',
+ env->hflags & HF_CPL_MASK,
+ (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1,
+ (env->a20_mask >> 20) & 1,
+ (env->hflags >> HF_SMM_SHIFT) & 1,
+ env->halted);
+ } else
+#endif
+ {
+ cpu_fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n"
+ "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n"
+ "EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d SMM=%d HLT=%d\n",
+ (uint32_t)env->regs[R_EAX],
+ (uint32_t)env->regs[R_EBX],
+ (uint32_t)env->regs[R_ECX],
+ (uint32_t)env->regs[R_EDX],
+ (uint32_t)env->regs[R_ESI],
+ (uint32_t)env->regs[R_EDI],
+ (uint32_t)env->regs[R_EBP],
+ (uint32_t)env->regs[R_ESP],
+ (uint32_t)env->eip, eflags,
+ eflags & DF_MASK ? 'D' : '-',
+ eflags & CC_O ? 'O' : '-',
+ eflags & CC_S ? 'S' : '-',
+ eflags & CC_Z ? 'Z' : '-',
+ eflags & CC_A ? 'A' : '-',
+ eflags & CC_P ? 'P' : '-',
+ eflags & CC_C ? 'C' : '-',
+ env->hflags & HF_CPL_MASK,
+ (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1,
+ (env->a20_mask >> 20) & 1,
+ (env->hflags >> HF_SMM_SHIFT) & 1,
+ env->halted);
+ }
+
+ for(i = 0; i < 6; i++) {
+ cpu_x86_dump_seg_cache(env, f, cpu_fprintf, seg_name[i],
+ &env->segs[i]);
+ }
+ cpu_x86_dump_seg_cache(env, f, cpu_fprintf, "LDT", &env->ldt);
+ cpu_x86_dump_seg_cache(env, f, cpu_fprintf, "TR", &env->tr);
+
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ cpu_fprintf(f, "GDT= %016" PRIx64 " %08x\n",
+ env->gdt.base, env->gdt.limit);
+ cpu_fprintf(f, "IDT= %016" PRIx64 " %08x\n",
+ env->idt.base, env->idt.limit);
+ cpu_fprintf(f, "CR0=%08x CR2=%016" PRIx64 " CR3=%016" PRIx64 " CR4=%08x\n",
+ (uint32_t)env->cr[0],
+ env->cr[2],
+ env->cr[3],
+ (uint32_t)env->cr[4]);
+ for(i = 0; i < 4; i++)
+ cpu_fprintf(f, "DR%d=%016" PRIx64 " ", i, env->dr[i]);
+ cpu_fprintf(f, "\nDR6=%016" PRIx64 " DR7=%016" PRIx64 "\n",
+ env->dr[6], env->dr[7]);
+ } else
+#endif
+ {
+ cpu_fprintf(f, "GDT= %08x %08x\n",
+ (uint32_t)env->gdt.base, env->gdt.limit);
+ cpu_fprintf(f, "IDT= %08x %08x\n",
+ (uint32_t)env->idt.base, env->idt.limit);
+ cpu_fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n",
+ (uint32_t)env->cr[0],
+ (uint32_t)env->cr[2],
+ (uint32_t)env->cr[3],
+ (uint32_t)env->cr[4]);
+ for(i = 0; i < 4; i++) {
+ cpu_fprintf(f, "DR%d=" TARGET_FMT_lx " ", i, env->dr[i]);
+ }
+ cpu_fprintf(f, "\nDR6=" TARGET_FMT_lx " DR7=" TARGET_FMT_lx "\n",
+ env->dr[6], env->dr[7]);
+ }
+ if (flags & X86_DUMP_CCOP) {
+ if ((unsigned)env->cc_op < CC_OP_NB)
+ snprintf(cc_op_name, sizeof(cc_op_name), "%s", cc_op_str[env->cc_op]);
+ else
+ snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK) {
+ cpu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%-8s\n",
+ env->cc_src, env->cc_dst,
+ cc_op_name);
+ } else
+#endif
+ {
+ cpu_fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n",
+ (uint32_t)env->cc_src, (uint32_t)env->cc_dst,
+ cc_op_name);
+ }
+ }
+ cpu_fprintf(f, "EFER=%016" PRIx64 "\n", env->efer);
+ if (flags & X86_DUMP_FPU) {
+ int fptag;
+ fptag = 0;
+ for(i = 0; i < 8; i++) {
+ fptag |= ((!env->fptags[i]) << i);
+ }
+ cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
+ env->fpuc,
+ (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11,
+ env->fpstt,
+ fptag,
+ env->mxcsr);
+ for(i=0;i<8;i++) {
+#if defined(USE_X86LDOUBLE)
+ union {
+ long double d;
+ struct {
+ uint64_t lower;
+ uint16_t upper;
+ } l;
+ } tmp;
+ tmp.d = env->fpregs[i].d;
+ cpu_fprintf(f, "FPR%d=%016" PRIx64 " %04x",
+ i, tmp.l.lower, tmp.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
+ cpu_fprintf(f, " ");
+ }
+ if (env->hflags & HF_CS64_MASK)
+ nb = 16;
+ else
+ nb = 8;
+ for(i=0;i<nb;i++) {
+ cpu_fprintf(f, "XMM%02d=%08x%08x%08x%08x",
+ i,
+ env->xmm_regs[i].XMM_L(3),
+ env->xmm_regs[i].XMM_L(2),
+ env->xmm_regs[i].XMM_L(1),
+ env->xmm_regs[i].XMM_L(0));
+ if ((i & 1) == 1)
+ cpu_fprintf(f, "\n");
+ else
+ cpu_fprintf(f, " ");
+ }
+ }
+ if (flags & CPU_DUMP_CODE) {
+ target_ulong base = env->segs[R_CS].base + env->eip;
+ target_ulong offs = MIN(env->eip, DUMP_CODE_BYTES_BACKWARD);
+ uint8_t code;
+ char codestr[3];
+
+ cpu_fprintf(f, "Code=");
+ for (i = 0; i < DUMP_CODE_BYTES_TOTAL; i++) {
+ if (cpu_memory_rw_debug(env, base - offs + i, &code, 1, 0) == 0) {
+ snprintf(codestr, sizeof(codestr), "%02x", code);
+ } else {
+ snprintf(codestr, sizeof(codestr), "??");
+ }
+ cpu_fprintf(f, "%s%s%s%s", i > 0 ? " " : "",
+ i == offs ? "<" : "", codestr, i == offs ? ">" : "");
+ }
+ cpu_fprintf(f, "\n");
+ }
+}
+
+/***********************************************************/
+/* x86 mmu */
+/* XXX: add PGE support */
+
+void cpu_x86_set_a20(CPUX86State *env, int a20_state)
+{
+ a20_state = (a20_state != 0);
+ if (a20_state != ((env->a20_mask >> 20) & 1)) {
+#if defined(DEBUG_MMU)
+ printf("A20 update: a20=%d\n", a20_state);
+#endif
+ /* if the cpu is currently executing code, we must unlink it and
+ all the potentially executing TB */
+ cpu_interrupt(env, CPU_INTERRUPT_EXITTB);
+
+ /* when a20 is changed, all the MMU mappings are invalid, so
+ we must flush everything */
+ tlb_flush(env, 1);
+ env->a20_mask = ~(1 << 20) | (a20_state << 20);
+ }
+}
+
+void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0)
+{
+ int pe_state;
+
+#if defined(DEBUG_MMU)
+ printf("CR0 update: CR0=0x%08x\n", new_cr0);
+#endif
+ if ((new_cr0 & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK)) !=
+ (env->cr[0] & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK))) {
+ tlb_flush(env, 1);
+ }
+
+#ifdef TARGET_X86_64
+ if (!(env->cr[0] & CR0_PG_MASK) && (new_cr0 & CR0_PG_MASK) &&
+ (env->efer & MSR_EFER_LME)) {
+ /* enter in long mode */
+ /* XXX: generate an exception */
+ if (!(env->cr[4] & CR4_PAE_MASK))
+ return;
+ env->efer |= MSR_EFER_LMA;
+ env->hflags |= HF_LMA_MASK;
+ } else if ((env->cr[0] & CR0_PG_MASK) && !(new_cr0 & CR0_PG_MASK) &&
+ (env->efer & MSR_EFER_LMA)) {
+ /* exit long mode */
+ env->efer &= ~MSR_EFER_LMA;
+ env->hflags &= ~(HF_LMA_MASK | HF_CS64_MASK);
+ env->eip &= 0xffffffff;
+ }
+#endif
+ env->cr[0] = new_cr0 | CR0_ET_MASK;
+
+ /* update PE flag in hidden flags */
+ pe_state = (env->cr[0] & CR0_PE_MASK);
+ env->hflags = (env->hflags & ~HF_PE_MASK) | (pe_state << HF_PE_SHIFT);
+ /* ensure that ADDSEG is always set in real mode */
+ env->hflags |= ((pe_state ^ 1) << HF_ADDSEG_SHIFT);
+ /* update FPU flags */
+ env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) |
+ ((new_cr0 << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK));
+}
+
+/* XXX: in legacy PAE mode, generate a GPF if reserved bits are set in
+ the PDPT */
+void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3)
+{
+ env->cr[3] = new_cr3;
+ if (env->cr[0] & CR0_PG_MASK) {
+#if defined(DEBUG_MMU)
+ printf("CR3 update: CR3=" TARGET_FMT_lx "\n", new_cr3);
+#endif
+ tlb_flush(env, 0);
+ }
+}
+
+void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4)
+{
+#if defined(DEBUG_MMU)
+ printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]);
+#endif
+ if ((new_cr4 & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK)) !=
+ (env->cr[4] & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK))) {
+ tlb_flush(env, 1);
+ }
+ /* SSE handling */
+ if (!(env->cpuid_features & CPUID_SSE))
+ new_cr4 &= ~CR4_OSFXSR_MASK;
+ if (new_cr4 & CR4_OSFXSR_MASK)
+ env->hflags |= HF_OSFXSR_MASK;
+ else
+ env->hflags &= ~HF_OSFXSR_MASK;
+
+ env->cr[4] = new_cr4;
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
+ int is_write, int mmu_idx, int is_softmmu)
+{
+ /* user mode only emulation */
+ is_write &= 1;
+ env->cr[2] = addr;
+ env->error_code = (is_write << PG_ERROR_W_BIT);
+ env->error_code |= PG_ERROR_U_MASK;
+ env->exception_index = EXCP0E_PAGE;
+ return 1;
+}
+
+#else
+
+/* XXX: This value should match the one returned by CPUID
+ * and in exec.c */
+# if defined(TARGET_X86_64)
+# define PHYS_ADDR_MASK 0xfffffff000LL
+# else
+# define PHYS_ADDR_MASK 0xffffff000LL
+# endif
+
+/* return value:
+ -1 = cannot handle fault
+ 0 = nothing more to do
+ 1 = generate PF fault
+*/
+int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr,
+ int is_write1, int mmu_idx, int is_softmmu)
+{
+ uint64_t ptep, pte;
+ target_ulong pde_addr, pte_addr;
+ int error_code, is_dirty, prot, page_size, is_write, is_user;
+ target_phys_addr_t paddr;
+ uint32_t page_offset;
+ target_ulong vaddr, virt_addr;
+
+ is_user = mmu_idx == MMU_USER_IDX;
+#if defined(DEBUG_MMU)
+ printf("MMU fault: addr=" TARGET_FMT_lx " w=%d u=%d eip=" TARGET_FMT_lx "\n",
+ addr, is_write1, is_user, env->eip);
+#endif
+ is_write = is_write1 & 1;
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ pte = addr;
+ virt_addr = addr & TARGET_PAGE_MASK;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ page_size = 4096;
+ goto do_mapping;
+ }
+
+ if (env->cr[4] & CR4_PAE_MASK) {
+ uint64_t pde, pdpe;
+ target_ulong pdpe_addr;
+
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint64_t pml4e_addr, pml4e;
+ int32_t sext;
+
+ /* test virtual address sign extension */
+ sext = (int64_t)addr >> 47;
+ if (sext != 0 && sext != -1) {
+ env->error_code = 0;
+ env->exception_index = EXCP0D_GPF;
+ return 1;
+ }
+
+ pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pml4e = ldq_phys(pml4e_addr);
+ if (!(pml4e & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pml4e & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ if (!(pml4e & PG_ACCESSED_MASK)) {
+ pml4e |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pml4e_addr, pml4e);
+ }
+ ptep = pml4e ^ PG_NX_MASK;
+ pdpe_addr = ((pml4e & PHYS_ADDR_MASK) + (((addr >> 30) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pdpe & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ ptep &= pdpe ^ PG_NX_MASK;
+ if (!(pdpe & PG_ACCESSED_MASK)) {
+ pdpe |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pdpe_addr, pdpe);
+ }
+ } else
+#endif
+ {
+ /* XXX: load them when cr3 is loaded ? */
+ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) &
+ env->a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK;
+ }
+
+ pde_addr = ((pdpe & PHYS_ADDR_MASK) + (((addr >> 21) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pde = ldq_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pde & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ ptep &= pde ^ PG_NX_MASK;
+ if (pde & PG_PSE_MASK) {
+ /* 2 MB page */
+ page_size = 2048 * 1024;
+ ptep ^= PG_NX_MASK;
+ if ((ptep & PG_NX_MASK) && is_write1 == 2)
+ goto do_fault_protect;
+ if (is_user) {
+ if (!(ptep & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pde & PG_DIRTY_MASK);
+ if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
+ pde |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pde |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+ /* align to page_size */
+ pte = pde & ((PHYS_ADDR_MASK & ~(page_size - 1)) | 0xfff);
+ virt_addr = addr & ~(page_size - 1);
+ } else {
+ /* 4 KB page */
+ if (!(pde & PG_ACCESSED_MASK)) {
+ pde |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+ pte_addr = ((pde & PHYS_ADDR_MASK) + (((addr >> 12) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pte = ldq_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ if (!(env->efer & MSR_EFER_NXE) && (pte & PG_NX_MASK)) {
+ error_code = PG_ERROR_RSVD_MASK;
+ goto do_fault;
+ }
+ /* combine pde and pte nx, user and rw protections */
+ ptep &= pte ^ PG_NX_MASK;
+ ptep ^= PG_NX_MASK;
+ if ((ptep & PG_NX_MASK) && is_write1 == 2)
+ goto do_fault_protect;
+ if (is_user) {
+ if (!(ptep & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pte & PG_DIRTY_MASK);
+ if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
+ pte |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pte |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pte_addr, pte);
+ }
+ page_size = 4096;
+ virt_addr = addr & ~0xfff;
+ pte = pte & (PHYS_ADDR_MASK | 0xfff);
+ }
+ } else {
+ uint32_t pde;
+
+ /* page directory entry */
+ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) &
+ env->a20_mask;
+ pde = ldl_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ /* if PSE bit is set, then we use a 4MB page */
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ page_size = 4096 * 1024;
+ if (is_user) {
+ if (!(pde & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(pde & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(pde & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pde & PG_DIRTY_MASK);
+ if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
+ pde |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pde |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+
+ pte = pde & ~( (page_size - 1) & ~0xfff); /* align to page_size */
+ ptep = pte;
+ virt_addr = addr & ~(page_size - 1);
+ } else {
+ if (!(pde & PG_ACCESSED_MASK)) {
+ pde |= PG_ACCESSED_MASK;
+ stl_phys_notdirty(pde_addr, pde);
+ }
+
+ /* page directory entry */
+ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) &
+ env->a20_mask;
+ pte = ldl_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK)) {
+ error_code = 0;
+ goto do_fault;
+ }
+ /* combine pde and pte user and rw protections */
+ ptep = pte & pde;
+ if (is_user) {
+ if (!(ptep & PG_USER_MASK))
+ goto do_fault_protect;
+ if (is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ } else {
+ if ((env->cr[0] & CR0_WP_MASK) &&
+ is_write && !(ptep & PG_RW_MASK))
+ goto do_fault_protect;
+ }
+ is_dirty = is_write && !(pte & PG_DIRTY_MASK);
+ if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
+ pte |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pte |= PG_DIRTY_MASK;
+ stl_phys_notdirty(pte_addr, pte);
+ }
+ page_size = 4096;
+ virt_addr = addr & ~0xfff;
+ }
+ }
+ /* the page can be put in the TLB */
+ prot = PAGE_READ;
+ if (!(ptep & PG_NX_MASK))
+ prot |= PAGE_EXEC;
+ if (pte & PG_DIRTY_MASK) {
+ /* only set write access if already dirty... otherwise wait
+ for dirty access */
+ if (is_user) {
+ if (ptep & PG_RW_MASK)
+ prot |= PAGE_WRITE;
+ } else {
+ if (!(env->cr[0] & CR0_WP_MASK) ||
+ (ptep & PG_RW_MASK))
+ prot |= PAGE_WRITE;
+ }
+ }
+ do_mapping:
+ pte = pte & env->a20_mask;
+
+ /* Even if 4MB pages, we map only one 4KB page in the cache to
+ avoid filling it too fast */
+ page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+ paddr = (pte & TARGET_PAGE_MASK) + page_offset;
+ vaddr = virt_addr + page_offset;
+
+ tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size);
+ return 0;
+ do_fault_protect:
+ error_code = PG_ERROR_P_MASK;
+ do_fault:
+ error_code |= (is_write << PG_ERROR_W_BIT);
+ if (is_user)
+ error_code |= PG_ERROR_U_MASK;
+ if (is_write1 == 2 &&
+ (env->efer & MSR_EFER_NXE) &&
+ (env->cr[4] & CR4_PAE_MASK))
+ error_code |= PG_ERROR_I_D_MASK;
+ if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) {
+ /* cr2 is not modified in case of exceptions */
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
+ addr);
+ } else {
+ env->cr[2] = addr;
+ }
+ env->error_code = error_code;
+ env->exception_index = EXCP0E_PAGE;
+ return 1;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ target_ulong pde_addr, pte_addr;
+ uint64_t pte;
+ target_phys_addr_t paddr;
+ uint32_t page_offset;
+ int page_size;
+
+ if (env->cr[4] & CR4_PAE_MASK) {
+ target_ulong pdpe_addr;
+ uint64_t pde, pdpe;
+
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint64_t pml4e_addr, pml4e;
+ int32_t sext;
+
+ /* test virtual address sign extension */
+ sext = (int64_t)addr >> 47;
+ if (sext != 0 && sext != -1)
+ return -1;
+
+ pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pml4e = ldq_phys(pml4e_addr);
+ if (!(pml4e & PG_PRESENT_MASK))
+ return -1;
+
+ pdpe_addr = ((pml4e & ~0xfff) + (((addr >> 30) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK))
+ return -1;
+ } else
+#endif
+ {
+ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) &
+ env->a20_mask;
+ pdpe = ldq_phys(pdpe_addr);
+ if (!(pdpe & PG_PRESENT_MASK))
+ return -1;
+ }
+
+ pde_addr = ((pdpe & ~0xfff) + (((addr >> 21) & 0x1ff) << 3)) &
+ env->a20_mask;
+ pde = ldq_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK)) {
+ return -1;
+ }
+ if (pde & PG_PSE_MASK) {
+ /* 2 MB page */
+ page_size = 2048 * 1024;
+ pte = pde & ~( (page_size - 1) & ~0xfff); /* align to page_size */
+ } else {
+ /* 4 KB page */
+ pte_addr = ((pde & ~0xfff) + (((addr >> 12) & 0x1ff) << 3)) &
+ env->a20_mask;
+ page_size = 4096;
+ pte = ldq_phys(pte_addr);
+ }
+ if (!(pte & PG_PRESENT_MASK))
+ return -1;
+ } else {
+ uint32_t pde;
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ pte = addr;
+ page_size = 4096;
+ } else {
+ /* page directory entry */
+ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask;
+ pde = ldl_phys(pde_addr);
+ if (!(pde & PG_PRESENT_MASK))
+ return -1;
+ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ pte = pde & ~0x003ff000; /* align to 4MB */
+ page_size = 4096 * 1024;
+ } else {
+ /* page directory entry */
+ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask;
+ pte = ldl_phys(pte_addr);
+ if (!(pte & PG_PRESENT_MASK))
+ return -1;
+ page_size = 4096;
+ }
+ }
+ pte = pte & env->a20_mask;
+ }
+
+ page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1);
+ paddr = (pte & TARGET_PAGE_MASK) + page_offset;
+ return paddr;
+}
+
+void hw_breakpoint_insert(CPUState *env, int index)
+{
+ int type, err = 0;
+
+ switch (hw_breakpoint_type(env->dr[7], index)) {
+ case 0:
+ if (hw_breakpoint_enabled(env->dr[7], index))
+ err = cpu_breakpoint_insert(env, env->dr[index], BP_CPU,
+ &env->cpu_breakpoint[index]);
+ break;
+ case 1:
+ type = BP_CPU | BP_MEM_WRITE;
+ goto insert_wp;
+ case 2:
+ /* No support for I/O watchpoints yet */
+ break;
+ case 3:
+ type = BP_CPU | BP_MEM_ACCESS;
+ insert_wp:
+ err = cpu_watchpoint_insert(env, env->dr[index],
+ hw_breakpoint_len(env->dr[7], index),
+ type, &env->cpu_watchpoint[index]);
+ break;
+ }
+ if (err)
+ env->cpu_breakpoint[index] = NULL;
+}
+
+void hw_breakpoint_remove(CPUState *env, int index)
+{
+ if (!env->cpu_breakpoint[index])
+ return;
+ switch (hw_breakpoint_type(env->dr[7], index)) {
+ case 0:
+ if (hw_breakpoint_enabled(env->dr[7], index))
+ cpu_breakpoint_remove_by_ref(env, env->cpu_breakpoint[index]);
+ break;
+ case 1:
+ case 3:
+ cpu_watchpoint_remove_by_ref(env, env->cpu_watchpoint[index]);
+ break;
+ case 2:
+ /* No support for I/O watchpoints yet */
+ break;
+ }
+}
+
+int check_hw_breakpoints(CPUState *env, int force_dr6_update)
+{
+ target_ulong dr6;
+ int reg, type;
+ int hit_enabled = 0;
+
+ dr6 = env->dr[6] & ~0xf;
+ for (reg = 0; reg < 4; reg++) {
+ type = hw_breakpoint_type(env->dr[7], reg);
+ if ((type == 0 && env->dr[reg] == env->eip) ||
+ ((type & 1) && env->cpu_watchpoint[reg] &&
+ (env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT))) {
+ dr6 |= 1 << reg;
+ if (hw_breakpoint_enabled(env->dr[7], reg))
+ hit_enabled = 1;
+ }
+ }
+ if (hit_enabled || force_dr6_update)
+ env->dr[6] = dr6;
+ return hit_enabled;
+}
+
+static CPUDebugExcpHandler *prev_debug_excp_handler;
+
+void raise_exception_env(int exception_index, CPUState *env);
+
+static void breakpoint_handler(CPUState *env)
+{
+ CPUBreakpoint *bp;
+
+ if (env->watchpoint_hit) {
+ if (env->watchpoint_hit->flags & BP_CPU) {
+ env->watchpoint_hit = NULL;
+ if (check_hw_breakpoints(env, 0))
+ raise_exception_env(EXCP01_DB, env);
+ else
+ cpu_resume_from_signal(env, NULL);
+ }
+ } else {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry)
+ if (bp->pc == env->eip) {
+ if (bp->flags & BP_CPU) {
+ check_hw_breakpoints(env, 1);
+ raise_exception_env(EXCP01_DB, env);
+ }
+ break;
+ }
+ }
+ if (prev_debug_excp_handler)
+ prev_debug_excp_handler(env);
+}
+
+/* This should come from sysemu.h - if we could include it here... */
+void qemu_system_reset_request(void);
+
+static void qemu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc)
+{
+ uint64_t mcg_cap = cenv->mcg_cap;
+ uint64_t *banks = cenv->mce_banks;
+
+ /*
+ * if MSR_MCG_CTL is not all 1s, the uncorrected error
+ * reporting is disabled
+ */
+ if ((status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) &&
+ cenv->mcg_ctl != ~(uint64_t)0)
+ return;
+ banks += 4 * bank;
+ /*
+ * if MSR_MCi_CTL is not all 1s, the uncorrected error
+ * reporting is disabled for the bank
+ */
+ if ((status & MCI_STATUS_UC) && banks[0] != ~(uint64_t)0)
+ return;
+ if (status & MCI_STATUS_UC) {
+ if ((cenv->mcg_status & MCG_STATUS_MCIP) ||
+ !(cenv->cr[4] & CR4_MCE_MASK)) {
+ fprintf(stderr, "injects mce exception while previous "
+ "one is in progress!\n");
+ qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
+ qemu_system_reset_request();
+ return;
+ }
+ if (banks[1] & MCI_STATUS_VAL)
+ status |= MCI_STATUS_OVER;
+ banks[2] = addr;
+ banks[3] = misc;
+ cenv->mcg_status = mcg_status;
+ banks[1] = status;
+ cpu_interrupt(cenv, CPU_INTERRUPT_MCE);
+ } else if (!(banks[1] & MCI_STATUS_VAL)
+ || !(banks[1] & MCI_STATUS_UC)) {
+ if (banks[1] & MCI_STATUS_VAL)
+ status |= MCI_STATUS_OVER;
+ banks[2] = addr;
+ banks[3] = misc;
+ banks[1] = status;
+ } else
+ banks[1] |= MCI_STATUS_OVER;
+}
+
+void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc,
+ int broadcast)
+{
+ unsigned bank_num = cenv->mcg_cap & 0xff;
+ CPUState *env;
+ int flag = 0;
+
+ if (bank >= bank_num || !(status & MCI_STATUS_VAL)) {
+ return;
+ }
+
+ if (broadcast) {
+ if (!cpu_x86_support_mca_broadcast(cenv)) {
+ fprintf(stderr, "Current CPU does not support broadcast\n");
+ return;
+ }
+ }
+
+ if (kvm_enabled()) {
+ if (broadcast) {
+ flag |= MCE_BROADCAST;
+ }
+
+ kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, flag);
+ } else {
+ qemu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc);
+ if (broadcast) {
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ if (cenv == env) {
+ continue;
+ }
+ qemu_inject_x86_mce(env, 1, MCI_STATUS_VAL | MCI_STATUS_UC,
+ MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0);
+ }
+ }
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
+
+static void mce_init(CPUX86State *cenv)
+{
+ unsigned int bank, bank_num;
+
+ if (((cenv->cpuid_version >> 8)&0xf) >= 6
+ && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)) {
+ cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF;
+ cenv->mcg_ctl = ~(uint64_t)0;
+ bank_num = MCE_BANKS_DEF;
+ for (bank = 0; bank < bank_num; bank++)
+ cenv->mce_banks[bank*4] = ~(uint64_t)0;
+ }
+}
+
+int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector,
+ target_ulong *base, unsigned int *limit,
+ unsigned int *flags)
+{
+ SegmentCache *dt;
+ target_ulong ptr;
+ uint32_t e1, e2;
+ int index;
+
+ if (selector & 0x4)
+ dt = &env->ldt;
+ else
+ dt = &env->gdt;
+ index = selector & ~7;
+ ptr = dt->base + index;
+ if ((index + 7) > dt->limit
+ || cpu_memory_rw_debug(env, ptr, (uint8_t *)&e1, sizeof(e1), 0) != 0
+ || cpu_memory_rw_debug(env, ptr+4, (uint8_t *)&e2, sizeof(e2), 0) != 0)
+ return 0;
+
+ *base = ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
+ *limit = (e1 & 0xffff) | (e2 & 0x000f0000);
+ if (e2 & DESC_G_MASK)
+ *limit = (*limit << 12) | 0xfff;
+ *flags = e2;
+
+ return 1;
+}
+
+CPUX86State *cpu_x86_init(const char *cpu_model)
+{
+ CPUX86State *env;
+ static int inited;
+
+ env = qemu_mallocz(sizeof(CPUX86State));
+ cpu_exec_init(env);
+ env->cpu_model_str = cpu_model;
+
+ /* init various static tables */
+ if (!inited) {
+ inited = 1;
+ optimize_flags_init();
+#ifndef CONFIG_USER_ONLY
+ prev_debug_excp_handler =
+ cpu_set_debug_excp_handler(breakpoint_handler);
+#endif
+ }
+ if (cpu_x86_register(env, cpu_model) < 0) {
+ cpu_x86_close(env);
+ return NULL;
+ }
+ mce_init(env);
+
+ qemu_init_vcpu(env);
+
+ return env;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void do_cpu_init(CPUState *env)
+{
+ int sipi = env->interrupt_request & CPU_INTERRUPT_SIPI;
+ cpu_reset(env);
+ env->interrupt_request = sipi;
+ apic_init_reset(env->apic_state);
+ env->halted = !cpu_is_bsp(env);
+}
+
+void do_cpu_sipi(CPUState *env)
+{
+ apic_sipi(env->apic_state);
+}
+#else
+void do_cpu_init(CPUState *env)
+{
+}
+void do_cpu_sipi(CPUState *env)
+{
+}
+#endif
diff --git a/target-i386/helper.h b/target-i386/helper.h
new file mode 100644
index 0000000..6b518ad
--- /dev/null
+++ b/target-i386/helper.h
@@ -0,0 +1,220 @@
+#include "def-helper.h"
+
+DEF_HELPER_FLAGS_1(cc_compute_all, TCG_CALL_PURE, i32, int)
+DEF_HELPER_FLAGS_1(cc_compute_c, TCG_CALL_PURE, i32, int)
+
+DEF_HELPER_0(lock, void)
+DEF_HELPER_0(unlock, void)
+DEF_HELPER_2(write_eflags, void, tl, i32)
+DEF_HELPER_0(read_eflags, tl)
+DEF_HELPER_1(divb_AL, void, tl)
+DEF_HELPER_1(idivb_AL, void, tl)
+DEF_HELPER_1(divw_AX, void, tl)
+DEF_HELPER_1(idivw_AX, void, tl)
+DEF_HELPER_1(divl_EAX, void, tl)
+DEF_HELPER_1(idivl_EAX, void, tl)
+#ifdef TARGET_X86_64
+DEF_HELPER_1(mulq_EAX_T0, void, tl)
+DEF_HELPER_1(imulq_EAX_T0, void, tl)
+DEF_HELPER_2(imulq_T0_T1, tl, tl, tl)
+DEF_HELPER_1(divq_EAX, void, tl)
+DEF_HELPER_1(idivq_EAX, void, tl)
+#endif
+
+DEF_HELPER_1(aam, void, int)
+DEF_HELPER_1(aad, void, int)
+DEF_HELPER_0(aaa, void)
+DEF_HELPER_0(aas, void)
+DEF_HELPER_0(daa, void)
+DEF_HELPER_0(das, void)
+
+DEF_HELPER_1(lsl, tl, tl)
+DEF_HELPER_1(lar, tl, tl)
+DEF_HELPER_1(verr, void, tl)
+DEF_HELPER_1(verw, void, tl)
+DEF_HELPER_1(lldt, void, int)
+DEF_HELPER_1(ltr, void, int)
+DEF_HELPER_2(load_seg, void, int, int)
+DEF_HELPER_3(ljmp_protected, void, int, tl, int)
+DEF_HELPER_4(lcall_real, void, int, tl, int, int)
+DEF_HELPER_4(lcall_protected, void, int, tl, int, int)
+DEF_HELPER_1(iret_real, void, int)
+DEF_HELPER_2(iret_protected, void, int, int)
+DEF_HELPER_2(lret_protected, void, int, int)
+DEF_HELPER_1(read_crN, tl, int)
+DEF_HELPER_2(write_crN, void, int, tl)
+DEF_HELPER_1(lmsw, void, tl)
+DEF_HELPER_0(clts, void)
+DEF_HELPER_2(movl_drN_T0, void, int, tl)
+DEF_HELPER_1(invlpg, void, tl)
+
+DEF_HELPER_3(enter_level, void, int, int, tl)
+#ifdef TARGET_X86_64
+DEF_HELPER_3(enter64_level, void, int, int, tl)
+#endif
+DEF_HELPER_0(sysenter, void)
+DEF_HELPER_1(sysexit, void, int)
+#ifdef TARGET_X86_64
+DEF_HELPER_1(syscall, void, int)
+DEF_HELPER_1(sysret, void, int)
+#endif
+DEF_HELPER_1(hlt, void, int)
+DEF_HELPER_1(monitor, void, tl)
+DEF_HELPER_1(mwait, void, int)
+DEF_HELPER_0(debug, void)
+DEF_HELPER_0(reset_rf, void)
+DEF_HELPER_2(raise_interrupt, void, int, int)
+DEF_HELPER_1(raise_exception, void, int)
+DEF_HELPER_0(cli, void)
+DEF_HELPER_0(sti, void)
+DEF_HELPER_0(set_inhibit_irq, void)
+DEF_HELPER_0(reset_inhibit_irq, void)
+DEF_HELPER_2(boundw, void, tl, int)
+DEF_HELPER_2(boundl, void, tl, int)
+DEF_HELPER_0(rsm, void)
+DEF_HELPER_1(into, void, int)
+DEF_HELPER_1(cmpxchg8b, void, tl)
+#ifdef TARGET_X86_64
+DEF_HELPER_1(cmpxchg16b, void, tl)
+#endif
+DEF_HELPER_0(single_step, void)
+DEF_HELPER_0(cpuid, void)
+DEF_HELPER_0(rdtsc, void)
+DEF_HELPER_0(rdtscp, void)
+DEF_HELPER_0(rdpmc, void)
+DEF_HELPER_0(rdmsr, void)
+DEF_HELPER_0(wrmsr, void)
+
+DEF_HELPER_1(check_iob, void, i32)
+DEF_HELPER_1(check_iow, void, i32)
+DEF_HELPER_1(check_iol, void, i32)
+DEF_HELPER_2(outb, void, i32, i32)
+DEF_HELPER_1(inb, tl, i32)
+DEF_HELPER_2(outw, void, i32, i32)
+DEF_HELPER_1(inw, tl, i32)
+DEF_HELPER_2(outl, void, i32, i32)
+DEF_HELPER_1(inl, tl, i32)
+
+DEF_HELPER_2(svm_check_intercept_param, void, i32, i64)
+DEF_HELPER_2(vmexit, void, i32, i64)
+DEF_HELPER_3(svm_check_io, void, i32, i32, i32)
+DEF_HELPER_2(vmrun, void, int, int)
+DEF_HELPER_0(vmmcall, void)
+DEF_HELPER_1(vmload, void, int)
+DEF_HELPER_1(vmsave, void, int)
+DEF_HELPER_0(stgi, void)
+DEF_HELPER_0(clgi, void)
+DEF_HELPER_0(skinit, void)
+DEF_HELPER_1(invlpga, void, int)
+
+/* x86 FPU */
+
+DEF_HELPER_1(flds_FT0, void, i32)
+DEF_HELPER_1(fldl_FT0, void, i64)
+DEF_HELPER_1(fildl_FT0, void, s32)
+DEF_HELPER_1(flds_ST0, void, i32)
+DEF_HELPER_1(fldl_ST0, void, i64)
+DEF_HELPER_1(fildl_ST0, void, s32)
+DEF_HELPER_1(fildll_ST0, void, s64)
+DEF_HELPER_0(fsts_ST0, i32)
+DEF_HELPER_0(fstl_ST0, i64)
+DEF_HELPER_0(fist_ST0, s32)
+DEF_HELPER_0(fistl_ST0, s32)
+DEF_HELPER_0(fistll_ST0, s64)
+DEF_HELPER_0(fistt_ST0, s32)
+DEF_HELPER_0(fisttl_ST0, s32)
+DEF_HELPER_0(fisttll_ST0, s64)
+DEF_HELPER_1(fldt_ST0, void, tl)
+DEF_HELPER_1(fstt_ST0, void, tl)
+DEF_HELPER_0(fpush, void)
+DEF_HELPER_0(fpop, void)
+DEF_HELPER_0(fdecstp, void)
+DEF_HELPER_0(fincstp, void)
+DEF_HELPER_1(ffree_STN, void, int)
+DEF_HELPER_0(fmov_ST0_FT0, void)
+DEF_HELPER_1(fmov_FT0_STN, void, int)
+DEF_HELPER_1(fmov_ST0_STN, void, int)
+DEF_HELPER_1(fmov_STN_ST0, void, int)
+DEF_HELPER_1(fxchg_ST0_STN, void, int)
+DEF_HELPER_0(fcom_ST0_FT0, void)
+DEF_HELPER_0(fucom_ST0_FT0, void)
+DEF_HELPER_0(fcomi_ST0_FT0, void)
+DEF_HELPER_0(fucomi_ST0_FT0, void)
+DEF_HELPER_0(fadd_ST0_FT0, void)
+DEF_HELPER_0(fmul_ST0_FT0, void)
+DEF_HELPER_0(fsub_ST0_FT0, void)
+DEF_HELPER_0(fsubr_ST0_FT0, void)
+DEF_HELPER_0(fdiv_ST0_FT0, void)
+DEF_HELPER_0(fdivr_ST0_FT0, void)
+DEF_HELPER_1(fadd_STN_ST0, void, int)
+DEF_HELPER_1(fmul_STN_ST0, void, int)
+DEF_HELPER_1(fsub_STN_ST0, void, int)
+DEF_HELPER_1(fsubr_STN_ST0, void, int)
+DEF_HELPER_1(fdiv_STN_ST0, void, int)
+DEF_HELPER_1(fdivr_STN_ST0, void, int)
+DEF_HELPER_0(fchs_ST0, void)
+DEF_HELPER_0(fabs_ST0, void)
+DEF_HELPER_0(fxam_ST0, void)
+DEF_HELPER_0(fld1_ST0, void)
+DEF_HELPER_0(fldl2t_ST0, void)
+DEF_HELPER_0(fldl2e_ST0, void)
+DEF_HELPER_0(fldpi_ST0, void)
+DEF_HELPER_0(fldlg2_ST0, void)
+DEF_HELPER_0(fldln2_ST0, void)
+DEF_HELPER_0(fldz_ST0, void)
+DEF_HELPER_0(fldz_FT0, void)
+DEF_HELPER_0(fnstsw, i32)
+DEF_HELPER_0(fnstcw, i32)
+DEF_HELPER_1(fldcw, void, i32)
+DEF_HELPER_0(fclex, void)
+DEF_HELPER_0(fwait, void)
+DEF_HELPER_0(fninit, void)
+DEF_HELPER_1(fbld_ST0, void, tl)
+DEF_HELPER_1(fbst_ST0, void, tl)
+DEF_HELPER_0(f2xm1, void)
+DEF_HELPER_0(fyl2x, void)
+DEF_HELPER_0(fptan, void)
+DEF_HELPER_0(fpatan, void)
+DEF_HELPER_0(fxtract, void)
+DEF_HELPER_0(fprem1, void)
+DEF_HELPER_0(fprem, void)
+DEF_HELPER_0(fyl2xp1, void)
+DEF_HELPER_0(fsqrt, void)
+DEF_HELPER_0(fsincos, void)
+DEF_HELPER_0(frndint, void)
+DEF_HELPER_0(fscale, void)
+DEF_HELPER_0(fsin, void)
+DEF_HELPER_0(fcos, void)
+DEF_HELPER_2(fstenv, void, tl, int)
+DEF_HELPER_2(fldenv, void, tl, int)
+DEF_HELPER_2(fsave, void, tl, int)
+DEF_HELPER_2(frstor, void, tl, int)
+DEF_HELPER_2(fxsave, void, tl, int)
+DEF_HELPER_2(fxrstor, void, tl, int)
+DEF_HELPER_1(bsf, tl, tl)
+DEF_HELPER_1(bsr, tl, tl)
+DEF_HELPER_2(lzcnt, tl, tl, int)
+
+/* MMX/SSE */
+
+DEF_HELPER_0(enter_mmx, void)
+DEF_HELPER_0(emms, void)
+DEF_HELPER_2(movq, void, ptr, ptr)
+
+#define SHIFT 0
+#include "ops_sse_header.h"
+#define SHIFT 1
+#include "ops_sse_header.h"
+
+DEF_HELPER_2(rclb, tl, tl, tl)
+DEF_HELPER_2(rclw, tl, tl, tl)
+DEF_HELPER_2(rcll, tl, tl, tl)
+DEF_HELPER_2(rcrb, tl, tl, tl)
+DEF_HELPER_2(rcrw, tl, tl, tl)
+DEF_HELPER_2(rcrl, tl, tl, tl)
+#ifdef TARGET_X86_64
+DEF_HELPER_2(rclq, tl, tl, tl)
+DEF_HELPER_2(rcrq, tl, tl, tl)
+#endif
+
+#include "def-helper.h"
diff --git a/target-i386/helper_template.h b/target-i386/helper_template.h
new file mode 100644
index 0000000..afc41fb
--- /dev/null
+++ b/target-i386/helper_template.h
@@ -0,0 +1,334 @@
+/*
+ * i386 helpers
+ *
+ * Copyright (c) 2008 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/>.
+ */
+#define DATA_BITS (1 << (3 + SHIFT))
+#define SHIFT_MASK (DATA_BITS - 1)
+#define SIGN_MASK (((target_ulong)1) << (DATA_BITS - 1))
+#if DATA_BITS <= 32
+#define SHIFT1_MASK 0x1f
+#else
+#define SHIFT1_MASK 0x3f
+#endif
+
+#if DATA_BITS == 8
+#define SUFFIX b
+#define DATA_TYPE uint8_t
+#define DATA_STYPE int8_t
+#define DATA_MASK 0xff
+#elif DATA_BITS == 16
+#define SUFFIX w
+#define DATA_TYPE uint16_t
+#define DATA_STYPE int16_t
+#define DATA_MASK 0xffff
+#elif DATA_BITS == 32
+#define SUFFIX l
+#define DATA_TYPE uint32_t
+#define DATA_STYPE int32_t
+#define DATA_MASK 0xffffffff
+#elif DATA_BITS == 64
+#define SUFFIX q
+#define DATA_TYPE uint64_t
+#define DATA_STYPE int64_t
+#define DATA_MASK 0xffffffffffffffffULL
+#else
+#error unhandled operand size
+#endif
+
+/* dynamic flags computation */
+
+static int glue(compute_all_add, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_SRC;
+ src2 = CC_DST - CC_SRC;
+ cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_add, SUFFIX)(void)
+{
+ int cf;
+ target_long src1;
+ src1 = CC_SRC;
+ cf = (DATA_TYPE)CC_DST < (DATA_TYPE)src1;
+ return cf;
+}
+
+static int glue(compute_all_adc, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_SRC;
+ src2 = CC_DST - CC_SRC - 1;
+ cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2 ^ -1) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_adc, SUFFIX)(void)
+{
+ int cf;
+ target_long src1;
+ src1 = CC_SRC;
+ cf = (DATA_TYPE)CC_DST <= (DATA_TYPE)src1;
+ return cf;
+}
+
+static int glue(compute_all_sub, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 < (DATA_TYPE)src2;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_sub, SUFFIX)(void)
+{
+ int cf;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 < (DATA_TYPE)src2;
+ return cf;
+}
+
+static int glue(compute_all_sbb, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC + 1;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = lshift((src1 ^ src2) & (src1 ^ CC_DST), 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_sbb, SUFFIX)(void)
+{
+ int cf;
+ target_long src1, src2;
+ src1 = CC_DST + CC_SRC + 1;
+ src2 = CC_SRC;
+ cf = (DATA_TYPE)src1 <= (DATA_TYPE)src2;
+ return cf;
+}
+
+static int glue(compute_all_logic, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = 0;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = 0;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_logic, SUFFIX)(void)
+{
+ return 0;
+}
+
+static int glue(compute_all_inc, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST - 1;
+ src2 = 1;
+ cf = CC_SRC;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = ((CC_DST & DATA_MASK) == SIGN_MASK) << 11;
+ return cf | pf | af | zf | sf | of;
+}
+
+#if DATA_BITS == 32
+static int glue(compute_c_inc, SUFFIX)(void)
+{
+ return CC_SRC;
+}
+#endif
+
+static int glue(compute_all_dec, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ target_long src1, src2;
+ src1 = CC_DST + 1;
+ src2 = 1;
+ cf = CC_SRC;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = (CC_DST ^ src1 ^ src2) & 0x10;
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = ((CC_DST & DATA_MASK) == ((target_ulong)SIGN_MASK - 1)) << 11;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_all_shl, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = (CC_SRC >> (DATA_BITS - 1)) & CC_C;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0; /* undefined */
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ /* of is defined if shift count == 1 */
+ of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+static int glue(compute_c_shl, SUFFIX)(void)
+{
+ return (CC_SRC >> (DATA_BITS - 1)) & CC_C;
+}
+
+#if DATA_BITS == 32
+static int glue(compute_c_sar, SUFFIX)(void)
+{
+ return CC_SRC & 1;
+}
+#endif
+
+static int glue(compute_all_sar, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = CC_SRC & 1;
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0; /* undefined */
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ /* of is defined if shift count == 1 */
+ of = lshift(CC_SRC ^ CC_DST, 12 - DATA_BITS) & CC_O;
+ return cf | pf | af | zf | sf | of;
+}
+
+#if DATA_BITS == 32
+static int glue(compute_c_mul, SUFFIX)(void)
+{
+ int cf;
+ cf = (CC_SRC != 0);
+ return cf;
+}
+#endif
+
+/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and
+ CF are modified and it is slower to do that. */
+static int glue(compute_all_mul, SUFFIX)(void)
+{
+ int cf, pf, af, zf, sf, of;
+ cf = (CC_SRC != 0);
+ pf = parity_table[(uint8_t)CC_DST];
+ af = 0; /* undefined */
+ zf = ((DATA_TYPE)CC_DST == 0) << 6;
+ sf = lshift(CC_DST, 8 - DATA_BITS) & 0x80;
+ of = cf << 11;
+ return cf | pf | af | zf | sf | of;
+}
+
+/* shifts */
+
+target_ulong glue(helper_rcl, SUFFIX)(target_ulong t0, target_ulong t1)
+{
+ int count, eflags;
+ target_ulong src;
+ target_long res;
+
+ count = t1 & SHIFT1_MASK;
+#if DATA_BITS == 16
+ count = rclw_table[count];
+#elif DATA_BITS == 8
+ count = rclb_table[count];
+#endif
+ if (count) {
+ eflags = helper_cc_compute_all(CC_OP);
+ t0 &= DATA_MASK;
+ src = t0;
+ res = (t0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1));
+ if (count > 1)
+ res |= t0 >> (DATA_BITS + 1 - count);
+ t0 = res;
+ env->cc_tmp = (eflags & ~(CC_C | CC_O)) |
+ (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) |
+ ((src >> (DATA_BITS - count)) & CC_C);
+ } else {
+ env->cc_tmp = -1;
+ }
+ return t0;
+}
+
+target_ulong glue(helper_rcr, SUFFIX)(target_ulong t0, target_ulong t1)
+{
+ int count, eflags;
+ target_ulong src;
+ target_long res;
+
+ count = t1 & SHIFT1_MASK;
+#if DATA_BITS == 16
+ count = rclw_table[count];
+#elif DATA_BITS == 8
+ count = rclb_table[count];
+#endif
+ if (count) {
+ eflags = helper_cc_compute_all(CC_OP);
+ t0 &= DATA_MASK;
+ src = t0;
+ res = (t0 >> count) | ((target_ulong)(eflags & CC_C) << (DATA_BITS - count));
+ if (count > 1)
+ res |= t0 << (DATA_BITS + 1 - count);
+ t0 = res;
+ env->cc_tmp = (eflags & ~(CC_C | CC_O)) |
+ (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) |
+ ((src >> (count - 1)) & CC_C);
+ } else {
+ env->cc_tmp = -1;
+ }
+ return t0;
+}
+
+#undef DATA_BITS
+#undef SHIFT_MASK
+#undef SHIFT1_MASK
+#undef SIGN_MASK
+#undef DATA_TYPE
+#undef DATA_STYPE
+#undef DATA_MASK
+#undef SUFFIX
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
new file mode 100644
index 0000000..f389b85
--- /dev/null
+++ b/target-i386/kvm.c
@@ -0,0 +1,1957 @@
+/*
+ * QEMU KVM support
+ *
+ * Copyright (C) 2006-2008 Qumranet Technologies
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "cpu.h"
+#include "gdbstub.h"
+#include "host-utils.h"
+#include "hw/pc.h"
+#include "hw/apic.h"
+#include "ioport.h"
+#include "kvm_x86.h"
+
+#ifdef CONFIG_KVM_PARA
+#include <linux/kvm_para.h>
+#endif
+//
+//#define DEBUG_KVM
+
+#ifdef DEBUG_KVM
+#define DPRINTF(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define MSR_KVM_WALL_CLOCK 0x11
+#define MSR_KVM_SYSTEM_TIME 0x12
+
+#ifndef BUS_MCEERR_AR
+#define BUS_MCEERR_AR 4
+#endif
+#ifndef BUS_MCEERR_AO
+#define BUS_MCEERR_AO 5
+#endif
+
+#ifdef OBSOLETE_KVM_IMPL
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_INFO(SET_TSS_ADDR),
+ KVM_CAP_INFO(EXT_CPUID),
+ KVM_CAP_INFO(MP_STATE),
+ KVM_CAP_LAST_INFO
+};
+#endif
+
+static bool has_msr_star;
+static bool has_msr_hsave_pa;
+#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF)
+static bool has_msr_async_pf_en;
+#endif
+static int lm_capable_kernel;
+
+static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r, size;
+
+ size = sizeof(*cpuid) + max * sizeof(*cpuid->entries);
+ cpuid = (struct kvm_cpuid2 *)qemu_mallocz(size);
+ cpuid->nent = max;
+ r = kvm_ioctl(s, KVM_GET_SUPPORTED_CPUID, cpuid);
+ if (r == 0 && cpuid->nent >= max) {
+ r = -E2BIG;
+ }
+ if (r < 0) {
+ if (r == -E2BIG) {
+ qemu_free(cpuid);
+ return NULL;
+ } else {
+ fprintf(stderr, "KVM_GET_SUPPORTED_CPUID failed: %s\n",
+ strerror(-r));
+ exit(1);
+ }
+ }
+ return cpuid;
+}
+
+uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function,
+ uint32_t index, int reg)
+{
+ struct kvm_cpuid2 *cpuid;
+ int i, max;
+ uint32_t ret = 0;
+ uint32_t cpuid_1_edx;
+
+ max = 1;
+ while ((cpuid = try_get_cpuid(env->kvm_state, max)) == NULL) {
+ max *= 2;
+ }
+
+ for (i = 0; i < cpuid->nent; ++i) {
+ if (cpuid->entries[i].function == function &&
+ cpuid->entries[i].index == index) {
+ switch (reg) {
+ case R_EAX:
+ ret = cpuid->entries[i].eax;
+ break;
+ case R_EBX:
+ ret = cpuid->entries[i].ebx;
+ break;
+ case R_ECX:
+ ret = cpuid->entries[i].ecx;
+ break;
+ case R_EDX:
+ ret = cpuid->entries[i].edx;
+ switch (function) {
+ case 1:
+ /* KVM before 2.6.30 misreports the following features */
+ ret |= CPUID_MTRR | CPUID_PAT | CPUID_MCE | CPUID_MCA;
+ break;
+ case 0x80000001:
+ /* On Intel, kvm returns cpuid according to the Intel spec,
+ * so add missing bits according to the AMD spec:
+ */
+ cpuid_1_edx = kvm_arch_get_supported_cpuid(env, 1, 0, R_EDX);
+ ret |= cpuid_1_edx & 0x183f7ff;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ qemu_free(cpuid);
+
+ return ret;
+}
+
+#ifdef CONFIG_KVM_PARA
+struct kvm_para_features {
+ int cap;
+ int feature;
+} para_features[] = {
+ { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE },
+ { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY },
+ { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
+#ifdef KVM_CAP_ASYNC_PF
+ { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF },
+#endif
+ { -1, -1 }
+};
+
+static int get_para_features(CPUState *env)
+{
+ int i, features = 0;
+
+ for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) {
+ if (kvm_check_extension(env->kvm_state, para_features[i].cap)) {
+ features |= (1 << para_features[i].feature);
+ }
+ }
+#ifdef KVM_CAP_ASYNC_PF
+ has_msr_async_pf_en = features & (1 << KVM_FEATURE_ASYNC_PF);
+#endif
+ return features;
+}
+#endif
+
+#ifdef KVM_CAP_MCE
+static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
+ int *max_banks)
+{
+ int r;
+
+ r = kvm_check_extension(s, KVM_CAP_MCE);
+ if (r > 0) {
+ *max_banks = r;
+ return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap);
+ }
+ return -ENOSYS;
+}
+
+static int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap)
+{
+ return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap);
+}
+
+static int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m)
+{
+ return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m);
+}
+
+static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n)
+{
+ struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs);
+ int r;
+
+ kmsrs->nmsrs = n;
+ memcpy(kmsrs->entries, msrs, n * sizeof *msrs);
+ r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs);
+ memcpy(msrs, kmsrs->entries, n * sizeof *msrs);
+ free(kmsrs);
+ return r;
+}
+
+/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */
+static int kvm_mce_in_progress(CPUState *env)
+{
+ struct kvm_msr_entry msr_mcg_status = {
+ .index = MSR_MCG_STATUS,
+ };
+ int r;
+
+ r = kvm_get_msr(env, &msr_mcg_status, 1);
+ if (r == -1 || r == 0) {
+ fprintf(stderr, "Failed to get MCE status\n");
+ return 0;
+ }
+ return !!(msr_mcg_status.data & MCG_STATUS_MCIP);
+}
+
+struct kvm_x86_mce_data
+{
+ CPUState *env;
+ struct kvm_x86_mce *mce;
+ int abort_on_error;
+};
+
+static void kvm_do_inject_x86_mce(void *_data)
+{
+ struct kvm_x86_mce_data *data = _data;
+ int r;
+
+ /* If there is an MCE exception being processed, ignore this SRAO MCE */
+ if ((data->env->mcg_cap & MCG_SER_P) &&
+ !(data->mce->status & MCI_STATUS_AR)) {
+ if (kvm_mce_in_progress(data->env)) {
+ return;
+ }
+ }
+
+ r = kvm_set_mce(data->env, data->mce);
+ if (r < 0) {
+ perror("kvm_set_mce FAILED");
+ if (data->abort_on_error) {
+ abort();
+ }
+ }
+}
+
+static void kvm_inject_x86_mce_on(CPUState *env, struct kvm_x86_mce *mce,
+ int flag)
+{
+ struct kvm_x86_mce_data data = {
+ .env = env,
+ .mce = mce,
+ .abort_on_error = (flag & ABORT_ON_ERROR),
+ };
+
+ if (!env->mcg_cap) {
+ fprintf(stderr, "MCE support is not enabled!\n");
+ return;
+ }
+
+ on_vcpu(env, kvm_do_inject_x86_mce, &data);
+}
+
+static void kvm_mce_broadcast_rest(CPUState *env);
+#endif
+
+void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc,
+ int flag)
+{
+#ifdef KVM_CAP_MCE
+ struct kvm_x86_mce mce = {
+ .bank = bank,
+ .status = status,
+ .mcg_status = mcg_status,
+ .addr = addr,
+ .misc = misc,
+ };
+
+ if (flag & MCE_BROADCAST) {
+ kvm_mce_broadcast_rest(cenv);
+ }
+
+ kvm_inject_x86_mce_on(cenv, &mce, flag);
+#else
+ if (flag & ABORT_ON_ERROR) {
+ abort();
+ }
+#endif
+}
+
+static int _kvm_arch_init_vcpu(CPUState *env);
+
+int kvm_arch_init_vcpu(CPUState *env)
+{
+ int r;
+ struct {
+ struct kvm_cpuid2 cpuid;
+ struct kvm_cpuid_entry2 entries[100];
+ } __attribute__((packed)) cpuid_data;
+ uint32_t limit, i, j, cpuid_i;
+ uint32_t unused;
+ struct kvm_cpuid_entry2 *c;
+#ifdef CONFIG_KVM_PARA
+ uint32_t signature[3];
+#endif
+
+ r = _kvm_arch_init_vcpu(env);
+ if (r < 0) {
+ return r;
+ }
+
+ env->cpuid_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_EDX);
+
+ i = env->cpuid_ext_features & CPUID_EXT_HYPERVISOR;
+ env->cpuid_ext_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_ECX);
+ env->cpuid_ext_features |= i;
+
+ env->cpuid_ext2_features &= kvm_arch_get_supported_cpuid(env, 0x80000001,
+ 0, R_EDX);
+ env->cpuid_ext3_features &= kvm_arch_get_supported_cpuid(env, 0x80000001,
+ 0, R_ECX);
+ env->cpuid_svm_features &= kvm_arch_get_supported_cpuid(env, 0x8000000A,
+ 0, R_EDX);
+
+
+ cpuid_i = 0;
+
+#ifdef CONFIG_KVM_PARA
+ /* Paravirtualization CPUIDs */
+ memcpy(signature, "KVMKVMKVM\0\0\0", 12);
+ c = &cpuid_data.entries[cpuid_i++];
+ memset(c, 0, sizeof(*c));
+ c->function = KVM_CPUID_SIGNATURE;
+ c->eax = 0;
+ c->ebx = signature[0];
+ c->ecx = signature[1];
+ c->edx = signature[2];
+
+ c = &cpuid_data.entries[cpuid_i++];
+ memset(c, 0, sizeof(*c));
+ c->function = KVM_CPUID_FEATURES;
+ c->eax = env->cpuid_kvm_features & get_para_features(env);
+#endif
+
+ cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused);
+
+ for (i = 0; i <= limit; i++) {
+ c = &cpuid_data.entries[cpuid_i++];
+
+ switch (i) {
+ case 2: {
+ /* Keep reading function 2 till all the input is received */
+ int times;
+
+ c->function = i;
+ c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC |
+ KVM_CPUID_FLAG_STATE_READ_NEXT;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ times = c->eax & 0xff;
+
+ for (j = 1; j < times; ++j) {
+ c = &cpuid_data.entries[cpuid_i++];
+ c->function = i;
+ c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ }
+ break;
+ }
+ case 4:
+ case 0xb:
+ case 0xd:
+ for (j = 0; ; j++) {
+ c->function = i;
+ c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ c->index = j;
+ cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx);
+
+ if (i == 4 && c->eax == 0) {
+ break;
+ }
+ if (i == 0xb && !(c->ecx & 0xff00)) {
+ break;
+ }
+ if (i == 0xd && c->eax == 0) {
+ break;
+ }
+ c = &cpuid_data.entries[cpuid_i++];
+ }
+ break;
+ default:
+ c->function = i;
+ c->flags = 0;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ break;
+ }
+ }
+ cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused);
+
+ for (i = 0x80000000; i <= limit; i++) {
+ c = &cpuid_data.entries[cpuid_i++];
+
+ c->function = i;
+ c->flags = 0;
+ cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx);
+ }
+
+ cpuid_data.cpuid.nent = cpuid_i;
+
+#ifdef KVM_CAP_MCE
+ if (((env->cpuid_version >> 8)&0xF) >= 6
+ && (env->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA)
+ && kvm_check_extension(env->kvm_state, KVM_CAP_MCE) > 0) {
+ uint64_t mcg_cap;
+ int banks;
+
+ if (kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks)) {
+ perror("kvm_get_mce_cap_supported FAILED");
+ } else {
+ if (banks > MCE_BANKS_DEF)
+ banks = MCE_BANKS_DEF;
+ mcg_cap &= MCE_CAP_DEF;
+ mcg_cap |= banks;
+ if (kvm_setup_mce(env, &mcg_cap)) {
+ perror("kvm_setup_mce FAILED");
+ } else {
+ env->mcg_cap = mcg_cap;
+ }
+ }
+ }
+#endif
+
+ return kvm_vcpu_ioctl(env, KVM_SET_CPUID2, &cpuid_data);
+}
+
+static void kvm_clear_vapic(CPUState *env)
+{
+#ifdef KVM_SET_VAPIC_ADDR
+ struct kvm_vapic_addr va = {
+ .vapic_addr = 0,
+ };
+
+ kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va);
+#endif
+}
+
+void kvm_arch_reset_vcpu(CPUState *env)
+{
+ kvm_clear_vapic(env);
+ env->exception_injected = -1;
+ env->interrupt_injected = -1;
+ env->xcr0 = 1;
+ if (kvm_irqchip_in_kernel()) {
+ env->mp_state = cpu_is_bsp(env) ? KVM_MP_STATE_RUNNABLE :
+ KVM_MP_STATE_UNINITIALIZED;
+ } else {
+ env->mp_state = KVM_MP_STATE_RUNNABLE;
+ }
+}
+
+
+static int kvm_get_supported_msrs(KVMState *s)
+{
+ static int kvm_supported_msrs;
+ int ret = 0;
+
+ /* first time */
+ if (kvm_supported_msrs == 0) {
+ struct kvm_msr_list msr_list, *kvm_msr_list;
+
+ kvm_supported_msrs = -1;
+
+ /* Obtain MSR list from KVM. These are the MSRs that we must
+ * save/restore */
+ msr_list.nmsrs = 0;
+ ret = kvm_ioctl(s, KVM_GET_MSR_INDEX_LIST, &msr_list);
+ if (ret < 0 && ret != -E2BIG) {
+ return ret;
+ }
+ /* Old kernel modules had a bug and could write beyond the provided
+ memory. Allocate at least a safe amount of 1K. */
+ kvm_msr_list = qemu_mallocz(MAX(1024, sizeof(msr_list) +
+ msr_list.nmsrs *
+ sizeof(msr_list.indices[0])));
+
+ kvm_msr_list->nmsrs = msr_list.nmsrs;
+ ret = kvm_ioctl(s, KVM_GET_MSR_INDEX_LIST, kvm_msr_list);
+ if (ret >= 0) {
+ int i;
+
+ for (i = 0; i < kvm_msr_list->nmsrs; i++) {
+ if (kvm_msr_list->indices[i] == MSR_STAR) {
+ has_msr_star = true;
+ continue;
+ }
+ if (kvm_msr_list->indices[i] == MSR_VM_HSAVE_PA) {
+ has_msr_hsave_pa = true;
+ continue;
+ }
+ }
+ }
+
+ free(kvm_msr_list);
+ }
+
+ return ret;
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+
+int kvm_arch_init(KVMState *s)
+{
+ uint64_t identity_base = 0xfffbc000;
+ int ret;
+ struct utsname utsname;
+
+ ret = kvm_get_supported_msrs(s);
+ if (ret < 0) {
+ return ret;
+ }
+
+ uname(&utsname);
+ lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0;
+
+ /*
+ * On older Intel CPUs, KVM uses vm86 mode to emulate 16-bit code directly.
+ * In order to use vm86 mode, an EPT identity map and a TSS are needed.
+ * Since these must be part of guest physical memory, we need to allocate
+ * them, both by setting their start addresses in the kernel and by
+ * creating a corresponding e820 entry. We need 4 pages before the BIOS.
+ *
+ * Older KVM versions may not support setting the identity map base. In
+ * that case we need to stick with the default, i.e. a 256K maximum BIOS
+ * size.
+ */
+#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR
+ if (kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) {
+ /* Allows up to 16M BIOSes. */
+ identity_base = 0xfeffc000;
+
+ ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+#endif
+ /* Set TSS base one page after EPT identity map. */
+ ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, identity_base + 0x1000);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Tell fw_cfg to notify the BIOS to reserve the range. */
+ ret = e820_add_entry(identity_base, 0x4000, E820_RESERVED);
+ if (ret < 0) {
+ fprintf(stderr, "e820_add_entry() table is full\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+#endif
+
+static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs)
+{
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->type = 3;
+ lhs->present = 1;
+ lhs->dpl = 3;
+ lhs->db = 0;
+ lhs->s = 1;
+ lhs->l = 0;
+ lhs->g = 0;
+ lhs->avl = 0;
+ lhs->unusable = 0;
+}
+
+static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs)
+{
+ unsigned flags = rhs->flags;
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->type = (flags >> DESC_TYPE_SHIFT) & 15;
+ lhs->present = (flags & DESC_P_MASK) != 0;
+ lhs->dpl = (flags >> DESC_DPL_SHIFT) & 3;
+ lhs->db = (flags >> DESC_B_SHIFT) & 1;
+ lhs->s = (flags & DESC_S_MASK) != 0;
+ lhs->l = (flags >> DESC_L_SHIFT) & 1;
+ lhs->g = (flags & DESC_G_MASK) != 0;
+ lhs->avl = (flags & DESC_AVL_MASK) != 0;
+ lhs->unusable = 0;
+}
+
+static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs)
+{
+ lhs->selector = rhs->selector;
+ lhs->base = rhs->base;
+ lhs->limit = rhs->limit;
+ lhs->flags = (rhs->type << DESC_TYPE_SHIFT) |
+ (rhs->present * DESC_P_MASK) |
+ (rhs->dpl << DESC_DPL_SHIFT) |
+ (rhs->db << DESC_B_SHIFT) |
+ (rhs->s * DESC_S_MASK) |
+ (rhs->l << DESC_L_SHIFT) |
+ (rhs->g * DESC_G_MASK) |
+ (rhs->avl * DESC_AVL_MASK);
+}
+
+
+static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set)
+{
+ if (set) {
+ *kvm_reg = *qemu_reg;
+ } else {
+ *qemu_reg = *kvm_reg;
+ }
+}
+
+static int kvm_getput_regs(CPUState *env, int set)
+{
+ struct kvm_regs regs;
+ int ret = 0;
+
+ if (!set) {
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ kvm_getput_reg(&regs.rax, &env->regs[R_EAX], set);
+ kvm_getput_reg(&regs.rbx, &env->regs[R_EBX], set);
+ kvm_getput_reg(&regs.rcx, &env->regs[R_ECX], set);
+ kvm_getput_reg(&regs.rdx, &env->regs[R_EDX], set);
+ kvm_getput_reg(&regs.rsi, &env->regs[R_ESI], set);
+ kvm_getput_reg(&regs.rdi, &env->regs[R_EDI], set);
+ kvm_getput_reg(&regs.rsp, &env->regs[R_ESP], set);
+ kvm_getput_reg(&regs.rbp, &env->regs[R_EBP], set);
+#ifdef TARGET_X86_64
+ kvm_getput_reg(&regs.r8, &env->regs[8], set);
+ kvm_getput_reg(&regs.r9, &env->regs[9], set);
+ kvm_getput_reg(&regs.r10, &env->regs[10], set);
+ kvm_getput_reg(&regs.r11, &env->regs[11], set);
+ kvm_getput_reg(&regs.r12, &env->regs[12], set);
+ kvm_getput_reg(&regs.r13, &env->regs[13], set);
+ kvm_getput_reg(&regs.r14, &env->regs[14], set);
+ kvm_getput_reg(&regs.r15, &env->regs[15], set);
+#endif
+
+ kvm_getput_reg(&regs.rflags, &env->eflags, set);
+ kvm_getput_reg(&regs.rip, &env->eip, set);
+
+ if (set) {
+ ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
+ }
+
+ return ret;
+}
+
+static int kvm_put_fpu(CPUState *env)
+{
+ struct kvm_fpu fpu;
+ int i;
+
+ memset(&fpu, 0, sizeof fpu);
+ fpu.fsw = env->fpus & ~(7 << 11);
+ fpu.fsw |= (env->fpstt & 7) << 11;
+ fpu.fcw = env->fpuc;
+ for (i = 0; i < 8; ++i) {
+ fpu.ftwx |= (!env->fptags[i]) << i;
+ }
+ memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs);
+ memcpy(fpu.xmm, env->xmm_regs, sizeof env->xmm_regs);
+ fpu.mxcsr = env->mxcsr;
+
+ return kvm_vcpu_ioctl(env, KVM_SET_FPU, &fpu);
+}
+
+#ifdef KVM_CAP_XSAVE
+#define XSAVE_CWD_RIP 2
+#define XSAVE_CWD_RDP 4
+#define XSAVE_MXCSR 6
+#define XSAVE_ST_SPACE 8
+#define XSAVE_XMM_SPACE 40
+#define XSAVE_XSTATE_BV 128
+#define XSAVE_YMMH_SPACE 144
+#endif
+
+static int kvm_put_xsave(CPUState *env)
+{
+#ifdef KVM_CAP_XSAVE
+ int i, r;
+ struct kvm_xsave* xsave;
+ uint16_t cwd, swd, twd, fop;
+
+ if (!kvm_has_xsave()) {
+ return kvm_put_fpu(env);
+ }
+
+ xsave = qemu_memalign(4096, sizeof(struct kvm_xsave));
+ memset(xsave, 0, sizeof(struct kvm_xsave));
+ cwd = swd = twd = fop = 0;
+ swd = env->fpus & ~(7 << 11);
+ swd |= (env->fpstt & 7) << 11;
+ cwd = env->fpuc;
+ for (i = 0; i < 8; ++i) {
+ twd |= (!env->fptags[i]) << i;
+ }
+ xsave->region[0] = (uint32_t)(swd << 16) + cwd;
+ xsave->region[1] = (uint32_t)(fop << 16) + twd;
+ memcpy(&xsave->region[XSAVE_ST_SPACE], env->fpregs,
+ sizeof env->fpregs);
+ memcpy(&xsave->region[XSAVE_XMM_SPACE], env->xmm_regs,
+ sizeof env->xmm_regs);
+ xsave->region[XSAVE_MXCSR] = env->mxcsr;
+ *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV] = env->xstate_bv;
+ memcpy(&xsave->region[XSAVE_YMMH_SPACE], env->ymmh_regs,
+ sizeof env->ymmh_regs);
+ r = kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave);
+ qemu_free(xsave);
+ return r;
+#else
+ return kvm_put_fpu(env);
+#endif
+}
+
+static int kvm_put_xcrs(CPUState *env)
+{
+#ifdef KVM_CAP_XCRS
+ struct kvm_xcrs xcrs;
+
+ if (!kvm_has_xcrs()) {
+ return 0;
+ }
+
+ xcrs.nr_xcrs = 1;
+ xcrs.flags = 0;
+ xcrs.xcrs[0].xcr = 0;
+ xcrs.xcrs[0].value = env->xcr0;
+ return kvm_vcpu_ioctl(env, KVM_SET_XCRS, &xcrs);
+#else
+ return 0;
+#endif
+}
+
+static int kvm_put_sregs(CPUState *env)
+{
+ struct kvm_sregs sregs;
+
+ memset(sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap));
+ if (env->interrupt_injected >= 0) {
+ sregs.interrupt_bitmap[env->interrupt_injected / 64] |=
+ (uint64_t)1 << (env->interrupt_injected % 64);
+ }
+
+ if ((env->eflags & VM_MASK)) {
+ set_v8086_seg(&sregs.cs, &env->segs[R_CS]);
+ set_v8086_seg(&sregs.ds, &env->segs[R_DS]);
+ set_v8086_seg(&sregs.es, &env->segs[R_ES]);
+ set_v8086_seg(&sregs.fs, &env->segs[R_FS]);
+ set_v8086_seg(&sregs.gs, &env->segs[R_GS]);
+ set_v8086_seg(&sregs.ss, &env->segs[R_SS]);
+ } else {
+ set_seg(&sregs.cs, &env->segs[R_CS]);
+ set_seg(&sregs.ds, &env->segs[R_DS]);
+ set_seg(&sregs.es, &env->segs[R_ES]);
+ set_seg(&sregs.fs, &env->segs[R_FS]);
+ set_seg(&sregs.gs, &env->segs[R_GS]);
+ set_seg(&sregs.ss, &env->segs[R_SS]);
+ }
+
+ set_seg(&sregs.tr, &env->tr);
+ set_seg(&sregs.ldt, &env->ldt);
+
+ sregs.idt.limit = env->idt.limit;
+ sregs.idt.base = env->idt.base;
+ sregs.gdt.limit = env->gdt.limit;
+ sregs.gdt.base = env->gdt.base;
+
+ sregs.cr0 = env->cr[0];
+ sregs.cr2 = env->cr[2];
+ sregs.cr3 = env->cr[3];
+ sregs.cr4 = env->cr[4];
+
+ sregs.cr8 = cpu_get_apic_tpr(env->apic_state);
+ sregs.apic_base = cpu_get_apic_base(env->apic_state);
+
+ sregs.efer = env->efer;
+
+ return kvm_vcpu_ioctl(env, KVM_SET_SREGS, &sregs);
+}
+
+static void kvm_msr_entry_set(struct kvm_msr_entry *entry,
+ uint32_t index, uint64_t value)
+{
+ entry->index = index;
+ entry->data = value;
+}
+
+static int kvm_put_msrs(CPUState *env, int level)
+{
+ struct {
+ struct kvm_msrs info;
+ struct kvm_msr_entry entries[100];
+ } msr_data;
+ struct kvm_msr_entry *msrs = msr_data.entries;
+ int n = 0;
+
+ kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs);
+ kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp);
+ kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip);
+ if (has_msr_star) {
+ kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star);
+ }
+ if (has_msr_hsave_pa) {
+ kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave);
+ }
+#ifdef TARGET_X86_64
+ if (lm_capable_kernel) {
+ kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
+ kvm_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase);
+ kvm_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask);
+ kvm_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar);
+ }
+#endif
+ if (level == KVM_PUT_FULL_STATE) {
+ /*
+ * KVM is yet unable to synchronize TSC values of multiple VCPUs on
+ * writeback. Until this is fixed, we only write the offset to SMP
+ * guests after migration, desynchronizing the VCPUs, but avoiding
+ * huge jump-backs that would occur without any writeback at all.
+ */
+ if (smp_cpus == 1 || env->tsc != 0) {
+ kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc);
+ }
+ }
+ /*
+ * The following paravirtual MSRs have side effects on the guest or are
+ * too heavy for normal writeback. Limit them to reset or full state
+ * updates.
+ */
+ if (level >= KVM_PUT_RESET_STATE) {
+ kvm_msr_entry_set(&msrs[n++], MSR_KVM_SYSTEM_TIME,
+ env->system_time_msr);
+ kvm_msr_entry_set(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr);
+#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF)
+ if (has_msr_async_pf_en) {
+ kvm_msr_entry_set(&msrs[n++], MSR_KVM_ASYNC_PF_EN,
+ env->async_pf_en_msr);
+ }
+#endif
+ }
+#ifdef KVM_CAP_MCE
+ if (env->mcg_cap) {
+ int i;
+
+ if (level == KVM_PUT_RESET_STATE) {
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
+ } else if (level == KVM_PUT_FULL_STATE) {
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status);
+ kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl);
+ for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
+ kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]);
+ }
+ }
+ }
+#endif
+
+ msr_data.info.nmsrs = n;
+
+ return kvm_vcpu_ioctl(env, KVM_SET_MSRS, &msr_data);
+
+}
+
+static int kvm_get_fpu(CPUState *env)
+{
+ struct kvm_fpu fpu;
+ int i, ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_FPU, &fpu);
+ if (ret < 0) {
+ return ret;
+ }
+
+ env->fpstt = (fpu.fsw >> 11) & 7;
+ env->fpus = fpu.fsw;
+ env->fpuc = fpu.fcw;
+ for (i = 0; i < 8; ++i) {
+ env->fptags[i] = !((fpu.ftwx >> i) & 1);
+ }
+ memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs);
+ memcpy(env->xmm_regs, fpu.xmm, sizeof env->xmm_regs);
+ env->mxcsr = fpu.mxcsr;
+
+ return 0;
+}
+
+static int kvm_get_xsave(CPUState *env)
+{
+#ifdef KVM_CAP_XSAVE
+ struct kvm_xsave* xsave;
+ int ret, i;
+ uint16_t cwd, swd, twd, fop;
+
+ if (!kvm_has_xsave()) {
+ return kvm_get_fpu(env);
+ }
+
+ xsave = qemu_memalign(4096, sizeof(struct kvm_xsave));
+ ret = kvm_vcpu_ioctl(env, KVM_GET_XSAVE, xsave);
+ if (ret < 0) {
+ qemu_free(xsave);
+ return ret;
+ }
+
+ cwd = (uint16_t)xsave->region[0];
+ swd = (uint16_t)(xsave->region[0] >> 16);
+ twd = (uint16_t)xsave->region[1];
+ fop = (uint16_t)(xsave->region[1] >> 16);
+ env->fpstt = (swd >> 11) & 7;
+ env->fpus = swd;
+ env->fpuc = cwd;
+ for (i = 0; i < 8; ++i) {
+ env->fptags[i] = !((twd >> i) & 1);
+ }
+ env->mxcsr = xsave->region[XSAVE_MXCSR];
+ memcpy(env->fpregs, &xsave->region[XSAVE_ST_SPACE],
+ sizeof env->fpregs);
+ memcpy(env->xmm_regs, &xsave->region[XSAVE_XMM_SPACE],
+ sizeof env->xmm_regs);
+ env->xstate_bv = *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV];
+ memcpy(env->ymmh_regs, &xsave->region[XSAVE_YMMH_SPACE],
+ sizeof env->ymmh_regs);
+ qemu_free(xsave);
+ return 0;
+#else
+ return kvm_get_fpu(env);
+#endif
+}
+
+static int kvm_get_xcrs(CPUState *env)
+{
+#ifdef KVM_CAP_XCRS
+ int i, ret;
+ struct kvm_xcrs xcrs;
+
+ if (!kvm_has_xcrs()) {
+ return 0;
+ }
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_XCRS, &xcrs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < xcrs.nr_xcrs; i++) {
+ /* Only support xcr0 now */
+ if (xcrs.xcrs[0].xcr == 0) {
+ env->xcr0 = xcrs.xcrs[0].value;
+ break;
+ }
+ }
+ return 0;
+#else
+ return 0;
+#endif
+}
+
+static int kvm_get_sregs(CPUState *env)
+{
+ struct kvm_sregs sregs;
+ uint32_t hflags;
+ int bit, i, ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* There can only be one pending IRQ set in the bitmap at a time, so try
+ to find it and save its number instead (-1 for none). */
+ env->interrupt_injected = -1;
+ for (i = 0; i < ARRAY_SIZE(sregs.interrupt_bitmap); i++) {
+ if (sregs.interrupt_bitmap[i]) {
+ bit = ctz64(sregs.interrupt_bitmap[i]);
+ env->interrupt_injected = i * 64 + bit;
+ break;
+ }
+ }
+
+ get_seg(&env->segs[R_CS], &sregs.cs);
+ get_seg(&env->segs[R_DS], &sregs.ds);
+ get_seg(&env->segs[R_ES], &sregs.es);
+ get_seg(&env->segs[R_FS], &sregs.fs);
+ get_seg(&env->segs[R_GS], &sregs.gs);
+ get_seg(&env->segs[R_SS], &sregs.ss);
+
+ get_seg(&env->tr, &sregs.tr);
+ get_seg(&env->ldt, &sregs.ldt);
+
+ env->idt.limit = sregs.idt.limit;
+ env->idt.base = sregs.idt.base;
+ env->gdt.limit = sregs.gdt.limit;
+ env->gdt.base = sregs.gdt.base;
+
+ env->cr[0] = sregs.cr0;
+ env->cr[2] = sregs.cr2;
+ env->cr[3] = sregs.cr3;
+ env->cr[4] = sregs.cr4;
+
+ cpu_set_apic_base(env->apic_state, sregs.apic_base);
+
+ env->efer = sregs.efer;
+ //cpu_set_apic_tpr(env->apic_state, sregs.cr8);
+
+#define HFLAG_COPY_MASK \
+ ~( HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \
+ HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \
+ HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \
+ HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)
+
+ hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK;
+ hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT);
+ hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) &
+ (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK);
+ hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK));
+ hflags |= (env->cr[4] & CR4_OSFXSR_MASK) <<
+ (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT);
+
+ if (env->efer & MSR_EFER_LMA) {
+ hflags |= HF_LMA_MASK;
+ }
+
+ if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) {
+ hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+ } else {
+ hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >>
+ (DESC_B_SHIFT - HF_CS32_SHIFT);
+ hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >>
+ (DESC_B_SHIFT - HF_SS32_SHIFT);
+ if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK) ||
+ !(hflags & HF_CS32_MASK)) {
+ hflags |= HF_ADDSEG_MASK;
+ } else {
+ hflags |= ((env->segs[R_DS].base | env->segs[R_ES].base |
+ env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT;
+ }
+ }
+ env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags;
+
+ return 0;
+}
+
+static int kvm_get_msrs(CPUState *env)
+{
+ struct {
+ struct kvm_msrs info;
+ struct kvm_msr_entry entries[100];
+ } msr_data;
+ struct kvm_msr_entry *msrs = msr_data.entries;
+ int ret, i, n;
+
+ n = 0;
+ msrs[n++].index = MSR_IA32_SYSENTER_CS;
+ msrs[n++].index = MSR_IA32_SYSENTER_ESP;
+ msrs[n++].index = MSR_IA32_SYSENTER_EIP;
+ if (has_msr_star) {
+ msrs[n++].index = MSR_STAR;
+ }
+ if (has_msr_hsave_pa) {
+ msrs[n++].index = MSR_VM_HSAVE_PA;
+ }
+ msrs[n++].index = MSR_IA32_TSC;
+#ifdef TARGET_X86_64
+ if (lm_capable_kernel) {
+ msrs[n++].index = MSR_CSTAR;
+ msrs[n++].index = MSR_KERNELGSBASE;
+ msrs[n++].index = MSR_FMASK;
+ msrs[n++].index = MSR_LSTAR;
+ }
+#endif
+ msrs[n++].index = MSR_KVM_SYSTEM_TIME;
+ msrs[n++].index = MSR_KVM_WALL_CLOCK;
+#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF)
+ if (has_msr_async_pf_en) {
+ msrs[n++].index = MSR_KVM_ASYNC_PF_EN;
+ }
+#endif
+
+#ifdef KVM_CAP_MCE
+ if (env->mcg_cap) {
+ msrs[n++].index = MSR_MCG_STATUS;
+ msrs[n++].index = MSR_MCG_CTL;
+ for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
+ msrs[n++].index = MSR_MC0_CTL + i;
+ }
+ }
+#endif
+
+ msr_data.info.nmsrs = n;
+ ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, &msr_data);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < ret; i++) {
+ switch (msrs[i].index) {
+ case MSR_IA32_SYSENTER_CS:
+ env->sysenter_cs = msrs[i].data;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ env->sysenter_esp = msrs[i].data;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ env->sysenter_eip = msrs[i].data;
+ break;
+ case MSR_STAR:
+ env->star = msrs[i].data;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_CSTAR:
+ env->cstar = msrs[i].data;
+ break;
+ case MSR_KERNELGSBASE:
+ env->kernelgsbase = msrs[i].data;
+ break;
+ case MSR_FMASK:
+ env->fmask = msrs[i].data;
+ break;
+ case MSR_LSTAR:
+ env->lstar = msrs[i].data;
+ break;
+#endif
+ case MSR_IA32_TSC:
+ env->tsc = msrs[i].data;
+ break;
+ case MSR_VM_HSAVE_PA:
+ env->vm_hsave = msrs[i].data;
+ break;
+ case MSR_KVM_SYSTEM_TIME:
+ env->system_time_msr = msrs[i].data;
+ break;
+ case MSR_KVM_WALL_CLOCK:
+ env->wall_clock_msr = msrs[i].data;
+ break;
+#ifdef KVM_CAP_MCE
+ case MSR_MCG_STATUS:
+ env->mcg_status = msrs[i].data;
+ break;
+ case MSR_MCG_CTL:
+ env->mcg_ctl = msrs[i].data;
+ break;
+#endif
+ default:
+#ifdef KVM_CAP_MCE
+ if (msrs[i].index >= MSR_MC0_CTL &&
+ msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) {
+ env->mce_banks[msrs[i].index - MSR_MC0_CTL] = msrs[i].data;
+ }
+#endif
+ break;
+#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF)
+ case MSR_KVM_ASYNC_PF_EN:
+ env->async_pf_en_msr = msrs[i].data;
+ break;
+#endif
+ }
+ }
+
+ return 0;
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+static int kvm_put_mp_state(CPUState *env)
+{
+ struct kvm_mp_state mp_state = { .mp_state = env->mp_state };
+
+ return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, &mp_state);
+}
+
+static int kvm_get_mp_state(CPUState *env)
+{
+ struct kvm_mp_state mp_state;
+ int ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, &mp_state);
+ if (ret < 0) {
+ return ret;
+ }
+ env->mp_state = mp_state.mp_state;
+ if (kvm_irqchip_in_kernel()) {
+ env->halted = (mp_state.mp_state == KVM_MP_STATE_HALTED);
+ }
+ return 0;
+}
+#endif
+
+static int kvm_put_vcpu_events(CPUState *env, int level)
+{
+#ifdef KVM_CAP_VCPU_EVENTS
+ struct kvm_vcpu_events events;
+
+ if (!kvm_has_vcpu_events()) {
+ return 0;
+ }
+
+ events.exception.injected = (env->exception_injected >= 0);
+ events.exception.nr = env->exception_injected;
+ events.exception.has_error_code = env->has_error_code;
+ events.exception.error_code = env->error_code;
+
+ events.interrupt.injected = (env->interrupt_injected >= 0);
+ events.interrupt.nr = env->interrupt_injected;
+ events.interrupt.soft = env->soft_interrupt;
+
+ events.nmi.injected = env->nmi_injected;
+ events.nmi.pending = env->nmi_pending;
+ events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK);
+
+ events.sipi_vector = env->sipi_vector;
+
+ events.flags = 0;
+ if (level >= KVM_PUT_RESET_STATE) {
+ events.flags |=
+ KVM_VCPUEVENT_VALID_NMI_PENDING | KVM_VCPUEVENT_VALID_SIPI_VECTOR;
+ }
+
+ return kvm_vcpu_ioctl(env, KVM_SET_VCPU_EVENTS, &events);
+#else
+ return 0;
+#endif
+}
+
+static int kvm_get_vcpu_events(CPUState *env)
+{
+#ifdef KVM_CAP_VCPU_EVENTS
+ struct kvm_vcpu_events events;
+ int ret;
+
+ if (!kvm_has_vcpu_events()) {
+ return 0;
+ }
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_VCPU_EVENTS, &events);
+ if (ret < 0) {
+ return ret;
+ }
+ env->exception_injected =
+ events.exception.injected ? events.exception.nr : -1;
+ env->has_error_code = events.exception.has_error_code;
+ env->error_code = events.exception.error_code;
+
+ env->interrupt_injected =
+ events.interrupt.injected ? events.interrupt.nr : -1;
+ env->soft_interrupt = events.interrupt.soft;
+
+ env->nmi_injected = events.nmi.injected;
+ env->nmi_pending = events.nmi.pending;
+ if (events.nmi.masked) {
+ env->hflags2 |= HF2_NMI_MASK;
+ } else {
+ env->hflags2 &= ~HF2_NMI_MASK;
+ }
+
+ env->sipi_vector = events.sipi_vector;
+#endif
+
+ return 0;
+}
+
+static int kvm_guest_debug_workarounds(CPUState *env)
+{
+ int ret = 0;
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+ unsigned long reinject_trap = 0;
+
+ if (!kvm_has_vcpu_events()) {
+ if (env->exception_injected == 1) {
+ reinject_trap = KVM_GUESTDBG_INJECT_DB;
+ } else if (env->exception_injected == 3) {
+ reinject_trap = KVM_GUESTDBG_INJECT_BP;
+ }
+ env->exception_injected = -1;
+ }
+
+ /*
+ * Kernels before KVM_CAP_X86_ROBUST_SINGLESTEP overwrote flags.TF
+ * injected via SET_GUEST_DEBUG while updating GP regs. Work around this
+ * by updating the debug state once again if single-stepping is on.
+ * Another reason to call kvm_update_guest_debug here is a pending debug
+ * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to
+ * reinject them via SET_GUEST_DEBUG.
+ */
+ if (reinject_trap ||
+ (!kvm_has_robust_singlestep() && env->singlestep_enabled)) {
+ ret = kvm_update_guest_debug(env, reinject_trap);
+ }
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
+ return ret;
+}
+
+static int kvm_put_debugregs(CPUState *env)
+{
+#ifdef KVM_CAP_DEBUGREGS
+ struct kvm_debugregs dbgregs;
+ int i;
+
+ if (!kvm_has_debugregs()) {
+ return 0;
+ }
+
+ for (i = 0; i < 4; i++) {
+ dbgregs.db[i] = env->dr[i];
+ }
+ dbgregs.dr6 = env->dr[6];
+ dbgregs.dr7 = env->dr[7];
+ dbgregs.flags = 0;
+
+ return kvm_vcpu_ioctl(env, KVM_SET_DEBUGREGS, &dbgregs);
+#else
+ return 0;
+#endif
+}
+
+static int kvm_get_debugregs(CPUState *env)
+{
+#ifdef KVM_CAP_DEBUGREGS
+ struct kvm_debugregs dbgregs;
+ int i, ret;
+
+ if (!kvm_has_debugregs()) {
+ return 0;
+ }
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_DEBUGREGS, &dbgregs);
+ if (ret < 0) {
+ return ret;
+ }
+ for (i = 0; i < 4; i++) {
+ env->dr[i] = dbgregs.db[i];
+ }
+ env->dr[4] = env->dr[6] = dbgregs.dr6;
+ env->dr[5] = env->dr[7] = dbgregs.dr7;
+#endif
+
+ return 0;
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+int kvm_arch_put_registers(CPUState *env, int level)
+{
+ int ret;
+
+ assert(cpu_is_stopped(env) || qemu_cpu_self(env));
+
+ ret = kvm_getput_regs(env, 1);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_put_xsave(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_put_xcrs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_put_sregs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_put_msrs(env, level);
+ if (ret < 0) {
+ return ret;
+ }
+ if (level >= KVM_PUT_RESET_STATE) {
+ ret = kvm_put_mp_state(env);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+ ret = kvm_put_vcpu_events(env, level);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_put_debugregs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ /* must be last */
+ ret = kvm_guest_debug_workarounds(env);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+int kvm_arch_get_registers(CPUState *env)
+{
+ int ret;
+
+ assert(cpu_is_stopped(env) || qemu_cpu_self(env));
+
+ ret = kvm_getput_regs(env, 0);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_xsave(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_xcrs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_sregs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_msrs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_mp_state(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_vcpu_events(env);
+ if (ret < 0) {
+ return ret;
+ }
+ ret = kvm_get_debugregs(env);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0;
+}
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+{
+ /* Inject NMI */
+ if (env->interrupt_request & CPU_INTERRUPT_NMI) {
+ env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+ DPRINTF("injected NMI\n");
+ kvm_vcpu_ioctl(env, KVM_NMI);
+ }
+
+ /* Try to inject an interrupt if the guest can accept it */
+ if (run->ready_for_interrupt_injection &&
+ (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) {
+ int irq;
+
+ env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+ irq = cpu_get_pic_interrupt(env);
+ if (irq >= 0) {
+ struct kvm_interrupt intr;
+ intr.irq = irq;
+ /* FIXME: errors */
+ DPRINTF("injected interrupt %d\n", irq);
+ kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr);
+ }
+ }
+
+ /* If we have an interrupt but the guest is not ready to receive an
+ * interrupt, request an interrupt window exit. This will
+ * cause a return to userspace as soon as the guest is ready to
+ * receive interrupts. */
+ if ((env->interrupt_request & CPU_INTERRUPT_HARD)) {
+ run->request_interrupt_window = 1;
+ } else {
+ run->request_interrupt_window = 0;
+ }
+
+ DPRINTF("setting tpr\n");
+ run->cr8 = cpu_get_apic_tpr(env->apic_state);
+
+ return 0;
+}
+#endif
+
+int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+{
+ if (run->if_flag) {
+ env->eflags |= IF_MASK;
+ } else {
+ env->eflags &= ~IF_MASK;
+ }
+ cpu_set_apic_tpr(env->apic_state, run->cr8);
+ cpu_set_apic_base(env->apic_state, run->apic_base);
+
+ return 0;
+}
+
+#ifdef OBSOLETE_KVM_IMPL
+
+int kvm_arch_process_irqchip_events(CPUState *env)
+{
+ if (env->interrupt_request & CPU_INTERRUPT_INIT) {
+ kvm_cpu_synchronize_state(env);
+ do_cpu_init(env);
+ env->exception_index = EXCP_HALTED;
+ }
+
+ if (env->interrupt_request & CPU_INTERRUPT_SIPI) {
+ kvm_cpu_synchronize_state(env);
+ do_cpu_sipi(env);
+ }
+
+ return env->halted;
+}
+
+static int kvm_handle_halt(CPUState *env)
+{
+ if (!((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->eflags & IF_MASK)) &&
+ !(env->interrupt_request & CPU_INTERRUPT_NMI)) {
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ return 0;
+ }
+
+ return 1;
+}
+
+static bool host_supports_vmx(void)
+{
+ uint32_t ecx, unused;
+
+ host_cpuid(1, 0, &unused, &unused, &ecx, &unused);
+ return ecx & CPUID_EXT_VMX;
+}
+
+#define VMX_INVALID_GUEST_STATE 0x80000021
+
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
+{
+ uint64_t code;
+ int ret = 0;
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_HLT:
+ DPRINTF("handle_hlt\n");
+ ret = kvm_handle_halt(env);
+ break;
+ case KVM_EXIT_SET_TPR:
+ ret = 1;
+ break;
+ case KVM_EXIT_FAIL_ENTRY:
+ code = run->fail_entry.hardware_entry_failure_reason;
+ fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n",
+ code);
+ if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) {
+ fprintf(stderr,
+ "\nIf you're runnning a guest on an Intel machine without "
+ "unrestricted mode\n"
+ "support, the failure can be most likely due to the guest "
+ "entering an invalid\n"
+ "state for Intel VT. For example, the guest maybe running "
+ "in big real mode\n"
+ "which is not supported on less recent Intel processors."
+ "\n\n");
+ }
+ ret = -1;
+ break;
+ case KVM_EXIT_EXCEPTION:
+ fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n",
+ run->ex.exception, run->ex.error_code);
+ ret = -1;
+ break;
+ default:
+ fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#ifdef KVM_CAP_SET_GUEST_DEBUG
+int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ static const uint8_t int3 = 0xcc;
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&int3, 1, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ uint8_t int3;
+
+ if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc ||
+ cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct {
+ target_ulong addr;
+ int len;
+ int type;
+} hw_breakpoint[4];
+
+static int nb_hw_breakpoint;
+
+static int find_hw_breakpoint(target_ulong addr, int len, int type)
+{
+ int n;
+
+ for (n = 0; n < nb_hw_breakpoint; n++) {
+ if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type &&
+ (hw_breakpoint[n].len == len || len == -1)) {
+ return n;
+ }
+ }
+ return -1;
+}
+
+int kvm_arch_insert_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ switch (type) {
+ case GDB_BREAKPOINT_HW:
+ len = 1;
+ break;
+ case GDB_WATCHPOINT_WRITE:
+ case GDB_WATCHPOINT_ACCESS:
+ switch (len) {
+ case 1:
+ break;
+ case 2:
+ case 4:
+ case 8:
+ if (addr & (len - 1)) {
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ if (nb_hw_breakpoint == 4) {
+ return -ENOBUFS;
+ }
+ if (find_hw_breakpoint(addr, len, type) >= 0) {
+ return -EEXIST;
+ }
+ hw_breakpoint[nb_hw_breakpoint].addr = addr;
+ hw_breakpoint[nb_hw_breakpoint].len = len;
+ hw_breakpoint[nb_hw_breakpoint].type = type;
+ nb_hw_breakpoint++;
+
+ return 0;
+}
+
+int kvm_arch_remove_hw_breakpoint(target_ulong addr,
+ target_ulong len, int type)
+{
+ int n;
+
+ n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type);
+ if (n < 0) {
+ return -ENOENT;
+ }
+ nb_hw_breakpoint--;
+ hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint];
+
+ return 0;
+}
+
+void kvm_arch_remove_all_hw_breakpoints(void)
+{
+ nb_hw_breakpoint = 0;
+}
+
+static CPUWatchpoint hw_watchpoint;
+
+int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info)
+{
+ int handle = 0;
+ int n;
+
+ if (arch_info->exception == 1) {
+ if (arch_info->dr6 & (1 << 14)) {
+ if (cpu_single_env->singlestep_enabled) {
+ handle = 1;
+ }
+ } else {
+ for (n = 0; n < 4; n++) {
+ if (arch_info->dr6 & (1 << n)) {
+ switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) {
+ case 0x0:
+ handle = 1;
+ break;
+ case 0x1:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = &hw_watchpoint;
+ hw_watchpoint.vaddr = hw_breakpoint[n].addr;
+ hw_watchpoint.flags = BP_MEM_WRITE;
+ break;
+ case 0x3:
+ handle = 1;
+ cpu_single_env->watchpoint_hit = &hw_watchpoint;
+ hw_watchpoint.vaddr = hw_breakpoint[n].addr;
+ hw_watchpoint.flags = BP_MEM_ACCESS;
+ break;
+ }
+ }
+ }
+ }
+ } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) {
+ handle = 1;
+ }
+ if (!handle) {
+ cpu_synchronize_state(cpu_single_env);
+ assert(cpu_single_env->exception_injected == -1);
+
+ cpu_single_env->exception_injected = arch_info->exception;
+ cpu_single_env->has_error_code = 0;
+ }
+
+ return handle;
+}
+
+void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
+{
+ const uint8_t type_code[] = {
+ [GDB_BREAKPOINT_HW] = 0x0,
+ [GDB_WATCHPOINT_WRITE] = 0x1,
+ [GDB_WATCHPOINT_ACCESS] = 0x3
+ };
+ const uint8_t len_code[] = {
+ [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2
+ };
+ int n;
+
+ if (kvm_sw_breakpoints_active(env)) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP;
+ }
+ if (nb_hw_breakpoint > 0) {
+ dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP;
+ dbg->arch.debugreg[7] = 0x0600;
+ for (n = 0; n < nb_hw_breakpoint; n++) {
+ dbg->arch.debugreg[n] = hw_breakpoint[n].addr;
+ dbg->arch.debugreg[7] |= (2 << (n * 2)) |
+ (type_code[hw_breakpoint[n].type] << (16 + n*4)) |
+ ((uint32_t)len_code[hw_breakpoint[n].len] << (18 + n*4));
+ }
+ }
+}
+#endif /* KVM_CAP_SET_GUEST_DEBUG */
+
+bool kvm_arch_stop_on_emulation_error(CPUState *env)
+{
+ return !(env->cr[0] & CR0_PE_MASK) ||
+ ((env->segs[R_CS].selector & 3) != 3);
+}
+
+static void hardware_memory_error(void)
+{
+ fprintf(stderr, "Hardware memory error!\n");
+ exit(1);
+}
+
+#ifdef KVM_CAP_MCE
+static void kvm_mce_broadcast_rest(CPUState *env)
+{
+ struct kvm_x86_mce mce = {
+ .bank = 1,
+ .status = MCI_STATUS_VAL | MCI_STATUS_UC,
+ .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
+ .addr = 0,
+ .misc = 0,
+ };
+ CPUState *cenv;
+
+ /* Broadcast MCA signal for processor version 06H_EH and above */
+ if (cpu_x86_support_mca_broadcast(env)) {
+ for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) {
+ if (cenv == env) {
+ continue;
+ }
+ kvm_inject_x86_mce_on(cenv, &mce, ABORT_ON_ERROR);
+ }
+ }
+}
+
+static void kvm_mce_inj_srar_dataload(CPUState *env, target_phys_addr_t paddr)
+{
+ struct kvm_x86_mce mce = {
+ .bank = 9,
+ .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
+ | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
+ | MCI_STATUS_AR | 0x134,
+ .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV,
+ .addr = paddr,
+ .misc = (MCM_ADDR_PHYS << 6) | 0xc,
+ };
+ int r;
+
+ r = kvm_set_mce(env, &mce);
+ if (r < 0) {
+ fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
+ abort();
+ }
+ kvm_mce_broadcast_rest(env);
+}
+
+static void kvm_mce_inj_srao_memscrub(CPUState *env, target_phys_addr_t paddr)
+{
+ struct kvm_x86_mce mce = {
+ .bank = 9,
+ .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
+ | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
+ | 0xc0,
+ .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
+ .addr = paddr,
+ .misc = (MCM_ADDR_PHYS << 6) | 0xc,
+ };
+ int r;
+
+ r = kvm_set_mce(env, &mce);
+ if (r < 0) {
+ fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno));
+ abort();
+ }
+ kvm_mce_broadcast_rest(env);
+}
+
+static void kvm_mce_inj_srao_memscrub2(CPUState *env, target_phys_addr_t paddr)
+{
+ struct kvm_x86_mce mce = {
+ .bank = 9,
+ .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN
+ | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S
+ | 0xc0,
+ .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV,
+ .addr = paddr,
+ .misc = (MCM_ADDR_PHYS << 6) | 0xc,
+ };
+
+ kvm_inject_x86_mce_on(env, &mce, ABORT_ON_ERROR);
+ kvm_mce_broadcast_rest(env);
+}
+
+#endif
+
+int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr)
+{
+#if defined(KVM_CAP_MCE)
+ void *vaddr;
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
+
+ if ((env->mcg_cap & MCG_SER_P) && addr
+ && (code == BUS_MCEERR_AR
+ || code == BUS_MCEERR_AO)) {
+ vaddr = (void *)addr;
+ if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr, &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!\n");
+ /* Hope we are lucky for AO MCE */
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else {
+ hardware_memory_error();
+ }
+ }
+
+ if (code == BUS_MCEERR_AR) {
+ /* Fake an Intel architectural Data Load SRAR UCR */
+ kvm_mce_inj_srar_dataload(env, paddr);
+ } else {
+ /*
+ * If there is an MCE excpetion being processed, ignore
+ * this SRAO MCE
+ */
+ if (!kvm_mce_in_progress(env)) {
+ /* Fake an Intel architectural Memory scrubbing UCR */
+ kvm_mce_inj_srao_memscrub(env, paddr);
+ }
+ }
+ } else
+#endif
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int kvm_on_sigbus(int code, void *addr)
+{
+#if defined(KVM_CAP_MCE)
+ if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) {
+ void *vaddr;
+ ram_addr_t ram_addr;
+ target_phys_addr_t paddr;
+
+ /* Hope we are lucky for AO MCE */
+ vaddr = addr;
+ if (qemu_ram_addr_from_host(vaddr, &ram_addr) ||
+ !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr, &paddr)) {
+ fprintf(stderr, "Hardware memory error for memory used by "
+ "QEMU itself instead of guest system!: %p\n", addr);
+ return 0;
+ }
+ kvm_mce_inj_srao_memscrub2(first_cpu, paddr);
+ } else
+#endif
+ {
+ if (code == BUS_MCEERR_AO) {
+ return 0;
+ } else if (code == BUS_MCEERR_AR) {
+ hardware_memory_error();
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#include "qemu-kvm-x86.c"
diff --git a/target-i386/kvm_x86.h b/target-i386/kvm_x86.h
new file mode 100644
index 0000000..9d7b584
--- /dev/null
+++ b/target-i386/kvm_x86.h
@@ -0,0 +1,25 @@
+/*
+ * QEMU KVM support
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef __KVM_X86_H__
+#define __KVM_X86_H__
+
+#define ABORT_ON_ERROR 0x01
+#define MCE_BROADCAST 0x02
+
+void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status,
+ uint64_t mcg_status, uint64_t addr, uint64_t misc,
+ int flag);
+
+#endif
diff --git a/target-i386/libkvm.h b/target-i386/libkvm.h
new file mode 100644
index 0000000..d85b6a1
--- /dev/null
+++ b/target-i386/libkvm.h
@@ -0,0 +1,28 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for x86.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_X86_H
+#define KVM_X86_H
+
+#define PAGE_SIZE 4096ul
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr);
+
+#define smp_wmb() asm volatile("" ::: "memory")
+
+#endif
diff --git a/target-i386/machine.c b/target-i386/machine.c
new file mode 100644
index 0000000..bf14067
--- /dev/null
+++ b/target-i386/machine.c
@@ -0,0 +1,516 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/pc.h"
+#include "hw/isa.h"
+
+#include "exec-all.h"
+#include "kvm.h"
+
+static const VMStateDescription vmstate_segment = {
+ .name = "segment",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(selector, SegmentCache),
+ VMSTATE_UINTTL(base, SegmentCache),
+ VMSTATE_UINT32(limit, SegmentCache),
+ VMSTATE_UINT32(flags, SegmentCache),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define VMSTATE_SEGMENT(_field, _state) { \
+ .name = (stringify(_field)), \
+ .size = sizeof(SegmentCache), \
+ .vmsd = &vmstate_segment, \
+ .flags = VMS_STRUCT, \
+ .offset = offsetof(_state, _field) \
+ + type_check(SegmentCache,typeof_field(_state, _field)) \
+}
+
+#define VMSTATE_SEGMENT_ARRAY(_field, _state, _n) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_segment, SegmentCache)
+
+static const VMStateDescription vmstate_xmm_reg = {
+ .name = "xmm_reg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(XMM_Q(0), XMMReg),
+ VMSTATE_UINT64(XMM_Q(1), XMMReg),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define VMSTATE_XMM_REGS(_field, _state, _n) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_xmm_reg, XMMReg)
+
+/* YMMH format is the same as XMM */
+static const VMStateDescription vmstate_ymmh_reg = {
+ .name = "ymmh_reg",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(XMM_Q(0), XMMReg),
+ VMSTATE_UINT64(XMM_Q(1), XMMReg),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define VMSTATE_YMMH_REGS_VARS(_field, _state, _n, _v) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_ymmh_reg, XMMReg)
+
+static const VMStateDescription vmstate_mtrr_var = {
+ .name = "mtrr_var",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(base, MTRRVar),
+ VMSTATE_UINT64(mask, MTRRVar),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+#define VMSTATE_MTRR_VARS(_field, _state, _n, _v) \
+ VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_mtrr_var, MTRRVar)
+
+static void put_fpreg_error(QEMUFile *f, void *opaque, size_t size)
+{
+ fprintf(stderr, "call put_fpreg() with invalid arguments\n");
+ exit(0);
+}
+
+#ifdef USE_X86LDOUBLE
+/* XXX: add that in a FPU generic layer */
+union x86_longdouble {
+ uint64_t mant;
+ uint16_t exp;
+};
+
+#define MANTD1(fp) (fp & ((1LL << 52) - 1))
+#define EXPBIAS1 1023
+#define EXPD1(fp) ((fp >> 52) & 0x7FF)
+#define SIGND1(fp) ((fp >> 32) & 0x80000000)
+
+static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp)
+{
+ int e;
+ /* mantissa */
+ p->mant = (MANTD1(temp) << 11) | (1LL << 63);
+ /* exponent + sign */
+ e = EXPD1(temp) - EXPBIAS1 + 16383;
+ e |= SIGND1(temp) >> 16;
+ p->exp = e;
+}
+
+static int get_fpreg(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;
+}
+
+static void put_fpreg(QEMUFile *f, void *opaque, size_t size)
+{
+ FPReg *fp_reg = opaque;
+ uint64_t mant;
+ uint16_t exp;
+ /* we save the real CPU data (in case of MMX usage only 'mant'
+ contains the MMX register */
+ cpu_get_fp80(&mant, &exp, fp_reg->d);
+ qemu_put_be64s(f, &mant);
+ qemu_put_be16s(f, &exp);
+}
+
+static const VMStateInfo vmstate_fpreg = {
+ .name = "fpreg",
+ .get = get_fpreg,
+ .put = put_fpreg,
+};
+
+static int get_fpreg_1_mmx(QEMUFile *f, void *opaque, size_t size)
+{
+ union x86_longdouble *p = opaque;
+ uint64_t mant;
+
+ qemu_get_be64s(f, &mant);
+ p->mant = mant;
+ p->exp = 0xffff;
+ return 0;
+}
+
+static const VMStateInfo vmstate_fpreg_1_mmx = {
+ .name = "fpreg_1_mmx",
+ .get = get_fpreg_1_mmx,
+ .put = put_fpreg_error,
+};
+
+static int get_fpreg_1_no_mmx(QEMUFile *f, void *opaque, size_t size)
+{
+ union x86_longdouble *p = opaque;
+ uint64_t mant;
+
+ qemu_get_be64s(f, &mant);
+ fp64_to_fp80(p, mant);
+ return 0;
+}
+
+static const VMStateInfo vmstate_fpreg_1_no_mmx = {
+ .name = "fpreg_1_no_mmx",
+ .get = get_fpreg_1_no_mmx,
+ .put = put_fpreg_error,
+};
+
+static bool fpregs_is_0(void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+
+ return (env->fpregs_format_vmstate == 0);
+}
+
+static bool fpregs_is_1_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 == 1));
+}
+
+static bool fpregs_is_1_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 == 1));
+}
+
+#define VMSTATE_FP_REGS(_field, _state, _n) \
+ VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0, vmstate_fpreg, FPReg), \
+ 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;
+}
+
+#ifdef TARGET_X86_64
+static bool less_than_7(void *opaque, int version_id)
+{
+ return version_id < 7;
+}
+
+static int get_uint64_as_uint32(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ *v = qemu_get_be32(f);
+ return 0;
+}
+
+static void put_uint64_as_uint32(QEMUFile *f, void *pv, size_t size)
+{
+ uint64_t *v = pv;
+ qemu_put_be32(f, *v);
+}
+
+static const VMStateInfo vmstate_hack_uint64_as_uint32 = {
+ .name = "uint64_as_uint32",
+ .get = get_uint64_as_uint32,
+ .put = put_uint64_as_uint32,
+};
+
+#define VMSTATE_HACK_UINT32(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint64_as_uint32, uint64_t)
+#endif
+
+static void cpu_pre_save(void *opaque)
+{
+ CPUState *env = opaque;
+ int i;
+
+ /* FPU */
+ env->fpus_vmstate = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ env->fptag_vmstate = 0;
+ for(i = 0; i < 8; i++) {
+ 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)
+{
+ CPUState *env = opaque;
+ int i;
+
+ /* XXX: restore FPU round state */
+ env->fpstt = (env->fpus_vmstate >> 11) & 7;
+ env->fpus = env->fpus_vmstate & ~0x3800;
+ env->fptag_vmstate ^= 0xff;
+ for(i = 0; i < 8; i++) {
+ env->fptags[i] = (env->fptag_vmstate >> i) & 1;
+ }
+
+ cpu_breakpoint_remove_all(env, BP_CPU);
+ cpu_watchpoint_remove_all(env, BP_CPU);
+ for (i = 0; i < 4; i++)
+ hw_breakpoint_insert(env, i);
+
+ tlb_flush(env, 1);
+ return 0;
+}
+
+static bool async_pf_msr_needed(void *opaque)
+{
+ CPUState *cpu = opaque;
+
+ return cpu->async_pf_en_msr != 0;
+}
+
+static const VMStateDescription vmstate_async_pf_msr = {
+ .name = "cpu/async_pf_msr",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(async_pf_en_msr, CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_cpu = {
+ .name = "cpu",
+ .version_id = CPU_SAVE_VERSION,
+ .minimum_version_id = 3,
+ .minimum_version_id_old = 3,
+ .pre_save = cpu_pre_save,
+ .post_load = cpu_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINTTL_ARRAY(regs, CPUState, CPU_NB_REGS),
+ VMSTATE_UINTTL(eip, CPUState),
+ VMSTATE_UINTTL(eflags, CPUState),
+ VMSTATE_UINT32(hflags, CPUState),
+ /* FPU */
+ VMSTATE_UINT16(fpuc, CPUState),
+ VMSTATE_UINT16(fpus_vmstate, CPUState),
+ VMSTATE_UINT16(fptag_vmstate, CPUState),
+ VMSTATE_UINT16(fpregs_format_vmstate, CPUState),
+ VMSTATE_FP_REGS(fpregs, CPUState, 8),
+
+ VMSTATE_SEGMENT_ARRAY(segs, CPUState, 6),
+ VMSTATE_SEGMENT(ldt, CPUState),
+ VMSTATE_SEGMENT(tr, CPUState),
+ VMSTATE_SEGMENT(gdt, CPUState),
+ VMSTATE_SEGMENT(idt, CPUState),
+
+ VMSTATE_UINT32(sysenter_cs, CPUState),
+#ifdef TARGET_X86_64
+ /* Hack: In v7 size changed from 32 to 64 bits on x86_64 */
+ VMSTATE_HACK_UINT32(sysenter_esp, CPUState, less_than_7),
+ VMSTATE_HACK_UINT32(sysenter_eip, CPUState, less_than_7),
+ VMSTATE_UINTTL_V(sysenter_esp, CPUState, 7),
+ VMSTATE_UINTTL_V(sysenter_eip, CPUState, 7),
+#else
+ VMSTATE_UINTTL(sysenter_esp, CPUState),
+ VMSTATE_UINTTL(sysenter_eip, CPUState),
+#endif
+
+ VMSTATE_UINTTL(cr[0], CPUState),
+ VMSTATE_UINTTL(cr[2], CPUState),
+ VMSTATE_UINTTL(cr[3], CPUState),
+ VMSTATE_UINTTL(cr[4], CPUState),
+ VMSTATE_UINTTL_ARRAY(dr, CPUState, 8),
+ /* MMU */
+ VMSTATE_INT32(a20_mask, CPUState),
+ /* XMM */
+ VMSTATE_UINT32(mxcsr, CPUState),
+ VMSTATE_XMM_REGS(xmm_regs, CPUState, CPU_NB_REGS),
+
+#ifdef TARGET_X86_64
+ VMSTATE_UINT64(efer, CPUState),
+ VMSTATE_UINT64(star, CPUState),
+ VMSTATE_UINT64(lstar, CPUState),
+ VMSTATE_UINT64(cstar, CPUState),
+ VMSTATE_UINT64(fmask, CPUState),
+ VMSTATE_UINT64(kernelgsbase, CPUState),
+#endif
+ VMSTATE_UINT32_V(smbase, CPUState, 4),
+
+ VMSTATE_UINT64_V(pat, CPUState, 5),
+ VMSTATE_UINT32_V(hflags2, CPUState, 5),
+
+ VMSTATE_UINT32_TEST(halted, CPUState, version_is_5),
+ VMSTATE_UINT64_V(vm_hsave, CPUState, 5),
+ VMSTATE_UINT64_V(vm_vmcb, CPUState, 5),
+ VMSTATE_UINT64_V(tsc_offset, CPUState, 5),
+ VMSTATE_UINT64_V(intercept, CPUState, 5),
+ VMSTATE_UINT16_V(intercept_cr_read, CPUState, 5),
+ VMSTATE_UINT16_V(intercept_cr_write, CPUState, 5),
+ VMSTATE_UINT16_V(intercept_dr_read, CPUState, 5),
+ VMSTATE_UINT16_V(intercept_dr_write, CPUState, 5),
+ VMSTATE_UINT32_V(intercept_exceptions, CPUState, 5),
+ VMSTATE_UINT8_V(v_tpr, CPUState, 5),
+ /* MTRRs */
+ VMSTATE_UINT64_ARRAY_V(mtrr_fixed, CPUState, 11, 8),
+ VMSTATE_UINT64_V(mtrr_deftype, CPUState, 8),
+ VMSTATE_MTRR_VARS(mtrr_var, CPUState, 8, 8),
+ /* KVM-related states */
+ VMSTATE_INT32_V(interrupt_injected, CPUState, 9),
+ VMSTATE_UINT32_V(mp_state, CPUState, 9),
+ VMSTATE_UINT64_V(tsc, CPUState, 9),
+ VMSTATE_INT32_V(exception_injected, CPUState, 11),
+ VMSTATE_UINT8_V(soft_interrupt, CPUState, 11),
+ VMSTATE_UINT8_V(nmi_injected, CPUState, 11),
+ VMSTATE_UINT8_V(nmi_pending, CPUState, 11),
+ VMSTATE_UINT8_V(has_error_code, CPUState, 11),
+ VMSTATE_UINT32_V(sipi_vector, CPUState, 11),
+ /* MCE */
+ VMSTATE_UINT64_V(mcg_cap, CPUState, 10),
+ VMSTATE_UINT64_V(mcg_status, CPUState, 10),
+ VMSTATE_UINT64_V(mcg_ctl, CPUState, 10),
+ VMSTATE_UINT64_ARRAY_V(mce_banks, CPUState, MCE_BANKS_DEF *4, 10),
+ /* rdtscp */
+ VMSTATE_UINT64_V(tsc_aux, CPUState, 11),
+ /* KVM pvclock msr */
+ VMSTATE_UINT64_V(system_time_msr, CPUState, 11),
+ VMSTATE_UINT64_V(wall_clock_msr, CPUState, 11),
+
+ /* XSAVE related fields */
+ VMSTATE_UINT64_V(xcr0, CPUState, 12),
+ VMSTATE_UINT64_V(xstate_bv, CPUState, 12),
+ VMSTATE_YMMH_REGS_VARS(ymmh_regs, CPUState, CPU_NB_REGS, 12),
+ VMSTATE_END_OF_LIST()
+ /* The above list is not sorted /wrt version numbers, watch out! */
+ },
+ .subsections = (VMStateSubsection []) {
+ {
+ .vmsd = &vmstate_async_pf_msr,
+ .needed = async_pf_msr_needed,
+ } , {
+ /* empty */
+ }
+ }
+};
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ vmstate_save_state(f, &vmstate_cpu, opaque);
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return vmstate_load_state(f, &vmstate_cpu, opaque, version_id);
+}
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
new file mode 100644
index 0000000..43fbd0c
--- /dev/null
+++ b/target-i386/op_helper.c
@@ -0,0 +1,5669 @@
+/*
+ * i386 helpers
+ *
+ * Copyright (c) 2003 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 "exec.h"
+#include "exec-all.h"
+#include "host-utils.h"
+#include "ioport.h"
+
+//#define DEBUG_PCALL
+
+
+#ifdef DEBUG_PCALL
+# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
+# define LOG_PCALL_STATE(env) \
+ log_cpu_state_mask(CPU_LOG_PCALL, (env), X86_DUMP_CCOP)
+#else
+# define LOG_PCALL(...) do { } while (0)
+# define LOG_PCALL_STATE(env) do { } while (0)
+#endif
+
+
+#if 0
+#define raise_exception_err(a, b)\
+do {\
+ qemu_log("raise_exception line=%d\n", __LINE__);\
+ (raise_exception_err)(a, b);\
+} while (0)
+#endif
+
+static const uint8_t parity_table[256] = {
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0,
+ 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P,
+};
+
+/* modulo 17 table */
+static const uint8_t rclw_table[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9,10,11,12,13,14,15,
+ 16, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9,10,11,12,13,14,
+};
+
+/* modulo 9 table */
+static const uint8_t rclb_table[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 0, 1, 2, 3, 4, 5,
+ 6, 7, 8, 0, 1, 2, 3, 4,
+};
+
+static const CPU86_LDouble f15rk[7] =
+{
+ 0.00000000000000000000L,
+ 1.00000000000000000000L,
+ 3.14159265358979323851L, /*pi*/
+ 0.30102999566398119523L, /*lg2*/
+ 0.69314718055994530943L, /*ln2*/
+ 1.44269504088896340739L, /*l2e*/
+ 3.32192809488736234781L, /*l2t*/
+};
+
+/* broken thread support */
+
+static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
+
+void helper_lock(void)
+{
+ spin_lock(&global_cpu_lock);
+}
+
+void helper_unlock(void)
+{
+ spin_unlock(&global_cpu_lock);
+}
+
+void helper_write_eflags(target_ulong t0, uint32_t update_mask)
+{
+ load_eflags(t0, update_mask);
+}
+
+target_ulong helper_read_eflags(void)
+{
+ uint32_t eflags;
+ eflags = helper_cc_compute_all(CC_OP);
+ eflags |= (DF & DF_MASK);
+ eflags |= env->eflags & ~(VM_MASK | RF_MASK);
+ return eflags;
+}
+
+/* return non zero if error */
+static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
+ int selector)
+{
+ SegmentCache *dt;
+ int index;
+ target_ulong ptr;
+
+ if (selector & 0x4)
+ dt = &env->ldt;
+ else
+ dt = &env->gdt;
+ index = selector & ~7;
+ if ((index + 7) > dt->limit)
+ return -1;
+ ptr = dt->base + index;
+ *e1_ptr = ldl_kernel(ptr);
+ *e2_ptr = ldl_kernel(ptr + 4);
+ return 0;
+}
+
+static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
+{
+ unsigned int limit;
+ limit = (e1 & 0xffff) | (e2 & 0x000f0000);
+ if (e2 & DESC_G_MASK)
+ limit = (limit << 12) | 0xfff;
+ return limit;
+}
+
+static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
+{
+ return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
+}
+
+static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
+{
+ sc->base = get_seg_base(e1, e2);
+ sc->limit = get_seg_limit(e1, e2);
+ sc->flags = e2;
+}
+
+/* init the segment cache in vm86 mode. */
+static inline void load_seg_vm(int seg, int selector)
+{
+ selector &= 0xffff;
+ cpu_x86_load_seg_cache(env, seg, selector,
+ (selector << 4), 0xffff, 0);
+}
+
+static inline void get_ss_esp_from_tss(uint32_t *ss_ptr,
+ uint32_t *esp_ptr, int dpl)
+{
+ int type, index, shift;
+
+#if 0
+ {
+ int i;
+ printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
+ for(i=0;i<env->tr.limit;i++) {
+ printf("%02x ", env->tr.base[i]);
+ if ((i & 7) == 7) printf("\n");
+ }
+ printf("\n");
+ }
+#endif
+
+ if (!(env->tr.flags & DESC_P_MASK))
+ cpu_abort(env, "invalid tss");
+ type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ if ((type & 7) != 1)
+ cpu_abort(env, "invalid tss type");
+ shift = type >> 3;
+ index = (dpl * 4 + 2) << shift;
+ if (index + (4 << shift) - 1 > env->tr.limit)
+ raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
+ if (shift == 0) {
+ *esp_ptr = lduw_kernel(env->tr.base + index);
+ *ss_ptr = lduw_kernel(env->tr.base + index + 2);
+ } else {
+ *esp_ptr = ldl_kernel(env->tr.base + index);
+ *ss_ptr = lduw_kernel(env->tr.base + index + 4);
+ }
+}
+
+/* XXX: merge with load_seg() */
+static void tss_load_seg(int seg_reg, int selector)
+{
+ uint32_t e1, e2;
+ int rpl, dpl, cpl;
+
+ if ((selector & 0xfffc) != 0) {
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (seg_reg == R_CS) {
+ if (!(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ /* XXX: is it correct ? */
+ if (dpl != rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ if ((e2 & DESC_C_MASK) && dpl > rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ } else if (seg_reg == R_SS) {
+ /* SS must be writable data */
+ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ if (dpl != cpl || dpl != rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ } else {
+ /* not readable code */
+ if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ /* if data or non conforming code, checks the rights */
+ if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ }
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ } else {
+ if (seg_reg == R_SS || seg_reg == R_CS)
+ raise_exception_err(EXCP0A_TSS, selector & 0xfffc);
+ }
+}
+
+#define SWITCH_TSS_JMP 0
+#define SWITCH_TSS_IRET 1
+#define SWITCH_TSS_CALL 2
+
+/* XXX: restore CPU state in registers (PowerPC case) */
+static void switch_tss(int tss_selector,
+ uint32_t e1, uint32_t e2, int source,
+ uint32_t next_eip)
+{
+ int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
+ target_ulong tss_base;
+ uint32_t new_regs[8], new_segs[6];
+ uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap;
+ uint32_t old_eflags, eflags_mask;
+ SegmentCache *dt;
+ int index;
+ target_ulong ptr;
+
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
+
+ /* if task gate, we read the TSS segment and we load it */
+ if (type == 5) {
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
+ tss_selector = e1 >> 16;
+ if (tss_selector & 4)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ if (load_segment(&e1, &e2, tss_selector) != 0)
+ raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
+ if (e2 & DESC_S_MASK)
+ raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ if ((type & 7) != 1)
+ raise_exception_err(EXCP0D_GPF, tss_selector & 0xfffc);
+ }
+
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, tss_selector & 0xfffc);
+
+ if (type & 8)
+ tss_limit_max = 103;
+ else
+ tss_limit_max = 43;
+ tss_limit = get_seg_limit(e1, e2);
+ tss_base = get_seg_base(e1, e2);
+ if ((tss_selector & 4) != 0 ||
+ tss_limit < tss_limit_max)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ if (old_type & 8)
+ old_tss_limit_max = 103;
+ else
+ old_tss_limit_max = 43;
+
+ /* read all the registers from the new TSS */
+ if (type & 8) {
+ /* 32 bit */
+ new_cr3 = ldl_kernel(tss_base + 0x1c);
+ new_eip = ldl_kernel(tss_base + 0x20);
+ new_eflags = ldl_kernel(tss_base + 0x24);
+ for(i = 0; i < 8; i++)
+ new_regs[i] = ldl_kernel(tss_base + (0x28 + i * 4));
+ for(i = 0; i < 6; i++)
+ new_segs[i] = lduw_kernel(tss_base + (0x48 + i * 4));
+ new_ldt = lduw_kernel(tss_base + 0x60);
+ new_trap = ldl_kernel(tss_base + 0x64);
+ } else {
+ /* 16 bit */
+ new_cr3 = 0;
+ new_eip = lduw_kernel(tss_base + 0x0e);
+ new_eflags = lduw_kernel(tss_base + 0x10);
+ for(i = 0; i < 8; i++)
+ new_regs[i] = lduw_kernel(tss_base + (0x12 + i * 2)) | 0xffff0000;
+ for(i = 0; i < 4; i++)
+ new_segs[i] = lduw_kernel(tss_base + (0x22 + i * 4));
+ new_ldt = lduw_kernel(tss_base + 0x2a);
+ new_segs[R_FS] = 0;
+ new_segs[R_GS] = 0;
+ new_trap = 0;
+ }
+ /* XXX: avoid a compiler warning, see
+ http://support.amd.com/us/Processor_TechDocs/24593.pdf
+ chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */
+ (void)new_trap;
+
+ /* NOTE: we must avoid memory exceptions during the task switch,
+ so we make dummy accesses before */
+ /* XXX: it can still fail in some cases, so a bigger hack is
+ necessary to valid the TLB after having done the accesses */
+
+ v1 = ldub_kernel(env->tr.base);
+ v2 = ldub_kernel(env->tr.base + old_tss_limit_max);
+ stb_kernel(env->tr.base, v1);
+ stb_kernel(env->tr.base + old_tss_limit_max, v2);
+
+ /* clear busy bit (it is restartable) */
+ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
+ target_ulong ptr;
+ uint32_t e2;
+ ptr = env->gdt.base + (env->tr.selector & ~7);
+ e2 = ldl_kernel(ptr + 4);
+ e2 &= ~DESC_TSS_BUSY_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+ old_eflags = compute_eflags();
+ if (source == SWITCH_TSS_IRET)
+ old_eflags &= ~NT_MASK;
+
+ /* save the current state in the old TSS */
+ if (type & 8) {
+ /* 32 bit */
+ stl_kernel(env->tr.base + 0x20, next_eip);
+ stl_kernel(env->tr.base + 0x24, old_eflags);
+ stl_kernel(env->tr.base + (0x28 + 0 * 4), EAX);
+ stl_kernel(env->tr.base + (0x28 + 1 * 4), ECX);
+ stl_kernel(env->tr.base + (0x28 + 2 * 4), EDX);
+ stl_kernel(env->tr.base + (0x28 + 3 * 4), EBX);
+ stl_kernel(env->tr.base + (0x28 + 4 * 4), ESP);
+ stl_kernel(env->tr.base + (0x28 + 5 * 4), EBP);
+ stl_kernel(env->tr.base + (0x28 + 6 * 4), ESI);
+ stl_kernel(env->tr.base + (0x28 + 7 * 4), EDI);
+ for(i = 0; i < 6; i++)
+ stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector);
+ } else {
+ /* 16 bit */
+ stw_kernel(env->tr.base + 0x0e, next_eip);
+ stw_kernel(env->tr.base + 0x10, old_eflags);
+ stw_kernel(env->tr.base + (0x12 + 0 * 2), EAX);
+ stw_kernel(env->tr.base + (0x12 + 1 * 2), ECX);
+ stw_kernel(env->tr.base + (0x12 + 2 * 2), EDX);
+ stw_kernel(env->tr.base + (0x12 + 3 * 2), EBX);
+ stw_kernel(env->tr.base + (0x12 + 4 * 2), ESP);
+ stw_kernel(env->tr.base + (0x12 + 5 * 2), EBP);
+ stw_kernel(env->tr.base + (0x12 + 6 * 2), ESI);
+ stw_kernel(env->tr.base + (0x12 + 7 * 2), EDI);
+ for(i = 0; i < 4; i++)
+ stw_kernel(env->tr.base + (0x22 + i * 4), env->segs[i].selector);
+ }
+
+ /* now if an exception occurs, it will occurs in the next task
+ context */
+
+ if (source == SWITCH_TSS_CALL) {
+ stw_kernel(tss_base, env->tr.selector);
+ new_eflags |= NT_MASK;
+ }
+
+ /* set busy bit */
+ if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
+ target_ulong ptr;
+ uint32_t e2;
+ ptr = env->gdt.base + (tss_selector & ~7);
+ e2 = ldl_kernel(ptr + 4);
+ e2 |= DESC_TSS_BUSY_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+
+ /* set the new CPU state */
+ /* from this point, any exception which occurs can give problems */
+ env->cr[0] |= CR0_TS_MASK;
+ env->hflags |= HF_TS_MASK;
+ env->tr.selector = tss_selector;
+ env->tr.base = tss_base;
+ env->tr.limit = tss_limit;
+ env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
+
+ if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
+ cpu_x86_update_cr3(env, new_cr3);
+ }
+
+ /* load all registers without an exception, then reload them with
+ possible exception */
+ env->eip = new_eip;
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
+ if (!(type & 8))
+ eflags_mask &= 0xffff;
+ load_eflags(new_eflags, eflags_mask);
+ /* XXX: what to do in 16 bit case ? */
+ EAX = new_regs[0];
+ ECX = new_regs[1];
+ EDX = new_regs[2];
+ EBX = new_regs[3];
+ ESP = new_regs[4];
+ EBP = new_regs[5];
+ ESI = new_regs[6];
+ EDI = new_regs[7];
+ if (new_eflags & VM_MASK) {
+ for(i = 0; i < 6; i++)
+ load_seg_vm(i, new_segs[i]);
+ /* in vm86, CPL is always 3 */
+ cpu_x86_set_cpl(env, 3);
+ } else {
+ /* CPL is set the RPL of CS */
+ cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
+ /* first just selectors as the rest may trigger exceptions */
+ for(i = 0; i < 6; i++)
+ cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
+ }
+
+ env->ldt.selector = new_ldt & ~4;
+ env->ldt.base = 0;
+ env->ldt.limit = 0;
+ env->ldt.flags = 0;
+
+ /* load the LDT */
+ if (new_ldt & 4)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+
+ if ((new_ldt & 0xfffc) != 0) {
+ dt = &env->gdt;
+ index = new_ldt & ~7;
+ if ((index + 7) > dt->limit)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, new_ldt & 0xfffc);
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ }
+
+ /* load the segments */
+ if (!(new_eflags & VM_MASK)) {
+ tss_load_seg(R_CS, new_segs[R_CS]);
+ tss_load_seg(R_SS, new_segs[R_SS]);
+ tss_load_seg(R_ES, new_segs[R_ES]);
+ tss_load_seg(R_DS, new_segs[R_DS]);
+ tss_load_seg(R_FS, new_segs[R_FS]);
+ tss_load_seg(R_GS, new_segs[R_GS]);
+ }
+
+ /* check that EIP is in the CS segment limits */
+ if (new_eip > env->segs[R_CS].limit) {
+ /* XXX: different exception if CALL ? */
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+
+#ifndef CONFIG_USER_ONLY
+ /* reset local breakpoints */
+ if (env->dr[7] & 0x55) {
+ for (i = 0; i < 4; i++) {
+ if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
+ hw_breakpoint_remove(env, i);
+ }
+ env->dr[7] &= ~0x55;
+ }
+#endif
+}
+
+/* check if Port I/O is allowed in TSS */
+static inline void check_io(int addr, int size)
+{
+ int io_offset, val, mask;
+
+ /* TSS must be a valid 32 bit one */
+ if (!(env->tr.flags & DESC_P_MASK) ||
+ ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
+ env->tr.limit < 103)
+ goto fail;
+ io_offset = lduw_kernel(env->tr.base + 0x66);
+ io_offset += (addr >> 3);
+ /* Note: the check needs two bytes */
+ if ((io_offset + 1) > env->tr.limit)
+ goto fail;
+ val = lduw_kernel(env->tr.base + io_offset);
+ val >>= (addr & 7);
+ mask = (1 << size) - 1;
+ /* all bits must be zero to allow the I/O */
+ if ((val & mask) != 0) {
+ fail:
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+}
+
+void helper_check_iob(uint32_t t0)
+{
+ check_io(t0, 1);
+}
+
+void helper_check_iow(uint32_t t0)
+{
+ check_io(t0, 2);
+}
+
+void helper_check_iol(uint32_t t0)
+{
+ check_io(t0, 4);
+}
+
+void helper_outb(uint32_t port, uint32_t data)
+{
+ cpu_outb(port, data & 0xff);
+}
+
+target_ulong helper_inb(uint32_t port)
+{
+ return cpu_inb(port);
+}
+
+void helper_outw(uint32_t port, uint32_t data)
+{
+ cpu_outw(port, data & 0xffff);
+}
+
+target_ulong helper_inw(uint32_t port)
+{
+ return cpu_inw(port);
+}
+
+void helper_outl(uint32_t port, uint32_t data)
+{
+ cpu_outl(port, data);
+}
+
+target_ulong helper_inl(uint32_t port)
+{
+ return cpu_inl(port);
+}
+
+static inline unsigned int get_sp_mask(unsigned int e2)
+{
+ if (e2 & DESC_B_MASK)
+ return 0xffffffff;
+ else
+ return 0xffff;
+}
+
+static int exeption_has_error_code(int intno)
+{
+ switch(intno) {
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 17:
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef TARGET_X86_64
+#define SET_ESP(val, sp_mask)\
+do {\
+ if ((sp_mask) == 0xffff)\
+ ESP = (ESP & ~0xffff) | ((val) & 0xffff);\
+ else if ((sp_mask) == 0xffffffffLL)\
+ ESP = (uint32_t)(val);\
+ else\
+ ESP = (val);\
+} while (0)
+#else
+#define SET_ESP(val, sp_mask) ESP = (ESP & ~(sp_mask)) | ((val) & (sp_mask))
+#endif
+
+/* in 64-bit machines, this can overflow. So this segment addition macro
+ * can be used to trim the value to 32-bit whenever needed */
+#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
+
+/* XXX: add a is_user flag to have proper security support */
+#define PUSHW(ssp, sp, sp_mask, val)\
+{\
+ sp -= 2;\
+ stw_kernel((ssp) + (sp & (sp_mask)), (val));\
+}
+
+#define PUSHL(ssp, sp, sp_mask, val)\
+{\
+ sp -= 4;\
+ stl_kernel(SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val));\
+}
+
+#define POPW(ssp, sp, sp_mask, val)\
+{\
+ val = lduw_kernel((ssp) + (sp & (sp_mask)));\
+ sp += 2;\
+}
+
+#define POPL(ssp, sp, sp_mask, val)\
+{\
+ val = (uint32_t)ldl_kernel(SEG_ADDL(ssp, sp, sp_mask));\
+ sp += 4;\
+}
+
+/* protected mode interrupt */
+static void do_interrupt_protected(int intno, int is_int, int error_code,
+ unsigned int next_eip, int is_hw)
+{
+ SegmentCache *dt;
+ target_ulong ptr, ssp;
+ int type, dpl, selector, ss_dpl, cpl;
+ int has_error_code, new_stack, shift;
+ uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
+ uint32_t old_eip, sp_mask;
+
+ has_error_code = 0;
+ if (!is_int && !is_hw)
+ has_error_code = exeption_has_error_code(intno);
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+
+ dt = &env->idt;
+ if (intno * 8 + 7 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ ptr = dt->base + intno * 8;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ switch(type) {
+ case 5: /* task gate */
+ /* must do that check here to return the correct error code */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
+ switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
+ if (has_error_code) {
+ int type;
+ uint32_t mask;
+ /* push the error code */
+ type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
+ shift = type >> 3;
+ if (env->segs[R_SS].flags & DESC_B_MASK)
+ mask = 0xffffffff;
+ else
+ mask = 0xffff;
+ esp = (ESP - (2 << shift)) & mask;
+ ssp = env->segs[R_SS].base + esp;
+ if (shift)
+ stl_kernel(ssp, error_code);
+ else
+ stw_kernel(ssp, error_code);
+ SET_ESP(esp, mask);
+ }
+ return;
+ case 6: /* 286 interrupt gate */
+ case 7: /* 286 trap gate */
+ case 14: /* 386 interrupt gate */
+ case 15: /* 386 trap gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ break;
+ }
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privilege if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
+ selector = e1 >> 16;
+ offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ if (!(e2 & DESC_C_MASK) && dpl < cpl) {
+ /* to inner privilege */
+ get_ss_esp_from_tss(&ss, &esp, dpl);
+ if ((ss & 0xfffc) == 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if ((ss & 3) != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, ss) != 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (ss_dpl != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ new_stack = 1;
+ sp_mask = get_sp_mask(ss_e2);
+ ssp = get_seg_base(ss_e1, ss_e2);
+ } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
+ /* to same privilege */
+ if (env->eflags & VM_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ esp = ESP;
+ dpl = cpl;
+ } else {
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0; /* avoid warning */
+ sp_mask = 0; /* avoid warning */
+ ssp = 0; /* avoid warning */
+ esp = 0; /* avoid warning */
+ }
+
+ shift = type >> 3;
+
+#if 0
+ /* XXX: check that enough room is available */
+ push_size = 6 + (new_stack << 2) + (has_error_code << 1);
+ if (env->eflags & VM_MASK)
+ push_size += 8;
+ push_size <<= shift;
+#endif
+ if (shift == 1) {
+ if (new_stack) {
+ if (env->eflags & VM_MASK) {
+ PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
+ }
+ PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
+ PUSHL(ssp, esp, sp_mask, ESP);
+ }
+ PUSHL(ssp, esp, sp_mask, compute_eflags());
+ PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, esp, sp_mask, old_eip);
+ if (has_error_code) {
+ PUSHL(ssp, esp, sp_mask, error_code);
+ }
+ } else {
+ if (new_stack) {
+ if (env->eflags & VM_MASK) {
+ PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
+ }
+ PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
+ PUSHW(ssp, esp, sp_mask, ESP);
+ }
+ PUSHW(ssp, esp, sp_mask, compute_eflags());
+ PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, esp, sp_mask, old_eip);
+ if (has_error_code) {
+ PUSHW(ssp, esp, sp_mask, error_code);
+ }
+ }
+
+ if (new_stack) {
+ if (env->eflags & VM_MASK) {
+ cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
+ cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
+ }
+ ss = (ss & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss,
+ ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
+ }
+ SET_ESP(esp, sp_mask);
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ env->eip = offset;
+
+ /* interrupt gate clear IF mask */
+ if ((type & 1) == 0) {
+ env->eflags &= ~IF_MASK;
+ }
+ env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
+}
+
+#ifdef TARGET_X86_64
+
+#define PUSHQ(sp, val)\
+{\
+ sp -= 8;\
+ stq_kernel(sp, (val));\
+}
+
+#define POPQ(sp, val)\
+{\
+ val = ldq_kernel(sp);\
+ sp += 8;\
+}
+
+static inline target_ulong get_rsp_from_tss(int level)
+{
+ int index;
+
+#if 0
+ printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
+ env->tr.base, env->tr.limit);
+#endif
+
+ if (!(env->tr.flags & DESC_P_MASK))
+ cpu_abort(env, "invalid tss");
+ index = 8 * level + 4;
+ if ((index + 7) > env->tr.limit)
+ raise_exception_err(EXCP0A_TSS, env->tr.selector & 0xfffc);
+ return ldq_kernel(env->tr.base + index);
+}
+
+/* 64 bit interrupt */
+static void do_interrupt64(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw)
+{
+ SegmentCache *dt;
+ target_ulong ptr;
+ int type, dpl, selector, cpl, ist;
+ int has_error_code, new_stack;
+ uint32_t e1, e2, e3, ss;
+ target_ulong old_eip, esp, offset;
+
+ has_error_code = 0;
+ if (!is_int && !is_hw)
+ has_error_code = exeption_has_error_code(intno);
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+
+ dt = &env->idt;
+ if (intno * 16 + 15 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ ptr = dt->base + intno * 16;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ e3 = ldl_kernel(ptr + 8);
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ switch(type) {
+ case 14: /* 386 interrupt gate */
+ case 15: /* 386 trap gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ break;
+ }
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privilege if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, intno * 16 + 2);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, intno * 16 + 2);
+ selector = e1 >> 16;
+ offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ ist = e2 & 7;
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
+ /* to inner privilege */
+ if (ist != 0)
+ esp = get_rsp_from_tss(ist + 3);
+ else
+ esp = get_rsp_from_tss(dpl);
+ esp &= ~0xfLL; /* align stack */
+ ss = 0;
+ new_stack = 1;
+ } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
+ /* to same privilege */
+ if (env->eflags & VM_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0;
+ if (ist != 0)
+ esp = get_rsp_from_tss(ist + 3);
+ else
+ esp = ESP;
+ esp &= ~0xfLL; /* align stack */
+ dpl = cpl;
+ } else {
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ new_stack = 0; /* avoid warning */
+ esp = 0; /* avoid warning */
+ }
+
+ PUSHQ(esp, env->segs[R_SS].selector);
+ PUSHQ(esp, ESP);
+ PUSHQ(esp, compute_eflags());
+ PUSHQ(esp, env->segs[R_CS].selector);
+ PUSHQ(esp, old_eip);
+ if (has_error_code) {
+ PUSHQ(esp, error_code);
+ }
+
+ if (new_stack) {
+ ss = 0 | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
+ }
+ ESP = esp;
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ env->eip = offset;
+
+ /* interrupt gate clear IF mask */
+ if ((type & 1) == 0) {
+ env->eflags &= ~IF_MASK;
+ }
+ env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
+}
+#endif
+
+#ifdef TARGET_X86_64
+#if defined(CONFIG_USER_ONLY)
+void helper_syscall(int next_eip_addend)
+{
+ env->exception_index = EXCP_SYSCALL;
+ env->exception_next_eip = env->eip + next_eip_addend;
+ cpu_loop_exit();
+}
+#else
+void helper_syscall(int next_eip_addend)
+{
+ int selector;
+
+ if (!(env->efer & MSR_EFER_SCE)) {
+ raise_exception_err(EXCP06_ILLOP, 0);
+ }
+ selector = (env->star >> 32) & 0xffff;
+ if (env->hflags & HF_LMA_MASK) {
+ int code64;
+
+ ECX = env->eip + next_eip_addend;
+ env->regs[11] = compute_eflags();
+
+ code64 = env->hflags & HF_CS64_MASK;
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~env->fmask;
+ load_eflags(env->eflags, 0);
+ if (code64)
+ env->eip = env->lstar;
+ else
+ env->eip = env->cstar;
+ } else {
+ ECX = (uint32_t)(env->eip + next_eip_addend);
+
+ cpu_x86_set_cpl(env, 0);
+ cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
+ env->eip = (uint32_t)env->star;
+ }
+}
+#endif
+#endif
+
+#ifdef TARGET_X86_64
+void helper_sysret(int dflag)
+{
+ int cpl, selector;
+
+ if (!(env->efer & MSR_EFER_SCE)) {
+ raise_exception_err(EXCP06_ILLOP, 0);
+ }
+ cpl = env->hflags & HF_CPL_MASK;
+ if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ selector = (env->star >> 48) & 0xffff;
+ if (env->hflags & HF_LMA_MASK) {
+ if (dflag == 2) {
+ cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
+ DESC_L_MASK);
+ env->eip = ECX;
+ } else {
+ cpu_x86_load_seg_cache(env, R_CS, selector | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ env->eip = (uint32_t)ECX;
+ }
+ cpu_x86_load_seg_cache(env, R_SS, selector + 8,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ load_eflags((uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
+ cpu_x86_set_cpl(env, 3);
+ } else {
+ cpu_x86_load_seg_cache(env, R_CS, selector | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ env->eip = (uint32_t)ECX;
+ cpu_x86_load_seg_cache(env, R_SS, selector + 8,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ env->eflags |= IF_MASK;
+ cpu_x86_set_cpl(env, 3);
+ }
+}
+#endif
+
+/* real mode interrupt */
+static void do_interrupt_real(int intno, int is_int, int error_code,
+ unsigned int next_eip)
+{
+ SegmentCache *dt;
+ target_ulong ptr, ssp;
+ int selector;
+ uint32_t offset, esp;
+ uint32_t old_cs, old_eip;
+
+ /* real mode (simpler !) */
+ dt = &env->idt;
+ if (intno * 4 + 3 > dt->limit)
+ raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
+ ptr = dt->base + intno * 4;
+ offset = lduw_kernel(ptr);
+ selector = lduw_kernel(ptr + 2);
+ esp = ESP;
+ ssp = env->segs[R_SS].base;
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
+ old_cs = env->segs[R_CS].selector;
+ /* XXX: use SS segment size ? */
+ PUSHW(ssp, esp, 0xffff, compute_eflags());
+ PUSHW(ssp, esp, 0xffff, old_cs);
+ PUSHW(ssp, esp, 0xffff, old_eip);
+
+ /* update processor state */
+ ESP = (ESP & ~0xffff) | (esp & 0xffff);
+ env->eip = offset;
+ env->segs[R_CS].selector = selector;
+ env->segs[R_CS].base = (selector << 4);
+ env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
+}
+
+/* fake user mode interrupt */
+void do_interrupt_user(int intno, int is_int, int error_code,
+ target_ulong next_eip)
+{
+ SegmentCache *dt;
+ target_ulong ptr;
+ int dpl, cpl, shift;
+ uint32_t e2;
+
+ dt = &env->idt;
+ if (env->hflags & HF_LMA_MASK) {
+ shift = 4;
+ } else {
+ shift = 3;
+ }
+ ptr = dt->base + (intno << shift);
+ e2 = ldl_kernel(ptr + 4);
+
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ /* check privilege if software int */
+ if (is_int && dpl < cpl)
+ raise_exception_err(EXCP0D_GPF, (intno << shift) + 2);
+
+ /* Since we emulate only user space, we cannot do more than
+ exiting the emulation with the suitable exception and error
+ code */
+ if (is_int)
+ EIP = next_eip;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void handle_even_inj(int intno, int is_int, int error_code,
+ int is_hw, int rm)
+{
+ uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
+ if (!(event_inj & SVM_EVTINJ_VALID)) {
+ int type;
+ if (is_int)
+ type = SVM_EVTINJ_TYPE_SOFT;
+ else
+ type = SVM_EVTINJ_TYPE_EXEPT;
+ event_inj = intno | type | SVM_EVTINJ_VALID;
+ if (!rm && exeption_has_error_code(intno)) {
+ event_inj |= SVM_EVTINJ_VALID_ERR;
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code);
+ }
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj);
+ }
+}
+#endif
+
+/*
+ * Begin execution of an interruption. is_int is TRUE if coming from
+ * the int instruction. next_eip is the EIP value AFTER the interrupt
+ * instruction. It is only relevant if is_int is TRUE.
+ */
+void do_interrupt(int intno, int is_int, int error_code,
+ target_ulong next_eip, int is_hw)
+{
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ if ((env->cr[0] & CR0_PE_MASK)) {
+ static int count;
+ qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
+ count, intno, error_code, is_int,
+ env->hflags & HF_CPL_MASK,
+ env->segs[R_CS].selector, EIP,
+ (int)env->segs[R_CS].base + EIP,
+ env->segs[R_SS].selector, ESP);
+ if (intno == 0x0e) {
+ qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
+ } else {
+ qemu_log(" EAX=" TARGET_FMT_lx, EAX);
+ }
+ qemu_log("\n");
+ log_cpu_state(env, X86_DUMP_CCOP);
+#if 0
+ {
+ int i;
+ target_ulong ptr;
+ qemu_log(" code=");
+ ptr = env->segs[R_CS].base + env->eip;
+ for(i = 0; i < 16; i++) {
+ qemu_log(" %02x", ldub(ptr + i));
+ }
+ qemu_log("\n");
+ }
+#endif
+ count++;
+ }
+ }
+ if (env->cr[0] & CR0_PE_MASK) {
+#if !defined(CONFIG_USER_ONLY)
+ if (env->hflags & HF_SVMI_MASK)
+ handle_even_inj(intno, is_int, error_code, is_hw, 0);
+#endif
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ do_interrupt64(intno, is_int, error_code, next_eip, is_hw);
+ } else
+#endif
+ {
+ do_interrupt_protected(intno, is_int, error_code, next_eip, is_hw);
+ }
+ } else {
+#if !defined(CONFIG_USER_ONLY)
+ if (env->hflags & HF_SVMI_MASK)
+ handle_even_inj(intno, is_int, error_code, is_hw, 1);
+#endif
+ do_interrupt_real(intno, is_int, error_code, next_eip);
+ }
+
+#if !defined(CONFIG_USER_ONLY)
+ if (env->hflags & HF_SVMI_MASK) {
+ uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID);
+ }
+#endif
+}
+
+/* This should come from sysemu.h - if we could include it here... */
+void qemu_system_reset_request(void);
+
+/*
+ * Check nested exceptions and change to double or triple fault if
+ * needed. It should only be called, if this is not an interrupt.
+ * Returns the new exception number.
+ */
+static int check_exception(int intno, int *error_code)
+{
+ int first_contributory = env->old_exception == 0 ||
+ (env->old_exception >= 10 &&
+ env->old_exception <= 13);
+ int second_contributory = intno == 0 ||
+ (intno >= 10 && intno <= 13);
+
+ qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n",
+ env->old_exception, intno);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (env->old_exception == EXCP08_DBLE) {
+ if (env->hflags & HF_SVMI_MASK)
+ helper_vmexit(SVM_EXIT_SHUTDOWN, 0); /* does not return */
+
+ qemu_log_mask(CPU_LOG_RESET, "Triple fault\n");
+
+ qemu_system_reset_request();
+ return EXCP_HLT;
+ }
+#endif
+
+ if ((first_contributory && second_contributory)
+ || (env->old_exception == EXCP0E_PAGE &&
+ (second_contributory || (intno == EXCP0E_PAGE)))) {
+ intno = EXCP08_DBLE;
+ *error_code = 0;
+ }
+
+ if (second_contributory || (intno == EXCP0E_PAGE) ||
+ (intno == EXCP08_DBLE))
+ env->old_exception = intno;
+
+ return intno;
+}
+
+/*
+ * Signal an interruption. It is executed in the main CPU loop.
+ * is_int is TRUE if coming from the int instruction. next_eip is the
+ * EIP value AFTER the interrupt instruction. It is only relevant if
+ * is_int is TRUE.
+ */
+static void QEMU_NORETURN raise_interrupt(int intno, int is_int, int error_code,
+ int next_eip_addend)
+{
+ if (!is_int) {
+ helper_svm_check_intercept_param(SVM_EXIT_EXCP_BASE + intno, error_code);
+ intno = check_exception(intno, &error_code);
+ } else {
+ helper_svm_check_intercept_param(SVM_EXIT_SWINT, 0);
+ }
+
+ env->exception_index = intno;
+ env->error_code = error_code;
+ env->exception_is_int = is_int;
+ env->exception_next_eip = env->eip + next_eip_addend;
+ cpu_loop_exit();
+}
+
+/* shortcuts to generate exceptions */
+
+void raise_exception_err(int exception_index, int error_code)
+{
+ raise_interrupt(exception_index, 0, error_code, 0);
+}
+
+void raise_exception(int exception_index)
+{
+ raise_interrupt(exception_index, 0, 0, 0);
+}
+
+void raise_exception_env(int exception_index, CPUState *nenv)
+{
+ env = nenv;
+ raise_exception(exception_index);
+}
+/* SMM support */
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_smm_enter(void)
+{
+}
+
+void helper_rsm(void)
+{
+}
+
+#else
+
+#ifdef TARGET_X86_64
+#define SMM_REVISION_ID 0x00020064
+#else
+#define SMM_REVISION_ID 0x00020000
+#endif
+
+void do_smm_enter(void)
+{
+ target_ulong sm_state;
+ SegmentCache *dt;
+ int i, offset;
+
+ qemu_log_mask(CPU_LOG_INT, "SMM: enter\n");
+ log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
+
+ env->hflags |= HF_SMM_MASK;
+ cpu_smm_update(env);
+
+ sm_state = env->smbase + 0x8000;
+
+#ifdef TARGET_X86_64
+ for(i = 0; i < 6; i++) {
+ dt = &env->segs[i];
+ offset = 0x7e00 + i * 16;
+ stw_phys(sm_state + offset, dt->selector);
+ stw_phys(sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff);
+ stl_phys(sm_state + offset + 4, dt->limit);
+ stq_phys(sm_state + offset + 8, dt->base);
+ }
+
+ stq_phys(sm_state + 0x7e68, env->gdt.base);
+ stl_phys(sm_state + 0x7e64, env->gdt.limit);
+
+ stw_phys(sm_state + 0x7e70, env->ldt.selector);
+ stq_phys(sm_state + 0x7e78, env->ldt.base);
+ stl_phys(sm_state + 0x7e74, env->ldt.limit);
+ stw_phys(sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff);
+
+ stq_phys(sm_state + 0x7e88, env->idt.base);
+ stl_phys(sm_state + 0x7e84, env->idt.limit);
+
+ stw_phys(sm_state + 0x7e90, env->tr.selector);
+ stq_phys(sm_state + 0x7e98, env->tr.base);
+ stl_phys(sm_state + 0x7e94, env->tr.limit);
+ stw_phys(sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff);
+
+ stq_phys(sm_state + 0x7ed0, env->efer);
+
+ stq_phys(sm_state + 0x7ff8, EAX);
+ stq_phys(sm_state + 0x7ff0, ECX);
+ stq_phys(sm_state + 0x7fe8, EDX);
+ stq_phys(sm_state + 0x7fe0, EBX);
+ stq_phys(sm_state + 0x7fd8, ESP);
+ stq_phys(sm_state + 0x7fd0, EBP);
+ stq_phys(sm_state + 0x7fc8, ESI);
+ stq_phys(sm_state + 0x7fc0, EDI);
+ for(i = 8; i < 16; i++)
+ stq_phys(sm_state + 0x7ff8 - i * 8, env->regs[i]);
+ stq_phys(sm_state + 0x7f78, env->eip);
+ stl_phys(sm_state + 0x7f70, compute_eflags());
+ stl_phys(sm_state + 0x7f68, env->dr[6]);
+ stl_phys(sm_state + 0x7f60, env->dr[7]);
+
+ stl_phys(sm_state + 0x7f48, env->cr[4]);
+ stl_phys(sm_state + 0x7f50, env->cr[3]);
+ stl_phys(sm_state + 0x7f58, env->cr[0]);
+
+ stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
+ stl_phys(sm_state + 0x7f00, env->smbase);
+#else
+ stl_phys(sm_state + 0x7ffc, env->cr[0]);
+ stl_phys(sm_state + 0x7ff8, env->cr[3]);
+ stl_phys(sm_state + 0x7ff4, compute_eflags());
+ stl_phys(sm_state + 0x7ff0, env->eip);
+ stl_phys(sm_state + 0x7fec, EDI);
+ stl_phys(sm_state + 0x7fe8, ESI);
+ stl_phys(sm_state + 0x7fe4, EBP);
+ stl_phys(sm_state + 0x7fe0, ESP);
+ stl_phys(sm_state + 0x7fdc, EBX);
+ stl_phys(sm_state + 0x7fd8, EDX);
+ stl_phys(sm_state + 0x7fd4, ECX);
+ stl_phys(sm_state + 0x7fd0, EAX);
+ stl_phys(sm_state + 0x7fcc, env->dr[6]);
+ stl_phys(sm_state + 0x7fc8, env->dr[7]);
+
+ stl_phys(sm_state + 0x7fc4, env->tr.selector);
+ stl_phys(sm_state + 0x7f64, env->tr.base);
+ stl_phys(sm_state + 0x7f60, env->tr.limit);
+ stl_phys(sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff);
+
+ stl_phys(sm_state + 0x7fc0, env->ldt.selector);
+ stl_phys(sm_state + 0x7f80, env->ldt.base);
+ stl_phys(sm_state + 0x7f7c, env->ldt.limit);
+ stl_phys(sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff);
+
+ stl_phys(sm_state + 0x7f74, env->gdt.base);
+ stl_phys(sm_state + 0x7f70, env->gdt.limit);
+
+ stl_phys(sm_state + 0x7f58, env->idt.base);
+ stl_phys(sm_state + 0x7f54, env->idt.limit);
+
+ for(i = 0; i < 6; i++) {
+ dt = &env->segs[i];
+ if (i < 3)
+ offset = 0x7f84 + i * 12;
+ else
+ offset = 0x7f2c + (i - 3) * 12;
+ stl_phys(sm_state + 0x7fa8 + i * 4, dt->selector);
+ stl_phys(sm_state + offset + 8, dt->base);
+ stl_phys(sm_state + offset + 4, dt->limit);
+ stl_phys(sm_state + offset, (dt->flags >> 8) & 0xf0ff);
+ }
+ stl_phys(sm_state + 0x7f14, env->cr[4]);
+
+ stl_phys(sm_state + 0x7efc, SMM_REVISION_ID);
+ stl_phys(sm_state + 0x7ef8, env->smbase);
+#endif
+ /* init SMM cpu state */
+
+#ifdef TARGET_X86_64
+ cpu_load_efer(env, 0);
+#endif
+ load_eflags(0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
+ env->eip = 0x00008000;
+ cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase,
+ 0xffffffff, 0);
+ cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, 0);
+ cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, 0);
+ cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, 0);
+ cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, 0);
+ cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, 0);
+
+ cpu_x86_update_cr0(env,
+ env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | CR0_PG_MASK));
+ cpu_x86_update_cr4(env, 0);
+ env->dr[7] = 0x00000400;
+ CC_OP = CC_OP_EFLAGS;
+}
+
+void helper_rsm(void)
+{
+ target_ulong sm_state;
+ int i, offset;
+ uint32_t val;
+
+ sm_state = env->smbase + 0x8000;
+#ifdef TARGET_X86_64
+ cpu_load_efer(env, ldq_phys(sm_state + 0x7ed0));
+
+ for(i = 0; i < 6; i++) {
+ offset = 0x7e00 + i * 16;
+ cpu_x86_load_seg_cache(env, i,
+ lduw_phys(sm_state + offset),
+ ldq_phys(sm_state + offset + 8),
+ ldl_phys(sm_state + offset + 4),
+ (lduw_phys(sm_state + offset + 2) & 0xf0ff) << 8);
+ }
+
+ env->gdt.base = ldq_phys(sm_state + 0x7e68);
+ env->gdt.limit = ldl_phys(sm_state + 0x7e64);
+
+ env->ldt.selector = lduw_phys(sm_state + 0x7e70);
+ env->ldt.base = ldq_phys(sm_state + 0x7e78);
+ env->ldt.limit = ldl_phys(sm_state + 0x7e74);
+ env->ldt.flags = (lduw_phys(sm_state + 0x7e72) & 0xf0ff) << 8;
+
+ env->idt.base = ldq_phys(sm_state + 0x7e88);
+ env->idt.limit = ldl_phys(sm_state + 0x7e84);
+
+ env->tr.selector = lduw_phys(sm_state + 0x7e90);
+ env->tr.base = ldq_phys(sm_state + 0x7e98);
+ env->tr.limit = ldl_phys(sm_state + 0x7e94);
+ env->tr.flags = (lduw_phys(sm_state + 0x7e92) & 0xf0ff) << 8;
+
+ EAX = ldq_phys(sm_state + 0x7ff8);
+ ECX = ldq_phys(sm_state + 0x7ff0);
+ EDX = ldq_phys(sm_state + 0x7fe8);
+ EBX = ldq_phys(sm_state + 0x7fe0);
+ ESP = ldq_phys(sm_state + 0x7fd8);
+ EBP = ldq_phys(sm_state + 0x7fd0);
+ ESI = ldq_phys(sm_state + 0x7fc8);
+ EDI = ldq_phys(sm_state + 0x7fc0);
+ for(i = 8; i < 16; i++)
+ env->regs[i] = ldq_phys(sm_state + 0x7ff8 - i * 8);
+ env->eip = ldq_phys(sm_state + 0x7f78);
+ load_eflags(ldl_phys(sm_state + 0x7f70),
+ ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
+ env->dr[6] = ldl_phys(sm_state + 0x7f68);
+ env->dr[7] = ldl_phys(sm_state + 0x7f60);
+
+ cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f48));
+ cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7f50));
+ cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7f58));
+
+ val = ldl_phys(sm_state + 0x7efc); /* revision ID */
+ if (val & 0x20000) {
+ env->smbase = ldl_phys(sm_state + 0x7f00) & ~0x7fff;
+ }
+#else
+ cpu_x86_update_cr0(env, ldl_phys(sm_state + 0x7ffc));
+ cpu_x86_update_cr3(env, ldl_phys(sm_state + 0x7ff8));
+ load_eflags(ldl_phys(sm_state + 0x7ff4),
+ ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
+ env->eip = ldl_phys(sm_state + 0x7ff0);
+ EDI = ldl_phys(sm_state + 0x7fec);
+ ESI = ldl_phys(sm_state + 0x7fe8);
+ EBP = ldl_phys(sm_state + 0x7fe4);
+ ESP = ldl_phys(sm_state + 0x7fe0);
+ EBX = ldl_phys(sm_state + 0x7fdc);
+ EDX = ldl_phys(sm_state + 0x7fd8);
+ ECX = ldl_phys(sm_state + 0x7fd4);
+ EAX = ldl_phys(sm_state + 0x7fd0);
+ env->dr[6] = ldl_phys(sm_state + 0x7fcc);
+ env->dr[7] = ldl_phys(sm_state + 0x7fc8);
+
+ env->tr.selector = ldl_phys(sm_state + 0x7fc4) & 0xffff;
+ env->tr.base = ldl_phys(sm_state + 0x7f64);
+ env->tr.limit = ldl_phys(sm_state + 0x7f60);
+ env->tr.flags = (ldl_phys(sm_state + 0x7f5c) & 0xf0ff) << 8;
+
+ env->ldt.selector = ldl_phys(sm_state + 0x7fc0) & 0xffff;
+ env->ldt.base = ldl_phys(sm_state + 0x7f80);
+ env->ldt.limit = ldl_phys(sm_state + 0x7f7c);
+ env->ldt.flags = (ldl_phys(sm_state + 0x7f78) & 0xf0ff) << 8;
+
+ env->gdt.base = ldl_phys(sm_state + 0x7f74);
+ env->gdt.limit = ldl_phys(sm_state + 0x7f70);
+
+ env->idt.base = ldl_phys(sm_state + 0x7f58);
+ env->idt.limit = ldl_phys(sm_state + 0x7f54);
+
+ for(i = 0; i < 6; i++) {
+ if (i < 3)
+ offset = 0x7f84 + i * 12;
+ else
+ offset = 0x7f2c + (i - 3) * 12;
+ cpu_x86_load_seg_cache(env, i,
+ ldl_phys(sm_state + 0x7fa8 + i * 4) & 0xffff,
+ ldl_phys(sm_state + offset + 8),
+ ldl_phys(sm_state + offset + 4),
+ (ldl_phys(sm_state + offset) & 0xf0ff) << 8);
+ }
+ cpu_x86_update_cr4(env, ldl_phys(sm_state + 0x7f14));
+
+ val = ldl_phys(sm_state + 0x7efc); /* revision ID */
+ if (val & 0x20000) {
+ env->smbase = ldl_phys(sm_state + 0x7ef8) & ~0x7fff;
+ }
+#endif
+ CC_OP = CC_OP_EFLAGS;
+ env->hflags &= ~HF_SMM_MASK;
+ cpu_smm_update(env);
+
+ qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
+ log_cpu_state_mask(CPU_LOG_INT, env, X86_DUMP_CCOP);
+}
+
+#endif /* !CONFIG_USER_ONLY */
+
+
+/* division, flags are undefined */
+
+void helper_divb_AL(target_ulong t0)
+{
+ unsigned int num, den, q, r;
+
+ num = (EAX & 0xffff);
+ den = (t0 & 0xff);
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q > 0xff)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xff;
+ r = (num % den) & 0xff;
+ EAX = (EAX & ~0xffff) | (r << 8) | q;
+}
+
+void helper_idivb_AL(target_ulong t0)
+{
+ int num, den, q, r;
+
+ num = (int16_t)EAX;
+ den = (int8_t)t0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q != (int8_t)q)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xff;
+ r = (num % den) & 0xff;
+ EAX = (EAX & ~0xffff) | (r << 8) | q;
+}
+
+void helper_divw_AX(target_ulong t0)
+{
+ unsigned int num, den, q, r;
+
+ num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
+ den = (t0 & 0xffff);
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q > 0xffff)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xffff;
+ r = (num % den) & 0xffff;
+ EAX = (EAX & ~0xffff) | q;
+ EDX = (EDX & ~0xffff) | r;
+}
+
+void helper_idivw_AX(target_ulong t0)
+{
+ int num, den, q, r;
+
+ num = (EAX & 0xffff) | ((EDX & 0xffff) << 16);
+ den = (int16_t)t0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ if (q != (int16_t)q)
+ raise_exception(EXCP00_DIVZ);
+ q &= 0xffff;
+ r = (num % den) & 0xffff;
+ EAX = (EAX & ~0xffff) | q;
+ EDX = (EDX & ~0xffff) | r;
+}
+
+void helper_divl_EAX(target_ulong t0)
+{
+ unsigned int den, r;
+ uint64_t num, q;
+
+ num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+ den = t0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ r = (num % den);
+ if (q > 0xffffffff)
+ raise_exception(EXCP00_DIVZ);
+ EAX = (uint32_t)q;
+ EDX = (uint32_t)r;
+}
+
+void helper_idivl_EAX(target_ulong t0)
+{
+ int den, r;
+ int64_t num, q;
+
+ num = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+ den = t0;
+ if (den == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ q = (num / den);
+ r = (num % den);
+ if (q != (int32_t)q)
+ raise_exception(EXCP00_DIVZ);
+ EAX = (uint32_t)q;
+ EDX = (uint32_t)r;
+}
+
+/* bcd */
+
+/* XXX: exception */
+void helper_aam(int base)
+{
+ int al, ah;
+ al = EAX & 0xff;
+ ah = al / base;
+ al = al % base;
+ EAX = (EAX & ~0xffff) | al | (ah << 8);
+ CC_DST = al;
+}
+
+void helper_aad(int base)
+{
+ int al, ah;
+ al = EAX & 0xff;
+ ah = (EAX >> 8) & 0xff;
+ al = ((ah * base) + al) & 0xff;
+ EAX = (EAX & ~0xffff) | al;
+ CC_DST = al;
+}
+
+void helper_aaa(void)
+{
+ int icarry;
+ int al, ah, af;
+ int eflags;
+
+ eflags = helper_cc_compute_all(CC_OP);
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+ ah = (EAX >> 8) & 0xff;
+
+ icarry = (al > 0xf9);
+ if (((al & 0x0f) > 9 ) || af) {
+ al = (al + 6) & 0x0f;
+ ah = (ah + 1 + icarry) & 0xff;
+ eflags |= CC_C | CC_A;
+ } else {
+ eflags &= ~(CC_C | CC_A);
+ al &= 0x0f;
+ }
+ EAX = (EAX & ~0xffff) | al | (ah << 8);
+ CC_SRC = eflags;
+}
+
+void helper_aas(void)
+{
+ int icarry;
+ int al, ah, af;
+ int eflags;
+
+ eflags = helper_cc_compute_all(CC_OP);
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+ ah = (EAX >> 8) & 0xff;
+
+ icarry = (al < 6);
+ if (((al & 0x0f) > 9 ) || af) {
+ al = (al - 6) & 0x0f;
+ ah = (ah - 1 - icarry) & 0xff;
+ eflags |= CC_C | CC_A;
+ } else {
+ eflags &= ~(CC_C | CC_A);
+ al &= 0x0f;
+ }
+ EAX = (EAX & ~0xffff) | al | (ah << 8);
+ CC_SRC = eflags;
+}
+
+void helper_daa(void)
+{
+ int al, af, cf;
+ int eflags;
+
+ eflags = helper_cc_compute_all(CC_OP);
+ cf = eflags & CC_C;
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+
+ eflags = 0;
+ if (((al & 0x0f) > 9 ) || af) {
+ al = (al + 6) & 0xff;
+ eflags |= CC_A;
+ }
+ if ((al > 0x9f) || cf) {
+ al = (al + 0x60) & 0xff;
+ eflags |= CC_C;
+ }
+ EAX = (EAX & ~0xff) | al;
+ /* well, speed is not an issue here, so we compute the flags by hand */
+ eflags |= (al == 0) << 6; /* zf */
+ eflags |= parity_table[al]; /* pf */
+ eflags |= (al & 0x80); /* sf */
+ CC_SRC = eflags;
+}
+
+void helper_das(void)
+{
+ int al, al1, af, cf;
+ int eflags;
+
+ eflags = helper_cc_compute_all(CC_OP);
+ cf = eflags & CC_C;
+ af = eflags & CC_A;
+ al = EAX & 0xff;
+
+ eflags = 0;
+ al1 = al;
+ if (((al & 0x0f) > 9 ) || af) {
+ eflags |= CC_A;
+ if (al < 6 || cf)
+ eflags |= CC_C;
+ al = (al - 6) & 0xff;
+ }
+ if ((al1 > 0x99) || cf) {
+ al = (al - 0x60) & 0xff;
+ eflags |= CC_C;
+ }
+ EAX = (EAX & ~0xff) | al;
+ /* well, speed is not an issue here, so we compute the flags by hand */
+ eflags |= (al == 0) << 6; /* zf */
+ eflags |= parity_table[al]; /* pf */
+ eflags |= (al & 0x80); /* sf */
+ CC_SRC = eflags;
+}
+
+void helper_into(int next_eip_addend)
+{
+ int eflags;
+ eflags = helper_cc_compute_all(CC_OP);
+ if (eflags & CC_O) {
+ raise_interrupt(EXCP04_INTO, 1, 0, next_eip_addend);
+ }
+}
+
+void helper_cmpxchg8b(target_ulong a0)
+{
+ uint64_t d;
+ int eflags;
+
+ eflags = helper_cc_compute_all(CC_OP);
+ d = ldq(a0);
+ if (d == (((uint64_t)EDX << 32) | (uint32_t)EAX)) {
+ stq(a0, ((uint64_t)ECX << 32) | (uint32_t)EBX);
+ eflags |= CC_Z;
+ } else {
+ /* always do the store */
+ stq(a0, d);
+ EDX = (uint32_t)(d >> 32);
+ EAX = (uint32_t)d;
+ eflags &= ~CC_Z;
+ }
+ CC_SRC = eflags;
+}
+
+#ifdef TARGET_X86_64
+void helper_cmpxchg16b(target_ulong a0)
+{
+ uint64_t d0, d1;
+ int eflags;
+
+ if ((a0 & 0xf) != 0)
+ raise_exception(EXCP0D_GPF);
+ eflags = helper_cc_compute_all(CC_OP);
+ d0 = ldq(a0);
+ d1 = ldq(a0 + 8);
+ if (d0 == EAX && d1 == EDX) {
+ stq(a0, EBX);
+ stq(a0 + 8, ECX);
+ eflags |= CC_Z;
+ } else {
+ /* always do the store */
+ stq(a0, d0);
+ stq(a0 + 8, d1);
+ EDX = d1;
+ EAX = d0;
+ eflags &= ~CC_Z;
+ }
+ CC_SRC = eflags;
+}
+#endif
+
+void helper_single_step(void)
+{
+#ifndef CONFIG_USER_ONLY
+ check_hw_breakpoints(env, 1);
+ env->dr[6] |= DR6_BS;
+#endif
+ raise_exception(EXCP01_DB);
+}
+
+void helper_cpuid(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ helper_svm_check_intercept_param(SVM_EXIT_CPUID, 0);
+
+ cpu_x86_cpuid(env, (uint32_t)EAX, (uint32_t)ECX, &eax, &ebx, &ecx, &edx);
+ EAX = eax;
+ EBX = ebx;
+ ECX = ecx;
+ EDX = edx;
+}
+
+void helper_enter_level(int level, int data32, target_ulong t1)
+{
+ target_ulong ssp;
+ uint32_t esp_mask, esp, ebp;
+
+ esp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ ebp = EBP;
+ esp = ESP;
+ if (data32) {
+ /* 32 bit */
+ esp -= 4;
+ while (--level) {
+ esp -= 4;
+ ebp -= 4;
+ stl(ssp + (esp & esp_mask), ldl(ssp + (ebp & esp_mask)));
+ }
+ esp -= 4;
+ stl(ssp + (esp & esp_mask), t1);
+ } else {
+ /* 16 bit */
+ esp -= 2;
+ while (--level) {
+ esp -= 2;
+ ebp -= 2;
+ stw(ssp + (esp & esp_mask), lduw(ssp + (ebp & esp_mask)));
+ }
+ esp -= 2;
+ stw(ssp + (esp & esp_mask), t1);
+ }
+}
+
+#ifdef TARGET_X86_64
+void helper_enter64_level(int level, int data64, target_ulong t1)
+{
+ target_ulong esp, ebp;
+ ebp = EBP;
+ esp = ESP;
+
+ if (data64) {
+ /* 64 bit */
+ esp -= 8;
+ while (--level) {
+ esp -= 8;
+ ebp -= 8;
+ stq(esp, ldq(ebp));
+ }
+ esp -= 8;
+ stq(esp, t1);
+ } else {
+ /* 16 bit */
+ esp -= 2;
+ while (--level) {
+ esp -= 2;
+ ebp -= 2;
+ stw(esp, lduw(ebp));
+ }
+ esp -= 2;
+ stw(esp, t1);
+ }
+}
+#endif
+
+void helper_lldt(int selector)
+{
+ SegmentCache *dt;
+ uint32_t e1, e2;
+ int index, entry_limit;
+ target_ulong ptr;
+
+ selector &= 0xffff;
+ if ((selector & 0xfffc) == 0) {
+ /* XXX: NULL selector case: invalid LDT */
+ env->ldt.base = 0;
+ env->ldt.limit = 0;
+ } else {
+ if (selector & 0x4)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dt = &env->gdt;
+ index = selector & ~7;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ entry_limit = 15;
+ else
+#endif
+ entry_limit = 7;
+ if ((index + entry_limit) > dt->limit)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t e3;
+ e3 = ldl_kernel(ptr + 8);
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ env->ldt.base |= (target_ulong)e3 << 32;
+ } else
+#endif
+ {
+ load_seg_cache_raw_dt(&env->ldt, e1, e2);
+ }
+ }
+ env->ldt.selector = selector;
+}
+
+void helper_ltr(int selector)
+{
+ SegmentCache *dt;
+ uint32_t e1, e2;
+ int index, type, entry_limit;
+ target_ulong ptr;
+
+ selector &= 0xffff;
+ if ((selector & 0xfffc) == 0) {
+ /* NULL selector case: invalid TR */
+ env->tr.base = 0;
+ env->tr.limit = 0;
+ env->tr.flags = 0;
+ } else {
+ if (selector & 0x4)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dt = &env->gdt;
+ index = selector & ~7;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ entry_limit = 15;
+ else
+#endif
+ entry_limit = 7;
+ if ((index + entry_limit) > dt->limit)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ if ((e2 & DESC_S_MASK) ||
+ (type != 1 && type != 9))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ uint32_t e3, e4;
+ e3 = ldl_kernel(ptr + 8);
+ e4 = ldl_kernel(ptr + 12);
+ if ((e4 >> DESC_TYPE_SHIFT) & 0xf)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ load_seg_cache_raw_dt(&env->tr, e1, e2);
+ env->tr.base |= (target_ulong)e3 << 32;
+ } else
+#endif
+ {
+ load_seg_cache_raw_dt(&env->tr, e1, e2);
+ }
+ e2 |= DESC_TSS_BUSY_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+ env->tr.selector = selector;
+}
+
+/* only works if protected mode and not VM86. seg_reg must be != R_CS */
+void helper_load_seg(int seg_reg, int selector)
+{
+ uint32_t e1, e2;
+ int cpl, dpl, rpl;
+ SegmentCache *dt;
+ int index;
+ target_ulong ptr;
+
+ selector &= 0xffff;
+ cpl = env->hflags & HF_CPL_MASK;
+ if ((selector & 0xfffc) == 0) {
+ /* null selector case */
+ if (seg_reg == R_SS
+#ifdef TARGET_X86_64
+ && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
+#endif
+ )
+ raise_exception_err(EXCP0D_GPF, 0);
+ cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
+ } else {
+
+ if (selector & 0x4)
+ dt = &env->ldt;
+ else
+ dt = &env->gdt;
+ index = selector & ~7;
+ if ((index + 7) > dt->limit)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ ptr = dt->base + index;
+ e1 = ldl_kernel(ptr);
+ e2 = ldl_kernel(ptr + 4);
+
+ if (!(e2 & DESC_S_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (seg_reg == R_SS) {
+ /* must be writable segment */
+ if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (rpl != cpl || dpl != cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ } else {
+ /* must be readable segment */
+ if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+
+ if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
+ /* if not conforming code, test rights */
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ }
+ }
+
+ if (!(e2 & DESC_P_MASK)) {
+ if (seg_reg == R_SS)
+ raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
+ else
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+ }
+
+ /* set the access bit if not already set */
+ if (!(e2 & DESC_A_MASK)) {
+ e2 |= DESC_A_MASK;
+ stl_kernel(ptr + 4, e2);
+ }
+
+ cpu_x86_load_seg_cache(env, seg_reg, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+#if 0
+ qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
+ selector, (unsigned long)sc->base, sc->limit, sc->flags);
+#endif
+ }
+}
+
+/* protected mode jump */
+void helper_ljmp_protected(int new_cs, target_ulong new_eip,
+ int next_eip_addend)
+{
+ int gate_cs, type;
+ uint32_t e1, e2, cpl, dpl, rpl, limit;
+ target_ulong next_eip;
+
+ if ((new_cs & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+ if (load_segment(&e1, &e2, new_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_S_MASK) {
+ if (!(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (e2 & DESC_C_MASK) {
+ /* conforming code segment */
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ } else {
+ /* non conforming code segment */
+ rpl = new_cs & 3;
+ if (rpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (dpl != cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit &&
+ !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
+ } else {
+ /* jump to call or task gate */
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ rpl = new_cs & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ switch(type) {
+ case 1: /* 286 TSS */
+ case 9: /* 386 TSS */
+ case 5: /* task gate */
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ next_eip = env->eip + next_eip_addend;
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
+ CC_OP = CC_OP_EFLAGS;
+ break;
+ case 4: /* 286 call gate */
+ case 12: /* 386 call gate */
+ if ((dpl < cpl) || (dpl < rpl))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ gate_cs = e1 >> 16;
+ new_eip = (e1 & 0xffff);
+ if (type == 12)
+ new_eip |= (e2 & 0xffff0000);
+ if (load_segment(&e1, &e2, gate_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ /* must be code segment */
+ if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
+ (DESC_S_MASK | DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
+ (!(e2 & DESC_C_MASK) && (dpl != cpl)))
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0D_GPF, gate_cs & 0xfffc);
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit)
+ raise_exception_err(EXCP0D_GPF, 0);
+ cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ break;
+ }
+ }
+}
+
+/* real mode call */
+void helper_lcall_real(int new_cs, target_ulong new_eip1,
+ int shift, int next_eip)
+{
+ int new_eip;
+ uint32_t esp, esp_mask;
+ target_ulong ssp;
+
+ new_eip = new_eip1;
+ esp = ESP;
+ esp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ if (shift) {
+ PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, esp, esp_mask, next_eip);
+ } else {
+ PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, esp, esp_mask, next_eip);
+ }
+
+ SET_ESP(esp, esp_mask);
+ env->eip = new_eip;
+ env->segs[R_CS].selector = new_cs;
+ env->segs[R_CS].base = (new_cs << 4);
+}
+
+/* protected mode call */
+void helper_lcall_protected(int new_cs, target_ulong new_eip,
+ int shift, int next_eip_addend)
+{
+ int new_stack, i;
+ uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
+ uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask;
+ uint32_t val, limit, old_sp_mask;
+ target_ulong ssp, old_ssp, next_eip;
+
+ next_eip = env->eip + next_eip_addend;
+ LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift);
+ LOG_PCALL_STATE(env);
+ if ((new_cs & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+ if (load_segment(&e1, &e2, new_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpl = env->hflags & HF_CPL_MASK;
+ LOG_PCALL("desc=%08x:%08x\n", e1, e2);
+ if (e2 & DESC_S_MASK) {
+ if (!(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (e2 & DESC_C_MASK) {
+ /* conforming code segment */
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ } else {
+ /* non conforming code segment */
+ rpl = new_cs & 3;
+ if (rpl > cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (dpl != cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+
+#ifdef TARGET_X86_64
+ /* XXX: check 16/32 bit cases in long mode */
+ if (shift == 2) {
+ target_ulong rsp;
+ /* 64 bit case */
+ rsp = ESP;
+ PUSHQ(rsp, env->segs[R_CS].selector);
+ PUSHQ(rsp, next_eip);
+ /* from this point, not restartable */
+ ESP = rsp;
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2), e2);
+ EIP = new_eip;
+ } else
+#endif
+ {
+ sp = ESP;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
+ }
+
+ limit = get_seg_limit(e1, e2);
+ if (new_eip > limit)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ /* from this point, not restartable */
+ SET_ESP(sp, sp_mask);
+ cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
+ get_seg_base(e1, e2), limit, e2);
+ EIP = new_eip;
+ }
+ } else {
+ /* check gate type */
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ rpl = new_cs & 3;
+ switch(type) {
+ case 1: /* available 286 TSS */
+ case 9: /* available 386 TSS */
+ case 5: /* task gate */
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
+ CC_OP = CC_OP_EFLAGS;
+ return;
+ case 4: /* 286 call gate */
+ case 12: /* 386 call gate */
+ break;
+ default:
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ break;
+ }
+ shift = type >> 3;
+
+ if (dpl < cpl || dpl < rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ /* check valid bit */
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ selector = e1 >> 16;
+ offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
+ param_count = e2 & 0x1f;
+ if ((selector & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, 0);
+
+ if (load_segment(&e1, &e2, selector) != 0)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl > cpl)
+ raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
+
+ if (!(e2 & DESC_C_MASK) && dpl < cpl) {
+ /* to inner privilege */
+ get_ss_esp_from_tss(&ss, &sp, dpl);
+ LOG_PCALL("new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
+ ss, sp, param_count, ESP);
+ if ((ss & 0xfffc) == 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if ((ss & 3) != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, ss) != 0)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (ss_dpl != dpl)
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
+
+ // push_size = ((param_count * 2) + 8) << shift;
+
+ old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ old_ssp = env->segs[R_SS].base;
+
+ sp_mask = get_sp_mask(ss_e2);
+ ssp = get_seg_base(ss_e1, ss_e2);
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
+ PUSHL(ssp, sp, sp_mask, ESP);
+ for(i = param_count - 1; i >= 0; i--) {
+ val = ldl_kernel(old_ssp + ((ESP + i * 4) & old_sp_mask));
+ PUSHL(ssp, sp, sp_mask, val);
+ }
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
+ PUSHW(ssp, sp, sp_mask, ESP);
+ for(i = param_count - 1; i >= 0; i--) {
+ val = lduw_kernel(old_ssp + ((ESP + i * 2) & old_sp_mask));
+ PUSHW(ssp, sp, sp_mask, val);
+ }
+ }
+ new_stack = 1;
+ } else {
+ /* to same privilege */
+ sp = ESP;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ // push_size = (4 << shift);
+ new_stack = 0;
+ }
+
+ if (shift) {
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
+ } else {
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
+ }
+
+ /* from this point, not restartable */
+
+ if (new_stack) {
+ ss = (ss & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss,
+ ssp,
+ get_seg_limit(ss_e1, ss_e2),
+ ss_e2);
+ }
+
+ selector = (selector & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_CS, selector,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, dpl);
+ SET_ESP(sp, sp_mask);
+ EIP = offset;
+ }
+}
+
+/* real and vm86 mode iret */
+void helper_iret_real(int shift)
+{
+ uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
+ target_ulong ssp;
+ int eflags_mask;
+
+ sp_mask = 0xffff; /* XXXX: use SS segment size ? */
+ sp = ESP;
+ ssp = env->segs[R_SS].base;
+ if (shift == 1) {
+ /* 32 bits */
+ POPL(ssp, sp, sp_mask, new_eip);
+ POPL(ssp, sp, sp_mask, new_cs);
+ new_cs &= 0xffff;
+ POPL(ssp, sp, sp_mask, new_eflags);
+ } else {
+ /* 16 bits */
+ POPW(ssp, sp, sp_mask, new_eip);
+ POPW(ssp, sp, sp_mask, new_cs);
+ POPW(ssp, sp, sp_mask, new_eflags);
+ }
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
+ env->segs[R_CS].selector = new_cs;
+ env->segs[R_CS].base = (new_cs << 4);
+ env->eip = new_eip;
+ if (env->eflags & VM_MASK)
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
+ else
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
+ if (shift == 0)
+ eflags_mask &= 0xffff;
+ load_eflags(new_eflags, eflags_mask);
+ env->hflags2 &= ~HF2_NMI_MASK;
+}
+
+static inline void validate_seg(int seg_reg, int cpl)
+{
+ int dpl;
+ uint32_t e2;
+
+ /* XXX: on x86_64, we do not want to nullify FS and GS because
+ they may still contain a valid base. I would be interested to
+ know how a real x86_64 CPU behaves */
+ if ((seg_reg == R_FS || seg_reg == R_GS) &&
+ (env->segs[seg_reg].selector & 0xfffc) == 0)
+ return;
+
+ e2 = env->segs[seg_reg].flags;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
+ /* data or non conforming code segment */
+ if (dpl < cpl) {
+ cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
+ }
+ }
+}
+
+/* protected mode iret */
+static inline void helper_ret_protected(int shift, int is_iret, int addend)
+{
+ uint32_t new_cs, new_eflags, new_ss;
+ uint32_t new_es, new_ds, new_fs, new_gs;
+ uint32_t e1, e2, ss_e1, ss_e2;
+ int cpl, dpl, rpl, eflags_mask, iopl;
+ target_ulong ssp, sp, new_eip, new_esp, sp_mask;
+
+#ifdef TARGET_X86_64
+ if (shift == 2)
+ sp_mask = -1;
+ else
+#endif
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ sp = ESP;
+ ssp = env->segs[R_SS].base;
+ new_eflags = 0; /* avoid warning */
+#ifdef TARGET_X86_64
+ if (shift == 2) {
+ POPQ(sp, new_eip);
+ POPQ(sp, new_cs);
+ new_cs &= 0xffff;
+ if (is_iret) {
+ POPQ(sp, new_eflags);
+ }
+ } else
+#endif
+ if (shift == 1) {
+ /* 32 bits */
+ POPL(ssp, sp, sp_mask, new_eip);
+ POPL(ssp, sp, sp_mask, new_cs);
+ new_cs &= 0xffff;
+ if (is_iret) {
+ POPL(ssp, sp, sp_mask, new_eflags);
+ if (new_eflags & VM_MASK)
+ goto return_to_vm86;
+ }
+ } else {
+ /* 16 bits */
+ POPW(ssp, sp, sp_mask, new_eip);
+ POPW(ssp, sp, sp_mask, new_cs);
+ if (is_iret)
+ POPW(ssp, sp, sp_mask, new_eflags);
+ }
+ LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
+ new_cs, new_eip, shift, addend);
+ LOG_PCALL_STATE(env);
+ if ((new_cs & 0xfffc) == 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (load_segment(&e1, &e2, new_cs) != 0)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ if (!(e2 & DESC_S_MASK) ||
+ !(e2 & DESC_CS_MASK))
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ cpl = env->hflags & HF_CPL_MASK;
+ rpl = new_cs & 3;
+ if (rpl < cpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ if (e2 & DESC_C_MASK) {
+ if (dpl > rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ } else {
+ if (dpl != rpl)
+ raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
+ }
+ if (!(e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+
+ sp += addend;
+ if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
+ ((env->hflags & HF_CS64_MASK) && !is_iret))) {
+ /* return to same privilege level */
+ cpu_x86_load_seg_cache(env, R_CS, new_cs,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ } else {
+ /* return to different privilege level */
+#ifdef TARGET_X86_64
+ if (shift == 2) {
+ POPQ(sp, new_esp);
+ POPQ(sp, new_ss);
+ new_ss &= 0xffff;
+ } else
+#endif
+ if (shift == 1) {
+ /* 32 bits */
+ POPL(ssp, sp, sp_mask, new_esp);
+ POPL(ssp, sp, sp_mask, new_ss);
+ new_ss &= 0xffff;
+ } else {
+ /* 16 bits */
+ POPW(ssp, sp, sp_mask, new_esp);
+ POPW(ssp, sp, sp_mask, new_ss);
+ }
+ LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
+ new_ss, new_esp);
+ if ((new_ss & 0xfffc) == 0) {
+#ifdef TARGET_X86_64
+ /* NULL ss is allowed in long mode if cpl != 3*/
+ /* XXX: test CS64 ? */
+ if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
+ cpu_x86_load_seg_cache(env, R_SS, new_ss,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ ss_e2 = DESC_B_MASK; /* XXX: should not be needed ? */
+ } else
+#endif
+ {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ } else {
+ if ((new_ss & 3) != rpl)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (!(ss_e2 & DESC_S_MASK) ||
+ (ss_e2 & DESC_CS_MASK) ||
+ !(ss_e2 & DESC_W_MASK))
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
+ if (dpl != rpl)
+ raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
+ if (!(ss_e2 & DESC_P_MASK))
+ raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
+ cpu_x86_load_seg_cache(env, R_SS, new_ss,
+ get_seg_base(ss_e1, ss_e2),
+ get_seg_limit(ss_e1, ss_e2),
+ ss_e2);
+ }
+
+ cpu_x86_load_seg_cache(env, R_CS, new_cs,
+ get_seg_base(e1, e2),
+ get_seg_limit(e1, e2),
+ e2);
+ cpu_x86_set_cpl(env, rpl);
+ sp = new_esp;
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_CS64_MASK)
+ sp_mask = -1;
+ else
+#endif
+ sp_mask = get_sp_mask(ss_e2);
+
+ /* validate data segments */
+ validate_seg(R_ES, rpl);
+ validate_seg(R_DS, rpl);
+ validate_seg(R_FS, rpl);
+ validate_seg(R_GS, rpl);
+
+ sp += addend;
+ }
+ SET_ESP(sp, sp_mask);
+ env->eip = new_eip;
+ if (is_iret) {
+ /* NOTE: 'cpl' is the _old_ CPL */
+ eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
+ if (cpl == 0)
+ eflags_mask |= IOPL_MASK;
+ iopl = (env->eflags >> IOPL_SHIFT) & 3;
+ if (cpl <= iopl)
+ eflags_mask |= IF_MASK;
+ if (shift == 0)
+ eflags_mask &= 0xffff;
+ load_eflags(new_eflags, eflags_mask);
+ }
+ return;
+
+ return_to_vm86:
+ POPL(ssp, sp, sp_mask, new_esp);
+ POPL(ssp, sp, sp_mask, new_ss);
+ POPL(ssp, sp, sp_mask, new_es);
+ POPL(ssp, sp, sp_mask, new_ds);
+ POPL(ssp, sp, sp_mask, new_fs);
+ POPL(ssp, sp, sp_mask, new_gs);
+
+ /* modify processor state */
+ load_eflags(new_eflags, TF_MASK | AC_MASK | ID_MASK |
+ IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
+ load_seg_vm(R_CS, new_cs & 0xffff);
+ cpu_x86_set_cpl(env, 3);
+ load_seg_vm(R_SS, new_ss & 0xffff);
+ load_seg_vm(R_ES, new_es & 0xffff);
+ load_seg_vm(R_DS, new_ds & 0xffff);
+ load_seg_vm(R_FS, new_fs & 0xffff);
+ load_seg_vm(R_GS, new_gs & 0xffff);
+
+ env->eip = new_eip & 0xffff;
+ ESP = new_esp;
+}
+
+void helper_iret_protected(int shift, int next_eip)
+{
+ int tss_selector, type;
+ uint32_t e1, e2;
+
+ /* specific case for TSS */
+ if (env->eflags & NT_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK)
+ raise_exception_err(EXCP0D_GPF, 0);
+#endif
+ tss_selector = lduw_kernel(env->tr.base + 0);
+ if (tss_selector & 4)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ if (load_segment(&e1, &e2, tss_selector) != 0)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
+ /* NOTE: we check both segment and busy TSS */
+ if (type != 3)
+ raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
+ switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
+ } else {
+ helper_ret_protected(shift, 1, 0);
+ }
+ env->hflags2 &= ~HF2_NMI_MASK;
+}
+
+void helper_lret_protected(int shift, int addend)
+{
+ helper_ret_protected(shift, 0, addend);
+}
+
+void helper_sysenter(void)
+{
+ if (env->sysenter_cs == 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
+ cpu_x86_set_cpl(env, 0);
+
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+ } else
+#endif
+ {
+ cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ }
+ cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK |
+ DESC_W_MASK | DESC_A_MASK);
+ ESP = env->sysenter_esp;
+ EIP = env->sysenter_eip;
+}
+
+void helper_sysexit(int dflag)
+{
+ int cpl;
+
+ cpl = env->hflags & HF_CPL_MASK;
+ if (env->sysenter_cs == 0 || cpl != 0) {
+ raise_exception_err(EXCP0D_GPF, 0);
+ }
+ cpu_x86_set_cpl(env, 3);
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ } else
+#endif
+ {
+ cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+ cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
+ 0, 0xffffffff,
+ DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+ DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
+ DESC_W_MASK | DESC_A_MASK);
+ }
+ ESP = ECX;
+ EIP = EDX;
+}
+
+#if defined(CONFIG_USER_ONLY)
+target_ulong helper_read_crN(int reg)
+{
+ return 0;
+}
+
+void helper_write_crN(int reg, target_ulong t0)
+{
+}
+
+void helper_movl_drN_T0(int reg, target_ulong t0)
+{
+}
+#else
+target_ulong helper_read_crN(int reg)
+{
+ target_ulong val;
+
+ helper_svm_check_intercept_param(SVM_EXIT_READ_CR0 + reg, 0);
+ switch(reg) {
+ default:
+ val = env->cr[reg];
+ break;
+ case 8:
+ if (!(env->hflags2 & HF2_VINTR_MASK)) {
+ val = cpu_get_apic_tpr(env->apic_state);
+ } else {
+ val = env->v_tpr;
+ }
+ break;
+ }
+ return val;
+}
+
+void helper_write_crN(int reg, target_ulong t0)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_WRITE_CR0 + reg, 0);
+ switch(reg) {
+ case 0:
+ cpu_x86_update_cr0(env, t0);
+ break;
+ case 3:
+ cpu_x86_update_cr3(env, t0);
+ break;
+ case 4:
+ cpu_x86_update_cr4(env, t0);
+ break;
+ case 8:
+ if (!(env->hflags2 & HF2_VINTR_MASK)) {
+ cpu_set_apic_tpr(env->apic_state, t0);
+ }
+ env->v_tpr = t0 & 0x0f;
+ break;
+ default:
+ env->cr[reg] = t0;
+ break;
+ }
+}
+
+void helper_movl_drN_T0(int reg, target_ulong t0)
+{
+ int i;
+
+ if (reg < 4) {
+ hw_breakpoint_remove(env, reg);
+ env->dr[reg] = t0;
+ hw_breakpoint_insert(env, reg);
+ } else if (reg == 7) {
+ for (i = 0; i < 4; i++)
+ hw_breakpoint_remove(env, i);
+ env->dr[7] = t0;
+ for (i = 0; i < 4; i++)
+ hw_breakpoint_insert(env, i);
+ } else
+ env->dr[reg] = t0;
+}
+#endif
+
+void helper_lmsw(target_ulong t0)
+{
+ /* only 4 lower bits of CR0 are modified. PE cannot be set to zero
+ if already set to one. */
+ t0 = (env->cr[0] & ~0xe) | (t0 & 0xf);
+ helper_write_crN(0, t0);
+}
+
+void helper_clts(void)
+{
+ env->cr[0] &= ~CR0_TS_MASK;
+ env->hflags &= ~HF_TS_MASK;
+}
+
+void helper_invlpg(target_ulong addr)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_INVLPG, 0);
+ tlb_flush_page(env, addr);
+}
+
+void helper_rdtsc(void)
+{
+ uint64_t val;
+
+ if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
+ raise_exception(EXCP0D_GPF);
+ }
+ helper_svm_check_intercept_param(SVM_EXIT_RDTSC, 0);
+
+ val = cpu_get_tsc(env) + env->tsc_offset;
+ EAX = (uint32_t)(val);
+ EDX = (uint32_t)(val >> 32);
+}
+
+void helper_rdtscp(void)
+{
+ helper_rdtsc();
+ ECX = (uint32_t)(env->tsc_aux);
+}
+
+void helper_rdpmc(void)
+{
+ if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
+ raise_exception(EXCP0D_GPF);
+ }
+ helper_svm_check_intercept_param(SVM_EXIT_RDPMC, 0);
+
+ /* currently unimplemented */
+ raise_exception_err(EXCP06_ILLOP, 0);
+}
+
+#if defined(CONFIG_USER_ONLY)
+void helper_wrmsr(void)
+{
+}
+
+void helper_rdmsr(void)
+{
+}
+#else
+void helper_wrmsr(void)
+{
+ uint64_t val;
+
+ helper_svm_check_intercept_param(SVM_EXIT_MSR, 1);
+
+ val = ((uint32_t)EAX) | ((uint64_t)((uint32_t)EDX) << 32);
+
+ switch((uint32_t)ECX) {
+ case MSR_IA32_SYSENTER_CS:
+ env->sysenter_cs = val & 0xffff;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ env->sysenter_esp = val;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ env->sysenter_eip = val;
+ break;
+ case MSR_IA32_APICBASE:
+ cpu_set_apic_base(env->apic_state, val);
+ break;
+ case MSR_EFER:
+ {
+ uint64_t update_mask;
+ update_mask = 0;
+ if (env->cpuid_ext2_features & CPUID_EXT2_SYSCALL)
+ update_mask |= MSR_EFER_SCE;
+ if (env->cpuid_ext2_features & CPUID_EXT2_LM)
+ update_mask |= MSR_EFER_LME;
+ if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
+ update_mask |= MSR_EFER_FFXSR;
+ if (env->cpuid_ext2_features & CPUID_EXT2_NX)
+ update_mask |= MSR_EFER_NXE;
+ if (env->cpuid_ext3_features & CPUID_EXT3_SVM)
+ update_mask |= MSR_EFER_SVME;
+ if (env->cpuid_ext2_features & CPUID_EXT2_FFXSR)
+ update_mask |= MSR_EFER_FFXSR;
+ cpu_load_efer(env, (env->efer & ~update_mask) |
+ (val & update_mask));
+ }
+ break;
+ case MSR_STAR:
+ env->star = val;
+ break;
+ case MSR_PAT:
+ env->pat = val;
+ break;
+ case MSR_VM_HSAVE_PA:
+ env->vm_hsave = val;
+ break;
+#ifdef TARGET_X86_64
+ case MSR_LSTAR:
+ env->lstar = val;
+ break;
+ case MSR_CSTAR:
+ env->cstar = val;
+ break;
+ case MSR_FMASK:
+ env->fmask = val;
+ break;
+ case MSR_FSBASE:
+ env->segs[R_FS].base = val;
+ break;
+ case MSR_GSBASE:
+ env->segs[R_GS].base = val;
+ break;
+ case MSR_KERNELGSBASE:
+ env->kernelgsbase = val;
+ break;
+#endif
+ case MSR_MTRRphysBase(0):
+ case MSR_MTRRphysBase(1):
+ case MSR_MTRRphysBase(2):
+ case MSR_MTRRphysBase(3):
+ case MSR_MTRRphysBase(4):
+ case MSR_MTRRphysBase(5):
+ case MSR_MTRRphysBase(6):
+ case MSR_MTRRphysBase(7):
+ env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base = val;
+ break;
+ case MSR_MTRRphysMask(0):
+ case MSR_MTRRphysMask(1):
+ case MSR_MTRRphysMask(2):
+ case MSR_MTRRphysMask(3):
+ case MSR_MTRRphysMask(4):
+ case MSR_MTRRphysMask(5):
+ case MSR_MTRRphysMask(6):
+ case MSR_MTRRphysMask(7):
+ env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask = val;
+ break;
+ case MSR_MTRRfix64K_00000:
+ env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix64K_00000] = val;
+ break;
+ case MSR_MTRRfix16K_80000:
+ case MSR_MTRRfix16K_A0000:
+ env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1] = val;
+ break;
+ case MSR_MTRRfix4K_C0000:
+ case MSR_MTRRfix4K_C8000:
+ case MSR_MTRRfix4K_D0000:
+ case MSR_MTRRfix4K_D8000:
+ case MSR_MTRRfix4K_E0000:
+ case MSR_MTRRfix4K_E8000:
+ case MSR_MTRRfix4K_F0000:
+ case MSR_MTRRfix4K_F8000:
+ env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3] = val;
+ break;
+ case MSR_MTRRdefType:
+ env->mtrr_deftype = val;
+ break;
+ case MSR_MCG_STATUS:
+ env->mcg_status = val;
+ break;
+ case MSR_MCG_CTL:
+ if ((env->mcg_cap & MCG_CTL_P)
+ && (val == 0 || val == ~(uint64_t)0))
+ env->mcg_ctl = val;
+ break;
+ case MSR_TSC_AUX:
+ env->tsc_aux = val;
+ break;
+ default:
+ if ((uint32_t)ECX >= MSR_MC0_CTL
+ && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
+ uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
+ if ((offset & 0x3) != 0
+ || (val == 0 || val == ~(uint64_t)0))
+ env->mce_banks[offset] = val;
+ break;
+ }
+ /* XXX: exception ? */
+ break;
+ }
+}
+
+void helper_rdmsr(void)
+{
+ uint64_t val;
+
+ helper_svm_check_intercept_param(SVM_EXIT_MSR, 0);
+
+ switch((uint32_t)ECX) {
+ case MSR_IA32_SYSENTER_CS:
+ val = env->sysenter_cs;
+ break;
+ case MSR_IA32_SYSENTER_ESP:
+ val = env->sysenter_esp;
+ break;
+ case MSR_IA32_SYSENTER_EIP:
+ val = env->sysenter_eip;
+ break;
+ case MSR_IA32_APICBASE:
+ val = cpu_get_apic_base(env->apic_state);
+ break;
+ case MSR_EFER:
+ val = env->efer;
+ break;
+ case MSR_STAR:
+ val = env->star;
+ break;
+ case MSR_PAT:
+ val = env->pat;
+ break;
+ case MSR_VM_HSAVE_PA:
+ val = env->vm_hsave;
+ break;
+ case MSR_IA32_PERF_STATUS:
+ /* tsc_increment_by_tick */
+ val = 1000ULL;
+ /* CPU multiplier */
+ val |= (((uint64_t)4ULL) << 40);
+ break;
+#ifdef TARGET_X86_64
+ case MSR_LSTAR:
+ val = env->lstar;
+ break;
+ case MSR_CSTAR:
+ val = env->cstar;
+ break;
+ case MSR_FMASK:
+ val = env->fmask;
+ break;
+ case MSR_FSBASE:
+ val = env->segs[R_FS].base;
+ break;
+ case MSR_GSBASE:
+ val = env->segs[R_GS].base;
+ break;
+ case MSR_KERNELGSBASE:
+ val = env->kernelgsbase;
+ break;
+ case MSR_TSC_AUX:
+ val = env->tsc_aux;
+ break;
+#endif
+ case MSR_MTRRphysBase(0):
+ case MSR_MTRRphysBase(1):
+ case MSR_MTRRphysBase(2):
+ case MSR_MTRRphysBase(3):
+ case MSR_MTRRphysBase(4):
+ case MSR_MTRRphysBase(5):
+ case MSR_MTRRphysBase(6):
+ case MSR_MTRRphysBase(7):
+ val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysBase(0)) / 2].base;
+ break;
+ case MSR_MTRRphysMask(0):
+ case MSR_MTRRphysMask(1):
+ case MSR_MTRRphysMask(2):
+ case MSR_MTRRphysMask(3):
+ case MSR_MTRRphysMask(4):
+ case MSR_MTRRphysMask(5):
+ case MSR_MTRRphysMask(6):
+ case MSR_MTRRphysMask(7):
+ val = env->mtrr_var[((uint32_t)ECX - MSR_MTRRphysMask(0)) / 2].mask;
+ break;
+ case MSR_MTRRfix64K_00000:
+ val = env->mtrr_fixed[0];
+ break;
+ case MSR_MTRRfix16K_80000:
+ case MSR_MTRRfix16K_A0000:
+ val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix16K_80000 + 1];
+ break;
+ case MSR_MTRRfix4K_C0000:
+ case MSR_MTRRfix4K_C8000:
+ case MSR_MTRRfix4K_D0000:
+ case MSR_MTRRfix4K_D8000:
+ case MSR_MTRRfix4K_E0000:
+ case MSR_MTRRfix4K_E8000:
+ case MSR_MTRRfix4K_F0000:
+ case MSR_MTRRfix4K_F8000:
+ val = env->mtrr_fixed[(uint32_t)ECX - MSR_MTRRfix4K_C0000 + 3];
+ break;
+ case MSR_MTRRdefType:
+ val = env->mtrr_deftype;
+ break;
+ case MSR_MTRRcap:
+ if (env->cpuid_features & CPUID_MTRR)
+ val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | MSR_MTRRcap_WC_SUPPORTED;
+ else
+ /* XXX: exception ? */
+ val = 0;
+ break;
+ case MSR_MCG_CAP:
+ val = env->mcg_cap;
+ break;
+ case MSR_MCG_CTL:
+ if (env->mcg_cap & MCG_CTL_P)
+ val = env->mcg_ctl;
+ else
+ val = 0;
+ break;
+ case MSR_MCG_STATUS:
+ val = env->mcg_status;
+ break;
+ default:
+ if ((uint32_t)ECX >= MSR_MC0_CTL
+ && (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
+ uint32_t offset = (uint32_t)ECX - MSR_MC0_CTL;
+ val = env->mce_banks[offset];
+ break;
+ }
+ /* XXX: exception ? */
+ val = 0;
+ break;
+ }
+ EAX = (uint32_t)(val);
+ EDX = (uint32_t)(val >> 32);
+}
+#endif
+
+target_ulong helper_lsl(target_ulong selector1)
+{
+ unsigned int limit;
+ uint32_t e1, e2, eflags, selector;
+ int rpl, dpl, cpl, type;
+
+ selector = selector1 & 0xffff;
+ eflags = helper_cc_compute_all(CC_OP);
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_S_MASK) {
+ if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
+ /* conforming */
+ } else {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ }
+ } else {
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ switch(type) {
+ case 1:
+ case 2:
+ case 3:
+ case 9:
+ case 11:
+ break;
+ default:
+ goto fail;
+ }
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return 0;
+ }
+ }
+ limit = get_seg_limit(e1, e2);
+ CC_SRC = eflags | CC_Z;
+ return limit;
+}
+
+target_ulong helper_lar(target_ulong selector1)
+{
+ uint32_t e1, e2, eflags, selector;
+ int rpl, dpl, cpl, type;
+
+ selector = selector1 & 0xffff;
+ eflags = helper_cc_compute_all(CC_OP);
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_S_MASK) {
+ if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
+ /* conforming */
+ } else {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ }
+ } else {
+ type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
+ switch(type) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 9:
+ case 11:
+ case 12:
+ break;
+ default:
+ goto fail;
+ }
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return 0;
+ }
+ }
+ CC_SRC = eflags | CC_Z;
+ return e2 & 0x00f0ff00;
+}
+
+void helper_verr(target_ulong selector1)
+{
+ uint32_t e1, e2, eflags, selector;
+ int rpl, dpl, cpl;
+
+ selector = selector1 & 0xffff;
+ eflags = helper_cc_compute_all(CC_OP);
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ if (!(e2 & DESC_S_MASK))
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_CS_MASK) {
+ if (!(e2 & DESC_R_MASK))
+ goto fail;
+ if (!(e2 & DESC_C_MASK)) {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ }
+ } else {
+ if (dpl < cpl || dpl < rpl) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return;
+ }
+ }
+ CC_SRC = eflags | CC_Z;
+}
+
+void helper_verw(target_ulong selector1)
+{
+ uint32_t e1, e2, eflags, selector;
+ int rpl, dpl, cpl;
+
+ selector = selector1 & 0xffff;
+ eflags = helper_cc_compute_all(CC_OP);
+ if ((selector & 0xfffc) == 0)
+ goto fail;
+ if (load_segment(&e1, &e2, selector) != 0)
+ goto fail;
+ if (!(e2 & DESC_S_MASK))
+ goto fail;
+ rpl = selector & 3;
+ dpl = (e2 >> DESC_DPL_SHIFT) & 3;
+ cpl = env->hflags & HF_CPL_MASK;
+ if (e2 & DESC_CS_MASK) {
+ goto fail;
+ } else {
+ if (dpl < cpl || dpl < rpl)
+ goto fail;
+ if (!(e2 & DESC_W_MASK)) {
+ fail:
+ CC_SRC = eflags & ~CC_Z;
+ return;
+ }
+ }
+ CC_SRC = eflags | CC_Z;
+}
+
+/* x87 FPU helpers */
+
+static void fpu_set_exception(int mask)
+{
+ env->fpus |= mask;
+ if (env->fpus & (~env->fpuc & FPUC_EM))
+ env->fpus |= FPUS_SE | FPUS_B;
+}
+
+static inline CPU86_LDouble helper_fdiv(CPU86_LDouble a, CPU86_LDouble b)
+{
+ if (b == 0.0)
+ fpu_set_exception(FPUS_ZE);
+ return a / b;
+}
+
+static void fpu_raise_exception(void)
+{
+ if (env->cr[0] & CR0_NE_MASK) {
+ raise_exception(EXCP10_COPR);
+ }
+#if !defined(CONFIG_USER_ONLY)
+ else {
+ cpu_set_ferr(env);
+ }
+#endif
+}
+
+void helper_flds_FT0(uint32_t val)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = val;
+ FT0 = float32_to_floatx(u.f, &env->fp_status);
+}
+
+void helper_fldl_FT0(uint64_t val)
+{
+ union {
+ float64 f;
+ uint64_t i;
+ } u;
+ u.i = val;
+ FT0 = float64_to_floatx(u.f, &env->fp_status);
+}
+
+void helper_fildl_FT0(int32_t val)
+{
+ FT0 = int32_to_floatx(val, &env->fp_status);
+}
+
+void helper_flds_ST0(uint32_t val)
+{
+ int new_fpstt;
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ new_fpstt = (env->fpstt - 1) & 7;
+ u.i = val;
+ env->fpregs[new_fpstt].d = float32_to_floatx(u.f, &env->fp_status);
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void helper_fldl_ST0(uint64_t val)
+{
+ int new_fpstt;
+ union {
+ float64 f;
+ uint64_t i;
+ } u;
+ new_fpstt = (env->fpstt - 1) & 7;
+ u.i = val;
+ env->fpregs[new_fpstt].d = float64_to_floatx(u.f, &env->fp_status);
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+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->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+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->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+uint32_t helper_fsts_ST0(void)
+{
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = floatx_to_float32(ST0, &env->fp_status);
+ return u.i;
+}
+
+uint64_t helper_fstl_ST0(void)
+{
+ union {
+ float64 f;
+ uint64_t i;
+ } u;
+ u.f = floatx_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);
+ if (val != (int16_t)val)
+ val = -32768;
+ return val;
+}
+
+int32_t helper_fistl_ST0(void)
+{
+ int32_t val;
+ val = floatx_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);
+ return val;
+}
+
+int32_t helper_fistt_ST0(void)
+{
+ int32_t val;
+ val = floatx_to_int32_round_to_zero(ST0, &env->fp_status);
+ if (val != (int16_t)val)
+ val = -32768;
+ return val;
+}
+
+int32_t helper_fisttl_ST0(void)
+{
+ int32_t val;
+ val = floatx_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);
+ return val;
+}
+
+void helper_fldt_ST0(target_ulong ptr)
+{
+ int new_fpstt;
+ new_fpstt = (env->fpstt - 1) & 7;
+ env->fpregs[new_fpstt].d = helper_fldt(ptr);
+ env->fpstt = new_fpstt;
+ env->fptags[new_fpstt] = 0; /* validate stack entry */
+}
+
+void helper_fstt_ST0(target_ulong ptr)
+{
+ helper_fstt(ST0, ptr);
+}
+
+void helper_fpush(void)
+{
+ fpush();
+}
+
+void helper_fpop(void)
+{
+ fpop();
+}
+
+void helper_fdecstp(void)
+{
+ env->fpstt = (env->fpstt - 1) & 7;
+ env->fpus &= (~0x4700);
+}
+
+void helper_fincstp(void)
+{
+ env->fpstt = (env->fpstt + 1) & 7;
+ env->fpus &= (~0x4700);
+}
+
+/* FPU move */
+
+void helper_ffree_STN(int st_index)
+{
+ env->fptags[(env->fpstt + st_index) & 7] = 1;
+}
+
+void helper_fmov_ST0_FT0(void)
+{
+ ST0 = FT0;
+}
+
+void helper_fmov_FT0_STN(int st_index)
+{
+ FT0 = ST(st_index);
+}
+
+void helper_fmov_ST0_STN(int st_index)
+{
+ ST0 = ST(st_index);
+}
+
+void helper_fmov_STN_ST0(int st_index)
+{
+ ST(st_index) = ST0;
+}
+
+void helper_fxchg_ST0_STN(int st_index)
+{
+ CPU86_LDouble tmp;
+ tmp = ST(st_index);
+ ST(st_index) = ST0;
+ ST0 = tmp;
+}
+
+/* FPU operations */
+
+static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500};
+
+void helper_fcom_ST0_FT0(void)
+{
+ int ret;
+
+ ret = floatx_compare(ST0, FT0, &env->fp_status);
+ env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1];
+}
+
+void helper_fucom_ST0_FT0(void)
+{
+ int ret;
+
+ ret = floatx_compare_quiet(ST0, FT0, &env->fp_status);
+ env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret+ 1];
+}
+
+static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
+
+void helper_fcomi_ST0_FT0(void)
+{
+ int eflags;
+ int ret;
+
+ ret = floatx_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;
+}
+
+void helper_fucomi_ST0_FT0(void)
+{
+ int eflags;
+ int ret;
+
+ ret = floatx_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;
+}
+
+void helper_fadd_ST0_FT0(void)
+{
+ ST0 += FT0;
+}
+
+void helper_fmul_ST0_FT0(void)
+{
+ ST0 *= FT0;
+}
+
+void helper_fsub_ST0_FT0(void)
+{
+ ST0 -= FT0;
+}
+
+void helper_fsubr_ST0_FT0(void)
+{
+ ST0 = FT0 - ST0;
+}
+
+void helper_fdiv_ST0_FT0(void)
+{
+ ST0 = helper_fdiv(ST0, FT0);
+}
+
+void helper_fdivr_ST0_FT0(void)
+{
+ ST0 = helper_fdiv(FT0, ST0);
+}
+
+/* fp operations between STN and ST0 */
+
+void helper_fadd_STN_ST0(int st_index)
+{
+ ST(st_index) += ST0;
+}
+
+void helper_fmul_STN_ST0(int st_index)
+{
+ ST(st_index) *= ST0;
+}
+
+void helper_fsub_STN_ST0(int st_index)
+{
+ ST(st_index) -= ST0;
+}
+
+void helper_fsubr_STN_ST0(int st_index)
+{
+ CPU86_LDouble *p;
+ p = &ST(st_index);
+ *p = ST0 - *p;
+}
+
+void helper_fdiv_STN_ST0(int st_index)
+{
+ CPU86_LDouble *p;
+ p = &ST(st_index);
+ *p = helper_fdiv(*p, ST0);
+}
+
+void helper_fdivr_STN_ST0(int st_index)
+{
+ CPU86_LDouble *p;
+ p = &ST(st_index);
+ *p = helper_fdiv(ST0, *p);
+}
+
+/* misc FPU operations */
+void helper_fchs_ST0(void)
+{
+ ST0 = floatx_chs(ST0);
+}
+
+void helper_fabs_ST0(void)
+{
+ ST0 = floatx_abs(ST0);
+}
+
+void helper_fld1_ST0(void)
+{
+ ST0 = f15rk[1];
+}
+
+void helper_fldl2t_ST0(void)
+{
+ ST0 = f15rk[6];
+}
+
+void helper_fldl2e_ST0(void)
+{
+ ST0 = f15rk[5];
+}
+
+void helper_fldpi_ST0(void)
+{
+ ST0 = f15rk[2];
+}
+
+void helper_fldlg2_ST0(void)
+{
+ ST0 = f15rk[3];
+}
+
+void helper_fldln2_ST0(void)
+{
+ ST0 = f15rk[4];
+}
+
+void helper_fldz_ST0(void)
+{
+ ST0 = f15rk[0];
+}
+
+void helper_fldz_FT0(void)
+{
+ FT0 = f15rk[0];
+}
+
+uint32_t helper_fnstsw(void)
+{
+ return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+}
+
+uint32_t helper_fnstcw(void)
+{
+ return env->fpuc;
+}
+
+static void update_fp_status(void)
+{
+ int rnd_type;
+
+ /* set rounding mode */
+ switch(env->fpuc & RC_MASK) {
+ default:
+ case RC_NEAR:
+ rnd_type = float_round_nearest_even;
+ break;
+ case RC_DOWN:
+ rnd_type = float_round_down;
+ break;
+ case RC_UP:
+ rnd_type = float_round_up;
+ break;
+ case RC_CHOP:
+ rnd_type = float_round_to_zero;
+ break;
+ }
+ set_float_rounding_mode(rnd_type, &env->fp_status);
+#ifdef FLOATX80
+ switch((env->fpuc >> 8) & 3) {
+ case 0:
+ rnd_type = 32;
+ break;
+ case 2:
+ rnd_type = 64;
+ break;
+ case 3:
+ default:
+ rnd_type = 80;
+ break;
+ }
+ set_floatx80_rounding_precision(rnd_type, &env->fp_status);
+#endif
+}
+
+void helper_fldcw(uint32_t val)
+{
+ env->fpuc = val;
+ update_fp_status();
+}
+
+void helper_fclex(void)
+{
+ env->fpus &= 0x7f00;
+}
+
+void helper_fwait(void)
+{
+ if (env->fpus & FPUS_SE)
+ fpu_raise_exception();
+}
+
+void helper_fninit(void)
+{
+ env->fpus = 0;
+ env->fpstt = 0;
+ env->fpuc = 0x37f;
+ env->fptags[0] = 1;
+ env->fptags[1] = 1;
+ env->fptags[2] = 1;
+ env->fptags[3] = 1;
+ env->fptags[4] = 1;
+ env->fptags[5] = 1;
+ env->fptags[6] = 1;
+ env->fptags[7] = 1;
+}
+
+/* BCD ops */
+
+void helper_fbld_ST0(target_ulong ptr)
+{
+ CPU86_LDouble tmp;
+ uint64_t val;
+ unsigned int v;
+ int i;
+
+ val = 0;
+ for(i = 8; i >= 0; i--) {
+ v = ldub(ptr + i);
+ val = (val * 100) + ((v >> 4) * 10) + (v & 0xf);
+ }
+ tmp = val;
+ if (ldub(ptr + 9) & 0x80)
+ tmp = -tmp;
+ fpush();
+ ST0 = tmp;
+}
+
+void helper_fbst_ST0(target_ulong ptr)
+{
+ int v;
+ target_ulong mem_ref, mem_end;
+ int64_t val;
+
+ val = floatx_to_int64(ST0, &env->fp_status);
+ mem_ref = ptr;
+ mem_end = mem_ref + 9;
+ if (val < 0) {
+ stb(mem_end, 0x80);
+ val = -val;
+ } else {
+ stb(mem_end, 0x00);
+ }
+ while (mem_ref < mem_end) {
+ if (val == 0)
+ break;
+ v = val % 100;
+ val = val / 100;
+ v = ((v / 10) << 4) | (v % 10);
+ stb(mem_ref++, v);
+ }
+ while (mem_ref < mem_end) {
+ stb(mem_ref++, 0);
+ }
+}
+
+void helper_f2xm1(void)
+{
+ ST0 = pow(2.0,ST0) - 1.0;
+}
+
+void helper_fyl2x(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if (fptemp>0.0){
+ fptemp = log(fptemp)/log(2.0); /* log2(ST) */
+ ST1 *= fptemp;
+ fpop();
+ } else {
+ env->fpus &= (~0x4700);
+ env->fpus |= 0x400;
+ }
+}
+
+void helper_fptan(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = tan(fptemp);
+ fpush();
+ ST0 = 1.0;
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg| < 2**52 only */
+ }
+}
+
+void helper_fpatan(void)
+{
+ CPU86_LDouble fptemp, fpsrcop;
+
+ fpsrcop = ST1;
+ fptemp = ST0;
+ ST1 = atan2(fpsrcop,fptemp);
+ fpop();
+}
+
+void helper_fxtract(void)
+{
+ CPU86_LDoubleU temp;
+ unsigned int expdif;
+
+ temp.d = ST0;
+ expdif = EXPD(temp) - EXPBIAS;
+ /*DP exponent bias*/
+ ST0 = expdif;
+ fpush();
+ BIASEXPONENT(temp);
+ ST0 = temp.d;
+}
+
+void helper_fprem1(void)
+{
+ CPU86_LDouble dblq, fpsrcop, fptemp;
+ CPU86_LDoubleU fpsrcop1, fptemp1;
+ int expdif;
+ signed long long int q;
+
+ if (isinf(ST0) || isnan(ST0) || isnan(ST1) || (ST1 == 0.0)) {
+ ST0 = 0.0 / 0.0; /* NaN */
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ return;
+ }
+
+ fpsrcop = ST0;
+ fptemp = ST1;
+ fpsrcop1.d = fpsrcop;
+ fptemp1.d = fptemp;
+ expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
+
+ if (expdif < 0) {
+ /* optimisation? taken from the AMD docs */
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ /* ST0 is unchanged */
+ return;
+ }
+
+ if (expdif < 53) {
+ dblq = fpsrcop / fptemp;
+ /* round dblq towards nearest integer */
+ dblq = rint(dblq);
+ ST0 = fpsrcop - fptemp * dblq;
+
+ /* convert dblq to q by truncating towards zero */
+ if (dblq < 0.0)
+ q = (signed long long int)(-dblq);
+ else
+ q = (signed long long int)dblq;
+
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ /* (C0,C3,C1) <-- (q2,q1,q0) */
+ env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
+ env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
+ env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
+ } else {
+ env->fpus |= 0x400; /* C2 <-- 1 */
+ fptemp = pow(2.0, expdif - 50);
+ fpsrcop = (ST0 / ST1) / fptemp;
+ /* fpsrcop = integer obtained by chopping */
+ fpsrcop = (fpsrcop < 0.0) ?
+ -(floor(fabs(fpsrcop))) : floor(fpsrcop);
+ ST0 -= (ST1 * fpsrcop * fptemp);
+ }
+}
+
+void helper_fprem(void)
+{
+ CPU86_LDouble dblq, fpsrcop, fptemp;
+ CPU86_LDoubleU fpsrcop1, fptemp1;
+ int expdif;
+ signed long long int q;
+
+ if (isinf(ST0) || isnan(ST0) || isnan(ST1) || (ST1 == 0.0)) {
+ ST0 = 0.0 / 0.0; /* NaN */
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ return;
+ }
+
+ fpsrcop = (CPU86_LDouble)ST0;
+ fptemp = (CPU86_LDouble)ST1;
+ fpsrcop1.d = fpsrcop;
+ fptemp1.d = fptemp;
+ expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
+
+ if (expdif < 0) {
+ /* optimisation? taken from the AMD docs */
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ /* ST0 is unchanged */
+ return;
+ }
+
+ if ( expdif < 53 ) {
+ dblq = fpsrcop/*ST0*/ / fptemp/*ST1*/;
+ /* round dblq towards zero */
+ dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq);
+ ST0 = fpsrcop/*ST0*/ - fptemp * dblq;
+
+ /* convert dblq to q by truncating towards zero */
+ if (dblq < 0.0)
+ q = (signed long long int)(-dblq);
+ else
+ q = (signed long long int)dblq;
+
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ /* (C0,C3,C1) <-- (q2,q1,q0) */
+ env->fpus |= (q & 0x4) << (8 - 2); /* (C0) <-- q2 */
+ env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */
+ env->fpus |= (q & 0x1) << (9 - 0); /* (C1) <-- q0 */
+ } else {
+ int N = 32 + (expdif % 32); /* as per AMD docs */
+ env->fpus |= 0x400; /* C2 <-- 1 */
+ fptemp = pow(2.0, (double)(expdif - N));
+ fpsrcop = (ST0 / ST1) / fptemp;
+ /* fpsrcop = integer obtained by chopping */
+ fpsrcop = (fpsrcop < 0.0) ?
+ -(floor(fabs(fpsrcop))) : floor(fpsrcop);
+ ST0 -= (ST1 * fpsrcop * fptemp);
+ }
+}
+
+void helper_fyl2xp1(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if ((fptemp+1.0)>0.0) {
+ fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
+ ST1 *= fptemp;
+ fpop();
+ } else {
+ env->fpus &= (~0x4700);
+ env->fpus |= 0x400;
+ }
+}
+
+void helper_fsqrt(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if (fptemp<0.0) {
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ env->fpus |= 0x400;
+ }
+ ST0 = sqrt(fptemp);
+}
+
+void helper_fsincos(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = sin(fptemp);
+ fpush();
+ ST0 = cos(fptemp);
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg| < 2**63 only */
+ }
+}
+
+void helper_frndint(void)
+{
+ ST0 = floatx_round_to_int(ST0, &env->fp_status);
+}
+
+void helper_fscale(void)
+{
+ ST0 = ldexp (ST0, (int)(ST1));
+}
+
+void helper_fsin(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = sin(fptemp);
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg| < 2**53 only */
+ }
+}
+
+void helper_fcos(void)
+{
+ CPU86_LDouble fptemp;
+
+ fptemp = ST0;
+ if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
+ env->fpus |= 0x400;
+ } else {
+ ST0 = cos(fptemp);
+ env->fpus &= (~0x400); /* C2 <-- 0 */
+ /* the above code is for |arg5 < 2**63 only */
+ }
+}
+
+void helper_fxam_ST0(void)
+{
+ CPU86_LDoubleU temp;
+ int expdif;
+
+ temp.d = ST0;
+
+ env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
+ if (SIGND(temp))
+ env->fpus |= 0x200; /* C1 <-- 1 */
+
+ /* 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*/;
+ } else if (expdif == 0) {
+ if (MANTD(temp) == 0)
+ env->fpus |= 0x4000 /*Zero*/;
+ else
+ env->fpus |= 0x4400 /*Denormal*/;
+ } else {
+ env->fpus |= 0x400;
+ }
+}
+
+void helper_fstenv(target_ulong ptr, int data32)
+{
+ int fpus, fptag, exp, i;
+ uint64_t mant;
+ CPU86_LDoubleU tmp;
+
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for (i=7; i>=0; i--) {
+ fptag <<= 2;
+ if (env->fptags[i]) {
+ fptag |= 3;
+ } else {
+ tmp.d = env->fpregs[i].d;
+ exp = EXPD(tmp);
+ mant = MANTD(tmp);
+ if (exp == 0 && mant == 0) {
+ /* zero */
+ fptag |= 1;
+ } else if (exp == 0 || exp == MAXEXPD
+#ifdef USE_X86LDOUBLE
+ || (mant & (1LL << 63)) == 0
+#endif
+ ) {
+ /* NaNs, infinity, denormal */
+ fptag |= 2;
+ }
+ }
+ }
+ if (data32) {
+ /* 32 bit */
+ stl(ptr, env->fpuc);
+ stl(ptr + 4, fpus);
+ stl(ptr + 8, fptag);
+ stl(ptr + 12, 0); /* fpip */
+ stl(ptr + 16, 0); /* fpcs */
+ stl(ptr + 20, 0); /* fpoo */
+ stl(ptr + 24, 0); /* fpos */
+ } else {
+ /* 16 bit */
+ stw(ptr, env->fpuc);
+ stw(ptr + 2, fpus);
+ stw(ptr + 4, fptag);
+ stw(ptr + 6, 0);
+ stw(ptr + 8, 0);
+ stw(ptr + 10, 0);
+ stw(ptr + 12, 0);
+ }
+}
+
+void helper_fldenv(target_ulong ptr, int data32)
+{
+ int i, fpus, fptag;
+
+ if (data32) {
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 4);
+ fptag = lduw(ptr + 8);
+ }
+ else {
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 2);
+ fptag = lduw(ptr + 4);
+ }
+ env->fpstt = (fpus >> 11) & 7;
+ env->fpus = fpus & ~0x3800;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag & 3) == 3);
+ fptag >>= 2;
+ }
+}
+
+void helper_fsave(target_ulong ptr, int data32)
+{
+ CPU86_LDouble tmp;
+ int i;
+
+ helper_fstenv(ptr, data32);
+
+ ptr += (14 << data32);
+ for(i = 0;i < 8; i++) {
+ tmp = ST(i);
+ helper_fstt(tmp, ptr);
+ ptr += 10;
+ }
+
+ /* fninit */
+ env->fpus = 0;
+ env->fpstt = 0;
+ env->fpuc = 0x37f;
+ env->fptags[0] = 1;
+ env->fptags[1] = 1;
+ env->fptags[2] = 1;
+ env->fptags[3] = 1;
+ env->fptags[4] = 1;
+ env->fptags[5] = 1;
+ env->fptags[6] = 1;
+ env->fptags[7] = 1;
+}
+
+void helper_frstor(target_ulong ptr, int data32)
+{
+ CPU86_LDouble tmp;
+ int i;
+
+ helper_fldenv(ptr, data32);
+ ptr += (14 << data32);
+
+ for(i = 0;i < 8; i++) {
+ tmp = helper_fldt(ptr);
+ ST(i) = tmp;
+ ptr += 10;
+ }
+}
+
+void helper_fxsave(target_ulong ptr, int data64)
+{
+ int fpus, fptag, i, nb_xmm_regs;
+ CPU86_LDouble tmp;
+ target_ulong addr;
+
+ /* The operand must be 16 byte aligned */
+ if (ptr & 0xf) {
+ raise_exception(EXCP0D_GPF);
+ }
+
+ fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ fptag = 0;
+ for(i = 0; i < 8; i++) {
+ fptag |= (env->fptags[i] << i);
+ }
+ stw(ptr, env->fpuc);
+ stw(ptr + 2, fpus);
+ stw(ptr + 4, fptag ^ 0xff);
+#ifdef TARGET_X86_64
+ if (data64) {
+ stq(ptr + 0x08, 0); /* rip */
+ stq(ptr + 0x10, 0); /* rdp */
+ } else
+#endif
+ {
+ stl(ptr + 0x08, 0); /* eip */
+ stl(ptr + 0x0c, 0); /* sel */
+ stl(ptr + 0x10, 0); /* dp */
+ stl(ptr + 0x14, 0); /* sel */
+ }
+
+ addr = ptr + 0x20;
+ for(i = 0;i < 8; i++) {
+ tmp = ST(i);
+ helper_fstt(tmp, addr);
+ addr += 16;
+ }
+
+ if (env->cr[4] & CR4_OSFXSR_MASK) {
+ /* XXX: finish it */
+ stl(ptr + 0x18, env->mxcsr); /* mxcsr */
+ stl(ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */
+ if (env->hflags & HF_CS64_MASK)
+ nb_xmm_regs = 16;
+ else
+ nb_xmm_regs = 8;
+ addr = ptr + 0xa0;
+ /* Fast FXSAVE leaves out the XMM registers */
+ if (!(env->efer & MSR_EFER_FFXSR)
+ || (env->hflags & HF_CPL_MASK)
+ || !(env->hflags & HF_LMA_MASK)) {
+ for(i = 0; i < nb_xmm_regs; i++) {
+ stq(addr, env->xmm_regs[i].XMM_Q(0));
+ stq(addr + 8, env->xmm_regs[i].XMM_Q(1));
+ addr += 16;
+ }
+ }
+ }
+}
+
+void helper_fxrstor(target_ulong ptr, int data64)
+{
+ int i, fpus, fptag, nb_xmm_regs;
+ CPU86_LDouble tmp;
+ target_ulong addr;
+
+ /* The operand must be 16 byte aligned */
+ if (ptr & 0xf) {
+ raise_exception(EXCP0D_GPF);
+ }
+
+ env->fpuc = lduw(ptr);
+ fpus = lduw(ptr + 2);
+ fptag = lduw(ptr + 4);
+ env->fpstt = (fpus >> 11) & 7;
+ env->fpus = fpus & ~0x3800;
+ fptag ^= 0xff;
+ for(i = 0;i < 8; i++) {
+ env->fptags[i] = ((fptag >> i) & 1);
+ }
+
+ addr = ptr + 0x20;
+ for(i = 0;i < 8; i++) {
+ tmp = helper_fldt(addr);
+ ST(i) = tmp;
+ addr += 16;
+ }
+
+ if (env->cr[4] & CR4_OSFXSR_MASK) {
+ /* XXX: finish it */
+ env->mxcsr = ldl(ptr + 0x18);
+ //ldl(ptr + 0x1c);
+ if (env->hflags & HF_CS64_MASK)
+ nb_xmm_regs = 16;
+ else
+ nb_xmm_regs = 8;
+ addr = ptr + 0xa0;
+ /* Fast FXRESTORE leaves out the XMM registers */
+ if (!(env->efer & MSR_EFER_FFXSR)
+ || (env->hflags & HF_CPL_MASK)
+ || !(env->hflags & HF_LMA_MASK)) {
+ for(i = 0; i < nb_xmm_regs; i++) {
+ env->xmm_regs[i].XMM_Q(0) = ldq(addr);
+ env->xmm_regs[i].XMM_Q(1) = ldq(addr + 8);
+ addr += 16;
+ }
+ }
+ }
+}
+
+#ifndef USE_X86LDOUBLE
+
+void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, CPU86_LDouble 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;
+
+ temp.d = f;
+ *pmant = temp.l.lower;
+ *pexp = temp.l.upper;
+}
+
+CPU86_LDouble cpu_set_fp80(uint64_t mant, uint16_t upper)
+{
+ CPU86_LDoubleU temp;
+
+ temp.l.upper = upper;
+ temp.l.lower = mant;
+ return temp.d;
+}
+#endif
+
+#ifdef TARGET_X86_64
+
+//#define DEBUG_MULDIV
+
+static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+ *plow += a;
+ /* carry test */
+ if (*plow < a)
+ (*phigh)++;
+ *phigh += b;
+}
+
+static void neg128(uint64_t *plow, uint64_t *phigh)
+{
+ *plow = ~ *plow;
+ *phigh = ~ *phigh;
+ add128(plow, phigh, 1, 0);
+}
+
+/* return TRUE if overflow */
+static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b)
+{
+ uint64_t q, r, a1, a0;
+ int i, qb, ab;
+
+ a0 = *plow;
+ a1 = *phigh;
+ if (a1 == 0) {
+ q = a0 / b;
+ r = a0 % b;
+ *plow = q;
+ *phigh = r;
+ } else {
+ if (a1 >= b)
+ return 1;
+ /* XXX: use a better algorithm */
+ for(i = 0; i < 64; i++) {
+ ab = a1 >> 63;
+ a1 = (a1 << 1) | (a0 >> 63);
+ if (ab || a1 >= b) {
+ a1 -= b;
+ qb = 1;
+ } else {
+ qb = 0;
+ }
+ a0 = (a0 << 1) | qb;
+ }
+#if defined(DEBUG_MULDIV)
+ printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n",
+ *phigh, *plow, b, a0, a1);
+#endif
+ *plow = a0;
+ *phigh = a1;
+ }
+ return 0;
+}
+
+/* return TRUE if overflow */
+static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b)
+{
+ int sa, sb;
+ sa = ((int64_t)*phigh < 0);
+ if (sa)
+ neg128(plow, phigh);
+ sb = (b < 0);
+ if (sb)
+ b = -b;
+ if (div64(plow, phigh, b) != 0)
+ return 1;
+ if (sa ^ sb) {
+ if (*plow > (1ULL << 63))
+ return 1;
+ *plow = - *plow;
+ } else {
+ if (*plow >= (1ULL << 63))
+ return 1;
+ }
+ if (sa)
+ *phigh = - *phigh;
+ return 0;
+}
+
+void helper_mulq_EAX_T0(target_ulong t0)
+{
+ uint64_t r0, r1;
+
+ mulu64(&r0, &r1, EAX, t0);
+ EAX = r0;
+ EDX = r1;
+ CC_DST = r0;
+ CC_SRC = r1;
+}
+
+void helper_imulq_EAX_T0(target_ulong t0)
+{
+ uint64_t r0, r1;
+
+ muls64(&r0, &r1, EAX, t0);
+ EAX = r0;
+ EDX = r1;
+ CC_DST = r0;
+ CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
+}
+
+target_ulong helper_imulq_T0_T1(target_ulong t0, target_ulong t1)
+{
+ uint64_t r0, r1;
+
+ muls64(&r0, &r1, t0, t1);
+ CC_DST = r0;
+ CC_SRC = ((int64_t)r1 != ((int64_t)r0 >> 63));
+ return r0;
+}
+
+void helper_divq_EAX(target_ulong t0)
+{
+ uint64_t r0, r1;
+ if (t0 == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ r0 = EAX;
+ r1 = EDX;
+ if (div64(&r0, &r1, t0))
+ raise_exception(EXCP00_DIVZ);
+ EAX = r0;
+ EDX = r1;
+}
+
+void helper_idivq_EAX(target_ulong t0)
+{
+ uint64_t r0, r1;
+ if (t0 == 0) {
+ raise_exception(EXCP00_DIVZ);
+ }
+ r0 = EAX;
+ r1 = EDX;
+ if (idiv64(&r0, &r1, t0))
+ raise_exception(EXCP00_DIVZ);
+ EAX = r0;
+ EDX = r1;
+}
+#endif
+
+static void do_hlt(void)
+{
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ cpu_loop_exit();
+}
+
+void helper_hlt(int next_eip_addend)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_HLT, 0);
+ EIP += next_eip_addend;
+
+ do_hlt();
+}
+
+void helper_monitor(target_ulong ptr)
+{
+ if ((uint32_t)ECX != 0)
+ raise_exception(EXCP0D_GPF);
+ /* XXX: store address ? */
+ helper_svm_check_intercept_param(SVM_EXIT_MONITOR, 0);
+}
+
+void helper_mwait(int next_eip_addend)
+{
+ if ((uint32_t)ECX != 0)
+ raise_exception(EXCP0D_GPF);
+ helper_svm_check_intercept_param(SVM_EXIT_MWAIT, 0);
+ EIP += next_eip_addend;
+
+ /* XXX: not complete but not completely erroneous */
+ if (env->cpu_index != 0 || env->next_cpu != NULL) {
+ /* more than one CPU: do not sleep because another CPU may
+ wake this one */
+ } else {
+ do_hlt();
+ }
+}
+
+void helper_debug(void)
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+void helper_reset_rf(void)
+{
+ env->eflags &= ~RF_MASK;
+}
+
+void helper_raise_interrupt(int intno, int next_eip_addend)
+{
+ raise_interrupt(intno, 1, 0, next_eip_addend);
+}
+
+void helper_raise_exception(int exception_index)
+{
+ raise_exception(exception_index);
+}
+
+void helper_cli(void)
+{
+ env->eflags &= ~IF_MASK;
+}
+
+void helper_sti(void)
+{
+ env->eflags |= IF_MASK;
+}
+
+#if 0
+/* vm86plus instructions */
+void helper_cli_vm(void)
+{
+ env->eflags &= ~VIF_MASK;
+}
+
+void helper_sti_vm(void)
+{
+ env->eflags |= VIF_MASK;
+ if (env->eflags & VIP_MASK) {
+ raise_exception(EXCP0D_GPF);
+ }
+}
+#endif
+
+void helper_set_inhibit_irq(void)
+{
+ env->hflags |= HF_INHIBIT_IRQ_MASK;
+}
+
+void helper_reset_inhibit_irq(void)
+{
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK;
+}
+
+void helper_boundw(target_ulong a0, int v)
+{
+ int low, high;
+ low = ldsw(a0);
+ high = ldsw(a0 + 2);
+ v = (int16_t)v;
+ if (v < low || v > high) {
+ raise_exception(EXCP05_BOUND);
+ }
+}
+
+void helper_boundl(target_ulong a0, int v)
+{
+ int low, high;
+ low = ldl(a0);
+ high = ldl(a0 + 4);
+ if (v < low || v > high) {
+ raise_exception(EXCP05_BOUND);
+ }
+}
+
+static float approx_rsqrt(float a)
+{
+ return 1.0 / sqrt(a);
+}
+
+static float approx_rcp(float a)
+{
+ return 1.0 / a;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#endif
+
+#if !defined(CONFIG_USER_ONLY)
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ int ret;
+ unsigned long pc;
+ CPUX86State *saved_env;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+ ret = cpu_x86_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (ret) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, NULL);
+ }
+ }
+ raise_exception_err(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
+#endif
+
+/* Secure Virtual Machine helpers */
+
+#if defined(CONFIG_USER_ONLY)
+
+void helper_vmrun(int aflag, int next_eip_addend)
+{
+}
+void helper_vmmcall(void)
+{
+}
+void helper_vmload(int aflag)
+{
+}
+void helper_vmsave(int aflag)
+{
+}
+void helper_stgi(void)
+{
+}
+void helper_clgi(void)
+{
+}
+void helper_skinit(void)
+{
+}
+void helper_invlpga(int aflag)
+{
+}
+void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
+{
+}
+void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
+{
+}
+
+void helper_svm_check_io(uint32_t port, uint32_t param,
+ uint32_t next_eip_addend)
+{
+}
+#else
+
+static inline void svm_save_seg(target_phys_addr_t addr,
+ const SegmentCache *sc)
+{
+ stw_phys(addr + offsetof(struct vmcb_seg, selector),
+ sc->selector);
+ stq_phys(addr + offsetof(struct vmcb_seg, base),
+ sc->base);
+ stl_phys(addr + offsetof(struct vmcb_seg, limit),
+ sc->limit);
+ stw_phys(addr + offsetof(struct vmcb_seg, attrib),
+ ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00));
+}
+
+static inline void svm_load_seg(target_phys_addr_t addr, SegmentCache *sc)
+{
+ unsigned int flags;
+
+ sc->selector = lduw_phys(addr + offsetof(struct vmcb_seg, selector));
+ sc->base = ldq_phys(addr + offsetof(struct vmcb_seg, base));
+ sc->limit = ldl_phys(addr + offsetof(struct vmcb_seg, limit));
+ flags = lduw_phys(addr + offsetof(struct vmcb_seg, attrib));
+ sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12);
+}
+
+static inline void svm_load_seg_cache(target_phys_addr_t addr,
+ CPUState *env, int seg_reg)
+{
+ SegmentCache sc1, *sc = &sc1;
+ svm_load_seg(addr, sc);
+ cpu_x86_load_seg_cache(env, seg_reg, sc->selector,
+ sc->base, sc->limit, sc->flags);
+}
+
+void helper_vmrun(int aflag, int next_eip_addend)
+{
+ target_ulong addr;
+ uint32_t event_inj;
+ uint32_t int_ctl;
+
+ helper_svm_check_intercept_param(SVM_EXIT_VMRUN, 0);
+
+ if (aflag == 2)
+ addr = EAX;
+ else
+ addr = (uint32_t)EAX;
+
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr);
+
+ env->vm_vmcb = addr;
+
+ /* save the current CPU state in the hsave page */
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
+ stl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
+
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base), env->idt.base);
+ stl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
+
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]);
+
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags), compute_eflags());
+
+ svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.es),
+ &env->segs[R_ES]);
+ svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.cs),
+ &env->segs[R_CS]);
+ svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ss),
+ &env->segs[R_SS]);
+ svm_save_seg(env->vm_hsave + offsetof(struct vmcb, save.ds),
+ &env->segs[R_DS]);
+
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip),
+ EIP + next_eip_addend);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp), ESP);
+ stq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax), EAX);
+
+ /* load the interception bitmaps so we do not need to access the
+ vmcb in svm mode */
+ env->intercept = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept));
+ env->intercept_cr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_read));
+ env->intercept_cr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_cr_write));
+ env->intercept_dr_read = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_read));
+ env->intercept_dr_write = lduw_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_dr_write));
+ env->intercept_exceptions = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.intercept_exceptions));
+
+ /* enable intercepts */
+ env->hflags |= HF_SVMI_MASK;
+
+ env->tsc_offset = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.tsc_offset));
+
+ env->gdt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base));
+ env->gdt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit));
+
+ env->idt.base = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base));
+ env->idt.limit = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit));
+
+ /* clear exit_info_2 so we behave like the real hardware */
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0);
+
+ cpu_x86_update_cr0(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0)));
+ cpu_x86_update_cr4(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4)));
+ cpu_x86_update_cr3(env, ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3)));
+ env->cr[2] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2));
+ int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
+ env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
+ if (int_ctl & V_INTR_MASKING_MASK) {
+ env->v_tpr = int_ctl & V_TPR_MASK;
+ env->hflags2 |= HF2_VINTR_MASK;
+ if (env->eflags & IF_MASK)
+ env->hflags2 |= HF2_HIF_MASK;
+ }
+
+ cpu_load_efer(env,
+ ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer)));
+ env->eflags = 0;
+ load_eflags(ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags)),
+ ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
+ CC_OP = CC_OP_EFLAGS;
+
+ svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.es),
+ env, R_ES);
+ svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.cs),
+ env, R_CS);
+ svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ss),
+ env, R_SS);
+ svm_load_seg_cache(env->vm_vmcb + offsetof(struct vmcb, save.ds),
+ env, R_DS);
+
+ EIP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip));
+ env->eip = EIP;
+ ESP = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp));
+ EAX = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax));
+ env->dr[7] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7));
+ env->dr[6] = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6));
+ cpu_x86_set_cpl(env, ldub_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl)));
+
+ /* FIXME: guest state consistency checks */
+
+ switch(ldub_phys(env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) {
+ case TLB_CONTROL_DO_NOTHING:
+ break;
+ case TLB_CONTROL_FLUSH_ALL_ASID:
+ /* FIXME: this is not 100% correct but should work for now */
+ tlb_flush(env, 1);
+ break;
+ }
+
+ env->hflags2 |= HF2_GIF_MASK;
+
+ if (int_ctl & V_IRQ_MASK) {
+ env->interrupt_request |= CPU_INTERRUPT_VIRQ;
+ }
+
+ /* maybe we need to inject an event */
+ event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
+ if (event_inj & SVM_EVTINJ_VALID) {
+ uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK;
+ uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR;
+ uint32_t event_inj_err = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err));
+
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err);
+ /* FIXME: need to implement valid_err */
+ switch (event_inj & SVM_EVTINJ_TYPE_MASK) {
+ case SVM_EVTINJ_TYPE_INTR:
+ env->exception_index = vector;
+ env->error_code = event_inj_err;
+ env->exception_is_int = 0;
+ env->exception_next_eip = -1;
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR");
+ /* XXX: is it always correct ? */
+ do_interrupt(vector, 0, 0, 0, 1);
+ break;
+ case SVM_EVTINJ_TYPE_NMI:
+ env->exception_index = EXCP02_NMI;
+ env->error_code = event_inj_err;
+ env->exception_is_int = 0;
+ env->exception_next_eip = EIP;
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI");
+ cpu_loop_exit();
+ break;
+ case SVM_EVTINJ_TYPE_EXEPT:
+ env->exception_index = vector;
+ env->error_code = event_inj_err;
+ env->exception_is_int = 0;
+ env->exception_next_eip = -1;
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT");
+ cpu_loop_exit();
+ break;
+ case SVM_EVTINJ_TYPE_SOFT:
+ env->exception_index = vector;
+ env->error_code = event_inj_err;
+ env->exception_is_int = 1;
+ env->exception_next_eip = EIP;
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT");
+ cpu_loop_exit();
+ break;
+ }
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", env->exception_index, env->error_code);
+ }
+}
+
+void helper_vmmcall(void)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_VMMCALL, 0);
+ raise_exception(EXCP06_ILLOP);
+}
+
+void helper_vmload(int aflag)
+{
+ target_ulong addr;
+ helper_svm_check_intercept_param(SVM_EXIT_VMLOAD, 0);
+
+ if (aflag == 2)
+ addr = EAX;
+ else
+ addr = (uint32_t)EAX;
+
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
+ addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
+ env->segs[R_FS].base);
+
+ svm_load_seg_cache(addr + offsetof(struct vmcb, save.fs),
+ env, R_FS);
+ svm_load_seg_cache(addr + offsetof(struct vmcb, save.gs),
+ env, R_GS);
+ svm_load_seg(addr + offsetof(struct vmcb, save.tr),
+ &env->tr);
+ svm_load_seg(addr + offsetof(struct vmcb, save.ldtr),
+ &env->ldt);
+
+#ifdef TARGET_X86_64
+ env->kernelgsbase = ldq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base));
+ env->lstar = ldq_phys(addr + offsetof(struct vmcb, save.lstar));
+ env->cstar = ldq_phys(addr + offsetof(struct vmcb, save.cstar));
+ env->fmask = ldq_phys(addr + offsetof(struct vmcb, save.sfmask));
+#endif
+ env->star = ldq_phys(addr + offsetof(struct vmcb, save.star));
+ env->sysenter_cs = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_cs));
+ env->sysenter_esp = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_esp));
+ env->sysenter_eip = ldq_phys(addr + offsetof(struct vmcb, save.sysenter_eip));
+}
+
+void helper_vmsave(int aflag)
+{
+ target_ulong addr;
+ helper_svm_check_intercept_param(SVM_EXIT_VMSAVE, 0);
+
+ if (aflag == 2)
+ addr = EAX;
+ else
+ addr = (uint32_t)EAX;
+
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n",
+ addr, ldq_phys(addr + offsetof(struct vmcb, save.fs.base)),
+ env->segs[R_FS].base);
+
+ svm_save_seg(addr + offsetof(struct vmcb, save.fs),
+ &env->segs[R_FS]);
+ svm_save_seg(addr + offsetof(struct vmcb, save.gs),
+ &env->segs[R_GS]);
+ svm_save_seg(addr + offsetof(struct vmcb, save.tr),
+ &env->tr);
+ svm_save_seg(addr + offsetof(struct vmcb, save.ldtr),
+ &env->ldt);
+
+#ifdef TARGET_X86_64
+ stq_phys(addr + offsetof(struct vmcb, save.kernel_gs_base), env->kernelgsbase);
+ stq_phys(addr + offsetof(struct vmcb, save.lstar), env->lstar);
+ stq_phys(addr + offsetof(struct vmcb, save.cstar), env->cstar);
+ stq_phys(addr + offsetof(struct vmcb, save.sfmask), env->fmask);
+#endif
+ stq_phys(addr + offsetof(struct vmcb, save.star), env->star);
+ stq_phys(addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs);
+ stq_phys(addr + offsetof(struct vmcb, save.sysenter_esp), env->sysenter_esp);
+ stq_phys(addr + offsetof(struct vmcb, save.sysenter_eip), env->sysenter_eip);
+}
+
+void helper_stgi(void)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_STGI, 0);
+ env->hflags2 |= HF2_GIF_MASK;
+}
+
+void helper_clgi(void)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_CLGI, 0);
+ env->hflags2 &= ~HF2_GIF_MASK;
+}
+
+void helper_skinit(void)
+{
+ helper_svm_check_intercept_param(SVM_EXIT_SKINIT, 0);
+ /* XXX: not implemented */
+ raise_exception(EXCP06_ILLOP);
+}
+
+void helper_invlpga(int aflag)
+{
+ target_ulong addr;
+ helper_svm_check_intercept_param(SVM_EXIT_INVLPGA, 0);
+
+ if (aflag == 2)
+ addr = EAX;
+ else
+ addr = (uint32_t)EAX;
+
+ /* XXX: could use the ASID to see if it is needed to do the
+ flush */
+ tlb_flush_page(env, addr);
+}
+
+void helper_svm_check_intercept_param(uint32_t type, uint64_t param)
+{
+ if (likely(!(env->hflags & HF_SVMI_MASK)))
+ return;
+ switch(type) {
+ case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8:
+ if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) {
+ helper_vmexit(type, param);
+ }
+ break;
+ case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8:
+ if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) {
+ helper_vmexit(type, param);
+ }
+ break;
+ case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7:
+ if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) {
+ helper_vmexit(type, param);
+ }
+ break;
+ case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7:
+ if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) {
+ helper_vmexit(type, param);
+ }
+ break;
+ case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31:
+ if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) {
+ helper_vmexit(type, param);
+ }
+ break;
+ case SVM_EXIT_MSR:
+ if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) {
+ /* FIXME: this should be read in at vmrun (faster this way?) */
+ uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.msrpm_base_pa));
+ uint32_t t0, t1;
+ switch((uint32_t)ECX) {
+ case 0 ... 0x1fff:
+ t0 = (ECX * 2) % 8;
+ t1 = (ECX * 2) / 8;
+ break;
+ case 0xc0000000 ... 0xc0001fff:
+ t0 = (8192 + ECX - 0xc0000000) * 2;
+ t1 = (t0 / 8);
+ t0 %= 8;
+ break;
+ case 0xc0010000 ... 0xc0011fff:
+ t0 = (16384 + ECX - 0xc0010000) * 2;
+ t1 = (t0 / 8);
+ t0 %= 8;
+ break;
+ default:
+ helper_vmexit(type, param);
+ t0 = 0;
+ t1 = 0;
+ break;
+ }
+ if (ldub_phys(addr + t1) & ((1 << param) << t0))
+ helper_vmexit(type, param);
+ }
+ break;
+ default:
+ if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) {
+ helper_vmexit(type, param);
+ }
+ break;
+ }
+}
+
+void helper_svm_check_io(uint32_t port, uint32_t param,
+ uint32_t next_eip_addend)
+{
+ if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) {
+ /* FIXME: this should be read in at vmrun (faster this way?) */
+ uint64_t addr = ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.iopm_base_pa));
+ uint16_t mask = (1 << ((param >> 4) & 7)) - 1;
+ if(lduw_phys(addr + port / 8) & (mask << (port & 7))) {
+ /* next EIP */
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2),
+ env->eip + next_eip_addend);
+ helper_vmexit(SVM_EXIT_IOIO, param | (port << 16));
+ }
+ }
+}
+
+/* Note: currently only 32 bits of exit_code are used */
+void helper_vmexit(uint32_t exit_code, uint64_t exit_info_1)
+{
+ uint32_t int_ctl;
+
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" PRIx64 ", " TARGET_FMT_lx ")!\n",
+ exit_code, exit_info_1,
+ ldq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2)),
+ EIP);
+
+ if(env->hflags & HF_INHIBIT_IRQ_MASK) {
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), SVM_INTERRUPT_SHADOW_MASK);
+ env->hflags &= ~HF_INHIBIT_IRQ_MASK;
+ } else {
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0);
+ }
+
+ /* Save the VM state in the vmcb */
+ svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.es),
+ &env->segs[R_ES]);
+ svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.cs),
+ &env->segs[R_CS]);
+ svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ss),
+ &env->segs[R_SS]);
+ svm_save_seg(env->vm_vmcb + offsetof(struct vmcb, save.ds),
+ &env->segs[R_DS]);
+
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), env->gdt.base);
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), env->gdt.limit);
+
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), env->idt.base);
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), env->idt.limit);
+
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]);
+
+ int_ctl = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl));
+ int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK);
+ int_ctl |= env->v_tpr & V_TPR_MASK;
+ if (env->interrupt_request & CPU_INTERRUPT_VIRQ)
+ int_ctl |= V_IRQ_MASK;
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl);
+
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rflags), compute_eflags());
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rip), env->eip);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rsp), ESP);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.rax), EAX);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]);
+ stb_phys(env->vm_vmcb + offsetof(struct vmcb, save.cpl), env->hflags & HF_CPL_MASK);
+
+ /* Reload the host state from vm_hsave */
+ env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK);
+ env->hflags &= ~HF_SVMI_MASK;
+ env->intercept = 0;
+ env->intercept_exceptions = 0;
+ env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+ env->tsc_offset = 0;
+
+ env->gdt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.base));
+ env->gdt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit));
+
+ env->idt.base = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.base));
+ env->idt.limit = ldl_phys(env->vm_hsave + offsetof(struct vmcb, save.idtr.limit));
+
+ cpu_x86_update_cr0(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr0)) | CR0_PE_MASK);
+ cpu_x86_update_cr4(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr4)));
+ cpu_x86_update_cr3(env, ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.cr3)));
+ /* we need to set the efer after the crs so the hidden flags get
+ set properly */
+ cpu_load_efer(env,
+ ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.efer)));
+ env->eflags = 0;
+ load_eflags(ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rflags)),
+ ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK));
+ CC_OP = CC_OP_EFLAGS;
+
+ svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.es),
+ env, R_ES);
+ svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.cs),
+ env, R_CS);
+ svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ss),
+ env, R_SS);
+ svm_load_seg_cache(env->vm_hsave + offsetof(struct vmcb, save.ds),
+ env, R_DS);
+
+ EIP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rip));
+ ESP = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rsp));
+ EAX = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.rax));
+
+ env->dr[6] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr6));
+ env->dr[7] = ldq_phys(env->vm_hsave + offsetof(struct vmcb, save.dr7));
+
+ /* other setups */
+ cpu_x86_set_cpl(env, 0);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_code), exit_code);
+ stq_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), exit_info_1);
+
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info),
+ ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj)));
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err),
+ ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err)));
+ stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0);
+
+ env->hflags2 &= ~HF2_GIF_MASK;
+ /* FIXME: Resets the current ASID register to zero (host ASID). */
+
+ /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */
+
+ /* Clears the TSC_OFFSET inside the processor. */
+
+ /* If the host is in PAE mode, the processor reloads the host's PDPEs
+ from the page table indicated the host's CR3. If the PDPEs contain
+ illegal state, the processor causes a shutdown. */
+
+ /* Forces CR0.PE = 1, RFLAGS.VM = 0. */
+ env->cr[0] |= CR0_PE_MASK;
+ env->eflags &= ~VM_MASK;
+
+ /* Disables all breakpoints in the host DR7 register. */
+
+ /* Checks the reloaded host state for consistency. */
+
+ /* If the host's rIP reloaded by #VMEXIT is outside the limit of the
+ host's code segment or non-canonical (in the case of long mode), a
+ #GP fault is delivered inside the host.) */
+
+ /* remove any pending exception */
+ env->exception_index = -1;
+ env->error_code = 0;
+ env->old_exception = -1;
+
+ cpu_loop_exit();
+}
+
+#endif
+
+/* MMX/SSE */
+/* XXX: optimize by storing fptt and fptags in the static cpu state */
+void helper_enter_mmx(void)
+{
+ env->fpstt = 0;
+ *(uint32_t *)(env->fptags) = 0;
+ *(uint32_t *)(env->fptags + 4) = 0;
+}
+
+void helper_emms(void)
+{
+ /* set to empty state */
+ *(uint32_t *)(env->fptags) = 0x01010101;
+ *(uint32_t *)(env->fptags + 4) = 0x01010101;
+}
+
+/* XXX: suppress */
+void helper_movq(void *d, void *s)
+{
+ *(uint64_t *)d = *(uint64_t *)s;
+}
+
+#define SHIFT 0
+#include "ops_sse.h"
+
+#define SHIFT 1
+#include "ops_sse.h"
+
+#define SHIFT 0
+#include "helper_template.h"
+#undef SHIFT
+
+#define SHIFT 1
+#include "helper_template.h"
+#undef SHIFT
+
+#define SHIFT 2
+#include "helper_template.h"
+#undef SHIFT
+
+#ifdef TARGET_X86_64
+
+#define SHIFT 3
+#include "helper_template.h"
+#undef SHIFT
+
+#endif
+
+/* bit operations */
+target_ulong helper_bsf(target_ulong t0)
+{
+ int count;
+ target_ulong res;
+
+ res = t0;
+ count = 0;
+ while ((res & 1) == 0) {
+ count++;
+ res >>= 1;
+ }
+ return count;
+}
+
+target_ulong helper_lzcnt(target_ulong t0, int wordsize)
+{
+ int count;
+ target_ulong res, mask;
+
+ if (wordsize > 0 && t0 == 0) {
+ return wordsize;
+ }
+ res = t0;
+ count = TARGET_LONG_BITS - 1;
+ mask = (target_ulong)1 << (TARGET_LONG_BITS - 1);
+ while ((res & mask) == 0) {
+ count--;
+ res <<= 1;
+ }
+ if (wordsize > 0) {
+ return wordsize - 1 - count;
+ }
+ return count;
+}
+
+target_ulong helper_bsr(target_ulong t0)
+{
+ return helper_lzcnt(t0, 0);
+}
+
+static int compute_all_eflags(void)
+{
+ return CC_SRC;
+}
+
+static int compute_c_eflags(void)
+{
+ return CC_SRC & CC_C;
+}
+
+uint32_t helper_cc_compute_all(int op)
+{
+ switch (op) {
+ default: /* should never happen */ return 0;
+
+ case CC_OP_EFLAGS: return compute_all_eflags();
+
+ case CC_OP_MULB: return compute_all_mulb();
+ case CC_OP_MULW: return compute_all_mulw();
+ case CC_OP_MULL: return compute_all_mull();
+
+ case CC_OP_ADDB: return compute_all_addb();
+ case CC_OP_ADDW: return compute_all_addw();
+ case CC_OP_ADDL: return compute_all_addl();
+
+ case CC_OP_ADCB: return compute_all_adcb();
+ case CC_OP_ADCW: return compute_all_adcw();
+ case CC_OP_ADCL: return compute_all_adcl();
+
+ case CC_OP_SUBB: return compute_all_subb();
+ case CC_OP_SUBW: return compute_all_subw();
+ case CC_OP_SUBL: return compute_all_subl();
+
+ case CC_OP_SBBB: return compute_all_sbbb();
+ case CC_OP_SBBW: return compute_all_sbbw();
+ case CC_OP_SBBL: return compute_all_sbbl();
+
+ case CC_OP_LOGICB: return compute_all_logicb();
+ case CC_OP_LOGICW: return compute_all_logicw();
+ case CC_OP_LOGICL: return compute_all_logicl();
+
+ case CC_OP_INCB: return compute_all_incb();
+ case CC_OP_INCW: return compute_all_incw();
+ case CC_OP_INCL: return compute_all_incl();
+
+ case CC_OP_DECB: return compute_all_decb();
+ case CC_OP_DECW: return compute_all_decw();
+ case CC_OP_DECL: return compute_all_decl();
+
+ case CC_OP_SHLB: return compute_all_shlb();
+ case CC_OP_SHLW: return compute_all_shlw();
+ case CC_OP_SHLL: return compute_all_shll();
+
+ case CC_OP_SARB: return compute_all_sarb();
+ case CC_OP_SARW: return compute_all_sarw();
+ case CC_OP_SARL: return compute_all_sarl();
+
+#ifdef TARGET_X86_64
+ case CC_OP_MULQ: return compute_all_mulq();
+
+ case CC_OP_ADDQ: return compute_all_addq();
+
+ case CC_OP_ADCQ: return compute_all_adcq();
+
+ case CC_OP_SUBQ: return compute_all_subq();
+
+ case CC_OP_SBBQ: return compute_all_sbbq();
+
+ case CC_OP_LOGICQ: return compute_all_logicq();
+
+ case CC_OP_INCQ: return compute_all_incq();
+
+ case CC_OP_DECQ: return compute_all_decq();
+
+ case CC_OP_SHLQ: return compute_all_shlq();
+
+ case CC_OP_SARQ: return compute_all_sarq();
+#endif
+ }
+}
+
+uint32_t helper_cc_compute_c(int op)
+{
+ switch (op) {
+ default: /* should never happen */ return 0;
+
+ case CC_OP_EFLAGS: return compute_c_eflags();
+
+ case CC_OP_MULB: return compute_c_mull();
+ case CC_OP_MULW: return compute_c_mull();
+ case CC_OP_MULL: return compute_c_mull();
+
+ case CC_OP_ADDB: return compute_c_addb();
+ case CC_OP_ADDW: return compute_c_addw();
+ case CC_OP_ADDL: return compute_c_addl();
+
+ case CC_OP_ADCB: return compute_c_adcb();
+ case CC_OP_ADCW: return compute_c_adcw();
+ case CC_OP_ADCL: return compute_c_adcl();
+
+ case CC_OP_SUBB: return compute_c_subb();
+ case CC_OP_SUBW: return compute_c_subw();
+ case CC_OP_SUBL: return compute_c_subl();
+
+ case CC_OP_SBBB: return compute_c_sbbb();
+ case CC_OP_SBBW: return compute_c_sbbw();
+ case CC_OP_SBBL: return compute_c_sbbl();
+
+ case CC_OP_LOGICB: return compute_c_logicb();
+ case CC_OP_LOGICW: return compute_c_logicw();
+ case CC_OP_LOGICL: return compute_c_logicl();
+
+ case CC_OP_INCB: return compute_c_incl();
+ case CC_OP_INCW: return compute_c_incl();
+ case CC_OP_INCL: return compute_c_incl();
+
+ case CC_OP_DECB: return compute_c_incl();
+ case CC_OP_DECW: return compute_c_incl();
+ case CC_OP_DECL: return compute_c_incl();
+
+ case CC_OP_SHLB: return compute_c_shlb();
+ case CC_OP_SHLW: return compute_c_shlw();
+ case CC_OP_SHLL: return compute_c_shll();
+
+ case CC_OP_SARB: return compute_c_sarl();
+ case CC_OP_SARW: return compute_c_sarl();
+ case CC_OP_SARL: return compute_c_sarl();
+
+#ifdef TARGET_X86_64
+ case CC_OP_MULQ: return compute_c_mull();
+
+ case CC_OP_ADDQ: return compute_c_addq();
+
+ case CC_OP_ADCQ: return compute_c_adcq();
+
+ case CC_OP_SUBQ: return compute_c_subq();
+
+ case CC_OP_SBBQ: return compute_c_sbbq();
+
+ case CC_OP_LOGICQ: return compute_c_logicq();
+
+ case CC_OP_INCQ: return compute_c_incl();
+
+ case CC_OP_DECQ: return compute_c_incl();
+
+ case CC_OP_SHLQ: return compute_c_shlq();
+
+ case CC_OP_SARQ: return compute_c_sarl();
+#endif
+ }
+}
diff --git a/target-i386/ops_sse.h b/target-i386/ops_sse.h
new file mode 100644
index 0000000..3232abd
--- /dev/null
+++ b/target-i386/ops_sse.h
@@ -0,0 +1,2075 @@
+/*
+ * MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2008 Intel Corporation <andrew.zaborowski@intel.com>
+ *
+ * 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/>.
+ */
+#if SHIFT == 0
+#define Reg MMXReg
+#define XMM_ONLY(...)
+#define B(n) MMX_B(n)
+#define W(n) MMX_W(n)
+#define L(n) MMX_L(n)
+#define Q(n) q
+#define SUFFIX _mmx
+#else
+#define Reg XMMReg
+#define XMM_ONLY(...) __VA_ARGS__
+#define B(n) XMM_B(n)
+#define W(n) XMM_W(n)
+#define L(n) XMM_L(n)
+#define Q(n) XMM_Q(n)
+#define SUFFIX _xmm
+#endif
+
+void glue(helper_psrlw, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 15) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->W(0) >>= shift;
+ d->W(1) >>= shift;
+ d->W(2) >>= shift;
+ d->W(3) >>= shift;
+#if SHIFT == 1
+ d->W(4) >>= shift;
+ d->W(5) >>= shift;
+ d->W(6) >>= shift;
+ d->W(7) >>= shift;
+#endif
+ }
+}
+
+void glue(helper_psraw, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 15) {
+ shift = 15;
+ } else {
+ shift = s->B(0);
+ }
+ d->W(0) = (int16_t)d->W(0) >> shift;
+ d->W(1) = (int16_t)d->W(1) >> shift;
+ d->W(2) = (int16_t)d->W(2) >> shift;
+ d->W(3) = (int16_t)d->W(3) >> shift;
+#if SHIFT == 1
+ d->W(4) = (int16_t)d->W(4) >> shift;
+ d->W(5) = (int16_t)d->W(5) >> shift;
+ d->W(6) = (int16_t)d->W(6) >> shift;
+ d->W(7) = (int16_t)d->W(7) >> shift;
+#endif
+}
+
+void glue(helper_psllw, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 15) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->W(0) <<= shift;
+ d->W(1) <<= shift;
+ d->W(2) <<= shift;
+ d->W(3) <<= shift;
+#if SHIFT == 1
+ d->W(4) <<= shift;
+ d->W(5) <<= shift;
+ d->W(6) <<= shift;
+ d->W(7) <<= shift;
+#endif
+ }
+}
+
+void glue(helper_psrld, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 31) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->L(0) >>= shift;
+ d->L(1) >>= shift;
+#if SHIFT == 1
+ d->L(2) >>= shift;
+ d->L(3) >>= shift;
+#endif
+ }
+}
+
+void glue(helper_psrad, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 31) {
+ shift = 31;
+ } else {
+ shift = s->B(0);
+ }
+ d->L(0) = (int32_t)d->L(0) >> shift;
+ d->L(1) = (int32_t)d->L(1) >> shift;
+#if SHIFT == 1
+ d->L(2) = (int32_t)d->L(2) >> shift;
+ d->L(3) = (int32_t)d->L(3) >> shift;
+#endif
+}
+
+void glue(helper_pslld, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 31) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->L(0) <<= shift;
+ d->L(1) <<= shift;
+#if SHIFT == 1
+ d->L(2) <<= shift;
+ d->L(3) <<= shift;
+#endif
+ }
+}
+
+void glue(helper_psrlq, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 63) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->Q(0) >>= shift;
+#if SHIFT == 1
+ d->Q(1) >>= shift;
+#endif
+ }
+}
+
+void glue(helper_psllq, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift;
+
+ if (s->Q(0) > 63) {
+ d->Q(0) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+ } else {
+ shift = s->B(0);
+ d->Q(0) <<= shift;
+#if SHIFT == 1
+ d->Q(1) <<= shift;
+#endif
+ }
+}
+
+#if SHIFT == 1
+void glue(helper_psrldq, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift, i;
+
+ shift = s->L(0);
+ if (shift > 16)
+ shift = 16;
+ for(i = 0; i < 16 - shift; i++)
+ d->B(i) = d->B(i + shift);
+ for(i = 16 - shift; i < 16; i++)
+ d->B(i) = 0;
+}
+
+void glue(helper_pslldq, SUFFIX)(Reg *d, Reg *s)
+{
+ int shift, i;
+
+ shift = s->L(0);
+ if (shift > 16)
+ shift = 16;
+ for(i = 15; i >= shift; i--)
+ d->B(i) = d->B(i - shift);
+ for(i = 0; i < shift; i++)
+ d->B(i) = 0;
+}
+#endif
+
+#define SSE_HELPER_B(name, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s)\
+{\
+ d->B(0) = F(d->B(0), s->B(0));\
+ d->B(1) = F(d->B(1), s->B(1));\
+ d->B(2) = F(d->B(2), s->B(2));\
+ d->B(3) = F(d->B(3), s->B(3));\
+ d->B(4) = F(d->B(4), s->B(4));\
+ d->B(5) = F(d->B(5), s->B(5));\
+ d->B(6) = F(d->B(6), s->B(6));\
+ d->B(7) = F(d->B(7), s->B(7));\
+ XMM_ONLY(\
+ d->B(8) = F(d->B(8), s->B(8));\
+ d->B(9) = F(d->B(9), s->B(9));\
+ d->B(10) = F(d->B(10), s->B(10));\
+ d->B(11) = F(d->B(11), s->B(11));\
+ d->B(12) = F(d->B(12), s->B(12));\
+ d->B(13) = F(d->B(13), s->B(13));\
+ d->B(14) = F(d->B(14), s->B(14));\
+ d->B(15) = F(d->B(15), s->B(15));\
+ )\
+}
+
+#define SSE_HELPER_W(name, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s)\
+{\
+ d->W(0) = F(d->W(0), s->W(0));\
+ d->W(1) = F(d->W(1), s->W(1));\
+ d->W(2) = F(d->W(2), s->W(2));\
+ d->W(3) = F(d->W(3), s->W(3));\
+ XMM_ONLY(\
+ d->W(4) = F(d->W(4), s->W(4));\
+ d->W(5) = F(d->W(5), s->W(5));\
+ d->W(6) = F(d->W(6), s->W(6));\
+ d->W(7) = F(d->W(7), s->W(7));\
+ )\
+}
+
+#define SSE_HELPER_L(name, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s)\
+{\
+ d->L(0) = F(d->L(0), s->L(0));\
+ d->L(1) = F(d->L(1), s->L(1));\
+ XMM_ONLY(\
+ d->L(2) = F(d->L(2), s->L(2));\
+ d->L(3) = F(d->L(3), s->L(3));\
+ )\
+}
+
+#define SSE_HELPER_Q(name, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s)\
+{\
+ d->Q(0) = F(d->Q(0), s->Q(0));\
+ XMM_ONLY(\
+ d->Q(1) = F(d->Q(1), s->Q(1));\
+ )\
+}
+
+#if SHIFT == 0
+static inline int satub(int x)
+{
+ if (x < 0)
+ return 0;
+ else if (x > 255)
+ return 255;
+ else
+ return x;
+}
+
+static inline int satuw(int x)
+{
+ if (x < 0)
+ return 0;
+ else if (x > 65535)
+ return 65535;
+ else
+ return x;
+}
+
+static inline int satsb(int x)
+{
+ if (x < -128)
+ return -128;
+ else if (x > 127)
+ return 127;
+ else
+ return x;
+}
+
+static inline int satsw(int x)
+{
+ if (x < -32768)
+ return -32768;
+ else if (x > 32767)
+ return 32767;
+ else
+ return x;
+}
+
+#define FADD(a, b) ((a) + (b))
+#define FADDUB(a, b) satub((a) + (b))
+#define FADDUW(a, b) satuw((a) + (b))
+#define FADDSB(a, b) satsb((int8_t)(a) + (int8_t)(b))
+#define FADDSW(a, b) satsw((int16_t)(a) + (int16_t)(b))
+
+#define FSUB(a, b) ((a) - (b))
+#define FSUBUB(a, b) satub((a) - (b))
+#define FSUBUW(a, b) satuw((a) - (b))
+#define FSUBSB(a, b) satsb((int8_t)(a) - (int8_t)(b))
+#define FSUBSW(a, b) satsw((int16_t)(a) - (int16_t)(b))
+#define FMINUB(a, b) ((a) < (b)) ? (a) : (b)
+#define FMINSW(a, b) ((int16_t)(a) < (int16_t)(b)) ? (a) : (b)
+#define FMAXUB(a, b) ((a) > (b)) ? (a) : (b)
+#define FMAXSW(a, b) ((int16_t)(a) > (int16_t)(b)) ? (a) : (b)
+
+#define FAND(a, b) (a) & (b)
+#define FANDN(a, b) ((~(a)) & (b))
+#define FOR(a, b) (a) | (b)
+#define FXOR(a, b) (a) ^ (b)
+
+#define FCMPGTB(a, b) (int8_t)(a) > (int8_t)(b) ? -1 : 0
+#define FCMPGTW(a, b) (int16_t)(a) > (int16_t)(b) ? -1 : 0
+#define FCMPGTL(a, b) (int32_t)(a) > (int32_t)(b) ? -1 : 0
+#define FCMPEQ(a, b) (a) == (b) ? -1 : 0
+
+#define FMULLW(a, b) (a) * (b)
+#define FMULHRW(a, b) ((int16_t)(a) * (int16_t)(b) + 0x8000) >> 16
+#define FMULHUW(a, b) (a) * (b) >> 16
+#define FMULHW(a, b) (int16_t)(a) * (int16_t)(b) >> 16
+
+#define FAVG(a, b) ((a) + (b) + 1) >> 1
+#endif
+
+SSE_HELPER_B(helper_paddb, FADD)
+SSE_HELPER_W(helper_paddw, FADD)
+SSE_HELPER_L(helper_paddl, FADD)
+SSE_HELPER_Q(helper_paddq, FADD)
+
+SSE_HELPER_B(helper_psubb, FSUB)
+SSE_HELPER_W(helper_psubw, FSUB)
+SSE_HELPER_L(helper_psubl, FSUB)
+SSE_HELPER_Q(helper_psubq, FSUB)
+
+SSE_HELPER_B(helper_paddusb, FADDUB)
+SSE_HELPER_B(helper_paddsb, FADDSB)
+SSE_HELPER_B(helper_psubusb, FSUBUB)
+SSE_HELPER_B(helper_psubsb, FSUBSB)
+
+SSE_HELPER_W(helper_paddusw, FADDUW)
+SSE_HELPER_W(helper_paddsw, FADDSW)
+SSE_HELPER_W(helper_psubusw, FSUBUW)
+SSE_HELPER_W(helper_psubsw, FSUBSW)
+
+SSE_HELPER_B(helper_pminub, FMINUB)
+SSE_HELPER_B(helper_pmaxub, FMAXUB)
+
+SSE_HELPER_W(helper_pminsw, FMINSW)
+SSE_HELPER_W(helper_pmaxsw, FMAXSW)
+
+SSE_HELPER_Q(helper_pand, FAND)
+SSE_HELPER_Q(helper_pandn, FANDN)
+SSE_HELPER_Q(helper_por, FOR)
+SSE_HELPER_Q(helper_pxor, FXOR)
+
+SSE_HELPER_B(helper_pcmpgtb, FCMPGTB)
+SSE_HELPER_W(helper_pcmpgtw, FCMPGTW)
+SSE_HELPER_L(helper_pcmpgtl, FCMPGTL)
+
+SSE_HELPER_B(helper_pcmpeqb, FCMPEQ)
+SSE_HELPER_W(helper_pcmpeqw, FCMPEQ)
+SSE_HELPER_L(helper_pcmpeql, FCMPEQ)
+
+SSE_HELPER_W(helper_pmullw, FMULLW)
+#if SHIFT == 0
+SSE_HELPER_W(helper_pmulhrw, FMULHRW)
+#endif
+SSE_HELPER_W(helper_pmulhuw, FMULHUW)
+SSE_HELPER_W(helper_pmulhw, FMULHW)
+
+SSE_HELPER_B(helper_pavgb, FAVG)
+SSE_HELPER_W(helper_pavgw, FAVG)
+
+void glue(helper_pmuludq, SUFFIX) (Reg *d, Reg *s)
+{
+ d->Q(0) = (uint64_t)s->L(0) * (uint64_t)d->L(0);
+#if SHIFT == 1
+ d->Q(1) = (uint64_t)s->L(2) * (uint64_t)d->L(2);
+#endif
+}
+
+void glue(helper_pmaddwd, SUFFIX) (Reg *d, Reg *s)
+{
+ int i;
+
+ for(i = 0; i < (2 << SHIFT); i++) {
+ d->L(i) = (int16_t)s->W(2*i) * (int16_t)d->W(2*i) +
+ (int16_t)s->W(2*i+1) * (int16_t)d->W(2*i+1);
+ }
+}
+
+#if SHIFT == 0
+static inline int abs1(int a)
+{
+ if (a < 0)
+ return -a;
+ else
+ return a;
+}
+#endif
+void glue(helper_psadbw, SUFFIX) (Reg *d, Reg *s)
+{
+ unsigned int val;
+
+ val = 0;
+ val += abs1(d->B(0) - s->B(0));
+ val += abs1(d->B(1) - s->B(1));
+ val += abs1(d->B(2) - s->B(2));
+ val += abs1(d->B(3) - s->B(3));
+ val += abs1(d->B(4) - s->B(4));
+ val += abs1(d->B(5) - s->B(5));
+ val += abs1(d->B(6) - s->B(6));
+ val += abs1(d->B(7) - s->B(7));
+ d->Q(0) = val;
+#if SHIFT == 1
+ val = 0;
+ val += abs1(d->B(8) - s->B(8));
+ val += abs1(d->B(9) - s->B(9));
+ val += abs1(d->B(10) - s->B(10));
+ val += abs1(d->B(11) - s->B(11));
+ val += abs1(d->B(12) - s->B(12));
+ val += abs1(d->B(13) - s->B(13));
+ val += abs1(d->B(14) - s->B(14));
+ val += abs1(d->B(15) - s->B(15));
+ d->Q(1) = val;
+#endif
+}
+
+void glue(helper_maskmov, SUFFIX) (Reg *d, Reg *s, target_ulong a0)
+{
+ int i;
+ for(i = 0; i < (8 << SHIFT); i++) {
+ if (s->B(i) & 0x80)
+ stb(a0 + i, d->B(i));
+ }
+}
+
+void glue(helper_movl_mm_T0, SUFFIX) (Reg *d, uint32_t val)
+{
+ d->L(0) = val;
+ d->L(1) = 0;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+}
+
+#ifdef TARGET_X86_64
+void glue(helper_movq_mm_T0, SUFFIX) (Reg *d, uint64_t val)
+{
+ d->Q(0) = val;
+#if SHIFT == 1
+ d->Q(1) = 0;
+#endif
+}
+#endif
+
+#if SHIFT == 0
+void glue(helper_pshufw, SUFFIX) (Reg *d, Reg *s, int order)
+{
+ Reg r;
+ r.W(0) = s->W(order & 3);
+ r.W(1) = s->W((order >> 2) & 3);
+ r.W(2) = s->W((order >> 4) & 3);
+ r.W(3) = s->W((order >> 6) & 3);
+ *d = r;
+}
+#else
+void helper_shufps(Reg *d, Reg *s, int order)
+{
+ Reg r;
+ r.L(0) = d->L(order & 3);
+ r.L(1) = d->L((order >> 2) & 3);
+ r.L(2) = s->L((order >> 4) & 3);
+ r.L(3) = s->L((order >> 6) & 3);
+ *d = r;
+}
+
+void helper_shufpd(Reg *d, Reg *s, int order)
+{
+ Reg r;
+ r.Q(0) = d->Q(order & 1);
+ r.Q(1) = s->Q((order >> 1) & 1);
+ *d = r;
+}
+
+void glue(helper_pshufd, SUFFIX) (Reg *d, Reg *s, int order)
+{
+ Reg r;
+ r.L(0) = s->L(order & 3);
+ r.L(1) = s->L((order >> 2) & 3);
+ r.L(2) = s->L((order >> 4) & 3);
+ r.L(3) = s->L((order >> 6) & 3);
+ *d = r;
+}
+
+void glue(helper_pshuflw, SUFFIX) (Reg *d, Reg *s, int order)
+{
+ Reg r;
+ r.W(0) = s->W(order & 3);
+ r.W(1) = s->W((order >> 2) & 3);
+ r.W(2) = s->W((order >> 4) & 3);
+ r.W(3) = s->W((order >> 6) & 3);
+ r.Q(1) = s->Q(1);
+ *d = r;
+}
+
+void glue(helper_pshufhw, SUFFIX) (Reg *d, Reg *s, int order)
+{
+ Reg r;
+ r.Q(0) = s->Q(0);
+ r.W(4) = s->W(4 + (order & 3));
+ r.W(5) = s->W(4 + ((order >> 2) & 3));
+ r.W(6) = s->W(4 + ((order >> 4) & 3));
+ r.W(7) = s->W(4 + ((order >> 6) & 3));
+ *d = r;
+}
+#endif
+
+#if SHIFT == 1
+/* FPU ops */
+/* XXX: not accurate */
+
+#define SSE_HELPER_S(name, F)\
+void helper_ ## name ## ps (Reg *d, Reg *s)\
+{\
+ d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+ d->XMM_S(1) = F(32, d->XMM_S(1), s->XMM_S(1));\
+ d->XMM_S(2) = F(32, d->XMM_S(2), s->XMM_S(2));\
+ d->XMM_S(3) = F(32, d->XMM_S(3), s->XMM_S(3));\
+}\
+\
+void helper_ ## name ## ss (Reg *d, Reg *s)\
+{\
+ d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+}\
+void helper_ ## name ## pd (Reg *d, Reg *s)\
+{\
+ d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+ d->XMM_D(1) = F(64, d->XMM_D(1), s->XMM_D(1));\
+}\
+\
+void helper_ ## name ## sd (Reg *d, Reg *s)\
+{\
+ d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+}
+
+#define FPU_ADD(size, a, b) float ## size ## _add(a, b, &env->sse_status)
+#define FPU_SUB(size, a, b) float ## size ## _sub(a, b, &env->sse_status)
+#define FPU_MUL(size, a, b) float ## size ## _mul(a, b, &env->sse_status)
+#define FPU_DIV(size, a, b) float ## size ## _div(a, b, &env->sse_status)
+#define FPU_MIN(size, a, b) (a) < (b) ? (a) : (b)
+#define FPU_MAX(size, a, b) (a) > (b) ? (a) : (b)
+#define FPU_SQRT(size, a, b) float ## size ## _sqrt(b, &env->sse_status)
+
+SSE_HELPER_S(add, FPU_ADD)
+SSE_HELPER_S(sub, FPU_SUB)
+SSE_HELPER_S(mul, FPU_MUL)
+SSE_HELPER_S(div, FPU_DIV)
+SSE_HELPER_S(min, FPU_MIN)
+SSE_HELPER_S(max, FPU_MAX)
+SSE_HELPER_S(sqrt, FPU_SQRT)
+
+
+/* float to float conversions */
+void helper_cvtps2pd(Reg *d, Reg *s)
+{
+ float32 s0, s1;
+ s0 = s->XMM_S(0);
+ s1 = s->XMM_S(1);
+ d->XMM_D(0) = float32_to_float64(s0, &env->sse_status);
+ d->XMM_D(1) = float32_to_float64(s1, &env->sse_status);
+}
+
+void helper_cvtpd2ps(Reg *d, Reg *s)
+{
+ d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status);
+ d->XMM_S(1) = float64_to_float32(s->XMM_D(1), &env->sse_status);
+ d->Q(1) = 0;
+}
+
+void helper_cvtss2sd(Reg *d, Reg *s)
+{
+ d->XMM_D(0) = float32_to_float64(s->XMM_S(0), &env->sse_status);
+}
+
+void helper_cvtsd2ss(Reg *d, Reg *s)
+{
+ d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status);
+}
+
+/* integer to float */
+void helper_cvtdq2ps(Reg *d, Reg *s)
+{
+ d->XMM_S(0) = int32_to_float32(s->XMM_L(0), &env->sse_status);
+ d->XMM_S(1) = int32_to_float32(s->XMM_L(1), &env->sse_status);
+ d->XMM_S(2) = int32_to_float32(s->XMM_L(2), &env->sse_status);
+ d->XMM_S(3) = int32_to_float32(s->XMM_L(3), &env->sse_status);
+}
+
+void helper_cvtdq2pd(Reg *d, Reg *s)
+{
+ int32_t l0, l1;
+ l0 = (int32_t)s->XMM_L(0);
+ l1 = (int32_t)s->XMM_L(1);
+ d->XMM_D(0) = int32_to_float64(l0, &env->sse_status);
+ d->XMM_D(1) = int32_to_float64(l1, &env->sse_status);
+}
+
+void helper_cvtpi2ps(XMMReg *d, MMXReg *s)
+{
+ d->XMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status);
+ d->XMM_S(1) = int32_to_float32(s->MMX_L(1), &env->sse_status);
+}
+
+void helper_cvtpi2pd(XMMReg *d, MMXReg *s)
+{
+ d->XMM_D(0) = int32_to_float64(s->MMX_L(0), &env->sse_status);
+ d->XMM_D(1) = int32_to_float64(s->MMX_L(1), &env->sse_status);
+}
+
+void helper_cvtsi2ss(XMMReg *d, uint32_t val)
+{
+ d->XMM_S(0) = int32_to_float32(val, &env->sse_status);
+}
+
+void helper_cvtsi2sd(XMMReg *d, uint32_t val)
+{
+ d->XMM_D(0) = int32_to_float64(val, &env->sse_status);
+}
+
+#ifdef TARGET_X86_64
+void helper_cvtsq2ss(XMMReg *d, uint64_t val)
+{
+ d->XMM_S(0) = int64_to_float32(val, &env->sse_status);
+}
+
+void helper_cvtsq2sd(XMMReg *d, uint64_t val)
+{
+ d->XMM_D(0) = int64_to_float64(val, &env->sse_status);
+}
+#endif
+
+/* float to integer */
+void helper_cvtps2dq(XMMReg *d, XMMReg *s)
+{
+ d->XMM_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status);
+ d->XMM_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status);
+ d->XMM_L(2) = float32_to_int32(s->XMM_S(2), &env->sse_status);
+ d->XMM_L(3) = float32_to_int32(s->XMM_S(3), &env->sse_status);
+}
+
+void helper_cvtpd2dq(XMMReg *d, XMMReg *s)
+{
+ d->XMM_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status);
+ d->XMM_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status);
+ d->XMM_Q(1) = 0;
+}
+
+void helper_cvtps2pi(MMXReg *d, XMMReg *s)
+{
+ d->MMX_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status);
+ d->MMX_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status);
+}
+
+void helper_cvtpd2pi(MMXReg *d, XMMReg *s)
+{
+ d->MMX_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status);
+ d->MMX_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status);
+}
+
+int32_t helper_cvtss2si(XMMReg *s)
+{
+ return float32_to_int32(s->XMM_S(0), &env->sse_status);
+}
+
+int32_t helper_cvtsd2si(XMMReg *s)
+{
+ return float64_to_int32(s->XMM_D(0), &env->sse_status);
+}
+
+#ifdef TARGET_X86_64
+int64_t helper_cvtss2sq(XMMReg *s)
+{
+ return float32_to_int64(s->XMM_S(0), &env->sse_status);
+}
+
+int64_t helper_cvtsd2sq(XMMReg *s)
+{
+ return float64_to_int64(s->XMM_D(0), &env->sse_status);
+}
+#endif
+
+/* float to integer truncated */
+void helper_cvttps2dq(XMMReg *d, XMMReg *s)
+{
+ d->XMM_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
+ d->XMM_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
+ d->XMM_L(2) = float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status);
+ d->XMM_L(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status);
+}
+
+void helper_cvttpd2dq(XMMReg *d, XMMReg *s)
+{
+ d->XMM_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
+ d->XMM_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
+ d->XMM_Q(1) = 0;
+}
+
+void helper_cvttps2pi(MMXReg *d, XMMReg *s)
+{
+ d->MMX_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
+ d->MMX_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status);
+}
+
+void helper_cvttpd2pi(MMXReg *d, XMMReg *s)
+{
+ d->MMX_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
+ d->MMX_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status);
+}
+
+int32_t helper_cvttss2si(XMMReg *s)
+{
+ return float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status);
+}
+
+int32_t helper_cvttsd2si(XMMReg *s)
+{
+ return float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status);
+}
+
+#ifdef TARGET_X86_64
+int64_t helper_cvttss2sq(XMMReg *s)
+{
+ return float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status);
+}
+
+int64_t helper_cvttsd2sq(XMMReg *s)
+{
+ return float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status);
+}
+#endif
+
+void helper_rsqrtps(XMMReg *d, XMMReg *s)
+{
+ d->XMM_S(0) = approx_rsqrt(s->XMM_S(0));
+ d->XMM_S(1) = approx_rsqrt(s->XMM_S(1));
+ d->XMM_S(2) = approx_rsqrt(s->XMM_S(2));
+ d->XMM_S(3) = approx_rsqrt(s->XMM_S(3));
+}
+
+void helper_rsqrtss(XMMReg *d, XMMReg *s)
+{
+ d->XMM_S(0) = approx_rsqrt(s->XMM_S(0));
+}
+
+void helper_rcpps(XMMReg *d, XMMReg *s)
+{
+ d->XMM_S(0) = approx_rcp(s->XMM_S(0));
+ d->XMM_S(1) = approx_rcp(s->XMM_S(1));
+ d->XMM_S(2) = approx_rcp(s->XMM_S(2));
+ d->XMM_S(3) = approx_rcp(s->XMM_S(3));
+}
+
+void helper_rcpss(XMMReg *d, XMMReg *s)
+{
+ d->XMM_S(0) = approx_rcp(s->XMM_S(0));
+}
+
+static inline uint64_t helper_extrq(uint64_t src, int shift, int len)
+{
+ uint64_t mask;
+
+ if (len == 0) {
+ mask = ~0LL;
+ } else {
+ mask = (1ULL << len) - 1;
+ }
+ return (src >> shift) & mask;
+}
+
+void helper_extrq_r(XMMReg *d, XMMReg *s)
+{
+ d->XMM_Q(0) = helper_extrq(d->XMM_Q(0), s->XMM_B(1), s->XMM_B(0));
+}
+
+void helper_extrq_i(XMMReg *d, int index, int length)
+{
+ d->XMM_Q(0) = helper_extrq(d->XMM_Q(0), index, length);
+}
+
+static inline uint64_t helper_insertq(uint64_t src, int shift, int len)
+{
+ uint64_t mask;
+
+ if (len == 0) {
+ mask = ~0ULL;
+ } else {
+ mask = (1ULL << len) - 1;
+ }
+ return (src & ~(mask << shift)) | ((src & mask) << shift);
+}
+
+void helper_insertq_r(XMMReg *d, XMMReg *s)
+{
+ d->XMM_Q(0) = helper_insertq(s->XMM_Q(0), s->XMM_B(9), s->XMM_B(8));
+}
+
+void helper_insertq_i(XMMReg *d, int index, int length)
+{
+ d->XMM_Q(0) = helper_insertq(d->XMM_Q(0), index, length);
+}
+
+void helper_haddps(XMMReg *d, XMMReg *s)
+{
+ XMMReg r;
+ r.XMM_S(0) = d->XMM_S(0) + d->XMM_S(1);
+ r.XMM_S(1) = d->XMM_S(2) + d->XMM_S(3);
+ r.XMM_S(2) = s->XMM_S(0) + s->XMM_S(1);
+ r.XMM_S(3) = s->XMM_S(2) + s->XMM_S(3);
+ *d = r;
+}
+
+void helper_haddpd(XMMReg *d, XMMReg *s)
+{
+ XMMReg r;
+ r.XMM_D(0) = d->XMM_D(0) + d->XMM_D(1);
+ r.XMM_D(1) = s->XMM_D(0) + s->XMM_D(1);
+ *d = r;
+}
+
+void helper_hsubps(XMMReg *d, XMMReg *s)
+{
+ XMMReg r;
+ r.XMM_S(0) = d->XMM_S(0) - d->XMM_S(1);
+ r.XMM_S(1) = d->XMM_S(2) - d->XMM_S(3);
+ r.XMM_S(2) = s->XMM_S(0) - s->XMM_S(1);
+ r.XMM_S(3) = s->XMM_S(2) - s->XMM_S(3);
+ *d = r;
+}
+
+void helper_hsubpd(XMMReg *d, XMMReg *s)
+{
+ XMMReg r;
+ r.XMM_D(0) = d->XMM_D(0) - d->XMM_D(1);
+ r.XMM_D(1) = s->XMM_D(0) - s->XMM_D(1);
+ *d = r;
+}
+
+void helper_addsubps(XMMReg *d, XMMReg *s)
+{
+ d->XMM_S(0) = d->XMM_S(0) - s->XMM_S(0);
+ d->XMM_S(1) = d->XMM_S(1) + s->XMM_S(1);
+ d->XMM_S(2) = d->XMM_S(2) - s->XMM_S(2);
+ d->XMM_S(3) = d->XMM_S(3) + s->XMM_S(3);
+}
+
+void helper_addsubpd(XMMReg *d, XMMReg *s)
+{
+ d->XMM_D(0) = d->XMM_D(0) - s->XMM_D(0);
+ d->XMM_D(1) = d->XMM_D(1) + s->XMM_D(1);
+}
+
+/* XXX: unordered */
+#define SSE_HELPER_CMP(name, F)\
+void helper_ ## name ## ps (Reg *d, Reg *s)\
+{\
+ d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+ d->XMM_L(1) = F(32, d->XMM_S(1), s->XMM_S(1));\
+ d->XMM_L(2) = F(32, d->XMM_S(2), s->XMM_S(2));\
+ d->XMM_L(3) = F(32, d->XMM_S(3), s->XMM_S(3));\
+}\
+\
+void helper_ ## name ## ss (Reg *d, Reg *s)\
+{\
+ d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));\
+}\
+void helper_ ## name ## pd (Reg *d, Reg *s)\
+{\
+ d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+ d->XMM_Q(1) = F(64, d->XMM_D(1), s->XMM_D(1));\
+}\
+\
+void helper_ ## name ## sd (Reg *d, Reg *s)\
+{\
+ d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));\
+}
+
+#define FPU_CMPEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? -1 : 0
+#define FPU_CMPLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0
+#define FPU_CMPLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? -1 : 0
+#define FPU_CMPUNORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? - 1 : 0
+#define FPU_CMPNEQ(size, a, b) float ## size ## _eq(a, b, &env->sse_status) ? 0 : -1
+#define FPU_CMPNLT(size, a, b) float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1
+#define FPU_CMPNLE(size, a, b) float ## size ## _le(a, b, &env->sse_status) ? 0 : -1
+#define FPU_CMPORD(size, a, b) float ## size ## _unordered(a, b, &env->sse_status) ? 0 : -1
+
+SSE_HELPER_CMP(cmpeq, FPU_CMPEQ)
+SSE_HELPER_CMP(cmplt, FPU_CMPLT)
+SSE_HELPER_CMP(cmple, FPU_CMPLE)
+SSE_HELPER_CMP(cmpunord, FPU_CMPUNORD)
+SSE_HELPER_CMP(cmpneq, FPU_CMPNEQ)
+SSE_HELPER_CMP(cmpnlt, FPU_CMPNLT)
+SSE_HELPER_CMP(cmpnle, FPU_CMPNLE)
+SSE_HELPER_CMP(cmpord, FPU_CMPORD)
+
+static const int comis_eflags[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C};
+
+void helper_ucomiss(Reg *d, Reg *s)
+{
+ int ret;
+ float32 s0, s1;
+
+ s0 = d->XMM_S(0);
+ s1 = s->XMM_S(0);
+ ret = float32_compare_quiet(s0, s1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+}
+
+void helper_comiss(Reg *d, Reg *s)
+{
+ int ret;
+ float32 s0, s1;
+
+ s0 = d->XMM_S(0);
+ s1 = s->XMM_S(0);
+ ret = float32_compare(s0, s1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+}
+
+void helper_ucomisd(Reg *d, Reg *s)
+{
+ int ret;
+ float64 d0, d1;
+
+ d0 = d->XMM_D(0);
+ d1 = s->XMM_D(0);
+ ret = float64_compare_quiet(d0, d1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+}
+
+void helper_comisd(Reg *d, Reg *s)
+{
+ int ret;
+ float64 d0, d1;
+
+ d0 = d->XMM_D(0);
+ d1 = s->XMM_D(0);
+ ret = float64_compare(d0, d1, &env->sse_status);
+ CC_SRC = comis_eflags[ret + 1];
+}
+
+uint32_t helper_movmskps(Reg *s)
+{
+ int b0, b1, b2, b3;
+ b0 = s->XMM_L(0) >> 31;
+ b1 = s->XMM_L(1) >> 31;
+ b2 = s->XMM_L(2) >> 31;
+ b3 = s->XMM_L(3) >> 31;
+ return b0 | (b1 << 1) | (b2 << 2) | (b3 << 3);
+}
+
+uint32_t helper_movmskpd(Reg *s)
+{
+ int b0, b1;
+ b0 = s->XMM_L(1) >> 31;
+ b1 = s->XMM_L(3) >> 31;
+ return b0 | (b1 << 1);
+}
+
+#endif
+
+uint32_t glue(helper_pmovmskb, SUFFIX)(Reg *s)
+{
+ uint32_t val;
+ val = 0;
+ val |= (s->B(0) >> 7);
+ val |= (s->B(1) >> 6) & 0x02;
+ val |= (s->B(2) >> 5) & 0x04;
+ val |= (s->B(3) >> 4) & 0x08;
+ val |= (s->B(4) >> 3) & 0x10;
+ val |= (s->B(5) >> 2) & 0x20;
+ val |= (s->B(6) >> 1) & 0x40;
+ val |= (s->B(7)) & 0x80;
+#if SHIFT == 1
+ val |= (s->B(8) << 1) & 0x0100;
+ val |= (s->B(9) << 2) & 0x0200;
+ val |= (s->B(10) << 3) & 0x0400;
+ val |= (s->B(11) << 4) & 0x0800;
+ val |= (s->B(12) << 5) & 0x1000;
+ val |= (s->B(13) << 6) & 0x2000;
+ val |= (s->B(14) << 7) & 0x4000;
+ val |= (s->B(15) << 8) & 0x8000;
+#endif
+ return val;
+}
+
+void glue(helper_packsswb, SUFFIX) (Reg *d, Reg *s)
+{
+ Reg r;
+
+ r.B(0) = satsb((int16_t)d->W(0));
+ r.B(1) = satsb((int16_t)d->W(1));
+ r.B(2) = satsb((int16_t)d->W(2));
+ r.B(3) = satsb((int16_t)d->W(3));
+#if SHIFT == 1
+ r.B(4) = satsb((int16_t)d->W(4));
+ r.B(5) = satsb((int16_t)d->W(5));
+ r.B(6) = satsb((int16_t)d->W(6));
+ r.B(7) = satsb((int16_t)d->W(7));
+#endif
+ r.B((4 << SHIFT) + 0) = satsb((int16_t)s->W(0));
+ r.B((4 << SHIFT) + 1) = satsb((int16_t)s->W(1));
+ r.B((4 << SHIFT) + 2) = satsb((int16_t)s->W(2));
+ r.B((4 << SHIFT) + 3) = satsb((int16_t)s->W(3));
+#if SHIFT == 1
+ r.B(12) = satsb((int16_t)s->W(4));
+ r.B(13) = satsb((int16_t)s->W(5));
+ r.B(14) = satsb((int16_t)s->W(6));
+ r.B(15) = satsb((int16_t)s->W(7));
+#endif
+ *d = r;
+}
+
+void glue(helper_packuswb, SUFFIX) (Reg *d, Reg *s)
+{
+ Reg r;
+
+ r.B(0) = satub((int16_t)d->W(0));
+ r.B(1) = satub((int16_t)d->W(1));
+ r.B(2) = satub((int16_t)d->W(2));
+ r.B(3) = satub((int16_t)d->W(3));
+#if SHIFT == 1
+ r.B(4) = satub((int16_t)d->W(4));
+ r.B(5) = satub((int16_t)d->W(5));
+ r.B(6) = satub((int16_t)d->W(6));
+ r.B(7) = satub((int16_t)d->W(7));
+#endif
+ r.B((4 << SHIFT) + 0) = satub((int16_t)s->W(0));
+ r.B((4 << SHIFT) + 1) = satub((int16_t)s->W(1));
+ r.B((4 << SHIFT) + 2) = satub((int16_t)s->W(2));
+ r.B((4 << SHIFT) + 3) = satub((int16_t)s->W(3));
+#if SHIFT == 1
+ r.B(12) = satub((int16_t)s->W(4));
+ r.B(13) = satub((int16_t)s->W(5));
+ r.B(14) = satub((int16_t)s->W(6));
+ r.B(15) = satub((int16_t)s->W(7));
+#endif
+ *d = r;
+}
+
+void glue(helper_packssdw, SUFFIX) (Reg *d, Reg *s)
+{
+ Reg r;
+
+ r.W(0) = satsw(d->L(0));
+ r.W(1) = satsw(d->L(1));
+#if SHIFT == 1
+ r.W(2) = satsw(d->L(2));
+ r.W(3) = satsw(d->L(3));
+#endif
+ r.W((2 << SHIFT) + 0) = satsw(s->L(0));
+ r.W((2 << SHIFT) + 1) = satsw(s->L(1));
+#if SHIFT == 1
+ r.W(6) = satsw(s->L(2));
+ r.W(7) = satsw(s->L(3));
+#endif
+ *d = r;
+}
+
+#define UNPCK_OP(base_name, base) \
+ \
+void glue(helper_punpck ## base_name ## bw, SUFFIX) (Reg *d, Reg *s) \
+{ \
+ Reg r; \
+ \
+ r.B(0) = d->B((base << (SHIFT + 2)) + 0); \
+ r.B(1) = s->B((base << (SHIFT + 2)) + 0); \
+ r.B(2) = d->B((base << (SHIFT + 2)) + 1); \
+ r.B(3) = s->B((base << (SHIFT + 2)) + 1); \
+ r.B(4) = d->B((base << (SHIFT + 2)) + 2); \
+ r.B(5) = s->B((base << (SHIFT + 2)) + 2); \
+ r.B(6) = d->B((base << (SHIFT + 2)) + 3); \
+ r.B(7) = s->B((base << (SHIFT + 2)) + 3); \
+XMM_ONLY( \
+ r.B(8) = d->B((base << (SHIFT + 2)) + 4); \
+ r.B(9) = s->B((base << (SHIFT + 2)) + 4); \
+ r.B(10) = d->B((base << (SHIFT + 2)) + 5); \
+ r.B(11) = s->B((base << (SHIFT + 2)) + 5); \
+ r.B(12) = d->B((base << (SHIFT + 2)) + 6); \
+ r.B(13) = s->B((base << (SHIFT + 2)) + 6); \
+ r.B(14) = d->B((base << (SHIFT + 2)) + 7); \
+ r.B(15) = s->B((base << (SHIFT + 2)) + 7); \
+) \
+ *d = r; \
+} \
+ \
+void glue(helper_punpck ## base_name ## wd, SUFFIX) (Reg *d, Reg *s) \
+{ \
+ Reg r; \
+ \
+ r.W(0) = d->W((base << (SHIFT + 1)) + 0); \
+ r.W(1) = s->W((base << (SHIFT + 1)) + 0); \
+ r.W(2) = d->W((base << (SHIFT + 1)) + 1); \
+ r.W(3) = s->W((base << (SHIFT + 1)) + 1); \
+XMM_ONLY( \
+ r.W(4) = d->W((base << (SHIFT + 1)) + 2); \
+ r.W(5) = s->W((base << (SHIFT + 1)) + 2); \
+ r.W(6) = d->W((base << (SHIFT + 1)) + 3); \
+ r.W(7) = s->W((base << (SHIFT + 1)) + 3); \
+) \
+ *d = r; \
+} \
+ \
+void glue(helper_punpck ## base_name ## dq, SUFFIX) (Reg *d, Reg *s) \
+{ \
+ Reg r; \
+ \
+ r.L(0) = d->L((base << SHIFT) + 0); \
+ r.L(1) = s->L((base << SHIFT) + 0); \
+XMM_ONLY( \
+ r.L(2) = d->L((base << SHIFT) + 1); \
+ r.L(3) = s->L((base << SHIFT) + 1); \
+) \
+ *d = r; \
+} \
+ \
+XMM_ONLY( \
+void glue(helper_punpck ## base_name ## qdq, SUFFIX) (Reg *d, Reg *s) \
+{ \
+ Reg r; \
+ \
+ r.Q(0) = d->Q(base); \
+ r.Q(1) = s->Q(base); \
+ *d = r; \
+} \
+)
+
+UNPCK_OP(l, 0)
+UNPCK_OP(h, 1)
+
+/* 3DNow! float ops */
+#if SHIFT == 0
+void helper_pi2fd(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = int32_to_float32(s->MMX_L(0), &env->mmx_status);
+ d->MMX_S(1) = int32_to_float32(s->MMX_L(1), &env->mmx_status);
+}
+
+void helper_pi2fw(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = int32_to_float32((int16_t)s->MMX_W(0), &env->mmx_status);
+ d->MMX_S(1) = int32_to_float32((int16_t)s->MMX_W(2), &env->mmx_status);
+}
+
+void helper_pf2id(MMXReg *d, MMXReg *s)
+{
+ d->MMX_L(0) = float32_to_int32_round_to_zero(s->MMX_S(0), &env->mmx_status);
+ d->MMX_L(1) = float32_to_int32_round_to_zero(s->MMX_S(1), &env->mmx_status);
+}
+
+void helper_pf2iw(MMXReg *d, MMXReg *s)
+{
+ d->MMX_L(0) = satsw(float32_to_int32_round_to_zero(s->MMX_S(0), &env->mmx_status));
+ d->MMX_L(1) = satsw(float32_to_int32_round_to_zero(s->MMX_S(1), &env->mmx_status));
+}
+
+void helper_pfacc(MMXReg *d, MMXReg *s)
+{
+ MMXReg r;
+ r.MMX_S(0) = float32_add(d->MMX_S(0), d->MMX_S(1), &env->mmx_status);
+ r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status);
+ *d = r;
+}
+
+void helper_pfadd(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = float32_add(d->MMX_S(0), s->MMX_S(0), &env->mmx_status);
+ d->MMX_S(1) = float32_add(d->MMX_S(1), s->MMX_S(1), &env->mmx_status);
+}
+
+void helper_pfcmpeq(MMXReg *d, MMXReg *s)
+{
+ d->MMX_L(0) = float32_eq(d->MMX_S(0), s->MMX_S(0), &env->mmx_status) ? -1 : 0;
+ d->MMX_L(1) = float32_eq(d->MMX_S(1), s->MMX_S(1), &env->mmx_status) ? -1 : 0;
+}
+
+void helper_pfcmpge(MMXReg *d, MMXReg *s)
+{
+ d->MMX_L(0) = float32_le(s->MMX_S(0), d->MMX_S(0), &env->mmx_status) ? -1 : 0;
+ d->MMX_L(1) = float32_le(s->MMX_S(1), d->MMX_S(1), &env->mmx_status) ? -1 : 0;
+}
+
+void helper_pfcmpgt(MMXReg *d, MMXReg *s)
+{
+ d->MMX_L(0) = float32_lt(s->MMX_S(0), d->MMX_S(0), &env->mmx_status) ? -1 : 0;
+ d->MMX_L(1) = float32_lt(s->MMX_S(1), d->MMX_S(1), &env->mmx_status) ? -1 : 0;
+}
+
+void helper_pfmax(MMXReg *d, MMXReg *s)
+{
+ if (float32_lt(d->MMX_S(0), s->MMX_S(0), &env->mmx_status))
+ d->MMX_S(0) = s->MMX_S(0);
+ if (float32_lt(d->MMX_S(1), s->MMX_S(1), &env->mmx_status))
+ d->MMX_S(1) = s->MMX_S(1);
+}
+
+void helper_pfmin(MMXReg *d, MMXReg *s)
+{
+ if (float32_lt(s->MMX_S(0), d->MMX_S(0), &env->mmx_status))
+ d->MMX_S(0) = s->MMX_S(0);
+ if (float32_lt(s->MMX_S(1), d->MMX_S(1), &env->mmx_status))
+ d->MMX_S(1) = s->MMX_S(1);
+}
+
+void helper_pfmul(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = float32_mul(d->MMX_S(0), s->MMX_S(0), &env->mmx_status);
+ d->MMX_S(1) = float32_mul(d->MMX_S(1), s->MMX_S(1), &env->mmx_status);
+}
+
+void helper_pfnacc(MMXReg *d, MMXReg *s)
+{
+ MMXReg r;
+ r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status);
+ r.MMX_S(1) = float32_sub(s->MMX_S(0), s->MMX_S(1), &env->mmx_status);
+ *d = r;
+}
+
+void helper_pfpnacc(MMXReg *d, MMXReg *s)
+{
+ MMXReg r;
+ r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status);
+ r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status);
+ *d = r;
+}
+
+void helper_pfrcp(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = approx_rcp(s->MMX_S(0));
+ d->MMX_S(1) = d->MMX_S(0);
+}
+
+void helper_pfrsqrt(MMXReg *d, MMXReg *s)
+{
+ d->MMX_L(1) = s->MMX_L(0) & 0x7fffffff;
+ d->MMX_S(1) = approx_rsqrt(d->MMX_S(1));
+ d->MMX_L(1) |= s->MMX_L(0) & 0x80000000;
+ d->MMX_L(0) = d->MMX_L(1);
+}
+
+void helper_pfsub(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = float32_sub(d->MMX_S(0), s->MMX_S(0), &env->mmx_status);
+ d->MMX_S(1) = float32_sub(d->MMX_S(1), s->MMX_S(1), &env->mmx_status);
+}
+
+void helper_pfsubr(MMXReg *d, MMXReg *s)
+{
+ d->MMX_S(0) = float32_sub(s->MMX_S(0), d->MMX_S(0), &env->mmx_status);
+ d->MMX_S(1) = float32_sub(s->MMX_S(1), d->MMX_S(1), &env->mmx_status);
+}
+
+void helper_pswapd(MMXReg *d, MMXReg *s)
+{
+ MMXReg r;
+ r.MMX_L(0) = s->MMX_L(1);
+ r.MMX_L(1) = s->MMX_L(0);
+ *d = r;
+}
+#endif
+
+/* SSSE3 op helpers */
+void glue(helper_pshufb, SUFFIX) (Reg *d, Reg *s)
+{
+ int i;
+ Reg r;
+
+ for (i = 0; i < (8 << SHIFT); i++)
+ r.B(i) = (s->B(i) & 0x80) ? 0 : (d->B(s->B(i) & ((8 << SHIFT) - 1)));
+
+ *d = r;
+}
+
+void glue(helper_phaddw, SUFFIX) (Reg *d, Reg *s)
+{
+ d->W(0) = (int16_t)d->W(0) + (int16_t)d->W(1);
+ d->W(1) = (int16_t)d->W(2) + (int16_t)d->W(3);
+ XMM_ONLY(d->W(2) = (int16_t)d->W(4) + (int16_t)d->W(5));
+ XMM_ONLY(d->W(3) = (int16_t)d->W(6) + (int16_t)d->W(7));
+ d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) + (int16_t)s->W(1);
+ d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) + (int16_t)s->W(3);
+ XMM_ONLY(d->W(6) = (int16_t)s->W(4) + (int16_t)s->W(5));
+ XMM_ONLY(d->W(7) = (int16_t)s->W(6) + (int16_t)s->W(7));
+}
+
+void glue(helper_phaddd, SUFFIX) (Reg *d, Reg *s)
+{
+ d->L(0) = (int32_t)d->L(0) + (int32_t)d->L(1);
+ XMM_ONLY(d->L(1) = (int32_t)d->L(2) + (int32_t)d->L(3));
+ d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) + (int32_t)s->L(1);
+ XMM_ONLY(d->L(3) = (int32_t)s->L(2) + (int32_t)s->L(3));
+}
+
+void glue(helper_phaddsw, SUFFIX) (Reg *d, Reg *s)
+{
+ d->W(0) = satsw((int16_t)d->W(0) + (int16_t)d->W(1));
+ d->W(1) = satsw((int16_t)d->W(2) + (int16_t)d->W(3));
+ XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) + (int16_t)d->W(5)));
+ XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) + (int16_t)d->W(7)));
+ d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) + (int16_t)s->W(1));
+ d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) + (int16_t)s->W(3));
+ XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) + (int16_t)s->W(5)));
+ XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) + (int16_t)s->W(7)));
+}
+
+void glue(helper_pmaddubsw, SUFFIX) (Reg *d, Reg *s)
+{
+ d->W(0) = satsw((int8_t)s->B( 0) * (uint8_t)d->B( 0) +
+ (int8_t)s->B( 1) * (uint8_t)d->B( 1));
+ d->W(1) = satsw((int8_t)s->B( 2) * (uint8_t)d->B( 2) +
+ (int8_t)s->B( 3) * (uint8_t)d->B( 3));
+ d->W(2) = satsw((int8_t)s->B( 4) * (uint8_t)d->B( 4) +
+ (int8_t)s->B( 5) * (uint8_t)d->B( 5));
+ d->W(3) = satsw((int8_t)s->B( 6) * (uint8_t)d->B( 6) +
+ (int8_t)s->B( 7) * (uint8_t)d->B( 7));
+#if SHIFT == 1
+ d->W(4) = satsw((int8_t)s->B( 8) * (uint8_t)d->B( 8) +
+ (int8_t)s->B( 9) * (uint8_t)d->B( 9));
+ d->W(5) = satsw((int8_t)s->B(10) * (uint8_t)d->B(10) +
+ (int8_t)s->B(11) * (uint8_t)d->B(11));
+ d->W(6) = satsw((int8_t)s->B(12) * (uint8_t)d->B(12) +
+ (int8_t)s->B(13) * (uint8_t)d->B(13));
+ d->W(7) = satsw((int8_t)s->B(14) * (uint8_t)d->B(14) +
+ (int8_t)s->B(15) * (uint8_t)d->B(15));
+#endif
+}
+
+void glue(helper_phsubw, SUFFIX) (Reg *d, Reg *s)
+{
+ d->W(0) = (int16_t)d->W(0) - (int16_t)d->W(1);
+ d->W(1) = (int16_t)d->W(2) - (int16_t)d->W(3);
+ XMM_ONLY(d->W(2) = (int16_t)d->W(4) - (int16_t)d->W(5));
+ XMM_ONLY(d->W(3) = (int16_t)d->W(6) - (int16_t)d->W(7));
+ d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) - (int16_t)s->W(1);
+ d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) - (int16_t)s->W(3);
+ XMM_ONLY(d->W(6) = (int16_t)s->W(4) - (int16_t)s->W(5));
+ XMM_ONLY(d->W(7) = (int16_t)s->W(6) - (int16_t)s->W(7));
+}
+
+void glue(helper_phsubd, SUFFIX) (Reg *d, Reg *s)
+{
+ d->L(0) = (int32_t)d->L(0) - (int32_t)d->L(1);
+ XMM_ONLY(d->L(1) = (int32_t)d->L(2) - (int32_t)d->L(3));
+ d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) - (int32_t)s->L(1);
+ XMM_ONLY(d->L(3) = (int32_t)s->L(2) - (int32_t)s->L(3));
+}
+
+void glue(helper_phsubsw, SUFFIX) (Reg *d, Reg *s)
+{
+ d->W(0) = satsw((int16_t)d->W(0) - (int16_t)d->W(1));
+ d->W(1) = satsw((int16_t)d->W(2) - (int16_t)d->W(3));
+ XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) - (int16_t)d->W(5)));
+ XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) - (int16_t)d->W(7)));
+ d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) - (int16_t)s->W(1));
+ d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) - (int16_t)s->W(3));
+ XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) - (int16_t)s->W(5)));
+ XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) - (int16_t)s->W(7)));
+}
+
+#define FABSB(_, x) x > INT8_MAX ? -(int8_t ) x : x
+#define FABSW(_, x) x > INT16_MAX ? -(int16_t) x : x
+#define FABSL(_, x) x > INT32_MAX ? -(int32_t) x : x
+SSE_HELPER_B(helper_pabsb, FABSB)
+SSE_HELPER_W(helper_pabsw, FABSW)
+SSE_HELPER_L(helper_pabsd, FABSL)
+
+#define FMULHRSW(d, s) ((int16_t) d * (int16_t) s + 0x4000) >> 15
+SSE_HELPER_W(helper_pmulhrsw, FMULHRSW)
+
+#define FSIGNB(d, s) s <= INT8_MAX ? s ? d : 0 : -(int8_t ) d
+#define FSIGNW(d, s) s <= INT16_MAX ? s ? d : 0 : -(int16_t) d
+#define FSIGNL(d, s) s <= INT32_MAX ? s ? d : 0 : -(int32_t) d
+SSE_HELPER_B(helper_psignb, FSIGNB)
+SSE_HELPER_W(helper_psignw, FSIGNW)
+SSE_HELPER_L(helper_psignd, FSIGNL)
+
+void glue(helper_palignr, SUFFIX) (Reg *d, Reg *s, int32_t shift)
+{
+ Reg r;
+
+ /* XXX could be checked during translation */
+ if (shift >= (16 << SHIFT)) {
+ r.Q(0) = 0;
+ XMM_ONLY(r.Q(1) = 0);
+ } else {
+ shift <<= 3;
+#define SHR(v, i) (i < 64 && i > -64 ? i > 0 ? v >> (i) : (v << -(i)) : 0)
+#if SHIFT == 0
+ r.Q(0) = SHR(s->Q(0), shift - 0) |
+ SHR(d->Q(0), shift - 64);
+#else
+ r.Q(0) = SHR(s->Q(0), shift - 0) |
+ SHR(s->Q(1), shift - 64) |
+ SHR(d->Q(0), shift - 128) |
+ SHR(d->Q(1), shift - 192);
+ r.Q(1) = SHR(s->Q(0), shift + 64) |
+ SHR(s->Q(1), shift - 0) |
+ SHR(d->Q(0), shift - 64) |
+ SHR(d->Q(1), shift - 128);
+#endif
+#undef SHR
+ }
+
+ *d = r;
+}
+
+#define XMM0 env->xmm_regs[0]
+
+#if SHIFT == 1
+#define SSE_HELPER_V(name, elem, num, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s)\
+{\
+ d->elem(0) = F(d->elem(0), s->elem(0), XMM0.elem(0));\
+ d->elem(1) = F(d->elem(1), s->elem(1), XMM0.elem(1));\
+ if (num > 2) {\
+ d->elem(2) = F(d->elem(2), s->elem(2), XMM0.elem(2));\
+ d->elem(3) = F(d->elem(3), s->elem(3), XMM0.elem(3));\
+ if (num > 4) {\
+ d->elem(4) = F(d->elem(4), s->elem(4), XMM0.elem(4));\
+ d->elem(5) = F(d->elem(5), s->elem(5), XMM0.elem(5));\
+ d->elem(6) = F(d->elem(6), s->elem(6), XMM0.elem(6));\
+ d->elem(7) = F(d->elem(7), s->elem(7), XMM0.elem(7));\
+ if (num > 8) {\
+ d->elem(8) = F(d->elem(8), s->elem(8), XMM0.elem(8));\
+ d->elem(9) = F(d->elem(9), s->elem(9), XMM0.elem(9));\
+ d->elem(10) = F(d->elem(10), s->elem(10), XMM0.elem(10));\
+ d->elem(11) = F(d->elem(11), s->elem(11), XMM0.elem(11));\
+ d->elem(12) = F(d->elem(12), s->elem(12), XMM0.elem(12));\
+ d->elem(13) = F(d->elem(13), s->elem(13), XMM0.elem(13));\
+ d->elem(14) = F(d->elem(14), s->elem(14), XMM0.elem(14));\
+ d->elem(15) = F(d->elem(15), s->elem(15), XMM0.elem(15));\
+ }\
+ }\
+ }\
+}
+
+#define SSE_HELPER_I(name, elem, num, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s, uint32_t imm)\
+{\
+ d->elem(0) = F(d->elem(0), s->elem(0), ((imm >> 0) & 1));\
+ d->elem(1) = F(d->elem(1), s->elem(1), ((imm >> 1) & 1));\
+ if (num > 2) {\
+ d->elem(2) = F(d->elem(2), s->elem(2), ((imm >> 2) & 1));\
+ d->elem(3) = F(d->elem(3), s->elem(3), ((imm >> 3) & 1));\
+ if (num > 4) {\
+ d->elem(4) = F(d->elem(4), s->elem(4), ((imm >> 4) & 1));\
+ d->elem(5) = F(d->elem(5), s->elem(5), ((imm >> 5) & 1));\
+ d->elem(6) = F(d->elem(6), s->elem(6), ((imm >> 6) & 1));\
+ d->elem(7) = F(d->elem(7), s->elem(7), ((imm >> 7) & 1));\
+ if (num > 8) {\
+ d->elem(8) = F(d->elem(8), s->elem(8), ((imm >> 8) & 1));\
+ d->elem(9) = F(d->elem(9), s->elem(9), ((imm >> 9) & 1));\
+ d->elem(10) = F(d->elem(10), s->elem(10), ((imm >> 10) & 1));\
+ d->elem(11) = F(d->elem(11), s->elem(11), ((imm >> 11) & 1));\
+ d->elem(12) = F(d->elem(12), s->elem(12), ((imm >> 12) & 1));\
+ d->elem(13) = F(d->elem(13), s->elem(13), ((imm >> 13) & 1));\
+ d->elem(14) = F(d->elem(14), s->elem(14), ((imm >> 14) & 1));\
+ d->elem(15) = F(d->elem(15), s->elem(15), ((imm >> 15) & 1));\
+ }\
+ }\
+ }\
+}
+
+/* SSE4.1 op helpers */
+#define FBLENDVB(d, s, m) (m & 0x80) ? s : d
+#define FBLENDVPS(d, s, m) (m & 0x80000000) ? s : d
+#define FBLENDVPD(d, s, m) (m & 0x8000000000000000LL) ? s : d
+SSE_HELPER_V(helper_pblendvb, B, 16, FBLENDVB)
+SSE_HELPER_V(helper_blendvps, L, 4, FBLENDVPS)
+SSE_HELPER_V(helper_blendvpd, Q, 2, FBLENDVPD)
+
+void glue(helper_ptest, SUFFIX) (Reg *d, Reg *s)
+{
+ uint64_t zf = (s->Q(0) & d->Q(0)) | (s->Q(1) & d->Q(1));
+ uint64_t cf = (s->Q(0) & ~d->Q(0)) | (s->Q(1) & ~d->Q(1));
+
+ CC_SRC = (zf ? 0 : CC_Z) | (cf ? 0 : CC_C);
+}
+
+#define SSE_HELPER_F(name, elem, num, F)\
+void glue(name, SUFFIX) (Reg *d, Reg *s)\
+{\
+ d->elem(0) = F(0);\
+ d->elem(1) = F(1);\
+ if (num > 2) {\
+ d->elem(2) = F(2);\
+ d->elem(3) = F(3);\
+ if (num > 4) {\
+ d->elem(4) = F(4);\
+ d->elem(5) = F(5);\
+ d->elem(6) = F(6);\
+ d->elem(7) = F(7);\
+ }\
+ }\
+}
+
+SSE_HELPER_F(helper_pmovsxbw, W, 8, (int8_t) s->B)
+SSE_HELPER_F(helper_pmovsxbd, L, 4, (int8_t) s->B)
+SSE_HELPER_F(helper_pmovsxbq, Q, 2, (int8_t) s->B)
+SSE_HELPER_F(helper_pmovsxwd, L, 4, (int16_t) s->W)
+SSE_HELPER_F(helper_pmovsxwq, Q, 2, (int16_t) s->W)
+SSE_HELPER_F(helper_pmovsxdq, Q, 2, (int32_t) s->L)
+SSE_HELPER_F(helper_pmovzxbw, W, 8, s->B)
+SSE_HELPER_F(helper_pmovzxbd, L, 4, s->B)
+SSE_HELPER_F(helper_pmovzxbq, Q, 2, s->B)
+SSE_HELPER_F(helper_pmovzxwd, L, 4, s->W)
+SSE_HELPER_F(helper_pmovzxwq, Q, 2, s->W)
+SSE_HELPER_F(helper_pmovzxdq, Q, 2, s->L)
+
+void glue(helper_pmuldq, SUFFIX) (Reg *d, Reg *s)
+{
+ d->Q(0) = (int64_t) (int32_t) d->L(0) * (int32_t) s->L(0);
+ d->Q(1) = (int64_t) (int32_t) d->L(2) * (int32_t) s->L(2);
+}
+
+#define FCMPEQQ(d, s) d == s ? -1 : 0
+SSE_HELPER_Q(helper_pcmpeqq, FCMPEQQ)
+
+void glue(helper_packusdw, SUFFIX) (Reg *d, Reg *s)
+{
+ d->W(0) = satuw((int32_t) d->L(0));
+ d->W(1) = satuw((int32_t) d->L(1));
+ d->W(2) = satuw((int32_t) d->L(2));
+ d->W(3) = satuw((int32_t) d->L(3));
+ d->W(4) = satuw((int32_t) s->L(0));
+ d->W(5) = satuw((int32_t) s->L(1));
+ d->W(6) = satuw((int32_t) s->L(2));
+ d->W(7) = satuw((int32_t) s->L(3));
+}
+
+#define FMINSB(d, s) MIN((int8_t) d, (int8_t) s)
+#define FMINSD(d, s) MIN((int32_t) d, (int32_t) s)
+#define FMAXSB(d, s) MAX((int8_t) d, (int8_t) s)
+#define FMAXSD(d, s) MAX((int32_t) d, (int32_t) s)
+SSE_HELPER_B(helper_pminsb, FMINSB)
+SSE_HELPER_L(helper_pminsd, FMINSD)
+SSE_HELPER_W(helper_pminuw, MIN)
+SSE_HELPER_L(helper_pminud, MIN)
+SSE_HELPER_B(helper_pmaxsb, FMAXSB)
+SSE_HELPER_L(helper_pmaxsd, FMAXSD)
+SSE_HELPER_W(helper_pmaxuw, MAX)
+SSE_HELPER_L(helper_pmaxud, MAX)
+
+#define FMULLD(d, s) (int32_t) d * (int32_t) s
+SSE_HELPER_L(helper_pmulld, FMULLD)
+
+void glue(helper_phminposuw, SUFFIX) (Reg *d, Reg *s)
+{
+ int idx = 0;
+
+ if (s->W(1) < s->W(idx))
+ idx = 1;
+ if (s->W(2) < s->W(idx))
+ idx = 2;
+ if (s->W(3) < s->W(idx))
+ idx = 3;
+ if (s->W(4) < s->W(idx))
+ idx = 4;
+ if (s->W(5) < s->W(idx))
+ idx = 5;
+ if (s->W(6) < s->W(idx))
+ idx = 6;
+ if (s->W(7) < s->W(idx))
+ idx = 7;
+
+ d->Q(1) = 0;
+ d->L(1) = 0;
+ d->W(1) = idx;
+ d->W(0) = s->W(idx);
+}
+
+void glue(helper_roundps, SUFFIX) (Reg *d, Reg *s, uint32_t mode)
+{
+ signed char prev_rounding_mode;
+
+ prev_rounding_mode = env->sse_status.float_rounding_mode;
+ if (!(mode & (1 << 2)))
+ switch (mode & 3) {
+ case 0:
+ set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
+ break;
+ case 1:
+ set_float_rounding_mode(float_round_down, &env->sse_status);
+ break;
+ case 2:
+ set_float_rounding_mode(float_round_up, &env->sse_status);
+ break;
+ case 3:
+ set_float_rounding_mode(float_round_to_zero, &env->sse_status);
+ break;
+ }
+
+ d->L(0) = float64_round_to_int(s->L(0), &env->sse_status);
+ d->L(1) = float64_round_to_int(s->L(1), &env->sse_status);
+ d->L(2) = float64_round_to_int(s->L(2), &env->sse_status);
+ d->L(3) = float64_round_to_int(s->L(3), &env->sse_status);
+
+#if 0 /* TODO */
+ if (mode & (1 << 3))
+ set_float_exception_flags(
+ get_float_exception_flags(&env->sse_status) &
+ ~float_flag_inexact,
+ &env->sse_status);
+#endif
+ env->sse_status.float_rounding_mode = prev_rounding_mode;
+}
+
+void glue(helper_roundpd, SUFFIX) (Reg *d, Reg *s, uint32_t mode)
+{
+ signed char prev_rounding_mode;
+
+ prev_rounding_mode = env->sse_status.float_rounding_mode;
+ if (!(mode & (1 << 2)))
+ switch (mode & 3) {
+ case 0:
+ set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
+ break;
+ case 1:
+ set_float_rounding_mode(float_round_down, &env->sse_status);
+ break;
+ case 2:
+ set_float_rounding_mode(float_round_up, &env->sse_status);
+ break;
+ case 3:
+ set_float_rounding_mode(float_round_to_zero, &env->sse_status);
+ break;
+ }
+
+ d->Q(0) = float64_round_to_int(s->Q(0), &env->sse_status);
+ d->Q(1) = float64_round_to_int(s->Q(1), &env->sse_status);
+
+#if 0 /* TODO */
+ if (mode & (1 << 3))
+ set_float_exception_flags(
+ get_float_exception_flags(&env->sse_status) &
+ ~float_flag_inexact,
+ &env->sse_status);
+#endif
+ env->sse_status.float_rounding_mode = prev_rounding_mode;
+}
+
+void glue(helper_roundss, SUFFIX) (Reg *d, Reg *s, uint32_t mode)
+{
+ signed char prev_rounding_mode;
+
+ prev_rounding_mode = env->sse_status.float_rounding_mode;
+ if (!(mode & (1 << 2)))
+ switch (mode & 3) {
+ case 0:
+ set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
+ break;
+ case 1:
+ set_float_rounding_mode(float_round_down, &env->sse_status);
+ break;
+ case 2:
+ set_float_rounding_mode(float_round_up, &env->sse_status);
+ break;
+ case 3:
+ set_float_rounding_mode(float_round_to_zero, &env->sse_status);
+ break;
+ }
+
+ d->L(0) = float64_round_to_int(s->L(0), &env->sse_status);
+
+#if 0 /* TODO */
+ if (mode & (1 << 3))
+ set_float_exception_flags(
+ get_float_exception_flags(&env->sse_status) &
+ ~float_flag_inexact,
+ &env->sse_status);
+#endif
+ env->sse_status.float_rounding_mode = prev_rounding_mode;
+}
+
+void glue(helper_roundsd, SUFFIX) (Reg *d, Reg *s, uint32_t mode)
+{
+ signed char prev_rounding_mode;
+
+ prev_rounding_mode = env->sse_status.float_rounding_mode;
+ if (!(mode & (1 << 2)))
+ switch (mode & 3) {
+ case 0:
+ set_float_rounding_mode(float_round_nearest_even, &env->sse_status);
+ break;
+ case 1:
+ set_float_rounding_mode(float_round_down, &env->sse_status);
+ break;
+ case 2:
+ set_float_rounding_mode(float_round_up, &env->sse_status);
+ break;
+ case 3:
+ set_float_rounding_mode(float_round_to_zero, &env->sse_status);
+ break;
+ }
+
+ d->Q(0) = float64_round_to_int(s->Q(0), &env->sse_status);
+
+#if 0 /* TODO */
+ if (mode & (1 << 3))
+ set_float_exception_flags(
+ get_float_exception_flags(&env->sse_status) &
+ ~float_flag_inexact,
+ &env->sse_status);
+#endif
+ env->sse_status.float_rounding_mode = prev_rounding_mode;
+}
+
+#define FBLENDP(d, s, m) m ? s : d
+SSE_HELPER_I(helper_blendps, L, 4, FBLENDP)
+SSE_HELPER_I(helper_blendpd, Q, 2, FBLENDP)
+SSE_HELPER_I(helper_pblendw, W, 8, FBLENDP)
+
+void glue(helper_dpps, SUFFIX) (Reg *d, Reg *s, uint32_t mask)
+{
+ float32 iresult = 0 /*float32_zero*/;
+
+ if (mask & (1 << 4))
+ iresult = float32_add(iresult,
+ float32_mul(d->L(0), s->L(0), &env->sse_status),
+ &env->sse_status);
+ if (mask & (1 << 5))
+ iresult = float32_add(iresult,
+ float32_mul(d->L(1), s->L(1), &env->sse_status),
+ &env->sse_status);
+ if (mask & (1 << 6))
+ iresult = float32_add(iresult,
+ float32_mul(d->L(2), s->L(2), &env->sse_status),
+ &env->sse_status);
+ if (mask & (1 << 7))
+ iresult = float32_add(iresult,
+ float32_mul(d->L(3), s->L(3), &env->sse_status),
+ &env->sse_status);
+ d->L(0) = (mask & (1 << 0)) ? iresult : 0 /*float32_zero*/;
+ d->L(1) = (mask & (1 << 1)) ? iresult : 0 /*float32_zero*/;
+ d->L(2) = (mask & (1 << 2)) ? iresult : 0 /*float32_zero*/;
+ d->L(3) = (mask & (1 << 3)) ? iresult : 0 /*float32_zero*/;
+}
+
+void glue(helper_dppd, SUFFIX) (Reg *d, Reg *s, uint32_t mask)
+{
+ float64 iresult = 0 /*float64_zero*/;
+
+ if (mask & (1 << 4))
+ iresult = float64_add(iresult,
+ float64_mul(d->Q(0), s->Q(0), &env->sse_status),
+ &env->sse_status);
+ if (mask & (1 << 5))
+ iresult = float64_add(iresult,
+ float64_mul(d->Q(1), s->Q(1), &env->sse_status),
+ &env->sse_status);
+ d->Q(0) = (mask & (1 << 0)) ? iresult : 0 /*float64_zero*/;
+ d->Q(1) = (mask & (1 << 1)) ? iresult : 0 /*float64_zero*/;
+}
+
+void glue(helper_mpsadbw, SUFFIX) (Reg *d, Reg *s, uint32_t offset)
+{
+ int s0 = (offset & 3) << 2;
+ int d0 = (offset & 4) << 0;
+ int i;
+ Reg r;
+
+ for (i = 0; i < 8; i++, d0++) {
+ r.W(i) = 0;
+ r.W(i) += abs1(d->B(d0 + 0) - s->B(s0 + 0));
+ r.W(i) += abs1(d->B(d0 + 1) - s->B(s0 + 1));
+ r.W(i) += abs1(d->B(d0 + 2) - s->B(s0 + 2));
+ r.W(i) += abs1(d->B(d0 + 3) - s->B(s0 + 3));
+ }
+
+ *d = r;
+}
+
+/* SSE4.2 op helpers */
+/* it's unclear whether signed or unsigned */
+#define FCMPGTQ(d, s) d > s ? -1 : 0
+SSE_HELPER_Q(helper_pcmpgtq, FCMPGTQ)
+
+static inline int pcmp_elen(int reg, uint32_t ctrl)
+{
+ int val;
+
+ /* Presence of REX.W is indicated by a bit higher than 7 set */
+ if (ctrl >> 8)
+ val = abs1((int64_t) env->regs[reg]);
+ else
+ val = abs1((int32_t) env->regs[reg]);
+
+ if (ctrl & 1) {
+ if (val > 8)
+ return 8;
+ } else
+ if (val > 16)
+ return 16;
+
+ return val;
+}
+
+static inline int pcmp_ilen(Reg *r, uint8_t ctrl)
+{
+ int val = 0;
+
+ if (ctrl & 1) {
+ while (val < 8 && r->W(val))
+ val++;
+ } else
+ while (val < 16 && r->B(val))
+ val++;
+
+ return val;
+}
+
+static inline int pcmp_val(Reg *r, uint8_t ctrl, int i)
+{
+ switch ((ctrl >> 0) & 3) {
+ case 0:
+ return r->B(i);
+ case 1:
+ return r->W(i);
+ case 2:
+ return (int8_t) r->B(i);
+ case 3:
+ default:
+ return (int16_t) r->W(i);
+ }
+}
+
+static inline unsigned pcmpxstrx(Reg *d, Reg *s,
+ int8_t ctrl, int valids, int validd)
+{
+ unsigned int res = 0;
+ int v;
+ int j, i;
+ int upper = (ctrl & 1) ? 7 : 15;
+
+ valids--;
+ validd--;
+
+ CC_SRC = (valids < upper ? CC_Z : 0) | (validd < upper ? CC_S : 0);
+
+ switch ((ctrl >> 2) & 3) {
+ case 0:
+ for (j = valids; j >= 0; j--) {
+ res <<= 1;
+ v = pcmp_val(s, ctrl, j);
+ for (i = validd; i >= 0; i--)
+ res |= (v == pcmp_val(d, ctrl, i));
+ }
+ break;
+ case 1:
+ for (j = valids; j >= 0; j--) {
+ res <<= 1;
+ v = pcmp_val(s, ctrl, j);
+ for (i = ((validd - 1) | 1); i >= 0; i -= 2)
+ res |= (pcmp_val(d, ctrl, i - 0) <= v &&
+ pcmp_val(d, ctrl, i - 1) >= v);
+ }
+ break;
+ case 2:
+ res = (2 << (upper - MAX(valids, validd))) - 1;
+ res <<= MAX(valids, validd) - MIN(valids, validd);
+ for (i = MIN(valids, validd); i >= 0; i--) {
+ res <<= 1;
+ v = pcmp_val(s, ctrl, i);
+ res |= (v == pcmp_val(d, ctrl, i));
+ }
+ break;
+ case 3:
+ for (j = valids - validd; j >= 0; j--) {
+ res <<= 1;
+ res |= 1;
+ for (i = MIN(upper - j, validd); i >= 0; i--)
+ res &= (pcmp_val(s, ctrl, i + j) == pcmp_val(d, ctrl, i));
+ }
+ break;
+ }
+
+ switch ((ctrl >> 4) & 3) {
+ case 1:
+ res ^= (2 << upper) - 1;
+ break;
+ case 3:
+ res ^= (2 << valids) - 1;
+ break;
+ }
+
+ if (res)
+ CC_SRC |= CC_C;
+ if (res & 1)
+ CC_SRC |= CC_O;
+
+ return res;
+}
+
+static inline int rffs1(unsigned int val)
+{
+ int ret = 1, hi;
+
+ for (hi = sizeof(val) * 4; hi; hi /= 2)
+ if (val >> hi) {
+ val >>= hi;
+ ret += hi;
+ }
+
+ return ret;
+}
+
+static inline int ffs1(unsigned int val)
+{
+ int ret = 1, hi;
+
+ for (hi = sizeof(val) * 4; hi; hi /= 2)
+ if (val << hi) {
+ val <<= hi;
+ ret += hi;
+ }
+
+ return ret;
+}
+
+void glue(helper_pcmpestri, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl)
+{
+ unsigned int res = pcmpxstrx(d, s, ctrl,
+ pcmp_elen(R_EDX, ctrl),
+ pcmp_elen(R_EAX, ctrl));
+
+ if (res)
+ env->regs[R_ECX] = ((ctrl & (1 << 6)) ? rffs1 : ffs1)(res) - 1;
+ else
+ env->regs[R_ECX] = 16 >> (ctrl & (1 << 0));
+}
+
+void glue(helper_pcmpestrm, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl)
+{
+ int i;
+ unsigned int res = pcmpxstrx(d, s, ctrl,
+ pcmp_elen(R_EDX, ctrl),
+ pcmp_elen(R_EAX, ctrl));
+
+ if ((ctrl >> 6) & 1) {
+ if (ctrl & 1)
+ for (i = 0; i <= 8; i--, res >>= 1)
+ d->W(i) = (res & 1) ? ~0 : 0;
+ else
+ for (i = 0; i <= 16; i--, res >>= 1)
+ d->B(i) = (res & 1) ? ~0 : 0;
+ } else {
+ d->Q(1) = 0;
+ d->Q(0) = res;
+ }
+}
+
+void glue(helper_pcmpistri, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl)
+{
+ unsigned int res = pcmpxstrx(d, s, ctrl,
+ pcmp_ilen(s, ctrl),
+ pcmp_ilen(d, ctrl));
+
+ if (res)
+ env->regs[R_ECX] = ((ctrl & (1 << 6)) ? rffs1 : ffs1)(res) - 1;
+ else
+ env->regs[R_ECX] = 16 >> (ctrl & (1 << 0));
+}
+
+void glue(helper_pcmpistrm, SUFFIX) (Reg *d, Reg *s, uint32_t ctrl)
+{
+ int i;
+ unsigned int res = pcmpxstrx(d, s, ctrl,
+ pcmp_ilen(s, ctrl),
+ pcmp_ilen(d, ctrl));
+
+ if ((ctrl >> 6) & 1) {
+ if (ctrl & 1)
+ for (i = 0; i <= 8; i--, res >>= 1)
+ d->W(i) = (res & 1) ? ~0 : 0;
+ else
+ for (i = 0; i <= 16; i--, res >>= 1)
+ d->B(i) = (res & 1) ? ~0 : 0;
+ } else {
+ d->Q(1) = 0;
+ d->Q(0) = res;
+ }
+}
+
+#define CRCPOLY 0x1edc6f41
+#define CRCPOLY_BITREV 0x82f63b78
+target_ulong helper_crc32(uint32_t crc1, target_ulong msg, uint32_t len)
+{
+ target_ulong crc = (msg & ((target_ulong) -1 >>
+ (TARGET_LONG_BITS - len))) ^ crc1;
+
+ while (len--)
+ crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_BITREV : 0);
+
+ return crc;
+}
+
+#define POPMASK(i) ((target_ulong) -1 / ((1LL << (1 << i)) + 1))
+#define POPCOUNT(n, i) (n & POPMASK(i)) + ((n >> (1 << i)) & POPMASK(i))
+target_ulong helper_popcnt(target_ulong n, uint32_t type)
+{
+ CC_SRC = n ? 0 : CC_Z;
+
+ n = POPCOUNT(n, 0);
+ n = POPCOUNT(n, 1);
+ n = POPCOUNT(n, 2);
+ n = POPCOUNT(n, 3);
+ if (type == 1)
+ return n & 0xff;
+
+ n = POPCOUNT(n, 4);
+#ifndef TARGET_X86_64
+ return n;
+#else
+ if (type == 2)
+ return n & 0xff;
+
+ return POPCOUNT(n, 5);
+#endif
+}
+#endif
+
+#undef SHIFT
+#undef XMM_ONLY
+#undef Reg
+#undef B
+#undef W
+#undef L
+#undef Q
+#undef SUFFIX
diff --git a/target-i386/ops_sse_header.h b/target-i386/ops_sse_header.h
new file mode 100644
index 0000000..8d4b2b7
--- /dev/null
+++ b/target-i386/ops_sse_header.h
@@ -0,0 +1,349 @@
+/*
+ * MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support
+ *
+ * Copyright (c) 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/>.
+ */
+#if SHIFT == 0
+#define Reg MMXReg
+#define SUFFIX _mmx
+#else
+#define Reg XMMReg
+#define SUFFIX _xmm
+#endif
+
+#define dh_alias_Reg ptr
+#define dh_alias_XMMReg ptr
+#define dh_alias_MMXReg ptr
+#define dh_ctype_Reg Reg *
+#define dh_ctype_XMMReg XMMReg *
+#define dh_ctype_MMXReg MMXReg *
+#define dh_is_signed_Reg dh_is_signed_ptr
+#define dh_is_signed_XMMReg dh_is_signed_ptr
+#define dh_is_signed_MMXReg dh_is_signed_ptr
+
+DEF_HELPER_2(glue(psrlw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psraw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psllw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psrld, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psrad, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pslld, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psrlq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psllq, SUFFIX), void, Reg, Reg)
+
+#if SHIFT == 1
+DEF_HELPER_2(glue(psrldq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pslldq, SUFFIX), void, Reg, Reg)
+#endif
+
+#define SSE_HELPER_B(name, F)\
+ DEF_HELPER_2(glue(name, SUFFIX), void, Reg, Reg)
+
+#define SSE_HELPER_W(name, F)\
+ DEF_HELPER_2(glue(name, SUFFIX), void, Reg, Reg)
+
+#define SSE_HELPER_L(name, F)\
+ DEF_HELPER_2(glue(name, SUFFIX), void, Reg, Reg)
+
+#define SSE_HELPER_Q(name, F)\
+ DEF_HELPER_2(glue(name, SUFFIX), void, Reg, Reg)
+
+SSE_HELPER_B(paddb, FADD)
+SSE_HELPER_W(paddw, FADD)
+SSE_HELPER_L(paddl, FADD)
+SSE_HELPER_Q(paddq, FADD)
+
+SSE_HELPER_B(psubb, FSUB)
+SSE_HELPER_W(psubw, FSUB)
+SSE_HELPER_L(psubl, FSUB)
+SSE_HELPER_Q(psubq, FSUB)
+
+SSE_HELPER_B(paddusb, FADDUB)
+SSE_HELPER_B(paddsb, FADDSB)
+SSE_HELPER_B(psubusb, FSUBUB)
+SSE_HELPER_B(psubsb, FSUBSB)
+
+SSE_HELPER_W(paddusw, FADDUW)
+SSE_HELPER_W(paddsw, FADDSW)
+SSE_HELPER_W(psubusw, FSUBUW)
+SSE_HELPER_W(psubsw, FSUBSW)
+
+SSE_HELPER_B(pminub, FMINUB)
+SSE_HELPER_B(pmaxub, FMAXUB)
+
+SSE_HELPER_W(pminsw, FMINSW)
+SSE_HELPER_W(pmaxsw, FMAXSW)
+
+SSE_HELPER_Q(pand, FAND)
+SSE_HELPER_Q(pandn, FANDN)
+SSE_HELPER_Q(por, FOR)
+SSE_HELPER_Q(pxor, FXOR)
+
+SSE_HELPER_B(pcmpgtb, FCMPGTB)
+SSE_HELPER_W(pcmpgtw, FCMPGTW)
+SSE_HELPER_L(pcmpgtl, FCMPGTL)
+
+SSE_HELPER_B(pcmpeqb, FCMPEQ)
+SSE_HELPER_W(pcmpeqw, FCMPEQ)
+SSE_HELPER_L(pcmpeql, FCMPEQ)
+
+SSE_HELPER_W(pmullw, FMULLW)
+#if SHIFT == 0
+SSE_HELPER_W(pmulhrw, FMULHRW)
+#endif
+SSE_HELPER_W(pmulhuw, FMULHUW)
+SSE_HELPER_W(pmulhw, FMULHW)
+
+SSE_HELPER_B(pavgb, FAVG)
+SSE_HELPER_W(pavgw, FAVG)
+
+DEF_HELPER_2(glue(pmuludq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmaddwd, SUFFIX), void, Reg, Reg)
+
+DEF_HELPER_2(glue(psadbw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_3(glue(maskmov, SUFFIX), void, Reg, Reg, tl)
+DEF_HELPER_2(glue(movl_mm_T0, SUFFIX), void, Reg, i32)
+#ifdef TARGET_X86_64
+DEF_HELPER_2(glue(movq_mm_T0, SUFFIX), void, Reg, i64)
+#endif
+
+#if SHIFT == 0
+DEF_HELPER_3(glue(pshufw, SUFFIX), void, Reg, Reg, int)
+#else
+DEF_HELPER_3(shufps, void, Reg, Reg, int)
+DEF_HELPER_3(shufpd, void, Reg, Reg, int)
+DEF_HELPER_3(glue(pshufd, SUFFIX), void, Reg, Reg, int)
+DEF_HELPER_3(glue(pshuflw, SUFFIX), void, Reg, Reg, int)
+DEF_HELPER_3(glue(pshufhw, SUFFIX), void, Reg, Reg, int)
+#endif
+
+#if SHIFT == 1
+/* FPU ops */
+/* XXX: not accurate */
+
+#define SSE_HELPER_S(name, F)\
+ DEF_HELPER_2(name ## ps , void, Reg, Reg) \
+ DEF_HELPER_2(name ## ss , void, Reg, Reg) \
+ DEF_HELPER_2(name ## pd , void, Reg, Reg) \
+ DEF_HELPER_2(name ## sd , void, Reg, Reg)
+
+SSE_HELPER_S(add, FPU_ADD)
+SSE_HELPER_S(sub, FPU_SUB)
+SSE_HELPER_S(mul, FPU_MUL)
+SSE_HELPER_S(div, FPU_DIV)
+SSE_HELPER_S(min, FPU_MIN)
+SSE_HELPER_S(max, FPU_MAX)
+SSE_HELPER_S(sqrt, FPU_SQRT)
+
+
+DEF_HELPER_2(cvtps2pd, void, Reg, Reg)
+DEF_HELPER_2(cvtpd2ps, void, Reg, Reg)
+DEF_HELPER_2(cvtss2sd, void, Reg, Reg)
+DEF_HELPER_2(cvtsd2ss, void, Reg, Reg)
+DEF_HELPER_2(cvtdq2ps, void, Reg, Reg)
+DEF_HELPER_2(cvtdq2pd, void, Reg, Reg)
+DEF_HELPER_2(cvtpi2ps, void, XMMReg, MMXReg)
+DEF_HELPER_2(cvtpi2pd, void, XMMReg, MMXReg)
+DEF_HELPER_2(cvtsi2ss, void, XMMReg, i32)
+DEF_HELPER_2(cvtsi2sd, void, XMMReg, i32)
+
+#ifdef TARGET_X86_64
+DEF_HELPER_2(cvtsq2ss, void, XMMReg, i64)
+DEF_HELPER_2(cvtsq2sd, void, XMMReg, i64)
+#endif
+
+DEF_HELPER_2(cvtps2dq, void, XMMReg, XMMReg)
+DEF_HELPER_2(cvtpd2dq, void, XMMReg, XMMReg)
+DEF_HELPER_2(cvtps2pi, void, MMXReg, XMMReg)
+DEF_HELPER_2(cvtpd2pi, void, MMXReg, XMMReg)
+DEF_HELPER_1(cvtss2si, s32, XMMReg)
+DEF_HELPER_1(cvtsd2si, s32, XMMReg)
+#ifdef TARGET_X86_64
+DEF_HELPER_1(cvtss2sq, s64, XMMReg)
+DEF_HELPER_1(cvtsd2sq, s64, XMMReg)
+#endif
+
+DEF_HELPER_2(cvttps2dq, void, XMMReg, XMMReg)
+DEF_HELPER_2(cvttpd2dq, void, XMMReg, XMMReg)
+DEF_HELPER_2(cvttps2pi, void, MMXReg, XMMReg)
+DEF_HELPER_2(cvttpd2pi, void, MMXReg, XMMReg)
+DEF_HELPER_1(cvttss2si, s32, XMMReg)
+DEF_HELPER_1(cvttsd2si, s32, XMMReg)
+#ifdef TARGET_X86_64
+DEF_HELPER_1(cvttss2sq, s64, XMMReg)
+DEF_HELPER_1(cvttsd2sq, s64, XMMReg)
+#endif
+
+DEF_HELPER_2(rsqrtps, void, XMMReg, XMMReg)
+DEF_HELPER_2(rsqrtss, void, XMMReg, XMMReg)
+DEF_HELPER_2(rcpps, void, XMMReg, XMMReg)
+DEF_HELPER_2(rcpss, void, XMMReg, XMMReg)
+DEF_HELPER_2(extrq_r, void, XMMReg, XMMReg)
+DEF_HELPER_3(extrq_i, void, XMMReg, int, int)
+DEF_HELPER_2(insertq_r, void, XMMReg, XMMReg)
+DEF_HELPER_3(insertq_i, void, XMMReg, int, int)
+DEF_HELPER_2(haddps, void, XMMReg, XMMReg)
+DEF_HELPER_2(haddpd, void, XMMReg, XMMReg)
+DEF_HELPER_2(hsubps, void, XMMReg, XMMReg)
+DEF_HELPER_2(hsubpd, void, XMMReg, XMMReg)
+DEF_HELPER_2(addsubps, void, XMMReg, XMMReg)
+DEF_HELPER_2(addsubpd, void, XMMReg, XMMReg)
+
+#define SSE_HELPER_CMP(name, F)\
+ DEF_HELPER_2( name ## ps , void, Reg, Reg) \
+ DEF_HELPER_2( name ## ss , void, Reg, Reg) \
+ DEF_HELPER_2( name ## pd , void, Reg, Reg) \
+ DEF_HELPER_2( name ## sd , void, Reg, Reg)
+
+SSE_HELPER_CMP(cmpeq, FPU_CMPEQ)
+SSE_HELPER_CMP(cmplt, FPU_CMPLT)
+SSE_HELPER_CMP(cmple, FPU_CMPLE)
+SSE_HELPER_CMP(cmpunord, FPU_CMPUNORD)
+SSE_HELPER_CMP(cmpneq, FPU_CMPNEQ)
+SSE_HELPER_CMP(cmpnlt, FPU_CMPNLT)
+SSE_HELPER_CMP(cmpnle, FPU_CMPNLE)
+SSE_HELPER_CMP(cmpord, FPU_CMPORD)
+
+DEF_HELPER_2(ucomiss, void, Reg, Reg)
+DEF_HELPER_2(comiss, void, Reg, Reg)
+DEF_HELPER_2(ucomisd, void, Reg, Reg)
+DEF_HELPER_2(comisd, void, Reg, Reg)
+DEF_HELPER_1(movmskps, i32, Reg)
+DEF_HELPER_1(movmskpd, i32, Reg)
+#endif
+
+DEF_HELPER_1(glue(pmovmskb, SUFFIX), i32, Reg)
+DEF_HELPER_2(glue(packsswb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(packuswb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(packssdw, SUFFIX), void, Reg, Reg)
+#define UNPCK_OP(base_name, base) \
+ DEF_HELPER_2(glue(punpck ## base_name ## bw, SUFFIX) , void, Reg, Reg) \
+ DEF_HELPER_2(glue(punpck ## base_name ## wd, SUFFIX) , void, Reg, Reg) \
+ DEF_HELPER_2(glue(punpck ## base_name ## dq, SUFFIX) , void, Reg, Reg)
+
+UNPCK_OP(l, 0)
+UNPCK_OP(h, 1)
+
+#if SHIFT == 1
+DEF_HELPER_2(glue(punpcklqdq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(punpckhqdq, SUFFIX), void, Reg, Reg)
+#endif
+
+/* 3DNow! float ops */
+#if SHIFT == 0
+DEF_HELPER_2(pi2fd, void, MMXReg, MMXReg)
+DEF_HELPER_2(pi2fw, void, MMXReg, MMXReg)
+DEF_HELPER_2(pf2id, void, MMXReg, MMXReg)
+DEF_HELPER_2(pf2iw, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfacc, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfadd, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfcmpeq, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfcmpge, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfcmpgt, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfmax, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfmin, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfmul, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfnacc, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfpnacc, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfrcp, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfrsqrt, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfsub, void, MMXReg, MMXReg)
+DEF_HELPER_2(pfsubr, void, MMXReg, MMXReg)
+DEF_HELPER_2(pswapd, void, MMXReg, MMXReg)
+#endif
+
+/* SSSE3 op helpers */
+DEF_HELPER_2(glue(phaddw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(phaddd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(phaddsw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(phsubw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(phsubd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(phsubsw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pabsb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pabsw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pabsd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmaddubsw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmulhrsw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pshufb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psignb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psignw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(psignd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_3(glue(palignr, SUFFIX), void, Reg, Reg, s32)
+
+/* SSE4.1 op helpers */
+#if SHIFT == 1
+DEF_HELPER_2(glue(pblendvb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(blendvps, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(blendvpd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(ptest, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovsxbw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovsxbd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovsxbq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovsxwd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovsxwq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovsxdq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovzxbw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovzxbd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovzxbq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovzxwd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovzxwq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmovzxdq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmuldq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pcmpeqq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(packusdw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pminsb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pminsd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pminuw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pminud, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmaxsb, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmaxsd, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmaxuw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmaxud, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(pmulld, SUFFIX), void, Reg, Reg)
+DEF_HELPER_2(glue(phminposuw, SUFFIX), void, Reg, Reg)
+DEF_HELPER_3(glue(roundps, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(roundpd, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(roundss, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(roundsd, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(blendps, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(blendpd, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(pblendw, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(dpps, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(dppd, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(mpsadbw, SUFFIX), void, Reg, Reg, i32)
+#endif
+
+/* SSE4.2 op helpers */
+#if SHIFT == 1
+DEF_HELPER_2(glue(pcmpgtq, SUFFIX), void, Reg, Reg)
+DEF_HELPER_3(glue(pcmpestri, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(pcmpestrm, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(pcmpistri, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(glue(pcmpistrm, SUFFIX), void, Reg, Reg, i32)
+DEF_HELPER_3(crc32, tl, i32, tl, i32)
+DEF_HELPER_2(popcnt, tl, tl, i32)
+#endif
+
+#undef SHIFT
+#undef Reg
+#undef SUFFIX
+
+#undef SSE_HELPER_B
+#undef SSE_HELPER_W
+#undef SSE_HELPER_L
+#undef SSE_HELPER_Q
+#undef SSE_HELPER_S
+#undef SSE_HELPER_CMP
+#undef UNPCK_OP
diff --git a/target-i386/svm.h b/target-i386/svm.h
new file mode 100644
index 0000000..a224aea
--- /dev/null
+++ b/target-i386/svm.h
@@ -0,0 +1,222 @@
+#ifndef __SVM_H
+#define __SVM_H
+
+#define TLB_CONTROL_DO_NOTHING 0
+#define TLB_CONTROL_FLUSH_ALL_ASID 1
+
+#define V_TPR_MASK 0x0f
+
+#define V_IRQ_SHIFT 8
+#define V_IRQ_MASK (1 << V_IRQ_SHIFT)
+
+#define V_INTR_PRIO_SHIFT 16
+#define V_INTR_PRIO_MASK (0x0f << V_INTR_PRIO_SHIFT)
+
+#define V_IGN_TPR_SHIFT 20
+#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT)
+
+#define V_INTR_MASKING_SHIFT 24
+#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT)
+
+#define SVM_INTERRUPT_SHADOW_MASK 1
+
+#define SVM_IOIO_STR_SHIFT 2
+#define SVM_IOIO_REP_SHIFT 3
+#define SVM_IOIO_SIZE_SHIFT 4
+#define SVM_IOIO_ASIZE_SHIFT 7
+
+#define SVM_IOIO_TYPE_MASK 1
+#define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT)
+#define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT)
+#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
+#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
+
+#define SVM_EVTINJ_VEC_MASK 0xff
+
+#define SVM_EVTINJ_TYPE_SHIFT 8
+#define SVM_EVTINJ_TYPE_MASK (7 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_TYPE_INTR (0 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_NMI (2 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_VALID (1 << 31)
+#define SVM_EVTINJ_VALID_ERR (1 << 11)
+
+#define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK
+
+#define SVM_EXITINTINFO_TYPE_INTR SVM_EVTINJ_TYPE_INTR
+#define SVM_EXITINTINFO_TYPE_NMI SVM_EVTINJ_TYPE_NMI
+#define SVM_EXITINTINFO_TYPE_EXEPT SVM_EVTINJ_TYPE_EXEPT
+#define SVM_EXITINTINFO_TYPE_SOFT SVM_EVTINJ_TYPE_SOFT
+
+#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID
+#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR
+
+#define SVM_EXIT_READ_CR0 0x000
+#define SVM_EXIT_READ_CR3 0x003
+#define SVM_EXIT_READ_CR4 0x004
+#define SVM_EXIT_READ_CR8 0x008
+#define SVM_EXIT_WRITE_CR0 0x010
+#define SVM_EXIT_WRITE_CR3 0x013
+#define SVM_EXIT_WRITE_CR4 0x014
+#define SVM_EXIT_WRITE_CR8 0x018
+#define SVM_EXIT_READ_DR0 0x020
+#define SVM_EXIT_READ_DR1 0x021
+#define SVM_EXIT_READ_DR2 0x022
+#define SVM_EXIT_READ_DR3 0x023
+#define SVM_EXIT_READ_DR4 0x024
+#define SVM_EXIT_READ_DR5 0x025
+#define SVM_EXIT_READ_DR6 0x026
+#define SVM_EXIT_READ_DR7 0x027
+#define SVM_EXIT_WRITE_DR0 0x030
+#define SVM_EXIT_WRITE_DR1 0x031
+#define SVM_EXIT_WRITE_DR2 0x032
+#define SVM_EXIT_WRITE_DR3 0x033
+#define SVM_EXIT_WRITE_DR4 0x034
+#define SVM_EXIT_WRITE_DR5 0x035
+#define SVM_EXIT_WRITE_DR6 0x036
+#define SVM_EXIT_WRITE_DR7 0x037
+#define SVM_EXIT_EXCP_BASE 0x040
+#define SVM_EXIT_INTR 0x060
+#define SVM_EXIT_NMI 0x061
+#define SVM_EXIT_SMI 0x062
+#define SVM_EXIT_INIT 0x063
+#define SVM_EXIT_VINTR 0x064
+#define SVM_EXIT_CR0_SEL_WRITE 0x065
+#define SVM_EXIT_IDTR_READ 0x066
+#define SVM_EXIT_GDTR_READ 0x067
+#define SVM_EXIT_LDTR_READ 0x068
+#define SVM_EXIT_TR_READ 0x069
+#define SVM_EXIT_IDTR_WRITE 0x06a
+#define SVM_EXIT_GDTR_WRITE 0x06b
+#define SVM_EXIT_LDTR_WRITE 0x06c
+#define SVM_EXIT_TR_WRITE 0x06d
+#define SVM_EXIT_RDTSC 0x06e
+#define SVM_EXIT_RDPMC 0x06f
+#define SVM_EXIT_PUSHF 0x070
+#define SVM_EXIT_POPF 0x071
+#define SVM_EXIT_CPUID 0x072
+#define SVM_EXIT_RSM 0x073
+#define SVM_EXIT_IRET 0x074
+#define SVM_EXIT_SWINT 0x075
+#define SVM_EXIT_INVD 0x076
+#define SVM_EXIT_PAUSE 0x077
+#define SVM_EXIT_HLT 0x078
+#define SVM_EXIT_INVLPG 0x079
+#define SVM_EXIT_INVLPGA 0x07a
+#define SVM_EXIT_IOIO 0x07b
+#define SVM_EXIT_MSR 0x07c
+#define SVM_EXIT_TASK_SWITCH 0x07d
+#define SVM_EXIT_FERR_FREEZE 0x07e
+#define SVM_EXIT_SHUTDOWN 0x07f
+#define SVM_EXIT_VMRUN 0x080
+#define SVM_EXIT_VMMCALL 0x081
+#define SVM_EXIT_VMLOAD 0x082
+#define SVM_EXIT_VMSAVE 0x083
+#define SVM_EXIT_STGI 0x084
+#define SVM_EXIT_CLGI 0x085
+#define SVM_EXIT_SKINIT 0x086
+#define SVM_EXIT_RDTSCP 0x087
+#define SVM_EXIT_ICEBP 0x088
+#define SVM_EXIT_WBINVD 0x089
+/* only included in documentation, maybe wrong */
+#define SVM_EXIT_MONITOR 0x08a
+#define SVM_EXIT_MWAIT 0x08b
+#define SVM_EXIT_NPF 0x400
+
+#define SVM_EXIT_ERR -1
+
+#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */
+
+struct __attribute__ ((__packed__)) vmcb_control_area {
+ uint16_t intercept_cr_read;
+ uint16_t intercept_cr_write;
+ uint16_t intercept_dr_read;
+ uint16_t intercept_dr_write;
+ uint32_t intercept_exceptions;
+ uint64_t intercept;
+ uint8_t reserved_1[44];
+ uint64_t iopm_base_pa;
+ uint64_t msrpm_base_pa;
+ uint64_t tsc_offset;
+ uint32_t asid;
+ uint8_t tlb_ctl;
+ uint8_t reserved_2[3];
+ uint32_t int_ctl;
+ uint32_t int_vector;
+ uint32_t int_state;
+ uint8_t reserved_3[4];
+ uint64_t exit_code;
+ uint64_t exit_info_1;
+ uint64_t exit_info_2;
+ uint32_t exit_int_info;
+ uint32_t exit_int_info_err;
+ uint64_t nested_ctl;
+ uint8_t reserved_4[16];
+ uint32_t event_inj;
+ uint32_t event_inj_err;
+ uint64_t nested_cr3;
+ uint64_t lbr_ctl;
+ uint8_t reserved_5[832];
+};
+
+struct __attribute__ ((__packed__)) vmcb_seg {
+ uint16_t selector;
+ uint16_t attrib;
+ uint32_t limit;
+ uint64_t base;
+};
+
+struct __attribute__ ((__packed__)) vmcb_save_area {
+ struct vmcb_seg es;
+ struct vmcb_seg cs;
+ struct vmcb_seg ss;
+ struct vmcb_seg ds;
+ struct vmcb_seg fs;
+ struct vmcb_seg gs;
+ struct vmcb_seg gdtr;
+ struct vmcb_seg ldtr;
+ struct vmcb_seg idtr;
+ struct vmcb_seg tr;
+ uint8_t reserved_1[43];
+ uint8_t cpl;
+ uint8_t reserved_2[4];
+ uint64_t efer;
+ uint8_t reserved_3[112];
+ uint64_t cr4;
+ uint64_t cr3;
+ uint64_t cr0;
+ uint64_t dr7;
+ uint64_t dr6;
+ uint64_t rflags;
+ uint64_t rip;
+ uint8_t reserved_4[88];
+ uint64_t rsp;
+ uint8_t reserved_5[24];
+ uint64_t rax;
+ uint64_t star;
+ uint64_t lstar;
+ uint64_t cstar;
+ uint64_t sfmask;
+ uint64_t kernel_gs_base;
+ uint64_t sysenter_cs;
+ uint64_t sysenter_esp;
+ uint64_t sysenter_eip;
+ uint64_t cr2;
+ uint8_t reserved_6[32];
+ uint64_t g_pat;
+ uint64_t dbgctl;
+ uint64_t br_from;
+ uint64_t br_to;
+ uint64_t last_excp_from;
+ uint64_t last_excp_to;
+};
+
+struct __attribute__ ((__packed__)) vmcb {
+ struct vmcb_control_area control;
+ struct vmcb_save_area save;
+};
+
+#endif
diff --git a/target-i386/translate.c b/target-i386/translate.c
new file mode 100644
index 0000000..c008450
--- /dev/null
+++ b/target-i386/translate.c
@@ -0,0 +1,7915 @@
+/*
+ * i386 translation
+ *
+ * Copyright (c) 2003 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+#define PREFIX_REPZ 0x01
+#define PREFIX_REPNZ 0x02
+#define PREFIX_LOCK 0x04
+#define PREFIX_DATA 0x08
+#define PREFIX_ADR 0x10
+
+#ifdef TARGET_X86_64
+#define X86_64_ONLY(x) x
+#define X86_64_DEF(...) __VA_ARGS__
+#define CODE64(s) ((s)->code64)
+#define REX_X(s) ((s)->rex_x)
+#define REX_B(s) ((s)->rex_b)
+/* XXX: gcc generates push/pop in some opcodes, so we cannot use them */
+#if 1
+#define BUGGY_64(x) NULL
+#endif
+#else
+#define X86_64_ONLY(x) NULL
+#define X86_64_DEF(...)
+#define CODE64(s) 0
+#define REX_X(s) 0
+#define REX_B(s) 0
+#endif
+
+//#define MACRO_TEST 1
+
+/* global register indexes */
+static TCGv_ptr cpu_env;
+static TCGv cpu_A0, cpu_cc_src, cpu_cc_dst, cpu_cc_tmp;
+static TCGv_i32 cpu_cc_op;
+static TCGv cpu_regs[CPU_NB_REGS];
+/* local temps */
+static TCGv cpu_T[2], cpu_T3;
+/* local register indexes (only used inside old micro ops) */
+static TCGv cpu_tmp0, cpu_tmp4;
+static TCGv_ptr cpu_ptr0, cpu_ptr1;
+static TCGv_i32 cpu_tmp2_i32, cpu_tmp3_i32;
+static TCGv_i64 cpu_tmp1_i64;
+static TCGv cpu_tmp5;
+
+static uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+
+#include "gen-icount.h"
+
+#ifdef TARGET_X86_64
+static int x86_64_hregs;
+#endif
+
+typedef struct DisasContext {
+ /* current insn context */
+ int override; /* -1 if no override */
+ int prefix;
+ int aflag, dflag;
+ target_ulong pc; /* pc = eip + cs_base */
+ int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
+ static state change (stop translation) */
+ /* current block context */
+ target_ulong cs_base; /* base of CS segment */
+ int pe; /* protected mode */
+ int code32; /* 32 bit code segment */
+#ifdef TARGET_X86_64
+ int lma; /* long mode active */
+ int code64; /* 64 bit code segment */
+ int rex_x, rex_b;
+#endif
+ int ss32; /* 32 bit stack segment */
+ int cc_op; /* current CC operation */
+ int addseg; /* non zero if either DS/ES/SS have a non zero base */
+ int f_st; /* currently unused */
+ int vm86; /* vm86 mode */
+ int cpl;
+ int iopl;
+ int tf; /* TF cpu flag */
+ int singlestep_enabled; /* "hardware" single step enabled */
+ int jmp_opt; /* use direct block chaining for direct jumps */
+ int mem_index; /* select memory access functions */
+ uint64_t flags; /* all execution flags */
+ struct TranslationBlock *tb;
+ int popl_esp_hack; /* for correct popl with esp base handling */
+ int rip_offset; /* only used in x86_64, but left for simplicity */
+ int cpuid_features;
+ int cpuid_ext_features;
+ int cpuid_ext2_features;
+ int cpuid_ext3_features;
+} DisasContext;
+
+static void gen_eob(DisasContext *s);
+static void gen_jmp(DisasContext *s, target_ulong eip);
+static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num);
+
+/* i386 arith/logic operations */
+enum {
+ OP_ADDL,
+ OP_ORL,
+ OP_ADCL,
+ OP_SBBL,
+ OP_ANDL,
+ OP_SUBL,
+ OP_XORL,
+ OP_CMPL,
+};
+
+/* i386 shift ops */
+enum {
+ OP_ROL,
+ OP_ROR,
+ OP_RCL,
+ OP_RCR,
+ OP_SHL,
+ OP_SHR,
+ OP_SHL1, /* undocumented */
+ OP_SAR = 7,
+};
+
+enum {
+ JCC_O,
+ JCC_B,
+ JCC_Z,
+ JCC_BE,
+ JCC_S,
+ JCC_P,
+ JCC_L,
+ JCC_LE,
+};
+
+/* operand size */
+enum {
+ OT_BYTE = 0,
+ OT_WORD,
+ OT_LONG,
+ OT_QUAD,
+};
+
+enum {
+ /* I386 int registers */
+ OR_EAX, /* MUST be even numbered */
+ OR_ECX,
+ OR_EDX,
+ OR_EBX,
+ OR_ESP,
+ OR_EBP,
+ OR_ESI,
+ OR_EDI,
+
+ OR_TMP0 = 16, /* temporary operand register */
+ OR_TMP1,
+ OR_A0, /* temporary register used when doing address evaluation */
+};
+
+static inline void gen_op_movl_T0_0(void)
+{
+ tcg_gen_movi_tl(cpu_T[0], 0);
+}
+
+static inline void gen_op_movl_T0_im(int32_t val)
+{
+ tcg_gen_movi_tl(cpu_T[0], val);
+}
+
+static inline void gen_op_movl_T0_imu(uint32_t val)
+{
+ tcg_gen_movi_tl(cpu_T[0], val);
+}
+
+static inline void gen_op_movl_T1_im(int32_t val)
+{
+ tcg_gen_movi_tl(cpu_T[1], val);
+}
+
+static inline void gen_op_movl_T1_imu(uint32_t val)
+{
+ tcg_gen_movi_tl(cpu_T[1], val);
+}
+
+static inline void gen_op_movl_A0_im(uint32_t val)
+{
+ tcg_gen_movi_tl(cpu_A0, val);
+}
+
+#ifdef TARGET_X86_64
+static inline void gen_op_movq_A0_im(int64_t val)
+{
+ tcg_gen_movi_tl(cpu_A0, val);
+}
+#endif
+
+static inline void gen_movtl_T0_im(target_ulong val)
+{
+ tcg_gen_movi_tl(cpu_T[0], val);
+}
+
+static inline void gen_movtl_T1_im(target_ulong val)
+{
+ tcg_gen_movi_tl(cpu_T[1], val);
+}
+
+static inline void gen_op_andl_T0_ffff(void)
+{
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffff);
+}
+
+static inline void gen_op_andl_T0_im(uint32_t val)
+{
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], val);
+}
+
+static inline void gen_op_movl_T0_T1(void)
+{
+ tcg_gen_mov_tl(cpu_T[0], cpu_T[1]);
+}
+
+static inline void gen_op_andl_A0_ffff(void)
+{
+ tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffff);
+}
+
+#ifdef TARGET_X86_64
+
+#define NB_OP_SIZES 4
+
+#else /* !TARGET_X86_64 */
+
+#define NB_OP_SIZES 3
+
+#endif /* !TARGET_X86_64 */
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define REG_B_OFFSET (sizeof(target_ulong) - 1)
+#define REG_H_OFFSET (sizeof(target_ulong) - 2)
+#define REG_W_OFFSET (sizeof(target_ulong) - 2)
+#define REG_L_OFFSET (sizeof(target_ulong) - 4)
+#define REG_LH_OFFSET (sizeof(target_ulong) - 8)
+#else
+#define REG_B_OFFSET 0
+#define REG_H_OFFSET 1
+#define REG_W_OFFSET 0
+#define REG_L_OFFSET 0
+#define REG_LH_OFFSET 4
+#endif
+
+static inline void gen_op_mov_reg_v(int ot, int reg, TCGv t0)
+{
+ switch(ot) {
+ case OT_BYTE:
+ if (reg < 4 X86_64_DEF( || reg >= 8 || x86_64_hregs)) {
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8);
+ } else {
+ tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8);
+ }
+ break;
+ case OT_WORD:
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16);
+ break;
+ default: /* XXX this shouldn't be reached; abort? */
+ case OT_LONG:
+ /* For x86_64, this sets the higher half of register to zero.
+ For i386, this is equivalent to a mov. */
+ tcg_gen_ext32u_tl(cpu_regs[reg], t0);
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ tcg_gen_mov_tl(cpu_regs[reg], t0);
+ break;
+#endif
+ }
+}
+
+static inline void gen_op_mov_reg_T0(int ot, int reg)
+{
+ gen_op_mov_reg_v(ot, reg, cpu_T[0]);
+}
+
+static inline void gen_op_mov_reg_T1(int ot, int reg)
+{
+ gen_op_mov_reg_v(ot, reg, cpu_T[1]);
+}
+
+static inline void gen_op_mov_reg_A0(int size, int reg)
+{
+ switch(size) {
+ case 0:
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_A0, 0, 16);
+ break;
+ default: /* XXX this shouldn't be reached; abort? */
+ case 1:
+ /* For x86_64, this sets the higher half of register to zero.
+ For i386, this is equivalent to a mov. */
+ tcg_gen_ext32u_tl(cpu_regs[reg], cpu_A0);
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ tcg_gen_mov_tl(cpu_regs[reg], cpu_A0);
+ break;
+#endif
+ }
+}
+
+static inline void gen_op_mov_v_reg(int ot, TCGv t0, int reg)
+{
+ switch(ot) {
+ case OT_BYTE:
+ if (reg < 4 X86_64_DEF( || reg >= 8 || x86_64_hregs)) {
+ goto std_case;
+ } else {
+ tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8);
+ tcg_gen_ext8u_tl(t0, t0);
+ }
+ break;
+ default:
+ std_case:
+ tcg_gen_mov_tl(t0, cpu_regs[reg]);
+ break;
+ }
+}
+
+static inline void gen_op_mov_TN_reg(int ot, int t_index, int reg)
+{
+ gen_op_mov_v_reg(ot, cpu_T[t_index], reg);
+}
+
+static inline void gen_op_movl_A0_reg(int reg)
+{
+ tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]);
+}
+
+static inline void gen_op_addl_A0_im(int32_t val)
+{
+ tcg_gen_addi_tl(cpu_A0, cpu_A0, val);
+#ifdef TARGET_X86_64
+ tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
+#endif
+}
+
+#ifdef TARGET_X86_64
+static inline void gen_op_addq_A0_im(int64_t val)
+{
+ tcg_gen_addi_tl(cpu_A0, cpu_A0, val);
+}
+#endif
+
+static void gen_add_A0_im(DisasContext *s, int val)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s))
+ gen_op_addq_A0_im(val);
+ else
+#endif
+ gen_op_addl_A0_im(val);
+}
+
+static inline void gen_op_addl_T0_T1(void)
+{
+ tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+}
+
+static inline void gen_op_jmp_T0(void)
+{
+ tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUState, eip));
+}
+
+static inline void gen_op_add_reg_im(int size, int reg, int32_t val)
+{
+ switch(size) {
+ case 0:
+ tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val);
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16);
+ break;
+ case 1:
+ tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val);
+ /* For x86_64, this sets the higher half of register to zero.
+ For i386, this is equivalent to a nop. */
+ tcg_gen_ext32u_tl(cpu_tmp0, cpu_tmp0);
+ tcg_gen_mov_tl(cpu_regs[reg], cpu_tmp0);
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ tcg_gen_addi_tl(cpu_regs[reg], cpu_regs[reg], val);
+ break;
+#endif
+ }
+}
+
+static inline void gen_op_add_reg_T0(int size, int reg)
+{
+ switch(size) {
+ case 0:
+ tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]);
+ tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16);
+ break;
+ case 1:
+ tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]);
+ /* For x86_64, this sets the higher half of register to zero.
+ For i386, this is equivalent to a nop. */
+ tcg_gen_ext32u_tl(cpu_tmp0, cpu_tmp0);
+ tcg_gen_mov_tl(cpu_regs[reg], cpu_tmp0);
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ tcg_gen_add_tl(cpu_regs[reg], cpu_regs[reg], cpu_T[0]);
+ break;
+#endif
+ }
+}
+
+static inline void gen_op_set_cc_op(int32_t val)
+{
+ tcg_gen_movi_i32(cpu_cc_op, val);
+}
+
+static inline void gen_op_addl_A0_reg_sN(int shift, int reg)
+{
+ tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]);
+ if (shift != 0)
+ tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift);
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
+ /* For x86_64, this sets the higher half of register to zero.
+ For i386, this is equivalent to a nop. */
+ tcg_gen_ext32u_tl(cpu_A0, cpu_A0);
+}
+
+static inline void gen_op_movl_A0_seg(int reg)
+{
+ tcg_gen_ld32u_tl(cpu_A0, cpu_env, offsetof(CPUState, segs[reg].base) + REG_L_OFFSET);
+}
+
+static inline void gen_op_addl_A0_seg(int reg)
+{
+ tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUState, segs[reg].base));
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
+#ifdef TARGET_X86_64
+ tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
+#endif
+}
+
+#ifdef TARGET_X86_64
+static inline void gen_op_movq_A0_seg(int reg)
+{
+ tcg_gen_ld_tl(cpu_A0, cpu_env, offsetof(CPUState, segs[reg].base));
+}
+
+static inline void gen_op_addq_A0_seg(int reg)
+{
+ tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUState, segs[reg].base));
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
+}
+
+static inline void gen_op_movq_A0_reg(int reg)
+{
+ tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]);
+}
+
+static inline void gen_op_addq_A0_reg_sN(int shift, int reg)
+{
+ tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]);
+ if (shift != 0)
+ tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift);
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
+}
+#endif
+
+static inline void gen_op_lds_T0_A0(int idx)
+{
+ int mem_index = (idx >> 2) - 1;
+ switch(idx & 3) {
+ case 0:
+ tcg_gen_qemu_ld8s(cpu_T[0], cpu_A0, mem_index);
+ break;
+ case 1:
+ tcg_gen_qemu_ld16s(cpu_T[0], cpu_A0, mem_index);
+ break;
+ default:
+ case 2:
+ tcg_gen_qemu_ld32s(cpu_T[0], cpu_A0, mem_index);
+ break;
+ }
+}
+
+static inline void gen_op_ld_v(int idx, TCGv t0, TCGv a0)
+{
+ int mem_index = (idx >> 2) - 1;
+ switch(idx & 3) {
+ case 0:
+ tcg_gen_qemu_ld8u(t0, a0, mem_index);
+ break;
+ case 1:
+ tcg_gen_qemu_ld16u(t0, a0, mem_index);
+ break;
+ case 2:
+ tcg_gen_qemu_ld32u(t0, a0, mem_index);
+ break;
+ default:
+ case 3:
+ /* Should never happen on 32-bit targets. */
+#ifdef TARGET_X86_64
+ tcg_gen_qemu_ld64(t0, a0, mem_index);
+#endif
+ break;
+ }
+}
+
+/* XXX: always use ldu or lds */
+static inline void gen_op_ld_T0_A0(int idx)
+{
+ gen_op_ld_v(idx, cpu_T[0], cpu_A0);
+}
+
+static inline void gen_op_ldu_T0_A0(int idx)
+{
+ gen_op_ld_v(idx, cpu_T[0], cpu_A0);
+}
+
+static inline void gen_op_ld_T1_A0(int idx)
+{
+ gen_op_ld_v(idx, cpu_T[1], cpu_A0);
+}
+
+static inline void gen_op_st_v(int idx, TCGv t0, TCGv a0)
+{
+ int mem_index = (idx >> 2) - 1;
+ switch(idx & 3) {
+ case 0:
+ tcg_gen_qemu_st8(t0, a0, mem_index);
+ break;
+ case 1:
+ tcg_gen_qemu_st16(t0, a0, mem_index);
+ break;
+ case 2:
+ tcg_gen_qemu_st32(t0, a0, mem_index);
+ break;
+ default:
+ case 3:
+ /* Should never happen on 32-bit targets. */
+#ifdef TARGET_X86_64
+ tcg_gen_qemu_st64(t0, a0, mem_index);
+#endif
+ break;
+ }
+}
+
+static inline void gen_op_st_T0_A0(int idx)
+{
+ gen_op_st_v(idx, cpu_T[0], cpu_A0);
+}
+
+static inline void gen_op_st_T1_A0(int idx)
+{
+ gen_op_st_v(idx, cpu_T[1], cpu_A0);
+}
+
+static inline void gen_jmp_im(target_ulong pc)
+{
+ tcg_gen_movi_tl(cpu_tmp0, pc);
+ tcg_gen_st_tl(cpu_tmp0, cpu_env, offsetof(CPUState, eip));
+}
+
+static inline void gen_string_movl_A0_ESI(DisasContext *s)
+{
+ int override;
+
+ override = s->override;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ if (override >= 0) {
+ gen_op_movq_A0_seg(override);
+ gen_op_addq_A0_reg_sN(0, R_ESI);
+ } else {
+ gen_op_movq_A0_reg(R_ESI);
+ }
+ } else
+#endif
+ if (s->aflag) {
+ /* 32 bit address */
+ if (s->addseg && override < 0)
+ override = R_DS;
+ if (override >= 0) {
+ gen_op_movl_A0_seg(override);
+ gen_op_addl_A0_reg_sN(0, R_ESI);
+ } else {
+ gen_op_movl_A0_reg(R_ESI);
+ }
+ } else {
+ /* 16 address, always override */
+ if (override < 0)
+ override = R_DS;
+ gen_op_movl_A0_reg(R_ESI);
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_seg(override);
+ }
+}
+
+static inline void gen_string_movl_A0_EDI(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg(R_EDI);
+ } else
+#endif
+ if (s->aflag) {
+ if (s->addseg) {
+ gen_op_movl_A0_seg(R_ES);
+ gen_op_addl_A0_reg_sN(0, R_EDI);
+ } else {
+ gen_op_movl_A0_reg(R_EDI);
+ }
+ } else {
+ gen_op_movl_A0_reg(R_EDI);
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_seg(R_ES);
+ }
+}
+
+static inline void gen_op_movl_T0_Dshift(int ot)
+{
+ tcg_gen_ld32s_tl(cpu_T[0], cpu_env, offsetof(CPUState, df));
+ tcg_gen_shli_tl(cpu_T[0], cpu_T[0], ot);
+};
+
+static void gen_extu(int ot, TCGv reg)
+{
+ switch(ot) {
+ case OT_BYTE:
+ tcg_gen_ext8u_tl(reg, reg);
+ break;
+ case OT_WORD:
+ tcg_gen_ext16u_tl(reg, reg);
+ break;
+ case OT_LONG:
+ tcg_gen_ext32u_tl(reg, reg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void gen_exts(int ot, TCGv reg)
+{
+ switch(ot) {
+ case OT_BYTE:
+ tcg_gen_ext8s_tl(reg, reg);
+ break;
+ case OT_WORD:
+ tcg_gen_ext16s_tl(reg, reg);
+ break;
+ case OT_LONG:
+ tcg_gen_ext32s_tl(reg, reg);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void gen_op_jnz_ecx(int size, int label1)
+{
+ tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]);
+ gen_extu(size + 1, cpu_tmp0);
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, label1);
+}
+
+static inline void gen_op_jz_ecx(int size, int label1)
+{
+ tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]);
+ gen_extu(size + 1, cpu_tmp0);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1);
+}
+
+static void gen_helper_in_func(int ot, TCGv v, TCGv_i32 n)
+{
+ switch (ot) {
+ case 0: gen_helper_inb(v, n); break;
+ case 1: gen_helper_inw(v, n); break;
+ case 2: gen_helper_inl(v, n); break;
+ }
+
+}
+
+static void gen_helper_out_func(int ot, TCGv_i32 v, TCGv_i32 n)
+{
+ switch (ot) {
+ case 0: gen_helper_outb(v, n); break;
+ case 1: gen_helper_outw(v, n); break;
+ case 2: gen_helper_outl(v, n); break;
+ }
+
+}
+
+static void gen_check_io(DisasContext *s, int ot, target_ulong cur_eip,
+ uint32_t svm_flags)
+{
+ int state_saved;
+ target_ulong next_eip;
+
+ state_saved = 0;
+ if (s->pe && (s->cpl > s->iopl || s->vm86)) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ state_saved = 1;
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ switch (ot) {
+ case 0: gen_helper_check_iob(cpu_tmp2_i32); break;
+ case 1: gen_helper_check_iow(cpu_tmp2_i32); break;
+ case 2: gen_helper_check_iol(cpu_tmp2_i32); break;
+ }
+ }
+ if(s->flags & HF_SVMI_MASK) {
+ if (!state_saved) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ }
+ svm_flags |= (1 << (4 + ot));
+ next_eip = s->pc - s->cs_base;
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_svm_check_io(cpu_tmp2_i32, tcg_const_i32(svm_flags),
+ tcg_const_i32(next_eip - cur_eip));
+ }
+}
+
+static inline void gen_movs(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_string_movl_A0_EDI(s);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_ESI);
+ gen_op_add_reg_T0(s->aflag, R_EDI);
+}
+
+static inline void gen_update_cc_op(DisasContext *s)
+{
+ if (s->cc_op != CC_OP_DYNAMIC) {
+ gen_op_set_cc_op(s->cc_op);
+ s->cc_op = CC_OP_DYNAMIC;
+ }
+}
+
+static void gen_op_update1_cc(void)
+{
+ tcg_gen_discard_tl(cpu_cc_src);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+}
+
+static void gen_op_update2_cc(void)
+{
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+}
+
+static inline void gen_op_cmpl_T0_T1_cc(void)
+{
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]);
+ tcg_gen_sub_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]);
+}
+
+static inline void gen_op_testl_T0_T1_cc(void)
+{
+ tcg_gen_discard_tl(cpu_cc_src);
+ tcg_gen_and_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]);
+}
+
+static void gen_op_update_neg_cc(void)
+{
+ tcg_gen_neg_tl(cpu_cc_src, cpu_T[0]);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+}
+
+/* compute eflags.C to reg */
+static void gen_compute_eflags_c(TCGv reg)
+{
+ gen_helper_cc_compute_c(cpu_tmp2_i32, cpu_cc_op);
+ tcg_gen_extu_i32_tl(reg, cpu_tmp2_i32);
+}
+
+/* compute all eflags to cc_src */
+static void gen_compute_eflags(TCGv reg)
+{
+ gen_helper_cc_compute_all(cpu_tmp2_i32, cpu_cc_op);
+ tcg_gen_extu_i32_tl(reg, cpu_tmp2_i32);
+}
+
+static inline void gen_setcc_slow_T0(DisasContext *s, int jcc_op)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ switch(jcc_op) {
+ case JCC_O:
+ gen_compute_eflags(cpu_T[0]);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 11);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ case JCC_B:
+ gen_compute_eflags_c(cpu_T[0]);
+ break;
+ case JCC_Z:
+ gen_compute_eflags(cpu_T[0]);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 6);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ case JCC_BE:
+ gen_compute_eflags(cpu_tmp0);
+ tcg_gen_shri_tl(cpu_T[0], cpu_tmp0, 6);
+ tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ case JCC_S:
+ gen_compute_eflags(cpu_T[0]);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 7);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ case JCC_P:
+ gen_compute_eflags(cpu_T[0]);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 2);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ case JCC_L:
+ gen_compute_eflags(cpu_tmp0);
+ tcg_gen_shri_tl(cpu_T[0], cpu_tmp0, 11); /* CC_O */
+ tcg_gen_shri_tl(cpu_tmp0, cpu_tmp0, 7); /* CC_S */
+ tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp0);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ default:
+ case JCC_LE:
+ gen_compute_eflags(cpu_tmp0);
+ tcg_gen_shri_tl(cpu_T[0], cpu_tmp0, 11); /* CC_O */
+ tcg_gen_shri_tl(cpu_tmp4, cpu_tmp0, 7); /* CC_S */
+ tcg_gen_shri_tl(cpu_tmp0, cpu_tmp0, 6); /* CC_Z */
+ tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp4);
+ tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 1);
+ break;
+ }
+}
+
+/* return true if setcc_slow is not needed (WARNING: must be kept in
+ sync with gen_jcc1) */
+static int is_fast_jcc_case(DisasContext *s, int b)
+{
+ int jcc_op;
+ jcc_op = (b >> 1) & 7;
+ switch(s->cc_op) {
+ /* we optimize the cmp/jcc case */
+ case CC_OP_SUBB:
+ case CC_OP_SUBW:
+ case CC_OP_SUBL:
+ case CC_OP_SUBQ:
+ if (jcc_op == JCC_O || jcc_op == JCC_P)
+ goto slow_jcc;
+ break;
+
+ /* some jumps are easy to compute */
+ case CC_OP_ADDB:
+ case CC_OP_ADDW:
+ case CC_OP_ADDL:
+ case CC_OP_ADDQ:
+
+ case CC_OP_LOGICB:
+ case CC_OP_LOGICW:
+ case CC_OP_LOGICL:
+ case CC_OP_LOGICQ:
+
+ case CC_OP_INCB:
+ case CC_OP_INCW:
+ case CC_OP_INCL:
+ case CC_OP_INCQ:
+
+ case CC_OP_DECB:
+ case CC_OP_DECW:
+ case CC_OP_DECL:
+ case CC_OP_DECQ:
+
+ case CC_OP_SHLB:
+ case CC_OP_SHLW:
+ case CC_OP_SHLL:
+ case CC_OP_SHLQ:
+ if (jcc_op != JCC_Z && jcc_op != JCC_S)
+ goto slow_jcc;
+ break;
+ default:
+ slow_jcc:
+ return 0;
+ }
+ return 1;
+}
+
+/* generate a conditional jump to label 'l1' according to jump opcode
+ value 'b'. In the fast case, T0 is guaranted not to be used. */
+static inline void gen_jcc1(DisasContext *s, int cc_op, int b, int l1)
+{
+ int inv, jcc_op, size, cond;
+ TCGv t0;
+
+ inv = b & 1;
+ jcc_op = (b >> 1) & 7;
+
+ switch(cc_op) {
+ /* we optimize the cmp/jcc case */
+ case CC_OP_SUBB:
+ case CC_OP_SUBW:
+ case CC_OP_SUBL:
+ case CC_OP_SUBQ:
+
+ size = cc_op - CC_OP_SUBB;
+ switch(jcc_op) {
+ case JCC_Z:
+ fast_jcc_z:
+ switch(size) {
+ case 0:
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0xff);
+ t0 = cpu_tmp0;
+ break;
+ case 1:
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0xffff);
+ t0 = cpu_tmp0;
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0xffffffff);
+ t0 = cpu_tmp0;
+ break;
+#endif
+ default:
+ t0 = cpu_cc_dst;
+ break;
+ }
+ tcg_gen_brcondi_tl(inv ? TCG_COND_NE : TCG_COND_EQ, t0, 0, l1);
+ break;
+ case JCC_S:
+ fast_jcc_s:
+ switch(size) {
+ case 0:
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x80);
+ tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0,
+ 0, l1);
+ break;
+ case 1:
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x8000);
+ tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0,
+ 0, l1);
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_dst, 0x80000000);
+ tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE, cpu_tmp0,
+ 0, l1);
+ break;
+#endif
+ default:
+ tcg_gen_brcondi_tl(inv ? TCG_COND_GE : TCG_COND_LT, cpu_cc_dst,
+ 0, l1);
+ break;
+ }
+ break;
+
+ case JCC_B:
+ cond = inv ? TCG_COND_GEU : TCG_COND_LTU;
+ goto fast_jcc_b;
+ case JCC_BE:
+ cond = inv ? TCG_COND_GTU : TCG_COND_LEU;
+ fast_jcc_b:
+ tcg_gen_add_tl(cpu_tmp4, cpu_cc_dst, cpu_cc_src);
+ switch(size) {
+ case 0:
+ t0 = cpu_tmp0;
+ tcg_gen_andi_tl(cpu_tmp4, cpu_tmp4, 0xff);
+ tcg_gen_andi_tl(t0, cpu_cc_src, 0xff);
+ break;
+ case 1:
+ t0 = cpu_tmp0;
+ tcg_gen_andi_tl(cpu_tmp4, cpu_tmp4, 0xffff);
+ tcg_gen_andi_tl(t0, cpu_cc_src, 0xffff);
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ t0 = cpu_tmp0;
+ tcg_gen_andi_tl(cpu_tmp4, cpu_tmp4, 0xffffffff);
+ tcg_gen_andi_tl(t0, cpu_cc_src, 0xffffffff);
+ break;
+#endif
+ default:
+ t0 = cpu_cc_src;
+ break;
+ }
+ tcg_gen_brcond_tl(cond, cpu_tmp4, t0, l1);
+ break;
+
+ case JCC_L:
+ cond = inv ? TCG_COND_GE : TCG_COND_LT;
+ goto fast_jcc_l;
+ case JCC_LE:
+ cond = inv ? TCG_COND_GT : TCG_COND_LE;
+ fast_jcc_l:
+ tcg_gen_add_tl(cpu_tmp4, cpu_cc_dst, cpu_cc_src);
+ switch(size) {
+ case 0:
+ t0 = cpu_tmp0;
+ tcg_gen_ext8s_tl(cpu_tmp4, cpu_tmp4);
+ tcg_gen_ext8s_tl(t0, cpu_cc_src);
+ break;
+ case 1:
+ t0 = cpu_tmp0;
+ tcg_gen_ext16s_tl(cpu_tmp4, cpu_tmp4);
+ tcg_gen_ext16s_tl(t0, cpu_cc_src);
+ break;
+#ifdef TARGET_X86_64
+ case 2:
+ t0 = cpu_tmp0;
+ tcg_gen_ext32s_tl(cpu_tmp4, cpu_tmp4);
+ tcg_gen_ext32s_tl(t0, cpu_cc_src);
+ break;
+#endif
+ default:
+ t0 = cpu_cc_src;
+ break;
+ }
+ tcg_gen_brcond_tl(cond, cpu_tmp4, t0, l1);
+ break;
+
+ default:
+ goto slow_jcc;
+ }
+ break;
+
+ /* some jumps are easy to compute */
+ case CC_OP_ADDB:
+ case CC_OP_ADDW:
+ case CC_OP_ADDL:
+ case CC_OP_ADDQ:
+
+ case CC_OP_ADCB:
+ case CC_OP_ADCW:
+ case CC_OP_ADCL:
+ case CC_OP_ADCQ:
+
+ case CC_OP_SBBB:
+ case CC_OP_SBBW:
+ case CC_OP_SBBL:
+ case CC_OP_SBBQ:
+
+ case CC_OP_LOGICB:
+ case CC_OP_LOGICW:
+ case CC_OP_LOGICL:
+ case CC_OP_LOGICQ:
+
+ case CC_OP_INCB:
+ case CC_OP_INCW:
+ case CC_OP_INCL:
+ case CC_OP_INCQ:
+
+ case CC_OP_DECB:
+ case CC_OP_DECW:
+ case CC_OP_DECL:
+ case CC_OP_DECQ:
+
+ case CC_OP_SHLB:
+ case CC_OP_SHLW:
+ case CC_OP_SHLL:
+ case CC_OP_SHLQ:
+
+ case CC_OP_SARB:
+ case CC_OP_SARW:
+ case CC_OP_SARL:
+ case CC_OP_SARQ:
+ switch(jcc_op) {
+ case JCC_Z:
+ size = (cc_op - CC_OP_ADDB) & 3;
+ goto fast_jcc_z;
+ case JCC_S:
+ size = (cc_op - CC_OP_ADDB) & 3;
+ goto fast_jcc_s;
+ default:
+ goto slow_jcc;
+ }
+ break;
+ default:
+ slow_jcc:
+ gen_setcc_slow_T0(s, jcc_op);
+ tcg_gen_brcondi_tl(inv ? TCG_COND_EQ : TCG_COND_NE,
+ cpu_T[0], 0, l1);
+ break;
+ }
+}
+
+/* XXX: does not work with gdbstub "ice" single step - not a
+ serious problem */
+static int gen_jz_ecx_string(DisasContext *s, target_ulong next_eip)
+{
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ gen_op_jnz_ecx(s->aflag, l1);
+ gen_set_label(l2);
+ gen_jmp_tb(s, next_eip, 1);
+ gen_set_label(l1);
+ return l2;
+}
+
+static inline void gen_stos(DisasContext *s, int ot)
+{
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ gen_string_movl_A0_EDI(s);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_EDI);
+}
+
+static inline void gen_lods(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_mov_reg_T0(ot, R_EAX);
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_ESI);
+}
+
+static inline void gen_scas(DisasContext *s, int ot)
+{
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ gen_string_movl_A0_EDI(s);
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_op_cmpl_T0_T1_cc();
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_EDI);
+}
+
+static inline void gen_cmps(DisasContext *s, int ot)
+{
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_string_movl_A0_EDI(s);
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_op_cmpl_T0_T1_cc();
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_ESI);
+ gen_op_add_reg_T0(s->aflag, R_EDI);
+}
+
+static inline void gen_ins(DisasContext *s, int ot)
+{
+ if (use_icount)
+ gen_io_start();
+ gen_string_movl_A0_EDI(s);
+ /* Note: we must do this dummy write first to be restartable in
+ case of page fault. */
+ gen_op_movl_T0_0();
+ gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_mov_TN_reg(OT_WORD, 1, R_EDX);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[1]);
+ tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
+ gen_helper_in_func(ot, cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_EDI);
+ if (use_icount)
+ gen_io_end();
+}
+
+static inline void gen_outs(DisasContext *s, int ot)
+{
+ if (use_icount)
+ gen_io_start();
+ gen_string_movl_A0_ESI(s);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+
+ gen_op_mov_TN_reg(OT_WORD, 1, R_EDX);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[1]);
+ tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
+ tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[0]);
+ gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32);
+
+ gen_op_movl_T0_Dshift(ot);
+ gen_op_add_reg_T0(s->aflag, R_ESI);
+ if (use_icount)
+ gen_io_end();
+}
+
+/* same method as Valgrind : we generate jumps to current or next
+ instruction */
+#define GEN_REPZ(op) \
+static inline void gen_repz_ ## op(DisasContext *s, int ot, \
+ target_ulong cur_eip, target_ulong next_eip) \
+{ \
+ int l2;\
+ gen_update_cc_op(s); \
+ l2 = gen_jz_ecx_string(s, next_eip); \
+ gen_ ## op(s, ot); \
+ gen_op_add_reg_im(s->aflag, R_ECX, -1); \
+ /* a loop would cause two single step exceptions if ECX = 1 \
+ before rep string_insn */ \
+ if (!s->jmp_opt) \
+ gen_op_jz_ecx(s->aflag, l2); \
+ gen_jmp(s, cur_eip); \
+}
+
+#define GEN_REPZ2(op) \
+static inline void gen_repz_ ## op(DisasContext *s, int ot, \
+ target_ulong cur_eip, \
+ target_ulong next_eip, \
+ int nz) \
+{ \
+ int l2;\
+ gen_update_cc_op(s); \
+ l2 = gen_jz_ecx_string(s, next_eip); \
+ gen_ ## op(s, ot); \
+ gen_op_add_reg_im(s->aflag, R_ECX, -1); \
+ gen_op_set_cc_op(CC_OP_SUBB + ot); \
+ gen_jcc1(s, CC_OP_SUBB + ot, (JCC_Z << 1) | (nz ^ 1), l2); \
+ if (!s->jmp_opt) \
+ gen_op_jz_ecx(s->aflag, l2); \
+ gen_jmp(s, cur_eip); \
+}
+
+GEN_REPZ(movs)
+GEN_REPZ(stos)
+GEN_REPZ(lods)
+GEN_REPZ(ins)
+GEN_REPZ(outs)
+GEN_REPZ2(scas)
+GEN_REPZ2(cmps)
+
+static void gen_helper_fp_arith_ST0_FT0(int op)
+{
+ switch (op) {
+ case 0: gen_helper_fadd_ST0_FT0(); break;
+ case 1: gen_helper_fmul_ST0_FT0(); break;
+ case 2: gen_helper_fcom_ST0_FT0(); break;
+ case 3: gen_helper_fcom_ST0_FT0(); break;
+ case 4: gen_helper_fsub_ST0_FT0(); break;
+ case 5: gen_helper_fsubr_ST0_FT0(); break;
+ case 6: gen_helper_fdiv_ST0_FT0(); break;
+ case 7: gen_helper_fdivr_ST0_FT0(); break;
+ }
+}
+
+/* NOTE the exception in "r" op ordering */
+static void gen_helper_fp_arith_STN_ST0(int op, int opreg)
+{
+ TCGv_i32 tmp = tcg_const_i32(opreg);
+ switch (op) {
+ case 0: gen_helper_fadd_STN_ST0(tmp); break;
+ case 1: gen_helper_fmul_STN_ST0(tmp); break;
+ case 4: gen_helper_fsubr_STN_ST0(tmp); break;
+ case 5: gen_helper_fsub_STN_ST0(tmp); break;
+ case 6: gen_helper_fdivr_STN_ST0(tmp); break;
+ case 7: gen_helper_fdiv_STN_ST0(tmp); break;
+ }
+}
+
+/* if d == OR_TMP0, it means memory operand (address in A0) */
+static void gen_op(DisasContext *s1, int op, int ot, int d)
+{
+ if (d != OR_TMP0) {
+ gen_op_mov_TN_reg(ot, 0, d);
+ } else {
+ gen_op_ld_T0_A0(ot + s1->mem_index);
+ }
+ switch(op) {
+ case OP_ADCL:
+ if (s1->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s1->cc_op);
+ gen_compute_eflags_c(cpu_tmp4);
+ tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_tmp4);
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp4);
+ tcg_gen_shli_i32(cpu_tmp2_i32, cpu_tmp2_i32, 2);
+ tcg_gen_addi_i32(cpu_cc_op, cpu_tmp2_i32, CC_OP_ADDB + ot);
+ s1->cc_op = CC_OP_DYNAMIC;
+ break;
+ case OP_SBBL:
+ if (s1->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s1->cc_op);
+ gen_compute_eflags_c(cpu_tmp4);
+ tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_tmp4);
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp4);
+ tcg_gen_shli_i32(cpu_tmp2_i32, cpu_tmp2_i32, 2);
+ tcg_gen_addi_i32(cpu_cc_op, cpu_tmp2_i32, CC_OP_SUBB + ot);
+ s1->cc_op = CC_OP_DYNAMIC;
+ break;
+ case OP_ADDL:
+ gen_op_addl_T0_T1();
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_update2_cc();
+ s1->cc_op = CC_OP_ADDB + ot;
+ break;
+ case OP_SUBL:
+ tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_update2_cc();
+ s1->cc_op = CC_OP_SUBB + ot;
+ break;
+ default:
+ case OP_ANDL:
+ tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_update1_cc();
+ s1->cc_op = CC_OP_LOGICB + ot;
+ break;
+ case OP_ORL:
+ tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_update1_cc();
+ s1->cc_op = CC_OP_LOGICB + ot;
+ break;
+ case OP_XORL:
+ tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_op_update1_cc();
+ s1->cc_op = CC_OP_LOGICB + ot;
+ break;
+ case OP_CMPL:
+ gen_op_cmpl_T0_T1_cc();
+ s1->cc_op = CC_OP_SUBB + ot;
+ break;
+ }
+}
+
+/* if d == OR_TMP0, it means memory operand (address in A0) */
+static void gen_inc(DisasContext *s1, int ot, int d, int c)
+{
+ if (d != OR_TMP0)
+ gen_op_mov_TN_reg(ot, 0, d);
+ else
+ gen_op_ld_T0_A0(ot + s1->mem_index);
+ if (s1->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s1->cc_op);
+ if (c > 0) {
+ tcg_gen_addi_tl(cpu_T[0], cpu_T[0], 1);
+ s1->cc_op = CC_OP_INCB + ot;
+ } else {
+ tcg_gen_addi_tl(cpu_T[0], cpu_T[0], -1);
+ s1->cc_op = CC_OP_DECB + ot;
+ }
+ if (d != OR_TMP0)
+ gen_op_mov_reg_T0(ot, d);
+ else
+ gen_op_st_T0_A0(ot + s1->mem_index);
+ gen_compute_eflags_c(cpu_cc_src);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+}
+
+static void gen_shift_rm_T1(DisasContext *s, int ot, int op1,
+ int is_right, int is_arith)
+{
+ target_ulong mask;
+ int shift_label;
+ TCGv t0, t1;
+
+ if (ot == OT_QUAD)
+ mask = 0x3f;
+ else
+ mask = 0x1f;
+
+ /* load */
+ if (op1 == OR_TMP0)
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_TN_reg(ot, 0, op1);
+
+ tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask);
+
+ tcg_gen_addi_tl(cpu_tmp5, cpu_T[1], -1);
+
+ if (is_right) {
+ if (is_arith) {
+ gen_exts(ot, cpu_T[0]);
+ tcg_gen_sar_tl(cpu_T3, cpu_T[0], cpu_tmp5);
+ tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ } else {
+ gen_extu(ot, cpu_T[0]);
+ tcg_gen_shr_tl(cpu_T3, cpu_T[0], cpu_tmp5);
+ tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ }
+ } else {
+ tcg_gen_shl_tl(cpu_T3, cpu_T[0], cpu_tmp5);
+ tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ }
+
+ /* store */
+ if (op1 == OR_TMP0)
+ gen_op_st_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_reg_T0(ot, op1);
+
+ /* update eflags if non zero shift */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+
+ /* XXX: inefficient */
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+
+ tcg_gen_mov_tl(t0, cpu_T[0]);
+ tcg_gen_mov_tl(t1, cpu_T3);
+
+ shift_label = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_T[1], 0, shift_label);
+
+ tcg_gen_mov_tl(cpu_cc_src, t1);
+ tcg_gen_mov_tl(cpu_cc_dst, t0);
+ if (is_right)
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SARB + ot);
+ else
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SHLB + ot);
+
+ gen_set_label(shift_label);
+ s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_shift_rm_im(DisasContext *s, int ot, int op1, int op2,
+ int is_right, int is_arith)
+{
+ int mask;
+
+ if (ot == OT_QUAD)
+ mask = 0x3f;
+ else
+ mask = 0x1f;
+
+ /* load */
+ if (op1 == OR_TMP0)
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_TN_reg(ot, 0, op1);
+
+ op2 &= mask;
+ if (op2 != 0) {
+ if (is_right) {
+ if (is_arith) {
+ gen_exts(ot, cpu_T[0]);
+ tcg_gen_sari_tl(cpu_tmp4, cpu_T[0], op2 - 1);
+ tcg_gen_sari_tl(cpu_T[0], cpu_T[0], op2);
+ } else {
+ gen_extu(ot, cpu_T[0]);
+ tcg_gen_shri_tl(cpu_tmp4, cpu_T[0], op2 - 1);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], op2);
+ }
+ } else {
+ tcg_gen_shli_tl(cpu_tmp4, cpu_T[0], op2 - 1);
+ tcg_gen_shli_tl(cpu_T[0], cpu_T[0], op2);
+ }
+ }
+
+ /* store */
+ if (op1 == OR_TMP0)
+ gen_op_st_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_reg_T0(ot, op1);
+
+ /* update eflags if non zero shift */
+ if (op2 != 0) {
+ tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ if (is_right)
+ s->cc_op = CC_OP_SARB + ot;
+ else
+ s->cc_op = CC_OP_SHLB + ot;
+ }
+}
+
+static inline void tcg_gen_lshift(TCGv ret, TCGv arg1, target_long arg2)
+{
+ if (arg2 >= 0)
+ tcg_gen_shli_tl(ret, arg1, arg2);
+ else
+ tcg_gen_shri_tl(ret, arg1, -arg2);
+}
+
+static void gen_rot_rm_T1(DisasContext *s, int ot, int op1,
+ int is_right)
+{
+ target_ulong mask;
+ int label1, label2, data_bits;
+ TCGv t0, t1, t2, a0;
+
+ /* XXX: inefficient, but we must use local temps */
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ t2 = tcg_temp_local_new();
+ a0 = tcg_temp_local_new();
+
+ if (ot == OT_QUAD)
+ mask = 0x3f;
+ else
+ mask = 0x1f;
+
+ /* load */
+ if (op1 == OR_TMP0) {
+ tcg_gen_mov_tl(a0, cpu_A0);
+ gen_op_ld_v(ot + s->mem_index, t0, a0);
+ } else {
+ gen_op_mov_v_reg(ot, t0, op1);
+ }
+
+ tcg_gen_mov_tl(t1, cpu_T[1]);
+
+ tcg_gen_andi_tl(t1, t1, mask);
+
+ /* Must test zero case to avoid using undefined behaviour in TCG
+ shifts. */
+ label1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, label1);
+
+ if (ot <= OT_WORD)
+ tcg_gen_andi_tl(cpu_tmp0, t1, (1 << (3 + ot)) - 1);
+ else
+ tcg_gen_mov_tl(cpu_tmp0, t1);
+
+ gen_extu(ot, t0);
+ tcg_gen_mov_tl(t2, t0);
+
+ data_bits = 8 << ot;
+ /* XXX: rely on behaviour of shifts when operand 2 overflows (XXX:
+ fix TCG definition) */
+ if (is_right) {
+ tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp0);
+ tcg_gen_subfi_tl(cpu_tmp0, data_bits, cpu_tmp0);
+ tcg_gen_shl_tl(t0, t0, cpu_tmp0);
+ } else {
+ tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp0);
+ tcg_gen_subfi_tl(cpu_tmp0, data_bits, cpu_tmp0);
+ tcg_gen_shr_tl(t0, t0, cpu_tmp0);
+ }
+ tcg_gen_or_tl(t0, t0, cpu_tmp4);
+
+ gen_set_label(label1);
+ /* store */
+ if (op1 == OR_TMP0) {
+ gen_op_st_v(ot + s->mem_index, t0, a0);
+ } else {
+ gen_op_mov_reg_v(ot, op1, t0);
+ }
+
+ /* update eflags */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+
+ label2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, label2);
+
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~(CC_O | CC_C));
+ tcg_gen_xor_tl(cpu_tmp0, t2, t0);
+ tcg_gen_lshift(cpu_tmp0, cpu_tmp0, 11 - (data_bits - 1));
+ tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, CC_O);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_tmp0);
+ if (is_right) {
+ tcg_gen_shri_tl(t0, t0, data_bits - 1);
+ }
+ tcg_gen_andi_tl(t0, t0, CC_C);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0);
+
+ tcg_gen_discard_tl(cpu_cc_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS);
+
+ gen_set_label(label2);
+ s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ tcg_temp_free(a0);
+}
+
+static void gen_rot_rm_im(DisasContext *s, int ot, int op1, int op2,
+ int is_right)
+{
+ int mask;
+ int data_bits;
+ TCGv t0, t1, a0;
+
+ /* XXX: inefficient, but we must use local temps */
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ a0 = tcg_temp_local_new();
+
+ if (ot == OT_QUAD)
+ mask = 0x3f;
+ else
+ mask = 0x1f;
+
+ /* load */
+ if (op1 == OR_TMP0) {
+ tcg_gen_mov_tl(a0, cpu_A0);
+ gen_op_ld_v(ot + s->mem_index, t0, a0);
+ } else {
+ gen_op_mov_v_reg(ot, t0, op1);
+ }
+
+ gen_extu(ot, t0);
+ tcg_gen_mov_tl(t1, t0);
+
+ op2 &= mask;
+ data_bits = 8 << ot;
+ if (op2 != 0) {
+ int shift = op2 & ((1 << (3 + ot)) - 1);
+ if (is_right) {
+ tcg_gen_shri_tl(cpu_tmp4, t0, shift);
+ tcg_gen_shli_tl(t0, t0, data_bits - shift);
+ }
+ else {
+ tcg_gen_shli_tl(cpu_tmp4, t0, shift);
+ tcg_gen_shri_tl(t0, t0, data_bits - shift);
+ }
+ tcg_gen_or_tl(t0, t0, cpu_tmp4);
+ }
+
+ /* store */
+ if (op1 == OR_TMP0) {
+ gen_op_st_v(ot + s->mem_index, t0, a0);
+ } else {
+ gen_op_mov_reg_v(ot, op1, t0);
+ }
+
+ if (op2 != 0) {
+ /* update eflags */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~(CC_O | CC_C));
+ tcg_gen_xor_tl(cpu_tmp0, t1, t0);
+ tcg_gen_lshift(cpu_tmp0, cpu_tmp0, 11 - (data_bits - 1));
+ tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, CC_O);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_tmp0);
+ if (is_right) {
+ tcg_gen_shri_tl(t0, t0, data_bits - 1);
+ }
+ tcg_gen_andi_tl(t0, t0, CC_C);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0);
+
+ tcg_gen_discard_tl(cpu_cc_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS);
+ s->cc_op = CC_OP_EFLAGS;
+ }
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(a0);
+}
+
+/* XXX: add faster immediate = 1 case */
+static void gen_rotc_rm_T1(DisasContext *s, int ot, int op1,
+ int is_right)
+{
+ int label1;
+
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+
+ /* load */
+ if (op1 == OR_TMP0)
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_TN_reg(ot, 0, op1);
+
+ if (is_right) {
+ switch (ot) {
+ case 0: gen_helper_rcrb(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+ case 1: gen_helper_rcrw(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+ case 2: gen_helper_rcrl(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+#ifdef TARGET_X86_64
+ case 3: gen_helper_rcrq(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+#endif
+ }
+ } else {
+ switch (ot) {
+ case 0: gen_helper_rclb(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+ case 1: gen_helper_rclw(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+ case 2: gen_helper_rcll(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+#ifdef TARGET_X86_64
+ case 3: gen_helper_rclq(cpu_T[0], cpu_T[0], cpu_T[1]); break;
+#endif
+ }
+ }
+ /* store */
+ if (op1 == OR_TMP0)
+ gen_op_st_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_reg_T0(ot, op1);
+
+ /* update eflags */
+ label1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cc_tmp, -1, label1);
+
+ tcg_gen_mov_tl(cpu_cc_src, cpu_cc_tmp);
+ tcg_gen_discard_tl(cpu_cc_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_EFLAGS);
+
+ gen_set_label(label1);
+ s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
+}
+
+/* XXX: add faster immediate case */
+static void gen_shiftd_rm_T1_T3(DisasContext *s, int ot, int op1,
+ int is_right)
+{
+ int label1, label2, data_bits;
+ target_ulong mask;
+ TCGv t0, t1, t2, a0;
+
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ t2 = tcg_temp_local_new();
+ a0 = tcg_temp_local_new();
+
+ if (ot == OT_QUAD)
+ mask = 0x3f;
+ else
+ mask = 0x1f;
+
+ /* load */
+ if (op1 == OR_TMP0) {
+ tcg_gen_mov_tl(a0, cpu_A0);
+ gen_op_ld_v(ot + s->mem_index, t0, a0);
+ } else {
+ gen_op_mov_v_reg(ot, t0, op1);
+ }
+
+ tcg_gen_andi_tl(cpu_T3, cpu_T3, mask);
+
+ tcg_gen_mov_tl(t1, cpu_T[1]);
+ tcg_gen_mov_tl(t2, cpu_T3);
+
+ /* Must test zero case to avoid using undefined behaviour in TCG
+ shifts. */
+ label1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1);
+
+ tcg_gen_addi_tl(cpu_tmp5, t2, -1);
+ if (ot == OT_WORD) {
+ /* Note: we implement the Intel behaviour for shift count > 16 */
+ if (is_right) {
+ tcg_gen_andi_tl(t0, t0, 0xffff);
+ tcg_gen_shli_tl(cpu_tmp0, t1, 16);
+ tcg_gen_or_tl(t0, t0, cpu_tmp0);
+ tcg_gen_ext32u_tl(t0, t0);
+
+ tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp5);
+
+ /* only needed if count > 16, but a test would complicate */
+ tcg_gen_subfi_tl(cpu_tmp5, 32, t2);
+ tcg_gen_shl_tl(cpu_tmp0, t0, cpu_tmp5);
+
+ tcg_gen_shr_tl(t0, t0, t2);
+
+ tcg_gen_or_tl(t0, t0, cpu_tmp0);
+ } else {
+ /* XXX: not optimal */
+ tcg_gen_andi_tl(t0, t0, 0xffff);
+ tcg_gen_shli_tl(t1, t1, 16);
+ tcg_gen_or_tl(t1, t1, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+
+ tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp5);
+ tcg_gen_subfi_tl(cpu_tmp0, 32, cpu_tmp5);
+ tcg_gen_shr_tl(cpu_tmp5, t1, cpu_tmp0);
+ tcg_gen_or_tl(cpu_tmp4, cpu_tmp4, cpu_tmp5);
+
+ tcg_gen_shl_tl(t0, t0, t2);
+ tcg_gen_subfi_tl(cpu_tmp5, 32, t2);
+ tcg_gen_shr_tl(t1, t1, cpu_tmp5);
+ tcg_gen_or_tl(t0, t0, t1);
+ }
+ } else {
+ data_bits = 8 << ot;
+ if (is_right) {
+ if (ot == OT_LONG)
+ tcg_gen_ext32u_tl(t0, t0);
+
+ tcg_gen_shr_tl(cpu_tmp4, t0, cpu_tmp5);
+
+ tcg_gen_shr_tl(t0, t0, t2);
+ tcg_gen_subfi_tl(cpu_tmp5, data_bits, t2);
+ tcg_gen_shl_tl(t1, t1, cpu_tmp5);
+ tcg_gen_or_tl(t0, t0, t1);
+
+ } else {
+ if (ot == OT_LONG)
+ tcg_gen_ext32u_tl(t1, t1);
+
+ tcg_gen_shl_tl(cpu_tmp4, t0, cpu_tmp5);
+
+ tcg_gen_shl_tl(t0, t0, t2);
+ tcg_gen_subfi_tl(cpu_tmp5, data_bits, t2);
+ tcg_gen_shr_tl(t1, t1, cpu_tmp5);
+ tcg_gen_or_tl(t0, t0, t1);
+ }
+ }
+ tcg_gen_mov_tl(t1, cpu_tmp4);
+
+ gen_set_label(label1);
+ /* store */
+ if (op1 == OR_TMP0) {
+ gen_op_st_v(ot + s->mem_index, t0, a0);
+ } else {
+ gen_op_mov_reg_v(ot, op1, t0);
+ }
+
+ /* update eflags */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+
+ label2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label2);
+
+ tcg_gen_mov_tl(cpu_cc_src, t1);
+ tcg_gen_mov_tl(cpu_cc_dst, t0);
+ if (is_right) {
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SARB + ot);
+ } else {
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SHLB + ot);
+ }
+ gen_set_label(label2);
+ s->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ tcg_temp_free(a0);
+}
+
+static void gen_shift(DisasContext *s1, int op, int ot, int d, int s)
+{
+ if (s != OR_TMP1)
+ gen_op_mov_TN_reg(ot, 1, s);
+ switch(op) {
+ case OP_ROL:
+ gen_rot_rm_T1(s1, ot, d, 0);
+ break;
+ case OP_ROR:
+ gen_rot_rm_T1(s1, ot, d, 1);
+ break;
+ case OP_SHL:
+ case OP_SHL1:
+ gen_shift_rm_T1(s1, ot, d, 0, 0);
+ break;
+ case OP_SHR:
+ gen_shift_rm_T1(s1, ot, d, 1, 0);
+ break;
+ case OP_SAR:
+ gen_shift_rm_T1(s1, ot, d, 1, 1);
+ break;
+ case OP_RCL:
+ gen_rotc_rm_T1(s1, ot, d, 0);
+ break;
+ case OP_RCR:
+ gen_rotc_rm_T1(s1, ot, d, 1);
+ break;
+ }
+}
+
+static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c)
+{
+ switch(op) {
+ case OP_ROL:
+ gen_rot_rm_im(s1, ot, d, c, 0);
+ break;
+ case OP_ROR:
+ gen_rot_rm_im(s1, ot, d, c, 1);
+ break;
+ case OP_SHL:
+ case OP_SHL1:
+ gen_shift_rm_im(s1, ot, d, c, 0, 0);
+ break;
+ case OP_SHR:
+ gen_shift_rm_im(s1, ot, d, c, 1, 0);
+ break;
+ case OP_SAR:
+ gen_shift_rm_im(s1, ot, d, c, 1, 1);
+ break;
+ default:
+ /* currently not optimized */
+ gen_op_movl_T1_im(c);
+ gen_shift(s1, op, ot, d, OR_TMP1);
+ break;
+ }
+}
+
+static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr)
+{
+ target_long disp;
+ int havesib;
+ int base;
+ int index;
+ int scale;
+ int opreg;
+ int mod, rm, code, override, must_add_seg;
+
+ override = s->override;
+ must_add_seg = s->addseg;
+ if (override >= 0)
+ must_add_seg = 1;
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+
+ if (s->aflag) {
+
+ havesib = 0;
+ base = rm;
+ index = 0;
+ scale = 0;
+
+ if (base == 4) {
+ havesib = 1;
+ code = ldub_code(s->pc++);
+ scale = (code >> 6) & 3;
+ index = ((code >> 3) & 7) | REX_X(s);
+ base = (code & 7);
+ }
+ base |= REX_B(s);
+
+ switch (mod) {
+ case 0:
+ if ((base & 7) == 5) {
+ base = -1;
+ disp = (int32_t)ldl_code(s->pc);
+ s->pc += 4;
+ if (CODE64(s) && !havesib) {
+ disp += s->pc + s->rip_offset;
+ }
+ } else {
+ disp = 0;
+ }
+ break;
+ case 1:
+ disp = (int8_t)ldub_code(s->pc++);
+ break;
+ default:
+ case 2:
+ disp = (int32_t)ldl_code(s->pc);
+ s->pc += 4;
+ break;
+ }
+
+ if (base >= 0) {
+ /* for correct popl handling with esp */
+ if (base == 4 && s->popl_esp_hack)
+ disp += s->popl_esp_hack;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg(base);
+ if (disp != 0) {
+ gen_op_addq_A0_im(disp);
+ }
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(base);
+ if (disp != 0)
+ gen_op_addl_A0_im(disp);
+ }
+ } else {
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_im(disp);
+ } else
+#endif
+ {
+ gen_op_movl_A0_im(disp);
+ }
+ }
+ /* index == 4 means no index */
+ if (havesib && (index != 4)) {
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_A0_reg_sN(scale, index);
+ } else
+#endif
+ {
+ gen_op_addl_A0_reg_sN(scale, index);
+ }
+ }
+ if (must_add_seg) {
+ if (override < 0) {
+ if (base == R_EBP || base == R_ESP)
+ override = R_SS;
+ else
+ override = R_DS;
+ }
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_addq_A0_seg(override);
+ } else
+#endif
+ {
+ gen_op_addl_A0_seg(override);
+ }
+ }
+ } else {
+ switch (mod) {
+ case 0:
+ if (rm == 6) {
+ disp = lduw_code(s->pc);
+ s->pc += 2;
+ gen_op_movl_A0_im(disp);
+ rm = 0; /* avoid SS override */
+ goto no_rm;
+ } else {
+ disp = 0;
+ }
+ break;
+ case 1:
+ disp = (int8_t)ldub_code(s->pc++);
+ break;
+ default:
+ case 2:
+ disp = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ }
+ switch(rm) {
+ case 0:
+ gen_op_movl_A0_reg(R_EBX);
+ gen_op_addl_A0_reg_sN(0, R_ESI);
+ break;
+ case 1:
+ gen_op_movl_A0_reg(R_EBX);
+ gen_op_addl_A0_reg_sN(0, R_EDI);
+ break;
+ case 2:
+ gen_op_movl_A0_reg(R_EBP);
+ gen_op_addl_A0_reg_sN(0, R_ESI);
+ break;
+ case 3:
+ gen_op_movl_A0_reg(R_EBP);
+ gen_op_addl_A0_reg_sN(0, R_EDI);
+ break;
+ case 4:
+ gen_op_movl_A0_reg(R_ESI);
+ break;
+ case 5:
+ gen_op_movl_A0_reg(R_EDI);
+ break;
+ case 6:
+ gen_op_movl_A0_reg(R_EBP);
+ break;
+ default:
+ case 7:
+ gen_op_movl_A0_reg(R_EBX);
+ break;
+ }
+ if (disp != 0)
+ gen_op_addl_A0_im(disp);
+ gen_op_andl_A0_ffff();
+ no_rm:
+ if (must_add_seg) {
+ if (override < 0) {
+ if (rm == 2 || rm == 3 || rm == 6)
+ override = R_SS;
+ else
+ override = R_DS;
+ }
+ gen_op_addl_A0_seg(override);
+ }
+ }
+
+ opreg = OR_A0;
+ disp = 0;
+ *reg_ptr = opreg;
+ *offset_ptr = disp;
+}
+
+static void gen_nop_modrm(DisasContext *s, int modrm)
+{
+ int mod, rm, base, code;
+
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ return;
+ rm = modrm & 7;
+
+ if (s->aflag) {
+
+ base = rm;
+
+ if (base == 4) {
+ code = ldub_code(s->pc++);
+ base = (code & 7);
+ }
+
+ switch (mod) {
+ case 0:
+ if (base == 5) {
+ s->pc += 4;
+ }
+ break;
+ case 1:
+ s->pc++;
+ break;
+ default:
+ case 2:
+ s->pc += 4;
+ break;
+ }
+ } else {
+ switch (mod) {
+ case 0:
+ if (rm == 6) {
+ s->pc += 2;
+ }
+ break;
+ case 1:
+ s->pc++;
+ break;
+ default:
+ case 2:
+ s->pc += 2;
+ break;
+ }
+ }
+}
+
+/* used for LEA and MOV AX, mem */
+static void gen_add_A0_ds_seg(DisasContext *s)
+{
+ int override, must_add_seg;
+ must_add_seg = s->addseg;
+ override = R_DS;
+ if (s->override >= 0) {
+ override = s->override;
+ must_add_seg = 1;
+ }
+ if (must_add_seg) {
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_addq_A0_seg(override);
+ } else
+#endif
+ {
+ gen_op_addl_A0_seg(override);
+ }
+ }
+}
+
+/* generate modrm memory load or store of 'reg'. TMP0 is used if reg ==
+ OR_TMP0 */
+static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store)
+{
+ int mod, rm, opreg, disp;
+
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod == 3) {
+ if (is_store) {
+ if (reg != OR_TMP0)
+ gen_op_mov_TN_reg(ot, 0, reg);
+ gen_op_mov_reg_T0(ot, rm);
+ } else {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ if (reg != OR_TMP0)
+ gen_op_mov_reg_T0(ot, reg);
+ }
+ } else {
+ gen_lea_modrm(s, modrm, &opreg, &disp);
+ if (is_store) {
+ if (reg != OR_TMP0)
+ gen_op_mov_TN_reg(ot, 0, reg);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ if (reg != OR_TMP0)
+ gen_op_mov_reg_T0(ot, reg);
+ }
+ }
+}
+
+static inline uint32_t insn_get(DisasContext *s, int ot)
+{
+ uint32_t ret;
+
+ switch(ot) {
+ case OT_BYTE:
+ ret = ldub_code(s->pc);
+ s->pc++;
+ break;
+ case OT_WORD:
+ ret = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ default:
+ case OT_LONG:
+ ret = ldl_code(s->pc);
+ s->pc += 4;
+ break;
+ }
+ return ret;
+}
+
+static inline int insn_const_size(unsigned int ot)
+{
+ if (ot <= OT_LONG)
+ return 1 << ot;
+ else
+ return 4;
+}
+
+static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip)
+{
+ TranslationBlock *tb;
+ target_ulong pc;
+
+ pc = s->cs_base + eip;
+ 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);
+ gen_jmp_im(eip);
+ tcg_gen_exit_tb((long)tb + tb_num);
+ } else {
+ /* jump to another page: currently not optimized */
+ gen_jmp_im(eip);
+ gen_eob(s);
+ }
+}
+
+static inline void gen_jcc(DisasContext *s, int b,
+ target_ulong val, target_ulong next_eip)
+{
+ int l1, l2, cc_op;
+
+ cc_op = s->cc_op;
+ gen_update_cc_op(s);
+ if (s->jmp_opt) {
+ l1 = gen_new_label();
+ gen_jcc1(s, cc_op, b, l1);
+
+ gen_goto_tb(s, 0, next_eip);
+
+ gen_set_label(l1);
+ gen_goto_tb(s, 1, val);
+ s->is_jmp = DISAS_TB_JUMP;
+ } else {
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ gen_jcc1(s, cc_op, b, l1);
+
+ gen_jmp_im(next_eip);
+ tcg_gen_br(l2);
+
+ gen_set_label(l1);
+ gen_jmp_im(val);
+ gen_set_label(l2);
+ gen_eob(s);
+ }
+}
+
+static void gen_setcc(DisasContext *s, int b)
+{
+ int inv, jcc_op, l1;
+ TCGv t0;
+
+ if (is_fast_jcc_case(s, b)) {
+ /* nominal case: we use a jump */
+ /* XXX: make it faster by adding new instructions in TCG */
+ t0 = tcg_temp_local_new();
+ tcg_gen_movi_tl(t0, 0);
+ l1 = gen_new_label();
+ gen_jcc1(s, s->cc_op, b ^ 1, l1);
+ tcg_gen_movi_tl(t0, 1);
+ gen_set_label(l1);
+ tcg_gen_mov_tl(cpu_T[0], t0);
+ tcg_temp_free(t0);
+ } else {
+ /* slow case: it is more efficient not to generate a jump,
+ although it is questionnable whether this optimization is
+ worth to */
+ inv = b & 1;
+ jcc_op = (b >> 1) & 7;
+ gen_setcc_slow_T0(s, jcc_op);
+ if (inv) {
+ tcg_gen_xori_tl(cpu_T[0], cpu_T[0], 1);
+ }
+ }
+}
+
+static inline void gen_op_movl_T0_seg(int seg_reg)
+{
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,segs[seg_reg].selector));
+}
+
+static inline void gen_op_movl_seg_T0_vm(int seg_reg)
+{
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffff);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,segs[seg_reg].selector));
+ tcg_gen_shli_tl(cpu_T[0], cpu_T[0], 4);
+ tcg_gen_st_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,segs[seg_reg].base));
+}
+
+/* move T0 to seg_reg and compute if the CPU state may change. Never
+ call this function with seg_reg == R_CS */
+static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip)
+{
+ if (s->pe && !s->vm86) {
+ /* XXX: optimize by finding processor state dynamically */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_load_seg(tcg_const_i32(seg_reg), cpu_tmp2_i32);
+ /* abort translation because the addseg value may change or
+ because ss32 may change. For R_SS, translation must always
+ stop as a special handling must be done to disable hardware
+ interrupts for the next instruction */
+ if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS))
+ s->is_jmp = DISAS_TB_JUMP;
+ } else {
+ gen_op_movl_seg_T0_vm(seg_reg);
+ if (seg_reg == R_SS)
+ s->is_jmp = DISAS_TB_JUMP;
+ }
+}
+
+static inline int svm_is_rep(int prefixes)
+{
+ return ((prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) ? 8 : 0);
+}
+
+static inline void
+gen_svm_check_intercept_param(DisasContext *s, target_ulong pc_start,
+ uint32_t type, uint64_t param)
+{
+ /* no SVM activated; fast case */
+ if (likely(!(s->flags & HF_SVMI_MASK)))
+ return;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_svm_check_intercept_param(tcg_const_i32(type),
+ tcg_const_i64(param));
+}
+
+static inline void
+gen_svm_check_intercept(DisasContext *s, target_ulong pc_start, uint64_t type)
+{
+ gen_svm_check_intercept_param(s, pc_start, type, 0);
+}
+
+static inline void gen_stack_update(DisasContext *s, int addend)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_add_reg_im(2, R_ESP, addend);
+ } else
+#endif
+ if (s->ss32) {
+ gen_op_add_reg_im(1, R_ESP, addend);
+ } else {
+ gen_op_add_reg_im(0, R_ESP, addend);
+ }
+}
+
+/* generate a push. It depends on ss32, addseg and dflag */
+static void gen_push_T0(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_movq_A0_reg(R_ESP);
+ if (s->dflag) {
+ gen_op_addq_A0_im(-8);
+ gen_op_st_T0_A0(OT_QUAD + s->mem_index);
+ } else {
+ gen_op_addq_A0_im(-2);
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ }
+ gen_op_mov_reg_A0(2, R_ESP);
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(R_ESP);
+ if (!s->dflag)
+ gen_op_addl_A0_im(-2);
+ else
+ gen_op_addl_A0_im(-4);
+ if (s->ss32) {
+ if (s->addseg) {
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+ gen_op_addl_A0_seg(R_SS);
+ }
+ } else {
+ gen_op_andl_A0_ffff();
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+ gen_op_addl_A0_seg(R_SS);
+ }
+ gen_op_st_T0_A0(s->dflag + 1 + s->mem_index);
+ if (s->ss32 && !s->addseg)
+ gen_op_mov_reg_A0(1, R_ESP);
+ else
+ gen_op_mov_reg_T1(s->ss32 + 1, R_ESP);
+ }
+}
+
+/* generate a push. It depends on ss32, addseg and dflag */
+/* slower version for T1, only used for call Ev */
+static void gen_push_T1(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_movq_A0_reg(R_ESP);
+ if (s->dflag) {
+ gen_op_addq_A0_im(-8);
+ gen_op_st_T1_A0(OT_QUAD + s->mem_index);
+ } else {
+ gen_op_addq_A0_im(-2);
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ }
+ gen_op_mov_reg_A0(2, R_ESP);
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(R_ESP);
+ if (!s->dflag)
+ gen_op_addl_A0_im(-2);
+ else
+ gen_op_addl_A0_im(-4);
+ if (s->ss32) {
+ if (s->addseg) {
+ gen_op_addl_A0_seg(R_SS);
+ }
+ } else {
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_seg(R_SS);
+ }
+ gen_op_st_T1_A0(s->dflag + 1 + s->mem_index);
+
+ if (s->ss32 && !s->addseg)
+ gen_op_mov_reg_A0(1, R_ESP);
+ else
+ gen_stack_update(s, (-2) << s->dflag);
+ }
+}
+
+/* two step pop is necessary for precise exceptions */
+static void gen_pop_T0(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ gen_op_movq_A0_reg(R_ESP);
+ gen_op_ld_T0_A0((s->dflag ? OT_QUAD : OT_WORD) + s->mem_index);
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(R_ESP);
+ if (s->ss32) {
+ if (s->addseg)
+ gen_op_addl_A0_seg(R_SS);
+ } else {
+ gen_op_andl_A0_ffff();
+ gen_op_addl_A0_seg(R_SS);
+ }
+ gen_op_ld_T0_A0(s->dflag + 1 + s->mem_index);
+ }
+}
+
+static void gen_pop_update(DisasContext *s)
+{
+#ifdef TARGET_X86_64
+ if (CODE64(s) && s->dflag) {
+ gen_stack_update(s, 8);
+ } else
+#endif
+ {
+ gen_stack_update(s, 2 << s->dflag);
+ }
+}
+
+static void gen_stack_A0(DisasContext *s)
+{
+ gen_op_movl_A0_reg(R_ESP);
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+ if (s->addseg)
+ gen_op_addl_A0_seg(R_SS);
+}
+
+/* NOTE: wrap around in 16 bit not fully handled */
+static void gen_pusha(DisasContext *s)
+{
+ int i;
+ gen_op_movl_A0_reg(R_ESP);
+ gen_op_addl_A0_im(-16 << s->dflag);
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+ if (s->addseg)
+ gen_op_addl_A0_seg(R_SS);
+ for(i = 0;i < 8; i++) {
+ gen_op_mov_TN_reg(OT_LONG, 0, 7 - i);
+ gen_op_st_T0_A0(OT_WORD + s->dflag + s->mem_index);
+ gen_op_addl_A0_im(2 << s->dflag);
+ }
+ gen_op_mov_reg_T1(OT_WORD + s->ss32, R_ESP);
+}
+
+/* NOTE: wrap around in 16 bit not fully handled */
+static void gen_popa(DisasContext *s)
+{
+ int i;
+ gen_op_movl_A0_reg(R_ESP);
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+ tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 16 << s->dflag);
+ if (s->addseg)
+ gen_op_addl_A0_seg(R_SS);
+ for(i = 0;i < 8; i++) {
+ /* ESP is not reloaded */
+ if (i != 3) {
+ gen_op_ld_T0_A0(OT_WORD + s->dflag + s->mem_index);
+ gen_op_mov_reg_T0(OT_WORD + s->dflag, 7 - i);
+ }
+ gen_op_addl_A0_im(2 << s->dflag);
+ }
+ gen_op_mov_reg_T1(OT_WORD + s->ss32, R_ESP);
+}
+
+static void gen_enter(DisasContext *s, int esp_addend, int level)
+{
+ int ot, opsize;
+
+ level &= 0x1f;
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ ot = s->dflag ? OT_QUAD : OT_WORD;
+ opsize = 1 << ot;
+
+ gen_op_movl_A0_reg(R_ESP);
+ gen_op_addq_A0_im(-opsize);
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+
+ /* push bp */
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ if (level) {
+ /* XXX: must save state */
+ gen_helper_enter64_level(tcg_const_i32(level),
+ tcg_const_i32((ot == OT_QUAD)),
+ cpu_T[1]);
+ }
+ gen_op_mov_reg_T1(ot, R_EBP);
+ tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level));
+ gen_op_mov_reg_T1(OT_QUAD, R_ESP);
+ } else
+#endif
+ {
+ ot = s->dflag + OT_WORD;
+ opsize = 2 << s->dflag;
+
+ gen_op_movl_A0_reg(R_ESP);
+ gen_op_addl_A0_im(-opsize);
+ if (!s->ss32)
+ gen_op_andl_A0_ffff();
+ tcg_gen_mov_tl(cpu_T[1], cpu_A0);
+ if (s->addseg)
+ gen_op_addl_A0_seg(R_SS);
+ /* push bp */
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ if (level) {
+ /* XXX: must save state */
+ gen_helper_enter_level(tcg_const_i32(level),
+ tcg_const_i32(s->dflag),
+ cpu_T[1]);
+ }
+ gen_op_mov_reg_T1(ot, R_EBP);
+ tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level));
+ gen_op_mov_reg_T1(OT_WORD + s->ss32, R_ESP);
+ }
+}
+
+static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_helper_raise_exception(tcg_const_i32(trapno));
+ s->is_jmp = DISAS_TB_JUMP;
+}
+
+/* an interrupt is different from an exception because of the
+ privilege checks */
+static void gen_interrupt(DisasContext *s, int intno,
+ target_ulong cur_eip, target_ulong next_eip)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_helper_raise_interrupt(tcg_const_i32(intno),
+ tcg_const_i32(next_eip - cur_eip));
+ s->is_jmp = DISAS_TB_JUMP;
+}
+
+static void gen_debug(DisasContext *s, target_ulong cur_eip)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(cur_eip);
+ gen_helper_debug();
+ s->is_jmp = DISAS_TB_JUMP;
+}
+
+/* generate a generic end of block. Trace exception is also generated
+ if needed */
+static void gen_eob(DisasContext *s)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (s->tb->flags & HF_INHIBIT_IRQ_MASK) {
+ gen_helper_reset_inhibit_irq();
+ }
+ if (s->tb->flags & HF_RF_MASK) {
+ gen_helper_reset_rf();
+ }
+ if (s->singlestep_enabled) {
+ gen_helper_debug();
+ } else if (s->tf) {
+ gen_helper_single_step();
+ } else {
+ tcg_gen_exit_tb(0);
+ }
+ s->is_jmp = DISAS_TB_JUMP;
+}
+
+/* generate a jump to eip. No segment change must happen before as a
+ direct call to the next block may occur */
+static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num)
+{
+ if (s->jmp_opt) {
+ gen_update_cc_op(s);
+ gen_goto_tb(s, tb_num, eip);
+ s->is_jmp = DISAS_TB_JUMP;
+ } else {
+ gen_jmp_im(eip);
+ gen_eob(s);
+ }
+}
+
+static void gen_jmp(DisasContext *s, target_ulong eip)
+{
+ gen_jmp_tb(s, eip, 0);
+}
+
+static inline void gen_ldq_env_A0(int idx, int offset)
+{
+ int mem_index = (idx >> 2) - 1;
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, mem_index);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset);
+}
+
+static inline void gen_stq_env_A0(int idx, int offset)
+{
+ int mem_index = (idx >> 2) - 1;
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset);
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, mem_index);
+}
+
+static inline void gen_ldo_env_A0(int idx, int offset)
+{
+ int mem_index = (idx >> 2) - 1;
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0, mem_index);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0)));
+ tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8);
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_tmp0, mem_index);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1)));
+}
+
+static inline void gen_sto_env_A0(int idx, int offset)
+{
+ int mem_index = (idx >> 2) - 1;
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0)));
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0, mem_index);
+ tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8);
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1)));
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_tmp0, mem_index);
+}
+
+static inline void gen_op_movo(int d_offset, int s_offset)
+{
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset);
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + 8);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + 8);
+}
+
+static inline void gen_op_movq(int d_offset, int s_offset)
+{
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset);
+}
+
+static inline void gen_op_movl(int d_offset, int s_offset)
+{
+ tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, s_offset);
+ tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, d_offset);
+}
+
+static inline void gen_op_movq_env_0(int d_offset)
+{
+ tcg_gen_movi_i64(cpu_tmp1_i64, 0);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset);
+}
+
+#define SSE_SPECIAL ((void *)1)
+#define SSE_DUMMY ((void *)2)
+
+#define MMX_OP2(x) { gen_helper_ ## x ## _mmx, gen_helper_ ## x ## _xmm }
+#define SSE_FOP(x) { gen_helper_ ## x ## ps, gen_helper_ ## x ## pd, \
+ gen_helper_ ## x ## ss, gen_helper_ ## x ## sd, }
+
+static void *sse_op_table1[256][4] = {
+ /* 3DNow! extensions */
+ [0x0e] = { SSE_DUMMY }, /* femms */
+ [0x0f] = { SSE_DUMMY }, /* pf... */
+ /* pure SSE operations */
+ [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */
+ [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */
+ [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */
+ [0x13] = { SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd */
+ [0x14] = { gen_helper_punpckldq_xmm, gen_helper_punpcklqdq_xmm },
+ [0x15] = { gen_helper_punpckhdq_xmm, gen_helper_punpckhqdq_xmm },
+ [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd, movshdup */
+ [0x17] = { SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd */
+
+ [0x28] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */
+ [0x29] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */
+ [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */
+ [0x2b] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd, movntss, movntsd */
+ [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */
+ [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */
+ [0x2e] = { gen_helper_ucomiss, gen_helper_ucomisd },
+ [0x2f] = { gen_helper_comiss, gen_helper_comisd },
+ [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */
+ [0x51] = SSE_FOP(sqrt),
+ [0x52] = { gen_helper_rsqrtps, NULL, gen_helper_rsqrtss, NULL },
+ [0x53] = { gen_helper_rcpps, NULL, gen_helper_rcpss, NULL },
+ [0x54] = { gen_helper_pand_xmm, gen_helper_pand_xmm }, /* andps, andpd */
+ [0x55] = { gen_helper_pandn_xmm, gen_helper_pandn_xmm }, /* andnps, andnpd */
+ [0x56] = { gen_helper_por_xmm, gen_helper_por_xmm }, /* orps, orpd */
+ [0x57] = { gen_helper_pxor_xmm, gen_helper_pxor_xmm }, /* xorps, xorpd */
+ [0x58] = SSE_FOP(add),
+ [0x59] = SSE_FOP(mul),
+ [0x5a] = { gen_helper_cvtps2pd, gen_helper_cvtpd2ps,
+ gen_helper_cvtss2sd, gen_helper_cvtsd2ss },
+ [0x5b] = { gen_helper_cvtdq2ps, gen_helper_cvtps2dq, gen_helper_cvttps2dq },
+ [0x5c] = SSE_FOP(sub),
+ [0x5d] = SSE_FOP(min),
+ [0x5e] = SSE_FOP(div),
+ [0x5f] = SSE_FOP(max),
+
+ [0xc2] = SSE_FOP(cmpeq),
+ [0xc6] = { gen_helper_shufps, gen_helper_shufpd },
+
+ [0x38] = { SSE_SPECIAL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* SSSE3/SSE4 */
+ [0x3a] = { SSE_SPECIAL, SSE_SPECIAL }, /* SSSE3/SSE4 */
+
+ /* MMX ops and their SSE extensions */
+ [0x60] = MMX_OP2(punpcklbw),
+ [0x61] = MMX_OP2(punpcklwd),
+ [0x62] = MMX_OP2(punpckldq),
+ [0x63] = MMX_OP2(packsswb),
+ [0x64] = MMX_OP2(pcmpgtb),
+ [0x65] = MMX_OP2(pcmpgtw),
+ [0x66] = MMX_OP2(pcmpgtl),
+ [0x67] = MMX_OP2(packuswb),
+ [0x68] = MMX_OP2(punpckhbw),
+ [0x69] = MMX_OP2(punpckhwd),
+ [0x6a] = MMX_OP2(punpckhdq),
+ [0x6b] = MMX_OP2(packssdw),
+ [0x6c] = { NULL, gen_helper_punpcklqdq_xmm },
+ [0x6d] = { NULL, gen_helper_punpckhqdq_xmm },
+ [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */
+ [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */
+ [0x70] = { gen_helper_pshufw_mmx,
+ gen_helper_pshufd_xmm,
+ gen_helper_pshufhw_xmm,
+ gen_helper_pshuflw_xmm },
+ [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */
+ [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */
+ [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */
+ [0x74] = MMX_OP2(pcmpeqb),
+ [0x75] = MMX_OP2(pcmpeqw),
+ [0x76] = MMX_OP2(pcmpeql),
+ [0x77] = { SSE_DUMMY }, /* emms */
+ [0x78] = { NULL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* extrq_i, insertq_i */
+ [0x79] = { NULL, gen_helper_extrq_r, NULL, gen_helper_insertq_r },
+ [0x7c] = { NULL, gen_helper_haddpd, NULL, gen_helper_haddps },
+ [0x7d] = { NULL, gen_helper_hsubpd, NULL, gen_helper_hsubps },
+ [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */
+ [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */
+ [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */
+ [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */
+ [0xd0] = { NULL, gen_helper_addsubpd, NULL, gen_helper_addsubps },
+ [0xd1] = MMX_OP2(psrlw),
+ [0xd2] = MMX_OP2(psrld),
+ [0xd3] = MMX_OP2(psrlq),
+ [0xd4] = MMX_OP2(paddq),
+ [0xd5] = MMX_OP2(pmullw),
+ [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL },
+ [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */
+ [0xd8] = MMX_OP2(psubusb),
+ [0xd9] = MMX_OP2(psubusw),
+ [0xda] = MMX_OP2(pminub),
+ [0xdb] = MMX_OP2(pand),
+ [0xdc] = MMX_OP2(paddusb),
+ [0xdd] = MMX_OP2(paddusw),
+ [0xde] = MMX_OP2(pmaxub),
+ [0xdf] = MMX_OP2(pandn),
+ [0xe0] = MMX_OP2(pavgb),
+ [0xe1] = MMX_OP2(psraw),
+ [0xe2] = MMX_OP2(psrad),
+ [0xe3] = MMX_OP2(pavgw),
+ [0xe4] = MMX_OP2(pmulhuw),
+ [0xe5] = MMX_OP2(pmulhw),
+ [0xe6] = { NULL, gen_helper_cvttpd2dq, gen_helper_cvtdq2pd, gen_helper_cvtpd2dq },
+ [0xe7] = { SSE_SPECIAL , SSE_SPECIAL }, /* movntq, movntq */
+ [0xe8] = MMX_OP2(psubsb),
+ [0xe9] = MMX_OP2(psubsw),
+ [0xea] = MMX_OP2(pminsw),
+ [0xeb] = MMX_OP2(por),
+ [0xec] = MMX_OP2(paddsb),
+ [0xed] = MMX_OP2(paddsw),
+ [0xee] = MMX_OP2(pmaxsw),
+ [0xef] = MMX_OP2(pxor),
+ [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */
+ [0xf1] = MMX_OP2(psllw),
+ [0xf2] = MMX_OP2(pslld),
+ [0xf3] = MMX_OP2(psllq),
+ [0xf4] = MMX_OP2(pmuludq),
+ [0xf5] = MMX_OP2(pmaddwd),
+ [0xf6] = MMX_OP2(psadbw),
+ [0xf7] = MMX_OP2(maskmov),
+ [0xf8] = MMX_OP2(psubb),
+ [0xf9] = MMX_OP2(psubw),
+ [0xfa] = MMX_OP2(psubl),
+ [0xfb] = MMX_OP2(psubq),
+ [0xfc] = MMX_OP2(paddb),
+ [0xfd] = MMX_OP2(paddw),
+ [0xfe] = MMX_OP2(paddl),
+};
+
+static void *sse_op_table2[3 * 8][2] = {
+ [0 + 2] = MMX_OP2(psrlw),
+ [0 + 4] = MMX_OP2(psraw),
+ [0 + 6] = MMX_OP2(psllw),
+ [8 + 2] = MMX_OP2(psrld),
+ [8 + 4] = MMX_OP2(psrad),
+ [8 + 6] = MMX_OP2(pslld),
+ [16 + 2] = MMX_OP2(psrlq),
+ [16 + 3] = { NULL, gen_helper_psrldq_xmm },
+ [16 + 6] = MMX_OP2(psllq),
+ [16 + 7] = { NULL, gen_helper_pslldq_xmm },
+};
+
+static void *sse_op_table3[4 * 3] = {
+ gen_helper_cvtsi2ss,
+ gen_helper_cvtsi2sd,
+ X86_64_ONLY(gen_helper_cvtsq2ss),
+ X86_64_ONLY(gen_helper_cvtsq2sd),
+
+ gen_helper_cvttss2si,
+ gen_helper_cvttsd2si,
+ X86_64_ONLY(gen_helper_cvttss2sq),
+ X86_64_ONLY(gen_helper_cvttsd2sq),
+
+ gen_helper_cvtss2si,
+ gen_helper_cvtsd2si,
+ X86_64_ONLY(gen_helper_cvtss2sq),
+ X86_64_ONLY(gen_helper_cvtsd2sq),
+};
+
+static void *sse_op_table4[8][4] = {
+ SSE_FOP(cmpeq),
+ SSE_FOP(cmplt),
+ SSE_FOP(cmple),
+ SSE_FOP(cmpunord),
+ SSE_FOP(cmpneq),
+ SSE_FOP(cmpnlt),
+ SSE_FOP(cmpnle),
+ SSE_FOP(cmpord),
+};
+
+static void *sse_op_table5[256] = {
+ [0x0c] = gen_helper_pi2fw,
+ [0x0d] = gen_helper_pi2fd,
+ [0x1c] = gen_helper_pf2iw,
+ [0x1d] = gen_helper_pf2id,
+ [0x8a] = gen_helper_pfnacc,
+ [0x8e] = gen_helper_pfpnacc,
+ [0x90] = gen_helper_pfcmpge,
+ [0x94] = gen_helper_pfmin,
+ [0x96] = gen_helper_pfrcp,
+ [0x97] = gen_helper_pfrsqrt,
+ [0x9a] = gen_helper_pfsub,
+ [0x9e] = gen_helper_pfadd,
+ [0xa0] = gen_helper_pfcmpgt,
+ [0xa4] = gen_helper_pfmax,
+ [0xa6] = gen_helper_movq, /* pfrcpit1; no need to actually increase precision */
+ [0xa7] = gen_helper_movq, /* pfrsqit1 */
+ [0xaa] = gen_helper_pfsubr,
+ [0xae] = gen_helper_pfacc,
+ [0xb0] = gen_helper_pfcmpeq,
+ [0xb4] = gen_helper_pfmul,
+ [0xb6] = gen_helper_movq, /* pfrcpit2 */
+ [0xb7] = gen_helper_pmulhrw_mmx,
+ [0xbb] = gen_helper_pswapd,
+ [0xbf] = gen_helper_pavgb_mmx /* pavgusb */
+};
+
+struct sse_op_helper_s {
+ void *op[2]; uint32_t ext_mask;
+};
+#define SSSE3_OP(x) { MMX_OP2(x), CPUID_EXT_SSSE3 }
+#define SSE41_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE41 }
+#define SSE42_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE42 }
+#define SSE41_SPECIAL { { NULL, SSE_SPECIAL }, CPUID_EXT_SSE41 }
+static struct sse_op_helper_s sse_op_table6[256] = {
+ [0x00] = SSSE3_OP(pshufb),
+ [0x01] = SSSE3_OP(phaddw),
+ [0x02] = SSSE3_OP(phaddd),
+ [0x03] = SSSE3_OP(phaddsw),
+ [0x04] = SSSE3_OP(pmaddubsw),
+ [0x05] = SSSE3_OP(phsubw),
+ [0x06] = SSSE3_OP(phsubd),
+ [0x07] = SSSE3_OP(phsubsw),
+ [0x08] = SSSE3_OP(psignb),
+ [0x09] = SSSE3_OP(psignw),
+ [0x0a] = SSSE3_OP(psignd),
+ [0x0b] = SSSE3_OP(pmulhrsw),
+ [0x10] = SSE41_OP(pblendvb),
+ [0x14] = SSE41_OP(blendvps),
+ [0x15] = SSE41_OP(blendvpd),
+ [0x17] = SSE41_OP(ptest),
+ [0x1c] = SSSE3_OP(pabsb),
+ [0x1d] = SSSE3_OP(pabsw),
+ [0x1e] = SSSE3_OP(pabsd),
+ [0x20] = SSE41_OP(pmovsxbw),
+ [0x21] = SSE41_OP(pmovsxbd),
+ [0x22] = SSE41_OP(pmovsxbq),
+ [0x23] = SSE41_OP(pmovsxwd),
+ [0x24] = SSE41_OP(pmovsxwq),
+ [0x25] = SSE41_OP(pmovsxdq),
+ [0x28] = SSE41_OP(pmuldq),
+ [0x29] = SSE41_OP(pcmpeqq),
+ [0x2a] = SSE41_SPECIAL, /* movntqda */
+ [0x2b] = SSE41_OP(packusdw),
+ [0x30] = SSE41_OP(pmovzxbw),
+ [0x31] = SSE41_OP(pmovzxbd),
+ [0x32] = SSE41_OP(pmovzxbq),
+ [0x33] = SSE41_OP(pmovzxwd),
+ [0x34] = SSE41_OP(pmovzxwq),
+ [0x35] = SSE41_OP(pmovzxdq),
+ [0x37] = SSE42_OP(pcmpgtq),
+ [0x38] = SSE41_OP(pminsb),
+ [0x39] = SSE41_OP(pminsd),
+ [0x3a] = SSE41_OP(pminuw),
+ [0x3b] = SSE41_OP(pminud),
+ [0x3c] = SSE41_OP(pmaxsb),
+ [0x3d] = SSE41_OP(pmaxsd),
+ [0x3e] = SSE41_OP(pmaxuw),
+ [0x3f] = SSE41_OP(pmaxud),
+ [0x40] = SSE41_OP(pmulld),
+ [0x41] = SSE41_OP(phminposuw),
+};
+
+static struct sse_op_helper_s sse_op_table7[256] = {
+ [0x08] = SSE41_OP(roundps),
+ [0x09] = SSE41_OP(roundpd),
+ [0x0a] = SSE41_OP(roundss),
+ [0x0b] = SSE41_OP(roundsd),
+ [0x0c] = SSE41_OP(blendps),
+ [0x0d] = SSE41_OP(blendpd),
+ [0x0e] = SSE41_OP(pblendw),
+ [0x0f] = SSSE3_OP(palignr),
+ [0x14] = SSE41_SPECIAL, /* pextrb */
+ [0x15] = SSE41_SPECIAL, /* pextrw */
+ [0x16] = SSE41_SPECIAL, /* pextrd/pextrq */
+ [0x17] = SSE41_SPECIAL, /* extractps */
+ [0x20] = SSE41_SPECIAL, /* pinsrb */
+ [0x21] = SSE41_SPECIAL, /* insertps */
+ [0x22] = SSE41_SPECIAL, /* pinsrd/pinsrq */
+ [0x40] = SSE41_OP(dpps),
+ [0x41] = SSE41_OP(dppd),
+ [0x42] = SSE41_OP(mpsadbw),
+ [0x60] = SSE42_OP(pcmpestrm),
+ [0x61] = SSE42_OP(pcmpestri),
+ [0x62] = SSE42_OP(pcmpistrm),
+ [0x63] = SSE42_OP(pcmpistri),
+};
+
+static void gen_sse(DisasContext *s, int b, target_ulong pc_start, int rex_r)
+{
+ int b1, op1_offset, op2_offset, is_xmm, val, ot;
+ int modrm, mod, rm, reg, reg_addr, offset_addr;
+ void *sse_op2;
+
+ b &= 0xff;
+ if (s->prefix & PREFIX_DATA)
+ b1 = 1;
+ else if (s->prefix & PREFIX_REPZ)
+ b1 = 2;
+ else if (s->prefix & PREFIX_REPNZ)
+ b1 = 3;
+ else
+ b1 = 0;
+ sse_op2 = sse_op_table1[b][b1];
+ if (!sse_op2)
+ goto illegal_op;
+ if ((b <= 0x5f && b >= 0x10) || b == 0xc6 || b == 0xc2) {
+ is_xmm = 1;
+ } else {
+ if (b1 == 0) {
+ /* MMX case */
+ is_xmm = 0;
+ } else {
+ is_xmm = 1;
+ }
+ }
+ /* simple MMX/SSE operation */
+ if (s->flags & HF_TS_MASK) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ return;
+ }
+ if (s->flags & HF_EM_MASK) {
+ illegal_op:
+ gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base);
+ return;
+ }
+ if (is_xmm && !(s->flags & HF_OSFXSR_MASK))
+ if ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA))
+ goto illegal_op;
+ if (b == 0x0e) {
+ if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW))
+ goto illegal_op;
+ /* femms */
+ gen_helper_emms();
+ return;
+ }
+ if (b == 0x77) {
+ /* emms */
+ gen_helper_emms();
+ return;
+ }
+ /* prepare MMX state (XXX: optimize by storing fptt and fptags in
+ the static cpu state) */
+ if (!is_xmm) {
+ gen_helper_enter_mmx();
+ }
+
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7);
+ if (is_xmm)
+ reg |= rex_r;
+ mod = (modrm >> 6) & 3;
+ if (sse_op2 == SSE_SPECIAL) {
+ b |= (b1 << 8);
+ switch(b) {
+ case 0x0e7: /* movntq */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
+ break;
+ case 0x1e7: /* movntdq */
+ case 0x02b: /* movntps */
+ case 0x12b: /* movntps */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ break;
+ case 0x3f0: /* lddqu */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ break;
+ case 0x22b: /* movntss */
+ case 0x32b: /* movntsd */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (b1 & 1) {
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,
+ xmm_regs[reg]));
+ } else {
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(0)));
+ gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ }
+ break;
+ case 0x6e: /* movd mm, ea */
+#ifdef TARGET_X86_64
+ if (s->dflag == 2) {
+ gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 0);
+ tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,fpregs[reg].mmx));
+ } else
+#endif
+ {
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
+ offsetof(CPUX86State,fpregs[reg].mmx));
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_movl_mm_T0_mmx(cpu_ptr0, cpu_tmp2_i32);
+ }
+ break;
+ case 0x16e: /* movd xmm, ea */
+#ifdef TARGET_X86_64
+ if (s->dflag == 2) {
+ gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 0);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg]));
+ gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T[0]);
+ } else
+#endif
+ {
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 0);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg]));
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_movl_mm_T0_xmm(cpu_ptr0, cpu_tmp2_i32);
+ }
+ break;
+ case 0x6f: /* movq mm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
+ } else {
+ rm = (modrm & 7);
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env,
+ offsetof(CPUX86State,fpregs[rm].mmx));
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env,
+ offsetof(CPUX86State,fpregs[reg].mmx));
+ }
+ break;
+ case 0x010: /* movups */
+ case 0x110: /* movupd */
+ case 0x028: /* movaps */
+ case 0x128: /* movapd */
+ case 0x16f: /* movdqa xmm, ea */
+ case 0x26f: /* movdqu xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movo(offsetof(CPUX86State,xmm_regs[reg]),
+ offsetof(CPUX86State,xmm_regs[rm]));
+ }
+ break;
+ case 0x210: /* movss xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_op_movl_T0_0();
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)));
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)));
+ }
+ break;
+ case 0x310: /* movsd xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_op_movl_T0_0();
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ break;
+ case 0x012: /* movlps */
+ case 0x112: /* movlpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ /* movhlps */
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1)));
+ }
+ break;
+ case 0x212: /* movsldup */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(2)));
+ }
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)));
+ break;
+ case 0x312: /* movddup */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ break;
+ case 0x016: /* movhps */
+ case 0x116: /* movhpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ } else {
+ /* movlhps */
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ break;
+ case 0x216: /* movshdup */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(1)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_L(3)));
+ }
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)));
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)));
+ break;
+ case 0x178:
+ case 0x378:
+ {
+ int bit_index, field_length;
+
+ if (b1 == 1 && reg != 0)
+ goto illegal_op;
+ field_length = ldub_code(s->pc++) & 0x3F;
+ bit_index = ldub_code(s->pc++) & 0x3F;
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg]));
+ if (b1 == 1)
+ gen_helper_extrq_i(cpu_ptr0, tcg_const_i32(bit_index),
+ tcg_const_i32(field_length));
+ else
+ gen_helper_insertq_i(cpu_ptr0, tcg_const_i32(bit_index),
+ tcg_const_i32(field_length));
+ }
+ break;
+ case 0x7e: /* movd ea, mm */
+#ifdef TARGET_X86_64
+ if (s->dflag == 2) {
+ tcg_gen_ld_i64(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,fpregs[reg].mmx));
+ gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 1);
+ } else
+#endif
+ {
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0)));
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1);
+ }
+ break;
+ case 0x17e: /* movd ea, xmm */
+#ifdef TARGET_X86_64
+ if (s->dflag == 2) {
+ tcg_gen_ld_i64(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_ldst_modrm(s, modrm, OT_QUAD, OR_TMP0, 1);
+ } else
+#endif
+ {
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_ldst_modrm(s, modrm, OT_LONG, OR_TMP0, 1);
+ }
+ break;
+ case 0x27e: /* movq xmm, ea */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ }
+ gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ break;
+ case 0x7f: /* movq ea, mm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,fpregs[reg].mmx));
+ } else {
+ rm = (modrm & 7);
+ gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx),
+ offsetof(CPUX86State,fpregs[reg].mmx));
+ }
+ break;
+ case 0x011: /* movups */
+ case 0x111: /* movupd */
+ case 0x029: /* movaps */
+ case 0x129: /* movapd */
+ case 0x17f: /* movdqa ea, xmm */
+ case 0x27f: /* movdqu ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_sto_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg]));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movo(offsetof(CPUX86State,xmm_regs[rm]),
+ offsetof(CPUX86State,xmm_regs[reg]));
+ }
+ break;
+ case 0x211: /* movss ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)));
+ }
+ break;
+ case 0x311: /* movsd ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ }
+ break;
+ case 0x013: /* movlps */
+ case 0x113: /* movlpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x017: /* movhps */
+ case 0x117: /* movhpd */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x71: /* shift mm, im */
+ case 0x72:
+ case 0x73:
+ case 0x171: /* shift xmm, im */
+ case 0x172:
+ case 0x173:
+ if (b1 >= 2) {
+ goto illegal_op;
+ }
+ val = ldub_code(s->pc++);
+ if (is_xmm) {
+ gen_op_movl_T0_im(val);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
+ gen_op_movl_T0_0();
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(1)));
+ op1_offset = offsetof(CPUX86State,xmm_t0);
+ } else {
+ gen_op_movl_T0_im(val);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(0)));
+ gen_op_movl_T0_0();
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(1)));
+ op1_offset = offsetof(CPUX86State,mmx_t0);
+ }
+ sse_op2 = sse_op_table2[((b - 1) & 3) * 8 + (((modrm >> 3)) & 7)][b1];
+ if (!sse_op2)
+ goto illegal_op;
+ if (is_xmm) {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ } else {
+ rm = (modrm & 7);
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ }
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op1_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr))sse_op2)(cpu_ptr0, cpu_ptr1);
+ break;
+ case 0x050: /* movmskps */
+ rm = (modrm & 7) | REX_B(s);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
+ offsetof(CPUX86State,xmm_regs[rm]));
+ gen_helper_movmskps(cpu_tmp2_i32, cpu_ptr0);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_mov_reg_T0(OT_LONG, reg);
+ break;
+ case 0x150: /* movmskpd */
+ rm = (modrm & 7) | REX_B(s);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env,
+ offsetof(CPUX86State,xmm_regs[rm]));
+ gen_helper_movmskpd(cpu_tmp2_i32, cpu_ptr0);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_mov_reg_T0(OT_LONG, reg);
+ break;
+ case 0x02a: /* cvtpi2ps */
+ case 0x12a: /* cvtpi2pd */
+ gen_helper_enter_mmx();
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,mmx_t0);
+ gen_ldq_env_A0(s->mem_index, op2_offset);
+ } else {
+ rm = (modrm & 7);
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ }
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ switch(b >> 8) {
+ case 0x0:
+ gen_helper_cvtpi2ps(cpu_ptr0, cpu_ptr1);
+ break;
+ default:
+ case 0x1:
+ gen_helper_cvtpi2pd(cpu_ptr0, cpu_ptr1);
+ break;
+ }
+ break;
+ case 0x22a: /* cvtsi2ss */
+ case 0x32a: /* cvtsi2sd */
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ sse_op2 = sse_op_table3[(s->dflag == 2) * 2 + ((b >> 8) - 2)];
+ if (ot == OT_LONG) {
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ ((void (*)(TCGv_ptr, TCGv_i32))sse_op2)(cpu_ptr0, cpu_tmp2_i32);
+ } else {
+ ((void (*)(TCGv_ptr, TCGv))sse_op2)(cpu_ptr0, cpu_T[0]);
+ }
+ break;
+ case 0x02c: /* cvttps2pi */
+ case 0x12c: /* cvttpd2pi */
+ case 0x02d: /* cvtps2pi */
+ case 0x12d: /* cvtpd2pi */
+ gen_helper_enter_mmx();
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ gen_ldo_env_A0(s->mem_index, op2_offset);
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ }
+ op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ switch(b) {
+ case 0x02c:
+ gen_helper_cvttps2pi(cpu_ptr0, cpu_ptr1);
+ break;
+ case 0x12c:
+ gen_helper_cvttpd2pi(cpu_ptr0, cpu_ptr1);
+ break;
+ case 0x02d:
+ gen_helper_cvtps2pi(cpu_ptr0, cpu_ptr1);
+ break;
+ case 0x12d:
+ gen_helper_cvtpd2pi(cpu_ptr0, cpu_ptr1);
+ break;
+ }
+ break;
+ case 0x22c: /* cvttss2si */
+ case 0x32c: /* cvttsd2si */
+ case 0x22d: /* cvtss2si */
+ case 0x32d: /* cvtsd2si */
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if ((b >> 8) & 1) {
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_Q(0)));
+ } else {
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
+ }
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ }
+ sse_op2 = sse_op_table3[(s->dflag == 2) * 2 + ((b >> 8) - 2) + 4 +
+ (b & 1) * 4];
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset);
+ if (ot == OT_LONG) {
+ ((void (*)(TCGv_i32, TCGv_ptr))sse_op2)(cpu_tmp2_i32, cpu_ptr0);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ } else {
+ ((void (*)(TCGv, TCGv_ptr))sse_op2)(cpu_T[0], cpu_ptr0);
+ }
+ gen_op_mov_reg_T0(ot, reg);
+ break;
+ case 0xc4: /* pinsrw */
+ case 0x1c4:
+ s->rip_offset = 1;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ val = ldub_code(s->pc++);
+ if (b1) {
+ val &= 7;
+ tcg_gen_st16_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg].XMM_W(val)));
+ } else {
+ val &= 3;
+ tcg_gen_st16_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val)));
+ }
+ break;
+ case 0xc5: /* pextrw */
+ case 0x1c5:
+ if (mod != 3)
+ goto illegal_op;
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ val = ldub_code(s->pc++);
+ if (b1) {
+ val &= 7;
+ rm = (modrm & 7) | REX_B(s);
+ tcg_gen_ld16u_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,xmm_regs[rm].XMM_W(val)));
+ } else {
+ val &= 3;
+ rm = (modrm & 7);
+ tcg_gen_ld16u_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val)));
+ }
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_op_mov_reg_T0(ot, reg);
+ break;
+ case 0x1d6: /* movq ea, xmm */
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_stq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)),
+ offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)));
+ gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1)));
+ }
+ break;
+ case 0x2d6: /* movq2dq */
+ gen_helper_enter_mmx();
+ rm = (modrm & 7);
+ gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)),
+ offsetof(CPUX86State,fpregs[rm].mmx));
+ gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)));
+ break;
+ case 0x3d6: /* movdq2q */
+ gen_helper_enter_mmx();
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_movq(offsetof(CPUX86State,fpregs[reg & 7].mmx),
+ offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)));
+ break;
+ case 0xd7: /* pmovmskb */
+ case 0x1d7:
+ if (mod != 3)
+ goto illegal_op;
+ if (b1) {
+ rm = (modrm & 7) | REX_B(s);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm]));
+ gen_helper_pmovmskb_xmm(cpu_tmp2_i32, cpu_ptr0);
+ } else {
+ rm = (modrm & 7);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,fpregs[rm].mmx));
+ gen_helper_pmovmskb_mmx(cpu_tmp2_i32, cpu_ptr0);
+ }
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_op_mov_reg_T0(OT_LONG, reg);
+ break;
+ case 0x138:
+ if (s->prefix & PREFIX_REPNZ)
+ goto crc32;
+ case 0x038:
+ b = modrm;
+ modrm = ldub_code(s->pc++);
+ rm = modrm & 7;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (b1 >= 2) {
+ goto illegal_op;
+ }
+
+ sse_op2 = sse_op_table6[b].op[b1];
+ if (!sse_op2)
+ goto illegal_op;
+ if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask))
+ goto illegal_op;
+
+ if (b1) {
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ if (mod == 3) {
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]);
+ } else {
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ switch (b) {
+ case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */
+ case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */
+ case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */
+ gen_ldq_env_A0(s->mem_index, op2_offset +
+ offsetof(XMMReg, XMM_Q(0)));
+ break;
+ case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */
+ case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */
+ tcg_gen_qemu_ld32u(cpu_tmp0, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, op2_offset +
+ offsetof(XMMReg, XMM_L(0)));
+ break;
+ case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */
+ tcg_gen_qemu_ld16u(cpu_tmp0, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ tcg_gen_st16_tl(cpu_tmp0, cpu_env, op2_offset +
+ offsetof(XMMReg, XMM_W(0)));
+ break;
+ case 0x2a: /* movntqda */
+ gen_ldo_env_A0(s->mem_index, op1_offset);
+ return;
+ default:
+ gen_ldo_env_A0(s->mem_index, op2_offset);
+ }
+ }
+ } else {
+ op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
+ if (mod == 3) {
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ } else {
+ op2_offset = offsetof(CPUX86State,mmx_t0);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, op2_offset);
+ }
+ }
+ if (sse_op2 == SSE_SPECIAL)
+ goto illegal_op;
+
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr))sse_op2)(cpu_ptr0, cpu_ptr1);
+
+ if (b == 0x17)
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x338: /* crc32 */
+ crc32:
+ b = modrm;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ if (b != 0xf0 && b != 0xf1)
+ goto illegal_op;
+ if (!(s->cpuid_ext_features & CPUID_EXT_SSE42))
+ goto illegal_op;
+
+ if (b == 0xf0)
+ ot = OT_BYTE;
+ else if (b == 0xf1 && s->dflag != 2)
+ if (s->prefix & PREFIX_DATA)
+ ot = OT_WORD;
+ else
+ ot = OT_LONG;
+ else
+ ot = OT_QUAD;
+
+ gen_op_mov_TN_reg(OT_LONG, 0, reg);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_helper_crc32(cpu_T[0], cpu_tmp2_i32,
+ cpu_T[0], tcg_const_i32(8 << ot));
+
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ gen_op_mov_reg_T0(ot, reg);
+ break;
+ case 0x03a:
+ case 0x13a:
+ b = modrm;
+ modrm = ldub_code(s->pc++);
+ rm = modrm & 7;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (b1 >= 2) {
+ goto illegal_op;
+ }
+
+ sse_op2 = sse_op_table7[b].op[b1];
+ if (!sse_op2)
+ goto illegal_op;
+ if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask))
+ goto illegal_op;
+
+ if (sse_op2 == SSE_SPECIAL) {
+ ot = (s->dflag == 2) ? OT_QUAD : OT_LONG;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3)
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ val = ldub_code(s->pc++);
+ switch (b) {
+ case 0x14: /* pextrb */
+ tcg_gen_ld8u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_B(val & 15)));
+ if (mod == 3)
+ gen_op_mov_reg_T0(ot, rm);
+ else
+ tcg_gen_qemu_st8(cpu_T[0], cpu_A0,
+ (s->mem_index >> 2) - 1);
+ break;
+ case 0x15: /* pextrw */
+ tcg_gen_ld16u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_W(val & 7)));
+ if (mod == 3)
+ gen_op_mov_reg_T0(ot, rm);
+ else
+ tcg_gen_qemu_st16(cpu_T[0], cpu_A0,
+ (s->mem_index >> 2) - 1);
+ break;
+ case 0x16:
+ if (ot == OT_LONG) { /* pextrd */
+ tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env,
+ offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(val & 3)));
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ if (mod == 3)
+ gen_op_mov_reg_v(ot, rm, cpu_T[0]);
+ else
+ tcg_gen_qemu_st32(cpu_T[0], cpu_A0,
+ (s->mem_index >> 2) - 1);
+ } else { /* pextrq */
+#ifdef TARGET_X86_64
+ tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env,
+ offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(val & 1)));
+ if (mod == 3)
+ gen_op_mov_reg_v(ot, rm, cpu_tmp1_i64);
+ else
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+#else
+ goto illegal_op;
+#endif
+ }
+ break;
+ case 0x17: /* extractps */
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(val & 3)));
+ if (mod == 3)
+ gen_op_mov_reg_T0(ot, rm);
+ else
+ tcg_gen_qemu_st32(cpu_T[0], cpu_A0,
+ (s->mem_index >> 2) - 1);
+ break;
+ case 0x20: /* pinsrb */
+ if (mod == 3)
+ gen_op_mov_TN_reg(OT_LONG, 0, rm);
+ else
+ tcg_gen_qemu_ld8u(cpu_tmp0, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ tcg_gen_st8_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_B(val & 15)));
+ break;
+ case 0x21: /* insertps */
+ if (mod == 3) {
+ tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env,
+ offsetof(CPUX86State,xmm_regs[rm]
+ .XMM_L((val >> 6) & 3)));
+ } else {
+ tcg_gen_qemu_ld32u(cpu_tmp0, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp0);
+ }
+ tcg_gen_st_i32(cpu_tmp2_i32, cpu_env,
+ offsetof(CPUX86State,xmm_regs[reg]
+ .XMM_L((val >> 4) & 3)));
+ if ((val >> 0) & 1)
+ tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/),
+ cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(0)));
+ if ((val >> 1) & 1)
+ tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/),
+ cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(1)));
+ if ((val >> 2) & 1)
+ tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/),
+ cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(2)));
+ if ((val >> 3) & 1)
+ tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/),
+ cpu_env, offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(3)));
+ break;
+ case 0x22:
+ if (ot == OT_LONG) { /* pinsrd */
+ if (mod == 3)
+ gen_op_mov_v_reg(ot, cpu_tmp0, rm);
+ else
+ tcg_gen_qemu_ld32u(cpu_tmp0, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp2_i32, cpu_env,
+ offsetof(CPUX86State,
+ xmm_regs[reg].XMM_L(val & 3)));
+ } else { /* pinsrq */
+#ifdef TARGET_X86_64
+ if (mod == 3)
+ gen_op_mov_v_reg(ot, cpu_tmp1_i64, rm);
+ else
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ tcg_gen_st_i64(cpu_tmp1_i64, cpu_env,
+ offsetof(CPUX86State,
+ xmm_regs[reg].XMM_Q(val & 1)));
+#else
+ goto illegal_op;
+#endif
+ }
+ break;
+ }
+ return;
+ }
+
+ if (b1) {
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ if (mod == 3) {
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]);
+ } else {
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldo_env_A0(s->mem_index, op2_offset);
+ }
+ } else {
+ op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
+ if (mod == 3) {
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ } else {
+ op2_offset = offsetof(CPUX86State,mmx_t0);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_ldq_env_A0(s->mem_index, op2_offset);
+ }
+ }
+ val = ldub_code(s->pc++);
+
+ if ((b & 0xfc) == 0x60) { /* pcmpXstrX */
+ s->cc_op = CC_OP_EFLAGS;
+
+ if (s->dflag == 2)
+ /* The helper must use entire 64-bit gp registers */
+ val |= 1 << 8;
+ }
+
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr, TCGv_i32))sse_op2)(cpu_ptr0, cpu_ptr1, tcg_const_i32(val));
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else {
+ /* generic MMX or SSE operation */
+ switch(b) {
+ case 0x70: /* pshufx insn */
+ case 0xc6: /* pshufx insn */
+ case 0xc2: /* compare insns */
+ s->rip_offset = 1;
+ break;
+ default:
+ break;
+ }
+ if (is_xmm) {
+ op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,xmm_t0);
+ if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) ||
+ b == 0xc2)) {
+ /* specific case for SSE single instructions */
+ if (b1 == 2) {
+ /* 32 bit access */
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0)));
+ } else {
+ /* 64 bit access */
+ gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_D(0)));
+ }
+ } else {
+ gen_ldo_env_A0(s->mem_index, op2_offset);
+ }
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ op2_offset = offsetof(CPUX86State,xmm_regs[rm]);
+ }
+ } else {
+ op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ op2_offset = offsetof(CPUX86State,mmx_t0);
+ gen_ldq_env_A0(s->mem_index, op2_offset);
+ } else {
+ rm = (modrm & 7);
+ op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+ }
+ }
+ switch(b) {
+ case 0x0f: /* 3DNow! data insns */
+ if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW))
+ goto illegal_op;
+ val = ldub_code(s->pc++);
+ sse_op2 = sse_op_table5[val];
+ if (!sse_op2)
+ goto illegal_op;
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr))sse_op2)(cpu_ptr0, cpu_ptr1);
+ break;
+ case 0x70: /* pshufx insn */
+ case 0xc6: /* pshufx insn */
+ val = ldub_code(s->pc++);
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr, TCGv_i32))sse_op2)(cpu_ptr0, cpu_ptr1, tcg_const_i32(val));
+ break;
+ case 0xc2:
+ /* compare insns */
+ val = ldub_code(s->pc++);
+ if (val >= 8)
+ goto illegal_op;
+ sse_op2 = sse_op_table4[val][b1];
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr))sse_op2)(cpu_ptr0, cpu_ptr1);
+ break;
+ case 0xf7:
+ /* maskmov : we must prepare A0 */
+ if (mod != 3)
+ goto illegal_op;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg(R_EDI);
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(R_EDI);
+ if (s->aflag == 0)
+ gen_op_andl_A0_ffff();
+ }
+ gen_add_A0_ds_seg(s);
+
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr, TCGv))sse_op2)(cpu_ptr0, cpu_ptr1, cpu_A0);
+ break;
+ default:
+ tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset);
+ tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset);
+ ((void (*)(TCGv_ptr, TCGv_ptr))sse_op2)(cpu_ptr0, cpu_ptr1);
+ break;
+ }
+ if (b == 0x2e || b == 0x2f) {
+ s->cc_op = CC_OP_EFLAGS;
+ }
+ }
+}
+
+/* convert one instruction. s->is_jmp is set if the translation must
+ be stopped. Return the next pc value */
+static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
+{
+ int b, prefixes, aflag, dflag;
+ int shift, ot;
+ int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val;
+ target_ulong next_eip, tval;
+ int rex_w, rex_r;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(pc_start);
+ s->pc = pc_start;
+ prefixes = 0;
+ aflag = s->code32;
+ dflag = s->code32;
+ s->override = -1;
+ rex_w = -1;
+ rex_r = 0;
+#ifdef TARGET_X86_64
+ s->rex_x = 0;
+ s->rex_b = 0;
+ x86_64_hregs = 0;
+#endif
+ s->rip_offset = 0; /* for relative ip address */
+ next_byte:
+ b = ldub_code(s->pc);
+ s->pc++;
+ /* check prefixes */
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ switch (b) {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ goto next_byte;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ goto next_byte;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ goto next_byte;
+ case 0x2e:
+ s->override = R_CS;
+ goto next_byte;
+ case 0x36:
+ s->override = R_SS;
+ goto next_byte;
+ case 0x3e:
+ s->override = R_DS;
+ goto next_byte;
+ case 0x26:
+ s->override = R_ES;
+ goto next_byte;
+ case 0x64:
+ s->override = R_FS;
+ goto next_byte;
+ case 0x65:
+ s->override = R_GS;
+ goto next_byte;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ goto next_byte;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ goto next_byte;
+ case 0x40 ... 0x4f:
+ /* REX prefix */
+ rex_w = (b >> 3) & 1;
+ rex_r = (b & 0x4) << 1;
+ s->rex_x = (b & 0x2) << 2;
+ REX_B(s) = (b & 0x1) << 3;
+ x86_64_hregs = 1; /* select uniform byte register addressing */
+ goto next_byte;
+ }
+ if (rex_w == 1) {
+ /* 0x66 is ignored if rex.w is set */
+ dflag = 2;
+ } else {
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+ }
+ if (!(prefixes & PREFIX_ADR))
+ aflag = 2;
+ } else
+#endif
+ {
+ switch (b) {
+ case 0xf3:
+ prefixes |= PREFIX_REPZ;
+ goto next_byte;
+ case 0xf2:
+ prefixes |= PREFIX_REPNZ;
+ goto next_byte;
+ case 0xf0:
+ prefixes |= PREFIX_LOCK;
+ goto next_byte;
+ case 0x2e:
+ s->override = R_CS;
+ goto next_byte;
+ case 0x36:
+ s->override = R_SS;
+ goto next_byte;
+ case 0x3e:
+ s->override = R_DS;
+ goto next_byte;
+ case 0x26:
+ s->override = R_ES;
+ goto next_byte;
+ case 0x64:
+ s->override = R_FS;
+ goto next_byte;
+ case 0x65:
+ s->override = R_GS;
+ goto next_byte;
+ case 0x66:
+ prefixes |= PREFIX_DATA;
+ goto next_byte;
+ case 0x67:
+ prefixes |= PREFIX_ADR;
+ goto next_byte;
+ }
+ if (prefixes & PREFIX_DATA)
+ dflag ^= 1;
+ if (prefixes & PREFIX_ADR)
+ aflag ^= 1;
+ }
+
+ s->prefix = prefixes;
+ s->aflag = aflag;
+ s->dflag = dflag;
+
+ /* lock generation */
+ if (prefixes & PREFIX_LOCK)
+ gen_helper_lock();
+
+ /* now check op code */
+ reswitch:
+ switch(b) {
+ case 0x0f:
+ /**************************/
+ /* extended op code */
+ b = ldub_code(s->pc++) | 0x100;
+ goto reswitch;
+
+ /**************************/
+ /* arith & logic */
+ case 0x00 ... 0x05:
+ case 0x08 ... 0x0d:
+ case 0x10 ... 0x15:
+ case 0x18 ... 0x1d:
+ case 0x20 ... 0x25:
+ case 0x28 ... 0x2d:
+ case 0x30 ... 0x35:
+ case 0x38 ... 0x3d:
+ {
+ int op, f, val;
+ op = (b >> 3) & 7;
+ f = (b >> 1) & 3;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ switch(f) {
+ case 0: /* OP Ev, Gv */
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else if (op == OP_XORL && rm == reg) {
+ xor_zero:
+ /* xor reg, reg optimisation */
+ gen_op_movl_T0_0();
+ s->cc_op = CC_OP_LOGICB + ot;
+ gen_op_mov_reg_T0(ot, reg);
+ gen_op_update1_cc();
+ break;
+ } else {
+ opreg = rm;
+ }
+ gen_op_mov_TN_reg(ot, 1, reg);
+ gen_op(s, op, ot, opreg);
+ break;
+ case 1: /* OP Gv, Ev */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ } else if (op == OP_XORL && rm == reg) {
+ goto xor_zero;
+ } else {
+ gen_op_mov_TN_reg(ot, 1, rm);
+ }
+ gen_op(s, op, ot, reg);
+ break;
+ case 2: /* OP A, Iv */
+ val = insn_get(s, ot);
+ gen_op_movl_T1_im(val);
+ gen_op(s, op, ot, OR_EAX);
+ break;
+ }
+ }
+ break;
+
+ case 0x82:
+ if (CODE64(s))
+ goto illegal_op;
+ case 0x80: /* GRP1 */
+ case 0x81:
+ case 0x83:
+ {
+ int val;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ op = (modrm >> 3) & 7;
+
+ if (mod != 3) {
+ if (b == 0x83)
+ s->rip_offset = 1;
+ else
+ s->rip_offset = insn_const_size(ot);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else {
+ opreg = rm;
+ }
+
+ switch(b) {
+ default:
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ val = insn_get(s, ot);
+ break;
+ case 0x83:
+ val = (int8_t)insn_get(s, OT_BYTE);
+ break;
+ }
+ gen_op_movl_T1_im(val);
+ gen_op(s, op, ot, opreg);
+ }
+ break;
+
+ /**************************/
+ /* inc, dec, and other misc arith */
+ case 0x40 ... 0x47: /* inc Gv */
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_inc(s, ot, OR_EAX + (b & 7), 1);
+ break;
+ case 0x48 ... 0x4f: /* dec Gv */
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_inc(s, ot, OR_EAX + (b & 7), -1);
+ break;
+ case 0xf6: /* GRP3 */
+ case 0xf7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ op = (modrm >> 3) & 7;
+ if (mod != 3) {
+ if (op == 0)
+ s->rip_offset = insn_const_size(ot);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ }
+
+ switch(op) {
+ case 0: /* test */
+ val = insn_get(s, ot);
+ gen_op_movl_T1_im(val);
+ gen_op_testl_T0_T1_cc();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+ case 2: /* not */
+ tcg_gen_not_tl(cpu_T[0], cpu_T[0]);
+ if (mod != 3) {
+ gen_op_st_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_mov_reg_T0(ot, rm);
+ }
+ break;
+ case 3: /* neg */
+ tcg_gen_neg_tl(cpu_T[0], cpu_T[0]);
+ if (mod != 3) {
+ gen_op_st_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_mov_reg_T0(ot, rm);
+ }
+ gen_op_update_neg_cc();
+ s->cc_op = CC_OP_SUBB + ot;
+ break;
+ case 4: /* mul */
+ switch(ot) {
+ case OT_BYTE:
+ gen_op_mov_TN_reg(OT_BYTE, 1, R_EAX);
+ tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext8u_tl(cpu_T[1], cpu_T[1]);
+ /* XXX: use 32 bit mul which could be faster */
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_T[0], 0xff00);
+ s->cc_op = CC_OP_MULB;
+ break;
+ case OT_WORD:
+ gen_op_mov_TN_reg(OT_WORD, 1, R_EAX);
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext16u_tl(cpu_T[1], cpu_T[1]);
+ /* XXX: use 32 bit mul which could be faster */
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16);
+ gen_op_mov_reg_T0(OT_WORD, R_EDX);
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]);
+ s->cc_op = CC_OP_MULW;
+ break;
+ default:
+ case OT_LONG:
+#ifdef TARGET_X86_64
+ gen_op_mov_TN_reg(OT_LONG, 1, R_EAX);
+ tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext32u_tl(cpu_T[1], cpu_T[1]);
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_T0(OT_LONG, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 32);
+ gen_op_mov_reg_T0(OT_LONG, R_EDX);
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]);
+#else
+ {
+ TCGv_i64 t0, t1;
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ gen_op_mov_TN_reg(OT_LONG, 1, R_EAX);
+ tcg_gen_extu_i32_i64(t0, cpu_T[0]);
+ tcg_gen_extu_i32_i64(t1, cpu_T[1]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_trunc_i64_i32(cpu_T[0], t0);
+ gen_op_mov_reg_T0(OT_LONG, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(cpu_T[0], t0);
+ gen_op_mov_reg_T0(OT_LONG, R_EDX);
+ tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]);
+ }
+#endif
+ s->cc_op = CC_OP_MULL;
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_helper_mulq_EAX_T0(cpu_T[0]);
+ s->cc_op = CC_OP_MULQ;
+ break;
+#endif
+ }
+ break;
+ case 5: /* imul */
+ switch(ot) {
+ case OT_BYTE:
+ gen_op_mov_TN_reg(OT_BYTE, 1, R_EAX);
+ tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext8s_tl(cpu_T[1], cpu_T[1]);
+ /* XXX: use 32 bit mul which could be faster */
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_ext8s_tl(cpu_tmp0, cpu_T[0]);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
+ s->cc_op = CC_OP_MULB;
+ break;
+ case OT_WORD:
+ gen_op_mov_TN_reg(OT_WORD, 1, R_EAX);
+ tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]);
+ /* XXX: use 32 bit mul which could be faster */
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16);
+ gen_op_mov_reg_T0(OT_WORD, R_EDX);
+ s->cc_op = CC_OP_MULW;
+ break;
+ default:
+ case OT_LONG:
+#ifdef TARGET_X86_64
+ gen_op_mov_TN_reg(OT_LONG, 1, R_EAX);
+ tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext32s_tl(cpu_T[1], cpu_T[1]);
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ gen_op_mov_reg_T0(OT_LONG, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_ext32s_tl(cpu_tmp0, cpu_T[0]);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
+ tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 32);
+ gen_op_mov_reg_T0(OT_LONG, R_EDX);
+#else
+ {
+ TCGv_i64 t0, t1;
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ gen_op_mov_TN_reg(OT_LONG, 1, R_EAX);
+ tcg_gen_ext_i32_i64(t0, cpu_T[0]);
+ tcg_gen_ext_i32_i64(t1, cpu_T[1]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_trunc_i64_i32(cpu_T[0], t0);
+ gen_op_mov_reg_T0(OT_LONG, R_EAX);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_sari_tl(cpu_tmp0, cpu_T[0], 31);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(cpu_T[0], t0);
+ gen_op_mov_reg_T0(OT_LONG, R_EDX);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
+ }
+#endif
+ s->cc_op = CC_OP_MULL;
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_helper_imulq_EAX_T0(cpu_T[0]);
+ s->cc_op = CC_OP_MULQ;
+ break;
+#endif
+ }
+ break;
+ case 6: /* div */
+ switch(ot) {
+ case OT_BYTE:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_divb_AL(cpu_T[0]);
+ break;
+ case OT_WORD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_divw_AX(cpu_T[0]);
+ break;
+ default:
+ case OT_LONG:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_divl_EAX(cpu_T[0]);
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_divq_EAX(cpu_T[0]);
+ break;
+#endif
+ }
+ break;
+ case 7: /* idiv */
+ switch(ot) {
+ case OT_BYTE:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_idivb_AL(cpu_T[0]);
+ break;
+ case OT_WORD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_idivw_AX(cpu_T[0]);
+ break;
+ default:
+ case OT_LONG:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_idivl_EAX(cpu_T[0]);
+ break;
+#ifdef TARGET_X86_64
+ case OT_QUAD:
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_idivq_EAX(cpu_T[0]);
+ break;
+#endif
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+
+ case 0xfe: /* GRP4 */
+ case 0xff: /* GRP5 */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ op = (modrm >> 3) & 7;
+ if (op >= 2 && b == 0xfe) {
+ goto illegal_op;
+ }
+ if (CODE64(s)) {
+ if (op == 2 || op == 4) {
+ /* operand size for jumps is 64 bit */
+ ot = OT_QUAD;
+ } else if (op == 3 || op == 5) {
+ ot = dflag ? OT_LONG + (rex_w == 1) : OT_WORD;
+ } else if (op == 6) {
+ /* default push size is 64 bit */
+ ot = dflag ? OT_QUAD : OT_WORD;
+ }
+ }
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (op >= 2 && op != 3 && op != 5)
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ }
+
+ switch(op) {
+ case 0: /* inc Ev */
+ if (mod != 3)
+ opreg = OR_TMP0;
+ else
+ opreg = rm;
+ gen_inc(s, ot, opreg, 1);
+ break;
+ case 1: /* dec Ev */
+ if (mod != 3)
+ opreg = OR_TMP0;
+ else
+ opreg = rm;
+ gen_inc(s, ot, opreg, -1);
+ break;
+ case 2: /* call Ev */
+ /* XXX: optimize if memory (no 'and' is necessary) */
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ next_eip = s->pc - s->cs_base;
+ gen_movtl_T1_im(next_eip);
+ gen_push_T1(s);
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 3: /* lcall Ev */
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ gen_op_ldu_T0_A0(OT_WORD + s->mem_index);
+ do_lcall:
+ if (s->pe && !s->vm86) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_lcall_protected(cpu_tmp2_i32, cpu_T[1],
+ tcg_const_i32(dflag),
+ tcg_const_i32(s->pc - pc_start));
+ } else {
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_lcall_real(cpu_tmp2_i32, cpu_T[1],
+ tcg_const_i32(dflag),
+ tcg_const_i32(s->pc - s->cs_base));
+ }
+ gen_eob(s);
+ break;
+ case 4: /* jmp Ev */
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 5: /* ljmp Ev */
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ gen_op_ldu_T0_A0(OT_WORD + s->mem_index);
+ do_ljmp:
+ if (s->pe && !s->vm86) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_ljmp_protected(cpu_tmp2_i32, cpu_T[1],
+ tcg_const_i32(s->pc - pc_start));
+ } else {
+ gen_op_movl_seg_T0_vm(R_CS);
+ gen_op_movl_T0_T1();
+ gen_op_jmp_T0();
+ }
+ gen_eob(s);
+ break;
+ case 6: /* push Ev */
+ gen_push_T0(s);
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+
+ case 0x84: /* test Ev, Gv */
+ case 0x85:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_op_mov_TN_reg(ot, 1, reg);
+ gen_op_testl_T0_T1_cc();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+
+ case 0xa8: /* test eAX, Iv */
+ case 0xa9:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ val = insn_get(s, ot);
+
+ gen_op_mov_TN_reg(ot, 0, OR_EAX);
+ gen_op_movl_T1_im(val);
+ gen_op_testl_T0_T1_cc();
+ s->cc_op = CC_OP_LOGICB + ot;
+ break;
+
+ case 0x98: /* CWDE/CBW */
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(OT_QUAD, R_EAX);
+ } else
+#endif
+ if (dflag == 1) {
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EAX);
+ tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(OT_LONG, R_EAX);
+ } else {
+ gen_op_mov_TN_reg(OT_BYTE, 0, R_EAX);
+ tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ }
+ break;
+ case 0x99: /* CDQ/CWD */
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ gen_op_mov_TN_reg(OT_QUAD, 0, R_EAX);
+ tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 63);
+ gen_op_mov_reg_T0(OT_QUAD, R_EDX);
+ } else
+#endif
+ if (dflag == 1) {
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 31);
+ gen_op_mov_reg_T0(OT_LONG, R_EDX);
+ } else {
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EAX);
+ tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 15);
+ gen_op_mov_reg_T0(OT_WORD, R_EDX);
+ }
+ break;
+ case 0x1af: /* imul Gv, Ev */
+ case 0x69: /* imul Gv, Ev, I */
+ case 0x6b:
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (b == 0x69)
+ s->rip_offset = insn_const_size(ot);
+ else if (b == 0x6b)
+ s->rip_offset = 1;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ if (b == 0x69) {
+ val = insn_get(s, ot);
+ gen_op_movl_T1_im(val);
+ } else if (b == 0x6b) {
+ val = (int8_t)insn_get(s, OT_BYTE);
+ gen_op_movl_T1_im(val);
+ } else {
+ gen_op_mov_TN_reg(ot, 1, reg);
+ }
+
+#ifdef TARGET_X86_64
+ if (ot == OT_QUAD) {
+ gen_helper_imulq_T0_T1(cpu_T[0], cpu_T[0], cpu_T[1]);
+ } else
+#endif
+ if (ot == OT_LONG) {
+#ifdef TARGET_X86_64
+ tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext32s_tl(cpu_T[1], cpu_T[1]);
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_ext32s_tl(cpu_tmp0, cpu_T[0]);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
+#else
+ {
+ TCGv_i64 t0, t1;
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ tcg_gen_ext_i32_i64(t0, cpu_T[0]);
+ tcg_gen_ext_i32_i64(t1, cpu_T[1]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_trunc_i64_i32(cpu_T[0], t0);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_sari_tl(cpu_tmp0, cpu_T[0], 31);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(cpu_T[1], t0);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[1], cpu_tmp0);
+ }
+#endif
+ } else {
+ tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]);
+ /* XXX: use 32 bit mul which could be faster */
+ tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]);
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]);
+ tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]);
+ tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0);
+ }
+ gen_op_mov_reg_T0(ot, reg);
+ s->cc_op = CC_OP_MULB + ot;
+ break;
+ case 0x1c0:
+ case 0x1c1: /* xadd Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3) {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_TN_reg(ot, 0, reg);
+ gen_op_mov_TN_reg(ot, 1, rm);
+ gen_op_addl_T0_T1();
+ gen_op_mov_reg_T1(ot, reg);
+ gen_op_mov_reg_T0(ot, rm);
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_mov_TN_reg(ot, 0, reg);
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_op_addl_T0_T1();
+ gen_op_st_T0_A0(ot + s->mem_index);
+ gen_op_mov_reg_T1(ot, reg);
+ }
+ gen_op_update2_cc();
+ s->cc_op = CC_OP_ADDB + ot;
+ break;
+ case 0x1b0:
+ case 0x1b1: /* cmpxchg Ev, Gv */
+ {
+ int label1, label2;
+ TCGv t0, t1, t2, a0;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ t2 = tcg_temp_local_new();
+ a0 = tcg_temp_local_new();
+ gen_op_mov_v_reg(ot, t1, reg);
+ if (mod == 3) {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_v_reg(ot, t0, rm);
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ tcg_gen_mov_tl(a0, cpu_A0);
+ gen_op_ld_v(ot + s->mem_index, t0, a0);
+ rm = 0; /* avoid warning */
+ }
+ label1 = gen_new_label();
+ tcg_gen_sub_tl(t2, cpu_regs[R_EAX], t0);
+ gen_extu(ot, t2);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1);
+ if (mod == 3) {
+ label2 = gen_new_label();
+ gen_op_mov_reg_v(ot, R_EAX, t0);
+ tcg_gen_br(label2);
+ gen_set_label(label1);
+ gen_op_mov_reg_v(ot, rm, t1);
+ gen_set_label(label2);
+ } else {
+ tcg_gen_mov_tl(t1, t0);
+ gen_op_mov_reg_v(ot, R_EAX, t0);
+ gen_set_label(label1);
+ /* always store */
+ gen_op_st_v(ot + s->mem_index, t1, a0);
+ }
+ tcg_gen_mov_tl(cpu_cc_src, t0);
+ tcg_gen_mov_tl(cpu_cc_dst, t2);
+ s->cc_op = CC_OP_SUBB + ot;
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ tcg_temp_free(a0);
+ }
+ break;
+ case 0x1c7: /* cmpxchg8b */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if ((mod == 3) || ((modrm & 0x38) != 0x8))
+ goto illegal_op;
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ if (!(s->cpuid_ext_features & CPUID_EXT_CX16))
+ goto illegal_op;
+ gen_jmp_im(pc_start - s->cs_base);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_helper_cmpxchg16b(cpu_A0);
+ } else
+#endif
+ {
+ if (!(s->cpuid_features & CPUID_CX8))
+ goto illegal_op;
+ gen_jmp_im(pc_start - s->cs_base);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_helper_cmpxchg8b(cpu_A0);
+ }
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+
+ /**************************/
+ /* push/pop */
+ case 0x50 ... 0x57: /* push */
+ gen_op_mov_TN_reg(OT_LONG, 0, (b & 7) | REX_B(s));
+ gen_push_T0(s);
+ break;
+ case 0x58 ... 0x5f: /* pop */
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ gen_pop_T0(s);
+ /* NOTE: order is important for pop %sp */
+ gen_pop_update(s);
+ gen_op_mov_reg_T0(ot, (b & 7) | REX_B(s));
+ break;
+ case 0x60: /* pusha */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_pusha(s);
+ break;
+ case 0x61: /* popa */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_popa(s);
+ break;
+ case 0x68: /* push Iv */
+ case 0x6a:
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ if (b == 0x68)
+ val = insn_get(s, ot);
+ else
+ val = (int8_t)insn_get(s, OT_BYTE);
+ gen_op_movl_T0_im(val);
+ gen_push_T0(s);
+ break;
+ case 0x8f: /* pop Ev */
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ gen_pop_T0(s);
+ if (mod == 3) {
+ /* NOTE: order is important for pop %sp */
+ gen_pop_update(s);
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_reg_T0(ot, rm);
+ } else {
+ /* NOTE: order is important too for MMU exceptions */
+ s->popl_esp_hack = 1 << ot;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ s->popl_esp_hack = 0;
+ gen_pop_update(s);
+ }
+ break;
+ case 0xc8: /* enter */
+ {
+ int level;
+ val = lduw_code(s->pc);
+ s->pc += 2;
+ level = ldub_code(s->pc++);
+ gen_enter(s, val, level);
+ }
+ break;
+ case 0xc9: /* leave */
+ /* XXX: exception not precise (ESP is updated before potential exception) */
+ if (CODE64(s)) {
+ gen_op_mov_TN_reg(OT_QUAD, 0, R_EBP);
+ gen_op_mov_reg_T0(OT_QUAD, R_ESP);
+ } else if (s->ss32) {
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EBP);
+ gen_op_mov_reg_T0(OT_LONG, R_ESP);
+ } else {
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EBP);
+ gen_op_mov_reg_T0(OT_WORD, R_ESP);
+ }
+ gen_pop_T0(s);
+ if (CODE64(s)) {
+ ot = dflag ? OT_QUAD : OT_WORD;
+ } else {
+ ot = dflag + OT_WORD;
+ }
+ gen_op_mov_reg_T0(ot, R_EBP);
+ gen_pop_update(s);
+ break;
+ case 0x06: /* push es */
+ case 0x0e: /* push cs */
+ case 0x16: /* push ss */
+ case 0x1e: /* push ds */
+ if (CODE64(s))
+ goto illegal_op;
+ gen_op_movl_T0_seg(b >> 3);
+ gen_push_T0(s);
+ break;
+ case 0x1a0: /* push fs */
+ case 0x1a8: /* push gs */
+ gen_op_movl_T0_seg((b >> 3) & 7);
+ gen_push_T0(s);
+ break;
+ case 0x07: /* pop es */
+ case 0x17: /* pop ss */
+ case 0x1f: /* pop ds */
+ if (CODE64(s))
+ goto illegal_op;
+ reg = b >> 3;
+ gen_pop_T0(s);
+ gen_movl_seg_T0(s, reg, pc_start - s->cs_base);
+ gen_pop_update(s);
+ if (reg == R_SS) {
+ /* if reg == SS, inhibit interrupts/trace. */
+ /* If several instructions disable interrupts, only the
+ _first_ does it */
+ if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK))
+ gen_helper_set_inhibit_irq();
+ s->tf = 0;
+ }
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 0x1a1: /* pop fs */
+ case 0x1a9: /* pop gs */
+ gen_pop_T0(s);
+ gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base);
+ gen_pop_update(s);
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+
+ /**************************/
+ /* mov */
+ case 0x88:
+ case 0x89: /* mov Gv, Ev */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ /* generate a generic store */
+ gen_ldst_modrm(s, modrm, ot, reg, 1);
+ break;
+ case 0xc6:
+ case 0xc7: /* mov Ev, Iv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod != 3) {
+ s->rip_offset = insn_const_size(ot);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ }
+ val = insn_get(s, ot);
+ gen_op_movl_T0_im(val);
+ if (mod != 3)
+ gen_op_st_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_reg_T0(ot, (modrm & 7) | REX_B(s));
+ break;
+ case 0x8a:
+ case 0x8b: /* mov Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = OT_WORD + dflag;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_op_mov_reg_T0(ot, reg);
+ break;
+ case 0x8e: /* mov seg, Gv */
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ if (reg >= 6 || reg == R_CS)
+ goto illegal_op;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_movl_seg_T0(s, reg, pc_start - s->cs_base);
+ if (reg == R_SS) {
+ /* if reg == SS, inhibit interrupts/trace */
+ /* If several instructions disable interrupts, only the
+ _first_ does it */
+ if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK))
+ gen_helper_set_inhibit_irq();
+ s->tf = 0;
+ }
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 0x8c: /* mov Gv, seg */
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ if (reg >= 6)
+ goto illegal_op;
+ gen_op_movl_T0_seg(reg);
+ if (mod == 3)
+ ot = OT_WORD + dflag;
+ else
+ ot = OT_WORD;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
+
+ case 0x1b6: /* movzbS Gv, Eb */
+ case 0x1b7: /* movzwS Gv, Eb */
+ case 0x1be: /* movsbS Gv, Eb */
+ case 0x1bf: /* movswS Gv, Eb */
+ {
+ int d_ot;
+ /* d_ot is the size of destination */
+ d_ot = dflag + OT_WORD;
+ /* ot is the size of source */
+ ot = (b & 1) + OT_BYTE;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+
+ if (mod == 3) {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ switch(ot | (b & 8)) {
+ case OT_BYTE:
+ tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]);
+ break;
+ case OT_BYTE | 8:
+ tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]);
+ break;
+ case OT_WORD:
+ tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]);
+ break;
+ default:
+ case OT_WORD | 8:
+ tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]);
+ break;
+ }
+ gen_op_mov_reg_T0(d_ot, reg);
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (b & 8) {
+ gen_op_lds_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_ldu_T0_A0(ot + s->mem_index);
+ }
+ gen_op_mov_reg_T0(d_ot, reg);
+ }
+ }
+ break;
+
+ case 0x8d: /* lea */
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ /* we must ensure that no segment is added */
+ s->override = -1;
+ val = s->addseg;
+ s->addseg = 0;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ s->addseg = val;
+ gen_op_mov_reg_A0(ot - OT_WORD, reg);
+ break;
+
+ case 0xa0: /* mov EAX, Ov */
+ case 0xa1:
+ case 0xa2: /* mov Ov, EAX */
+ case 0xa3:
+ {
+ target_ulong offset_addr;
+
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ offset_addr = ldq_code(s->pc);
+ s->pc += 8;
+ gen_op_movq_A0_im(offset_addr);
+ } else
+#endif
+ {
+ if (s->aflag) {
+ offset_addr = insn_get(s, OT_LONG);
+ } else {
+ offset_addr = insn_get(s, OT_WORD);
+ }
+ gen_op_movl_A0_im(offset_addr);
+ }
+ gen_add_A0_ds_seg(s);
+ if ((b & 2) == 0) {
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ gen_op_mov_reg_T0(ot, R_EAX);
+ } else {
+ gen_op_mov_TN_reg(ot, 0, R_EAX);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ }
+ }
+ break;
+ case 0xd7: /* xlat */
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg(R_EBX);
+ gen_op_mov_TN_reg(OT_QUAD, 0, R_EAX);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xff);
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]);
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(R_EBX);
+ gen_op_mov_TN_reg(OT_LONG, 0, R_EAX);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xff);
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]);
+ if (s->aflag == 0)
+ gen_op_andl_A0_ffff();
+ else
+ tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
+ }
+ gen_add_A0_ds_seg(s);
+ gen_op_ldu_T0_A0(OT_BYTE + s->mem_index);
+ gen_op_mov_reg_T0(OT_BYTE, R_EAX);
+ break;
+ case 0xb0 ... 0xb7: /* mov R, Ib */
+ val = insn_get(s, OT_BYTE);
+ gen_op_movl_T0_im(val);
+ gen_op_mov_reg_T0(OT_BYTE, (b & 7) | REX_B(s));
+ break;
+ case 0xb8 ... 0xbf: /* mov R, Iv */
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ uint64_t tmp;
+ /* 64 bit case */
+ tmp = ldq_code(s->pc);
+ s->pc += 8;
+ reg = (b & 7) | REX_B(s);
+ gen_movtl_T0_im(tmp);
+ gen_op_mov_reg_T0(OT_QUAD, reg);
+ } else
+#endif
+ {
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = insn_get(s, ot);
+ reg = (b & 7) | REX_B(s);
+ gen_op_movl_T0_im(val);
+ gen_op_mov_reg_T0(ot, reg);
+ }
+ break;
+
+ case 0x91 ... 0x97: /* xchg R, EAX */
+ do_xchg_reg_eax:
+ ot = dflag + OT_WORD;
+ reg = (b & 7) | REX_B(s);
+ rm = R_EAX;
+ goto do_xchg_reg;
+ case 0x86:
+ case 0x87: /* xchg Ev, Gv */
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3) {
+ rm = (modrm & 7) | REX_B(s);
+ do_xchg_reg:
+ gen_op_mov_TN_reg(ot, 0, reg);
+ gen_op_mov_TN_reg(ot, 1, rm);
+ gen_op_mov_reg_T0(ot, rm);
+ gen_op_mov_reg_T1(ot, reg);
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_mov_TN_reg(ot, 0, reg);
+ /* for xchg, lock is implicit */
+ if (!(prefixes & PREFIX_LOCK))
+ gen_helper_lock();
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_op_st_T0_A0(ot + s->mem_index);
+ if (!(prefixes & PREFIX_LOCK))
+ gen_helper_unlock();
+ gen_op_mov_reg_T1(ot, reg);
+ }
+ break;
+ case 0xc4: /* les Gv */
+ if (CODE64(s))
+ goto illegal_op;
+ op = R_ES;
+ goto do_lxx;
+ case 0xc5: /* lds Gv */
+ if (CODE64(s))
+ goto illegal_op;
+ op = R_DS;
+ goto do_lxx;
+ case 0x1b2: /* lss Gv */
+ op = R_SS;
+ goto do_lxx;
+ case 0x1b4: /* lfs Gv */
+ op = R_FS;
+ goto do_lxx;
+ case 0x1b5: /* lgs Gv */
+ op = R_GS;
+ do_lxx:
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0(ot + s->mem_index);
+ gen_add_A0_im(s, 1 << (ot - OT_WORD + 1));
+ /* load the segment first to handle exceptions properly */
+ gen_op_ldu_T0_A0(OT_WORD + s->mem_index);
+ gen_movl_seg_T0(s, op, pc_start - s->cs_base);
+ /* then put the data */
+ gen_op_mov_reg_T1(ot, reg);
+ if (s->is_jmp) {
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+
+ /************************/
+ /* shifts */
+ case 0xc0:
+ case 0xc1:
+ /* shift Ev,Ib */
+ shift = 2;
+ grp2:
+ {
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+
+ if (mod != 3) {
+ if (shift == 2) {
+ s->rip_offset = 1;
+ }
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else {
+ opreg = (modrm & 7) | REX_B(s);
+ }
+
+ /* simpler op */
+ if (shift == 0) {
+ gen_shift(s, op, ot, opreg, OR_ECX);
+ } else {
+ if (shift == 2) {
+ shift = ldub_code(s->pc++);
+ }
+ gen_shifti(s, op, ot, opreg, shift);
+ }
+ }
+ break;
+ case 0xd0:
+ case 0xd1:
+ /* shift Ev,1 */
+ shift = 1;
+ goto grp2;
+ case 0xd2:
+ case 0xd3:
+ /* shift Ev,cl */
+ shift = 0;
+ goto grp2;
+
+ case 0x1a4: /* shld imm */
+ op = 0;
+ shift = 1;
+ goto do_shiftd;
+ case 0x1a5: /* shld cl */
+ op = 0;
+ shift = 0;
+ goto do_shiftd;
+ case 0x1ac: /* shrd imm */
+ op = 1;
+ shift = 1;
+ goto do_shiftd;
+ case 0x1ad: /* shrd cl */
+ op = 1;
+ shift = 0;
+ do_shiftd:
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ opreg = OR_TMP0;
+ } else {
+ opreg = rm;
+ }
+ gen_op_mov_TN_reg(ot, 1, reg);
+
+ if (shift) {
+ val = ldub_code(s->pc++);
+ tcg_gen_movi_tl(cpu_T3, val);
+ } else {
+ tcg_gen_mov_tl(cpu_T3, cpu_regs[R_ECX]);
+ }
+ gen_shiftd_rm_T1_T3(s, ot, opreg, op);
+ break;
+
+ /************************/
+ /* floats */
+ case 0xd8 ... 0xdf:
+ if (s->flags & (HF_EM_MASK | HF_TS_MASK)) {
+ /* if CR0.EM or CR0.TS are set, generate an FPU exception */
+ /* XXX: what to do if illegal op ? */
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+ op = ((b & 7) << 3) | ((modrm >> 3) & 7);
+ if (mod != 3) {
+ /* memory op */
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ switch(op) {
+ case 0x00 ... 0x07: /* fxxxs */
+ case 0x10 ... 0x17: /* fixxxl */
+ case 0x20 ... 0x27: /* fxxxl */
+ case 0x30 ... 0x37: /* fixxx */
+ {
+ int op1;
+ op1 = op & 7;
+
+ switch(op >> 4) {
+ case 0:
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_flds_FT0(cpu_tmp2_i32);
+ break;
+ case 1:
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_fildl_FT0(cpu_tmp2_i32);
+ break;
+ case 2:
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ gen_helper_fldl_FT0(cpu_tmp1_i64);
+ break;
+ case 3:
+ default:
+ gen_op_lds_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_fildl_FT0(cpu_tmp2_i32);
+ break;
+ }
+
+ gen_helper_fp_arith_ST0_FT0(op1);
+ if (op1 == 3) {
+ /* fcomp needs pop */
+ gen_helper_fpop();
+ }
+ }
+ break;
+ case 0x08: /* flds */
+ case 0x0a: /* fsts */
+ case 0x0b: /* fstps */
+ case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */
+ case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */
+ case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */
+ switch(op & 7) {
+ case 0:
+ switch(op >> 4) {
+ case 0:
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_flds_ST0(cpu_tmp2_i32);
+ break;
+ case 1:
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_fildl_ST0(cpu_tmp2_i32);
+ break;
+ case 2:
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ gen_helper_fldl_ST0(cpu_tmp1_i64);
+ break;
+ case 3:
+ default:
+ gen_op_lds_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_fildl_ST0(cpu_tmp2_i32);
+ break;
+ }
+ break;
+ case 1:
+ /* XXX: the corresponding CPUID bit must be tested ! */
+ switch(op >> 4) {
+ case 1:
+ gen_helper_fisttl_ST0(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ break;
+ case 2:
+ gen_helper_fisttll_ST0(cpu_tmp1_i64);
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ break;
+ case 3:
+ default:
+ gen_helper_fistt_ST0(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ break;
+ }
+ gen_helper_fpop();
+ break;
+ default:
+ switch(op >> 4) {
+ case 0:
+ gen_helper_fsts_ST0(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ break;
+ case 1:
+ gen_helper_fistl_ST0(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ break;
+ case 2:
+ gen_helper_fstl_ST0(cpu_tmp1_i64);
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ break;
+ case 3:
+ default:
+ gen_helper_fist_ST0(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ break;
+ }
+ if ((op & 7) == 3)
+ gen_helper_fpop();
+ break;
+ }
+ break;
+ case 0x0c: /* fldenv mem */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fldenv(
+ cpu_A0, tcg_const_i32(s->dflag));
+ break;
+ case 0x0d: /* fldcw mem */
+ gen_op_ld_T0_A0(OT_WORD + s->mem_index);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_fldcw(cpu_tmp2_i32);
+ break;
+ case 0x0e: /* fnstenv mem */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fstenv(cpu_A0, tcg_const_i32(s->dflag));
+ break;
+ case 0x0f: /* fnstcw mem */
+ gen_helper_fnstcw(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ break;
+ case 0x1d: /* fldt mem */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fldt_ST0(cpu_A0);
+ break;
+ case 0x1f: /* fstpt mem */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fstt_ST0(cpu_A0);
+ gen_helper_fpop();
+ break;
+ case 0x2c: /* frstor mem */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_frstor(cpu_A0, tcg_const_i32(s->dflag));
+ break;
+ case 0x2e: /* fnsave mem */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fsave(cpu_A0, tcg_const_i32(s->dflag));
+ break;
+ case 0x2f: /* fnstsw mem */
+ gen_helper_fnstsw(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ break;
+ case 0x3c: /* fbld */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fbld_ST0(cpu_A0);
+ break;
+ case 0x3e: /* fbstp */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fbst_ST0(cpu_A0);
+ gen_helper_fpop();
+ break;
+ case 0x3d: /* fildll */
+ tcg_gen_qemu_ld64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ gen_helper_fildll_ST0(cpu_tmp1_i64);
+ break;
+ case 0x3f: /* fistpll */
+ gen_helper_fistll_ST0(cpu_tmp1_i64);
+ tcg_gen_qemu_st64(cpu_tmp1_i64, cpu_A0,
+ (s->mem_index >> 2) - 1);
+ gen_helper_fpop();
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else {
+ /* register float ops */
+ opreg = rm;
+
+ switch(op) {
+ case 0x08: /* fld sti */
+ gen_helper_fpush();
+ gen_helper_fmov_ST0_STN(tcg_const_i32((opreg + 1) & 7));
+ break;
+ case 0x09: /* fxchg sti */
+ case 0x29: /* fxchg4 sti, undocumented op */
+ case 0x39: /* fxchg7 sti, undocumented op */
+ gen_helper_fxchg_ST0_STN(tcg_const_i32(opreg));
+ break;
+ case 0x0a: /* grp d9/2 */
+ switch(rm) {
+ case 0: /* fnop */
+ /* check exceptions (FreeBSD FPU probe) */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fwait();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0c: /* grp d9/4 */
+ switch(rm) {
+ case 0: /* fchs */
+ gen_helper_fchs_ST0();
+ break;
+ case 1: /* fabs */
+ gen_helper_fabs_ST0();
+ break;
+ case 4: /* ftst */
+ gen_helper_fldz_FT0();
+ gen_helper_fcom_ST0_FT0();
+ break;
+ case 5: /* fxam */
+ gen_helper_fxam_ST0();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0d: /* grp d9/5 */
+ {
+ switch(rm) {
+ case 0:
+ gen_helper_fpush();
+ gen_helper_fld1_ST0();
+ break;
+ case 1:
+ gen_helper_fpush();
+ gen_helper_fldl2t_ST0();
+ break;
+ case 2:
+ gen_helper_fpush();
+ gen_helper_fldl2e_ST0();
+ break;
+ case 3:
+ gen_helper_fpush();
+ gen_helper_fldpi_ST0();
+ break;
+ case 4:
+ gen_helper_fpush();
+ gen_helper_fldlg2_ST0();
+ break;
+ case 5:
+ gen_helper_fpush();
+ gen_helper_fldln2_ST0();
+ break;
+ case 6:
+ gen_helper_fpush();
+ gen_helper_fldz_ST0();
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ break;
+ case 0x0e: /* grp d9/6 */
+ switch(rm) {
+ case 0: /* f2xm1 */
+ gen_helper_f2xm1();
+ break;
+ case 1: /* fyl2x */
+ gen_helper_fyl2x();
+ break;
+ case 2: /* fptan */
+ gen_helper_fptan();
+ break;
+ case 3: /* fpatan */
+ gen_helper_fpatan();
+ break;
+ case 4: /* fxtract */
+ gen_helper_fxtract();
+ break;
+ case 5: /* fprem1 */
+ gen_helper_fprem1();
+ break;
+ case 6: /* fdecstp */
+ gen_helper_fdecstp();
+ break;
+ default:
+ case 7: /* fincstp */
+ gen_helper_fincstp();
+ break;
+ }
+ break;
+ case 0x0f: /* grp d9/7 */
+ switch(rm) {
+ case 0: /* fprem */
+ gen_helper_fprem();
+ break;
+ case 1: /* fyl2xp1 */
+ gen_helper_fyl2xp1();
+ break;
+ case 2: /* fsqrt */
+ gen_helper_fsqrt();
+ break;
+ case 3: /* fsincos */
+ gen_helper_fsincos();
+ break;
+ case 5: /* fscale */
+ gen_helper_fscale();
+ break;
+ case 4: /* frndint */
+ gen_helper_frndint();
+ break;
+ case 6: /* fsin */
+ gen_helper_fsin();
+ break;
+ default:
+ case 7: /* fcos */
+ gen_helper_fcos();
+ break;
+ }
+ break;
+ case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */
+ case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */
+ case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */
+ {
+ int op1;
+
+ op1 = op & 7;
+ if (op >= 0x20) {
+ gen_helper_fp_arith_STN_ST0(op1, opreg);
+ if (op >= 0x30)
+ gen_helper_fpop();
+ } else {
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fp_arith_ST0_FT0(op1);
+ }
+ }
+ break;
+ case 0x02: /* fcom */
+ case 0x22: /* fcom2, undocumented op */
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fcom_ST0_FT0();
+ break;
+ case 0x03: /* fcomp */
+ case 0x23: /* fcomp3, undocumented op */
+ case 0x32: /* fcomp5, undocumented op */
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fcom_ST0_FT0();
+ gen_helper_fpop();
+ break;
+ case 0x15: /* da/5 */
+ switch(rm) {
+ case 1: /* fucompp */
+ gen_helper_fmov_FT0_STN(tcg_const_i32(1));
+ gen_helper_fucom_ST0_FT0();
+ gen_helper_fpop();
+ gen_helper_fpop();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x1c:
+ switch(rm) {
+ case 0: /* feni (287 only, just do nop here) */
+ break;
+ case 1: /* fdisi (287 only, just do nop here) */
+ break;
+ case 2: /* fclex */
+ gen_helper_fclex();
+ break;
+ case 3: /* fninit */
+ gen_helper_fninit();
+ break;
+ case 4: /* fsetpm (287 only, just do nop here) */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x1d: /* fucomi */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fucomi_ST0_FT0();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x1e: /* fcomi */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fcomi_ST0_FT0();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x28: /* ffree sti */
+ gen_helper_ffree_STN(tcg_const_i32(opreg));
+ break;
+ case 0x2a: /* fst sti */
+ gen_helper_fmov_STN_ST0(tcg_const_i32(opreg));
+ break;
+ case 0x2b: /* fstp sti */
+ case 0x0b: /* fstp1 sti, undocumented op */
+ case 0x3a: /* fstp8 sti, undocumented op */
+ case 0x3b: /* fstp9 sti, undocumented op */
+ gen_helper_fmov_STN_ST0(tcg_const_i32(opreg));
+ gen_helper_fpop();
+ break;
+ case 0x2c: /* fucom st(i) */
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fucom_ST0_FT0();
+ break;
+ case 0x2d: /* fucomp st(i) */
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fucom_ST0_FT0();
+ gen_helper_fpop();
+ break;
+ case 0x33: /* de/3 */
+ switch(rm) {
+ case 1: /* fcompp */
+ gen_helper_fmov_FT0_STN(tcg_const_i32(1));
+ gen_helper_fcom_ST0_FT0();
+ gen_helper_fpop();
+ gen_helper_fpop();
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x38: /* ffreep sti, undocumented op */
+ gen_helper_ffree_STN(tcg_const_i32(opreg));
+ gen_helper_fpop();
+ break;
+ case 0x3c: /* df/4 */
+ switch(rm) {
+ case 0:
+ gen_helper_fnstsw(cpu_tmp2_i32);
+ tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32);
+ gen_op_mov_reg_T0(OT_WORD, R_EAX);
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x3d: /* fucomip */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fucomi_ST0_FT0();
+ gen_helper_fpop();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x3e: /* fcomip */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_fmov_FT0_STN(tcg_const_i32(opreg));
+ gen_helper_fcomi_ST0_FT0();
+ gen_helper_fpop();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x10 ... 0x13: /* fcmovxx */
+ case 0x18 ... 0x1b:
+ {
+ int op1, l1;
+ static const uint8_t fcmov_cc[8] = {
+ (JCC_B << 1),
+ (JCC_Z << 1),
+ (JCC_BE << 1),
+ (JCC_P << 1),
+ };
+ op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1);
+ l1 = gen_new_label();
+ gen_jcc1(s, s->cc_op, op1, l1);
+ gen_helper_fmov_ST0_STN(tcg_const_i32(opreg));
+ gen_set_label(l1);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ break;
+ /************************/
+ /* string ops */
+
+ case 0xa4: /* movsS */
+ case 0xa5:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_movs(s, ot);
+ }
+ break;
+
+ case 0xaa: /* stosS */
+ case 0xab:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_stos(s, ot);
+ }
+ break;
+ case 0xac: /* lodsS */
+ case 0xad:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_lods(s, ot);
+ }
+ break;
+ case 0xae: /* scasS */
+ case 0xaf:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ if (prefixes & PREFIX_REPNZ) {
+ gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1);
+ } else if (prefixes & PREFIX_REPZ) {
+ gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0);
+ } else {
+ gen_scas(s, ot);
+ s->cc_op = CC_OP_SUBB + ot;
+ }
+ break;
+
+ case 0xa6: /* cmpsS */
+ case 0xa7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag + OT_WORD;
+ if (prefixes & PREFIX_REPNZ) {
+ gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1);
+ } else if (prefixes & PREFIX_REPZ) {
+ gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0);
+ } else {
+ gen_cmps(s, ot);
+ s->cc_op = CC_OP_SUBB + ot;
+ }
+ break;
+ case 0x6c: /* insS */
+ case 0x6d:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
+ gen_op_andl_T0_ffff();
+ gen_check_io(s, ot, pc_start - s->cs_base,
+ SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4);
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_ins(s, ot);
+ if (use_icount) {
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ }
+ break;
+ case 0x6e: /* outsS */
+ case 0x6f:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
+ gen_op_andl_T0_ffff();
+ gen_check_io(s, ot, pc_start - s->cs_base,
+ svm_is_rep(prefixes) | 4);
+ if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
+ gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ } else {
+ gen_outs(s, ot);
+ if (use_icount) {
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ }
+ break;
+
+ /************************/
+ /* port I/O */
+
+ case 0xe4:
+ case 0xe5:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = ldub_code(s->pc++);
+ gen_op_movl_T0_im(val);
+ gen_check_io(s, ot, pc_start - s->cs_base,
+ SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes));
+ if (use_icount)
+ gen_io_start();
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32);
+ gen_op_mov_reg_T1(ot, R_EAX);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+ case 0xe6:
+ case 0xe7:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ val = ldub_code(s->pc++);
+ gen_op_movl_T0_im(val);
+ gen_check_io(s, ot, pc_start - s->cs_base,
+ svm_is_rep(prefixes));
+ gen_op_mov_TN_reg(ot, 1, R_EAX);
+
+ if (use_icount)
+ gen_io_start();
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
+ tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
+ gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+ case 0xec:
+ case 0xed:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
+ gen_op_andl_T0_ffff();
+ gen_check_io(s, ot, pc_start - s->cs_base,
+ SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes));
+ if (use_icount)
+ gen_io_start();
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32);
+ gen_op_mov_reg_T1(ot, R_EAX);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+ case 0xee:
+ case 0xef:
+ if ((b & 1) == 0)
+ ot = OT_BYTE;
+ else
+ ot = dflag ? OT_LONG : OT_WORD;
+ gen_op_mov_TN_reg(OT_WORD, 0, R_EDX);
+ gen_op_andl_T0_ffff();
+ gen_check_io(s, ot, pc_start - s->cs_base,
+ svm_is_rep(prefixes));
+ gen_op_mov_TN_reg(ot, 1, R_EAX);
+
+ if (use_icount)
+ gen_io_start();
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff);
+ tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]);
+ gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32);
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+
+ /************************/
+ /* control */
+ case 0xc2: /* ret im */
+ val = ldsw_code(s->pc);
+ s->pc += 2;
+ gen_pop_T0(s);
+ if (CODE64(s) && s->dflag)
+ s->dflag = 2;
+ gen_stack_update(s, val + (2 << s->dflag));
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 0xc3: /* ret */
+ gen_pop_T0(s);
+ gen_pop_update(s);
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ gen_op_jmp_T0();
+ gen_eob(s);
+ break;
+ case 0xca: /* lret im */
+ val = ldsw_code(s->pc);
+ s->pc += 2;
+ do_lret:
+ if (s->pe && !s->vm86) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_lret_protected(tcg_const_i32(s->dflag),
+ tcg_const_i32(val));
+ } else {
+ gen_stack_A0(s);
+ /* pop offset */
+ gen_op_ld_T0_A0(1 + s->dflag + s->mem_index);
+ if (s->dflag == 0)
+ gen_op_andl_T0_ffff();
+ /* NOTE: keeping EIP updated is not a problem in case of
+ exception */
+ gen_op_jmp_T0();
+ /* pop selector */
+ gen_op_addl_A0_im(2 << s->dflag);
+ gen_op_ld_T0_A0(1 + s->dflag + s->mem_index);
+ gen_op_movl_seg_T0_vm(R_CS);
+ /* add stack offset */
+ gen_stack_update(s, val + (4 << s->dflag));
+ }
+ gen_eob(s);
+ break;
+ case 0xcb: /* lret */
+ val = 0;
+ goto do_lret;
+ case 0xcf: /* iret */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_IRET);
+ if (!s->pe) {
+ /* real mode */
+ gen_helper_iret_real(tcg_const_i32(s->dflag));
+ s->cc_op = CC_OP_EFLAGS;
+ } else if (s->vm86) {
+ if (s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_helper_iret_real(tcg_const_i32(s->dflag));
+ s->cc_op = CC_OP_EFLAGS;
+ }
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_iret_protected(tcg_const_i32(s->dflag),
+ tcg_const_i32(s->pc - s->cs_base));
+ s->cc_op = CC_OP_EFLAGS;
+ }
+ gen_eob(s);
+ break;
+ case 0xe8: /* call im */
+ {
+ if (dflag)
+ tval = (int32_t)insn_get(s, OT_LONG);
+ else
+ tval = (int16_t)insn_get(s, OT_WORD);
+ next_eip = s->pc - s->cs_base;
+ tval += next_eip;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ else if(!CODE64(s))
+ tval &= 0xffffffff;
+ gen_movtl_T0_im(next_eip);
+ gen_push_T0(s);
+ gen_jmp(s, tval);
+ }
+ break;
+ case 0x9a: /* lcall im */
+ {
+ unsigned int selector, offset;
+
+ if (CODE64(s))
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ offset = insn_get(s, ot);
+ selector = insn_get(s, OT_WORD);
+
+ gen_op_movl_T0_im(selector);
+ gen_op_movl_T1_imu(offset);
+ }
+ goto do_lcall;
+ case 0xe9: /* jmp im */
+ if (dflag)
+ tval = (int32_t)insn_get(s, OT_LONG);
+ else
+ tval = (int16_t)insn_get(s, OT_WORD);
+ tval += s->pc - s->cs_base;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ else if(!CODE64(s))
+ tval &= 0xffffffff;
+ gen_jmp(s, tval);
+ break;
+ case 0xea: /* ljmp im */
+ {
+ unsigned int selector, offset;
+
+ if (CODE64(s))
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ offset = insn_get(s, ot);
+ selector = insn_get(s, OT_WORD);
+
+ gen_op_movl_T0_im(selector);
+ gen_op_movl_T1_imu(offset);
+ }
+ goto do_ljmp;
+ case 0xeb: /* jmp Jb */
+ tval = (int8_t)insn_get(s, OT_BYTE);
+ tval += s->pc - s->cs_base;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ gen_jmp(s, tval);
+ break;
+ case 0x70 ... 0x7f: /* jcc Jb */
+ tval = (int8_t)insn_get(s, OT_BYTE);
+ goto do_jcc;
+ case 0x180 ... 0x18f: /* jcc Jv */
+ if (dflag) {
+ tval = (int32_t)insn_get(s, OT_LONG);
+ } else {
+ tval = (int16_t)insn_get(s, OT_WORD);
+ }
+ do_jcc:
+ next_eip = s->pc - s->cs_base;
+ tval += next_eip;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+ gen_jcc(s, b, tval, next_eip);
+ break;
+
+ case 0x190 ... 0x19f: /* setcc Gv */
+ modrm = ldub_code(s->pc++);
+ gen_setcc(s, b);
+ gen_ldst_modrm(s, modrm, OT_BYTE, OR_TMP0, 1);
+ break;
+ case 0x140 ... 0x14f: /* cmov Gv, Ev */
+ {
+ int l1;
+ TCGv t0;
+
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ t0 = tcg_temp_local_new();
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
+ } else {
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_v_reg(ot, t0, rm);
+ }
+#ifdef TARGET_X86_64
+ if (ot == OT_LONG) {
+ /* XXX: specific Intel behaviour ? */
+ l1 = gen_new_label();
+ gen_jcc1(s, s->cc_op, b ^ 1, l1);
+ tcg_gen_mov_tl(cpu_regs[reg], t0);
+ gen_set_label(l1);
+ tcg_gen_ext32u_tl(cpu_regs[reg], cpu_regs[reg]);
+ } else
+#endif
+ {
+ l1 = gen_new_label();
+ gen_jcc1(s, s->cc_op, b ^ 1, l1);
+ gen_op_mov_reg_v(ot, reg, t0);
+ gen_set_label(l1);
+ }
+ tcg_temp_free(t0);
+ }
+ break;
+
+ /************************/
+ /* flags */
+ case 0x9c: /* pushf */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_PUSHF);
+ if (s->vm86 && s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_read_eflags(cpu_T[0]);
+ gen_push_T0(s);
+ }
+ break;
+ case 0x9d: /* popf */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_POPF);
+ if (s->vm86 && s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_pop_T0(s);
+ if (s->cpl == 0) {
+ if (s->dflag) {
+ gen_helper_write_eflags(cpu_T[0],
+ tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK)));
+ } else {
+ gen_helper_write_eflags(cpu_T[0],
+ tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK) & 0xffff));
+ }
+ } else {
+ if (s->cpl <= s->iopl) {
+ if (s->dflag) {
+ gen_helper_write_eflags(cpu_T[0],
+ tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK)));
+ } else {
+ gen_helper_write_eflags(cpu_T[0],
+ tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK) & 0xffff));
+ }
+ } else {
+ if (s->dflag) {
+ gen_helper_write_eflags(cpu_T[0],
+ tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK)));
+ } else {
+ gen_helper_write_eflags(cpu_T[0],
+ tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff));
+ }
+ }
+ }
+ gen_pop_update(s);
+ s->cc_op = CC_OP_EFLAGS;
+ /* abort translation because TF flag may change */
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 0x9e: /* sahf */
+ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM))
+ goto illegal_op;
+ gen_op_mov_TN_reg(OT_BYTE, 0, R_AH);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O);
+ tcg_gen_andi_tl(cpu_T[0], cpu_T[0], CC_S | CC_Z | CC_A | CC_P | CC_C);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_T[0]);
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x9f: /* lahf */
+ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags(cpu_T[0]);
+ /* Note: gen_compute_eflags() only gives the condition codes */
+ tcg_gen_ori_tl(cpu_T[0], cpu_T[0], 0x02);
+ gen_op_mov_reg_T0(OT_BYTE, R_AH);
+ break;
+ case 0xf5: /* cmc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C);
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xf8: /* clc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C);
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xf9: /* stc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C);
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xfc: /* cld */
+ tcg_gen_movi_i32(cpu_tmp2_i32, 1);
+ tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUState, df));
+ break;
+ case 0xfd: /* std */
+ tcg_gen_movi_i32(cpu_tmp2_i32, -1);
+ tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUState, df));
+ break;
+
+ /************************/
+ /* bit operations */
+ case 0x1ba: /* bt/bts/btr/btc Gv, im */
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ op = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ if (mod != 3) {
+ s->rip_offset = 1;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ }
+ /* load shift */
+ val = ldub_code(s->pc++);
+ gen_op_movl_T1_im(val);
+ if (op < 4)
+ goto illegal_op;
+ op -= 4;
+ goto bt_op;
+ case 0x1a3: /* bt Gv, Ev */
+ op = 0;
+ goto do_btx;
+ case 0x1ab: /* bts */
+ op = 1;
+ goto do_btx;
+ case 0x1b3: /* btr */
+ op = 2;
+ goto do_btx;
+ case 0x1bb: /* btc */
+ op = 3;
+ do_btx:
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+ gen_op_mov_TN_reg(OT_LONG, 1, reg);
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ /* specific case: we need to add a displacement */
+ gen_exts(ot, cpu_T[1]);
+ tcg_gen_sari_tl(cpu_tmp0, cpu_T[1], 3 + ot);
+ tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, ot);
+ tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
+ gen_op_ld_T0_A0(ot + s->mem_index);
+ } else {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ }
+ bt_op:
+ tcg_gen_andi_tl(cpu_T[1], cpu_T[1], (1 << (3 + ot)) - 1);
+ switch(op) {
+ case 0:
+ tcg_gen_shr_tl(cpu_cc_src, cpu_T[0], cpu_T[1]);
+ tcg_gen_movi_tl(cpu_cc_dst, 0);
+ break;
+ case 1:
+ tcg_gen_shr_tl(cpu_tmp4, cpu_T[0], cpu_T[1]);
+ tcg_gen_movi_tl(cpu_tmp0, 1);
+ tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]);
+ tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0);
+ break;
+ case 2:
+ tcg_gen_shr_tl(cpu_tmp4, cpu_T[0], cpu_T[1]);
+ tcg_gen_movi_tl(cpu_tmp0, 1);
+ tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]);
+ tcg_gen_not_tl(cpu_tmp0, cpu_tmp0);
+ tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_tmp0);
+ break;
+ default:
+ case 3:
+ tcg_gen_shr_tl(cpu_tmp4, cpu_T[0], cpu_T[1]);
+ tcg_gen_movi_tl(cpu_tmp0, 1);
+ tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]);
+ tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp0);
+ break;
+ }
+ s->cc_op = CC_OP_SARB + ot;
+ if (op != 0) {
+ if (mod != 3)
+ gen_op_st_T0_A0(ot + s->mem_index);
+ else
+ gen_op_mov_reg_T0(ot, rm);
+ tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4);
+ tcg_gen_movi_tl(cpu_cc_dst, 0);
+ }
+ break;
+ case 0x1bc: /* bsf */
+ case 0x1bd: /* bsr */
+ {
+ int label1;
+ TCGv t0;
+
+ ot = dflag + OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_ldst_modrm(s,modrm, ot, OR_TMP0, 0);
+ gen_extu(ot, cpu_T[0]);
+ t0 = tcg_temp_local_new();
+ tcg_gen_mov_tl(t0, cpu_T[0]);
+ if ((b & 1) && (prefixes & PREFIX_REPZ) &&
+ (s->cpuid_ext3_features & CPUID_EXT3_ABM)) {
+ switch(ot) {
+ case OT_WORD: gen_helper_lzcnt(cpu_T[0], t0,
+ tcg_const_i32(16)); break;
+ case OT_LONG: gen_helper_lzcnt(cpu_T[0], t0,
+ tcg_const_i32(32)); break;
+ case OT_QUAD: gen_helper_lzcnt(cpu_T[0], t0,
+ tcg_const_i32(64)); break;
+ }
+ gen_op_mov_reg_T0(ot, reg);
+ } else {
+ label1 = gen_new_label();
+ tcg_gen_movi_tl(cpu_cc_dst, 0);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, label1);
+ if (b & 1) {
+ gen_helper_bsr(cpu_T[0], t0);
+ } else {
+ gen_helper_bsf(cpu_T[0], t0);
+ }
+ gen_op_mov_reg_T0(ot, reg);
+ tcg_gen_movi_tl(cpu_cc_dst, 1);
+ gen_set_label(label1);
+ tcg_gen_discard_tl(cpu_cc_src);
+ s->cc_op = CC_OP_LOGICB + ot;
+ }
+ tcg_temp_free(t0);
+ }
+ break;
+ /************************/
+ /* bcd */
+ case 0x27: /* daa */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_daa();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x2f: /* das */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_das();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x37: /* aaa */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_aaa();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x3f: /* aas */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_helper_aas();
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0xd4: /* aam */
+ if (CODE64(s))
+ goto illegal_op;
+ val = ldub_code(s->pc++);
+ if (val == 0) {
+ gen_exception(s, EXCP00_DIVZ, pc_start - s->cs_base);
+ } else {
+ gen_helper_aam(tcg_const_i32(val));
+ s->cc_op = CC_OP_LOGICB;
+ }
+ break;
+ case 0xd5: /* aad */
+ if (CODE64(s))
+ goto illegal_op;
+ val = ldub_code(s->pc++);
+ gen_helper_aad(tcg_const_i32(val));
+ s->cc_op = CC_OP_LOGICB;
+ break;
+ /************************/
+ /* misc */
+ case 0x90: /* nop */
+ /* XXX: correct lock test for all insn */
+ if (prefixes & PREFIX_LOCK) {
+ goto illegal_op;
+ }
+ /* If REX_B is set, then this is xchg eax, r8d, not a nop. */
+ if (REX_B(s)) {
+ goto do_xchg_reg_eax;
+ }
+ if (prefixes & PREFIX_REPZ) {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_PAUSE);
+ }
+ break;
+ case 0x9b: /* fwait */
+ if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) ==
+ (HF_MP_MASK | HF_TS_MASK)) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fwait();
+ }
+ break;
+ case 0xcc: /* int3 */
+ gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base);
+ break;
+ case 0xcd: /* int N */
+ val = ldub_code(s->pc++);
+ if (s->vm86 && s->iopl != 3) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base);
+ }
+ break;
+ case 0xce: /* into */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_into(tcg_const_i32(s->pc - pc_start));
+ break;
+#ifdef WANT_ICEBP
+ case 0xf1: /* icebp (undocumented, exits to external debugger) */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_ICEBP);
+#if 1
+ gen_debug(s, pc_start - s->cs_base);
+#else
+ /* start debug */
+ tb_flush(cpu_single_env);
+ cpu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM);
+#endif
+ break;
+#endif
+ case 0xfa: /* cli */
+ if (!s->vm86) {
+ if (s->cpl <= s->iopl) {
+ gen_helper_cli();
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ } else {
+ if (s->iopl == 3) {
+ gen_helper_cli();
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ }
+ break;
+ case 0xfb: /* sti */
+ if (!s->vm86) {
+ if (s->cpl <= s->iopl) {
+ gen_sti:
+ gen_helper_sti();
+ /* interruptions are enabled only the first insn after sti */
+ /* If several instructions disable interrupts, only the
+ _first_ does it */
+ if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK))
+ gen_helper_set_inhibit_irq();
+ /* give a chance to handle pending irqs */
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ } else {
+ if (s->iopl == 3) {
+ goto gen_sti;
+ } else {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ }
+ }
+ break;
+ case 0x62: /* bound */
+ if (CODE64(s))
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ gen_op_mov_TN_reg(ot, 0, reg);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_jmp_im(pc_start - s->cs_base);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ if (ot == OT_WORD)
+ gen_helper_boundw(cpu_A0, cpu_tmp2_i32);
+ else
+ gen_helper_boundl(cpu_A0, cpu_tmp2_i32);
+ break;
+ case 0x1c8 ... 0x1cf: /* bswap reg */
+ reg = (b & 7) | REX_B(s);
+#ifdef TARGET_X86_64
+ if (dflag == 2) {
+ gen_op_mov_TN_reg(OT_QUAD, 0, reg);
+ tcg_gen_bswap64_i64(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(OT_QUAD, reg);
+ } else
+#endif
+ {
+ gen_op_mov_TN_reg(OT_LONG, 0, reg);
+ tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]);
+ tcg_gen_bswap32_tl(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(OT_LONG, reg);
+ }
+ break;
+ case 0xd6: /* salc */
+ if (CODE64(s))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags_c(cpu_T[0]);
+ tcg_gen_neg_tl(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(OT_BYTE, R_EAX);
+ break;
+ case 0xe0: /* loopnz */
+ case 0xe1: /* loopz */
+ case 0xe2: /* loop */
+ case 0xe3: /* jecxz */
+ {
+ int l1, l2, l3;
+
+ tval = (int8_t)insn_get(s, OT_BYTE);
+ next_eip = s->pc - s->cs_base;
+ tval += next_eip;
+ if (s->dflag == 0)
+ tval &= 0xffff;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ l3 = gen_new_label();
+ b &= 3;
+ switch(b) {
+ case 0: /* loopnz */
+ case 1: /* loopz */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_op_add_reg_im(s->aflag, R_ECX, -1);
+ gen_op_jz_ecx(s->aflag, l3);
+ gen_compute_eflags(cpu_tmp0);
+ tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, CC_Z);
+ if (b == 0) {
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, l1);
+ } else {
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, l1);
+ }
+ break;
+ case 2: /* loop */
+ gen_op_add_reg_im(s->aflag, R_ECX, -1);
+ gen_op_jnz_ecx(s->aflag, l1);
+ break;
+ default:
+ case 3: /* jcxz */
+ gen_op_jz_ecx(s->aflag, l1);
+ break;
+ }
+
+ gen_set_label(l3);
+ gen_jmp_im(next_eip);
+ tcg_gen_br(l2);
+
+ gen_set_label(l1);
+ gen_jmp_im(tval);
+ gen_set_label(l2);
+ gen_eob(s);
+ }
+ break;
+ case 0x130: /* wrmsr */
+ case 0x132: /* rdmsr */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ if (b & 2) {
+ gen_helper_rdmsr();
+ } else {
+ gen_helper_wrmsr();
+ }
+ }
+ break;
+ case 0x131: /* rdtsc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ if (use_icount)
+ gen_io_start();
+ gen_helper_rdtsc();
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+ case 0x133: /* rdpmc */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_rdpmc();
+ break;
+ case 0x134: /* sysenter */
+ /* For Intel SYSENTER is valid on 64-bit */
+ if (CODE64(s) && cpu_single_env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1)
+ goto illegal_op;
+ if (!s->pe) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_sysenter();
+ gen_eob(s);
+ }
+ break;
+ case 0x135: /* sysexit */
+ /* For Intel SYSEXIT is valid on 64-bit */
+ if (CODE64(s) && cpu_single_env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1)
+ goto illegal_op;
+ if (!s->pe) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_sysexit(tcg_const_i32(dflag));
+ gen_eob(s);
+ }
+ break;
+#ifdef TARGET_X86_64
+ case 0x105: /* syscall */
+ /* XXX: is it usable in real mode ? */
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_syscall(tcg_const_i32(s->pc - pc_start));
+ gen_eob(s);
+ break;
+ case 0x107: /* sysret */
+ if (!s->pe) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_sysret(tcg_const_i32(s->dflag));
+ /* condition codes are modified only in long mode */
+ if (s->lma)
+ s->cc_op = CC_OP_EFLAGS;
+ gen_eob(s);
+ }
+ break;
+#endif
+ case 0x1a2: /* cpuid */
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_cpuid();
+ break;
+ case 0xf4: /* hlt */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_hlt(tcg_const_i32(s->pc - pc_start));
+ s->is_jmp = DISAS_TB_JUMP;
+ }
+ break;
+ case 0x100:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ switch(op) {
+ case 0: /* sldt */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ);
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,ldt.selector));
+ ot = OT_WORD;
+ if (mod == 3)
+ ot += s->dflag;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
+ case 2: /* lldt */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE);
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_jmp_im(pc_start - s->cs_base);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_lldt(cpu_tmp2_i32);
+ }
+ break;
+ case 1: /* str */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ);
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,tr.selector));
+ ot = OT_WORD;
+ if (mod == 3)
+ ot += s->dflag;
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1);
+ break;
+ case 3: /* ltr */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE);
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_jmp_im(pc_start - s->cs_base);
+ tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]);
+ gen_helper_ltr(cpu_tmp2_i32);
+ }
+ break;
+ case 4: /* verr */
+ case 5: /* verw */
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (op == 4)
+ gen_helper_verr(cpu_T[0]);
+ else
+ gen_helper_verw(cpu_T[0]);
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x101:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ rm = modrm & 7;
+ switch(op) {
+ case 0: /* sgdt */
+ if (mod == 3)
+ goto illegal_op;
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.limit));
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ gen_add_A0_im(s, 2);
+ tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.base));
+ if (!s->dflag)
+ gen_op_andl_T0_im(0xffffff);
+ gen_op_st_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
+ break;
+ case 1:
+ if (mod == 3) {
+ switch (rm) {
+ case 0: /* monitor */
+ if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) ||
+ s->cpl != 0)
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+#ifdef TARGET_X86_64
+ if (s->aflag == 2) {
+ gen_op_movq_A0_reg(R_EAX);
+ } else
+#endif
+ {
+ gen_op_movl_A0_reg(R_EAX);
+ if (s->aflag == 0)
+ gen_op_andl_A0_ffff();
+ }
+ gen_add_A0_ds_seg(s);
+ gen_helper_monitor(cpu_A0);
+ break;
+ case 1: /* mwait */
+ if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) ||
+ s->cpl != 0)
+ goto illegal_op;
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_mwait(tcg_const_i32(s->pc - pc_start));
+ gen_eob(s);
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else { /* sidt */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.limit));
+ gen_op_st_T0_A0(OT_WORD + s->mem_index);
+ gen_add_A0_im(s, 2);
+ tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.base));
+ if (!s->dflag)
+ gen_op_andl_T0_im(0xffffff);
+ gen_op_st_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
+ }
+ break;
+ case 2: /* lgdt */
+ case 3: /* lidt */
+ if (mod == 3) {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ switch(rm) {
+ case 0: /* VMRUN */
+ if (!(s->flags & HF_SVME_MASK) || !s->pe)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ break;
+ } else {
+ gen_helper_vmrun(tcg_const_i32(s->aflag),
+ tcg_const_i32(s->pc - pc_start));
+ tcg_gen_exit_tb(0);
+ s->is_jmp = DISAS_TB_JUMP;
+ }
+ break;
+ case 1: /* VMMCALL */
+ if (!(s->flags & HF_SVME_MASK))
+ goto illegal_op;
+ gen_helper_vmmcall();
+ break;
+ case 2: /* VMLOAD */
+ if (!(s->flags & HF_SVME_MASK) || !s->pe)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ break;
+ } else {
+ gen_helper_vmload(tcg_const_i32(s->aflag));
+ }
+ break;
+ case 3: /* VMSAVE */
+ if (!(s->flags & HF_SVME_MASK) || !s->pe)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ break;
+ } else {
+ gen_helper_vmsave(tcg_const_i32(s->aflag));
+ }
+ break;
+ case 4: /* STGI */
+ if ((!(s->flags & HF_SVME_MASK) &&
+ !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) ||
+ !s->pe)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ break;
+ } else {
+ gen_helper_stgi();
+ }
+ break;
+ case 5: /* CLGI */
+ if (!(s->flags & HF_SVME_MASK) || !s->pe)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ break;
+ } else {
+ gen_helper_clgi();
+ }
+ break;
+ case 6: /* SKINIT */
+ if ((!(s->flags & HF_SVME_MASK) &&
+ !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) ||
+ !s->pe)
+ goto illegal_op;
+ gen_helper_skinit();
+ break;
+ case 7: /* INVLPGA */
+ if (!(s->flags & HF_SVME_MASK) || !s->pe)
+ goto illegal_op;
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ break;
+ } else {
+ gen_helper_invlpga(tcg_const_i32(s->aflag));
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ } else if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_svm_check_intercept(s, pc_start,
+ op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_T1_A0(OT_WORD + s->mem_index);
+ gen_add_A0_im(s, 2);
+ gen_op_ld_T0_A0(CODE64(s) + OT_LONG + s->mem_index);
+ if (!s->dflag)
+ gen_op_andl_T0_im(0xffffff);
+ if (op == 2) {
+ tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,gdt.base));
+ tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,gdt.limit));
+ } else {
+ tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,idt.base));
+ tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,idt.limit));
+ }
+ }
+ break;
+ case 4: /* smsw */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0);
+#if defined TARGET_X86_64 && defined HOST_WORDS_BIGENDIAN
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0]) + 4);
+#else
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0]));
+#endif
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 1);
+ break;
+ case 6: /* lmsw */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0);
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ gen_helper_lmsw(cpu_T[0]);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ case 7:
+ if (mod != 3) { /* invlpg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_helper_invlpg(cpu_A0);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ } else {
+ switch (rm) {
+ case 0: /* swapgs */
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ tcg_gen_ld_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,segs[R_GS].base));
+ tcg_gen_ld_tl(cpu_T[1], cpu_env,
+ offsetof(CPUX86State,kernelgsbase));
+ tcg_gen_st_tl(cpu_T[1], cpu_env,
+ offsetof(CPUX86State,segs[R_GS].base));
+ tcg_gen_st_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,kernelgsbase));
+ }
+ } else
+#endif
+ {
+ goto illegal_op;
+ }
+ break;
+ case 1: /* rdtscp */
+ if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP))
+ goto illegal_op;
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ if (use_icount)
+ gen_io_start();
+ gen_helper_rdtscp();
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x108: /* invd */
+ case 0x109: /* wbinvd */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_svm_check_intercept(s, pc_start, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD);
+ /* nothing to do */
+ }
+ break;
+ case 0x63: /* arpl or movslS (x86_64) */
+#ifdef TARGET_X86_64
+ if (CODE64(s)) {
+ int d_ot;
+ /* d_ot is the size of destination */
+ d_ot = dflag + OT_WORD;
+
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ mod = (modrm >> 6) & 3;
+ rm = (modrm & 7) | REX_B(s);
+
+ if (mod == 3) {
+ gen_op_mov_TN_reg(OT_LONG, 0, rm);
+ /* sign extend */
+ if (d_ot == OT_QUAD)
+ tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]);
+ gen_op_mov_reg_T0(d_ot, reg);
+ } else {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (d_ot == OT_QUAD) {
+ gen_op_lds_T0_A0(OT_LONG + s->mem_index);
+ } else {
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ }
+ gen_op_mov_reg_T0(d_ot, reg);
+ }
+ } else
+#endif
+ {
+ int label1;
+ TCGv t0, t1, t2, a0;
+
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ t2 = tcg_temp_local_new();
+ ot = OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = (modrm >> 3) & 7;
+ mod = (modrm >> 6) & 3;
+ rm = modrm & 7;
+ if (mod != 3) {
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_op_ld_v(ot + s->mem_index, t0, cpu_A0);
+ a0 = tcg_temp_local_new();
+ tcg_gen_mov_tl(a0, cpu_A0);
+ } else {
+ gen_op_mov_v_reg(ot, t0, rm);
+ TCGV_UNUSED(a0);
+ }
+ gen_op_mov_v_reg(ot, t1, reg);
+ tcg_gen_andi_tl(cpu_tmp0, t0, 3);
+ tcg_gen_andi_tl(t1, t1, 3);
+ tcg_gen_movi_tl(t2, 0);
+ label1 = gen_new_label();
+ tcg_gen_brcond_tl(TCG_COND_GE, cpu_tmp0, t1, label1);
+ tcg_gen_andi_tl(t0, t0, ~3);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_gen_movi_tl(t2, CC_Z);
+ gen_set_label(label1);
+ if (mod != 3) {
+ gen_op_st_v(ot + s->mem_index, t0, a0);
+ tcg_temp_free(a0);
+ } else {
+ gen_op_mov_reg_v(ot, rm, t0);
+ }
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_compute_eflags(cpu_cc_src);
+ tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2);
+ s->cc_op = CC_OP_EFLAGS;
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ }
+ break;
+ case 0x102: /* lar */
+ case 0x103: /* lsl */
+ {
+ int label1;
+ TCGv t0;
+ if (!s->pe || s->vm86)
+ goto illegal_op;
+ ot = dflag ? OT_LONG : OT_WORD;
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0);
+ t0 = tcg_temp_local_new();
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ if (b == 0x102)
+ gen_helper_lar(t0, cpu_T[0]);
+ else
+ gen_helper_lsl(t0, cpu_T[0]);
+ tcg_gen_andi_tl(cpu_tmp0, cpu_cc_src, CC_Z);
+ label1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1);
+ gen_op_mov_reg_v(ot, reg, t0);
+ gen_set_label(label1);
+ s->cc_op = CC_OP_EFLAGS;
+ tcg_temp_free(t0);
+ }
+ break;
+ case 0x118:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ switch(op) {
+ case 0: /* prefetchnta */
+ case 1: /* prefetchnt0 */
+ case 2: /* prefetchnt0 */
+ case 3: /* prefetchnt0 */
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ /* nothing more to do */
+ break;
+ default: /* nop (multi byte) */
+ gen_nop_modrm(s, modrm);
+ break;
+ }
+ break;
+ case 0x119 ... 0x11f: /* nop (multi byte) */
+ modrm = ldub_code(s->pc++);
+ gen_nop_modrm(s, modrm);
+ break;
+ case 0x120: /* mov reg, crN */
+ case 0x122: /* mov crN, reg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ modrm = ldub_code(s->pc++);
+ if ((modrm & 0xc0) != 0xc0)
+ goto illegal_op;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (CODE64(s))
+ ot = OT_QUAD;
+ else
+ ot = OT_LONG;
+ if ((prefixes & PREFIX_LOCK) && (reg == 0) &&
+ (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) {
+ reg = 8;
+ }
+ switch(reg) {
+ case 0:
+ case 2:
+ case 3:
+ case 4:
+ case 8:
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ if (b & 2) {
+ gen_op_mov_TN_reg(ot, 0, rm);
+ gen_helper_write_crN(tcg_const_i32(reg), cpu_T[0]);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ } else {
+ gen_helper_read_crN(cpu_T[0], tcg_const_i32(reg));
+ gen_op_mov_reg_T0(ot, rm);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ }
+ break;
+ case 0x121: /* mov reg, drN */
+ case 0x123: /* mov drN, reg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ modrm = ldub_code(s->pc++);
+ if ((modrm & 0xc0) != 0xc0)
+ goto illegal_op;
+ rm = (modrm & 7) | REX_B(s);
+ reg = ((modrm >> 3) & 7) | rex_r;
+ if (CODE64(s))
+ ot = OT_QUAD;
+ else
+ ot = OT_LONG;
+ /* XXX: do it dynamically with CR4.DE bit */
+ if (reg == 4 || reg == 5 || reg >= 8)
+ goto illegal_op;
+ if (b & 2) {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg);
+ gen_op_mov_TN_reg(ot, 0, rm);
+ gen_helper_movl_drN_T0(tcg_const_i32(reg), cpu_T[0]);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ } else {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg);
+ tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,dr[reg]));
+ gen_op_mov_reg_T0(ot, rm);
+ }
+ }
+ break;
+ case 0x106: /* clts */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0);
+ gen_helper_clts();
+ /* abort block because static cpu state changed */
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
+ break;
+ /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */
+ case 0x1c3: /* MOVNTI reg, mem */
+ if (!(s->cpuid_features & CPUID_SSE2))
+ goto illegal_op;
+ ot = s->dflag == 2 ? OT_QUAD : OT_LONG;
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ reg = ((modrm >> 3) & 7) | rex_r;
+ /* generate a generic store */
+ gen_ldst_modrm(s, modrm, ot, reg, 1);
+ break;
+ case 0x1ae:
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ op = (modrm >> 3) & 7;
+ switch(op) {
+ case 0: /* fxsave */
+ if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) ||
+ (s->prefix & PREFIX_LOCK))
+ goto illegal_op;
+ if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fxsave(cpu_A0, tcg_const_i32((s->dflag == 2)));
+ break;
+ case 1: /* fxrstor */
+ if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) ||
+ (s->prefix & PREFIX_LOCK))
+ goto illegal_op;
+ if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_fxrstor(cpu_A0, tcg_const_i32((s->dflag == 2)));
+ break;
+ case 2: /* ldmxcsr */
+ case 3: /* stmxcsr */
+ if (s->flags & HF_TS_MASK) {
+ gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+ break;
+ }
+ if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) ||
+ mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ if (op == 2) {
+ gen_op_ld_T0_A0(OT_LONG + s->mem_index);
+ tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
+ } else {
+ tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr));
+ gen_op_st_T0_A0(OT_LONG + s->mem_index);
+ }
+ break;
+ case 5: /* lfence */
+ case 6: /* mfence */
+ if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE))
+ goto illegal_op;
+ break;
+ case 7: /* sfence / clflush */
+ if ((modrm & 0xc7) == 0xc0) {
+ /* sfence */
+ /* XXX: also check for cpuid_ext2_features & CPUID_EXT2_EMMX */
+ if (!(s->cpuid_features & CPUID_SSE))
+ goto illegal_op;
+ } else {
+ /* clflush */
+ if (!(s->cpuid_features & CPUID_CLFLUSH))
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x10d: /* 3DNow! prefetch(w) */
+ modrm = ldub_code(s->pc++);
+ mod = (modrm >> 6) & 3;
+ if (mod == 3)
+ goto illegal_op;
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ /* ignore for now */
+ break;
+ case 0x1aa: /* rsm */
+ gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM);
+ if (!(s->flags & HF_SMM_MASK))
+ goto illegal_op;
+ gen_update_cc_op(s);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_helper_rsm();
+ gen_eob(s);
+ break;
+ case 0x1b8: /* SSE4.2 popcnt */
+ if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) !=
+ PREFIX_REPZ)
+ goto illegal_op;
+ if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT))
+ goto illegal_op;
+
+ modrm = ldub_code(s->pc++);
+ reg = ((modrm >> 3) & 7);
+
+ if (s->prefix & PREFIX_DATA)
+ ot = OT_WORD;
+ else if (s->dflag != 2)
+ ot = OT_LONG;
+ else
+ ot = OT_QUAD;
+
+ gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0);
+ gen_helper_popcnt(cpu_T[0], cpu_T[0], tcg_const_i32(ot));
+ gen_op_mov_reg_T0(ot, reg);
+
+ s->cc_op = CC_OP_EFLAGS;
+ break;
+ case 0x10e ... 0x10f:
+ /* 3DNow! instructions, ignore prefixes */
+ s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA);
+ case 0x110 ... 0x117:
+ case 0x128 ... 0x12f:
+ case 0x138 ... 0x13a:
+ case 0x150 ... 0x179:
+ case 0x17c ... 0x17f:
+ case 0x1c2:
+ case 0x1c4 ... 0x1c6:
+ case 0x1d0 ... 0x1fe:
+ gen_sse(s, b, pc_start, rex_r);
+ break;
+ default:
+ goto illegal_op;
+ }
+ /* lock generation */
+ if (s->prefix & PREFIX_LOCK)
+ gen_helper_unlock();
+ return s->pc;
+ illegal_op:
+ if (s->prefix & PREFIX_LOCK)
+ gen_helper_unlock();
+ /* XXX: ensure that no lock was generated */
+ gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base);
+ return s->pc;
+}
+
+void optimize_flags_init(void)
+{
+#if TCG_TARGET_REG_BITS == 32
+ assert(sizeof(CCTable) == (1 << 3));
+#else
+ assert(sizeof(CCTable) == (1 << 4));
+#endif
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+ cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, cc_op), "cc_op");
+ cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cc_src),
+ "cc_src");
+ cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cc_dst),
+ "cc_dst");
+ cpu_cc_tmp = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cc_tmp),
+ "cc_tmp");
+
+#ifdef TARGET_X86_64
+ cpu_regs[R_EAX] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_EAX]), "rax");
+ cpu_regs[R_ECX] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_ECX]), "rcx");
+ cpu_regs[R_EDX] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_EDX]), "rdx");
+ cpu_regs[R_EBX] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_EBX]), "rbx");
+ cpu_regs[R_ESP] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_ESP]), "rsp");
+ cpu_regs[R_EBP] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_EBP]), "rbp");
+ cpu_regs[R_ESI] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_ESI]), "rsi");
+ cpu_regs[R_EDI] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[R_EDI]), "rdi");
+ cpu_regs[8] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[8]), "r8");
+ cpu_regs[9] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[9]), "r9");
+ cpu_regs[10] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[10]), "r10");
+ cpu_regs[11] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[11]), "r11");
+ cpu_regs[12] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[12]), "r12");
+ cpu_regs[13] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[13]), "r13");
+ cpu_regs[14] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[14]), "r14");
+ cpu_regs[15] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, regs[15]), "r15");
+#else
+ cpu_regs[R_EAX] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_EAX]), "eax");
+ cpu_regs[R_ECX] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_ECX]), "ecx");
+ cpu_regs[R_EDX] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_EDX]), "edx");
+ cpu_regs[R_EBX] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_EBX]), "ebx");
+ cpu_regs[R_ESP] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_ESP]), "esp");
+ cpu_regs[R_EBP] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_EBP]), "ebp");
+ cpu_regs[R_ESI] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_ESI]), "esi");
+ cpu_regs[R_EDI] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, regs[R_EDI]), "edi");
+#endif
+
+ /* register helpers */
+#define GEN_HELPER 2
+#include "helper.h"
+}
+
+/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
+ basic block 'tb'. If search_pc is TRUE, also generate PC
+ information for each intermediate instruction. */
+static inline void gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext dc1, *dc = &dc1;
+ target_ulong pc_ptr;
+ uint16_t *gen_opc_end;
+ CPUBreakpoint *bp;
+ int j, lj;
+ uint64_t flags;
+ target_ulong pc_start;
+ target_ulong cs_base;
+ int num_insns;
+ int max_insns;
+
+ /* generate intermediate code */
+ pc_start = tb->pc;
+ cs_base = tb->cs_base;
+ flags = tb->flags;
+
+ dc->pe = (flags >> HF_PE_SHIFT) & 1;
+ dc->code32 = (flags >> HF_CS32_SHIFT) & 1;
+ dc->ss32 = (flags >> HF_SS32_SHIFT) & 1;
+ dc->addseg = (flags >> HF_ADDSEG_SHIFT) & 1;
+ dc->f_st = 0;
+ dc->vm86 = (flags >> VM_SHIFT) & 1;
+ dc->cpl = (flags >> HF_CPL_SHIFT) & 3;
+ dc->iopl = (flags >> IOPL_SHIFT) & 3;
+ dc->tf = (flags >> TF_SHIFT) & 1;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->cc_op = CC_OP_DYNAMIC;
+ dc->cs_base = cs_base;
+ dc->tb = tb;
+ dc->popl_esp_hack = 0;
+ /* select memory access functions */
+ dc->mem_index = 0;
+ if (flags & HF_SOFTMMU_MASK) {
+ if (dc->cpl == 3)
+ dc->mem_index = 2 * 4;
+ else
+ dc->mem_index = 1 * 4;
+ }
+ dc->cpuid_features = env->cpuid_features;
+ dc->cpuid_ext_features = env->cpuid_ext_features;
+ dc->cpuid_ext2_features = env->cpuid_ext2_features;
+ dc->cpuid_ext3_features = env->cpuid_ext3_features;
+#ifdef TARGET_X86_64
+ dc->lma = (flags >> HF_LMA_SHIFT) & 1;
+ dc->code64 = (flags >> HF_CS64_SHIFT) & 1;
+#endif
+ dc->flags = flags;
+ dc->jmp_opt = !(dc->tf || env->singlestep_enabled ||
+ (flags & HF_INHIBIT_IRQ_MASK)
+#ifndef CONFIG_SOFTMMU
+ || (flags & HF_SOFTMMU_MASK)
+#endif
+ );
+#if 0
+ /* check addseg logic */
+ if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32))
+ printf("ERROR addseg\n");
+#endif
+
+ cpu_T[0] = tcg_temp_new();
+ cpu_T[1] = tcg_temp_new();
+ cpu_A0 = tcg_temp_new();
+ cpu_T3 = tcg_temp_new();
+
+ cpu_tmp0 = tcg_temp_new();
+ cpu_tmp1_i64 = tcg_temp_new_i64();
+ cpu_tmp2_i32 = tcg_temp_new_i32();
+ cpu_tmp3_i32 = tcg_temp_new_i32();
+ cpu_tmp4 = tcg_temp_new();
+ cpu_tmp5 = tcg_temp_new();
+ cpu_ptr0 = tcg_temp_new_ptr();
+ cpu_ptr1 = tcg_temp_new_ptr();
+
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ dc->is_jmp = DISAS_NEXT;
+ pc_ptr = pc_start;
+ lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
+ for(;;) {
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == pc_ptr &&
+ !((bp->flags & BP_CPU) && (tb->flags & HF_RF_MASK))) {
+ gen_debug(dc, pc_ptr - dc->cs_base);
+ 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] = pc_ptr;
+ 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();
+
+ pc_ptr = disas_insn(dc, pc_ptr);
+ num_insns++;
+ /* stop translation if indicated */
+ if (dc->is_jmp)
+ break;
+ /* if single step mode, we generate only one instruction and
+ generate an exception */
+ /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear
+ the flag and abort the translation to give the irqs a
+ change to be happen */
+ if (dc->tf || dc->singlestep_enabled ||
+ (flags & HF_INHIBIT_IRQ_MASK)) {
+ gen_jmp_im(pc_ptr - dc->cs_base);
+ gen_eob(dc);
+ break;
+ }
+ /* if too long translation, stop generation too */
+ if (gen_opc_ptr >= gen_opc_end ||
+ (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32) ||
+ num_insns >= max_insns) {
+ gen_jmp_im(pc_ptr - dc->cs_base);
+ gen_eob(dc);
+ break;
+ }
+ if (singlestep) {
+ gen_jmp_im(pc_ptr - dc->cs_base);
+ gen_eob(dc);
+ break;
+ }
+ }
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+ /* we don't forget to fill the last values */
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ }
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ int disas_flags;
+ qemu_log("----------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+#ifdef TARGET_X86_64
+ if (dc->code64)
+ disas_flags = 2;
+ else
+#endif
+ disas_flags = !dc->code32;
+ log_target_disas(pc_start, pc_ptr - pc_start, disas_flags);
+ qemu_log("\n");
+ }
+#endif
+
+ if (!search_pc) {
+ tb->size = pc_ptr - pc_start;
+ tb->icount = num_insns;
+ }
+}
+
+void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 1);
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ int cc_op;
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_OP)) {
+ int i;
+ qemu_log("RESTORE:\n");
+ for(i = 0;i <= pc_pos; i++) {
+ if (gen_opc_instr_start[i]) {
+ qemu_log("0x%04x: " TARGET_FMT_lx "\n", i, gen_opc_pc[i]);
+ }
+ }
+ qemu_log("spc=0x%08lx pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n",
+ searched_pc, pc_pos, gen_opc_pc[pc_pos] - tb->cs_base,
+ (uint32_t)tb->cs_base);
+ }
+#endif
+ env->eip = gen_opc_pc[pc_pos] - tb->cs_base;
+ cc_op = gen_opc_cc_op[pc_pos];
+ if (cc_op != CC_OP_DYNAMIC)
+ env->cc_op = cc_op;
+}
diff --git a/target-ia64/cpu.h b/target-ia64/cpu.h
new file mode 100644
index 0000000..fb51463
--- /dev/null
+++ b/target-ia64/cpu.h
@@ -0,0 +1,85 @@
+/*
+ * IA64 virtual CPU header
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Copyright (c) 2007 Intel Corporation
+ * Zhang xiantao <xiantao.zhang@intel.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef CPU_IA64_H
+#define CPU_IA64_H
+#include "config.h"
+#include "ia64intrin.h"
+
+#include<string.h>
+
+#define TARGET_LONG_BITS 64
+
+#define TARGET_PAGE_BITS 16
+
+#define ELF_MACHINE EM_IA_64
+
+#define NB_MMU_MODES 2
+#define CPU_PAL_HALT 1
+#define HF_HALTED_MASK (1 << CPU_PAL_HALT)
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define CPUState struct CPUIA64State
+
+typedef struct CPUIA64State {
+ CPU_COMMON;
+ uint32_t hflags;
+ int mp_state;
+} CPUIA64State;
+
+#define cpu_gen_code cpu_ia64_gen_code
+#define cpu_init cpu_ia64_init
+#define cpu_signal_handler cpu_ia64_signal_handler
+
+extern struct CPUIA64State *env;
+int cpu_get_pic_interrupt(CPUIA64State *s);
+int cpu_exec(CPUState *env1);
+CPUState *cpu_ia64_init(const char * cpu_model);
+
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return 0;
+}
+
+#define CPU_PC_FROM_TB(env, tb) do{}while(0)
+
+#include "cpu-all.h"
+
+/*
+ * These ones really should go to the appropriate tcg header file, if/when
+ * tcg support is added for ia64.
+ */
+void tcg_dump_info(FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = 0;
+ *cs_base = 0;
+ *flags = 0;
+}
+
+#endif
diff --git a/target-ia64/exec.h b/target-ia64/exec.h
new file mode 100644
index 0000000..060d9c3
--- /dev/null
+++ b/target-ia64/exec.h
@@ -0,0 +1,61 @@
+/*
+ * IA64 execution defines
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2007 Intel Corporation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __IA64_H__
+#define __IA64_H__
+
+//#include "dyngen-exec.h"
+#include "config.h"
+
+#include "dyngen-exec.h"
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#define tcg_qemu_tb_exec(tb_ptr) 0
+
+register struct CPUIA64State *env asm(AREG0);
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+void do_interrupt (CPUState *env);
+
+void cpu_lock(void);
+void cpu_unlock(void);
+
+static inline int cpu_halted(CPUState *env) {
+ /* handle exit of HALTED state */
+ if (!(env->hflags & HF_HALTED_MASK))
+ return 0;
+ return EXCP_HALTED;
+}
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request & (CPU_INTERRUPT_HARD));
+}
+
+#endif
diff --git a/target-ia64/fake-exec.c b/target-ia64/fake-exec.c
new file mode 100644
index 0000000..8d6ded0
--- /dev/null
+++ b/target-ia64/fake-exec.c
@@ -0,0 +1,50 @@
+/*
+ * fake-exec.c for ia64.
+ *
+ * This is a file for stub functions so that compilation is possible
+ * when TCG CPU emulation is disabled during compilation.
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by & Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ *
+ * Copyright 2008 Intel Corporation.
+ * Added by Xiantao Zhang <xiantao.zhang@intel.com>
+ *
+ * This work is licensed under the GNU GPL licence version 2 or later.
+ *
+ */
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+int code_copy_enabled = 0;
+
+void cpu_gen_init(void)
+{
+}
+
+unsigned long code_gen_max_block_size(void)
+{
+ return 32;
+}
+
+int cpu_ia64_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ return 0;
+}
+
+void tcg_dump_info(FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+ return;
+}
+
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+
+{
+ return 0;
+}
diff --git a/target-ia64/firmware.c b/target-ia64/firmware.c
new file mode 100644
index 0000000..79f8464
--- /dev/null
+++ b/target-ia64/firmware.c
@@ -0,0 +1,715 @@
+/*
+ * firmware.c : Firmware build logic for ia64 platform.
+ *
+ * Ported from Xen 3.0 Source.
+ * Copyright (c) 2007, Intel Corporation.
+ * Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "cpu.h"
+
+#include "firmware.h"
+
+#include "qemu-common.h"
+
+typedef struct {
+ unsigned long signature;
+ unsigned int type;
+ unsigned int length;
+} HOB_GENERIC_HEADER;
+
+/*
+ * INFO HOB is the first data data in one HOB list
+ * it contains the control information of the HOB list
+ */
+typedef struct {
+ HOB_GENERIC_HEADER header;
+ unsigned long length; // current length of hob
+ unsigned long cur_pos; // current poisiton of hob
+ unsigned long buf_size; // size of hob buffer
+} HOB_INFO;
+
+typedef struct{
+ unsigned long start;
+ unsigned long size;
+} hob_mem_t;
+
+typedef enum {
+ HOB_TYPE_INFO=0,
+ HOB_TYPE_TERMINAL,
+ HOB_TYPE_MEM,
+ HOB_TYPE_PAL_BUS_GET_FEATURES_DATA,
+ HOB_TYPE_PAL_CACHE_SUMMARY,
+ HOB_TYPE_PAL_MEM_ATTRIB,
+ HOB_TYPE_PAL_CACHE_INFO,
+ HOB_TYPE_PAL_CACHE_PROT_INFO,
+ HOB_TYPE_PAL_DEBUG_INFO,
+ HOB_TYPE_PAL_FIXED_ADDR,
+ HOB_TYPE_PAL_FREQ_BASE,
+ HOB_TYPE_PAL_FREQ_RATIOS,
+ HOB_TYPE_PAL_HALT_INFO,
+ HOB_TYPE_PAL_PERF_MON_INFO,
+ HOB_TYPE_PAL_PROC_GET_FEATURES,
+ HOB_TYPE_PAL_PTCE_INFO,
+ HOB_TYPE_PAL_REGISTER_INFO,
+ HOB_TYPE_PAL_RSE_INFO,
+ HOB_TYPE_PAL_TEST_INFO,
+ HOB_TYPE_PAL_VM_SUMMARY,
+ HOB_TYPE_PAL_VM_INFO,
+ HOB_TYPE_PAL_VM_PAGE_SIZE,
+ HOB_TYPE_NR_VCPU,
+ HOB_TYPE_NR_NVRAM,
+ HOB_TYPE_MAX
+} hob_type_t;
+
+static int hob_init(void *buffer ,unsigned long buf_size);
+static int add_pal_hob(void* hob_buf);
+static int add_mem_hob(void* hob_buf, unsigned long dom_mem_size);
+static int add_vcpus_hob(void* hob_buf, unsigned long nr_vcpu);
+static int add_nvram_hob(void *hob_buf, unsigned long nvram_addr);
+static int build_hob(void *hob_buf, unsigned long hob_buf_size,
+ unsigned long dom_mem_size, unsigned long vcpus,
+ unsigned long nvram_addr);
+static int load_hob(void *hob_buf, unsigned long dom_mem_size);
+
+int
+kvm_ia64_build_hob(unsigned long memsize, unsigned long vcpus,
+ unsigned long nvram_addr)
+{
+ char *hob_buf;
+
+ hob_buf = malloc(GFW_HOB_SIZE);
+ if (hob_buf == NULL) {
+ Hob_Output("Hob: Could not allocate hob");
+ return -1;
+ }
+
+ if (build_hob(hob_buf, GFW_HOB_SIZE, memsize, vcpus, nvram_addr) < 0) {
+ free(hob_buf);
+ Hob_Output("Could not build hob");
+ return -1;
+ }
+
+ if (load_hob(hob_buf, memsize) < 0) {
+ free(hob_buf);
+ Hob_Output("Could not load hob");
+ return -1;
+ }
+ free(hob_buf);
+
+ return 0;
+}
+
+static int
+hob_init(void *buffer, unsigned long buf_size)
+{
+ HOB_INFO *phit;
+ HOB_GENERIC_HEADER *terminal;
+
+ if (sizeof(HOB_INFO) + sizeof(HOB_GENERIC_HEADER) > buf_size) {
+ // buffer too small
+ return -1;
+ }
+
+ phit = (HOB_INFO*)buffer;
+ phit->header.signature = HOB_SIGNATURE;
+ phit->header.type = HOB_TYPE_INFO;
+ phit->header.length = sizeof(HOB_INFO);
+ phit->length = sizeof(HOB_INFO) + sizeof(HOB_GENERIC_HEADER);
+ phit->cur_pos = 0;
+ phit->buf_size = buf_size;
+
+ terminal = (HOB_GENERIC_HEADER*)(buffer + sizeof(HOB_INFO));
+ terminal->signature = HOB_SIGNATURE;
+ terminal->type = HOB_TYPE_TERMINAL;
+ terminal->length = sizeof(HOB_GENERIC_HEADER);
+
+ return 0;
+}
+
+/*
+ * Add a new HOB to the HOB List.
+ *
+ * hob_start - start address of hob buffer
+ * type - type of the hob to be added
+ * data - data of the hob to be added
+ * data_size - size of the data
+ */
+static int
+hob_add(void* hob_start, int type, void* data, int data_size)
+{
+ HOB_INFO *phit;
+ HOB_GENERIC_HEADER *newhob, *tail;
+
+ phit = (HOB_INFO*)hob_start;
+
+ if (phit->length + data_size > phit->buf_size) {
+ // no space for new hob
+ return -1;
+ }
+
+ //append new HOB
+ newhob = (HOB_GENERIC_HEADER*)(hob_start + phit->length -
+ sizeof(HOB_GENERIC_HEADER));
+ newhob->signature = HOB_SIGNATURE;
+ newhob->type = type;
+ newhob->length = data_size + sizeof(HOB_GENERIC_HEADER);
+ memcpy((void*)newhob + sizeof(HOB_GENERIC_HEADER), data, data_size);
+
+ // append terminal HOB
+ tail = (HOB_GENERIC_HEADER*)(hob_start + phit->length + data_size);
+ tail->signature = HOB_SIGNATURE;
+ tail->type = HOB_TYPE_TERMINAL;
+ tail->length = sizeof(HOB_GENERIC_HEADER);
+
+ // adjust HOB list length
+ phit->length += sizeof(HOB_GENERIC_HEADER) + data_size;
+
+ return 0;
+}
+
+static int
+get_hob_size(void* hob_buf)
+{
+ HOB_INFO *phit = (HOB_INFO*)hob_buf;
+
+ if (phit->header.signature != HOB_SIGNATURE) {
+ Hob_Output("xc_get_hob_size:Incorrect signature");
+ return -1;
+ }
+ return phit->length;
+}
+
+static int
+add_max_hob_entry(void* hob_buf)
+{
+ long max_hob = 0;
+ return hob_add(hob_buf, HOB_TYPE_MAX, &max_hob, sizeof(long));
+}
+
+static int
+build_hob(void* hob_buf, unsigned long hob_buf_size,
+ unsigned long dom_mem_size, unsigned long vcpus,
+ unsigned long nvram_addr)
+{
+ //Init HOB List
+ if (hob_init(hob_buf, hob_buf_size) < 0) {
+ Hob_Output("buffer too small");
+ goto err_out;
+ }
+
+ if (add_mem_hob(hob_buf,dom_mem_size) < 0) {
+ Hob_Output("Add memory hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_vcpus_hob(hob_buf, vcpus) < 0) {
+ Hob_Output("Add NR_VCPU hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_pal_hob(hob_buf) < 0) {
+ Hob_Output("Add PAL hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_nvram_hob(hob_buf, nvram_addr) < 0) {
+ Hob_Output("Add nvram hob failed, buffer too small");
+ goto err_out;
+ }
+
+ if (add_max_hob_entry(hob_buf) < 0) {
+ Hob_Output("Add max hob entry failed, buffer too small");
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ return -1;
+}
+static int
+load_hob(void *hob_buf, unsigned long dom_mem_size)
+{
+ int hob_size;
+
+ hob_size = get_hob_size(hob_buf);
+ if (hob_size < 0) {
+ Hob_Output("Invalid hob data");
+ return -1;
+ }
+
+ if (hob_size > GFW_HOB_SIZE) {
+ Hob_Output("No enough memory for hob data");
+ return -1;
+ }
+
+ cpu_physical_memory_write(GFW_HOB_START, hob_buf, hob_size);
+
+ return 0;
+}
+
+static int
+add_mem_hob(void* hob_buf, unsigned long dom_mem_size)
+{
+ hob_mem_t memhob;
+
+ // less than 3G
+ memhob.start = 0;
+ memhob.size = MIN(dom_mem_size, 0xC0000000);
+
+ if (hob_add(hob_buf, HOB_TYPE_MEM, &memhob, sizeof(memhob)) < 0)
+ return -1;
+
+ if (dom_mem_size > 0xC0000000) {
+ // 4G ~ 4G+remain
+ memhob.start = 0x100000000; //4G
+ memhob.size = dom_mem_size - 0xC0000000;
+ if (hob_add(hob_buf, HOB_TYPE_MEM, &memhob, sizeof(memhob)) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static int
+add_vcpus_hob(void* hob_buf, unsigned long vcpus)
+{
+ return hob_add(hob_buf, HOB_TYPE_NR_VCPU, &vcpus, sizeof(vcpus));
+}
+
+static int
+add_nvram_hob(void *hob_buf, unsigned long nvram_addr)
+{
+ return hob_add(hob_buf, HOB_TYPE_NR_NVRAM,
+ &nvram_addr, sizeof(nvram_addr));
+}
+
+static const unsigned char config_pal_bus_get_features_data[24] = {
+ 0, 0, 0, 32, 0, 0, 240, 189, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_cache_summary[16] = {
+ 3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_mem_attrib[8] = {
+ 241, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_cache_info[152] = {
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 4, 6, 7, 255, 1, 0, 1, 0, 64, 0, 0, 12, 12,
+ 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 7, 0, 1,
+ 0, 1, 0, 64, 0, 0, 12, 12, 49, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 6, 8, 7, 7, 255, 7, 0, 11, 0, 0, 16, 0,
+ 12, 17, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 8, 7,
+ 7, 7, 5, 9, 11, 0, 0, 4, 0, 12, 15, 49, 0, 254, 255,
+ 255, 255, 255, 255, 255, 255, 2, 8, 7, 7, 7, 5, 9,
+ 11, 0, 0, 4, 0, 12, 15, 49, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 12, 7, 7, 7, 14, 1, 3, 0, 0, 192, 0, 12, 20, 49, 0
+};
+
+static const unsigned char config_pal_cache_prot_info[200] = {
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 45, 0, 16, 8, 0, 76, 12, 64, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 8, 0, 16, 4, 0, 76, 44, 68, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32,
+ 0, 16, 8, 0, 81, 44, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0,
+ 112, 12, 0, 79, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255,
+ 32, 0, 112, 12, 0, 79, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 160,
+ 12, 0, 84, 124, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0
+};
+
+static const unsigned char config_pal_debug_info[16] = {
+ 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_fixed_addr[8] = {
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_freq_base[8] = {
+ 109, 219, 182, 13, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_freq_ratios[24] = {
+ 11, 1, 0, 0, 77, 7, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 4,
+ 0, 0, 0, 7, 0, 0, 0
+};
+
+static const unsigned char config_pal_halt_info[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_perf_mon_info[136] = {
+ 12, 47, 18, 8, 0, 0, 0, 0, 241, 255, 0, 0, 255, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 241, 255, 0, 0, 223, 0, 255, 255,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 240, 255, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_proc_get_features[104] = {
+ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 64, 6, 64, 49, 0, 0, 0, 0, 64, 6, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
+ 231, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_ptce_info[24] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_register_info[64] = {
+ 255, 0, 47, 127, 17, 17, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0,
+ 255, 208, 128, 238, 238, 0, 0, 248, 255, 255, 255, 255, 255, 0, 0, 7, 3,
+ 251, 3, 0, 0, 0, 0, 255, 7, 3, 0, 0, 0, 0, 0, 248, 252, 4,
+ 252, 255, 255, 255, 255, 2, 248, 252, 255, 255, 255, 255, 255
+};
+
+static const unsigned char config_pal_rse_info[16] = {
+ 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_test_info[48] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_vm_summary[16] = {
+ 101, 18, 15, 2, 7, 7, 4, 2, 59, 18, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_vm_info[104] = {
+ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 32, 32, 0, 0, 0, 0, 0, 0, 112, 85, 21, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 32, 32, 0, 0, 0, 0, 0, 0, 112, 85,
+ 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 128, 128, 0,
+ 4, 0, 0, 0, 0, 112, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 128, 128, 0, 4, 0, 0, 0, 0, 112, 85, 0, 0, 0, 0, 0
+};
+
+static const unsigned char config_pal_vm_page_size[16] = {
+ 0, 112, 85, 21, 0, 0, 0, 0, 0, 112, 85, 21, 0, 0, 0, 0
+};
+
+typedef struct{
+ hob_type_t type;
+ void* data;
+ unsigned long size;
+} hob_batch_t;
+
+static const hob_batch_t hob_batch[]={
+ { HOB_TYPE_PAL_BUS_GET_FEATURES_DATA,
+ &config_pal_bus_get_features_data,
+ sizeof(config_pal_bus_get_features_data)
+ },
+ { HOB_TYPE_PAL_CACHE_SUMMARY,
+ &config_pal_cache_summary,
+ sizeof(config_pal_cache_summary)
+ },
+ { HOB_TYPE_PAL_MEM_ATTRIB,
+ &config_pal_mem_attrib,
+ sizeof(config_pal_mem_attrib)
+ },
+ { HOB_TYPE_PAL_CACHE_INFO,
+ &config_pal_cache_info,
+ sizeof(config_pal_cache_info)
+ },
+ { HOB_TYPE_PAL_CACHE_PROT_INFO,
+ &config_pal_cache_prot_info,
+ sizeof(config_pal_cache_prot_info)
+ },
+ { HOB_TYPE_PAL_DEBUG_INFO,
+ &config_pal_debug_info,
+ sizeof(config_pal_debug_info)
+ },
+ { HOB_TYPE_PAL_FIXED_ADDR,
+ &config_pal_fixed_addr,
+ sizeof(config_pal_fixed_addr)
+ },
+ { HOB_TYPE_PAL_FREQ_BASE,
+ &config_pal_freq_base,
+ sizeof(config_pal_freq_base)
+ },
+ { HOB_TYPE_PAL_FREQ_RATIOS,
+ &config_pal_freq_ratios,
+ sizeof(config_pal_freq_ratios)
+ },
+ { HOB_TYPE_PAL_HALT_INFO,
+ &config_pal_halt_info,
+ sizeof(config_pal_halt_info)
+ },
+ { HOB_TYPE_PAL_PERF_MON_INFO,
+ &config_pal_perf_mon_info,
+ sizeof(config_pal_perf_mon_info)
+ },
+ { HOB_TYPE_PAL_PROC_GET_FEATURES,
+ &config_pal_proc_get_features,
+ sizeof(config_pal_proc_get_features)
+ },
+ { HOB_TYPE_PAL_PTCE_INFO,
+ &config_pal_ptce_info,
+ sizeof(config_pal_ptce_info)
+ },
+ { HOB_TYPE_PAL_REGISTER_INFO,
+ &config_pal_register_info,
+ sizeof(config_pal_register_info)
+ },
+ { HOB_TYPE_PAL_RSE_INFO,
+ &config_pal_rse_info,
+ sizeof(config_pal_rse_info)
+ },
+ { HOB_TYPE_PAL_TEST_INFO,
+ &config_pal_test_info,
+ sizeof(config_pal_test_info)
+ },
+ { HOB_TYPE_PAL_VM_SUMMARY,
+ &config_pal_vm_summary,
+ sizeof(config_pal_vm_summary)
+ },
+ { HOB_TYPE_PAL_VM_INFO,
+ &config_pal_vm_info,
+ sizeof(config_pal_vm_info)
+ },
+ { HOB_TYPE_PAL_VM_PAGE_SIZE,
+ &config_pal_vm_page_size,
+ sizeof(config_pal_vm_page_size)
+ },
+};
+
+static int
+add_pal_hob(void* hob_buf)
+{
+ int i;
+ for (i = 0; i < sizeof(hob_batch)/sizeof(hob_batch_t); i++) {
+ if (hob_add(hob_buf, hob_batch[i].type, hob_batch[i].data,
+ hob_batch[i].size) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+uint8_t *read_image(const char *filename, unsigned long *size)
+{
+ int kernel_fd = -1;
+ gzFile kernel_gfd = NULL;
+ uint8_t *image = NULL, *tmp;
+ unsigned int bytes;
+
+ if ((filename == NULL) || (size == NULL))
+ return NULL;
+
+ kernel_fd = open(filename, O_RDONLY);
+ if (kernel_fd < 0) {
+ Hob_Output("Could not open kernel image\n");
+ goto out_1;
+ }
+
+ if ((kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL) {
+ Hob_Output("Could not allocate decompression state for state file\n");
+ goto out_1;
+ }
+
+ *size = 0;
+
+#define CHUNK 1*1024*1024
+ while(1)
+ {
+ if ((tmp = realloc(image, *size + CHUNK)) == NULL) {
+ Hob_Output("Could not allocate memory for kernel image");
+ free(image);
+ image = NULL;
+ goto out;
+ }
+ image = tmp;
+
+ bytes = gzread(kernel_gfd, image + *size, CHUNK);
+ switch (bytes) {
+ case -1:
+ Hob_Output("Error reading kernel image");
+ free(image);
+ image = NULL;
+ goto out;
+ case 0: /* EOF */
+ goto out;
+ default:
+ *size += bytes;
+ break;
+ }
+ }
+#undef CHUNK
+
+out:
+ if (*size == 0) {
+ Hob_Output("Could not read kernel image");
+ free(image);
+ image = NULL;
+ } else if (image) {
+ /* Shrink allocation to fit image. */
+ tmp = realloc(image, *size);
+ if (tmp)
+ image = tmp;
+ }
+
+ if (kernel_gfd != NULL)
+ gzclose(kernel_gfd);
+ else if (kernel_fd >= 0)
+ close(kernel_fd);
+ return image;
+
+out_1:
+ return NULL;
+}
+
+int kvm_ia64_nvram_init(unsigned long type)
+{
+ unsigned long nvram_fd;
+ char nvram_path[PATH_MAX];
+ unsigned long i;
+
+ if (nvram) {
+ if (strlen(nvram) > PATH_MAX) {
+ goto out;
+ }
+ if (type == READ_FROM_NVRAM) {
+ if (access(nvram, R_OK | W_OK | X_OK) == -1)
+ goto out;
+ nvram_fd = open(nvram, O_RDONLY);
+ return nvram_fd;
+ }
+ else { /* write from gfw to nvram file */
+ i = access(nvram, R_OK | W_OK | X_OK);
+ if ((i == -1) && (errno != ENOENT))
+ goto out;
+ nvram_fd = open(nvram, O_CREAT|O_RDWR, 0777);
+ return nvram_fd;
+ }
+ }
+ else {
+ strcpy(nvram_path, "nvram.dat");
+ if (type == READ_FROM_NVRAM) {
+ if (access(nvram_path, R_OK | W_OK | X_OK) == -1)
+ goto out;
+ nvram_fd = open(nvram_path, O_RDONLY);
+ return nvram_fd;
+ }
+ else { /* write from gfw to nvram file */
+ i = access(nvram_path, R_OK | W_OK | X_OK);
+ if ((i == -1) && (errno != ENOENT))
+ goto out;
+ nvram_fd = open(nvram_path, O_CREAT|O_RDWR, 0777);
+ return nvram_fd;
+ }
+ }
+out:
+ return -1;
+}
+
+int
+kvm_ia64_copy_from_nvram_to_GFW(unsigned long nvram_fd)
+{
+ struct stat file_stat;
+ uint8_t *nvram_buf;
+ int r = 0;
+
+ nvram_buf = malloc(NVRAM_SIZE);
+
+ if ((fstat(nvram_fd, &file_stat) < 0) ||
+ (NVRAM_SIZE != file_stat.st_size) ||
+ (read(nvram_fd, nvram_buf, NVRAM_SIZE) != NVRAM_SIZE)) {
+ r = -1;
+ goto out;
+ }
+
+ cpu_physical_memory_write(NVRAM_START, nvram_buf, NVRAM_SIZE);
+
+ out:
+ free(nvram_buf);
+ return r;
+}
+
+int
+kvm_ia64_copy_from_GFW_to_nvram()
+{
+ struct nvram_save_addr nvram_addr_buf;
+ uint8_t *nvram_buf;
+ unsigned long nvram_fd;
+ unsigned long type = WRITE_TO_NVRAM;
+ int ret = -1;
+
+ nvram_buf = malloc(NVRAM_SIZE);
+ if (!nvram_buf)
+ goto out_free;
+
+ cpu_physical_memory_read(NVRAM_START, (uint8_t *)&nvram_addr_buf,
+ sizeof(struct nvram_save_addr));
+ if (nvram_addr_buf.signature != NVRAM_VALID_SIG) {
+ goto out_free;
+ }
+
+ cpu_physical_memory_read(nvram_addr_buf.addr, nvram_buf, NVRAM_SIZE);
+
+ nvram_fd = kvm_ia64_nvram_init(type);
+ if (nvram_fd == -1)
+ goto out;
+
+ lseek(nvram_fd, 0, SEEK_SET);
+ if (write(nvram_fd, nvram_buf, NVRAM_SIZE) != NVRAM_SIZE)
+ goto out;
+
+ ret = 0;
+ out:
+ close(nvram_fd);
+ out_free:
+ free(nvram_buf);
+ return ret;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/target-ia64/firmware.h b/target-ia64/firmware.h
new file mode 100644
index 0000000..a47db67
--- /dev/null
+++ b/target-ia64/firmware.h
@@ -0,0 +1,62 @@
+/*
+ * firmwar.h: Firmware build logic head file
+ *
+ * Copyright (c) 2007, Intel Corporation.
+ * Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+#ifndef __FIRM_WARE_H
+#define __FIRM_WARE_
+#include "cpu.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <zlib.h>
+
+#define GFW_SIZE (16UL<<20)
+#define GFW_START ((4UL<<30) - GFW_SIZE)
+
+#define HOB_SIGNATURE 0x3436474953424f48 // "HOBSIG64"
+#define GFW_HOB_START ((4UL<<30) - (14UL<<20)) // 4G - 14M
+#define GFW_HOB_SIZE (1UL<<20) // 1M
+#define HOB_OFFSET (GFW_HOB_START-GFW_START)
+
+#define Hob_Output(s) fprintf(stderr, s)
+
+#define NVRAM_START (GFW_START + NVRAM_OFFSET)
+#define NVRAM_OFFSET (10 * (1UL << 20))
+#define NVRAM_SIZE (64 * (1UL << 10))
+#define NVRAM_VALID_SIG 0x4650494e45584948 /* "HIXENIPF" */
+#define VALIDATE_NVRAM_FD(x) ((1UL<<(sizeof(x)*8 - 1)) | x)
+#define IS_VALID_NVRAM_FD(x) ((uint64_t)x >> (sizeof(x)*8 - 1))
+#define READ_FROM_NVRAM 0
+#define WRITE_TO_NVRAM 1
+
+struct nvram_save_addr {
+ unsigned long addr;
+ unsigned long signature;
+};
+
+extern const char *nvram;
+extern int kvm_ia64_build_hob(unsigned long memsize, unsigned long vcpus,
+ unsigned long nvram_addr);
+extern uint8_t *read_image(const char *filename, unsigned long *size);
+
+extern int kvm_ia64_copy_from_GFW_to_nvram(void);
+extern int kvm_ia64_nvram_init(unsigned long type);
+extern int kvm_ia64_copy_from_nvram_to_GFW(unsigned long nvram_fd);
+#endif //__FIRM_WARE_
diff --git a/target-ia64/helper.c b/target-ia64/helper.c
new file mode 100644
index 0000000..4a94dca
--- /dev/null
+++ b/target-ia64/helper.c
@@ -0,0 +1,5 @@
+
+/*
+ * IA64 emulation helpers for qemu. (Leave it as blank now.)
+ *
+ */
diff --git a/target-ia64/libkvm.c b/target-ia64/libkvm.c
new file mode 100644
index 0000000..bf9dded
--- /dev/null
+++ b/target-ia64/libkvm.c
@@ -0,0 +1,82 @@
+/*
+ * libkvm-ia64.c :Kernel-based Virtual Machine control library for ia64.
+ *
+ * This library provides an API to control the kvm hardware virtualization
+ * module.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright (C) 2007 Intel
+ * Added by : Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ *
+ */
+
+#include "libkvm-all.h"
+#include "libkvm.h"
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int kvm_arch_run(kvm_vcpu_context_t vcpu)
+{
+ int r = 0;
+
+ switch (vcpu->run->exit_reason) {
+ default:
+ r = 1;
+ break;
+ }
+
+ return r;
+}
+
+void kvm_show_code(kvm_vcpu_context_t vcpu)
+{
+ fprintf(stderr, "kvm_show_code not supported yet!\n");
+}
+
+void kvm_show_regs(kvm_vcpu_context_t vcpu)
+{
+ fprintf(stderr,"kvm_show_regs not supportted today!\n");
+}
+
+int kvm_create_memory_alias(kvm_context_t kvm,
+ uint64_t phys_start,
+ uint64_t len,
+ uint64_t target_phys)
+{
+ return 0;
+}
+
+int kvm_destroy_memory_alias(kvm_context_t kvm, uint64_t phys_start)
+{
+ return 0;
+}
diff --git a/target-ia64/libkvm.h b/target-ia64/libkvm.h
new file mode 100644
index 0000000..417f7f1
--- /dev/null
+++ b/target-ia64/libkvm.h
@@ -0,0 +1,31 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for x86.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * derived from libkvm.c
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_IA64_H
+#define KVM_IA64_H
+
+#include "libkvm-all.h"
+
+extern int kvm_page_size;
+
+#define PAGE_SIZE kvm_page_size
+#define PAGE_MASK (~(kvm_page_size - 1))
+
+#define ia64_mf() asm volatile ("mf" ::: "memory")
+#define smp_wmb() ia64_mf()
+
+#endif
diff --git a/target-ia64/machine.c b/target-ia64/machine.c
new file mode 100644
index 0000000..8cf5bdd
--- /dev/null
+++ b/target-ia64/machine.c
@@ -0,0 +1,36 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+#include "exec-all.h"
+#include "qemu-kvm.h"
+
+void kvm_arch_save_mpstate(CPUState *env);
+void kvm_arch_load_mpstate(CPUState *env);
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+
+ if (kvm_enabled()) {
+ kvm_arch_save_mpstate(env);
+ }
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+
+ if (kvm_enabled()) {
+ kvm_arch_load_mpstate(env);
+ }
+ return 0;
+}
+
+extern QEMUMachine ipf_machine;
+
+static void ipf_machine_init(void)
+{
+ qemu_register_machine(&ipf_machine);
+}
+
+machine_init(ipf_machine_init);
diff --git a/target-ia64/op.c b/target-ia64/op.c
new file mode 100644
index 0000000..f7301c6
--- /dev/null
+++ b/target-ia64/op.c
@@ -0,0 +1,22 @@
+/*
+ * IA64 micro operations
+ *
+ * Leave it blank for future implementation
+ *
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
diff --git a/target-ia64/op_helper.c b/target-ia64/op_helper.c
new file mode 100644
index 0000000..d51525a
--- /dev/null
+++ b/target-ia64/op_helper.c
@@ -0,0 +1,104 @@
+/*
+ * op_helper.c: IA64 emulation cpu micro-operations helpers for qemu.
+ *
+ * Copyright (c) 2007 Intel Corporation
+ * Zhang Xiantao <xiantao.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#include "qemu-kvm.h"
+#include "qemu-common.h"
+
+void cpu_ia64_set_model(CPUIA64State *env, uint32_t id);
+void cpu_ia64_close(CPUIA64State *env);
+void switch_mode(CPUState *env, int mode);
+void do_interrupt(CPUIA64State *env);
+int cpu_ia64_handle_mmu_fault (CPUState *env, target_ulong address,
+ int access_type, int is_user, int is_softmmu);
+CPUState *cpu_ia64_init(const char *cpu_model)
+{
+ CPUState *env;
+ env = qemu_mallocz(sizeof(CPUState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ cpu_reset(env);
+ if (kvm_enabled()) {
+ kvm_qemu_init_env(env);
+ kvm_init_vcpu(env);
+ }
+ return env;
+}
+
+void cpu_reset(CPUIA64State *env)
+{
+}
+
+static inline void set_feature(CPUIA64State *env, int feature)
+{
+}
+
+void cpu_ia64_set_model(CPUIA64State *env, uint32_t id)
+{
+}
+
+void cpu_ia64_close(CPUIA64State *env)
+{
+ free(env);
+}
+
+extern int semihosting_enabled;
+
+void switch_mode(CPUState *env, int mode)
+{
+}
+
+/* Handle a CPU exception. */
+void do_interrupt(CPUIA64State *env)
+{
+ if (kvm_enabled()) {
+ printf("%s: unexpect\n", __FUNCTION__);
+ exit(-1);
+ }
+}
+
+int cpu_ia64_handle_mmu_fault (CPUState *env, target_ulong address,
+ int access_type, int is_user, int is_softmmu)
+{
+ return 1;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return -1;
+}
+
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+ return;
+}
+
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ return;
+}
diff --git a/target-ia64/translate.c b/target-ia64/translate.c
new file mode 100644
index 0000000..86f48f5
--- /dev/null
+++ b/target-ia64/translate.c
@@ -0,0 +1,39 @@
+/*
+ * translation.c : IA64 translation code.
+ * Just put it as blank now, and implement it later.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+static uint16_t *gen_opc_ptr;
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "gen-op.h"
+
+int gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ return 0;
+}
+int gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ return 0;
+}
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h
new file mode 100644
index 0000000..b025b66
--- /dev/null
+++ b/target-m68k/cpu.h
@@ -0,0 +1,257 @@
+/*
+ * m68k virtual CPU header
+ *
+ * Copyright (c) 2005-2007 CodeSourcery
+ * Written by Paul Brook
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CPU_M68K_H
+#define CPU_M68K_H
+
+#define TARGET_LONG_BITS 32
+
+#define CPUState struct CPUM68KState
+
+#include "qemu-common.h"
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define MAX_QREGS 32
+
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE EM_68K
+
+#define EXCP_ACCESS 2 /* Access (MMU) error. */
+#define EXCP_ADDRESS 3 /* Address error. */
+#define EXCP_ILLEGAL 4 /* Illegal instruction. */
+#define EXCP_DIV0 5 /* Divide by zero */
+#define EXCP_PRIVILEGE 8 /* Privilege violation. */
+#define EXCP_TRACE 9
+#define EXCP_LINEA 10 /* Unimplemented line-A (MAC) opcode. */
+#define EXCP_LINEF 11 /* Unimplemented line-F (FPU) opcode. */
+#define EXCP_DEBUGNBP 12 /* Non-breakpoint debug interrupt. */
+#define EXCP_DEBEGBP 13 /* Breakpoint debug interrupt. */
+#define EXCP_FORMAT 14 /* RTE format error. */
+#define EXCP_UNINITIALIZED 15
+#define EXCP_TRAP0 32 /* User trap #0. */
+#define EXCP_TRAP15 47 /* User trap #15. */
+#define EXCP_UNSUPPORTED 61
+#define EXCP_ICE 13
+
+#define EXCP_RTE 0x100
+#define EXCP_HALT_INSN 0x101
+
+#define NB_MMU_MODES 2
+
+typedef struct CPUM68KState {
+ uint32_t dregs[8];
+ uint32_t aregs[8];
+ uint32_t pc;
+ uint32_t sr;
+
+ /* SSP and USP. The current_sp is stored in aregs[7], the other here. */
+ int current_sp;
+ uint32_t sp[2];
+
+ /* Condition flags. */
+ uint32_t cc_op;
+ uint32_t cc_dest;
+ uint32_t cc_src;
+ uint32_t cc_x;
+
+ float64 fregs[8];
+ float64 fp_result;
+ uint32_t fpcr;
+ uint32_t fpsr;
+ float_status fp_status;
+
+ uint64_t mactmp;
+ /* EMAC Hardware deals with 48-bit values composed of one 32-bit and
+ two 8-bit parts. We store a single 64-bit value and
+ rearrange/extend this when changing modes. */
+ uint64_t macc[4];
+ uint32_t macsr;
+ uint32_t mac_mask;
+
+ /* Temporary storage for DIV helpers. */
+ uint32_t div1;
+ uint32_t div2;
+
+ /* MMU status. */
+ struct {
+ uint32_t ar;
+ } mmu;
+
+ /* Control registers. */
+ uint32_t vbr;
+ uint32_t mbar;
+ uint32_t rambar0;
+ uint32_t cacr;
+
+ /* ??? remove this. */
+ uint32_t t1;
+
+ int pending_vector;
+ int pending_level;
+
+ uint32_t qregs[MAX_QREGS];
+
+ CPU_COMMON
+
+ uint32_t features;
+} CPUM68KState;
+
+void m68k_tcg_init(void);
+CPUM68KState *cpu_m68k_init(const char *cpu_model);
+int cpu_m68k_exec(CPUM68KState *s);
+void cpu_m68k_close(CPUM68KState *s);
+void do_interrupt(int is_hw);
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_m68k_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+void cpu_m68k_flush_flags(CPUM68KState *, int);
+
+enum {
+ CC_OP_DYNAMIC, /* Use env->cc_op */
+ CC_OP_FLAGS, /* CC_DEST = CVZN, CC_SRC = unused */
+ CC_OP_LOGIC, /* CC_DEST = result, CC_SRC = unused */
+ CC_OP_ADD, /* CC_DEST = result, CC_SRC = source */
+ CC_OP_SUB, /* CC_DEST = result, CC_SRC = source */
+ CC_OP_CMPB, /* CC_DEST = result, CC_SRC = source */
+ CC_OP_CMPW, /* CC_DEST = result, CC_SRC = source */
+ CC_OP_ADDX, /* CC_DEST = result, CC_SRC = source */
+ CC_OP_SUBX, /* CC_DEST = result, CC_SRC = source */
+ CC_OP_SHIFT, /* CC_DEST = result, CC_SRC = carry */
+};
+
+#define CCF_C 0x01
+#define CCF_V 0x02
+#define CCF_Z 0x04
+#define CCF_N 0x08
+#define CCF_X 0x10
+
+#define SR_I_SHIFT 8
+#define SR_I 0x0700
+#define SR_M 0x1000
+#define SR_S 0x2000
+#define SR_T 0x8000
+
+#define M68K_SSP 0
+#define M68K_USP 1
+
+/* CACR fields are implementation defined, but some bits are common. */
+#define M68K_CACR_EUSP 0x10
+
+#define MACSR_PAV0 0x100
+#define MACSR_OMC 0x080
+#define MACSR_SU 0x040
+#define MACSR_FI 0x020
+#define MACSR_RT 0x010
+#define MACSR_N 0x008
+#define MACSR_Z 0x004
+#define MACSR_V 0x002
+#define MACSR_EV 0x001
+
+void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector);
+void m68k_set_macsr(CPUM68KState *env, uint32_t val);
+void m68k_switch_sp(CPUM68KState *env);
+
+#define M68K_FPCR_PREC (1 << 6)
+
+void do_m68k_semihosting(CPUM68KState *env, int nr);
+
+/* There are 4 ColdFire core ISA revisions: A, A+, B and C.
+ Each feature covers the subset of instructions common to the
+ ISA revisions mentioned. */
+
+enum m68k_features {
+ M68K_FEATURE_CF_ISA_A,
+ M68K_FEATURE_CF_ISA_B, /* (ISA B or C). */
+ M68K_FEATURE_CF_ISA_APLUSC, /* BIT/BITREV, FF1, STRLDSR (ISA A+ or C). */
+ M68K_FEATURE_BRAL, /* Long unconditional branch. (ISA A+ or B). */
+ M68K_FEATURE_CF_FPU,
+ M68K_FEATURE_CF_MAC,
+ M68K_FEATURE_CF_EMAC,
+ M68K_FEATURE_CF_EMAC_B, /* Revision B EMAC (dual accumulate). */
+ M68K_FEATURE_USP, /* User Stack Pointer. (ISA A+, B or C). */
+ M68K_FEATURE_EXT_FULL, /* 68020+ full extension word. */
+ M68K_FEATURE_WORD_INDEX /* word sized address index registers. */
+};
+
+static inline int m68k_feature(CPUM68KState *env, int feature)
+{
+ return (env->features & (1u << feature)) != 0;
+}
+
+void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+
+void register_m68k_insns (CPUM68KState *env);
+
+#ifdef CONFIG_USER_ONLY
+/* Linux uses 8k pages. */
+#define TARGET_PAGE_BITS 13
+#else
+/* Smallest TLB entry size is 1k. */
+#define TARGET_PAGE_BITS 10
+#endif
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define cpu_init cpu_m68k_init
+#define cpu_exec cpu_m68k_exec
+#define cpu_gen_code cpu_m68k_gen_code
+#define cpu_signal_handler cpu_m68k_signal_handler
+#define cpu_list m68k_cpu_list
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return (env->sr & SR_S) == 0 ? 1 : 0;
+}
+
+int cpu_m68k_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_m68k_handle_mmu_fault
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->aregs[7] = newsp;
+ env->dregs[0] = 0;
+}
+#endif
+
+#include "cpu-all.h"
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = (env->fpcr & M68K_FPCR_PREC) /* Bit 6 */
+ | (env->sr & SR_S) /* Bit 13 */
+ | ((env->macsr >> 4) & 0xf); /* Bits 0-3 */
+}
+
+#endif
diff --git a/target-m68k/exec.h b/target-m68k/exec.h
new file mode 100644
index 0000000..f31e06e
--- /dev/null
+++ b/target-m68k/exec.h
@@ -0,0 +1,50 @@
+/*
+ * m68k execution defines
+ *
+ * Copyright (c) 2005-2006 CodeSourcery
+ * Written by Paul Brook
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "dyngen-exec.h"
+
+register struct CPUM68KState *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request & (CPU_INTERRUPT_HARD));
+}
+
+static inline int cpu_halted(CPUState *env) {
+ if (!env->halted)
+ return 0;
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->pc = tb->pc;
+}
+
diff --git a/target-m68k/helper.c b/target-m68k/helper.c
new file mode 100644
index 0000000..514b039
--- /dev/null
+++ b/target-m68k/helper.c
@@ -0,0 +1,924 @@
+/*
+ * m68k op helpers
+ *
+ * Copyright (c) 2006-2007 CodeSourcery
+ * Written by Paul Brook
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "qemu-common.h"
+#include "gdbstub.h"
+
+#include "helpers.h"
+
+#define SIGNBIT (1u << 31)
+
+enum m68k_cpuid {
+ M68K_CPUID_M5206,
+ M68K_CPUID_M5208,
+ M68K_CPUID_CFV4E,
+ M68K_CPUID_ANY,
+};
+
+typedef struct m68k_def_t m68k_def_t;
+
+struct m68k_def_t {
+ const char * name;
+ enum m68k_cpuid id;
+};
+
+static m68k_def_t m68k_cpu_defs[] = {
+ {"m5206", M68K_CPUID_M5206},
+ {"m5208", M68K_CPUID_M5208},
+ {"cfv4e", M68K_CPUID_CFV4E},
+ {"any", M68K_CPUID_ANY},
+ {NULL, 0},
+};
+
+void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ unsigned int i;
+
+ for (i = 0; m68k_cpu_defs[i].name; i++) {
+ (*cpu_fprintf)(f, "%s\n", m68k_cpu_defs[i].name);
+ }
+}
+
+static int fpu_gdb_get_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ stfq_p(mem_buf, env->fregs[n]);
+ return 8;
+ }
+ if (n < 11) {
+ /* FP control registers (not implemented) */
+ memset(mem_buf, 0, 4);
+ return 4;
+ }
+ return 0;
+}
+
+static int fpu_gdb_set_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 8) {
+ env->fregs[n] = ldfq_p(mem_buf);
+ return 8;
+ }
+ if (n < 11) {
+ /* FP control registers (not implemented) */
+ return 4;
+ }
+ return 0;
+}
+
+static void m68k_set_feature(CPUM68KState *env, int feature)
+{
+ env->features |= (1u << feature);
+}
+
+static int cpu_m68k_set_model(CPUM68KState *env, const char *name)
+{
+ m68k_def_t *def;
+
+ for (def = m68k_cpu_defs; def->name; def++) {
+ if (strcmp(def->name, name) == 0)
+ break;
+ }
+ if (!def->name)
+ return -1;
+
+ switch (def->id) {
+ case M68K_CPUID_M5206:
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
+ break;
+ case M68K_CPUID_M5208:
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC);
+ m68k_set_feature(env, M68K_FEATURE_BRAL);
+ m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
+ m68k_set_feature(env, M68K_FEATURE_USP);
+ break;
+ case M68K_CPUID_CFV4E:
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_B);
+ m68k_set_feature(env, M68K_FEATURE_BRAL);
+ m68k_set_feature(env, M68K_FEATURE_CF_FPU);
+ m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
+ m68k_set_feature(env, M68K_FEATURE_USP);
+ break;
+ case M68K_CPUID_ANY:
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_B);
+ m68k_set_feature(env, M68K_FEATURE_CF_ISA_APLUSC);
+ m68k_set_feature(env, M68K_FEATURE_BRAL);
+ m68k_set_feature(env, M68K_FEATURE_CF_FPU);
+ /* MAC and EMAC are mututally exclusive, so pick EMAC.
+ It's mostly backwards compatible. */
+ m68k_set_feature(env, M68K_FEATURE_CF_EMAC);
+ m68k_set_feature(env, M68K_FEATURE_CF_EMAC_B);
+ m68k_set_feature(env, M68K_FEATURE_USP);
+ m68k_set_feature(env, M68K_FEATURE_EXT_FULL);
+ m68k_set_feature(env, M68K_FEATURE_WORD_INDEX);
+ break;
+ }
+
+ register_m68k_insns(env);
+ if (m68k_feature (env, M68K_FEATURE_CF_FPU)) {
+ gdb_register_coprocessor(env, fpu_gdb_get_reg, fpu_gdb_set_reg,
+ 11, "cf-fp.xml", 18);
+ }
+ /* TODO: Add [E]MAC registers. */
+ return 0;
+}
+
+void cpu_reset(CPUM68KState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ memset(env, 0, offsetof(CPUM68KState, breakpoints));
+#if !defined (CONFIG_USER_ONLY)
+ env->sr = 0x2700;
+#endif
+ m68k_switch_sp(env);
+ /* ??? FP regs should be initialized to NaN. */
+ env->cc_op = CC_OP_FLAGS;
+ /* TODO: We should set PC from the interrupt vector. */
+ env->pc = 0;
+ tlb_flush(env, 1);
+}
+
+CPUM68KState *cpu_m68k_init(const char *cpu_model)
+{
+ CPUM68KState *env;
+ static int inited;
+
+ env = qemu_mallocz(sizeof(CPUM68KState));
+ cpu_exec_init(env);
+ if (!inited) {
+ inited = 1;
+ m68k_tcg_init();
+ }
+
+ env->cpu_model_str = cpu_model;
+
+ if (cpu_m68k_set_model(env, cpu_model) < 0) {
+ cpu_m68k_close(env);
+ return NULL;
+ }
+
+ cpu_reset(env);
+ qemu_init_vcpu(env);
+ return env;
+}
+
+void cpu_m68k_close(CPUM68KState *env)
+{
+ qemu_free(env);
+}
+
+void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op)
+{
+ int flags;
+ uint32_t src;
+ uint32_t dest;
+ uint32_t tmp;
+
+#define HIGHBIT 0x80000000u
+
+#define SET_NZ(x) do { \
+ if ((x) == 0) \
+ flags |= CCF_Z; \
+ else if ((int32_t)(x) < 0) \
+ flags |= CCF_N; \
+ } while (0)
+
+#define SET_FLAGS_SUB(type, utype) do { \
+ SET_NZ((type)dest); \
+ tmp = dest + src; \
+ if ((utype) tmp < (utype) src) \
+ flags |= CCF_C; \
+ if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \
+ flags |= CCF_V; \
+ } while (0)
+
+ flags = 0;
+ src = env->cc_src;
+ dest = env->cc_dest;
+ switch (cc_op) {
+ case CC_OP_FLAGS:
+ flags = dest;
+ break;
+ case CC_OP_LOGIC:
+ SET_NZ(dest);
+ break;
+ case CC_OP_ADD:
+ SET_NZ(dest);
+ if (dest < src)
+ flags |= CCF_C;
+ tmp = dest - src;
+ if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
+ flags |= CCF_V;
+ break;
+ case CC_OP_SUB:
+ SET_FLAGS_SUB(int32_t, uint32_t);
+ break;
+ case CC_OP_CMPB:
+ SET_FLAGS_SUB(int8_t, uint8_t);
+ break;
+ case CC_OP_CMPW:
+ SET_FLAGS_SUB(int16_t, uint16_t);
+ break;
+ case CC_OP_ADDX:
+ SET_NZ(dest);
+ if (dest <= src)
+ flags |= CCF_C;
+ tmp = dest - src - 1;
+ if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
+ flags |= CCF_V;
+ break;
+ case CC_OP_SUBX:
+ SET_NZ(dest);
+ tmp = dest + src + 1;
+ if (tmp <= src)
+ flags |= CCF_C;
+ if (HIGHBIT & (tmp ^ dest) & (tmp ^ src))
+ flags |= CCF_V;
+ break;
+ case CC_OP_SHIFT:
+ SET_NZ(dest);
+ if (src)
+ flags |= CCF_C;
+ break;
+ default:
+ cpu_abort(env, "Bad CC_OP %d", cc_op);
+ }
+ env->cc_op = CC_OP_FLAGS;
+ env->cc_dest = flags;
+}
+
+void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val)
+{
+ switch (reg) {
+ case 0x02: /* CACR */
+ env->cacr = val;
+ m68k_switch_sp(env);
+ break;
+ case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */
+ /* TODO: Implement Access Control Registers. */
+ break;
+ case 0x801: /* VBR */
+ env->vbr = val;
+ break;
+ /* TODO: Implement control registers. */
+ default:
+ cpu_abort(env, "Unimplemented control register write 0x%x = 0x%x\n",
+ reg, val);
+ }
+}
+
+void HELPER(set_macsr)(CPUM68KState *env, uint32_t val)
+{
+ uint32_t acc;
+ int8_t exthigh;
+ uint8_t extlow;
+ uint64_t regval;
+ int i;
+ if ((env->macsr ^ val) & (MACSR_FI | MACSR_SU)) {
+ for (i = 0; i < 4; i++) {
+ regval = env->macc[i];
+ exthigh = regval >> 40;
+ if (env->macsr & MACSR_FI) {
+ acc = regval >> 8;
+ extlow = regval;
+ } else {
+ acc = regval;
+ extlow = regval >> 32;
+ }
+ if (env->macsr & MACSR_FI) {
+ regval = (((uint64_t)acc) << 8) | extlow;
+ regval |= ((int64_t)exthigh) << 40;
+ } else if (env->macsr & MACSR_SU) {
+ regval = acc | (((int64_t)extlow) << 32);
+ regval |= ((int64_t)exthigh) << 40;
+ } else {
+ regval = acc | (((uint64_t)extlow) << 32);
+ regval |= ((uint64_t)(uint8_t)exthigh) << 40;
+ }
+ env->macc[i] = regval;
+ }
+ }
+ env->macsr = val;
+}
+
+void m68k_switch_sp(CPUM68KState *env)
+{
+ int new_sp;
+
+ env->sp[env->current_sp] = env->aregs[7];
+ new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP)
+ ? M68K_SSP : M68K_USP;
+ env->aregs[7] = env->sp[new_sp];
+ env->current_sp = new_sp;
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ env->exception_index = EXCP_ACCESS;
+ env->mmu.ar = address;
+ return 1;
+}
+
+#else
+
+/* MMU */
+
+/* TODO: This will need fixing once the MMU is implemented. */
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+
+int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ int prot;
+
+ address &= TARGET_PAGE_MASK;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
+ return 0;
+}
+
+/* Notify CPU of a pending interrupt. Prioritization and vectoring should
+ be handled by the interrupt controller. Real hardware only requests
+ the vector when the interrupt is acknowledged by the CPU. For
+ simplicitly we calculate it when the interrupt is signalled. */
+void m68k_set_irq_level(CPUM68KState *env, int level, uint8_t vector)
+{
+ env->pending_level = level;
+ env->pending_vector = vector;
+ if (level)
+ cpu_interrupt(env, CPU_INTERRUPT_HARD);
+ else
+ cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+}
+
+#endif
+
+uint32_t HELPER(bitrev)(uint32_t x)
+{
+ x = ((x >> 1) & 0x55555555u) | ((x << 1) & 0xaaaaaaaau);
+ x = ((x >> 2) & 0x33333333u) | ((x << 2) & 0xccccccccu);
+ x = ((x >> 4) & 0x0f0f0f0fu) | ((x << 4) & 0xf0f0f0f0u);
+ return bswap32(x);
+}
+
+uint32_t HELPER(ff1)(uint32_t x)
+{
+ int n;
+ for (n = 32; x; n--)
+ x >>= 1;
+ return n;
+}
+
+uint32_t HELPER(sats)(uint32_t val, uint32_t ccr)
+{
+ /* The result has the opposite sign to the original value. */
+ if (ccr & CCF_V)
+ val = (((int32_t)val) >> 31) ^ SIGNBIT;
+ return val;
+}
+
+uint32_t HELPER(subx_cc)(CPUState *env, uint32_t op1, uint32_t op2)
+{
+ uint32_t res;
+ uint32_t old_flags;
+
+ old_flags = env->cc_dest;
+ if (env->cc_x) {
+ env->cc_x = (op1 <= op2);
+ env->cc_op = CC_OP_SUBX;
+ res = op1 - (op2 + 1);
+ } else {
+ env->cc_x = (op1 < op2);
+ env->cc_op = CC_OP_SUB;
+ res = op1 - op2;
+ }
+ env->cc_dest = res;
+ env->cc_src = op2;
+ cpu_m68k_flush_flags(env, env->cc_op);
+ /* !Z is sticky. */
+ env->cc_dest &= (old_flags | ~CCF_Z);
+ return res;
+}
+
+uint32_t HELPER(addx_cc)(CPUState *env, uint32_t op1, uint32_t op2)
+{
+ uint32_t res;
+ uint32_t old_flags;
+
+ old_flags = env->cc_dest;
+ if (env->cc_x) {
+ res = op1 + op2 + 1;
+ env->cc_x = (res <= op2);
+ env->cc_op = CC_OP_ADDX;
+ } else {
+ res = op1 + op2;
+ env->cc_x = (res < op2);
+ env->cc_op = CC_OP_ADD;
+ }
+ env->cc_dest = res;
+ env->cc_src = op2;
+ cpu_m68k_flush_flags(env, env->cc_op);
+ /* !Z is sticky. */
+ env->cc_dest &= (old_flags | ~CCF_Z);
+ return res;
+}
+
+uint32_t HELPER(xflag_lt)(uint32_t a, uint32_t b)
+{
+ return a < b;
+}
+
+void HELPER(set_sr)(CPUState *env, uint32_t val)
+{
+ env->sr = val & 0xffff;
+ m68k_switch_sp(env);
+}
+
+uint32_t HELPER(shl_cc)(CPUState *env, uint32_t val, uint32_t shift)
+{
+ uint32_t result;
+ uint32_t cf;
+
+ shift &= 63;
+ if (shift == 0) {
+ result = val;
+ cf = env->cc_src & CCF_C;
+ } else if (shift < 32) {
+ result = val << shift;
+ cf = (val >> (32 - shift)) & 1;
+ } else if (shift == 32) {
+ result = 0;
+ cf = val & 1;
+ } else /* shift > 32 */ {
+ result = 0;
+ cf = 0;
+ }
+ env->cc_src = cf;
+ env->cc_x = (cf != 0);
+ env->cc_dest = result;
+ return result;
+}
+
+uint32_t HELPER(shr_cc)(CPUState *env, uint32_t val, uint32_t shift)
+{
+ uint32_t result;
+ uint32_t cf;
+
+ shift &= 63;
+ if (shift == 0) {
+ result = val;
+ cf = env->cc_src & CCF_C;
+ } else if (shift < 32) {
+ result = val >> shift;
+ cf = (val >> (shift - 1)) & 1;
+ } else if (shift == 32) {
+ result = 0;
+ cf = val >> 31;
+ } else /* shift > 32 */ {
+ result = 0;
+ cf = 0;
+ }
+ env->cc_src = cf;
+ env->cc_x = (cf != 0);
+ env->cc_dest = result;
+ return result;
+}
+
+uint32_t HELPER(sar_cc)(CPUState *env, uint32_t val, uint32_t shift)
+{
+ uint32_t result;
+ uint32_t cf;
+
+ shift &= 63;
+ if (shift == 0) {
+ result = val;
+ cf = (env->cc_src & CCF_C) != 0;
+ } else if (shift < 32) {
+ result = (int32_t)val >> shift;
+ cf = (val >> (shift - 1)) & 1;
+ } else /* shift >= 32 */ {
+ result = (int32_t)val >> 31;
+ cf = val >> 31;
+ }
+ env->cc_src = cf;
+ env->cc_x = cf;
+ env->cc_dest = result;
+ return result;
+}
+
+/* FPU helpers. */
+uint32_t HELPER(f64_to_i32)(CPUState *env, float64 val)
+{
+ return float64_to_int32(val, &env->fp_status);
+}
+
+float32 HELPER(f64_to_f32)(CPUState *env, float64 val)
+{
+ return float64_to_float32(val, &env->fp_status);
+}
+
+float64 HELPER(i32_to_f64)(CPUState *env, uint32_t val)
+{
+ return int32_to_float64(val, &env->fp_status);
+}
+
+float64 HELPER(f32_to_f64)(CPUState *env, float32 val)
+{
+ return float32_to_float64(val, &env->fp_status);
+}
+
+float64 HELPER(iround_f64)(CPUState *env, float64 val)
+{
+ return float64_round_to_int(val, &env->fp_status);
+}
+
+float64 HELPER(itrunc_f64)(CPUState *env, float64 val)
+{
+ return float64_trunc_to_int(val, &env->fp_status);
+}
+
+float64 HELPER(sqrt_f64)(CPUState *env, float64 val)
+{
+ return float64_sqrt(val, &env->fp_status);
+}
+
+float64 HELPER(abs_f64)(float64 val)
+{
+ return float64_abs(val);
+}
+
+float64 HELPER(chs_f64)(float64 val)
+{
+ return float64_chs(val);
+}
+
+float64 HELPER(add_f64)(CPUState *env, float64 a, float64 b)
+{
+ return float64_add(a, b, &env->fp_status);
+}
+
+float64 HELPER(sub_f64)(CPUState *env, float64 a, float64 b)
+{
+ return float64_sub(a, b, &env->fp_status);
+}
+
+float64 HELPER(mul_f64)(CPUState *env, float64 a, float64 b)
+{
+ return float64_mul(a, b, &env->fp_status);
+}
+
+float64 HELPER(div_f64)(CPUState *env, float64 a, float64 b)
+{
+ return float64_div(a, b, &env->fp_status);
+}
+
+float64 HELPER(sub_cmp_f64)(CPUState *env, float64 a, float64 b)
+{
+ /* ??? This may incorrectly raise exceptions. */
+ /* ??? Should flush denormals to zero. */
+ float64 res;
+ res = float64_sub(a, b, &env->fp_status);
+ if (float64_is_quiet_nan(res)) {
+ /* +/-inf compares equal against itself, but sub returns nan. */
+ if (!float64_is_quiet_nan(a)
+ && !float64_is_quiet_nan(b)) {
+ res = float64_zero;
+ if (float64_lt_quiet(a, res, &env->fp_status))
+ res = float64_chs(res);
+ }
+ }
+ return res;
+}
+
+uint32_t HELPER(compare_f64)(CPUState *env, float64 val)
+{
+ return float64_compare_quiet(val, float64_zero, &env->fp_status);
+}
+
+/* MAC unit. */
+/* FIXME: The MAC unit implementation is a bit of a mess. Some helpers
+ take values, others take register numbers and manipulate the contents
+ in-place. */
+void HELPER(mac_move)(CPUState *env, uint32_t dest, uint32_t src)
+{
+ uint32_t mask;
+ env->macc[dest] = env->macc[src];
+ mask = MACSR_PAV0 << dest;
+ if (env->macsr & (MACSR_PAV0 << src))
+ env->macsr |= mask;
+ else
+ env->macsr &= ~mask;
+}
+
+uint64_t HELPER(macmuls)(CPUState *env, uint32_t op1, uint32_t op2)
+{
+ int64_t product;
+ int64_t res;
+
+ product = (uint64_t)op1 * op2;
+ res = (product << 24) >> 24;
+ if (res != product) {
+ env->macsr |= MACSR_V;
+ if (env->macsr & MACSR_OMC) {
+ /* Make sure the accumulate operation overflows. */
+ if (product < 0)
+ res = ~(1ll << 50);
+ else
+ res = 1ll << 50;
+ }
+ }
+ return res;
+}
+
+uint64_t HELPER(macmulu)(CPUState *env, uint32_t op1, uint32_t op2)
+{
+ uint64_t product;
+
+ product = (uint64_t)op1 * op2;
+ if (product & (0xffffffull << 40)) {
+ env->macsr |= MACSR_V;
+ if (env->macsr & MACSR_OMC) {
+ /* Make sure the accumulate operation overflows. */
+ product = 1ll << 50;
+ } else {
+ product &= ((1ull << 40) - 1);
+ }
+ }
+ return product;
+}
+
+uint64_t HELPER(macmulf)(CPUState *env, uint32_t op1, uint32_t op2)
+{
+ uint64_t product;
+ uint32_t remainder;
+
+ product = (uint64_t)op1 * op2;
+ if (env->macsr & MACSR_RT) {
+ remainder = product & 0xffffff;
+ product >>= 24;
+ if (remainder > 0x800000)
+ product++;
+ else if (remainder == 0x800000)
+ product += (product & 1);
+ } else {
+ product >>= 24;
+ }
+ return product;
+}
+
+void HELPER(macsats)(CPUState *env, uint32_t acc)
+{
+ int64_t tmp;
+ int64_t result;
+ tmp = env->macc[acc];
+ result = ((tmp << 16) >> 16);
+ if (result != tmp) {
+ env->macsr |= MACSR_V;
+ }
+ if (env->macsr & MACSR_V) {
+ env->macsr |= MACSR_PAV0 << acc;
+ if (env->macsr & MACSR_OMC) {
+ /* The result is saturated to 32 bits, despite overflow occuring
+ at 48 bits. Seems weird, but that's what the hardware docs
+ say. */
+ result = (result >> 63) ^ 0x7fffffff;
+ }
+ }
+ env->macc[acc] = result;
+}
+
+void HELPER(macsatu)(CPUState *env, uint32_t acc)
+{
+ uint64_t val;
+
+ val = env->macc[acc];
+ if (val & (0xffffull << 48)) {
+ env->macsr |= MACSR_V;
+ }
+ if (env->macsr & MACSR_V) {
+ env->macsr |= MACSR_PAV0 << acc;
+ if (env->macsr & MACSR_OMC) {
+ if (val > (1ull << 53))
+ val = 0;
+ else
+ val = (1ull << 48) - 1;
+ } else {
+ val &= ((1ull << 48) - 1);
+ }
+ }
+ env->macc[acc] = val;
+}
+
+void HELPER(macsatf)(CPUState *env, uint32_t acc)
+{
+ int64_t sum;
+ int64_t result;
+
+ sum = env->macc[acc];
+ result = (sum << 16) >> 16;
+ if (result != sum) {
+ env->macsr |= MACSR_V;
+ }
+ if (env->macsr & MACSR_V) {
+ env->macsr |= MACSR_PAV0 << acc;
+ if (env->macsr & MACSR_OMC) {
+ result = (result >> 63) ^ 0x7fffffffffffll;
+ }
+ }
+ env->macc[acc] = result;
+}
+
+void HELPER(mac_set_flags)(CPUState *env, uint32_t acc)
+{
+ uint64_t val;
+ val = env->macc[acc];
+ if (val == 0) {
+ env->macsr |= MACSR_Z;
+ } else if (val & (1ull << 47)) {
+ env->macsr |= MACSR_N;
+ }
+ if (env->macsr & (MACSR_PAV0 << acc)) {
+ env->macsr |= MACSR_V;
+ }
+ if (env->macsr & MACSR_FI) {
+ val = ((int64_t)val) >> 40;
+ if (val != 0 && val != -1)
+ env->macsr |= MACSR_EV;
+ } else if (env->macsr & MACSR_SU) {
+ val = ((int64_t)val) >> 32;
+ if (val != 0 && val != -1)
+ env->macsr |= MACSR_EV;
+ } else {
+ if ((val >> 32) != 0)
+ env->macsr |= MACSR_EV;
+ }
+}
+
+void HELPER(flush_flags)(CPUState *env, uint32_t cc_op)
+{
+ cpu_m68k_flush_flags(env, cc_op);
+}
+
+uint32_t HELPER(get_macf)(CPUState *env, uint64_t val)
+{
+ int rem;
+ uint32_t result;
+
+ if (env->macsr & MACSR_SU) {
+ /* 16-bit rounding. */
+ rem = val & 0xffffff;
+ val = (val >> 24) & 0xffffu;
+ if (rem > 0x800000)
+ val++;
+ else if (rem == 0x800000)
+ val += (val & 1);
+ } else if (env->macsr & MACSR_RT) {
+ /* 32-bit rounding. */
+ rem = val & 0xff;
+ val >>= 8;
+ if (rem > 0x80)
+ val++;
+ else if (rem == 0x80)
+ val += (val & 1);
+ } else {
+ /* No rounding. */
+ val >>= 8;
+ }
+ if (env->macsr & MACSR_OMC) {
+ /* Saturate. */
+ if (env->macsr & MACSR_SU) {
+ if (val != (uint16_t) val) {
+ result = ((val >> 63) ^ 0x7fff) & 0xffff;
+ } else {
+ result = val & 0xffff;
+ }
+ } else {
+ if (val != (uint32_t)val) {
+ result = ((uint32_t)(val >> 63) & 0x7fffffff);
+ } else {
+ result = (uint32_t)val;
+ }
+ }
+ } else {
+ /* No saturation. */
+ if (env->macsr & MACSR_SU) {
+ result = val & 0xffff;
+ } else {
+ result = (uint32_t)val;
+ }
+ }
+ return result;
+}
+
+uint32_t HELPER(get_macs)(uint64_t val)
+{
+ if (val == (int32_t)val) {
+ return (int32_t)val;
+ } else {
+ return (val >> 61) ^ ~SIGNBIT;
+ }
+}
+
+uint32_t HELPER(get_macu)(uint64_t val)
+{
+ if ((val >> 32) == 0) {
+ return (uint32_t)val;
+ } else {
+ return 0xffffffffu;
+ }
+}
+
+uint32_t HELPER(get_mac_extf)(CPUState *env, uint32_t acc)
+{
+ uint32_t val;
+ val = env->macc[acc] & 0x00ff;
+ val = (env->macc[acc] >> 32) & 0xff00;
+ val |= (env->macc[acc + 1] << 16) & 0x00ff0000;
+ val |= (env->macc[acc + 1] >> 16) & 0xff000000;
+ return val;
+}
+
+uint32_t HELPER(get_mac_exti)(CPUState *env, uint32_t acc)
+{
+ uint32_t val;
+ val = (env->macc[acc] >> 32) & 0xffff;
+ val |= (env->macc[acc + 1] >> 16) & 0xffff0000;
+ return val;
+}
+
+void HELPER(set_mac_extf)(CPUState *env, uint32_t val, uint32_t acc)
+{
+ int64_t res;
+ int32_t tmp;
+ res = env->macc[acc] & 0xffffffff00ull;
+ tmp = (int16_t)(val & 0xff00);
+ res |= ((int64_t)tmp) << 32;
+ res |= val & 0xff;
+ env->macc[acc] = res;
+ res = env->macc[acc + 1] & 0xffffffff00ull;
+ tmp = (val & 0xff000000);
+ res |= ((int64_t)tmp) << 16;
+ res |= (val >> 16) & 0xff;
+ env->macc[acc + 1] = res;
+}
+
+void HELPER(set_mac_exts)(CPUState *env, uint32_t val, uint32_t acc)
+{
+ int64_t res;
+ int32_t tmp;
+ res = (uint32_t)env->macc[acc];
+ tmp = (int16_t)val;
+ res |= ((int64_t)tmp) << 32;
+ env->macc[acc] = res;
+ res = (uint32_t)env->macc[acc + 1];
+ tmp = val & 0xffff0000;
+ res |= (int64_t)tmp << 16;
+ env->macc[acc + 1] = res;
+}
+
+void HELPER(set_mac_extu)(CPUState *env, uint32_t val, uint32_t acc)
+{
+ uint64_t res;
+ res = (uint32_t)env->macc[acc];
+ res |= ((uint64_t)(val & 0xffff)) << 32;
+ env->macc[acc] = res;
+ res = (uint32_t)env->macc[acc + 1];
+ res |= (uint64_t)(val & 0xffff0000) << 16;
+ env->macc[acc + 1] = res;
+}
diff --git a/target-m68k/helpers.h b/target-m68k/helpers.h
new file mode 100644
index 0000000..cb8a0c7
--- /dev/null
+++ b/target-m68k/helpers.h
@@ -0,0 +1,54 @@
+#include "def-helper.h"
+
+DEF_HELPER_1(bitrev, i32, i32)
+DEF_HELPER_1(ff1, i32, i32)
+DEF_HELPER_2(sats, i32, i32, i32)
+DEF_HELPER_2(divu, void, env, i32)
+DEF_HELPER_2(divs, void, env, i32)
+DEF_HELPER_3(addx_cc, i32, env, i32, i32)
+DEF_HELPER_3(subx_cc, i32, env, i32, i32)
+DEF_HELPER_3(shl_cc, i32, env, i32, i32)
+DEF_HELPER_3(shr_cc, i32, env, i32, i32)
+DEF_HELPER_3(sar_cc, i32, env, i32, i32)
+DEF_HELPER_2(xflag_lt, i32, i32, i32)
+DEF_HELPER_2(set_sr, void, env, i32)
+DEF_HELPER_3(movec, void, env, i32, i32)
+
+DEF_HELPER_2(f64_to_i32, f32, env, f64)
+DEF_HELPER_2(f64_to_f32, f32, env, f64)
+DEF_HELPER_2(i32_to_f64, f64, env, i32)
+DEF_HELPER_2(f32_to_f64, f64, env, f32)
+DEF_HELPER_2(iround_f64, f64, env, f64)
+DEF_HELPER_2(itrunc_f64, f64, env, f64)
+DEF_HELPER_2(sqrt_f64, f64, env, f64)
+DEF_HELPER_1(abs_f64, f64, f64)
+DEF_HELPER_1(chs_f64, f64, f64)
+DEF_HELPER_3(add_f64, f64, env, f64, f64)
+DEF_HELPER_3(sub_f64, f64, env, f64, f64)
+DEF_HELPER_3(mul_f64, f64, env, f64, f64)
+DEF_HELPER_3(div_f64, f64, env, f64, f64)
+DEF_HELPER_3(sub_cmp_f64, f64, env, f64, f64)
+DEF_HELPER_2(compare_f64, i32, env, f64)
+
+DEF_HELPER_3(mac_move, void, env, i32, i32)
+DEF_HELPER_3(macmulf, i64, env, i32, i32)
+DEF_HELPER_3(macmuls, i64, env, i32, i32)
+DEF_HELPER_3(macmulu, i64, env, i32, i32)
+DEF_HELPER_2(macsats, void, env, i32)
+DEF_HELPER_2(macsatu, void, env, i32)
+DEF_HELPER_2(macsatf, void, env, i32)
+DEF_HELPER_2(mac_set_flags, void, env, i32)
+DEF_HELPER_2(set_macsr, void, env, i32)
+DEF_HELPER_2(get_macf, i32, env, i64)
+DEF_HELPER_1(get_macs, i32, i64)
+DEF_HELPER_1(get_macu, i32, i64)
+DEF_HELPER_2(get_mac_extf, i32, env, i32)
+DEF_HELPER_2(get_mac_exti, i32, env, i32)
+DEF_HELPER_3(set_mac_extf, void, env, i32, i32)
+DEF_HELPER_3(set_mac_exts, void, env, i32, i32)
+DEF_HELPER_3(set_mac_extu, void, env, i32, i32)
+
+DEF_HELPER_2(flush_flags, void, env, i32)
+DEF_HELPER_1(raise_exception, void, i32)
+
+#include "def-helper.h"
diff --git a/target-m68k/m68k-qreg.h b/target-m68k/m68k-qreg.h
new file mode 100644
index 0000000..c224d5e
--- /dev/null
+++ b/target-m68k/m68k-qreg.h
@@ -0,0 +1,11 @@
+enum {
+#define DEFO32(name, offset) QREG_##name,
+#define DEFR(name, reg, mode) QREG_##name,
+#define DEFF64(name, offset) QREG_##name,
+ QREG_NULL,
+#include "qregs.def"
+ TARGET_NUM_QREGS = 0x100
+#undef DEFO32
+#undef DEFR
+#undef DEFF64
+};
diff --git a/target-m68k/machine.c b/target-m68k/machine.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/target-m68k/machine.c
diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c
new file mode 100644
index 0000000..0711107
--- /dev/null
+++ b/target-m68k/op_helper.c
@@ -0,0 +1,225 @@
+/*
+ * M68K helper routines
+ *
+ * Copyright (c) 2007 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "exec.h"
+#include "helpers.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt(int is_hw)
+{
+ env->exception_index = -1;
+}
+
+#else
+
+extern int semihosting_enabled;
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* Try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_m68k_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, NULL);
+ }
+ }
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+static void do_rte(void)
+{
+ uint32_t sp;
+ uint32_t fmt;
+
+ sp = env->aregs[7];
+ fmt = ldl_kernel(sp);
+ env->pc = ldl_kernel(sp + 4);
+ sp |= (fmt >> 28) & 3;
+ env->sr = fmt & 0xffff;
+ m68k_switch_sp(env);
+ env->aregs[7] = sp + 8;
+}
+
+void do_interrupt(int is_hw)
+{
+ uint32_t sp;
+ uint32_t fmt;
+ uint32_t retaddr;
+ uint32_t vector;
+
+ fmt = 0;
+ retaddr = env->pc;
+
+ if (!is_hw) {
+ switch (env->exception_index) {
+ case EXCP_RTE:
+ /* Return from an exception. */
+ do_rte();
+ return;
+ case EXCP_HALT_INSN:
+ if (semihosting_enabled
+ && (env->sr & SR_S) != 0
+ && (env->pc & 3) == 0
+ && lduw_code(env->pc - 4) == 0x4e71
+ && ldl_code(env->pc) == 0x4e7bf000) {
+ env->pc += 4;
+ do_m68k_semihosting(env, env->dregs[0]);
+ return;
+ }
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ cpu_loop_exit();
+ return;
+ }
+ if (env->exception_index >= EXCP_TRAP0
+ && env->exception_index <= EXCP_TRAP15) {
+ /* Move the PC after the trap instruction. */
+ retaddr += 2;
+ }
+ }
+
+ vector = env->exception_index << 2;
+
+ sp = env->aregs[7];
+
+ fmt |= 0x40000000;
+ fmt |= (sp & 3) << 28;
+ fmt |= vector << 16;
+ fmt |= env->sr;
+
+ env->sr |= SR_S;
+ if (is_hw) {
+ env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
+ env->sr &= ~SR_M;
+ }
+ m68k_switch_sp(env);
+
+ /* ??? This could cause MMU faults. */
+ sp &= ~3;
+ sp -= 4;
+ stl_kernel(sp, retaddr);
+ sp -= 4;
+ stl_kernel(sp, fmt);
+ env->aregs[7] = sp;
+ /* Jump to vector. */
+ env->pc = ldl_kernel(env->vbr + vector);
+}
+
+#endif
+
+static void raise_exception(int tt)
+{
+ env->exception_index = tt;
+ cpu_loop_exit();
+}
+
+void HELPER(raise_exception)(uint32_t tt)
+{
+ raise_exception(tt);
+}
+
+void HELPER(divu)(CPUState *env, uint32_t word)
+{
+ uint32_t num;
+ uint32_t den;
+ uint32_t quot;
+ uint32_t rem;
+ uint32_t flags;
+
+ num = env->div1;
+ den = env->div2;
+ /* ??? This needs to make sure the throwing location is accurate. */
+ if (den == 0)
+ raise_exception(EXCP_DIV0);
+ quot = num / den;
+ rem = num % den;
+ flags = 0;
+ /* Avoid using a PARAM1 of zero. This breaks dyngen because it uses
+ the address of a symbol, and gcc knows symbols can't have address
+ zero. */
+ if (word && quot > 0xffff)
+ flags |= CCF_V;
+ if (quot == 0)
+ flags |= CCF_Z;
+ else if ((int32_t)quot < 0)
+ flags |= CCF_N;
+ env->div1 = quot;
+ env->div2 = rem;
+ env->cc_dest = flags;
+}
+
+void HELPER(divs)(CPUState *env, uint32_t word)
+{
+ int32_t num;
+ int32_t den;
+ int32_t quot;
+ int32_t rem;
+ int32_t flags;
+
+ num = env->div1;
+ den = env->div2;
+ if (den == 0)
+ raise_exception(EXCP_DIV0);
+ quot = num / den;
+ rem = num % den;
+ flags = 0;
+ if (word && quot != (int16_t)quot)
+ flags |= CCF_V;
+ if (quot == 0)
+ flags |= CCF_Z;
+ else if (quot < 0)
+ flags |= CCF_N;
+ env->div1 = quot;
+ env->div2 = rem;
+ env->cc_dest = flags;
+}
diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def
new file mode 100644
index 0000000..49400c4
--- /dev/null
+++ b/target-m68k/qregs.def
@@ -0,0 +1,13 @@
+DEFF64(FP_RESULT, fp_result)
+DEFO32(PC, pc)
+DEFO32(SR, sr)
+DEFO32(CC_OP, cc_op)
+DEFO32(CC_DEST, cc_dest)
+DEFO32(CC_SRC, cc_src)
+DEFO32(CC_X, cc_x)
+DEFO32(DIV1, div1)
+DEFO32(DIV2, div2)
+DEFO32(EXCEPTION, exception_index)
+DEFO32(HALTED, halted)
+DEFO32(MACSR, macsr)
+DEFO32(MAC_MASK, mac_mask)
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
new file mode 100644
index 0000000..6f72a2b
--- /dev/null
+++ b/target-m68k/translate.c
@@ -0,0 +1,3120 @@
+/*
+ * m68k translation
+ *
+ * Copyright (c) 2005-2007 CodeSourcery
+ * Written by Paul Brook
+ *
+ * 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
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-log.h"
+
+#include "helpers.h"
+#define GEN_HELPER 1
+#include "helpers.h"
+
+//#define DEBUG_DISPATCH 1
+
+/* Fake floating point. */
+#define tcg_gen_mov_f64 tcg_gen_mov_i64
+#define tcg_gen_qemu_ldf64 tcg_gen_qemu_ld64
+#define tcg_gen_qemu_stf64 tcg_gen_qemu_st64
+
+#define DEFO32(name, offset) static TCGv QREG_##name;
+#define DEFO64(name, offset) static TCGv_i64 QREG_##name;
+#define DEFF64(name, offset) static TCGv_i64 QREG_##name;
+#include "qregs.def"
+#undef DEFO32
+#undef DEFO64
+#undef DEFF64
+
+static TCGv_ptr cpu_env;
+
+static char cpu_reg_names[3*8*3 + 5*4];
+static TCGv cpu_dregs[8];
+static TCGv cpu_aregs[8];
+static TCGv_i64 cpu_fregs[8];
+static TCGv_i64 cpu_macc[4];
+
+#define DREG(insn, pos) cpu_dregs[((insn) >> (pos)) & 7]
+#define AREG(insn, pos) cpu_aregs[((insn) >> (pos)) & 7]
+#define FREG(insn, pos) cpu_fregs[((insn) >> (pos)) & 7]
+#define MACREG(acc) cpu_macc[acc]
+#define QREG_SP cpu_aregs[7]
+
+static TCGv NULL_QREG;
+#define IS_NULL_QREG(t) (TCGV_EQUAL(t, NULL_QREG))
+/* Used to distinguish stores from bad addressing modes. */
+static TCGv store_dummy;
+
+#include "gen-icount.h"
+
+void m68k_tcg_init(void)
+{
+ char *p;
+ int i;
+
+#define DEFO32(name, offset) QREG_##name = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, offset), #name);
+#define DEFO64(name, offset) QREG_##name = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, offset), #name);
+#define DEFF64(name, offset) DEFO64(name, offset)
+#include "qregs.def"
+#undef DEFO32
+#undef DEFO64
+#undef DEFF64
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ p = cpu_reg_names;
+ for (i = 0; i < 8; i++) {
+ sprintf(p, "D%d", i);
+ cpu_dregs[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUM68KState, dregs[i]), p);
+ p += 3;
+ sprintf(p, "A%d", i);
+ cpu_aregs[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUM68KState, aregs[i]), p);
+ p += 3;
+ sprintf(p, "F%d", i);
+ cpu_fregs[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUM68KState, fregs[i]), p);
+ p += 3;
+ }
+ for (i = 0; i < 4; i++) {
+ sprintf(p, "ACC%d", i);
+ cpu_macc[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUM68KState, macc[i]), p);
+ p += 5;
+ }
+
+ NULL_QREG = tcg_global_mem_new(TCG_AREG0, -4, "NULL");
+ store_dummy = tcg_global_mem_new(TCG_AREG0, -8, "NULL");
+
+#define GEN_HELPER 2
+#include "helpers.h"
+}
+
+static inline void qemu_assert(int cond, const char *msg)
+{
+ if (!cond) {
+ fprintf (stderr, "badness: %s\n", msg);
+ abort();
+ }
+}
+
+/* internal defines */
+typedef struct DisasContext {
+ CPUM68KState *env;
+ target_ulong insn_pc; /* Start of the current instruction. */
+ target_ulong pc;
+ int is_jmp;
+ int cc_op;
+ int user;
+ uint32_t fpcr;
+ struct TranslationBlock *tb;
+ int singlestep_enabled;
+ int is_mem;
+ TCGv_i64 mactmp;
+ int done_mac;
+} DisasContext;
+
+#define DISAS_JUMP_NEXT 4
+
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(s) 1
+#else
+#define IS_USER(s) s->user
+#endif
+
+/* XXX: move that elsewhere */
+/* ??? Fix exceptions. */
+static void *gen_throws_exception;
+#define gen_last_qop NULL
+
+#define OS_BYTE 0
+#define OS_WORD 1
+#define OS_LONG 2
+#define OS_SINGLE 4
+#define OS_DOUBLE 5
+
+typedef void (*disas_proc)(DisasContext *, uint16_t);
+
+#ifdef DEBUG_DISPATCH
+#define DISAS_INSN(name) \
+ static void real_disas_##name (DisasContext *s, uint16_t insn); \
+ static void disas_##name (DisasContext *s, uint16_t insn) { \
+ qemu_log("Dispatch " #name "\n"); \
+ real_disas_##name(s, insn); } \
+ static void real_disas_##name (DisasContext *s, uint16_t insn)
+#else
+#define DISAS_INSN(name) \
+ static void disas_##name (DisasContext *s, uint16_t insn)
+#endif
+
+/* FIXME: Remove this. */
+#define gen_im32(val) tcg_const_i32(val)
+
+/* Generate a load from the specified address. Narrow values are
+ sign extended to full register width. */
+static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
+{
+ TCGv tmp;
+ int index = IS_USER(s);
+ s->is_mem = 1;
+ tmp = tcg_temp_new_i32();
+ switch(opsize) {
+ case OS_BYTE:
+ if (sign)
+ tcg_gen_qemu_ld8s(tmp, addr, index);
+ else
+ tcg_gen_qemu_ld8u(tmp, addr, index);
+ break;
+ case OS_WORD:
+ if (sign)
+ tcg_gen_qemu_ld16s(tmp, addr, index);
+ else
+ tcg_gen_qemu_ld16u(tmp, addr, index);
+ break;
+ case OS_LONG:
+ case OS_SINGLE:
+ tcg_gen_qemu_ld32u(tmp, addr, index);
+ break;
+ default:
+ qemu_assert(0, "bad load size");
+ }
+ gen_throws_exception = gen_last_qop;
+ return tmp;
+}
+
+static inline TCGv_i64 gen_load64(DisasContext * s, TCGv addr)
+{
+ TCGv_i64 tmp;
+ int index = IS_USER(s);
+ s->is_mem = 1;
+ tmp = tcg_temp_new_i64();
+ tcg_gen_qemu_ldf64(tmp, addr, index);
+ gen_throws_exception = gen_last_qop;
+ return tmp;
+}
+
+/* Generate a store. */
+static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val)
+{
+ int index = IS_USER(s);
+ s->is_mem = 1;
+ switch(opsize) {
+ case OS_BYTE:
+ tcg_gen_qemu_st8(val, addr, index);
+ break;
+ case OS_WORD:
+ tcg_gen_qemu_st16(val, addr, index);
+ break;
+ case OS_LONG:
+ case OS_SINGLE:
+ tcg_gen_qemu_st32(val, addr, index);
+ break;
+ default:
+ qemu_assert(0, "bad store size");
+ }
+ gen_throws_exception = gen_last_qop;
+}
+
+static inline void gen_store64(DisasContext *s, TCGv addr, TCGv_i64 val)
+{
+ int index = IS_USER(s);
+ s->is_mem = 1;
+ tcg_gen_qemu_stf64(val, addr, index);
+ gen_throws_exception = gen_last_qop;
+}
+
+typedef enum {
+ EA_STORE,
+ EA_LOADU,
+ EA_LOADS
+} ea_what;
+
+/* Generate an unsigned load if VAL is 0 a signed load if val is -1,
+ otherwise generate a store. */
+static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val,
+ ea_what what)
+{
+ if (what == EA_STORE) {
+ gen_store(s, opsize, addr, val);
+ return store_dummy;
+ } else {
+ return gen_load(s, opsize, addr, what == EA_LOADS);
+ }
+}
+
+/* Read a 32-bit immediate constant. */
+static inline uint32_t read_im32(DisasContext *s)
+{
+ uint32_t im;
+ im = ((uint32_t)lduw_code(s->pc)) << 16;
+ s->pc += 2;
+ im |= lduw_code(s->pc);
+ s->pc += 2;
+ return im;
+}
+
+/* Calculate and address index. */
+static TCGv gen_addr_index(uint16_t ext, TCGv tmp)
+{
+ TCGv add;
+ int scale;
+
+ add = (ext & 0x8000) ? AREG(ext, 12) : DREG(ext, 12);
+ if ((ext & 0x800) == 0) {
+ tcg_gen_ext16s_i32(tmp, add);
+ add = tmp;
+ }
+ scale = (ext >> 9) & 3;
+ if (scale != 0) {
+ tcg_gen_shli_i32(tmp, add, scale);
+ add = tmp;
+ }
+ return add;
+}
+
+/* Handle a base + index + displacement effective addresss.
+ A NULL_QREG base means pc-relative. */
+static TCGv gen_lea_indexed(DisasContext *s, int opsize, TCGv base)
+{
+ uint32_t offset;
+ uint16_t ext;
+ TCGv add;
+ TCGv tmp;
+ uint32_t bd, od;
+
+ offset = s->pc;
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+
+ if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX))
+ return NULL_QREG;
+
+ if (ext & 0x100) {
+ /* full extension word format */
+ if (!m68k_feature(s->env, M68K_FEATURE_EXT_FULL))
+ return NULL_QREG;
+
+ if ((ext & 0x30) > 0x10) {
+ /* base displacement */
+ if ((ext & 0x30) == 0x20) {
+ bd = (int16_t)lduw_code(s->pc);
+ s->pc += 2;
+ } else {
+ bd = read_im32(s);
+ }
+ } else {
+ bd = 0;
+ }
+ tmp = tcg_temp_new();
+ if ((ext & 0x44) == 0) {
+ /* pre-index */
+ add = gen_addr_index(ext, tmp);
+ } else {
+ add = NULL_QREG;
+ }
+ if ((ext & 0x80) == 0) {
+ /* base not suppressed */
+ if (IS_NULL_QREG(base)) {
+ base = gen_im32(offset + bd);
+ bd = 0;
+ }
+ if (!IS_NULL_QREG(add)) {
+ tcg_gen_add_i32(tmp, add, base);
+ add = tmp;
+ } else {
+ add = base;
+ }
+ }
+ if (!IS_NULL_QREG(add)) {
+ if (bd != 0) {
+ tcg_gen_addi_i32(tmp, add, bd);
+ add = tmp;
+ }
+ } else {
+ add = gen_im32(bd);
+ }
+ if ((ext & 3) != 0) {
+ /* memory indirect */
+ base = gen_load(s, OS_LONG, add, 0);
+ if ((ext & 0x44) == 4) {
+ add = gen_addr_index(ext, tmp);
+ tcg_gen_add_i32(tmp, add, base);
+ add = tmp;
+ } else {
+ add = base;
+ }
+ if ((ext & 3) > 1) {
+ /* outer displacement */
+ if ((ext & 3) == 2) {
+ od = (int16_t)lduw_code(s->pc);
+ s->pc += 2;
+ } else {
+ od = read_im32(s);
+ }
+ } else {
+ od = 0;
+ }
+ if (od != 0) {
+ tcg_gen_addi_i32(tmp, add, od);
+ add = tmp;
+ }
+ }
+ } else {
+ /* brief extension word format */
+ tmp = tcg_temp_new();
+ add = gen_addr_index(ext, tmp);
+ if (!IS_NULL_QREG(base)) {
+ tcg_gen_add_i32(tmp, add, base);
+ if ((int8_t)ext)
+ tcg_gen_addi_i32(tmp, tmp, (int8_t)ext);
+ } else {
+ tcg_gen_addi_i32(tmp, add, offset + (int8_t)ext);
+ }
+ add = tmp;
+ }
+ return add;
+}
+
+/* Update the CPU env CC_OP state. */
+static inline void gen_flush_cc_op(DisasContext *s)
+{
+ if (s->cc_op != CC_OP_DYNAMIC)
+ tcg_gen_movi_i32(QREG_CC_OP, s->cc_op);
+}
+
+/* Evaluate all the CC flags. */
+static inline void gen_flush_flags(DisasContext *s)
+{
+ if (s->cc_op == CC_OP_FLAGS)
+ return;
+ gen_flush_cc_op(s);
+ gen_helper_flush_flags(cpu_env, QREG_CC_OP);
+ s->cc_op = CC_OP_FLAGS;
+}
+
+static void gen_logic_cc(DisasContext *s, TCGv val)
+{
+ tcg_gen_mov_i32(QREG_CC_DEST, val);
+ s->cc_op = CC_OP_LOGIC;
+}
+
+static void gen_update_cc_add(TCGv dest, TCGv src)
+{
+ tcg_gen_mov_i32(QREG_CC_DEST, dest);
+ tcg_gen_mov_i32(QREG_CC_SRC, src);
+}
+
+static inline int opsize_bytes(int opsize)
+{
+ switch (opsize) {
+ case OS_BYTE: return 1;
+ case OS_WORD: return 2;
+ case OS_LONG: return 4;
+ case OS_SINGLE: return 4;
+ case OS_DOUBLE: return 8;
+ default:
+ qemu_assert(0, "bad operand size");
+ return 0;
+ }
+}
+
+/* Assign value to a register. If the width is less than the register width
+ only the low part of the register is set. */
+static void gen_partset_reg(int opsize, TCGv reg, TCGv val)
+{
+ TCGv tmp;
+ switch (opsize) {
+ case OS_BYTE:
+ tcg_gen_andi_i32(reg, reg, 0xffffff00);
+ tmp = tcg_temp_new();
+ tcg_gen_ext8u_i32(tmp, val);
+ tcg_gen_or_i32(reg, reg, tmp);
+ break;
+ case OS_WORD:
+ tcg_gen_andi_i32(reg, reg, 0xffff0000);
+ tmp = tcg_temp_new();
+ tcg_gen_ext16u_i32(tmp, val);
+ tcg_gen_or_i32(reg, reg, tmp);
+ break;
+ case OS_LONG:
+ case OS_SINGLE:
+ tcg_gen_mov_i32(reg, val);
+ break;
+ default:
+ qemu_assert(0, "Bad operand size");
+ break;
+ }
+}
+
+/* Sign or zero extend a value. */
+static inline TCGv gen_extend(TCGv val, int opsize, int sign)
+{
+ TCGv tmp;
+
+ switch (opsize) {
+ case OS_BYTE:
+ tmp = tcg_temp_new();
+ if (sign)
+ tcg_gen_ext8s_i32(tmp, val);
+ else
+ tcg_gen_ext8u_i32(tmp, val);
+ break;
+ case OS_WORD:
+ tmp = tcg_temp_new();
+ if (sign)
+ tcg_gen_ext16s_i32(tmp, val);
+ else
+ tcg_gen_ext16u_i32(tmp, val);
+ break;
+ case OS_LONG:
+ case OS_SINGLE:
+ tmp = val;
+ break;
+ default:
+ qemu_assert(0, "Bad operand size");
+ }
+ return tmp;
+}
+
+/* Generate code for an "effective address". Does not adjust the base
+ register for autoincrement addressing modes. */
+static TCGv gen_lea(DisasContext *s, uint16_t insn, int opsize)
+{
+ TCGv reg;
+ TCGv tmp;
+ uint16_t ext;
+ uint32_t offset;
+
+ switch ((insn >> 3) & 7) {
+ case 0: /* Data register direct. */
+ case 1: /* Address register direct. */
+ return NULL_QREG;
+ case 2: /* Indirect register */
+ case 3: /* Indirect postincrement. */
+ return AREG(insn, 0);
+ case 4: /* Indirect predecrememnt. */
+ reg = AREG(insn, 0);
+ tmp = tcg_temp_new();
+ tcg_gen_subi_i32(tmp, reg, opsize_bytes(opsize));
+ return tmp;
+ case 5: /* Indirect displacement. */
+ reg = AREG(insn, 0);
+ tmp = tcg_temp_new();
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ tcg_gen_addi_i32(tmp, reg, (int16_t)ext);
+ return tmp;
+ case 6: /* Indirect index + displacement. */
+ reg = AREG(insn, 0);
+ return gen_lea_indexed(s, opsize, reg);
+ case 7: /* Other */
+ switch (insn & 7) {
+ case 0: /* Absolute short. */
+ offset = ldsw_code(s->pc);
+ s->pc += 2;
+ return gen_im32(offset);
+ case 1: /* Absolute long. */
+ offset = read_im32(s);
+ return gen_im32(offset);
+ case 2: /* pc displacement */
+ offset = s->pc;
+ offset += ldsw_code(s->pc);
+ s->pc += 2;
+ return gen_im32(offset);
+ case 3: /* pc index+displacement. */
+ return gen_lea_indexed(s, opsize, NULL_QREG);
+ case 4: /* Immediate. */
+ default:
+ return NULL_QREG;
+ }
+ }
+ /* Should never happen. */
+ return NULL_QREG;
+}
+
+/* Helper function for gen_ea. Reuse the computed address between the
+ for read/write operands. */
+static inline TCGv gen_ea_once(DisasContext *s, uint16_t insn, int opsize,
+ TCGv val, TCGv *addrp, ea_what what)
+{
+ TCGv tmp;
+
+ if (addrp && what == EA_STORE) {
+ tmp = *addrp;
+ } else {
+ tmp = gen_lea(s, insn, opsize);
+ if (IS_NULL_QREG(tmp))
+ return tmp;
+ if (addrp)
+ *addrp = tmp;
+ }
+ return gen_ldst(s, opsize, tmp, val, what);
+}
+
+/* Generate code to load/store a value ito/from an EA. If VAL > 0 this is
+ a write otherwise it is a read (0 == sign extend, -1 == zero extend).
+ ADDRP is non-null for readwrite operands. */
+static TCGv gen_ea(DisasContext *s, uint16_t insn, int opsize, TCGv val,
+ TCGv *addrp, ea_what what)
+{
+ TCGv reg;
+ TCGv result;
+ uint32_t offset;
+
+ switch ((insn >> 3) & 7) {
+ case 0: /* Data register direct. */
+ reg = DREG(insn, 0);
+ if (what == EA_STORE) {
+ gen_partset_reg(opsize, reg, val);
+ return store_dummy;
+ } else {
+ return gen_extend(reg, opsize, what == EA_LOADS);
+ }
+ case 1: /* Address register direct. */
+ reg = AREG(insn, 0);
+ if (what == EA_STORE) {
+ tcg_gen_mov_i32(reg, val);
+ return store_dummy;
+ } else {
+ return gen_extend(reg, opsize, what == EA_LOADS);
+ }
+ case 2: /* Indirect register */
+ reg = AREG(insn, 0);
+ return gen_ldst(s, opsize, reg, val, what);
+ case 3: /* Indirect postincrement. */
+ reg = AREG(insn, 0);
+ result = gen_ldst(s, opsize, reg, val, what);
+ /* ??? This is not exception safe. The instruction may still
+ fault after this point. */
+ if (what == EA_STORE || !addrp)
+ tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
+ return result;
+ case 4: /* Indirect predecrememnt. */
+ {
+ TCGv tmp;
+ if (addrp && what == EA_STORE) {
+ tmp = *addrp;
+ } else {
+ tmp = gen_lea(s, insn, opsize);
+ if (IS_NULL_QREG(tmp))
+ return tmp;
+ if (addrp)
+ *addrp = tmp;
+ }
+ result = gen_ldst(s, opsize, tmp, val, what);
+ /* ??? This is not exception safe. The instruction may still
+ fault after this point. */
+ if (what == EA_STORE || !addrp) {
+ reg = AREG(insn, 0);
+ tcg_gen_mov_i32(reg, tmp);
+ }
+ }
+ return result;
+ case 5: /* Indirect displacement. */
+ case 6: /* Indirect index + displacement. */
+ return gen_ea_once(s, insn, opsize, val, addrp, what);
+ case 7: /* Other */
+ switch (insn & 7) {
+ case 0: /* Absolute short. */
+ case 1: /* Absolute long. */
+ case 2: /* pc displacement */
+ case 3: /* pc index+displacement. */
+ return gen_ea_once(s, insn, opsize, val, addrp, what);
+ case 4: /* Immediate. */
+ /* Sign extend values for consistency. */
+ switch (opsize) {
+ case OS_BYTE:
+ if (what == EA_LOADS)
+ offset = ldsb_code(s->pc + 1);
+ else
+ offset = ldub_code(s->pc + 1);
+ s->pc += 2;
+ break;
+ case OS_WORD:
+ if (what == EA_LOADS)
+ offset = ldsw_code(s->pc);
+ else
+ offset = lduw_code(s->pc);
+ s->pc += 2;
+ break;
+ case OS_LONG:
+ offset = read_im32(s);
+ break;
+ default:
+ qemu_assert(0, "Bad immediate operand");
+ }
+ return tcg_const_i32(offset);
+ default:
+ return NULL_QREG;
+ }
+ }
+ /* Should never happen. */
+ return NULL_QREG;
+}
+
+/* This generates a conditional branch, clobbering all temporaries. */
+static void gen_jmpcc(DisasContext *s, int cond, int l1)
+{
+ TCGv tmp;
+
+ /* TODO: Optimize compare/branch pairs rather than always flushing
+ flag state to CC_OP_FLAGS. */
+ gen_flush_flags(s);
+ switch (cond) {
+ case 0: /* T */
+ tcg_gen_br(l1);
+ break;
+ case 1: /* F */
+ break;
+ case 2: /* HI (!C && !Z) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 3: /* LS (C || Z) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C | CCF_Z);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ case 4: /* CC (!C) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 5: /* CS (C) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_C);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ case 6: /* NE (!Z) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 7: /* EQ (Z) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_Z);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ case 8: /* VC (!V) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 9: /* VS (V) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_V);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ case 10: /* PL (!N) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 11: /* MI (N) */
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ case 12: /* GE (!(N ^ V)) */
+ tmp = tcg_temp_new();
+ assert(CCF_V == (CCF_N >> 2));
+ tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2);
+ tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
+ tcg_gen_andi_i32(tmp, tmp, CCF_V);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 13: /* LT (N ^ V) */
+ tmp = tcg_temp_new();
+ assert(CCF_V == (CCF_N >> 2));
+ tcg_gen_shri_i32(tmp, QREG_CC_DEST, 2);
+ tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
+ tcg_gen_andi_i32(tmp, tmp, CCF_V);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ case 14: /* GT (!(Z || (N ^ V))) */
+ tmp = tcg_temp_new();
+ assert(CCF_V == (CCF_N >> 2));
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
+ tcg_gen_shri_i32(tmp, tmp, 2);
+ tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
+ tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+ break;
+ case 15: /* LE (Z || (N ^ V)) */
+ tmp = tcg_temp_new();
+ assert(CCF_V == (CCF_N >> 2));
+ tcg_gen_andi_i32(tmp, QREG_CC_DEST, CCF_N);
+ tcg_gen_shri_i32(tmp, tmp, 2);
+ tcg_gen_xor_i32(tmp, tmp, QREG_CC_DEST);
+ tcg_gen_andi_i32(tmp, tmp, CCF_V | CCF_Z);
+ tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+ break;
+ default:
+ /* Should ever happen. */
+ abort();
+ }
+}
+
+DISAS_INSN(scc)
+{
+ int l1;
+ int cond;
+ TCGv reg;
+
+ l1 = gen_new_label();
+ cond = (insn >> 8) & 0xf;
+ reg = DREG(insn, 0);
+ tcg_gen_andi_i32(reg, reg, 0xffffff00);
+ /* This is safe because we modify the reg directly, with no other values
+ live. */
+ gen_jmpcc(s, cond ^ 1, l1);
+ tcg_gen_ori_i32(reg, reg, 0xff);
+ gen_set_label(l1);
+}
+
+/* Force a TB lookup after an instruction that changes the CPU state. */
+static void gen_lookup_tb(DisasContext *s)
+{
+ gen_flush_cc_op(s);
+ tcg_gen_movi_i32(QREG_PC, s->pc);
+ s->is_jmp = DISAS_UPDATE;
+}
+
+/* Generate a jump to an immediate address. */
+static void gen_jmp_im(DisasContext *s, uint32_t dest)
+{
+ gen_flush_cc_op(s);
+ tcg_gen_movi_i32(QREG_PC, dest);
+ s->is_jmp = DISAS_JUMP;
+}
+
+/* Generate a jump to the address in qreg DEST. */
+static void gen_jmp(DisasContext *s, TCGv dest)
+{
+ gen_flush_cc_op(s);
+ tcg_gen_mov_i32(QREG_PC, dest);
+ s->is_jmp = DISAS_JUMP;
+}
+
+static void gen_exception(DisasContext *s, uint32_t where, int nr)
+{
+ gen_flush_cc_op(s);
+ gen_jmp_im(s, where);
+ gen_helper_raise_exception(tcg_const_i32(nr));
+}
+
+static inline void gen_addr_fault(DisasContext *s)
+{
+ gen_exception(s, s->insn_pc, EXCP_ADDRESS);
+}
+
+#define SRC_EA(result, opsize, op_sign, addrp) do { \
+ result = gen_ea(s, insn, opsize, NULL_QREG, addrp, op_sign ? EA_LOADS : EA_LOADU); \
+ if (IS_NULL_QREG(result)) { \
+ gen_addr_fault(s); \
+ return; \
+ } \
+ } while (0)
+
+#define DEST_EA(insn, opsize, val, addrp) do { \
+ TCGv ea_result = gen_ea(s, insn, opsize, val, addrp, EA_STORE); \
+ if (IS_NULL_QREG(ea_result)) { \
+ gen_addr_fault(s); \
+ return; \
+ } \
+ } while (0)
+
+/* Generate a jump to an immediate address. */
+static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
+{
+ TranslationBlock *tb;
+
+ tb = s->tb;
+ if (unlikely(s->singlestep_enabled)) {
+ gen_exception(s, dest, EXCP_DEBUG);
+ } else if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) ||
+ (s->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_i32(QREG_PC, dest);
+ tcg_gen_exit_tb((long)tb + n);
+ } else {
+ gen_jmp_im(s, dest);
+ tcg_gen_exit_tb(0);
+ }
+ s->is_jmp = DISAS_TB_JUMP;
+}
+
+DISAS_INSN(undef_mac)
+{
+ gen_exception(s, s->pc - 2, EXCP_LINEA);
+}
+
+DISAS_INSN(undef_fpu)
+{
+ gen_exception(s, s->pc - 2, EXCP_LINEF);
+}
+
+DISAS_INSN(undef)
+{
+ gen_exception(s, s->pc - 2, EXCP_UNSUPPORTED);
+ cpu_abort(cpu_single_env, "Illegal instruction: %04x @ %08x",
+ insn, s->pc - 2);
+}
+
+DISAS_INSN(mulw)
+{
+ TCGv reg;
+ TCGv tmp;
+ TCGv src;
+ int sign;
+
+ sign = (insn & 0x100) != 0;
+ reg = DREG(insn, 9);
+ tmp = tcg_temp_new();
+ if (sign)
+ tcg_gen_ext16s_i32(tmp, reg);
+ else
+ tcg_gen_ext16u_i32(tmp, reg);
+ SRC_EA(src, OS_WORD, sign, NULL);
+ tcg_gen_mul_i32(tmp, tmp, src);
+ tcg_gen_mov_i32(reg, tmp);
+ /* Unlike m68k, coldfire always clears the overflow bit. */
+ gen_logic_cc(s, tmp);
+}
+
+DISAS_INSN(divw)
+{
+ TCGv reg;
+ TCGv tmp;
+ TCGv src;
+ int sign;
+
+ sign = (insn & 0x100) != 0;
+ reg = DREG(insn, 9);
+ if (sign) {
+ tcg_gen_ext16s_i32(QREG_DIV1, reg);
+ } else {
+ tcg_gen_ext16u_i32(QREG_DIV1, reg);
+ }
+ SRC_EA(src, OS_WORD, sign, NULL);
+ tcg_gen_mov_i32(QREG_DIV2, src);
+ if (sign) {
+ gen_helper_divs(cpu_env, tcg_const_i32(1));
+ } else {
+ gen_helper_divu(cpu_env, tcg_const_i32(1));
+ }
+
+ tmp = tcg_temp_new();
+ src = tcg_temp_new();
+ tcg_gen_ext16u_i32(tmp, QREG_DIV1);
+ tcg_gen_shli_i32(src, QREG_DIV2, 16);
+ tcg_gen_or_i32(reg, tmp, src);
+ s->cc_op = CC_OP_FLAGS;
+}
+
+DISAS_INSN(divl)
+{
+ TCGv num;
+ TCGv den;
+ TCGv reg;
+ uint16_t ext;
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ if (ext & 0x87f8) {
+ gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+ return;
+ }
+ num = DREG(ext, 12);
+ reg = DREG(ext, 0);
+ tcg_gen_mov_i32(QREG_DIV1, num);
+ SRC_EA(den, OS_LONG, 0, NULL);
+ tcg_gen_mov_i32(QREG_DIV2, den);
+ if (ext & 0x0800) {
+ gen_helper_divs(cpu_env, tcg_const_i32(0));
+ } else {
+ gen_helper_divu(cpu_env, tcg_const_i32(0));
+ }
+ if ((ext & 7) == ((ext >> 12) & 7)) {
+ /* div */
+ tcg_gen_mov_i32 (reg, QREG_DIV1);
+ } else {
+ /* rem */
+ tcg_gen_mov_i32 (reg, QREG_DIV2);
+ }
+ s->cc_op = CC_OP_FLAGS;
+}
+
+DISAS_INSN(addsub)
+{
+ TCGv reg;
+ TCGv dest;
+ TCGv src;
+ TCGv tmp;
+ TCGv addr;
+ int add;
+
+ add = (insn & 0x4000) != 0;
+ reg = DREG(insn, 9);
+ dest = tcg_temp_new();
+ if (insn & 0x100) {
+ SRC_EA(tmp, OS_LONG, 0, &addr);
+ src = reg;
+ } else {
+ tmp = reg;
+ SRC_EA(src, OS_LONG, 0, NULL);
+ }
+ if (add) {
+ tcg_gen_add_i32(dest, tmp, src);
+ gen_helper_xflag_lt(QREG_CC_X, dest, src);
+ s->cc_op = CC_OP_ADD;
+ } else {
+ gen_helper_xflag_lt(QREG_CC_X, tmp, src);
+ tcg_gen_sub_i32(dest, tmp, src);
+ s->cc_op = CC_OP_SUB;
+ }
+ gen_update_cc_add(dest, src);
+ if (insn & 0x100) {
+ DEST_EA(insn, OS_LONG, dest, &addr);
+ } else {
+ tcg_gen_mov_i32(reg, dest);
+ }
+}
+
+
+/* Reverse the order of the bits in REG. */
+DISAS_INSN(bitrev)
+{
+ TCGv reg;
+ reg = DREG(insn, 0);
+ gen_helper_bitrev(reg, reg);
+}
+
+DISAS_INSN(bitop_reg)
+{
+ int opsize;
+ int op;
+ TCGv src1;
+ TCGv src2;
+ TCGv tmp;
+ TCGv addr;
+ TCGv dest;
+
+ if ((insn & 0x38) != 0)
+ opsize = OS_BYTE;
+ else
+ opsize = OS_LONG;
+ op = (insn >> 6) & 3;
+ SRC_EA(src1, opsize, 0, op ? &addr: NULL);
+ src2 = DREG(insn, 9);
+ dest = tcg_temp_new();
+
+ gen_flush_flags(s);
+ tmp = tcg_temp_new();
+ if (opsize == OS_BYTE)
+ tcg_gen_andi_i32(tmp, src2, 7);
+ else
+ tcg_gen_andi_i32(tmp, src2, 31);
+ src2 = tmp;
+ tmp = tcg_temp_new();
+ tcg_gen_shr_i32(tmp, src1, src2);
+ tcg_gen_andi_i32(tmp, tmp, 1);
+ tcg_gen_shli_i32(tmp, tmp, 2);
+ /* Clear CCF_Z if bit set. */
+ tcg_gen_ori_i32(QREG_CC_DEST, QREG_CC_DEST, CCF_Z);
+ tcg_gen_xor_i32(QREG_CC_DEST, QREG_CC_DEST, tmp);
+
+ tcg_gen_shl_i32(tmp, tcg_const_i32(1), src2);
+ switch (op) {
+ case 1: /* bchg */
+ tcg_gen_xor_i32(dest, src1, tmp);
+ break;
+ case 2: /* bclr */
+ tcg_gen_not_i32(tmp, tmp);
+ tcg_gen_and_i32(dest, src1, tmp);
+ break;
+ case 3: /* bset */
+ tcg_gen_or_i32(dest, src1, tmp);
+ break;
+ default: /* btst */
+ break;
+ }
+ if (op)
+ DEST_EA(insn, opsize, dest, &addr);
+}
+
+DISAS_INSN(sats)
+{
+ TCGv reg;
+ reg = DREG(insn, 0);
+ gen_flush_flags(s);
+ gen_helper_sats(reg, reg, QREG_CC_DEST);
+ gen_logic_cc(s, reg);
+}
+
+static void gen_push(DisasContext *s, TCGv val)
+{
+ TCGv tmp;
+
+ tmp = tcg_temp_new();
+ tcg_gen_subi_i32(tmp, QREG_SP, 4);
+ gen_store(s, OS_LONG, tmp, val);
+ tcg_gen_mov_i32(QREG_SP, tmp);
+}
+
+DISAS_INSN(movem)
+{
+ TCGv addr;
+ int i;
+ uint16_t mask;
+ TCGv reg;
+ TCGv tmp;
+ int is_load;
+
+ mask = lduw_code(s->pc);
+ s->pc += 2;
+ tmp = gen_lea(s, insn, OS_LONG);
+ if (IS_NULL_QREG(tmp)) {
+ gen_addr_fault(s);
+ return;
+ }
+ addr = tcg_temp_new();
+ tcg_gen_mov_i32(addr, tmp);
+ is_load = ((insn & 0x0400) != 0);
+ for (i = 0; i < 16; i++, mask >>= 1) {
+ if (mask & 1) {
+ if (i < 8)
+ reg = DREG(i, 0);
+ else
+ reg = AREG(i, 0);
+ if (is_load) {
+ tmp = gen_load(s, OS_LONG, addr, 0);
+ tcg_gen_mov_i32(reg, tmp);
+ } else {
+ gen_store(s, OS_LONG, addr, reg);
+ }
+ if (mask != 1)
+ tcg_gen_addi_i32(addr, addr, 4);
+ }
+ }
+}
+
+DISAS_INSN(bitop_im)
+{
+ int opsize;
+ int op;
+ TCGv src1;
+ uint32_t mask;
+ int bitnum;
+ TCGv tmp;
+ TCGv addr;
+
+ if ((insn & 0x38) != 0)
+ opsize = OS_BYTE;
+ else
+ opsize = OS_LONG;
+ op = (insn >> 6) & 3;
+
+ bitnum = lduw_code(s->pc);
+ s->pc += 2;
+ if (bitnum & 0xff00) {
+ disas_undef(s, insn);
+ return;
+ }
+
+ SRC_EA(src1, opsize, 0, op ? &addr: NULL);
+
+ gen_flush_flags(s);
+ if (opsize == OS_BYTE)
+ bitnum &= 7;
+ else
+ bitnum &= 31;
+ mask = 1 << bitnum;
+
+ tmp = tcg_temp_new();
+ assert (CCF_Z == (1 << 2));
+ if (bitnum > 2)
+ tcg_gen_shri_i32(tmp, src1, bitnum - 2);
+ else if (bitnum < 2)
+ tcg_gen_shli_i32(tmp, src1, 2 - bitnum);
+ else
+ tcg_gen_mov_i32(tmp, src1);
+ tcg_gen_andi_i32(tmp, tmp, CCF_Z);
+ /* Clear CCF_Z if bit set. */
+ tcg_gen_ori_i32(QREG_CC_DEST, QREG_CC_DEST, CCF_Z);
+ tcg_gen_xor_i32(QREG_CC_DEST, QREG_CC_DEST, tmp);
+ if (op) {
+ switch (op) {
+ case 1: /* bchg */
+ tcg_gen_xori_i32(tmp, src1, mask);
+ break;
+ case 2: /* bclr */
+ tcg_gen_andi_i32(tmp, src1, ~mask);
+ break;
+ case 3: /* bset */
+ tcg_gen_ori_i32(tmp, src1, mask);
+ break;
+ default: /* btst */
+ break;
+ }
+ DEST_EA(insn, opsize, tmp, &addr);
+ }
+}
+
+DISAS_INSN(arith_im)
+{
+ int op;
+ uint32_t im;
+ TCGv src1;
+ TCGv dest;
+ TCGv addr;
+
+ op = (insn >> 9) & 7;
+ SRC_EA(src1, OS_LONG, 0, (op == 6) ? NULL : &addr);
+ im = read_im32(s);
+ dest = tcg_temp_new();
+ switch (op) {
+ case 0: /* ori */
+ tcg_gen_ori_i32(dest, src1, im);
+ gen_logic_cc(s, dest);
+ break;
+ case 1: /* andi */
+ tcg_gen_andi_i32(dest, src1, im);
+ gen_logic_cc(s, dest);
+ break;
+ case 2: /* subi */
+ tcg_gen_mov_i32(dest, src1);
+ gen_helper_xflag_lt(QREG_CC_X, dest, gen_im32(im));
+ tcg_gen_subi_i32(dest, dest, im);
+ gen_update_cc_add(dest, gen_im32(im));
+ s->cc_op = CC_OP_SUB;
+ break;
+ case 3: /* addi */
+ tcg_gen_mov_i32(dest, src1);
+ tcg_gen_addi_i32(dest, dest, im);
+ gen_update_cc_add(dest, gen_im32(im));
+ gen_helper_xflag_lt(QREG_CC_X, dest, gen_im32(im));
+ s->cc_op = CC_OP_ADD;
+ break;
+ case 5: /* eori */
+ tcg_gen_xori_i32(dest, src1, im);
+ gen_logic_cc(s, dest);
+ break;
+ case 6: /* cmpi */
+ tcg_gen_mov_i32(dest, src1);
+ tcg_gen_subi_i32(dest, dest, im);
+ gen_update_cc_add(dest, gen_im32(im));
+ s->cc_op = CC_OP_SUB;
+ break;
+ default:
+ abort();
+ }
+ if (op != 6) {
+ DEST_EA(insn, OS_LONG, dest, &addr);
+ }
+}
+
+DISAS_INSN(byterev)
+{
+ TCGv reg;
+
+ reg = DREG(insn, 0);
+ tcg_gen_bswap32_i32(reg, reg);
+}
+
+DISAS_INSN(move)
+{
+ TCGv src;
+ TCGv dest;
+ int op;
+ int opsize;
+
+ switch (insn >> 12) {
+ case 1: /* move.b */
+ opsize = OS_BYTE;
+ break;
+ case 2: /* move.l */
+ opsize = OS_LONG;
+ break;
+ case 3: /* move.w */
+ opsize = OS_WORD;
+ break;
+ default:
+ abort();
+ }
+ SRC_EA(src, opsize, 1, NULL);
+ op = (insn >> 6) & 7;
+ if (op == 1) {
+ /* movea */
+ /* The value will already have been sign extended. */
+ dest = AREG(insn, 9);
+ tcg_gen_mov_i32(dest, src);
+ } else {
+ /* normal move */
+ uint16_t dest_ea;
+ dest_ea = ((insn >> 9) & 7) | (op << 3);
+ DEST_EA(dest_ea, opsize, src, NULL);
+ /* This will be correct because loads sign extend. */
+ gen_logic_cc(s, src);
+ }
+}
+
+DISAS_INSN(negx)
+{
+ TCGv reg;
+
+ gen_flush_flags(s);
+ reg = DREG(insn, 0);
+ gen_helper_subx_cc(reg, cpu_env, tcg_const_i32(0), reg);
+}
+
+DISAS_INSN(lea)
+{
+ TCGv reg;
+ TCGv tmp;
+
+ reg = AREG(insn, 9);
+ tmp = gen_lea(s, insn, OS_LONG);
+ if (IS_NULL_QREG(tmp)) {
+ gen_addr_fault(s);
+ return;
+ }
+ tcg_gen_mov_i32(reg, tmp);
+}
+
+DISAS_INSN(clr)
+{
+ int opsize;
+
+ switch ((insn >> 6) & 3) {
+ case 0: /* clr.b */
+ opsize = OS_BYTE;
+ break;
+ case 1: /* clr.w */
+ opsize = OS_WORD;
+ break;
+ case 2: /* clr.l */
+ opsize = OS_LONG;
+ break;
+ default:
+ abort();
+ }
+ DEST_EA(insn, opsize, gen_im32(0), NULL);
+ gen_logic_cc(s, gen_im32(0));
+}
+
+static TCGv gen_get_ccr(DisasContext *s)
+{
+ TCGv dest;
+
+ gen_flush_flags(s);
+ dest = tcg_temp_new();
+ tcg_gen_shli_i32(dest, QREG_CC_X, 4);
+ tcg_gen_or_i32(dest, dest, QREG_CC_DEST);
+ return dest;
+}
+
+DISAS_INSN(move_from_ccr)
+{
+ TCGv reg;
+ TCGv ccr;
+
+ ccr = gen_get_ccr(s);
+ reg = DREG(insn, 0);
+ gen_partset_reg(OS_WORD, reg, ccr);
+}
+
+DISAS_INSN(neg)
+{
+ TCGv reg;
+ TCGv src1;
+
+ reg = DREG(insn, 0);
+ src1 = tcg_temp_new();
+ tcg_gen_mov_i32(src1, reg);
+ tcg_gen_neg_i32(reg, src1);
+ s->cc_op = CC_OP_SUB;
+ gen_update_cc_add(reg, src1);
+ gen_helper_xflag_lt(QREG_CC_X, tcg_const_i32(0), src1);
+ s->cc_op = CC_OP_SUB;
+}
+
+static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
+{
+ tcg_gen_movi_i32(QREG_CC_DEST, val & 0xf);
+ tcg_gen_movi_i32(QREG_CC_X, (val & 0x10) >> 4);
+ if (!ccr_only) {
+ gen_helper_set_sr(cpu_env, tcg_const_i32(val & 0xff00));
+ }
+}
+
+static void gen_set_sr(DisasContext *s, uint16_t insn, int ccr_only)
+{
+ TCGv tmp;
+ TCGv reg;
+
+ s->cc_op = CC_OP_FLAGS;
+ if ((insn & 0x38) == 0)
+ {
+ tmp = tcg_temp_new();
+ reg = DREG(insn, 0);
+ tcg_gen_andi_i32(QREG_CC_DEST, reg, 0xf);
+ tcg_gen_shri_i32(tmp, reg, 4);
+ tcg_gen_andi_i32(QREG_CC_X, tmp, 1);
+ if (!ccr_only) {
+ gen_helper_set_sr(cpu_env, reg);
+ }
+ }
+ else if ((insn & 0x3f) == 0x3c)
+ {
+ uint16_t val;
+ val = lduw_code(s->pc);
+ s->pc += 2;
+ gen_set_sr_im(s, val, ccr_only);
+ }
+ else
+ disas_undef(s, insn);
+}
+
+DISAS_INSN(move_to_ccr)
+{
+ gen_set_sr(s, insn, 1);
+}
+
+DISAS_INSN(not)
+{
+ TCGv reg;
+
+ reg = DREG(insn, 0);
+ tcg_gen_not_i32(reg, reg);
+ gen_logic_cc(s, reg);
+}
+
+DISAS_INSN(swap)
+{
+ TCGv src1;
+ TCGv src2;
+ TCGv reg;
+
+ src1 = tcg_temp_new();
+ src2 = tcg_temp_new();
+ reg = DREG(insn, 0);
+ tcg_gen_shli_i32(src1, reg, 16);
+ tcg_gen_shri_i32(src2, reg, 16);
+ tcg_gen_or_i32(reg, src1, src2);
+ gen_logic_cc(s, reg);
+}
+
+DISAS_INSN(pea)
+{
+ TCGv tmp;
+
+ tmp = gen_lea(s, insn, OS_LONG);
+ if (IS_NULL_QREG(tmp)) {
+ gen_addr_fault(s);
+ return;
+ }
+ gen_push(s, tmp);
+}
+
+DISAS_INSN(ext)
+{
+ int op;
+ TCGv reg;
+ TCGv tmp;
+
+ reg = DREG(insn, 0);
+ op = (insn >> 6) & 7;
+ tmp = tcg_temp_new();
+ if (op == 3)
+ tcg_gen_ext16s_i32(tmp, reg);
+ else
+ tcg_gen_ext8s_i32(tmp, reg);
+ if (op == 2)
+ gen_partset_reg(OS_WORD, reg, tmp);
+ else
+ tcg_gen_mov_i32(reg, tmp);
+ gen_logic_cc(s, tmp);
+}
+
+DISAS_INSN(tst)
+{
+ int opsize;
+ TCGv tmp;
+
+ switch ((insn >> 6) & 3) {
+ case 0: /* tst.b */
+ opsize = OS_BYTE;
+ break;
+ case 1: /* tst.w */
+ opsize = OS_WORD;
+ break;
+ case 2: /* tst.l */
+ opsize = OS_LONG;
+ break;
+ default:
+ abort();
+ }
+ SRC_EA(tmp, opsize, 1, NULL);
+ gen_logic_cc(s, tmp);
+}
+
+DISAS_INSN(pulse)
+{
+ /* Implemented as a NOP. */
+}
+
+DISAS_INSN(illegal)
+{
+ gen_exception(s, s->pc - 2, EXCP_ILLEGAL);
+}
+
+/* ??? This should be atomic. */
+DISAS_INSN(tas)
+{
+ TCGv dest;
+ TCGv src1;
+ TCGv addr;
+
+ dest = tcg_temp_new();
+ SRC_EA(src1, OS_BYTE, 1, &addr);
+ gen_logic_cc(s, src1);
+ tcg_gen_ori_i32(dest, src1, 0x80);
+ DEST_EA(insn, OS_BYTE, dest, &addr);
+}
+
+DISAS_INSN(mull)
+{
+ uint16_t ext;
+ TCGv reg;
+ TCGv src1;
+ TCGv dest;
+
+ /* The upper 32 bits of the product are discarded, so
+ muls.l and mulu.l are functionally equivalent. */
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ if (ext & 0x87ff) {
+ gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+ return;
+ }
+ reg = DREG(ext, 12);
+ SRC_EA(src1, OS_LONG, 0, NULL);
+ dest = tcg_temp_new();
+ tcg_gen_mul_i32(dest, src1, reg);
+ tcg_gen_mov_i32(reg, dest);
+ /* Unlike m68k, coldfire always clears the overflow bit. */
+ gen_logic_cc(s, dest);
+}
+
+DISAS_INSN(link)
+{
+ int16_t offset;
+ TCGv reg;
+ TCGv tmp;
+
+ offset = ldsw_code(s->pc);
+ s->pc += 2;
+ reg = AREG(insn, 0);
+ tmp = tcg_temp_new();
+ tcg_gen_subi_i32(tmp, QREG_SP, 4);
+ gen_store(s, OS_LONG, tmp, reg);
+ if ((insn & 7) != 7)
+ tcg_gen_mov_i32(reg, tmp);
+ tcg_gen_addi_i32(QREG_SP, tmp, offset);
+}
+
+DISAS_INSN(unlk)
+{
+ TCGv src;
+ TCGv reg;
+ TCGv tmp;
+
+ src = tcg_temp_new();
+ reg = AREG(insn, 0);
+ tcg_gen_mov_i32(src, reg);
+ tmp = gen_load(s, OS_LONG, src, 0);
+ tcg_gen_mov_i32(reg, tmp);
+ tcg_gen_addi_i32(QREG_SP, src, 4);
+}
+
+DISAS_INSN(nop)
+{
+}
+
+DISAS_INSN(rts)
+{
+ TCGv tmp;
+
+ tmp = gen_load(s, OS_LONG, QREG_SP, 0);
+ tcg_gen_addi_i32(QREG_SP, QREG_SP, 4);
+ gen_jmp(s, tmp);
+}
+
+DISAS_INSN(jump)
+{
+ TCGv tmp;
+
+ /* Load the target address first to ensure correct exception
+ behavior. */
+ tmp = gen_lea(s, insn, OS_LONG);
+ if (IS_NULL_QREG(tmp)) {
+ gen_addr_fault(s);
+ return;
+ }
+ if ((insn & 0x40) == 0) {
+ /* jsr */
+ gen_push(s, gen_im32(s->pc));
+ }
+ gen_jmp(s, tmp);
+}
+
+DISAS_INSN(addsubq)
+{
+ TCGv src1;
+ TCGv src2;
+ TCGv dest;
+ int val;
+ TCGv addr;
+
+ SRC_EA(src1, OS_LONG, 0, &addr);
+ val = (insn >> 9) & 7;
+ if (val == 0)
+ val = 8;
+ dest = tcg_temp_new();
+ tcg_gen_mov_i32(dest, src1);
+ if ((insn & 0x38) == 0x08) {
+ /* Don't update condition codes if the destination is an
+ address register. */
+ if (insn & 0x0100) {
+ tcg_gen_subi_i32(dest, dest, val);
+ } else {
+ tcg_gen_addi_i32(dest, dest, val);
+ }
+ } else {
+ src2 = gen_im32(val);
+ if (insn & 0x0100) {
+ gen_helper_xflag_lt(QREG_CC_X, dest, src2);
+ tcg_gen_subi_i32(dest, dest, val);
+ s->cc_op = CC_OP_SUB;
+ } else {
+ tcg_gen_addi_i32(dest, dest, val);
+ gen_helper_xflag_lt(QREG_CC_X, dest, src2);
+ s->cc_op = CC_OP_ADD;
+ }
+ gen_update_cc_add(dest, src2);
+ }
+ DEST_EA(insn, OS_LONG, dest, &addr);
+}
+
+DISAS_INSN(tpf)
+{
+ switch (insn & 7) {
+ case 2: /* One extension word. */
+ s->pc += 2;
+ break;
+ case 3: /* Two extension words. */
+ s->pc += 4;
+ break;
+ case 4: /* No extension words. */
+ break;
+ default:
+ disas_undef(s, insn);
+ }
+}
+
+DISAS_INSN(branch)
+{
+ int32_t offset;
+ uint32_t base;
+ int op;
+ int l1;
+
+ base = s->pc;
+ op = (insn >> 8) & 0xf;
+ offset = (int8_t)insn;
+ if (offset == 0) {
+ offset = ldsw_code(s->pc);
+ s->pc += 2;
+ } else if (offset == -1) {
+ offset = read_im32(s);
+ }
+ if (op == 1) {
+ /* bsr */
+ gen_push(s, gen_im32(s->pc));
+ }
+ gen_flush_cc_op(s);
+ if (op > 1) {
+ /* Bcc */
+ l1 = gen_new_label();
+ gen_jmpcc(s, ((insn >> 8) & 0xf) ^ 1, l1);
+ gen_jmp_tb(s, 1, base + offset);
+ gen_set_label(l1);
+ gen_jmp_tb(s, 0, s->pc);
+ } else {
+ /* Unconditional branch. */
+ gen_jmp_tb(s, 0, base + offset);
+ }
+}
+
+DISAS_INSN(moveq)
+{
+ uint32_t val;
+
+ val = (int8_t)insn;
+ tcg_gen_movi_i32(DREG(insn, 9), val);
+ gen_logic_cc(s, tcg_const_i32(val));
+}
+
+DISAS_INSN(mvzs)
+{
+ int opsize;
+ TCGv src;
+ TCGv reg;
+
+ if (insn & 0x40)
+ opsize = OS_WORD;
+ else
+ opsize = OS_BYTE;
+ SRC_EA(src, opsize, (insn & 0x80) == 0, NULL);
+ reg = DREG(insn, 9);
+ tcg_gen_mov_i32(reg, src);
+ gen_logic_cc(s, src);
+}
+
+DISAS_INSN(or)
+{
+ TCGv reg;
+ TCGv dest;
+ TCGv src;
+ TCGv addr;
+
+ reg = DREG(insn, 9);
+ dest = tcg_temp_new();
+ if (insn & 0x100) {
+ SRC_EA(src, OS_LONG, 0, &addr);
+ tcg_gen_or_i32(dest, src, reg);
+ DEST_EA(insn, OS_LONG, dest, &addr);
+ } else {
+ SRC_EA(src, OS_LONG, 0, NULL);
+ tcg_gen_or_i32(dest, src, reg);
+ tcg_gen_mov_i32(reg, dest);
+ }
+ gen_logic_cc(s, dest);
+}
+
+DISAS_INSN(suba)
+{
+ TCGv src;
+ TCGv reg;
+
+ SRC_EA(src, OS_LONG, 0, NULL);
+ reg = AREG(insn, 9);
+ tcg_gen_sub_i32(reg, reg, src);
+}
+
+DISAS_INSN(subx)
+{
+ TCGv reg;
+ TCGv src;
+
+ gen_flush_flags(s);
+ reg = DREG(insn, 9);
+ src = DREG(insn, 0);
+ gen_helper_subx_cc(reg, cpu_env, reg, src);
+}
+
+DISAS_INSN(mov3q)
+{
+ TCGv src;
+ int val;
+
+ val = (insn >> 9) & 7;
+ if (val == 0)
+ val = -1;
+ src = gen_im32(val);
+ gen_logic_cc(s, src);
+ DEST_EA(insn, OS_LONG, src, NULL);
+}
+
+DISAS_INSN(cmp)
+{
+ int op;
+ TCGv src;
+ TCGv reg;
+ TCGv dest;
+ int opsize;
+
+ op = (insn >> 6) & 3;
+ switch (op) {
+ case 0: /* cmp.b */
+ opsize = OS_BYTE;
+ s->cc_op = CC_OP_CMPB;
+ break;
+ case 1: /* cmp.w */
+ opsize = OS_WORD;
+ s->cc_op = CC_OP_CMPW;
+ break;
+ case 2: /* cmp.l */
+ opsize = OS_LONG;
+ s->cc_op = CC_OP_SUB;
+ break;
+ default:
+ abort();
+ }
+ SRC_EA(src, opsize, 1, NULL);
+ reg = DREG(insn, 9);
+ dest = tcg_temp_new();
+ tcg_gen_sub_i32(dest, reg, src);
+ gen_update_cc_add(dest, src);
+}
+
+DISAS_INSN(cmpa)
+{
+ int opsize;
+ TCGv src;
+ TCGv reg;
+ TCGv dest;
+
+ if (insn & 0x100) {
+ opsize = OS_LONG;
+ } else {
+ opsize = OS_WORD;
+ }
+ SRC_EA(src, opsize, 1, NULL);
+ reg = AREG(insn, 9);
+ dest = tcg_temp_new();
+ tcg_gen_sub_i32(dest, reg, src);
+ gen_update_cc_add(dest, src);
+ s->cc_op = CC_OP_SUB;
+}
+
+DISAS_INSN(eor)
+{
+ TCGv src;
+ TCGv reg;
+ TCGv dest;
+ TCGv addr;
+
+ SRC_EA(src, OS_LONG, 0, &addr);
+ reg = DREG(insn, 9);
+ dest = tcg_temp_new();
+ tcg_gen_xor_i32(dest, src, reg);
+ gen_logic_cc(s, dest);
+ DEST_EA(insn, OS_LONG, dest, &addr);
+}
+
+DISAS_INSN(and)
+{
+ TCGv src;
+ TCGv reg;
+ TCGv dest;
+ TCGv addr;
+
+ reg = DREG(insn, 9);
+ dest = tcg_temp_new();
+ if (insn & 0x100) {
+ SRC_EA(src, OS_LONG, 0, &addr);
+ tcg_gen_and_i32(dest, src, reg);
+ DEST_EA(insn, OS_LONG, dest, &addr);
+ } else {
+ SRC_EA(src, OS_LONG, 0, NULL);
+ tcg_gen_and_i32(dest, src, reg);
+ tcg_gen_mov_i32(reg, dest);
+ }
+ gen_logic_cc(s, dest);
+}
+
+DISAS_INSN(adda)
+{
+ TCGv src;
+ TCGv reg;
+
+ SRC_EA(src, OS_LONG, 0, NULL);
+ reg = AREG(insn, 9);
+ tcg_gen_add_i32(reg, reg, src);
+}
+
+DISAS_INSN(addx)
+{
+ TCGv reg;
+ TCGv src;
+
+ gen_flush_flags(s);
+ reg = DREG(insn, 9);
+ src = DREG(insn, 0);
+ gen_helper_addx_cc(reg, cpu_env, reg, src);
+ s->cc_op = CC_OP_FLAGS;
+}
+
+/* TODO: This could be implemented without helper functions. */
+DISAS_INSN(shift_im)
+{
+ TCGv reg;
+ int tmp;
+ TCGv shift;
+
+ reg = DREG(insn, 0);
+ tmp = (insn >> 9) & 7;
+ if (tmp == 0)
+ tmp = 8;
+ shift = gen_im32(tmp);
+ /* No need to flush flags becuse we know we will set C flag. */
+ if (insn & 0x100) {
+ gen_helper_shl_cc(reg, cpu_env, reg, shift);
+ } else {
+ if (insn & 8) {
+ gen_helper_shr_cc(reg, cpu_env, reg, shift);
+ } else {
+ gen_helper_sar_cc(reg, cpu_env, reg, shift);
+ }
+ }
+ s->cc_op = CC_OP_SHIFT;
+}
+
+DISAS_INSN(shift_reg)
+{
+ TCGv reg;
+ TCGv shift;
+
+ reg = DREG(insn, 0);
+ shift = DREG(insn, 9);
+ /* Shift by zero leaves C flag unmodified. */
+ gen_flush_flags(s);
+ if (insn & 0x100) {
+ gen_helper_shl_cc(reg, cpu_env, reg, shift);
+ } else {
+ if (insn & 8) {
+ gen_helper_shr_cc(reg, cpu_env, reg, shift);
+ } else {
+ gen_helper_sar_cc(reg, cpu_env, reg, shift);
+ }
+ }
+ s->cc_op = CC_OP_SHIFT;
+}
+
+DISAS_INSN(ff1)
+{
+ TCGv reg;
+ reg = DREG(insn, 0);
+ gen_logic_cc(s, reg);
+ gen_helper_ff1(reg, reg);
+}
+
+static TCGv gen_get_sr(DisasContext *s)
+{
+ TCGv ccr;
+ TCGv sr;
+
+ ccr = gen_get_ccr(s);
+ sr = tcg_temp_new();
+ tcg_gen_andi_i32(sr, QREG_SR, 0xffe0);
+ tcg_gen_or_i32(sr, sr, ccr);
+ return sr;
+}
+
+DISAS_INSN(strldsr)
+{
+ uint16_t ext;
+ uint32_t addr;
+
+ addr = s->pc - 2;
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ if (ext != 0x46FC) {
+ gen_exception(s, addr, EXCP_UNSUPPORTED);
+ return;
+ }
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ if (IS_USER(s) || (ext & SR_S) == 0) {
+ gen_exception(s, addr, EXCP_PRIVILEGE);
+ return;
+ }
+ gen_push(s, gen_get_sr(s));
+ gen_set_sr_im(s, ext, 0);
+}
+
+DISAS_INSN(move_from_sr)
+{
+ TCGv reg;
+ TCGv sr;
+
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ sr = gen_get_sr(s);
+ reg = DREG(insn, 0);
+ gen_partset_reg(OS_WORD, reg, sr);
+}
+
+DISAS_INSN(move_to_sr)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ gen_set_sr(s, insn, 0);
+ gen_lookup_tb(s);
+}
+
+DISAS_INSN(move_from_usp)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* TODO: Implement USP. */
+ gen_exception(s, s->pc - 2, EXCP_ILLEGAL);
+}
+
+DISAS_INSN(move_to_usp)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* TODO: Implement USP. */
+ gen_exception(s, s->pc - 2, EXCP_ILLEGAL);
+}
+
+DISAS_INSN(halt)
+{
+ gen_exception(s, s->pc, EXCP_HALT_INSN);
+}
+
+DISAS_INSN(stop)
+{
+ uint16_t ext;
+
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+
+ gen_set_sr_im(s, ext, 0);
+ tcg_gen_movi_i32(QREG_HALTED, 1);
+ gen_exception(s, s->pc, EXCP_HLT);
+}
+
+DISAS_INSN(rte)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ gen_exception(s, s->pc - 2, EXCP_RTE);
+}
+
+DISAS_INSN(movec)
+{
+ uint16_t ext;
+ TCGv reg;
+
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+
+ if (ext & 0x8000) {
+ reg = AREG(ext, 12);
+ } else {
+ reg = DREG(ext, 12);
+ }
+ gen_helper_movec(cpu_env, tcg_const_i32(ext & 0xfff), reg);
+ gen_lookup_tb(s);
+}
+
+DISAS_INSN(intouch)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* ICache fetch. Implement as no-op. */
+}
+
+DISAS_INSN(cpushl)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* Cache push/invalidate. Implement as no-op. */
+}
+
+DISAS_INSN(wddata)
+{
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+}
+
+DISAS_INSN(wdebug)
+{
+ if (IS_USER(s)) {
+ gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+ return;
+ }
+ /* TODO: Implement wdebug. */
+ qemu_assert(0, "WDEBUG not implemented");
+}
+
+DISAS_INSN(trap)
+{
+ gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf));
+}
+
+/* ??? FP exceptions are not implemented. Most exceptions are deferred until
+ immediately before the next FP instruction is executed. */
+DISAS_INSN(fpu)
+{
+ uint16_t ext;
+ int32_t offset;
+ int opmode;
+ TCGv_i64 src;
+ TCGv_i64 dest;
+ TCGv_i64 res;
+ TCGv tmp32;
+ int round;
+ int set_dest;
+ int opsize;
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+ opmode = ext & 0x7f;
+ switch ((ext >> 13) & 7) {
+ case 0: case 2:
+ break;
+ case 1:
+ goto undef;
+ case 3: /* fmove out */
+ src = FREG(ext, 7);
+ tmp32 = tcg_temp_new_i32();
+ /* fmove */
+ /* ??? TODO: Proper behavior on overflow. */
+ switch ((ext >> 10) & 7) {
+ case 0:
+ opsize = OS_LONG;
+ gen_helper_f64_to_i32(tmp32, cpu_env, src);
+ break;
+ case 1:
+ opsize = OS_SINGLE;
+ gen_helper_f64_to_f32(tmp32, cpu_env, src);
+ break;
+ case 4:
+ opsize = OS_WORD;
+ gen_helper_f64_to_i32(tmp32, cpu_env, src);
+ break;
+ case 5: /* OS_DOUBLE */
+ tcg_gen_mov_i32(tmp32, AREG(insn, 0));
+ switch ((insn >> 3) & 7) {
+ case 2:
+ case 3:
+ break;
+ case 4:
+ tcg_gen_addi_i32(tmp32, tmp32, -8);
+ break;
+ case 5:
+ offset = ldsw_code(s->pc);
+ s->pc += 2;
+ tcg_gen_addi_i32(tmp32, tmp32, offset);
+ break;
+ default:
+ goto undef;
+ }
+ gen_store64(s, tmp32, src);
+ switch ((insn >> 3) & 7) {
+ case 3:
+ tcg_gen_addi_i32(tmp32, tmp32, 8);
+ tcg_gen_mov_i32(AREG(insn, 0), tmp32);
+ break;
+ case 4:
+ tcg_gen_mov_i32(AREG(insn, 0), tmp32);
+ break;
+ }
+ tcg_temp_free_i32(tmp32);
+ return;
+ case 6:
+ opsize = OS_BYTE;
+ gen_helper_f64_to_i32(tmp32, cpu_env, src);
+ break;
+ default:
+ goto undef;
+ }
+ DEST_EA(insn, opsize, tmp32, NULL);
+ tcg_temp_free_i32(tmp32);
+ return;
+ case 4: /* fmove to control register. */
+ switch ((ext >> 10) & 7) {
+ case 4: /* FPCR */
+ /* Not implemented. Ignore writes. */
+ break;
+ case 1: /* FPIAR */
+ case 2: /* FPSR */
+ default:
+ cpu_abort(NULL, "Unimplemented: fmove to control %d",
+ (ext >> 10) & 7);
+ }
+ break;
+ case 5: /* fmove from control register. */
+ switch ((ext >> 10) & 7) {
+ case 4: /* FPCR */
+ /* Not implemented. Always return zero. */
+ tmp32 = gen_im32(0);
+ break;
+ case 1: /* FPIAR */
+ case 2: /* FPSR */
+ default:
+ cpu_abort(NULL, "Unimplemented: fmove from control %d",
+ (ext >> 10) & 7);
+ goto undef;
+ }
+ DEST_EA(insn, OS_LONG, tmp32, NULL);
+ break;
+ case 6: /* fmovem */
+ case 7:
+ {
+ TCGv addr;
+ uint16_t mask;
+ int i;
+ if ((ext & 0x1f00) != 0x1000 || (ext & 0xff) == 0)
+ goto undef;
+ tmp32 = gen_lea(s, insn, OS_LONG);
+ if (IS_NULL_QREG(tmp32)) {
+ gen_addr_fault(s);
+ return;
+ }
+ addr = tcg_temp_new_i32();
+ tcg_gen_mov_i32(addr, tmp32);
+ mask = 0x80;
+ for (i = 0; i < 8; i++) {
+ if (ext & mask) {
+ s->is_mem = 1;
+ dest = FREG(i, 0);
+ if (ext & (1 << 13)) {
+ /* store */
+ tcg_gen_qemu_stf64(dest, addr, IS_USER(s));
+ } else {
+ /* load */
+ tcg_gen_qemu_ldf64(dest, addr, IS_USER(s));
+ }
+ if (ext & (mask - 1))
+ tcg_gen_addi_i32(addr, addr, 8);
+ }
+ mask >>= 1;
+ }
+ tcg_temp_free_i32(addr);
+ }
+ return;
+ }
+ if (ext & (1 << 14)) {
+ /* Source effective address. */
+ switch ((ext >> 10) & 7) {
+ case 0: opsize = OS_LONG; break;
+ case 1: opsize = OS_SINGLE; break;
+ case 4: opsize = OS_WORD; break;
+ case 5: opsize = OS_DOUBLE; break;
+ case 6: opsize = OS_BYTE; break;
+ default:
+ goto undef;
+ }
+ if (opsize == OS_DOUBLE) {
+ tmp32 = tcg_temp_new_i32();
+ tcg_gen_mov_i32(tmp32, AREG(insn, 0));
+ switch ((insn >> 3) & 7) {
+ case 2:
+ case 3:
+ break;
+ case 4:
+ tcg_gen_addi_i32(tmp32, tmp32, -8);
+ break;
+ case 5:
+ offset = ldsw_code(s->pc);
+ s->pc += 2;
+ tcg_gen_addi_i32(tmp32, tmp32, offset);
+ break;
+ case 7:
+ offset = ldsw_code(s->pc);
+ offset += s->pc - 2;
+ s->pc += 2;
+ tcg_gen_addi_i32(tmp32, tmp32, offset);
+ break;
+ default:
+ goto undef;
+ }
+ src = gen_load64(s, tmp32);
+ switch ((insn >> 3) & 7) {
+ case 3:
+ tcg_gen_addi_i32(tmp32, tmp32, 8);
+ tcg_gen_mov_i32(AREG(insn, 0), tmp32);
+ break;
+ case 4:
+ tcg_gen_mov_i32(AREG(insn, 0), tmp32);
+ break;
+ }
+ tcg_temp_free_i32(tmp32);
+ } else {
+ SRC_EA(tmp32, opsize, 1, NULL);
+ src = tcg_temp_new_i64();
+ switch (opsize) {
+ case OS_LONG:
+ case OS_WORD:
+ case OS_BYTE:
+ gen_helper_i32_to_f64(src, cpu_env, tmp32);
+ break;
+ case OS_SINGLE:
+ gen_helper_f32_to_f64(src, cpu_env, tmp32);
+ break;
+ }
+ }
+ } else {
+ /* Source register. */
+ src = FREG(ext, 10);
+ }
+ dest = FREG(ext, 7);
+ res = tcg_temp_new_i64();
+ if (opmode != 0x3a)
+ tcg_gen_mov_f64(res, dest);
+ round = 1;
+ set_dest = 1;
+ switch (opmode) {
+ case 0: case 0x40: case 0x44: /* fmove */
+ tcg_gen_mov_f64(res, src);
+ break;
+ case 1: /* fint */
+ gen_helper_iround_f64(res, cpu_env, src);
+ round = 0;
+ break;
+ case 3: /* fintrz */
+ gen_helper_itrunc_f64(res, cpu_env, src);
+ round = 0;
+ break;
+ case 4: case 0x41: case 0x45: /* fsqrt */
+ gen_helper_sqrt_f64(res, cpu_env, src);
+ break;
+ case 0x18: case 0x58: case 0x5c: /* fabs */
+ gen_helper_abs_f64(res, src);
+ break;
+ case 0x1a: case 0x5a: case 0x5e: /* fneg */
+ gen_helper_chs_f64(res, src);
+ break;
+ case 0x20: case 0x60: case 0x64: /* fdiv */
+ gen_helper_div_f64(res, cpu_env, res, src);
+ break;
+ case 0x22: case 0x62: case 0x66: /* fadd */
+ gen_helper_add_f64(res, cpu_env, res, src);
+ break;
+ case 0x23: case 0x63: case 0x67: /* fmul */
+ gen_helper_mul_f64(res, cpu_env, res, src);
+ break;
+ case 0x28: case 0x68: case 0x6c: /* fsub */
+ gen_helper_sub_f64(res, cpu_env, res, src);
+ break;
+ case 0x38: /* fcmp */
+ gen_helper_sub_cmp_f64(res, cpu_env, res, src);
+ set_dest = 0;
+ round = 0;
+ break;
+ case 0x3a: /* ftst */
+ tcg_gen_mov_f64(res, src);
+ set_dest = 0;
+ round = 0;
+ break;
+ default:
+ goto undef;
+ }
+ if (ext & (1 << 14)) {
+ tcg_temp_free_i64(src);
+ }
+ if (round) {
+ if (opmode & 0x40) {
+ if ((opmode & 0x4) != 0)
+ round = 0;
+ } else if ((s->fpcr & M68K_FPCR_PREC) == 0) {
+ round = 0;
+ }
+ }
+ if (round) {
+ TCGv tmp = tcg_temp_new_i32();
+ gen_helper_f64_to_f32(tmp, cpu_env, res);
+ gen_helper_f32_to_f64(res, cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+ }
+ tcg_gen_mov_f64(QREG_FP_RESULT, res);
+ if (set_dest) {
+ tcg_gen_mov_f64(dest, res);
+ }
+ tcg_temp_free_i64(res);
+ return;
+undef:
+ /* FIXME: Is this right for offset addressing modes? */
+ s->pc -= 2;
+ disas_undef_fpu(s, insn);
+}
+
+DISAS_INSN(fbcc)
+{
+ uint32_t offset;
+ uint32_t addr;
+ TCGv flag;
+ int l1;
+
+ addr = s->pc;
+ offset = ldsw_code(s->pc);
+ s->pc += 2;
+ if (insn & (1 << 6)) {
+ offset = (offset << 16) | lduw_code(s->pc);
+ s->pc += 2;
+ }
+
+ l1 = gen_new_label();
+ /* TODO: Raise BSUN exception. */
+ flag = tcg_temp_new();
+ gen_helper_compare_f64(flag, cpu_env, QREG_FP_RESULT);
+ /* Jump to l1 if condition is true. */
+ switch (insn & 0xf) {
+ case 0: /* f */
+ break;
+ case 1: /* eq (=0) */
+ tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1);
+ break;
+ case 2: /* ogt (=1) */
+ tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(1), l1);
+ break;
+ case 3: /* oge (=0 or =1) */
+ tcg_gen_brcond_i32(TCG_COND_LEU, flag, tcg_const_i32(1), l1);
+ break;
+ case 4: /* olt (=-1) */
+ tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(0), l1);
+ break;
+ case 5: /* ole (=-1 or =0) */
+ tcg_gen_brcond_i32(TCG_COND_LE, flag, tcg_const_i32(0), l1);
+ break;
+ case 6: /* ogl (=-1 or =1) */
+ tcg_gen_andi_i32(flag, flag, 1);
+ tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1);
+ break;
+ case 7: /* or (=2) */
+ tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(2), l1);
+ break;
+ case 8: /* un (<2) */
+ tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(2), l1);
+ break;
+ case 9: /* ueq (=0 or =2) */
+ tcg_gen_andi_i32(flag, flag, 1);
+ tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1);
+ break;
+ case 10: /* ugt (>0) */
+ tcg_gen_brcond_i32(TCG_COND_GT, flag, tcg_const_i32(0), l1);
+ break;
+ case 11: /* uge (>=0) */
+ tcg_gen_brcond_i32(TCG_COND_GE, flag, tcg_const_i32(0), l1);
+ break;
+ case 12: /* ult (=-1 or =2) */
+ tcg_gen_brcond_i32(TCG_COND_GEU, flag, tcg_const_i32(2), l1);
+ break;
+ case 13: /* ule (!=1) */
+ tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(1), l1);
+ break;
+ case 14: /* ne (!=0) */
+ tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1);
+ break;
+ case 15: /* t */
+ tcg_gen_br(l1);
+ break;
+ }
+ gen_jmp_tb(s, 0, s->pc);
+ gen_set_label(l1);
+ gen_jmp_tb(s, 1, addr + offset);
+}
+
+DISAS_INSN(frestore)
+{
+ /* TODO: Implement frestore. */
+ qemu_assert(0, "FRESTORE not implemented");
+}
+
+DISAS_INSN(fsave)
+{
+ /* TODO: Implement fsave. */
+ qemu_assert(0, "FSAVE not implemented");
+}
+
+static inline TCGv gen_mac_extract_word(DisasContext *s, TCGv val, int upper)
+{
+ TCGv tmp = tcg_temp_new();
+ if (s->env->macsr & MACSR_FI) {
+ if (upper)
+ tcg_gen_andi_i32(tmp, val, 0xffff0000);
+ else
+ tcg_gen_shli_i32(tmp, val, 16);
+ } else if (s->env->macsr & MACSR_SU) {
+ if (upper)
+ tcg_gen_sari_i32(tmp, val, 16);
+ else
+ tcg_gen_ext16s_i32(tmp, val);
+ } else {
+ if (upper)
+ tcg_gen_shri_i32(tmp, val, 16);
+ else
+ tcg_gen_ext16u_i32(tmp, val);
+ }
+ return tmp;
+}
+
+static void gen_mac_clear_flags(void)
+{
+ tcg_gen_andi_i32(QREG_MACSR, QREG_MACSR,
+ ~(MACSR_V | MACSR_Z | MACSR_N | MACSR_EV));
+}
+
+DISAS_INSN(mac)
+{
+ TCGv rx;
+ TCGv ry;
+ uint16_t ext;
+ int acc;
+ TCGv tmp;
+ TCGv addr;
+ TCGv loadval;
+ int dual;
+ TCGv saved_flags;
+
+ if (!s->done_mac) {
+ s->mactmp = tcg_temp_new_i64();
+ s->done_mac = 1;
+ }
+
+ ext = lduw_code(s->pc);
+ s->pc += 2;
+
+ acc = ((insn >> 7) & 1) | ((ext >> 3) & 2);
+ dual = ((insn & 0x30) != 0 && (ext & 3) != 0);
+ if (dual && !m68k_feature(s->env, M68K_FEATURE_CF_EMAC_B)) {
+ disas_undef(s, insn);
+ return;
+ }
+ if (insn & 0x30) {
+ /* MAC with load. */
+ tmp = gen_lea(s, insn, OS_LONG);
+ addr = tcg_temp_new();
+ tcg_gen_and_i32(addr, tmp, QREG_MAC_MASK);
+ /* Load the value now to ensure correct exception behavior.
+ Perform writeback after reading the MAC inputs. */
+ loadval = gen_load(s, OS_LONG, addr, 0);
+
+ acc ^= 1;
+ rx = (ext & 0x8000) ? AREG(ext, 12) : DREG(insn, 12);
+ ry = (ext & 8) ? AREG(ext, 0) : DREG(ext, 0);
+ } else {
+ loadval = addr = NULL_QREG;
+ rx = (insn & 0x40) ? AREG(insn, 9) : DREG(insn, 9);
+ ry = (insn & 8) ? AREG(insn, 0) : DREG(insn, 0);
+ }
+
+ gen_mac_clear_flags();
+#if 0
+ l1 = -1;
+ /* Disabled because conditional branches clobber temporary vars. */
+ if ((s->env->macsr & MACSR_OMC) != 0 && !dual) {
+ /* Skip the multiply if we know we will ignore it. */
+ l1 = gen_new_label();
+ tmp = tcg_temp_new();
+ tcg_gen_andi_i32(tmp, QREG_MACSR, 1 << (acc + 8));
+ gen_op_jmp_nz32(tmp, l1);
+ }
+#endif
+
+ if ((ext & 0x0800) == 0) {
+ /* Word. */
+ rx = gen_mac_extract_word(s, rx, (ext & 0x80) != 0);
+ ry = gen_mac_extract_word(s, ry, (ext & 0x40) != 0);
+ }
+ if (s->env->macsr & MACSR_FI) {
+ gen_helper_macmulf(s->mactmp, cpu_env, rx, ry);
+ } else {
+ if (s->env->macsr & MACSR_SU)
+ gen_helper_macmuls(s->mactmp, cpu_env, rx, ry);
+ else
+ gen_helper_macmulu(s->mactmp, cpu_env, rx, ry);
+ switch ((ext >> 9) & 3) {
+ case 1:
+ tcg_gen_shli_i64(s->mactmp, s->mactmp, 1);
+ break;
+ case 3:
+ tcg_gen_shri_i64(s->mactmp, s->mactmp, 1);
+ break;
+ }
+ }
+
+ if (dual) {
+ /* Save the overflow flag from the multiply. */
+ saved_flags = tcg_temp_new();
+ tcg_gen_mov_i32(saved_flags, QREG_MACSR);
+ } else {
+ saved_flags = NULL_QREG;
+ }
+
+#if 0
+ /* Disabled because conditional branches clobber temporary vars. */
+ if ((s->env->macsr & MACSR_OMC) != 0 && dual) {
+ /* Skip the accumulate if the value is already saturated. */
+ l1 = gen_new_label();
+ tmp = tcg_temp_new();
+ gen_op_and32(tmp, QREG_MACSR, gen_im32(MACSR_PAV0 << acc));
+ gen_op_jmp_nz32(tmp, l1);
+ }
+#endif
+
+ if (insn & 0x100)
+ tcg_gen_sub_i64(MACREG(acc), MACREG(acc), s->mactmp);
+ else
+ tcg_gen_add_i64(MACREG(acc), MACREG(acc), s->mactmp);
+
+ if (s->env->macsr & MACSR_FI)
+ gen_helper_macsatf(cpu_env, tcg_const_i32(acc));
+ else if (s->env->macsr & MACSR_SU)
+ gen_helper_macsats(cpu_env, tcg_const_i32(acc));
+ else
+ gen_helper_macsatu(cpu_env, tcg_const_i32(acc));
+
+#if 0
+ /* Disabled because conditional branches clobber temporary vars. */
+ if (l1 != -1)
+ gen_set_label(l1);
+#endif
+
+ if (dual) {
+ /* Dual accumulate variant. */
+ acc = (ext >> 2) & 3;
+ /* Restore the overflow flag from the multiplier. */
+ tcg_gen_mov_i32(QREG_MACSR, saved_flags);
+#if 0
+ /* Disabled because conditional branches clobber temporary vars. */
+ if ((s->env->macsr & MACSR_OMC) != 0) {
+ /* Skip the accumulate if the value is already saturated. */
+ l1 = gen_new_label();
+ tmp = tcg_temp_new();
+ gen_op_and32(tmp, QREG_MACSR, gen_im32(MACSR_PAV0 << acc));
+ gen_op_jmp_nz32(tmp, l1);
+ }
+#endif
+ if (ext & 2)
+ tcg_gen_sub_i64(MACREG(acc), MACREG(acc), s->mactmp);
+ else
+ tcg_gen_add_i64(MACREG(acc), MACREG(acc), s->mactmp);
+ if (s->env->macsr & MACSR_FI)
+ gen_helper_macsatf(cpu_env, tcg_const_i32(acc));
+ else if (s->env->macsr & MACSR_SU)
+ gen_helper_macsats(cpu_env, tcg_const_i32(acc));
+ else
+ gen_helper_macsatu(cpu_env, tcg_const_i32(acc));
+#if 0
+ /* Disabled because conditional branches clobber temporary vars. */
+ if (l1 != -1)
+ gen_set_label(l1);
+#endif
+ }
+ gen_helper_mac_set_flags(cpu_env, tcg_const_i32(acc));
+
+ if (insn & 0x30) {
+ TCGv rw;
+ rw = (insn & 0x40) ? AREG(insn, 9) : DREG(insn, 9);
+ tcg_gen_mov_i32(rw, loadval);
+ /* FIXME: Should address writeback happen with the masked or
+ unmasked value? */
+ switch ((insn >> 3) & 7) {
+ case 3: /* Post-increment. */
+ tcg_gen_addi_i32(AREG(insn, 0), addr, 4);
+ break;
+ case 4: /* Pre-decrement. */
+ tcg_gen_mov_i32(AREG(insn, 0), addr);
+ }
+ }
+}
+
+DISAS_INSN(from_mac)
+{
+ TCGv rx;
+ TCGv_i64 acc;
+ int accnum;
+
+ rx = (insn & 8) ? AREG(insn, 0) : DREG(insn, 0);
+ accnum = (insn >> 9) & 3;
+ acc = MACREG(accnum);
+ if (s->env->macsr & MACSR_FI) {
+ gen_helper_get_macf(rx, cpu_env, acc);
+ } else if ((s->env->macsr & MACSR_OMC) == 0) {
+ tcg_gen_trunc_i64_i32(rx, acc);
+ } else if (s->env->macsr & MACSR_SU) {
+ gen_helper_get_macs(rx, acc);
+ } else {
+ gen_helper_get_macu(rx, acc);
+ }
+ if (insn & 0x40) {
+ tcg_gen_movi_i64(acc, 0);
+ tcg_gen_andi_i32(QREG_MACSR, QREG_MACSR, ~(MACSR_PAV0 << accnum));
+ }
+}
+
+DISAS_INSN(move_mac)
+{
+ /* FIXME: This can be done without a helper. */
+ int src;
+ TCGv dest;
+ src = insn & 3;
+ dest = tcg_const_i32((insn >> 9) & 3);
+ gen_helper_mac_move(cpu_env, dest, tcg_const_i32(src));
+ gen_mac_clear_flags();
+ gen_helper_mac_set_flags(cpu_env, dest);
+}
+
+DISAS_INSN(from_macsr)
+{
+ TCGv reg;
+
+ reg = (insn & 8) ? AREG(insn, 0) : DREG(insn, 0);
+ tcg_gen_mov_i32(reg, QREG_MACSR);
+}
+
+DISAS_INSN(from_mask)
+{
+ TCGv reg;
+ reg = (insn & 8) ? AREG(insn, 0) : DREG(insn, 0);
+ tcg_gen_mov_i32(reg, QREG_MAC_MASK);
+}
+
+DISAS_INSN(from_mext)
+{
+ TCGv reg;
+ TCGv acc;
+ reg = (insn & 8) ? AREG(insn, 0) : DREG(insn, 0);
+ acc = tcg_const_i32((insn & 0x400) ? 2 : 0);
+ if (s->env->macsr & MACSR_FI)
+ gen_helper_get_mac_extf(reg, cpu_env, acc);
+ else
+ gen_helper_get_mac_exti(reg, cpu_env, acc);
+}
+
+DISAS_INSN(macsr_to_ccr)
+{
+ tcg_gen_movi_i32(QREG_CC_X, 0);
+ tcg_gen_andi_i32(QREG_CC_DEST, QREG_MACSR, 0xf);
+ s->cc_op = CC_OP_FLAGS;
+}
+
+DISAS_INSN(to_mac)
+{
+ TCGv_i64 acc;
+ TCGv val;
+ int accnum;
+ accnum = (insn >> 9) & 3;
+ acc = MACREG(accnum);
+ SRC_EA(val, OS_LONG, 0, NULL);
+ if (s->env->macsr & MACSR_FI) {
+ tcg_gen_ext_i32_i64(acc, val);
+ tcg_gen_shli_i64(acc, acc, 8);
+ } else if (s->env->macsr & MACSR_SU) {
+ tcg_gen_ext_i32_i64(acc, val);
+ } else {
+ tcg_gen_extu_i32_i64(acc, val);
+ }
+ tcg_gen_andi_i32(QREG_MACSR, QREG_MACSR, ~(MACSR_PAV0 << accnum));
+ gen_mac_clear_flags();
+ gen_helper_mac_set_flags(cpu_env, tcg_const_i32(accnum));
+}
+
+DISAS_INSN(to_macsr)
+{
+ TCGv val;
+ SRC_EA(val, OS_LONG, 0, NULL);
+ gen_helper_set_macsr(cpu_env, val);
+ gen_lookup_tb(s);
+}
+
+DISAS_INSN(to_mask)
+{
+ TCGv val;
+ SRC_EA(val, OS_LONG, 0, NULL);
+ tcg_gen_ori_i32(QREG_MAC_MASK, val, 0xffff0000);
+}
+
+DISAS_INSN(to_mext)
+{
+ TCGv val;
+ TCGv acc;
+ SRC_EA(val, OS_LONG, 0, NULL);
+ acc = tcg_const_i32((insn & 0x400) ? 2 : 0);
+ if (s->env->macsr & MACSR_FI)
+ gen_helper_set_mac_extf(cpu_env, val, acc);
+ else if (s->env->macsr & MACSR_SU)
+ gen_helper_set_mac_exts(cpu_env, val, acc);
+ else
+ gen_helper_set_mac_extu(cpu_env, val, acc);
+}
+
+static disas_proc opcode_table[65536];
+
+static void
+register_opcode (disas_proc proc, uint16_t opcode, uint16_t mask)
+{
+ int i;
+ int from;
+ int to;
+
+ /* Sanity check. All set bits must be included in the mask. */
+ if (opcode & ~mask) {
+ fprintf(stderr,
+ "qemu internal error: bogus opcode definition %04x/%04x\n",
+ opcode, mask);
+ abort();
+ }
+ /* This could probably be cleverer. For now just optimize the case where
+ the top bits are known. */
+ /* Find the first zero bit in the mask. */
+ i = 0x8000;
+ while ((i & mask) != 0)
+ i >>= 1;
+ /* Iterate over all combinations of this and lower bits. */
+ if (i == 0)
+ i = 1;
+ else
+ i <<= 1;
+ from = opcode & ~(i - 1);
+ to = from + i;
+ for (i = from; i < to; i++) {
+ if ((i & mask) == opcode)
+ opcode_table[i] = proc;
+ }
+}
+
+/* Register m68k opcode handlers. Order is important.
+ Later insn override earlier ones. */
+void register_m68k_insns (CPUM68KState *env)
+{
+#define INSN(name, opcode, mask, feature) do { \
+ if (m68k_feature(env, M68K_FEATURE_##feature)) \
+ register_opcode(disas_##name, 0x##opcode, 0x##mask); \
+ } while(0)
+ INSN(undef, 0000, 0000, CF_ISA_A);
+ INSN(arith_im, 0080, fff8, CF_ISA_A);
+ INSN(bitrev, 00c0, fff8, CF_ISA_APLUSC);
+ INSN(bitop_reg, 0100, f1c0, CF_ISA_A);
+ INSN(bitop_reg, 0140, f1c0, CF_ISA_A);
+ INSN(bitop_reg, 0180, f1c0, CF_ISA_A);
+ INSN(bitop_reg, 01c0, f1c0, CF_ISA_A);
+ INSN(arith_im, 0280, fff8, CF_ISA_A);
+ INSN(byterev, 02c0, fff8, CF_ISA_APLUSC);
+ INSN(arith_im, 0480, fff8, CF_ISA_A);
+ INSN(ff1, 04c0, fff8, CF_ISA_APLUSC);
+ INSN(arith_im, 0680, fff8, CF_ISA_A);
+ INSN(bitop_im, 0800, ffc0, CF_ISA_A);
+ INSN(bitop_im, 0840, ffc0, CF_ISA_A);
+ INSN(bitop_im, 0880, ffc0, CF_ISA_A);
+ INSN(bitop_im, 08c0, ffc0, CF_ISA_A);
+ INSN(arith_im, 0a80, fff8, CF_ISA_A);
+ INSN(arith_im, 0c00, ff38, CF_ISA_A);
+ INSN(move, 1000, f000, CF_ISA_A);
+ INSN(move, 2000, f000, CF_ISA_A);
+ INSN(move, 3000, f000, CF_ISA_A);
+ INSN(strldsr, 40e7, ffff, CF_ISA_APLUSC);
+ INSN(negx, 4080, fff8, CF_ISA_A);
+ INSN(move_from_sr, 40c0, fff8, CF_ISA_A);
+ INSN(lea, 41c0, f1c0, CF_ISA_A);
+ INSN(clr, 4200, ff00, CF_ISA_A);
+ INSN(undef, 42c0, ffc0, CF_ISA_A);
+ INSN(move_from_ccr, 42c0, fff8, CF_ISA_A);
+ INSN(neg, 4480, fff8, CF_ISA_A);
+ INSN(move_to_ccr, 44c0, ffc0, CF_ISA_A);
+ INSN(not, 4680, fff8, CF_ISA_A);
+ INSN(move_to_sr, 46c0, ffc0, CF_ISA_A);
+ INSN(pea, 4840, ffc0, CF_ISA_A);
+ INSN(swap, 4840, fff8, CF_ISA_A);
+ INSN(movem, 48c0, fbc0, CF_ISA_A);
+ INSN(ext, 4880, fff8, CF_ISA_A);
+ INSN(ext, 48c0, fff8, CF_ISA_A);
+ INSN(ext, 49c0, fff8, CF_ISA_A);
+ INSN(tst, 4a00, ff00, CF_ISA_A);
+ INSN(tas, 4ac0, ffc0, CF_ISA_B);
+ INSN(halt, 4ac8, ffff, CF_ISA_A);
+ INSN(pulse, 4acc, ffff, CF_ISA_A);
+ INSN(illegal, 4afc, ffff, CF_ISA_A);
+ INSN(mull, 4c00, ffc0, CF_ISA_A);
+ INSN(divl, 4c40, ffc0, CF_ISA_A);
+ INSN(sats, 4c80, fff8, CF_ISA_B);
+ INSN(trap, 4e40, fff0, CF_ISA_A);
+ INSN(link, 4e50, fff8, CF_ISA_A);
+ INSN(unlk, 4e58, fff8, CF_ISA_A);
+ INSN(move_to_usp, 4e60, fff8, USP);
+ INSN(move_from_usp, 4e68, fff8, USP);
+ INSN(nop, 4e71, ffff, CF_ISA_A);
+ INSN(stop, 4e72, ffff, CF_ISA_A);
+ INSN(rte, 4e73, ffff, CF_ISA_A);
+ INSN(rts, 4e75, ffff, CF_ISA_A);
+ INSN(movec, 4e7b, ffff, CF_ISA_A);
+ INSN(jump, 4e80, ffc0, CF_ISA_A);
+ INSN(jump, 4ec0, ffc0, CF_ISA_A);
+ INSN(addsubq, 5180, f1c0, CF_ISA_A);
+ INSN(scc, 50c0, f0f8, CF_ISA_A);
+ INSN(addsubq, 5080, f1c0, CF_ISA_A);
+ INSN(tpf, 51f8, fff8, CF_ISA_A);
+
+ /* Branch instructions. */
+ INSN(branch, 6000, f000, CF_ISA_A);
+ /* Disable long branch instructions, then add back the ones we want. */
+ INSN(undef, 60ff, f0ff, CF_ISA_A); /* All long branches. */
+ INSN(branch, 60ff, f0ff, CF_ISA_B);
+ INSN(undef, 60ff, ffff, CF_ISA_B); /* bra.l */
+ INSN(branch, 60ff, ffff, BRAL);
+
+ INSN(moveq, 7000, f100, CF_ISA_A);
+ INSN(mvzs, 7100, f100, CF_ISA_B);
+ INSN(or, 8000, f000, CF_ISA_A);
+ INSN(divw, 80c0, f0c0, CF_ISA_A);
+ INSN(addsub, 9000, f000, CF_ISA_A);
+ INSN(subx, 9180, f1f8, CF_ISA_A);
+ INSN(suba, 91c0, f1c0, CF_ISA_A);
+
+ INSN(undef_mac, a000, f000, CF_ISA_A);
+ INSN(mac, a000, f100, CF_EMAC);
+ INSN(from_mac, a180, f9b0, CF_EMAC);
+ INSN(move_mac, a110, f9fc, CF_EMAC);
+ INSN(from_macsr,a980, f9f0, CF_EMAC);
+ INSN(from_mask, ad80, fff0, CF_EMAC);
+ INSN(from_mext, ab80, fbf0, CF_EMAC);
+ INSN(macsr_to_ccr, a9c0, ffff, CF_EMAC);
+ INSN(to_mac, a100, f9c0, CF_EMAC);
+ INSN(to_macsr, a900, ffc0, CF_EMAC);
+ INSN(to_mext, ab00, fbc0, CF_EMAC);
+ INSN(to_mask, ad00, ffc0, CF_EMAC);
+
+ INSN(mov3q, a140, f1c0, CF_ISA_B);
+ INSN(cmp, b000, f1c0, CF_ISA_B); /* cmp.b */
+ INSN(cmp, b040, f1c0, CF_ISA_B); /* cmp.w */
+ INSN(cmpa, b0c0, f1c0, CF_ISA_B); /* cmpa.w */
+ INSN(cmp, b080, f1c0, CF_ISA_A);
+ INSN(cmpa, b1c0, f1c0, CF_ISA_A);
+ INSN(eor, b180, f1c0, CF_ISA_A);
+ INSN(and, c000, f000, CF_ISA_A);
+ INSN(mulw, c0c0, f0c0, CF_ISA_A);
+ INSN(addsub, d000, f000, CF_ISA_A);
+ INSN(addx, d180, f1f8, CF_ISA_A);
+ INSN(adda, d1c0, f1c0, CF_ISA_A);
+ INSN(shift_im, e080, f0f0, CF_ISA_A);
+ INSN(shift_reg, e0a0, f0f0, CF_ISA_A);
+ INSN(undef_fpu, f000, f000, CF_ISA_A);
+ INSN(fpu, f200, ffc0, CF_FPU);
+ INSN(fbcc, f280, ffc0, CF_FPU);
+ INSN(frestore, f340, ffc0, CF_FPU);
+ INSN(fsave, f340, ffc0, CF_FPU);
+ INSN(intouch, f340, ffc0, CF_ISA_A);
+ INSN(cpushl, f428, ff38, CF_ISA_A);
+ INSN(wddata, fb00, ff00, CF_ISA_A);
+ INSN(wdebug, fbc0, ffc0, CF_ISA_A);
+#undef INSN
+}
+
+/* ??? Some of this implementation is not exception safe. We should always
+ write back the result to memory before setting the condition codes. */
+static void disas_m68k_insn(CPUState * env, DisasContext *s)
+{
+ uint16_t insn;
+
+ insn = lduw_code(s->pc);
+ s->pc += 2;
+
+ opcode_table[insn](s, insn);
+}
+
+/* generate intermediate code for basic block 'tb'. */
+static inline void
+gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext dc1, *dc = &dc1;
+ uint16_t *gen_opc_end;
+ CPUBreakpoint *bp;
+ int j, lj;
+ target_ulong pc_start;
+ int pc_offset;
+ int num_insns;
+ int max_insns;
+
+ /* generate intermediate code */
+ pc_start = tb->pc;
+
+ dc->tb = tb;
+
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ dc->env = env;
+ dc->is_jmp = DISAS_NEXT;
+ dc->pc = pc_start;
+ dc->cc_op = CC_OP_DYNAMIC;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->fpcr = env->fpcr;
+ dc->user = (env->sr & SR_S) == 0;
+ dc->is_mem = 0;
+ dc->done_mac = 0;
+ lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
+ do {
+ pc_offset = dc->pc - pc_start;
+ gen_throws_exception = NULL;
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == dc->pc) {
+ gen_exception(dc, dc->pc, EXCP_DEBUG);
+ dc->is_jmp = DISAS_JUMP;
+ break;
+ }
+ }
+ if (dc->is_jmp)
+ 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_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
+ }
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+ dc->insn_pc = dc->pc;
+ disas_m68k_insn(env, dc);
+ num_insns++;
+ } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
+ !env->singlestep_enabled &&
+ !singlestep &&
+ (pc_offset) < (TARGET_PAGE_SIZE - 32) &&
+ num_insns < max_insns);
+
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ if (unlikely(env->singlestep_enabled)) {
+ /* Make sure the pc is updated, and raise a debug exception. */
+ if (!dc->is_jmp) {
+ gen_flush_cc_op(dc);
+ tcg_gen_movi_i32(QREG_PC, dc->pc);
+ }
+ gen_helper_raise_exception(tcg_const_i32(EXCP_DEBUG));
+ } else {
+ switch(dc->is_jmp) {
+ case DISAS_NEXT:
+ gen_flush_cc_op(dc);
+ gen_jmp_tb(dc, 0, dc->pc);
+ break;
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ gen_flush_cc_op(dc);
+ /* indicate that the hash table must be used to find the next TB */
+ tcg_gen_exit_tb(0);
+ break;
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ }
+ }
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("----------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, dc->pc - pc_start, 0);
+ qemu_log("\n");
+ }
+#endif
+ 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;
+ }
+
+ //optimize_flags();
+ //expand_target_qops();
+}
+
+void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 1);
+}
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+ uint16_t sr;
+ CPU_DoubleU u;
+ for (i = 0; i < 8; i++)
+ {
+ u.d = env->fregs[i];
+ cpu_fprintf (f, "D%d = %08x A%d = %08x F%d = %08x%08x (%12g)\n",
+ i, env->dregs[i], i, env->aregs[i],
+ i, u.l.upper, u.l.lower, *(double *)&u.d);
+ }
+ cpu_fprintf (f, "PC = %08x ", env->pc);
+ sr = env->sr;
+ cpu_fprintf (f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-',
+ (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-',
+ (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-');
+ cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result);
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->pc = gen_opc_pc[pc_pos];
+}
diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h
new file mode 100644
index 0000000..3aa28bf
--- /dev/null
+++ b/target-microblaze/cpu.h
@@ -0,0 +1,336 @@
+/*
+ * MicroBlaze virtual CPU header
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CPU_MICROBLAZE_H
+#define CPU_MICROBLAZE_H
+
+#define TARGET_LONG_BITS 32
+
+#define CPUState struct CPUMBState
+
+#include "cpu-defs.h"
+#include "softfloat.h"
+struct CPUMBState;
+#if !defined(CONFIG_USER_ONLY)
+#include "mmu.h"
+#endif
+
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE EM_MICROBLAZE
+
+#define EXCP_NMI 1
+#define EXCP_MMU 2
+#define EXCP_IRQ 3
+#define EXCP_BREAK 4
+#define EXCP_HW_BREAK 5
+#define EXCP_HW_EXCP 6
+
+/* Register aliases. R0 - R15 */
+#define R_SP 1
+#define SR_PC 0
+#define SR_MSR 1
+#define SR_EAR 3
+#define SR_ESR 5
+#define SR_FSR 7
+#define SR_BTR 0xb
+#define SR_EDR 0xd
+
+/* MSR flags. */
+#define MSR_BE (1<<0) /* 0x001 */
+#define MSR_IE (1<<1) /* 0x002 */
+#define MSR_C (1<<2) /* 0x004 */
+#define MSR_BIP (1<<3) /* 0x008 */
+#define MSR_FSL (1<<4) /* 0x010 */
+#define MSR_ICE (1<<5) /* 0x020 */
+#define MSR_DZ (1<<6) /* 0x040 */
+#define MSR_DCE (1<<7) /* 0x080 */
+#define MSR_EE (1<<8) /* 0x100 */
+#define MSR_EIP (1<<9) /* 0x200 */
+#define MSR_CC (1<<31)
+
+/* Machine State Register (MSR) Fields */
+#define MSR_UM (1<<11) /* User Mode */
+#define MSR_UMS (1<<12) /* User Mode Save */
+#define MSR_VM (1<<13) /* Virtual Mode */
+#define MSR_VMS (1<<14) /* Virtual Mode Save */
+
+#define MSR_KERNEL MSR_EE|MSR_VM
+//#define MSR_USER MSR_KERNEL|MSR_UM|MSR_IE
+#define MSR_KERNEL_VMS MSR_EE|MSR_VMS
+//#define MSR_USER_VMS MSR_KERNEL_VMS|MSR_UMS|MSR_IE
+
+/* Exception State Register (ESR) Fields */
+#define ESR_DIZ (1<<11) /* Zone Protection */
+#define ESR_S (1<<10) /* Store instruction */
+
+#define ESR_EC_FSL 0
+#define ESR_EC_UNALIGNED_DATA 1
+#define ESR_EC_ILLEGAL_OP 2
+#define ESR_EC_INSN_BUS 3
+#define ESR_EC_DATA_BUS 4
+#define ESR_EC_DIVZERO 5
+#define ESR_EC_FPU 6
+#define ESR_EC_PRIVINSN 7
+#define ESR_EC_DATA_STORAGE 8
+#define ESR_EC_INSN_STORAGE 9
+#define ESR_EC_DATA_TLB 10
+#define ESR_EC_INSN_TLB 11
+
+/* Floating Point Status Register (FSR) Bits */
+#define FSR_IO (1<<4) /* Invalid operation */
+#define FSR_DZ (1<<3) /* Divide-by-zero */
+#define FSR_OF (1<<2) /* Overflow */
+#define FSR_UF (1<<1) /* Underflow */
+#define FSR_DO (1<<0) /* Denormalized operand error */
+
+/* Version reg. */
+/* Basic PVR mask */
+#define PVR0_PVR_FULL_MASK 0x80000000
+#define PVR0_USE_BARREL_MASK 0x40000000
+#define PVR0_USE_DIV_MASK 0x20000000
+#define PVR0_USE_HW_MUL_MASK 0x10000000
+#define PVR0_USE_FPU_MASK 0x08000000
+#define PVR0_USE_EXC_MASK 0x04000000
+#define PVR0_USE_ICACHE_MASK 0x02000000
+#define PVR0_USE_DCACHE_MASK 0x01000000
+#define PVR0_USE_MMU 0x00800000 /* new */
+#define PVR0_VERSION_MASK 0x0000FF00
+#define PVR0_USER1_MASK 0x000000FF
+
+/* User 2 PVR mask */
+#define PVR1_USER2_MASK 0xFFFFFFFF
+
+/* Configuration PVR masks */
+#define PVR2_D_OPB_MASK 0x80000000
+#define PVR2_D_LMB_MASK 0x40000000
+#define PVR2_I_OPB_MASK 0x20000000
+#define PVR2_I_LMB_MASK 0x10000000
+#define PVR2_INTERRUPT_IS_EDGE_MASK 0x08000000
+#define PVR2_EDGE_IS_POSITIVE_MASK 0x04000000
+#define PVR2_D_PLB_MASK 0x02000000 /* new */
+#define PVR2_I_PLB_MASK 0x01000000 /* new */
+#define PVR2_INTERCONNECT 0x00800000 /* new */
+#define PVR2_USE_EXTEND_FSL 0x00080000 /* new */
+#define PVR2_USE_FSL_EXC 0x00040000 /* new */
+#define PVR2_USE_MSR_INSTR 0x00020000
+#define PVR2_USE_PCMP_INSTR 0x00010000
+#define PVR2_AREA_OPTIMISED 0x00008000
+#define PVR2_USE_BARREL_MASK 0x00004000
+#define PVR2_USE_DIV_MASK 0x00002000
+#define PVR2_USE_HW_MUL_MASK 0x00001000
+#define PVR2_USE_FPU_MASK 0x00000800
+#define PVR2_USE_MUL64_MASK 0x00000400
+#define PVR2_USE_FPU2_MASK 0x00000200 /* new */
+#define PVR2_USE_IPLBEXC 0x00000100
+#define PVR2_USE_DPLBEXC 0x00000080
+#define PVR2_OPCODE_0x0_ILL_MASK 0x00000040
+#define PVR2_UNALIGNED_EXC_MASK 0x00000020
+#define PVR2_ILL_OPCODE_EXC_MASK 0x00000010
+#define PVR2_IOPB_BUS_EXC_MASK 0x00000008
+#define PVR2_DOPB_BUS_EXC_MASK 0x00000004
+#define PVR2_DIV_ZERO_EXC_MASK 0x00000002
+#define PVR2_FPU_EXC_MASK 0x00000001
+
+/* Debug and exception PVR masks */
+#define PVR3_DEBUG_ENABLED_MASK 0x80000000
+#define PVR3_NUMBER_OF_PC_BRK_MASK 0x1E000000
+#define PVR3_NUMBER_OF_RD_ADDR_BRK_MASK 0x00380000
+#define PVR3_NUMBER_OF_WR_ADDR_BRK_MASK 0x0000E000
+#define PVR3_FSL_LINKS_MASK 0x00000380
+
+/* ICache config PVR masks */
+#define PVR4_USE_ICACHE_MASK 0x80000000
+#define PVR4_ICACHE_ADDR_TAG_BITS_MASK 0x7C000000
+#define PVR4_ICACHE_USE_FSL_MASK 0x02000000
+#define PVR4_ICACHE_ALLOW_WR_MASK 0x01000000
+#define PVR4_ICACHE_LINE_LEN_MASK 0x00E00000
+#define PVR4_ICACHE_BYTE_SIZE_MASK 0x001F0000
+
+/* DCache config PVR masks */
+#define PVR5_USE_DCACHE_MASK 0x80000000
+#define PVR5_DCACHE_ADDR_TAG_BITS_MASK 0x7C000000
+#define PVR5_DCACHE_USE_FSL_MASK 0x02000000
+#define PVR5_DCACHE_ALLOW_WR_MASK 0x01000000
+#define PVR5_DCACHE_LINE_LEN_MASK 0x00E00000
+#define PVR5_DCACHE_BYTE_SIZE_MASK 0x001F0000
+
+/* ICache base address PVR mask */
+#define PVR6_ICACHE_BASEADDR_MASK 0xFFFFFFFF
+
+/* ICache high address PVR mask */
+#define PVR7_ICACHE_HIGHADDR_MASK 0xFFFFFFFF
+
+/* DCache base address PVR mask */
+#define PVR8_DCACHE_BASEADDR_MASK 0xFFFFFFFF
+
+/* DCache high address PVR mask */
+#define PVR9_DCACHE_HIGHADDR_MASK 0xFFFFFFFF
+
+/* Target family PVR mask */
+#define PVR10_TARGET_FAMILY_MASK 0xFF000000
+
+/* MMU descrtiption */
+#define PVR11_USE_MMU 0xC0000000
+#define PVR11_MMU_ITLB_SIZE 0x38000000
+#define PVR11_MMU_DTLB_SIZE 0x07000000
+#define PVR11_MMU_TLB_ACCESS 0x00C00000
+#define PVR11_MMU_ZONES 0x003C0000
+/* MSR Reset value PVR mask */
+#define PVR11_MSR_RESET_VALUE_MASK 0x000007FF
+
+
+
+/* CPU flags. */
+
+/* Condition codes. */
+#define CC_GE 5
+#define CC_GT 4
+#define CC_LE 3
+#define CC_LT 2
+#define CC_NE 1
+#define CC_EQ 0
+
+#define NB_MMU_MODES 3
+typedef struct CPUMBState {
+ uint32_t debug;
+ uint32_t btaken;
+ uint32_t btarget;
+ uint32_t bimm;
+
+ uint32_t imm;
+ uint32_t regs[33];
+ uint32_t sregs[24];
+ float_status fp_status;
+
+ /* Internal flags. */
+#define IMM_FLAG 4
+#define MSR_EE_FLAG (1 << 8)
+#define DRTI_FLAG (1 << 16)
+#define DRTE_FLAG (1 << 17)
+#define DRTB_FLAG (1 << 18)
+#define D_FLAG (1 << 19) /* Bit in ESR. */
+/* TB dependant CPUState. */
+#define IFLAGS_TB_MASK (D_FLAG | IMM_FLAG | DRTI_FLAG | DRTE_FLAG | DRTB_FLAG)
+ uint32_t iflags;
+
+ struct {
+ uint32_t regs[16];
+ } pvr;
+
+#if !defined(CONFIG_USER_ONLY)
+ /* Unified MMU. */
+ struct microblaze_mmu mmu;
+#endif
+
+ CPU_COMMON
+} CPUMBState;
+
+CPUState *cpu_mb_init(const char *cpu_model);
+int cpu_mb_exec(CPUState *s);
+void cpu_mb_close(CPUState *s);
+void do_interrupt(CPUState *env);
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_mb_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+
+enum {
+ CC_OP_DYNAMIC, /* Use env->cc_op */
+ CC_OP_FLAGS,
+ CC_OP_CMP,
+};
+
+/* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */
+#define TARGET_PAGE_BITS 12
+#define MMAP_SHIFT TARGET_PAGE_BITS
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define cpu_init cpu_mb_init
+#define cpu_exec cpu_mb_exec
+#define cpu_gen_code cpu_mb_gen_code
+#define cpu_signal_handler cpu_mb_signal_handler
+
+#define CPU_SAVE_VERSION 1
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _nommu
+#define MMU_MODE1_SUFFIX _kernel
+#define MMU_MODE2_SUFFIX _user
+#define MMU_NOMMU_IDX 0
+#define MMU_KERNEL_IDX 1
+#define MMU_USER_IDX 2
+/* See NB_MMU_MODES further up the file. */
+
+static inline int cpu_mmu_index (CPUState *env)
+{
+ /* Are we in nommu mode?. */
+ if (!(env->sregs[SR_MSR] & MSR_VM))
+ return MMU_NOMMU_IDX;
+
+ if (env->sregs[SR_MSR] & MSR_UM)
+ return MMU_USER_IDX;
+ return MMU_KERNEL_IDX;
+}
+
+int cpu_mb_handle_mmu_fault(CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_mb_handle_mmu_fault
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regs[R_SP] = newsp;
+ env->regs[3] = 0;
+}
+#endif
+
+static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
+{
+}
+
+static inline int cpu_interrupts_enabled(CPUState *env)
+{
+ return env->sregs[SR_MSR] & MSR_IE;
+}
+
+#include "cpu-all.h"
+
+static inline target_ulong cpu_get_pc(CPUState *env)
+{
+ return env->sregs[SR_PC];
+}
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->sregs[SR_PC];
+ *cs_base = 0;
+ *flags = (env->iflags & IFLAGS_TB_MASK) |
+ (env->sregs[SR_MSR] & (MSR_UM | MSR_VM | MSR_EE));
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int is_asi, int size);
+#endif
+#endif
diff --git a/target-microblaze/exec.h b/target-microblaze/exec.h
new file mode 100644
index 0000000..87b2494
--- /dev/null
+++ b/target-microblaze/exec.h
@@ -0,0 +1,52 @@
+/*
+ * Microblaze execution defines
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "dyngen-exec.h"
+
+register struct CPUMBState *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI));
+}
+
+static inline int cpu_halted(CPUState *env) {
+ if (!env->halted)
+ return 0;
+
+ /* IRQ, NMI and GURU execeptions wakes us up. */
+ if (env->interrupt_request
+ & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->sregs[SR_PC] = tb->pc;
+}
+
diff --git a/target-microblaze/helper.c b/target-microblaze/helper.c
new file mode 100644
index 0000000..5230b52
--- /dev/null
+++ b/target-microblaze/helper.c
@@ -0,0 +1,282 @@
+/*
+ * MicroBlaze helper routines.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "host-utils.h"
+
+#define D(x)
+#define DMMU(x)
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+ env->regs[14] = env->sregs[SR_PC];
+}
+
+int cpu_mb_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ env->exception_index = 0xaa;
+ cpu_dump_state(env, stderr, fprintf, 0);
+ return 1;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+int cpu_mb_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ unsigned int hit;
+ unsigned int mmu_available;
+ int r = 1;
+ int prot;
+
+ mmu_available = 0;
+ if (env->pvr.regs[0] & PVR0_USE_MMU) {
+ mmu_available = 1;
+ if ((env->pvr.regs[0] & PVR0_PVR_FULL_MASK)
+ && (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) {
+ mmu_available = 0;
+ }
+ }
+
+ /* Translate if the MMU is available and enabled. */
+ if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) {
+ target_ulong vaddr, paddr;
+ struct microblaze_mmu_lookup lu;
+
+ hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx);
+ if (hit) {
+ vaddr = address & TARGET_PAGE_MASK;
+ paddr = lu.paddr + vaddr - lu.vaddr;
+
+ DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n",
+ mmu_idx, vaddr, paddr, lu.prot));
+ tlb_set_page(env, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE);
+ r = 0;
+ } else {
+ env->sregs[SR_EAR] = address;
+ DMMU(qemu_log("mmu=%d miss v=%x\n", mmu_idx, address));
+
+ switch (lu.err) {
+ case ERR_PROT:
+ env->sregs[SR_ESR] = rw == 2 ? 17 : 16;
+ env->sregs[SR_ESR] |= (rw == 1) << 10;
+ break;
+ case ERR_MISS:
+ env->sregs[SR_ESR] = rw == 2 ? 19 : 18;
+ env->sregs[SR_ESR] |= (rw == 1) << 10;
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ if (env->exception_index == EXCP_MMU) {
+ cpu_abort(env, "recursive faults\n");
+ }
+
+ /* TLB miss. */
+ env->exception_index = EXCP_MMU;
+ }
+ } else {
+ /* MMU disabled or not available. */
+ address &= TARGET_PAGE_MASK;
+ prot = PAGE_BITS;
+ tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE);
+ r = 0;
+ }
+ return r;
+}
+
+void do_interrupt(CPUState *env)
+{
+ uint32_t t;
+
+ /* IMM flag cannot propagate accross a branch and into the dslot. */
+ assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG)));
+ assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG)));
+/* assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions. */
+ switch (env->exception_index) {
+ case EXCP_HW_EXCP:
+ if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) {
+ qemu_log("Exception raised on system without exceptions!\n");
+ return;
+ }
+
+ env->regs[17] = env->sregs[SR_PC] + 4;
+ env->sregs[SR_ESR] &= ~(1 << 12);
+
+ /* Exception breaks branch + dslot sequence? */
+ if (env->iflags & D_FLAG) {
+ env->sregs[SR_ESR] |= 1 << 12 ;
+ env->sregs[SR_BTR] = env->btarget;
+ }
+
+ /* Disable the MMU. */
+ t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
+ env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
+ env->sregs[SR_MSR] |= t;
+ /* Exception in progress. */
+ env->sregs[SR_MSR] |= MSR_EIP;
+
+ qemu_log_mask(CPU_LOG_INT,
+ "hw exception at pc=%x ear=%x esr=%x iflags=%x\n",
+ env->sregs[SR_PC], env->sregs[SR_EAR],
+ env->sregs[SR_ESR], env->iflags);
+ log_cpu_state_mask(CPU_LOG_INT, env, 0);
+ env->iflags &= ~(IMM_FLAG | D_FLAG);
+ env->sregs[SR_PC] = 0x20;
+ break;
+
+ case EXCP_MMU:
+ env->regs[17] = env->sregs[SR_PC];
+
+ env->sregs[SR_ESR] &= ~(1 << 12);
+ /* Exception breaks branch + dslot sequence? */
+ if (env->iflags & D_FLAG) {
+ D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm));
+ env->sregs[SR_ESR] |= 1 << 12 ;
+ env->sregs[SR_BTR] = env->btarget;
+
+ /* Reexecute the branch. */
+ env->regs[17] -= 4;
+ /* was the branch immprefixed?. */
+ if (env->bimm) {
+ qemu_log_mask(CPU_LOG_INT,
+ "bimm exception at pc=%x iflags=%x\n",
+ env->sregs[SR_PC], env->iflags);
+ env->regs[17] -= 4;
+ log_cpu_state_mask(CPU_LOG_INT, env, 0);
+ }
+ } else if (env->iflags & IMM_FLAG) {
+ D(qemu_log("IMM_FLAG set at exception\n"));
+ env->regs[17] -= 4;
+ }
+
+ /* Disable the MMU. */
+ t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
+ env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
+ env->sregs[SR_MSR] |= t;
+ /* Exception in progress. */
+ env->sregs[SR_MSR] |= MSR_EIP;
+
+ qemu_log_mask(CPU_LOG_INT,
+ "exception at pc=%x ear=%x iflags=%x\n",
+ env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags);
+ log_cpu_state_mask(CPU_LOG_INT, env, 0);
+ env->iflags &= ~(IMM_FLAG | D_FLAG);
+ env->sregs[SR_PC] = 0x20;
+ break;
+
+ case EXCP_IRQ:
+ assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)));
+ assert(env->sregs[SR_MSR] & MSR_IE);
+ assert(!(env->iflags & D_FLAG));
+
+ t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
+
+#if 0
+#include "disas.h"
+
+/* Useful instrumentation when debugging interrupt issues in either
+ the models or in sw. */
+ {
+ const char *sym;
+
+ sym = lookup_symbol(env->sregs[SR_PC]);
+ if (sym
+ && (!strcmp("netif_rx", sym)
+ || !strcmp("process_backlog", sym))) {
+
+ qemu_log(
+ "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n",
+ env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags,
+ sym);
+
+ log_cpu_state(env, 0);
+ }
+ }
+#endif
+ qemu_log_mask(CPU_LOG_INT,
+ "interrupt at pc=%x msr=%x %x iflags=%x\n",
+ env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags);
+
+ env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \
+ | MSR_UM | MSR_IE);
+ env->sregs[SR_MSR] |= t;
+
+ env->regs[14] = env->sregs[SR_PC];
+ env->sregs[SR_PC] = 0x10;
+ //log_cpu_state_mask(CPU_LOG_INT, env, 0);
+ break;
+
+ case EXCP_BREAK:
+ case EXCP_HW_BREAK:
+ assert(!(env->iflags & IMM_FLAG));
+ assert(!(env->iflags & D_FLAG));
+ t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1;
+ qemu_log_mask(CPU_LOG_INT,
+ "break at pc=%x msr=%x %x iflags=%x\n",
+ env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags);
+ log_cpu_state_mask(CPU_LOG_INT, env, 0);
+ env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM);
+ env->sregs[SR_MSR] |= t;
+ env->sregs[SR_MSR] |= MSR_BIP;
+ if (env->exception_index == EXCP_HW_BREAK) {
+ env->regs[16] = env->sregs[SR_PC];
+ env->sregs[SR_MSR] |= MSR_BIP;
+ env->sregs[SR_PC] = 0x18;
+ } else
+ env->sregs[SR_PC] = env->btarget;
+ break;
+ default:
+ cpu_abort(env, "unhandled exception type=%d\n",
+ env->exception_index);
+ break;
+ }
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr)
+{
+ target_ulong vaddr, paddr = 0;
+ struct microblaze_mmu_lookup lu;
+ unsigned int hit;
+
+ if (env->sregs[SR_MSR] & MSR_VM) {
+ hit = mmu_translate(&env->mmu, &lu, addr, 0, 0);
+ if (hit) {
+ vaddr = addr & TARGET_PAGE_MASK;
+ paddr = lu.paddr + vaddr - lu.vaddr;
+ } else
+ paddr = 0; /* ???. */
+ } else
+ paddr = addr & TARGET_PAGE_MASK;
+
+ return paddr;
+}
+#endif
diff --git a/target-microblaze/helper.h b/target-microblaze/helper.h
new file mode 100644
index 0000000..1696b88
--- /dev/null
+++ b/target-microblaze/helper.h
@@ -0,0 +1,36 @@
+#include "def-helper.h"
+
+DEF_HELPER_1(raise_exception, void, i32)
+DEF_HELPER_0(debug, void)
+DEF_HELPER_FLAGS_3(carry, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32, i32)
+DEF_HELPER_2(cmp, i32, i32, i32)
+DEF_HELPER_2(cmpu, i32, i32, i32)
+
+DEF_HELPER_2(divs, i32, i32, i32)
+DEF_HELPER_2(divu, i32, i32, i32)
+
+DEF_HELPER_2(fadd, i32, i32, i32)
+DEF_HELPER_2(frsub, i32, i32, i32)
+DEF_HELPER_2(fmul, i32, i32, i32)
+DEF_HELPER_2(fdiv, i32, i32, i32)
+DEF_HELPER_1(flt, i32, i32)
+DEF_HELPER_1(fint, i32, i32)
+DEF_HELPER_1(fsqrt, i32, i32)
+
+DEF_HELPER_2(fcmp_un, i32, i32, i32)
+DEF_HELPER_2(fcmp_lt, i32, i32, i32)
+DEF_HELPER_2(fcmp_eq, i32, i32, i32)
+DEF_HELPER_2(fcmp_le, i32, i32, i32)
+DEF_HELPER_2(fcmp_gt, i32, i32, i32)
+DEF_HELPER_2(fcmp_ne, i32, i32, i32)
+DEF_HELPER_2(fcmp_ge, i32, i32, i32)
+
+DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32)
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_1(mmu_read, i32, i32)
+DEF_HELPER_2(mmu_write, void, i32, i32)
+#endif
+
+DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
+
+#include "def-helper.h"
diff --git a/target-microblaze/machine.c b/target-microblaze/machine.c
new file mode 100644
index 0000000..1be1c35
--- /dev/null
+++ b/target-microblaze/machine.c
@@ -0,0 +1,11 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
diff --git a/target-microblaze/microblaze-decode.h b/target-microblaze/microblaze-decode.h
new file mode 100644
index 0000000..4a27476
--- /dev/null
+++ b/target-microblaze/microblaze-decode.h
@@ -0,0 +1,52 @@
+/*
+ * MicroBlaze insn decoding macros.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>
+ *
+ * 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/>.
+ */
+
+/* Convenient binary macros. */
+#define HEX__(n) 0x##n##LU
+#define B8__(x) ((x&0x0000000FLU)?1:0) \
+ + ((x&0x000000F0LU)?2:0) \
+ + ((x&0x00000F00LU)?4:0) \
+ + ((x&0x0000F000LU)?8:0) \
+ + ((x&0x000F0000LU)?16:0) \
+ + ((x&0x00F00000LU)?32:0) \
+ + ((x&0x0F000000LU)?64:0) \
+ + ((x&0xF0000000LU)?128:0)
+#define B8(d) ((unsigned char)B8__(HEX__(d)))
+
+/* Decode logic, value and mask. */
+#define DEC_ADD {B8(00000000), B8(00110001)}
+#define DEC_SUB {B8(00000001), B8(00110001)}
+#define DEC_AND {B8(00100001), B8(00110101)}
+#define DEC_XOR {B8(00100010), B8(00110111)}
+#define DEC_OR {B8(00100000), B8(00110111)}
+#define DEC_BIT {B8(00100100), B8(00111111)}
+#define DEC_MSR {B8(00100101), B8(00111111)}
+
+#define DEC_BARREL {B8(00010001), B8(00110111)}
+#define DEC_MUL {B8(00010000), B8(00110111)}
+#define DEC_DIV {B8(00010010), B8(00110111)}
+#define DEC_FPU {B8(00010110), B8(00111111)}
+
+#define DEC_LD {B8(00110000), B8(00110100)}
+#define DEC_ST {B8(00110100), B8(00110100)}
+#define DEC_IMM {B8(00101100), B8(00111111)}
+
+#define DEC_BR {B8(00100110), B8(00110111)}
+#define DEC_BCC {B8(00100111), B8(00110111)}
+#define DEC_RTS {B8(00101101), B8(00111111)}
diff --git a/target-microblaze/mmu.c b/target-microblaze/mmu.c
new file mode 100644
index 0000000..b38f7d9
--- /dev/null
+++ b/target-microblaze/mmu.c
@@ -0,0 +1,303 @@
+/*
+ * Microblaze MMU emulation for qemu.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+
+#define D(x)
+
+static unsigned int tlb_decode_size(unsigned int f)
+{
+ static const unsigned int sizes[] = {
+ 1 * 1024, 4 * 1024, 16 * 1024, 64 * 1024, 256 * 1024,
+ 1 * 1024 * 1024, 4 * 1024 * 1024, 16 * 1024 * 1024
+ };
+ assert(f < ARRAY_SIZE(sizes));
+ return sizes[f];
+}
+
+static void mmu_flush_idx(CPUState *env, unsigned int idx)
+{
+ struct microblaze_mmu *mmu = &env->mmu;
+ unsigned int tlb_size;
+ uint32_t tlb_tag, end, t;
+
+ t = mmu->rams[RAM_TAG][idx];
+ if (!(t & TLB_VALID))
+ return;
+
+ tlb_tag = t & TLB_EPN_MASK;
+ tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
+ end = tlb_tag + tlb_size;
+
+ while (tlb_tag < end) {
+ tlb_flush_page(env, tlb_tag);
+ tlb_tag += TARGET_PAGE_SIZE;
+ }
+}
+
+static void mmu_change_pid(CPUState *env, unsigned int newpid)
+{
+ struct microblaze_mmu *mmu = &env->mmu;
+ unsigned int i;
+ uint32_t t;
+
+ if (newpid & ~0xff)
+ qemu_log("Illegal rpid=%x\n", newpid);
+
+ for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) {
+ /* Lookup and decode. */
+ t = mmu->rams[RAM_TAG][i];
+ if (t & TLB_VALID) {
+ if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i]))
+ mmu_flush_idx(env, i);
+ }
+ }
+}
+
+/* rw - 0 = read, 1 = write, 2 = fetch. */
+unsigned int mmu_translate(struct microblaze_mmu *mmu,
+ struct microblaze_mmu_lookup *lu,
+ target_ulong vaddr, int rw, int mmu_idx)
+{
+ unsigned int i, hit = 0;
+ unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel;
+ unsigned int tlb_size;
+ uint32_t tlb_tag, tlb_rpn, mask, t0;
+
+ lu->err = ERR_MISS;
+ for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) {
+ uint32_t t, d;
+
+ /* Lookup and decode. */
+ t = mmu->rams[RAM_TAG][i];
+ D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID));
+ if (t & TLB_VALID) {
+ tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7);
+ if (tlb_size < TARGET_PAGE_SIZE) {
+ qemu_log("%d pages not supported\n", tlb_size);
+ abort();
+ }
+
+ mask = ~(tlb_size - 1);
+ tlb_tag = t & TLB_EPN_MASK;
+ if ((vaddr & mask) != (tlb_tag & mask)) {
+ D(qemu_log("TLB %d vaddr=%x != tag=%x\n",
+ i, vaddr & mask, tlb_tag & mask));
+ continue;
+ }
+ if (mmu->tids[i]
+ && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) {
+ D(qemu_log("TLB %d pid=%x != tid=%x\n",
+ i, mmu->regs[MMU_R_PID], mmu->tids[i]));
+ continue;
+ }
+
+ /* Bring in the data part. */
+ d = mmu->rams[RAM_DATA][i];
+ tlb_ex = d & TLB_EX;
+ tlb_wr = d & TLB_WR;
+
+ /* Now lets see if there is a zone that overrides the protbits. */
+ tlb_zsel = (d >> 4) & 0xf;
+ t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2));
+ t0 &= 0x3;
+
+ if (tlb_zsel > mmu->c_mmu_zones) {
+ qemu_log("tlb zone select out of range! %d\n", tlb_zsel);
+ t0 = 1; /* Ignore. */
+ }
+
+ if (mmu->c_mmu == 1) {
+ t0 = 1; /* Zones are disabled. */
+ }
+
+ switch (t0) {
+ case 0:
+ if (mmu_idx == MMU_USER_IDX)
+ continue;
+ break;
+ case 2:
+ if (mmu_idx != MMU_USER_IDX) {
+ tlb_ex = 1;
+ tlb_wr = 1;
+ }
+ break;
+ case 3:
+ tlb_ex = 1;
+ tlb_wr = 1;
+ break;
+ default: break;
+ }
+
+ lu->err = ERR_PROT;
+ lu->prot = PAGE_READ;
+ if (tlb_wr)
+ lu->prot |= PAGE_WRITE;
+ else if (rw == 1)
+ goto done;
+ if (tlb_ex)
+ lu->prot |=PAGE_EXEC;
+ else if (rw == 2) {
+ goto done;
+ }
+
+ tlb_rpn = d & TLB_RPN_MASK;
+
+ lu->vaddr = tlb_tag;
+ lu->paddr = tlb_rpn;
+ lu->size = tlb_size;
+ lu->err = ERR_HIT;
+ lu->idx = i;
+ hit = 1;
+ goto done;
+ }
+ }
+done:
+ D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n",
+ vaddr, rw, tlb_wr, tlb_ex, hit));
+ return hit;
+}
+
+/* Writes/reads to the MMU's special regs end up here. */
+uint32_t mmu_read(CPUState *env, uint32_t rn)
+{
+ unsigned int i;
+ uint32_t r;
+
+ if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) {
+ qemu_log("MMU access on MMU-less system\n");
+ return 0;
+ }
+
+ switch (rn) {
+ /* Reads to HI/LO trig reads from the mmu rams. */
+ case MMU_R_TLBLO:
+ case MMU_R_TLBHI:
+ if (!(env->mmu.c_mmu_tlb_access & 1)) {
+ qemu_log("Invalid access to MMU reg %d\n", rn);
+ return 0;
+ }
+
+ i = env->mmu.regs[MMU_R_TLBX] & 0xff;
+ r = env->mmu.rams[rn & 1][i];
+ if (rn == MMU_R_TLBHI)
+ env->mmu.regs[MMU_R_PID] = env->mmu.tids[i];
+ break;
+ case MMU_R_PID:
+ case MMU_R_ZPR:
+ if (!(env->mmu.c_mmu_tlb_access & 1)) {
+ qemu_log("Invalid access to MMU reg %d\n", rn);
+ return 0;
+ }
+ r = env->mmu.regs[rn];
+ break;
+ default:
+ r = env->mmu.regs[rn];
+ break;
+ }
+ D(qemu_log("%s rn=%d=%x\n", __func__, rn, r));
+ return r;
+}
+
+void mmu_write(CPUState *env, uint32_t rn, uint32_t v)
+{
+ unsigned int i;
+ D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]));
+
+ if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) {
+ qemu_log("MMU access on MMU-less system\n");
+ return;
+ }
+
+ switch (rn) {
+ /* Writes to HI/LO trig writes to the mmu rams. */
+ case MMU_R_TLBLO:
+ case MMU_R_TLBHI:
+ i = env->mmu.regs[MMU_R_TLBX] & 0xff;
+ if (rn == MMU_R_TLBHI) {
+ if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0))
+ qemu_log("invalidating index %x at pc=%x\n",
+ i, env->sregs[SR_PC]);
+ env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff;
+ mmu_flush_idx(env, i);
+ }
+ env->mmu.rams[rn & 1][i] = v;
+
+ D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v));
+ break;
+ case MMU_R_ZPR:
+ if (env->mmu.c_mmu_tlb_access <= 1) {
+ qemu_log("Invalid access to MMU reg %d\n", rn);
+ return;
+ }
+
+ /* Changes to the zone protection reg flush the QEMU TLB.
+ Fortunately, these are very uncommon. */
+ if (v != env->mmu.regs[rn]) {
+ tlb_flush(env, 1);
+ }
+ env->mmu.regs[rn] = v;
+ break;
+ case MMU_R_PID:
+ if (env->mmu.c_mmu_tlb_access <= 1) {
+ qemu_log("Invalid access to MMU reg %d\n", rn);
+ return;
+ }
+
+ if (v != env->mmu.regs[rn]) {
+ mmu_change_pid(env, v);
+ env->mmu.regs[rn] = v;
+ }
+ break;
+ case MMU_R_TLBSX:
+ {
+ struct microblaze_mmu_lookup lu;
+ int hit;
+
+ if (env->mmu.c_mmu_tlb_access <= 1) {
+ qemu_log("Invalid access to MMU reg %d\n", rn);
+ return;
+ }
+
+ hit = mmu_translate(&env->mmu, &lu,
+ v & TLB_EPN_MASK, 0, cpu_mmu_index(env));
+ if (hit) {
+ env->mmu.regs[MMU_R_TLBX] = lu.idx;
+ } else
+ env->mmu.regs[MMU_R_TLBX] |= 0x80000000;
+ break;
+ }
+ default:
+ env->mmu.regs[rn] = v;
+ break;
+ }
+}
+
+void mmu_init(struct microblaze_mmu *mmu)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mmu->regs); i++) {
+ mmu->regs[i] = 0;
+ }
+}
diff --git a/target-microblaze/mmu.h b/target-microblaze/mmu.h
new file mode 100644
index 0000000..56149a5
--- /dev/null
+++ b/target-microblaze/mmu.h
@@ -0,0 +1,91 @@
+/*
+ * Microblaze MMU emulation for qemu.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias
+ *
+ * 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/>.
+ */
+
+#define MMU_R_PID 0
+#define MMU_R_ZPR 1
+#define MMU_R_TLBX 2
+#define MMU_R_TLBLO 3
+#define MMU_R_TLBHI 4
+#define MMU_R_TLBSX 5
+
+#define RAM_DATA 1
+#define RAM_TAG 0
+
+/* Tag portion */
+#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */
+#define TLB_PAGESZ_MASK 0x00000380
+#define TLB_PAGESZ(x) (((x) & 0x7) << 7)
+#define PAGESZ_1K 0
+#define PAGESZ_4K 1
+#define PAGESZ_16K 2
+#define PAGESZ_64K 3
+#define PAGESZ_256K 4
+#define PAGESZ_1M 5
+#define PAGESZ_4M 6
+#define PAGESZ_16M 7
+#define TLB_VALID 0x00000040 /* Entry is valid */
+
+/* Data portion */
+#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */
+#define TLB_PERM_MASK 0x00000300
+#define TLB_EX 0x00000200 /* Instruction execution allowed */
+#define TLB_WR 0x00000100 /* Writes permitted */
+#define TLB_ZSEL_MASK 0x000000F0
+#define TLB_ZSEL(x) (((x) & 0xF) << 4)
+#define TLB_ATTR_MASK 0x0000000F
+#define TLB_W 0x00000008 /* Caching is write-through */
+#define TLB_I 0x00000004 /* Caching is inhibited */
+#define TLB_M 0x00000002 /* Memory is coherent */
+#define TLB_G 0x00000001 /* Memory is guarded from prefetch */
+
+#define TLB_ENTRIES 64
+
+struct microblaze_mmu
+{
+ /* Data and tag brams. */
+ uint32_t rams[2][TLB_ENTRIES];
+ /* We keep a separate ram for the tids to avoid the 48 bit tag width. */
+ uint8_t tids[TLB_ENTRIES];
+ /* Control flops. */
+ uint32_t regs[8];
+
+ int c_mmu;
+ int c_mmu_tlb_access;
+ int c_mmu_zones;
+};
+
+struct microblaze_mmu_lookup
+{
+ uint32_t paddr;
+ uint32_t vaddr;
+ unsigned int size;
+ unsigned int idx;
+ int prot;
+ enum {
+ ERR_PROT, ERR_MISS, ERR_HIT
+ } err;
+};
+
+void mmu_flip_um(CPUState *env, unsigned int um);
+unsigned int mmu_translate(struct microblaze_mmu *mmu,
+ struct microblaze_mmu_lookup *lu,
+ target_ulong vaddr, int rw, int mmu_idx);
+uint32_t mmu_read(CPUState *env, uint32_t rn);
+void mmu_write(CPUState *env, uint32_t rn, uint32_t v);
+void mmu_init(struct microblaze_mmu *mmu);
diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c
new file mode 100644
index 0000000..d75a53c
--- /dev/null
+++ b/target-microblaze/op_helper.c
@@ -0,0 +1,491 @@
+/*
+ * Microblaze helper routines.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>.
+ *
+ * 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 <assert.h>
+#include "exec.h"
+#include "helper.h"
+#include "host-utils.h"
+
+#define D(x)
+
+#if !defined(CONFIG_USER_ONLY)
+#define MMUSUFFIX _mmu
+#define SHIFT 0
+#include "softmmu_template.h"
+#define SHIFT 1
+#include "softmmu_template.h"
+#define SHIFT 2
+#include "softmmu_template.h"
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* Try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+ ret = cpu_mb_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, NULL);
+ }
+ }
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+#endif
+
+void helper_raise_exception(uint32_t index)
+{
+ env->exception_index = index;
+ cpu_loop_exit();
+}
+
+void helper_debug(void)
+{
+ int i;
+
+ qemu_log("PC=%8.8x\n", env->sregs[SR_PC]);
+ qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n",
+ env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR],
+ env->debug, env->imm, env->iflags);
+ qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n",
+ env->btaken, env->btarget,
+ (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel",
+ (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel",
+ (env->sregs[SR_MSR] & MSR_EIP),
+ (env->sregs[SR_MSR] & MSR_IE));
+ for (i = 0; i < 32; i++) {
+ qemu_log("r%2.2d=%8.8x ", i, env->regs[i]);
+ if ((i + 1) % 4 == 0)
+ qemu_log("\n");
+ }
+ qemu_log("\n\n");
+}
+
+static inline uint32_t compute_carry(uint32_t a, uint32_t b, uint32_t cin)
+{
+ uint32_t cout = 0;
+
+ if ((b == ~0) && cin)
+ cout = 1;
+ else if ((~0 - a) < (b + cin))
+ cout = 1;
+ return cout;
+}
+
+uint32_t helper_cmp(uint32_t a, uint32_t b)
+{
+ uint32_t t;
+
+ t = b + ~a + 1;
+ if ((b & 0x80000000) ^ (a & 0x80000000))
+ t = (t & 0x7fffffff) | (b & 0x80000000);
+ return t;
+}
+
+uint32_t helper_cmpu(uint32_t a, uint32_t b)
+{
+ uint32_t t;
+
+ t = b + ~a + 1;
+ if ((b & 0x80000000) ^ (a & 0x80000000))
+ t = (t & 0x7fffffff) | (a & 0x80000000);
+ return t;
+}
+
+uint32_t helper_carry(uint32_t a, uint32_t b, uint32_t cf)
+{
+ uint32_t ncf;
+ ncf = compute_carry(a, b, cf);
+ return ncf;
+}
+
+static inline int div_prepare(uint32_t a, uint32_t b)
+{
+ if (b == 0) {
+ env->sregs[SR_MSR] |= MSR_DZ;
+
+ if ((env->sregs[SR_MSR] & MSR_EE)
+ && !(env->pvr.regs[2] & PVR2_DIV_ZERO_EXC_MASK)) {
+ env->sregs[SR_ESR] = ESR_EC_DIVZERO;
+ helper_raise_exception(EXCP_HW_EXCP);
+ }
+ return 0;
+ }
+ env->sregs[SR_MSR] &= ~MSR_DZ;
+ return 1;
+}
+
+uint32_t helper_divs(uint32_t a, uint32_t b)
+{
+ if (!div_prepare(a, b))
+ return 0;
+ return (int32_t)a / (int32_t)b;
+}
+
+uint32_t helper_divu(uint32_t a, uint32_t b)
+{
+ if (!div_prepare(a, b))
+ return 0;
+ return a / b;
+}
+
+/* raise FPU exception. */
+static void raise_fpu_exception(void)
+{
+ env->sregs[SR_ESR] = ESR_EC_FPU;
+ helper_raise_exception(EXCP_HW_EXCP);
+}
+
+static void update_fpu_flags(int flags)
+{
+ int raise = 0;
+
+ if (flags & float_flag_invalid) {
+ env->sregs[SR_FSR] |= FSR_IO;
+ raise = 1;
+ }
+ if (flags & float_flag_divbyzero) {
+ env->sregs[SR_FSR] |= FSR_DZ;
+ raise = 1;
+ }
+ if (flags & float_flag_overflow) {
+ env->sregs[SR_FSR] |= FSR_OF;
+ raise = 1;
+ }
+ if (flags & float_flag_underflow) {
+ env->sregs[SR_FSR] |= FSR_UF;
+ raise = 1;
+ }
+ if (raise
+ && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK)
+ && (env->sregs[SR_MSR] & MSR_EE)) {
+ raise_fpu_exception();
+ }
+}
+
+uint32_t helper_fadd(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_add(fa.f, fb.f, &env->fp_status);
+
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+ return fd.l;
+}
+
+uint32_t helper_frsub(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_sub(fb.f, fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+ return fd.l;
+}
+
+uint32_t helper_fmul(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_mul(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return fd.l;
+}
+
+uint32_t helper_fdiv(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fd, fa, fb;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ fd.f = float32_div(fb.f, fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return fd.l;
+}
+
+uint32_t helper_fcmp_un(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ uint32_t r = 0;
+
+ fa.l = a;
+ fb.l = b;
+
+ if (float32_is_signaling_nan(fa.f) || float32_is_signaling_nan(fb.f)) {
+ update_fpu_flags(float_flag_invalid);
+ r = 1;
+ }
+
+ if (float32_is_quiet_nan(fa.f) || float32_is_quiet_nan(fb.f)) {
+ r = 1;
+ }
+
+ return r;
+}
+
+uint32_t helper_fcmp_lt(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int r;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ r = float32_lt(fb.f, fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_fcmp_eq(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags;
+ int r;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fb.l = b;
+ r = float32_eq(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_fcmp_le(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags;
+ int r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = float32_le(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+
+ return r;
+}
+
+uint32_t helper_fcmp_gt(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags, r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = float32_lt(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+ return r;
+}
+
+uint32_t helper_fcmp_ne(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags, r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = !float32_eq(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_fcmp_ge(uint32_t a, uint32_t b)
+{
+ CPU_FloatU fa, fb;
+ int flags, r;
+
+ fa.l = a;
+ fb.l = b;
+ set_float_exception_flags(0, &env->fp_status);
+ r = !float32_lt(fa.f, fb.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags & float_flag_invalid);
+
+ return r;
+}
+
+uint32_t helper_flt(uint32_t a)
+{
+ CPU_FloatU fd, fa;
+
+ fa.l = a;
+ fd.f = int32_to_float32(fa.l, &env->fp_status);
+ return fd.l;
+}
+
+uint32_t helper_fint(uint32_t a)
+{
+ CPU_FloatU fa;
+ uint32_t r;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ r = float32_to_int32(fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return r;
+}
+
+uint32_t helper_fsqrt(uint32_t a)
+{
+ CPU_FloatU fd, fa;
+ int flags;
+
+ set_float_exception_flags(0, &env->fp_status);
+ fa.l = a;
+ fd.l = float32_sqrt(fa.f, &env->fp_status);
+ flags = get_float_exception_flags(&env->fp_status);
+ update_fpu_flags(flags);
+
+ return fd.l;
+}
+
+uint32_t helper_pcmpbf(uint32_t a, uint32_t b)
+{
+ unsigned int i;
+ uint32_t mask = 0xff000000;
+
+ for (i = 0; i < 4; i++) {
+ if ((a & mask) == (b & mask))
+ return i + 1;
+ mask >>= 8;
+ }
+ return 0;
+}
+
+void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
+{
+ if (addr & mask) {
+ qemu_log_mask(CPU_LOG_INT,
+ "unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
+ addr, mask, wr, dr);
+ env->sregs[SR_EAR] = addr;
+ env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \
+ | (dr & 31) << 5;
+ if (mask == 3) {
+ env->sregs[SR_ESR] |= 1 << 11;
+ }
+ if (!(env->sregs[SR_MSR] & MSR_EE)) {
+ return;
+ }
+ helper_raise_exception(EXCP_HW_EXCP);
+ }
+}
+
+#if !defined(CONFIG_USER_ONLY)
+/* Writes/reads to the MMU's special regs end up here. */
+uint32_t helper_mmu_read(uint32_t rn)
+{
+ return mmu_read(env, rn);
+}
+
+void helper_mmu_write(uint32_t rn, uint32_t v)
+{
+ mmu_write(env, rn, v);
+}
+
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int is_asi, int size)
+{
+ CPUState *saved_env;
+
+ if (!cpu_single_env) {
+ /* XXX: ??? */
+ return;
+ }
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
+ addr, is_write, is_exec);
+ if (!(env->sregs[SR_MSR] & MSR_EE)) {
+ env = saved_env;
+ return;
+ }
+
+ env->sregs[SR_EAR] = addr;
+ if (is_exec) {
+ if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) {
+ env->sregs[SR_ESR] = ESR_EC_INSN_BUS;
+ helper_raise_exception(EXCP_HW_EXCP);
+ }
+ } else {
+ if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) {
+ env->sregs[SR_ESR] = ESR_EC_DATA_BUS;
+ helper_raise_exception(EXCP_HW_EXCP);
+ }
+ }
+ env = saved_env;
+}
+#endif
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
new file mode 100644
index 0000000..2207431
--- /dev/null
+++ b/target-microblaze/translate.c
@@ -0,0 +1,1906 @@
+/*
+ * Xilinx MicroBlaze emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "helper.h"
+#include "microblaze-decode.h"
+#include "qemu-common.h"
+
+#define GEN_HELPER 1
+#include "helper.h"
+
+#define SIM_COMPAT 0
+#define DISAS_GNU 1
+#define DISAS_MB 1
+#if DISAS_MB && !SIM_COMPAT
+# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+# define LOG_DIS(...) do { } while (0)
+#endif
+
+#define D(x)
+
+#define EXTRACT_FIELD(src, start, end) \
+ (((src) >> start) & ((1 << (end - start + 1)) - 1))
+
+static TCGv env_debug;
+static TCGv_ptr cpu_env;
+static TCGv cpu_R[32];
+static TCGv cpu_SR[18];
+static TCGv env_imm;
+static TCGv env_btaken;
+static TCGv env_btarget;
+static TCGv env_iflags;
+
+#include "gen-icount.h"
+
+/* This is the state at translation time. */
+typedef struct DisasContext {
+ CPUState *env;
+ target_ulong pc;
+
+ /* Decoder. */
+ int type_b;
+ uint32_t ir;
+ uint8_t opcode;
+ uint8_t rd, ra, rb;
+ uint16_t imm;
+
+ unsigned int cpustate_changed;
+ unsigned int delayed_branch;
+ unsigned int tb_flags, synced_flags; /* tb dependent flags. */
+ unsigned int clear_imm;
+ int is_jmp;
+
+#define JMP_NOJMP 0
+#define JMP_DIRECT 1
+#define JMP_DIRECT_CC 2
+#define JMP_INDIRECT 3
+ unsigned int jmp;
+ uint32_t jmp_pc;
+
+ int abort_at_next_insn;
+ int nr_nops;
+ struct TranslationBlock *tb;
+ int singlestep_enabled;
+} DisasContext;
+
+static const char *regnames[] =
+{
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+};
+
+static const char *special_regnames[] =
+{
+ "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7",
+ "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15",
+ "sr16", "sr17", "sr18"
+};
+
+/* Sign extend at translation time. */
+static inline int sign_extend(unsigned int val, unsigned int width)
+{
+ int sval;
+
+ /* LSL. */
+ val <<= 31 - width;
+ sval = val;
+ /* ASR. */
+ sval >>= 31 - width;
+ return sval;
+}
+
+static inline void t_sync_flags(DisasContext *dc)
+{
+ /* Synch the tb dependant flags between translator and runtime. */
+ if (dc->tb_flags != dc->synced_flags) {
+ tcg_gen_movi_tl(env_iflags, dc->tb_flags);
+ dc->synced_flags = dc->tb_flags;
+ }
+}
+
+static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index)
+{
+ TCGv_i32 tmp = tcg_const_i32(index);
+
+ t_sync_flags(dc);
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
+ gen_helper_raise_exception(tmp);
+ tcg_temp_free_i32(tmp);
+ dc->is_jmp = DISAS_UPDATE;
+}
+
+static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = dc->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dest);
+ tcg_gen_exit_tb((long)tb + n);
+ } else {
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dest);
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static void read_carry(DisasContext *dc, TCGv d)
+{
+ tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31);
+}
+
+static void write_carry(DisasContext *dc, TCGv v)
+{
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_shli_tl(t0, v, 31);
+ tcg_gen_sari_tl(t0, t0, 31);
+ tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC));
+ tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR],
+ ~(MSR_C | MSR_CC));
+ tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0);
+ tcg_temp_free(t0);
+}
+
+/* True if ALU operand b is a small immediate that may deserve
+ faster treatment. */
+static inline int dec_alu_op_b_is_small_imm(DisasContext *dc)
+{
+ /* Immediate insn without the imm prefix ? */
+ return dc->type_b && !(dc->tb_flags & IMM_FLAG);
+}
+
+static inline TCGv *dec_alu_op_b(DisasContext *dc)
+{
+ if (dc->type_b) {
+ if (dc->tb_flags & IMM_FLAG)
+ tcg_gen_ori_tl(env_imm, env_imm, dc->imm);
+ else
+ tcg_gen_movi_tl(env_imm, (int32_t)((int16_t)dc->imm));
+ return &env_imm;
+ } else
+ return &cpu_R[dc->rb];
+}
+
+static void dec_add(DisasContext *dc)
+{
+ unsigned int k, c;
+ TCGv cf;
+
+ k = dc->opcode & 4;
+ c = dc->opcode & 2;
+
+ LOG_DIS("add%s%s%s r%d r%d r%d\n",
+ dc->type_b ? "i" : "", k ? "k" : "", c ? "c" : "",
+ dc->rd, dc->ra, dc->rb);
+
+ /* Take care of the easy cases first. */
+ if (k) {
+ /* k - keep carry, no need to update MSR. */
+ /* If rd == r0, it's a nop. */
+ if (dc->rd) {
+ tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+
+ if (c) {
+ /* c - Add carry into the result. */
+ cf = tcg_temp_new();
+
+ read_carry(dc, cf);
+ tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+ tcg_temp_free(cf);
+ }
+ }
+ return;
+ }
+
+ /* From now on, we can assume k is zero. So we need to update MSR. */
+ /* Extract carry. */
+ cf = tcg_temp_new();
+ if (c) {
+ read_carry(dc, cf);
+ } else {
+ tcg_gen_movi_tl(cf, 0);
+ }
+
+ if (dc->rd) {
+ TCGv ncf = tcg_temp_new();
+ gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf);
+ tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+ tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+ write_carry(dc, ncf);
+ tcg_temp_free(ncf);
+ } else {
+ gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf);
+ write_carry(dc, cf);
+ }
+ tcg_temp_free(cf);
+}
+
+static void dec_sub(DisasContext *dc)
+{
+ unsigned int u, cmp, k, c;
+ TCGv cf, na;
+
+ u = dc->imm & 2;
+ k = dc->opcode & 4;
+ c = dc->opcode & 2;
+ cmp = (dc->imm & 1) && (!dc->type_b) && k;
+
+ if (cmp) {
+ LOG_DIS("cmp%s r%d, r%d ir=%x\n", u ? "u" : "", dc->rd, dc->ra, dc->ir);
+ if (dc->rd) {
+ if (u)
+ gen_helper_cmpu(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ else
+ gen_helper_cmp(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ }
+ return;
+ }
+
+ LOG_DIS("sub%s%s r%d, r%d r%d\n",
+ k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb);
+
+ /* Take care of the easy cases first. */
+ if (k) {
+ /* k - keep carry, no need to update MSR. */
+ /* If rd == r0, it's a nop. */
+ if (dc->rd) {
+ tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]);
+
+ if (c) {
+ /* c - Add carry into the result. */
+ cf = tcg_temp_new();
+
+ read_carry(dc, cf);
+ tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+ tcg_temp_free(cf);
+ }
+ }
+ return;
+ }
+
+ /* From now on, we can assume k is zero. So we need to update MSR. */
+ /* Extract carry. And complement a into na. */
+ cf = tcg_temp_new();
+ na = tcg_temp_new();
+ if (c) {
+ read_carry(dc, cf);
+ } else {
+ tcg_gen_movi_tl(cf, 1);
+ }
+
+ /* d = b + ~a + c. carry defaults to 1. */
+ tcg_gen_not_tl(na, cpu_R[dc->ra]);
+
+ if (dc->rd) {
+ TCGv ncf = tcg_temp_new();
+ gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf);
+ tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc)));
+ tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf);
+ write_carry(dc, ncf);
+ tcg_temp_free(ncf);
+ } else {
+ gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf);
+ write_carry(dc, cf);
+ }
+ tcg_temp_free(cf);
+ tcg_temp_free(na);
+}
+
+static void dec_pattern(DisasContext *dc)
+{
+ unsigned int mode;
+ int l1;
+
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
+ && !((dc->env->pvr.regs[2] & PVR2_USE_PCMP_INSTR))) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+
+ mode = dc->opcode & 3;
+ switch (mode) {
+ case 0:
+ /* pcmpbf. */
+ LOG_DIS("pcmpbf r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ if (dc->rd)
+ gen_helper_pcmpbf(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 2:
+ LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ if (dc->rd) {
+ TCGv t0 = tcg_temp_local_new();
+ l1 = gen_new_label();
+ tcg_gen_movi_tl(t0, 1);
+ tcg_gen_brcond_tl(TCG_COND_EQ,
+ cpu_R[dc->ra], cpu_R[dc->rb], l1);
+ tcg_gen_movi_tl(t0, 0);
+ gen_set_label(l1);
+ tcg_gen_mov_tl(cpu_R[dc->rd], t0);
+ tcg_temp_free(t0);
+ }
+ break;
+ case 3:
+ LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ l1 = gen_new_label();
+ if (dc->rd) {
+ TCGv t0 = tcg_temp_local_new();
+ tcg_gen_movi_tl(t0, 1);
+ tcg_gen_brcond_tl(TCG_COND_NE,
+ cpu_R[dc->ra], cpu_R[dc->rb], l1);
+ tcg_gen_movi_tl(t0, 0);
+ gen_set_label(l1);
+ tcg_gen_mov_tl(cpu_R[dc->rd], t0);
+ tcg_temp_free(t0);
+ }
+ break;
+ default:
+ cpu_abort(dc->env,
+ "unsupported pattern insn opcode=%x\n", dc->opcode);
+ break;
+ }
+}
+
+static void dec_and(DisasContext *dc)
+{
+ unsigned int not;
+
+ if (!dc->type_b && (dc->imm & (1 << 10))) {
+ dec_pattern(dc);
+ return;
+ }
+
+ not = dc->opcode & (1 << 1);
+ LOG_DIS("and%s\n", not ? "n" : "");
+
+ if (!dc->rd)
+ return;
+
+ if (not) {
+ TCGv t = tcg_temp_new();
+ tcg_gen_not_tl(t, *(dec_alu_op_b(dc)));
+ tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], t);
+ tcg_temp_free(t);
+ } else
+ tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+}
+
+static void dec_or(DisasContext *dc)
+{
+ if (!dc->type_b && (dc->imm & (1 << 10))) {
+ dec_pattern(dc);
+ return;
+ }
+
+ LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm);
+ if (dc->rd)
+ tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+}
+
+static void dec_xor(DisasContext *dc)
+{
+ if (!dc->type_b && (dc->imm & (1 << 10))) {
+ dec_pattern(dc);
+ return;
+ }
+
+ LOG_DIS("xor r%d\n", dc->rd);
+ if (dc->rd)
+ tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+}
+
+static inline void msr_read(DisasContext *dc, TCGv d)
+{
+ tcg_gen_mov_tl(d, cpu_SR[SR_MSR]);
+}
+
+static inline void msr_write(DisasContext *dc, TCGv v)
+{
+ dc->cpustate_changed = 1;
+ tcg_gen_mov_tl(cpu_SR[SR_MSR], v);
+ /* PVR, we have a processor version register. */
+ tcg_gen_ori_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], (1 << 10));
+}
+
+static void dec_msr(DisasContext *dc)
+{
+ TCGv t0, t1;
+ unsigned int sr, to, rn;
+ int mem_index = cpu_mmu_index(dc->env);
+
+ sr = dc->imm & ((1 << 14) - 1);
+ to = dc->imm & (1 << 14);
+ dc->type_b = 1;
+ if (to)
+ dc->cpustate_changed = 1;
+
+ /* msrclr and msrset. */
+ if (!(dc->imm & (1 << 15))) {
+ unsigned int clr = dc->ir & (1 << 16);
+
+ LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set",
+ dc->rd, dc->imm);
+
+ if (!(dc->env->pvr.regs[2] & PVR2_USE_MSR_INSTR)) {
+ /* nop??? */
+ return;
+ }
+
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX && (dc->imm != 4 && dc->imm != 0)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ if (dc->rd)
+ msr_read(dc, cpu_R[dc->rd]);
+
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ msr_read(dc, t0);
+ tcg_gen_mov_tl(t1, *(dec_alu_op_b(dc)));
+
+ if (clr) {
+ tcg_gen_not_tl(t1, t1);
+ tcg_gen_and_tl(t0, t0, t1);
+ } else
+ tcg_gen_or_tl(t0, t0, t1);
+ msr_write(dc, t0);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4);
+ dc->is_jmp = DISAS_UPDATE;
+ return;
+ }
+
+ if (to) {
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+ }
+
+#if !defined(CONFIG_USER_ONLY)
+ /* Catch read/writes to the mmu block. */
+ if ((sr & ~0xff) == 0x1000) {
+ sr &= 7;
+ LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm);
+ if (to)
+ gen_helper_mmu_write(tcg_const_tl(sr), cpu_R[dc->ra]);
+ else
+ gen_helper_mmu_read(cpu_R[dc->rd], tcg_const_tl(sr));
+ return;
+ }
+#endif
+
+ if (to) {
+ LOG_DIS("m%ss sr%x r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm);
+ switch (sr) {
+ case 0:
+ break;
+ case 1:
+ msr_write(dc, cpu_R[dc->ra]);
+ break;
+ case 0x3:
+ tcg_gen_mov_tl(cpu_SR[SR_EAR], cpu_R[dc->ra]);
+ break;
+ case 0x5:
+ tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]);
+ break;
+ case 0x7:
+ tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31);
+ break;
+ default:
+ cpu_abort(dc->env, "unknown mts reg %x\n", sr);
+ break;
+ }
+ } else {
+ LOG_DIS("m%ss r%d sr%x imm=%x\n", to ? "t" : "f", dc->rd, sr, dc->imm);
+
+ switch (sr) {
+ case 0:
+ tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc);
+ break;
+ case 1:
+ msr_read(dc, cpu_R[dc->rd]);
+ break;
+ case 0x3:
+ tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_EAR]);
+ break;
+ case 0x5:
+ tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]);
+ break;
+ case 0x7:
+ tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]);
+ break;
+ case 0xb:
+ tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]);
+ break;
+ case 0x2000:
+ case 0x2001:
+ case 0x2002:
+ case 0x2003:
+ case 0x2004:
+ case 0x2005:
+ case 0x2006:
+ case 0x2007:
+ case 0x2008:
+ case 0x2009:
+ case 0x200a:
+ case 0x200b:
+ case 0x200c:
+ rn = sr & 0xf;
+ tcg_gen_ld_tl(cpu_R[dc->rd],
+ cpu_env, offsetof(CPUState, pvr.regs[rn]));
+ break;
+ default:
+ cpu_abort(dc->env, "unknown mfs reg %x\n", sr);
+ break;
+ }
+ }
+
+ if (dc->rd == 0) {
+ tcg_gen_movi_tl(cpu_R[0], 0);
+ }
+}
+
+/* 64-bit signed mul, lower result in d and upper in d2. */
+static void t_gen_muls(TCGv d, TCGv d2, TCGv a, TCGv b)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_i32_i64(t0, a);
+ tcg_gen_ext_i32_i64(t1, b);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_trunc_i64_i32(d, t0);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(d2, t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+/* 64-bit unsigned muls, lower result in d and upper in d2. */
+static void t_gen_mulu(TCGv d, TCGv d2, TCGv a, TCGv b)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(t0, a);
+ tcg_gen_extu_i32_i64(t1, b);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_trunc_i64_i32(d, t0);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(d2, t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+/* Multiplier unit. */
+static void dec_mul(DisasContext *dc)
+{
+ TCGv d[2];
+ unsigned int subcode;
+
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
+ && !(dc->env->pvr.regs[0] & PVR0_USE_HW_MUL_MASK)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ subcode = dc->imm & 3;
+ d[0] = tcg_temp_new();
+ d[1] = tcg_temp_new();
+
+ if (dc->type_b) {
+ LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm);
+ t_gen_mulu(cpu_R[dc->rd], d[1], cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+ goto done;
+ }
+
+ /* mulh, mulhsu and mulhu are not available if C_USE_HW_MUL is < 2. */
+ if (subcode >= 1 && subcode <= 3
+ && !((dc->env->pvr.regs[2] & PVR2_USE_MUL64_MASK))) {
+ /* nop??? */
+ }
+
+ switch (subcode) {
+ case 0:
+ LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ t_gen_mulu(cpu_R[dc->rd], d[1], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 1:
+ LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ t_gen_muls(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 2:
+ LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ t_gen_muls(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 3:
+ LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb);
+ t_gen_mulu(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ default:
+ cpu_abort(dc->env, "unknown MUL insn %x\n", subcode);
+ break;
+ }
+done:
+ tcg_temp_free(d[0]);
+ tcg_temp_free(d[1]);
+}
+
+/* Div unit. */
+static void dec_div(DisasContext *dc)
+{
+ unsigned int u;
+
+ u = dc->imm & 2;
+ LOG_DIS("div\n");
+
+ if ((dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
+ && !((dc->env->pvr.regs[0] & PVR0_USE_DIV_MASK))) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+
+ if (u)
+ gen_helper_divu(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]);
+ else
+ gen_helper_divs(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]);
+ if (!dc->rd)
+ tcg_gen_movi_tl(cpu_R[dc->rd], 0);
+}
+
+static void dec_barrel(DisasContext *dc)
+{
+ TCGv t0;
+ unsigned int s, t;
+
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
+ && !(dc->env->pvr.regs[0] & PVR0_USE_BARREL_MASK)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ s = dc->imm & (1 << 10);
+ t = dc->imm & (1 << 9);
+
+ LOG_DIS("bs%s%s r%d r%d r%d\n",
+ s ? "l" : "r", t ? "a" : "l", dc->rd, dc->ra, dc->rb);
+
+ t0 = tcg_temp_new();
+
+ tcg_gen_mov_tl(t0, *(dec_alu_op_b(dc)));
+ tcg_gen_andi_tl(t0, t0, 31);
+
+ if (s)
+ tcg_gen_shl_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0);
+ else {
+ if (t)
+ tcg_gen_sar_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0);
+ else
+ tcg_gen_shr_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0);
+ }
+}
+
+static void dec_bit(DisasContext *dc)
+{
+ TCGv t0, t1;
+ unsigned int op;
+ int mem_index = cpu_mmu_index(dc->env);
+
+ op = dc->ir & ((1 << 8) - 1);
+ switch (op) {
+ case 0x21:
+ /* src. */
+ t0 = tcg_temp_new();
+
+ LOG_DIS("src r%d r%d\n", dc->rd, dc->ra);
+ tcg_gen_andi_tl(t0, cpu_R[dc->ra], 1);
+ if (dc->rd) {
+ t1 = tcg_temp_new();
+ read_carry(dc, t1);
+ tcg_gen_shli_tl(t1, t1, 31);
+
+ tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1);
+ tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t1);
+ tcg_temp_free(t1);
+ }
+
+ /* Update carry. */
+ write_carry(dc, t0);
+ tcg_temp_free(t0);
+ break;
+
+ case 0x1:
+ case 0x41:
+ /* srl. */
+ t0 = tcg_temp_new();
+ LOG_DIS("srl r%d r%d\n", dc->rd, dc->ra);
+
+ /* Update carry. */
+ tcg_gen_andi_tl(t0, cpu_R[dc->ra], 1);
+ write_carry(dc, t0);
+ tcg_temp_free(t0);
+ if (dc->rd) {
+ if (op == 0x41)
+ tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1);
+ else
+ tcg_gen_sari_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1);
+ }
+ break;
+ case 0x60:
+ LOG_DIS("ext8s r%d r%d\n", dc->rd, dc->ra);
+ tcg_gen_ext8s_i32(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+ case 0x61:
+ LOG_DIS("ext16s r%d r%d\n", dc->rd, dc->ra);
+ tcg_gen_ext16s_i32(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+ case 0x64:
+ case 0x66:
+ case 0x74:
+ case 0x76:
+ /* wdc. */
+ LOG_DIS("wdc r%d\n", dc->ra);
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+ break;
+ case 0x68:
+ /* wic. */
+ LOG_DIS("wic r%d\n", dc->ra);
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+ break;
+ default:
+ cpu_abort(dc->env, "unknown bit oc=%x op=%x rd=%d ra=%d rb=%d\n",
+ dc->pc, op, dc->rd, dc->ra, dc->rb);
+ break;
+ }
+}
+
+static inline void sync_jmpstate(DisasContext *dc)
+{
+ if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) {
+ if (dc->jmp == JMP_DIRECT) {
+ tcg_gen_movi_tl(env_btaken, 1);
+ }
+ dc->jmp = JMP_INDIRECT;
+ tcg_gen_movi_tl(env_btarget, dc->jmp_pc);
+ }
+}
+
+static void dec_imm(DisasContext *dc)
+{
+ LOG_DIS("imm %x\n", dc->imm << 16);
+ tcg_gen_movi_tl(env_imm, (dc->imm << 16));
+ dc->tb_flags |= IMM_FLAG;
+ dc->clear_imm = 0;
+}
+
+static inline void gen_load(DisasContext *dc, TCGv dst, TCGv addr,
+ unsigned int size)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ if (size == 1) {
+ tcg_gen_qemu_ld8u(dst, addr, mem_index);
+ } else if (size == 2) {
+ tcg_gen_qemu_ld16u(dst, addr, mem_index);
+ } else if (size == 4) {
+ tcg_gen_qemu_ld32u(dst, addr, mem_index);
+ } else
+ cpu_abort(dc->env, "Incorrect load size %d\n", size);
+}
+
+static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t)
+{
+ unsigned int extimm = dc->tb_flags & IMM_FLAG;
+
+ /* Treat the common cases first. */
+ if (!dc->type_b) {
+ /* If any of the regs is r0, return a ptr to the other. */
+ if (dc->ra == 0) {
+ return &cpu_R[dc->rb];
+ } else if (dc->rb == 0) {
+ return &cpu_R[dc->ra];
+ }
+
+ *t = tcg_temp_new();
+ tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]);
+ return t;
+ }
+ /* Immediate. */
+ if (!extimm) {
+ if (dc->imm == 0) {
+ return &cpu_R[dc->ra];
+ }
+ *t = tcg_temp_new();
+ tcg_gen_movi_tl(*t, (int32_t)((int16_t)dc->imm));
+ tcg_gen_add_tl(*t, cpu_R[dc->ra], *t);
+ } else {
+ *t = tcg_temp_new();
+ tcg_gen_add_tl(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+ }
+
+ return t;
+}
+
+static inline void dec_byteswap(DisasContext *dc, TCGv dst, TCGv src, int size)
+{
+ if (size == 4) {
+ tcg_gen_bswap32_tl(dst, src);
+ } else if (size == 2) {
+ TCGv t = tcg_temp_new();
+
+ /* bswap16 assumes the high bits are zero. */
+ tcg_gen_andi_tl(t, src, 0xffff);
+ tcg_gen_bswap16_tl(dst, t);
+ tcg_temp_free(t);
+ } else {
+ /* Ignore.
+ cpu_abort(dc->env, "Invalid ldst byteswap size %d\n", size);
+ */
+ }
+}
+
+static void dec_load(DisasContext *dc)
+{
+ TCGv t, *addr;
+ unsigned int size, rev = 0;
+
+ size = 1 << (dc->opcode & 3);
+
+ if (!dc->type_b) {
+ rev = (dc->ir >> 9) & 1;
+ }
+
+ if (size > 4 && (dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ LOG_DIS("l%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "");
+
+ t_sync_flags(dc);
+ addr = compute_ldst_addr(dc, &t);
+
+ /*
+ * When doing reverse accesses we need to do two things.
+ *
+ * 1. Reverse the address wrt endianess.
+ * 2. Byteswap the data lanes on the way back into the CPU core.
+ */
+ if (rev && size != 4) {
+ /* Endian reverse the address. t is addr. */
+ switch (size) {
+ case 1:
+ {
+ /* 00 -> 11
+ 01 -> 10
+ 10 -> 10
+ 11 -> 00 */
+ TCGv low = tcg_temp_new();
+
+ /* Force addr into the temp. */
+ if (addr != &t) {
+ t = tcg_temp_new();
+ tcg_gen_mov_tl(t, *addr);
+ addr = &t;
+ }
+
+ tcg_gen_andi_tl(low, t, 3);
+ tcg_gen_sub_tl(low, tcg_const_tl(3), low);
+ tcg_gen_andi_tl(t, t, ~3);
+ tcg_gen_or_tl(t, t, low);
+ tcg_gen_mov_tl(env_imm, t);
+ tcg_temp_free(low);
+ break;
+ }
+
+ case 2:
+ /* 00 -> 10
+ 10 -> 00. */
+ /* Force addr into the temp. */
+ if (addr != &t) {
+ t = tcg_temp_new();
+ tcg_gen_xori_tl(t, *addr, 2);
+ addr = &t;
+ } else {
+ tcg_gen_xori_tl(t, t, 2);
+ }
+ break;
+ default:
+ cpu_abort(dc->env, "Invalid reverse size\n");
+ break;
+ }
+ }
+
+ /* If we get a fault on a dslot, the jmpstate better be in sync. */
+ sync_jmpstate(dc);
+
+ /* Verify alignment if needed. */
+ if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {
+ TCGv v = tcg_temp_new();
+
+ /*
+ * Microblaze gives MMU faults priority over faults due to
+ * unaligned addresses. That's why we speculatively do the load
+ * into v. If the load succeeds, we verify alignment of the
+ * address and if that succeeds we write into the destination reg.
+ */
+ gen_load(dc, v, *addr, size);
+
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
+ gen_helper_memalign(*addr, tcg_const_tl(dc->rd),
+ tcg_const_tl(0), tcg_const_tl(size - 1));
+ if (dc->rd) {
+ if (rev) {
+ dec_byteswap(dc, cpu_R[dc->rd], v, size);
+ } else {
+ tcg_gen_mov_tl(cpu_R[dc->rd], v);
+ }
+ }
+ tcg_temp_free(v);
+ } else {
+ if (dc->rd) {
+ gen_load(dc, cpu_R[dc->rd], *addr, size);
+ if (rev) {
+ dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size);
+ }
+ } else {
+ /* We are loading into r0, no need to reverse. */
+ gen_load(dc, env_imm, *addr, size);
+ }
+ }
+
+ if (addr == &t)
+ tcg_temp_free(t);
+}
+
+static void gen_store(DisasContext *dc, TCGv addr, TCGv val,
+ unsigned int size)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ if (size == 1)
+ tcg_gen_qemu_st8(val, addr, mem_index);
+ else if (size == 2) {
+ tcg_gen_qemu_st16(val, addr, mem_index);
+ } else if (size == 4) {
+ tcg_gen_qemu_st32(val, addr, mem_index);
+ } else
+ cpu_abort(dc->env, "Incorrect store size %d\n", size);
+}
+
+static void dec_store(DisasContext *dc)
+{
+ TCGv t, *addr;
+ unsigned int size, rev = 0;
+
+ size = 1 << (dc->opcode & 3);
+ if (!dc->type_b) {
+ rev = (dc->ir >> 9) & 1;
+ }
+
+ if (size > 4 && (dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ LOG_DIS("s%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "");
+ t_sync_flags(dc);
+ /* If we get a fault on a dslot, the jmpstate better be in sync. */
+ sync_jmpstate(dc);
+ addr = compute_ldst_addr(dc, &t);
+
+ if (rev && size != 4) {
+ /* Endian reverse the address. t is addr. */
+ switch (size) {
+ case 1:
+ {
+ /* 00 -> 11
+ 01 -> 10
+ 10 -> 10
+ 11 -> 00 */
+ TCGv low = tcg_temp_new();
+
+ /* Force addr into the temp. */
+ if (addr != &t) {
+ t = tcg_temp_new();
+ tcg_gen_mov_tl(t, *addr);
+ addr = &t;
+ }
+
+ tcg_gen_andi_tl(low, t, 3);
+ tcg_gen_sub_tl(low, tcg_const_tl(3), low);
+ tcg_gen_andi_tl(t, t, ~3);
+ tcg_gen_or_tl(t, t, low);
+ tcg_gen_mov_tl(env_imm, t);
+ tcg_temp_free(low);
+ break;
+ }
+
+ case 2:
+ /* 00 -> 10
+ 10 -> 00. */
+ /* Force addr into the temp. */
+ if (addr != &t) {
+ t = tcg_temp_new();
+ tcg_gen_xori_tl(t, *addr, 2);
+ addr = &t;
+ } else {
+ tcg_gen_xori_tl(t, t, 2);
+ }
+ break;
+ default:
+ cpu_abort(dc->env, "Invalid reverse size\n");
+ break;
+ }
+
+ if (size != 1) {
+ TCGv bs_data = tcg_temp_new();
+ dec_byteswap(dc, bs_data, cpu_R[dc->rd], size);
+ gen_store(dc, *addr, bs_data, size);
+ tcg_temp_free(bs_data);
+ } else {
+ gen_store(dc, *addr, cpu_R[dc->rd], size);
+ }
+ } else {
+ if (rev) {
+ TCGv bs_data = tcg_temp_new();
+ dec_byteswap(dc, bs_data, cpu_R[dc->rd], size);
+ gen_store(dc, *addr, bs_data, size);
+ tcg_temp_free(bs_data);
+ } else {
+ gen_store(dc, *addr, cpu_R[dc->rd], size);
+ }
+ }
+
+ /* Verify alignment if needed. */
+ if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) {
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
+ /* FIXME: if the alignment is wrong, we should restore the value
+ * in memory. One possible way to acheive this is to probe
+ * the MMU prior to the memaccess, thay way we could put
+ * the alignment checks in between the probe and the mem
+ * access.
+ */
+ gen_helper_memalign(*addr, tcg_const_tl(dc->rd),
+ tcg_const_tl(1), tcg_const_tl(size - 1));
+ }
+
+ if (addr == &t)
+ tcg_temp_free(t);
+}
+
+static inline void eval_cc(DisasContext *dc, unsigned int cc,
+ TCGv d, TCGv a, TCGv b)
+{
+ switch (cc) {
+ case CC_EQ:
+ tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b);
+ break;
+ case CC_NE:
+ tcg_gen_setcond_tl(TCG_COND_NE, d, a, b);
+ break;
+ case CC_LT:
+ tcg_gen_setcond_tl(TCG_COND_LT, d, a, b);
+ break;
+ case CC_LE:
+ tcg_gen_setcond_tl(TCG_COND_LE, d, a, b);
+ break;
+ case CC_GE:
+ tcg_gen_setcond_tl(TCG_COND_GE, d, a, b);
+ break;
+ case CC_GT:
+ tcg_gen_setcond_tl(TCG_COND_GT, d, a, b);
+ break;
+ default:
+ cpu_abort(dc->env, "Unknown condition code %x.\n", cc);
+ break;
+ }
+}
+
+static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false)
+{
+ int l1;
+
+ l1 = gen_new_label();
+ /* Conditional jmp. */
+ tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1);
+ tcg_gen_mov_tl(cpu_SR[SR_PC], pc_true);
+ gen_set_label(l1);
+}
+
+static void dec_bcc(DisasContext *dc)
+{
+ unsigned int cc;
+ unsigned int dslot;
+
+ cc = EXTRACT_FIELD(dc->ir, 21, 23);
+ dslot = dc->ir & (1 << 25);
+ LOG_DIS("bcc%s r%d %x\n", dslot ? "d" : "", dc->ra, dc->imm);
+
+ dc->delayed_branch = 1;
+ if (dslot) {
+ dc->delayed_branch = 2;
+ dc->tb_flags |= D_FLAG;
+ tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)),
+ cpu_env, offsetof(CPUState, bimm));
+ }
+
+ if (dec_alu_op_b_is_small_imm(dc)) {
+ int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */
+
+ tcg_gen_movi_tl(env_btarget, dc->pc + offset);
+ dc->jmp = JMP_DIRECT_CC;
+ dc->jmp_pc = dc->pc + offset;
+ } else {
+ dc->jmp = JMP_INDIRECT;
+ tcg_gen_movi_tl(env_btarget, dc->pc);
+ tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
+ }
+ eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0));
+}
+
+static void dec_br(DisasContext *dc)
+{
+ unsigned int dslot, link, abs;
+ int mem_index = cpu_mmu_index(dc->env);
+
+ dslot = dc->ir & (1 << 20);
+ abs = dc->ir & (1 << 19);
+ link = dc->ir & (1 << 18);
+ LOG_DIS("br%s%s%s%s imm=%x\n",
+ abs ? "a" : "", link ? "l" : "",
+ dc->type_b ? "i" : "", dslot ? "d" : "",
+ dc->imm);
+
+ dc->delayed_branch = 1;
+ if (dslot) {
+ dc->delayed_branch = 2;
+ dc->tb_flags |= D_FLAG;
+ tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)),
+ cpu_env, offsetof(CPUState, bimm));
+ }
+ if (link && dc->rd)
+ tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc);
+
+ dc->jmp = JMP_INDIRECT;
+ if (abs) {
+ tcg_gen_movi_tl(env_btaken, 1);
+ tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc)));
+ if (link && !dslot) {
+ if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18))
+ t_gen_raise_exception(dc, EXCP_BREAK);
+ if (dc->imm == 0) {
+ if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ t_gen_raise_exception(dc, EXCP_DEBUG);
+ }
+ }
+ } else {
+ if (dec_alu_op_b_is_small_imm(dc)) {
+ dc->jmp = JMP_DIRECT;
+ dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm);
+ } else {
+ tcg_gen_movi_tl(env_btaken, 1);
+ tcg_gen_movi_tl(env_btarget, dc->pc);
+ tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc)));
+ }
+ }
+}
+
+static inline void do_rti(DisasContext *dc)
+{
+ TCGv t0, t1;
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_SR[SR_MSR], 1);
+ tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_IE);
+ tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM));
+
+ tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM));
+ tcg_gen_or_tl(t1, t1, t0);
+ msr_write(dc, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ dc->tb_flags &= ~DRTI_FLAG;
+}
+
+static inline void do_rtb(DisasContext *dc)
+{
+ TCGv t0, t1;
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_SR[SR_MSR], ~MSR_BIP);
+ tcg_gen_shri_tl(t0, t1, 1);
+ tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM));
+
+ tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM));
+ tcg_gen_or_tl(t1, t1, t0);
+ msr_write(dc, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ dc->tb_flags &= ~DRTB_FLAG;
+}
+
+static inline void do_rte(DisasContext *dc)
+{
+ TCGv t0, t1;
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+
+ tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_EE);
+ tcg_gen_andi_tl(t1, t1, ~MSR_EIP);
+ tcg_gen_shri_tl(t0, t1, 1);
+ tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM));
+
+ tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM));
+ tcg_gen_or_tl(t1, t1, t0);
+ msr_write(dc, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ dc->tb_flags &= ~DRTE_FLAG;
+}
+
+static void dec_rts(DisasContext *dc)
+{
+ unsigned int b_bit, i_bit, e_bit;
+ int mem_index = cpu_mmu_index(dc->env);
+
+ i_bit = dc->ir & (1 << 21);
+ b_bit = dc->ir & (1 << 22);
+ e_bit = dc->ir & (1 << 23);
+
+ dc->delayed_branch = 2;
+ dc->tb_flags |= D_FLAG;
+ tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)),
+ cpu_env, offsetof(CPUState, bimm));
+
+ if (i_bit) {
+ LOG_DIS("rtid ir=%x\n", dc->ir);
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+ dc->tb_flags |= DRTI_FLAG;
+ } else if (b_bit) {
+ LOG_DIS("rtbd ir=%x\n", dc->ir);
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+ dc->tb_flags |= DRTB_FLAG;
+ } else if (e_bit) {
+ LOG_DIS("rted ir=%x\n", dc->ir);
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && mem_index == MMU_USER_IDX) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+ dc->tb_flags |= DRTE_FLAG;
+ } else
+ LOG_DIS("rts ir=%x\n", dc->ir);
+
+ dc->jmp = JMP_INDIRECT;
+ tcg_gen_movi_tl(env_btaken, 1);
+ tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc)));
+}
+
+static int dec_check_fpuv2(DisasContext *dc)
+{
+ int r;
+
+ r = dc->env->pvr.regs[2] & PVR2_USE_FPU2_MASK;
+
+ if (!r && (dc->tb_flags & MSR_EE_FLAG)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ }
+ return r;
+}
+
+static void dec_fpu(DisasContext *dc)
+{
+ unsigned int fpu_insn;
+
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
+ && !((dc->env->pvr.regs[2] & PVR2_USE_FPU_MASK))) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ fpu_insn = (dc->ir >> 7) & 7;
+
+ switch (fpu_insn) {
+ case 0:
+ gen_helper_fadd(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 1:
+ gen_helper_frsub(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 2:
+ gen_helper_fmul(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 3:
+ gen_helper_fdiv(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+
+ case 4:
+ switch ((dc->ir >> 4) & 7) {
+ case 0:
+ gen_helper_fcmp_un(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 1:
+ gen_helper_fcmp_lt(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 2:
+ gen_helper_fcmp_eq(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 3:
+ gen_helper_fcmp_le(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 4:
+ gen_helper_fcmp_gt(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 5:
+ gen_helper_fcmp_ne(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ case 6:
+ gen_helper_fcmp_ge(cpu_R[dc->rd],
+ cpu_R[dc->ra], cpu_R[dc->rb]);
+ break;
+ default:
+ qemu_log ("unimplemented fcmp fpu_insn=%x pc=%x opc=%x\n",
+ fpu_insn, dc->pc, dc->opcode);
+ dc->abort_at_next_insn = 1;
+ break;
+ }
+ break;
+
+ case 5:
+ if (!dec_check_fpuv2(dc)) {
+ return;
+ }
+ gen_helper_flt(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+
+ case 6:
+ if (!dec_check_fpuv2(dc)) {
+ return;
+ }
+ gen_helper_fint(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+
+ case 7:
+ if (!dec_check_fpuv2(dc)) {
+ return;
+ }
+ gen_helper_fsqrt(cpu_R[dc->rd], cpu_R[dc->ra]);
+ break;
+
+ default:
+ qemu_log ("unimplemented FPU insn fpu_insn=%x pc=%x opc=%x\n",
+ fpu_insn, dc->pc, dc->opcode);
+ dc->abort_at_next_insn = 1;
+ break;
+ }
+}
+
+static void dec_null(DisasContext *dc)
+{
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+ qemu_log ("unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode);
+ dc->abort_at_next_insn = 1;
+}
+
+static struct decoder_info {
+ struct {
+ uint32_t bits;
+ uint32_t mask;
+ };
+ void (*dec)(DisasContext *dc);
+} decinfo[] = {
+ {DEC_ADD, dec_add},
+ {DEC_SUB, dec_sub},
+ {DEC_AND, dec_and},
+ {DEC_XOR, dec_xor},
+ {DEC_OR, dec_or},
+ {DEC_BIT, dec_bit},
+ {DEC_BARREL, dec_barrel},
+ {DEC_LD, dec_load},
+ {DEC_ST, dec_store},
+ {DEC_IMM, dec_imm},
+ {DEC_BR, dec_br},
+ {DEC_BCC, dec_bcc},
+ {DEC_RTS, dec_rts},
+ {DEC_FPU, dec_fpu},
+ {DEC_MUL, dec_mul},
+ {DEC_DIV, dec_div},
+ {DEC_MSR, dec_msr},
+ {{0, 0}, dec_null}
+};
+
+static inline void decode(DisasContext *dc)
+{
+ uint32_t ir;
+ int i;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(dc->pc);
+
+ dc->ir = ir = ldl_code(dc->pc);
+ LOG_DIS("%8.8x\t", dc->ir);
+
+ if (dc->ir)
+ dc->nr_nops = 0;
+ else {
+ if ((dc->tb_flags & MSR_EE_FLAG)
+ && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)
+ && (dc->env->pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK)) {
+ tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP);
+ t_gen_raise_exception(dc, EXCP_HW_EXCP);
+ return;
+ }
+
+ LOG_DIS("nr_nops=%d\t", dc->nr_nops);
+ dc->nr_nops++;
+ if (dc->nr_nops > 4)
+ cpu_abort(dc->env, "fetching nop sequence\n");
+ }
+ /* bit 2 seems to indicate insn type. */
+ dc->type_b = ir & (1 << 29);
+
+ dc->opcode = EXTRACT_FIELD(ir, 26, 31);
+ dc->rd = EXTRACT_FIELD(ir, 21, 25);
+ dc->ra = EXTRACT_FIELD(ir, 16, 20);
+ dc->rb = EXTRACT_FIELD(ir, 11, 15);
+ dc->imm = EXTRACT_FIELD(ir, 0, 15);
+
+ /* Large switch for all insns. */
+ for (i = 0; i < ARRAY_SIZE(decinfo); i++) {
+ if ((dc->opcode & decinfo[i].mask) == decinfo[i].bits) {
+ decinfo[i].dec(dc);
+ break;
+ }
+ }
+}
+
+static void check_breakpoint(CPUState *env, DisasContext *dc)
+{
+ CPUBreakpoint *bp;
+
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == dc->pc) {
+ t_gen_raise_exception(dc, EXCP_DEBUG);
+ dc->is_jmp = DISAS_UPDATE;
+ }
+ }
+ }
+}
+
+/* generate intermediate code for basic block 'tb'. */
+static void
+gen_intermediate_code_internal(CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ uint16_t *gen_opc_end;
+ uint32_t pc_start;
+ int j, lj;
+ struct DisasContext ctx;
+ struct DisasContext *dc = &ctx;
+ uint32_t next_page_start, org_flags;
+ target_ulong npc;
+ int num_insns;
+ int max_insns;
+
+ qemu_log_try_set_file(stderr);
+
+ pc_start = tb->pc;
+ dc->env = env;
+ dc->tb = tb;
+ org_flags = dc->synced_flags = dc->tb_flags = tb->flags;
+
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ dc->is_jmp = DISAS_NEXT;
+ dc->jmp = 0;
+ dc->delayed_branch = !!(dc->tb_flags & D_FLAG);
+ if (dc->delayed_branch) {
+ dc->jmp = JMP_INDIRECT;
+ }
+ dc->pc = pc_start;
+ dc->singlestep_enabled = env->singlestep_enabled;
+ dc->cpustate_changed = 0;
+ dc->abort_at_next_insn = 0;
+ dc->nr_nops = 0;
+
+ if (pc_start & 3)
+ cpu_abort(env, "Microblaze: unaligned PC=%x\n", pc_start);
+
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+#if !SIM_COMPAT
+ qemu_log("--------------\n");
+ log_cpu_state(env, 0);
+#endif
+ }
+
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+ lj = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
+ do
+ {
+#if SIM_COMPAT
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
+ gen_helper_debug();
+ }
+#endif
+ check_breakpoint(env, dc);
+
+ 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_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
+ }
+
+ /* Pretty disas. */
+ LOG_DIS("%8.8x:\t", dc->pc);
+
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+
+ dc->clear_imm = 1;
+ decode(dc);
+ if (dc->clear_imm)
+ dc->tb_flags &= ~IMM_FLAG;
+ dc->pc += 4;
+ num_insns++;
+
+ if (dc->delayed_branch) {
+ dc->delayed_branch--;
+ if (!dc->delayed_branch) {
+ if (dc->tb_flags & DRTI_FLAG)
+ do_rti(dc);
+ if (dc->tb_flags & DRTB_FLAG)
+ do_rtb(dc);
+ if (dc->tb_flags & DRTE_FLAG)
+ do_rte(dc);
+ /* Clear the delay slot flag. */
+ dc->tb_flags &= ~D_FLAG;
+ /* If it is a direct jump, try direct chaining. */
+ if (dc->jmp == JMP_INDIRECT) {
+ eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc));
+ dc->is_jmp = DISAS_JUMP;
+ } else if (dc->jmp == JMP_DIRECT) {
+ t_sync_flags(dc);
+ gen_goto_tb(dc, 0, dc->jmp_pc);
+ dc->is_jmp = DISAS_TB_JUMP;
+ } else if (dc->jmp == JMP_DIRECT_CC) {
+ int l1;
+
+ t_sync_flags(dc);
+ l1 = gen_new_label();
+ /* Conditional jmp. */
+ tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1);
+ gen_goto_tb(dc, 1, dc->pc);
+ gen_set_label(l1);
+ gen_goto_tb(dc, 0, dc->jmp_pc);
+
+ dc->is_jmp = DISAS_TB_JUMP;
+ }
+ break;
+ }
+ }
+ if (env->singlestep_enabled)
+ break;
+ } while (!dc->is_jmp && !dc->cpustate_changed
+ && gen_opc_ptr < gen_opc_end
+ && !singlestep
+ && (dc->pc < next_page_start)
+ && num_insns < max_insns);
+
+ npc = dc->pc;
+ if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) {
+ if (dc->tb_flags & D_FLAG) {
+ dc->is_jmp = DISAS_UPDATE;
+ tcg_gen_movi_tl(cpu_SR[SR_PC], npc);
+ sync_jmpstate(dc);
+ } else
+ npc = dc->jmp_pc;
+ }
+
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ /* Force an update if the per-tb cpu state has changed. */
+ if (dc->is_jmp == DISAS_NEXT
+ && (dc->cpustate_changed || org_flags != dc->tb_flags)) {
+ dc->is_jmp = DISAS_UPDATE;
+ tcg_gen_movi_tl(cpu_SR[SR_PC], npc);
+ }
+ t_sync_flags(dc);
+
+ if (unlikely(env->singlestep_enabled)) {
+ t_gen_raise_exception(dc, EXCP_DEBUG);
+ if (dc->is_jmp == DISAS_NEXT)
+ tcg_gen_movi_tl(cpu_SR[SR_PC], npc);
+ } else {
+ switch(dc->is_jmp) {
+ case DISAS_NEXT:
+ gen_goto_tb(dc, 1, npc);
+ break;
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ /* indicate that the hash table must be used
+ to find the next TB */
+ tcg_gen_exit_tb(0);
+ break;
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ }
+ }
+ 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;
+ }
+
+#ifdef DEBUG_DISAS
+#if !SIM_COMPAT
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("\n");
+#if DISAS_GNU
+ log_target_disas(pc_start, dc->pc - pc_start, 0);
+#endif
+ qemu_log("\nisize=%d osize=%td\n",
+ dc->pc - pc_start, gen_opc_ptr - gen_opc_buf);
+ }
+#endif
+#endif
+ assert(!dc->abort_at_next_insn);
+}
+
+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 cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+
+ if (!env || !f)
+ return;
+
+ cpu_fprintf(f, "IN: PC=%x %s\n",
+ env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC]));
+ cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n",
+ env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR],
+ env->debug, env->imm, env->iflags, env->sregs[SR_FSR]);
+ cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n",
+ env->btaken, env->btarget,
+ (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel",
+ (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel",
+ (env->sregs[SR_MSR] & MSR_EIP),
+ (env->sregs[SR_MSR] & MSR_IE));
+
+ for (i = 0; i < 32; i++) {
+ cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]);
+ if ((i + 1) % 4 == 0)
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "\n\n");
+}
+
+CPUState *cpu_mb_init (const char *cpu_model)
+{
+ CPUState *env;
+ static int tcg_initialized = 0;
+ int i;
+
+ env = qemu_mallocz(sizeof(CPUState));
+
+ cpu_exec_init(env);
+ cpu_reset(env);
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+
+ if (tcg_initialized)
+ return env;
+
+ tcg_initialized = 1;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ env_debug = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, debug),
+ "debug0");
+ env_iflags = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, iflags),
+ "iflags");
+ env_imm = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, imm),
+ "imm");
+ env_btarget = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btarget),
+ "btarget");
+ env_btaken = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btaken),
+ "btaken");
+ for (i = 0; i < ARRAY_SIZE(cpu_R); i++) {
+ cpu_R[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, regs[i]),
+ regnames[i]);
+ }
+ for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) {
+ cpu_SR[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, sregs[i]),
+ special_regnames[i]);
+ }
+#define GEN_HELPER 2
+#include "helper.h"
+
+ return env;
+}
+
+void cpu_reset (CPUState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ memset(env, 0, offsetof(CPUMBState, breakpoints));
+ tlb_flush(env, 1);
+
+ env->pvr.regs[0] = PVR0_PVR_FULL_MASK \
+ | PVR0_USE_BARREL_MASK \
+ | PVR0_USE_DIV_MASK \
+ | PVR0_USE_HW_MUL_MASK \
+ | PVR0_USE_EXC_MASK \
+ | PVR0_USE_ICACHE_MASK \
+ | PVR0_USE_DCACHE_MASK \
+ | PVR0_USE_MMU \
+ | (0xb << 8);
+ env->pvr.regs[2] = PVR2_D_OPB_MASK \
+ | PVR2_D_LMB_MASK \
+ | PVR2_I_OPB_MASK \
+ | PVR2_I_LMB_MASK \
+ | PVR2_USE_MSR_INSTR \
+ | PVR2_USE_PCMP_INSTR \
+ | PVR2_USE_BARREL_MASK \
+ | PVR2_USE_DIV_MASK \
+ | PVR2_USE_HW_MUL_MASK \
+ | PVR2_USE_MUL64_MASK \
+ | PVR2_USE_FPU_MASK \
+ | PVR2_USE_FPU2_MASK \
+ | PVR2_FPU_EXC_MASK \
+ | 0;
+ env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */
+ env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17);
+
+#if defined(CONFIG_USER_ONLY)
+ /* start in user mode with interrupts enabled. */
+ env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM;
+ env->pvr.regs[10] = 0x0c000000; /* Spartan 3a dsp. */
+#else
+ env->sregs[SR_MSR] = 0;
+ mmu_init(&env->mmu);
+ env->mmu.c_mmu = 3;
+ env->mmu.c_mmu_tlb_access = 3;
+ env->mmu.c_mmu_zones = 16;
+#endif
+}
+
+void gen_pc_load(CPUState *env, struct TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->sregs[SR_PC] = gen_opc_pc[pc_pos];
+}
diff --git a/target-mips/TODO b/target-mips/TODO
new file mode 100644
index 0000000..9101881
--- /dev/null
+++ b/target-mips/TODO
@@ -0,0 +1,52 @@
+Unsolved issues/bugs in the mips/mipsel backend
+-----------------------------------------------
+
+General
+-------
+- Unimplemented ASEs:
+ - MDMX
+ - SmartMIPS
+ - DSP r1
+ - DSP r2
+- MT ASE only partially implemented and not functional
+- Shadow register support only partially implemented,
+ lacks set switching on interrupt/exception.
+- 34K ITC not implemented.
+- A general lack of documentation, especially for technical internals.
+ Existing documentation is x86-centric.
+- Reverse endianness bit not implemented
+- The TLB emulation is very inefficient:
+ Qemu's softmmu implements a x86-style MMU, with separate entries
+ for read/write/execute, a TLB index which is just a modulo of the
+ virtual address, and a set of TLBs for each user/kernel/supervisor
+ MMU mode.
+ MIPS has a single entry for read/write/execute and only one MMU mode.
+ But it is fully associative with randomized entry indices, and uses
+ up to 256 ASID tags as additional matching criterion (which roughly
+ equates to 256 MMU modes). It also has a global flag which causes
+ entries to match regardless of ASID.
+ To cope with these differences, Qemu currently flushes the TLB at
+ each ASID change. Using the MMU modes to implement ASIDs hinges on
+ implementing the global bit efficiently.
+- save/restore of the CPU state is not implemented (see machine.c).
+
+MIPS64
+------
+- Userland emulation (both n32 and n64) not functional.
+
+"Generic" 4Kc system emulation
+------------------------------
+- Doesn't correspond to any real hardware. Should be removed some day,
+ U-Boot is the last remaining user.
+
+PICA 61 system emulation
+------------------------
+- No framebuffer support yet.
+
+MALTA system emulation
+----------------------
+- We fake firmware support instead of doing the real thing
+- Real firmware (YAMON) falls over when trying to init RAM, presumably
+ due to lacking system controller emulation.
+- Bonito system controller not implemented
+- MSC1 system controller not implemented
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
new file mode 100644
index 0000000..2419aa9
--- /dev/null
+++ b/target-mips/cpu.h
@@ -0,0 +1,659 @@
+#if !defined (__MIPS_CPU_H__)
+#define __MIPS_CPU_H__
+
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE EM_MIPS
+
+#define CPUState struct CPUMIPSState
+
+#include "config.h"
+#include "qemu-common.h"
+#include "mips-defs.h"
+#include "cpu-defs.h"
+#include "softfloat.h"
+
+// uint_fast8_t and uint_fast16_t not in <sys/int_types.h>
+// XXX: move that elsewhere
+#if defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10
+typedef unsigned char uint_fast8_t;
+typedef unsigned int uint_fast16_t;
+#endif
+
+struct CPUMIPSState;
+
+typedef struct r4k_tlb_t r4k_tlb_t;
+struct r4k_tlb_t {
+ target_ulong VPN;
+ uint32_t PageMask;
+ uint_fast8_t ASID;
+ uint_fast16_t G:1;
+ uint_fast16_t C0:3;
+ uint_fast16_t C1:3;
+ uint_fast16_t V0:1;
+ uint_fast16_t V1:1;
+ uint_fast16_t D0:1;
+ uint_fast16_t D1:1;
+ target_ulong PFN[2];
+};
+
+#if !defined(CONFIG_USER_ONLY)
+typedef struct CPUMIPSTLBContext CPUMIPSTLBContext;
+struct CPUMIPSTLBContext {
+ uint32_t nb_tlb;
+ uint32_t tlb_in_use;
+ int (*map_address) (struct CPUMIPSState *env, target_phys_addr_t *physical, int *prot, target_ulong address, int rw, int access_type);
+ void (*helper_tlbwi) (void);
+ void (*helper_tlbwr) (void);
+ void (*helper_tlbp) (void);
+ void (*helper_tlbr) (void);
+ union {
+ struct {
+ r4k_tlb_t tlb[MIPS_TLB_MAX];
+ } r4k;
+ } mmu;
+};
+#endif
+
+typedef union fpr_t fpr_t;
+union fpr_t {
+ float64 fd; /* ieee double precision */
+ float32 fs[2];/* ieee single precision */
+ uint64_t d; /* binary double fixed-point */
+ uint32_t w[2]; /* binary single fixed-point */
+};
+/* define FP_ENDIAN_IDX to access the same location
+ * in the fpr_t union regardless of the host endianess
+ */
+#if defined(HOST_WORDS_BIGENDIAN)
+# define FP_ENDIAN_IDX 1
+#else
+# define FP_ENDIAN_IDX 0
+#endif
+
+typedef struct CPUMIPSFPUContext CPUMIPSFPUContext;
+struct CPUMIPSFPUContext {
+ /* Floating point registers */
+ fpr_t fpr[32];
+ float_status fp_status;
+ /* fpu implementation/revision register (fir) */
+ uint32_t fcr0;
+#define FCR0_F64 22
+#define FCR0_L 21
+#define FCR0_W 20
+#define FCR0_3D 19
+#define FCR0_PS 18
+#define FCR0_D 17
+#define FCR0_S 16
+#define FCR0_PRID 8
+#define FCR0_REV 0
+ /* fcsr */
+ uint32_t fcr31;
+#define SET_FP_COND(num,env) do { ((env).fcr31) |= ((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0)
+#define CLEAR_FP_COND(num,env) do { ((env).fcr31) &= ~((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0)
+#define GET_FP_COND(env) ((((env).fcr31 >> 24) & 0xfe) | (((env).fcr31 >> 23) & 0x1))
+#define GET_FP_CAUSE(reg) (((reg) >> 12) & 0x3f)
+#define GET_FP_ENABLE(reg) (((reg) >> 7) & 0x1f)
+#define GET_FP_FLAGS(reg) (((reg) >> 2) & 0x1f)
+#define SET_FP_CAUSE(reg,v) do { (reg) = ((reg) & ~(0x3f << 12)) | ((v & 0x3f) << 12); } while(0)
+#define SET_FP_ENABLE(reg,v) do { (reg) = ((reg) & ~(0x1f << 7)) | ((v & 0x1f) << 7); } while(0)
+#define SET_FP_FLAGS(reg,v) do { (reg) = ((reg) & ~(0x1f << 2)) | ((v & 0x1f) << 2); } while(0)
+#define UPDATE_FP_FLAGS(reg,v) do { (reg) |= ((v & 0x1f) << 2); } while(0)
+#define FP_INEXACT 1
+#define FP_UNDERFLOW 2
+#define FP_OVERFLOW 4
+#define FP_DIV0 8
+#define FP_INVALID 16
+#define FP_UNIMPLEMENTED 32
+};
+
+#define NB_MMU_MODES 3
+
+typedef struct CPUMIPSMVPContext CPUMIPSMVPContext;
+struct CPUMIPSMVPContext {
+ int32_t CP0_MVPControl;
+#define CP0MVPCo_CPA 3
+#define CP0MVPCo_STLB 2
+#define CP0MVPCo_VPC 1
+#define CP0MVPCo_EVP 0
+ int32_t CP0_MVPConf0;
+#define CP0MVPC0_M 31
+#define CP0MVPC0_TLBS 29
+#define CP0MVPC0_GS 28
+#define CP0MVPC0_PCP 27
+#define CP0MVPC0_PTLBE 16
+#define CP0MVPC0_TCA 15
+#define CP0MVPC0_PVPE 10
+#define CP0MVPC0_PTC 0
+ int32_t CP0_MVPConf1;
+#define CP0MVPC1_CIM 31
+#define CP0MVPC1_CIF 30
+#define CP0MVPC1_PCX 20
+#define CP0MVPC1_PCP2 10
+#define CP0MVPC1_PCP1 0
+};
+
+typedef struct mips_def_t mips_def_t;
+
+#define MIPS_SHADOW_SET_MAX 16
+#define MIPS_TC_MAX 5
+#define MIPS_FPU_MAX 1
+#define MIPS_DSP_ACC 4
+
+typedef struct TCState TCState;
+struct TCState {
+ target_ulong gpr[32];
+ target_ulong PC;
+ target_ulong HI[MIPS_DSP_ACC];
+ target_ulong LO[MIPS_DSP_ACC];
+ target_ulong ACX[MIPS_DSP_ACC];
+ target_ulong DSPControl;
+ int32_t CP0_TCStatus;
+#define CP0TCSt_TCU3 31
+#define CP0TCSt_TCU2 30
+#define CP0TCSt_TCU1 29
+#define CP0TCSt_TCU0 28
+#define CP0TCSt_TMX 27
+#define CP0TCSt_RNST 23
+#define CP0TCSt_TDS 21
+#define CP0TCSt_DT 20
+#define CP0TCSt_DA 15
+#define CP0TCSt_A 13
+#define CP0TCSt_TKSU 11
+#define CP0TCSt_IXMT 10
+#define CP0TCSt_TASID 0
+ int32_t CP0_TCBind;
+#define CP0TCBd_CurTC 21
+#define CP0TCBd_TBE 17
+#define CP0TCBd_CurVPE 0
+ target_ulong CP0_TCHalt;
+ target_ulong CP0_TCContext;
+ target_ulong CP0_TCSchedule;
+ target_ulong CP0_TCScheFBack;
+ int32_t CP0_Debug_tcstatus;
+};
+
+typedef struct CPUMIPSState CPUMIPSState;
+struct CPUMIPSState {
+ TCState active_tc;
+ CPUMIPSFPUContext active_fpu;
+
+ uint32_t current_tc;
+ uint32_t current_fpu;
+
+ uint32_t SEGBITS;
+ uint32_t PABITS;
+ target_ulong SEGMask;
+ target_ulong PAMask;
+
+ int32_t CP0_Index;
+ /* CP0_MVP* are per MVP registers. */
+ int32_t CP0_Random;
+ int32_t CP0_VPEControl;
+#define CP0VPECo_YSI 21
+#define CP0VPECo_GSI 20
+#define CP0VPECo_EXCPT 16
+#define CP0VPECo_TE 15
+#define CP0VPECo_TargTC 0
+ int32_t CP0_VPEConf0;
+#define CP0VPEC0_M 31
+#define CP0VPEC0_XTC 21
+#define CP0VPEC0_TCS 19
+#define CP0VPEC0_SCS 18
+#define CP0VPEC0_DSC 17
+#define CP0VPEC0_ICS 16
+#define CP0VPEC0_MVP 1
+#define CP0VPEC0_VPA 0
+ int32_t CP0_VPEConf1;
+#define CP0VPEC1_NCX 20
+#define CP0VPEC1_NCP2 10
+#define CP0VPEC1_NCP1 0
+ target_ulong CP0_YQMask;
+ target_ulong CP0_VPESchedule;
+ target_ulong CP0_VPEScheFBack;
+ int32_t CP0_VPEOpt;
+#define CP0VPEOpt_IWX7 15
+#define CP0VPEOpt_IWX6 14
+#define CP0VPEOpt_IWX5 13
+#define CP0VPEOpt_IWX4 12
+#define CP0VPEOpt_IWX3 11
+#define CP0VPEOpt_IWX2 10
+#define CP0VPEOpt_IWX1 9
+#define CP0VPEOpt_IWX0 8
+#define CP0VPEOpt_DWX7 7
+#define CP0VPEOpt_DWX6 6
+#define CP0VPEOpt_DWX5 5
+#define CP0VPEOpt_DWX4 4
+#define CP0VPEOpt_DWX3 3
+#define CP0VPEOpt_DWX2 2
+#define CP0VPEOpt_DWX1 1
+#define CP0VPEOpt_DWX0 0
+ target_ulong CP0_EntryLo0;
+ target_ulong CP0_EntryLo1;
+ target_ulong CP0_Context;
+ int32_t CP0_PageMask;
+ int32_t CP0_PageGrain;
+ int32_t CP0_Wired;
+ int32_t CP0_SRSConf0_rw_bitmask;
+ int32_t CP0_SRSConf0;
+#define CP0SRSC0_M 31
+#define CP0SRSC0_SRS3 20
+#define CP0SRSC0_SRS2 10
+#define CP0SRSC0_SRS1 0
+ int32_t CP0_SRSConf1_rw_bitmask;
+ int32_t CP0_SRSConf1;
+#define CP0SRSC1_M 31
+#define CP0SRSC1_SRS6 20
+#define CP0SRSC1_SRS5 10
+#define CP0SRSC1_SRS4 0
+ int32_t CP0_SRSConf2_rw_bitmask;
+ int32_t CP0_SRSConf2;
+#define CP0SRSC2_M 31
+#define CP0SRSC2_SRS9 20
+#define CP0SRSC2_SRS8 10
+#define CP0SRSC2_SRS7 0
+ int32_t CP0_SRSConf3_rw_bitmask;
+ int32_t CP0_SRSConf3;
+#define CP0SRSC3_M 31
+#define CP0SRSC3_SRS12 20
+#define CP0SRSC3_SRS11 10
+#define CP0SRSC3_SRS10 0
+ int32_t CP0_SRSConf4_rw_bitmask;
+ int32_t CP0_SRSConf4;
+#define CP0SRSC4_SRS15 20
+#define CP0SRSC4_SRS14 10
+#define CP0SRSC4_SRS13 0
+ int32_t CP0_HWREna;
+ target_ulong CP0_BadVAddr;
+ int32_t CP0_Count;
+ target_ulong CP0_EntryHi;
+ int32_t CP0_Compare;
+ int32_t CP0_Status;
+#define CP0St_CU3 31
+#define CP0St_CU2 30
+#define CP0St_CU1 29
+#define CP0St_CU0 28
+#define CP0St_RP 27
+#define CP0St_FR 26
+#define CP0St_RE 25
+#define CP0St_MX 24
+#define CP0St_PX 23
+#define CP0St_BEV 22
+#define CP0St_TS 21
+#define CP0St_SR 20
+#define CP0St_NMI 19
+#define CP0St_IM 8
+#define CP0St_KX 7
+#define CP0St_SX 6
+#define CP0St_UX 5
+#define CP0St_KSU 3
+#define CP0St_ERL 2
+#define CP0St_EXL 1
+#define CP0St_IE 0
+ int32_t CP0_IntCtl;
+#define CP0IntCtl_IPTI 29
+#define CP0IntCtl_IPPC1 26
+#define CP0IntCtl_VS 5
+ int32_t CP0_SRSCtl;
+#define CP0SRSCtl_HSS 26
+#define CP0SRSCtl_EICSS 18
+#define CP0SRSCtl_ESS 12
+#define CP0SRSCtl_PSS 6
+#define CP0SRSCtl_CSS 0
+ int32_t CP0_SRSMap;
+#define CP0SRSMap_SSV7 28
+#define CP0SRSMap_SSV6 24
+#define CP0SRSMap_SSV5 20
+#define CP0SRSMap_SSV4 16
+#define CP0SRSMap_SSV3 12
+#define CP0SRSMap_SSV2 8
+#define CP0SRSMap_SSV1 4
+#define CP0SRSMap_SSV0 0
+ int32_t CP0_Cause;
+#define CP0Ca_BD 31
+#define CP0Ca_TI 30
+#define CP0Ca_CE 28
+#define CP0Ca_DC 27
+#define CP0Ca_PCI 26
+#define CP0Ca_IV 23
+#define CP0Ca_WP 22
+#define CP0Ca_IP 8
+#define CP0Ca_IP_mask 0x0000FF00
+#define CP0Ca_EC 2
+ target_ulong CP0_EPC;
+ int32_t CP0_PRid;
+ int32_t CP0_EBase;
+ int32_t CP0_Config0;
+#define CP0C0_M 31
+#define CP0C0_K23 28
+#define CP0C0_KU 25
+#define CP0C0_MDU 20
+#define CP0C0_MM 17
+#define CP0C0_BM 16
+#define CP0C0_BE 15
+#define CP0C0_AT 13
+#define CP0C0_AR 10
+#define CP0C0_MT 7
+#define CP0C0_VI 3
+#define CP0C0_K0 0
+ int32_t CP0_Config1;
+#define CP0C1_M 31
+#define CP0C1_MMU 25
+#define CP0C1_IS 22
+#define CP0C1_IL 19
+#define CP0C1_IA 16
+#define CP0C1_DS 13
+#define CP0C1_DL 10
+#define CP0C1_DA 7
+#define CP0C1_C2 6
+#define CP0C1_MD 5
+#define CP0C1_PC 4
+#define CP0C1_WR 3
+#define CP0C1_CA 2
+#define CP0C1_EP 1
+#define CP0C1_FP 0
+ int32_t CP0_Config2;
+#define CP0C2_M 31
+#define CP0C2_TU 28
+#define CP0C2_TS 24
+#define CP0C2_TL 20
+#define CP0C2_TA 16
+#define CP0C2_SU 12
+#define CP0C2_SS 8
+#define CP0C2_SL 4
+#define CP0C2_SA 0
+ int32_t CP0_Config3;
+#define CP0C3_M 31
+#define CP0C3_ISA_ON_EXC 16
+#define CP0C3_DSPP 10
+#define CP0C3_LPA 7
+#define CP0C3_VEIC 6
+#define CP0C3_VInt 5
+#define CP0C3_SP 4
+#define CP0C3_MT 2
+#define CP0C3_SM 1
+#define CP0C3_TL 0
+ int32_t CP0_Config6;
+ int32_t CP0_Config7;
+ /* XXX: Maybe make LLAddr per-TC? */
+ target_ulong lladdr;
+ target_ulong llval;
+ target_ulong llnewval;
+ target_ulong llreg;
+ target_ulong CP0_LLAddr_rw_bitmask;
+ int CP0_LLAddr_shift;
+ target_ulong CP0_WatchLo[8];
+ int32_t CP0_WatchHi[8];
+ target_ulong CP0_XContext;
+ int32_t CP0_Framemask;
+ int32_t CP0_Debug;
+#define CP0DB_DBD 31
+#define CP0DB_DM 30
+#define CP0DB_LSNM 28
+#define CP0DB_Doze 27
+#define CP0DB_Halt 26
+#define CP0DB_CNT 25
+#define CP0DB_IBEP 24
+#define CP0DB_DBEP 21
+#define CP0DB_IEXI 20
+#define CP0DB_VER 15
+#define CP0DB_DEC 10
+#define CP0DB_SSt 8
+#define CP0DB_DINT 5
+#define CP0DB_DIB 4
+#define CP0DB_DDBS 3
+#define CP0DB_DDBL 2
+#define CP0DB_DBp 1
+#define CP0DB_DSS 0
+ target_ulong CP0_DEPC;
+ int32_t CP0_Performance0;
+ int32_t CP0_TagLo;
+ int32_t CP0_DataLo;
+ int32_t CP0_TagHi;
+ int32_t CP0_DataHi;
+ target_ulong CP0_ErrorEPC;
+ int32_t CP0_DESAVE;
+ /* We waste some space so we can handle shadow registers like TCs. */
+ TCState tcs[MIPS_SHADOW_SET_MAX];
+ CPUMIPSFPUContext fpus[MIPS_FPU_MAX];
+ /* Qemu */
+ int error_code;
+ uint32_t hflags; /* CPU State */
+ /* TMASK defines different execution modes */
+#define MIPS_HFLAG_TMASK 0x007FF
+#define MIPS_HFLAG_MODE 0x00007 /* execution modes */
+ /* The KSU flags must be the lowest bits in hflags. The flag order
+ must be the same as defined for CP0 Status. This allows to use
+ the bits as the value of mmu_idx. */
+#define MIPS_HFLAG_KSU 0x00003 /* kernel/supervisor/user mode mask */
+#define MIPS_HFLAG_UM 0x00002 /* user mode flag */
+#define MIPS_HFLAG_SM 0x00001 /* supervisor mode flag */
+#define MIPS_HFLAG_KM 0x00000 /* kernel mode flag */
+#define MIPS_HFLAG_DM 0x00004 /* Debug mode */
+#define MIPS_HFLAG_64 0x00008 /* 64-bit instructions enabled */
+#define MIPS_HFLAG_CP0 0x00010 /* CP0 enabled */
+#define MIPS_HFLAG_FPU 0x00020 /* FPU enabled */
+#define MIPS_HFLAG_F64 0x00040 /* 64-bit FPU enabled */
+ /* True if the MIPS IV COP1X instructions can be used. This also
+ controls the non-COP1X instructions RECIP.S, RECIP.D, RSQRT.S
+ and RSQRT.D. */
+#define MIPS_HFLAG_COP1X 0x00080 /* COP1X instructions enabled */
+#define MIPS_HFLAG_RE 0x00100 /* Reversed endianness */
+#define MIPS_HFLAG_UX 0x00200 /* 64-bit user mode */
+#define MIPS_HFLAG_M16 0x00400 /* MIPS16 mode flag */
+#define MIPS_HFLAG_M16_SHIFT 10
+ /* If translation is interrupted between the branch instruction and
+ * the delay slot, record what type of branch it is so that we can
+ * resume translation properly. It might be possible to reduce
+ * this from three bits to two. */
+#define MIPS_HFLAG_BMASK_BASE 0x03800
+#define MIPS_HFLAG_B 0x00800 /* Unconditional branch */
+#define MIPS_HFLAG_BC 0x01000 /* Conditional branch */
+#define MIPS_HFLAG_BL 0x01800 /* Likely branch */
+#define MIPS_HFLAG_BR 0x02000 /* branch to register (can't link TB) */
+ /* Extra flags about the current pending branch. */
+#define MIPS_HFLAG_BMASK_EXT 0x3C000
+#define MIPS_HFLAG_B16 0x04000 /* branch instruction was 16 bits */
+#define MIPS_HFLAG_BDS16 0x08000 /* branch requires 16-bit delay slot */
+#define MIPS_HFLAG_BDS32 0x10000 /* branch requires 32-bit delay slot */
+#define MIPS_HFLAG_BX 0x20000 /* branch exchanges execution mode */
+#define MIPS_HFLAG_BMASK (MIPS_HFLAG_BMASK_BASE | MIPS_HFLAG_BMASK_EXT)
+ target_ulong btarget; /* Jump / branch target */
+ target_ulong bcond; /* Branch condition (if needed) */
+
+ int SYNCI_Step; /* Address step size for SYNCI */
+ int CCRes; /* Cycle count resolution/divisor */
+ uint32_t CP0_Status_rw_bitmask; /* Read/write bits in CP0_Status */
+ uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */
+ int insn_flags; /* Supported instruction set */
+
+ target_ulong tls_value; /* For usermode emulation */
+
+ CPU_COMMON
+
+ CPUMIPSMVPContext *mvp;
+#if !defined(CONFIG_USER_ONLY)
+ CPUMIPSTLBContext *tlb;
+#endif
+
+ const mips_def_t *cpu_model;
+ void *irq[8];
+ struct QEMUTimer *timer; /* Internal timer */
+};
+
+#if !defined(CONFIG_USER_ONLY)
+int no_mmu_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+int fixed_mmu_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+int r4k_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int access_type);
+void r4k_helper_tlbwi (void);
+void r4k_helper_tlbwr (void);
+void r4k_helper_tlbp (void);
+void r4k_helper_tlbr (void);
+
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int unused, int size);
+#endif
+
+void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf);
+
+#define cpu_init cpu_mips_init
+#define cpu_exec cpu_mips_exec
+#define cpu_gen_code cpu_mips_gen_code
+#define cpu_signal_handler cpu_mips_signal_handler
+#define cpu_list mips_cpu_list
+
+#define CPU_SAVE_VERSION 3
+
+/* MMU modes definitions. We carefully match the indices with our
+ hflags layout. */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _super
+#define MMU_MODE2_SUFFIX _user
+#define MMU_USER_IDX 2
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return env->hflags & MIPS_HFLAG_KSU;
+}
+
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->active_tc.gpr[29] = newsp;
+ env->active_tc.gpr[7] = 0;
+ env->active_tc.gpr[2] = 0;
+}
+
+static inline int cpu_mips_hw_interrupts_pending(CPUState *env)
+{
+ int32_t pending;
+ int32_t status;
+ int r;
+
+ if (!(env->CP0_Status & (1 << CP0St_IE)) ||
+ (env->CP0_Status & (1 << CP0St_EXL)) ||
+ (env->CP0_Status & (1 << CP0St_ERL)) ||
+ (env->hflags & MIPS_HFLAG_DM)) {
+ /* Interrupts are disabled */
+ return 0;
+ }
+
+ pending = env->CP0_Cause & CP0Ca_IP_mask;
+ status = env->CP0_Status & CP0Ca_IP_mask;
+
+ if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+ /* A MIPS configured with a vectorizing external interrupt controller
+ will feed a vector into the Cause pending lines. The core treats
+ the status lines as a vector level, not as indiviual masks. */
+ r = pending > status;
+ } else {
+ /* A MIPS configured with compatibility or VInt (Vectored Interrupts)
+ treats the pending lines as individual interrupt lines, the status
+ lines are individual masks. */
+ r = pending & status;
+ }
+ return r;
+}
+
+#include "cpu-all.h"
+
+/* Memory access type :
+ * may be needed for precise access rights control and precise exceptions.
+ */
+enum {
+ /* 1 bit to define user level / supervisor access */
+ ACCESS_USER = 0x00,
+ ACCESS_SUPER = 0x01,
+ /* 1 bit to indicate direction */
+ ACCESS_STORE = 0x02,
+ /* Type of instruction that generated the access */
+ ACCESS_CODE = 0x10, /* Code fetch access */
+ ACCESS_INT = 0x20, /* Integer load/store access */
+ ACCESS_FLOAT = 0x30, /* floating point load/store access */
+};
+
+/* Exceptions */
+enum {
+ EXCP_NONE = -1,
+ EXCP_RESET = 0,
+ EXCP_SRESET,
+ EXCP_DSS,
+ EXCP_DINT,
+ EXCP_DDBL,
+ EXCP_DDBS,
+ EXCP_NMI,
+ EXCP_MCHECK,
+ EXCP_EXT_INTERRUPT, /* 8 */
+ EXCP_DFWATCH,
+ EXCP_DIB,
+ EXCP_IWATCH,
+ EXCP_AdEL,
+ EXCP_AdES,
+ EXCP_TLBF,
+ EXCP_IBE,
+ EXCP_DBp, /* 16 */
+ EXCP_SYSCALL,
+ EXCP_BREAK,
+ EXCP_CpU,
+ EXCP_RI,
+ EXCP_OVERFLOW,
+ EXCP_TRAP,
+ EXCP_FPE,
+ EXCP_DWATCH, /* 24 */
+ EXCP_LTLBL,
+ EXCP_TLBL,
+ EXCP_TLBS,
+ EXCP_DBE,
+ EXCP_THREAD,
+ EXCP_MDMX,
+ EXCP_C2E,
+ EXCP_CACHE, /* 32 */
+
+ EXCP_LAST = EXCP_CACHE,
+};
+/* Dummy exception for conditional stores. */
+#define EXCP_SC 0x100
+
+int cpu_mips_exec(CPUMIPSState *s);
+CPUMIPSState *cpu_mips_init(const char *cpu_model);
+//~ uint32_t cpu_mips_get_clock (void);
+int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc);
+
+/* mips_timer.c */
+uint32_t cpu_mips_get_random (CPUState *env);
+uint32_t cpu_mips_get_count (CPUState *env);
+void cpu_mips_store_count (CPUState *env, uint32_t value);
+void cpu_mips_store_compare (CPUState *env, uint32_t value);
+void cpu_mips_start_count(CPUState *env);
+void cpu_mips_stop_count(CPUState *env);
+
+/* mips_int.c */
+void cpu_mips_soft_irq(CPUState *env, int irq, int level);
+
+/* helper.c */
+int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_mips_handle_mmu_fault
+void do_interrupt (CPUState *env);
+#if !defined(CONFIG_USER_ONLY)
+void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra);
+target_phys_addr_t cpu_mips_translate_address (CPUState *env, target_ulong address,
+ int rw);
+#endif
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->active_tc.PC;
+ *cs_base = 0;
+ *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK);
+}
+
+static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
+{
+ env->tls_value = newtls;
+}
+
+#endif /* !defined (__MIPS_CPU_H__) */
diff --git a/target-mips/exec.h b/target-mips/exec.h
new file mode 100644
index 0000000..1273654
--- /dev/null
+++ b/target-mips/exec.h
@@ -0,0 +1,98 @@
+#if !defined(__QEMU_MIPS_EXEC_H__)
+#define __QEMU_MIPS_EXEC_H__
+
+//#define DEBUG_OP
+
+#include "config.h"
+#include "mips-defs.h"
+#include "dyngen-exec.h"
+#include "cpu-defs.h"
+
+register struct CPUMIPSState *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline int cpu_has_work(CPUState *env)
+{
+ int has_work = 0;
+
+ /* It is implementation dependent if non-enabled interrupts
+ wake-up the CPU, however most of the implementations only
+ check for interrupts that can be taken. */
+ if ((env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ cpu_mips_hw_interrupts_pending(env)) {
+ has_work = 1;
+ }
+
+ if (env->interrupt_request & CPU_INTERRUPT_TIMER) {
+ has_work = 1;
+ }
+
+ return has_work;
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+ if (!env->halted)
+ return 0;
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void compute_hflags(CPUState *env)
+{
+ env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
+ MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
+ MIPS_HFLAG_UX);
+ if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
+ !(env->CP0_Status & (1 << CP0St_ERL)) &&
+ !(env->hflags & MIPS_HFLAG_DM)) {
+ env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU;
+ }
+#if defined(TARGET_MIPS64)
+ if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) ||
+ (env->CP0_Status & (1 << CP0St_PX)) ||
+ (env->CP0_Status & (1 << CP0St_UX)))
+ env->hflags |= MIPS_HFLAG_64;
+ if (env->CP0_Status & (1 << CP0St_UX))
+ env->hflags |= MIPS_HFLAG_UX;
+#endif
+ if ((env->CP0_Status & (1 << CP0St_CU0)) ||
+ !(env->hflags & MIPS_HFLAG_KSU))
+ env->hflags |= MIPS_HFLAG_CP0;
+ if (env->CP0_Status & (1 << CP0St_CU1))
+ env->hflags |= MIPS_HFLAG_FPU;
+ if (env->CP0_Status & (1 << CP0St_FR))
+ env->hflags |= MIPS_HFLAG_F64;
+ if (env->insn_flags & ISA_MIPS32R2) {
+ if (env->active_fpu.fcr0 & (1 << FCR0_F64))
+ env->hflags |= MIPS_HFLAG_COP1X;
+ } else if (env->insn_flags & ISA_MIPS32) {
+ if (env->hflags & MIPS_HFLAG_64)
+ env->hflags |= MIPS_HFLAG_COP1X;
+ } else if (env->insn_flags & ISA_MIPS4) {
+ /* All supported MIPS IV CPUs use the XX (CU3) to enable
+ and disable the MIPS IV extensions to the MIPS III ISA.
+ Some other MIPS IV CPUs ignore the bit, so the check here
+ would be too restrictive for them. */
+ if (env->CP0_Status & (1 << CP0St_CU3))
+ env->hflags |= MIPS_HFLAG_COP1X;
+ }
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->active_tc.PC = tb->pc;
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ env->hflags |= tb->flags & MIPS_HFLAG_BMASK;
+}
+
+#endif /* !defined(__QEMU_MIPS_EXEC_H__) */
diff --git a/target-mips/helper.c b/target-mips/helper.c
new file mode 100644
index 0000000..bdc1e53
--- /dev/null
+++ b/target-mips/helper.c
@@ -0,0 +1,696 @@
+/*
+ * MIPS emulation helpers for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+enum {
+ TLBRET_DIRTY = -4,
+ TLBRET_INVALID = -3,
+ TLBRET_NOMATCH = -2,
+ TLBRET_BADADDR = -1,
+ TLBRET_MATCH = 0
+};
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* no MMU emulation */
+int no_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int access_type)
+{
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE;
+ return TLBRET_MATCH;
+}
+
+/* fixed mapping MMU emulation */
+int fixed_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int access_type)
+{
+ if (address <= (int32_t)0x7FFFFFFFUL) {
+ if (!(env->CP0_Status & (1 << CP0St_ERL)))
+ *physical = address + 0x40000000UL;
+ else
+ *physical = address;
+ } else if (address <= (int32_t)0xBFFFFFFFUL)
+ *physical = address & 0x1FFFFFFF;
+ else
+ *physical = address;
+
+ *prot = PAGE_READ | PAGE_WRITE;
+ return TLBRET_MATCH;
+}
+
+/* MIPS32/MIPS64 R4000-style MMU emulation */
+int r4k_map_address (CPUState *env, target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int access_type)
+{
+ uint8_t ASID = env->CP0_EntryHi & 0xFF;
+ int i;
+
+ for (i = 0; i < env->tlb->tlb_in_use; i++) {
+ r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i];
+ /* 1k pages are not supported. */
+ target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
+ target_ulong tag = address & ~mask;
+ target_ulong VPN = tlb->VPN & ~mask;
+#if defined(TARGET_MIPS64)
+ tag &= env->SEGMask;
+#endif
+
+ /* Check ASID, virtual page number & size */
+ if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
+ /* TLB match */
+ int n = !!(address & mask & ~(mask >> 1));
+ /* Check access rights */
+ if (!(n ? tlb->V1 : tlb->V0))
+ return TLBRET_INVALID;
+ if (rw == 0 || (n ? tlb->D1 : tlb->D0)) {
+ *physical = tlb->PFN[n] | (address & (mask >> 1));
+ *prot = PAGE_READ;
+ if (n ? tlb->D1 : tlb->D0)
+ *prot |= PAGE_WRITE;
+ return TLBRET_MATCH;
+ }
+ return TLBRET_DIRTY;
+ }
+ }
+ return TLBRET_NOMATCH;
+}
+
+static int get_physical_address (CPUState *env, target_phys_addr_t *physical,
+ int *prot, target_ulong address,
+ int rw, int access_type)
+{
+ /* User mode can only access useg/xuseg */
+ int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM;
+ int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM;
+ int kernel_mode = !user_mode && !supervisor_mode;
+#if defined(TARGET_MIPS64)
+ int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
+ int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
+ int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
+#endif
+ int ret = TLBRET_MATCH;
+
+#if 0
+ qemu_log("user mode %d h %08x\n", user_mode, env->hflags);
+#endif
+
+ if (address <= (int32_t)0x7FFFFFFFUL) {
+ /* useg */
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ *physical = address & 0xFFFFFFFF;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else {
+ ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
+ }
+#if defined(TARGET_MIPS64)
+ } else if (address < 0x4000000000000000ULL) {
+ /* xuseg */
+ if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) {
+ ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < 0x8000000000000000ULL) {
+ /* xsseg */
+ if ((supervisor_mode || kernel_mode) &&
+ SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) {
+ ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < 0xC000000000000000ULL) {
+ /* xkphys */
+ if (kernel_mode && KX &&
+ (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) {
+ *physical = address & env->PAMask;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < 0xFFFFFFFF80000000ULL) {
+ /* xkseg */
+ if (kernel_mode && KX &&
+ address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) {
+ ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+#endif
+ } else if (address < (int32_t)0xA0000000UL) {
+ /* kseg0 */
+ if (kernel_mode) {
+ *physical = address - (int32_t)0x80000000UL;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < (int32_t)0xC0000000UL) {
+ /* kseg1 */
+ if (kernel_mode) {
+ *physical = address - (int32_t)0xA0000000UL;
+ *prot = PAGE_READ | PAGE_WRITE;
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else if (address < (int32_t)0xE0000000UL) {
+ /* sseg (kseg2) */
+ if (supervisor_mode || kernel_mode) {
+ ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ } else {
+ /* kseg3 */
+ /* XXX: debug segment is not emulated */
+ if (kernel_mode) {
+ ret = env->tlb->map_address(env, physical, prot, address, rw, access_type);
+ } else {
+ ret = TLBRET_BADADDR;
+ }
+ }
+#if 0
+ qemu_log(TARGET_FMT_lx " %d %d => " TARGET_FMT_lx " %d (%d)\n",
+ address, rw, access_type, *physical, *prot, ret);
+#endif
+
+ return ret;
+}
+#endif
+
+static void raise_mmu_exception(CPUState *env, target_ulong address,
+ int rw, int tlb_error)
+{
+ int exception = 0, error_code = 0;
+
+ switch (tlb_error) {
+ default:
+ case TLBRET_BADADDR:
+ /* Reference to kernel address from user mode or supervisor mode */
+ /* Reference to supervisor address from user mode */
+ if (rw)
+ exception = EXCP_AdES;
+ else
+ exception = EXCP_AdEL;
+ break;
+ case TLBRET_NOMATCH:
+ /* No TLB match for a mapped address */
+ if (rw)
+ exception = EXCP_TLBS;
+ else
+ exception = EXCP_TLBL;
+ error_code = 1;
+ break;
+ case TLBRET_INVALID:
+ /* TLB match with no valid bit */
+ if (rw)
+ exception = EXCP_TLBS;
+ else
+ exception = EXCP_TLBL;
+ break;
+ case TLBRET_DIRTY:
+ /* TLB match but 'D' bit is cleared */
+ exception = EXCP_LTLBL;
+ break;
+
+ }
+ /* Raise exception */
+ env->CP0_BadVAddr = address;
+ env->CP0_Context = (env->CP0_Context & ~0x007fffff) |
+ ((address >> 9) & 0x007ffff0);
+ env->CP0_EntryHi =
+ (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1));
+#if defined(TARGET_MIPS64)
+ env->CP0_EntryHi &= env->SEGMask;
+ env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) |
+ ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) |
+ ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9);
+#endif
+ env->exception_index = exception;
+ env->error_code = error_code;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ target_phys_addr_t phys_addr;
+ int prot;
+
+ if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
+ return -1;
+ return phys_addr;
+}
+#endif
+
+int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+#if !defined(CONFIG_USER_ONLY)
+ target_phys_addr_t physical;
+ int prot;
+#endif
+ int access_type;
+ int ret = 0;
+
+#if 0
+ log_cpu_state(env, 0);
+#endif
+ qemu_log("%s pc " TARGET_FMT_lx " ad " TARGET_FMT_lx " rw %d mmu_idx %d smmu %d\n",
+ __func__, env->active_tc.PC, address, rw, mmu_idx, is_softmmu);
+
+ rw &= 1;
+
+ /* data access */
+ /* XXX: put correct access by using cpu_restore_state()
+ correctly */
+ access_type = ACCESS_INT;
+#if defined(CONFIG_USER_ONLY)
+ ret = TLBRET_NOMATCH;
+#else
+ ret = get_physical_address(env, &physical, &prot,
+ address, rw, access_type);
+ qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx " prot %d\n",
+ __func__, address, ret, physical, prot);
+ if (ret == TLBRET_MATCH) {
+ tlb_set_page(env, address & TARGET_PAGE_MASK,
+ physical & TARGET_PAGE_MASK, prot | PAGE_EXEC,
+ mmu_idx, TARGET_PAGE_SIZE);
+ ret = 0;
+ } else if (ret < 0)
+#endif
+ {
+ raise_mmu_exception(env, address, rw, ret);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+target_phys_addr_t cpu_mips_translate_address(CPUState *env, target_ulong address, int rw)
+{
+ target_phys_addr_t physical;
+ int prot;
+ int access_type;
+ int ret = 0;
+
+ rw &= 1;
+
+ /* data access */
+ access_type = ACCESS_INT;
+ ret = get_physical_address(env, &physical, &prot,
+ address, rw, access_type);
+ if (ret != TLBRET_MATCH) {
+ raise_mmu_exception(env, address, rw, ret);
+ return -1LL;
+ } else {
+ return physical;
+ }
+}
+#endif
+
+static const char * const excp_names[EXCP_LAST + 1] = {
+ [EXCP_RESET] = "reset",
+ [EXCP_SRESET] = "soft reset",
+ [EXCP_DSS] = "debug single step",
+ [EXCP_DINT] = "debug interrupt",
+ [EXCP_NMI] = "non-maskable interrupt",
+ [EXCP_MCHECK] = "machine check",
+ [EXCP_EXT_INTERRUPT] = "interrupt",
+ [EXCP_DFWATCH] = "deferred watchpoint",
+ [EXCP_DIB] = "debug instruction breakpoint",
+ [EXCP_IWATCH] = "instruction fetch watchpoint",
+ [EXCP_AdEL] = "address error load",
+ [EXCP_AdES] = "address error store",
+ [EXCP_TLBF] = "TLB refill",
+ [EXCP_IBE] = "instruction bus error",
+ [EXCP_DBp] = "debug breakpoint",
+ [EXCP_SYSCALL] = "syscall",
+ [EXCP_BREAK] = "break",
+ [EXCP_CpU] = "coprocessor unusable",
+ [EXCP_RI] = "reserved instruction",
+ [EXCP_OVERFLOW] = "arithmetic overflow",
+ [EXCP_TRAP] = "trap",
+ [EXCP_FPE] = "floating point",
+ [EXCP_DDBS] = "debug data break store",
+ [EXCP_DWATCH] = "data watchpoint",
+ [EXCP_LTLBL] = "TLB modify",
+ [EXCP_TLBL] = "TLB load",
+ [EXCP_TLBS] = "TLB store",
+ [EXCP_DBE] = "data bus error",
+ [EXCP_DDBL] = "debug data break load",
+ [EXCP_THREAD] = "thread",
+ [EXCP_MDMX] = "MDMX",
+ [EXCP_C2E] = "precise coprocessor 2",
+ [EXCP_CACHE] = "cache error",
+};
+
+#if !defined(CONFIG_USER_ONLY)
+static target_ulong exception_resume_pc (CPUState *env)
+{
+ target_ulong bad_pc;
+ target_ulong isa_mode;
+
+ isa_mode = !!(env->hflags & MIPS_HFLAG_M16);
+ bad_pc = env->active_tc.PC | isa_mode;
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ /* If the exception was raised from a delay slot, come back to
+ the jump. */
+ bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4);
+ }
+
+ return bad_pc;
+}
+
+static void set_hflags_for_handler (CPUState *env)
+{
+ /* Exception handlers are entered in 32-bit mode. */
+ env->hflags &= ~(MIPS_HFLAG_M16);
+ /* ...except that microMIPS lets you choose. */
+ if (env->insn_flags & ASE_MICROMIPS) {
+ env->hflags |= (!!(env->CP0_Config3
+ & (1 << CP0C3_ISA_ON_EXC))
+ << MIPS_HFLAG_M16_SHIFT);
+ }
+}
+#endif
+
+void do_interrupt (CPUState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ target_ulong offset;
+ int cause = -1;
+ const char *name;
+
+ if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
+ if (env->exception_index < 0 || env->exception_index > EXCP_LAST)
+ name = "unknown";
+ else
+ name = excp_names[env->exception_index];
+
+ qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n",
+ __func__, env->active_tc.PC, env->CP0_EPC, name);
+ }
+ if (env->exception_index == EXCP_EXT_INTERRUPT &&
+ (env->hflags & MIPS_HFLAG_DM))
+ env->exception_index = EXCP_DINT;
+ offset = 0x180;
+ switch (env->exception_index) {
+ case EXCP_DSS:
+ env->CP0_Debug |= 1 << CP0DB_DSS;
+ /* Debug single step cannot be raised inside a delay slot and
+ resume will always occur on the next instruction
+ (but we assume the pc has always been updated during
+ code translation). */
+ env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16);
+ goto enter_debug_mode;
+ case EXCP_DINT:
+ env->CP0_Debug |= 1 << CP0DB_DINT;
+ goto set_DEPC;
+ case EXCP_DIB:
+ env->CP0_Debug |= 1 << CP0DB_DIB;
+ goto set_DEPC;
+ case EXCP_DBp:
+ env->CP0_Debug |= 1 << CP0DB_DBp;
+ goto set_DEPC;
+ case EXCP_DDBS:
+ env->CP0_Debug |= 1 << CP0DB_DDBS;
+ goto set_DEPC;
+ case EXCP_DDBL:
+ env->CP0_Debug |= 1 << CP0DB_DDBL;
+ set_DEPC:
+ env->CP0_DEPC = exception_resume_pc(env);
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ enter_debug_mode:
+ env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
+ env->hflags &= ~(MIPS_HFLAG_KSU);
+ /* EJTAG probe trap enable is not implemented... */
+ if (!(env->CP0_Status & (1 << CP0St_EXL)))
+ env->CP0_Cause &= ~(1 << CP0Ca_BD);
+ env->active_tc.PC = (int32_t)0xBFC00480;
+ set_hflags_for_handler(env);
+ break;
+ case EXCP_RESET:
+ cpu_reset(env);
+ break;
+ case EXCP_SRESET:
+ env->CP0_Status |= (1 << CP0St_SR);
+ memset(env->CP0_WatchLo, 0, sizeof(*env->CP0_WatchLo));
+ goto set_error_EPC;
+ case EXCP_NMI:
+ env->CP0_Status |= (1 << CP0St_NMI);
+ set_error_EPC:
+ env->CP0_ErrorEPC = exception_resume_pc(env);
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV);
+ env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
+ env->hflags &= ~(MIPS_HFLAG_KSU);
+ if (!(env->CP0_Status & (1 << CP0St_EXL)))
+ env->CP0_Cause &= ~(1 << CP0Ca_BD);
+ env->active_tc.PC = (int32_t)0xBFC00000;
+ set_hflags_for_handler(env);
+ break;
+ case EXCP_EXT_INTERRUPT:
+ cause = 0;
+ if (env->CP0_Cause & (1 << CP0Ca_IV))
+ offset = 0x200;
+
+ if (env->CP0_Config3 & ((1 << CP0C3_VInt) | (1 << CP0C3_VEIC))) {
+ /* Vectored Interrupts. */
+ unsigned int spacing;
+ unsigned int vector;
+ unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8;
+
+ /* Compute the Vector Spacing. */
+ spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1);
+ spacing <<= 5;
+
+ if (env->CP0_Config3 & (1 << CP0C3_VInt)) {
+ /* For VInt mode, the MIPS computes the vector internally. */
+ for (vector = 0; vector < 8; vector++) {
+ if (pending & 1) {
+ /* Found it. */
+ break;
+ }
+ pending >>= 1;
+ }
+ } else {
+ /* For VEIC mode, the external interrupt controller feeds the
+ vector throught the CP0Cause IP lines. */
+ vector = pending;
+ }
+ offset = 0x200 + vector * spacing;
+ }
+ goto set_EPC;
+ case EXCP_LTLBL:
+ cause = 1;
+ goto set_EPC;
+ case EXCP_TLBL:
+ cause = 2;
+ if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
+#if defined(TARGET_MIPS64)
+ int R = env->CP0_BadVAddr >> 62;
+ int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
+ int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
+ int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
+
+ if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
+ (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
+ offset = 0x080;
+ else
+#endif
+ offset = 0x000;
+ }
+ goto set_EPC;
+ case EXCP_TLBS:
+ cause = 3;
+ if (env->error_code == 1 && !(env->CP0_Status & (1 << CP0St_EXL))) {
+#if defined(TARGET_MIPS64)
+ int R = env->CP0_BadVAddr >> 62;
+ int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0;
+ int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0;
+ int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0;
+
+ if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) &&
+ (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F))))
+ offset = 0x080;
+ else
+#endif
+ offset = 0x000;
+ }
+ goto set_EPC;
+ case EXCP_AdEL:
+ cause = 4;
+ goto set_EPC;
+ case EXCP_AdES:
+ cause = 5;
+ goto set_EPC;
+ case EXCP_IBE:
+ cause = 6;
+ goto set_EPC;
+ case EXCP_DBE:
+ cause = 7;
+ goto set_EPC;
+ case EXCP_SYSCALL:
+ cause = 8;
+ goto set_EPC;
+ case EXCP_BREAK:
+ cause = 9;
+ goto set_EPC;
+ case EXCP_RI:
+ cause = 10;
+ goto set_EPC;
+ case EXCP_CpU:
+ cause = 11;
+ env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) |
+ (env->error_code << CP0Ca_CE);
+ goto set_EPC;
+ case EXCP_OVERFLOW:
+ cause = 12;
+ goto set_EPC;
+ case EXCP_TRAP:
+ cause = 13;
+ goto set_EPC;
+ case EXCP_FPE:
+ cause = 15;
+ goto set_EPC;
+ case EXCP_C2E:
+ cause = 18;
+ goto set_EPC;
+ case EXCP_MDMX:
+ cause = 22;
+ goto set_EPC;
+ case EXCP_DWATCH:
+ cause = 23;
+ /* XXX: TODO: manage defered watch exceptions */
+ goto set_EPC;
+ case EXCP_MCHECK:
+ cause = 24;
+ goto set_EPC;
+ case EXCP_THREAD:
+ cause = 25;
+ goto set_EPC;
+ case EXCP_CACHE:
+ cause = 30;
+ if (env->CP0_Status & (1 << CP0St_BEV)) {
+ offset = 0x100;
+ } else {
+ offset = 0x20000100;
+ }
+ set_EPC:
+ if (!(env->CP0_Status & (1 << CP0St_EXL))) {
+ env->CP0_EPC = exception_resume_pc(env);
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ env->CP0_Cause |= (1 << CP0Ca_BD);
+ } else {
+ env->CP0_Cause &= ~(1 << CP0Ca_BD);
+ }
+ env->CP0_Status |= (1 << CP0St_EXL);
+ env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0;
+ env->hflags &= ~(MIPS_HFLAG_KSU);
+ }
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ if (env->CP0_Status & (1 << CP0St_BEV)) {
+ env->active_tc.PC = (int32_t)0xBFC00200;
+ } else {
+ env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff);
+ }
+ env->active_tc.PC += offset;
+ set_hflags_for_handler(env);
+ env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC);
+ break;
+ default:
+ qemu_log("Invalid MIPS exception %d. Exiting\n", env->exception_index);
+ printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
+ exit(1);
+ }
+ if (qemu_log_enabled() && env->exception_index != EXCP_EXT_INTERRUPT) {
+ qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n"
+ " S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n",
+ __func__, env->active_tc.PC, env->CP0_EPC, cause,
+ env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
+ env->CP0_DEPC);
+ }
+#endif
+ env->exception_index = EXCP_NONE;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra)
+{
+ r4k_tlb_t *tlb;
+ target_ulong addr;
+ target_ulong end;
+ uint8_t ASID = env->CP0_EntryHi & 0xFF;
+ target_ulong mask;
+
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+ /* The qemu TLB is flushed when the ASID changes, so no need to
+ flush these entries again. */
+ if (tlb->G == 0 && tlb->ASID != ASID) {
+ return;
+ }
+
+ if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) {
+ /* For tlbwr, we can shadow the discarded entry into
+ a new (fake) TLB entry, as long as the guest can not
+ tell that it's there. */
+ env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb;
+ env->tlb->tlb_in_use++;
+ return;
+ }
+
+ /* 1k pages are not supported. */
+ mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
+ if (tlb->V0) {
+ addr = tlb->VPN & ~mask;
+#if defined(TARGET_MIPS64)
+ if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
+ addr |= 0x3FFFFF0000000000ULL;
+ }
+#endif
+ end = addr | (mask >> 1);
+ while (addr < end) {
+ tlb_flush_page (env, addr);
+ addr += TARGET_PAGE_SIZE;
+ }
+ }
+ if (tlb->V1) {
+ addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1);
+#if defined(TARGET_MIPS64)
+ if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) {
+ addr |= 0x3FFFFF0000000000ULL;
+ }
+#endif
+ end = addr | mask;
+ while (addr - 1 < end) {
+ tlb_flush_page (env, addr);
+ addr += TARGET_PAGE_SIZE;
+ }
+ }
+}
+#endif
diff --git a/target-mips/helper.h b/target-mips/helper.h
new file mode 100644
index 0000000..297ab64
--- /dev/null
+++ b/target-mips/helper.h
@@ -0,0 +1,290 @@
+#include "def-helper.h"
+
+DEF_HELPER_2(raise_exception_err, void, i32, int)
+DEF_HELPER_1(raise_exception, void, i32)
+
+#ifdef TARGET_MIPS64
+DEF_HELPER_3(ldl, tl, tl, tl, int)
+DEF_HELPER_3(ldr, tl, tl, tl, int)
+DEF_HELPER_3(sdl, void, tl, tl, int)
+DEF_HELPER_3(sdr, void, tl, tl, int)
+#endif
+DEF_HELPER_3(lwl, tl, tl, tl, int)
+DEF_HELPER_3(lwr, tl, tl, tl, int)
+DEF_HELPER_3(swl, void, tl, tl, int)
+DEF_HELPER_3(swr, void, tl, tl, int)
+
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_2(ll, tl, tl, int)
+DEF_HELPER_3(sc, tl, tl, tl, int)
+#ifdef TARGET_MIPS64
+DEF_HELPER_2(lld, tl, tl, int)
+DEF_HELPER_3(scd, tl, tl, tl, int)
+#endif
+#endif
+
+DEF_HELPER_FLAGS_1(clo, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(clz, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+#ifdef TARGET_MIPS64
+DEF_HELPER_FLAGS_1(dclo, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(dclz, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_2(dmult, void, tl, tl)
+DEF_HELPER_2(dmultu, void, tl, tl)
+#endif
+
+DEF_HELPER_2(muls, tl, tl, tl)
+DEF_HELPER_2(mulsu, tl, tl, tl)
+DEF_HELPER_2(macc, tl, tl, tl)
+DEF_HELPER_2(maccu, tl, tl, tl)
+DEF_HELPER_2(msac, tl, tl, tl)
+DEF_HELPER_2(msacu, tl, tl, tl)
+DEF_HELPER_2(mulhi, tl, tl, tl)
+DEF_HELPER_2(mulhiu, tl, tl, tl)
+DEF_HELPER_2(mulshi, tl, tl, tl)
+DEF_HELPER_2(mulshiu, tl, tl, tl)
+DEF_HELPER_2(macchi, tl, tl, tl)
+DEF_HELPER_2(macchiu, tl, tl, tl)
+DEF_HELPER_2(msachi, tl, tl, tl)
+DEF_HELPER_2(msachiu, tl, tl, tl)
+
+#ifndef CONFIG_USER_ONLY
+/* CP0 helpers */
+DEF_HELPER_0(mfc0_mvpcontrol, tl)
+DEF_HELPER_0(mfc0_mvpconf0, tl)
+DEF_HELPER_0(mfc0_mvpconf1, tl)
+DEF_HELPER_0(mfc0_random, tl)
+DEF_HELPER_0(mfc0_tcstatus, tl)
+DEF_HELPER_0(mftc0_tcstatus, tl)
+DEF_HELPER_0(mfc0_tcbind, tl)
+DEF_HELPER_0(mftc0_tcbind, tl)
+DEF_HELPER_0(mfc0_tcrestart, tl)
+DEF_HELPER_0(mftc0_tcrestart, tl)
+DEF_HELPER_0(mfc0_tchalt, tl)
+DEF_HELPER_0(mftc0_tchalt, tl)
+DEF_HELPER_0(mfc0_tccontext, tl)
+DEF_HELPER_0(mftc0_tccontext, tl)
+DEF_HELPER_0(mfc0_tcschedule, tl)
+DEF_HELPER_0(mftc0_tcschedule, tl)
+DEF_HELPER_0(mfc0_tcschefback, tl)
+DEF_HELPER_0(mftc0_tcschefback, tl)
+DEF_HELPER_0(mfc0_count, tl)
+DEF_HELPER_0(mftc0_entryhi, tl)
+DEF_HELPER_0(mftc0_status, tl)
+DEF_HELPER_0(mfc0_lladdr, tl)
+DEF_HELPER_1(mfc0_watchlo, tl, i32)
+DEF_HELPER_1(mfc0_watchhi, tl, i32)
+DEF_HELPER_0(mfc0_debug, tl)
+DEF_HELPER_0(mftc0_debug, tl)
+#ifdef TARGET_MIPS64
+DEF_HELPER_0(dmfc0_tcrestart, tl)
+DEF_HELPER_0(dmfc0_tchalt, tl)
+DEF_HELPER_0(dmfc0_tccontext, tl)
+DEF_HELPER_0(dmfc0_tcschedule, tl)
+DEF_HELPER_0(dmfc0_tcschefback, tl)
+DEF_HELPER_0(dmfc0_lladdr, tl)
+DEF_HELPER_1(dmfc0_watchlo, tl, i32)
+#endif /* TARGET_MIPS64 */
+
+DEF_HELPER_1(mtc0_index, void, tl)
+DEF_HELPER_1(mtc0_mvpcontrol, void, tl)
+DEF_HELPER_1(mtc0_vpecontrol, void, tl)
+DEF_HELPER_1(mtc0_vpeconf0, void, tl)
+DEF_HELPER_1(mtc0_vpeconf1, void, tl)
+DEF_HELPER_1(mtc0_yqmask, void, tl)
+DEF_HELPER_1(mtc0_vpeopt, void, tl)
+DEF_HELPER_1(mtc0_entrylo0, void, tl)
+DEF_HELPER_1(mtc0_tcstatus, void, tl)
+DEF_HELPER_1(mttc0_tcstatus, void, tl)
+DEF_HELPER_1(mtc0_tcbind, void, tl)
+DEF_HELPER_1(mttc0_tcbind, void, tl)
+DEF_HELPER_1(mtc0_tcrestart, void, tl)
+DEF_HELPER_1(mttc0_tcrestart, void, tl)
+DEF_HELPER_1(mtc0_tchalt, void, tl)
+DEF_HELPER_1(mttc0_tchalt, void, tl)
+DEF_HELPER_1(mtc0_tccontext, void, tl)
+DEF_HELPER_1(mttc0_tccontext, void, tl)
+DEF_HELPER_1(mtc0_tcschedule, void, tl)
+DEF_HELPER_1(mttc0_tcschedule, void, tl)
+DEF_HELPER_1(mtc0_tcschefback, void, tl)
+DEF_HELPER_1(mttc0_tcschefback, void, tl)
+DEF_HELPER_1(mtc0_entrylo1, void, tl)
+DEF_HELPER_1(mtc0_context, void, tl)
+DEF_HELPER_1(mtc0_pagemask, void, tl)
+DEF_HELPER_1(mtc0_pagegrain, void, tl)
+DEF_HELPER_1(mtc0_wired, void, tl)
+DEF_HELPER_1(mtc0_srsconf0, void, tl)
+DEF_HELPER_1(mtc0_srsconf1, void, tl)
+DEF_HELPER_1(mtc0_srsconf2, void, tl)
+DEF_HELPER_1(mtc0_srsconf3, void, tl)
+DEF_HELPER_1(mtc0_srsconf4, void, tl)
+DEF_HELPER_1(mtc0_hwrena, void, tl)
+DEF_HELPER_1(mtc0_count, void, tl)
+DEF_HELPER_1(mtc0_entryhi, void, tl)
+DEF_HELPER_1(mttc0_entryhi, void, tl)
+DEF_HELPER_1(mtc0_compare, void, tl)
+DEF_HELPER_1(mtc0_status, void, tl)
+DEF_HELPER_1(mttc0_status, void, tl)
+DEF_HELPER_1(mtc0_intctl, void, tl)
+DEF_HELPER_1(mtc0_srsctl, void, tl)
+DEF_HELPER_1(mtc0_cause, void, tl)
+DEF_HELPER_1(mtc0_ebase, void, tl)
+DEF_HELPER_1(mtc0_config0, void, tl)
+DEF_HELPER_1(mtc0_config2, void, tl)
+DEF_HELPER_1(mtc0_lladdr, void, tl)
+DEF_HELPER_2(mtc0_watchlo, void, tl, i32)
+DEF_HELPER_2(mtc0_watchhi, void, tl, i32)
+DEF_HELPER_1(mtc0_xcontext, void, tl)
+DEF_HELPER_1(mtc0_framemask, void, tl)
+DEF_HELPER_1(mtc0_debug, void, tl)
+DEF_HELPER_1(mttc0_debug, void, tl)
+DEF_HELPER_1(mtc0_performance0, void, tl)
+DEF_HELPER_1(mtc0_taglo, void, tl)
+DEF_HELPER_1(mtc0_datalo, void, tl)
+DEF_HELPER_1(mtc0_taghi, void, tl)
+DEF_HELPER_1(mtc0_datahi, void, tl)
+
+/* MIPS MT functions */
+DEF_HELPER_1(mftgpr, tl, i32);
+DEF_HELPER_1(mftlo, tl, i32)
+DEF_HELPER_1(mfthi, tl, i32)
+DEF_HELPER_1(mftacx, tl, i32)
+DEF_HELPER_0(mftdsp, tl)
+DEF_HELPER_2(mttgpr, void, tl, i32)
+DEF_HELPER_2(mttlo, void, tl, i32)
+DEF_HELPER_2(mtthi, void, tl, i32)
+DEF_HELPER_2(mttacx, void, tl, i32)
+DEF_HELPER_1(mttdsp, void, tl)
+DEF_HELPER_0(dmt, tl)
+DEF_HELPER_0(emt, tl)
+DEF_HELPER_0(dvpe, tl)
+DEF_HELPER_0(evpe, tl)
+#endif /* !CONFIG_USER_ONLY */
+
+/* microMIPS functions */
+DEF_HELPER_3(lwm, void, tl, tl, i32);
+DEF_HELPER_3(swm, void, tl, tl, i32);
+#ifdef TARGET_MIPS64
+DEF_HELPER_3(ldm, void, tl, tl, i32);
+DEF_HELPER_3(sdm, void, tl, tl, i32);
+#endif
+
+DEF_HELPER_2(fork, void, tl, tl)
+DEF_HELPER_1(yield, tl, tl)
+
+/* CP1 functions */
+DEF_HELPER_1(cfc1, tl, i32)
+DEF_HELPER_2(ctc1, void, tl, i32)
+
+DEF_HELPER_1(float_cvtd_s, i64, i32)
+DEF_HELPER_1(float_cvtd_w, i64, i32)
+DEF_HELPER_1(float_cvtd_l, i64, i64)
+DEF_HELPER_1(float_cvtl_d, i64, i64)
+DEF_HELPER_1(float_cvtl_s, i64, i32)
+DEF_HELPER_1(float_cvtps_pw, i64, i64)
+DEF_HELPER_1(float_cvtpw_ps, i64, i64)
+DEF_HELPER_1(float_cvts_d, i32, i64)
+DEF_HELPER_1(float_cvts_w, i32, i32)
+DEF_HELPER_1(float_cvts_l, i32, i64)
+DEF_HELPER_1(float_cvts_pl, i32, i32)
+DEF_HELPER_1(float_cvts_pu, i32, i32)
+DEF_HELPER_1(float_cvtw_s, i32, i32)
+DEF_HELPER_1(float_cvtw_d, i32, i64)
+
+DEF_HELPER_2(float_addr_ps, i64, i64, i64)
+DEF_HELPER_2(float_mulr_ps, i64, i64, i64)
+
+#define FOP_PROTO(op) \
+DEF_HELPER_1(float_ ## op ## l_s, i64, i32) \
+DEF_HELPER_1(float_ ## op ## l_d, i64, i64) \
+DEF_HELPER_1(float_ ## op ## w_s, i32, i32) \
+DEF_HELPER_1(float_ ## op ## w_d, i32, i64)
+FOP_PROTO(round)
+FOP_PROTO(trunc)
+FOP_PROTO(ceil)
+FOP_PROTO(floor)
+#undef FOP_PROTO
+
+#define FOP_PROTO(op) \
+DEF_HELPER_1(float_ ## op ## _s, i32, i32) \
+DEF_HELPER_1(float_ ## op ## _d, i64, i64)
+FOP_PROTO(sqrt)
+FOP_PROTO(rsqrt)
+FOP_PROTO(recip)
+#undef FOP_PROTO
+
+#define FOP_PROTO(op) \
+DEF_HELPER_1(float_ ## op ## _s, i32, i32) \
+DEF_HELPER_1(float_ ## op ## _d, i64, i64) \
+DEF_HELPER_1(float_ ## op ## _ps, i64, i64)
+FOP_PROTO(abs)
+FOP_PROTO(chs)
+FOP_PROTO(recip1)
+FOP_PROTO(rsqrt1)
+#undef FOP_PROTO
+
+#define FOP_PROTO(op) \
+DEF_HELPER_2(float_ ## op ## _s, i32, i32, i32) \
+DEF_HELPER_2(float_ ## op ## _d, i64, i64, i64) \
+DEF_HELPER_2(float_ ## op ## _ps, i64, i64, i64)
+FOP_PROTO(add)
+FOP_PROTO(sub)
+FOP_PROTO(mul)
+FOP_PROTO(div)
+FOP_PROTO(recip2)
+FOP_PROTO(rsqrt2)
+#undef FOP_PROTO
+
+#define FOP_PROTO(op) \
+DEF_HELPER_3(float_ ## op ## _s, i32, i32, i32, i32) \
+DEF_HELPER_3(float_ ## op ## _d, i64, i64, i64, i64) \
+DEF_HELPER_3(float_ ## op ## _ps, i64, i64, i64, i64)
+FOP_PROTO(muladd)
+FOP_PROTO(mulsub)
+FOP_PROTO(nmuladd)
+FOP_PROTO(nmulsub)
+#undef FOP_PROTO
+
+#define FOP_PROTO(op) \
+DEF_HELPER_3(cmp_d_ ## op, void, i64, i64, int) \
+DEF_HELPER_3(cmpabs_d_ ## op, void, i64, i64, int) \
+DEF_HELPER_3(cmp_s_ ## op, void, i32, i32, int) \
+DEF_HELPER_3(cmpabs_s_ ## op, void, i32, i32, int) \
+DEF_HELPER_3(cmp_ps_ ## op, void, i64, i64, int) \
+DEF_HELPER_3(cmpabs_ps_ ## op, void, i64, i64, int)
+FOP_PROTO(f)
+FOP_PROTO(un)
+FOP_PROTO(eq)
+FOP_PROTO(ueq)
+FOP_PROTO(olt)
+FOP_PROTO(ult)
+FOP_PROTO(ole)
+FOP_PROTO(ule)
+FOP_PROTO(sf)
+FOP_PROTO(ngle)
+FOP_PROTO(seq)
+FOP_PROTO(ngl)
+FOP_PROTO(lt)
+FOP_PROTO(nge)
+FOP_PROTO(le)
+FOP_PROTO(ngt)
+#undef FOP_PROTO
+
+/* Special functions */
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_0(tlbwi, void)
+DEF_HELPER_0(tlbwr, void)
+DEF_HELPER_0(tlbp, void)
+DEF_HELPER_0(tlbr, void)
+DEF_HELPER_0(di, tl)
+DEF_HELPER_0(ei, tl)
+DEF_HELPER_0(eret, void)
+DEF_HELPER_0(deret, void)
+#endif /* !CONFIG_USER_ONLY */
+DEF_HELPER_0(rdhwr_cpunum, tl)
+DEF_HELPER_0(rdhwr_synci_step, tl)
+DEF_HELPER_0(rdhwr_cc, tl)
+DEF_HELPER_0(rdhwr_ccres, tl)
+DEF_HELPER_1(pmon, void, int)
+DEF_HELPER_0(wait, void)
+
+#include "def-helper.h"
diff --git a/target-mips/machine.c b/target-mips/machine.c
new file mode 100644
index 0000000..9ffac71
--- /dev/null
+++ b/target-mips/machine.c
@@ -0,0 +1,308 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+#include "exec-all.h"
+
+static void save_tc(QEMUFile *f, TCState *tc)
+{
+ int i;
+
+ /* Save active TC */
+ for(i = 0; i < 32; i++)
+ qemu_put_betls(f, &tc->gpr[i]);
+ qemu_put_betls(f, &tc->PC);
+ for(i = 0; i < MIPS_DSP_ACC; i++)
+ qemu_put_betls(f, &tc->HI[i]);
+ for(i = 0; i < MIPS_DSP_ACC; i++)
+ qemu_put_betls(f, &tc->LO[i]);
+ for(i = 0; i < MIPS_DSP_ACC; i++)
+ qemu_put_betls(f, &tc->ACX[i]);
+ qemu_put_betls(f, &tc->DSPControl);
+ qemu_put_sbe32s(f, &tc->CP0_TCStatus);
+ qemu_put_sbe32s(f, &tc->CP0_TCBind);
+ qemu_put_betls(f, &tc->CP0_TCHalt);
+ qemu_put_betls(f, &tc->CP0_TCContext);
+ qemu_put_betls(f, &tc->CP0_TCSchedule);
+ qemu_put_betls(f, &tc->CP0_TCScheFBack);
+ qemu_put_sbe32s(f, &tc->CP0_Debug_tcstatus);
+}
+
+static void save_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu)
+{
+ int i;
+
+ for(i = 0; i < 32; i++)
+ qemu_put_be64s(f, &fpu->fpr[i].d);
+ qemu_put_s8s(f, &fpu->fp_status.float_detect_tininess);
+ qemu_put_s8s(f, &fpu->fp_status.float_rounding_mode);
+ qemu_put_s8s(f, &fpu->fp_status.float_exception_flags);
+ qemu_put_be32s(f, &fpu->fcr0);
+ qemu_put_be32s(f, &fpu->fcr31);
+}
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+ int i;
+
+ /* Save active TC */
+ save_tc(f, &env->active_tc);
+
+ /* Save active FPU */
+ save_fpu(f, &env->active_fpu);
+
+ /* Save MVP */
+ qemu_put_sbe32s(f, &env->mvp->CP0_MVPControl);
+ qemu_put_sbe32s(f, &env->mvp->CP0_MVPConf0);
+ qemu_put_sbe32s(f, &env->mvp->CP0_MVPConf1);
+
+ /* Save TLB */
+ qemu_put_be32s(f, &env->tlb->nb_tlb);
+ qemu_put_be32s(f, &env->tlb->tlb_in_use);
+ for(i = 0; i < MIPS_TLB_MAX; i++) {
+ uint16_t flags = ((env->tlb->mmu.r4k.tlb[i].G << 10) |
+ (env->tlb->mmu.r4k.tlb[i].C0 << 7) |
+ (env->tlb->mmu.r4k.tlb[i].C1 << 4) |
+ (env->tlb->mmu.r4k.tlb[i].V0 << 3) |
+ (env->tlb->mmu.r4k.tlb[i].V1 << 2) |
+ (env->tlb->mmu.r4k.tlb[i].D0 << 1) |
+ (env->tlb->mmu.r4k.tlb[i].D1 << 0));
+ uint8_t asid;
+
+ qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].VPN);
+ qemu_put_be32s(f, &env->tlb->mmu.r4k.tlb[i].PageMask);
+ asid = env->tlb->mmu.r4k.tlb[i].ASID;
+ qemu_put_8s(f, &asid);
+ qemu_put_be16s(f, &flags);
+ qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]);
+ qemu_put_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]);
+ }
+
+ /* Save CPU metastate */
+ qemu_put_be32s(f, &env->current_tc);
+ qemu_put_be32s(f, &env->current_fpu);
+ qemu_put_sbe32s(f, &env->error_code);
+ qemu_put_be32s(f, &env->hflags);
+ qemu_put_betls(f, &env->btarget);
+ i = env->bcond;
+ qemu_put_sbe32s(f, &i);
+
+ /* Save remaining CP1 registers */
+ qemu_put_sbe32s(f, &env->CP0_Index);
+ qemu_put_sbe32s(f, &env->CP0_Random);
+ qemu_put_sbe32s(f, &env->CP0_VPEControl);
+ qemu_put_sbe32s(f, &env->CP0_VPEConf0);
+ qemu_put_sbe32s(f, &env->CP0_VPEConf1);
+ qemu_put_betls(f, &env->CP0_YQMask);
+ qemu_put_betls(f, &env->CP0_VPESchedule);
+ qemu_put_betls(f, &env->CP0_VPEScheFBack);
+ qemu_put_sbe32s(f, &env->CP0_VPEOpt);
+ qemu_put_betls(f, &env->CP0_EntryLo0);
+ qemu_put_betls(f, &env->CP0_EntryLo1);
+ qemu_put_betls(f, &env->CP0_Context);
+ qemu_put_sbe32s(f, &env->CP0_PageMask);
+ qemu_put_sbe32s(f, &env->CP0_PageGrain);
+ qemu_put_sbe32s(f, &env->CP0_Wired);
+ qemu_put_sbe32s(f, &env->CP0_SRSConf0);
+ qemu_put_sbe32s(f, &env->CP0_SRSConf1);
+ qemu_put_sbe32s(f, &env->CP0_SRSConf2);
+ qemu_put_sbe32s(f, &env->CP0_SRSConf3);
+ qemu_put_sbe32s(f, &env->CP0_SRSConf4);
+ qemu_put_sbe32s(f, &env->CP0_HWREna);
+ qemu_put_betls(f, &env->CP0_BadVAddr);
+ qemu_put_sbe32s(f, &env->CP0_Count);
+ qemu_put_betls(f, &env->CP0_EntryHi);
+ qemu_put_sbe32s(f, &env->CP0_Compare);
+ qemu_put_sbe32s(f, &env->CP0_Status);
+ qemu_put_sbe32s(f, &env->CP0_IntCtl);
+ qemu_put_sbe32s(f, &env->CP0_SRSCtl);
+ qemu_put_sbe32s(f, &env->CP0_SRSMap);
+ qemu_put_sbe32s(f, &env->CP0_Cause);
+ qemu_put_betls(f, &env->CP0_EPC);
+ qemu_put_sbe32s(f, &env->CP0_PRid);
+ qemu_put_sbe32s(f, &env->CP0_EBase);
+ qemu_put_sbe32s(f, &env->CP0_Config0);
+ qemu_put_sbe32s(f, &env->CP0_Config1);
+ qemu_put_sbe32s(f, &env->CP0_Config2);
+ qemu_put_sbe32s(f, &env->CP0_Config3);
+ qemu_put_sbe32s(f, &env->CP0_Config6);
+ qemu_put_sbe32s(f, &env->CP0_Config7);
+ qemu_put_betls(f, &env->lladdr);
+ for(i = 0; i < 8; i++)
+ qemu_put_betls(f, &env->CP0_WatchLo[i]);
+ for(i = 0; i < 8; i++)
+ qemu_put_sbe32s(f, &env->CP0_WatchHi[i]);
+ qemu_put_betls(f, &env->CP0_XContext);
+ qemu_put_sbe32s(f, &env->CP0_Framemask);
+ qemu_put_sbe32s(f, &env->CP0_Debug);
+ qemu_put_betls(f, &env->CP0_DEPC);
+ qemu_put_sbe32s(f, &env->CP0_Performance0);
+ qemu_put_sbe32s(f, &env->CP0_TagLo);
+ qemu_put_sbe32s(f, &env->CP0_DataLo);
+ qemu_put_sbe32s(f, &env->CP0_TagHi);
+ qemu_put_sbe32s(f, &env->CP0_DataHi);
+ qemu_put_betls(f, &env->CP0_ErrorEPC);
+ qemu_put_sbe32s(f, &env->CP0_DESAVE);
+
+ /* Save inactive TC state */
+ for (i = 0; i < MIPS_SHADOW_SET_MAX; i++)
+ save_tc(f, &env->tcs[i]);
+ for (i = 0; i < MIPS_FPU_MAX; i++)
+ save_fpu(f, &env->fpus[i]);
+}
+
+static void load_tc(QEMUFile *f, TCState *tc)
+{
+ int i;
+
+ /* Save active TC */
+ for(i = 0; i < 32; i++)
+ qemu_get_betls(f, &tc->gpr[i]);
+ qemu_get_betls(f, &tc->PC);
+ for(i = 0; i < MIPS_DSP_ACC; i++)
+ qemu_get_betls(f, &tc->HI[i]);
+ for(i = 0; i < MIPS_DSP_ACC; i++)
+ qemu_get_betls(f, &tc->LO[i]);
+ for(i = 0; i < MIPS_DSP_ACC; i++)
+ qemu_get_betls(f, &tc->ACX[i]);
+ qemu_get_betls(f, &tc->DSPControl);
+ qemu_get_sbe32s(f, &tc->CP0_TCStatus);
+ qemu_get_sbe32s(f, &tc->CP0_TCBind);
+ qemu_get_betls(f, &tc->CP0_TCHalt);
+ qemu_get_betls(f, &tc->CP0_TCContext);
+ qemu_get_betls(f, &tc->CP0_TCSchedule);
+ qemu_get_betls(f, &tc->CP0_TCScheFBack);
+ qemu_get_sbe32s(f, &tc->CP0_Debug_tcstatus);
+}
+
+static void load_fpu(QEMUFile *f, CPUMIPSFPUContext *fpu)
+{
+ int i;
+
+ for(i = 0; i < 32; i++)
+ qemu_get_be64s(f, &fpu->fpr[i].d);
+ qemu_get_s8s(f, &fpu->fp_status.float_detect_tininess);
+ qemu_get_s8s(f, &fpu->fp_status.float_rounding_mode);
+ qemu_get_s8s(f, &fpu->fp_status.float_exception_flags);
+ qemu_get_be32s(f, &fpu->fcr0);
+ qemu_get_be32s(f, &fpu->fcr31);
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+ int i;
+
+ if (version_id != 3)
+ return -EINVAL;
+
+ /* Load active TC */
+ load_tc(f, &env->active_tc);
+
+ /* Load active FPU */
+ load_fpu(f, &env->active_fpu);
+
+ /* Load MVP */
+ qemu_get_sbe32s(f, &env->mvp->CP0_MVPControl);
+ qemu_get_sbe32s(f, &env->mvp->CP0_MVPConf0);
+ qemu_get_sbe32s(f, &env->mvp->CP0_MVPConf1);
+
+ /* Load TLB */
+ qemu_get_be32s(f, &env->tlb->nb_tlb);
+ qemu_get_be32s(f, &env->tlb->tlb_in_use);
+ for(i = 0; i < MIPS_TLB_MAX; i++) {
+ uint16_t flags;
+ uint8_t asid;
+
+ qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].VPN);
+ qemu_get_be32s(f, &env->tlb->mmu.r4k.tlb[i].PageMask);
+ qemu_get_8s(f, &asid);
+ env->tlb->mmu.r4k.tlb[i].ASID = asid;
+ qemu_get_be16s(f, &flags);
+ env->tlb->mmu.r4k.tlb[i].G = (flags >> 10) & 1;
+ env->tlb->mmu.r4k.tlb[i].C0 = (flags >> 7) & 3;
+ env->tlb->mmu.r4k.tlb[i].C1 = (flags >> 4) & 3;
+ env->tlb->mmu.r4k.tlb[i].V0 = (flags >> 3) & 1;
+ env->tlb->mmu.r4k.tlb[i].V1 = (flags >> 2) & 1;
+ env->tlb->mmu.r4k.tlb[i].D0 = (flags >> 1) & 1;
+ env->tlb->mmu.r4k.tlb[i].D1 = (flags >> 0) & 1;
+ qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[0]);
+ qemu_get_betls(f, &env->tlb->mmu.r4k.tlb[i].PFN[1]);
+ }
+
+ /* Load CPU metastate */
+ qemu_get_be32s(f, &env->current_tc);
+ qemu_get_be32s(f, &env->current_fpu);
+ qemu_get_sbe32s(f, &env->error_code);
+ qemu_get_be32s(f, &env->hflags);
+ qemu_get_betls(f, &env->btarget);
+ qemu_get_sbe32s(f, &i);
+ env->bcond = i;
+
+ /* Load remaining CP1 registers */
+ qemu_get_sbe32s(f, &env->CP0_Index);
+ qemu_get_sbe32s(f, &env->CP0_Random);
+ qemu_get_sbe32s(f, &env->CP0_VPEControl);
+ qemu_get_sbe32s(f, &env->CP0_VPEConf0);
+ qemu_get_sbe32s(f, &env->CP0_VPEConf1);
+ qemu_get_betls(f, &env->CP0_YQMask);
+ qemu_get_betls(f, &env->CP0_VPESchedule);
+ qemu_get_betls(f, &env->CP0_VPEScheFBack);
+ qemu_get_sbe32s(f, &env->CP0_VPEOpt);
+ qemu_get_betls(f, &env->CP0_EntryLo0);
+ qemu_get_betls(f, &env->CP0_EntryLo1);
+ qemu_get_betls(f, &env->CP0_Context);
+ qemu_get_sbe32s(f, &env->CP0_PageMask);
+ qemu_get_sbe32s(f, &env->CP0_PageGrain);
+ qemu_get_sbe32s(f, &env->CP0_Wired);
+ qemu_get_sbe32s(f, &env->CP0_SRSConf0);
+ qemu_get_sbe32s(f, &env->CP0_SRSConf1);
+ qemu_get_sbe32s(f, &env->CP0_SRSConf2);
+ qemu_get_sbe32s(f, &env->CP0_SRSConf3);
+ qemu_get_sbe32s(f, &env->CP0_SRSConf4);
+ qemu_get_sbe32s(f, &env->CP0_HWREna);
+ qemu_get_betls(f, &env->CP0_BadVAddr);
+ qemu_get_sbe32s(f, &env->CP0_Count);
+ qemu_get_betls(f, &env->CP0_EntryHi);
+ qemu_get_sbe32s(f, &env->CP0_Compare);
+ qemu_get_sbe32s(f, &env->CP0_Status);
+ qemu_get_sbe32s(f, &env->CP0_IntCtl);
+ qemu_get_sbe32s(f, &env->CP0_SRSCtl);
+ qemu_get_sbe32s(f, &env->CP0_SRSMap);
+ qemu_get_sbe32s(f, &env->CP0_Cause);
+ qemu_get_betls(f, &env->CP0_EPC);
+ qemu_get_sbe32s(f, &env->CP0_PRid);
+ qemu_get_sbe32s(f, &env->CP0_EBase);
+ qemu_get_sbe32s(f, &env->CP0_Config0);
+ qemu_get_sbe32s(f, &env->CP0_Config1);
+ qemu_get_sbe32s(f, &env->CP0_Config2);
+ qemu_get_sbe32s(f, &env->CP0_Config3);
+ qemu_get_sbe32s(f, &env->CP0_Config6);
+ qemu_get_sbe32s(f, &env->CP0_Config7);
+ qemu_get_betls(f, &env->lladdr);
+ for(i = 0; i < 8; i++)
+ qemu_get_betls(f, &env->CP0_WatchLo[i]);
+ for(i = 0; i < 8; i++)
+ qemu_get_sbe32s(f, &env->CP0_WatchHi[i]);
+ qemu_get_betls(f, &env->CP0_XContext);
+ qemu_get_sbe32s(f, &env->CP0_Framemask);
+ qemu_get_sbe32s(f, &env->CP0_Debug);
+ qemu_get_betls(f, &env->CP0_DEPC);
+ qemu_get_sbe32s(f, &env->CP0_Performance0);
+ qemu_get_sbe32s(f, &env->CP0_TagLo);
+ qemu_get_sbe32s(f, &env->CP0_DataLo);
+ qemu_get_sbe32s(f, &env->CP0_TagHi);
+ qemu_get_sbe32s(f, &env->CP0_DataHi);
+ qemu_get_betls(f, &env->CP0_ErrorEPC);
+ qemu_get_sbe32s(f, &env->CP0_DESAVE);
+
+ /* Load inactive TC state */
+ for (i = 0; i < MIPS_SHADOW_SET_MAX; i++)
+ load_tc(f, &env->tcs[i]);
+ for (i = 0; i < MIPS_FPU_MAX; i++)
+ load_fpu(f, &env->fpus[i]);
+
+ /* XXX: ensure compatiblity for halted bit ? */
+ tlb_flush(env, 1);
+ return 0;
+}
diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
new file mode 100644
index 0000000..bf094a3
--- /dev/null
+++ b/target-mips/mips-defs.h
@@ -0,0 +1,72 @@
+#if !defined (__QEMU_MIPS_DEFS_H__)
+#define __QEMU_MIPS_DEFS_H__
+
+/* If we want to use host float regs... */
+//#define USE_HOST_FLOAT_REGS
+
+/* Real pages are variable size... */
+#define TARGET_PAGE_BITS 12
+#define MIPS_TLB_MAX 128
+
+#if defined(TARGET_MIPS64)
+#define TARGET_LONG_BITS 64
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_VIRT_ADDR_SPACE_BITS 42
+#else
+#define TARGET_LONG_BITS 32
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#endif
+
+/* Masks used to mark instructions to indicate which ISA level they
+ were introduced in. */
+#define ISA_MIPS1 0x00000001
+#define ISA_MIPS2 0x00000002
+#define ISA_MIPS3 0x00000004
+#define ISA_MIPS4 0x00000008
+#define ISA_MIPS5 0x00000010
+#define ISA_MIPS32 0x00000020
+#define ISA_MIPS32R2 0x00000040
+#define ISA_MIPS64 0x00000080
+#define ISA_MIPS64R2 0x00000100
+
+/* MIPS ASEs. */
+#define ASE_MIPS16 0x00001000
+#define ASE_MIPS3D 0x00002000
+#define ASE_MDMX 0x00004000
+#define ASE_DSP 0x00008000
+#define ASE_DSPR2 0x00010000
+#define ASE_MT 0x00020000
+#define ASE_SMARTMIPS 0x00040000
+#define ASE_MICROMIPS 0x00080000
+
+/* Chip specific instructions. */
+#define INSN_LOONGSON2E 0x20000000
+#define INSN_LOONGSON2F 0x40000000
+#define INSN_VR54XX 0x80000000
+
+/* MIPS CPU defines. */
+#define CPU_MIPS1 (ISA_MIPS1)
+#define CPU_MIPS2 (CPU_MIPS1 | ISA_MIPS2)
+#define CPU_MIPS3 (CPU_MIPS2 | ISA_MIPS3)
+#define CPU_MIPS4 (CPU_MIPS3 | ISA_MIPS4)
+#define CPU_VR54XX (CPU_MIPS4 | INSN_VR54XX)
+#define CPU_LOONGSON2E (CPU_MIPS3 | INSN_LOONGSON2E)
+#define CPU_LOONGSON2F (CPU_MIPS3 | INSN_LOONGSON2F)
+
+#define CPU_MIPS5 (CPU_MIPS4 | ISA_MIPS5)
+
+/* MIPS Technologies "Release 1" */
+#define CPU_MIPS32 (CPU_MIPS2 | ISA_MIPS32)
+#define CPU_MIPS64 (CPU_MIPS5 | CPU_MIPS32 | ISA_MIPS64)
+
+/* MIPS Technologies "Release 2" */
+#define CPU_MIPS32R2 (CPU_MIPS32 | ISA_MIPS32R2)
+#define CPU_MIPS64R2 (CPU_MIPS64 | CPU_MIPS32R2 | ISA_MIPS64R2)
+
+/* Strictly follow the architecture standard:
+ - Disallow "special" instruction handling for PMON/SPIM.
+ Note that we still maintain Count/Compare to match the host clock. */
+//#define MIPS_STRICT_STANDARD 1
+
+#endif /* !defined (__QEMU_MIPS_DEFS_H__) */
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
new file mode 100644
index 0000000..669faf1
--- /dev/null
+++ b/target-mips/op_helper.c
@@ -0,0 +1,3044 @@
+/*
+ * MIPS emulation helpers for qemu.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * 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 <stdlib.h>
+#include "exec.h"
+
+#include "host-utils.h"
+
+#include "helper.h"
+
+#ifndef CONFIG_USER_ONLY
+static inline void cpu_mips_tlb_flush (CPUState *env, int flush_global);
+#endif
+
+/*****************************************************************************/
+/* Exceptions processing helpers */
+
+void helper_raise_exception_err (uint32_t exception, int error_code)
+{
+#if 1
+ if (exception < 0x100)
+ qemu_log("%s: %d %d\n", __func__, exception, error_code);
+#endif
+ env->exception_index = exception;
+ env->error_code = error_code;
+ cpu_loop_exit();
+}
+
+void helper_raise_exception (uint32_t exception)
+{
+ helper_raise_exception_err(exception, 0);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void do_restore_state (void *pc_ptr)
+{
+ TranslationBlock *tb;
+ unsigned long pc = (unsigned long) pc_ptr;
+
+ tb = tb_find_pc (pc);
+ if (tb) {
+ cpu_restore_state (tb, env, pc, NULL);
+ }
+}
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+#define HELPER_LD(name, insn, type) \
+static inline type do_##name(target_ulong addr, int mem_idx) \
+{ \
+ return (type) insn##_raw(addr); \
+}
+#else
+#define HELPER_LD(name, insn, type) \
+static inline type do_##name(target_ulong addr, int mem_idx) \
+{ \
+ switch (mem_idx) \
+ { \
+ case 0: return (type) insn##_kernel(addr); break; \
+ case 1: return (type) insn##_super(addr); break; \
+ default: \
+ case 2: return (type) insn##_user(addr); break; \
+ } \
+}
+#endif
+HELPER_LD(lbu, ldub, uint8_t)
+HELPER_LD(lw, ldl, int32_t)
+#ifdef TARGET_MIPS64
+HELPER_LD(ld, ldq, int64_t)
+#endif
+#undef HELPER_LD
+
+#if defined(CONFIG_USER_ONLY)
+#define HELPER_ST(name, insn, type) \
+static inline void do_##name(target_ulong addr, type val, int mem_idx) \
+{ \
+ insn##_raw(addr, val); \
+}
+#else
+#define HELPER_ST(name, insn, type) \
+static inline void do_##name(target_ulong addr, type val, int mem_idx) \
+{ \
+ switch (mem_idx) \
+ { \
+ case 0: insn##_kernel(addr, val); break; \
+ case 1: insn##_super(addr, val); break; \
+ default: \
+ case 2: insn##_user(addr, val); break; \
+ } \
+}
+#endif
+HELPER_ST(sb, stb, uint8_t)
+HELPER_ST(sw, stl, uint32_t)
+#ifdef TARGET_MIPS64
+HELPER_ST(sd, stq, uint64_t)
+#endif
+#undef HELPER_ST
+
+target_ulong helper_clo (target_ulong arg1)
+{
+ return clo32(arg1);
+}
+
+target_ulong helper_clz (target_ulong arg1)
+{
+ return clz32(arg1);
+}
+
+#if defined(TARGET_MIPS64)
+target_ulong helper_dclo (target_ulong arg1)
+{
+ return clo64(arg1);
+}
+
+target_ulong helper_dclz (target_ulong arg1)
+{
+ return clz64(arg1);
+}
+#endif /* TARGET_MIPS64 */
+
+/* 64 bits arithmetic for 32 bits hosts */
+static inline uint64_t get_HILO (void)
+{
+ return ((uint64_t)(env->active_tc.HI[0]) << 32) | (uint32_t)env->active_tc.LO[0];
+}
+
+static inline void set_HILO (uint64_t HILO)
+{
+ env->active_tc.LO[0] = (int32_t)HILO;
+ env->active_tc.HI[0] = (int32_t)(HILO >> 32);
+}
+
+static inline void set_HIT0_LO (target_ulong arg1, uint64_t HILO)
+{
+ env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
+ arg1 = env->active_tc.HI[0] = (int32_t)(HILO >> 32);
+}
+
+static inline void set_HI_LOT0 (target_ulong arg1, uint64_t HILO)
+{
+ arg1 = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF);
+ env->active_tc.HI[0] = (int32_t)(HILO >> 32);
+}
+
+/* Multiplication variants of the vr54xx. */
+target_ulong helper_muls (target_ulong arg1, target_ulong arg2)
+{
+ set_HI_LOT0(arg1, 0 - ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_mulsu (target_ulong arg1, target_ulong arg2)
+{
+ set_HI_LOT0(arg1, 0 - ((uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_macc (target_ulong arg1, target_ulong arg2)
+{
+ set_HI_LOT0(arg1, ((int64_t)get_HILO()) + ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_macchi (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, ((int64_t)get_HILO()) + ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_maccu (target_ulong arg1, target_ulong arg2)
+{
+ set_HI_LOT0(arg1, ((uint64_t)get_HILO()) + ((uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_macchiu (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, ((uint64_t)get_HILO()) + ((uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_msac (target_ulong arg1, target_ulong arg2)
+{
+ set_HI_LOT0(arg1, ((int64_t)get_HILO()) - ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_msachi (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, ((int64_t)get_HILO()) - ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_msacu (target_ulong arg1, target_ulong arg2)
+{
+ set_HI_LOT0(arg1, ((uint64_t)get_HILO()) - ((uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_msachiu (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, ((uint64_t)get_HILO()) - ((uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_mulhi (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2);
+
+ return arg1;
+}
+
+target_ulong helper_mulhiu (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2);
+
+ return arg1;
+}
+
+target_ulong helper_mulshi (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, 0 - ((int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2));
+
+ return arg1;
+}
+
+target_ulong helper_mulshiu (target_ulong arg1, target_ulong arg2)
+{
+ set_HIT0_LO(arg1, 0 - ((uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2));
+
+ return arg1;
+}
+
+#ifdef TARGET_MIPS64
+void helper_dmult (target_ulong arg1, target_ulong arg2)
+{
+ muls64(&(env->active_tc.LO[0]), &(env->active_tc.HI[0]), arg1, arg2);
+}
+
+void helper_dmultu (target_ulong arg1, target_ulong arg2)
+{
+ mulu64(&(env->active_tc.LO[0]), &(env->active_tc.HI[0]), arg1, arg2);
+}
+#endif
+
+#ifndef CONFIG_USER_ONLY
+
+static inline target_phys_addr_t do_translate_address(target_ulong address, int rw)
+{
+ target_phys_addr_t lladdr;
+
+ lladdr = cpu_mips_translate_address(env, address, rw);
+
+ if (lladdr == -1LL) {
+ cpu_loop_exit();
+ } else {
+ return lladdr;
+ }
+}
+
+#define HELPER_LD_ATOMIC(name, insn) \
+target_ulong helper_##name(target_ulong arg, int mem_idx) \
+{ \
+ env->lladdr = do_translate_address(arg, 0); \
+ env->llval = do_##insn(arg, mem_idx); \
+ return env->llval; \
+}
+HELPER_LD_ATOMIC(ll, lw)
+#ifdef TARGET_MIPS64
+HELPER_LD_ATOMIC(lld, ld)
+#endif
+#undef HELPER_LD_ATOMIC
+
+#define HELPER_ST_ATOMIC(name, ld_insn, st_insn, almask) \
+target_ulong helper_##name(target_ulong arg1, target_ulong arg2, int mem_idx) \
+{ \
+ target_long tmp; \
+ \
+ if (arg2 & almask) { \
+ env->CP0_BadVAddr = arg2; \
+ helper_raise_exception(EXCP_AdES); \
+ } \
+ if (do_translate_address(arg2, 1) == env->lladdr) { \
+ tmp = do_##ld_insn(arg2, mem_idx); \
+ if (tmp == env->llval) { \
+ do_##st_insn(arg2, arg1, mem_idx); \
+ return 1; \
+ } \
+ } \
+ return 0; \
+}
+HELPER_ST_ATOMIC(sc, lw, sw, 0x3)
+#ifdef TARGET_MIPS64
+HELPER_ST_ATOMIC(scd, ld, sd, 0x7)
+#endif
+#undef HELPER_ST_ATOMIC
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define GET_LMASK(v) ((v) & 3)
+#define GET_OFFSET(addr, offset) (addr + (offset))
+#else
+#define GET_LMASK(v) (((v) & 3) ^ 3)
+#define GET_OFFSET(addr, offset) (addr - (offset))
+#endif
+
+target_ulong helper_lwl(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ target_ulong tmp;
+
+ tmp = do_lbu(arg2, mem_idx);
+ arg1 = (arg1 & 0x00FFFFFF) | (tmp << 24);
+
+ if (GET_LMASK(arg2) <= 2) {
+ tmp = do_lbu(GET_OFFSET(arg2, 1), mem_idx);
+ arg1 = (arg1 & 0xFF00FFFF) | (tmp << 16);
+ }
+
+ if (GET_LMASK(arg2) <= 1) {
+ tmp = do_lbu(GET_OFFSET(arg2, 2), mem_idx);
+ arg1 = (arg1 & 0xFFFF00FF) | (tmp << 8);
+ }
+
+ if (GET_LMASK(arg2) == 0) {
+ tmp = do_lbu(GET_OFFSET(arg2, 3), mem_idx);
+ arg1 = (arg1 & 0xFFFFFF00) | tmp;
+ }
+ return (int32_t)arg1;
+}
+
+target_ulong helper_lwr(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ target_ulong tmp;
+
+ tmp = do_lbu(arg2, mem_idx);
+ arg1 = (arg1 & 0xFFFFFF00) | tmp;
+
+ if (GET_LMASK(arg2) >= 1) {
+ tmp = do_lbu(GET_OFFSET(arg2, -1), mem_idx);
+ arg1 = (arg1 & 0xFFFF00FF) | (tmp << 8);
+ }
+
+ if (GET_LMASK(arg2) >= 2) {
+ tmp = do_lbu(GET_OFFSET(arg2, -2), mem_idx);
+ arg1 = (arg1 & 0xFF00FFFF) | (tmp << 16);
+ }
+
+ if (GET_LMASK(arg2) == 3) {
+ tmp = do_lbu(GET_OFFSET(arg2, -3), mem_idx);
+ arg1 = (arg1 & 0x00FFFFFF) | (tmp << 24);
+ }
+ return (int32_t)arg1;
+}
+
+void helper_swl(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ do_sb(arg2, (uint8_t)(arg1 >> 24), mem_idx);
+
+ if (GET_LMASK(arg2) <= 2)
+ do_sb(GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx);
+
+ if (GET_LMASK(arg2) <= 1)
+ do_sb(GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx);
+
+ if (GET_LMASK(arg2) == 0)
+ do_sb(GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx);
+}
+
+void helper_swr(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ do_sb(arg2, (uint8_t)arg1, mem_idx);
+
+ if (GET_LMASK(arg2) >= 1)
+ do_sb(GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx);
+
+ if (GET_LMASK(arg2) >= 2)
+ do_sb(GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx);
+
+ if (GET_LMASK(arg2) == 3)
+ do_sb(GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx);
+}
+
+#if defined(TARGET_MIPS64)
+/* "half" load and stores. We must do the memory access inline,
+ or fault handling won't work. */
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define GET_LMASK64(v) ((v) & 7)
+#else
+#define GET_LMASK64(v) (((v) & 7) ^ 7)
+#endif
+
+target_ulong helper_ldl(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ uint64_t tmp;
+
+ tmp = do_lbu(arg2, mem_idx);
+ arg1 = (arg1 & 0x00FFFFFFFFFFFFFFULL) | (tmp << 56);
+
+ if (GET_LMASK64(arg2) <= 6) {
+ tmp = do_lbu(GET_OFFSET(arg2, 1), mem_idx);
+ arg1 = (arg1 & 0xFF00FFFFFFFFFFFFULL) | (tmp << 48);
+ }
+
+ if (GET_LMASK64(arg2) <= 5) {
+ tmp = do_lbu(GET_OFFSET(arg2, 2), mem_idx);
+ arg1 = (arg1 & 0xFFFF00FFFFFFFFFFULL) | (tmp << 40);
+ }
+
+ if (GET_LMASK64(arg2) <= 4) {
+ tmp = do_lbu(GET_OFFSET(arg2, 3), mem_idx);
+ arg1 = (arg1 & 0xFFFFFF00FFFFFFFFULL) | (tmp << 32);
+ }
+
+ if (GET_LMASK64(arg2) <= 3) {
+ tmp = do_lbu(GET_OFFSET(arg2, 4), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFF00FFFFFFULL) | (tmp << 24);
+ }
+
+ if (GET_LMASK64(arg2) <= 2) {
+ tmp = do_lbu(GET_OFFSET(arg2, 5), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFFFF00FFFFULL) | (tmp << 16);
+ }
+
+ if (GET_LMASK64(arg2) <= 1) {
+ tmp = do_lbu(GET_OFFSET(arg2, 6), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFFFFFF00FFULL) | (tmp << 8);
+ }
+
+ if (GET_LMASK64(arg2) == 0) {
+ tmp = do_lbu(GET_OFFSET(arg2, 7), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFFFFFFFF00ULL) | tmp;
+ }
+
+ return arg1;
+}
+
+target_ulong helper_ldr(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ uint64_t tmp;
+
+ tmp = do_lbu(arg2, mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFFFFFFFF00ULL) | tmp;
+
+ if (GET_LMASK64(arg2) >= 1) {
+ tmp = do_lbu(GET_OFFSET(arg2, -1), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFFFFFF00FFULL) | (tmp << 8);
+ }
+
+ if (GET_LMASK64(arg2) >= 2) {
+ tmp = do_lbu(GET_OFFSET(arg2, -2), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFFFF00FFFFULL) | (tmp << 16);
+ }
+
+ if (GET_LMASK64(arg2) >= 3) {
+ tmp = do_lbu(GET_OFFSET(arg2, -3), mem_idx);
+ arg1 = (arg1 & 0xFFFFFFFF00FFFFFFULL) | (tmp << 24);
+ }
+
+ if (GET_LMASK64(arg2) >= 4) {
+ tmp = do_lbu(GET_OFFSET(arg2, -4), mem_idx);
+ arg1 = (arg1 & 0xFFFFFF00FFFFFFFFULL) | (tmp << 32);
+ }
+
+ if (GET_LMASK64(arg2) >= 5) {
+ tmp = do_lbu(GET_OFFSET(arg2, -5), mem_idx);
+ arg1 = (arg1 & 0xFFFF00FFFFFFFFFFULL) | (tmp << 40);
+ }
+
+ if (GET_LMASK64(arg2) >= 6) {
+ tmp = do_lbu(GET_OFFSET(arg2, -6), mem_idx);
+ arg1 = (arg1 & 0xFF00FFFFFFFFFFFFULL) | (tmp << 48);
+ }
+
+ if (GET_LMASK64(arg2) == 7) {
+ tmp = do_lbu(GET_OFFSET(arg2, -7), mem_idx);
+ arg1 = (arg1 & 0x00FFFFFFFFFFFFFFULL) | (tmp << 56);
+ }
+
+ return arg1;
+}
+
+void helper_sdl(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ do_sb(arg2, (uint8_t)(arg1 >> 56), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 6)
+ do_sb(GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 5)
+ do_sb(GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 4)
+ do_sb(GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 3)
+ do_sb(GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 2)
+ do_sb(GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 1)
+ do_sb(GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx);
+
+ if (GET_LMASK64(arg2) <= 0)
+ do_sb(GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx);
+}
+
+void helper_sdr(target_ulong arg1, target_ulong arg2, int mem_idx)
+{
+ do_sb(arg2, (uint8_t)arg1, mem_idx);
+
+ if (GET_LMASK64(arg2) >= 1)
+ do_sb(GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx);
+
+ if (GET_LMASK64(arg2) >= 2)
+ do_sb(GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx);
+
+ if (GET_LMASK64(arg2) >= 3)
+ do_sb(GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx);
+
+ if (GET_LMASK64(arg2) >= 4)
+ do_sb(GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx);
+
+ if (GET_LMASK64(arg2) >= 5)
+ do_sb(GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx);
+
+ if (GET_LMASK64(arg2) >= 6)
+ do_sb(GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx);
+
+ if (GET_LMASK64(arg2) == 7)
+ do_sb(GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx);
+}
+#endif /* TARGET_MIPS64 */
+
+static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 };
+
+void helper_lwm (target_ulong addr, target_ulong reglist, uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+#ifdef CONFIG_USER_ONLY
+#undef ldfun
+#define ldfun ldl_raw
+#else
+ uint32_t (*ldfun)(target_ulong);
+
+ switch (mem_idx)
+ {
+ case 0: ldfun = ldl_kernel; break;
+ case 1: ldfun = ldl_super; break;
+ default:
+ case 2: ldfun = ldl_user; break;
+ }
+#endif
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ env->active_tc.gpr[multiple_regs[i]] = (target_long) ldfun(addr);
+ addr += 4;
+ }
+ }
+
+ if (do_r31) {
+ env->active_tc.gpr[31] = (target_long) ldfun(addr);
+ }
+}
+
+void helper_swm (target_ulong addr, target_ulong reglist, uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+#ifdef CONFIG_USER_ONLY
+#undef stfun
+#define stfun stl_raw
+#else
+ void (*stfun)(target_ulong, uint32_t);
+
+ switch (mem_idx)
+ {
+ case 0: stfun = stl_kernel; break;
+ case 1: stfun = stl_super; break;
+ default:
+ case 2: stfun = stl_user; break;
+ }
+#endif
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ stfun(addr, env->active_tc.gpr[multiple_regs[i]]);
+ addr += 4;
+ }
+ }
+
+ if (do_r31) {
+ stfun(addr, env->active_tc.gpr[31]);
+ }
+}
+
+#if defined(TARGET_MIPS64)
+void helper_ldm (target_ulong addr, target_ulong reglist, uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+#ifdef CONFIG_USER_ONLY
+#undef ldfun
+#define ldfun ldq_raw
+#else
+ uint64_t (*ldfun)(target_ulong);
+
+ switch (mem_idx)
+ {
+ case 0: ldfun = ldq_kernel; break;
+ case 1: ldfun = ldq_super; break;
+ default:
+ case 2: ldfun = ldq_user; break;
+ }
+#endif
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ env->active_tc.gpr[multiple_regs[i]] = ldfun(addr);
+ addr += 8;
+ }
+ }
+
+ if (do_r31) {
+ env->active_tc.gpr[31] = ldfun(addr);
+ }
+}
+
+void helper_sdm (target_ulong addr, target_ulong reglist, uint32_t mem_idx)
+{
+ target_ulong base_reglist = reglist & 0xf;
+ target_ulong do_r31 = reglist & 0x10;
+#ifdef CONFIG_USER_ONLY
+#undef stfun
+#define stfun stq_raw
+#else
+ void (*stfun)(target_ulong, uint64_t);
+
+ switch (mem_idx)
+ {
+ case 0: stfun = stq_kernel; break;
+ case 1: stfun = stq_super; break;
+ default:
+ case 2: stfun = stq_user; break;
+ }
+#endif
+
+ if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) {
+ target_ulong i;
+
+ for (i = 0; i < base_reglist; i++) {
+ stfun(addr, env->active_tc.gpr[multiple_regs[i]]);
+ addr += 8;
+ }
+ }
+
+ if (do_r31) {
+ stfun(addr, env->active_tc.gpr[31]);
+ }
+}
+#endif
+
+#ifndef CONFIG_USER_ONLY
+/* CP0 helpers */
+target_ulong helper_mfc0_mvpcontrol (void)
+{
+ return env->mvp->CP0_MVPControl;
+}
+
+target_ulong helper_mfc0_mvpconf0 (void)
+{
+ return env->mvp->CP0_MVPConf0;
+}
+
+target_ulong helper_mfc0_mvpconf1 (void)
+{
+ return env->mvp->CP0_MVPConf1;
+}
+
+target_ulong helper_mfc0_random (void)
+{
+ return (int32_t)cpu_mips_get_random(env);
+}
+
+target_ulong helper_mfc0_tcstatus (void)
+{
+ return env->active_tc.CP0_TCStatus;
+}
+
+target_ulong helper_mftc0_tcstatus(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.CP0_TCStatus;
+ else
+ return env->tcs[other_tc].CP0_TCStatus;
+}
+
+target_ulong helper_mfc0_tcbind (void)
+{
+ return env->active_tc.CP0_TCBind;
+}
+
+target_ulong helper_mftc0_tcbind(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.CP0_TCBind;
+ else
+ return env->tcs[other_tc].CP0_TCBind;
+}
+
+target_ulong helper_mfc0_tcrestart (void)
+{
+ return env->active_tc.PC;
+}
+
+target_ulong helper_mftc0_tcrestart(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.PC;
+ else
+ return env->tcs[other_tc].PC;
+}
+
+target_ulong helper_mfc0_tchalt (void)
+{
+ return env->active_tc.CP0_TCHalt;
+}
+
+target_ulong helper_mftc0_tchalt(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.CP0_TCHalt;
+ else
+ return env->tcs[other_tc].CP0_TCHalt;
+}
+
+target_ulong helper_mfc0_tccontext (void)
+{
+ return env->active_tc.CP0_TCContext;
+}
+
+target_ulong helper_mftc0_tccontext(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.CP0_TCContext;
+ else
+ return env->tcs[other_tc].CP0_TCContext;
+}
+
+target_ulong helper_mfc0_tcschedule (void)
+{
+ return env->active_tc.CP0_TCSchedule;
+}
+
+target_ulong helper_mftc0_tcschedule(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.CP0_TCSchedule;
+ else
+ return env->tcs[other_tc].CP0_TCSchedule;
+}
+
+target_ulong helper_mfc0_tcschefback (void)
+{
+ return env->active_tc.CP0_TCScheFBack;
+}
+
+target_ulong helper_mftc0_tcschefback(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.CP0_TCScheFBack;
+ else
+ return env->tcs[other_tc].CP0_TCScheFBack;
+}
+
+target_ulong helper_mfc0_count (void)
+{
+ return (int32_t)cpu_mips_get_count(env);
+}
+
+target_ulong helper_mftc0_entryhi(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ int32_t tcstatus;
+
+ if (other_tc == env->current_tc)
+ tcstatus = env->active_tc.CP0_TCStatus;
+ else
+ tcstatus = env->tcs[other_tc].CP0_TCStatus;
+
+ return (env->CP0_EntryHi & ~0xff) | (tcstatus & 0xff);
+}
+
+target_ulong helper_mftc0_status(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ target_ulong t0;
+ int32_t tcstatus;
+
+ if (other_tc == env->current_tc)
+ tcstatus = env->active_tc.CP0_TCStatus;
+ else
+ tcstatus = env->tcs[other_tc].CP0_TCStatus;
+
+ t0 = env->CP0_Status & ~0xf1000018;
+ t0 |= tcstatus & (0xf << CP0TCSt_TCU0);
+ t0 |= (tcstatus & (1 << CP0TCSt_TMX)) >> (CP0TCSt_TMX - CP0St_MX);
+ t0 |= (tcstatus & (0x3 << CP0TCSt_TKSU)) >> (CP0TCSt_TKSU - CP0St_KSU);
+
+ return t0;
+}
+
+target_ulong helper_mfc0_lladdr (void)
+{
+ return (int32_t)(env->lladdr >> env->CP0_LLAddr_shift);
+}
+
+target_ulong helper_mfc0_watchlo (uint32_t sel)
+{
+ return (int32_t)env->CP0_WatchLo[sel];
+}
+
+target_ulong helper_mfc0_watchhi (uint32_t sel)
+{
+ return env->CP0_WatchHi[sel];
+}
+
+target_ulong helper_mfc0_debug (void)
+{
+ target_ulong t0 = env->CP0_Debug;
+ if (env->hflags & MIPS_HFLAG_DM)
+ t0 |= 1 << CP0DB_DM;
+
+ return t0;
+}
+
+target_ulong helper_mftc0_debug(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ int32_t tcstatus;
+
+ if (other_tc == env->current_tc)
+ tcstatus = env->active_tc.CP0_Debug_tcstatus;
+ else
+ tcstatus = env->tcs[other_tc].CP0_Debug_tcstatus;
+
+ /* XXX: Might be wrong, check with EJTAG spec. */
+ return (env->CP0_Debug & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) |
+ (tcstatus & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt)));
+}
+
+#if defined(TARGET_MIPS64)
+target_ulong helper_dmfc0_tcrestart (void)
+{
+ return env->active_tc.PC;
+}
+
+target_ulong helper_dmfc0_tchalt (void)
+{
+ return env->active_tc.CP0_TCHalt;
+}
+
+target_ulong helper_dmfc0_tccontext (void)
+{
+ return env->active_tc.CP0_TCContext;
+}
+
+target_ulong helper_dmfc0_tcschedule (void)
+{
+ return env->active_tc.CP0_TCSchedule;
+}
+
+target_ulong helper_dmfc0_tcschefback (void)
+{
+ return env->active_tc.CP0_TCScheFBack;
+}
+
+target_ulong helper_dmfc0_lladdr (void)
+{
+ return env->lladdr >> env->CP0_LLAddr_shift;
+}
+
+target_ulong helper_dmfc0_watchlo (uint32_t sel)
+{
+ return env->CP0_WatchLo[sel];
+}
+#endif /* TARGET_MIPS64 */
+
+void helper_mtc0_index (target_ulong arg1)
+{
+ int num = 1;
+ unsigned int tmp = env->tlb->nb_tlb;
+
+ do {
+ tmp >>= 1;
+ num <<= 1;
+ } while (tmp);
+ env->CP0_Index = (env->CP0_Index & 0x80000000) | (arg1 & (num - 1));
+}
+
+void helper_mtc0_mvpcontrol (target_ulong arg1)
+{
+ uint32_t mask = 0;
+ uint32_t newval;
+
+ if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP))
+ mask |= (1 << CP0MVPCo_CPA) | (1 << CP0MVPCo_VPC) |
+ (1 << CP0MVPCo_EVP);
+ if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC))
+ mask |= (1 << CP0MVPCo_STLB);
+ newval = (env->mvp->CP0_MVPControl & ~mask) | (arg1 & mask);
+
+ // TODO: Enable/disable shared TLB, enable/disable VPEs.
+
+ env->mvp->CP0_MVPControl = newval;
+}
+
+void helper_mtc0_vpecontrol (target_ulong arg1)
+{
+ uint32_t mask;
+ uint32_t newval;
+
+ mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) |
+ (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC);
+ newval = (env->CP0_VPEControl & ~mask) | (arg1 & mask);
+
+ /* Yield scheduler intercept not implemented. */
+ /* Gating storage scheduler intercept not implemented. */
+
+ // TODO: Enable/disable TCs.
+
+ env->CP0_VPEControl = newval;
+}
+
+void helper_mtc0_vpeconf0 (target_ulong arg1)
+{
+ uint32_t mask = 0;
+ uint32_t newval;
+
+ if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) {
+ if (env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA))
+ mask |= (0xff << CP0VPEC0_XTC);
+ mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA);
+ }
+ newval = (env->CP0_VPEConf0 & ~mask) | (arg1 & mask);
+
+ // TODO: TC exclusive handling due to ERL/EXL.
+
+ env->CP0_VPEConf0 = newval;
+}
+
+void helper_mtc0_vpeconf1 (target_ulong arg1)
+{
+ uint32_t mask = 0;
+ uint32_t newval;
+
+ if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC))
+ mask |= (0xff << CP0VPEC1_NCX) | (0xff << CP0VPEC1_NCP2) |
+ (0xff << CP0VPEC1_NCP1);
+ newval = (env->CP0_VPEConf1 & ~mask) | (arg1 & mask);
+
+ /* UDI not implemented. */
+ /* CP2 not implemented. */
+
+ // TODO: Handle FPU (CP1) binding.
+
+ env->CP0_VPEConf1 = newval;
+}
+
+void helper_mtc0_yqmask (target_ulong arg1)
+{
+ /* Yield qualifier inputs not implemented. */
+ env->CP0_YQMask = 0x00000000;
+}
+
+void helper_mtc0_vpeopt (target_ulong arg1)
+{
+ env->CP0_VPEOpt = arg1 & 0x0000ffff;
+}
+
+void helper_mtc0_entrylo0 (target_ulong arg1)
+{
+ /* Large physaddr (PABITS) not implemented */
+ /* 1k pages not implemented */
+ env->CP0_EntryLo0 = arg1 & 0x3FFFFFFF;
+}
+
+void helper_mtc0_tcstatus (target_ulong arg1)
+{
+ uint32_t mask = env->CP0_TCStatus_rw_bitmask;
+ uint32_t newval;
+
+ newval = (env->active_tc.CP0_TCStatus & ~mask) | (arg1 & mask);
+
+ // TODO: Sync with CP0_Status.
+
+ env->active_tc.CP0_TCStatus = newval;
+}
+
+void helper_mttc0_tcstatus (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ // TODO: Sync with CP0_Status.
+
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_TCStatus = arg1;
+ else
+ env->tcs[other_tc].CP0_TCStatus = arg1;
+}
+
+void helper_mtc0_tcbind (target_ulong arg1)
+{
+ uint32_t mask = (1 << CP0TCBd_TBE);
+ uint32_t newval;
+
+ if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC))
+ mask |= (1 << CP0TCBd_CurVPE);
+ newval = (env->active_tc.CP0_TCBind & ~mask) | (arg1 & mask);
+ env->active_tc.CP0_TCBind = newval;
+}
+
+void helper_mttc0_tcbind (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ uint32_t mask = (1 << CP0TCBd_TBE);
+ uint32_t newval;
+
+ if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC))
+ mask |= (1 << CP0TCBd_CurVPE);
+ if (other_tc == env->current_tc) {
+ newval = (env->active_tc.CP0_TCBind & ~mask) | (arg1 & mask);
+ env->active_tc.CP0_TCBind = newval;
+ } else {
+ newval = (env->tcs[other_tc].CP0_TCBind & ~mask) | (arg1 & mask);
+ env->tcs[other_tc].CP0_TCBind = newval;
+ }
+}
+
+void helper_mtc0_tcrestart (target_ulong arg1)
+{
+ env->active_tc.PC = arg1;
+ env->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS);
+ env->lladdr = 0ULL;
+ /* MIPS16 not implemented. */
+}
+
+void helper_mttc0_tcrestart (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc) {
+ env->active_tc.PC = arg1;
+ env->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS);
+ env->lladdr = 0ULL;
+ /* MIPS16 not implemented. */
+ } else {
+ env->tcs[other_tc].PC = arg1;
+ env->tcs[other_tc].CP0_TCStatus &= ~(1 << CP0TCSt_TDS);
+ env->lladdr = 0ULL;
+ /* MIPS16 not implemented. */
+ }
+}
+
+void helper_mtc0_tchalt (target_ulong arg1)
+{
+ env->active_tc.CP0_TCHalt = arg1 & 0x1;
+
+ // TODO: Halt TC / Restart (if allocated+active) TC.
+}
+
+void helper_mttc0_tchalt (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ // TODO: Halt TC / Restart (if allocated+active) TC.
+
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_TCHalt = arg1;
+ else
+ env->tcs[other_tc].CP0_TCHalt = arg1;
+}
+
+void helper_mtc0_tccontext (target_ulong arg1)
+{
+ env->active_tc.CP0_TCContext = arg1;
+}
+
+void helper_mttc0_tccontext (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_TCContext = arg1;
+ else
+ env->tcs[other_tc].CP0_TCContext = arg1;
+}
+
+void helper_mtc0_tcschedule (target_ulong arg1)
+{
+ env->active_tc.CP0_TCSchedule = arg1;
+}
+
+void helper_mttc0_tcschedule (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_TCSchedule = arg1;
+ else
+ env->tcs[other_tc].CP0_TCSchedule = arg1;
+}
+
+void helper_mtc0_tcschefback (target_ulong arg1)
+{
+ env->active_tc.CP0_TCScheFBack = arg1;
+}
+
+void helper_mttc0_tcschefback (target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_TCScheFBack = arg1;
+ else
+ env->tcs[other_tc].CP0_TCScheFBack = arg1;
+}
+
+void helper_mtc0_entrylo1 (target_ulong arg1)
+{
+ /* Large physaddr (PABITS) not implemented */
+ /* 1k pages not implemented */
+ env->CP0_EntryLo1 = arg1 & 0x3FFFFFFF;
+}
+
+void helper_mtc0_context (target_ulong arg1)
+{
+ env->CP0_Context = (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007FFFFF);
+}
+
+void helper_mtc0_pagemask (target_ulong arg1)
+{
+ /* 1k pages not implemented */
+ env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1));
+}
+
+void helper_mtc0_pagegrain (target_ulong arg1)
+{
+ /* SmartMIPS not implemented */
+ /* Large physaddr (PABITS) not implemented */
+ /* 1k pages not implemented */
+ env->CP0_PageGrain = 0;
+}
+
+void helper_mtc0_wired (target_ulong arg1)
+{
+ env->CP0_Wired = arg1 % env->tlb->nb_tlb;
+}
+
+void helper_mtc0_srsconf0 (target_ulong arg1)
+{
+ env->CP0_SRSConf0 |= arg1 & env->CP0_SRSConf0_rw_bitmask;
+}
+
+void helper_mtc0_srsconf1 (target_ulong arg1)
+{
+ env->CP0_SRSConf1 |= arg1 & env->CP0_SRSConf1_rw_bitmask;
+}
+
+void helper_mtc0_srsconf2 (target_ulong arg1)
+{
+ env->CP0_SRSConf2 |= arg1 & env->CP0_SRSConf2_rw_bitmask;
+}
+
+void helper_mtc0_srsconf3 (target_ulong arg1)
+{
+ env->CP0_SRSConf3 |= arg1 & env->CP0_SRSConf3_rw_bitmask;
+}
+
+void helper_mtc0_srsconf4 (target_ulong arg1)
+{
+ env->CP0_SRSConf4 |= arg1 & env->CP0_SRSConf4_rw_bitmask;
+}
+
+void helper_mtc0_hwrena (target_ulong arg1)
+{
+ env->CP0_HWREna = arg1 & 0x0000000F;
+}
+
+void helper_mtc0_count (target_ulong arg1)
+{
+ cpu_mips_store_count(env, arg1);
+}
+
+void helper_mtc0_entryhi (target_ulong arg1)
+{
+ target_ulong old, val;
+
+ /* 1k pages not implemented */
+ val = arg1 & ((TARGET_PAGE_MASK << 1) | 0xFF);
+#if defined(TARGET_MIPS64)
+ val &= env->SEGMask;
+#endif
+ old = env->CP0_EntryHi;
+ env->CP0_EntryHi = val;
+ if (env->CP0_Config3 & (1 << CP0C3_MT)) {
+ uint32_t tcst = env->active_tc.CP0_TCStatus & ~0xff;
+ env->active_tc.CP0_TCStatus = tcst | (val & 0xff);
+ }
+ /* If the ASID changes, flush qemu's TLB. */
+ if ((old & 0xFF) != (val & 0xFF))
+ cpu_mips_tlb_flush(env, 1);
+}
+
+void helper_mttc0_entryhi(target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ int32_t tcstatus;
+
+ env->CP0_EntryHi = (env->CP0_EntryHi & 0xff) | (arg1 & ~0xff);
+ if (other_tc == env->current_tc) {
+ tcstatus = (env->active_tc.CP0_TCStatus & ~0xff) | (arg1 & 0xff);
+ env->active_tc.CP0_TCStatus = tcstatus;
+ } else {
+ tcstatus = (env->tcs[other_tc].CP0_TCStatus & ~0xff) | (arg1 & 0xff);
+ env->tcs[other_tc].CP0_TCStatus = tcstatus;
+ }
+}
+
+void helper_mtc0_compare (target_ulong arg1)
+{
+ cpu_mips_store_compare(env, arg1);
+}
+
+void helper_mtc0_status (target_ulong arg1)
+{
+ uint32_t val, old;
+ uint32_t mask = env->CP0_Status_rw_bitmask;
+
+ val = arg1 & mask;
+ old = env->CP0_Status;
+ env->CP0_Status = (env->CP0_Status & ~mask) | val;
+ compute_hflags(env);
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
+ qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x",
+ old, old & env->CP0_Cause & CP0Ca_IP_mask,
+ val, val & env->CP0_Cause & CP0Ca_IP_mask,
+ env->CP0_Cause);
+ switch (env->hflags & MIPS_HFLAG_KSU) {
+ case MIPS_HFLAG_UM: qemu_log(", UM\n"); break;
+ case MIPS_HFLAG_SM: qemu_log(", SM\n"); break;
+ case MIPS_HFLAG_KM: qemu_log("\n"); break;
+ default: cpu_abort(env, "Invalid MMU mode!\n"); break;
+ }
+ }
+}
+
+void helper_mttc0_status(target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ int32_t tcstatus = env->tcs[other_tc].CP0_TCStatus;
+
+ env->CP0_Status = arg1 & ~0xf1000018;
+ tcstatus = (tcstatus & ~(0xf << CP0TCSt_TCU0)) | (arg1 & (0xf << CP0St_CU0));
+ tcstatus = (tcstatus & ~(1 << CP0TCSt_TMX)) | ((arg1 & (1 << CP0St_MX)) << (CP0TCSt_TMX - CP0St_MX));
+ tcstatus = (tcstatus & ~(0x3 << CP0TCSt_TKSU)) | ((arg1 & (0x3 << CP0St_KSU)) << (CP0TCSt_TKSU - CP0St_KSU));
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_TCStatus = tcstatus;
+ else
+ env->tcs[other_tc].CP0_TCStatus = tcstatus;
+}
+
+void helper_mtc0_intctl (target_ulong arg1)
+{
+ /* vectored interrupts not implemented, no performance counters. */
+ env->CP0_IntCtl = (env->CP0_IntCtl & ~0x000002e0) | (arg1 & 0x000002e0);
+}
+
+void helper_mtc0_srsctl (target_ulong arg1)
+{
+ uint32_t mask = (0xf << CP0SRSCtl_ESS) | (0xf << CP0SRSCtl_PSS);
+ env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask);
+}
+
+void helper_mtc0_cause (target_ulong arg1)
+{
+ uint32_t mask = 0x00C00300;
+ uint32_t old = env->CP0_Cause;
+ int i;
+
+ if (env->insn_flags & ISA_MIPS32R2)
+ mask |= 1 << CP0Ca_DC;
+
+ env->CP0_Cause = (env->CP0_Cause & ~mask) | (arg1 & mask);
+
+ if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) {
+ if (env->CP0_Cause & (1 << CP0Ca_DC))
+ cpu_mips_stop_count(env);
+ else
+ cpu_mips_start_count(env);
+ }
+
+ /* Set/reset software interrupts */
+ for (i = 0 ; i < 2 ; i++) {
+ if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) {
+ cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i)));
+ }
+ }
+}
+
+void helper_mtc0_ebase (target_ulong arg1)
+{
+ /* vectored interrupts not implemented */
+ env->CP0_EBase = (env->CP0_EBase & ~0x3FFFF000) | (arg1 & 0x3FFFF000);
+}
+
+void helper_mtc0_config0 (target_ulong arg1)
+{
+ env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (arg1 & 0x00000007);
+}
+
+void helper_mtc0_config2 (target_ulong arg1)
+{
+ /* tertiary/secondary caches not implemented */
+ env->CP0_Config2 = (env->CP0_Config2 & 0x8FFF0FFF);
+}
+
+void helper_mtc0_lladdr (target_ulong arg1)
+{
+ target_long mask = env->CP0_LLAddr_rw_bitmask;
+ arg1 = arg1 << env->CP0_LLAddr_shift;
+ env->lladdr = (env->lladdr & ~mask) | (arg1 & mask);
+}
+
+void helper_mtc0_watchlo (target_ulong arg1, uint32_t sel)
+{
+ /* Watch exceptions for instructions, data loads, data stores
+ not implemented. */
+ env->CP0_WatchLo[sel] = (arg1 & ~0x7);
+}
+
+void helper_mtc0_watchhi (target_ulong arg1, uint32_t sel)
+{
+ env->CP0_WatchHi[sel] = (arg1 & 0x40FF0FF8);
+ env->CP0_WatchHi[sel] &= ~(env->CP0_WatchHi[sel] & arg1 & 0x7);
+}
+
+void helper_mtc0_xcontext (target_ulong arg1)
+{
+ target_ulong mask = (1ULL << (env->SEGBITS - 7)) - 1;
+ env->CP0_XContext = (env->CP0_XContext & mask) | (arg1 & ~mask);
+}
+
+void helper_mtc0_framemask (target_ulong arg1)
+{
+ env->CP0_Framemask = arg1; /* XXX */
+}
+
+void helper_mtc0_debug (target_ulong arg1)
+{
+ env->CP0_Debug = (env->CP0_Debug & 0x8C03FC1F) | (arg1 & 0x13300120);
+ if (arg1 & (1 << CP0DB_DM))
+ env->hflags |= MIPS_HFLAG_DM;
+ else
+ env->hflags &= ~MIPS_HFLAG_DM;
+}
+
+void helper_mttc0_debug(target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ uint32_t val = arg1 & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt));
+
+ /* XXX: Might be wrong, check with EJTAG spec. */
+ if (other_tc == env->current_tc)
+ env->active_tc.CP0_Debug_tcstatus = val;
+ else
+ env->tcs[other_tc].CP0_Debug_tcstatus = val;
+ env->CP0_Debug = (env->CP0_Debug & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) |
+ (arg1 & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt)));
+}
+
+void helper_mtc0_performance0 (target_ulong arg1)
+{
+ env->CP0_Performance0 = arg1 & 0x000007ff;
+}
+
+void helper_mtc0_taglo (target_ulong arg1)
+{
+ env->CP0_TagLo = arg1 & 0xFFFFFCF6;
+}
+
+void helper_mtc0_datalo (target_ulong arg1)
+{
+ env->CP0_DataLo = arg1; /* XXX */
+}
+
+void helper_mtc0_taghi (target_ulong arg1)
+{
+ env->CP0_TagHi = arg1; /* XXX */
+}
+
+void helper_mtc0_datahi (target_ulong arg1)
+{
+ env->CP0_DataHi = arg1; /* XXX */
+}
+
+/* MIPS MT functions */
+target_ulong helper_mftgpr(uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.gpr[sel];
+ else
+ return env->tcs[other_tc].gpr[sel];
+}
+
+target_ulong helper_mftlo(uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.LO[sel];
+ else
+ return env->tcs[other_tc].LO[sel];
+}
+
+target_ulong helper_mfthi(uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.HI[sel];
+ else
+ return env->tcs[other_tc].HI[sel];
+}
+
+target_ulong helper_mftacx(uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.ACX[sel];
+ else
+ return env->tcs[other_tc].ACX[sel];
+}
+
+target_ulong helper_mftdsp(void)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ return env->active_tc.DSPControl;
+ else
+ return env->tcs[other_tc].DSPControl;
+}
+
+void helper_mttgpr(target_ulong arg1, uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.gpr[sel] = arg1;
+ else
+ env->tcs[other_tc].gpr[sel] = arg1;
+}
+
+void helper_mttlo(target_ulong arg1, uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.LO[sel] = arg1;
+ else
+ env->tcs[other_tc].LO[sel] = arg1;
+}
+
+void helper_mtthi(target_ulong arg1, uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.HI[sel] = arg1;
+ else
+ env->tcs[other_tc].HI[sel] = arg1;
+}
+
+void helper_mttacx(target_ulong arg1, uint32_t sel)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.ACX[sel] = arg1;
+ else
+ env->tcs[other_tc].ACX[sel] = arg1;
+}
+
+void helper_mttdsp(target_ulong arg1)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+
+ if (other_tc == env->current_tc)
+ env->active_tc.DSPControl = arg1;
+ else
+ env->tcs[other_tc].DSPControl = arg1;
+}
+
+/* MIPS MT functions */
+target_ulong helper_dmt(void)
+{
+ // TODO
+ return 0;
+}
+
+target_ulong helper_emt(void)
+{
+ // TODO
+ return 0;
+}
+
+target_ulong helper_dvpe(void)
+{
+ // TODO
+ return 0;
+}
+
+target_ulong helper_evpe(void)
+{
+ // TODO
+ return 0;
+}
+#endif /* !CONFIG_USER_ONLY */
+
+void helper_fork(target_ulong arg1, target_ulong arg2)
+{
+ // arg1 = rt, arg2 = rs
+ arg1 = 0;
+ // TODO: store to TC register
+}
+
+target_ulong helper_yield(target_ulong arg)
+{
+ target_long arg1 = arg;
+
+ if (arg1 < 0) {
+ /* No scheduling policy implemented. */
+ if (arg1 != -2) {
+ if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) &&
+ env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) {
+ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
+ env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT;
+ helper_raise_exception(EXCP_THREAD);
+ }
+ }
+ } else if (arg1 == 0) {
+ if (0 /* TODO: TC underflow */) {
+ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
+ helper_raise_exception(EXCP_THREAD);
+ } else {
+ // TODO: Deallocate TC
+ }
+ } else if (arg1 > 0) {
+ /* Yield qualifier inputs not implemented. */
+ env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT);
+ env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT;
+ helper_raise_exception(EXCP_THREAD);
+ }
+ return env->CP0_YQMask;
+}
+
+#ifndef CONFIG_USER_ONLY
+/* TLB management */
+static void cpu_mips_tlb_flush (CPUState *env, int flush_global)
+{
+ /* Flush qemu's TLB and discard all shadowed entries. */
+ tlb_flush (env, flush_global);
+ env->tlb->tlb_in_use = env->tlb->nb_tlb;
+}
+
+static void r4k_mips_tlb_flush_extra (CPUState *env, int first)
+{
+ /* Discard entries from env->tlb[first] onwards. */
+ while (env->tlb->tlb_in_use > first) {
+ r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0);
+ }
+}
+
+static void r4k_fill_tlb (int idx)
+{
+ r4k_tlb_t *tlb;
+
+ /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+ tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
+#if defined(TARGET_MIPS64)
+ tlb->VPN &= env->SEGMask;
+#endif
+ tlb->ASID = env->CP0_EntryHi & 0xFF;
+ tlb->PageMask = env->CP0_PageMask;
+ tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
+ tlb->V0 = (env->CP0_EntryLo0 & 2) != 0;
+ tlb->D0 = (env->CP0_EntryLo0 & 4) != 0;
+ tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7;
+ tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
+ tlb->V1 = (env->CP0_EntryLo1 & 2) != 0;
+ tlb->D1 = (env->CP0_EntryLo1 & 4) != 0;
+ tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7;
+ tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
+}
+
+void r4k_helper_tlbwi (void)
+{
+ int idx;
+
+ idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb;
+
+ /* Discard cached TLB entries. We could avoid doing this if the
+ tlbwi is just upgrading access permissions on the current entry;
+ that might be a further win. */
+ r4k_mips_tlb_flush_extra (env, env->tlb->nb_tlb);
+
+ r4k_invalidate_tlb(env, idx, 0);
+ r4k_fill_tlb(idx);
+}
+
+void r4k_helper_tlbwr (void)
+{
+ int r = cpu_mips_get_random(env);
+
+ r4k_invalidate_tlb(env, r, 1);
+ r4k_fill_tlb(r);
+}
+
+void r4k_helper_tlbp (void)
+{
+ r4k_tlb_t *tlb;
+ target_ulong mask;
+ target_ulong tag;
+ target_ulong VPN;
+ uint8_t ASID;
+ int i;
+
+ ASID = env->CP0_EntryHi & 0xFF;
+ for (i = 0; i < env->tlb->nb_tlb; i++) {
+ tlb = &env->tlb->mmu.r4k.tlb[i];
+ /* 1k pages are not supported. */
+ mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
+ tag = env->CP0_EntryHi & ~mask;
+ VPN = tlb->VPN & ~mask;
+ /* Check ASID, virtual page number & size */
+ if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
+ /* TLB match */
+ env->CP0_Index = i;
+ break;
+ }
+ }
+ if (i == env->tlb->nb_tlb) {
+ /* No match. Discard any shadow entries, if any of them match. */
+ for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) {
+ tlb = &env->tlb->mmu.r4k.tlb[i];
+ /* 1k pages are not supported. */
+ mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1);
+ tag = env->CP0_EntryHi & ~mask;
+ VPN = tlb->VPN & ~mask;
+ /* Check ASID, virtual page number & size */
+ if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
+ r4k_mips_tlb_flush_extra (env, i);
+ break;
+ }
+ }
+
+ env->CP0_Index |= 0x80000000;
+ }
+}
+
+void r4k_helper_tlbr (void)
+{
+ r4k_tlb_t *tlb;
+ uint8_t ASID;
+ int idx;
+
+ ASID = env->CP0_EntryHi & 0xFF;
+ idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb;
+ tlb = &env->tlb->mmu.r4k.tlb[idx];
+
+ /* If this will change the current ASID, flush qemu's TLB. */
+ if (ASID != tlb->ASID)
+ cpu_mips_tlb_flush (env, 1);
+
+ r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
+
+ env->CP0_EntryHi = tlb->VPN | tlb->ASID;
+ env->CP0_PageMask = tlb->PageMask;
+ env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
+ (tlb->C0 << 3) | (tlb->PFN[0] >> 6);
+ env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
+ (tlb->C1 << 3) | (tlb->PFN[1] >> 6);
+}
+
+void helper_tlbwi(void)
+{
+ env->tlb->helper_tlbwi();
+}
+
+void helper_tlbwr(void)
+{
+ env->tlb->helper_tlbwr();
+}
+
+void helper_tlbp(void)
+{
+ env->tlb->helper_tlbp();
+}
+
+void helper_tlbr(void)
+{
+ env->tlb->helper_tlbr();
+}
+
+/* Specials */
+target_ulong helper_di (void)
+{
+ target_ulong t0 = env->CP0_Status;
+
+ env->CP0_Status = t0 & ~(1 << CP0St_IE);
+ return t0;
+}
+
+target_ulong helper_ei (void)
+{
+ target_ulong t0 = env->CP0_Status;
+
+ env->CP0_Status = t0 | (1 << CP0St_IE);
+ return t0;
+}
+
+static void debug_pre_eret (void)
+{
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
+ qemu_log("ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
+ env->active_tc.PC, env->CP0_EPC);
+ if (env->CP0_Status & (1 << CP0St_ERL))
+ qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
+ if (env->hflags & MIPS_HFLAG_DM)
+ qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC);
+ qemu_log("\n");
+ }
+}
+
+static void debug_post_eret (void)
+{
+ if (qemu_loglevel_mask(CPU_LOG_EXEC)) {
+ qemu_log(" => PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx,
+ env->active_tc.PC, env->CP0_EPC);
+ if (env->CP0_Status & (1 << CP0St_ERL))
+ qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC);
+ if (env->hflags & MIPS_HFLAG_DM)
+ qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC);
+ switch (env->hflags & MIPS_HFLAG_KSU) {
+ case MIPS_HFLAG_UM: qemu_log(", UM\n"); break;
+ case MIPS_HFLAG_SM: qemu_log(", SM\n"); break;
+ case MIPS_HFLAG_KM: qemu_log("\n"); break;
+ default: cpu_abort(env, "Invalid MMU mode!\n"); break;
+ }
+ }
+}
+
+static void set_pc (target_ulong error_pc)
+{
+ env->active_tc.PC = error_pc & ~(target_ulong)1;
+ if (error_pc & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
+ } else {
+ env->hflags &= ~(MIPS_HFLAG_M16);
+ }
+}
+
+void helper_eret (void)
+{
+ debug_pre_eret();
+ if (env->CP0_Status & (1 << CP0St_ERL)) {
+ set_pc(env->CP0_ErrorEPC);
+ env->CP0_Status &= ~(1 << CP0St_ERL);
+ } else {
+ set_pc(env->CP0_EPC);
+ env->CP0_Status &= ~(1 << CP0St_EXL);
+ }
+ compute_hflags(env);
+ debug_post_eret();
+ env->lladdr = 1;
+}
+
+void helper_deret (void)
+{
+ debug_pre_eret();
+ set_pc(env->CP0_DEPC);
+
+ env->hflags &= MIPS_HFLAG_DM;
+ compute_hflags(env);
+ debug_post_eret();
+ env->lladdr = 1;
+}
+#endif /* !CONFIG_USER_ONLY */
+
+target_ulong helper_rdhwr_cpunum(void)
+{
+ if ((env->hflags & MIPS_HFLAG_CP0) ||
+ (env->CP0_HWREna & (1 << 0)))
+ return env->CP0_EBase & 0x3ff;
+ else
+ helper_raise_exception(EXCP_RI);
+
+ return 0;
+}
+
+target_ulong helper_rdhwr_synci_step(void)
+{
+ if ((env->hflags & MIPS_HFLAG_CP0) ||
+ (env->CP0_HWREna & (1 << 1)))
+ return env->SYNCI_Step;
+ else
+ helper_raise_exception(EXCP_RI);
+
+ return 0;
+}
+
+target_ulong helper_rdhwr_cc(void)
+{
+ if ((env->hflags & MIPS_HFLAG_CP0) ||
+ (env->CP0_HWREna & (1 << 2)))
+ return env->CP0_Count;
+ else
+ helper_raise_exception(EXCP_RI);
+
+ return 0;
+}
+
+target_ulong helper_rdhwr_ccres(void)
+{
+ if ((env->hflags & MIPS_HFLAG_CP0) ||
+ (env->CP0_HWREna & (1 << 3)))
+ return env->CCRes;
+ else
+ helper_raise_exception(EXCP_RI);
+
+ return 0;
+}
+
+void helper_pmon (int function)
+{
+ function /= 2;
+ switch (function) {
+ case 2: /* TODO: char inbyte(int waitflag); */
+ if (env->active_tc.gpr[4] == 0)
+ env->active_tc.gpr[2] = -1;
+ /* Fall through */
+ case 11: /* TODO: char inbyte (void); */
+ env->active_tc.gpr[2] = -1;
+ break;
+ case 3:
+ case 12:
+ printf("%c", (char)(env->active_tc.gpr[4] & 0xFF));
+ break;
+ case 17:
+ break;
+ case 158:
+ {
+ unsigned char *fmt = (void *)(unsigned long)env->active_tc.gpr[4];
+ printf("%s", fmt);
+ }
+ break;
+ }
+}
+
+void helper_wait (void)
+{
+ env->halted = 1;
+ helper_raise_exception(EXCP_HLT);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
+
+#define MMUSUFFIX _mmu
+#define ALIGNED_ONLY
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ env->CP0_BadVAddr = addr;
+ do_restore_state (retaddr);
+ helper_raise_exception ((is_write == 1) ? EXCP_AdES : EXCP_AdEL);
+}
+
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_mips_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (ret) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, NULL);
+ }
+ }
+ helper_raise_exception_err(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
+
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int unused, int size)
+{
+ if (is_exec)
+ helper_raise_exception(EXCP_IBE);
+ else
+ helper_raise_exception(EXCP_DBE);
+}
+#endif /* !CONFIG_USER_ONLY */
+
+/* Complex FPU operations which may need stack space. */
+
+#define FLOAT_ONE32 make_float32(0x3f8 << 20)
+#define FLOAT_ONE64 make_float64(0x3ffULL << 52)
+#define FLOAT_TWO32 make_float32(1 << 30)
+#define FLOAT_TWO64 make_float64(1ULL << 62)
+#define FLOAT_QNAN32 0x7fbfffff
+#define FLOAT_QNAN64 0x7ff7ffffffffffffULL
+#define FLOAT_SNAN32 0x7fffffff
+#define FLOAT_SNAN64 0x7fffffffffffffffULL
+
+/* convert MIPS rounding mode in FCR31 to IEEE library */
+static unsigned int ieee_rm[] = {
+ float_round_nearest_even,
+ float_round_to_zero,
+ float_round_up,
+ float_round_down
+};
+
+#define RESTORE_ROUNDING_MODE \
+ set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], &env->active_fpu.fp_status)
+
+#define RESTORE_FLUSH_MODE \
+ set_flush_to_zero((env->active_fpu.fcr31 & (1 << 24)) != 0, &env->active_fpu.fp_status);
+
+target_ulong helper_cfc1 (uint32_t reg)
+{
+ target_ulong arg1;
+
+ switch (reg) {
+ case 0:
+ arg1 = (int32_t)env->active_fpu.fcr0;
+ break;
+ case 25:
+ arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1);
+ break;
+ case 26:
+ arg1 = env->active_fpu.fcr31 & 0x0003f07c;
+ break;
+ case 28:
+ arg1 = (env->active_fpu.fcr31 & 0x00000f83) | ((env->active_fpu.fcr31 >> 22) & 0x4);
+ break;
+ default:
+ arg1 = (int32_t)env->active_fpu.fcr31;
+ break;
+ }
+
+ return arg1;
+}
+
+void helper_ctc1 (target_ulong arg1, uint32_t reg)
+{
+ switch(reg) {
+ case 25:
+ if (arg1 & 0xffffff00)
+ return;
+ env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | ((arg1 & 0xfe) << 24) |
+ ((arg1 & 0x1) << 23);
+ break;
+ case 26:
+ if (arg1 & 0x007c0000)
+ return;
+ env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfffc0f83) | (arg1 & 0x0003f07c);
+ break;
+ case 28:
+ if (arg1 & 0x007c0000)
+ return;
+ env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfefff07c) | (arg1 & 0x00000f83) |
+ ((arg1 & 0x4) << 22);
+ break;
+ case 31:
+ if (arg1 & 0x007c0000)
+ return;
+ env->active_fpu.fcr31 = arg1;
+ break;
+ default:
+ return;
+ }
+ /* set rounding mode */
+ RESTORE_ROUNDING_MODE;
+ /* set flush-to-zero mode */
+ RESTORE_FLUSH_MODE;
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ if ((GET_FP_ENABLE(env->active_fpu.fcr31) | 0x20) & GET_FP_CAUSE(env->active_fpu.fcr31))
+ helper_raise_exception(EXCP_FPE);
+}
+
+static inline char ieee_ex_to_mips(char xcpt)
+{
+ return (xcpt & float_flag_inexact) >> 5 |
+ (xcpt & float_flag_underflow) >> 3 |
+ (xcpt & float_flag_overflow) >> 1 |
+ (xcpt & float_flag_divbyzero) << 1 |
+ (xcpt & float_flag_invalid) << 4;
+}
+
+static inline char mips_ex_to_ieee(char xcpt)
+{
+ return (xcpt & FP_INEXACT) << 5 |
+ (xcpt & FP_UNDERFLOW) << 3 |
+ (xcpt & FP_OVERFLOW) << 1 |
+ (xcpt & FP_DIV0) >> 1 |
+ (xcpt & FP_INVALID) >> 4;
+}
+
+static inline void update_fcr31(void)
+{
+ int tmp = ieee_ex_to_mips(get_float_exception_flags(&env->active_fpu.fp_status));
+
+ SET_FP_CAUSE(env->active_fpu.fcr31, tmp);
+ if (GET_FP_ENABLE(env->active_fpu.fcr31) & tmp)
+ helper_raise_exception(EXCP_FPE);
+ else
+ UPDATE_FP_FLAGS(env->active_fpu.fcr31, tmp);
+}
+
+/* Float support.
+ Single precition routines have a "s" suffix, double precision a
+ "d" suffix, 32bit integer "w", 64bit integer "l", paired single "ps",
+ paired single lower "pl", paired single upper "pu". */
+
+/* unary operations, modifying fp status */
+uint64_t helper_float_sqrt_d(uint64_t fdt0)
+{
+ return float64_sqrt(fdt0, &env->active_fpu.fp_status);
+}
+
+uint32_t helper_float_sqrt_s(uint32_t fst0)
+{
+ return float32_sqrt(fst0, &env->active_fpu.fp_status);
+}
+
+uint64_t helper_float_cvtd_s(uint32_t fst0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint64_t helper_float_cvtd_w(uint32_t wt0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = int32_to_float64(wt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint64_t helper_float_cvtd_l(uint64_t dt0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = int64_to_float64(dt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint64_t helper_float_cvtl_d(uint64_t fdt0)
+{
+ uint64_t dt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint64_t helper_float_cvtl_s(uint32_t fst0)
+{
+ uint64_t dt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint64_t helper_float_cvtps_pw(uint64_t dt0)
+{
+ uint32_t fst2;
+ uint32_t fsth2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = int32_to_float32(dt0 & 0XFFFFFFFF, &env->active_fpu.fp_status);
+ fsth2 = int32_to_float32(dt0 >> 32, &env->active_fpu.fp_status);
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+uint64_t helper_float_cvtpw_ps(uint64_t fdt0)
+{
+ uint32_t wt2;
+ uint32_t wth2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ wt2 = float32_to_int32(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status);
+ wth2 = float32_to_int32(fdt0 >> 32, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID)) {
+ wt2 = FLOAT_SNAN32;
+ wth2 = FLOAT_SNAN32;
+ }
+ return ((uint64_t)wth2 << 32) | wt2;
+}
+
+uint32_t helper_float_cvts_d(uint64_t fdt0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint32_t helper_float_cvts_w(uint32_t wt0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = int32_to_float32(wt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint32_t helper_float_cvts_l(uint64_t dt0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = int64_to_float32(dt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint32_t helper_float_cvts_pl(uint32_t wt0)
+{
+ uint32_t wt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ wt2 = wt0;
+ update_fcr31();
+ return wt2;
+}
+
+uint32_t helper_float_cvts_pu(uint32_t wth0)
+{
+ uint32_t wt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ wt2 = wth0;
+ update_fcr31();
+ return wt2;
+}
+
+uint32_t helper_float_cvtw_s(uint32_t fst0)
+{
+ uint32_t wt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint32_t helper_float_cvtw_d(uint64_t fdt0)
+{
+ uint32_t wt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint64_t helper_float_roundl_d(uint64_t fdt0)
+{
+ uint64_t dt2;
+
+ set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
+ dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint64_t helper_float_roundl_s(uint32_t fst0)
+{
+ uint64_t dt2;
+
+ set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
+ dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint32_t helper_float_roundw_d(uint64_t fdt0)
+{
+ uint32_t wt2;
+
+ set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
+ wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint32_t helper_float_roundw_s(uint32_t fst0)
+{
+ uint32_t wt2;
+
+ set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
+ wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint64_t helper_float_truncl_d(uint64_t fdt0)
+{
+ uint64_t dt2;
+
+ dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint64_t helper_float_truncl_s(uint32_t fst0)
+{
+ uint64_t dt2;
+
+ dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint32_t helper_float_truncw_d(uint64_t fdt0)
+{
+ uint32_t wt2;
+
+ wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint32_t helper_float_truncw_s(uint32_t fst0)
+{
+ uint32_t wt2;
+
+ wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint64_t helper_float_ceill_d(uint64_t fdt0)
+{
+ uint64_t dt2;
+
+ set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
+ dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint64_t helper_float_ceill_s(uint32_t fst0)
+{
+ uint64_t dt2;
+
+ set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
+ dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint32_t helper_float_ceilw_d(uint64_t fdt0)
+{
+ uint32_t wt2;
+
+ set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
+ wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint32_t helper_float_ceilw_s(uint32_t fst0)
+{
+ uint32_t wt2;
+
+ set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
+ wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint64_t helper_float_floorl_d(uint64_t fdt0)
+{
+ uint64_t dt2;
+
+ set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
+ dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint64_t helper_float_floorl_s(uint32_t fst0)
+{
+ uint64_t dt2;
+
+ set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
+ dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ dt2 = FLOAT_SNAN64;
+ return dt2;
+}
+
+uint32_t helper_float_floorw_d(uint64_t fdt0)
+{
+ uint32_t wt2;
+
+ set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
+ wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+uint32_t helper_float_floorw_s(uint32_t fst0)
+{
+ uint32_t wt2;
+
+ set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
+ wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
+ RESTORE_ROUNDING_MODE;
+ update_fcr31();
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & (FP_OVERFLOW | FP_INVALID))
+ wt2 = FLOAT_SNAN32;
+ return wt2;
+}
+
+/* unary operations, not modifying fp status */
+#define FLOAT_UNOP(name) \
+uint64_t helper_float_ ## name ## _d(uint64_t fdt0) \
+{ \
+ return float64_ ## name(fdt0); \
+} \
+uint32_t helper_float_ ## name ## _s(uint32_t fst0) \
+{ \
+ return float32_ ## name(fst0); \
+} \
+uint64_t helper_float_ ## name ## _ps(uint64_t fdt0) \
+{ \
+ uint32_t wt0; \
+ uint32_t wth0; \
+ \
+ wt0 = float32_ ## name(fdt0 & 0XFFFFFFFF); \
+ wth0 = float32_ ## name(fdt0 >> 32); \
+ return ((uint64_t)wth0 << 32) | wt0; \
+}
+FLOAT_UNOP(abs)
+FLOAT_UNOP(chs)
+#undef FLOAT_UNOP
+
+/* MIPS specific unary operations */
+uint64_t helper_float_recip_d(uint64_t fdt0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float64_div(FLOAT_ONE64, fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint32_t helper_float_recip_s(uint32_t fst0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_div(FLOAT_ONE32, fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint64_t helper_float_rsqrt_d(uint64_t fdt0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status);
+ fdt2 = float64_div(FLOAT_ONE64, fdt2, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint32_t helper_float_rsqrt_s(uint32_t fst0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status);
+ fst2 = float32_div(FLOAT_ONE32, fst2, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint64_t helper_float_recip1_d(uint64_t fdt0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float64_div(FLOAT_ONE64, fdt0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint32_t helper_float_recip1_s(uint32_t fst0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_div(FLOAT_ONE32, fst0, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint64_t helper_float_recip1_ps(uint64_t fdt0)
+{
+ uint32_t fst2;
+ uint32_t fsth2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_div(FLOAT_ONE32, fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status);
+ fsth2 = float32_div(FLOAT_ONE32, fdt0 >> 32, &env->active_fpu.fp_status);
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+uint64_t helper_float_rsqrt1_d(uint64_t fdt0)
+{
+ uint64_t fdt2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status);
+ fdt2 = float64_div(FLOAT_ONE64, fdt2, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fdt2;
+}
+
+uint32_t helper_float_rsqrt1_s(uint32_t fst0)
+{
+ uint32_t fst2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status);
+ fst2 = float32_div(FLOAT_ONE32, fst2, &env->active_fpu.fp_status);
+ update_fcr31();
+ return fst2;
+}
+
+uint64_t helper_float_rsqrt1_ps(uint64_t fdt0)
+{
+ uint32_t fst2;
+ uint32_t fsth2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_sqrt(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status);
+ fsth2 = float32_sqrt(fdt0 >> 32, &env->active_fpu.fp_status);
+ fst2 = float32_div(FLOAT_ONE32, fst2, &env->active_fpu.fp_status);
+ fsth2 = float32_div(FLOAT_ONE32, fsth2, &env->active_fpu.fp_status);
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+#define FLOAT_OP(name, p) void helper_float_##name##_##p(void)
+
+/* binary operations */
+#define FLOAT_BINOP(name) \
+uint64_t helper_float_ ## name ## _d(uint64_t fdt0, uint64_t fdt1) \
+{ \
+ uint64_t dt2; \
+ \
+ set_float_exception_flags(0, &env->active_fpu.fp_status); \
+ dt2 = float64_ ## name (fdt0, fdt1, &env->active_fpu.fp_status); \
+ update_fcr31(); \
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) \
+ dt2 = FLOAT_QNAN64; \
+ return dt2; \
+} \
+ \
+uint32_t helper_float_ ## name ## _s(uint32_t fst0, uint32_t fst1) \
+{ \
+ uint32_t wt2; \
+ \
+ set_float_exception_flags(0, &env->active_fpu.fp_status); \
+ wt2 = float32_ ## name (fst0, fst1, &env->active_fpu.fp_status); \
+ update_fcr31(); \
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) \
+ wt2 = FLOAT_QNAN32; \
+ return wt2; \
+} \
+ \
+uint64_t helper_float_ ## name ## _ps(uint64_t fdt0, uint64_t fdt1) \
+{ \
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF; \
+ uint32_t fsth0 = fdt0 >> 32; \
+ uint32_t fst1 = fdt1 & 0XFFFFFFFF; \
+ uint32_t fsth1 = fdt1 >> 32; \
+ uint32_t wt2; \
+ uint32_t wth2; \
+ \
+ set_float_exception_flags(0, &env->active_fpu.fp_status); \
+ wt2 = float32_ ## name (fst0, fst1, &env->active_fpu.fp_status); \
+ wth2 = float32_ ## name (fsth0, fsth1, &env->active_fpu.fp_status); \
+ update_fcr31(); \
+ if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) { \
+ wt2 = FLOAT_QNAN32; \
+ wth2 = FLOAT_QNAN32; \
+ } \
+ return ((uint64_t)wth2 << 32) | wt2; \
+}
+
+FLOAT_BINOP(add)
+FLOAT_BINOP(sub)
+FLOAT_BINOP(mul)
+FLOAT_BINOP(div)
+#undef FLOAT_BINOP
+
+/* ternary operations */
+#define FLOAT_TERNOP(name1, name2) \
+uint64_t helper_float_ ## name1 ## name2 ## _d(uint64_t fdt0, uint64_t fdt1, \
+ uint64_t fdt2) \
+{ \
+ fdt0 = float64_ ## name1 (fdt0, fdt1, &env->active_fpu.fp_status); \
+ return float64_ ## name2 (fdt0, fdt2, &env->active_fpu.fp_status); \
+} \
+ \
+uint32_t helper_float_ ## name1 ## name2 ## _s(uint32_t fst0, uint32_t fst1, \
+ uint32_t fst2) \
+{ \
+ fst0 = float32_ ## name1 (fst0, fst1, &env->active_fpu.fp_status); \
+ return float32_ ## name2 (fst0, fst2, &env->active_fpu.fp_status); \
+} \
+ \
+uint64_t helper_float_ ## name1 ## name2 ## _ps(uint64_t fdt0, uint64_t fdt1, \
+ uint64_t fdt2) \
+{ \
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF; \
+ uint32_t fsth0 = fdt0 >> 32; \
+ uint32_t fst1 = fdt1 & 0XFFFFFFFF; \
+ uint32_t fsth1 = fdt1 >> 32; \
+ uint32_t fst2 = fdt2 & 0XFFFFFFFF; \
+ uint32_t fsth2 = fdt2 >> 32; \
+ \
+ fst0 = float32_ ## name1 (fst0, fst1, &env->active_fpu.fp_status); \
+ fsth0 = float32_ ## name1 (fsth0, fsth1, &env->active_fpu.fp_status); \
+ fst2 = float32_ ## name2 (fst0, fst2, &env->active_fpu.fp_status); \
+ fsth2 = float32_ ## name2 (fsth0, fsth2, &env->active_fpu.fp_status); \
+ return ((uint64_t)fsth2 << 32) | fst2; \
+}
+
+FLOAT_TERNOP(mul, add)
+FLOAT_TERNOP(mul, sub)
+#undef FLOAT_TERNOP
+
+/* negated ternary operations */
+#define FLOAT_NTERNOP(name1, name2) \
+uint64_t helper_float_n ## name1 ## name2 ## _d(uint64_t fdt0, uint64_t fdt1, \
+ uint64_t fdt2) \
+{ \
+ fdt0 = float64_ ## name1 (fdt0, fdt1, &env->active_fpu.fp_status); \
+ fdt2 = float64_ ## name2 (fdt0, fdt2, &env->active_fpu.fp_status); \
+ return float64_chs(fdt2); \
+} \
+ \
+uint32_t helper_float_n ## name1 ## name2 ## _s(uint32_t fst0, uint32_t fst1, \
+ uint32_t fst2) \
+{ \
+ fst0 = float32_ ## name1 (fst0, fst1, &env->active_fpu.fp_status); \
+ fst2 = float32_ ## name2 (fst0, fst2, &env->active_fpu.fp_status); \
+ return float32_chs(fst2); \
+} \
+ \
+uint64_t helper_float_n ## name1 ## name2 ## _ps(uint64_t fdt0, uint64_t fdt1,\
+ uint64_t fdt2) \
+{ \
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF; \
+ uint32_t fsth0 = fdt0 >> 32; \
+ uint32_t fst1 = fdt1 & 0XFFFFFFFF; \
+ uint32_t fsth1 = fdt1 >> 32; \
+ uint32_t fst2 = fdt2 & 0XFFFFFFFF; \
+ uint32_t fsth2 = fdt2 >> 32; \
+ \
+ fst0 = float32_ ## name1 (fst0, fst1, &env->active_fpu.fp_status); \
+ fsth0 = float32_ ## name1 (fsth0, fsth1, &env->active_fpu.fp_status); \
+ fst2 = float32_ ## name2 (fst0, fst2, &env->active_fpu.fp_status); \
+ fsth2 = float32_ ## name2 (fsth0, fsth2, &env->active_fpu.fp_status); \
+ fst2 = float32_chs(fst2); \
+ fsth2 = float32_chs(fsth2); \
+ return ((uint64_t)fsth2 << 32) | fst2; \
+}
+
+FLOAT_NTERNOP(mul, add)
+FLOAT_NTERNOP(mul, sub)
+#undef FLOAT_NTERNOP
+
+/* MIPS specific binary operations */
+uint64_t helper_float_recip2_d(uint64_t fdt0, uint64_t fdt2)
+{
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status);
+ fdt2 = float64_chs(float64_sub(fdt2, FLOAT_ONE64, &env->active_fpu.fp_status));
+ update_fcr31();
+ return fdt2;
+}
+
+uint32_t helper_float_recip2_s(uint32_t fst0, uint32_t fst2)
+{
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status);
+ fst2 = float32_chs(float32_sub(fst2, FLOAT_ONE32, &env->active_fpu.fp_status));
+ update_fcr31();
+ return fst2;
+}
+
+uint64_t helper_float_recip2_ps(uint64_t fdt0, uint64_t fdt2)
+{
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF;
+ uint32_t fsth0 = fdt0 >> 32;
+ uint32_t fst2 = fdt2 & 0XFFFFFFFF;
+ uint32_t fsth2 = fdt2 >> 32;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status);
+ fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status);
+ fst2 = float32_chs(float32_sub(fst2, FLOAT_ONE32, &env->active_fpu.fp_status));
+ fsth2 = float32_chs(float32_sub(fsth2, FLOAT_ONE32, &env->active_fpu.fp_status));
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+uint64_t helper_float_rsqrt2_d(uint64_t fdt0, uint64_t fdt2)
+{
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status);
+ fdt2 = float64_sub(fdt2, FLOAT_ONE64, &env->active_fpu.fp_status);
+ fdt2 = float64_chs(float64_div(fdt2, FLOAT_TWO64, &env->active_fpu.fp_status));
+ update_fcr31();
+ return fdt2;
+}
+
+uint32_t helper_float_rsqrt2_s(uint32_t fst0, uint32_t fst2)
+{
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status);
+ fst2 = float32_sub(fst2, FLOAT_ONE32, &env->active_fpu.fp_status);
+ fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, &env->active_fpu.fp_status));
+ update_fcr31();
+ return fst2;
+}
+
+uint64_t helper_float_rsqrt2_ps(uint64_t fdt0, uint64_t fdt2)
+{
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF;
+ uint32_t fsth0 = fdt0 >> 32;
+ uint32_t fst2 = fdt2 & 0XFFFFFFFF;
+ uint32_t fsth2 = fdt2 >> 32;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status);
+ fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status);
+ fst2 = float32_sub(fst2, FLOAT_ONE32, &env->active_fpu.fp_status);
+ fsth2 = float32_sub(fsth2, FLOAT_ONE32, &env->active_fpu.fp_status);
+ fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, &env->active_fpu.fp_status));
+ fsth2 = float32_chs(float32_div(fsth2, FLOAT_TWO32, &env->active_fpu.fp_status));
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+uint64_t helper_float_addr_ps(uint64_t fdt0, uint64_t fdt1)
+{
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF;
+ uint32_t fsth0 = fdt0 >> 32;
+ uint32_t fst1 = fdt1 & 0XFFFFFFFF;
+ uint32_t fsth1 = fdt1 >> 32;
+ uint32_t fst2;
+ uint32_t fsth2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_add (fst0, fsth0, &env->active_fpu.fp_status);
+ fsth2 = float32_add (fst1, fsth1, &env->active_fpu.fp_status);
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+uint64_t helper_float_mulr_ps(uint64_t fdt0, uint64_t fdt1)
+{
+ uint32_t fst0 = fdt0 & 0XFFFFFFFF;
+ uint32_t fsth0 = fdt0 >> 32;
+ uint32_t fst1 = fdt1 & 0XFFFFFFFF;
+ uint32_t fsth1 = fdt1 >> 32;
+ uint32_t fst2;
+ uint32_t fsth2;
+
+ set_float_exception_flags(0, &env->active_fpu.fp_status);
+ fst2 = float32_mul (fst0, fsth0, &env->active_fpu.fp_status);
+ fsth2 = float32_mul (fst1, fsth1, &env->active_fpu.fp_status);
+ update_fcr31();
+ return ((uint64_t)fsth2 << 32) | fst2;
+}
+
+/* compare operations */
+#define FOP_COND_D(op, cond) \
+void helper_cmp_d_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \
+{ \
+ int c = cond; \
+ update_fcr31(); \
+ if (c) \
+ SET_FP_COND(cc, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc, env->active_fpu); \
+} \
+void helper_cmpabs_d_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \
+{ \
+ int c; \
+ fdt0 = float64_abs(fdt0); \
+ fdt1 = float64_abs(fdt1); \
+ c = cond; \
+ update_fcr31(); \
+ if (c) \
+ SET_FP_COND(cc, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc, env->active_fpu); \
+}
+
+static int float64_is_unordered(int sig, float64 a, float64 b STATUS_PARAM)
+{
+ if (float64_is_signaling_nan(a) ||
+ float64_is_signaling_nan(b) ||
+ (sig && (float64_is_quiet_nan(a) || float64_is_quiet_nan(b)))) {
+ float_raise(float_flag_invalid, status);
+ return 1;
+ } else if (float64_is_quiet_nan(a) || float64_is_quiet_nan(b)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called. */
+FOP_COND_D(f, (float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status), 0))
+FOP_COND_D(un, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status))
+FOP_COND_D(eq, !float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) && float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(ueq, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(olt, !float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) && float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(ult, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(ole, !float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) && float64_le(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(ule, float64_is_unordered(0, fdt1, fdt0, &env->active_fpu.fp_status) || float64_le(fdt0, fdt1, &env->active_fpu.fp_status))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called. */
+FOP_COND_D(sf, (float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status), 0))
+FOP_COND_D(ngle,float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status))
+FOP_COND_D(seq, !float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) && float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(ngl, float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(lt, !float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) && float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(nge, float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(le, !float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) && float64_le(fdt0, fdt1, &env->active_fpu.fp_status))
+FOP_COND_D(ngt, float64_is_unordered(1, fdt1, fdt0, &env->active_fpu.fp_status) || float64_le(fdt0, fdt1, &env->active_fpu.fp_status))
+
+#define FOP_COND_S(op, cond) \
+void helper_cmp_s_ ## op (uint32_t fst0, uint32_t fst1, int cc) \
+{ \
+ int c = cond; \
+ update_fcr31(); \
+ if (c) \
+ SET_FP_COND(cc, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc, env->active_fpu); \
+} \
+void helper_cmpabs_s_ ## op (uint32_t fst0, uint32_t fst1, int cc) \
+{ \
+ int c; \
+ fst0 = float32_abs(fst0); \
+ fst1 = float32_abs(fst1); \
+ c = cond; \
+ update_fcr31(); \
+ if (c) \
+ SET_FP_COND(cc, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc, env->active_fpu); \
+}
+
+static flag float32_is_unordered(int sig, float32 a, float32 b STATUS_PARAM)
+{
+ if (float32_is_signaling_nan(a) ||
+ float32_is_signaling_nan(b) ||
+ (sig && (float32_is_quiet_nan(a) || float32_is_quiet_nan(b)))) {
+ float_raise(float_flag_invalid, status);
+ return 1;
+ } else if (float32_is_quiet_nan(a) || float32_is_quiet_nan(b)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called. */
+FOP_COND_S(f, (float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status), 0))
+FOP_COND_S(un, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status))
+FOP_COND_S(eq, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(ueq, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(olt, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(ult, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(ole, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(ule, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called. */
+FOP_COND_S(sf, (float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status), 0))
+FOP_COND_S(ngle,float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status))
+FOP_COND_S(seq, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(ngl, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(lt, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(nge, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(le, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status))
+FOP_COND_S(ngt, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status))
+
+#define FOP_COND_PS(op, condl, condh) \
+void helper_cmp_ps_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \
+{ \
+ uint32_t fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \
+ uint32_t fsth0 = float32_abs(fdt0 >> 32); \
+ uint32_t fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \
+ uint32_t fsth1 = float32_abs(fdt1 >> 32); \
+ int cl = condl; \
+ int ch = condh; \
+ \
+ update_fcr31(); \
+ if (cl) \
+ SET_FP_COND(cc, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc, env->active_fpu); \
+ if (ch) \
+ SET_FP_COND(cc + 1, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc + 1, env->active_fpu); \
+} \
+void helper_cmpabs_ps_ ## op (uint64_t fdt0, uint64_t fdt1, int cc) \
+{ \
+ uint32_t fst0 = float32_abs(fdt0 & 0XFFFFFFFF); \
+ uint32_t fsth0 = float32_abs(fdt0 >> 32); \
+ uint32_t fst1 = float32_abs(fdt1 & 0XFFFFFFFF); \
+ uint32_t fsth1 = float32_abs(fdt1 >> 32); \
+ int cl = condl; \
+ int ch = condh; \
+ \
+ update_fcr31(); \
+ if (cl) \
+ SET_FP_COND(cc, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc, env->active_fpu); \
+ if (ch) \
+ SET_FP_COND(cc + 1, env->active_fpu); \
+ else \
+ CLEAR_FP_COND(cc + 1, env->active_fpu); \
+}
+
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called. */
+FOP_COND_PS(f, (float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status), 0),
+ (float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status), 0))
+FOP_COND_PS(un, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status),
+ float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status))
+FOP_COND_PS(eq, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status),
+ !float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) && float32_eq(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(ueq, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status),
+ float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) || float32_eq(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(olt, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status),
+ !float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) && float32_lt(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(ult, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status),
+ float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) || float32_lt(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(ole, !float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status),
+ !float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) && float32_le(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(ule, float32_is_unordered(0, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status),
+ float32_is_unordered(0, fsth1, fsth0, &env->active_fpu.fp_status) || float32_le(fsth0, fsth1, &env->active_fpu.fp_status))
+/* NOTE: the comma operator will make "cond" to eval to false,
+ * but float*_is_unordered() is still called. */
+FOP_COND_PS(sf, (float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status), 0),
+ (float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status), 0))
+FOP_COND_PS(ngle,float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status),
+ float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status))
+FOP_COND_PS(seq, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_eq(fst0, fst1, &env->active_fpu.fp_status),
+ !float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) && float32_eq(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(ngl, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_eq(fst0, fst1, &env->active_fpu.fp_status),
+ float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) || float32_eq(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(lt, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_lt(fst0, fst1, &env->active_fpu.fp_status),
+ !float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) && float32_lt(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(nge, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_lt(fst0, fst1, &env->active_fpu.fp_status),
+ float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) || float32_lt(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(le, !float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) && float32_le(fst0, fst1, &env->active_fpu.fp_status),
+ !float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) && float32_le(fsth0, fsth1, &env->active_fpu.fp_status))
+FOP_COND_PS(ngt, float32_is_unordered(1, fst1, fst0, &env->active_fpu.fp_status) || float32_le(fst0, fst1, &env->active_fpu.fp_status),
+ float32_is_unordered(1, fsth1, fsth0, &env->active_fpu.fp_status) || float32_le(fsth0, fsth1, &env->active_fpu.fp_status))
diff --git a/target-mips/translate.c b/target-mips/translate.c
new file mode 100644
index 0000000..0f93e2a
--- /dev/null
+++ b/target-mips/translate.c
@@ -0,0 +1,12746 @@
+/*
+ * MIPS32 emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2006 Marius Groeger (FPU operations)
+ * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support)
+ * Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support)
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-common.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+//#define MIPS_DEBUG_DISAS
+//#define MIPS_DEBUG_SIGN_EXTENSIONS
+
+/* MIPS major opcodes */
+#define MASK_OP_MAJOR(op) (op & (0x3F << 26))
+
+enum {
+ /* indirect opcode tables */
+ OPC_SPECIAL = (0x00 << 26),
+ OPC_REGIMM = (0x01 << 26),
+ OPC_CP0 = (0x10 << 26),
+ OPC_CP1 = (0x11 << 26),
+ OPC_CP2 = (0x12 << 26),
+ OPC_CP3 = (0x13 << 26),
+ OPC_SPECIAL2 = (0x1C << 26),
+ OPC_SPECIAL3 = (0x1F << 26),
+ /* arithmetic with immediate */
+ OPC_ADDI = (0x08 << 26),
+ OPC_ADDIU = (0x09 << 26),
+ OPC_SLTI = (0x0A << 26),
+ OPC_SLTIU = (0x0B << 26),
+ /* logic with immediate */
+ OPC_ANDI = (0x0C << 26),
+ OPC_ORI = (0x0D << 26),
+ OPC_XORI = (0x0E << 26),
+ OPC_LUI = (0x0F << 26),
+ /* arithmetic with immediate */
+ OPC_DADDI = (0x18 << 26),
+ OPC_DADDIU = (0x19 << 26),
+ /* Jump and branches */
+ OPC_J = (0x02 << 26),
+ OPC_JAL = (0x03 << 26),
+ OPC_JALS = OPC_JAL | 0x5,
+ OPC_BEQ = (0x04 << 26), /* Unconditional if rs = rt = 0 (B) */
+ OPC_BEQL = (0x14 << 26),
+ OPC_BNE = (0x05 << 26),
+ OPC_BNEL = (0x15 << 26),
+ OPC_BLEZ = (0x06 << 26),
+ OPC_BLEZL = (0x16 << 26),
+ OPC_BGTZ = (0x07 << 26),
+ OPC_BGTZL = (0x17 << 26),
+ OPC_JALX = (0x1D << 26), /* MIPS 16 only */
+ OPC_JALXS = OPC_JALX | 0x5,
+ /* Load and stores */
+ OPC_LDL = (0x1A << 26),
+ OPC_LDR = (0x1B << 26),
+ OPC_LB = (0x20 << 26),
+ OPC_LH = (0x21 << 26),
+ OPC_LWL = (0x22 << 26),
+ OPC_LW = (0x23 << 26),
+ OPC_LWPC = OPC_LW | 0x5,
+ OPC_LBU = (0x24 << 26),
+ OPC_LHU = (0x25 << 26),
+ OPC_LWR = (0x26 << 26),
+ OPC_LWU = (0x27 << 26),
+ OPC_SB = (0x28 << 26),
+ OPC_SH = (0x29 << 26),
+ OPC_SWL = (0x2A << 26),
+ OPC_SW = (0x2B << 26),
+ OPC_SDL = (0x2C << 26),
+ OPC_SDR = (0x2D << 26),
+ OPC_SWR = (0x2E << 26),
+ OPC_LL = (0x30 << 26),
+ OPC_LLD = (0x34 << 26),
+ OPC_LD = (0x37 << 26),
+ OPC_LDPC = OPC_LD | 0x5,
+ OPC_SC = (0x38 << 26),
+ OPC_SCD = (0x3C << 26),
+ OPC_SD = (0x3F << 26),
+ /* Floating point load/store */
+ OPC_LWC1 = (0x31 << 26),
+ OPC_LWC2 = (0x32 << 26),
+ OPC_LDC1 = (0x35 << 26),
+ OPC_LDC2 = (0x36 << 26),
+ OPC_SWC1 = (0x39 << 26),
+ OPC_SWC2 = (0x3A << 26),
+ OPC_SDC1 = (0x3D << 26),
+ OPC_SDC2 = (0x3E << 26),
+ /* MDMX ASE specific */
+ OPC_MDMX = (0x1E << 26),
+ /* Cache and prefetch */
+ OPC_CACHE = (0x2F << 26),
+ OPC_PREF = (0x33 << 26),
+ /* Reserved major opcode */
+ OPC_MAJOR3B_RESERVED = (0x3B << 26),
+};
+
+/* MIPS special opcodes */
+#define MASK_SPECIAL(op) MASK_OP_MAJOR(op) | (op & 0x3F)
+
+enum {
+ /* Shifts */
+ OPC_SLL = 0x00 | OPC_SPECIAL,
+ /* NOP is SLL r0, r0, 0 */
+ /* SSNOP is SLL r0, r0, 1 */
+ /* EHB is SLL r0, r0, 3 */
+ OPC_SRL = 0x02 | OPC_SPECIAL, /* also ROTR */
+ OPC_ROTR = OPC_SRL | (1 << 21),
+ OPC_SRA = 0x03 | OPC_SPECIAL,
+ OPC_SLLV = 0x04 | OPC_SPECIAL,
+ OPC_SRLV = 0x06 | OPC_SPECIAL, /* also ROTRV */
+ OPC_ROTRV = OPC_SRLV | (1 << 6),
+ OPC_SRAV = 0x07 | OPC_SPECIAL,
+ OPC_DSLLV = 0x14 | OPC_SPECIAL,
+ OPC_DSRLV = 0x16 | OPC_SPECIAL, /* also DROTRV */
+ OPC_DROTRV = OPC_DSRLV | (1 << 6),
+ OPC_DSRAV = 0x17 | OPC_SPECIAL,
+ OPC_DSLL = 0x38 | OPC_SPECIAL,
+ OPC_DSRL = 0x3A | OPC_SPECIAL, /* also DROTR */
+ OPC_DROTR = OPC_DSRL | (1 << 21),
+ OPC_DSRA = 0x3B | OPC_SPECIAL,
+ OPC_DSLL32 = 0x3C | OPC_SPECIAL,
+ OPC_DSRL32 = 0x3E | OPC_SPECIAL, /* also DROTR32 */
+ OPC_DROTR32 = OPC_DSRL32 | (1 << 21),
+ OPC_DSRA32 = 0x3F | OPC_SPECIAL,
+ /* Multiplication / division */
+ OPC_MULT = 0x18 | OPC_SPECIAL,
+ OPC_MULTU = 0x19 | OPC_SPECIAL,
+ OPC_DIV = 0x1A | OPC_SPECIAL,
+ OPC_DIVU = 0x1B | OPC_SPECIAL,
+ OPC_DMULT = 0x1C | OPC_SPECIAL,
+ OPC_DMULTU = 0x1D | OPC_SPECIAL,
+ OPC_DDIV = 0x1E | OPC_SPECIAL,
+ OPC_DDIVU = 0x1F | OPC_SPECIAL,
+ /* 2 registers arithmetic / logic */
+ OPC_ADD = 0x20 | OPC_SPECIAL,
+ OPC_ADDU = 0x21 | OPC_SPECIAL,
+ OPC_SUB = 0x22 | OPC_SPECIAL,
+ OPC_SUBU = 0x23 | OPC_SPECIAL,
+ OPC_AND = 0x24 | OPC_SPECIAL,
+ OPC_OR = 0x25 | OPC_SPECIAL,
+ OPC_XOR = 0x26 | OPC_SPECIAL,
+ OPC_NOR = 0x27 | OPC_SPECIAL,
+ OPC_SLT = 0x2A | OPC_SPECIAL,
+ OPC_SLTU = 0x2B | OPC_SPECIAL,
+ OPC_DADD = 0x2C | OPC_SPECIAL,
+ OPC_DADDU = 0x2D | OPC_SPECIAL,
+ OPC_DSUB = 0x2E | OPC_SPECIAL,
+ OPC_DSUBU = 0x2F | OPC_SPECIAL,
+ /* Jumps */
+ OPC_JR = 0x08 | OPC_SPECIAL, /* Also JR.HB */
+ OPC_JALR = 0x09 | OPC_SPECIAL, /* Also JALR.HB */
+ OPC_JALRC = OPC_JALR | (0x5 << 6),
+ OPC_JALRS = 0x10 | OPC_SPECIAL | (0x5 << 6),
+ /* Traps */
+ OPC_TGE = 0x30 | OPC_SPECIAL,
+ OPC_TGEU = 0x31 | OPC_SPECIAL,
+ OPC_TLT = 0x32 | OPC_SPECIAL,
+ OPC_TLTU = 0x33 | OPC_SPECIAL,
+ OPC_TEQ = 0x34 | OPC_SPECIAL,
+ OPC_TNE = 0x36 | OPC_SPECIAL,
+ /* HI / LO registers load & stores */
+ OPC_MFHI = 0x10 | OPC_SPECIAL,
+ OPC_MTHI = 0x11 | OPC_SPECIAL,
+ OPC_MFLO = 0x12 | OPC_SPECIAL,
+ OPC_MTLO = 0x13 | OPC_SPECIAL,
+ /* Conditional moves */
+ OPC_MOVZ = 0x0A | OPC_SPECIAL,
+ OPC_MOVN = 0x0B | OPC_SPECIAL,
+
+ OPC_MOVCI = 0x01 | OPC_SPECIAL,
+
+ /* Special */
+ OPC_PMON = 0x05 | OPC_SPECIAL, /* unofficial */
+ OPC_SYSCALL = 0x0C | OPC_SPECIAL,
+ OPC_BREAK = 0x0D | OPC_SPECIAL,
+ OPC_SPIM = 0x0E | OPC_SPECIAL, /* unofficial */
+ OPC_SYNC = 0x0F | OPC_SPECIAL,
+
+ OPC_SPECIAL15_RESERVED = 0x15 | OPC_SPECIAL,
+ OPC_SPECIAL28_RESERVED = 0x28 | OPC_SPECIAL,
+ OPC_SPECIAL29_RESERVED = 0x29 | OPC_SPECIAL,
+ OPC_SPECIAL35_RESERVED = 0x35 | OPC_SPECIAL,
+ OPC_SPECIAL37_RESERVED = 0x37 | OPC_SPECIAL,
+ OPC_SPECIAL39_RESERVED = 0x39 | OPC_SPECIAL,
+ OPC_SPECIAL3D_RESERVED = 0x3D | OPC_SPECIAL,
+};
+
+/* Multiplication variants of the vr54xx. */
+#define MASK_MUL_VR54XX(op) MASK_SPECIAL(op) | (op & (0x1F << 6))
+
+enum {
+ OPC_VR54XX_MULS = (0x03 << 6) | OPC_MULT,
+ OPC_VR54XX_MULSU = (0x03 << 6) | OPC_MULTU,
+ OPC_VR54XX_MACC = (0x05 << 6) | OPC_MULT,
+ OPC_VR54XX_MACCU = (0x05 << 6) | OPC_MULTU,
+ OPC_VR54XX_MSAC = (0x07 << 6) | OPC_MULT,
+ OPC_VR54XX_MSACU = (0x07 << 6) | OPC_MULTU,
+ OPC_VR54XX_MULHI = (0x09 << 6) | OPC_MULT,
+ OPC_VR54XX_MULHIU = (0x09 << 6) | OPC_MULTU,
+ OPC_VR54XX_MULSHI = (0x0B << 6) | OPC_MULT,
+ OPC_VR54XX_MULSHIU = (0x0B << 6) | OPC_MULTU,
+ OPC_VR54XX_MACCHI = (0x0D << 6) | OPC_MULT,
+ OPC_VR54XX_MACCHIU = (0x0D << 6) | OPC_MULTU,
+ OPC_VR54XX_MSACHI = (0x0F << 6) | OPC_MULT,
+ OPC_VR54XX_MSACHIU = (0x0F << 6) | OPC_MULTU,
+};
+
+/* REGIMM (rt field) opcodes */
+#define MASK_REGIMM(op) MASK_OP_MAJOR(op) | (op & (0x1F << 16))
+
+enum {
+ OPC_BLTZ = (0x00 << 16) | OPC_REGIMM,
+ OPC_BLTZL = (0x02 << 16) | OPC_REGIMM,
+ OPC_BGEZ = (0x01 << 16) | OPC_REGIMM,
+ OPC_BGEZL = (0x03 << 16) | OPC_REGIMM,
+ OPC_BLTZAL = (0x10 << 16) | OPC_REGIMM,
+ OPC_BLTZALS = OPC_BLTZAL | 0x5, /* microMIPS */
+ OPC_BLTZALL = (0x12 << 16) | OPC_REGIMM,
+ OPC_BGEZAL = (0x11 << 16) | OPC_REGIMM,
+ OPC_BGEZALS = OPC_BGEZAL | 0x5, /* microMIPS */
+ OPC_BGEZALL = (0x13 << 16) | OPC_REGIMM,
+ OPC_TGEI = (0x08 << 16) | OPC_REGIMM,
+ OPC_TGEIU = (0x09 << 16) | OPC_REGIMM,
+ OPC_TLTI = (0x0A << 16) | OPC_REGIMM,
+ OPC_TLTIU = (0x0B << 16) | OPC_REGIMM,
+ OPC_TEQI = (0x0C << 16) | OPC_REGIMM,
+ OPC_TNEI = (0x0E << 16) | OPC_REGIMM,
+ OPC_SYNCI = (0x1F << 16) | OPC_REGIMM,
+};
+
+/* Special2 opcodes */
+#define MASK_SPECIAL2(op) MASK_OP_MAJOR(op) | (op & 0x3F)
+
+enum {
+ /* Multiply & xxx operations */
+ OPC_MADD = 0x00 | OPC_SPECIAL2,
+ OPC_MADDU = 0x01 | OPC_SPECIAL2,
+ OPC_MUL = 0x02 | OPC_SPECIAL2,
+ OPC_MSUB = 0x04 | OPC_SPECIAL2,
+ OPC_MSUBU = 0x05 | OPC_SPECIAL2,
+ /* Loongson 2F */
+ OPC_MULT_G_2F = 0x10 | OPC_SPECIAL2,
+ OPC_DMULT_G_2F = 0x11 | OPC_SPECIAL2,
+ OPC_MULTU_G_2F = 0x12 | OPC_SPECIAL2,
+ OPC_DMULTU_G_2F = 0x13 | OPC_SPECIAL2,
+ OPC_DIV_G_2F = 0x14 | OPC_SPECIAL2,
+ OPC_DDIV_G_2F = 0x15 | OPC_SPECIAL2,
+ OPC_DIVU_G_2F = 0x16 | OPC_SPECIAL2,
+ OPC_DDIVU_G_2F = 0x17 | OPC_SPECIAL2,
+ OPC_MOD_G_2F = 0x1c | OPC_SPECIAL2,
+ OPC_DMOD_G_2F = 0x1d | OPC_SPECIAL2,
+ OPC_MODU_G_2F = 0x1e | OPC_SPECIAL2,
+ OPC_DMODU_G_2F = 0x1f | OPC_SPECIAL2,
+ /* Misc */
+ OPC_CLZ = 0x20 | OPC_SPECIAL2,
+ OPC_CLO = 0x21 | OPC_SPECIAL2,
+ OPC_DCLZ = 0x24 | OPC_SPECIAL2,
+ OPC_DCLO = 0x25 | OPC_SPECIAL2,
+ /* Special */
+ OPC_SDBBP = 0x3F | OPC_SPECIAL2,
+};
+
+/* Special3 opcodes */
+#define MASK_SPECIAL3(op) MASK_OP_MAJOR(op) | (op & 0x3F)
+
+enum {
+ OPC_EXT = 0x00 | OPC_SPECIAL3,
+ OPC_DEXTM = 0x01 | OPC_SPECIAL3,
+ OPC_DEXTU = 0x02 | OPC_SPECIAL3,
+ OPC_DEXT = 0x03 | OPC_SPECIAL3,
+ OPC_INS = 0x04 | OPC_SPECIAL3,
+ OPC_DINSM = 0x05 | OPC_SPECIAL3,
+ OPC_DINSU = 0x06 | OPC_SPECIAL3,
+ OPC_DINS = 0x07 | OPC_SPECIAL3,
+ OPC_FORK = 0x08 | OPC_SPECIAL3,
+ OPC_YIELD = 0x09 | OPC_SPECIAL3,
+ OPC_BSHFL = 0x20 | OPC_SPECIAL3,
+ OPC_DBSHFL = 0x24 | OPC_SPECIAL3,
+ OPC_RDHWR = 0x3B | OPC_SPECIAL3,
+
+ /* Loongson 2E */
+ OPC_MULT_G_2E = 0x18 | OPC_SPECIAL3,
+ OPC_MULTU_G_2E = 0x19 | OPC_SPECIAL3,
+ OPC_DIV_G_2E = 0x1A | OPC_SPECIAL3,
+ OPC_DIVU_G_2E = 0x1B | OPC_SPECIAL3,
+ OPC_DMULT_G_2E = 0x1C | OPC_SPECIAL3,
+ OPC_DMULTU_G_2E = 0x1D | OPC_SPECIAL3,
+ OPC_DDIV_G_2E = 0x1E | OPC_SPECIAL3,
+ OPC_DDIVU_G_2E = 0x1F | OPC_SPECIAL3,
+ OPC_MOD_G_2E = 0x22 | OPC_SPECIAL3,
+ OPC_MODU_G_2E = 0x23 | OPC_SPECIAL3,
+ OPC_DMOD_G_2E = 0x26 | OPC_SPECIAL3,
+ OPC_DMODU_G_2E = 0x27 | OPC_SPECIAL3,
+};
+
+/* BSHFL opcodes */
+#define MASK_BSHFL(op) MASK_SPECIAL3(op) | (op & (0x1F << 6))
+
+enum {
+ OPC_WSBH = (0x02 << 6) | OPC_BSHFL,
+ OPC_SEB = (0x10 << 6) | OPC_BSHFL,
+ OPC_SEH = (0x18 << 6) | OPC_BSHFL,
+};
+
+/* DBSHFL opcodes */
+#define MASK_DBSHFL(op) MASK_SPECIAL3(op) | (op & (0x1F << 6))
+
+enum {
+ OPC_DSBH = (0x02 << 6) | OPC_DBSHFL,
+ OPC_DSHD = (0x05 << 6) | OPC_DBSHFL,
+};
+
+/* Coprocessor 0 (rs field) */
+#define MASK_CP0(op) MASK_OP_MAJOR(op) | (op & (0x1F << 21))
+
+enum {
+ OPC_MFC0 = (0x00 << 21) | OPC_CP0,
+ OPC_DMFC0 = (0x01 << 21) | OPC_CP0,
+ OPC_MTC0 = (0x04 << 21) | OPC_CP0,
+ OPC_DMTC0 = (0x05 << 21) | OPC_CP0,
+ OPC_MFTR = (0x08 << 21) | OPC_CP0,
+ OPC_RDPGPR = (0x0A << 21) | OPC_CP0,
+ OPC_MFMC0 = (0x0B << 21) | OPC_CP0,
+ OPC_MTTR = (0x0C << 21) | OPC_CP0,
+ OPC_WRPGPR = (0x0E << 21) | OPC_CP0,
+ OPC_C0 = (0x10 << 21) | OPC_CP0,
+ OPC_C0_FIRST = (0x10 << 21) | OPC_CP0,
+ OPC_C0_LAST = (0x1F << 21) | OPC_CP0,
+};
+
+/* MFMC0 opcodes */
+#define MASK_MFMC0(op) MASK_CP0(op) | (op & 0xFFFF)
+
+enum {
+ OPC_DMT = 0x01 | (0 << 5) | (0x0F << 6) | (0x01 << 11) | OPC_MFMC0,
+ OPC_EMT = 0x01 | (1 << 5) | (0x0F << 6) | (0x01 << 11) | OPC_MFMC0,
+ OPC_DVPE = 0x01 | (0 << 5) | OPC_MFMC0,
+ OPC_EVPE = 0x01 | (1 << 5) | OPC_MFMC0,
+ OPC_DI = (0 << 5) | (0x0C << 11) | OPC_MFMC0,
+ OPC_EI = (1 << 5) | (0x0C << 11) | OPC_MFMC0,
+};
+
+/* Coprocessor 0 (with rs == C0) */
+#define MASK_C0(op) MASK_CP0(op) | (op & 0x3F)
+
+enum {
+ OPC_TLBR = 0x01 | OPC_C0,
+ OPC_TLBWI = 0x02 | OPC_C0,
+ OPC_TLBWR = 0x06 | OPC_C0,
+ OPC_TLBP = 0x08 | OPC_C0,
+ OPC_RFE = 0x10 | OPC_C0,
+ OPC_ERET = 0x18 | OPC_C0,
+ OPC_DERET = 0x1F | OPC_C0,
+ OPC_WAIT = 0x20 | OPC_C0,
+};
+
+/* Coprocessor 1 (rs field) */
+#define MASK_CP1(op) MASK_OP_MAJOR(op) | (op & (0x1F << 21))
+
+/* Values for the fmt field in FP instructions */
+enum {
+ /* 0 - 15 are reserved */
+ FMT_S = 16, /* single fp */
+ FMT_D = 17, /* double fp */
+ FMT_E = 18, /* extended fp */
+ FMT_Q = 19, /* quad fp */
+ FMT_W = 20, /* 32-bit fixed */
+ FMT_L = 21, /* 64-bit fixed */
+ FMT_PS = 22, /* paired single fp */
+ /* 23 - 31 are reserved */
+};
+
+enum {
+ OPC_MFC1 = (0x00 << 21) | OPC_CP1,
+ OPC_DMFC1 = (0x01 << 21) | OPC_CP1,
+ OPC_CFC1 = (0x02 << 21) | OPC_CP1,
+ OPC_MFHC1 = (0x03 << 21) | OPC_CP1,
+ OPC_MTC1 = (0x04 << 21) | OPC_CP1,
+ OPC_DMTC1 = (0x05 << 21) | OPC_CP1,
+ OPC_CTC1 = (0x06 << 21) | OPC_CP1,
+ OPC_MTHC1 = (0x07 << 21) | OPC_CP1,
+ OPC_BC1 = (0x08 << 21) | OPC_CP1, /* bc */
+ OPC_BC1ANY2 = (0x09 << 21) | OPC_CP1,
+ OPC_BC1ANY4 = (0x0A << 21) | OPC_CP1,
+ OPC_S_FMT = (FMT_S << 21) | OPC_CP1,
+ OPC_D_FMT = (FMT_D << 21) | OPC_CP1,
+ OPC_E_FMT = (FMT_E << 21) | OPC_CP1,
+ OPC_Q_FMT = (FMT_Q << 21) | OPC_CP1,
+ OPC_W_FMT = (FMT_W << 21) | OPC_CP1,
+ OPC_L_FMT = (FMT_L << 21) | OPC_CP1,
+ OPC_PS_FMT = (FMT_PS << 21) | OPC_CP1,
+};
+
+#define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F)
+#define MASK_BC1(op) MASK_CP1(op) | (op & (0x3 << 16))
+
+enum {
+ OPC_BC1F = (0x00 << 16) | OPC_BC1,
+ OPC_BC1T = (0x01 << 16) | OPC_BC1,
+ OPC_BC1FL = (0x02 << 16) | OPC_BC1,
+ OPC_BC1TL = (0x03 << 16) | OPC_BC1,
+};
+
+enum {
+ OPC_BC1FANY2 = (0x00 << 16) | OPC_BC1ANY2,
+ OPC_BC1TANY2 = (0x01 << 16) | OPC_BC1ANY2,
+};
+
+enum {
+ OPC_BC1FANY4 = (0x00 << 16) | OPC_BC1ANY4,
+ OPC_BC1TANY4 = (0x01 << 16) | OPC_BC1ANY4,
+};
+
+#define MASK_CP2(op) MASK_OP_MAJOR(op) | (op & (0x1F << 21))
+
+enum {
+ OPC_MFC2 = (0x00 << 21) | OPC_CP2,
+ OPC_DMFC2 = (0x01 << 21) | OPC_CP2,
+ OPC_CFC2 = (0x02 << 21) | OPC_CP2,
+ OPC_MFHC2 = (0x03 << 21) | OPC_CP2,
+ OPC_MTC2 = (0x04 << 21) | OPC_CP2,
+ OPC_DMTC2 = (0x05 << 21) | OPC_CP2,
+ OPC_CTC2 = (0x06 << 21) | OPC_CP2,
+ OPC_MTHC2 = (0x07 << 21) | OPC_CP2,
+ OPC_BC2 = (0x08 << 21) | OPC_CP2,
+};
+
+#define MASK_CP3(op) MASK_OP_MAJOR(op) | (op & 0x3F)
+
+enum {
+ OPC_LWXC1 = 0x00 | OPC_CP3,
+ OPC_LDXC1 = 0x01 | OPC_CP3,
+ OPC_LUXC1 = 0x05 | OPC_CP3,
+ OPC_SWXC1 = 0x08 | OPC_CP3,
+ OPC_SDXC1 = 0x09 | OPC_CP3,
+ OPC_SUXC1 = 0x0D | OPC_CP3,
+ OPC_PREFX = 0x0F | OPC_CP3,
+ OPC_ALNV_PS = 0x1E | OPC_CP3,
+ OPC_MADD_S = 0x20 | OPC_CP3,
+ OPC_MADD_D = 0x21 | OPC_CP3,
+ OPC_MADD_PS = 0x26 | OPC_CP3,
+ OPC_MSUB_S = 0x28 | OPC_CP3,
+ OPC_MSUB_D = 0x29 | OPC_CP3,
+ OPC_MSUB_PS = 0x2E | OPC_CP3,
+ OPC_NMADD_S = 0x30 | OPC_CP3,
+ OPC_NMADD_D = 0x31 | OPC_CP3,
+ OPC_NMADD_PS= 0x36 | OPC_CP3,
+ OPC_NMSUB_S = 0x38 | OPC_CP3,
+ OPC_NMSUB_D = 0x39 | OPC_CP3,
+ OPC_NMSUB_PS= 0x3E | OPC_CP3,
+};
+
+/* global register indices */
+static TCGv_ptr cpu_env;
+static TCGv cpu_gpr[32], cpu_PC;
+static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC], cpu_ACX[MIPS_DSP_ACC];
+static TCGv cpu_dspctrl, btarget, bcond;
+static TCGv_i32 hflags;
+static TCGv_i32 fpu_fcr0, fpu_fcr31;
+
+static uint32_t gen_opc_hflags[OPC_BUF_SIZE];
+
+#include "gen-icount.h"
+
+#define gen_helper_0i(name, arg) do { \
+ TCGv_i32 helper_tmp = tcg_const_i32(arg); \
+ gen_helper_##name(helper_tmp); \
+ tcg_temp_free_i32(helper_tmp); \
+ } while(0)
+
+#define gen_helper_1i(name, arg1, arg2) do { \
+ TCGv_i32 helper_tmp = tcg_const_i32(arg2); \
+ gen_helper_##name(arg1, helper_tmp); \
+ tcg_temp_free_i32(helper_tmp); \
+ } while(0)
+
+#define gen_helper_2i(name, arg1, arg2, arg3) do { \
+ TCGv_i32 helper_tmp = tcg_const_i32(arg3); \
+ gen_helper_##name(arg1, arg2, helper_tmp); \
+ tcg_temp_free_i32(helper_tmp); \
+ } while(0)
+
+#define gen_helper_3i(name, arg1, arg2, arg3, arg4) do { \
+ TCGv_i32 helper_tmp = tcg_const_i32(arg4); \
+ gen_helper_##name(arg1, arg2, arg3, helper_tmp); \
+ tcg_temp_free_i32(helper_tmp); \
+ } while(0)
+
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong pc, saved_pc;
+ uint32_t opcode;
+ int singlestep_enabled;
+ /* Routine used to access memory */
+ int mem_idx;
+ uint32_t hflags, saved_hflags;
+ int bstate;
+ target_ulong btarget;
+} DisasContext;
+
+enum {
+ BS_NONE = 0, /* We go out of the TB without reaching a branch or an
+ * exception condition */
+ BS_STOP = 1, /* We want to stop translation for any reason */
+ BS_BRANCH = 2, /* We reached a branch condition */
+ BS_EXCP = 3, /* We reached an exception condition */
+};
+
+static const char *regnames[] =
+ { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", };
+
+static const char *regnames_HI[] =
+ { "HI0", "HI1", "HI2", "HI3", };
+
+static const char *regnames_LO[] =
+ { "LO0", "LO1", "LO2", "LO3", };
+
+static const char *regnames_ACX[] =
+ { "ACX0", "ACX1", "ACX2", "ACX3", };
+
+static const char *fregnames[] =
+ { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", };
+
+#ifdef MIPS_DEBUG_DISAS
+#define MIPS_DEBUG(fmt, ...) \
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, \
+ TARGET_FMT_lx ": %08x " fmt "\n", \
+ ctx->pc, ctx->opcode , ## __VA_ARGS__)
+#define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+#define MIPS_DEBUG(fmt, ...) do { } while(0)
+#define LOG_DISAS(...) do { } while (0)
+#endif
+
+#define MIPS_INVAL(op) \
+do { \
+ MIPS_DEBUG("Invalid %s %03x %03x %03x", op, ctx->opcode >> 26, \
+ ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)); \
+} while (0)
+
+/* General purpose registers moves. */
+static inline void gen_load_gpr (TCGv t, int reg)
+{
+ if (reg == 0)
+ tcg_gen_movi_tl(t, 0);
+ else
+ tcg_gen_mov_tl(t, cpu_gpr[reg]);
+}
+
+static inline void gen_store_gpr (TCGv t, int reg)
+{
+ if (reg != 0)
+ tcg_gen_mov_tl(cpu_gpr[reg], t);
+}
+
+/* Moves to/from ACX register. */
+static inline void gen_load_ACX (TCGv t, int reg)
+{
+ tcg_gen_mov_tl(t, cpu_ACX[reg]);
+}
+
+static inline void gen_store_ACX (TCGv t, int reg)
+{
+ tcg_gen_mov_tl(cpu_ACX[reg], t);
+}
+
+/* Moves to/from shadow registers. */
+static inline void gen_load_srsgpr (int from, int to)
+{
+ TCGv t0 = tcg_temp_new();
+
+ if (from == 0)
+ tcg_gen_movi_tl(t0, 0);
+ else {
+ TCGv_i32 t2 = tcg_temp_new_i32();
+ TCGv_ptr addr = tcg_temp_new_ptr();
+
+ tcg_gen_ld_i32(t2, cpu_env, offsetof(CPUState, CP0_SRSCtl));
+ tcg_gen_shri_i32(t2, t2, CP0SRSCtl_PSS);
+ tcg_gen_andi_i32(t2, t2, 0xf);
+ tcg_gen_muli_i32(t2, t2, sizeof(target_ulong) * 32);
+ tcg_gen_ext_i32_ptr(addr, t2);
+ tcg_gen_add_ptr(addr, cpu_env, addr);
+
+ tcg_gen_ld_tl(t0, addr, sizeof(target_ulong) * from);
+ tcg_temp_free_ptr(addr);
+ tcg_temp_free_i32(t2);
+ }
+ gen_store_gpr(t0, to);
+ tcg_temp_free(t0);
+}
+
+static inline void gen_store_srsgpr (int from, int to)
+{
+ if (to != 0) {
+ TCGv t0 = tcg_temp_new();
+ TCGv_i32 t2 = tcg_temp_new_i32();
+ TCGv_ptr addr = tcg_temp_new_ptr();
+
+ gen_load_gpr(t0, from);
+ tcg_gen_ld_i32(t2, cpu_env, offsetof(CPUState, CP0_SRSCtl));
+ tcg_gen_shri_i32(t2, t2, CP0SRSCtl_PSS);
+ tcg_gen_andi_i32(t2, t2, 0xf);
+ tcg_gen_muli_i32(t2, t2, sizeof(target_ulong) * 32);
+ tcg_gen_ext_i32_ptr(addr, t2);
+ tcg_gen_add_ptr(addr, cpu_env, addr);
+
+ tcg_gen_st_tl(t0, addr, sizeof(target_ulong) * to);
+ tcg_temp_free_ptr(addr);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free(t0);
+ }
+}
+
+/* Floating point register moves. */
+static inline void gen_load_fpr32 (TCGv_i32 t, int reg)
+{
+ tcg_gen_ld_i32(t, cpu_env, offsetof(CPUState, active_fpu.fpr[reg].w[FP_ENDIAN_IDX]));
+}
+
+static inline void gen_store_fpr32 (TCGv_i32 t, int reg)
+{
+ tcg_gen_st_i32(t, cpu_env, offsetof(CPUState, active_fpu.fpr[reg].w[FP_ENDIAN_IDX]));
+}
+
+static inline void gen_load_fpr32h (TCGv_i32 t, int reg)
+{
+ tcg_gen_ld_i32(t, cpu_env, offsetof(CPUState, active_fpu.fpr[reg].w[!FP_ENDIAN_IDX]));
+}
+
+static inline void gen_store_fpr32h (TCGv_i32 t, int reg)
+{
+ tcg_gen_st_i32(t, cpu_env, offsetof(CPUState, active_fpu.fpr[reg].w[!FP_ENDIAN_IDX]));
+}
+
+static inline void gen_load_fpr64 (DisasContext *ctx, TCGv_i64 t, int reg)
+{
+ if (ctx->hflags & MIPS_HFLAG_F64) {
+ tcg_gen_ld_i64(t, cpu_env, offsetof(CPUState, active_fpu.fpr[reg].d));
+ } else {
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ gen_load_fpr32(t0, reg & ~1);
+ gen_load_fpr32(t1, reg | 1);
+ tcg_gen_concat_i32_i64(t, t0, t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+ }
+}
+
+static inline void gen_store_fpr64 (DisasContext *ctx, TCGv_i64 t, int reg)
+{
+ if (ctx->hflags & MIPS_HFLAG_F64) {
+ tcg_gen_st_i64(t, cpu_env, offsetof(CPUState, active_fpu.fpr[reg].d));
+ } else {
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(t1, t);
+ gen_store_fpr32(t1, reg & ~1);
+ tcg_gen_shri_i64(t0, t, 32);
+ tcg_gen_trunc_i64_i32(t1, t0);
+ gen_store_fpr32(t1, reg | 1);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i64(t0);
+ }
+}
+
+static inline int get_fp_bit (int cc)
+{
+ if (cc)
+ return 24 + cc;
+ else
+ return 23;
+}
+
+/* Tests */
+static inline void gen_save_pc(target_ulong pc)
+{
+ tcg_gen_movi_tl(cpu_PC, pc);
+}
+
+static inline void save_cpu_state (DisasContext *ctx, int do_save_pc)
+{
+ LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags);
+ if (do_save_pc && ctx->pc != ctx->saved_pc) {
+ gen_save_pc(ctx->pc);
+ ctx->saved_pc = ctx->pc;
+ }
+ if (ctx->hflags != ctx->saved_hflags) {
+ tcg_gen_movi_i32(hflags, ctx->hflags);
+ ctx->saved_hflags = ctx->hflags;
+ switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
+ case MIPS_HFLAG_BR:
+ break;
+ case MIPS_HFLAG_BC:
+ case MIPS_HFLAG_BL:
+ case MIPS_HFLAG_B:
+ tcg_gen_movi_tl(btarget, ctx->btarget);
+ break;
+ }
+ }
+}
+
+static inline void restore_cpu_state (CPUState *env, DisasContext *ctx)
+{
+ ctx->saved_hflags = ctx->hflags;
+ switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
+ case MIPS_HFLAG_BR:
+ break;
+ case MIPS_HFLAG_BC:
+ case MIPS_HFLAG_BL:
+ case MIPS_HFLAG_B:
+ ctx->btarget = env->btarget;
+ break;
+ }
+}
+
+static inline void
+generate_exception_err (DisasContext *ctx, int excp, int err)
+{
+ TCGv_i32 texcp = tcg_const_i32(excp);
+ TCGv_i32 terr = tcg_const_i32(err);
+ save_cpu_state(ctx, 1);
+ gen_helper_raise_exception_err(texcp, terr);
+ tcg_temp_free_i32(terr);
+ tcg_temp_free_i32(texcp);
+}
+
+static inline void
+generate_exception (DisasContext *ctx, int excp)
+{
+ save_cpu_state(ctx, 1);
+ gen_helper_0i(raise_exception, excp);
+}
+
+/* Addresses computation */
+static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv arg1)
+{
+ tcg_gen_add_tl(ret, arg0, arg1);
+
+#if defined(TARGET_MIPS64)
+ /* For compatibility with 32-bit code, data reference in user mode
+ with Status_UX = 0 should be casted to 32-bit and sign extended.
+ See the MIPS64 PRA manual, section 4.10. */
+ if (((ctx->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) &&
+ !(ctx->hflags & MIPS_HFLAG_UX)) {
+ tcg_gen_ext32s_i64(ret, ret);
+ }
+#endif
+}
+
+static inline void check_cp0_enabled(DisasContext *ctx)
+{
+ if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0)))
+ generate_exception_err(ctx, EXCP_CpU, 0);
+}
+
+static inline void check_cp1_enabled(DisasContext *ctx)
+{
+ if (unlikely(!(ctx->hflags & MIPS_HFLAG_FPU)))
+ generate_exception_err(ctx, EXCP_CpU, 1);
+}
+
+/* Verify that the processor is running with COP1X instructions enabled.
+ This is associated with the nabla symbol in the MIPS32 and MIPS64
+ opcode tables. */
+
+static inline void check_cop1x(DisasContext *ctx)
+{
+ if (unlikely(!(ctx->hflags & MIPS_HFLAG_COP1X)))
+ generate_exception(ctx, EXCP_RI);
+}
+
+/* Verify that the processor is running with 64-bit floating-point
+ operations enabled. */
+
+static inline void check_cp1_64bitmode(DisasContext *ctx)
+{
+ if (unlikely(~ctx->hflags & (MIPS_HFLAG_F64 | MIPS_HFLAG_COP1X)))
+ generate_exception(ctx, EXCP_RI);
+}
+
+/*
+ * Verify if floating point register is valid; an operation is not defined
+ * if bit 0 of any register specification is set and the FR bit in the
+ * Status register equals zero, since the register numbers specify an
+ * even-odd pair of adjacent coprocessor general registers. When the FR bit
+ * in the Status register equals one, both even and odd register numbers
+ * are valid. This limitation exists only for 64 bit wide (d,l,ps) registers.
+ *
+ * Multiple 64 bit wide registers can be checked by calling
+ * gen_op_cp1_registers(freg1 | freg2 | ... | fregN);
+ */
+static inline void check_cp1_registers(DisasContext *ctx, int regs)
+{
+ if (unlikely(!(ctx->hflags & MIPS_HFLAG_F64) && (regs & 1)))
+ generate_exception(ctx, EXCP_RI);
+}
+
+/* This code generates a "reserved instruction" exception if the
+ CPU does not support the instruction set corresponding to flags. */
+static inline void check_insn(CPUState *env, DisasContext *ctx, int flags)
+{
+ if (unlikely(!(env->insn_flags & flags)))
+ generate_exception(ctx, EXCP_RI);
+}
+
+/* This code generates a "reserved instruction" exception if 64-bit
+ instructions are not enabled. */
+static inline void check_mips_64(DisasContext *ctx)
+{
+ if (unlikely(!(ctx->hflags & MIPS_HFLAG_64)))
+ generate_exception(ctx, EXCP_RI);
+}
+
+/* Define small wrappers for gen_load_fpr* so that we have a uniform
+ calling interface for 32 and 64-bit FPRs. No sense in changing
+ all callers for gen_load_fpr32 when we need the CTX parameter for
+ this one use. */
+#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(x, y)
+#define gen_ldcmp_fpr64(ctx, x, y) gen_load_fpr64(ctx, x, y)
+#define FOP_CONDS(type, abs, fmt, ifmt, bits) \
+static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n, \
+ int ft, int fs, int cc) \
+{ \
+ TCGv_i##bits fp0 = tcg_temp_new_i##bits (); \
+ TCGv_i##bits fp1 = tcg_temp_new_i##bits (); \
+ switch (ifmt) { \
+ case FMT_PS: \
+ check_cp1_64bitmode(ctx); \
+ break; \
+ case FMT_D: \
+ if (abs) { \
+ check_cop1x(ctx); \
+ } \
+ check_cp1_registers(ctx, fs | ft); \
+ break; \
+ case FMT_S: \
+ if (abs) { \
+ check_cop1x(ctx); \
+ } \
+ break; \
+ } \
+ gen_ldcmp_fpr##bits (ctx, fp0, fs); \
+ gen_ldcmp_fpr##bits (ctx, fp1, ft); \
+ switch (n) { \
+ case 0: gen_helper_2i(cmp ## type ## _ ## fmt ## _f, fp0, fp1, cc); break;\
+ case 1: gen_helper_2i(cmp ## type ## _ ## fmt ## _un, fp0, fp1, cc); break;\
+ case 2: gen_helper_2i(cmp ## type ## _ ## fmt ## _eq, fp0, fp1, cc); break;\
+ case 3: gen_helper_2i(cmp ## type ## _ ## fmt ## _ueq, fp0, fp1, cc); break;\
+ case 4: gen_helper_2i(cmp ## type ## _ ## fmt ## _olt, fp0, fp1, cc); break;\
+ case 5: gen_helper_2i(cmp ## type ## _ ## fmt ## _ult, fp0, fp1, cc); break;\
+ case 6: gen_helper_2i(cmp ## type ## _ ## fmt ## _ole, fp0, fp1, cc); break;\
+ case 7: gen_helper_2i(cmp ## type ## _ ## fmt ## _ule, fp0, fp1, cc); break;\
+ case 8: gen_helper_2i(cmp ## type ## _ ## fmt ## _sf, fp0, fp1, cc); break;\
+ case 9: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngle, fp0, fp1, cc); break;\
+ case 10: gen_helper_2i(cmp ## type ## _ ## fmt ## _seq, fp0, fp1, cc); break;\
+ case 11: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngl, fp0, fp1, cc); break;\
+ case 12: gen_helper_2i(cmp ## type ## _ ## fmt ## _lt, fp0, fp1, cc); break;\
+ case 13: gen_helper_2i(cmp ## type ## _ ## fmt ## _nge, fp0, fp1, cc); break;\
+ case 14: gen_helper_2i(cmp ## type ## _ ## fmt ## _le, fp0, fp1, cc); break;\
+ case 15: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngt, fp0, fp1, cc); break;\
+ default: abort(); \
+ } \
+ tcg_temp_free_i##bits (fp0); \
+ tcg_temp_free_i##bits (fp1); \
+}
+
+FOP_CONDS(, 0, d, FMT_D, 64)
+FOP_CONDS(abs, 1, d, FMT_D, 64)
+FOP_CONDS(, 0, s, FMT_S, 32)
+FOP_CONDS(abs, 1, s, FMT_S, 32)
+FOP_CONDS(, 0, ps, FMT_PS, 64)
+FOP_CONDS(abs, 1, ps, FMT_PS, 64)
+#undef FOP_CONDS
+#undef gen_ldcmp_fpr32
+#undef gen_ldcmp_fpr64
+
+/* load/store instructions. */
+#define OP_LD(insn,fname) \
+static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \
+{ \
+ tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx); \
+}
+OP_LD(lb,ld8s);
+OP_LD(lbu,ld8u);
+OP_LD(lh,ld16s);
+OP_LD(lhu,ld16u);
+OP_LD(lw,ld32s);
+#if defined(TARGET_MIPS64)
+OP_LD(lwu,ld32u);
+OP_LD(ld,ld64);
+#endif
+#undef OP_LD
+
+#define OP_ST(insn,fname) \
+static inline void op_st_##insn(TCGv arg1, TCGv arg2, DisasContext *ctx) \
+{ \
+ tcg_gen_qemu_##fname(arg1, arg2, ctx->mem_idx); \
+}
+OP_ST(sb,st8);
+OP_ST(sh,st16);
+OP_ST(sw,st32);
+#if defined(TARGET_MIPS64)
+OP_ST(sd,st64);
+#endif
+#undef OP_ST
+
+#ifdef CONFIG_USER_ONLY
+#define OP_LD_ATOMIC(insn,fname) \
+static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \
+{ \
+ TCGv t0 = tcg_temp_new(); \
+ tcg_gen_mov_tl(t0, arg1); \
+ tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx); \
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, lladdr)); \
+ tcg_gen_st_tl(ret, cpu_env, offsetof(CPUState, llval)); \
+ tcg_temp_free(t0); \
+}
+#else
+#define OP_LD_ATOMIC(insn,fname) \
+static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \
+{ \
+ gen_helper_2i(insn, ret, arg1, ctx->mem_idx); \
+}
+#endif
+OP_LD_ATOMIC(ll,ld32s);
+#if defined(TARGET_MIPS64)
+OP_LD_ATOMIC(lld,ld64);
+#endif
+#undef OP_LD_ATOMIC
+
+#ifdef CONFIG_USER_ONLY
+#define OP_ST_ATOMIC(insn,fname,ldname,almask) \
+static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \
+{ \
+ TCGv t0 = tcg_temp_new(); \
+ int l1 = gen_new_label(); \
+ int l2 = gen_new_label(); \
+ \
+ tcg_gen_andi_tl(t0, arg2, almask); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); \
+ tcg_gen_st_tl(arg2, cpu_env, offsetof(CPUState, CP0_BadVAddr)); \
+ generate_exception(ctx, EXCP_AdES); \
+ gen_set_label(l1); \
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, lladdr)); \
+ tcg_gen_brcond_tl(TCG_COND_NE, arg2, t0, l2); \
+ tcg_gen_movi_tl(t0, rt | ((almask << 3) & 0x20)); \
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, llreg)); \
+ tcg_gen_st_tl(arg1, cpu_env, offsetof(CPUState, llnewval)); \
+ gen_helper_0i(raise_exception, EXCP_SC); \
+ gen_set_label(l2); \
+ tcg_gen_movi_tl(t0, 0); \
+ gen_store_gpr(t0, rt); \
+ tcg_temp_free(t0); \
+}
+#else
+#define OP_ST_ATOMIC(insn,fname,ldname,almask) \
+static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \
+{ \
+ TCGv t0 = tcg_temp_new(); \
+ gen_helper_3i(insn, t0, arg1, arg2, ctx->mem_idx); \
+ gen_store_gpr(t0, rt); \
+ tcg_temp_free(t0); \
+}
+#endif
+OP_ST_ATOMIC(sc,st32,ld32s,0x3);
+#if defined(TARGET_MIPS64)
+OP_ST_ATOMIC(scd,st64,ld64,0x7);
+#endif
+#undef OP_ST_ATOMIC
+
+static void gen_base_offset_addr (DisasContext *ctx, TCGv addr,
+ int base, int16_t offset)
+{
+ if (base == 0) {
+ tcg_gen_movi_tl(addr, offset);
+ } else if (offset == 0) {
+ gen_load_gpr(addr, base);
+ } else {
+ tcg_gen_movi_tl(addr, offset);
+ gen_op_addr_add(ctx, addr, cpu_gpr[base], addr);
+ }
+}
+
+static target_ulong pc_relative_pc (DisasContext *ctx)
+{
+ target_ulong pc = ctx->pc;
+
+ if (ctx->hflags & MIPS_HFLAG_BMASK) {
+ int branch_bytes = ctx->hflags & MIPS_HFLAG_BDS16 ? 2 : 4;
+
+ pc -= branch_bytes;
+ }
+
+ pc &= ~(target_ulong)3;
+ return pc;
+}
+
+/* Load */
+static void gen_ld (CPUState *env, DisasContext *ctx, uint32_t opc,
+ int rt, int base, int16_t offset)
+{
+ const char *opn = "ld";
+ TCGv t0, t1;
+
+ if (rt == 0 && env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) {
+ /* Loongson CPU uses a load to zero register for prefetch.
+ We emulate it as a NOP. On other CPU we must perform the
+ actual memory access. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ gen_base_offset_addr(ctx, t0, base, offset);
+
+ switch (opc) {
+#if defined(TARGET_MIPS64)
+ case OPC_LWU:
+ save_cpu_state(ctx, 0);
+ op_ld_lwu(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lwu";
+ break;
+ case OPC_LD:
+ save_cpu_state(ctx, 0);
+ op_ld_ld(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "ld";
+ break;
+ case OPC_LLD:
+ save_cpu_state(ctx, 1);
+ op_ld_lld(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lld";
+ break;
+ case OPC_LDL:
+ save_cpu_state(ctx, 1);
+ gen_load_gpr(t1, rt);
+ gen_helper_3i(ldl, t1, t1, t0, ctx->mem_idx);
+ gen_store_gpr(t1, rt);
+ opn = "ldl";
+ break;
+ case OPC_LDR:
+ save_cpu_state(ctx, 1);
+ gen_load_gpr(t1, rt);
+ gen_helper_3i(ldr, t1, t1, t0, ctx->mem_idx);
+ gen_store_gpr(t1, rt);
+ opn = "ldr";
+ break;
+ case OPC_LDPC:
+ save_cpu_state(ctx, 0);
+ tcg_gen_movi_tl(t1, pc_relative_pc(ctx));
+ gen_op_addr_add(ctx, t0, t0, t1);
+ op_ld_ld(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "ldpc";
+ break;
+#endif
+ case OPC_LWPC:
+ save_cpu_state(ctx, 0);
+ tcg_gen_movi_tl(t1, pc_relative_pc(ctx));
+ gen_op_addr_add(ctx, t0, t0, t1);
+ op_ld_lw(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lwpc";
+ break;
+ case OPC_LW:
+ save_cpu_state(ctx, 0);
+ op_ld_lw(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lw";
+ break;
+ case OPC_LH:
+ save_cpu_state(ctx, 0);
+ op_ld_lh(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lh";
+ break;
+ case OPC_LHU:
+ save_cpu_state(ctx, 0);
+ op_ld_lhu(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lhu";
+ break;
+ case OPC_LB:
+ save_cpu_state(ctx, 0);
+ op_ld_lb(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lb";
+ break;
+ case OPC_LBU:
+ save_cpu_state(ctx, 0);
+ op_ld_lbu(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "lbu";
+ break;
+ case OPC_LWL:
+ save_cpu_state(ctx, 1);
+ gen_load_gpr(t1, rt);
+ gen_helper_3i(lwl, t1, t1, t0, ctx->mem_idx);
+ gen_store_gpr(t1, rt);
+ opn = "lwl";
+ break;
+ case OPC_LWR:
+ save_cpu_state(ctx, 1);
+ gen_load_gpr(t1, rt);
+ gen_helper_3i(lwr, t1, t1, t0, ctx->mem_idx);
+ gen_store_gpr(t1, rt);
+ opn = "lwr";
+ break;
+ case OPC_LL:
+ save_cpu_state(ctx, 1);
+ op_ld_ll(t0, t0, ctx);
+ gen_store_gpr(t0, rt);
+ opn = "ll";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* Store */
+static void gen_st (DisasContext *ctx, uint32_t opc, int rt,
+ int base, int16_t offset)
+{
+ const char *opn = "st";
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ gen_base_offset_addr(ctx, t0, base, offset);
+ gen_load_gpr(t1, rt);
+ switch (opc) {
+#if defined(TARGET_MIPS64)
+ case OPC_SD:
+ save_cpu_state(ctx, 0);
+ op_st_sd(t1, t0, ctx);
+ opn = "sd";
+ break;
+ case OPC_SDL:
+ save_cpu_state(ctx, 1);
+ gen_helper_2i(sdl, t1, t0, ctx->mem_idx);
+ opn = "sdl";
+ break;
+ case OPC_SDR:
+ save_cpu_state(ctx, 1);
+ gen_helper_2i(sdr, t1, t0, ctx->mem_idx);
+ opn = "sdr";
+ break;
+#endif
+ case OPC_SW:
+ save_cpu_state(ctx, 0);
+ op_st_sw(t1, t0, ctx);
+ opn = "sw";
+ break;
+ case OPC_SH:
+ save_cpu_state(ctx, 0);
+ op_st_sh(t1, t0, ctx);
+ opn = "sh";
+ break;
+ case OPC_SB:
+ save_cpu_state(ctx, 0);
+ op_st_sb(t1, t0, ctx);
+ opn = "sb";
+ break;
+ case OPC_SWL:
+ save_cpu_state(ctx, 1);
+ gen_helper_2i(swl, t1, t0, ctx->mem_idx);
+ opn = "swl";
+ break;
+ case OPC_SWR:
+ save_cpu_state(ctx, 1);
+ gen_helper_2i(swr, t1, t0, ctx->mem_idx);
+ opn = "swr";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+
+/* Store conditional */
+static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt,
+ int base, int16_t offset)
+{
+ const char *opn = "st_cond";
+ TCGv t0, t1;
+
+ t0 = tcg_temp_local_new();
+
+ gen_base_offset_addr(ctx, t0, base, offset);
+ /* Don't do NOP if destination is zero: we must perform the actual
+ memory access. */
+
+ t1 = tcg_temp_local_new();
+ gen_load_gpr(t1, rt);
+ switch (opc) {
+#if defined(TARGET_MIPS64)
+ case OPC_SCD:
+ save_cpu_state(ctx, 1);
+ op_st_scd(t1, t0, rt, ctx);
+ opn = "scd";
+ break;
+#endif
+ case OPC_SC:
+ save_cpu_state(ctx, 1);
+ op_st_sc(t1, t0, rt, ctx);
+ opn = "sc";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+}
+
+/* Load and store */
+static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
+ int base, int16_t offset)
+{
+ const char *opn = "flt_ldst";
+ TCGv t0 = tcg_temp_new();
+
+ gen_base_offset_addr(ctx, t0, base, offset);
+ /* Don't do NOP if destination is zero: we must perform the actual
+ memory access. */
+ switch (opc) {
+ case OPC_LWC1:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ tcg_gen_qemu_ld32s(t0, t0, ctx->mem_idx);
+ tcg_gen_trunc_tl_i32(fp0, t0);
+ gen_store_fpr32(fp0, ft);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "lwc1";
+ break;
+ case OPC_SWC1:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new();
+
+ gen_load_fpr32(fp0, ft);
+ tcg_gen_extu_i32_tl(t1, fp0);
+ tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_temp_free(t1);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "swc1";
+ break;
+ case OPC_LDC1:
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld64(fp0, t0, ctx->mem_idx);
+ gen_store_fpr64(ctx, fp0, ft);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "ldc1";
+ break;
+ case OPC_SDC1:
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, ft);
+ tcg_gen_qemu_st64(fp0, t0, ctx->mem_idx);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "sdc1";
+ break;
+ default:
+ MIPS_INVAL(opn);
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]);
+ out:
+ tcg_temp_free(t0);
+}
+
+static void gen_cop1_ldst(CPUState *env, DisasContext *ctx,
+ uint32_t op, int rt, int rs, int16_t imm)
+{
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ check_cp1_enabled(ctx);
+ gen_flt_ldst(ctx, op, rt, rs, imm);
+ } else {
+ generate_exception_err(ctx, EXCP_CpU, 1);
+ }
+}
+
+/* Arithmetic with immediate operand */
+static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
+ int rt, int rs, int16_t imm)
+{
+ target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */
+ const char *opn = "imm arith";
+
+ if (rt == 0 && opc != OPC_ADDI && opc != OPC_DADDI) {
+ /* If no destination, treat it as a NOP.
+ For addi, we must generate the overflow exception when needed. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ switch (opc) {
+ case OPC_ADDI:
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ int l1 = gen_new_label();
+
+ gen_load_gpr(t1, rs);
+ tcg_gen_addi_tl(t0, t1, uimm);
+ tcg_gen_ext32s_tl(t0, t0);
+
+ tcg_gen_xori_tl(t1, t1, ~uimm);
+ tcg_gen_xori_tl(t2, t0, uimm);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_temp_free(t1);
+ /* operands of same sign, result different sign */
+ generate_exception(ctx, EXCP_OVERFLOW);
+ gen_set_label(l1);
+ tcg_gen_ext32s_tl(t0, t0);
+ gen_store_gpr(t0, rt);
+ tcg_temp_free(t0);
+ }
+ opn = "addi";
+ break;
+ case OPC_ADDIU:
+ if (rs != 0) {
+ tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
+ tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rt], uimm);
+ }
+ opn = "addiu";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DADDI:
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ int l1 = gen_new_label();
+
+ gen_load_gpr(t1, rs);
+ tcg_gen_addi_tl(t0, t1, uimm);
+
+ tcg_gen_xori_tl(t1, t1, ~uimm);
+ tcg_gen_xori_tl(t2, t0, uimm);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_temp_free(t1);
+ /* operands of same sign, result different sign */
+ generate_exception(ctx, EXCP_OVERFLOW);
+ gen_set_label(l1);
+ gen_store_gpr(t0, rt);
+ tcg_temp_free(t0);
+ }
+ opn = "daddi";
+ break;
+ case OPC_DADDIU:
+ if (rs != 0) {
+ tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rt], uimm);
+ }
+ opn = "daddiu";
+ break;
+#endif
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
+}
+
+/* Logic with immediate operand */
+static void gen_logic_imm (CPUState *env, uint32_t opc, int rt, int rs, int16_t imm)
+{
+ target_ulong uimm;
+ const char *opn = "imm logic";
+
+ if (rt == 0) {
+ /* If no destination, treat it as a NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ uimm = (uint16_t)imm;
+ switch (opc) {
+ case OPC_ANDI:
+ if (likely(rs != 0))
+ tcg_gen_andi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
+ else
+ tcg_gen_movi_tl(cpu_gpr[rt], 0);
+ opn = "andi";
+ break;
+ case OPC_ORI:
+ if (rs != 0)
+ tcg_gen_ori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
+ else
+ tcg_gen_movi_tl(cpu_gpr[rt], uimm);
+ opn = "ori";
+ break;
+ case OPC_XORI:
+ if (likely(rs != 0))
+ tcg_gen_xori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
+ else
+ tcg_gen_movi_tl(cpu_gpr[rt], uimm);
+ opn = "xori";
+ break;
+ case OPC_LUI:
+ tcg_gen_movi_tl(cpu_gpr[rt], imm << 16);
+ opn = "lui";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
+}
+
+/* Set on less than with immediate operand */
+static void gen_slt_imm (CPUState *env, uint32_t opc, int rt, int rs, int16_t imm)
+{
+ target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */
+ const char *opn = "imm arith";
+ TCGv t0;
+
+ if (rt == 0) {
+ /* If no destination, treat it as a NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_load_gpr(t0, rs);
+ switch (opc) {
+ case OPC_SLTI:
+ tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr[rt], t0, uimm);
+ opn = "slti";
+ break;
+ case OPC_SLTIU:
+ tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr[rt], t0, uimm);
+ opn = "sltiu";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
+ tcg_temp_free(t0);
+}
+
+/* Shifts with immediate operand */
+static void gen_shift_imm(CPUState *env, DisasContext *ctx, uint32_t opc,
+ int rt, int rs, int16_t imm)
+{
+ target_ulong uimm = ((uint16_t)imm) & 0x1f;
+ const char *opn = "imm shift";
+ TCGv t0;
+
+ if (rt == 0) {
+ /* If no destination, treat it as a NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ gen_load_gpr(t0, rs);
+ switch (opc) {
+ case OPC_SLL:
+ tcg_gen_shli_tl(t0, t0, uimm);
+ tcg_gen_ext32s_tl(cpu_gpr[rt], t0);
+ opn = "sll";
+ break;
+ case OPC_SRA:
+ tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm);
+ opn = "sra";
+ break;
+ case OPC_SRL:
+ if (uimm != 0) {
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm);
+ } else {
+ tcg_gen_ext32s_tl(cpu_gpr[rt], t0);
+ }
+ opn = "srl";
+ break;
+ case OPC_ROTR:
+ if (uimm != 0) {
+ TCGv_i32 t1 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(t1, t0);
+ tcg_gen_rotri_i32(t1, t1, uimm);
+ tcg_gen_ext_i32_tl(cpu_gpr[rt], t1);
+ tcg_temp_free_i32(t1);
+ } else {
+ tcg_gen_ext32s_tl(cpu_gpr[rt], t0);
+ }
+ opn = "rotr";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DSLL:
+ tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm);
+ opn = "dsll";
+ break;
+ case OPC_DSRA:
+ tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm);
+ opn = "dsra";
+ break;
+ case OPC_DSRL:
+ tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm);
+ opn = "dsrl";
+ break;
+ case OPC_DROTR:
+ if (uimm != 0) {
+ tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm);
+ } else {
+ tcg_gen_mov_tl(cpu_gpr[rt], t0);
+ }
+ opn = "drotr";
+ break;
+ case OPC_DSLL32:
+ tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm + 32);
+ opn = "dsll32";
+ break;
+ case OPC_DSRA32:
+ tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm + 32);
+ opn = "dsra32";
+ break;
+ case OPC_DSRL32:
+ tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32);
+ opn = "dsrl32";
+ break;
+ case OPC_DROTR32:
+ tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm + 32);
+ opn = "drotr32";
+ break;
+#endif
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
+ tcg_temp_free(t0);
+}
+
+/* Arithmetic */
+static void gen_arith (CPUState *env, DisasContext *ctx, uint32_t opc,
+ int rd, int rs, int rt)
+{
+ const char *opn = "arith";
+
+ if (rd == 0 && opc != OPC_ADD && opc != OPC_SUB
+ && opc != OPC_DADD && opc != OPC_DSUB) {
+ /* If no destination, treat it as a NOP.
+ For add & sub, we must generate the overflow exception when needed. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ switch (opc) {
+ case OPC_ADD:
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ int l1 = gen_new_label();
+
+ gen_load_gpr(t1, rs);
+ gen_load_gpr(t2, rt);
+ tcg_gen_add_tl(t0, t1, t2);
+ tcg_gen_ext32s_tl(t0, t0);
+ tcg_gen_xor_tl(t1, t1, t2);
+ tcg_gen_xor_tl(t2, t0, t2);
+ tcg_gen_andc_tl(t1, t2, t1);
+ tcg_temp_free(t2);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_temp_free(t1);
+ /* operands of same sign, result different sign */
+ generate_exception(ctx, EXCP_OVERFLOW);
+ gen_set_label(l1);
+ gen_store_gpr(t0, rd);
+ tcg_temp_free(t0);
+ }
+ opn = "add";
+ break;
+ case OPC_ADDU:
+ if (rs != 0 && rt != 0) {
+ tcg_gen_add_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "addu";
+ break;
+ case OPC_SUB:
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ int l1 = gen_new_label();
+
+ gen_load_gpr(t1, rs);
+ gen_load_gpr(t2, rt);
+ tcg_gen_sub_tl(t0, t1, t2);
+ tcg_gen_ext32s_tl(t0, t0);
+ tcg_gen_xor_tl(t2, t1, t2);
+ tcg_gen_xor_tl(t1, t0, t1);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_temp_free(t1);
+ /* operands of different sign, first operand and result different sign */
+ generate_exception(ctx, EXCP_OVERFLOW);
+ gen_set_label(l1);
+ gen_store_gpr(t0, rd);
+ tcg_temp_free(t0);
+ }
+ opn = "sub";
+ break;
+ case OPC_SUBU:
+ if (rs != 0 && rt != 0) {
+ tcg_gen_sub_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_neg_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "subu";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DADD:
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ int l1 = gen_new_label();
+
+ gen_load_gpr(t1, rs);
+ gen_load_gpr(t2, rt);
+ tcg_gen_add_tl(t0, t1, t2);
+ tcg_gen_xor_tl(t1, t1, t2);
+ tcg_gen_xor_tl(t2, t0, t2);
+ tcg_gen_andc_tl(t1, t2, t1);
+ tcg_temp_free(t2);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_temp_free(t1);
+ /* operands of same sign, result different sign */
+ generate_exception(ctx, EXCP_OVERFLOW);
+ gen_set_label(l1);
+ gen_store_gpr(t0, rd);
+ tcg_temp_free(t0);
+ }
+ opn = "dadd";
+ break;
+ case OPC_DADDU:
+ if (rs != 0 && rt != 0) {
+ tcg_gen_add_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "daddu";
+ break;
+ case OPC_DSUB:
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ int l1 = gen_new_label();
+
+ gen_load_gpr(t1, rs);
+ gen_load_gpr(t2, rt);
+ tcg_gen_sub_tl(t0, t1, t2);
+ tcg_gen_xor_tl(t2, t1, t2);
+ tcg_gen_xor_tl(t1, t0, t1);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_temp_free(t2);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_temp_free(t1);
+ /* operands of different sign, first operand and result different sign */
+ generate_exception(ctx, EXCP_OVERFLOW);
+ gen_set_label(l1);
+ gen_store_gpr(t0, rd);
+ tcg_temp_free(t0);
+ }
+ opn = "dsub";
+ break;
+ case OPC_DSUBU:
+ if (rs != 0 && rt != 0) {
+ tcg_gen_sub_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_neg_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "dsubu";
+ break;
+#endif
+ case OPC_MUL:
+ if (likely(rs != 0 && rt != 0)) {
+ tcg_gen_mul_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "mul";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+}
+
+/* Conditional move */
+static void gen_cond_move (CPUState *env, uint32_t opc, int rd, int rs, int rt)
+{
+ const char *opn = "cond move";
+ int l1;
+
+ if (rd == 0) {
+ /* If no destination, treat it as a NOP.
+ For add & sub, we must generate the overflow exception when needed. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ l1 = gen_new_label();
+ switch (opc) {
+ case OPC_MOVN:
+ if (likely(rt != 0))
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rt], 0, l1);
+ else
+ tcg_gen_br(l1);
+ opn = "movn";
+ break;
+ case OPC_MOVZ:
+ if (likely(rt != 0))
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[rt], 0, l1);
+ opn = "movz";
+ break;
+ }
+ if (rs != 0)
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ else
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ gen_set_label(l1);
+
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+}
+
+/* Logic */
+static void gen_logic (CPUState *env, uint32_t opc, int rd, int rs, int rt)
+{
+ const char *opn = "logic";
+
+ if (rd == 0) {
+ /* If no destination, treat it as a NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ switch (opc) {
+ case OPC_AND:
+ if (likely(rs != 0 && rt != 0)) {
+ tcg_gen_and_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "and";
+ break;
+ case OPC_NOR:
+ if (rs != 0 && rt != 0) {
+ tcg_gen_nor_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_not_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_not_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], ~((target_ulong)0));
+ }
+ opn = "nor";
+ break;
+ case OPC_OR:
+ if (likely(rs != 0 && rt != 0)) {
+ tcg_gen_or_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "or";
+ break;
+ case OPC_XOR:
+ if (likely(rs != 0 && rt != 0)) {
+ tcg_gen_xor_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]);
+ } else if (rs == 0 && rt != 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]);
+ } else if (rs != 0 && rt == 0) {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ } else {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ }
+ opn = "xor";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+}
+
+/* Set on lower than */
+static void gen_slt (CPUState *env, uint32_t opc, int rd, int rs, int rt)
+{
+ const char *opn = "slt";
+ TCGv t0, t1;
+
+ if (rd == 0) {
+ /* If no destination, treat it as a NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+ switch (opc) {
+ case OPC_SLT:
+ tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr[rd], t0, t1);
+ opn = "slt";
+ break;
+ case OPC_SLTU:
+ tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr[rd], t0, t1);
+ opn = "sltu";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* Shifts */
+static void gen_shift (CPUState *env, DisasContext *ctx, uint32_t opc,
+ int rd, int rs, int rt)
+{
+ const char *opn = "shifts";
+ TCGv t0, t1;
+
+ if (rd == 0) {
+ /* If no destination, treat it as a NOP.
+ For add & sub, we must generate the overflow exception when needed. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+ switch (opc) {
+ case OPC_SLLV:
+ tcg_gen_andi_tl(t0, t0, 0x1f);
+ tcg_gen_shl_tl(t0, t1, t0);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], t0);
+ opn = "sllv";
+ break;
+ case OPC_SRAV:
+ tcg_gen_andi_tl(t0, t0, 0x1f);
+ tcg_gen_sar_tl(cpu_gpr[rd], t1, t0);
+ opn = "srav";
+ break;
+ case OPC_SRLV:
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_andi_tl(t0, t0, 0x1f);
+ tcg_gen_shr_tl(t0, t1, t0);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], t0);
+ opn = "srlv";
+ break;
+ case OPC_ROTRV:
+ {
+ TCGv_i32 t2 = tcg_temp_new_i32();
+ TCGv_i32 t3 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(t2, t0);
+ tcg_gen_trunc_tl_i32(t3, t1);
+ tcg_gen_andi_i32(t2, t2, 0x1f);
+ tcg_gen_rotr_i32(t2, t3, t2);
+ tcg_gen_ext_i32_tl(cpu_gpr[rd], t2);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t3);
+ opn = "rotrv";
+ }
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DSLLV:
+ tcg_gen_andi_tl(t0, t0, 0x3f);
+ tcg_gen_shl_tl(cpu_gpr[rd], t1, t0);
+ opn = "dsllv";
+ break;
+ case OPC_DSRAV:
+ tcg_gen_andi_tl(t0, t0, 0x3f);
+ tcg_gen_sar_tl(cpu_gpr[rd], t1, t0);
+ opn = "dsrav";
+ break;
+ case OPC_DSRLV:
+ tcg_gen_andi_tl(t0, t0, 0x3f);
+ tcg_gen_shr_tl(cpu_gpr[rd], t1, t0);
+ opn = "dsrlv";
+ break;
+ case OPC_DROTRV:
+ tcg_gen_andi_tl(t0, t0, 0x3f);
+ tcg_gen_rotr_tl(cpu_gpr[rd], t1, t0);
+ opn = "drotrv";
+ break;
+#endif
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* Arithmetic on HI/LO registers */
+static void gen_HILO (DisasContext *ctx, uint32_t opc, int reg)
+{
+ const char *opn = "hilo";
+
+ if (reg == 0 && (opc == OPC_MFHI || opc == OPC_MFLO)) {
+ /* Treat as NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ switch (opc) {
+ case OPC_MFHI:
+ tcg_gen_mov_tl(cpu_gpr[reg], cpu_HI[0]);
+ opn = "mfhi";
+ break;
+ case OPC_MFLO:
+ tcg_gen_mov_tl(cpu_gpr[reg], cpu_LO[0]);
+ opn = "mflo";
+ break;
+ case OPC_MTHI:
+ if (reg != 0)
+ tcg_gen_mov_tl(cpu_HI[0], cpu_gpr[reg]);
+ else
+ tcg_gen_movi_tl(cpu_HI[0], 0);
+ opn = "mthi";
+ break;
+ case OPC_MTLO:
+ if (reg != 0)
+ tcg_gen_mov_tl(cpu_LO[0], cpu_gpr[reg]);
+ else
+ tcg_gen_movi_tl(cpu_LO[0], 0);
+ opn = "mtlo";
+ break;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s", opn, regnames[reg]);
+}
+
+static void gen_muldiv (DisasContext *ctx, uint32_t opc,
+ int rs, int rt)
+{
+ const char *opn = "mul/div";
+ TCGv t0, t1;
+
+ switch (opc) {
+ case OPC_DIV:
+ case OPC_DIVU:
+#if defined(TARGET_MIPS64)
+ case OPC_DDIV:
+ case OPC_DDIVU:
+#endif
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ break;
+ default:
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ break;
+ }
+
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+ switch (opc) {
+ case OPC_DIV:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+
+ tcg_gen_ext32s_tl(t0, t0);
+ tcg_gen_ext32s_tl(t1, t1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2);
+
+ tcg_gen_mov_tl(cpu_LO[0], t0);
+ tcg_gen_movi_tl(cpu_HI[0], 0);
+ tcg_gen_br(l1);
+ gen_set_label(l2);
+ tcg_gen_div_tl(cpu_LO[0], t0, t1);
+ tcg_gen_rem_tl(cpu_HI[0], t0, t1);
+ tcg_gen_ext32s_tl(cpu_LO[0], cpu_LO[0]);
+ tcg_gen_ext32s_tl(cpu_HI[0], cpu_HI[0]);
+ gen_set_label(l1);
+ }
+ opn = "div";
+ break;
+ case OPC_DIVU:
+ {
+ int l1 = gen_new_label();
+
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_divu_tl(cpu_LO[0], t0, t1);
+ tcg_gen_remu_tl(cpu_HI[0], t0, t1);
+ tcg_gen_ext32s_tl(cpu_LO[0], cpu_LO[0]);
+ tcg_gen_ext32s_tl(cpu_HI[0], cpu_HI[0]);
+ gen_set_label(l1);
+ }
+ opn = "divu";
+ break;
+ case OPC_MULT:
+ {
+ TCGv_i64 t2 = tcg_temp_new_i64();
+ TCGv_i64 t3 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t2, t0);
+ tcg_gen_ext_tl_i64(t3, t1);
+ tcg_gen_mul_i64(t2, t2, t3);
+ tcg_temp_free_i64(t3);
+ tcg_gen_trunc_i64_tl(t0, t2);
+ tcg_gen_shri_i64(t2, t2, 32);
+ tcg_gen_trunc_i64_tl(t1, t2);
+ tcg_temp_free_i64(t2);
+ tcg_gen_ext32s_tl(cpu_LO[0], t0);
+ tcg_gen_ext32s_tl(cpu_HI[0], t1);
+ }
+ opn = "mult";
+ break;
+ case OPC_MULTU:
+ {
+ TCGv_i64 t2 = tcg_temp_new_i64();
+ TCGv_i64 t3 = tcg_temp_new_i64();
+
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_extu_tl_i64(t2, t0);
+ tcg_gen_extu_tl_i64(t3, t1);
+ tcg_gen_mul_i64(t2, t2, t3);
+ tcg_temp_free_i64(t3);
+ tcg_gen_trunc_i64_tl(t0, t2);
+ tcg_gen_shri_i64(t2, t2, 32);
+ tcg_gen_trunc_i64_tl(t1, t2);
+ tcg_temp_free_i64(t2);
+ tcg_gen_ext32s_tl(cpu_LO[0], t0);
+ tcg_gen_ext32s_tl(cpu_HI[0], t1);
+ }
+ opn = "multu";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DDIV:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
+ tcg_gen_mov_tl(cpu_LO[0], t0);
+ tcg_gen_movi_tl(cpu_HI[0], 0);
+ tcg_gen_br(l1);
+ gen_set_label(l2);
+ tcg_gen_div_i64(cpu_LO[0], t0, t1);
+ tcg_gen_rem_i64(cpu_HI[0], t0, t1);
+ gen_set_label(l1);
+ }
+ opn = "ddiv";
+ break;
+ case OPC_DDIVU:
+ {
+ int l1 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_divu_i64(cpu_LO[0], t0, t1);
+ tcg_gen_remu_i64(cpu_HI[0], t0, t1);
+ gen_set_label(l1);
+ }
+ opn = "ddivu";
+ break;
+ case OPC_DMULT:
+ gen_helper_dmult(t0, t1);
+ opn = "dmult";
+ break;
+ case OPC_DMULTU:
+ gen_helper_dmultu(t0, t1);
+ opn = "dmultu";
+ break;
+#endif
+ case OPC_MADD:
+ {
+ TCGv_i64 t2 = tcg_temp_new_i64();
+ TCGv_i64 t3 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t2, t0);
+ tcg_gen_ext_tl_i64(t3, t1);
+ tcg_gen_mul_i64(t2, t2, t3);
+ tcg_gen_concat_tl_i64(t3, cpu_LO[0], cpu_HI[0]);
+ tcg_gen_add_i64(t2, t2, t3);
+ tcg_temp_free_i64(t3);
+ tcg_gen_trunc_i64_tl(t0, t2);
+ tcg_gen_shri_i64(t2, t2, 32);
+ tcg_gen_trunc_i64_tl(t1, t2);
+ tcg_temp_free_i64(t2);
+ tcg_gen_ext32s_tl(cpu_LO[0], t0);
+ tcg_gen_ext32s_tl(cpu_HI[0], t1);
+ }
+ opn = "madd";
+ break;
+ case OPC_MADDU:
+ {
+ TCGv_i64 t2 = tcg_temp_new_i64();
+ TCGv_i64 t3 = tcg_temp_new_i64();
+
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_extu_tl_i64(t2, t0);
+ tcg_gen_extu_tl_i64(t3, t1);
+ tcg_gen_mul_i64(t2, t2, t3);
+ tcg_gen_concat_tl_i64(t3, cpu_LO[0], cpu_HI[0]);
+ tcg_gen_add_i64(t2, t2, t3);
+ tcg_temp_free_i64(t3);
+ tcg_gen_trunc_i64_tl(t0, t2);
+ tcg_gen_shri_i64(t2, t2, 32);
+ tcg_gen_trunc_i64_tl(t1, t2);
+ tcg_temp_free_i64(t2);
+ tcg_gen_ext32s_tl(cpu_LO[0], t0);
+ tcg_gen_ext32s_tl(cpu_HI[0], t1);
+ }
+ opn = "maddu";
+ break;
+ case OPC_MSUB:
+ {
+ TCGv_i64 t2 = tcg_temp_new_i64();
+ TCGv_i64 t3 = tcg_temp_new_i64();
+
+ tcg_gen_ext_tl_i64(t2, t0);
+ tcg_gen_ext_tl_i64(t3, t1);
+ tcg_gen_mul_i64(t2, t2, t3);
+ tcg_gen_concat_tl_i64(t3, cpu_LO[0], cpu_HI[0]);
+ tcg_gen_sub_i64(t2, t3, t2);
+ tcg_temp_free_i64(t3);
+ tcg_gen_trunc_i64_tl(t0, t2);
+ tcg_gen_shri_i64(t2, t2, 32);
+ tcg_gen_trunc_i64_tl(t1, t2);
+ tcg_temp_free_i64(t2);
+ tcg_gen_ext32s_tl(cpu_LO[0], t0);
+ tcg_gen_ext32s_tl(cpu_HI[0], t1);
+ }
+ opn = "msub";
+ break;
+ case OPC_MSUBU:
+ {
+ TCGv_i64 t2 = tcg_temp_new_i64();
+ TCGv_i64 t3 = tcg_temp_new_i64();
+
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_extu_tl_i64(t2, t0);
+ tcg_gen_extu_tl_i64(t3, t1);
+ tcg_gen_mul_i64(t2, t2, t3);
+ tcg_gen_concat_tl_i64(t3, cpu_LO[0], cpu_HI[0]);
+ tcg_gen_sub_i64(t2, t3, t2);
+ tcg_temp_free_i64(t3);
+ tcg_gen_trunc_i64_tl(t0, t2);
+ tcg_gen_shri_i64(t2, t2, 32);
+ tcg_gen_trunc_i64_tl(t1, t2);
+ tcg_temp_free_i64(t2);
+ tcg_gen_ext32s_tl(cpu_LO[0], t0);
+ tcg_gen_ext32s_tl(cpu_HI[0], t1);
+ }
+ opn = "msubu";
+ break;
+ default:
+ MIPS_INVAL(opn);
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]);
+ out:
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc,
+ int rd, int rs, int rt)
+{
+ const char *opn = "mul vr54xx";
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+
+ switch (opc) {
+ case OPC_VR54XX_MULS:
+ gen_helper_muls(t0, t0, t1);
+ opn = "muls";
+ break;
+ case OPC_VR54XX_MULSU:
+ gen_helper_mulsu(t0, t0, t1);
+ opn = "mulsu";
+ break;
+ case OPC_VR54XX_MACC:
+ gen_helper_macc(t0, t0, t1);
+ opn = "macc";
+ break;
+ case OPC_VR54XX_MACCU:
+ gen_helper_maccu(t0, t0, t1);
+ opn = "maccu";
+ break;
+ case OPC_VR54XX_MSAC:
+ gen_helper_msac(t0, t0, t1);
+ opn = "msac";
+ break;
+ case OPC_VR54XX_MSACU:
+ gen_helper_msacu(t0, t0, t1);
+ opn = "msacu";
+ break;
+ case OPC_VR54XX_MULHI:
+ gen_helper_mulhi(t0, t0, t1);
+ opn = "mulhi";
+ break;
+ case OPC_VR54XX_MULHIU:
+ gen_helper_mulhiu(t0, t0, t1);
+ opn = "mulhiu";
+ break;
+ case OPC_VR54XX_MULSHI:
+ gen_helper_mulshi(t0, t0, t1);
+ opn = "mulshi";
+ break;
+ case OPC_VR54XX_MULSHIU:
+ gen_helper_mulshiu(t0, t0, t1);
+ opn = "mulshiu";
+ break;
+ case OPC_VR54XX_MACCHI:
+ gen_helper_macchi(t0, t0, t1);
+ opn = "macchi";
+ break;
+ case OPC_VR54XX_MACCHIU:
+ gen_helper_macchiu(t0, t0, t1);
+ opn = "macchiu";
+ break;
+ case OPC_VR54XX_MSACHI:
+ gen_helper_msachi(t0, t0, t1);
+ opn = "msachi";
+ break;
+ case OPC_VR54XX_MSACHIU:
+ gen_helper_msachiu(t0, t0, t1);
+ opn = "msachiu";
+ break;
+ default:
+ MIPS_INVAL("mul vr54xx");
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ gen_store_gpr(t0, rd);
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]);
+
+ out:
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_cl (DisasContext *ctx, uint32_t opc,
+ int rd, int rs)
+{
+ const char *opn = "CLx";
+ TCGv t0;
+
+ if (rd == 0) {
+ /* Treat as NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_load_gpr(t0, rs);
+ switch (opc) {
+ case OPC_CLO:
+ gen_helper_clo(cpu_gpr[rd], t0);
+ opn = "clo";
+ break;
+ case OPC_CLZ:
+ gen_helper_clz(cpu_gpr[rd], t0);
+ opn = "clz";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DCLO:
+ gen_helper_dclo(cpu_gpr[rd], t0);
+ opn = "dclo";
+ break;
+ case OPC_DCLZ:
+ gen_helper_dclz(cpu_gpr[rd], t0);
+ opn = "dclz";
+ break;
+#endif
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]);
+ tcg_temp_free(t0);
+}
+
+/* Godson integer instructions */
+static void gen_loongson_integer (DisasContext *ctx, uint32_t opc,
+ int rd, int rs, int rt)
+{
+ const char *opn = "loongson";
+ TCGv t0, t1;
+
+ if (rd == 0) {
+ /* Treat as NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ switch (opc) {
+ case OPC_MULT_G_2E:
+ case OPC_MULT_G_2F:
+ case OPC_MULTU_G_2E:
+ case OPC_MULTU_G_2F:
+#if defined(TARGET_MIPS64)
+ case OPC_DMULT_G_2E:
+ case OPC_DMULT_G_2F:
+ case OPC_DMULTU_G_2E:
+ case OPC_DMULTU_G_2F:
+#endif
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ break;
+ default:
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ break;
+ }
+
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+
+ switch (opc) {
+ case OPC_MULT_G_2E:
+ case OPC_MULT_G_2F:
+ tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ opn = "mult.g";
+ break;
+ case OPC_MULTU_G_2E:
+ case OPC_MULTU_G_2F:
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ opn = "multu.g";
+ break;
+ case OPC_DIV_G_2E:
+ case OPC_DIV_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ int l3 = gen_new_label();
+ tcg_gen_ext32s_tl(t0, t0);
+ tcg_gen_ext32s_tl(t1, t1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l3);
+ gen_set_label(l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2);
+ tcg_gen_mov_tl(cpu_gpr[rd], t0);
+ tcg_gen_br(l3);
+ gen_set_label(l2);
+ tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ gen_set_label(l3);
+ }
+ opn = "div.g";
+ break;
+ case OPC_DIVU_G_2E:
+ case OPC_DIVU_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ gen_set_label(l2);
+ }
+ opn = "divu.g";
+ break;
+ case OPC_MOD_G_2E:
+ case OPC_MOD_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ int l3 = gen_new_label();
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l3);
+ gen_set_label(l2);
+ tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ gen_set_label(l3);
+ }
+ opn = "mod.g";
+ break;
+ case OPC_MODU_G_2E:
+ case OPC_MODU_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_ext32u_tl(t1, t1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+ gen_set_label(l2);
+ }
+ opn = "modu.g";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DMULT_G_2E:
+ case OPC_DMULT_G_2F:
+ tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+ opn = "dmult.g";
+ break;
+ case OPC_DMULTU_G_2E:
+ case OPC_DMULTU_G_2F:
+ tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+ opn = "dmultu.g";
+ break;
+ case OPC_DDIV_G_2E:
+ case OPC_DDIV_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ int l3 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l3);
+ gen_set_label(l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
+ tcg_gen_mov_tl(cpu_gpr[rd], t0);
+ tcg_gen_br(l3);
+ gen_set_label(l2);
+ tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+ gen_set_label(l3);
+ }
+ opn = "ddiv.g";
+ break;
+ case OPC_DDIVU_G_2E:
+ case OPC_DDIVU_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
+ gen_set_label(l2);
+ }
+ opn = "ddivu.g";
+ break;
+ case OPC_DMOD_G_2E:
+ case OPC_DMOD_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ int l3 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l3);
+ gen_set_label(l2);
+ tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
+ gen_set_label(l3);
+ }
+ opn = "dmod.g";
+ break;
+ case OPC_DMODU_G_2E:
+ case OPC_DMODU_G_2F:
+ {
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
+ gen_set_label(l2);
+ }
+ opn = "dmodu.g";
+ break;
+#endif
+ }
+
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* Traps */
+static void gen_trap (DisasContext *ctx, uint32_t opc,
+ int rs, int rt, int16_t imm)
+{
+ int cond;
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ cond = 0;
+ /* Load needed operands */
+ switch (opc) {
+ case OPC_TEQ:
+ case OPC_TGE:
+ case OPC_TGEU:
+ case OPC_TLT:
+ case OPC_TLTU:
+ case OPC_TNE:
+ /* Compare two registers */
+ if (rs != rt) {
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+ cond = 1;
+ }
+ break;
+ case OPC_TEQI:
+ case OPC_TGEI:
+ case OPC_TGEIU:
+ case OPC_TLTI:
+ case OPC_TLTIU:
+ case OPC_TNEI:
+ /* Compare register to immediate */
+ if (rs != 0 || imm != 0) {
+ gen_load_gpr(t0, rs);
+ tcg_gen_movi_tl(t1, (int32_t)imm);
+ cond = 1;
+ }
+ break;
+ }
+ if (cond == 0) {
+ switch (opc) {
+ case OPC_TEQ: /* rs == rs */
+ case OPC_TEQI: /* r0 == 0 */
+ case OPC_TGE: /* rs >= rs */
+ case OPC_TGEI: /* r0 >= 0 */
+ case OPC_TGEU: /* rs >= rs unsigned */
+ case OPC_TGEIU: /* r0 >= 0 unsigned */
+ /* Always trap */
+ generate_exception(ctx, EXCP_TRAP);
+ break;
+ case OPC_TLT: /* rs < rs */
+ case OPC_TLTI: /* r0 < 0 */
+ case OPC_TLTU: /* rs < rs unsigned */
+ case OPC_TLTIU: /* r0 < 0 unsigned */
+ case OPC_TNE: /* rs != rs */
+ case OPC_TNEI: /* r0 != 0 */
+ /* Never trap: treat as NOP. */
+ break;
+ }
+ } else {
+ int l1 = gen_new_label();
+
+ switch (opc) {
+ case OPC_TEQ:
+ case OPC_TEQI:
+ tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l1);
+ break;
+ case OPC_TGE:
+ case OPC_TGEI:
+ tcg_gen_brcond_tl(TCG_COND_LT, t0, t1, l1);
+ break;
+ case OPC_TGEU:
+ case OPC_TGEIU:
+ tcg_gen_brcond_tl(TCG_COND_LTU, t0, t1, l1);
+ break;
+ case OPC_TLT:
+ case OPC_TLTI:
+ tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1);
+ break;
+ case OPC_TLTU:
+ case OPC_TLTIU:
+ tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1);
+ break;
+ case OPC_TNE:
+ case OPC_TNEI:
+ tcg_gen_brcond_tl(TCG_COND_EQ, t0, t1, l1);
+ break;
+ }
+ generate_exception(ctx, EXCP_TRAP);
+ gen_set_label(l1);
+ }
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
+ likely(!ctx->singlestep_enabled)) {
+ tcg_gen_goto_tb(n);
+ gen_save_pc(dest);
+ tcg_gen_exit_tb((long)tb + n);
+ } else {
+ gen_save_pc(dest);
+ if (ctx->singlestep_enabled) {
+ save_cpu_state(ctx, 0);
+ gen_helper_0i(raise_exception, EXCP_DEBUG);
+ }
+ tcg_gen_exit_tb(0);
+ }
+}
+
+/* Branches (before delay slot) */
+static void gen_compute_branch (DisasContext *ctx, uint32_t opc,
+ int insn_bytes,
+ int rs, int rt, int32_t offset)
+{
+ target_ulong btgt = -1;
+ int blink = 0;
+ int bcond_compute = 0;
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ if (ctx->hflags & MIPS_HFLAG_BMASK) {
+#ifdef MIPS_DEBUG_DISAS
+ LOG_DISAS("Branch in delay slot at PC 0x" TARGET_FMT_lx "\n", ctx->pc);
+#endif
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+
+ /* Load needed operands */
+ switch (opc) {
+ case OPC_BEQ:
+ case OPC_BEQL:
+ case OPC_BNE:
+ case OPC_BNEL:
+ /* Compare two registers */
+ if (rs != rt) {
+ gen_load_gpr(t0, rs);
+ gen_load_gpr(t1, rt);
+ bcond_compute = 1;
+ }
+ btgt = ctx->pc + insn_bytes + offset;
+ break;
+ case OPC_BGEZ:
+ case OPC_BGEZAL:
+ case OPC_BGEZALS:
+ case OPC_BGEZALL:
+ case OPC_BGEZL:
+ case OPC_BGTZ:
+ case OPC_BGTZL:
+ case OPC_BLEZ:
+ case OPC_BLEZL:
+ case OPC_BLTZ:
+ case OPC_BLTZAL:
+ case OPC_BLTZALS:
+ case OPC_BLTZALL:
+ case OPC_BLTZL:
+ /* Compare to zero */
+ if (rs != 0) {
+ gen_load_gpr(t0, rs);
+ bcond_compute = 1;
+ }
+ btgt = ctx->pc + insn_bytes + offset;
+ break;
+ case OPC_J:
+ case OPC_JAL:
+ case OPC_JALX:
+ case OPC_JALS:
+ case OPC_JALXS:
+ /* Jump to immediate */
+ btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset;
+ break;
+ case OPC_JR:
+ case OPC_JALR:
+ case OPC_JALRC:
+ case OPC_JALRS:
+ /* Jump to register */
+ if (offset != 0 && offset != 16) {
+ /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the
+ others are reserved. */
+ MIPS_INVAL("jump hint");
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ gen_load_gpr(btarget, rs);
+ break;
+ default:
+ MIPS_INVAL("branch/jump");
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ if (bcond_compute == 0) {
+ /* No condition to be computed */
+ switch (opc) {
+ case OPC_BEQ: /* rx == rx */
+ case OPC_BEQL: /* rx == rx likely */
+ case OPC_BGEZ: /* 0 >= 0 */
+ case OPC_BGEZL: /* 0 >= 0 likely */
+ case OPC_BLEZ: /* 0 <= 0 */
+ case OPC_BLEZL: /* 0 <= 0 likely */
+ /* Always take */
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("balways");
+ break;
+ case OPC_BGEZALS:
+ case OPC_BGEZAL: /* 0 >= 0 */
+ case OPC_BGEZALL: /* 0 >= 0 likely */
+ ctx->hflags |= (opc == OPC_BGEZALS
+ ? MIPS_HFLAG_BDS16
+ : MIPS_HFLAG_BDS32);
+ /* Always take and link */
+ blink = 31;
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("balways and link");
+ break;
+ case OPC_BNE: /* rx != rx */
+ case OPC_BGTZ: /* 0 > 0 */
+ case OPC_BLTZ: /* 0 < 0 */
+ /* Treat as NOP. */
+ MIPS_DEBUG("bnever (NOP)");
+ goto out;
+ case OPC_BLTZALS:
+ case OPC_BLTZAL: /* 0 < 0 */
+ ctx->hflags |= (opc == OPC_BLTZALS
+ ? MIPS_HFLAG_BDS16
+ : MIPS_HFLAG_BDS32);
+ /* Handle as an unconditional branch to get correct delay
+ slot checking. */
+ blink = 31;
+ btgt = ctx->pc + (opc == OPC_BLTZALS ? 6 : 8);
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("bnever and link");
+ break;
+ case OPC_BLTZALL: /* 0 < 0 likely */
+ tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8);
+ /* Skip the instruction in the delay slot */
+ MIPS_DEBUG("bnever, link and skip");
+ ctx->pc += 4;
+ goto out;
+ case OPC_BNEL: /* rx != rx likely */
+ case OPC_BGTZL: /* 0 > 0 likely */
+ case OPC_BLTZL: /* 0 < 0 likely */
+ /* Skip the instruction in the delay slot */
+ MIPS_DEBUG("bnever and skip");
+ ctx->pc += 4;
+ goto out;
+ case OPC_J:
+ ctx->hflags |= MIPS_HFLAG_B;
+ MIPS_DEBUG("j " TARGET_FMT_lx, btgt);
+ break;
+ case OPC_JALXS:
+ case OPC_JALX:
+ ctx->hflags |= MIPS_HFLAG_BX;
+ /* Fallthrough */
+ case OPC_JALS:
+ case OPC_JAL:
+ blink = 31;
+ ctx->hflags |= MIPS_HFLAG_B;
+ ctx->hflags |= ((opc == OPC_JALS || opc == OPC_JALXS)
+ ? MIPS_HFLAG_BDS16
+ : MIPS_HFLAG_BDS32);
+ MIPS_DEBUG("jal " TARGET_FMT_lx, btgt);
+ break;
+ case OPC_JR:
+ ctx->hflags |= MIPS_HFLAG_BR;
+ if (insn_bytes == 4)
+ ctx->hflags |= MIPS_HFLAG_BDS32;
+ MIPS_DEBUG("jr %s", regnames[rs]);
+ break;
+ case OPC_JALRS:
+ case OPC_JALR:
+ case OPC_JALRC:
+ blink = rt;
+ ctx->hflags |= MIPS_HFLAG_BR;
+ ctx->hflags |= (opc == OPC_JALRS
+ ? MIPS_HFLAG_BDS16
+ : MIPS_HFLAG_BDS32);
+ MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]);
+ break;
+ default:
+ MIPS_INVAL("branch/jump");
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ } else {
+ switch (opc) {
+ case OPC_BEQ:
+ tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1);
+ MIPS_DEBUG("beq %s, %s, " TARGET_FMT_lx,
+ regnames[rs], regnames[rt], btgt);
+ goto not_likely;
+ case OPC_BEQL:
+ tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1);
+ MIPS_DEBUG("beql %s, %s, " TARGET_FMT_lx,
+ regnames[rs], regnames[rt], btgt);
+ goto likely;
+ case OPC_BNE:
+ tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1);
+ MIPS_DEBUG("bne %s, %s, " TARGET_FMT_lx,
+ regnames[rs], regnames[rt], btgt);
+ goto not_likely;
+ case OPC_BNEL:
+ tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1);
+ MIPS_DEBUG("bnel %s, %s, " TARGET_FMT_lx,
+ regnames[rs], regnames[rt], btgt);
+ goto likely;
+ case OPC_BGEZ:
+ tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
+ MIPS_DEBUG("bgez %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto not_likely;
+ case OPC_BGEZL:
+ tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
+ MIPS_DEBUG("bgezl %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto likely;
+ case OPC_BGEZALS:
+ case OPC_BGEZAL:
+ ctx->hflags |= (opc == OPC_BGEZALS
+ ? MIPS_HFLAG_BDS16
+ : MIPS_HFLAG_BDS32);
+ tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
+ MIPS_DEBUG("bgezal %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ blink = 31;
+ goto not_likely;
+ case OPC_BGEZALL:
+ tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0);
+ blink = 31;
+ MIPS_DEBUG("bgezall %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto likely;
+ case OPC_BGTZ:
+ tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0);
+ MIPS_DEBUG("bgtz %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto not_likely;
+ case OPC_BGTZL:
+ tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0);
+ MIPS_DEBUG("bgtzl %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto likely;
+ case OPC_BLEZ:
+ tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0);
+ MIPS_DEBUG("blez %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto not_likely;
+ case OPC_BLEZL:
+ tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0);
+ MIPS_DEBUG("blezl %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto likely;
+ case OPC_BLTZ:
+ tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
+ MIPS_DEBUG("bltz %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto not_likely;
+ case OPC_BLTZL:
+ tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
+ MIPS_DEBUG("bltzl %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ goto likely;
+ case OPC_BLTZALS:
+ case OPC_BLTZAL:
+ ctx->hflags |= (opc == OPC_BLTZALS
+ ? MIPS_HFLAG_BDS16
+ : MIPS_HFLAG_BDS32);
+ tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
+ blink = 31;
+ MIPS_DEBUG("bltzal %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ not_likely:
+ ctx->hflags |= MIPS_HFLAG_BC;
+ break;
+ case OPC_BLTZALL:
+ tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0);
+ blink = 31;
+ MIPS_DEBUG("bltzall %s, " TARGET_FMT_lx, regnames[rs], btgt);
+ likely:
+ ctx->hflags |= MIPS_HFLAG_BL;
+ break;
+ default:
+ MIPS_INVAL("conditional branch/jump");
+ generate_exception(ctx, EXCP_RI);
+ goto out;
+ }
+ }
+ MIPS_DEBUG("enter ds: link %d cond %02x target " TARGET_FMT_lx,
+ blink, ctx->hflags, btgt);
+
+ ctx->btarget = btgt;
+ if (blink > 0) {
+ int post_delay = insn_bytes;
+ int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16);
+
+ if (opc != OPC_JALRC)
+ post_delay += ((ctx->hflags & MIPS_HFLAG_BDS16) ? 2 : 4);
+
+ tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit);
+ }
+
+ out:
+ if (insn_bytes == 2)
+ ctx->hflags |= MIPS_HFLAG_B16;
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* special3 bitfield operations */
+static void gen_bitops (DisasContext *ctx, uint32_t opc, int rt,
+ int rs, int lsb, int msb)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ target_ulong mask;
+
+ gen_load_gpr(t1, rs);
+ switch (opc) {
+ case OPC_EXT:
+ if (lsb + msb > 31)
+ goto fail;
+ tcg_gen_shri_tl(t0, t1, lsb);
+ if (msb != 31) {
+ tcg_gen_andi_tl(t0, t0, (1 << (msb + 1)) - 1);
+ } else {
+ tcg_gen_ext32s_tl(t0, t0);
+ }
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DEXTM:
+ tcg_gen_shri_tl(t0, t1, lsb);
+ if (msb != 31) {
+ tcg_gen_andi_tl(t0, t0, (1ULL << (msb + 1 + 32)) - 1);
+ }
+ break;
+ case OPC_DEXTU:
+ tcg_gen_shri_tl(t0, t1, lsb + 32);
+ tcg_gen_andi_tl(t0, t0, (1ULL << (msb + 1)) - 1);
+ break;
+ case OPC_DEXT:
+ tcg_gen_shri_tl(t0, t1, lsb);
+ tcg_gen_andi_tl(t0, t0, (1ULL << (msb + 1)) - 1);
+ break;
+#endif
+ case OPC_INS:
+ if (lsb > msb)
+ goto fail;
+ mask = ((msb - lsb + 1 < 32) ? ((1 << (msb - lsb + 1)) - 1) : ~0) << lsb;
+ gen_load_gpr(t0, rt);
+ tcg_gen_andi_tl(t0, t0, ~mask);
+ tcg_gen_shli_tl(t1, t1, lsb);
+ tcg_gen_andi_tl(t1, t1, mask);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_gen_ext32s_tl(t0, t0);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DINSM:
+ if (lsb > msb)
+ goto fail;
+ mask = ((msb - lsb + 1 + 32 < 64) ? ((1ULL << (msb - lsb + 1 + 32)) - 1) : ~0ULL) << lsb;
+ gen_load_gpr(t0, rt);
+ tcg_gen_andi_tl(t0, t0, ~mask);
+ tcg_gen_shli_tl(t1, t1, lsb);
+ tcg_gen_andi_tl(t1, t1, mask);
+ tcg_gen_or_tl(t0, t0, t1);
+ break;
+ case OPC_DINSU:
+ if (lsb > msb)
+ goto fail;
+ mask = ((1ULL << (msb - lsb + 1)) - 1) << (lsb + 32);
+ gen_load_gpr(t0, rt);
+ tcg_gen_andi_tl(t0, t0, ~mask);
+ tcg_gen_shli_tl(t1, t1, lsb + 32);
+ tcg_gen_andi_tl(t1, t1, mask);
+ tcg_gen_or_tl(t0, t0, t1);
+ break;
+ case OPC_DINS:
+ if (lsb > msb)
+ goto fail;
+ gen_load_gpr(t0, rt);
+ mask = ((1ULL << (msb - lsb + 1)) - 1) << lsb;
+ gen_load_gpr(t0, rt);
+ tcg_gen_andi_tl(t0, t0, ~mask);
+ tcg_gen_shli_tl(t1, t1, lsb);
+ tcg_gen_andi_tl(t1, t1, mask);
+ tcg_gen_or_tl(t0, t0, t1);
+ break;
+#endif
+ default:
+fail:
+ MIPS_INVAL("bitops");
+ generate_exception(ctx, EXCP_RI);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ return;
+ }
+ gen_store_gpr(t0, rt);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd)
+{
+ TCGv t0;
+
+ if (rd == 0) {
+ /* If no destination, treat it as a NOP. */
+ MIPS_DEBUG("NOP");
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ gen_load_gpr(t0, rt);
+ switch (op2) {
+ case OPC_WSBH:
+ {
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_shri_tl(t1, t0, 8);
+ tcg_gen_andi_tl(t1, t1, 0x00FF00FF);
+ tcg_gen_shli_tl(t0, t0, 8);
+ tcg_gen_andi_tl(t0, t0, ~0x00FF00FF);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+ tcg_gen_ext32s_tl(cpu_gpr[rd], t0);
+ }
+ break;
+ case OPC_SEB:
+ tcg_gen_ext8s_tl(cpu_gpr[rd], t0);
+ break;
+ case OPC_SEH:
+ tcg_gen_ext16s_tl(cpu_gpr[rd], t0);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DSBH:
+ {
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_shri_tl(t1, t0, 8);
+ tcg_gen_andi_tl(t1, t1, 0x00FF00FF00FF00FFULL);
+ tcg_gen_shli_tl(t0, t0, 8);
+ tcg_gen_andi_tl(t0, t0, ~0x00FF00FF00FF00FFULL);
+ tcg_gen_or_tl(cpu_gpr[rd], t0, t1);
+ tcg_temp_free(t1);
+ }
+ break;
+ case OPC_DSHD:
+ {
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_shri_tl(t1, t0, 16);
+ tcg_gen_andi_tl(t1, t1, 0x0000FFFF0000FFFFULL);
+ tcg_gen_shli_tl(t0, t0, 16);
+ tcg_gen_andi_tl(t0, t0, ~0x0000FFFF0000FFFFULL);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_gen_shri_tl(t1, t0, 32);
+ tcg_gen_shli_tl(t0, t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rd], t0, t1);
+ tcg_temp_free(t1);
+ }
+ break;
+#endif
+ default:
+ MIPS_INVAL("bsfhl");
+ generate_exception(ctx, EXCP_RI);
+ tcg_temp_free(t0);
+ return;
+ }
+ tcg_temp_free(t0);
+}
+
+#ifndef CONFIG_USER_ONLY
+/* CP0 (MMU and control) */
+static inline void gen_mfc0_load32 (TCGv arg, target_ulong off)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+
+ tcg_gen_ld_i32(t0, cpu_env, off);
+ tcg_gen_ext_i32_tl(arg, t0);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void gen_mfc0_load64 (TCGv arg, target_ulong off)
+{
+ tcg_gen_ld_tl(arg, cpu_env, off);
+ tcg_gen_ext32s_tl(arg, arg);
+}
+
+static inline void gen_mtc0_store32 (TCGv arg, target_ulong off)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(t0, arg);
+ tcg_gen_st_i32(t0, cpu_env, off);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void gen_mtc0_store64 (TCGv arg, target_ulong off)
+{
+ tcg_gen_ext32s_tl(arg, arg);
+ tcg_gen_st_tl(arg, cpu_env, off);
+}
+
+static void gen_mfc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+
+ if (sel != 0)
+ check_insn(env, ctx, ISA_MIPS32);
+
+ switch (reg) {
+ case 0:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Index));
+ rn = "Index";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_mvpcontrol(arg);
+ rn = "MVPControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_mvpconf0(arg);
+ rn = "MVPConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_mvpconf1(arg);
+ rn = "MVPConf1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 1:
+ switch (sel) {
+ case 0:
+ gen_helper_mfc0_random(arg);
+ rn = "Random";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEControl));
+ rn = "VPEControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEConf0));
+ rn = "VPEConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEConf1));
+ rn = "VPEConf1";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load64(arg, offsetof(CPUState, CP0_YQMask));
+ rn = "YQMask";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load64(arg, offsetof(CPUState, CP0_VPESchedule));
+ rn = "VPESchedule";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load64(arg, offsetof(CPUState, CP0_VPEScheFBack));
+ rn = "VPEScheFBack";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEOpt));
+ rn = "VPEOpt";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 2:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EntryLo0));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "EntryLo0";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcstatus(arg);
+ rn = "TCStatus";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcbind(arg);
+ rn = "TCBind";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcrestart(arg);
+ rn = "TCRestart";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tchalt(arg);
+ rn = "TCHalt";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tccontext(arg);
+ rn = "TCContext";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcschedule(arg);
+ rn = "TCSchedule";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcschefback(arg);
+ rn = "TCScheFBack";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EntryLo1));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "EntryLo1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 4:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_Context));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "Context";
+ break;
+ case 1:
+// gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */
+ rn = "ContextConfig";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 5:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_PageMask));
+ rn = "PageMask";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_PageGrain));
+ rn = "PageGrain";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 6:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Wired));
+ rn = "Wired";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf0));
+ rn = "SRSConf0";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf1));
+ rn = "SRSConf1";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf2));
+ rn = "SRSConf2";
+ break;
+ case 4:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf3));
+ rn = "SRSConf3";
+ break;
+ case 5:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf4));
+ rn = "SRSConf4";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 7:
+ switch (sel) {
+ case 0:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_HWREna));
+ rn = "HWREna";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 8:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_BadVAddr));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "BadVAddr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 9:
+ switch (sel) {
+ case 0:
+ /* Mark as an IO operation because we read the time. */
+ if (use_icount)
+ gen_io_start();
+ gen_helper_mfc0_count(arg);
+ if (use_icount) {
+ gen_io_end();
+ }
+ /* Break the TB to be able to take timer interrupts immediately
+ after reading count. */
+ ctx->bstate = BS_STOP;
+ rn = "Count";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ break;
+ case 10:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EntryHi));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "EntryHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 11:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Compare));
+ rn = "Compare";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ break;
+ case 12:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Status));
+ rn = "Status";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_IntCtl));
+ rn = "IntCtl";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSCtl));
+ rn = "SRSCtl";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSMap));
+ rn = "SRSMap";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 13:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Cause));
+ rn = "Cause";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 14:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EPC));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "EPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 15:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_PRid));
+ rn = "PRid";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_EBase));
+ rn = "EBase";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 16:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config0));
+ rn = "Config";
+ break;
+ case 1:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config1));
+ rn = "Config1";
+ break;
+ case 2:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config2));
+ rn = "Config2";
+ break;
+ case 3:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config3));
+ rn = "Config3";
+ break;
+ /* 4,5 are reserved */
+ /* 6,7 are implementation dependent */
+ case 6:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config6));
+ rn = "Config6";
+ break;
+ case 7:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config7));
+ rn = "Config7";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ gen_helper_mfc0_lladdr(arg);
+ rn = "LLAddr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 18:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(mfc0_watchlo, arg, sel);
+ rn = "WatchLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 19:
+ switch (sel) {
+ case 0 ...7:
+ gen_helper_1i(mfc0_watchhi, arg, sel);
+ rn = "WatchHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 20:
+ switch (sel) {
+ case 0:
+#if defined(TARGET_MIPS64)
+ check_insn(env, ctx, ISA_MIPS3);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_XContext));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "XContext";
+ break;
+#endif
+ default:
+ goto die;
+ }
+ break;
+ case 21:
+ /* Officially reserved, but sel 0 is used for R1x000 framemask */
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Framemask));
+ rn = "Framemask";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 22:
+ tcg_gen_movi_tl(arg, 0); /* unimplemented */
+ rn = "'Diagnostic"; /* implementation dependent */
+ break;
+ case 23:
+ switch (sel) {
+ case 0:
+ gen_helper_mfc0_debug(arg); /* EJTAG support */
+ rn = "Debug";
+ break;
+ case 1:
+// gen_helper_mfc0_tracecontrol(arg); /* PDtrace support */
+ rn = "TraceControl";
+// break;
+ case 2:
+// gen_helper_mfc0_tracecontrol2(arg); /* PDtrace support */
+ rn = "TraceControl2";
+// break;
+ case 3:
+// gen_helper_mfc0_usertracedata(arg); /* PDtrace support */
+ rn = "UserTraceData";
+// break;
+ case 4:
+// gen_helper_mfc0_tracebpc(arg); /* PDtrace support */
+ rn = "TraceBPC";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 24:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_DEPC));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "DEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 25:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Performance0));
+ rn = "Performance0";
+ break;
+ case 1:
+// gen_helper_mfc0_performance1(arg);
+ rn = "Performance1";
+// break;
+ case 2:
+// gen_helper_mfc0_performance2(arg);
+ rn = "Performance2";
+// break;
+ case 3:
+// gen_helper_mfc0_performance3(arg);
+ rn = "Performance3";
+// break;
+ case 4:
+// gen_helper_mfc0_performance4(arg);
+ rn = "Performance4";
+// break;
+ case 5:
+// gen_helper_mfc0_performance5(arg);
+ rn = "Performance5";
+// break;
+ case 6:
+// gen_helper_mfc0_performance6(arg);
+ rn = "Performance6";
+// break;
+ case 7:
+// gen_helper_mfc0_performance7(arg);
+ rn = "Performance7";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 26:
+ tcg_gen_movi_tl(arg, 0); /* unimplemented */
+ rn = "ECC";
+ break;
+ case 27:
+ switch (sel) {
+ case 0 ... 3:
+ tcg_gen_movi_tl(arg, 0); /* unimplemented */
+ rn = "CacheErr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_TagLo));
+ rn = "TagLo";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_DataLo));
+ rn = "DataLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 29:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_TagHi));
+ rn = "TagHi";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_DataHi));
+ rn = "DataHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 30:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_ErrorEPC));
+ tcg_gen_ext32s_tl(arg, arg);
+ rn = "ErrorEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 31:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_DESAVE));
+ rn = "DESAVE";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ default:
+ goto die;
+ }
+ (void)rn; /* avoid a compiler warning */
+ LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ return;
+
+die:
+ LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ generate_exception(ctx, EXCP_RI);
+}
+
+static void gen_mtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+
+ if (sel != 0)
+ check_insn(env, ctx, ISA_MIPS32);
+
+ if (use_icount)
+ gen_io_start();
+
+ switch (reg) {
+ case 0:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_index(arg);
+ rn = "Index";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_mvpcontrol(arg);
+ rn = "MVPControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ /* ignored */
+ rn = "MVPConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ /* ignored */
+ rn = "MVPConf1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 1:
+ switch (sel) {
+ case 0:
+ /* ignored */
+ rn = "Random";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpecontrol(arg);
+ rn = "VPEControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpeconf0(arg);
+ rn = "VPEConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpeconf1(arg);
+ rn = "VPEConf1";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_yqmask(arg);
+ rn = "YQMask";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ gen_mtc0_store64(arg, offsetof(CPUState, CP0_VPESchedule));
+ rn = "VPESchedule";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ gen_mtc0_store64(arg, offsetof(CPUState, CP0_VPEScheFBack));
+ rn = "VPEScheFBack";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpeopt(arg);
+ rn = "VPEOpt";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 2:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_entrylo0(arg);
+ rn = "EntryLo0";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcstatus(arg);
+ rn = "TCStatus";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcbind(arg);
+ rn = "TCBind";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcrestart(arg);
+ rn = "TCRestart";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tchalt(arg);
+ rn = "TCHalt";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tccontext(arg);
+ rn = "TCContext";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcschedule(arg);
+ rn = "TCSchedule";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcschefback(arg);
+ rn = "TCScheFBack";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_entrylo1(arg);
+ rn = "EntryLo1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 4:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_context(arg);
+ rn = "Context";
+ break;
+ case 1:
+// gen_helper_mtc0_contextconfig(arg); /* SmartMIPS ASE */
+ rn = "ContextConfig";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 5:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_pagemask(arg);
+ rn = "PageMask";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_pagegrain(arg);
+ rn = "PageGrain";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 6:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_wired(arg);
+ rn = "Wired";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf0(arg);
+ rn = "SRSConf0";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf1(arg);
+ rn = "SRSConf1";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf2(arg);
+ rn = "SRSConf2";
+ break;
+ case 4:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf3(arg);
+ rn = "SRSConf3";
+ break;
+ case 5:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf4(arg);
+ rn = "SRSConf4";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 7:
+ switch (sel) {
+ case 0:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_hwrena(arg);
+ rn = "HWREna";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 8:
+ /* ignored */
+ rn = "BadVAddr";
+ break;
+ case 9:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_count(arg);
+ rn = "Count";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ break;
+ case 10:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_entryhi(arg);
+ rn = "EntryHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 11:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_compare(arg);
+ rn = "Compare";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ break;
+ case 12:
+ switch (sel) {
+ case 0:
+ save_cpu_state(ctx, 1);
+ gen_helper_mtc0_status(arg);
+ /* BS_STOP isn't good enough here, hflags may have changed. */
+ gen_save_pc(ctx->pc + 4);
+ ctx->bstate = BS_EXCP;
+ rn = "Status";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_intctl(arg);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "IntCtl";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsctl(arg);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "SRSCtl";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mtc0_store32(arg, offsetof(CPUState, CP0_SRSMap));
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "SRSMap";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 13:
+ switch (sel) {
+ case 0:
+ save_cpu_state(ctx, 1);
+ gen_helper_mtc0_cause(arg);
+ rn = "Cause";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 14:
+ switch (sel) {
+ case 0:
+ gen_mtc0_store64(arg, offsetof(CPUState, CP0_EPC));
+ rn = "EPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 15:
+ switch (sel) {
+ case 0:
+ /* ignored */
+ rn = "PRid";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_ebase(arg);
+ rn = "EBase";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 16:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_config0(arg);
+ rn = "Config";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case 1:
+ /* ignored, read only */
+ rn = "Config1";
+ break;
+ case 2:
+ gen_helper_mtc0_config2(arg);
+ rn = "Config2";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case 3:
+ /* ignored, read only */
+ rn = "Config3";
+ break;
+ /* 4,5 are reserved */
+ /* 6,7 are implementation dependent */
+ case 6:
+ /* ignored */
+ rn = "Config6";
+ break;
+ case 7:
+ /* ignored */
+ rn = "Config7";
+ break;
+ default:
+ rn = "Invalid config selector";
+ goto die;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_lladdr(arg);
+ rn = "LLAddr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 18:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(mtc0_watchlo, arg, sel);
+ rn = "WatchLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 19:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(mtc0_watchhi, arg, sel);
+ rn = "WatchHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 20:
+ switch (sel) {
+ case 0:
+#if defined(TARGET_MIPS64)
+ check_insn(env, ctx, ISA_MIPS3);
+ gen_helper_mtc0_xcontext(arg);
+ rn = "XContext";
+ break;
+#endif
+ default:
+ goto die;
+ }
+ break;
+ case 21:
+ /* Officially reserved, but sel 0 is used for R1x000 framemask */
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_framemask(arg);
+ rn = "Framemask";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 22:
+ /* ignored */
+ rn = "Diagnostic"; /* implementation dependent */
+ break;
+ case 23:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_debug(arg); /* EJTAG support */
+ /* BS_STOP isn't good enough here, hflags may have changed. */
+ gen_save_pc(ctx->pc + 4);
+ ctx->bstate = BS_EXCP;
+ rn = "Debug";
+ break;
+ case 1:
+// gen_helper_mtc0_tracecontrol(arg); /* PDtrace support */
+ rn = "TraceControl";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+// break;
+ case 2:
+// gen_helper_mtc0_tracecontrol2(arg); /* PDtrace support */
+ rn = "TraceControl2";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+// break;
+ case 3:
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+// gen_helper_mtc0_usertracedata(arg); /* PDtrace support */
+ rn = "UserTraceData";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+// break;
+ case 4:
+// gen_helper_mtc0_tracebpc(arg); /* PDtrace support */
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "TraceBPC";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 24:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ gen_mtc0_store64(arg, offsetof(CPUState, CP0_DEPC));
+ rn = "DEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 25:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_performance0(arg);
+ rn = "Performance0";
+ break;
+ case 1:
+// gen_helper_mtc0_performance1(arg);
+ rn = "Performance1";
+// break;
+ case 2:
+// gen_helper_mtc0_performance2(arg);
+ rn = "Performance2";
+// break;
+ case 3:
+// gen_helper_mtc0_performance3(arg);
+ rn = "Performance3";
+// break;
+ case 4:
+// gen_helper_mtc0_performance4(arg);
+ rn = "Performance4";
+// break;
+ case 5:
+// gen_helper_mtc0_performance5(arg);
+ rn = "Performance5";
+// break;
+ case 6:
+// gen_helper_mtc0_performance6(arg);
+ rn = "Performance6";
+// break;
+ case 7:
+// gen_helper_mtc0_performance7(arg);
+ rn = "Performance7";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 26:
+ /* ignored */
+ rn = "ECC";
+ break;
+ case 27:
+ switch (sel) {
+ case 0 ... 3:
+ /* ignored */
+ rn = "CacheErr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_helper_mtc0_taglo(arg);
+ rn = "TagLo";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_helper_mtc0_datalo(arg);
+ rn = "DataLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 29:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_helper_mtc0_taghi(arg);
+ rn = "TagHi";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_helper_mtc0_datahi(arg);
+ rn = "DataHi";
+ break;
+ default:
+ rn = "invalid sel";
+ goto die;
+ }
+ break;
+ case 30:
+ switch (sel) {
+ case 0:
+ gen_mtc0_store64(arg, offsetof(CPUState, CP0_ErrorEPC));
+ rn = "ErrorEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 31:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ gen_mtc0_store32(arg, offsetof(CPUState, CP0_DESAVE));
+ rn = "DESAVE";
+ break;
+ default:
+ goto die;
+ }
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ default:
+ goto die;
+ }
+ (void)rn; /* avoid a compiler warning */
+ LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ /* For simplicity assume that all writes can cause interrupts. */
+ if (use_icount) {
+ gen_io_end();
+ ctx->bstate = BS_STOP;
+ }
+ return;
+
+die:
+ LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ generate_exception(ctx, EXCP_RI);
+}
+
+#if defined(TARGET_MIPS64)
+static void gen_dmfc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+
+ if (sel != 0)
+ check_insn(env, ctx, ISA_MIPS64);
+
+ switch (reg) {
+ case 0:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Index));
+ rn = "Index";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_mvpcontrol(arg);
+ rn = "MVPControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_mvpconf0(arg);
+ rn = "MVPConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_mvpconf1(arg);
+ rn = "MVPConf1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 1:
+ switch (sel) {
+ case 0:
+ gen_helper_mfc0_random(arg);
+ rn = "Random";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEControl));
+ rn = "VPEControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEConf0));
+ rn = "VPEConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEConf1));
+ rn = "VPEConf1";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_YQMask));
+ rn = "YQMask";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_VPESchedule));
+ rn = "VPESchedule";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_VPEScheFBack));
+ rn = "VPEScheFBack";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_VPEOpt));
+ rn = "VPEOpt";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 2:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EntryLo0));
+ rn = "EntryLo0";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcstatus(arg);
+ rn = "TCStatus";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mfc0_tcbind(arg);
+ rn = "TCBind";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dmfc0_tcrestart(arg);
+ rn = "TCRestart";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dmfc0_tchalt(arg);
+ rn = "TCHalt";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dmfc0_tccontext(arg);
+ rn = "TCContext";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dmfc0_tcschedule(arg);
+ rn = "TCSchedule";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dmfc0_tcschefback(arg);
+ rn = "TCScheFBack";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EntryLo1));
+ rn = "EntryLo1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 4:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_Context));
+ rn = "Context";
+ break;
+ case 1:
+// gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */
+ rn = "ContextConfig";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 5:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_PageMask));
+ rn = "PageMask";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_PageGrain));
+ rn = "PageGrain";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 6:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Wired));
+ rn = "Wired";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf0));
+ rn = "SRSConf0";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf1));
+ rn = "SRSConf1";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf2));
+ rn = "SRSConf2";
+ break;
+ case 4:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf3));
+ rn = "SRSConf3";
+ break;
+ case 5:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSConf4));
+ rn = "SRSConf4";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 7:
+ switch (sel) {
+ case 0:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_HWREna));
+ rn = "HWREna";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 8:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_BadVAddr));
+ rn = "BadVAddr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 9:
+ switch (sel) {
+ case 0:
+ /* Mark as an IO operation because we read the time. */
+ if (use_icount)
+ gen_io_start();
+ gen_helper_mfc0_count(arg);
+ if (use_icount) {
+ gen_io_end();
+ }
+ /* Break the TB to be able to take timer interrupts immediately
+ after reading count. */
+ ctx->bstate = BS_STOP;
+ rn = "Count";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ break;
+ case 10:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EntryHi));
+ rn = "EntryHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 11:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Compare));
+ rn = "Compare";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ break;
+ case 12:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Status));
+ rn = "Status";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_IntCtl));
+ rn = "IntCtl";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSCtl));
+ rn = "SRSCtl";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_SRSMap));
+ rn = "SRSMap";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 13:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Cause));
+ rn = "Cause";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 14:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_EPC));
+ rn = "EPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 15:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_PRid));
+ rn = "PRid";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_EBase));
+ rn = "EBase";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 16:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config0));
+ rn = "Config";
+ break;
+ case 1:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config1));
+ rn = "Config1";
+ break;
+ case 2:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config2));
+ rn = "Config2";
+ break;
+ case 3:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config3));
+ rn = "Config3";
+ break;
+ /* 6,7 are implementation dependent */
+ case 6:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config6));
+ rn = "Config6";
+ break;
+ case 7:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Config7));
+ rn = "Config7";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ gen_helper_dmfc0_lladdr(arg);
+ rn = "LLAddr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 18:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(dmfc0_watchlo, arg, sel);
+ rn = "WatchLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 19:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(mfc0_watchhi, arg, sel);
+ rn = "WatchHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 20:
+ switch (sel) {
+ case 0:
+ check_insn(env, ctx, ISA_MIPS3);
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_XContext));
+ rn = "XContext";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 21:
+ /* Officially reserved, but sel 0 is used for R1x000 framemask */
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Framemask));
+ rn = "Framemask";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 22:
+ tcg_gen_movi_tl(arg, 0); /* unimplemented */
+ rn = "'Diagnostic"; /* implementation dependent */
+ break;
+ case 23:
+ switch (sel) {
+ case 0:
+ gen_helper_mfc0_debug(arg); /* EJTAG support */
+ rn = "Debug";
+ break;
+ case 1:
+// gen_helper_dmfc0_tracecontrol(arg); /* PDtrace support */
+ rn = "TraceControl";
+// break;
+ case 2:
+// gen_helper_dmfc0_tracecontrol2(arg); /* PDtrace support */
+ rn = "TraceControl2";
+// break;
+ case 3:
+// gen_helper_dmfc0_usertracedata(arg); /* PDtrace support */
+ rn = "UserTraceData";
+// break;
+ case 4:
+// gen_helper_dmfc0_tracebpc(arg); /* PDtrace support */
+ rn = "TraceBPC";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 24:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_DEPC));
+ rn = "DEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 25:
+ switch (sel) {
+ case 0:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_Performance0));
+ rn = "Performance0";
+ break;
+ case 1:
+// gen_helper_dmfc0_performance1(arg);
+ rn = "Performance1";
+// break;
+ case 2:
+// gen_helper_dmfc0_performance2(arg);
+ rn = "Performance2";
+// break;
+ case 3:
+// gen_helper_dmfc0_performance3(arg);
+ rn = "Performance3";
+// break;
+ case 4:
+// gen_helper_dmfc0_performance4(arg);
+ rn = "Performance4";
+// break;
+ case 5:
+// gen_helper_dmfc0_performance5(arg);
+ rn = "Performance5";
+// break;
+ case 6:
+// gen_helper_dmfc0_performance6(arg);
+ rn = "Performance6";
+// break;
+ case 7:
+// gen_helper_dmfc0_performance7(arg);
+ rn = "Performance7";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 26:
+ tcg_gen_movi_tl(arg, 0); /* unimplemented */
+ rn = "ECC";
+ break;
+ case 27:
+ switch (sel) {
+ /* ignored */
+ case 0 ... 3:
+ tcg_gen_movi_tl(arg, 0); /* unimplemented */
+ rn = "CacheErr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_TagLo));
+ rn = "TagLo";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_DataLo));
+ rn = "DataLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 29:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_TagHi));
+ rn = "TagHi";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_DataHi));
+ rn = "DataHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 30:
+ switch (sel) {
+ case 0:
+ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUState, CP0_ErrorEPC));
+ rn = "ErrorEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 31:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ gen_mfc0_load32(arg, offsetof(CPUState, CP0_DESAVE));
+ rn = "DESAVE";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ default:
+ goto die;
+ }
+ (void)rn; /* avoid a compiler warning */
+ LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ return;
+
+die:
+ LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ generate_exception(ctx, EXCP_RI);
+}
+
+static void gen_dmtc0 (CPUState *env, DisasContext *ctx, TCGv arg, int reg, int sel)
+{
+ const char *rn = "invalid";
+
+ if (sel != 0)
+ check_insn(env, ctx, ISA_MIPS64);
+
+ if (use_icount)
+ gen_io_start();
+
+ switch (reg) {
+ case 0:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_index(arg);
+ rn = "Index";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_mvpcontrol(arg);
+ rn = "MVPControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ /* ignored */
+ rn = "MVPConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ /* ignored */
+ rn = "MVPConf1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 1:
+ switch (sel) {
+ case 0:
+ /* ignored */
+ rn = "Random";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpecontrol(arg);
+ rn = "VPEControl";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpeconf0(arg);
+ rn = "VPEConf0";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpeconf1(arg);
+ rn = "VPEConf1";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_yqmask(arg);
+ rn = "YQMask";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUState, CP0_VPESchedule));
+ rn = "VPESchedule";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUState, CP0_VPEScheFBack));
+ rn = "VPEScheFBack";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_vpeopt(arg);
+ rn = "VPEOpt";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 2:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_entrylo0(arg);
+ rn = "EntryLo0";
+ break;
+ case 1:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcstatus(arg);
+ rn = "TCStatus";
+ break;
+ case 2:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcbind(arg);
+ rn = "TCBind";
+ break;
+ case 3:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcrestart(arg);
+ rn = "TCRestart";
+ break;
+ case 4:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tchalt(arg);
+ rn = "TCHalt";
+ break;
+ case 5:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tccontext(arg);
+ rn = "TCContext";
+ break;
+ case 6:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcschedule(arg);
+ rn = "TCSchedule";
+ break;
+ case 7:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_mtc0_tcschefback(arg);
+ rn = "TCScheFBack";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 3:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_entrylo1(arg);
+ rn = "EntryLo1";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 4:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_context(arg);
+ rn = "Context";
+ break;
+ case 1:
+// gen_helper_mtc0_contextconfig(arg); /* SmartMIPS ASE */
+ rn = "ContextConfig";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 5:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_pagemask(arg);
+ rn = "PageMask";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_pagegrain(arg);
+ rn = "PageGrain";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 6:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_wired(arg);
+ rn = "Wired";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf0(arg);
+ rn = "SRSConf0";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf1(arg);
+ rn = "SRSConf1";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf2(arg);
+ rn = "SRSConf2";
+ break;
+ case 4:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf3(arg);
+ rn = "SRSConf3";
+ break;
+ case 5:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsconf4(arg);
+ rn = "SRSConf4";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 7:
+ switch (sel) {
+ case 0:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_hwrena(arg);
+ rn = "HWREna";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 8:
+ /* ignored */
+ rn = "BadVAddr";
+ break;
+ case 9:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_count(arg);
+ rn = "Count";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case 10:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_entryhi(arg);
+ rn = "EntryHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 11:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_compare(arg);
+ rn = "Compare";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ goto die;
+ }
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case 12:
+ switch (sel) {
+ case 0:
+ save_cpu_state(ctx, 1);
+ gen_helper_mtc0_status(arg);
+ /* BS_STOP isn't good enough here, hflags may have changed. */
+ gen_save_pc(ctx->pc + 4);
+ ctx->bstate = BS_EXCP;
+ rn = "Status";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_intctl(arg);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "IntCtl";
+ break;
+ case 2:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_srsctl(arg);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "SRSCtl";
+ break;
+ case 3:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_mtc0_store32(arg, offsetof(CPUState, CP0_SRSMap));
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "SRSMap";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 13:
+ switch (sel) {
+ case 0:
+ save_cpu_state(ctx, 1);
+ /* Mark as an IO operation because we may trigger a software
+ interrupt. */
+ if (use_icount) {
+ gen_io_start();
+ }
+ gen_helper_mtc0_cause(arg);
+ if (use_icount) {
+ gen_io_end();
+ }
+ /* Stop translation as we may have triggered an intetrupt */
+ ctx->bstate = BS_STOP;
+ rn = "Cause";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 14:
+ switch (sel) {
+ case 0:
+ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUState, CP0_EPC));
+ rn = "EPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 15:
+ switch (sel) {
+ case 0:
+ /* ignored */
+ rn = "PRid";
+ break;
+ case 1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_helper_mtc0_ebase(arg);
+ rn = "EBase";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 16:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_config0(arg);
+ rn = "Config";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case 1:
+ /* ignored, read only */
+ rn = "Config1";
+ break;
+ case 2:
+ gen_helper_mtc0_config2(arg);
+ rn = "Config2";
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case 3:
+ /* ignored */
+ rn = "Config3";
+ break;
+ /* 6,7 are implementation dependent */
+ default:
+ rn = "Invalid config selector";
+ goto die;
+ }
+ break;
+ case 17:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_lladdr(arg);
+ rn = "LLAddr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 18:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(mtc0_watchlo, arg, sel);
+ rn = "WatchLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 19:
+ switch (sel) {
+ case 0 ... 7:
+ gen_helper_1i(mtc0_watchhi, arg, sel);
+ rn = "WatchHi";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 20:
+ switch (sel) {
+ case 0:
+ check_insn(env, ctx, ISA_MIPS3);
+ gen_helper_mtc0_xcontext(arg);
+ rn = "XContext";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 21:
+ /* Officially reserved, but sel 0 is used for R1x000 framemask */
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_framemask(arg);
+ rn = "Framemask";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 22:
+ /* ignored */
+ rn = "Diagnostic"; /* implementation dependent */
+ break;
+ case 23:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_debug(arg); /* EJTAG support */
+ /* BS_STOP isn't good enough here, hflags may have changed. */
+ gen_save_pc(ctx->pc + 4);
+ ctx->bstate = BS_EXCP;
+ rn = "Debug";
+ break;
+ case 1:
+// gen_helper_mtc0_tracecontrol(arg); /* PDtrace support */
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "TraceControl";
+// break;
+ case 2:
+// gen_helper_mtc0_tracecontrol2(arg); /* PDtrace support */
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "TraceControl2";
+// break;
+ case 3:
+// gen_helper_mtc0_usertracedata(arg); /* PDtrace support */
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "UserTraceData";
+// break;
+ case 4:
+// gen_helper_mtc0_tracebpc(arg); /* PDtrace support */
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ rn = "TraceBPC";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 24:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUState, CP0_DEPC));
+ rn = "DEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 25:
+ switch (sel) {
+ case 0:
+ gen_helper_mtc0_performance0(arg);
+ rn = "Performance0";
+ break;
+ case 1:
+// gen_helper_mtc0_performance1(arg);
+ rn = "Performance1";
+// break;
+ case 2:
+// gen_helper_mtc0_performance2(arg);
+ rn = "Performance2";
+// break;
+ case 3:
+// gen_helper_mtc0_performance3(arg);
+ rn = "Performance3";
+// break;
+ case 4:
+// gen_helper_mtc0_performance4(arg);
+ rn = "Performance4";
+// break;
+ case 5:
+// gen_helper_mtc0_performance5(arg);
+ rn = "Performance5";
+// break;
+ case 6:
+// gen_helper_mtc0_performance6(arg);
+ rn = "Performance6";
+// break;
+ case 7:
+// gen_helper_mtc0_performance7(arg);
+ rn = "Performance7";
+// break;
+ default:
+ goto die;
+ }
+ break;
+ case 26:
+ /* ignored */
+ rn = "ECC";
+ break;
+ case 27:
+ switch (sel) {
+ case 0 ... 3:
+ /* ignored */
+ rn = "CacheErr";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 28:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_helper_mtc0_taglo(arg);
+ rn = "TagLo";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_helper_mtc0_datalo(arg);
+ rn = "DataLo";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 29:
+ switch (sel) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ gen_helper_mtc0_taghi(arg);
+ rn = "TagHi";
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ gen_helper_mtc0_datahi(arg);
+ rn = "DataHi";
+ break;
+ default:
+ rn = "invalid sel";
+ goto die;
+ }
+ break;
+ case 30:
+ switch (sel) {
+ case 0:
+ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUState, CP0_ErrorEPC));
+ rn = "ErrorEPC";
+ break;
+ default:
+ goto die;
+ }
+ break;
+ case 31:
+ switch (sel) {
+ case 0:
+ /* EJTAG support */
+ gen_mtc0_store32(arg, offsetof(CPUState, CP0_DESAVE));
+ rn = "DESAVE";
+ break;
+ default:
+ goto die;
+ }
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ default:
+ goto die;
+ }
+ (void)rn; /* avoid a compiler warning */
+ LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ /* For simplicity assume that all writes can cause interrupts. */
+ if (use_icount) {
+ gen_io_end();
+ ctx->bstate = BS_STOP;
+ }
+ return;
+
+die:
+ LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel);
+ generate_exception(ctx, EXCP_RI);
+}
+#endif /* TARGET_MIPS64 */
+
+static void gen_mftr(CPUState *env, DisasContext *ctx, int rt, int rd,
+ int u, int sel, int h)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ TCGv t0 = tcg_temp_local_new();
+
+ if ((env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) == 0 &&
+ ((env->tcs[other_tc].CP0_TCBind & (0xf << CP0TCBd_CurVPE)) !=
+ (env->active_tc.CP0_TCBind & (0xf << CP0TCBd_CurVPE))))
+ tcg_gen_movi_tl(t0, -1);
+ else if ((env->CP0_VPEControl & (0xff << CP0VPECo_TargTC)) >
+ (env->mvp->CP0_MVPConf0 & (0xff << CP0MVPC0_PTC)))
+ tcg_gen_movi_tl(t0, -1);
+ else if (u == 0) {
+ switch (rt) {
+ case 2:
+ switch (sel) {
+ case 1:
+ gen_helper_mftc0_tcstatus(t0);
+ break;
+ case 2:
+ gen_helper_mftc0_tcbind(t0);
+ break;
+ case 3:
+ gen_helper_mftc0_tcrestart(t0);
+ break;
+ case 4:
+ gen_helper_mftc0_tchalt(t0);
+ break;
+ case 5:
+ gen_helper_mftc0_tccontext(t0);
+ break;
+ case 6:
+ gen_helper_mftc0_tcschedule(t0);
+ break;
+ case 7:
+ gen_helper_mftc0_tcschefback(t0);
+ break;
+ default:
+ gen_mfc0(env, ctx, t0, rt, sel);
+ break;
+ }
+ break;
+ case 10:
+ switch (sel) {
+ case 0:
+ gen_helper_mftc0_entryhi(t0);
+ break;
+ default:
+ gen_mfc0(env, ctx, t0, rt, sel);
+ break;
+ }
+ case 12:
+ switch (sel) {
+ case 0:
+ gen_helper_mftc0_status(t0);
+ break;
+ default:
+ gen_mfc0(env, ctx, t0, rt, sel);
+ break;
+ }
+ case 23:
+ switch (sel) {
+ case 0:
+ gen_helper_mftc0_debug(t0);
+ break;
+ default:
+ gen_mfc0(env, ctx, t0, rt, sel);
+ break;
+ }
+ break;
+ default:
+ gen_mfc0(env, ctx, t0, rt, sel);
+ }
+ } else switch (sel) {
+ /* GPR registers. */
+ case 0:
+ gen_helper_1i(mftgpr, t0, rt);
+ break;
+ /* Auxiliary CPU registers */
+ case 1:
+ switch (rt) {
+ case 0:
+ gen_helper_1i(mftlo, t0, 0);
+ break;
+ case 1:
+ gen_helper_1i(mfthi, t0, 0);
+ break;
+ case 2:
+ gen_helper_1i(mftacx, t0, 0);
+ break;
+ case 4:
+ gen_helper_1i(mftlo, t0, 1);
+ break;
+ case 5:
+ gen_helper_1i(mfthi, t0, 1);
+ break;
+ case 6:
+ gen_helper_1i(mftacx, t0, 1);
+ break;
+ case 8:
+ gen_helper_1i(mftlo, t0, 2);
+ break;
+ case 9:
+ gen_helper_1i(mfthi, t0, 2);
+ break;
+ case 10:
+ gen_helper_1i(mftacx, t0, 2);
+ break;
+ case 12:
+ gen_helper_1i(mftlo, t0, 3);
+ break;
+ case 13:
+ gen_helper_1i(mfthi, t0, 3);
+ break;
+ case 14:
+ gen_helper_1i(mftacx, t0, 3);
+ break;
+ case 16:
+ gen_helper_mftdsp(t0);
+ break;
+ default:
+ goto die;
+ }
+ break;
+ /* Floating point (COP1). */
+ case 2:
+ /* XXX: For now we support only a single FPU context. */
+ if (h == 0) {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, rt);
+ tcg_gen_ext_i32_tl(t0, fp0);
+ tcg_temp_free_i32(fp0);
+ } else {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32h(fp0, rt);
+ tcg_gen_ext_i32_tl(t0, fp0);
+ tcg_temp_free_i32(fp0);
+ }
+ break;
+ case 3:
+ /* XXX: For now we support only a single FPU context. */
+ gen_helper_1i(cfc1, t0, rt);
+ break;
+ /* COP2: Not implemented. */
+ case 4:
+ case 5:
+ /* fall through */
+ default:
+ goto die;
+ }
+ LOG_DISAS("mftr (reg %d u %d sel %d h %d)\n", rt, u, sel, h);
+ gen_store_gpr(t0, rd);
+ tcg_temp_free(t0);
+ return;
+
+die:
+ tcg_temp_free(t0);
+ LOG_DISAS("mftr (reg %d u %d sel %d h %d)\n", rt, u, sel, h);
+ generate_exception(ctx, EXCP_RI);
+}
+
+static void gen_mttr(CPUState *env, DisasContext *ctx, int rd, int rt,
+ int u, int sel, int h)
+{
+ int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC);
+ TCGv t0 = tcg_temp_local_new();
+
+ gen_load_gpr(t0, rt);
+ if ((env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) == 0 &&
+ ((env->tcs[other_tc].CP0_TCBind & (0xf << CP0TCBd_CurVPE)) !=
+ (env->active_tc.CP0_TCBind & (0xf << CP0TCBd_CurVPE))))
+ /* NOP */ ;
+ else if ((env->CP0_VPEControl & (0xff << CP0VPECo_TargTC)) >
+ (env->mvp->CP0_MVPConf0 & (0xff << CP0MVPC0_PTC)))
+ /* NOP */ ;
+ else if (u == 0) {
+ switch (rd) {
+ case 2:
+ switch (sel) {
+ case 1:
+ gen_helper_mttc0_tcstatus(t0);
+ break;
+ case 2:
+ gen_helper_mttc0_tcbind(t0);
+ break;
+ case 3:
+ gen_helper_mttc0_tcrestart(t0);
+ break;
+ case 4:
+ gen_helper_mttc0_tchalt(t0);
+ break;
+ case 5:
+ gen_helper_mttc0_tccontext(t0);
+ break;
+ case 6:
+ gen_helper_mttc0_tcschedule(t0);
+ break;
+ case 7:
+ gen_helper_mttc0_tcschefback(t0);
+ break;
+ default:
+ gen_mtc0(env, ctx, t0, rd, sel);
+ break;
+ }
+ break;
+ case 10:
+ switch (sel) {
+ case 0:
+ gen_helper_mttc0_entryhi(t0);
+ break;
+ default:
+ gen_mtc0(env, ctx, t0, rd, sel);
+ break;
+ }
+ case 12:
+ switch (sel) {
+ case 0:
+ gen_helper_mttc0_status(t0);
+ break;
+ default:
+ gen_mtc0(env, ctx, t0, rd, sel);
+ break;
+ }
+ case 23:
+ switch (sel) {
+ case 0:
+ gen_helper_mttc0_debug(t0);
+ break;
+ default:
+ gen_mtc0(env, ctx, t0, rd, sel);
+ break;
+ }
+ break;
+ default:
+ gen_mtc0(env, ctx, t0, rd, sel);
+ }
+ } else switch (sel) {
+ /* GPR registers. */
+ case 0:
+ gen_helper_1i(mttgpr, t0, rd);
+ break;
+ /* Auxiliary CPU registers */
+ case 1:
+ switch (rd) {
+ case 0:
+ gen_helper_1i(mttlo, t0, 0);
+ break;
+ case 1:
+ gen_helper_1i(mtthi, t0, 0);
+ break;
+ case 2:
+ gen_helper_1i(mttacx, t0, 0);
+ break;
+ case 4:
+ gen_helper_1i(mttlo, t0, 1);
+ break;
+ case 5:
+ gen_helper_1i(mtthi, t0, 1);
+ break;
+ case 6:
+ gen_helper_1i(mttacx, t0, 1);
+ break;
+ case 8:
+ gen_helper_1i(mttlo, t0, 2);
+ break;
+ case 9:
+ gen_helper_1i(mtthi, t0, 2);
+ break;
+ case 10:
+ gen_helper_1i(mttacx, t0, 2);
+ break;
+ case 12:
+ gen_helper_1i(mttlo, t0, 3);
+ break;
+ case 13:
+ gen_helper_1i(mtthi, t0, 3);
+ break;
+ case 14:
+ gen_helper_1i(mttacx, t0, 3);
+ break;
+ case 16:
+ gen_helper_mttdsp(t0);
+ break;
+ default:
+ goto die;
+ }
+ break;
+ /* Floating point (COP1). */
+ case 2:
+ /* XXX: For now we support only a single FPU context. */
+ if (h == 0) {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(fp0, t0);
+ gen_store_fpr32(fp0, rd);
+ tcg_temp_free_i32(fp0);
+ } else {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(fp0, t0);
+ gen_store_fpr32h(fp0, rd);
+ tcg_temp_free_i32(fp0);
+ }
+ break;
+ case 3:
+ /* XXX: For now we support only a single FPU context. */
+ gen_helper_1i(ctc1, t0, rd);
+ break;
+ /* COP2: Not implemented. */
+ case 4:
+ case 5:
+ /* fall through */
+ default:
+ goto die;
+ }
+ LOG_DISAS("mttr (reg %d u %d sel %d h %d)\n", rd, u, sel, h);
+ tcg_temp_free(t0);
+ return;
+
+die:
+ tcg_temp_free(t0);
+ LOG_DISAS("mttr (reg %d u %d sel %d h %d)\n", rd, u, sel, h);
+ generate_exception(ctx, EXCP_RI);
+}
+
+static void gen_cp0 (CPUState *env, DisasContext *ctx, uint32_t opc, int rt, int rd)
+{
+ const char *opn = "ldst";
+
+ switch (opc) {
+ case OPC_MFC0:
+ if (rt == 0) {
+ /* Treat as NOP. */
+ return;
+ }
+ gen_mfc0(env, ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7);
+ opn = "mfc0";
+ break;
+ case OPC_MTC0:
+ {
+ TCGv t0 = tcg_temp_new();
+
+ gen_load_gpr(t0, rt);
+ gen_mtc0(env, ctx, t0, rd, ctx->opcode & 0x7);
+ tcg_temp_free(t0);
+ }
+ opn = "mtc0";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DMFC0:
+ check_insn(env, ctx, ISA_MIPS3);
+ if (rt == 0) {
+ /* Treat as NOP. */
+ return;
+ }
+ gen_dmfc0(env, ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7);
+ opn = "dmfc0";
+ break;
+ case OPC_DMTC0:
+ check_insn(env, ctx, ISA_MIPS3);
+ {
+ TCGv t0 = tcg_temp_new();
+
+ gen_load_gpr(t0, rt);
+ gen_dmtc0(env, ctx, t0, rd, ctx->opcode & 0x7);
+ tcg_temp_free(t0);
+ }
+ opn = "dmtc0";
+ break;
+#endif
+ case OPC_MFTR:
+ check_insn(env, ctx, ASE_MT);
+ if (rd == 0) {
+ /* Treat as NOP. */
+ return;
+ }
+ gen_mftr(env, ctx, rt, rd, (ctx->opcode >> 5) & 1,
+ ctx->opcode & 0x7, (ctx->opcode >> 4) & 1);
+ opn = "mftr";
+ break;
+ case OPC_MTTR:
+ check_insn(env, ctx, ASE_MT);
+ gen_mttr(env, ctx, rd, rt, (ctx->opcode >> 5) & 1,
+ ctx->opcode & 0x7, (ctx->opcode >> 4) & 1);
+ opn = "mttr";
+ break;
+ case OPC_TLBWI:
+ opn = "tlbwi";
+ if (!env->tlb->helper_tlbwi)
+ goto die;
+ gen_helper_tlbwi();
+ break;
+ case OPC_TLBWR:
+ opn = "tlbwr";
+ if (!env->tlb->helper_tlbwr)
+ goto die;
+ gen_helper_tlbwr();
+ break;
+ case OPC_TLBP:
+ opn = "tlbp";
+ if (!env->tlb->helper_tlbp)
+ goto die;
+ gen_helper_tlbp();
+ break;
+ case OPC_TLBR:
+ opn = "tlbr";
+ if (!env->tlb->helper_tlbr)
+ goto die;
+ gen_helper_tlbr();
+ break;
+ case OPC_ERET:
+ opn = "eret";
+ check_insn(env, ctx, ISA_MIPS2);
+ gen_helper_eret();
+ ctx->bstate = BS_EXCP;
+ break;
+ case OPC_DERET:
+ opn = "deret";
+ check_insn(env, ctx, ISA_MIPS32);
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ MIPS_INVAL(opn);
+ generate_exception(ctx, EXCP_RI);
+ } else {
+ gen_helper_deret();
+ ctx->bstate = BS_EXCP;
+ }
+ break;
+ case OPC_WAIT:
+ opn = "wait";
+ check_insn(env, ctx, ISA_MIPS3 | ISA_MIPS32);
+ /* If we get an exception, we want to restart at next instruction */
+ ctx->pc += 4;
+ save_cpu_state(ctx, 1);
+ ctx->pc -= 4;
+ gen_helper_wait();
+ ctx->bstate = BS_EXCP;
+ break;
+ default:
+ die:
+ MIPS_INVAL(opn);
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd);
+}
+#endif /* !CONFIG_USER_ONLY */
+
+/* CP1 Branches (before delay slot) */
+static void gen_compute_branch1 (CPUState *env, DisasContext *ctx, uint32_t op,
+ int32_t cc, int32_t offset)
+{
+ target_ulong btarget;
+ const char *opn = "cp1 cond branch";
+ TCGv_i32 t0 = tcg_temp_new_i32();
+
+ if (cc != 0)
+ check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32);
+
+ btarget = ctx->pc + 4 + offset;
+
+ switch (op) {
+ case OPC_BC1F:
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_not_i32(t0, t0);
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ opn = "bc1f";
+ goto not_likely;
+ case OPC_BC1FL:
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_not_i32(t0, t0);
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ opn = "bc1fl";
+ goto likely;
+ case OPC_BC1T:
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ opn = "bc1t";
+ goto not_likely;
+ case OPC_BC1TL:
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ opn = "bc1tl";
+ likely:
+ ctx->hflags |= MIPS_HFLAG_BL;
+ break;
+ case OPC_BC1FANY2:
+ {
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1));
+ tcg_gen_nor_i32(t0, t0, t1);
+ tcg_temp_free_i32(t1);
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ }
+ opn = "bc1any2f";
+ goto not_likely;
+ case OPC_BC1TANY2:
+ {
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1));
+ tcg_gen_or_i32(t0, t0, t1);
+ tcg_temp_free_i32(t1);
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ }
+ opn = "bc1any2t";
+ goto not_likely;
+ case OPC_BC1FANY4:
+ {
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1));
+ tcg_gen_or_i32(t0, t0, t1);
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+2));
+ tcg_gen_or_i32(t0, t0, t1);
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+3));
+ tcg_gen_nor_i32(t0, t0, t1);
+ tcg_temp_free_i32(t1);
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ }
+ opn = "bc1any4f";
+ goto not_likely;
+ case OPC_BC1TANY4:
+ {
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc));
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1));
+ tcg_gen_or_i32(t0, t0, t1);
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+2));
+ tcg_gen_or_i32(t0, t0, t1);
+ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+3));
+ tcg_gen_or_i32(t0, t0, t1);
+ tcg_temp_free_i32(t1);
+ tcg_gen_andi_i32(t0, t0, 1);
+ tcg_gen_extu_i32_tl(bcond, t0);
+ }
+ opn = "bc1any4t";
+ not_likely:
+ ctx->hflags |= MIPS_HFLAG_BC;
+ break;
+ default:
+ MIPS_INVAL(opn);
+ generate_exception (ctx, EXCP_RI);
+ goto out;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn,
+ ctx->hflags, btarget);
+ ctx->btarget = btarget;
+
+ out:
+ tcg_temp_free_i32(t0);
+}
+
+/* Coprocessor 1 (FPU) */
+
+#define FOP(func, fmt) (((fmt) << 21) | (func))
+
+enum fopcode {
+ OPC_ADD_S = FOP(0, FMT_S),
+ OPC_SUB_S = FOP(1, FMT_S),
+ OPC_MUL_S = FOP(2, FMT_S),
+ OPC_DIV_S = FOP(3, FMT_S),
+ OPC_SQRT_S = FOP(4, FMT_S),
+ OPC_ABS_S = FOP(5, FMT_S),
+ OPC_MOV_S = FOP(6, FMT_S),
+ OPC_NEG_S = FOP(7, FMT_S),
+ OPC_ROUND_L_S = FOP(8, FMT_S),
+ OPC_TRUNC_L_S = FOP(9, FMT_S),
+ OPC_CEIL_L_S = FOP(10, FMT_S),
+ OPC_FLOOR_L_S = FOP(11, FMT_S),
+ OPC_ROUND_W_S = FOP(12, FMT_S),
+ OPC_TRUNC_W_S = FOP(13, FMT_S),
+ OPC_CEIL_W_S = FOP(14, FMT_S),
+ OPC_FLOOR_W_S = FOP(15, FMT_S),
+ OPC_MOVCF_S = FOP(17, FMT_S),
+ OPC_MOVZ_S = FOP(18, FMT_S),
+ OPC_MOVN_S = FOP(19, FMT_S),
+ OPC_RECIP_S = FOP(21, FMT_S),
+ OPC_RSQRT_S = FOP(22, FMT_S),
+ OPC_RECIP2_S = FOP(28, FMT_S),
+ OPC_RECIP1_S = FOP(29, FMT_S),
+ OPC_RSQRT1_S = FOP(30, FMT_S),
+ OPC_RSQRT2_S = FOP(31, FMT_S),
+ OPC_CVT_D_S = FOP(33, FMT_S),
+ OPC_CVT_W_S = FOP(36, FMT_S),
+ OPC_CVT_L_S = FOP(37, FMT_S),
+ OPC_CVT_PS_S = FOP(38, FMT_S),
+ OPC_CMP_F_S = FOP (48, FMT_S),
+ OPC_CMP_UN_S = FOP (49, FMT_S),
+ OPC_CMP_EQ_S = FOP (50, FMT_S),
+ OPC_CMP_UEQ_S = FOP (51, FMT_S),
+ OPC_CMP_OLT_S = FOP (52, FMT_S),
+ OPC_CMP_ULT_S = FOP (53, FMT_S),
+ OPC_CMP_OLE_S = FOP (54, FMT_S),
+ OPC_CMP_ULE_S = FOP (55, FMT_S),
+ OPC_CMP_SF_S = FOP (56, FMT_S),
+ OPC_CMP_NGLE_S = FOP (57, FMT_S),
+ OPC_CMP_SEQ_S = FOP (58, FMT_S),
+ OPC_CMP_NGL_S = FOP (59, FMT_S),
+ OPC_CMP_LT_S = FOP (60, FMT_S),
+ OPC_CMP_NGE_S = FOP (61, FMT_S),
+ OPC_CMP_LE_S = FOP (62, FMT_S),
+ OPC_CMP_NGT_S = FOP (63, FMT_S),
+
+ OPC_ADD_D = FOP(0, FMT_D),
+ OPC_SUB_D = FOP(1, FMT_D),
+ OPC_MUL_D = FOP(2, FMT_D),
+ OPC_DIV_D = FOP(3, FMT_D),
+ OPC_SQRT_D = FOP(4, FMT_D),
+ OPC_ABS_D = FOP(5, FMT_D),
+ OPC_MOV_D = FOP(6, FMT_D),
+ OPC_NEG_D = FOP(7, FMT_D),
+ OPC_ROUND_L_D = FOP(8, FMT_D),
+ OPC_TRUNC_L_D = FOP(9, FMT_D),
+ OPC_CEIL_L_D = FOP(10, FMT_D),
+ OPC_FLOOR_L_D = FOP(11, FMT_D),
+ OPC_ROUND_W_D = FOP(12, FMT_D),
+ OPC_TRUNC_W_D = FOP(13, FMT_D),
+ OPC_CEIL_W_D = FOP(14, FMT_D),
+ OPC_FLOOR_W_D = FOP(15, FMT_D),
+ OPC_MOVCF_D = FOP(17, FMT_D),
+ OPC_MOVZ_D = FOP(18, FMT_D),
+ OPC_MOVN_D = FOP(19, FMT_D),
+ OPC_RECIP_D = FOP(21, FMT_D),
+ OPC_RSQRT_D = FOP(22, FMT_D),
+ OPC_RECIP2_D = FOP(28, FMT_D),
+ OPC_RECIP1_D = FOP(29, FMT_D),
+ OPC_RSQRT1_D = FOP(30, FMT_D),
+ OPC_RSQRT2_D = FOP(31, FMT_D),
+ OPC_CVT_S_D = FOP(32, FMT_D),
+ OPC_CVT_W_D = FOP(36, FMT_D),
+ OPC_CVT_L_D = FOP(37, FMT_D),
+ OPC_CMP_F_D = FOP (48, FMT_D),
+ OPC_CMP_UN_D = FOP (49, FMT_D),
+ OPC_CMP_EQ_D = FOP (50, FMT_D),
+ OPC_CMP_UEQ_D = FOP (51, FMT_D),
+ OPC_CMP_OLT_D = FOP (52, FMT_D),
+ OPC_CMP_ULT_D = FOP (53, FMT_D),
+ OPC_CMP_OLE_D = FOP (54, FMT_D),
+ OPC_CMP_ULE_D = FOP (55, FMT_D),
+ OPC_CMP_SF_D = FOP (56, FMT_D),
+ OPC_CMP_NGLE_D = FOP (57, FMT_D),
+ OPC_CMP_SEQ_D = FOP (58, FMT_D),
+ OPC_CMP_NGL_D = FOP (59, FMT_D),
+ OPC_CMP_LT_D = FOP (60, FMT_D),
+ OPC_CMP_NGE_D = FOP (61, FMT_D),
+ OPC_CMP_LE_D = FOP (62, FMT_D),
+ OPC_CMP_NGT_D = FOP (63, FMT_D),
+
+ OPC_CVT_S_W = FOP(32, FMT_W),
+ OPC_CVT_D_W = FOP(33, FMT_W),
+ OPC_CVT_S_L = FOP(32, FMT_L),
+ OPC_CVT_D_L = FOP(33, FMT_L),
+ OPC_CVT_PS_PW = FOP(38, FMT_W),
+
+ OPC_ADD_PS = FOP(0, FMT_PS),
+ OPC_SUB_PS = FOP(1, FMT_PS),
+ OPC_MUL_PS = FOP(2, FMT_PS),
+ OPC_DIV_PS = FOP(3, FMT_PS),
+ OPC_ABS_PS = FOP(5, FMT_PS),
+ OPC_MOV_PS = FOP(6, FMT_PS),
+ OPC_NEG_PS = FOP(7, FMT_PS),
+ OPC_MOVCF_PS = FOP(17, FMT_PS),
+ OPC_MOVZ_PS = FOP(18, FMT_PS),
+ OPC_MOVN_PS = FOP(19, FMT_PS),
+ OPC_ADDR_PS = FOP(24, FMT_PS),
+ OPC_MULR_PS = FOP(26, FMT_PS),
+ OPC_RECIP2_PS = FOP(28, FMT_PS),
+ OPC_RECIP1_PS = FOP(29, FMT_PS),
+ OPC_RSQRT1_PS = FOP(30, FMT_PS),
+ OPC_RSQRT2_PS = FOP(31, FMT_PS),
+
+ OPC_CVT_S_PU = FOP(32, FMT_PS),
+ OPC_CVT_PW_PS = FOP(36, FMT_PS),
+ OPC_CVT_S_PL = FOP(40, FMT_PS),
+ OPC_PLL_PS = FOP(44, FMT_PS),
+ OPC_PLU_PS = FOP(45, FMT_PS),
+ OPC_PUL_PS = FOP(46, FMT_PS),
+ OPC_PUU_PS = FOP(47, FMT_PS),
+ OPC_CMP_F_PS = FOP (48, FMT_PS),
+ OPC_CMP_UN_PS = FOP (49, FMT_PS),
+ OPC_CMP_EQ_PS = FOP (50, FMT_PS),
+ OPC_CMP_UEQ_PS = FOP (51, FMT_PS),
+ OPC_CMP_OLT_PS = FOP (52, FMT_PS),
+ OPC_CMP_ULT_PS = FOP (53, FMT_PS),
+ OPC_CMP_OLE_PS = FOP (54, FMT_PS),
+ OPC_CMP_ULE_PS = FOP (55, FMT_PS),
+ OPC_CMP_SF_PS = FOP (56, FMT_PS),
+ OPC_CMP_NGLE_PS = FOP (57, FMT_PS),
+ OPC_CMP_SEQ_PS = FOP (58, FMT_PS),
+ OPC_CMP_NGL_PS = FOP (59, FMT_PS),
+ OPC_CMP_LT_PS = FOP (60, FMT_PS),
+ OPC_CMP_NGE_PS = FOP (61, FMT_PS),
+ OPC_CMP_LE_PS = FOP (62, FMT_PS),
+ OPC_CMP_NGT_PS = FOP (63, FMT_PS),
+};
+
+static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs)
+{
+ const char *opn = "cp1 move";
+ TCGv t0 = tcg_temp_new();
+
+ switch (opc) {
+ case OPC_MFC1:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ tcg_gen_ext_i32_tl(t0, fp0);
+ tcg_temp_free_i32(fp0);
+ }
+ gen_store_gpr(t0, rt);
+ opn = "mfc1";
+ break;
+ case OPC_MTC1:
+ gen_load_gpr(t0, rt);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(fp0, t0);
+ gen_store_fpr32(fp0, fs);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "mtc1";
+ break;
+ case OPC_CFC1:
+ gen_helper_1i(cfc1, t0, fs);
+ gen_store_gpr(t0, rt);
+ opn = "cfc1";
+ break;
+ case OPC_CTC1:
+ gen_load_gpr(t0, rt);
+ gen_helper_1i(ctc1, t0, fs);
+ opn = "ctc1";
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DMFC1:
+ gen_load_fpr64(ctx, t0, fs);
+ gen_store_gpr(t0, rt);
+ opn = "dmfc1";
+ break;
+ case OPC_DMTC1:
+ gen_load_gpr(t0, rt);
+ gen_store_fpr64(ctx, t0, fs);
+ opn = "dmtc1";
+ break;
+#endif
+ case OPC_MFHC1:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32h(fp0, fs);
+ tcg_gen_ext_i32_tl(t0, fp0);
+ tcg_temp_free_i32(fp0);
+ }
+ gen_store_gpr(t0, rt);
+ opn = "mfhc1";
+ break;
+ case OPC_MTHC1:
+ gen_load_gpr(t0, rt);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(fp0, t0);
+ gen_store_fpr32h(fp0, fs);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "mthc1";
+ break;
+ default:
+ MIPS_INVAL(opn);
+ generate_exception (ctx, EXCP_RI);
+ goto out;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]);
+
+ out:
+ tcg_temp_free(t0);
+}
+
+static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf)
+{
+ int l1;
+ TCGCond cond;
+ TCGv_i32 t0;
+
+ if (rd == 0) {
+ /* Treat as NOP. */
+ return;
+ }
+
+ if (tf)
+ cond = TCG_COND_EQ;
+ else
+ cond = TCG_COND_NE;
+
+ l1 = gen_new_label();
+ t0 = tcg_temp_new_i32();
+ tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc));
+ tcg_gen_brcondi_i32(cond, t0, 0, l1);
+ tcg_temp_free_i32(t0);
+ if (rs == 0) {
+ tcg_gen_movi_tl(cpu_gpr[rd], 0);
+ } else {
+ tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]);
+ }
+ gen_set_label(l1);
+}
+
+static inline void gen_movcf_s (int fs, int fd, int cc, int tf)
+{
+ int cond;
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ int l1 = gen_new_label();
+
+ if (tf)
+ cond = TCG_COND_EQ;
+ else
+ cond = TCG_COND_NE;
+
+ tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc));
+ tcg_gen_brcondi_i32(cond, t0, 0, l1);
+ gen_load_fpr32(t0, fs);
+ gen_store_fpr32(t0, fd);
+ gen_set_label(l1);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void gen_movcf_d (DisasContext *ctx, int fs, int fd, int cc, int tf)
+{
+ int cond;
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv_i64 fp0;
+ int l1 = gen_new_label();
+
+ if (tf)
+ cond = TCG_COND_EQ;
+ else
+ cond = TCG_COND_NE;
+
+ tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc));
+ tcg_gen_brcondi_i32(cond, t0, 0, l1);
+ tcg_temp_free_i32(t0);
+ fp0 = tcg_temp_new_i64();
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ gen_set_label(l1);
+}
+
+static inline void gen_movcf_ps (int fs, int fd, int cc, int tf)
+{
+ int cond;
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+
+ if (tf)
+ cond = TCG_COND_EQ;
+ else
+ cond = TCG_COND_NE;
+
+ tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc));
+ tcg_gen_brcondi_i32(cond, t0, 0, l1);
+ gen_load_fpr32(t0, fs);
+ gen_store_fpr32(t0, fd);
+ gen_set_label(l1);
+
+ tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc+1));
+ tcg_gen_brcondi_i32(cond, t0, 0, l2);
+ gen_load_fpr32h(t0, fs);
+ gen_store_fpr32h(t0, fd);
+ tcg_temp_free_i32(t0);
+ gen_set_label(l2);
+}
+
+
+static void gen_farith (DisasContext *ctx, enum fopcode op1,
+ int ft, int fs, int fd, int cc)
+{
+ const char *opn = "farith";
+ const char *condnames[] = {
+ "c.f",
+ "c.un",
+ "c.eq",
+ "c.ueq",
+ "c.olt",
+ "c.ult",
+ "c.ole",
+ "c.ule",
+ "c.sf",
+ "c.ngle",
+ "c.seq",
+ "c.ngl",
+ "c.lt",
+ "c.nge",
+ "c.le",
+ "c.ngt",
+ };
+ const char *condnames_abs[] = {
+ "cabs.f",
+ "cabs.un",
+ "cabs.eq",
+ "cabs.ueq",
+ "cabs.olt",
+ "cabs.ult",
+ "cabs.ole",
+ "cabs.ule",
+ "cabs.sf",
+ "cabs.ngle",
+ "cabs.seq",
+ "cabs.ngl",
+ "cabs.lt",
+ "cabs.nge",
+ "cabs.le",
+ "cabs.ngt",
+ };
+ enum { BINOP, CMPOP, OTHEROP } optype = OTHEROP;
+ uint32_t func = ctx->opcode & 0x3f;
+
+ switch (op1) {
+ case OPC_ADD_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_helper_float_add_s(fp0, fp0, fp1);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "add.s";
+ optype = BINOP;
+ break;
+ case OPC_SUB_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_helper_float_sub_s(fp0, fp0, fp1);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "sub.s";
+ optype = BINOP;
+ break;
+ case OPC_MUL_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_helper_float_mul_s(fp0, fp0, fp1);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "mul.s";
+ optype = BINOP;
+ break;
+ case OPC_DIV_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_helper_float_div_s(fp0, fp0, fp1);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "div.s";
+ optype = BINOP;
+ break;
+ case OPC_SQRT_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_sqrt_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "sqrt.s";
+ break;
+ case OPC_ABS_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_abs_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "abs.s";
+ break;
+ case OPC_MOV_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "mov.s";
+ break;
+ case OPC_NEG_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_chs_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "neg.s";
+ break;
+ case OPC_ROUND_L_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_roundl_s(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "round.l.s";
+ break;
+ case OPC_TRUNC_L_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_truncl_s(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "trunc.l.s";
+ break;
+ case OPC_CEIL_L_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_ceill_s(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "ceil.l.s";
+ break;
+ case OPC_FLOOR_L_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_floorl_s(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "floor.l.s";
+ break;
+ case OPC_ROUND_W_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_roundw_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "round.w.s";
+ break;
+ case OPC_TRUNC_W_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_truncw_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "trunc.w.s";
+ break;
+ case OPC_CEIL_W_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_ceilw_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "ceil.w.s";
+ break;
+ case OPC_FLOOR_W_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_floorw_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "floor.w.s";
+ break;
+ case OPC_MOVCF_S:
+ gen_movcf_s(fs, fd, (ft >> 2) & 0x7, ft & 0x1);
+ opn = "movcf.s";
+ break;
+ case OPC_MOVZ_S:
+ {
+ int l1 = gen_new_label();
+ TCGv_i32 fp0;
+
+ if (ft != 0) {
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1);
+ }
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(fp0, fs);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ gen_set_label(l1);
+ }
+ opn = "movz.s";
+ break;
+ case OPC_MOVN_S:
+ {
+ int l1 = gen_new_label();
+ TCGv_i32 fp0;
+
+ if (ft != 0) {
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1);
+ fp0 = tcg_temp_new_i32();
+ gen_load_fpr32(fp0, fs);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ gen_set_label(l1);
+ }
+ }
+ opn = "movn.s";
+ break;
+ case OPC_RECIP_S:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_recip_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "recip.s";
+ break;
+ case OPC_RSQRT_S:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_rsqrt_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "rsqrt.s";
+ break;
+ case OPC_RECIP2_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, fd);
+ gen_helper_float_recip2_s(fp0, fp0, fp1);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "recip2.s";
+ break;
+ case OPC_RECIP1_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_recip1_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "recip1.s";
+ break;
+ case OPC_RSQRT1_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_rsqrt1_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "rsqrt1.s";
+ break;
+ case OPC_RSQRT2_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_helper_float_rsqrt2_s(fp0, fp0, fp1);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "rsqrt2.s";
+ break;
+ case OPC_CVT_D_S:
+ check_cp1_registers(ctx, fd);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_cvtd_s(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "cvt.d.s";
+ break;
+ case OPC_CVT_W_S:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_cvtw_s(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "cvt.w.s";
+ break;
+ case OPC_CVT_L_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_cvtl_s(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "cvt.l.s";
+ break;
+ case OPC_CVT_PS_S:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+ TCGv_i32 fp32_0 = tcg_temp_new_i32();
+ TCGv_i32 fp32_1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp32_0, fs);
+ gen_load_fpr32(fp32_1, ft);
+ tcg_gen_concat_i32_i64(fp64, fp32_0, fp32_1);
+ tcg_temp_free_i32(fp32_1);
+ tcg_temp_free_i32(fp32_0);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "cvt.ps.s";
+ break;
+ case OPC_CMP_F_S:
+ case OPC_CMP_UN_S:
+ case OPC_CMP_EQ_S:
+ case OPC_CMP_UEQ_S:
+ case OPC_CMP_OLT_S:
+ case OPC_CMP_ULT_S:
+ case OPC_CMP_OLE_S:
+ case OPC_CMP_ULE_S:
+ case OPC_CMP_SF_S:
+ case OPC_CMP_NGLE_S:
+ case OPC_CMP_SEQ_S:
+ case OPC_CMP_NGL_S:
+ case OPC_CMP_LT_S:
+ case OPC_CMP_NGE_S:
+ case OPC_CMP_LE_S:
+ case OPC_CMP_NGT_S:
+ if (ctx->opcode & (1 << 6)) {
+ gen_cmpabs_s(ctx, func-48, ft, fs, cc);
+ opn = condnames_abs[func-48];
+ } else {
+ gen_cmp_s(ctx, func-48, ft, fs, cc);
+ opn = condnames[func-48];
+ }
+ break;
+ case OPC_ADD_D:
+ check_cp1_registers(ctx, fs | ft | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_add_d(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "add.d";
+ optype = BINOP;
+ break;
+ case OPC_SUB_D:
+ check_cp1_registers(ctx, fs | ft | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_sub_d(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "sub.d";
+ optype = BINOP;
+ break;
+ case OPC_MUL_D:
+ check_cp1_registers(ctx, fs | ft | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_mul_d(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "mul.d";
+ optype = BINOP;
+ break;
+ case OPC_DIV_D:
+ check_cp1_registers(ctx, fs | ft | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_div_d(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "div.d";
+ optype = BINOP;
+ break;
+ case OPC_SQRT_D:
+ check_cp1_registers(ctx, fs | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_sqrt_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "sqrt.d";
+ break;
+ case OPC_ABS_D:
+ check_cp1_registers(ctx, fs | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_abs_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "abs.d";
+ break;
+ case OPC_MOV_D:
+ check_cp1_registers(ctx, fs | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "mov.d";
+ break;
+ case OPC_NEG_D:
+ check_cp1_registers(ctx, fs | fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_chs_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "neg.d";
+ break;
+ case OPC_ROUND_L_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_roundl_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "round.l.d";
+ break;
+ case OPC_TRUNC_L_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_truncl_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "trunc.l.d";
+ break;
+ case OPC_CEIL_L_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_ceill_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "ceil.l.d";
+ break;
+ case OPC_FLOOR_L_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_floorl_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "floor.l.d";
+ break;
+ case OPC_ROUND_W_D:
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_roundw_d(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "round.w.d";
+ break;
+ case OPC_TRUNC_W_D:
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_truncw_d(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "trunc.w.d";
+ break;
+ case OPC_CEIL_W_D:
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_ceilw_d(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "ceil.w.d";
+ break;
+ case OPC_FLOOR_W_D:
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_floorw_d(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "floor.w.d";
+ break;
+ case OPC_MOVCF_D:
+ gen_movcf_d(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1);
+ opn = "movcf.d";
+ break;
+ case OPC_MOVZ_D:
+ {
+ int l1 = gen_new_label();
+ TCGv_i64 fp0;
+
+ if (ft != 0) {
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1);
+ }
+ fp0 = tcg_temp_new_i64();
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ gen_set_label(l1);
+ }
+ opn = "movz.d";
+ break;
+ case OPC_MOVN_D:
+ {
+ int l1 = gen_new_label();
+ TCGv_i64 fp0;
+
+ if (ft != 0) {
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1);
+ fp0 = tcg_temp_new_i64();
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ gen_set_label(l1);
+ }
+ }
+ opn = "movn.d";
+ break;
+ case OPC_RECIP_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_recip_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "recip.d";
+ break;
+ case OPC_RSQRT_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_rsqrt_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "rsqrt.d";
+ break;
+ case OPC_RECIP2_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_recip2_d(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "recip2.d";
+ break;
+ case OPC_RECIP1_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_recip1_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "recip1.d";
+ break;
+ case OPC_RSQRT1_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_rsqrt1_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "rsqrt1.d";
+ break;
+ case OPC_RSQRT2_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_rsqrt2_d(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "rsqrt2.d";
+ break;
+ case OPC_CMP_F_D:
+ case OPC_CMP_UN_D:
+ case OPC_CMP_EQ_D:
+ case OPC_CMP_UEQ_D:
+ case OPC_CMP_OLT_D:
+ case OPC_CMP_ULT_D:
+ case OPC_CMP_OLE_D:
+ case OPC_CMP_ULE_D:
+ case OPC_CMP_SF_D:
+ case OPC_CMP_NGLE_D:
+ case OPC_CMP_SEQ_D:
+ case OPC_CMP_NGL_D:
+ case OPC_CMP_LT_D:
+ case OPC_CMP_NGE_D:
+ case OPC_CMP_LE_D:
+ case OPC_CMP_NGT_D:
+ if (ctx->opcode & (1 << 6)) {
+ gen_cmpabs_d(ctx, func-48, ft, fs, cc);
+ opn = condnames_abs[func-48];
+ } else {
+ gen_cmp_d(ctx, func-48, ft, fs, cc);
+ opn = condnames[func-48];
+ }
+ break;
+ case OPC_CVT_S_D:
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_cvts_d(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "cvt.s.d";
+ break;
+ case OPC_CVT_W_D:
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_cvtw_d(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "cvt.w.d";
+ break;
+ case OPC_CVT_L_D:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_cvtl_d(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "cvt.l.d";
+ break;
+ case OPC_CVT_S_W:
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_cvts_w(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "cvt.s.w";
+ break;
+ case OPC_CVT_D_W:
+ check_cp1_registers(ctx, fd);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr32(fp32, fs);
+ gen_helper_float_cvtd_w(fp64, fp32);
+ tcg_temp_free_i32(fp32);
+ gen_store_fpr64(ctx, fp64, fd);
+ tcg_temp_free_i64(fp64);
+ }
+ opn = "cvt.d.w";
+ break;
+ case OPC_CVT_S_L:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp32 = tcg_temp_new_i32();
+ TCGv_i64 fp64 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp64, fs);
+ gen_helper_float_cvts_l(fp32, fp64);
+ tcg_temp_free_i64(fp64);
+ gen_store_fpr32(fp32, fd);
+ tcg_temp_free_i32(fp32);
+ }
+ opn = "cvt.s.l";
+ break;
+ case OPC_CVT_D_L:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_cvtd_l(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "cvt.d.l";
+ break;
+ case OPC_CVT_PS_PW:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_cvtps_pw(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "cvt.ps.pw";
+ break;
+ case OPC_ADD_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_add_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "add.ps";
+ break;
+ case OPC_SUB_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_sub_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "sub.ps";
+ break;
+ case OPC_MUL_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_mul_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "mul.ps";
+ break;
+ case OPC_ABS_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_abs_ps(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "abs.ps";
+ break;
+ case OPC_MOV_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "mov.ps";
+ break;
+ case OPC_NEG_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_chs_ps(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "neg.ps";
+ break;
+ case OPC_MOVCF_PS:
+ check_cp1_64bitmode(ctx);
+ gen_movcf_ps(fs, fd, (ft >> 2) & 0x7, ft & 0x1);
+ opn = "movcf.ps";
+ break;
+ case OPC_MOVZ_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ int l1 = gen_new_label();
+ TCGv_i64 fp0;
+
+ if (ft != 0)
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1);
+ fp0 = tcg_temp_new_i64();
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ gen_set_label(l1);
+ }
+ opn = "movz.ps";
+ break;
+ case OPC_MOVN_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ int l1 = gen_new_label();
+ TCGv_i64 fp0;
+
+ if (ft != 0) {
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1);
+ fp0 = tcg_temp_new_i64();
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ gen_set_label(l1);
+ }
+ }
+ opn = "movn.ps";
+ break;
+ case OPC_ADDR_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, ft);
+ gen_load_fpr64(ctx, fp1, fs);
+ gen_helper_float_addr_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "addr.ps";
+ break;
+ case OPC_MULR_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, ft);
+ gen_load_fpr64(ctx, fp1, fs);
+ gen_helper_float_mulr_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "mulr.ps";
+ break;
+ case OPC_RECIP2_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, fd);
+ gen_helper_float_recip2_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "recip2.ps";
+ break;
+ case OPC_RECIP1_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_recip1_ps(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "recip1.ps";
+ break;
+ case OPC_RSQRT1_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_rsqrt1_ps(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "rsqrt1.ps";
+ break;
+ case OPC_RSQRT2_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_helper_float_rsqrt2_ps(fp0, fp0, fp1);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "rsqrt2.ps";
+ break;
+ case OPC_CVT_S_PU:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32h(fp0, fs);
+ gen_helper_float_cvts_pu(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "cvt.s.pu";
+ break;
+ case OPC_CVT_PW_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_helper_float_cvtpw_ps(fp0, fp0);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "cvt.pw.ps";
+ break;
+ case OPC_CVT_S_PL:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_helper_float_cvts_pl(fp0, fp0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "cvt.s.pl";
+ break;
+ case OPC_PLL_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_store_fpr32h(fp0, fd);
+ gen_store_fpr32(fp1, fd);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ }
+ opn = "pll.ps";
+ break;
+ case OPC_PLU_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32h(fp1, ft);
+ gen_store_fpr32(fp1, fd);
+ gen_store_fpr32h(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ }
+ opn = "plu.ps";
+ break;
+ case OPC_PUL_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32h(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_store_fpr32(fp1, fd);
+ gen_store_fpr32h(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ }
+ opn = "pul.ps";
+ break;
+ case OPC_PUU_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+
+ gen_load_fpr32h(fp0, fs);
+ gen_load_fpr32h(fp1, ft);
+ gen_store_fpr32(fp1, fd);
+ gen_store_fpr32h(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ }
+ opn = "puu.ps";
+ break;
+ case OPC_CMP_F_PS:
+ case OPC_CMP_UN_PS:
+ case OPC_CMP_EQ_PS:
+ case OPC_CMP_UEQ_PS:
+ case OPC_CMP_OLT_PS:
+ case OPC_CMP_ULT_PS:
+ case OPC_CMP_OLE_PS:
+ case OPC_CMP_ULE_PS:
+ case OPC_CMP_SF_PS:
+ case OPC_CMP_NGLE_PS:
+ case OPC_CMP_SEQ_PS:
+ case OPC_CMP_NGL_PS:
+ case OPC_CMP_LT_PS:
+ case OPC_CMP_NGE_PS:
+ case OPC_CMP_LE_PS:
+ case OPC_CMP_NGT_PS:
+ if (ctx->opcode & (1 << 6)) {
+ gen_cmpabs_ps(ctx, func-48, ft, fs, cc);
+ opn = condnames_abs[func-48];
+ } else {
+ gen_cmp_ps(ctx, func-48, ft, fs, cc);
+ opn = condnames[func-48];
+ }
+ break;
+ default:
+ MIPS_INVAL(opn);
+ generate_exception (ctx, EXCP_RI);
+ return;
+ }
+ (void)opn; /* avoid a compiler warning */
+ switch (optype) {
+ case BINOP:
+ MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]);
+ break;
+ case CMPOP:
+ MIPS_DEBUG("%s %s,%s", opn, fregnames[fs], fregnames[ft]);
+ break;
+ default:
+ MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]);
+ break;
+ }
+}
+
+/* Coprocessor 3 (FPU) */
+static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc,
+ int fd, int fs, int base, int index)
+{
+ const char *opn = "extended float load/store";
+ int store = 0;
+ TCGv t0 = tcg_temp_new();
+
+ if (base == 0) {
+ gen_load_gpr(t0, index);
+ } else if (index == 0) {
+ gen_load_gpr(t0, base);
+ } else {
+ gen_load_gpr(t0, index);
+ gen_op_addr_add(ctx, t0, cpu_gpr[base], t0);
+ }
+ /* Don't do NOP if destination is zero: we must perform the actual
+ memory access. */
+ save_cpu_state(ctx, 0);
+ switch (opc) {
+ case OPC_LWXC1:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+
+ tcg_gen_qemu_ld32s(t0, t0, ctx->mem_idx);
+ tcg_gen_trunc_tl_i32(fp0, t0);
+ gen_store_fpr32(fp0, fd);
+ tcg_temp_free_i32(fp0);
+ }
+ opn = "lwxc1";
+ break;
+ case OPC_LDXC1:
+ check_cop1x(ctx);
+ check_cp1_registers(ctx, fd);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld64(fp0, t0, ctx->mem_idx);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "ldxc1";
+ break;
+ case OPC_LUXC1:
+ check_cp1_64bitmode(ctx);
+ tcg_gen_andi_tl(t0, t0, ~0x7);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ tcg_gen_qemu_ld64(fp0, t0, ctx->mem_idx);
+ gen_store_fpr64(ctx, fp0, fd);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "luxc1";
+ break;
+ case OPC_SWXC1:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new();
+
+ gen_load_fpr32(fp0, fs);
+ tcg_gen_extu_i32_tl(t1, fp0);
+ tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free(t1);
+ }
+ opn = "swxc1";
+ store = 1;
+ break;
+ case OPC_SDXC1:
+ check_cop1x(ctx);
+ check_cp1_registers(ctx, fs);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ tcg_gen_qemu_st64(fp0, t0, ctx->mem_idx);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "sdxc1";
+ store = 1;
+ break;
+ case OPC_SUXC1:
+ check_cp1_64bitmode(ctx);
+ tcg_gen_andi_tl(t0, t0, ~0x7);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ tcg_gen_qemu_st64(fp0, t0, ctx->mem_idx);
+ tcg_temp_free_i64(fp0);
+ }
+ opn = "suxc1";
+ store = 1;
+ break;
+ }
+ tcg_temp_free(t0);
+ (void)opn; (void)store; /* avoid compiler warnings */
+ MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[store ? fs : fd],
+ regnames[index], regnames[base]);
+}
+
+static void gen_flt3_arith (DisasContext *ctx, uint32_t opc,
+ int fd, int fr, int fs, int ft)
+{
+ const char *opn = "flt3_arith";
+
+ switch (opc) {
+ case OPC_ALNV_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv t0 = tcg_temp_local_new();
+ TCGv_i32 fp = tcg_temp_new_i32();
+ TCGv_i32 fph = tcg_temp_new_i32();
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+
+ gen_load_gpr(t0, fr);
+ tcg_gen_andi_tl(t0, t0, 0x7);
+
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1);
+ gen_load_fpr32(fp, fs);
+ gen_load_fpr32h(fph, fs);
+ gen_store_fpr32(fp, fd);
+ gen_store_fpr32h(fph, fd);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, t0, 4, l2);
+ tcg_temp_free(t0);
+#ifdef TARGET_WORDS_BIGENDIAN
+ gen_load_fpr32(fp, fs);
+ gen_load_fpr32h(fph, ft);
+ gen_store_fpr32h(fp, fd);
+ gen_store_fpr32(fph, fd);
+#else
+ gen_load_fpr32h(fph, fs);
+ gen_load_fpr32(fp, ft);
+ gen_store_fpr32(fph, fd);
+ gen_store_fpr32h(fp, fd);
+#endif
+ gen_set_label(l2);
+ tcg_temp_free_i32(fp);
+ tcg_temp_free_i32(fph);
+ }
+ opn = "alnv.ps";
+ break;
+ case OPC_MADD_S:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+ TCGv_i32 fp2 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(fp2, fr);
+ gen_helper_float_muladd_s(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp2, fd);
+ tcg_temp_free_i32(fp2);
+ }
+ opn = "madd.s";
+ break;
+ case OPC_MADD_D:
+ check_cop1x(ctx);
+ check_cp1_registers(ctx, fd | fs | ft | fr);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_muladd_d(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "madd.d";
+ break;
+ case OPC_MADD_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_muladd_ps(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "madd.ps";
+ break;
+ case OPC_MSUB_S:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+ TCGv_i32 fp2 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(fp2, fr);
+ gen_helper_float_mulsub_s(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp2, fd);
+ tcg_temp_free_i32(fp2);
+ }
+ opn = "msub.s";
+ break;
+ case OPC_MSUB_D:
+ check_cop1x(ctx);
+ check_cp1_registers(ctx, fd | fs | ft | fr);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_mulsub_d(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "msub.d";
+ break;
+ case OPC_MSUB_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_mulsub_ps(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "msub.ps";
+ break;
+ case OPC_NMADD_S:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+ TCGv_i32 fp2 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(fp2, fr);
+ gen_helper_float_nmuladd_s(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp2, fd);
+ tcg_temp_free_i32(fp2);
+ }
+ opn = "nmadd.s";
+ break;
+ case OPC_NMADD_D:
+ check_cop1x(ctx);
+ check_cp1_registers(ctx, fd | fs | ft | fr);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_nmuladd_d(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "nmadd.d";
+ break;
+ case OPC_NMADD_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_nmuladd_ps(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "nmadd.ps";
+ break;
+ case OPC_NMSUB_S:
+ check_cop1x(ctx);
+ {
+ TCGv_i32 fp0 = tcg_temp_new_i32();
+ TCGv_i32 fp1 = tcg_temp_new_i32();
+ TCGv_i32 fp2 = tcg_temp_new_i32();
+
+ gen_load_fpr32(fp0, fs);
+ gen_load_fpr32(fp1, ft);
+ gen_load_fpr32(fp2, fr);
+ gen_helper_float_nmulsub_s(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i32(fp0);
+ tcg_temp_free_i32(fp1);
+ gen_store_fpr32(fp2, fd);
+ tcg_temp_free_i32(fp2);
+ }
+ opn = "nmsub.s";
+ break;
+ case OPC_NMSUB_D:
+ check_cop1x(ctx);
+ check_cp1_registers(ctx, fd | fs | ft | fr);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_nmulsub_d(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "nmsub.d";
+ break;
+ case OPC_NMSUB_PS:
+ check_cp1_64bitmode(ctx);
+ {
+ TCGv_i64 fp0 = tcg_temp_new_i64();
+ TCGv_i64 fp1 = tcg_temp_new_i64();
+ TCGv_i64 fp2 = tcg_temp_new_i64();
+
+ gen_load_fpr64(ctx, fp0, fs);
+ gen_load_fpr64(ctx, fp1, ft);
+ gen_load_fpr64(ctx, fp2, fr);
+ gen_helper_float_nmulsub_ps(fp2, fp0, fp1, fp2);
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ gen_store_fpr64(ctx, fp2, fd);
+ tcg_temp_free_i64(fp2);
+ }
+ opn = "nmsub.ps";
+ break;
+ default:
+ MIPS_INVAL(opn);
+ generate_exception (ctx, EXCP_RI);
+ return;
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr],
+ fregnames[fs], fregnames[ft]);
+}
+
+static void
+gen_rdhwr (CPUState *env, DisasContext *ctx, int rt, int rd)
+{
+ TCGv t0;
+
+ check_insn(env, ctx, ISA_MIPS32R2);
+ t0 = tcg_temp_new();
+
+ switch (rd) {
+ case 0:
+ save_cpu_state(ctx, 1);
+ gen_helper_rdhwr_cpunum(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case 1:
+ save_cpu_state(ctx, 1);
+ gen_helper_rdhwr_synci_step(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case 2:
+ save_cpu_state(ctx, 1);
+ gen_helper_rdhwr_cc(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case 3:
+ save_cpu_state(ctx, 1);
+ gen_helper_rdhwr_ccres(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case 29:
+#if defined(CONFIG_USER_ONLY)
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, tls_value));
+ gen_store_gpr(t0, rt);
+ break;
+#else
+ /* XXX: Some CPUs implement this in hardware.
+ Not supported yet. */
+#endif
+ default: /* Invalid */
+ MIPS_INVAL("rdhwr");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ tcg_temp_free(t0);
+}
+
+static void handle_delay_slot (CPUState *env, DisasContext *ctx,
+ int insn_bytes)
+{
+ if (ctx->hflags & MIPS_HFLAG_BMASK) {
+ int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK;
+ /* Branches completion */
+ ctx->hflags &= ~MIPS_HFLAG_BMASK;
+ ctx->bstate = BS_BRANCH;
+ save_cpu_state(ctx, 0);
+ /* FIXME: Need to clear can_do_io. */
+ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) {
+ case MIPS_HFLAG_B:
+ /* unconditional branch */
+ MIPS_DEBUG("unconditional branch");
+ if (proc_hflags & MIPS_HFLAG_BX) {
+ tcg_gen_xori_i32(hflags, hflags, MIPS_HFLAG_M16);
+ }
+ gen_goto_tb(ctx, 0, ctx->btarget);
+ break;
+ case MIPS_HFLAG_BL:
+ /* blikely taken case */
+ MIPS_DEBUG("blikely branch taken");
+ gen_goto_tb(ctx, 0, ctx->btarget);
+ break;
+ case MIPS_HFLAG_BC:
+ /* Conditional branch */
+ MIPS_DEBUG("conditional branch");
+ {
+ int l1 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1);
+ gen_goto_tb(ctx, 1, ctx->pc + insn_bytes);
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 0, ctx->btarget);
+ }
+ break;
+ case MIPS_HFLAG_BR:
+ /* unconditional branch to register */
+ MIPS_DEBUG("branch to register");
+ if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) {
+ TCGv t0 = tcg_temp_new();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+
+ tcg_gen_andi_tl(t0, btarget, 0x1);
+ tcg_gen_trunc_tl_i32(t1, t0);
+ tcg_temp_free(t0);
+ tcg_gen_andi_i32(hflags, hflags, ~(uint32_t)MIPS_HFLAG_M16);
+ tcg_gen_shli_i32(t1, t1, MIPS_HFLAG_M16_SHIFT);
+ tcg_gen_or_i32(hflags, hflags, t1);
+ tcg_temp_free_i32(t1);
+
+ tcg_gen_andi_tl(cpu_PC, btarget, ~(target_ulong)0x1);
+ } else {
+ tcg_gen_mov_tl(cpu_PC, btarget);
+ }
+ if (ctx->singlestep_enabled) {
+ save_cpu_state(ctx, 0);
+ gen_helper_0i(raise_exception, EXCP_DEBUG);
+ }
+ tcg_gen_exit_tb(0);
+ break;
+ default:
+ MIPS_DEBUG("unknown branch");
+ break;
+ }
+ }
+}
+
+/* ISA extensions (ASEs) */
+/* MIPS16 extension to MIPS32 */
+
+/* MIPS16 major opcodes */
+enum {
+ M16_OPC_ADDIUSP = 0x00,
+ M16_OPC_ADDIUPC = 0x01,
+ M16_OPC_B = 0x02,
+ M16_OPC_JAL = 0x03,
+ M16_OPC_BEQZ = 0x04,
+ M16_OPC_BNEQZ = 0x05,
+ M16_OPC_SHIFT = 0x06,
+ M16_OPC_LD = 0x07,
+ M16_OPC_RRIA = 0x08,
+ M16_OPC_ADDIU8 = 0x09,
+ M16_OPC_SLTI = 0x0a,
+ M16_OPC_SLTIU = 0x0b,
+ M16_OPC_I8 = 0x0c,
+ M16_OPC_LI = 0x0d,
+ M16_OPC_CMPI = 0x0e,
+ M16_OPC_SD = 0x0f,
+ M16_OPC_LB = 0x10,
+ M16_OPC_LH = 0x11,
+ M16_OPC_LWSP = 0x12,
+ M16_OPC_LW = 0x13,
+ M16_OPC_LBU = 0x14,
+ M16_OPC_LHU = 0x15,
+ M16_OPC_LWPC = 0x16,
+ M16_OPC_LWU = 0x17,
+ M16_OPC_SB = 0x18,
+ M16_OPC_SH = 0x19,
+ M16_OPC_SWSP = 0x1a,
+ M16_OPC_SW = 0x1b,
+ M16_OPC_RRR = 0x1c,
+ M16_OPC_RR = 0x1d,
+ M16_OPC_EXTEND = 0x1e,
+ M16_OPC_I64 = 0x1f
+};
+
+/* I8 funct field */
+enum {
+ I8_BTEQZ = 0x0,
+ I8_BTNEZ = 0x1,
+ I8_SWRASP = 0x2,
+ I8_ADJSP = 0x3,
+ I8_SVRS = 0x4,
+ I8_MOV32R = 0x5,
+ I8_MOVR32 = 0x7
+};
+
+/* RRR f field */
+enum {
+ RRR_DADDU = 0x0,
+ RRR_ADDU = 0x1,
+ RRR_DSUBU = 0x2,
+ RRR_SUBU = 0x3
+};
+
+/* RR funct field */
+enum {
+ RR_JR = 0x00,
+ RR_SDBBP = 0x01,
+ RR_SLT = 0x02,
+ RR_SLTU = 0x03,
+ RR_SLLV = 0x04,
+ RR_BREAK = 0x05,
+ RR_SRLV = 0x06,
+ RR_SRAV = 0x07,
+ RR_DSRL = 0x08,
+ RR_CMP = 0x0a,
+ RR_NEG = 0x0b,
+ RR_AND = 0x0c,
+ RR_OR = 0x0d,
+ RR_XOR = 0x0e,
+ RR_NOT = 0x0f,
+ RR_MFHI = 0x10,
+ RR_CNVT = 0x11,
+ RR_MFLO = 0x12,
+ RR_DSRA = 0x13,
+ RR_DSLLV = 0x14,
+ RR_DSRLV = 0x16,
+ RR_DSRAV = 0x17,
+ RR_MULT = 0x18,
+ RR_MULTU = 0x19,
+ RR_DIV = 0x1a,
+ RR_DIVU = 0x1b,
+ RR_DMULT = 0x1c,
+ RR_DMULTU = 0x1d,
+ RR_DDIV = 0x1e,
+ RR_DDIVU = 0x1f
+};
+
+/* I64 funct field */
+enum {
+ I64_LDSP = 0x0,
+ I64_SDSP = 0x1,
+ I64_SDRASP = 0x2,
+ I64_DADJSP = 0x3,
+ I64_LDPC = 0x4,
+ I64_DADDIU5 = 0x5,
+ I64_DADDIUPC = 0x6,
+ I64_DADDIUSP = 0x7
+};
+
+/* RR ry field for CNVT */
+enum {
+ RR_RY_CNVT_ZEB = 0x0,
+ RR_RY_CNVT_ZEH = 0x1,
+ RR_RY_CNVT_ZEW = 0x2,
+ RR_RY_CNVT_SEB = 0x4,
+ RR_RY_CNVT_SEH = 0x5,
+ RR_RY_CNVT_SEW = 0x6,
+};
+
+static int xlat (int r)
+{
+ static int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 };
+
+ return map[r];
+}
+
+static void gen_mips16_save (DisasContext *ctx,
+ int xsregs, int aregs,
+ int do_ra, int do_s0, int do_s1,
+ int framesize)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ int args, astatic;
+
+ switch (aregs) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 11:
+ args = 0;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ args = 1;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ args = 2;
+ break;
+ case 12:
+ case 13:
+ args = 3;
+ break;
+ case 14:
+ args = 4;
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+
+ switch (args) {
+ case 4:
+ gen_base_offset_addr(ctx, t0, 29, 12);
+ gen_load_gpr(t1, 7);
+ op_st_sw(t1, t0, ctx);
+ /* Fall through */
+ case 3:
+ gen_base_offset_addr(ctx, t0, 29, 8);
+ gen_load_gpr(t1, 6);
+ op_st_sw(t1, t0, ctx);
+ /* Fall through */
+ case 2:
+ gen_base_offset_addr(ctx, t0, 29, 4);
+ gen_load_gpr(t1, 5);
+ op_st_sw(t1, t0, ctx);
+ /* Fall through */
+ case 1:
+ gen_base_offset_addr(ctx, t0, 29, 0);
+ gen_load_gpr(t1, 4);
+ op_st_sw(t1, t0, ctx);
+ }
+
+ gen_load_gpr(t0, 29);
+
+#define DECR_AND_STORE(reg) do { \
+ tcg_gen_subi_tl(t0, t0, 4); \
+ gen_load_gpr(t1, reg); \
+ op_st_sw(t1, t0, ctx); \
+ } while (0)
+
+ if (do_ra) {
+ DECR_AND_STORE(31);
+ }
+
+ switch (xsregs) {
+ case 7:
+ DECR_AND_STORE(30);
+ /* Fall through */
+ case 6:
+ DECR_AND_STORE(23);
+ /* Fall through */
+ case 5:
+ DECR_AND_STORE(22);
+ /* Fall through */
+ case 4:
+ DECR_AND_STORE(21);
+ /* Fall through */
+ case 3:
+ DECR_AND_STORE(20);
+ /* Fall through */
+ case 2:
+ DECR_AND_STORE(19);
+ /* Fall through */
+ case 1:
+ DECR_AND_STORE(18);
+ }
+
+ if (do_s1) {
+ DECR_AND_STORE(17);
+ }
+ if (do_s0) {
+ DECR_AND_STORE(16);
+ }
+
+ switch (aregs) {
+ case 0:
+ case 4:
+ case 8:
+ case 12:
+ case 14:
+ astatic = 0;
+ break;
+ case 1:
+ case 5:
+ case 9:
+ case 13:
+ astatic = 1;
+ break;
+ case 2:
+ case 6:
+ case 10:
+ astatic = 2;
+ break;
+ case 3:
+ case 7:
+ astatic = 3;
+ break;
+ case 11:
+ astatic = 4;
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+
+ if (astatic > 0) {
+ DECR_AND_STORE(7);
+ if (astatic > 1) {
+ DECR_AND_STORE(6);
+ if (astatic > 2) {
+ DECR_AND_STORE(5);
+ if (astatic > 3) {
+ DECR_AND_STORE(4);
+ }
+ }
+ }
+ }
+#undef DECR_AND_STORE
+
+ tcg_gen_subi_tl(cpu_gpr[29], cpu_gpr[29], framesize);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_mips16_restore (DisasContext *ctx,
+ int xsregs, int aregs,
+ int do_ra, int do_s0, int do_s1,
+ int framesize)
+{
+ int astatic;
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_addi_tl(t0, cpu_gpr[29], framesize);
+
+#define DECR_AND_LOAD(reg) do { \
+ tcg_gen_subi_tl(t0, t0, 4); \
+ op_ld_lw(t1, t0, ctx); \
+ gen_store_gpr(t1, reg); \
+ } while (0)
+
+ if (do_ra) {
+ DECR_AND_LOAD(31);
+ }
+
+ switch (xsregs) {
+ case 7:
+ DECR_AND_LOAD(30);
+ /* Fall through */
+ case 6:
+ DECR_AND_LOAD(23);
+ /* Fall through */
+ case 5:
+ DECR_AND_LOAD(22);
+ /* Fall through */
+ case 4:
+ DECR_AND_LOAD(21);
+ /* Fall through */
+ case 3:
+ DECR_AND_LOAD(20);
+ /* Fall through */
+ case 2:
+ DECR_AND_LOAD(19);
+ /* Fall through */
+ case 1:
+ DECR_AND_LOAD(18);
+ }
+
+ if (do_s1) {
+ DECR_AND_LOAD(17);
+ }
+ if (do_s0) {
+ DECR_AND_LOAD(16);
+ }
+
+ switch (aregs) {
+ case 0:
+ case 4:
+ case 8:
+ case 12:
+ case 14:
+ astatic = 0;
+ break;
+ case 1:
+ case 5:
+ case 9:
+ case 13:
+ astatic = 1;
+ break;
+ case 2:
+ case 6:
+ case 10:
+ astatic = 2;
+ break;
+ case 3:
+ case 7:
+ astatic = 3;
+ break;
+ case 11:
+ astatic = 4;
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+
+ if (astatic > 0) {
+ DECR_AND_LOAD(7);
+ if (astatic > 1) {
+ DECR_AND_LOAD(6);
+ if (astatic > 2) {
+ DECR_AND_LOAD(5);
+ if (astatic > 3) {
+ DECR_AND_LOAD(4);
+ }
+ }
+ }
+ }
+#undef DECR_AND_LOAD
+
+ tcg_gen_addi_tl(cpu_gpr[29], cpu_gpr[29], framesize);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_addiupc (DisasContext *ctx, int rx, int imm,
+ int is_64_bit, int extended)
+{
+ TCGv t0;
+
+ if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) {
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+
+ t0 = tcg_temp_new();
+
+ tcg_gen_movi_tl(t0, pc_relative_pc(ctx));
+ tcg_gen_addi_tl(cpu_gpr[rx], t0, imm);
+ if (!is_64_bit) {
+ tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ }
+
+ tcg_temp_free(t0);
+}
+
+#if defined(TARGET_MIPS64)
+static void decode_i64_mips16 (CPUState *env, DisasContext *ctx,
+ int ry, int funct, int16_t offset,
+ int extended)
+{
+ switch (funct) {
+ case I64_LDSP:
+ check_mips_64(ctx);
+ offset = extended ? offset : offset << 3;
+ gen_ld(env, ctx, OPC_LD, ry, 29, offset);
+ break;
+ case I64_SDSP:
+ check_mips_64(ctx);
+ offset = extended ? offset : offset << 3;
+ gen_st(ctx, OPC_SD, ry, 29, offset);
+ break;
+ case I64_SDRASP:
+ check_mips_64(ctx);
+ offset = extended ? offset : (ctx->opcode & 0xff) << 3;
+ gen_st(ctx, OPC_SD, 31, 29, offset);
+ break;
+ case I64_DADJSP:
+ check_mips_64(ctx);
+ offset = extended ? offset : ((int8_t)ctx->opcode) << 3;
+ gen_arith_imm(env, ctx, OPC_DADDIU, 29, 29, offset);
+ break;
+ case I64_LDPC:
+ if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) {
+ generate_exception(ctx, EXCP_RI);
+ } else {
+ offset = extended ? offset : offset << 3;
+ gen_ld(env, ctx, OPC_LDPC, ry, 0, offset);
+ }
+ break;
+ case I64_DADDIU5:
+ check_mips_64(ctx);
+ offset = extended ? offset : ((int8_t)(offset << 3)) >> 3;
+ gen_arith_imm(env, ctx, OPC_DADDIU, ry, ry, offset);
+ break;
+ case I64_DADDIUPC:
+ check_mips_64(ctx);
+ offset = extended ? offset : offset << 2;
+ gen_addiupc(ctx, ry, offset, 1, extended);
+ break;
+ case I64_DADDIUSP:
+ check_mips_64(ctx);
+ offset = extended ? offset : offset << 2;
+ gen_arith_imm(env, ctx, OPC_DADDIU, ry, 29, offset);
+ break;
+ }
+}
+#endif
+
+static int decode_extended_mips16_opc (CPUState *env, DisasContext *ctx,
+ int *is_branch)
+{
+ int extend = lduw_code(ctx->pc + 2);
+ int op, rx, ry, funct, sa;
+ int16_t imm, offset;
+
+ ctx->opcode = (ctx->opcode << 16) | extend;
+ op = (ctx->opcode >> 11) & 0x1f;
+ sa = (ctx->opcode >> 22) & 0x1f;
+ funct = (ctx->opcode >> 8) & 0x7;
+ rx = xlat((ctx->opcode >> 8) & 0x7);
+ ry = xlat((ctx->opcode >> 5) & 0x7);
+ offset = imm = (int16_t) (((ctx->opcode >> 16) & 0x1f) << 11
+ | ((ctx->opcode >> 21) & 0x3f) << 5
+ | (ctx->opcode & 0x1f));
+
+ /* The extended opcodes cleverly reuse the opcodes from their 16-bit
+ counterparts. */
+ switch (op) {
+ case M16_OPC_ADDIUSP:
+ gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm);
+ break;
+ case M16_OPC_ADDIUPC:
+ gen_addiupc(ctx, rx, imm, 0, 1);
+ break;
+ case M16_OPC_B:
+ gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1);
+ /* No delay slot, so just process as a normal instruction */
+ break;
+ case M16_OPC_BEQZ:
+ gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1);
+ /* No delay slot, so just process as a normal instruction */
+ break;
+ case M16_OPC_BNEQZ:
+ gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1);
+ /* No delay slot, so just process as a normal instruction */
+ break;
+ case M16_OPC_SHIFT:
+ switch (ctx->opcode & 0x3) {
+ case 0x0:
+ gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa);
+ break;
+ case 0x1:
+#if defined(TARGET_MIPS64)
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa);
+#else
+ generate_exception(ctx, EXCP_RI);
+#endif
+ break;
+ case 0x2:
+ gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa);
+ break;
+ case 0x3:
+ gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa);
+ break;
+ }
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_LD:
+ check_mips_64(ctx);
+ gen_ld(env, ctx, OPC_LD, ry, rx, offset);
+ break;
+#endif
+ case M16_OPC_RRIA:
+ imm = ctx->opcode & 0xf;
+ imm = imm | ((ctx->opcode >> 20) & 0x7f) << 4;
+ imm = imm | ((ctx->opcode >> 16) & 0xf) << 11;
+ imm = (int16_t) (imm << 1) >> 1;
+ if ((ctx->opcode >> 4) & 0x1) {
+#if defined(TARGET_MIPS64)
+ check_mips_64(ctx);
+ gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm);
+#else
+ generate_exception(ctx, EXCP_RI);
+#endif
+ } else {
+ gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm);
+ }
+ break;
+ case M16_OPC_ADDIU8:
+ gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm);
+ break;
+ case M16_OPC_SLTI:
+ gen_slt_imm(env, OPC_SLTI, 24, rx, imm);
+ break;
+ case M16_OPC_SLTIU:
+ gen_slt_imm(env, OPC_SLTIU, 24, rx, imm);
+ break;
+ case M16_OPC_I8:
+ switch (funct) {
+ case I8_BTEQZ:
+ gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1);
+ break;
+ case I8_BTNEZ:
+ gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1);
+ break;
+ case I8_SWRASP:
+ gen_st(ctx, OPC_SW, 31, 29, imm);
+ break;
+ case I8_ADJSP:
+ gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm);
+ break;
+ case I8_SVRS:
+ {
+ int xsregs = (ctx->opcode >> 24) & 0x7;
+ int aregs = (ctx->opcode >> 16) & 0xf;
+ int do_ra = (ctx->opcode >> 6) & 0x1;
+ int do_s0 = (ctx->opcode >> 5) & 0x1;
+ int do_s1 = (ctx->opcode >> 4) & 0x1;
+ int framesize = (((ctx->opcode >> 20) & 0xf) << 4
+ | (ctx->opcode & 0xf)) << 3;
+
+ if (ctx->opcode & (1 << 7)) {
+ gen_mips16_save(ctx, xsregs, aregs,
+ do_ra, do_s0, do_s1,
+ framesize);
+ } else {
+ gen_mips16_restore(ctx, xsregs, aregs,
+ do_ra, do_s0, do_s1,
+ framesize);
+ }
+ }
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case M16_OPC_LI:
+ tcg_gen_movi_tl(cpu_gpr[rx], (uint16_t) imm);
+ break;
+ case M16_OPC_CMPI:
+ tcg_gen_xori_tl(cpu_gpr[24], cpu_gpr[rx], (uint16_t) imm);
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_SD:
+ gen_st(ctx, OPC_SD, ry, rx, offset);
+ break;
+#endif
+ case M16_OPC_LB:
+ gen_ld(env, ctx, OPC_LB, ry, rx, offset);
+ break;
+ case M16_OPC_LH:
+ gen_ld(env, ctx, OPC_LH, ry, rx, offset);
+ break;
+ case M16_OPC_LWSP:
+ gen_ld(env, ctx, OPC_LW, rx, 29, offset);
+ break;
+ case M16_OPC_LW:
+ gen_ld(env, ctx, OPC_LW, ry, rx, offset);
+ break;
+ case M16_OPC_LBU:
+ gen_ld(env, ctx, OPC_LBU, ry, rx, offset);
+ break;
+ case M16_OPC_LHU:
+ gen_ld(env, ctx, OPC_LHU, ry, rx, offset);
+ break;
+ case M16_OPC_LWPC:
+ gen_ld(env, ctx, OPC_LWPC, rx, 0, offset);
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_LWU:
+ gen_ld(env, ctx, OPC_LWU, ry, rx, offset);
+ break;
+#endif
+ case M16_OPC_SB:
+ gen_st(ctx, OPC_SB, ry, rx, offset);
+ break;
+ case M16_OPC_SH:
+ gen_st(ctx, OPC_SH, ry, rx, offset);
+ break;
+ case M16_OPC_SWSP:
+ gen_st(ctx, OPC_SW, rx, 29, offset);
+ break;
+ case M16_OPC_SW:
+ gen_st(ctx, OPC_SW, ry, rx, offset);
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_I64:
+ decode_i64_mips16(env, ctx, ry, funct, offset, 1);
+ break;
+#endif
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+
+ return 4;
+}
+
+static int decode_mips16_opc (CPUState *env, DisasContext *ctx,
+ int *is_branch)
+{
+ int rx, ry;
+ int sa;
+ int op, cnvt_op, op1, offset;
+ int funct;
+ int n_bytes;
+
+ op = (ctx->opcode >> 11) & 0x1f;
+ sa = (ctx->opcode >> 2) & 0x7;
+ sa = sa == 0 ? 8 : sa;
+ rx = xlat((ctx->opcode >> 8) & 0x7);
+ cnvt_op = (ctx->opcode >> 5) & 0x7;
+ ry = xlat((ctx->opcode >> 5) & 0x7);
+ op1 = offset = ctx->opcode & 0x1f;
+
+ n_bytes = 2;
+
+ switch (op) {
+ case M16_OPC_ADDIUSP:
+ {
+ int16_t imm = ((uint8_t) ctx->opcode) << 2;
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm);
+ }
+ break;
+ case M16_OPC_ADDIUPC:
+ gen_addiupc(ctx, rx, ((uint8_t) ctx->opcode) << 2, 0, 0);
+ break;
+ case M16_OPC_B:
+ offset = (ctx->opcode & 0x7ff) << 1;
+ offset = (int16_t)(offset << 4) >> 4;
+ gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset);
+ /* No delay slot, so just process as a normal instruction */
+ break;
+ case M16_OPC_JAL:
+ offset = lduw_code(ctx->pc + 2);
+ offset = (((ctx->opcode & 0x1f) << 21)
+ | ((ctx->opcode >> 5) & 0x1f) << 16
+ | offset) << 2;
+ op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALXS : OPC_JALS;
+ gen_compute_branch(ctx, op, 4, rx, ry, offset);
+ n_bytes = 4;
+ *is_branch = 1;
+ break;
+ case M16_OPC_BEQZ:
+ gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, ((int8_t)ctx->opcode) << 1);
+ /* No delay slot, so just process as a normal instruction */
+ break;
+ case M16_OPC_BNEQZ:
+ gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, ((int8_t)ctx->opcode) << 1);
+ /* No delay slot, so just process as a normal instruction */
+ break;
+ case M16_OPC_SHIFT:
+ switch (ctx->opcode & 0x3) {
+ case 0x0:
+ gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa);
+ break;
+ case 0x1:
+#if defined(TARGET_MIPS64)
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa);
+#else
+ generate_exception(ctx, EXCP_RI);
+#endif
+ break;
+ case 0x2:
+ gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa);
+ break;
+ case 0x3:
+ gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa);
+ break;
+ }
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_LD:
+ check_mips_64(ctx);
+ gen_ld(env, ctx, OPC_LD, ry, rx, offset << 3);
+ break;
+#endif
+ case M16_OPC_RRIA:
+ {
+ int16_t imm = (int8_t)((ctx->opcode & 0xf) << 4) >> 4;
+
+ if ((ctx->opcode >> 4) & 1) {
+#if defined(TARGET_MIPS64)
+ check_mips_64(ctx);
+ gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm);
+#else
+ generate_exception(ctx, EXCP_RI);
+#endif
+ } else {
+ gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm);
+ }
+ }
+ break;
+ case M16_OPC_ADDIU8:
+ {
+ int16_t imm = (int8_t) ctx->opcode;
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm);
+ }
+ break;
+ case M16_OPC_SLTI:
+ {
+ int16_t imm = (uint8_t) ctx->opcode;
+
+ gen_slt_imm(env, OPC_SLTI, 24, rx, imm);
+ }
+ break;
+ case M16_OPC_SLTIU:
+ {
+ int16_t imm = (uint8_t) ctx->opcode;
+
+ gen_slt_imm(env, OPC_SLTIU, 24, rx, imm);
+ }
+ break;
+ case M16_OPC_I8:
+ {
+ int reg32;
+
+ funct = (ctx->opcode >> 8) & 0x7;
+ switch (funct) {
+ case I8_BTEQZ:
+ gen_compute_branch(ctx, OPC_BEQ, 2, 24, 0,
+ ((int8_t)ctx->opcode) << 1);
+ break;
+ case I8_BTNEZ:
+ gen_compute_branch(ctx, OPC_BNE, 2, 24, 0,
+ ((int8_t)ctx->opcode) << 1);
+ break;
+ case I8_SWRASP:
+ gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2);
+ break;
+ case I8_ADJSP:
+ gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29,
+ ((int8_t)ctx->opcode) << 3);
+ break;
+ case I8_SVRS:
+ {
+ int do_ra = ctx->opcode & (1 << 6);
+ int do_s0 = ctx->opcode & (1 << 5);
+ int do_s1 = ctx->opcode & (1 << 4);
+ int framesize = ctx->opcode & 0xf;
+
+ if (framesize == 0) {
+ framesize = 128;
+ } else {
+ framesize = framesize << 3;
+ }
+
+ if (ctx->opcode & (1 << 7)) {
+ gen_mips16_save(ctx, 0, 0,
+ do_ra, do_s0, do_s1, framesize);
+ } else {
+ gen_mips16_restore(ctx, 0, 0,
+ do_ra, do_s0, do_s1, framesize);
+ }
+ }
+ break;
+ case I8_MOV32R:
+ {
+ int rz = xlat(ctx->opcode & 0x7);
+
+ reg32 = (((ctx->opcode >> 3) & 0x3) << 3) |
+ ((ctx->opcode >> 5) & 0x7);
+ gen_arith(env, ctx, OPC_ADDU, reg32, rz, 0);
+ }
+ break;
+ case I8_MOVR32:
+ reg32 = ctx->opcode & 0x1f;
+ gen_arith(env, ctx, OPC_ADDU, ry, reg32, 0);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ }
+ break;
+ case M16_OPC_LI:
+ {
+ int16_t imm = (uint8_t) ctx->opcode;
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rx, 0, imm);
+ }
+ break;
+ case M16_OPC_CMPI:
+ {
+ int16_t imm = (uint8_t) ctx->opcode;
+
+ gen_logic_imm(env, OPC_XORI, 24, rx, imm);
+ }
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_SD:
+ check_mips_64(ctx);
+ gen_st(ctx, OPC_SD, ry, rx, offset << 3);
+ break;
+#endif
+ case M16_OPC_LB:
+ gen_ld(env, ctx, OPC_LB, ry, rx, offset);
+ break;
+ case M16_OPC_LH:
+ gen_ld(env, ctx, OPC_LH, ry, rx, offset << 1);
+ break;
+ case M16_OPC_LWSP:
+ gen_ld(env, ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2);
+ break;
+ case M16_OPC_LW:
+ gen_ld(env, ctx, OPC_LW, ry, rx, offset << 2);
+ break;
+ case M16_OPC_LBU:
+ gen_ld(env, ctx, OPC_LBU, ry, rx, offset);
+ break;
+ case M16_OPC_LHU:
+ gen_ld(env, ctx, OPC_LHU, ry, rx, offset << 1);
+ break;
+ case M16_OPC_LWPC:
+ gen_ld(env, ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2);
+ break;
+#if defined (TARGET_MIPS64)
+ case M16_OPC_LWU:
+ check_mips_64(ctx);
+ gen_ld(env, ctx, OPC_LWU, ry, rx, offset << 2);
+ break;
+#endif
+ case M16_OPC_SB:
+ gen_st(ctx, OPC_SB, ry, rx, offset);
+ break;
+ case M16_OPC_SH:
+ gen_st(ctx, OPC_SH, ry, rx, offset << 1);
+ break;
+ case M16_OPC_SWSP:
+ gen_st(ctx, OPC_SW, rx, 29, ((uint8_t)ctx->opcode) << 2);
+ break;
+ case M16_OPC_SW:
+ gen_st(ctx, OPC_SW, ry, rx, offset << 2);
+ break;
+ case M16_OPC_RRR:
+ {
+ int rz = xlat((ctx->opcode >> 2) & 0x7);
+ int mips32_op;
+
+ switch (ctx->opcode & 0x3) {
+ case RRR_ADDU:
+ mips32_op = OPC_ADDU;
+ break;
+ case RRR_SUBU:
+ mips32_op = OPC_SUBU;
+ break;
+#if defined(TARGET_MIPS64)
+ case RRR_DADDU:
+ mips32_op = OPC_DADDU;
+ check_mips_64(ctx);
+ break;
+ case RRR_DSUBU:
+ mips32_op = OPC_DSUBU;
+ check_mips_64(ctx);
+ break;
+#endif
+ default:
+ generate_exception(ctx, EXCP_RI);
+ goto done;
+ }
+
+ gen_arith(env, ctx, mips32_op, rz, rx, ry);
+ done:
+ ;
+ }
+ break;
+ case M16_OPC_RR:
+ switch (op1) {
+ case RR_JR:
+ {
+ int nd = (ctx->opcode >> 7) & 0x1;
+ int link = (ctx->opcode >> 6) & 0x1;
+ int ra = (ctx->opcode >> 5) & 0x1;
+
+ if (link) {
+ op = nd ? OPC_JALRC : OPC_JALRS;
+ } else {
+ op = OPC_JR;
+ }
+
+ gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0);
+ if (!nd) {
+ *is_branch = 1;
+ }
+ }
+ break;
+ case RR_SDBBP:
+ /* XXX: not clear which exception should be raised
+ * when in debug mode...
+ */
+ check_insn(env, ctx, ISA_MIPS32);
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ generate_exception(ctx, EXCP_DBp);
+ } else {
+ generate_exception(ctx, EXCP_DBp);
+ }
+ break;
+ case RR_SLT:
+ gen_slt(env, OPC_SLT, 24, rx, ry);
+ break;
+ case RR_SLTU:
+ gen_slt(env, OPC_SLTU, 24, rx, ry);
+ break;
+ case RR_BREAK:
+ generate_exception(ctx, EXCP_BREAK);
+ break;
+ case RR_SLLV:
+ gen_shift(env, ctx, OPC_SLLV, ry, rx, ry);
+ break;
+ case RR_SRLV:
+ gen_shift(env, ctx, OPC_SRLV, ry, rx, ry);
+ break;
+ case RR_SRAV:
+ gen_shift(env, ctx, OPC_SRAV, ry, rx, ry);
+ break;
+#if defined (TARGET_MIPS64)
+ case RR_DSRL:
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, OPC_DSRL, ry, ry, sa);
+ break;
+#endif
+ case RR_CMP:
+ gen_logic(env, OPC_XOR, 24, rx, ry);
+ break;
+ case RR_NEG:
+ gen_arith(env, ctx, OPC_SUBU, rx, 0, ry);
+ break;
+ case RR_AND:
+ gen_logic(env, OPC_AND, rx, rx, ry);
+ break;
+ case RR_OR:
+ gen_logic(env, OPC_OR, rx, rx, ry);
+ break;
+ case RR_XOR:
+ gen_logic(env, OPC_XOR, rx, rx, ry);
+ break;
+ case RR_NOT:
+ gen_logic(env, OPC_NOR, rx, ry, 0);
+ break;
+ case RR_MFHI:
+ gen_HILO(ctx, OPC_MFHI, rx);
+ break;
+ case RR_CNVT:
+ switch (cnvt_op) {
+ case RR_RY_CNVT_ZEB:
+ tcg_gen_ext8u_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ break;
+ case RR_RY_CNVT_ZEH:
+ tcg_gen_ext16u_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ break;
+ case RR_RY_CNVT_SEB:
+ tcg_gen_ext8s_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ break;
+ case RR_RY_CNVT_SEH:
+ tcg_gen_ext16s_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ break;
+#if defined (TARGET_MIPS64)
+ case RR_RY_CNVT_ZEW:
+ check_mips_64(ctx);
+ tcg_gen_ext32u_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ break;
+ case RR_RY_CNVT_SEW:
+ check_mips_64(ctx);
+ tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]);
+ break;
+#endif
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case RR_MFLO:
+ gen_HILO(ctx, OPC_MFLO, rx);
+ break;
+#if defined (TARGET_MIPS64)
+ case RR_DSRA:
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, OPC_DSRA, ry, ry, sa);
+ break;
+ case RR_DSLLV:
+ check_mips_64(ctx);
+ gen_shift(env, ctx, OPC_DSLLV, ry, rx, ry);
+ break;
+ case RR_DSRLV:
+ check_mips_64(ctx);
+ gen_shift(env, ctx, OPC_DSRLV, ry, rx, ry);
+ break;
+ case RR_DSRAV:
+ check_mips_64(ctx);
+ gen_shift(env, ctx, OPC_DSRAV, ry, rx, ry);
+ break;
+#endif
+ case RR_MULT:
+ gen_muldiv(ctx, OPC_MULT, rx, ry);
+ break;
+ case RR_MULTU:
+ gen_muldiv(ctx, OPC_MULTU, rx, ry);
+ break;
+ case RR_DIV:
+ gen_muldiv(ctx, OPC_DIV, rx, ry);
+ break;
+ case RR_DIVU:
+ gen_muldiv(ctx, OPC_DIVU, rx, ry);
+ break;
+#if defined (TARGET_MIPS64)
+ case RR_DMULT:
+ check_mips_64(ctx);
+ gen_muldiv(ctx, OPC_DMULT, rx, ry);
+ break;
+ case RR_DMULTU:
+ check_mips_64(ctx);
+ gen_muldiv(ctx, OPC_DMULTU, rx, ry);
+ break;
+ case RR_DDIV:
+ check_mips_64(ctx);
+ gen_muldiv(ctx, OPC_DDIV, rx, ry);
+ break;
+ case RR_DDIVU:
+ check_mips_64(ctx);
+ gen_muldiv(ctx, OPC_DDIVU, rx, ry);
+ break;
+#endif
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case M16_OPC_EXTEND:
+ decode_extended_mips16_opc(env, ctx, is_branch);
+ n_bytes = 4;
+ break;
+#if defined(TARGET_MIPS64)
+ case M16_OPC_I64:
+ funct = (ctx->opcode >> 8) & 0x7;
+ decode_i64_mips16(env, ctx, ry, funct, offset, 0);
+ break;
+#endif
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+
+ return n_bytes;
+}
+
+/* microMIPS extension to MIPS32 */
+
+/* microMIPS32 major opcodes */
+
+enum {
+ POOL32A = 0x00,
+ POOL16A = 0x01,
+ LBU16 = 0x02,
+ MOVE16 = 0x03,
+ ADDI32 = 0x04,
+ LBU32 = 0x05,
+ SB32 = 0x06,
+ LB32 = 0x07,
+
+ POOL32B = 0x08,
+ POOL16B = 0x09,
+ LHU16 = 0x0a,
+ ANDI16 = 0x0b,
+ ADDIU32 = 0x0c,
+ LHU32 = 0x0d,
+ SH32 = 0x0e,
+ LH32 = 0x0f,
+
+ POOL32I = 0x10,
+ POOL16C = 0x11,
+ LWSP16 = 0x12,
+ POOL16D = 0x13,
+ ORI32 = 0x14,
+ POOL32F = 0x15,
+ POOL32S = 0x16,
+ DADDIU32 = 0x17,
+
+ POOL32C = 0x18,
+ LWGP16 = 0x19,
+ LW16 = 0x1a,
+ POOL16E = 0x1b,
+ XORI32 = 0x1c,
+ JALS32 = 0x1d,
+ ADDIUPC = 0x1e,
+ POOL48A = 0x1f,
+
+ /* 0x20 is reserved */
+ RES_20 = 0x20,
+ POOL16F = 0x21,
+ SB16 = 0x22,
+ BEQZ16 = 0x23,
+ SLTI32 = 0x24,
+ BEQ32 = 0x25,
+ SWC132 = 0x26,
+ LWC132 = 0x27,
+
+ /* 0x28 and 0x29 are reserved */
+ RES_28 = 0x28,
+ RES_29 = 0x29,
+ SH16 = 0x2a,
+ BNEZ16 = 0x2b,
+ SLTIU32 = 0x2c,
+ BNE32 = 0x2d,
+ SDC132 = 0x2e,
+ LDC132 = 0x2f,
+
+ /* 0x30 and 0x31 are reserved */
+ RES_30 = 0x30,
+ RES_31 = 0x31,
+ SWSP16 = 0x32,
+ B16 = 0x33,
+ ANDI32 = 0x34,
+ J32 = 0x35,
+ SD32 = 0x36,
+ LD32 = 0x37,
+
+ /* 0x38 and 0x39 are reserved */
+ RES_38 = 0x38,
+ RES_39 = 0x39,
+ SW16 = 0x3a,
+ LI16 = 0x3b,
+ JALX32 = 0x3c,
+ JAL32 = 0x3d,
+ SW32 = 0x3e,
+ LW32 = 0x3f
+};
+
+/* POOL32A encoding of minor opcode field */
+
+enum {
+ /* These opcodes are distinguished only by bits 9..6; those bits are
+ * what are recorded below. */
+ SLL32 = 0x0,
+ SRL32 = 0x1,
+ SRA = 0x2,
+ ROTR = 0x3,
+
+ SLLV = 0x0,
+ SRLV = 0x1,
+ SRAV = 0x2,
+ ROTRV = 0x3,
+ ADD = 0x4,
+ ADDU32 = 0x5,
+ SUB = 0x6,
+ SUBU32 = 0x7,
+ MUL = 0x8,
+ AND = 0x9,
+ OR32 = 0xa,
+ NOR = 0xb,
+ XOR32 = 0xc,
+ SLT = 0xd,
+ SLTU = 0xe,
+
+ MOVN = 0x0,
+ MOVZ = 0x1,
+ LWXS = 0x4,
+
+ /* The following can be distinguished by their lower 6 bits. */
+ INS = 0x0c,
+ EXT = 0x2c,
+ POOL32AXF = 0x3c
+};
+
+/* POOL32AXF encoding of minor opcode field extension */
+
+enum {
+ /* bits 11..6 */
+ TEQ = 0x00,
+ TGE = 0x08,
+ TGEU = 0x10,
+ TLT = 0x20,
+ TLTU = 0x28,
+ TNE = 0x30,
+
+ MFC0 = 0x03,
+ MTC0 = 0x0b,
+
+ /* bits 13..12 for 0x01 */
+ MFHI_ACC = 0x0,
+ MFLO_ACC = 0x1,
+ MTHI_ACC = 0x2,
+ MTLO_ACC = 0x3,
+
+ /* bits 13..12 for 0x2a */
+ MADD_ACC = 0x0,
+ MADDU_ACC = 0x1,
+ MSUB_ACC = 0x2,
+ MSUBU_ACC = 0x3,
+
+ /* bits 13..12 for 0x32 */
+ MULT_ACC = 0x0,
+ MULTU_ACC = 0x0,
+
+ /* bits 15..12 for 0x2c */
+ SEB = 0x2,
+ SEH = 0x3,
+ CLO = 0x4,
+ CLZ = 0x5,
+ RDHWR = 0x6,
+ WSBH = 0x7,
+ MULT = 0x8,
+ MULTU = 0x9,
+ DIV = 0xa,
+ DIVU = 0xb,
+ MADD = 0xc,
+ MADDU = 0xd,
+ MSUB = 0xe,
+ MSUBU = 0xf,
+
+ /* bits 15..12 for 0x34 */
+ MFC2 = 0x4,
+ MTC2 = 0x5,
+ MFHC2 = 0x8,
+ MTHC2 = 0x9,
+ CFC2 = 0xc,
+ CTC2 = 0xd,
+
+ /* bits 15..12 for 0x3c */
+ JALR = 0x0,
+ JR = 0x0, /* alias */
+ JALR_HB = 0x1,
+ JALRS = 0x4,
+ JALRS_HB = 0x5,
+
+ /* bits 15..12 for 0x05 */
+ RDPGPR = 0xe,
+ WRPGPR = 0xf,
+
+ /* bits 15..12 for 0x0d */
+ TLBP = 0x0,
+ TLBR = 0x1,
+ TLBWI = 0x2,
+ TLBWR = 0x3,
+ WAIT = 0x9,
+ IRET = 0xd,
+ DERET = 0xe,
+ ERET = 0xf,
+
+ /* bits 15..12 for 0x15 */
+ DMT = 0x0,
+ DVPE = 0x1,
+ EMT = 0x2,
+ EVPE = 0x3,
+
+ /* bits 15..12 for 0x1d */
+ DI = 0x4,
+ EI = 0x5,
+
+ /* bits 15..12 for 0x2d */
+ SYNC = 0x6,
+ SYSCALL = 0x8,
+ SDBBP = 0xd,
+
+ /* bits 15..12 for 0x35 */
+ MFHI32 = 0x0,
+ MFLO32 = 0x1,
+ MTHI32 = 0x2,
+ MTLO32 = 0x3,
+};
+
+/* POOL32B encoding of minor opcode field (bits 15..12) */
+
+enum {
+ LWC2 = 0x0,
+ LWP = 0x1,
+ LDP = 0x4,
+ LWM32 = 0x5,
+ CACHE = 0x6,
+ LDM = 0x7,
+ SWC2 = 0x8,
+ SWP = 0x9,
+ SDP = 0xc,
+ SWM32 = 0xd,
+ SDM = 0xf
+};
+
+/* POOL32C encoding of minor opcode field (bits 15..12) */
+
+enum {
+ LWL = 0x0,
+ SWL = 0x8,
+ LWR = 0x1,
+ SWR = 0x9,
+ PREF = 0x2,
+ /* 0xa is reserved */
+ LL = 0x3,
+ SC = 0xb,
+ LDL = 0x4,
+ SDL = 0xc,
+ LDR = 0x5,
+ SDR = 0xd,
+ /* 0x6 is reserved */
+ LWU = 0xe,
+ LLD = 0x7,
+ SCD = 0xf
+};
+
+/* POOL32F encoding of minor opcode field (bits 5..0) */
+
+enum {
+ /* These are the bit 7..6 values */
+ ADD_FMT = 0x0,
+ MOVN_FMT = 0x0,
+
+ SUB_FMT = 0x1,
+ MOVZ_FMT = 0x1,
+
+ MUL_FMT = 0x2,
+
+ DIV_FMT = 0x3,
+
+ /* These are the bit 8..6 values */
+ RSQRT2_FMT = 0x0,
+ MOVF_FMT = 0x0,
+
+ LWXC1 = 0x1,
+ MOVT_FMT = 0x1,
+
+ PLL_PS = 0x2,
+ SWXC1 = 0x2,
+
+ PLU_PS = 0x3,
+ LDXC1 = 0x3,
+
+ PUL_PS = 0x4,
+ SDXC1 = 0x4,
+ RECIP2_FMT = 0x4,
+
+ PUU_PS = 0x5,
+ LUXC1 = 0x5,
+
+ CVT_PS_S = 0x6,
+ SUXC1 = 0x6,
+ ADDR_PS = 0x6,
+ PREFX = 0x6,
+
+ MULR_PS = 0x7,
+
+ MADD_S = 0x01,
+ MADD_D = 0x09,
+ MADD_PS = 0x11,
+ ALNV_PS = 0x19,
+ MSUB_S = 0x21,
+ MSUB_D = 0x29,
+ MSUB_PS = 0x31,
+
+ NMADD_S = 0x02,
+ NMADD_D = 0x0a,
+ NMADD_PS = 0x12,
+ NMSUB_S = 0x22,
+ NMSUB_D = 0x2a,
+ NMSUB_PS = 0x32,
+
+ POOL32FXF = 0x3b,
+
+ CABS_COND_FMT = 0x1c, /* MIPS3D */
+ C_COND_FMT = 0x3c
+};
+
+/* POOL32Fxf encoding of minor opcode extension field */
+
+enum {
+ CVT_L = 0x04,
+ RSQRT_FMT = 0x08,
+ FLOOR_L = 0x0c,
+ CVT_PW_PS = 0x1c,
+ CVT_W = 0x24,
+ SQRT_FMT = 0x28,
+ FLOOR_W = 0x2c,
+ CVT_PS_PW = 0x3c,
+ CFC1 = 0x40,
+ RECIP_FMT = 0x48,
+ CEIL_L = 0x4c,
+ CTC1 = 0x60,
+ CEIL_W = 0x6c,
+ MFC1 = 0x80,
+ CVT_S_PL = 0x84,
+ TRUNC_L = 0x8c,
+ MTC1 = 0xa0,
+ CVT_S_PU = 0xa4,
+ TRUNC_W = 0xac,
+ MFHC1 = 0xc0,
+ ROUND_L = 0xcc,
+ MTHC1 = 0xe0,
+ ROUND_W = 0xec,
+
+ MOV_FMT = 0x01,
+ MOVF = 0x05,
+ ABS_FMT = 0x0d,
+ RSQRT1_FMT = 0x1d,
+ MOVT = 0x25,
+ NEG_FMT = 0x2d,
+ CVT_D = 0x4d,
+ RECIP1_FMT = 0x5d,
+ CVT_S = 0x6d
+};
+
+/* POOL32I encoding of minor opcode field (bits 25..21) */
+
+enum {
+ BLTZ = 0x00,
+ BLTZAL = 0x01,
+ BGEZ = 0x02,
+ BGEZAL = 0x03,
+ BLEZ = 0x04,
+ BNEZC = 0x05,
+ BGTZ = 0x06,
+ BEQZC = 0x07,
+ TLTI = 0x08,
+ TGEI = 0x09,
+ TLTIU = 0x0a,
+ TGEIU = 0x0b,
+ TNEI = 0x0c,
+ LUI = 0x0d,
+ TEQI = 0x0e,
+ SYNCI = 0x10,
+ BLTZALS = 0x11,
+ BGEZALS = 0x13,
+ BC2F = 0x14,
+ BC2T = 0x15,
+ BPOSGE64 = 0x1a,
+ BPOSGE32 = 0x1b,
+ /* These overlap and are distinguished by bit16 of the instruction */
+ BC1F = 0x1c,
+ BC1T = 0x1d,
+ BC1ANY2F = 0x1c,
+ BC1ANY2T = 0x1d,
+ BC1ANY4F = 0x1e,
+ BC1ANY4T = 0x1f
+};
+
+/* POOL16A encoding of minor opcode field */
+
+enum {
+ ADDU16 = 0x0,
+ SUBU16 = 0x1
+};
+
+/* POOL16B encoding of minor opcode field */
+
+enum {
+ SLL16 = 0x0,
+ SRL16 = 0x1
+};
+
+/* POOL16C encoding of minor opcode field */
+
+enum {
+ NOT16 = 0x00,
+ XOR16 = 0x04,
+ AND16 = 0x08,
+ OR16 = 0x0c,
+ LWM16 = 0x10,
+ SWM16 = 0x14,
+ JR16 = 0x18,
+ JRC16 = 0x1a,
+ JALR16 = 0x1c,
+ JALR16S = 0x1e,
+ MFHI16 = 0x20,
+ MFLO16 = 0x24,
+ BREAK16 = 0x28,
+ SDBBP16 = 0x2c,
+ JRADDIUSP = 0x30
+};
+
+/* POOL16D encoding of minor opcode field */
+
+enum {
+ ADDIUS5 = 0x0,
+ ADDIUSP = 0x1
+};
+
+/* POOL16E encoding of minor opcode field */
+
+enum {
+ ADDIUR2 = 0x0,
+ ADDIUR1SP = 0x1
+};
+
+static int mmreg (int r)
+{
+ static const int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 };
+
+ return map[r];
+}
+
+/* Used for 16-bit store instructions. */
+static int mmreg2 (int r)
+{
+ static const int map[] = { 0, 17, 2, 3, 4, 5, 6, 7 };
+
+ return map[r];
+}
+
+#define uMIPS_RD(op) ((op >> 7) & 0x7)
+#define uMIPS_RS(op) ((op >> 4) & 0x7)
+#define uMIPS_RS2(op) uMIPS_RS(op)
+#define uMIPS_RS1(op) ((op >> 1) & 0x7)
+#define uMIPS_RD5(op) ((op >> 5) & 0x1f)
+#define uMIPS_RS5(op) (op & 0x1f)
+
+/* Signed immediate */
+#define SIMM(op, start, width) \
+ ((int32_t)(((op >> start) & ((~0U) >> (32-width))) \
+ << (32-width)) \
+ >> (32-width))
+/* Zero-extended immediate */
+#define ZIMM(op, start, width) ((op >> start) & ((~0U) >> (32-width)))
+
+static void gen_addiur1sp (CPUState *env, DisasContext *ctx)
+{
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2);
+}
+
+static void gen_addiur2 (CPUState *env, DisasContext *ctx)
+{
+ static const int decoded_imm[] = { 1, 4, 8, 12, 16, 20, 24, -1 };
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rs = mmreg(uMIPS_RS(ctx->opcode));
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]);
+}
+
+static void gen_addiusp (CPUState *env, DisasContext *ctx)
+{
+ int encoded = ZIMM(ctx->opcode, 1, 9);
+ int decoded;
+
+ if (encoded <= 1) {
+ decoded = 256 + encoded;
+ } else if (encoded <= 255) {
+ decoded = encoded;
+ } else if (encoded <= 509) {
+ decoded = encoded - 512;
+ } else {
+ decoded = encoded - 768;
+ }
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, decoded << 2);
+}
+
+static void gen_addius5 (CPUState *env, DisasContext *ctx)
+{
+ int imm = SIMM(ctx->opcode, 1, 4);
+ int rd = (ctx->opcode >> 5) & 0x1f;
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rd, rd, imm);
+}
+
+static void gen_andi16 (CPUState *env, DisasContext *ctx)
+{
+ static const int decoded_imm[] = { 128, 1, 2, 3, 4, 7, 8, 15, 16,
+ 31, 32, 63, 64, 255, 32768, 65535 };
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rs = mmreg(uMIPS_RS(ctx->opcode));
+ int encoded = ZIMM(ctx->opcode, 0, 4);
+
+ gen_logic_imm(env, OPC_ANDI, rd, rs, decoded_imm[encoded]);
+}
+
+static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist,
+ int base, int16_t offset)
+{
+ TCGv t0, t1;
+ TCGv_i32 t2;
+
+ if (ctx->hflags & MIPS_HFLAG_BMASK) {
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+
+ t0 = tcg_temp_new();
+
+ gen_base_offset_addr(ctx, t0, base, offset);
+
+ t1 = tcg_const_tl(reglist);
+ t2 = tcg_const_i32(ctx->mem_idx);
+
+ save_cpu_state(ctx, 1);
+ switch (opc) {
+ case LWM32:
+ gen_helper_lwm(t0, t1, t2);
+ break;
+ case SWM32:
+ gen_helper_swm(t0, t1, t2);
+ break;
+#ifdef TARGET_MIPS64
+ case LDM:
+ gen_helper_ldm(t0, t1, t2);
+ break;
+ case SDM:
+ gen_helper_sdm(t0, t1, t2);
+ break;
+#endif
+ }
+ MIPS_DEBUG("%s, %x, %d(%s)", opn, reglist, offset, regnames[base]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free_i32(t2);
+}
+
+
+static void gen_pool16c_insn (CPUState *env, DisasContext *ctx, int *is_branch)
+{
+ int rd = mmreg((ctx->opcode >> 3) & 0x7);
+ int rs = mmreg(ctx->opcode & 0x7);
+ int opc;
+
+ switch (((ctx->opcode) >> 4) & 0x3f) {
+ case NOT16 + 0:
+ case NOT16 + 1:
+ case NOT16 + 2:
+ case NOT16 + 3:
+ gen_logic(env, OPC_NOR, rd, rs, 0);
+ break;
+ case XOR16 + 0:
+ case XOR16 + 1:
+ case XOR16 + 2:
+ case XOR16 + 3:
+ gen_logic(env, OPC_XOR, rd, rd, rs);
+ break;
+ case AND16 + 0:
+ case AND16 + 1:
+ case AND16 + 2:
+ case AND16 + 3:
+ gen_logic(env, OPC_AND, rd, rd, rs);
+ break;
+ case OR16 + 0:
+ case OR16 + 1:
+ case OR16 + 2:
+ case OR16 + 3:
+ gen_logic(env, OPC_OR, rd, rd, rs);
+ break;
+ case LWM16 + 0:
+ case LWM16 + 1:
+ case LWM16 + 2:
+ case LWM16 + 3:
+ {
+ static const int lwm_convert[] = { 0x11, 0x12, 0x13, 0x14 };
+ int offset = ZIMM(ctx->opcode, 0, 4);
+
+ gen_ldst_multiple(ctx, LWM32, lwm_convert[(ctx->opcode >> 4) & 0x3],
+ 29, offset << 2);
+ }
+ break;
+ case SWM16 + 0:
+ case SWM16 + 1:
+ case SWM16 + 2:
+ case SWM16 + 3:
+ {
+ static const int swm_convert[] = { 0x11, 0x12, 0x13, 0x14 };
+ int offset = ZIMM(ctx->opcode, 0, 4);
+
+ gen_ldst_multiple(ctx, SWM32, swm_convert[(ctx->opcode >> 4) & 0x3],
+ 29, offset << 2);
+ }
+ break;
+ case JR16 + 0:
+ case JR16 + 1:
+ {
+ int reg = ctx->opcode & 0x1f;
+
+ gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0);
+ }
+ *is_branch = 1;
+ break;
+ case JRC16 + 0:
+ case JRC16 + 1:
+ {
+ int reg = ctx->opcode & 0x1f;
+
+ gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0);
+ /* Let normal delay slot handling in our caller take us
+ to the branch target. */
+ }
+ break;
+ case JALR16 + 0:
+ case JALR16 + 1:
+ opc = OPC_JALR;
+ goto do_jalr;
+ case JALR16S + 0:
+ case JALR16S + 1:
+ opc = OPC_JALRS;
+ do_jalr:
+ {
+ int reg = ctx->opcode & 0x1f;
+
+ gen_compute_branch(ctx, opc, 2, reg, 31, 0);
+ }
+ *is_branch = 1;
+ break;
+ case MFHI16 + 0:
+ case MFHI16 + 1:
+ gen_HILO(ctx, OPC_MFHI, uMIPS_RS5(ctx->opcode));
+ break;
+ case MFLO16 + 0:
+ case MFLO16 + 1:
+ gen_HILO(ctx, OPC_MFLO, uMIPS_RS5(ctx->opcode));
+ break;
+ case BREAK16:
+ generate_exception(ctx, EXCP_BREAK);
+ break;
+ case SDBBP16:
+ /* XXX: not clear which exception should be raised
+ * when in debug mode...
+ */
+ check_insn(env, ctx, ISA_MIPS32);
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ generate_exception(ctx, EXCP_DBp);
+ } else {
+ generate_exception(ctx, EXCP_DBp);
+ }
+ break;
+ case JRADDIUSP + 0:
+ case JRADDIUSP + 1:
+ {
+ int imm = ZIMM(ctx->opcode, 0, 5);
+
+ gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0);
+ gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm << 2);
+ /* Let normal delay slot handling in our caller take us
+ to the branch target. */
+ }
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+}
+
+static void gen_ldxs (DisasContext *ctx, int base, int index, int rd)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ gen_load_gpr(t0, base);
+
+ if (index != 0) {
+ gen_load_gpr(t1, index);
+ tcg_gen_shli_tl(t1, t1, 2);
+ gen_op_addr_add(ctx, t0, t1, t0);
+ }
+
+ save_cpu_state(ctx, 0);
+ op_ld_lw(t1, t0, ctx);
+ gen_store_gpr(t1, rd);
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd,
+ int base, int16_t offset)
+{
+ const char *opn = "ldst_pair";
+ TCGv t0, t1;
+
+ if (ctx->hflags & MIPS_HFLAG_BMASK || rd == 31 || rd == base) {
+ generate_exception(ctx, EXCP_RI);
+ return;
+ }
+
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+
+ gen_base_offset_addr(ctx, t0, base, offset);
+
+ switch (opc) {
+ case LWP:
+ save_cpu_state(ctx, 0);
+ op_ld_lw(t1, t0, ctx);
+ gen_store_gpr(t1, rd);
+ tcg_gen_movi_tl(t1, 4);
+ gen_op_addr_add(ctx, t0, t0, t1);
+ op_ld_lw(t1, t0, ctx);
+ gen_store_gpr(t1, rd+1);
+ opn = "lwp";
+ break;
+ case SWP:
+ save_cpu_state(ctx, 0);
+ gen_load_gpr(t1, rd);
+ op_st_sw(t1, t0, ctx);
+ tcg_gen_movi_tl(t1, 4);
+ gen_op_addr_add(ctx, t0, t0, t1);
+ gen_load_gpr(t1, rd+1);
+ op_st_sw(t1, t0, ctx);
+ opn = "swp";
+ break;
+#ifdef TARGET_MIPS64
+ case LDP:
+ save_cpu_state(ctx, 0);
+ op_ld_ld(t1, t0, ctx);
+ gen_store_gpr(t1, rd);
+ tcg_gen_movi_tl(t1, 8);
+ gen_op_addr_add(ctx, t0, t0, t1);
+ op_ld_ld(t1, t0, ctx);
+ gen_store_gpr(t1, rd+1);
+ opn = "ldp";
+ break;
+ case SDP:
+ save_cpu_state(ctx, 0);
+ gen_load_gpr(t1, rd);
+ op_st_sd(t1, t0, ctx);
+ tcg_gen_movi_tl(t1, 8);
+ gen_op_addr_add(ctx, t0, t0, t1);
+ gen_load_gpr(t1, rd+1);
+ op_st_sd(t1, t0, ctx);
+ opn = "sdp";
+ break;
+#endif
+ }
+ (void)opn; /* avoid a compiler warning */
+ MIPS_DEBUG("%s, %s, %d(%s)", opn, regnames[rd], offset, regnames[base]);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+static void gen_pool32axf (CPUState *env, DisasContext *ctx, int rt, int rs,
+ int *is_branch)
+{
+ int extension = (ctx->opcode >> 6) & 0x3f;
+ int minor = (ctx->opcode >> 12) & 0xf;
+ uint32_t mips32_op;
+
+ switch (extension) {
+ case TEQ:
+ mips32_op = OPC_TEQ;
+ goto do_trap;
+ case TGE:
+ mips32_op = OPC_TGE;
+ goto do_trap;
+ case TGEU:
+ mips32_op = OPC_TGEU;
+ goto do_trap;
+ case TLT:
+ mips32_op = OPC_TLT;
+ goto do_trap;
+ case TLTU:
+ mips32_op = OPC_TLTU;
+ goto do_trap;
+ case TNE:
+ mips32_op = OPC_TNE;
+ do_trap:
+ gen_trap(ctx, mips32_op, rs, rt, -1);
+ break;
+#ifndef CONFIG_USER_ONLY
+ case MFC0:
+ case MFC0 + 32:
+ if (rt == 0) {
+ /* Treat as NOP. */
+ break;
+ }
+ gen_mfc0(env, ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7);
+ break;
+ case MTC0:
+ case MTC0 + 32:
+ {
+ TCGv t0 = tcg_temp_new();
+
+ gen_load_gpr(t0, rt);
+ gen_mtc0(env, ctx, t0, rs, (ctx->opcode >> 11) & 0x7);
+ tcg_temp_free(t0);
+ }
+ break;
+#endif
+ case 0x2c:
+ switch (minor) {
+ case SEB:
+ gen_bshfl(ctx, OPC_SEB, rs, rt);
+ break;
+ case SEH:
+ gen_bshfl(ctx, OPC_SEH, rs, rt);
+ break;
+ case CLO:
+ mips32_op = OPC_CLO;
+ goto do_cl;
+ case CLZ:
+ mips32_op = OPC_CLZ;
+ do_cl:
+ check_insn(env, ctx, ISA_MIPS32);
+ gen_cl(ctx, mips32_op, rt, rs);
+ break;
+ case RDHWR:
+ gen_rdhwr(env, ctx, rt, rs);
+ break;
+ case WSBH:
+ gen_bshfl(ctx, OPC_WSBH, rs, rt);
+ break;
+ case MULT:
+ mips32_op = OPC_MULT;
+ goto do_muldiv;
+ case MULTU:
+ mips32_op = OPC_MULTU;
+ goto do_muldiv;
+ case DIV:
+ mips32_op = OPC_DIV;
+ goto do_muldiv;
+ case DIVU:
+ mips32_op = OPC_DIVU;
+ goto do_muldiv;
+ case MADD:
+ mips32_op = OPC_MADD;
+ goto do_muldiv;
+ case MADDU:
+ mips32_op = OPC_MADDU;
+ goto do_muldiv;
+ case MSUB:
+ mips32_op = OPC_MSUB;
+ goto do_muldiv;
+ case MSUBU:
+ mips32_op = OPC_MSUBU;
+ do_muldiv:
+ check_insn(env, ctx, ISA_MIPS32);
+ gen_muldiv(ctx, mips32_op, rs, rt);
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+ case 0x34:
+ switch (minor) {
+ case MFC2:
+ case MTC2:
+ case MFHC2:
+ case MTHC2:
+ case CFC2:
+ case CTC2:
+ generate_exception_err(ctx, EXCP_CpU, 2);
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+ case 0x3c:
+ switch (minor) {
+ case JALR:
+ case JALR_HB:
+ gen_compute_branch (ctx, OPC_JALR, 4, rs, rt, 0);
+ *is_branch = 1;
+ break;
+ case JALRS:
+ case JALRS_HB:
+ gen_compute_branch (ctx, OPC_JALRS, 4, rs, rt, 0);
+ *is_branch = 1;
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+ case 0x05:
+ switch (minor) {
+ case RDPGPR:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_load_srsgpr(rt, rs);
+ break;
+ case WRPGPR:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_store_srsgpr(rt, rs);
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+#ifndef CONFIG_USER_ONLY
+ case 0x0d:
+ switch (minor) {
+ case TLBP:
+ mips32_op = OPC_TLBP;
+ goto do_cp0;
+ case TLBR:
+ mips32_op = OPC_TLBR;
+ goto do_cp0;
+ case TLBWI:
+ mips32_op = OPC_TLBWI;
+ goto do_cp0;
+ case TLBWR:
+ mips32_op = OPC_TLBWR;
+ goto do_cp0;
+ case WAIT:
+ mips32_op = OPC_WAIT;
+ goto do_cp0;
+ case DERET:
+ mips32_op = OPC_DERET;
+ goto do_cp0;
+ case ERET:
+ mips32_op = OPC_ERET;
+ do_cp0:
+ gen_cp0(env, ctx, mips32_op, rt, rs);
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+ case 0x1d:
+ switch (minor) {
+ case DI:
+ {
+ TCGv t0 = tcg_temp_new();
+
+ save_cpu_state(ctx, 1);
+ gen_helper_di(t0);
+ gen_store_gpr(t0, rs);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ tcg_temp_free(t0);
+ }
+ break;
+ case EI:
+ {
+ TCGv t0 = tcg_temp_new();
+
+ save_cpu_state(ctx, 1);
+ gen_helper_ei(t0);
+ gen_store_gpr(t0, rs);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ tcg_temp_free(t0);
+ }
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+#endif
+ case 0x2d:
+ switch (minor) {
+ case SYNC:
+ /* NOP */
+ break;
+ case SYSCALL:
+ generate_exception(ctx, EXCP_SYSCALL);
+ ctx->bstate = BS_STOP;
+ break;
+ case SDBBP:
+ check_insn(env, ctx, ISA_MIPS32);
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ generate_exception(ctx, EXCP_DBp);
+ } else {
+ generate_exception(ctx, EXCP_DBp);
+ }
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+ case 0x35:
+ switch (minor) {
+ case MFHI32:
+ gen_HILO(ctx, OPC_MFHI, rs);
+ break;
+ case MFLO32:
+ gen_HILO(ctx, OPC_MFLO, rs);
+ break;
+ case MTHI32:
+ gen_HILO(ctx, OPC_MTHI, rs);
+ break;
+ case MTLO32:
+ gen_HILO(ctx, OPC_MTLO, rs);
+ break;
+ default:
+ goto pool32axf_invalid;
+ }
+ break;
+ default:
+ pool32axf_invalid:
+ MIPS_INVAL("pool32axf");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+}
+
+/* Values for microMIPS fmt field. Variable-width, depending on which
+ formats the instruction supports. */
+
+enum {
+ FMT_SD_S = 0,
+ FMT_SD_D = 1,
+
+ FMT_SDPS_S = 0,
+ FMT_SDPS_D = 1,
+ FMT_SDPS_PS = 2,
+
+ FMT_SWL_S = 0,
+ FMT_SWL_W = 1,
+ FMT_SWL_L = 2,
+
+ FMT_DWL_D = 0,
+ FMT_DWL_W = 1,
+ FMT_DWL_L = 2
+};
+
+static void gen_pool32fxf (CPUState *env, DisasContext *ctx, int rt, int rs)
+{
+ int extension = (ctx->opcode >> 6) & 0x3ff;
+ uint32_t mips32_op;
+
+#define FLOAT_1BIT_FMT(opc, fmt) (fmt << 8) | opc
+#define FLOAT_2BIT_FMT(opc, fmt) (fmt << 7) | opc
+#define COND_FLOAT_MOV(opc, cond) (cond << 7) | opc
+
+ switch (extension) {
+ case FLOAT_1BIT_FMT(CFC1, 0):
+ mips32_op = OPC_CFC1;
+ goto do_cp1;
+ case FLOAT_1BIT_FMT(CTC1, 0):
+ mips32_op = OPC_CTC1;
+ goto do_cp1;
+ case FLOAT_1BIT_FMT(MFC1, 0):
+ mips32_op = OPC_MFC1;
+ goto do_cp1;
+ case FLOAT_1BIT_FMT(MTC1, 0):
+ mips32_op = OPC_MTC1;
+ goto do_cp1;
+ case FLOAT_1BIT_FMT(MFHC1, 0):
+ mips32_op = OPC_MFHC1;
+ goto do_cp1;
+ case FLOAT_1BIT_FMT(MTHC1, 0):
+ mips32_op = OPC_MTHC1;
+ do_cp1:
+ gen_cp1(ctx, mips32_op, rt, rs);
+ break;
+
+ /* Reciprocal square root */
+ case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_S):
+ mips32_op = OPC_RSQRT_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_D):
+ mips32_op = OPC_RSQRT_D;
+ goto do_unaryfp;
+
+ /* Square root */
+ case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_S):
+ mips32_op = OPC_SQRT_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_D):
+ mips32_op = OPC_SQRT_D;
+ goto do_unaryfp;
+
+ /* Reciprocal */
+ case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_S):
+ mips32_op = OPC_RECIP_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_D):
+ mips32_op = OPC_RECIP_D;
+ goto do_unaryfp;
+
+ /* Floor */
+ case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_S):
+ mips32_op = OPC_FLOOR_L_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_D):
+ mips32_op = OPC_FLOOR_L_D;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_S):
+ mips32_op = OPC_FLOOR_W_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_D):
+ mips32_op = OPC_FLOOR_W_D;
+ goto do_unaryfp;
+
+ /* Ceiling */
+ case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_S):
+ mips32_op = OPC_CEIL_L_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_D):
+ mips32_op = OPC_CEIL_L_D;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_S):
+ mips32_op = OPC_CEIL_W_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_D):
+ mips32_op = OPC_CEIL_W_D;
+ goto do_unaryfp;
+
+ /* Truncation */
+ case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_S):
+ mips32_op = OPC_TRUNC_L_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_D):
+ mips32_op = OPC_TRUNC_L_D;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_S):
+ mips32_op = OPC_TRUNC_W_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_D):
+ mips32_op = OPC_TRUNC_W_D;
+ goto do_unaryfp;
+
+ /* Round */
+ case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_S):
+ mips32_op = OPC_ROUND_L_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_D):
+ mips32_op = OPC_ROUND_L_D;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_S):
+ mips32_op = OPC_ROUND_W_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_D):
+ mips32_op = OPC_ROUND_W_D;
+ goto do_unaryfp;
+
+ /* Integer to floating-point conversion */
+ case FLOAT_1BIT_FMT(CVT_L, FMT_SD_S):
+ mips32_op = OPC_CVT_L_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CVT_L, FMT_SD_D):
+ mips32_op = OPC_CVT_L_D;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CVT_W, FMT_SD_S):
+ mips32_op = OPC_CVT_W_S;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CVT_W, FMT_SD_D):
+ mips32_op = OPC_CVT_W_D;
+ goto do_unaryfp;
+
+ /* Paired-foo conversions */
+ case FLOAT_1BIT_FMT(CVT_S_PL, 0):
+ mips32_op = OPC_CVT_S_PL;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CVT_S_PU, 0):
+ mips32_op = OPC_CVT_S_PU;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CVT_PW_PS, 0):
+ mips32_op = OPC_CVT_PW_PS;
+ goto do_unaryfp;
+ case FLOAT_1BIT_FMT(CVT_PS_PW, 0):
+ mips32_op = OPC_CVT_PS_PW;
+ goto do_unaryfp;
+
+ /* Floating-point moves */
+ case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_S):
+ mips32_op = OPC_MOV_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_D):
+ mips32_op = OPC_MOV_D;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_PS):
+ mips32_op = OPC_MOV_PS;
+ goto do_unaryfp;
+
+ /* Absolute value */
+ case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_S):
+ mips32_op = OPC_ABS_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_D):
+ mips32_op = OPC_ABS_D;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_PS):
+ mips32_op = OPC_ABS_PS;
+ goto do_unaryfp;
+
+ /* Negation */
+ case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_S):
+ mips32_op = OPC_NEG_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_D):
+ mips32_op = OPC_NEG_D;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_PS):
+ mips32_op = OPC_NEG_PS;
+ goto do_unaryfp;
+
+ /* Reciprocal square root step */
+ case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_S):
+ mips32_op = OPC_RSQRT1_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_D):
+ mips32_op = OPC_RSQRT1_D;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_PS):
+ mips32_op = OPC_RSQRT1_PS;
+ goto do_unaryfp;
+
+ /* Reciprocal step */
+ case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_S):
+ mips32_op = OPC_RECIP1_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_D):
+ mips32_op = OPC_RECIP1_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_PS):
+ mips32_op = OPC_RECIP1_PS;
+ goto do_unaryfp;
+
+ /* Conversions from double */
+ case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_S):
+ mips32_op = OPC_CVT_D_S;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_W):
+ mips32_op = OPC_CVT_D_W;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_L):
+ mips32_op = OPC_CVT_D_L;
+ goto do_unaryfp;
+
+ /* Conversions from single */
+ case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_D):
+ mips32_op = OPC_CVT_S_D;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_W):
+ mips32_op = OPC_CVT_S_W;
+ goto do_unaryfp;
+ case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_L):
+ mips32_op = OPC_CVT_S_L;
+ do_unaryfp:
+ gen_farith(ctx, mips32_op, -1, rs, rt, 0);
+ break;
+
+ /* Conditional moves on floating-point codes */
+ case COND_FLOAT_MOV(MOVT, 0):
+ case COND_FLOAT_MOV(MOVT, 1):
+ case COND_FLOAT_MOV(MOVT, 2):
+ case COND_FLOAT_MOV(MOVT, 3):
+ case COND_FLOAT_MOV(MOVT, 4):
+ case COND_FLOAT_MOV(MOVT, 5):
+ case COND_FLOAT_MOV(MOVT, 6):
+ case COND_FLOAT_MOV(MOVT, 7):
+ gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 1);
+ break;
+ case COND_FLOAT_MOV(MOVF, 0):
+ case COND_FLOAT_MOV(MOVF, 1):
+ case COND_FLOAT_MOV(MOVF, 2):
+ case COND_FLOAT_MOV(MOVF, 3):
+ case COND_FLOAT_MOV(MOVF, 4):
+ case COND_FLOAT_MOV(MOVF, 5):
+ case COND_FLOAT_MOV(MOVF, 6):
+ case COND_FLOAT_MOV(MOVF, 7):
+ gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 0);
+ break;
+ default:
+ MIPS_INVAL("pool32fxf");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+}
+
+static void decode_micromips32_opc (CPUState *env, DisasContext *ctx,
+ uint16_t insn_hw1, int *is_branch)
+{
+ int32_t offset;
+ uint16_t insn;
+ int rt, rs, rd, rr;
+ int16_t imm;
+ uint32_t op, minor, mips32_op;
+ uint32_t cond, fmt, cc;
+
+ insn = lduw_code(ctx->pc + 2);
+ ctx->opcode = (ctx->opcode << 16) | insn;
+
+ rt = (ctx->opcode >> 21) & 0x1f;
+ rs = (ctx->opcode >> 16) & 0x1f;
+ rd = (ctx->opcode >> 11) & 0x1f;
+ rr = (ctx->opcode >> 6) & 0x1f;
+ imm = (int16_t) ctx->opcode;
+
+ op = (ctx->opcode >> 26) & 0x3f;
+ switch (op) {
+ case POOL32A:
+ minor = ctx->opcode & 0x3f;
+ switch (minor) {
+ case 0x00:
+ minor = (ctx->opcode >> 6) & 0xf;
+ switch (minor) {
+ case SLL32:
+ mips32_op = OPC_SLL;
+ goto do_shifti;
+ case SRA:
+ mips32_op = OPC_SRA;
+ goto do_shifti;
+ case SRL32:
+ mips32_op = OPC_SRL;
+ goto do_shifti;
+ case ROTR:
+ mips32_op = OPC_ROTR;
+ do_shifti:
+ gen_shift_imm(env, ctx, mips32_op, rt, rs, rd);
+ break;
+ default:
+ goto pool32a_invalid;
+ }
+ break;
+ case 0x10:
+ minor = (ctx->opcode >> 6) & 0xf;
+ switch (minor) {
+ /* Arithmetic */
+ case ADD:
+ mips32_op = OPC_ADD;
+ goto do_arith;
+ case ADDU32:
+ mips32_op = OPC_ADDU;
+ goto do_arith;
+ case SUB:
+ mips32_op = OPC_SUB;
+ goto do_arith;
+ case SUBU32:
+ mips32_op = OPC_SUBU;
+ goto do_arith;
+ case MUL:
+ mips32_op = OPC_MUL;
+ do_arith:
+ gen_arith(env, ctx, mips32_op, rd, rs, rt);
+ break;
+ /* Shifts */
+ case SLLV:
+ mips32_op = OPC_SLLV;
+ goto do_shift;
+ case SRLV:
+ mips32_op = OPC_SRLV;
+ goto do_shift;
+ case SRAV:
+ mips32_op = OPC_SRAV;
+ goto do_shift;
+ case ROTRV:
+ mips32_op = OPC_ROTRV;
+ do_shift:
+ gen_shift(env, ctx, mips32_op, rd, rs, rt);
+ break;
+ /* Logical operations */
+ case AND:
+ mips32_op = OPC_AND;
+ goto do_logic;
+ case OR32:
+ mips32_op = OPC_OR;
+ goto do_logic;
+ case NOR:
+ mips32_op = OPC_NOR;
+ goto do_logic;
+ case XOR32:
+ mips32_op = OPC_XOR;
+ do_logic:
+ gen_logic(env, mips32_op, rd, rs, rt);
+ break;
+ /* Set less than */
+ case SLT:
+ mips32_op = OPC_SLT;
+ goto do_slt;
+ case SLTU:
+ mips32_op = OPC_SLTU;
+ do_slt:
+ gen_slt(env, mips32_op, rd, rs, rt);
+ break;
+ default:
+ goto pool32a_invalid;
+ }
+ break;
+ case 0x18:
+ minor = (ctx->opcode >> 6) & 0xf;
+ switch (minor) {
+ /* Conditional moves */
+ case MOVN:
+ mips32_op = OPC_MOVN;
+ goto do_cmov;
+ case MOVZ:
+ mips32_op = OPC_MOVZ;
+ do_cmov:
+ gen_cond_move(env, mips32_op, rd, rs, rt);
+ break;
+ case LWXS:
+ gen_ldxs(ctx, rs, rt, rd);
+ break;
+ default:
+ goto pool32a_invalid;
+ }
+ break;
+ case INS:
+ gen_bitops(ctx, OPC_INS, rt, rs, rr, rd);
+ return;
+ case EXT:
+ gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd);
+ return;
+ case POOL32AXF:
+ gen_pool32axf(env, ctx, rt, rs, is_branch);
+ break;
+ case 0x07:
+ generate_exception(ctx, EXCP_BREAK);
+ break;
+ default:
+ pool32a_invalid:
+ MIPS_INVAL("pool32a");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case POOL32B:
+ minor = (ctx->opcode >> 12) & 0xf;
+ switch (minor) {
+ case CACHE:
+ /* Treat as no-op. */
+ break;
+ case LWC2:
+ case SWC2:
+ /* COP2: Not implemented. */
+ generate_exception_err(ctx, EXCP_CpU, 2);
+ break;
+ case LWP:
+ case SWP:
+#ifdef TARGET_MIPS64
+ case LDP:
+ case SDP:
+#endif
+ gen_ldst_pair(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12));
+ break;
+ case LWM32:
+ case SWM32:
+#ifdef TARGET_MIPS64
+ case LDM:
+ case SDM:
+#endif
+ gen_ldst_multiple(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12));
+ break;
+ default:
+ MIPS_INVAL("pool32b");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case POOL32F:
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ minor = ctx->opcode & 0x3f;
+ check_cp1_enabled(ctx);
+ switch (minor) {
+ case ALNV_PS:
+ mips32_op = OPC_ALNV_PS;
+ goto do_madd;
+ case MADD_S:
+ mips32_op = OPC_MADD_S;
+ goto do_madd;
+ case MADD_D:
+ mips32_op = OPC_MADD_D;
+ goto do_madd;
+ case MADD_PS:
+ mips32_op = OPC_MADD_PS;
+ goto do_madd;
+ case MSUB_S:
+ mips32_op = OPC_MSUB_S;
+ goto do_madd;
+ case MSUB_D:
+ mips32_op = OPC_MSUB_D;
+ goto do_madd;
+ case MSUB_PS:
+ mips32_op = OPC_MSUB_PS;
+ goto do_madd;
+ case NMADD_S:
+ mips32_op = OPC_NMADD_S;
+ goto do_madd;
+ case NMADD_D:
+ mips32_op = OPC_NMADD_D;
+ goto do_madd;
+ case NMADD_PS:
+ mips32_op = OPC_NMADD_PS;
+ goto do_madd;
+ case NMSUB_S:
+ mips32_op = OPC_NMSUB_S;
+ goto do_madd;
+ case NMSUB_D:
+ mips32_op = OPC_NMSUB_D;
+ goto do_madd;
+ case NMSUB_PS:
+ mips32_op = OPC_NMSUB_PS;
+ do_madd:
+ gen_flt3_arith(ctx, mips32_op, rd, rr, rs, rt);
+ break;
+ case CABS_COND_FMT:
+ cond = (ctx->opcode >> 6) & 0xf;
+ cc = (ctx->opcode >> 13) & 0x7;
+ fmt = (ctx->opcode >> 10) & 0x3;
+ switch (fmt) {
+ case 0x0:
+ gen_cmpabs_s(ctx, cond, rt, rs, cc);
+ break;
+ case 0x1:
+ gen_cmpabs_d(ctx, cond, rt, rs, cc);
+ break;
+ case 0x2:
+ gen_cmpabs_ps(ctx, cond, rt, rs, cc);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case C_COND_FMT:
+ cond = (ctx->opcode >> 6) & 0xf;
+ cc = (ctx->opcode >> 13) & 0x7;
+ fmt = (ctx->opcode >> 10) & 0x3;
+ switch (fmt) {
+ case 0x0:
+ gen_cmp_s(ctx, cond, rt, rs, cc);
+ break;
+ case 0x1:
+ gen_cmp_d(ctx, cond, rt, rs, cc);
+ break;
+ case 0x2:
+ gen_cmp_ps(ctx, cond, rt, rs, cc);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case POOL32FXF:
+ gen_pool32fxf(env, ctx, rt, rs);
+ break;
+ case 0x00:
+ /* PLL foo */
+ switch ((ctx->opcode >> 6) & 0x7) {
+ case PLL_PS:
+ mips32_op = OPC_PLL_PS;
+ goto do_ps;
+ case PLU_PS:
+ mips32_op = OPC_PLU_PS;
+ goto do_ps;
+ case PUL_PS:
+ mips32_op = OPC_PUL_PS;
+ goto do_ps;
+ case PUU_PS:
+ mips32_op = OPC_PUU_PS;
+ goto do_ps;
+ case CVT_PS_S:
+ mips32_op = OPC_CVT_PS_S;
+ do_ps:
+ gen_farith(ctx, mips32_op, rt, rs, rd, 0);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case 0x08:
+ /* [LS][WDU]XC1 */
+ switch ((ctx->opcode >> 6) & 0x7) {
+ case LWXC1:
+ mips32_op = OPC_LWXC1;
+ goto do_ldst_cp1;
+ case SWXC1:
+ mips32_op = OPC_SWXC1;
+ goto do_ldst_cp1;
+ case LDXC1:
+ mips32_op = OPC_LDXC1;
+ goto do_ldst_cp1;
+ case SDXC1:
+ mips32_op = OPC_SDXC1;
+ goto do_ldst_cp1;
+ case LUXC1:
+ mips32_op = OPC_LUXC1;
+ goto do_ldst_cp1;
+ case SUXC1:
+ mips32_op = OPC_SUXC1;
+ do_ldst_cp1:
+ gen_flt3_ldst(ctx, mips32_op, rd, rd, rt, rs);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case 0x18:
+ /* 3D insns */
+ fmt = (ctx->opcode >> 9) & 0x3;
+ switch ((ctx->opcode >> 6) & 0x7) {
+ case RSQRT2_FMT:
+ switch (fmt) {
+ case FMT_SDPS_S:
+ mips32_op = OPC_RSQRT2_S;
+ goto do_3d;
+ case FMT_SDPS_D:
+ mips32_op = OPC_RSQRT2_D;
+ goto do_3d;
+ case FMT_SDPS_PS:
+ mips32_op = OPC_RSQRT2_PS;
+ goto do_3d;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case RECIP2_FMT:
+ switch (fmt) {
+ case FMT_SDPS_S:
+ mips32_op = OPC_RECIP2_S;
+ goto do_3d;
+ case FMT_SDPS_D:
+ mips32_op = OPC_RECIP2_D;
+ goto do_3d;
+ case FMT_SDPS_PS:
+ mips32_op = OPC_RECIP2_PS;
+ goto do_3d;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case ADDR_PS:
+ mips32_op = OPC_ADDR_PS;
+ goto do_3d;
+ case MULR_PS:
+ mips32_op = OPC_MULR_PS;
+ do_3d:
+ gen_farith(ctx, mips32_op, rt, rs, rd, 0);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case 0x20:
+ /* MOV[FT].fmt and PREFX */
+ cc = (ctx->opcode >> 13) & 0x7;
+ fmt = (ctx->opcode >> 9) & 0x3;
+ switch ((ctx->opcode >> 6) & 0x7) {
+ case MOVF_FMT:
+ switch (fmt) {
+ case FMT_SDPS_S:
+ gen_movcf_s(rs, rt, cc, 0);
+ break;
+ case FMT_SDPS_D:
+ gen_movcf_d(ctx, rs, rt, cc, 0);
+ break;
+ case FMT_SDPS_PS:
+ gen_movcf_ps(rs, rt, cc, 0);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case MOVT_FMT:
+ switch (fmt) {
+ case FMT_SDPS_S:
+ gen_movcf_s(rs, rt, cc, 1);
+ break;
+ case FMT_SDPS_D:
+ gen_movcf_d(ctx, rs, rt, cc, 1);
+ break;
+ case FMT_SDPS_PS:
+ gen_movcf_ps(rs, rt, cc, 1);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case PREFX:
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+#define FINSN_3ARG_SDPS(prfx) \
+ switch ((ctx->opcode >> 8) & 0x3) { \
+ case FMT_SDPS_S: \
+ mips32_op = OPC_##prfx##_S; \
+ goto do_fpop; \
+ case FMT_SDPS_D: \
+ mips32_op = OPC_##prfx##_D; \
+ goto do_fpop; \
+ case FMT_SDPS_PS: \
+ mips32_op = OPC_##prfx##_PS; \
+ goto do_fpop; \
+ default: \
+ goto pool32f_invalid; \
+ }
+ case 0x30:
+ /* regular FP ops */
+ switch ((ctx->opcode >> 6) & 0x3) {
+ case ADD_FMT:
+ FINSN_3ARG_SDPS(ADD);
+ break;
+ case SUB_FMT:
+ FINSN_3ARG_SDPS(SUB);
+ break;
+ case MUL_FMT:
+ FINSN_3ARG_SDPS(MUL);
+ break;
+ case DIV_FMT:
+ fmt = (ctx->opcode >> 8) & 0x3;
+ if (fmt == 1) {
+ mips32_op = OPC_DIV_D;
+ } else if (fmt == 0) {
+ mips32_op = OPC_DIV_S;
+ } else {
+ goto pool32f_invalid;
+ }
+ goto do_fpop;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ case 0x38:
+ /* cmovs */
+ switch ((ctx->opcode >> 6) & 0x3) {
+ case MOVN_FMT:
+ FINSN_3ARG_SDPS(MOVN);
+ break;
+ case MOVZ_FMT:
+ FINSN_3ARG_SDPS(MOVZ);
+ break;
+ default:
+ goto pool32f_invalid;
+ }
+ break;
+ do_fpop:
+ gen_farith(ctx, mips32_op, rt, rs, rd, 0);
+ break;
+ default:
+ pool32f_invalid:
+ MIPS_INVAL("pool32f");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ } else {
+ generate_exception_err(ctx, EXCP_CpU, 1);
+ }
+ break;
+ case POOL32I:
+ minor = (ctx->opcode >> 21) & 0x1f;
+ switch (minor) {
+ case BLTZ:
+ mips32_op = OPC_BLTZ;
+ goto do_branch;
+ case BLTZAL:
+ mips32_op = OPC_BLTZAL;
+ goto do_branch;
+ case BLTZALS:
+ mips32_op = OPC_BLTZALS;
+ goto do_branch;
+ case BGEZ:
+ mips32_op = OPC_BGEZ;
+ goto do_branch;
+ case BGEZAL:
+ mips32_op = OPC_BGEZAL;
+ goto do_branch;
+ case BGEZALS:
+ mips32_op = OPC_BGEZALS;
+ goto do_branch;
+ case BLEZ:
+ mips32_op = OPC_BLEZ;
+ goto do_branch;
+ case BGTZ:
+ mips32_op = OPC_BGTZ;
+ do_branch:
+ gen_compute_branch(ctx, mips32_op, 4, rs, -1, imm << 1);
+ *is_branch = 1;
+ break;
+
+ /* Traps */
+ case TLTI:
+ mips32_op = OPC_TLTI;
+ goto do_trapi;
+ case TGEI:
+ mips32_op = OPC_TGEI;
+ goto do_trapi;
+ case TLTIU:
+ mips32_op = OPC_TLTIU;
+ goto do_trapi;
+ case TGEIU:
+ mips32_op = OPC_TGEIU;
+ goto do_trapi;
+ case TNEI:
+ mips32_op = OPC_TNEI;
+ goto do_trapi;
+ case TEQI:
+ mips32_op = OPC_TEQI;
+ do_trapi:
+ gen_trap(ctx, mips32_op, rs, -1, imm);
+ break;
+
+ case BNEZC:
+ case BEQZC:
+ gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ,
+ 4, rs, 0, imm << 1);
+ /* Compact branches don't have a delay slot, so just let
+ the normal delay slot handling take us to the branch
+ target. */
+ break;
+ case LUI:
+ gen_logic_imm(env, OPC_LUI, rs, -1, imm);
+ break;
+ case SYNCI:
+ break;
+ case BC2F:
+ case BC2T:
+ /* COP2: Not implemented. */
+ generate_exception_err(ctx, EXCP_CpU, 2);
+ break;
+ case BC1F:
+ mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1FANY2 : OPC_BC1F;
+ goto do_cp1branch;
+ case BC1T:
+ mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1TANY2 : OPC_BC1T;
+ goto do_cp1branch;
+ case BC1ANY4F:
+ mips32_op = OPC_BC1FANY4;
+ goto do_cp1mips3d;
+ case BC1ANY4T:
+ mips32_op = OPC_BC1TANY4;
+ do_cp1mips3d:
+ check_cop1x(ctx);
+ check_insn(env, ctx, ASE_MIPS3D);
+ /* Fall through */
+ do_cp1branch:
+ gen_compute_branch1(env, ctx, mips32_op,
+ (ctx->opcode >> 18) & 0x7, imm << 1);
+ *is_branch = 1;
+ break;
+ case BPOSGE64:
+ case BPOSGE32:
+ /* MIPS DSP: not implemented */
+ /* Fall through */
+ default:
+ MIPS_INVAL("pool32i");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case POOL32C:
+ minor = (ctx->opcode >> 12) & 0xf;
+ switch (minor) {
+ case LWL:
+ mips32_op = OPC_LWL;
+ goto do_ld_lr;
+ case SWL:
+ mips32_op = OPC_SWL;
+ goto do_st_lr;
+ case LWR:
+ mips32_op = OPC_LWR;
+ goto do_ld_lr;
+ case SWR:
+ mips32_op = OPC_SWR;
+ goto do_st_lr;
+#if defined(TARGET_MIPS64)
+ case LDL:
+ mips32_op = OPC_LDL;
+ goto do_ld_lr;
+ case SDL:
+ mips32_op = OPC_SDL;
+ goto do_st_lr;
+ case LDR:
+ mips32_op = OPC_LDR;
+ goto do_ld_lr;
+ case SDR:
+ mips32_op = OPC_SDR;
+ goto do_st_lr;
+ case LWU:
+ mips32_op = OPC_LWU;
+ goto do_ld_lr;
+ case LLD:
+ mips32_op = OPC_LLD;
+ goto do_ld_lr;
+#endif
+ case LL:
+ mips32_op = OPC_LL;
+ goto do_ld_lr;
+ do_ld_lr:
+ gen_ld(env, ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12));
+ break;
+ do_st_lr:
+ gen_st(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12));
+ break;
+ case SC:
+ gen_st_cond(ctx, OPC_SC, rt, rs, SIMM(ctx->opcode, 0, 12));
+ break;
+#if defined(TARGET_MIPS64)
+ case SCD:
+ gen_st_cond(ctx, OPC_SCD, rt, rs, SIMM(ctx->opcode, 0, 12));
+ break;
+#endif
+ case PREF:
+ /* Treat as no-op */
+ break;
+ default:
+ MIPS_INVAL("pool32c");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case ADDI32:
+ mips32_op = OPC_ADDI;
+ goto do_addi;
+ case ADDIU32:
+ mips32_op = OPC_ADDIU;
+ do_addi:
+ gen_arith_imm(env, ctx, mips32_op, rt, rs, imm);
+ break;
+
+ /* Logical operations */
+ case ORI32:
+ mips32_op = OPC_ORI;
+ goto do_logici;
+ case XORI32:
+ mips32_op = OPC_XORI;
+ goto do_logici;
+ case ANDI32:
+ mips32_op = OPC_ANDI;
+ do_logici:
+ gen_logic_imm(env, mips32_op, rt, rs, imm);
+ break;
+
+ /* Set less than immediate */
+ case SLTI32:
+ mips32_op = OPC_SLTI;
+ goto do_slti;
+ case SLTIU32:
+ mips32_op = OPC_SLTIU;
+ do_slti:
+ gen_slt_imm(env, mips32_op, rt, rs, imm);
+ break;
+ case JALX32:
+ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
+ gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset);
+ *is_branch = 1;
+ break;
+ case JALS32:
+ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1;
+ gen_compute_branch(ctx, OPC_JALS, 4, rt, rs, offset);
+ *is_branch = 1;
+ break;
+ case BEQ32:
+ gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1);
+ *is_branch = 1;
+ break;
+ case BNE32:
+ gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1);
+ *is_branch = 1;
+ break;
+ case J32:
+ gen_compute_branch(ctx, OPC_J, 4, rt, rs,
+ (int32_t)(ctx->opcode & 0x3FFFFFF) << 1);
+ *is_branch = 1;
+ break;
+ case JAL32:
+ gen_compute_branch(ctx, OPC_JAL, 4, rt, rs,
+ (int32_t)(ctx->opcode & 0x3FFFFFF) << 1);
+ *is_branch = 1;
+ break;
+ /* Floating point (COP1) */
+ case LWC132:
+ mips32_op = OPC_LWC1;
+ goto do_cop1;
+ case LDC132:
+ mips32_op = OPC_LDC1;
+ goto do_cop1;
+ case SWC132:
+ mips32_op = OPC_SWC1;
+ goto do_cop1;
+ case SDC132:
+ mips32_op = OPC_SDC1;
+ do_cop1:
+ gen_cop1_ldst(env, ctx, mips32_op, rt, rs, imm);
+ break;
+ case ADDIUPC:
+ {
+ int reg = mmreg(ZIMM(ctx->opcode, 23, 3));
+ int offset = SIMM(ctx->opcode, 0, 23) << 2;
+
+ gen_addiupc(ctx, reg, offset, 0, 0);
+ }
+ break;
+ /* Loads and stores */
+ case LB32:
+ mips32_op = OPC_LB;
+ goto do_ld;
+ case LBU32:
+ mips32_op = OPC_LBU;
+ goto do_ld;
+ case LH32:
+ mips32_op = OPC_LH;
+ goto do_ld;
+ case LHU32:
+ mips32_op = OPC_LHU;
+ goto do_ld;
+ case LW32:
+ mips32_op = OPC_LW;
+ goto do_ld;
+#ifdef TARGET_MIPS64
+ case LD32:
+ mips32_op = OPC_LD;
+ goto do_ld;
+ case SD32:
+ mips32_op = OPC_SD;
+ goto do_st;
+#endif
+ case SB32:
+ mips32_op = OPC_SB;
+ goto do_st;
+ case SH32:
+ mips32_op = OPC_SH;
+ goto do_st;
+ case SW32:
+ mips32_op = OPC_SW;
+ goto do_st;
+ do_ld:
+ gen_ld(env, ctx, mips32_op, rt, rs, imm);
+ break;
+ do_st:
+ gen_st(ctx, mips32_op, rt, rs, imm);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+}
+
+static int decode_micromips_opc (CPUState *env, DisasContext *ctx, int *is_branch)
+{
+ uint32_t op;
+
+ /* make sure instructions are on a halfword boundary */
+ if (ctx->pc & 0x1) {
+ env->CP0_BadVAddr = ctx->pc;
+ generate_exception(ctx, EXCP_AdEL);
+ ctx->bstate = BS_STOP;
+ return 2;
+ }
+
+ op = (ctx->opcode >> 10) & 0x3f;
+ /* Enforce properly-sized instructions in a delay slot */
+ if (ctx->hflags & MIPS_HFLAG_BMASK) {
+ int bits = ctx->hflags & MIPS_HFLAG_BMASK_EXT;
+
+ switch (op) {
+ case POOL32A:
+ case POOL32B:
+ case POOL32I:
+ case POOL32C:
+ case ADDI32:
+ case ADDIU32:
+ case ORI32:
+ case XORI32:
+ case SLTI32:
+ case SLTIU32:
+ case ANDI32:
+ case JALX32:
+ case LBU32:
+ case LHU32:
+ case POOL32F:
+ case JALS32:
+ case BEQ32:
+ case BNE32:
+ case J32:
+ case JAL32:
+ case SB32:
+ case SH32:
+ case POOL32S:
+ case ADDIUPC:
+ case SWC132:
+ case SDC132:
+ case SD32:
+ case SW32:
+ case LB32:
+ case LH32:
+ case DADDIU32:
+ case POOL48A: /* ??? */
+ case LWC132:
+ case LDC132:
+ case LD32:
+ case LW32:
+ if (bits & MIPS_HFLAG_BDS16) {
+ generate_exception(ctx, EXCP_RI);
+ /* Just stop translation; the user is confused. */
+ ctx->bstate = BS_STOP;
+ return 2;
+ }
+ break;
+ case POOL16A:
+ case POOL16B:
+ case POOL16C:
+ case LWGP16:
+ case POOL16F:
+ case LBU16:
+ case LHU16:
+ case LWSP16:
+ case LW16:
+ case SB16:
+ case SH16:
+ case SWSP16:
+ case SW16:
+ case MOVE16:
+ case ANDI16:
+ case POOL16D:
+ case POOL16E:
+ case BEQZ16:
+ case BNEZ16:
+ case B16:
+ case LI16:
+ if (bits & MIPS_HFLAG_BDS32) {
+ generate_exception(ctx, EXCP_RI);
+ /* Just stop translation; the user is confused. */
+ ctx->bstate = BS_STOP;
+ return 2;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ switch (op) {
+ case POOL16A:
+ {
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rs1 = mmreg(uMIPS_RS1(ctx->opcode));
+ int rs2 = mmreg(uMIPS_RS2(ctx->opcode));
+ uint32_t opc = 0;
+
+ switch (ctx->opcode & 0x1) {
+ case ADDU16:
+ opc = OPC_ADDU;
+ break;
+ case SUBU16:
+ opc = OPC_SUBU;
+ break;
+ }
+
+ gen_arith(env, ctx, opc, rd, rs1, rs2);
+ }
+ break;
+ case POOL16B:
+ {
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rs = mmreg(uMIPS_RS(ctx->opcode));
+ int amount = (ctx->opcode >> 1) & 0x7;
+ uint32_t opc = 0;
+ amount = amount == 0 ? 8 : amount;
+
+ switch (ctx->opcode & 0x1) {
+ case SLL16:
+ opc = OPC_SLL;
+ break;
+ case SRL16:
+ opc = OPC_SRL;
+ break;
+ }
+
+ gen_shift_imm(env, ctx, opc, rd, rs, amount);
+ }
+ break;
+ case POOL16C:
+ gen_pool16c_insn(env, ctx, is_branch);
+ break;
+ case LWGP16:
+ {
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rb = 28; /* GP */
+ int16_t offset = SIMM(ctx->opcode, 0, 7) << 2;
+
+ gen_ld(env, ctx, OPC_LW, rd, rb, offset);
+ }
+ break;
+ case POOL16F:
+ if (ctx->opcode & 1) {
+ generate_exception(ctx, EXCP_RI);
+ } else {
+ /* MOVEP */
+ int enc_dest = uMIPS_RD(ctx->opcode);
+ int enc_rt = uMIPS_RS2(ctx->opcode);
+ int enc_rs = uMIPS_RS1(ctx->opcode);
+ int rd, rs, re, rt;
+ static const int rd_enc[] = { 5, 5, 6, 4, 4, 4, 4, 4 };
+ static const int re_enc[] = { 6, 7, 7, 21, 22, 5, 6, 7 };
+ static const int rs_rt_enc[] = { 0, 17, 2, 3, 16, 18, 19, 20 };
+
+ rd = rd_enc[enc_dest];
+ re = re_enc[enc_dest];
+ rs = rs_rt_enc[enc_rs];
+ rt = rs_rt_enc[enc_rt];
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0);
+ gen_arith_imm(env, ctx, OPC_ADDIU, re, rt, 0);
+ }
+ break;
+ case LBU16:
+ {
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rb = mmreg(uMIPS_RS(ctx->opcode));
+ int16_t offset = ZIMM(ctx->opcode, 0, 4);
+ offset = (offset == 0xf ? -1 : offset);
+
+ gen_ld(env, ctx, OPC_LBU, rd, rb, offset);
+ }
+ break;
+ case LHU16:
+ {
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rb = mmreg(uMIPS_RS(ctx->opcode));
+ int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1;
+
+ gen_ld(env, ctx, OPC_LHU, rd, rb, offset);
+ }
+ break;
+ case LWSP16:
+ {
+ int rd = (ctx->opcode >> 5) & 0x1f;
+ int rb = 29; /* SP */
+ int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2;
+
+ gen_ld(env, ctx, OPC_LW, rd, rb, offset);
+ }
+ break;
+ case LW16:
+ {
+ int rd = mmreg(uMIPS_RD(ctx->opcode));
+ int rb = mmreg(uMIPS_RS(ctx->opcode));
+ int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2;
+
+ gen_ld(env, ctx, OPC_LW, rd, rb, offset);
+ }
+ break;
+ case SB16:
+ {
+ int rd = mmreg2(uMIPS_RD(ctx->opcode));
+ int rb = mmreg(uMIPS_RS(ctx->opcode));
+ int16_t offset = ZIMM(ctx->opcode, 0, 4);
+
+ gen_st(ctx, OPC_SB, rd, rb, offset);
+ }
+ break;
+ case SH16:
+ {
+ int rd = mmreg2(uMIPS_RD(ctx->opcode));
+ int rb = mmreg(uMIPS_RS(ctx->opcode));
+ int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1;
+
+ gen_st(ctx, OPC_SH, rd, rb, offset);
+ }
+ break;
+ case SWSP16:
+ {
+ int rd = (ctx->opcode >> 5) & 0x1f;
+ int rb = 29; /* SP */
+ int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2;
+
+ gen_st(ctx, OPC_SW, rd, rb, offset);
+ }
+ break;
+ case SW16:
+ {
+ int rd = mmreg2(uMIPS_RD(ctx->opcode));
+ int rb = mmreg(uMIPS_RS(ctx->opcode));
+ int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2;
+
+ gen_st(ctx, OPC_SW, rd, rb, offset);
+ }
+ break;
+ case MOVE16:
+ {
+ int rd = uMIPS_RD5(ctx->opcode);
+ int rs = uMIPS_RS5(ctx->opcode);
+
+ gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0);
+ }
+ break;
+ case ANDI16:
+ gen_andi16(env, ctx);
+ break;
+ case POOL16D:
+ switch (ctx->opcode & 0x1) {
+ case ADDIUS5:
+ gen_addius5(env, ctx);
+ break;
+ case ADDIUSP:
+ gen_addiusp(env, ctx);
+ break;
+ }
+ break;
+ case POOL16E:
+ switch (ctx->opcode & 0x1) {
+ case ADDIUR2:
+ gen_addiur2(env, ctx);
+ break;
+ case ADDIUR1SP:
+ gen_addiur1sp(env, ctx);
+ break;
+ }
+ break;
+ case B16:
+ gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0,
+ SIMM(ctx->opcode, 0, 10) << 1);
+ *is_branch = 1;
+ break;
+ case BNEZ16:
+ case BEQZ16:
+ gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2,
+ mmreg(uMIPS_RD(ctx->opcode)),
+ 0, SIMM(ctx->opcode, 0, 7) << 1);
+ *is_branch = 1;
+ break;
+ case LI16:
+ {
+ int reg = mmreg(uMIPS_RD(ctx->opcode));
+ int imm = ZIMM(ctx->opcode, 0, 7);
+
+ imm = (imm == 0x7f ? -1 : imm);
+ tcg_gen_movi_tl(cpu_gpr[reg], imm);
+ }
+ break;
+ case RES_20:
+ case RES_28:
+ case RES_29:
+ case RES_30:
+ case RES_31:
+ case RES_38:
+ case RES_39:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ default:
+ decode_micromips32_opc (env, ctx, op, is_branch);
+ return 4;
+ }
+
+ return 2;
+}
+
+/* SmartMIPS extension to MIPS32 */
+
+#if defined(TARGET_MIPS64)
+
+/* MDMX extension to MIPS64 */
+
+#endif
+
+static void decode_opc (CPUState *env, DisasContext *ctx, int *is_branch)
+{
+ int32_t offset;
+ int rs, rt, rd, sa;
+ uint32_t op, op1, op2;
+ int16_t imm;
+
+ /* make sure instructions are on a word boundary */
+ if (ctx->pc & 0x3) {
+ env->CP0_BadVAddr = ctx->pc;
+ generate_exception(ctx, EXCP_AdEL);
+ return;
+ }
+
+ /* Handle blikely not taken case */
+ if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) {
+ int l1 = gen_new_label();
+
+ MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4);
+ tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1);
+ tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK);
+ gen_goto_tb(ctx, 1, ctx->pc + 4);
+ gen_set_label(l1);
+ }
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(ctx->pc);
+
+ op = MASK_OP_MAJOR(ctx->opcode);
+ rs = (ctx->opcode >> 21) & 0x1f;
+ rt = (ctx->opcode >> 16) & 0x1f;
+ rd = (ctx->opcode >> 11) & 0x1f;
+ sa = (ctx->opcode >> 6) & 0x1f;
+ imm = (int16_t)ctx->opcode;
+ switch (op) {
+ case OPC_SPECIAL:
+ op1 = MASK_SPECIAL(ctx->opcode);
+ switch (op1) {
+ case OPC_SLL: /* Shift with immediate */
+ case OPC_SRA:
+ gen_shift_imm(env, ctx, op1, rd, rt, sa);
+ break;
+ case OPC_SRL:
+ switch ((ctx->opcode >> 21) & 0x1f) {
+ case 1:
+ /* rotr is decoded as srl on non-R2 CPUs */
+ if (env->insn_flags & ISA_MIPS32R2) {
+ op1 = OPC_ROTR;
+ }
+ /* Fallthrough */
+ case 0:
+ gen_shift_imm(env, ctx, op1, rd, rt, sa);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_MOVN: /* Conditional move */
+ case OPC_MOVZ:
+ check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32 |
+ INSN_LOONGSON2E | INSN_LOONGSON2F);
+ gen_cond_move(env, op1, rd, rs, rt);
+ break;
+ case OPC_ADD ... OPC_SUBU:
+ gen_arith(env, ctx, op1, rd, rs, rt);
+ break;
+ case OPC_SLLV: /* Shifts */
+ case OPC_SRAV:
+ gen_shift(env, ctx, op1, rd, rs, rt);
+ break;
+ case OPC_SRLV:
+ switch ((ctx->opcode >> 6) & 0x1f) {
+ case 1:
+ /* rotrv is decoded as srlv on non-R2 CPUs */
+ if (env->insn_flags & ISA_MIPS32R2) {
+ op1 = OPC_ROTRV;
+ }
+ /* Fallthrough */
+ case 0:
+ gen_shift(env, ctx, op1, rd, rs, rt);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_SLT: /* Set on less than */
+ case OPC_SLTU:
+ gen_slt(env, op1, rd, rs, rt);
+ break;
+ case OPC_AND: /* Logic*/
+ case OPC_OR:
+ case OPC_NOR:
+ case OPC_XOR:
+ gen_logic(env, op1, rd, rs, rt);
+ break;
+ case OPC_MULT ... OPC_DIVU:
+ if (sa) {
+ check_insn(env, ctx, INSN_VR54XX);
+ op1 = MASK_MUL_VR54XX(ctx->opcode);
+ gen_mul_vr54xx(ctx, op1, rd, rs, rt);
+ } else
+ gen_muldiv(ctx, op1, rs, rt);
+ break;
+ case OPC_JR ... OPC_JALR:
+ gen_compute_branch(ctx, op1, 4, rs, rd, sa);
+ *is_branch = 1;
+ break;
+ case OPC_TGE ... OPC_TEQ: /* Traps */
+ case OPC_TNE:
+ gen_trap(ctx, op1, rs, rt, -1);
+ break;
+ case OPC_MFHI: /* Move from HI/LO */
+ case OPC_MFLO:
+ gen_HILO(ctx, op1, rd);
+ break;
+ case OPC_MTHI:
+ case OPC_MTLO: /* Move to HI/LO */
+ gen_HILO(ctx, op1, rs);
+ break;
+ case OPC_PMON: /* Pmon entry point, also R4010 selsl */
+#ifdef MIPS_STRICT_STANDARD
+ MIPS_INVAL("PMON / selsl");
+ generate_exception(ctx, EXCP_RI);
+#else
+ gen_helper_0i(pmon, sa);
+#endif
+ break;
+ case OPC_SYSCALL:
+ generate_exception(ctx, EXCP_SYSCALL);
+ ctx->bstate = BS_STOP;
+ break;
+ case OPC_BREAK:
+ generate_exception(ctx, EXCP_BREAK);
+ break;
+ case OPC_SPIM:
+#ifdef MIPS_STRICT_STANDARD
+ MIPS_INVAL("SPIM");
+ generate_exception(ctx, EXCP_RI);
+#else
+ /* Implemented as RI exception for now. */
+ MIPS_INVAL("spim (unofficial)");
+ generate_exception(ctx, EXCP_RI);
+#endif
+ break;
+ case OPC_SYNC:
+ /* Treat as NOP. */
+ break;
+
+ case OPC_MOVCI:
+ check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32);
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ check_cp1_enabled(ctx);
+ gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7,
+ (ctx->opcode >> 16) & 1);
+ } else {
+ generate_exception_err(ctx, EXCP_CpU, 1);
+ }
+ break;
+
+#if defined(TARGET_MIPS64)
+ /* MIPS64 specific opcodes */
+ case OPC_DSLL:
+ case OPC_DSRA:
+ case OPC_DSLL32:
+ case OPC_DSRA32:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, op1, rd, rt, sa);
+ break;
+ case OPC_DSRL:
+ switch ((ctx->opcode >> 21) & 0x1f) {
+ case 1:
+ /* drotr is decoded as dsrl on non-R2 CPUs */
+ if (env->insn_flags & ISA_MIPS32R2) {
+ op1 = OPC_DROTR;
+ }
+ /* Fallthrough */
+ case 0:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, op1, rd, rt, sa);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_DSRL32:
+ switch ((ctx->opcode >> 21) & 0x1f) {
+ case 1:
+ /* drotr32 is decoded as dsrl32 on non-R2 CPUs */
+ if (env->insn_flags & ISA_MIPS32R2) {
+ op1 = OPC_DROTR32;
+ }
+ /* Fallthrough */
+ case 0:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_shift_imm(env, ctx, op1, rd, rt, sa);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_DADD ... OPC_DSUBU:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_arith(env, ctx, op1, rd, rs, rt);
+ break;
+ case OPC_DSLLV:
+ case OPC_DSRAV:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_shift(env, ctx, op1, rd, rs, rt);
+ break;
+ case OPC_DSRLV:
+ switch ((ctx->opcode >> 6) & 0x1f) {
+ case 1:
+ /* drotrv is decoded as dsrlv on non-R2 CPUs */
+ if (env->insn_flags & ISA_MIPS32R2) {
+ op1 = OPC_DROTRV;
+ }
+ /* Fallthrough */
+ case 0:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_shift(env, ctx, op1, rd, rs, rt);
+ break;
+ default:
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_DMULT ... OPC_DDIVU:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_muldiv(ctx, op1, rs, rt);
+ break;
+#endif
+ default: /* Invalid */
+ MIPS_INVAL("special");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_SPECIAL2:
+ op1 = MASK_SPECIAL2(ctx->opcode);
+ switch (op1) {
+ case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */
+ case OPC_MSUB ... OPC_MSUBU:
+ check_insn(env, ctx, ISA_MIPS32);
+ gen_muldiv(ctx, op1, rs, rt);
+ break;
+ case OPC_MUL:
+ gen_arith(env, ctx, op1, rd, rs, rt);
+ break;
+ case OPC_CLO:
+ case OPC_CLZ:
+ check_insn(env, ctx, ISA_MIPS32);
+ gen_cl(ctx, op1, rd, rs);
+ break;
+ case OPC_SDBBP:
+ /* XXX: not clear which exception should be raised
+ * when in debug mode...
+ */
+ check_insn(env, ctx, ISA_MIPS32);
+ if (!(ctx->hflags & MIPS_HFLAG_DM)) {
+ generate_exception(ctx, EXCP_DBp);
+ } else {
+ generate_exception(ctx, EXCP_DBp);
+ }
+ /* Treat as NOP. */
+ break;
+ case OPC_DIV_G_2F:
+ case OPC_DIVU_G_2F:
+ case OPC_MULT_G_2F:
+ case OPC_MULTU_G_2F:
+ case OPC_MOD_G_2F:
+ case OPC_MODU_G_2F:
+ check_insn(env, ctx, INSN_LOONGSON2F);
+ gen_loongson_integer(ctx, op1, rd, rs, rt);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DCLO:
+ case OPC_DCLZ:
+ check_insn(env, ctx, ISA_MIPS64);
+ check_mips_64(ctx);
+ gen_cl(ctx, op1, rd, rs);
+ break;
+ case OPC_DMULT_G_2F:
+ case OPC_DMULTU_G_2F:
+ case OPC_DDIV_G_2F:
+ case OPC_DDIVU_G_2F:
+ case OPC_DMOD_G_2F:
+ case OPC_DMODU_G_2F:
+ check_insn(env, ctx, INSN_LOONGSON2F);
+ gen_loongson_integer(ctx, op1, rd, rs, rt);
+ break;
+#endif
+ default: /* Invalid */
+ MIPS_INVAL("special2");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_SPECIAL3:
+ op1 = MASK_SPECIAL3(ctx->opcode);
+ switch (op1) {
+ case OPC_EXT:
+ case OPC_INS:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_bitops(ctx, op1, rt, rs, sa, rd);
+ break;
+ case OPC_BSHFL:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ op2 = MASK_BSHFL(ctx->opcode);
+ gen_bshfl(ctx, op2, rt, rd);
+ break;
+ case OPC_RDHWR:
+ gen_rdhwr(env, ctx, rt, rd);
+ break;
+ case OPC_FORK:
+ check_insn(env, ctx, ASE_MT);
+ {
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ gen_load_gpr(t0, rt);
+ gen_load_gpr(t1, rs);
+ gen_helper_fork(t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ }
+ break;
+ case OPC_YIELD:
+ check_insn(env, ctx, ASE_MT);
+ {
+ TCGv t0 = tcg_temp_new();
+
+ save_cpu_state(ctx, 1);
+ gen_load_gpr(t0, rs);
+ gen_helper_yield(t0, t0);
+ gen_store_gpr(t0, rd);
+ tcg_temp_free(t0);
+ }
+ break;
+ case OPC_DIV_G_2E ... OPC_DIVU_G_2E:
+ case OPC_MULT_G_2E ... OPC_MULTU_G_2E:
+ case OPC_MOD_G_2E ... OPC_MODU_G_2E:
+ check_insn(env, ctx, INSN_LOONGSON2E);
+ gen_loongson_integer(ctx, op1, rd, rs, rt);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DEXTM ... OPC_DEXT:
+ case OPC_DINSM ... OPC_DINS:
+ check_insn(env, ctx, ISA_MIPS64R2);
+ check_mips_64(ctx);
+ gen_bitops(ctx, op1, rt, rs, sa, rd);
+ break;
+ case OPC_DBSHFL:
+ check_insn(env, ctx, ISA_MIPS64R2);
+ check_mips_64(ctx);
+ op2 = MASK_DBSHFL(ctx->opcode);
+ gen_bshfl(ctx, op2, rt, rd);
+ break;
+ case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E:
+ case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E:
+ case OPC_DMOD_G_2E ... OPC_DMODU_G_2E:
+ check_insn(env, ctx, INSN_LOONGSON2E);
+ gen_loongson_integer(ctx, op1, rd, rs, rt);
+ break;
+#endif
+ default: /* Invalid */
+ MIPS_INVAL("special3");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_REGIMM:
+ op1 = MASK_REGIMM(ctx->opcode);
+ switch (op1) {
+ case OPC_BLTZ ... OPC_BGEZL: /* REGIMM branches */
+ case OPC_BLTZAL ... OPC_BGEZALL:
+ gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2);
+ *is_branch = 1;
+ break;
+ case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */
+ case OPC_TNEI:
+ gen_trap(ctx, op1, rs, -1, imm);
+ break;
+ case OPC_SYNCI:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ /* Treat as NOP. */
+ break;
+ default: /* Invalid */
+ MIPS_INVAL("regimm");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_CP0:
+ check_cp0_enabled(ctx);
+ op1 = MASK_CP0(ctx->opcode);
+ switch (op1) {
+ case OPC_MFC0:
+ case OPC_MTC0:
+ case OPC_MFTR:
+ case OPC_MTTR:
+#if defined(TARGET_MIPS64)
+ case OPC_DMFC0:
+ case OPC_DMTC0:
+#endif
+#ifndef CONFIG_USER_ONLY
+ gen_cp0(env, ctx, op1, rt, rd);
+#endif /* !CONFIG_USER_ONLY */
+ break;
+ case OPC_C0_FIRST ... OPC_C0_LAST:
+#ifndef CONFIG_USER_ONLY
+ gen_cp0(env, ctx, MASK_C0(ctx->opcode), rt, rd);
+#endif /* !CONFIG_USER_ONLY */
+ break;
+ case OPC_MFMC0:
+#ifndef CONFIG_USER_ONLY
+ {
+ TCGv t0 = tcg_temp_new();
+
+ op2 = MASK_MFMC0(ctx->opcode);
+ switch (op2) {
+ case OPC_DMT:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dmt(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case OPC_EMT:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_emt(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case OPC_DVPE:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_dvpe(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case OPC_EVPE:
+ check_insn(env, ctx, ASE_MT);
+ gen_helper_evpe(t0);
+ gen_store_gpr(t0, rt);
+ break;
+ case OPC_DI:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ save_cpu_state(ctx, 1);
+ gen_helper_di(t0);
+ gen_store_gpr(t0, rt);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ case OPC_EI:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ save_cpu_state(ctx, 1);
+ gen_helper_ei(t0);
+ gen_store_gpr(t0, rt);
+ /* Stop translation as we may have switched the execution mode */
+ ctx->bstate = BS_STOP;
+ break;
+ default: /* Invalid */
+ MIPS_INVAL("mfmc0");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ tcg_temp_free(t0);
+ }
+#endif /* !CONFIG_USER_ONLY */
+ break;
+ case OPC_RDPGPR:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_load_srsgpr(rt, rd);
+ break;
+ case OPC_WRPGPR:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ gen_store_srsgpr(rt, rd);
+ break;
+ default:
+ MIPS_INVAL("cp0");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+ break;
+ case OPC_ADDI: /* Arithmetic with immediate opcode */
+ case OPC_ADDIU:
+ gen_arith_imm(env, ctx, op, rt, rs, imm);
+ break;
+ case OPC_SLTI: /* Set on less than with immediate opcode */
+ case OPC_SLTIU:
+ gen_slt_imm(env, op, rt, rs, imm);
+ break;
+ case OPC_ANDI: /* Arithmetic with immediate opcode */
+ case OPC_LUI:
+ case OPC_ORI:
+ case OPC_XORI:
+ gen_logic_imm(env, op, rt, rs, imm);
+ break;
+ case OPC_J ... OPC_JAL: /* Jump */
+ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
+ gen_compute_branch(ctx, op, 4, rs, rt, offset);
+ *is_branch = 1;
+ break;
+ case OPC_BEQ ... OPC_BGTZ: /* Branch */
+ case OPC_BEQL ... OPC_BGTZL:
+ gen_compute_branch(ctx, op, 4, rs, rt, imm << 2);
+ *is_branch = 1;
+ break;
+ case OPC_LB ... OPC_LWR: /* Load and stores */
+ case OPC_LL:
+ gen_ld(env, ctx, op, rt, rs, imm);
+ break;
+ case OPC_SB ... OPC_SW:
+ case OPC_SWR:
+ gen_st(ctx, op, rt, rs, imm);
+ break;
+ case OPC_SC:
+ gen_st_cond(ctx, op, rt, rs, imm);
+ break;
+ case OPC_CACHE:
+ check_insn(env, ctx, ISA_MIPS3 | ISA_MIPS32);
+ /* Treat as NOP. */
+ break;
+ case OPC_PREF:
+ check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32);
+ /* Treat as NOP. */
+ break;
+
+ /* Floating point (COP1). */
+ case OPC_LWC1:
+ case OPC_LDC1:
+ case OPC_SWC1:
+ case OPC_SDC1:
+ gen_cop1_ldst(env, ctx, op, rt, rs, imm);
+ break;
+
+ case OPC_CP1:
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ check_cp1_enabled(ctx);
+ op1 = MASK_CP1(ctx->opcode);
+ switch (op1) {
+ case OPC_MFHC1:
+ case OPC_MTHC1:
+ check_insn(env, ctx, ISA_MIPS32R2);
+ case OPC_MFC1:
+ case OPC_CFC1:
+ case OPC_MTC1:
+ case OPC_CTC1:
+ gen_cp1(ctx, op1, rt, rd);
+ break;
+#if defined(TARGET_MIPS64)
+ case OPC_DMFC1:
+ case OPC_DMTC1:
+ check_insn(env, ctx, ISA_MIPS3);
+ gen_cp1(ctx, op1, rt, rd);
+ break;
+#endif
+ case OPC_BC1ANY2:
+ case OPC_BC1ANY4:
+ check_cop1x(ctx);
+ check_insn(env, ctx, ASE_MIPS3D);
+ /* fall through */
+ case OPC_BC1:
+ gen_compute_branch1(env, ctx, MASK_BC1(ctx->opcode),
+ (rt >> 2) & 0x7, imm << 2);
+ *is_branch = 1;
+ break;
+ case OPC_S_FMT:
+ case OPC_D_FMT:
+ case OPC_W_FMT:
+ case OPC_L_FMT:
+ case OPC_PS_FMT:
+ gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
+ (imm >> 8) & 0x7);
+ break;
+ default:
+ MIPS_INVAL("cp1");
+ generate_exception (ctx, EXCP_RI);
+ break;
+ }
+ } else {
+ generate_exception_err(ctx, EXCP_CpU, 1);
+ }
+ break;
+
+ /* COP2. */
+ case OPC_LWC2:
+ case OPC_LDC2:
+ case OPC_SWC2:
+ case OPC_SDC2:
+ case OPC_CP2:
+ /* COP2: Not implemented. */
+ generate_exception_err(ctx, EXCP_CpU, 2);
+ break;
+
+ case OPC_CP3:
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ check_cp1_enabled(ctx);
+ op1 = MASK_CP3(ctx->opcode);
+ switch (op1) {
+ case OPC_LWXC1:
+ case OPC_LDXC1:
+ case OPC_LUXC1:
+ case OPC_SWXC1:
+ case OPC_SDXC1:
+ case OPC_SUXC1:
+ gen_flt3_ldst(ctx, op1, sa, rd, rs, rt);
+ break;
+ case OPC_PREFX:
+ /* Treat as NOP. */
+ break;
+ case OPC_ALNV_PS:
+ case OPC_MADD_S:
+ case OPC_MADD_D:
+ case OPC_MADD_PS:
+ case OPC_MSUB_S:
+ case OPC_MSUB_D:
+ case OPC_MSUB_PS:
+ case OPC_NMADD_S:
+ case OPC_NMADD_D:
+ case OPC_NMADD_PS:
+ case OPC_NMSUB_S:
+ case OPC_NMSUB_D:
+ case OPC_NMSUB_PS:
+ gen_flt3_arith(ctx, op1, sa, rs, rd, rt);
+ break;
+ default:
+ MIPS_INVAL("cp3");
+ generate_exception (ctx, EXCP_RI);
+ break;
+ }
+ } else {
+ generate_exception_err(ctx, EXCP_CpU, 1);
+ }
+ break;
+
+#if defined(TARGET_MIPS64)
+ /* MIPS64 opcodes */
+ case OPC_LWU:
+ case OPC_LDL ... OPC_LDR:
+ case OPC_LLD:
+ case OPC_LD:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_ld(env, ctx, op, rt, rs, imm);
+ break;
+ case OPC_SDL ... OPC_SDR:
+ case OPC_SD:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_st(ctx, op, rt, rs, imm);
+ break;
+ case OPC_SCD:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_st_cond(ctx, op, rt, rs, imm);
+ break;
+ case OPC_DADDI:
+ case OPC_DADDIU:
+ check_insn(env, ctx, ISA_MIPS3);
+ check_mips_64(ctx);
+ gen_arith_imm(env, ctx, op, rt, rs, imm);
+ break;
+#endif
+ case OPC_JALX:
+ check_insn(env, ctx, ASE_MIPS16 | ASE_MICROMIPS);
+ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
+ gen_compute_branch(ctx, op, 4, rs, rt, offset);
+ *is_branch = 1;
+ break;
+ case OPC_MDMX:
+ check_insn(env, ctx, ASE_MDMX);
+ /* MDMX: Not implemented. */
+ default: /* Invalid */
+ MIPS_INVAL("major opcode");
+ generate_exception(ctx, EXCP_RI);
+ break;
+ }
+}
+
+static inline void
+gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext ctx;
+ target_ulong pc_start;
+ uint16_t *gen_opc_end;
+ CPUBreakpoint *bp;
+ int j, lj = -1;
+ int num_insns;
+ int max_insns;
+ int insn_bytes;
+ int is_branch;
+
+ if (search_pc)
+ qemu_log("search pc %d\n", search_pc);
+
+ pc_start = tb->pc;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ ctx.pc = pc_start;
+ ctx.saved_pc = -1;
+ ctx.singlestep_enabled = env->singlestep_enabled;
+ ctx.tb = tb;
+ ctx.bstate = BS_NONE;
+ /* Restore delay slot state from the tb context. */
+ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
+ restore_cpu_state(env, &ctx);
+#ifdef CONFIG_USER_ONLY
+ ctx.mem_idx = MIPS_HFLAG_UM;
+#else
+ ctx.mem_idx = ctx.hflags & MIPS_HFLAG_KSU;
+#endif
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+ LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags);
+ gen_icount_start();
+ while (ctx.bstate == BS_NONE) {
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == ctx.pc) {
+ save_cpu_state(&ctx, 1);
+ ctx.bstate = BS_BRANCH;
+ gen_helper_0i(raise_exception, EXCP_DEBUG);
+ /* Include the breakpoint location or the tb won't
+ * be flushed when it must be. */
+ ctx.pc += 4;
+ goto done_generating;
+ }
+ }
+ }
+
+ 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] = ctx.pc;
+ gen_opc_hflags[lj] = ctx.hflags & MIPS_HFLAG_BMASK;
+ 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();
+
+ is_branch = 0;
+ if (!(ctx.hflags & MIPS_HFLAG_M16)) {
+ ctx.opcode = ldl_code(ctx.pc);
+ insn_bytes = 4;
+ decode_opc(env, &ctx, &is_branch);
+ } else if (env->insn_flags & ASE_MICROMIPS) {
+ ctx.opcode = lduw_code(ctx.pc);
+ insn_bytes = decode_micromips_opc(env, &ctx, &is_branch);
+ } else if (env->insn_flags & ASE_MIPS16) {
+ ctx.opcode = lduw_code(ctx.pc);
+ insn_bytes = decode_mips16_opc(env, &ctx, &is_branch);
+ } else {
+ generate_exception(&ctx, EXCP_RI);
+ ctx.bstate = BS_STOP;
+ break;
+ }
+ if (!is_branch) {
+ handle_delay_slot(env, &ctx, insn_bytes);
+ }
+ ctx.pc += insn_bytes;
+
+ num_insns++;
+
+ /* Execute a branch and its delay slot as a single instruction.
+ This is what GDB expects and is consistent with what the
+ hardware does (e.g. if a delay slot instruction faults, the
+ reported PC is the PC of the branch). */
+ if (env->singlestep_enabled && (ctx.hflags & MIPS_HFLAG_BMASK) == 0)
+ break;
+
+ if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0)
+ break;
+
+ if (gen_opc_ptr >= gen_opc_end)
+ break;
+
+ if (num_insns >= max_insns)
+ break;
+
+ if (singlestep)
+ break;
+ }
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ if (env->singlestep_enabled && ctx.bstate != BS_BRANCH) {
+ save_cpu_state(&ctx, ctx.bstate == BS_NONE);
+ gen_helper_0i(raise_exception, EXCP_DEBUG);
+ } else {
+ switch (ctx.bstate) {
+ case BS_STOP:
+ gen_goto_tb(&ctx, 0, ctx.pc);
+ break;
+ case BS_NONE:
+ save_cpu_state(&ctx, 0);
+ gen_goto_tb(&ctx, 0, ctx.pc);
+ break;
+ case BS_EXCP:
+ tcg_gen_exit_tb(0);
+ break;
+ case BS_BRANCH:
+ default:
+ break;
+ }
+ }
+done_generating:
+ 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 = ctx.pc - pc_start;
+ tb->icount = num_insns;
+ }
+#ifdef DEBUG_DISAS
+ LOG_DISAS("\n");
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, ctx.pc - pc_start, 0);
+ 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);
+}
+
+static void fpu_dump_state(CPUState *env, FILE *f, fprintf_function fpu_fprintf,
+ int flags)
+{
+ int i;
+ int is_fpu64 = !!(env->hflags & MIPS_HFLAG_F64);
+
+#define printfpr(fp) \
+ do { \
+ if (is_fpu64) \
+ fpu_fprintf(f, "w:%08x d:%016" PRIx64 \
+ " fd:%13g fs:%13g psu: %13g\n", \
+ (fp)->w[FP_ENDIAN_IDX], (fp)->d, \
+ (double)(fp)->fd, \
+ (double)(fp)->fs[FP_ENDIAN_IDX], \
+ (double)(fp)->fs[!FP_ENDIAN_IDX]); \
+ else { \
+ fpr_t tmp; \
+ tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX]; \
+ tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX]; \
+ fpu_fprintf(f, "w:%08x d:%016" PRIx64 \
+ " fd:%13g fs:%13g psu:%13g\n", \
+ tmp.w[FP_ENDIAN_IDX], tmp.d, \
+ (double)tmp.fd, \
+ (double)tmp.fs[FP_ENDIAN_IDX], \
+ (double)tmp.fs[!FP_ENDIAN_IDX]); \
+ } \
+ } while(0)
+
+
+ fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%02x\n",
+ env->active_fpu.fcr0, env->active_fpu.fcr31, is_fpu64,
+ get_float_exception_flags(&env->active_fpu.fp_status));
+ for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) {
+ fpu_fprintf(f, "%3s: ", fregnames[i]);
+ printfpr(&env->active_fpu.fpr[i]);
+ }
+
+#undef printfpr
+}
+
+#if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS)
+/* Debug help: The architecture requires 32bit code to maintain proper
+ sign-extended values on 64bit machines. */
+
+#define SIGN_EXT_P(val) ((((val) & ~0x7fffffff) == 0) || (((val) & ~0x7fffffff) == ~0x7fffffff))
+
+static void
+cpu_mips_check_sign_extensions (CPUState *env, FILE *f,
+ fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+
+ if (!SIGN_EXT_P(env->active_tc.PC))
+ cpu_fprintf(f, "BROKEN: pc=0x" TARGET_FMT_lx "\n", env->active_tc.PC);
+ if (!SIGN_EXT_P(env->active_tc.HI[0]))
+ cpu_fprintf(f, "BROKEN: HI=0x" TARGET_FMT_lx "\n", env->active_tc.HI[0]);
+ if (!SIGN_EXT_P(env->active_tc.LO[0]))
+ cpu_fprintf(f, "BROKEN: LO=0x" TARGET_FMT_lx "\n", env->active_tc.LO[0]);
+ if (!SIGN_EXT_P(env->btarget))
+ cpu_fprintf(f, "BROKEN: btarget=0x" TARGET_FMT_lx "\n", env->btarget);
+
+ for (i = 0; i < 32; i++) {
+ if (!SIGN_EXT_P(env->active_tc.gpr[i]))
+ cpu_fprintf(f, "BROKEN: %s=0x" TARGET_FMT_lx "\n", regnames[i], env->active_tc.gpr[i]);
+ }
+
+ if (!SIGN_EXT_P(env->CP0_EPC))
+ cpu_fprintf(f, "BROKEN: EPC=0x" TARGET_FMT_lx "\n", env->CP0_EPC);
+ if (!SIGN_EXT_P(env->lladdr))
+ cpu_fprintf(f, "BROKEN: LLAddr=0x" TARGET_FMT_lx "\n", env->lladdr);
+}
+#endif
+
+void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+
+ cpu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx
+ " LO=0x" TARGET_FMT_lx " ds %04x "
+ TARGET_FMT_lx " " TARGET_FMT_ld "\n",
+ env->active_tc.PC, env->active_tc.HI[0], env->active_tc.LO[0],
+ env->hflags, env->btarget, env->bcond);
+ for (i = 0; i < 32; i++) {
+ if ((i & 3) == 0)
+ cpu_fprintf(f, "GPR%02d:", i);
+ cpu_fprintf(f, " %s " TARGET_FMT_lx, regnames[i], env->active_tc.gpr[i]);
+ if ((i & 3) == 3)
+ cpu_fprintf(f, "\n");
+ }
+
+ cpu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x" TARGET_FMT_lx "\n",
+ env->CP0_Status, env->CP0_Cause, env->CP0_EPC);
+ cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x" TARGET_FMT_lx "\n",
+ env->CP0_Config0, env->CP0_Config1, env->lladdr);
+ if (env->hflags & MIPS_HFLAG_FPU)
+ fpu_dump_state(env, f, cpu_fprintf, flags);
+#if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS)
+ cpu_mips_check_sign_extensions(env, f, cpu_fprintf, flags);
+#endif
+}
+
+static void mips_tcg_init(void)
+{
+ int i;
+ static int inited;
+
+ /* Initialize various static tables. */
+ if (inited)
+ return;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+ TCGV_UNUSED(cpu_gpr[0]);
+ for (i = 1; i < 32; i++)
+ cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, active_tc.gpr[i]),
+ regnames[i]);
+ cpu_PC = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, active_tc.PC), "PC");
+ for (i = 0; i < MIPS_DSP_ACC; i++) {
+ cpu_HI[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, active_tc.HI[i]),
+ regnames_HI[i]);
+ cpu_LO[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, active_tc.LO[i]),
+ regnames_LO[i]);
+ cpu_ACX[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, active_tc.ACX[i]),
+ regnames_ACX[i]);
+ }
+ cpu_dspctrl = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, active_tc.DSPControl),
+ "DSPControl");
+ bcond = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, bcond), "bcond");
+ btarget = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, btarget), "btarget");
+ hflags = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, hflags), "hflags");
+
+ fpu_fcr0 = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, active_fpu.fcr0),
+ "fcr0");
+ fpu_fcr31 = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, active_fpu.fcr31),
+ "fcr31");
+
+ /* register helpers */
+#define GEN_HELPER 2
+#include "helper.h"
+
+ inited = 1;
+}
+
+#include "translate_init.c"
+
+CPUMIPSState *cpu_mips_init (const char *cpu_model)
+{
+ CPUMIPSState *env;
+ const mips_def_t *def;
+
+ def = cpu_mips_find_by_name(cpu_model);
+ if (!def)
+ return NULL;
+ env = qemu_mallocz(sizeof(CPUMIPSState));
+ env->cpu_model = def;
+ env->cpu_model_str = cpu_model;
+
+ cpu_exec_init(env);
+#ifndef CONFIG_USER_ONLY
+ mmu_init(env, def);
+#endif
+ fpu_init(env, def);
+ mvp_init(env, def);
+ mips_tcg_init();
+ cpu_reset(env);
+ qemu_init_vcpu(env);
+ return env;
+}
+
+void cpu_reset (CPUMIPSState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ memset(env, 0, offsetof(CPUMIPSState, breakpoints));
+ tlb_flush(env, 1);
+
+ /* Reset registers to their default values */
+ env->CP0_PRid = env->cpu_model->CP0_PRid;
+ env->CP0_Config0 = env->cpu_model->CP0_Config0;
+#ifdef TARGET_WORDS_BIGENDIAN
+ env->CP0_Config0 |= (1 << CP0C0_BE);
+#endif
+ env->CP0_Config1 = env->cpu_model->CP0_Config1;
+ env->CP0_Config2 = env->cpu_model->CP0_Config2;
+ env->CP0_Config3 = env->cpu_model->CP0_Config3;
+ env->CP0_Config6 = env->cpu_model->CP0_Config6;
+ env->CP0_Config7 = env->cpu_model->CP0_Config7;
+ env->CP0_LLAddr_rw_bitmask = env->cpu_model->CP0_LLAddr_rw_bitmask
+ << env->cpu_model->CP0_LLAddr_shift;
+ env->CP0_LLAddr_shift = env->cpu_model->CP0_LLAddr_shift;
+ env->SYNCI_Step = env->cpu_model->SYNCI_Step;
+ env->CCRes = env->cpu_model->CCRes;
+ env->CP0_Status_rw_bitmask = env->cpu_model->CP0_Status_rw_bitmask;
+ env->CP0_TCStatus_rw_bitmask = env->cpu_model->CP0_TCStatus_rw_bitmask;
+ env->CP0_SRSCtl = env->cpu_model->CP0_SRSCtl;
+ env->current_tc = 0;
+ env->SEGBITS = env->cpu_model->SEGBITS;
+ env->SEGMask = (target_ulong)((1ULL << env->cpu_model->SEGBITS) - 1);
+#if defined(TARGET_MIPS64)
+ if (env->cpu_model->insn_flags & ISA_MIPS3) {
+ env->SEGMask |= 3ULL << 62;
+ }
+#endif
+ env->PABITS = env->cpu_model->PABITS;
+ env->PAMask = (target_ulong)((1ULL << env->cpu_model->PABITS) - 1);
+ env->CP0_SRSConf0_rw_bitmask = env->cpu_model->CP0_SRSConf0_rw_bitmask;
+ env->CP0_SRSConf0 = env->cpu_model->CP0_SRSConf0;
+ env->CP0_SRSConf1_rw_bitmask = env->cpu_model->CP0_SRSConf1_rw_bitmask;
+ env->CP0_SRSConf1 = env->cpu_model->CP0_SRSConf1;
+ env->CP0_SRSConf2_rw_bitmask = env->cpu_model->CP0_SRSConf2_rw_bitmask;
+ env->CP0_SRSConf2 = env->cpu_model->CP0_SRSConf2;
+ env->CP0_SRSConf3_rw_bitmask = env->cpu_model->CP0_SRSConf3_rw_bitmask;
+ env->CP0_SRSConf3 = env->cpu_model->CP0_SRSConf3;
+ env->CP0_SRSConf4_rw_bitmask = env->cpu_model->CP0_SRSConf4_rw_bitmask;
+ env->CP0_SRSConf4 = env->cpu_model->CP0_SRSConf4;
+ env->insn_flags = env->cpu_model->insn_flags;
+
+#if defined(CONFIG_USER_ONLY)
+ env->hflags = MIPS_HFLAG_UM;
+ /* Enable access to the SYNCI_Step register. */
+ env->CP0_HWREna |= (1 << 1);
+ if (env->CP0_Config1 & (1 << CP0C1_FP)) {
+ env->hflags |= MIPS_HFLAG_FPU;
+ }
+#ifdef TARGET_MIPS64
+ if (env->active_fpu.fcr0 & (1 << FCR0_F64)) {
+ env->hflags |= MIPS_HFLAG_F64;
+ }
+#endif
+#else
+ if (env->hflags & MIPS_HFLAG_BMASK) {
+ /* If the exception was raised from a delay slot,
+ come back to the jump. */
+ env->CP0_ErrorEPC = env->active_tc.PC - 4;
+ } else {
+ env->CP0_ErrorEPC = env->active_tc.PC;
+ }
+ env->active_tc.PC = (int32_t)0xBFC00000;
+ env->CP0_Random = env->tlb->nb_tlb - 1;
+ env->tlb->tlb_in_use = env->tlb->nb_tlb;
+ env->CP0_Wired = 0;
+ env->CP0_EBase = 0x80000000 | (env->cpu_index & 0x3FF);
+ env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL);
+ /* vectored interrupts not implemented, timer on int 7,
+ no performance counters. */
+ env->CP0_IntCtl = 0xe0000000;
+ {
+ int i;
+
+ for (i = 0; i < 7; i++) {
+ env->CP0_WatchLo[i] = 0;
+ env->CP0_WatchHi[i] = 0x80000000;
+ }
+ env->CP0_WatchLo[7] = 0;
+ env->CP0_WatchHi[7] = 0;
+ }
+ /* Count register increments in debug mode, EJTAG version 1 */
+ env->CP0_Debug = (1 << CP0DB_CNT) | (0x1 << CP0DB_VER);
+ env->hflags = MIPS_HFLAG_CP0;
+#endif
+#if defined(TARGET_MIPS64)
+ if (env->cpu_model->insn_flags & ISA_MIPS3) {
+ env->hflags |= MIPS_HFLAG_64;
+ }
+#endif
+ env->exception_index = EXCP_NONE;
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->active_tc.PC = gen_opc_pc[pc_pos];
+ env->hflags &= ~MIPS_HFLAG_BMASK;
+ env->hflags |= gen_opc_hflags[pc_pos];
+}
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
new file mode 100644
index 0000000..590e092
--- /dev/null
+++ b/target-mips/translate_init.c
@@ -0,0 +1,594 @@
+/*
+ * MIPS emulation for qemu: CPU initialisation routines.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * Copyright (c) 2007 Herve Poussineau
+ *
+ * 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/>.
+ */
+
+/* CPU / CPU family specific config register values. */
+
+/* Have config1, uncached coherency */
+#define MIPS_CONFIG0 \
+ ((1 << CP0C0_M) | (0x2 << CP0C0_K0))
+
+/* Have config2, no coprocessor2 attached, no MDMX support attached,
+ no performance counters, watch registers present,
+ no code compression, EJTAG present, no FPU */
+#define MIPS_CONFIG1 \
+((1 << CP0C1_M) | \
+ (0 << CP0C1_C2) | (0 << CP0C1_MD) | (0 << CP0C1_PC) | \
+ (1 << CP0C1_WR) | (0 << CP0C1_CA) | (1 << CP0C1_EP) | \
+ (0 << CP0C1_FP))
+
+/* Have config3, no tertiary/secondary caches implemented */
+#define MIPS_CONFIG2 \
+((1 << CP0C2_M))
+
+/* No config4, no DSP ASE, no large physaddr (PABITS),
+ no external interrupt controller, no vectored interupts,
+ no 1kb pages, no SmartMIPS ASE, no trace logic */
+#define MIPS_CONFIG3 \
+((0 << CP0C3_M) | (0 << CP0C3_DSPP) | (0 << CP0C3_LPA) | \
+ (0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) | \
+ (0 << CP0C3_SM) | (0 << CP0C3_TL))
+
+/* MMU types, the first four entries have the same layout as the
+ CP0C0_MT field. */
+enum mips_mmu_types {
+ MMU_TYPE_NONE,
+ MMU_TYPE_R4000,
+ MMU_TYPE_RESERVED,
+ MMU_TYPE_FMT,
+ MMU_TYPE_R3000,
+ MMU_TYPE_R6000,
+ MMU_TYPE_R8000
+};
+
+struct mips_def_t {
+ const char *name;
+ int32_t CP0_PRid;
+ int32_t CP0_Config0;
+ int32_t CP0_Config1;
+ int32_t CP0_Config2;
+ int32_t CP0_Config3;
+ int32_t CP0_Config6;
+ int32_t CP0_Config7;
+ target_ulong CP0_LLAddr_rw_bitmask;
+ int CP0_LLAddr_shift;
+ int32_t SYNCI_Step;
+ int32_t CCRes;
+ int32_t CP0_Status_rw_bitmask;
+ int32_t CP0_TCStatus_rw_bitmask;
+ int32_t CP0_SRSCtl;
+ int32_t CP1_fcr0;
+ int32_t SEGBITS;
+ int32_t PABITS;
+ int32_t CP0_SRSConf0_rw_bitmask;
+ int32_t CP0_SRSConf0;
+ int32_t CP0_SRSConf1_rw_bitmask;
+ int32_t CP0_SRSConf1;
+ int32_t CP0_SRSConf2_rw_bitmask;
+ int32_t CP0_SRSConf2;
+ int32_t CP0_SRSConf3_rw_bitmask;
+ int32_t CP0_SRSConf3;
+ int32_t CP0_SRSConf4_rw_bitmask;
+ int32_t CP0_SRSConf4;
+ int insn_flags;
+ enum mips_mmu_types mmu_type;
+};
+
+/*****************************************************************************/
+/* MIPS CPU definitions */
+static const mips_def_t mips_defs[] =
+{
+ {
+ .name = "4Kc",
+ .CP0_PRid = 0x00018000,
+ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (0 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x1278FF17,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "4Km",
+ .CP0_PRid = 0x00018300,
+ /* Config1 implemented, fixed mapping MMU,
+ no virtual icache, uncached coherency. */
+ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_FMT << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x1258FF17,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32 | ASE_MIPS16,
+ .mmu_type = MMU_TYPE_FMT,
+ },
+ {
+ .name = "4KEcR1",
+ .CP0_PRid = 0x00018400,
+ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (0 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x1278FF17,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "4KEmR1",
+ .CP0_PRid = 0x00018500,
+ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_FMT << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x1258FF17,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32 | ASE_MIPS16,
+ .mmu_type = MMU_TYPE_FMT,
+ },
+ {
+ .name = "4KEc",
+ .CP0_PRid = 0x00019000,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (0 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt),
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x1278FF17,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32R2,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "4KEm",
+ .CP0_PRid = 0x00019100,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) |
+ (MMU_TYPE_FMT << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x1258FF17,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32R2 | ASE_MIPS16,
+ .mmu_type = MMU_TYPE_FMT,
+ },
+ {
+ .name = "24Kc",
+ .CP0_PRid = 0x00019300,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt),
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ /* No DSP implemented. */
+ .CP0_Status_rw_bitmask = 0x1278FF1F,
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32R2 | ASE_MIPS16,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "24Kf",
+ .CP0_PRid = 0x00019300,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt),
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ /* No DSP implemented. */
+ .CP0_Status_rw_bitmask = 0x3678FF1F,
+ .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
+ (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID),
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32R2 | ASE_MIPS16,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "34Kf",
+ .CP0_PRid = 0x00019500,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) |
+ (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_CA),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt) | (1 << CP0C3_MT),
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 0,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ /* No DSP implemented. */
+ .CP0_Status_rw_bitmask = 0x3678FF1F,
+ /* No DSP implemented. */
+ .CP0_TCStatus_rw_bitmask = (0 << CP0TCSt_TCU3) | (0 << CP0TCSt_TCU2) |
+ (1 << CP0TCSt_TCU1) | (1 << CP0TCSt_TCU0) |
+ (0 << CP0TCSt_TMX) | (1 << CP0TCSt_DT) |
+ (1 << CP0TCSt_DA) | (1 << CP0TCSt_A) |
+ (0x3 << CP0TCSt_TKSU) | (1 << CP0TCSt_IXMT) |
+ (0xff << CP0TCSt_TASID),
+ .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
+ (1 << FCR0_D) | (1 << FCR0_S) | (0x95 << FCR0_PRID),
+ .CP0_SRSCtl = (0xf << CP0SRSCtl_HSS),
+ .CP0_SRSConf0_rw_bitmask = 0x3fffffff,
+ .CP0_SRSConf0 = (1 << CP0SRSC0_M) | (0x3fe << CP0SRSC0_SRS3) |
+ (0x3fe << CP0SRSC0_SRS2) | (0x3fe << CP0SRSC0_SRS1),
+ .CP0_SRSConf1_rw_bitmask = 0x3fffffff,
+ .CP0_SRSConf1 = (1 << CP0SRSC1_M) | (0x3fe << CP0SRSC1_SRS6) |
+ (0x3fe << CP0SRSC1_SRS5) | (0x3fe << CP0SRSC1_SRS4),
+ .CP0_SRSConf2_rw_bitmask = 0x3fffffff,
+ .CP0_SRSConf2 = (1 << CP0SRSC2_M) | (0x3fe << CP0SRSC2_SRS9) |
+ (0x3fe << CP0SRSC2_SRS8) | (0x3fe << CP0SRSC2_SRS7),
+ .CP0_SRSConf3_rw_bitmask = 0x3fffffff,
+ .CP0_SRSConf3 = (1 << CP0SRSC3_M) | (0x3fe << CP0SRSC3_SRS12) |
+ (0x3fe << CP0SRSC3_SRS11) | (0x3fe << CP0SRSC3_SRS10),
+ .CP0_SRSConf4_rw_bitmask = 0x3fffffff,
+ .CP0_SRSConf4 = (0x3fe << CP0SRSC4_SRS15) |
+ (0x3fe << CP0SRSC4_SRS14) | (0x3fe << CP0SRSC4_SRS13),
+ .SEGBITS = 32,
+ .PABITS = 32,
+ .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_MT,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+#if defined(TARGET_MIPS64)
+ {
+ .name = "R4000",
+ .CP0_PRid = 0x00000400,
+ /* No L2 cache, icache size 8k, dcache size 8k, uncached coherency. */
+ .CP0_Config0 = (1 << 17) | (0x1 << 9) | (0x1 << 6) | (0x2 << CP0C0_K0),
+ /* Note: Config1 is only used internally, the R4000 has only Config0. */
+ .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU),
+ .CP0_LLAddr_rw_bitmask = 0xFFFFFFFF,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 16,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x3678FFFF,
+ /* The R4000 has a full 64bit FPU but doesn't use the fcr0 bits. */
+ .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x0 << FCR0_REV),
+ .SEGBITS = 40,
+ .PABITS = 36,
+ .insn_flags = CPU_MIPS3,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "VR5432",
+ .CP0_PRid = 0x00005400,
+ /* No L2 cache, icache size 8k, dcache size 8k, uncached coherency. */
+ .CP0_Config0 = (1 << 17) | (0x1 << 9) | (0x1 << 6) | (0x2 << CP0C0_K0),
+ .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU),
+ .CP0_LLAddr_rw_bitmask = 0xFFFFFFFFL,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 16,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x3678FFFF,
+ /* The VR5432 has a full 64bit FPU but doesn't use the fcr0 bits. */
+ .CP1_fcr0 = (0x54 << FCR0_PRID) | (0x0 << FCR0_REV),
+ .SEGBITS = 40,
+ .PABITS = 32,
+ .insn_flags = CPU_VR54XX,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "5Kc",
+ .CP0_PRid = 0x00018100,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AT) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (31 << CP0C1_MMU) |
+ (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x32F8FFFF,
+ .SEGBITS = 42,
+ .PABITS = 36,
+ .insn_flags = CPU_MIPS64,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "5Kf",
+ .CP0_PRid = 0x00018100,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AT) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (31 << CP0C1_MMU) |
+ (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) |
+ (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) |
+ (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 4,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x36F8FFFF,
+ /* The 5Kf has F64 / L / W but doesn't use the fcr0 bits. */
+ .CP1_fcr0 = (1 << FCR0_D) | (1 << FCR0_S) |
+ (0x81 << FCR0_PRID) | (0x0 << FCR0_REV),
+ .SEGBITS = 42,
+ .PABITS = 36,
+ .insn_flags = CPU_MIPS64,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "20Kc",
+ /* We emulate a later version of the 20Kc, earlier ones had a broken
+ WAIT instruction. */
+ .CP0_PRid = 0x000182a0,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AT) |
+ (MMU_TYPE_R4000 << CP0C0_MT) | (1 << CP0C0_VI),
+ .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (47 << CP0C1_MMU) |
+ (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) |
+ (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) |
+ (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3,
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 0,
+ .SYNCI_Step = 32,
+ .CCRes = 1,
+ .CP0_Status_rw_bitmask = 0x36FBFFFF,
+ /* The 20Kc has F64 / L / W but doesn't use the fcr0 bits. */
+ .CP1_fcr0 = (1 << FCR0_3D) | (1 << FCR0_PS) |
+ (1 << FCR0_D) | (1 << FCR0_S) |
+ (0x82 << FCR0_PRID) | (0x0 << FCR0_REV),
+ .SEGBITS = 40,
+ .PABITS = 36,
+ .insn_flags = CPU_MIPS64 | ASE_MIPS3D,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ /* A generic CPU providing MIPS64 Release 2 features.
+ FIXME: Eventually this should be replaced by a real CPU model. */
+ .name = "MIPS64R2-generic",
+ .CP0_PRid = 0x00010000,
+ .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) |
+ (MMU_TYPE_R4000 << CP0C0_MT),
+ .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) |
+ (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) |
+ (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) |
+ (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP),
+ .CP0_Config2 = MIPS_CONFIG2,
+ .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA),
+ .CP0_LLAddr_rw_bitmask = 0,
+ .CP0_LLAddr_shift = 0,
+ .SYNCI_Step = 32,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x36FBFFFF,
+ .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_3D) | (1 << FCR0_PS) |
+ (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) |
+ (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
+ .SEGBITS = 42,
+ /* The architectural limit is 59, but we have hardcoded 36 bit
+ in some places...
+ .PABITS = 59, */ /* the architectural limit */
+ .PABITS = 36,
+ .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "Loongson-2E",
+ .CP0_PRid = 0x6302,
+ /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/
+ .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | (0x1<<5) |
+ (0x1<<4) | (0x1<<1),
+ /* Note: Config1 is only used internally, Loongson-2E has only Config0. */
+ .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU),
+ .SYNCI_Step = 16,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0x35D0FFFF,
+ .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV),
+ .SEGBITS = 40,
+ .PABITS = 40,
+ .insn_flags = CPU_LOONGSON2E,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+ {
+ .name = "Loongson-2F",
+ .CP0_PRid = 0x6303,
+ /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/
+ .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | (0x1<<5) |
+ (0x1<<4) | (0x1<<1),
+ /* Note: Config1 is only used internally, Loongson-2F has only Config0. */
+ .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU),
+ .SYNCI_Step = 16,
+ .CCRes = 2,
+ .CP0_Status_rw_bitmask = 0xF5D0FF1F, /*bit5:7 not writeable*/
+ .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV),
+ .SEGBITS = 40,
+ .PABITS = 40,
+ .insn_flags = CPU_LOONGSON2F,
+ .mmu_type = MMU_TYPE_R4000,
+ },
+
+#endif
+};
+
+static const mips_def_t *cpu_mips_find_by_name (const char *name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mips_defs); i++) {
+ if (strcasecmp(name, mips_defs[i].name) == 0) {
+ return &mips_defs[i];
+ }
+ }
+ return NULL;
+}
+
+void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mips_defs); i++) {
+ (*cpu_fprintf)(f, "MIPS '%s'\n",
+ mips_defs[i].name);
+ }
+}
+
+#ifndef CONFIG_USER_ONLY
+static void no_mmu_init (CPUMIPSState *env, const mips_def_t *def)
+{
+ env->tlb->nb_tlb = 1;
+ env->tlb->map_address = &no_mmu_map_address;
+}
+
+static void fixed_mmu_init (CPUMIPSState *env, const mips_def_t *def)
+{
+ env->tlb->nb_tlb = 1;
+ env->tlb->map_address = &fixed_mmu_map_address;
+}
+
+static void r4k_mmu_init (CPUMIPSState *env, const mips_def_t *def)
+{
+ env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63);
+ env->tlb->map_address = &r4k_map_address;
+ env->tlb->helper_tlbwi = r4k_helper_tlbwi;
+ env->tlb->helper_tlbwr = r4k_helper_tlbwr;
+ env->tlb->helper_tlbp = r4k_helper_tlbp;
+ env->tlb->helper_tlbr = r4k_helper_tlbr;
+}
+
+static void mmu_init (CPUMIPSState *env, const mips_def_t *def)
+{
+ env->tlb = qemu_mallocz(sizeof(CPUMIPSTLBContext));
+
+ switch (def->mmu_type) {
+ case MMU_TYPE_NONE:
+ no_mmu_init(env, def);
+ break;
+ case MMU_TYPE_R4000:
+ r4k_mmu_init(env, def);
+ break;
+ case MMU_TYPE_FMT:
+ fixed_mmu_init(env, def);
+ break;
+ case MMU_TYPE_R3000:
+ case MMU_TYPE_R6000:
+ case MMU_TYPE_R8000:
+ default:
+ cpu_abort(env, "MMU type not supported\n");
+ }
+}
+#endif /* CONFIG_USER_ONLY */
+
+static void fpu_init (CPUMIPSState *env, const mips_def_t *def)
+{
+ int i;
+
+ for (i = 0; i < MIPS_FPU_MAX; i++)
+ env->fpus[i].fcr0 = def->CP1_fcr0;
+
+ memcpy(&env->active_fpu, &env->fpus[0], sizeof(env->active_fpu));
+}
+
+static void mvp_init (CPUMIPSState *env, const mips_def_t *def)
+{
+ env->mvp = qemu_mallocz(sizeof(CPUMIPSMVPContext));
+
+ /* MVPConf1 implemented, TLB sharable, no gating storage support,
+ programmable cache partitioning implemented, number of allocatable
+ and sharable TLB entries, MVP has allocatable TCs, 2 VPEs
+ implemented, 5 TCs implemented. */
+ env->mvp->CP0_MVPConf0 = (1 << CP0MVPC0_M) | (1 << CP0MVPC0_TLBS) |
+ (0 << CP0MVPC0_GS) | (1 << CP0MVPC0_PCP) |
+// TODO: actually do 2 VPEs.
+// (1 << CP0MVPC0_TCA) | (0x1 << CP0MVPC0_PVPE) |
+// (0x04 << CP0MVPC0_PTC);
+ (1 << CP0MVPC0_TCA) | (0x0 << CP0MVPC0_PVPE) |
+ (0x04 << CP0MVPC0_PTC);
+#if !defined(CONFIG_USER_ONLY)
+ /* Usermode has no TLB support */
+ env->mvp->CP0_MVPConf0 |= (env->tlb->nb_tlb << CP0MVPC0_PTLBE);
+#endif
+
+ /* Allocatable CP1 have media extensions, allocatable CP1 have FP support,
+ no UDI implemented, no CP2 implemented, 1 CP1 implemented. */
+ env->mvp->CP0_MVPConf1 = (1 << CP0MVPC1_CIM) | (1 << CP0MVPC1_CIF) |
+ (0x0 << CP0MVPC1_PCX) | (0x0 << CP0MVPC1_PCP2) |
+ (0x1 << CP0MVPC1_PCP1);
+}
diff --git a/target-ppc/STATUS b/target-ppc/STATUS
new file mode 100644
index 0000000..32e7ffa
--- /dev/null
+++ b/target-ppc/STATUS
@@ -0,0 +1,559 @@
+PowerPC emulation status.
+The goal of this file is to provide a reference status to avoid regressions.
+
+===============================================================================
+PowerPC core emulation status
+
+INSN: instruction set.
+ OK => all instructions are emulated
+ KO => some insns are missing or some should be removed
+ ? => unchecked
+SPR: special purpose registers set
+ OK => all SPR registered (but some may be fake)
+ KO => some SPR are missing or should be removed
+ ? => uncheked
+MSR: MSR bits definitions
+ OK => all MSR bits properly defined
+ KO => MSR definition is incorrect
+ ? => unchecked
+IRQ: input signals definitions (mostly interrupts)
+ OK => input signals are properly defined
+ KO => input signals are not implemented (system emulation does not work)
+ ? => input signals definitions may be incorrect
+MMU: MMU model implementation
+ OK => MMU model is implemented and Linux is able to boot
+ KO => MMU model not implemented or bugged
+ ? => MMU model not tested
+EXCP: exceptions model implementation
+ OK => exception model is implemented and Linux is able to boot
+ KO => exception model not implemented or known to be buggy
+ ? => exception model may be incorrect or is untested
+
+Embedded PowerPC cores
+***
+PowerPC 401:
+INSN OK
+SPR OK 401A1
+MSR OK
+IRQ KO partially implemented
+MMU OK
+EXCP ?
+
+PowerPC 401x2:
+INSN OK
+SPR OK 401B2 401C2 401D2 401E2 401F2
+MSR OK
+IRQ KO partially implemented
+MMU OK
+EXCP ?
+
+PowerPC IOP480:
+INSN OK
+SPR OK IOP480
+MSR OK
+IRQ KO partially implemented
+MMU OK
+EXCP ?
+
+To be checked: 401G2 401B3 Cobra
+
+***
+PowerPC 403:
+INSN OK
+SPR OK 403GA 403GB
+MMU OK
+MSR OK
+IRQ KO not implemented
+EXCP ?
+
+PowerPC 403GCX:
+INSN OK
+SPR OK 403GCX
+MMU OK
+MSR OK
+IRQ KO not implemented
+EXCP ?
+
+To be checked: 403GC
+
+***
+PowerPC 405:
+Checked: 405CRa 405CRb 405CRc 405EP 405GPa 405GPb 405GPc 405GPd 405GPe 405GPR
+ Npe405H Npe405H2 Npe405L
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots (at least 1 proprietary firmware).
+ uboot seems to freeze at boot time.
+To be checked: 405D2 405D4 405EZ 405LP Npe4GS3 STB03 STB04 STB25
+ x2vp4 x2vp7 x2vp20 x2vp50
+
+XXX: find what is IBM e407b4
+
+***
+PowerPC 440:
+Checked: 440EPa 440EPb 440GXa 440GXb 440GXc 440GXf 440SP 440SP2
+INSN OK
+SPR OK
+MSR OK
+IRQ KO not implemented
+MMU ?
+EXCP ?
+
+PowerPC 440GP:
+Checked: 440GPb 440GPc
+INSN OK
+SPR OK
+MSR OK
+IRQ KO not implemented
+MMU ?
+EXCP ?
+
+PowerPC 440x4:
+Checked: 440A4 440B4 440G4 440H4
+INSN OK
+SPR OK
+MSR OK
+IRQ KO not implemented
+MMU ?
+EXCP ?
+
+PowerPC 440x5:
+Checked: 440A5 440F5 440G5 440H6 440GRa
+INSN OK
+SPR OK
+MSR OK
+IRQ KO not implemented
+MMU ?
+EXCP ?
+
+To be checked: 440EPx 440GRx 440SPE
+
+***
+PowerPC 460: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+PowerPC 460F: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+***
+PowerPC e200: (not implemented)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+***
+PowerPC e300: (not implemented)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+***
+PowerPC e500: (not implemented)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+***
+PowerPC e600: (not implemented)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+***
+32 bits PowerPC
+PowerPC 601: (601 601v2)
+INSN OK
+SPR OK is HID15 only on 601v2 ?
+MSR OK
+IRQ KO not implemented
+MMU ?
+EXCP ?
+Remarks: some instructions should have a specific behavior (not implemented)
+
+PowerPC 602: 602
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU ?
+EXCP ? at least timer and external interrupt are OK
+Remarks: Linux 2.4 crashes when entering user-mode.
+ Linux 2.6.22 boots on this CPU but does not recognize it.
+
+PowerPC 603: (603)
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots and properly recognizes the CPU
+ Linux 2.6.22 idem.
+
+PowerPC 603e: (603e11)
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots and properly recognizes the CPU
+ Linux 2.6.22 idem.
+
+PowerPC G2:
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots, recognizes the CPU as a 82xx.
+ Linux 2.6.22 idem.
+
+PowerPC G2le:
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 does not boots. Same symptoms as 602.
+ Linux 2.6.22 boots and properly recognizes the CPU.
+
+PowerPC 604:
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots and properly recognizes the CPU.
+ Linux 2.6.22 idem.
+
+PowerPC 7x0:
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots and properly recognizes the CPU.
+ Linux 2.6.22 idem.
+
+PowerPC 750fx:
+INSN OK
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP OK
+Remarks: Linux 2.4 boots but does not properly recognizes the CPU.
+ Linux 2.6.22 boots and properly recognizes the CPU.
+
+PowerPC 7x5:
+INSN ?
+SPR ?
+MSR ?
+IRQ OK
+MMU ?
+EXCP OK
+Remarks: Linux 2.4 does not boot.
+ Linux 2.6.22 idem.
+
+PowerPC 7400:
+INSN KO Altivec missing
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP ? Altivec, ...
+Remarks: Linux 2.4 boots and properly recognize the CPU.
+ Linux 2.6.22 idem.
+
+PowerPC 7410:
+INSN KO Altivec missing
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP ? Altivec, ...
+Remarks: Linux 2.4 boots and properly recognize the CPU.
+ Linux 2.6.22 idem.
+ Note that UM says tlbld & tlbli are implemented but this may be a mistake
+ as TLB loads are managed by the hardware and the CPU does not implement the
+ needed registers.
+
+PowerPC 7441:
+INSN KO Altivec missing
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP ? Altivec, ...
+Remarks: Linux does not have the code to handle TLB miss on this CPU
+ Linux 2.6.22 idem.
+
+PowerPC 7450/7451:
+INSN KO Altivec missing
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP ? Altivec, ...
+Remarks: Linux does not have the code to handle TLB miss on this CPU
+ Linux 2.6.22 idem.
+
+PowerPC 7445/7447:
+INSN KO Altivec missing
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP ? Altivec, ...
+Remarks: Linux does not have the code to handle TLB miss on this CPU
+ Linux 2.6.22 idem.
+
+PowerPC 7455/7457:
+INSN KO Altivec missing
+SPR OK
+MSR OK
+IRQ OK
+MMU OK
+EXCP ? Altivec, ...
+Remarks: Linux does not have the code to handle TLB miss on this CPU
+ Linux 2.6.22 idem.
+
+64 bits PowerPC
+PowerPC 620: (disabled)
+INSN KO
+SPR KO
+MSR ?
+IRQ KO
+MMU KO
+EXCP KO
+Remarks: not much documentation for this implementation...
+
+PowerPC 970:
+INSN KO Altivec missing and more
+SPR KO
+MSR ?
+IRQ OK
+MMU OK
+EXCP KO partially implemented
+Remarks: Should be able to boot but there is no hw platform currently emulated.
+
+PowerPC 970FX:
+INSN KO Altivec missing and more
+SPR KO
+MSR ?
+IRQ OK
+MMU OK
+EXCP KO partially implemented
+Remarks: Should be able to boot but there is no hw platform currently emulated.
+
+PowerPC 970GX:
+INSN KO Altivec missing and more
+SPR KO
+MSR ?
+IRQ OK
+MMU OK
+EXCP KO partially implemented
+Remarks: Should be able to boot but there is no hw platform currently emulated.
+
+PowerPC Cell:
+INSN KO Altivec missing and more
+SPR KO
+MSR ?
+IRQ ?
+MMU ?
+EXCP ? partially implemented
+Remarks: As the core is mostly a 970, should be able to boot.
+ SPE are not implemented.
+
+PowerPC 630: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+PowerPC 631: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+POWER4: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+POWER4+: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+POWER5: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+POWER5+: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+POWER6: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+RS64: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+RS64-II: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+RS64-III: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+RS64-IV: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+Original POWER
+POWER: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+POWER2: (disabled: lack of detailed specifications)
+INSN KO
+SPR KO
+MSR KO
+IRQ KO
+MMU KO
+EXCP KO
+
+===============================================================================
+PowerPC microcontrollers emulation status
+
+Implemementation should be sufficient to boot Linux:
+(there seem to be problems with uboot freezing at some point)
+- PowerPC 405CR
+- PowerPC 405EP
+
+TODO:
+- PowerPC 401 microcontrollers emulation
+- PowerPC 403 microcontrollers emulation
+- more PowerPC 405 microcontrollers emulation
+- Fixes / more features for implemented PowerPC 405 microcontrollers emulation
+- PowerPC 440 microcontrollers emulation
+- e200 microcontrollers emulation
+- e300 microcontrollers emulation
+- e500 microcontrollers emulation
+- e600 microcontrollers emulation
+
+===============================================================================
+PowerPC based platforms emulation status
+
+* PREP platform (RS/6000 7043...) - TO BE CHECKED (broken)
+- Gentoo Linux live CDROM 1.4
+- Debian Linux 3.0
+- Mandrake Linux 9
+
+* heathrow PowerMac platform (beige PowerMac) - TO BE CHECKED (broken)
+- Gentoo Linux live CDROM 1.4
+- Debian Linux 3.0
+- Mandrake Linux 9
+
+* mac99 platform (white and blue PowerMac, ...)
+- Gentoo Linux live CDROM 1.4 - boots, compiles linux kernel
+- Debian Linux woody - boots from CDROM and HDD
+- Mandrake Linux 9 - boots from CDROM, freezes during install
+- Knoppix 2003-07-13_4 boots from CDROM, pb with X configuration
+ distribution bug: X runs with a properly hand-coded configuration.
+- rock Linux 2.0 runs from CDROM
+
+* Linux 2.6 support seems deadly broken (used to boot...).
+
+* PowerPC 405EP reference boards:
+- can boot Linux 2.4 & 2.6.
+ Need to provide a flash image ready to boot for reproductible tests.
+
+TODO:
+- URGENT: fix PreP and heathrow platforms
+- PowerPC 64 reference platform
+- MCA based RS/6000 emulation
+- CHRP emulation (not PowerMac)
+- PPAR emulation
+- ePPAR emulation
+- misc PowerPC reference boards emulation
+
+===============================================================================
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
new file mode 100644
index 0000000..22b14d8
--- /dev/null
+++ b/target-ppc/cpu.h
@@ -0,0 +1,1634 @@
+/*
+ * PowerPC emulation cpu definitions for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+#if !defined (__CPU_PPC_H__)
+#define __CPU_PPC_H__
+
+#include "config.h"
+#include "qemu-common.h"
+
+//#define PPC_EMULATE_32BITS_HYPV
+
+#if defined (TARGET_PPC64)
+/* PowerPC 64 definitions */
+#define TARGET_LONG_BITS 64
+#define TARGET_PAGE_BITS 12
+
+/* Note that the official physical address space bits is 62-M where M
+ is implementation dependent. I've not looked up M for the set of
+ cpus we emulate at the system level. */
+#define TARGET_PHYS_ADDR_SPACE_BITS 62
+
+/* Note that the PPC environment architecture talks about 80 bit virtual
+ addresses, with segmentation. Obviously that's not all visible to a
+ single process, which is all we're concerned with here. */
+#ifdef TARGET_ABI32
+# define TARGET_VIRT_ADDR_SPACE_BITS 32
+#else
+# define TARGET_VIRT_ADDR_SPACE_BITS 64
+#endif
+
+#else /* defined (TARGET_PPC64) */
+/* PowerPC 32 definitions */
+#define TARGET_LONG_BITS 32
+
+#if defined(TARGET_PPCEMB)
+/* Specific definitions for PowerPC embedded */
+/* BookE have 36 bits physical address space */
+#if defined(CONFIG_USER_ONLY) || defined(USE_KVM)
+/* It looks like a lot of Linux programs assume page size
+ * is 4kB long. This is evil, but we have to deal with it...
+ * Also kvm for embedded powerpc needs (atm) 4kB aligned pages
+ */
+#define TARGET_PAGE_BITS 12
+#else /* defined(CONFIG_USER_ONLY) */
+/* Pages can be 1 kB small */
+#define TARGET_PAGE_BITS 10
+#endif /* defined(CONFIG_USER_ONLY) */
+#else /* defined(TARGET_PPCEMB) */
+/* "standard" PowerPC 32 definitions */
+#define TARGET_PAGE_BITS 12
+#endif /* defined(TARGET_PPCEMB) */
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#endif /* defined (TARGET_PPC64) */
+
+#define CPUState struct CPUPPCState
+
+#include "cpu-defs.h"
+
+#include <setjmp.h>
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+#if defined (TARGET_PPC64)
+#define ELF_MACHINE EM_PPC64
+#else
+#define ELF_MACHINE EM_PPC
+#endif
+
+/*****************************************************************************/
+/* MMU model */
+typedef enum powerpc_mmu_t powerpc_mmu_t;
+enum powerpc_mmu_t {
+ POWERPC_MMU_UNKNOWN = 0x00000000,
+ /* Standard 32 bits PowerPC MMU */
+ POWERPC_MMU_32B = 0x00000001,
+ /* PowerPC 6xx MMU with software TLB */
+ POWERPC_MMU_SOFT_6xx = 0x00000002,
+ /* PowerPC 74xx MMU with software TLB */
+ POWERPC_MMU_SOFT_74xx = 0x00000003,
+ /* PowerPC 4xx MMU with software TLB */
+ POWERPC_MMU_SOFT_4xx = 0x00000004,
+ /* PowerPC 4xx MMU with software TLB and zones protections */
+ POWERPC_MMU_SOFT_4xx_Z = 0x00000005,
+ /* PowerPC MMU in real mode only */
+ POWERPC_MMU_REAL = 0x00000006,
+ /* Freescale MPC8xx MMU model */
+ POWERPC_MMU_MPC8xx = 0x00000007,
+ /* BookE MMU model */
+ POWERPC_MMU_BOOKE = 0x00000008,
+ /* BookE FSL MMU model */
+ POWERPC_MMU_BOOKE_FSL = 0x00000009,
+ /* PowerPC 601 MMU model (specific BATs format) */
+ POWERPC_MMU_601 = 0x0000000A,
+#if defined(TARGET_PPC64)
+#define POWERPC_MMU_64 0x00010000
+ /* 64 bits PowerPC MMU */
+ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001,
+ /* 620 variant (no segment exceptions) */
+ POWERPC_MMU_620 = POWERPC_MMU_64 | 0x00000002,
+#endif /* defined(TARGET_PPC64) */
+};
+
+/*****************************************************************************/
+/* Exception model */
+typedef enum powerpc_excp_t powerpc_excp_t;
+enum powerpc_excp_t {
+ POWERPC_EXCP_UNKNOWN = 0,
+ /* Standard PowerPC exception model */
+ POWERPC_EXCP_STD,
+ /* PowerPC 40x exception model */
+ POWERPC_EXCP_40x,
+ /* PowerPC 601 exception model */
+ POWERPC_EXCP_601,
+ /* PowerPC 602 exception model */
+ POWERPC_EXCP_602,
+ /* PowerPC 603 exception model */
+ POWERPC_EXCP_603,
+ /* PowerPC 603e exception model */
+ POWERPC_EXCP_603E,
+ /* PowerPC G2 exception model */
+ POWERPC_EXCP_G2,
+ /* PowerPC 604 exception model */
+ POWERPC_EXCP_604,
+ /* PowerPC 7x0 exception model */
+ POWERPC_EXCP_7x0,
+ /* PowerPC 7x5 exception model */
+ POWERPC_EXCP_7x5,
+ /* PowerPC 74xx exception model */
+ POWERPC_EXCP_74xx,
+ /* BookE exception model */
+ POWERPC_EXCP_BOOKE,
+#if defined(TARGET_PPC64)
+ /* PowerPC 970 exception model */
+ POWERPC_EXCP_970,
+#endif /* defined(TARGET_PPC64) */
+};
+
+/*****************************************************************************/
+/* Exception vectors definitions */
+enum {
+ POWERPC_EXCP_NONE = -1,
+ /* The 64 first entries are used by the PowerPC embedded specification */
+ POWERPC_EXCP_CRITICAL = 0, /* Critical input */
+ POWERPC_EXCP_MCHECK = 1, /* Machine check exception */
+ POWERPC_EXCP_DSI = 2, /* Data storage exception */
+ POWERPC_EXCP_ISI = 3, /* Instruction storage exception */
+ POWERPC_EXCP_EXTERNAL = 4, /* External input */
+ POWERPC_EXCP_ALIGN = 5, /* Alignment exception */
+ POWERPC_EXCP_PROGRAM = 6, /* Program exception */
+ POWERPC_EXCP_FPU = 7, /* Floating-point unavailable exception */
+ POWERPC_EXCP_SYSCALL = 8, /* System call exception */
+ POWERPC_EXCP_APU = 9, /* Auxiliary processor unavailable */
+ POWERPC_EXCP_DECR = 10, /* Decrementer exception */
+ POWERPC_EXCP_FIT = 11, /* Fixed-interval timer interrupt */
+ POWERPC_EXCP_WDT = 12, /* Watchdog timer interrupt */
+ POWERPC_EXCP_DTLB = 13, /* Data TLB miss */
+ POWERPC_EXCP_ITLB = 14, /* Instruction TLB miss */
+ POWERPC_EXCP_DEBUG = 15, /* Debug interrupt */
+ /* Vectors 16 to 31 are reserved */
+ POWERPC_EXCP_SPEU = 32, /* SPE/embedded floating-point unavailable */
+ POWERPC_EXCP_EFPDI = 33, /* Embedded floating-point data interrupt */
+ POWERPC_EXCP_EFPRI = 34, /* Embedded floating-point round interrupt */
+ POWERPC_EXCP_EPERFM = 35, /* Embedded performance monitor interrupt */
+ POWERPC_EXCP_DOORI = 36, /* Embedded doorbell interrupt */
+ POWERPC_EXCP_DOORCI = 37, /* Embedded doorbell critical interrupt */
+ /* Vectors 38 to 63 are reserved */
+ /* Exceptions defined in the PowerPC server specification */
+ POWERPC_EXCP_RESET = 64, /* System reset exception */
+ POWERPC_EXCP_DSEG = 65, /* Data segment exception */
+ POWERPC_EXCP_ISEG = 66, /* Instruction segment exception */
+ POWERPC_EXCP_HDECR = 67, /* Hypervisor decrementer exception */
+ POWERPC_EXCP_TRACE = 68, /* Trace exception */
+ POWERPC_EXCP_HDSI = 69, /* Hypervisor data storage exception */
+ POWERPC_EXCP_HISI = 70, /* Hypervisor instruction storage exception */
+ POWERPC_EXCP_HDSEG = 71, /* Hypervisor data segment exception */
+ POWERPC_EXCP_HISEG = 72, /* Hypervisor instruction segment exception */
+ POWERPC_EXCP_VPU = 73, /* Vector unavailable exception */
+ /* 40x specific exceptions */
+ POWERPC_EXCP_PIT = 74, /* Programmable interval timer interrupt */
+ /* 601 specific exceptions */
+ POWERPC_EXCP_IO = 75, /* IO error exception */
+ POWERPC_EXCP_RUNM = 76, /* Run mode exception */
+ /* 602 specific exceptions */
+ POWERPC_EXCP_EMUL = 77, /* Emulation trap exception */
+ /* 602/603 specific exceptions */
+ POWERPC_EXCP_IFTLB = 78, /* Instruction fetch TLB miss */
+ POWERPC_EXCP_DLTLB = 79, /* Data load TLB miss */
+ POWERPC_EXCP_DSTLB = 80, /* Data store TLB miss */
+ /* Exceptions available on most PowerPC */
+ POWERPC_EXCP_FPA = 81, /* Floating-point assist exception */
+ POWERPC_EXCP_DABR = 82, /* Data address breakpoint */
+ POWERPC_EXCP_IABR = 83, /* Instruction address breakpoint */
+ POWERPC_EXCP_SMI = 84, /* System management interrupt */
+ POWERPC_EXCP_PERFM = 85, /* Embedded performance monitor interrupt */
+ /* 7xx/74xx specific exceptions */
+ POWERPC_EXCP_THERM = 86, /* Thermal interrupt */
+ /* 74xx specific exceptions */
+ POWERPC_EXCP_VPUA = 87, /* Vector assist exception */
+ /* 970FX specific exceptions */
+ POWERPC_EXCP_SOFTP = 88, /* Soft patch exception */
+ POWERPC_EXCP_MAINT = 89, /* Maintenance exception */
+ /* Freescale embeded cores specific exceptions */
+ POWERPC_EXCP_MEXTBR = 90, /* Maskable external breakpoint */
+ POWERPC_EXCP_NMEXTBR = 91, /* Non maskable external breakpoint */
+ POWERPC_EXCP_ITLBE = 92, /* Instruction TLB error */
+ POWERPC_EXCP_DTLBE = 93, /* Data TLB error */
+ /* EOL */
+ POWERPC_EXCP_NB = 96,
+ /* Qemu exceptions: used internally during code translation */
+ POWERPC_EXCP_STOP = 0x200, /* stop translation */
+ POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */
+ /* Qemu exceptions: special cases we want to stop translation */
+ POWERPC_EXCP_SYNC = 0x202, /* context synchronizing instruction */
+ POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only */
+ POWERPC_EXCP_STCX = 0x204 /* Conditional stores in user mode */
+};
+
+/* Exceptions error codes */
+enum {
+ /* Exception subtypes for POWERPC_EXCP_ALIGN */
+ POWERPC_EXCP_ALIGN_FP = 0x01, /* FP alignment exception */
+ POWERPC_EXCP_ALIGN_LST = 0x02, /* Unaligned mult/extern load/store */
+ POWERPC_EXCP_ALIGN_LE = 0x03, /* Multiple little-endian access */
+ POWERPC_EXCP_ALIGN_PROT = 0x04, /* Access cross protection boundary */
+ POWERPC_EXCP_ALIGN_BAT = 0x05, /* Access cross a BAT/seg boundary */
+ POWERPC_EXCP_ALIGN_CACHE = 0x06, /* Impossible dcbz access */
+ /* Exception subtypes for POWERPC_EXCP_PROGRAM */
+ /* FP exceptions */
+ POWERPC_EXCP_FP = 0x10,
+ POWERPC_EXCP_FP_OX = 0x01, /* FP overflow */
+ POWERPC_EXCP_FP_UX = 0x02, /* FP underflow */
+ POWERPC_EXCP_FP_ZX = 0x03, /* FP divide by zero */
+ POWERPC_EXCP_FP_XX = 0x04, /* FP inexact */
+ POWERPC_EXCP_FP_VXSNAN = 0x05, /* FP invalid SNaN op */
+ POWERPC_EXCP_FP_VXISI = 0x06, /* FP invalid infinite subtraction */
+ POWERPC_EXCP_FP_VXIDI = 0x07, /* FP invalid infinite divide */
+ POWERPC_EXCP_FP_VXZDZ = 0x08, /* FP invalid zero divide */
+ POWERPC_EXCP_FP_VXIMZ = 0x09, /* FP invalid infinite * zero */
+ POWERPC_EXCP_FP_VXVC = 0x0A, /* FP invalid compare */
+ POWERPC_EXCP_FP_VXSOFT = 0x0B, /* FP invalid operation */
+ POWERPC_EXCP_FP_VXSQRT = 0x0C, /* FP invalid square root */
+ POWERPC_EXCP_FP_VXCVI = 0x0D, /* FP invalid integer conversion */
+ /* Invalid instruction */
+ POWERPC_EXCP_INVAL = 0x20,
+ POWERPC_EXCP_INVAL_INVAL = 0x01, /* Invalid instruction */
+ POWERPC_EXCP_INVAL_LSWX = 0x02, /* Invalid lswx instruction */
+ POWERPC_EXCP_INVAL_SPR = 0x03, /* Invalid SPR access */
+ POWERPC_EXCP_INVAL_FP = 0x04, /* Unimplemented mandatory fp instr */
+ /* Privileged instruction */
+ POWERPC_EXCP_PRIV = 0x30,
+ POWERPC_EXCP_PRIV_OPC = 0x01, /* Privileged operation exception */
+ POWERPC_EXCP_PRIV_REG = 0x02, /* Privileged register exception */
+ /* Trap */
+ POWERPC_EXCP_TRAP = 0x40,
+};
+
+/*****************************************************************************/
+/* Input pins model */
+typedef enum powerpc_input_t powerpc_input_t;
+enum powerpc_input_t {
+ PPC_FLAGS_INPUT_UNKNOWN = 0,
+ /* PowerPC 6xx bus */
+ PPC_FLAGS_INPUT_6xx,
+ /* BookE bus */
+ PPC_FLAGS_INPUT_BookE,
+ /* PowerPC 405 bus */
+ PPC_FLAGS_INPUT_405,
+ /* PowerPC 970 bus */
+ PPC_FLAGS_INPUT_970,
+ /* PowerPC 401 bus */
+ PPC_FLAGS_INPUT_401,
+ /* Freescale RCPU bus */
+ PPC_FLAGS_INPUT_RCPU,
+};
+
+#define PPC_INPUT(env) (env->bus_model)
+
+/*****************************************************************************/
+typedef struct ppc_def_t ppc_def_t;
+typedef struct opc_handler_t opc_handler_t;
+
+/*****************************************************************************/
+/* Types used to describe some PowerPC registers */
+typedef struct CPUPPCState CPUPPCState;
+typedef struct ppc_tb_t ppc_tb_t;
+typedef struct ppc_spr_t ppc_spr_t;
+typedef struct ppc_dcr_t ppc_dcr_t;
+typedef union ppc_avr_t ppc_avr_t;
+typedef union ppc_tlb_t ppc_tlb_t;
+
+/* SPR access micro-ops generations callbacks */
+struct ppc_spr_t {
+ void (*uea_read)(void *opaque, int gpr_num, int spr_num);
+ void (*uea_write)(void *opaque, int spr_num, int gpr_num);
+#if !defined(CONFIG_USER_ONLY)
+ void (*oea_read)(void *opaque, int gpr_num, int spr_num);
+ void (*oea_write)(void *opaque, int spr_num, int gpr_num);
+ void (*hea_read)(void *opaque, int gpr_num, int spr_num);
+ void (*hea_write)(void *opaque, int spr_num, int gpr_num);
+#endif
+ const char *name;
+};
+
+/* Altivec registers (128 bits) */
+union ppc_avr_t {
+ float32 f[4];
+ uint8_t u8[16];
+ uint16_t u16[8];
+ uint32_t u32[4];
+ int8_t s8[16];
+ int16_t s16[8];
+ int32_t s32[4];
+ uint64_t u64[2];
+};
+
+#if !defined(CONFIG_USER_ONLY)
+/* Software TLB cache */
+typedef struct ppc6xx_tlb_t ppc6xx_tlb_t;
+struct ppc6xx_tlb_t {
+ target_ulong pte0;
+ target_ulong pte1;
+ target_ulong EPN;
+};
+
+typedef struct ppcemb_tlb_t ppcemb_tlb_t;
+struct ppcemb_tlb_t {
+ target_phys_addr_t RPN;
+ target_ulong EPN;
+ target_ulong PID;
+ target_ulong size;
+ uint32_t prot;
+ uint32_t attr; /* Storage attributes */
+};
+
+union ppc_tlb_t {
+ ppc6xx_tlb_t tlb6;
+ ppcemb_tlb_t tlbe;
+};
+#endif
+
+typedef struct ppc_slb_t ppc_slb_t;
+struct ppc_slb_t {
+ uint64_t tmp64;
+ uint32_t tmp;
+};
+
+/*****************************************************************************/
+/* Machine state register bits definition */
+#define MSR_SF 63 /* Sixty-four-bit mode hflags */
+#define MSR_TAG 62 /* Tag-active mode (POWERx ?) */
+#define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */
+#define MSR_SHV 60 /* hypervisor state hflags */
+#define MSR_CM 31 /* Computation mode for BookE hflags */
+#define MSR_ICM 30 /* Interrupt computation mode for BookE */
+#define MSR_THV 29 /* hypervisor state for 32 bits PowerPC hflags */
+#define MSR_UCLE 26 /* User-mode cache lock enable for BookE */
+#define MSR_VR 25 /* altivec available x hflags */
+#define MSR_SPE 25 /* SPE enable for BookE x hflags */
+#define MSR_AP 23 /* Access privilege state on 602 hflags */
+#define MSR_SA 22 /* Supervisor access mode on 602 hflags */
+#define MSR_KEY 19 /* key bit on 603e */
+#define MSR_POW 18 /* Power management */
+#define MSR_TGPR 17 /* TGPR usage on 602/603 x */
+#define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC x */
+#define MSR_ILE 16 /* Interrupt little-endian mode */
+#define MSR_EE 15 /* External interrupt enable */
+#define MSR_PR 14 /* Problem state hflags */
+#define MSR_FP 13 /* Floating point available hflags */
+#define MSR_ME 12 /* Machine check interrupt enable */
+#define MSR_FE0 11 /* Floating point exception mode 0 hflags */
+#define MSR_SE 10 /* Single-step trace enable x hflags */
+#define MSR_DWE 10 /* Debug wait enable on 405 x */
+#define MSR_UBLE 10 /* User BTB lock enable on e500 x */
+#define MSR_BE 9 /* Branch trace enable x hflags */
+#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC x */
+#define MSR_FE1 8 /* Floating point exception mode 1 hflags */
+#define MSR_AL 7 /* AL bit on POWER */
+#define MSR_EP 6 /* Exception prefix on 601 */
+#define MSR_IR 5 /* Instruction relocate */
+#define MSR_DR 4 /* Data relocate */
+#define MSR_PE 3 /* Protection enable on 403 */
+#define MSR_PX 2 /* Protection exclusive on 403 x */
+#define MSR_PMM 2 /* Performance monitor mark on POWER x */
+#define MSR_RI 1 /* Recoverable interrupt 1 */
+#define MSR_LE 0 /* Little-endian mode 1 hflags */
+
+#define msr_sf ((env->msr >> MSR_SF) & 1)
+#define msr_isf ((env->msr >> MSR_ISF) & 1)
+#define msr_shv ((env->msr >> MSR_SHV) & 1)
+#define msr_cm ((env->msr >> MSR_CM) & 1)
+#define msr_icm ((env->msr >> MSR_ICM) & 1)
+#define msr_thv ((env->msr >> MSR_THV) & 1)
+#define msr_ucle ((env->msr >> MSR_UCLE) & 1)
+#define msr_vr ((env->msr >> MSR_VR) & 1)
+#define msr_spe ((env->msr >> MSR_SPE) & 1)
+#define msr_ap ((env->msr >> MSR_AP) & 1)
+#define msr_sa ((env->msr >> MSR_SA) & 1)
+#define msr_key ((env->msr >> MSR_KEY) & 1)
+#define msr_pow ((env->msr >> MSR_POW) & 1)
+#define msr_tgpr ((env->msr >> MSR_TGPR) & 1)
+#define msr_ce ((env->msr >> MSR_CE) & 1)
+#define msr_ile ((env->msr >> MSR_ILE) & 1)
+#define msr_ee ((env->msr >> MSR_EE) & 1)
+#define msr_pr ((env->msr >> MSR_PR) & 1)
+#define msr_fp ((env->msr >> MSR_FP) & 1)
+#define msr_me ((env->msr >> MSR_ME) & 1)
+#define msr_fe0 ((env->msr >> MSR_FE0) & 1)
+#define msr_se ((env->msr >> MSR_SE) & 1)
+#define msr_dwe ((env->msr >> MSR_DWE) & 1)
+#define msr_uble ((env->msr >> MSR_UBLE) & 1)
+#define msr_be ((env->msr >> MSR_BE) & 1)
+#define msr_de ((env->msr >> MSR_DE) & 1)
+#define msr_fe1 ((env->msr >> MSR_FE1) & 1)
+#define msr_al ((env->msr >> MSR_AL) & 1)
+#define msr_ep ((env->msr >> MSR_EP) & 1)
+#define msr_ir ((env->msr >> MSR_IR) & 1)
+#define msr_dr ((env->msr >> MSR_DR) & 1)
+#define msr_pe ((env->msr >> MSR_PE) & 1)
+#define msr_px ((env->msr >> MSR_PX) & 1)
+#define msr_pmm ((env->msr >> MSR_PMM) & 1)
+#define msr_ri ((env->msr >> MSR_RI) & 1)
+#define msr_le ((env->msr >> MSR_LE) & 1)
+/* Hypervisor bit is more specific */
+#if defined(TARGET_PPC64)
+#define MSR_HVB (1ULL << MSR_SHV)
+#define msr_hv msr_shv
+#else
+#if defined(PPC_EMULATE_32BITS_HYPV)
+#define MSR_HVB (1ULL << MSR_THV)
+#define msr_hv msr_thv
+#else
+#define MSR_HVB (0ULL)
+#define msr_hv (0)
+#endif
+#endif
+
+/* Exception state register bits definition */
+#define ESR_ST 23 /* Exception was caused by a store type access. */
+
+enum {
+ POWERPC_FLAG_NONE = 0x00000000,
+ /* Flag for MSR bit 25 signification (VRE/SPE) */
+ POWERPC_FLAG_SPE = 0x00000001,
+ POWERPC_FLAG_VRE = 0x00000002,
+ /* Flag for MSR bit 17 signification (TGPR/CE) */
+ POWERPC_FLAG_TGPR = 0x00000004,
+ POWERPC_FLAG_CE = 0x00000008,
+ /* Flag for MSR bit 10 signification (SE/DWE/UBLE) */
+ POWERPC_FLAG_SE = 0x00000010,
+ POWERPC_FLAG_DWE = 0x00000020,
+ POWERPC_FLAG_UBLE = 0x00000040,
+ /* Flag for MSR bit 9 signification (BE/DE) */
+ POWERPC_FLAG_BE = 0x00000080,
+ POWERPC_FLAG_DE = 0x00000100,
+ /* Flag for MSR bit 2 signification (PX/PMM) */
+ POWERPC_FLAG_PX = 0x00000200,
+ POWERPC_FLAG_PMM = 0x00000400,
+ /* Flag for special features */
+ /* Decrementer clock: RTC clock (POWER, 601) or bus clock */
+ POWERPC_FLAG_RTC_CLK = 0x00010000,
+ POWERPC_FLAG_BUS_CLK = 0x00020000,
+};
+
+/*****************************************************************************/
+/* Floating point status and control register */
+#define FPSCR_FX 31 /* Floating-point exception summary */
+#define FPSCR_FEX 30 /* Floating-point enabled exception summary */
+#define FPSCR_VX 29 /* Floating-point invalid operation exception summ. */
+#define FPSCR_OX 28 /* Floating-point overflow exception */
+#define FPSCR_UX 27 /* Floating-point underflow exception */
+#define FPSCR_ZX 26 /* Floating-point zero divide exception */
+#define FPSCR_XX 25 /* Floating-point inexact exception */
+#define FPSCR_VXSNAN 24 /* Floating-point invalid operation exception (sNan) */
+#define FPSCR_VXISI 23 /* Floating-point invalid operation exception (inf) */
+#define FPSCR_VXIDI 22 /* Floating-point invalid operation exception (inf) */
+#define FPSCR_VXZDZ 21 /* Floating-point invalid operation exception (zero) */
+#define FPSCR_VXIMZ 20 /* Floating-point invalid operation exception (inf) */
+#define FPSCR_VXVC 19 /* Floating-point invalid operation exception (comp) */
+#define FPSCR_FR 18 /* Floating-point fraction rounded */
+#define FPSCR_FI 17 /* Floating-point fraction inexact */
+#define FPSCR_C 16 /* Floating-point result class descriptor */
+#define FPSCR_FL 15 /* Floating-point less than or negative */
+#define FPSCR_FG 14 /* Floating-point greater than or negative */
+#define FPSCR_FE 13 /* Floating-point equal or zero */
+#define FPSCR_FU 12 /* Floating-point unordered or NaN */
+#define FPSCR_FPCC 12 /* Floating-point condition code */
+#define FPSCR_FPRF 12 /* Floating-point result flags */
+#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */
+#define FPSCR_VXSQRT 9 /* Floating-point invalid operation exception (sqrt) */
+#define FPSCR_VXCVI 8 /* Floating-point invalid operation exception (int) */
+#define FPSCR_VE 7 /* Floating-point invalid operation exception enable */
+#define FPSCR_OE 6 /* Floating-point overflow exception enable */
+#define FPSCR_UE 5 /* Floating-point undeflow exception enable */
+#define FPSCR_ZE 4 /* Floating-point zero divide exception enable */
+#define FPSCR_XE 3 /* Floating-point inexact exception enable */
+#define FPSCR_NI 2 /* Floating-point non-IEEE mode */
+#define FPSCR_RN1 1
+#define FPSCR_RN 0 /* Floating-point rounding control */
+#define fpscr_fex (((env->fpscr) >> FPSCR_FEX) & 0x1)
+#define fpscr_vx (((env->fpscr) >> FPSCR_VX) & 0x1)
+#define fpscr_ox (((env->fpscr) >> FPSCR_OX) & 0x1)
+#define fpscr_ux (((env->fpscr) >> FPSCR_UX) & 0x1)
+#define fpscr_zx (((env->fpscr) >> FPSCR_ZX) & 0x1)
+#define fpscr_xx (((env->fpscr) >> FPSCR_XX) & 0x1)
+#define fpscr_vxsnan (((env->fpscr) >> FPSCR_VXSNAN) & 0x1)
+#define fpscr_vxisi (((env->fpscr) >> FPSCR_VXISI) & 0x1)
+#define fpscr_vxidi (((env->fpscr) >> FPSCR_VXIDI) & 0x1)
+#define fpscr_vxzdz (((env->fpscr) >> FPSCR_VXZDZ) & 0x1)
+#define fpscr_vximz (((env->fpscr) >> FPSCR_VXIMZ) & 0x1)
+#define fpscr_vxvc (((env->fpscr) >> FPSCR_VXVC) & 0x1)
+#define fpscr_fpcc (((env->fpscr) >> FPSCR_FPCC) & 0xF)
+#define fpscr_vxsoft (((env->fpscr) >> FPSCR_VXSOFT) & 0x1)
+#define fpscr_vxsqrt (((env->fpscr) >> FPSCR_VXSQRT) & 0x1)
+#define fpscr_vxcvi (((env->fpscr) >> FPSCR_VXCVI) & 0x1)
+#define fpscr_ve (((env->fpscr) >> FPSCR_VE) & 0x1)
+#define fpscr_oe (((env->fpscr) >> FPSCR_OE) & 0x1)
+#define fpscr_ue (((env->fpscr) >> FPSCR_UE) & 0x1)
+#define fpscr_ze (((env->fpscr) >> FPSCR_ZE) & 0x1)
+#define fpscr_xe (((env->fpscr) >> FPSCR_XE) & 0x1)
+#define fpscr_ni (((env->fpscr) >> FPSCR_NI) & 0x1)
+#define fpscr_rn (((env->fpscr) >> FPSCR_RN) & 0x3)
+/* Invalid operation exception summary */
+#define fpscr_ix ((env->fpscr) & ((1 << FPSCR_VXSNAN) | (1 << FPSCR_VXISI) | \
+ (1 << FPSCR_VXIDI) | (1 << FPSCR_VXZDZ) | \
+ (1 << FPSCR_VXIMZ) | (1 << FPSCR_VXVC) | \
+ (1 << FPSCR_VXSOFT) | (1 << FPSCR_VXSQRT) | \
+ (1 << FPSCR_VXCVI)))
+/* exception summary */
+#define fpscr_ex (((env->fpscr) >> FPSCR_XX) & 0x1F)
+/* enabled exception summary */
+#define fpscr_eex (((env->fpscr) >> FPSCR_XX) & ((env->fpscr) >> FPSCR_XE) & \
+ 0x1F)
+
+/*****************************************************************************/
+/* Vector status and control register */
+#define VSCR_NJ 16 /* Vector non-java */
+#define VSCR_SAT 0 /* Vector saturation */
+#define vscr_nj (((env->vscr) >> VSCR_NJ) & 0x1)
+#define vscr_sat (((env->vscr) >> VSCR_SAT) & 0x1)
+
+/*****************************************************************************/
+/* The whole PowerPC CPU context */
+#define NB_MMU_MODES 3
+
+struct CPUPPCState {
+ /* First are the most commonly used resources
+ * during translated code execution
+ */
+ /* general purpose registers */
+ target_ulong gpr[32];
+#if !defined(TARGET_PPC64)
+ /* Storage for GPR MSB, used by the SPE extension */
+ target_ulong gprh[32];
+#endif
+ /* LR */
+ target_ulong lr;
+ /* CTR */
+ target_ulong ctr;
+ /* condition register */
+ uint32_t crf[8];
+ /* XER */
+ target_ulong xer;
+ /* Reservation address */
+ target_ulong reserve_addr;
+ /* Reservation value */
+ target_ulong reserve_val;
+ /* Reservation store address */
+ target_ulong reserve_ea;
+ /* Reserved store source register and size */
+ target_ulong reserve_info;
+
+ /* Those ones are used in supervisor mode only */
+ /* machine state register */
+ target_ulong msr;
+ /* temporary general purpose registers */
+ target_ulong tgpr[4]; /* Used to speed-up TLB assist handlers */
+
+ /* Floating point execution context */
+ float_status fp_status;
+ /* floating point registers */
+ float64 fpr[32];
+ /* floating point status and control register */
+ uint32_t fpscr;
+
+ /* Next instruction pointer */
+ target_ulong nip;
+
+ int access_type; /* when a memory exception occurs, the access
+ type is stored here */
+
+ CPU_COMMON
+
+ /* MMU context - only relevant for full system emulation */
+#if !defined(CONFIG_USER_ONLY)
+#if defined(TARGET_PPC64)
+ /* Address space register */
+ target_ulong asr;
+ /* PowerPC 64 SLB area */
+ ppc_slb_t slb[64];
+ int slb_nr;
+#endif
+ /* segment registers */
+ target_ulong sdr1;
+ target_ulong sr[32];
+ /* BATs */
+ int nb_BATs;
+ target_ulong DBAT[2][8];
+ target_ulong IBAT[2][8];
+ /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
+ int nb_tlb; /* Total number of TLB */
+ int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */
+ int nb_ways; /* Number of ways in the TLB set */
+ int last_way; /* Last used way used to allocate TLB in a LRU way */
+ int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */
+ int nb_pids; /* Number of available PID registers */
+ ppc_tlb_t *tlb; /* TLB is optional. Allocate them only if needed */
+ /* 403 dedicated access protection registers */
+ target_ulong pb[4];
+#endif
+
+ /* Other registers */
+ /* Special purpose registers */
+ target_ulong spr[1024];
+ ppc_spr_t spr_cb[1024];
+ /* Altivec registers */
+ ppc_avr_t avr[32];
+ uint32_t vscr;
+ /* SPE registers */
+ uint64_t spe_acc;
+ uint32_t spe_fscr;
+ /* SPE and Altivec can share a status since they will never be used
+ * simultaneously */
+ float_status vec_status;
+
+ /* Internal devices resources */
+ /* Time base and decrementer */
+ ppc_tb_t *tb_env;
+ /* Device control registers */
+ ppc_dcr_t *dcr_env;
+
+ int dcache_line_size;
+ int icache_line_size;
+
+ /* Those resources are used during exception processing */
+ /* CPU model definition */
+ target_ulong msr_mask;
+ powerpc_mmu_t mmu_model;
+ powerpc_excp_t excp_model;
+ powerpc_input_t bus_model;
+ int bfd_mach;
+ uint32_t flags;
+ uint64_t insns_flags;
+
+ int error_code;
+ uint32_t pending_interrupts;
+#if !defined(CONFIG_USER_ONLY)
+ /* This is the IRQ controller, which is implementation dependant
+ * and only relevant when emulating a complete machine.
+ */
+ uint32_t irq_input_state;
+ void **irq_inputs;
+ /* Exception vectors */
+ target_ulong excp_vectors[POWERPC_EXCP_NB];
+ target_ulong excp_prefix;
+ target_ulong hreset_excp_prefix;
+ target_ulong ivor_mask;
+ target_ulong ivpr_mask;
+ target_ulong hreset_vector;
+#endif
+
+ /* Those resources are used only during code translation */
+ /* opcode handlers */
+ opc_handler_t *opcodes[0x40];
+
+ /* Those resources are used only in Qemu core */
+ target_ulong hflags; /* hflags is a MSR & HFLAGS_MASK */
+ target_ulong hflags_nmsr; /* specific hflags, not comming from MSR */
+ int mmu_idx; /* precomputed MMU index to speed up mem accesses */
+
+ /* Power management */
+ int power_mode;
+ int (*check_pow)(CPUPPCState *env);
+
+#if !defined(CONFIG_USER_ONLY)
+ void *load_info; /* Holds boot loading state. */
+#endif
+};
+
+#if !defined(CONFIG_USER_ONLY)
+/* Context used internally during MMU translations */
+typedef struct mmu_ctx_t mmu_ctx_t;
+struct mmu_ctx_t {
+ target_phys_addr_t raddr; /* Real address */
+ target_phys_addr_t eaddr; /* Effective address */
+ int prot; /* Protection bits */
+ target_phys_addr_t pg_addr[2]; /* PTE tables base addresses */
+ target_ulong ptem; /* Virtual segment ID | API */
+ int key; /* Access key */
+ int nx; /* Non-execute area */
+};
+#endif
+
+/*****************************************************************************/
+CPUPPCState *cpu_ppc_init (const char *cpu_model);
+void ppc_translate_init(void);
+int cpu_ppc_exec (CPUPPCState *s);
+void cpu_ppc_close (CPUPPCState *s);
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_ppc_signal_handler (int host_signum, void *pinfo,
+ void *puc);
+int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_ppc_handle_mmu_fault
+#if !defined(CONFIG_USER_ONLY)
+int get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, target_ulong vaddr,
+ int rw, int access_type);
+#endif
+void do_interrupt (CPUPPCState *env);
+void ppc_hw_interrupt (CPUPPCState *env);
+
+void cpu_dump_rfi (target_ulong RA, target_ulong msr);
+
+#if !defined(CONFIG_USER_ONLY)
+void ppc6xx_tlb_store (CPUPPCState *env, target_ulong EPN, int way, int is_code,
+ target_ulong pte0, target_ulong pte1);
+void ppc_store_ibatu (CPUPPCState *env, int nr, target_ulong value);
+void ppc_store_ibatl (CPUPPCState *env, int nr, target_ulong value);
+void ppc_store_dbatu (CPUPPCState *env, int nr, target_ulong value);
+void ppc_store_dbatl (CPUPPCState *env, int nr, target_ulong value);
+void ppc_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value);
+void ppc_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value);
+void ppc_store_sdr1 (CPUPPCState *env, target_ulong value);
+#if defined(TARGET_PPC64)
+void ppc_store_asr (CPUPPCState *env, target_ulong value);
+target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr);
+target_ulong ppc_load_sr (CPUPPCState *env, int sr_nr);
+void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs);
+#endif /* defined(TARGET_PPC64) */
+void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value);
+#endif /* !defined(CONFIG_USER_ONLY) */
+void ppc_store_msr (CPUPPCState *env, target_ulong value);
+
+void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf);
+
+const ppc_def_t *cpu_ppc_find_by_name (const char *name);
+int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def);
+
+/* Time-base and decrementer management */
+#ifndef NO_CPU_IO_DEFS
+uint64_t cpu_ppc_load_tbl (CPUPPCState *env);
+uint32_t cpu_ppc_load_tbu (CPUPPCState *env);
+void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value);
+void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value);
+uint64_t cpu_ppc_load_atbl (CPUPPCState *env);
+uint32_t cpu_ppc_load_atbu (CPUPPCState *env);
+void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value);
+void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value);
+uint32_t cpu_ppc_load_decr (CPUPPCState *env);
+void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value);
+uint32_t cpu_ppc_load_hdecr (CPUPPCState *env);
+void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value);
+uint64_t cpu_ppc_load_purr (CPUPPCState *env);
+void cpu_ppc_store_purr (CPUPPCState *env, uint64_t value);
+uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env);
+uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env);
+#if !defined(CONFIG_USER_ONLY)
+void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value);
+void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value);
+target_ulong load_40x_pit (CPUPPCState *env);
+void store_40x_pit (CPUPPCState *env, target_ulong val);
+void store_40x_dbcr0 (CPUPPCState *env, uint32_t val);
+void store_40x_sler (CPUPPCState *env, uint32_t val);
+void store_booke_tcr (CPUPPCState *env, target_ulong val);
+void store_booke_tsr (CPUPPCState *env, target_ulong val);
+void ppc_tlb_invalidate_all (CPUPPCState *env);
+void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr);
+#if defined(TARGET_PPC64)
+void ppc_slb_invalidate_all (CPUPPCState *env);
+void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0);
+#endif
+int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid);
+#endif
+#endif
+
+static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn)
+{
+ uint64_t gprv;
+
+ gprv = env->gpr[gprn];
+#if !defined(TARGET_PPC64)
+ if (env->flags & POWERPC_FLAG_SPE) {
+ /* If the CPU implements the SPE extension, we have to get the
+ * high bits of the GPR from the gprh storage area
+ */
+ gprv &= 0xFFFFFFFFULL;
+ gprv |= (uint64_t)env->gprh[gprn] << 32;
+ }
+#endif
+
+ return gprv;
+}
+
+/* Device control registers */
+int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp);
+int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val);
+
+#define cpu_init cpu_ppc_init
+#define cpu_exec cpu_ppc_exec
+#define cpu_gen_code cpu_ppc_gen_code
+#define cpu_signal_handler cpu_ppc_signal_handler
+#define cpu_list ppc_cpu_list
+
+#define CPU_SAVE_VERSION 4
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _user
+#define MMU_MODE1_SUFFIX _kernel
+#define MMU_MODE2_SUFFIX _hypv
+#define MMU_USER_IDX 0
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return env->mmu_idx;
+}
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->gpr[1] = newsp;
+ env->gpr[3] = 0;
+}
+#endif
+
+#include "cpu-all.h"
+
+/*****************************************************************************/
+/* CRF definitions */
+#define CRF_LT 3
+#define CRF_GT 2
+#define CRF_EQ 1
+#define CRF_SO 0
+#define CRF_CH (1 << CRF_LT)
+#define CRF_CL (1 << CRF_GT)
+#define CRF_CH_OR_CL (1 << CRF_EQ)
+#define CRF_CH_AND_CL (1 << CRF_SO)
+
+/* XER definitions */
+#define XER_SO 31
+#define XER_OV 30
+#define XER_CA 29
+#define XER_CMP 8
+#define XER_BC 0
+#define xer_so ((env->xer >> XER_SO) & 1)
+#define xer_ov ((env->xer >> XER_OV) & 1)
+#define xer_ca ((env->xer >> XER_CA) & 1)
+#define xer_cmp ((env->xer >> XER_CMP) & 0xFF)
+#define xer_bc ((env->xer >> XER_BC) & 0x7F)
+
+/* SPR definitions */
+#define SPR_MQ (0x000)
+#define SPR_XER (0x001)
+#define SPR_601_VRTCU (0x004)
+#define SPR_601_VRTCL (0x005)
+#define SPR_601_UDECR (0x006)
+#define SPR_LR (0x008)
+#define SPR_CTR (0x009)
+#define SPR_DSISR (0x012)
+#define SPR_DAR (0x013) /* DAE for PowerPC 601 */
+#define SPR_601_RTCU (0x014)
+#define SPR_601_RTCL (0x015)
+#define SPR_DECR (0x016)
+#define SPR_SDR1 (0x019)
+#define SPR_SRR0 (0x01A)
+#define SPR_SRR1 (0x01B)
+#define SPR_AMR (0x01D)
+#define SPR_BOOKE_PID (0x030)
+#define SPR_BOOKE_DECAR (0x036)
+#define SPR_BOOKE_CSRR0 (0x03A)
+#define SPR_BOOKE_CSRR1 (0x03B)
+#define SPR_BOOKE_DEAR (0x03D)
+#define SPR_BOOKE_ESR (0x03E)
+#define SPR_BOOKE_IVPR (0x03F)
+#define SPR_MPC_EIE (0x050)
+#define SPR_MPC_EID (0x051)
+#define SPR_MPC_NRI (0x052)
+#define SPR_CTRL (0x088)
+#define SPR_MPC_CMPA (0x090)
+#define SPR_MPC_CMPB (0x091)
+#define SPR_MPC_CMPC (0x092)
+#define SPR_MPC_CMPD (0x093)
+#define SPR_MPC_ECR (0x094)
+#define SPR_MPC_DER (0x095)
+#define SPR_MPC_COUNTA (0x096)
+#define SPR_MPC_COUNTB (0x097)
+#define SPR_UCTRL (0x098)
+#define SPR_MPC_CMPE (0x098)
+#define SPR_MPC_CMPF (0x099)
+#define SPR_MPC_CMPG (0x09A)
+#define SPR_MPC_CMPH (0x09B)
+#define SPR_MPC_LCTRL1 (0x09C)
+#define SPR_MPC_LCTRL2 (0x09D)
+#define SPR_MPC_ICTRL (0x09E)
+#define SPR_MPC_BAR (0x09F)
+#define SPR_VRSAVE (0x100)
+#define SPR_USPRG0 (0x100)
+#define SPR_USPRG1 (0x101)
+#define SPR_USPRG2 (0x102)
+#define SPR_USPRG3 (0x103)
+#define SPR_USPRG4 (0x104)
+#define SPR_USPRG5 (0x105)
+#define SPR_USPRG6 (0x106)
+#define SPR_USPRG7 (0x107)
+#define SPR_VTBL (0x10C)
+#define SPR_VTBU (0x10D)
+#define SPR_SPRG0 (0x110)
+#define SPR_SPRG1 (0x111)
+#define SPR_SPRG2 (0x112)
+#define SPR_SPRG3 (0x113)
+#define SPR_SPRG4 (0x114)
+#define SPR_SCOMC (0x114)
+#define SPR_SPRG5 (0x115)
+#define SPR_SCOMD (0x115)
+#define SPR_SPRG6 (0x116)
+#define SPR_SPRG7 (0x117)
+#define SPR_ASR (0x118)
+#define SPR_EAR (0x11A)
+#define SPR_TBL (0x11C)
+#define SPR_TBU (0x11D)
+#define SPR_TBU40 (0x11E)
+#define SPR_SVR (0x11E)
+#define SPR_BOOKE_PIR (0x11E)
+#define SPR_PVR (0x11F)
+#define SPR_HSPRG0 (0x130)
+#define SPR_BOOKE_DBSR (0x130)
+#define SPR_HSPRG1 (0x131)
+#define SPR_HDSISR (0x132)
+#define SPR_HDAR (0x133)
+#define SPR_BOOKE_DBCR0 (0x134)
+#define SPR_IBCR (0x135)
+#define SPR_PURR (0x135)
+#define SPR_BOOKE_DBCR1 (0x135)
+#define SPR_DBCR (0x136)
+#define SPR_HDEC (0x136)
+#define SPR_BOOKE_DBCR2 (0x136)
+#define SPR_HIOR (0x137)
+#define SPR_MBAR (0x137)
+#define SPR_RMOR (0x138)
+#define SPR_BOOKE_IAC1 (0x138)
+#define SPR_HRMOR (0x139)
+#define SPR_BOOKE_IAC2 (0x139)
+#define SPR_HSRR0 (0x13A)
+#define SPR_BOOKE_IAC3 (0x13A)
+#define SPR_HSRR1 (0x13B)
+#define SPR_BOOKE_IAC4 (0x13B)
+#define SPR_LPCR (0x13C)
+#define SPR_BOOKE_DAC1 (0x13C)
+#define SPR_LPIDR (0x13D)
+#define SPR_DABR2 (0x13D)
+#define SPR_BOOKE_DAC2 (0x13D)
+#define SPR_BOOKE_DVC1 (0x13E)
+#define SPR_BOOKE_DVC2 (0x13F)
+#define SPR_BOOKE_TSR (0x150)
+#define SPR_BOOKE_TCR (0x154)
+#define SPR_BOOKE_IVOR0 (0x190)
+#define SPR_BOOKE_IVOR1 (0x191)
+#define SPR_BOOKE_IVOR2 (0x192)
+#define SPR_BOOKE_IVOR3 (0x193)
+#define SPR_BOOKE_IVOR4 (0x194)
+#define SPR_BOOKE_IVOR5 (0x195)
+#define SPR_BOOKE_IVOR6 (0x196)
+#define SPR_BOOKE_IVOR7 (0x197)
+#define SPR_BOOKE_IVOR8 (0x198)
+#define SPR_BOOKE_IVOR9 (0x199)
+#define SPR_BOOKE_IVOR10 (0x19A)
+#define SPR_BOOKE_IVOR11 (0x19B)
+#define SPR_BOOKE_IVOR12 (0x19C)
+#define SPR_BOOKE_IVOR13 (0x19D)
+#define SPR_BOOKE_IVOR14 (0x19E)
+#define SPR_BOOKE_IVOR15 (0x19F)
+#define SPR_BOOKE_SPEFSCR (0x200)
+#define SPR_Exxx_BBEAR (0x201)
+#define SPR_Exxx_BBTAR (0x202)
+#define SPR_Exxx_L1CFG0 (0x203)
+#define SPR_Exxx_NPIDR (0x205)
+#define SPR_ATBL (0x20E)
+#define SPR_ATBU (0x20F)
+#define SPR_IBAT0U (0x210)
+#define SPR_BOOKE_IVOR32 (0x210)
+#define SPR_RCPU_MI_GRA (0x210)
+#define SPR_IBAT0L (0x211)
+#define SPR_BOOKE_IVOR33 (0x211)
+#define SPR_IBAT1U (0x212)
+#define SPR_BOOKE_IVOR34 (0x212)
+#define SPR_IBAT1L (0x213)
+#define SPR_BOOKE_IVOR35 (0x213)
+#define SPR_IBAT2U (0x214)
+#define SPR_BOOKE_IVOR36 (0x214)
+#define SPR_IBAT2L (0x215)
+#define SPR_BOOKE_IVOR37 (0x215)
+#define SPR_IBAT3U (0x216)
+#define SPR_IBAT3L (0x217)
+#define SPR_DBAT0U (0x218)
+#define SPR_RCPU_L2U_GRA (0x218)
+#define SPR_DBAT0L (0x219)
+#define SPR_DBAT1U (0x21A)
+#define SPR_DBAT1L (0x21B)
+#define SPR_DBAT2U (0x21C)
+#define SPR_DBAT2L (0x21D)
+#define SPR_DBAT3U (0x21E)
+#define SPR_DBAT3L (0x21F)
+#define SPR_IBAT4U (0x230)
+#define SPR_RPCU_BBCMCR (0x230)
+#define SPR_MPC_IC_CST (0x230)
+#define SPR_Exxx_CTXCR (0x230)
+#define SPR_IBAT4L (0x231)
+#define SPR_MPC_IC_ADR (0x231)
+#define SPR_Exxx_DBCR3 (0x231)
+#define SPR_IBAT5U (0x232)
+#define SPR_MPC_IC_DAT (0x232)
+#define SPR_Exxx_DBCNT (0x232)
+#define SPR_IBAT5L (0x233)
+#define SPR_IBAT6U (0x234)
+#define SPR_IBAT6L (0x235)
+#define SPR_IBAT7U (0x236)
+#define SPR_IBAT7L (0x237)
+#define SPR_DBAT4U (0x238)
+#define SPR_RCPU_L2U_MCR (0x238)
+#define SPR_MPC_DC_CST (0x238)
+#define SPR_Exxx_ALTCTXCR (0x238)
+#define SPR_DBAT4L (0x239)
+#define SPR_MPC_DC_ADR (0x239)
+#define SPR_DBAT5U (0x23A)
+#define SPR_BOOKE_MCSRR0 (0x23A)
+#define SPR_MPC_DC_DAT (0x23A)
+#define SPR_DBAT5L (0x23B)
+#define SPR_BOOKE_MCSRR1 (0x23B)
+#define SPR_DBAT6U (0x23C)
+#define SPR_BOOKE_MCSR (0x23C)
+#define SPR_DBAT6L (0x23D)
+#define SPR_Exxx_MCAR (0x23D)
+#define SPR_DBAT7U (0x23E)
+#define SPR_BOOKE_DSRR0 (0x23E)
+#define SPR_DBAT7L (0x23F)
+#define SPR_BOOKE_DSRR1 (0x23F)
+#define SPR_BOOKE_SPRG8 (0x25C)
+#define SPR_BOOKE_SPRG9 (0x25D)
+#define SPR_BOOKE_MAS0 (0x270)
+#define SPR_BOOKE_MAS1 (0x271)
+#define SPR_BOOKE_MAS2 (0x272)
+#define SPR_BOOKE_MAS3 (0x273)
+#define SPR_BOOKE_MAS4 (0x274)
+#define SPR_BOOKE_MAS5 (0x275)
+#define SPR_BOOKE_MAS6 (0x276)
+#define SPR_BOOKE_PID1 (0x279)
+#define SPR_BOOKE_PID2 (0x27A)
+#define SPR_MPC_DPDR (0x280)
+#define SPR_MPC_IMMR (0x288)
+#define SPR_BOOKE_TLB0CFG (0x2B0)
+#define SPR_BOOKE_TLB1CFG (0x2B1)
+#define SPR_BOOKE_TLB2CFG (0x2B2)
+#define SPR_BOOKE_TLB3CFG (0x2B3)
+#define SPR_BOOKE_EPR (0x2BE)
+#define SPR_PERF0 (0x300)
+#define SPR_RCPU_MI_RBA0 (0x300)
+#define SPR_MPC_MI_CTR (0x300)
+#define SPR_PERF1 (0x301)
+#define SPR_RCPU_MI_RBA1 (0x301)
+#define SPR_PERF2 (0x302)
+#define SPR_RCPU_MI_RBA2 (0x302)
+#define SPR_MPC_MI_AP (0x302)
+#define SPR_PERF3 (0x303)
+#define SPR_620_PMC1R (0x303)
+#define SPR_RCPU_MI_RBA3 (0x303)
+#define SPR_MPC_MI_EPN (0x303)
+#define SPR_PERF4 (0x304)
+#define SPR_620_PMC2R (0x304)
+#define SPR_PERF5 (0x305)
+#define SPR_MPC_MI_TWC (0x305)
+#define SPR_PERF6 (0x306)
+#define SPR_MPC_MI_RPN (0x306)
+#define SPR_PERF7 (0x307)
+#define SPR_PERF8 (0x308)
+#define SPR_RCPU_L2U_RBA0 (0x308)
+#define SPR_MPC_MD_CTR (0x308)
+#define SPR_PERF9 (0x309)
+#define SPR_RCPU_L2U_RBA1 (0x309)
+#define SPR_MPC_MD_CASID (0x309)
+#define SPR_PERFA (0x30A)
+#define SPR_RCPU_L2U_RBA2 (0x30A)
+#define SPR_MPC_MD_AP (0x30A)
+#define SPR_PERFB (0x30B)
+#define SPR_620_MMCR0R (0x30B)
+#define SPR_RCPU_L2U_RBA3 (0x30B)
+#define SPR_MPC_MD_EPN (0x30B)
+#define SPR_PERFC (0x30C)
+#define SPR_MPC_MD_TWB (0x30C)
+#define SPR_PERFD (0x30D)
+#define SPR_MPC_MD_TWC (0x30D)
+#define SPR_PERFE (0x30E)
+#define SPR_MPC_MD_RPN (0x30E)
+#define SPR_PERFF (0x30F)
+#define SPR_MPC_MD_TW (0x30F)
+#define SPR_UPERF0 (0x310)
+#define SPR_UPERF1 (0x311)
+#define SPR_UPERF2 (0x312)
+#define SPR_UPERF3 (0x313)
+#define SPR_620_PMC1W (0x313)
+#define SPR_UPERF4 (0x314)
+#define SPR_620_PMC2W (0x314)
+#define SPR_UPERF5 (0x315)
+#define SPR_UPERF6 (0x316)
+#define SPR_UPERF7 (0x317)
+#define SPR_UPERF8 (0x318)
+#define SPR_UPERF9 (0x319)
+#define SPR_UPERFA (0x31A)
+#define SPR_UPERFB (0x31B)
+#define SPR_620_MMCR0W (0x31B)
+#define SPR_UPERFC (0x31C)
+#define SPR_UPERFD (0x31D)
+#define SPR_UPERFE (0x31E)
+#define SPR_UPERFF (0x31F)
+#define SPR_RCPU_MI_RA0 (0x320)
+#define SPR_MPC_MI_DBCAM (0x320)
+#define SPR_RCPU_MI_RA1 (0x321)
+#define SPR_MPC_MI_DBRAM0 (0x321)
+#define SPR_RCPU_MI_RA2 (0x322)
+#define SPR_MPC_MI_DBRAM1 (0x322)
+#define SPR_RCPU_MI_RA3 (0x323)
+#define SPR_RCPU_L2U_RA0 (0x328)
+#define SPR_MPC_MD_DBCAM (0x328)
+#define SPR_RCPU_L2U_RA1 (0x329)
+#define SPR_MPC_MD_DBRAM0 (0x329)
+#define SPR_RCPU_L2U_RA2 (0x32A)
+#define SPR_MPC_MD_DBRAM1 (0x32A)
+#define SPR_RCPU_L2U_RA3 (0x32B)
+#define SPR_440_INV0 (0x370)
+#define SPR_440_INV1 (0x371)
+#define SPR_440_INV2 (0x372)
+#define SPR_440_INV3 (0x373)
+#define SPR_440_ITV0 (0x374)
+#define SPR_440_ITV1 (0x375)
+#define SPR_440_ITV2 (0x376)
+#define SPR_440_ITV3 (0x377)
+#define SPR_440_CCR1 (0x378)
+#define SPR_DCRIPR (0x37B)
+#define SPR_PPR (0x380)
+#define SPR_750_GQR0 (0x390)
+#define SPR_440_DNV0 (0x390)
+#define SPR_750_GQR1 (0x391)
+#define SPR_440_DNV1 (0x391)
+#define SPR_750_GQR2 (0x392)
+#define SPR_440_DNV2 (0x392)
+#define SPR_750_GQR3 (0x393)
+#define SPR_440_DNV3 (0x393)
+#define SPR_750_GQR4 (0x394)
+#define SPR_440_DTV0 (0x394)
+#define SPR_750_GQR5 (0x395)
+#define SPR_440_DTV1 (0x395)
+#define SPR_750_GQR6 (0x396)
+#define SPR_440_DTV2 (0x396)
+#define SPR_750_GQR7 (0x397)
+#define SPR_440_DTV3 (0x397)
+#define SPR_750_THRM4 (0x398)
+#define SPR_750CL_HID2 (0x398)
+#define SPR_440_DVLIM (0x398)
+#define SPR_750_WPAR (0x399)
+#define SPR_440_IVLIM (0x399)
+#define SPR_750_DMAU (0x39A)
+#define SPR_750_DMAL (0x39B)
+#define SPR_440_RSTCFG (0x39B)
+#define SPR_BOOKE_DCDBTRL (0x39C)
+#define SPR_BOOKE_DCDBTRH (0x39D)
+#define SPR_BOOKE_ICDBTRL (0x39E)
+#define SPR_BOOKE_ICDBTRH (0x39F)
+#define SPR_UMMCR2 (0x3A0)
+#define SPR_UPMC5 (0x3A1)
+#define SPR_UPMC6 (0x3A2)
+#define SPR_UBAMR (0x3A7)
+#define SPR_UMMCR0 (0x3A8)
+#define SPR_UPMC1 (0x3A9)
+#define SPR_UPMC2 (0x3AA)
+#define SPR_USIAR (0x3AB)
+#define SPR_UMMCR1 (0x3AC)
+#define SPR_UPMC3 (0x3AD)
+#define SPR_UPMC4 (0x3AE)
+#define SPR_USDA (0x3AF)
+#define SPR_40x_ZPR (0x3B0)
+#define SPR_BOOKE_MAS7 (0x3B0)
+#define SPR_620_PMR0 (0x3B0)
+#define SPR_MMCR2 (0x3B0)
+#define SPR_PMC5 (0x3B1)
+#define SPR_40x_PID (0x3B1)
+#define SPR_620_PMR1 (0x3B1)
+#define SPR_PMC6 (0x3B2)
+#define SPR_440_MMUCR (0x3B2)
+#define SPR_620_PMR2 (0x3B2)
+#define SPR_4xx_CCR0 (0x3B3)
+#define SPR_BOOKE_EPLC (0x3B3)
+#define SPR_620_PMR3 (0x3B3)
+#define SPR_405_IAC3 (0x3B4)
+#define SPR_BOOKE_EPSC (0x3B4)
+#define SPR_620_PMR4 (0x3B4)
+#define SPR_405_IAC4 (0x3B5)
+#define SPR_620_PMR5 (0x3B5)
+#define SPR_405_DVC1 (0x3B6)
+#define SPR_620_PMR6 (0x3B6)
+#define SPR_405_DVC2 (0x3B7)
+#define SPR_620_PMR7 (0x3B7)
+#define SPR_BAMR (0x3B7)
+#define SPR_MMCR0 (0x3B8)
+#define SPR_620_PMR8 (0x3B8)
+#define SPR_PMC1 (0x3B9)
+#define SPR_40x_SGR (0x3B9)
+#define SPR_620_PMR9 (0x3B9)
+#define SPR_PMC2 (0x3BA)
+#define SPR_40x_DCWR (0x3BA)
+#define SPR_620_PMRA (0x3BA)
+#define SPR_SIAR (0x3BB)
+#define SPR_405_SLER (0x3BB)
+#define SPR_620_PMRB (0x3BB)
+#define SPR_MMCR1 (0x3BC)
+#define SPR_405_SU0R (0x3BC)
+#define SPR_620_PMRC (0x3BC)
+#define SPR_401_SKR (0x3BC)
+#define SPR_PMC3 (0x3BD)
+#define SPR_405_DBCR1 (0x3BD)
+#define SPR_620_PMRD (0x3BD)
+#define SPR_PMC4 (0x3BE)
+#define SPR_620_PMRE (0x3BE)
+#define SPR_SDA (0x3BF)
+#define SPR_620_PMRF (0x3BF)
+#define SPR_403_VTBL (0x3CC)
+#define SPR_403_VTBU (0x3CD)
+#define SPR_DMISS (0x3D0)
+#define SPR_DCMP (0x3D1)
+#define SPR_HASH1 (0x3D2)
+#define SPR_HASH2 (0x3D3)
+#define SPR_BOOKE_ICDBDR (0x3D3)
+#define SPR_TLBMISS (0x3D4)
+#define SPR_IMISS (0x3D4)
+#define SPR_40x_ESR (0x3D4)
+#define SPR_PTEHI (0x3D5)
+#define SPR_ICMP (0x3D5)
+#define SPR_40x_DEAR (0x3D5)
+#define SPR_PTELO (0x3D6)
+#define SPR_RPA (0x3D6)
+#define SPR_40x_EVPR (0x3D6)
+#define SPR_L3PM (0x3D7)
+#define SPR_403_CDBCR (0x3D7)
+#define SPR_L3ITCR0 (0x3D8)
+#define SPR_TCR (0x3D8)
+#define SPR_40x_TSR (0x3D8)
+#define SPR_IBR (0x3DA)
+#define SPR_40x_TCR (0x3DA)
+#define SPR_ESASRR (0x3DB)
+#define SPR_40x_PIT (0x3DB)
+#define SPR_403_TBL (0x3DC)
+#define SPR_403_TBU (0x3DD)
+#define SPR_SEBR (0x3DE)
+#define SPR_40x_SRR2 (0x3DE)
+#define SPR_SER (0x3DF)
+#define SPR_40x_SRR3 (0x3DF)
+#define SPR_L3OHCR (0x3E8)
+#define SPR_L3ITCR1 (0x3E9)
+#define SPR_L3ITCR2 (0x3EA)
+#define SPR_L3ITCR3 (0x3EB)
+#define SPR_HID0 (0x3F0)
+#define SPR_40x_DBSR (0x3F0)
+#define SPR_HID1 (0x3F1)
+#define SPR_IABR (0x3F2)
+#define SPR_40x_DBCR0 (0x3F2)
+#define SPR_601_HID2 (0x3F2)
+#define SPR_Exxx_L1CSR0 (0x3F2)
+#define SPR_ICTRL (0x3F3)
+#define SPR_HID2 (0x3F3)
+#define SPR_750CL_HID4 (0x3F3)
+#define SPR_Exxx_L1CSR1 (0x3F3)
+#define SPR_440_DBDR (0x3F3)
+#define SPR_LDSTDB (0x3F4)
+#define SPR_750_TDCL (0x3F4)
+#define SPR_40x_IAC1 (0x3F4)
+#define SPR_MMUCSR0 (0x3F4)
+#define SPR_DABR (0x3F5)
+#define DABR_MASK (~(target_ulong)0x7)
+#define SPR_Exxx_BUCSR (0x3F5)
+#define SPR_40x_IAC2 (0x3F5)
+#define SPR_601_HID5 (0x3F5)
+#define SPR_40x_DAC1 (0x3F6)
+#define SPR_MSSCR0 (0x3F6)
+#define SPR_970_HID5 (0x3F6)
+#define SPR_MSSSR0 (0x3F7)
+#define SPR_MSSCR1 (0x3F7)
+#define SPR_DABRX (0x3F7)
+#define SPR_40x_DAC2 (0x3F7)
+#define SPR_MMUCFG (0x3F7)
+#define SPR_LDSTCR (0x3F8)
+#define SPR_L2PMCR (0x3F8)
+#define SPR_750FX_HID2 (0x3F8)
+#define SPR_620_BUSCSR (0x3F8)
+#define SPR_Exxx_L1FINV0 (0x3F8)
+#define SPR_L2CR (0x3F9)
+#define SPR_620_L2CR (0x3F9)
+#define SPR_L3CR (0x3FA)
+#define SPR_750_TDCH (0x3FA)
+#define SPR_IABR2 (0x3FA)
+#define SPR_40x_DCCR (0x3FA)
+#define SPR_620_L2SR (0x3FA)
+#define SPR_ICTC (0x3FB)
+#define SPR_40x_ICCR (0x3FB)
+#define SPR_THRM1 (0x3FC)
+#define SPR_403_PBL1 (0x3FC)
+#define SPR_SP (0x3FD)
+#define SPR_THRM2 (0x3FD)
+#define SPR_403_PBU1 (0x3FD)
+#define SPR_604_HID13 (0x3FD)
+#define SPR_LT (0x3FE)
+#define SPR_THRM3 (0x3FE)
+#define SPR_RCPU_FPECR (0x3FE)
+#define SPR_403_PBL2 (0x3FE)
+#define SPR_PIR (0x3FF)
+#define SPR_403_PBU2 (0x3FF)
+#define SPR_601_HID15 (0x3FF)
+#define SPR_604_HID15 (0x3FF)
+#define SPR_E500_SVR (0x3FF)
+
+/*****************************************************************************/
+/* PowerPC Instructions types definitions */
+enum {
+ PPC_NONE = 0x0000000000000000ULL,
+ /* PowerPC base instructions set */
+ PPC_INSNS_BASE = 0x0000000000000001ULL,
+ /* integer operations instructions */
+#define PPC_INTEGER PPC_INSNS_BASE
+ /* flow control instructions */
+#define PPC_FLOW PPC_INSNS_BASE
+ /* virtual memory instructions */
+#define PPC_MEM PPC_INSNS_BASE
+ /* ld/st with reservation instructions */
+#define PPC_RES PPC_INSNS_BASE
+ /* spr/msr access instructions */
+#define PPC_MISC PPC_INSNS_BASE
+ /* Deprecated instruction sets */
+ /* Original POWER instruction set */
+ PPC_POWER = 0x0000000000000002ULL,
+ /* POWER2 instruction set extension */
+ PPC_POWER2 = 0x0000000000000004ULL,
+ /* Power RTC support */
+ PPC_POWER_RTC = 0x0000000000000008ULL,
+ /* Power-to-PowerPC bridge (601) */
+ PPC_POWER_BR = 0x0000000000000010ULL,
+ /* 64 bits PowerPC instruction set */
+ PPC_64B = 0x0000000000000020ULL,
+ /* New 64 bits extensions (PowerPC 2.0x) */
+ PPC_64BX = 0x0000000000000040ULL,
+ /* 64 bits hypervisor extensions */
+ PPC_64H = 0x0000000000000080ULL,
+ /* New wait instruction (PowerPC 2.0x) */
+ PPC_WAIT = 0x0000000000000100ULL,
+ /* Time base mftb instruction */
+ PPC_MFTB = 0x0000000000000200ULL,
+
+ /* Fixed-point unit extensions */
+ /* PowerPC 602 specific */
+ PPC_602_SPEC = 0x0000000000000400ULL,
+ /* isel instruction */
+ PPC_ISEL = 0x0000000000000800ULL,
+ /* popcntb instruction */
+ PPC_POPCNTB = 0x0000000000001000ULL,
+ /* string load / store */
+ PPC_STRING = 0x0000000000002000ULL,
+
+ /* Floating-point unit extensions */
+ /* Optional floating point instructions */
+ PPC_FLOAT = 0x0000000000010000ULL,
+ /* New floating-point extensions (PowerPC 2.0x) */
+ PPC_FLOAT_EXT = 0x0000000000020000ULL,
+ PPC_FLOAT_FSQRT = 0x0000000000040000ULL,
+ PPC_FLOAT_FRES = 0x0000000000080000ULL,
+ PPC_FLOAT_FRSQRTE = 0x0000000000100000ULL,
+ PPC_FLOAT_FRSQRTES = 0x0000000000200000ULL,
+ PPC_FLOAT_FSEL = 0x0000000000400000ULL,
+ PPC_FLOAT_STFIWX = 0x0000000000800000ULL,
+
+ /* Vector/SIMD extensions */
+ /* Altivec support */
+ PPC_ALTIVEC = 0x0000000001000000ULL,
+ /* PowerPC 2.03 SPE extension */
+ PPC_SPE = 0x0000000002000000ULL,
+ /* PowerPC 2.03 SPE single-precision floating-point extension */
+ PPC_SPE_SINGLE = 0x0000000004000000ULL,
+ /* PowerPC 2.03 SPE double-precision floating-point extension */
+ PPC_SPE_DOUBLE = 0x0000000008000000ULL,
+
+ /* Optional memory control instructions */
+ PPC_MEM_TLBIA = 0x0000000010000000ULL,
+ PPC_MEM_TLBIE = 0x0000000020000000ULL,
+ PPC_MEM_TLBSYNC = 0x0000000040000000ULL,
+ /* sync instruction */
+ PPC_MEM_SYNC = 0x0000000080000000ULL,
+ /* eieio instruction */
+ PPC_MEM_EIEIO = 0x0000000100000000ULL,
+
+ /* Cache control instructions */
+ PPC_CACHE = 0x0000000200000000ULL,
+ /* icbi instruction */
+ PPC_CACHE_ICBI = 0x0000000400000000ULL,
+ /* dcbz instruction with fixed cache line size */
+ PPC_CACHE_DCBZ = 0x0000000800000000ULL,
+ /* dcbz instruction with tunable cache line size */
+ PPC_CACHE_DCBZT = 0x0000001000000000ULL,
+ /* dcba instruction */
+ PPC_CACHE_DCBA = 0x0000002000000000ULL,
+ /* Freescale cache locking instructions */
+ PPC_CACHE_LOCK = 0x0000004000000000ULL,
+
+ /* MMU related extensions */
+ /* external control instructions */
+ PPC_EXTERN = 0x0000010000000000ULL,
+ /* segment register access instructions */
+ PPC_SEGMENT = 0x0000020000000000ULL,
+ /* PowerPC 6xx TLB management instructions */
+ PPC_6xx_TLB = 0x0000040000000000ULL,
+ /* PowerPC 74xx TLB management instructions */
+ PPC_74xx_TLB = 0x0000080000000000ULL,
+ /* PowerPC 40x TLB management instructions */
+ PPC_40x_TLB = 0x0000100000000000ULL,
+ /* segment register access instructions for PowerPC 64 "bridge" */
+ PPC_SEGMENT_64B = 0x0000200000000000ULL,
+ /* SLB management */
+ PPC_SLBI = 0x0000400000000000ULL,
+
+ /* Embedded PowerPC dedicated instructions */
+ PPC_WRTEE = 0x0001000000000000ULL,
+ /* PowerPC 40x exception model */
+ PPC_40x_EXCP = 0x0002000000000000ULL,
+ /* PowerPC 405 Mac instructions */
+ PPC_405_MAC = 0x0004000000000000ULL,
+ /* PowerPC 440 specific instructions */
+ PPC_440_SPEC = 0x0008000000000000ULL,
+ /* BookE (embedded) PowerPC specification */
+ PPC_BOOKE = 0x0010000000000000ULL,
+ /* mfapidi instruction */
+ PPC_MFAPIDI = 0x0020000000000000ULL,
+ /* tlbiva instruction */
+ PPC_TLBIVA = 0x0040000000000000ULL,
+ /* tlbivax instruction */
+ PPC_TLBIVAX = 0x0080000000000000ULL,
+ /* PowerPC 4xx dedicated instructions */
+ PPC_4xx_COMMON = 0x0100000000000000ULL,
+ /* PowerPC 40x ibct instructions */
+ PPC_40x_ICBT = 0x0200000000000000ULL,
+ /* rfmci is not implemented in all BookE PowerPC */
+ PPC_RFMCI = 0x0400000000000000ULL,
+ /* rfdi instruction */
+ PPC_RFDI = 0x0800000000000000ULL,
+ /* DCR accesses */
+ PPC_DCR = 0x1000000000000000ULL,
+ /* DCR extended accesse */
+ PPC_DCRX = 0x2000000000000000ULL,
+ /* user-mode DCR access, implemented in PowerPC 460 */
+ PPC_DCRUX = 0x4000000000000000ULL,
+};
+
+/*****************************************************************************/
+/* Memory access type :
+ * may be needed for precise access rights control and precise exceptions.
+ */
+enum {
+ /* 1 bit to define user level / supervisor access */
+ ACCESS_USER = 0x00,
+ ACCESS_SUPER = 0x01,
+ /* Type of instruction that generated the access */
+ ACCESS_CODE = 0x10, /* Code fetch access */
+ ACCESS_INT = 0x20, /* Integer load/store access */
+ ACCESS_FLOAT = 0x30, /* floating point load/store access */
+ ACCESS_RES = 0x40, /* load/store with reservation */
+ ACCESS_EXT = 0x50, /* external access */
+ ACCESS_CACHE = 0x60, /* Cache manipulation */
+};
+
+/* Hardware interruption sources:
+ * all those exception can be raised simulteaneously
+ */
+/* Input pins definitions */
+enum {
+ /* 6xx bus input pins */
+ PPC6xx_INPUT_HRESET = 0,
+ PPC6xx_INPUT_SRESET = 1,
+ PPC6xx_INPUT_CKSTP_IN = 2,
+ PPC6xx_INPUT_MCP = 3,
+ PPC6xx_INPUT_SMI = 4,
+ PPC6xx_INPUT_INT = 5,
+ PPC6xx_INPUT_TBEN = 6,
+ PPC6xx_INPUT_WAKEUP = 7,
+ PPC6xx_INPUT_NB,
+};
+
+enum {
+ /* Embedded PowerPC input pins */
+ PPCBookE_INPUT_HRESET = 0,
+ PPCBookE_INPUT_SRESET = 1,
+ PPCBookE_INPUT_CKSTP_IN = 2,
+ PPCBookE_INPUT_MCP = 3,
+ PPCBookE_INPUT_SMI = 4,
+ PPCBookE_INPUT_INT = 5,
+ PPCBookE_INPUT_CINT = 6,
+ PPCBookE_INPUT_NB,
+};
+
+enum {
+ /* PowerPC E500 input pins */
+ PPCE500_INPUT_RESET_CORE = 0,
+ PPCE500_INPUT_MCK = 1,
+ PPCE500_INPUT_CINT = 3,
+ PPCE500_INPUT_INT = 4,
+ PPCE500_INPUT_DEBUG = 6,
+ PPCE500_INPUT_NB,
+};
+
+enum {
+ /* PowerPC 40x input pins */
+ PPC40x_INPUT_RESET_CORE = 0,
+ PPC40x_INPUT_RESET_CHIP = 1,
+ PPC40x_INPUT_RESET_SYS = 2,
+ PPC40x_INPUT_CINT = 3,
+ PPC40x_INPUT_INT = 4,
+ PPC40x_INPUT_HALT = 5,
+ PPC40x_INPUT_DEBUG = 6,
+ PPC40x_INPUT_NB,
+};
+
+enum {
+ /* RCPU input pins */
+ PPCRCPU_INPUT_PORESET = 0,
+ PPCRCPU_INPUT_HRESET = 1,
+ PPCRCPU_INPUT_SRESET = 2,
+ PPCRCPU_INPUT_IRQ0 = 3,
+ PPCRCPU_INPUT_IRQ1 = 4,
+ PPCRCPU_INPUT_IRQ2 = 5,
+ PPCRCPU_INPUT_IRQ3 = 6,
+ PPCRCPU_INPUT_IRQ4 = 7,
+ PPCRCPU_INPUT_IRQ5 = 8,
+ PPCRCPU_INPUT_IRQ6 = 9,
+ PPCRCPU_INPUT_IRQ7 = 10,
+ PPCRCPU_INPUT_NB,
+};
+
+#if defined(TARGET_PPC64)
+enum {
+ /* PowerPC 970 input pins */
+ PPC970_INPUT_HRESET = 0,
+ PPC970_INPUT_SRESET = 1,
+ PPC970_INPUT_CKSTP = 2,
+ PPC970_INPUT_TBEN = 3,
+ PPC970_INPUT_MCP = 4,
+ PPC970_INPUT_INT = 5,
+ PPC970_INPUT_THINT = 6,
+ PPC970_INPUT_NB,
+};
+#endif
+
+/* Hardware exceptions definitions */
+enum {
+ /* External hardware exception sources */
+ PPC_INTERRUPT_RESET = 0, /* Reset exception */
+ PPC_INTERRUPT_WAKEUP, /* Wakeup exception */
+ PPC_INTERRUPT_MCK, /* Machine check exception */
+ PPC_INTERRUPT_EXT, /* External interrupt */
+ PPC_INTERRUPT_SMI, /* System management interrupt */
+ PPC_INTERRUPT_CEXT, /* Critical external interrupt */
+ PPC_INTERRUPT_DEBUG, /* External debug exception */
+ PPC_INTERRUPT_THERM, /* Thermal exception */
+ /* Internal hardware exception sources */
+ PPC_INTERRUPT_DECR, /* Decrementer exception */
+ PPC_INTERRUPT_HDECR, /* Hypervisor decrementer exception */
+ PPC_INTERRUPT_PIT, /* Programmable inteval timer interrupt */
+ PPC_INTERRUPT_FIT, /* Fixed interval timer interrupt */
+ PPC_INTERRUPT_WDT, /* Watchdog timer interrupt */
+ PPC_INTERRUPT_CDOORBELL, /* Critical doorbell interrupt */
+ PPC_INTERRUPT_DOORBELL, /* Doorbell interrupt */
+ PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */
+};
+
+/*****************************************************************************/
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->nip;
+ *cs_base = 0;
+ *flags = env->hflags;
+}
+
+static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
+{
+#if defined(TARGET_PPC64)
+ /* The kernel checks TIF_32BIT here; we don't support loading 32-bit
+ binaries on PPC64 yet. */
+ env->gpr[13] = newtls;
+#else
+ env->gpr[2] = newtls;
+#endif
+}
+
+/* hidden flags (hflags) - used internally by qemu to represent additional
+ * cpu states.
+ */
+#define HF_HALTED_SHIFT 1
+
+#define HF_HALTED_MASK 1<<HF_HALTED_SHIFT
+
+#endif /* !defined (__CPU_PPC_H__) */
diff --git a/target-ppc/exec.h b/target-ppc/exec.h
new file mode 100644
index 0000000..4688ef5
--- /dev/null
+++ b/target-ppc/exec.h
@@ -0,0 +1,57 @@
+/*
+ * PowerPC emulation definitions for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+#if !defined (__PPC_H__)
+#define __PPC_H__
+
+#include "config.h"
+
+#include "dyngen-exec.h"
+
+#include "cpu.h"
+#include "exec-all.h"
+
+register struct CPUPPCState *env asm(AREG0);
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (msr_ee && (env->interrupt_request & CPU_INTERRUPT_HARD));
+}
+
+
+static inline int cpu_halted(CPUState *env)
+{
+ if (!env->halted)
+ return 0;
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->nip = tb->pc;
+}
+
+#endif /* !defined (__PPC_H__) */
diff --git a/target-ppc/fake-exec.c b/target-ppc/fake-exec.c
new file mode 100644
index 0000000..259e06d
--- /dev/null
+++ b/target-ppc/fake-exec.c
@@ -0,0 +1,104 @@
+/*
+ * fake-exec.c
+ *
+ * This is a file for stub functions so that compilation is possible
+ * when TCG CPU emulation is disabled during compilation.
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by & Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * This work is licensed under the GNU GPL licence version 2 or later.
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+
+struct ppc_def_t {
+ const unsigned char *name;
+ uint32_t pvr;
+ uint32_t svr;
+ uint64_t insns_flags;
+ uint64_t msr_mask;
+ powerpc_mmu_t mmu_model;
+ powerpc_excp_t excp_model;
+ powerpc_input_t bus_model;
+ uint32_t flags;
+ int bfd_mach;
+ void (*init_proc)(CPUPPCState *env);
+ int (*check_pow)(CPUPPCState *env);
+};
+
+int code_copy_enabled = 0;
+
+void cpu_dump_state (CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+}
+
+void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+}
+
+void cpu_dump_statistics (CPUState *env, FILE*f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+}
+
+unsigned long code_gen_max_block_size(void)
+{
+ return 32;
+}
+
+void cpu_gen_init(void)
+{
+}
+
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+
+{
+ return 0;
+}
+
+int cpu_ppc_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ return 0;
+}
+
+void init_proc_ppc440ep_kvm(CPUPPCState *env)
+{
+ ppc40x_irq_init(env);
+}
+
+static ppc_def_t ppc440ep_kvm = {
+ .name = "440EP KVM",
+ .mmu_model = POWERPC_MMU_SOFT_4xx, /*XXX needed for GDB stub */
+ .init_proc = init_proc_ppc440ep_kvm,
+};
+
+const ppc_def_t *cpu_ppc_find_by_name (const unsigned char *name)
+{
+ return &ppc440ep_kvm;
+}
+
+int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
+{
+ env->mmu_model = def->mmu_model;
+ (*def->init_proc)(env);
+ return 0;
+}
+
+void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
new file mode 100644
index 0000000..23e3189
--- /dev/null
+++ b/target-ppc/helper.c
@@ -0,0 +1,2822 @@
+/*
+ * PowerPC emulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "helper_regs.h"
+#include "qemu-common.h"
+#include "kvm.h"
+#include "qemu-kvm.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_BATS
+//#define DEBUG_SLB
+//#define DEBUG_SOFTWARE_TLB
+//#define DUMP_PAGE_TABLES
+//#define DEBUG_EXCEPTIONS
+//#define FLUSH_ALL_TLBS
+
+#ifdef DEBUG_MMU
+# define LOG_MMU(...) qemu_log(__VA_ARGS__)
+# define LOG_MMU_STATE(env) log_cpu_state((env), 0)
+#else
+# define LOG_MMU(...) do { } while (0)
+# define LOG_MMU_STATE(...) do { } while (0)
+#endif
+
+
+#ifdef DEBUG_SOFTWARE_TLB
+# define LOG_SWTLB(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_SWTLB(...) do { } while (0)
+#endif
+
+#ifdef DEBUG_BATS
+# define LOG_BATS(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_BATS(...) do { } while (0)
+#endif
+
+#ifdef DEBUG_SLB
+# define LOG_SLB(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_SLB(...) do { } while (0)
+#endif
+
+#ifdef DEBUG_EXCEPTIONS
+# define LOG_EXCP(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_EXCP(...) do { } while (0)
+#endif
+
+
+/*****************************************************************************/
+/* PowerPC MMU emulation */
+
+#if defined(CONFIG_USER_ONLY)
+int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ int exception, error_code;
+
+ if (rw == 2) {
+ exception = POWERPC_EXCP_ISI;
+ error_code = 0x40000000;
+ } else {
+ exception = POWERPC_EXCP_DSI;
+ error_code = 0x40000000;
+ if (rw)
+ error_code |= 0x02000000;
+ env->spr[SPR_DAR] = address;
+ env->spr[SPR_DSISR] = error_code;
+ }
+ env->exception_index = exception;
+ env->error_code = error_code;
+
+ return 1;
+}
+
+#else
+/* Common routines used by software and hardware TLBs emulation */
+static inline int pte_is_valid(target_ulong pte0)
+{
+ return pte0 & 0x80000000 ? 1 : 0;
+}
+
+static inline void pte_invalidate(target_ulong *pte0)
+{
+ *pte0 &= ~0x80000000;
+}
+
+#if defined(TARGET_PPC64)
+static inline int pte64_is_valid(target_ulong pte0)
+{
+ return pte0 & 0x0000000000000001ULL ? 1 : 0;
+}
+
+static inline void pte64_invalidate(target_ulong *pte0)
+{
+ *pte0 &= ~0x0000000000000001ULL;
+}
+#endif
+
+#define PTE_PTEM_MASK 0x7FFFFFBF
+#define PTE_CHECK_MASK (TARGET_PAGE_MASK | 0x7B)
+#if defined(TARGET_PPC64)
+#define PTE64_PTEM_MASK 0xFFFFFFFFFFFFFF80ULL
+#define PTE64_CHECK_MASK (TARGET_PAGE_MASK | 0x7F)
+#endif
+
+static inline int pp_check(int key, int pp, int nx)
+{
+ int access;
+
+ /* Compute access rights */
+ /* When pp is 3/7, the result is undefined. Set it to noaccess */
+ access = 0;
+ if (key == 0) {
+ switch (pp) {
+ case 0x0:
+ case 0x1:
+ case 0x2:
+ access |= PAGE_WRITE;
+ /* No break here */
+ case 0x3:
+ case 0x6:
+ access |= PAGE_READ;
+ break;
+ }
+ } else {
+ switch (pp) {
+ case 0x0:
+ case 0x6:
+ access = 0;
+ break;
+ case 0x1:
+ case 0x3:
+ access = PAGE_READ;
+ break;
+ case 0x2:
+ access = PAGE_READ | PAGE_WRITE;
+ break;
+ }
+ }
+ if (nx == 0)
+ access |= PAGE_EXEC;
+
+ return access;
+}
+
+static inline int check_prot(int prot, int rw, int access_type)
+{
+ int ret;
+
+ if (access_type == ACCESS_CODE) {
+ if (prot & PAGE_EXEC)
+ ret = 0;
+ else
+ ret = -2;
+ } else if (rw) {
+ if (prot & PAGE_WRITE)
+ ret = 0;
+ else
+ ret = -2;
+ } else {
+ if (prot & PAGE_READ)
+ ret = 0;
+ else
+ ret = -2;
+ }
+
+ return ret;
+}
+
+static inline int _pte_check(mmu_ctx_t *ctx, int is_64b, target_ulong pte0,
+ target_ulong pte1, int h, int rw, int type)
+{
+ target_ulong ptem, mmask;
+ int access, ret, pteh, ptev, pp;
+
+ ret = -1;
+ /* Check validity and table match */
+#if defined(TARGET_PPC64)
+ if (is_64b) {
+ ptev = pte64_is_valid(pte0);
+ pteh = (pte0 >> 1) & 1;
+ } else
+#endif
+ {
+ ptev = pte_is_valid(pte0);
+ pteh = (pte0 >> 6) & 1;
+ }
+ if (ptev && h == pteh) {
+ /* Check vsid & api */
+#if defined(TARGET_PPC64)
+ if (is_64b) {
+ ptem = pte0 & PTE64_PTEM_MASK;
+ mmask = PTE64_CHECK_MASK;
+ pp = (pte1 & 0x00000003) | ((pte1 >> 61) & 0x00000004);
+ ctx->nx = (pte1 >> 2) & 1; /* No execute bit */
+ ctx->nx |= (pte1 >> 3) & 1; /* Guarded bit */
+ } else
+#endif
+ {
+ ptem = pte0 & PTE_PTEM_MASK;
+ mmask = PTE_CHECK_MASK;
+ pp = pte1 & 0x00000003;
+ }
+ if (ptem == ctx->ptem) {
+ if (ctx->raddr != (target_phys_addr_t)-1ULL) {
+ /* all matches should have equal RPN, WIMG & PP */
+ if ((ctx->raddr & mmask) != (pte1 & mmask)) {
+ qemu_log("Bad RPN/WIMG/PP\n");
+ return -3;
+ }
+ }
+ /* Compute access rights */
+ access = pp_check(ctx->key, pp, ctx->nx);
+ /* Keep the matching PTE informations */
+ ctx->raddr = pte1;
+ ctx->prot = access;
+ ret = check_prot(ctx->prot, rw, type);
+ if (ret == 0) {
+ /* Access granted */
+ LOG_MMU("PTE access granted !\n");
+ } else {
+ /* Access right violation */
+ LOG_MMU("PTE access rejected\n");
+ }
+ }
+ }
+
+ return ret;
+}
+
+static inline int pte32_check(mmu_ctx_t *ctx, target_ulong pte0,
+ target_ulong pte1, int h, int rw, int type)
+{
+ return _pte_check(ctx, 0, pte0, pte1, h, rw, type);
+}
+
+#if defined(TARGET_PPC64)
+static inline int pte64_check(mmu_ctx_t *ctx, target_ulong pte0,
+ target_ulong pte1, int h, int rw, int type)
+{
+ return _pte_check(ctx, 1, pte0, pte1, h, rw, type);
+}
+#endif
+
+static inline int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p,
+ int ret, int rw)
+{
+ int store = 0;
+
+ /* Update page flags */
+ if (!(*pte1p & 0x00000100)) {
+ /* Update accessed flag */
+ *pte1p |= 0x00000100;
+ store = 1;
+ }
+ if (!(*pte1p & 0x00000080)) {
+ if (rw == 1 && ret == 0) {
+ /* Update changed flag */
+ *pte1p |= 0x00000080;
+ store = 1;
+ } else {
+ /* Force page fault for first write access */
+ ctx->prot &= ~PAGE_WRITE;
+ }
+ }
+
+ return store;
+}
+
+/* Software driven TLB helpers */
+static inline int ppc6xx_tlb_getnum(CPUState *env, target_ulong eaddr, int way,
+ int is_code)
+{
+ int nr;
+
+ /* Select TLB num in a way from address */
+ nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1);
+ /* Select TLB way */
+ nr += env->tlb_per_way * way;
+ /* 6xx have separate TLBs for instructions and data */
+ if (is_code && env->id_tlbs == 1)
+ nr += env->nb_tlb;
+
+ return nr;
+}
+
+static inline void ppc6xx_tlb_invalidate_all(CPUState *env)
+{
+ ppc6xx_tlb_t *tlb;
+ int nr, max;
+
+ //LOG_SWTLB("Invalidate all TLBs\n");
+ /* Invalidate all defined software TLB */
+ max = env->nb_tlb;
+ if (env->id_tlbs == 1)
+ max *= 2;
+ for (nr = 0; nr < max; nr++) {
+ tlb = &env->tlb[nr].tlb6;
+ pte_invalidate(&tlb->pte0);
+ }
+ tlb_flush(env, 1);
+}
+
+static inline void __ppc6xx_tlb_invalidate_virt(CPUState *env,
+ target_ulong eaddr,
+ int is_code, int match_epn)
+{
+#if !defined(FLUSH_ALL_TLBS)
+ ppc6xx_tlb_t *tlb;
+ int way, nr;
+
+ /* Invalidate ITLB + DTLB, all ways */
+ for (way = 0; way < env->nb_ways; way++) {
+ nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code);
+ tlb = &env->tlb[nr].tlb6;
+ if (pte_is_valid(tlb->pte0) && (match_epn == 0 || eaddr == tlb->EPN)) {
+ LOG_SWTLB("TLB invalidate %d/%d " TARGET_FMT_lx "\n", nr,
+ env->nb_tlb, eaddr);
+ pte_invalidate(&tlb->pte0);
+ tlb_flush_page(env, tlb->EPN);
+ }
+ }
+#else
+ /* XXX: PowerPC specification say this is valid as well */
+ ppc6xx_tlb_invalidate_all(env);
+#endif
+}
+
+static inline void ppc6xx_tlb_invalidate_virt(CPUState *env,
+ target_ulong eaddr, int is_code)
+{
+ __ppc6xx_tlb_invalidate_virt(env, eaddr, is_code, 0);
+}
+
+void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code,
+ target_ulong pte0, target_ulong pte1)
+{
+ ppc6xx_tlb_t *tlb;
+ int nr;
+
+ nr = ppc6xx_tlb_getnum(env, EPN, way, is_code);
+ tlb = &env->tlb[nr].tlb6;
+ LOG_SWTLB("Set TLB %d/%d EPN " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
+ " PTE1 " TARGET_FMT_lx "\n", nr, env->nb_tlb, EPN, pte0, pte1);
+ /* Invalidate any pending reference in Qemu for this virtual address */
+ __ppc6xx_tlb_invalidate_virt(env, EPN, is_code, 1);
+ tlb->pte0 = pte0;
+ tlb->pte1 = pte1;
+ tlb->EPN = EPN;
+ /* Store last way for LRU mechanism */
+ env->last_way = way;
+}
+
+static inline int ppc6xx_tlb_check(CPUState *env, mmu_ctx_t *ctx,
+ target_ulong eaddr, int rw, int access_type)
+{
+ ppc6xx_tlb_t *tlb;
+ int nr, best, way;
+ int ret;
+
+ best = -1;
+ ret = -1; /* No TLB found */
+ for (way = 0; way < env->nb_ways; way++) {
+ nr = ppc6xx_tlb_getnum(env, eaddr, way,
+ access_type == ACCESS_CODE ? 1 : 0);
+ tlb = &env->tlb[nr].tlb6;
+ /* This test "emulates" the PTE index match for hardware TLBs */
+ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) {
+ LOG_SWTLB("TLB %d/%d %s [" TARGET_FMT_lx " " TARGET_FMT_lx
+ "] <> " TARGET_FMT_lx "\n", nr, env->nb_tlb,
+ pte_is_valid(tlb->pte0) ? "valid" : "inval",
+ tlb->EPN, tlb->EPN + TARGET_PAGE_SIZE, eaddr);
+ continue;
+ }
+ LOG_SWTLB("TLB %d/%d %s " TARGET_FMT_lx " <> " TARGET_FMT_lx " "
+ TARGET_FMT_lx " %c %c\n", nr, env->nb_tlb,
+ pte_is_valid(tlb->pte0) ? "valid" : "inval",
+ tlb->EPN, eaddr, tlb->pte1,
+ rw ? 'S' : 'L', access_type == ACCESS_CODE ? 'I' : 'D');
+ switch (pte32_check(ctx, tlb->pte0, tlb->pte1, 0, rw, access_type)) {
+ case -3:
+ /* TLB inconsistency */
+ return -1;
+ case -2:
+ /* Access violation */
+ ret = -2;
+ best = nr;
+ break;
+ case -1:
+ default:
+ /* No match */
+ break;
+ case 0:
+ /* access granted */
+ /* XXX: we should go on looping to check all TLBs consistency
+ * but we can speed-up the whole thing as the
+ * result would be undefined if TLBs are not consistent.
+ */
+ ret = 0;
+ best = nr;
+ goto done;
+ }
+ }
+ if (best != -1) {
+ done:
+ LOG_SWTLB("found TLB at addr " TARGET_FMT_plx " prot=%01x ret=%d\n",
+ ctx->raddr & TARGET_PAGE_MASK, ctx->prot, ret);
+ /* Update page flags */
+ pte_update_flags(ctx, &env->tlb[best].tlb6.pte1, ret, rw);
+ }
+
+ return ret;
+}
+
+/* Perform BAT hit & translation */
+static inline void bat_size_prot(CPUState *env, target_ulong *blp, int *validp,
+ int *protp, target_ulong *BATu,
+ target_ulong *BATl)
+{
+ target_ulong bl;
+ int pp, valid, prot;
+
+ bl = (*BATu & 0x00001FFC) << 15;
+ valid = 0;
+ prot = 0;
+ if (((msr_pr == 0) && (*BATu & 0x00000002)) ||
+ ((msr_pr != 0) && (*BATu & 0x00000001))) {
+ valid = 1;
+ pp = *BATl & 0x00000003;
+ if (pp != 0) {
+ prot = PAGE_READ | PAGE_EXEC;
+ if (pp == 0x2)
+ prot |= PAGE_WRITE;
+ }
+ }
+ *blp = bl;
+ *validp = valid;
+ *protp = prot;
+}
+
+static inline void bat_601_size_prot(CPUState *env, target_ulong *blp,
+ int *validp, int *protp,
+ target_ulong *BATu, target_ulong *BATl)
+{
+ target_ulong bl;
+ int key, pp, valid, prot;
+
+ bl = (*BATl & 0x0000003F) << 17;
+ LOG_BATS("b %02x ==> bl " TARGET_FMT_lx " msk " TARGET_FMT_lx "\n",
+ (uint8_t)(*BATl & 0x0000003F), bl, ~bl);
+ prot = 0;
+ valid = (*BATl >> 6) & 1;
+ if (valid) {
+ pp = *BATu & 0x00000003;
+ if (msr_pr == 0)
+ key = (*BATu >> 3) & 1;
+ else
+ key = (*BATu >> 2) & 1;
+ prot = pp_check(key, pp, 0);
+ }
+ *blp = bl;
+ *validp = valid;
+ *protp = prot;
+}
+
+static inline int get_bat(CPUState *env, mmu_ctx_t *ctx, target_ulong virtual,
+ int rw, int type)
+{
+ target_ulong *BATlt, *BATut, *BATu, *BATl;
+ target_ulong BEPIl, BEPIu, bl;
+ int i, valid, prot;
+ int ret = -1;
+
+ LOG_BATS("%s: %cBAT v " TARGET_FMT_lx "\n", __func__,
+ type == ACCESS_CODE ? 'I' : 'D', virtual);
+ switch (type) {
+ case ACCESS_CODE:
+ BATlt = env->IBAT[1];
+ BATut = env->IBAT[0];
+ break;
+ default:
+ BATlt = env->DBAT[1];
+ BATut = env->DBAT[0];
+ break;
+ }
+ for (i = 0; i < env->nb_BATs; i++) {
+ BATu = &BATut[i];
+ BATl = &BATlt[i];
+ BEPIu = *BATu & 0xF0000000;
+ BEPIl = *BATu & 0x0FFE0000;
+ if (unlikely(env->mmu_model == POWERPC_MMU_601)) {
+ bat_601_size_prot(env, &bl, &valid, &prot, BATu, BATl);
+ } else {
+ bat_size_prot(env, &bl, &valid, &prot, BATu, BATl);
+ }
+ LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
+ " BATl " TARGET_FMT_lx "\n", __func__,
+ type == ACCESS_CODE ? 'I' : 'D', i, virtual, *BATu, *BATl);
+ if ((virtual & 0xF0000000) == BEPIu &&
+ ((virtual & 0x0FFE0000) & ~bl) == BEPIl) {
+ /* BAT matches */
+ if (valid != 0) {
+ /* Get physical address */
+ ctx->raddr = (*BATl & 0xF0000000) |
+ ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) |
+ (virtual & 0x0001F000);
+ /* Compute access rights */
+ ctx->prot = prot;
+ ret = check_prot(ctx->prot, rw, type);
+ if (ret == 0)
+ LOG_BATS("BAT %d match: r " TARGET_FMT_plx " prot=%c%c\n",
+ i, ctx->raddr, ctx->prot & PAGE_READ ? 'R' : '-',
+ ctx->prot & PAGE_WRITE ? 'W' : '-');
+ break;
+ }
+ }
+ }
+ if (ret < 0) {
+#if defined(DEBUG_BATS)
+ if (qemu_log_enabled()) {
+ LOG_BATS("no BAT match for " TARGET_FMT_lx ":\n", virtual);
+ for (i = 0; i < 4; i++) {
+ BATu = &BATut[i];
+ BATl = &BATlt[i];
+ BEPIu = *BATu & 0xF0000000;
+ BEPIl = *BATu & 0x0FFE0000;
+ bl = (*BATu & 0x00001FFC) << 15;
+ LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx
+ " BATl " TARGET_FMT_lx " \n\t" TARGET_FMT_lx " "
+ TARGET_FMT_lx " " TARGET_FMT_lx "\n",
+ __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
+ *BATu, *BATl, BEPIu, BEPIl, bl);
+ }
+ }
+#endif
+ }
+ /* No hit */
+ return ret;
+}
+
+/* PTE table lookup */
+static inline int _find_pte(mmu_ctx_t *ctx, int is_64b, int h, int rw,
+ int type, int target_page_bits)
+{
+ target_ulong base, pte0, pte1;
+ int i, good = -1;
+ int ret, r;
+
+ ret = -1; /* No entry found */
+ base = ctx->pg_addr[h];
+ for (i = 0; i < 8; i++) {
+#if defined(TARGET_PPC64)
+ if (is_64b) {
+ pte0 = ldq_phys(base + (i * 16));
+ pte1 = ldq_phys(base + (i * 16) + 8);
+
+ /* We have a TLB that saves 4K pages, so let's
+ * split a huge page to 4k chunks */
+ if (target_page_bits != TARGET_PAGE_BITS)
+ pte1 |= (ctx->eaddr & (( 1 << target_page_bits ) - 1))
+ & TARGET_PAGE_MASK;
+
+ 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",
+ base + (i * 16), pte0, pte1, (int)(pte0 & 1), h,
+ (int)((pte0 >> 1) & 1), ctx->ptem);
+ } else
+#endif
+ {
+ pte0 = ldl_phys(base + (i * 8));
+ pte1 = ldl_phys(base + (i * 8) + 4);
+ 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",
+ base + (i * 8), pte0, pte1, (int)(pte0 >> 31), h,
+ (int)((pte0 >> 6) & 1), ctx->ptem);
+ }
+ switch (r) {
+ case -3:
+ /* PTE inconsistency */
+ return -1;
+ case -2:
+ /* Access violation */
+ ret = -2;
+ good = i;
+ break;
+ case -1:
+ default:
+ /* No PTE match */
+ break;
+ case 0:
+ /* access granted */
+ /* XXX: we should go on looping to check all PTEs consistency
+ * but if we can speed-up the whole thing as the
+ * result would be undefined if PTEs are not consistent.
+ */
+ ret = 0;
+ good = i;
+ goto done;
+ }
+ }
+ if (good != -1) {
+ done:
+ LOG_MMU("found PTE at addr " TARGET_FMT_lx " prot=%01x ret=%d\n",
+ ctx->raddr, ctx->prot, ret);
+ /* Update page flags */
+ pte1 = ctx->raddr;
+ if (pte_update_flags(ctx, &pte1, ret, rw) == 1) {
+#if defined(TARGET_PPC64)
+ if (is_64b) {
+ stq_phys_notdirty(base + (good * 16) + 8, pte1);
+ } else
+#endif
+ {
+ stl_phys_notdirty(base + (good * 8) + 4, pte1);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static inline int find_pte32(mmu_ctx_t *ctx, int h, int rw, int type,
+ int target_page_bits)
+{
+ return _find_pte(ctx, 0, h, rw, type, target_page_bits);
+}
+
+#if defined(TARGET_PPC64)
+static inline int find_pte64(mmu_ctx_t *ctx, int h, int rw, int type,
+ int target_page_bits)
+{
+ return _find_pte(ctx, 1, h, rw, type, target_page_bits);
+}
+#endif
+
+static inline int find_pte(CPUState *env, mmu_ctx_t *ctx, int h, int rw,
+ int type, int target_page_bits)
+{
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64)
+ return find_pte64(ctx, h, rw, type, target_page_bits);
+#endif
+
+ return find_pte32(ctx, h, rw, type, target_page_bits);
+}
+
+#if defined(TARGET_PPC64)
+static ppc_slb_t *slb_get_entry(CPUPPCState *env, int nr)
+{
+ ppc_slb_t *retval = &env->slb[nr];
+
+#if 0 // XXX implement bridge mode?
+ if (env->spr[SPR_ASR] & 1) {
+ target_phys_addr_t sr_base;
+
+ sr_base = env->spr[SPR_ASR] & 0xfffffffffffff000;
+ sr_base += (12 * nr);
+
+ retval->tmp64 = ldq_phys(sr_base);
+ retval->tmp = ldl_phys(sr_base + 8);
+ }
+#endif
+
+ return retval;
+}
+
+static void slb_set_entry(CPUPPCState *env, int nr, ppc_slb_t *slb)
+{
+ ppc_slb_t *entry = &env->slb[nr];
+
+ if (slb == entry)
+ return;
+
+ entry->tmp64 = slb->tmp64;
+ entry->tmp = slb->tmp;
+}
+
+static inline int slb_is_valid(ppc_slb_t *slb)
+{
+ return (int)(slb->tmp64 & 0x0000000008000000ULL);
+}
+
+static inline void slb_invalidate(ppc_slb_t *slb)
+{
+ slb->tmp64 &= ~0x0000000008000000ULL;
+}
+
+static inline int slb_lookup(CPUPPCState *env, target_ulong eaddr,
+ target_ulong *vsid, target_ulong *page_mask,
+ int *attr, int *target_page_bits)
+{
+ target_ulong mask;
+ int n, ret;
+
+ ret = -5;
+ LOG_SLB("%s: eaddr " TARGET_FMT_lx "\n", __func__, eaddr);
+ mask = 0x0000000000000000ULL; /* Avoid gcc warning */
+ for (n = 0; n < env->slb_nr; n++) {
+ ppc_slb_t *slb = slb_get_entry(env, n);
+
+ LOG_SLB("%s: seg %d %016" PRIx64 " %08"
+ PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp);
+ if (slb_is_valid(slb)) {
+ /* SLB entry is valid */
+ mask = 0xFFFFFFFFF0000000ULL;
+ if (slb->tmp & 0x8) {
+ /* 16 MB PTEs */
+ if (target_page_bits)
+ *target_page_bits = 24;
+ } else {
+ /* 4 KB PTEs */
+ if (target_page_bits)
+ *target_page_bits = TARGET_PAGE_BITS;
+ }
+ if ((eaddr & mask) == (slb->tmp64 & mask)) {
+ /* SLB match */
+ *vsid = ((slb->tmp64 << 24) | (slb->tmp >> 8)) & 0x0003FFFFFFFFFFFFULL;
+ *page_mask = ~mask;
+ *attr = slb->tmp & 0xFF;
+ ret = n;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void ppc_slb_invalidate_all (CPUPPCState *env)
+{
+ int n, do_invalidate;
+
+ do_invalidate = 0;
+ /* XXX: Warning: slbia never invalidates the first segment */
+ for (n = 1; n < env->slb_nr; n++) {
+ ppc_slb_t *slb = slb_get_entry(env, n);
+
+ if (slb_is_valid(slb)) {
+ slb_invalidate(slb);
+ slb_set_entry(env, n, slb);
+ /* XXX: given the fact that segment size is 256 MB or 1TB,
+ * and we still don't have a tlb_flush_mask(env, n, mask)
+ * in Qemu, we just invalidate all TLBs
+ */
+ do_invalidate = 1;
+ }
+ }
+ if (do_invalidate)
+ tlb_flush(env, 1);
+}
+
+void ppc_slb_invalidate_one (CPUPPCState *env, uint64_t T0)
+{
+ target_ulong vsid, page_mask;
+ int attr;
+ int n;
+
+ n = slb_lookup(env, T0, &vsid, &page_mask, &attr, NULL);
+ if (n >= 0) {
+ ppc_slb_t *slb = slb_get_entry(env, n);
+
+ if (slb_is_valid(slb)) {
+ slb_invalidate(slb);
+ slb_set_entry(env, n, slb);
+ /* XXX: given the fact that segment size is 256 MB or 1TB,
+ * and we still don't have a tlb_flush_mask(env, n, mask)
+ * in Qemu, we just invalidate all TLBs
+ */
+ tlb_flush(env, 1);
+ }
+ }
+}
+
+target_ulong ppc_load_slb (CPUPPCState *env, int slb_nr)
+{
+ target_ulong rt;
+ ppc_slb_t *slb = slb_get_entry(env, slb_nr);
+
+ if (slb_is_valid(slb)) {
+ /* SLB entry is valid */
+ /* Copy SLB bits 62:88 to Rt 37:63 (VSID 23:49) */
+ rt = slb->tmp >> 8; /* 65:88 => 40:63 */
+ rt |= (slb->tmp64 & 0x7) << 24; /* 62:64 => 37:39 */
+ /* Copy SLB bits 89:92 to Rt 33:36 (KsKpNL) */
+ rt |= ((slb->tmp >> 4) & 0xF) << 27;
+ } else {
+ rt = 0;
+ }
+ LOG_SLB("%s: %016" PRIx64 " %08" PRIx32 " => %d "
+ TARGET_FMT_lx "\n", __func__, slb->tmp64, slb->tmp, slb_nr, rt);
+
+ return rt;
+}
+
+void ppc_store_slb (CPUPPCState *env, target_ulong rb, target_ulong rs)
+{
+ ppc_slb_t *slb;
+
+ uint64_t vsid;
+ uint64_t esid;
+ int flags, valid, slb_nr;
+
+ vsid = rs >> 12;
+ flags = ((rs >> 8) & 0xf);
+
+ esid = rb >> 28;
+ valid = (rb & (1 << 27));
+ slb_nr = rb & 0xfff;
+
+ slb = slb_get_entry(env, slb_nr);
+ slb->tmp64 = (esid << 28) | valid | (vsid >> 24);
+ slb->tmp = (vsid << 8) | (flags << 3);
+
+ LOG_SLB("%s: %d " TARGET_FMT_lx " - " TARGET_FMT_lx " => %016" PRIx64
+ " %08" PRIx32 "\n", __func__, slb_nr, rb, rs, slb->tmp64,
+ slb->tmp);
+
+ slb_set_entry(env, slb_nr, slb);
+}
+#endif /* defined(TARGET_PPC64) */
+
+/* Perform segment based translation */
+static inline target_phys_addr_t get_pgaddr(target_phys_addr_t sdr1,
+ int sdr_sh,
+ target_phys_addr_t hash,
+ target_phys_addr_t mask)
+{
+ return (sdr1 & ((target_phys_addr_t)(-1ULL) << sdr_sh)) | (hash & mask);
+}
+
+static inline int get_segment(CPUState *env, mmu_ctx_t *ctx,
+ target_ulong eaddr, int rw, int type)
+{
+ target_phys_addr_t sdr, hash, mask, sdr_mask, htab_mask;
+ target_ulong sr, vsid, vsid_mask, pgidx, page_mask;
+#if defined(TARGET_PPC64)
+ int attr;
+#endif
+ int ds, vsid_sh, sdr_sh, pr, target_page_bits;
+ int ret, ret2;
+
+ pr = msr_pr;
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64) {
+ LOG_MMU("Check SLBs\n");
+ ret = slb_lookup(env, eaddr, &vsid, &page_mask, &attr,
+ &target_page_bits);
+ if (ret < 0)
+ return ret;
+ ctx->key = ((attr & 0x40) && (pr != 0)) ||
+ ((attr & 0x80) && (pr == 0)) ? 1 : 0;
+ ds = 0;
+ ctx->nx = attr & 0x10 ? 1 : 0;
+ ctx->eaddr = eaddr;
+ vsid_mask = 0x00003FFFFFFFFF80ULL;
+ vsid_sh = 7;
+ sdr_sh = 18;
+ sdr_mask = 0x3FF80;
+ } else
+#endif /* defined(TARGET_PPC64) */
+ {
+ sr = env->sr[eaddr >> 28];
+ page_mask = 0x0FFFFFFF;
+ ctx->key = (((sr & 0x20000000) && (pr != 0)) ||
+ ((sr & 0x40000000) && (pr == 0))) ? 1 : 0;
+ ds = sr & 0x80000000 ? 1 : 0;
+ ctx->nx = sr & 0x10000000 ? 1 : 0;
+ vsid = sr & 0x00FFFFFF;
+ vsid_mask = 0x01FFFFC0;
+ vsid_sh = 6;
+ sdr_sh = 16;
+ sdr_mask = 0xFFC0;
+ target_page_bits = TARGET_PAGE_BITS;
+ LOG_MMU("Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip="
+ TARGET_FMT_lx " lr=" TARGET_FMT_lx
+ " ir=%d dr=%d pr=%d %d t=%d\n",
+ eaddr, (int)(eaddr >> 28), sr, env->nip, env->lr, (int)msr_ir,
+ (int)msr_dr, pr != 0 ? 1 : 0, rw, type);
+ }
+ LOG_MMU("pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n",
+ ctx->key, ds, ctx->nx, vsid);
+ ret = -1;
+ if (!ds) {
+ /* Check if instruction fetch is allowed, if needed */
+ if (type != ACCESS_CODE || ctx->nx == 0) {
+ /* Page address translation */
+ /* Primary table address */
+ sdr = env->sdr1;
+ pgidx = (eaddr & page_mask) >> target_page_bits;
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64) {
+ htab_mask = 0x0FFFFFFF >> (28 - (sdr & 0x1F));
+ /* XXX: this is false for 1 TB segments */
+ hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
+ } else
+#endif
+ {
+ htab_mask = sdr & 0x000001FF;
+ hash = ((vsid ^ pgidx) << vsid_sh) & vsid_mask;
+ }
+ mask = (htab_mask << sdr_sh) | sdr_mask;
+ LOG_MMU("sdr " TARGET_FMT_plx " sh %d hash " TARGET_FMT_plx
+ " mask " TARGET_FMT_plx " " TARGET_FMT_lx "\n",
+ sdr, sdr_sh, hash, mask, page_mask);
+ ctx->pg_addr[0] = get_pgaddr(sdr, sdr_sh, hash, mask);
+ /* Secondary table address */
+ hash = (~hash) & vsid_mask;
+ LOG_MMU("sdr " TARGET_FMT_plx " sh %d hash " TARGET_FMT_plx
+ " mask " TARGET_FMT_plx "\n", sdr, sdr_sh, hash, mask);
+ ctx->pg_addr[1] = get_pgaddr(sdr, sdr_sh, hash, mask);
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64) {
+ /* Only 5 bits of the page index are used in the AVPN */
+ if (target_page_bits > 23) {
+ ctx->ptem = (vsid << 12) |
+ ((pgidx << (target_page_bits - 16)) & 0xF80);
+ } else {
+ ctx->ptem = (vsid << 12) | ((pgidx >> 4) & 0x0F80);
+ }
+ } else
+#endif
+ {
+ ctx->ptem = (vsid << 7) | (pgidx >> 10);
+ }
+ /* Initialize real address with an invalid value */
+ ctx->raddr = (target_phys_addr_t)-1ULL;
+ if (unlikely(env->mmu_model == POWERPC_MMU_SOFT_6xx ||
+ env->mmu_model == POWERPC_MMU_SOFT_74xx)) {
+ /* Software TLB search */
+ ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type);
+ } else {
+ LOG_MMU("0 sdr1=" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " "
+ "api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx
+ " pg_addr=" TARGET_FMT_plx "\n",
+ sdr, vsid, pgidx, hash, ctx->pg_addr[0]);
+ /* Primary table lookup */
+ ret = find_pte(env, ctx, 0, rw, type, target_page_bits);
+ if (ret < 0) {
+ /* Secondary table lookup */
+ if (eaddr != 0xEFFFFFFF)
+ LOG_MMU("1 sdr1=" TARGET_FMT_plx " vsid=" TARGET_FMT_lx " "
+ "api=" TARGET_FMT_lx " hash=" TARGET_FMT_plx
+ " pg_addr=" TARGET_FMT_plx "\n", sdr, vsid,
+ pgidx, hash, ctx->pg_addr[1]);
+ ret2 = find_pte(env, ctx, 1, rw, type,
+ target_page_bits);
+ if (ret2 != -1)
+ ret = ret2;
+ }
+ }
+#if defined (DUMP_PAGE_TABLES)
+ if (qemu_log_enabled()) {
+ target_phys_addr_t curaddr;
+ uint32_t a0, a1, a2, a3;
+ qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx
+ "\n", sdr, mask + 0x80);
+ for (curaddr = sdr; curaddr < (sdr + mask + 0x80);
+ curaddr += 16) {
+ a0 = ldl_phys(curaddr);
+ a1 = ldl_phys(curaddr + 4);
+ a2 = ldl_phys(curaddr + 8);
+ a3 = ldl_phys(curaddr + 12);
+ if (a0 != 0 || a1 != 0 || a2 != 0 || a3 != 0) {
+ qemu_log(TARGET_FMT_plx ": %08x %08x %08x %08x\n",
+ curaddr, a0, a1, a2, a3);
+ }
+ }
+ }
+#endif
+ } else {
+ LOG_MMU("No access allowed\n");
+ ret = -3;
+ }
+ } else {
+ LOG_MMU("direct store...\n");
+ /* Direct-store segment : absolutely *BUGGY* for now */
+ switch (type) {
+ case ACCESS_INT:
+ /* Integer load/store : only access allowed */
+ break;
+ case ACCESS_CODE:
+ /* No code fetch is allowed in direct-store areas */
+ return -4;
+ case ACCESS_FLOAT:
+ /* Floating point load/store */
+ return -4;
+ case ACCESS_RES:
+ /* lwarx, ldarx or srwcx. */
+ return -4;
+ case ACCESS_CACHE:
+ /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
+ /* Should make the instruction do no-op.
+ * As it already do no-op, it's quite easy :-)
+ */
+ ctx->raddr = eaddr;
+ return 0;
+ case ACCESS_EXT:
+ /* eciwx or ecowx */
+ return -4;
+ default:
+ qemu_log("ERROR: instruction should not need "
+ "address translation\n");
+ return -4;
+ }
+ if ((rw == 1 || ctx->key != 1) && (rw == 0 || ctx->key != 0)) {
+ ctx->raddr = eaddr;
+ ret = 2;
+ } else {
+ ret = -2;
+ }
+ }
+
+ return ret;
+}
+
+/* Generic TLB check function for embedded PowerPC implementations */
+static inline int ppcemb_tlb_check(CPUState *env, ppcemb_tlb_t *tlb,
+ target_phys_addr_t *raddrp,
+ target_ulong address, uint32_t pid, int ext,
+ int i)
+{
+ target_ulong mask;
+
+ /* Check valid flag */
+ if (!(tlb->prot & PAGE_VALID)) {
+ return -1;
+ }
+ mask = ~(tlb->size - 1);
+ LOG_SWTLB("%s: TLB %d address " TARGET_FMT_lx " PID %u <=> " TARGET_FMT_lx
+ " " TARGET_FMT_lx " %u\n", __func__, i, address, pid, tlb->EPN,
+ mask, (uint32_t)tlb->PID);
+ /* Check PID */
+ if (tlb->PID != 0 && tlb->PID != pid)
+ return -1;
+ /* Check effective address */
+ if ((address & mask) != tlb->EPN)
+ return -1;
+ *raddrp = (tlb->RPN & mask) | (address & ~mask);
+#if (TARGET_PHYS_ADDR_BITS >= 36)
+ if (ext) {
+ /* Extend the physical address to 36 bits */
+ *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32;
+ }
+#endif
+
+ return 0;
+}
+
+/* Generic TLB search function for PowerPC embedded implementations */
+int ppcemb_tlb_search (CPUPPCState *env, target_ulong address, uint32_t pid)
+{
+ ppcemb_tlb_t *tlb;
+ target_phys_addr_t raddr;
+ int i, ret;
+
+ /* Default return value is no match */
+ ret = -1;
+ for (i = 0; i < env->nb_tlb; i++) {
+ tlb = &env->tlb[i].tlbe;
+ if (ppcemb_tlb_check(env, tlb, &raddr, address, pid, 0, i) == 0) {
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* Helpers specific to PowerPC 40x implementations */
+static inline void ppc4xx_tlb_invalidate_all(CPUState *env)
+{
+ ppcemb_tlb_t *tlb;
+ int i;
+
+ for (i = 0; i < env->nb_tlb; i++) {
+ tlb = &env->tlb[i].tlbe;
+ tlb->prot &= ~PAGE_VALID;
+ }
+ tlb_flush(env, 1);
+}
+
+static inline void ppc4xx_tlb_invalidate_virt(CPUState *env,
+ target_ulong eaddr, uint32_t pid)
+{
+#if !defined(FLUSH_ALL_TLBS)
+ ppcemb_tlb_t *tlb;
+ target_phys_addr_t raddr;
+ target_ulong page, end;
+ int i;
+
+ for (i = 0; i < env->nb_tlb; i++) {
+ tlb = &env->tlb[i].tlbe;
+ if (ppcemb_tlb_check(env, tlb, &raddr, eaddr, pid, 0, i) == 0) {
+ end = tlb->EPN + tlb->size;
+ for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+ tlb->prot &= ~PAGE_VALID;
+ break;
+ }
+ }
+#else
+ ppc4xx_tlb_invalidate_all(env);
+#endif
+}
+
+static int mmu40x_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
+ target_ulong address, int rw, int access_type)
+{
+ ppcemb_tlb_t *tlb;
+ target_phys_addr_t raddr;
+ int i, ret, zsel, zpr, pr;
+
+ ret = -1;
+ raddr = (target_phys_addr_t)-1ULL;
+ pr = msr_pr;
+ for (i = 0; i < env->nb_tlb; i++) {
+ tlb = &env->tlb[i].tlbe;
+ if (ppcemb_tlb_check(env, tlb, &raddr, address,
+ env->spr[SPR_40x_PID], 0, i) < 0)
+ continue;
+ zsel = (tlb->attr >> 4) & 0xF;
+ zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3;
+ LOG_SWTLB("%s: TLB %d zsel %d zpr %d rw %d attr %08x\n",
+ __func__, i, zsel, zpr, rw, tlb->attr);
+ /* Check execute enable bit */
+ switch (zpr) {
+ case 0x2:
+ if (pr != 0)
+ goto check_perms;
+ /* No break here */
+ case 0x3:
+ /* All accesses granted */
+ ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ ret = 0;
+ break;
+ case 0x0:
+ if (pr != 0) {
+ /* Raise Zone protection fault. */
+ env->spr[SPR_40x_ESR] = 1 << 22;
+ ctx->prot = 0;
+ ret = -2;
+ break;
+ }
+ /* No break here */
+ case 0x1:
+ check_perms:
+ /* Check from TLB entry */
+ ctx->prot = tlb->prot;
+ ret = check_prot(ctx->prot, rw, access_type);
+ if (ret == -2)
+ env->spr[SPR_40x_ESR] = 0;
+ break;
+ }
+ if (ret >= 0) {
+ ctx->raddr = raddr;
+ LOG_SWTLB("%s: access granted " TARGET_FMT_lx " => " TARGET_FMT_plx
+ " %d %d\n", __func__, address, ctx->raddr, ctx->prot,
+ ret);
+ return 0;
+ }
+ }
+ LOG_SWTLB("%s: access refused " TARGET_FMT_lx " => " TARGET_FMT_plx
+ " %d %d\n", __func__, address, raddr, ctx->prot, ret);
+
+ return ret;
+}
+
+void store_40x_sler (CPUPPCState *env, uint32_t val)
+{
+ /* XXX: TO BE FIXED */
+ if (val != 0x00000000) {
+ cpu_abort(env, "Little-endian regions are not supported by now\n");
+ }
+ env->spr[SPR_405_SLER] = val;
+}
+
+static int mmubooke_get_physical_address (CPUState *env, mmu_ctx_t *ctx,
+ target_ulong address, int rw,
+ int access_type)
+{
+ ppcemb_tlb_t *tlb;
+ target_phys_addr_t raddr;
+ int i, prot, ret;
+
+ ret = -1;
+ raddr = (target_phys_addr_t)-1ULL;
+ for (i = 0; i < env->nb_tlb; i++) {
+ tlb = &env->tlb[i].tlbe;
+ if (ppcemb_tlb_check(env, tlb, &raddr, address,
+ env->spr[SPR_BOOKE_PID], 1, i) < 0)
+ continue;
+ if (msr_pr != 0)
+ prot = tlb->prot & 0xF;
+ else
+ prot = (tlb->prot >> 4) & 0xF;
+ /* Check the address space */
+ if (access_type == ACCESS_CODE) {
+ if (msr_ir != (tlb->attr & 1))
+ continue;
+ ctx->prot = prot;
+ if (prot & PAGE_EXEC) {
+ ret = 0;
+ break;
+ }
+ ret = -3;
+ } else {
+ if (msr_dr != (tlb->attr & 1))
+ continue;
+ ctx->prot = prot;
+ if ((!rw && prot & PAGE_READ) || (rw && (prot & PAGE_WRITE))) {
+ ret = 0;
+ break;
+ }
+ ret = -2;
+ }
+ }
+ if (ret >= 0)
+ ctx->raddr = raddr;
+
+ return ret;
+}
+
+static inline int check_physical(CPUState *env, mmu_ctx_t *ctx,
+ target_ulong eaddr, int rw)
+{
+ int in_plb, ret;
+
+ ctx->raddr = eaddr;
+ ctx->prot = PAGE_READ | PAGE_EXEC;
+ ret = 0;
+ switch (env->mmu_model) {
+ case POWERPC_MMU_32B:
+ case POWERPC_MMU_601:
+ case POWERPC_MMU_SOFT_6xx:
+ case POWERPC_MMU_SOFT_74xx:
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_REAL:
+ case POWERPC_MMU_BOOKE:
+ ctx->prot |= PAGE_WRITE;
+ break;
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_620:
+ case POWERPC_MMU_64B:
+ /* Real address are 60 bits long */
+ ctx->raddr &= 0x0FFFFFFFFFFFFFFFULL;
+ ctx->prot |= PAGE_WRITE;
+ break;
+#endif
+ case POWERPC_MMU_SOFT_4xx_Z:
+ if (unlikely(msr_pe != 0)) {
+ /* 403 family add some particular protections,
+ * using PBL/PBU registers for accesses with no translation.
+ */
+ in_plb =
+ /* Check PLB validity */
+ (env->pb[0] < env->pb[1] &&
+ /* and address in plb area */
+ eaddr >= env->pb[0] && eaddr < env->pb[1]) ||
+ (env->pb[2] < env->pb[3] &&
+ eaddr >= env->pb[2] && eaddr < env->pb[3]) ? 1 : 0;
+ if (in_plb ^ msr_px) {
+ /* Access in protected area */
+ if (rw == 1) {
+ /* Access is not allowed */
+ ret = -2;
+ }
+ } else {
+ /* Read-write access is allowed */
+ ctx->prot |= PAGE_WRITE;
+ }
+ }
+ break;
+ case POWERPC_MMU_MPC8xx:
+ /* XXX: TODO */
+ cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_BOOKE_FSL:
+ /* XXX: TODO */
+ cpu_abort(env, "BookE FSL MMU model not implemented\n");
+ break;
+ default:
+ cpu_abort(env, "Unknown or invalid MMU model\n");
+ return -1;
+ }
+
+ return ret;
+}
+
+int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr,
+ int rw, int access_type)
+{
+ int ret;
+
+#if 0
+ qemu_log("%s\n", __func__);
+#endif
+ if ((access_type == ACCESS_CODE && msr_ir == 0) ||
+ (access_type != ACCESS_CODE && msr_dr == 0)) {
+ if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ /* The BookE MMU always performs address translation. The
+ IS and DS bits only affect the address space. */
+ ret = mmubooke_get_physical_address(env, ctx, eaddr,
+ rw, access_type);
+ } else {
+ /* No address translation. */
+ ret = check_physical(env, ctx, eaddr, rw);
+ }
+ } else {
+ ret = -1;
+ switch (env->mmu_model) {
+ case POWERPC_MMU_32B:
+ case POWERPC_MMU_601:
+ case POWERPC_MMU_SOFT_6xx:
+ case POWERPC_MMU_SOFT_74xx:
+ /* Try to find a BAT */
+ if (env->nb_BATs != 0)
+ ret = get_bat(env, ctx, eaddr, rw, access_type);
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_620:
+ case POWERPC_MMU_64B:
+#endif
+ if (ret < 0) {
+ /* We didn't match any BAT entry or don't have BATs */
+ ret = get_segment(env, ctx, eaddr, rw, access_type);
+ }
+ break;
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_SOFT_4xx_Z:
+ ret = mmu40x_get_physical_address(env, ctx, eaddr,
+ rw, access_type);
+ break;
+ case POWERPC_MMU_BOOKE:
+ ret = mmubooke_get_physical_address(env, ctx, eaddr,
+ rw, access_type);
+ break;
+ case POWERPC_MMU_MPC8xx:
+ /* XXX: TODO */
+ cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_BOOKE_FSL:
+ /* XXX: TODO */
+ cpu_abort(env, "BookE FSL MMU model not implemented\n");
+ return -1;
+ case POWERPC_MMU_REAL:
+ cpu_abort(env, "PowerPC in real mode do not do any translation\n");
+ return -1;
+ default:
+ cpu_abort(env, "Unknown or invalid MMU model\n");
+ return -1;
+ }
+ }
+#if 0
+ qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n",
+ __func__, eaddr, ret, ctx->raddr);
+#endif
+
+ return ret;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr)
+{
+ mmu_ctx_t ctx;
+
+ if (unlikely(get_physical_address(env, &ctx, addr, 0, ACCESS_INT) != 0))
+ return -1;
+
+ return ctx.raddr & TARGET_PAGE_MASK;
+}
+
+/* Perform address translation */
+int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ mmu_ctx_t ctx;
+ int access_type;
+ int ret = 0;
+
+ if (rw == 2) {
+ /* code access */
+ rw = 0;
+ access_type = ACCESS_CODE;
+ } else {
+ /* data access */
+ access_type = env->access_type;
+ }
+ ret = get_physical_address(env, &ctx, address, rw, access_type);
+ if (ret == 0) {
+ tlb_set_page(env, address & TARGET_PAGE_MASK,
+ ctx.raddr & TARGET_PAGE_MASK, ctx.prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+ ret = 0;
+ } else if (ret < 0) {
+ LOG_MMU_STATE(env);
+ if (access_type == ACCESS_CODE) {
+ switch (ret) {
+ case -1:
+ /* No matches in page tables or TLB */
+ switch (env->mmu_model) {
+ case POWERPC_MMU_SOFT_6xx:
+ env->exception_index = POWERPC_EXCP_IFTLB;
+ env->error_code = 1 << 18;
+ env->spr[SPR_IMISS] = address;
+ env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem;
+ goto tlb_miss;
+ case POWERPC_MMU_SOFT_74xx:
+ env->exception_index = POWERPC_EXCP_IFTLB;
+ goto tlb_miss_74xx;
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_SOFT_4xx_Z:
+ env->exception_index = POWERPC_EXCP_ITLB;
+ env->error_code = 0;
+ env->spr[SPR_40x_DEAR] = address;
+ env->spr[SPR_40x_ESR] = 0x00000000;
+ break;
+ case POWERPC_MMU_32B:
+ case POWERPC_MMU_601:
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_620:
+ case POWERPC_MMU_64B:
+#endif
+ env->exception_index = POWERPC_EXCP_ISI;
+ env->error_code = 0x40000000;
+ break;
+ case POWERPC_MMU_BOOKE:
+ env->exception_index = POWERPC_EXCP_ITLB;
+ env->error_code = 0;
+ env->spr[SPR_BOOKE_DEAR] = address;
+ return -1;
+ case POWERPC_MMU_BOOKE_FSL:
+ /* XXX: TODO */
+ cpu_abort(env, "BookE FSL MMU model is not implemented\n");
+ return -1;
+ case POWERPC_MMU_MPC8xx:
+ /* XXX: TODO */
+ cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_REAL:
+ cpu_abort(env, "PowerPC in real mode should never raise "
+ "any MMU exceptions\n");
+ return -1;
+ default:
+ cpu_abort(env, "Unknown or invalid MMU model\n");
+ return -1;
+ }
+ break;
+ case -2:
+ /* Access rights violation */
+ env->exception_index = POWERPC_EXCP_ISI;
+ env->error_code = 0x08000000;
+ break;
+ case -3:
+ /* No execute protection violation */
+ if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ env->spr[SPR_BOOKE_ESR] = 0x00000000;
+ }
+ env->exception_index = POWERPC_EXCP_ISI;
+ env->error_code = 0x10000000;
+ break;
+ case -4:
+ /* Direct store exception */
+ /* No code fetch is allowed in direct-store areas */
+ env->exception_index = POWERPC_EXCP_ISI;
+ env->error_code = 0x10000000;
+ break;
+#if defined(TARGET_PPC64)
+ case -5:
+ /* No match in segment table */
+ if (env->mmu_model == POWERPC_MMU_620) {
+ env->exception_index = POWERPC_EXCP_ISI;
+ /* XXX: this might be incorrect */
+ env->error_code = 0x40000000;
+ } else {
+ env->exception_index = POWERPC_EXCP_ISEG;
+ env->error_code = 0;
+ }
+ break;
+#endif
+ }
+ } else {
+ switch (ret) {
+ case -1:
+ /* No matches in page tables or TLB */
+ switch (env->mmu_model) {
+ case POWERPC_MMU_SOFT_6xx:
+ if (rw == 1) {
+ env->exception_index = POWERPC_EXCP_DSTLB;
+ env->error_code = 1 << 16;
+ } else {
+ env->exception_index = POWERPC_EXCP_DLTLB;
+ env->error_code = 0;
+ }
+ env->spr[SPR_DMISS] = address;
+ env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem;
+ tlb_miss:
+ env->error_code |= ctx.key << 19;
+ env->spr[SPR_HASH1] = ctx.pg_addr[0];
+ env->spr[SPR_HASH2] = ctx.pg_addr[1];
+ break;
+ case POWERPC_MMU_SOFT_74xx:
+ if (rw == 1) {
+ env->exception_index = POWERPC_EXCP_DSTLB;
+ } else {
+ env->exception_index = POWERPC_EXCP_DLTLB;
+ }
+ tlb_miss_74xx:
+ /* Implement LRU algorithm */
+ env->error_code = ctx.key << 19;
+ env->spr[SPR_TLBMISS] = (address & ~((target_ulong)0x3)) |
+ ((env->last_way + 1) & (env->nb_ways - 1));
+ env->spr[SPR_PTEHI] = 0x80000000 | ctx.ptem;
+ break;
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_SOFT_4xx_Z:
+ env->exception_index = POWERPC_EXCP_DTLB;
+ env->error_code = 0;
+ env->spr[SPR_40x_DEAR] = address;
+ if (rw)
+ env->spr[SPR_40x_ESR] = 0x00800000;
+ else
+ env->spr[SPR_40x_ESR] = 0x00000000;
+ break;
+ case POWERPC_MMU_32B:
+ case POWERPC_MMU_601:
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_620:
+ case POWERPC_MMU_64B:
+#endif
+ env->exception_index = POWERPC_EXCP_DSI;
+ env->error_code = 0;
+ env->spr[SPR_DAR] = address;
+ if (rw == 1)
+ env->spr[SPR_DSISR] = 0x42000000;
+ else
+ env->spr[SPR_DSISR] = 0x40000000;
+ break;
+ case POWERPC_MMU_MPC8xx:
+ /* XXX: TODO */
+ cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_BOOKE:
+ env->exception_index = POWERPC_EXCP_DTLB;
+ env->error_code = 0;
+ env->spr[SPR_BOOKE_DEAR] = address;
+ env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
+ return -1;
+ case POWERPC_MMU_BOOKE_FSL:
+ /* XXX: TODO */
+ cpu_abort(env, "BookE FSL MMU model is not implemented\n");
+ return -1;
+ case POWERPC_MMU_REAL:
+ cpu_abort(env, "PowerPC in real mode should never raise "
+ "any MMU exceptions\n");
+ return -1;
+ default:
+ cpu_abort(env, "Unknown or invalid MMU model\n");
+ return -1;
+ }
+ break;
+ case -2:
+ /* Access rights violation */
+ env->exception_index = POWERPC_EXCP_DSI;
+ env->error_code = 0;
+ if (env->mmu_model == POWERPC_MMU_SOFT_4xx
+ || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) {
+ env->spr[SPR_40x_DEAR] = address;
+ if (rw) {
+ env->spr[SPR_40x_ESR] |= 0x00800000;
+ }
+ } else if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ env->spr[SPR_BOOKE_DEAR] = address;
+ env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0;
+ } else {
+ env->spr[SPR_DAR] = address;
+ if (rw == 1) {
+ env->spr[SPR_DSISR] = 0x0A000000;
+ } else {
+ env->spr[SPR_DSISR] = 0x08000000;
+ }
+ }
+ break;
+ case -4:
+ /* Direct store exception */
+ switch (access_type) {
+ case ACCESS_FLOAT:
+ /* Floating point load/store */
+ env->exception_index = POWERPC_EXCP_ALIGN;
+ env->error_code = POWERPC_EXCP_ALIGN_FP;
+ env->spr[SPR_DAR] = address;
+ break;
+ case ACCESS_RES:
+ /* lwarx, ldarx or stwcx. */
+ env->exception_index = POWERPC_EXCP_DSI;
+ env->error_code = 0;
+ env->spr[SPR_DAR] = address;
+ if (rw == 1)
+ env->spr[SPR_DSISR] = 0x06000000;
+ else
+ env->spr[SPR_DSISR] = 0x04000000;
+ break;
+ case ACCESS_EXT:
+ /* eciwx or ecowx */
+ env->exception_index = POWERPC_EXCP_DSI;
+ env->error_code = 0;
+ env->spr[SPR_DAR] = address;
+ if (rw == 1)
+ env->spr[SPR_DSISR] = 0x06100000;
+ else
+ env->spr[SPR_DSISR] = 0x04100000;
+ break;
+ default:
+ printf("DSI: invalid exception (%d)\n", ret);
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ env->error_code =
+ POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL;
+ env->spr[SPR_DAR] = address;
+ break;
+ }
+ break;
+#if defined(TARGET_PPC64)
+ case -5:
+ /* No match in segment table */
+ if (env->mmu_model == POWERPC_MMU_620) {
+ env->exception_index = POWERPC_EXCP_DSI;
+ env->error_code = 0;
+ env->spr[SPR_DAR] = address;
+ /* XXX: this might be incorrect */
+ if (rw == 1)
+ env->spr[SPR_DSISR] = 0x42000000;
+ else
+ env->spr[SPR_DSISR] = 0x40000000;
+ } else {
+ env->exception_index = POWERPC_EXCP_DSEG;
+ env->error_code = 0;
+ env->spr[SPR_DAR] = address;
+ }
+ break;
+#endif
+ }
+ }
+#if 0
+ printf("%s: set exception to %d %02x\n", __func__,
+ env->exception, env->error_code);
+#endif
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* BATs management */
+#if !defined(FLUSH_ALL_TLBS)
+static inline void do_invalidate_BAT(CPUPPCState *env, target_ulong BATu,
+ target_ulong mask)
+{
+ target_ulong base, end, page;
+
+ base = BATu & ~0x0001FFFF;
+ end = base + mask + 0x00020000;
+ LOG_BATS("Flush BAT from " TARGET_FMT_lx " to " TARGET_FMT_lx " ("
+ TARGET_FMT_lx ")\n", base, end, mask);
+ for (page = base; page != end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+ LOG_BATS("Flush done\n");
+}
+#endif
+
+static inline void dump_store_bat(CPUPPCState *env, char ID, int ul, int nr,
+ target_ulong value)
+{
+ LOG_BATS("Set %cBAT%d%c to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", ID,
+ nr, ul == 0 ? 'u' : 'l', value, env->nip);
+}
+
+void ppc_store_ibatu (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+
+ dump_store_bat(env, 'I', 0, nr, value);
+ if (env->IBAT[0][nr] != value) {
+ mask = (value << 15) & 0x0FFE0000UL;
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#endif
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ mask = (value << 15) & 0x0FFE0000UL;
+ env->IBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->IBAT[1][nr] = (env->IBAT[1][nr] & 0x0000007B) |
+ (env->IBAT[1][nr] & ~0x0001FFFF & ~mask);
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void ppc_store_ibatl (CPUPPCState *env, int nr, target_ulong value)
+{
+ dump_store_bat(env, 'I', 1, nr, value);
+ env->IBAT[1][nr] = value;
+}
+
+void ppc_store_dbatu (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+
+ dump_store_bat(env, 'D', 0, nr, value);
+ if (env->DBAT[0][nr] != value) {
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ mask = (value << 15) & 0x0FFE0000UL;
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->DBAT[0][nr], mask);
+#endif
+ mask = (value << 15) & 0x0FFE0000UL;
+ env->DBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->DBAT[1][nr] = (env->DBAT[1][nr] & 0x0000007B) |
+ (env->DBAT[1][nr] & ~0x0001FFFF & ~mask);
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->DBAT[0][nr], mask);
+#else
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void ppc_store_dbatl (CPUPPCState *env, int nr, target_ulong value)
+{
+ dump_store_bat(env, 'D', 1, nr, value);
+ env->DBAT[1][nr] = value;
+}
+
+void ppc_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+#if defined(FLUSH_ALL_TLBS)
+ int do_inval;
+#endif
+
+ dump_store_bat(env, 'I', 0, nr, value);
+ if (env->IBAT[0][nr] != value) {
+#if defined(FLUSH_ALL_TLBS)
+ do_inval = 0;
+#endif
+ mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL;
+ if (env->IBAT[1][nr] & 0x40) {
+ /* Invalidate BAT only if it is valid */
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+ do_inval = 1;
+#endif
+ }
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ env->IBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->DBAT[0][nr] = env->IBAT[0][nr];
+ if (env->IBAT[1][nr] & 0x40) {
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+ do_inval = 1;
+#endif
+ }
+#if defined(FLUSH_ALL_TLBS)
+ if (do_inval)
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void ppc_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+#if defined(FLUSH_ALL_TLBS)
+ int do_inval;
+#endif
+
+ dump_store_bat(env, 'I', 1, nr, value);
+ if (env->IBAT[1][nr] != value) {
+#if defined(FLUSH_ALL_TLBS)
+ do_inval = 0;
+#endif
+ if (env->IBAT[1][nr] & 0x40) {
+#if !defined(FLUSH_ALL_TLBS)
+ mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL;
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+ do_inval = 1;
+#endif
+ }
+ if (value & 0x40) {
+#if !defined(FLUSH_ALL_TLBS)
+ mask = (value << 17) & 0x0FFE0000UL;
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#else
+ do_inval = 1;
+#endif
+ }
+ env->IBAT[1][nr] = value;
+ env->DBAT[1][nr] = value;
+#if defined(FLUSH_ALL_TLBS)
+ if (do_inval)
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+/*****************************************************************************/
+/* TLB management */
+void ppc_tlb_invalidate_all (CPUPPCState *env)
+{
+ switch (env->mmu_model) {
+ case POWERPC_MMU_SOFT_6xx:
+ case POWERPC_MMU_SOFT_74xx:
+ ppc6xx_tlb_invalidate_all(env);
+ break;
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_SOFT_4xx_Z:
+ ppc4xx_tlb_invalidate_all(env);
+ break;
+ case POWERPC_MMU_REAL:
+ cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n");
+ break;
+ case POWERPC_MMU_MPC8xx:
+ /* XXX: TODO */
+ cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_BOOKE:
+ tlb_flush(env, 1);
+ break;
+ case POWERPC_MMU_BOOKE_FSL:
+ /* XXX: TODO */
+ if (!kvm_enabled())
+ cpu_abort(env, "BookE MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_32B:
+ case POWERPC_MMU_601:
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_620:
+ case POWERPC_MMU_64B:
+#endif /* defined(TARGET_PPC64) */
+ tlb_flush(env, 1);
+ break;
+ default:
+ /* XXX: TODO */
+ cpu_abort(env, "Unknown MMU model\n");
+ break;
+ }
+}
+
+void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr)
+{
+#if !defined(FLUSH_ALL_TLBS)
+ addr &= TARGET_PAGE_MASK;
+ switch (env->mmu_model) {
+ case POWERPC_MMU_SOFT_6xx:
+ case POWERPC_MMU_SOFT_74xx:
+ ppc6xx_tlb_invalidate_virt(env, addr, 0);
+ if (env->id_tlbs == 1)
+ ppc6xx_tlb_invalidate_virt(env, addr, 1);
+ break;
+ case POWERPC_MMU_SOFT_4xx:
+ case POWERPC_MMU_SOFT_4xx_Z:
+ ppc4xx_tlb_invalidate_virt(env, addr, env->spr[SPR_40x_PID]);
+ break;
+ case POWERPC_MMU_REAL:
+ cpu_abort(env, "No TLB for PowerPC 4xx in real mode\n");
+ break;
+ case POWERPC_MMU_MPC8xx:
+ /* XXX: TODO */
+ cpu_abort(env, "MPC8xx MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_BOOKE:
+ /* XXX: TODO */
+ cpu_abort(env, "BookE MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_BOOKE_FSL:
+ /* XXX: TODO */
+ cpu_abort(env, "BookE FSL MMU model is not implemented\n");
+ break;
+ case POWERPC_MMU_32B:
+ case POWERPC_MMU_601:
+ /* tlbie invalidate TLBs for all segments */
+ addr &= ~((target_ulong)-1ULL << 28);
+ /* XXX: this case should be optimized,
+ * giving a mask to tlb_flush_page
+ */
+ tlb_flush_page(env, addr | (0x0 << 28));
+ tlb_flush_page(env, addr | (0x1 << 28));
+ tlb_flush_page(env, addr | (0x2 << 28));
+ tlb_flush_page(env, addr | (0x3 << 28));
+ tlb_flush_page(env, addr | (0x4 << 28));
+ tlb_flush_page(env, addr | (0x5 << 28));
+ tlb_flush_page(env, addr | (0x6 << 28));
+ tlb_flush_page(env, addr | (0x7 << 28));
+ tlb_flush_page(env, addr | (0x8 << 28));
+ tlb_flush_page(env, addr | (0x9 << 28));
+ tlb_flush_page(env, addr | (0xA << 28));
+ tlb_flush_page(env, addr | (0xB << 28));
+ tlb_flush_page(env, addr | (0xC << 28));
+ tlb_flush_page(env, addr | (0xD << 28));
+ tlb_flush_page(env, addr | (0xE << 28));
+ tlb_flush_page(env, addr | (0xF << 28));
+ break;
+#if defined(TARGET_PPC64)
+ case POWERPC_MMU_620:
+ case POWERPC_MMU_64B:
+ /* tlbie invalidate TLBs for all segments */
+ /* XXX: given the fact that there are too many segments to invalidate,
+ * and we still don't have a tlb_flush_mask(env, n, mask) in Qemu,
+ * we just invalidate all TLBs
+ */
+ tlb_flush(env, 1);
+ break;
+#endif /* defined(TARGET_PPC64) */
+ default:
+ /* XXX: TODO */
+ cpu_abort(env, "Unknown MMU model\n");
+ break;
+ }
+#else
+ ppc_tlb_invalidate_all(env);
+#endif
+}
+
+/*****************************************************************************/
+/* Special registers manipulation */
+#if defined(TARGET_PPC64)
+void ppc_store_asr (CPUPPCState *env, target_ulong value)
+{
+ if (env->asr != value) {
+ env->asr = value;
+ tlb_flush(env, 1);
+ }
+}
+#endif
+
+void ppc_store_sdr1 (CPUPPCState *env, target_ulong value)
+{
+ LOG_MMU("%s: " TARGET_FMT_lx "\n", __func__, value);
+ if (env->sdr1 != value) {
+ /* XXX: for PowerPC 64, should check that the HTABSIZE value
+ * is <= 28
+ */
+ env->sdr1 = value;
+ tlb_flush(env, 1);
+ }
+}
+
+#if defined(TARGET_PPC64)
+target_ulong ppc_load_sr (CPUPPCState *env, int slb_nr)
+{
+ // XXX
+ return 0;
+}
+#endif
+
+void ppc_store_sr (CPUPPCState *env, int srnum, target_ulong value)
+{
+ LOG_MMU("%s: reg=%d " TARGET_FMT_lx " " TARGET_FMT_lx "\n", __func__,
+ srnum, value, env->sr[srnum]);
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64) {
+ uint64_t rb = 0, rs = 0;
+
+ /* ESID = srnum */
+ rb |= ((uint32_t)srnum & 0xf) << 28;
+ /* Set the valid bit */
+ rb |= 1 << 27;
+ /* Index = ESID */
+ rb |= (uint32_t)srnum;
+
+ /* VSID = VSID */
+ rs |= (value & 0xfffffff) << 12;
+ /* flags = flags */
+ rs |= ((value >> 27) & 0xf) << 9;
+
+ ppc_store_slb(env, rb, rs);
+ } else
+#endif
+ if (env->sr[srnum] != value) {
+ env->sr[srnum] = value;
+/* Invalidating 256MB of virtual memory in 4kB pages is way longer than
+ flusing the whole TLB. */
+#if !defined(FLUSH_ALL_TLBS) && 0
+ {
+ target_ulong page, end;
+ /* Invalidate 256 MB of virtual memory */
+ page = (16 << 20) * srnum;
+ end = page + (16 << 20);
+ for (; page != end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+ }
+#else
+ tlb_flush(env, 1);
+#endif
+ }
+}
+#endif /* !defined (CONFIG_USER_ONLY) */
+
+/* GDBstub can read and write MSR... */
+void ppc_store_msr (CPUPPCState *env, target_ulong value)
+{
+ hreg_store_msr(env, value, 0);
+}
+
+/*****************************************************************************/
+/* Exception processing */
+#if defined (CONFIG_USER_ONLY)
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+}
+
+void ppc_hw_interrupt (CPUState *env)
+{
+ env->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+}
+#else /* defined (CONFIG_USER_ONLY) */
+static inline void dump_syscall(CPUState *env)
+{
+ qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64
+ " r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64
+ " nip=" TARGET_FMT_lx "\n",
+ ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3),
+ ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5),
+ ppc_dump_gpr(env, 6), env->nip);
+}
+
+/* Note that this function should be greatly optimized
+ * when called with a constant excp, from ppc_hw_interrupt
+ */
+static inline void powerpc_excp(CPUState *env, int excp_model, int excp)
+{
+ target_ulong msr, new_msr, vector;
+ int srr0, srr1, asrr0, asrr1;
+ int lpes0, lpes1, lev;
+
+ if (0) {
+ /* XXX: find a suitable condition to enable the hypervisor mode */
+ lpes0 = (env->spr[SPR_LPCR] >> 1) & 1;
+ lpes1 = (env->spr[SPR_LPCR] >> 2) & 1;
+ } else {
+ /* Those values ensure we won't enter the hypervisor mode */
+ lpes0 = 0;
+ lpes1 = 1;
+ }
+
+ qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
+ " => %08x (%02x)\n", env->nip, excp, env->error_code);
+
+ /* new srr1 value excluding must-be-zero bits */
+ msr = env->msr & ~0x783f0000ULL;
+
+ /* new interrupt handler msr */
+ new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+
+ /* target registers */
+ srr0 = SPR_SRR0;
+ srr1 = SPR_SRR1;
+ asrr0 = -1;
+ asrr1 = -1;
+
+ switch (excp) {
+ case POWERPC_EXCP_NONE:
+ /* Should never happen */
+ return;
+ case POWERPC_EXCP_CRITICAL: /* Critical input */
+ switch (excp_model) {
+ case POWERPC_EXCP_40x:
+ srr0 = SPR_40x_SRR2;
+ srr1 = SPR_40x_SRR3;
+ break;
+ case POWERPC_EXCP_BOOKE:
+ srr0 = SPR_BOOKE_CSRR0;
+ srr1 = SPR_BOOKE_CSRR1;
+ break;
+ case POWERPC_EXCP_G2:
+ break;
+ default:
+ goto excp_invalid;
+ }
+ goto store_next;
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ if (msr_me == 0) {
+ /* Machine check exception is not enabled.
+ * Enter checkstop state.
+ */
+ if (qemu_log_enabled()) {
+ qemu_log("Machine check while not allowed. "
+ "Entering checkstop state\n");
+ } else {
+ fprintf(stderr, "Machine check while not allowed. "
+ "Entering checkstop state\n");
+ }
+ env->halted = 1;
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ }
+ if (0) {
+ /* XXX: find a suitable condition to enable the hypervisor mode */
+ new_msr |= (target_ulong)MSR_HVB;
+ }
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+
+ /* XXX: should also have something loaded in DAR / DSISR */
+ switch (excp_model) {
+ case POWERPC_EXCP_40x:
+ srr0 = SPR_40x_SRR2;
+ srr1 = SPR_40x_SRR3;
+ break;
+ case POWERPC_EXCP_BOOKE:
+ srr0 = SPR_BOOKE_MCSRR0;
+ srr1 = SPR_BOOKE_MCSRR1;
+ asrr0 = SPR_BOOKE_CSRR0;
+ asrr1 = SPR_BOOKE_CSRR1;
+ break;
+ default:
+ break;
+ }
+ goto store_next;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx
+ "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx
+ "\n", msr, env->nip);
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ msr |= env->error_code;
+ goto store_next;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ if (lpes0 == 1)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ /* XXX: this is false */
+ /* Get rS/rD and rA from faulting opcode */
+ env->spr[SPR_DSISR] |= (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16;
+ goto store_current;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
+ LOG_EXCP("Ignore floating point exception\n");
+ env->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+ return;
+ }
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ msr |= 0x00100000;
+ if (msr_fe0 == msr_fe1)
+ goto store_next;
+ msr |= 0x00010000;
+ break;
+ case POWERPC_EXCP_INVAL:
+ LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip);
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ msr |= 0x00080000;
+ break;
+ case POWERPC_EXCP_PRIV:
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ msr |= 0x00040000;
+ break;
+ case POWERPC_EXCP_TRAP:
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ msr |= 0x00020000;
+ break;
+ default:
+ /* Should never occur */
+ cpu_abort(env, "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+ goto store_current;
+ case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_current;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ dump_syscall(env);
+ lev = env->error_code;
+ if (lev == 1 || (lpes0 == 0 && lpes1 == 0))
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
+ goto store_current;
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
+ /* FIT on 4xx */
+ LOG_EXCP("FIT exception\n");
+ goto store_next;
+ case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
+ LOG_EXCP("WDT exception\n");
+ switch (excp_model) {
+ case POWERPC_EXCP_BOOKE:
+ srr0 = SPR_BOOKE_CSRR0;
+ srr1 = SPR_BOOKE_CSRR1;
+ break;
+ default:
+ break;
+ }
+ goto store_next;
+ case POWERPC_EXCP_DTLB: /* Data TLB error */
+ goto store_next;
+ case POWERPC_EXCP_ITLB: /* Instruction TLB error */
+ goto store_next;
+ case POWERPC_EXCP_DEBUG: /* Debug interrupt */
+ switch (excp_model) {
+ case POWERPC_EXCP_BOOKE:
+ srr0 = SPR_BOOKE_DSRR0;
+ srr1 = SPR_BOOKE_DSRR1;
+ asrr0 = SPR_BOOKE_CSRR0;
+ asrr1 = SPR_BOOKE_CSRR1;
+ break;
+ default:
+ break;
+ }
+ /* XXX: TODO */
+ cpu_abort(env, "Debug exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
+ goto store_current;
+ case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
+ /* XXX: TODO */
+ cpu_abort(env, "Embedded floating point data exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
+ /* XXX: TODO */
+ cpu_abort(env, "Embedded floating point round exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Performance counter exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Embedded doorbell interrupt is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
+ switch (excp_model) {
+ case POWERPC_EXCP_BOOKE:
+ srr0 = SPR_BOOKE_CSRR0;
+ srr1 = SPR_BOOKE_CSRR1;
+ break;
+ default:
+ break;
+ }
+ /* XXX: TODO */
+ cpu_abort(env, "Embedded doorbell critical interrupt "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ if (msr_pow) {
+ /* indicate that we resumed from power save mode */
+ msr |= 0x10000;
+ } else {
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+ }
+
+ if (0) {
+ /* XXX: find a suitable condition to enable the hypervisor mode */
+ new_msr |= (target_ulong)MSR_HVB;
+ }
+ goto store_next;
+ case POWERPC_EXCP_DSEG: /* Data segment exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_ISEG: /* Instruction segment exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ goto store_next;
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_next;
+ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ goto store_next;
+ case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ goto store_next;
+ case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ goto store_next;
+ case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ goto store_next;
+ case POWERPC_EXCP_VPU: /* Vector unavailable exception */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ goto store_current;
+ case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
+ LOG_EXCP("PIT exception\n");
+ goto store_next;
+ case POWERPC_EXCP_IO: /* IO error exception */
+ /* XXX: TODO */
+ cpu_abort(env, "601 IO error exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_RUNM: /* Run mode exception */
+ /* XXX: TODO */
+ cpu_abort(env, "601 run mode exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_EMUL: /* Emulation trap exception */
+ /* XXX: TODO */
+ cpu_abort(env, "602 emulation trap exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
+ if (lpes1 == 0) /* XXX: check this */
+ new_msr |= (target_ulong)MSR_HVB;
+ switch (excp_model) {
+ case POWERPC_EXCP_602:
+ case POWERPC_EXCP_603:
+ case POWERPC_EXCP_603E:
+ case POWERPC_EXCP_G2:
+ goto tlb_miss_tgpr;
+ case POWERPC_EXCP_7x5:
+ goto tlb_miss;
+ case POWERPC_EXCP_74xx:
+ goto tlb_miss_74xx;
+ default:
+ cpu_abort(env, "Invalid instruction TLB miss exception\n");
+ break;
+ }
+ break;
+ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
+ if (lpes1 == 0) /* XXX: check this */
+ new_msr |= (target_ulong)MSR_HVB;
+ switch (excp_model) {
+ case POWERPC_EXCP_602:
+ case POWERPC_EXCP_603:
+ case POWERPC_EXCP_603E:
+ case POWERPC_EXCP_G2:
+ goto tlb_miss_tgpr;
+ case POWERPC_EXCP_7x5:
+ goto tlb_miss;
+ case POWERPC_EXCP_74xx:
+ goto tlb_miss_74xx;
+ default:
+ cpu_abort(env, "Invalid data load TLB miss exception\n");
+ break;
+ }
+ break;
+ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
+ if (lpes1 == 0) /* XXX: check this */
+ new_msr |= (target_ulong)MSR_HVB;
+ switch (excp_model) {
+ case POWERPC_EXCP_602:
+ case POWERPC_EXCP_603:
+ case POWERPC_EXCP_603E:
+ case POWERPC_EXCP_G2:
+ tlb_miss_tgpr:
+ /* Swap temporary saved registers with GPRs */
+ if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) {
+ new_msr |= (target_ulong)1 << MSR_TGPR;
+ hreg_swap_gpr_tgpr(env);
+ }
+ goto tlb_miss;
+ case POWERPC_EXCP_7x5:
+ tlb_miss:
+#if defined (DEBUG_SOFTWARE_TLB)
+ if (qemu_log_enabled()) {
+ const char *es;
+ target_ulong *miss, *cmp;
+ int en;
+ if (excp == POWERPC_EXCP_IFTLB) {
+ es = "I";
+ en = 'I';
+ miss = &env->spr[SPR_IMISS];
+ cmp = &env->spr[SPR_ICMP];
+ } else {
+ if (excp == POWERPC_EXCP_DLTLB)
+ es = "DL";
+ else
+ es = "DS";
+ en = 'D';
+ miss = &env->spr[SPR_DMISS];
+ cmp = &env->spr[SPR_DCMP];
+ }
+ qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
+ TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
+ TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
+ env->spr[SPR_HASH1], env->spr[SPR_HASH2],
+ env->error_code);
+ }
+#endif
+ msr |= env->crf[0] << 28;
+ msr |= env->error_code; /* key, D/I, S/L bits */
+ /* Set way using a LRU mechanism */
+ msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17;
+ break;
+ case POWERPC_EXCP_74xx:
+ tlb_miss_74xx:
+#if defined (DEBUG_SOFTWARE_TLB)
+ if (qemu_log_enabled()) {
+ const char *es;
+ target_ulong *miss, *cmp;
+ int en;
+ if (excp == POWERPC_EXCP_IFTLB) {
+ es = "I";
+ en = 'I';
+ miss = &env->spr[SPR_TLBMISS];
+ cmp = &env->spr[SPR_PTEHI];
+ } else {
+ if (excp == POWERPC_EXCP_DLTLB)
+ es = "DL";
+ else
+ es = "DS";
+ en = 'D';
+ miss = &env->spr[SPR_TLBMISS];
+ cmp = &env->spr[SPR_PTEHI];
+ }
+ qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
+ TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
+ env->error_code);
+ }
+#endif
+ msr |= env->error_code; /* key bit */
+ break;
+ default:
+ cpu_abort(env, "Invalid data store TLB miss exception\n");
+ break;
+ }
+ goto store_next;
+ case POWERPC_EXCP_FPA: /* Floating-point assist exception */
+ /* XXX: TODO */
+ cpu_abort(env, "Floating point assist exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_DABR: /* Data address breakpoint */
+ /* XXX: TODO */
+ cpu_abort(env, "DABR exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
+ /* XXX: TODO */
+ cpu_abort(env, "IABR exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_SMI: /* System management interrupt */
+ /* XXX: TODO */
+ cpu_abort(env, "SMI exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_THERM: /* Thermal interrupt */
+ /* XXX: TODO */
+ cpu_abort(env, "Thermal management exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
+ if (lpes1 == 0)
+ new_msr |= (target_ulong)MSR_HVB;
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Performance counter exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_VPUA: /* Vector assist exception */
+ /* XXX: TODO */
+ cpu_abort(env, "VPU assist exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_SOFTP: /* Soft patch exception */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 soft-patch exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_MAINT: /* Maintenance exception */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 maintenance exception is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */
+ /* XXX: TODO */
+ cpu_abort(env, "Maskable external exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */
+ /* XXX: TODO */
+ cpu_abort(env, "Non maskable external exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ default:
+ excp_invalid:
+ cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp);
+ break;
+ store_current:
+ /* save current instruction location */
+ env->spr[srr0] = env->nip - 4;
+ break;
+ store_next:
+ /* save next instruction location */
+ env->spr[srr0] = env->nip;
+ break;
+ }
+ /* Save MSR */
+ env->spr[srr1] = msr;
+ /* If any alternate SRR register are defined, duplicate saved values */
+ if (asrr0 != -1)
+ env->spr[asrr0] = env->spr[srr0];
+ if (asrr1 != -1)
+ env->spr[asrr1] = env->spr[srr1];
+ /* If we disactivated any translation, flush TLBs */
+ if (new_msr & ((1 << MSR_IR) | (1 << MSR_DR)))
+ tlb_flush(env, 1);
+
+ if (msr_ile) {
+ new_msr |= (target_ulong)1 << MSR_LE;
+ }
+
+ /* Jump to handler */
+ vector = env->excp_vectors[excp];
+ if (vector == (target_ulong)-1ULL) {
+ cpu_abort(env, "Raised an exception without defined vector %d\n",
+ excp);
+ }
+ vector |= env->excp_prefix;
+#if defined(TARGET_PPC64)
+ if (excp_model == POWERPC_EXCP_BOOKE) {
+ if (!msr_icm) {
+ vector = (uint32_t)vector;
+ } else {
+ new_msr |= (target_ulong)1 << MSR_CM;
+ }
+ } else {
+ if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) {
+ vector = (uint32_t)vector;
+ } else {
+ new_msr |= (target_ulong)1 << MSR_SF;
+ }
+ }
+#endif
+ /* XXX: we don't use hreg_store_msr here as already have treated
+ * any special case that could occur. Just store MSR and update hflags
+ */
+ env->msr = new_msr & env->msr_mask;
+ hreg_compute_hflags(env);
+ env->nip = vector;
+ /* Reset exception state */
+ env->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+
+ if (env->mmu_model == POWERPC_MMU_BOOKE) {
+ /* XXX: The BookE changes address space when switching modes,
+ we should probably implement that as different MMU indexes,
+ but for the moment we do it the slow way and flush all. */
+ tlb_flush(env, 1);
+ }
+}
+
+void do_interrupt (CPUState *env)
+{
+ powerpc_excp(env, env->excp_model, env->exception_index);
+}
+
+void ppc_hw_interrupt (CPUPPCState *env)
+{
+ int hdice;
+
+#if 0
+ qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n",
+ __func__, env, env->pending_interrupts,
+ env->interrupt_request, (int)msr_me, (int)msr_ee);
+#endif
+ /* External reset */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET);
+ return;
+ }
+ /* Machine check exception */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK);
+ return;
+ }
+#if 0 /* TODO */
+ /* External debug exception */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG);
+ return;
+ }
+#endif
+ if (0) {
+ /* XXX: find a suitable condition to enable the hypervisor mode */
+ hdice = env->spr[SPR_LPCR] & 1;
+ } else {
+ hdice = 0;
+ }
+ if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) {
+ /* Hypervisor decrementer exception */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR);
+ return;
+ }
+ }
+ if (msr_ce != 0) {
+ /* External critical interrupt */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) {
+ /* Taking a critical external interrupt does not clear the external
+ * critical interrupt status
+ */
+#if 0
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT);
+#endif
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL);
+ return;
+ }
+ }
+ if (msr_ee != 0) {
+ /* Watchdog timer on embedded PowerPC */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT);
+ return;
+ }
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI);
+ return;
+ }
+ /* Fixed interval timer on embedded PowerPC */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT);
+ return;
+ }
+ /* Programmable interval timer on embedded PowerPC */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT);
+ return;
+ }
+ /* Decrementer exception */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR);
+ return;
+ }
+ /* External interrupt */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
+ /* Taking an external interrupt does not clear the external
+ * interrupt status
+ */
+#if 0
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT);
+#endif
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL);
+ return;
+ }
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI);
+ return;
+ }
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM);
+ return;
+ }
+ /* Thermal interrupt */
+ if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) {
+ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM);
+ powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM);
+ return;
+ }
+ }
+}
+#endif /* !CONFIG_USER_ONLY */
+
+void cpu_dump_rfi (target_ulong RA, target_ulong msr)
+{
+ qemu_log("Return from exception at " TARGET_FMT_lx " with flags "
+ TARGET_FMT_lx "\n", RA, msr);
+}
+
+void cpu_reset(CPUPPCState *env)
+{
+ target_ulong msr;
+
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ msr = (target_ulong)0;
+ if (0) {
+ /* XXX: find a suitable condition to enable the hypervisor mode */
+ msr |= (target_ulong)MSR_HVB;
+ }
+ msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */
+ msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */
+ msr |= (target_ulong)1 << MSR_EP;
+#if defined (DO_SINGLE_STEP) && 0
+ /* Single step trace mode */
+ msr |= (target_ulong)1 << MSR_SE;
+ msr |= (target_ulong)1 << MSR_BE;
+#endif
+#if defined(CONFIG_USER_ONLY)
+ msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */
+ msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */
+ msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */
+ msr |= (target_ulong)1 << MSR_PR;
+#else
+ env->excp_prefix = env->hreset_excp_prefix;
+ env->nip = env->hreset_vector | env->excp_prefix;
+ if (env->mmu_model != POWERPC_MMU_REAL)
+ ppc_tlb_invalidate_all(env);
+#endif
+ env->msr = msr & env->msr_mask;
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64)
+ env->msr |= (1ULL << MSR_SF);
+#endif
+ hreg_compute_hflags(env);
+ env->reserve_addr = (target_ulong)-1ULL;
+ /* Be sure no exception or interrupt is pending */
+ env->pending_interrupts = 0;
+ env->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+ /* Flush all TLBs */
+ tlb_flush(env, 1);
+}
+
+CPUPPCState *cpu_ppc_init (const char *cpu_model)
+{
+ CPUPPCState *env;
+ const ppc_def_t *def;
+
+ def = cpu_ppc_find_by_name(cpu_model);
+ if (!def)
+ return NULL;
+
+ env = qemu_mallocz(sizeof(CPUPPCState));
+ cpu_exec_init(env);
+ ppc_translate_init();
+ env->cpu_model_str = cpu_model;
+ cpu_ppc_register_internal(env, def);
+
+ qemu_init_vcpu(env);
+
+ return env;
+}
+
+void cpu_ppc_close (CPUPPCState *env)
+{
+ /* Should also remove all opcode tables... */
+ qemu_free(env);
+}
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
new file mode 100644
index 0000000..2bf9283
--- /dev/null
+++ b/target-ppc/helper.h
@@ -0,0 +1,404 @@
+#include "def-helper.h"
+
+DEF_HELPER_2(raise_exception_err, void, i32, i32)
+DEF_HELPER_1(raise_exception, void, i32)
+DEF_HELPER_3(tw, void, tl, tl, i32)
+#if defined(TARGET_PPC64)
+DEF_HELPER_3(td, void, tl, tl, i32)
+#endif
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_1(store_msr, void, tl)
+DEF_HELPER_0(rfi, void)
+DEF_HELPER_0(rfsvc, void)
+DEF_HELPER_0(40x_rfci, void)
+DEF_HELPER_0(rfci, void)
+DEF_HELPER_0(rfdi, void)
+DEF_HELPER_0(rfmci, void)
+#if defined(TARGET_PPC64)
+DEF_HELPER_0(rfid, void)
+DEF_HELPER_0(hrfid, void)
+#endif
+#endif
+
+DEF_HELPER_2(lmw, void, tl, i32)
+DEF_HELPER_2(stmw, void, tl, i32)
+DEF_HELPER_3(lsw, void, tl, i32, i32)
+DEF_HELPER_4(lswx, void, tl, i32, i32, i32)
+DEF_HELPER_3(stsw, void, tl, i32, i32)
+DEF_HELPER_1(dcbz, void, tl)
+DEF_HELPER_1(dcbz_970, void, tl)
+DEF_HELPER_1(icbi, void, tl)
+DEF_HELPER_4(lscbx, tl, tl, i32, i32, i32)
+
+#if defined(TARGET_PPC64)
+DEF_HELPER_FLAGS_2(mulhd, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(mulhdu, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_2(mulldo, i64, i64, i64)
+#endif
+
+DEF_HELPER_FLAGS_1(cntlzw, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_2(sraw, tl, tl, tl)
+#if defined(TARGET_PPC64)
+DEF_HELPER_FLAGS_1(cntlzd, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_FLAGS_1(popcntb_64, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+DEF_HELPER_2(srad, tl, tl, tl)
+#endif
+
+DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_CONST | TCG_CALL_PURE, i32, i32)
+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)
+DEF_HELPER_1(fpscr_setbit, void, i32)
+DEF_HELPER_1(float64_to_float32, i32, i64)
+DEF_HELPER_1(float32_to_float64, i64, i32)
+
+DEF_HELPER_3(fcmpo, void, i64, i64, i32)
+DEF_HELPER_3(fcmpu, void, i64, i64, i32)
+
+DEF_HELPER_1(fctiw, i64, i64)
+DEF_HELPER_1(fctiwz, i64, i64)
+#if defined(TARGET_PPC64)
+DEF_HELPER_1(fcfid, i64, i64)
+DEF_HELPER_1(fctid, i64, i64)
+DEF_HELPER_1(fctidz, i64, i64)
+#endif
+DEF_HELPER_1(frsp, i64, i64)
+DEF_HELPER_1(frin, i64, i64)
+DEF_HELPER_1(friz, i64, i64)
+DEF_HELPER_1(frip, i64, i64)
+DEF_HELPER_1(frim, i64, i64)
+
+DEF_HELPER_2(fadd, i64, i64, i64)
+DEF_HELPER_2(fsub, i64, i64, i64)
+DEF_HELPER_2(fmul, i64, i64, i64)
+DEF_HELPER_2(fdiv, i64, i64, i64)
+DEF_HELPER_3(fmadd, i64, i64, i64, i64)
+DEF_HELPER_3(fmsub, i64, i64, i64, i64)
+DEF_HELPER_3(fnmadd, i64, i64, i64, i64)
+DEF_HELPER_3(fnmsub, i64, i64, i64, i64)
+DEF_HELPER_1(fabs, i64, i64)
+DEF_HELPER_1(fnabs, i64, i64)
+DEF_HELPER_1(fneg, i64, i64)
+DEF_HELPER_1(fsqrt, i64, i64)
+DEF_HELPER_1(fre, i64, i64)
+DEF_HELPER_1(fres, i64, i64)
+DEF_HELPER_1(frsqrte, i64, i64)
+DEF_HELPER_3(fsel, i64, i64, i64, i64)
+
+#define dh_alias_avr ptr
+#define dh_ctype_avr ppc_avr_t *
+#define dh_is_signed_avr dh_is_signed_ptr
+
+DEF_HELPER_3(vaddubm, void, avr, avr, avr)
+DEF_HELPER_3(vadduhm, void, avr, avr, avr)
+DEF_HELPER_3(vadduwm, void, avr, avr, avr)
+DEF_HELPER_3(vsububm, void, avr, avr, avr)
+DEF_HELPER_3(vsubuhm, void, avr, avr, avr)
+DEF_HELPER_3(vsubuwm, void, avr, avr, avr)
+DEF_HELPER_3(vavgub, void, avr, avr, avr)
+DEF_HELPER_3(vavguh, void, avr, avr, avr)
+DEF_HELPER_3(vavguw, void, avr, avr, avr)
+DEF_HELPER_3(vavgsb, void, avr, avr, avr)
+DEF_HELPER_3(vavgsh, void, avr, avr, avr)
+DEF_HELPER_3(vavgsw, void, avr, avr, avr)
+DEF_HELPER_3(vminsb, void, avr, avr, avr)
+DEF_HELPER_3(vminsh, void, avr, avr, avr)
+DEF_HELPER_3(vminsw, void, avr, avr, avr)
+DEF_HELPER_3(vmaxsb, void, avr, avr, avr)
+DEF_HELPER_3(vmaxsh, void, avr, avr, avr)
+DEF_HELPER_3(vmaxsw, void, avr, avr, avr)
+DEF_HELPER_3(vminub, void, avr, avr, avr)
+DEF_HELPER_3(vminuh, void, avr, avr, avr)
+DEF_HELPER_3(vminuw, void, avr, avr, avr)
+DEF_HELPER_3(vmaxub, void, avr, avr, avr)
+DEF_HELPER_3(vmaxuh, void, avr, avr, avr)
+DEF_HELPER_3(vmaxuw, void, avr, avr, avr)
+DEF_HELPER_3(vcmpequb, void, avr, avr, avr)
+DEF_HELPER_3(vcmpequh, void, avr, avr, avr)
+DEF_HELPER_3(vcmpequw, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtub, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtuh, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtuw, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtsb, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtsh, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtsw, void, avr, avr, avr)
+DEF_HELPER_3(vcmpeqfp, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgefp, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtfp, void, avr, avr, avr)
+DEF_HELPER_3(vcmpbfp, void, avr, avr, avr)
+DEF_HELPER_3(vcmpequb_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpequh_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpequw_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtub_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtuh_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtuw_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtsb_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtsh_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtsw_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpeqfp_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgefp_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpgtfp_dot, void, avr, avr, avr)
+DEF_HELPER_3(vcmpbfp_dot, void, avr, avr, avr)
+DEF_HELPER_3(vmrglb, void, avr, avr, avr)
+DEF_HELPER_3(vmrglh, void, avr, avr, avr)
+DEF_HELPER_3(vmrglw, void, avr, avr, avr)
+DEF_HELPER_3(vmrghb, void, avr, avr, avr)
+DEF_HELPER_3(vmrghh, void, avr, avr, avr)
+DEF_HELPER_3(vmrghw, void, avr, avr, avr)
+DEF_HELPER_3(vmulesb, void, avr, avr, avr)
+DEF_HELPER_3(vmulesh, void, avr, avr, avr)
+DEF_HELPER_3(vmuleub, void, avr, avr, avr)
+DEF_HELPER_3(vmuleuh, void, avr, avr, avr)
+DEF_HELPER_3(vmulosb, void, avr, avr, avr)
+DEF_HELPER_3(vmulosh, void, avr, avr, avr)
+DEF_HELPER_3(vmuloub, void, avr, avr, avr)
+DEF_HELPER_3(vmulouh, void, avr, avr, avr)
+DEF_HELPER_3(vsrab, void, avr, avr, avr)
+DEF_HELPER_3(vsrah, void, avr, avr, avr)
+DEF_HELPER_3(vsraw, void, avr, avr, avr)
+DEF_HELPER_3(vsrb, void, avr, avr, avr)
+DEF_HELPER_3(vsrh, void, avr, avr, avr)
+DEF_HELPER_3(vsrw, void, avr, avr, avr)
+DEF_HELPER_3(vslb, void, avr, avr, avr)
+DEF_HELPER_3(vslh, void, avr, avr, avr)
+DEF_HELPER_3(vslw, void, avr, avr, avr)
+DEF_HELPER_3(vslo, void, avr, avr, avr)
+DEF_HELPER_3(vsro, void, avr, avr, avr)
+DEF_HELPER_3(vaddcuw, void, avr, avr, avr)
+DEF_HELPER_3(vsubcuw, void, avr, avr, avr)
+DEF_HELPER_2(lvsl, void, avr, tl);
+DEF_HELPER_2(lvsr, void, avr, tl);
+DEF_HELPER_3(vaddsbs, void, avr, avr, avr)
+DEF_HELPER_3(vaddshs, void, avr, avr, avr)
+DEF_HELPER_3(vaddsws, void, avr, avr, avr)
+DEF_HELPER_3(vsubsbs, void, avr, avr, avr)
+DEF_HELPER_3(vsubshs, void, avr, avr, avr)
+DEF_HELPER_3(vsubsws, void, avr, avr, avr)
+DEF_HELPER_3(vaddubs, void, avr, avr, avr)
+DEF_HELPER_3(vadduhs, void, avr, avr, avr)
+DEF_HELPER_3(vadduws, void, avr, avr, avr)
+DEF_HELPER_3(vsububs, void, avr, avr, avr)
+DEF_HELPER_3(vsubuhs, void, avr, avr, avr)
+DEF_HELPER_3(vsubuws, void, avr, avr, avr)
+DEF_HELPER_3(vrlb, void, avr, avr, avr)
+DEF_HELPER_3(vrlh, void, avr, avr, avr)
+DEF_HELPER_3(vrlw, void, avr, avr, avr)
+DEF_HELPER_3(vsl, void, avr, avr, avr)
+DEF_HELPER_3(vsr, void, avr, avr, avr)
+DEF_HELPER_4(vsldoi, void, avr, avr, avr, i32)
+DEF_HELPER_2(vspltisb, void, avr, i32)
+DEF_HELPER_2(vspltish, void, avr, i32)
+DEF_HELPER_2(vspltisw, void, avr, i32)
+DEF_HELPER_3(vspltb, void, avr, avr, i32)
+DEF_HELPER_3(vsplth, void, avr, avr, i32)
+DEF_HELPER_3(vspltw, void, avr, avr, i32)
+DEF_HELPER_2(vupkhpx, void, avr, avr)
+DEF_HELPER_2(vupklpx, void, avr, avr)
+DEF_HELPER_2(vupkhsb, void, avr, avr)
+DEF_HELPER_2(vupkhsh, void, avr, avr)
+DEF_HELPER_2(vupklsb, void, avr, avr)
+DEF_HELPER_2(vupklsh, void, avr, avr)
+DEF_HELPER_4(vmsumubm, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmsummbm, void, avr, avr, avr, avr)
+DEF_HELPER_4(vsel, void, avr, avr, avr, avr)
+DEF_HELPER_4(vperm, void, avr, avr, avr, avr)
+DEF_HELPER_3(vpkshss, void, avr, avr, avr)
+DEF_HELPER_3(vpkshus, void, avr, avr, avr)
+DEF_HELPER_3(vpkswss, void, avr, avr, avr)
+DEF_HELPER_3(vpkswus, void, avr, avr, avr)
+DEF_HELPER_3(vpkuhus, void, avr, avr, avr)
+DEF_HELPER_3(vpkuwus, void, avr, avr, avr)
+DEF_HELPER_3(vpkuhum, void, avr, avr, avr)
+DEF_HELPER_3(vpkuwum, void, avr, avr, avr)
+DEF_HELPER_3(vpkpx, void, avr, avr, avr)
+DEF_HELPER_4(vmhaddshs, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmhraddshs, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmsumuhm, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmsumuhs, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmsumshm, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmsumshs, void, avr, avr, avr, avr)
+DEF_HELPER_4(vmladduhm, void, avr, avr, avr, avr)
+DEF_HELPER_1(mtvscr, void, avr);
+DEF_HELPER_2(lvebx, void, avr, tl)
+DEF_HELPER_2(lvehx, void, avr, tl)
+DEF_HELPER_2(lvewx, void, avr, tl)
+DEF_HELPER_2(stvebx, void, avr, tl)
+DEF_HELPER_2(stvehx, void, avr, tl)
+DEF_HELPER_2(stvewx, void, avr, tl)
+DEF_HELPER_3(vsumsws, void, avr, avr, avr)
+DEF_HELPER_3(vsum2sws, void, avr, avr, avr)
+DEF_HELPER_3(vsum4sbs, void, avr, avr, avr)
+DEF_HELPER_3(vsum4shs, void, avr, avr, avr)
+DEF_HELPER_3(vsum4ubs, void, avr, avr, avr)
+DEF_HELPER_3(vaddfp, void, avr, avr, avr)
+DEF_HELPER_3(vsubfp, void, avr, avr, avr)
+DEF_HELPER_3(vmaxfp, void, avr, avr, avr)
+DEF_HELPER_3(vminfp, void, avr, avr, avr)
+DEF_HELPER_2(vrefp, void, avr, avr)
+DEF_HELPER_2(vrsqrtefp, void, avr, avr)
+DEF_HELPER_4(vmaddfp, void, avr, avr, avr, avr)
+DEF_HELPER_4(vnmsubfp, void, avr, avr, avr, avr)
+DEF_HELPER_2(vexptefp, void, avr, avr)
+DEF_HELPER_2(vlogefp, void, avr, avr)
+DEF_HELPER_2(vrfim, void, avr, avr)
+DEF_HELPER_2(vrfin, void, avr, avr)
+DEF_HELPER_2(vrfip, void, avr, avr)
+DEF_HELPER_2(vrfiz, void, avr, avr)
+DEF_HELPER_3(vcfux, void, avr, avr, i32)
+DEF_HELPER_3(vcfsx, void, avr, avr, i32)
+DEF_HELPER_3(vctuxs, void, avr, avr, i32)
+DEF_HELPER_3(vctsxs, void, avr, avr, i32)
+
+DEF_HELPER_1(efscfsi, i32, i32)
+DEF_HELPER_1(efscfui, i32, i32)
+DEF_HELPER_1(efscfuf, i32, i32)
+DEF_HELPER_1(efscfsf, i32, i32)
+DEF_HELPER_1(efsctsi, i32, i32)
+DEF_HELPER_1(efsctui, i32, i32)
+DEF_HELPER_1(efsctsiz, i32, i32)
+DEF_HELPER_1(efsctuiz, i32, i32)
+DEF_HELPER_1(efsctsf, i32, i32)
+DEF_HELPER_1(efsctuf, i32, i32)
+DEF_HELPER_1(evfscfsi, i64, i64)
+DEF_HELPER_1(evfscfui, i64, i64)
+DEF_HELPER_1(evfscfuf, i64, i64)
+DEF_HELPER_1(evfscfsf, i64, i64)
+DEF_HELPER_1(evfsctsi, i64, i64)
+DEF_HELPER_1(evfsctui, i64, i64)
+DEF_HELPER_1(evfsctsiz, i64, i64)
+DEF_HELPER_1(evfsctuiz, i64, i64)
+DEF_HELPER_1(evfsctsf, i64, i64)
+DEF_HELPER_1(evfsctuf, i64, i64)
+DEF_HELPER_2(efsadd, i32, i32, i32)
+DEF_HELPER_2(efssub, i32, i32, i32)
+DEF_HELPER_2(efsmul, i32, i32, i32)
+DEF_HELPER_2(efsdiv, i32, i32, i32)
+DEF_HELPER_2(evfsadd, i64, i64, i64)
+DEF_HELPER_2(evfssub, i64, i64, i64)
+DEF_HELPER_2(evfsmul, i64, i64, i64)
+DEF_HELPER_2(evfsdiv, i64, i64, i64)
+DEF_HELPER_2(efststlt, i32, i32, i32)
+DEF_HELPER_2(efststgt, i32, i32, i32)
+DEF_HELPER_2(efststeq, i32, i32, i32)
+DEF_HELPER_2(efscmplt, i32, i32, i32)
+DEF_HELPER_2(efscmpgt, i32, i32, i32)
+DEF_HELPER_2(efscmpeq, i32, i32, i32)
+DEF_HELPER_2(evfststlt, i32, i64, i64)
+DEF_HELPER_2(evfststgt, i32, i64, i64)
+DEF_HELPER_2(evfststeq, i32, i64, i64)
+DEF_HELPER_2(evfscmplt, i32, i64, i64)
+DEF_HELPER_2(evfscmpgt, i32, i64, i64)
+DEF_HELPER_2(evfscmpeq, i32, i64, i64)
+DEF_HELPER_1(efdcfsi, i64, i32)
+DEF_HELPER_1(efdcfsid, i64, i64)
+DEF_HELPER_1(efdcfui, i64, i32)
+DEF_HELPER_1(efdcfuid, i64, i64)
+DEF_HELPER_1(efdctsi, i32, i64)
+DEF_HELPER_1(efdctui, i32, i64)
+DEF_HELPER_1(efdctsiz, i32, i64)
+DEF_HELPER_1(efdctsidz, i64, i64)
+DEF_HELPER_1(efdctuiz, i32, i64)
+DEF_HELPER_1(efdctuidz, i64, i64)
+DEF_HELPER_1(efdcfsf, i64, i32)
+DEF_HELPER_1(efdcfuf, i64, i32)
+DEF_HELPER_1(efdctsf, i32, i64)
+DEF_HELPER_1(efdctuf, i32, i64)
+DEF_HELPER_1(efscfd, i32, i64)
+DEF_HELPER_1(efdcfs, i64, i32)
+DEF_HELPER_2(efdadd, i64, i64, i64)
+DEF_HELPER_2(efdsub, i64, i64, i64)
+DEF_HELPER_2(efdmul, i64, i64, i64)
+DEF_HELPER_2(efddiv, i64, i64, i64)
+DEF_HELPER_2(efdtstlt, i32, i64, i64)
+DEF_HELPER_2(efdtstgt, i32, i64, i64)
+DEF_HELPER_2(efdtsteq, i32, i64, i64)
+DEF_HELPER_2(efdcmplt, i32, i64, i64)
+DEF_HELPER_2(efdcmpgt, i32, i64, i64)
+DEF_HELPER_2(efdcmpeq, i32, i64, i64)
+
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_1(4xx_tlbre_hi, tl, tl)
+DEF_HELPER_1(4xx_tlbre_lo, tl, tl)
+DEF_HELPER_2(4xx_tlbwe_hi, void, tl, tl)
+DEF_HELPER_2(4xx_tlbwe_lo, void, tl, tl)
+DEF_HELPER_1(4xx_tlbsx, tl, tl)
+DEF_HELPER_2(440_tlbre, tl, i32, tl)
+DEF_HELPER_3(440_tlbwe, void, i32, tl, tl)
+DEF_HELPER_1(440_tlbsx, tl, tl)
+DEF_HELPER_1(6xx_tlbd, void, tl)
+DEF_HELPER_1(6xx_tlbi, void, tl)
+DEF_HELPER_1(74xx_tlbd, void, tl)
+DEF_HELPER_1(74xx_tlbi, void, tl)
+DEF_HELPER_FLAGS_0(tlbia, TCG_CALL_CONST, void)
+DEF_HELPER_FLAGS_1(tlbie, TCG_CALL_CONST, void, tl)
+#if defined(TARGET_PPC64)
+DEF_HELPER_FLAGS_1(load_slb, TCG_CALL_CONST, tl, tl)
+DEF_HELPER_FLAGS_2(store_slb, TCG_CALL_CONST, void, tl, tl)
+DEF_HELPER_FLAGS_0(slbia, TCG_CALL_CONST, void)
+DEF_HELPER_FLAGS_1(slbie, TCG_CALL_CONST, void, tl)
+#endif
+DEF_HELPER_FLAGS_1(load_sr, TCG_CALL_CONST, tl, tl);
+DEF_HELPER_FLAGS_2(store_sr, TCG_CALL_CONST, void, tl, tl)
+
+DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl)
+#endif
+
+DEF_HELPER_3(dlmzb, tl, tl, tl, i32)
+DEF_HELPER_FLAGS_1(clcs, TCG_CALL_CONST | TCG_CALL_PURE, tl, i32)
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_1(rac, tl, tl)
+#endif
+DEF_HELPER_2(div, tl, tl, tl)
+DEF_HELPER_2(divo, tl, tl, tl)
+DEF_HELPER_2(divs, tl, tl, tl)
+DEF_HELPER_2(divso, tl, tl, tl)
+
+DEF_HELPER_1(load_dcr, tl, tl);
+DEF_HELPER_2(store_dcr, void, tl, tl)
+
+DEF_HELPER_1(load_dump_spr, void, i32)
+DEF_HELPER_1(store_dump_spr, void, i32)
+DEF_HELPER_0(load_tbl, tl)
+DEF_HELPER_0(load_tbu, tl)
+DEF_HELPER_0(load_atbl, tl)
+DEF_HELPER_0(load_atbu, tl)
+DEF_HELPER_0(load_601_rtcl, tl)
+DEF_HELPER_0(load_601_rtcu, tl)
+#if !defined(CONFIG_USER_ONLY)
+#if defined(TARGET_PPC64)
+DEF_HELPER_1(store_asr, void, tl)
+#endif
+DEF_HELPER_1(store_sdr1, void, tl)
+DEF_HELPER_1(store_tbl, void, tl)
+DEF_HELPER_1(store_tbu, void, tl)
+DEF_HELPER_1(store_atbl, void, tl)
+DEF_HELPER_1(store_atbu, void, tl)
+DEF_HELPER_1(store_601_rtcl, void, tl)
+DEF_HELPER_1(store_601_rtcu, void, tl)
+DEF_HELPER_0(load_decr, tl)
+DEF_HELPER_1(store_decr, void, tl)
+DEF_HELPER_1(store_hid0_601, void, tl)
+DEF_HELPER_2(store_403_pbr, void, i32, tl)
+DEF_HELPER_0(load_40x_pit, tl)
+DEF_HELPER_1(store_40x_pit, void, tl)
+DEF_HELPER_1(store_40x_dbcr0, void, tl)
+DEF_HELPER_1(store_40x_sler, void, tl)
+DEF_HELPER_1(store_booke_tcr, void, tl)
+DEF_HELPER_1(store_booke_tsr, void, tl)
+DEF_HELPER_2(store_ibatl, void, i32, tl)
+DEF_HELPER_2(store_ibatu, void, i32, tl)
+DEF_HELPER_2(store_dbatl, void, i32, tl)
+DEF_HELPER_2(store_dbatu, void, i32, tl)
+DEF_HELPER_2(store_601_batl, void, i32, tl)
+DEF_HELPER_2(store_601_batu, void, i32, tl)
+#endif
+
+#include "def-helper.h"
diff --git a/target-ppc/helper_regs.h b/target-ppc/helper_regs.h
new file mode 100644
index 0000000..3c98850
--- /dev/null
+++ b/target-ppc/helper_regs.h
@@ -0,0 +1,111 @@
+/*
+ * PowerPC emulation special registers manipulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+#if !defined(__HELPER_REGS_H__)
+#define __HELPER_REGS_H__
+
+/* Swap temporary saved registers with GPRs */
+static inline void hreg_swap_gpr_tgpr(CPUPPCState *env)
+{
+ target_ulong tmp;
+
+ tmp = env->gpr[0];
+ env->gpr[0] = env->tgpr[0];
+ env->tgpr[0] = tmp;
+ tmp = env->gpr[1];
+ env->gpr[1] = env->tgpr[1];
+ env->tgpr[1] = tmp;
+ tmp = env->gpr[2];
+ env->gpr[2] = env->tgpr[2];
+ env->tgpr[2] = tmp;
+ tmp = env->gpr[3];
+ env->gpr[3] = env->tgpr[3];
+ env->tgpr[3] = tmp;
+}
+
+static inline void hreg_compute_mem_idx(CPUPPCState *env)
+{
+ /* Precompute MMU index */
+ if (msr_pr == 0 && msr_hv != 0) {
+ env->mmu_idx = 2;
+ } else {
+ env->mmu_idx = 1 - msr_pr;
+ }
+}
+
+static inline void hreg_compute_hflags(CPUPPCState *env)
+{
+ target_ulong hflags_mask;
+
+ /* We 'forget' FE0 & FE1: we'll never generate imprecise exceptions */
+ hflags_mask = (1 << MSR_VR) | (1 << MSR_AP) | (1 << MSR_SA) |
+ (1 << MSR_PR) | (1 << MSR_FP) | (1 << MSR_SE) | (1 << MSR_BE) |
+ (1 << MSR_LE);
+ hflags_mask |= (1ULL << MSR_CM) | (1ULL << MSR_SF) | MSR_HVB;
+ hreg_compute_mem_idx(env);
+ env->hflags = env->msr & hflags_mask;
+ /* Merge with hflags coming from other registers */
+ env->hflags |= env->hflags_nmsr;
+}
+
+static inline int hreg_store_msr(CPUPPCState *env, target_ulong value,
+ int alter_hv)
+{
+ int excp;
+
+ excp = 0;
+ value &= env->msr_mask;
+#if !defined (CONFIG_USER_ONLY)
+ if (!alter_hv) {
+ /* mtmsr cannot alter the hypervisor state */
+ value &= ~MSR_HVB;
+ value |= env->msr & MSR_HVB;
+ }
+ if (((value >> MSR_IR) & 1) != msr_ir ||
+ ((value >> MSR_DR) & 1) != msr_dr) {
+ /* Flush all tlb when changing translation mode */
+ tlb_flush(env, 1);
+ excp = POWERPC_EXCP_NONE;
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ }
+ if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
+ ((value ^ env->msr) & (1 << MSR_TGPR)))) {
+ /* Swap temporary saved registers with GPRs */
+ hreg_swap_gpr_tgpr(env);
+ }
+ if (unlikely((value >> MSR_EP) & 1) != msr_ep) {
+ /* Change the exception prefix on PowerPC 601 */
+ env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
+ }
+#endif
+ env->msr = value;
+ hreg_compute_hflags(env);
+#if !defined (CONFIG_USER_ONLY)
+ if (unlikely(msr_pow == 1)) {
+ if ((*env->check_pow)(env)) {
+ env->halted = 1;
+ excp = EXCP_HALTED;
+ }
+ }
+#endif
+
+ return excp;
+}
+
+#endif /* !defined(__HELPER_REGS_H__) */
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
new file mode 100644
index 0000000..710eca1
--- /dev/null
+++ b/target-ppc/kvm.c
@@ -0,0 +1,406 @@
+/*
+ * PowerPC implementation of KVM hooks
+ *
+ * Copyright IBM Corp. 2007
+ *
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "kvm_ppc.h"
+#include "cpu.h"
+#include "device_tree.h"
+
+//#define DEBUG_KVM
+
+#ifdef DEBUG_KVM
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+static int cap_interrupt_unset = false;
+static int cap_interrupt_level = false;
+
+/* 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
+ * takes but ignores it, goes to sleep and never gets notified that there's
+ * still an interrupt pending.
+ *
+ * As a quick workaround, let's just wake up again 20 ms after we injected
+ * an interrupt. That way we can assure that we're always reinjecting
+ * interrupts in case the guest swallowed them.
+ */
+static QEMUTimer *idle_timer;
+
+static void kvm_kick_env(void *env)
+{
+ qemu_cpu_kick(env);
+}
+
+int kvm_arch_init(KVMState *s)
+{
+#ifdef KVM_CAP_PPC_UNSET_IRQ
+ cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ);
+#endif
+#ifdef KVM_CAP_PPC_IRQ_LEVEL
+ cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL);
+#endif
+
+ if (!cap_interrupt_level) {
+ fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the "
+ "VM to stall at times!\n");
+ }
+
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *cenv)
+{
+ int ret = 0;
+ struct kvm_sregs sregs;
+
+ sregs.pvr = cenv->spr[SPR_PVR];
+ ret = kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs);
+
+ idle_timer = qemu_new_timer(vm_clock, kvm_kick_env, cenv);
+
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUState *env)
+{
+}
+
+int kvm_arch_put_registers(CPUState *env, int level)
+{
+ struct kvm_regs regs;
+ int ret;
+ int i;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ regs.ctr = env->ctr;
+ regs.lr = env->lr;
+ regs.xer = env->xer;
+ regs.msr = env->msr;
+ regs.pc = env->nip;
+
+ regs.srr0 = env->spr[SPR_SRR0];
+ regs.srr1 = env->spr[SPR_SRR1];
+
+ regs.sprg0 = env->spr[SPR_SPRG0];
+ regs.sprg1 = env->spr[SPR_SPRG1];
+ regs.sprg2 = env->spr[SPR_SPRG2];
+ regs.sprg3 = env->spr[SPR_SPRG3];
+ regs.sprg4 = env->spr[SPR_SPRG4];
+ regs.sprg5 = env->spr[SPR_SPRG5];
+ regs.sprg6 = env->spr[SPR_SPRG6];
+ regs.sprg7 = env->spr[SPR_SPRG7];
+
+ for (i = 0;i < 32; i++)
+ regs.gpr[i] = env->gpr[i];
+
+ ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *env)
+{
+ struct kvm_regs regs;
+ struct kvm_sregs sregs;
+ int i, ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0)
+ return ret;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs);
+ if (ret < 0)
+ return ret;
+
+ env->ctr = regs.ctr;
+ env->lr = regs.lr;
+ env->xer = regs.xer;
+ env->msr = regs.msr;
+ env->nip = regs.pc;
+
+ env->spr[SPR_SRR0] = regs.srr0;
+ env->spr[SPR_SRR1] = regs.srr1;
+
+ env->spr[SPR_SPRG0] = regs.sprg0;
+ env->spr[SPR_SPRG1] = regs.sprg1;
+ env->spr[SPR_SPRG2] = regs.sprg2;
+ env->spr[SPR_SPRG3] = regs.sprg3;
+ env->spr[SPR_SPRG4] = regs.sprg4;
+ env->spr[SPR_SPRG5] = regs.sprg5;
+ env->spr[SPR_SPRG6] = regs.sprg6;
+ env->spr[SPR_SPRG7] = regs.sprg7;
+
+ for (i = 0;i < 32; i++)
+ env->gpr[i] = regs.gpr[i];
+
+#ifdef KVM_CAP_PPC_SEGSTATE
+ if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_SEGSTATE)) {
+ env->sdr1 = sregs.u.s.sdr1;
+
+ /* Sync SLB */
+#ifdef TARGET_PPC64
+ for (i = 0; i < 64; i++) {
+ ppc_store_slb(env, sregs.u.s.ppc64.slb[i].slbe,
+ sregs.u.s.ppc64.slb[i].slbv);
+ }
+#endif
+
+ /* Sync SRs */
+ for (i = 0; i < 16; i++) {
+ env->sr[i] = sregs.u.s.ppc32.sr[i];
+ }
+
+ /* Sync BATs */
+ for (i = 0; i < 8; i++) {
+ env->DBAT[0][i] = sregs.u.s.ppc32.dbat[i] & 0xffffffff;
+ env->DBAT[1][i] = sregs.u.s.ppc32.dbat[i] >> 32;
+ 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;
+}
+
+int kvmppc_set_interrupt(CPUState *env, int irq, int level)
+{
+ unsigned virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET;
+
+ if (irq != PPC_INTERRUPT_EXT) {
+ return 0;
+ }
+
+ if (!kvm_enabled() || !cap_interrupt_unset || !cap_interrupt_level) {
+ return 0;
+ }
+
+ kvm_vcpu_ioctl(env, KVM_INTERRUPT, &virq);
+
+ return 0;
+}
+
+#if defined(TARGET_PPCEMB)
+#define PPC_INPUT_INT PPC40x_INPUT_INT
+#elif defined(TARGET_PPC64)
+#define PPC_INPUT_INT PPC970_INPUT_INT
+#else
+#define PPC_INPUT_INT PPC6xx_INPUT_INT
+#endif
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+{
+ int r;
+ unsigned irq;
+
+ /* PowerPC Qemu tracks the various core input pins (interrupt, critical
+ * interrupt, reset, etc) in PPC-specific env->irq_input_state. */
+ if (!cap_interrupt_level &&
+ run->ready_for_interrupt_injection &&
+ (env->interrupt_request & CPU_INTERRUPT_HARD) &&
+ (env->irq_input_state & (1<<PPC_INPUT_INT)))
+ {
+ /* For now KVM disregards the 'irq' argument. However, in the
+ * future KVM could cache it in-kernel to avoid a heavyweight exit
+ * when reading the UIC.
+ */
+ irq = KVM_INTERRUPT_SET;
+
+ dprintf("injected interrupt %d\n", irq);
+ r = kvm_vcpu_ioctl(env, KVM_INTERRUPT, &irq);
+ if (r < 0)
+ printf("cpu %d fail inject %x\n", env->cpu_index, irq);
+
+ /* Always wake up soon in case the interrupt was level based */
+ qemu_mod_timer(idle_timer, qemu_get_clock(vm_clock) +
+ (get_ticks_per_sec() / 50));
+ }
+
+ /* We don't know if there are more interrupts pending after this. However,
+ * the guest will return to userspace in the course of handling this one
+ * anyways, so we will get a chance to deliver the rest. */
+ return 0;
+}
+
+int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+{
+ return 0;
+}
+
+int kvm_arch_process_irqchip_events(CPUState *env)
+{
+ return 0;
+}
+
+static int kvmppc_handle_halt(CPUState *env)
+{
+ if (!(env->interrupt_request & CPU_INTERRUPT_HARD) && (msr_ee)) {
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ }
+
+ return 1;
+}
+
+/* map dcr access to existing qemu dcr emulation */
+static int kvmppc_handle_dcr_read(CPUState *env, uint32_t dcrn, uint32_t *data)
+{
+ if (ppc_dcr_read(env->dcr_env, dcrn, data) < 0)
+ fprintf(stderr, "Read to unhandled DCR (0x%x)\n", dcrn);
+
+ return 1;
+}
+
+static int kvmppc_handle_dcr_write(CPUState *env, uint32_t dcrn, uint32_t data)
+{
+ if (ppc_dcr_write(env->dcr_env, dcrn, data) < 0)
+ fprintf(stderr, "Write to unhandled DCR (0x%x)\n", dcrn);
+
+ return 1;
+}
+
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
+{
+ int ret = 0;
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_DCR:
+ if (run->dcr.is_write) {
+ dprintf("handle dcr write\n");
+ ret = kvmppc_handle_dcr_write(env, run->dcr.dcrn, run->dcr.data);
+ } else {
+ dprintf("handle dcr read\n");
+ ret = kvmppc_handle_dcr_read(env, run->dcr.dcrn, &run->dcr.data);
+ }
+ break;
+ case KVM_EXIT_HLT:
+ dprintf("handle halt\n");
+ ret = kvmppc_handle_halt(env);
+ break;
+ default:
+ fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+static int read_cpuinfo(const char *field, char *value, int len)
+{
+ FILE *f;
+ int ret = -1;
+ int field_len = strlen(field);
+ char line[512];
+
+ f = fopen("/proc/cpuinfo", "r");
+ if (!f) {
+ return -1;
+ }
+
+ do {
+ if(!fgets(line, sizeof(line), f)) {
+ break;
+ }
+ if (!strncmp(line, field, field_len)) {
+ strncpy(value, line, len);
+ ret = 0;
+ break;
+ }
+ } while(*line);
+
+ fclose(f);
+
+ return ret;
+}
+
+uint32_t kvmppc_get_tbfreq(void)
+{
+ char line[512];
+ char *ns;
+ uint32_t retval = get_ticks_per_sec();
+
+ if (read_cpuinfo("timebase", line, sizeof(line))) {
+ return retval;
+ }
+
+ if (!(ns = strchr(line, ':'))) {
+ return retval;
+ }
+
+ ns++;
+
+ retval = atoi(ns);
+ return retval;
+}
+
+int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len)
+{
+ uint32_t *hc = (uint32_t*)buf;
+
+#ifdef KVM_CAP_PPC_GET_PVINFO
+ struct kvm_ppc_pvinfo pvinfo;
+
+ if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO) &&
+ !kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_PVINFO, &pvinfo)) {
+ memcpy(buf, pvinfo.hcall, buf_len);
+
+ return 0;
+ }
+#endif
+
+ /*
+ * Fallback to always fail hypercalls:
+ *
+ * li r3, -1
+ * nop
+ * nop
+ * nop
+ */
+
+ hc[0] = 0x3860ffff;
+ hc[1] = 0x60000000;
+ hc[2] = 0x60000000;
+ hc[3] = 0x60000000;
+
+ return 0;
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUState *env)
+{
+ return true;
+}
diff --git a/target-ppc/kvm_ppc.c b/target-ppc/kvm_ppc.c
new file mode 100644
index 0000000..6f0468c
--- /dev/null
+++ b/target-ppc/kvm_ppc.c
@@ -0,0 +1,105 @@
+/*
+ * PowerPC KVM support
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ * Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "kvm_ppc.h"
+#include "device_tree.h"
+
+#define PROC_DEVTREE_PATH "/proc/device-tree"
+
+static QEMUTimer *kvmppc_timer;
+static unsigned int kvmppc_timer_rate;
+
+#ifdef CONFIG_FDT
+int kvmppc_read_host_property(const char *node_path, const char *prop,
+ void *val, size_t len)
+{
+ char *path;
+ FILE *f;
+ int ret = 0;
+ int pathlen;
+
+ pathlen = snprintf(NULL, 0, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop)
+ + 1;
+ path = qemu_malloc(pathlen);
+
+ snprintf(path, pathlen, "%s/%s/%s", PROC_DEVTREE_PATH, node_path, prop);
+
+ f = fopen(path, "rb");
+ if (f == NULL) {
+ ret = errno;
+ goto free;
+ }
+
+ len = fread(val, len, 1, f);
+ if (len != 1) {
+ ret = ferror(f);
+ goto close;
+ }
+
+close:
+ fclose(f);
+free:
+ free(path);
+ return ret;
+}
+
+static int kvmppc_copy_host_cell(void *fdt, const char *node, const char *prop)
+{
+ uint32_t cell;
+ int ret;
+
+ ret = kvmppc_read_host_property(node, prop, &cell, sizeof(cell));
+ if (ret < 0) {
+ fprintf(stderr, "couldn't read host %s/%s\n", node, prop);
+ goto out;
+ }
+
+ ret = qemu_devtree_setprop_cell(fdt, node, prop, cell);
+ if (ret < 0) {
+ fprintf(stderr, "couldn't set guest %s/%s\n", node, prop);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+void kvmppc_fdt_update(void *fdt)
+{
+ /* Copy data from the host device tree into the guest. Since the guest can
+ * directly access the timebase without host involvement, we must expose
+ * the correct frequencies. */
+ kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "clock-frequency");
+ kvmppc_copy_host_cell(fdt, "/cpus/cpu@0", "timebase-frequency");
+}
+#endif
+
+static void kvmppc_timer_hack(void *opaque)
+{
+ qemu_service_io();
+ qemu_mod_timer(kvmppc_timer, qemu_get_clock(vm_clock) + kvmppc_timer_rate);
+}
+
+void kvmppc_init(void)
+{
+ /* XXX The only reason KVM yields control back to qemu is device IO. Since
+ * an idle guest does no IO, qemu's device model will never get a chance to
+ * run. So, until Qemu gains IO threads, we create this timer to ensure
+ * that the device model gets a chance to run. */
+ kvmppc_timer_rate = get_ticks_per_sec() / 10;
+ kvmppc_timer = qemu_new_timer(vm_clock, &kvmppc_timer_hack, NULL);
+ qemu_mod_timer(kvmppc_timer, qemu_get_clock(vm_clock) + kvmppc_timer_rate);
+}
+
diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h
new file mode 100644
index 0000000..911b19e
--- /dev/null
+++ b/target-ppc/kvm_ppc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 IBM Corporation.
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * This work is licensed under the GNU GPL license version 2 or later.
+ *
+ */
+
+#ifndef __KVM_PPC_H__
+#define __KVM_PPC_H__
+
+void kvmppc_init(void);
+void kvmppc_fdt_update(void *fdt);
+int kvmppc_read_host_property(const char *node_path, const char *prop,
+ void *val, size_t len);
+
+uint32_t kvmppc_get_tbfreq(void);
+int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len);
+int kvmppc_set_interrupt(CPUState *env, int irq, int level);
+
+#ifndef KVM_INTERRUPT_SET
+#define KVM_INTERRUPT_SET -1
+#endif
+
+#ifndef KVM_INTERRUPT_UNSET
+#define KVM_INTERRUPT_UNSET -2
+#endif
+
+#ifndef KVM_INTERRUPT_SET_LEVEL
+#define KVM_INTERRUPT_SET_LEVEL -3
+#endif
+
+#endif /* __KVM_PPC_H__ */
diff --git a/target-ppc/libkvm.c b/target-ppc/libkvm.c
new file mode 100644
index 0000000..da93026
--- /dev/null
+++ b/target-ppc/libkvm.c
@@ -0,0 +1,102 @@
+/*
+ * This file contains the powerpc specific implementation for the
+ * architecture dependent functions defined in kvm-common.h and
+ * libkvm.h
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright IBM Corp. 2007,2008
+ * Authors:
+ * Jerone Young <jyoung5@us.ibm.com>
+ * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include "libkvm-all.h"
+#include "libkvm.h"
+#include <errno.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+int handle_dcr(kvm_vcpu_context_t vcpu)
+{
+ int ret = 0;
+ struct kvm_run *run = vcpu->run;
+ kvm_context_t kvm = vcpu->kvm;
+
+ if (run->dcr.is_write)
+ ret = kvm->callbacks->powerpc_dcr_write(vcpu,
+ run->dcr.dcrn,
+ run->dcr.data);
+ else
+ ret = kvm->callbacks->powerpc_dcr_read(vcpu,
+ run->dcr.dcrn,
+ &(run->dcr.data));
+
+ return ret;
+}
+
+void kvm_show_code(kvm_vcpu_context_t vcpu)
+{
+ fprintf(stderr, "%s: Operation not supported\n", __FUNCTION__);
+}
+
+void kvm_show_regs(kvm_vcpu_context_t vcpu)
+{
+ struct kvm_regs regs;
+ int i;
+
+ if (kvm_get_regs(vcpu, &regs))
+ return;
+
+ fprintf(stderr,"guest vcpu #%d\n", vcpu);
+ fprintf(stderr,"pc: %016"PRIx64" msr: %016"PRIx64"\n",
+ regs.pc, regs.msr);
+ fprintf(stderr,"lr: %016"PRIx64" ctr: %016"PRIx64"\n",
+ regs.lr, regs.ctr);
+ fprintf(stderr,"srr0: %016"PRIx64" srr1: %016"PRIx64"\n",
+ regs.srr0, regs.srr1);
+ for (i=0; i<32; i+=4)
+ {
+ fprintf(stderr, "gpr%02d: %016"PRIx64" %016"PRIx64" %016"PRIx64
+ " %016"PRIx64"\n", i,
+ regs.gpr[i],
+ regs.gpr[i+1],
+ regs.gpr[i+2],
+ regs.gpr[i+3]);
+ }
+
+ fflush(stdout);
+}
+
+int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes,
+ void **vm_mem)
+{
+ int r;
+
+ r = kvm_init_coalesced_mmio(kvm);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int kvm_arch_run(kvm_vcpu_context_t vcpu)
+{
+ int ret = 0;
+
+ switch (vcpu->run->exit_reason){
+ case KVM_EXIT_DCR:
+ ret = handle_dcr(vcpu);
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ return ret;
+}
diff --git a/target-ppc/libkvm.h b/target-ppc/libkvm.h
new file mode 100644
index 0000000..80b6b06
--- /dev/null
+++ b/target-ppc/libkvm.h
@@ -0,0 +1,36 @@
+/*
+ * This header is for functions & variables that will ONLY be
+ * used inside libkvm for powerpc.
+ * THESE ARE NOT EXPOSED TO THE USER AND ARE ONLY FOR USE
+ * WITHIN LIBKVM.
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ *
+ * Authors:
+ * Avi Kivity <avi@qumranet.com>
+ * Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * Copyright 2007 IBM Corporation.
+ * Added by: Jerone Young <jyoung5@us.ibm.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#ifndef KVM_POWERPC_H
+#define KVM_POWERPC_H
+
+#include "libkvm-all.h"
+
+extern int kvm_page_size;
+
+#define PAGE_SIZE kvm_page_size
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+static inline void eieio(void)
+{
+ asm volatile("eieio" : : : "memory");
+}
+
+#define smp_wmb() eieio()
+
+#endif
diff --git a/target-ppc/machine.c b/target-ppc/machine.c
new file mode 100644
index 0000000..81d3f72
--- /dev/null
+++ b/target-ppc/machine.c
@@ -0,0 +1,180 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "kvm.h"
+#include "qemu-kvm.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = (CPUState *)opaque;
+ unsigned int i, j;
+
+ for (i = 0; i < 32; i++)
+ qemu_put_betls(f, &env->gpr[i]);
+#if !defined(TARGET_PPC64)
+ for (i = 0; i < 32; i++)
+ qemu_put_betls(f, &env->gprh[i]);
+#endif
+ qemu_put_betls(f, &env->lr);
+ qemu_put_betls(f, &env->ctr);
+ for (i = 0; i < 8; i++)
+ qemu_put_be32s(f, &env->crf[i]);
+ qemu_put_betls(f, &env->xer);
+ qemu_put_betls(f, &env->reserve_addr);
+ qemu_put_betls(f, &env->msr);
+ for (i = 0; i < 4; i++)
+ qemu_put_betls(f, &env->tgpr[i]);
+ for (i = 0; i < 32; i++) {
+ union {
+ float64 d;
+ uint64_t l;
+ } u;
+ u.d = env->fpr[i];
+ qemu_put_be64(f, u.l);
+ }
+ qemu_put_be32s(f, &env->fpscr);
+ qemu_put_sbe32s(f, &env->access_type);
+#if !defined(CONFIG_USER_ONLY)
+#if defined(TARGET_PPC64)
+ qemu_put_betls(f, &env->asr);
+ qemu_put_sbe32s(f, &env->slb_nr);
+#endif
+ qemu_put_betls(f, &env->sdr1);
+ for (i = 0; i < 32; i++)
+ qemu_put_betls(f, &env->sr[i]);
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 8; j++)
+ qemu_put_betls(f, &env->DBAT[i][j]);
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 8; j++)
+ qemu_put_betls(f, &env->IBAT[i][j]);
+ qemu_put_sbe32s(f, &env->nb_tlb);
+ qemu_put_sbe32s(f, &env->tlb_per_way);
+ qemu_put_sbe32s(f, &env->nb_ways);
+ qemu_put_sbe32s(f, &env->last_way);
+ qemu_put_sbe32s(f, &env->id_tlbs);
+ qemu_put_sbe32s(f, &env->nb_pids);
+ if (env->tlb) {
+ // XXX assumes 6xx
+ for (i = 0; i < env->nb_tlb; i++) {
+ qemu_put_betls(f, &env->tlb[i].tlb6.pte0);
+ qemu_put_betls(f, &env->tlb[i].tlb6.pte1);
+ qemu_put_betls(f, &env->tlb[i].tlb6.EPN);
+ }
+ }
+ for (i = 0; i < 4; i++)
+ qemu_put_betls(f, &env->pb[i]);
+#endif
+ for (i = 0; i < 1024; i++)
+ qemu_put_betls(f, &env->spr[i]);
+ qemu_put_be32s(f, &env->vscr);
+ qemu_put_be64s(f, &env->spe_acc);
+ qemu_put_be32s(f, &env->spe_fscr);
+ qemu_put_betls(f, &env->msr_mask);
+ qemu_put_be32s(f, &env->flags);
+ qemu_put_sbe32s(f, &env->error_code);
+ qemu_put_be32s(f, &env->pending_interrupts);
+#if !defined(CONFIG_USER_ONLY)
+ qemu_put_be32s(f, &env->irq_input_state);
+ for (i = 0; i < POWERPC_EXCP_NB; i++)
+ qemu_put_betls(f, &env->excp_vectors[i]);
+ qemu_put_betls(f, &env->excp_prefix);
+ qemu_put_betls(f, &env->hreset_excp_prefix);
+ qemu_put_betls(f, &env->ivor_mask);
+ qemu_put_betls(f, &env->ivpr_mask);
+ qemu_put_betls(f, &env->hreset_vector);
+#endif
+ qemu_put_betls(f, &env->nip);
+ qemu_put_betls(f, &env->hflags);
+ qemu_put_betls(f, &env->hflags_nmsr);
+ qemu_put_sbe32s(f, &env->mmu_idx);
+ qemu_put_sbe32s(f, &env->power_mode);
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = (CPUState *)opaque;
+ unsigned int i, j;
+
+ for (i = 0; i < 32; i++)
+ qemu_get_betls(f, &env->gpr[i]);
+#if !defined(TARGET_PPC64)
+ for (i = 0; i < 32; i++)
+ qemu_get_betls(f, &env->gprh[i]);
+#endif
+ qemu_get_betls(f, &env->lr);
+ qemu_get_betls(f, &env->ctr);
+ for (i = 0; i < 8; i++)
+ qemu_get_be32s(f, &env->crf[i]);
+ qemu_get_betls(f, &env->xer);
+ qemu_get_betls(f, &env->reserve_addr);
+ qemu_get_betls(f, &env->msr);
+ for (i = 0; i < 4; i++)
+ qemu_get_betls(f, &env->tgpr[i]);
+ for (i = 0; i < 32; i++) {
+ union {
+ float64 d;
+ uint64_t l;
+ } u;
+ u.l = qemu_get_be64(f);
+ env->fpr[i] = u.d;
+ }
+ qemu_get_be32s(f, &env->fpscr);
+ qemu_get_sbe32s(f, &env->access_type);
+#if !defined(CONFIG_USER_ONLY)
+#if defined(TARGET_PPC64)
+ qemu_get_betls(f, &env->asr);
+ qemu_get_sbe32s(f, &env->slb_nr);
+#endif
+ qemu_get_betls(f, &env->sdr1);
+ for (i = 0; i < 32; i++)
+ qemu_get_betls(f, &env->sr[i]);
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 8; j++)
+ qemu_get_betls(f, &env->DBAT[i][j]);
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 8; j++)
+ qemu_get_betls(f, &env->IBAT[i][j]);
+ qemu_get_sbe32s(f, &env->nb_tlb);
+ qemu_get_sbe32s(f, &env->tlb_per_way);
+ qemu_get_sbe32s(f, &env->nb_ways);
+ qemu_get_sbe32s(f, &env->last_way);
+ qemu_get_sbe32s(f, &env->id_tlbs);
+ qemu_get_sbe32s(f, &env->nb_pids);
+ if (env->tlb) {
+ // XXX assumes 6xx
+ for (i = 0; i < env->nb_tlb; i++) {
+ qemu_get_betls(f, &env->tlb[i].tlb6.pte0);
+ qemu_get_betls(f, &env->tlb[i].tlb6.pte1);
+ qemu_get_betls(f, &env->tlb[i].tlb6.EPN);
+ }
+ }
+ for (i = 0; i < 4; i++)
+ qemu_get_betls(f, &env->pb[i]);
+#endif
+ for (i = 0; i < 1024; i++)
+ qemu_get_betls(f, &env->spr[i]);
+ qemu_get_be32s(f, &env->vscr);
+ qemu_get_be64s(f, &env->spe_acc);
+ qemu_get_be32s(f, &env->spe_fscr);
+ qemu_get_betls(f, &env->msr_mask);
+ qemu_get_be32s(f, &env->flags);
+ qemu_get_sbe32s(f, &env->error_code);
+ qemu_get_be32s(f, &env->pending_interrupts);
+#if !defined(CONFIG_USER_ONLY)
+ qemu_get_be32s(f, &env->irq_input_state);
+ for (i = 0; i < POWERPC_EXCP_NB; i++)
+ qemu_get_betls(f, &env->excp_vectors[i]);
+ qemu_get_betls(f, &env->excp_prefix);
+ qemu_get_betls(f, &env->hreset_excp_prefix);
+ qemu_get_betls(f, &env->ivor_mask);
+ qemu_get_betls(f, &env->ivpr_mask);
+ qemu_get_betls(f, &env->hreset_vector);
+#endif
+ qemu_get_betls(f, &env->nip);
+ qemu_get_betls(f, &env->hflags);
+ qemu_get_betls(f, &env->hflags_nmsr);
+ qemu_get_sbe32s(f, &env->mmu_idx);
+ qemu_get_sbe32s(f, &env->power_mode);
+
+ return 0;
+}
diff --git a/target-ppc/mfrom_table.c b/target-ppc/mfrom_table.c
new file mode 100644
index 0000000..6a1fa37
--- /dev/null
+++ b/target-ppc/mfrom_table.c
@@ -0,0 +1,79 @@
+static const uint8_t mfrom_ROM_table[602] =
+{
+ 77, 77, 76, 76, 75, 75, 74, 74,
+ 73, 73, 72, 72, 71, 71, 70, 70,
+ 69, 69, 68, 68, 68, 67, 67, 66,
+ 66, 65, 65, 64, 64, 64, 63, 63,
+ 62, 62, 61, 61, 61, 60, 60, 59,
+ 59, 58, 58, 58, 57, 57, 56, 56,
+ 56, 55, 55, 54, 54, 54, 53, 53,
+ 53, 52, 52, 51, 51, 51, 50, 50,
+ 50, 49, 49, 49, 48, 48, 47, 47,
+ 47, 46, 46, 46, 45, 45, 45, 44,
+ 44, 44, 43, 43, 43, 42, 42, 42,
+ 42, 41, 41, 41, 40, 40, 40, 39,
+ 39, 39, 39, 38, 38, 38, 37, 37,
+ 37, 37, 36, 36, 36, 35, 35, 35,
+ 35, 34, 34, 34, 34, 33, 33, 33,
+ 33, 32, 32, 32, 32, 31, 31, 31,
+ 31, 30, 30, 30, 30, 29, 29, 29,
+ 29, 28, 28, 28, 28, 28, 27, 27,
+ 27, 27, 26, 26, 26, 26, 26, 25,
+ 25, 25, 25, 25, 24, 24, 24, 24,
+ 24, 23, 23, 23, 23, 23, 23, 22,
+ 22, 22, 22, 22, 21, 21, 21, 21,
+ 21, 21, 20, 20, 20, 20, 20, 20,
+ 19, 19, 19, 19, 19, 19, 19, 18,
+ 18, 18, 18, 18, 18, 17, 17, 17,
+ 17, 17, 17, 17, 16, 16, 16, 16,
+ 16, 16, 16, 16, 15, 15, 15, 15,
+ 15, 15, 15, 15, 14, 14, 14, 14,
+ 14, 14, 14, 14, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0,
+};
diff --git a/target-ppc/mfrom_table_gen.c b/target-ppc/mfrom_table_gen.c
new file mode 100644
index 0000000..4c06aa4
--- /dev/null
+++ b/target-ppc/mfrom_table_gen.c
@@ -0,0 +1,33 @@
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdio.h>
+#include <math.h>
+
+int main (void)
+{
+ double d;
+ uint8_t n;
+ int i;
+
+ printf("static const uint8_t mfrom_ROM_table[602] =\n{\n ");
+ for (i = 0; i < 602; i++) {
+ /* Extremly decomposed:
+ * -T0 / 256
+ * T0 = 256 * log10(10 + 1.0) + 0.5
+ */
+ d = -i;
+ d /= 256.0;
+ d = exp10(d);
+ d += 1.0;
+ d = log10(d);
+ d *= 256;
+ d += 0.5;
+ n = d;
+ printf("%3d, ", n);
+ if ((i & 7) == 7)
+ printf("\n ");
+ }
+ printf("\n};\n");
+
+ return 0;
+}
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
new file mode 100644
index 0000000..17e070a
--- /dev/null
+++ b/target-ppc/op_helper.c
@@ -0,0 +1,4172 @@
+/*
+ * PowerPC emulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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 <string.h>
+#include "exec.h"
+#include "host-utils.h"
+#include "helper.h"
+
+#include "helper_regs.h"
+
+//#define DEBUG_OP
+//#define DEBUG_EXCEPTIONS
+//#define DEBUG_SOFTWARE_TLB
+
+#ifdef DEBUG_SOFTWARE_TLB
+# define LOG_SWTLB(...) qemu_log(__VA_ARGS__)
+#else
+# define LOG_SWTLB(...) do { } while (0)
+#endif
+
+
+/*****************************************************************************/
+/* Exceptions processing helpers */
+
+void helper_raise_exception_err (uint32_t exception, uint32_t error_code)
+{
+#if 0
+ printf("Raise exception %3x code : %d\n", exception, error_code);
+#endif
+ env->exception_index = exception;
+ env->error_code = error_code;
+ cpu_loop_exit();
+}
+
+void helper_raise_exception (uint32_t exception)
+{
+ helper_raise_exception_err(exception, 0);
+}
+
+/*****************************************************************************/
+/* SPR accesses */
+void helper_load_dump_spr (uint32_t sprn)
+{
+ qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn,
+ env->spr[sprn]);
+}
+
+void helper_store_dump_spr (uint32_t sprn)
+{
+ qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn,
+ env->spr[sprn]);
+}
+
+target_ulong helper_load_tbl (void)
+{
+ return (target_ulong)cpu_ppc_load_tbl(env);
+}
+
+target_ulong helper_load_tbu (void)
+{
+ return cpu_ppc_load_tbu(env);
+}
+
+target_ulong helper_load_atbl (void)
+{
+ return (target_ulong)cpu_ppc_load_atbl(env);
+}
+
+target_ulong helper_load_atbu (void)
+{
+ return cpu_ppc_load_atbu(env);
+}
+
+target_ulong helper_load_601_rtcl (void)
+{
+ return cpu_ppc601_load_rtcl(env);
+}
+
+target_ulong helper_load_601_rtcu (void)
+{
+ return cpu_ppc601_load_rtcu(env);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+#if defined (TARGET_PPC64)
+void helper_store_asr (target_ulong val)
+{
+ ppc_store_asr(env, val);
+}
+#endif
+
+void helper_store_sdr1 (target_ulong val)
+{
+ ppc_store_sdr1(env, val);
+}
+
+void helper_store_tbl (target_ulong val)
+{
+ cpu_ppc_store_tbl(env, val);
+}
+
+void helper_store_tbu (target_ulong val)
+{
+ cpu_ppc_store_tbu(env, val);
+}
+
+void helper_store_atbl (target_ulong val)
+{
+ cpu_ppc_store_atbl(env, val);
+}
+
+void helper_store_atbu (target_ulong val)
+{
+ cpu_ppc_store_atbu(env, val);
+}
+
+void helper_store_601_rtcl (target_ulong val)
+{
+ cpu_ppc601_store_rtcl(env, val);
+}
+
+void helper_store_601_rtcu (target_ulong val)
+{
+ cpu_ppc601_store_rtcu(env, val);
+}
+
+target_ulong helper_load_decr (void)
+{
+ return cpu_ppc_load_decr(env);
+}
+
+void helper_store_decr (target_ulong val)
+{
+ cpu_ppc_store_decr(env, val);
+}
+
+void helper_store_hid0_601 (target_ulong val)
+{
+ target_ulong hid0;
+
+ hid0 = env->spr[SPR_HID0];
+ if ((val ^ hid0) & 0x00000008) {
+ /* Change current endianness */
+ env->hflags &= ~(1 << MSR_LE);
+ env->hflags_nmsr &= ~(1 << MSR_LE);
+ env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE);
+ env->hflags |= env->hflags_nmsr;
+ qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__,
+ val & 0x8 ? 'l' : 'b', env->hflags);
+ }
+ env->spr[SPR_HID0] = (uint32_t)val;
+}
+
+void helper_store_403_pbr (uint32_t num, target_ulong value)
+{
+ if (likely(env->pb[num] != value)) {
+ env->pb[num] = value;
+ /* Should be optimized */
+ tlb_flush(env, 1);
+ }
+}
+
+target_ulong helper_load_40x_pit (void)
+{
+ return load_40x_pit(env);
+}
+
+void helper_store_40x_pit (target_ulong val)
+{
+ store_40x_pit(env, val);
+}
+
+void helper_store_40x_dbcr0 (target_ulong val)
+{
+ store_40x_dbcr0(env, val);
+}
+
+void helper_store_40x_sler (target_ulong val)
+{
+ store_40x_sler(env, val);
+}
+
+void helper_store_booke_tcr (target_ulong val)
+{
+ store_booke_tcr(env, val);
+}
+
+void helper_store_booke_tsr (target_ulong val)
+{
+ store_booke_tsr(env, val);
+}
+
+void helper_store_ibatu (uint32_t nr, target_ulong val)
+{
+ ppc_store_ibatu(env, nr, val);
+}
+
+void helper_store_ibatl (uint32_t nr, target_ulong val)
+{
+ ppc_store_ibatl(env, nr, val);
+}
+
+void helper_store_dbatu (uint32_t nr, target_ulong val)
+{
+ ppc_store_dbatu(env, nr, val);
+}
+
+void helper_store_dbatl (uint32_t nr, target_ulong val)
+{
+ ppc_store_dbatl(env, nr, val);
+}
+
+void helper_store_601_batl (uint32_t nr, target_ulong val)
+{
+ ppc_store_ibatl_601(env, nr, val);
+}
+
+void helper_store_601_batu (uint32_t nr, target_ulong val)
+{
+ ppc_store_ibatu_601(env, nr, val);
+}
+#endif
+
+/*****************************************************************************/
+/* Memory load and stores */
+
+static inline target_ulong addr_add(target_ulong addr, target_long arg)
+{
+#if defined(TARGET_PPC64)
+ if (!msr_sf)
+ return (uint32_t)(addr + arg);
+ else
+#endif
+ return addr + arg;
+}
+
+void helper_lmw (target_ulong addr, uint32_t reg)
+{
+ for (; reg < 32; reg++) {
+ if (msr_le)
+ env->gpr[reg] = bswap32(ldl(addr));
+ else
+ env->gpr[reg] = ldl(addr);
+ addr = addr_add(addr, 4);
+ }
+}
+
+void helper_stmw (target_ulong addr, uint32_t reg)
+{
+ for (; reg < 32; reg++) {
+ if (msr_le)
+ stl(addr, bswap32((uint32_t)env->gpr[reg]));
+ else
+ stl(addr, (uint32_t)env->gpr[reg]);
+ addr = addr_add(addr, 4);
+ }
+}
+
+void helper_lsw(target_ulong addr, uint32_t nb, uint32_t reg)
+{
+ int sh;
+ for (; nb > 3; nb -= 4) {
+ env->gpr[reg] = ldl(addr);
+ reg = (reg + 1) % 32;
+ addr = addr_add(addr, 4);
+ }
+ if (unlikely(nb > 0)) {
+ env->gpr[reg] = 0;
+ for (sh = 24; nb > 0; nb--, sh -= 8) {
+ env->gpr[reg] |= ldub(addr) << sh;
+ addr = addr_add(addr, 1);
+ }
+ }
+}
+/* PPC32 specification says we must generate an exception if
+ * rA is in the range of registers to be loaded.
+ * In an other hand, IBM says this is valid, but rA won't be loaded.
+ * For now, I'll follow the spec...
+ */
+void helper_lswx(target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb)
+{
+ if (likely(xer_bc != 0)) {
+ if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) ||
+ (reg < rb && (reg + xer_bc) > rb))) {
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM,
+ POWERPC_EXCP_INVAL |
+ POWERPC_EXCP_INVAL_LSWX);
+ } else {
+ helper_lsw(addr, xer_bc, reg);
+ }
+ }
+}
+
+void helper_stsw(target_ulong addr, uint32_t nb, uint32_t reg)
+{
+ int sh;
+ for (; nb > 3; nb -= 4) {
+ stl(addr, env->gpr[reg]);
+ reg = (reg + 1) % 32;
+ addr = addr_add(addr, 4);
+ }
+ if (unlikely(nb > 0)) {
+ for (sh = 24; nb > 0; nb--, sh -= 8) {
+ stb(addr, (env->gpr[reg] >> sh) & 0xFF);
+ addr = addr_add(addr, 1);
+ }
+ }
+}
+
+static void do_dcbz(target_ulong addr, int dcache_line_size)
+{
+ addr &= ~(dcache_line_size - 1);
+ int i;
+ for (i = 0 ; i < dcache_line_size ; i += 4) {
+ stl(addr + i , 0);
+ }
+ if (env->reserve_addr == addr)
+ env->reserve_addr = (target_ulong)-1ULL;
+}
+
+void helper_dcbz(target_ulong addr)
+{
+ do_dcbz(addr, env->dcache_line_size);
+}
+
+void helper_dcbz_970(target_ulong addr)
+{
+ if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1)
+ do_dcbz(addr, 32);
+ else
+ do_dcbz(addr, env->dcache_line_size);
+}
+
+void helper_icbi(target_ulong addr)
+{
+ addr &= ~(env->dcache_line_size - 1);
+ /* Invalidate one cache line :
+ * PowerPC specification says this is to be treated like a load
+ * (not a fetch) by the MMU. To be sure it will be so,
+ * do the load "by hand".
+ */
+ ldl(addr);
+ tb_invalidate_page_range(addr, addr + env->icache_line_size);
+}
+
+// XXX: to be tested
+target_ulong helper_lscbx (target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb)
+{
+ int i, c, d;
+ d = 24;
+ for (i = 0; i < xer_bc; i++) {
+ c = ldub(addr);
+ addr = addr_add(addr, 1);
+ /* ra (if not 0) and rb are never modified */
+ if (likely(reg != rb && (ra == 0 || reg != ra))) {
+ env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d);
+ }
+ if (unlikely(c == xer_cmp))
+ break;
+ if (likely(d != 0)) {
+ d -= 8;
+ } else {
+ d = 24;
+ reg++;
+ reg = reg & 0x1F;
+ }
+ }
+ return i;
+}
+
+/*****************************************************************************/
+/* Fixed point operations helpers */
+#if defined(TARGET_PPC64)
+
+/* multiply high word */
+uint64_t helper_mulhd (uint64_t arg1, uint64_t arg2)
+{
+ uint64_t tl, th;
+
+ muls64(&tl, &th, arg1, arg2);
+ return th;
+}
+
+/* multiply high word unsigned */
+uint64_t helper_mulhdu (uint64_t arg1, uint64_t arg2)
+{
+ uint64_t tl, th;
+
+ mulu64(&tl, &th, arg1, arg2);
+ return th;
+}
+
+uint64_t helper_mulldo (uint64_t arg1, uint64_t arg2)
+{
+ int64_t th;
+ uint64_t tl;
+
+ muls64(&tl, (uint64_t *)&th, arg1, arg2);
+ /* If th != 0 && th != -1, then we had an overflow */
+ if (likely((uint64_t)(th + 1) <= 1)) {
+ env->xer &= ~(1 << XER_OV);
+ } else {
+ env->xer |= (1 << XER_OV) | (1 << XER_SO);
+ }
+ return (int64_t)tl;
+}
+#endif
+
+target_ulong helper_cntlzw (target_ulong t)
+{
+ return clz32(t);
+}
+
+#if defined(TARGET_PPC64)
+target_ulong helper_cntlzd (target_ulong t)
+{
+ return clz64(t);
+}
+#endif
+
+/* shift right arithmetic helper */
+target_ulong helper_sraw (target_ulong value, target_ulong shift)
+{
+ int32_t ret;
+
+ if (likely(!(shift & 0x20))) {
+ if (likely((uint32_t)shift != 0)) {
+ shift &= 0x1f;
+ ret = (int32_t)value >> shift;
+ if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) {
+ env->xer &= ~(1 << XER_CA);
+ } else {
+ env->xer |= (1 << XER_CA);
+ }
+ } else {
+ ret = (int32_t)value;
+ env->xer &= ~(1 << XER_CA);
+ }
+ } else {
+ ret = (int32_t)value >> 31;
+ if (ret) {
+ env->xer |= (1 << XER_CA);
+ } else {
+ env->xer &= ~(1 << XER_CA);
+ }
+ }
+ return (target_long)ret;
+}
+
+#if defined(TARGET_PPC64)
+target_ulong helper_srad (target_ulong value, target_ulong shift)
+{
+ int64_t ret;
+
+ if (likely(!(shift & 0x40))) {
+ if (likely((uint64_t)shift != 0)) {
+ shift &= 0x3f;
+ ret = (int64_t)value >> shift;
+ if (likely(ret >= 0 || (value & ((1 << shift) - 1)) == 0)) {
+ env->xer &= ~(1 << XER_CA);
+ } else {
+ env->xer |= (1 << XER_CA);
+ }
+ } else {
+ ret = (int64_t)value;
+ env->xer &= ~(1 << XER_CA);
+ }
+ } else {
+ ret = (int64_t)value >> 63;
+ if (ret) {
+ env->xer |= (1 << XER_CA);
+ } else {
+ env->xer &= ~(1 << XER_CA);
+ }
+ }
+ return ret;
+}
+#endif
+
+target_ulong helper_popcntb (target_ulong val)
+{
+ val = (val & 0x55555555) + ((val >> 1) & 0x55555555);
+ val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
+ val = (val & 0x0f0f0f0f) + ((val >> 4) & 0x0f0f0f0f);
+ return val;
+}
+
+#if defined(TARGET_PPC64)
+target_ulong helper_popcntb_64 (target_ulong val)
+{
+ val = (val & 0x5555555555555555ULL) + ((val >> 1) & 0x5555555555555555ULL);
+ val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL);
+ val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >> 4) & 0x0f0f0f0f0f0f0f0fULL);
+ return val;
+}
+#endif
+
+/*****************************************************************************/
+/* Floating point operations helpers */
+uint64_t helper_float32_to_float64(uint32_t arg)
+{
+ CPU_FloatU f;
+ CPU_DoubleU d;
+ f.l = arg;
+ d.d = float32_to_float64(f.f, &env->fp_status);
+ return d.ll;
+}
+
+uint32_t helper_float64_to_float32(uint64_t arg)
+{
+ CPU_FloatU f;
+ CPU_DoubleU d;
+ d.ll = arg;
+ f.f = float64_to_float32(d.d, &env->fp_status);
+ return f.l;
+}
+
+static inline int isden(float64 d)
+{
+ CPU_DoubleU u;
+
+ u.d = d;
+
+ return ((u.ll >> 52) & 0x7FF) == 0;
+}
+
+uint32_t helper_compute_fprf (uint64_t arg, uint32_t set_fprf)
+{
+ CPU_DoubleU farg;
+ int isneg;
+ int ret;
+ farg.ll = arg;
+ isneg = float64_is_neg(farg.d);
+ if (unlikely(float64_is_any_nan(farg.d))) {
+ if (float64_is_signaling_nan(farg.d)) {
+ /* Signaling NaN: flags are undefined */
+ ret = 0x00;
+ } else {
+ /* Quiet NaN */
+ ret = 0x11;
+ }
+ } else if (unlikely(float64_is_infinity(farg.d))) {
+ /* +/- infinity */
+ if (isneg)
+ ret = 0x09;
+ else
+ ret = 0x05;
+ } else {
+ if (float64_is_zero(farg.d)) {
+ /* +/- zero */
+ if (isneg)
+ ret = 0x12;
+ else
+ ret = 0x02;
+ } else {
+ if (isden(farg.d)) {
+ /* Denormalized numbers */
+ ret = 0x10;
+ } else {
+ /* Normalized numbers */
+ ret = 0x00;
+ }
+ if (isneg) {
+ ret |= 0x08;
+ } else {
+ ret |= 0x04;
+ }
+ }
+ }
+ if (set_fprf) {
+ /* We update FPSCR_FPRF */
+ env->fpscr &= ~(0x1F << FPSCR_FPRF);
+ env->fpscr |= ret << FPSCR_FPRF;
+ }
+ /* We just need fpcc to update Rc1 */
+ return ret & 0xF;
+}
+
+/* Floating-point invalid operations exception */
+static inline uint64_t fload_invalid_op_excp(int op)
+{
+ uint64_t ret = 0;
+ int ve;
+
+ ve = fpscr_ve;
+ switch (op) {
+ case POWERPC_EXCP_FP_VXSNAN:
+ env->fpscr |= 1 << FPSCR_VXSNAN;
+ break;
+ case POWERPC_EXCP_FP_VXSOFT:
+ env->fpscr |= 1 << FPSCR_VXSOFT;
+ break;
+ case POWERPC_EXCP_FP_VXISI:
+ /* Magnitude subtraction of infinities */
+ env->fpscr |= 1 << FPSCR_VXISI;
+ goto update_arith;
+ case POWERPC_EXCP_FP_VXIDI:
+ /* Division of infinity by infinity */
+ env->fpscr |= 1 << FPSCR_VXIDI;
+ goto update_arith;
+ case POWERPC_EXCP_FP_VXZDZ:
+ /* Division of zero by zero */
+ env->fpscr |= 1 << FPSCR_VXZDZ;
+ goto update_arith;
+ case POWERPC_EXCP_FP_VXIMZ:
+ /* Multiplication of zero by infinity */
+ env->fpscr |= 1 << FPSCR_VXIMZ;
+ goto update_arith;
+ case POWERPC_EXCP_FP_VXVC:
+ /* Ordered comparison of NaN */
+ env->fpscr |= 1 << FPSCR_VXVC;
+ env->fpscr &= ~(0xF << FPSCR_FPCC);
+ env->fpscr |= 0x11 << FPSCR_FPCC;
+ /* We must update the target FPR before raising the exception */
+ if (ve != 0) {
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC;
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ /* Exception is differed */
+ ve = 0;
+ }
+ break;
+ case POWERPC_EXCP_FP_VXSQRT:
+ /* Square root of a negative number */
+ env->fpscr |= 1 << FPSCR_VXSQRT;
+ update_arith:
+ env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI));
+ if (ve == 0) {
+ /* Set the result to quiet NaN */
+ ret = 0x7FF8000000000000ULL;
+ env->fpscr &= ~(0xF << FPSCR_FPCC);
+ env->fpscr |= 0x11 << FPSCR_FPCC;
+ }
+ break;
+ case POWERPC_EXCP_FP_VXCVI:
+ /* Invalid conversion */
+ env->fpscr |= 1 << FPSCR_VXCVI;
+ env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI));
+ if (ve == 0) {
+ /* Set the result to quiet NaN */
+ ret = 0x7FF8000000000000ULL;
+ env->fpscr &= ~(0xF << FPSCR_FPCC);
+ env->fpscr |= 0x11 << FPSCR_FPCC;
+ }
+ break;
+ }
+ /* Update the floating-point invalid operation summary */
+ env->fpscr |= 1 << FPSCR_VX;
+ /* Update the floating-point exception summary */
+ env->fpscr |= 1 << FPSCR_FX;
+ if (ve != 0) {
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ if (msr_fe0 != 0 || msr_fe1 != 0)
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | op);
+ }
+ return ret;
+}
+
+static inline void float_zero_divide_excp(void)
+{
+ env->fpscr |= 1 << FPSCR_ZX;
+ env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI));
+ /* Update the floating-point exception summary */
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_ze != 0) {
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ if (msr_fe0 != 0 || msr_fe1 != 0) {
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM,
+ POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX);
+ }
+ }
+}
+
+static inline void float_overflow_excp(void)
+{
+ env->fpscr |= 1 << FPSCR_OX;
+ /* Update the floating-point exception summary */
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_oe != 0) {
+ /* XXX: should adjust the result */
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ /* We must update the target FPR before raising the exception */
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX;
+ } else {
+ env->fpscr |= 1 << FPSCR_XX;
+ env->fpscr |= 1 << FPSCR_FI;
+ }
+}
+
+static inline void float_underflow_excp(void)
+{
+ env->fpscr |= 1 << FPSCR_UX;
+ /* Update the floating-point exception summary */
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_ue != 0) {
+ /* XXX: should adjust the result */
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ /* We must update the target FPR before raising the exception */
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX;
+ }
+}
+
+static inline void float_inexact_excp(void)
+{
+ env->fpscr |= 1 << FPSCR_XX;
+ /* Update the floating-point exception summary */
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_xe != 0) {
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ /* We must update the target FPR before raising the exception */
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX;
+ }
+}
+
+static inline void fpscr_set_rounding_mode(void)
+{
+ int rnd_type;
+
+ /* Set rounding mode */
+ switch (fpscr_rn) {
+ case 0:
+ /* Best approximation (round to nearest) */
+ rnd_type = float_round_nearest_even;
+ break;
+ case 1:
+ /* Smaller magnitude (round toward zero) */
+ rnd_type = float_round_to_zero;
+ break;
+ case 2:
+ /* Round toward +infinite */
+ rnd_type = float_round_up;
+ break;
+ default:
+ case 3:
+ /* Round toward -infinite */
+ rnd_type = float_round_down;
+ break;
+ }
+ set_float_rounding_mode(rnd_type, &env->fp_status);
+}
+
+void helper_fpscr_clrbit (uint32_t bit)
+{
+ int prev;
+
+ prev = (env->fpscr >> bit) & 1;
+ env->fpscr &= ~(1 << bit);
+ if (prev == 1) {
+ switch (bit) {
+ case FPSCR_RN1:
+ case FPSCR_RN:
+ fpscr_set_rounding_mode();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void helper_fpscr_setbit (uint32_t bit)
+{
+ int prev;
+
+ prev = (env->fpscr >> bit) & 1;
+ env->fpscr |= 1 << bit;
+ if (prev == 0) {
+ switch (bit) {
+ case FPSCR_VX:
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_ve)
+ goto raise_ve;
+ case FPSCR_OX:
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_oe)
+ goto raise_oe;
+ break;
+ case FPSCR_UX:
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_ue)
+ goto raise_ue;
+ break;
+ case FPSCR_ZX:
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_ze)
+ goto raise_ze;
+ break;
+ case FPSCR_XX:
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_xe)
+ goto raise_xe;
+ break;
+ case FPSCR_VXSNAN:
+ case FPSCR_VXISI:
+ case FPSCR_VXIDI:
+ case FPSCR_VXZDZ:
+ case FPSCR_VXIMZ:
+ case FPSCR_VXVC:
+ case FPSCR_VXSOFT:
+ case FPSCR_VXSQRT:
+ case FPSCR_VXCVI:
+ env->fpscr |= 1 << FPSCR_VX;
+ env->fpscr |= 1 << FPSCR_FX;
+ if (fpscr_ve != 0)
+ goto raise_ve;
+ break;
+ case FPSCR_VE:
+ if (fpscr_vx != 0) {
+ raise_ve:
+ env->error_code = POWERPC_EXCP_FP;
+ if (fpscr_vxsnan)
+ env->error_code |= POWERPC_EXCP_FP_VXSNAN;
+ if (fpscr_vxisi)
+ env->error_code |= POWERPC_EXCP_FP_VXISI;
+ if (fpscr_vxidi)
+ env->error_code |= POWERPC_EXCP_FP_VXIDI;
+ if (fpscr_vxzdz)
+ env->error_code |= POWERPC_EXCP_FP_VXZDZ;
+ if (fpscr_vximz)
+ env->error_code |= POWERPC_EXCP_FP_VXIMZ;
+ if (fpscr_vxvc)
+ env->error_code |= POWERPC_EXCP_FP_VXVC;
+ if (fpscr_vxsoft)
+ env->error_code |= POWERPC_EXCP_FP_VXSOFT;
+ if (fpscr_vxsqrt)
+ env->error_code |= POWERPC_EXCP_FP_VXSQRT;
+ if (fpscr_vxcvi)
+ env->error_code |= POWERPC_EXCP_FP_VXCVI;
+ goto raise_excp;
+ }
+ break;
+ case FPSCR_OE:
+ if (fpscr_ox != 0) {
+ raise_oe:
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX;
+ goto raise_excp;
+ }
+ break;
+ case FPSCR_UE:
+ if (fpscr_ux != 0) {
+ raise_ue:
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX;
+ goto raise_excp;
+ }
+ break;
+ case FPSCR_ZE:
+ if (fpscr_zx != 0) {
+ raise_ze:
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX;
+ goto raise_excp;
+ }
+ break;
+ case FPSCR_XE:
+ if (fpscr_xx != 0) {
+ raise_xe:
+ env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX;
+ goto raise_excp;
+ }
+ break;
+ case FPSCR_RN1:
+ case FPSCR_RN:
+ fpscr_set_rounding_mode();
+ break;
+ default:
+ break;
+ raise_excp:
+ /* Update the floating-point enabled exception summary */
+ env->fpscr |= 1 << FPSCR_FEX;
+ /* We have to update Rc1 before raising the exception */
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ break;
+ }
+ }
+}
+
+void helper_store_fpscr (uint64_t arg, uint32_t mask)
+{
+ /*
+ * We use only the 32 LSB of the incoming fpr
+ */
+ uint32_t prev, new;
+ int i;
+
+ prev = env->fpscr;
+ new = (uint32_t)arg;
+ new &= ~0x60000000;
+ new |= prev & 0x60000000;
+ for (i = 0; i < 8; i++) {
+ if (mask & (1 << i)) {
+ env->fpscr &= ~(0xF << (4 * i));
+ env->fpscr |= new & (0xF << (4 * i));
+ }
+ }
+ /* Update VX and FEX */
+ if (fpscr_ix != 0)
+ env->fpscr |= 1 << FPSCR_VX;
+ else
+ env->fpscr &= ~(1 << FPSCR_VX);
+ if ((fpscr_ex & fpscr_eex) != 0) {
+ env->fpscr |= 1 << FPSCR_FEX;
+ env->exception_index = POWERPC_EXCP_PROGRAM;
+ /* XXX: we should compute it properly */
+ env->error_code = POWERPC_EXCP_FP;
+ }
+ else
+ env->fpscr &= ~(1 << FPSCR_FEX);
+ fpscr_set_rounding_mode();
+}
+
+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 */
+ if (msr_fe0 != 0 || msr_fe1 != 0)
+ helper_raise_exception_err(env->exception_index, env->error_code);
+ } else {
+ int status = get_float_exception_flags(&env->fp_status);
+ if (status & float_flag_divbyzero) {
+ float_zero_divide_excp();
+ } else if (status & float_flag_overflow) {
+ float_overflow_excp();
+ } else if (status & float_flag_underflow) {
+ float_underflow_excp();
+ } else if (status & float_flag_inexact) {
+ 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)
+{
+ CPU_DoubleU farg1, farg2;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+
+ if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) &&
+ float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) {
+ /* Magnitude subtraction of infinities */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d))) {
+ /* sNaN addition */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status);
+ }
+
+ return farg1.ll;
+}
+
+/* fsub - fsub. */
+uint64_t helper_fsub (uint64_t arg1, uint64_t arg2)
+{
+ CPU_DoubleU farg1, farg2;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+
+ if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) &&
+ float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) {
+ /* Magnitude subtraction of infinities */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d))) {
+ /* sNaN subtraction */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status);
+ }
+
+ return farg1.ll;
+}
+
+/* fmul - fmul. */
+uint64_t helper_fmul (uint64_t arg1, uint64_t arg2)
+{
+ CPU_DoubleU farg1, farg2;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+
+ if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
+ (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
+ /* Multiplication of zero by infinity */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d))) {
+ /* sNaN multiplication */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status);
+ }
+
+ return farg1.ll;
+}
+
+/* fdiv - fdiv. */
+uint64_t helper_fdiv (uint64_t arg1, uint64_t arg2)
+{
+ CPU_DoubleU farg1, farg2;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+
+ if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) {
+ /* Division of infinity by infinity */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIDI);
+ } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) {
+ /* Division of zero by zero */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXZDZ);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d))) {
+ /* sNaN division */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status);
+ }
+
+ return farg1.ll;
+}
+
+/* fabs */
+uint64_t helper_fabs (uint64_t arg)
+{
+ CPU_DoubleU farg;
+
+ farg.ll = arg;
+ farg.d = float64_abs(farg.d);
+ return farg.ll;
+}
+
+/* fnabs */
+uint64_t helper_fnabs (uint64_t arg)
+{
+ CPU_DoubleU farg;
+
+ farg.ll = arg;
+ farg.d = float64_abs(farg.d);
+ farg.d = float64_chs(farg.d);
+ return farg.ll;
+}
+
+/* fneg */
+uint64_t helper_fneg (uint64_t arg)
+{
+ CPU_DoubleU farg;
+
+ farg.ll = arg;
+ farg.d = float64_chs(farg.d);
+ return farg.ll;
+}
+
+/* fctiw - fctiw. */
+uint64_t helper_fctiw (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+ } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) {
+ /* qNan / infinity conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+ } else {
+ farg.ll = float64_to_int32(farg.d, &env->fp_status);
+ /* XXX: higher bits are not supposed to be significant.
+ * to make tests easier, return the same as a real PowerPC 750
+ */
+ farg.ll |= 0xFFF80000ULL << 32;
+ }
+ return farg.ll;
+}
+
+/* fctiwz - fctiwz. */
+uint64_t helper_fctiwz (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+ } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) {
+ /* qNan / infinity conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+ } else {
+ farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status);
+ /* XXX: higher bits are not supposed to be significant.
+ * to make tests easier, return the same as a real PowerPC 750
+ */
+ farg.ll |= 0xFFF80000ULL << 32;
+ }
+ return farg.ll;
+}
+
+#if defined(TARGET_PPC64)
+/* fcfid - fcfid. */
+uint64_t helper_fcfid (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.d = int64_to_float64(arg, &env->fp_status);
+ return farg.ll;
+}
+
+/* fctid - fctid. */
+uint64_t helper_fctid (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+ } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) {
+ /* qNan / infinity conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+ } else {
+ farg.ll = float64_to_int64(farg.d, &env->fp_status);
+ }
+ return farg.ll;
+}
+
+/* fctidz - fctidz. */
+uint64_t helper_fctidz (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+ } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) {
+ /* qNan / infinity conversion */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+ } else {
+ farg.ll = float64_to_int64_round_to_zero(farg.d, &env->fp_status);
+ }
+ return farg.ll;
+}
+
+#endif
+
+static inline uint64_t do_fri(uint64_t arg, int rounding_mode)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN round */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+ } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) {
+ /* qNan / infinity round */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+ } else {
+ set_float_rounding_mode(rounding_mode, &env->fp_status);
+ farg.ll = float64_round_to_int(farg.d, &env->fp_status);
+ /* Restore rounding mode from FPSCR */
+ fpscr_set_rounding_mode();
+ }
+ return farg.ll;
+}
+
+uint64_t helper_frin (uint64_t arg)
+{
+ return do_fri(arg, float_round_nearest_even);
+}
+
+uint64_t helper_friz (uint64_t arg)
+{
+ return do_fri(arg, float_round_to_zero);
+}
+
+uint64_t helper_frip (uint64_t arg)
+{
+ return do_fri(arg, float_round_up);
+}
+
+uint64_t helper_frim (uint64_t arg)
+{
+ return do_fri(arg, float_round_down);
+}
+
+/* fmadd - fmadd. */
+uint64_t helper_fmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+ CPU_DoubleU farg1, farg2, farg3;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+ farg3.ll = arg3;
+
+ if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
+ (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
+ /* Multiplication of zero by infinity */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d) ||
+ float64_is_signaling_nan(farg3.d))) {
+ /* sNaN operation */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+#ifdef FLOAT128
+ /* This is the way the PowerPC specification defines it */
+ float128 ft0_128, ft1_128;
+
+ ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
+ ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
+ ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+ if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) &&
+ float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) {
+ /* Magnitude subtraction of infinities */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+ } else {
+ ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
+ ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
+ farg1.d = float128_to_float64(ft0_128, &env->fp_status);
+ }
+#else
+ /* This is OK on x86 hosts */
+ farg1.d = (farg1.d * farg2.d) + farg3.d;
+#endif
+ }
+
+ return farg1.ll;
+}
+
+/* fmsub - fmsub. */
+uint64_t helper_fmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+ CPU_DoubleU farg1, farg2, farg3;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+ farg3.ll = arg3;
+
+ if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
+ (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
+ /* Multiplication of zero by infinity */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d) ||
+ float64_is_signaling_nan(farg3.d))) {
+ /* sNaN operation */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+#ifdef FLOAT128
+ /* This is the way the PowerPC specification defines it */
+ float128 ft0_128, ft1_128;
+
+ ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
+ ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
+ ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+ if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) &&
+ float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) {
+ /* Magnitude subtraction of infinities */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+ } else {
+ ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
+ ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
+ farg1.d = float128_to_float64(ft0_128, &env->fp_status);
+ }
+#else
+ /* This is OK on x86 hosts */
+ farg1.d = (farg1.d * farg2.d) - farg3.d;
+#endif
+ }
+ return farg1.ll;
+}
+
+/* fnmadd - fnmadd. */
+uint64_t helper_fnmadd (uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+ CPU_DoubleU farg1, farg2, farg3;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+ farg3.ll = arg3;
+
+ if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
+ (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
+ /* Multiplication of zero by infinity */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d) ||
+ float64_is_signaling_nan(farg3.d))) {
+ /* sNaN operation */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+#ifdef FLOAT128
+ /* This is the way the PowerPC specification defines it */
+ float128 ft0_128, ft1_128;
+
+ ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
+ ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
+ ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+ if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) &&
+ float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) {
+ /* Magnitude subtraction of infinities */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+ } else {
+ ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
+ ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
+ farg1.d = float128_to_float64(ft0_128, &env->fp_status);
+ }
+#else
+ /* This is OK on x86 hosts */
+ farg1.d = (farg1.d * farg2.d) + farg3.d;
+#endif
+ if (likely(!float64_is_any_nan(farg1.d))) {
+ farg1.d = float64_chs(farg1.d);
+ }
+ }
+ return farg1.ll;
+}
+
+/* fnmsub - fnmsub. */
+uint64_t helper_fnmsub (uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+ CPU_DoubleU farg1, farg2, farg3;
+
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+ farg3.ll = arg3;
+
+ if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) ||
+ (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) {
+ /* Multiplication of zero by infinity */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d) ||
+ float64_is_signaling_nan(farg3.d))) {
+ /* sNaN operation */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+#ifdef FLOAT128
+ /* This is the way the PowerPC specification defines it */
+ float128 ft0_128, ft1_128;
+
+ ft0_128 = float64_to_float128(farg1.d, &env->fp_status);
+ ft1_128 = float64_to_float128(farg2.d, &env->fp_status);
+ ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+ if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) &&
+ float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) {
+ /* Magnitude subtraction of infinities */
+ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+ } else {
+ ft1_128 = float64_to_float128(farg3.d, &env->fp_status);
+ ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
+ farg1.d = float128_to_float64(ft0_128, &env->fp_status);
+ }
+#else
+ /* This is OK on x86 hosts */
+ farg1.d = (farg1.d * farg2.d) - farg3.d;
+#endif
+ if (likely(!float64_is_any_nan(farg1.d))) {
+ farg1.d = float64_chs(farg1.d);
+ }
+ }
+ return farg1.ll;
+}
+
+/* frsp - frsp. */
+uint64_t helper_frsp (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ float32 f32;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN square root */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ f32 = float64_to_float32(farg.d, &env->fp_status);
+ farg.d = float32_to_float64(f32, &env->fp_status);
+
+ return farg.ll;
+}
+
+/* fsqrt - fsqrt. */
+uint64_t helper_fsqrt (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) {
+ /* Square root of a negative nonzero number */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN square root */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg.d = float64_sqrt(farg.d, &env->fp_status);
+ }
+ return farg.ll;
+}
+
+/* fre - fre. */
+uint64_t helper_fre (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN reciprocal */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg.d = float64_div(float64_one, farg.d, &env->fp_status);
+ return farg.d;
+}
+
+/* fres - fres. */
+uint64_t helper_fres (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ float32 f32;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN reciprocal */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg.d = float64_div(float64_one, farg.d, &env->fp_status);
+ f32 = float64_to_float32(farg.d, &env->fp_status);
+ farg.d = float32_to_float64(f32, &env->fp_status);
+
+ return farg.ll;
+}
+
+/* frsqrte - frsqrte. */
+uint64_t helper_frsqrte (uint64_t arg)
+{
+ CPU_DoubleU farg;
+ float32 f32;
+ farg.ll = arg;
+
+ if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) {
+ /* Reciprocal square root of a negative nonzero number */
+ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT);
+ } else {
+ if (unlikely(float64_is_signaling_nan(farg.d))) {
+ /* sNaN reciprocal square root */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+ farg.d = float64_sqrt(farg.d, &env->fp_status);
+ farg.d = float64_div(float64_one, farg.d, &env->fp_status);
+ f32 = float64_to_float32(farg.d, &env->fp_status);
+ farg.d = float32_to_float64(f32, &env->fp_status);
+ }
+ return farg.ll;
+}
+
+/* fsel - fsel. */
+uint64_t helper_fsel (uint64_t arg1, uint64_t arg2, uint64_t arg3)
+{
+ CPU_DoubleU farg1;
+
+ farg1.ll = arg1;
+
+ if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_any_nan(farg1.d)) {
+ return arg2;
+ } else {
+ return arg3;
+ }
+}
+
+void helper_fcmpu (uint64_t arg1, uint64_t arg2, uint32_t crfD)
+{
+ CPU_DoubleU farg1, farg2;
+ uint32_t ret = 0;
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+
+ if (unlikely(float64_is_any_nan(farg1.d) ||
+ float64_is_any_nan(farg2.d))) {
+ ret = 0x01UL;
+ } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) {
+ ret = 0x08UL;
+ } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) {
+ ret = 0x04UL;
+ } else {
+ ret = 0x02UL;
+ }
+
+ env->fpscr &= ~(0x0F << FPSCR_FPRF);
+ env->fpscr |= ret << FPSCR_FPRF;
+ env->crf[crfD] = ret;
+ if (unlikely(ret == 0x01UL
+ && (float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d)))) {
+ /* sNaN comparison */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+ }
+}
+
+void helper_fcmpo (uint64_t arg1, uint64_t arg2, uint32_t crfD)
+{
+ CPU_DoubleU farg1, farg2;
+ uint32_t ret = 0;
+ farg1.ll = arg1;
+ farg2.ll = arg2;
+
+ if (unlikely(float64_is_any_nan(farg1.d) ||
+ float64_is_any_nan(farg2.d))) {
+ ret = 0x01UL;
+ } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) {
+ ret = 0x08UL;
+ } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) {
+ ret = 0x04UL;
+ } else {
+ ret = 0x02UL;
+ }
+
+ env->fpscr &= ~(0x0F << FPSCR_FPRF);
+ env->fpscr |= ret << FPSCR_FPRF;
+ env->crf[crfD] = ret;
+ if (unlikely (ret == 0x01UL)) {
+ if (float64_is_signaling_nan(farg1.d) ||
+ float64_is_signaling_nan(farg2.d)) {
+ /* sNaN comparison */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN |
+ POWERPC_EXCP_FP_VXVC);
+ } else {
+ /* qNaN comparison */
+ fload_invalid_op_excp(POWERPC_EXCP_FP_VXVC);
+ }
+ }
+}
+
+#if !defined (CONFIG_USER_ONLY)
+void helper_store_msr (target_ulong val)
+{
+ val = hreg_store_msr(env, val, 0);
+ if (val != 0) {
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ helper_raise_exception(val);
+ }
+}
+
+static inline void do_rfi(target_ulong nip, target_ulong msr,
+ target_ulong msrm, int keep_msrh)
+{
+#if defined(TARGET_PPC64)
+ if (msr & (1ULL << MSR_SF)) {
+ nip = (uint64_t)nip;
+ msr &= (uint64_t)msrm;
+ } else {
+ nip = (uint32_t)nip;
+ msr = (uint32_t)(msr & msrm);
+ if (keep_msrh)
+ msr |= env->msr & ~((uint64_t)0xFFFFFFFF);
+ }
+#else
+ nip = (uint32_t)nip;
+ msr &= (uint32_t)msrm;
+#endif
+ /* XXX: beware: this is false if VLE is supported */
+ env->nip = nip & ~((target_ulong)0x00000003);
+ hreg_store_msr(env, msr, 1);
+#if defined (DEBUG_OP)
+ cpu_dump_rfi(env->nip, env->msr);
+#endif
+ /* No need to raise an exception here,
+ * as rfi is always the last insn of a TB
+ */
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+void helper_rfi (void)
+{
+ do_rfi(env->spr[SPR_SRR0], env->spr[SPR_SRR1],
+ ~((target_ulong)0x783F0000), 1);
+}
+
+#if defined(TARGET_PPC64)
+void helper_rfid (void)
+{
+ do_rfi(env->spr[SPR_SRR0], env->spr[SPR_SRR1],
+ ~((target_ulong)0x783F0000), 0);
+}
+
+void helper_hrfid (void)
+{
+ do_rfi(env->spr[SPR_HSRR0], env->spr[SPR_HSRR1],
+ ~((target_ulong)0x783F0000), 0);
+}
+#endif
+#endif
+
+void helper_tw (target_ulong arg1, target_ulong arg2, uint32_t flags)
+{
+ if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) ||
+ ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) ||
+ ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) ||
+ ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) ||
+ ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) {
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP);
+ }
+}
+
+#if defined(TARGET_PPC64)
+void helper_td (target_ulong arg1, target_ulong arg2, uint32_t flags)
+{
+ if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) ||
+ ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) ||
+ ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) ||
+ ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) ||
+ ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01)))))
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP);
+}
+#endif
+
+/*****************************************************************************/
+/* PowerPC 601 specific instructions (POWER bridge) */
+
+target_ulong helper_clcs (uint32_t arg)
+{
+ switch (arg) {
+ case 0x0CUL:
+ /* Instruction cache line size */
+ return env->icache_line_size;
+ break;
+ case 0x0DUL:
+ /* Data cache line size */
+ return env->dcache_line_size;
+ break;
+ case 0x0EUL:
+ /* Minimum cache line size */
+ return (env->icache_line_size < env->dcache_line_size) ?
+ env->icache_line_size : env->dcache_line_size;
+ break;
+ case 0x0FUL:
+ /* Maximum cache line size */
+ return (env->icache_line_size > env->dcache_line_size) ?
+ env->icache_line_size : env->dcache_line_size;
+ break;
+ default:
+ /* Undefined */
+ return 0;
+ break;
+ }
+}
+
+target_ulong helper_div (target_ulong arg1, target_ulong arg2)
+{
+ uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ];
+
+ if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) ||
+ (int32_t)arg2 == 0) {
+ env->spr[SPR_MQ] = 0;
+ return INT32_MIN;
+ } else {
+ env->spr[SPR_MQ] = tmp % arg2;
+ return tmp / (int32_t)arg2;
+ }
+}
+
+target_ulong helper_divo (target_ulong arg1, target_ulong arg2)
+{
+ uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ];
+
+ if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) ||
+ (int32_t)arg2 == 0) {
+ env->xer |= (1 << XER_OV) | (1 << XER_SO);
+ env->spr[SPR_MQ] = 0;
+ return INT32_MIN;
+ } else {
+ env->spr[SPR_MQ] = tmp % arg2;
+ tmp /= (int32_t)arg2;
+ if ((int32_t)tmp != tmp) {
+ env->xer |= (1 << XER_OV) | (1 << XER_SO);
+ } else {
+ env->xer &= ~(1 << XER_OV);
+ }
+ return tmp;
+ }
+}
+
+target_ulong helper_divs (target_ulong arg1, target_ulong arg2)
+{
+ if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) ||
+ (int32_t)arg2 == 0) {
+ env->spr[SPR_MQ] = 0;
+ return INT32_MIN;
+ } else {
+ env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2;
+ return (int32_t)arg1 / (int32_t)arg2;
+ }
+}
+
+target_ulong helper_divso (target_ulong arg1, target_ulong arg2)
+{
+ if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) ||
+ (int32_t)arg2 == 0) {
+ env->xer |= (1 << XER_OV) | (1 << XER_SO);
+ env->spr[SPR_MQ] = 0;
+ return INT32_MIN;
+ } else {
+ env->xer &= ~(1 << XER_OV);
+ env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2;
+ return (int32_t)arg1 / (int32_t)arg2;
+ }
+}
+
+#if !defined (CONFIG_USER_ONLY)
+target_ulong helper_rac (target_ulong addr)
+{
+ mmu_ctx_t ctx;
+ int nb_BATs;
+ target_ulong ret = 0;
+
+ /* We don't have to generate many instances of this instruction,
+ * as rac is supervisor only.
+ */
+ /* XXX: FIX THIS: Pretend we have no BAT */
+ nb_BATs = env->nb_BATs;
+ env->nb_BATs = 0;
+ if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0)
+ ret = ctx.raddr;
+ env->nb_BATs = nb_BATs;
+ return ret;
+}
+
+void helper_rfsvc (void)
+{
+ do_rfi(env->lr, env->ctr, 0x0000FFFF, 0);
+}
+#endif
+
+/*****************************************************************************/
+/* 602 specific instructions */
+/* mfrom is the most crazy instruction ever seen, imho ! */
+/* Real implementation uses a ROM table. Do the same */
+/* Extremly decomposed:
+ * -arg / 256
+ * return 256 * log10(10 + 1.0) + 0.5
+ */
+#if !defined (CONFIG_USER_ONLY)
+target_ulong helper_602_mfrom (target_ulong arg)
+{
+ if (likely(arg < 602)) {
+#include "mfrom_table.c"
+ return mfrom_ROM_table[arg];
+ } else {
+ return 0;
+ }
+}
+#endif
+
+/*****************************************************************************/
+/* Embedded PowerPC specific helpers */
+
+/* XXX: to be improved to check access rights when in user-mode */
+target_ulong helper_load_dcr (target_ulong dcrn)
+{
+ uint32_t val = 0;
+
+ if (unlikely(env->dcr_env == NULL)) {
+ qemu_log("No DCR environment\n");
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM,
+ POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL);
+ } else if (unlikely(ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val) != 0)) {
+ qemu_log("DCR read error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn);
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM,
+ POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG);
+ }
+ return val;
+}
+
+void helper_store_dcr (target_ulong dcrn, target_ulong val)
+{
+ if (unlikely(env->dcr_env == NULL)) {
+ qemu_log("No DCR environment\n");
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM,
+ POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL);
+ } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val) != 0)) {
+ qemu_log("DCR write error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn);
+ helper_raise_exception_err(POWERPC_EXCP_PROGRAM,
+ POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG);
+ }
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void helper_40x_rfci (void)
+{
+ do_rfi(env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3],
+ ~((target_ulong)0xFFFF0000), 0);
+}
+
+void helper_rfci (void)
+{
+ do_rfi(env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1,
+ ~((target_ulong)0x3FFF0000), 0);
+}
+
+void helper_rfdi (void)
+{
+ do_rfi(env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1,
+ ~((target_ulong)0x3FFF0000), 0);
+}
+
+void helper_rfmci (void)
+{
+ do_rfi(env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1,
+ ~((target_ulong)0x3FFF0000), 0);
+}
+#endif
+
+/* 440 specific */
+target_ulong helper_dlmzb (target_ulong high, target_ulong low, uint32_t update_Rc)
+{
+ target_ulong mask;
+ int i;
+
+ i = 1;
+ for (mask = 0xFF000000; mask != 0; mask = mask >> 8) {
+ if ((high & mask) == 0) {
+ if (update_Rc) {
+ env->crf[0] = 0x4;
+ }
+ goto done;
+ }
+ i++;
+ }
+ for (mask = 0xFF000000; mask != 0; mask = mask >> 8) {
+ if ((low & mask) == 0) {
+ if (update_Rc) {
+ env->crf[0] = 0x8;
+ }
+ goto done;
+ }
+ i++;
+ }
+ if (update_Rc) {
+ env->crf[0] = 0x2;
+ }
+ done:
+ env->xer = (env->xer & ~0x7F) | i;
+ if (update_Rc) {
+ env->crf[0] |= xer_so;
+ }
+ return i;
+}
+
+/*****************************************************************************/
+/* Altivec extension helpers */
+#if defined(HOST_WORDS_BIGENDIAN)
+#define HI_IDX 0
+#define LO_IDX 1
+#else
+#define HI_IDX 1
+#define LO_IDX 0
+#endif
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define VECTOR_FOR_INORDER_I(index, element) \
+ for (index = 0; index < ARRAY_SIZE(r->element); index++)
+#else
+#define VECTOR_FOR_INORDER_I(index, element) \
+ for (index = ARRAY_SIZE(r->element)-1; index >= 0; index--)
+#endif
+
+/* If X is a NaN, store the corresponding QNaN into RESULT. Otherwise,
+ * execute the following block. */
+#define DO_HANDLE_NAN(result, x) \
+ if (float32_is_any_nan(x)) { \
+ CPU_FloatU __f; \
+ __f.f = x; \
+ __f.l = __f.l | (1 << 22); /* Set QNaN bit. */ \
+ result = __f.f; \
+ } else
+
+#define HANDLE_NAN1(result, x) \
+ DO_HANDLE_NAN(result, x)
+#define HANDLE_NAN2(result, x, y) \
+ DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y)
+#define HANDLE_NAN3(result, x, y, z) \
+ DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) DO_HANDLE_NAN(result, z)
+
+/* Saturating arithmetic helpers. */
+#define SATCVT(from, to, from_type, to_type, min, max) \
+ static inline to_type cvt##from##to(from_type x, int *sat) \
+ { \
+ to_type r; \
+ if (x < (from_type)min) { \
+ r = min; \
+ *sat = 1; \
+ } else if (x > (from_type)max) { \
+ r = max; \
+ *sat = 1; \
+ } else { \
+ r = x; \
+ } \
+ return r; \
+ }
+#define SATCVTU(from, to, from_type, to_type, min, max) \
+ static inline to_type cvt##from##to(from_type x, int *sat) \
+ { \
+ to_type r; \
+ if (x > (from_type)max) { \
+ r = max; \
+ *sat = 1; \
+ } else { \
+ r = x; \
+ } \
+ return r; \
+ }
+SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX)
+SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX)
+SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX)
+
+SATCVTU(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX)
+SATCVTU(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX)
+SATCVTU(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX)
+SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX)
+SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX)
+SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX)
+#undef SATCVT
+#undef SATCVTU
+
+#define LVE(name, access, swap, element) \
+ void helper_##name (ppc_avr_t *r, target_ulong addr) \
+ { \
+ size_t n_elems = ARRAY_SIZE(r->element); \
+ int adjust = HI_IDX*(n_elems-1); \
+ int sh = sizeof(r->element[0]) >> 1; \
+ int index = (addr & 0xf) >> sh; \
+ if(msr_le) { \
+ r->element[LO_IDX ? index : (adjust - index)] = swap(access(addr)); \
+ } else { \
+ r->element[LO_IDX ? index : (adjust - index)] = access(addr); \
+ } \
+ }
+#define I(x) (x)
+LVE(lvebx, ldub, I, u8)
+LVE(lvehx, lduw, bswap16, u16)
+LVE(lvewx, ldl, bswap32, u32)
+#undef I
+#undef LVE
+
+void helper_lvsl (ppc_avr_t *r, target_ulong sh)
+{
+ int i, j = (sh & 0xf);
+
+ VECTOR_FOR_INORDER_I (i, u8) {
+ r->u8[i] = j++;
+ }
+}
+
+void helper_lvsr (ppc_avr_t *r, target_ulong sh)
+{
+ int i, j = 0x10 - (sh & 0xf);
+
+ VECTOR_FOR_INORDER_I (i, u8) {
+ r->u8[i] = j++;
+ }
+}
+
+#define STVE(name, access, swap, element) \
+ void helper_##name (ppc_avr_t *r, target_ulong addr) \
+ { \
+ size_t n_elems = ARRAY_SIZE(r->element); \
+ int adjust = HI_IDX*(n_elems-1); \
+ int sh = sizeof(r->element[0]) >> 1; \
+ int index = (addr & 0xf) >> sh; \
+ if(msr_le) { \
+ access(addr, swap(r->element[LO_IDX ? index : (adjust - index)])); \
+ } else { \
+ access(addr, r->element[LO_IDX ? index : (adjust - index)]); \
+ } \
+ }
+#define I(x) (x)
+STVE(stvebx, stb, I, u8)
+STVE(stvehx, stw, bswap16, u16)
+STVE(stvewx, stl, bswap32, u32)
+#undef I
+#undef LVE
+
+void helper_mtvscr (ppc_avr_t *r)
+{
+#if defined(HOST_WORDS_BIGENDIAN)
+ env->vscr = r->u32[3];
+#else
+ env->vscr = r->u32[0];
+#endif
+ set_flush_to_zero(vscr_nj, &env->vec_status);
+}
+
+void helper_vaddcuw (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
+ r->u32[i] = ~a->u32[i] < b->u32[i];
+ }
+}
+
+#define VARITH_DO(name, op, element) \
+void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+{ \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ r->element[i] = a->element[i] op b->element[i]; \
+ } \
+}
+#define VARITH(suffix, element) \
+ VARITH_DO(add##suffix, +, element) \
+ VARITH_DO(sub##suffix, -, element)
+VARITH(ubm, u8)
+VARITH(uhm, u16)
+VARITH(uwm, u32)
+#undef VARITH_DO
+#undef VARITH
+
+#define VARITHFP(suffix, func) \
+ void helper_v##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \
+ HANDLE_NAN2(r->f[i], a->f[i], b->f[i]) { \
+ r->f[i] = func(a->f[i], b->f[i], &env->vec_status); \
+ } \
+ } \
+ }
+VARITHFP(addfp, float32_add)
+VARITHFP(subfp, float32_sub)
+#undef VARITHFP
+
+#define VARITHSAT_CASE(type, op, cvt, element) \
+ { \
+ type result = (type)a->element[i] op (type)b->element[i]; \
+ r->element[i] = cvt(result, &sat); \
+ }
+
+#define VARITHSAT_DO(name, op, optype, cvt, element) \
+ void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int sat = 0; \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ switch (sizeof(r->element[0])) { \
+ case 1: VARITHSAT_CASE(optype, op, cvt, element); break; \
+ case 2: VARITHSAT_CASE(optype, op, cvt, element); break; \
+ case 4: VARITHSAT_CASE(optype, op, cvt, element); break; \
+ } \
+ } \
+ if (sat) { \
+ env->vscr |= (1 << VSCR_SAT); \
+ } \
+ }
+#define VARITHSAT_SIGNED(suffix, element, optype, cvt) \
+ VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \
+ VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element)
+#define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \
+ VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \
+ VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element)
+VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb)
+VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh)
+VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw)
+VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub)
+VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh)
+VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw)
+#undef VARITHSAT_CASE
+#undef VARITHSAT_DO
+#undef VARITHSAT_SIGNED
+#undef VARITHSAT_UNSIGNED
+
+#define VAVG_DO(name, element, etype) \
+ void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ etype x = (etype)a->element[i] + (etype)b->element[i] + 1; \
+ r->element[i] = x >> 1; \
+ } \
+ }
+
+#define VAVG(type, signed_element, signed_type, unsigned_element, unsigned_type) \
+ VAVG_DO(avgs##type, signed_element, signed_type) \
+ VAVG_DO(avgu##type, unsigned_element, unsigned_type)
+VAVG(b, s8, int16_t, u8, uint16_t)
+VAVG(h, s16, int32_t, u16, uint32_t)
+VAVG(w, s32, int64_t, u32, uint64_t)
+#undef VAVG_DO
+#undef VAVG
+
+#define VCF(suffix, cvt, element) \
+ void helper_vcf##suffix (ppc_avr_t *r, ppc_avr_t *b, uint32_t uim) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \
+ float32 t = cvt(b->element[i], &env->vec_status); \
+ r->f[i] = float32_scalbn (t, -uim, &env->vec_status); \
+ } \
+ }
+VCF(ux, uint32_to_float32, u32)
+VCF(sx, int32_to_float32, s32)
+#undef VCF
+
+#define VCMP_DO(suffix, compare, element, record) \
+ void helper_vcmp##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ uint32_t ones = (uint32_t)-1; \
+ uint32_t all = ones; \
+ uint32_t none = 0; \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ uint32_t result = (a->element[i] compare b->element[i] ? ones : 0x0); \
+ switch (sizeof (a->element[0])) { \
+ case 4: r->u32[i] = result; break; \
+ case 2: r->u16[i] = result; break; \
+ case 1: r->u8[i] = result; break; \
+ } \
+ all &= result; \
+ none |= result; \
+ } \
+ if (record) { \
+ env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \
+ } \
+ }
+#define VCMP(suffix, compare, element) \
+ VCMP_DO(suffix, compare, element, 0) \
+ VCMP_DO(suffix##_dot, compare, element, 1)
+VCMP(equb, ==, u8)
+VCMP(equh, ==, u16)
+VCMP(equw, ==, u32)
+VCMP(gtub, >, u8)
+VCMP(gtuh, >, u16)
+VCMP(gtuw, >, u32)
+VCMP(gtsb, >, s8)
+VCMP(gtsh, >, s16)
+VCMP(gtsw, >, s32)
+#undef VCMP_DO
+#undef VCMP
+
+#define VCMPFP_DO(suffix, compare, order, record) \
+ void helper_vcmp##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ uint32_t ones = (uint32_t)-1; \
+ uint32_t all = ones; \
+ uint32_t none = 0; \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \
+ uint32_t result; \
+ int rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); \
+ if (rel == float_relation_unordered) { \
+ result = 0; \
+ } else if (rel compare order) { \
+ result = ones; \
+ } else { \
+ result = 0; \
+ } \
+ r->u32[i] = result; \
+ all &= result; \
+ none |= result; \
+ } \
+ if (record) { \
+ env->crf[6] = ((all != 0) << 3) | ((none == 0) << 1); \
+ } \
+ }
+#define VCMPFP(suffix, compare, order) \
+ VCMPFP_DO(suffix, compare, order, 0) \
+ VCMPFP_DO(suffix##_dot, compare, order, 1)
+VCMPFP(eqfp, ==, float_relation_equal)
+VCMPFP(gefp, !=, float_relation_less)
+VCMPFP(gtfp, ==, float_relation_greater)
+#undef VCMPFP_DO
+#undef VCMPFP
+
+static inline void vcmpbfp_internal(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b,
+ int record)
+{
+ int i;
+ int all_in = 0;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ int le_rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status);
+ if (le_rel == float_relation_unordered) {
+ r->u32[i] = 0xc0000000;
+ /* ALL_IN does not need to be updated here. */
+ } else {
+ float32 bneg = float32_chs(b->f[i]);
+ int ge_rel = float32_compare_quiet(a->f[i], bneg, &env->vec_status);
+ int le = le_rel != float_relation_greater;
+ int ge = ge_rel != float_relation_less;
+ r->u32[i] = ((!le) << 31) | ((!ge) << 30);
+ all_in |= (!le | !ge);
+ }
+ }
+ if (record) {
+ env->crf[6] = (all_in == 0) << 1;
+ }
+}
+
+void helper_vcmpbfp (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ vcmpbfp_internal(r, a, b, 0);
+}
+
+void helper_vcmpbfp_dot (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ vcmpbfp_internal(r, a, b, 1);
+}
+
+#define VCT(suffix, satcvt, element) \
+ void helper_vct##suffix (ppc_avr_t *r, ppc_avr_t *b, uint32_t uim) \
+ { \
+ int i; \
+ int sat = 0; \
+ float_status s = env->vec_status; \
+ set_float_rounding_mode(float_round_to_zero, &s); \
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \
+ if (float32_is_any_nan(b->f[i])) { \
+ r->element[i] = 0; \
+ } else { \
+ float64 t = float32_to_float64(b->f[i], &s); \
+ int64_t j; \
+ t = float64_scalbn(t, uim, &s); \
+ j = float64_to_int64(t, &s); \
+ r->element[i] = satcvt(j, &sat); \
+ } \
+ } \
+ if (sat) { \
+ env->vscr |= (1 << VSCR_SAT); \
+ } \
+ }
+VCT(uxs, cvtsduw, u32)
+VCT(sxs, cvtsdsw, s32)
+#undef VCT
+
+void helper_vmaddfp (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN3(r->f[i], a->f[i], b->f[i], c->f[i]) {
+ /* Need to do the computation in higher precision and round
+ * once at the end. */
+ float64 af, bf, cf, t;
+ af = float32_to_float64(a->f[i], &env->vec_status);
+ bf = float32_to_float64(b->f[i], &env->vec_status);
+ cf = float32_to_float64(c->f[i], &env->vec_status);
+ t = float64_mul(af, cf, &env->vec_status);
+ t = float64_add(t, bf, &env->vec_status);
+ r->f[i] = float64_to_float32(t, &env->vec_status);
+ }
+ }
+}
+
+void helper_vmhaddshs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int sat = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->s16); i++) {
+ int32_t prod = a->s16[i] * b->s16[i];
+ int32_t t = (int32_t)c->s16[i] + (prod >> 15);
+ r->s16[i] = cvtswsh (t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+void helper_vmhraddshs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int sat = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->s16); i++) {
+ int32_t prod = a->s16[i] * b->s16[i] + 0x00004000;
+ int32_t t = (int32_t)c->s16[i] + (prod >> 15);
+ r->s16[i] = cvtswsh (t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+#define VMINMAX_DO(name, compare, element) \
+ void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ if (a->element[i] compare b->element[i]) { \
+ r->element[i] = b->element[i]; \
+ } else { \
+ r->element[i] = a->element[i]; \
+ } \
+ } \
+ }
+#define VMINMAX(suffix, element) \
+ VMINMAX_DO(min##suffix, >, element) \
+ VMINMAX_DO(max##suffix, <, element)
+VMINMAX(sb, s8)
+VMINMAX(sh, s16)
+VMINMAX(sw, s32)
+VMINMAX(ub, u8)
+VMINMAX(uh, u16)
+VMINMAX(uw, u32)
+#undef VMINMAX_DO
+#undef VMINMAX
+
+#define VMINMAXFP(suffix, rT, rF) \
+ void helper_v##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \
+ HANDLE_NAN2(r->f[i], a->f[i], b->f[i]) { \
+ if (float32_lt_quiet(a->f[i], b->f[i], &env->vec_status)) { \
+ r->f[i] = rT->f[i]; \
+ } else { \
+ r->f[i] = rF->f[i]; \
+ } \
+ } \
+ } \
+ }
+VMINMAXFP(minfp, a, b)
+VMINMAXFP(maxfp, b, a)
+#undef VMINMAXFP
+
+void helper_vmladduhm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->s16); i++) {
+ int32_t prod = a->s16[i] * b->s16[i];
+ r->s16[i] = (int16_t) (prod + c->s16[i]);
+ }
+}
+
+#define VMRG_DO(name, element, highp) \
+ void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ ppc_avr_t result; \
+ int i; \
+ size_t n_elems = ARRAY_SIZE(r->element); \
+ for (i = 0; i < n_elems/2; i++) { \
+ if (highp) { \
+ result.element[i*2+HI_IDX] = a->element[i]; \
+ result.element[i*2+LO_IDX] = b->element[i]; \
+ } else { \
+ result.element[n_elems - i*2 - (1+HI_IDX)] = b->element[n_elems - i - 1]; \
+ result.element[n_elems - i*2 - (1+LO_IDX)] = a->element[n_elems - i - 1]; \
+ } \
+ } \
+ *r = result; \
+ }
+#if defined(HOST_WORDS_BIGENDIAN)
+#define MRGHI 0
+#define MRGLO 1
+#else
+#define MRGHI 1
+#define MRGLO 0
+#endif
+#define VMRG(suffix, element) \
+ VMRG_DO(mrgl##suffix, element, MRGHI) \
+ VMRG_DO(mrgh##suffix, element, MRGLO)
+VMRG(b, u8)
+VMRG(h, u16)
+VMRG(w, u32)
+#undef VMRG_DO
+#undef VMRG
+#undef MRGHI
+#undef MRGLO
+
+void helper_vmsummbm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int32_t prod[16];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->s8); i++) {
+ prod[i] = (int32_t)a->s8[i] * b->u8[i];
+ }
+
+ VECTOR_FOR_INORDER_I(i, s32) {
+ r->s32[i] = c->s32[i] + prod[4*i] + prod[4*i+1] + prod[4*i+2] + prod[4*i+3];
+ }
+}
+
+void helper_vmsumshm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int32_t prod[8];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->s16); i++) {
+ prod[i] = a->s16[i] * b->s16[i];
+ }
+
+ VECTOR_FOR_INORDER_I(i, s32) {
+ r->s32[i] = c->s32[i] + prod[2*i] + prod[2*i+1];
+ }
+}
+
+void helper_vmsumshs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int32_t prod[8];
+ int i;
+ int sat = 0;
+
+ for (i = 0; i < ARRAY_SIZE(r->s16); i++) {
+ prod[i] = (int32_t)a->s16[i] * b->s16[i];
+ }
+
+ VECTOR_FOR_INORDER_I (i, s32) {
+ int64_t t = (int64_t)c->s32[i] + prod[2*i] + prod[2*i+1];
+ r->u32[i] = cvtsdsw(t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+void helper_vmsumubm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ uint16_t prod[16];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->u8); i++) {
+ prod[i] = a->u8[i] * b->u8[i];
+ }
+
+ VECTOR_FOR_INORDER_I(i, u32) {
+ r->u32[i] = c->u32[i] + prod[4*i] + prod[4*i+1] + prod[4*i+2] + prod[4*i+3];
+ }
+}
+
+void helper_vmsumuhm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ uint32_t prod[8];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->u16); i++) {
+ prod[i] = a->u16[i] * b->u16[i];
+ }
+
+ VECTOR_FOR_INORDER_I(i, u32) {
+ r->u32[i] = c->u32[i] + prod[2*i] + prod[2*i+1];
+ }
+}
+
+void helper_vmsumuhs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ uint32_t prod[8];
+ int i;
+ int sat = 0;
+
+ for (i = 0; i < ARRAY_SIZE(r->u16); i++) {
+ prod[i] = a->u16[i] * b->u16[i];
+ }
+
+ VECTOR_FOR_INORDER_I (i, s32) {
+ uint64_t t = (uint64_t)c->u32[i] + prod[2*i] + prod[2*i+1];
+ r->u32[i] = cvtuduw(t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+#define VMUL_DO(name, mul_element, prod_element, evenp) \
+ void helper_v##name (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ VECTOR_FOR_INORDER_I(i, prod_element) { \
+ if (evenp) { \
+ r->prod_element[i] = a->mul_element[i*2+HI_IDX] * b->mul_element[i*2+HI_IDX]; \
+ } else { \
+ r->prod_element[i] = a->mul_element[i*2+LO_IDX] * b->mul_element[i*2+LO_IDX]; \
+ } \
+ } \
+ }
+#define VMUL(suffix, mul_element, prod_element) \
+ VMUL_DO(mule##suffix, mul_element, prod_element, 1) \
+ VMUL_DO(mulo##suffix, mul_element, prod_element, 0)
+VMUL(sb, s8, s16)
+VMUL(sh, s16, s32)
+VMUL(ub, u8, u16)
+VMUL(uh, u16, u32)
+#undef VMUL_DO
+#undef VMUL
+
+void helper_vnmsubfp (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN3(r->f[i], a->f[i], b->f[i], c->f[i]) {
+ /* Need to do the computation is higher precision and round
+ * once at the end. */
+ float64 af, bf, cf, t;
+ af = float32_to_float64(a->f[i], &env->vec_status);
+ bf = float32_to_float64(b->f[i], &env->vec_status);
+ cf = float32_to_float64(c->f[i], &env->vec_status);
+ t = float64_mul(af, cf, &env->vec_status);
+ t = float64_sub(t, bf, &env->vec_status);
+ t = float64_chs(t);
+ r->f[i] = float64_to_float32(t, &env->vec_status);
+ }
+ }
+}
+
+void helper_vperm (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ ppc_avr_t result;
+ int i;
+ VECTOR_FOR_INORDER_I (i, u8) {
+ int s = c->u8[i] & 0x1f;
+#if defined(HOST_WORDS_BIGENDIAN)
+ int index = s & 0xf;
+#else
+ int index = 15 - (s & 0xf);
+#endif
+ if (s & 0x10) {
+ result.u8[i] = b->u8[index];
+ } else {
+ result.u8[i] = a->u8[index];
+ }
+ }
+ *r = result;
+}
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define PKBIG 1
+#else
+#define PKBIG 0
+#endif
+void helper_vpkpx (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int i, j;
+ ppc_avr_t result;
+#if defined(HOST_WORDS_BIGENDIAN)
+ const ppc_avr_t *x[2] = { a, b };
+#else
+ const ppc_avr_t *x[2] = { b, a };
+#endif
+
+ VECTOR_FOR_INORDER_I (i, u64) {
+ VECTOR_FOR_INORDER_I (j, u32){
+ uint32_t e = x[i]->u32[j];
+ result.u16[4*i+j] = (((e >> 9) & 0xfc00) |
+ ((e >> 6) & 0x3e0) |
+ ((e >> 3) & 0x1f));
+ }
+ }
+ *r = result;
+}
+
+#define VPK(suffix, from, to, cvt, dosat) \
+ void helper_vpk##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ int sat = 0; \
+ ppc_avr_t result; \
+ ppc_avr_t *a0 = PKBIG ? a : b; \
+ ppc_avr_t *a1 = PKBIG ? b : a; \
+ VECTOR_FOR_INORDER_I (i, from) { \
+ result.to[i] = cvt(a0->from[i], &sat); \
+ result.to[i+ARRAY_SIZE(r->from)] = cvt(a1->from[i], &sat); \
+ } \
+ *r = result; \
+ if (dosat && sat) { \
+ env->vscr |= (1 << VSCR_SAT); \
+ } \
+ }
+#define I(x, y) (x)
+VPK(shss, s16, s8, cvtshsb, 1)
+VPK(shus, s16, u8, cvtshub, 1)
+VPK(swss, s32, s16, cvtswsh, 1)
+VPK(swus, s32, u16, cvtswuh, 1)
+VPK(uhus, u16, u8, cvtuhub, 1)
+VPK(uwus, u32, u16, cvtuwuh, 1)
+VPK(uhum, u16, u8, I, 0)
+VPK(uwum, u32, u16, I, 0)
+#undef I
+#undef VPK
+#undef PKBIG
+
+void helper_vrefp (ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN1(r->f[i], b->f[i]) {
+ r->f[i] = float32_div(float32_one, b->f[i], &env->vec_status);
+ }
+ }
+}
+
+#define VRFI(suffix, rounding) \
+ void helper_vrfi##suffix (ppc_avr_t *r, ppc_avr_t *b) \
+ { \
+ int i; \
+ float_status s = env->vec_status; \
+ set_float_rounding_mode(rounding, &s); \
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \
+ HANDLE_NAN1(r->f[i], b->f[i]) { \
+ r->f[i] = float32_round_to_int (b->f[i], &s); \
+ } \
+ } \
+ }
+VRFI(n, float_round_nearest_even)
+VRFI(m, float_round_down)
+VRFI(p, float_round_up)
+VRFI(z, float_round_to_zero)
+#undef VRFI
+
+#define VROTATE(suffix, element) \
+ void helper_vrl##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ unsigned int mask = ((1 << (3 + (sizeof (a->element[0]) >> 1))) - 1); \
+ unsigned int shift = b->element[i] & mask; \
+ r->element[i] = (a->element[i] << shift) | (a->element[i] >> (sizeof(a->element[0]) * 8 - shift)); \
+ } \
+ }
+VROTATE(b, u8)
+VROTATE(h, u16)
+VROTATE(w, u32)
+#undef VROTATE
+
+void helper_vrsqrtefp (ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN1(r->f[i], b->f[i]) {
+ float32 t = float32_sqrt(b->f[i], &env->vec_status);
+ r->f[i] = float32_div(float32_one, t, &env->vec_status);
+ }
+ }
+}
+
+void helper_vsel (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c)
+{
+ r->u64[0] = (a->u64[0] & ~c->u64[0]) | (b->u64[0] & c->u64[0]);
+ r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]);
+}
+
+void helper_vexptefp (ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN1(r->f[i], b->f[i]) {
+ r->f[i] = float32_exp2(b->f[i], &env->vec_status);
+ }
+ }
+}
+
+void helper_vlogefp (ppc_avr_t *r, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->f); i++) {
+ HANDLE_NAN1(r->f[i], b->f[i]) {
+ r->f[i] = float32_log2(b->f[i], &env->vec_status);
+ }
+ }
+}
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define LEFT 0
+#define RIGHT 1
+#else
+#define LEFT 1
+#define RIGHT 0
+#endif
+/* The specification says that the results are undefined if all of the
+ * shift counts are not identical. We check to make sure that they are
+ * to conform to what real hardware appears to do. */
+#define VSHIFT(suffix, leftp) \
+ void helper_vs##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int shift = b->u8[LO_IDX*15] & 0x7; \
+ int doit = 1; \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->u8); i++) { \
+ doit = doit && ((b->u8[i] & 0x7) == shift); \
+ } \
+ if (doit) { \
+ if (shift == 0) { \
+ *r = *a; \
+ } else if (leftp) { \
+ uint64_t carry = a->u64[LO_IDX] >> (64 - shift); \
+ r->u64[HI_IDX] = (a->u64[HI_IDX] << shift) | carry; \
+ r->u64[LO_IDX] = a->u64[LO_IDX] << shift; \
+ } else { \
+ uint64_t carry = a->u64[HI_IDX] << (64 - shift); \
+ r->u64[LO_IDX] = (a->u64[LO_IDX] >> shift) | carry; \
+ r->u64[HI_IDX] = a->u64[HI_IDX] >> shift; \
+ } \
+ } \
+ }
+VSHIFT(l, LEFT)
+VSHIFT(r, RIGHT)
+#undef VSHIFT
+#undef LEFT
+#undef RIGHT
+
+#define VSL(suffix, element) \
+ void helper_vsl##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ unsigned int mask = ((1 << (3 + (sizeof (a->element[0]) >> 1))) - 1); \
+ unsigned int shift = b->element[i] & mask; \
+ r->element[i] = a->element[i] << shift; \
+ } \
+ }
+VSL(b, u8)
+VSL(h, u16)
+VSL(w, u32)
+#undef VSL
+
+void helper_vsldoi (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift)
+{
+ int sh = shift & 0xf;
+ int i;
+ ppc_avr_t result;
+
+#if defined(HOST_WORDS_BIGENDIAN)
+ for (i = 0; i < ARRAY_SIZE(r->u8); i++) {
+ int index = sh + i;
+ if (index > 0xf) {
+ result.u8[i] = b->u8[index-0x10];
+ } else {
+ result.u8[i] = a->u8[index];
+ }
+ }
+#else
+ for (i = 0; i < ARRAY_SIZE(r->u8); i++) {
+ int index = (16 - sh) + i;
+ if (index > 0xf) {
+ result.u8[i] = a->u8[index-0x10];
+ } else {
+ result.u8[i] = b->u8[index];
+ }
+ }
+#endif
+ *r = result;
+}
+
+void helper_vslo (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf;
+
+#if defined (HOST_WORDS_BIGENDIAN)
+ memmove (&r->u8[0], &a->u8[sh], 16-sh);
+ memset (&r->u8[16-sh], 0, sh);
+#else
+ memmove (&r->u8[sh], &a->u8[0], 16-sh);
+ memset (&r->u8[0], 0, sh);
+#endif
+}
+
+/* Experimental testing shows that hardware masks the immediate. */
+#define _SPLAT_MASKED(element) (splat & (ARRAY_SIZE(r->element) - 1))
+#if defined(HOST_WORDS_BIGENDIAN)
+#define SPLAT_ELEMENT(element) _SPLAT_MASKED(element)
+#else
+#define SPLAT_ELEMENT(element) (ARRAY_SIZE(r->element)-1 - _SPLAT_MASKED(element))
+#endif
+#define VSPLT(suffix, element) \
+ void helper_vsplt##suffix (ppc_avr_t *r, ppc_avr_t *b, uint32_t splat) \
+ { \
+ uint32_t s = b->element[SPLAT_ELEMENT(element)]; \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ r->element[i] = s; \
+ } \
+ }
+VSPLT(b, u8)
+VSPLT(h, u16)
+VSPLT(w, u32)
+#undef VSPLT
+#undef SPLAT_ELEMENT
+#undef _SPLAT_MASKED
+
+#define VSPLTI(suffix, element, splat_type) \
+ void helper_vspltis##suffix (ppc_avr_t *r, uint32_t splat) \
+ { \
+ splat_type x = (int8_t)(splat << 3) >> 3; \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ r->element[i] = x; \
+ } \
+ }
+VSPLTI(b, s8, int8_t)
+VSPLTI(h, s16, int16_t)
+VSPLTI(w, s32, int32_t)
+#undef VSPLTI
+
+#define VSR(suffix, element) \
+ void helper_vsr##suffix (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \
+ { \
+ int i; \
+ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \
+ unsigned int mask = ((1 << (3 + (sizeof (a->element[0]) >> 1))) - 1); \
+ unsigned int shift = b->element[i] & mask; \
+ r->element[i] = a->element[i] >> shift; \
+ } \
+ }
+VSR(ab, s8)
+VSR(ah, s16)
+VSR(aw, s32)
+VSR(b, u8)
+VSR(h, u16)
+VSR(w, u32)
+#undef VSR
+
+void helper_vsro (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf;
+
+#if defined (HOST_WORDS_BIGENDIAN)
+ memmove (&r->u8[sh], &a->u8[0], 16-sh);
+ memset (&r->u8[0], 0, sh);
+#else
+ memmove (&r->u8[0], &a->u8[sh], 16-sh);
+ memset (&r->u8[16-sh], 0, sh);
+#endif
+}
+
+void helper_vsubcuw (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
+ r->u32[i] = a->u32[i] >= b->u32[i];
+ }
+}
+
+void helper_vsumsws (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int64_t t;
+ int i, upper;
+ ppc_avr_t result;
+ int sat = 0;
+
+#if defined(HOST_WORDS_BIGENDIAN)
+ upper = ARRAY_SIZE(r->s32)-1;
+#else
+ upper = 0;
+#endif
+ t = (int64_t)b->s32[upper];
+ for (i = 0; i < ARRAY_SIZE(r->s32); i++) {
+ t += a->s32[i];
+ result.s32[i] = 0;
+ }
+ result.s32[upper] = cvtsdsw(t, &sat);
+ *r = result;
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+void helper_vsum2sws (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int i, j, upper;
+ ppc_avr_t result;
+ int sat = 0;
+
+#if defined(HOST_WORDS_BIGENDIAN)
+ upper = 1;
+#else
+ upper = 0;
+#endif
+ for (i = 0; i < ARRAY_SIZE(r->u64); i++) {
+ int64_t t = (int64_t)b->s32[upper+i*2];
+ result.u64[i] = 0;
+ for (j = 0; j < ARRAY_SIZE(r->u64); j++) {
+ t += a->s32[2*i+j];
+ }
+ result.s32[upper+i*2] = cvtsdsw(t, &sat);
+ }
+
+ *r = result;
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+void helper_vsum4sbs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int i, j;
+ int sat = 0;
+
+ for (i = 0; i < ARRAY_SIZE(r->s32); i++) {
+ int64_t t = (int64_t)b->s32[i];
+ for (j = 0; j < ARRAY_SIZE(r->s32); j++) {
+ t += a->s8[4*i+j];
+ }
+ r->s32[i] = cvtsdsw(t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+void helper_vsum4shs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int sat = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(r->s32); i++) {
+ int64_t t = (int64_t)b->s32[i];
+ t += a->s16[2*i] + a->s16[2*i+1];
+ r->s32[i] = cvtsdsw(t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+void helper_vsum4ubs (ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b)
+{
+ int i, j;
+ int sat = 0;
+
+ for (i = 0; i < ARRAY_SIZE(r->u32); i++) {
+ uint64_t t = (uint64_t)b->u32[i];
+ for (j = 0; j < ARRAY_SIZE(r->u32); j++) {
+ t += a->u8[4*i+j];
+ }
+ r->u32[i] = cvtuduw(t, &sat);
+ }
+
+ if (sat) {
+ env->vscr |= (1 << VSCR_SAT);
+ }
+}
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define UPKHI 1
+#define UPKLO 0
+#else
+#define UPKHI 0
+#define UPKLO 1
+#endif
+#define VUPKPX(suffix, hi) \
+ void helper_vupk##suffix (ppc_avr_t *r, ppc_avr_t *b) \
+ { \
+ int i; \
+ ppc_avr_t result; \
+ for (i = 0; i < ARRAY_SIZE(r->u32); i++) { \
+ uint16_t e = b->u16[hi ? i : i+4]; \
+ uint8_t a = (e >> 15) ? 0xff : 0; \
+ uint8_t r = (e >> 10) & 0x1f; \
+ uint8_t g = (e >> 5) & 0x1f; \
+ uint8_t b = e & 0x1f; \
+ result.u32[i] = (a << 24) | (r << 16) | (g << 8) | b; \
+ } \
+ *r = result; \
+ }
+VUPKPX(lpx, UPKLO)
+VUPKPX(hpx, UPKHI)
+#undef VUPKPX
+
+#define VUPK(suffix, unpacked, packee, hi) \
+ void helper_vupk##suffix (ppc_avr_t *r, ppc_avr_t *b) \
+ { \
+ int i; \
+ ppc_avr_t result; \
+ if (hi) { \
+ for (i = 0; i < ARRAY_SIZE(r->unpacked); i++) { \
+ result.unpacked[i] = b->packee[i]; \
+ } \
+ } else { \
+ for (i = ARRAY_SIZE(r->unpacked); i < ARRAY_SIZE(r->packee); i++) { \
+ result.unpacked[i-ARRAY_SIZE(r->unpacked)] = b->packee[i]; \
+ } \
+ } \
+ *r = result; \
+ }
+VUPK(hsb, s16, s8, UPKHI)
+VUPK(hsh, s32, s16, UPKHI)
+VUPK(lsb, s16, s8, UPKLO)
+VUPK(lsh, s32, s16, UPKLO)
+#undef VUPK
+#undef UPKHI
+#undef UPKLO
+
+#undef DO_HANDLE_NAN
+#undef HANDLE_NAN1
+#undef HANDLE_NAN2
+#undef HANDLE_NAN3
+#undef VECTOR_FOR_INORDER_I
+#undef HI_IDX
+#undef LO_IDX
+
+/*****************************************************************************/
+/* SPE extension helpers */
+/* Use a table to make this quicker */
+static uint8_t hbrev[16] = {
+ 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE,
+ 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF,
+};
+
+static inline uint8_t byte_reverse(uint8_t val)
+{
+ return hbrev[val >> 4] | (hbrev[val & 0xF] << 4);
+}
+
+static inline uint32_t word_reverse(uint32_t val)
+{
+ return byte_reverse(val >> 24) | (byte_reverse(val >> 16) << 8) |
+ (byte_reverse(val >> 8) << 16) | (byte_reverse(val) << 24);
+}
+
+#define MASKBITS 16 // Random value - to be fixed (implementation dependant)
+target_ulong helper_brinc (target_ulong arg1, target_ulong arg2)
+{
+ uint32_t a, b, d, mask;
+
+ mask = UINT32_MAX >> (32 - MASKBITS);
+ a = arg1 & mask;
+ b = arg2 & mask;
+ d = word_reverse(1 + word_reverse(a | ~b));
+ return (arg1 & ~mask) | (d & b);
+}
+
+uint32_t helper_cntlsw32 (uint32_t val)
+{
+ if (val & 0x80000000)
+ return clz32(~val);
+ else
+ return clz32(val);
+}
+
+uint32_t helper_cntlzw32 (uint32_t val)
+{
+ return clz32(val);
+}
+
+/* Single-precision floating-point conversions */
+static inline uint32_t efscfsi(uint32_t val)
+{
+ CPU_FloatU u;
+
+ u.f = int32_to_float32(val, &env->vec_status);
+
+ return u.l;
+}
+
+static inline uint32_t efscfui(uint32_t val)
+{
+ CPU_FloatU u;
+
+ u.f = uint32_to_float32(val, &env->vec_status);
+
+ return u.l;
+}
+
+static inline int32_t efsctsi(uint32_t val)
+{
+ CPU_FloatU u;
+
+ u.l = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float32_is_quiet_nan(u.f)))
+ return 0;
+
+ return float32_to_int32(u.f, &env->vec_status);
+}
+
+static inline uint32_t efsctui(uint32_t val)
+{
+ CPU_FloatU u;
+
+ u.l = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float32_is_quiet_nan(u.f)))
+ return 0;
+
+ return float32_to_uint32(u.f, &env->vec_status);
+}
+
+static inline uint32_t efsctsiz(uint32_t val)
+{
+ CPU_FloatU u;
+
+ u.l = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float32_is_quiet_nan(u.f)))
+ return 0;
+
+ return float32_to_int32_round_to_zero(u.f, &env->vec_status);
+}
+
+static inline uint32_t efsctuiz(uint32_t val)
+{
+ CPU_FloatU u;
+
+ u.l = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float32_is_quiet_nan(u.f)))
+ return 0;
+
+ return float32_to_uint32_round_to_zero(u.f, &env->vec_status);
+}
+
+static inline uint32_t efscfsf(uint32_t val)
+{
+ CPU_FloatU u;
+ float32 tmp;
+
+ u.f = int32_to_float32(val, &env->vec_status);
+ tmp = int64_to_float32(1ULL << 32, &env->vec_status);
+ u.f = float32_div(u.f, tmp, &env->vec_status);
+
+ return u.l;
+}
+
+static inline uint32_t efscfuf(uint32_t val)
+{
+ CPU_FloatU u;
+ float32 tmp;
+
+ u.f = uint32_to_float32(val, &env->vec_status);
+ tmp = uint64_to_float32(1ULL << 32, &env->vec_status);
+ u.f = float32_div(u.f, tmp, &env->vec_status);
+
+ return u.l;
+}
+
+static inline uint32_t efsctsf(uint32_t val)
+{
+ CPU_FloatU u;
+ float32 tmp;
+
+ u.l = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float32_is_quiet_nan(u.f)))
+ return 0;
+ tmp = uint64_to_float32(1ULL << 32, &env->vec_status);
+ u.f = float32_mul(u.f, tmp, &env->vec_status);
+
+ return float32_to_int32(u.f, &env->vec_status);
+}
+
+static inline uint32_t efsctuf(uint32_t val)
+{
+ CPU_FloatU u;
+ float32 tmp;
+
+ u.l = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float32_is_quiet_nan(u.f)))
+ return 0;
+ tmp = uint64_to_float32(1ULL << 32, &env->vec_status);
+ u.f = float32_mul(u.f, tmp, &env->vec_status);
+
+ return float32_to_uint32(u.f, &env->vec_status);
+}
+
+#define HELPER_SPE_SINGLE_CONV(name) \
+uint32_t helper_e##name (uint32_t val) \
+{ \
+ return e##name(val); \
+}
+/* efscfsi */
+HELPER_SPE_SINGLE_CONV(fscfsi);
+/* efscfui */
+HELPER_SPE_SINGLE_CONV(fscfui);
+/* efscfuf */
+HELPER_SPE_SINGLE_CONV(fscfuf);
+/* efscfsf */
+HELPER_SPE_SINGLE_CONV(fscfsf);
+/* efsctsi */
+HELPER_SPE_SINGLE_CONV(fsctsi);
+/* efsctui */
+HELPER_SPE_SINGLE_CONV(fsctui);
+/* efsctsiz */
+HELPER_SPE_SINGLE_CONV(fsctsiz);
+/* efsctuiz */
+HELPER_SPE_SINGLE_CONV(fsctuiz);
+/* efsctsf */
+HELPER_SPE_SINGLE_CONV(fsctsf);
+/* efsctuf */
+HELPER_SPE_SINGLE_CONV(fsctuf);
+
+#define HELPER_SPE_VECTOR_CONV(name) \
+uint64_t helper_ev##name (uint64_t val) \
+{ \
+ return ((uint64_t)e##name(val >> 32) << 32) | \
+ (uint64_t)e##name(val); \
+}
+/* evfscfsi */
+HELPER_SPE_VECTOR_CONV(fscfsi);
+/* evfscfui */
+HELPER_SPE_VECTOR_CONV(fscfui);
+/* evfscfuf */
+HELPER_SPE_VECTOR_CONV(fscfuf);
+/* evfscfsf */
+HELPER_SPE_VECTOR_CONV(fscfsf);
+/* evfsctsi */
+HELPER_SPE_VECTOR_CONV(fsctsi);
+/* evfsctui */
+HELPER_SPE_VECTOR_CONV(fsctui);
+/* evfsctsiz */
+HELPER_SPE_VECTOR_CONV(fsctsiz);
+/* evfsctuiz */
+HELPER_SPE_VECTOR_CONV(fsctuiz);
+/* evfsctsf */
+HELPER_SPE_VECTOR_CONV(fsctsf);
+/* evfsctuf */
+HELPER_SPE_VECTOR_CONV(fsctuf);
+
+/* Single-precision floating-point arithmetic */
+static inline uint32_t efsadd(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ u1.f = float32_add(u1.f, u2.f, &env->vec_status);
+ return u1.l;
+}
+
+static inline uint32_t efssub(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ u1.f = float32_sub(u1.f, u2.f, &env->vec_status);
+ return u1.l;
+}
+
+static inline uint32_t efsmul(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ u1.f = float32_mul(u1.f, u2.f, &env->vec_status);
+ return u1.l;
+}
+
+static inline uint32_t efsdiv(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ u1.f = float32_div(u1.f, u2.f, &env->vec_status);
+ return u1.l;
+}
+
+#define HELPER_SPE_SINGLE_ARITH(name) \
+uint32_t helper_e##name (uint32_t op1, uint32_t op2) \
+{ \
+ return e##name(op1, op2); \
+}
+/* efsadd */
+HELPER_SPE_SINGLE_ARITH(fsadd);
+/* efssub */
+HELPER_SPE_SINGLE_ARITH(fssub);
+/* efsmul */
+HELPER_SPE_SINGLE_ARITH(fsmul);
+/* efsdiv */
+HELPER_SPE_SINGLE_ARITH(fsdiv);
+
+#define HELPER_SPE_VECTOR_ARITH(name) \
+uint64_t helper_ev##name (uint64_t op1, uint64_t op2) \
+{ \
+ return ((uint64_t)e##name(op1 >> 32, op2 >> 32) << 32) | \
+ (uint64_t)e##name(op1, op2); \
+}
+/* evfsadd */
+HELPER_SPE_VECTOR_ARITH(fsadd);
+/* evfssub */
+HELPER_SPE_VECTOR_ARITH(fssub);
+/* evfsmul */
+HELPER_SPE_VECTOR_ARITH(fsmul);
+/* evfsdiv */
+HELPER_SPE_VECTOR_ARITH(fsdiv);
+
+/* Single-precision floating-point comparisons */
+static inline uint32_t efststlt(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ return float32_lt(u1.f, u2.f, &env->vec_status) ? 4 : 0;
+}
+
+static inline uint32_t efststgt(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ return float32_le(u1.f, u2.f, &env->vec_status) ? 0 : 4;
+}
+
+static inline uint32_t efststeq(uint32_t op1, uint32_t op2)
+{
+ CPU_FloatU u1, u2;
+ u1.l = op1;
+ u2.l = op2;
+ return float32_eq(u1.f, u2.f, &env->vec_status) ? 4 : 0;
+}
+
+static inline uint32_t efscmplt(uint32_t op1, uint32_t op2)
+{
+ /* XXX: TODO: test special values (NaN, infinites, ...) */
+ return efststlt(op1, op2);
+}
+
+static inline uint32_t efscmpgt(uint32_t op1, uint32_t op2)
+{
+ /* XXX: TODO: test special values (NaN, infinites, ...) */
+ return efststgt(op1, op2);
+}
+
+static inline uint32_t efscmpeq(uint32_t op1, uint32_t op2)
+{
+ /* XXX: TODO: test special values (NaN, infinites, ...) */
+ return efststeq(op1, op2);
+}
+
+#define HELPER_SINGLE_SPE_CMP(name) \
+uint32_t helper_e##name (uint32_t op1, uint32_t op2) \
+{ \
+ return e##name(op1, op2) << 2; \
+}
+/* efststlt */
+HELPER_SINGLE_SPE_CMP(fststlt);
+/* efststgt */
+HELPER_SINGLE_SPE_CMP(fststgt);
+/* efststeq */
+HELPER_SINGLE_SPE_CMP(fststeq);
+/* efscmplt */
+HELPER_SINGLE_SPE_CMP(fscmplt);
+/* efscmpgt */
+HELPER_SINGLE_SPE_CMP(fscmpgt);
+/* efscmpeq */
+HELPER_SINGLE_SPE_CMP(fscmpeq);
+
+static inline uint32_t evcmp_merge(int t0, int t1)
+{
+ return (t0 << 3) | (t1 << 2) | ((t0 | t1) << 1) | (t0 & t1);
+}
+
+#define HELPER_VECTOR_SPE_CMP(name) \
+uint32_t helper_ev##name (uint64_t op1, uint64_t op2) \
+{ \
+ return evcmp_merge(e##name(op1 >> 32, op2 >> 32), e##name(op1, op2)); \
+}
+/* evfststlt */
+HELPER_VECTOR_SPE_CMP(fststlt);
+/* evfststgt */
+HELPER_VECTOR_SPE_CMP(fststgt);
+/* evfststeq */
+HELPER_VECTOR_SPE_CMP(fststeq);
+/* evfscmplt */
+HELPER_VECTOR_SPE_CMP(fscmplt);
+/* evfscmpgt */
+HELPER_VECTOR_SPE_CMP(fscmpgt);
+/* evfscmpeq */
+HELPER_VECTOR_SPE_CMP(fscmpeq);
+
+/* Double-precision floating-point conversion */
+uint64_t helper_efdcfsi (uint32_t val)
+{
+ CPU_DoubleU u;
+
+ u.d = int32_to_float64(val, &env->vec_status);
+
+ return u.ll;
+}
+
+uint64_t helper_efdcfsid (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.d = int64_to_float64(val, &env->vec_status);
+
+ return u.ll;
+}
+
+uint64_t helper_efdcfui (uint32_t val)
+{
+ CPU_DoubleU u;
+
+ u.d = uint32_to_float64(val, &env->vec_status);
+
+ return u.ll;
+}
+
+uint64_t helper_efdcfuid (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.d = uint64_to_float64(val, &env->vec_status);
+
+ return u.ll;
+}
+
+uint32_t helper_efdctsi (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+
+ return float64_to_int32(u.d, &env->vec_status);
+}
+
+uint32_t helper_efdctui (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+
+ return float64_to_uint32(u.d, &env->vec_status);
+}
+
+uint32_t helper_efdctsiz (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+
+ return float64_to_int32_round_to_zero(u.d, &env->vec_status);
+}
+
+uint64_t helper_efdctsidz (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+
+ return float64_to_int64_round_to_zero(u.d, &env->vec_status);
+}
+
+uint32_t helper_efdctuiz (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+
+ return float64_to_uint32_round_to_zero(u.d, &env->vec_status);
+}
+
+uint64_t helper_efdctuidz (uint64_t val)
+{
+ CPU_DoubleU u;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+
+ return float64_to_uint64_round_to_zero(u.d, &env->vec_status);
+}
+
+uint64_t helper_efdcfsf (uint32_t val)
+{
+ CPU_DoubleU u;
+ float64 tmp;
+
+ u.d = int32_to_float64(val, &env->vec_status);
+ tmp = int64_to_float64(1ULL << 32, &env->vec_status);
+ u.d = float64_div(u.d, tmp, &env->vec_status);
+
+ return u.ll;
+}
+
+uint64_t helper_efdcfuf (uint32_t val)
+{
+ CPU_DoubleU u;
+ float64 tmp;
+
+ u.d = uint32_to_float64(val, &env->vec_status);
+ tmp = int64_to_float64(1ULL << 32, &env->vec_status);
+ u.d = float64_div(u.d, tmp, &env->vec_status);
+
+ return u.ll;
+}
+
+uint32_t helper_efdctsf (uint64_t val)
+{
+ CPU_DoubleU u;
+ float64 tmp;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+ tmp = uint64_to_float64(1ULL << 32, &env->vec_status);
+ u.d = float64_mul(u.d, tmp, &env->vec_status);
+
+ return float64_to_int32(u.d, &env->vec_status);
+}
+
+uint32_t helper_efdctuf (uint64_t val)
+{
+ CPU_DoubleU u;
+ float64 tmp;
+
+ u.ll = val;
+ /* NaN are not treated the same way IEEE 754 does */
+ if (unlikely(float64_is_any_nan(u.d))) {
+ return 0;
+ }
+ tmp = uint64_to_float64(1ULL << 32, &env->vec_status);
+ u.d = float64_mul(u.d, tmp, &env->vec_status);
+
+ return float64_to_uint32(u.d, &env->vec_status);
+}
+
+uint32_t helper_efscfd (uint64_t val)
+{
+ CPU_DoubleU u1;
+ CPU_FloatU u2;
+
+ u1.ll = val;
+ u2.f = float64_to_float32(u1.d, &env->vec_status);
+
+ return u2.l;
+}
+
+uint64_t helper_efdcfs (uint32_t val)
+{
+ CPU_DoubleU u2;
+ CPU_FloatU u1;
+
+ u1.l = val;
+ u2.d = float32_to_float64(u1.f, &env->vec_status);
+
+ return u2.ll;
+}
+
+/* Double precision fixed-point arithmetic */
+uint64_t helper_efdadd (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ u1.d = float64_add(u1.d, u2.d, &env->vec_status);
+ return u1.ll;
+}
+
+uint64_t helper_efdsub (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ u1.d = float64_sub(u1.d, u2.d, &env->vec_status);
+ return u1.ll;
+}
+
+uint64_t helper_efdmul (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ u1.d = float64_mul(u1.d, u2.d, &env->vec_status);
+ return u1.ll;
+}
+
+uint64_t helper_efddiv (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ u1.d = float64_div(u1.d, u2.d, &env->vec_status);
+ return u1.ll;
+}
+
+/* Double precision floating point helpers */
+uint32_t helper_efdtstlt (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ return float64_lt(u1.d, u2.d, &env->vec_status) ? 4 : 0;
+}
+
+uint32_t helper_efdtstgt (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ return float64_le(u1.d, u2.d, &env->vec_status) ? 0 : 4;
+}
+
+uint32_t helper_efdtsteq (uint64_t op1, uint64_t op2)
+{
+ CPU_DoubleU u1, u2;
+ u1.ll = op1;
+ u2.ll = op2;
+ return float64_eq(u1.d, u2.d, &env->vec_status) ? 4 : 0;
+}
+
+uint32_t helper_efdcmplt (uint64_t op1, uint64_t op2)
+{
+ /* XXX: TODO: test special values (NaN, infinites, ...) */
+ return helper_efdtstlt(op1, op2);
+}
+
+uint32_t helper_efdcmpgt (uint64_t op1, uint64_t op2)
+{
+ /* XXX: TODO: test special values (NaN, infinites, ...) */
+ return helper_efdtstgt(op1, op2);
+}
+
+uint32_t helper_efdcmpeq (uint64_t op1, uint64_t op2)
+{
+ /* XXX: TODO: test special values (NaN, infinites, ...) */
+ return helper_efdtsteq(op1, op2);
+}
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret != 0)) {
+ if (likely(retaddr)) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (likely(tb)) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ helper_raise_exception_err(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
+
+/* Segment registers load and store */
+target_ulong helper_load_sr (target_ulong sr_num)
+{
+#if defined(TARGET_PPC64)
+ if (env->mmu_model & POWERPC_MMU_64)
+ return ppc_load_sr(env, sr_num);
+#endif
+ return env->sr[sr_num];
+}
+
+void helper_store_sr (target_ulong sr_num, target_ulong val)
+{
+ ppc_store_sr(env, sr_num, val);
+}
+
+/* SLB management */
+#if defined(TARGET_PPC64)
+target_ulong helper_load_slb (target_ulong slb_nr)
+{
+ return ppc_load_slb(env, slb_nr);
+}
+
+void helper_store_slb (target_ulong rb, target_ulong rs)
+{
+ ppc_store_slb(env, rb, rs);
+}
+
+void helper_slbia (void)
+{
+ ppc_slb_invalidate_all(env);
+}
+
+void helper_slbie (target_ulong addr)
+{
+ ppc_slb_invalidate_one(env, addr);
+}
+
+#endif /* defined(TARGET_PPC64) */
+
+/* TLB management */
+void helper_tlbia (void)
+{
+ ppc_tlb_invalidate_all(env);
+}
+
+void helper_tlbie (target_ulong addr)
+{
+ ppc_tlb_invalidate_one(env, addr);
+}
+
+/* Software driven TLBs management */
+/* PowerPC 602/603 software TLB load instructions helpers */
+static void do_6xx_tlb (target_ulong new_EPN, int is_code)
+{
+ target_ulong RPN, CMP, EPN;
+ int way;
+
+ RPN = env->spr[SPR_RPA];
+ if (is_code) {
+ CMP = env->spr[SPR_ICMP];
+ EPN = env->spr[SPR_IMISS];
+ } else {
+ CMP = env->spr[SPR_DCMP];
+ EPN = env->spr[SPR_DMISS];
+ }
+ way = (env->spr[SPR_SRR1] >> 17) & 1;
+ (void)EPN; /* avoid a compiler warning */
+ LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
+ " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP,
+ RPN, way);
+ /* Store this TLB */
+ ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK),
+ way, is_code, CMP, RPN);
+}
+
+void helper_6xx_tlbd (target_ulong EPN)
+{
+ do_6xx_tlb(EPN, 0);
+}
+
+void helper_6xx_tlbi (target_ulong EPN)
+{
+ do_6xx_tlb(EPN, 1);
+}
+
+/* PowerPC 74xx software TLB load instructions helpers */
+static void do_74xx_tlb (target_ulong new_EPN, int is_code)
+{
+ target_ulong RPN, CMP, EPN;
+ int way;
+
+ RPN = env->spr[SPR_PTELO];
+ CMP = env->spr[SPR_PTEHI];
+ EPN = env->spr[SPR_TLBMISS] & ~0x3;
+ way = env->spr[SPR_TLBMISS] & 0x3;
+ (void)EPN; /* avoid a compiler warning */
+ LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx
+ " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP,
+ RPN, way);
+ /* Store this TLB */
+ ppc6xx_tlb_store(env, (uint32_t)(new_EPN & TARGET_PAGE_MASK),
+ way, is_code, CMP, RPN);
+}
+
+void helper_74xx_tlbd (target_ulong EPN)
+{
+ do_74xx_tlb(EPN, 0);
+}
+
+void helper_74xx_tlbi (target_ulong EPN)
+{
+ do_74xx_tlb(EPN, 1);
+}
+
+static inline target_ulong booke_tlb_to_page_size(int size)
+{
+ return 1024 << (2 * size);
+}
+
+static inline int booke_page_size_to_tlb(target_ulong page_size)
+{
+ int size;
+
+ switch (page_size) {
+ case 0x00000400UL:
+ size = 0x0;
+ break;
+ case 0x00001000UL:
+ size = 0x1;
+ break;
+ case 0x00004000UL:
+ size = 0x2;
+ break;
+ case 0x00010000UL:
+ size = 0x3;
+ break;
+ case 0x00040000UL:
+ size = 0x4;
+ break;
+ case 0x00100000UL:
+ size = 0x5;
+ break;
+ case 0x00400000UL:
+ size = 0x6;
+ break;
+ case 0x01000000UL:
+ size = 0x7;
+ break;
+ case 0x04000000UL:
+ size = 0x8;
+ break;
+ case 0x10000000UL:
+ size = 0x9;
+ break;
+ case 0x40000000UL:
+ size = 0xA;
+ break;
+#if defined (TARGET_PPC64)
+ case 0x000100000000ULL:
+ size = 0xB;
+ break;
+ case 0x000400000000ULL:
+ size = 0xC;
+ break;
+ case 0x001000000000ULL:
+ size = 0xD;
+ break;
+ case 0x004000000000ULL:
+ size = 0xE;
+ break;
+ case 0x010000000000ULL:
+ size = 0xF;
+ break;
+#endif
+ default:
+ size = -1;
+ break;
+ }
+
+ return size;
+}
+
+/* Helpers for 4xx TLB management */
+#define PPC4XX_TLB_ENTRY_MASK 0x0000003f /* Mask for 64 TLB entries */
+
+#define PPC4XX_TLBHI_V 0x00000040
+#define PPC4XX_TLBHI_E 0x00000020
+#define PPC4XX_TLBHI_SIZE_MIN 0
+#define PPC4XX_TLBHI_SIZE_MAX 7
+#define PPC4XX_TLBHI_SIZE_DEFAULT 1
+#define PPC4XX_TLBHI_SIZE_SHIFT 7
+#define PPC4XX_TLBHI_SIZE_MASK 0x00000007
+
+#define PPC4XX_TLBLO_EX 0x00000200
+#define PPC4XX_TLBLO_WR 0x00000100
+#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF
+#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00
+
+target_ulong helper_4xx_tlbre_hi (target_ulong entry)
+{
+ ppcemb_tlb_t *tlb;
+ target_ulong ret;
+ int size;
+
+ entry &= PPC4XX_TLB_ENTRY_MASK;
+ tlb = &env->tlb[entry].tlbe;
+ ret = tlb->EPN;
+ if (tlb->prot & PAGE_VALID) {
+ ret |= PPC4XX_TLBHI_V;
+ }
+ size = booke_page_size_to_tlb(tlb->size);
+ if (size < PPC4XX_TLBHI_SIZE_MIN || size > PPC4XX_TLBHI_SIZE_MAX) {
+ size = PPC4XX_TLBHI_SIZE_DEFAULT;
+ }
+ ret |= size << PPC4XX_TLBHI_SIZE_SHIFT;
+ env->spr[SPR_40x_PID] = tlb->PID;
+ return ret;
+}
+
+target_ulong helper_4xx_tlbre_lo (target_ulong entry)
+{
+ ppcemb_tlb_t *tlb;
+ target_ulong ret;
+
+ entry &= PPC4XX_TLB_ENTRY_MASK;
+ tlb = &env->tlb[entry].tlbe;
+ ret = tlb->RPN;
+ if (tlb->prot & PAGE_EXEC) {
+ ret |= PPC4XX_TLBLO_EX;
+ }
+ if (tlb->prot & PAGE_WRITE) {
+ ret |= PPC4XX_TLBLO_WR;
+ }
+ return ret;
+}
+
+void helper_4xx_tlbwe_hi (target_ulong entry, target_ulong val)
+{
+ ppcemb_tlb_t *tlb;
+ target_ulong page, end;
+
+ LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry,
+ val);
+ entry &= PPC4XX_TLB_ENTRY_MASK;
+ tlb = &env->tlb[entry].tlbe;
+ /* Invalidate previous TLB (if it's valid) */
+ if (tlb->prot & PAGE_VALID) {
+ end = tlb->EPN + tlb->size;
+ LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end "
+ TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end);
+ for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
+ tlb_flush_page(env, page);
+ }
+ }
+ tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT)
+ & PPC4XX_TLBHI_SIZE_MASK);
+ /* We cannot handle TLB size < TARGET_PAGE_SIZE.
+ * If this ever occurs, one should use the ppcemb target instead
+ * of the ppc or ppc64 one
+ */
+ if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) {
+ cpu_abort(env, "TLB size " TARGET_FMT_lu " < %u "
+ "are not supported (%d)\n",
+ tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7));
+ }
+ tlb->EPN = val & ~(tlb->size - 1);
+ if (val & PPC4XX_TLBHI_V) {
+ tlb->prot |= PAGE_VALID;
+ if (val & PPC4XX_TLBHI_E) {
+ /* XXX: TO BE FIXED */
+ cpu_abort(env,
+ "Little-endian TLB entries are not supported by now\n");
+ }
+ } else {
+ tlb->prot &= ~PAGE_VALID;
+ }
+ tlb->PID = env->spr[SPR_40x_PID]; /* PID */
+ LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx
+ " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__,
+ (int)entry, tlb->RPN, tlb->EPN, tlb->size,
+ tlb->prot & PAGE_READ ? 'r' : '-',
+ tlb->prot & PAGE_WRITE ? 'w' : '-',
+ tlb->prot & PAGE_EXEC ? 'x' : '-',
+ tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
+ /* Invalidate new TLB (if valid) */
+ if (tlb->prot & PAGE_VALID) {
+ end = tlb->EPN + tlb->size;
+ LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end "
+ TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end);
+ for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) {
+ tlb_flush_page(env, page);
+ }
+ }
+}
+
+void helper_4xx_tlbwe_lo (target_ulong entry, target_ulong val)
+{
+ ppcemb_tlb_t *tlb;
+
+ LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry,
+ val);
+ entry &= PPC4XX_TLB_ENTRY_MASK;
+ tlb = &env->tlb[entry].tlbe;
+ tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK;
+ tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK;
+ tlb->prot = PAGE_READ;
+ if (val & PPC4XX_TLBLO_EX) {
+ tlb->prot |= PAGE_EXEC;
+ }
+ if (val & PPC4XX_TLBLO_WR) {
+ tlb->prot |= PAGE_WRITE;
+ }
+ LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx
+ " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__,
+ (int)entry, tlb->RPN, tlb->EPN, tlb->size,
+ tlb->prot & PAGE_READ ? 'r' : '-',
+ tlb->prot & PAGE_WRITE ? 'w' : '-',
+ tlb->prot & PAGE_EXEC ? 'x' : '-',
+ tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
+}
+
+target_ulong helper_4xx_tlbsx (target_ulong address)
+{
+ return ppcemb_tlb_search(env, address, env->spr[SPR_40x_PID]);
+}
+
+/* PowerPC 440 TLB management */
+void helper_440_tlbwe (uint32_t word, target_ulong entry, target_ulong value)
+{
+ ppcemb_tlb_t *tlb;
+ target_ulong EPN, RPN, size;
+ int do_flush_tlbs;
+
+ LOG_SWTLB("%s word %d entry %d value " TARGET_FMT_lx "\n",
+ __func__, word, (int)entry, value);
+ do_flush_tlbs = 0;
+ entry &= 0x3F;
+ tlb = &env->tlb[entry].tlbe;
+ switch (word) {
+ default:
+ /* Just here to please gcc */
+ case 0:
+ EPN = value & 0xFFFFFC00;
+ if ((tlb->prot & PAGE_VALID) && EPN != tlb->EPN)
+ do_flush_tlbs = 1;
+ tlb->EPN = EPN;
+ size = booke_tlb_to_page_size((value >> 4) & 0xF);
+ if ((tlb->prot & PAGE_VALID) && tlb->size < size)
+ do_flush_tlbs = 1;
+ tlb->size = size;
+ tlb->attr &= ~0x1;
+ tlb->attr |= (value >> 8) & 1;
+ if (value & 0x200) {
+ tlb->prot |= PAGE_VALID;
+ } else {
+ if (tlb->prot & PAGE_VALID) {
+ tlb->prot &= ~PAGE_VALID;
+ do_flush_tlbs = 1;
+ }
+ }
+ tlb->PID = env->spr[SPR_440_MMUCR] & 0x000000FF;
+ if (do_flush_tlbs)
+ tlb_flush(env, 1);
+ break;
+ case 1:
+ RPN = value & 0xFFFFFC0F;
+ if ((tlb->prot & PAGE_VALID) && tlb->RPN != RPN)
+ tlb_flush(env, 1);
+ tlb->RPN = RPN;
+ break;
+ case 2:
+ tlb->attr = (tlb->attr & 0x1) | (value & 0x0000FF00);
+ tlb->prot = tlb->prot & PAGE_VALID;
+ if (value & 0x1)
+ tlb->prot |= PAGE_READ << 4;
+ if (value & 0x2)
+ tlb->prot |= PAGE_WRITE << 4;
+ if (value & 0x4)
+ tlb->prot |= PAGE_EXEC << 4;
+ if (value & 0x8)
+ tlb->prot |= PAGE_READ;
+ if (value & 0x10)
+ tlb->prot |= PAGE_WRITE;
+ if (value & 0x20)
+ tlb->prot |= PAGE_EXEC;
+ break;
+ }
+}
+
+target_ulong helper_440_tlbre (uint32_t word, target_ulong entry)
+{
+ ppcemb_tlb_t *tlb;
+ target_ulong ret;
+ int size;
+
+ entry &= 0x3F;
+ tlb = &env->tlb[entry].tlbe;
+ switch (word) {
+ default:
+ /* Just here to please gcc */
+ case 0:
+ ret = tlb->EPN;
+ size = booke_page_size_to_tlb(tlb->size);
+ if (size < 0 || size > 0xF)
+ size = 1;
+ ret |= size << 4;
+ if (tlb->attr & 0x1)
+ ret |= 0x100;
+ if (tlb->prot & PAGE_VALID)
+ ret |= 0x200;
+ env->spr[SPR_440_MMUCR] &= ~0x000000FF;
+ env->spr[SPR_440_MMUCR] |= tlb->PID;
+ break;
+ case 1:
+ ret = tlb->RPN;
+ break;
+ case 2:
+ ret = tlb->attr & ~0x1;
+ if (tlb->prot & (PAGE_READ << 4))
+ ret |= 0x1;
+ if (tlb->prot & (PAGE_WRITE << 4))
+ ret |= 0x2;
+ if (tlb->prot & (PAGE_EXEC << 4))
+ ret |= 0x4;
+ if (tlb->prot & PAGE_READ)
+ ret |= 0x8;
+ if (tlb->prot & PAGE_WRITE)
+ ret |= 0x10;
+ if (tlb->prot & PAGE_EXEC)
+ ret |= 0x20;
+ break;
+ }
+ return ret;
+}
+
+target_ulong helper_440_tlbsx (target_ulong address)
+{
+ return ppcemb_tlb_search(env, address, env->spr[SPR_440_MMUCR] & 0xFF);
+}
+
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
new file mode 100644
index 0000000..89413c5
--- /dev/null
+++ b/target-ppc/translate.c
@@ -0,0 +1,9141 @@
+/*
+ * PowerPC emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-common.h"
+#include "host-utils.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+#define CPU_SINGLE_STEP 0x1
+#define CPU_BRANCH_STEP 0x2
+#define GDBSTUB_SINGLE_STEP 0x4
+
+/* Include definitions for instructions classes and implementations flags */
+//#define PPC_DEBUG_DISAS
+//#define DO_PPC_STATISTICS
+
+#ifdef PPC_DEBUG_DISAS
+# define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+# define LOG_DISAS(...) do { } while (0)
+#endif
+/*****************************************************************************/
+/* Code translation helpers */
+
+/* global register indexes */
+static TCGv_ptr cpu_env;
+static char cpu_reg_names[10*3 + 22*4 /* GPR */
+#if !defined(TARGET_PPC64)
+ + 10*4 + 22*5 /* SPE GPRh */
+#endif
+ + 10*4 + 22*5 /* FPR */
+ + 2*(10*6 + 22*7) /* AVRh, AVRl */
+ + 8*5 /* CRF */];
+static TCGv cpu_gpr[32];
+#if !defined(TARGET_PPC64)
+static TCGv cpu_gprh[32];
+#endif
+static TCGv_i64 cpu_fpr[32];
+static TCGv_i64 cpu_avrh[32], cpu_avrl[32];
+static TCGv_i32 cpu_crf[8];
+static TCGv cpu_nip;
+static TCGv cpu_msr;
+static TCGv cpu_ctr;
+static TCGv cpu_lr;
+static TCGv cpu_xer;
+static TCGv cpu_reserve;
+static TCGv_i32 cpu_fpscr;
+static TCGv_i32 cpu_access_type;
+
+#include "gen-icount.h"
+
+void ppc_translate_init(void)
+{
+ int i;
+ char* p;
+ size_t cpu_reg_names_size;
+ static int done_init = 0;
+
+ if (done_init)
+ return;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ p = cpu_reg_names;
+ cpu_reg_names_size = sizeof(cpu_reg_names);
+
+ for (i = 0; i < 8; i++) {
+ snprintf(p, cpu_reg_names_size, "crf%d", i);
+ cpu_crf[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, crf[i]), p);
+ p += 5;
+ cpu_reg_names_size -= 5;
+ }
+
+ for (i = 0; i < 32; i++) {
+ snprintf(p, cpu_reg_names_size, "r%d", i);
+ cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, gpr[i]), p);
+ p += (i < 10) ? 3 : 4;
+ cpu_reg_names_size -= (i < 10) ? 3 : 4;
+#if !defined(TARGET_PPC64)
+ snprintf(p, cpu_reg_names_size, "r%dH", i);
+ cpu_gprh[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, gprh[i]), p);
+ p += (i < 10) ? 4 : 5;
+ cpu_reg_names_size -= (i < 10) ? 4 : 5;
+#endif
+
+ snprintf(p, cpu_reg_names_size, "fp%d", i);
+ cpu_fpr[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, fpr[i]), p);
+ p += (i < 10) ? 4 : 5;
+ cpu_reg_names_size -= (i < 10) ? 4 : 5;
+
+ snprintf(p, cpu_reg_names_size, "avr%dH", i);
+#ifdef HOST_WORDS_BIGENDIAN
+ cpu_avrh[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, avr[i].u64[0]), p);
+#else
+ cpu_avrh[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, avr[i].u64[1]), p);
+#endif
+ p += (i < 10) ? 6 : 7;
+ cpu_reg_names_size -= (i < 10) ? 6 : 7;
+
+ snprintf(p, cpu_reg_names_size, "avr%dL", i);
+#ifdef HOST_WORDS_BIGENDIAN
+ cpu_avrl[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, avr[i].u64[1]), p);
+#else
+ cpu_avrl[i] = tcg_global_mem_new_i64(TCG_AREG0,
+ offsetof(CPUState, avr[i].u64[0]), p);
+#endif
+ p += (i < 10) ? 6 : 7;
+ cpu_reg_names_size -= (i < 10) ? 6 : 7;
+ }
+
+ cpu_nip = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, nip), "nip");
+
+ cpu_msr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, msr), "msr");
+
+ cpu_ctr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, ctr), "ctr");
+
+ cpu_lr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, lr), "lr");
+
+ cpu_xer = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, xer), "xer");
+
+ cpu_reserve = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, reserve_addr),
+ "reserve_addr");
+
+ cpu_fpscr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, fpscr), "fpscr");
+
+ cpu_access_type = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, access_type), "access_type");
+
+ /* register helpers */
+#define GEN_HELPER 2
+#include "helper.h"
+
+ done_init = 1;
+}
+
+/* internal defines */
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong nip;
+ uint32_t opcode;
+ uint32_t exception;
+ /* Routine used to access memory */
+ int mem_idx;
+ int access_type;
+ /* Translation flags */
+ int le_mode;
+#if defined(TARGET_PPC64)
+ int sf_mode;
+#endif
+ int fpu_enabled;
+ int altivec_enabled;
+ int spe_enabled;
+ ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
+ int singlestep_enabled;
+} DisasContext;
+
+struct opc_handler_t {
+ /* invalid bits */
+ uint32_t inval;
+ /* instruction type */
+ uint64_t type;
+ /* handler */
+ void (*handler)(DisasContext *ctx);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ const char *oname;
+#endif
+#if defined(DO_PPC_STATISTICS)
+ uint64_t count;
+#endif
+};
+
+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)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+
+ if (set_fprf != 0) {
+ /* This case might be optimized later */
+ tcg_gen_movi_i32(t0, 1);
+ gen_helper_compute_fprf(t0, arg, t0);
+ if (unlikely(set_rc)) {
+ tcg_gen_mov_i32(cpu_crf[1], t0);
+ }
+ gen_helper_float_check_status();
+ } else if (unlikely(set_rc)) {
+ /* We always need to compute fpcc */
+ tcg_gen_movi_i32(t0, 0);
+ gen_helper_compute_fprf(t0, arg, t0);
+ tcg_gen_mov_i32(cpu_crf[1], t0);
+ }
+
+ tcg_temp_free_i32(t0);
+}
+
+static inline void gen_set_access_type(DisasContext *ctx, int access_type)
+{
+ if (ctx->access_type != access_type) {
+ tcg_gen_movi_i32(cpu_access_type, access_type);
+ ctx->access_type = access_type;
+ }
+}
+
+static inline void gen_update_nip(DisasContext *ctx, target_ulong nip)
+{
+#if defined(TARGET_PPC64)
+ if (ctx->sf_mode)
+ tcg_gen_movi_tl(cpu_nip, nip);
+ else
+#endif
+ tcg_gen_movi_tl(cpu_nip, (uint32_t)nip);
+}
+
+static inline void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error)
+{
+ TCGv_i32 t0, t1;
+ if (ctx->exception == POWERPC_EXCP_NONE) {
+ gen_update_nip(ctx, ctx->nip);
+ }
+ t0 = tcg_const_i32(excp);
+ t1 = tcg_const_i32(error);
+ gen_helper_raise_exception_err(t0, t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+ ctx->exception = (excp);
+}
+
+static inline void gen_exception(DisasContext *ctx, uint32_t excp)
+{
+ TCGv_i32 t0;
+ if (ctx->exception == POWERPC_EXCP_NONE) {
+ gen_update_nip(ctx, ctx->nip);
+ }
+ t0 = tcg_const_i32(excp);
+ gen_helper_raise_exception(t0);
+ tcg_temp_free_i32(t0);
+ ctx->exception = (excp);
+}
+
+static inline void gen_debug_exception(DisasContext *ctx)
+{
+ TCGv_i32 t0;
+
+ if (ctx->exception != POWERPC_EXCP_BRANCH)
+ gen_update_nip(ctx, ctx->nip);
+ t0 = tcg_const_i32(EXCP_DEBUG);
+ gen_helper_raise_exception(t0);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void gen_inval_exception(DisasContext *ctx, uint32_t error)
+{
+ gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | error);
+}
+
+/* Stop translation */
+static inline void gen_stop_exception(DisasContext *ctx)
+{
+ gen_update_nip(ctx, ctx->nip);
+ ctx->exception = POWERPC_EXCP_STOP;
+}
+
+/* No need to update nip here, as execution flow will change */
+static inline void gen_sync_exception(DisasContext *ctx)
+{
+ ctx->exception = POWERPC_EXCP_SYNC;
+}
+
+#define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \
+GEN_OPCODE(name, opc1, opc2, opc3, inval, type)
+
+#define GEN_HANDLER2(name, onam, opc1, opc2, opc3, inval, type) \
+GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type)
+
+typedef struct opcode_t {
+ unsigned char opc1, opc2, opc3;
+#if HOST_LONG_BITS == 64 /* Explicitly align to 64 bits */
+ unsigned char pad[5];
+#else
+ unsigned char pad[1];
+#endif
+ opc_handler_t handler;
+ const char *oname;
+} opcode_t;
+
+/*****************************************************************************/
+/*** Instruction decoding ***/
+#define EXTRACT_HELPER(name, shift, nb) \
+static inline uint32_t name(uint32_t opcode) \
+{ \
+ return (opcode >> (shift)) & ((1 << (nb)) - 1); \
+}
+
+#define EXTRACT_SHELPER(name, shift, nb) \
+static inline int32_t name(uint32_t opcode) \
+{ \
+ return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \
+}
+
+/* Opcode part 1 */
+EXTRACT_HELPER(opc1, 26, 6);
+/* Opcode part 2 */
+EXTRACT_HELPER(opc2, 1, 5);
+/* Opcode part 3 */
+EXTRACT_HELPER(opc3, 6, 5);
+/* Update Cr0 flags */
+EXTRACT_HELPER(Rc, 0, 1);
+/* Destination */
+EXTRACT_HELPER(rD, 21, 5);
+/* Source */
+EXTRACT_HELPER(rS, 21, 5);
+/* First operand */
+EXTRACT_HELPER(rA, 16, 5);
+/* Second operand */
+EXTRACT_HELPER(rB, 11, 5);
+/* Third operand */
+EXTRACT_HELPER(rC, 6, 5);
+/*** Get CRn ***/
+EXTRACT_HELPER(crfD, 23, 3);
+EXTRACT_HELPER(crfS, 18, 3);
+EXTRACT_HELPER(crbD, 21, 5);
+EXTRACT_HELPER(crbA, 16, 5);
+EXTRACT_HELPER(crbB, 11, 5);
+/* SPR / TBL */
+EXTRACT_HELPER(_SPR, 11, 10);
+static inline uint32_t SPR(uint32_t opcode)
+{
+ uint32_t sprn = _SPR(opcode);
+
+ return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+}
+/*** Get constants ***/
+EXTRACT_HELPER(IMM, 12, 8);
+/* 16 bits signed immediate value */
+EXTRACT_SHELPER(SIMM, 0, 16);
+/* 16 bits unsigned immediate value */
+EXTRACT_HELPER(UIMM, 0, 16);
+/* 5 bits signed immediate value */
+EXTRACT_HELPER(SIMM5, 16, 5);
+/* 5 bits signed immediate value */
+EXTRACT_HELPER(UIMM5, 16, 5);
+/* Bit count */
+EXTRACT_HELPER(NB, 11, 5);
+/* Shift count */
+EXTRACT_HELPER(SH, 11, 5);
+/* Vector shift count */
+EXTRACT_HELPER(VSH, 6, 4);
+/* Mask start */
+EXTRACT_HELPER(MB, 6, 5);
+/* Mask end */
+EXTRACT_HELPER(ME, 1, 5);
+/* Trap operand */
+EXTRACT_HELPER(TO, 21, 5);
+
+EXTRACT_HELPER(CRM, 12, 8);
+EXTRACT_HELPER(FM, 17, 8);
+EXTRACT_HELPER(SR, 16, 4);
+EXTRACT_HELPER(FPIMM, 12, 4);
+
+/*** Jump target decoding ***/
+/* Displacement */
+EXTRACT_SHELPER(d, 0, 16);
+/* Immediate address */
+static inline target_ulong LI(uint32_t opcode)
+{
+ return (opcode >> 0) & 0x03FFFFFC;
+}
+
+static inline uint32_t BD(uint32_t opcode)
+{
+ return (opcode >> 0) & 0xFFFC;
+}
+
+EXTRACT_HELPER(BO, 21, 5);
+EXTRACT_HELPER(BI, 16, 5);
+/* Absolute/relative address */
+EXTRACT_HELPER(AA, 1, 1);
+/* Link */
+EXTRACT_HELPER(LK, 0, 1);
+
+/* Create a mask between <start> and <end> bits */
+static inline target_ulong MASK(uint32_t start, uint32_t end)
+{
+ target_ulong ret;
+
+#if defined(TARGET_PPC64)
+ if (likely(start == 0)) {
+ ret = UINT64_MAX << (63 - end);
+ } else if (likely(end == 63)) {
+ ret = UINT64_MAX >> start;
+ }
+#else
+ if (likely(start == 0)) {
+ ret = UINT32_MAX << (31 - end);
+ } else if (likely(end == 31)) {
+ ret = UINT32_MAX >> start;
+ }
+#endif
+ else {
+ ret = (((target_ulong)(-1ULL)) >> (start)) ^
+ (((target_ulong)(-1ULL) >> (end)) >> 1);
+ if (unlikely(start > end))
+ return ~ret;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* PowerPC instructions table */
+
+#if defined(DO_PPC_STATISTICS)
+#define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \
+{ \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = invl, \
+ .type = _typ, \
+ .handler = &gen_##name, \
+ .oname = stringify(name), \
+ }, \
+ .oname = stringify(name), \
+}
+#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ) \
+{ \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = invl, \
+ .type = _typ, \
+ .handler = &gen_##name, \
+ .oname = onam, \
+ }, \
+ .oname = onam, \
+}
+#else
+#define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \
+{ \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = invl, \
+ .type = _typ, \
+ .handler = &gen_##name, \
+ }, \
+ .oname = stringify(name), \
+}
+#define GEN_OPCODE2(name, onam, op1, op2, op3, invl, _typ) \
+{ \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = invl, \
+ .type = _typ, \
+ .handler = &gen_##name, \
+ }, \
+ .oname = onam, \
+}
+#endif
+
+/* SPR load/store helpers */
+static inline void gen_load_spr(TCGv t, int reg)
+{
+ tcg_gen_ld_tl(t, cpu_env, offsetof(CPUState, spr[reg]));
+}
+
+static inline void gen_store_spr(int reg, TCGv t)
+{
+ tcg_gen_st_tl(t, cpu_env, offsetof(CPUState, spr[reg]));
+}
+
+/* Invalid instruction */
+static void gen_invalid(DisasContext *ctx)
+{
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+}
+
+static opc_handler_t invalid_handler = {
+ .inval = 0xFFFFFFFF,
+ .type = PPC_NONE,
+ .handler = gen_invalid,
+};
+
+/*** Integer comparison ***/
+
+static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf)
+{
+ int l1, l2, l3;
+
+ tcg_gen_trunc_tl_i32(cpu_crf[crf], cpu_xer);
+ tcg_gen_shri_i32(cpu_crf[crf], cpu_crf[crf], XER_SO);
+ tcg_gen_andi_i32(cpu_crf[crf], cpu_crf[crf], 1);
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ l3 = gen_new_label();
+ if (s) {
+ tcg_gen_brcond_tl(TCG_COND_LT, arg0, arg1, l1);
+ tcg_gen_brcond_tl(TCG_COND_GT, arg0, arg1, l2);
+ } else {
+ tcg_gen_brcond_tl(TCG_COND_LTU, arg0, arg1, l1);
+ tcg_gen_brcond_tl(TCG_COND_GTU, arg0, arg1, l2);
+ }
+ tcg_gen_ori_i32(cpu_crf[crf], cpu_crf[crf], 1 << CRF_EQ);
+ tcg_gen_br(l3);
+ gen_set_label(l1);
+ tcg_gen_ori_i32(cpu_crf[crf], cpu_crf[crf], 1 << CRF_LT);
+ tcg_gen_br(l3);
+ gen_set_label(l2);
+ tcg_gen_ori_i32(cpu_crf[crf], cpu_crf[crf], 1 << CRF_GT);
+ gen_set_label(l3);
+}
+
+static inline void gen_op_cmpi(TCGv arg0, target_ulong arg1, int s, int crf)
+{
+ TCGv t0 = tcg_const_local_tl(arg1);
+ gen_op_cmp(arg0, t0, s, crf);
+ tcg_temp_free(t0);
+}
+
+#if defined(TARGET_PPC64)
+static inline void gen_op_cmp32(TCGv arg0, TCGv arg1, int s, int crf)
+{
+ TCGv t0, t1;
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+ if (s) {
+ tcg_gen_ext32s_tl(t0, arg0);
+ tcg_gen_ext32s_tl(t1, arg1);
+ } else {
+ tcg_gen_ext32u_tl(t0, arg0);
+ tcg_gen_ext32u_tl(t1, arg1);
+ }
+ gen_op_cmp(t0, t1, s, crf);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_cmpi32(TCGv arg0, target_ulong arg1, int s, int crf)
+{
+ TCGv t0 = tcg_const_local_tl(arg1);
+ gen_op_cmp32(arg0, t0, s, crf);
+ tcg_temp_free(t0);
+}
+#endif
+
+static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg)
+{
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode))
+ gen_op_cmpi32(reg, 0, 1, 0);
+ else
+#endif
+ gen_op_cmpi(reg, 0, 1, 0);
+}
+
+/* cmp */
+static void gen_cmp(DisasContext *ctx)
+{
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+ gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
+ 1, crfD(ctx->opcode));
+ else
+#endif
+ gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
+ 1, crfD(ctx->opcode));
+}
+
+/* cmpi */
+static void gen_cmpi(DisasContext *ctx)
+{
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+ gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
+ 1, crfD(ctx->opcode));
+ else
+#endif
+ gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], SIMM(ctx->opcode),
+ 1, crfD(ctx->opcode));
+}
+
+/* cmpl */
+static void gen_cmpl(DisasContext *ctx)
+{
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+ gen_op_cmp32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
+ 0, crfD(ctx->opcode));
+ else
+#endif
+ gen_op_cmp(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)],
+ 0, crfD(ctx->opcode));
+}
+
+/* cmpli */
+static void gen_cmpli(DisasContext *ctx)
+{
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode && (ctx->opcode & 0x00200000)))
+ gen_op_cmpi32(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
+ 0, crfD(ctx->opcode));
+ else
+#endif
+ gen_op_cmpi(cpu_gpr[rA(ctx->opcode)], UIMM(ctx->opcode),
+ 0, crfD(ctx->opcode));
+}
+
+/* isel (PowerPC 2.03 specification) */
+static void gen_isel(DisasContext *ctx)
+{
+ int l1, l2;
+ uint32_t bi = rC(ctx->opcode);
+ uint32_t mask;
+ TCGv_i32 t0;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+
+ mask = 1 << (3 - (bi & 0x03));
+ t0 = tcg_temp_new_i32();
+ tcg_gen_andi_i32(t0, cpu_crf[bi >> 2], mask);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l1);
+ if (rA(ctx->opcode) == 0)
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0);
+ else
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ gen_set_label(l2);
+ tcg_temp_free_i32(t0);
+}
+
+/*** Integer arithmetic ***/
+
+static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0,
+ TCGv arg1, TCGv arg2, int sub)
+{
+ int l1;
+ TCGv t0;
+
+ l1 = gen_new_label();
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ t0 = tcg_temp_local_new();
+ tcg_gen_xor_tl(t0, arg0, arg1);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode)
+ tcg_gen_ext32s_tl(t0, t0);
+#endif
+ if (sub)
+ tcg_gen_brcondi_tl(TCG_COND_LT, t0, 0, l1);
+ else
+ tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1);
+ tcg_gen_xor_tl(t0, arg1, arg2);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode)
+ tcg_gen_ext32s_tl(t0, t0);
+#endif
+ if (sub)
+ tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1);
+ else
+ tcg_gen_brcondi_tl(TCG_COND_LT, t0, 0, l1);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_arith_compute_ca(DisasContext *ctx, TCGv arg1,
+ TCGv arg2, int sub)
+{
+ int l1 = gen_new_label();
+
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode)) {
+ TCGv t0, t1;
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+
+ tcg_gen_ext32u_tl(t0, arg1);
+ tcg_gen_ext32u_tl(t1, arg2);
+ if (sub) {
+ tcg_gen_brcond_tl(TCG_COND_GTU, t0, t1, l1);
+ } else {
+ tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1);
+ }
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA);
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ } else
+#endif
+ {
+ if (sub) {
+ tcg_gen_brcond_tl(TCG_COND_GTU, arg1, arg2, l1);
+ } else {
+ tcg_gen_brcond_tl(TCG_COND_GEU, arg1, arg2, l1);
+ }
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA);
+ gen_set_label(l1);
+ }
+}
+
+/* Common add function */
+static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1,
+ TCGv arg2, int add_ca, int compute_ca,
+ int compute_ov)
+{
+ TCGv t0, t1;
+
+ if ((!compute_ca && !compute_ov) ||
+ (!TCGV_EQUAL(ret,arg1) && !TCGV_EQUAL(ret, arg2))) {
+ t0 = ret;
+ } else {
+ t0 = tcg_temp_local_new();
+ }
+
+ if (add_ca) {
+ t1 = tcg_temp_local_new();
+ tcg_gen_andi_tl(t1, cpu_xer, (1 << XER_CA));
+ tcg_gen_shri_tl(t1, t1, XER_CA);
+ } else {
+ TCGV_UNUSED(t1);
+ }
+
+ if (compute_ca && compute_ov) {
+ /* Start with XER CA and OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~((1 << XER_CA) | (1 << XER_OV)));
+ } else if (compute_ca) {
+ /* Start with XER CA disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ } else if (compute_ov) {
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ }
+
+ tcg_gen_add_tl(t0, arg1, arg2);
+
+ if (compute_ca) {
+ gen_op_arith_compute_ca(ctx, t0, arg1, 0);
+ }
+ if (add_ca) {
+ tcg_gen_add_tl(t0, t0, t1);
+ gen_op_arith_compute_ca(ctx, t0, t1, 0);
+ tcg_temp_free(t1);
+ }
+ if (compute_ov) {
+ gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 0);
+ }
+
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, t0);
+
+ if (!TCGV_EQUAL(t0, ret)) {
+ tcg_gen_mov_tl(ret, t0);
+ tcg_temp_free(t0);
+ }
+}
+/* Add functions with two operands */
+#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
+ add_ca, compute_ca, compute_ov); \
+}
+/* Add functions with one operand and one immediate */
+#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \
+ add_ca, compute_ca, compute_ov) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv t0 = tcg_const_local_tl(const_val); \
+ gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], t0, \
+ add_ca, compute_ca, compute_ov); \
+ tcg_temp_free(t0); \
+}
+
+/* add add. addo addo. */
+GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0)
+GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1)
+/* addc addc. addco addco. */
+GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0)
+GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1)
+/* adde adde. addeo addeo. */
+GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0)
+GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1)
+/* addme addme. addmeo addmeo. */
+GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0)
+GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1)
+/* addze addze. addzeo addzeo.*/
+GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0)
+GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1)
+/* addi */
+static void gen_addi(DisasContext *ctx)
+{
+ target_long simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ /* li case */
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], simm);
+ } else {
+ tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], simm);
+ }
+}
+/* addic addic.*/
+static inline void gen_op_addic(DisasContext *ctx, TCGv ret, TCGv arg1,
+ int compute_Rc0)
+{
+ target_long simm = SIMM(ctx->opcode);
+
+ /* Start with XER CA and OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+
+ if (likely(simm != 0)) {
+ TCGv t0 = tcg_temp_local_new();
+ tcg_gen_addi_tl(t0, arg1, simm);
+ gen_op_arith_compute_ca(ctx, t0, arg1, 0);
+ tcg_gen_mov_tl(ret, t0);
+ tcg_temp_free(t0);
+ } else {
+ tcg_gen_mov_tl(ret, arg1);
+ }
+ if (compute_Rc0) {
+ gen_set_Rc0(ctx, ret);
+ }
+}
+
+static void gen_addic(DisasContext *ctx)
+{
+ gen_op_addic(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0);
+}
+
+static void gen_addic_(DisasContext *ctx)
+{
+ gen_op_addic(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 1);
+}
+
+/* addis */
+static void gen_addis(DisasContext *ctx)
+{
+ target_long simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ /* lis case */
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], simm << 16);
+ } else {
+ tcg_gen_addi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], simm << 16);
+ }
+}
+
+static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, TCGv arg1,
+ TCGv arg2, int sign, int compute_ov)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ TCGv_i32 t0 = tcg_temp_local_new_i32();
+ TCGv_i32 t1 = tcg_temp_local_new_i32();
+
+ tcg_gen_trunc_tl_i32(t0, arg1);
+ tcg_gen_trunc_tl_i32(t1, arg2);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, t1, 0, l1);
+ if (sign) {
+ int l3 = gen_new_label();
+ tcg_gen_brcondi_i32(TCG_COND_NE, t1, -1, l3);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, t0, INT32_MIN, l1);
+ gen_set_label(l3);
+ tcg_gen_div_i32(t0, t0, t1);
+ } else {
+ tcg_gen_divu_i32(t0, t0, t1);
+ }
+ if (compute_ov) {
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ }
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ if (sign) {
+ tcg_gen_sari_i32(t0, t0, 31);
+ } else {
+ tcg_gen_movi_i32(t0, 0);
+ }
+ if (compute_ov) {
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ }
+ gen_set_label(l2);
+ tcg_gen_extu_i32_tl(ret, t0);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, ret);
+}
+/* Div functions */
+#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
+ sign, compute_ov); \
+}
+/* divwu divwu. divwuo divwuo. */
+GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0);
+GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1);
+/* divw divw. divwo divwo. */
+GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0);
+GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1);
+#if defined(TARGET_PPC64)
+static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1,
+ TCGv arg2, int sign, int compute_ov)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+
+ tcg_gen_brcondi_i64(TCG_COND_EQ, arg2, 0, l1);
+ if (sign) {
+ int l3 = gen_new_label();
+ tcg_gen_brcondi_i64(TCG_COND_NE, arg2, -1, l3);
+ tcg_gen_brcondi_i64(TCG_COND_EQ, arg1, INT64_MIN, l1);
+ gen_set_label(l3);
+ tcg_gen_div_i64(ret, arg1, arg2);
+ } else {
+ tcg_gen_divu_i64(ret, arg1, arg2);
+ }
+ if (compute_ov) {
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ }
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ if (sign) {
+ tcg_gen_sari_i64(ret, arg1, 63);
+ } else {
+ tcg_gen_movi_i64(ret, 0);
+ }
+ if (compute_ov) {
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ }
+ gen_set_label(l2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, ret);
+}
+#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ gen_op_arith_divd(ctx, cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
+ sign, compute_ov); \
+}
+/* divwu divwu. divwuo divwuo. */
+GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0);
+GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1);
+/* divw divw. divwo divwo. */
+GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0);
+GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1);
+#endif
+
+/* mulhw mulhw. */
+static void gen_mulhw(DisasContext *ctx)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+#if defined(TARGET_PPC64)
+ tcg_gen_ext32s_tl(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_ext32s_tl(t1, cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_shri_i64(cpu_gpr[rD(ctx->opcode)], t0, 32);
+#else
+ tcg_gen_ext_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_ext_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* mulhwu mulhwu. */
+static void gen_mulhwu(DisasContext *ctx)
+{
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+#if defined(TARGET_PPC64)
+ tcg_gen_ext32u_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_ext32u_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_shri_i64(cpu_gpr[rD(ctx->opcode)], t0, 32);
+#else
+ tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* mullw mullw. */
+static void gen_mullw(DisasContext *ctx)
+{
+ tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)],
+ cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_ext32s_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* mullwo mullwo. */
+static void gen_mullwo(DisasContext *ctx)
+{
+ int l1;
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ l1 = gen_new_label();
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+#if defined(TARGET_PPC64)
+ tcg_gen_ext32s_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_ext32s_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+#else
+ tcg_gen_ext_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_ext_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+#endif
+ tcg_gen_mul_i64(t0, t0, t1);
+#if defined(TARGET_PPC64)
+ tcg_gen_ext32s_i64(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_brcond_i64(TCG_COND_EQ, t0, cpu_gpr[rD(ctx->opcode)], l1);
+#else
+ tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_ext32s_i64(t1, t0);
+ tcg_gen_brcond_i64(TCG_COND_EQ, t0, t1, l1);
+#endif
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ gen_set_label(l1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* mulli */
+static void gen_mulli(DisasContext *ctx)
+{
+ tcg_gen_muli_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)],
+ SIMM(ctx->opcode));
+}
+#if defined(TARGET_PPC64)
+#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ gen_helper_##name (cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \
+ if (unlikely(Rc(ctx->opcode) != 0)) \
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); \
+}
+/* mulhd mulhd. */
+GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00);
+/* mulhdu mulhdu. */
+GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02);
+
+/* mulld mulld. */
+static void gen_mulld(DisasContext *ctx)
+{
+ tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)],
+ cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+/* mulldo mulldo. */
+GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17);
+#endif
+
+/* neg neg. nego nego. */
+static inline void gen_op_arith_neg(DisasContext *ctx, TCGv ret, TCGv arg1,
+ int ov_check)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ TCGv t0 = tcg_temp_local_new();
+#if defined(TARGET_PPC64)
+ if (ctx->sf_mode) {
+ tcg_gen_mov_tl(t0, arg1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, INT64_MIN, l1);
+ } else
+#endif
+ {
+ tcg_gen_ext32s_tl(t0, arg1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, INT32_MIN, l1);
+ }
+ tcg_gen_neg_tl(ret, arg1);
+ if (ov_check) {
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ }
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_mov_tl(ret, t0);
+ if (ov_check) {
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ }
+ gen_set_label(l2);
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, ret);
+}
+
+static void gen_neg(DisasContext *ctx)
+{
+ gen_op_arith_neg(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0);
+}
+
+static void gen_nego(DisasContext *ctx)
+{
+ gen_op_arith_neg(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 1);
+}
+
+/* Common subf function */
+static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1,
+ TCGv arg2, int add_ca, int compute_ca,
+ int compute_ov)
+{
+ TCGv t0, t1;
+
+ if ((!compute_ca && !compute_ov) ||
+ (!TCGV_EQUAL(ret, arg1) && !TCGV_EQUAL(ret, arg2))) {
+ t0 = ret;
+ } else {
+ t0 = tcg_temp_local_new();
+ }
+
+ if (add_ca) {
+ t1 = tcg_temp_local_new();
+ tcg_gen_andi_tl(t1, cpu_xer, (1 << XER_CA));
+ tcg_gen_shri_tl(t1, t1, XER_CA);
+ } else {
+ TCGV_UNUSED(t1);
+ }
+
+ if (compute_ca && compute_ov) {
+ /* Start with XER CA and OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~((1 << XER_CA) | (1 << XER_OV)));
+ } else if (compute_ca) {
+ /* Start with XER CA disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ } else if (compute_ov) {
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ }
+
+ if (add_ca) {
+ tcg_gen_not_tl(t0, arg1);
+ tcg_gen_add_tl(t0, t0, arg2);
+ gen_op_arith_compute_ca(ctx, t0, arg2, 0);
+ tcg_gen_add_tl(t0, t0, t1);
+ gen_op_arith_compute_ca(ctx, t0, t1, 0);
+ tcg_temp_free(t1);
+ } else {
+ tcg_gen_sub_tl(t0, arg2, arg1);
+ if (compute_ca) {
+ gen_op_arith_compute_ca(ctx, t0, arg2, 1);
+ }
+ }
+ if (compute_ov) {
+ gen_op_arith_compute_ov(ctx, t0, arg1, arg2, 1);
+ }
+
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, t0);
+
+ if (!TCGV_EQUAL(t0, ret)) {
+ tcg_gen_mov_tl(ret, t0);
+ tcg_temp_free(t0);
+ }
+}
+/* Sub functions with Two operands functions */
+#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
+ add_ca, compute_ca, compute_ov); \
+}
+/* Sub functions with one operand and one immediate */
+#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \
+ add_ca, compute_ca, compute_ov) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv t0 = tcg_const_local_tl(const_val); \
+ gen_op_arith_subf(ctx, cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], t0, \
+ add_ca, compute_ca, compute_ov); \
+ tcg_temp_free(t0); \
+}
+/* subf subf. subfo subfo. */
+GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0)
+GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1)
+/* subfc subfc. subfco subfco. */
+GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0)
+GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1)
+/* subfe subfe. subfeo subfo. */
+GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0)
+GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1)
+/* subfme subfme. subfmeo subfmeo. */
+GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0)
+GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1)
+/* subfze subfze. subfzeo subfzeo.*/
+GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0)
+GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1)
+
+/* subfic */
+static void gen_subfic(DisasContext *ctx)
+{
+ /* Start with XER CA and OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_const_local_tl(SIMM(ctx->opcode));
+ tcg_gen_sub_tl(t0, t1, cpu_gpr[rA(ctx->opcode)]);
+ gen_op_arith_compute_ca(ctx, t0, t1, 1);
+ tcg_temp_free(t1);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+}
+
+/*** Integer logical ***/
+#define GEN_LOGICAL2(name, tcg_op, opc, type) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], \
+ cpu_gpr[rB(ctx->opcode)]); \
+ if (unlikely(Rc(ctx->opcode) != 0)) \
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \
+}
+
+#define GEN_LOGICAL1(name, tcg_op, opc, type) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); \
+ if (unlikely(Rc(ctx->opcode) != 0)) \
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \
+}
+
+/* and & and. */
+GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER);
+/* andc & andc. */
+GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER);
+
+/* andi. */
+static void gen_andi_(DisasContext *ctx)
+{
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], UIMM(ctx->opcode));
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* andis. */
+static void gen_andis_(DisasContext *ctx)
+{
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], UIMM(ctx->opcode) << 16);
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* cntlzw */
+static void gen_cntlzw(DisasContext *ctx)
+{
+ gen_helper_cntlzw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+/* eqv & eqv. */
+GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER);
+/* extsb & extsb. */
+GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER);
+/* extsh & extsh. */
+GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER);
+/* nand & nand. */
+GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER);
+/* nor & nor. */
+GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER);
+
+/* or & or. */
+static void gen_or(DisasContext *ctx)
+{
+ int rs, ra, rb;
+
+ rs = rS(ctx->opcode);
+ ra = rA(ctx->opcode);
+ rb = rB(ctx->opcode);
+ /* Optimisation for mr. ri case */
+ if (rs != ra || rs != rb) {
+ if (rs != rb)
+ tcg_gen_or_tl(cpu_gpr[ra], cpu_gpr[rs], cpu_gpr[rb]);
+ else
+ tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rs]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[ra]);
+ } else if (unlikely(Rc(ctx->opcode) != 0)) {
+ gen_set_Rc0(ctx, cpu_gpr[rs]);
+#if defined(TARGET_PPC64)
+ } else {
+ int prio = 0;
+
+ switch (rs) {
+ case 1:
+ /* Set process priority to low */
+ prio = 2;
+ break;
+ case 6:
+ /* Set process priority to medium-low */
+ prio = 3;
+ break;
+ case 2:
+ /* Set process priority to normal */
+ prio = 4;
+ break;
+#if !defined(CONFIG_USER_ONLY)
+ case 31:
+ if (ctx->mem_idx > 0) {
+ /* Set process priority to very low */
+ prio = 1;
+ }
+ break;
+ case 5:
+ if (ctx->mem_idx > 0) {
+ /* Set process priority to medium-hight */
+ prio = 5;
+ }
+ break;
+ case 3:
+ if (ctx->mem_idx > 0) {
+ /* Set process priority to high */
+ prio = 6;
+ }
+ break;
+ case 7:
+ if (ctx->mem_idx > 1) {
+ /* Set process priority to very high */
+ prio = 7;
+ }
+ break;
+#endif
+ default:
+ /* nop */
+ break;
+ }
+ if (prio) {
+ TCGv t0 = tcg_temp_new();
+ gen_load_spr(t0, SPR_PPR);
+ tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL);
+ tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50);
+ gen_store_spr(SPR_PPR, t0);
+ tcg_temp_free(t0);
+ }
+#endif
+ }
+}
+/* orc & orc. */
+GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER);
+
+/* xor & xor. */
+static void gen_xor(DisasContext *ctx)
+{
+ /* Optimisation for "set to zero" case */
+ if (rS(ctx->opcode) != rB(ctx->opcode))
+ tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ else
+ tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* ori */
+static void gen_ori(DisasContext *ctx)
+{
+ target_ulong uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ /* XXX: should handle special NOPs for POWER series */
+ return;
+ }
+ tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm);
+}
+
+/* oris */
+static void gen_oris(DisasContext *ctx)
+{
+ target_ulong uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm << 16);
+}
+
+/* xori */
+static void gen_xori(DisasContext *ctx)
+{
+ target_ulong uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm);
+}
+
+/* xoris */
+static void gen_xoris(DisasContext *ctx)
+{
+ target_ulong uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm << 16);
+}
+
+/* popcntb : PowerPC 2.03 specification */
+static void gen_popcntb(DisasContext *ctx)
+{
+#if defined(TARGET_PPC64)
+ if (ctx->sf_mode)
+ gen_helper_popcntb_64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ else
+#endif
+ gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+}
+
+#if defined(TARGET_PPC64)
+/* extsw & extsw. */
+GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B);
+
+/* cntlzd */
+static void gen_cntlzd(DisasContext *ctx)
+{
+ gen_helper_cntlzd(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+#endif
+
+/*** Integer rotate ***/
+
+/* rlwimi & rlwimi. */
+static void gen_rlwimi(DisasContext *ctx)
+{
+ uint32_t mb, me, sh;
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ sh = SH(ctx->opcode);
+ if (likely(sh == 0 && mb == 0 && me == 31)) {
+ tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ } else {
+ target_ulong mask;
+ TCGv t1;
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ TCGv_i32 t2 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(t2, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_rotli_i32(t2, t2, sh);
+ tcg_gen_extu_i32_i64(t0, t2);
+ tcg_temp_free_i32(t2);
+#else
+ tcg_gen_rotli_i32(t0, cpu_gpr[rS(ctx->opcode)], sh);
+#endif
+#if defined(TARGET_PPC64)
+ mb += 32;
+ me += 32;
+#endif
+ mask = MASK(mb, me);
+ t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, t0, mask);
+ tcg_gen_andi_tl(t1, cpu_gpr[rA(ctx->opcode)], ~mask);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ }
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* rlwinm & rlwinm. */
+static void gen_rlwinm(DisasContext *ctx)
+{
+ uint32_t mb, me, sh;
+
+ sh = SH(ctx->opcode);
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+
+ if (likely(mb == 0 && me == (31 - sh))) {
+ if (likely(sh == 0)) {
+ tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ } else {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext32u_tl(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_shli_tl(t0, t0, sh);
+ tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+ }
+ } else if (likely(sh != 0 && me == 31 && sh == (32 - mb))) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext32u_tl(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_shri_tl(t0, t0, mb);
+ tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+ } else {
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(t1, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_rotli_i32(t1, t1, sh);
+ tcg_gen_extu_i32_i64(t0, t1);
+ tcg_temp_free_i32(t1);
+#else
+ tcg_gen_rotli_i32(t0, cpu_gpr[rS(ctx->opcode)], sh);
+#endif
+#if defined(TARGET_PPC64)
+ mb += 32;
+ me += 32;
+#endif
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], t0, MASK(mb, me));
+ tcg_temp_free(t0);
+ }
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* rlwnm & rlwnm. */
+static void gen_rlwnm(DisasContext *ctx)
+{
+ uint32_t mb, me;
+ TCGv t0;
+#if defined(TARGET_PPC64)
+ TCGv_i32 t1, t2;
+#endif
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1f);
+#if defined(TARGET_PPC64)
+ t1 = tcg_temp_new_i32();
+ t2 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(t1, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_trunc_i64_i32(t2, t0);
+ tcg_gen_rotl_i32(t1, t1, t2);
+ tcg_gen_extu_i32_i64(t0, t1);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+#else
+ tcg_gen_rotl_i32(t0, cpu_gpr[rS(ctx->opcode)], t0);
+#endif
+ if (unlikely(mb != 0 || me != 31)) {
+#if defined(TARGET_PPC64)
+ mb += 32;
+ me += 32;
+#endif
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], t0, MASK(mb, me));
+ } else {
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ }
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+#if defined(TARGET_PPC64)
+#define GEN_PPC64_R2(name, opc1, opc2) \
+static void glue(gen_, name##0)(DisasContext *ctx) \
+{ \
+ gen_##name(ctx, 0); \
+} \
+ \
+static void glue(gen_, name##1)(DisasContext *ctx) \
+{ \
+ gen_##name(ctx, 1); \
+}
+#define GEN_PPC64_R4(name, opc1, opc2) \
+static void glue(gen_, name##0)(DisasContext *ctx) \
+{ \
+ gen_##name(ctx, 0, 0); \
+} \
+ \
+static void glue(gen_, name##1)(DisasContext *ctx) \
+{ \
+ gen_##name(ctx, 0, 1); \
+} \
+ \
+static void glue(gen_, name##2)(DisasContext *ctx) \
+{ \
+ gen_##name(ctx, 1, 0); \
+} \
+ \
+static void glue(gen_, name##3)(DisasContext *ctx) \
+{ \
+ gen_##name(ctx, 1, 1); \
+}
+
+static inline void gen_rldinm(DisasContext *ctx, uint32_t mb, uint32_t me,
+ uint32_t sh)
+{
+ if (likely(sh != 0 && mb == 0 && me == (63 - sh))) {
+ tcg_gen_shli_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh);
+ } else if (likely(sh != 0 && me == 63 && sh == (64 - mb))) {
+ tcg_gen_shri_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], mb);
+ } else {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_rotli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ if (likely(mb == 0 && me == 63)) {
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ } else {
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], t0, MASK(mb, me));
+ }
+ tcg_temp_free(t0);
+ }
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+/* rldicl - rldicl. */
+static inline void gen_rldicl(DisasContext *ctx, int mbn, int shn)
+{
+ uint32_t sh, mb;
+
+ sh = SH(ctx->opcode) | (shn << 5);
+ mb = MB(ctx->opcode) | (mbn << 5);
+ gen_rldinm(ctx, mb, 63, sh);
+}
+GEN_PPC64_R4(rldicl, 0x1E, 0x00);
+/* rldicr - rldicr. */
+static inline void gen_rldicr(DisasContext *ctx, int men, int shn)
+{
+ uint32_t sh, me;
+
+ sh = SH(ctx->opcode) | (shn << 5);
+ me = MB(ctx->opcode) | (men << 5);
+ gen_rldinm(ctx, 0, me, sh);
+}
+GEN_PPC64_R4(rldicr, 0x1E, 0x02);
+/* rldic - rldic. */
+static inline void gen_rldic(DisasContext *ctx, int mbn, int shn)
+{
+ uint32_t sh, mb;
+
+ sh = SH(ctx->opcode) | (shn << 5);
+ mb = MB(ctx->opcode) | (mbn << 5);
+ gen_rldinm(ctx, mb, 63 - sh, sh);
+}
+GEN_PPC64_R4(rldic, 0x1E, 0x04);
+
+static inline void gen_rldnm(DisasContext *ctx, uint32_t mb, uint32_t me)
+{
+ TCGv t0;
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x3f);
+ tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ if (unlikely(mb != 0 || me != 63)) {
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], t0, MASK(mb, me));
+ } else {
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ }
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* rldcl - rldcl. */
+static inline void gen_rldcl(DisasContext *ctx, int mbn)
+{
+ uint32_t mb;
+
+ mb = MB(ctx->opcode) | (mbn << 5);
+ gen_rldnm(ctx, mb, 63);
+}
+GEN_PPC64_R2(rldcl, 0x1E, 0x08);
+/* rldcr - rldcr. */
+static inline void gen_rldcr(DisasContext *ctx, int men)
+{
+ uint32_t me;
+
+ me = MB(ctx->opcode) | (men << 5);
+ gen_rldnm(ctx, 0, me);
+}
+GEN_PPC64_R2(rldcr, 0x1E, 0x09);
+/* rldimi - rldimi. */
+static inline void gen_rldimi(DisasContext *ctx, int mbn, int shn)
+{
+ uint32_t sh, mb, me;
+
+ sh = SH(ctx->opcode) | (shn << 5);
+ mb = MB(ctx->opcode) | (mbn << 5);
+ me = 63 - sh;
+ if (unlikely(sh == 0 && mb == 0)) {
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ } else {
+ TCGv t0, t1;
+ target_ulong mask;
+
+ t0 = tcg_temp_new();
+ tcg_gen_rotli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ t1 = tcg_temp_new();
+ mask = MASK(mb, me);
+ tcg_gen_andi_tl(t0, t0, mask);
+ tcg_gen_andi_tl(t1, cpu_gpr[rA(ctx->opcode)], ~mask);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ }
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+GEN_PPC64_R4(rldimi, 0x1E, 0x06);
+#endif
+
+/*** Integer shift ***/
+
+/* slw & slw. */
+static void gen_slw(DisasContext *ctx)
+{
+ TCGv t0, t1;
+
+ t0 = tcg_temp_new();
+ /* AND rS with a mask that is 0 when rB >= 0x20 */
+#if defined(TARGET_PPC64)
+ tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x3a);
+ tcg_gen_sari_tl(t0, t0, 0x3f);
+#else
+ tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1a);
+ tcg_gen_sari_tl(t0, t0, 0x1f);
+#endif
+ tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1f);
+ tcg_gen_shl_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ tcg_gen_ext32u_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sraw & sraw. */
+static void gen_sraw(DisasContext *ctx)
+{
+ gen_helper_sraw(cpu_gpr[rA(ctx->opcode)],
+ cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srawi & srawi. */
+static void gen_srawi(DisasContext *ctx)
+{
+ int sh = SH(ctx->opcode);
+ if (sh != 0) {
+ int l1, l2;
+ TCGv t0;
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ t0 = tcg_temp_local_new();
+ tcg_gen_ext32s_tl(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1);
+ tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1ULL << sh) - 1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ gen_set_label(l2);
+ tcg_gen_ext32s_tl(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], t0, sh);
+ tcg_temp_free(t0);
+ } else {
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ }
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srw & srw. */
+static void gen_srw(DisasContext *ctx)
+{
+ TCGv t0, t1;
+
+ t0 = tcg_temp_new();
+ /* AND rS with a mask that is 0 when rB >= 0x20 */
+#if defined(TARGET_PPC64)
+ tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x3a);
+ tcg_gen_sari_tl(t0, t0, 0x3f);
+#else
+ tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1a);
+ tcg_gen_sari_tl(t0, t0, 0x1f);
+#endif
+ tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ tcg_gen_ext32u_tl(t0, t0);
+ t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1f);
+ tcg_gen_shr_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+#if defined(TARGET_PPC64)
+/* sld & sld. */
+static void gen_sld(DisasContext *ctx)
+{
+ TCGv t0, t1;
+
+ t0 = tcg_temp_new();
+ /* AND rS with a mask that is 0 when rB >= 0x40 */
+ tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x39);
+ tcg_gen_sari_tl(t0, t0, 0x3f);
+ tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x3f);
+ tcg_gen_shl_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srad & srad. */
+static void gen_srad(DisasContext *ctx)
+{
+ gen_helper_srad(cpu_gpr[rA(ctx->opcode)],
+ cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+/* sradi & sradi. */
+static inline void gen_sradi(DisasContext *ctx, int n)
+{
+ int sh = SH(ctx->opcode) + (n << 5);
+ if (sh != 0) {
+ int l1, l2;
+ TCGv t0;
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ t0 = tcg_temp_local_new();
+ tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rS(ctx->opcode)], 0, l1);
+ tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1ULL << sh) - 1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, 1 << XER_CA);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ gen_set_label(l2);
+ tcg_temp_free(t0);
+ tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh);
+ } else {
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ }
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+static void gen_sradi0(DisasContext *ctx)
+{
+ gen_sradi(ctx, 0);
+}
+
+static void gen_sradi1(DisasContext *ctx)
+{
+ gen_sradi(ctx, 1);
+}
+
+/* srd & srd. */
+static void gen_srd(DisasContext *ctx)
+{
+ TCGv t0, t1;
+
+ t0 = tcg_temp_new();
+ /* AND rS with a mask that is 0 when rB >= 0x40 */
+ tcg_gen_shli_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x39);
+ tcg_gen_sari_tl(t0, t0, 0x3f);
+ tcg_gen_andc_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x3f);
+ tcg_gen_shr_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t1);
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+#endif
+
+/*** Floating-Point arithmetic ***/
+#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \
+static void gen_f##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ /* NIP cannot be restored if the memory exception comes from an helper */ \
+ gen_update_nip(ctx, ctx->nip - 4); \
+ gen_reset_fpstatus(); \
+ gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], \
+ cpu_fpr[rC(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \
+ if (isfloat) { \
+ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], set_fprf, \
+ Rc(ctx->opcode) != 0); \
+}
+
+#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \
+_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type); \
+_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type);
+
+#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \
+static void gen_f##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ /* NIP cannot be restored if the memory exception comes from an helper */ \
+ gen_update_nip(ctx, ctx->nip - 4); \
+ gen_reset_fpstatus(); \
+ gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], \
+ cpu_fpr[rB(ctx->opcode)]); \
+ if (isfloat) { \
+ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
+ set_fprf, Rc(ctx->opcode) != 0); \
+}
+#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \
+_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \
+_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type);
+
+#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \
+static void gen_f##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ /* NIP cannot be restored if the memory exception comes from an helper */ \
+ gen_update_nip(ctx, ctx->nip - 4); \
+ gen_reset_fpstatus(); \
+ gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], \
+ cpu_fpr[rC(ctx->opcode)]); \
+ if (isfloat) { \
+ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
+ set_fprf, Rc(ctx->opcode) != 0); \
+}
+#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \
+_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \
+_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type);
+
+#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \
+static void gen_f##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ /* NIP cannot be restored if the memory exception comes from an helper */ \
+ gen_update_nip(ctx, ctx->nip - 4); \
+ gen_reset_fpstatus(); \
+ gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
+ set_fprf, Rc(ctx->opcode) != 0); \
+}
+
+#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \
+static void gen_f##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ /* NIP cannot be restored if the memory exception comes from an helper */ \
+ gen_update_nip(ctx, ctx->nip - 4); \
+ gen_reset_fpstatus(); \
+ gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
+ set_fprf, Rc(ctx->opcode) != 0); \
+}
+
+/* fadd - fadds */
+GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT);
+/* fdiv - fdivs */
+GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT);
+/* fmul - fmuls */
+GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT);
+
+/* fre */
+GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT);
+
+/* fres */
+GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES);
+
+/* frsqrte */
+GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE);
+
+/* frsqrtes */
+static void gen_frsqrtes(DisasContext *ctx)
+{
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ gen_helper_frsqrte(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]);
+ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0);
+}
+
+/* fsel */
+_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL);
+/* fsub - fsubs */
+GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT);
+/* Optional: */
+
+/* fsqrt */
+static void gen_fsqrt(DisasContext *ctx)
+{
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0);
+}
+
+static void gen_fsqrts(DisasContext *ctx)
+{
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]);
+ gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rD(ctx->opcode)]);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0);
+}
+
+/*** Floating-Point multiply-and-add ***/
+/* fmadd - fmadds */
+GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT);
+/* fmsub - fmsubs */
+GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT);
+/* fnmadd - fnmadds */
+GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT);
+/* fnmsub - fnmsubs */
+GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT);
+
+/*** Floating-Point round & convert ***/
+/* fctiw */
+GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT);
+/* fctiwz */
+GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT);
+/* frsp */
+GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT);
+#if defined(TARGET_PPC64)
+/* fcfid */
+GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B);
+/* fctid */
+GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B);
+/* fctidz */
+GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B);
+#endif
+
+/* frin */
+GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT);
+/* friz */
+GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT);
+/* frip */
+GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT);
+/* frim */
+GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT);
+
+/*** Floating-Point compare ***/
+
+/* fcmpo */
+static void gen_fcmpo(DisasContext *ctx)
+{
+ TCGv_i32 crf;
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ crf = tcg_const_i32(crfD(ctx->opcode));
+ gen_helper_fcmpo(cpu_fpr[rA(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], crf);
+ tcg_temp_free_i32(crf);
+ gen_helper_float_check_status();
+}
+
+/* fcmpu */
+static void gen_fcmpu(DisasContext *ctx)
+{
+ TCGv_i32 crf;
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ crf = tcg_const_i32(crfD(ctx->opcode));
+ gen_helper_fcmpu(cpu_fpr[rA(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], crf);
+ tcg_temp_free_i32(crf);
+ gen_helper_float_check_status();
+}
+
+/*** Floating-point move ***/
+/* fabs */
+/* XXX: beware that fabs never checks for NaNs nor update FPSCR */
+GEN_FLOAT_B(abs, 0x08, 0x08, 0, PPC_FLOAT);
+
+/* fmr - fmr. */
+/* XXX: beware that fmr never checks for NaNs nor update FPSCR */
+static void gen_fmr(DisasContext *ctx)
+{
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ tcg_gen_mov_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+}
+
+/* fnabs */
+/* XXX: beware that fnabs never checks for NaNs nor update FPSCR */
+GEN_FLOAT_B(nabs, 0x08, 0x04, 0, PPC_FLOAT);
+/* fneg */
+/* XXX: beware that fneg never checks for NaNs nor update FPSCR */
+GEN_FLOAT_B(neg, 0x08, 0x01, 0, PPC_FLOAT);
+
+/*** Floating-Point status & ctrl register ***/
+
+/* mcrfs */
+static void gen_mcrfs(DisasContext *ctx)
+{
+ int bfa;
+
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ bfa = 4 * (7 - crfS(ctx->opcode));
+ tcg_gen_shri_i32(cpu_crf[crfD(ctx->opcode)], cpu_fpscr, bfa);
+ tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], 0xf);
+ tcg_gen_andi_i32(cpu_fpscr, cpu_fpscr, ~(0xF << bfa));
+}
+
+/* mffs */
+static void gen_mffs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ gen_reset_fpstatus();
+ tcg_gen_extu_i32_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpscr);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+}
+
+/* mtfsb0 */
+static void gen_mtfsb0(DisasContext *ctx)
+{
+ uint8_t crb;
+
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ crb = 31 - crbD(ctx->opcode);
+ gen_reset_fpstatus();
+ if (likely(crb != FPSCR_FEX && crb != FPSCR_VX)) {
+ TCGv_i32 t0;
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_const_i32(crb);
+ gen_helper_fpscr_clrbit(t0);
+ tcg_temp_free_i32(t0);
+ }
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX);
+ }
+}
+
+/* mtfsb1 */
+static void gen_mtfsb1(DisasContext *ctx)
+{
+ uint8_t crb;
+
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ crb = 31 - crbD(ctx->opcode);
+ gen_reset_fpstatus();
+ /* XXX: we pretend we can only do IEEE floating-point computations */
+ if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) {
+ TCGv_i32 t0;
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_const_i32(crb);
+ gen_helper_fpscr_setbit(t0);
+ tcg_temp_free_i32(t0);
+ }
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX);
+ }
+ /* We can raise a differed exception */
+ gen_helper_float_check_status();
+}
+
+/* mtfsf */
+static void gen_mtfsf(DisasContext *ctx)
+{
+ TCGv_i32 t0;
+ int L = ctx->opcode & 0x02000000;
+
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ if (L)
+ t0 = tcg_const_i32(0xff);
+ else
+ t0 = tcg_const_i32(FM(ctx->opcode));
+ gen_helper_store_fpscr(cpu_fpr[rB(ctx->opcode)], t0);
+ tcg_temp_free_i32(t0);
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX);
+ }
+ /* We can raise a differed exception */
+ gen_helper_float_check_status();
+}
+
+/* mtfsfi */
+static void gen_mtfsfi(DisasContext *ctx)
+{
+ int bf, sh;
+ TCGv_i64 t0;
+ TCGv_i32 t1;
+
+ if (unlikely(!ctx->fpu_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_FPU);
+ return;
+ }
+ bf = crbD(ctx->opcode) >> 2;
+ sh = 7 - bf;
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_reset_fpstatus();
+ t0 = tcg_const_i64(FPIMM(ctx->opcode) << (4 * sh));
+ t1 = tcg_const_i32(1 << sh);
+ gen_helper_store_fpscr(t0, t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i32(t1);
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ tcg_gen_shri_i32(cpu_crf[1], cpu_fpscr, FPSCR_OX);
+ }
+ /* We can raise a differed exception */
+ gen_helper_float_check_status();
+}
+
+/*** Addressing modes ***/
+/* Register indirect with immediate index : EA = (rA|0) + SIMM */
+static inline void gen_addr_imm_index(DisasContext *ctx, TCGv EA,
+ target_long maskl)
+{
+ target_long simm = SIMM(ctx->opcode);
+
+ simm &= ~maskl;
+ if (rA(ctx->opcode) == 0) {
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_movi_tl(EA, (uint32_t)simm);
+ } else
+#endif
+ tcg_gen_movi_tl(EA, simm);
+ } else if (likely(simm != 0)) {
+ tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], simm);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(EA, EA);
+ }
+#endif
+ } else {
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+ } else
+#endif
+ tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+ }
+}
+
+static inline void gen_addr_reg_index(DisasContext *ctx, TCGv EA)
+{
+ if (rA(ctx->opcode) == 0) {
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(EA, cpu_gpr[rB(ctx->opcode)]);
+ } else
+#endif
+ tcg_gen_mov_tl(EA, cpu_gpr[rB(ctx->opcode)]);
+ } else {
+ tcg_gen_add_tl(EA, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(EA, EA);
+ }
+#endif
+ }
+}
+
+static inline void gen_addr_register(DisasContext *ctx, TCGv EA)
+{
+ if (rA(ctx->opcode) == 0) {
+ tcg_gen_movi_tl(EA, 0);
+ } else {
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+ } else
+#endif
+ tcg_gen_mov_tl(EA, cpu_gpr[rA(ctx->opcode)]);
+ }
+}
+
+static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1,
+ target_long val)
+{
+ tcg_gen_addi_tl(ret, arg1, val);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(ret, ret);
+ }
+#endif
+}
+
+static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask)
+{
+ int l1 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv_i32 t1, t2;
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ tcg_gen_andi_tl(t0, EA, mask);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
+ t1 = tcg_const_i32(POWERPC_EXCP_ALIGN);
+ t2 = tcg_const_i32(0);
+ gen_helper_raise_exception_err(t1, t2);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+}
+
+/*** Integer load ***/
+static inline void gen_qemu_ld8u(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld8u(arg1, arg2, ctx->mem_idx);
+}
+
+static inline void gen_qemu_ld8s(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld8s(arg1, arg2, ctx->mem_idx);
+}
+
+static inline void gen_qemu_ld16u(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld16u(arg1, arg2, ctx->mem_idx);
+ if (unlikely(ctx->le_mode)) {
+ tcg_gen_bswap16_tl(arg1, arg1);
+ }
+}
+
+static inline void gen_qemu_ld16s(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ if (unlikely(ctx->le_mode)) {
+ tcg_gen_qemu_ld16u(arg1, arg2, ctx->mem_idx);
+ tcg_gen_bswap16_tl(arg1, arg1);
+ tcg_gen_ext16s_tl(arg1, arg1);
+ } else {
+ tcg_gen_qemu_ld16s(arg1, arg2, ctx->mem_idx);
+ }
+}
+
+static inline void gen_qemu_ld32u(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld32u(arg1, arg2, ctx->mem_idx);
+ if (unlikely(ctx->le_mode)) {
+ tcg_gen_bswap32_tl(arg1, arg1);
+ }
+}
+
+#if defined(TARGET_PPC64)
+static inline void gen_qemu_ld32s(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ if (unlikely(ctx->le_mode)) {
+ tcg_gen_qemu_ld32u(arg1, arg2, ctx->mem_idx);
+ tcg_gen_bswap32_tl(arg1, arg1);
+ tcg_gen_ext32s_tl(arg1, arg1);
+ } else
+ tcg_gen_qemu_ld32s(arg1, arg2, ctx->mem_idx);
+}
+#endif
+
+static inline void gen_qemu_ld64(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld64(arg1, arg2, ctx->mem_idx);
+ if (unlikely(ctx->le_mode)) {
+ tcg_gen_bswap64_i64(arg1, arg1);
+ }
+}
+
+static inline void gen_qemu_st8(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_st8(arg1, arg2, ctx->mem_idx);
+}
+
+static inline void gen_qemu_st16(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ if (unlikely(ctx->le_mode)) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext16u_tl(t0, arg1);
+ tcg_gen_bswap16_tl(t0, t0);
+ tcg_gen_qemu_st16(t0, arg2, ctx->mem_idx);
+ tcg_temp_free(t0);
+ } else {
+ tcg_gen_qemu_st16(arg1, arg2, ctx->mem_idx);
+ }
+}
+
+static inline void gen_qemu_st32(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ if (unlikely(ctx->le_mode)) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext32u_tl(t0, arg1);
+ tcg_gen_bswap32_tl(t0, t0);
+ tcg_gen_qemu_st32(t0, arg2, ctx->mem_idx);
+ tcg_temp_free(t0);
+ } else {
+ tcg_gen_qemu_st32(arg1, arg2, ctx->mem_idx);
+ }
+}
+
+static inline void gen_qemu_st64(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
+{
+ if (unlikely(ctx->le_mode)) {
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ tcg_gen_bswap64_i64(t0, arg1);
+ tcg_gen_qemu_st64(t0, arg2, ctx->mem_idx);
+ tcg_temp_free_i64(t0);
+ } else
+ tcg_gen_qemu_st64(arg1, arg2, ctx->mem_idx);
+}
+
+#define GEN_LD(name, ldop, opc, type) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDU(name, ldop, opc, type) \
+static void glue(gen_, name##u)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode))) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ if (type == PPC_64B) \
+ gen_addr_imm_index(ctx, EA, 0x03); \
+ else \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDUX(name, ldop, opc2, opc3, type) \
+static void glue(gen_, name##ux)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode))) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDX(name, ldop, opc2, opc3, type) \
+static void glue(gen_, name##x)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##ldop(ctx, cpu_gpr[rD(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDS(name, ldop, op, type) \
+GEN_LD(name, ldop, op | 0x20, type); \
+GEN_LDU(name, ldop, op | 0x21, type); \
+GEN_LDUX(name, ldop, 0x17, op | 0x01, type); \
+GEN_LDX(name, ldop, 0x17, op | 0x00, type)
+
+/* lbz lbzu lbzux lbzx */
+GEN_LDS(lbz, ld8u, 0x02, PPC_INTEGER);
+/* lha lhau lhaux lhax */
+GEN_LDS(lha, ld16s, 0x0A, PPC_INTEGER);
+/* lhz lhzu lhzux lhzx */
+GEN_LDS(lhz, ld16u, 0x08, PPC_INTEGER);
+/* lwz lwzu lwzux lwzx */
+GEN_LDS(lwz, ld32u, 0x00, PPC_INTEGER);
+#if defined(TARGET_PPC64)
+/* lwaux */
+GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B);
+/* lwax */
+GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B);
+/* ldux */
+GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B);
+/* ldx */
+GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B);
+
+static void gen_ld(DisasContext *ctx)
+{
+ TCGv EA;
+ if (Rc(ctx->opcode)) {
+ if (unlikely(rA(ctx->opcode) == 0 ||
+ rA(ctx->opcode) == rD(ctx->opcode))) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ return;
+ }
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_imm_index(ctx, EA, 0x03);
+ if (ctx->opcode & 0x02) {
+ /* lwa (lwau is undefined) */
+ gen_qemu_ld32s(ctx, cpu_gpr[rD(ctx->opcode)], EA);
+ } else {
+ /* ld - ldu */
+ gen_qemu_ld64(ctx, cpu_gpr[rD(ctx->opcode)], EA);
+ }
+ if (Rc(ctx->opcode))
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA);
+ tcg_temp_free(EA);
+}
+
+/* lq */
+static void gen_lq(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ int ra, rd;
+ TCGv EA;
+
+ /* Restore CPU state */
+ if (unlikely(ctx->mem_idx == 0)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ ra = rA(ctx->opcode);
+ rd = rD(ctx->opcode);
+ if (unlikely((rd & 1) || rd == ra)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ return;
+ }
+ if (unlikely(ctx->le_mode)) {
+ /* Little-endian mode is not handled */
+ gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_imm_index(ctx, EA, 0x0F);
+ gen_qemu_ld64(ctx, cpu_gpr[rd], EA);
+ gen_addr_add(ctx, EA, EA, 8);
+ gen_qemu_ld64(ctx, cpu_gpr[rd+1], EA);
+ tcg_temp_free(EA);
+#endif
+}
+#endif
+
+/*** Integer store ***/
+#define GEN_ST(name, stop, opc, type) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STU(name, stop, opc, type) \
+static void glue(gen_, stop##u)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(rA(ctx->opcode) == 0)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ if (type == PPC_64B) \
+ gen_addr_imm_index(ctx, EA, 0x03); \
+ else \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STUX(name, stop, opc2, opc3, type) \
+static void glue(gen_, name##ux)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(rA(ctx->opcode) == 0)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STX(name, stop, opc2, opc3, type) \
+static void glue(gen_, name##x)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##stop(ctx, cpu_gpr[rS(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STS(name, stop, op, type) \
+GEN_ST(name, stop, op | 0x20, type); \
+GEN_STU(name, stop, op | 0x21, type); \
+GEN_STUX(name, stop, 0x17, op | 0x01, type); \
+GEN_STX(name, stop, 0x17, op | 0x00, type)
+
+/* stb stbu stbux stbx */
+GEN_STS(stb, st8, 0x06, PPC_INTEGER);
+/* sth sthu sthux sthx */
+GEN_STS(sth, st16, 0x0C, PPC_INTEGER);
+/* stw stwu stwux stwx */
+GEN_STS(stw, st32, 0x04, PPC_INTEGER);
+#if defined(TARGET_PPC64)
+GEN_STUX(std, st64, 0x15, 0x05, PPC_64B);
+GEN_STX(std, st64, 0x15, 0x04, PPC_64B);
+
+static void gen_std(DisasContext *ctx)
+{
+ int rs;
+ TCGv EA;
+
+ rs = rS(ctx->opcode);
+ if ((ctx->opcode & 0x3) == 0x2) {
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ /* stq */
+ if (unlikely(ctx->mem_idx == 0)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ if (unlikely(rs & 1)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ return;
+ }
+ if (unlikely(ctx->le_mode)) {
+ /* Little-endian mode is not handled */
+ gen_exception_err(ctx, POWERPC_EXCP_ALIGN, POWERPC_EXCP_ALIGN_LE);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_imm_index(ctx, EA, 0x03);
+ gen_qemu_st64(ctx, cpu_gpr[rs], EA);
+ gen_addr_add(ctx, EA, EA, 8);
+ gen_qemu_st64(ctx, cpu_gpr[rs+1], EA);
+ tcg_temp_free(EA);
+#endif
+ } else {
+ /* std / stdu */
+ if (Rc(ctx->opcode)) {
+ if (unlikely(rA(ctx->opcode) == 0)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ return;
+ }
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ EA = tcg_temp_new();
+ gen_addr_imm_index(ctx, EA, 0x03);
+ gen_qemu_st64(ctx, cpu_gpr[rs], EA);
+ if (Rc(ctx->opcode))
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA);
+ tcg_temp_free(EA);
+ }
+}
+#endif
+/*** Integer load and store with byte reverse ***/
+/* lhbrx */
+static inline void gen_qemu_ld16ur(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld16u(arg1, arg2, ctx->mem_idx);
+ if (likely(!ctx->le_mode)) {
+ tcg_gen_bswap16_tl(arg1, arg1);
+ }
+}
+GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER);
+
+/* lwbrx */
+static inline void gen_qemu_ld32ur(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ tcg_gen_qemu_ld32u(arg1, arg2, ctx->mem_idx);
+ if (likely(!ctx->le_mode)) {
+ tcg_gen_bswap32_tl(arg1, arg1);
+ }
+}
+GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER);
+
+/* sthbrx */
+static inline void gen_qemu_st16r(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ if (likely(!ctx->le_mode)) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext16u_tl(t0, arg1);
+ tcg_gen_bswap16_tl(t0, t0);
+ tcg_gen_qemu_st16(t0, arg2, ctx->mem_idx);
+ tcg_temp_free(t0);
+ } else {
+ tcg_gen_qemu_st16(arg1, arg2, ctx->mem_idx);
+ }
+}
+GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER);
+
+/* stwbrx */
+static inline void gen_qemu_st32r(DisasContext *ctx, TCGv arg1, TCGv arg2)
+{
+ if (likely(!ctx->le_mode)) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext32u_tl(t0, arg1);
+ tcg_gen_bswap32_tl(t0, t0);
+ tcg_gen_qemu_st32(t0, arg2, ctx->mem_idx);
+ tcg_temp_free(t0);
+ } else {
+ tcg_gen_qemu_st32(arg1, arg2, ctx->mem_idx);
+ }
+}
+GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER);
+
+/*** Integer load and store multiple ***/
+
+/* lmw */
+static void gen_lmw(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv_i32 t1;
+ gen_set_access_type(ctx, ACCESS_INT);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ t1 = tcg_const_i32(rD(ctx->opcode));
+ gen_addr_imm_index(ctx, t0, 0);
+ gen_helper_lmw(t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+}
+
+/* stmw */
+static void gen_stmw(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv_i32 t1;
+ gen_set_access_type(ctx, ACCESS_INT);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ t1 = tcg_const_i32(rS(ctx->opcode));
+ gen_addr_imm_index(ctx, t0, 0);
+ gen_helper_stmw(t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+}
+
+/*** Integer load and store strings ***/
+
+/* lswi */
+/* PowerPC32 specification says we must generate an exception if
+ * rA is in the range of registers to be loaded.
+ * In an other hand, IBM says this is valid, but rA won't be loaded.
+ * For now, I'll follow the spec...
+ */
+static void gen_lswi(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv_i32 t1, t2;
+ int nb = NB(ctx->opcode);
+ int start = rD(ctx->opcode);
+ int ra = rA(ctx->opcode);
+ int nr;
+
+ if (nb == 0)
+ nb = 32;
+ nr = nb / 4;
+ if (unlikely(((start + nr) > 32 &&
+ start <= ra && (start + nr - 32) > ra) ||
+ ((start + nr) <= 32 && start <= ra && (start + nr) > ra))) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_LSWX);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_INT);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_register(ctx, t0);
+ t1 = tcg_const_i32(nb);
+ t2 = tcg_const_i32(start);
+ gen_helper_lsw(t0, t1, t2);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+}
+
+/* lswx */
+static void gen_lswx(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv_i32 t1, t2, t3;
+ gen_set_access_type(ctx, ACCESS_INT);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ t1 = tcg_const_i32(rD(ctx->opcode));
+ t2 = tcg_const_i32(rA(ctx->opcode));
+ t3 = tcg_const_i32(rB(ctx->opcode));
+ gen_helper_lswx(t0, t1, t2, t3);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t3);
+}
+
+/* stswi */
+static void gen_stswi(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv_i32 t1, t2;
+ int nb = NB(ctx->opcode);
+ gen_set_access_type(ctx, ACCESS_INT);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_register(ctx, t0);
+ if (nb == 0)
+ nb = 32;
+ t1 = tcg_const_i32(nb);
+ t2 = tcg_const_i32(rS(ctx->opcode));
+ gen_helper_stsw(t0, t1, t2);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+}
+
+/* stswx */
+static void gen_stswx(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv_i32 t1, t2;
+ gen_set_access_type(ctx, ACCESS_INT);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ t1 = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(t1, cpu_xer);
+ tcg_gen_andi_i32(t1, t1, 0x7F);
+ t2 = tcg_const_i32(rS(ctx->opcode));
+ gen_helper_stsw(t0, t1, t2);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+}
+
+/*** Memory synchronisation ***/
+/* eieio */
+static void gen_eieio(DisasContext *ctx)
+{
+}
+
+/* isync */
+static void gen_isync(DisasContext *ctx)
+{
+ gen_stop_exception(ctx);
+}
+
+/* lwarx */
+static void gen_lwarx(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv gpr = cpu_gpr[rD(ctx->opcode)];
+ gen_set_access_type(ctx, ACCESS_RES);
+ t0 = tcg_temp_local_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_check_align(ctx, t0, 0x03);
+ gen_qemu_ld32u(ctx, gpr, t0);
+ tcg_gen_mov_tl(cpu_reserve, t0);
+ tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUState, reserve_val));
+ tcg_temp_free(t0);
+}
+
+#if defined(CONFIG_USER_ONLY)
+static void gen_conditional_store (DisasContext *ctx, TCGv EA,
+ int reg, int size)
+{
+ TCGv t0 = tcg_temp_new();
+ uint32_t save_exception = ctx->exception;
+
+ tcg_gen_st_tl(EA, cpu_env, offsetof(CPUState, reserve_ea));
+ tcg_gen_movi_tl(t0, (size << 5) | reg);
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, reserve_info));
+ tcg_temp_free(t0);
+ gen_update_nip(ctx, ctx->nip-4);
+ ctx->exception = POWERPC_EXCP_BRANCH;
+ gen_exception(ctx, POWERPC_EXCP_STCX);
+ ctx->exception = save_exception;
+}
+#endif
+
+/* stwcx. */
+static void gen_stwcx_(DisasContext *ctx)
+{
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_RES);
+ t0 = tcg_temp_local_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_check_align(ctx, t0, 0x03);
+#if defined(CONFIG_USER_ONLY)
+ gen_conditional_store(ctx, t0, rS(ctx->opcode), 4);
+#else
+ {
+ int l1;
+
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer);
+ tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO);
+ tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1);
+ l1 = gen_new_label();
+ tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1);
+ tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ);
+ gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], t0);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_reserve, -1);
+ }
+#endif
+ tcg_temp_free(t0);
+}
+
+#if defined(TARGET_PPC64)
+/* ldarx */
+static void gen_ldarx(DisasContext *ctx)
+{
+ TCGv t0;
+ TCGv gpr = cpu_gpr[rD(ctx->opcode)];
+ gen_set_access_type(ctx, ACCESS_RES);
+ t0 = tcg_temp_local_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_check_align(ctx, t0, 0x07);
+ gen_qemu_ld64(ctx, gpr, t0);
+ tcg_gen_mov_tl(cpu_reserve, t0);
+ tcg_gen_st_tl(gpr, cpu_env, offsetof(CPUState, reserve_val));
+ tcg_temp_free(t0);
+}
+
+/* stdcx. */
+static void gen_stdcx_(DisasContext *ctx)
+{
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_RES);
+ t0 = tcg_temp_local_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_check_align(ctx, t0, 0x07);
+#if defined(CONFIG_USER_ONLY)
+ gen_conditional_store(ctx, t0, rS(ctx->opcode), 8);
+#else
+ {
+ int l1;
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer);
+ tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO);
+ tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1);
+ l1 = gen_new_label();
+ tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1);
+ tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 1 << CRF_EQ);
+ gen_qemu_st64(ctx, cpu_gpr[rS(ctx->opcode)], t0);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_reserve, -1);
+ }
+#endif
+ tcg_temp_free(t0);
+}
+#endif /* defined(TARGET_PPC64) */
+
+/* sync */
+static void gen_sync(DisasContext *ctx)
+{
+}
+
+/* wait */
+static void gen_wait(DisasContext *ctx)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ tcg_gen_st_i32(t0, cpu_env, offsetof(CPUState, halted));
+ tcg_temp_free_i32(t0);
+ /* Stop translation, as the CPU is supposed to sleep from now */
+ gen_exception_err(ctx, EXCP_HLT, 1);
+}
+
+/*** Floating-point load ***/
+#define GEN_LDF(name, ldop, opc, type) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDUF(name, ldop, opc, type) \
+static void glue(gen_, name##u)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ if (unlikely(rA(ctx->opcode) == 0)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDUXF(name, ldop, opc, type) \
+static void glue(gen_, name##ux)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ if (unlikely(rA(ctx->opcode) == 0)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDXF(name, ldop, opc2, opc3, type) \
+static void glue(gen_, name##x)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_LDFS(name, ldop, op, type) \
+GEN_LDF(name, ldop, op | 0x20, type); \
+GEN_LDUF(name, ldop, op | 0x21, type); \
+GEN_LDUXF(name, ldop, op | 0x01, type); \
+GEN_LDXF(name, ldop, 0x17, op | 0x00, type)
+
+static inline void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv_i32 t1 = tcg_temp_new_i32();
+ gen_qemu_ld32u(ctx, t0, arg2);
+ tcg_gen_trunc_tl_i32(t1, t0);
+ tcg_temp_free(t0);
+ gen_helper_float32_to_float64(arg1, t1);
+ tcg_temp_free_i32(t1);
+}
+
+ /* lfd lfdu lfdux lfdx */
+GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT);
+ /* lfs lfsu lfsux lfsx */
+GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT);
+
+/*** Floating-point store ***/
+#define GEN_STF(name, stop, opc, type) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STUF(name, stop, opc, type) \
+static void glue(gen_, name##u)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ if (unlikely(rA(ctx->opcode) == 0)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_imm_index(ctx, EA, 0); \
+ gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STUXF(name, stop, opc, type) \
+static void glue(gen_, name##ux)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ if (unlikely(rA(ctx->opcode) == 0)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STXF(name, stop, opc2, opc3, type) \
+static void glue(gen_, name##x)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->fpu_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_FPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_FLOAT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_STFS(name, stop, op, type) \
+GEN_STF(name, stop, op | 0x20, type); \
+GEN_STUF(name, stop, op | 0x21, type); \
+GEN_STUXF(name, stop, op | 0x01, type); \
+GEN_STXF(name, stop, 0x17, op | 0x00, type)
+
+static inline void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new();
+ gen_helper_float64_to_float32(t0, arg1);
+ tcg_gen_extu_i32_tl(t1, t0);
+ tcg_temp_free_i32(t0);
+ gen_qemu_st32(ctx, t1, arg2);
+ tcg_temp_free(t1);
+}
+
+/* stfd stfdu stfdux stfdx */
+GEN_STFS(stfd, st64, 0x16, PPC_FLOAT);
+/* stfs stfsu stfsux stfsx */
+GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT);
+
+/* Optional: */
+static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
+{
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_trunc_i64_tl(t0, arg1),
+ gen_qemu_st32(ctx, t0, arg2);
+ tcg_temp_free(t0);
+}
+/* stfiwx */
+GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX);
+
+/*** Branch ***/
+static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode)
+ dest = (uint32_t) dest;
+#endif
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
+ likely(!ctx->singlestep_enabled)) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_tl(cpu_nip, dest & ~3);
+ tcg_gen_exit_tb((long)tb + n);
+ } else {
+ tcg_gen_movi_tl(cpu_nip, dest & ~3);
+ if (unlikely(ctx->singlestep_enabled)) {
+ if ((ctx->singlestep_enabled &
+ (CPU_BRANCH_STEP | CPU_SINGLE_STEP)) &&
+ ctx->exception == POWERPC_EXCP_BRANCH) {
+ target_ulong tmp = ctx->nip;
+ ctx->nip = dest;
+ gen_exception(ctx, POWERPC_EXCP_TRACE);
+ ctx->nip = tmp;
+ }
+ if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) {
+ gen_debug_exception(ctx);
+ }
+ }
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static inline void gen_setlr(DisasContext *ctx, target_ulong nip)
+{
+#if defined(TARGET_PPC64)
+ if (ctx->sf_mode == 0)
+ tcg_gen_movi_tl(cpu_lr, (uint32_t)nip);
+ else
+#endif
+ tcg_gen_movi_tl(cpu_lr, nip);
+}
+
+/* b ba bl bla */
+static void gen_b(DisasContext *ctx)
+{
+ target_ulong li, target;
+
+ ctx->exception = POWERPC_EXCP_BRANCH;
+ /* sign extend LI */
+#if defined(TARGET_PPC64)
+ if (ctx->sf_mode)
+ li = ((int64_t)LI(ctx->opcode) << 38) >> 38;
+ else
+#endif
+ li = ((int32_t)LI(ctx->opcode) << 6) >> 6;
+ if (likely(AA(ctx->opcode) == 0))
+ target = ctx->nip + li - 4;
+ else
+ target = li;
+ if (LK(ctx->opcode))
+ gen_setlr(ctx, ctx->nip);
+ gen_goto_tb(ctx, 0, target);
+}
+
+#define BCOND_IM 0
+#define BCOND_LR 1
+#define BCOND_CTR 2
+
+static inline void gen_bcond(DisasContext *ctx, int type)
+{
+ uint32_t bo = BO(ctx->opcode);
+ int l1;
+ TCGv target;
+
+ ctx->exception = POWERPC_EXCP_BRANCH;
+ if (type == BCOND_LR || type == BCOND_CTR) {
+ target = tcg_temp_local_new();
+ if (type == BCOND_CTR)
+ tcg_gen_mov_tl(target, cpu_ctr);
+ else
+ tcg_gen_mov_tl(target, cpu_lr);
+ } else {
+ TCGV_UNUSED(target);
+ }
+ if (LK(ctx->opcode))
+ gen_setlr(ctx, ctx->nip);
+ l1 = gen_new_label();
+ if ((bo & 0x4) == 0) {
+ /* Decrement and test CTR */
+ TCGv temp = tcg_temp_new();
+ if (unlikely(type == BCOND_CTR)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ return;
+ }
+ tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode)
+ tcg_gen_ext32u_tl(temp, cpu_ctr);
+ else
+#endif
+ tcg_gen_mov_tl(temp, cpu_ctr);
+ if (bo & 0x2) {
+ tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1);
+ } else {
+ tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1);
+ }
+ tcg_temp_free(temp);
+ }
+ if ((bo & 0x10) == 0) {
+ /* Test CR */
+ uint32_t bi = BI(ctx->opcode);
+ uint32_t mask = 1 << (3 - (bi & 0x03));
+ TCGv_i32 temp = tcg_temp_new_i32();
+
+ if (bo & 0x8) {
+ tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, temp, 0, l1);
+ } else {
+ tcg_gen_andi_i32(temp, cpu_crf[bi >> 2], mask);
+ tcg_gen_brcondi_i32(TCG_COND_NE, temp, 0, l1);
+ }
+ tcg_temp_free_i32(temp);
+ }
+ if (type == BCOND_IM) {
+ target_ulong li = (target_long)((int16_t)(BD(ctx->opcode)));
+ if (likely(AA(ctx->opcode) == 0)) {
+ gen_goto_tb(ctx, 0, ctx->nip + li - 4);
+ } else {
+ gen_goto_tb(ctx, 0, li);
+ }
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 1, ctx->nip);
+ } else {
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode))
+ tcg_gen_andi_tl(cpu_nip, target, (uint32_t)~3);
+ else
+#endif
+ tcg_gen_andi_tl(cpu_nip, target, ~3);
+ tcg_gen_exit_tb(0);
+ gen_set_label(l1);
+#if defined(TARGET_PPC64)
+ if (!(ctx->sf_mode))
+ tcg_gen_movi_tl(cpu_nip, (uint32_t)ctx->nip);
+ else
+#endif
+ tcg_gen_movi_tl(cpu_nip, ctx->nip);
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static void gen_bc(DisasContext *ctx)
+{
+ gen_bcond(ctx, BCOND_IM);
+}
+
+static void gen_bcctr(DisasContext *ctx)
+{
+ gen_bcond(ctx, BCOND_CTR);
+}
+
+static void gen_bclr(DisasContext *ctx)
+{
+ gen_bcond(ctx, BCOND_LR);
+}
+
+/*** Condition register logical ***/
+#define GEN_CRLOGIC(name, tcg_op, opc) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ uint8_t bitmask; \
+ int sh; \
+ TCGv_i32 t0, t1; \
+ sh = (crbD(ctx->opcode) & 0x03) - (crbA(ctx->opcode) & 0x03); \
+ t0 = tcg_temp_new_i32(); \
+ if (sh > 0) \
+ tcg_gen_shri_i32(t0, cpu_crf[crbA(ctx->opcode) >> 2], sh); \
+ else if (sh < 0) \
+ tcg_gen_shli_i32(t0, cpu_crf[crbA(ctx->opcode) >> 2], -sh); \
+ else \
+ tcg_gen_mov_i32(t0, cpu_crf[crbA(ctx->opcode) >> 2]); \
+ t1 = tcg_temp_new_i32(); \
+ sh = (crbD(ctx->opcode) & 0x03) - (crbB(ctx->opcode) & 0x03); \
+ if (sh > 0) \
+ tcg_gen_shri_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2], sh); \
+ else if (sh < 0) \
+ tcg_gen_shli_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2], -sh); \
+ else \
+ tcg_gen_mov_i32(t1, cpu_crf[crbB(ctx->opcode) >> 2]); \
+ tcg_op(t0, t0, t1); \
+ bitmask = 1 << (3 - (crbD(ctx->opcode) & 0x03)); \
+ tcg_gen_andi_i32(t0, t0, bitmask); \
+ tcg_gen_andi_i32(t1, cpu_crf[crbD(ctx->opcode) >> 2], ~bitmask); \
+ tcg_gen_or_i32(cpu_crf[crbD(ctx->opcode) >> 2], t0, t1); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+
+/* crand */
+GEN_CRLOGIC(crand, tcg_gen_and_i32, 0x08);
+/* crandc */
+GEN_CRLOGIC(crandc, tcg_gen_andc_i32, 0x04);
+/* creqv */
+GEN_CRLOGIC(creqv, tcg_gen_eqv_i32, 0x09);
+/* crnand */
+GEN_CRLOGIC(crnand, tcg_gen_nand_i32, 0x07);
+/* crnor */
+GEN_CRLOGIC(crnor, tcg_gen_nor_i32, 0x01);
+/* cror */
+GEN_CRLOGIC(cror, tcg_gen_or_i32, 0x0E);
+/* crorc */
+GEN_CRLOGIC(crorc, tcg_gen_orc_i32, 0x0D);
+/* crxor */
+GEN_CRLOGIC(crxor, tcg_gen_xor_i32, 0x06);
+
+/* mcrf */
+static void gen_mcrf(DisasContext *ctx)
+{
+ tcg_gen_mov_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfS(ctx->opcode)]);
+}
+
+/*** System linkage ***/
+
+/* rfi (mem_idx only) */
+static void gen_rfi(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ /* Restore CPU state */
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_rfi();
+ gen_sync_exception(ctx);
+#endif
+}
+
+#if defined(TARGET_PPC64)
+static void gen_rfid(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ /* Restore CPU state */
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_rfid();
+ gen_sync_exception(ctx);
+#endif
+}
+
+static void gen_hrfid(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ /* Restore CPU state */
+ if (unlikely(ctx->mem_idx <= 1)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_hrfid();
+ gen_sync_exception(ctx);
+#endif
+}
+#endif
+
+/* sc */
+#if defined(CONFIG_USER_ONLY)
+#define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL_USER
+#else
+#define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL
+#endif
+static void gen_sc(DisasContext *ctx)
+{
+ uint32_t lev;
+
+ lev = (ctx->opcode >> 5) & 0x7F;
+ gen_exception_err(ctx, POWERPC_SYSCALL, lev);
+}
+
+/*** Trap ***/
+
+/* tw */
+static void gen_tw(DisasContext *ctx)
+{
+ TCGv_i32 t0 = tcg_const_i32(TO(ctx->opcode));
+ /* Update the nip since this might generate a trap exception */
+ gen_update_nip(ctx, ctx->nip);
+ gen_helper_tw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0);
+ tcg_temp_free_i32(t0);
+}
+
+/* twi */
+static void gen_twi(DisasContext *ctx)
+{
+ TCGv t0 = tcg_const_tl(SIMM(ctx->opcode));
+ TCGv_i32 t1 = tcg_const_i32(TO(ctx->opcode));
+ /* Update the nip since this might generate a trap exception */
+ gen_update_nip(ctx, ctx->nip);
+ gen_helper_tw(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+}
+
+#if defined(TARGET_PPC64)
+/* td */
+static void gen_td(DisasContext *ctx)
+{
+ TCGv_i32 t0 = tcg_const_i32(TO(ctx->opcode));
+ /* Update the nip since this might generate a trap exception */
+ gen_update_nip(ctx, ctx->nip);
+ gen_helper_td(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0);
+ tcg_temp_free_i32(t0);
+}
+
+/* tdi */
+static void gen_tdi(DisasContext *ctx)
+{
+ TCGv t0 = tcg_const_tl(SIMM(ctx->opcode));
+ TCGv_i32 t1 = tcg_const_i32(TO(ctx->opcode));
+ /* Update the nip since this might generate a trap exception */
+ gen_update_nip(ctx, ctx->nip);
+ gen_helper_td(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free_i32(t1);
+}
+#endif
+
+/*** Processor control ***/
+
+/* mcrxr */
+static void gen_mcrxr(DisasContext *ctx)
+{
+ tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], cpu_xer);
+ tcg_gen_shri_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], XER_CA);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_SO | 1 << XER_OV | 1 << XER_CA));
+}
+
+/* mfcr mfocrf */
+static void gen_mfcr(DisasContext *ctx)
+{
+ uint32_t crm, crn;
+
+ if (likely(ctx->opcode & 0x00100000)) {
+ crm = CRM(ctx->opcode);
+ if (likely(crm && ((crm & (crm - 1)) == 0))) {
+ crn = ctz32 (crm);
+ tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], cpu_crf[7 - crn]);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)],
+ cpu_gpr[rD(ctx->opcode)], crn * 4);
+ }
+ } else {
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ tcg_gen_mov_i32(t0, cpu_crf[0]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[1]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[2]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[3]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[4]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[5]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[6]);
+ tcg_gen_shli_i32(t0, t0, 4);
+ tcg_gen_or_i32(t0, t0, cpu_crf[7]);
+ tcg_gen_extu_i32_tl(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+/* mfmsr */
+static void gen_mfmsr(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_msr);
+#endif
+}
+
+static void spr_noaccess(void *opaque, int gprn, int sprn)
+{
+#if 0
+ sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+ printf("ERROR: try to access SPR %d !\n", sprn);
+#endif
+}
+#define SPR_NOACCESS (&spr_noaccess)
+
+/* mfspr */
+static inline void gen_op_mfspr(DisasContext *ctx)
+{
+ void (*read_cb)(void *opaque, int gprn, int sprn);
+ uint32_t sprn = SPR(ctx->opcode);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->mem_idx == 2)
+ read_cb = ctx->spr_cb[sprn].hea_read;
+ else if (ctx->mem_idx)
+ read_cb = ctx->spr_cb[sprn].oea_read;
+ else
+#endif
+ read_cb = ctx->spr_cb[sprn].uea_read;
+ if (likely(read_cb != NULL)) {
+ if (likely(read_cb != SPR_NOACCESS)) {
+ (*read_cb)(ctx, rD(ctx->opcode), sprn);
+ } else {
+ /* Privilege exception */
+ /* This is a hack to avoid warnings when running Linux:
+ * this OS breaks the PowerPC virtualisation model,
+ * allowing userland application to read the PVR
+ */
+ if (sprn != SPR_PVR) {
+ qemu_log("Trying to read privileged spr %d %03x at "
+ TARGET_FMT_lx "\n", sprn, sprn, ctx->nip);
+ printf("Trying to read privileged spr %d %03x at "
+ TARGET_FMT_lx "\n", sprn, sprn, ctx->nip);
+ }
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ }
+ } else {
+ /* Not defined */
+ qemu_log("Trying to read invalid spr %d %03x at "
+ TARGET_FMT_lx "\n", sprn, sprn, ctx->nip);
+ printf("Trying to read invalid spr %d %03x at " TARGET_FMT_lx "\n",
+ sprn, sprn, ctx->nip);
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+ }
+}
+
+static void gen_mfspr(DisasContext *ctx)
+{
+ gen_op_mfspr(ctx);
+}
+
+/* mftb */
+static void gen_mftb(DisasContext *ctx)
+{
+ gen_op_mfspr(ctx);
+}
+
+/* mtcrf mtocrf*/
+static void gen_mtcrf(DisasContext *ctx)
+{
+ uint32_t crm, crn;
+
+ crm = CRM(ctx->opcode);
+ if (likely((ctx->opcode & 0x00100000))) {
+ if (crm && ((crm & (crm - 1)) == 0)) {
+ TCGv_i32 temp = tcg_temp_new_i32();
+ crn = ctz32 (crm);
+ tcg_gen_trunc_tl_i32(temp, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_shri_i32(temp, temp, crn * 4);
+ tcg_gen_andi_i32(cpu_crf[7 - crn], temp, 0xf);
+ tcg_temp_free_i32(temp);
+ }
+ } else {
+ TCGv_i32 temp = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(temp, cpu_gpr[rS(ctx->opcode)]);
+ for (crn = 0 ; crn < 8 ; crn++) {
+ if (crm & (1 << crn)) {
+ tcg_gen_shri_i32(cpu_crf[7 - crn], temp, crn * 4);
+ tcg_gen_andi_i32(cpu_crf[7 - crn], cpu_crf[7 - crn], 0xf);
+ }
+ }
+ tcg_temp_free_i32(temp);
+ }
+}
+
+/* mtmsr */
+#if defined(TARGET_PPC64)
+static void gen_mtmsrd(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ if (ctx->opcode & 0x00010000) {
+ /* Special form that does not need any synchronisation */
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
+ tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+ tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
+ tcg_temp_free(t0);
+ } else {
+ /* XXX: we need to update nip before the store
+ * if we enter power saving mode, we will exit the loop
+ * directly from ppc_store_msr
+ */
+ gen_update_nip(ctx, ctx->nip);
+ gen_helper_store_msr(cpu_gpr[rS(ctx->opcode)]);
+ /* Must stop the translation as machine state (may have) changed */
+ /* Note that mtmsr is not always defined as context-synchronizing */
+ gen_stop_exception(ctx);
+ }
+#endif
+}
+#endif
+
+static void gen_mtmsr(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ if (ctx->opcode & 0x00010000) {
+ /* Special form that does not need any synchronisation */
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rS(ctx->opcode)], (1 << MSR_RI) | (1 << MSR_EE));
+ tcg_gen_andi_tl(cpu_msr, cpu_msr, ~((1 << MSR_RI) | (1 << MSR_EE)));
+ tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
+ tcg_temp_free(t0);
+ } else {
+ /* XXX: we need to update nip before the store
+ * if we enter power saving mode, we will exit the loop
+ * directly from ppc_store_msr
+ */
+ gen_update_nip(ctx, ctx->nip);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_msr, 0xFFFFFFFF00000000ULL);
+ tcg_gen_ext32u_tl(t1, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t1);
+ gen_helper_store_msr(t0);
+ tcg_temp_free(t0);
+ } else
+#endif
+ gen_helper_store_msr(cpu_gpr[rS(ctx->opcode)]);
+ /* Must stop the translation as machine state (may have) changed */
+ /* Note that mtmsr is not always defined as context-synchronizing */
+ gen_stop_exception(ctx);
+ }
+#endif
+}
+
+/* mtspr */
+static void gen_mtspr(DisasContext *ctx)
+{
+ void (*write_cb)(void *opaque, int sprn, int gprn);
+ uint32_t sprn = SPR(ctx->opcode);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->mem_idx == 2)
+ write_cb = ctx->spr_cb[sprn].hea_write;
+ else if (ctx->mem_idx)
+ write_cb = ctx->spr_cb[sprn].oea_write;
+ else
+#endif
+ write_cb = ctx->spr_cb[sprn].uea_write;
+ if (likely(write_cb != NULL)) {
+ if (likely(write_cb != SPR_NOACCESS)) {
+ (*write_cb)(ctx, sprn, rS(ctx->opcode));
+ } else {
+ /* Privilege exception */
+ qemu_log("Trying to write privileged spr %d %03x at "
+ TARGET_FMT_lx "\n", sprn, sprn, ctx->nip);
+ printf("Trying to write privileged spr %d %03x at " TARGET_FMT_lx
+ "\n", sprn, sprn, ctx->nip);
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ }
+ } else {
+ /* Not defined */
+ qemu_log("Trying to write invalid spr %d %03x at "
+ TARGET_FMT_lx "\n", sprn, sprn, ctx->nip);
+ printf("Trying to write invalid spr %d %03x at " TARGET_FMT_lx "\n",
+ sprn, sprn, ctx->nip);
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_SPR);
+ }
+}
+
+/*** Cache management ***/
+
+/* dcbf */
+static void gen_dcbf(DisasContext *ctx)
+{
+ /* XXX: specification says this is treated as a load by the MMU */
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_qemu_ld8u(ctx, t0, t0);
+ tcg_temp_free(t0);
+}
+
+/* dcbi (Supervisor only) */
+static void gen_dcbi(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv EA, val;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ EA = tcg_temp_new();
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ gen_addr_reg_index(ctx, EA);
+ val = tcg_temp_new();
+ /* XXX: specification says this should be treated as a store by the MMU */
+ gen_qemu_ld8u(ctx, val, EA);
+ gen_qemu_st8(ctx, val, EA);
+ tcg_temp_free(val);
+ tcg_temp_free(EA);
+#endif
+}
+
+/* dcdst */
+static void gen_dcbst(DisasContext *ctx)
+{
+ /* XXX: specification say this is treated as a load by the MMU */
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_qemu_ld8u(ctx, t0, t0);
+ tcg_temp_free(t0);
+}
+
+/* dcbt */
+static void gen_dcbt(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+ /* XXX: specification say this is treated as a load by the MMU
+ * but does not generate any exception
+ */
+}
+
+/* dcbtst */
+static void gen_dcbtst(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+ /* XXX: specification say this is treated as a load by the MMU
+ * but does not generate any exception
+ */
+}
+
+/* dcbz */
+static void gen_dcbz(DisasContext *ctx)
+{
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_helper_dcbz(t0);
+ tcg_temp_free(t0);
+}
+
+static void gen_dcbz_970(DisasContext *ctx)
+{
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ if (ctx->opcode & 0x00200000)
+ gen_helper_dcbz(t0);
+ else
+ gen_helper_dcbz_970(t0);
+ tcg_temp_free(t0);
+}
+
+/* dst / dstt */
+static void gen_dst(DisasContext *ctx)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_LSWX);
+ } else {
+ /* interpreted as no-op */
+ }
+}
+
+/* dstst /dststt */
+static void gen_dstst(DisasContext *ctx)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_LSWX);
+ } else {
+ /* interpreted as no-op */
+ }
+
+}
+
+/* dss / dssall */
+static void gen_dss(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+}
+
+/* icbi */
+static void gen_icbi(DisasContext *ctx)
+{
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_helper_icbi(t0);
+ tcg_temp_free(t0);
+}
+
+/* Optional: */
+/* dcba */
+static void gen_dcba(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+ /* XXX: specification say this is treated as a store by the MMU
+ * but does not generate any exception
+ */
+}
+
+/*** Segment register manipulation ***/
+/* Supervisor only: */
+
+/* mfsr */
+static void gen_mfsr(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_const_tl(SR(ctx->opcode));
+ gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* mfsrin */
+static void gen_mfsrin(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
+ tcg_gen_andi_tl(t0, t0, 0xF);
+ gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* mtsr */
+static void gen_mtsr(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_const_tl(SR(ctx->opcode));
+ gen_helper_store_sr(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* mtsrin */
+static void gen_mtsrin(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
+ tcg_gen_andi_tl(t0, t0, 0xF);
+ gen_helper_store_sr(t0, cpu_gpr[rD(ctx->opcode)]);
+ tcg_temp_free(t0);
+#endif
+}
+
+#if defined(TARGET_PPC64)
+/* Specific implementation for PowerPC 64 "bridge" emulation using SLB */
+
+/* mfsr */
+static void gen_mfsr_64b(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_const_tl(SR(ctx->opcode));
+ gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* mfsrin */
+static void gen_mfsrin_64b(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
+ tcg_gen_andi_tl(t0, t0, 0xF);
+ gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* mtsr */
+static void gen_mtsr_64b(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_const_tl(SR(ctx->opcode));
+ gen_helper_store_sr(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* mtsrin */
+static void gen_mtsrin_64b(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 28);
+ tcg_gen_andi_tl(t0, t0, 0xF);
+ gen_helper_store_sr(t0, cpu_gpr[rS(ctx->opcode)]);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* slbmte */
+static void gen_slbmte(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ gen_helper_store_slb(cpu_gpr[rB(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+#endif
+}
+
+#endif /* defined(TARGET_PPC64) */
+
+/*** Lookaside buffer management ***/
+/* Optional & mem_idx only: */
+
+/* tlbia */
+static void gen_tlbia(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_tlbia();
+#endif
+}
+
+/* tlbiel */
+static void gen_tlbiel(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_tlbie(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbie */
+static void gen_tlbie(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ext32u_tl(t0, cpu_gpr[rB(ctx->opcode)]);
+ gen_helper_tlbie(t0);
+ tcg_temp_free(t0);
+ } else
+#endif
+ gen_helper_tlbie(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbsync */
+static void gen_tlbsync(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* This has no effect: it should ensure that all previous
+ * tlbie have completed
+ */
+ gen_stop_exception(ctx);
+#endif
+}
+
+#if defined(TARGET_PPC64)
+/* slbia */
+static void gen_slbia(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_slbia();
+#endif
+}
+
+/* slbie */
+static void gen_slbie(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_slbie(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+#endif
+
+/*** External control ***/
+/* Optional: */
+
+/* eciwx */
+static void gen_eciwx(DisasContext *ctx)
+{
+ TCGv t0;
+ /* Should check EAR[E] ! */
+ gen_set_access_type(ctx, ACCESS_EXT);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_check_align(ctx, t0, 0x03);
+ gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+}
+
+/* ecowx */
+static void gen_ecowx(DisasContext *ctx)
+{
+ TCGv t0;
+ /* Should check EAR[E] ! */
+ gen_set_access_type(ctx, ACCESS_EXT);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_check_align(ctx, t0, 0x03);
+ gen_qemu_st32(ctx, cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+}
+
+/* PowerPC 601 specific instructions */
+
+/* abs - abs. */
+static void gen_abs(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l1);
+ tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ gen_set_label(l2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* abso - abso. */
+static void gen_abso(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ int l3 = gen_new_label();
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rA(ctx->opcode)], 0, l2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[rA(ctx->opcode)], 0x80000000, l1);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l3);
+ gen_set_label(l2);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ gen_set_label(l3);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* clcs */
+static void gen_clcs(DisasContext *ctx)
+{
+ TCGv_i32 t0 = tcg_const_i32(rA(ctx->opcode));
+ gen_helper_clcs(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free_i32(t0);
+ /* Rc=1 sets CR0 to an undefined state */
+}
+
+/* div - div. */
+static void gen_div(DisasContext *ctx)
+{
+ gen_helper_div(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* divo - divo. */
+static void gen_divo(DisasContext *ctx)
+{
+ gen_helper_divo(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* divs - divs. */
+static void gen_divs(DisasContext *ctx)
+{
+ gen_helper_divs(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* divso - divso. */
+static void gen_divso(DisasContext *ctx)
+{
+ gen_helper_divso(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* doz - doz. */
+static void gen_doz(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], l1);
+ tcg_gen_sub_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0);
+ gen_set_label(l2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* dozo - dozo. */
+static void gen_dozo(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], l1);
+ tcg_gen_sub_tl(t0, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_xor_tl(t1, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_xor_tl(t2, cpu_gpr[rA(ctx->opcode)], t0);
+ tcg_gen_andc_tl(t1, t1, t2);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0);
+ gen_set_label(l2);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* dozi */
+static void gen_dozi(DisasContext *ctx)
+{
+ target_long simm = SIMM(ctx->opcode);
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_LT, cpu_gpr[rA(ctx->opcode)], simm, l1);
+ tcg_gen_subfi_tl(cpu_gpr[rD(ctx->opcode)], simm, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0);
+ gen_set_label(l2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* lscbx - lscbx. */
+static void gen_lscbx(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv_i32 t1 = tcg_const_i32(rD(ctx->opcode));
+ TCGv_i32 t2 = tcg_const_i32(rA(ctx->opcode));
+ TCGv_i32 t3 = tcg_const_i32(rB(ctx->opcode));
+
+ gen_addr_reg_index(ctx, t0);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_helper_lscbx(t0, t0, t1, t2, t3);
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t3);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~0x7F);
+ tcg_gen_or_tl(cpu_xer, cpu_xer, t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, t0);
+ tcg_temp_free(t0);
+}
+
+/* maskg - maskg. */
+static void gen_maskg(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ TCGv t3 = tcg_temp_new();
+ tcg_gen_movi_tl(t3, 0xFFFFFFFF);
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_andi_tl(t1, cpu_gpr[rS(ctx->opcode)], 0x1F);
+ tcg_gen_addi_tl(t2, t0, 1);
+ tcg_gen_shr_tl(t2, t3, t2);
+ tcg_gen_shr_tl(t3, t3, t1);
+ tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], t2, t3);
+ tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1);
+ tcg_gen_neg_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ tcg_temp_free(t3);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* maskir - maskir. */
+static void gen_maskir(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_and_tl(t0, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_andc_tl(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* mul - mul. */
+static void gen_mul(DisasContext *ctx)
+{
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+ TCGv t2 = tcg_temp_new();
+ tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_trunc_i64_tl(t2, t0);
+ gen_store_spr(SPR_MQ, t2);
+ tcg_gen_shri_i64(t1, t0, 32);
+ tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* mulo - mulo. */
+static void gen_mulo(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+ TCGv t2 = tcg_temp_new();
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mul_i64(t0, t0, t1);
+ tcg_gen_trunc_i64_tl(t2, t0);
+ gen_store_spr(SPR_MQ, t2);
+ tcg_gen_shri_i64(t1, t0, 32);
+ tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1);
+ tcg_gen_ext32s_i64(t1, t0);
+ tcg_gen_brcond_i64(TCG_COND_EQ, t0, t1, l1);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ gen_set_label(l1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* nabs - nabs. */
+static void gen_nabs(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_GT, cpu_gpr[rA(ctx->opcode)], 0, l1);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ gen_set_label(l2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* nabso - nabso. */
+static void gen_nabso(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_GT, cpu_gpr[rA(ctx->opcode)], 0, l1);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ gen_set_label(l2);
+ /* nabs never overflows */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]);
+}
+
+/* rlmi - rlmi. */
+static void gen_rlmi(DisasContext *ctx)
+{
+ uint32_t mb = MB(ctx->opcode);
+ uint32_t me = ME(ctx->opcode);
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ tcg_gen_andi_tl(t0, t0, MASK(mb, me));
+ tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~MASK(mb, me));
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* rrib - rrib. */
+static void gen_rrib(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_movi_tl(t1, 0x80000000);
+ tcg_gen_shr_tl(t1, t1, t0);
+ tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ tcg_gen_and_tl(t0, t0, t1);
+ tcg_gen_andc_tl(t1, cpu_gpr[rA(ctx->opcode)], t1);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sle - sle. */
+static void gen_sle(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_subfi_tl(t1, 32, t1);
+ tcg_gen_shr_tl(t1, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_or_tl(t1, t0, t1);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ gen_store_spr(SPR_MQ, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sleq - sleq. */
+static void gen_sleq(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_movi_tl(t2, 0xFFFFFFFF);
+ tcg_gen_shl_tl(t2, t2, t0);
+ tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ gen_load_spr(t1, SPR_MQ);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_and_tl(t0, t0, t2);
+ tcg_gen_andc_tl(t1, t1, t2);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sliq - sliq. */
+static void gen_sliq(DisasContext *ctx)
+{
+ int sh = SH(ctx->opcode);
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_shli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ tcg_gen_shri_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh);
+ tcg_gen_or_tl(t1, t0, t1);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ gen_store_spr(SPR_MQ, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* slliq - slliq. */
+static void gen_slliq(DisasContext *ctx)
+{
+ int sh = SH(ctx->opcode);
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_rotli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ gen_load_spr(t1, SPR_MQ);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_andi_tl(t0, t0, (0xFFFFFFFFU << sh));
+ tcg_gen_andi_tl(t1, t1, ~(0xFFFFFFFFU << sh));
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sllq - sllq. */
+static void gen_sllq(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_local_new();
+ TCGv t2 = tcg_temp_local_new();
+ tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_movi_tl(t1, 0xFFFFFFFF);
+ tcg_gen_shl_tl(t1, t1, t2);
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
+ gen_load_spr(t0, SPR_MQ);
+ tcg_gen_and_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t2);
+ gen_load_spr(t2, SPR_MQ);
+ tcg_gen_andc_tl(t1, t2, t1);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ gen_set_label(l2);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* slq - slq. */
+static void gen_slq(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_subfi_tl(t1, 32, t1);
+ tcg_gen_shr_tl(t1, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_or_tl(t1, t0, t1);
+ gen_store_spr(SPR_MQ, t1);
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x20);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0);
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sraiq - sraiq. */
+static void gen_sraiq(DisasContext *ctx)
+{
+ int sh = SH(ctx->opcode);
+ int l1 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh);
+ tcg_gen_or_tl(t0, t0, t1);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1);
+ tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rS(ctx->opcode)], 0, l1);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_CA));
+ gen_set_label(l1);
+ tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sraq - sraq. */
+static void gen_sraq(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_local_new();
+ TCGv t2 = tcg_temp_local_new();
+ tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t2);
+ tcg_gen_sar_tl(t1, cpu_gpr[rS(ctx->opcode)], t2);
+ tcg_gen_subfi_tl(t2, 32, t2);
+ tcg_gen_shl_tl(t2, cpu_gpr[rS(ctx->opcode)], t2);
+ tcg_gen_or_tl(t0, t0, t2);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l1);
+ tcg_gen_mov_tl(t2, cpu_gpr[rS(ctx->opcode)]);
+ tcg_gen_sari_tl(t1, cpu_gpr[rS(ctx->opcode)], 31);
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t1);
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_CA));
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l2);
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_CA));
+ gen_set_label(l2);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sre - sre. */
+static void gen_sre(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_subfi_tl(t1, 32, t1);
+ tcg_gen_shl_tl(t1, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_or_tl(t1, t0, t1);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ gen_store_spr(SPR_MQ, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srea - srea. */
+static void gen_srea(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_rotr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_sar_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sreq */
+static void gen_sreq(DisasContext *ctx)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_movi_tl(t1, 0xFFFFFFFF);
+ tcg_gen_shr_tl(t1, t1, t0);
+ tcg_gen_rotr_tl(t0, cpu_gpr[rS(ctx->opcode)], t0);
+ gen_load_spr(t2, SPR_MQ);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_and_tl(t0, t0, t1);
+ tcg_gen_andc_tl(t2, t2, t1);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t2);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* sriq */
+static void gen_sriq(DisasContext *ctx)
+{
+ int sh = SH(ctx->opcode);
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh);
+ tcg_gen_or_tl(t1, t0, t1);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ gen_store_spr(SPR_MQ, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srliq */
+static void gen_srliq(DisasContext *ctx)
+{
+ int sh = SH(ctx->opcode);
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_rotri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh);
+ gen_load_spr(t1, SPR_MQ);
+ gen_store_spr(SPR_MQ, t0);
+ tcg_gen_andi_tl(t0, t0, (0xFFFFFFFFU >> sh));
+ tcg_gen_andi_tl(t1, t1, ~(0xFFFFFFFFU >> sh));
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srlq */
+static void gen_srlq(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ TCGv t0 = tcg_temp_local_new();
+ TCGv t1 = tcg_temp_local_new();
+ TCGv t2 = tcg_temp_local_new();
+ tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_movi_tl(t1, 0xFFFFFFFF);
+ tcg_gen_shr_tl(t2, t1, t2);
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
+ gen_load_spr(t0, SPR_MQ);
+ tcg_gen_and_tl(cpu_gpr[rA(ctx->opcode)], t0, t2);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t2);
+ tcg_gen_and_tl(t0, t0, t2);
+ gen_load_spr(t1, SPR_MQ);
+ tcg_gen_andc_tl(t1, t1, t2);
+ tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1);
+ gen_set_label(l2);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* srq */
+static void gen_srq(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F);
+ tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_subfi_tl(t1, 32, t1);
+ tcg_gen_shl_tl(t1, cpu_gpr[rS(ctx->opcode)], t1);
+ tcg_gen_or_tl(t1, t0, t1);
+ gen_store_spr(SPR_MQ, t1);
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x20);
+ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
+ tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0);
+ gen_set_label(l1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc(ctx->opcode) != 0))
+ gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]);
+}
+
+/* PowerPC 602 specific instructions */
+
+/* dsa */
+static void gen_dsa(DisasContext *ctx)
+{
+ /* XXX: TODO */
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+}
+
+/* esa */
+static void gen_esa(DisasContext *ctx)
+{
+ /* XXX: TODO */
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+}
+
+/* mfrom */
+static void gen_mfrom(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+#endif
+}
+
+/* 602 - 603 - G2 TLB management */
+
+/* tlbld */
+static void gen_tlbld_6xx(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_6xx_tlbd(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbli */
+static void gen_tlbli_6xx(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_6xx_tlbi(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* 74xx TLB management */
+
+/* tlbld */
+static void gen_tlbld_74xx(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_74xx_tlbd(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* tlbli */
+static void gen_tlbli_74xx(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_74xx_tlbi(cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+
+/* POWER instructions not in PowerPC 601 */
+
+/* clf */
+static void gen_clf(DisasContext *ctx)
+{
+ /* Cache line flush: implemented as no-op */
+}
+
+/* cli */
+static void gen_cli(DisasContext *ctx)
+{
+ /* Cache line invalidate: privileged and treated as no-op */
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+#endif
+}
+
+/* dclst */
+static void gen_dclst(DisasContext *ctx)
+{
+ /* Data cache line store: treated as no-op */
+}
+
+static void gen_mfsri(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ int ra = rA(ctx->opcode);
+ int rd = rD(ctx->opcode);
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ tcg_gen_shri_tl(t0, t0, 28);
+ tcg_gen_andi_tl(t0, t0, 0xF);
+ gen_helper_load_sr(cpu_gpr[rd], t0);
+ tcg_temp_free(t0);
+ if (ra != 0 && ra != rd)
+ tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]);
+#endif
+}
+
+static void gen_rac(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_helper_rac(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#endif
+}
+
+static void gen_rfsvc(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_helper_rfsvc();
+ gen_sync_exception(ctx);
+#endif
+}
+
+/* svc is not implemented for now */
+
+/* POWER2 specific instructions */
+/* Quad manipulation (load/store two floats at a time) */
+
+/* lfq */
+static void gen_lfq(DisasContext *ctx)
+{
+ int rd = rD(ctx->opcode);
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ gen_addr_imm_index(ctx, t0, 0);
+ gen_qemu_ld64(ctx, cpu_fpr[rd], t0);
+ gen_addr_add(ctx, t0, t0, 8);
+ gen_qemu_ld64(ctx, cpu_fpr[(rd + 1) % 32], t0);
+ tcg_temp_free(t0);
+}
+
+/* lfqu */
+static void gen_lfqu(DisasContext *ctx)
+{
+ int ra = rA(ctx->opcode);
+ int rd = rD(ctx->opcode);
+ TCGv t0, t1;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ t1 = tcg_temp_new();
+ gen_addr_imm_index(ctx, t0, 0);
+ gen_qemu_ld64(ctx, cpu_fpr[rd], t0);
+ gen_addr_add(ctx, t1, t0, 8);
+ gen_qemu_ld64(ctx, cpu_fpr[(rd + 1) % 32], t1);
+ if (ra != 0)
+ tcg_gen_mov_tl(cpu_gpr[ra], t0);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* lfqux */
+static void gen_lfqux(DisasContext *ctx)
+{
+ int ra = rA(ctx->opcode);
+ int rd = rD(ctx->opcode);
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ TCGv t0, t1;
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_qemu_ld64(ctx, cpu_fpr[rd], t0);
+ t1 = tcg_temp_new();
+ gen_addr_add(ctx, t1, t0, 8);
+ gen_qemu_ld64(ctx, cpu_fpr[(rd + 1) % 32], t1);
+ tcg_temp_free(t1);
+ if (ra != 0)
+ tcg_gen_mov_tl(cpu_gpr[ra], t0);
+ tcg_temp_free(t0);
+}
+
+/* lfqx */
+static void gen_lfqx(DisasContext *ctx)
+{
+ int rd = rD(ctx->opcode);
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_qemu_ld64(ctx, cpu_fpr[rd], t0);
+ gen_addr_add(ctx, t0, t0, 8);
+ gen_qemu_ld64(ctx, cpu_fpr[(rd + 1) % 32], t0);
+ tcg_temp_free(t0);
+}
+
+/* stfq */
+static void gen_stfq(DisasContext *ctx)
+{
+ int rd = rD(ctx->opcode);
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ gen_addr_imm_index(ctx, t0, 0);
+ gen_qemu_st64(ctx, cpu_fpr[rd], t0);
+ gen_addr_add(ctx, t0, t0, 8);
+ gen_qemu_st64(ctx, cpu_fpr[(rd + 1) % 32], t0);
+ tcg_temp_free(t0);
+}
+
+/* stfqu */
+static void gen_stfqu(DisasContext *ctx)
+{
+ int ra = rA(ctx->opcode);
+ int rd = rD(ctx->opcode);
+ TCGv t0, t1;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ gen_addr_imm_index(ctx, t0, 0);
+ gen_qemu_st64(ctx, cpu_fpr[rd], t0);
+ t1 = tcg_temp_new();
+ gen_addr_add(ctx, t1, t0, 8);
+ gen_qemu_st64(ctx, cpu_fpr[(rd + 1) % 32], t1);
+ tcg_temp_free(t1);
+ if (ra != 0)
+ tcg_gen_mov_tl(cpu_gpr[ra], t0);
+ tcg_temp_free(t0);
+}
+
+/* stfqux */
+static void gen_stfqux(DisasContext *ctx)
+{
+ int ra = rA(ctx->opcode);
+ int rd = rD(ctx->opcode);
+ TCGv t0, t1;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_qemu_st64(ctx, cpu_fpr[rd], t0);
+ t1 = tcg_temp_new();
+ gen_addr_add(ctx, t1, t0, 8);
+ gen_qemu_st64(ctx, cpu_fpr[(rd + 1) % 32], t1);
+ tcg_temp_free(t1);
+ if (ra != 0)
+ tcg_gen_mov_tl(cpu_gpr[ra], t0);
+ tcg_temp_free(t0);
+}
+
+/* stfqx */
+static void gen_stfqx(DisasContext *ctx)
+{
+ int rd = rD(ctx->opcode);
+ TCGv t0;
+ gen_set_access_type(ctx, ACCESS_FLOAT);
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_qemu_st64(ctx, cpu_fpr[rd], t0);
+ gen_addr_add(ctx, t0, t0, 8);
+ gen_qemu_st64(ctx, cpu_fpr[(rd + 1) % 32], t0);
+ tcg_temp_free(t0);
+}
+
+/* BookE specific instructions */
+
+/* XXX: not implemented on 440 ? */
+static void gen_mfapidi(DisasContext *ctx)
+{
+ /* XXX: TODO */
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+}
+
+/* XXX: not implemented on 440 ? */
+static void gen_tlbiva(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_helper_tlbie(cpu_gpr[rB(ctx->opcode)]);
+ tcg_temp_free(t0);
+#endif
+}
+
+/* All 405 MAC instructions are translated here */
+static inline void gen_405_mulladd_insn(DisasContext *ctx, int opc2, int opc3,
+ int ra, int rb, int rt, int Rc)
+{
+ TCGv t0, t1;
+
+ t0 = tcg_temp_local_new();
+ t1 = tcg_temp_local_new();
+
+ switch (opc3 & 0x0D) {
+ case 0x05:
+ /* macchw - macchw. - macchwo - macchwo. */
+ /* macchws - macchws. - macchwso - macchwso. */
+ /* nmacchw - nmacchw. - nmacchwo - nmacchwo. */
+ /* nmacchws - nmacchws. - nmacchwso - nmacchwso. */
+ /* mulchw - mulchw. */
+ tcg_gen_ext16s_tl(t0, cpu_gpr[ra]);
+ tcg_gen_sari_tl(t1, cpu_gpr[rb], 16);
+ tcg_gen_ext16s_tl(t1, t1);
+ break;
+ case 0x04:
+ /* macchwu - macchwu. - macchwuo - macchwuo. */
+ /* macchwsu - macchwsu. - macchwsuo - macchwsuo. */
+ /* mulchwu - mulchwu. */
+ tcg_gen_ext16u_tl(t0, cpu_gpr[ra]);
+ tcg_gen_shri_tl(t1, cpu_gpr[rb], 16);
+ tcg_gen_ext16u_tl(t1, t1);
+ break;
+ case 0x01:
+ /* machhw - machhw. - machhwo - machhwo. */
+ /* machhws - machhws. - machhwso - machhwso. */
+ /* nmachhw - nmachhw. - nmachhwo - nmachhwo. */
+ /* nmachhws - nmachhws. - nmachhwso - nmachhwso. */
+ /* mulhhw - mulhhw. */
+ tcg_gen_sari_tl(t0, cpu_gpr[ra], 16);
+ tcg_gen_ext16s_tl(t0, t0);
+ tcg_gen_sari_tl(t1, cpu_gpr[rb], 16);
+ tcg_gen_ext16s_tl(t1, t1);
+ break;
+ case 0x00:
+ /* machhwu - machhwu. - machhwuo - machhwuo. */
+ /* machhwsu - machhwsu. - machhwsuo - machhwsuo. */
+ /* mulhhwu - mulhhwu. */
+ tcg_gen_shri_tl(t0, cpu_gpr[ra], 16);
+ tcg_gen_ext16u_tl(t0, t0);
+ tcg_gen_shri_tl(t1, cpu_gpr[rb], 16);
+ tcg_gen_ext16u_tl(t1, t1);
+ break;
+ case 0x0D:
+ /* maclhw - maclhw. - maclhwo - maclhwo. */
+ /* maclhws - maclhws. - maclhwso - maclhwso. */
+ /* nmaclhw - nmaclhw. - nmaclhwo - nmaclhwo. */
+ /* nmaclhws - nmaclhws. - nmaclhwso - nmaclhwso. */
+ /* mullhw - mullhw. */
+ tcg_gen_ext16s_tl(t0, cpu_gpr[ra]);
+ tcg_gen_ext16s_tl(t1, cpu_gpr[rb]);
+ break;
+ case 0x0C:
+ /* maclhwu - maclhwu. - maclhwuo - maclhwuo. */
+ /* maclhwsu - maclhwsu. - maclhwsuo - maclhwsuo. */
+ /* mullhwu - mullhwu. */
+ tcg_gen_ext16u_tl(t0, cpu_gpr[ra]);
+ tcg_gen_ext16u_tl(t1, cpu_gpr[rb]);
+ break;
+ }
+ if (opc2 & 0x04) {
+ /* (n)multiply-and-accumulate (0x0C / 0x0E) */
+ tcg_gen_mul_tl(t1, t0, t1);
+ if (opc2 & 0x02) {
+ /* nmultiply-and-accumulate (0x0E) */
+ tcg_gen_sub_tl(t0, cpu_gpr[rt], t1);
+ } else {
+ /* multiply-and-accumulate (0x0C) */
+ tcg_gen_add_tl(t0, cpu_gpr[rt], t1);
+ }
+
+ if (opc3 & 0x12) {
+ /* Check overflow and/or saturate */
+ int l1 = gen_new_label();
+
+ if (opc3 & 0x10) {
+ /* Start with XER OV disabled, the most likely case */
+ tcg_gen_andi_tl(cpu_xer, cpu_xer, ~(1 << XER_OV));
+ }
+ if (opc3 & 0x01) {
+ /* Signed */
+ tcg_gen_xor_tl(t1, cpu_gpr[rt], t1);
+ tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
+ tcg_gen_xor_tl(t1, cpu_gpr[rt], t0);
+ tcg_gen_brcondi_tl(TCG_COND_LT, t1, 0, l1);
+ if (opc3 & 0x02) {
+ /* Saturate */
+ tcg_gen_sari_tl(t0, cpu_gpr[rt], 31);
+ tcg_gen_xori_tl(t0, t0, 0x7fffffff);
+ }
+ } else {
+ /* Unsigned */
+ tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1);
+ if (opc3 & 0x02) {
+ /* Saturate */
+ tcg_gen_movi_tl(t0, UINT32_MAX);
+ }
+ }
+ if (opc3 & 0x10) {
+ /* Check overflow */
+ tcg_gen_ori_tl(cpu_xer, cpu_xer, (1 << XER_OV) | (1 << XER_SO));
+ }
+ gen_set_label(l1);
+ tcg_gen_mov_tl(cpu_gpr[rt], t0);
+ }
+ } else {
+ tcg_gen_mul_tl(cpu_gpr[rt], t0, t1);
+ }
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ if (unlikely(Rc) != 0) {
+ /* Update Rc0 */
+ gen_set_Rc0(ctx, cpu_gpr[rt]);
+ }
+}
+
+#define GEN_MAC_HANDLER(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ gen_405_mulladd_insn(ctx, opc2, opc3, rA(ctx->opcode), rB(ctx->opcode), \
+ rD(ctx->opcode), Rc(ctx->opcode)); \
+}
+
+/* macchw - macchw. */
+GEN_MAC_HANDLER(macchw, 0x0C, 0x05);
+/* macchwo - macchwo. */
+GEN_MAC_HANDLER(macchwo, 0x0C, 0x15);
+/* macchws - macchws. */
+GEN_MAC_HANDLER(macchws, 0x0C, 0x07);
+/* macchwso - macchwso. */
+GEN_MAC_HANDLER(macchwso, 0x0C, 0x17);
+/* macchwsu - macchwsu. */
+GEN_MAC_HANDLER(macchwsu, 0x0C, 0x06);
+/* macchwsuo - macchwsuo. */
+GEN_MAC_HANDLER(macchwsuo, 0x0C, 0x16);
+/* macchwu - macchwu. */
+GEN_MAC_HANDLER(macchwu, 0x0C, 0x04);
+/* macchwuo - macchwuo. */
+GEN_MAC_HANDLER(macchwuo, 0x0C, 0x14);
+/* machhw - machhw. */
+GEN_MAC_HANDLER(machhw, 0x0C, 0x01);
+/* machhwo - machhwo. */
+GEN_MAC_HANDLER(machhwo, 0x0C, 0x11);
+/* machhws - machhws. */
+GEN_MAC_HANDLER(machhws, 0x0C, 0x03);
+/* machhwso - machhwso. */
+GEN_MAC_HANDLER(machhwso, 0x0C, 0x13);
+/* machhwsu - machhwsu. */
+GEN_MAC_HANDLER(machhwsu, 0x0C, 0x02);
+/* machhwsuo - machhwsuo. */
+GEN_MAC_HANDLER(machhwsuo, 0x0C, 0x12);
+/* machhwu - machhwu. */
+GEN_MAC_HANDLER(machhwu, 0x0C, 0x00);
+/* machhwuo - machhwuo. */
+GEN_MAC_HANDLER(machhwuo, 0x0C, 0x10);
+/* maclhw - maclhw. */
+GEN_MAC_HANDLER(maclhw, 0x0C, 0x0D);
+/* maclhwo - maclhwo. */
+GEN_MAC_HANDLER(maclhwo, 0x0C, 0x1D);
+/* maclhws - maclhws. */
+GEN_MAC_HANDLER(maclhws, 0x0C, 0x0F);
+/* maclhwso - maclhwso. */
+GEN_MAC_HANDLER(maclhwso, 0x0C, 0x1F);
+/* maclhwu - maclhwu. */
+GEN_MAC_HANDLER(maclhwu, 0x0C, 0x0C);
+/* maclhwuo - maclhwuo. */
+GEN_MAC_HANDLER(maclhwuo, 0x0C, 0x1C);
+/* maclhwsu - maclhwsu. */
+GEN_MAC_HANDLER(maclhwsu, 0x0C, 0x0E);
+/* maclhwsuo - maclhwsuo. */
+GEN_MAC_HANDLER(maclhwsuo, 0x0C, 0x1E);
+/* nmacchw - nmacchw. */
+GEN_MAC_HANDLER(nmacchw, 0x0E, 0x05);
+/* nmacchwo - nmacchwo. */
+GEN_MAC_HANDLER(nmacchwo, 0x0E, 0x15);
+/* nmacchws - nmacchws. */
+GEN_MAC_HANDLER(nmacchws, 0x0E, 0x07);
+/* nmacchwso - nmacchwso. */
+GEN_MAC_HANDLER(nmacchwso, 0x0E, 0x17);
+/* nmachhw - nmachhw. */
+GEN_MAC_HANDLER(nmachhw, 0x0E, 0x01);
+/* nmachhwo - nmachhwo. */
+GEN_MAC_HANDLER(nmachhwo, 0x0E, 0x11);
+/* nmachhws - nmachhws. */
+GEN_MAC_HANDLER(nmachhws, 0x0E, 0x03);
+/* nmachhwso - nmachhwso. */
+GEN_MAC_HANDLER(nmachhwso, 0x0E, 0x13);
+/* nmaclhw - nmaclhw. */
+GEN_MAC_HANDLER(nmaclhw, 0x0E, 0x0D);
+/* nmaclhwo - nmaclhwo. */
+GEN_MAC_HANDLER(nmaclhwo, 0x0E, 0x1D);
+/* nmaclhws - nmaclhws. */
+GEN_MAC_HANDLER(nmaclhws, 0x0E, 0x0F);
+/* nmaclhwso - nmaclhwso. */
+GEN_MAC_HANDLER(nmaclhwso, 0x0E, 0x1F);
+
+/* mulchw - mulchw. */
+GEN_MAC_HANDLER(mulchw, 0x08, 0x05);
+/* mulchwu - mulchwu. */
+GEN_MAC_HANDLER(mulchwu, 0x08, 0x04);
+/* mulhhw - mulhhw. */
+GEN_MAC_HANDLER(mulhhw, 0x08, 0x01);
+/* mulhhwu - mulhhwu. */
+GEN_MAC_HANDLER(mulhhwu, 0x08, 0x00);
+/* mullhw - mullhw. */
+GEN_MAC_HANDLER(mullhw, 0x08, 0x0D);
+/* mullhwu - mullhwu. */
+GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C);
+
+/* mfdcr */
+static void gen_mfdcr(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv dcrn;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ dcrn = tcg_const_tl(SPR(ctx->opcode));
+ gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], dcrn);
+ tcg_temp_free(dcrn);
+#endif
+}
+
+/* mtdcr */
+static void gen_mtdcr(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ TCGv dcrn;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ dcrn = tcg_const_tl(SPR(ctx->opcode));
+ gen_helper_store_dcr(dcrn, cpu_gpr[rS(ctx->opcode)]);
+ tcg_temp_free(dcrn);
+#endif
+}
+
+/* mfdcrx */
+/* XXX: not implemented on 440 ? */
+static void gen_mfdcrx(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ /* Note: Rc update flag set leads to undefined state of Rc0 */
+#endif
+}
+
+/* mtdcrx */
+/* XXX: not implemented on 440 ? */
+static void gen_mtdcrx(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ return;
+ }
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_helper_store_dcr(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ /* Note: Rc update flag set leads to undefined state of Rc0 */
+#endif
+}
+
+/* mfdcrux (PPC 460) : user-mode access to DCR */
+static void gen_mfdcrux(DisasContext *ctx)
+{
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ /* Note: Rc update flag set leads to undefined state of Rc0 */
+}
+
+/* mtdcrux (PPC 460) : user-mode access to DCR */
+static void gen_mtdcrux(DisasContext *ctx)
+{
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_update_nip(ctx, ctx->nip - 4);
+ gen_helper_store_dcr(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ /* Note: Rc update flag set leads to undefined state of Rc0 */
+}
+
+/* dccci */
+static void gen_dccci(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* interpreted as no-op */
+#endif
+}
+
+/* dcread */
+static void gen_dcread(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv EA, val;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ gen_set_access_type(ctx, ACCESS_CACHE);
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ val = tcg_temp_new();
+ gen_qemu_ld32u(ctx, val, EA);
+ tcg_temp_free(val);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], EA);
+ tcg_temp_free(EA);
+#endif
+}
+
+/* icbt */
+static void gen_icbt_40x(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+ /* XXX: specification say this is treated as a load by the MMU
+ * but does not generate any exception
+ */
+}
+
+/* iccci */
+static void gen_iccci(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* interpreted as no-op */
+#endif
+}
+
+/* icread */
+static void gen_icread(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* interpreted as no-op */
+#endif
+}
+
+/* rfci (mem_idx only) */
+static void gen_rfci_40x(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* Restore CPU state */
+ gen_helper_40x_rfci();
+ gen_sync_exception(ctx);
+#endif
+}
+
+static void gen_rfci(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* Restore CPU state */
+ gen_helper_rfci();
+ gen_sync_exception(ctx);
+#endif
+}
+
+/* BookE specific */
+
+/* XXX: not implemented on 440 ? */
+static void gen_rfdi(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* Restore CPU state */
+ gen_helper_rfdi();
+ gen_sync_exception(ctx);
+#endif
+}
+
+/* XXX: not implemented on 440 ? */
+static void gen_rfmci(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ /* Restore CPU state */
+ gen_helper_rfmci();
+ gen_sync_exception(ctx);
+#endif
+}
+
+/* TLB management - PowerPC 405 implementation */
+
+/* tlbre */
+static void gen_tlbre_40x(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ switch (rB(ctx->opcode)) {
+ case 0:
+ gen_helper_4xx_tlbre_hi(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ break;
+ case 1:
+ gen_helper_4xx_tlbre_lo(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ break;
+ default:
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ break;
+ }
+#endif
+}
+
+/* tlbsx - tlbsx. */
+static void gen_tlbsx_40x(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_helper_4xx_tlbsx(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+ if (Rc(ctx->opcode)) {
+ int l1 = gen_new_label();
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer);
+ tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO);
+ tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1);
+ tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02);
+ gen_set_label(l1);
+ }
+#endif
+}
+
+/* tlbwe */
+static void gen_tlbwe_40x(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ switch (rB(ctx->opcode)) {
+ case 0:
+ gen_helper_4xx_tlbwe_hi(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ break;
+ case 1:
+ gen_helper_4xx_tlbwe_lo(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ break;
+ default:
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ break;
+ }
+#endif
+}
+
+/* TLB management - PowerPC 440 implementation */
+
+/* tlbre */
+static void gen_tlbre_440(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ switch (rB(ctx->opcode)) {
+ case 0:
+ case 1:
+ case 2:
+ {
+ TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode));
+ gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], t0, cpu_gpr[rA(ctx->opcode)]);
+ tcg_temp_free_i32(t0);
+ }
+ break;
+ default:
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ break;
+ }
+#endif
+}
+
+/* tlbsx - tlbsx. */
+static void gen_tlbsx_440(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ t0 = tcg_temp_new();
+ gen_addr_reg_index(ctx, t0);
+ gen_helper_440_tlbsx(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+ if (Rc(ctx->opcode)) {
+ int l1 = gen_new_label();
+ tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_xer);
+ tcg_gen_shri_i32(cpu_crf[0], cpu_crf[0], XER_SO);
+ tcg_gen_andi_i32(cpu_crf[0], cpu_crf[0], 1);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[rD(ctx->opcode)], -1, l1);
+ tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], 0x02);
+ gen_set_label(l1);
+ }
+#endif
+}
+
+/* tlbwe */
+static void gen_tlbwe_440(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ switch (rB(ctx->opcode)) {
+ case 0:
+ case 1:
+ case 2:
+ {
+ TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode));
+ gen_helper_440_tlbwe(t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]);
+ tcg_temp_free_i32(t0);
+ }
+ break;
+ default:
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+ break;
+ }
+#endif
+}
+
+/* wrtee */
+static void gen_wrtee(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ TCGv t0;
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rD(ctx->opcode)], (1 << MSR_EE));
+ tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE));
+ tcg_gen_or_tl(cpu_msr, cpu_msr, t0);
+ tcg_temp_free(t0);
+ /* Stop translation to have a chance to raise an exception
+ * if we just set msr_ee to 1
+ */
+ gen_stop_exception(ctx);
+#endif
+}
+
+/* wrteei */
+static void gen_wrteei(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+#else
+ if (unlikely(!ctx->mem_idx)) {
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC);
+ return;
+ }
+ if (ctx->opcode & 0x00008000) {
+ tcg_gen_ori_tl(cpu_msr, cpu_msr, (1 << MSR_EE));
+ /* Stop translation to have a chance to raise an exception */
+ gen_stop_exception(ctx);
+ } else {
+ tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE));
+ }
+#endif
+}
+
+/* PowerPC 440 specific instructions */
+
+/* dlmzb */
+static void gen_dlmzb(DisasContext *ctx)
+{
+ TCGv_i32 t0 = tcg_const_i32(Rc(ctx->opcode));
+ gen_helper_dlmzb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)],
+ cpu_gpr[rB(ctx->opcode)], t0);
+ tcg_temp_free_i32(t0);
+}
+
+/* mbar replaces eieio on 440 */
+static void gen_mbar(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+}
+
+/* msync replaces sync on 440 */
+static void gen_msync(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+}
+
+/* icbt */
+static void gen_icbt_440(DisasContext *ctx)
+{
+ /* interpreted as no-op */
+ /* XXX: specification say this is treated as a load by the MMU
+ * but does not generate any exception
+ */
+}
+
+/*** Altivec vector extension ***/
+/* Altivec registers moves */
+
+static inline TCGv_ptr gen_avr_ptr(int reg)
+{
+ TCGv_ptr r = tcg_temp_new_ptr();
+ tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, avr[reg]));
+ return r;
+}
+
+#define GEN_VR_LDX(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ tcg_gen_andi_tl(EA, EA, ~0xf); \
+ if (ctx->le_mode) { \
+ gen_qemu_ld64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \
+ tcg_gen_addi_tl(EA, EA, 8); \
+ gen_qemu_ld64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \
+ } else { \
+ gen_qemu_ld64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \
+ tcg_gen_addi_tl(EA, EA, 8); \
+ gen_qemu_ld64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \
+ } \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_VR_STX(name, opc2, opc3) \
+static void gen_st##name(DisasContext *ctx) \
+{ \
+ TCGv EA; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ tcg_gen_andi_tl(EA, EA, ~0xf); \
+ if (ctx->le_mode) { \
+ gen_qemu_st64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \
+ tcg_gen_addi_tl(EA, EA, 8); \
+ gen_qemu_st64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \
+ } else { \
+ gen_qemu_st64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \
+ tcg_gen_addi_tl(EA, EA, 8); \
+ gen_qemu_st64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \
+ } \
+ tcg_temp_free(EA); \
+}
+
+#define GEN_VR_LVE(name, opc2, opc3) \
+static void gen_lve##name(DisasContext *ctx) \
+ { \
+ TCGv EA; \
+ TCGv_ptr rs; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ rs = gen_avr_ptr(rS(ctx->opcode)); \
+ gen_helper_lve##name (rs, EA); \
+ tcg_temp_free(EA); \
+ tcg_temp_free_ptr(rs); \
+ }
+
+#define GEN_VR_STVE(name, opc2, opc3) \
+static void gen_stve##name(DisasContext *ctx) \
+ { \
+ TCGv EA; \
+ TCGv_ptr rs; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ EA = tcg_temp_new(); \
+ gen_addr_reg_index(ctx, EA); \
+ rs = gen_avr_ptr(rS(ctx->opcode)); \
+ gen_helper_stve##name (rs, EA); \
+ tcg_temp_free(EA); \
+ tcg_temp_free_ptr(rs); \
+ }
+
+GEN_VR_LDX(lvx, 0x07, 0x03);
+/* As we don't emulate the cache, lvxl is stricly equivalent to lvx */
+GEN_VR_LDX(lvxl, 0x07, 0x0B);
+
+GEN_VR_LVE(bx, 0x07, 0x00);
+GEN_VR_LVE(hx, 0x07, 0x01);
+GEN_VR_LVE(wx, 0x07, 0x02);
+
+GEN_VR_STX(svx, 0x07, 0x07);
+/* As we don't emulate the cache, stvxl is stricly equivalent to stvx */
+GEN_VR_STX(svxl, 0x07, 0x0F);
+
+GEN_VR_STVE(bx, 0x07, 0x04);
+GEN_VR_STVE(hx, 0x07, 0x05);
+GEN_VR_STVE(wx, 0x07, 0x06);
+
+static void gen_lvsl(DisasContext *ctx)
+{
+ TCGv_ptr rd;
+ TCGv EA;
+ if (unlikely(!ctx->altivec_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VPU);
+ return;
+ }
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ rd = gen_avr_ptr(rD(ctx->opcode));
+ gen_helper_lvsl(rd, EA);
+ tcg_temp_free(EA);
+ tcg_temp_free_ptr(rd);
+}
+
+static void gen_lvsr(DisasContext *ctx)
+{
+ TCGv_ptr rd;
+ TCGv EA;
+ if (unlikely(!ctx->altivec_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VPU);
+ return;
+ }
+ EA = tcg_temp_new();
+ gen_addr_reg_index(ctx, EA);
+ rd = gen_avr_ptr(rD(ctx->opcode));
+ gen_helper_lvsr(rd, EA);
+ tcg_temp_free(EA);
+ tcg_temp_free_ptr(rd);
+}
+
+static void gen_mfvscr(DisasContext *ctx)
+{
+ TCGv_i32 t;
+ if (unlikely(!ctx->altivec_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VPU);
+ return;
+ }
+ tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0);
+ t = tcg_temp_new_i32();
+ tcg_gen_ld_i32(t, cpu_env, offsetof(CPUState, vscr));
+ tcg_gen_extu_i32_i64(cpu_avrl[rD(ctx->opcode)], t);
+ tcg_temp_free_i32(t);
+}
+
+static void gen_mtvscr(DisasContext *ctx)
+{
+ TCGv_ptr p;
+ if (unlikely(!ctx->altivec_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VPU);
+ return;
+ }
+ p = gen_avr_ptr(rD(ctx->opcode));
+ gen_helper_mtvscr(p);
+ tcg_temp_free_ptr(p);
+}
+
+/* Logical operations */
+#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ tcg_op(cpu_avrh[rD(ctx->opcode)], cpu_avrh[rA(ctx->opcode)], cpu_avrh[rB(ctx->opcode)]); \
+ tcg_op(cpu_avrl[rD(ctx->opcode)], cpu_avrl[rA(ctx->opcode)], cpu_avrl[rB(ctx->opcode)]); \
+}
+
+GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16);
+GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17);
+GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18);
+GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19);
+GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20);
+
+#define GEN_VXFORM(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv_ptr ra, rb, rd; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ ra = gen_avr_ptr(rA(ctx->opcode)); \
+ rb = gen_avr_ptr(rB(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ gen_helper_##name (rd, ra, rb); \
+ tcg_temp_free_ptr(ra); \
+ tcg_temp_free_ptr(rb); \
+ tcg_temp_free_ptr(rd); \
+}
+
+GEN_VXFORM(vaddubm, 0, 0);
+GEN_VXFORM(vadduhm, 0, 1);
+GEN_VXFORM(vadduwm, 0, 2);
+GEN_VXFORM(vsububm, 0, 16);
+GEN_VXFORM(vsubuhm, 0, 17);
+GEN_VXFORM(vsubuwm, 0, 18);
+GEN_VXFORM(vmaxub, 1, 0);
+GEN_VXFORM(vmaxuh, 1, 1);
+GEN_VXFORM(vmaxuw, 1, 2);
+GEN_VXFORM(vmaxsb, 1, 4);
+GEN_VXFORM(vmaxsh, 1, 5);
+GEN_VXFORM(vmaxsw, 1, 6);
+GEN_VXFORM(vminub, 1, 8);
+GEN_VXFORM(vminuh, 1, 9);
+GEN_VXFORM(vminuw, 1, 10);
+GEN_VXFORM(vminsb, 1, 12);
+GEN_VXFORM(vminsh, 1, 13);
+GEN_VXFORM(vminsw, 1, 14);
+GEN_VXFORM(vavgub, 1, 16);
+GEN_VXFORM(vavguh, 1, 17);
+GEN_VXFORM(vavguw, 1, 18);
+GEN_VXFORM(vavgsb, 1, 20);
+GEN_VXFORM(vavgsh, 1, 21);
+GEN_VXFORM(vavgsw, 1, 22);
+GEN_VXFORM(vmrghb, 6, 0);
+GEN_VXFORM(vmrghh, 6, 1);
+GEN_VXFORM(vmrghw, 6, 2);
+GEN_VXFORM(vmrglb, 6, 4);
+GEN_VXFORM(vmrglh, 6, 5);
+GEN_VXFORM(vmrglw, 6, 6);
+GEN_VXFORM(vmuloub, 4, 0);
+GEN_VXFORM(vmulouh, 4, 1);
+GEN_VXFORM(vmulosb, 4, 4);
+GEN_VXFORM(vmulosh, 4, 5);
+GEN_VXFORM(vmuleub, 4, 8);
+GEN_VXFORM(vmuleuh, 4, 9);
+GEN_VXFORM(vmulesb, 4, 12);
+GEN_VXFORM(vmulesh, 4, 13);
+GEN_VXFORM(vslb, 2, 4);
+GEN_VXFORM(vslh, 2, 5);
+GEN_VXFORM(vslw, 2, 6);
+GEN_VXFORM(vsrb, 2, 8);
+GEN_VXFORM(vsrh, 2, 9);
+GEN_VXFORM(vsrw, 2, 10);
+GEN_VXFORM(vsrab, 2, 12);
+GEN_VXFORM(vsrah, 2, 13);
+GEN_VXFORM(vsraw, 2, 14);
+GEN_VXFORM(vslo, 6, 16);
+GEN_VXFORM(vsro, 6, 17);
+GEN_VXFORM(vaddcuw, 0, 6);
+GEN_VXFORM(vsubcuw, 0, 22);
+GEN_VXFORM(vaddubs, 0, 8);
+GEN_VXFORM(vadduhs, 0, 9);
+GEN_VXFORM(vadduws, 0, 10);
+GEN_VXFORM(vaddsbs, 0, 12);
+GEN_VXFORM(vaddshs, 0, 13);
+GEN_VXFORM(vaddsws, 0, 14);
+GEN_VXFORM(vsububs, 0, 24);
+GEN_VXFORM(vsubuhs, 0, 25);
+GEN_VXFORM(vsubuws, 0, 26);
+GEN_VXFORM(vsubsbs, 0, 28);
+GEN_VXFORM(vsubshs, 0, 29);
+GEN_VXFORM(vsubsws, 0, 30);
+GEN_VXFORM(vrlb, 2, 0);
+GEN_VXFORM(vrlh, 2, 1);
+GEN_VXFORM(vrlw, 2, 2);
+GEN_VXFORM(vsl, 2, 7);
+GEN_VXFORM(vsr, 2, 11);
+GEN_VXFORM(vpkuhum, 7, 0);
+GEN_VXFORM(vpkuwum, 7, 1);
+GEN_VXFORM(vpkuhus, 7, 2);
+GEN_VXFORM(vpkuwus, 7, 3);
+GEN_VXFORM(vpkshus, 7, 4);
+GEN_VXFORM(vpkswus, 7, 5);
+GEN_VXFORM(vpkshss, 7, 6);
+GEN_VXFORM(vpkswss, 7, 7);
+GEN_VXFORM(vpkpx, 7, 12);
+GEN_VXFORM(vsum4ubs, 4, 24);
+GEN_VXFORM(vsum4sbs, 4, 28);
+GEN_VXFORM(vsum4shs, 4, 25);
+GEN_VXFORM(vsum2sws, 4, 26);
+GEN_VXFORM(vsumsws, 4, 30);
+GEN_VXFORM(vaddfp, 5, 0);
+GEN_VXFORM(vsubfp, 5, 1);
+GEN_VXFORM(vmaxfp, 5, 16);
+GEN_VXFORM(vminfp, 5, 17);
+
+#define GEN_VXRFORM1(opname, name, str, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+ { \
+ TCGv_ptr ra, rb, rd; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ ra = gen_avr_ptr(rA(ctx->opcode)); \
+ rb = gen_avr_ptr(rB(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ gen_helper_##opname (rd, ra, rb); \
+ tcg_temp_free_ptr(ra); \
+ tcg_temp_free_ptr(rb); \
+ tcg_temp_free_ptr(rd); \
+ }
+
+#define GEN_VXRFORM(name, opc2, opc3) \
+ GEN_VXRFORM1(name, name, #name, opc2, opc3) \
+ GEN_VXRFORM1(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4)))
+
+GEN_VXRFORM(vcmpequb, 3, 0)
+GEN_VXRFORM(vcmpequh, 3, 1)
+GEN_VXRFORM(vcmpequw, 3, 2)
+GEN_VXRFORM(vcmpgtsb, 3, 12)
+GEN_VXRFORM(vcmpgtsh, 3, 13)
+GEN_VXRFORM(vcmpgtsw, 3, 14)
+GEN_VXRFORM(vcmpgtub, 3, 8)
+GEN_VXRFORM(vcmpgtuh, 3, 9)
+GEN_VXRFORM(vcmpgtuw, 3, 10)
+GEN_VXRFORM(vcmpeqfp, 3, 3)
+GEN_VXRFORM(vcmpgefp, 3, 7)
+GEN_VXRFORM(vcmpgtfp, 3, 11)
+GEN_VXRFORM(vcmpbfp, 3, 15)
+
+#define GEN_VXFORM_SIMM(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+ { \
+ TCGv_ptr rd; \
+ TCGv_i32 simm; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ simm = tcg_const_i32(SIMM5(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ gen_helper_##name (rd, simm); \
+ tcg_temp_free_i32(simm); \
+ tcg_temp_free_ptr(rd); \
+ }
+
+GEN_VXFORM_SIMM(vspltisb, 6, 12);
+GEN_VXFORM_SIMM(vspltish, 6, 13);
+GEN_VXFORM_SIMM(vspltisw, 6, 14);
+
+#define GEN_VXFORM_NOA(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+ { \
+ TCGv_ptr rb, rd; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ rb = gen_avr_ptr(rB(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ gen_helper_##name (rd, rb); \
+ tcg_temp_free_ptr(rb); \
+ tcg_temp_free_ptr(rd); \
+ }
+
+GEN_VXFORM_NOA(vupkhsb, 7, 8);
+GEN_VXFORM_NOA(vupkhsh, 7, 9);
+GEN_VXFORM_NOA(vupklsb, 7, 10);
+GEN_VXFORM_NOA(vupklsh, 7, 11);
+GEN_VXFORM_NOA(vupkhpx, 7, 13);
+GEN_VXFORM_NOA(vupklpx, 7, 15);
+GEN_VXFORM_NOA(vrefp, 5, 4);
+GEN_VXFORM_NOA(vrsqrtefp, 5, 5);
+GEN_VXFORM_NOA(vexptefp, 5, 6);
+GEN_VXFORM_NOA(vlogefp, 5, 7);
+GEN_VXFORM_NOA(vrfim, 5, 8);
+GEN_VXFORM_NOA(vrfin, 5, 9);
+GEN_VXFORM_NOA(vrfip, 5, 10);
+GEN_VXFORM_NOA(vrfiz, 5, 11);
+
+#define GEN_VXFORM_SIMM(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+ { \
+ TCGv_ptr rd; \
+ TCGv_i32 simm; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ simm = tcg_const_i32(SIMM5(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ gen_helper_##name (rd, simm); \
+ tcg_temp_free_i32(simm); \
+ tcg_temp_free_ptr(rd); \
+ }
+
+#define GEN_VXFORM_UIMM(name, opc2, opc3) \
+static void glue(gen_, name)(DisasContext *ctx) \
+ { \
+ TCGv_ptr rb, rd; \
+ TCGv_i32 uimm; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ uimm = tcg_const_i32(UIMM5(ctx->opcode)); \
+ rb = gen_avr_ptr(rB(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ gen_helper_##name (rd, rb, uimm); \
+ tcg_temp_free_i32(uimm); \
+ tcg_temp_free_ptr(rb); \
+ tcg_temp_free_ptr(rd); \
+ }
+
+GEN_VXFORM_UIMM(vspltb, 6, 8);
+GEN_VXFORM_UIMM(vsplth, 6, 9);
+GEN_VXFORM_UIMM(vspltw, 6, 10);
+GEN_VXFORM_UIMM(vcfux, 5, 12);
+GEN_VXFORM_UIMM(vcfsx, 5, 13);
+GEN_VXFORM_UIMM(vctuxs, 5, 14);
+GEN_VXFORM_UIMM(vctsxs, 5, 15);
+
+static void gen_vsldoi(DisasContext *ctx)
+{
+ TCGv_ptr ra, rb, rd;
+ TCGv_i32 sh;
+ if (unlikely(!ctx->altivec_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VPU);
+ return;
+ }
+ ra = gen_avr_ptr(rA(ctx->opcode));
+ rb = gen_avr_ptr(rB(ctx->opcode));
+ rd = gen_avr_ptr(rD(ctx->opcode));
+ sh = tcg_const_i32(VSH(ctx->opcode));
+ gen_helper_vsldoi (rd, ra, rb, sh);
+ tcg_temp_free_ptr(ra);
+ tcg_temp_free_ptr(rb);
+ tcg_temp_free_ptr(rd);
+ tcg_temp_free_i32(sh);
+}
+
+#define GEN_VAFORM_PAIRED(name0, name1, opc2) \
+static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
+ { \
+ TCGv_ptr ra, rb, rc, rd; \
+ if (unlikely(!ctx->altivec_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_VPU); \
+ return; \
+ } \
+ ra = gen_avr_ptr(rA(ctx->opcode)); \
+ rb = gen_avr_ptr(rB(ctx->opcode)); \
+ rc = gen_avr_ptr(rC(ctx->opcode)); \
+ rd = gen_avr_ptr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) { \
+ gen_helper_##name1 (rd, ra, rb, rc); \
+ } else { \
+ gen_helper_##name0 (rd, ra, rb, rc); \
+ } \
+ tcg_temp_free_ptr(ra); \
+ tcg_temp_free_ptr(rb); \
+ tcg_temp_free_ptr(rc); \
+ tcg_temp_free_ptr(rd); \
+ }
+
+GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16)
+
+static void gen_vmladduhm(DisasContext *ctx)
+{
+ TCGv_ptr ra, rb, rc, rd;
+ if (unlikely(!ctx->altivec_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_VPU);
+ return;
+ }
+ ra = gen_avr_ptr(rA(ctx->opcode));
+ rb = gen_avr_ptr(rB(ctx->opcode));
+ rc = gen_avr_ptr(rC(ctx->opcode));
+ rd = gen_avr_ptr(rD(ctx->opcode));
+ gen_helper_vmladduhm(rd, ra, rb, rc);
+ tcg_temp_free_ptr(ra);
+ tcg_temp_free_ptr(rb);
+ tcg_temp_free_ptr(rc);
+ tcg_temp_free_ptr(rd);
+}
+
+GEN_VAFORM_PAIRED(vmsumubm, vmsummbm, 18)
+GEN_VAFORM_PAIRED(vmsumuhm, vmsumuhs, 19)
+GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20)
+GEN_VAFORM_PAIRED(vsel, vperm, 21)
+GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23)
+
+/*** SPE extension ***/
+/* Register moves */
+
+static inline void gen_load_gpr64(TCGv_i64 t, int reg)
+{
+#if defined(TARGET_PPC64)
+ tcg_gen_mov_i64(t, cpu_gpr[reg]);
+#else
+ tcg_gen_concat_i32_i64(t, cpu_gpr[reg], cpu_gprh[reg]);
+#endif
+}
+
+static inline void gen_store_gpr64(int reg, TCGv_i64 t)
+{
+#if defined(TARGET_PPC64)
+ tcg_gen_mov_i64(cpu_gpr[reg], t);
+#else
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_trunc_i64_i32(cpu_gpr[reg], t);
+ tcg_gen_shri_i64(tmp, t, 32);
+ tcg_gen_trunc_i64_i32(cpu_gprh[reg], tmp);
+ tcg_temp_free_i64(tmp);
+#endif
+}
+
+#define GEN_SPE(name0, name1, opc2, opc3, inval, type) \
+static void glue(gen_, name0##_##name1)(DisasContext *ctx) \
+{ \
+ if (Rc(ctx->opcode)) \
+ gen_##name1(ctx); \
+ else \
+ gen_##name0(ctx); \
+}
+
+/* Handler for undefined SPE opcodes */
+static inline void gen_speundef(DisasContext *ctx)
+{
+ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
+}
+
+/* SPE logic */
+#if defined(TARGET_PPC64)
+#define GEN_SPEOP_LOGIC2(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ tcg_op(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \
+ cpu_gpr[rB(ctx->opcode)]); \
+}
+#else
+#define GEN_SPEOP_LOGIC2(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ tcg_op(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \
+ cpu_gpr[rB(ctx->opcode)]); \
+ tcg_op(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], \
+ cpu_gprh[rB(ctx->opcode)]); \
+}
+#endif
+
+GEN_SPEOP_LOGIC2(evand, tcg_gen_and_tl);
+GEN_SPEOP_LOGIC2(evandc, tcg_gen_andc_tl);
+GEN_SPEOP_LOGIC2(evxor, tcg_gen_xor_tl);
+GEN_SPEOP_LOGIC2(evor, tcg_gen_or_tl);
+GEN_SPEOP_LOGIC2(evnor, tcg_gen_nor_tl);
+GEN_SPEOP_LOGIC2(eveqv, tcg_gen_eqv_tl);
+GEN_SPEOP_LOGIC2(evorc, tcg_gen_orc_tl);
+GEN_SPEOP_LOGIC2(evnand, tcg_gen_nand_tl);
+
+/* SPE logic immediate */
+#if defined(TARGET_PPC64)
+#define GEN_SPEOP_TCG_LOGIC_IMM2(name, tcg_opi) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ TCGv_i32 t0 = tcg_temp_local_new_i32(); \
+ TCGv_i32 t1 = tcg_temp_local_new_i32(); \
+ TCGv_i64 t2 = tcg_temp_local_new_i64(); \
+ tcg_gen_trunc_i64_i32(t0, cpu_gpr[rA(ctx->opcode)]); \
+ tcg_opi(t0, t0, rB(ctx->opcode)); \
+ tcg_gen_shri_i64(t2, cpu_gpr[rA(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t1, t2); \
+ tcg_temp_free_i64(t2); \
+ tcg_opi(t1, t1, rB(ctx->opcode)); \
+ tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+#else
+#define GEN_SPEOP_TCG_LOGIC_IMM2(name, tcg_opi) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ tcg_opi(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \
+ rB(ctx->opcode)); \
+ tcg_opi(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], \
+ rB(ctx->opcode)); \
+}
+#endif
+GEN_SPEOP_TCG_LOGIC_IMM2(evslwi, tcg_gen_shli_i32);
+GEN_SPEOP_TCG_LOGIC_IMM2(evsrwiu, tcg_gen_shri_i32);
+GEN_SPEOP_TCG_LOGIC_IMM2(evsrwis, tcg_gen_sari_i32);
+GEN_SPEOP_TCG_LOGIC_IMM2(evrlwi, tcg_gen_rotli_i32);
+
+/* SPE arithmetic */
+#if defined(TARGET_PPC64)
+#define GEN_SPEOP_ARITH1(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ TCGv_i32 t0 = tcg_temp_local_new_i32(); \
+ TCGv_i32 t1 = tcg_temp_local_new_i32(); \
+ TCGv_i64 t2 = tcg_temp_local_new_i64(); \
+ tcg_gen_trunc_i64_i32(t0, cpu_gpr[rA(ctx->opcode)]); \
+ tcg_op(t0, t0); \
+ tcg_gen_shri_i64(t2, cpu_gpr[rA(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t1, t2); \
+ tcg_temp_free_i64(t2); \
+ tcg_op(t1, t1); \
+ tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+#else
+#define GEN_SPEOP_ARITH1(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ tcg_op(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); \
+ tcg_op(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]); \
+}
+#endif
+
+static inline void gen_op_evabs(TCGv_i32 ret, TCGv_i32 arg1)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+
+ tcg_gen_brcondi_i32(TCG_COND_GE, arg1, 0, l1);
+ tcg_gen_neg_i32(ret, arg1);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_mov_i32(ret, arg1);
+ gen_set_label(l2);
+}
+GEN_SPEOP_ARITH1(evabs, gen_op_evabs);
+GEN_SPEOP_ARITH1(evneg, tcg_gen_neg_i32);
+GEN_SPEOP_ARITH1(evextsb, tcg_gen_ext8s_i32);
+GEN_SPEOP_ARITH1(evextsh, tcg_gen_ext16s_i32);
+static inline void gen_op_evrndw(TCGv_i32 ret, TCGv_i32 arg1)
+{
+ tcg_gen_addi_i32(ret, arg1, 0x8000);
+ tcg_gen_ext16u_i32(ret, ret);
+}
+GEN_SPEOP_ARITH1(evrndw, gen_op_evrndw);
+GEN_SPEOP_ARITH1(evcntlsw, gen_helper_cntlsw32);
+GEN_SPEOP_ARITH1(evcntlzw, gen_helper_cntlzw32);
+
+#if defined(TARGET_PPC64)
+#define GEN_SPEOP_ARITH2(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ TCGv_i32 t0 = tcg_temp_local_new_i32(); \
+ TCGv_i32 t1 = tcg_temp_local_new_i32(); \
+ TCGv_i32 t2 = tcg_temp_local_new_i32(); \
+ TCGv_i64 t3 = tcg_temp_local_new_i64(); \
+ tcg_gen_trunc_i64_i32(t0, cpu_gpr[rA(ctx->opcode)]); \
+ tcg_gen_trunc_i64_i32(t2, cpu_gpr[rB(ctx->opcode)]); \
+ tcg_op(t0, t0, t2); \
+ tcg_gen_shri_i64(t3, cpu_gpr[rA(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t1, t3); \
+ tcg_gen_shri_i64(t3, cpu_gpr[rB(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t2, t3); \
+ tcg_temp_free_i64(t3); \
+ tcg_op(t1, t1, t2); \
+ tcg_temp_free_i32(t2); \
+ tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+#else
+#define GEN_SPEOP_ARITH2(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ tcg_op(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \
+ cpu_gpr[rB(ctx->opcode)]); \
+ tcg_op(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], \
+ cpu_gprh[rB(ctx->opcode)]); \
+}
+#endif
+
+static inline void gen_op_evsrwu(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ t0 = tcg_temp_local_new_i32();
+ /* No error here: 6 bits are used */
+ tcg_gen_andi_i32(t0, arg2, 0x3F);
+ tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1);
+ tcg_gen_shr_i32(ret, arg1, t0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_movi_i32(ret, 0);
+ gen_set_label(l2);
+ tcg_temp_free_i32(t0);
+}
+GEN_SPEOP_ARITH2(evsrwu, gen_op_evsrwu);
+static inline void gen_op_evsrws(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ t0 = tcg_temp_local_new_i32();
+ /* No error here: 6 bits are used */
+ tcg_gen_andi_i32(t0, arg2, 0x3F);
+ tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1);
+ tcg_gen_sar_i32(ret, arg1, t0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_movi_i32(ret, 0);
+ gen_set_label(l2);
+ tcg_temp_free_i32(t0);
+}
+GEN_SPEOP_ARITH2(evsrws, gen_op_evsrws);
+static inline void gen_op_evslw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ t0 = tcg_temp_local_new_i32();
+ /* No error here: 6 bits are used */
+ tcg_gen_andi_i32(t0, arg2, 0x3F);
+ tcg_gen_brcondi_i32(TCG_COND_GE, t0, 32, l1);
+ tcg_gen_shl_i32(ret, arg1, t0);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_movi_i32(ret, 0);
+ gen_set_label(l2);
+ tcg_temp_free_i32(t0);
+}
+GEN_SPEOP_ARITH2(evslw, gen_op_evslw);
+static inline void gen_op_evrlw(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ tcg_gen_andi_i32(t0, arg2, 0x1F);
+ tcg_gen_rotl_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+}
+GEN_SPEOP_ARITH2(evrlw, gen_op_evrlw);
+static inline void gen_evmergehi(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 32);
+ tcg_gen_andi_tl(t1, cpu_gpr[rA(ctx->opcode)], 0xFFFFFFFF0000000ULL);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+#else
+ tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]);
+ tcg_gen_mov_i32(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]);
+#endif
+}
+GEN_SPEOP_ARITH2(evaddw, tcg_gen_add_i32);
+static inline void gen_op_evsubf(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_sub_i32(ret, arg2, arg1);
+}
+GEN_SPEOP_ARITH2(evsubfw, gen_op_evsubf);
+
+/* SPE arithmetic immediate */
+#if defined(TARGET_PPC64)
+#define GEN_SPEOP_ARITH_IMM2(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ TCGv_i32 t0 = tcg_temp_local_new_i32(); \
+ TCGv_i32 t1 = tcg_temp_local_new_i32(); \
+ TCGv_i64 t2 = tcg_temp_local_new_i64(); \
+ tcg_gen_trunc_i64_i32(t0, cpu_gpr[rB(ctx->opcode)]); \
+ tcg_op(t0, t0, rA(ctx->opcode)); \
+ tcg_gen_shri_i64(t2, cpu_gpr[rB(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t1, t2); \
+ tcg_temp_free_i64(t2); \
+ tcg_op(t1, t1, rA(ctx->opcode)); \
+ tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+#else
+#define GEN_SPEOP_ARITH_IMM2(name, tcg_op) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ tcg_op(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \
+ rA(ctx->opcode)); \
+ tcg_op(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)], \
+ rA(ctx->opcode)); \
+}
+#endif
+GEN_SPEOP_ARITH_IMM2(evaddiw, tcg_gen_addi_i32);
+GEN_SPEOP_ARITH_IMM2(evsubifw, tcg_gen_subi_i32);
+
+/* SPE comparison */
+#if defined(TARGET_PPC64)
+#define GEN_SPEOP_COMP(name, tcg_cond) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ int l1 = gen_new_label(); \
+ int l2 = gen_new_label(); \
+ int l3 = gen_new_label(); \
+ int l4 = gen_new_label(); \
+ TCGv_i32 t0 = tcg_temp_local_new_i32(); \
+ TCGv_i32 t1 = tcg_temp_local_new_i32(); \
+ TCGv_i64 t2 = tcg_temp_local_new_i64(); \
+ tcg_gen_trunc_i64_i32(t0, cpu_gpr[rA(ctx->opcode)]); \
+ tcg_gen_trunc_i64_i32(t1, cpu_gpr[rB(ctx->opcode)]); \
+ tcg_gen_brcond_i32(tcg_cond, t0, t1, l1); \
+ tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], 0); \
+ tcg_gen_br(l2); \
+ gen_set_label(l1); \
+ tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], \
+ CRF_CL | CRF_CH_OR_CL | CRF_CH_AND_CL); \
+ gen_set_label(l2); \
+ tcg_gen_shri_i64(t2, cpu_gpr[rA(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t0, t2); \
+ tcg_gen_shri_i64(t2, cpu_gpr[rB(ctx->opcode)], 32); \
+ tcg_gen_trunc_i64_i32(t1, t2); \
+ tcg_temp_free_i64(t2); \
+ tcg_gen_brcond_i32(tcg_cond, t0, t1, l3); \
+ tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], \
+ ~(CRF_CH | CRF_CH_AND_CL)); \
+ tcg_gen_br(l4); \
+ gen_set_label(l3); \
+ tcg_gen_ori_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], \
+ CRF_CH | CRF_CH_OR_CL); \
+ gen_set_label(l4); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+#else
+#define GEN_SPEOP_COMP(name, tcg_cond) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ int l1 = gen_new_label(); \
+ int l2 = gen_new_label(); \
+ int l3 = gen_new_label(); \
+ int l4 = gen_new_label(); \
+ \
+ tcg_gen_brcond_i32(tcg_cond, cpu_gpr[rA(ctx->opcode)], \
+ cpu_gpr[rB(ctx->opcode)], l1); \
+ tcg_gen_movi_tl(cpu_crf[crfD(ctx->opcode)], 0); \
+ tcg_gen_br(l2); \
+ gen_set_label(l1); \
+ tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], \
+ CRF_CL | CRF_CH_OR_CL | CRF_CH_AND_CL); \
+ gen_set_label(l2); \
+ tcg_gen_brcond_i32(tcg_cond, cpu_gprh[rA(ctx->opcode)], \
+ cpu_gprh[rB(ctx->opcode)], l3); \
+ tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], \
+ ~(CRF_CH | CRF_CH_AND_CL)); \
+ tcg_gen_br(l4); \
+ gen_set_label(l3); \
+ tcg_gen_ori_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)], \
+ CRF_CH | CRF_CH_OR_CL); \
+ gen_set_label(l4); \
+}
+#endif
+GEN_SPEOP_COMP(evcmpgtu, TCG_COND_GTU);
+GEN_SPEOP_COMP(evcmpgts, TCG_COND_GT);
+GEN_SPEOP_COMP(evcmpltu, TCG_COND_LTU);
+GEN_SPEOP_COMP(evcmplts, TCG_COND_LT);
+GEN_SPEOP_COMP(evcmpeq, TCG_COND_EQ);
+
+/* SPE misc */
+static inline void gen_brinc(DisasContext *ctx)
+{
+ /* Note: brinc is usable even if SPE is disabled */
+ gen_helper_brinc(cpu_gpr[rD(ctx->opcode)],
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+}
+static inline void gen_evmergelo(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x00000000FFFFFFFFLL);
+ tcg_gen_shli_tl(t1, cpu_gpr[rA(ctx->opcode)], 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+#else
+ tcg_gen_mov_i32(cpu_gprh[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+#endif
+}
+static inline void gen_evmergehilo(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x00000000FFFFFFFFLL);
+ tcg_gen_andi_tl(t1, cpu_gpr[rA(ctx->opcode)], 0xFFFFFFFF0000000ULL);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+#else
+ tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+ tcg_gen_mov_i32(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]);
+#endif
+}
+static inline void gen_evmergelohi(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rB(ctx->opcode)], 32);
+ tcg_gen_shli_tl(t1, cpu_gpr[rA(ctx->opcode)], 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t0, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+#else
+ if (rD(ctx->opcode) == rA(ctx->opcode)) {
+ TCGv_i32 tmp = tcg_temp_new_i32();
+ tcg_gen_mov_i32(tmp, cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]);
+ tcg_gen_mov_i32(cpu_gprh[rD(ctx->opcode)], tmp);
+ tcg_temp_free_i32(tmp);
+ } else {
+ tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]);
+ tcg_gen_mov_i32(cpu_gprh[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ }
+#endif
+}
+static inline void gen_evsplati(DisasContext *ctx)
+{
+ uint64_t imm = ((int32_t)(rA(ctx->opcode) << 27)) >> 27;
+
+#if defined(TARGET_PPC64)
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], (imm << 32) | imm);
+#else
+ tcg_gen_movi_i32(cpu_gpr[rD(ctx->opcode)], imm);
+ tcg_gen_movi_i32(cpu_gprh[rD(ctx->opcode)], imm);
+#endif
+}
+static inline void gen_evsplatfi(DisasContext *ctx)
+{
+ uint64_t imm = rA(ctx->opcode) << 27;
+
+#if defined(TARGET_PPC64)
+ tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], (imm << 32) | imm);
+#else
+ tcg_gen_movi_i32(cpu_gpr[rD(ctx->opcode)], imm);
+ tcg_gen_movi_i32(cpu_gprh[rD(ctx->opcode)], imm);
+#endif
+}
+
+static inline void gen_evsel(DisasContext *ctx)
+{
+ int l1 = gen_new_label();
+ int l2 = gen_new_label();
+ int l3 = gen_new_label();
+ int l4 = gen_new_label();
+ TCGv_i32 t0 = tcg_temp_local_new_i32();
+#if defined(TARGET_PPC64)
+ TCGv t1 = tcg_temp_local_new();
+ TCGv t2 = tcg_temp_local_new();
+#endif
+ tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 3);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l1);
+#if defined(TARGET_PPC64)
+ tcg_gen_andi_tl(t1, cpu_gpr[rA(ctx->opcode)], 0xFFFFFFFF00000000ULL);
+#else
+ tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)]);
+#endif
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+#if defined(TARGET_PPC64)
+ tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0xFFFFFFFF00000000ULL);
+#else
+ tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rB(ctx->opcode)]);
+#endif
+ gen_set_label(l2);
+ tcg_gen_andi_i32(t0, cpu_crf[ctx->opcode & 0x07], 1 << 2);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, t0, 0, l3);
+#if defined(TARGET_PPC64)
+ tcg_gen_andi_tl(t2, cpu_gpr[rA(ctx->opcode)], 0x00000000FFFFFFFFULL);
+#else
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+#endif
+ tcg_gen_br(l4);
+ gen_set_label(l3);
+#if defined(TARGET_PPC64)
+ tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x00000000FFFFFFFFULL);
+#else
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]);
+#endif
+ gen_set_label(l4);
+ tcg_temp_free_i32(t0);
+#if defined(TARGET_PPC64)
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], t1, t2);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+#endif
+}
+
+static void gen_evsel0(DisasContext *ctx)
+{
+ gen_evsel(ctx);
+}
+
+static void gen_evsel1(DisasContext *ctx)
+{
+ gen_evsel(ctx);
+}
+
+static void gen_evsel2(DisasContext *ctx)
+{
+ gen_evsel(ctx);
+}
+
+static void gen_evsel3(DisasContext *ctx)
+{
+ gen_evsel(ctx);
+}
+
+GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, PPC_SPE);
+GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, PPC_SPE);
+GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, PPC_SPE); ////
+GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, PPC_SPE); ////
+GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, PPC_SPE); ////
+GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x00000000, PPC_SPE); //
+GEN_SPE(speundef, evand, 0x08, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(speundef, evorc, 0x0D, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, PPC_SPE);
+GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, PPC_SPE);
+GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, PPC_SPE); //
+GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, PPC_SPE);
+GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, PPC_SPE); ////
+GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, PPC_SPE); ////
+GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, PPC_SPE); ////
+GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, PPC_SPE); ////
+
+/* SPE load and stores */
+static inline void gen_addr_spe_imm_index(DisasContext *ctx, TCGv EA, int sh)
+{
+ target_ulong uimm = rB(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ tcg_gen_movi_tl(EA, uimm << sh);
+ } else {
+ tcg_gen_addi_tl(EA, cpu_gpr[rA(ctx->opcode)], uimm << sh);
+#if defined(TARGET_PPC64)
+ if (!ctx->sf_mode) {
+ tcg_gen_ext32u_tl(EA, EA);
+ }
+#endif
+ }
+}
+
+static inline void gen_op_evldd(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ gen_qemu_ld64(ctx, cpu_gpr[rD(ctx->opcode)], addr);
+#else
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ gen_qemu_ld64(ctx, t0, addr);
+ tcg_gen_trunc_i64_i32(cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(cpu_gprh[rD(ctx->opcode)], t0);
+ tcg_temp_free_i64(t0);
+#endif
+}
+
+static inline void gen_op_evldw(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld32u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 32);
+ gen_addr_add(ctx, addr, addr, 4);
+ gen_qemu_ld32u(ctx, t0, addr);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#else
+ gen_qemu_ld32u(ctx, cpu_gprh[rD(ctx->opcode)], addr);
+ gen_addr_add(ctx, addr, addr, 4);
+ gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], addr);
+#endif
+}
+
+static inline void gen_op_evldh(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 48);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(t0, t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(t0, t0, 16);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_or_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evlhhesplat(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld16u(ctx, t0, addr);
+#if defined(TARGET_PPC64)
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 48);
+ tcg_gen_shli_tl(t0, t0, 16);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ tcg_gen_shli_tl(t0, t0, 16);
+ tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evlhhousplat(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld16u(ctx, t0, addr);
+#if defined(TARGET_PPC64)
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evlhhossplat(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld16s(ctx, t0, addr);
+#if defined(TARGET_PPC64)
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 32);
+ tcg_gen_ext32u_tl(t0, t0);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evlwhe(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 48);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(t0, t0, 16);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 16);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evlwhou(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld16u(ctx, cpu_gpr[rD(ctx->opcode)], addr);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(t0, t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#else
+ gen_qemu_ld16u(ctx, cpu_gprh[rD(ctx->opcode)], addr);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, cpu_gpr[rD(ctx->opcode)], addr);
+#endif
+}
+
+static inline void gen_op_evlwhos(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld16s(ctx, t0, addr);
+ tcg_gen_ext32u_tl(cpu_gpr[rD(ctx->opcode)], t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16s(ctx, t0, addr);
+ tcg_gen_shli_tl(t0, t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_temp_free(t0);
+#else
+ gen_qemu_ld16s(ctx, cpu_gprh[rD(ctx->opcode)], addr);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16s(ctx, cpu_gpr[rD(ctx->opcode)], addr);
+#endif
+}
+
+static inline void gen_op_evlwwsplat(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+ gen_qemu_ld32u(ctx, t0, addr);
+#if defined(TARGET_PPC64)
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ tcg_gen_mov_tl(cpu_gprh[rD(ctx->opcode)], t0);
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evlwhsplat(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 48);
+ tcg_gen_shli_tl(t0, t0, 32);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+ tcg_gen_shli_tl(t0, t0, 16);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t0);
+#else
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gprh[rD(ctx->opcode)], t0, 16);
+ tcg_gen_or_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_ld16u(ctx, t0, addr);
+ tcg_gen_shli_tl(cpu_gpr[rD(ctx->opcode)], t0, 16);
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gprh[rD(ctx->opcode)], t0);
+#endif
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evstdd(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ gen_qemu_st64(ctx, cpu_gpr[rS(ctx->opcode)], addr);
+#else
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ tcg_gen_concat_i32_i64(t0, cpu_gpr[rS(ctx->opcode)], cpu_gprh[rS(ctx->opcode)]);
+ gen_qemu_st64(ctx, t0, addr);
+ tcg_temp_free_i64(t0);
+#endif
+}
+
+static inline void gen_op_evstdw(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 32);
+ gen_qemu_st32(ctx, t0, addr);
+ tcg_temp_free(t0);
+#else
+ gen_qemu_st32(ctx, cpu_gprh[rS(ctx->opcode)], addr);
+#endif
+ gen_addr_add(ctx, addr, addr, 4);
+ gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], addr);
+}
+
+static inline void gen_op_evstdh(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 48);
+#else
+ tcg_gen_shri_tl(t0, cpu_gprh[rS(ctx->opcode)], 16);
+#endif
+ gen_qemu_st16(ctx, t0, addr);
+ gen_addr_add(ctx, addr, addr, 2);
+#if defined(TARGET_PPC64)
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 32);
+ gen_qemu_st16(ctx, t0, addr);
+#else
+ gen_qemu_st16(ctx, cpu_gprh[rS(ctx->opcode)], addr);
+#endif
+ gen_addr_add(ctx, addr, addr, 2);
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 16);
+ gen_qemu_st16(ctx, t0, addr);
+ tcg_temp_free(t0);
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_st16(ctx, cpu_gpr[rS(ctx->opcode)], addr);
+}
+
+static inline void gen_op_evstwhe(DisasContext *ctx, TCGv addr)
+{
+ TCGv t0 = tcg_temp_new();
+#if defined(TARGET_PPC64)
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 48);
+#else
+ tcg_gen_shri_tl(t0, cpu_gprh[rS(ctx->opcode)], 16);
+#endif
+ gen_qemu_st16(ctx, t0, addr);
+ gen_addr_add(ctx, addr, addr, 2);
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 16);
+ gen_qemu_st16(ctx, t0, addr);
+ tcg_temp_free(t0);
+}
+
+static inline void gen_op_evstwho(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 32);
+ gen_qemu_st16(ctx, t0, addr);
+ tcg_temp_free(t0);
+#else
+ gen_qemu_st16(ctx, cpu_gprh[rS(ctx->opcode)], addr);
+#endif
+ gen_addr_add(ctx, addr, addr, 2);
+ gen_qemu_st16(ctx, cpu_gpr[rS(ctx->opcode)], addr);
+}
+
+static inline void gen_op_evstwwe(DisasContext *ctx, TCGv addr)
+{
+#if defined(TARGET_PPC64)
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], 32);
+ gen_qemu_st32(ctx, t0, addr);
+ tcg_temp_free(t0);
+#else
+ gen_qemu_st32(ctx, cpu_gprh[rS(ctx->opcode)], addr);
+#endif
+}
+
+static inline void gen_op_evstwwo(DisasContext *ctx, TCGv addr)
+{
+ gen_qemu_st32(ctx, cpu_gpr[rS(ctx->opcode)], addr);
+}
+
+#define GEN_SPEOP_LDST(name, opc2, sh) \
+static void glue(gen_, name)(DisasContext *ctx) \
+{ \
+ TCGv t0; \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ gen_set_access_type(ctx, ACCESS_INT); \
+ t0 = tcg_temp_new(); \
+ if (Rc(ctx->opcode)) { \
+ gen_addr_spe_imm_index(ctx, t0, sh); \
+ } else { \
+ gen_addr_reg_index(ctx, t0); \
+ } \
+ gen_op_##name(ctx, t0); \
+ tcg_temp_free(t0); \
+}
+
+GEN_SPEOP_LDST(evldd, 0x00, 3);
+GEN_SPEOP_LDST(evldw, 0x01, 3);
+GEN_SPEOP_LDST(evldh, 0x02, 3);
+GEN_SPEOP_LDST(evlhhesplat, 0x04, 1);
+GEN_SPEOP_LDST(evlhhousplat, 0x06, 1);
+GEN_SPEOP_LDST(evlhhossplat, 0x07, 1);
+GEN_SPEOP_LDST(evlwhe, 0x08, 2);
+GEN_SPEOP_LDST(evlwhou, 0x0A, 2);
+GEN_SPEOP_LDST(evlwhos, 0x0B, 2);
+GEN_SPEOP_LDST(evlwwsplat, 0x0C, 2);
+GEN_SPEOP_LDST(evlwhsplat, 0x0E, 2);
+
+GEN_SPEOP_LDST(evstdd, 0x10, 3);
+GEN_SPEOP_LDST(evstdw, 0x11, 3);
+GEN_SPEOP_LDST(evstdh, 0x12, 3);
+GEN_SPEOP_LDST(evstwhe, 0x18, 2);
+GEN_SPEOP_LDST(evstwho, 0x1A, 2);
+GEN_SPEOP_LDST(evstwwe, 0x1C, 2);
+GEN_SPEOP_LDST(evstwwo, 0x1E, 2);
+
+/* Multiply and add - TODO */
+#if 0
+GEN_SPE(speundef, evmhessf, 0x01, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhossf, 0x03, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(evmheumi, evmhesmi, 0x04, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhesmf, 0x05, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(evmhoumi, evmhosmi, 0x06, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhosmf, 0x07, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhessfa, 0x11, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhossfa, 0x13, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(evmheumia, evmhesmia, 0x14, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhesmfa, 0x15, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(evmhoumia, evmhosmia, 0x16, 0x10, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhosmfa, 0x17, 0x10, 0x00000000, PPC_SPE);
+
+GEN_SPE(speundef, evmwhssf, 0x03, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(evmwlumi, speundef, 0x04, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(evmwhumi, evmwhsmi, 0x06, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwhsmf, 0x07, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwssf, 0x09, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(evmwumi, evmwsmi, 0x0C, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwsmf, 0x0D, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwhssfa, 0x13, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(evmwlumia, speundef, 0x14, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(evmwhumia, evmwhsmia, 0x16, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwhsmfa, 0x17, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwssfa, 0x19, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(evmwumia, evmwsmia, 0x1C, 0x11, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwsmfa, 0x1D, 0x11, 0x00000000, PPC_SPE);
+
+GEN_SPE(evadduiaaw, evaddsiaaw, 0x00, 0x13, 0x0000F800, PPC_SPE);
+GEN_SPE(evsubfusiaaw, evsubfssiaaw, 0x01, 0x13, 0x0000F800, PPC_SPE);
+GEN_SPE(evaddumiaaw, evaddsmiaaw, 0x04, 0x13, 0x0000F800, PPC_SPE);
+GEN_SPE(evsubfumiaaw, evsubfsmiaaw, 0x05, 0x13, 0x0000F800, PPC_SPE);
+GEN_SPE(evdivws, evdivwu, 0x06, 0x13, 0x00000000, PPC_SPE);
+GEN_SPE(evmra, speundef, 0x07, 0x13, 0x0000F800, PPC_SPE);
+
+GEN_SPE(evmheusiaaw, evmhessiaaw, 0x00, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhessfaaw, 0x01, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(evmhousiaaw, evmhossiaaw, 0x02, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhossfaaw, 0x03, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(evmheumiaaw, evmhesmiaaw, 0x04, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhesmfaaw, 0x05, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(evmhoumiaaw, evmhosmiaaw, 0x06, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhosmfaaw, 0x07, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(evmhegumiaa, evmhegsmiaa, 0x14, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhegsmfaa, 0x15, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(evmhogumiaa, evmhogsmiaa, 0x16, 0x14, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhogsmfaa, 0x17, 0x14, 0x00000000, PPC_SPE);
+
+GEN_SPE(evmwlusiaaw, evmwlssiaaw, 0x00, 0x15, 0x00000000, PPC_SPE);
+GEN_SPE(evmwlumiaaw, evmwlsmiaaw, 0x04, 0x15, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwssfaa, 0x09, 0x15, 0x00000000, PPC_SPE);
+GEN_SPE(evmwumiaa, evmwsmiaa, 0x0C, 0x15, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwsmfaa, 0x0D, 0x15, 0x00000000, PPC_SPE);
+
+GEN_SPE(evmheusianw, evmhessianw, 0x00, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhessfanw, 0x01, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(evmhousianw, evmhossianw, 0x02, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhossfanw, 0x03, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(evmheumianw, evmhesmianw, 0x04, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhesmfanw, 0x05, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(evmhoumianw, evmhosmianw, 0x06, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhosmfanw, 0x07, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(evmhegumian, evmhegsmian, 0x14, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhegsmfan, 0x15, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(evmhigumian, evmhigsmian, 0x16, 0x16, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmhogsmfan, 0x17, 0x16, 0x00000000, PPC_SPE);
+
+GEN_SPE(evmwlusianw, evmwlssianw, 0x00, 0x17, 0x00000000, PPC_SPE);
+GEN_SPE(evmwlumianw, evmwlsmianw, 0x04, 0x17, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwssfan, 0x09, 0x17, 0x00000000, PPC_SPE);
+GEN_SPE(evmwumian, evmwsmian, 0x0C, 0x17, 0x00000000, PPC_SPE);
+GEN_SPE(speundef, evmwsmfan, 0x0D, 0x17, 0x00000000, PPC_SPE);
+#endif
+
+/*** SPE floating-point extension ***/
+#if defined(TARGET_PPC64)
+#define GEN_SPEFPUOP_CONV_32_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i32 t0; \
+ TCGv t1; \
+ t0 = tcg_temp_new_i32(); \
+ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \
+ gen_helper_##name(t0, t0); \
+ t1 = tcg_temp_new(); \
+ tcg_gen_extu_i32_tl(t1, t0); \
+ tcg_temp_free_i32(t0); \
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], \
+ 0xFFFFFFFF00000000ULL); \
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t1); \
+ tcg_temp_free(t1); \
+}
+#define GEN_SPEFPUOP_CONV_32_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i32 t0; \
+ TCGv t1; \
+ t0 = tcg_temp_new_i32(); \
+ gen_helper_##name(t0, cpu_gpr[rB(ctx->opcode)]); \
+ t1 = tcg_temp_new(); \
+ tcg_gen_extu_i32_tl(t1, t0); \
+ tcg_temp_free_i32(t0); \
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], \
+ 0xFFFFFFFF00000000ULL); \
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t1); \
+ tcg_temp_free(t1); \
+}
+#define GEN_SPEFPUOP_CONV_64_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i32 t0 = tcg_temp_new_i32(); \
+ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rB(ctx->opcode)]); \
+ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], t0); \
+ tcg_temp_free_i32(t0); \
+}
+#define GEN_SPEFPUOP_CONV_64_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \
+}
+#define GEN_SPEFPUOP_ARITH2_32_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i32 t0, t1; \
+ TCGv_i64 t2; \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ t0 = tcg_temp_new_i32(); \
+ t1 = tcg_temp_new_i32(); \
+ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \
+ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \
+ gen_helper_##name(t0, t0, t1); \
+ tcg_temp_free_i32(t1); \
+ t2 = tcg_temp_new(); \
+ tcg_gen_extu_i32_tl(t2, t0); \
+ tcg_temp_free_i32(t0); \
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], \
+ 0xFFFFFFFF00000000ULL); \
+ tcg_gen_or_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rD(ctx->opcode)], t2); \
+ tcg_temp_free(t2); \
+}
+#define GEN_SPEFPUOP_ARITH2_64_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], \
+ cpu_gpr[rB(ctx->opcode)]); \
+}
+#define GEN_SPEFPUOP_COMP_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i32 t0, t1; \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ t0 = tcg_temp_new_i32(); \
+ t1 = tcg_temp_new_i32(); \
+ tcg_gen_trunc_tl_i32(t0, cpu_gpr[rA(ctx->opcode)]); \
+ tcg_gen_trunc_tl_i32(t1, cpu_gpr[rB(ctx->opcode)]); \
+ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], t0, t1); \
+ tcg_temp_free_i32(t0); \
+ tcg_temp_free_i32(t1); \
+}
+#define GEN_SPEFPUOP_COMP_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \
+}
+#else
+#define GEN_SPEFPUOP_CONV_32_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \
+}
+#define GEN_SPEFPUOP_CONV_32_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i64 t0 = tcg_temp_new_i64(); \
+ gen_load_gpr64(t0, rB(ctx->opcode)); \
+ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], t0); \
+ tcg_temp_free_i64(t0); \
+}
+#define GEN_SPEFPUOP_CONV_64_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i64 t0 = tcg_temp_new_i64(); \
+ gen_helper_##name(t0, cpu_gpr[rB(ctx->opcode)]); \
+ gen_store_gpr64(rD(ctx->opcode), t0); \
+ tcg_temp_free_i64(t0); \
+}
+#define GEN_SPEFPUOP_CONV_64_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i64 t0 = tcg_temp_new_i64(); \
+ gen_load_gpr64(t0, rB(ctx->opcode)); \
+ gen_helper_##name(t0, t0); \
+ gen_store_gpr64(rD(ctx->opcode), t0); \
+ tcg_temp_free_i64(t0); \
+}
+#define GEN_SPEFPUOP_ARITH2_32_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ gen_helper_##name(cpu_gpr[rD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \
+}
+#define GEN_SPEFPUOP_ARITH2_64_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i64 t0, t1; \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ t0 = tcg_temp_new_i64(); \
+ t1 = tcg_temp_new_i64(); \
+ gen_load_gpr64(t0, rA(ctx->opcode)); \
+ gen_load_gpr64(t1, rB(ctx->opcode)); \
+ gen_helper_##name(t0, t0, t1); \
+ gen_store_gpr64(rD(ctx->opcode), t0); \
+ tcg_temp_free_i64(t0); \
+ tcg_temp_free_i64(t1); \
+}
+#define GEN_SPEFPUOP_COMP_32(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], \
+ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); \
+}
+#define GEN_SPEFPUOP_COMP_64(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ TCGv_i64 t0, t1; \
+ if (unlikely(!ctx->spe_enabled)) { \
+ gen_exception(ctx, POWERPC_EXCP_APU); \
+ return; \
+ } \
+ t0 = tcg_temp_new_i64(); \
+ t1 = tcg_temp_new_i64(); \
+ gen_load_gpr64(t0, rA(ctx->opcode)); \
+ gen_load_gpr64(t1, rB(ctx->opcode)); \
+ gen_helper_##name(cpu_crf[crfD(ctx->opcode)], t0, t1); \
+ tcg_temp_free_i64(t0); \
+ tcg_temp_free_i64(t1); \
+}
+#endif
+
+/* Single precision floating-point vectors operations */
+/* Arithmetic */
+GEN_SPEFPUOP_ARITH2_64_64(evfsadd);
+GEN_SPEFPUOP_ARITH2_64_64(evfssub);
+GEN_SPEFPUOP_ARITH2_64_64(evfsmul);
+GEN_SPEFPUOP_ARITH2_64_64(evfsdiv);
+static inline void gen_evfsabs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x8000000080000000LL);
+#else
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x80000000);
+ tcg_gen_andi_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], ~0x80000000);
+#endif
+}
+static inline void gen_evfsnabs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000080000000LL);
+#else
+ tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000);
+ tcg_gen_ori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000);
+#endif
+}
+static inline void gen_evfsneg(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000080000000LL);
+#else
+ tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000);
+ tcg_gen_xori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000);
+#endif
+}
+
+/* Conversion */
+GEN_SPEFPUOP_CONV_64_64(evfscfui);
+GEN_SPEFPUOP_CONV_64_64(evfscfsi);
+GEN_SPEFPUOP_CONV_64_64(evfscfuf);
+GEN_SPEFPUOP_CONV_64_64(evfscfsf);
+GEN_SPEFPUOP_CONV_64_64(evfsctui);
+GEN_SPEFPUOP_CONV_64_64(evfsctsi);
+GEN_SPEFPUOP_CONV_64_64(evfsctuf);
+GEN_SPEFPUOP_CONV_64_64(evfsctsf);
+GEN_SPEFPUOP_CONV_64_64(evfsctuiz);
+GEN_SPEFPUOP_CONV_64_64(evfsctsiz);
+
+/* Comparison */
+GEN_SPEFPUOP_COMP_64(evfscmpgt);
+GEN_SPEFPUOP_COMP_64(evfscmplt);
+GEN_SPEFPUOP_COMP_64(evfscmpeq);
+GEN_SPEFPUOP_COMP_64(evfststgt);
+GEN_SPEFPUOP_COMP_64(evfststlt);
+GEN_SPEFPUOP_COMP_64(evfststeq);
+
+/* Opcodes definitions */
+GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, PPC_SPE_SINGLE); //
+GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, PPC_SPE_SINGLE); //
+GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, PPC_SPE_SINGLE); //
+GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, PPC_SPE_SINGLE); //
+GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
+GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
+GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
+GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, PPC_SPE_SINGLE); //
+
+/* Single precision floating-point operations */
+/* Arithmetic */
+GEN_SPEFPUOP_ARITH2_32_32(efsadd);
+GEN_SPEFPUOP_ARITH2_32_32(efssub);
+GEN_SPEFPUOP_ARITH2_32_32(efsmul);
+GEN_SPEFPUOP_ARITH2_32_32(efsdiv);
+static inline void gen_efsabs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], (target_long)~0x80000000LL);
+}
+static inline void gen_efsnabs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+ tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000);
+}
+static inline void gen_efsneg(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+ tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000);
+}
+
+/* Conversion */
+GEN_SPEFPUOP_CONV_32_32(efscfui);
+GEN_SPEFPUOP_CONV_32_32(efscfsi);
+GEN_SPEFPUOP_CONV_32_32(efscfuf);
+GEN_SPEFPUOP_CONV_32_32(efscfsf);
+GEN_SPEFPUOP_CONV_32_32(efsctui);
+GEN_SPEFPUOP_CONV_32_32(efsctsi);
+GEN_SPEFPUOP_CONV_32_32(efsctuf);
+GEN_SPEFPUOP_CONV_32_32(efsctsf);
+GEN_SPEFPUOP_CONV_32_32(efsctuiz);
+GEN_SPEFPUOP_CONV_32_32(efsctsiz);
+GEN_SPEFPUOP_CONV_32_64(efscfd);
+
+/* Comparison */
+GEN_SPEFPUOP_COMP_32(efscmpgt);
+GEN_SPEFPUOP_COMP_32(efscmplt);
+GEN_SPEFPUOP_COMP_32(efscmpeq);
+GEN_SPEFPUOP_COMP_32(efststgt);
+GEN_SPEFPUOP_COMP_32(efststlt);
+GEN_SPEFPUOP_COMP_32(efststeq);
+
+/* Opcodes definitions */
+GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, PPC_SPE_SINGLE); //
+GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, PPC_SPE_SINGLE); //
+GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, PPC_SPE_SINGLE); //
+GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, PPC_SPE_SINGLE); //
+GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
+GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
+GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, PPC_SPE_SINGLE); //
+GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
+GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, PPC_SPE_SINGLE); //
+
+/* Double precision floating-point operations */
+/* Arithmetic */
+GEN_SPEFPUOP_ARITH2_64_64(efdadd);
+GEN_SPEFPUOP_ARITH2_64_64(efdsub);
+GEN_SPEFPUOP_ARITH2_64_64(efdmul);
+GEN_SPEFPUOP_ARITH2_64_64(efddiv);
+static inline void gen_efdabs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x8000000000000000LL);
+#else
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_andi_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], ~0x80000000);
+#endif
+}
+static inline void gen_efdnabs(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000000000000LL);
+#else
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_ori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000);
+#endif
+}
+static inline void gen_efdneg(DisasContext *ctx)
+{
+ if (unlikely(!ctx->spe_enabled)) {
+ gen_exception(ctx, POWERPC_EXCP_APU);
+ return;
+ }
+#if defined(TARGET_PPC64)
+ tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000000000000LL);
+#else
+ tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
+ tcg_gen_xori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000);
+#endif
+}
+
+/* Conversion */
+GEN_SPEFPUOP_CONV_64_32(efdcfui);
+GEN_SPEFPUOP_CONV_64_32(efdcfsi);
+GEN_SPEFPUOP_CONV_64_32(efdcfuf);
+GEN_SPEFPUOP_CONV_64_32(efdcfsf);
+GEN_SPEFPUOP_CONV_32_64(efdctui);
+GEN_SPEFPUOP_CONV_32_64(efdctsi);
+GEN_SPEFPUOP_CONV_32_64(efdctuf);
+GEN_SPEFPUOP_CONV_32_64(efdctsf);
+GEN_SPEFPUOP_CONV_32_64(efdctuiz);
+GEN_SPEFPUOP_CONV_32_64(efdctsiz);
+GEN_SPEFPUOP_CONV_64_32(efdcfs);
+GEN_SPEFPUOP_CONV_64_64(efdcfuid);
+GEN_SPEFPUOP_CONV_64_64(efdcfsid);
+GEN_SPEFPUOP_CONV_64_64(efdctuidz);
+GEN_SPEFPUOP_CONV_64_64(efdctsidz);
+
+/* Comparison */
+GEN_SPEFPUOP_COMP_64(efdcmpgt);
+GEN_SPEFPUOP_COMP_64(efdcmplt);
+GEN_SPEFPUOP_COMP_64(efdcmpeq);
+GEN_SPEFPUOP_COMP_64(efdtstgt);
+GEN_SPEFPUOP_COMP_64(efdtstlt);
+GEN_SPEFPUOP_COMP_64(efdtsteq);
+
+/* Opcodes definitions */
+GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, PPC_SPE_DOUBLE); //
+GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, PPC_SPE_DOUBLE); //
+GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
+GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, PPC_SPE_DOUBLE); //
+
+static opcode_t opcodes[] = {
+GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE),
+GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER),
+GEN_HANDLER(cmpi, 0x0B, 0xFF, 0xFF, 0x00400000, PPC_INTEGER),
+GEN_HANDLER(cmpl, 0x1F, 0x00, 0x01, 0x00400000, PPC_INTEGER),
+GEN_HANDLER(cmpli, 0x0A, 0xFF, 0xFF, 0x00400000, PPC_INTEGER),
+GEN_HANDLER(isel, 0x1F, 0x0F, 0xFF, 0x00000001, PPC_ISEL),
+GEN_HANDLER(addi, 0x0E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(addic, 0x0C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER2(addic_, "addic.", 0x0D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(addis, 0x0F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(mulhw, 0x1F, 0x0B, 0x02, 0x00000400, PPC_INTEGER),
+GEN_HANDLER(mulhwu, 0x1F, 0x0B, 0x00, 0x00000400, PPC_INTEGER),
+GEN_HANDLER(mullw, 0x1F, 0x0B, 0x07, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(mullwo, 0x1F, 0x0B, 0x17, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(mulld, 0x1F, 0x09, 0x07, 0x00000000, PPC_64B),
+#endif
+GEN_HANDLER(neg, 0x1F, 0x08, 0x03, 0x0000F800, PPC_INTEGER),
+GEN_HANDLER(nego, 0x1F, 0x08, 0x13, 0x0000F800, PPC_INTEGER),
+GEN_HANDLER(subfic, 0x08, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER2(andi_, "andi.", 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER2(andis_, "andis.", 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(cntlzw, 0x1F, 0x1A, 0x00, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(popcntb, 0x1F, 0x03, 0x03, 0x0000F801, PPC_POPCNTB),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B),
+#endif
+GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(slw, 0x1F, 0x18, 0x00, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(sraw, 0x1F, 0x18, 0x18, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(srawi, 0x1F, 0x18, 0x19, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(srw, 0x1F, 0x18, 0x10, 0x00000000, PPC_INTEGER),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(sld, 0x1F, 0x1B, 0x00, 0x00000000, PPC_64B),
+GEN_HANDLER(srad, 0x1F, 0x1A, 0x18, 0x00000000, PPC_64B),
+GEN_HANDLER2(sradi0, "sradi", 0x1F, 0x1A, 0x19, 0x00000000, PPC_64B),
+GEN_HANDLER2(sradi1, "sradi", 0x1F, 0x1B, 0x19, 0x00000000, PPC_64B),
+GEN_HANDLER(srd, 0x1F, 0x1B, 0x10, 0x00000000, PPC_64B),
+#endif
+GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES),
+GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT),
+GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT),
+GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT),
+GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT),
+GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT),
+GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT),
+GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT),
+GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT),
+GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT),
+GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x00010000, PPC_FLOAT),
+GEN_HANDLER(mtfsfi, 0x3F, 0x06, 0x04, 0x006f0800, PPC_FLOAT),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(ld, 0x3A, 0xFF, 0xFF, 0x00000000, PPC_64B),
+GEN_HANDLER(lq, 0x38, 0xFF, 0xFF, 0x00000000, PPC_64BX),
+GEN_HANDLER(std, 0x3E, 0xFF, 0xFF, 0x00000000, PPC_64B),
+#endif
+GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER),
+GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING),
+GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING),
+GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING),
+GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING),
+GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO),
+GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM),
+GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000000, PPC_RES),
+GEN_HANDLER2(stwcx_, "stwcx.", 0x1F, 0x16, 0x04, 0x00000000, PPC_RES),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000000, PPC_64B),
+GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B),
+#endif
+GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC),
+GEN_HANDLER(wait, 0x1F, 0x1E, 0x01, 0x03FFF801, PPC_WAIT),
+GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW),
+GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW),
+GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW),
+GEN_HANDLER(bclr, 0x13, 0x10, 0x00, 0x00000000, PPC_FLOW),
+GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
+GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
+GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
+#endif
+GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
+GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW),
+GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(td, 0x1F, 0x04, 0x02, 0x00000001, PPC_64B),
+GEN_HANDLER(tdi, 0x02, 0xFF, 0xFF, 0x00000000, PPC_64B),
+#endif
+GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC),
+GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x00000801, PPC_MISC),
+GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC),
+GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC),
+GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_MFTB),
+GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00000801, PPC_MISC),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(mtmsrd, 0x1F, 0x12, 0x05, 0x001EF801, PPC_64B),
+#endif
+GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC),
+GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC),
+GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03C00001, PPC_CACHE),
+GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE),
+GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE),
+GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x02000001, PPC_CACHE),
+GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x02000001, PPC_CACHE),
+GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03E00001, PPC_CACHE_DCBZ),
+GEN_HANDLER2(dcbz_970, "dcbz", 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZT),
+GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC),
+GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC),
+GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC),
+GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE_ICBI),
+GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_DCBA),
+GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT),
+GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT),
+GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT),
+GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT),
+#if defined(TARGET_PPC64)
+GEN_HANDLER2(mfsr_64b, "mfsr", 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT_64B),
+GEN_HANDLER2(mfsrin_64b, "mfsrin", 0x1F, 0x13, 0x14, 0x001F0001,
+ PPC_SEGMENT_64B),
+GEN_HANDLER2(mtsr_64b, "mtsr", 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT_64B),
+GEN_HANDLER2(mtsrin_64b, "mtsrin", 0x1F, 0x12, 0x07, 0x001F0001,
+ PPC_SEGMENT_64B),
+GEN_HANDLER2(slbmte, "slbmte", 0x1F, 0x12, 0x0C, 0x00000000, PPC_SEGMENT_64B),
+#endif
+GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA),
+GEN_HANDLER(tlbiel, 0x1F, 0x12, 0x08, 0x03FF0001, PPC_MEM_TLBIE),
+GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM_TLBIE),
+GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM_TLBSYNC),
+#if defined(TARGET_PPC64)
+GEN_HANDLER(slbia, 0x1F, 0x12, 0x0F, 0x03FFFC01, PPC_SLBI),
+GEN_HANDLER(slbie, 0x1F, 0x12, 0x0D, 0x03FF0001, PPC_SLBI),
+#endif
+GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN),
+GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN),
+GEN_HANDLER(abs, 0x1F, 0x08, 0x0B, 0x0000F800, PPC_POWER_BR),
+GEN_HANDLER(abso, 0x1F, 0x08, 0x1B, 0x0000F800, PPC_POWER_BR),
+GEN_HANDLER(clcs, 0x1F, 0x10, 0x13, 0x0000F800, PPC_POWER_BR),
+GEN_HANDLER(div, 0x1F, 0x0B, 0x0A, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(divo, 0x1F, 0x0B, 0x1A, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(divs, 0x1F, 0x0B, 0x0B, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(divso, 0x1F, 0x0B, 0x1B, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(doz, 0x1F, 0x08, 0x08, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(dozo, 0x1F, 0x08, 0x18, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(dozi, 0x09, 0xFF, 0xFF, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(lscbx, 0x1F, 0x15, 0x08, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(maskg, 0x1F, 0x1D, 0x00, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(maskir, 0x1F, 0x1D, 0x10, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(mul, 0x1F, 0x0B, 0x03, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(mulo, 0x1F, 0x0B, 0x13, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(nabs, 0x1F, 0x08, 0x0F, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(nabso, 0x1F, 0x08, 0x1F, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(rlmi, 0x16, 0xFF, 0xFF, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(rrib, 0x1F, 0x19, 0x10, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sle, 0x1F, 0x19, 0x04, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sleq, 0x1F, 0x19, 0x06, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sliq, 0x1F, 0x18, 0x05, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(slliq, 0x1F, 0x18, 0x07, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sllq, 0x1F, 0x18, 0x06, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(slq, 0x1F, 0x18, 0x04, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sraiq, 0x1F, 0x18, 0x1D, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sraq, 0x1F, 0x18, 0x1C, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sre, 0x1F, 0x19, 0x14, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(srea, 0x1F, 0x19, 0x1C, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sreq, 0x1F, 0x19, 0x16, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(sriq, 0x1F, 0x18, 0x15, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(srliq, 0x1F, 0x18, 0x17, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(srlq, 0x1F, 0x18, 0x16, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(srq, 0x1F, 0x18, 0x14, 0x00000000, PPC_POWER_BR),
+GEN_HANDLER(dsa, 0x1F, 0x14, 0x13, 0x03FFF801, PPC_602_SPEC),
+GEN_HANDLER(esa, 0x1F, 0x14, 0x12, 0x03FFF801, PPC_602_SPEC),
+GEN_HANDLER(mfrom, 0x1F, 0x09, 0x08, 0x03E0F801, PPC_602_SPEC),
+GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB),
+GEN_HANDLER2(tlbli_6xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_6xx_TLB),
+GEN_HANDLER2(tlbld_74xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_74xx_TLB),
+GEN_HANDLER2(tlbli_74xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_74xx_TLB),
+GEN_HANDLER(clf, 0x1F, 0x16, 0x03, 0x03E00000, PPC_POWER),
+GEN_HANDLER(cli, 0x1F, 0x16, 0x0F, 0x03E00000, PPC_POWER),
+GEN_HANDLER(dclst, 0x1F, 0x16, 0x13, 0x03E00000, PPC_POWER),
+GEN_HANDLER(mfsri, 0x1F, 0x13, 0x13, 0x00000001, PPC_POWER),
+GEN_HANDLER(rac, 0x1F, 0x12, 0x19, 0x00000001, PPC_POWER),
+GEN_HANDLER(rfsvc, 0x13, 0x12, 0x02, 0x03FFF0001, PPC_POWER),
+GEN_HANDLER(lfq, 0x38, 0xFF, 0xFF, 0x00000003, PPC_POWER2),
+GEN_HANDLER(lfqu, 0x39, 0xFF, 0xFF, 0x00000003, PPC_POWER2),
+GEN_HANDLER(lfqux, 0x1F, 0x17, 0x19, 0x00000001, PPC_POWER2),
+GEN_HANDLER(lfqx, 0x1F, 0x17, 0x18, 0x00000001, PPC_POWER2),
+GEN_HANDLER(stfq, 0x3C, 0xFF, 0xFF, 0x00000003, PPC_POWER2),
+GEN_HANDLER(stfqu, 0x3D, 0xFF, 0xFF, 0x00000003, PPC_POWER2),
+GEN_HANDLER(stfqux, 0x1F, 0x17, 0x1D, 0x00000001, PPC_POWER2),
+GEN_HANDLER(stfqx, 0x1F, 0x17, 0x1C, 0x00000001, PPC_POWER2),
+GEN_HANDLER(mfapidi, 0x1F, 0x13, 0x08, 0x0000F801, PPC_MFAPIDI),
+GEN_HANDLER(tlbiva, 0x1F, 0x12, 0x18, 0x03FFF801, PPC_TLBIVA),
+GEN_HANDLER(mfdcr, 0x1F, 0x03, 0x0A, 0x00000001, PPC_DCR),
+GEN_HANDLER(mtdcr, 0x1F, 0x03, 0x0E, 0x00000001, PPC_DCR),
+GEN_HANDLER(mfdcrx, 0x1F, 0x03, 0x08, 0x00000000, PPC_DCRX),
+GEN_HANDLER(mtdcrx, 0x1F, 0x03, 0x0C, 0x00000000, PPC_DCRX),
+GEN_HANDLER(mfdcrux, 0x1F, 0x03, 0x09, 0x00000000, PPC_DCRUX),
+GEN_HANDLER(mtdcrux, 0x1F, 0x03, 0x0D, 0x00000000, PPC_DCRUX),
+GEN_HANDLER(dccci, 0x1F, 0x06, 0x0E, 0x03E00001, PPC_4xx_COMMON),
+GEN_HANDLER(dcread, 0x1F, 0x06, 0x0F, 0x00000001, PPC_4xx_COMMON),
+GEN_HANDLER2(icbt_40x, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_40x_ICBT),
+GEN_HANDLER(iccci, 0x1F, 0x06, 0x1E, 0x00000001, PPC_4xx_COMMON),
+GEN_HANDLER(icread, 0x1F, 0x06, 0x1F, 0x03E00001, PPC_4xx_COMMON),
+GEN_HANDLER2(rfci_40x, "rfci", 0x13, 0x13, 0x01, 0x03FF8001, PPC_40x_EXCP),
+GEN_HANDLER(rfci, 0x13, 0x13, 0x01, 0x03FF8001, PPC_BOOKE),
+GEN_HANDLER(rfdi, 0x13, 0x07, 0x01, 0x03FF8001, PPC_RFDI),
+GEN_HANDLER(rfmci, 0x13, 0x06, 0x01, 0x03FF8001, PPC_RFMCI),
+GEN_HANDLER2(tlbre_40x, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_40x_TLB),
+GEN_HANDLER2(tlbsx_40x, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_40x_TLB),
+GEN_HANDLER2(tlbwe_40x, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_40x_TLB),
+GEN_HANDLER2(tlbre_440, "tlbre", 0x1F, 0x12, 0x1D, 0x00000001, PPC_BOOKE),
+GEN_HANDLER2(tlbsx_440, "tlbsx", 0x1F, 0x12, 0x1C, 0x00000000, PPC_BOOKE),
+GEN_HANDLER2(tlbwe_440, "tlbwe", 0x1F, 0x12, 0x1E, 0x00000001, PPC_BOOKE),
+GEN_HANDLER(wrtee, 0x1F, 0x03, 0x04, 0x000FFC01, PPC_WRTEE),
+GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE),
+GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC),
+GEN_HANDLER(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, PPC_BOOKE),
+GEN_HANDLER(msync, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE),
+GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE),
+GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC),
+GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC),
+GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC),
+GEN_HANDLER(mtvscr, 0x04, 0x2, 0x19, 0x03ff0000, PPC_ALTIVEC),
+GEN_HANDLER(vsldoi, 0x04, 0x16, 0xFF, 0x00000400, PPC_ALTIVEC),
+GEN_HANDLER(vmladduhm, 0x04, 0x11, 0xFF, 0x00000000, PPC_ALTIVEC),
+GEN_HANDLER2(evsel0, "evsel", 0x04, 0x1c, 0x09, 0x00000000, PPC_SPE),
+GEN_HANDLER2(evsel1, "evsel", 0x04, 0x1d, 0x09, 0x00000000, PPC_SPE),
+GEN_HANDLER2(evsel2, "evsel", 0x04, 0x1e, 0x09, 0x00000000, PPC_SPE),
+GEN_HANDLER2(evsel3, "evsel", 0x04, 0x1f, 0x09, 0x00000000, PPC_SPE),
+
+#undef GEN_INT_ARITH_ADD
+#undef GEN_INT_ARITH_ADD_CONST
+#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \
+GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x00000000, PPC_INTEGER),
+#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \
+ add_ca, compute_ca, compute_ov) \
+GEN_HANDLER(name, 0x1F, 0x0A, opc3, 0x0000F800, PPC_INTEGER),
+GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0)
+GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1)
+GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0)
+GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1)
+GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0)
+GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1)
+GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0)
+GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1)
+GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0)
+GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1)
+
+#undef GEN_INT_ARITH_DIVW
+#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \
+GEN_HANDLER(name, 0x1F, 0x0B, opc3, 0x00000000, PPC_INTEGER)
+GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0),
+GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1),
+GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0),
+GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1),
+
+#if defined(TARGET_PPC64)
+#undef GEN_INT_ARITH_DIVD
+#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \
+GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B)
+GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0),
+GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1),
+GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0),
+GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1),
+
+#undef GEN_INT_ARITH_MUL_HELPER
+#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \
+GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B)
+GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00),
+GEN_INT_ARITH_MUL_HELPER(mulhd, 0x02),
+GEN_INT_ARITH_MUL_HELPER(mulldo, 0x17),
+#endif
+
+#undef GEN_INT_ARITH_SUBF
+#undef GEN_INT_ARITH_SUBF_CONST
+#define GEN_INT_ARITH_SUBF(name, opc3, add_ca, compute_ca, compute_ov) \
+GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x00000000, PPC_INTEGER),
+#define GEN_INT_ARITH_SUBF_CONST(name, opc3, const_val, \
+ add_ca, compute_ca, compute_ov) \
+GEN_HANDLER(name, 0x1F, 0x08, opc3, 0x0000F800, PPC_INTEGER),
+GEN_INT_ARITH_SUBF(subf, 0x01, 0, 0, 0)
+GEN_INT_ARITH_SUBF(subfo, 0x11, 0, 0, 1)
+GEN_INT_ARITH_SUBF(subfc, 0x00, 0, 1, 0)
+GEN_INT_ARITH_SUBF(subfco, 0x10, 0, 1, 1)
+GEN_INT_ARITH_SUBF(subfe, 0x04, 1, 1, 0)
+GEN_INT_ARITH_SUBF(subfeo, 0x14, 1, 1, 1)
+GEN_INT_ARITH_SUBF_CONST(subfme, 0x07, -1LL, 1, 1, 0)
+GEN_INT_ARITH_SUBF_CONST(subfmeo, 0x17, -1LL, 1, 1, 1)
+GEN_INT_ARITH_SUBF_CONST(subfze, 0x06, 0, 1, 1, 0)
+GEN_INT_ARITH_SUBF_CONST(subfzeo, 0x16, 0, 1, 1, 1)
+
+#undef GEN_LOGICAL1
+#undef GEN_LOGICAL2
+#define GEN_LOGICAL2(name, tcg_op, opc, type) \
+GEN_HANDLER(name, 0x1F, 0x1C, opc, 0x00000000, type)
+#define GEN_LOGICAL1(name, tcg_op, opc, type) \
+GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, type)
+GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER),
+GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER),
+GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER),
+GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER),
+GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER),
+GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER),
+GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER),
+GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER),
+#if defined(TARGET_PPC64)
+GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B),
+#endif
+
+#if defined(TARGET_PPC64)
+#undef GEN_PPC64_R2
+#undef GEN_PPC64_R4
+#define GEN_PPC64_R2(name, opc1, opc2) \
+GEN_HANDLER2(name##0, stringify(name), opc1, opc2, 0xFF, 0x00000000, PPC_64B),\
+GEN_HANDLER2(name##1, stringify(name), opc1, opc2 | 0x10, 0xFF, 0x00000000, \
+ PPC_64B)
+#define GEN_PPC64_R4(name, opc1, opc2) \
+GEN_HANDLER2(name##0, stringify(name), opc1, opc2, 0xFF, 0x00000000, PPC_64B),\
+GEN_HANDLER2(name##1, stringify(name), opc1, opc2 | 0x01, 0xFF, 0x00000000, \
+ PPC_64B), \
+GEN_HANDLER2(name##2, stringify(name), opc1, opc2 | 0x10, 0xFF, 0x00000000, \
+ PPC_64B), \
+GEN_HANDLER2(name##3, stringify(name), opc1, opc2 | 0x11, 0xFF, 0x00000000, \
+ PPC_64B)
+GEN_PPC64_R4(rldicl, 0x1E, 0x00),
+GEN_PPC64_R4(rldicr, 0x1E, 0x02),
+GEN_PPC64_R4(rldic, 0x1E, 0x04),
+GEN_PPC64_R2(rldcl, 0x1E, 0x08),
+GEN_PPC64_R2(rldcr, 0x1E, 0x09),
+GEN_PPC64_R4(rldimi, 0x1E, 0x06),
+#endif
+
+#undef _GEN_FLOAT_ACB
+#undef GEN_FLOAT_ACB
+#undef _GEN_FLOAT_AB
+#undef GEN_FLOAT_AB
+#undef _GEN_FLOAT_AC
+#undef GEN_FLOAT_AC
+#undef GEN_FLOAT_B
+#undef GEN_FLOAT_BS
+#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type)
+#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \
+_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type), \
+_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type)
+#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type)
+#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \
+_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type), \
+_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type)
+#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type)
+#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \
+_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type), \
+_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type)
+#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \
+GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type)
+#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type)
+
+GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT),
+GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT),
+GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT),
+GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT),
+GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES),
+GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE),
+_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL),
+GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT),
+GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT),
+GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT),
+GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT),
+GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT),
+GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT),
+GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT),
+GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT),
+#if defined(TARGET_PPC64)
+GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B),
+GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B),
+GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B),
+#endif
+GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT),
+GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT),
+GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT),
+GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT),
+GEN_FLOAT_B(abs, 0x08, 0x08, 0, PPC_FLOAT),
+GEN_FLOAT_B(nabs, 0x08, 0x04, 0, PPC_FLOAT),
+GEN_FLOAT_B(neg, 0x08, 0x01, 0, PPC_FLOAT),
+
+#undef GEN_LD
+#undef GEN_LDU
+#undef GEN_LDUX
+#undef GEN_LDX
+#undef GEN_LDS
+#define GEN_LD(name, ldop, opc, type) \
+GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_LDU(name, ldop, opc, type) \
+GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_LDUX(name, ldop, opc2, opc3, type) \
+GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
+#define GEN_LDX(name, ldop, opc2, opc3, type) \
+GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type),
+#define GEN_LDS(name, ldop, op, type) \
+GEN_LD(name, ldop, op | 0x20, type) \
+GEN_LDU(name, ldop, op | 0x21, type) \
+GEN_LDUX(name, ldop, 0x17, op | 0x01, type) \
+GEN_LDX(name, ldop, 0x17, op | 0x00, type)
+
+GEN_LDS(lbz, ld8u, 0x02, PPC_INTEGER)
+GEN_LDS(lha, ld16s, 0x0A, PPC_INTEGER)
+GEN_LDS(lhz, ld16u, 0x08, PPC_INTEGER)
+GEN_LDS(lwz, ld32u, 0x00, PPC_INTEGER)
+#if defined(TARGET_PPC64)
+GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B)
+GEN_LDX(lwa, ld32s, 0x15, 0x0A, PPC_64B)
+GEN_LDUX(ld, ld64, 0x15, 0x01, PPC_64B)
+GEN_LDX(ld, ld64, 0x15, 0x00, PPC_64B)
+#endif
+GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER)
+GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER)
+
+#undef GEN_ST
+#undef GEN_STU
+#undef GEN_STUX
+#undef GEN_STX
+#undef GEN_STS
+#define GEN_ST(name, stop, opc, type) \
+GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_STU(name, stop, opc, type) \
+GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_STUX(name, stop, opc2, opc3, type) \
+GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type),
+#define GEN_STX(name, stop, opc2, opc3, type) \
+GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type),
+#define GEN_STS(name, stop, op, type) \
+GEN_ST(name, stop, op | 0x20, type) \
+GEN_STU(name, stop, op | 0x21, type) \
+GEN_STUX(name, stop, 0x17, op | 0x01, type) \
+GEN_STX(name, stop, 0x17, op | 0x00, type)
+
+GEN_STS(stb, st8, 0x06, PPC_INTEGER)
+GEN_STS(sth, st16, 0x0C, PPC_INTEGER)
+GEN_STS(stw, st32, 0x04, PPC_INTEGER)
+#if defined(TARGET_PPC64)
+GEN_STUX(std, st64, 0x15, 0x05, PPC_64B)
+GEN_STX(std, st64, 0x15, 0x04, PPC_64B)
+#endif
+GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER)
+GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER)
+
+#undef GEN_LDF
+#undef GEN_LDUF
+#undef GEN_LDUXF
+#undef GEN_LDXF
+#undef GEN_LDFS
+#define GEN_LDF(name, ldop, opc, type) \
+GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_LDUF(name, ldop, opc, type) \
+GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_LDUXF(name, ldop, opc, type) \
+GEN_HANDLER(name##ux, 0x1F, 0x17, opc, 0x00000001, type),
+#define GEN_LDXF(name, ldop, opc2, opc3, type) \
+GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type),
+#define GEN_LDFS(name, ldop, op, type) \
+GEN_LDF(name, ldop, op | 0x20, type) \
+GEN_LDUF(name, ldop, op | 0x21, type) \
+GEN_LDUXF(name, ldop, op | 0x01, type) \
+GEN_LDXF(name, ldop, 0x17, op | 0x00, type)
+
+GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT)
+GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT)
+
+#undef GEN_STF
+#undef GEN_STUF
+#undef GEN_STUXF
+#undef GEN_STXF
+#undef GEN_STFS
+#define GEN_STF(name, stop, opc, type) \
+GEN_HANDLER(name, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_STUF(name, stop, opc, type) \
+GEN_HANDLER(name##u, opc, 0xFF, 0xFF, 0x00000000, type),
+#define GEN_STUXF(name, stop, opc, type) \
+GEN_HANDLER(name##ux, 0x1F, 0x17, opc, 0x00000001, type),
+#define GEN_STXF(name, stop, opc2, opc3, type) \
+GEN_HANDLER(name##x, 0x1F, opc2, opc3, 0x00000001, type),
+#define GEN_STFS(name, stop, op, type) \
+GEN_STF(name, stop, op | 0x20, type) \
+GEN_STUF(name, stop, op | 0x21, type) \
+GEN_STUXF(name, stop, op | 0x01, type) \
+GEN_STXF(name, stop, 0x17, op | 0x00, type)
+
+GEN_STFS(stfd, st64, 0x16, PPC_FLOAT)
+GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT)
+GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX)
+
+#undef GEN_CRLOGIC
+#define GEN_CRLOGIC(name, tcg_op, opc) \
+GEN_HANDLER(name, 0x13, 0x01, opc, 0x00000001, PPC_INTEGER)
+GEN_CRLOGIC(crand, tcg_gen_and_i32, 0x08),
+GEN_CRLOGIC(crandc, tcg_gen_andc_i32, 0x04),
+GEN_CRLOGIC(creqv, tcg_gen_eqv_i32, 0x09),
+GEN_CRLOGIC(crnand, tcg_gen_nand_i32, 0x07),
+GEN_CRLOGIC(crnor, tcg_gen_nor_i32, 0x01),
+GEN_CRLOGIC(cror, tcg_gen_or_i32, 0x0E),
+GEN_CRLOGIC(crorc, tcg_gen_orc_i32, 0x0D),
+GEN_CRLOGIC(crxor, tcg_gen_xor_i32, 0x06),
+
+#undef GEN_MAC_HANDLER
+#define GEN_MAC_HANDLER(name, opc2, opc3) \
+GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_405_MAC)
+GEN_MAC_HANDLER(macchw, 0x0C, 0x05),
+GEN_MAC_HANDLER(macchwo, 0x0C, 0x15),
+GEN_MAC_HANDLER(macchws, 0x0C, 0x07),
+GEN_MAC_HANDLER(macchwso, 0x0C, 0x17),
+GEN_MAC_HANDLER(macchwsu, 0x0C, 0x06),
+GEN_MAC_HANDLER(macchwsuo, 0x0C, 0x16),
+GEN_MAC_HANDLER(macchwu, 0x0C, 0x04),
+GEN_MAC_HANDLER(macchwuo, 0x0C, 0x14),
+GEN_MAC_HANDLER(machhw, 0x0C, 0x01),
+GEN_MAC_HANDLER(machhwo, 0x0C, 0x11),
+GEN_MAC_HANDLER(machhws, 0x0C, 0x03),
+GEN_MAC_HANDLER(machhwso, 0x0C, 0x13),
+GEN_MAC_HANDLER(machhwsu, 0x0C, 0x02),
+GEN_MAC_HANDLER(machhwsuo, 0x0C, 0x12),
+GEN_MAC_HANDLER(machhwu, 0x0C, 0x00),
+GEN_MAC_HANDLER(machhwuo, 0x0C, 0x10),
+GEN_MAC_HANDLER(maclhw, 0x0C, 0x0D),
+GEN_MAC_HANDLER(maclhwo, 0x0C, 0x1D),
+GEN_MAC_HANDLER(maclhws, 0x0C, 0x0F),
+GEN_MAC_HANDLER(maclhwso, 0x0C, 0x1F),
+GEN_MAC_HANDLER(maclhwu, 0x0C, 0x0C),
+GEN_MAC_HANDLER(maclhwuo, 0x0C, 0x1C),
+GEN_MAC_HANDLER(maclhwsu, 0x0C, 0x0E),
+GEN_MAC_HANDLER(maclhwsuo, 0x0C, 0x1E),
+GEN_MAC_HANDLER(nmacchw, 0x0E, 0x05),
+GEN_MAC_HANDLER(nmacchwo, 0x0E, 0x15),
+GEN_MAC_HANDLER(nmacchws, 0x0E, 0x07),
+GEN_MAC_HANDLER(nmacchwso, 0x0E, 0x17),
+GEN_MAC_HANDLER(nmachhw, 0x0E, 0x01),
+GEN_MAC_HANDLER(nmachhwo, 0x0E, 0x11),
+GEN_MAC_HANDLER(nmachhws, 0x0E, 0x03),
+GEN_MAC_HANDLER(nmachhwso, 0x0E, 0x13),
+GEN_MAC_HANDLER(nmaclhw, 0x0E, 0x0D),
+GEN_MAC_HANDLER(nmaclhwo, 0x0E, 0x1D),
+GEN_MAC_HANDLER(nmaclhws, 0x0E, 0x0F),
+GEN_MAC_HANDLER(nmaclhwso, 0x0E, 0x1F),
+GEN_MAC_HANDLER(mulchw, 0x08, 0x05),
+GEN_MAC_HANDLER(mulchwu, 0x08, 0x04),
+GEN_MAC_HANDLER(mulhhw, 0x08, 0x01),
+GEN_MAC_HANDLER(mulhhwu, 0x08, 0x00),
+GEN_MAC_HANDLER(mullhw, 0x08, 0x0D),
+GEN_MAC_HANDLER(mullhwu, 0x08, 0x0C),
+
+#undef GEN_VR_LDX
+#undef GEN_VR_STX
+#undef GEN_VR_LVE
+#undef GEN_VR_STVE
+#define GEN_VR_LDX(name, opc2, opc3) \
+GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC)
+#define GEN_VR_STX(name, opc2, opc3) \
+GEN_HANDLER(st##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC)
+#define GEN_VR_LVE(name, opc2, opc3) \
+ GEN_HANDLER(lve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC)
+#define GEN_VR_STVE(name, opc2, opc3) \
+ GEN_HANDLER(stve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC)
+GEN_VR_LDX(lvx, 0x07, 0x03),
+GEN_VR_LDX(lvxl, 0x07, 0x0B),
+GEN_VR_LVE(bx, 0x07, 0x00),
+GEN_VR_LVE(hx, 0x07, 0x01),
+GEN_VR_LVE(wx, 0x07, 0x02),
+GEN_VR_STX(svx, 0x07, 0x07),
+GEN_VR_STX(svxl, 0x07, 0x0F),
+GEN_VR_STVE(bx, 0x07, 0x04),
+GEN_VR_STVE(hx, 0x07, 0x05),
+GEN_VR_STVE(wx, 0x07, 0x06),
+
+#undef GEN_VX_LOGICAL
+#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \
+GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC)
+GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16),
+GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17),
+GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18),
+GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19),
+GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20),
+
+#undef GEN_VXFORM
+#define GEN_VXFORM(name, opc2, opc3) \
+GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC)
+GEN_VXFORM(vaddubm, 0, 0),
+GEN_VXFORM(vadduhm, 0, 1),
+GEN_VXFORM(vadduwm, 0, 2),
+GEN_VXFORM(vsububm, 0, 16),
+GEN_VXFORM(vsubuhm, 0, 17),
+GEN_VXFORM(vsubuwm, 0, 18),
+GEN_VXFORM(vmaxub, 1, 0),
+GEN_VXFORM(vmaxuh, 1, 1),
+GEN_VXFORM(vmaxuw, 1, 2),
+GEN_VXFORM(vmaxsb, 1, 4),
+GEN_VXFORM(vmaxsh, 1, 5),
+GEN_VXFORM(vmaxsw, 1, 6),
+GEN_VXFORM(vminub, 1, 8),
+GEN_VXFORM(vminuh, 1, 9),
+GEN_VXFORM(vminuw, 1, 10),
+GEN_VXFORM(vminsb, 1, 12),
+GEN_VXFORM(vminsh, 1, 13),
+GEN_VXFORM(vminsw, 1, 14),
+GEN_VXFORM(vavgub, 1, 16),
+GEN_VXFORM(vavguh, 1, 17),
+GEN_VXFORM(vavguw, 1, 18),
+GEN_VXFORM(vavgsb, 1, 20),
+GEN_VXFORM(vavgsh, 1, 21),
+GEN_VXFORM(vavgsw, 1, 22),
+GEN_VXFORM(vmrghb, 6, 0),
+GEN_VXFORM(vmrghh, 6, 1),
+GEN_VXFORM(vmrghw, 6, 2),
+GEN_VXFORM(vmrglb, 6, 4),
+GEN_VXFORM(vmrglh, 6, 5),
+GEN_VXFORM(vmrglw, 6, 6),
+GEN_VXFORM(vmuloub, 4, 0),
+GEN_VXFORM(vmulouh, 4, 1),
+GEN_VXFORM(vmulosb, 4, 4),
+GEN_VXFORM(vmulosh, 4, 5),
+GEN_VXFORM(vmuleub, 4, 8),
+GEN_VXFORM(vmuleuh, 4, 9),
+GEN_VXFORM(vmulesb, 4, 12),
+GEN_VXFORM(vmulesh, 4, 13),
+GEN_VXFORM(vslb, 2, 4),
+GEN_VXFORM(vslh, 2, 5),
+GEN_VXFORM(vslw, 2, 6),
+GEN_VXFORM(vsrb, 2, 8),
+GEN_VXFORM(vsrh, 2, 9),
+GEN_VXFORM(vsrw, 2, 10),
+GEN_VXFORM(vsrab, 2, 12),
+GEN_VXFORM(vsrah, 2, 13),
+GEN_VXFORM(vsraw, 2, 14),
+GEN_VXFORM(vslo, 6, 16),
+GEN_VXFORM(vsro, 6, 17),
+GEN_VXFORM(vaddcuw, 0, 6),
+GEN_VXFORM(vsubcuw, 0, 22),
+GEN_VXFORM(vaddubs, 0, 8),
+GEN_VXFORM(vadduhs, 0, 9),
+GEN_VXFORM(vadduws, 0, 10),
+GEN_VXFORM(vaddsbs, 0, 12),
+GEN_VXFORM(vaddshs, 0, 13),
+GEN_VXFORM(vaddsws, 0, 14),
+GEN_VXFORM(vsububs, 0, 24),
+GEN_VXFORM(vsubuhs, 0, 25),
+GEN_VXFORM(vsubuws, 0, 26),
+GEN_VXFORM(vsubsbs, 0, 28),
+GEN_VXFORM(vsubshs, 0, 29),
+GEN_VXFORM(vsubsws, 0, 30),
+GEN_VXFORM(vrlb, 2, 0),
+GEN_VXFORM(vrlh, 2, 1),
+GEN_VXFORM(vrlw, 2, 2),
+GEN_VXFORM(vsl, 2, 7),
+GEN_VXFORM(vsr, 2, 11),
+GEN_VXFORM(vpkuhum, 7, 0),
+GEN_VXFORM(vpkuwum, 7, 1),
+GEN_VXFORM(vpkuhus, 7, 2),
+GEN_VXFORM(vpkuwus, 7, 3),
+GEN_VXFORM(vpkshus, 7, 4),
+GEN_VXFORM(vpkswus, 7, 5),
+GEN_VXFORM(vpkshss, 7, 6),
+GEN_VXFORM(vpkswss, 7, 7),
+GEN_VXFORM(vpkpx, 7, 12),
+GEN_VXFORM(vsum4ubs, 4, 24),
+GEN_VXFORM(vsum4sbs, 4, 28),
+GEN_VXFORM(vsum4shs, 4, 25),
+GEN_VXFORM(vsum2sws, 4, 26),
+GEN_VXFORM(vsumsws, 4, 30),
+GEN_VXFORM(vaddfp, 5, 0),
+GEN_VXFORM(vsubfp, 5, 1),
+GEN_VXFORM(vmaxfp, 5, 16),
+GEN_VXFORM(vminfp, 5, 17),
+
+#undef GEN_VXRFORM1
+#undef GEN_VXRFORM
+#define GEN_VXRFORM1(opname, name, str, opc2, opc3) \
+ GEN_HANDLER2(name, str, 0x4, opc2, opc3, 0x00000000, PPC_ALTIVEC),
+#define GEN_VXRFORM(name, opc2, opc3) \
+ GEN_VXRFORM1(name, name, #name, opc2, opc3) \
+ GEN_VXRFORM1(name##_dot, name##_, #name ".", opc2, (opc3 | (0x1 << 4)))
+GEN_VXRFORM(vcmpequb, 3, 0)
+GEN_VXRFORM(vcmpequh, 3, 1)
+GEN_VXRFORM(vcmpequw, 3, 2)
+GEN_VXRFORM(vcmpgtsb, 3, 12)
+GEN_VXRFORM(vcmpgtsh, 3, 13)
+GEN_VXRFORM(vcmpgtsw, 3, 14)
+GEN_VXRFORM(vcmpgtub, 3, 8)
+GEN_VXRFORM(vcmpgtuh, 3, 9)
+GEN_VXRFORM(vcmpgtuw, 3, 10)
+GEN_VXRFORM(vcmpeqfp, 3, 3)
+GEN_VXRFORM(vcmpgefp, 3, 7)
+GEN_VXRFORM(vcmpgtfp, 3, 11)
+GEN_VXRFORM(vcmpbfp, 3, 15)
+
+#undef GEN_VXFORM_SIMM
+#define GEN_VXFORM_SIMM(name, opc2, opc3) \
+ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC)
+GEN_VXFORM_SIMM(vspltisb, 6, 12),
+GEN_VXFORM_SIMM(vspltish, 6, 13),
+GEN_VXFORM_SIMM(vspltisw, 6, 14),
+
+#undef GEN_VXFORM_NOA
+#define GEN_VXFORM_NOA(name, opc2, opc3) \
+ GEN_HANDLER(name, 0x04, opc2, opc3, 0x001f0000, PPC_ALTIVEC)
+GEN_VXFORM_NOA(vupkhsb, 7, 8),
+GEN_VXFORM_NOA(vupkhsh, 7, 9),
+GEN_VXFORM_NOA(vupklsb, 7, 10),
+GEN_VXFORM_NOA(vupklsh, 7, 11),
+GEN_VXFORM_NOA(vupkhpx, 7, 13),
+GEN_VXFORM_NOA(vupklpx, 7, 15),
+GEN_VXFORM_NOA(vrefp, 5, 4),
+GEN_VXFORM_NOA(vrsqrtefp, 5, 5),
+GEN_VXFORM_NOA(vexptefp, 5, 6),
+GEN_VXFORM_NOA(vlogefp, 5, 7),
+GEN_VXFORM_NOA(vrfim, 5, 8),
+GEN_VXFORM_NOA(vrfin, 5, 9),
+GEN_VXFORM_NOA(vrfip, 5, 10),
+GEN_VXFORM_NOA(vrfiz, 5, 11),
+
+#undef GEN_VXFORM_UIMM
+#define GEN_VXFORM_UIMM(name, opc2, opc3) \
+ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC)
+GEN_VXFORM_UIMM(vspltb, 6, 8),
+GEN_VXFORM_UIMM(vsplth, 6, 9),
+GEN_VXFORM_UIMM(vspltw, 6, 10),
+GEN_VXFORM_UIMM(vcfux, 5, 12),
+GEN_VXFORM_UIMM(vcfsx, 5, 13),
+GEN_VXFORM_UIMM(vctuxs, 5, 14),
+GEN_VXFORM_UIMM(vctsxs, 5, 15),
+
+#undef GEN_VAFORM_PAIRED
+#define GEN_VAFORM_PAIRED(name0, name1, opc2) \
+ GEN_HANDLER(name0##_##name1, 0x04, opc2, 0xFF, 0x00000000, PPC_ALTIVEC)
+GEN_VAFORM_PAIRED(vmhaddshs, vmhraddshs, 16),
+GEN_VAFORM_PAIRED(vmsumubm, vmsummbm, 18),
+GEN_VAFORM_PAIRED(vmsumuhm, vmsumuhs, 19),
+GEN_VAFORM_PAIRED(vmsumshm, vmsumshs, 20),
+GEN_VAFORM_PAIRED(vsel, vperm, 21),
+GEN_VAFORM_PAIRED(vmaddfp, vnmsubfp, 23),
+
+#undef GEN_SPE
+#define GEN_SPE(name0, name1, opc2, opc3, inval, type) \
+GEN_HANDLER(name0##_##name1, 0x04, opc2, opc3, inval, type)
+GEN_SPE(evaddw, speundef, 0x00, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evaddiw, speundef, 0x01, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evsubfw, speundef, 0x02, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evsubifw, speundef, 0x03, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evabs, evneg, 0x04, 0x08, 0x0000F800, PPC_SPE),
+GEN_SPE(evextsb, evextsh, 0x05, 0x08, 0x0000F800, PPC_SPE),
+GEN_SPE(evrndw, evcntlzw, 0x06, 0x08, 0x0000F800, PPC_SPE),
+GEN_SPE(evcntlsw, brinc, 0x07, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(speundef, evand, 0x08, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evandc, speundef, 0x09, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evxor, evor, 0x0B, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evnor, eveqv, 0x0C, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(speundef, evorc, 0x0D, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evnand, speundef, 0x0F, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evsrwu, evsrws, 0x10, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evsrwiu, evsrwis, 0x11, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evslw, speundef, 0x12, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evslwi, speundef, 0x13, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evrlw, evsplati, 0x14, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evrlwi, evsplatfi, 0x15, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evmergehi, evmergelo, 0x16, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evmergehilo, evmergelohi, 0x17, 0x08, 0x00000000, PPC_SPE),
+GEN_SPE(evcmpgtu, evcmpgts, 0x18, 0x08, 0x00600000, PPC_SPE),
+GEN_SPE(evcmpltu, evcmplts, 0x19, 0x08, 0x00600000, PPC_SPE),
+GEN_SPE(evcmpeq, speundef, 0x1A, 0x08, 0x00600000, PPC_SPE),
+
+GEN_SPE(evfsadd, evfssub, 0x00, 0x0A, 0x00000000, PPC_SPE_SINGLE),
+GEN_SPE(evfsabs, evfsnabs, 0x02, 0x0A, 0x0000F800, PPC_SPE_SINGLE),
+GEN_SPE(evfsneg, speundef, 0x03, 0x0A, 0x0000F800, PPC_SPE_SINGLE),
+GEN_SPE(evfsmul, evfsdiv, 0x04, 0x0A, 0x00000000, PPC_SPE_SINGLE),
+GEN_SPE(evfscmpgt, evfscmplt, 0x06, 0x0A, 0x00600000, PPC_SPE_SINGLE),
+GEN_SPE(evfscmpeq, speundef, 0x07, 0x0A, 0x00600000, PPC_SPE_SINGLE),
+GEN_SPE(evfscfui, evfscfsi, 0x08, 0x0A, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(evfscfuf, evfscfsf, 0x09, 0x0A, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(evfsctui, evfsctsi, 0x0A, 0x0A, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(evfsctuf, evfsctsf, 0x0B, 0x0A, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(evfsctuiz, speundef, 0x0C, 0x0A, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(evfsctsiz, speundef, 0x0D, 0x0A, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(evfststgt, evfststlt, 0x0E, 0x0A, 0x00600000, PPC_SPE_SINGLE),
+GEN_SPE(evfststeq, speundef, 0x0F, 0x0A, 0x00600000, PPC_SPE_SINGLE),
+
+GEN_SPE(efsadd, efssub, 0x00, 0x0B, 0x00000000, PPC_SPE_SINGLE),
+GEN_SPE(efsabs, efsnabs, 0x02, 0x0B, 0x0000F800, PPC_SPE_SINGLE),
+GEN_SPE(efsneg, speundef, 0x03, 0x0B, 0x0000F800, PPC_SPE_SINGLE),
+GEN_SPE(efsmul, efsdiv, 0x04, 0x0B, 0x00000000, PPC_SPE_SINGLE),
+GEN_SPE(efscmpgt, efscmplt, 0x06, 0x0B, 0x00600000, PPC_SPE_SINGLE),
+GEN_SPE(efscmpeq, efscfd, 0x07, 0x0B, 0x00600000, PPC_SPE_SINGLE),
+GEN_SPE(efscfui, efscfsi, 0x08, 0x0B, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(efscfuf, efscfsf, 0x09, 0x0B, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(efsctui, efsctsi, 0x0A, 0x0B, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(efsctuf, efsctsf, 0x0B, 0x0B, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(efsctuiz, speundef, 0x0C, 0x0B, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(efsctsiz, speundef, 0x0D, 0x0B, 0x00180000, PPC_SPE_SINGLE),
+GEN_SPE(efststgt, efststlt, 0x0E, 0x0B, 0x00600000, PPC_SPE_SINGLE),
+GEN_SPE(efststeq, speundef, 0x0F, 0x0B, 0x00600000, PPC_SPE_SINGLE),
+
+GEN_SPE(efdadd, efdsub, 0x10, 0x0B, 0x00000000, PPC_SPE_DOUBLE),
+GEN_SPE(efdcfuid, efdcfsid, 0x11, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdabs, efdnabs, 0x12, 0x0B, 0x0000F800, PPC_SPE_DOUBLE),
+GEN_SPE(efdneg, speundef, 0x13, 0x0B, 0x0000F800, PPC_SPE_DOUBLE),
+GEN_SPE(efdmul, efddiv, 0x14, 0x0B, 0x00000000, PPC_SPE_DOUBLE),
+GEN_SPE(efdctuidz, efdctsidz, 0x15, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdcmpgt, efdcmplt, 0x16, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
+GEN_SPE(efdcmpeq, efdcfs, 0x17, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
+GEN_SPE(efdcfui, efdcfsi, 0x18, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdcfuf, efdcfsf, 0x19, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdctui, efdctsi, 0x1A, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdctuf, efdctsf, 0x1B, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdctuiz, speundef, 0x1C, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, PPC_SPE_DOUBLE),
+GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
+GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, PPC_SPE_DOUBLE),
+
+#undef GEN_SPEOP_LDST
+#define GEN_SPEOP_LDST(name, opc2, sh) \
+GEN_HANDLER(name, 0x04, opc2, 0x0C, 0x00000000, PPC_SPE)
+GEN_SPEOP_LDST(evldd, 0x00, 3),
+GEN_SPEOP_LDST(evldw, 0x01, 3),
+GEN_SPEOP_LDST(evldh, 0x02, 3),
+GEN_SPEOP_LDST(evlhhesplat, 0x04, 1),
+GEN_SPEOP_LDST(evlhhousplat, 0x06, 1),
+GEN_SPEOP_LDST(evlhhossplat, 0x07, 1),
+GEN_SPEOP_LDST(evlwhe, 0x08, 2),
+GEN_SPEOP_LDST(evlwhou, 0x0A, 2),
+GEN_SPEOP_LDST(evlwhos, 0x0B, 2),
+GEN_SPEOP_LDST(evlwwsplat, 0x0C, 2),
+GEN_SPEOP_LDST(evlwhsplat, 0x0E, 2),
+
+GEN_SPEOP_LDST(evstdd, 0x10, 3),
+GEN_SPEOP_LDST(evstdw, 0x11, 3),
+GEN_SPEOP_LDST(evstdh, 0x12, 3),
+GEN_SPEOP_LDST(evstwhe, 0x18, 2),
+GEN_SPEOP_LDST(evstwho, 0x1A, 2),
+GEN_SPEOP_LDST(evstwwe, 0x1C, 2),
+GEN_SPEOP_LDST(evstwwo, 0x1E, 2),
+};
+
+#include "translate_init.c"
+#include "helper_regs.h"
+
+/*****************************************************************************/
+/* Misc PowerPC helpers */
+void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+#define RGPL 4
+#define RFPL 4
+
+ int i;
+
+ cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR "
+ TARGET_FMT_lx " XER " TARGET_FMT_lx "\n",
+ env->nip, env->lr, env->ctr, env->xer);
+ cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF "
+ TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0],
+ env->hflags, env->mmu_idx);
+#if !defined(NO_TIMER_DUMP)
+ cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64
+#if !defined(CONFIG_USER_ONLY)
+ " DECR %08" PRIu32
+#endif
+ "\n",
+ cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env)
+#if !defined(CONFIG_USER_ONLY)
+ , cpu_ppc_load_decr(env)
+#endif
+ );
+#endif
+ for (i = 0; i < 32; i++) {
+ if ((i & (RGPL - 1)) == 0)
+ cpu_fprintf(f, "GPR%02d", i);
+ cpu_fprintf(f, " %016" PRIx64, ppc_dump_gpr(env, i));
+ if ((i & (RGPL - 1)) == (RGPL - 1))
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "CR ");
+ for (i = 0; i < 8; i++)
+ cpu_fprintf(f, "%01x", env->crf[i]);
+ cpu_fprintf(f, " [");
+ for (i = 0; i < 8; i++) {
+ char a = '-';
+ if (env->crf[i] & 0x08)
+ a = 'L';
+ else if (env->crf[i] & 0x04)
+ a = 'G';
+ else if (env->crf[i] & 0x02)
+ a = 'E';
+ cpu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' ');
+ }
+ cpu_fprintf(f, " ] RES " TARGET_FMT_lx "\n",
+ env->reserve_addr);
+ for (i = 0; i < 32; i++) {
+ if ((i & (RFPL - 1)) == 0)
+ cpu_fprintf(f, "FPR%02d", i);
+ cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i]));
+ if ((i & (RFPL - 1)) == (RFPL - 1))
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "FPSCR %08x\n", env->fpscr);
+#if !defined(CONFIG_USER_ONLY)
+ cpu_fprintf(f, "SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx " SDR1 "
+ TARGET_FMT_lx "\n", env->spr[SPR_SRR0], env->spr[SPR_SRR1],
+ env->sdr1);
+#endif
+
+#undef RGPL
+#undef RFPL
+}
+
+void cpu_dump_statistics (CPUState *env, FILE*f, fprintf_function cpu_fprintf,
+ int flags)
+{
+#if defined(DO_PPC_STATISTICS)
+ opc_handler_t **t1, **t2, **t3, *handler;
+ int op1, op2, op3;
+
+ t1 = env->opcodes;
+ for (op1 = 0; op1 < 64; op1++) {
+ handler = t1[op1];
+ if (is_indirect_opcode(handler)) {
+ t2 = ind_table(handler);
+ for (op2 = 0; op2 < 32; op2++) {
+ handler = t2[op2];
+ if (is_indirect_opcode(handler)) {
+ t3 = ind_table(handler);
+ for (op3 = 0; op3 < 32; op3++) {
+ handler = t3[op3];
+ if (handler->count == 0)
+ continue;
+ cpu_fprintf(f, "%02x %02x %02x (%02x %04d) %16s: "
+ "%016" PRIx64 " %" PRId64 "\n",
+ op1, op2, op3, op1, (op3 << 5) | op2,
+ handler->oname,
+ handler->count, handler->count);
+ }
+ } else {
+ if (handler->count == 0)
+ continue;
+ cpu_fprintf(f, "%02x %02x (%02x %04d) %16s: "
+ "%016" PRIx64 " %" PRId64 "\n",
+ op1, op2, op1, op2, handler->oname,
+ handler->count, handler->count);
+ }
+ }
+ } else {
+ if (handler->count == 0)
+ continue;
+ cpu_fprintf(f, "%02x (%02x ) %16s: %016" PRIx64
+ " %" PRId64 "\n",
+ op1, op1, handler->oname,
+ handler->count, handler->count);
+ }
+ }
+#endif
+}
+
+/*****************************************************************************/
+static inline void gen_intermediate_code_internal(CPUState *env,
+ TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext ctx, *ctxp = &ctx;
+ opc_handler_t **table, *handler;
+ target_ulong pc_start;
+ uint16_t *gen_opc_end;
+ CPUBreakpoint *bp;
+ int j, lj = -1;
+ int num_insns;
+ int max_insns;
+
+ pc_start = tb->pc;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ ctx.nip = pc_start;
+ ctx.tb = tb;
+ ctx.exception = POWERPC_EXCP_NONE;
+ ctx.spr_cb = env->spr_cb;
+ ctx.mem_idx = env->mmu_idx;
+ ctx.access_type = -1;
+ ctx.le_mode = env->hflags & (1 << MSR_LE) ? 1 : 0;
+#if defined(TARGET_PPC64)
+ ctx.sf_mode = msr_sf;
+#endif
+ ctx.fpu_enabled = msr_fp;
+ if ((env->flags & POWERPC_FLAG_SPE) && msr_spe)
+ ctx.spe_enabled = msr_spe;
+ else
+ ctx.spe_enabled = 0;
+ if ((env->flags & POWERPC_FLAG_VRE) && msr_vr)
+ ctx.altivec_enabled = msr_vr;
+ else
+ ctx.altivec_enabled = 0;
+ if ((env->flags & POWERPC_FLAG_SE) && msr_se)
+ ctx.singlestep_enabled = CPU_SINGLE_STEP;
+ else
+ ctx.singlestep_enabled = 0;
+ if ((env->flags & POWERPC_FLAG_BE) && msr_be)
+ ctx.singlestep_enabled |= CPU_BRANCH_STEP;
+ if (unlikely(env->singlestep_enabled))
+ ctx.singlestep_enabled |= GDBSTUB_SINGLE_STEP;
+#if defined (DO_SINGLE_STEP) && 0
+ /* Single step trace mode */
+ msr_se = 1;
+#endif
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+
+ gen_icount_start();
+ /* Set env in case of segfault during code fetch */
+ while (ctx.exception == POWERPC_EXCP_NONE && gen_opc_ptr < gen_opc_end) {
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (bp->pc == ctx.nip) {
+ gen_debug_exception(ctxp);
+ break;
+ }
+ }
+ }
+ if (unlikely(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] = ctx.nip;
+ gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
+ }
+ LOG_DISAS("----------------\n");
+ LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n",
+ ctx.nip, ctx.mem_idx, (int)msr_ir);
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+ if (unlikely(ctx.le_mode)) {
+ ctx.opcode = bswap32(ldl_code(ctx.nip));
+ } else {
+ ctx.opcode = ldl_code(ctx.nip);
+ }
+ LOG_DISAS("translate opcode %08x (%02x %02x %02x) (%s)\n",
+ ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), little_endian ? "little" : "big");
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(ctx.nip);
+ ctx.nip += 4;
+ table = env->opcodes;
+ num_insns++;
+ handler = table[opc1(ctx.opcode)];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ handler = table[opc2(ctx.opcode)];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ handler = table[opc3(ctx.opcode)];
+ }
+ }
+ /* Is opcode *REALLY* valid ? */
+ if (unlikely(handler->handler == &gen_invalid)) {
+ if (qemu_log_enabled()) {
+ qemu_log("invalid/unsupported opcode: "
+ "%02x - %02x - %02x (%08x) " TARGET_FMT_lx " %d\n",
+ opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, (int)msr_ir);
+ }
+ } else {
+ if (unlikely((ctx.opcode & handler->inval) != 0)) {
+ if (qemu_log_enabled()) {
+ qemu_log("invalid bits: %08x for opcode: "
+ "%02x - %02x - %02x (%08x) " TARGET_FMT_lx "\n",
+ ctx.opcode & handler->inval, opc1(ctx.opcode),
+ opc2(ctx.opcode), opc3(ctx.opcode),
+ ctx.opcode, ctx.nip - 4);
+ }
+ gen_inval_exception(ctxp, POWERPC_EXCP_INVAL_INVAL);
+ break;
+ }
+ }
+ (*(handler->handler))(&ctx);
+#if defined(DO_PPC_STATISTICS)
+ handler->count++;
+#endif
+ /* Check trace mode exceptions */
+ if (unlikely(ctx.singlestep_enabled & CPU_SINGLE_STEP &&
+ (ctx.nip <= 0x100 || ctx.nip > 0xF00) &&
+ ctx.exception != POWERPC_SYSCALL &&
+ ctx.exception != POWERPC_EXCP_TRAP &&
+ ctx.exception != POWERPC_EXCP_BRANCH)) {
+ gen_exception(ctxp, POWERPC_EXCP_TRACE);
+ } else if (unlikely(((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) ||
+ (env->singlestep_enabled) ||
+ singlestep ||
+ num_insns >= max_insns)) {
+ /* if we reach a page boundary or are single stepping, stop
+ * generation
+ */
+ break;
+ }
+ }
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ if (ctx.exception == POWERPC_EXCP_NONE) {
+ gen_goto_tb(&ctx, 0, ctx.nip);
+ } else if (ctx.exception != POWERPC_EXCP_BRANCH) {
+ if (unlikely(env->singlestep_enabled)) {
+ gen_debug_exception(ctxp);
+ }
+ /* Generate the return instruction */
+ tcg_gen_exit_tb(0);
+ }
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+ if (unlikely(search_pc)) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+ } else {
+ tb->size = ctx.nip - pc_start;
+ tb->icount = num_insns;
+ }
+#if defined(DEBUG_DISAS)
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ int flags;
+ flags = env->bfd_mach;
+ flags |= ctx.le_mode << 16;
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, ctx.nip - pc_start, flags);
+ 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 gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->nip = gen_opc_pc[pc_pos];
+}
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
new file mode 100644
index 0000000..dfcd949
--- /dev/null
+++ b/target-ppc/translate_init.c
@@ -0,0 +1,9768 @@
+/*
+ * PowerPC CPU initialization for qemu.
+ *
+ * Copyright (c) 2003-2007 Jocelyn Mayer
+ *
+ * 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/>.
+ */
+
+/* A lot of PowerPC definition have been included here.
+ * Most of them are not usable for now but have been kept
+ * inside "#if defined(TODO) ... #endif" statements to make tests easier.
+ */
+
+#include "dis-asm.h"
+#include "gdbstub.h"
+
+//#define PPC_DUMP_CPU
+//#define PPC_DEBUG_SPR
+//#define PPC_DUMP_SPR_ACCESSES
+#if defined(CONFIG_USER_ONLY)
+#define TODO_USER_ONLY 1
+#endif
+
+struct ppc_def_t {
+ const char *name;
+ uint32_t pvr;
+ uint32_t svr;
+ uint64_t insns_flags;
+ uint64_t msr_mask;
+ powerpc_mmu_t mmu_model;
+ powerpc_excp_t excp_model;
+ powerpc_input_t bus_model;
+ uint32_t flags;
+ int bfd_mach;
+ void (*init_proc)(CPUPPCState *env);
+ int (*check_pow)(CPUPPCState *env);
+};
+
+/* For user-mode emulation, we don't emulate any IRQ controller */
+#if defined(CONFIG_USER_ONLY)
+#define PPC_IRQ_INIT_FN(name) \
+static inline void glue(glue(ppc, name),_irq_init) (CPUPPCState *env) \
+{ \
+}
+#else
+#define PPC_IRQ_INIT_FN(name) \
+void glue(glue(ppc, name),_irq_init) (CPUPPCState *env);
+#endif
+
+PPC_IRQ_INIT_FN(40x);
+PPC_IRQ_INIT_FN(6xx);
+PPC_IRQ_INIT_FN(970);
+PPC_IRQ_INIT_FN(e500);
+
+/* Generic callbacks:
+ * do nothing but store/retrieve spr value
+ */
+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);
+ gen_helper_load_dump_spr(t0);
+ tcg_temp_free_i32(t0);
+ }
+#endif
+}
+
+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);
+ gen_helper_store_dump_spr(t0);
+ tcg_temp_free_i32(t0);
+ }
+#endif
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_clear (void *opaque, int sprn, int gprn)
+{
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+ gen_load_spr(t0, sprn);
+ tcg_gen_neg_tl(t1, cpu_gpr[gprn]);
+ tcg_gen_and_tl(t0, t0, t1);
+ gen_store_spr(sprn, t0);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+#endif
+
+/* SPR common to all PowerPC */
+/* XER */
+static void spr_read_xer (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_mov_tl(cpu_gpr[gprn], cpu_xer);
+}
+
+static void spr_write_xer (void *opaque, int sprn, int gprn)
+{
+ tcg_gen_mov_tl(cpu_xer, cpu_gpr[gprn]);
+}
+
+/* LR */
+static void spr_read_lr (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_mov_tl(cpu_gpr[gprn], cpu_lr);
+}
+
+static void spr_write_lr (void *opaque, int sprn, int gprn)
+{
+ tcg_gen_mov_tl(cpu_lr, cpu_gpr[gprn]);
+}
+
+/* CTR */
+static void spr_read_ctr (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_mov_tl(cpu_gpr[gprn], cpu_ctr);
+}
+
+static void spr_write_ctr (void *opaque, int sprn, int gprn)
+{
+ tcg_gen_mov_tl(cpu_ctr, cpu_gpr[gprn]);
+}
+
+/* User read access to SPR */
+/* USPRx */
+/* UMMCRx */
+/* UPMCx */
+/* USIA */
+/* UDECR */
+static void spr_read_ureg (void *opaque, int gprn, int sprn)
+{
+ gen_load_spr(cpu_gpr[gprn], sprn + 0x10);
+}
+
+/* SPR common to all non-embedded PowerPC */
+/* DECR */
+#if !defined(CONFIG_USER_ONLY)
+static void spr_read_decr (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_decr(cpu_gpr[gprn]);
+}
+
+static void spr_write_decr (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_decr(cpu_gpr[gprn]);
+}
+#endif
+
+/* SPR common to all non-embedded PowerPC, except 601 */
+/* Time base */
+static void spr_read_tbl (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_tbl(cpu_gpr[gprn]);
+}
+
+static void spr_read_tbu (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_tbu(cpu_gpr[gprn]);
+}
+
+__attribute__ (( unused ))
+static void spr_read_atbl (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_atbl(cpu_gpr[gprn]);
+}
+
+__attribute__ (( unused ))
+static void spr_read_atbu (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_atbu(cpu_gpr[gprn]);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_tbl (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_tbl(cpu_gpr[gprn]);
+}
+
+static void spr_write_tbu (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_tbu(cpu_gpr[gprn]);
+}
+
+__attribute__ (( unused ))
+static void spr_write_atbl (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_atbl(cpu_gpr[gprn]);
+}
+
+__attribute__ (( unused ))
+static void spr_write_atbu (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_atbu(cpu_gpr[gprn]);
+}
+#endif
+
+#if !defined(CONFIG_USER_ONLY)
+/* IBAT0U...IBAT0U */
+/* IBAT0L...IBAT7L */
+static void spr_read_ibat (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2]));
+}
+
+static void spr_read_ibat_h (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, IBAT[sprn & 1][(sprn - SPR_IBAT4U) / 2]));
+}
+
+static void spr_write_ibatu (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2);
+ gen_helper_store_ibatu(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_ibatu_h (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4U) / 2) + 4);
+ gen_helper_store_ibatu(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_ibatl (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0L) / 2);
+ gen_helper_store_ibatl(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_ibatl_h (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_IBAT4L) / 2) + 4);
+ gen_helper_store_ibatl(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+/* DBAT0U...DBAT7U */
+/* DBAT0L...DBAT7L */
+static void spr_read_dbat (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, DBAT[sprn & 1][(sprn - SPR_DBAT0U) / 2]));
+}
+
+static void spr_read_dbat_h (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, DBAT[sprn & 1][((sprn - SPR_DBAT4U) / 2) + 4]));
+}
+
+static void spr_write_dbatu (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0U) / 2);
+ gen_helper_store_dbatu(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_dbatu_h (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4U) / 2) + 4);
+ gen_helper_store_dbatu(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_dbatl (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32((sprn - SPR_DBAT0L) / 2);
+ gen_helper_store_dbatl(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_dbatl_h (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32(((sprn - SPR_DBAT4L) / 2) + 4);
+ gen_helper_store_dbatl(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+/* SDR1 */
+static void spr_read_sdr1 (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, sdr1));
+}
+
+static void spr_write_sdr1 (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_sdr1(cpu_gpr[gprn]);
+}
+
+/* 64 bits PowerPC specific SPRs */
+/* ASR */
+#if defined(TARGET_PPC64)
+static void spr_read_hior (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, excp_prefix));
+}
+
+static void spr_write_hior (void *opaque, int sprn, int gprn)
+{
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0x3FFFFF00000ULL);
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, excp_prefix));
+ tcg_temp_free(t0);
+}
+
+static void spr_read_asr (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, asr));
+}
+
+static void spr_write_asr (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_asr(cpu_gpr[gprn]);
+}
+#endif
+#endif
+
+/* PowerPC 601 specific registers */
+/* RTC */
+static void spr_read_601_rtcl (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_601_rtcl(cpu_gpr[gprn]);
+}
+
+static void spr_read_601_rtcu (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_601_rtcu(cpu_gpr[gprn]);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void spr_write_601_rtcu (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_601_rtcu(cpu_gpr[gprn]);
+}
+
+static void spr_write_601_rtcl (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_601_rtcl(cpu_gpr[gprn]);
+}
+
+static void spr_write_hid0_601 (void *opaque, int sprn, int gprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_helper_store_hid0_601(cpu_gpr[gprn]);
+ /* Must stop the translation as endianness may have changed */
+ gen_stop_exception(ctx);
+}
+#endif
+
+/* Unified bats */
+#if !defined(CONFIG_USER_ONLY)
+static void spr_read_601_ubat (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2]));
+}
+
+static void spr_write_601_ubatu (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2);
+ gen_helper_store_601_batl(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_601_ubatl (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2);
+ gen_helper_store_601_batu(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+#endif
+
+/* PowerPC 40x specific registers */
+#if !defined(CONFIG_USER_ONLY)
+static void spr_read_40x_pit (void *opaque, int gprn, int sprn)
+{
+ gen_helper_load_40x_pit(cpu_gpr[gprn]);
+}
+
+static void spr_write_40x_pit (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_40x_pit(cpu_gpr[gprn]);
+}
+
+static void spr_write_40x_dbcr0 (void *opaque, int sprn, int gprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_helper_store_40x_dbcr0(cpu_gpr[gprn]);
+ /* We must stop translation as we may have rebooted */
+ gen_stop_exception(ctx);
+}
+
+static void spr_write_40x_sler (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_40x_sler(cpu_gpr[gprn]);
+}
+
+static void spr_write_booke_tcr (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_booke_tcr(cpu_gpr[gprn]);
+}
+
+static void spr_write_booke_tsr (void *opaque, int sprn, int gprn)
+{
+ gen_helper_store_booke_tsr(cpu_gpr[gprn]);
+}
+#endif
+
+/* PowerPC 403 specific registers */
+/* PBL1 / PBU1 / PBL2 / PBU2 */
+#if !defined(CONFIG_USER_ONLY)
+static void spr_read_403_pbr (void *opaque, int gprn, int sprn)
+{
+ tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUState, pb[sprn - SPR_403_PBL1]));
+}
+
+static void spr_write_403_pbr (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1);
+ gen_helper_store_403_pbr(t0, cpu_gpr[gprn]);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_pir (void *opaque, int sprn, int gprn)
+{
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xF);
+ gen_store_spr(SPR_PIR, t0);
+ tcg_temp_free(t0);
+}
+#endif
+
+/* SPE specific registers */
+static void spr_read_spefscr (void *opaque, int gprn, int sprn)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ tcg_gen_ld_i32(t0, cpu_env, offsetof(CPUState, spe_fscr));
+ tcg_gen_extu_i32_tl(cpu_gpr[gprn], t0);
+ tcg_temp_free_i32(t0);
+}
+
+static void spr_write_spefscr (void *opaque, int sprn, int gprn)
+{
+ TCGv_i32 t0 = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(t0, cpu_gpr[gprn]);
+ tcg_gen_st_i32(t0, cpu_env, offsetof(CPUState, spe_fscr));
+ tcg_temp_free_i32(t0);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+/* Callback used to write the exception vector base */
+static void spr_write_excp_prefix (void *opaque, int sprn, int gprn)
+{
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, ivpr_mask));
+ tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]);
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, excp_prefix));
+ gen_store_spr(sprn, t0);
+ tcg_temp_free(t0);
+}
+
+static void spr_write_excp_vector (void *opaque, int sprn, int gprn)
+{
+ DisasContext *ctx = opaque;
+
+ if (sprn >= SPR_BOOKE_IVOR0 && sprn <= SPR_BOOKE_IVOR15) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, ivor_mask));
+ tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]);
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, excp_vectors[sprn - SPR_BOOKE_IVOR0]));
+ gen_store_spr(sprn, t0);
+ tcg_temp_free(t0);
+ } else if (sprn >= SPR_BOOKE_IVOR32 && sprn <= SPR_BOOKE_IVOR37) {
+ TCGv t0 = tcg_temp_new();
+ tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, ivor_mask));
+ tcg_gen_and_tl(t0, t0, cpu_gpr[gprn]);
+ tcg_gen_st_tl(t0, cpu_env, offsetof(CPUState, excp_vectors[sprn - SPR_BOOKE_IVOR32 + 32]));
+ gen_store_spr(sprn, t0);
+ tcg_temp_free(t0);
+ } else {
+ printf("Trying to write an unknown exception vector %d %03x\n",
+ sprn, sprn);
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG);
+ }
+}
+#endif
+
+static inline void vscr_init (CPUPPCState *env, uint32_t val)
+{
+ env->vscr = val;
+ /* Altivec always uses round-to-nearest */
+ set_float_rounding_mode(float_round_nearest_even, &env->vec_status);
+ set_flush_to_zero(vscr_nj, &env->vec_status);
+}
+
+#if defined(CONFIG_USER_ONLY)
+#define spr_register(env, num, name, uea_read, uea_write, \
+ oea_read, oea_write, initial_value) \
+do { \
+ _spr_register(env, num, name, uea_read, uea_write, initial_value); \
+} while (0)
+static inline void _spr_register (CPUPPCState *env, int num,
+ const char *name,
+ void (*uea_read)(void *opaque, int gprn, int sprn),
+ void (*uea_write)(void *opaque, int sprn, int gprn),
+ target_ulong initial_value)
+#else
+static inline void spr_register (CPUPPCState *env, int num,
+ const char *name,
+ void (*uea_read)(void *opaque, int gprn, int sprn),
+ void (*uea_write)(void *opaque, int sprn, int gprn),
+ void (*oea_read)(void *opaque, int gprn, int sprn),
+ void (*oea_write)(void *opaque, int sprn, int gprn),
+ target_ulong initial_value)
+#endif
+{
+ ppc_spr_t *spr;
+
+ spr = &env->spr_cb[num];
+ if (spr->name != NULL ||env-> spr[num] != 0x00000000 ||
+#if !defined(CONFIG_USER_ONLY)
+ spr->oea_read != NULL || spr->oea_write != NULL ||
+#endif
+ spr->uea_read != NULL || spr->uea_write != NULL) {
+ printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num);
+ exit(1);
+ }
+#if defined(PPC_DEBUG_SPR)
+ printf("*** register spr %d (%03x) %s val " TARGET_FMT_lx "\n", num, num,
+ name, initial_value);
+#endif
+ spr->name = name;
+ spr->uea_read = uea_read;
+ spr->uea_write = uea_write;
+#if !defined(CONFIG_USER_ONLY)
+ spr->oea_read = oea_read;
+ spr->oea_write = oea_write;
+#endif
+ env->spr[num] = initial_value;
+}
+
+/* Generic PowerPC SPRs */
+static void gen_spr_generic (CPUPPCState *env)
+{
+ /* Integer processing */
+ spr_register(env, SPR_XER, "XER",
+ &spr_read_xer, &spr_write_xer,
+ &spr_read_xer, &spr_write_xer,
+ 0x00000000);
+ /* Branch contol */
+ spr_register(env, SPR_LR, "LR",
+ &spr_read_lr, &spr_write_lr,
+ &spr_read_lr, &spr_write_lr,
+ 0x00000000);
+ spr_register(env, SPR_CTR, "CTR",
+ &spr_read_ctr, &spr_write_ctr,
+ &spr_read_ctr, &spr_write_ctr,
+ 0x00000000);
+ /* Interrupt processing */
+ spr_register(env, SPR_SRR0, "SRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SRR1, "SRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Processor control */
+ spr_register(env, SPR_SPRG0, "SPRG0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG1, "SPRG1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG2, "SPRG2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG3, "SPRG3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR common to all non-embedded PowerPC, including 601 */
+static void gen_spr_ne_601 (CPUPPCState *env)
+{
+ /* Exception processing */
+ spr_register(env, SPR_DSISR, "DSISR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_DAR, "DAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Timer */
+ spr_register(env, SPR_DECR, "DECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_decr, &spr_write_decr,
+ 0x00000000);
+ /* Memory management */
+ spr_register(env, SPR_SDR1, "SDR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_sdr1, &spr_write_sdr1,
+ 0x00000000);
+}
+
+/* BATs 0-3 */
+static void gen_low_BATs (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ spr_register(env, SPR_IBAT0U, "IBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT0L, "IBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1U, "IBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1L, "IBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2U, "IBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2L, "IBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3U, "IBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3L, "IBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT0U, "DBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT0L, "DBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT1U, "DBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT1L, "DBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT2U, "DBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT2L, "DBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT3U, "DBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT3L, "DBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ env->nb_BATs += 4;
+#endif
+}
+
+/* BATs 4-7 */
+static void gen_high_BATs (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ spr_register(env, SPR_IBAT4U, "IBAT4U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT4L, "IBAT4L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT5U, "IBAT5U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT5L, "IBAT5L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT6U, "IBAT6U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT6L, "IBAT6L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT7U, "IBAT7U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT7L, "IBAT7L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT4U, "DBAT4U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT4L, "DBAT4L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT5U, "DBAT5U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT5L, "DBAT5L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT6U, "DBAT6U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT6L, "DBAT6L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT7U, "DBAT7U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT7L, "DBAT7L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ env->nb_BATs += 4;
+#endif
+}
+
+/* Generic PowerPC time base */
+static void gen_tbl (CPUPPCState *env)
+{
+ spr_register(env, SPR_VTBL, "TBL",
+ &spr_read_tbl, SPR_NOACCESS,
+ &spr_read_tbl, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_TBL, "TBL",
+ &spr_read_tbl, SPR_NOACCESS,
+ &spr_read_tbl, &spr_write_tbl,
+ 0x00000000);
+ spr_register(env, SPR_VTBU, "TBU",
+ &spr_read_tbu, SPR_NOACCESS,
+ &spr_read_tbu, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_TBU, "TBU",
+ &spr_read_tbu, SPR_NOACCESS,
+ &spr_read_tbu, &spr_write_tbu,
+ 0x00000000);
+}
+
+/* Softare table search registers */
+static void gen_6xx_7xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = nb_tlbs;
+ env->nb_ways = nb_ways;
+ env->id_tlbs = 1;
+ spr_register(env, SPR_DMISS, "DMISS",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_DCMP, "DCMP",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_HASH1, "HASH1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_HASH2, "HASH2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_IMISS, "IMISS",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_ICMP, "ICMP",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_RPA, "RPA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#endif
+}
+
+/* SPR common to MPC755 and G2 */
+static void gen_spr_G2_755 (CPUPPCState *env)
+{
+ /* SGPRs */
+ spr_register(env, SPR_SPRG4, "SPRG4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG5, "SPRG5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG6, "SPRG6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG7, "SPRG7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR common to all 7xx PowerPC implementations */
+static void gen_spr_7xx (CPUPPCState *env)
+{
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Cache management */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTC, "ICTC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Performance monitors */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR0, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR1, "MMCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC1, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC2, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC3, "PMC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC4, "PMC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIAR, "SIAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UMMCR0, "UMMCR0",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UMMCR1, "UMMCR1",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC1, "UPMC1",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC2, "UPMC2",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC3, "UPMC3",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC4, "UPMC4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_USIAR, "USIAR",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+static void gen_spr_thrm (CPUPPCState *env)
+{
+ /* Thermal management */
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM1, "THRM1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM2, "THRM2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM3, "THRM3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 604 implementation */
+static void gen_spr_604 (CPUPPCState *env)
+{
+ /* Processor identification */
+ spr_register(env, SPR_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Performance counters */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR0, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC1, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC2, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIAR, "SIAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SDA, "SDA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 603 implementation */
+static void gen_spr_603 (CPUPPCState *env)
+{
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC G2 implementation */
+static void gen_spr_G2 (CPUPPCState *env)
+{
+ /* Memory base address */
+ /* MBAR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MBAR, "MBAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Exception processing */
+ spr_register(env, SPR_BOOKE_CSRR0, "CSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_CSRR1, "CSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR2, "DABR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR2, "IABR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IBCR, "IBCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DBCR, "DBCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 602 implementation */
+static void gen_spr_602 (CPUPPCState *env)
+{
+ /* ESA registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_SER, "SER",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SEBR, "SEBR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_ESASRR, "ESASRR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Floating point status */
+ /* XXX : not implemented */
+ spr_register(env, SPR_SP, "SP",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_LT, "LT",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Watchdog timer */
+ /* XXX : not implemented */
+ spr_register(env, SPR_TCR, "TCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Interrupt base */
+ spr_register(env, SPR_IBR, "IBR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 601 implementation */
+static void gen_spr_601 (CPUPPCState *env)
+{
+ /* Multiplication/division register */
+ /* MQ */
+ spr_register(env, SPR_MQ, "MQ",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* RTC registers */
+ spr_register(env, SPR_601_RTCU, "RTCU",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_601_rtcu,
+ 0x00000000);
+ spr_register(env, SPR_601_VRTCU, "RTCU",
+ &spr_read_601_rtcu, SPR_NOACCESS,
+ &spr_read_601_rtcu, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_601_RTCL, "RTCL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_601_rtcl,
+ 0x00000000);
+ spr_register(env, SPR_601_VRTCL, "RTCL",
+ &spr_read_601_rtcl, SPR_NOACCESS,
+ &spr_read_601_rtcl, SPR_NOACCESS,
+ 0x00000000);
+ /* Timer */
+#if 0 /* ? */
+ spr_register(env, SPR_601_UDECR, "UDECR",
+ &spr_read_decr, SPR_NOACCESS,
+ &spr_read_decr, SPR_NOACCESS,
+ 0x00000000);
+#endif
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ spr_register(env, SPR_IBAT0U, "IBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT0L, "IBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1U, "IBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1L, "IBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2U, "IBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2L, "IBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3U, "IBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3L, "IBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_601_ubat, &spr_write_601_ubatl,
+ 0x00000000);
+ env->nb_BATs = 4;
+#endif
+}
+
+static void gen_spr_74xx (CPUPPCState *env)
+{
+ /* Processor identification */
+ spr_register(env, SPR_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR2, "MMCR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UMMCR2, "UMMCR2",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX: not implemented */
+ spr_register(env, SPR_BAMR, "BAMR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSCR0, "MSSCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Altivec */
+ spr_register(env, SPR_VRSAVE, "VRSAVE",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Not strictly an SPR */
+ vscr_init(env, 0x00010000);
+}
+
+static void gen_l3_ctrl (CPUPPCState *env)
+{
+ /* L3CR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3CR, "L3CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3ITCR0 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR0, "L3ITCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3PM */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3PM, "L3PM",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+static void gen_74xx_soft_tlb (CPUPPCState *env, int nb_tlbs, int nb_ways)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = nb_tlbs;
+ env->nb_ways = nb_ways;
+ env->id_tlbs = 1;
+ /* XXX : not implemented */
+ spr_register(env, SPR_PTEHI, "PTEHI",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PTELO, "PTELO",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_TLBMISS, "TLBMISS",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#endif
+}
+
+static void gen_spr_usprgh (CPUPPCState *env)
+{
+ spr_register(env, SPR_USPRG4, "USPRG4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_USPRG5, "USPRG5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_USPRG6, "USPRG6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_USPRG7, "USPRG7",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+}
+
+/* PowerPC BookE SPR */
+static void gen_spr_BookE (CPUPPCState *env, uint64_t ivor_mask)
+{
+ const char *ivor_names[64] = {
+ "IVOR0", "IVOR1", "IVOR2", "IVOR3",
+ "IVOR4", "IVOR5", "IVOR6", "IVOR7",
+ "IVOR8", "IVOR9", "IVOR10", "IVOR11",
+ "IVOR12", "IVOR13", "IVOR14", "IVOR15",
+ "IVOR16", "IVOR17", "IVOR18", "IVOR19",
+ "IVOR20", "IVOR21", "IVOR22", "IVOR23",
+ "IVOR24", "IVOR25", "IVOR26", "IVOR27",
+ "IVOR28", "IVOR29", "IVOR30", "IVOR31",
+ "IVOR32", "IVOR33", "IVOR34", "IVOR35",
+ "IVOR36", "IVOR37", "IVOR38", "IVOR39",
+ "IVOR40", "IVOR41", "IVOR42", "IVOR43",
+ "IVOR44", "IVOR45", "IVOR46", "IVOR47",
+ "IVOR48", "IVOR49", "IVOR50", "IVOR51",
+ "IVOR52", "IVOR53", "IVOR54", "IVOR55",
+ "IVOR56", "IVOR57", "IVOR58", "IVOR59",
+ "IVOR60", "IVOR61", "IVOR62", "IVOR63",
+ };
+#define SPR_BOOKE_IVORxx (-1)
+ int ivor_sprn[64] = {
+ SPR_BOOKE_IVOR0, SPR_BOOKE_IVOR1, SPR_BOOKE_IVOR2, SPR_BOOKE_IVOR3,
+ SPR_BOOKE_IVOR4, SPR_BOOKE_IVOR5, SPR_BOOKE_IVOR6, SPR_BOOKE_IVOR7,
+ SPR_BOOKE_IVOR8, SPR_BOOKE_IVOR9, SPR_BOOKE_IVOR10, SPR_BOOKE_IVOR11,
+ SPR_BOOKE_IVOR12, SPR_BOOKE_IVOR13, SPR_BOOKE_IVOR14, SPR_BOOKE_IVOR15,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVOR32, SPR_BOOKE_IVOR33, SPR_BOOKE_IVOR34, SPR_BOOKE_IVOR35,
+ SPR_BOOKE_IVOR36, SPR_BOOKE_IVOR37, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx, SPR_BOOKE_IVORxx,
+ };
+ int i;
+
+ /* Interrupt processing */
+ spr_register(env, SPR_BOOKE_CSRR0, "CSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_CSRR1, "CSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Debug */
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC1, "IAC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC2, "IAC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DAC1, "DAC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DAC2, "DAC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DBCR0, "DBCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DBCR1, "DBCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DBCR2, "DBCR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DBSR, "DBSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_DEAR, "DEAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_ESR, "ESR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_IVPR, "IVPR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_excp_prefix,
+ 0x00000000);
+ /* Exception vectors */
+ for (i = 0; i < 64; i++) {
+ if (ivor_mask & (1ULL << i)) {
+ if (ivor_sprn[i] == SPR_BOOKE_IVORxx) {
+ fprintf(stderr, "ERROR: IVOR %d SPR is not defined\n", i);
+ exit(1);
+ }
+ spr_register(env, ivor_sprn[i], ivor_names[i],
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_excp_vector,
+ 0x00000000);
+ }
+ }
+ spr_register(env, SPR_BOOKE_PID, "PID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_TCR, "TCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_booke_tcr,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_TSR, "TSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_booke_tsr,
+ 0x00000000);
+ /* Timer */
+ spr_register(env, SPR_DECR, "DECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_decr, &spr_write_decr,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_DECAR, "DECAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_generic,
+ 0x00000000);
+ /* SPRGs */
+ spr_register(env, SPR_USPRG0, "USPRG0",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG4, "SPRG4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG5, "SPRG5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG6, "SPRG6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG7, "SPRG7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* FSL storage control registers */
+static void gen_spr_BookE_FSL (CPUPPCState *env, uint32_t mas_mask)
+{
+#if !defined(CONFIG_USER_ONLY)
+ const char *mas_names[8] = {
+ "MAS0", "MAS1", "MAS2", "MAS3", "MAS4", "MAS5", "MAS6", "MAS7",
+ };
+ int mas_sprn[8] = {
+ SPR_BOOKE_MAS0, SPR_BOOKE_MAS1, SPR_BOOKE_MAS2, SPR_BOOKE_MAS3,
+ SPR_BOOKE_MAS4, SPR_BOOKE_MAS5, SPR_BOOKE_MAS6, SPR_BOOKE_MAS7,
+ };
+ int i;
+
+ /* TLB assist registers */
+ /* XXX : not implemented */
+ for (i = 0; i < 8; i++) {
+ if (mas_mask & (1 << i)) {
+ spr_register(env, mas_sprn[i], mas_names[i],
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ }
+ }
+ if (env->nb_pids > 1) {
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_PID1, "PID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ }
+ if (env->nb_pids > 2) {
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_PID2, "PID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ }
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCFG, "MMUCFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000); /* TOFIX */
+ switch (env->nb_ways) {
+ case 4:
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB3CFG, "TLB3CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* Fallthru */
+ case 3:
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB2CFG, "TLB2CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* Fallthru */
+ case 2:
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* Fallthru */
+ case 1:
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* Fallthru */
+ case 0:
+ default:
+ break;
+ }
+#endif
+}
+
+/* SPR specific to PowerPC 440 implementation */
+static void gen_spr_440 (CPUPPCState *env)
+{
+ /* Cache control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DNV0, "DNV0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DNV1, "DNV1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DNV2, "DNV2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DNV3, "DNV3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DTV0, "DTV0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DTV1, "DTV1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DTV2, "DTV2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DTV3, "DTV3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DVLIM, "DVLIM",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_INV0, "INV0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_INV1, "INV1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_INV2, "INV2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_INV3, "INV3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_ITV0, "ITV0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_ITV1, "ITV1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_ITV2, "ITV2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_ITV3, "ITV3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_IVLIM, "IVLIM",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Cache debug */
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DCDBTRH, "DCDBTRH",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DCDBTRL, "DCDBTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_ICDBDR, "ICDBDR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_ICDBTRH, "ICDBTRH",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_ICDBTRL, "ICDBTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_DBDR, "DBDR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Processor control */
+ spr_register(env, SPR_4xx_CCR0, "CCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_440_RSTCFG, "RSTCFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* Storage control */
+ spr_register(env, SPR_440_MMUCR, "MMUCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR shared between PowerPC 40x implementations */
+static void gen_spr_40x (CPUPPCState *env)
+{
+ /* Cache */
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_40x_DCCR, "DCCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_40x_ICCR, "ICCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_BOOKE_ICDBDR, "ICDBDR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* Exception */
+ spr_register(env, SPR_40x_DEAR, "DEAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_ESR, "ESR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_EVPR, "EVPR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_excp_prefix,
+ 0x00000000);
+ spr_register(env, SPR_40x_SRR2, "SRR2",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_SRR3, "SRR3",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Timers */
+ spr_register(env, SPR_40x_PIT, "PIT",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_40x_pit, &spr_write_40x_pit,
+ 0x00000000);
+ spr_register(env, SPR_40x_TCR, "TCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_booke_tcr,
+ 0x00000000);
+ spr_register(env, SPR_40x_TSR, "TSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_booke_tsr,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 405 implementation */
+static void gen_spr_405 (CPUPPCState *env)
+{
+ /* MMU */
+ spr_register(env, SPR_40x_PID, "PID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_4xx_CCR0, "CCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00700000);
+ /* Debug interface */
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DBCR0, "DBCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_40x_dbcr0,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_405_DBCR1, "DBCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DBSR, "DBSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ /* Last reset was system reset */
+ 0x00000300);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DAC1, "DAC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_DAC2, "DAC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_405_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_405_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_IAC1, "IAC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_IAC2, "IAC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_405_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_405_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Storage control */
+ /* XXX: TODO: not implemented */
+ spr_register(env, SPR_405_SLER, "SLER",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_40x_sler,
+ 0x00000000);
+ spr_register(env, SPR_40x_ZPR, "ZPR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_405_SU0R, "SU0R",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* SPRG */
+ spr_register(env, SPR_USPRG0, "USPRG0",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG4, "SPRG4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG5, "SPRG5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG6, "SPRG6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG7, "SPRG7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ gen_spr_usprgh(env);
+}
+
+/* SPR shared between PowerPC 401 & 403 implementations */
+static void gen_spr_401_403 (CPUPPCState *env)
+{
+ /* Time base */
+ spr_register(env, SPR_403_VTBL, "TBL",
+ &spr_read_tbl, SPR_NOACCESS,
+ &spr_read_tbl, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_403_TBL, "TBL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_tbl,
+ 0x00000000);
+ spr_register(env, SPR_403_VTBU, "TBU",
+ &spr_read_tbu, SPR_NOACCESS,
+ &spr_read_tbu, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_403_TBU, "TBU",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_tbu,
+ 0x00000000);
+ /* Debug */
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_403_CDBCR, "CDBCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 401 implementation */
+static void gen_spr_401 (CPUPPCState *env)
+{
+ /* Debug interface */
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DBCR0, "DBCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_40x_dbcr0,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DBSR, "DBSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ /* Last reset was system reset */
+ 0x00000300);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DAC1, "DAC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_IAC1, "IAC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Storage control */
+ /* XXX: TODO: not implemented */
+ spr_register(env, SPR_405_SLER, "SLER",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_40x_sler,
+ 0x00000000);
+ /* not emulated, as Qemu never does speculative access */
+ spr_register(env, SPR_40x_SGR, "SGR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0xFFFFFFFF);
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_40x_DCWR, "DCWR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+static void gen_spr_401x2 (CPUPPCState *env)
+{
+ gen_spr_401(env);
+ spr_register(env, SPR_40x_PID, "PID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_ZPR, "ZPR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 403 implementation */
+static void gen_spr_403 (CPUPPCState *env)
+{
+ /* Debug interface */
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DBCR0, "DBCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_40x_dbcr0,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DBSR, "DBSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ /* Last reset was system reset */
+ 0x00000300);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DAC1, "DAC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_DAC2, "DAC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_IAC1, "IAC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_40x_IAC2, "IAC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+static void gen_spr_403_real (CPUPPCState *env)
+{
+ spr_register(env, SPR_403_PBL1, "PBL1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_403_pbr, &spr_write_403_pbr,
+ 0x00000000);
+ spr_register(env, SPR_403_PBU1, "PBU1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_403_pbr, &spr_write_403_pbr,
+ 0x00000000);
+ spr_register(env, SPR_403_PBL2, "PBL2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_403_pbr, &spr_write_403_pbr,
+ 0x00000000);
+ spr_register(env, SPR_403_PBU2, "PBU2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_403_pbr, &spr_write_403_pbr,
+ 0x00000000);
+}
+
+static void gen_spr_403_mmu (CPUPPCState *env)
+{
+ /* MMU */
+ spr_register(env, SPR_40x_PID, "PID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_40x_ZPR, "ZPR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC compression coprocessor extension */
+static void gen_spr_compress (CPUPPCState *env)
+{
+ /* XXX : not implemented */
+ spr_register(env, SPR_401_SKR, "SKR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+#if defined (TARGET_PPC64)
+/* SPR specific to PowerPC 620 */
+static void gen_spr_620 (CPUPPCState *env)
+{
+ /* Processor identification */
+ spr_register(env, SPR_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ spr_register(env, SPR_ASR, "ASR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_asr, &spr_write_asr,
+ 0x00000000);
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIAR, "SIAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SDA, "SDA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMC1R, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_620_PMC1W, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMC2R, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_620_PMC2W, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_MMCR0R, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_620_MMCR0W, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_generic,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#if 0 // XXX: check this
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR0, "PMR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR1, "PMR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR2, "PMR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR3, "PMR3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR4, "PMR4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR5, "PMR5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR6, "PMR6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR7, "PMR7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR8, "PMR8",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMR9, "PMR9",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMRA, "PMR10",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMRB, "PMR11",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMRC, "PMR12",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMRD, "PMR13",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMRE, "PMR14",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_PMRF, "PMR15",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#endif
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_BUSCSR, "BUSCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_620_L2SR, "L2SR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+#endif /* defined (TARGET_PPC64) */
+
+static void gen_spr_5xx_8xx (CPUPPCState *env)
+{
+ /* Exception processing */
+ spr_register(env, SPR_DSISR, "DSISR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_DAR, "DAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Timer */
+ spr_register(env, SPR_DECR, "DECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_decr, &spr_write_decr,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_EIE, "EIE",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_EID, "EID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_NRI, "NRI",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPA, "CMPA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPB, "CMPB",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPC, "CMPC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPD, "CMPD",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_ECR, "ECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_DER, "DER",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_COUNTA, "COUNTA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_COUNTB, "COUNTB",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPE, "CMPE",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPF, "CMPF",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPG, "CMPG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_CMPH, "CMPH",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_LCTRL1, "LCTRL1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_LCTRL2, "LCTRL2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_BAR, "BAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_DPDR, "DPDR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_IMMR, "IMMR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+static void gen_spr_5xx (CPUPPCState *env)
+{
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_GRA, "MI_GRA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_GRA, "L2U_GRA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RPCU_BBCMCR, "L2U_BBCMCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_MCR, "L2U_MCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RBA0, "MI_RBA0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RBA1, "MI_RBA1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RBA2, "MI_RBA2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RBA3, "MI_RBA3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RBA0, "L2U_RBA0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RBA1, "L2U_RBA1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RBA2, "L2U_RBA2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RBA3, "L2U_RBA3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RA0, "MI_RA0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RA1, "MI_RA1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RA2, "MI_RA2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_MI_RA3, "MI_RA3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RA0, "L2U_RA0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RA1, "L2U_RA1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RA2, "L2U_RA2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_L2U_RA3, "L2U_RA3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_RCPU_FPECR, "FPECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+static void gen_spr_8xx (CPUPPCState *env)
+{
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_IC_CST, "IC_CST",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_IC_ADR, "IC_ADR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_IC_DAT, "IC_DAT",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_DC_CST, "DC_CST",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_DC_ADR, "DC_ADR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_DC_DAT, "DC_DAT",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_CTR, "MI_CTR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_AP, "MI_AP",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_EPN, "MI_EPN",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_TWC, "MI_TWC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_RPN, "MI_RPN",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_DBCAM, "MI_DBCAM",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_DBRAM0, "MI_DBRAM0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MI_DBRAM1, "MI_DBRAM1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_CTR, "MD_CTR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_CASID, "MD_CASID",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_AP, "MD_AP",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_EPN, "MD_EPN",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_TWB, "MD_TWB",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_TWC, "MD_TWC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_RPN, "MD_RPN",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_TW, "MD_TW",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_DBCAM, "MD_DBCAM",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_DBRAM0, "MD_DBRAM0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MPC_MD_DBRAM1, "MD_DBRAM1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+// XXX: TODO
+/*
+ * AMR => SPR 29 (Power 2.04)
+ * CTRL => SPR 136 (Power 2.04)
+ * CTRL => SPR 152 (Power 2.04)
+ * SCOMC => SPR 276 (64 bits ?)
+ * SCOMD => SPR 277 (64 bits ?)
+ * TBU40 => SPR 286 (Power 2.04 hypv)
+ * HSPRG0 => SPR 304 (Power 2.04 hypv)
+ * HSPRG1 => SPR 305 (Power 2.04 hypv)
+ * HDSISR => SPR 306 (Power 2.04 hypv)
+ * HDAR => SPR 307 (Power 2.04 hypv)
+ * PURR => SPR 309 (Power 2.04 hypv)
+ * HDEC => SPR 310 (Power 2.04 hypv)
+ * HIOR => SPR 311 (hypv)
+ * RMOR => SPR 312 (970)
+ * HRMOR => SPR 313 (Power 2.04 hypv)
+ * HSRR0 => SPR 314 (Power 2.04 hypv)
+ * HSRR1 => SPR 315 (Power 2.04 hypv)
+ * LPCR => SPR 316 (970)
+ * LPIDR => SPR 317 (970)
+ * EPR => SPR 702 (Power 2.04 emb)
+ * perf => 768-783 (Power 2.04)
+ * perf => 784-799 (Power 2.04)
+ * PPR => SPR 896 (Power 2.04)
+ * EPLC => SPR 947 (Power 2.04 emb)
+ * EPSC => SPR 948 (Power 2.04 emb)
+ * DABRX => 1015 (Power 2.04 hypv)
+ * FPECR => SPR 1022 (?)
+ * ... and more (thermal management, performance counters, ...)
+ */
+
+/*****************************************************************************/
+/* Exception vectors models */
+static void init_excp_4xx_real (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_PIT] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_FIT] = 0x00001010;
+ env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001020;
+ env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00002000;
+ env->hreset_excp_prefix = 0x00000000UL;
+ env->ivor_mask = 0x0000FFF0UL;
+ env->ivpr_mask = 0xFFFF0000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_4xx_softmmu (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_PIT] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_FIT] = 0x00001010;
+ env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001020;
+ env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00002000;
+ env->hreset_excp_prefix = 0x00000000UL;
+ env->ivor_mask = 0x0000FFF0UL;
+ env->ivpr_mask = 0xFFFF0000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_MPC5xx (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_FPA] = 0x00000E00;
+ env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_DABR] = 0x00001C00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001C00;
+ env->excp_vectors[POWERPC_EXCP_MEXTBR] = 0x00001E00;
+ env->excp_vectors[POWERPC_EXCP_NMEXTBR] = 0x00001F00;
+ env->hreset_excp_prefix = 0x00000000UL;
+ env->ivor_mask = 0x0000FFF0UL;
+ env->ivpr_mask = 0xFFFF0000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_MPC8xx (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_FPA] = 0x00000E00;
+ env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_ITLBE] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_DTLBE] = 0x00001400;
+ env->excp_vectors[POWERPC_EXCP_DABR] = 0x00001C00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001C00;
+ env->excp_vectors[POWERPC_EXCP_MEXTBR] = 0x00001E00;
+ env->excp_vectors[POWERPC_EXCP_NMEXTBR] = 0x00001F00;
+ env->hreset_excp_prefix = 0x00000000UL;
+ env->ivor_mask = 0x0000FFF0UL;
+ env->ivpr_mask = 0xFFFF0000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_G2 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000A00;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_e200 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000FFC;
+ env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_APU] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_FIT] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_WDT] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_SPEU] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_EFPDI] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_EFPRI] = 0x00000000;
+ env->hreset_excp_prefix = 0x00000000UL;
+ env->ivor_mask = 0x0000FFF7UL;
+ env->ivpr_mask = 0xFFFF0000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_BookE (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_CRITICAL] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_APU] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_FIT] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_WDT] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DTLB] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_ITLB] = 0x00000000;
+ env->excp_vectors[POWERPC_EXCP_DEBUG] = 0x00000000;
+ env->hreset_excp_prefix = 0x00000000UL;
+ env->ivor_mask = 0x0000FFE0UL;
+ env->ivpr_mask = 0xFFFF0000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_601 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_IO] = 0x00000A00;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_RUNM] = 0x00002000;
+ env->hreset_excp_prefix = 0xFFF00000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0x00000100UL;
+#endif
+}
+
+static void init_excp_602 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ /* XXX: exception prefix has a special behavior on 602 */
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001500;
+ env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001600;
+ env->hreset_excp_prefix = 0xFFF00000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_603 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_604 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->hreset_excp_prefix = 0xFFF00000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0x00000100UL;
+#endif
+}
+
+#if defined(TARGET_PPC64)
+static void init_excp_620 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->hreset_excp_prefix = 0xFFF00000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0x0000000000000100ULL;
+#endif
+}
+#endif /* defined(TARGET_PPC64) */
+
+static void init_excp_7x0 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_750cl (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_750cx (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+/* XXX: Check if this is correct */
+static void init_excp_7x5 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_7400 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001600;
+ env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001700;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+static void init_excp_7450 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20;
+ env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
+ env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
+ env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
+ env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001600;
+ env->hreset_excp_prefix = 0x00000000UL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0xFFFFFFFCUL;
+#endif
+}
+
+#if defined (TARGET_PPC64)
+static void init_excp_970 (CPUPPCState *env)
+{
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
+ env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
+ env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
+ env->excp_vectors[POWERPC_EXCP_DSEG] = 0x00000380;
+ env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
+ env->excp_vectors[POWERPC_EXCP_ISEG] = 0x00000480;
+ env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
+ env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
+ env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
+ env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
+ env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
+ env->excp_vectors[POWERPC_EXCP_HDECR] = 0x00000980;
+ env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
+ env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
+ env->excp_vectors[POWERPC_EXCP_PERFM] = 0x00000F00;
+ env->excp_vectors[POWERPC_EXCP_VPU] = 0x00000F20;
+ env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
+ env->excp_vectors[POWERPC_EXCP_MAINT] = 0x00001600;
+ env->excp_vectors[POWERPC_EXCP_VPUA] = 0x00001700;
+ env->excp_vectors[POWERPC_EXCP_THERM] = 0x00001800;
+ env->hreset_excp_prefix = 0x00000000FFF00000ULL;
+ /* Hardware reset vector */
+ env->hreset_vector = 0x0000000000000100ULL;
+#endif
+}
+#endif
+
+/*****************************************************************************/
+/* Power management enable checks */
+static int check_pow_none (CPUPPCState *env)
+{
+ return 0;
+}
+
+static int check_pow_nocheck (CPUPPCState *env)
+{
+ return 1;
+}
+
+static int check_pow_hid0 (CPUPPCState *env)
+{
+ if (env->spr[SPR_HID0] & 0x00E00000)
+ return 1;
+
+ return 0;
+}
+
+static int check_pow_hid0_74xx (CPUPPCState *env)
+{
+ if (env->spr[SPR_HID0] & 0x00600000)
+ return 1;
+
+ return 0;
+}
+
+/*****************************************************************************/
+/* PowerPC implementations definitions */
+
+/* PowerPC 401 */
+#define POWERPC_INSNS_401 (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_WRTEE | PPC_DCR | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_4xx_COMMON | PPC_40x_EXCP)
+#define POWERPC_MSRM_401 (0x00000000000FD201ULL)
+#define POWERPC_MMU_401 (POWERPC_MMU_REAL)
+#define POWERPC_EXCP_401 (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_401 (PPC_FLAGS_INPUT_401)
+#define POWERPC_BFDM_401 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_401 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_401 check_pow_nocheck
+
+static void init_proc_401 (CPUPPCState *env)
+{
+ gen_spr_40x(env);
+ gen_spr_401_403(env);
+ gen_spr_401(env);
+ init_excp_4xx_real(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 401x2 */
+#define POWERPC_INSNS_401x2 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \
+ PPC_4xx_COMMON | PPC_40x_EXCP)
+#define POWERPC_MSRM_401x2 (0x00000000001FD231ULL)
+#define POWERPC_MMU_401x2 (POWERPC_MMU_SOFT_4xx_Z)
+#define POWERPC_EXCP_401x2 (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_401x2 (PPC_FLAGS_INPUT_401)
+#define POWERPC_BFDM_401x2 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_401x2 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_401x2 check_pow_nocheck
+
+static void init_proc_401x2 (CPUPPCState *env)
+{
+ gen_spr_40x(env);
+ gen_spr_401_403(env);
+ gen_spr_401x2(env);
+ gen_spr_compress(env);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_4xx_softmmu(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 401x3 */
+#define POWERPC_INSNS_401x3 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \
+ PPC_4xx_COMMON | PPC_40x_EXCP)
+#define POWERPC_MSRM_401x3 (0x00000000001FD631ULL)
+#define POWERPC_MMU_401x3 (POWERPC_MMU_SOFT_4xx_Z)
+#define POWERPC_EXCP_401x3 (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_401x3 (PPC_FLAGS_INPUT_401)
+#define POWERPC_BFDM_401x3 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_401x3 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_401x3 check_pow_nocheck
+
+__attribute__ (( unused ))
+static void init_proc_401x3 (CPUPPCState *env)
+{
+ gen_spr_40x(env);
+ gen_spr_401_403(env);
+ gen_spr_401(env);
+ gen_spr_401x2(env);
+ gen_spr_compress(env);
+ init_excp_4xx_softmmu(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* IOP480 */
+#define POWERPC_INSNS_IOP480 (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \
+ PPC_4xx_COMMON | PPC_40x_EXCP)
+#define POWERPC_MSRM_IOP480 (0x00000000001FD231ULL)
+#define POWERPC_MMU_IOP480 (POWERPC_MMU_SOFT_4xx_Z)
+#define POWERPC_EXCP_IOP480 (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_IOP480 (PPC_FLAGS_INPUT_401)
+#define POWERPC_BFDM_IOP480 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_IOP480 (POWERPC_FLAG_CE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_IOP480 check_pow_nocheck
+
+static void init_proc_IOP480 (CPUPPCState *env)
+{
+ gen_spr_40x(env);
+ gen_spr_401_403(env);
+ gen_spr_401x2(env);
+ gen_spr_compress(env);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_4xx_softmmu(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 403 */
+#define POWERPC_INSNS_403 (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_4xx_COMMON | PPC_40x_EXCP)
+#define POWERPC_MSRM_403 (0x000000000007D00DULL)
+#define POWERPC_MMU_403 (POWERPC_MMU_REAL)
+#define POWERPC_EXCP_403 (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_403 (PPC_FLAGS_INPUT_401)
+#define POWERPC_BFDM_403 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_403 (POWERPC_FLAG_CE | POWERPC_FLAG_PX | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_403 check_pow_nocheck
+
+static void init_proc_403 (CPUPPCState *env)
+{
+ gen_spr_40x(env);
+ gen_spr_401_403(env);
+ gen_spr_403(env);
+ gen_spr_403_real(env);
+ init_excp_4xx_real(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 403 GCX */
+#define POWERPC_INSNS_403GCX (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \
+ PPC_4xx_COMMON | PPC_40x_EXCP)
+#define POWERPC_MSRM_403GCX (0x000000000007D00DULL)
+#define POWERPC_MMU_403GCX (POWERPC_MMU_SOFT_4xx_Z)
+#define POWERPC_EXCP_403GCX (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_403GCX (PPC_FLAGS_INPUT_401)
+#define POWERPC_BFDM_403GCX (bfd_mach_ppc_403)
+#define POWERPC_FLAG_403GCX (POWERPC_FLAG_CE | POWERPC_FLAG_PX | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_403GCX check_pow_nocheck
+
+static void init_proc_403GCX (CPUPPCState *env)
+{
+ gen_spr_40x(env);
+ gen_spr_401_403(env);
+ gen_spr_403(env);
+ gen_spr_403_real(env);
+ gen_spr_403_mmu(env);
+ /* Bus access control */
+ /* not emulated, as Qemu never does speculative access */
+ spr_register(env, SPR_40x_SGR, "SGR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0xFFFFFFFF);
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_40x_DCWR, "DCWR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_4xx_softmmu(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 405 */
+#define POWERPC_INSNS_405 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | \
+ PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP)
+#define POWERPC_MSRM_405 (0x000000000006E630ULL)
+#define POWERPC_MMU_405 (POWERPC_MMU_SOFT_4xx)
+#define POWERPC_EXCP_405 (POWERPC_EXCP_40x)
+#define POWERPC_INPUT_405 (PPC_FLAGS_INPUT_405)
+#define POWERPC_BFDM_405 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_405 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_405 check_pow_nocheck
+
+static void init_proc_405 (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_40x(env);
+ gen_spr_405(env);
+ /* Bus access control */
+ /* not emulated, as Qemu never does speculative access */
+ spr_register(env, SPR_40x_SGR, "SGR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0xFFFFFFFF);
+ /* not emulated, as Qemu do not emulate caches */
+ spr_register(env, SPR_40x_DCWR, "DCWR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_4xx_softmmu(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 440 EP */
+#define POWERPC_INSNS_440EP (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_WRTEE | PPC_RFMCI | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_MFTB | \
+ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define POWERPC_MSRM_440EP (0x000000000006D630ULL)
+#define POWERPC_MMU_440EP (POWERPC_MMU_BOOKE)
+#define POWERPC_EXCP_440EP (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_440EP (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_440EP (bfd_mach_ppc_403)
+#define POWERPC_FLAG_440EP (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_440EP check_pow_nocheck
+
+__attribute__ (( unused ))
+static void init_proc_440EP (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000000000FFFFULL);
+ gen_spr_440(env);
+ gen_spr_usprgh(env);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_MCSR, "MCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_CCR1, "CCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_BookE(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* PowerPC 440 GP */
+#define POWERPC_INSNS_440GP (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_TLBIVA | PPC_MFTB | \
+ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define POWERPC_MSRM_440GP (0x000000000006FF30ULL)
+#define POWERPC_MMU_440GP (POWERPC_MMU_BOOKE)
+#define POWERPC_EXCP_440GP (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_440GP (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_440GP (bfd_mach_ppc_403)
+#define POWERPC_FLAG_440GP (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_440GP check_pow_nocheck
+
+__attribute__ (( unused ))
+static void init_proc_440GP (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000000000FFFFULL);
+ gen_spr_440(env);
+ gen_spr_usprgh(env);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_BookE(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* PowerPC 440x4 */
+#define POWERPC_INSNS_440x4 (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_WRTEE | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_MFTB | \
+ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define POWERPC_MSRM_440x4 (0x000000000006FF30ULL)
+#define POWERPC_MMU_440x4 (POWERPC_MMU_BOOKE)
+#define POWERPC_EXCP_440x4 (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_440x4 (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_440x4 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_440x4 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_440x4 check_pow_nocheck
+
+__attribute__ (( unused ))
+static void init_proc_440x4 (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000000000FFFFULL);
+ gen_spr_440(env);
+ gen_spr_usprgh(env);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_BookE(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* PowerPC 440x5 */
+#define POWERPC_INSNS_440x5 (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_WRTEE | PPC_RFMCI | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_MFTB | \
+ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define POWERPC_MSRM_440x5 (0x000000000006FF30ULL)
+#define POWERPC_MMU_440x5 (POWERPC_MMU_BOOKE)
+#define POWERPC_EXCP_440x5 (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_440x5 (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_440x5 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_440x5 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_440x5 check_pow_nocheck
+
+static void init_proc_440x5 (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000000000FFFFULL);
+ gen_spr_440(env);
+ gen_spr_usprgh(env);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_MCSR, "MCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_CCR1, "CCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_BookE(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ ppc40x_irq_init(env);
+}
+
+/* PowerPC 460 (guessed) */
+#define POWERPC_INSNS_460 (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_DCR | PPC_DCRX | PPC_DCRUX | \
+ PPC_WRTEE | PPC_MFAPIDI | PPC_MFTB | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_TLBIVA | \
+ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define POWERPC_MSRM_460 (0x000000000006FF30ULL)
+#define POWERPC_MMU_460 (POWERPC_MMU_BOOKE)
+#define POWERPC_EXCP_460 (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_460 (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_460 (bfd_mach_ppc_403)
+#define POWERPC_FLAG_460 (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_460 check_pow_nocheck
+
+__attribute__ (( unused ))
+static void init_proc_460 (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000000000FFFFULL);
+ gen_spr_440(env);
+ gen_spr_usprgh(env);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_MCSR, "MCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_CCR1, "CCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DCRIPR, "SPR_DCRIPR",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_BookE(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* PowerPC 460F (guessed) */
+#define POWERPC_INSNS_460F (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | PPC_MFTB | \
+ PPC_DCR | PPC_DCRX | PPC_DCRUX | \
+ PPC_WRTEE | PPC_MFAPIDI | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_TLBIVA | \
+ PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define POWERPC_MSRM_460 (0x000000000006FF30ULL)
+#define POWERPC_MMU_460F (POWERPC_MMU_BOOKE)
+#define POWERPC_EXCP_460F (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_460F (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_460F (bfd_mach_ppc_403)
+#define POWERPC_FLAG_460F (POWERPC_FLAG_CE | POWERPC_FLAG_DWE | \
+ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_460F check_pow_nocheck
+
+__attribute__ (( unused ))
+static void init_proc_460F (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000000000FFFFULL);
+ gen_spr_440(env);
+ gen_spr_usprgh(env);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC1, "DVC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_DVC2, "DVC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_MCSR, "MCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_440_CCR1, "CCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DCRIPR, "SPR_DCRIPR",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_BookE(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* Freescale 5xx cores (aka RCPU) */
+#define POWERPC_INSNS_MPC5xx (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_MEM_EIEIO | PPC_MEM_SYNC | \
+ PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | \
+ PPC_MFTB)
+#define POWERPC_MSRM_MPC5xx (0x000000000001FF43ULL)
+#define POWERPC_MMU_MPC5xx (POWERPC_MMU_REAL)
+#define POWERPC_EXCP_MPC5xx (POWERPC_EXCP_603)
+#define POWERPC_INPUT_MPC5xx (PPC_FLAGS_INPUT_RCPU)
+#define POWERPC_BFDM_MPC5xx (bfd_mach_ppc_505)
+#define POWERPC_FLAG_MPC5xx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_MPC5xx check_pow_none
+
+__attribute__ (( unused ))
+static void init_proc_MPC5xx (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_5xx_8xx(env);
+ gen_spr_5xx(env);
+ init_excp_MPC5xx(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* Freescale 8xx cores (aka PowerQUICC) */
+#define POWERPC_INSNS_MPC8xx (PPC_INSNS_BASE | PPC_STRING | \
+ PPC_MEM_EIEIO | PPC_MEM_SYNC | \
+ PPC_CACHE_ICBI | PPC_MFTB)
+#define POWERPC_MSRM_MPC8xx (0x000000000001F673ULL)
+#define POWERPC_MMU_MPC8xx (POWERPC_MMU_MPC8xx)
+#define POWERPC_EXCP_MPC8xx (POWERPC_EXCP_603)
+#define POWERPC_INPUT_MPC8xx (PPC_FLAGS_INPUT_RCPU)
+#define POWERPC_BFDM_MPC8xx (bfd_mach_ppc_860)
+#define POWERPC_FLAG_MPC8xx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_MPC8xx check_pow_none
+
+__attribute__ (( unused ))
+static void init_proc_MPC8xx (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_5xx_8xx(env);
+ gen_spr_8xx(env);
+ init_excp_MPC8xx(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* Freescale 82xx cores (aka PowerQUICC-II) */
+/* PowerPC G2 */
+#define POWERPC_INSNS_G2 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_G2 (0x000000000006FFF2ULL)
+#define POWERPC_MMU_G2 (POWERPC_MMU_SOFT_6xx)
+//#define POWERPC_EXCP_G2 (POWERPC_EXCP_G2)
+#define POWERPC_INPUT_G2 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_G2 (bfd_mach_ppc_ec603e)
+#define POWERPC_FLAG_G2 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_G2 check_pow_hid0
+
+static void init_proc_G2 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_G2_755(env);
+ gen_spr_G2(env);
+ /* Time base */
+ gen_tbl(env);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation register */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_G2(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC G2LE */
+#define POWERPC_INSNS_G2LE (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_G2LE (0x000000000007FFF3ULL)
+#define POWERPC_MMU_G2LE (POWERPC_MMU_SOFT_6xx)
+#define POWERPC_EXCP_G2LE (POWERPC_EXCP_G2)
+#define POWERPC_INPUT_G2LE (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_G2LE (bfd_mach_ppc_ec603e)
+#define POWERPC_FLAG_G2LE (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_G2LE check_pow_hid0
+
+static void init_proc_G2LE (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_G2_755(env);
+ gen_spr_G2(env);
+ /* Time base */
+ gen_tbl(env);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation register */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_G2(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* e200 core */
+/* XXX: unimplemented instructions:
+ * dcblc
+ * dcbtlst
+ * dcbtstls
+ * icblc
+ * icbtls
+ * tlbivax
+ * all SPE multiply-accumulate instructions
+ */
+#define POWERPC_INSNS_e200 (PPC_INSNS_BASE | PPC_ISEL | \
+ PPC_SPE | PPC_SPE_SINGLE | \
+ PPC_WRTEE | PPC_RFDI | \
+ PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_TLBIVAX | \
+ PPC_BOOKE)
+#define POWERPC_MSRM_e200 (0x000000000606FF30ULL)
+#define POWERPC_MMU_e200 (POWERPC_MMU_BOOKE_FSL)
+#define POWERPC_EXCP_e200 (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_e200 (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_e200 (bfd_mach_ppc_860)
+#define POWERPC_FLAG_e200 (POWERPC_FLAG_SPE | POWERPC_FLAG_CE | \
+ POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_e200 check_pow_hid0
+
+__attribute__ (( unused ))
+static void init_proc_e200 (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x000000070000FFFFULL);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_SPEFSCR, "SPEFSCR",
+ &spr_read_spefscr, &spr_write_spefscr,
+ &spr_read_spefscr, &spr_write_spefscr,
+ 0x00000000);
+ /* Memory management */
+ gen_spr_BookE_FSL(env, 0x0000005D);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_ALTCTXCR, "ALTCTXCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_BUCSR, "BUCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_CTXCR, "CTXCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_DBCNT, "DBCNT",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_DBCR3, "DBCR3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_L1CFG0, "L1CFG0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_L1FINV0, "L1FINV0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC3, "IAC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_IAC4, "IAC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_DSRR0, "DSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_DSRR1, "DSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_e200(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* XXX: TODO: allocate internal IRQ controller */
+}
+
+/* e300 core */
+#define POWERPC_INSNS_e300 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_e300 (0x000000000007FFF3ULL)
+#define POWERPC_MMU_e300 (POWERPC_MMU_SOFT_6xx)
+#define POWERPC_EXCP_e300 (POWERPC_EXCP_603)
+#define POWERPC_INPUT_e300 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_e300 (bfd_mach_ppc_603)
+#define POWERPC_FLAG_e300 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_e300 check_pow_hid0
+
+__attribute__ (( unused ))
+static void init_proc_e300 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_603(env);
+ /* Time base */
+ gen_tbl(env);
+ /* hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_603(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* e500v1 core */
+#define POWERPC_INSNS_e500v1 (PPC_INSNS_BASE | PPC_ISEL | \
+ PPC_SPE | PPC_SPE_SINGLE | \
+ PPC_WRTEE | PPC_RFDI | \
+ PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_TLBIVAX | \
+ PPC_BOOKE)
+#define POWERPC_MSRM_e500v1 (0x000000000606FF30ULL)
+#define POWERPC_MMU_e500v1 (POWERPC_MMU_BOOKE_FSL)
+#define POWERPC_EXCP_e500v1 (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_e500v1 (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_e500v1 (bfd_mach_ppc_860)
+#define POWERPC_FLAG_e500v1 (POWERPC_FLAG_SPE | POWERPC_FLAG_CE | \
+ POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_e500v1 check_pow_hid0
+#define init_proc_e500v1 init_proc_e500
+
+/* e500v2 core */
+#define POWERPC_INSNS_e500v2 (PPC_INSNS_BASE | PPC_ISEL | \
+ PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | \
+ PPC_WRTEE | PPC_RFDI | \
+ PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBZ | PPC_CACHE_DCBA | \
+ PPC_MEM_TLBSYNC | PPC_TLBIVAX | \
+ PPC_BOOKE)
+#define POWERPC_MSRM_e500v2 (0x000000000606FF30ULL)
+#define POWERPC_MMU_e500v2 (POWERPC_MMU_BOOKE_FSL)
+#define POWERPC_EXCP_e500v2 (POWERPC_EXCP_BOOKE)
+#define POWERPC_INPUT_e500v2 (PPC_FLAGS_INPUT_BookE)
+#define POWERPC_BFDM_e500v2 (bfd_mach_ppc_860)
+#define POWERPC_FLAG_e500v2 (POWERPC_FLAG_SPE | POWERPC_FLAG_CE | \
+ POWERPC_FLAG_UBLE | POWERPC_FLAG_DE | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_e500v2 check_pow_hid0
+#define init_proc_e500v2 init_proc_e500
+
+static void init_proc_e500 (CPUPPCState *env)
+{
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_BookE(env, 0x0000000F0000FD7FULL);
+ /* Processor identification */
+ spr_register(env, SPR_BOOKE_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_SPEFSCR, "SPEFSCR",
+ &spr_read_spefscr, &spr_write_spefscr,
+ &spr_read_spefscr, &spr_write_spefscr,
+ 0x00000000);
+ /* Memory management */
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_pids = 3;
+#endif
+ gen_spr_BookE_FSL(env, 0x0000005F);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_BBEAR, "BBEAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_BBTAR, "BBTAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_MCAR, "MCAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_MCSR, "MCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_NPIDR, "NPIDR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_BUCSR, "BUCSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_L1CFG0, "L1CFG0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_L1CSR0, "L1CSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_Exxx_L1CSR1, "L1CSR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB0CFG, "TLB0CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_BOOKE_TLB1CFG, "TLB1CFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR0, "MCSRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_BOOKE_MCSRR1, "MCSRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+ env->nb_tlb = 64;
+ env->nb_ways = 1;
+ env->id_tlbs = 0;
+#endif
+ init_excp_e200(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppce500_irq_init(env);
+}
+
+/* Non-embedded PowerPC */
+
+/* POWER : same as 601, without mfmsr, mfsr */
+#if defined(TODO)
+#define POWERPC_INSNS_POWER (XXX_TODO)
+/* POWER RSC (from RAD6000) */
+#define POWERPC_MSRM_POWER (0x00000000FEF0ULL)
+#endif /* TODO */
+
+/* PowerPC 601 */
+#define POWERPC_INSNS_601 (PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | \
+ PPC_FLOAT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_601 (0x000000000000FD70ULL)
+#define POWERPC_MSRR_601 (0x0000000000001040ULL)
+//#define POWERPC_MMU_601 (POWERPC_MMU_601)
+//#define POWERPC_EXCP_601 (POWERPC_EXCP_601)
+#define POWERPC_INPUT_601 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_601 (bfd_mach_ppc_601)
+#define POWERPC_FLAG_601 (POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK)
+#define check_pow_601 check_pow_none
+
+static void init_proc_601 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_601(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_hid0_601,
+ 0x80010080);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_601_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_601_HID5, "HID5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ init_excp_601(env);
+ /* XXX: beware that dcache line size is 64
+ * but dcbz uses 32 bytes "sectors"
+ * XXX: this breaks clcs instruction !
+ */
+ env->dcache_line_size = 32;
+ env->icache_line_size = 64;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 601v */
+#define POWERPC_INSNS_601v (PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | \
+ PPC_FLOAT | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_601v (0x000000000000FD70ULL)
+#define POWERPC_MSRR_601v (0x0000000000001040ULL)
+#define POWERPC_MMU_601v (POWERPC_MMU_601)
+#define POWERPC_EXCP_601v (POWERPC_EXCP_601)
+#define POWERPC_INPUT_601v (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_601v (bfd_mach_ppc_601)
+#define POWERPC_FLAG_601v (POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK)
+#define check_pow_601v check_pow_none
+
+static void init_proc_601v (CPUPPCState *env)
+{
+ init_proc_601(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_601_HID15, "HID15",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* PowerPC 602 */
+#define POWERPC_INSNS_602 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_602_SPEC)
+#define POWERPC_MSRM_602 (0x0000000000C7FF73ULL)
+/* XXX: 602 MMU is quite specific. Should add a special case */
+#define POWERPC_MMU_602 (POWERPC_MMU_SOFT_6xx)
+//#define POWERPC_EXCP_602 (POWERPC_EXCP_602)
+#define POWERPC_INPUT_602 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_602 (bfd_mach_ppc_602)
+#define POWERPC_FLAG_602 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_602 check_pow_hid0
+
+static void init_proc_602 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_602(env);
+ /* Time base */
+ gen_tbl(env);
+ /* hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_602(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 603 */
+#define POWERPC_INSNS_603 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_603 (0x000000000007FF73ULL)
+#define POWERPC_MMU_603 (POWERPC_MMU_SOFT_6xx)
+//#define POWERPC_EXCP_603 (POWERPC_EXCP_603)
+#define POWERPC_INPUT_603 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_603 (bfd_mach_ppc_603)
+#define POWERPC_FLAG_603 (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_603 check_pow_hid0
+
+static void init_proc_603 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_603(env);
+ /* Time base */
+ gen_tbl(env);
+ /* hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_603(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 603e */
+#define POWERPC_INSNS_603E (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_603E (0x000000000007FF73ULL)
+#define POWERPC_MMU_603E (POWERPC_MMU_SOFT_6xx)
+//#define POWERPC_EXCP_603E (POWERPC_EXCP_603E)
+#define POWERPC_INPUT_603E (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_603E (bfd_mach_ppc_ec603e)
+#define POWERPC_FLAG_603E (POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK)
+#define check_pow_603E check_pow_hid0
+
+static void init_proc_603E (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_603(env);
+ /* Time base */
+ gen_tbl(env);
+ /* hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_603(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 604 */
+#define POWERPC_INSNS_604 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_604 (0x000000000005FF77ULL)
+#define POWERPC_MMU_604 (POWERPC_MMU_32B)
+//#define POWERPC_EXCP_604 (POWERPC_EXCP_604)
+#define POWERPC_INPUT_604 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_604 (bfd_mach_ppc_604)
+#define POWERPC_FLAG_604 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_604 check_pow_nocheck
+
+static void init_proc_604 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_604(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ init_excp_604(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 604E */
+#define POWERPC_INSNS_604E (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_604E (0x000000000005FF77ULL)
+#define POWERPC_MMU_604E (POWERPC_MMU_32B)
+#define POWERPC_EXCP_604E (POWERPC_EXCP_604)
+#define POWERPC_INPUT_604E (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_604E (bfd_mach_ppc_604)
+#define POWERPC_FLAG_604E (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_604E check_pow_nocheck
+
+static void init_proc_604E (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_604(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR1, "MMCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC3, "PMC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC4, "PMC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ init_excp_604(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 740 */
+#define POWERPC_INSNS_740 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_740 (0x000000000005FF77ULL)
+#define POWERPC_MMU_740 (POWERPC_MMU_32B)
+#define POWERPC_EXCP_740 (POWERPC_EXCP_7x0)
+#define POWERPC_INPUT_740 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_740 (bfd_mach_ppc_750)
+#define POWERPC_FLAG_740 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_740 check_pow_hid0
+
+static void init_proc_740 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ init_excp_7x0(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 750 */
+#define POWERPC_INSNS_750 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_750 (0x000000000005FF77ULL)
+#define POWERPC_MMU_750 (POWERPC_MMU_32B)
+#define POWERPC_EXCP_750 (POWERPC_EXCP_7x0)
+#define POWERPC_INPUT_750 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_750 (bfd_mach_ppc_750)
+#define POWERPC_FLAG_750 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_750 check_pow_hid0
+
+static void init_proc_750 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* XXX: high BATs are also present but are known to be bugged on
+ * die version 1.x
+ */
+ init_excp_7x0(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 750 CL */
+/* XXX: not implemented:
+ * cache lock instructions:
+ * dcbz_l
+ * floating point paired instructions
+ * psq_lux
+ * psq_lx
+ * psq_stux
+ * psq_stx
+ * ps_abs
+ * ps_add
+ * ps_cmpo0
+ * ps_cmpo1
+ * ps_cmpu0
+ * ps_cmpu1
+ * ps_div
+ * ps_madd
+ * ps_madds0
+ * ps_madds1
+ * ps_merge00
+ * ps_merge01
+ * ps_merge10
+ * ps_merge11
+ * ps_mr
+ * ps_msub
+ * ps_mul
+ * ps_muls0
+ * ps_muls1
+ * ps_nabs
+ * ps_neg
+ * ps_nmadd
+ * ps_nmsub
+ * ps_res
+ * ps_rsqrte
+ * ps_sel
+ * ps_sub
+ * ps_sum0
+ * ps_sum1
+ */
+#define POWERPC_INSNS_750cl (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_750cl (0x000000000005FF77ULL)
+#define POWERPC_MMU_750cl (POWERPC_MMU_32B)
+#define POWERPC_EXCP_750cl (POWERPC_EXCP_7x0)
+#define POWERPC_INPUT_750cl (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_750cl (bfd_mach_ppc_750)
+#define POWERPC_FLAG_750cl (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_750cl check_pow_hid0
+
+static void init_proc_750cl (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ /* Those registers are fake on 750CL */
+ spr_register(env, SPR_THRM1, "THRM1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_THRM2, "THRM2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_THRM3, "THRM3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX: not implemented */
+ spr_register(env, SPR_750_TDCL, "TDCL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_750_TDCH, "TDCH",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* DMA */
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_WPAR, "WPAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_750_DMAL, "DMAL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_750_DMAU, "DMAU",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750CL_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750CL_HID4, "HID4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Quantization registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR0, "GQR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR1, "GQR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR2, "GQR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR3, "GQR3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR4, "GQR4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR5, "GQR5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR6, "GQR6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_GQR7, "GQR7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* PowerPC 750cl has 8 DBATs and 8 IBATs */
+ gen_high_BATs(env);
+ init_excp_750cl(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 750CX */
+#define POWERPC_INSNS_750cx (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_750cx (0x000000000005FF77ULL)
+#define POWERPC_MMU_750cx (POWERPC_MMU_32B)
+#define POWERPC_EXCP_750cx (POWERPC_EXCP_7x0)
+#define POWERPC_INPUT_750cx (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_750cx (bfd_mach_ppc_750)
+#define POWERPC_FLAG_750cx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_750cx check_pow_hid0
+
+static void init_proc_750cx (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* This register is not implemented but is present for compatibility */
+ spr_register(env, SPR_SDA, "SDA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* PowerPC 750cx has 8 DBATs and 8 IBATs */
+ gen_high_BATs(env);
+ init_excp_750cx(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 750FX */
+#define POWERPC_INSNS_750fx (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_750fx (0x000000000005FF77ULL)
+#define POWERPC_MMU_750fx (POWERPC_MMU_32B)
+#define POWERPC_EXCP_750fx (POWERPC_EXCP_7x0)
+#define POWERPC_INPUT_750fx (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_750fx (bfd_mach_ppc_750)
+#define POWERPC_FLAG_750fx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_750fx check_pow_hid0
+
+static void init_proc_750fx (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_THRM4, "THRM4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750FX_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */
+ gen_high_BATs(env);
+ init_excp_7x0(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 750GX */
+#define POWERPC_INSNS_750gx (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_750gx (0x000000000005FF77ULL)
+#define POWERPC_MMU_750gx (POWERPC_MMU_32B)
+#define POWERPC_EXCP_750gx (POWERPC_EXCP_7x0)
+#define POWERPC_INPUT_750gx (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_750gx (bfd_mach_ppc_750)
+#define POWERPC_FLAG_750gx (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_750gx check_pow_hid0
+
+static void init_proc_750gx (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented (XXX: different from 750fx) */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_THRM4, "THRM4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented (XXX: different from 750fx) */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented (XXX: different from 750fx) */
+ spr_register(env, SPR_750FX_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */
+ gen_high_BATs(env);
+ init_excp_7x0(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 745 */
+#define POWERPC_INSNS_745 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_745 (0x000000000005FF77ULL)
+#define POWERPC_MMU_745 (POWERPC_MMU_SOFT_6xx)
+#define POWERPC_EXCP_745 (POWERPC_EXCP_7x5)
+#define POWERPC_INPUT_745 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_745 (bfd_mach_ppc_750)
+#define POWERPC_FLAG_745 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_745 check_pow_hid0
+
+static void init_proc_745 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ gen_spr_G2_755(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_7x5(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 755 */
+#define POWERPC_INSNS_755 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | PPC_6xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN)
+#define POWERPC_MSRM_755 (0x000000000005FF77ULL)
+#define POWERPC_MMU_755 (POWERPC_MMU_SOFT_6xx)
+#define POWERPC_EXCP_755 (POWERPC_EXCP_7x5)
+#define POWERPC_INPUT_755 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_755 (bfd_mach_ppc_750)
+#define POWERPC_FLAG_755 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_755 check_pow_hid0
+
+static void init_proc_755 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ gen_spr_G2_755(env);
+ /* Time base */
+ gen_tbl(env);
+ /* L2 cache control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2PMCR, "L2PMCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_6xx_7xx_soft_tlb(env, 64, 2);
+ init_excp_7x5(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7400 (aka G4) */
+#define POWERPC_INSNS_7400 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7400 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7400 (POWERPC_MMU_32B)
+#define POWERPC_EXCP_7400 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7400 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7400 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7400 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7400 check_pow_hid0
+
+static void init_proc_7400 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UBAMR, "UBAMR",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX: this seems not implemented on all revisions. */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSCR1, "MSSCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ init_excp_7400(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7410 (aka G4) */
+#define POWERPC_INSNS_7410 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7410 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7410 (POWERPC_MMU_32B)
+#define POWERPC_EXCP_7410 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7410 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7410 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7410 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7410 check_pow_hid0
+
+static void init_proc_7410 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UBAMR, "UBAMR",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Thermal management */
+ gen_spr_thrm(env);
+ /* L2PMCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2PMCR, "L2PMCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* LDSTDB */
+ /* XXX : not implemented */
+ spr_register(env, SPR_LDSTDB, "LDSTDB",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ init_excp_7400(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7440 (aka G4) */
+#define POWERPC_INSNS_7440 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | PPC_74xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7440 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7440 (POWERPC_MMU_SOFT_74xx)
+#define POWERPC_EXCP_7440 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7440 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7440 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7440 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7440 check_pow_hid0_74xx
+
+__attribute__ (( unused ))
+static void init_proc_7440 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UBAMR, "UBAMR",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* LDSTCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_LDSTCR, "LDSTCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* ICTRL */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTRL, "ICTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* MSSSR0 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSSR0, "MSSSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* PMC */
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC5, "PMC5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC5, "UPMC5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC6, "PMC6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC6, "UPMC6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_74xx_soft_tlb(env, 128, 2);
+ init_excp_7450(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7450 (aka G4) */
+#define POWERPC_INSNS_7450 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | PPC_74xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7450 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7450 (POWERPC_MMU_SOFT_74xx)
+#define POWERPC_EXCP_7450 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7450 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7450 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7450 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7450 check_pow_hid0_74xx
+
+__attribute__ (( unused ))
+static void init_proc_7450 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* Level 3 cache control */
+ gen_l3_ctrl(env);
+ /* L3ITCR1 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR1, "L3ITCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3ITCR2 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR2, "L3ITCR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3ITCR3 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR3, "L3ITCR3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3OHCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3OHCR, "L3OHCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UBAMR, "UBAMR",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* LDSTCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_LDSTCR, "LDSTCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* ICTRL */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTRL, "ICTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* MSSSR0 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSSR0, "MSSSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* PMC */
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC5, "PMC5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC5, "UPMC5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC6, "PMC6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC6, "UPMC6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_74xx_soft_tlb(env, 128, 2);
+ init_excp_7450(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7445 (aka G4) */
+#define POWERPC_INSNS_7445 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | PPC_74xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7445 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7445 (POWERPC_MMU_SOFT_74xx)
+#define POWERPC_EXCP_7445 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7445 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7445 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7445 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7445 check_pow_hid0_74xx
+
+__attribute__ (( unused ))
+static void init_proc_7445 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* LDSTCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_LDSTCR, "LDSTCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* ICTRL */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTRL, "ICTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* MSSSR0 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSSR0, "MSSSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* PMC */
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC5, "PMC5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC5, "UPMC5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC6, "PMC6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC6, "UPMC6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* SPRGs */
+ spr_register(env, SPR_SPRG4, "SPRG4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG4, "USPRG4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG5, "SPRG5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG5, "USPRG5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG6, "SPRG6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG6, "USPRG6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG7, "SPRG7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG7, "USPRG7",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_74xx_soft_tlb(env, 128, 2);
+ init_excp_7450(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7455 (aka G4) */
+#define POWERPC_INSNS_7455 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | PPC_74xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7455 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7455 (POWERPC_MMU_SOFT_74xx)
+#define POWERPC_EXCP_7455 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7455 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7455 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7455 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7455 check_pow_hid0_74xx
+
+__attribute__ (( unused ))
+static void init_proc_7455 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* Level 3 cache control */
+ gen_l3_ctrl(env);
+ /* LDSTCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_LDSTCR, "LDSTCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* ICTRL */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTRL, "ICTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* MSSSR0 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSSR0, "MSSSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* PMC */
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC5, "PMC5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC5, "UPMC5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC6, "PMC6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC6, "UPMC6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* SPRGs */
+ spr_register(env, SPR_SPRG4, "SPRG4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG4, "USPRG4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG5, "SPRG5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG5, "USPRG5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG6, "SPRG6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG6, "USPRG6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG7, "SPRG7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG7, "USPRG7",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_74xx_soft_tlb(env, 128, 2);
+ init_excp_7450(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+/* PowerPC 7457 (aka G4) */
+#define POWERPC_INSNS_7457 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | \
+ PPC_CACHE_DCBA | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_MEM_TLBIA | PPC_74xx_TLB | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_ALTIVEC)
+#define POWERPC_MSRM_7457 (0x000000000205FF77ULL)
+#define POWERPC_MMU_7457 (POWERPC_MMU_SOFT_74xx)
+#define POWERPC_EXCP_7457 (POWERPC_EXCP_74xx)
+#define POWERPC_INPUT_7457 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_7457 (bfd_mach_ppc_7400)
+#define POWERPC_FLAG_7457 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+#define check_pow_7457 check_pow_hid0_74xx
+
+__attribute__ (( unused ))
+static void init_proc_7457 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* 74xx specific SPR */
+ gen_spr_74xx(env);
+ /* Level 3 cache control */
+ gen_l3_ctrl(env);
+ /* L3ITCR1 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR1, "L3ITCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3ITCR2 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR2, "L3ITCR2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3ITCR3 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3ITCR3, "L3ITCR3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* L3OHCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_L3OHCR, "L3OHCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* LDSTCR */
+ /* XXX : not implemented */
+ spr_register(env, SPR_LDSTCR, "LDSTCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* ICTRL */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTRL, "ICTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* MSSSR0 */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MSSSR0, "MSSSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* PMC */
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC5, "PMC5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC5, "UPMC5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC6, "PMC6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_UPMC6, "UPMC6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* SPRGs */
+ spr_register(env, SPR_SPRG4, "SPRG4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG4, "USPRG4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG5, "SPRG5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG5, "USPRG5",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG6, "SPRG6",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG6, "USPRG6",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_SPRG7, "SPRG7",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_USPRG7, "USPRG7",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ gen_high_BATs(env);
+ gen_74xx_soft_tlb(env, 128, 2);
+ init_excp_7450(env);
+ env->dcache_line_size = 32;
+ env->icache_line_size = 32;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+
+#if defined (TARGET_PPC64)
+/* PowerPC 970 */
+#define POWERPC_INSNS_970 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_64B | PPC_ALTIVEC | \
+ PPC_SEGMENT_64B | PPC_SLBI)
+#define POWERPC_MSRM_970 (0x900000000204FF36ULL)
+#define POWERPC_MMU_970 (POWERPC_MMU_64B)
+//#define POWERPC_EXCP_970 (POWERPC_EXCP_970)
+#define POWERPC_INPUT_970 (PPC_FLAGS_INPUT_970)
+#define POWERPC_BFDM_970 (bfd_mach_ppc64)
+#define POWERPC_FLAG_970 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+
+#if defined(CONFIG_USER_ONLY)
+#define POWERPC970_HID5_INIT 0x00000080
+#else
+#define POWERPC970_HID5_INIT 0x00000000
+#endif
+
+static int check_pow_970 (CPUPPCState *env)
+{
+ if (env->spr[SPR_HID0] & 0x00600000)
+ return 1;
+
+ return 0;
+}
+
+static void init_proc_970 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ 0x60000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750FX_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_970_HID5, "HID5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ POWERPC970_HID5_INIT);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ /* XXX: not correct */
+ gen_low_BATs(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCFG, "MMUCFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000); /* TOFIX */
+ spr_register(env, SPR_HIOR, "SPR_HIOR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_hior, &spr_write_hior,
+ 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+ env->slb_nr = 32;
+#endif
+ init_excp_970(env);
+ env->dcache_line_size = 128;
+ env->icache_line_size = 128;
+ /* Allocate hardware IRQ controller */
+ ppc970_irq_init(env);
+ /* Can't find information on what this should be on reset. This
+ * value is the one used by 74xx processors. */
+ vscr_init(env, 0x00010000);
+}
+
+/* PowerPC 970FX (aka G5) */
+#define POWERPC_INSNS_970FX (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_64B | PPC_ALTIVEC | \
+ PPC_SEGMENT_64B | PPC_SLBI)
+#define POWERPC_MSRM_970FX (0x800000000204FF36ULL)
+#define POWERPC_MMU_970FX (POWERPC_MMU_64B)
+#define POWERPC_EXCP_970FX (POWERPC_EXCP_970)
+#define POWERPC_INPUT_970FX (PPC_FLAGS_INPUT_970)
+#define POWERPC_BFDM_970FX (bfd_mach_ppc64)
+#define POWERPC_FLAG_970FX (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+
+static int check_pow_970FX (CPUPPCState *env)
+{
+ if (env->spr[SPR_HID0] & 0x00600000)
+ return 1;
+
+ return 0;
+}
+
+static void init_proc_970FX (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ 0x60000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750FX_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_970_HID5, "HID5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ POWERPC970_HID5_INIT);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ /* XXX: not correct */
+ gen_low_BATs(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCFG, "MMUCFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000); /* TOFIX */
+ spr_register(env, SPR_HIOR, "SPR_HIOR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_hior, &spr_write_hior,
+ 0x00000000);
+ spr_register(env, SPR_CTRL, "SPR_CTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_UCTRL, "SPR_UCTRL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_VRSAVE, "SPR_VRSAVE",
+ &spr_read_generic, &spr_write_generic,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+ env->slb_nr = 64;
+#endif
+ init_excp_970(env);
+ env->dcache_line_size = 128;
+ env->icache_line_size = 128;
+ /* Allocate hardware IRQ controller */
+ ppc970_irq_init(env);
+ /* Can't find information on what this should be on reset. This
+ * value is the one used by 74xx processors. */
+ vscr_init(env, 0x00010000);
+}
+
+/* PowerPC 970 GX */
+#define POWERPC_INSNS_970GX (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_64B | PPC_ALTIVEC | \
+ PPC_SEGMENT_64B | PPC_SLBI)
+#define POWERPC_MSRM_970GX (0x800000000204FF36ULL)
+#define POWERPC_MMU_970GX (POWERPC_MMU_64B)
+#define POWERPC_EXCP_970GX (POWERPC_EXCP_970)
+#define POWERPC_INPUT_970GX (PPC_FLAGS_INPUT_970)
+#define POWERPC_BFDM_970GX (bfd_mach_ppc64)
+#define POWERPC_FLAG_970GX (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+
+static int check_pow_970GX (CPUPPCState *env)
+{
+ if (env->spr[SPR_HID0] & 0x00600000)
+ return 1;
+
+ return 0;
+}
+
+static void init_proc_970GX (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ 0x60000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750FX_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_970_HID5, "HID5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ POWERPC970_HID5_INIT);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ /* XXX: not correct */
+ gen_low_BATs(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCFG, "MMUCFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000); /* TOFIX */
+ spr_register(env, SPR_HIOR, "SPR_HIOR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_hior, &spr_write_hior,
+ 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+ env->slb_nr = 32;
+#endif
+ init_excp_970(env);
+ env->dcache_line_size = 128;
+ env->icache_line_size = 128;
+ /* Allocate hardware IRQ controller */
+ ppc970_irq_init(env);
+ /* Can't find information on what this should be on reset. This
+ * value is the one used by 74xx processors. */
+ vscr_init(env, 0x00010000);
+}
+
+/* PowerPC 970 MP */
+#define POWERPC_INSNS_970MP (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZT | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_64B | PPC_ALTIVEC | \
+ PPC_SEGMENT_64B | PPC_SLBI)
+#define POWERPC_MSRM_970MP (0x900000000204FF36ULL)
+#define POWERPC_MMU_970MP (POWERPC_MMU_64B)
+#define POWERPC_EXCP_970MP (POWERPC_EXCP_970)
+#define POWERPC_INPUT_970MP (PPC_FLAGS_INPUT_970)
+#define POWERPC_BFDM_970MP (bfd_mach_ppc64)
+#define POWERPC_FLAG_970MP (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \
+ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \
+ POWERPC_FLAG_BUS_CLK)
+
+static int check_pow_970MP (CPUPPCState *env)
+{
+ if (env->spr[SPR_HID0] & 0x01C00000)
+ return 1;
+
+ return 0;
+}
+
+static void init_proc_970MP (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_7xx(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_clear,
+ 0x60000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750FX_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_970_HID5, "HID5",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ POWERPC970_HID5_INIT);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ /* XXX: not correct */
+ gen_low_BATs(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCFG, "MMUCFG",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000); /* TOFIX */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMUCSR0, "MMUCSR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000); /* TOFIX */
+ spr_register(env, SPR_HIOR, "SPR_HIOR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_hior, &spr_write_hior,
+ 0x00000000);
+#if !defined(CONFIG_USER_ONLY)
+ env->slb_nr = 32;
+#endif
+ init_excp_970(env);
+ env->dcache_line_size = 128;
+ env->icache_line_size = 128;
+ /* Allocate hardware IRQ controller */
+ ppc970_irq_init(env);
+ /* Can't find information on what this should be on reset. This
+ * value is the one used by 74xx processors. */
+ vscr_init(env, 0x00010000);
+}
+
+/* PowerPC 620 */
+#define POWERPC_INSNS_620 (PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | \
+ PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \
+ PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | \
+ PPC_FLOAT_STFIWX | \
+ PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | \
+ PPC_MEM_SYNC | PPC_MEM_EIEIO | \
+ PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | \
+ PPC_SEGMENT | PPC_EXTERN | \
+ PPC_64B | PPC_SLBI)
+#define POWERPC_MSRM_620 (0x800000000005FF77ULL)
+//#define POWERPC_MMU_620 (POWERPC_MMU_620)
+#define POWERPC_EXCP_620 (POWERPC_EXCP_970)
+#define POWERPC_INPUT_620 (PPC_FLAGS_INPUT_6xx)
+#define POWERPC_BFDM_620 (bfd_mach_ppc64)
+#define POWERPC_FLAG_620 (POWERPC_FLAG_SE | POWERPC_FLAG_BE | \
+ POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK)
+#define check_pow_620 check_pow_nocheck /* Check this */
+
+__attribute__ (( unused ))
+static void init_proc_620 (CPUPPCState *env)
+{
+ gen_spr_ne_601(env);
+ gen_spr_620(env);
+ /* Time base */
+ gen_tbl(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Memory management */
+ gen_low_BATs(env);
+ init_excp_620(env);
+ env->dcache_line_size = 64;
+ env->icache_line_size = 64;
+ /* Allocate hardware IRQ controller */
+ ppc6xx_irq_init(env);
+}
+#endif /* defined (TARGET_PPC64) */
+
+/* Default 32 bits PowerPC target will be 604 */
+#define CPU_POWERPC_PPC32 CPU_POWERPC_604
+#define POWERPC_INSNS_PPC32 POWERPC_INSNS_604
+#define POWERPC_MSRM_PPC32 POWERPC_MSRM_604
+#define POWERPC_MMU_PPC32 POWERPC_MMU_604
+#define POWERPC_EXCP_PPC32 POWERPC_EXCP_604
+#define POWERPC_INPUT_PPC32 POWERPC_INPUT_604
+#define POWERPC_BFDM_PPC32 POWERPC_BFDM_604
+#define POWERPC_FLAG_PPC32 POWERPC_FLAG_604
+#define check_pow_PPC32 check_pow_604
+#define init_proc_PPC32 init_proc_604
+
+/* Default 64 bits PowerPC target will be 970 FX */
+#define CPU_POWERPC_PPC64 CPU_POWERPC_970FX
+#define POWERPC_INSNS_PPC64 POWERPC_INSNS_970FX
+#define POWERPC_MSRM_PPC64 POWERPC_MSRM_970FX
+#define POWERPC_MMU_PPC64 POWERPC_MMU_970FX
+#define POWERPC_EXCP_PPC64 POWERPC_EXCP_970FX
+#define POWERPC_INPUT_PPC64 POWERPC_INPUT_970FX
+#define POWERPC_BFDM_PPC64 POWERPC_BFDM_970FX
+#define POWERPC_FLAG_PPC64 POWERPC_FLAG_970FX
+#define check_pow_PPC64 check_pow_970FX
+#define init_proc_PPC64 init_proc_970FX
+
+/* Default PowerPC target will be PowerPC 32 */
+#if defined (TARGET_PPC64) && 0 // XXX: TODO
+#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC64
+#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC64
+#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC64
+#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC64
+#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC64
+#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC64
+#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC64
+#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC64
+#define check_pow_DEFAULT check_pow_PPC64
+#define init_proc_DEFAULT init_proc_PPC64
+#else
+#define CPU_POWERPC_DEFAULT CPU_POWERPC_PPC32
+#define POWERPC_INSNS_DEFAULT POWERPC_INSNS_PPC32
+#define POWERPC_MSRM_DEFAULT POWERPC_MSRM_PPC32
+#define POWERPC_MMU_DEFAULT POWERPC_MMU_PPC32
+#define POWERPC_EXCP_DEFAULT POWERPC_EXCP_PPC32
+#define POWERPC_INPUT_DEFAULT POWERPC_INPUT_PPC32
+#define POWERPC_BFDM_DEFAULT POWERPC_BFDM_PPC32
+#define POWERPC_FLAG_DEFAULT POWERPC_FLAG_PPC32
+#define check_pow_DEFAULT check_pow_PPC32
+#define init_proc_DEFAULT init_proc_PPC32
+#endif
+
+/*****************************************************************************/
+/* PVR definitions for most known PowerPC */
+enum {
+ /* PowerPC 401 family */
+ /* Generic PowerPC 401 */
+#define CPU_POWERPC_401 CPU_POWERPC_401G2
+ /* PowerPC 401 cores */
+ CPU_POWERPC_401A1 = 0x00210000,
+ CPU_POWERPC_401B2 = 0x00220000,
+#if 0
+ CPU_POWERPC_401B3 = xxx,
+#endif
+ CPU_POWERPC_401C2 = 0x00230000,
+ CPU_POWERPC_401D2 = 0x00240000,
+ CPU_POWERPC_401E2 = 0x00250000,
+ CPU_POWERPC_401F2 = 0x00260000,
+ CPU_POWERPC_401G2 = 0x00270000,
+ /* PowerPC 401 microcontrolers */
+#if 0
+ CPU_POWERPC_401GF = xxx,
+#endif
+#define CPU_POWERPC_IOP480 CPU_POWERPC_401B2
+ /* IBM Processor for Network Resources */
+ CPU_POWERPC_COBRA = 0x10100000, /* XXX: 405 ? */
+#if 0
+ CPU_POWERPC_XIPCHIP = xxx,
+#endif
+ /* PowerPC 403 family */
+ /* Generic PowerPC 403 */
+#define CPU_POWERPC_403 CPU_POWERPC_403GC
+ /* PowerPC 403 microcontrollers */
+ CPU_POWERPC_403GA = 0x00200011,
+ CPU_POWERPC_403GB = 0x00200100,
+ CPU_POWERPC_403GC = 0x00200200,
+ CPU_POWERPC_403GCX = 0x00201400,
+#if 0
+ CPU_POWERPC_403GP = xxx,
+#endif
+ /* PowerPC 405 family */
+ /* Generic PowerPC 405 */
+#define CPU_POWERPC_405 CPU_POWERPC_405D4
+ /* PowerPC 405 cores */
+#if 0
+ CPU_POWERPC_405A3 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405A4 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405B3 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405B4 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405C3 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405C4 = xxx,
+#endif
+ CPU_POWERPC_405D2 = 0x20010000,
+#if 0
+ CPU_POWERPC_405D3 = xxx,
+#endif
+ CPU_POWERPC_405D4 = 0x41810000,
+#if 0
+ CPU_POWERPC_405D5 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405E4 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405F4 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405F5 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405F6 = xxx,
+#endif
+ /* PowerPC 405 microcontrolers */
+ /* XXX: missing 0x200108a0 */
+#define CPU_POWERPC_405CR CPU_POWERPC_405CRc
+ CPU_POWERPC_405CRa = 0x40110041,
+ CPU_POWERPC_405CRb = 0x401100C5,
+ CPU_POWERPC_405CRc = 0x40110145,
+ CPU_POWERPC_405EP = 0x51210950,
+#if 0
+ CPU_POWERPC_405EXr = xxx,
+#endif
+ CPU_POWERPC_405EZ = 0x41511460, /* 0x51210950 ? */
+#if 0
+ CPU_POWERPC_405FX = xxx,
+#endif
+#define CPU_POWERPC_405GP CPU_POWERPC_405GPd
+ CPU_POWERPC_405GPa = 0x40110000,
+ CPU_POWERPC_405GPb = 0x40110040,
+ CPU_POWERPC_405GPc = 0x40110082,
+ CPU_POWERPC_405GPd = 0x401100C4,
+#define CPU_POWERPC_405GPe CPU_POWERPC_405CRc
+ CPU_POWERPC_405GPR = 0x50910951,
+#if 0
+ CPU_POWERPC_405H = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405L = xxx,
+#endif
+ CPU_POWERPC_405LP = 0x41F10000,
+#if 0
+ CPU_POWERPC_405PM = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405PS = xxx,
+#endif
+#if 0
+ CPU_POWERPC_405S = xxx,
+#endif
+ /* IBM network processors */
+ CPU_POWERPC_NPE405H = 0x414100C0,
+ CPU_POWERPC_NPE405H2 = 0x41410140,
+ CPU_POWERPC_NPE405L = 0x416100C0,
+ CPU_POWERPC_NPE4GS3 = 0x40B10000,
+#if 0
+ CPU_POWERPC_NPCxx1 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_NPR161 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_LC77700 = xxx,
+#endif
+ /* IBM STBxxx (PowerPC 401/403/405 core based microcontrollers) */
+#if 0
+ CPU_POWERPC_STB01000 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_STB01010 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_STB0210 = xxx, /* 401B3 */
+#endif
+ CPU_POWERPC_STB03 = 0x40310000, /* 0x40130000 ? */
+#if 0
+ CPU_POWERPC_STB043 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_STB045 = xxx,
+#endif
+ CPU_POWERPC_STB04 = 0x41810000,
+ CPU_POWERPC_STB25 = 0x51510950,
+#if 0
+ CPU_POWERPC_STB130 = xxx,
+#endif
+ /* Xilinx cores */
+ CPU_POWERPC_X2VP4 = 0x20010820,
+#define CPU_POWERPC_X2VP7 CPU_POWERPC_X2VP4
+ CPU_POWERPC_X2VP20 = 0x20010860,
+#define CPU_POWERPC_X2VP50 CPU_POWERPC_X2VP20
+#if 0
+ CPU_POWERPC_ZL10310 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_ZL10311 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_ZL10320 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_ZL10321 = xxx,
+#endif
+ /* PowerPC 440 family */
+ /* Generic PowerPC 440 */
+#define CPU_POWERPC_440 CPU_POWERPC_440GXf
+ /* PowerPC 440 cores */
+#if 0
+ CPU_POWERPC_440A4 = xxx,
+#endif
+ CPU_POWERPC_440_XILINX = 0x7ff21910,
+#if 0
+ CPU_POWERPC_440A5 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_440B4 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_440F5 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_440G5 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_440H4 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_440H6 = xxx,
+#endif
+ /* PowerPC 440 microcontrolers */
+#define CPU_POWERPC_440EP CPU_POWERPC_440EPb
+ CPU_POWERPC_440EPa = 0x42221850,
+ CPU_POWERPC_440EPb = 0x422218D3,
+#define CPU_POWERPC_440GP CPU_POWERPC_440GPc
+ CPU_POWERPC_440GPb = 0x40120440,
+ CPU_POWERPC_440GPc = 0x40120481,
+#define CPU_POWERPC_440GR CPU_POWERPC_440GRa
+#define CPU_POWERPC_440GRa CPU_POWERPC_440EPb
+ CPU_POWERPC_440GRX = 0x200008D0,
+#define CPU_POWERPC_440EPX CPU_POWERPC_440GRX
+#define CPU_POWERPC_440GX CPU_POWERPC_440GXf
+ CPU_POWERPC_440GXa = 0x51B21850,
+ CPU_POWERPC_440GXb = 0x51B21851,
+ CPU_POWERPC_440GXc = 0x51B21892,
+ CPU_POWERPC_440GXf = 0x51B21894,
+#if 0
+ CPU_POWERPC_440S = xxx,
+#endif
+ CPU_POWERPC_440SP = 0x53221850,
+ CPU_POWERPC_440SP2 = 0x53221891,
+ CPU_POWERPC_440SPE = 0x53421890,
+ /* PowerPC 460 family */
+#if 0
+ /* Generic PowerPC 464 */
+#define CPU_POWERPC_464 CPU_POWERPC_464H90
+#endif
+ /* PowerPC 464 microcontrolers */
+#if 0
+ CPU_POWERPC_464H90 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_464H90FP = xxx,
+#endif
+ /* Freescale embedded PowerPC cores */
+ /* PowerPC MPC 5xx cores (aka RCPU) */
+ CPU_POWERPC_MPC5xx = 0x00020020,
+#define CPU_POWERPC_MGT560 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC509 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC533 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC534 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC555 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC556 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC560 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC561 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC562 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC563 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC564 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC565 CPU_POWERPC_MPC5xx
+#define CPU_POWERPC_MPC566 CPU_POWERPC_MPC5xx
+ /* PowerPC MPC 8xx cores (aka PowerQUICC) */
+ CPU_POWERPC_MPC8xx = 0x00500000,
+#define CPU_POWERPC_MGT823 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC821 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC823 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC850 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC852T CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC855T CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC857 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC859 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC860 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC862 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC866 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC870 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC875 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC880 CPU_POWERPC_MPC8xx
+#define CPU_POWERPC_MPC885 CPU_POWERPC_MPC8xx
+ /* G2 cores (aka PowerQUICC-II) */
+ CPU_POWERPC_G2 = 0x00810011,
+ CPU_POWERPC_G2H4 = 0x80811010,
+ CPU_POWERPC_G2gp = 0x80821010,
+ CPU_POWERPC_G2ls = 0x90810010,
+ CPU_POWERPC_MPC603 = 0x00810100,
+ CPU_POWERPC_G2_HIP3 = 0x00810101,
+ CPU_POWERPC_G2_HIP4 = 0x80811014,
+ /* G2_LE core (aka PowerQUICC-II) */
+ CPU_POWERPC_G2LE = 0x80820010,
+ CPU_POWERPC_G2LEgp = 0x80822010,
+ CPU_POWERPC_G2LEls = 0xA0822010,
+ CPU_POWERPC_G2LEgp1 = 0x80822011,
+ CPU_POWERPC_G2LEgp3 = 0x80822013,
+ /* MPC52xx microcontrollers */
+ /* XXX: MPC 5121 ? */
+#define CPU_POWERPC_MPC52xx CPU_POWERPC_MPC5200
+#define CPU_POWERPC_MPC5200 CPU_POWERPC_MPC5200_v12
+#define CPU_POWERPC_MPC5200_v10 CPU_POWERPC_G2LEgp1
+#define CPU_POWERPC_MPC5200_v11 CPU_POWERPC_G2LEgp1
+#define CPU_POWERPC_MPC5200_v12 CPU_POWERPC_G2LEgp1
+#define CPU_POWERPC_MPC5200B CPU_POWERPC_MPC5200B_v21
+#define CPU_POWERPC_MPC5200B_v20 CPU_POWERPC_G2LEgp1
+#define CPU_POWERPC_MPC5200B_v21 CPU_POWERPC_G2LEgp1
+ /* MPC82xx microcontrollers */
+#define CPU_POWERPC_MPC82xx CPU_POWERPC_MPC8280
+#define CPU_POWERPC_MPC8240 CPU_POWERPC_MPC603
+#define CPU_POWERPC_MPC8241 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8245 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8247 CPU_POWERPC_G2LEgp3
+#define CPU_POWERPC_MPC8248 CPU_POWERPC_G2LEgp3
+#define CPU_POWERPC_MPC8250 CPU_POWERPC_MPC8250_HiP4
+#define CPU_POWERPC_MPC8250_HiP3 CPU_POWERPC_G2_HIP3
+#define CPU_POWERPC_MPC8250_HiP4 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8255 CPU_POWERPC_MPC8255_HiP4
+#define CPU_POWERPC_MPC8255_HiP3 CPU_POWERPC_G2_HIP3
+#define CPU_POWERPC_MPC8255_HiP4 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8260 CPU_POWERPC_MPC8260_HiP4
+#define CPU_POWERPC_MPC8260_HiP3 CPU_POWERPC_G2_HIP3
+#define CPU_POWERPC_MPC8260_HiP4 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8264 CPU_POWERPC_MPC8264_HiP4
+#define CPU_POWERPC_MPC8264_HiP3 CPU_POWERPC_G2_HIP3
+#define CPU_POWERPC_MPC8264_HiP4 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8265 CPU_POWERPC_MPC8265_HiP4
+#define CPU_POWERPC_MPC8265_HiP3 CPU_POWERPC_G2_HIP3
+#define CPU_POWERPC_MPC8265_HiP4 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8266 CPU_POWERPC_MPC8266_HiP4
+#define CPU_POWERPC_MPC8266_HiP3 CPU_POWERPC_G2_HIP3
+#define CPU_POWERPC_MPC8266_HiP4 CPU_POWERPC_G2_HIP4
+#define CPU_POWERPC_MPC8270 CPU_POWERPC_G2LEgp3
+#define CPU_POWERPC_MPC8271 CPU_POWERPC_G2LEgp3
+#define CPU_POWERPC_MPC8272 CPU_POWERPC_G2LEgp3
+#define CPU_POWERPC_MPC8275 CPU_POWERPC_G2LEgp3
+#define CPU_POWERPC_MPC8280 CPU_POWERPC_G2LEgp3
+ /* e200 family */
+ /* e200 cores */
+#define CPU_POWERPC_e200 CPU_POWERPC_e200z6
+#if 0
+ CPU_POWERPC_e200z0 = xxx,
+#endif
+#if 0
+ CPU_POWERPC_e200z1 = xxx,
+#endif
+#if 0 /* ? */
+ CPU_POWERPC_e200z3 = 0x81120000,
+#endif
+ CPU_POWERPC_e200z5 = 0x81000000,
+ CPU_POWERPC_e200z6 = 0x81120000,
+ /* MPC55xx microcontrollers */
+#define CPU_POWERPC_MPC55xx CPU_POWERPC_MPC5567
+#if 0
+#define CPU_POWERPC_MPC5514E CPU_POWERPC_MPC5514E_v1
+#define CPU_POWERPC_MPC5514E_v0 CPU_POWERPC_e200z0
+#define CPU_POWERPC_MPC5514E_v1 CPU_POWERPC_e200z1
+#define CPU_POWERPC_MPC5514G CPU_POWERPC_MPC5514G_v1
+#define CPU_POWERPC_MPC5514G_v0 CPU_POWERPC_e200z0
+#define CPU_POWERPC_MPC5514G_v1 CPU_POWERPC_e200z1
+#define CPU_POWERPC_MPC5515S CPU_POWERPC_e200z1
+#define CPU_POWERPC_MPC5516E CPU_POWERPC_MPC5516E_v1
+#define CPU_POWERPC_MPC5516E_v0 CPU_POWERPC_e200z0
+#define CPU_POWERPC_MPC5516E_v1 CPU_POWERPC_e200z1
+#define CPU_POWERPC_MPC5516G CPU_POWERPC_MPC5516G_v1
+#define CPU_POWERPC_MPC5516G_v0 CPU_POWERPC_e200z0
+#define CPU_POWERPC_MPC5516G_v1 CPU_POWERPC_e200z1
+#define CPU_POWERPC_MPC5516S CPU_POWERPC_e200z1
+#endif
+#if 0
+#define CPU_POWERPC_MPC5533 CPU_POWERPC_e200z3
+#define CPU_POWERPC_MPC5534 CPU_POWERPC_e200z3
+#endif
+#define CPU_POWERPC_MPC5553 CPU_POWERPC_e200z6
+#define CPU_POWERPC_MPC5554 CPU_POWERPC_e200z6
+#define CPU_POWERPC_MPC5561 CPU_POWERPC_e200z6
+#define CPU_POWERPC_MPC5565 CPU_POWERPC_e200z6
+#define CPU_POWERPC_MPC5566 CPU_POWERPC_e200z6
+#define CPU_POWERPC_MPC5567 CPU_POWERPC_e200z6
+ /* e300 family */
+ /* e300 cores */
+#define CPU_POWERPC_e300 CPU_POWERPC_e300c3
+ CPU_POWERPC_e300c1 = 0x00830010,
+ CPU_POWERPC_e300c2 = 0x00840010,
+ CPU_POWERPC_e300c3 = 0x00850010,
+ CPU_POWERPC_e300c4 = 0x00860010,
+ /* MPC83xx microcontrollers */
+#define CPU_POWERPC_MPC831x CPU_POWERPC_e300c3
+#define CPU_POWERPC_MPC832x CPU_POWERPC_e300c2
+#define CPU_POWERPC_MPC834x CPU_POWERPC_e300c1
+#define CPU_POWERPC_MPC835x CPU_POWERPC_e300c1
+#define CPU_POWERPC_MPC836x CPU_POWERPC_e300c1
+#define CPU_POWERPC_MPC837x CPU_POWERPC_e300c4
+ /* e500 family */
+ /* e500 cores */
+#define CPU_POWERPC_e500 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_e500v1 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_e500v2 CPU_POWERPC_e500v2_v22
+ CPU_POWERPC_e500v1_v10 = 0x80200010,
+ CPU_POWERPC_e500v1_v20 = 0x80200020,
+ CPU_POWERPC_e500v2_v10 = 0x80210010,
+ CPU_POWERPC_e500v2_v11 = 0x80210011,
+ CPU_POWERPC_e500v2_v20 = 0x80210020,
+ CPU_POWERPC_e500v2_v21 = 0x80210021,
+ CPU_POWERPC_e500v2_v22 = 0x80210022,
+ CPU_POWERPC_e500v2_v30 = 0x80210030,
+ /* MPC85xx microcontrollers */
+#define CPU_POWERPC_MPC8533 CPU_POWERPC_MPC8533_v11
+#define CPU_POWERPC_MPC8533_v10 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8533_v11 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8533E CPU_POWERPC_MPC8533E_v11
+#define CPU_POWERPC_MPC8533E_v10 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8533E_v11 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8540 CPU_POWERPC_MPC8540_v21
+#define CPU_POWERPC_MPC8540_v10 CPU_POWERPC_e500v1_v10
+#define CPU_POWERPC_MPC8540_v20 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_MPC8540_v21 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_MPC8541 CPU_POWERPC_MPC8541_v11
+#define CPU_POWERPC_MPC8541_v10 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_MPC8541_v11 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_MPC8541E CPU_POWERPC_MPC8541E_v11
+#define CPU_POWERPC_MPC8541E_v10 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_MPC8541E_v11 CPU_POWERPC_e500v1_v20
+#define CPU_POWERPC_MPC8543 CPU_POWERPC_MPC8543_v21
+#define CPU_POWERPC_MPC8543_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8543_v11 CPU_POWERPC_e500v2_v11
+#define CPU_POWERPC_MPC8543_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8543_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8543E CPU_POWERPC_MPC8543E_v21
+#define CPU_POWERPC_MPC8543E_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8543E_v11 CPU_POWERPC_e500v2_v11
+#define CPU_POWERPC_MPC8543E_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8543E_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8544 CPU_POWERPC_MPC8544_v11
+#define CPU_POWERPC_MPC8544_v10 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8544_v11 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8544E_v11 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8544E CPU_POWERPC_MPC8544E_v11
+#define CPU_POWERPC_MPC8544E_v10 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8545 CPU_POWERPC_MPC8545_v21
+#define CPU_POWERPC_MPC8545_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8545_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8545_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8545E CPU_POWERPC_MPC8545E_v21
+#define CPU_POWERPC_MPC8545E_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8545E_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8545E_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8547E CPU_POWERPC_MPC8545E_v21
+#define CPU_POWERPC_MPC8547E_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8547E_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8547E_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8548 CPU_POWERPC_MPC8548_v21
+#define CPU_POWERPC_MPC8548_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8548_v11 CPU_POWERPC_e500v2_v11
+#define CPU_POWERPC_MPC8548_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8548_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8548E CPU_POWERPC_MPC8548E_v21
+#define CPU_POWERPC_MPC8548E_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8548E_v11 CPU_POWERPC_e500v2_v11
+#define CPU_POWERPC_MPC8548E_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8548E_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8555 CPU_POWERPC_MPC8555_v11
+#define CPU_POWERPC_MPC8555_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8555_v11 CPU_POWERPC_e500v2_v11
+#define CPU_POWERPC_MPC8555E CPU_POWERPC_MPC8555E_v11
+#define CPU_POWERPC_MPC8555E_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8555E_v11 CPU_POWERPC_e500v2_v11
+#define CPU_POWERPC_MPC8560 CPU_POWERPC_MPC8560_v21
+#define CPU_POWERPC_MPC8560_v10 CPU_POWERPC_e500v2_v10
+#define CPU_POWERPC_MPC8560_v20 CPU_POWERPC_e500v2_v20
+#define CPU_POWERPC_MPC8560_v21 CPU_POWERPC_e500v2_v21
+#define CPU_POWERPC_MPC8567 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8567E CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8568 CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8568E CPU_POWERPC_e500v2_v22
+#define CPU_POWERPC_MPC8572 CPU_POWERPC_e500v2_v30
+#define CPU_POWERPC_MPC8572E CPU_POWERPC_e500v2_v30
+ /* e600 family */
+ /* e600 cores */
+ CPU_POWERPC_e600 = 0x80040010,
+ /* MPC86xx microcontrollers */
+#define CPU_POWERPC_MPC8610 CPU_POWERPC_e600
+#define CPU_POWERPC_MPC8641 CPU_POWERPC_e600
+#define CPU_POWERPC_MPC8641D CPU_POWERPC_e600
+ /* PowerPC 6xx cores */
+#define CPU_POWERPC_601 CPU_POWERPC_601_v2
+ CPU_POWERPC_601_v0 = 0x00010001,
+ CPU_POWERPC_601_v1 = 0x00010001,
+#define CPU_POWERPC_601v CPU_POWERPC_601_v2
+ CPU_POWERPC_601_v2 = 0x00010002,
+ CPU_POWERPC_602 = 0x00050100,
+ CPU_POWERPC_603 = 0x00030100,
+#define CPU_POWERPC_603E CPU_POWERPC_603E_v41
+ CPU_POWERPC_603E_v11 = 0x00060101,
+ CPU_POWERPC_603E_v12 = 0x00060102,
+ CPU_POWERPC_603E_v13 = 0x00060103,
+ CPU_POWERPC_603E_v14 = 0x00060104,
+ CPU_POWERPC_603E_v22 = 0x00060202,
+ CPU_POWERPC_603E_v3 = 0x00060300,
+ CPU_POWERPC_603E_v4 = 0x00060400,
+ CPU_POWERPC_603E_v41 = 0x00060401,
+ CPU_POWERPC_603E7t = 0x00071201,
+ CPU_POWERPC_603E7v = 0x00070100,
+ CPU_POWERPC_603E7v1 = 0x00070101,
+ CPU_POWERPC_603E7v2 = 0x00070201,
+ CPU_POWERPC_603E7 = 0x00070200,
+ CPU_POWERPC_603P = 0x00070000,
+#define CPU_POWERPC_603R CPU_POWERPC_603E7t
+ /* XXX: missing 0x00040303 (604) */
+ CPU_POWERPC_604 = 0x00040103,
+#define CPU_POWERPC_604E CPU_POWERPC_604E_v24
+ /* XXX: missing 0x00091203 */
+ /* XXX: missing 0x00092110 */
+ /* XXX: missing 0x00092120 */
+ CPU_POWERPC_604E_v10 = 0x00090100,
+ CPU_POWERPC_604E_v22 = 0x00090202,
+ CPU_POWERPC_604E_v24 = 0x00090204,
+ /* XXX: missing 0x000a0100 */
+ /* XXX: missing 0x00093102 */
+ CPU_POWERPC_604R = 0x000a0101,
+#if 0
+ CPU_POWERPC_604EV = xxx, /* XXX: same as 604R ? */
+#endif
+ /* PowerPC 740/750 cores (aka G3) */
+ /* XXX: missing 0x00084202 */
+#define CPU_POWERPC_7x0 CPU_POWERPC_7x0_v31
+ CPU_POWERPC_7x0_v10 = 0x00080100,
+ CPU_POWERPC_7x0_v20 = 0x00080200,
+ CPU_POWERPC_7x0_v21 = 0x00080201,
+ CPU_POWERPC_7x0_v22 = 0x00080202,
+ CPU_POWERPC_7x0_v30 = 0x00080300,
+ CPU_POWERPC_7x0_v31 = 0x00080301,
+ CPU_POWERPC_740E = 0x00080100,
+ CPU_POWERPC_750E = 0x00080200,
+ CPU_POWERPC_7x0P = 0x10080000,
+ /* XXX: missing 0x00087010 (CL ?) */
+#define CPU_POWERPC_750CL CPU_POWERPC_750CL_v20
+ CPU_POWERPC_750CL_v10 = 0x00087200,
+ CPU_POWERPC_750CL_v20 = 0x00087210, /* aka rev E */
+#define CPU_POWERPC_750CX CPU_POWERPC_750CX_v22
+ CPU_POWERPC_750CX_v10 = 0x00082100,
+ CPU_POWERPC_750CX_v20 = 0x00082200,
+ CPU_POWERPC_750CX_v21 = 0x00082201,
+ CPU_POWERPC_750CX_v22 = 0x00082202,
+#define CPU_POWERPC_750CXE CPU_POWERPC_750CXE_v31b
+ CPU_POWERPC_750CXE_v21 = 0x00082211,
+ CPU_POWERPC_750CXE_v22 = 0x00082212,
+ CPU_POWERPC_750CXE_v23 = 0x00082213,
+ CPU_POWERPC_750CXE_v24 = 0x00082214,
+ CPU_POWERPC_750CXE_v24b = 0x00083214,
+ CPU_POWERPC_750CXE_v30 = 0x00082310,
+ CPU_POWERPC_750CXE_v31 = 0x00082311,
+ CPU_POWERPC_750CXE_v31b = 0x00083311,
+ CPU_POWERPC_750CXR = 0x00083410,
+ CPU_POWERPC_750FL = 0x70000203,
+#define CPU_POWERPC_750FX CPU_POWERPC_750FX_v23
+ CPU_POWERPC_750FX_v10 = 0x70000100,
+ CPU_POWERPC_750FX_v20 = 0x70000200,
+ CPU_POWERPC_750FX_v21 = 0x70000201,
+ CPU_POWERPC_750FX_v22 = 0x70000202,
+ CPU_POWERPC_750FX_v23 = 0x70000203,
+ CPU_POWERPC_750GL = 0x70020102,
+#define CPU_POWERPC_750GX CPU_POWERPC_750GX_v12
+ CPU_POWERPC_750GX_v10 = 0x70020100,
+ CPU_POWERPC_750GX_v11 = 0x70020101,
+ CPU_POWERPC_750GX_v12 = 0x70020102,
+#define CPU_POWERPC_750L CPU_POWERPC_750L_v32 /* Aka LoneStar */
+ CPU_POWERPC_750L_v20 = 0x00088200,
+ CPU_POWERPC_750L_v21 = 0x00088201,
+ CPU_POWERPC_750L_v22 = 0x00088202,
+ CPU_POWERPC_750L_v30 = 0x00088300,
+ CPU_POWERPC_750L_v32 = 0x00088302,
+ /* PowerPC 745/755 cores */
+#define CPU_POWERPC_7x5 CPU_POWERPC_7x5_v28
+ CPU_POWERPC_7x5_v10 = 0x00083100,
+ CPU_POWERPC_7x5_v11 = 0x00083101,
+ CPU_POWERPC_7x5_v20 = 0x00083200,
+ CPU_POWERPC_7x5_v21 = 0x00083201,
+ CPU_POWERPC_7x5_v22 = 0x00083202, /* aka D */
+ CPU_POWERPC_7x5_v23 = 0x00083203, /* aka E */
+ CPU_POWERPC_7x5_v24 = 0x00083204,
+ CPU_POWERPC_7x5_v25 = 0x00083205,
+ CPU_POWERPC_7x5_v26 = 0x00083206,
+ CPU_POWERPC_7x5_v27 = 0x00083207,
+ CPU_POWERPC_7x5_v28 = 0x00083208,
+#if 0
+ CPU_POWERPC_7x5P = xxx,
+#endif
+ /* PowerPC 74xx cores (aka G4) */
+ /* XXX: missing 0x000C1101 */
+#define CPU_POWERPC_7400 CPU_POWERPC_7400_v29
+ CPU_POWERPC_7400_v10 = 0x000C0100,
+ CPU_POWERPC_7400_v11 = 0x000C0101,
+ CPU_POWERPC_7400_v20 = 0x000C0200,
+ CPU_POWERPC_7400_v21 = 0x000C0201,
+ CPU_POWERPC_7400_v22 = 0x000C0202,
+ CPU_POWERPC_7400_v26 = 0x000C0206,
+ CPU_POWERPC_7400_v27 = 0x000C0207,
+ CPU_POWERPC_7400_v28 = 0x000C0208,
+ CPU_POWERPC_7400_v29 = 0x000C0209,
+#define CPU_POWERPC_7410 CPU_POWERPC_7410_v14
+ CPU_POWERPC_7410_v10 = 0x800C1100,
+ CPU_POWERPC_7410_v11 = 0x800C1101,
+ CPU_POWERPC_7410_v12 = 0x800C1102, /* aka C */
+ CPU_POWERPC_7410_v13 = 0x800C1103, /* aka D */
+ CPU_POWERPC_7410_v14 = 0x800C1104, /* aka E */
+#define CPU_POWERPC_7448 CPU_POWERPC_7448_v21
+ CPU_POWERPC_7448_v10 = 0x80040100,
+ CPU_POWERPC_7448_v11 = 0x80040101,
+ CPU_POWERPC_7448_v20 = 0x80040200,
+ CPU_POWERPC_7448_v21 = 0x80040201,
+#define CPU_POWERPC_7450 CPU_POWERPC_7450_v21
+ CPU_POWERPC_7450_v10 = 0x80000100,
+ CPU_POWERPC_7450_v11 = 0x80000101,
+ CPU_POWERPC_7450_v12 = 0x80000102,
+ CPU_POWERPC_7450_v20 = 0x80000200, /* aka A, B, C, D: 2.04 */
+ CPU_POWERPC_7450_v21 = 0x80000201, /* aka E */
+#define CPU_POWERPC_74x1 CPU_POWERPC_74x1_v23
+ CPU_POWERPC_74x1_v23 = 0x80000203, /* aka G: 2.3 */
+ /* XXX: this entry might be a bug in some documentation */
+ CPU_POWERPC_74x1_v210 = 0x80000210, /* aka G: 2.3 ? */
+#define CPU_POWERPC_74x5 CPU_POWERPC_74x5_v32
+ CPU_POWERPC_74x5_v10 = 0x80010100,
+ /* XXX: missing 0x80010200 */
+ CPU_POWERPC_74x5_v21 = 0x80010201, /* aka C: 2.1 */
+ CPU_POWERPC_74x5_v32 = 0x80010302,
+ CPU_POWERPC_74x5_v33 = 0x80010303, /* aka F: 3.3 */
+ CPU_POWERPC_74x5_v34 = 0x80010304, /* aka G: 3.4 */
+#define CPU_POWERPC_74x7 CPU_POWERPC_74x7_v12
+ CPU_POWERPC_74x7_v10 = 0x80020100, /* aka A: 1.0 */
+ CPU_POWERPC_74x7_v11 = 0x80020101, /* aka B: 1.1 */
+ CPU_POWERPC_74x7_v12 = 0x80020102, /* aka C: 1.2 */
+#define CPU_POWERPC_74x7A CPU_POWERPC_74x7A_v12
+ CPU_POWERPC_74x7A_v10 = 0x80030100, /* aka A: 1.0 */
+ CPU_POWERPC_74x7A_v11 = 0x80030101, /* aka B: 1.1 */
+ CPU_POWERPC_74x7A_v12 = 0x80030102, /* aka C: 1.2 */
+ /* 64 bits PowerPC */
+#if defined(TARGET_PPC64)
+ CPU_POWERPC_620 = 0x00140000,
+ CPU_POWERPC_630 = 0x00400000,
+ CPU_POWERPC_631 = 0x00410104,
+ CPU_POWERPC_POWER4 = 0x00350000,
+ CPU_POWERPC_POWER4P = 0x00380000,
+ /* XXX: missing 0x003A0201 */
+ CPU_POWERPC_POWER5 = 0x003A0203,
+#define CPU_POWERPC_POWER5GR CPU_POWERPC_POWER5
+ CPU_POWERPC_POWER5P = 0x003B0000,
+#define CPU_POWERPC_POWER5GS CPU_POWERPC_POWER5P
+ CPU_POWERPC_POWER6 = 0x003E0000,
+ CPU_POWERPC_POWER6_5 = 0x0F000001, /* POWER6 in POWER5 mode */
+ CPU_POWERPC_POWER6A = 0x0F000002,
+ CPU_POWERPC_970 = 0x00390202,
+#define CPU_POWERPC_970FX CPU_POWERPC_970FX_v31
+ CPU_POWERPC_970FX_v10 = 0x00391100,
+ CPU_POWERPC_970FX_v20 = 0x003C0200,
+ CPU_POWERPC_970FX_v21 = 0x003C0201,
+ CPU_POWERPC_970FX_v30 = 0x003C0300,
+ CPU_POWERPC_970FX_v31 = 0x003C0301,
+ CPU_POWERPC_970GX = 0x00450000,
+#define CPU_POWERPC_970MP CPU_POWERPC_970MP_v11
+ CPU_POWERPC_970MP_v10 = 0x00440100,
+ CPU_POWERPC_970MP_v11 = 0x00440101,
+#define CPU_POWERPC_CELL CPU_POWERPC_CELL_v32
+ CPU_POWERPC_CELL_v10 = 0x00700100,
+ CPU_POWERPC_CELL_v20 = 0x00700400,
+ CPU_POWERPC_CELL_v30 = 0x00700500,
+ CPU_POWERPC_CELL_v31 = 0x00700501,
+#define CPU_POWERPC_CELL_v32 CPU_POWERPC_CELL_v31
+ CPU_POWERPC_RS64 = 0x00330000,
+ CPU_POWERPC_RS64II = 0x00340000,
+ CPU_POWERPC_RS64III = 0x00360000,
+ CPU_POWERPC_RS64IV = 0x00370000,
+#endif /* defined(TARGET_PPC64) */
+ /* Original POWER */
+ /* XXX: should be POWER (RIOS), RSC3308, RSC4608,
+ * POWER2 (RIOS2) & RSC2 (P2SC) here
+ */
+#if 0
+ CPU_POWER = xxx, /* 0x20000 ? 0x30000 for RSC ? */
+#endif
+#if 0
+ CPU_POWER2 = xxx, /* 0x40000 ? */
+#endif
+ /* PA Semi core */
+ CPU_POWERPC_PA6T = 0x00900000,
+};
+
+/* System version register (used on MPC 8xxx) */
+enum {
+ POWERPC_SVR_NONE = 0x00000000,
+#define POWERPC_SVR_52xx POWERPC_SVR_5200
+#define POWERPC_SVR_5200 POWERPC_SVR_5200_v12
+ POWERPC_SVR_5200_v10 = 0x80110010,
+ POWERPC_SVR_5200_v11 = 0x80110011,
+ POWERPC_SVR_5200_v12 = 0x80110012,
+#define POWERPC_SVR_5200B POWERPC_SVR_5200B_v21
+ POWERPC_SVR_5200B_v20 = 0x80110020,
+ POWERPC_SVR_5200B_v21 = 0x80110021,
+#define POWERPC_SVR_55xx POWERPC_SVR_5567
+#if 0
+ POWERPC_SVR_5533 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5534 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5553 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5554 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5561 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5565 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5566 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_5567 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8313 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8313E = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8314 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8314E = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8315 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8315E = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8321 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8321E = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8323 = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8323E = xxx,
+#endif
+ POWERPC_SVR_8343 = 0x80570010,
+ POWERPC_SVR_8343A = 0x80570030,
+ POWERPC_SVR_8343E = 0x80560010,
+ POWERPC_SVR_8343EA = 0x80560030,
+#define POWERPC_SVR_8347 POWERPC_SVR_8347T
+ POWERPC_SVR_8347P = 0x80550010, /* PBGA package */
+ POWERPC_SVR_8347T = 0x80530010, /* TBGA package */
+#define POWERPC_SVR_8347A POWERPC_SVR_8347AT
+ POWERPC_SVR_8347AP = 0x80550030, /* PBGA package */
+ POWERPC_SVR_8347AT = 0x80530030, /* TBGA package */
+#define POWERPC_SVR_8347E POWERPC_SVR_8347ET
+ POWERPC_SVR_8347EP = 0x80540010, /* PBGA package */
+ POWERPC_SVR_8347ET = 0x80520010, /* TBGA package */
+#define POWERPC_SVR_8347EA POWERPC_SVR_8347EAT
+ POWERPC_SVR_8347EAP = 0x80540030, /* PBGA package */
+ POWERPC_SVR_8347EAT = 0x80520030, /* TBGA package */
+ POWERPC_SVR_8349 = 0x80510010,
+ POWERPC_SVR_8349A = 0x80510030,
+ POWERPC_SVR_8349E = 0x80500010,
+ POWERPC_SVR_8349EA = 0x80500030,
+#if 0
+ POWERPC_SVR_8358E = xxx,
+#endif
+#if 0
+ POWERPC_SVR_8360E = xxx,
+#endif
+#define POWERPC_SVR_E500 0x40000000
+ POWERPC_SVR_8377 = 0x80C70010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8377E = 0x80C60010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8378 = 0x80C50010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8378E = 0x80C40010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8379 = 0x80C30010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8379E = 0x80C00010 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8533 POWERPC_SVR_8533_v11
+ POWERPC_SVR_8533_v10 = 0x80340010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8533_v11 = 0x80340011 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8533E POWERPC_SVR_8533E_v11
+ POWERPC_SVR_8533E_v10 = 0x803C0010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8533E_v11 = 0x803C0011 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8540 POWERPC_SVR_8540_v21
+ POWERPC_SVR_8540_v10 = 0x80300010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8540_v20 = 0x80300020 | POWERPC_SVR_E500,
+ POWERPC_SVR_8540_v21 = 0x80300021 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8541 POWERPC_SVR_8541_v11
+ POWERPC_SVR_8541_v10 = 0x80720010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8541_v11 = 0x80720011 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8541E POWERPC_SVR_8541E_v11
+ POWERPC_SVR_8541E_v10 = 0x807A0010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8541E_v11 = 0x807A0011 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8543 POWERPC_SVR_8543_v21
+ POWERPC_SVR_8543_v10 = 0x80320010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8543_v11 = 0x80320011 | POWERPC_SVR_E500,
+ POWERPC_SVR_8543_v20 = 0x80320020 | POWERPC_SVR_E500,
+ POWERPC_SVR_8543_v21 = 0x80320021 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8543E POWERPC_SVR_8543E_v21
+ POWERPC_SVR_8543E_v10 = 0x803A0010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8543E_v11 = 0x803A0011 | POWERPC_SVR_E500,
+ POWERPC_SVR_8543E_v20 = 0x803A0020 | POWERPC_SVR_E500,
+ POWERPC_SVR_8543E_v21 = 0x803A0021 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8544 POWERPC_SVR_8544_v11
+ POWERPC_SVR_8544_v10 = 0x80340110 | POWERPC_SVR_E500,
+ POWERPC_SVR_8544_v11 = 0x80340111 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8544E POWERPC_SVR_8544E_v11
+ POWERPC_SVR_8544E_v10 = 0x803C0110 | POWERPC_SVR_E500,
+ POWERPC_SVR_8544E_v11 = 0x803C0111 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8545 POWERPC_SVR_8545_v21
+ POWERPC_SVR_8545_v20 = 0x80310220 | POWERPC_SVR_E500,
+ POWERPC_SVR_8545_v21 = 0x80310221 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8545E POWERPC_SVR_8545E_v21
+ POWERPC_SVR_8545E_v20 = 0x80390220 | POWERPC_SVR_E500,
+ POWERPC_SVR_8545E_v21 = 0x80390221 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8547E POWERPC_SVR_8547E_v21
+ POWERPC_SVR_8547E_v20 = 0x80390120 | POWERPC_SVR_E500,
+ POWERPC_SVR_8547E_v21 = 0x80390121 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8548 POWERPC_SVR_8548_v21
+ POWERPC_SVR_8548_v10 = 0x80310010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8548_v11 = 0x80310011 | POWERPC_SVR_E500,
+ POWERPC_SVR_8548_v20 = 0x80310020 | POWERPC_SVR_E500,
+ POWERPC_SVR_8548_v21 = 0x80310021 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8548E POWERPC_SVR_8548E_v21
+ POWERPC_SVR_8548E_v10 = 0x80390010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8548E_v11 = 0x80390011 | POWERPC_SVR_E500,
+ POWERPC_SVR_8548E_v20 = 0x80390020 | POWERPC_SVR_E500,
+ POWERPC_SVR_8548E_v21 = 0x80390021 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8555 POWERPC_SVR_8555_v11
+ POWERPC_SVR_8555_v10 = 0x80710010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8555_v11 = 0x80710011 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8555E POWERPC_SVR_8555_v11
+ POWERPC_SVR_8555E_v10 = 0x80790010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8555E_v11 = 0x80790011 | POWERPC_SVR_E500,
+#define POWERPC_SVR_8560 POWERPC_SVR_8560_v21
+ POWERPC_SVR_8560_v10 = 0x80700010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8560_v20 = 0x80700020 | POWERPC_SVR_E500,
+ POWERPC_SVR_8560_v21 = 0x80700021 | POWERPC_SVR_E500,
+ POWERPC_SVR_8567 = 0x80750111 | POWERPC_SVR_E500,
+ POWERPC_SVR_8567E = 0x807D0111 | POWERPC_SVR_E500,
+ POWERPC_SVR_8568 = 0x80750011 | POWERPC_SVR_E500,
+ POWERPC_SVR_8568E = 0x807D0011 | POWERPC_SVR_E500,
+ POWERPC_SVR_8572 = 0x80E00010 | POWERPC_SVR_E500,
+ POWERPC_SVR_8572E = 0x80E80010 | POWERPC_SVR_E500,
+#if 0
+ POWERPC_SVR_8610 = xxx,
+#endif
+ POWERPC_SVR_8641 = 0x80900021,
+ POWERPC_SVR_8641D = 0x80900121,
+};
+
+/*****************************************************************************/
+/* PowerPC CPU definitions */
+#define POWERPC_DEF_SVR(_name, _pvr, _svr, _type) \
+ { \
+ .name = _name, \
+ .pvr = _pvr, \
+ .svr = _svr, \
+ .insns_flags = glue(POWERPC_INSNS_,_type), \
+ .msr_mask = glue(POWERPC_MSRM_,_type), \
+ .mmu_model = glue(POWERPC_MMU_,_type), \
+ .excp_model = glue(POWERPC_EXCP_,_type), \
+ .bus_model = glue(POWERPC_INPUT_,_type), \
+ .bfd_mach = glue(POWERPC_BFDM_,_type), \
+ .flags = glue(POWERPC_FLAG_,_type), \
+ .init_proc = &glue(init_proc_,_type), \
+ .check_pow = &glue(check_pow_,_type), \
+ }
+#define POWERPC_DEF(_name, _pvr, _type) \
+POWERPC_DEF_SVR(_name, _pvr, POWERPC_SVR_NONE, _type)
+
+static const ppc_def_t ppc_defs[] = {
+ /* Embedded PowerPC */
+ /* PowerPC 401 family */
+ /* Generic PowerPC 401 */
+ POWERPC_DEF("401", CPU_POWERPC_401, 401),
+ /* PowerPC 401 cores */
+ /* PowerPC 401A1 */
+ POWERPC_DEF("401A1", CPU_POWERPC_401A1, 401),
+ /* PowerPC 401B2 */
+ POWERPC_DEF("401B2", CPU_POWERPC_401B2, 401x2),
+#if defined (TODO)
+ /* PowerPC 401B3 */
+ POWERPC_DEF("401B3", CPU_POWERPC_401B3, 401x3),
+#endif
+ /* PowerPC 401C2 */
+ POWERPC_DEF("401C2", CPU_POWERPC_401C2, 401x2),
+ /* PowerPC 401D2 */
+ POWERPC_DEF("401D2", CPU_POWERPC_401D2, 401x2),
+ /* PowerPC 401E2 */
+ POWERPC_DEF("401E2", CPU_POWERPC_401E2, 401x2),
+ /* PowerPC 401F2 */
+ POWERPC_DEF("401F2", CPU_POWERPC_401F2, 401x2),
+ /* PowerPC 401G2 */
+ /* XXX: to be checked */
+ POWERPC_DEF("401G2", CPU_POWERPC_401G2, 401x2),
+ /* PowerPC 401 microcontrolers */
+#if defined (TODO)
+ /* PowerPC 401GF */
+ POWERPC_DEF("401GF", CPU_POWERPC_401GF, 401),
+#endif
+ /* IOP480 (401 microcontroler) */
+ POWERPC_DEF("IOP480", CPU_POWERPC_IOP480, IOP480),
+ /* IBM Processor for Network Resources */
+ POWERPC_DEF("Cobra", CPU_POWERPC_COBRA, 401),
+#if defined (TODO)
+ POWERPC_DEF("Xipchip", CPU_POWERPC_XIPCHIP, 401),
+#endif
+ /* PowerPC 403 family */
+ /* Generic PowerPC 403 */
+ POWERPC_DEF("403", CPU_POWERPC_403, 403),
+ /* PowerPC 403 microcontrolers */
+ /* PowerPC 403 GA */
+ POWERPC_DEF("403GA", CPU_POWERPC_403GA, 403),
+ /* PowerPC 403 GB */
+ POWERPC_DEF("403GB", CPU_POWERPC_403GB, 403),
+ /* PowerPC 403 GC */
+ POWERPC_DEF("403GC", CPU_POWERPC_403GC, 403),
+ /* PowerPC 403 GCX */
+ POWERPC_DEF("403GCX", CPU_POWERPC_403GCX, 403GCX),
+#if defined (TODO)
+ /* PowerPC 403 GP */
+ POWERPC_DEF("403GP", CPU_POWERPC_403GP, 403),
+#endif
+ /* PowerPC 405 family */
+ /* Generic PowerPC 405 */
+ POWERPC_DEF("405", CPU_POWERPC_405, 405),
+ /* PowerPC 405 cores */
+#if defined (TODO)
+ /* PowerPC 405 A3 */
+ POWERPC_DEF("405A3", CPU_POWERPC_405A3, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 A4 */
+ POWERPC_DEF("405A4", CPU_POWERPC_405A4, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 B3 */
+ POWERPC_DEF("405B3", CPU_POWERPC_405B3, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 B4 */
+ POWERPC_DEF("405B4", CPU_POWERPC_405B4, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 C3 */
+ POWERPC_DEF("405C3", CPU_POWERPC_405C3, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 C4 */
+ POWERPC_DEF("405C4", CPU_POWERPC_405C4, 405),
+#endif
+ /* PowerPC 405 D2 */
+ POWERPC_DEF("405D2", CPU_POWERPC_405D2, 405),
+#if defined (TODO)
+ /* PowerPC 405 D3 */
+ POWERPC_DEF("405D3", CPU_POWERPC_405D3, 405),
+#endif
+ /* PowerPC 405 D4 */
+ POWERPC_DEF("405D4", CPU_POWERPC_405D4, 405),
+#if defined (TODO)
+ /* PowerPC 405 D5 */
+ POWERPC_DEF("405D5", CPU_POWERPC_405D5, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 E4 */
+ POWERPC_DEF("405E4", CPU_POWERPC_405E4, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 F4 */
+ POWERPC_DEF("405F4", CPU_POWERPC_405F4, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 F5 */
+ POWERPC_DEF("405F5", CPU_POWERPC_405F5, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC 405 F6 */
+ POWERPC_DEF("405F6", CPU_POWERPC_405F6, 405),
+#endif
+ /* PowerPC 405 microcontrolers */
+ /* PowerPC 405 CR */
+ POWERPC_DEF("405CR", CPU_POWERPC_405CR, 405),
+ /* PowerPC 405 CRa */
+ POWERPC_DEF("405CRa", CPU_POWERPC_405CRa, 405),
+ /* PowerPC 405 CRb */
+ POWERPC_DEF("405CRb", CPU_POWERPC_405CRb, 405),
+ /* PowerPC 405 CRc */
+ POWERPC_DEF("405CRc", CPU_POWERPC_405CRc, 405),
+ /* PowerPC 405 EP */
+ POWERPC_DEF("405EP", CPU_POWERPC_405EP, 405),
+#if defined(TODO)
+ /* PowerPC 405 EXr */
+ POWERPC_DEF("405EXr", CPU_POWERPC_405EXr, 405),
+#endif
+ /* PowerPC 405 EZ */
+ POWERPC_DEF("405EZ", CPU_POWERPC_405EZ, 405),
+#if defined(TODO)
+ /* PowerPC 405 FX */
+ POWERPC_DEF("405FX", CPU_POWERPC_405FX, 405),
+#endif
+ /* PowerPC 405 GP */
+ POWERPC_DEF("405GP", CPU_POWERPC_405GP, 405),
+ /* PowerPC 405 GPa */
+ POWERPC_DEF("405GPa", CPU_POWERPC_405GPa, 405),
+ /* PowerPC 405 GPb */
+ POWERPC_DEF("405GPb", CPU_POWERPC_405GPb, 405),
+ /* PowerPC 405 GPc */
+ POWERPC_DEF("405GPc", CPU_POWERPC_405GPc, 405),
+ /* PowerPC 405 GPd */
+ POWERPC_DEF("405GPd", CPU_POWERPC_405GPd, 405),
+ /* PowerPC 405 GPe */
+ POWERPC_DEF("405GPe", CPU_POWERPC_405GPe, 405),
+ /* PowerPC 405 GPR */
+ POWERPC_DEF("405GPR", CPU_POWERPC_405GPR, 405),
+#if defined(TODO)
+ /* PowerPC 405 H */
+ POWERPC_DEF("405H", CPU_POWERPC_405H, 405),
+#endif
+#if defined(TODO)
+ /* PowerPC 405 L */
+ POWERPC_DEF("405L", CPU_POWERPC_405L, 405),
+#endif
+ /* PowerPC 405 LP */
+ POWERPC_DEF("405LP", CPU_POWERPC_405LP, 405),
+#if defined(TODO)
+ /* PowerPC 405 PM */
+ POWERPC_DEF("405PM", CPU_POWERPC_405PM, 405),
+#endif
+#if defined(TODO)
+ /* PowerPC 405 PS */
+ POWERPC_DEF("405PS", CPU_POWERPC_405PS, 405),
+#endif
+#if defined(TODO)
+ /* PowerPC 405 S */
+ POWERPC_DEF("405S", CPU_POWERPC_405S, 405),
+#endif
+ /* Npe405 H */
+ POWERPC_DEF("Npe405H", CPU_POWERPC_NPE405H, 405),
+ /* Npe405 H2 */
+ POWERPC_DEF("Npe405H2", CPU_POWERPC_NPE405H2, 405),
+ /* Npe405 L */
+ POWERPC_DEF("Npe405L", CPU_POWERPC_NPE405L, 405),
+ /* Npe4GS3 */
+ POWERPC_DEF("Npe4GS3", CPU_POWERPC_NPE4GS3, 405),
+#if defined (TODO)
+ POWERPC_DEF("Npcxx1", CPU_POWERPC_NPCxx1, 405),
+#endif
+#if defined (TODO)
+ POWERPC_DEF("Npr161", CPU_POWERPC_NPR161, 405),
+#endif
+#if defined (TODO)
+ /* PowerPC LC77700 (Sanyo) */
+ POWERPC_DEF("LC77700", CPU_POWERPC_LC77700, 405),
+#endif
+ /* PowerPC 401/403/405 based set-top-box microcontrolers */
+#if defined (TODO)
+ /* STB010000 */
+ POWERPC_DEF("STB01000", CPU_POWERPC_STB01000, 401x2),
+#endif
+#if defined (TODO)
+ /* STB01010 */
+ POWERPC_DEF("STB01010", CPU_POWERPC_STB01010, 401x2),
+#endif
+#if defined (TODO)
+ /* STB0210 */
+ POWERPC_DEF("STB0210", CPU_POWERPC_STB0210, 401x3),
+#endif
+ /* STB03xx */
+ POWERPC_DEF("STB03", CPU_POWERPC_STB03, 405),
+#if defined (TODO)
+ /* STB043x */
+ POWERPC_DEF("STB043", CPU_POWERPC_STB043, 405),
+#endif
+#if defined (TODO)
+ /* STB045x */
+ POWERPC_DEF("STB045", CPU_POWERPC_STB045, 405),
+#endif
+ /* STB04xx */
+ POWERPC_DEF("STB04", CPU_POWERPC_STB04, 405),
+ /* STB25xx */
+ POWERPC_DEF("STB25", CPU_POWERPC_STB25, 405),
+#if defined (TODO)
+ /* STB130 */
+ POWERPC_DEF("STB130", CPU_POWERPC_STB130, 405),
+#endif
+ /* Xilinx PowerPC 405 cores */
+ POWERPC_DEF("x2vp4", CPU_POWERPC_X2VP4, 405),
+ POWERPC_DEF("x2vp7", CPU_POWERPC_X2VP7, 405),
+ POWERPC_DEF("x2vp20", CPU_POWERPC_X2VP20, 405),
+ POWERPC_DEF("x2vp50", CPU_POWERPC_X2VP50, 405),
+#if defined (TODO)
+ /* Zarlink ZL10310 */
+ POWERPC_DEF("zl10310", CPU_POWERPC_ZL10310, 405),
+#endif
+#if defined (TODO)
+ /* Zarlink ZL10311 */
+ POWERPC_DEF("zl10311", CPU_POWERPC_ZL10311, 405),
+#endif
+#if defined (TODO)
+ /* Zarlink ZL10320 */
+ POWERPC_DEF("zl10320", CPU_POWERPC_ZL10320, 405),
+#endif
+#if defined (TODO)
+ /* Zarlink ZL10321 */
+ POWERPC_DEF("zl10321", CPU_POWERPC_ZL10321, 405),
+#endif
+ /* PowerPC 440 family */
+#if defined(TODO_USER_ONLY)
+ /* Generic PowerPC 440 */
+ POWERPC_DEF("440", CPU_POWERPC_440, 440GP),
+#endif
+ /* PowerPC 440 cores */
+#if defined (TODO)
+ /* PowerPC 440 A4 */
+ POWERPC_DEF("440A4", CPU_POWERPC_440A4, 440x4),
+#endif
+ /* PowerPC 440 Xilinx 5 */
+ POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5),
+#if defined (TODO)
+ /* PowerPC 440 A5 */
+ POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5),
+#endif
+#if defined (TODO)
+ /* PowerPC 440 B4 */
+ POWERPC_DEF("440B4", CPU_POWERPC_440B4, 440x4),
+#endif
+#if defined (TODO)
+ /* PowerPC 440 G4 */
+ POWERPC_DEF("440G4", CPU_POWERPC_440G4, 440x4),
+#endif
+#if defined (TODO)
+ /* PowerPC 440 F5 */
+ POWERPC_DEF("440F5", CPU_POWERPC_440F5, 440x5),
+#endif
+#if defined (TODO)
+ /* PowerPC 440 G5 */
+ POWERPC_DEF("440G5", CPU_POWERPC_440G5, 440x5),
+#endif
+#if defined (TODO)
+ /* PowerPC 440H4 */
+ POWERPC_DEF("440H4", CPU_POWERPC_440H4, 440x4),
+#endif
+#if defined (TODO)
+ /* PowerPC 440H6 */
+ POWERPC_DEF("440H6", CPU_POWERPC_440H6, 440Gx5),
+#endif
+ /* PowerPC 440 microcontrolers */
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 EP */
+ POWERPC_DEF("440EP", CPU_POWERPC_440EP, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 EPa */
+ POWERPC_DEF("440EPa", CPU_POWERPC_440EPa, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 EPb */
+ POWERPC_DEF("440EPb", CPU_POWERPC_440EPb, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 EPX */
+ POWERPC_DEF("440EPX", CPU_POWERPC_440EPX, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GP */
+ POWERPC_DEF("440GP", CPU_POWERPC_440GP, 440GP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GPb */
+ POWERPC_DEF("440GPb", CPU_POWERPC_440GPb, 440GP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GPc */
+ POWERPC_DEF("440GPc", CPU_POWERPC_440GPc, 440GP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GR */
+ POWERPC_DEF("440GR", CPU_POWERPC_440GR, 440x5),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GRa */
+ POWERPC_DEF("440GRa", CPU_POWERPC_440GRa, 440x5),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GRX */
+ POWERPC_DEF("440GRX", CPU_POWERPC_440GRX, 440x5),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GX */
+ POWERPC_DEF("440GX", CPU_POWERPC_440GX, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GXa */
+ POWERPC_DEF("440GXa", CPU_POWERPC_440GXa, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GXb */
+ POWERPC_DEF("440GXb", CPU_POWERPC_440GXb, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GXc */
+ POWERPC_DEF("440GXc", CPU_POWERPC_440GXc, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 GXf */
+ POWERPC_DEF("440GXf", CPU_POWERPC_440GXf, 440EP),
+#endif
+#if defined(TODO)
+ /* PowerPC 440 S */
+ POWERPC_DEF("440S", CPU_POWERPC_440S, 440),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 SP */
+ POWERPC_DEF("440SP", CPU_POWERPC_440SP, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 SP2 */
+ POWERPC_DEF("440SP2", CPU_POWERPC_440SP2, 440EP),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* PowerPC 440 SPE */
+ POWERPC_DEF("440SPE", CPU_POWERPC_440SPE, 440EP),
+#endif
+ /* PowerPC 460 family */
+#if defined (TODO)
+ /* Generic PowerPC 464 */
+ POWERPC_DEF("464", CPU_POWERPC_464, 460),
+#endif
+ /* PowerPC 464 microcontrolers */
+#if defined (TODO)
+ /* PowerPC 464H90 */
+ POWERPC_DEF("464H90", CPU_POWERPC_464H90, 460),
+#endif
+#if defined (TODO)
+ /* PowerPC 464H90F */
+ POWERPC_DEF("464H90F", CPU_POWERPC_464H90F, 460F),
+#endif
+ /* Freescale embedded PowerPC cores */
+ /* MPC5xx family (aka RCPU) */
+#if defined(TODO_USER_ONLY)
+ /* Generic MPC5xx core */
+ POWERPC_DEF("MPC5xx", CPU_POWERPC_MPC5xx, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* Codename for MPC5xx core */
+ POWERPC_DEF("RCPU", CPU_POWERPC_MPC5xx, MPC5xx),
+#endif
+ /* MPC5xx microcontrollers */
+#if defined(TODO_USER_ONLY)
+ /* MGT560 */
+ POWERPC_DEF("MGT560", CPU_POWERPC_MGT560, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC509 */
+ POWERPC_DEF("MPC509", CPU_POWERPC_MPC509, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC533 */
+ POWERPC_DEF("MPC533", CPU_POWERPC_MPC533, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC534 */
+ POWERPC_DEF("MPC534", CPU_POWERPC_MPC534, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC555 */
+ POWERPC_DEF("MPC555", CPU_POWERPC_MPC555, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC556 */
+ POWERPC_DEF("MPC556", CPU_POWERPC_MPC556, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC560 */
+ POWERPC_DEF("MPC560", CPU_POWERPC_MPC560, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC561 */
+ POWERPC_DEF("MPC561", CPU_POWERPC_MPC561, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC562 */
+ POWERPC_DEF("MPC562", CPU_POWERPC_MPC562, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC563 */
+ POWERPC_DEF("MPC563", CPU_POWERPC_MPC563, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC564 */
+ POWERPC_DEF("MPC564", CPU_POWERPC_MPC564, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC565 */
+ POWERPC_DEF("MPC565", CPU_POWERPC_MPC565, MPC5xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC566 */
+ POWERPC_DEF("MPC566", CPU_POWERPC_MPC566, MPC5xx),
+#endif
+ /* MPC8xx family (aka PowerQUICC) */
+#if defined(TODO_USER_ONLY)
+ /* Generic MPC8xx core */
+ POWERPC_DEF("MPC8xx", CPU_POWERPC_MPC8xx, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* Codename for MPC8xx core */
+ POWERPC_DEF("PowerQUICC", CPU_POWERPC_MPC8xx, MPC8xx),
+#endif
+ /* MPC8xx microcontrollers */
+#if defined(TODO_USER_ONLY)
+ /* MGT823 */
+ POWERPC_DEF("MGT823", CPU_POWERPC_MGT823, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC821 */
+ POWERPC_DEF("MPC821", CPU_POWERPC_MPC821, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC823 */
+ POWERPC_DEF("MPC823", CPU_POWERPC_MPC823, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC850 */
+ POWERPC_DEF("MPC850", CPU_POWERPC_MPC850, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC852T */
+ POWERPC_DEF("MPC852T", CPU_POWERPC_MPC852T, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC855T */
+ POWERPC_DEF("MPC855T", CPU_POWERPC_MPC855T, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC857 */
+ POWERPC_DEF("MPC857", CPU_POWERPC_MPC857, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC859 */
+ POWERPC_DEF("MPC859", CPU_POWERPC_MPC859, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC860 */
+ POWERPC_DEF("MPC860", CPU_POWERPC_MPC860, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC862 */
+ POWERPC_DEF("MPC862", CPU_POWERPC_MPC862, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC866 */
+ POWERPC_DEF("MPC866", CPU_POWERPC_MPC866, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC870 */
+ POWERPC_DEF("MPC870", CPU_POWERPC_MPC870, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC875 */
+ POWERPC_DEF("MPC875", CPU_POWERPC_MPC875, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC880 */
+ POWERPC_DEF("MPC880", CPU_POWERPC_MPC880, MPC8xx),
+#endif
+#if defined(TODO_USER_ONLY)
+ /* MPC885 */
+ POWERPC_DEF("MPC885", CPU_POWERPC_MPC885, MPC8xx),
+#endif
+ /* MPC82xx family (aka PowerQUICC-II) */
+ /* Generic MPC52xx core */
+ POWERPC_DEF_SVR("MPC52xx",
+ CPU_POWERPC_MPC52xx, POWERPC_SVR_52xx, G2LE),
+ /* Generic MPC82xx core */
+ POWERPC_DEF("MPC82xx", CPU_POWERPC_MPC82xx, G2),
+ /* Codename for MPC82xx */
+ POWERPC_DEF("PowerQUICC-II", CPU_POWERPC_MPC82xx, G2),
+ /* PowerPC G2 core */
+ POWERPC_DEF("G2", CPU_POWERPC_G2, G2),
+ /* PowerPC G2 H4 core */
+ POWERPC_DEF("G2H4", CPU_POWERPC_G2H4, G2),
+ /* PowerPC G2 GP core */
+ POWERPC_DEF("G2GP", CPU_POWERPC_G2gp, G2),
+ /* PowerPC G2 LS core */
+ POWERPC_DEF("G2LS", CPU_POWERPC_G2ls, G2),
+ /* PowerPC G2 HiP3 core */
+ POWERPC_DEF("G2HiP3", CPU_POWERPC_G2_HIP3, G2),
+ /* PowerPC G2 HiP4 core */
+ POWERPC_DEF("G2HiP4", CPU_POWERPC_G2_HIP4, G2),
+ /* PowerPC MPC603 core */
+ POWERPC_DEF("MPC603", CPU_POWERPC_MPC603, 603E),
+ /* PowerPC G2le core (same as G2 plus little-endian mode support) */
+ POWERPC_DEF("G2le", CPU_POWERPC_G2LE, G2LE),
+ /* PowerPC G2LE GP core */
+ POWERPC_DEF("G2leGP", CPU_POWERPC_G2LEgp, G2LE),
+ /* PowerPC G2LE LS core */
+ POWERPC_DEF("G2leLS", CPU_POWERPC_G2LEls, G2LE),
+ /* PowerPC G2LE GP1 core */
+ POWERPC_DEF("G2leGP1", CPU_POWERPC_G2LEgp1, G2LE),
+ /* PowerPC G2LE GP3 core */
+ POWERPC_DEF("G2leGP3", CPU_POWERPC_G2LEgp1, G2LE),
+ /* PowerPC MPC603 microcontrollers */
+ /* MPC8240 */
+ POWERPC_DEF("MPC8240", CPU_POWERPC_MPC8240, 603E),
+ /* PowerPC G2 microcontrollers */
+#if defined(TODO)
+ /* MPC5121 */
+ POWERPC_DEF_SVR("MPC5121",
+ CPU_POWERPC_MPC5121, POWERPC_SVR_5121, G2LE),
+#endif
+ /* MPC5200 */
+ POWERPC_DEF_SVR("MPC5200",
+ CPU_POWERPC_MPC5200, POWERPC_SVR_5200, G2LE),
+ /* MPC5200 v1.0 */
+ POWERPC_DEF_SVR("MPC5200_v10",
+ CPU_POWERPC_MPC5200_v10, POWERPC_SVR_5200_v10, G2LE),
+ /* MPC5200 v1.1 */
+ POWERPC_DEF_SVR("MPC5200_v11",
+ CPU_POWERPC_MPC5200_v11, POWERPC_SVR_5200_v11, G2LE),
+ /* MPC5200 v1.2 */
+ POWERPC_DEF_SVR("MPC5200_v12",
+ CPU_POWERPC_MPC5200_v12, POWERPC_SVR_5200_v12, G2LE),
+ /* MPC5200B */
+ POWERPC_DEF_SVR("MPC5200B",
+ CPU_POWERPC_MPC5200B, POWERPC_SVR_5200B, G2LE),
+ /* MPC5200B v2.0 */
+ POWERPC_DEF_SVR("MPC5200B_v20",
+ CPU_POWERPC_MPC5200B_v20, POWERPC_SVR_5200B_v20, G2LE),
+ /* MPC5200B v2.1 */
+ POWERPC_DEF_SVR("MPC5200B_v21",
+ CPU_POWERPC_MPC5200B_v21, POWERPC_SVR_5200B_v21, G2LE),
+ /* MPC8241 */
+ POWERPC_DEF("MPC8241", CPU_POWERPC_MPC8241, G2),
+ /* MPC8245 */
+ POWERPC_DEF("MPC8245", CPU_POWERPC_MPC8245, G2),
+ /* MPC8247 */
+ POWERPC_DEF("MPC8247", CPU_POWERPC_MPC8247, G2LE),
+ /* MPC8248 */
+ POWERPC_DEF("MPC8248", CPU_POWERPC_MPC8248, G2LE),
+ /* MPC8250 */
+ POWERPC_DEF("MPC8250", CPU_POWERPC_MPC8250, G2),
+ /* MPC8250 HiP3 */
+ POWERPC_DEF("MPC8250_HiP3", CPU_POWERPC_MPC8250_HiP3, G2),
+ /* MPC8250 HiP4 */
+ POWERPC_DEF("MPC8250_HiP4", CPU_POWERPC_MPC8250_HiP4, G2),
+ /* MPC8255 */
+ POWERPC_DEF("MPC8255", CPU_POWERPC_MPC8255, G2),
+ /* MPC8255 HiP3 */
+ POWERPC_DEF("MPC8255_HiP3", CPU_POWERPC_MPC8255_HiP3, G2),
+ /* MPC8255 HiP4 */
+ POWERPC_DEF("MPC8255_HiP4", CPU_POWERPC_MPC8255_HiP4, G2),
+ /* MPC8260 */
+ POWERPC_DEF("MPC8260", CPU_POWERPC_MPC8260, G2),
+ /* MPC8260 HiP3 */
+ POWERPC_DEF("MPC8260_HiP3", CPU_POWERPC_MPC8260_HiP3, G2),
+ /* MPC8260 HiP4 */
+ POWERPC_DEF("MPC8260_HiP4", CPU_POWERPC_MPC8260_HiP4, G2),
+ /* MPC8264 */
+ POWERPC_DEF("MPC8264", CPU_POWERPC_MPC8264, G2),
+ /* MPC8264 HiP3 */
+ POWERPC_DEF("MPC8264_HiP3", CPU_POWERPC_MPC8264_HiP3, G2),
+ /* MPC8264 HiP4 */
+ POWERPC_DEF("MPC8264_HiP4", CPU_POWERPC_MPC8264_HiP4, G2),
+ /* MPC8265 */
+ POWERPC_DEF("MPC8265", CPU_POWERPC_MPC8265, G2),
+ /* MPC8265 HiP3 */
+ POWERPC_DEF("MPC8265_HiP3", CPU_POWERPC_MPC8265_HiP3, G2),
+ /* MPC8265 HiP4 */
+ POWERPC_DEF("MPC8265_HiP4", CPU_POWERPC_MPC8265_HiP4, G2),
+ /* MPC8266 */
+ POWERPC_DEF("MPC8266", CPU_POWERPC_MPC8266, G2),
+ /* MPC8266 HiP3 */
+ POWERPC_DEF("MPC8266_HiP3", CPU_POWERPC_MPC8266_HiP3, G2),
+ /* MPC8266 HiP4 */
+ POWERPC_DEF("MPC8266_HiP4", CPU_POWERPC_MPC8266_HiP4, G2),
+ /* MPC8270 */
+ POWERPC_DEF("MPC8270", CPU_POWERPC_MPC8270, G2LE),
+ /* MPC8271 */
+ POWERPC_DEF("MPC8271", CPU_POWERPC_MPC8271, G2LE),
+ /* MPC8272 */
+ POWERPC_DEF("MPC8272", CPU_POWERPC_MPC8272, G2LE),
+ /* MPC8275 */
+ POWERPC_DEF("MPC8275", CPU_POWERPC_MPC8275, G2LE),
+ /* MPC8280 */
+ POWERPC_DEF("MPC8280", CPU_POWERPC_MPC8280, G2LE),
+ /* e200 family */
+ /* Generic PowerPC e200 core */
+ POWERPC_DEF("e200", CPU_POWERPC_e200, e200),
+ /* Generic MPC55xx core */
+#if defined (TODO)
+ POWERPC_DEF_SVR("MPC55xx",
+ CPU_POWERPC_MPC55xx, POWERPC_SVR_55xx, e200),
+#endif
+#if defined (TODO)
+ /* PowerPC e200z0 core */
+ POWERPC_DEF("e200z0", CPU_POWERPC_e200z0, e200),
+#endif
+#if defined (TODO)
+ /* PowerPC e200z1 core */
+ POWERPC_DEF("e200z1", CPU_POWERPC_e200z1, e200),
+#endif
+#if defined (TODO)
+ /* PowerPC e200z3 core */
+ POWERPC_DEF("e200z3", CPU_POWERPC_e200z3, e200),
+#endif
+ /* PowerPC e200z5 core */
+ POWERPC_DEF("e200z5", CPU_POWERPC_e200z5, e200),
+ /* PowerPC e200z6 core */
+ POWERPC_DEF("e200z6", CPU_POWERPC_e200z6, e200),
+ /* PowerPC e200 microcontrollers */
+#if defined (TODO)
+ /* MPC5514E */
+ POWERPC_DEF_SVR("MPC5514E",
+ CPU_POWERPC_MPC5514E, POWERPC_SVR_5514E, e200),
+#endif
+#if defined (TODO)
+ /* MPC5514E v0 */
+ POWERPC_DEF_SVR("MPC5514E_v0",
+ CPU_POWERPC_MPC5514E_v0, POWERPC_SVR_5514E_v0, e200),
+#endif
+#if defined (TODO)
+ /* MPC5514E v1 */
+ POWERPC_DEF_SVR("MPC5514E_v1",
+ CPU_POWERPC_MPC5514E_v1, POWERPC_SVR_5514E_v1, e200),
+#endif
+#if defined (TODO)
+ /* MPC5514G */
+ POWERPC_DEF_SVR("MPC5514G",
+ CPU_POWERPC_MPC5514G, POWERPC_SVR_5514G, e200),
+#endif
+#if defined (TODO)
+ /* MPC5514G v0 */
+ POWERPC_DEF_SVR("MPC5514G_v0",
+ CPU_POWERPC_MPC5514G_v0, POWERPC_SVR_5514G_v0, e200),
+#endif
+#if defined (TODO)
+ /* MPC5514G v1 */
+ POWERPC_DEF_SVR("MPC5514G_v1",
+ CPU_POWERPC_MPC5514G_v1, POWERPC_SVR_5514G_v1, e200),
+#endif
+#if defined (TODO)
+ /* MPC5515S */
+ POWERPC_DEF_SVR("MPC5515S",
+ CPU_POWERPC_MPC5515S, POWERPC_SVR_5515S, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516E */
+ POWERPC_DEF_SVR("MPC5516E",
+ CPU_POWERPC_MPC5516E, POWERPC_SVR_5516E, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516E v0 */
+ POWERPC_DEF_SVR("MPC5516E_v0",
+ CPU_POWERPC_MPC5516E_v0, POWERPC_SVR_5516E_v0, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516E v1 */
+ POWERPC_DEF_SVR("MPC5516E_v1",
+ CPU_POWERPC_MPC5516E_v1, POWERPC_SVR_5516E_v1, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516G */
+ POWERPC_DEF_SVR("MPC5516G",
+ CPU_POWERPC_MPC5516G, POWERPC_SVR_5516G, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516G v0 */
+ POWERPC_DEF_SVR("MPC5516G_v0",
+ CPU_POWERPC_MPC5516G_v0, POWERPC_SVR_5516G_v0, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516G v1 */
+ POWERPC_DEF_SVR("MPC5516G_v1",
+ CPU_POWERPC_MPC5516G_v1, POWERPC_SVR_5516G_v1, e200),
+#endif
+#if defined (TODO)
+ /* MPC5516S */
+ POWERPC_DEF_SVR("MPC5516S",
+ CPU_POWERPC_MPC5516S, POWERPC_SVR_5516S, e200),
+#endif
+#if defined (TODO)
+ /* MPC5533 */
+ POWERPC_DEF_SVR("MPC5533",
+ CPU_POWERPC_MPC5533, POWERPC_SVR_5533, e200),
+#endif
+#if defined (TODO)
+ /* MPC5534 */
+ POWERPC_DEF_SVR("MPC5534",
+ CPU_POWERPC_MPC5534, POWERPC_SVR_5534, e200),
+#endif
+#if defined (TODO)
+ /* MPC5553 */
+ POWERPC_DEF_SVR("MPC5553",
+ CPU_POWERPC_MPC5553, POWERPC_SVR_5553, e200),
+#endif
+#if defined (TODO)
+ /* MPC5554 */
+ POWERPC_DEF_SVR("MPC5554",
+ CPU_POWERPC_MPC5554, POWERPC_SVR_5554, e200),
+#endif
+#if defined (TODO)
+ /* MPC5561 */
+ POWERPC_DEF_SVR("MPC5561",
+ CPU_POWERPC_MPC5561, POWERPC_SVR_5561, e200),
+#endif
+#if defined (TODO)
+ /* MPC5565 */
+ POWERPC_DEF_SVR("MPC5565",
+ CPU_POWERPC_MPC5565, POWERPC_SVR_5565, e200),
+#endif
+#if defined (TODO)
+ /* MPC5566 */
+ POWERPC_DEF_SVR("MPC5566",
+ CPU_POWERPC_MPC5566, POWERPC_SVR_5566, e200),
+#endif
+#if defined (TODO)
+ /* MPC5567 */
+ POWERPC_DEF_SVR("MPC5567",
+ CPU_POWERPC_MPC5567, POWERPC_SVR_5567, e200),
+#endif
+ /* e300 family */
+ /* Generic PowerPC e300 core */
+ POWERPC_DEF("e300", CPU_POWERPC_e300, e300),
+ /* PowerPC e300c1 core */
+ POWERPC_DEF("e300c1", CPU_POWERPC_e300c1, e300),
+ /* PowerPC e300c2 core */
+ POWERPC_DEF("e300c2", CPU_POWERPC_e300c2, e300),
+ /* PowerPC e300c3 core */
+ POWERPC_DEF("e300c3", CPU_POWERPC_e300c3, e300),
+ /* PowerPC e300c4 core */
+ POWERPC_DEF("e300c4", CPU_POWERPC_e300c4, e300),
+ /* PowerPC e300 microcontrollers */
+#if defined (TODO)
+ /* MPC8313 */
+ POWERPC_DEF_SVR("MPC8313",
+ CPU_POWERPC_MPC831x, POWERPC_SVR_8313, e300),
+#endif
+#if defined (TODO)
+ /* MPC8313E */
+ POWERPC_DEF_SVR("MPC8313E",
+ CPU_POWERPC_MPC831x, POWERPC_SVR_8313E, e300),
+#endif
+#if defined (TODO)
+ /* MPC8314 */
+ POWERPC_DEF_SVR("MPC8314",
+ CPU_POWERPC_MPC831x, POWERPC_SVR_8314, e300),
+#endif
+#if defined (TODO)
+ /* MPC8314E */
+ POWERPC_DEF_SVR("MPC8314E",
+ CPU_POWERPC_MPC831x, POWERPC_SVR_8314E, e300),
+#endif
+#if defined (TODO)
+ /* MPC8315 */
+ POWERPC_DEF_SVR("MPC8315",
+ CPU_POWERPC_MPC831x, POWERPC_SVR_8315, e300),
+#endif
+#if defined (TODO)
+ /* MPC8315E */
+ POWERPC_DEF_SVR("MPC8315E",
+ CPU_POWERPC_MPC831x, POWERPC_SVR_8315E, e300),
+#endif
+#if defined (TODO)
+ /* MPC8321 */
+ POWERPC_DEF_SVR("MPC8321",
+ CPU_POWERPC_MPC832x, POWERPC_SVR_8321, e300),
+#endif
+#if defined (TODO)
+ /* MPC8321E */
+ POWERPC_DEF_SVR("MPC8321E",
+ CPU_POWERPC_MPC832x, POWERPC_SVR_8321E, e300),
+#endif
+#if defined (TODO)
+ /* MPC8323 */
+ POWERPC_DEF_SVR("MPC8323",
+ CPU_POWERPC_MPC832x, POWERPC_SVR_8323, e300),
+#endif
+#if defined (TODO)
+ /* MPC8323E */
+ POWERPC_DEF_SVR("MPC8323E",
+ CPU_POWERPC_MPC832x, POWERPC_SVR_8323E, e300),
+#endif
+ /* MPC8343 */
+ POWERPC_DEF_SVR("MPC8343",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8343, e300),
+ /* MPC8343A */
+ POWERPC_DEF_SVR("MPC8343A",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8343A, e300),
+ /* MPC8343E */
+ POWERPC_DEF_SVR("MPC8343E",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8343E, e300),
+ /* MPC8343EA */
+ POWERPC_DEF_SVR("MPC8343EA",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8343EA, e300),
+ /* MPC8347 */
+ POWERPC_DEF_SVR("MPC8347",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347, e300),
+ /* MPC8347T */
+ POWERPC_DEF_SVR("MPC8347T",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347T, e300),
+ /* MPC8347P */
+ POWERPC_DEF_SVR("MPC8347P",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347P, e300),
+ /* MPC8347A */
+ POWERPC_DEF_SVR("MPC8347A",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347A, e300),
+ /* MPC8347AT */
+ POWERPC_DEF_SVR("MPC8347AT",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347AT, e300),
+ /* MPC8347AP */
+ POWERPC_DEF_SVR("MPC8347AP",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347AP, e300),
+ /* MPC8347E */
+ POWERPC_DEF_SVR("MPC8347E",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347E, e300),
+ /* MPC8347ET */
+ POWERPC_DEF_SVR("MPC8347ET",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347ET, e300),
+ /* MPC8343EP */
+ POWERPC_DEF_SVR("MPC8347EP",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347EP, e300),
+ /* MPC8347EA */
+ POWERPC_DEF_SVR("MPC8347EA",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347EA, e300),
+ /* MPC8347EAT */
+ POWERPC_DEF_SVR("MPC8347EAT",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAT, e300),
+ /* MPC8343EAP */
+ POWERPC_DEF_SVR("MPC8347EAP",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8347EAP, e300),
+ /* MPC8349 */
+ POWERPC_DEF_SVR("MPC8349",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8349, e300),
+ /* MPC8349A */
+ POWERPC_DEF_SVR("MPC8349A",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8349A, e300),
+ /* MPC8349E */
+ POWERPC_DEF_SVR("MPC8349E",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8349E, e300),
+ /* MPC8349EA */
+ POWERPC_DEF_SVR("MPC8349EA",
+ CPU_POWERPC_MPC834x, POWERPC_SVR_8349EA, e300),
+#if defined (TODO)
+ /* MPC8358E */
+ POWERPC_DEF_SVR("MPC8358E",
+ CPU_POWERPC_MPC835x, POWERPC_SVR_8358E, e300),
+#endif
+#if defined (TODO)
+ /* MPC8360E */
+ POWERPC_DEF_SVR("MPC8360E",
+ CPU_POWERPC_MPC836x, POWERPC_SVR_8360E, e300),
+#endif
+ /* MPC8377 */
+ POWERPC_DEF_SVR("MPC8377",
+ CPU_POWERPC_MPC837x, POWERPC_SVR_8377, e300),
+ /* MPC8377E */
+ POWERPC_DEF_SVR("MPC8377E",
+ CPU_POWERPC_MPC837x, POWERPC_SVR_8377E, e300),
+ /* MPC8378 */
+ POWERPC_DEF_SVR("MPC8378",
+ CPU_POWERPC_MPC837x, POWERPC_SVR_8378, e300),
+ /* MPC8378E */
+ POWERPC_DEF_SVR("MPC8378E",
+ CPU_POWERPC_MPC837x, POWERPC_SVR_8378E, e300),
+ /* MPC8379 */
+ POWERPC_DEF_SVR("MPC8379",
+ CPU_POWERPC_MPC837x, POWERPC_SVR_8379, e300),
+ /* MPC8379E */
+ POWERPC_DEF_SVR("MPC8379E",
+ CPU_POWERPC_MPC837x, POWERPC_SVR_8379E, e300),
+ /* e500 family */
+ /* PowerPC e500 core */
+ POWERPC_DEF("e500", CPU_POWERPC_e500v2_v22, e500v2),
+ /* PowerPC e500v1 core */
+ POWERPC_DEF("e500v1", CPU_POWERPC_e500v1, e500v1),
+ /* PowerPC e500 v1.0 core */
+ POWERPC_DEF("e500_v10", CPU_POWERPC_e500v1_v10, e500v1),
+ /* PowerPC e500 v2.0 core */
+ POWERPC_DEF("e500_v20", CPU_POWERPC_e500v1_v20, e500v1),
+ /* PowerPC e500v2 core */
+ POWERPC_DEF("e500v2", CPU_POWERPC_e500v2, e500v2),
+ /* PowerPC e500v2 v1.0 core */
+ POWERPC_DEF("e500v2_v10", CPU_POWERPC_e500v2_v10, e500v2),
+ /* PowerPC e500v2 v2.0 core */
+ POWERPC_DEF("e500v2_v20", CPU_POWERPC_e500v2_v20, e500v2),
+ /* PowerPC e500v2 v2.1 core */
+ POWERPC_DEF("e500v2_v21", CPU_POWERPC_e500v2_v21, e500v2),
+ /* PowerPC e500v2 v2.2 core */
+ POWERPC_DEF("e500v2_v22", CPU_POWERPC_e500v2_v22, e500v2),
+ /* PowerPC e500v2 v3.0 core */
+ POWERPC_DEF("e500v2_v30", CPU_POWERPC_e500v2_v30, e500v2),
+ /* PowerPC e500 microcontrollers */
+ /* MPC8533 */
+ POWERPC_DEF_SVR("MPC8533",
+ CPU_POWERPC_MPC8533, POWERPC_SVR_8533, e500v2),
+ /* MPC8533 v1.0 */
+ POWERPC_DEF_SVR("MPC8533_v10",
+ CPU_POWERPC_MPC8533_v10, POWERPC_SVR_8533_v10, e500v2),
+ /* MPC8533 v1.1 */
+ POWERPC_DEF_SVR("MPC8533_v11",
+ CPU_POWERPC_MPC8533_v11, POWERPC_SVR_8533_v11, e500v2),
+ /* MPC8533E */
+ POWERPC_DEF_SVR("MPC8533E",
+ CPU_POWERPC_MPC8533E, POWERPC_SVR_8533E, e500v2),
+ /* MPC8533E v1.0 */
+ POWERPC_DEF_SVR("MPC8533E_v10",
+ CPU_POWERPC_MPC8533E_v10, POWERPC_SVR_8533E_v10, e500v2),
+ POWERPC_DEF_SVR("MPC8533E_v11",
+ CPU_POWERPC_MPC8533E_v11, POWERPC_SVR_8533E_v11, e500v2),
+ /* MPC8540 */
+ POWERPC_DEF_SVR("MPC8540",
+ CPU_POWERPC_MPC8540, POWERPC_SVR_8540, e500v1),
+ /* MPC8540 v1.0 */
+ POWERPC_DEF_SVR("MPC8540_v10",
+ CPU_POWERPC_MPC8540_v10, POWERPC_SVR_8540_v10, e500v1),
+ /* MPC8540 v2.0 */
+ POWERPC_DEF_SVR("MPC8540_v20",
+ CPU_POWERPC_MPC8540_v20, POWERPC_SVR_8540_v20, e500v1),
+ /* MPC8540 v2.1 */
+ POWERPC_DEF_SVR("MPC8540_v21",
+ CPU_POWERPC_MPC8540_v21, POWERPC_SVR_8540_v21, e500v1),
+ /* MPC8541 */
+ POWERPC_DEF_SVR("MPC8541",
+ CPU_POWERPC_MPC8541, POWERPC_SVR_8541, e500v1),
+ /* MPC8541 v1.0 */
+ POWERPC_DEF_SVR("MPC8541_v10",
+ CPU_POWERPC_MPC8541_v10, POWERPC_SVR_8541_v10, e500v1),
+ /* MPC8541 v1.1 */
+ POWERPC_DEF_SVR("MPC8541_v11",
+ CPU_POWERPC_MPC8541_v11, POWERPC_SVR_8541_v11, e500v1),
+ /* MPC8541E */
+ POWERPC_DEF_SVR("MPC8541E",
+ CPU_POWERPC_MPC8541E, POWERPC_SVR_8541E, e500v1),
+ /* MPC8541E v1.0 */
+ POWERPC_DEF_SVR("MPC8541E_v10",
+ CPU_POWERPC_MPC8541E_v10, POWERPC_SVR_8541E_v10, e500v1),
+ /* MPC8541E v1.1 */
+ POWERPC_DEF_SVR("MPC8541E_v11",
+ CPU_POWERPC_MPC8541E_v11, POWERPC_SVR_8541E_v11, e500v1),
+ /* MPC8543 */
+ POWERPC_DEF_SVR("MPC8543",
+ CPU_POWERPC_MPC8543, POWERPC_SVR_8543, e500v2),
+ /* MPC8543 v1.0 */
+ POWERPC_DEF_SVR("MPC8543_v10",
+ CPU_POWERPC_MPC8543_v10, POWERPC_SVR_8543_v10, e500v2),
+ /* MPC8543 v1.1 */
+ POWERPC_DEF_SVR("MPC8543_v11",
+ CPU_POWERPC_MPC8543_v11, POWERPC_SVR_8543_v11, e500v2),
+ /* MPC8543 v2.0 */
+ POWERPC_DEF_SVR("MPC8543_v20",
+ CPU_POWERPC_MPC8543_v20, POWERPC_SVR_8543_v20, e500v2),
+ /* MPC8543 v2.1 */
+ POWERPC_DEF_SVR("MPC8543_v21",
+ CPU_POWERPC_MPC8543_v21, POWERPC_SVR_8543_v21, e500v2),
+ /* MPC8543E */
+ POWERPC_DEF_SVR("MPC8543E",
+ CPU_POWERPC_MPC8543E, POWERPC_SVR_8543E, e500v2),
+ /* MPC8543E v1.0 */
+ POWERPC_DEF_SVR("MPC8543E_v10",
+ CPU_POWERPC_MPC8543E_v10, POWERPC_SVR_8543E_v10, e500v2),
+ /* MPC8543E v1.1 */
+ POWERPC_DEF_SVR("MPC8543E_v11",
+ CPU_POWERPC_MPC8543E_v11, POWERPC_SVR_8543E_v11, e500v2),
+ /* MPC8543E v2.0 */
+ POWERPC_DEF_SVR("MPC8543E_v20",
+ CPU_POWERPC_MPC8543E_v20, POWERPC_SVR_8543E_v20, e500v2),
+ /* MPC8543E v2.1 */
+ POWERPC_DEF_SVR("MPC8543E_v21",
+ CPU_POWERPC_MPC8543E_v21, POWERPC_SVR_8543E_v21, e500v2),
+ /* MPC8544 */
+ POWERPC_DEF_SVR("MPC8544",
+ CPU_POWERPC_MPC8544, POWERPC_SVR_8544, e500v2),
+ /* MPC8544 v1.0 */
+ POWERPC_DEF_SVR("MPC8544_v10",
+ CPU_POWERPC_MPC8544_v10, POWERPC_SVR_8544_v10, e500v2),
+ /* MPC8544 v1.1 */
+ POWERPC_DEF_SVR("MPC8544_v11",
+ CPU_POWERPC_MPC8544_v11, POWERPC_SVR_8544_v11, e500v2),
+ /* MPC8544E */
+ POWERPC_DEF_SVR("MPC8544E",
+ CPU_POWERPC_MPC8544E, POWERPC_SVR_8544E, e500v2),
+ /* MPC8544E v1.0 */
+ POWERPC_DEF_SVR("MPC8544E_v10",
+ CPU_POWERPC_MPC8544E_v10, POWERPC_SVR_8544E_v10, e500v2),
+ /* MPC8544E v1.1 */
+ POWERPC_DEF_SVR("MPC8544E_v11",
+ CPU_POWERPC_MPC8544E_v11, POWERPC_SVR_8544E_v11, e500v2),
+ /* MPC8545 */
+ POWERPC_DEF_SVR("MPC8545",
+ CPU_POWERPC_MPC8545, POWERPC_SVR_8545, e500v2),
+ /* MPC8545 v2.0 */
+ POWERPC_DEF_SVR("MPC8545_v20",
+ CPU_POWERPC_MPC8545_v20, POWERPC_SVR_8545_v20, e500v2),
+ /* MPC8545 v2.1 */
+ POWERPC_DEF_SVR("MPC8545_v21",
+ CPU_POWERPC_MPC8545_v21, POWERPC_SVR_8545_v21, e500v2),
+ /* MPC8545E */
+ POWERPC_DEF_SVR("MPC8545E",
+ CPU_POWERPC_MPC8545E, POWERPC_SVR_8545E, e500v2),
+ /* MPC8545E v2.0 */
+ POWERPC_DEF_SVR("MPC8545E_v20",
+ CPU_POWERPC_MPC8545E_v20, POWERPC_SVR_8545E_v20, e500v2),
+ /* MPC8545E v2.1 */
+ POWERPC_DEF_SVR("MPC8545E_v21",
+ CPU_POWERPC_MPC8545E_v21, POWERPC_SVR_8545E_v21, e500v2),
+ /* MPC8547E */
+ POWERPC_DEF_SVR("MPC8547E",
+ CPU_POWERPC_MPC8547E, POWERPC_SVR_8547E, e500v2),
+ /* MPC8547E v2.0 */
+ POWERPC_DEF_SVR("MPC8547E_v20",
+ CPU_POWERPC_MPC8547E_v20, POWERPC_SVR_8547E_v20, e500v2),
+ /* MPC8547E v2.1 */
+ POWERPC_DEF_SVR("MPC8547E_v21",
+ CPU_POWERPC_MPC8547E_v21, POWERPC_SVR_8547E_v21, e500v2),
+ /* MPC8548 */
+ POWERPC_DEF_SVR("MPC8548",
+ CPU_POWERPC_MPC8548, POWERPC_SVR_8548, e500v2),
+ /* MPC8548 v1.0 */
+ POWERPC_DEF_SVR("MPC8548_v10",
+ CPU_POWERPC_MPC8548_v10, POWERPC_SVR_8548_v10, e500v2),
+ /* MPC8548 v1.1 */
+ POWERPC_DEF_SVR("MPC8548_v11",
+ CPU_POWERPC_MPC8548_v11, POWERPC_SVR_8548_v11, e500v2),
+ /* MPC8548 v2.0 */
+ POWERPC_DEF_SVR("MPC8548_v20",
+ CPU_POWERPC_MPC8548_v20, POWERPC_SVR_8548_v20, e500v2),
+ /* MPC8548 v2.1 */
+ POWERPC_DEF_SVR("MPC8548_v21",
+ CPU_POWERPC_MPC8548_v21, POWERPC_SVR_8548_v21, e500v2),
+ /* MPC8548E */
+ POWERPC_DEF_SVR("MPC8548E",
+ CPU_POWERPC_MPC8548E, POWERPC_SVR_8548E, e500v2),
+ /* MPC8548E v1.0 */
+ POWERPC_DEF_SVR("MPC8548E_v10",
+ CPU_POWERPC_MPC8548E_v10, POWERPC_SVR_8548E_v10, e500v2),
+ /* MPC8548E v1.1 */
+ POWERPC_DEF_SVR("MPC8548E_v11",
+ CPU_POWERPC_MPC8548E_v11, POWERPC_SVR_8548E_v11, e500v2),
+ /* MPC8548E v2.0 */
+ POWERPC_DEF_SVR("MPC8548E_v20",
+ CPU_POWERPC_MPC8548E_v20, POWERPC_SVR_8548E_v20, e500v2),
+ /* MPC8548E v2.1 */
+ POWERPC_DEF_SVR("MPC8548E_v21",
+ CPU_POWERPC_MPC8548E_v21, POWERPC_SVR_8548E_v21, e500v2),
+ /* MPC8555 */
+ POWERPC_DEF_SVR("MPC8555",
+ CPU_POWERPC_MPC8555, POWERPC_SVR_8555, e500v2),
+ /* MPC8555 v1.0 */
+ POWERPC_DEF_SVR("MPC8555_v10",
+ CPU_POWERPC_MPC8555_v10, POWERPC_SVR_8555_v10, e500v2),
+ /* MPC8555 v1.1 */
+ POWERPC_DEF_SVR("MPC8555_v11",
+ CPU_POWERPC_MPC8555_v11, POWERPC_SVR_8555_v11, e500v2),
+ /* MPC8555E */
+ POWERPC_DEF_SVR("MPC8555E",
+ CPU_POWERPC_MPC8555E, POWERPC_SVR_8555E, e500v2),
+ /* MPC8555E v1.0 */
+ POWERPC_DEF_SVR("MPC8555E_v10",
+ CPU_POWERPC_MPC8555E_v10, POWERPC_SVR_8555E_v10, e500v2),
+ /* MPC8555E v1.1 */
+ POWERPC_DEF_SVR("MPC8555E_v11",
+ CPU_POWERPC_MPC8555E_v11, POWERPC_SVR_8555E_v11, e500v2),
+ /* MPC8560 */
+ POWERPC_DEF_SVR("MPC8560",
+ CPU_POWERPC_MPC8560, POWERPC_SVR_8560, e500v2),
+ /* MPC8560 v1.0 */
+ POWERPC_DEF_SVR("MPC8560_v10",
+ CPU_POWERPC_MPC8560_v10, POWERPC_SVR_8560_v10, e500v2),
+ /* MPC8560 v2.0 */
+ POWERPC_DEF_SVR("MPC8560_v20",
+ CPU_POWERPC_MPC8560_v20, POWERPC_SVR_8560_v20, e500v2),
+ /* MPC8560 v2.1 */
+ POWERPC_DEF_SVR("MPC8560_v21",
+ CPU_POWERPC_MPC8560_v21, POWERPC_SVR_8560_v21, e500v2),
+ /* MPC8567 */
+ POWERPC_DEF_SVR("MPC8567",
+ CPU_POWERPC_MPC8567, POWERPC_SVR_8567, e500v2),
+ /* MPC8567E */
+ POWERPC_DEF_SVR("MPC8567E",
+ CPU_POWERPC_MPC8567E, POWERPC_SVR_8567E, e500v2),
+ /* MPC8568 */
+ POWERPC_DEF_SVR("MPC8568",
+ CPU_POWERPC_MPC8568, POWERPC_SVR_8568, e500v2),
+ /* MPC8568E */
+ POWERPC_DEF_SVR("MPC8568E",
+ CPU_POWERPC_MPC8568E, POWERPC_SVR_8568E, e500v2),
+ /* MPC8572 */
+ POWERPC_DEF_SVR("MPC8572",
+ CPU_POWERPC_MPC8572, POWERPC_SVR_8572, e500v2),
+ /* MPC8572E */
+ POWERPC_DEF_SVR("MPC8572E",
+ CPU_POWERPC_MPC8572E, POWERPC_SVR_8572E, e500v2),
+ /* e600 family */
+ /* PowerPC e600 core */
+ POWERPC_DEF("e600", CPU_POWERPC_e600, 7400),
+ /* PowerPC e600 microcontrollers */
+#if defined (TODO)
+ /* MPC8610 */
+ POWERPC_DEF_SVR("MPC8610",
+ CPU_POWERPC_MPC8610, POWERPC_SVR_8610, 7400),
+#endif
+ /* MPC8641 */
+ POWERPC_DEF_SVR("MPC8641",
+ CPU_POWERPC_MPC8641, POWERPC_SVR_8641, 7400),
+ /* MPC8641D */
+ POWERPC_DEF_SVR("MPC8641D",
+ CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, 7400),
+ /* 32 bits "classic" PowerPC */
+ /* PowerPC 6xx family */
+ /* PowerPC 601 */
+ POWERPC_DEF("601", CPU_POWERPC_601, 601v),
+ /* PowerPC 601v0 */
+ POWERPC_DEF("601_v0", CPU_POWERPC_601_v0, 601),
+ /* PowerPC 601v1 */
+ POWERPC_DEF("601_v1", CPU_POWERPC_601_v1, 601),
+ /* PowerPC 601v */
+ POWERPC_DEF("601v", CPU_POWERPC_601v, 601v),
+ /* PowerPC 601v2 */
+ POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v),
+ /* PowerPC 602 */
+ POWERPC_DEF("602", CPU_POWERPC_602, 602),
+ /* PowerPC 603 */
+ POWERPC_DEF("603", CPU_POWERPC_603, 603),
+ /* Code name for PowerPC 603 */
+ POWERPC_DEF("Vanilla", CPU_POWERPC_603, 603),
+ /* PowerPC 603e (aka PID6) */
+ POWERPC_DEF("603e", CPU_POWERPC_603E, 603E),
+ /* Code name for PowerPC 603e */
+ POWERPC_DEF("Stretch", CPU_POWERPC_603E, 603E),
+ /* PowerPC 603e v1.1 */
+ POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E),
+ /* PowerPC 603e v1.2 */
+ POWERPC_DEF("603e_v1.2", CPU_POWERPC_603E_v12, 603E),
+ /* PowerPC 603e v1.3 */
+ POWERPC_DEF("603e_v1.3", CPU_POWERPC_603E_v13, 603E),
+ /* PowerPC 603e v1.4 */
+ POWERPC_DEF("603e_v1.4", CPU_POWERPC_603E_v14, 603E),
+ /* PowerPC 603e v2.2 */
+ POWERPC_DEF("603e_v2.2", CPU_POWERPC_603E_v22, 603E),
+ /* PowerPC 603e v3 */
+ POWERPC_DEF("603e_v3", CPU_POWERPC_603E_v3, 603E),
+ /* PowerPC 603e v4 */
+ POWERPC_DEF("603e_v4", CPU_POWERPC_603E_v4, 603E),
+ /* PowerPC 603e v4.1 */
+ POWERPC_DEF("603e_v4.1", CPU_POWERPC_603E_v41, 603E),
+ /* PowerPC 603e (aka PID7) */
+ POWERPC_DEF("603e7", CPU_POWERPC_603E7, 603E),
+ /* PowerPC 603e7t */
+ POWERPC_DEF("603e7t", CPU_POWERPC_603E7t, 603E),
+ /* PowerPC 603e7v */
+ POWERPC_DEF("603e7v", CPU_POWERPC_603E7v, 603E),
+ /* Code name for PowerPC 603ev */
+ POWERPC_DEF("Vaillant", CPU_POWERPC_603E7v, 603E),
+ /* PowerPC 603e7v1 */
+ POWERPC_DEF("603e7v1", CPU_POWERPC_603E7v1, 603E),
+ /* PowerPC 603e7v2 */
+ POWERPC_DEF("603e7v2", CPU_POWERPC_603E7v2, 603E),
+ /* PowerPC 603p (aka PID7v) */
+ POWERPC_DEF("603p", CPU_POWERPC_603P, 603E),
+ /* PowerPC 603r (aka PID7t) */
+ POWERPC_DEF("603r", CPU_POWERPC_603R, 603E),
+ /* Code name for PowerPC 603r */
+ POWERPC_DEF("Goldeneye", CPU_POWERPC_603R, 603E),
+ /* PowerPC 604 */
+ POWERPC_DEF("604", CPU_POWERPC_604, 604),
+ /* PowerPC 604e (aka PID9) */
+ POWERPC_DEF("604e", CPU_POWERPC_604E, 604E),
+ /* Code name for PowerPC 604e */
+ POWERPC_DEF("Sirocco", CPU_POWERPC_604E, 604E),
+ /* PowerPC 604e v1.0 */
+ POWERPC_DEF("604e_v1.0", CPU_POWERPC_604E_v10, 604E),
+ /* PowerPC 604e v2.2 */
+ POWERPC_DEF("604e_v2.2", CPU_POWERPC_604E_v22, 604E),
+ /* PowerPC 604e v2.4 */
+ POWERPC_DEF("604e_v2.4", CPU_POWERPC_604E_v24, 604E),
+ /* PowerPC 604r (aka PIDA) */
+ POWERPC_DEF("604r", CPU_POWERPC_604R, 604E),
+ /* Code name for PowerPC 604r */
+ POWERPC_DEF("Mach5", CPU_POWERPC_604R, 604E),
+#if defined(TODO)
+ /* PowerPC 604ev */
+ POWERPC_DEF("604ev", CPU_POWERPC_604EV, 604E),
+#endif
+ /* PowerPC 7xx family */
+ /* Generic PowerPC 740 (G3) */
+ POWERPC_DEF("740", CPU_POWERPC_7x0, 740),
+ /* Code name for PowerPC 740 */
+ POWERPC_DEF("Arthur", CPU_POWERPC_7x0, 740),
+ /* Generic PowerPC 750 (G3) */
+ POWERPC_DEF("750", CPU_POWERPC_7x0, 750),
+ /* Code name for PowerPC 750 */
+ POWERPC_DEF("Typhoon", CPU_POWERPC_7x0, 750),
+ /* PowerPC 740/750 is also known as G3 */
+ POWERPC_DEF("G3", CPU_POWERPC_7x0, 750),
+ /* PowerPC 740 v1.0 (G3) */
+ POWERPC_DEF("740_v1.0", CPU_POWERPC_7x0_v10, 740),
+ /* PowerPC 750 v1.0 (G3) */
+ POWERPC_DEF("750_v1.0", CPU_POWERPC_7x0_v10, 750),
+ /* PowerPC 740 v2.0 (G3) */
+ POWERPC_DEF("740_v2.0", CPU_POWERPC_7x0_v20, 740),
+ /* PowerPC 750 v2.0 (G3) */
+ POWERPC_DEF("750_v2.0", CPU_POWERPC_7x0_v20, 750),
+ /* PowerPC 740 v2.1 (G3) */
+ POWERPC_DEF("740_v2.1", CPU_POWERPC_7x0_v21, 740),
+ /* PowerPC 750 v2.1 (G3) */
+ POWERPC_DEF("750_v2.1", CPU_POWERPC_7x0_v21, 750),
+ /* PowerPC 740 v2.2 (G3) */
+ POWERPC_DEF("740_v2.2", CPU_POWERPC_7x0_v22, 740),
+ /* PowerPC 750 v2.2 (G3) */
+ POWERPC_DEF("750_v2.2", CPU_POWERPC_7x0_v22, 750),
+ /* PowerPC 740 v3.0 (G3) */
+ POWERPC_DEF("740_v3.0", CPU_POWERPC_7x0_v30, 740),
+ /* PowerPC 750 v3.0 (G3) */
+ POWERPC_DEF("750_v3.0", CPU_POWERPC_7x0_v30, 750),
+ /* PowerPC 740 v3.1 (G3) */
+ POWERPC_DEF("740_v3.1", CPU_POWERPC_7x0_v31, 740),
+ /* PowerPC 750 v3.1 (G3) */
+ POWERPC_DEF("750_v3.1", CPU_POWERPC_7x0_v31, 750),
+ /* PowerPC 740E (G3) */
+ POWERPC_DEF("740e", CPU_POWERPC_740E, 740),
+ /* PowerPC 750E (G3) */
+ POWERPC_DEF("750e", CPU_POWERPC_750E, 750),
+ /* PowerPC 740P (G3) */
+ POWERPC_DEF("740p", CPU_POWERPC_7x0P, 740),
+ /* PowerPC 750P (G3) */
+ POWERPC_DEF("750p", CPU_POWERPC_7x0P, 750),
+ /* Code name for PowerPC 740P/750P (G3) */
+ POWERPC_DEF("Conan/Doyle", CPU_POWERPC_7x0P, 750),
+ /* PowerPC 750CL (G3 embedded) */
+ POWERPC_DEF("750cl", CPU_POWERPC_750CL, 750cl),
+ /* PowerPC 750CL v1.0 */
+ POWERPC_DEF("750cl_v1.0", CPU_POWERPC_750CL_v10, 750cl),
+ /* PowerPC 750CL v2.0 */
+ POWERPC_DEF("750cl_v2.0", CPU_POWERPC_750CL_v20, 750cl),
+ /* PowerPC 750CX (G3 embedded) */
+ POWERPC_DEF("750cx", CPU_POWERPC_750CX, 750cx),
+ /* PowerPC 750CX v1.0 (G3 embedded) */
+ POWERPC_DEF("750cx_v1.0", CPU_POWERPC_750CX_v10, 750cx),
+ /* PowerPC 750CX v2.1 (G3 embedded) */
+ POWERPC_DEF("750cx_v2.0", CPU_POWERPC_750CX_v20, 750cx),
+ /* PowerPC 750CX v2.1 (G3 embedded) */
+ POWERPC_DEF("750cx_v2.1", CPU_POWERPC_750CX_v21, 750cx),
+ /* PowerPC 750CX v2.2 (G3 embedded) */
+ POWERPC_DEF("750cx_v2.2", CPU_POWERPC_750CX_v22, 750cx),
+ /* PowerPC 750CXe (G3 embedded) */
+ POWERPC_DEF("750cxe", CPU_POWERPC_750CXE, 750cx),
+ /* PowerPC 750CXe v2.1 (G3 embedded) */
+ POWERPC_DEF("750cxe_v2.1", CPU_POWERPC_750CXE_v21, 750cx),
+ /* PowerPC 750CXe v2.2 (G3 embedded) */
+ POWERPC_DEF("750cxe_v2.2", CPU_POWERPC_750CXE_v22, 750cx),
+ /* PowerPC 750CXe v2.3 (G3 embedded) */
+ POWERPC_DEF("750cxe_v2.3", CPU_POWERPC_750CXE_v23, 750cx),
+ /* PowerPC 750CXe v2.4 (G3 embedded) */
+ POWERPC_DEF("750cxe_v2.4", CPU_POWERPC_750CXE_v24, 750cx),
+ /* PowerPC 750CXe v2.4b (G3 embedded) */
+ POWERPC_DEF("750cxe_v2.4b", CPU_POWERPC_750CXE_v24b, 750cx),
+ /* PowerPC 750CXe v3.0 (G3 embedded) */
+ POWERPC_DEF("750cxe_v3.0", CPU_POWERPC_750CXE_v30, 750cx),
+ /* PowerPC 750CXe v3.1 (G3 embedded) */
+ POWERPC_DEF("750cxe_v3.1", CPU_POWERPC_750CXE_v31, 750cx),
+ /* PowerPC 750CXe v3.1b (G3 embedded) */
+ POWERPC_DEF("750cxe_v3.1b", CPU_POWERPC_750CXE_v31b, 750cx),
+ /* PowerPC 750CXr (G3 embedded) */
+ POWERPC_DEF("750cxr", CPU_POWERPC_750CXR, 750cx),
+ /* PowerPC 750FL (G3 embedded) */
+ POWERPC_DEF("750fl", CPU_POWERPC_750FL, 750fx),
+ /* PowerPC 750FX (G3 embedded) */
+ POWERPC_DEF("750fx", CPU_POWERPC_750FX, 750fx),
+ /* PowerPC 750FX v1.0 (G3 embedded) */
+ POWERPC_DEF("750fx_v1.0", CPU_POWERPC_750FX_v10, 750fx),
+ /* PowerPC 750FX v2.0 (G3 embedded) */
+ POWERPC_DEF("750fx_v2.0", CPU_POWERPC_750FX_v20, 750fx),
+ /* PowerPC 750FX v2.1 (G3 embedded) */
+ POWERPC_DEF("750fx_v2.1", CPU_POWERPC_750FX_v21, 750fx),
+ /* PowerPC 750FX v2.2 (G3 embedded) */
+ POWERPC_DEF("750fx_v2.2", CPU_POWERPC_750FX_v22, 750fx),
+ /* PowerPC 750FX v2.3 (G3 embedded) */
+ POWERPC_DEF("750fx_v2.3", CPU_POWERPC_750FX_v23, 750fx),
+ /* PowerPC 750GL (G3 embedded) */
+ POWERPC_DEF("750gl", CPU_POWERPC_750GL, 750gx),
+ /* PowerPC 750GX (G3 embedded) */
+ POWERPC_DEF("750gx", CPU_POWERPC_750GX, 750gx),
+ /* PowerPC 750GX v1.0 (G3 embedded) */
+ POWERPC_DEF("750gx_v1.0", CPU_POWERPC_750GX_v10, 750gx),
+ /* PowerPC 750GX v1.1 (G3 embedded) */
+ POWERPC_DEF("750gx_v1.1", CPU_POWERPC_750GX_v11, 750gx),
+ /* PowerPC 750GX v1.2 (G3 embedded) */
+ POWERPC_DEF("750gx_v1.2", CPU_POWERPC_750GX_v12, 750gx),
+ /* PowerPC 750L (G3 embedded) */
+ POWERPC_DEF("750l", CPU_POWERPC_750L, 750),
+ /* Code name for PowerPC 750L (G3 embedded) */
+ POWERPC_DEF("LoneStar", CPU_POWERPC_750L, 750),
+ /* PowerPC 750L v2.0 (G3 embedded) */
+ POWERPC_DEF("750l_v2.0", CPU_POWERPC_750L_v20, 750),
+ /* PowerPC 750L v2.1 (G3 embedded) */
+ POWERPC_DEF("750l_v2.1", CPU_POWERPC_750L_v21, 750),
+ /* PowerPC 750L v2.2 (G3 embedded) */
+ POWERPC_DEF("750l_v2.2", CPU_POWERPC_750L_v22, 750),
+ /* PowerPC 750L v3.0 (G3 embedded) */
+ POWERPC_DEF("750l_v3.0", CPU_POWERPC_750L_v30, 750),
+ /* PowerPC 750L v3.2 (G3 embedded) */
+ POWERPC_DEF("750l_v3.2", CPU_POWERPC_750L_v32, 750),
+ /* Generic PowerPC 745 */
+ POWERPC_DEF("745", CPU_POWERPC_7x5, 745),
+ /* Generic PowerPC 755 */
+ POWERPC_DEF("755", CPU_POWERPC_7x5, 755),
+ /* Code name for PowerPC 745/755 */
+ POWERPC_DEF("Goldfinger", CPU_POWERPC_7x5, 755),
+ /* PowerPC 745 v1.0 */
+ POWERPC_DEF("745_v1.0", CPU_POWERPC_7x5_v10, 745),
+ /* PowerPC 755 v1.0 */
+ POWERPC_DEF("755_v1.0", CPU_POWERPC_7x5_v10, 755),
+ /* PowerPC 745 v1.1 */
+ POWERPC_DEF("745_v1.1", CPU_POWERPC_7x5_v11, 745),
+ /* PowerPC 755 v1.1 */
+ POWERPC_DEF("755_v1.1", CPU_POWERPC_7x5_v11, 755),
+ /* PowerPC 745 v2.0 */
+ POWERPC_DEF("745_v2.0", CPU_POWERPC_7x5_v20, 745),
+ /* PowerPC 755 v2.0 */
+ POWERPC_DEF("755_v2.0", CPU_POWERPC_7x5_v20, 755),
+ /* PowerPC 745 v2.1 */
+ POWERPC_DEF("745_v2.1", CPU_POWERPC_7x5_v21, 745),
+ /* PowerPC 755 v2.1 */
+ POWERPC_DEF("755_v2.1", CPU_POWERPC_7x5_v21, 755),
+ /* PowerPC 745 v2.2 */
+ POWERPC_DEF("745_v2.2", CPU_POWERPC_7x5_v22, 745),
+ /* PowerPC 755 v2.2 */
+ POWERPC_DEF("755_v2.2", CPU_POWERPC_7x5_v22, 755),
+ /* PowerPC 745 v2.3 */
+ POWERPC_DEF("745_v2.3", CPU_POWERPC_7x5_v23, 745),
+ /* PowerPC 755 v2.3 */
+ POWERPC_DEF("755_v2.3", CPU_POWERPC_7x5_v23, 755),
+ /* PowerPC 745 v2.4 */
+ POWERPC_DEF("745_v2.4", CPU_POWERPC_7x5_v24, 745),
+ /* PowerPC 755 v2.4 */
+ POWERPC_DEF("755_v2.4", CPU_POWERPC_7x5_v24, 755),
+ /* PowerPC 745 v2.5 */
+ POWERPC_DEF("745_v2.5", CPU_POWERPC_7x5_v25, 745),
+ /* PowerPC 755 v2.5 */
+ POWERPC_DEF("755_v2.5", CPU_POWERPC_7x5_v25, 755),
+ /* PowerPC 745 v2.6 */
+ POWERPC_DEF("745_v2.6", CPU_POWERPC_7x5_v26, 745),
+ /* PowerPC 755 v2.6 */
+ POWERPC_DEF("755_v2.6", CPU_POWERPC_7x5_v26, 755),
+ /* PowerPC 745 v2.7 */
+ POWERPC_DEF("745_v2.7", CPU_POWERPC_7x5_v27, 745),
+ /* PowerPC 755 v2.7 */
+ POWERPC_DEF("755_v2.7", CPU_POWERPC_7x5_v27, 755),
+ /* PowerPC 745 v2.8 */
+ POWERPC_DEF("745_v2.8", CPU_POWERPC_7x5_v28, 745),
+ /* PowerPC 755 v2.8 */
+ POWERPC_DEF("755_v2.8", CPU_POWERPC_7x5_v28, 755),
+#if defined (TODO)
+ /* PowerPC 745P (G3) */
+ POWERPC_DEF("745p", CPU_POWERPC_7x5P, 745),
+ /* PowerPC 755P (G3) */
+ POWERPC_DEF("755p", CPU_POWERPC_7x5P, 755),
+#endif
+ /* PowerPC 74xx family */
+ /* PowerPC 7400 (G4) */
+ POWERPC_DEF("7400", CPU_POWERPC_7400, 7400),
+ /* Code name for PowerPC 7400 */
+ POWERPC_DEF("Max", CPU_POWERPC_7400, 7400),
+ /* PowerPC 74xx is also well known as G4 */
+ POWERPC_DEF("G4", CPU_POWERPC_7400, 7400),
+ /* PowerPC 7400 v1.0 (G4) */
+ POWERPC_DEF("7400_v1.0", CPU_POWERPC_7400_v10, 7400),
+ /* PowerPC 7400 v1.1 (G4) */
+ POWERPC_DEF("7400_v1.1", CPU_POWERPC_7400_v11, 7400),
+ /* PowerPC 7400 v2.0 (G4) */
+ POWERPC_DEF("7400_v2.0", CPU_POWERPC_7400_v20, 7400),
+ /* PowerPC 7400 v2.1 (G4) */
+ POWERPC_DEF("7400_v2.1", CPU_POWERPC_7400_v21, 7400),
+ /* PowerPC 7400 v2.2 (G4) */
+ POWERPC_DEF("7400_v2.2", CPU_POWERPC_7400_v22, 7400),
+ /* PowerPC 7400 v2.6 (G4) */
+ POWERPC_DEF("7400_v2.6", CPU_POWERPC_7400_v26, 7400),
+ /* PowerPC 7400 v2.7 (G4) */
+ POWERPC_DEF("7400_v2.7", CPU_POWERPC_7400_v27, 7400),
+ /* PowerPC 7400 v2.8 (G4) */
+ POWERPC_DEF("7400_v2.8", CPU_POWERPC_7400_v28, 7400),
+ /* PowerPC 7400 v2.9 (G4) */
+ POWERPC_DEF("7400_v2.9", CPU_POWERPC_7400_v29, 7400),
+ /* PowerPC 7410 (G4) */
+ POWERPC_DEF("7410", CPU_POWERPC_7410, 7410),
+ /* Code name for PowerPC 7410 */
+ POWERPC_DEF("Nitro", CPU_POWERPC_7410, 7410),
+ /* PowerPC 7410 v1.0 (G4) */
+ POWERPC_DEF("7410_v1.0", CPU_POWERPC_7410_v10, 7410),
+ /* PowerPC 7410 v1.1 (G4) */
+ POWERPC_DEF("7410_v1.1", CPU_POWERPC_7410_v11, 7410),
+ /* PowerPC 7410 v1.2 (G4) */
+ POWERPC_DEF("7410_v1.2", CPU_POWERPC_7410_v12, 7410),
+ /* PowerPC 7410 v1.3 (G4) */
+ POWERPC_DEF("7410_v1.3", CPU_POWERPC_7410_v13, 7410),
+ /* PowerPC 7410 v1.4 (G4) */
+ POWERPC_DEF("7410_v1.4", CPU_POWERPC_7410_v14, 7410),
+ /* PowerPC 7448 (G4) */
+ POWERPC_DEF("7448", CPU_POWERPC_7448, 7400),
+ /* PowerPC 7448 v1.0 (G4) */
+ POWERPC_DEF("7448_v1.0", CPU_POWERPC_7448_v10, 7400),
+ /* PowerPC 7448 v1.1 (G4) */
+ POWERPC_DEF("7448_v1.1", CPU_POWERPC_7448_v11, 7400),
+ /* PowerPC 7448 v2.0 (G4) */
+ POWERPC_DEF("7448_v2.0", CPU_POWERPC_7448_v20, 7400),
+ /* PowerPC 7448 v2.1 (G4) */
+ POWERPC_DEF("7448_v2.1", CPU_POWERPC_7448_v21, 7400),
+ /* PowerPC 7450 (G4) */
+ POWERPC_DEF("7450", CPU_POWERPC_7450, 7450),
+ /* Code name for PowerPC 7450 */
+ POWERPC_DEF("Vger", CPU_POWERPC_7450, 7450),
+ /* PowerPC 7450 v1.0 (G4) */
+ POWERPC_DEF("7450_v1.0", CPU_POWERPC_7450_v10, 7450),
+ /* PowerPC 7450 v1.1 (G4) */
+ POWERPC_DEF("7450_v1.1", CPU_POWERPC_7450_v11, 7450),
+ /* PowerPC 7450 v1.2 (G4) */
+ POWERPC_DEF("7450_v1.2", CPU_POWERPC_7450_v12, 7450),
+ /* PowerPC 7450 v2.0 (G4) */
+ POWERPC_DEF("7450_v2.0", CPU_POWERPC_7450_v20, 7450),
+ /* PowerPC 7450 v2.1 (G4) */
+ POWERPC_DEF("7450_v2.1", CPU_POWERPC_7450_v21, 7450),
+ /* PowerPC 7441 (G4) */
+ POWERPC_DEF("7441", CPU_POWERPC_74x1, 7440),
+ /* PowerPC 7451 (G4) */
+ POWERPC_DEF("7451", CPU_POWERPC_74x1, 7450),
+ /* PowerPC 7441 v2.1 (G4) */
+ POWERPC_DEF("7441_v2.1", CPU_POWERPC_7450_v21, 7440),
+ /* PowerPC 7441 v2.3 (G4) */
+ POWERPC_DEF("7441_v2.3", CPU_POWERPC_74x1_v23, 7440),
+ /* PowerPC 7451 v2.3 (G4) */
+ POWERPC_DEF("7451_v2.3", CPU_POWERPC_74x1_v23, 7450),
+ /* PowerPC 7441 v2.10 (G4) */
+ POWERPC_DEF("7441_v2.10", CPU_POWERPC_74x1_v210, 7440),
+ /* PowerPC 7451 v2.10 (G4) */
+ POWERPC_DEF("7451_v2.10", CPU_POWERPC_74x1_v210, 7450),
+ /* PowerPC 7445 (G4) */
+ POWERPC_DEF("7445", CPU_POWERPC_74x5, 7445),
+ /* PowerPC 7455 (G4) */
+ POWERPC_DEF("7455", CPU_POWERPC_74x5, 7455),
+ /* Code name for PowerPC 7445/7455 */
+ POWERPC_DEF("Apollo6", CPU_POWERPC_74x5, 7455),
+ /* PowerPC 7445 v1.0 (G4) */
+ POWERPC_DEF("7445_v1.0", CPU_POWERPC_74x5_v10, 7445),
+ /* PowerPC 7455 v1.0 (G4) */
+ POWERPC_DEF("7455_v1.0", CPU_POWERPC_74x5_v10, 7455),
+ /* PowerPC 7445 v2.1 (G4) */
+ POWERPC_DEF("7445_v2.1", CPU_POWERPC_74x5_v21, 7445),
+ /* PowerPC 7455 v2.1 (G4) */
+ POWERPC_DEF("7455_v2.1", CPU_POWERPC_74x5_v21, 7455),
+ /* PowerPC 7445 v3.2 (G4) */
+ POWERPC_DEF("7445_v3.2", CPU_POWERPC_74x5_v32, 7445),
+ /* PowerPC 7455 v3.2 (G4) */
+ POWERPC_DEF("7455_v3.2", CPU_POWERPC_74x5_v32, 7455),
+ /* PowerPC 7445 v3.3 (G4) */
+ POWERPC_DEF("7445_v3.3", CPU_POWERPC_74x5_v33, 7445),
+ /* PowerPC 7455 v3.3 (G4) */
+ POWERPC_DEF("7455_v3.3", CPU_POWERPC_74x5_v33, 7455),
+ /* PowerPC 7445 v3.4 (G4) */
+ POWERPC_DEF("7445_v3.4", CPU_POWERPC_74x5_v34, 7445),
+ /* PowerPC 7455 v3.4 (G4) */
+ POWERPC_DEF("7455_v3.4", CPU_POWERPC_74x5_v34, 7455),
+ /* PowerPC 7447 (G4) */
+ POWERPC_DEF("7447", CPU_POWERPC_74x7, 7445),
+ /* PowerPC 7457 (G4) */
+ POWERPC_DEF("7457", CPU_POWERPC_74x7, 7455),
+ /* Code name for PowerPC 7447/7457 */
+ POWERPC_DEF("Apollo7", CPU_POWERPC_74x7, 7455),
+ /* PowerPC 7447 v1.0 (G4) */
+ POWERPC_DEF("7447_v1.0", CPU_POWERPC_74x7_v10, 7445),
+ /* PowerPC 7457 v1.0 (G4) */
+ POWERPC_DEF("7457_v1.0", CPU_POWERPC_74x7_v10, 7455),
+ /* PowerPC 7447 v1.1 (G4) */
+ POWERPC_DEF("7447_v1.1", CPU_POWERPC_74x7_v11, 7445),
+ /* PowerPC 7457 v1.1 (G4) */
+ POWERPC_DEF("7457_v1.1", CPU_POWERPC_74x7_v11, 7455),
+ /* PowerPC 7457 v1.2 (G4) */
+ POWERPC_DEF("7457_v1.2", CPU_POWERPC_74x7_v12, 7455),
+ /* PowerPC 7447A (G4) */
+ POWERPC_DEF("7447A", CPU_POWERPC_74x7A, 7445),
+ /* PowerPC 7457A (G4) */
+ POWERPC_DEF("7457A", CPU_POWERPC_74x7A, 7455),
+ /* PowerPC 7447A v1.0 (G4) */
+ POWERPC_DEF("7447A_v1.0", CPU_POWERPC_74x7A_v10, 7445),
+ /* PowerPC 7457A v1.0 (G4) */
+ POWERPC_DEF("7457A_v1.0", CPU_POWERPC_74x7A_v10, 7455),
+ /* Code name for PowerPC 7447A/7457A */
+ POWERPC_DEF("Apollo7PM", CPU_POWERPC_74x7A_v10, 7455),
+ /* PowerPC 7447A v1.1 (G4) */
+ POWERPC_DEF("7447A_v1.1", CPU_POWERPC_74x7A_v11, 7445),
+ /* PowerPC 7457A v1.1 (G4) */
+ POWERPC_DEF("7457A_v1.1", CPU_POWERPC_74x7A_v11, 7455),
+ /* PowerPC 7447A v1.2 (G4) */
+ POWERPC_DEF("7447A_v1.2", CPU_POWERPC_74x7A_v12, 7445),
+ /* PowerPC 7457A v1.2 (G4) */
+ POWERPC_DEF("7457A_v1.2", CPU_POWERPC_74x7A_v12, 7455),
+ /* 64 bits PowerPC */
+#if defined (TARGET_PPC64)
+ /* PowerPC 620 */
+ POWERPC_DEF("620", CPU_POWERPC_620, 620),
+ /* Code name for PowerPC 620 */
+ POWERPC_DEF("Trident", CPU_POWERPC_620, 620),
+#if defined (TODO)
+ /* PowerPC 630 (POWER3) */
+ POWERPC_DEF("630", CPU_POWERPC_630, 630),
+ POWERPC_DEF("POWER3", CPU_POWERPC_630, 630),
+ /* Code names for POWER3 */
+ POWERPC_DEF("Boxer", CPU_POWERPC_630, 630),
+ POWERPC_DEF("Dino", CPU_POWERPC_630, 630),
+#endif
+#if defined (TODO)
+ /* PowerPC 631 (Power 3+) */
+ POWERPC_DEF("631", CPU_POWERPC_631, 631),
+ POWERPC_DEF("POWER3+", CPU_POWERPC_631, 631),
+#endif
+#if defined (TODO)
+ /* POWER4 */
+ POWERPC_DEF("POWER4", CPU_POWERPC_POWER4, POWER4),
+#endif
+#if defined (TODO)
+ /* POWER4p */
+ POWERPC_DEF("POWER4+", CPU_POWERPC_POWER4P, POWER4P),
+#endif
+#if defined (TODO)
+ /* POWER5 */
+ POWERPC_DEF("POWER5", CPU_POWERPC_POWER5, POWER5),
+ /* POWER5GR */
+ POWERPC_DEF("POWER5gr", CPU_POWERPC_POWER5GR, POWER5),
+#endif
+#if defined (TODO)
+ /* POWER5+ */
+ POWERPC_DEF("POWER5+", CPU_POWERPC_POWER5P, POWER5P),
+ /* POWER5GS */
+ POWERPC_DEF("POWER5gs", CPU_POWERPC_POWER5GS, POWER5P),
+#endif
+#if defined (TODO)
+ /* POWER6 */
+ POWERPC_DEF("POWER6", CPU_POWERPC_POWER6, POWER6),
+ /* POWER6 running in POWER5 mode */
+ POWERPC_DEF("POWER6_5", CPU_POWERPC_POWER6_5, POWER5),
+ /* POWER6A */
+ POWERPC_DEF("POWER6A", CPU_POWERPC_POWER6A, POWER6),
+#endif
+ /* PowerPC 970 */
+ POWERPC_DEF("970", CPU_POWERPC_970, 970),
+ /* PowerPC 970FX (G5) */
+ POWERPC_DEF("970fx", CPU_POWERPC_970FX, 970FX),
+ /* PowerPC 970FX v1.0 (G5) */
+ POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970FX),
+ /* PowerPC 970FX v2.0 (G5) */
+ POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970FX),
+ /* PowerPC 970FX v2.1 (G5) */
+ POWERPC_DEF("970fx_v2.1", CPU_POWERPC_970FX_v21, 970FX),
+ /* PowerPC 970FX v3.0 (G5) */
+ POWERPC_DEF("970fx_v3.0", CPU_POWERPC_970FX_v30, 970FX),
+ /* PowerPC 970FX v3.1 (G5) */
+ POWERPC_DEF("970fx_v3.1", CPU_POWERPC_970FX_v31, 970FX),
+ /* PowerPC 970GX (G5) */
+ POWERPC_DEF("970gx", CPU_POWERPC_970GX, 970GX),
+ /* PowerPC 970MP */
+ POWERPC_DEF("970mp", CPU_POWERPC_970MP, 970MP),
+ /* PowerPC 970MP v1.0 */
+ POWERPC_DEF("970mp_v1.0", CPU_POWERPC_970MP_v10, 970MP),
+ /* PowerPC 970MP v1.1 */
+ POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970MP),
+#if defined (TODO)
+ /* PowerPC Cell */
+ POWERPC_DEF("Cell", CPU_POWERPC_CELL, 970),
+#endif
+#if defined (TODO)
+ /* PowerPC Cell v1.0 */
+ POWERPC_DEF("Cell_v1.0", CPU_POWERPC_CELL_v10, 970),
+#endif
+#if defined (TODO)
+ /* PowerPC Cell v2.0 */
+ POWERPC_DEF("Cell_v2.0", CPU_POWERPC_CELL_v20, 970),
+#endif
+#if defined (TODO)
+ /* PowerPC Cell v3.0 */
+ POWERPC_DEF("Cell_v3.0", CPU_POWERPC_CELL_v30, 970),
+#endif
+#if defined (TODO)
+ /* PowerPC Cell v3.1 */
+ POWERPC_DEF("Cell_v3.1", CPU_POWERPC_CELL_v31, 970),
+#endif
+#if defined (TODO)
+ /* PowerPC Cell v3.2 */
+ POWERPC_DEF("Cell_v3.2", CPU_POWERPC_CELL_v32, 970),
+#endif
+#if defined (TODO)
+ /* RS64 (Apache/A35) */
+ /* This one seems to support the whole POWER2 instruction set
+ * and the PowerPC 64 one.
+ */
+ /* What about A10 & A30 ? */
+ POWERPC_DEF("RS64", CPU_POWERPC_RS64, RS64),
+ POWERPC_DEF("Apache", CPU_POWERPC_RS64, RS64),
+ POWERPC_DEF("A35", CPU_POWERPC_RS64, RS64),
+#endif
+#if defined (TODO)
+ /* RS64-II (NorthStar/A50) */
+ POWERPC_DEF("RS64-II", CPU_POWERPC_RS64II, RS64),
+ POWERPC_DEF("NorthStar", CPU_POWERPC_RS64II, RS64),
+ POWERPC_DEF("A50", CPU_POWERPC_RS64II, RS64),
+#endif
+#if defined (TODO)
+ /* RS64-III (Pulsar) */
+ POWERPC_DEF("RS64-III", CPU_POWERPC_RS64III, RS64),
+ POWERPC_DEF("Pulsar", CPU_POWERPC_RS64III, RS64),
+#endif
+#if defined (TODO)
+ /* RS64-IV (IceStar/IStar/SStar) */
+ POWERPC_DEF("RS64-IV", CPU_POWERPC_RS64IV, RS64),
+ POWERPC_DEF("IceStar", CPU_POWERPC_RS64IV, RS64),
+ POWERPC_DEF("IStar", CPU_POWERPC_RS64IV, RS64),
+ POWERPC_DEF("SStar", CPU_POWERPC_RS64IV, RS64),
+#endif
+#endif /* defined (TARGET_PPC64) */
+ /* POWER */
+#if defined (TODO)
+ /* Original POWER */
+ POWERPC_DEF("POWER", CPU_POWERPC_POWER, POWER),
+ POWERPC_DEF("RIOS", CPU_POWERPC_POWER, POWER),
+ POWERPC_DEF("RSC", CPU_POWERPC_POWER, POWER),
+ POWERPC_DEF("RSC3308", CPU_POWERPC_POWER, POWER),
+ POWERPC_DEF("RSC4608", CPU_POWERPC_POWER, POWER),
+#endif
+#if defined (TODO)
+ /* POWER2 */
+ POWERPC_DEF("POWER2", CPU_POWERPC_POWER2, POWER),
+ POWERPC_DEF("RSC2", CPU_POWERPC_POWER2, POWER),
+ POWERPC_DEF("P2SC", CPU_POWERPC_POWER2, POWER),
+#endif
+ /* PA semi cores */
+#if defined (TODO)
+ /* PA PA6T */
+ POWERPC_DEF("PA6T", CPU_POWERPC_PA6T, PA6T),
+#endif
+ /* Generic PowerPCs */
+#if defined (TARGET_PPC64)
+ POWERPC_DEF("ppc64", CPU_POWERPC_PPC64, PPC64),
+#endif
+ POWERPC_DEF("ppc32", CPU_POWERPC_PPC32, PPC32),
+ POWERPC_DEF("ppc", CPU_POWERPC_DEFAULT, DEFAULT),
+ /* Fallback */
+ POWERPC_DEF("default", CPU_POWERPC_DEFAULT, DEFAULT),
+};
+
+/*****************************************************************************/
+/* Generic CPU instanciation routine */
+static void init_ppc_proc (CPUPPCState *env, const ppc_def_t *def)
+{
+#if !defined(CONFIG_USER_ONLY)
+ int i;
+
+ env->irq_inputs = NULL;
+ /* Set all exception vectors to an invalid address */
+ for (i = 0; i < POWERPC_EXCP_NB; i++)
+ env->excp_vectors[i] = (target_ulong)(-1ULL);
+ env->hreset_excp_prefix = 0x00000000;
+ env->ivor_mask = 0x00000000;
+ env->ivpr_mask = 0x00000000;
+ /* Default MMU definitions */
+ env->nb_BATs = 0;
+ env->nb_tlb = 0;
+ env->nb_ways = 0;
+#endif
+ /* Register SPR common to all PowerPC implementations */
+ gen_spr_generic(env);
+ spr_register(env, SPR_PVR, "PVR",
+ /* Linux permits userspace to read PVR */
+#if defined(CONFIG_LINUX_USER)
+ &spr_read_generic,
+#else
+ SPR_NOACCESS,
+#endif
+ SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ def->pvr);
+ /* Register SVR if it's defined to anything else than POWERPC_SVR_NONE */
+ if (def->svr != POWERPC_SVR_NONE) {
+ if (def->svr & POWERPC_SVR_E500) {
+ spr_register(env, SPR_E500_SVR, "SVR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ def->svr & ~POWERPC_SVR_E500);
+ } else {
+ spr_register(env, SPR_SVR, "SVR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ def->svr);
+ }
+ }
+ /* PowerPC implementation specific initialisations (SPRs, timers, ...) */
+ (*def->init_proc)(env);
+#if !defined(CONFIG_USER_ONLY)
+ env->excp_prefix = env->hreset_excp_prefix;
+#endif
+ /* MSR bits & flags consistency checks */
+ if (env->msr_mask & (1 << 25)) {
+ switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) {
+ case POWERPC_FLAG_SPE:
+ case POWERPC_FLAG_VRE:
+ break;
+ default:
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should define POWERPC_FLAG_SPE or POWERPC_FLAG_VRE\n");
+ exit(1);
+ }
+ } else if (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) {
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should not define POWERPC_FLAG_SPE nor POWERPC_FLAG_VRE\n");
+ exit(1);
+ }
+ if (env->msr_mask & (1 << 17)) {
+ switch (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE)) {
+ case POWERPC_FLAG_TGPR:
+ case POWERPC_FLAG_CE:
+ break;
+ default:
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should define POWERPC_FLAG_TGPR or POWERPC_FLAG_CE\n");
+ exit(1);
+ }
+ } else if (env->flags & (POWERPC_FLAG_TGPR | POWERPC_FLAG_CE)) {
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should not define POWERPC_FLAG_TGPR nor POWERPC_FLAG_CE\n");
+ exit(1);
+ }
+ if (env->msr_mask & (1 << 10)) {
+ switch (env->flags & (POWERPC_FLAG_SE | POWERPC_FLAG_DWE |
+ POWERPC_FLAG_UBLE)) {
+ case POWERPC_FLAG_SE:
+ case POWERPC_FLAG_DWE:
+ case POWERPC_FLAG_UBLE:
+ break;
+ default:
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should define POWERPC_FLAG_SE or POWERPC_FLAG_DWE or "
+ "POWERPC_FLAG_UBLE\n");
+ exit(1);
+ }
+ } else if (env->flags & (POWERPC_FLAG_SE | POWERPC_FLAG_DWE |
+ POWERPC_FLAG_UBLE)) {
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should not define POWERPC_FLAG_SE nor POWERPC_FLAG_DWE nor "
+ "POWERPC_FLAG_UBLE\n");
+ exit(1);
+ }
+ if (env->msr_mask & (1 << 9)) {
+ switch (env->flags & (POWERPC_FLAG_BE | POWERPC_FLAG_DE)) {
+ case POWERPC_FLAG_BE:
+ case POWERPC_FLAG_DE:
+ break;
+ default:
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should define POWERPC_FLAG_BE or POWERPC_FLAG_DE\n");
+ exit(1);
+ }
+ } else if (env->flags & (POWERPC_FLAG_BE | POWERPC_FLAG_DE)) {
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should not define POWERPC_FLAG_BE nor POWERPC_FLAG_DE\n");
+ exit(1);
+ }
+ if (env->msr_mask & (1 << 2)) {
+ switch (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM)) {
+ case POWERPC_FLAG_PX:
+ case POWERPC_FLAG_PMM:
+ break;
+ default:
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should define POWERPC_FLAG_PX or POWERPC_FLAG_PMM\n");
+ exit(1);
+ }
+ } else if (env->flags & (POWERPC_FLAG_PX | POWERPC_FLAG_PMM)) {
+ fprintf(stderr, "PowerPC MSR definition inconsistency\n"
+ "Should not define POWERPC_FLAG_PX nor POWERPC_FLAG_PMM\n");
+ exit(1);
+ }
+ if ((env->flags & (POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_BUS_CLK)) == 0) {
+ fprintf(stderr, "PowerPC flags inconsistency\n"
+ "Should define the time-base and decrementer clock source\n");
+ exit(1);
+ }
+ /* Allocate TLBs buffer when needed */
+#if !defined(CONFIG_USER_ONLY)
+ if (env->nb_tlb != 0) {
+ int nb_tlb = env->nb_tlb;
+ if (env->id_tlbs != 0)
+ nb_tlb *= 2;
+ env->tlb = qemu_mallocz(nb_tlb * sizeof(ppc_tlb_t));
+ /* Pre-compute some useful values */
+ env->tlb_per_way = env->nb_tlb / env->nb_ways;
+ }
+ if (env->irq_inputs == NULL) {
+ fprintf(stderr, "WARNING: no internal IRQ controller registered.\n"
+ " Attempt Qemu to crash very soon !\n");
+ }
+#endif
+ if (env->check_pow == NULL) {
+ fprintf(stderr, "WARNING: no power management check handler "
+ "registered.\n"
+ " Attempt Qemu to crash very soon !\n");
+ }
+}
+
+#if defined(PPC_DUMP_CPU)
+static void dump_ppc_sprs (CPUPPCState *env)
+{
+ ppc_spr_t *spr;
+#if !defined(CONFIG_USER_ONLY)
+ uint32_t sr, sw;
+#endif
+ uint32_t ur, uw;
+ int i, j, n;
+
+ printf("Special purpose registers:\n");
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < 32; j++) {
+ n = (i << 5) | j;
+ spr = &env->spr_cb[n];
+ uw = spr->uea_write != NULL && spr->uea_write != SPR_NOACCESS;
+ ur = spr->uea_read != NULL && spr->uea_read != SPR_NOACCESS;
+#if !defined(CONFIG_USER_ONLY)
+ sw = spr->oea_write != NULL && spr->oea_write != SPR_NOACCESS;
+ sr = spr->oea_read != NULL && spr->oea_read != SPR_NOACCESS;
+ if (sw || sr || uw || ur) {
+ printf("SPR: %4d (%03x) %-8s s%c%c u%c%c\n",
+ (i << 5) | j, (i << 5) | j, spr->name,
+ sw ? 'w' : '-', sr ? 'r' : '-',
+ uw ? 'w' : '-', ur ? 'r' : '-');
+ }
+#else
+ if (uw || ur) {
+ printf("SPR: %4d (%03x) %-8s u%c%c\n",
+ (i << 5) | j, (i << 5) | j, spr->name,
+ uw ? 'w' : '-', ur ? 'r' : '-');
+ }
+#endif
+ }
+ }
+ fflush(stdout);
+ fflush(stderr);
+}
+#endif
+
+/*****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+
+/* Opcode types */
+enum {
+ PPC_DIRECT = 0, /* Opcode routine */
+ PPC_INDIRECT = 1, /* Indirect opcode table */
+};
+
+static inline int is_indirect_opcode (void *handler)
+{
+ return ((unsigned long)handler & 0x03) == PPC_INDIRECT;
+}
+
+static inline opc_handler_t **ind_table(void *handler)
+{
+ return (opc_handler_t **)((unsigned long)handler & ~3);
+}
+
+/* Instruction table creation */
+/* Opcodes tables creation */
+static void fill_new_table (opc_handler_t **table, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ table[i] = &invalid_handler;
+}
+
+static int create_new_table (opc_handler_t **table, unsigned char idx)
+{
+ opc_handler_t **tmp;
+
+ tmp = malloc(0x20 * sizeof(opc_handler_t));
+ fill_new_table(tmp, 0x20);
+ table[idx] = (opc_handler_t *)((unsigned long)tmp | PPC_INDIRECT);
+
+ return 0;
+}
+
+static int insert_in_table (opc_handler_t **table, unsigned char idx,
+ opc_handler_t *handler)
+{
+ if (table[idx] != &invalid_handler)
+ return -1;
+ table[idx] = handler;
+
+ return 0;
+}
+
+static int register_direct_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx, opc_handler_t *handler)
+{
+ if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in main "
+ "opcode table\n", idx);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ printf(" Registered handler '%s' - new handler '%s'\n",
+ ppc_opcodes[idx]->oname, handler->oname);
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_in_table (opc_handler_t **table,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ if (table[idx1] == &invalid_handler) {
+ if (create_new_table(table, idx1) < 0) {
+ printf("*** ERROR: unable to create indirect table "
+ "idx=%02x\n", idx1);
+ return -1;
+ }
+ } else {
+ if (!is_indirect_opcode(table[idx1])) {
+ printf("*** ERROR: idx %02x already assigned to a direct "
+ "opcode\n", idx1);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ printf(" Registered handler '%s' - new handler '%s'\n",
+ ind_table(table[idx1])[idx2]->oname, handler->oname);
+#endif
+ return -1;
+ }
+ }
+ if (handler != NULL &&
+ insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in "
+ "opcode table %02x\n", idx2, idx1);
+#if defined(DO_PPC_STATISTICS) || defined(PPC_DUMP_CPU)
+ printf(" Registered handler '%s' - new handler '%s'\n",
+ ind_table(table[idx1])[idx2]->oname, handler->oname);
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ int ret;
+
+ ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
+
+ return ret;
+}
+
+static int register_dblind_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ unsigned char idx3, opc_handler_t *handler)
+{
+ if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
+ printf("*** ERROR: unable to join indirect table idx "
+ "[%02x-%02x]\n", idx1, idx2);
+ return -1;
+ }
+ if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
+ handler) < 0) {
+ printf("*** ERROR: unable to insert opcode "
+ "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn)
+{
+ if (insn->opc2 != 0xFF) {
+ if (insn->opc3 != 0xFF) {
+ if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
+ insn->opc3, &insn->handler) < 0)
+ return -1;
+ } else {
+ if (register_ind_insn(ppc_opcodes, insn->opc1,
+ insn->opc2, &insn->handler) < 0)
+ return -1;
+ }
+ } else {
+ if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_opcode_table (opc_handler_t **table, int len)
+{
+ int i, count, tmp;
+
+ for (i = 0, count = 0; i < len; i++) {
+ /* Consistency fixup */
+ if (table[i] == NULL)
+ table[i] = &invalid_handler;
+ if (table[i] != &invalid_handler) {
+ if (is_indirect_opcode(table[i])) {
+ tmp = test_opcode_table(ind_table(table[i]), 0x20);
+ if (tmp == 0) {
+ free(table[i]);
+ table[i] = &invalid_handler;
+ } else {
+ count++;
+ }
+ } else {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+static void fix_opcode_tables (opc_handler_t **ppc_opcodes)
+{
+ if (test_opcode_table(ppc_opcodes, 0x40) == 0)
+ printf("*** WARNING: no opcode defined !\n");
+}
+
+/*****************************************************************************/
+static int create_ppc_opcodes (CPUPPCState *env, const ppc_def_t *def)
+{
+ opcode_t *opc;
+
+ fill_new_table(env->opcodes, 0x40);
+ for (opc = opcodes; opc < &opcodes[ARRAY_SIZE(opcodes)]; opc++) {
+ if ((opc->handler.type & def->insns_flags) != 0) {
+ if (register_insn(env->opcodes, opc) < 0) {
+ printf("*** ERROR initializing PowerPC instruction "
+ "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2,
+ opc->opc3);
+ return -1;
+ }
+ }
+ }
+ fix_opcode_tables(env->opcodes);
+ fflush(stdout);
+ fflush(stderr);
+
+ return 0;
+}
+
+#if defined(PPC_DUMP_CPU)
+static void dump_ppc_insns (CPUPPCState *env)
+{
+ opc_handler_t **table, *handler;
+ const char *p, *q;
+ uint8_t opc1, opc2, opc3;
+
+ printf("Instructions set:\n");
+ /* opc1 is 6 bits long */
+ for (opc1 = 0x00; opc1 < 0x40; opc1++) {
+ table = env->opcodes;
+ handler = table[opc1];
+ if (is_indirect_opcode(handler)) {
+ /* opc2 is 5 bits long */
+ for (opc2 = 0; opc2 < 0x20; opc2++) {
+ table = env->opcodes;
+ handler = env->opcodes[opc1];
+ table = ind_table(handler);
+ handler = table[opc2];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ /* opc3 is 5 bits long */
+ for (opc3 = 0; opc3 < 0x20; opc3++) {
+ handler = table[opc3];
+ if (handler->handler != &gen_invalid) {
+ /* Special hack to properly dump SPE insns */
+ p = strchr(handler->oname, '_');
+ if (p == NULL) {
+ printf("INSN: %02x %02x %02x (%02d %04d) : "
+ "%s\n",
+ opc1, opc2, opc3, opc1,
+ (opc3 << 5) | opc2,
+ handler->oname);
+ } else {
+ q = "speundef";
+ if ((p - handler->oname) != strlen(q) ||
+ memcmp(handler->oname, q, strlen(q)) != 0) {
+ /* First instruction */
+ printf("INSN: %02x %02x %02x (%02d %04d) : "
+ "%.*s\n",
+ opc1, opc2 << 1, opc3, opc1,
+ (opc3 << 6) | (opc2 << 1),
+ (int)(p - handler->oname),
+ handler->oname);
+ }
+ if (strcmp(p + 1, q) != 0) {
+ /* Second instruction */
+ printf("INSN: %02x %02x %02x (%02d %04d) : "
+ "%s\n",
+ opc1, (opc2 << 1) | 1, opc3, opc1,
+ (opc3 << 6) | (opc2 << 1) | 1,
+ p + 1);
+ }
+ }
+ }
+ }
+ } else {
+ if (handler->handler != &gen_invalid) {
+ printf("INSN: %02x %02x -- (%02d %04d) : %s\n",
+ opc1, opc2, opc1, opc2, handler->oname);
+ }
+ }
+ }
+ } else {
+ if (handler->handler != &gen_invalid) {
+ printf("INSN: %02x -- -- (%02d ----) : %s\n",
+ opc1, opc1, handler->oname);
+ }
+ }
+ }
+}
+#endif
+
+static int gdb_get_float_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ stfq_p(mem_buf, env->fpr[n]);
+ return 8;
+ }
+ if (n == 32) {
+ /* FPSCR not implemented */
+ memset(mem_buf, 0, 4);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_set_float_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+ env->fpr[n] = ldfq_p(mem_buf);
+ return 8;
+ }
+ if (n == 32) {
+ /* FPSCR not implemented */
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_get_avr_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+#ifdef HOST_WORDS_BIGENDIAN
+ stq_p(mem_buf, env->avr[n].u64[0]);
+ stq_p(mem_buf+8, env->avr[n].u64[1]);
+#else
+ stq_p(mem_buf, env->avr[n].u64[1]);
+ stq_p(mem_buf+8, env->avr[n].u64[0]);
+#endif
+ return 16;
+ }
+ if (n == 32) {
+ stl_p(mem_buf, env->vscr);
+ return 4;
+ }
+ if (n == 33) {
+ stl_p(mem_buf, (uint32_t)env->spr[SPR_VRSAVE]);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_set_avr_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+#ifdef HOST_WORDS_BIGENDIAN
+ env->avr[n].u64[0] = ldq_p(mem_buf);
+ env->avr[n].u64[1] = ldq_p(mem_buf+8);
+#else
+ env->avr[n].u64[1] = ldq_p(mem_buf);
+ env->avr[n].u64[0] = ldq_p(mem_buf+8);
+#endif
+ return 16;
+ }
+ if (n == 32) {
+ env->vscr = ldl_p(mem_buf);
+ return 4;
+ }
+ if (n == 33) {
+ env->spr[SPR_VRSAVE] = (target_ulong)ldl_p(mem_buf);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_get_spe_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+#if defined(TARGET_PPC64)
+ stl_p(mem_buf, env->gpr[n] >> 32);
+#else
+ stl_p(mem_buf, env->gprh[n]);
+#endif
+ return 4;
+ }
+ if (n == 32) {
+ stq_p(mem_buf, env->spe_acc);
+ return 8;
+ }
+ if (n == 33) {
+ stl_p(mem_buf, env->spe_fscr);
+ return 4;
+ }
+ return 0;
+}
+
+static int gdb_set_spe_reg(CPUState *env, uint8_t *mem_buf, int n)
+{
+ if (n < 32) {
+#if defined(TARGET_PPC64)
+ target_ulong lo = (uint32_t)env->gpr[n];
+ target_ulong hi = (target_ulong)ldl_p(mem_buf) << 32;
+ env->gpr[n] = lo | hi;
+#else
+ env->gprh[n] = ldl_p(mem_buf);
+#endif
+ return 4;
+ }
+ if (n == 32) {
+ env->spe_acc = ldq_p(mem_buf);
+ return 8;
+ }
+ if (n == 33) {
+ env->spe_fscr = ldl_p(mem_buf);
+ return 4;
+ }
+ return 0;
+}
+
+int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def)
+{
+ env->msr_mask = def->msr_mask;
+ env->mmu_model = def->mmu_model;
+ env->excp_model = def->excp_model;
+ env->bus_model = def->bus_model;
+ env->insns_flags = def->insns_flags;
+ env->flags = def->flags;
+ env->bfd_mach = def->bfd_mach;
+ env->check_pow = def->check_pow;
+ if (create_ppc_opcodes(env, def) < 0)
+ return -1;
+ init_ppc_proc(env, def);
+
+ if (def->insns_flags & PPC_FLOAT) {
+ gdb_register_coprocessor(env, gdb_get_float_reg, gdb_set_float_reg,
+ 33, "power-fpu.xml", 0);
+ }
+ if (def->insns_flags & PPC_ALTIVEC) {
+ gdb_register_coprocessor(env, gdb_get_avr_reg, gdb_set_avr_reg,
+ 34, "power-altivec.xml", 0);
+ }
+ if (def->insns_flags & PPC_SPE) {
+ gdb_register_coprocessor(env, gdb_get_spe_reg, gdb_set_spe_reg,
+ 34, "power-spe.xml", 0);
+ }
+
+#if defined(PPC_DUMP_CPU)
+ {
+ const char *mmu_model, *excp_model, *bus_model;
+ switch (env->mmu_model) {
+ case POWERPC_MMU_32B:
+ mmu_model = "PowerPC 32";
+ break;
+ case POWERPC_MMU_SOFT_6xx:
+ mmu_model = "PowerPC 6xx/7xx with software driven TLBs";
+ break;
+ case POWERPC_MMU_SOFT_74xx:
+ mmu_model = "PowerPC 74xx with software driven TLBs";
+ break;
+ case POWERPC_MMU_SOFT_4xx:
+ mmu_model = "PowerPC 4xx with software driven TLBs";
+ break;
+ case POWERPC_MMU_SOFT_4xx_Z:
+ mmu_model = "PowerPC 4xx with software driven TLBs "
+ "and zones protections";
+ break;
+ case POWERPC_MMU_REAL:
+ mmu_model = "PowerPC real mode only";
+ break;
+ case POWERPC_MMU_MPC8xx:
+ mmu_model = "PowerPC MPC8xx";
+ break;
+ case POWERPC_MMU_BOOKE:
+ mmu_model = "PowerPC BookE";
+ break;
+ case POWERPC_MMU_BOOKE_FSL:
+ mmu_model = "PowerPC BookE FSL";
+ break;
+ case POWERPC_MMU_601:
+ mmu_model = "PowerPC 601";
+ break;
+#if defined (TARGET_PPC64)
+ case POWERPC_MMU_64B:
+ mmu_model = "PowerPC 64";
+ break;
+ case POWERPC_MMU_620:
+ mmu_model = "PowerPC 620";
+ break;
+#endif
+ default:
+ mmu_model = "Unknown or invalid";
+ break;
+ }
+ switch (env->excp_model) {
+ case POWERPC_EXCP_STD:
+ excp_model = "PowerPC";
+ break;
+ case POWERPC_EXCP_40x:
+ excp_model = "PowerPC 40x";
+ break;
+ case POWERPC_EXCP_601:
+ excp_model = "PowerPC 601";
+ break;
+ case POWERPC_EXCP_602:
+ excp_model = "PowerPC 602";
+ break;
+ case POWERPC_EXCP_603:
+ excp_model = "PowerPC 603";
+ break;
+ case POWERPC_EXCP_603E:
+ excp_model = "PowerPC 603e";
+ break;
+ case POWERPC_EXCP_604:
+ excp_model = "PowerPC 604";
+ break;
+ case POWERPC_EXCP_7x0:
+ excp_model = "PowerPC 740/750";
+ break;
+ case POWERPC_EXCP_7x5:
+ excp_model = "PowerPC 745/755";
+ break;
+ case POWERPC_EXCP_74xx:
+ excp_model = "PowerPC 74xx";
+ break;
+ case POWERPC_EXCP_BOOKE:
+ excp_model = "PowerPC BookE";
+ break;
+#if defined (TARGET_PPC64)
+ case POWERPC_EXCP_970:
+ excp_model = "PowerPC 970";
+ break;
+#endif
+ default:
+ excp_model = "Unknown or invalid";
+ break;
+ }
+ switch (env->bus_model) {
+ case PPC_FLAGS_INPUT_6xx:
+ bus_model = "PowerPC 6xx";
+ break;
+ case PPC_FLAGS_INPUT_BookE:
+ bus_model = "PowerPC BookE";
+ break;
+ case PPC_FLAGS_INPUT_405:
+ bus_model = "PowerPC 405";
+ break;
+ case PPC_FLAGS_INPUT_401:
+ bus_model = "PowerPC 401/403";
+ break;
+ case PPC_FLAGS_INPUT_RCPU:
+ bus_model = "RCPU / MPC8xx";
+ break;
+#if defined (TARGET_PPC64)
+ case PPC_FLAGS_INPUT_970:
+ bus_model = "PowerPC 970";
+ break;
+#endif
+ default:
+ bus_model = "Unknown or invalid";
+ break;
+ }
+ printf("PowerPC %-12s : PVR %08x MSR %016" PRIx64 "\n"
+ " MMU model : %s\n",
+ def->name, def->pvr, def->msr_mask, mmu_model);
+#if !defined(CONFIG_USER_ONLY)
+ if (env->tlb != NULL) {
+ printf(" %d %s TLB in %d ways\n",
+ env->nb_tlb, env->id_tlbs ? "splitted" : "merged",
+ env->nb_ways);
+ }
+#endif
+ printf(" Exceptions model : %s\n"
+ " Bus model : %s\n",
+ excp_model, bus_model);
+ printf(" MSR features :\n");
+ if (env->flags & POWERPC_FLAG_SPE)
+ printf(" signal processing engine enable"
+ "\n");
+ else if (env->flags & POWERPC_FLAG_VRE)
+ printf(" vector processor enable\n");
+ if (env->flags & POWERPC_FLAG_TGPR)
+ printf(" temporary GPRs\n");
+ else if (env->flags & POWERPC_FLAG_CE)
+ printf(" critical input enable\n");
+ if (env->flags & POWERPC_FLAG_SE)
+ printf(" single-step trace mode\n");
+ else if (env->flags & POWERPC_FLAG_DWE)
+ printf(" debug wait enable\n");
+ else if (env->flags & POWERPC_FLAG_UBLE)
+ printf(" user BTB lock enable\n");
+ if (env->flags & POWERPC_FLAG_BE)
+ printf(" branch-step trace mode\n");
+ else if (env->flags & POWERPC_FLAG_DE)
+ printf(" debug interrupt enable\n");
+ if (env->flags & POWERPC_FLAG_PX)
+ printf(" inclusive protection\n");
+ else if (env->flags & POWERPC_FLAG_PMM)
+ printf(" performance monitor mark\n");
+ if (env->flags == POWERPC_FLAG_NONE)
+ printf(" none\n");
+ printf(" Time-base/decrementer clock source: %s\n",
+ env->flags & POWERPC_FLAG_RTC_CLK ? "RTC clock" : "bus clock");
+ }
+ dump_ppc_insns(env);
+ dump_ppc_sprs(env);
+ fflush(stdout);
+#endif
+
+ return 0;
+}
+
+static const ppc_def_t *ppc_find_by_pvr (uint32_t pvr)
+{
+ const ppc_def_t *ret;
+ uint32_t pvr_rev;
+ int i, best, match, best_match, max;
+
+ ret = NULL;
+ max = ARRAY_SIZE(ppc_defs);
+ best = -1;
+ pvr_rev = pvr & 0xFFFF;
+ /* We want all specified bits to match */
+ best_match = 32 - ctz32(pvr_rev);
+ for (i = 0; i < max; i++) {
+ /* We check that the 16 higher bits are the same to ensure the CPU
+ * model will be the choosen one.
+ */
+ if (((pvr ^ ppc_defs[i].pvr) >> 16) == 0) {
+ /* We want as much as possible of the low-level 16 bits
+ * to be the same but we allow inexact matches.
+ */
+ match = clz32(pvr_rev ^ (ppc_defs[i].pvr & 0xFFFF));
+ /* We check '>=' instead of '>' because the PPC_defs table
+ * is ordered by increasing revision.
+ * Then, we will match the higher revision compatible
+ * with the requested PVR
+ */
+ if (match >= best_match) {
+ best = i;
+ best_match = match;
+ }
+ }
+ }
+ if (best != -1)
+ ret = &ppc_defs[best];
+
+ return ret;
+}
+
+#include <ctype.h>
+
+const ppc_def_t *cpu_ppc_find_by_name (const char *name)
+{
+ const ppc_def_t *ret;
+ const char *p;
+ int i, max, len;
+
+ /* Check if the given name is a PVR */
+ len = strlen(name);
+ if (len == 10 && name[0] == '0' && name[1] == 'x') {
+ p = name + 2;
+ goto check_pvr;
+ } else if (len == 8) {
+ p = name;
+ check_pvr:
+ for (i = 0; i < 8; i++) {
+ if (!qemu_isxdigit(*p++))
+ break;
+ }
+ if (i == 8)
+ return ppc_find_by_pvr(strtoul(name, NULL, 16));
+ }
+ ret = NULL;
+ max = ARRAY_SIZE(ppc_defs);
+ for (i = 0; i < max; i++) {
+ if (strcasecmp(name, ppc_defs[i].name) == 0) {
+ ret = &ppc_defs[i];
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf)
+{
+ int i, max;
+
+ max = ARRAY_SIZE(ppc_defs);
+ for (i = 0; i < max; i++) {
+ (*cpu_fprintf)(f, "PowerPC %-16s PVR %08x\n",
+ ppc_defs[i].name, ppc_defs[i].pvr);
+ }
+}
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
new file mode 100644
index 0000000..e47c372
--- /dev/null
+++ b/target-s390x/cpu.h
@@ -0,0 +1,269 @@
+/*
+ * S/390 virtual CPU header
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CPU_S390X_H
+#define CPU_S390X_H
+
+#define TARGET_LONG_BITS 64
+
+#define ELF_MACHINE EM_S390
+
+#define CPUState struct CPUS390XState
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define NB_MMU_MODES 2
+
+typedef union FPReg {
+ struct {
+#ifdef WORDS_BIGENDIAN
+ float32 e;
+ int32_t __pad;
+#else
+ int32_t __pad;
+ float32 e;
+#endif
+ };
+ float64 d;
+ uint64_t i;
+} FPReg;
+
+typedef struct CPUS390XState {
+ uint64_t regs[16]; /* GP registers */
+
+ uint32_t aregs[16]; /* access registers */
+
+ uint32_t fpc; /* floating-point control register */
+ FPReg fregs[16]; /* FP registers */
+ float_status fpu_status; /* passed to softfloat lib */
+
+ struct {
+ uint64_t mask;
+ uint64_t addr;
+ } psw;
+
+ int cc; /* condition code (0-3) */
+
+ uint64_t __excp_addr;
+
+ CPU_COMMON
+} CPUS390XState;
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regs[15] = newsp;
+ env->regs[0] = 0;
+}
+#endif
+
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ /* XXX: Currently we don't implement virtual memory */
+ return 0;
+}
+
+CPUS390XState *cpu_s390x_init(const char *cpu_model);
+int cpu_s390x_exec(CPUS390XState *s);
+void cpu_s390x_close(CPUS390XState *s);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+ signal handlers to inform the virtual CPU of exceptions. non zero
+ is returned if the signal was handled by the virtual CPU. */
+int cpu_s390x_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+int cpu_s390x_handle_mmu_fault (CPUS390XState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmuu);
+#define cpu_handle_mmu_fault cpu_s390x_handle_mmu_fault
+
+#define TARGET_PAGE_BITS 12
+
+/* ??? This is certainly wrong for 64-bit s390x, but given that only KVM
+ emulation actually works, this is good enough for a placeholder. */
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#ifndef CONFIG_USER_ONLY
+int s390_virtio_hypercall(CPUState *env);
+void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token);
+CPUState *s390_cpu_addr2state(uint16_t cpu_addr);
+#endif
+
+
+#define cpu_init cpu_s390x_init
+#define cpu_exec cpu_s390x_exec
+#define cpu_gen_code cpu_s390x_gen_code
+
+#include "cpu-all.h"
+
+#define EXCP_OPEX 1 /* operation exception (sigill) */
+#define EXCP_SVC 2 /* supervisor call (syscall) */
+#define EXCP_ADDR 5 /* addressing exception */
+#define EXCP_EXECUTE_SVC 0xff00000 /* supervisor call via execute insn */
+
+static inline void cpu_get_tb_cpu_state(CPUState* env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->psw.addr;
+ /* XXX this is correct for user-mode emulation, but needs
+ * the asce register information as well when softmmu
+ * is implemented in the future */
+ *cs_base = 0;
+ *flags = env->psw.mask;
+}
+
+/* Program Status Word. */
+#define S390_PSWM_REGNUM 0
+#define S390_PSWA_REGNUM 1
+/* General Purpose Registers. */
+#define S390_R0_REGNUM 2
+#define S390_R1_REGNUM 3
+#define S390_R2_REGNUM 4
+#define S390_R3_REGNUM 5
+#define S390_R4_REGNUM 6
+#define S390_R5_REGNUM 7
+#define S390_R6_REGNUM 8
+#define S390_R7_REGNUM 9
+#define S390_R8_REGNUM 10
+#define S390_R9_REGNUM 11
+#define S390_R10_REGNUM 12
+#define S390_R11_REGNUM 13
+#define S390_R12_REGNUM 14
+#define S390_R13_REGNUM 15
+#define S390_R14_REGNUM 16
+#define S390_R15_REGNUM 17
+/* Access Registers. */
+#define S390_A0_REGNUM 18
+#define S390_A1_REGNUM 19
+#define S390_A2_REGNUM 20
+#define S390_A3_REGNUM 21
+#define S390_A4_REGNUM 22
+#define S390_A5_REGNUM 23
+#define S390_A6_REGNUM 24
+#define S390_A7_REGNUM 25
+#define S390_A8_REGNUM 26
+#define S390_A9_REGNUM 27
+#define S390_A10_REGNUM 28
+#define S390_A11_REGNUM 29
+#define S390_A12_REGNUM 30
+#define S390_A13_REGNUM 31
+#define S390_A14_REGNUM 32
+#define S390_A15_REGNUM 33
+/* Floating Point Control Word. */
+#define S390_FPC_REGNUM 34
+/* Floating Point Registers. */
+#define S390_F0_REGNUM 35
+#define S390_F1_REGNUM 36
+#define S390_F2_REGNUM 37
+#define S390_F3_REGNUM 38
+#define S390_F4_REGNUM 39
+#define S390_F5_REGNUM 40
+#define S390_F6_REGNUM 41
+#define S390_F7_REGNUM 42
+#define S390_F8_REGNUM 43
+#define S390_F9_REGNUM 44
+#define S390_F10_REGNUM 45
+#define S390_F11_REGNUM 46
+#define S390_F12_REGNUM 47
+#define S390_F13_REGNUM 48
+#define S390_F14_REGNUM 49
+#define S390_F15_REGNUM 50
+/* Total. */
+#define S390_NUM_REGS 51
+
+/* Pseudo registers -- PC and condition code. */
+#define S390_PC_REGNUM S390_NUM_REGS
+#define S390_CC_REGNUM (S390_NUM_REGS+1)
+#define S390_NUM_PSEUDO_REGS 2
+#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2)
+
+
+
+/* Program Status Word. */
+#define S390_PSWM_REGNUM 0
+#define S390_PSWA_REGNUM 1
+/* General Purpose Registers. */
+#define S390_R0_REGNUM 2
+#define S390_R1_REGNUM 3
+#define S390_R2_REGNUM 4
+#define S390_R3_REGNUM 5
+#define S390_R4_REGNUM 6
+#define S390_R5_REGNUM 7
+#define S390_R6_REGNUM 8
+#define S390_R7_REGNUM 9
+#define S390_R8_REGNUM 10
+#define S390_R9_REGNUM 11
+#define S390_R10_REGNUM 12
+#define S390_R11_REGNUM 13
+#define S390_R12_REGNUM 14
+#define S390_R13_REGNUM 15
+#define S390_R14_REGNUM 16
+#define S390_R15_REGNUM 17
+/* Access Registers. */
+#define S390_A0_REGNUM 18
+#define S390_A1_REGNUM 19
+#define S390_A2_REGNUM 20
+#define S390_A3_REGNUM 21
+#define S390_A4_REGNUM 22
+#define S390_A5_REGNUM 23
+#define S390_A6_REGNUM 24
+#define S390_A7_REGNUM 25
+#define S390_A8_REGNUM 26
+#define S390_A9_REGNUM 27
+#define S390_A10_REGNUM 28
+#define S390_A11_REGNUM 29
+#define S390_A12_REGNUM 30
+#define S390_A13_REGNUM 31
+#define S390_A14_REGNUM 32
+#define S390_A15_REGNUM 33
+/* Floating Point Control Word. */
+#define S390_FPC_REGNUM 34
+/* Floating Point Registers. */
+#define S390_F0_REGNUM 35
+#define S390_F1_REGNUM 36
+#define S390_F2_REGNUM 37
+#define S390_F3_REGNUM 38
+#define S390_F4_REGNUM 39
+#define S390_F5_REGNUM 40
+#define S390_F6_REGNUM 41
+#define S390_F7_REGNUM 42
+#define S390_F8_REGNUM 43
+#define S390_F9_REGNUM 44
+#define S390_F10_REGNUM 45
+#define S390_F11_REGNUM 46
+#define S390_F12_REGNUM 47
+#define S390_F13_REGNUM 48
+#define S390_F14_REGNUM 49
+#define S390_F15_REGNUM 50
+/* Total. */
+#define S390_NUM_REGS 51
+
+/* Pseudo registers -- PC and condition code. */
+#define S390_PC_REGNUM S390_NUM_REGS
+#define S390_CC_REGNUM (S390_NUM_REGS+1)
+#define S390_NUM_PSEUDO_REGS 2
+#define S390_NUM_TOTAL_REGS (S390_NUM_REGS+2)
+
+
+#endif
diff --git a/target-s390x/exec.h b/target-s390x/exec.h
new file mode 100644
index 0000000..bf3f264
--- /dev/null
+++ b/target-s390x/exec.h
@@ -0,0 +1,53 @@
+/*
+ * S/390 execution defines
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dyngen-exec.h"
+
+register struct CPUS390XState *env asm(AREG0);
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return env->interrupt_request & CPU_INTERRUPT_HARD; // guess
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+ if (!env->halted) {
+ return 0;
+ }
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock* tb)
+{
+ env->psw.addr = tb->pc;
+}
+
diff --git a/target-s390x/helper.c b/target-s390x/helper.c
new file mode 100644
index 0000000..4a5297b
--- /dev/null
+++ b/target-s390x/helper.c
@@ -0,0 +1,84 @@
+/*
+ * S/390 helpers
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "gdbstub.h"
+#include "qemu-common.h"
+
+#include <linux/kvm.h>
+#include "kvm.h"
+
+CPUS390XState *cpu_s390x_init(const char *cpu_model)
+{
+ CPUS390XState *env;
+ static int inited = 0;
+
+ env = qemu_mallocz(sizeof(CPUS390XState));
+ cpu_exec_init(env);
+ if (!inited) {
+ inited = 1;
+ }
+
+ env->cpu_model_str = cpu_model;
+ cpu_reset(env);
+ qemu_init_vcpu(env);
+ return env;
+}
+
+void cpu_reset(CPUS390XState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ memset(env, 0, offsetof(CPUS390XState, breakpoints));
+ /* FIXME: reset vector? */
+ tlb_flush(env, 1);
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return 0;
+}
+
+#ifndef CONFIG_USER_ONLY
+
+int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ target_ulong phys;
+ int prot;
+
+ /* XXX: implement mmu */
+
+ phys = address;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+
+ tlb_set_page(env, address & TARGET_PAGE_MASK,
+ phys & TARGET_PAGE_MASK, prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+ return 0;
+}
+#endif /* CONFIG_USER_ONLY */
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
new file mode 100644
index 0000000..38823f5
--- /dev/null
+++ b/target-s390x/kvm.c
@@ -0,0 +1,507 @@
+/*
+ * QEMU S390x KVM implementation
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kvm.h>
+#include <asm/ptrace.h>
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "kvm.h"
+#include "cpu.h"
+#include "device_tree.h"
+
+/* #define DEBUG_KVM */
+
+#ifdef DEBUG_KVM
+#define dprintf(fmt, ...) \
+ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+ do { } while (0)
+#endif
+
+#define IPA0_DIAG 0x8300
+#define IPA0_SIGP 0xae00
+#define IPA0_PRIV 0xb200
+
+#define PRIV_SCLP_CALL 0x20
+#define DIAG_KVM_HYPERCALL 0x500
+#define DIAG_KVM_BREAKPOINT 0x501
+
+#define SCP_LENGTH 0x00
+#define SCP_FUNCTION_CODE 0x02
+#define SCP_CONTROL_MASK 0x03
+#define SCP_RESPONSE_CODE 0x06
+#define SCP_MEM_CODE 0x08
+#define SCP_INCREMENT 0x0a
+
+#define ICPT_INSTRUCTION 0x04
+#define ICPT_WAITPSW 0x1c
+#define ICPT_SOFT_INTERCEPT 0x24
+#define ICPT_CPU_STOP 0x28
+#define ICPT_IO 0x40
+
+#define SIGP_RESTART 0x06
+#define SIGP_INITIAL_CPU_RESET 0x0b
+#define SIGP_STORE_STATUS_ADDR 0x0e
+#define SIGP_SET_ARCH 0x12
+
+#define SCLP_CMDW_READ_SCP_INFO 0x00020001
+#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+int kvm_arch_init(KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_init_vcpu(CPUState *env)
+{
+ int ret = 0;
+
+ if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) {
+ perror("cannot init reset vcpu");
+ }
+
+ return ret;
+}
+
+void kvm_arch_reset_vcpu(CPUState *env)
+{
+ /* FIXME: add code to reset vcpu. */
+}
+
+int kvm_arch_put_registers(CPUState *env, int level)
+{
+ struct kvm_regs regs;
+ int ret;
+ int i;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < 16; i++) {
+ regs.gprs[i] = env->regs[i];
+ }
+
+ ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ env->kvm_run->psw_addr = env->psw.addr;
+ env->kvm_run->psw_mask = env->psw.mask;
+
+ return ret;
+}
+
+int kvm_arch_get_registers(CPUState *env)
+{
+ int ret;
+ struct kvm_regs regs;
+ int i;
+
+ ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, &regs);
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = regs.gprs[i];
+ }
+
+ env->psw.addr = env->kvm_run->psw_addr;
+ env->psw.mask = env->kvm_run->psw_mask;
+
+ return 0;
+}
+
+int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
+
+ if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) ||
+ cpu_memory_rw_debug(env, bp->pc, (uint8_t *)diag_501, 4, 1)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp)
+{
+ uint8_t t[4];
+ static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01};
+
+ if (cpu_memory_rw_debug(env, bp->pc, t, 4, 0)) {
+ return -EINVAL;
+ } else if (memcmp(t, diag_501, 4)) {
+ return -EINVAL;
+ } else if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int kvm_arch_pre_run(CPUState *env, struct kvm_run *run)
+{
+ return 0;
+}
+
+int kvm_arch_post_run(CPUState *env, struct kvm_run *run)
+{
+ return 0;
+}
+
+int kvm_arch_process_irqchip_events(CPUState *env)
+{
+ return 0;
+}
+
+static void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm,
+ uint64_t parm64, int vm)
+{
+ struct kvm_s390_interrupt kvmint;
+ int r;
+
+ if (!env->kvm_state) {
+ return;
+ }
+
+ env->halted = 0;
+ env->exception_index = -1;
+
+ kvmint.type = type;
+ kvmint.parm = parm;
+ kvmint.parm64 = parm64;
+
+ if (vm) {
+ r = kvm_vm_ioctl(env->kvm_state, KVM_S390_INTERRUPT, &kvmint);
+ } else {
+ r = kvm_vcpu_ioctl(env, KVM_S390_INTERRUPT, &kvmint);
+ }
+
+ if (r < 0) {
+ fprintf(stderr, "KVM failed to inject interrupt\n");
+ exit(1);
+ }
+}
+
+void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token)
+{
+ kvm_s390_interrupt_internal(env, KVM_S390_INT_VIRTIO, config_change,
+ token, 1);
+}
+
+static void kvm_s390_interrupt(CPUState *env, int type, uint32_t code)
+{
+ kvm_s390_interrupt_internal(env, type, code, 0, 0);
+}
+
+static void enter_pgmcheck(CPUState *env, uint16_t code)
+{
+ kvm_s390_interrupt(env, KVM_S390_PROGRAM_INT, code);
+}
+
+static void setcc(CPUState *env, uint64_t cc)
+{
+ env->kvm_run->psw_mask &= ~(3ul << 44);
+ env->kvm_run->psw_mask |= (cc & 3) << 44;
+
+ env->psw.mask &= ~(3ul << 44);
+ env->psw.mask |= (cc & 3) << 44;
+}
+
+static int sclp_service_call(CPUState *env, struct kvm_run *run, uint16_t ipbh0)
+{
+ uint32_t sccb;
+ uint64_t code;
+ int r = 0;
+
+ cpu_synchronize_state(env);
+ sccb = env->regs[ipbh0 & 0xf];
+ code = env->regs[(ipbh0 & 0xf0) >> 4];
+
+ dprintf("sclp(0x%x, 0x%lx)\n", sccb, code);
+
+ if (sccb & ~0x7ffffff8ul) {
+ fprintf(stderr, "KVM: invalid sccb address 0x%x\n", sccb);
+ r = -1;
+ goto out;
+ }
+
+ switch(code) {
+ case SCLP_CMDW_READ_SCP_INFO:
+ case SCLP_CMDW_READ_SCP_INFO_FORCED:
+ stw_phys(sccb + SCP_MEM_CODE, ram_size >> 20);
+ stb_phys(sccb + SCP_INCREMENT, 1);
+ stw_phys(sccb + SCP_RESPONSE_CODE, 0x10);
+ setcc(env, 0);
+
+ kvm_s390_interrupt_internal(env, KVM_S390_INT_SERVICE,
+ sccb & ~3, 0, 1);
+ break;
+ default:
+ dprintf("KVM: invalid sclp call 0x%x / 0x%lx\n", sccb, code);
+ r = -1;
+ break;
+ }
+
+out:
+ if (r < 0) {
+ setcc(env, 3);
+ }
+ return 0;
+}
+
+static int handle_priv(CPUState *env, struct kvm_run *run, uint8_t ipa1)
+{
+ int r = 0;
+ uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16;
+
+ dprintf("KVM: PRIV: %d\n", ipa1);
+ switch (ipa1) {
+ case PRIV_SCLP_CALL:
+ r = sclp_service_call(env, run, ipbh0);
+ break;
+ default:
+ dprintf("KVM: unknown PRIV: 0x%x\n", ipa1);
+ r = -1;
+ break;
+ }
+
+ return r;
+}
+
+static int handle_hypercall(CPUState *env, struct kvm_run *run)
+{
+ int r;
+
+ cpu_synchronize_state(env);
+ r = s390_virtio_hypercall(env);
+
+ return r;
+}
+
+static int handle_diag(CPUState *env, struct kvm_run *run, int ipb_code)
+{
+ int r = 0;
+
+ switch (ipb_code) {
+ case DIAG_KVM_HYPERCALL:
+ r = handle_hypercall(env, run);
+ break;
+ case DIAG_KVM_BREAKPOINT:
+ sleep(10);
+ break;
+ default:
+ dprintf("KVM: unknown DIAG: 0x%x\n", ipb_code);
+ r = -1;
+ break;
+ }
+
+ return r;
+}
+
+static int s390_cpu_restart(CPUState *env)
+{
+ kvm_s390_interrupt(env, KVM_S390_RESTART, 0);
+ env->halted = 0;
+ env->exception_index = -1;
+ qemu_cpu_kick(env);
+ dprintf("DONE: SIGP cpu restart: %p\n", env);
+ return 0;
+}
+
+static int s390_store_status(CPUState *env, uint32_t parameter)
+{
+ /* XXX */
+ fprintf(stderr, "XXX SIGP store status\n");
+ return -1;
+}
+
+static int s390_cpu_initial_reset(CPUState *env)
+{
+ int i;
+
+ if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) {
+ perror("cannot init reset vcpu");
+ }
+
+ /* Manually zero out all registers */
+ cpu_synchronize_state(env);
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = 0;
+ }
+
+ dprintf("DONE: SIGP initial reset: %p\n", env);
+ return 0;
+}
+
+static int handle_sigp(CPUState *env, struct kvm_run *run, uint8_t ipa1)
+{
+ uint8_t order_code;
+ uint32_t parameter;
+ uint16_t cpu_addr;
+ uint8_t t;
+ int r = -1;
+ CPUState *target_env;
+
+ cpu_synchronize_state(env);
+
+ /* get order code */
+ order_code = run->s390_sieic.ipb >> 28;
+ if (order_code > 0) {
+ order_code = env->regs[order_code];
+ }
+ order_code += (run->s390_sieic.ipb & 0x0fff0000) >> 16;
+
+ /* get parameters */
+ t = (ipa1 & 0xf0) >> 4;
+ if (!(t % 2)) {
+ t++;
+ }
+
+ parameter = env->regs[t] & 0x7ffffe00;
+ cpu_addr = env->regs[ipa1 & 0x0f];
+
+ target_env = s390_cpu_addr2state(cpu_addr);
+ if (!target_env) {
+ goto out;
+ }
+
+ switch (order_code) {
+ case SIGP_RESTART:
+ r = s390_cpu_restart(target_env);
+ break;
+ case SIGP_STORE_STATUS_ADDR:
+ r = s390_store_status(target_env, parameter);
+ break;
+ case SIGP_SET_ARCH:
+ /* make the caller panic */
+ return -1;
+ case SIGP_INITIAL_CPU_RESET:
+ r = s390_cpu_initial_reset(target_env);
+ break;
+ default:
+ fprintf(stderr, "KVM: unknown SIGP: 0x%x\n", ipa1);
+ break;
+ }
+
+out:
+ setcc(env, r ? 3 : 0);
+ return 0;
+}
+
+static int handle_instruction(CPUState *env, struct kvm_run *run)
+{
+ unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00);
+ uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff;
+ int ipb_code = (run->s390_sieic.ipb & 0x0fff0000) >> 16;
+ int r = -1;
+
+ dprintf("handle_instruction 0x%x 0x%x\n", run->s390_sieic.ipa, run->s390_sieic.ipb);
+ switch (ipa0) {
+ case IPA0_PRIV:
+ r = handle_priv(env, run, ipa1);
+ break;
+ case IPA0_DIAG:
+ r = handle_diag(env, run, ipb_code);
+ break;
+ case IPA0_SIGP:
+ r = handle_sigp(env, run, ipa1);
+ break;
+ }
+
+ if (r < 0) {
+ enter_pgmcheck(env, 0x0001);
+ }
+ return r;
+}
+
+static int handle_intercept(CPUState *env)
+{
+ struct kvm_run *run = env->kvm_run;
+ int icpt_code = run->s390_sieic.icptcode;
+ int r = 0;
+
+ dprintf("intercept: 0x%x (at 0x%lx)\n", icpt_code, env->kvm_run->psw_addr);
+ switch (icpt_code) {
+ case ICPT_INSTRUCTION:
+ r = handle_instruction(env, run);
+ break;
+ case ICPT_WAITPSW:
+ /* XXX What to do on system shutdown? */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ break;
+ case ICPT_SOFT_INTERCEPT:
+ fprintf(stderr, "KVM unimplemented icpt SOFT\n");
+ exit(1);
+ break;
+ case ICPT_CPU_STOP:
+ qemu_system_shutdown_request();
+ break;
+ case ICPT_IO:
+ fprintf(stderr, "KVM unimplemented icpt IO\n");
+ exit(1);
+ break;
+ default:
+ fprintf(stderr, "Unknown intercept code: %d\n", icpt_code);
+ exit(1);
+ break;
+ }
+
+ return r;
+}
+
+int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run)
+{
+ int ret = 0;
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_S390_SIEIC:
+ ret = handle_intercept(env);
+ break;
+ case KVM_EXIT_S390_RESET:
+ fprintf(stderr, "RESET not implemented\n");
+ exit(1);
+ break;
+ default:
+ fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason);
+ break;
+ }
+
+ return ret;
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUState *env)
+{
+ return true;
+}
diff --git a/target-s390x/machine.c b/target-s390x/machine.c
new file mode 100644
index 0000000..3e79be6
--- /dev/null
+++ b/target-s390x/machine.c
@@ -0,0 +1,30 @@
+/*
+ * QEMU S390x machine definitions
+ *
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ return 0;
+}
diff --git a/target-s390x/op_helper.c b/target-s390x/op_helper.c
new file mode 100644
index 0000000..402df2d
--- /dev/null
+++ b/target-s390x/op_helper.c
@@ -0,0 +1,73 @@
+/*
+ * S/390 helper routines
+ *
+ * Copyright (c) 2009 Alexander Graf
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exec.h"
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ unsigned long pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret != 0)) {
+ if (likely(retaddr)) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ tb = tb_find_pc(pc);
+ if (likely(tb)) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+ }
+ }
+ /* XXX */
+ /* helper_raise_exception_err(env->exception_index, env->error_code); */
+ }
+ env = saved_env;
+}
+
+#endif
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
new file mode 100644
index 0000000..d33bfb1
--- /dev/null
+++ b/target-s390x/translate.c
@@ -0,0 +1,61 @@
+/*
+ * S/390 translation
+ *
+ * Copyright (c) 2009 Ulrich Hecht
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-log.h"
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "R%02d=%016lx", i, env->regs[i]);
+ if ((i % 4) == 3) {
+ cpu_fprintf(f, "\n");
+ } else {
+ cpu_fprintf(f, " ");
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ cpu_fprintf(f, "F%02d=%016lx", i, (long)env->fregs[i].i);
+ if ((i % 4) == 3) {
+ cpu_fprintf(f, "\n");
+ } else {
+ cpu_fprintf(f, " ");
+ }
+ }
+ cpu_fprintf(f, "PSW=mask %016lx addr %016lx cc %02x\n", env->psw.mask, env->psw.addr, env->cc);
+}
+
+void gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
+{
+}
+
+void gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
+{
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->psw.addr = gen_opc_pc[pc_pos];
+}
diff --git a/target-sh4/README.sh4 b/target-sh4/README.sh4
new file mode 100644
index 0000000..e578830
--- /dev/null
+++ b/target-sh4/README.sh4
@@ -0,0 +1,150 @@
+qemu target: sh4
+author: Samuel Tardieu <sam@rfc1149.net>
+last modified: Tue Dec 6 07:22:44 CET 2005
+
+The sh4 target is not ready at all yet for integration in qemu. This
+file describes the current state of implementation.
+
+Most places requiring attention and/or modification can be detected by
+looking for "XXXXX" or "abort()".
+
+The sh4 core is located in target-sh4/*, while the 7750 peripheral
+features (IO ports for example) are located in hw/sh7750.[ch]. The
+main board description is in hw/shix.c, and the NAND flash in
+hw/tc58128.[ch].
+
+All the shortcomings indicated here will eventually be resolved. This
+is a work in progress. Features are added in a semi-random order: if a
+point is blocking to progress on booting the Linux kernel for the shix
+board, it is addressed first; if feedback is necessary and no progress
+can be made on blocking points until it is received, a random feature
+is worked on.
+
+Goals
+-----
+
+The primary model being worked on is the soft MMU target to be able to
+emulate the Shix 2.0 board by Alexis Polti, described at
+http://perso.enst.fr/~polti/realisations/shix20/
+
+Ultimately, qemu will be coupled with a system C or a verilog
+simulator to simulate the whole board functionalities.
+
+A sh4 user-mode has also somewhat started but will be worked on
+afterwards. The goal is to automate tests for GNAT (GNU Ada) compiler
+that I ported recently to the sh4-linux target.
+
+Registers
+---------
+
+16 general purpose registers are available at any time. The first 8
+registers are banked and the non-directly visible ones can be accessed
+by privileged instructions. In qemu, we define 24 general purpose
+registers and the code generation use either [0-7]+[8-15] or
+[16-23]+[8-15] depending on the MD and RB flags in the sr
+configuration register.
+
+Instructions
+------------
+
+Most sh4 instructions have been implemented. The missing ones at this
+time are:
+ - FPU related instructions
+ - LDTLB to load a new MMU entry
+ - SLEEP to put the processor in sleep mode
+
+Most instructions could be optimized a lot. This will be worked on
+after the current model is fully functional unless debugging
+convenience requires that it is done early.
+
+Many instructions did not have a chance to be tested yet. The plan is
+to implement unit and regression testing of those in the future.
+
+MMU
+---
+
+The MMU is implemented in the sh4 core. MMU management has not been
+tested at all yet. In the sh7750, it can be manipulated through memory
+mapped registers and this part has not yet been implemented.
+
+Exceptions
+----------
+
+Exceptions are implemented as described in the sh4 reference manual
+but have not been tested yet. They do not use qemu EXCP_ features
+yet.
+
+IRQ
+---
+
+IRQ are not implemented yet.
+
+Peripheral features
+-------------------
+
+ + Serial ports
+
+Configuration and use of the first serial port (SCI) without
+interrupts is supported. Input has not yet been tested.
+
+Configuration of the second serial port (SCIF) is supported. FIFO
+handling infrastructure has been started but is not completed yet.
+
+ + GPIO ports
+
+GPIO ports have been implemented. A registration function allows
+external modules to register interest in some port changes (see
+hw/tc58128.[ch] for an example) and will be called back. Interrupt
+generation is not yet supported but some infrastructure is in place
+for this purpose. Note that in the current model a peripheral module
+cannot directly simulate a H->L->H input port transition and have an
+interrupt generated on the low level.
+
+ + TC58128 NAND flash
+
+TC58128 NAND flash is partially implemented through GPIO ports. It
+supports reading from flash.
+
+GDB
+---
+
+GDB remote target support has been implemented and lightly tested.
+
+Files
+-----
+
+File names are hardcoded at this time. The bootloader must be stored in
+shix_bios.bin in the current directory. The initial Linux image must
+be stored in shix_linux_nand.bin in the current directory in NAND
+format. Test files can be obtained from
+http://perso.enst.fr/~polti/robot/ as well as the various datasheets I
+use.
+
+qemu disk parameter on the command line is unused. You can supply any
+existing image and it will be ignored. As the goal is to simulate an
+embedded target, it is not clear how this parameter will be handled in
+the future.
+
+To build an ELF kernel image from the NAND image, 16 bytes have to be
+stripped off the end of every 528 bytes, keeping only 512 of them. The
+following Python code snippet does it:
+
+#! /usr/bin/python
+
+def denand (infd, outfd):
+ while True:
+ d = infd.read (528)
+ if not d: return
+ outfd.write (d[:512])
+
+if __name__ == '__main__':
+ import sys
+ denand (open (sys.argv[1], 'rb'),
+ open (sys.argv[2], 'wb'))
+
+Style isssues
+-------------
+
+There is currently a mix between my style (space before opening
+parenthesis) and qemu style. This will be resolved before final
+integration is proposed.
diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h
new file mode 100644
index 0000000..789d188
--- /dev/null
+++ b/target-sh4/cpu.h
@@ -0,0 +1,364 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _CPU_SH4_H
+#define _CPU_SH4_H
+
+#include "config.h"
+#include "qemu-common.h"
+
+#define TARGET_LONG_BITS 32
+#define TARGET_HAS_ICE 1
+
+#define ELF_MACHINE EM_SH
+
+/* CPU Subtypes */
+#define SH_CPU_SH7750 (1 << 0)
+#define SH_CPU_SH7750S (1 << 1)
+#define SH_CPU_SH7750R (1 << 2)
+#define SH_CPU_SH7751 (1 << 3)
+#define SH_CPU_SH7751R (1 << 4)
+#define SH_CPU_SH7785 (1 << 5)
+#define SH_CPU_SH7750_ALL (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7750R)
+#define SH_CPU_SH7751_ALL (SH_CPU_SH7751 | SH_CPU_SH7751R)
+
+#define CPUState struct CPUSH4State
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_PAGE_BITS 12 /* 4k XXXXX */
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define SR_MD (1 << 30)
+#define SR_RB (1 << 29)
+#define SR_BL (1 << 28)
+#define SR_FD (1 << 15)
+#define SR_M (1 << 9)
+#define SR_Q (1 << 8)
+#define SR_I3 (1 << 7)
+#define SR_I2 (1 << 6)
+#define SR_I1 (1 << 5)
+#define SR_I0 (1 << 4)
+#define SR_S (1 << 1)
+#define SR_T (1 << 0)
+
+#define FPSCR_MASK (0x003fffff)
+#define FPSCR_FR (1 << 21)
+#define FPSCR_SZ (1 << 20)
+#define FPSCR_PR (1 << 19)
+#define FPSCR_DN (1 << 18)
+#define FPSCR_CAUSE_MASK (0x3f << 12)
+#define FPSCR_CAUSE_SHIFT (12)
+#define FPSCR_CAUSE_E (1 << 17)
+#define FPSCR_CAUSE_V (1 << 16)
+#define FPSCR_CAUSE_Z (1 << 15)
+#define FPSCR_CAUSE_O (1 << 14)
+#define FPSCR_CAUSE_U (1 << 13)
+#define FPSCR_CAUSE_I (1 << 12)
+#define FPSCR_ENABLE_MASK (0x1f << 7)
+#define FPSCR_ENABLE_SHIFT (7)
+#define FPSCR_ENABLE_V (1 << 11)
+#define FPSCR_ENABLE_Z (1 << 10)
+#define FPSCR_ENABLE_O (1 << 9)
+#define FPSCR_ENABLE_U (1 << 8)
+#define FPSCR_ENABLE_I (1 << 7)
+#define FPSCR_FLAG_MASK (0x1f << 2)
+#define FPSCR_FLAG_SHIFT (2)
+#define FPSCR_FLAG_V (1 << 6)
+#define FPSCR_FLAG_Z (1 << 5)
+#define FPSCR_FLAG_O (1 << 4)
+#define FPSCR_FLAG_U (1 << 3)
+#define FPSCR_FLAG_I (1 << 2)
+#define FPSCR_RM_MASK (0x03 << 0)
+#define FPSCR_RM_NEAREST (0 << 0)
+#define FPSCR_RM_ZERO (1 << 0)
+
+#define DELAY_SLOT (1 << 0)
+#define DELAY_SLOT_CONDITIONAL (1 << 1)
+#define DELAY_SLOT_TRUE (1 << 2)
+#define DELAY_SLOT_CLEARME (1 << 3)
+/* The dynamic value of the DELAY_SLOT_TRUE flag determines whether the jump
+ * after the delay slot should be taken or not. It is calculated from SR_T.
+ *
+ * It is unclear if it is permitted to modify the SR_T flag in a delay slot.
+ * The use of DELAY_SLOT_TRUE flag makes us accept such SR_T modification.
+ */
+
+typedef struct tlb_t {
+ uint32_t vpn; /* virtual page number */
+ uint32_t ppn; /* physical page number */
+ uint32_t size; /* mapped page size in bytes */
+ uint8_t asid; /* address space identifier */
+ uint8_t v:1; /* validity */
+ uint8_t sz:2; /* page size */
+ uint8_t sh:1; /* share status */
+ uint8_t c:1; /* cacheability */
+ uint8_t pr:2; /* protection key */
+ uint8_t d:1; /* dirty */
+ uint8_t wt:1; /* write through */
+ uint8_t sa:3; /* space attribute (PCMCIA) */
+ uint8_t tc:1; /* timing control */
+} tlb_t;
+
+#define UTLB_SIZE 64
+#define ITLB_SIZE 4
+
+#define NB_MMU_MODES 2
+
+enum sh_features {
+ SH_FEATURE_SH4A = 1,
+ SH_FEATURE_BCR3_AND_BCR4 = 2,
+};
+
+typedef struct memory_content {
+ uint32_t address;
+ uint32_t value;
+ struct memory_content *next;
+} memory_content;
+
+typedef struct CPUSH4State {
+ uint32_t flags; /* general execution flags */
+ uint32_t gregs[24]; /* general registers */
+ float32 fregs[32]; /* floating point registers */
+ uint32_t sr; /* status register */
+ uint32_t ssr; /* saved status register */
+ uint32_t spc; /* saved program counter */
+ uint32_t gbr; /* global base register */
+ uint32_t vbr; /* vector base register */
+ uint32_t sgr; /* saved global register 15 */
+ uint32_t dbr; /* debug base register */
+ uint32_t pc; /* program counter */
+ uint32_t delayed_pc; /* target of delayed jump */
+ uint32_t mach; /* multiply and accumulate high */
+ uint32_t macl; /* multiply and accumulate low */
+ uint32_t pr; /* procedure register */
+ uint32_t fpscr; /* floating point status/control register */
+ uint32_t fpul; /* floating point communication register */
+
+ /* float point status register */
+ float_status fp_status;
+
+ /* The features that we should emulate. See sh_features above. */
+ uint32_t features;
+
+ /* Those belong to the specific unit (SH7750) but are handled here */
+ uint32_t mmucr; /* MMU control register */
+ uint32_t pteh; /* page table entry high register */
+ uint32_t ptel; /* page table entry low register */
+ uint32_t ptea; /* page table entry assistance register */
+ uint32_t ttb; /* tranlation table base register */
+ uint32_t tea; /* TLB exception address register */
+ uint32_t tra; /* TRAPA exception register */
+ uint32_t expevt; /* exception event register */
+ uint32_t intevt; /* interrupt event register */
+
+ tlb_t itlb[ITLB_SIZE]; /* instruction translation table */
+ tlb_t utlb[UTLB_SIZE]; /* unified translation table */
+
+ uint32_t ldst;
+
+ CPU_COMMON
+
+ int id; /* CPU model */
+ uint32_t pvr; /* Processor Version Register */
+ uint32_t prr; /* Processor Revision Register */
+ uint32_t cvr; /* Cache Version Register */
+
+ void *intc_handle;
+ int intr_at_halt; /* SR_BL ignored during sleep */
+ memory_content *movcal_backup;
+ memory_content **movcal_backup_tail;
+} CPUSH4State;
+
+CPUSH4State *cpu_sh4_init(const char *cpu_model);
+int cpu_sh4_exec(CPUSH4State * s);
+int cpu_sh4_signal_handler(int host_signum, void *pinfo,
+ void *puc);
+int cpu_sh4_handle_mmu_fault(CPUSH4State * env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_sh4_handle_mmu_fault
+void do_interrupt(CPUSH4State * env);
+
+void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+#if !defined(CONFIG_USER_ONLY)
+void cpu_sh4_invalidate_tlb(CPUSH4State *s);
+uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s,
+ target_phys_addr_t addr);
+void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value);
+uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s,
+ target_phys_addr_t addr);
+void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value);
+uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s,
+ target_phys_addr_t addr);
+void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value);
+uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s,
+ target_phys_addr_t addr);
+void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value);
+#endif
+
+int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr);
+
+static inline void cpu_set_tls(CPUSH4State *env, target_ulong newtls)
+{
+ env->gbr = newtls;
+}
+
+void cpu_load_tlb(CPUSH4State * env);
+
+#include "softfloat.h"
+
+#define cpu_init cpu_sh4_init
+#define cpu_exec cpu_sh4_exec
+#define cpu_gen_code cpu_sh4_gen_code
+#define cpu_signal_handler cpu_sh4_signal_handler
+#define cpu_list sh4_cpu_list
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+ return (env->sr & SR_MD) == 0 ? 1 : 0;
+}
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->gregs[15] = newsp;
+ env->gregs[0] = 0;
+}
+#endif
+
+#include "cpu-all.h"
+
+/* Memory access type */
+enum {
+ /* Privilege */
+ ACCESS_PRIV = 0x01,
+ /* Direction */
+ ACCESS_WRITE = 0x02,
+ /* Type of instruction */
+ ACCESS_CODE = 0x10,
+ ACCESS_INT = 0x20
+};
+
+/* MMU control register */
+#define MMUCR 0x1F000010
+#define MMUCR_AT (1<<0)
+#define MMUCR_TI (1<<2)
+#define MMUCR_SV (1<<8)
+#define MMUCR_URC_BITS (6)
+#define MMUCR_URC_OFFSET (10)
+#define MMUCR_URC_SIZE (1 << MMUCR_URC_BITS)
+#define MMUCR_URC_MASK (((MMUCR_URC_SIZE) - 1) << MMUCR_URC_OFFSET)
+static inline int cpu_mmucr_urc (uint32_t mmucr)
+{
+ return ((mmucr & MMUCR_URC_MASK) >> MMUCR_URC_OFFSET);
+}
+
+/* PTEH : Page Translation Entry High register */
+#define PTEH_ASID_BITS (8)
+#define PTEH_ASID_SIZE (1 << PTEH_ASID_BITS)
+#define PTEH_ASID_MASK (PTEH_ASID_SIZE - 1)
+#define cpu_pteh_asid(pteh) ((pteh) & PTEH_ASID_MASK)
+#define PTEH_VPN_BITS (22)
+#define PTEH_VPN_OFFSET (10)
+#define PTEH_VPN_SIZE (1 << PTEH_VPN_BITS)
+#define PTEH_VPN_MASK (((PTEH_VPN_SIZE) - 1) << PTEH_VPN_OFFSET)
+static inline int cpu_pteh_vpn (uint32_t pteh)
+{
+ return ((pteh & PTEH_VPN_MASK) >> PTEH_VPN_OFFSET);
+}
+
+/* PTEL : Page Translation Entry Low register */
+#define PTEL_V (1 << 8)
+#define cpu_ptel_v(ptel) (((ptel) & PTEL_V) >> 8)
+#define PTEL_C (1 << 3)
+#define cpu_ptel_c(ptel) (((ptel) & PTEL_C) >> 3)
+#define PTEL_D (1 << 2)
+#define cpu_ptel_d(ptel) (((ptel) & PTEL_D) >> 2)
+#define PTEL_SH (1 << 1)
+#define cpu_ptel_sh(ptel)(((ptel) & PTEL_SH) >> 1)
+#define PTEL_WT (1 << 0)
+#define cpu_ptel_wt(ptel) ((ptel) & PTEL_WT)
+
+#define PTEL_SZ_HIGH_OFFSET (7)
+#define PTEL_SZ_HIGH (1 << PTEL_SZ_HIGH_OFFSET)
+#define PTEL_SZ_LOW_OFFSET (4)
+#define PTEL_SZ_LOW (1 << PTEL_SZ_LOW_OFFSET)
+static inline int cpu_ptel_sz (uint32_t ptel)
+{
+ int sz;
+ sz = (ptel & PTEL_SZ_HIGH) >> PTEL_SZ_HIGH_OFFSET;
+ sz <<= 1;
+ sz |= (ptel & PTEL_SZ_LOW) >> PTEL_SZ_LOW_OFFSET;
+ return sz;
+}
+
+#define PTEL_PPN_BITS (19)
+#define PTEL_PPN_OFFSET (10)
+#define PTEL_PPN_SIZE (1 << PTEL_PPN_BITS)
+#define PTEL_PPN_MASK (((PTEL_PPN_SIZE) - 1) << PTEL_PPN_OFFSET)
+static inline int cpu_ptel_ppn (uint32_t ptel)
+{
+ return ((ptel & PTEL_PPN_MASK) >> PTEL_PPN_OFFSET);
+}
+
+#define PTEL_PR_BITS (2)
+#define PTEL_PR_OFFSET (5)
+#define PTEL_PR_SIZE (1 << PTEL_PR_BITS)
+#define PTEL_PR_MASK (((PTEL_PR_SIZE) - 1) << PTEL_PR_OFFSET)
+static inline int cpu_ptel_pr (uint32_t ptel)
+{
+ return ((ptel & PTEL_PR_MASK) >> PTEL_PR_OFFSET);
+}
+
+/* PTEA : Page Translation Entry Assistance register */
+#define PTEA_SA_BITS (3)
+#define PTEA_SA_SIZE (1 << PTEA_SA_BITS)
+#define PTEA_SA_MASK (PTEA_SA_SIZE - 1)
+#define cpu_ptea_sa(ptea) ((ptea) & PTEA_SA_MASK)
+#define PTEA_TC (1 << 3)
+#define cpu_ptea_tc(ptea) (((ptea) & PTEA_TC) >> 3)
+
+#define TB_FLAG_PENDING_MOVCA (1 << 4)
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->pc;
+ *cs_base = 0;
+ *flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL
+ | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME)) /* Bits 0- 3 */
+ | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */
+ | (env->sr & (SR_MD | SR_RB)) /* Bits 29-30 */
+ | (env->sr & SR_FD) /* Bit 15 */
+ | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */
+}
+
+#endif /* _CPU_SH4_H */
diff --git a/target-sh4/exec.h b/target-sh4/exec.h
new file mode 100644
index 0000000..2999c02
--- /dev/null
+++ b/target-sh4/exec.h
@@ -0,0 +1,56 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _EXEC_SH4_H
+#define _EXEC_SH4_H
+
+#include "config.h"
+#include "dyngen-exec.h"
+
+register struct CPUSH4State *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return (env->interrupt_request & CPU_INTERRUPT_HARD);
+}
+
+static inline int cpu_halted(CPUState *env) {
+ if (!env->halted)
+ return 0;
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ env->intr_at_halt = 1;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+#ifndef CONFIG_USER_ONLY
+#include "softmmu_exec.h"
+#endif
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->pc = tb->pc;
+ env->flags = tb->flags;
+}
+
+#endif /* _EXEC_SH4_H */
diff --git a/target-sh4/helper.c b/target-sh4/helper.c
new file mode 100644
index 0000000..d2038bd
--- /dev/null
+++ b/target-sh4/helper.c
@@ -0,0 +1,843 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "hw/sh_intc.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+
+int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ env->tea = address;
+ env->exception_index = -1;
+ switch (rw) {
+ case 0:
+ env->exception_index = 0x0a0;
+ break;
+ case 1:
+ env->exception_index = 0x0c0;
+ break;
+ case 2:
+ env->exception_index = 0x0a0;
+ break;
+ }
+ return 1;
+}
+
+int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
+{
+ /* For user mode, only U0 area is cachable. */
+ return !(addr & 0x80000000);
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+#define MMU_OK 0
+#define MMU_ITLB_MISS (-1)
+#define MMU_ITLB_MULTIPLE (-2)
+#define MMU_ITLB_VIOLATION (-3)
+#define MMU_DTLB_MISS_READ (-4)
+#define MMU_DTLB_MISS_WRITE (-5)
+#define MMU_DTLB_INITIAL_WRITE (-6)
+#define MMU_DTLB_VIOLATION_READ (-7)
+#define MMU_DTLB_VIOLATION_WRITE (-8)
+#define MMU_DTLB_MULTIPLE (-9)
+#define MMU_DTLB_MISS (-10)
+#define MMU_IADDR_ERROR (-11)
+#define MMU_DADDR_ERROR_READ (-12)
+#define MMU_DADDR_ERROR_WRITE (-13)
+
+void do_interrupt(CPUState * env)
+{
+ int do_irq = env->interrupt_request & CPU_INTERRUPT_HARD;
+ int do_exp, irq_vector = env->exception_index;
+
+ /* prioritize exceptions over interrupts */
+
+ do_exp = env->exception_index != -1;
+ do_irq = do_irq && (env->exception_index == -1);
+
+ if (env->sr & SR_BL) {
+ if (do_exp && env->exception_index != 0x1e0) {
+ env->exception_index = 0x000; /* masked exception -> reset */
+ }
+ if (do_irq && !env->intr_at_halt) {
+ return; /* masked */
+ }
+ env->intr_at_halt = 0;
+ }
+
+ if (do_irq) {
+ irq_vector = sh_intc_get_pending_vector(env->intc_handle,
+ (env->sr >> 4) & 0xf);
+ if (irq_vector == -1) {
+ return; /* masked */
+ }
+ }
+
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ const char *expname;
+ switch (env->exception_index) {
+ case 0x0e0:
+ expname = "addr_error";
+ break;
+ case 0x040:
+ expname = "tlb_miss";
+ break;
+ case 0x0a0:
+ expname = "tlb_violation";
+ break;
+ case 0x180:
+ expname = "illegal_instruction";
+ break;
+ case 0x1a0:
+ expname = "slot_illegal_instruction";
+ break;
+ case 0x800:
+ expname = "fpu_disable";
+ break;
+ case 0x820:
+ expname = "slot_fpu";
+ break;
+ case 0x100:
+ expname = "data_write";
+ break;
+ case 0x060:
+ expname = "dtlb_miss_write";
+ break;
+ case 0x0c0:
+ expname = "dtlb_violation_write";
+ break;
+ case 0x120:
+ expname = "fpu_exception";
+ break;
+ case 0x080:
+ expname = "initial_page_write";
+ break;
+ case 0x160:
+ expname = "trapa";
+ break;
+ default:
+ expname = do_irq ? "interrupt" : "???";
+ break;
+ }
+ qemu_log("exception 0x%03x [%s] raised\n",
+ irq_vector, expname);
+ log_cpu_state(env, 0);
+ }
+
+ env->ssr = env->sr;
+ env->spc = env->pc;
+ env->sgr = env->gregs[15];
+ env->sr |= SR_BL | SR_MD | SR_RB;
+
+ if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
+ /* Branch instruction should be executed again before delay slot. */
+ env->spc -= 2;
+ /* Clear flags for exception/interrupt routine. */
+ env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL | DELAY_SLOT_TRUE);
+ }
+ if (env->flags & DELAY_SLOT_CLEARME)
+ env->flags = 0;
+
+ if (do_exp) {
+ env->expevt = env->exception_index;
+ switch (env->exception_index) {
+ case 0x000:
+ case 0x020:
+ case 0x140:
+ env->sr &= ~SR_FD;
+ env->sr |= 0xf << 4; /* IMASK */
+ env->pc = 0xa0000000;
+ break;
+ case 0x040:
+ case 0x060:
+ env->pc = env->vbr + 0x400;
+ break;
+ case 0x160:
+ env->spc += 2; /* special case for TRAPA */
+ /* fall through */
+ default:
+ env->pc = env->vbr + 0x100;
+ break;
+ }
+ return;
+ }
+
+ if (do_irq) {
+ env->intevt = irq_vector;
+ env->pc = env->vbr + 0x600;
+ return;
+ }
+}
+
+static void update_itlb_use(CPUState * env, int itlbnb)
+{
+ uint8_t or_mask = 0, and_mask = (uint8_t) - 1;
+
+ switch (itlbnb) {
+ case 0:
+ and_mask = 0x1f;
+ break;
+ case 1:
+ and_mask = 0xe7;
+ or_mask = 0x80;
+ break;
+ case 2:
+ and_mask = 0xfb;
+ or_mask = 0x50;
+ break;
+ case 3:
+ or_mask = 0x2c;
+ break;
+ }
+
+ env->mmucr &= (and_mask << 24) | 0x00ffffff;
+ env->mmucr |= (or_mask << 24);
+}
+
+static int itlb_replacement(CPUState * env)
+{
+ if ((env->mmucr & 0xe0000000) == 0xe0000000)
+ return 0;
+ if ((env->mmucr & 0x98000000) == 0x18000000)
+ return 1;
+ if ((env->mmucr & 0x54000000) == 0x04000000)
+ return 2;
+ if ((env->mmucr & 0x2c000000) == 0x00000000)
+ return 3;
+ cpu_abort(env, "Unhandled itlb_replacement");
+}
+
+/* Find the corresponding entry in the right TLB
+ Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE
+*/
+static int find_tlb_entry(CPUState * env, target_ulong address,
+ tlb_t * entries, uint8_t nbtlb, int use_asid)
+{
+ int match = MMU_DTLB_MISS;
+ uint32_t start, end;
+ uint8_t asid;
+ int i;
+
+ asid = env->pteh & 0xff;
+
+ for (i = 0; i < nbtlb; i++) {
+ if (!entries[i].v)
+ continue; /* Invalid entry */
+ if (!entries[i].sh && use_asid && entries[i].asid != asid)
+ continue; /* Bad ASID */
+ start = (entries[i].vpn << 10) & ~(entries[i].size - 1);
+ end = start + entries[i].size - 1;
+ if (address >= start && address <= end) { /* Match */
+ if (match != MMU_DTLB_MISS)
+ return MMU_DTLB_MULTIPLE; /* Multiple match */
+ match = i;
+ }
+ }
+ return match;
+}
+
+static void increment_urc(CPUState * env)
+{
+ uint8_t urb, urc;
+
+ /* Increment URC */
+ urb = ((env->mmucr) >> 18) & 0x3f;
+ urc = ((env->mmucr) >> 10) & 0x3f;
+ urc++;
+ if ((urb > 0 && urc > urb) || urc > (UTLB_SIZE - 1))
+ urc = 0;
+ env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10);
+}
+
+/* Copy and utlb entry into itlb
+ Return entry
+*/
+static int copy_utlb_entry_itlb(CPUState *env, int utlb)
+{
+ int itlb;
+
+ tlb_t * ientry;
+ itlb = itlb_replacement(env);
+ ientry = &env->itlb[itlb];
+ if (ientry->v) {
+ tlb_flush_page(env, ientry->vpn << 10);
+ }
+ *ientry = env->utlb[utlb];
+ update_itlb_use(env, itlb);
+ return itlb;
+}
+
+/* Find itlb entry
+ Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE
+*/
+static int find_itlb_entry(CPUState * env, target_ulong address,
+ int use_asid)
+{
+ int e;
+
+ e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid);
+ if (e == MMU_DTLB_MULTIPLE) {
+ e = MMU_ITLB_MULTIPLE;
+ } else if (e == MMU_DTLB_MISS) {
+ e = MMU_ITLB_MISS;
+ } else if (e >= 0) {
+ update_itlb_use(env, e);
+ }
+ return e;
+}
+
+/* Find utlb entry
+ Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */
+static int find_utlb_entry(CPUState * env, target_ulong address, int use_asid)
+{
+ /* per utlb access */
+ increment_urc(env);
+
+ /* Return entry */
+ return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
+}
+
+/* Match address against MMU
+ Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE,
+ MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ,
+ MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS,
+ MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION,
+ MMU_IADDR_ERROR, MMU_DADDR_ERROR_READ, MMU_DADDR_ERROR_WRITE.
+*/
+static int get_mmu_address(CPUState * env, target_ulong * physical,
+ int *prot, target_ulong address,
+ int rw, int access_type)
+{
+ int use_asid, n;
+ tlb_t *matching = NULL;
+
+ use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0;
+
+ if (rw == 2) {
+ n = find_itlb_entry(env, address, use_asid);
+ if (n >= 0) {
+ matching = &env->itlb[n];
+ if (!(env->sr & SR_MD) && !(matching->pr & 2))
+ n = MMU_ITLB_VIOLATION;
+ else
+ *prot = PAGE_EXEC;
+ } else {
+ n = find_utlb_entry(env, address, use_asid);
+ if (n >= 0) {
+ n = copy_utlb_entry_itlb(env, n);
+ matching = &env->itlb[n];
+ if (!(env->sr & SR_MD) && !(matching->pr & 2)) {
+ n = MMU_ITLB_VIOLATION;
+ } else {
+ *prot = PAGE_READ | PAGE_EXEC;
+ if ((matching->pr & 1) && matching->d) {
+ *prot |= PAGE_WRITE;
+ }
+ }
+ } else if (n == MMU_DTLB_MULTIPLE) {
+ n = MMU_ITLB_MULTIPLE;
+ } else if (n == MMU_DTLB_MISS) {
+ n = MMU_ITLB_MISS;
+ }
+ }
+ } else {
+ n = find_utlb_entry(env, address, use_asid);
+ if (n >= 0) {
+ matching = &env->utlb[n];
+ if (!(env->sr & SR_MD) && !(matching->pr & 2)) {
+ n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE :
+ MMU_DTLB_VIOLATION_READ;
+ } else if ((rw == 1) && !(matching->pr & 1)) {
+ n = MMU_DTLB_VIOLATION_WRITE;
+ } else if ((rw == 1) && !matching->d) {
+ n = MMU_DTLB_INITIAL_WRITE;
+ } else {
+ *prot = PAGE_READ;
+ if ((matching->pr & 1) && matching->d) {
+ *prot |= PAGE_WRITE;
+ }
+ }
+ } else if (n == MMU_DTLB_MISS) {
+ n = (rw == 1) ? MMU_DTLB_MISS_WRITE :
+ MMU_DTLB_MISS_READ;
+ }
+ }
+ if (n >= 0) {
+ n = MMU_OK;
+ *physical = ((matching->ppn << 10) & ~(matching->size - 1)) |
+ (address & (matching->size - 1));
+ }
+ return n;
+}
+
+static int get_physical_address(CPUState * env, target_ulong * physical,
+ int *prot, target_ulong address,
+ int rw, int access_type)
+{
+ /* P1, P2 and P4 areas do not use translation */
+ if ((address >= 0x80000000 && address < 0xc0000000) ||
+ address >= 0xe0000000) {
+ if (!(env->sr & SR_MD)
+ && (address < 0xe0000000 || address >= 0xe4000000)) {
+ /* Unauthorized access in user mode (only store queues are available) */
+ fprintf(stderr, "Unauthorized access\n");
+ if (rw == 0)
+ return MMU_DADDR_ERROR_READ;
+ else if (rw == 1)
+ return MMU_DADDR_ERROR_WRITE;
+ else
+ return MMU_IADDR_ERROR;
+ }
+ if (address >= 0x80000000 && address < 0xc0000000) {
+ /* Mask upper 3 bits for P1 and P2 areas */
+ *physical = address & 0x1fffffff;
+ } else {
+ *physical = address;
+ }
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return MMU_OK;
+ }
+
+ /* If MMU is disabled, return the corresponding physical page */
+ if (!(env->mmucr & MMUCR_AT)) {
+ *physical = address & 0x1FFFFFFF;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return MMU_OK;
+ }
+
+ /* We need to resort to the MMU */
+ return get_mmu_address(env, physical, prot, address, rw, access_type);
+}
+
+int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ target_ulong physical;
+ int prot, ret, access_type;
+
+ access_type = ACCESS_INT;
+ ret =
+ get_physical_address(env, &physical, &prot, address, rw,
+ access_type);
+
+ if (ret != MMU_OK) {
+ env->tea = address;
+ if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) {
+ env->pteh = (env->pteh & PTEH_ASID_MASK) |
+ (address & PTEH_VPN_MASK);
+ }
+ switch (ret) {
+ case MMU_ITLB_MISS:
+ case MMU_DTLB_MISS_READ:
+ env->exception_index = 0x040;
+ break;
+ case MMU_DTLB_MULTIPLE:
+ case MMU_ITLB_MULTIPLE:
+ env->exception_index = 0x140;
+ break;
+ case MMU_ITLB_VIOLATION:
+ env->exception_index = 0x0a0;
+ break;
+ case MMU_DTLB_MISS_WRITE:
+ env->exception_index = 0x060;
+ break;
+ case MMU_DTLB_INITIAL_WRITE:
+ env->exception_index = 0x080;
+ break;
+ case MMU_DTLB_VIOLATION_READ:
+ env->exception_index = 0x0a0;
+ break;
+ case MMU_DTLB_VIOLATION_WRITE:
+ env->exception_index = 0x0c0;
+ break;
+ case MMU_IADDR_ERROR:
+ case MMU_DADDR_ERROR_READ:
+ env->exception_index = 0x0e0;
+ break;
+ case MMU_DADDR_ERROR_WRITE:
+ env->exception_index = 0x100;
+ break;
+ default:
+ cpu_abort(env, "Unhandled MMU fault");
+ }
+ return 1;
+ }
+
+ address &= TARGET_PAGE_MASK;
+ physical &= TARGET_PAGE_MASK;
+
+ tlb_set_page(env, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE);
+ return 0;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr)
+{
+ target_ulong physical;
+ int prot;
+
+ get_physical_address(env, &physical, &prot, addr, 0, 0);
+ return physical;
+}
+
+void cpu_load_tlb(CPUSH4State * env)
+{
+ int n = cpu_mmucr_urc(env->mmucr);
+ tlb_t * entry = &env->utlb[n];
+
+ if (entry->v) {
+ /* Overwriting valid entry in utlb. */
+ target_ulong address = entry->vpn << 10;
+ tlb_flush_page(env, address);
+ }
+
+ /* Take values into cpu status from registers. */
+ entry->asid = (uint8_t)cpu_pteh_asid(env->pteh);
+ entry->vpn = cpu_pteh_vpn(env->pteh);
+ entry->v = (uint8_t)cpu_ptel_v(env->ptel);
+ entry->ppn = cpu_ptel_ppn(env->ptel);
+ entry->sz = (uint8_t)cpu_ptel_sz(env->ptel);
+ switch (entry->sz) {
+ case 0: /* 00 */
+ entry->size = 1024; /* 1K */
+ break;
+ case 1: /* 01 */
+ entry->size = 1024 * 4; /* 4K */
+ break;
+ case 2: /* 10 */
+ entry->size = 1024 * 64; /* 64K */
+ break;
+ case 3: /* 11 */
+ entry->size = 1024 * 1024; /* 1M */
+ break;
+ default:
+ cpu_abort(env, "Unhandled load_tlb");
+ break;
+ }
+ entry->sh = (uint8_t)cpu_ptel_sh(env->ptel);
+ entry->c = (uint8_t)cpu_ptel_c(env->ptel);
+ entry->pr = (uint8_t)cpu_ptel_pr(env->ptel);
+ entry->d = (uint8_t)cpu_ptel_d(env->ptel);
+ entry->wt = (uint8_t)cpu_ptel_wt(env->ptel);
+ entry->sa = (uint8_t)cpu_ptea_sa(env->ptea);
+ entry->tc = (uint8_t)cpu_ptea_tc(env->ptea);
+}
+
+ void cpu_sh4_invalidate_tlb(CPUSH4State *s)
+{
+ int i;
+
+ /* UTLB */
+ for (i = 0; i < UTLB_SIZE; i++) {
+ tlb_t * entry = &s->utlb[i];
+ entry->v = 0;
+ }
+ /* ITLB */
+ for (i = 0; i < ITLB_SIZE; i++) {
+ tlb_t * entry = &s->itlb[i];
+ entry->v = 0;
+ }
+
+ tlb_flush(s, 1);
+}
+
+uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s,
+ target_phys_addr_t addr)
+{
+ int index = (addr & 0x00000300) >> 8;
+ tlb_t * entry = &s->itlb[index];
+
+ return (entry->vpn << 10) |
+ (entry->v << 8) |
+ (entry->asid);
+}
+
+void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ uint32_t vpn = (mem_value & 0xfffffc00) >> 10;
+ uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8);
+ uint8_t asid = (uint8_t)(mem_value & 0x000000ff);
+
+ int index = (addr & 0x00000300) >> 8;
+ tlb_t * entry = &s->itlb[index];
+ if (entry->v) {
+ /* Overwriting valid entry in itlb. */
+ target_ulong address = entry->vpn << 10;
+ tlb_flush_page(s, address);
+ }
+ entry->asid = asid;
+ entry->vpn = vpn;
+ entry->v = v;
+}
+
+uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s,
+ target_phys_addr_t addr)
+{
+ int array = (addr & 0x00800000) >> 23;
+ int index = (addr & 0x00000300) >> 8;
+ tlb_t * entry = &s->itlb[index];
+
+ if (array == 0) {
+ /* ITLB Data Array 1 */
+ return (entry->ppn << 10) |
+ (entry->v << 8) |
+ (entry->pr << 5) |
+ ((entry->sz & 1) << 6) |
+ ((entry->sz & 2) << 4) |
+ (entry->c << 3) |
+ (entry->sh << 1);
+ } else {
+ /* ITLB Data Array 2 */
+ return (entry->tc << 1) |
+ (entry->sa);
+ }
+}
+
+void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ int array = (addr & 0x00800000) >> 23;
+ int index = (addr & 0x00000300) >> 8;
+ tlb_t * entry = &s->itlb[index];
+
+ if (array == 0) {
+ /* ITLB Data Array 1 */
+ if (entry->v) {
+ /* Overwriting valid entry in utlb. */
+ target_ulong address = entry->vpn << 10;
+ tlb_flush_page(s, address);
+ }
+ entry->ppn = (mem_value & 0x1ffffc00) >> 10;
+ entry->v = (mem_value & 0x00000100) >> 8;
+ entry->sz = (mem_value & 0x00000080) >> 6 |
+ (mem_value & 0x00000010) >> 4;
+ entry->pr = (mem_value & 0x00000040) >> 5;
+ entry->c = (mem_value & 0x00000008) >> 3;
+ entry->sh = (mem_value & 0x00000002) >> 1;
+ } else {
+ /* ITLB Data Array 2 */
+ entry->tc = (mem_value & 0x00000008) >> 3;
+ entry->sa = (mem_value & 0x00000007);
+ }
+}
+
+uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s,
+ target_phys_addr_t addr)
+{
+ int index = (addr & 0x00003f00) >> 8;
+ tlb_t * entry = &s->utlb[index];
+
+ increment_urc(s); /* per utlb access */
+
+ return (entry->vpn << 10) |
+ (entry->v << 8) |
+ (entry->asid);
+}
+
+void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ int associate = addr & 0x0000080;
+ uint32_t vpn = (mem_value & 0xfffffc00) >> 10;
+ uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9);
+ uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8);
+ uint8_t asid = (uint8_t)(mem_value & 0x000000ff);
+ int use_asid = (s->mmucr & MMUCR_SV) == 0 || (s->sr & SR_MD) == 0;
+
+ if (associate) {
+ int i;
+ tlb_t * utlb_match_entry = NULL;
+ int needs_tlb_flush = 0;
+
+ /* search UTLB */
+ for (i = 0; i < UTLB_SIZE; i++) {
+ tlb_t * entry = &s->utlb[i];
+ if (!entry->v)
+ continue;
+
+ if (entry->vpn == vpn
+ && (!use_asid || entry->asid == asid || entry->sh)) {
+ if (utlb_match_entry) {
+ /* Multiple TLB Exception */
+ s->exception_index = 0x140;
+ s->tea = addr;
+ break;
+ }
+ if (entry->v && !v)
+ needs_tlb_flush = 1;
+ entry->v = v;
+ entry->d = d;
+ utlb_match_entry = entry;
+ }
+ increment_urc(s); /* per utlb access */
+ }
+
+ /* search ITLB */
+ for (i = 0; i < ITLB_SIZE; i++) {
+ tlb_t * entry = &s->itlb[i];
+ if (entry->vpn == vpn
+ && (!use_asid || entry->asid == asid || entry->sh)) {
+ if (entry->v && !v)
+ needs_tlb_flush = 1;
+ if (utlb_match_entry)
+ *entry = *utlb_match_entry;
+ else
+ entry->v = v;
+ break;
+ }
+ }
+
+ if (needs_tlb_flush)
+ tlb_flush_page(s, vpn << 10);
+
+ } else {
+ int index = (addr & 0x00003f00) >> 8;
+ tlb_t * entry = &s->utlb[index];
+ if (entry->v) {
+ /* Overwriting valid entry in utlb. */
+ target_ulong address = entry->vpn << 10;
+ tlb_flush_page(s, address);
+ }
+ entry->asid = asid;
+ entry->vpn = vpn;
+ entry->d = d;
+ entry->v = v;
+ increment_urc(s);
+ }
+}
+
+uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s,
+ target_phys_addr_t addr)
+{
+ int array = (addr & 0x00800000) >> 23;
+ int index = (addr & 0x00003f00) >> 8;
+ tlb_t * entry = &s->utlb[index];
+
+ increment_urc(s); /* per utlb access */
+
+ if (array == 0) {
+ /* ITLB Data Array 1 */
+ return (entry->ppn << 10) |
+ (entry->v << 8) |
+ (entry->pr << 5) |
+ ((entry->sz & 1) << 6) |
+ ((entry->sz & 2) << 4) |
+ (entry->c << 3) |
+ (entry->d << 2) |
+ (entry->sh << 1) |
+ (entry->wt);
+ } else {
+ /* ITLB Data Array 2 */
+ return (entry->tc << 1) |
+ (entry->sa);
+ }
+}
+
+void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, target_phys_addr_t addr,
+ uint32_t mem_value)
+{
+ int array = (addr & 0x00800000) >> 23;
+ int index = (addr & 0x00003f00) >> 8;
+ tlb_t * entry = &s->utlb[index];
+
+ increment_urc(s); /* per utlb access */
+
+ if (array == 0) {
+ /* UTLB Data Array 1 */
+ if (entry->v) {
+ /* Overwriting valid entry in utlb. */
+ target_ulong address = entry->vpn << 10;
+ tlb_flush_page(s, address);
+ }
+ entry->ppn = (mem_value & 0x1ffffc00) >> 10;
+ entry->v = (mem_value & 0x00000100) >> 8;
+ entry->sz = (mem_value & 0x00000080) >> 6 |
+ (mem_value & 0x00000010) >> 4;
+ entry->pr = (mem_value & 0x00000060) >> 5;
+ entry->c = (mem_value & 0x00000008) >> 3;
+ entry->d = (mem_value & 0x00000004) >> 2;
+ entry->sh = (mem_value & 0x00000002) >> 1;
+ entry->wt = (mem_value & 0x00000001);
+ } else {
+ /* UTLB Data Array 2 */
+ entry->tc = (mem_value & 0x00000008) >> 3;
+ entry->sa = (mem_value & 0x00000007);
+ }
+}
+
+int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
+{
+ int n;
+ int use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0;
+
+ /* check area */
+ if (env->sr & SR_MD) {
+ /* For previledged mode, P2 and P4 area is not cachable. */
+ if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr)
+ return 0;
+ } else {
+ /* For user mode, only U0 area is cachable. */
+ if (0x80000000 <= addr)
+ return 0;
+ }
+
+ /*
+ * TODO : Evaluate CCR and check if the cache is on or off.
+ * Now CCR is not in CPUSH4State, but in SH7750State.
+ * When you move the ccr inot CPUSH4State, the code will be
+ * as follows.
+ */
+#if 0
+ /* check if operand cache is enabled or not. */
+ if (!(env->ccr & 1))
+ return 0;
+#endif
+
+ /* if MMU is off, no check for TLB. */
+ if (env->mmucr & MMUCR_AT)
+ return 1;
+
+ /* check TLB */
+ n = find_tlb_entry(env, addr, env->itlb, ITLB_SIZE, use_asid);
+ if (n >= 0)
+ return env->itlb[n].c;
+
+ n = find_tlb_entry(env, addr, env->utlb, UTLB_SIZE, use_asid);
+ if (n >= 0)
+ return env->utlb[n].c;
+
+ return 0;
+}
+
+#endif
diff --git a/target-sh4/helper.h b/target-sh4/helper.h
new file mode 100644
index 0000000..2e52768
--- /dev/null
+++ b/target-sh4/helper.h
@@ -0,0 +1,54 @@
+#include "def-helper.h"
+
+DEF_HELPER_0(ldtlb, void)
+DEF_HELPER_0(raise_illegal_instruction, void)
+DEF_HELPER_0(raise_slot_illegal_instruction, void)
+DEF_HELPER_0(raise_fpu_disable, void)
+DEF_HELPER_0(raise_slot_fpu_disable, void)
+DEF_HELPER_0(debug, void)
+DEF_HELPER_1(sleep, void, i32)
+DEF_HELPER_1(trapa, void, i32)
+
+DEF_HELPER_2(movcal, void, i32, i32)
+DEF_HELPER_0(discard_movcal_backup, void)
+DEF_HELPER_1(ocbi, void, i32)
+
+DEF_HELPER_2(addv, i32, i32, i32)
+DEF_HELPER_2(addc, i32, i32, i32)
+DEF_HELPER_2(subv, i32, i32, i32)
+DEF_HELPER_2(subc, i32, i32, i32)
+DEF_HELPER_2(div1, i32, i32, i32)
+DEF_HELPER_2(macl, void, i32, i32)
+DEF_HELPER_2(macw, void, i32, i32)
+
+DEF_HELPER_1(ld_fpscr, void, i32)
+
+DEF_HELPER_1(fabs_FT, i32, i32)
+DEF_HELPER_1(fabs_DT, i64, i64)
+DEF_HELPER_2(fadd_FT, i32, i32, i32)
+DEF_HELPER_2(fadd_DT, i64, i64, i64)
+DEF_HELPER_1(fcnvsd_FT_DT, i64, i32)
+DEF_HELPER_1(fcnvds_DT_FT, i32, i64)
+
+DEF_HELPER_2(fcmp_eq_FT, void, i32, i32)
+DEF_HELPER_2(fcmp_eq_DT, void, i64, i64)
+DEF_HELPER_2(fcmp_gt_FT, void, i32, i32)
+DEF_HELPER_2(fcmp_gt_DT, void, i64, i64)
+DEF_HELPER_2(fdiv_FT, i32, i32, i32)
+DEF_HELPER_2(fdiv_DT, i64, i64, i64)
+DEF_HELPER_1(float_FT, i32, i32)
+DEF_HELPER_1(float_DT, i64, i32)
+DEF_HELPER_3(fmac_FT, i32, i32, i32, i32)
+DEF_HELPER_2(fmul_FT, i32, i32, i32)
+DEF_HELPER_2(fmul_DT, i64, i64, i64)
+DEF_HELPER_1(fneg_T, i32, i32)
+DEF_HELPER_2(fsub_FT, i32, i32, i32)
+DEF_HELPER_2(fsub_DT, i64, i64, i64)
+DEF_HELPER_1(fsqrt_FT, i32, i32)
+DEF_HELPER_1(fsqrt_DT, i64, i64)
+DEF_HELPER_1(ftrc_FT, i32, i32)
+DEF_HELPER_1(ftrc_DT, i32, i64)
+DEF_HELPER_2(fipr, void, i32, i32)
+DEF_HELPER_1(ftrv, void, i32)
+
+#include "def-helper.h"
diff --git a/target-sh4/machine.c b/target-sh4/machine.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/target-sh4/machine.c
diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c
new file mode 100644
index 0000000..30f9842
--- /dev/null
+++ b/target-sh4/op_helper.c
@@ -0,0 +1,817 @@
+/*
+ * SH4 emulation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+#include "exec.h"
+#include "helper.h"
+
+static void cpu_restore_state_from_retaddr(void *retaddr)
+{
+ TranslationBlock *tb;
+ unsigned long pc;
+
+ if (retaddr) {
+ pc = (unsigned long) retaddr;
+ 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, NULL);
+ }
+ }
+}
+
+#ifndef CONFIG_USER_ONLY
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ CPUState *saved_env;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_sh4_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (ret) {
+ /* now we have a real cpu fault */
+ cpu_restore_state_from_retaddr(retaddr);
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+#endif
+
+void helper_ldtlb(void)
+{
+#ifdef CONFIG_USER_ONLY
+ /* XXXXX */
+ cpu_abort(env, "Unhandled ldtlb");
+#else
+ cpu_load_tlb(env);
+#endif
+}
+
+static inline void raise_exception(int index, void *retaddr)
+{
+ env->exception_index = index;
+ cpu_restore_state_from_retaddr(retaddr);
+ cpu_loop_exit();
+}
+
+void helper_raise_illegal_instruction(void)
+{
+ raise_exception(0x180, GETPC());
+}
+
+void helper_raise_slot_illegal_instruction(void)
+{
+ raise_exception(0x1a0, GETPC());
+}
+
+void helper_raise_fpu_disable(void)
+{
+ raise_exception(0x800, GETPC());
+}
+
+void helper_raise_slot_fpu_disable(void)
+{
+ raise_exception(0x820, GETPC());
+}
+
+void helper_debug(void)
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+void helper_sleep(uint32_t next_pc)
+{
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ env->pc = next_pc;
+ cpu_loop_exit();
+}
+
+void helper_trapa(uint32_t tra)
+{
+ env->tra = tra << 2;
+ raise_exception(0x160, GETPC());
+}
+
+void helper_movcal(uint32_t address, uint32_t value)
+{
+ if (cpu_sh4_is_cached (env, address))
+ {
+ memory_content *r = malloc (sizeof(memory_content));
+ r->address = address;
+ r->value = value;
+ r->next = NULL;
+
+ *(env->movcal_backup_tail) = r;
+ env->movcal_backup_tail = &(r->next);
+ }
+}
+
+void helper_discard_movcal_backup(void)
+{
+ memory_content *current = env->movcal_backup;
+
+ while(current)
+ {
+ memory_content *next = current->next;
+ free (current);
+ env->movcal_backup = current = next;
+ if (current == NULL)
+ env->movcal_backup_tail = &(env->movcal_backup);
+ }
+}
+
+void helper_ocbi(uint32_t address)
+{
+ memory_content **current = &(env->movcal_backup);
+ while (*current)
+ {
+ uint32_t a = (*current)->address;
+ if ((a & ~0x1F) == (address & ~0x1F))
+ {
+ memory_content *next = (*current)->next;
+ stl(a, (*current)->value);
+
+ if (next == NULL)
+ {
+ env->movcal_backup_tail = current;
+ }
+
+ free (*current);
+ *current = next;
+ break;
+ }
+ }
+}
+
+uint32_t helper_addc(uint32_t arg0, uint32_t arg1)
+{
+ uint32_t tmp0, tmp1;
+
+ tmp1 = arg0 + arg1;
+ tmp0 = arg1;
+ arg1 = tmp1 + (env->sr & 1);
+ if (tmp0 > tmp1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ if (tmp1 > arg1)
+ env->sr |= SR_T;
+ return arg1;
+}
+
+uint32_t helper_addv(uint32_t arg0, uint32_t arg1)
+{
+ uint32_t dest, src, ans;
+
+ if ((int32_t) arg1 >= 0)
+ dest = 0;
+ else
+ dest = 1;
+ if ((int32_t) arg0 >= 0)
+ src = 0;
+ else
+ src = 1;
+ src += dest;
+ arg1 += arg0;
+ if ((int32_t) arg1 >= 0)
+ ans = 0;
+ else
+ ans = 1;
+ ans += dest;
+ if (src == 0 || src == 2) {
+ if (ans == 1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ } else
+ env->sr &= ~SR_T;
+ return arg1;
+}
+
+#define T (env->sr & SR_T)
+#define Q (env->sr & SR_Q ? 1 : 0)
+#define M (env->sr & SR_M ? 1 : 0)
+#define SETT env->sr |= SR_T
+#define CLRT env->sr &= ~SR_T
+#define SETQ env->sr |= SR_Q
+#define CLRQ env->sr &= ~SR_Q
+#define SETM env->sr |= SR_M
+#define CLRM env->sr &= ~SR_M
+
+uint32_t helper_div1(uint32_t arg0, uint32_t arg1)
+{
+ uint32_t tmp0, tmp2;
+ uint8_t old_q, tmp1 = 0xff;
+
+ //printf("div1 arg0=0x%08x arg1=0x%08x M=%d Q=%d T=%d\n", arg0, arg1, M, Q, T);
+ old_q = Q;
+ if ((0x80000000 & arg1) != 0)
+ SETQ;
+ else
+ CLRQ;
+ tmp2 = arg0;
+ arg1 <<= 1;
+ arg1 |= T;
+ switch (old_q) {
+ case 0:
+ switch (M) {
+ case 0:
+ tmp0 = arg1;
+ arg1 -= tmp2;
+ tmp1 = arg1 > tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ case 1:
+ tmp0 = arg1;
+ arg1 += tmp2;
+ tmp1 = arg1 < tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ }
+ break;
+ case 1:
+ switch (M) {
+ case 0:
+ tmp0 = arg1;
+ arg1 += tmp2;
+ tmp1 = arg1 < tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ case 1:
+ tmp0 = arg1;
+ arg1 -= tmp2;
+ tmp1 = arg1 > tmp0;
+ switch (Q) {
+ case 0:
+ if (tmp1 == 0)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ case 1:
+ if (tmp1)
+ SETQ;
+ else
+ CLRQ;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ if (Q == M)
+ SETT;
+ else
+ CLRT;
+ //printf("Output: arg1=0x%08x M=%d Q=%d T=%d\n", arg1, M, Q, T);
+ return arg1;
+}
+
+void helper_macl(uint32_t arg0, uint32_t arg1)
+{
+ int64_t res;
+
+ res = ((uint64_t) env->mach << 32) | env->macl;
+ res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1;
+ env->mach = (res >> 32) & 0xffffffff;
+ env->macl = res & 0xffffffff;
+ if (env->sr & SR_S) {
+ if (res < 0)
+ env->mach |= 0xffff0000;
+ else
+ env->mach &= 0x00007fff;
+ }
+}
+
+void helper_macw(uint32_t arg0, uint32_t arg1)
+{
+ int64_t res;
+
+ res = ((uint64_t) env->mach << 32) | env->macl;
+ res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1;
+ env->mach = (res >> 32) & 0xffffffff;
+ env->macl = res & 0xffffffff;
+ if (env->sr & SR_S) {
+ if (res < -0x80000000) {
+ env->mach = 1;
+ env->macl = 0x80000000;
+ } else if (res > 0x000000007fffffff) {
+ env->mach = 1;
+ env->macl = 0x7fffffff;
+ }
+ }
+}
+
+uint32_t helper_subc(uint32_t arg0, uint32_t arg1)
+{
+ uint32_t tmp0, tmp1;
+
+ tmp1 = arg1 - arg0;
+ tmp0 = arg1;
+ arg1 = tmp1 - (env->sr & SR_T);
+ if (tmp0 < tmp1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ if (tmp1 < arg1)
+ env->sr |= SR_T;
+ return arg1;
+}
+
+uint32_t helper_subv(uint32_t arg0, uint32_t arg1)
+{
+ int32_t dest, src, ans;
+
+ if ((int32_t) arg1 >= 0)
+ dest = 0;
+ else
+ dest = 1;
+ if ((int32_t) arg0 >= 0)
+ src = 0;
+ else
+ src = 1;
+ src += dest;
+ arg1 -= arg0;
+ if ((int32_t) arg1 >= 0)
+ ans = 0;
+ else
+ ans = 1;
+ ans += dest;
+ if (src == 1) {
+ if (ans == 1)
+ env->sr |= SR_T;
+ else
+ env->sr &= ~SR_T;
+ } else
+ env->sr &= ~SR_T;
+ return arg1;
+}
+
+static inline void set_t(void)
+{
+ env->sr |= SR_T;
+}
+
+static inline void clr_t(void)
+{
+ env->sr &= ~SR_T;
+}
+
+void helper_ld_fpscr(uint32_t val)
+{
+ env->fpscr = val & FPSCR_MASK;
+ if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) {
+ set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+ } else {
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+ }
+ set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status);
+}
+
+static void update_fpscr(void *retaddr)
+{
+ int xcpt, cause, enable;
+
+ xcpt = get_float_exception_flags(&env->fp_status);
+
+ /* Clear the flag entries */
+ env->fpscr &= ~FPSCR_FLAG_MASK;
+
+ if (unlikely(xcpt)) {
+ if (xcpt & float_flag_invalid) {
+ env->fpscr |= FPSCR_FLAG_V;
+ }
+ if (xcpt & float_flag_divbyzero) {
+ env->fpscr |= FPSCR_FLAG_Z;
+ }
+ if (xcpt & float_flag_overflow) {
+ env->fpscr |= FPSCR_FLAG_O;
+ }
+ if (xcpt & float_flag_underflow) {
+ env->fpscr |= FPSCR_FLAG_U;
+ }
+ if (xcpt & float_flag_inexact) {
+ env->fpscr |= FPSCR_FLAG_I;
+ }
+
+ /* Accumulate in cause entries */
+ env->fpscr |= (env->fpscr & FPSCR_FLAG_MASK)
+ << (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT);
+
+ /* Generate an exception if enabled */
+ cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT;
+ enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT;
+ if (cause & enable) {
+ cpu_restore_state_from_retaddr(retaddr);
+ env->exception_index = 0x120;
+ cpu_loop_exit();
+ }
+ }
+}
+
+uint32_t helper_fabs_FT(uint32_t t0)
+{
+ CPU_FloatU f;
+ f.l = t0;
+ f.f = float32_abs(f.f);
+ return f.l;
+}
+
+uint64_t helper_fabs_DT(uint64_t t0)
+{
+ CPU_DoubleU d;
+ d.ll = t0;
+ d.d = float64_abs(d.d);
+ return d.ll;
+}
+
+uint32_t helper_fadd_FT(uint32_t t0, uint32_t t1)
+{
+ CPU_FloatU f0, f1;
+ f0.l = t0;
+ f1.l = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ f0.f = float32_add(f0.f, f1.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return f0.l;
+}
+
+uint64_t helper_fadd_DT(uint64_t t0, uint64_t t1)
+{
+ CPU_DoubleU d0, d1;
+ d0.ll = t0;
+ d1.ll = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ d0.d = float64_add(d0.d, d1.d, &env->fp_status);
+ update_fpscr(GETPC());
+ return d0.ll;
+}
+
+void helper_fcmp_eq_FT(uint32_t t0, uint32_t t1)
+{
+ CPU_FloatU f0, f1;
+ int relation;
+ f0.l = t0;
+ f1.l = t1;
+
+ set_float_exception_flags(0, &env->fp_status);
+ relation = float32_compare(f0.f, f1.f, &env->fp_status);
+ if (unlikely(relation == float_relation_unordered)) {
+ update_fpscr(GETPC());
+ } else if (relation == float_relation_equal) {
+ set_t();
+ } else {
+ clr_t();
+ }
+}
+
+void helper_fcmp_eq_DT(uint64_t t0, uint64_t t1)
+{
+ CPU_DoubleU d0, d1;
+ int relation;
+ d0.ll = t0;
+ d1.ll = t1;
+
+ set_float_exception_flags(0, &env->fp_status);
+ relation = float64_compare(d0.d, d1.d, &env->fp_status);
+ if (unlikely(relation == float_relation_unordered)) {
+ update_fpscr(GETPC());
+ } else if (relation == float_relation_equal) {
+ set_t();
+ } else {
+ clr_t();
+ }
+}
+
+void helper_fcmp_gt_FT(uint32_t t0, uint32_t t1)
+{
+ CPU_FloatU f0, f1;
+ int relation;
+ f0.l = t0;
+ f1.l = t1;
+
+ set_float_exception_flags(0, &env->fp_status);
+ relation = float32_compare(f0.f, f1.f, &env->fp_status);
+ if (unlikely(relation == float_relation_unordered)) {
+ update_fpscr(GETPC());
+ } else if (relation == float_relation_greater) {
+ set_t();
+ } else {
+ clr_t();
+ }
+}
+
+void helper_fcmp_gt_DT(uint64_t t0, uint64_t t1)
+{
+ CPU_DoubleU d0, d1;
+ int relation;
+ d0.ll = t0;
+ d1.ll = t1;
+
+ set_float_exception_flags(0, &env->fp_status);
+ relation = float64_compare(d0.d, d1.d, &env->fp_status);
+ if (unlikely(relation == float_relation_unordered)) {
+ update_fpscr(GETPC());
+ } else if (relation == float_relation_greater) {
+ set_t();
+ } else {
+ clr_t();
+ }
+}
+
+uint64_t helper_fcnvsd_FT_DT(uint32_t t0)
+{
+ CPU_DoubleU d;
+ CPU_FloatU f;
+ f.l = t0;
+ set_float_exception_flags(0, &env->fp_status);
+ d.d = float32_to_float64(f.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return d.ll;
+}
+
+uint32_t helper_fcnvds_DT_FT(uint64_t t0)
+{
+ CPU_DoubleU d;
+ CPU_FloatU f;
+ d.ll = t0;
+ set_float_exception_flags(0, &env->fp_status);
+ f.f = float64_to_float32(d.d, &env->fp_status);
+ update_fpscr(GETPC());
+ return f.l;
+}
+
+uint32_t helper_fdiv_FT(uint32_t t0, uint32_t t1)
+{
+ CPU_FloatU f0, f1;
+ f0.l = t0;
+ f1.l = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ f0.f = float32_div(f0.f, f1.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return f0.l;
+}
+
+uint64_t helper_fdiv_DT(uint64_t t0, uint64_t t1)
+{
+ CPU_DoubleU d0, d1;
+ d0.ll = t0;
+ d1.ll = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ d0.d = float64_div(d0.d, d1.d, &env->fp_status);
+ update_fpscr(GETPC());
+ return d0.ll;
+}
+
+uint32_t helper_float_FT(uint32_t t0)
+{
+ CPU_FloatU f;
+
+ set_float_exception_flags(0, &env->fp_status);
+ f.f = int32_to_float32(t0, &env->fp_status);
+ update_fpscr(GETPC());
+
+ return f.l;
+}
+
+uint64_t helper_float_DT(uint32_t t0)
+{
+ CPU_DoubleU d;
+ set_float_exception_flags(0, &env->fp_status);
+ d.d = int32_to_float64(t0, &env->fp_status);
+ update_fpscr(GETPC());
+ return d.ll;
+}
+
+uint32_t helper_fmac_FT(uint32_t t0, uint32_t t1, uint32_t t2)
+{
+ CPU_FloatU f0, f1, f2;
+ f0.l = t0;
+ f1.l = t1;
+ f2.l = t2;
+ set_float_exception_flags(0, &env->fp_status);
+ f0.f = float32_mul(f0.f, f1.f, &env->fp_status);
+ f0.f = float32_add(f0.f, f2.f, &env->fp_status);
+ update_fpscr(GETPC());
+
+ return f0.l;
+}
+
+uint32_t helper_fmul_FT(uint32_t t0, uint32_t t1)
+{
+ CPU_FloatU f0, f1;
+ f0.l = t0;
+ f1.l = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ f0.f = float32_mul(f0.f, f1.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return f0.l;
+}
+
+uint64_t helper_fmul_DT(uint64_t t0, uint64_t t1)
+{
+ CPU_DoubleU d0, d1;
+ d0.ll = t0;
+ d1.ll = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ d0.d = float64_mul(d0.d, d1.d, &env->fp_status);
+ update_fpscr(GETPC());
+
+ return d0.ll;
+}
+
+uint32_t helper_fneg_T(uint32_t t0)
+{
+ CPU_FloatU f;
+ f.l = t0;
+ f.f = float32_chs(f.f);
+ return f.l;
+}
+
+uint32_t helper_fsqrt_FT(uint32_t t0)
+{
+ CPU_FloatU f;
+ f.l = t0;
+ set_float_exception_flags(0, &env->fp_status);
+ f.f = float32_sqrt(f.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return f.l;
+}
+
+uint64_t helper_fsqrt_DT(uint64_t t0)
+{
+ CPU_DoubleU d;
+ d.ll = t0;
+ set_float_exception_flags(0, &env->fp_status);
+ d.d = float64_sqrt(d.d, &env->fp_status);
+ update_fpscr(GETPC());
+ return d.ll;
+}
+
+uint32_t helper_fsub_FT(uint32_t t0, uint32_t t1)
+{
+ CPU_FloatU f0, f1;
+ f0.l = t0;
+ f1.l = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ f0.f = float32_sub(f0.f, f1.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return f0.l;
+}
+
+uint64_t helper_fsub_DT(uint64_t t0, uint64_t t1)
+{
+ CPU_DoubleU d0, d1;
+
+ d0.ll = t0;
+ d1.ll = t1;
+ set_float_exception_flags(0, &env->fp_status);
+ d0.d = float64_sub(d0.d, d1.d, &env->fp_status);
+ update_fpscr(GETPC());
+ return d0.ll;
+}
+
+uint32_t helper_ftrc_FT(uint32_t t0)
+{
+ CPU_FloatU f;
+ uint32_t ret;
+ f.l = t0;
+ set_float_exception_flags(0, &env->fp_status);
+ ret = float32_to_int32_round_to_zero(f.f, &env->fp_status);
+ update_fpscr(GETPC());
+ return ret;
+}
+
+uint32_t helper_ftrc_DT(uint64_t t0)
+{
+ CPU_DoubleU d;
+ uint32_t ret;
+ d.ll = t0;
+ set_float_exception_flags(0, &env->fp_status);
+ ret = float64_to_int32_round_to_zero(d.d, &env->fp_status);
+ update_fpscr(GETPC());
+ return ret;
+}
+
+void helper_fipr(uint32_t m, uint32_t n)
+{
+ int bank, i;
+ float32 r, p;
+
+ bank = (env->sr & FPSCR_FR) ? 16 : 0;
+ r = float32_zero;
+ set_float_exception_flags(0, &env->fp_status);
+
+ for (i = 0 ; i < 4 ; i++) {
+ p = float32_mul(env->fregs[bank + m + i],
+ env->fregs[bank + n + i],
+ &env->fp_status);
+ r = float32_add(r, p, &env->fp_status);
+ }
+ update_fpscr(GETPC());
+
+ env->fregs[bank + n + 3] = r;
+}
+
+void helper_ftrv(uint32_t n)
+{
+ int bank_matrix, bank_vector;
+ int i, j;
+ float32 r[4];
+ float32 p;
+
+ bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16;
+ bank_vector = (env->sr & FPSCR_FR) ? 16 : 0;
+ set_float_exception_flags(0, &env->fp_status);
+ for (i = 0 ; i < 4 ; i++) {
+ r[i] = float32_zero;
+ for (j = 0 ; j < 4 ; j++) {
+ p = float32_mul(env->fregs[bank_matrix + 4 * j + i],
+ env->fregs[bank_vector + j],
+ &env->fp_status);
+ r[i] = float32_add(r[i], p, &env->fp_status);
+ }
+ }
+ update_fpscr(GETPC());
+
+ for (i = 0 ; i < 4 ; i++) {
+ env->fregs[bank_vector + i] = r[i];
+ }
+}
diff --git a/target-sh4/translate.c b/target-sh4/translate.c
new file mode 100644
index 0000000..58e9b8f
--- /dev/null
+++ b/target-sh4/translate.c
@@ -0,0 +1,2077 @@
+/*
+ * SH4 translation
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define DEBUG_DISAS
+#define SH4_DEBUG_DISAS
+//#define SH4_SINGLE_STEP
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-common.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong pc;
+ uint32_t sr;
+ uint32_t fpscr;
+ uint16_t opcode;
+ uint32_t flags;
+ int bstate;
+ int memidx;
+ uint32_t delayed_pc;
+ int singlestep_enabled;
+ uint32_t features;
+ int has_movcal;
+} DisasContext;
+
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(ctx) 1
+#else
+#define IS_USER(ctx) (!(ctx->sr & SR_MD))
+#endif
+
+enum {
+ BS_NONE = 0, /* We go out of the TB without reaching a branch or an
+ * exception condition
+ */
+ BS_STOP = 1, /* We want to stop translation for any reason */
+ BS_BRANCH = 2, /* We reached a branch condition */
+ BS_EXCP = 3, /* We reached an exception condition */
+};
+
+/* global register indexes */
+static TCGv_ptr cpu_env;
+static TCGv cpu_gregs[24];
+static TCGv cpu_pc, cpu_sr, cpu_ssr, cpu_spc, cpu_gbr;
+static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl;
+static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst;
+static TCGv cpu_fregs[32];
+
+/* internal register indexes */
+static TCGv cpu_flags, cpu_delayed_pc;
+
+static uint32_t gen_opc_hflags[OPC_BUF_SIZE];
+
+#include "gen-icount.h"
+
+static void sh4_translate_init(void)
+{
+ int i;
+ static int done_init = 0;
+ static const char * const gregnames[24] = {
+ "R0_BANK0", "R1_BANK0", "R2_BANK0", "R3_BANK0",
+ "R4_BANK0", "R5_BANK0", "R6_BANK0", "R7_BANK0",
+ "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
+ "R0_BANK1", "R1_BANK1", "R2_BANK1", "R3_BANK1",
+ "R4_BANK1", "R5_BANK1", "R6_BANK1", "R7_BANK1"
+ };
+ static const char * const fregnames[32] = {
+ "FPR0_BANK0", "FPR1_BANK0", "FPR2_BANK0", "FPR3_BANK0",
+ "FPR4_BANK0", "FPR5_BANK0", "FPR6_BANK0", "FPR7_BANK0",
+ "FPR8_BANK0", "FPR9_BANK0", "FPR10_BANK0", "FPR11_BANK0",
+ "FPR12_BANK0", "FPR13_BANK0", "FPR14_BANK0", "FPR15_BANK0",
+ "FPR0_BANK1", "FPR1_BANK1", "FPR2_BANK1", "FPR3_BANK1",
+ "FPR4_BANK1", "FPR5_BANK1", "FPR6_BANK1", "FPR7_BANK1",
+ "FPR8_BANK1", "FPR9_BANK1", "FPR10_BANK1", "FPR11_BANK1",
+ "FPR12_BANK1", "FPR13_BANK1", "FPR14_BANK1", "FPR15_BANK1",
+ };
+
+ if (done_init)
+ return;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ for (i = 0; i < 24; i++)
+ cpu_gregs[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, gregs[i]),
+ gregnames[i]);
+
+ cpu_pc = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, pc), "PC");
+ cpu_sr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, sr), "SR");
+ cpu_ssr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, ssr), "SSR");
+ cpu_spc = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, spc), "SPC");
+ cpu_gbr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, gbr), "GBR");
+ cpu_vbr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, vbr), "VBR");
+ cpu_sgr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, sgr), "SGR");
+ cpu_dbr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, dbr), "DBR");
+ cpu_mach = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, mach), "MACH");
+ cpu_macl = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, macl), "MACL");
+ cpu_pr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, pr), "PR");
+ cpu_fpscr = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, fpscr), "FPSCR");
+ cpu_fpul = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, fpul), "FPUL");
+
+ cpu_flags = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, flags), "_flags_");
+ cpu_delayed_pc = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, delayed_pc),
+ "_delayed_pc_");
+ cpu_ldst = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, ldst), "_ldst_");
+
+ for (i = 0; i < 32; i++)
+ cpu_fregs[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, fregs[i]),
+ fregnames[i]);
+
+ /* register helpers */
+#define GEN_HELPER 2
+#include "helper.h"
+
+ done_init = 1;
+}
+
+void cpu_dump_state(CPUState * env, FILE * f,
+ int (*cpu_fprintf) (FILE * f, const char *fmt, ...),
+ int flags)
+{
+ int i;
+ cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n",
+ env->pc, env->sr, env->pr, env->fpscr);
+ cpu_fprintf(f, "spc=0x%08x ssr=0x%08x gbr=0x%08x vbr=0x%08x\n",
+ env->spc, env->ssr, env->gbr, env->vbr);
+ cpu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n",
+ env->sgr, env->dbr, env->delayed_pc, env->fpul);
+ for (i = 0; i < 24; i += 4) {
+ cpu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n",
+ i, env->gregs[i], i + 1, env->gregs[i + 1],
+ i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]);
+ }
+ if (env->flags & DELAY_SLOT) {
+ cpu_fprintf(f, "in delay slot (delayed_pc=0x%08x)\n",
+ env->delayed_pc);
+ } else if (env->flags & DELAY_SLOT_CONDITIONAL) {
+ cpu_fprintf(f, "in conditional delay slot (delayed_pc=0x%08x)\n",
+ env->delayed_pc);
+ }
+}
+
+void cpu_reset(CPUSH4State * env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ memset(env, 0, offsetof(CPUSH4State, breakpoints));
+ tlb_flush(env, 1);
+
+ env->pc = 0xA0000000;
+#if defined(CONFIG_USER_ONLY)
+ env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */
+#else
+ env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0;
+ env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */
+ set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+ set_flush_to_zero(1, &env->fp_status);
+#endif
+ set_default_nan_mode(1, &env->fp_status);
+}
+
+typedef struct {
+ const char *name;
+ int id;
+ uint32_t pvr;
+ uint32_t prr;
+ uint32_t cvr;
+ uint32_t features;
+} sh4_def_t;
+
+static sh4_def_t sh4_defs[] = {
+ {
+ .name = "SH7750R",
+ .id = SH_CPU_SH7750R,
+ .pvr = 0x00050000,
+ .prr = 0x00000100,
+ .cvr = 0x00110000,
+ .features = SH_FEATURE_BCR3_AND_BCR4,
+ }, {
+ .name = "SH7751R",
+ .id = SH_CPU_SH7751R,
+ .pvr = 0x04050005,
+ .prr = 0x00000113,
+ .cvr = 0x00110000, /* Neutered caches, should be 0x20480000 */
+ .features = SH_FEATURE_BCR3_AND_BCR4,
+ }, {
+ .name = "SH7785",
+ .id = SH_CPU_SH7785,
+ .pvr = 0x10300700,
+ .prr = 0x00000200,
+ .cvr = 0x71440211,
+ .features = SH_FEATURE_SH4A,
+ },
+};
+
+static const sh4_def_t *cpu_sh4_find_by_name(const char *name)
+{
+ int i;
+
+ if (strcasecmp(name, "any") == 0)
+ return &sh4_defs[0];
+
+ for (i = 0; i < ARRAY_SIZE(sh4_defs); i++)
+ if (strcasecmp(name, sh4_defs[i].name) == 0)
+ return &sh4_defs[i];
+
+ return NULL;
+}
+
+void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sh4_defs); i++)
+ (*cpu_fprintf)(f, "%s\n", sh4_defs[i].name);
+}
+
+static void cpu_register(CPUSH4State *env, const sh4_def_t *def)
+{
+ env->pvr = def->pvr;
+ env->prr = def->prr;
+ env->cvr = def->cvr;
+ env->id = def->id;
+}
+
+CPUSH4State *cpu_sh4_init(const char *cpu_model)
+{
+ CPUSH4State *env;
+ const sh4_def_t *def;
+
+ def = cpu_sh4_find_by_name(cpu_model);
+ if (!def)
+ return NULL;
+ env = qemu_mallocz(sizeof(CPUSH4State));
+ env->features = def->features;
+ cpu_exec_init(env);
+ env->movcal_backup_tail = &(env->movcal_backup);
+ sh4_translate_init();
+ env->cpu_model_str = cpu_model;
+ cpu_reset(env);
+ cpu_register(env, def);
+ qemu_init_vcpu(env);
+ return env;
+}
+
+static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
+ !ctx->singlestep_enabled) {
+ /* Use a direct jump if in same page and singlestep not enabled */
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_i32(cpu_pc, dest);
+ tcg_gen_exit_tb((long) tb + n);
+ } else {
+ tcg_gen_movi_i32(cpu_pc, dest);
+ if (ctx->singlestep_enabled)
+ gen_helper_debug();
+ tcg_gen_exit_tb(0);
+ }
+}
+
+static void gen_jump(DisasContext * ctx)
+{
+ if (ctx->delayed_pc == (uint32_t) - 1) {
+ /* Target is not statically known, it comes necessarily from a
+ delayed jump as immediate jump are conditinal jumps */
+ tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc);
+ if (ctx->singlestep_enabled)
+ gen_helper_debug();
+ tcg_gen_exit_tb(0);
+ } else {
+ gen_goto_tb(ctx, 0, ctx->delayed_pc);
+ }
+}
+
+static inline void gen_branch_slot(uint32_t delayed_pc, int t)
+{
+ TCGv sr;
+ int label = gen_new_label();
+ tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc);
+ sr = tcg_temp_new();
+ tcg_gen_andi_i32(sr, cpu_sr, SR_T);
+ tcg_gen_brcondi_i32(t ? TCG_COND_EQ:TCG_COND_NE, sr, 0, label);
+ tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE);
+ gen_set_label(label);
+}
+
+/* Immediate conditional jump (bt or bf) */
+static void gen_conditional_jump(DisasContext * ctx,
+ target_ulong ift, target_ulong ifnott)
+{
+ int l1;
+ TCGv sr;
+
+ l1 = gen_new_label();
+ sr = tcg_temp_new();
+ tcg_gen_andi_i32(sr, cpu_sr, SR_T);
+ tcg_gen_brcondi_i32(TCG_COND_NE, sr, 0, l1);
+ gen_goto_tb(ctx, 0, ifnott);
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 1, ift);
+}
+
+/* Delayed conditional jump (bt or bf) */
+static void gen_delayed_conditional_jump(DisasContext * ctx)
+{
+ int l1;
+ TCGv ds;
+
+ l1 = gen_new_label();
+ ds = tcg_temp_new();
+ tcg_gen_andi_i32(ds, cpu_flags, DELAY_SLOT_TRUE);
+ tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1);
+ gen_goto_tb(ctx, 1, ctx->pc + 2);
+ gen_set_label(l1);
+ tcg_gen_andi_i32(cpu_flags, cpu_flags, ~DELAY_SLOT_TRUE);
+ gen_jump(ctx);
+}
+
+static inline void gen_set_t(void)
+{
+ tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_T);
+}
+
+static inline void gen_clr_t(void)
+{
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
+}
+
+static inline void gen_cmp(int cond, TCGv t0, TCGv t1)
+{
+ TCGv t;
+
+ t = tcg_temp_new();
+ tcg_gen_setcond_i32(cond, t, t1, t0);
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, t);
+
+ tcg_temp_free(t);
+}
+
+static inline void gen_cmp_imm(int cond, TCGv t0, int32_t imm)
+{
+ TCGv t;
+
+ t = tcg_temp_new();
+ tcg_gen_setcondi_i32(cond, t, t0, imm);
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, t);
+
+ tcg_temp_free(t);
+}
+
+static inline void gen_store_flags(uint32_t flags)
+{
+ tcg_gen_andi_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE);
+ tcg_gen_ori_i32(cpu_flags, cpu_flags, flags);
+}
+
+static inline void gen_copy_bit_i32(TCGv t0, int p0, TCGv t1, int p1)
+{
+ TCGv tmp = tcg_temp_new();
+
+ p0 &= 0x1f;
+ p1 &= 0x1f;
+
+ tcg_gen_andi_i32(tmp, t1, (1 << p1));
+ tcg_gen_andi_i32(t0, t0, ~(1 << p0));
+ if (p0 < p1)
+ tcg_gen_shri_i32(tmp, tmp, p1 - p0);
+ else if (p0 > p1)
+ tcg_gen_shli_i32(tmp, tmp, p0 - p1);
+ tcg_gen_or_i32(t0, t0, tmp);
+
+ tcg_temp_free(tmp);
+}
+
+static inline void gen_load_fpr64(TCGv_i64 t, int reg)
+{
+ tcg_gen_concat_i32_i64(t, cpu_fregs[reg + 1], cpu_fregs[reg]);
+}
+
+static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
+{
+ TCGv_i32 tmp = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(tmp, t);
+ tcg_gen_mov_i32(cpu_fregs[reg + 1], tmp);
+ tcg_gen_shri_i64(t, t, 32);
+ tcg_gen_trunc_i64_i32(tmp, t);
+ tcg_gen_mov_i32(cpu_fregs[reg], tmp);
+ tcg_temp_free_i32(tmp);
+}
+
+#define B3_0 (ctx->opcode & 0xf)
+#define B6_4 ((ctx->opcode >> 4) & 0x7)
+#define B7_4 ((ctx->opcode >> 4) & 0xf)
+#define B7_0 (ctx->opcode & 0xff)
+#define B7_0s ((int32_t) (int8_t) (ctx->opcode & 0xff))
+#define B11_0s (ctx->opcode & 0x800 ? 0xfffff000 | (ctx->opcode & 0xfff) : \
+ (ctx->opcode & 0xfff))
+#define B11_8 ((ctx->opcode >> 8) & 0xf)
+#define B15_12 ((ctx->opcode >> 12) & 0xf)
+
+#define REG(x) ((x) < 8 && (ctx->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB) ? \
+ (cpu_gregs[x + 16]) : (cpu_gregs[x]))
+
+#define ALTREG(x) ((x) < 8 && (ctx->sr & (SR_MD | SR_RB)) != (SR_MD | SR_RB) \
+ ? (cpu_gregs[x + 16]) : (cpu_gregs[x]))
+
+#define FREG(x) (ctx->fpscr & FPSCR_FR ? (x) ^ 0x10 : (x))
+#define XHACK(x) ((((x) & 1 ) << 4) | ((x) & 0xe))
+#define XREG(x) (ctx->fpscr & FPSCR_FR ? XHACK(x) ^ 0x10 : XHACK(x))
+#define DREG(x) FREG(x) /* Assumes lsb of (x) is always 0 */
+
+#define CHECK_NOT_DELAY_SLOT \
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) \
+ { \
+ gen_helper_raise_slot_illegal_instruction(); \
+ ctx->bstate = BS_EXCP; \
+ return; \
+ }
+
+#define CHECK_PRIVILEGED \
+ if (IS_USER(ctx)) { \
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \
+ gen_helper_raise_slot_illegal_instruction(); \
+ } else { \
+ gen_helper_raise_illegal_instruction(); \
+ } \
+ ctx->bstate = BS_EXCP; \
+ return; \
+ }
+
+#define CHECK_FPU_ENABLED \
+ if (ctx->flags & SR_FD) { \
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \
+ gen_helper_raise_slot_fpu_disable(); \
+ } else { \
+ gen_helper_raise_fpu_disable(); \
+ } \
+ ctx->bstate = BS_EXCP; \
+ return; \
+ }
+
+static void _decode_opc(DisasContext * ctx)
+{
+ /* This code tries to make movcal emulation sufficiently
+ accurate for Linux purposes. This instruction writes
+ memory, and prior to that, always allocates a cache line.
+ It is used in two contexts:
+ - in memcpy, where data is copied in blocks, the first write
+ of to a block uses movca.l for performance.
+ - in arch/sh/mm/cache-sh4.c, movcal.l + ocbi combination is used
+ to flush the cache. Here, the data written by movcal.l is never
+ written to memory, and the data written is just bogus.
+
+ To simulate this, we simulate movcal.l, we store the value to memory,
+ but we also remember the previous content. If we see ocbi, we check
+ if movcal.l for that address was done previously. If so, the write should
+ not have hit the memory, so we restore the previous content.
+ When we see an instruction that is neither movca.l
+ nor ocbi, the previous content is discarded.
+
+ To optimize, we only try to flush stores when we're at the start of
+ TB, or if we already saw movca.l in this TB and did not flush stores
+ yet. */
+ if (ctx->has_movcal)
+ {
+ int opcode = ctx->opcode & 0xf0ff;
+ if (opcode != 0x0093 /* ocbi */
+ && opcode != 0x00c3 /* movca.l */)
+ {
+ gen_helper_discard_movcal_backup ();
+ ctx->has_movcal = 0;
+ }
+ }
+
+#if 0
+ fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
+#endif
+
+ switch (ctx->opcode) {
+ case 0x0019: /* div0u */
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(SR_M | SR_Q | SR_T));
+ return;
+ case 0x000b: /* rts */
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_mov_i32(cpu_delayed_pc, cpu_pr);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x0028: /* clrmac */
+ tcg_gen_movi_i32(cpu_mach, 0);
+ tcg_gen_movi_i32(cpu_macl, 0);
+ return;
+ case 0x0048: /* clrs */
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_S);
+ return;
+ case 0x0008: /* clrt */
+ gen_clr_t();
+ return;
+ case 0x0038: /* ldtlb */
+ CHECK_PRIVILEGED
+ gen_helper_ldtlb();
+ return;
+ case 0x002b: /* rte */
+ CHECK_PRIVILEGED
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_mov_i32(cpu_sr, cpu_ssr);
+ tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x0058: /* sets */
+ tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_S);
+ return;
+ case 0x0018: /* sett */
+ gen_set_t();
+ return;
+ case 0xfbfd: /* frchg */
+ tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR);
+ ctx->bstate = BS_STOP;
+ return;
+ case 0xf3fd: /* fschg */
+ tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_SZ);
+ ctx->bstate = BS_STOP;
+ return;
+ case 0x0009: /* nop */
+ return;
+ case 0x001b: /* sleep */
+ CHECK_PRIVILEGED
+ gen_helper_sleep(tcg_const_i32(ctx->pc + 2));
+ return;
+ }
+
+ switch (ctx->opcode & 0xf000) {
+ case 0x1000: /* mov.l Rm,@(disp,Rn) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, REG(B11_8), B3_0 * 4);
+ tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x5000: /* mov.l @(disp,Rm),Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 4);
+ tcg_gen_qemu_ld32s(REG(B11_8), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xe000: /* mov #imm,Rn */
+ tcg_gen_movi_i32(REG(B11_8), B7_0s);
+ return;
+ case 0x9000: /* mov.w @(disp,PC),Rn */
+ {
+ TCGv addr = tcg_const_i32(ctx->pc + 4 + B7_0 * 2);
+ tcg_gen_qemu_ld16s(REG(B11_8), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xd000: /* mov.l @(disp,PC),Rn */
+ {
+ TCGv addr = tcg_const_i32((ctx->pc + 4 + B7_0 * 4) & ~3);
+ tcg_gen_qemu_ld32s(REG(B11_8), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x7000: /* add #imm,Rn */
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), B7_0s);
+ return;
+ case 0xa000: /* bra disp */
+ CHECK_NOT_DELAY_SLOT
+ ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2;
+ tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc);
+ ctx->flags |= DELAY_SLOT;
+ return;
+ case 0xb000: /* bsr disp */
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_movi_i32(cpu_pr, ctx->pc + 4);
+ ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2;
+ tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc);
+ ctx->flags |= DELAY_SLOT;
+ return;
+ }
+
+ switch (ctx->opcode & 0xf00f) {
+ case 0x6003: /* mov Rm,Rn */
+ tcg_gen_mov_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x2000: /* mov.b Rm,@Rn */
+ tcg_gen_qemu_st8(REG(B7_4), REG(B11_8), ctx->memidx);
+ return;
+ case 0x2001: /* mov.w Rm,@Rn */
+ tcg_gen_qemu_st16(REG(B7_4), REG(B11_8), ctx->memidx);
+ return;
+ case 0x2002: /* mov.l Rm,@Rn */
+ tcg_gen_qemu_st32(REG(B7_4), REG(B11_8), ctx->memidx);
+ return;
+ case 0x6000: /* mov.b @Rm,Rn */
+ tcg_gen_qemu_ld8s(REG(B11_8), REG(B7_4), ctx->memidx);
+ return;
+ case 0x6001: /* mov.w @Rm,Rn */
+ tcg_gen_qemu_ld16s(REG(B11_8), REG(B7_4), ctx->memidx);
+ return;
+ case 0x6002: /* mov.l @Rm,Rn */
+ tcg_gen_qemu_ld32s(REG(B11_8), REG(B7_4), ctx->memidx);
+ return;
+ case 0x2004: /* mov.b Rm,@-Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_subi_i32(addr, REG(B11_8), 1);
+ tcg_gen_qemu_st8(REG(B7_4), addr, ctx->memidx); /* might cause re-execution */
+ tcg_gen_mov_i32(REG(B11_8), addr); /* modify register status */
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x2005: /* mov.w Rm,@-Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_subi_i32(addr, REG(B11_8), 2);
+ tcg_gen_qemu_st16(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x2006: /* mov.l Rm,@-Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
+ tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ }
+ return;
+ case 0x6004: /* mov.b @Rm+,Rn */
+ tcg_gen_qemu_ld8s(REG(B11_8), REG(B7_4), ctx->memidx);
+ if ( B11_8 != B7_4 )
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 1);
+ return;
+ case 0x6005: /* mov.w @Rm+,Rn */
+ tcg_gen_qemu_ld16s(REG(B11_8), REG(B7_4), ctx->memidx);
+ if ( B11_8 != B7_4 )
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2);
+ return;
+ case 0x6006: /* mov.l @Rm+,Rn */
+ tcg_gen_qemu_ld32s(REG(B11_8), REG(B7_4), ctx->memidx);
+ if ( B11_8 != B7_4 )
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
+ return;
+ case 0x0004: /* mov.b Rm,@(R0,Rn) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+ tcg_gen_qemu_st8(REG(B7_4), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x0005: /* mov.w Rm,@(R0,Rn) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+ tcg_gen_qemu_st16(REG(B7_4), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x0006: /* mov.l Rm,@(R0,Rn) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+ tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x000c: /* mov.b @(R0,Rm),Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+ tcg_gen_qemu_ld8s(REG(B11_8), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x000d: /* mov.w @(R0,Rm),Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+ tcg_gen_qemu_ld16s(REG(B11_8), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x000e: /* mov.l @(R0,Rm),Rn */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+ tcg_gen_qemu_ld32s(REG(B11_8), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x6008: /* swap.b Rm,Rn */
+ {
+ TCGv high, low;
+ high = tcg_temp_new();
+ tcg_gen_andi_i32(high, REG(B7_4), 0xffff0000);
+ low = tcg_temp_new();
+ tcg_gen_ext16u_i32(low, REG(B7_4));
+ tcg_gen_bswap16_i32(low, low);
+ tcg_gen_or_i32(REG(B11_8), high, low);
+ tcg_temp_free(low);
+ tcg_temp_free(high);
+ }
+ return;
+ case 0x6009: /* swap.w Rm,Rn */
+ {
+ TCGv high, low;
+ high = tcg_temp_new();
+ tcg_gen_shli_i32(high, REG(B7_4), 16);
+ low = tcg_temp_new();
+ tcg_gen_shri_i32(low, REG(B7_4), 16);
+ tcg_gen_ext16u_i32(low, low);
+ tcg_gen_or_i32(REG(B11_8), high, low);
+ tcg_temp_free(low);
+ tcg_temp_free(high);
+ }
+ return;
+ case 0x200d: /* xtrct Rm,Rn */
+ {
+ TCGv high, low;
+ high = tcg_temp_new();
+ tcg_gen_shli_i32(high, REG(B7_4), 16);
+ low = tcg_temp_new();
+ tcg_gen_shri_i32(low, REG(B11_8), 16);
+ tcg_gen_ext16u_i32(low, low);
+ tcg_gen_or_i32(REG(B11_8), high, low);
+ tcg_temp_free(low);
+ tcg_temp_free(high);
+ }
+ return;
+ case 0x300c: /* add Rm,Rn */
+ tcg_gen_add_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+ return;
+ case 0x300e: /* addc Rm,Rn */
+ gen_helper_addc(REG(B11_8), REG(B7_4), REG(B11_8));
+ return;
+ case 0x300f: /* addv Rm,Rn */
+ gen_helper_addv(REG(B11_8), REG(B7_4), REG(B11_8));
+ return;
+ case 0x2009: /* and Rm,Rn */
+ tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+ return;
+ case 0x3000: /* cmp/eq Rm,Rn */
+ gen_cmp(TCG_COND_EQ, REG(B7_4), REG(B11_8));
+ return;
+ case 0x3003: /* cmp/ge Rm,Rn */
+ gen_cmp(TCG_COND_GE, REG(B7_4), REG(B11_8));
+ return;
+ case 0x3007: /* cmp/gt Rm,Rn */
+ gen_cmp(TCG_COND_GT, REG(B7_4), REG(B11_8));
+ return;
+ case 0x3006: /* cmp/hi Rm,Rn */
+ gen_cmp(TCG_COND_GTU, REG(B7_4), REG(B11_8));
+ return;
+ case 0x3002: /* cmp/hs Rm,Rn */
+ gen_cmp(TCG_COND_GEU, REG(B7_4), REG(B11_8));
+ return;
+ case 0x200c: /* cmp/str Rm,Rn */
+ {
+ TCGv cmp1 = tcg_temp_new();
+ TCGv cmp2 = tcg_temp_new();
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
+ tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8));
+ tcg_gen_andi_i32(cmp2, cmp1, 0xff000000);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff);
+ tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2);
+ tcg_temp_free(cmp2);
+ tcg_temp_free(cmp1);
+ }
+ return;
+ case 0x2007: /* div0s Rm,Rn */
+ {
+ gen_copy_bit_i32(cpu_sr, 8, REG(B11_8), 31); /* SR_Q */
+ gen_copy_bit_i32(cpu_sr, 9, REG(B7_4), 31); /* SR_M */
+ TCGv val = tcg_temp_new();
+ tcg_gen_xor_i32(val, REG(B7_4), REG(B11_8));
+ gen_copy_bit_i32(cpu_sr, 0, val, 31); /* SR_T */
+ tcg_temp_free(val);
+ }
+ return;
+ case 0x3004: /* div1 Rm,Rn */
+ gen_helper_div1(REG(B11_8), REG(B7_4), REG(B11_8));
+ return;
+ case 0x300d: /* dmuls.l Rm,Rn */
+ {
+ TCGv_i64 tmp1 = tcg_temp_new_i64();
+ TCGv_i64 tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_ext_i32_i64(tmp1, REG(B7_4));
+ tcg_gen_ext_i32_i64(tmp2, REG(B11_8));
+ tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+ tcg_gen_trunc_i64_i32(cpu_macl, tmp1);
+ tcg_gen_shri_i64(tmp1, tmp1, 32);
+ tcg_gen_trunc_i64_i32(cpu_mach, tmp1);
+
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp1);
+ }
+ return;
+ case 0x3005: /* dmulu.l Rm,Rn */
+ {
+ TCGv_i64 tmp1 = tcg_temp_new_i64();
+ TCGv_i64 tmp2 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(tmp1, REG(B7_4));
+ tcg_gen_extu_i32_i64(tmp2, REG(B11_8));
+ tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+ tcg_gen_trunc_i64_i32(cpu_macl, tmp1);
+ tcg_gen_shri_i64(tmp1, tmp1, 32);
+ tcg_gen_trunc_i64_i32(cpu_mach, tmp1);
+
+ tcg_temp_free_i64(tmp2);
+ tcg_temp_free_i64(tmp1);
+ }
+ return;
+ case 0x600e: /* exts.b Rm,Rn */
+ tcg_gen_ext8s_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x600f: /* exts.w Rm,Rn */
+ tcg_gen_ext16s_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x600c: /* extu.b Rm,Rn */
+ tcg_gen_ext8u_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x600d: /* extu.w Rm,Rn */
+ tcg_gen_ext16u_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x000f: /* mac.l @Rm+,@Rn+ */
+ {
+ TCGv arg0, arg1;
+ arg0 = tcg_temp_new();
+ tcg_gen_qemu_ld32s(arg0, REG(B7_4), ctx->memidx);
+ arg1 = tcg_temp_new();
+ tcg_gen_qemu_ld32s(arg1, REG(B11_8), ctx->memidx);
+ gen_helper_macl(arg0, arg1);
+ tcg_temp_free(arg1);
+ tcg_temp_free(arg0);
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+ }
+ return;
+ case 0x400f: /* mac.w @Rm+,@Rn+ */
+ {
+ TCGv arg0, arg1;
+ arg0 = tcg_temp_new();
+ tcg_gen_qemu_ld32s(arg0, REG(B7_4), ctx->memidx);
+ arg1 = tcg_temp_new();
+ tcg_gen_qemu_ld32s(arg1, REG(B11_8), ctx->memidx);
+ gen_helper_macw(arg0, arg1);
+ tcg_temp_free(arg1);
+ tcg_temp_free(arg0);
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 2);
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2);
+ }
+ return;
+ case 0x0007: /* mul.l Rm,Rn */
+ tcg_gen_mul_i32(cpu_macl, REG(B7_4), REG(B11_8));
+ return;
+ case 0x200f: /* muls.w Rm,Rn */
+ {
+ TCGv arg0, arg1;
+ arg0 = tcg_temp_new();
+ tcg_gen_ext16s_i32(arg0, REG(B7_4));
+ arg1 = tcg_temp_new();
+ tcg_gen_ext16s_i32(arg1, REG(B11_8));
+ tcg_gen_mul_i32(cpu_macl, arg0, arg1);
+ tcg_temp_free(arg1);
+ tcg_temp_free(arg0);
+ }
+ return;
+ case 0x200e: /* mulu.w Rm,Rn */
+ {
+ TCGv arg0, arg1;
+ arg0 = tcg_temp_new();
+ tcg_gen_ext16u_i32(arg0, REG(B7_4));
+ arg1 = tcg_temp_new();
+ tcg_gen_ext16u_i32(arg1, REG(B11_8));
+ tcg_gen_mul_i32(cpu_macl, arg0, arg1);
+ tcg_temp_free(arg1);
+ tcg_temp_free(arg0);
+ }
+ return;
+ case 0x600b: /* neg Rm,Rn */
+ tcg_gen_neg_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x600a: /* negc Rm,Rn */
+ {
+ TCGv t0, t1;
+ t0 = tcg_temp_new();
+ tcg_gen_neg_i32(t0, REG(B7_4));
+ t1 = tcg_temp_new();
+ tcg_gen_andi_i32(t1, cpu_sr, SR_T);
+ tcg_gen_sub_i32(REG(B11_8), t0, t1);
+ tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T);
+ tcg_gen_setcondi_i32(TCG_COND_GTU, t1, t0, 0);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
+ tcg_gen_setcond_i32(TCG_COND_GTU, t1, REG(B11_8), t0);
+ tcg_gen_or_i32(cpu_sr, cpu_sr, t1);
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+ }
+ return;
+ case 0x6007: /* not Rm,Rn */
+ tcg_gen_not_i32(REG(B11_8), REG(B7_4));
+ return;
+ case 0x200b: /* or Rm,Rn */
+ tcg_gen_or_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+ return;
+ case 0x400c: /* shad Rm,Rn */
+ {
+ int label1 = gen_new_label();
+ int label2 = gen_new_label();
+ int label3 = gen_new_label();
+ int label4 = gen_new_label();
+ TCGv shift;
+ tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1);
+ /* Rm positive, shift to the left */
+ shift = tcg_temp_new();
+ tcg_gen_andi_i32(shift, REG(B7_4), 0x1f);
+ tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift);
+ tcg_temp_free(shift);
+ tcg_gen_br(label4);
+ /* Rm negative, shift to the right */
+ gen_set_label(label1);
+ shift = tcg_temp_new();
+ tcg_gen_andi_i32(shift, REG(B7_4), 0x1f);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2);
+ tcg_gen_not_i32(shift, REG(B7_4));
+ tcg_gen_andi_i32(shift, shift, 0x1f);
+ tcg_gen_addi_i32(shift, shift, 1);
+ tcg_gen_sar_i32(REG(B11_8), REG(B11_8), shift);
+ tcg_temp_free(shift);
+ tcg_gen_br(label4);
+ /* Rm = -32 */
+ gen_set_label(label2);
+ tcg_gen_brcondi_i32(TCG_COND_LT, REG(B11_8), 0, label3);
+ tcg_gen_movi_i32(REG(B11_8), 0);
+ tcg_gen_br(label4);
+ gen_set_label(label3);
+ tcg_gen_movi_i32(REG(B11_8), 0xffffffff);
+ gen_set_label(label4);
+ }
+ return;
+ case 0x400d: /* shld Rm,Rn */
+ {
+ int label1 = gen_new_label();
+ int label2 = gen_new_label();
+ int label3 = gen_new_label();
+ TCGv shift;
+ tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1);
+ /* Rm positive, shift to the left */
+ shift = tcg_temp_new();
+ tcg_gen_andi_i32(shift, REG(B7_4), 0x1f);
+ tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift);
+ tcg_temp_free(shift);
+ tcg_gen_br(label3);
+ /* Rm negative, shift to the right */
+ gen_set_label(label1);
+ shift = tcg_temp_new();
+ tcg_gen_andi_i32(shift, REG(B7_4), 0x1f);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2);
+ tcg_gen_not_i32(shift, REG(B7_4));
+ tcg_gen_andi_i32(shift, shift, 0x1f);
+ tcg_gen_addi_i32(shift, shift, 1);
+ tcg_gen_shr_i32(REG(B11_8), REG(B11_8), shift);
+ tcg_temp_free(shift);
+ tcg_gen_br(label3);
+ /* Rm = -32 */
+ gen_set_label(label2);
+ tcg_gen_movi_i32(REG(B11_8), 0);
+ gen_set_label(label3);
+ }
+ return;
+ case 0x3008: /* sub Rm,Rn */
+ tcg_gen_sub_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+ return;
+ case 0x300a: /* subc Rm,Rn */
+ gen_helper_subc(REG(B11_8), REG(B7_4), REG(B11_8));
+ return;
+ case 0x300b: /* subv Rm,Rn */
+ gen_helper_subv(REG(B11_8), REG(B7_4), REG(B11_8));
+ return;
+ case 0x2008: /* tst Rm,Rn */
+ {
+ TCGv val = tcg_temp_new();
+ tcg_gen_and_i32(val, REG(B7_4), REG(B11_8));
+ gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_temp_free(val);
+ }
+ return;
+ case 0x200a: /* xor Rm,Rn */
+ tcg_gen_xor_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+ return;
+ case 0xf00c: /* fmov {F,D,X}Rm,{F,D,X}Rn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_SZ) {
+ TCGv_i64 fp = tcg_temp_new_i64();
+ gen_load_fpr64(fp, XREG(B7_4));
+ gen_store_fpr64(fp, XREG(B11_8));
+ tcg_temp_free_i64(fp);
+ } else {
+ tcg_gen_mov_i32(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ }
+ return;
+ case 0xf00a: /* fmov {F,D,X}Rm,@Rn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_SZ) {
+ TCGv addr_hi = tcg_temp_new();
+ int fr = XREG(B7_4);
+ tcg_gen_addi_i32(addr_hi, REG(B11_8), 4);
+ tcg_gen_qemu_st32(cpu_fregs[fr ], REG(B11_8), ctx->memidx);
+ tcg_gen_qemu_st32(cpu_fregs[fr+1], addr_hi, ctx->memidx);
+ tcg_temp_free(addr_hi);
+ } else {
+ tcg_gen_qemu_st32(cpu_fregs[FREG(B7_4)], REG(B11_8), ctx->memidx);
+ }
+ return;
+ case 0xf008: /* fmov @Rm,{F,D,X}Rn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_SZ) {
+ TCGv addr_hi = tcg_temp_new();
+ int fr = XREG(B11_8);
+ tcg_gen_addi_i32(addr_hi, REG(B7_4), 4);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr ], REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr_hi, ctx->memidx);
+ tcg_temp_free(addr_hi);
+ } else {
+ tcg_gen_qemu_ld32u(cpu_fregs[FREG(B11_8)], REG(B7_4), ctx->memidx);
+ }
+ return;
+ case 0xf009: /* fmov @Rm+,{F,D,X}Rn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_SZ) {
+ TCGv addr_hi = tcg_temp_new();
+ int fr = XREG(B11_8);
+ tcg_gen_addi_i32(addr_hi, REG(B7_4), 4);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr ], REG(B7_4), ctx->memidx);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr_hi, ctx->memidx);
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 8);
+ tcg_temp_free(addr_hi);
+ } else {
+ tcg_gen_qemu_ld32u(cpu_fregs[FREG(B11_8)], REG(B7_4), ctx->memidx);
+ tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
+ }
+ return;
+ case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_SZ) {
+ TCGv addr = tcg_temp_new_i32();
+ int fr = XREG(B7_4);
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
+ tcg_gen_qemu_st32(cpu_fregs[fr+1], addr, ctx->memidx);
+ tcg_gen_subi_i32(addr, addr, 4);
+ tcg_gen_qemu_st32(cpu_fregs[fr ], addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
+ } else {
+ TCGv addr;
+ addr = tcg_temp_new_i32();
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
+ tcg_gen_qemu_st32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ {
+ TCGv addr = tcg_temp_new_i32();
+ tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+ if (ctx->fpscr & FPSCR_SZ) {
+ int fr = XREG(B11_8);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr ], addr, ctx->memidx);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr, ctx->memidx);
+ } else {
+ tcg_gen_qemu_ld32u(cpu_fregs[FREG(B11_8)], addr, ctx->memidx);
+ }
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xf007: /* fmov {F,D,X}Rn,@(R0,Rn) - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+ if (ctx->fpscr & FPSCR_SZ) {
+ int fr = XREG(B7_4);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr ], addr, ctx->memidx);
+ tcg_gen_addi_i32(addr, addr, 4);
+ tcg_gen_qemu_ld32u(cpu_fregs[fr+1], addr, ctx->memidx);
+ } else {
+ tcg_gen_qemu_st32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx);
+ }
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xf000: /* fadd Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+ case 0xf001: /* fsub Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+ case 0xf002: /* fmul Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+ case 0xf003: /* fdiv Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+ case 0xf004: /* fcmp/eq Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */
+ case 0xf005: /* fcmp/gt Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */
+ {
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_PR) {
+ TCGv_i64 fp0, fp1;
+
+ if (ctx->opcode & 0x0110)
+ break; /* illegal instruction */
+ fp0 = tcg_temp_new_i64();
+ fp1 = tcg_temp_new_i64();
+ gen_load_fpr64(fp0, DREG(B11_8));
+ gen_load_fpr64(fp1, DREG(B7_4));
+ switch (ctx->opcode & 0xf00f) {
+ case 0xf000: /* fadd Rm,Rn */
+ gen_helper_fadd_DT(fp0, fp0, fp1);
+ break;
+ case 0xf001: /* fsub Rm,Rn */
+ gen_helper_fsub_DT(fp0, fp0, fp1);
+ break;
+ case 0xf002: /* fmul Rm,Rn */
+ gen_helper_fmul_DT(fp0, fp0, fp1);
+ break;
+ case 0xf003: /* fdiv Rm,Rn */
+ gen_helper_fdiv_DT(fp0, fp0, fp1);
+ break;
+ case 0xf004: /* fcmp/eq Rm,Rn */
+ gen_helper_fcmp_eq_DT(fp0, fp1);
+ return;
+ case 0xf005: /* fcmp/gt Rm,Rn */
+ gen_helper_fcmp_gt_DT(fp0, fp1);
+ return;
+ }
+ gen_store_fpr64(fp0, DREG(B11_8));
+ tcg_temp_free_i64(fp0);
+ tcg_temp_free_i64(fp1);
+ } else {
+ switch (ctx->opcode & 0xf00f) {
+ case 0xf000: /* fadd Rm,Rn */
+ gen_helper_fadd_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ break;
+ case 0xf001: /* fsub Rm,Rn */
+ gen_helper_fsub_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ break;
+ case 0xf002: /* fmul Rm,Rn */
+ gen_helper_fmul_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ break;
+ case 0xf003: /* fdiv Rm,Rn */
+ gen_helper_fdiv_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ break;
+ case 0xf004: /* fcmp/eq Rm,Rn */
+ gen_helper_fcmp_eq_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ return;
+ case 0xf005: /* fcmp/gt Rm,Rn */
+ gen_helper_fcmp_gt_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+ return;
+ }
+ }
+ }
+ return;
+ case 0xf00e: /* fmac FR0,RM,Rn */
+ {
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_PR) {
+ break; /* illegal instruction */
+ } else {
+ gen_helper_fmac_FT(cpu_fregs[FREG(B11_8)],
+ cpu_fregs[FREG(0)], cpu_fregs[FREG(B7_4)], cpu_fregs[FREG(B11_8)]);
+ return;
+ }
+ }
+ }
+
+ switch (ctx->opcode & 0xff00) {
+ case 0xc900: /* and #imm,R0 */
+ tcg_gen_andi_i32(REG(0), REG(0), B7_0);
+ return;
+ case 0xcd00: /* and.b #imm,@(R0,GBR) */
+ {
+ TCGv addr, val;
+ addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(0), cpu_gbr);
+ val = tcg_temp_new();
+ tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_andi_i32(val, val, B7_0);
+ tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_temp_free(val);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x8b00: /* bf label */
+ CHECK_NOT_DELAY_SLOT
+ gen_conditional_jump(ctx, ctx->pc + 2,
+ ctx->pc + 4 + B7_0s * 2);
+ ctx->bstate = BS_BRANCH;
+ return;
+ case 0x8f00: /* bf/s label */
+ CHECK_NOT_DELAY_SLOT
+ gen_branch_slot(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2, 0);
+ ctx->flags |= DELAY_SLOT_CONDITIONAL;
+ return;
+ case 0x8900: /* bt label */
+ CHECK_NOT_DELAY_SLOT
+ gen_conditional_jump(ctx, ctx->pc + 4 + B7_0s * 2,
+ ctx->pc + 2);
+ ctx->bstate = BS_BRANCH;
+ return;
+ case 0x8d00: /* bt/s label */
+ CHECK_NOT_DELAY_SLOT
+ gen_branch_slot(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2, 1);
+ ctx->flags |= DELAY_SLOT_CONDITIONAL;
+ return;
+ case 0x8800: /* cmp/eq #imm,R0 */
+ gen_cmp_imm(TCG_COND_EQ, REG(0), B7_0s);
+ return;
+ case 0xc400: /* mov.b @(disp,GBR),R0 */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, cpu_gbr, B7_0);
+ tcg_gen_qemu_ld8s(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc500: /* mov.w @(disp,GBR),R0 */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2);
+ tcg_gen_qemu_ld16s(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc600: /* mov.l @(disp,GBR),R0 */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4);
+ tcg_gen_qemu_ld32s(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc000: /* mov.b R0,@(disp,GBR) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, cpu_gbr, B7_0);
+ tcg_gen_qemu_st8(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc100: /* mov.w R0,@(disp,GBR) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2);
+ tcg_gen_qemu_st16(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc200: /* mov.l R0,@(disp,GBR) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4);
+ tcg_gen_qemu_st32(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x8000: /* mov.b R0,@(disp,Rn) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, REG(B7_4), B3_0);
+ tcg_gen_qemu_st8(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x8100: /* mov.w R0,@(disp,Rn) */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2);
+ tcg_gen_qemu_st16(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x8400: /* mov.b @(disp,Rn),R0 */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, REG(B7_4), B3_0);
+ tcg_gen_qemu_ld8s(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0x8500: /* mov.w @(disp,Rn),R0 */
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2);
+ tcg_gen_qemu_ld16s(REG(0), addr, ctx->memidx);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc700: /* mova @(disp,PC),R0 */
+ tcg_gen_movi_i32(REG(0), ((ctx->pc & 0xfffffffc) + 4 + B7_0 * 4) & ~3);
+ return;
+ case 0xcb00: /* or #imm,R0 */
+ tcg_gen_ori_i32(REG(0), REG(0), B7_0);
+ return;
+ case 0xcf00: /* or.b #imm,@(R0,GBR) */
+ {
+ TCGv addr, val;
+ addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(0), cpu_gbr);
+ val = tcg_temp_new();
+ tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_ori_i32(val, val, B7_0);
+ tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_temp_free(val);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xc300: /* trapa #imm */
+ {
+ TCGv imm;
+ CHECK_NOT_DELAY_SLOT
+ imm = tcg_const_i32(B7_0);
+ gen_helper_trapa(imm);
+ tcg_temp_free(imm);
+ ctx->bstate = BS_BRANCH;
+ }
+ return;
+ case 0xc800: /* tst #imm,R0 */
+ {
+ TCGv val = tcg_temp_new();
+ tcg_gen_andi_i32(val, REG(0), B7_0);
+ gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_temp_free(val);
+ }
+ return;
+ case 0xcc00: /* tst.b #imm,@(R0,GBR) */
+ {
+ TCGv val = tcg_temp_new();
+ tcg_gen_add_i32(val, REG(0), cpu_gbr);
+ tcg_gen_qemu_ld8u(val, val, ctx->memidx);
+ tcg_gen_andi_i32(val, val, B7_0);
+ gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_temp_free(val);
+ }
+ return;
+ case 0xca00: /* xor #imm,R0 */
+ tcg_gen_xori_i32(REG(0), REG(0), B7_0);
+ return;
+ case 0xce00: /* xor.b #imm,@(R0,GBR) */
+ {
+ TCGv addr, val;
+ addr = tcg_temp_new();
+ tcg_gen_add_i32(addr, REG(0), cpu_gbr);
+ val = tcg_temp_new();
+ tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ tcg_gen_xori_i32(val, val, B7_0);
+ tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_temp_free(val);
+ tcg_temp_free(addr);
+ }
+ return;
+ }
+
+ switch (ctx->opcode & 0xf08f) {
+ case 0x408e: /* ldc Rm,Rn_BANK */
+ CHECK_PRIVILEGED
+ tcg_gen_mov_i32(ALTREG(B6_4), REG(B11_8));
+ return;
+ case 0x4087: /* ldc.l @Rm+,Rn_BANK */
+ CHECK_PRIVILEGED
+ tcg_gen_qemu_ld32s(ALTREG(B6_4), REG(B11_8), ctx->memidx);
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+ return;
+ case 0x0082: /* stc Rm_BANK,Rn */
+ CHECK_PRIVILEGED
+ tcg_gen_mov_i32(REG(B11_8), ALTREG(B6_4));
+ return;
+ case 0x4083: /* stc.l Rm_BANK,@-Rn */
+ CHECK_PRIVILEGED
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
+ tcg_gen_qemu_st32(ALTREG(B6_4), addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
+ }
+ return;
+ }
+
+ switch (ctx->opcode & 0xf0ff) {
+ case 0x0023: /* braf Rn */
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->pc + 4);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x0003: /* bsrf Rn */
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_movi_i32(cpu_pr, ctx->pc + 4);
+ tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr);
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x4015: /* cmp/pl Rn */
+ gen_cmp_imm(TCG_COND_GT, REG(B11_8), 0);
+ return;
+ case 0x4011: /* cmp/pz Rn */
+ gen_cmp_imm(TCG_COND_GE, REG(B11_8), 0);
+ return;
+ case 0x4010: /* dt Rn */
+ tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1);
+ gen_cmp_imm(TCG_COND_EQ, REG(B11_8), 0);
+ return;
+ case 0x402b: /* jmp @Rn */
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x400b: /* jsr @Rn */
+ CHECK_NOT_DELAY_SLOT
+ tcg_gen_movi_i32(cpu_pr, ctx->pc + 4);
+ tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
+ ctx->flags |= DELAY_SLOT;
+ ctx->delayed_pc = (uint32_t) - 1;
+ return;
+ case 0x400e: /* ldc Rm,SR */
+ CHECK_PRIVILEGED
+ tcg_gen_andi_i32(cpu_sr, REG(B11_8), 0x700083f3);
+ ctx->bstate = BS_STOP;
+ return;
+ case 0x4007: /* ldc.l @Rm+,SR */
+ CHECK_PRIVILEGED
+ {
+ TCGv val = tcg_temp_new();
+ tcg_gen_qemu_ld32s(val, REG(B11_8), ctx->memidx);
+ tcg_gen_andi_i32(cpu_sr, val, 0x700083f3);
+ tcg_temp_free(val);
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+ ctx->bstate = BS_STOP;
+ }
+ return;
+ case 0x0002: /* stc SR,Rn */
+ CHECK_PRIVILEGED
+ tcg_gen_mov_i32(REG(B11_8), cpu_sr);
+ return;
+ case 0x4003: /* stc SR,@-Rn */
+ CHECK_PRIVILEGED
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
+ tcg_gen_qemu_st32(cpu_sr, addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
+ }
+ return;
+#define LD(reg,ldnum,ldpnum,prechk) \
+ case ldnum: \
+ prechk \
+ tcg_gen_mov_i32 (cpu_##reg, REG(B11_8)); \
+ return; \
+ case ldpnum: \
+ prechk \
+ tcg_gen_qemu_ld32s (cpu_##reg, REG(B11_8), ctx->memidx); \
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); \
+ return;
+#define ST(reg,stnum,stpnum,prechk) \
+ case stnum: \
+ prechk \
+ tcg_gen_mov_i32 (REG(B11_8), cpu_##reg); \
+ return; \
+ case stpnum: \
+ prechk \
+ { \
+ TCGv addr = tcg_temp_new(); \
+ tcg_gen_subi_i32(addr, REG(B11_8), 4); \
+ tcg_gen_qemu_st32 (cpu_##reg, addr, ctx->memidx); \
+ tcg_gen_mov_i32(REG(B11_8), addr); \
+ tcg_temp_free(addr); \
+ } \
+ return;
+#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk) \
+ LD(reg,ldnum,ldpnum,prechk) \
+ ST(reg,stnum,stpnum,prechk)
+ LDST(gbr, 0x401e, 0x4017, 0x0012, 0x4013, {})
+ LDST(vbr, 0x402e, 0x4027, 0x0022, 0x4023, CHECK_PRIVILEGED)
+ LDST(ssr, 0x403e, 0x4037, 0x0032, 0x4033, CHECK_PRIVILEGED)
+ LDST(spc, 0x404e, 0x4047, 0x0042, 0x4043, CHECK_PRIVILEGED)
+ ST(sgr, 0x003a, 0x4032, CHECK_PRIVILEGED)
+ LD(sgr, 0x403a, 0x4036, CHECK_PRIVILEGED if (!(ctx->features & SH_FEATURE_SH4A)) break;)
+ LDST(dbr, 0x40fa, 0x40f6, 0x00fa, 0x40f2, CHECK_PRIVILEGED)
+ LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {})
+ LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {})
+ LDST(pr, 0x402a, 0x4026, 0x002a, 0x4022, {})
+ LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {CHECK_FPU_ENABLED})
+ case 0x406a: /* lds Rm,FPSCR */
+ CHECK_FPU_ENABLED
+ gen_helper_ld_fpscr(REG(B11_8));
+ ctx->bstate = BS_STOP;
+ return;
+ case 0x4066: /* lds.l @Rm+,FPSCR */
+ CHECK_FPU_ENABLED
+ {
+ TCGv addr = tcg_temp_new();
+ tcg_gen_qemu_ld32s(addr, REG(B11_8), ctx->memidx);
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+ gen_helper_ld_fpscr(addr);
+ tcg_temp_free(addr);
+ ctx->bstate = BS_STOP;
+ }
+ return;
+ case 0x006a: /* sts FPSCR,Rn */
+ CHECK_FPU_ENABLED
+ tcg_gen_andi_i32(REG(B11_8), cpu_fpscr, 0x003fffff);
+ return;
+ case 0x4062: /* sts FPSCR,@-Rn */
+ CHECK_FPU_ENABLED
+ {
+ TCGv addr, val;
+ val = tcg_temp_new();
+ tcg_gen_andi_i32(val, cpu_fpscr, 0x003fffff);
+ addr = tcg_temp_new();
+ tcg_gen_subi_i32(addr, REG(B11_8), 4);
+ tcg_gen_qemu_st32(val, addr, ctx->memidx);
+ tcg_gen_mov_i32(REG(B11_8), addr);
+ tcg_temp_free(addr);
+ tcg_temp_free(val);
+ }
+ return;
+ case 0x00c3: /* movca.l R0,@Rm */
+ {
+ TCGv val = tcg_temp_new();
+ tcg_gen_qemu_ld32u(val, REG(B11_8), ctx->memidx);
+ gen_helper_movcal (REG(B11_8), val);
+ tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+ }
+ ctx->has_movcal = 1;
+ return;
+ case 0x40a9:
+ /* MOVUA.L @Rm,R0 (Rm) -> R0
+ Load non-boundary-aligned data */
+ tcg_gen_qemu_ld32u(REG(0), REG(B11_8), ctx->memidx);
+ return;
+ case 0x40e9:
+ /* MOVUA.L @Rm+,R0 (Rm) -> R0, Rm + 4 -> Rm
+ Load non-boundary-aligned data */
+ tcg_gen_qemu_ld32u(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+ return;
+ case 0x0029: /* movt Rn */
+ tcg_gen_andi_i32(REG(B11_8), cpu_sr, SR_T);
+ return;
+ case 0x0073:
+ /* MOVCO.L
+ LDST -> T
+ If (T == 1) R0 -> (Rn)
+ 0 -> LDST
+ */
+ if (ctx->features & SH_FEATURE_SH4A) {
+ int label = gen_new_label();
+ gen_clr_t();
+ tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst);
+ tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label);
+ tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+ gen_set_label(label);
+ tcg_gen_movi_i32(cpu_ldst, 0);
+ return;
+ } else
+ break;
+ case 0x0063:
+ /* MOVLI.L @Rm,R0
+ 1 -> LDST
+ (Rm) -> R0
+ When interrupt/exception
+ occurred 0 -> LDST
+ */
+ if (ctx->features & SH_FEATURE_SH4A) {
+ tcg_gen_movi_i32(cpu_ldst, 0);
+ tcg_gen_qemu_ld32s(REG(0), REG(B11_8), ctx->memidx);
+ tcg_gen_movi_i32(cpu_ldst, 1);
+ return;
+ } else
+ break;
+ case 0x0093: /* ocbi @Rn */
+ {
+ gen_helper_ocbi (REG(B11_8));
+ }
+ return;
+ case 0x00a3: /* ocbp @Rn */
+ {
+ TCGv dummy = tcg_temp_new();
+ tcg_gen_qemu_ld32s(dummy, REG(B11_8), ctx->memidx);
+ tcg_temp_free(dummy);
+ }
+ return;
+ case 0x00b3: /* ocbwb @Rn */
+ {
+ TCGv dummy = tcg_temp_new();
+ tcg_gen_qemu_ld32s(dummy, REG(B11_8), ctx->memidx);
+ tcg_temp_free(dummy);
+ }
+ return;
+ case 0x0083: /* pref @Rn */
+ return;
+ case 0x00d3: /* prefi @Rn */
+ if (ctx->features & SH_FEATURE_SH4A)
+ return;
+ else
+ break;
+ case 0x00e3: /* icbi @Rn */
+ if (ctx->features & SH_FEATURE_SH4A)
+ return;
+ else
+ break;
+ case 0x00ab: /* synco */
+ if (ctx->features & SH_FEATURE_SH4A)
+ return;
+ else
+ break;
+ case 0x4024: /* rotcl Rn */
+ {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_mov_i32(tmp, cpu_sr);
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31);
+ tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
+ gen_copy_bit_i32(REG(B11_8), 0, tmp, 0);
+ tcg_temp_free(tmp);
+ }
+ return;
+ case 0x4025: /* rotcr Rn */
+ {
+ TCGv tmp = tcg_temp_new();
+ tcg_gen_mov_i32(tmp, cpu_sr);
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1);
+ gen_copy_bit_i32(REG(B11_8), 31, tmp, 0);
+ tcg_temp_free(tmp);
+ }
+ return;
+ case 0x4004: /* rotl Rn */
+ tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1);
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ return;
+ case 0x4005: /* rotr Rn */
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1);
+ return;
+ case 0x4000: /* shll Rn */
+ case 0x4020: /* shal Rn */
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31);
+ tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
+ return;
+ case 0x4021: /* shar Rn */
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1);
+ return;
+ case 0x4001: /* shlr Rn */
+ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0);
+ tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1);
+ return;
+ case 0x4008: /* shll2 Rn */
+ tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 2);
+ return;
+ case 0x4018: /* shll8 Rn */
+ tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 8);
+ return;
+ case 0x4028: /* shll16 Rn */
+ tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 16);
+ return;
+ case 0x4009: /* shlr2 Rn */
+ tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 2);
+ return;
+ case 0x4019: /* shlr8 Rn */
+ tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 8);
+ return;
+ case 0x4029: /* shlr16 Rn */
+ tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 16);
+ return;
+ case 0x401b: /* tas.b @Rn */
+ {
+ TCGv addr, val;
+ addr = tcg_temp_local_new();
+ tcg_gen_mov_i32(addr, REG(B11_8));
+ val = tcg_temp_local_new();
+ tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
+ gen_cmp_imm(TCG_COND_EQ, val, 0);
+ tcg_gen_ori_i32(val, val, 0x80);
+ tcg_gen_qemu_st8(val, addr, ctx->memidx);
+ tcg_temp_free(val);
+ tcg_temp_free(addr);
+ }
+ return;
+ case 0xf00d: /* fsts FPUL,FRn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ tcg_gen_mov_i32(cpu_fregs[FREG(B11_8)], cpu_fpul);
+ return;
+ case 0xf01d: /* flds FRm,FPUL - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ tcg_gen_mov_i32(cpu_fpul, cpu_fregs[FREG(B11_8)]);
+ return;
+ case 0xf02d: /* float FPUL,FRn/DRn - FPSCR: R[PR,Enable.I]/W[Cause,Flag] */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_PR) {
+ TCGv_i64 fp;
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ fp = tcg_temp_new_i64();
+ gen_helper_float_DT(fp, cpu_fpul);
+ gen_store_fpr64(fp, DREG(B11_8));
+ tcg_temp_free_i64(fp);
+ }
+ else {
+ gen_helper_float_FT(cpu_fregs[FREG(B11_8)], cpu_fpul);
+ }
+ return;
+ case 0xf03d: /* ftrc FRm/DRm,FPUL - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_PR) {
+ TCGv_i64 fp;
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ fp = tcg_temp_new_i64();
+ gen_load_fpr64(fp, DREG(B11_8));
+ gen_helper_ftrc_DT(cpu_fpul, fp);
+ tcg_temp_free_i64(fp);
+ }
+ else {
+ gen_helper_ftrc_FT(cpu_fpul, cpu_fregs[FREG(B11_8)]);
+ }
+ return;
+ case 0xf04d: /* fneg FRn/DRn - FPSCR: Nothing */
+ CHECK_FPU_ENABLED
+ {
+ gen_helper_fneg_T(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]);
+ }
+ return;
+ case 0xf05d: /* fabs FRn/DRn */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_PR) {
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ TCGv_i64 fp = tcg_temp_new_i64();
+ gen_load_fpr64(fp, DREG(B11_8));
+ gen_helper_fabs_DT(fp, fp);
+ gen_store_fpr64(fp, DREG(B11_8));
+ tcg_temp_free_i64(fp);
+ } else {
+ gen_helper_fabs_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]);
+ }
+ return;
+ case 0xf06d: /* fsqrt FRn */
+ CHECK_FPU_ENABLED
+ if (ctx->fpscr & FPSCR_PR) {
+ if (ctx->opcode & 0x0100)
+ break; /* illegal instruction */
+ TCGv_i64 fp = tcg_temp_new_i64();
+ gen_load_fpr64(fp, DREG(B11_8));
+ gen_helper_fsqrt_DT(fp, fp);
+ gen_store_fpr64(fp, DREG(B11_8));
+ tcg_temp_free_i64(fp);
+ } else {
+ gen_helper_fsqrt_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]);
+ }
+ return;
+ case 0xf07d: /* fsrra FRn */
+ CHECK_FPU_ENABLED
+ break;
+ case 0xf08d: /* fldi0 FRn - FPSCR: R[PR] */
+ CHECK_FPU_ENABLED
+ if (!(ctx->fpscr & FPSCR_PR)) {
+ tcg_gen_movi_i32(cpu_fregs[FREG(B11_8)], 0);
+ }
+ return;
+ case 0xf09d: /* fldi1 FRn - FPSCR: R[PR] */
+ CHECK_FPU_ENABLED
+ if (!(ctx->fpscr & FPSCR_PR)) {
+ tcg_gen_movi_i32(cpu_fregs[FREG(B11_8)], 0x3f800000);
+ }
+ return;
+ case 0xf0ad: /* fcnvsd FPUL,DRn */
+ CHECK_FPU_ENABLED
+ {
+ TCGv_i64 fp = tcg_temp_new_i64();
+ gen_helper_fcnvsd_FT_DT(fp, cpu_fpul);
+ gen_store_fpr64(fp, DREG(B11_8));
+ tcg_temp_free_i64(fp);
+ }
+ return;
+ case 0xf0bd: /* fcnvds DRn,FPUL */
+ CHECK_FPU_ENABLED
+ {
+ TCGv_i64 fp = tcg_temp_new_i64();
+ gen_load_fpr64(fp, DREG(B11_8));
+ gen_helper_fcnvds_DT_FT(cpu_fpul, fp);
+ tcg_temp_free_i64(fp);
+ }
+ return;
+ case 0xf0ed: /* fipr FVm,FVn */
+ CHECK_FPU_ENABLED
+ if ((ctx->fpscr & FPSCR_PR) == 0) {
+ TCGv m, n;
+ m = tcg_const_i32((ctx->opcode >> 16) & 3);
+ n = tcg_const_i32((ctx->opcode >> 18) & 3);
+ gen_helper_fipr(m, n);
+ tcg_temp_free(m);
+ tcg_temp_free(n);
+ return;
+ }
+ break;
+ case 0xf0fd: /* ftrv XMTRX,FVn */
+ CHECK_FPU_ENABLED
+ if ((ctx->opcode & 0x0300) == 0x0100 &&
+ (ctx->fpscr & FPSCR_PR) == 0) {
+ TCGv n;
+ n = tcg_const_i32((ctx->opcode >> 18) & 3);
+ gen_helper_ftrv(n);
+ tcg_temp_free(n);
+ return;
+ }
+ break;
+ }
+#if 0
+ fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n",
+ ctx->opcode, ctx->pc);
+ fflush(stderr);
+#endif
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
+ gen_helper_raise_slot_illegal_instruction();
+ } else {
+ gen_helper_raise_illegal_instruction();
+ }
+ ctx->bstate = BS_EXCP;
+}
+
+static void decode_opc(DisasContext * ctx)
+{
+ uint32_t old_flags = ctx->flags;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) {
+ tcg_gen_debug_insn_start(ctx->pc);
+ }
+
+ _decode_opc(ctx);
+
+ if (old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
+ if (ctx->flags & DELAY_SLOT_CLEARME) {
+ gen_store_flags(0);
+ } else {
+ /* go out of the delay slot */
+ uint32_t new_flags = ctx->flags;
+ new_flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
+ gen_store_flags(new_flags);
+ }
+ ctx->flags = 0;
+ ctx->bstate = BS_BRANCH;
+ if (old_flags & DELAY_SLOT_CONDITIONAL) {
+ gen_delayed_conditional_jump(ctx);
+ } else if (old_flags & DELAY_SLOT) {
+ gen_jump(ctx);
+ }
+
+ }
+
+ /* go into a delay slot */
+ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL))
+ gen_store_flags(ctx->flags);
+}
+
+static inline void
+gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
+ int search_pc)
+{
+ DisasContext ctx;
+ target_ulong pc_start;
+ static uint16_t *gen_opc_end;
+ CPUBreakpoint *bp;
+ int i, ii;
+ int num_insns;
+ int max_insns;
+
+ pc_start = tb->pc;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ ctx.pc = pc_start;
+ ctx.flags = (uint32_t)tb->flags;
+ ctx.bstate = BS_NONE;
+ ctx.sr = env->sr;
+ ctx.fpscr = env->fpscr;
+ ctx.memidx = (env->sr & SR_MD) == 0 ? 1 : 0;
+ /* We don't know if the delayed pc came from a dynamic or static branch,
+ so assume it is a dynamic branch. */
+ ctx.delayed_pc = -1; /* use delayed pc from env pointer */
+ ctx.tb = tb;
+ ctx.singlestep_enabled = env->singlestep_enabled;
+ ctx.features = env->features;
+ ctx.has_movcal = (tb->flags & TB_FLAG_PENDING_MOVCA);
+
+ ii = -1;
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0)
+ max_insns = CF_COUNT_MASK;
+ gen_icount_start();
+ while (ctx.bstate == BS_NONE && gen_opc_ptr < gen_opc_end) {
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+ if (ctx.pc == bp->pc) {
+ /* We have hit a breakpoint - make sure PC is up-to-date */
+ tcg_gen_movi_i32(cpu_pc, ctx.pc);
+ gen_helper_debug();
+ ctx.bstate = BS_EXCP;
+ break;
+ }
+ }
+ }
+ if (search_pc) {
+ i = gen_opc_ptr - gen_opc_buf;
+ if (ii < i) {
+ ii++;
+ while (ii < i)
+ gen_opc_instr_start[ii++] = 0;
+ }
+ gen_opc_pc[ii] = ctx.pc;
+ gen_opc_hflags[ii] = ctx.flags;
+ gen_opc_instr_start[ii] = 1;
+ gen_opc_icount[ii] = num_insns;
+ }
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+ gen_io_start();
+#if 0
+ fprintf(stderr, "Loading opcode at address 0x%08x\n", ctx.pc);
+ fflush(stderr);
+#endif
+ ctx.opcode = lduw_code(ctx.pc);
+ decode_opc(&ctx);
+ num_insns++;
+ ctx.pc += 2;
+ if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0)
+ break;
+ if (env->singlestep_enabled)
+ break;
+ if (num_insns >= max_insns)
+ break;
+ if (singlestep)
+ break;
+ }
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ if (env->singlestep_enabled) {
+ tcg_gen_movi_i32(cpu_pc, ctx.pc);
+ gen_helper_debug();
+ } else {
+ switch (ctx.bstate) {
+ case BS_STOP:
+ /* gen_op_interrupt_restart(); */
+ /* fall through */
+ case BS_NONE:
+ if (ctx.flags) {
+ gen_store_flags(ctx.flags | DELAY_SLOT_CLEARME);
+ }
+ gen_goto_tb(&ctx, 0, ctx.pc);
+ break;
+ case BS_EXCP:
+ /* gen_op_interrupt_restart(); */
+ tcg_gen_exit_tb(0);
+ break;
+ case BS_BRANCH:
+ default:
+ break;
+ }
+ }
+
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+ if (search_pc) {
+ i = gen_opc_ptr - gen_opc_buf;
+ ii++;
+ while (ii <= i)
+ gen_opc_instr_start[ii++] = 0;
+ } else {
+ tb->size = ctx.pc - pc_start;
+ tb->icount = num_insns;
+ }
+
+#ifdef DEBUG_DISAS
+#ifdef SH4_DEBUG_DISAS
+ qemu_log_mask(CPU_LOG_TB_IN_ASM, "\n");
+#endif
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("IN:\n"); /* , lookup_symbol(pc_start)); */
+ log_target_disas(pc_start, ctx.pc - pc_start, 0);
+ 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 gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ env->pc = gen_opc_pc[pc_pos];
+ env->flags = gen_opc_hflags[pc_pos];
+}
diff --git a/target-sparc/TODO b/target-sparc/TODO
new file mode 100644
index 0000000..c87459f
--- /dev/null
+++ b/target-sparc/TODO
@@ -0,0 +1,88 @@
+TODO-list:
+
+CPU common:
+- Unimplemented features/bugs:
+ - Delay slot handling may fail sometimes (branch end of page, delay
+ slot next page)
+ - Atomical instructions
+ - CPU features should match real CPUs (also ASI selection)
+- Optimizations/improvements:
+ - Condition code/branch handling like x86, also for FPU?
+ - Remove remaining explicit alignment checks
+ - Global register for regwptr, so that windowed registers can be
+ accessed directly
+ - Improve Sparc32plus addressing
+ - NPC/PC static optimisations (use JUMP_TB when possible)? (Is this
+ obsolete?)
+ - Synthetic instructions
+ - MMU model dependant on CPU model
+ - Select ASI helper at translation time (on V9 only if known)
+ - KQemu/KVM support for VM only
+ - Hardware breakpoint/watchpoint support
+ - Cache emulation mode
+ - Reverse-endian pages
+ - Faster FPU emulation
+ - Busy loop detection
+
+Sparc32 CPUs:
+- Unimplemented features/bugs:
+ - Sun4/Sun4c MMUs
+ - Some V8 ASIs
+
+Sparc64 CPUs:
+- Unimplemented features/bugs:
+ - Interrupt handling
+ - Secondary address space, other MMU functions
+ - Many V9/UA2005/UA2007 ASIs
+ - Rest of V9 instructions, missing VIS instructions
+ - IG/MG/AG vs. UA2007 globals
+ - Full hypervisor support
+ - SMP/CMT
+ - Sun4v CPUs
+
+Sun4:
+- To be added
+
+Sun4c:
+- A lot of unimplemented features
+- Maybe split from Sun4m
+
+Sun4m:
+- Unimplemented features/bugs:
+ - Hardware devices do not match real boards
+ - Floppy does not work
+ - CS4231: merge with cs4231a, add DMA
+ - Add cg6, bwtwo
+ - Arbitrary resolution support
+ - PCI for MicroSparc-IIe
+ - JavaStation machines
+ - SBus slot probing, FCode ROM support
+ - SMP probing support
+ - Interrupt routing does not match real HW
+ - SuSE 7.3 keyboard sometimes unresponsive
+ - Gentoo 2004.1 SMP does not work
+ - SS600MP ledma -> lebuffer
+ - Type 5 keyboard
+ - Less fixed hardware choices
+ - DBRI audio (Am7930)
+ - BPP parallel
+ - Diagnostic switch
+ - ESP PIO mode
+
+Sun4d:
+- A lot of unimplemented features:
+ - SBI
+ - IO-unit
+- Maybe split from Sun4m
+
+Sun4u:
+- Unimplemented features/bugs:
+ - Interrupt controller
+ - PCI/IOMMU support (Simba, JIO, Tomatillo, Psycho, Schizo, Safari...)
+ - SMP
+ - Happy Meal Ethernet, flash, I2C, GPIO
+ - A lot of real machine types
+
+Sun4v:
+- A lot of unimplemented features
+ - A lot of real machine types
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
new file mode 100644
index 0000000..320530e
--- /dev/null
+++ b/target-sparc/cpu.h
@@ -0,0 +1,659 @@
+#ifndef CPU_SPARC_H
+#define CPU_SPARC_H
+
+#include "config.h"
+#include "qemu-common.h"
+
+#if !defined(TARGET_SPARC64)
+#define TARGET_LONG_BITS 32
+#define TARGET_FPREGS 32
+#define TARGET_PAGE_BITS 12 /* 4k */
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#else
+#define TARGET_LONG_BITS 64
+#define TARGET_FPREGS 64
+#define TARGET_PAGE_BITS 13 /* 8k */
+#define TARGET_PHYS_ADDR_SPACE_BITS 41
+# ifdef TARGET_ABI32
+# define TARGET_VIRT_ADDR_SPACE_BITS 32
+# else
+# define TARGET_VIRT_ADDR_SPACE_BITS 44
+# endif
+#endif
+
+#define CPUState struct CPUSPARCState
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+#if !defined(TARGET_SPARC64)
+#define ELF_MACHINE EM_SPARC
+#else
+#define ELF_MACHINE EM_SPARCV9
+#endif
+
+/*#define EXCP_INTERRUPT 0x100*/
+
+/* trap definitions */
+#ifndef TARGET_SPARC64
+#define TT_TFAULT 0x01
+#define TT_ILL_INSN 0x02
+#define TT_PRIV_INSN 0x03
+#define TT_NFPU_INSN 0x04
+#define TT_WIN_OVF 0x05
+#define TT_WIN_UNF 0x06
+#define TT_UNALIGNED 0x07
+#define TT_FP_EXCP 0x08
+#define TT_DFAULT 0x09
+#define TT_TOVF 0x0a
+#define TT_EXTINT 0x10
+#define TT_CODE_ACCESS 0x21
+#define TT_UNIMP_FLUSH 0x25
+#define TT_DATA_ACCESS 0x29
+#define TT_DIV_ZERO 0x2a
+#define TT_NCP_INSN 0x24
+#define TT_TRAP 0x80
+#else
+#define TT_POWER_ON_RESET 0x01
+#define TT_TFAULT 0x08
+#define TT_CODE_ACCESS 0x0a
+#define TT_ILL_INSN 0x10
+#define TT_UNIMP_FLUSH TT_ILL_INSN
+#define TT_PRIV_INSN 0x11
+#define TT_NFPU_INSN 0x20
+#define TT_FP_EXCP 0x21
+#define TT_TOVF 0x23
+#define TT_CLRWIN 0x24
+#define TT_DIV_ZERO 0x28
+#define TT_DFAULT 0x30
+#define TT_DATA_ACCESS 0x32
+#define TT_UNALIGNED 0x34
+#define TT_PRIV_ACT 0x37
+#define TT_EXTINT 0x40
+#define TT_IVEC 0x60
+#define TT_TMISS 0x64
+#define TT_DMISS 0x68
+#define TT_DPROT 0x6c
+#define TT_SPILL 0x80
+#define TT_FILL 0xc0
+#define TT_WOTHER (1 << 5)
+#define TT_TRAP 0x100
+#endif
+
+#define PSR_NEG_SHIFT 23
+#define PSR_NEG (1 << PSR_NEG_SHIFT)
+#define PSR_ZERO_SHIFT 22
+#define PSR_ZERO (1 << PSR_ZERO_SHIFT)
+#define PSR_OVF_SHIFT 21
+#define PSR_OVF (1 << PSR_OVF_SHIFT)
+#define PSR_CARRY_SHIFT 20
+#define PSR_CARRY (1 << PSR_CARRY_SHIFT)
+#define PSR_ICC (PSR_NEG|PSR_ZERO|PSR_OVF|PSR_CARRY)
+#if !defined(TARGET_SPARC64)
+#define PSR_EF (1<<12)
+#define PSR_PIL 0xf00
+#define PSR_S (1<<7)
+#define PSR_PS (1<<6)
+#define PSR_ET (1<<5)
+#define PSR_CWP 0x1f
+#endif
+
+#define CC_SRC (env->cc_src)
+#define CC_SRC2 (env->cc_src2)
+#define CC_DST (env->cc_dst)
+#define CC_OP (env->cc_op)
+
+enum {
+ CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
+ CC_OP_FLAGS, /* all cc are back in status register */
+ CC_OP_DIV, /* modify N, Z and V, C = 0*/
+ CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_TADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_TADDTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */
+ CC_OP_SUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_SUBX, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_TSUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
+ CC_OP_TSUBTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */
+ CC_OP_LOGIC, /* modify N and Z, C = V = 0, CC_DST = res */
+ CC_OP_NB,
+};
+
+/* Trap base register */
+#define TBR_BASE_MASK 0xfffff000
+
+#if defined(TARGET_SPARC64)
+#define PS_TCT (1<<12) /* UA2007, impl.dep. trap on control transfer */
+#define PS_IG (1<<11) /* v9, zero on UA2007 */
+#define PS_MG (1<<10) /* v9, zero on UA2007 */
+#define PS_CLE (1<<9) /* UA2007 */
+#define PS_TLE (1<<8) /* UA2007 */
+#define PS_RMO (1<<7)
+#define PS_RED (1<<5) /* v9, zero on UA2007 */
+#define PS_PEF (1<<4) /* enable fpu */
+#define PS_AM (1<<3) /* address mask */
+#define PS_PRIV (1<<2)
+#define PS_IE (1<<1)
+#define PS_AG (1<<0) /* v9, zero on UA2007 */
+
+#define FPRS_FEF (1<<2)
+
+#define HS_PRIV (1<<2)
+#endif
+
+/* Fcc */
+#define FSR_RD1 (1ULL << 31)
+#define FSR_RD0 (1ULL << 30)
+#define FSR_RD_MASK (FSR_RD1 | FSR_RD0)
+#define FSR_RD_NEAREST 0
+#define FSR_RD_ZERO FSR_RD0
+#define FSR_RD_POS FSR_RD1
+#define FSR_RD_NEG (FSR_RD1 | FSR_RD0)
+
+#define FSR_NVM (1ULL << 27)
+#define FSR_OFM (1ULL << 26)
+#define FSR_UFM (1ULL << 25)
+#define FSR_DZM (1ULL << 24)
+#define FSR_NXM (1ULL << 23)
+#define FSR_TEM_MASK (FSR_NVM | FSR_OFM | FSR_UFM | FSR_DZM | FSR_NXM)
+
+#define FSR_NVA (1ULL << 9)
+#define FSR_OFA (1ULL << 8)
+#define FSR_UFA (1ULL << 7)
+#define FSR_DZA (1ULL << 6)
+#define FSR_NXA (1ULL << 5)
+#define FSR_AEXC_MASK (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
+
+#define FSR_NVC (1ULL << 4)
+#define FSR_OFC (1ULL << 3)
+#define FSR_UFC (1ULL << 2)
+#define FSR_DZC (1ULL << 1)
+#define FSR_NXC (1ULL << 0)
+#define FSR_CEXC_MASK (FSR_NVC | FSR_OFC | FSR_UFC | FSR_DZC | FSR_NXC)
+
+#define FSR_FTT2 (1ULL << 16)
+#define FSR_FTT1 (1ULL << 15)
+#define FSR_FTT0 (1ULL << 14)
+//gcc warns about constant overflow for ~FSR_FTT_MASK
+//#define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0)
+#ifdef TARGET_SPARC64
+#define FSR_FTT_NMASK 0xfffffffffffe3fffULL
+#define FSR_FTT_CEXC_NMASK 0xfffffffffffe3fe0ULL
+#define FSR_LDFSR_OLDMASK 0x0000003f000fc000ULL
+#define FSR_LDXFSR_MASK 0x0000003fcfc00fffULL
+#define FSR_LDXFSR_OLDMASK 0x00000000000fc000ULL
+#else
+#define FSR_FTT_NMASK 0xfffe3fffULL
+#define FSR_FTT_CEXC_NMASK 0xfffe3fe0ULL
+#define FSR_LDFSR_OLDMASK 0x000fc000ULL
+#endif
+#define FSR_LDFSR_MASK 0xcfc00fffULL
+#define FSR_FTT_IEEE_EXCP (1ULL << 14)
+#define FSR_FTT_UNIMPFPOP (3ULL << 14)
+#define FSR_FTT_SEQ_ERROR (4ULL << 14)
+#define FSR_FTT_INVAL_FPR (6ULL << 14)
+
+#define FSR_FCC1_SHIFT 11
+#define FSR_FCC1 (1ULL << FSR_FCC1_SHIFT)
+#define FSR_FCC0_SHIFT 10
+#define FSR_FCC0 (1ULL << FSR_FCC0_SHIFT)
+
+/* MMU */
+#define MMU_E (1<<0)
+#define MMU_NF (1<<1)
+
+#define PTE_ENTRYTYPE_MASK 3
+#define PTE_ACCESS_MASK 0x1c
+#define PTE_ACCESS_SHIFT 2
+#define PTE_PPN_SHIFT 7
+#define PTE_ADDR_MASK 0xffffff00
+
+#define PG_ACCESSED_BIT 5
+#define PG_MODIFIED_BIT 6
+#define PG_CACHE_BIT 7
+
+#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT)
+#define PG_MODIFIED_MASK (1 << PG_MODIFIED_BIT)
+#define PG_CACHE_MASK (1 << PG_CACHE_BIT)
+
+/* 3 <= NWINDOWS <= 32. */
+#define MIN_NWINDOWS 3
+#define MAX_NWINDOWS 32
+
+#if !defined(TARGET_SPARC64)
+#define NB_MMU_MODES 2
+#else
+#define NB_MMU_MODES 6
+typedef struct trap_state {
+ uint64_t tpc;
+ uint64_t tnpc;
+ uint64_t tstate;
+ uint32_t tt;
+} trap_state;
+#endif
+
+typedef struct sparc_def_t {
+ const char *name;
+ target_ulong iu_version;
+ uint32_t fpu_version;
+ uint32_t mmu_version;
+ uint32_t mmu_bm;
+ uint32_t mmu_ctpr_mask;
+ uint32_t mmu_cxr_mask;
+ uint32_t mmu_sfsr_mask;
+ uint32_t mmu_trcr_mask;
+ uint32_t mxcc_version;
+ uint32_t features;
+ uint32_t nwindows;
+ uint32_t maxtl;
+} sparc_def_t;
+
+#define CPU_FEATURE_FLOAT (1 << 0)
+#define CPU_FEATURE_FLOAT128 (1 << 1)
+#define CPU_FEATURE_SWAP (1 << 2)
+#define CPU_FEATURE_MUL (1 << 3)
+#define CPU_FEATURE_DIV (1 << 4)
+#define CPU_FEATURE_FLUSH (1 << 5)
+#define CPU_FEATURE_FSQRT (1 << 6)
+#define CPU_FEATURE_FMUL (1 << 7)
+#define CPU_FEATURE_VIS1 (1 << 8)
+#define CPU_FEATURE_VIS2 (1 << 9)
+#define CPU_FEATURE_FSMULD (1 << 10)
+#define CPU_FEATURE_HYPV (1 << 11)
+#define CPU_FEATURE_CMT (1 << 12)
+#define CPU_FEATURE_GL (1 << 13)
+#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */
+#define CPU_FEATURE_ASR17 (1 << 15)
+#define CPU_FEATURE_CACHE_CTRL (1 << 16)
+
+#ifndef TARGET_SPARC64
+#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \
+ CPU_FEATURE_MUL | CPU_FEATURE_DIV | \
+ CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \
+ CPU_FEATURE_FMUL | CPU_FEATURE_FSMULD)
+#else
+#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \
+ CPU_FEATURE_MUL | CPU_FEATURE_DIV | \
+ CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \
+ CPU_FEATURE_FMUL | CPU_FEATURE_VIS1 | \
+ CPU_FEATURE_VIS2 | CPU_FEATURE_FSMULD)
+enum {
+ mmu_us_12, // Ultrasparc < III (64 entry TLB)
+ mmu_us_3, // Ultrasparc III (512 entry TLB)
+ mmu_us_4, // Ultrasparc IV (several TLBs, 32 and 256MB pages)
+ mmu_sun4v, // T1, T2
+};
+#endif
+
+#define TTE_VALID_BIT (1ULL << 63)
+#define TTE_USED_BIT (1ULL << 41)
+#define TTE_LOCKED_BIT (1ULL << 6)
+#define TTE_GLOBAL_BIT (1ULL << 0)
+
+#define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT)
+#define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT)
+#define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT)
+#define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT)
+
+#define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT)
+#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
+
+typedef struct SparcTLBEntry {
+ uint64_t tag;
+ uint64_t tte;
+} SparcTLBEntry;
+
+struct CPUTimer
+{
+ const char *name;
+ uint32_t frequency;
+ uint32_t disabled;
+ uint64_t disabled_mask;
+ int64_t clock_offset;
+ struct QEMUTimer *qtimer;
+};
+
+typedef struct CPUTimer CPUTimer;
+
+struct QEMUFile;
+void cpu_put_timer(struct QEMUFile *f, CPUTimer *s);
+void cpu_get_timer(struct QEMUFile *f, CPUTimer *s);
+
+typedef struct CPUSPARCState {
+ target_ulong gregs[8]; /* general registers */
+ target_ulong *regwptr; /* pointer to current register window */
+ target_ulong pc; /* program counter */
+ target_ulong npc; /* next program counter */
+ target_ulong y; /* multiply/divide register */
+
+ /* emulator internal flags handling */
+ target_ulong cc_src, cc_src2;
+ target_ulong cc_dst;
+ uint32_t cc_op;
+
+ target_ulong t0, t1; /* temporaries live across basic blocks */
+ target_ulong cond; /* conditional branch result (XXX: save it in a
+ temporary register when possible) */
+
+ uint32_t psr; /* processor state register */
+ target_ulong fsr; /* FPU state register */
+ float32 fpr[TARGET_FPREGS]; /* floating point registers */
+ uint32_t cwp; /* index of current register window (extracted
+ from PSR) */
+#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
+ uint32_t wim; /* window invalid mask */
+#endif
+ target_ulong tbr; /* trap base register */
+#if !defined(TARGET_SPARC64)
+ int psrs; /* supervisor mode (extracted from PSR) */
+ int psrps; /* previous supervisor mode */
+ int psret; /* enable traps */
+#endif
+ uint32_t psrpil; /* interrupt blocking level */
+ uint32_t pil_in; /* incoming interrupt level bitmap */
+#if !defined(TARGET_SPARC64)
+ int psref; /* enable fpu */
+#endif
+ target_ulong version;
+ int interrupt_index;
+ uint32_t nwindows;
+ /* NOTE: we allow 8 more registers to handle wrapping */
+ target_ulong regbase[MAX_NWINDOWS * 16 + 8];
+
+ CPU_COMMON
+
+ /* MMU regs */
+#if defined(TARGET_SPARC64)
+ uint64_t lsu;
+#define DMMU_E 0x8
+#define IMMU_E 0x4
+ //typedef struct SparcMMU
+ union {
+ uint64_t immuregs[16];
+ struct {
+ uint64_t tsb_tag_target;
+ uint64_t unused_mmu_primary_context; // use DMMU
+ uint64_t unused_mmu_secondary_context; // use DMMU
+ uint64_t sfsr;
+ uint64_t sfar;
+ uint64_t tsb;
+ uint64_t tag_access;
+ } immu;
+ };
+ union {
+ uint64_t dmmuregs[16];
+ struct {
+ uint64_t tsb_tag_target;
+ uint64_t mmu_primary_context;
+ uint64_t mmu_secondary_context;
+ uint64_t sfsr;
+ uint64_t sfar;
+ uint64_t tsb;
+ uint64_t tag_access;
+ } dmmu;
+ };
+ SparcTLBEntry itlb[64];
+ SparcTLBEntry dtlb[64];
+ uint32_t mmu_version;
+#else
+ uint32_t mmuregs[32];
+ uint64_t mxccdata[4];
+ uint64_t mxccregs[8];
+ uint64_t mmubpregs[4];
+ uint64_t prom_addr;
+#endif
+ /* temporary float registers */
+ float64 dt0, dt1;
+ float128 qt0, qt1;
+ float_status fp_status;
+#if defined(TARGET_SPARC64)
+#define MAXTL_MAX 8
+#define MAXTL_MASK (MAXTL_MAX - 1)
+ trap_state ts[MAXTL_MAX];
+ uint32_t xcc; /* Extended integer condition codes */
+ uint32_t asi;
+ uint32_t pstate;
+ uint32_t tl;
+ uint32_t maxtl;
+ uint32_t cansave, canrestore, otherwin, wstate, cleanwin;
+ uint64_t agregs[8]; /* alternate general registers */
+ uint64_t bgregs[8]; /* backup for normal global registers */
+ uint64_t igregs[8]; /* interrupt general registers */
+ uint64_t mgregs[8]; /* mmu general registers */
+ uint64_t fprs;
+ uint64_t tick_cmpr, stick_cmpr;
+ CPUTimer *tick, *stick;
+#define TICK_NPT_MASK 0x8000000000000000ULL
+#define TICK_INT_DIS 0x8000000000000000ULL
+ uint64_t gsr;
+ uint32_t gl; // UA2005
+ /* UA 2005 hyperprivileged registers */
+ uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
+ CPUTimer *hstick; // UA 2005
+ uint32_t softint;
+#define SOFTINT_TIMER 1
+#define SOFTINT_STIMER (1 << 16)
+#define SOFTINT_INTRMASK (0xFFFE)
+#define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER)
+#endif
+ sparc_def_t *def;
+
+ void *irq_manager;
+ void (*qemu_irq_ack) (void *irq_manager, int intno);
+
+ /* Leon3 cache control */
+ uint32_t cache_control;
+} CPUSPARCState;
+
+#ifndef NO_CPU_IO_DEFS
+/* helper.c */
+CPUSPARCState *cpu_sparc_init(const char *cpu_model);
+void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu);
+void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+int cpu_sparc_handle_mmu_fault(CPUSPARCState *env1, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_sparc_handle_mmu_fault
+target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev);
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env);
+
+/* translate.c */
+void gen_intermediate_code_init(CPUSPARCState *env);
+
+/* cpu-exec.c */
+int cpu_sparc_exec(CPUSPARCState *s);
+
+/* op_helper.c */
+target_ulong cpu_get_psr(CPUState *env1);
+void cpu_put_psr(CPUState *env1, target_ulong val);
+#ifdef TARGET_SPARC64
+target_ulong cpu_get_ccr(CPUState *env1);
+void cpu_put_ccr(CPUState *env1, target_ulong val);
+target_ulong cpu_get_cwp64(CPUState *env1);
+void cpu_put_cwp64(CPUState *env1, int cwp);
+#endif
+int cpu_cwp_inc(CPUState *env1, int cwp);
+int cpu_cwp_dec(CPUState *env1, int cwp);
+void cpu_set_cwp(CPUState *env1, int new_cwp);
+void leon3_irq_manager(void *irq_manager, int intno);
+
+/* sun4m.c, sun4u.c */
+void cpu_check_irqs(CPUSPARCState *env);
+
+/* leon3.c */
+void leon3_irq_ack(void *irq_manager, int intno);
+
+#if defined (TARGET_SPARC64)
+
+static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
+{
+ return (x & mask) == (y & mask);
+}
+
+#define MMU_CONTEXT_BITS 13
+#define MMU_CONTEXT_MASK ((1 << MMU_CONTEXT_BITS) - 1)
+
+static inline int tlb_compare_context(const SparcTLBEntry *tlb,
+ uint64_t context)
+{
+ return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK);
+}
+
+#endif
+#endif
+
+/* cpu-exec.c */
+#if !defined(CONFIG_USER_ONLY)
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int is_asi, int size);
+target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
+ int mmu_idx);
+
+#endif
+int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
+
+#define cpu_init cpu_sparc_init
+#define cpu_exec cpu_sparc_exec
+#define cpu_gen_code cpu_sparc_gen_code
+#define cpu_signal_handler cpu_sparc_signal_handler
+#define cpu_list sparc_cpu_list
+
+#define CPU_SAVE_VERSION 6
+
+/* MMU modes definitions */
+#if defined (TARGET_SPARC64)
+#define MMU_USER_IDX 0
+#define MMU_MODE0_SUFFIX _user
+#define MMU_USER_SECONDARY_IDX 1
+#define MMU_MODE1_SUFFIX _user_secondary
+#define MMU_KERNEL_IDX 2
+#define MMU_MODE2_SUFFIX _kernel
+#define MMU_KERNEL_SECONDARY_IDX 3
+#define MMU_MODE3_SUFFIX _kernel_secondary
+#define MMU_NUCLEUS_IDX 4
+#define MMU_MODE4_SUFFIX _nucleus
+#define MMU_HYPV_IDX 5
+#define MMU_MODE5_SUFFIX _hypv
+#else
+#define MMU_USER_IDX 0
+#define MMU_MODE0_SUFFIX _user
+#define MMU_KERNEL_IDX 1
+#define MMU_MODE1_SUFFIX _kernel
+#endif
+
+#if defined (TARGET_SPARC64)
+static inline int cpu_has_hypervisor(CPUState *env1)
+{
+ return env1->def->features & CPU_FEATURE_HYPV;
+}
+
+static inline int cpu_hypervisor_mode(CPUState *env1)
+{
+ return cpu_has_hypervisor(env1) && (env1->hpstate & HS_PRIV);
+}
+
+static inline int cpu_supervisor_mode(CPUState *env1)
+{
+ return env1->pstate & PS_PRIV;
+}
+#endif
+
+static inline int cpu_mmu_index(CPUState *env1)
+{
+#if defined(CONFIG_USER_ONLY)
+ return MMU_USER_IDX;
+#elif !defined(TARGET_SPARC64)
+ return env1->psrs;
+#else
+ if (env1->tl > 0) {
+ return MMU_NUCLEUS_IDX;
+ } else if (cpu_hypervisor_mode(env1)) {
+ return MMU_HYPV_IDX;
+ } else if (cpu_supervisor_mode(env1)) {
+ return MMU_KERNEL_IDX;
+ } else {
+ return MMU_USER_IDX;
+ }
+#endif
+}
+
+static inline int cpu_interrupts_enabled(CPUState *env1)
+{
+#if !defined (TARGET_SPARC64)
+ if (env1->psret != 0)
+ return 1;
+#else
+ if (env1->pstate & PS_IE)
+ return 1;
+#endif
+
+ return 0;
+}
+
+static inline int cpu_pil_allowed(CPUState *env1, int pil)
+{
+#if !defined(TARGET_SPARC64)
+ /* level 15 is non-maskable on sparc v8 */
+ return pil == 15 || pil > env1->psrpil;
+#else
+ return pil > env1->psrpil;
+#endif
+}
+
+static inline int cpu_fpu_enabled(CPUState *env1)
+{
+#if defined(CONFIG_USER_ONLY)
+ return 1;
+#elif !defined(TARGET_SPARC64)
+ return env1->psref;
+#else
+ return ((env1->pstate & PS_PEF) != 0) && ((env1->fprs & FPRS_FEF) != 0);
+#endif
+}
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+ if (newsp)
+ env->regwptr[22] = newsp;
+ env->regwptr[0] = 0;
+ /* FIXME: Do we also need to clear CF? */
+ /* XXXXX */
+ printf ("HELPME: %s:%d\n", __FILE__, __LINE__);
+}
+#endif
+
+#include "cpu-all.h"
+
+#ifdef TARGET_SPARC64
+/* sun4u.c */
+void cpu_tick_set_count(CPUTimer *timer, uint64_t count);
+uint64_t cpu_tick_get_count(CPUTimer *timer);
+void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit);
+trap_state* cpu_tsptr(CPUState* env);
+#endif
+
+static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->pc;
+ *cs_base = env->npc;
+#ifdef TARGET_SPARC64
+ // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled
+ *flags = ((env->pstate & PS_AM) << 2) /* 5 */
+ | (((env->pstate & PS_PEF) >> 1) /* 3 */
+ | ((env->fprs & FPRS_FEF) << 2)) /* 4 */
+ | (env->pstate & PS_PRIV) /* 2 */
+ | ((env->lsu & (DMMU_E | IMMU_E)) >> 2) /* 1, 0 */
+ | ((env->tl & 0xff) << 8)
+ | (env->dmmu.mmu_primary_context << 16); /* 16... */
+#else
+ // FPU enable . Supervisor
+ *flags = (env->psref << 4) | env->psrs;
+#endif
+}
+
+#endif
diff --git a/target-sparc/exec.h b/target-sparc/exec.h
new file mode 100644
index 0000000..f811571
--- /dev/null
+++ b/target-sparc/exec.h
@@ -0,0 +1,41 @@
+#ifndef EXEC_SPARC_H
+#define EXEC_SPARC_H 1
+#include "config.h"
+#include "dyngen-exec.h"
+
+register struct CPUSPARCState *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+/* op_helper.c */
+void do_interrupt(CPUState *env);
+
+static inline int cpu_has_work(CPUState *env1)
+{
+ return (env1->interrupt_request & CPU_INTERRUPT_HARD) &&
+ cpu_interrupts_enabled(env1);
+}
+
+
+static inline int cpu_halted(CPUState *env1) {
+ if (!env1->halted)
+ return 0;
+ if (cpu_has_work(env1)) {
+ env1->halted = 0;
+ return 0;
+ }
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->pc = tb->pc;
+ env->npc = tb->cs_base;
+}
+
+#endif
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
new file mode 100644
index 0000000..b2d4d70
--- /dev/null
+++ b/target-sparc/helper.c
@@ -0,0 +1,1546 @@
+/*
+ * sparc helpers
+ *
+ * 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "qemu-common.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_FEATURES
+
+#ifdef DEBUG_MMU
+#define DPRINTF_MMU(fmt, ...) \
+ do { printf("MMU: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_MMU(fmt, ...) do {} while (0)
+#endif
+
+static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model);
+
+/* Sparc MMU emulation */
+
+#if defined(CONFIG_USER_ONLY)
+
+int cpu_sparc_handle_mmu_fault(CPUState *env1, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ if (rw & 2)
+ env1->exception_index = TT_TFAULT;
+ else
+ env1->exception_index = TT_DFAULT;
+ return 1;
+}
+
+#else
+
+#ifndef TARGET_SPARC64
+/*
+ * Sparc V8 Reference MMU (SRMMU)
+ */
+static const int access_table[8][8] = {
+ { 0, 0, 0, 0, 8, 0, 12, 12 },
+ { 0, 0, 0, 0, 8, 0, 0, 0 },
+ { 8, 8, 0, 0, 0, 8, 12, 12 },
+ { 8, 8, 0, 0, 0, 8, 0, 0 },
+ { 8, 0, 8, 0, 8, 8, 12, 12 },
+ { 8, 0, 8, 0, 8, 0, 8, 0 },
+ { 8, 8, 8, 0, 8, 8, 12, 12 },
+ { 8, 8, 8, 0, 8, 8, 8, 0 }
+};
+
+static const int perm_table[2][8] = {
+ {
+ PAGE_READ,
+ PAGE_READ | PAGE_WRITE,
+ PAGE_READ | PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC,
+ PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE,
+ PAGE_READ | PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC
+ },
+ {
+ PAGE_READ,
+ PAGE_READ | PAGE_WRITE,
+ PAGE_READ | PAGE_EXEC,
+ PAGE_READ | PAGE_WRITE | PAGE_EXEC,
+ PAGE_EXEC,
+ PAGE_READ,
+ 0,
+ 0,
+ }
+};
+
+static int get_physical_address(CPUState *env, target_phys_addr_t *physical,
+ int *prot, int *access_index,
+ target_ulong address, int rw, int mmu_idx,
+ target_ulong *page_size)
+{
+ int access_perms = 0;
+ target_phys_addr_t pde_ptr;
+ uint32_t pde;
+ int error_code = 0, is_dirty, is_user;
+ unsigned long page_offset;
+
+ is_user = mmu_idx == MMU_USER_IDX;
+
+ if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
+ *page_size = TARGET_PAGE_SIZE;
+ // Boot mode: instruction fetches are taken from PROM
+ if (rw == 2 && (env->mmuregs[0] & env->def->mmu_bm)) {
+ *physical = env->prom_addr | (address & 0x7ffffULL);
+ *prot = PAGE_READ | PAGE_EXEC;
+ return 0;
+ }
+ *physical = address;
+ *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ return 0;
+ }
+
+ *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
+ *physical = 0xffffffffffff0000ULL;
+
+ /* SPARC reference MMU table walk: Context table->L1->L2->PTE */
+ /* Context base + context number */
+ pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
+ pde = ldl_phys(pde_ptr);
+
+ /* Ctx pde */
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return 1 << 2;
+ case 2: /* L0 PTE, maybe should not happen? */
+ case 3: /* Reserved */
+ return 4 << 2;
+ case 1: /* L0 PDE */
+ pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return (1 << 8) | (1 << 2);
+ case 3: /* Reserved */
+ return (1 << 8) | (4 << 2);
+ case 1: /* L1 PDE */
+ pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return (2 << 8) | (1 << 2);
+ case 3: /* Reserved */
+ return (2 << 8) | (4 << 2);
+ case 1: /* L2 PDE */
+ pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ return (3 << 8) | (1 << 2);
+ case 1: /* PDE, should not happen */
+ case 3: /* Reserved */
+ return (3 << 8) | (4 << 2);
+ case 2: /* L3 PTE */
+ page_offset = (address & TARGET_PAGE_MASK) &
+ (TARGET_PAGE_SIZE - 1);
+ }
+ *page_size = TARGET_PAGE_SIZE;
+ break;
+ case 2: /* L2 PTE */
+ page_offset = address & 0x3ffff;
+ *page_size = 0x40000;
+ }
+ break;
+ case 2: /* L1 PTE */
+ page_offset = address & 0xffffff;
+ *page_size = 0x1000000;
+ }
+ }
+
+ /* check access */
+ access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT;
+ error_code = access_table[*access_index][access_perms];
+ if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user))
+ return error_code;
+
+ /* update page modified and dirty bits */
+ is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK);
+ if (!(pde & PG_ACCESSED_MASK) || is_dirty) {
+ pde |= PG_ACCESSED_MASK;
+ if (is_dirty)
+ pde |= PG_MODIFIED_MASK;
+ stl_phys_notdirty(pde_ptr, pde);
+ }
+
+ /* the page can be put in the TLB */
+ *prot = perm_table[is_user][access_perms];
+ if (!(pde & PG_MODIFIED_MASK)) {
+ /* only set write access if already dirty... otherwise wait
+ for dirty access */
+ *prot &= ~PAGE_WRITE;
+ }
+
+ /* Even if large ptes, we map only one 4KB page in the cache to
+ avoid filling it too fast */
+ *physical = ((target_phys_addr_t)(pde & PTE_ADDR_MASK) << 4) + page_offset;
+ return error_code;
+}
+
+/* Perform address translation */
+int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ target_phys_addr_t paddr;
+ target_ulong vaddr;
+ target_ulong page_size;
+ int error_code = 0, prot, access_index;
+
+ error_code = get_physical_address(env, &paddr, &prot, &access_index,
+ address, rw, mmu_idx, &page_size);
+ if (error_code == 0) {
+ vaddr = address & TARGET_PAGE_MASK;
+ paddr &= TARGET_PAGE_MASK;
+#ifdef DEBUG_MMU
+ printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr "
+ TARGET_FMT_lx "\n", address, paddr, vaddr);
+#endif
+ tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size);
+ return 0;
+ }
+
+ if (env->mmuregs[3]) /* Fault status register */
+ env->mmuregs[3] = 1; /* overflow (not read before another fault) */
+ env->mmuregs[3] |= (access_index << 5) | error_code | 2;
+ env->mmuregs[4] = address; /* Fault address register */
+
+ if ((env->mmuregs[0] & MMU_NF) || env->psret == 0) {
+ // No fault mode: if a mapping is available, just override
+ // permissions. If no mapping is available, redirect accesses to
+ // neverland. Fake/overridden mappings will be flushed when
+ // switching to normal mode.
+ vaddr = address & TARGET_PAGE_MASK;
+ prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+ tlb_set_page(env, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE);
+ return 0;
+ } else {
+ if (rw & 2)
+ env->exception_index = TT_TFAULT;
+ else
+ env->exception_index = TT_DFAULT;
+ return 1;
+ }
+}
+
+target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
+{
+ target_phys_addr_t pde_ptr;
+ uint32_t pde;
+
+ /* Context base + context number */
+ pde_ptr = (target_phys_addr_t)(env->mmuregs[1] << 4) +
+ (env->mmuregs[2] << 2);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 2: /* PTE, maybe should not happen? */
+ case 3: /* Reserved */
+ return 0;
+ case 1: /* L1 PDE */
+ if (mmulev == 3)
+ return pde;
+ pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 3: /* Reserved */
+ return 0;
+ case 2: /* L1 PTE */
+ return pde;
+ case 1: /* L2 PDE */
+ if (mmulev == 2)
+ return pde;
+ pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 3: /* Reserved */
+ return 0;
+ case 2: /* L2 PTE */
+ return pde;
+ case 1: /* L3 PDE */
+ if (mmulev == 1)
+ return pde;
+ pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
+ pde = ldl_phys(pde_ptr);
+
+ switch (pde & PTE_ENTRYTYPE_MASK) {
+ default:
+ case 0: /* Invalid */
+ case 1: /* PDE, should not happen */
+ case 3: /* Reserved */
+ return 0;
+ case 2: /* L3 PTE */
+ return pde;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
+{
+ target_ulong va, va1, va2;
+ unsigned int n, m, o;
+ target_phys_addr_t pde_ptr, pa;
+ uint32_t pde;
+
+ pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
+ pde = ldl_phys(pde_ptr);
+ (*cpu_fprintf)(f, "Root ptr: " TARGET_FMT_plx ", ctx: %d\n",
+ (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]);
+ for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) {
+ pde = mmu_probe(env, va, 2);
+ if (pde) {
+ pa = cpu_get_phys_page_debug(env, va);
+ (*cpu_fprintf)(f, "VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx
+ " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
+ for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
+ pde = mmu_probe(env, va1, 1);
+ if (pde) {
+ pa = cpu_get_phys_page_debug(env, va1);
+ (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: "
+ TARGET_FMT_plx " PDE: " TARGET_FMT_lx "\n",
+ va1, pa, pde);
+ for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
+ pde = mmu_probe(env, va2, 0);
+ if (pde) {
+ pa = cpu_get_phys_page_debug(env, va2);
+ (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: "
+ TARGET_FMT_plx " PTE: "
+ TARGET_FMT_lx "\n",
+ va2, pa, pde);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#else /* !TARGET_SPARC64 */
+
+// 41 bit physical address space
+static inline target_phys_addr_t ultrasparc_truncate_physical(uint64_t x)
+{
+ return x & 0x1ffffffffffULL;
+}
+
+/*
+ * UltraSparc IIi I/DMMUs
+ */
+
+// Returns true if TTE tag is valid and matches virtual address value in context
+// requires virtual address mask value calculated from TTE entry size
+static inline int ultrasparc_tag_match(SparcTLBEntry *tlb,
+ uint64_t address, uint64_t context,
+ target_phys_addr_t *physical)
+{
+ uint64_t mask;
+
+ switch ((tlb->tte >> 61) & 3) {
+ default:
+ case 0x0: // 8k
+ mask = 0xffffffffffffe000ULL;
+ break;
+ case 0x1: // 64k
+ mask = 0xffffffffffff0000ULL;
+ break;
+ case 0x2: // 512k
+ mask = 0xfffffffffff80000ULL;
+ break;
+ case 0x3: // 4M
+ mask = 0xffffffffffc00000ULL;
+ break;
+ }
+
+ // valid, context match, virtual address match?
+ if (TTE_IS_VALID(tlb->tte) &&
+ (TTE_IS_GLOBAL(tlb->tte) || tlb_compare_context(tlb, context))
+ && compare_masked(address, tlb->tag, mask))
+ {
+ // decode physical address
+ *physical = ((tlb->tte & mask) | (address & ~mask)) & 0x1ffffffe000ULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_physical_address_data(CPUState *env,
+ target_phys_addr_t *physical, int *prot,
+ target_ulong address, int rw, int mmu_idx)
+{
+ unsigned int i;
+ uint64_t context;
+
+ int is_user = (mmu_idx == MMU_USER_IDX ||
+ mmu_idx == MMU_USER_SECONDARY_IDX);
+
+ if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
+ *physical = ultrasparc_truncate_physical(address);
+ *prot = PAGE_READ | PAGE_WRITE;
+ return 0;
+ }
+
+ switch(mmu_idx) {
+ case MMU_USER_IDX:
+ case MMU_KERNEL_IDX:
+ context = env->dmmu.mmu_primary_context & 0x1fff;
+ break;
+ case MMU_USER_SECONDARY_IDX:
+ case MMU_KERNEL_SECONDARY_IDX:
+ context = env->dmmu.mmu_secondary_context & 0x1fff;
+ break;
+ case MMU_NUCLEUS_IDX:
+ default:
+ context = 0;
+ break;
+ }
+
+ for (i = 0; i < 64; i++) {
+ // ctx match, vaddr match, valid?
+ if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) {
+
+ uint8_t fault_type = 0;
+
+ // access ok?
+ if ((env->dtlb[i].tte & 0x4) && is_user) {
+ fault_type |= 1; /* privilege violation */
+ env->exception_index = TT_DFAULT;
+
+ DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64
+ " mmu_idx=%d tl=%d\n",
+ address, context, mmu_idx, env->tl);
+ } else if (!(env->dtlb[i].tte & 0x2) && (rw == 1)) {
+ env->exception_index = TT_DPROT;
+
+ DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64
+ " mmu_idx=%d tl=%d\n",
+ address, context, mmu_idx, env->tl);
+ } else {
+ *prot = PAGE_READ;
+ if (env->dtlb[i].tte & 0x2)
+ *prot |= PAGE_WRITE;
+
+ TTE_SET_USED(env->dtlb[i].tte);
+
+ return 0;
+ }
+
+ if (env->dmmu.sfsr & 1) /* Fault status register */
+ env->dmmu.sfsr = 2; /* overflow (not read before
+ another fault) */
+
+ env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1;
+
+ env->dmmu.sfsr |= (fault_type << 7);
+
+ env->dmmu.sfar = address; /* Fault address register */
+
+ env->dmmu.tag_access = (address & ~0x1fffULL) | context;
+
+ return 1;
+ }
+ }
+
+ DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n",
+ address, context);
+
+ env->dmmu.tag_access = (address & ~0x1fffULL) | context;
+ env->exception_index = TT_DMISS;
+ return 1;
+}
+
+static int get_physical_address_code(CPUState *env,
+ target_phys_addr_t *physical, int *prot,
+ target_ulong address, int mmu_idx)
+{
+ unsigned int i;
+ uint64_t context;
+
+ int is_user = (mmu_idx == MMU_USER_IDX ||
+ mmu_idx == MMU_USER_SECONDARY_IDX);
+
+ if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) {
+ /* IMMU disabled */
+ *physical = ultrasparc_truncate_physical(address);
+ *prot = PAGE_EXEC;
+ return 0;
+ }
+
+ if (env->tl == 0) {
+ /* PRIMARY context */
+ context = env->dmmu.mmu_primary_context & 0x1fff;
+ } else {
+ /* NUCLEUS context */
+ context = 0;
+ }
+
+ for (i = 0; i < 64; i++) {
+ // ctx match, vaddr match, valid?
+ if (ultrasparc_tag_match(&env->itlb[i],
+ address, context, physical)) {
+ // access ok?
+ if ((env->itlb[i].tte & 0x4) && is_user) {
+ if (env->immu.sfsr) /* Fault status register */
+ env->immu.sfsr = 2; /* overflow (not read before
+ another fault) */
+ env->immu.sfsr |= (is_user << 3) | 1;
+ env->exception_index = TT_TFAULT;
+
+ env->immu.tag_access = (address & ~0x1fffULL) | context;
+
+ DPRINTF_MMU("TFAULT at %" PRIx64 " context %" PRIx64 "\n",
+ address, context);
+
+ return 1;
+ }
+ *prot = PAGE_EXEC;
+ TTE_SET_USED(env->itlb[i].tte);
+ return 0;
+ }
+ }
+
+ DPRINTF_MMU("TMISS at %" PRIx64 " context %" PRIx64 "\n",
+ address, context);
+
+ /* Context is stored in DMMU (dmmuregs[1]) also for IMMU */
+ env->immu.tag_access = (address & ~0x1fffULL) | context;
+ env->exception_index = TT_TMISS;
+ return 1;
+}
+
+static int get_physical_address(CPUState *env, target_phys_addr_t *physical,
+ int *prot, int *access_index,
+ target_ulong address, int rw, int mmu_idx,
+ target_ulong *page_size)
+{
+ /* ??? We treat everything as a small page, then explicitly flush
+ everything when an entry is evicted. */
+ *page_size = TARGET_PAGE_SIZE;
+
+#if defined (DEBUG_MMU)
+ /* safety net to catch wrong softmmu index use from dynamic code */
+ if (env->tl > 0 && mmu_idx != MMU_NUCLEUS_IDX) {
+ DPRINTF_MMU("get_physical_address %s tl=%d mmu_idx=%d"
+ " primary context=%" PRIx64
+ " secondary context=%" PRIx64
+ " address=%" PRIx64
+ "\n",
+ (rw == 2 ? "CODE" : "DATA"),
+ env->tl, mmu_idx,
+ env->dmmu.mmu_primary_context,
+ env->dmmu.mmu_secondary_context,
+ address);
+ }
+#endif
+
+ if (rw == 2)
+ return get_physical_address_code(env, physical, prot, address,
+ mmu_idx);
+ else
+ return get_physical_address_data(env, physical, prot, address, rw,
+ mmu_idx);
+}
+
+/* Perform address translation */
+int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+ int mmu_idx, int is_softmmu)
+{
+ target_ulong virt_addr, vaddr;
+ target_phys_addr_t paddr;
+ target_ulong page_size;
+ int error_code = 0, prot, access_index;
+
+ error_code = get_physical_address(env, &paddr, &prot, &access_index,
+ address, rw, mmu_idx, &page_size);
+ if (error_code == 0) {
+ virt_addr = address & TARGET_PAGE_MASK;
+ vaddr = virt_addr + ((address & TARGET_PAGE_MASK) &
+ (TARGET_PAGE_SIZE - 1));
+
+ DPRINTF_MMU("Translate at %" PRIx64 " -> %" PRIx64 ","
+ " vaddr %" PRIx64
+ " mmu_idx=%d"
+ " tl=%d"
+ " primary context=%" PRIx64
+ " secondary context=%" PRIx64
+ "\n",
+ address, paddr, vaddr, mmu_idx, env->tl,
+ env->dmmu.mmu_primary_context,
+ env->dmmu.mmu_secondary_context);
+
+ tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size);
+ return 0;
+ }
+ // XXX
+ return 1;
+}
+
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env)
+{
+ unsigned int i;
+ const char *mask;
+
+ (*cpu_fprintf)(f, "MMU contexts: Primary: %" PRId64 ", Secondary: %"
+ PRId64 "\n",
+ env->dmmu.mmu_primary_context,
+ env->dmmu.mmu_secondary_context);
+ if ((env->lsu & DMMU_E) == 0) {
+ (*cpu_fprintf)(f, "DMMU disabled\n");
+ } else {
+ (*cpu_fprintf)(f, "DMMU dump\n");
+ for (i = 0; i < 64; i++) {
+ switch ((env->dtlb[i].tte >> 61) & 3) {
+ default:
+ case 0x0:
+ mask = " 8k";
+ break;
+ case 0x1:
+ mask = " 64k";
+ break;
+ case 0x2:
+ mask = "512k";
+ break;
+ case 0x3:
+ mask = " 4M";
+ break;
+ }
+ if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) {
+ (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64
+ ", %s, %s, %s, %s, ctx %" PRId64 " %s\n",
+ i,
+ env->dtlb[i].tag & (uint64_t)~0x1fffULL,
+ env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL,
+ mask,
+ env->dtlb[i].tte & 0x4? "priv": "user",
+ env->dtlb[i].tte & 0x2? "RW": "RO",
+ env->dtlb[i].tte & 0x40? "locked": "unlocked",
+ env->dtlb[i].tag & (uint64_t)0x1fffULL,
+ TTE_IS_GLOBAL(env->dtlb[i].tte)?
+ "global" : "local");
+ }
+ }
+ }
+ if ((env->lsu & IMMU_E) == 0) {
+ (*cpu_fprintf)(f, "IMMU disabled\n");
+ } else {
+ (*cpu_fprintf)(f, "IMMU dump\n");
+ for (i = 0; i < 64; i++) {
+ switch ((env->itlb[i].tte >> 61) & 3) {
+ default:
+ case 0x0:
+ mask = " 8k";
+ break;
+ case 0x1:
+ mask = " 64k";
+ break;
+ case 0x2:
+ mask = "512k";
+ break;
+ case 0x3:
+ mask = " 4M";
+ break;
+ }
+ if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) {
+ (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64
+ ", %s, %s, %s, ctx %" PRId64 " %s\n",
+ i,
+ env->itlb[i].tag & (uint64_t)~0x1fffULL,
+ env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL,
+ mask,
+ env->itlb[i].tte & 0x4? "priv": "user",
+ env->itlb[i].tte & 0x40? "locked": "unlocked",
+ env->itlb[i].tag & (uint64_t)0x1fffULL,
+ TTE_IS_GLOBAL(env->itlb[i].tte)?
+ "global" : "local");
+ }
+ }
+ }
+}
+
+#endif /* TARGET_SPARC64 */
+#endif /* !CONFIG_USER_ONLY */
+
+
+#if !defined(CONFIG_USER_ONLY)
+target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr,
+ int mmu_idx)
+{
+ target_phys_addr_t phys_addr;
+ target_ulong page_size;
+ int prot, access_index;
+
+ if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2,
+ mmu_idx, &page_size) != 0)
+ if (get_physical_address(env, &phys_addr, &prot, &access_index, addr,
+ 0, mmu_idx, &page_size) != 0)
+ return -1;
+ if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
+ return -1;
+ return phys_addr;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return cpu_get_phys_page_nofault(env, addr, cpu_mmu_index(env));
+}
+#endif
+
+void cpu_reset(CPUSPARCState *env)
+{
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ tlb_flush(env, 1);
+ env->cwp = 0;
+#ifndef TARGET_SPARC64
+ env->wim = 1;
+#endif
+ env->regwptr = env->regbase + (env->cwp * 16);
+ CC_OP = CC_OP_FLAGS;
+#if defined(CONFIG_USER_ONLY)
+#ifdef TARGET_SPARC64
+ env->cleanwin = env->nwindows - 2;
+ env->cansave = env->nwindows - 2;
+ env->pstate = PS_RMO | PS_PEF | PS_IE;
+ env->asi = 0x82; // Primary no-fault
+#endif
+#else
+#if !defined(TARGET_SPARC64)
+ env->psret = 0;
+ env->psrs = 1;
+ env->psrps = 1;
+#endif
+#ifdef TARGET_SPARC64
+ env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG;
+ env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0;
+ env->tl = env->maxtl;
+ cpu_tsptr(env)->tt = TT_POWER_ON_RESET;
+ env->lsu = 0;
+#else
+ env->mmuregs[0] &= ~(MMU_E | MMU_NF);
+ env->mmuregs[0] |= env->def->mmu_bm;
+#endif
+ env->pc = 0;
+ env->npc = env->pc + 4;
+#endif
+ env->cache_control = 0;
+}
+
+static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model)
+{
+ sparc_def_t def1, *def = &def1;
+
+ if (cpu_sparc_find_by_name(def, cpu_model) < 0)
+ return -1;
+
+ env->def = qemu_mallocz(sizeof(*def));
+ memcpy(env->def, def, sizeof(*def));
+#if defined(CONFIG_USER_ONLY)
+ if ((env->def->features & CPU_FEATURE_FLOAT))
+ env->def->features |= CPU_FEATURE_FLOAT128;
+#endif
+ env->cpu_model_str = cpu_model;
+ env->version = def->iu_version;
+ env->fsr = def->fpu_version;
+ env->nwindows = def->nwindows;
+#if !defined(TARGET_SPARC64)
+ env->mmuregs[0] |= def->mmu_version;
+ cpu_sparc_set_id(env, 0);
+ env->mxccregs[7] |= def->mxcc_version;
+#else
+ env->mmu_version = def->mmu_version;
+ env->maxtl = def->maxtl;
+ env->version |= def->maxtl << 8;
+ env->version |= def->nwindows - 1;
+#endif
+ return 0;
+}
+
+static void cpu_sparc_close(CPUSPARCState *env)
+{
+ free(env->def);
+ free(env);
+}
+
+CPUSPARCState *cpu_sparc_init(const char *cpu_model)
+{
+ CPUSPARCState *env;
+
+ env = qemu_mallocz(sizeof(CPUSPARCState));
+ cpu_exec_init(env);
+
+ gen_intermediate_code_init(env);
+
+ if (cpu_sparc_register(env, cpu_model) < 0) {
+ cpu_sparc_close(env);
+ return NULL;
+ }
+ qemu_init_vcpu(env);
+
+ return env;
+}
+
+void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu)
+{
+#if !defined(TARGET_SPARC64)
+ env->mxccregs[7] = ((cpu + 8) & 0xf) << 24;
+#endif
+}
+
+static const sparc_def_t sparc_defs[] = {
+#ifdef TARGET_SPARC64
+ {
+ .name = "Fujitsu Sparc64",
+ .iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 4,
+ .maxtl = 4,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Fujitsu Sparc64 III",
+ .iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 5,
+ .maxtl = 4,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Fujitsu Sparc64 IV",
+ .iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Fujitsu Sparc64 V",
+ .iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI UltraSparc I",
+ .iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI UltraSparc II",
+ .iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI UltraSparc IIi",
+ .iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI UltraSparc IIe",
+ .iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Sun UltraSparc III",
+ .iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Sun UltraSparc III Cu",
+ .iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_3,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Sun UltraSparc IIIi",
+ .iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Sun UltraSparc IV",
+ .iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_4,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Sun UltraSparc IV+",
+ .iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT,
+ },
+ {
+ .name = "Sun UltraSparc IIIi+",
+ .iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_3,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Sun UltraSparc T1",
+ // defined in sparc_ifu_fdp.v and ctu.h
+ .iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_sun4v,
+ .nwindows = 8,
+ .maxtl = 6,
+ .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT
+ | CPU_FEATURE_GL,
+ },
+ {
+ .name = "Sun UltraSparc T2",
+ // defined in tlu_asi_ctl.v and n2_revid_cust.v
+ .iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_sun4v,
+ .nwindows = 8,
+ .maxtl = 6,
+ .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT
+ | CPU_FEATURE_GL,
+ },
+ {
+ .name = "NEC UltraSparc I",
+ .iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
+ .fpu_version = 0x00000000,
+ .mmu_version = mmu_us_12,
+ .nwindows = 8,
+ .maxtl = 5,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+#else
+ {
+ .name = "Fujitsu MB86900",
+ .iu_version = 0x00 << 24, /* Impl 0, ver 0 */
+ .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
+ .mmu_version = 0x00 << 24, /* Impl 0, ver 0 */
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 7,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_FSMULD,
+ },
+ {
+ .name = "Fujitsu MB86904",
+ .iu_version = 0x04 << 24, /* Impl 0, ver 4 */
+ .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
+ .mmu_version = 0x04 << 24, /* Impl 0, ver 4 */
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x00ffffc0,
+ .mmu_cxr_mask = 0x000000ff,
+ .mmu_sfsr_mask = 0x00016fff,
+ .mmu_trcr_mask = 0x00ffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Fujitsu MB86907",
+ .iu_version = 0x05 << 24, /* Impl 0, ver 5 */
+ .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
+ .mmu_version = 0x05 << 24, /* Impl 0, ver 5 */
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x000000ff,
+ .mmu_sfsr_mask = 0x00016fff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "LSI L64811",
+ .iu_version = 0x10 << 24, /* Impl 1, ver 0 */
+ .fpu_version = 1 << 17, /* FPU version 1 (LSI L64814) */
+ .mmu_version = 0x10 << 24,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
+ CPU_FEATURE_FSMULD,
+ },
+ {
+ .name = "Cypress CY7C601",
+ .iu_version = 0x11 << 24, /* Impl 1, ver 1 */
+ .fpu_version = 3 << 17, /* FPU version 3 (Cypress CY7C602) */
+ .mmu_version = 0x10 << 24,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
+ CPU_FEATURE_FSMULD,
+ },
+ {
+ .name = "Cypress CY7C611",
+ .iu_version = 0x13 << 24, /* Impl 1, ver 3 */
+ .fpu_version = 3 << 17, /* FPU version 3 (Cypress CY7C602) */
+ .mmu_version = 0x10 << 24,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
+ CPU_FEATURE_FSMULD,
+ },
+ {
+ .name = "TI MicroSparc I",
+ .iu_version = 0x41000000,
+ .fpu_version = 4 << 17,
+ .mmu_version = 0x41000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0x00016fff,
+ .mmu_trcr_mask = 0x0000003f,
+ .nwindows = 7,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_MUL |
+ CPU_FEATURE_DIV | CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT |
+ CPU_FEATURE_FMUL,
+ },
+ {
+ .name = "TI MicroSparc II",
+ .iu_version = 0x42000000,
+ .fpu_version = 4 << 17,
+ .mmu_version = 0x02000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x00ffffc0,
+ .mmu_cxr_mask = 0x000000ff,
+ .mmu_sfsr_mask = 0x00016fff,
+ .mmu_trcr_mask = 0x00ffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI MicroSparc IIep",
+ .iu_version = 0x42000000,
+ .fpu_version = 4 << 17,
+ .mmu_version = 0x04000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x00ffffc0,
+ .mmu_cxr_mask = 0x000000ff,
+ .mmu_sfsr_mask = 0x00016bff,
+ .mmu_trcr_mask = 0x00ffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI SuperSparc 40", // STP1020NPGA
+ .iu_version = 0x41000000, // SuperSPARC 2.x
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x00000800, // SuperSPARC 2.x, no MXCC
+ .mmu_bm = 0x00002000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x0000ffff,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI SuperSparc 50", // STP1020PGA
+ .iu_version = 0x40000000, // SuperSPARC 3.x
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x01000800, // SuperSPARC 3.x, no MXCC
+ .mmu_bm = 0x00002000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x0000ffff,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI SuperSparc 51",
+ .iu_version = 0x40000000, // SuperSPARC 3.x
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x01000000, // SuperSPARC 3.x, MXCC
+ .mmu_bm = 0x00002000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x0000ffff,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .mxcc_version = 0x00000104,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI SuperSparc 60", // STP1020APGA
+ .iu_version = 0x40000000, // SuperSPARC 3.x
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x01000800, // SuperSPARC 3.x, no MXCC
+ .mmu_bm = 0x00002000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x0000ffff,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI SuperSparc 61",
+ .iu_version = 0x44000000, // SuperSPARC 3.x
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x01000000, // SuperSPARC 3.x, MXCC
+ .mmu_bm = 0x00002000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x0000ffff,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .mxcc_version = 0x00000104,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "TI SuperSparc II",
+ .iu_version = 0x40000000, // SuperSPARC II 1.x
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x08000000, // SuperSPARC II 1.x, MXCC
+ .mmu_bm = 0x00002000,
+ .mmu_ctpr_mask = 0xffffffc0,
+ .mmu_cxr_mask = 0x0000ffff,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .mxcc_version = 0x00000104,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Ross RT625",
+ .iu_version = 0x1e000000,
+ .fpu_version = 1 << 17,
+ .mmu_version = 0x1e000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "Ross RT620",
+ .iu_version = 0x1f000000,
+ .fpu_version = 1 << 17,
+ .mmu_version = 0x1f000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "BIT B5010",
+ .iu_version = 0x20000000,
+ .fpu_version = 0 << 17, /* B5010/B5110/B5120/B5210 */
+ .mmu_version = 0x20000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
+ CPU_FEATURE_FSMULD,
+ },
+ {
+ .name = "Matsushita MN10501",
+ .iu_version = 0x50000000,
+ .fpu_version = 0 << 17,
+ .mmu_version = 0x50000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_FEATURE_FLOAT | CPU_FEATURE_MUL | CPU_FEATURE_FSQRT |
+ CPU_FEATURE_FSMULD,
+ },
+ {
+ .name = "Weitek W8601",
+ .iu_version = 0x90 << 24, /* Impl 9, ver 0 */
+ .fpu_version = 3 << 17, /* FPU version 3 (Weitek WTL3170/2) */
+ .mmu_version = 0x10 << 24,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES,
+ },
+ {
+ .name = "LEON2",
+ .iu_version = 0xf2000000,
+ .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
+ .mmu_version = 0xf2000000,
+ .mmu_bm = 0x00004000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN,
+ },
+ {
+ .name = "LEON3",
+ .iu_version = 0xf3000000,
+ .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
+ .mmu_version = 0xf3000000,
+ .mmu_bm = 0x00000000,
+ .mmu_ctpr_mask = 0x007ffff0,
+ .mmu_cxr_mask = 0x0000003f,
+ .mmu_sfsr_mask = 0xffffffff,
+ .mmu_trcr_mask = 0xffffffff,
+ .nwindows = 8,
+ .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN |
+ CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL,
+ },
+#endif
+};
+
+static const char * const feature_name[] = {
+ "float",
+ "float128",
+ "swap",
+ "mul",
+ "div",
+ "flush",
+ "fsqrt",
+ "fmul",
+ "vis1",
+ "vis2",
+ "fsmuld",
+ "hypv",
+ "cmt",
+ "gl",
+};
+
+static void print_features(FILE *f, fprintf_function cpu_fprintf,
+ uint32_t features, const char *prefix)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(feature_name); i++)
+ if (feature_name[i] && (features & (1 << i))) {
+ if (prefix)
+ (*cpu_fprintf)(f, "%s", prefix);
+ (*cpu_fprintf)(f, "%s ", feature_name[i]);
+ }
+}
+
+static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(feature_name); i++)
+ if (feature_name[i] && !strcmp(flagname, feature_name[i])) {
+ *features |= 1 << i;
+ return;
+ }
+ fprintf(stderr, "CPU feature %s not found\n", flagname);
+}
+
+static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model)
+{
+ unsigned int i;
+ const sparc_def_t *def = NULL;
+ char *s = strdup(cpu_model);
+ char *featurestr, *name = strtok(s, ",");
+ uint32_t plus_features = 0;
+ uint32_t minus_features = 0;
+ uint64_t iu_version;
+ uint32_t fpu_version, mmu_version, nwindows;
+
+ for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) {
+ if (strcasecmp(name, sparc_defs[i].name) == 0) {
+ def = &sparc_defs[i];
+ }
+ }
+ if (!def)
+ goto error;
+ memcpy(cpu_def, def, sizeof(*def));
+
+ featurestr = strtok(NULL, ",");
+ while (featurestr) {
+ char *val;
+
+ if (featurestr[0] == '+') {
+ add_flagname_to_bitmaps(featurestr + 1, &plus_features);
+ } else if (featurestr[0] == '-') {
+ add_flagname_to_bitmaps(featurestr + 1, &minus_features);
+ } else if ((val = strchr(featurestr, '='))) {
+ *val = 0; val++;
+ if (!strcmp(featurestr, "iu_version")) {
+ char *err;
+
+ iu_version = strtoll(val, &err, 0);
+ if (!*val || *err) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ cpu_def->iu_version = iu_version;
+#ifdef DEBUG_FEATURES
+ fprintf(stderr, "iu_version %" PRIx64 "\n", iu_version);
+#endif
+ } else if (!strcmp(featurestr, "fpu_version")) {
+ char *err;
+
+ fpu_version = strtol(val, &err, 0);
+ if (!*val || *err) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ cpu_def->fpu_version = fpu_version;
+#ifdef DEBUG_FEATURES
+ fprintf(stderr, "fpu_version %x\n", fpu_version);
+#endif
+ } else if (!strcmp(featurestr, "mmu_version")) {
+ char *err;
+
+ mmu_version = strtol(val, &err, 0);
+ if (!*val || *err) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ cpu_def->mmu_version = mmu_version;
+#ifdef DEBUG_FEATURES
+ fprintf(stderr, "mmu_version %x\n", mmu_version);
+#endif
+ } else if (!strcmp(featurestr, "nwindows")) {
+ char *err;
+
+ nwindows = strtol(val, &err, 0);
+ if (!*val || *err || nwindows > MAX_NWINDOWS ||
+ nwindows < MIN_NWINDOWS) {
+ fprintf(stderr, "bad numerical value %s\n", val);
+ goto error;
+ }
+ cpu_def->nwindows = nwindows;
+#ifdef DEBUG_FEATURES
+ fprintf(stderr, "nwindows %d\n", nwindows);
+#endif
+ } else {
+ fprintf(stderr, "unrecognized feature %s\n", featurestr);
+ goto error;
+ }
+ } else {
+ fprintf(stderr, "feature string `%s' not in format "
+ "(+feature|-feature|feature=xyz)\n", featurestr);
+ goto error;
+ }
+ featurestr = strtok(NULL, ",");
+ }
+ cpu_def->features |= plus_features;
+ cpu_def->features &= ~minus_features;
+#ifdef DEBUG_FEATURES
+ print_features(stderr, fprintf, cpu_def->features, NULL);
+#endif
+ free(s);
+ return 0;
+
+ error:
+ free(s);
+ return -1;
+}
+
+void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) {
+ (*cpu_fprintf)(f, "Sparc %16s IU " TARGET_FMT_lx " FPU %08x MMU %08x NWINS %d ",
+ sparc_defs[i].name,
+ sparc_defs[i].iu_version,
+ sparc_defs[i].fpu_version,
+ sparc_defs[i].mmu_version,
+ sparc_defs[i].nwindows);
+ print_features(f, cpu_fprintf, CPU_DEFAULT_FEATURES &
+ ~sparc_defs[i].features, "-");
+ print_features(f, cpu_fprintf, ~CPU_DEFAULT_FEATURES &
+ sparc_defs[i].features, "+");
+ (*cpu_fprintf)(f, "\n");
+ }
+ (*cpu_fprintf)(f, "Default CPU feature flags (use '-' to remove): ");
+ print_features(f, cpu_fprintf, CPU_DEFAULT_FEATURES, NULL);
+ (*cpu_fprintf)(f, "\n");
+ (*cpu_fprintf)(f, "Available CPU feature flags (use '+' to add): ");
+ print_features(f, cpu_fprintf, ~CPU_DEFAULT_FEATURES, NULL);
+ (*cpu_fprintf)(f, "\n");
+ (*cpu_fprintf)(f, "Numerical features (use '=' to set): iu_version "
+ "fpu_version mmu_version nwindows\n");
+}
+
+static void cpu_print_cc(FILE *f, fprintf_function cpu_fprintf,
+ uint32_t cc)
+{
+ cpu_fprintf(f, "%c%c%c%c", cc & PSR_NEG? 'N' : '-',
+ cc & PSR_ZERO? 'Z' : '-', cc & PSR_OVF? 'V' : '-',
+ cc & PSR_CARRY? 'C' : '-');
+}
+
+#ifdef TARGET_SPARC64
+#define REGS_PER_LINE 4
+#else
+#define REGS_PER_LINE 8
+#endif
+
+void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i, x;
+
+ cpu_fprintf(f, "pc: " TARGET_FMT_lx " npc: " TARGET_FMT_lx "\n", env->pc,
+ env->npc);
+ cpu_fprintf(f, "General Registers:\n");
+
+ for (i = 0; i < 8; i++) {
+ if (i % REGS_PER_LINE == 0) {
+ cpu_fprintf(f, "%%g%d-%d:", i, i + REGS_PER_LINE - 1);
+ }
+ cpu_fprintf(f, " " TARGET_FMT_lx, env->gregs[i]);
+ if (i % REGS_PER_LINE == REGS_PER_LINE - 1) {
+ cpu_fprintf(f, "\n");
+ }
+ }
+ cpu_fprintf(f, "\nCurrent Register Window:\n");
+ for (x = 0; x < 3; x++) {
+ for (i = 0; i < 8; i++) {
+ if (i % REGS_PER_LINE == 0) {
+ cpu_fprintf(f, "%%%c%d-%d: ",
+ x == 0 ? 'o' : (x == 1 ? 'l' : 'i'),
+ i, i + REGS_PER_LINE - 1);
+ }
+ cpu_fprintf(f, TARGET_FMT_lx " ", env->regwptr[i + x * 8]);
+ if (i % REGS_PER_LINE == REGS_PER_LINE - 1) {
+ cpu_fprintf(f, "\n");
+ }
+ }
+ }
+ cpu_fprintf(f, "\nFloating Point Registers:\n");
+ for (i = 0; i < TARGET_FPREGS; i++) {
+ if ((i & 3) == 0)
+ cpu_fprintf(f, "%%f%02d:", i);
+ cpu_fprintf(f, " %016f", *(float *)&env->fpr[i]);
+ if ((i & 3) == 3)
+ cpu_fprintf(f, "\n");
+ }
+#ifdef TARGET_SPARC64
+ cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate,
+ (unsigned)cpu_get_ccr(env));
+ cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << PSR_CARRY_SHIFT);
+ cpu_fprintf(f, " xcc: ");
+ cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << (PSR_CARRY_SHIFT - 4));
+ cpu_fprintf(f, ") asi: %02x tl: %d pil: %x\n", env->asi, env->tl,
+ env->psrpil);
+ cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate: %d "
+ "cleanwin: %d cwp: %d\n",
+ env->cansave, env->canrestore, env->otherwin, env->wstate,
+ env->cleanwin, env->nwindows - 1 - env->cwp);
+ cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: "
+ TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs);
+#else
+ cpu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env));
+ cpu_print_cc(f, cpu_fprintf, cpu_get_psr(env));
+ cpu_fprintf(f, " SPE: %c%c%c) wim: %08x\n", env->psrs? 'S' : '-',
+ env->psrps? 'P' : '-', env->psret? 'E' : '-',
+ env->wim);
+ cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx "\n",
+ env->fsr, env->y);
+#endif
+}
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
new file mode 100644
index 0000000..12e8557
--- /dev/null
+++ b/target-sparc/helper.h
@@ -0,0 +1,166 @@
+#include "def-helper.h"
+
+#ifndef TARGET_SPARC64
+DEF_HELPER_0(rett, void)
+DEF_HELPER_1(wrpsr, void, tl)
+DEF_HELPER_0(rdpsr, tl)
+#else
+DEF_HELPER_1(wrpil, void, tl)
+DEF_HELPER_1(wrpstate, void, tl)
+DEF_HELPER_0(done, void)
+DEF_HELPER_0(retry, void)
+DEF_HELPER_0(flushw, void)
+DEF_HELPER_0(saved, void)
+DEF_HELPER_0(restored, void)
+DEF_HELPER_0(rdccr, tl)
+DEF_HELPER_1(wrccr, void, tl)
+DEF_HELPER_0(rdcwp, tl)
+DEF_HELPER_1(wrcwp, void, tl)
+DEF_HELPER_2(array8, tl, tl, tl)
+DEF_HELPER_2(alignaddr, tl, tl, tl)
+DEF_HELPER_1(popc, tl, tl)
+DEF_HELPER_3(ldda_asi, void, tl, int, int)
+DEF_HELPER_4(ldf_asi, void, tl, int, int, int)
+DEF_HELPER_4(stf_asi, void, tl, int, int, int)
+DEF_HELPER_4(cas_asi, tl, tl, tl, tl, i32)
+DEF_HELPER_4(casx_asi, tl, tl, tl, tl, i32)
+DEF_HELPER_1(set_softint, void, i64)
+DEF_HELPER_1(clear_softint, void, i64)
+DEF_HELPER_1(write_softint, void, i64)
+DEF_HELPER_2(tick_set_count, void, ptr, i64)
+DEF_HELPER_1(tick_get_count, i64, ptr)
+DEF_HELPER_2(tick_set_limit, void, ptr, i64)
+#endif
+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)
+DEF_HELPER_2(sdiv_cc, tl, tl, tl)
+DEF_HELPER_2(stdf, void, tl, int)
+DEF_HELPER_2(lddf, void, tl, int)
+DEF_HELPER_2(ldqf, void, tl, int)
+DEF_HELPER_2(stqf, void, tl, int)
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+DEF_HELPER_4(ld_asi, i64, tl, int, int, int)
+DEF_HELPER_4(st_asi, void, tl, i64, int, int)
+#endif
+DEF_HELPER_1(ldfsr, void, i32)
+DEF_HELPER_0(check_ieee_exceptions, void)
+DEF_HELPER_0(clear_float_exceptions, void)
+DEF_HELPER_1(fabss, f32, f32)
+DEF_HELPER_1(fsqrts, f32, f32)
+DEF_HELPER_0(fsqrtd, void)
+DEF_HELPER_2(fcmps, void, f32, f32)
+DEF_HELPER_0(fcmpd, void)
+DEF_HELPER_2(fcmpes, void, f32, f32)
+DEF_HELPER_0(fcmped, void)
+DEF_HELPER_0(fsqrtq, void)
+DEF_HELPER_0(fcmpq, void)
+DEF_HELPER_0(fcmpeq, void)
+#ifdef TARGET_SPARC64
+DEF_HELPER_1(ldxfsr, void, i64)
+DEF_HELPER_0(fabsd, void)
+DEF_HELPER_2(fcmps_fcc1, void, f32, f32)
+DEF_HELPER_2(fcmps_fcc2, void, f32, f32)
+DEF_HELPER_2(fcmps_fcc3, void, f32, f32)
+DEF_HELPER_0(fcmpd_fcc1, void)
+DEF_HELPER_0(fcmpd_fcc2, void)
+DEF_HELPER_0(fcmpd_fcc3, void)
+DEF_HELPER_2(fcmpes_fcc1, void, f32, f32)
+DEF_HELPER_2(fcmpes_fcc2, void, f32, f32)
+DEF_HELPER_2(fcmpes_fcc3, void, f32, f32)
+DEF_HELPER_0(fcmped_fcc1, void)
+DEF_HELPER_0(fcmped_fcc2, void)
+DEF_HELPER_0(fcmped_fcc3, void)
+DEF_HELPER_0(fabsq, void)
+DEF_HELPER_0(fcmpq_fcc1, void)
+DEF_HELPER_0(fcmpq_fcc2, void)
+DEF_HELPER_0(fcmpq_fcc3, void)
+DEF_HELPER_0(fcmpeq_fcc1, void)
+DEF_HELPER_0(fcmpeq_fcc2, void)
+DEF_HELPER_0(fcmpeq_fcc3, void)
+#endif
+DEF_HELPER_1(raise_exception, void, int)
+DEF_HELPER_0(shutdown, void)
+#define F_HELPER_0_0(name) DEF_HELPER_0(f ## name, void)
+#define F_HELPER_DQ_0_0(name) \
+ F_HELPER_0_0(name ## d); \
+ F_HELPER_0_0(name ## q)
+
+F_HELPER_DQ_0_0(add);
+F_HELPER_DQ_0_0(sub);
+F_HELPER_DQ_0_0(mul);
+F_HELPER_DQ_0_0(div);
+
+DEF_HELPER_2(fadds, f32, f32, f32)
+DEF_HELPER_2(fsubs, f32, f32, f32)
+DEF_HELPER_2(fmuls, f32, f32, f32)
+DEF_HELPER_2(fdivs, f32, f32, f32)
+
+DEF_HELPER_2(fsmuld, void, f32, f32)
+F_HELPER_0_0(dmulq);
+
+DEF_HELPER_1(fnegs, f32, f32)
+DEF_HELPER_1(fitod, void, s32)
+DEF_HELPER_1(fitoq, void, s32)
+
+DEF_HELPER_1(fitos, f32, s32)
+
+#ifdef TARGET_SPARC64
+DEF_HELPER_0(fnegd, void)
+DEF_HELPER_0(fnegq, void)
+DEF_HELPER_0(fxtos, i32)
+F_HELPER_DQ_0_0(xto);
+#endif
+DEF_HELPER_0(fdtos, f32)
+DEF_HELPER_1(fstod, void, f32)
+DEF_HELPER_0(fqtos, f32)
+DEF_HELPER_1(fstoq, void, f32)
+F_HELPER_0_0(qtod);
+F_HELPER_0_0(dtoq);
+DEF_HELPER_1(fstoi, s32, f32)
+DEF_HELPER_0(fdtoi, s32)
+DEF_HELPER_0(fqtoi, s32)
+#ifdef TARGET_SPARC64
+DEF_HELPER_1(fstox, void, i32)
+F_HELPER_0_0(dtox);
+F_HELPER_0_0(qtox);
+F_HELPER_0_0(aligndata);
+
+F_HELPER_0_0(pmerge);
+F_HELPER_0_0(mul8x16);
+F_HELPER_0_0(mul8x16al);
+F_HELPER_0_0(mul8x16au);
+F_HELPER_0_0(mul8sux16);
+F_HELPER_0_0(mul8ulx16);
+F_HELPER_0_0(muld8sux16);
+F_HELPER_0_0(muld8ulx16);
+F_HELPER_0_0(expand);
+#define VIS_HELPER(name) \
+ F_HELPER_0_0(name##16); \
+ DEF_HELPER_2(f ## name ## 16s, i32, i32, i32) \
+ F_HELPER_0_0(name##32); \
+ DEF_HELPER_2(f ## name ## 32s, i32, i32, i32)
+
+VIS_HELPER(padd);
+VIS_HELPER(psub);
+#define VIS_CMPHELPER(name) \
+ F_HELPER_0_0(name##16); \
+ F_HELPER_0_0(name##32)
+VIS_CMPHELPER(cmpgt);
+VIS_CMPHELPER(cmpeq);
+VIS_CMPHELPER(cmple);
+VIS_CMPHELPER(cmpne);
+#endif
+#undef F_HELPER_0_0
+#undef F_HELPER_DQ_0_0
+#undef VIS_HELPER
+#undef VIS_CMPHELPER
+DEF_HELPER_0(compute_psr, void);
+DEF_HELPER_0(compute_C_icc, i32);
+
+#include "def-helper.h"
diff --git a/target-sparc/machine.c b/target-sparc/machine.c
new file mode 100644
index 0000000..752e431
--- /dev/null
+++ b/target-sparc/machine.c
@@ -0,0 +1,199 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "qemu-timer.h"
+
+#include "exec-all.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ CPUState *env = opaque;
+ int i;
+ uint32_t tmp;
+
+ // if env->cwp == env->nwindows - 1, this will set the ins of the last
+ // window as the outs of the first window
+ cpu_set_cwp(env, env->cwp);
+
+ for(i = 0; i < 8; i++)
+ qemu_put_betls(f, &env->gregs[i]);
+ qemu_put_be32s(f, &env->nwindows);
+ for(i = 0; i < env->nwindows * 16; i++)
+ qemu_put_betls(f, &env->regbase[i]);
+
+ /* FPU */
+ for(i = 0; i < TARGET_FPREGS; i++) {
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.f = env->fpr[i];
+ qemu_put_be32(f, u.i);
+ }
+
+ qemu_put_betls(f, &env->pc);
+ qemu_put_betls(f, &env->npc);
+ qemu_put_betls(f, &env->y);
+ tmp = cpu_get_psr(env);
+ qemu_put_be32(f, tmp);
+ qemu_put_betls(f, &env->fsr);
+ qemu_put_betls(f, &env->tbr);
+ tmp = env->interrupt_index;
+ qemu_put_be32(f, tmp);
+ qemu_put_be32s(f, &env->pil_in);
+#ifndef TARGET_SPARC64
+ qemu_put_be32s(f, &env->wim);
+ /* MMU */
+ for (i = 0; i < 32; i++)
+ qemu_put_be32s(f, &env->mmuregs[i]);
+#else
+ qemu_put_be64s(f, &env->lsu);
+ for (i = 0; i < 16; i++) {
+ qemu_put_be64s(f, &env->immuregs[i]);
+ qemu_put_be64s(f, &env->dmmuregs[i]);
+ }
+ for (i = 0; i < 64; i++) {
+ qemu_put_be64s(f, &env->itlb[i].tag);
+ qemu_put_be64s(f, &env->itlb[i].tte);
+ qemu_put_be64s(f, &env->dtlb[i].tag);
+ qemu_put_be64s(f, &env->dtlb[i].tte);
+ }
+ qemu_put_be32s(f, &env->mmu_version);
+ for (i = 0; i < MAXTL_MAX; i++) {
+ qemu_put_be64s(f, &env->ts[i].tpc);
+ qemu_put_be64s(f, &env->ts[i].tnpc);
+ qemu_put_be64s(f, &env->ts[i].tstate);
+ qemu_put_be32s(f, &env->ts[i].tt);
+ }
+ qemu_put_be32s(f, &env->xcc);
+ qemu_put_be32s(f, &env->asi);
+ qemu_put_be32s(f, &env->pstate);
+ qemu_put_be32s(f, &env->tl);
+ qemu_put_be32s(f, &env->cansave);
+ qemu_put_be32s(f, &env->canrestore);
+ qemu_put_be32s(f, &env->otherwin);
+ qemu_put_be32s(f, &env->wstate);
+ qemu_put_be32s(f, &env->cleanwin);
+ for (i = 0; i < 8; i++)
+ qemu_put_be64s(f, &env->agregs[i]);
+ for (i = 0; i < 8; i++)
+ qemu_put_be64s(f, &env->bgregs[i]);
+ for (i = 0; i < 8; i++)
+ qemu_put_be64s(f, &env->igregs[i]);
+ for (i = 0; i < 8; i++)
+ qemu_put_be64s(f, &env->mgregs[i]);
+ qemu_put_be64s(f, &env->fprs);
+ qemu_put_be64s(f, &env->tick_cmpr);
+ qemu_put_be64s(f, &env->stick_cmpr);
+ cpu_put_timer(f, env->tick);
+ cpu_put_timer(f, env->stick);
+ qemu_put_be64s(f, &env->gsr);
+ qemu_put_be32s(f, &env->gl);
+ qemu_put_be64s(f, &env->hpstate);
+ for (i = 0; i < MAXTL_MAX; i++)
+ qemu_put_be64s(f, &env->htstate[i]);
+ qemu_put_be64s(f, &env->hintp);
+ qemu_put_be64s(f, &env->htba);
+ qemu_put_be64s(f, &env->hver);
+ qemu_put_be64s(f, &env->hstick_cmpr);
+ qemu_put_be64s(f, &env->ssr);
+ cpu_put_timer(f, env->hstick);
+#endif
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ CPUState *env = opaque;
+ int i;
+ uint32_t tmp;
+
+ if (version_id < 6)
+ return -EINVAL;
+ for(i = 0; i < 8; i++)
+ qemu_get_betls(f, &env->gregs[i]);
+ qemu_get_be32s(f, &env->nwindows);
+ for(i = 0; i < env->nwindows * 16; i++)
+ qemu_get_betls(f, &env->regbase[i]);
+
+ /* FPU */
+ for(i = 0; i < TARGET_FPREGS; i++) {
+ union {
+ float32 f;
+ uint32_t i;
+ } u;
+ u.i = qemu_get_be32(f);
+ env->fpr[i] = u.f;
+ }
+
+ qemu_get_betls(f, &env->pc);
+ qemu_get_betls(f, &env->npc);
+ qemu_get_betls(f, &env->y);
+ tmp = qemu_get_be32(f);
+ env->cwp = 0; /* needed to ensure that the wrapping registers are
+ correctly updated */
+ cpu_put_psr(env, tmp);
+ qemu_get_betls(f, &env->fsr);
+ qemu_get_betls(f, &env->tbr);
+ tmp = qemu_get_be32(f);
+ env->interrupt_index = tmp;
+ qemu_get_be32s(f, &env->pil_in);
+#ifndef TARGET_SPARC64
+ qemu_get_be32s(f, &env->wim);
+ /* MMU */
+ for (i = 0; i < 32; i++)
+ qemu_get_be32s(f, &env->mmuregs[i]);
+#else
+ qemu_get_be64s(f, &env->lsu);
+ for (i = 0; i < 16; i++) {
+ qemu_get_be64s(f, &env->immuregs[i]);
+ qemu_get_be64s(f, &env->dmmuregs[i]);
+ }
+ for (i = 0; i < 64; i++) {
+ qemu_get_be64s(f, &env->itlb[i].tag);
+ qemu_get_be64s(f, &env->itlb[i].tte);
+ qemu_get_be64s(f, &env->dtlb[i].tag);
+ qemu_get_be64s(f, &env->dtlb[i].tte);
+ }
+ qemu_get_be32s(f, &env->mmu_version);
+ for (i = 0; i < MAXTL_MAX; i++) {
+ qemu_get_be64s(f, &env->ts[i].tpc);
+ qemu_get_be64s(f, &env->ts[i].tnpc);
+ qemu_get_be64s(f, &env->ts[i].tstate);
+ qemu_get_be32s(f, &env->ts[i].tt);
+ }
+ qemu_get_be32s(f, &env->xcc);
+ qemu_get_be32s(f, &env->asi);
+ qemu_get_be32s(f, &env->pstate);
+ qemu_get_be32s(f, &env->tl);
+ qemu_get_be32s(f, &env->cansave);
+ qemu_get_be32s(f, &env->canrestore);
+ qemu_get_be32s(f, &env->otherwin);
+ qemu_get_be32s(f, &env->wstate);
+ qemu_get_be32s(f, &env->cleanwin);
+ for (i = 0; i < 8; i++)
+ qemu_get_be64s(f, &env->agregs[i]);
+ for (i = 0; i < 8; i++)
+ qemu_get_be64s(f, &env->bgregs[i]);
+ for (i = 0; i < 8; i++)
+ qemu_get_be64s(f, &env->igregs[i]);
+ for (i = 0; i < 8; i++)
+ qemu_get_be64s(f, &env->mgregs[i]);
+ qemu_get_be64s(f, &env->fprs);
+ qemu_get_be64s(f, &env->tick_cmpr);
+ qemu_get_be64s(f, &env->stick_cmpr);
+ cpu_get_timer(f, env->tick);
+ cpu_get_timer(f, env->stick);
+ qemu_get_be64s(f, &env->gsr);
+ qemu_get_be32s(f, &env->gl);
+ qemu_get_be64s(f, &env->hpstate);
+ for (i = 0; i < MAXTL_MAX; i++)
+ qemu_get_be64s(f, &env->htstate[i]);
+ qemu_get_be64s(f, &env->hintp);
+ qemu_get_be64s(f, &env->htba);
+ qemu_get_be64s(f, &env->hver);
+ qemu_get_be64s(f, &env->hstick_cmpr);
+ qemu_get_be64s(f, &env->ssr);
+ cpu_get_timer(f, env->hstick);
+#endif
+ tlb_flush(env, 1);
+ return 0;
+}
diff --git a/target-sparc/op_helper.c b/target-sparc/op_helper.c
new file mode 100644
index 0000000..854f168
--- /dev/null
+++ b/target-sparc/op_helper.c
@@ -0,0 +1,4535 @@
+#include "exec.h"
+#include "host-utils.h"
+#include "helper.h"
+#include "sysemu.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_MXCC
+//#define DEBUG_UNALIGNED
+//#define DEBUG_UNASSIGNED
+//#define DEBUG_ASI
+//#define DEBUG_PCALL
+//#define DEBUG_PSTATE
+//#define DEBUG_CACHE_CONTROL
+
+#ifdef DEBUG_MMU
+#define DPRINTF_MMU(fmt, ...) \
+ do { printf("MMU: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_MMU(fmt, ...) do {} while (0)
+#endif
+
+#ifdef DEBUG_MXCC
+#define DPRINTF_MXCC(fmt, ...) \
+ do { printf("MXCC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_MXCC(fmt, ...) do {} while (0)
+#endif
+
+#ifdef DEBUG_ASI
+#define DPRINTF_ASI(fmt, ...) \
+ do { printf("ASI: " fmt , ## __VA_ARGS__); } while (0)
+#endif
+
+#ifdef DEBUG_PSTATE
+#define DPRINTF_PSTATE(fmt, ...) \
+ do { printf("PSTATE: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_PSTATE(fmt, ...) do {} while (0)
+#endif
+
+#ifdef DEBUG_CACHE_CONTROL
+#define DPRINTF_CACHE_CONTROL(fmt, ...) \
+ do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0)
+#endif
+
+#ifdef TARGET_SPARC64
+#ifndef TARGET_ABI32
+#define AM_CHECK(env1) ((env1)->pstate & PS_AM)
+#else
+#define AM_CHECK(env1) (1)
+#endif
+#endif
+
+#define DT0 (env->dt0)
+#define DT1 (env->dt1)
+#define QT0 (env->qt0)
+#define QT1 (env->qt1)
+
+/* Leon3 cache control */
+
+/* Cache control: emulate the behavior of cache control registers but without
+ any effect on the emulated */
+
+#define CACHE_STATE_MASK 0x3
+#define CACHE_DISABLED 0x0
+#define CACHE_FROZEN 0x1
+#define CACHE_ENABLED 0x3
+
+/* Cache Control register fields */
+
+#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */
+#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */
+#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */
+#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */
+#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */
+#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */
+#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
+#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
+
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64)
+static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
+ int is_asi, int size);
+#endif
+
+#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
+// Calculates TSB pointer value for fault page size 8k or 64k
+static uint64_t ultrasparc_tsb_pointer(uint64_t tsb_register,
+ uint64_t tag_access_register,
+ int page_size)
+{
+ uint64_t tsb_base = tsb_register & ~0x1fffULL;
+ int tsb_split = (tsb_register & 0x1000ULL) ? 1 : 0;
+ int tsb_size = tsb_register & 0xf;
+
+ // discard lower 13 bits which hold tag access context
+ uint64_t tag_access_va = tag_access_register & ~0x1fffULL;
+
+ // now reorder bits
+ uint64_t tsb_base_mask = ~0x1fffULL;
+ uint64_t va = tag_access_va;
+
+ // move va bits to correct position
+ if (page_size == 8*1024) {
+ va >>= 9;
+ } else if (page_size == 64*1024) {
+ va >>= 12;
+ }
+
+ if (tsb_size) {
+ tsb_base_mask <<= tsb_size;
+ }
+
+ // calculate tsb_base mask and adjust va if split is in use
+ if (tsb_split) {
+ if (page_size == 8*1024) {
+ va &= ~(1ULL << (13 + tsb_size));
+ } else if (page_size == 64*1024) {
+ va |= (1ULL << (13 + tsb_size));
+ }
+ tsb_base_mask <<= 1;
+ }
+
+ return ((tsb_base & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL;
+}
+
+// Calculates tag target register value by reordering bits
+// in tag access register
+static uint64_t ultrasparc_tag_target(uint64_t tag_access_register)
+{
+ return ((tag_access_register & 0x1fff) << 48) | (tag_access_register >> 22);
+}
+
+static void replace_tlb_entry(SparcTLBEntry *tlb,
+ uint64_t tlb_tag, uint64_t tlb_tte,
+ CPUState *env1)
+{
+ target_ulong mask, size, va, offset;
+
+ // flush page range if translation is valid
+ if (TTE_IS_VALID(tlb->tte)) {
+
+ mask = 0xffffffffffffe000ULL;
+ mask <<= 3 * ((tlb->tte >> 61) & 3);
+ size = ~mask + 1;
+
+ va = tlb->tag & mask;
+
+ for (offset = 0; offset < size; offset += TARGET_PAGE_SIZE) {
+ tlb_flush_page(env1, va + offset);
+ }
+ }
+
+ tlb->tag = tlb_tag;
+ tlb->tte = tlb_tte;
+}
+
+static void demap_tlb(SparcTLBEntry *tlb, target_ulong demap_addr,
+ const char* strmmu, CPUState *env1)
+{
+ unsigned int i;
+ target_ulong mask;
+ uint64_t context;
+
+ int is_demap_context = (demap_addr >> 6) & 1;
+
+ // demap context
+ switch ((demap_addr >> 4) & 3) {
+ case 0: // primary
+ context = env1->dmmu.mmu_primary_context;
+ break;
+ case 1: // secondary
+ context = env1->dmmu.mmu_secondary_context;
+ break;
+ case 2: // nucleus
+ context = 0;
+ break;
+ case 3: // reserved
+ default:
+ return;
+ }
+
+ for (i = 0; i < 64; i++) {
+ if (TTE_IS_VALID(tlb[i].tte)) {
+
+ if (is_demap_context) {
+ // will remove non-global entries matching context value
+ if (TTE_IS_GLOBAL(tlb[i].tte) ||
+ !tlb_compare_context(&tlb[i], context)) {
+ continue;
+ }
+ } else {
+ // demap page
+ // will remove any entry matching VA
+ mask = 0xffffffffffffe000ULL;
+ mask <<= 3 * ((tlb[i].tte >> 61) & 3);
+
+ if (!compare_masked(demap_addr, tlb[i].tag, mask)) {
+ continue;
+ }
+
+ // entry should be global or matching context value
+ if (!TTE_IS_GLOBAL(tlb[i].tte) &&
+ !tlb_compare_context(&tlb[i], context)) {
+ continue;
+ }
+ }
+
+ replace_tlb_entry(&tlb[i], 0, 0, env1);
+#ifdef DEBUG_MMU
+ DPRINTF_MMU("%s demap invalidated entry [%02u]\n", strmmu, i);
+ dump_mmu(stdout, fprintf, env1);
+#endif
+ }
+ }
+}
+
+static void replace_tlb_1bit_lru(SparcTLBEntry *tlb,
+ uint64_t tlb_tag, uint64_t tlb_tte,
+ const char* strmmu, CPUState *env1)
+{
+ unsigned int i, replace_used;
+
+ // Try replacing invalid entry
+ for (i = 0; i < 64; i++) {
+ if (!TTE_IS_VALID(tlb[i].tte)) {
+ replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1);
+#ifdef DEBUG_MMU
+ DPRINTF_MMU("%s lru replaced invalid entry [%i]\n", strmmu, i);
+ dump_mmu(stdout, fprintf, env1);
+#endif
+ return;
+ }
+ }
+
+ // All entries are valid, try replacing unlocked entry
+
+ for (replace_used = 0; replace_used < 2; ++replace_used) {
+
+ // Used entries are not replaced on first pass
+
+ for (i = 0; i < 64; i++) {
+ if (!TTE_IS_LOCKED(tlb[i].tte) && !TTE_IS_USED(tlb[i].tte)) {
+
+ replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1);
+#ifdef DEBUG_MMU
+ DPRINTF_MMU("%s lru replaced unlocked %s entry [%i]\n",
+ strmmu, (replace_used?"used":"unused"), i);
+ dump_mmu(stdout, fprintf, env1);
+#endif
+ return;
+ }
+ }
+
+ // Now reset used bit and search for unused entries again
+
+ for (i = 0; i < 64; i++) {
+ TTE_SET_UNUSED(tlb[i].tte);
+ }
+ }
+
+#ifdef DEBUG_MMU
+ DPRINTF_MMU("%s lru replacement failed: no entries available\n", strmmu);
+#endif
+ // error state?
+}
+
+#endif
+
+static inline target_ulong address_mask(CPUState *env1, target_ulong addr)
+{
+#ifdef TARGET_SPARC64
+ if (AM_CHECK(env1))
+ addr &= 0xffffffffULL;
+#endif
+ return addr;
+}
+
+/* returns true if access using this ASI is to have address translated by MMU
+ otherwise access is to raw physical address */
+static inline int is_translating_asi(int asi)
+{
+#ifdef TARGET_SPARC64
+ /* Ultrasparc IIi translating asi
+ - note this list is defined by cpu implementation
+ */
+ switch (asi) {
+ case 0x04 ... 0x11:
+ case 0x18 ... 0x19:
+ case 0x24 ... 0x2C:
+ case 0x70 ... 0x73:
+ case 0x78 ... 0x79:
+ case 0x80 ... 0xFF:
+ return 1;
+
+ default:
+ return 0;
+ }
+#else
+ /* TODO: check sparc32 bits */
+ return 0;
+#endif
+}
+
+static inline target_ulong asi_address_mask(CPUState *env1,
+ int asi, target_ulong addr)
+{
+ if (is_translating_asi(asi)) {
+ return address_mask(env, addr);
+ } else {
+ return addr;
+ }
+}
+
+static void raise_exception(int tt)
+{
+ env->exception_index = tt;
+ cpu_loop_exit();
+}
+
+void HELPER(raise_exception)(int tt)
+{
+ raise_exception(tt);
+}
+
+void helper_shutdown(void)
+{
+#if !defined(CONFIG_USER_ONLY)
+ qemu_system_shutdown_request();
+#endif
+}
+
+void helper_check_align(target_ulong addr, uint32_t align)
+{
+ if (addr & align) {
+#ifdef DEBUG_UNALIGNED
+ printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
+ "\n", addr, env->pc);
+#endif
+ raise_exception(TT_UNALIGNED);
+ }
+}
+
+#define F_HELPER(name, p) void helper_f##name##p(void)
+
+#define F_BINOP(name) \
+ float32 helper_f ## name ## s (float32 src1, float32 src2) \
+ { \
+ return float32_ ## name (src1, src2, &env->fp_status); \
+ } \
+ F_HELPER(name, d) \
+ { \
+ DT0 = float64_ ## name (DT0, DT1, &env->fp_status); \
+ } \
+ F_HELPER(name, q) \
+ { \
+ QT0 = float128_ ## name (QT0, QT1, &env->fp_status); \
+ }
+
+F_BINOP(add);
+F_BINOP(sub);
+F_BINOP(mul);
+F_BINOP(div);
+#undef F_BINOP
+
+void helper_fsmuld(float32 src1, float32 src2)
+{
+ DT0 = float64_mul(float32_to_float64(src1, &env->fp_status),
+ float32_to_float64(src2, &env->fp_status),
+ &env->fp_status);
+}
+
+void helper_fdmulq(void)
+{
+ QT0 = float128_mul(float64_to_float128(DT0, &env->fp_status),
+ float64_to_float128(DT1, &env->fp_status),
+ &env->fp_status);
+}
+
+float32 helper_fnegs(float32 src)
+{
+ return float32_chs(src);
+}
+
+#ifdef TARGET_SPARC64
+F_HELPER(neg, d)
+{
+ DT0 = float64_chs(DT1);
+}
+
+F_HELPER(neg, q)
+{
+ QT0 = float128_chs(QT1);
+}
+#endif
+
+/* Integer to float conversion. */
+float32 helper_fitos(int32_t src)
+{
+ return int32_to_float32(src, &env->fp_status);
+}
+
+void helper_fitod(int32_t src)
+{
+ DT0 = int32_to_float64(src, &env->fp_status);
+}
+
+void helper_fitoq(int32_t src)
+{
+ QT0 = int32_to_float128(src, &env->fp_status);
+}
+
+#ifdef TARGET_SPARC64
+float32 helper_fxtos(void)
+{
+ return int64_to_float32(*((int64_t *)&DT1), &env->fp_status);
+}
+
+F_HELPER(xto, d)
+{
+ DT0 = int64_to_float64(*((int64_t *)&DT1), &env->fp_status);
+}
+
+F_HELPER(xto, q)
+{
+ QT0 = int64_to_float128(*((int64_t *)&DT1), &env->fp_status);
+}
+#endif
+#undef F_HELPER
+
+/* floating point conversion */
+float32 helper_fdtos(void)
+{
+ return float64_to_float32(DT1, &env->fp_status);
+}
+
+void helper_fstod(float32 src)
+{
+ DT0 = float32_to_float64(src, &env->fp_status);
+}
+
+float32 helper_fqtos(void)
+{
+ return float128_to_float32(QT1, &env->fp_status);
+}
+
+void helper_fstoq(float32 src)
+{
+ QT0 = float32_to_float128(src, &env->fp_status);
+}
+
+void helper_fqtod(void)
+{
+ DT0 = float128_to_float64(QT1, &env->fp_status);
+}
+
+void helper_fdtoq(void)
+{
+ QT0 = float64_to_float128(DT1, &env->fp_status);
+}
+
+/* Float to integer conversion. */
+int32_t helper_fstoi(float32 src)
+{
+ return float32_to_int32_round_to_zero(src, &env->fp_status);
+}
+
+int32_t helper_fdtoi(void)
+{
+ return float64_to_int32_round_to_zero(DT1, &env->fp_status);
+}
+
+int32_t helper_fqtoi(void)
+{
+ return float128_to_int32_round_to_zero(QT1, &env->fp_status);
+}
+
+#ifdef TARGET_SPARC64
+void helper_fstox(float32 src)
+{
+ *((int64_t *)&DT0) = float32_to_int64_round_to_zero(src, &env->fp_status);
+}
+
+void helper_fdtox(void)
+{
+ *((int64_t *)&DT0) = float64_to_int64_round_to_zero(DT1, &env->fp_status);
+}
+
+void helper_fqtox(void)
+{
+ *((int64_t *)&DT0) = float128_to_int64_round_to_zero(QT1, &env->fp_status);
+}
+
+void helper_faligndata(void)
+{
+ uint64_t tmp;
+
+ tmp = (*((uint64_t *)&DT0)) << ((env->gsr & 7) * 8);
+ /* on many architectures a shift of 64 does nothing */
+ if ((env->gsr & 7) != 0) {
+ tmp |= (*((uint64_t *)&DT1)) >> (64 - (env->gsr & 7) * 8);
+ }
+ *((uint64_t *)&DT0) = tmp;
+}
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define VIS_B64(n) b[7 - (n)]
+#define VIS_W64(n) w[3 - (n)]
+#define VIS_SW64(n) sw[3 - (n)]
+#define VIS_L64(n) l[1 - (n)]
+#define VIS_B32(n) b[3 - (n)]
+#define VIS_W32(n) w[1 - (n)]
+#else
+#define VIS_B64(n) b[n]
+#define VIS_W64(n) w[n]
+#define VIS_SW64(n) sw[n]
+#define VIS_L64(n) l[n]
+#define VIS_B32(n) b[n]
+#define VIS_W32(n) w[n]
+#endif
+
+typedef union {
+ uint8_t b[8];
+ uint16_t w[4];
+ int16_t sw[4];
+ uint32_t l[2];
+ float64 d;
+} vis64;
+
+typedef union {
+ uint8_t b[4];
+ uint16_t w[2];
+ uint32_t l;
+ float32 f;
+} vis32;
+
+void helper_fpmerge(void)
+{
+ vis64 s, d;
+
+ s.d = DT0;
+ d.d = DT1;
+
+ // Reverse calculation order to handle overlap
+ d.VIS_B64(7) = s.VIS_B64(3);
+ d.VIS_B64(6) = d.VIS_B64(3);
+ d.VIS_B64(5) = s.VIS_B64(2);
+ d.VIS_B64(4) = d.VIS_B64(2);
+ d.VIS_B64(3) = s.VIS_B64(1);
+ d.VIS_B64(2) = d.VIS_B64(1);
+ d.VIS_B64(1) = s.VIS_B64(0);
+ //d.VIS_B64(0) = d.VIS_B64(0);
+
+ DT0 = d.d;
+}
+
+void helper_fmul8x16(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_W64(r) = tmp >> 8;
+
+ PMUL(0);
+ PMUL(1);
+ PMUL(2);
+ PMUL(3);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fmul8x16al(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_W64(r) = tmp >> 8;
+
+ PMUL(0);
+ PMUL(1);
+ PMUL(2);
+ PMUL(3);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fmul8x16au(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_W64(r) = tmp >> 8;
+
+ PMUL(0);
+ PMUL(1);
+ PMUL(2);
+ PMUL(3);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fmul8sux16(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_W64(r) = tmp >> 8;
+
+ PMUL(0);
+ PMUL(1);
+ PMUL(2);
+ PMUL(3);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fmul8ulx16(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_W64(r) = tmp >> 8;
+
+ PMUL(0);
+ PMUL(1);
+ PMUL(2);
+ PMUL(3);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fmuld8sux16(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_L64(r) = tmp;
+
+ // Reverse calculation order to handle overlap
+ PMUL(1);
+ PMUL(0);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fmuld8ulx16(void)
+{
+ vis64 s, d;
+ uint32_t tmp;
+
+ s.d = DT0;
+ d.d = DT1;
+
+#define PMUL(r) \
+ tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
+ if ((tmp & 0xff) > 0x7f) \
+ tmp += 0x100; \
+ d.VIS_L64(r) = tmp;
+
+ // Reverse calculation order to handle overlap
+ PMUL(1);
+ PMUL(0);
+#undef PMUL
+
+ DT0 = d.d;
+}
+
+void helper_fexpand(void)
+{
+ vis32 s;
+ vis64 d;
+
+ s.l = (uint32_t)(*(uint64_t *)&DT0 & 0xffffffff);
+ d.d = DT1;
+ d.VIS_W64(0) = s.VIS_B32(0) << 4;
+ d.VIS_W64(1) = s.VIS_B32(1) << 4;
+ d.VIS_W64(2) = s.VIS_B32(2) << 4;
+ d.VIS_W64(3) = s.VIS_B32(3) << 4;
+
+ DT0 = d.d;
+}
+
+#define VIS_HELPER(name, F) \
+ void name##16(void) \
+ { \
+ vis64 s, d; \
+ \
+ s.d = DT0; \
+ d.d = DT1; \
+ \
+ d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0)); \
+ d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1)); \
+ d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2)); \
+ d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3)); \
+ \
+ DT0 = d.d; \
+ } \
+ \
+ uint32_t name##16s(uint32_t src1, uint32_t src2) \
+ { \
+ vis32 s, d; \
+ \
+ s.l = src1; \
+ d.l = src2; \
+ \
+ d.VIS_W32(0) = F(d.VIS_W32(0), s.VIS_W32(0)); \
+ d.VIS_W32(1) = F(d.VIS_W32(1), s.VIS_W32(1)); \
+ \
+ return d.l; \
+ } \
+ \
+ void name##32(void) \
+ { \
+ vis64 s, d; \
+ \
+ s.d = DT0; \
+ d.d = DT1; \
+ \
+ d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0)); \
+ d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1)); \
+ \
+ DT0 = d.d; \
+ } \
+ \
+ uint32_t name##32s(uint32_t src1, uint32_t src2) \
+ { \
+ vis32 s, d; \
+ \
+ s.l = src1; \
+ d.l = src2; \
+ \
+ d.l = F(d.l, s.l); \
+ \
+ return d.l; \
+ }
+
+#define FADD(a, b) ((a) + (b))
+#define FSUB(a, b) ((a) - (b))
+VIS_HELPER(helper_fpadd, FADD)
+VIS_HELPER(helper_fpsub, FSUB)
+
+#define VIS_CMPHELPER(name, F) \
+ void name##16(void) \
+ { \
+ vis64 s, d; \
+ \
+ s.d = DT0; \
+ d.d = DT1; \
+ \
+ d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0))? 1: 0; \
+ d.VIS_W64(0) |= F(d.VIS_W64(1), s.VIS_W64(1))? 2: 0; \
+ d.VIS_W64(0) |= F(d.VIS_W64(2), s.VIS_W64(2))? 4: 0; \
+ d.VIS_W64(0) |= F(d.VIS_W64(3), s.VIS_W64(3))? 8: 0; \
+ \
+ DT0 = d.d; \
+ } \
+ \
+ void name##32(void) \
+ { \
+ vis64 s, d; \
+ \
+ s.d = DT0; \
+ d.d = DT1; \
+ \
+ d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0))? 1: 0; \
+ d.VIS_L64(0) |= F(d.VIS_L64(1), s.VIS_L64(1))? 2: 0; \
+ \
+ DT0 = d.d; \
+ }
+
+#define FCMPGT(a, b) ((a) > (b))
+#define FCMPEQ(a, b) ((a) == (b))
+#define FCMPLE(a, b) ((a) <= (b))
+#define FCMPNE(a, b) ((a) != (b))
+
+VIS_CMPHELPER(helper_fcmpgt, FCMPGT)
+VIS_CMPHELPER(helper_fcmpeq, FCMPEQ)
+VIS_CMPHELPER(helper_fcmple, FCMPLE)
+VIS_CMPHELPER(helper_fcmpne, FCMPNE)
+#endif
+
+void helper_check_ieee_exceptions(void)
+{
+ target_ulong status;
+
+ status = get_float_exception_flags(&env->fp_status);
+ if (status) {
+ /* Copy IEEE 754 flags into FSR */
+ if (status & float_flag_invalid)
+ env->fsr |= FSR_NVC;
+ if (status & float_flag_overflow)
+ env->fsr |= FSR_OFC;
+ if (status & float_flag_underflow)
+ env->fsr |= FSR_UFC;
+ if (status & float_flag_divbyzero)
+ env->fsr |= FSR_DZC;
+ if (status & float_flag_inexact)
+ env->fsr |= FSR_NXC;
+
+ if ((env->fsr & FSR_CEXC_MASK) & ((env->fsr & FSR_TEM_MASK) >> 23)) {
+ /* Unmasked exception, generate a trap */
+ env->fsr |= FSR_FTT_IEEE_EXCP;
+ raise_exception(TT_FP_EXCP);
+ } else {
+ /* Accumulate exceptions */
+ env->fsr |= (env->fsr & FSR_CEXC_MASK) << 5;
+ }
+ }
+}
+
+void helper_clear_float_exceptions(void)
+{
+ set_float_exception_flags(0, &env->fp_status);
+}
+
+float32 helper_fabss(float32 src)
+{
+ return float32_abs(src);
+}
+
+#ifdef TARGET_SPARC64
+void helper_fabsd(void)
+{
+ DT0 = float64_abs(DT1);
+}
+
+void helper_fabsq(void)
+{
+ QT0 = float128_abs(QT1);
+}
+#endif
+
+float32 helper_fsqrts(float32 src)
+{
+ return float32_sqrt(src, &env->fp_status);
+}
+
+void helper_fsqrtd(void)
+{
+ DT0 = float64_sqrt(DT1, &env->fp_status);
+}
+
+void helper_fsqrtq(void)
+{
+ QT0 = float128_sqrt(QT1, &env->fp_status);
+}
+
+#define GEN_FCMP(name, size, reg1, reg2, FS, E) \
+ void glue(helper_, name) (void) \
+ { \
+ env->fsr &= FSR_FTT_NMASK; \
+ if (E && (glue(size, _is_any_nan)(reg1) || \
+ glue(size, _is_any_nan)(reg2)) && \
+ (env->fsr & FSR_NVM)) { \
+ env->fsr |= FSR_NVC; \
+ env->fsr |= FSR_FTT_IEEE_EXCP; \
+ raise_exception(TT_FP_EXCP); \
+ } \
+ switch (glue(size, _compare) (reg1, reg2, &env->fp_status)) { \
+ case float_relation_unordered: \
+ if ((env->fsr & FSR_NVM)) { \
+ env->fsr |= FSR_NVC; \
+ env->fsr |= FSR_FTT_IEEE_EXCP; \
+ raise_exception(TT_FP_EXCP); \
+ } else { \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \
+ env->fsr |= FSR_NVA; \
+ } \
+ break; \
+ case float_relation_less: \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ env->fsr |= FSR_FCC0 << FS; \
+ break; \
+ case float_relation_greater: \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ env->fsr |= FSR_FCC1 << FS; \
+ break; \
+ default: \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ break; \
+ } \
+ }
+#define GEN_FCMPS(name, size, FS, E) \
+ void glue(helper_, name)(float32 src1, float32 src2) \
+ { \
+ env->fsr &= FSR_FTT_NMASK; \
+ if (E && (glue(size, _is_any_nan)(src1) || \
+ glue(size, _is_any_nan)(src2)) && \
+ (env->fsr & FSR_NVM)) { \
+ env->fsr |= FSR_NVC; \
+ env->fsr |= FSR_FTT_IEEE_EXCP; \
+ raise_exception(TT_FP_EXCP); \
+ } \
+ switch (glue(size, _compare) (src1, src2, &env->fp_status)) { \
+ case float_relation_unordered: \
+ if ((env->fsr & FSR_NVM)) { \
+ env->fsr |= FSR_NVC; \
+ env->fsr |= FSR_FTT_IEEE_EXCP; \
+ raise_exception(TT_FP_EXCP); \
+ } else { \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \
+ env->fsr |= FSR_NVA; \
+ } \
+ break; \
+ case float_relation_less: \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ env->fsr |= FSR_FCC0 << FS; \
+ break; \
+ case float_relation_greater: \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ env->fsr |= FSR_FCC1 << FS; \
+ break; \
+ default: \
+ env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
+ break; \
+ } \
+ }
+
+GEN_FCMPS(fcmps, float32, 0, 0);
+GEN_FCMP(fcmpd, float64, DT0, DT1, 0, 0);
+
+GEN_FCMPS(fcmpes, float32, 0, 1);
+GEN_FCMP(fcmped, float64, DT0, DT1, 0, 1);
+
+GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0);
+GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1);
+
+static uint32_t compute_all_flags(void)
+{
+ return env->psr & PSR_ICC;
+}
+
+static uint32_t compute_C_flags(void)
+{
+ return env->psr & PSR_CARRY;
+}
+
+static inline uint32_t get_NZ_icc(int32_t dst)
+{
+ uint32_t ret = 0;
+
+ if (dst == 0) {
+ ret = PSR_ZERO;
+ } else if (dst < 0) {
+ ret = PSR_NEG;
+ }
+ return ret;
+}
+
+#ifdef TARGET_SPARC64
+static uint32_t compute_all_flags_xcc(void)
+{
+ return env->xcc & PSR_ICC;
+}
+
+static uint32_t compute_C_flags_xcc(void)
+{
+ return env->xcc & PSR_CARRY;
+}
+
+static inline uint32_t get_NZ_xcc(target_long dst)
+{
+ uint32_t ret = 0;
+
+ if (!dst) {
+ ret = PSR_ZERO;
+ } else if (dst < 0) {
+ ret = PSR_NEG;
+ }
+ return ret;
+}
+#endif
+
+static inline uint32_t get_V_div_icc(target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if (src2 != 0) {
+ ret = PSR_OVF;
+ }
+ return ret;
+}
+
+static uint32_t compute_all_div(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_V_div_icc(CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_div(void)
+{
+ return 0;
+}
+
+static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1)
+{
+ uint32_t ret = 0;
+
+ if (dst < src1) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_C_addx_icc(uint32_t dst, uint32_t src1,
+ uint32_t src2)
+{
+ uint32_t ret = 0;
+
+ if (((src1 & src2) | (~dst & (src1 | src2))) & (1U << 31)) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_V_add_icc(uint32_t dst, uint32_t src1,
+ uint32_t src2)
+{
+ uint32_t ret = 0;
+
+ if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1U << 31)) {
+ ret = PSR_OVF;
+ }
+ return ret;
+}
+
+#ifdef TARGET_SPARC64
+static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1)
+{
+ uint32_t ret = 0;
+
+ if (dst < src1) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_C_addx_xcc(target_ulong dst, target_ulong src1,
+ target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if (((src1 & src2) | (~dst & (src1 | src2))) & (1ULL << 63)) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1,
+ target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) {
+ ret = PSR_OVF;
+ }
+ return ret;
+}
+
+static uint32_t compute_all_add_xcc(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_xcc(CC_DST);
+ ret |= get_C_add_xcc(CC_DST, CC_SRC);
+ ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_add_xcc(void)
+{
+ return get_C_add_xcc(CC_DST, CC_SRC);
+}
+#endif
+
+static uint32_t compute_all_add(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_add_icc(CC_DST, CC_SRC);
+ ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_add(void)
+{
+ return get_C_add_icc(CC_DST, CC_SRC);
+}
+
+#ifdef TARGET_SPARC64
+static uint32_t compute_all_addx_xcc(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_xcc(CC_DST);
+ ret |= get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2);
+ ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_addx_xcc(void)
+{
+ uint32_t ret;
+
+ ret = get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+#endif
+
+static uint32_t compute_all_addx(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2);
+ ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_addx(void)
+{
+ uint32_t ret;
+
+ ret = get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if ((src1 | src2) & 0x3) {
+ ret = PSR_OVF;
+ }
+ return ret;
+}
+
+static uint32_t compute_all_tadd(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_add_icc(CC_DST, CC_SRC);
+ ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
+ ret |= get_V_tag_icc(CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_all_taddtv(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_add_icc(CC_DST, CC_SRC);
+ return ret;
+}
+
+static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2)
+{
+ uint32_t ret = 0;
+
+ if (src1 < src2) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_C_subx_icc(uint32_t dst, uint32_t src1,
+ uint32_t src2)
+{
+ uint32_t ret = 0;
+
+ if (((~src1 & src2) | (dst & (~src1 | src2))) & (1U << 31)) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_V_sub_icc(uint32_t dst, uint32_t src1,
+ uint32_t src2)
+{
+ uint32_t ret = 0;
+
+ if (((src1 ^ src2) & (src1 ^ dst)) & (1U << 31)) {
+ ret = PSR_OVF;
+ }
+ return ret;
+}
+
+
+#ifdef TARGET_SPARC64
+static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if (src1 < src2) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_C_subx_xcc(target_ulong dst, target_ulong src1,
+ target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if (((~src1 & src2) | (dst & (~src1 | src2))) & (1ULL << 63)) {
+ ret = PSR_CARRY;
+ }
+ return ret;
+}
+
+static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1,
+ target_ulong src2)
+{
+ uint32_t ret = 0;
+
+ if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) {
+ ret = PSR_OVF;
+ }
+ return ret;
+}
+
+static uint32_t compute_all_sub_xcc(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_xcc(CC_DST);
+ ret |= get_C_sub_xcc(CC_SRC, CC_SRC2);
+ ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_sub_xcc(void)
+{
+ return get_C_sub_xcc(CC_SRC, CC_SRC2);
+}
+#endif
+
+static uint32_t compute_all_sub(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_sub_icc(CC_SRC, CC_SRC2);
+ ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_sub(void)
+{
+ return get_C_sub_icc(CC_SRC, CC_SRC2);
+}
+
+#ifdef TARGET_SPARC64
+static uint32_t compute_all_subx_xcc(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_xcc(CC_DST);
+ ret |= get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2);
+ ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_subx_xcc(void)
+{
+ uint32_t ret;
+
+ ret = get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+#endif
+
+static uint32_t compute_all_subx(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2);
+ ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_C_subx(void)
+{
+ uint32_t ret;
+
+ ret = get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_all_tsub(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_sub_icc(CC_SRC, CC_SRC2);
+ ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
+ ret |= get_V_tag_icc(CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_all_tsubtv(void)
+{
+ uint32_t ret;
+
+ ret = get_NZ_icc(CC_DST);
+ ret |= get_C_sub_icc(CC_SRC, CC_SRC2);
+ return ret;
+}
+
+static uint32_t compute_all_logic(void)
+{
+ return get_NZ_icc(CC_DST);
+}
+
+static uint32_t compute_C_logic(void)
+{
+ return 0;
+}
+
+#ifdef TARGET_SPARC64
+static uint32_t compute_all_logic_xcc(void)
+{
+ return get_NZ_xcc(CC_DST);
+}
+#endif
+
+typedef struct CCTable {
+ uint32_t (*compute_all)(void); /* return all the flags */
+ uint32_t (*compute_c)(void); /* return the C flag */
+} CCTable;
+
+static const CCTable icc_table[CC_OP_NB] = {
+ /* CC_OP_DYNAMIC should never happen */
+ [CC_OP_FLAGS] = { compute_all_flags, compute_C_flags },
+ [CC_OP_DIV] = { compute_all_div, compute_C_div },
+ [CC_OP_ADD] = { compute_all_add, compute_C_add },
+ [CC_OP_ADDX] = { compute_all_addx, compute_C_addx },
+ [CC_OP_TADD] = { compute_all_tadd, compute_C_add },
+ [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add },
+ [CC_OP_SUB] = { compute_all_sub, compute_C_sub },
+ [CC_OP_SUBX] = { compute_all_subx, compute_C_subx },
+ [CC_OP_TSUB] = { compute_all_tsub, compute_C_sub },
+ [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub },
+ [CC_OP_LOGIC] = { compute_all_logic, compute_C_logic },
+};
+
+#ifdef TARGET_SPARC64
+static const CCTable xcc_table[CC_OP_NB] = {
+ /* CC_OP_DYNAMIC should never happen */
+ [CC_OP_FLAGS] = { compute_all_flags_xcc, compute_C_flags_xcc },
+ [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic },
+ [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc },
+ [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc },
+ [CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc },
+ [CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc },
+ [CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc },
+ [CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc },
+ [CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc },
+ [CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc },
+ [CC_OP_LOGIC] = { compute_all_logic_xcc, compute_C_logic },
+};
+#endif
+
+void helper_compute_psr(void)
+{
+ uint32_t new_psr;
+
+ new_psr = icc_table[CC_OP].compute_all();
+ env->psr = new_psr;
+#ifdef TARGET_SPARC64
+ new_psr = xcc_table[CC_OP].compute_all();
+ env->xcc = new_psr;
+#endif
+ CC_OP = CC_OP_FLAGS;
+}
+
+uint32_t helper_compute_C_icc(void)
+{
+ uint32_t ret;
+
+ ret = icc_table[CC_OP].compute_c() >> PSR_CARRY_SHIFT;
+ return ret;
+}
+
+static inline void memcpy32(target_ulong *dst, const target_ulong *src)
+{
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ dst[3] = src[3];
+ dst[4] = src[4];
+ dst[5] = src[5];
+ dst[6] = src[6];
+ dst[7] = src[7];
+}
+
+static void set_cwp(int new_cwp)
+{
+ /* put the modified wrap registers at their proper location */
+ if (env->cwp == env->nwindows - 1) {
+ memcpy32(env->regbase, env->regbase + env->nwindows * 16);
+ }
+ env->cwp = new_cwp;
+
+ /* put the wrap registers at their temporary location */
+ if (new_cwp == env->nwindows - 1) {
+ memcpy32(env->regbase + env->nwindows * 16, env->regbase);
+ }
+ env->regwptr = env->regbase + (new_cwp * 16);
+}
+
+void cpu_set_cwp(CPUState *env1, int new_cwp)
+{
+ CPUState *saved_env;
+
+ saved_env = env;
+ env = env1;
+ set_cwp(new_cwp);
+ env = saved_env;
+}
+
+static target_ulong get_psr(void)
+{
+ helper_compute_psr();
+
+#if !defined (TARGET_SPARC64)
+ return env->version | (env->psr & PSR_ICC) |
+ (env->psref? PSR_EF : 0) |
+ (env->psrpil << 8) |
+ (env->psrs? PSR_S : 0) |
+ (env->psrps? PSR_PS : 0) |
+ (env->psret? PSR_ET : 0) | env->cwp;
+#else
+ return env->psr & PSR_ICC;
+#endif
+}
+
+target_ulong cpu_get_psr(CPUState *env1)
+{
+ CPUState *saved_env;
+ target_ulong ret;
+
+ saved_env = env;
+ env = env1;
+ ret = get_psr();
+ env = saved_env;
+ return ret;
+}
+
+static void put_psr(target_ulong val)
+{
+ env->psr = val & PSR_ICC;
+#if !defined (TARGET_SPARC64)
+ env->psref = (val & PSR_EF)? 1 : 0;
+ env->psrpil = (val & PSR_PIL) >> 8;
+#endif
+#if ((!defined (TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY))
+ cpu_check_irqs(env);
+#endif
+#if !defined (TARGET_SPARC64)
+ env->psrs = (val & PSR_S)? 1 : 0;
+ env->psrps = (val & PSR_PS)? 1 : 0;
+ env->psret = (val & PSR_ET)? 1 : 0;
+ set_cwp(val & PSR_CWP);
+#endif
+ env->cc_op = CC_OP_FLAGS;
+}
+
+void cpu_put_psr(CPUState *env1, target_ulong val)
+{
+ CPUState *saved_env;
+
+ saved_env = env;
+ env = env1;
+ put_psr(val);
+ env = saved_env;
+}
+
+static int cwp_inc(int cwp)
+{
+ if (unlikely(cwp >= env->nwindows)) {
+ cwp -= env->nwindows;
+ }
+ return cwp;
+}
+
+int cpu_cwp_inc(CPUState *env1, int cwp)
+{
+ CPUState *saved_env;
+ target_ulong ret;
+
+ saved_env = env;
+ env = env1;
+ ret = cwp_inc(cwp);
+ env = saved_env;
+ return ret;
+}
+
+static int cwp_dec(int cwp)
+{
+ if (unlikely(cwp < 0)) {
+ cwp += env->nwindows;
+ }
+ return cwp;
+}
+
+int cpu_cwp_dec(CPUState *env1, int cwp)
+{
+ CPUState *saved_env;
+ target_ulong ret;
+
+ saved_env = env;
+ env = env1;
+ ret = cwp_dec(cwp);
+ env = saved_env;
+ return ret;
+}
+
+#ifdef TARGET_SPARC64
+GEN_FCMPS(fcmps_fcc1, float32, 22, 0);
+GEN_FCMP(fcmpd_fcc1, float64, DT0, DT1, 22, 0);
+GEN_FCMP(fcmpq_fcc1, float128, QT0, QT1, 22, 0);
+
+GEN_FCMPS(fcmps_fcc2, float32, 24, 0);
+GEN_FCMP(fcmpd_fcc2, float64, DT0, DT1, 24, 0);
+GEN_FCMP(fcmpq_fcc2, float128, QT0, QT1, 24, 0);
+
+GEN_FCMPS(fcmps_fcc3, float32, 26, 0);
+GEN_FCMP(fcmpd_fcc3, float64, DT0, DT1, 26, 0);
+GEN_FCMP(fcmpq_fcc3, float128, QT0, QT1, 26, 0);
+
+GEN_FCMPS(fcmpes_fcc1, float32, 22, 1);
+GEN_FCMP(fcmped_fcc1, float64, DT0, DT1, 22, 1);
+GEN_FCMP(fcmpeq_fcc1, float128, QT0, QT1, 22, 1);
+
+GEN_FCMPS(fcmpes_fcc2, float32, 24, 1);
+GEN_FCMP(fcmped_fcc2, float64, DT0, DT1, 24, 1);
+GEN_FCMP(fcmpeq_fcc2, float128, QT0, QT1, 24, 1);
+
+GEN_FCMPS(fcmpes_fcc3, float32, 26, 1);
+GEN_FCMP(fcmped_fcc3, float64, DT0, DT1, 26, 1);
+GEN_FCMP(fcmpeq_fcc3, float128, QT0, QT1, 26, 1);
+#endif
+#undef GEN_FCMPS
+
+#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) && \
+ defined(DEBUG_MXCC)
+static void dump_mxcc(CPUState *env)
+{
+ printf("mxccdata: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64
+ "\n",
+ env->mxccdata[0], env->mxccdata[1],
+ env->mxccdata[2], env->mxccdata[3]);
+ printf("mxccregs: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64
+ "\n"
+ " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64
+ "\n",
+ env->mxccregs[0], env->mxccregs[1],
+ env->mxccregs[2], env->mxccregs[3],
+ env->mxccregs[4], env->mxccregs[5],
+ env->mxccregs[6], env->mxccregs[7]);
+}
+#endif
+
+#if (defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY)) \
+ && defined(DEBUG_ASI)
+static void dump_asi(const char *txt, target_ulong addr, int asi, int size,
+ uint64_t r1)
+{
+ switch (size)
+ {
+ case 1:
+ DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %02" PRIx64 "\n", txt,
+ addr, asi, r1 & 0xff);
+ break;
+ case 2:
+ DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %04" PRIx64 "\n", txt,
+ addr, asi, r1 & 0xffff);
+ break;
+ case 4:
+ DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %08" PRIx64 "\n", txt,
+ addr, asi, r1 & 0xffffffff);
+ break;
+ case 8:
+ DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %016" PRIx64 "\n", txt,
+ addr, asi, r1);
+ break;
+ }
+}
+#endif
+
+#ifndef TARGET_SPARC64
+#ifndef CONFIG_USER_ONLY
+
+
+/* Leon3 cache control */
+
+static void leon3_cache_control_int(void)
+{
+ uint32_t state = 0;
+
+ if (env->cache_control & CACHE_CTRL_IF) {
+ /* Instruction cache state */
+ state = env->cache_control & CACHE_STATE_MASK;
+ if (state == CACHE_ENABLED) {
+ state = CACHE_FROZEN;
+ DPRINTF_CACHE_CONTROL("Instruction cache: freeze\n");
+ }
+
+ env->cache_control &= ~CACHE_STATE_MASK;
+ env->cache_control |= state;
+ }
+
+ if (env->cache_control & CACHE_CTRL_DF) {
+ /* Data cache state */
+ state = (env->cache_control >> 2) & CACHE_STATE_MASK;
+ if (state == CACHE_ENABLED) {
+ state = CACHE_FROZEN;
+ DPRINTF_CACHE_CONTROL("Data cache: freeze\n");
+ }
+
+ env->cache_control &= ~(CACHE_STATE_MASK << 2);
+ env->cache_control |= (state << 2);
+ }
+}
+
+static void leon3_cache_control_st(target_ulong addr, uint64_t val, int size)
+{
+ DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n",
+ addr, val, size);
+
+ if (size != 4) {
+ DPRINTF_CACHE_CONTROL("32bits only\n");
+ return;
+ }
+
+ switch (addr) {
+ case 0x00: /* Cache control */
+
+ /* These values must always be read as zeros */
+ val &= ~CACHE_CTRL_FD;
+ val &= ~CACHE_CTRL_FI;
+ val &= ~CACHE_CTRL_IB;
+ val &= ~CACHE_CTRL_IP;
+ val &= ~CACHE_CTRL_DP;
+
+ env->cache_control = val;
+ break;
+ case 0x04: /* Instruction cache configuration */
+ case 0x08: /* Data cache configuration */
+ /* Read Only */
+ break;
+ default:
+ DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr);
+ break;
+ };
+}
+
+static uint64_t leon3_cache_control_ld(target_ulong addr, int size)
+{
+ uint64_t ret = 0;
+
+ if (size != 4) {
+ DPRINTF_CACHE_CONTROL("32bits only\n");
+ return 0;
+ }
+
+ switch (addr) {
+ case 0x00: /* Cache control */
+ ret = env->cache_control;
+ break;
+
+ /* Configuration registers are read and only always keep those
+ predefined values */
+
+ case 0x04: /* Instruction cache configuration */
+ ret = 0x10220000;
+ break;
+ case 0x08: /* Data cache configuration */
+ ret = 0x18220000;
+ break;
+ default:
+ DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr);
+ break;
+ };
+ DPRINTF_CACHE_CONTROL("ld addr:%08x, ret:0x%" PRIx64 ", size:%d\n",
+ addr, ret, size);
+ return ret;
+}
+
+void leon3_irq_manager(void *irq_manager, int intno)
+{
+ leon3_irq_ack(irq_manager, intno);
+ leon3_cache_control_int();
+}
+
+uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
+{
+ uint64_t ret = 0;
+#if defined(DEBUG_MXCC) || defined(DEBUG_ASI)
+ uint32_t last_addr = addr;
+#endif
+
+ helper_check_align(addr, size - 1);
+ switch (asi) {
+ case 2: /* SuperSparc MXCC registers and Leon3 cache control */
+ switch (addr) {
+ case 0x00: /* Leon3 Cache Control */
+ case 0x08: /* Leon3 Instruction Cache config */
+ case 0x0C: /* Leon3 Date Cache config */
+ if (env->def->features & CPU_FEATURE_CACHE_CTRL) {
+ ret = leon3_cache_control_ld(addr, size);
+ }
+ break;
+ case 0x01c00a00: /* MXCC control register */
+ if (size == 8)
+ ret = env->mxccregs[3];
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00a04: /* MXCC control register */
+ if (size == 4)
+ ret = env->mxccregs[3];
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00c00: /* Module reset register */
+ if (size == 8) {
+ ret = env->mxccregs[5];
+ // should we do something here?
+ } else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00f00: /* MBus port address register */
+ if (size == 8)
+ ret = env->mxccregs[7];
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ default:
+ DPRINTF_MXCC("%08x: unimplemented address, size: %d\n", addr,
+ size);
+ break;
+ }
+ DPRINTF_MXCC("asi = %d, size = %d, sign = %d, "
+ "addr = %08x -> ret = %" PRIx64 ","
+ "addr = %08x\n", asi, size, sign, last_addr, ret, addr);
+#ifdef DEBUG_MXCC
+ dump_mxcc(env);
+#endif
+ break;
+ case 3: /* MMU probe */
+ {
+ int mmulev;
+
+ mmulev = (addr >> 8) & 15;
+ if (mmulev > 4)
+ ret = 0;
+ else
+ ret = mmu_probe(env, addr, mmulev);
+ DPRINTF_MMU("mmu_probe: 0x%08x (lev %d) -> 0x%08" PRIx64 "\n",
+ addr, mmulev, ret);
+ }
+ break;
+ case 4: /* read MMU regs */
+ {
+ int reg = (addr >> 8) & 0x1f;
+
+ ret = env->mmuregs[reg];
+ if (reg == 3) /* Fault status cleared on read */
+ env->mmuregs[3] = 0;
+ else if (reg == 0x13) /* Fault status read */
+ ret = env->mmuregs[3];
+ else if (reg == 0x14) /* Fault address read */
+ ret = env->mmuregs[4];
+ DPRINTF_MMU("mmu_read: reg[%d] = 0x%08" PRIx64 "\n", reg, ret);
+ }
+ break;
+ case 5: // Turbosparc ITLB Diagnostic
+ case 6: // Turbosparc DTLB Diagnostic
+ case 7: // Turbosparc IOTLB Diagnostic
+ break;
+ case 9: /* Supervisor code access */
+ switch(size) {
+ case 1:
+ ret = ldub_code(addr);
+ break;
+ case 2:
+ ret = lduw_code(addr);
+ break;
+ default:
+ case 4:
+ ret = ldl_code(addr);
+ break;
+ case 8:
+ ret = ldq_code(addr);
+ break;
+ }
+ break;
+ case 0xa: /* User data access */
+ switch(size) {
+ case 1:
+ ret = ldub_user(addr);
+ break;
+ case 2:
+ ret = lduw_user(addr);
+ break;
+ default:
+ case 4:
+ ret = ldl_user(addr);
+ break;
+ case 8:
+ ret = ldq_user(addr);
+ break;
+ }
+ break;
+ case 0xb: /* Supervisor data access */
+ switch(size) {
+ case 1:
+ ret = ldub_kernel(addr);
+ break;
+ case 2:
+ ret = lduw_kernel(addr);
+ break;
+ default:
+ case 4:
+ ret = ldl_kernel(addr);
+ break;
+ case 8:
+ ret = ldq_kernel(addr);
+ break;
+ }
+ break;
+ case 0xc: /* I-cache tag */
+ case 0xd: /* I-cache data */
+ case 0xe: /* D-cache tag */
+ case 0xf: /* D-cache data */
+ break;
+ case 0x20: /* MMU passthrough */
+ switch(size) {
+ case 1:
+ ret = ldub_phys(addr);
+ break;
+ case 2:
+ ret = lduw_phys(addr);
+ break;
+ default:
+ case 4:
+ ret = ldl_phys(addr);
+ break;
+ case 8:
+ ret = ldq_phys(addr);
+ break;
+ }
+ break;
+ case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
+ switch(size) {
+ case 1:
+ ret = ldub_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32));
+ break;
+ case 2:
+ ret = lduw_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32));
+ break;
+ default:
+ case 4:
+ ret = ldl_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32));
+ break;
+ case 8:
+ ret = ldq_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32));
+ break;
+ }
+ break;
+ case 0x30: // Turbosparc secondary cache diagnostic
+ case 0x31: // Turbosparc RAM snoop
+ case 0x32: // Turbosparc page table descriptor diagnostic
+ case 0x39: /* data cache diagnostic register */
+ case 0x4c: /* SuperSPARC MMU Breakpoint Action register */
+ ret = 0;
+ break;
+ case 0x38: /* SuperSPARC MMU Breakpoint Control Registers */
+ {
+ int reg = (addr >> 8) & 3;
+
+ switch(reg) {
+ case 0: /* Breakpoint Value (Addr) */
+ ret = env->mmubpregs[reg];
+ break;
+ case 1: /* Breakpoint Mask */
+ ret = env->mmubpregs[reg];
+ break;
+ case 2: /* Breakpoint Control */
+ ret = env->mmubpregs[reg];
+ break;
+ case 3: /* Breakpoint Status */
+ ret = env->mmubpregs[reg];
+ env->mmubpregs[reg] = 0ULL;
+ break;
+ }
+ DPRINTF_MMU("read breakpoint reg[%d] 0x%016" PRIx64 "\n", reg,
+ ret);
+ }
+ break;
+ case 8: /* User code access, XXX */
+ default:
+ do_unassigned_access(addr, 0, 0, asi, size);
+ ret = 0;
+ break;
+ }
+ if (sign) {
+ switch(size) {
+ case 1:
+ ret = (int8_t) ret;
+ break;
+ case 2:
+ ret = (int16_t) ret;
+ break;
+ case 4:
+ ret = (int32_t) ret;
+ break;
+ default:
+ break;
+ }
+ }
+#ifdef DEBUG_ASI
+ dump_asi("read ", last_addr, asi, size, ret);
+#endif
+ return ret;
+}
+
+void helper_st_asi(target_ulong addr, uint64_t val, int asi, int size)
+{
+ helper_check_align(addr, size - 1);
+ switch(asi) {
+ case 2: /* SuperSparc MXCC registers and Leon3 cache control */
+ switch (addr) {
+ case 0x00: /* Leon3 Cache Control */
+ case 0x08: /* Leon3 Instruction Cache config */
+ case 0x0C: /* Leon3 Date Cache config */
+ if (env->def->features & CPU_FEATURE_CACHE_CTRL) {
+ leon3_cache_control_st(addr, val, size);
+ }
+ break;
+
+ case 0x01c00000: /* MXCC stream data register 0 */
+ if (size == 8)
+ env->mxccdata[0] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00008: /* MXCC stream data register 1 */
+ if (size == 8)
+ env->mxccdata[1] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00010: /* MXCC stream data register 2 */
+ if (size == 8)
+ env->mxccdata[2] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00018: /* MXCC stream data register 3 */
+ if (size == 8)
+ env->mxccdata[3] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00100: /* MXCC stream source */
+ if (size == 8)
+ env->mxccregs[0] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ env->mxccdata[0] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
+ 0);
+ env->mxccdata[1] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
+ 8);
+ env->mxccdata[2] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
+ 16);
+ env->mxccdata[3] = ldq_phys((env->mxccregs[0] & 0xffffffffULL) +
+ 24);
+ break;
+ case 0x01c00200: /* MXCC stream destination */
+ if (size == 8)
+ env->mxccregs[1] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ stq_phys((env->mxccregs[1] & 0xffffffffULL) + 0,
+ env->mxccdata[0]);
+ stq_phys((env->mxccregs[1] & 0xffffffffULL) + 8,
+ env->mxccdata[1]);
+ stq_phys((env->mxccregs[1] & 0xffffffffULL) + 16,
+ env->mxccdata[2]);
+ stq_phys((env->mxccregs[1] & 0xffffffffULL) + 24,
+ env->mxccdata[3]);
+ break;
+ case 0x01c00a00: /* MXCC control register */
+ if (size == 8)
+ env->mxccregs[3] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00a04: /* MXCC control register */
+ if (size == 4)
+ env->mxccregs[3] = (env->mxccregs[3] & 0xffffffff00000000ULL)
+ | val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00e00: /* MXCC error register */
+ // writing a 1 bit clears the error
+ if (size == 8)
+ env->mxccregs[6] &= ~val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ case 0x01c00f00: /* MBus port address register */
+ if (size == 8)
+ env->mxccregs[7] = val;
+ else
+ DPRINTF_MXCC("%08x: unimplemented access size: %d\n", addr,
+ size);
+ break;
+ default:
+ DPRINTF_MXCC("%08x: unimplemented address, size: %d\n", addr,
+ size);
+ break;
+ }
+ DPRINTF_MXCC("asi = %d, size = %d, addr = %08x, val = %" PRIx64 "\n",
+ asi, size, addr, val);
+#ifdef DEBUG_MXCC
+ dump_mxcc(env);
+#endif
+ break;
+ case 3: /* MMU flush */
+ {
+ int mmulev;
+
+ mmulev = (addr >> 8) & 15;
+ DPRINTF_MMU("mmu flush level %d\n", mmulev);
+ switch (mmulev) {
+ case 0: // flush page
+ tlb_flush_page(env, addr & 0xfffff000);
+ break;
+ case 1: // flush segment (256k)
+ case 2: // flush region (16M)
+ case 3: // flush context (4G)
+ case 4: // flush entire
+ tlb_flush(env, 1);
+ break;
+ default:
+ break;
+ }
+#ifdef DEBUG_MMU
+ dump_mmu(stdout, fprintf, env);
+#endif
+ }
+ break;
+ case 4: /* write MMU regs */
+ {
+ int reg = (addr >> 8) & 0x1f;
+ uint32_t oldreg;
+
+ oldreg = env->mmuregs[reg];
+ switch(reg) {
+ case 0: // Control Register
+ env->mmuregs[reg] = (env->mmuregs[reg] & 0xff000000) |
+ (val & 0x00ffffff);
+ // Mappings generated during no-fault mode or MMU
+ // disabled mode are invalid in normal mode
+ if ((oldreg & (MMU_E | MMU_NF | env->def->mmu_bm)) !=
+ (env->mmuregs[reg] & (MMU_E | MMU_NF | env->def->mmu_bm)))
+ tlb_flush(env, 1);
+ break;
+ case 1: // Context Table Pointer Register
+ env->mmuregs[reg] = val & env->def->mmu_ctpr_mask;
+ break;
+ case 2: // Context Register
+ env->mmuregs[reg] = val & env->def->mmu_cxr_mask;
+ if (oldreg != env->mmuregs[reg]) {
+ /* we flush when the MMU context changes because
+ QEMU has no MMU context support */
+ tlb_flush(env, 1);
+ }
+ break;
+ case 3: // Synchronous Fault Status Register with Clear
+ case 4: // Synchronous Fault Address Register
+ break;
+ case 0x10: // TLB Replacement Control Register
+ env->mmuregs[reg] = val & env->def->mmu_trcr_mask;
+ break;
+ case 0x13: // Synchronous Fault Status Register with Read and Clear
+ env->mmuregs[3] = val & env->def->mmu_sfsr_mask;
+ break;
+ case 0x14: // Synchronous Fault Address Register
+ env->mmuregs[4] = val;
+ break;
+ default:
+ env->mmuregs[reg] = val;
+ break;
+ }
+ if (oldreg != env->mmuregs[reg]) {
+ DPRINTF_MMU("mmu change reg[%d]: 0x%08x -> 0x%08x\n",
+ reg, oldreg, env->mmuregs[reg]);
+ }
+#ifdef DEBUG_MMU
+ dump_mmu(stdout, fprintf, env);
+#endif
+ }
+ break;
+ case 5: // Turbosparc ITLB Diagnostic
+ case 6: // Turbosparc DTLB Diagnostic
+ case 7: // Turbosparc IOTLB Diagnostic
+ break;
+ case 0xa: /* User data access */
+ switch(size) {
+ case 1:
+ stb_user(addr, val);
+ break;
+ case 2:
+ stw_user(addr, val);
+ break;
+ default:
+ case 4:
+ stl_user(addr, val);
+ break;
+ case 8:
+ stq_user(addr, val);
+ break;
+ }
+ break;
+ case 0xb: /* Supervisor data access */
+ switch(size) {
+ case 1:
+ stb_kernel(addr, val);
+ break;
+ case 2:
+ stw_kernel(addr, val);
+ break;
+ default:
+ case 4:
+ stl_kernel(addr, val);
+ break;
+ case 8:
+ stq_kernel(addr, val);
+ break;
+ }
+ break;
+ case 0xc: /* I-cache tag */
+ case 0xd: /* I-cache data */
+ case 0xe: /* D-cache tag */
+ case 0xf: /* D-cache data */
+ case 0x10: /* I/D-cache flush page */
+ case 0x11: /* I/D-cache flush segment */
+ case 0x12: /* I/D-cache flush region */
+ case 0x13: /* I/D-cache flush context */
+ case 0x14: /* I/D-cache flush user */
+ break;
+ case 0x17: /* Block copy, sta access */
+ {
+ // val = src
+ // addr = dst
+ // copy 32 bytes
+ unsigned int i;
+ uint32_t src = val & ~3, dst = addr & ~3, temp;
+
+ for (i = 0; i < 32; i += 4, src += 4, dst += 4) {
+ temp = ldl_kernel(src);
+ stl_kernel(dst, temp);
+ }
+ }
+ break;
+ case 0x1f: /* Block fill, stda access */
+ {
+ // addr = dst
+ // fill 32 bytes with val
+ unsigned int i;
+ uint32_t dst = addr & 7;
+
+ for (i = 0; i < 32; i += 8, dst += 8)
+ stq_kernel(dst, val);
+ }
+ break;
+ case 0x20: /* MMU passthrough */
+ {
+ switch(size) {
+ case 1:
+ stb_phys(addr, val);
+ break;
+ case 2:
+ stw_phys(addr, val);
+ break;
+ case 4:
+ default:
+ stl_phys(addr, val);
+ break;
+ case 8:
+ stq_phys(addr, val);
+ break;
+ }
+ }
+ break;
+ case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
+ {
+ switch(size) {
+ case 1:
+ stb_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32), val);
+ break;
+ case 2:
+ stw_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32), val);
+ break;
+ case 4:
+ default:
+ stl_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32), val);
+ break;
+ case 8:
+ stq_phys((target_phys_addr_t)addr
+ | ((target_phys_addr_t)(asi & 0xf) << 32), val);
+ break;
+ }
+ }
+ break;
+ case 0x30: // store buffer tags or Turbosparc secondary cache diagnostic
+ case 0x31: // store buffer data, Ross RT620 I-cache flush or
+ // Turbosparc snoop RAM
+ case 0x32: // store buffer control or Turbosparc page table
+ // descriptor diagnostic
+ case 0x36: /* I-cache flash clear */
+ case 0x37: /* D-cache flash clear */
+ case 0x4c: /* breakpoint action */
+ break;
+ case 0x38: /* SuperSPARC MMU Breakpoint Control Registers*/
+ {
+ int reg = (addr >> 8) & 3;
+
+ switch(reg) {
+ case 0: /* Breakpoint Value (Addr) */
+ env->mmubpregs[reg] = (val & 0xfffffffffULL);
+ break;
+ case 1: /* Breakpoint Mask */
+ env->mmubpregs[reg] = (val & 0xfffffffffULL);
+ break;
+ case 2: /* Breakpoint Control */
+ env->mmubpregs[reg] = (val & 0x7fULL);
+ break;
+ case 3: /* Breakpoint Status */
+ env->mmubpregs[reg] = (val & 0xfULL);
+ break;
+ }
+ DPRINTF_MMU("write breakpoint reg[%d] 0x%016x\n", reg,
+ env->mmuregs[reg]);
+ }
+ break;
+ case 8: /* User code access, XXX */
+ case 9: /* Supervisor code access, XXX */
+ default:
+ do_unassigned_access(addr, 1, 0, asi, size);
+ break;
+ }
+#ifdef DEBUG_ASI
+ dump_asi("write", addr, asi, size, val);
+#endif
+}
+
+#endif /* CONFIG_USER_ONLY */
+#else /* TARGET_SPARC64 */
+
+#ifdef CONFIG_USER_ONLY
+uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
+{
+ uint64_t ret = 0;
+#if defined(DEBUG_ASI)
+ target_ulong last_addr = addr;
+#endif
+
+ if (asi < 0x80)
+ raise_exception(TT_PRIV_ACT);
+
+ helper_check_align(addr, size - 1);
+ addr = asi_address_mask(env, asi, addr);
+
+ switch (asi) {
+ case 0x82: // Primary no-fault
+ case 0x8a: // Primary no-fault LE
+ if (page_check_range(addr, size, PAGE_READ) == -1) {
+#ifdef DEBUG_ASI
+ dump_asi("read ", last_addr, asi, size, ret);
+#endif
+ return 0;
+ }
+ // Fall through
+ case 0x80: // Primary
+ case 0x88: // Primary LE
+ {
+ switch(size) {
+ case 1:
+ ret = ldub_raw(addr);
+ break;
+ case 2:
+ ret = lduw_raw(addr);
+ break;
+ case 4:
+ ret = ldl_raw(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_raw(addr);
+ break;
+ }
+ }
+ break;
+ case 0x83: // Secondary no-fault
+ case 0x8b: // Secondary no-fault LE
+ if (page_check_range(addr, size, PAGE_READ) == -1) {
+#ifdef DEBUG_ASI
+ dump_asi("read ", last_addr, asi, size, ret);
+#endif
+ return 0;
+ }
+ // Fall through
+ case 0x81: // Secondary
+ case 0x89: // Secondary LE
+ // XXX
+ break;
+ default:
+ break;
+ }
+
+ /* Convert from little endian */
+ switch (asi) {
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ case 0x8a: // Primary no-fault LE
+ case 0x8b: // Secondary no-fault LE
+ switch(size) {
+ case 2:
+ ret = bswap16(ret);
+ break;
+ case 4:
+ ret = bswap32(ret);
+ break;
+ case 8:
+ ret = bswap64(ret);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Convert to signed number */
+ if (sign) {
+ switch(size) {
+ case 1:
+ ret = (int8_t) ret;
+ break;
+ case 2:
+ ret = (int16_t) ret;
+ break;
+ case 4:
+ ret = (int32_t) ret;
+ break;
+ default:
+ break;
+ }
+ }
+#ifdef DEBUG_ASI
+ dump_asi("read ", last_addr, asi, size, ret);
+#endif
+ return ret;
+}
+
+void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
+{
+#ifdef DEBUG_ASI
+ dump_asi("write", addr, asi, size, val);
+#endif
+ if (asi < 0x80)
+ raise_exception(TT_PRIV_ACT);
+
+ helper_check_align(addr, size - 1);
+ addr = asi_address_mask(env, asi, addr);
+
+ /* Convert to little endian */
+ switch (asi) {
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ switch(size) {
+ case 2:
+ val = bswap16(val);
+ break;
+ case 4:
+ val = bswap32(val);
+ break;
+ case 8:
+ val = bswap64(val);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch(asi) {
+ case 0x80: // Primary
+ case 0x88: // Primary LE
+ {
+ switch(size) {
+ case 1:
+ stb_raw(addr, val);
+ break;
+ case 2:
+ stw_raw(addr, val);
+ break;
+ case 4:
+ stl_raw(addr, val);
+ break;
+ case 8:
+ default:
+ stq_raw(addr, val);
+ break;
+ }
+ }
+ break;
+ case 0x81: // Secondary
+ case 0x89: // Secondary LE
+ // XXX
+ return;
+
+ case 0x82: // Primary no-fault, RO
+ case 0x83: // Secondary no-fault, RO
+ case 0x8a: // Primary no-fault LE, RO
+ case 0x8b: // Secondary no-fault LE, RO
+ default:
+ do_unassigned_access(addr, 1, 0, 1, size);
+ return;
+ }
+}
+
+#else /* CONFIG_USER_ONLY */
+
+uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign)
+{
+ uint64_t ret = 0;
+#if defined(DEBUG_ASI)
+ target_ulong last_addr = addr;
+#endif
+
+ asi &= 0xff;
+
+ if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
+ || (cpu_has_hypervisor(env)
+ && asi >= 0x30 && asi < 0x80
+ && !(env->hpstate & HS_PRIV)))
+ raise_exception(TT_PRIV_ACT);
+
+ helper_check_align(addr, size - 1);
+ addr = asi_address_mask(env, asi, addr);
+
+ switch (asi) {
+ case 0x82: // Primary no-fault
+ case 0x8a: // Primary no-fault LE
+ case 0x83: // Secondary no-fault
+ case 0x8b: // Secondary no-fault LE
+ {
+ /* secondary space access has lowest asi bit equal to 1 */
+ int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX
+ : MMU_KERNEL_SECONDARY_IDX;
+
+ if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) {
+#ifdef DEBUG_ASI
+ dump_asi("read ", last_addr, asi, size, ret);
+#endif
+ return 0;
+ }
+ }
+ // Fall through
+ case 0x10: // As if user primary
+ case 0x11: // As if user secondary
+ case 0x18: // As if user primary LE
+ case 0x19: // As if user secondary LE
+ case 0x80: // Primary
+ case 0x81: // Secondary
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ case 0xe2: // UA2007 Primary block init
+ case 0xe3: // UA2007 Secondary block init
+ if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
+ if (cpu_hypervisor_mode(env)) {
+ switch(size) {
+ case 1:
+ ret = ldub_hypv(addr);
+ break;
+ case 2:
+ ret = lduw_hypv(addr);
+ break;
+ case 4:
+ ret = ldl_hypv(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_hypv(addr);
+ break;
+ }
+ } else {
+ /* secondary space access has lowest asi bit equal to 1 */
+ if (asi & 1) {
+ switch(size) {
+ case 1:
+ ret = ldub_kernel_secondary(addr);
+ break;
+ case 2:
+ ret = lduw_kernel_secondary(addr);
+ break;
+ case 4:
+ ret = ldl_kernel_secondary(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_kernel_secondary(addr);
+ break;
+ }
+ } else {
+ switch(size) {
+ case 1:
+ ret = ldub_kernel(addr);
+ break;
+ case 2:
+ ret = lduw_kernel(addr);
+ break;
+ case 4:
+ ret = ldl_kernel(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_kernel(addr);
+ break;
+ }
+ }
+ }
+ } else {
+ /* secondary space access has lowest asi bit equal to 1 */
+ if (asi & 1) {
+ switch(size) {
+ case 1:
+ ret = ldub_user_secondary(addr);
+ break;
+ case 2:
+ ret = lduw_user_secondary(addr);
+ break;
+ case 4:
+ ret = ldl_user_secondary(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_user_secondary(addr);
+ break;
+ }
+ } else {
+ switch(size) {
+ case 1:
+ ret = ldub_user(addr);
+ break;
+ case 2:
+ ret = lduw_user(addr);
+ break;
+ case 4:
+ ret = ldl_user(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_user(addr);
+ break;
+ }
+ }
+ }
+ break;
+ case 0x14: // Bypass
+ case 0x15: // Bypass, non-cacheable
+ case 0x1c: // Bypass LE
+ case 0x1d: // Bypass, non-cacheable LE
+ {
+ switch(size) {
+ case 1:
+ ret = ldub_phys(addr);
+ break;
+ case 2:
+ ret = lduw_phys(addr);
+ break;
+ case 4:
+ ret = ldl_phys(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_phys(addr);
+ break;
+ }
+ break;
+ }
+ case 0x24: // Nucleus quad LDD 128 bit atomic
+ case 0x2c: // Nucleus quad LDD 128 bit atomic LE
+ // Only ldda allowed
+ raise_exception(TT_ILL_INSN);
+ return 0;
+ case 0x04: // Nucleus
+ case 0x0c: // Nucleus Little Endian (LE)
+ {
+ switch(size) {
+ case 1:
+ ret = ldub_nucleus(addr);
+ break;
+ case 2:
+ ret = lduw_nucleus(addr);
+ break;
+ case 4:
+ ret = ldl_nucleus(addr);
+ break;
+ default:
+ case 8:
+ ret = ldq_nucleus(addr);
+ break;
+ }
+ break;
+ }
+ case 0x4a: // UPA config
+ // XXX
+ break;
+ case 0x45: // LSU
+ ret = env->lsu;
+ break;
+ case 0x50: // I-MMU regs
+ {
+ int reg = (addr >> 3) & 0xf;
+
+ if (reg == 0) {
+ // I-TSB Tag Target register
+ ret = ultrasparc_tag_target(env->immu.tag_access);
+ } else {
+ ret = env->immuregs[reg];
+ }
+
+ break;
+ }
+ case 0x51: // I-MMU 8k TSB pointer
+ {
+ // env->immuregs[5] holds I-MMU TSB register value
+ // env->immuregs[6] holds I-MMU Tag Access register value
+ ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
+ 8*1024);
+ break;
+ }
+ case 0x52: // I-MMU 64k TSB pointer
+ {
+ // env->immuregs[5] holds I-MMU TSB register value
+ // env->immuregs[6] holds I-MMU Tag Access register value
+ ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access,
+ 64*1024);
+ break;
+ }
+ case 0x55: // I-MMU data access
+ {
+ int reg = (addr >> 3) & 0x3f;
+
+ ret = env->itlb[reg].tte;
+ break;
+ }
+ case 0x56: // I-MMU tag read
+ {
+ int reg = (addr >> 3) & 0x3f;
+
+ ret = env->itlb[reg].tag;
+ break;
+ }
+ case 0x58: // D-MMU regs
+ {
+ int reg = (addr >> 3) & 0xf;
+
+ if (reg == 0) {
+ // D-TSB Tag Target register
+ ret = ultrasparc_tag_target(env->dmmu.tag_access);
+ } else {
+ ret = env->dmmuregs[reg];
+ }
+ break;
+ }
+ case 0x59: // D-MMU 8k TSB pointer
+ {
+ // env->dmmuregs[5] holds D-MMU TSB register value
+ // env->dmmuregs[6] holds D-MMU Tag Access register value
+ ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
+ 8*1024);
+ break;
+ }
+ case 0x5a: // D-MMU 64k TSB pointer
+ {
+ // env->dmmuregs[5] holds D-MMU TSB register value
+ // env->dmmuregs[6] holds D-MMU Tag Access register value
+ ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access,
+ 64*1024);
+ break;
+ }
+ case 0x5d: // D-MMU data access
+ {
+ int reg = (addr >> 3) & 0x3f;
+
+ ret = env->dtlb[reg].tte;
+ break;
+ }
+ case 0x5e: // D-MMU tag read
+ {
+ int reg = (addr >> 3) & 0x3f;
+
+ ret = env->dtlb[reg].tag;
+ break;
+ }
+ case 0x46: // D-cache data
+ case 0x47: // D-cache tag access
+ case 0x4b: // E-cache error enable
+ case 0x4c: // E-cache asynchronous fault status
+ case 0x4d: // E-cache asynchronous fault address
+ case 0x4e: // E-cache tag data
+ case 0x66: // I-cache instruction access
+ case 0x67: // I-cache tag access
+ case 0x6e: // I-cache predecode
+ case 0x6f: // I-cache LRU etc.
+ case 0x76: // E-cache tag
+ case 0x7e: // E-cache tag
+ break;
+ case 0x5b: // D-MMU data pointer
+ case 0x48: // Interrupt dispatch, RO
+ case 0x49: // Interrupt data receive
+ case 0x7f: // Incoming interrupt vector, RO
+ // XXX
+ break;
+ case 0x54: // I-MMU data in, WO
+ case 0x57: // I-MMU demap, WO
+ case 0x5c: // D-MMU data in, WO
+ case 0x5f: // D-MMU demap, WO
+ case 0x77: // Interrupt vector, WO
+ default:
+ do_unassigned_access(addr, 0, 0, 1, size);
+ ret = 0;
+ break;
+ }
+
+ /* Convert from little endian */
+ switch (asi) {
+ case 0x0c: // Nucleus Little Endian (LE)
+ case 0x18: // As if user primary LE
+ case 0x19: // As if user secondary LE
+ case 0x1c: // Bypass LE
+ case 0x1d: // Bypass, non-cacheable LE
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ case 0x8a: // Primary no-fault LE
+ case 0x8b: // Secondary no-fault LE
+ switch(size) {
+ case 2:
+ ret = bswap16(ret);
+ break;
+ case 4:
+ ret = bswap32(ret);
+ break;
+ case 8:
+ ret = bswap64(ret);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Convert to signed number */
+ if (sign) {
+ switch(size) {
+ case 1:
+ ret = (int8_t) ret;
+ break;
+ case 2:
+ ret = (int16_t) ret;
+ break;
+ case 4:
+ ret = (int32_t) ret;
+ break;
+ default:
+ break;
+ }
+ }
+#ifdef DEBUG_ASI
+ dump_asi("read ", last_addr, asi, size, ret);
+#endif
+ return ret;
+}
+
+void helper_st_asi(target_ulong addr, target_ulong val, int asi, int size)
+{
+#ifdef DEBUG_ASI
+ dump_asi("write", addr, asi, size, val);
+#endif
+
+ asi &= 0xff;
+
+ if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
+ || (cpu_has_hypervisor(env)
+ && asi >= 0x30 && asi < 0x80
+ && !(env->hpstate & HS_PRIV)))
+ raise_exception(TT_PRIV_ACT);
+
+ helper_check_align(addr, size - 1);
+ addr = asi_address_mask(env, asi, addr);
+
+ /* Convert to little endian */
+ switch (asi) {
+ case 0x0c: // Nucleus Little Endian (LE)
+ case 0x18: // As if user primary LE
+ case 0x19: // As if user secondary LE
+ case 0x1c: // Bypass LE
+ case 0x1d: // Bypass, non-cacheable LE
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ switch(size) {
+ case 2:
+ val = bswap16(val);
+ break;
+ case 4:
+ val = bswap32(val);
+ break;
+ case 8:
+ val = bswap64(val);
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch(asi) {
+ case 0x10: // As if user primary
+ case 0x11: // As if user secondary
+ case 0x18: // As if user primary LE
+ case 0x19: // As if user secondary LE
+ case 0x80: // Primary
+ case 0x81: // Secondary
+ case 0x88: // Primary LE
+ case 0x89: // Secondary LE
+ case 0xe2: // UA2007 Primary block init
+ case 0xe3: // UA2007 Secondary block init
+ if ((asi & 0x80) && (env->pstate & PS_PRIV)) {
+ if (cpu_hypervisor_mode(env)) {
+ switch(size) {
+ case 1:
+ stb_hypv(addr, val);
+ break;
+ case 2:
+ stw_hypv(addr, val);
+ break;
+ case 4:
+ stl_hypv(addr, val);
+ break;
+ case 8:
+ default:
+ stq_hypv(addr, val);
+ break;
+ }
+ } else {
+ /* secondary space access has lowest asi bit equal to 1 */
+ if (asi & 1) {
+ switch(size) {
+ case 1:
+ stb_kernel_secondary(addr, val);
+ break;
+ case 2:
+ stw_kernel_secondary(addr, val);
+ break;
+ case 4:
+ stl_kernel_secondary(addr, val);
+ break;
+ case 8:
+ default:
+ stq_kernel_secondary(addr, val);
+ break;
+ }
+ } else {
+ switch(size) {
+ case 1:
+ stb_kernel(addr, val);
+ break;
+ case 2:
+ stw_kernel(addr, val);
+ break;
+ case 4:
+ stl_kernel(addr, val);
+ break;
+ case 8:
+ default:
+ stq_kernel(addr, val);
+ break;
+ }
+ }
+ }
+ } else {
+ /* secondary space access has lowest asi bit equal to 1 */
+ if (asi & 1) {
+ switch(size) {
+ case 1:
+ stb_user_secondary(addr, val);
+ break;
+ case 2:
+ stw_user_secondary(addr, val);
+ break;
+ case 4:
+ stl_user_secondary(addr, val);
+ break;
+ case 8:
+ default:
+ stq_user_secondary(addr, val);
+ break;
+ }
+ } else {
+ switch(size) {
+ case 1:
+ stb_user(addr, val);
+ break;
+ case 2:
+ stw_user(addr, val);
+ break;
+ case 4:
+ stl_user(addr, val);
+ break;
+ case 8:
+ default:
+ stq_user(addr, val);
+ break;
+ }
+ }
+ }
+ break;
+ case 0x14: // Bypass
+ case 0x15: // Bypass, non-cacheable
+ case 0x1c: // Bypass LE
+ case 0x1d: // Bypass, non-cacheable LE
+ {
+ switch(size) {
+ case 1:
+ stb_phys(addr, val);
+ break;
+ case 2:
+ stw_phys(addr, val);
+ break;
+ case 4:
+ stl_phys(addr, val);
+ break;
+ case 8:
+ default:
+ stq_phys(addr, val);
+ break;
+ }
+ }
+ return;
+ case 0x24: // Nucleus quad LDD 128 bit atomic
+ case 0x2c: // Nucleus quad LDD 128 bit atomic LE
+ // Only ldda allowed
+ raise_exception(TT_ILL_INSN);
+ return;
+ case 0x04: // Nucleus
+ case 0x0c: // Nucleus Little Endian (LE)
+ {
+ switch(size) {
+ case 1:
+ stb_nucleus(addr, val);
+ break;
+ case 2:
+ stw_nucleus(addr, val);
+ break;
+ case 4:
+ stl_nucleus(addr, val);
+ break;
+ default:
+ case 8:
+ stq_nucleus(addr, val);
+ break;
+ }
+ break;
+ }
+
+ case 0x4a: // UPA config
+ // XXX
+ return;
+ case 0x45: // LSU
+ {
+ uint64_t oldreg;
+
+ oldreg = env->lsu;
+ env->lsu = val & (DMMU_E | IMMU_E);
+ // Mappings generated during D/I MMU disabled mode are
+ // invalid in normal mode
+ if (oldreg != env->lsu) {
+ DPRINTF_MMU("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n",
+ oldreg, env->lsu);
+#ifdef DEBUG_MMU
+ dump_mmu(stdout, fprintf, env1);
+#endif
+ tlb_flush(env, 1);
+ }
+ return;
+ }
+ case 0x50: // I-MMU regs
+ {
+ int reg = (addr >> 3) & 0xf;
+ uint64_t oldreg;
+
+ oldreg = env->immuregs[reg];
+ switch(reg) {
+ case 0: // RO
+ return;
+ case 1: // Not in I-MMU
+ case 2:
+ return;
+ case 3: // SFSR
+ if ((val & 1) == 0)
+ val = 0; // Clear SFSR
+ env->immu.sfsr = val;
+ break;
+ case 4: // RO
+ return;
+ case 5: // TSB access
+ DPRINTF_MMU("immu TSB write: 0x%016" PRIx64 " -> 0x%016"
+ PRIx64 "\n", env->immu.tsb, val);
+ env->immu.tsb = val;
+ break;
+ case 6: // Tag access
+ env->immu.tag_access = val;
+ break;
+ case 7:
+ case 8:
+ return;
+ default:
+ break;
+ }
+
+ if (oldreg != env->immuregs[reg]) {
+ DPRINTF_MMU("immu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
+ PRIx64 "\n", reg, oldreg, env->immuregs[reg]);
+ }
+#ifdef DEBUG_MMU
+ dump_mmu(stdout, fprintf, env);
+#endif
+ return;
+ }
+ case 0x54: // I-MMU data in
+ replace_tlb_1bit_lru(env->itlb, env->immu.tag_access, val, "immu", env);
+ return;
+ case 0x55: // I-MMU data access
+ {
+ // TODO: auto demap
+
+ unsigned int i = (addr >> 3) & 0x3f;
+
+ replace_tlb_entry(&env->itlb[i], env->immu.tag_access, val, env);
+
+#ifdef DEBUG_MMU
+ DPRINTF_MMU("immu data access replaced entry [%i]\n", i);
+ dump_mmu(stdout, fprintf, env);
+#endif
+ return;
+ }
+ case 0x57: // I-MMU demap
+ demap_tlb(env->itlb, addr, "immu", env);
+ return;
+ case 0x58: // D-MMU regs
+ {
+ int reg = (addr >> 3) & 0xf;
+ uint64_t oldreg;
+
+ oldreg = env->dmmuregs[reg];
+ switch(reg) {
+ case 0: // RO
+ case 4:
+ return;
+ case 3: // SFSR
+ if ((val & 1) == 0) {
+ val = 0; // Clear SFSR, Fault address
+ env->dmmu.sfar = 0;
+ }
+ env->dmmu.sfsr = val;
+ break;
+ case 1: // Primary context
+ env->dmmu.mmu_primary_context = val;
+ /* can be optimized to only flush MMU_USER_IDX
+ and MMU_KERNEL_IDX entries */
+ tlb_flush(env, 1);
+ break;
+ case 2: // Secondary context
+ env->dmmu.mmu_secondary_context = val;
+ /* can be optimized to only flush MMU_USER_SECONDARY_IDX
+ and MMU_KERNEL_SECONDARY_IDX entries */
+ tlb_flush(env, 1);
+ break;
+ case 5: // TSB access
+ DPRINTF_MMU("dmmu TSB write: 0x%016" PRIx64 " -> 0x%016"
+ PRIx64 "\n", env->dmmu.tsb, val);
+ env->dmmu.tsb = val;
+ break;
+ case 6: // Tag access
+ env->dmmu.tag_access = val;
+ break;
+ case 7: // Virtual Watchpoint
+ case 8: // Physical Watchpoint
+ default:
+ env->dmmuregs[reg] = val;
+ break;
+ }
+
+ if (oldreg != env->dmmuregs[reg]) {
+ DPRINTF_MMU("dmmu change reg[%d]: 0x%016" PRIx64 " -> 0x%016"
+ PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]);
+ }
+#ifdef DEBUG_MMU
+ dump_mmu(stdout, fprintf, env);
+#endif
+ return;
+ }
+ case 0x5c: // D-MMU data in
+ replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access, val, "dmmu", env);
+ return;
+ case 0x5d: // D-MMU data access
+ {
+ unsigned int i = (addr >> 3) & 0x3f;
+
+ replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access, val, env);
+
+#ifdef DEBUG_MMU
+ DPRINTF_MMU("dmmu data access replaced entry [%i]\n", i);
+ dump_mmu(stdout, fprintf, env);
+#endif
+ return;
+ }
+ case 0x5f: // D-MMU demap
+ demap_tlb(env->dtlb, addr, "dmmu", env);
+ return;
+ case 0x49: // Interrupt data receive
+ // XXX
+ return;
+ case 0x46: // D-cache data
+ case 0x47: // D-cache tag access
+ case 0x4b: // E-cache error enable
+ case 0x4c: // E-cache asynchronous fault status
+ case 0x4d: // E-cache asynchronous fault address
+ case 0x4e: // E-cache tag data
+ case 0x66: // I-cache instruction access
+ case 0x67: // I-cache tag access
+ case 0x6e: // I-cache predecode
+ case 0x6f: // I-cache LRU etc.
+ case 0x76: // E-cache tag
+ case 0x7e: // E-cache tag
+ return;
+ case 0x51: // I-MMU 8k TSB pointer, RO
+ case 0x52: // I-MMU 64k TSB pointer, RO
+ case 0x56: // I-MMU tag read, RO
+ case 0x59: // D-MMU 8k TSB pointer, RO
+ case 0x5a: // D-MMU 64k TSB pointer, RO
+ case 0x5b: // D-MMU data pointer, RO
+ case 0x5e: // D-MMU tag read, RO
+ case 0x48: // Interrupt dispatch, RO
+ case 0x7f: // Incoming interrupt vector, RO
+ case 0x82: // Primary no-fault, RO
+ case 0x83: // Secondary no-fault, RO
+ case 0x8a: // Primary no-fault LE, RO
+ case 0x8b: // Secondary no-fault LE, RO
+ default:
+ do_unassigned_access(addr, 1, 0, 1, size);
+ return;
+ }
+}
+#endif /* CONFIG_USER_ONLY */
+
+void helper_ldda_asi(target_ulong addr, int asi, int rd)
+{
+ if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0)
+ || (cpu_has_hypervisor(env)
+ && asi >= 0x30 && asi < 0x80
+ && !(env->hpstate & HS_PRIV)))
+ raise_exception(TT_PRIV_ACT);
+
+ addr = asi_address_mask(env, asi, addr);
+
+ switch (asi) {
+#if !defined(CONFIG_USER_ONLY)
+ case 0x24: // Nucleus quad LDD 128 bit atomic
+ case 0x2c: // Nucleus quad LDD 128 bit atomic LE
+ helper_check_align(addr, 0xf);
+ if (rd == 0) {
+ env->gregs[1] = ldq_nucleus(addr + 8);
+ if (asi == 0x2c)
+ bswap64s(&env->gregs[1]);
+ } else if (rd < 8) {
+ env->gregs[rd] = ldq_nucleus(addr);
+ env->gregs[rd + 1] = ldq_nucleus(addr + 8);
+ if (asi == 0x2c) {
+ bswap64s(&env->gregs[rd]);
+ bswap64s(&env->gregs[rd + 1]);
+ }
+ } else {
+ env->regwptr[rd] = ldq_nucleus(addr);
+ env->regwptr[rd + 1] = ldq_nucleus(addr + 8);
+ if (asi == 0x2c) {
+ bswap64s(&env->regwptr[rd]);
+ bswap64s(&env->regwptr[rd + 1]);
+ }
+ }
+ break;
+#endif
+ default:
+ helper_check_align(addr, 0x3);
+ if (rd == 0)
+ env->gregs[1] = helper_ld_asi(addr + 4, asi, 4, 0);
+ else if (rd < 8) {
+ env->gregs[rd] = helper_ld_asi(addr, asi, 4, 0);
+ env->gregs[rd + 1] = helper_ld_asi(addr + 4, asi, 4, 0);
+ } else {
+ env->regwptr[rd] = helper_ld_asi(addr, asi, 4, 0);
+ env->regwptr[rd + 1] = helper_ld_asi(addr + 4, asi, 4, 0);
+ }
+ break;
+ }
+}
+
+void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
+{
+ unsigned int i;
+ target_ulong val;
+
+ helper_check_align(addr, 3);
+ addr = asi_address_mask(env, asi, addr);
+
+ switch (asi) {
+ case 0xf0: // Block load primary
+ case 0xf1: // Block load secondary
+ case 0xf8: // Block load primary LE
+ case 0xf9: // Block load secondary LE
+ if (rd & 7) {
+ raise_exception(TT_ILL_INSN);
+ return;
+ }
+ helper_check_align(addr, 0x3f);
+ for (i = 0; i < 16; i++) {
+ *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x8f, 4,
+ 0);
+ addr += 4;
+ }
+
+ return;
+ case 0x70: // Block load primary, user privilege
+ case 0x71: // Block load secondary, user privilege
+ if (rd & 7) {
+ raise_exception(TT_ILL_INSN);
+ return;
+ }
+ helper_check_align(addr, 0x3f);
+ for (i = 0; i < 16; i++) {
+ *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x1f, 4,
+ 0);
+ addr += 4;
+ }
+
+ return;
+ default:
+ break;
+ }
+
+ val = helper_ld_asi(addr, asi, size, 0);
+ switch(size) {
+ default:
+ case 4:
+ *((uint32_t *)&env->fpr[rd]) = val;
+ break;
+ case 8:
+ *((int64_t *)&DT0) = val;
+ break;
+ case 16:
+ // XXX
+ break;
+ }
+}
+
+void helper_stf_asi(target_ulong addr, int asi, int size, int rd)
+{
+ unsigned int i;
+ target_ulong val = 0;
+
+ helper_check_align(addr, 3);
+ addr = asi_address_mask(env, asi, addr);
+
+ switch (asi) {
+ case 0xe0: // UA2007 Block commit store primary (cache flush)
+ case 0xe1: // UA2007 Block commit store secondary (cache flush)
+ case 0xf0: // Block store primary
+ case 0xf1: // Block store secondary
+ case 0xf8: // Block store primary LE
+ case 0xf9: // Block store secondary LE
+ if (rd & 7) {
+ raise_exception(TT_ILL_INSN);
+ return;
+ }
+ helper_check_align(addr, 0x3f);
+ for (i = 0; i < 16; i++) {
+ val = *(uint32_t *)&env->fpr[rd++];
+ helper_st_asi(addr, val, asi & 0x8f, 4);
+ addr += 4;
+ }
+
+ return;
+ case 0x70: // Block store primary, user privilege
+ case 0x71: // Block store secondary, user privilege
+ if (rd & 7) {
+ raise_exception(TT_ILL_INSN);
+ return;
+ }
+ helper_check_align(addr, 0x3f);
+ for (i = 0; i < 16; i++) {
+ val = *(uint32_t *)&env->fpr[rd++];
+ helper_st_asi(addr, val, asi & 0x1f, 4);
+ addr += 4;
+ }
+
+ return;
+ default:
+ break;
+ }
+
+ switch(size) {
+ default:
+ case 4:
+ val = *((uint32_t *)&env->fpr[rd]);
+ break;
+ case 8:
+ val = *((int64_t *)&DT0);
+ break;
+ case 16:
+ // XXX
+ break;
+ }
+ helper_st_asi(addr, val, asi, size);
+}
+
+target_ulong helper_cas_asi(target_ulong addr, target_ulong val1,
+ target_ulong val2, uint32_t asi)
+{
+ target_ulong ret;
+
+ val2 &= 0xffffffffUL;
+ ret = helper_ld_asi(addr, asi, 4, 0);
+ ret &= 0xffffffffUL;
+ if (val2 == ret)
+ helper_st_asi(addr, val1 & 0xffffffffUL, asi, 4);
+ return ret;
+}
+
+target_ulong helper_casx_asi(target_ulong addr, target_ulong val1,
+ target_ulong val2, uint32_t asi)
+{
+ target_ulong ret;
+
+ ret = helper_ld_asi(addr, asi, 8, 0);
+ if (val2 == ret)
+ helper_st_asi(addr, val1, asi, 8);
+ return ret;
+}
+#endif /* TARGET_SPARC64 */
+
+#ifndef TARGET_SPARC64
+void helper_rett(void)
+{
+ unsigned int cwp;
+
+ if (env->psret == 1)
+ raise_exception(TT_ILL_INSN);
+
+ env->psret = 1;
+ cwp = cwp_inc(env->cwp + 1) ;
+ if (env->wim & (1 << cwp)) {
+ raise_exception(TT_WIN_UNF);
+ }
+ set_cwp(cwp);
+ env->psrs = env->psrps;
+}
+#endif
+
+static target_ulong helper_udiv_common(target_ulong a, target_ulong b, int cc)
+{
+ int overflow = 0;
+ uint64_t x0;
+ uint32_t x1;
+
+ x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
+ x1 = (b & 0xffffffff);
+
+ if (x1 == 0) {
+ raise_exception(TT_DIV_ZERO);
+ }
+
+ x0 = x0 / x1;
+ if (x0 > 0xffffffff) {
+ x0 = 0xffffffff;
+ overflow = 1;
+ }
+
+ if (cc) {
+ env->cc_dst = x0;
+ env->cc_src2 = overflow;
+ env->cc_op = CC_OP_DIV;
+ }
+ return x0;
+}
+
+target_ulong helper_udiv(target_ulong a, target_ulong b)
+{
+ return helper_udiv_common(a, b, 0);
+}
+
+target_ulong helper_udiv_cc(target_ulong a, target_ulong b)
+{
+ return helper_udiv_common(a, b, 1);
+}
+
+static target_ulong helper_sdiv_common(target_ulong a, target_ulong b, int cc)
+{
+ int overflow = 0;
+ int64_t x0;
+ int32_t x1;
+
+ x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
+ x1 = (b & 0xffffffff);
+
+ if (x1 == 0) {
+ raise_exception(TT_DIV_ZERO);
+ }
+
+ x0 = x0 / x1;
+ if ((int32_t) x0 != x0) {
+ x0 = x0 < 0 ? 0x80000000: 0x7fffffff;
+ overflow = 1;
+ }
+
+ if (cc) {
+ env->cc_dst = x0;
+ env->cc_src2 = overflow;
+ env->cc_op = CC_OP_DIV;
+ }
+ return x0;
+}
+
+target_ulong helper_sdiv(target_ulong a, target_ulong b)
+{
+ return helper_sdiv_common(a, b, 0);
+}
+
+target_ulong helper_sdiv_cc(target_ulong a, target_ulong b)
+{
+ return helper_sdiv_common(a, b, 1);
+}
+
+void helper_stdf(target_ulong addr, int mem_idx)
+{
+ helper_check_align(addr, 7);
+#if !defined(CONFIG_USER_ONLY)
+ switch (mem_idx) {
+ case MMU_USER_IDX:
+ stfq_user(addr, DT0);
+ break;
+ case MMU_KERNEL_IDX:
+ stfq_kernel(addr, DT0);
+ break;
+#ifdef TARGET_SPARC64
+ case MMU_HYPV_IDX:
+ stfq_hypv(addr, DT0);
+ break;
+#endif
+ default:
+ DPRINTF_MMU("helper_stdf: need to check MMU idx %d\n", mem_idx);
+ break;
+ }
+#else
+ stfq_raw(address_mask(env, addr), DT0);
+#endif
+}
+
+void helper_lddf(target_ulong addr, int mem_idx)
+{
+ helper_check_align(addr, 7);
+#if !defined(CONFIG_USER_ONLY)
+ switch (mem_idx) {
+ case MMU_USER_IDX:
+ DT0 = ldfq_user(addr);
+ break;
+ case MMU_KERNEL_IDX:
+ DT0 = ldfq_kernel(addr);
+ break;
+#ifdef TARGET_SPARC64
+ case MMU_HYPV_IDX:
+ DT0 = ldfq_hypv(addr);
+ break;
+#endif
+ default:
+ DPRINTF_MMU("helper_lddf: need to check MMU idx %d\n", mem_idx);
+ break;
+ }
+#else
+ DT0 = ldfq_raw(address_mask(env, addr));
+#endif
+}
+
+void helper_ldqf(target_ulong addr, int mem_idx)
+{
+ // XXX add 128 bit load
+ CPU_QuadU u;
+
+ helper_check_align(addr, 7);
+#if !defined(CONFIG_USER_ONLY)
+ switch (mem_idx) {
+ case MMU_USER_IDX:
+ u.ll.upper = ldq_user(addr);
+ u.ll.lower = ldq_user(addr + 8);
+ QT0 = u.q;
+ break;
+ case MMU_KERNEL_IDX:
+ u.ll.upper = ldq_kernel(addr);
+ u.ll.lower = ldq_kernel(addr + 8);
+ QT0 = u.q;
+ break;
+#ifdef TARGET_SPARC64
+ case MMU_HYPV_IDX:
+ u.ll.upper = ldq_hypv(addr);
+ u.ll.lower = ldq_hypv(addr + 8);
+ QT0 = u.q;
+ break;
+#endif
+ default:
+ DPRINTF_MMU("helper_ldqf: need to check MMU idx %d\n", mem_idx);
+ break;
+ }
+#else
+ u.ll.upper = ldq_raw(address_mask(env, addr));
+ u.ll.lower = ldq_raw(address_mask(env, addr + 8));
+ QT0 = u.q;
+#endif
+}
+
+void helper_stqf(target_ulong addr, int mem_idx)
+{
+ // XXX add 128 bit store
+ CPU_QuadU u;
+
+ helper_check_align(addr, 7);
+#if !defined(CONFIG_USER_ONLY)
+ switch (mem_idx) {
+ case MMU_USER_IDX:
+ u.q = QT0;
+ stq_user(addr, u.ll.upper);
+ stq_user(addr + 8, u.ll.lower);
+ break;
+ case MMU_KERNEL_IDX:
+ u.q = QT0;
+ stq_kernel(addr, u.ll.upper);
+ stq_kernel(addr + 8, u.ll.lower);
+ break;
+#ifdef TARGET_SPARC64
+ case MMU_HYPV_IDX:
+ u.q = QT0;
+ stq_hypv(addr, u.ll.upper);
+ stq_hypv(addr + 8, u.ll.lower);
+ break;
+#endif
+ default:
+ DPRINTF_MMU("helper_stqf: need to check MMU idx %d\n", mem_idx);
+ break;
+ }
+#else
+ u.q = QT0;
+ stq_raw(address_mask(env, addr), u.ll.upper);
+ stq_raw(address_mask(env, addr + 8), u.ll.lower);
+#endif
+}
+
+static inline void set_fsr(void)
+{
+ int rnd_mode;
+
+ switch (env->fsr & FSR_RD_MASK) {
+ case FSR_RD_NEAREST:
+ rnd_mode = float_round_nearest_even;
+ break;
+ default:
+ case FSR_RD_ZERO:
+ rnd_mode = float_round_to_zero;
+ break;
+ case FSR_RD_POS:
+ rnd_mode = float_round_up;
+ break;
+ case FSR_RD_NEG:
+ rnd_mode = float_round_down;
+ break;
+ }
+ set_float_rounding_mode(rnd_mode, &env->fp_status);
+}
+
+void helper_ldfsr(uint32_t new_fsr)
+{
+ env->fsr = (new_fsr & FSR_LDFSR_MASK) | (env->fsr & FSR_LDFSR_OLDMASK);
+ set_fsr();
+}
+
+#ifdef TARGET_SPARC64
+void helper_ldxfsr(uint64_t new_fsr)
+{
+ env->fsr = (new_fsr & FSR_LDXFSR_MASK) | (env->fsr & FSR_LDXFSR_OLDMASK);
+ set_fsr();
+}
+#endif
+
+void helper_debug(void)
+{
+ env->exception_index = EXCP_DEBUG;
+ cpu_loop_exit();
+}
+
+#ifndef TARGET_SPARC64
+/* XXX: use another pointer for %iN registers to avoid slow wrapping
+ handling ? */
+void helper_save(void)
+{
+ uint32_t cwp;
+
+ cwp = cwp_dec(env->cwp - 1);
+ if (env->wim & (1 << cwp)) {
+ raise_exception(TT_WIN_OVF);
+ }
+ set_cwp(cwp);
+}
+
+void helper_restore(void)
+{
+ uint32_t cwp;
+
+ cwp = cwp_inc(env->cwp + 1);
+ if (env->wim & (1 << cwp)) {
+ raise_exception(TT_WIN_UNF);
+ }
+ set_cwp(cwp);
+}
+
+void helper_wrpsr(target_ulong new_psr)
+{
+ if ((new_psr & PSR_CWP) >= env->nwindows) {
+ raise_exception(TT_ILL_INSN);
+ } else {
+ cpu_put_psr(env, new_psr);
+ }
+}
+
+target_ulong helper_rdpsr(void)
+{
+ return get_psr();
+}
+
+#else
+/* XXX: use another pointer for %iN registers to avoid slow wrapping
+ handling ? */
+void helper_save(void)
+{
+ uint32_t cwp;
+
+ cwp = cwp_dec(env->cwp - 1);
+ if (env->cansave == 0) {
+ raise_exception(TT_SPILL | (env->otherwin != 0 ?
+ (TT_WOTHER | ((env->wstate & 0x38) >> 1)):
+ ((env->wstate & 0x7) << 2)));
+ } else {
+ if (env->cleanwin - env->canrestore == 0) {
+ // XXX Clean windows without trap
+ raise_exception(TT_CLRWIN);
+ } else {
+ env->cansave--;
+ env->canrestore++;
+ set_cwp(cwp);
+ }
+ }
+}
+
+void helper_restore(void)
+{
+ uint32_t cwp;
+
+ cwp = cwp_inc(env->cwp + 1);
+ if (env->canrestore == 0) {
+ raise_exception(TT_FILL | (env->otherwin != 0 ?
+ (TT_WOTHER | ((env->wstate & 0x38) >> 1)):
+ ((env->wstate & 0x7) << 2)));
+ } else {
+ env->cansave++;
+ env->canrestore--;
+ set_cwp(cwp);
+ }
+}
+
+void helper_flushw(void)
+{
+ if (env->cansave != env->nwindows - 2) {
+ raise_exception(TT_SPILL | (env->otherwin != 0 ?
+ (TT_WOTHER | ((env->wstate & 0x38) >> 1)):
+ ((env->wstate & 0x7) << 2)));
+ }
+}
+
+void helper_saved(void)
+{
+ env->cansave++;
+ if (env->otherwin == 0)
+ env->canrestore--;
+ else
+ env->otherwin--;
+}
+
+void helper_restored(void)
+{
+ env->canrestore++;
+ if (env->cleanwin < env->nwindows - 1)
+ env->cleanwin++;
+ if (env->otherwin == 0)
+ env->cansave--;
+ else
+ env->otherwin--;
+}
+
+static target_ulong get_ccr(void)
+{
+ target_ulong psr;
+
+ psr = get_psr();
+
+ return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20);
+}
+
+target_ulong cpu_get_ccr(CPUState *env1)
+{
+ CPUState *saved_env;
+ target_ulong ret;
+
+ saved_env = env;
+ env = env1;
+ ret = get_ccr();
+ env = saved_env;
+ return ret;
+}
+
+static void put_ccr(target_ulong val)
+{
+ target_ulong tmp = val;
+
+ env->xcc = (tmp >> 4) << 20;
+ env->psr = (tmp & 0xf) << 20;
+ CC_OP = CC_OP_FLAGS;
+}
+
+void cpu_put_ccr(CPUState *env1, target_ulong val)
+{
+ CPUState *saved_env;
+
+ saved_env = env;
+ env = env1;
+ put_ccr(val);
+ env = saved_env;
+}
+
+static target_ulong get_cwp64(void)
+{
+ return env->nwindows - 1 - env->cwp;
+}
+
+target_ulong cpu_get_cwp64(CPUState *env1)
+{
+ CPUState *saved_env;
+ target_ulong ret;
+
+ saved_env = env;
+ env = env1;
+ ret = get_cwp64();
+ env = saved_env;
+ return ret;
+}
+
+static void put_cwp64(int cwp)
+{
+ if (unlikely(cwp >= env->nwindows || cwp < 0)) {
+ cwp %= env->nwindows;
+ }
+ set_cwp(env->nwindows - 1 - cwp);
+}
+
+void cpu_put_cwp64(CPUState *env1, int cwp)
+{
+ CPUState *saved_env;
+
+ saved_env = env;
+ env = env1;
+ put_cwp64(cwp);
+ env = saved_env;
+}
+
+target_ulong helper_rdccr(void)
+{
+ return get_ccr();
+}
+
+void helper_wrccr(target_ulong new_ccr)
+{
+ put_ccr(new_ccr);
+}
+
+// CWP handling is reversed in V9, but we still use the V8 register
+// order.
+target_ulong helper_rdcwp(void)
+{
+ return get_cwp64();
+}
+
+void helper_wrcwp(target_ulong new_cwp)
+{
+ put_cwp64(new_cwp);
+}
+
+// This function uses non-native bit order
+#define GET_FIELD(X, FROM, TO) \
+ ((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1))
+
+// This function uses the order in the manuals, i.e. bit 0 is 2^0
+#define GET_FIELD_SP(X, FROM, TO) \
+ GET_FIELD(X, 63 - (TO), 63 - (FROM))
+
+target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
+{
+ return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) |
+ (GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) |
+ (GET_FIELD_SP(pixel_addr, 17 + cubesize - 1, 17) << 17) |
+ (GET_FIELD_SP(pixel_addr, 56, 59) << 13) |
+ (GET_FIELD_SP(pixel_addr, 35, 38) << 9) |
+ (GET_FIELD_SP(pixel_addr, 13, 16) << 5) |
+ (((pixel_addr >> 55) & 1) << 4) |
+ (GET_FIELD_SP(pixel_addr, 33, 34) << 2) |
+ GET_FIELD_SP(pixel_addr, 11, 12);
+}
+
+target_ulong helper_alignaddr(target_ulong addr, target_ulong offset)
+{
+ uint64_t tmp;
+
+ tmp = addr + offset;
+ env->gsr &= ~7ULL;
+ env->gsr |= tmp & 7ULL;
+ return tmp & ~7ULL;
+}
+
+target_ulong helper_popc(target_ulong val)
+{
+ return ctpop64(val);
+}
+
+static inline uint64_t *get_gregset(uint32_t pstate)
+{
+ switch (pstate) {
+ default:
+ DPRINTF_PSTATE("ERROR in get_gregset: active pstate bits=%x%s%s%s\n",
+ pstate,
+ (pstate & PS_IG) ? " IG" : "",
+ (pstate & PS_MG) ? " MG" : "",
+ (pstate & PS_AG) ? " AG" : "");
+ /* pass through to normal set of global registers */
+ case 0:
+ return env->bgregs;
+ case PS_AG:
+ return env->agregs;
+ case PS_MG:
+ return env->mgregs;
+ case PS_IG:
+ return env->igregs;
+ }
+}
+
+static inline void change_pstate(uint32_t new_pstate)
+{
+ uint32_t pstate_regs, new_pstate_regs;
+ uint64_t *src, *dst;
+
+ if (env->def->features & CPU_FEATURE_GL) {
+ // PS_AG is not implemented in this case
+ new_pstate &= ~PS_AG;
+ }
+
+ pstate_regs = env->pstate & 0xc01;
+ new_pstate_regs = new_pstate & 0xc01;
+
+ if (new_pstate_regs != pstate_regs) {
+ DPRINTF_PSTATE("change_pstate: switching regs old=%x new=%x\n",
+ pstate_regs, new_pstate_regs);
+ // Switch global register bank
+ src = get_gregset(new_pstate_regs);
+ dst = get_gregset(pstate_regs);
+ memcpy32(dst, env->gregs);
+ memcpy32(env->gregs, src);
+ }
+ else {
+ DPRINTF_PSTATE("change_pstate: regs new=%x (unchanged)\n",
+ new_pstate_regs);
+ }
+ env->pstate = new_pstate;
+}
+
+void helper_wrpstate(target_ulong new_state)
+{
+ change_pstate(new_state & 0xf3f);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (cpu_interrupts_enabled(env)) {
+ cpu_check_irqs(env);
+ }
+#endif
+}
+
+void helper_wrpil(target_ulong new_pil)
+{
+#if !defined(CONFIG_USER_ONLY)
+ DPRINTF_PSTATE("helper_wrpil old=%x new=%x\n",
+ env->psrpil, (uint32_t)new_pil);
+
+ env->psrpil = new_pil;
+
+ if (cpu_interrupts_enabled(env)) {
+ cpu_check_irqs(env);
+ }
+#endif
+}
+
+void helper_done(void)
+{
+ trap_state* tsptr = cpu_tsptr(env);
+
+ env->pc = tsptr->tnpc;
+ env->npc = tsptr->tnpc + 4;
+ put_ccr(tsptr->tstate >> 32);
+ env->asi = (tsptr->tstate >> 24) & 0xff;
+ change_pstate((tsptr->tstate >> 8) & 0xf3f);
+ put_cwp64(tsptr->tstate & 0xff);
+ env->tl--;
+
+ DPRINTF_PSTATE("... helper_done tl=%d\n", env->tl);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (cpu_interrupts_enabled(env)) {
+ cpu_check_irqs(env);
+ }
+#endif
+}
+
+void helper_retry(void)
+{
+ trap_state* tsptr = cpu_tsptr(env);
+
+ env->pc = tsptr->tpc;
+ env->npc = tsptr->tnpc;
+ put_ccr(tsptr->tstate >> 32);
+ env->asi = (tsptr->tstate >> 24) & 0xff;
+ change_pstate((tsptr->tstate >> 8) & 0xf3f);
+ put_cwp64(tsptr->tstate & 0xff);
+ env->tl--;
+
+ DPRINTF_PSTATE("... helper_retry tl=%d\n", env->tl);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (cpu_interrupts_enabled(env)) {
+ cpu_check_irqs(env);
+ }
+#endif
+}
+
+static void do_modify_softint(const char* operation, uint32_t value)
+{
+ if (env->softint != value) {
+ env->softint = value;
+ DPRINTF_PSTATE(": %s new %08x\n", operation, env->softint);
+#if !defined(CONFIG_USER_ONLY)
+ if (cpu_interrupts_enabled(env)) {
+ cpu_check_irqs(env);
+ }
+#endif
+ }
+}
+
+void helper_set_softint(uint64_t value)
+{
+ do_modify_softint("helper_set_softint", env->softint | (uint32_t)value);
+}
+
+void helper_clear_softint(uint64_t value)
+{
+ do_modify_softint("helper_clear_softint", env->softint & (uint32_t)~value);
+}
+
+void helper_write_softint(uint64_t value)
+{
+ do_modify_softint("helper_write_softint", (uint32_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] = {
+ [TT_TFAULT] = "Instruction Access Fault",
+ [TT_TMISS] = "Instruction Access MMU Miss",
+ [TT_CODE_ACCESS] = "Instruction Access Error",
+ [TT_ILL_INSN] = "Illegal Instruction",
+ [TT_PRIV_INSN] = "Privileged Instruction",
+ [TT_NFPU_INSN] = "FPU Disabled",
+ [TT_FP_EXCP] = "FPU Exception",
+ [TT_TOVF] = "Tag Overflow",
+ [TT_CLRWIN] = "Clean Windows",
+ [TT_DIV_ZERO] = "Division By Zero",
+ [TT_DFAULT] = "Data Access Fault",
+ [TT_DMISS] = "Data Access MMU Miss",
+ [TT_DATA_ACCESS] = "Data Access Error",
+ [TT_DPROT] = "Data Protection Error",
+ [TT_UNALIGNED] = "Unaligned Memory Access",
+ [TT_PRIV_ACT] = "Privileged Action",
+ [TT_EXTINT | 0x1] = "External Interrupt 1",
+ [TT_EXTINT | 0x2] = "External Interrupt 2",
+ [TT_EXTINT | 0x3] = "External Interrupt 3",
+ [TT_EXTINT | 0x4] = "External Interrupt 4",
+ [TT_EXTINT | 0x5] = "External Interrupt 5",
+ [TT_EXTINT | 0x6] = "External Interrupt 6",
+ [TT_EXTINT | 0x7] = "External Interrupt 7",
+ [TT_EXTINT | 0x8] = "External Interrupt 8",
+ [TT_EXTINT | 0x9] = "External Interrupt 9",
+ [TT_EXTINT | 0xa] = "External Interrupt 10",
+ [TT_EXTINT | 0xb] = "External Interrupt 11",
+ [TT_EXTINT | 0xc] = "External Interrupt 12",
+ [TT_EXTINT | 0xd] = "External Interrupt 13",
+ [TT_EXTINT | 0xe] = "External Interrupt 14",
+ [TT_EXTINT | 0xf] = "External Interrupt 15",
+};
+#endif
+
+trap_state* cpu_tsptr(CPUState* env)
+{
+ return &env->ts[env->tl & MAXTL_MASK];
+}
+
+void do_interrupt(CPUState *env)
+{
+ int intno = env->exception_index;
+ trap_state* tsptr;
+
+#ifdef DEBUG_PCALL
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ static int count;
+ const char *name;
+
+ if (intno < 0 || intno >= 0x180)
+ name = "Unknown";
+ else if (intno >= 0x100)
+ name = "Trap Instruction";
+ else if (intno >= 0xc0)
+ name = "Window Fill";
+ else if (intno >= 0x80)
+ name = "Window Spill";
+ else {
+ name = excp_names[intno];
+ if (!name)
+ name = "Unknown";
+ }
+
+ qemu_log("%6d: %s (v=%04x) pc=%016" PRIx64 " npc=%016" PRIx64
+ " SP=%016" PRIx64 "\n",
+ count, name, intno,
+ env->pc,
+ env->npc, env->regwptr[6]);
+ log_cpu_state(env, 0);
+#if 0
+ {
+ int i;
+ uint8_t *ptr;
+
+ qemu_log(" code=");
+ ptr = (uint8_t *)env->pc;
+ for(i = 0; i < 16; i++) {
+ qemu_log(" %02x", ldub(ptr + i));
+ }
+ qemu_log("\n");
+ }
+#endif
+ count++;
+ }
+#endif
+#if !defined(CONFIG_USER_ONLY)
+ if (env->tl >= env->maxtl) {
+ cpu_abort(env, "Trap 0x%04x while trap level (%d) >= MAXTL (%d),"
+ " Error state", env->exception_index, env->tl, env->maxtl);
+ return;
+ }
+#endif
+ if (env->tl < env->maxtl - 1) {
+ env->tl++;
+ } else {
+ env->pstate |= PS_RED;
+ if (env->tl < env->maxtl)
+ env->tl++;
+ }
+ tsptr = cpu_tsptr(env);
+
+ tsptr->tstate = (get_ccr() << 32) |
+ ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) |
+ get_cwp64();
+ tsptr->tpc = env->pc;
+ tsptr->tnpc = env->npc;
+ tsptr->tt = intno;
+
+ switch (intno) {
+ case TT_IVEC:
+ change_pstate(PS_PEF | PS_PRIV | PS_IG);
+ break;
+ case TT_TFAULT:
+ case TT_DFAULT:
+ case TT_TMISS ... TT_TMISS + 3:
+ case TT_DMISS ... TT_DMISS + 3:
+ case TT_DPROT ... TT_DPROT + 3:
+ change_pstate(PS_PEF | PS_PRIV | PS_MG);
+ break;
+ default:
+ change_pstate(PS_PEF | PS_PRIV | PS_AG);
+ break;
+ }
+
+ if (intno == TT_CLRWIN) {
+ set_cwp(cwp_dec(env->cwp - 1));
+ } else if ((intno & 0x1c0) == TT_SPILL) {
+ set_cwp(cwp_dec(env->cwp - env->cansave - 2));
+ } else if ((intno & 0x1c0) == TT_FILL) {
+ set_cwp(cwp_inc(env->cwp + 1));
+ }
+ env->tbr &= ~0x7fffULL;
+ env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
+ env->pc = env->tbr;
+ env->npc = env->pc + 4;
+ env->exception_index = -1;
+}
+#else
+#ifdef DEBUG_PCALL
+static const char * const excp_names[0x80] = {
+ [TT_TFAULT] = "Instruction Access Fault",
+ [TT_ILL_INSN] = "Illegal Instruction",
+ [TT_PRIV_INSN] = "Privileged Instruction",
+ [TT_NFPU_INSN] = "FPU Disabled",
+ [TT_WIN_OVF] = "Window Overflow",
+ [TT_WIN_UNF] = "Window Underflow",
+ [TT_UNALIGNED] = "Unaligned Memory Access",
+ [TT_FP_EXCP] = "FPU Exception",
+ [TT_DFAULT] = "Data Access Fault",
+ [TT_TOVF] = "Tag Overflow",
+ [TT_EXTINT | 0x1] = "External Interrupt 1",
+ [TT_EXTINT | 0x2] = "External Interrupt 2",
+ [TT_EXTINT | 0x3] = "External Interrupt 3",
+ [TT_EXTINT | 0x4] = "External Interrupt 4",
+ [TT_EXTINT | 0x5] = "External Interrupt 5",
+ [TT_EXTINT | 0x6] = "External Interrupt 6",
+ [TT_EXTINT | 0x7] = "External Interrupt 7",
+ [TT_EXTINT | 0x8] = "External Interrupt 8",
+ [TT_EXTINT | 0x9] = "External Interrupt 9",
+ [TT_EXTINT | 0xa] = "External Interrupt 10",
+ [TT_EXTINT | 0xb] = "External Interrupt 11",
+ [TT_EXTINT | 0xc] = "External Interrupt 12",
+ [TT_EXTINT | 0xd] = "External Interrupt 13",
+ [TT_EXTINT | 0xe] = "External Interrupt 14",
+ [TT_EXTINT | 0xf] = "External Interrupt 15",
+ [TT_TOVF] = "Tag Overflow",
+ [TT_CODE_ACCESS] = "Instruction Access Error",
+ [TT_DATA_ACCESS] = "Data Access Error",
+ [TT_DIV_ZERO] = "Division By Zero",
+ [TT_NCP_INSN] = "Coprocessor Disabled",
+};
+#endif
+
+void do_interrupt(CPUState *env)
+{
+ int cwp, intno = env->exception_index;
+
+#ifdef DEBUG_PCALL
+ if (qemu_loglevel_mask(CPU_LOG_INT)) {
+ static int count;
+ const char *name;
+
+ if (intno < 0 || intno >= 0x100)
+ name = "Unknown";
+ else if (intno >= 0x80)
+ name = "Trap Instruction";
+ else {
+ name = excp_names[intno];
+ if (!name)
+ name = "Unknown";
+ }
+
+ qemu_log("%6d: %s (v=%02x) pc=%08x npc=%08x SP=%08x\n",
+ count, name, intno,
+ env->pc,
+ env->npc, env->regwptr[6]);
+ log_cpu_state(env, 0);
+#if 0
+ {
+ int i;
+ uint8_t *ptr;
+
+ qemu_log(" code=");
+ ptr = (uint8_t *)env->pc;
+ for(i = 0; i < 16; i++) {
+ qemu_log(" %02x", ldub(ptr + i));
+ }
+ qemu_log("\n");
+ }
+#endif
+ count++;
+ }
+#endif
+#if !defined(CONFIG_USER_ONLY)
+ if (env->psret == 0) {
+ cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state",
+ env->exception_index);
+ return;
+ }
+#endif
+ env->psret = 0;
+ cwp = cwp_dec(env->cwp - 1);
+ set_cwp(cwp);
+ env->regwptr[9] = env->pc;
+ env->regwptr[10] = env->npc;
+ env->psrps = env->psrs;
+ env->psrs = 1;
+ env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4);
+ env->pc = env->tbr;
+ env->npc = env->pc + 4;
+ env->exception_index = -1;
+
+#if !defined(CONFIG_USER_ONLY)
+ /* IRQ acknowledgment */
+ if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) {
+ env->qemu_irq_ack(env->irq_manager, intno);
+ }
+#endif
+}
+#endif
+
+#if !defined(CONFIG_USER_ONLY)
+
+static void do_unaligned_access(target_ulong addr, int is_write, int is_user,
+ void *retaddr);
+
+#define MMUSUFFIX _mmu
+#define ALIGNED_ONLY
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* XXX: make it generic ? */
+static void cpu_restore_state2(void *retaddr)
+{
+ TranslationBlock *tb;
+ unsigned long pc;
+
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ pc = (unsigned long)retaddr;
+ 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, (void *)(long)env->cond);
+ }
+ }
+}
+
+static void do_unaligned_access(target_ulong addr, int is_write, int is_user,
+ void *retaddr)
+{
+#ifdef DEBUG_UNALIGNED
+ printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx
+ "\n", addr, env->pc);
+#endif
+ cpu_restore_state2(retaddr);
+ raise_exception(TT_UNALIGNED);
+}
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+ int ret;
+ CPUState *saved_env;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+ ret = cpu_sparc_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (ret) {
+ cpu_restore_state2(retaddr);
+ cpu_loop_exit();
+ }
+ env = saved_env;
+}
+
+#endif /* !CONFIG_USER_ONLY */
+
+#ifndef TARGET_SPARC64
+#if !defined(CONFIG_USER_ONLY)
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int is_asi, int size)
+{
+ CPUState *saved_env;
+ int fault_type;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+#ifdef DEBUG_UNASSIGNED
+ if (is_asi)
+ printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx
+ " asi 0x%02x from " TARGET_FMT_lx "\n",
+ is_exec ? "exec" : is_write ? "write" : "read", size,
+ size == 1 ? "" : "s", addr, is_asi, env->pc);
+ else
+ printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx
+ " from " TARGET_FMT_lx "\n",
+ is_exec ? "exec" : is_write ? "write" : "read", size,
+ size == 1 ? "" : "s", addr, env->pc);
+#endif
+ /* Don't overwrite translation and access faults */
+ fault_type = (env->mmuregs[3] & 0x1c) >> 2;
+ if ((fault_type > 4) || (fault_type == 0)) {
+ env->mmuregs[3] = 0; /* Fault status register */
+ if (is_asi)
+ env->mmuregs[3] |= 1 << 16;
+ if (env->psrs)
+ env->mmuregs[3] |= 1 << 5;
+ if (is_exec)
+ env->mmuregs[3] |= 1 << 6;
+ if (is_write)
+ env->mmuregs[3] |= 1 << 7;
+ env->mmuregs[3] |= (5 << 2) | 2;
+ /* SuperSPARC will never place instruction fault addresses in the FAR */
+ if (!is_exec) {
+ env->mmuregs[4] = addr; /* Fault address register */
+ }
+ }
+ /* overflow (same type fault was not read before another fault) */
+ if (fault_type == ((env->mmuregs[3] & 0x1c)) >> 2) {
+ env->mmuregs[3] |= 1;
+ }
+
+ if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) {
+ if (is_exec)
+ raise_exception(TT_CODE_ACCESS);
+ else
+ raise_exception(TT_DATA_ACCESS);
+ }
+
+ /* flush neverland mappings created during no-fault mode,
+ so the sequential MMU faults report proper fault types */
+ if (env->mmuregs[0] & MMU_NF) {
+ tlb_flush(env, 1);
+ }
+
+ env = saved_env;
+}
+#endif
+#else
+#if defined(CONFIG_USER_ONLY)
+static void do_unassigned_access(target_ulong addr, int is_write, int is_exec,
+ int is_asi, int size)
+#else
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+ int is_asi, int size)
+#endif
+{
+ CPUState *saved_env;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+
+#ifdef DEBUG_UNASSIGNED
+ printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx
+ "\n", addr, env->pc);
+#endif
+
+ if (is_exec)
+ raise_exception(TT_CODE_ACCESS);
+ else
+ raise_exception(TT_DATA_ACCESS);
+
+ env = saved_env;
+}
+#endif
+
+
+#ifdef TARGET_SPARC64
+void helper_tick_set_count(void *opaque, uint64_t count)
+{
+#if !defined(CONFIG_USER_ONLY)
+ cpu_tick_set_count(opaque, count);
+#endif
+}
+
+uint64_t helper_tick_get_count(void *opaque)
+{
+#if !defined(CONFIG_USER_ONLY)
+ return cpu_tick_get_count(opaque);
+#else
+ return 0;
+#endif
+}
+
+void helper_tick_set_limit(void *opaque, uint64_t limit)
+{
+#if !defined(CONFIG_USER_ONLY)
+ cpu_tick_set_limit(opaque, limit);
+#endif
+}
+#endif
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
new file mode 100644
index 0000000..e26462e
--- /dev/null
+++ b/target-sparc/translate.c
@@ -0,0 +1,5106 @@
+/*
+ SPARC translation
+
+ Copyright (C) 2003 Thomas M. Ogrisegg <tom@fnord.at>
+ 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "helper.h"
+#include "tcg-op.h"
+
+#define GEN_HELPER 1
+#include "helper.h"
+
+#define DEBUG_DISAS
+
+#define DYNAMIC_PC 1 /* dynamic pc value */
+#define JUMP_PC 2 /* dynamic pc value which takes only two values
+ according to jump_pc[T2] */
+
+/* global register indexes */
+static TCGv_ptr cpu_env, cpu_regwptr;
+static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst;
+static TCGv_i32 cpu_cc_op;
+static TCGv_i32 cpu_psr;
+static TCGv cpu_fsr, cpu_pc, cpu_npc, cpu_gregs[8];
+static TCGv cpu_y;
+#ifndef CONFIG_USER_ONLY
+static TCGv cpu_tbr;
+#endif
+static TCGv cpu_cond, cpu_dst, cpu_addr, cpu_val;
+#ifdef TARGET_SPARC64
+static TCGv_i32 cpu_xcc, cpu_asi, cpu_fprs;
+static TCGv cpu_gsr;
+static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr;
+static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver;
+static TCGv_i32 cpu_softint;
+#else
+static TCGv cpu_wim;
+#endif
+/* local register indexes (only used inside old micro ops) */
+static TCGv cpu_tmp0;
+static TCGv_i32 cpu_tmp32;
+static TCGv_i64 cpu_tmp64;
+/* Floating point registers */
+static TCGv_i32 cpu_fpr[TARGET_FPREGS];
+
+static target_ulong gen_opc_npc[OPC_BUF_SIZE];
+static target_ulong gen_opc_jump_pc[2];
+
+#include "gen-icount.h"
+
+typedef struct DisasContext {
+ target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */
+ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */
+ target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */
+ int is_br;
+ int mem_idx;
+ int fpu_enabled;
+ int address_mask_32bit;
+ int singlestep;
+ uint32_t cc_op; /* current CC operation */
+ struct TranslationBlock *tb;
+ sparc_def_t *def;
+} DisasContext;
+
+// This function uses non-native bit order
+#define GET_FIELD(X, FROM, TO) \
+ ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
+
+// This function uses the order in the manuals, i.e. bit 0 is 2^0
+#define GET_FIELD_SP(X, FROM, TO) \
+ GET_FIELD(X, 31 - (TO), 31 - (FROM))
+
+#define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1)
+#define GET_FIELD_SPs(x,a,b) sign_extend (GET_FIELD_SP(x,a,b), ((b) - (a) + 1))
+
+#ifdef TARGET_SPARC64
+#define DFPREG(r) (((r & 1) << 5) | (r & 0x1e))
+#define QFPREG(r) (((r & 1) << 5) | (r & 0x1c))
+#else
+#define DFPREG(r) (r & 0x1e)
+#define QFPREG(r) (r & 0x1c)
+#endif
+
+#define UA2005_HTRAP_MASK 0xff
+#define V8_TRAP_MASK 0x7f
+
+static int sign_extend(int x, int len)
+{
+ len = 32 - len;
+ return (x << len) >> len;
+}
+
+#define IS_IMM (insn & (1<<13))
+
+/* floating point registers moves */
+static void gen_op_load_fpr_DT0(unsigned int src)
+{
+ tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, dt0) +
+ offsetof(CPU_DoubleU, l.upper));
+ tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, dt0) +
+ offsetof(CPU_DoubleU, l.lower));
+}
+
+static void gen_op_load_fpr_DT1(unsigned int src)
+{
+ tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, dt1) +
+ offsetof(CPU_DoubleU, l.upper));
+ tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, dt1) +
+ offsetof(CPU_DoubleU, l.lower));
+}
+
+static void gen_op_store_DT0_fpr(unsigned int dst)
+{
+ tcg_gen_ld_i32(cpu_fpr[dst], cpu_env, offsetof(CPUSPARCState, dt0) +
+ offsetof(CPU_DoubleU, l.upper));
+ tcg_gen_ld_i32(cpu_fpr[dst + 1], cpu_env, offsetof(CPUSPARCState, dt0) +
+ offsetof(CPU_DoubleU, l.lower));
+}
+
+static void gen_op_load_fpr_QT0(unsigned int src)
+{
+ tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.upmost));
+ tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.upper));
+ tcg_gen_st_i32(cpu_fpr[src + 2], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.lower));
+ tcg_gen_st_i32(cpu_fpr[src + 3], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.lowest));
+}
+
+static void gen_op_load_fpr_QT1(unsigned int src)
+{
+ tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, qt1) +
+ offsetof(CPU_QuadU, l.upmost));
+ tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, qt1) +
+ offsetof(CPU_QuadU, l.upper));
+ tcg_gen_st_i32(cpu_fpr[src + 2], cpu_env, offsetof(CPUSPARCState, qt1) +
+ offsetof(CPU_QuadU, l.lower));
+ tcg_gen_st_i32(cpu_fpr[src + 3], cpu_env, offsetof(CPUSPARCState, qt1) +
+ offsetof(CPU_QuadU, l.lowest));
+}
+
+static void gen_op_store_QT0_fpr(unsigned int dst)
+{
+ tcg_gen_ld_i32(cpu_fpr[dst], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.upmost));
+ tcg_gen_ld_i32(cpu_fpr[dst + 1], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.upper));
+ tcg_gen_ld_i32(cpu_fpr[dst + 2], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.lower));
+ tcg_gen_ld_i32(cpu_fpr[dst + 3], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, l.lowest));
+}
+
+/* moves */
+#ifdef CONFIG_USER_ONLY
+#define supervisor(dc) 0
+#ifdef TARGET_SPARC64
+#define hypervisor(dc) 0
+#endif
+#else
+#define supervisor(dc) (dc->mem_idx >= MMU_KERNEL_IDX)
+#ifdef TARGET_SPARC64
+#define hypervisor(dc) (dc->mem_idx == MMU_HYPV_IDX)
+#else
+#endif
+#endif
+
+#ifdef TARGET_SPARC64
+#ifndef TARGET_ABI32
+#define AM_CHECK(dc) ((dc)->address_mask_32bit)
+#else
+#define AM_CHECK(dc) (1)
+#endif
+#endif
+
+static inline void gen_address_mask(DisasContext *dc, TCGv addr)
+{
+#ifdef TARGET_SPARC64
+ if (AM_CHECK(dc))
+ tcg_gen_andi_tl(addr, addr, 0xffffffffULL);
+#endif
+}
+
+static inline void gen_movl_reg_TN(int reg, TCGv tn)
+{
+ if (reg == 0)
+ tcg_gen_movi_tl(tn, 0);
+ else if (reg < 8)
+ tcg_gen_mov_tl(tn, cpu_gregs[reg]);
+ else {
+ tcg_gen_ld_tl(tn, cpu_regwptr, (reg - 8) * sizeof(target_ulong));
+ }
+}
+
+static inline void gen_movl_TN_reg(int reg, TCGv tn)
+{
+ if (reg == 0)
+ return;
+ else if (reg < 8)
+ tcg_gen_mov_tl(cpu_gregs[reg], tn);
+ else {
+ tcg_gen_st_tl(tn, cpu_regwptr, (reg - 8) * sizeof(target_ulong));
+ }
+}
+
+static inline void gen_goto_tb(DisasContext *s, int tb_num,
+ target_ulong pc, target_ulong npc)
+{
+ TranslationBlock *tb;
+
+ tb = s->tb;
+ if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) &&
+ (npc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) &&
+ !s->singlestep) {
+ /* jump to same page: we can use a direct jump */
+ tcg_gen_goto_tb(tb_num);
+ tcg_gen_movi_tl(cpu_pc, pc);
+ tcg_gen_movi_tl(cpu_npc, npc);
+ tcg_gen_exit_tb((long)tb + tb_num);
+ } else {
+ /* jump to another page: currently not optimized */
+ tcg_gen_movi_tl(cpu_pc, pc);
+ tcg_gen_movi_tl(cpu_npc, npc);
+ tcg_gen_exit_tb(0);
+ }
+}
+
+// XXX suboptimal
+static inline void gen_mov_reg_N(TCGv reg, TCGv_i32 src)
+{
+ tcg_gen_extu_i32_tl(reg, src);
+ tcg_gen_shri_tl(reg, reg, PSR_NEG_SHIFT);
+ tcg_gen_andi_tl(reg, reg, 0x1);
+}
+
+static inline void gen_mov_reg_Z(TCGv reg, TCGv_i32 src)
+{
+ tcg_gen_extu_i32_tl(reg, src);
+ tcg_gen_shri_tl(reg, reg, PSR_ZERO_SHIFT);
+ tcg_gen_andi_tl(reg, reg, 0x1);
+}
+
+static inline void gen_mov_reg_V(TCGv reg, TCGv_i32 src)
+{
+ tcg_gen_extu_i32_tl(reg, src);
+ tcg_gen_shri_tl(reg, reg, PSR_OVF_SHIFT);
+ tcg_gen_andi_tl(reg, reg, 0x1);
+}
+
+static inline void gen_mov_reg_C(TCGv reg, TCGv_i32 src)
+{
+ tcg_gen_extu_i32_tl(reg, src);
+ tcg_gen_shri_tl(reg, reg, PSR_CARRY_SHIFT);
+ tcg_gen_andi_tl(reg, reg, 0x1);
+}
+
+static inline void gen_add_tv(TCGv dst, TCGv src1, TCGv src2)
+{
+ TCGv r_temp;
+ TCGv_i32 r_const;
+ int l1;
+
+ l1 = gen_new_label();
+
+ r_temp = tcg_temp_new();
+ tcg_gen_xor_tl(r_temp, src1, src2);
+ tcg_gen_not_tl(r_temp, r_temp);
+ tcg_gen_xor_tl(cpu_tmp0, src1, dst);
+ tcg_gen_and_tl(r_temp, r_temp, cpu_tmp0);
+ tcg_gen_andi_tl(r_temp, r_temp, (1ULL << 31));
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_temp, 0, l1);
+ r_const = tcg_const_i32(TT_TOVF);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ gen_set_label(l1);
+ tcg_temp_free(r_temp);
+}
+
+static inline void gen_tag_tv(TCGv src1, TCGv src2)
+{
+ int l1;
+ TCGv_i32 r_const;
+
+ l1 = gen_new_label();
+ tcg_gen_or_tl(cpu_tmp0, src1, src2);
+ tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, 0x3);
+ tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, l1);
+ r_const = tcg_const_i32(TT_TOVF);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ gen_set_label(l1);
+}
+
+static inline void gen_op_addi_cc(TCGv dst, TCGv src1, target_long src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_movi_tl(cpu_cc_src2, src2);
+ tcg_gen_addi_tl(cpu_cc_dst, cpu_cc_src, src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static TCGv_i32 gen_add32_carry32(void)
+{
+ TCGv_i32 carry_32, cc_src1_32, cc_src2_32;
+
+ /* Carry is computed from a previous add: (dst < src) */
+#if TARGET_LONG_BITS == 64
+ cc_src1_32 = tcg_temp_new_i32();
+ cc_src2_32 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_dst);
+ tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src);
+#else
+ cc_src1_32 = cpu_cc_dst;
+ cc_src2_32 = cpu_cc_src;
+#endif
+
+ carry_32 = tcg_temp_new_i32();
+ tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32);
+
+#if TARGET_LONG_BITS == 64
+ tcg_temp_free_i32(cc_src1_32);
+ tcg_temp_free_i32(cc_src2_32);
+#endif
+
+ return carry_32;
+}
+
+static TCGv_i32 gen_sub32_carry32(void)
+{
+ TCGv_i32 carry_32, cc_src1_32, cc_src2_32;
+
+ /* Carry is computed from a previous borrow: (src1 < src2) */
+#if TARGET_LONG_BITS == 64
+ cc_src1_32 = tcg_temp_new_i32();
+ cc_src2_32 = tcg_temp_new_i32();
+ tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_src);
+ tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src2);
+#else
+ cc_src1_32 = cpu_cc_src;
+ cc_src2_32 = cpu_cc_src2;
+#endif
+
+ carry_32 = tcg_temp_new_i32();
+ tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32);
+
+#if TARGET_LONG_BITS == 64
+ tcg_temp_free_i32(cc_src1_32);
+ tcg_temp_free_i32(cc_src2_32);
+#endif
+
+ return carry_32;
+}
+
+static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1,
+ TCGv src2, int update_cc)
+{
+ TCGv_i32 carry_32;
+ TCGv carry;
+
+ switch (dc->cc_op) {
+ case CC_OP_DIV:
+ case CC_OP_LOGIC:
+ /* Carry is known to be zero. Fall back to plain ADD. */
+ if (update_cc) {
+ gen_op_add_cc(dst, src1, src2);
+ } else {
+ tcg_gen_add_tl(dst, src1, src2);
+ }
+ return;
+
+ case CC_OP_ADD:
+ case CC_OP_TADD:
+ case CC_OP_TADDTV:
+#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
+ {
+ /* For 32-bit hosts, we can re-use the host's hardware carry
+ generation by using an ADD2 opcode. We discard the low
+ part of the output. Ideally we'd combine this operation
+ with the add that generated the carry in the first place. */
+ TCGv dst_low = tcg_temp_new();
+ tcg_gen_op6_i32(INDEX_op_add2_i32, dst_low, dst,
+ cpu_cc_src, src1, cpu_cc_src2, src2);
+ tcg_temp_free(dst_low);
+ goto add_done;
+ }
+#endif
+ carry_32 = gen_add32_carry32();
+ break;
+
+ case CC_OP_SUB:
+ case CC_OP_TSUB:
+ case CC_OP_TSUBTV:
+ carry_32 = gen_sub32_carry32();
+ break;
+
+ default:
+ /* We need external help to produce the carry. */
+ carry_32 = tcg_temp_new_i32();
+ gen_helper_compute_C_icc(carry_32);
+ break;
+ }
+
+#if TARGET_LONG_BITS == 64
+ carry = tcg_temp_new();
+ tcg_gen_extu_i32_i64(carry, carry_32);
+#else
+ carry = carry_32;
+#endif
+
+ tcg_gen_add_tl(dst, src1, src2);
+ tcg_gen_add_tl(dst, dst, carry);
+
+ tcg_temp_free_i32(carry_32);
+#if TARGET_LONG_BITS == 64
+ tcg_temp_free(carry);
+#endif
+
+#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
+ add_done:
+#endif
+ if (update_cc) {
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ tcg_gen_mov_tl(cpu_cc_dst, dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX);
+ dc->cc_op = CC_OP_ADDX;
+ }
+}
+
+static inline void gen_op_tadd_cc(TCGv dst, TCGv src1, TCGv src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_op_tadd_ccTV(TCGv dst, TCGv src1, TCGv src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ gen_tag_tv(cpu_cc_src, cpu_cc_src2);
+ tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ gen_add_tv(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_sub_tv(TCGv dst, TCGv src1, TCGv src2)
+{
+ TCGv r_temp;
+ TCGv_i32 r_const;
+ int l1;
+
+ l1 = gen_new_label();
+
+ r_temp = tcg_temp_new();
+ tcg_gen_xor_tl(r_temp, src1, src2);
+ tcg_gen_xor_tl(cpu_tmp0, src1, dst);
+ tcg_gen_and_tl(r_temp, r_temp, cpu_tmp0);
+ tcg_gen_andi_tl(r_temp, r_temp, (1ULL << 31));
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_temp, 0, l1);
+ r_const = tcg_const_i32(TT_TOVF);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ gen_set_label(l1);
+ tcg_temp_free(r_temp);
+}
+
+static inline void gen_op_subi_cc(TCGv dst, TCGv src1, target_long src2, DisasContext *dc)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_movi_tl(cpu_cc_src2, src2);
+ if (src2 == 0) {
+ tcg_gen_mov_tl(cpu_cc_dst, src1);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ } else {
+ tcg_gen_subi_tl(cpu_cc_dst, cpu_cc_src, src2);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB);
+ dc->cc_op = CC_OP_SUB;
+ }
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1,
+ TCGv src2, int update_cc)
+{
+ TCGv_i32 carry_32;
+ TCGv carry;
+
+ switch (dc->cc_op) {
+ case CC_OP_DIV:
+ case CC_OP_LOGIC:
+ /* Carry is known to be zero. Fall back to plain SUB. */
+ if (update_cc) {
+ gen_op_sub_cc(dst, src1, src2);
+ } else {
+ tcg_gen_sub_tl(dst, src1, src2);
+ }
+ return;
+
+ case CC_OP_ADD:
+ case CC_OP_TADD:
+ case CC_OP_TADDTV:
+ carry_32 = gen_add32_carry32();
+ break;
+
+ case CC_OP_SUB:
+ case CC_OP_TSUB:
+ case CC_OP_TSUBTV:
+#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
+ {
+ /* For 32-bit hosts, we can re-use the host's hardware carry
+ generation by using a SUB2 opcode. We discard the low
+ part of the output. Ideally we'd combine this operation
+ with the add that generated the carry in the first place. */
+ TCGv dst_low = tcg_temp_new();
+ tcg_gen_op6_i32(INDEX_op_sub2_i32, dst_low, dst,
+ cpu_cc_src, src1, cpu_cc_src2, src2);
+ tcg_temp_free(dst_low);
+ goto sub_done;
+ }
+#endif
+ carry_32 = gen_sub32_carry32();
+ break;
+
+ default:
+ /* We need external help to produce the carry. */
+ carry_32 = tcg_temp_new_i32();
+ gen_helper_compute_C_icc(carry_32);
+ break;
+ }
+
+#if TARGET_LONG_BITS == 64
+ carry = tcg_temp_new();
+ tcg_gen_extu_i32_i64(carry, carry_32);
+#else
+ carry = carry_32;
+#endif
+
+ tcg_gen_sub_tl(dst, src1, src2);
+ tcg_gen_sub_tl(dst, dst, carry);
+
+ tcg_temp_free_i32(carry_32);
+#if TARGET_LONG_BITS == 64
+ tcg_temp_free(carry);
+#endif
+
+#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
+ sub_done:
+#endif
+ if (update_cc) {
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ tcg_gen_mov_tl(cpu_cc_dst, dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX);
+ dc->cc_op = CC_OP_SUBX;
+ }
+}
+
+static inline void gen_op_tsub_cc(TCGv dst, TCGv src1, TCGv src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_op_tsub_ccTV(TCGv dst, TCGv src1, TCGv src2)
+{
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ gen_tag_tv(cpu_cc_src, cpu_cc_src2);
+ tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ gen_sub_tv(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2)
+{
+ TCGv r_temp;
+ int l1;
+
+ l1 = gen_new_label();
+ r_temp = tcg_temp_new();
+
+ /* old op:
+ if (!(env->y & 1))
+ T1 = 0;
+ */
+ tcg_gen_andi_tl(cpu_cc_src, src1, 0xffffffff);
+ tcg_gen_andi_tl(r_temp, cpu_y, 0x1);
+ tcg_gen_andi_tl(cpu_cc_src2, src2, 0xffffffff);
+ tcg_gen_brcondi_tl(TCG_COND_NE, r_temp, 0, l1);
+ tcg_gen_movi_tl(cpu_cc_src2, 0);
+ gen_set_label(l1);
+
+ // b2 = T0 & 1;
+ // env->y = (b2 << 31) | (env->y >> 1);
+ tcg_gen_andi_tl(r_temp, cpu_cc_src, 0x1);
+ tcg_gen_shli_tl(r_temp, r_temp, 31);
+ tcg_gen_shri_tl(cpu_tmp0, cpu_y, 1);
+ tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, 0x7fffffff);
+ tcg_gen_or_tl(cpu_tmp0, cpu_tmp0, r_temp);
+ tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff);
+
+ // b1 = N ^ V;
+ gen_mov_reg_N(cpu_tmp0, cpu_psr);
+ gen_mov_reg_V(r_temp, cpu_psr);
+ tcg_gen_xor_tl(cpu_tmp0, cpu_tmp0, r_temp);
+ tcg_temp_free(r_temp);
+
+ // T0 = (b1 << 31) | (T0 >> 1);
+ // src1 = T0;
+ tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, 31);
+ tcg_gen_shri_tl(cpu_cc_src, cpu_cc_src, 1);
+ tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_tmp0);
+
+ tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2);
+
+ tcg_gen_mov_tl(dst, cpu_cc_dst);
+}
+
+static inline void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext)
+{
+ TCGv_i32 r_src1, r_src2;
+ TCGv_i64 r_temp, r_temp2;
+
+ r_src1 = tcg_temp_new_i32();
+ r_src2 = tcg_temp_new_i32();
+
+ tcg_gen_trunc_tl_i32(r_src1, src1);
+ tcg_gen_trunc_tl_i32(r_src2, src2);
+
+ r_temp = tcg_temp_new_i64();
+ r_temp2 = tcg_temp_new_i64();
+
+ if (sign_ext) {
+ tcg_gen_ext_i32_i64(r_temp, r_src2);
+ tcg_gen_ext_i32_i64(r_temp2, r_src1);
+ } else {
+ tcg_gen_extu_i32_i64(r_temp, r_src2);
+ tcg_gen_extu_i32_i64(r_temp2, r_src1);
+ }
+
+ tcg_gen_mul_i64(r_temp2, r_temp, r_temp2);
+
+ tcg_gen_shri_i64(r_temp, r_temp2, 32);
+ tcg_gen_trunc_i64_tl(cpu_tmp0, r_temp);
+ tcg_temp_free_i64(r_temp);
+ tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff);
+
+ tcg_gen_trunc_i64_tl(dst, r_temp2);
+
+ tcg_temp_free_i64(r_temp2);
+
+ tcg_temp_free_i32(r_src1);
+ tcg_temp_free_i32(r_src2);
+}
+
+static inline void gen_op_umul(TCGv dst, TCGv src1, TCGv src2)
+{
+ /* zero-extend truncated operands before multiplication */
+ gen_op_multiply(dst, src1, src2, 0);
+}
+
+static inline void gen_op_smul(TCGv dst, TCGv src1, TCGv src2)
+{
+ /* sign-extend truncated operands before multiplication */
+ gen_op_multiply(dst, src1, src2, 1);
+}
+
+#ifdef TARGET_SPARC64
+static inline void gen_trap_ifdivzero_tl(TCGv divisor)
+{
+ TCGv_i32 r_const;
+ int l1;
+
+ l1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_NE, divisor, 0, l1);
+ r_const = tcg_const_i32(TT_DIV_ZERO);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ gen_set_label(l1);
+}
+
+static inline void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2)
+{
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+ tcg_gen_mov_tl(cpu_cc_src, src1);
+ tcg_gen_mov_tl(cpu_cc_src2, src2);
+ gen_trap_ifdivzero_tl(cpu_cc_src2);
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cc_src, INT64_MIN, l1);
+ tcg_gen_brcondi_tl(TCG_COND_NE, cpu_cc_src2, -1, l1);
+ tcg_gen_movi_i64(dst, INT64_MIN);
+ tcg_gen_br(l2);
+ gen_set_label(l1);
+ tcg_gen_div_i64(dst, cpu_cc_src, cpu_cc_src2);
+ gen_set_label(l2);
+}
+#endif
+
+// 1
+static inline void gen_op_eval_ba(TCGv dst)
+{
+ tcg_gen_movi_tl(dst, 1);
+}
+
+// Z
+static inline void gen_op_eval_be(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_Z(dst, src);
+}
+
+// Z | (N ^ V)
+static inline void gen_op_eval_ble(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_N(cpu_tmp0, src);
+ gen_mov_reg_V(dst, src);
+ tcg_gen_xor_tl(dst, dst, cpu_tmp0);
+ gen_mov_reg_Z(cpu_tmp0, src);
+ tcg_gen_or_tl(dst, dst, cpu_tmp0);
+}
+
+// N ^ V
+static inline void gen_op_eval_bl(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_V(cpu_tmp0, src);
+ gen_mov_reg_N(dst, src);
+ tcg_gen_xor_tl(dst, dst, cpu_tmp0);
+}
+
+// C | Z
+static inline void gen_op_eval_bleu(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_Z(cpu_tmp0, src);
+ gen_mov_reg_C(dst, src);
+ tcg_gen_or_tl(dst, dst, cpu_tmp0);
+}
+
+// C
+static inline void gen_op_eval_bcs(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_C(dst, src);
+}
+
+// V
+static inline void gen_op_eval_bvs(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_V(dst, src);
+}
+
+// 0
+static inline void gen_op_eval_bn(TCGv dst)
+{
+ tcg_gen_movi_tl(dst, 0);
+}
+
+// N
+static inline void gen_op_eval_bneg(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_N(dst, src);
+}
+
+// !Z
+static inline void gen_op_eval_bne(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_Z(dst, src);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !(Z | (N ^ V))
+static inline void gen_op_eval_bg(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_N(cpu_tmp0, src);
+ gen_mov_reg_V(dst, src);
+ tcg_gen_xor_tl(dst, dst, cpu_tmp0);
+ gen_mov_reg_Z(cpu_tmp0, src);
+ tcg_gen_or_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !(N ^ V)
+static inline void gen_op_eval_bge(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_V(cpu_tmp0, src);
+ gen_mov_reg_N(dst, src);
+ tcg_gen_xor_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !(C | Z)
+static inline void gen_op_eval_bgu(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_Z(cpu_tmp0, src);
+ gen_mov_reg_C(dst, src);
+ tcg_gen_or_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !C
+static inline void gen_op_eval_bcc(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_C(dst, src);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !N
+static inline void gen_op_eval_bpos(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_N(dst, src);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !V
+static inline void gen_op_eval_bvc(TCGv dst, TCGv_i32 src)
+{
+ gen_mov_reg_V(dst, src);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+/*
+ FPSR bit field FCC1 | FCC0:
+ 0 =
+ 1 <
+ 2 >
+ 3 unordered
+*/
+static inline void gen_mov_reg_FCC0(TCGv reg, TCGv src,
+ unsigned int fcc_offset)
+{
+ tcg_gen_shri_tl(reg, src, FSR_FCC0_SHIFT + fcc_offset);
+ tcg_gen_andi_tl(reg, reg, 0x1);
+}
+
+static inline void gen_mov_reg_FCC1(TCGv reg, TCGv src,
+ unsigned int fcc_offset)
+{
+ tcg_gen_shri_tl(reg, src, FSR_FCC1_SHIFT + fcc_offset);
+ tcg_gen_andi_tl(reg, reg, 0x1);
+}
+
+// !0: FCC0 | FCC1
+static inline void gen_op_eval_fbne(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_or_tl(dst, dst, cpu_tmp0);
+}
+
+// 1 or 2: FCC0 ^ FCC1
+static inline void gen_op_eval_fblg(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_xor_tl(dst, dst, cpu_tmp0);
+}
+
+// 1 or 3: FCC0
+static inline void gen_op_eval_fbul(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+}
+
+// 1: FCC0 & !FCC1
+static inline void gen_op_eval_fbl(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_xori_tl(cpu_tmp0, cpu_tmp0, 0x1);
+ tcg_gen_and_tl(dst, dst, cpu_tmp0);
+}
+
+// 2 or 3: FCC1
+static inline void gen_op_eval_fbug(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC1(dst, src, fcc_offset);
+}
+
+// 2: !FCC0 & FCC1
+static inline void gen_op_eval_fbg(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_and_tl(dst, dst, cpu_tmp0);
+}
+
+// 3: FCC0 & FCC1
+static inline void gen_op_eval_fbu(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_and_tl(dst, dst, cpu_tmp0);
+}
+
+// 0: !(FCC0 | FCC1)
+static inline void gen_op_eval_fbe(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_or_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// 0 or 3: !(FCC0 ^ FCC1)
+static inline void gen_op_eval_fbue(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_xor_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// 0 or 2: !FCC0
+static inline void gen_op_eval_fbge(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !1: !(FCC0 & !FCC1)
+static inline void gen_op_eval_fbuge(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_xori_tl(cpu_tmp0, cpu_tmp0, 0x1);
+ tcg_gen_and_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// 0 or 1: !FCC1
+static inline void gen_op_eval_fble(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC1(dst, src, fcc_offset);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !2: !(!FCC0 & FCC1)
+static inline void gen_op_eval_fbule(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_and_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+// !3: !(FCC0 & FCC1)
+static inline void gen_op_eval_fbo(TCGv dst, TCGv src,
+ unsigned int fcc_offset)
+{
+ gen_mov_reg_FCC0(dst, src, fcc_offset);
+ gen_mov_reg_FCC1(cpu_tmp0, src, fcc_offset);
+ tcg_gen_and_tl(dst, dst, cpu_tmp0);
+ tcg_gen_xori_tl(dst, dst, 0x1);
+}
+
+static inline void gen_branch2(DisasContext *dc, target_ulong pc1,
+ target_ulong pc2, TCGv r_cond)
+{
+ int l1;
+
+ l1 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1);
+
+ gen_goto_tb(dc, 0, pc1, pc1 + 4);
+
+ gen_set_label(l1);
+ gen_goto_tb(dc, 1, pc2, pc2 + 4);
+}
+
+static inline void gen_branch_a(DisasContext *dc, target_ulong pc1,
+ target_ulong pc2, TCGv r_cond)
+{
+ int l1;
+
+ l1 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1);
+
+ gen_goto_tb(dc, 0, pc2, pc1);
+
+ gen_set_label(l1);
+ gen_goto_tb(dc, 1, pc2 + 4, pc2 + 8);
+}
+
+static inline void gen_generic_branch(target_ulong npc1, target_ulong npc2,
+ TCGv r_cond)
+{
+ int l1, l2;
+
+ l1 = gen_new_label();
+ l2 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1);
+
+ tcg_gen_movi_tl(cpu_npc, npc1);
+ tcg_gen_br(l2);
+
+ gen_set_label(l1);
+ tcg_gen_movi_tl(cpu_npc, npc2);
+ gen_set_label(l2);
+}
+
+/* call this function before using the condition register as it may
+ have been set for a jump */
+static inline void flush_cond(DisasContext *dc, TCGv cond)
+{
+ if (dc->npc == JUMP_PC) {
+ gen_generic_branch(dc->jump_pc[0], dc->jump_pc[1], cond);
+ dc->npc = DYNAMIC_PC;
+ }
+}
+
+static inline void save_npc(DisasContext *dc, TCGv cond)
+{
+ if (dc->npc == JUMP_PC) {
+ gen_generic_branch(dc->jump_pc[0], dc->jump_pc[1], cond);
+ dc->npc = DYNAMIC_PC;
+ } else if (dc->npc != DYNAMIC_PC) {
+ tcg_gen_movi_tl(cpu_npc, dc->npc);
+ }
+}
+
+static inline void save_state(DisasContext *dc, TCGv cond)
+{
+ tcg_gen_movi_tl(cpu_pc, dc->pc);
+ /* flush pending conditional evaluations before exposing cpu state */
+ if (dc->cc_op != CC_OP_FLAGS) {
+ dc->cc_op = CC_OP_FLAGS;
+ gen_helper_compute_psr();
+ }
+ save_npc(dc, cond);
+}
+
+static inline void gen_mov_pc_npc(DisasContext *dc, TCGv cond)
+{
+ if (dc->npc == JUMP_PC) {
+ gen_generic_branch(dc->jump_pc[0], dc->jump_pc[1], cond);
+ tcg_gen_mov_tl(cpu_pc, cpu_npc);
+ dc->pc = DYNAMIC_PC;
+ } else if (dc->npc == DYNAMIC_PC) {
+ tcg_gen_mov_tl(cpu_pc, cpu_npc);
+ dc->pc = DYNAMIC_PC;
+ } else {
+ dc->pc = dc->npc;
+ }
+}
+
+static inline void gen_op_next_insn(void)
+{
+ tcg_gen_mov_tl(cpu_pc, cpu_npc);
+ tcg_gen_addi_tl(cpu_npc, cpu_npc, 4);
+}
+
+static inline void gen_cond(TCGv r_dst, unsigned int cc, unsigned int cond,
+ DisasContext *dc)
+{
+ TCGv_i32 r_src;
+
+#ifdef TARGET_SPARC64
+ if (cc)
+ r_src = cpu_xcc;
+ else
+ r_src = cpu_psr;
+#else
+ r_src = cpu_psr;
+#endif
+ switch (dc->cc_op) {
+ case CC_OP_FLAGS:
+ break;
+ default:
+ gen_helper_compute_psr();
+ dc->cc_op = CC_OP_FLAGS;
+ break;
+ }
+ switch (cond) {
+ case 0x0:
+ gen_op_eval_bn(r_dst);
+ break;
+ case 0x1:
+ gen_op_eval_be(r_dst, r_src);
+ break;
+ case 0x2:
+ gen_op_eval_ble(r_dst, r_src);
+ break;
+ case 0x3:
+ gen_op_eval_bl(r_dst, r_src);
+ break;
+ case 0x4:
+ gen_op_eval_bleu(r_dst, r_src);
+ break;
+ case 0x5:
+ gen_op_eval_bcs(r_dst, r_src);
+ break;
+ case 0x6:
+ gen_op_eval_bneg(r_dst, r_src);
+ break;
+ case 0x7:
+ gen_op_eval_bvs(r_dst, r_src);
+ break;
+ case 0x8:
+ gen_op_eval_ba(r_dst);
+ break;
+ case 0x9:
+ gen_op_eval_bne(r_dst, r_src);
+ break;
+ case 0xa:
+ gen_op_eval_bg(r_dst, r_src);
+ break;
+ case 0xb:
+ gen_op_eval_bge(r_dst, r_src);
+ break;
+ case 0xc:
+ gen_op_eval_bgu(r_dst, r_src);
+ break;
+ case 0xd:
+ gen_op_eval_bcc(r_dst, r_src);
+ break;
+ case 0xe:
+ gen_op_eval_bpos(r_dst, r_src);
+ break;
+ case 0xf:
+ gen_op_eval_bvc(r_dst, r_src);
+ break;
+ }
+}
+
+static inline void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond)
+{
+ unsigned int offset;
+
+ switch (cc) {
+ default:
+ case 0x0:
+ offset = 0;
+ break;
+ case 0x1:
+ offset = 32 - 10;
+ break;
+ case 0x2:
+ offset = 34 - 10;
+ break;
+ case 0x3:
+ offset = 36 - 10;
+ break;
+ }
+
+ switch (cond) {
+ case 0x0:
+ gen_op_eval_bn(r_dst);
+ break;
+ case 0x1:
+ gen_op_eval_fbne(r_dst, cpu_fsr, offset);
+ break;
+ case 0x2:
+ gen_op_eval_fblg(r_dst, cpu_fsr, offset);
+ break;
+ case 0x3:
+ gen_op_eval_fbul(r_dst, cpu_fsr, offset);
+ break;
+ case 0x4:
+ gen_op_eval_fbl(r_dst, cpu_fsr, offset);
+ break;
+ case 0x5:
+ gen_op_eval_fbug(r_dst, cpu_fsr, offset);
+ break;
+ case 0x6:
+ gen_op_eval_fbg(r_dst, cpu_fsr, offset);
+ break;
+ case 0x7:
+ gen_op_eval_fbu(r_dst, cpu_fsr, offset);
+ break;
+ case 0x8:
+ gen_op_eval_ba(r_dst);
+ break;
+ case 0x9:
+ gen_op_eval_fbe(r_dst, cpu_fsr, offset);
+ break;
+ case 0xa:
+ gen_op_eval_fbue(r_dst, cpu_fsr, offset);
+ break;
+ case 0xb:
+ gen_op_eval_fbge(r_dst, cpu_fsr, offset);
+ break;
+ case 0xc:
+ gen_op_eval_fbuge(r_dst, cpu_fsr, offset);
+ break;
+ case 0xd:
+ gen_op_eval_fble(r_dst, cpu_fsr, offset);
+ break;
+ case 0xe:
+ gen_op_eval_fbule(r_dst, cpu_fsr, offset);
+ break;
+ case 0xf:
+ gen_op_eval_fbo(r_dst, cpu_fsr, offset);
+ break;
+ }
+}
+
+#ifdef TARGET_SPARC64
+// Inverted logic
+static const int gen_tcg_cond_reg[8] = {
+ -1,
+ TCG_COND_NE,
+ TCG_COND_GT,
+ TCG_COND_GE,
+ -1,
+ TCG_COND_EQ,
+ TCG_COND_LE,
+ TCG_COND_LT,
+};
+
+static inline void gen_cond_reg(TCGv r_dst, int cond, TCGv r_src)
+{
+ int l1;
+
+ l1 = gen_new_label();
+ tcg_gen_movi_tl(r_dst, 0);
+ tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], r_src, 0, l1);
+ tcg_gen_movi_tl(r_dst, 1);
+ gen_set_label(l1);
+}
+#endif
+
+/* XXX: potentially incorrect if dynamic npc */
+static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc,
+ TCGv r_cond)
+{
+ unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29));
+ target_ulong target = dc->pc + offset;
+
+ if (cond == 0x0) {
+ /* unconditional not taken */
+ if (a) {
+ dc->pc = dc->npc + 4;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = dc->pc + 4;
+ }
+ } else if (cond == 0x8) {
+ /* unconditional taken */
+ if (a) {
+ dc->pc = target;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = target;
+ tcg_gen_mov_tl(cpu_pc, cpu_npc);
+ }
+ } else {
+ flush_cond(dc, r_cond);
+ gen_cond(r_cond, cc, cond, dc);
+ if (a) {
+ gen_branch_a(dc, target, dc->npc, r_cond);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->jump_pc[0] = target;
+ dc->jump_pc[1] = dc->npc + 4;
+ dc->npc = JUMP_PC;
+ }
+ }
+}
+
+/* XXX: potentially incorrect if dynamic npc */
+static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc,
+ TCGv r_cond)
+{
+ unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29));
+ target_ulong target = dc->pc + offset;
+
+ if (cond == 0x0) {
+ /* unconditional not taken */
+ if (a) {
+ dc->pc = dc->npc + 4;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = dc->pc + 4;
+ }
+ } else if (cond == 0x8) {
+ /* unconditional taken */
+ if (a) {
+ dc->pc = target;
+ dc->npc = dc->pc + 4;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = target;
+ tcg_gen_mov_tl(cpu_pc, cpu_npc);
+ }
+ } else {
+ flush_cond(dc, r_cond);
+ gen_fcond(r_cond, cc, cond);
+ if (a) {
+ gen_branch_a(dc, target, dc->npc, r_cond);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->jump_pc[0] = target;
+ dc->jump_pc[1] = dc->npc + 4;
+ dc->npc = JUMP_PC;
+ }
+ }
+}
+
+#ifdef TARGET_SPARC64
+/* XXX: potentially incorrect if dynamic npc */
+static void do_branch_reg(DisasContext *dc, int32_t offset, uint32_t insn,
+ TCGv r_cond, TCGv r_reg)
+{
+ unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29));
+ target_ulong target = dc->pc + offset;
+
+ flush_cond(dc, r_cond);
+ gen_cond_reg(r_cond, cond, r_reg);
+ if (a) {
+ gen_branch_a(dc, target, dc->npc, r_cond);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->jump_pc[0] = target;
+ dc->jump_pc[1] = dc->npc + 4;
+ dc->npc = JUMP_PC;
+ }
+}
+
+static inline void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2)
+{
+ switch (fccno) {
+ case 0:
+ gen_helper_fcmps(r_rs1, r_rs2);
+ break;
+ case 1:
+ gen_helper_fcmps_fcc1(r_rs1, r_rs2);
+ break;
+ case 2:
+ gen_helper_fcmps_fcc2(r_rs1, r_rs2);
+ break;
+ case 3:
+ gen_helper_fcmps_fcc3(r_rs1, r_rs2);
+ break;
+ }
+}
+
+static inline void gen_op_fcmpd(int fccno)
+{
+ switch (fccno) {
+ case 0:
+ gen_helper_fcmpd();
+ break;
+ case 1:
+ gen_helper_fcmpd_fcc1();
+ break;
+ case 2:
+ gen_helper_fcmpd_fcc2();
+ break;
+ case 3:
+ gen_helper_fcmpd_fcc3();
+ break;
+ }
+}
+
+static inline void gen_op_fcmpq(int fccno)
+{
+ switch (fccno) {
+ case 0:
+ gen_helper_fcmpq();
+ break;
+ case 1:
+ gen_helper_fcmpq_fcc1();
+ break;
+ case 2:
+ gen_helper_fcmpq_fcc2();
+ break;
+ case 3:
+ gen_helper_fcmpq_fcc3();
+ break;
+ }
+}
+
+static inline void gen_op_fcmpes(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2)
+{
+ switch (fccno) {
+ case 0:
+ gen_helper_fcmpes(r_rs1, r_rs2);
+ break;
+ case 1:
+ gen_helper_fcmpes_fcc1(r_rs1, r_rs2);
+ break;
+ case 2:
+ gen_helper_fcmpes_fcc2(r_rs1, r_rs2);
+ break;
+ case 3:
+ gen_helper_fcmpes_fcc3(r_rs1, r_rs2);
+ break;
+ }
+}
+
+static inline void gen_op_fcmped(int fccno)
+{
+ switch (fccno) {
+ case 0:
+ gen_helper_fcmped();
+ break;
+ case 1:
+ gen_helper_fcmped_fcc1();
+ break;
+ case 2:
+ gen_helper_fcmped_fcc2();
+ break;
+ case 3:
+ gen_helper_fcmped_fcc3();
+ break;
+ }
+}
+
+static inline void gen_op_fcmpeq(int fccno)
+{
+ switch (fccno) {
+ case 0:
+ gen_helper_fcmpeq();
+ break;
+ case 1:
+ gen_helper_fcmpeq_fcc1();
+ break;
+ case 2:
+ gen_helper_fcmpeq_fcc2();
+ break;
+ case 3:
+ gen_helper_fcmpeq_fcc3();
+ break;
+ }
+}
+
+#else
+
+static inline void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2)
+{
+ gen_helper_fcmps(r_rs1, r_rs2);
+}
+
+static inline void gen_op_fcmpd(int fccno)
+{
+ gen_helper_fcmpd();
+}
+
+static inline void gen_op_fcmpq(int fccno)
+{
+ gen_helper_fcmpq();
+}
+
+static inline void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2)
+{
+ gen_helper_fcmpes(r_rs1, r_rs2);
+}
+
+static inline void gen_op_fcmped(int fccno)
+{
+ gen_helper_fcmped();
+}
+
+static inline void gen_op_fcmpeq(int fccno)
+{
+ gen_helper_fcmpeq();
+}
+#endif
+
+static inline void gen_op_fpexception_im(int fsr_flags)
+{
+ TCGv_i32 r_const;
+
+ tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_NMASK);
+ tcg_gen_ori_tl(cpu_fsr, cpu_fsr, fsr_flags);
+ r_const = tcg_const_i32(TT_FP_EXCP);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+}
+
+static int gen_trap_ifnofpu(DisasContext *dc, TCGv r_cond)
+{
+#if !defined(CONFIG_USER_ONLY)
+ if (!dc->fpu_enabled) {
+ TCGv_i32 r_const;
+
+ save_state(dc, r_cond);
+ r_const = tcg_const_i32(TT_NFPU_INSN);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ dc->is_br = 1;
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static inline void gen_op_clear_ieee_excp_and_FTT(void)
+{
+ tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK);
+}
+
+static inline void gen_clear_float_exceptions(void)
+{
+ gen_helper_clear_float_exceptions();
+}
+
+/* asi moves */
+#ifdef TARGET_SPARC64
+static inline TCGv_i32 gen_get_asi(int insn, TCGv r_addr)
+{
+ int asi;
+ TCGv_i32 r_asi;
+
+ if (IS_IMM) {
+ r_asi = tcg_temp_new_i32();
+ tcg_gen_mov_i32(r_asi, cpu_asi);
+ } else {
+ asi = GET_FIELD(insn, 19, 26);
+ r_asi = tcg_const_i32(asi);
+ }
+ return r_asi;
+}
+
+static inline void gen_ld_asi(TCGv dst, TCGv addr, int insn, int size,
+ int sign)
+{
+ TCGv_i32 r_asi, r_size, r_sign;
+
+ r_asi = gen_get_asi(insn, addr);
+ r_size = tcg_const_i32(size);
+ r_sign = tcg_const_i32(sign);
+ gen_helper_ld_asi(dst, addr, r_asi, r_size, r_sign);
+ tcg_temp_free_i32(r_sign);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+}
+
+static inline void gen_st_asi(TCGv src, TCGv addr, int insn, int size)
+{
+ TCGv_i32 r_asi, r_size;
+
+ r_asi = gen_get_asi(insn, addr);
+ r_size = tcg_const_i32(size);
+ gen_helper_st_asi(addr, src, r_asi, r_size);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+}
+
+static inline void gen_ldf_asi(TCGv addr, int insn, int size, int rd)
+{
+ TCGv_i32 r_asi, r_size, r_rd;
+
+ r_asi = gen_get_asi(insn, addr);
+ r_size = tcg_const_i32(size);
+ r_rd = tcg_const_i32(rd);
+ gen_helper_ldf_asi(addr, r_asi, r_size, r_rd);
+ tcg_temp_free_i32(r_rd);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+}
+
+static inline void gen_stf_asi(TCGv addr, int insn, int size, int rd)
+{
+ TCGv_i32 r_asi, r_size, r_rd;
+
+ r_asi = gen_get_asi(insn, addr);
+ r_size = tcg_const_i32(size);
+ r_rd = tcg_const_i32(rd);
+ gen_helper_stf_asi(addr, r_asi, r_size, r_rd);
+ tcg_temp_free_i32(r_rd);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+}
+
+static inline void gen_swap_asi(TCGv dst, TCGv addr, int insn)
+{
+ TCGv_i32 r_asi, r_size, r_sign;
+
+ r_asi = gen_get_asi(insn, addr);
+ r_size = tcg_const_i32(4);
+ r_sign = tcg_const_i32(0);
+ gen_helper_ld_asi(cpu_tmp64, addr, r_asi, r_size, r_sign);
+ tcg_temp_free_i32(r_sign);
+ gen_helper_st_asi(addr, dst, r_asi, r_size);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+ tcg_gen_trunc_i64_tl(dst, cpu_tmp64);
+}
+
+static inline void gen_ldda_asi(TCGv hi, TCGv addr, int insn, int rd)
+{
+ TCGv_i32 r_asi, r_rd;
+
+ r_asi = gen_get_asi(insn, addr);
+ r_rd = tcg_const_i32(rd);
+ gen_helper_ldda_asi(addr, r_asi, r_rd);
+ tcg_temp_free_i32(r_rd);
+ tcg_temp_free_i32(r_asi);
+}
+
+static inline void gen_stda_asi(TCGv hi, TCGv addr, int insn, int rd)
+{
+ TCGv_i32 r_asi, r_size;
+
+ gen_movl_reg_TN(rd + 1, cpu_tmp0);
+ tcg_gen_concat_tl_i64(cpu_tmp64, cpu_tmp0, hi);
+ r_asi = gen_get_asi(insn, addr);
+ r_size = tcg_const_i32(8);
+ gen_helper_st_asi(addr, cpu_tmp64, r_asi, r_size);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+}
+
+static inline void gen_cas_asi(TCGv dst, TCGv addr, TCGv val2, int insn,
+ int rd)
+{
+ TCGv r_val1;
+ TCGv_i32 r_asi;
+
+ r_val1 = tcg_temp_new();
+ gen_movl_reg_TN(rd, r_val1);
+ r_asi = gen_get_asi(insn, addr);
+ gen_helper_cas_asi(dst, addr, r_val1, val2, r_asi);
+ tcg_temp_free_i32(r_asi);
+ tcg_temp_free(r_val1);
+}
+
+static inline void gen_casx_asi(TCGv dst, TCGv addr, TCGv val2, int insn,
+ int rd)
+{
+ TCGv_i32 r_asi;
+
+ gen_movl_reg_TN(rd, cpu_tmp64);
+ r_asi = gen_get_asi(insn, addr);
+ gen_helper_casx_asi(dst, addr, cpu_tmp64, val2, r_asi);
+ tcg_temp_free_i32(r_asi);
+}
+
+#elif !defined(CONFIG_USER_ONLY)
+
+static inline void gen_ld_asi(TCGv dst, TCGv addr, int insn, int size,
+ int sign)
+{
+ TCGv_i32 r_asi, r_size, r_sign;
+
+ r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26));
+ r_size = tcg_const_i32(size);
+ r_sign = tcg_const_i32(sign);
+ gen_helper_ld_asi(cpu_tmp64, addr, r_asi, r_size, r_sign);
+ tcg_temp_free(r_sign);
+ tcg_temp_free(r_size);
+ tcg_temp_free(r_asi);
+ tcg_gen_trunc_i64_tl(dst, cpu_tmp64);
+}
+
+static inline void gen_st_asi(TCGv src, TCGv addr, int insn, int size)
+{
+ TCGv_i32 r_asi, r_size;
+
+ tcg_gen_extu_tl_i64(cpu_tmp64, src);
+ r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26));
+ r_size = tcg_const_i32(size);
+ gen_helper_st_asi(addr, cpu_tmp64, r_asi, r_size);
+ tcg_temp_free(r_size);
+ tcg_temp_free(r_asi);
+}
+
+static inline void gen_swap_asi(TCGv dst, TCGv addr, int insn)
+{
+ TCGv_i32 r_asi, r_size, r_sign;
+ TCGv_i64 r_val;
+
+ r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26));
+ r_size = tcg_const_i32(4);
+ r_sign = tcg_const_i32(0);
+ gen_helper_ld_asi(cpu_tmp64, addr, r_asi, r_size, r_sign);
+ tcg_temp_free(r_sign);
+ r_val = tcg_temp_new_i64();
+ tcg_gen_extu_tl_i64(r_val, dst);
+ gen_helper_st_asi(addr, r_val, r_asi, r_size);
+ tcg_temp_free_i64(r_val);
+ tcg_temp_free(r_size);
+ tcg_temp_free(r_asi);
+ tcg_gen_trunc_i64_tl(dst, cpu_tmp64);
+}
+
+static inline void gen_ldda_asi(TCGv hi, TCGv addr, int insn, int rd)
+{
+ TCGv_i32 r_asi, r_size, r_sign;
+
+ r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26));
+ r_size = tcg_const_i32(8);
+ r_sign = tcg_const_i32(0);
+ gen_helper_ld_asi(cpu_tmp64, addr, r_asi, r_size, r_sign);
+ tcg_temp_free(r_sign);
+ tcg_temp_free(r_size);
+ tcg_temp_free(r_asi);
+ tcg_gen_trunc_i64_tl(cpu_tmp0, cpu_tmp64);
+ gen_movl_TN_reg(rd + 1, cpu_tmp0);
+ tcg_gen_shri_i64(cpu_tmp64, cpu_tmp64, 32);
+ tcg_gen_trunc_i64_tl(hi, cpu_tmp64);
+ gen_movl_TN_reg(rd, hi);
+}
+
+static inline void gen_stda_asi(TCGv hi, TCGv addr, int insn, int rd)
+{
+ TCGv_i32 r_asi, r_size;
+
+ gen_movl_reg_TN(rd + 1, cpu_tmp0);
+ tcg_gen_concat_tl_i64(cpu_tmp64, cpu_tmp0, hi);
+ r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26));
+ r_size = tcg_const_i32(8);
+ gen_helper_st_asi(addr, cpu_tmp64, r_asi, r_size);
+ tcg_temp_free(r_size);
+ tcg_temp_free(r_asi);
+}
+#endif
+
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+static inline void gen_ldstub_asi(TCGv dst, TCGv addr, int insn)
+{
+ TCGv_i64 r_val;
+ TCGv_i32 r_asi, r_size;
+
+ gen_ld_asi(dst, addr, insn, 1, 0);
+
+ r_val = tcg_const_i64(0xffULL);
+ r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26));
+ r_size = tcg_const_i32(1);
+ gen_helper_st_asi(addr, r_val, r_asi, r_size);
+ tcg_temp_free_i32(r_size);
+ tcg_temp_free_i32(r_asi);
+ tcg_temp_free_i64(r_val);
+}
+#endif
+
+static inline TCGv get_src1(unsigned int insn, TCGv def)
+{
+ TCGv r_rs1 = def;
+ unsigned int rs1;
+
+ rs1 = GET_FIELD(insn, 13, 17);
+ if (rs1 == 0) {
+ tcg_gen_movi_tl(def, 0);
+ } else if (rs1 < 8) {
+ r_rs1 = cpu_gregs[rs1];
+ } else {
+ tcg_gen_ld_tl(def, cpu_regwptr, (rs1 - 8) * sizeof(target_ulong));
+ }
+ return r_rs1;
+}
+
+static inline TCGv get_src2(unsigned int insn, TCGv def)
+{
+ TCGv r_rs2 = def;
+
+ if (IS_IMM) { /* immediate */
+ target_long simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_movi_tl(def, simm);
+ } else { /* register */
+ unsigned int rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2 == 0) {
+ tcg_gen_movi_tl(def, 0);
+ } else if (rs2 < 8) {
+ r_rs2 = cpu_gregs[rs2];
+ } else {
+ tcg_gen_ld_tl(def, cpu_regwptr, (rs2 - 8) * sizeof(target_ulong));
+ }
+ }
+ return r_rs2;
+}
+
+#ifdef TARGET_SPARC64
+static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env)
+{
+ TCGv_i32 r_tl = tcg_temp_new_i32();
+
+ /* load env->tl into r_tl */
+ tcg_gen_ld_i32(r_tl, cpu_env, offsetof(CPUSPARCState, tl));
+
+ /* tl = [0 ... MAXTL_MASK] where MAXTL_MASK must be power of 2 */
+ tcg_gen_andi_i32(r_tl, r_tl, MAXTL_MASK);
+
+ /* calculate offset to current trap state from env->ts, reuse r_tl */
+ tcg_gen_muli_i32(r_tl, r_tl, sizeof (trap_state));
+ tcg_gen_addi_ptr(r_tsptr, cpu_env, offsetof(CPUState, ts));
+
+ /* tsptr = env->ts[env->tl & MAXTL_MASK] */
+ {
+ TCGv_ptr r_tl_tmp = tcg_temp_new_ptr();
+ tcg_gen_ext_i32_ptr(r_tl_tmp, r_tl);
+ tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl_tmp);
+ tcg_temp_free_ptr(r_tl_tmp);
+ }
+
+ tcg_temp_free_i32(r_tl);
+}
+#endif
+
+#define CHECK_IU_FEATURE(dc, FEATURE) \
+ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \
+ goto illegal_insn;
+#define CHECK_FPU_FEATURE(dc, FEATURE) \
+ if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE)) \
+ goto nfpu_insn;
+
+/* before an instruction, dc->pc must be static */
+static void disas_sparc_insn(DisasContext * dc)
+{
+ unsigned int insn, opc, rs1, rs2, rd;
+ TCGv cpu_src1, cpu_src2, cpu_tmp1, cpu_tmp2;
+ target_long simm;
+
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
+ tcg_gen_debug_insn_start(dc->pc);
+ insn = ldl_code(dc->pc);
+ opc = GET_FIELD(insn, 0, 1);
+
+ rd = GET_FIELD(insn, 2, 6);
+
+ cpu_tmp1 = cpu_src1 = tcg_temp_new();
+ cpu_tmp2 = cpu_src2 = tcg_temp_new();
+
+ switch (opc) {
+ case 0: /* branches/sethi */
+ {
+ unsigned int xop = GET_FIELD(insn, 7, 9);
+ int32_t target;
+ switch (xop) {
+#ifdef TARGET_SPARC64
+ case 0x1: /* V9 BPcc */
+ {
+ int cc;
+
+ target = GET_FIELD_SP(insn, 0, 18);
+ target = sign_extend(target, 18);
+ target <<= 2;
+ cc = GET_FIELD_SP(insn, 20, 21);
+ if (cc == 0)
+ do_branch(dc, target, insn, 0, cpu_cond);
+ else if (cc == 2)
+ do_branch(dc, target, insn, 1, cpu_cond);
+ else
+ goto illegal_insn;
+ goto jmp_insn;
+ }
+ case 0x3: /* V9 BPr */
+ {
+ target = GET_FIELD_SP(insn, 0, 13) |
+ (GET_FIELD_SP(insn, 20, 21) << 14);
+ target = sign_extend(target, 16);
+ target <<= 2;
+ cpu_src1 = get_src1(insn, cpu_src1);
+ do_branch_reg(dc, target, insn, cpu_cond, cpu_src1);
+ goto jmp_insn;
+ }
+ case 0x5: /* V9 FBPcc */
+ {
+ int cc = GET_FIELD_SP(insn, 20, 21);
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ target = GET_FIELD_SP(insn, 0, 18);
+ target = sign_extend(target, 19);
+ target <<= 2;
+ do_fbranch(dc, target, insn, cc, cpu_cond);
+ goto jmp_insn;
+ }
+#else
+ case 0x7: /* CBN+x */
+ {
+ goto ncp_insn;
+ }
+#endif
+ case 0x2: /* BN+x */
+ {
+ target = GET_FIELD(insn, 10, 31);
+ target = sign_extend(target, 22);
+ target <<= 2;
+ do_branch(dc, target, insn, 0, cpu_cond);
+ goto jmp_insn;
+ }
+ case 0x6: /* FBN+x */
+ {
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ target = GET_FIELD(insn, 10, 31);
+ target = sign_extend(target, 22);
+ target <<= 2;
+ do_fbranch(dc, target, insn, 0, cpu_cond);
+ goto jmp_insn;
+ }
+ case 0x4: /* SETHI */
+ if (rd) { // nop
+ uint32_t value = GET_FIELD(insn, 10, 31);
+ TCGv r_const;
+
+ r_const = tcg_const_tl(value << 10);
+ gen_movl_TN_reg(rd, r_const);
+ tcg_temp_free(r_const);
+ }
+ break;
+ case 0x0: /* UNIMPL */
+ default:
+ goto illegal_insn;
+ }
+ break;
+ }
+ break;
+ case 1: /*CALL*/
+ {
+ target_long target = GET_FIELDs(insn, 2, 31) << 2;
+ TCGv r_const;
+
+ r_const = tcg_const_tl(dc->pc);
+ gen_movl_TN_reg(15, r_const);
+ tcg_temp_free(r_const);
+ target += dc->pc;
+ gen_mov_pc_npc(dc, cpu_cond);
+ dc->npc = target;
+ }
+ goto jmp_insn;
+ case 2: /* FPU & Logical Operations */
+ {
+ unsigned int xop = GET_FIELD(insn, 7, 12);
+ if (xop == 0x3a) { /* generate trap */
+ int cond;
+
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) {
+ rs2 = GET_FIELD(insn, 25, 31);
+ tcg_gen_addi_tl(cpu_dst, cpu_src1, rs2);
+ } else {
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2 != 0) {
+ gen_movl_reg_TN(rs2, cpu_src2);
+ tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2);
+ } else
+ tcg_gen_mov_tl(cpu_dst, cpu_src1);
+ }
+
+ cond = GET_FIELD(insn, 3, 6);
+ if (cond == 0x8) { /* Trap Always */
+ save_state(dc, cpu_cond);
+ if ((dc->def->features & CPU_FEATURE_HYPV) &&
+ supervisor(dc))
+ tcg_gen_andi_tl(cpu_dst, cpu_dst, UA2005_HTRAP_MASK);
+ else
+ tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK);
+ tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP);
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst);
+
+ if (rs2 == 0 &&
+ dc->def->features & CPU_FEATURE_TA0_SHUTDOWN) {
+
+ gen_helper_shutdown();
+
+ } else {
+ gen_helper_raise_exception(cpu_tmp32);
+ }
+ } else if (cond != 0) {
+ TCGv r_cond = tcg_temp_new();
+ int l1;
+#ifdef TARGET_SPARC64
+ /* V9 icc/xcc */
+ int cc = GET_FIELD_SP(insn, 11, 12);
+
+ save_state(dc, cpu_cond);
+ if (cc == 0)
+ gen_cond(r_cond, 0, cond, dc);
+ else if (cc == 2)
+ gen_cond(r_cond, 1, cond, dc);
+ else
+ goto illegal_insn;
+#else
+ save_state(dc, cpu_cond);
+ gen_cond(r_cond, 0, cond, dc);
+#endif
+ l1 = gen_new_label();
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1);
+
+ if ((dc->def->features & CPU_FEATURE_HYPV) &&
+ supervisor(dc))
+ tcg_gen_andi_tl(cpu_dst, cpu_dst, UA2005_HTRAP_MASK);
+ else
+ tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK);
+ tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP);
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst);
+ gen_helper_raise_exception(cpu_tmp32);
+
+ gen_set_label(l1);
+ tcg_temp_free(r_cond);
+ }
+ gen_op_next_insn();
+ tcg_gen_exit_tb(0);
+ dc->is_br = 1;
+ goto jmp_insn;
+ } else if (xop == 0x28) {
+ rs1 = GET_FIELD(insn, 13, 17);
+ switch(rs1) {
+ case 0: /* rdy */
+#ifndef TARGET_SPARC64
+ case 0x01 ... 0x0e: /* undefined in the SPARCv8
+ manual, rdy on the microSPARC
+ II */
+ case 0x0f: /* stbar in the SPARCv8 manual,
+ rdy on the microSPARC II */
+ case 0x10 ... 0x1f: /* implementation-dependent in the
+ SPARCv8 manual, rdy on the
+ microSPARC II */
+ /* Read Asr17 */
+ if (rs1 == 0x11 && dc->def->features & CPU_FEATURE_ASR17) {
+ TCGv r_const;
+
+ /* Read Asr17 for a Leon3 monoprocessor */
+ r_const = tcg_const_tl((1 << 8)
+ | (dc->def->nwindows - 1));
+ gen_movl_TN_reg(rd, r_const);
+ tcg_temp_free(r_const);
+ break;
+ }
+#endif
+ gen_movl_TN_reg(rd, cpu_y);
+ break;
+#ifdef TARGET_SPARC64
+ case 0x2: /* V9 rdccr */
+ gen_helper_compute_psr();
+ gen_helper_rdccr(cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x3: /* V9 rdasi */
+ tcg_gen_ext_i32_tl(cpu_dst, cpu_asi);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x4: /* V9 rdtick */
+ {
+ TCGv_ptr r_tickptr;
+
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, tick));
+ gen_helper_tick_get_count(cpu_dst, r_tickptr);
+ tcg_temp_free_ptr(r_tickptr);
+ gen_movl_TN_reg(rd, cpu_dst);
+ }
+ break;
+ case 0x5: /* V9 rdpc */
+ {
+ TCGv r_const;
+
+ r_const = tcg_const_tl(dc->pc);
+ gen_movl_TN_reg(rd, r_const);
+ tcg_temp_free(r_const);
+ }
+ break;
+ case 0x6: /* V9 rdfprs */
+ tcg_gen_ext_i32_tl(cpu_dst, cpu_fprs);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0xf: /* V9 membar */
+ break; /* no effect */
+ case 0x13: /* Graphics Status */
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ gen_movl_TN_reg(rd, cpu_gsr);
+ break;
+ case 0x16: /* Softint */
+ tcg_gen_ext_i32_tl(cpu_dst, cpu_softint);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x17: /* Tick compare */
+ gen_movl_TN_reg(rd, cpu_tick_cmpr);
+ break;
+ case 0x18: /* System tick */
+ {
+ TCGv_ptr r_tickptr;
+
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, stick));
+ gen_helper_tick_get_count(cpu_dst, r_tickptr);
+ tcg_temp_free_ptr(r_tickptr);
+ gen_movl_TN_reg(rd, cpu_dst);
+ }
+ break;
+ case 0x19: /* System tick compare */
+ gen_movl_TN_reg(rd, cpu_stick_cmpr);
+ break;
+ case 0x10: /* Performance Control */
+ case 0x11: /* Performance Instrumentation Counter */
+ case 0x12: /* Dispatch Control */
+ case 0x14: /* Softint set, WO */
+ case 0x15: /* Softint clear, WO */
+#endif
+ default:
+ goto illegal_insn;
+ }
+#if !defined(CONFIG_USER_ONLY)
+ } else if (xop == 0x29) { /* rdpsr / UA2005 rdhpr */
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_helper_compute_psr();
+ dc->cc_op = CC_OP_FLAGS;
+ gen_helper_rdpsr(cpu_dst);
+#else
+ CHECK_IU_FEATURE(dc, HYPV);
+ if (!hypervisor(dc))
+ goto priv_insn;
+ rs1 = GET_FIELD(insn, 13, 17);
+ switch (rs1) {
+ case 0: // hpstate
+ // gen_op_rdhpstate();
+ break;
+ case 1: // htstate
+ // gen_op_rdhtstate();
+ break;
+ case 3: // hintp
+ tcg_gen_mov_tl(cpu_dst, cpu_hintp);
+ break;
+ case 5: // htba
+ tcg_gen_mov_tl(cpu_dst, cpu_htba);
+ break;
+ case 6: // hver
+ tcg_gen_mov_tl(cpu_dst, cpu_hver);
+ break;
+ case 31: // hstick_cmpr
+ tcg_gen_mov_tl(cpu_dst, cpu_hstick_cmpr);
+ break;
+ default:
+ goto illegal_insn;
+ }
+#endif
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ } else if (xop == 0x2a) { /* rdwim / V9 rdpr */
+ if (!supervisor(dc))
+ goto priv_insn;
+#ifdef TARGET_SPARC64
+ rs1 = GET_FIELD(insn, 13, 17);
+ switch (rs1) {
+ case 0: // tpc
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_ld_tl(cpu_tmp0, r_tsptr,
+ offsetof(trap_state, tpc));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 1: // tnpc
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_ld_tl(cpu_tmp0, r_tsptr,
+ offsetof(trap_state, tnpc));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 2: // tstate
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_ld_tl(cpu_tmp0, r_tsptr,
+ offsetof(trap_state, tstate));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 3: // tt
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_ld_i32(cpu_tmp32, r_tsptr,
+ offsetof(trap_state, tt));
+ tcg_temp_free_ptr(r_tsptr);
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ }
+ break;
+ case 4: // tick
+ {
+ TCGv_ptr r_tickptr;
+
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, tick));
+ gen_helper_tick_get_count(cpu_tmp0, r_tickptr);
+ gen_movl_TN_reg(rd, cpu_tmp0);
+ tcg_temp_free_ptr(r_tickptr);
+ }
+ break;
+ case 5: // tba
+ tcg_gen_mov_tl(cpu_tmp0, cpu_tbr);
+ break;
+ case 6: // pstate
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, pstate));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 7: // tl
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, tl));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 8: // pil
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, psrpil));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 9: // cwp
+ gen_helper_rdcwp(cpu_tmp0);
+ break;
+ case 10: // cansave
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, cansave));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 11: // canrestore
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, canrestore));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 12: // cleanwin
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, cleanwin));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 13: // otherwin
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, otherwin));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 14: // wstate
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, wstate));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 16: // UA2005 gl
+ CHECK_IU_FEATURE(dc, GL);
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, gl));
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_tmp32);
+ break;
+ case 26: // UA2005 strand status
+ CHECK_IU_FEATURE(dc, HYPV);
+ if (!hypervisor(dc))
+ goto priv_insn;
+ tcg_gen_mov_tl(cpu_tmp0, cpu_ssr);
+ break;
+ case 31: // ver
+ tcg_gen_mov_tl(cpu_tmp0, cpu_ver);
+ break;
+ case 15: // fq
+ default:
+ goto illegal_insn;
+ }
+#else
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_wim);
+#endif
+ gen_movl_TN_reg(rd, cpu_tmp0);
+ break;
+ } else if (xop == 0x2b) { /* rdtbr / V9 flushw */
+#ifdef TARGET_SPARC64
+ save_state(dc, cpu_cond);
+ gen_helper_flushw();
+#else
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_movl_TN_reg(rd, cpu_tbr);
+#endif
+ break;
+#endif
+ } else if (xop == 0x34) { /* FPU Operations */
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ gen_op_clear_ieee_excp_and_FTT();
+ rs1 = GET_FIELD(insn, 13, 17);
+ rs2 = GET_FIELD(insn, 27, 31);
+ xop = GET_FIELD(insn, 18, 26);
+ save_state(dc, cpu_cond);
+ switch (xop) {
+ case 0x1: /* fmovs */
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ break;
+ case 0x5: /* fnegs */
+ gen_helper_fnegs(cpu_fpr[rd], cpu_fpr[rs2]);
+ break;
+ case 0x9: /* fabss */
+ gen_helper_fabss(cpu_fpr[rd], cpu_fpr[rs2]);
+ break;
+ case 0x29: /* fsqrts */
+ CHECK_FPU_FEATURE(dc, FSQRT);
+ gen_clear_float_exceptions();
+ gen_helper_fsqrts(cpu_tmp32, cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0x2a: /* fsqrtd */
+ CHECK_FPU_FEATURE(dc, FSQRT);
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fsqrtd();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x2b: /* fsqrtq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fsqrtq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0x41: /* fadds */
+ gen_clear_float_exceptions();
+ gen_helper_fadds(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0x42: /* faddd */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_faddd();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x43: /* faddq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_faddq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0x45: /* fsubs */
+ gen_clear_float_exceptions();
+ gen_helper_fsubs(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0x46: /* fsubd */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fsubd();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x47: /* fsubq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fsubq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0x49: /* fmuls */
+ CHECK_FPU_FEATURE(dc, FMUL);
+ gen_clear_float_exceptions();
+ gen_helper_fmuls(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0x4a: /* fmuld */
+ CHECK_FPU_FEATURE(dc, FMUL);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fmuld();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x4b: /* fmulq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ CHECK_FPU_FEATURE(dc, FMUL);
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fmulq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0x4d: /* fdivs */
+ gen_clear_float_exceptions();
+ gen_helper_fdivs(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0x4e: /* fdivd */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fdivd();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x4f: /* fdivq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fdivq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0x69: /* fsmuld */
+ CHECK_FPU_FEATURE(dc, FSMULD);
+ gen_clear_float_exceptions();
+ gen_helper_fsmuld(cpu_fpr[rs1], cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x6e: /* fdmulq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fdmulq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0xc4: /* fitos */
+ gen_clear_float_exceptions();
+ gen_helper_fitos(cpu_tmp32, cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0xc6: /* fdtos */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fdtos(cpu_tmp32);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0xc7: /* fqtos */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fqtos(cpu_tmp32);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0xc8: /* fitod */
+ gen_helper_fitod(cpu_fpr[rs2]);
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xc9: /* fstod */
+ gen_helper_fstod(cpu_fpr[rs2]);
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xcb: /* fqtod */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fqtod();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xcc: /* fitoq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_helper_fitoq(cpu_fpr[rs2]);
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0xcd: /* fstoq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_helper_fstoq(cpu_fpr[rs2]);
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0xce: /* fdtoq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fdtoq();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0xd1: /* fstoi */
+ gen_clear_float_exceptions();
+ gen_helper_fstoi(cpu_tmp32, cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0xd2: /* fdtoi */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fdtoi(cpu_tmp32);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0xd3: /* fqtoi */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fqtoi(cpu_tmp32);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+#ifdef TARGET_SPARC64
+ case 0x2: /* V9 fmovd */
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x3: /* V9 fmovq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], cpu_fpr[QFPREG(rs2)]);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1],
+ cpu_fpr[QFPREG(rs2) + 1]);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2],
+ cpu_fpr[QFPREG(rs2) + 2]);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3],
+ cpu_fpr[QFPREG(rs2) + 3]);
+ break;
+ case 0x6: /* V9 fnegd */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fnegd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x7: /* V9 fnegq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_helper_fnegq();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0xa: /* V9 fabsd */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fabsd();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0xb: /* V9 fabsq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_helper_fabsq();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+ case 0x81: /* V9 fstox */
+ gen_clear_float_exceptions();
+ gen_helper_fstox(cpu_fpr[rs2]);
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x82: /* V9 fdtox */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fdtox();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x83: /* V9 fqtox */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fqtox();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x84: /* V9 fxtos */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fxtos(cpu_tmp32);
+ gen_helper_check_ieee_exceptions();
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
+ break;
+ case 0x88: /* V9 fxtod */
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fxtod();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x8c: /* V9 fxtoq */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_clear_float_exceptions();
+ gen_helper_fxtoq();
+ gen_helper_check_ieee_exceptions();
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop == 0x35) { /* FPU Operations */
+#ifdef TARGET_SPARC64
+ int cond;
+#endif
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ gen_op_clear_ieee_excp_and_FTT();
+ rs1 = GET_FIELD(insn, 13, 17);
+ rs2 = GET_FIELD(insn, 27, 31);
+ xop = GET_FIELD(insn, 18, 26);
+ save_state(dc, cpu_cond);
+#ifdef TARGET_SPARC64
+ if ((xop & 0x11f) == 0x005) { // V9 fmovsr
+ int l1;
+
+ l1 = gen_new_label();
+ cond = GET_FIELD_SP(insn, 14, 17);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
+ 0, l1);
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ gen_set_label(l1);
+ break;
+ } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr
+ int l1;
+
+ l1 = gen_new_label();
+ cond = GET_FIELD_SP(insn, 14, 17);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
+ 0, l1);
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1]);
+ gen_set_label(l1);
+ break;
+ } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr
+ int l1;
+
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ l1 = gen_new_label();
+ cond = GET_FIELD_SP(insn, 14, 17);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
+ 0, l1);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], cpu_fpr[QFPREG(rs2)]);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], cpu_fpr[QFPREG(rs2) + 1]);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], cpu_fpr[QFPREG(rs2) + 2]);
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], cpu_fpr[QFPREG(rs2) + 3]);
+ gen_set_label(l1);
+ break;
+ }
+#endif
+ switch (xop) {
+#ifdef TARGET_SPARC64
+#define FMOVSCC(fcc) \
+ { \
+ TCGv r_cond; \
+ int l1; \
+ \
+ l1 = gen_new_label(); \
+ r_cond = tcg_temp_new(); \
+ cond = GET_FIELD_SP(insn, 14, 17); \
+ gen_fcond(r_cond, fcc, cond); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
+ 0, l1); \
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \
+ gen_set_label(l1); \
+ tcg_temp_free(r_cond); \
+ }
+#define FMOVDCC(fcc) \
+ { \
+ TCGv r_cond; \
+ int l1; \
+ \
+ l1 = gen_new_label(); \
+ r_cond = tcg_temp_new(); \
+ cond = GET_FIELD_SP(insn, 14, 17); \
+ gen_fcond(r_cond, fcc, cond); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
+ 0, l1); \
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], \
+ cpu_fpr[DFPREG(rs2)]); \
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \
+ cpu_fpr[DFPREG(rs2) + 1]); \
+ gen_set_label(l1); \
+ tcg_temp_free(r_cond); \
+ }
+#define FMOVQCC(fcc) \
+ { \
+ TCGv r_cond; \
+ int l1; \
+ \
+ l1 = gen_new_label(); \
+ r_cond = tcg_temp_new(); \
+ cond = GET_FIELD_SP(insn, 14, 17); \
+ gen_fcond(r_cond, fcc, cond); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
+ 0, l1); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], \
+ cpu_fpr[QFPREG(rs2)]); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], \
+ cpu_fpr[QFPREG(rs2) + 1]); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], \
+ cpu_fpr[QFPREG(rs2) + 2]); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \
+ cpu_fpr[QFPREG(rs2) + 3]); \
+ gen_set_label(l1); \
+ tcg_temp_free(r_cond); \
+ }
+ case 0x001: /* V9 fmovscc %fcc0 */
+ FMOVSCC(0);
+ break;
+ case 0x002: /* V9 fmovdcc %fcc0 */
+ FMOVDCC(0);
+ break;
+ case 0x003: /* V9 fmovqcc %fcc0 */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ FMOVQCC(0);
+ break;
+ case 0x041: /* V9 fmovscc %fcc1 */
+ FMOVSCC(1);
+ break;
+ case 0x042: /* V9 fmovdcc %fcc1 */
+ FMOVDCC(1);
+ break;
+ case 0x043: /* V9 fmovqcc %fcc1 */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ FMOVQCC(1);
+ break;
+ case 0x081: /* V9 fmovscc %fcc2 */
+ FMOVSCC(2);
+ break;
+ case 0x082: /* V9 fmovdcc %fcc2 */
+ FMOVDCC(2);
+ break;
+ case 0x083: /* V9 fmovqcc %fcc2 */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ FMOVQCC(2);
+ break;
+ case 0x0c1: /* V9 fmovscc %fcc3 */
+ FMOVSCC(3);
+ break;
+ case 0x0c2: /* V9 fmovdcc %fcc3 */
+ FMOVDCC(3);
+ break;
+ case 0x0c3: /* V9 fmovqcc %fcc3 */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ FMOVQCC(3);
+ break;
+#undef FMOVSCC
+#undef FMOVDCC
+#undef FMOVQCC
+#define FMOVSCC(icc) \
+ { \
+ TCGv r_cond; \
+ int l1; \
+ \
+ l1 = gen_new_label(); \
+ r_cond = tcg_temp_new(); \
+ cond = GET_FIELD_SP(insn, 14, 17); \
+ gen_cond(r_cond, icc, cond, dc); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
+ 0, l1); \
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \
+ gen_set_label(l1); \
+ tcg_temp_free(r_cond); \
+ }
+#define FMOVDCC(icc) \
+ { \
+ TCGv r_cond; \
+ int l1; \
+ \
+ l1 = gen_new_label(); \
+ r_cond = tcg_temp_new(); \
+ cond = GET_FIELD_SP(insn, 14, 17); \
+ gen_cond(r_cond, icc, cond, dc); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
+ 0, l1); \
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], \
+ cpu_fpr[DFPREG(rs2)]); \
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \
+ cpu_fpr[DFPREG(rs2) + 1]); \
+ gen_set_label(l1); \
+ tcg_temp_free(r_cond); \
+ }
+#define FMOVQCC(icc) \
+ { \
+ TCGv r_cond; \
+ int l1; \
+ \
+ l1 = gen_new_label(); \
+ r_cond = tcg_temp_new(); \
+ cond = GET_FIELD_SP(insn, 14, 17); \
+ gen_cond(r_cond, icc, cond, dc); \
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
+ 0, l1); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], \
+ cpu_fpr[QFPREG(rs2)]); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], \
+ cpu_fpr[QFPREG(rs2) + 1]); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], \
+ cpu_fpr[QFPREG(rs2) + 2]); \
+ tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \
+ cpu_fpr[QFPREG(rs2) + 3]); \
+ gen_set_label(l1); \
+ tcg_temp_free(r_cond); \
+ }
+
+ case 0x101: /* V9 fmovscc %icc */
+ FMOVSCC(0);
+ break;
+ case 0x102: /* V9 fmovdcc %icc */
+ FMOVDCC(0);
+ case 0x103: /* V9 fmovqcc %icc */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ FMOVQCC(0);
+ break;
+ case 0x181: /* V9 fmovscc %xcc */
+ FMOVSCC(1);
+ break;
+ case 0x182: /* V9 fmovdcc %xcc */
+ FMOVDCC(1);
+ break;
+ case 0x183: /* V9 fmovqcc %xcc */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ FMOVQCC(1);
+ break;
+#undef FMOVSCC
+#undef FMOVDCC
+#undef FMOVQCC
+#endif
+ case 0x51: /* fcmps, V9 %fcc */
+ gen_op_fcmps(rd & 3, cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x52: /* fcmpd, V9 %fcc */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fcmpd(rd & 3);
+ break;
+ case 0x53: /* fcmpq, V9 %fcc */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_op_fcmpq(rd & 3);
+ break;
+ case 0x55: /* fcmpes, V9 %fcc */
+ gen_op_fcmpes(rd & 3, cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x56: /* fcmped, V9 %fcc */
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_op_fcmped(rd & 3);
+ break;
+ case 0x57: /* fcmpeq, V9 %fcc */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+ gen_op_fcmpeq(rd & 3);
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop == 0x2) {
+ // clr/mov shortcut
+
+ rs1 = GET_FIELD(insn, 13, 17);
+ if (rs1 == 0) {
+ // or %g0, x, y -> mov T0, x; mov y, T0
+ if (IS_IMM) { /* immediate */
+ TCGv r_const;
+
+ simm = GET_FIELDs(insn, 19, 31);
+ r_const = tcg_const_tl(simm);
+ gen_movl_TN_reg(rd, r_const);
+ tcg_temp_free(r_const);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_TN(rs2, cpu_dst);
+ gen_movl_TN_reg(rd, cpu_dst);
+ }
+ } else {
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_ori_tl(cpu_dst, cpu_src1, simm);
+ gen_movl_TN_reg(rd, cpu_dst);
+ } else { /* register */
+ // or x, %g0, y -> mov T1, x; mov y, T1
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2 != 0) {
+ gen_movl_reg_TN(rs2, cpu_src2);
+ tcg_gen_or_tl(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ } else
+ gen_movl_TN_reg(rd, cpu_src1);
+ }
+ }
+#ifdef TARGET_SPARC64
+ } else if (xop == 0x25) { /* sll, V9 sllx */
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 20, 31);
+ if (insn & (1 << 12)) {
+ tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x3f);
+ } else {
+ tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x1f);
+ }
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ if (insn & (1 << 12)) {
+ tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f);
+ } else {
+ tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f);
+ }
+ tcg_gen_shl_i64(cpu_dst, cpu_src1, cpu_tmp0);
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+ } else if (xop == 0x26) { /* srl, V9 srlx */
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 20, 31);
+ if (insn & (1 << 12)) {
+ tcg_gen_shri_i64(cpu_dst, cpu_src1, simm & 0x3f);
+ } else {
+ tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL);
+ tcg_gen_shri_i64(cpu_dst, cpu_dst, simm & 0x1f);
+ }
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ if (insn & (1 << 12)) {
+ tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f);
+ tcg_gen_shr_i64(cpu_dst, cpu_src1, cpu_tmp0);
+ } else {
+ tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f);
+ tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL);
+ tcg_gen_shr_i64(cpu_dst, cpu_dst, cpu_tmp0);
+ }
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+ } else if (xop == 0x27) { /* sra, V9 srax */
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 20, 31);
+ if (insn & (1 << 12)) {
+ tcg_gen_sari_i64(cpu_dst, cpu_src1, simm & 0x3f);
+ } else {
+ tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL);
+ tcg_gen_ext32s_i64(cpu_dst, cpu_dst);
+ tcg_gen_sari_i64(cpu_dst, cpu_dst, simm & 0x1f);
+ }
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ if (insn & (1 << 12)) {
+ tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f);
+ tcg_gen_sar_i64(cpu_dst, cpu_src1, cpu_tmp0);
+ } else {
+ tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f);
+ tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL);
+ tcg_gen_ext32s_i64(cpu_dst, cpu_dst);
+ tcg_gen_sar_i64(cpu_dst, cpu_dst, cpu_tmp0);
+ }
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+#endif
+ } else if (xop < 0x36) {
+ if (xop < 0x20) {
+ cpu_src1 = get_src1(insn, cpu_src1);
+ cpu_src2 = get_src2(insn, cpu_src2);
+ switch (xop & ~0x10) {
+ case 0x0: /* add */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ if (xop & 0x10) {
+ gen_op_addi_cc(cpu_dst, cpu_src1, simm);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD);
+ dc->cc_op = CC_OP_ADD;
+ } else {
+ tcg_gen_addi_tl(cpu_dst, cpu_src1, simm);
+ }
+ } else {
+ if (xop & 0x10) {
+ gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD);
+ dc->cc_op = CC_OP_ADD;
+ } else {
+ tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ }
+ break;
+ case 0x1: /* and */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_andi_tl(cpu_dst, cpu_src1, simm);
+ } else {
+ tcg_gen_and_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0x2: /* or */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_ori_tl(cpu_dst, cpu_src1, simm);
+ } else {
+ tcg_gen_or_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0x3: /* xor */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_xori_tl(cpu_dst, cpu_src1, simm);
+ } else {
+ tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0x4: /* sub */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ if (xop & 0x10) {
+ gen_op_subi_cc(cpu_dst, cpu_src1, simm, dc);
+ } else {
+ tcg_gen_subi_tl(cpu_dst, cpu_src1, simm);
+ }
+ } else {
+ if (xop & 0x10) {
+ gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB);
+ dc->cc_op = CC_OP_SUB;
+ } else {
+ tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ }
+ break;
+ case 0x5: /* andn */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_andi_tl(cpu_dst, cpu_src1, ~simm);
+ } else {
+ tcg_gen_andc_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0x6: /* orn */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_ori_tl(cpu_dst, cpu_src1, ~simm);
+ } else {
+ tcg_gen_orc_tl(cpu_dst, cpu_src1, cpu_src2);
+ }
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0x7: /* xorn */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_xori_tl(cpu_dst, cpu_src1, ~simm);
+ } else {
+ tcg_gen_not_tl(cpu_tmp0, cpu_src2);
+ tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_tmp0);
+ }
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0x8: /* addx, V9 addc */
+ gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2,
+ (xop & 0x10));
+ break;
+#ifdef TARGET_SPARC64
+ case 0x9: /* V9 mulx */
+ if (IS_IMM) {
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_muli_i64(cpu_dst, cpu_src1, simm);
+ } else {
+ tcg_gen_mul_i64(cpu_dst, cpu_src1, cpu_src2);
+ }
+ break;
+#endif
+ case 0xa: /* umul */
+ CHECK_IU_FEATURE(dc, MUL);
+ gen_op_umul(cpu_dst, cpu_src1, cpu_src2);
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0xb: /* smul */
+ CHECK_IU_FEATURE(dc, MUL);
+ gen_op_smul(cpu_dst, cpu_src1, cpu_src2);
+ if (xop & 0x10) {
+ tcg_gen_mov_tl(cpu_cc_dst, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC);
+ dc->cc_op = CC_OP_LOGIC;
+ }
+ break;
+ case 0xc: /* subx, V9 subc */
+ gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2,
+ (xop & 0x10));
+ break;
+#ifdef TARGET_SPARC64
+ case 0xd: /* V9 udivx */
+ tcg_gen_mov_tl(cpu_cc_src, cpu_src1);
+ tcg_gen_mov_tl(cpu_cc_src2, cpu_src2);
+ gen_trap_ifdivzero_tl(cpu_cc_src2);
+ tcg_gen_divu_i64(cpu_dst, cpu_cc_src, cpu_cc_src2);
+ break;
+#endif
+ case 0xe: /* udiv */
+ CHECK_IU_FEATURE(dc, DIV);
+ if (xop & 0x10) {
+ gen_helper_udiv_cc(cpu_dst, cpu_src1, cpu_src2);
+ dc->cc_op = CC_OP_DIV;
+ } else {
+ gen_helper_udiv(cpu_dst, cpu_src1, cpu_src2);
+ }
+ break;
+ case 0xf: /* sdiv */
+ CHECK_IU_FEATURE(dc, DIV);
+ if (xop & 0x10) {
+ gen_helper_sdiv_cc(cpu_dst, cpu_src1, cpu_src2);
+ dc->cc_op = CC_OP_DIV;
+ } else {
+ gen_helper_sdiv(cpu_dst, cpu_src1, cpu_src2);
+ }
+ break;
+ default:
+ goto illegal_insn;
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+ } else {
+ cpu_src1 = get_src1(insn, cpu_src1);
+ cpu_src2 = get_src2(insn, cpu_src2);
+ switch (xop) {
+ case 0x20: /* taddcc */
+ gen_op_tadd_cc(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_TADD);
+ dc->cc_op = CC_OP_TADD;
+ break;
+ case 0x21: /* tsubcc */
+ gen_op_tsub_cc(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_TSUB);
+ dc->cc_op = CC_OP_TSUB;
+ break;
+ case 0x22: /* taddcctv */
+ save_state(dc, cpu_cond);
+ gen_op_tadd_ccTV(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_TADDTV);
+ dc->cc_op = CC_OP_TADDTV;
+ break;
+ case 0x23: /* tsubcctv */
+ save_state(dc, cpu_cond);
+ gen_op_tsub_ccTV(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_TSUBTV);
+ dc->cc_op = CC_OP_TSUBTV;
+ break;
+ case 0x24: /* mulscc */
+ gen_helper_compute_psr();
+ gen_op_mulscc(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD);
+ dc->cc_op = CC_OP_ADD;
+ break;
+#ifndef TARGET_SPARC64
+ case 0x25: /* sll */
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 20, 31);
+ tcg_gen_shli_tl(cpu_dst, cpu_src1, simm & 0x1f);
+ } else { /* register */
+ tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f);
+ tcg_gen_shl_tl(cpu_dst, cpu_src1, cpu_tmp0);
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x26: /* srl */
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 20, 31);
+ tcg_gen_shri_tl(cpu_dst, cpu_src1, simm & 0x1f);
+ } else { /* register */
+ tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f);
+ tcg_gen_shr_tl(cpu_dst, cpu_src1, cpu_tmp0);
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x27: /* sra */
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 20, 31);
+ tcg_gen_sari_tl(cpu_dst, cpu_src1, simm & 0x1f);
+ } else { /* register */
+ tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f);
+ tcg_gen_sar_tl(cpu_dst, cpu_src1, cpu_tmp0);
+ }
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+#endif
+ case 0x30:
+ {
+ switch(rd) {
+ case 0: /* wry */
+ tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2);
+ tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff);
+ break;
+#ifndef TARGET_SPARC64
+ case 0x01 ... 0x0f: /* undefined in the
+ SPARCv8 manual, nop
+ on the microSPARC
+ II */
+ case 0x10 ... 0x1f: /* implementation-dependent
+ in the SPARCv8
+ manual, nop on the
+ microSPARC II */
+ break;
+#else
+ case 0x2: /* V9 wrccr */
+ tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2);
+ gen_helper_wrccr(cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS);
+ dc->cc_op = CC_OP_FLAGS;
+ break;
+ case 0x3: /* V9 wrasi */
+ tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_andi_tl(cpu_dst, cpu_dst, 0xff);
+ tcg_gen_trunc_tl_i32(cpu_asi, cpu_dst);
+ break;
+ case 0x6: /* V9 wrfprs */
+ tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_trunc_tl_i32(cpu_fprs, cpu_dst);
+ save_state(dc, cpu_cond);
+ gen_op_next_insn();
+ tcg_gen_exit_tb(0);
+ dc->is_br = 1;
+ break;
+ case 0xf: /* V9 sir, nop if user */
+#if !defined(CONFIG_USER_ONLY)
+ if (supervisor(dc)) {
+ ; // XXX
+ }
+#endif
+ break;
+ case 0x13: /* Graphics Status */
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ tcg_gen_xor_tl(cpu_gsr, cpu_src1, cpu_src2);
+ break;
+ case 0x14: /* Softint set */
+ if (!supervisor(dc))
+ goto illegal_insn;
+ tcg_gen_xor_tl(cpu_tmp64, cpu_src1, cpu_src2);
+ gen_helper_set_softint(cpu_tmp64);
+ break;
+ case 0x15: /* Softint clear */
+ if (!supervisor(dc))
+ goto illegal_insn;
+ tcg_gen_xor_tl(cpu_tmp64, cpu_src1, cpu_src2);
+ gen_helper_clear_softint(cpu_tmp64);
+ break;
+ case 0x16: /* Softint write */
+ if (!supervisor(dc))
+ goto illegal_insn;
+ tcg_gen_xor_tl(cpu_tmp64, cpu_src1, cpu_src2);
+ gen_helper_write_softint(cpu_tmp64);
+ break;
+ case 0x17: /* Tick compare */
+#if !defined(CONFIG_USER_ONLY)
+ if (!supervisor(dc))
+ goto illegal_insn;
+#endif
+ {
+ TCGv_ptr r_tickptr;
+
+ tcg_gen_xor_tl(cpu_tick_cmpr, cpu_src1,
+ cpu_src2);
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, tick));
+ gen_helper_tick_set_limit(r_tickptr,
+ cpu_tick_cmpr);
+ tcg_temp_free_ptr(r_tickptr);
+ }
+ break;
+ case 0x18: /* System tick */
+#if !defined(CONFIG_USER_ONLY)
+ if (!supervisor(dc))
+ goto illegal_insn;
+#endif
+ {
+ TCGv_ptr r_tickptr;
+
+ tcg_gen_xor_tl(cpu_dst, cpu_src1,
+ cpu_src2);
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, stick));
+ gen_helper_tick_set_count(r_tickptr,
+ cpu_dst);
+ tcg_temp_free_ptr(r_tickptr);
+ }
+ break;
+ case 0x19: /* System tick compare */
+#if !defined(CONFIG_USER_ONLY)
+ if (!supervisor(dc))
+ goto illegal_insn;
+#endif
+ {
+ TCGv_ptr r_tickptr;
+
+ tcg_gen_xor_tl(cpu_stick_cmpr, cpu_src1,
+ cpu_src2);
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, stick));
+ gen_helper_tick_set_limit(r_tickptr,
+ cpu_stick_cmpr);
+ tcg_temp_free_ptr(r_tickptr);
+ }
+ break;
+
+ case 0x10: /* Performance Control */
+ case 0x11: /* Performance Instrumentation
+ Counter */
+ case 0x12: /* Dispatch Control */
+#endif
+ default:
+ goto illegal_insn;
+ }
+ }
+ break;
+#if !defined(CONFIG_USER_ONLY)
+ case 0x31: /* wrpsr, V9 saved, restored */
+ {
+ if (!supervisor(dc))
+ goto priv_insn;
+#ifdef TARGET_SPARC64
+ switch (rd) {
+ case 0:
+ gen_helper_saved();
+ break;
+ case 1:
+ gen_helper_restored();
+ break;
+ case 2: /* UA2005 allclean */
+ case 3: /* UA2005 otherw */
+ case 4: /* UA2005 normalw */
+ case 5: /* UA2005 invalw */
+ // XXX
+ default:
+ goto illegal_insn;
+ }
+#else
+ tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2);
+ gen_helper_wrpsr(cpu_dst);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS);
+ dc->cc_op = CC_OP_FLAGS;
+ save_state(dc, cpu_cond);
+ gen_op_next_insn();
+ tcg_gen_exit_tb(0);
+ dc->is_br = 1;
+#endif
+ }
+ break;
+ case 0x32: /* wrwim, V9 wrpr */
+ {
+ if (!supervisor(dc))
+ goto priv_insn;
+ tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2);
+#ifdef TARGET_SPARC64
+ switch (rd) {
+ case 0: // tpc
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_st_tl(cpu_tmp0, r_tsptr,
+ offsetof(trap_state, tpc));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 1: // tnpc
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_st_tl(cpu_tmp0, r_tsptr,
+ offsetof(trap_state, tnpc));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 2: // tstate
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_st_tl(cpu_tmp0, r_tsptr,
+ offsetof(trap_state,
+ tstate));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 3: // tt
+ {
+ TCGv_ptr r_tsptr;
+
+ r_tsptr = tcg_temp_new_ptr();
+ gen_load_trap_state_at_tl(r_tsptr, cpu_env);
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, r_tsptr,
+ offsetof(trap_state, tt));
+ tcg_temp_free_ptr(r_tsptr);
+ }
+ break;
+ case 4: // tick
+ {
+ TCGv_ptr r_tickptr;
+
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, tick));
+ gen_helper_tick_set_count(r_tickptr,
+ cpu_tmp0);
+ tcg_temp_free_ptr(r_tickptr);
+ }
+ break;
+ case 5: // tba
+ tcg_gen_mov_tl(cpu_tbr, cpu_tmp0);
+ break;
+ case 6: // pstate
+ save_state(dc, cpu_cond);
+ gen_helper_wrpstate(cpu_tmp0);
+ dc->npc = DYNAMIC_PC;
+ break;
+ case 7: // tl
+ save_state(dc, cpu_cond);
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, tl));
+ dc->npc = DYNAMIC_PC;
+ break;
+ case 8: // pil
+ gen_helper_wrpil(cpu_tmp0);
+ break;
+ case 9: // cwp
+ gen_helper_wrcwp(cpu_tmp0);
+ break;
+ case 10: // cansave
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState,
+ cansave));
+ break;
+ case 11: // canrestore
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState,
+ canrestore));
+ break;
+ case 12: // cleanwin
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState,
+ cleanwin));
+ break;
+ case 13: // otherwin
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState,
+ otherwin));
+ break;
+ case 14: // wstate
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState,
+ wstate));
+ break;
+ case 16: // UA2005 gl
+ CHECK_IU_FEATURE(dc, GL);
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ tcg_gen_st_i32(cpu_tmp32, cpu_env,
+ offsetof(CPUSPARCState, gl));
+ break;
+ case 26: // UA2005 strand status
+ CHECK_IU_FEATURE(dc, HYPV);
+ if (!hypervisor(dc))
+ goto priv_insn;
+ tcg_gen_mov_tl(cpu_ssr, cpu_tmp0);
+ break;
+ default:
+ goto illegal_insn;
+ }
+#else
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ if (dc->def->nwindows != 32)
+ tcg_gen_andi_tl(cpu_tmp32, cpu_tmp32,
+ (1 << dc->def->nwindows) - 1);
+ tcg_gen_mov_i32(cpu_wim, cpu_tmp32);
+#endif
+ }
+ break;
+ case 0x33: /* wrtbr, UA2005 wrhpr */
+ {
+#ifndef TARGET_SPARC64
+ if (!supervisor(dc))
+ goto priv_insn;
+ tcg_gen_xor_tl(cpu_tbr, cpu_src1, cpu_src2);
+#else
+ CHECK_IU_FEATURE(dc, HYPV);
+ if (!hypervisor(dc))
+ goto priv_insn;
+ tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2);
+ switch (rd) {
+ case 0: // hpstate
+ // XXX gen_op_wrhpstate();
+ save_state(dc, cpu_cond);
+ gen_op_next_insn();
+ tcg_gen_exit_tb(0);
+ dc->is_br = 1;
+ break;
+ case 1: // htstate
+ // XXX gen_op_wrhtstate();
+ break;
+ case 3: // hintp
+ tcg_gen_mov_tl(cpu_hintp, cpu_tmp0);
+ break;
+ case 5: // htba
+ tcg_gen_mov_tl(cpu_htba, cpu_tmp0);
+ break;
+ case 31: // hstick_cmpr
+ {
+ TCGv_ptr r_tickptr;
+
+ tcg_gen_mov_tl(cpu_hstick_cmpr, cpu_tmp0);
+ r_tickptr = tcg_temp_new_ptr();
+ tcg_gen_ld_ptr(r_tickptr, cpu_env,
+ offsetof(CPUState, hstick));
+ gen_helper_tick_set_limit(r_tickptr,
+ cpu_hstick_cmpr);
+ tcg_temp_free_ptr(r_tickptr);
+ }
+ break;
+ case 6: // hver readonly
+ default:
+ goto illegal_insn;
+ }
+#endif
+ }
+ break;
+#endif
+#ifdef TARGET_SPARC64
+ case 0x2c: /* V9 movcc */
+ {
+ int cc = GET_FIELD_SP(insn, 11, 12);
+ int cond = GET_FIELD_SP(insn, 14, 17);
+ TCGv r_cond;
+ int l1;
+
+ r_cond = tcg_temp_new();
+ if (insn & (1 << 18)) {
+ if (cc == 0)
+ gen_cond(r_cond, 0, cond, dc);
+ else if (cc == 2)
+ gen_cond(r_cond, 1, cond, dc);
+ else
+ goto illegal_insn;
+ } else {
+ gen_fcond(r_cond, cc, cond);
+ }
+
+ l1 = gen_new_label();
+
+ tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1);
+ if (IS_IMM) { /* immediate */
+ TCGv r_const;
+
+ simm = GET_FIELD_SPs(insn, 0, 10);
+ r_const = tcg_const_tl(simm);
+ gen_movl_TN_reg(rd, r_const);
+ tcg_temp_free(r_const);
+ } else {
+ rs2 = GET_FIELD_SP(insn, 0, 4);
+ gen_movl_reg_TN(rs2, cpu_tmp0);
+ gen_movl_TN_reg(rd, cpu_tmp0);
+ }
+ gen_set_label(l1);
+ tcg_temp_free(r_cond);
+ break;
+ }
+ case 0x2d: /* V9 sdivx */
+ gen_op_sdivx(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x2e: /* V9 popc */
+ {
+ cpu_src2 = get_src2(insn, cpu_src2);
+ gen_helper_popc(cpu_dst, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ }
+ case 0x2f: /* V9 movr */
+ {
+ int cond = GET_FIELD_SP(insn, 10, 12);
+ int l1;
+
+ cpu_src1 = get_src1(insn, cpu_src1);
+
+ l1 = gen_new_label();
+
+ tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond],
+ cpu_src1, 0, l1);
+ if (IS_IMM) { /* immediate */
+ TCGv r_const;
+
+ simm = GET_FIELD_SPs(insn, 0, 9);
+ r_const = tcg_const_tl(simm);
+ gen_movl_TN_reg(rd, r_const);
+ tcg_temp_free(r_const);
+ } else {
+ rs2 = GET_FIELD_SP(insn, 0, 4);
+ gen_movl_reg_TN(rs2, cpu_tmp0);
+ gen_movl_TN_reg(rd, cpu_tmp0);
+ }
+ gen_set_label(l1);
+ break;
+ }
+#endif
+ default:
+ goto illegal_insn;
+ }
+ }
+ } else if (xop == 0x36) { /* UltraSparc shutdown, VIS, V8 CPop1 */
+#ifdef TARGET_SPARC64
+ int opf = GET_FIELD_SP(insn, 5, 13);
+ rs1 = GET_FIELD(insn, 13, 17);
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+
+ switch (opf) {
+ case 0x000: /* VIS I edge8cc */
+ case 0x001: /* VIS II edge8n */
+ case 0x002: /* VIS I edge8lcc */
+ case 0x003: /* VIS II edge8ln */
+ case 0x004: /* VIS I edge16cc */
+ case 0x005: /* VIS II edge16n */
+ case 0x006: /* VIS I edge16lcc */
+ case 0x007: /* VIS II edge16ln */
+ case 0x008: /* VIS I edge32cc */
+ case 0x009: /* VIS II edge32n */
+ case 0x00a: /* VIS I edge32lcc */
+ case 0x00b: /* VIS II edge32ln */
+ // XXX
+ goto illegal_insn;
+ case 0x010: /* VIS I array8 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_helper_array8(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x012: /* VIS I array16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_helper_array8(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_shli_i64(cpu_dst, cpu_dst, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x014: /* VIS I array32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_helper_array8(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_shli_i64(cpu_dst, cpu_dst, 2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x018: /* VIS I alignaddr */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_helper_alignaddr(cpu_dst, cpu_src1, cpu_src2);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x019: /* VIS II bmask */
+ case 0x01a: /* VIS I alignaddrl */
+ // XXX
+ goto illegal_insn;
+ case 0x020: /* VIS I fcmple16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmple16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x022: /* VIS I fcmpne16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmpne16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x024: /* VIS I fcmple32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmple32();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x026: /* VIS I fcmpne32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmpne32();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x028: /* VIS I fcmpgt16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmpgt16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x02a: /* VIS I fcmpeq16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmpeq16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x02c: /* VIS I fcmpgt32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmpgt32();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x02e: /* VIS I fcmpeq32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fcmpeq32();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x031: /* VIS I fmul8x16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmul8x16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x033: /* VIS I fmul8x16au */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmul8x16au();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x035: /* VIS I fmul8x16al */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmul8x16al();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x036: /* VIS I fmul8sux16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmul8sux16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x037: /* VIS I fmul8ulx16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmul8ulx16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x038: /* VIS I fmuld8sux16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmuld8sux16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x039: /* VIS I fmuld8ulx16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fmuld8ulx16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x03a: /* VIS I fpack32 */
+ case 0x03b: /* VIS I fpack16 */
+ case 0x03d: /* VIS I fpackfix */
+ case 0x03e: /* VIS I pdist */
+ // XXX
+ goto illegal_insn;
+ case 0x048: /* VIS I faligndata */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_faligndata();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x04b: /* VIS I fpmerge */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fpmerge();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x04c: /* VIS II bshuffle */
+ // XXX
+ goto illegal_insn;
+ case 0x04d: /* VIS I fexpand */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fexpand();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x050: /* VIS I fpadd16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fpadd16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x051: /* VIS I fpadd16s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_helper_fpadd16s(cpu_fpr[rd],
+ cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x052: /* VIS I fpadd32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fpadd32();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x053: /* VIS I fpadd32s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_helper_fpadd32s(cpu_fpr[rd],
+ cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x054: /* VIS I fpsub16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fpsub16();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x055: /* VIS I fpsub16s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_helper_fpsub16s(cpu_fpr[rd],
+ cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x056: /* VIS I fpsub32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs1));
+ gen_op_load_fpr_DT1(DFPREG(rs2));
+ gen_helper_fpsub32();
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x057: /* VIS I fpsub32s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_helper_fpsub32s(cpu_fpr[rd],
+ cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x060: /* VIS I fzero */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], 0);
+ tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], 0);
+ break;
+ case 0x061: /* VIS I fzeros */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_movi_i32(cpu_fpr[rd], 0);
+ break;
+ case 0x062: /* VIS I fnor */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x063: /* VIS I fnors */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_nor_i32(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x064: /* VIS I fandnot2 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_andc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x065: /* VIS I fandnot2s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x066: /* VIS I fnot2 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x067: /* VIS I fnot2s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ break;
+ case 0x068: /* VIS I fandnot1 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_andc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)],
+ cpu_fpr[DFPREG(rs1)]);
+ tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs2) + 1],
+ cpu_fpr[DFPREG(rs1) + 1]);
+ break;
+ case 0x069: /* VIS I fandnot1s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]);
+ break;
+ case 0x06a: /* VIS I fnot1 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]);
+ tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1]);
+ break;
+ case 0x06b: /* VIS I fnot1s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs1]);
+ break;
+ case 0x06c: /* VIS I fxor */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_xor_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x06d: /* VIS I fxors */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_xor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x06e: /* VIS I fnand */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x06f: /* VIS I fnands */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_nand_i32(cpu_tmp32, cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x070: /* VIS I fand */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_and_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_and_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x071: /* VIS I fands */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_and_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x072: /* VIS I fxnor */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[DFPREG(rs2)], -1);
+ tcg_gen_xor_i32(cpu_fpr[DFPREG(rd)], cpu_tmp32,
+ cpu_fpr[DFPREG(rs1)]);
+ tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[DFPREG(rs2) + 1], -1);
+ tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1], cpu_tmp32,
+ cpu_fpr[DFPREG(rs1) + 1]);
+ break;
+ case 0x073: /* VIS I fxnors */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[rs2], -1);
+ tcg_gen_xor_i32(cpu_fpr[rd], cpu_tmp32, cpu_fpr[rs1]);
+ break;
+ case 0x074: /* VIS I fsrc1 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]);
+ tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1]);
+ break;
+ case 0x075: /* VIS I fsrc1s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs1]);
+ break;
+ case 0x076: /* VIS I fornot2 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_orc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x077: /* VIS I fornot2s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x078: /* VIS I fsrc2 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_op_load_fpr_DT0(DFPREG(rs2));
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ break;
+ case 0x079: /* VIS I fsrc2s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
+ break;
+ case 0x07a: /* VIS I fornot1 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_orc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)],
+ cpu_fpr[DFPREG(rs1)]);
+ tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs2) + 1],
+ cpu_fpr[DFPREG(rs1) + 1]);
+ break;
+ case 0x07b: /* VIS I fornot1s */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]);
+ break;
+ case 0x07c: /* VIS I for */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_or_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
+ cpu_fpr[DFPREG(rs2)]);
+ tcg_gen_or_i32(cpu_fpr[DFPREG(rd) + 1],
+ cpu_fpr[DFPREG(rs1) + 1],
+ cpu_fpr[DFPREG(rs2) + 1]);
+ break;
+ case 0x07d: /* VIS I fors */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_or_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
+ break;
+ case 0x07e: /* VIS I fone */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], -1);
+ tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], -1);
+ break;
+ case 0x07f: /* VIS I fones */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ tcg_gen_movi_i32(cpu_fpr[rd], -1);
+ break;
+ case 0x080: /* VIS I shutdown */
+ case 0x081: /* VIS II siam */
+ // XXX
+ goto illegal_insn;
+ default:
+ goto illegal_insn;
+ }
+#else
+ goto ncp_insn;
+#endif
+ } else if (xop == 0x37) { /* V8 CPop2, V9 impdep2 */
+#ifdef TARGET_SPARC64
+ goto illegal_insn;
+#else
+ goto ncp_insn;
+#endif
+#ifdef TARGET_SPARC64
+ } else if (xop == 0x39) { /* V9 return */
+ TCGv_i32 r_const;
+
+ save_state(dc, cpu_cond);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_addi_tl(cpu_dst, cpu_src1, simm);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2) {
+ gen_movl_reg_TN(rs2, cpu_src2);
+ tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2);
+ } else
+ tcg_gen_mov_tl(cpu_dst, cpu_src1);
+ }
+ gen_helper_restore();
+ gen_mov_pc_npc(dc, cpu_cond);
+ r_const = tcg_const_i32(3);
+ gen_helper_check_align(cpu_dst, r_const);
+ tcg_temp_free_i32(r_const);
+ tcg_gen_mov_tl(cpu_npc, cpu_dst);
+ dc->npc = DYNAMIC_PC;
+ goto jmp_insn;
+#endif
+ } else {
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_addi_tl(cpu_dst, cpu_src1, simm);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2) {
+ gen_movl_reg_TN(rs2, cpu_src2);
+ tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2);
+ } else
+ tcg_gen_mov_tl(cpu_dst, cpu_src1);
+ }
+ switch (xop) {
+ case 0x38: /* jmpl */
+ {
+ TCGv r_pc;
+ TCGv_i32 r_const;
+
+ r_pc = tcg_const_tl(dc->pc);
+ gen_movl_TN_reg(rd, r_pc);
+ tcg_temp_free(r_pc);
+ gen_mov_pc_npc(dc, cpu_cond);
+ r_const = tcg_const_i32(3);
+ gen_helper_check_align(cpu_dst, r_const);
+ tcg_temp_free_i32(r_const);
+ tcg_gen_mov_tl(cpu_npc, cpu_dst);
+ dc->npc = DYNAMIC_PC;
+ }
+ goto jmp_insn;
+#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64)
+ case 0x39: /* rett, V9 return */
+ {
+ TCGv_i32 r_const;
+
+ if (!supervisor(dc))
+ goto priv_insn;
+ gen_mov_pc_npc(dc, cpu_cond);
+ r_const = tcg_const_i32(3);
+ gen_helper_check_align(cpu_dst, r_const);
+ tcg_temp_free_i32(r_const);
+ tcg_gen_mov_tl(cpu_npc, cpu_dst);
+ dc->npc = DYNAMIC_PC;
+ gen_helper_rett();
+ }
+ goto jmp_insn;
+#endif
+ case 0x3b: /* flush */
+ if (!((dc)->def->features & CPU_FEATURE_FLUSH))
+ goto unimp_flush;
+ gen_helper_flush(cpu_dst);
+ break;
+ case 0x3c: /* save */
+ save_state(dc, cpu_cond);
+ gen_helper_save();
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x3d: /* restore */
+ save_state(dc, cpu_cond);
+ gen_helper_restore();
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+#if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64)
+ case 0x3e: /* V9 done/retry */
+ {
+ switch (rd) {
+ case 0:
+ if (!supervisor(dc))
+ goto priv_insn;
+ dc->npc = DYNAMIC_PC;
+ dc->pc = DYNAMIC_PC;
+ gen_helper_done();
+ goto jmp_insn;
+ case 1:
+ if (!supervisor(dc))
+ goto priv_insn;
+ dc->npc = DYNAMIC_PC;
+ dc->pc = DYNAMIC_PC;
+ gen_helper_retry();
+ goto jmp_insn;
+ default:
+ goto illegal_insn;
+ }
+ }
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ }
+ break;
+ }
+ break;
+ case 3: /* load/store instructions */
+ {
+ unsigned int xop = GET_FIELD(insn, 7, 12);
+
+ /* flush pending conditional evaluations before exposing
+ cpu state */
+ if (dc->cc_op != CC_OP_FLAGS) {
+ dc->cc_op = CC_OP_FLAGS;
+ gen_helper_compute_psr();
+ }
+ cpu_src1 = get_src1(insn, cpu_src1);
+ if (xop == 0x3c || xop == 0x3e) { // V9 casa/casxa
+ rs2 = GET_FIELD(insn, 27, 31);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ tcg_gen_mov_tl(cpu_addr, cpu_src1);
+ } else if (IS_IMM) { /* immediate */
+ simm = GET_FIELDs(insn, 19, 31);
+ tcg_gen_addi_tl(cpu_addr, cpu_src1, simm);
+ } else { /* register */
+ rs2 = GET_FIELD(insn, 27, 31);
+ if (rs2 != 0) {
+ gen_movl_reg_TN(rs2, cpu_src2);
+ tcg_gen_add_tl(cpu_addr, cpu_src1, cpu_src2);
+ } else
+ tcg_gen_mov_tl(cpu_addr, cpu_src1);
+ }
+ if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) ||
+ (xop > 0x17 && xop <= 0x1d ) ||
+ (xop > 0x2c && xop <= 0x33) || xop == 0x1f || xop == 0x3d) {
+ switch (xop) {
+ case 0x0: /* ld, V9 lduw, load unsigned word */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld32u(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x1: /* ldub, load unsigned byte */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld8u(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x2: /* lduh, load unsigned halfword */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld16u(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x3: /* ldd, load double word */
+ if (rd & 1)
+ goto illegal_insn;
+ else {
+ TCGv_i32 r_const;
+
+ save_state(dc, cpu_cond);
+ r_const = tcg_const_i32(7);
+ gen_helper_check_align(cpu_addr, r_const); // XXX remove
+ tcg_temp_free_i32(r_const);
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld64(cpu_tmp64, cpu_addr, dc->mem_idx);
+ tcg_gen_trunc_i64_tl(cpu_tmp0, cpu_tmp64);
+ tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, 0xffffffffULL);
+ gen_movl_TN_reg(rd + 1, cpu_tmp0);
+ tcg_gen_shri_i64(cpu_tmp64, cpu_tmp64, 32);
+ tcg_gen_trunc_i64_tl(cpu_val, cpu_tmp64);
+ tcg_gen_andi_tl(cpu_val, cpu_val, 0xffffffffULL);
+ }
+ break;
+ case 0x9: /* ldsb, load signed byte */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld8s(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0xa: /* ldsh, load signed halfword */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld16s(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0xd: /* ldstub -- XXX: should be atomically */
+ {
+ TCGv r_const;
+
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld8s(cpu_val, cpu_addr, dc->mem_idx);
+ r_const = tcg_const_tl(0xff);
+ tcg_gen_qemu_st8(r_const, cpu_addr, dc->mem_idx);
+ tcg_temp_free(r_const);
+ }
+ break;
+ case 0x0f: /* swap, swap register with memory. Also
+ atomically */
+ CHECK_IU_FEATURE(dc, SWAP);
+ gen_movl_reg_TN(rd, cpu_val);
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx);
+ tcg_gen_qemu_st32(cpu_val, cpu_addr, dc->mem_idx);
+ tcg_gen_mov_tl(cpu_val, cpu_tmp0);
+ break;
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+ case 0x10: /* lda, V9 lduwa, load word alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 4, 0);
+ break;
+ case 0x11: /* lduba, load unsigned byte alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 1, 0);
+ break;
+ case 0x12: /* lduha, load unsigned halfword alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 2, 0);
+ break;
+ case 0x13: /* ldda, load double word alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ if (rd & 1)
+ goto illegal_insn;
+ save_state(dc, cpu_cond);
+ gen_ldda_asi(cpu_val, cpu_addr, insn, rd);
+ goto skip_move;
+ case 0x19: /* ldsba, load signed byte alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 1, 1);
+ break;
+ case 0x1a: /* ldsha, load signed halfword alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 2, 1);
+ break;
+ case 0x1d: /* ldstuba -- XXX: should be atomically */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_ldstub_asi(cpu_val, cpu_addr, insn);
+ break;
+ case 0x1f: /* swapa, swap reg with alt. memory. Also
+ atomically */
+ CHECK_IU_FEATURE(dc, SWAP);
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_movl_reg_TN(rd, cpu_val);
+ gen_swap_asi(cpu_val, cpu_addr, insn);
+ break;
+
+#ifndef TARGET_SPARC64
+ case 0x30: /* ldc */
+ case 0x31: /* ldcsr */
+ case 0x33: /* lddc */
+ goto ncp_insn;
+#endif
+#endif
+#ifdef TARGET_SPARC64
+ case 0x08: /* V9 ldsw */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld32s(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x0b: /* V9 ldx */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld64(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x18: /* V9 ldswa */
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 4, 1);
+ break;
+ case 0x1b: /* V9 ldxa */
+ save_state(dc, cpu_cond);
+ gen_ld_asi(cpu_val, cpu_addr, insn, 8, 0);
+ break;
+ case 0x2d: /* V9 prefetch, no effect */
+ goto skip_move;
+ case 0x30: /* V9 ldfa */
+ save_state(dc, cpu_cond);
+ gen_ldf_asi(cpu_addr, insn, 4, rd);
+ goto skip_move;
+ case 0x33: /* V9 lddfa */
+ save_state(dc, cpu_cond);
+ gen_ldf_asi(cpu_addr, insn, 8, DFPREG(rd));
+ goto skip_move;
+ case 0x3d: /* V9 prefetcha, no effect */
+ goto skip_move;
+ case 0x32: /* V9 ldqfa */
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ save_state(dc, cpu_cond);
+ gen_ldf_asi(cpu_addr, insn, 16, QFPREG(rd));
+ goto skip_move;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ gen_movl_TN_reg(rd, cpu_val);
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+ skip_move: ;
+#endif
+ } else if (xop >= 0x20 && xop < 0x24) {
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ save_state(dc, cpu_cond);
+ switch (xop) {
+ case 0x20: /* ldf, load fpreg */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx);
+ tcg_gen_trunc_tl_i32(cpu_fpr[rd], cpu_tmp0);
+ break;
+ case 0x21: /* ldfsr, V9 ldxfsr */
+#ifdef TARGET_SPARC64
+ gen_address_mask(dc, cpu_addr);
+ if (rd == 1) {
+ tcg_gen_qemu_ld64(cpu_tmp64, cpu_addr, dc->mem_idx);
+ gen_helper_ldxfsr(cpu_tmp64);
+ } else {
+ tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx);
+ tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0);
+ gen_helper_ldfsr(cpu_tmp32);
+ }
+#else
+ {
+ tcg_gen_qemu_ld32u(cpu_tmp32, cpu_addr, dc->mem_idx);
+ gen_helper_ldfsr(cpu_tmp32);
+ }
+#endif
+ break;
+ case 0x22: /* ldqf, load quad fpreg */
+ {
+ TCGv_i32 r_const;
+
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ r_const = tcg_const_i32(dc->mem_idx);
+ gen_address_mask(dc, cpu_addr);
+ gen_helper_ldqf(cpu_addr, r_const);
+ tcg_temp_free_i32(r_const);
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ }
+ break;
+ case 0x23: /* lddf, load double fpreg */
+ {
+ TCGv_i32 r_const;
+
+ r_const = tcg_const_i32(dc->mem_idx);
+ gen_address_mask(dc, cpu_addr);
+ gen_helper_lddf(cpu_addr, r_const);
+ tcg_temp_free_i32(r_const);
+ gen_op_store_DT0_fpr(DFPREG(rd));
+ }
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) ||
+ xop == 0xe || xop == 0x1e) {
+ gen_movl_reg_TN(rd, cpu_val);
+ switch (xop) {
+ case 0x4: /* st, store word */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_st32(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x5: /* stb, store byte */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_st8(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x6: /* sth, store halfword */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_st16(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x7: /* std, store double word */
+ if (rd & 1)
+ goto illegal_insn;
+ else {
+ TCGv_i32 r_const;
+
+ save_state(dc, cpu_cond);
+ gen_address_mask(dc, cpu_addr);
+ r_const = tcg_const_i32(7);
+ gen_helper_check_align(cpu_addr, r_const); // XXX remove
+ tcg_temp_free_i32(r_const);
+ gen_movl_reg_TN(rd + 1, cpu_tmp0);
+ tcg_gen_concat_tl_i64(cpu_tmp64, cpu_tmp0, cpu_val);
+ tcg_gen_qemu_st64(cpu_tmp64, cpu_addr, dc->mem_idx);
+ }
+ break;
+#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
+ case 0x14: /* sta, V9 stwa, store word alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_st_asi(cpu_val, cpu_addr, insn, 4);
+ dc->npc = DYNAMIC_PC;
+ break;
+ case 0x15: /* stba, store byte alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_st_asi(cpu_val, cpu_addr, insn, 1);
+ dc->npc = DYNAMIC_PC;
+ break;
+ case 0x16: /* stha, store halfword alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ save_state(dc, cpu_cond);
+ gen_st_asi(cpu_val, cpu_addr, insn, 2);
+ dc->npc = DYNAMIC_PC;
+ break;
+ case 0x17: /* stda, store double word alternate */
+#ifndef TARGET_SPARC64
+ if (IS_IMM)
+ goto illegal_insn;
+ if (!supervisor(dc))
+ goto priv_insn;
+#endif
+ if (rd & 1)
+ goto illegal_insn;
+ else {
+ save_state(dc, cpu_cond);
+ gen_stda_asi(cpu_val, cpu_addr, insn, rd);
+ }
+ break;
+#endif
+#ifdef TARGET_SPARC64
+ case 0x0e: /* V9 stx */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_qemu_st64(cpu_val, cpu_addr, dc->mem_idx);
+ break;
+ case 0x1e: /* V9 stxa */
+ save_state(dc, cpu_cond);
+ gen_st_asi(cpu_val, cpu_addr, insn, 8);
+ dc->npc = DYNAMIC_PC;
+ break;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop > 0x23 && xop < 0x28) {
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ save_state(dc, cpu_cond);
+ switch (xop) {
+ case 0x24: /* stf, store fpreg */
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_fpr[rd]);
+ tcg_gen_qemu_st32(cpu_tmp0, cpu_addr, dc->mem_idx);
+ break;
+ case 0x25: /* stfsr, V9 stxfsr */
+#ifdef TARGET_SPARC64
+ gen_address_mask(dc, cpu_addr);
+ tcg_gen_ld_i64(cpu_tmp64, cpu_env, offsetof(CPUState, fsr));
+ if (rd == 1)
+ tcg_gen_qemu_st64(cpu_tmp64, cpu_addr, dc->mem_idx);
+ else
+ tcg_gen_qemu_st32(cpu_tmp64, cpu_addr, dc->mem_idx);
+#else
+ tcg_gen_ld_i32(cpu_tmp32, cpu_env, offsetof(CPUState, fsr));
+ tcg_gen_qemu_st32(cpu_tmp32, cpu_addr, dc->mem_idx);
+#endif
+ break;
+ case 0x26:
+#ifdef TARGET_SPARC64
+ /* V9 stqf, store quad fpreg */
+ {
+ TCGv_i32 r_const;
+
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ gen_op_load_fpr_QT0(QFPREG(rd));
+ r_const = tcg_const_i32(dc->mem_idx);
+ gen_address_mask(dc, cpu_addr);
+ gen_helper_stqf(cpu_addr, r_const);
+ tcg_temp_free_i32(r_const);
+ }
+ break;
+#else /* !TARGET_SPARC64 */
+ /* stdfq, store floating point queue */
+#if defined(CONFIG_USER_ONLY)
+ goto illegal_insn;
+#else
+ if (!supervisor(dc))
+ goto priv_insn;
+ if (gen_trap_ifnofpu(dc, cpu_cond))
+ goto jmp_insn;
+ goto nfq_insn;
+#endif
+#endif
+ case 0x27: /* stdf, store double fpreg */
+ {
+ TCGv_i32 r_const;
+
+ gen_op_load_fpr_DT0(DFPREG(rd));
+ r_const = tcg_const_i32(dc->mem_idx);
+ gen_address_mask(dc, cpu_addr);
+ gen_helper_stdf(cpu_addr, r_const);
+ tcg_temp_free_i32(r_const);
+ }
+ break;
+ default:
+ goto illegal_insn;
+ }
+ } else if (xop > 0x33 && xop < 0x3f) {
+ save_state(dc, cpu_cond);
+ switch (xop) {
+#ifdef TARGET_SPARC64
+ case 0x34: /* V9 stfa */
+ gen_stf_asi(cpu_addr, insn, 4, rd);
+ break;
+ case 0x36: /* V9 stqfa */
+ {
+ TCGv_i32 r_const;
+
+ CHECK_FPU_FEATURE(dc, FLOAT128);
+ r_const = tcg_const_i32(7);
+ gen_helper_check_align(cpu_addr, r_const);
+ tcg_temp_free_i32(r_const);
+ gen_op_load_fpr_QT0(QFPREG(rd));
+ gen_stf_asi(cpu_addr, insn, 16, QFPREG(rd));
+ }
+ break;
+ case 0x37: /* V9 stdfa */
+ gen_op_load_fpr_DT0(DFPREG(rd));
+ gen_stf_asi(cpu_addr, insn, 8, DFPREG(rd));
+ break;
+ case 0x3c: /* V9 casa */
+ gen_cas_asi(cpu_val, cpu_addr, cpu_src2, insn, rd);
+ gen_movl_TN_reg(rd, cpu_val);
+ break;
+ case 0x3e: /* V9 casxa */
+ gen_casx_asi(cpu_val, cpu_addr, cpu_src2, insn, rd);
+ gen_movl_TN_reg(rd, cpu_val);
+ break;
+#else
+ case 0x34: /* stc */
+ case 0x35: /* stcsr */
+ case 0x36: /* stdcq */
+ case 0x37: /* stdc */
+ goto ncp_insn;
+#endif
+ default:
+ goto illegal_insn;
+ }
+ } else
+ goto illegal_insn;
+ }
+ break;
+ }
+ /* default case for non jump instructions */
+ if (dc->npc == DYNAMIC_PC) {
+ dc->pc = DYNAMIC_PC;
+ gen_op_next_insn();
+ } else if (dc->npc == JUMP_PC) {
+ /* we can do a static jump */
+ gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond);
+ dc->is_br = 1;
+ } else {
+ dc->pc = dc->npc;
+ dc->npc = dc->npc + 4;
+ }
+ jmp_insn:
+ goto egress;
+ illegal_insn:
+ {
+ TCGv_i32 r_const;
+
+ save_state(dc, cpu_cond);
+ r_const = tcg_const_i32(TT_ILL_INSN);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ dc->is_br = 1;
+ }
+ goto egress;
+ unimp_flush:
+ {
+ TCGv_i32 r_const;
+
+ save_state(dc, cpu_cond);
+ r_const = tcg_const_i32(TT_UNIMP_FLUSH);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ dc->is_br = 1;
+ }
+ goto egress;
+#if !defined(CONFIG_USER_ONLY)
+ priv_insn:
+ {
+ TCGv_i32 r_const;
+
+ save_state(dc, cpu_cond);
+ r_const = tcg_const_i32(TT_PRIV_INSN);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free_i32(r_const);
+ dc->is_br = 1;
+ }
+ goto egress;
+#endif
+ nfpu_insn:
+ save_state(dc, cpu_cond);
+ gen_op_fpexception_im(FSR_FTT_UNIMPFPOP);
+ dc->is_br = 1;
+ goto egress;
+#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64)
+ nfq_insn:
+ save_state(dc, cpu_cond);
+ gen_op_fpexception_im(FSR_FTT_SEQ_ERROR);
+ dc->is_br = 1;
+ goto egress;
+#endif
+#ifndef TARGET_SPARC64
+ ncp_insn:
+ {
+ TCGv r_const;
+
+ save_state(dc, cpu_cond);
+ r_const = tcg_const_i32(TT_NCP_INSN);
+ gen_helper_raise_exception(r_const);
+ tcg_temp_free(r_const);
+ dc->is_br = 1;
+ }
+ goto egress;
+#endif
+ egress:
+ tcg_temp_free(cpu_tmp1);
+ tcg_temp_free(cpu_tmp2);
+}
+
+static inline void gen_intermediate_code_internal(TranslationBlock * tb,
+ int spc, CPUSPARCState *env)
+{
+ target_ulong pc_start, last_pc;
+ uint16_t *gen_opc_end;
+ DisasContext dc1, *dc = &dc1;
+ CPUBreakpoint *bp;
+ int j, lj = -1;
+ int num_insns;
+ int max_insns;
+
+ memset(dc, 0, sizeof(DisasContext));
+ dc->tb = tb;
+ pc_start = tb->pc;
+ dc->pc = pc_start;
+ last_pc = dc->pc;
+ dc->npc = (target_ulong) tb->cs_base;
+ dc->cc_op = CC_OP_DYNAMIC;
+ dc->mem_idx = cpu_mmu_index(env);
+ dc->def = env->def;
+ if ((dc->def->features & CPU_FEATURE_FLOAT))
+ dc->fpu_enabled = cpu_fpu_enabled(env);
+ else
+ dc->fpu_enabled = 0;
+#ifdef TARGET_SPARC64
+ dc->address_mask_32bit = env->pstate & PS_AM;
+#endif
+ dc->singlestep = (env->singlestep_enabled || singlestep);
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ cpu_tmp0 = tcg_temp_new();
+ cpu_tmp32 = tcg_temp_new_i32();
+ cpu_tmp64 = tcg_temp_new_i64();
+
+ cpu_dst = tcg_temp_local_new();
+
+ // loads and stores
+ cpu_val = tcg_temp_local_new();
+ cpu_addr = tcg_temp_local_new();
+
+ 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) {
+ if (dc->pc != pc_start)
+ save_state(dc, cpu_cond);
+ gen_helper_debug();
+ tcg_gen_exit_tb(0);
+ dc->is_br = 1;
+ goto exit_gen_loop;
+ }
+ }
+ }
+ if (spc) {
+ qemu_log("Search PC...\n");
+ 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_npc[lj] = dc->npc;
+ 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();
+ last_pc = dc->pc;
+ disas_sparc_insn(dc);
+ num_insns++;
+
+ if (dc->is_br)
+ break;
+ /* if the next PC is different, we abort now */
+ if (dc->pc != (last_pc + 4))
+ break;
+ /* if we reach a page boundary, we stop generation so that the
+ PC of a TT_TFAULT exception is always in the right page */
+ if ((dc->pc & (TARGET_PAGE_SIZE - 1)) == 0)
+ break;
+ /* if single step mode, we generate only one instruction and
+ generate an exception */
+ if (dc->singlestep) {
+ break;
+ }
+ } while ((gen_opc_ptr < gen_opc_end) &&
+ (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32) &&
+ num_insns < max_insns);
+
+ exit_gen_loop:
+ tcg_temp_free(cpu_addr);
+ tcg_temp_free(cpu_val);
+ tcg_temp_free(cpu_dst);
+ tcg_temp_free_i64(cpu_tmp64);
+ tcg_temp_free_i32(cpu_tmp32);
+ tcg_temp_free(cpu_tmp0);
+ if (tb->cflags & CF_LAST_IO)
+ gen_io_end();
+ if (!dc->is_br) {
+ if (dc->pc != DYNAMIC_PC &&
+ (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) {
+ /* static PC and NPC: we can use direct chaining */
+ gen_goto_tb(dc, 0, dc->pc, dc->npc);
+ } else {
+ if (dc->pc != DYNAMIC_PC)
+ tcg_gen_movi_tl(cpu_pc, dc->pc);
+ save_npc(dc, cpu_cond);
+ tcg_gen_exit_tb(0);
+ }
+ }
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+ if (spc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j)
+ gen_opc_instr_start[lj++] = 0;
+#if 0
+ log_page_dump();
+#endif
+ gen_opc_jump_pc[0] = dc->jump_pc[0];
+ gen_opc_jump_pc[1] = dc->jump_pc[1];
+ } else {
+ tb->size = last_pc + 4 - pc_start;
+ tb->icount = num_insns;
+ }
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("--------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
+ log_target_disas(pc_start, last_pc + 4 - pc_start, 0);
+ qemu_log("\n");
+ }
+#endif
+}
+
+void gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb)
+{
+ gen_intermediate_code_internal(tb, 0, env);
+}
+
+void gen_intermediate_code_pc(CPUSPARCState * env, TranslationBlock * tb)
+{
+ gen_intermediate_code_internal(tb, 1, env);
+}
+
+void gen_intermediate_code_init(CPUSPARCState *env)
+{
+ unsigned int i;
+ static int inited;
+ static const char * const gregnames[8] = {
+ NULL, // g0 not used
+ "g1",
+ "g2",
+ "g3",
+ "g4",
+ "g5",
+ "g6",
+ "g7",
+ };
+ static const char * const fregnames[64] = {
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+ "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+ "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+ "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39",
+ "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47",
+ "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55",
+ "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63",
+ };
+
+ /* init various static tables */
+ if (!inited) {
+ inited = 1;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+ cpu_regwptr = tcg_global_mem_new_ptr(TCG_AREG0,
+ offsetof(CPUState, regwptr),
+ "regwptr");
+#ifdef TARGET_SPARC64
+ cpu_xcc = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, xcc),
+ "xcc");
+ cpu_asi = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, asi),
+ "asi");
+ cpu_fprs = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, fprs),
+ "fprs");
+ cpu_gsr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, gsr),
+ "gsr");
+ cpu_tick_cmpr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, tick_cmpr),
+ "tick_cmpr");
+ cpu_stick_cmpr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, stick_cmpr),
+ "stick_cmpr");
+ cpu_hstick_cmpr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, hstick_cmpr),
+ "hstick_cmpr");
+ cpu_hintp = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, hintp),
+ "hintp");
+ cpu_htba = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, htba),
+ "htba");
+ cpu_hver = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, hver),
+ "hver");
+ cpu_ssr = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, ssr), "ssr");
+ cpu_ver = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, version), "ver");
+ cpu_softint = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, softint),
+ "softint");
+#else
+ cpu_wim = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, wim),
+ "wim");
+#endif
+ cpu_cond = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cond),
+ "cond");
+ cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cc_src),
+ "cc_src");
+ cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, cc_src2),
+ "cc_src2");
+ cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cc_dst),
+ "cc_dst");
+ cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, cc_op),
+ "cc_op");
+ cpu_psr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, psr),
+ "psr");
+ cpu_fsr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, fsr),
+ "fsr");
+ cpu_pc = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, pc),
+ "pc");
+ cpu_npc = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, npc),
+ "npc");
+ cpu_y = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, y), "y");
+#ifndef CONFIG_USER_ONLY
+ cpu_tbr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, tbr),
+ "tbr");
+#endif
+ for (i = 1; i < 8; i++)
+ cpu_gregs[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUState, gregs[i]),
+ gregnames[i]);
+ for (i = 0; i < TARGET_FPREGS; i++)
+ cpu_fpr[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ offsetof(CPUState, fpr[i]),
+ fregnames[i]);
+
+ /* register helpers */
+
+#define GEN_HELPER 2
+#include "helper.h"
+ }
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+ unsigned long searched_pc, int pc_pos, void *puc)
+{
+ target_ulong npc;
+ env->pc = gen_opc_pc[pc_pos];
+ npc = gen_opc_npc[pc_pos];
+ if (npc == 1) {
+ /* dynamic NPC: already stored */
+ } else if (npc == 2) {
+ /* jump PC: use 'cond' and the jump targets of the translation */
+ if (env->cond) {
+ env->npc = gen_opc_jump_pc[0];
+ } else {
+ env->npc = gen_opc_jump_pc[1];
+ }
+ } else {
+ env->npc = npc;
+ }
+
+ /* flush pending conditional evaluations before exposing cpu state */
+ if (CC_OP != CC_OP_FLAGS) {
+ helper_compute_psr();
+ }
+}
diff --git a/targphys.h b/targphys.h
new file mode 100644
index 0000000..95648d6
--- /dev/null
+++ b/targphys.h
@@ -0,0 +1,21 @@
+/* Define target_phys_addr_t if it exists. */
+
+#ifndef TARGPHYS_H
+#define TARGPHYS_H
+
+#ifdef TARGET_PHYS_ADDR_BITS
+/* target_phys_addr_t is the type of a physical address (its size can
+ be different from 'target_ulong'). */
+
+#if TARGET_PHYS_ADDR_BITS == 32
+typedef uint32_t target_phys_addr_t;
+#define TARGET_PHYS_ADDR_MAX UINT32_MAX
+#define TARGET_FMT_plx "%08x"
+#elif TARGET_PHYS_ADDR_BITS == 64
+typedef uint64_t target_phys_addr_t;
+#define TARGET_PHYS_ADDR_MAX UINT64_MAX
+#define TARGET_FMT_plx "%016" PRIx64
+#endif
+#endif
+
+#endif
diff --git a/tcg-runtime.c b/tcg-runtime.c
new file mode 100644
index 0000000..abfc364
--- /dev/null
+++ b/tcg-runtime.c
@@ -0,0 +1,85 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdint.h>
+
+#include "tcg/tcg-runtime.h"
+
+/* 32-bit helpers */
+
+int32_t tcg_helper_div_i32(int32_t arg1, int32_t arg2)
+{
+ return arg1 / arg2;
+}
+
+int32_t tcg_helper_rem_i32(int32_t arg1, int32_t arg2)
+{
+ return arg1 % arg2;
+}
+
+uint32_t tcg_helper_divu_i32(uint32_t arg1, uint32_t arg2)
+{
+ return arg1 / arg2;
+}
+
+uint32_t tcg_helper_remu_i32(uint32_t arg1, uint32_t arg2)
+{
+ return arg1 % arg2;
+}
+
+/* 64-bit helpers */
+
+int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2)
+{
+ return arg1 << arg2;
+}
+
+int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2)
+{
+ return (uint64_t)arg1 >> arg2;
+}
+
+int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2)
+{
+ return arg1 >> arg2;
+}
+
+int64_t tcg_helper_div_i64(int64_t arg1, int64_t arg2)
+{
+ return arg1 / arg2;
+}
+
+int64_t tcg_helper_rem_i64(int64_t arg1, int64_t arg2)
+{
+ return arg1 % arg2;
+}
+
+uint64_t tcg_helper_divu_i64(uint64_t arg1, uint64_t arg2)
+{
+ return arg1 / arg2;
+}
+
+uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2)
+{
+ return arg1 % arg2;
+}
diff --git a/tcg/LICENSE b/tcg/LICENSE
new file mode 100644
index 0000000..be817fa
--- /dev/null
+++ b/tcg/LICENSE
@@ -0,0 +1,3 @@
+All the files in this directory and subdirectories are released under
+a BSD like license (see header in each file). No other license is
+accepted.
diff --git a/tcg/README b/tcg/README
new file mode 100644
index 0000000..6600122
--- /dev/null
+++ b/tcg/README
@@ -0,0 +1,511 @@
+Tiny Code Generator - Fabrice Bellard.
+
+1) Introduction
+
+TCG (Tiny Code Generator) began as a generic backend for a C
+compiler. It was simplified to be used in QEMU. It also has its roots
+in the QOP code generator written by Paul Brook.
+
+2) Definitions
+
+The TCG "target" is the architecture for which we generate the
+code. It is of course not the same as the "target" of QEMU which is
+the emulated architecture. As TCG started as a generic C backend used
+for cross compiling, it is assumed that the TCG target is different
+from the host, although it is never the case for QEMU.
+
+A TCG "function" corresponds to a QEMU Translated Block (TB).
+
+A TCG "temporary" is a variable only live in a basic
+block. Temporaries are allocated explicitly in each function.
+
+A TCG "local temporary" is a variable only live in a function. Local
+temporaries are allocated explicitly in each function.
+
+A TCG "global" is a variable which is live in all the functions
+(equivalent of a C global variable). They are defined before the
+functions defined. A TCG global can be a memory location (e.g. a QEMU
+CPU register), a fixed host register (e.g. the QEMU CPU state pointer)
+or a memory location which is stored in a register outside QEMU TBs
+(not implemented yet).
+
+A TCG "basic block" corresponds to a list of instructions terminated
+by a branch instruction.
+
+3) Intermediate representation
+
+3.1) Introduction
+
+TCG instructions operate on variables which are temporaries, local
+temporaries or globals. TCG instructions and variables are strongly
+typed. Two types are supported: 32 bit integers and 64 bit
+integers. Pointers are defined as an alias to 32 bit or 64 bit
+integers depending on the TCG target word size.
+
+Each instruction has a fixed number of output variable operands, input
+variable operands and always constant operands.
+
+The notable exception is the call instruction which has a variable
+number of outputs and inputs.
+
+In the textual form, output operands usually come first, followed by
+input operands, followed by constant operands. The output type is
+included in the instruction name. Constants are prefixed with a '$'.
+
+add_i32 t0, t1, t2 (t0 <- t1 + t2)
+
+3.2) Assumptions
+
+* Basic blocks
+
+- Basic blocks end after branches (e.g. brcond_i32 instruction),
+ goto_tb and exit_tb instructions.
+- Basic blocks start after the end of a previous basic block, or at a
+ set_label instruction.
+
+After the end of a basic block, the content of temporaries is
+destroyed, but local temporaries and globals are preserved.
+
+* Floating point types are not supported yet
+
+* Pointers: depending on the TCG target, pointer size is 32 bit or 64
+ bit. The type TCG_TYPE_PTR is an alias to TCG_TYPE_I32 or
+ TCG_TYPE_I64.
+
+* Helpers:
+
+Using the tcg_gen_helper_x_y it is possible to call any function
+taking i32, i64 or pointer types. By default, before calling a helper,
+all globals are stored at their canonical location and it is assumed
+that the function can modify them. This can be overridden by the
+TCG_CALL_CONST function modifier. By default, the helper is allowed to
+modify the CPU state or raise an exception. This can be overridden by
+the TCG_CALL_PURE function modifier, in which case the call to the
+function is removed if the return value is not used.
+
+On some TCG targets (e.g. x86), several calling conventions are
+supported.
+
+* Branches:
+
+Use the instruction 'br' to jump to a label. Use 'jmp' to jump to an
+explicit address. Conditional branches can only jump to labels.
+
+3.3) Code Optimizations
+
+When generating instructions, you can count on at least the following
+optimizations:
+
+- Single instructions are simplified, e.g.
+
+ and_i32 t0, t0, $0xffffffff
+
+ is suppressed.
+
+- A liveness analysis is done at the basic block level. The
+ information is used to suppress moves from a dead variable to
+ another one. It is also used to remove instructions which compute
+ dead results. The later is especially useful for condition code
+ optimization in QEMU.
+
+ In the following example:
+
+ add_i32 t0, t1, t2
+ add_i32 t0, t0, $1
+ mov_i32 t0, $1
+
+ only the last instruction is kept.
+
+3.4) Instruction Reference
+
+********* Function call
+
+* call <ret> <params> ptr
+
+call function 'ptr' (pointer type)
+
+<ret> optional 32 bit or 64 bit return value
+<params> optional 32 bit or 64 bit parameters
+
+********* Jumps/Labels
+
+* jmp t0
+
+Absolute jump to address t0 (pointer type).
+
+* set_label $label
+
+Define label 'label' at the current program point.
+
+* br $label
+
+Jump to label.
+
+* brcond_i32/i64 cond, t0, t1, label
+
+Conditional jump if t0 cond t1 is true. cond can be:
+ TCG_COND_EQ
+ TCG_COND_NE
+ TCG_COND_LT /* signed */
+ TCG_COND_GE /* signed */
+ TCG_COND_LE /* signed */
+ TCG_COND_GT /* signed */
+ TCG_COND_LTU /* unsigned */
+ TCG_COND_GEU /* unsigned */
+ TCG_COND_LEU /* unsigned */
+ TCG_COND_GTU /* unsigned */
+
+********* Arithmetic
+
+* add_i32/i64 t0, t1, t2
+
+t0=t1+t2
+
+* sub_i32/i64 t0, t1, t2
+
+t0=t1-t2
+
+* neg_i32/i64 t0, t1
+
+t0=-t1 (two's complement)
+
+* mul_i32/i64 t0, t1, t2
+
+t0=t1*t2
+
+* div_i32/i64 t0, t1, t2
+
+t0=t1/t2 (signed). Undefined behavior if division by zero or overflow.
+
+* divu_i32/i64 t0, t1, t2
+
+t0=t1/t2 (unsigned). Undefined behavior if division by zero.
+
+* rem_i32/i64 t0, t1, t2
+
+t0=t1%t2 (signed). Undefined behavior if division by zero or overflow.
+
+* remu_i32/i64 t0, t1, t2
+
+t0=t1%t2 (unsigned). Undefined behavior if division by zero.
+
+********* Logical
+
+* and_i32/i64 t0, t1, t2
+
+t0=t1&t2
+
+* or_i32/i64 t0, t1, t2
+
+t0=t1|t2
+
+* xor_i32/i64 t0, t1, t2
+
+t0=t1^t2
+
+* not_i32/i64 t0, t1
+
+t0=~t1
+
+* andc_i32/i64 t0, t1, t2
+
+t0=t1&~t2
+
+* eqv_i32/i64 t0, t1, t2
+
+t0=~(t1^t2), or equivalently, t0=t1^~t2
+
+* nand_i32/i64 t0, t1, t2
+
+t0=~(t1&t2)
+
+* nor_i32/i64 t0, t1, t2
+
+t0=~(t1|t2)
+
+* orc_i32/i64 t0, t1, t2
+
+t0=t1|~t2
+
+********* Shifts/Rotates
+
+* shl_i32/i64 t0, t1, t2
+
+t0=t1 << t2. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+* shr_i32/i64 t0, t1, t2
+
+t0=t1 >> t2 (unsigned). Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+* sar_i32/i64 t0, t1, t2
+
+t0=t1 >> t2 (signed). Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+* rotl_i32/i64 t0, t1, t2
+
+Rotation of t2 bits to the left. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+* rotr_i32/i64 t0, t1, t2
+
+Rotation of t2 bits to the right. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+********* Misc
+
+* mov_i32/i64 t0, t1
+
+t0 = t1
+
+Move t1 to t0 (both operands must have the same type).
+
+* ext8s_i32/i64 t0, t1
+ext8u_i32/i64 t0, t1
+ext16s_i32/i64 t0, t1
+ext16u_i32/i64 t0, t1
+ext32s_i64 t0, t1
+ext32u_i64 t0, t1
+
+8, 16 or 32 bit sign/zero extension (both operands must have the same type)
+
+* bswap16_i32/i64 t0, t1
+
+16 bit byte swap on a 32/64 bit value. It assumes that the two/six high order
+bytes are set to zero.
+
+* bswap32_i32/i64 t0, t1
+
+32 bit byte swap on a 32/64 bit value. With a 64 bit value, it assumes that
+the four high order bytes are set to zero.
+
+* bswap64_i64 t0, t1
+
+64 bit byte swap
+
+* discard_i32/i64 t0
+
+Indicate that the value of t0 won't be used later. It is useful to
+force dead code elimination.
+
+* deposit_i32/i64 dest, t1, t2, pos, len
+
+Deposit T2 as a bitfield into T1, placing the result in DEST.
+The bitfield is described by POS/LEN, which are immediate values:
+
+ LEN - the length of the bitfield
+ POS - the position of the first bit, counting from the LSB
+
+For example, pos=8, len=4 indicates a 4-bit field at bit 8.
+This operation would be equivalent to
+
+ dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00)
+
+
+********* Conditional moves
+
+* setcond_i32/i64 cond, dest, t1, t2
+
+dest = (t1 cond t2)
+
+Set DEST to 1 if (T1 cond T2) is true, otherwise set to 0.
+
+********* Type conversions
+
+* ext_i32_i64 t0, t1
+Convert t1 (32 bit) to t0 (64 bit) and does sign extension
+
+* extu_i32_i64 t0, t1
+Convert t1 (32 bit) to t0 (64 bit) and does zero extension
+
+* trunc_i64_i32 t0, t1
+Truncate t1 (64 bit) to t0 (32 bit)
+
+* concat_i32_i64 t0, t1, t2
+Construct t0 (64-bit) taking the low half from t1 (32 bit) and the high half
+from t2 (32 bit).
+
+* concat32_i64 t0, t1, t2
+Construct t0 (64-bit) taking the low half from t1 (64 bit) and the high half
+from t2 (64 bit).
+
+********* Load/Store
+
+* ld_i32/i64 t0, t1, offset
+ld8s_i32/i64 t0, t1, offset
+ld8u_i32/i64 t0, t1, offset
+ld16s_i32/i64 t0, t1, offset
+ld16u_i32/i64 t0, t1, offset
+ld32s_i64 t0, t1, offset
+ld32u_i64 t0, t1, offset
+
+t0 = read(t1 + offset)
+Load 8, 16, 32 or 64 bits with or without sign extension from host memory.
+offset must be a constant.
+
+* st_i32/i64 t0, t1, offset
+st8_i32/i64 t0, t1, offset
+st16_i32/i64 t0, t1, offset
+st32_i64 t0, t1, offset
+
+write(t0, t1 + offset)
+Write 8, 16, 32 or 64 bits to host memory.
+
+********* 64-bit target on 32-bit host support
+
+The following opcodes are internal to TCG. Thus they are to be implemented by
+32-bit host code generators, but are not to be emitted by guest translators.
+They are emitted as needed by inline functions within "tcg-op.h".
+
+* brcond2_i32 cond, t0_low, t0_high, t1_low, t1_high, label
+
+Similar to brcond, except that the 64-bit values T0 and T1
+are formed from two 32-bit arguments.
+
+* add2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high
+* sub2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high
+
+Similar to add/sub, except that the 64-bit inputs T1 and T2 are
+formed from two 32-bit arguments, and the 64-bit output T0
+is returned in two 32-bit outputs.
+
+* mulu2_i32 t0_low, t0_high, t1, t2
+
+Similar to mul, except two 32-bit (unsigned) inputs T1 and T2 yielding
+the full 64-bit product T0. The later is returned in two 32-bit outputs.
+
+* setcond2_i32 cond, dest, t1_low, t1_high, t2_low, t2_high
+
+Similar to setcond, except that the 64-bit values T1 and T2 are
+formed from two 32-bit arguments. The result is a 32-bit value.
+
+********* QEMU specific operations
+
+* exit_tb t0
+
+Exit the current TB and return the value t0 (word type).
+
+* goto_tb index
+
+Exit the current TB and jump to the TB index 'index' (constant) if the
+current TB was linked to this TB. Otherwise execute the next
+instructions.
+
+* qemu_ld8u t0, t1, flags
+qemu_ld8s t0, t1, flags
+qemu_ld16u t0, t1, flags
+qemu_ld16s t0, t1, flags
+qemu_ld32 t0, t1, flags
+qemu_ld32u t0, t1, flags
+qemu_ld32s t0, t1, flags
+qemu_ld64 t0, t1, flags
+
+Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU address
+type. 'flags' contains the QEMU memory index (selects user or kernel access)
+for example.
+
+Note that "qemu_ld32" implies a 32-bit result, while "qemu_ld32u" and
+"qemu_ld32s" imply a 64-bit result appropriately extended from 32 bits.
+
+* qemu_st8 t0, t1, flags
+qemu_st16 t0, t1, flags
+qemu_st32 t0, t1, flags
+qemu_st64 t0, t1, flags
+
+Store the data t0 at the QEMU CPU Address t1. t1 has the QEMU CPU
+address type. 'flags' contains the QEMU memory index (selects user or
+kernel access) for example.
+
+Note 1: Some shortcuts are defined when the last operand is known to be
+a constant (e.g. addi for add, movi for mov).
+
+Note 2: When using TCG, the opcodes must never be generated directly
+as some of them may not be available as "real" opcodes. Always use the
+function tcg_gen_xxx(args).
+
+4) Backend
+
+tcg-target.h contains the target specific definitions. tcg-target.c
+contains the target specific code.
+
+4.1) Assumptions
+
+The target word size (TCG_TARGET_REG_BITS) is expected to be 32 bit or
+64 bit. It is expected that the pointer has the same size as the word.
+
+On a 32 bit target, all 64 bit operations are converted to 32 bits. A
+few specific operations must be implemented to allow it (see add2_i32,
+sub2_i32, brcond2_i32).
+
+Floating point operations are not supported in this version. A
+previous incarnation of the code generator had full support of them,
+but it is better to concentrate on integer operations first.
+
+On a 64 bit target, no assumption is made in TCG about the storage of
+the 32 bit values in 64 bit registers.
+
+4.2) Constraints
+
+GCC like constraints are used to define the constraints of every
+instruction. Memory constraints are not supported in this
+version. Aliases are specified in the input operands as for GCC.
+
+The same register may be used for both an input and an output, even when
+they are not explicitly aliased. If an op expands to multiple target
+instructions then care must be taken to avoid clobbering input values.
+GCC style "early clobber" outputs are not currently supported.
+
+A target can define specific register or constant constraints. If an
+operation uses a constant input constraint which does not allow all
+constants, it must also accept registers in order to have a fallback.
+
+The movi_i32 and movi_i64 operations must accept any constants.
+
+The mov_i32 and mov_i64 operations must accept any registers of the
+same type.
+
+The ld/st instructions must accept signed 32 bit constant offsets. It
+can be implemented by reserving a specific register to compute the
+address if the offset is too big.
+
+The ld/st instructions must accept any destination (ld) or source (st)
+register.
+
+4.3) Function call assumptions
+
+- The only supported types for parameters and return value are: 32 and
+ 64 bit integers and pointer.
+- The stack grows downwards.
+- The first N parameters are passed in registers.
+- The next parameters are passed on the stack by storing them as words.
+- Some registers are clobbered during the call.
+- The function can return 0 or 1 value in registers. On a 32 bit
+ target, functions must be able to return 2 values in registers for
+ 64 bit return type.
+
+5) Recommended coding rules for best performance
+
+- Use globals to represent the parts of the QEMU CPU state which are
+ often modified, e.g. the integer registers and the condition
+ codes. TCG will be able to use host registers to store them.
+
+- Avoid globals stored in fixed registers. They must be used only to
+ store the pointer to the CPU state and possibly to store a pointer
+ to a register window.
+
+- Use temporaries. Use local temporaries only when really needed,
+ e.g. when you need to use a value after a jump. Local temporaries
+ introduce a performance hit in the current TCG implementation: their
+ content is saved to memory at end of each basic block.
+
+- Free temporaries and local temporaries when they are no longer used
+ (tcg_temp_free). Since tcg_const_x() also creates a temporary, you
+ should free it after it is used. Freeing temporaries does not yield
+ a better generated code, but it reduces the memory usage of TCG and
+ the speed of the translation.
+
+- Don't hesitate to use helpers for complicated or seldom used target
+ instructions. There is little performance advantage in using TCG to
+ implement target instructions taking more than about twenty TCG
+ instructions.
+
+- Use the 'discard' instruction if you know that TCG won't be able to
+ prove that a given global is "dead" at a given program point. The
+ x86 target uses it to improve the condition codes optimisation.
diff --git a/tcg/TODO b/tcg/TODO
new file mode 100644
index 0000000..0747847
--- /dev/null
+++ b/tcg/TODO
@@ -0,0 +1,14 @@
+- Add new instructions such as: clz, ctz, popcnt.
+
+- See if it is worth exporting mul2, mulu2, div2, divu2.
+
+- Support of globals saved in fixed registers between TBs.
+
+Ideas:
+
+- Move the slow part of the qemu_ld/st ops after the end of the TB.
+
+- Change exception syntax to get closer to QOP system (exception
+ parameters given with a specific instruction).
+
+- Add float and vector support.
diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c
new file mode 100644
index 0000000..918e2f7
--- /dev/null
+++ b/tcg/arm/tcg-target.c
@@ -0,0 +1,1843 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if defined(__ARM_ARCH_7__) || \
+ defined(__ARM_ARCH_7A__) || \
+ defined(__ARM_ARCH_7EM__) || \
+ defined(__ARM_ARCH_7M__) || \
+ defined(__ARM_ARCH_7R__)
+#define USE_ARMV7_INSTRUCTIONS
+#endif
+
+#if defined(USE_ARMV7_INSTRUCTIONS) || \
+ defined(__ARM_ARCH_6J__) || \
+ defined(__ARM_ARCH_6K__) || \
+ defined(__ARM_ARCH_6T2__) || \
+ defined(__ARM_ARCH_6Z__) || \
+ defined(__ARM_ARCH_6ZK__)
+#define USE_ARMV6_INSTRUCTIONS
+#endif
+
+#if defined(USE_ARMV6_INSTRUCTIONS) || \
+ defined(__ARM_ARCH_5T__) || \
+ defined(__ARM_ARCH_5TE__) || \
+ defined(__ARM_ARCH_5TEJ__)
+#define USE_ARMV5_INSTRUCTIONS
+#endif
+
+#ifdef USE_ARMV5_INSTRUCTIONS
+static const int use_armv5_instructions = 1;
+#else
+static const int use_armv5_instructions = 0;
+#endif
+#undef USE_ARMV5_INSTRUCTIONS
+
+#ifdef USE_ARMV6_INSTRUCTIONS
+static const int use_armv6_instructions = 1;
+#else
+static const int use_armv6_instructions = 0;
+#endif
+#undef USE_ARMV6_INSTRUCTIONS
+
+#ifdef USE_ARMV7_INSTRUCTIONS
+static const int use_armv7_instructions = 1;
+#else
+static const int use_armv7_instructions = 0;
+#endif
+#undef USE_ARMV7_INSTRUCTIONS
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "%r0",
+ "%r1",
+ "%r2",
+ "%r3",
+ "%r4",
+ "%r5",
+ "%r6",
+ "%r7",
+ "%r8",
+ "%r9",
+ "%r10",
+ "%r11",
+ "%r12",
+ "%r13",
+ "%r14",
+ "%pc",
+};
+#endif
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R13,
+ TCG_REG_R0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R12,
+ TCG_REG_R14,
+};
+
+static const int tcg_target_call_iarg_regs[4] = {
+ TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3
+};
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_R0, TCG_REG_R1
+};
+
+static inline void reloc_abs32(void *code_ptr, tcg_target_long target)
+{
+ *(uint32_t *) code_ptr = target;
+}
+
+static inline void reloc_pc24(void *code_ptr, tcg_target_long target)
+{
+ uint32_t offset = ((target - ((tcg_target_long) code_ptr + 8)) >> 2);
+
+ *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & ~0xffffff)
+ | (offset & 0xffffff);
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ switch (type) {
+ case R_ARM_ABS32:
+ reloc_abs32(code_ptr, value);
+ break;
+
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ default:
+ tcg_abort();
+
+ case R_ARM_PC24:
+ reloc_pc24(code_ptr, value);
+ break;
+ }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return 4;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch (ct_str[0]) {
+ case 'I':
+ ct->ct |= TCG_CT_CONST_ARM;
+ break;
+
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+ break;
+
+ /* qemu_ld address */
+ case 'l':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+#ifdef CONFIG_SOFTMMU
+ /* r0 and r1 will be overwritten when reading the tlb entry,
+ so don't use these. */
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+#endif
+ break;
+ case 'L':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+#ifdef CONFIG_SOFTMMU
+ /* r1 is still needed to load data_reg or data_reg2,
+ so don't use it. */
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+#endif
+ break;
+
+ /* qemu_st address & data_reg */
+ case 's':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+ /* r0 and r1 will be overwritten when reading the tlb entry
+ (softmmu only) and doing the byte swapping, so don't
+ use these. */
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+ break;
+ /* qemu_st64 data_reg2 */
+ case 'S':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+ /* r0 and r1 will be overwritten when reading the tlb entry
+ (softmmu only) and doing the byte swapping, so don't
+ use these. */
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+#ifdef CONFIG_SOFTMMU
+ /* r2 is still needed to load data_reg, so don't use it. */
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2);
+#endif
+ break;
+
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+
+ return 0;
+}
+
+static inline uint32_t rotl(uint32_t val, int n)
+{
+ return (val << n) | (val >> (32 - n));
+}
+
+/* ARM immediates for ALU instructions are made of an unsigned 8-bit
+ right-rotated by an even amount between 0 and 30. */
+static inline int encode_imm(uint32_t imm)
+{
+ int shift;
+
+ /* simple case, only lower bits */
+ if ((imm & ~0xff) == 0)
+ return 0;
+ /* then try a simple even shift */
+ shift = ctz32(imm) & ~1;
+ if (((imm >> shift) & ~0xff) == 0)
+ return 32 - shift;
+ /* now try harder with rotations */
+ if ((rotl(imm, 2) & ~0xff) == 0)
+ return 2;
+ if ((rotl(imm, 4) & ~0xff) == 0)
+ return 4;
+ if ((rotl(imm, 6) & ~0xff) == 0)
+ return 6;
+ /* imm can't be encoded */
+ return -1;
+}
+
+static inline int check_fit_imm(uint32_t imm)
+{
+ return encode_imm(imm) >= 0;
+}
+
+/* Test if a constant matches the constraint.
+ * TODO: define constraints for:
+ *
+ * ldr/str offset: between -0xfff and 0xfff
+ * ldrh/strh offset: between -0xff and 0xff
+ * mov operand2: values represented with x << (2 * y), x < 0x100
+ * add, sub, eor...: ditto
+ */
+static inline int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct;
+ ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST)
+ return 1;
+ else if ((ct & TCG_CT_CONST_ARM) && check_fit_imm(val))
+ return 1;
+ else
+ return 0;
+}
+
+enum arm_data_opc_e {
+ ARITH_AND = 0x0,
+ ARITH_EOR = 0x1,
+ ARITH_SUB = 0x2,
+ ARITH_RSB = 0x3,
+ ARITH_ADD = 0x4,
+ ARITH_ADC = 0x5,
+ ARITH_SBC = 0x6,
+ ARITH_RSC = 0x7,
+ ARITH_TST = 0x8,
+ ARITH_CMP = 0xa,
+ ARITH_CMN = 0xb,
+ ARITH_ORR = 0xc,
+ ARITH_MOV = 0xd,
+ ARITH_BIC = 0xe,
+ ARITH_MVN = 0xf,
+};
+
+#define TO_CPSR(opc) \
+ ((opc == ARITH_CMP || opc == ARITH_CMN || opc == ARITH_TST) << 20)
+
+#define SHIFT_IMM_LSL(im) (((im) << 7) | 0x00)
+#define SHIFT_IMM_LSR(im) (((im) << 7) | 0x20)
+#define SHIFT_IMM_ASR(im) (((im) << 7) | 0x40)
+#define SHIFT_IMM_ROR(im) (((im) << 7) | 0x60)
+#define SHIFT_REG_LSL(rs) (((rs) << 8) | 0x10)
+#define SHIFT_REG_LSR(rs) (((rs) << 8) | 0x30)
+#define SHIFT_REG_ASR(rs) (((rs) << 8) | 0x50)
+#define SHIFT_REG_ROR(rs) (((rs) << 8) | 0x70)
+
+enum arm_cond_code_e {
+ COND_EQ = 0x0,
+ COND_NE = 0x1,
+ COND_CS = 0x2, /* Unsigned greater or equal */
+ COND_CC = 0x3, /* Unsigned less than */
+ COND_MI = 0x4, /* Negative */
+ COND_PL = 0x5, /* Zero or greater */
+ COND_VS = 0x6, /* Overflow */
+ COND_VC = 0x7, /* No overflow */
+ COND_HI = 0x8, /* Unsigned greater than */
+ COND_LS = 0x9, /* Unsigned less or equal */
+ COND_GE = 0xa,
+ COND_LT = 0xb,
+ COND_GT = 0xc,
+ COND_LE = 0xd,
+ COND_AL = 0xe,
+};
+
+static const uint8_t tcg_cond_to_arm_cond[10] = {
+ [TCG_COND_EQ] = COND_EQ,
+ [TCG_COND_NE] = COND_NE,
+ [TCG_COND_LT] = COND_LT,
+ [TCG_COND_GE] = COND_GE,
+ [TCG_COND_LE] = COND_LE,
+ [TCG_COND_GT] = COND_GT,
+ /* unsigned */
+ [TCG_COND_LTU] = COND_CC,
+ [TCG_COND_GEU] = COND_CS,
+ [TCG_COND_LEU] = COND_LS,
+ [TCG_COND_GTU] = COND_HI,
+};
+
+static inline void tcg_out_bx(TCGContext *s, int cond, int rn)
+{
+ tcg_out32(s, (cond << 28) | 0x012fff10 | rn);
+}
+
+static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset)
+{
+ tcg_out32(s, (cond << 28) | 0x0a000000 |
+ (((offset - 8) >> 2) & 0x00ffffff));
+}
+
+static inline void tcg_out_b_noaddr(TCGContext *s, int cond)
+{
+ /* We pay attention here to not modify the branch target by skipping
+ the corresponding bytes. This ensure that caches and memory are
+ kept coherent during retranslation. */
+#ifdef HOST_WORDS_BIGENDIAN
+ tcg_out8(s, (cond << 4) | 0x0a);
+ s->code_ptr += 3;
+#else
+ s->code_ptr += 3;
+ tcg_out8(s, (cond << 4) | 0x0a);
+#endif
+}
+
+static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset)
+{
+ tcg_out32(s, (cond << 28) | 0x0b000000 |
+ (((offset - 8) >> 2) & 0x00ffffff));
+}
+
+static inline void tcg_out_blx(TCGContext *s, int cond, int rn)
+{
+ tcg_out32(s, (cond << 28) | 0x012fff30 | rn);
+}
+
+static inline void tcg_out_dat_reg(TCGContext *s,
+ int cond, int opc, int rd, int rn, int rm, int shift)
+{
+ tcg_out32(s, (cond << 28) | (0 << 25) | (opc << 21) | TO_CPSR(opc) |
+ (rn << 16) | (rd << 12) | shift | rm);
+}
+
+static inline void tcg_out_dat_reg2(TCGContext *s,
+ int cond, int opc0, int opc1, int rd0, int rd1,
+ int rn0, int rn1, int rm0, int rm1, int shift)
+{
+ if (rd0 == rn1 || rd0 == rm1) {
+ tcg_out32(s, (cond << 28) | (0 << 25) | (opc0 << 21) | (1 << 20) |
+ (rn0 << 16) | (8 << 12) | shift | rm0);
+ tcg_out32(s, (cond << 28) | (0 << 25) | (opc1 << 21) |
+ (rn1 << 16) | (rd1 << 12) | shift | rm1);
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd0, 0, TCG_REG_R8, SHIFT_IMM_LSL(0));
+ } else {
+ tcg_out32(s, (cond << 28) | (0 << 25) | (opc0 << 21) | (1 << 20) |
+ (rn0 << 16) | (rd0 << 12) | shift | rm0);
+ tcg_out32(s, (cond << 28) | (0 << 25) | (opc1 << 21) |
+ (rn1 << 16) | (rd1 << 12) | shift | rm1);
+ }
+}
+
+static inline void tcg_out_dat_imm(TCGContext *s,
+ int cond, int opc, int rd, int rn, int im)
+{
+ tcg_out32(s, (cond << 28) | (1 << 25) | (opc << 21) | TO_CPSR(opc) |
+ (rn << 16) | (rd << 12) | im);
+}
+
+static inline void tcg_out_movi32(TCGContext *s,
+ int cond, int rd, uint32_t arg)
+{
+ /* TODO: This is very suboptimal, we can easily have a constant
+ * pool somewhere after all the instructions. */
+ if ((int)arg < 0 && (int)arg >= -0x100) {
+ tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff);
+ } else if (use_armv7_instructions) {
+ /* use movw/movt */
+ /* movw */
+ tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12)
+ | ((arg << 4) & 0x000f0000) | (arg & 0xfff));
+ if (arg & 0xffff0000) {
+ /* movt */
+ tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12)
+ | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff));
+ }
+ } else {
+ int opc = ARITH_MOV;
+ int rn = 0;
+
+ do {
+ int i, rot;
+
+ i = ctz32(arg) & ~1;
+ rot = ((32 - i) << 7) & 0xf00;
+ tcg_out_dat_imm(s, cond, opc, rd, rn, ((arg >> i) & 0xff) | rot);
+ arg &= ~(0xff << i);
+
+ opc = ARITH_ORR;
+ rn = rd;
+ } while (arg);
+ }
+}
+
+static inline void tcg_out_mul32(TCGContext *s,
+ int cond, int rd, int rs, int rm)
+{
+ if (rd != rm)
+ tcg_out32(s, (cond << 28) | (rd << 16) | (0 << 12) |
+ (rs << 8) | 0x90 | rm);
+ else if (rd != rs)
+ tcg_out32(s, (cond << 28) | (rd << 16) | (0 << 12) |
+ (rm << 8) | 0x90 | rs);
+ else {
+ tcg_out32(s, (cond << 28) | ( 8 << 16) | (0 << 12) |
+ (rs << 8) | 0x90 | rm);
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, TCG_REG_R8, SHIFT_IMM_LSL(0));
+ }
+}
+
+static inline void tcg_out_umull32(TCGContext *s,
+ int cond, int rd0, int rd1, int rs, int rm)
+{
+ if (rd0 != rm && rd1 != rm)
+ tcg_out32(s, (cond << 28) | 0x800090 |
+ (rd1 << 16) | (rd0 << 12) | (rs << 8) | rm);
+ else if (rd0 != rs && rd1 != rs)
+ tcg_out32(s, (cond << 28) | 0x800090 |
+ (rd1 << 16) | (rd0 << 12) | (rm << 8) | rs);
+ else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ TCG_REG_R8, 0, rm, SHIFT_IMM_LSL(0));
+ tcg_out32(s, (cond << 28) | 0x800098 |
+ (rd1 << 16) | (rd0 << 12) | (rs << 8));
+ }
+}
+
+static inline void tcg_out_smull32(TCGContext *s,
+ int cond, int rd0, int rd1, int rs, int rm)
+{
+ if (rd0 != rm && rd1 != rm)
+ tcg_out32(s, (cond << 28) | 0xc00090 |
+ (rd1 << 16) | (rd0 << 12) | (rs << 8) | rm);
+ else if (rd0 != rs && rd1 != rs)
+ tcg_out32(s, (cond << 28) | 0xc00090 |
+ (rd1 << 16) | (rd0 << 12) | (rm << 8) | rs);
+ else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ TCG_REG_R8, 0, rm, SHIFT_IMM_LSL(0));
+ tcg_out32(s, (cond << 28) | 0xc00098 |
+ (rd1 << 16) | (rd0 << 12) | (rs << 8));
+ }
+}
+
+static inline void tcg_out_ext8s(TCGContext *s, int cond,
+ int rd, int rn)
+{
+ if (use_armv6_instructions) {
+ /* sxtb */
+ tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rn, SHIFT_IMM_LSL(24));
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rd, SHIFT_IMM_ASR(24));
+ }
+}
+
+static inline void tcg_out_ext8u(TCGContext *s, int cond,
+ int rd, int rn)
+{
+ tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff);
+}
+
+static inline void tcg_out_ext16s(TCGContext *s, int cond,
+ int rd, int rn)
+{
+ if (use_armv6_instructions) {
+ /* sxth */
+ tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rn, SHIFT_IMM_LSL(16));
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rd, SHIFT_IMM_ASR(16));
+ }
+}
+
+static inline void tcg_out_ext16u(TCGContext *s, int cond,
+ int rd, int rn)
+{
+ if (use_armv6_instructions) {
+ /* uxth */
+ tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rn, SHIFT_IMM_LSL(16));
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rd, SHIFT_IMM_LSR(16));
+ }
+}
+
+static inline void tcg_out_bswap16s(TCGContext *s, int cond, int rd, int rn)
+{
+ if (use_armv6_instructions) {
+ /* revsh */
+ tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24));
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_ASR(16));
+ tcg_out_dat_reg(s, cond, ARITH_ORR,
+ rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8));
+ }
+}
+
+static inline void tcg_out_bswap16(TCGContext *s, int cond, int rd, int rn)
+{
+ if (use_armv6_instructions) {
+ /* rev16 */
+ tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24));
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_LSR(16));
+ tcg_out_dat_reg(s, cond, ARITH_ORR,
+ rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8));
+ }
+}
+
+static inline void tcg_out_bswap32(TCGContext *s, int cond, int rd, int rn)
+{
+ if (use_armv6_instructions) {
+ /* rev */
+ tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_EOR,
+ TCG_REG_R8, rn, rn, SHIFT_IMM_ROR(16));
+ tcg_out_dat_imm(s, cond, ARITH_BIC,
+ TCG_REG_R8, TCG_REG_R8, 0xff | 0x800);
+ tcg_out_dat_reg(s, cond, ARITH_MOV,
+ rd, 0, rn, SHIFT_IMM_ROR(8));
+ tcg_out_dat_reg(s, cond, ARITH_EOR,
+ rd, rd, TCG_REG_R8, SHIFT_IMM_LSR(8));
+ }
+}
+
+static inline void tcg_out_ld32_12(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x05900000 |
+ (rn << 16) | (rd << 12) | (im & 0xfff));
+ else
+ tcg_out32(s, (cond << 28) | 0x05100000 |
+ (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_st32_12(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x05800000 |
+ (rn << 16) | (rd << 12) | (im & 0xfff));
+ else
+ tcg_out32(s, (cond << 28) | 0x05000000 |
+ (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_ld32_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x07900000 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st32_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x07800000 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+/* Register pre-increment with base writeback. */
+static inline void tcg_out_ld32_rwb(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x07b00000 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st32_rwb(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x07a00000 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld16u_8(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x01d000b0 |
+ (rn << 16) | (rd << 12) |
+ ((im & 0xf0) << 4) | (im & 0xf));
+ else
+ tcg_out32(s, (cond << 28) | 0x015000b0 |
+ (rn << 16) | (rd << 12) |
+ (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_st16_8(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x01c000b0 |
+ (rn << 16) | (rd << 12) |
+ ((im & 0xf0) << 4) | (im & 0xf));
+ else
+ tcg_out32(s, (cond << 28) | 0x014000b0 |
+ (rn << 16) | (rd << 12) |
+ (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_ld16u_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x019000b0 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st16_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x018000b0 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld16s_8(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x01d000f0 |
+ (rn << 16) | (rd << 12) |
+ ((im & 0xf0) << 4) | (im & 0xf));
+ else
+ tcg_out32(s, (cond << 28) | 0x015000f0 |
+ (rn << 16) | (rd << 12) |
+ (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_ld16s_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x019000f0 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld8_12(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x05d00000 |
+ (rn << 16) | (rd << 12) | (im & 0xfff));
+ else
+ tcg_out32(s, (cond << 28) | 0x05500000 |
+ (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_st8_12(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x05c00000 |
+ (rn << 16) | (rd << 12) | (im & 0xfff));
+ else
+ tcg_out32(s, (cond << 28) | 0x05400000 |
+ (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_ld8_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x07d00000 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st8_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x07c00000 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld8s_8(TCGContext *s, int cond,
+ int rd, int rn, tcg_target_long im)
+{
+ if (im >= 0)
+ tcg_out32(s, (cond << 28) | 0x01d000d0 |
+ (rn << 16) | (rd << 12) |
+ ((im & 0xf0) << 4) | (im & 0xf));
+ else
+ tcg_out32(s, (cond << 28) | 0x015000d0 |
+ (rn << 16) | (rd << 12) |
+ (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_ld8s_r(TCGContext *s, int cond,
+ int rd, int rn, int rm)
+{
+ tcg_out32(s, (cond << 28) | 0x019000d0 |
+ (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld32u(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xfff || offset < -0xfff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_ld32_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_ld32_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_st32(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xfff || offset < -0xfff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_st32_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_st32_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld16u(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xff || offset < -0xff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_ld16u_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld16s(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xff || offset < -0xff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_ld16s_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_st16(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xff || offset < -0xff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_st16_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_st16_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld8u(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xfff || offset < -0xfff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_ld8_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld8s(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xff || offset < -0xff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_ld8s_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_st8(TCGContext *s, int cond,
+ int rd, int rn, int32_t offset)
+{
+ if (offset > 0xfff || offset < -0xfff) {
+ tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+ tcg_out_st8_r(s, cond, rd, rn, TCG_REG_R8);
+ } else
+ tcg_out_st8_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr)
+{
+ int32_t val;
+
+ val = addr - (tcg_target_long) s->code_ptr;
+ if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd)
+ tcg_out_b(s, cond, val);
+ else {
+#if 1
+ tcg_abort();
+#else
+ if (cond == COND_AL) {
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4);
+ tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */
+ } else {
+ tcg_out_movi32(s, cond, TCG_REG_R8, val - 8);
+ tcg_out_dat_reg(s, cond, ARITH_ADD,
+ TCG_REG_PC, TCG_REG_PC,
+ TCG_REG_R8, SHIFT_IMM_LSL(0));
+ }
+#endif
+ }
+}
+
+static inline void tcg_out_call(TCGContext *s, int cond, uint32_t addr)
+{
+ int32_t val;
+
+ val = addr - (tcg_target_long) s->code_ptr;
+ if (val < 0x01fffffd && val > -0x01fffffd)
+ tcg_out_bl(s, cond, val);
+ else {
+#if 1
+ tcg_abort();
+#else
+ if (cond == COND_AL) {
+ tcg_out_dat_imm(s, cond, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4);
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4);
+ tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */
+ } else {
+ tcg_out_movi32(s, cond, TCG_REG_R9, addr);
+ tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0,
+ TCG_REG_PC, SHIFT_IMM_LSL(0));
+ tcg_out_bx(s, cond, TCG_REG_R9);
+ }
+#endif
+ }
+}
+
+static inline void tcg_out_callr(TCGContext *s, int cond, int arg)
+{
+ if (use_armv5_instructions) {
+ tcg_out_blx(s, cond, arg);
+ } else {
+ tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0,
+ TCG_REG_PC, SHIFT_IMM_LSL(0));
+ tcg_out_bx(s, cond, arg);
+ }
+}
+
+static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index)
+{
+ TCGLabel *l = &s->labels[label_index];
+
+ if (l->has_value)
+ tcg_out_goto(s, cond, l->u.value);
+ else if (cond == COND_AL) {
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4);
+ tcg_out_reloc(s, s->code_ptr, R_ARM_ABS32, label_index, 31337);
+ s->code_ptr += 4;
+ } else {
+ /* Probably this should be preferred even for COND_AL... */
+ tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, label_index, 31337);
+ tcg_out_b_noaddr(s, cond);
+ }
+}
+
+#ifdef CONFIG_SOFTMMU
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+#endif
+
+#define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS)
+
+static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, data_reg, data_reg2, bswap;
+#ifdef CONFIG_SOFTMMU
+ int mem_index, s_bits;
+# if TARGET_LONG_BITS == 64
+ int addr_reg2;
+# endif
+ uint32_t *label_ptr;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 1;
+#else
+ bswap = 0;
+#endif
+ data_reg = *args++;
+ if (opc == 3)
+ data_reg2 = *args++;
+ else
+ data_reg2 = 0; /* suppress warning */
+ addr_reg = *args++;
+#ifdef CONFIG_SOFTMMU
+# if TARGET_LONG_BITS == 64
+ addr_reg2 = *args++;
+# endif
+ mem_index = *args;
+ s_bits = opc & 3;
+
+ /* Should generate something like the following:
+ * shr r8, addr_reg, #TARGET_PAGE_BITS
+ * and r0, r8, #(CPU_TLB_SIZE - 1) @ Assumption: CPU_TLB_BITS <= 8
+ * add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS
+ */
+# if CPU_TLB_BITS > 8
+# error
+# endif
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_R8,
+ 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS));
+ tcg_out_dat_imm(s, COND_AL, ARITH_AND,
+ TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1);
+ tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_AREG0,
+ TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS));
+ /* In the
+ * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_read))]
+ * below, the offset is likely to exceed 12 bits if mem_index != 0 and
+ * not exceed otherwise, so use an
+ * add r0, r0, #(mem_index * sizeof *CPUState.tlb_table)
+ * before.
+ */
+ if (mem_index)
+ tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0,
+ (mem_index << (TLB_SHIFT & 1)) |
+ ((16 - (TLB_SHIFT >> 1)) << 8));
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0,
+ offsetof(CPUState, tlb_table[0][0].addr_read));
+ tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1,
+ TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS));
+ /* Check alignment. */
+ if (s_bits)
+ tcg_out_dat_imm(s, COND_EQ, ARITH_TST,
+ 0, addr_reg, (1 << s_bits) - 1);
+# if TARGET_LONG_BITS == 64
+ /* XXX: possibly we could use a block data load or writeback in
+ * the first access. */
+ tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0,
+ offsetof(CPUState, tlb_table[0][0].addr_read) + 4);
+ tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
+ TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0));
+# endif
+ tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0,
+ offsetof(CPUState, tlb_table[0][0].addend));
+
+ switch (opc) {
+ case 0:
+ tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ break;
+ case 0 | 4:
+ tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ break;
+ case 1:
+ tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ if (bswap) {
+ tcg_out_bswap16(s, COND_EQ, data_reg, data_reg);
+ }
+ break;
+ case 1 | 4:
+ if (bswap) {
+ tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ tcg_out_bswap16s(s, COND_EQ, data_reg, data_reg);
+ } else {
+ tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ }
+ break;
+ case 2:
+ default:
+ tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ if (bswap) {
+ tcg_out_bswap32(s, COND_EQ, data_reg, data_reg);
+ }
+ break;
+ case 3:
+ if (bswap) {
+ tcg_out_ld32_rwb(s, COND_EQ, data_reg2, TCG_REG_R1, addr_reg);
+ tcg_out_ld32_12(s, COND_EQ, data_reg, TCG_REG_R1, 4);
+ tcg_out_bswap32(s, COND_EQ, data_reg2, data_reg2);
+ tcg_out_bswap32(s, COND_EQ, data_reg, data_reg);
+ } else {
+ tcg_out_ld32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg);
+ tcg_out_ld32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4);
+ }
+ break;
+ }
+
+ label_ptr = (void *) s->code_ptr;
+ tcg_out_b_noaddr(s, COND_EQ);
+
+ /* TODO: move this code to where the constants pool will be */
+ if (addr_reg != TCG_REG_R0) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0));
+ }
+# if TARGET_LONG_BITS == 32
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R1, 0, mem_index);
+# else
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0));
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index);
+# endif
+ tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_ld_helpers[s_bits] -
+ (tcg_target_long) s->code_ptr);
+
+ switch (opc) {
+ case 0 | 4:
+ tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0);
+ break;
+ case 1 | 4:
+ tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ default:
+ if (data_reg != TCG_REG_R0) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0));
+ }
+ break;
+ case 3:
+ if (data_reg != TCG_REG_R0) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0));
+ }
+ if (data_reg2 != TCG_REG_R1) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ data_reg2, 0, TCG_REG_R1, SHIFT_IMM_LSL(0));
+ }
+ break;
+ }
+
+ reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr);
+#else /* !CONFIG_SOFTMMU */
+ if (GUEST_BASE) {
+ uint32_t offset = GUEST_BASE;
+ int i;
+ int rot;
+
+ while (offset) {
+ i = ctz32(offset) & ~1;
+ rot = ((32 - i) << 7) & 0xf00;
+
+ tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R8, addr_reg,
+ ((offset >> i) & 0xff) | rot);
+ addr_reg = TCG_REG_R8;
+ offset &= ~(0xff << i);
+ }
+ }
+ switch (opc) {
+ case 0:
+ tcg_out_ld8_12(s, COND_AL, data_reg, addr_reg, 0);
+ break;
+ case 0 | 4:
+ tcg_out_ld8s_8(s, COND_AL, data_reg, addr_reg, 0);
+ break;
+ case 1:
+ tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0);
+ if (bswap) {
+ tcg_out_bswap16(s, COND_AL, data_reg, data_reg);
+ }
+ break;
+ case 1 | 4:
+ if (bswap) {
+ tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0);
+ tcg_out_bswap16s(s, COND_AL, data_reg, data_reg);
+ } else {
+ tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0);
+ }
+ break;
+ case 2:
+ default:
+ tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0);
+ if (bswap) {
+ tcg_out_bswap32(s, COND_AL, data_reg, data_reg);
+ }
+ break;
+ case 3:
+ /* TODO: use block load -
+ * check that data_reg2 > data_reg or the other way */
+ if (data_reg == addr_reg) {
+ tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4);
+ tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0);
+ } else {
+ tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0);
+ tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4);
+ }
+ if (bswap) {
+ tcg_out_bswap32(s, COND_AL, data_reg, data_reg);
+ tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2);
+ }
+ break;
+ }
+#endif
+}
+
+static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, data_reg, data_reg2, bswap;
+#ifdef CONFIG_SOFTMMU
+ int mem_index, s_bits;
+# if TARGET_LONG_BITS == 64
+ int addr_reg2;
+# endif
+ uint32_t *label_ptr;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 1;
+#else
+ bswap = 0;
+#endif
+ data_reg = *args++;
+ if (opc == 3)
+ data_reg2 = *args++;
+ else
+ data_reg2 = 0; /* suppress warning */
+ addr_reg = *args++;
+#ifdef CONFIG_SOFTMMU
+# if TARGET_LONG_BITS == 64
+ addr_reg2 = *args++;
+# endif
+ mem_index = *args;
+ s_bits = opc & 3;
+
+ /* Should generate something like the following:
+ * shr r8, addr_reg, #TARGET_PAGE_BITS
+ * and r0, r8, #(CPU_TLB_SIZE - 1) @ Assumption: CPU_TLB_BITS <= 8
+ * add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS
+ */
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS));
+ tcg_out_dat_imm(s, COND_AL, ARITH_AND,
+ TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1);
+ tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0,
+ TCG_AREG0, TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS));
+ /* In the
+ * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_write))]
+ * below, the offset is likely to exceed 12 bits if mem_index != 0 and
+ * not exceed otherwise, so use an
+ * add r0, r0, #(mem_index * sizeof *CPUState.tlb_table)
+ * before.
+ */
+ if (mem_index)
+ tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0,
+ (mem_index << (TLB_SHIFT & 1)) |
+ ((16 - (TLB_SHIFT >> 1)) << 8));
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0,
+ offsetof(CPUState, tlb_table[0][0].addr_write));
+ tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1,
+ TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS));
+ /* Check alignment. */
+ if (s_bits)
+ tcg_out_dat_imm(s, COND_EQ, ARITH_TST,
+ 0, addr_reg, (1 << s_bits) - 1);
+# if TARGET_LONG_BITS == 64
+ /* XXX: possibly we could use a block data load or writeback in
+ * the first access. */
+ tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0,
+ offsetof(CPUState, tlb_table[0][0].addr_write) + 4);
+ tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
+ TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0));
+# endif
+ tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0,
+ offsetof(CPUState, tlb_table[0][0].addend));
+
+ switch (opc) {
+ case 0:
+ tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ break;
+ case 1:
+ if (bswap) {
+ tcg_out_bswap16(s, COND_EQ, TCG_REG_R0, data_reg);
+ tcg_out_st16_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1);
+ } else {
+ tcg_out_st16_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ }
+ break;
+ case 2:
+ default:
+ if (bswap) {
+ tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg);
+ tcg_out_st32_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1);
+ } else {
+ tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1);
+ }
+ break;
+ case 3:
+ if (bswap) {
+ tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg2);
+ tcg_out_st32_rwb(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, addr_reg);
+ tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg);
+ tcg_out_st32_12(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, 4);
+ } else {
+ tcg_out_st32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg);
+ tcg_out_st32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4);
+ }
+ break;
+ }
+
+ label_ptr = (void *) s->code_ptr;
+ tcg_out_b_noaddr(s, COND_EQ);
+
+ /* TODO: move this code to where the constants pool will be */
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0));
+# if TARGET_LONG_BITS == 32
+ switch (opc) {
+ case 0:
+ tcg_out_ext8u(s, COND_AL, TCG_REG_R1, data_reg);
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index);
+ break;
+ case 1:
+ tcg_out_ext16u(s, COND_AL, TCG_REG_R1, data_reg);
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index);
+ break;
+ case 2:
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R1, 0, data_reg, SHIFT_IMM_LSL(0));
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index);
+ break;
+ case 3:
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index);
+ tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */
+ if (data_reg != TCG_REG_R2) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0));
+ }
+ if (data_reg2 != TCG_REG_R3) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0));
+ }
+ break;
+ }
+# else
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0));
+ switch (opc) {
+ case 0:
+ tcg_out_ext8u(s, COND_AL, TCG_REG_R2, data_reg);
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index);
+ break;
+ case 1:
+ tcg_out_ext16u(s, COND_AL, TCG_REG_R2, data_reg);
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index);
+ break;
+ case 2:
+ if (data_reg != TCG_REG_R2) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0));
+ }
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index);
+ break;
+ case 3:
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index);
+ tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */
+ if (data_reg != TCG_REG_R2) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0));
+ }
+ if (data_reg2 != TCG_REG_R3) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0));
+ }
+ break;
+ }
+# endif
+
+ tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_st_helpers[s_bits] -
+ (tcg_target_long) s->code_ptr);
+ if (opc == 3)
+ tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R13, TCG_REG_R13, 0x10);
+
+ reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr);
+#else /* !CONFIG_SOFTMMU */
+ if (GUEST_BASE) {
+ uint32_t offset = GUEST_BASE;
+ int i;
+ int rot;
+
+ while (offset) {
+ i = ctz32(offset) & ~1;
+ rot = ((32 - i) << 7) & 0xf00;
+
+ tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R1, addr_reg,
+ ((offset >> i) & 0xff) | rot);
+ addr_reg = TCG_REG_R1;
+ offset &= ~(0xff << i);
+ }
+ }
+ switch (opc) {
+ case 0:
+ tcg_out_st8_12(s, COND_AL, data_reg, addr_reg, 0);
+ break;
+ case 1:
+ if (bswap) {
+ tcg_out_bswap16(s, COND_AL, TCG_REG_R0, data_reg);
+ tcg_out_st16_8(s, COND_AL, TCG_REG_R0, addr_reg, 0);
+ } else {
+ tcg_out_st16_8(s, COND_AL, data_reg, addr_reg, 0);
+ }
+ break;
+ case 2:
+ default:
+ if (bswap) {
+ tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg);
+ tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0);
+ } else {
+ tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0);
+ }
+ break;
+ case 3:
+ /* TODO: use block store -
+ * check that data_reg2 > data_reg or the other way */
+ if (bswap) {
+ tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2);
+ tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0);
+ tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg);
+ tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 4);
+ } else {
+ tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0);
+ tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4);
+ }
+ break;
+ }
+#endif
+}
+
+static uint8_t *tb_ret_addr;
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
+ const TCGArg *args, const int *const_args)
+{
+ int c;
+
+ switch (opc) {
+ case INDEX_op_exit_tb:
+ {
+ uint8_t *ld_ptr = s->code_ptr;
+ if (args[0] >> 8)
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0);
+ else
+ tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]);
+ tcg_out_goto(s, COND_AL, (tcg_target_ulong) tb_ret_addr);
+ if (args[0] >> 8) {
+ *ld_ptr = (uint8_t) (s->code_ptr - ld_ptr) - 8;
+ tcg_out32(s, args[0]);
+ }
+ }
+ break;
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ /* Direct jump method */
+#if defined(USE_DIRECT_JUMP)
+ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+ tcg_out_b_noaddr(s, COND_AL);
+#else
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4);
+ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+ tcg_out32(s, 0);
+#endif
+ } else {
+ /* Indirect jump method */
+#if 1
+ c = (int) (s->tb_next + args[0]) - ((int) s->code_ptr + 8);
+ if (c > 0xfff || c < -0xfff) {
+ tcg_out_movi32(s, COND_AL, TCG_REG_R0,
+ (tcg_target_long) (s->tb_next + args[0]));
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0);
+ } else
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, c);
+#else
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0);
+ tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0);
+ tcg_out32(s, (tcg_target_long) (s->tb_next + args[0]));
+#endif
+ }
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+ case INDEX_op_call:
+ if (const_args[0])
+ tcg_out_call(s, COND_AL, args[0]);
+ else
+ tcg_out_callr(s, COND_AL, args[0]);
+ break;
+ case INDEX_op_jmp:
+ if (const_args[0])
+ tcg_out_goto(s, COND_AL, args[0]);
+ else
+ tcg_out_bx(s, COND_AL, args[0]);
+ break;
+ case INDEX_op_br:
+ tcg_out_goto_label(s, COND_AL, args[0]);
+ break;
+
+ case INDEX_op_ld8u_i32:
+ tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld8s_i32:
+ tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld16u_i32:
+ tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld16s_i32:
+ tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld_i32:
+ tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st8_i32:
+ tcg_out_st8(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st16_i32:
+ tcg_out_st16(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st_i32:
+ tcg_out_st32(s, COND_AL, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_mov_i32:
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+ args[0], 0, args[1], SHIFT_IMM_LSL(0));
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi32(s, COND_AL, args[0], args[1]);
+ break;
+ case INDEX_op_add_i32:
+ c = ARITH_ADD;
+ goto gen_arith;
+ case INDEX_op_sub_i32:
+ c = ARITH_SUB;
+ goto gen_arith;
+ case INDEX_op_and_i32:
+ c = ARITH_AND;
+ goto gen_arith;
+ case INDEX_op_andc_i32:
+ c = ARITH_BIC;
+ goto gen_arith;
+ case INDEX_op_or_i32:
+ c = ARITH_ORR;
+ goto gen_arith;
+ case INDEX_op_xor_i32:
+ c = ARITH_EOR;
+ /* Fall through. */
+ gen_arith:
+ if (const_args[2]) {
+ int rot;
+ rot = encode_imm(args[2]);
+ tcg_out_dat_imm(s, COND_AL, c,
+ args[0], args[1], rotl(args[2], rot) | (rot << 7));
+ } else
+ tcg_out_dat_reg(s, COND_AL, c,
+ args[0], args[1], args[2], SHIFT_IMM_LSL(0));
+ break;
+ case INDEX_op_add2_i32:
+ tcg_out_dat_reg2(s, COND_AL, ARITH_ADD, ARITH_ADC,
+ args[0], args[1], args[2], args[3],
+ args[4], args[5], SHIFT_IMM_LSL(0));
+ break;
+ case INDEX_op_sub2_i32:
+ tcg_out_dat_reg2(s, COND_AL, ARITH_SUB, ARITH_SBC,
+ args[0], args[1], args[2], args[3],
+ args[4], args[5], SHIFT_IMM_LSL(0));
+ break;
+ case INDEX_op_neg_i32:
+ tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0);
+ break;
+ case INDEX_op_not_i32:
+ tcg_out_dat_reg(s, COND_AL,
+ ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0));
+ break;
+ case INDEX_op_mul_i32:
+ tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_mulu2_i32:
+ tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]);
+ break;
+ /* XXX: Perhaps args[2] & 0x1f is wrong */
+ case INDEX_op_shl_i32:
+ c = const_args[2] ?
+ SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]);
+ goto gen_shift32;
+ case INDEX_op_shr_i32:
+ c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) :
+ SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]);
+ goto gen_shift32;
+ case INDEX_op_sar_i32:
+ c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) :
+ SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]);
+ goto gen_shift32;
+ case INDEX_op_rotr_i32:
+ c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) :
+ SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]);
+ /* Fall through. */
+ gen_shift32:
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c);
+ break;
+
+ case INDEX_op_rotl_i32:
+ if (const_args[2]) {
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1],
+ ((0x20 - args[2]) & 0x1f) ?
+ SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) :
+ SHIFT_IMM_LSL(0));
+ } else {
+ tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_R8, args[1], 0x20);
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1],
+ SHIFT_REG_ROR(TCG_REG_R8));
+ }
+ break;
+
+ case INDEX_op_brcond_i32:
+ if (const_args[1]) {
+ int rot;
+ rot = encode_imm(args[1]);
+ tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0,
+ args[0], rotl(args[1], rot) | (rot << 7));
+ } else {
+ tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0,
+ args[0], args[1], SHIFT_IMM_LSL(0));
+ }
+ tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], args[3]);
+ break;
+ case INDEX_op_brcond2_i32:
+ /* The resulting conditions are:
+ * TCG_COND_EQ --> a0 == a2 && a1 == a3,
+ * TCG_COND_NE --> (a0 != a2 && a1 == a3) || a1 != a3,
+ * TCG_COND_LT(U) --> (a0 < a2 && a1 == a3) || a1 < a3,
+ * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3),
+ * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3),
+ * TCG_COND_GT(U) --> (a0 > a2 && a1 == a3) || a1 > a3,
+ */
+ tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0,
+ args[1], args[3], SHIFT_IMM_LSL(0));
+ tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
+ args[0], args[2], SHIFT_IMM_LSL(0));
+ tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], args[5]);
+ break;
+ case INDEX_op_setcond_i32:
+ if (const_args[2]) {
+ int rot;
+ rot = encode_imm(args[2]);
+ tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0,
+ args[1], rotl(args[2], rot) | (rot << 7));
+ } else {
+ tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0,
+ args[1], args[2], SHIFT_IMM_LSL(0));
+ }
+ tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]],
+ ARITH_MOV, args[0], 0, 1);
+ tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])],
+ ARITH_MOV, args[0], 0, 0);
+ break;
+ case INDEX_op_setcond2_i32:
+ /* See brcond2_i32 comment */
+ tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0,
+ args[2], args[4], SHIFT_IMM_LSL(0));
+ tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
+ args[1], args[3], SHIFT_IMM_LSL(0));
+ tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]],
+ ARITH_MOV, args[0], 0, 1);
+ tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])],
+ ARITH_MOV, args[0], 0, 0);
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+ case INDEX_op_bswap16_i32:
+ tcg_out_bswap16(s, COND_AL, args[0], args[1]);
+ break;
+ case INDEX_op_bswap32_i32:
+ tcg_out_bswap32(s, COND_AL, args[0], args[1]);
+ break;
+
+ case INDEX_op_ext8s_i32:
+ tcg_out_ext8s(s, COND_AL, args[0], args[1]);
+ break;
+ case INDEX_op_ext16s_i32:
+ tcg_out_ext16s(s, COND_AL, args[0], args[1]);
+ break;
+ case INDEX_op_ext16u_i32:
+ tcg_out_ext16u(s, COND_AL, args[0], args[1]);
+ break;
+
+ default:
+ tcg_abort();
+ }
+}
+
+static const TCGTargetOpDef arm_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "ri" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "r", "r" } },
+ { INDEX_op_st16_i32, { "r", "r" } },
+ { INDEX_op_st_i32, { "r", "r" } },
+
+ /* TODO: "r", "r", "ri" */
+ { INDEX_op_add_i32, { "r", "r", "rI" } },
+ { INDEX_op_sub_i32, { "r", "r", "rI" } },
+ { INDEX_op_mul_i32, { "r", "r", "r" } },
+ { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } },
+ { INDEX_op_and_i32, { "r", "r", "rI" } },
+ { INDEX_op_andc_i32, { "r", "r", "rI" } },
+ { INDEX_op_or_i32, { "r", "r", "rI" } },
+ { INDEX_op_xor_i32, { "r", "r", "rI" } },
+ { INDEX_op_neg_i32, { "r", "r" } },
+ { INDEX_op_not_i32, { "r", "r" } },
+
+ { INDEX_op_shl_i32, { "r", "r", "ri" } },
+ { INDEX_op_shr_i32, { "r", "r", "ri" } },
+ { INDEX_op_sar_i32, { "r", "r", "ri" } },
+ { INDEX_op_rotl_i32, { "r", "r", "ri" } },
+ { INDEX_op_rotr_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_brcond_i32, { "r", "rI" } },
+ { INDEX_op_setcond_i32, { "r", "r", "rI" } },
+
+ /* TODO: "r", "r", "r", "r", "ri", "ri" */
+ { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } },
+ { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } },
+ { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } },
+ { INDEX_op_setcond2_i32, { "r", "r", "r", "r", "r" } },
+
+#if TARGET_LONG_BITS == 32
+ { INDEX_op_qemu_ld8u, { "r", "l" } },
+ { INDEX_op_qemu_ld8s, { "r", "l" } },
+ { INDEX_op_qemu_ld16u, { "r", "l" } },
+ { INDEX_op_qemu_ld16s, { "r", "l" } },
+ { INDEX_op_qemu_ld32, { "r", "l" } },
+ { INDEX_op_qemu_ld64, { "L", "L", "l" } },
+
+ { INDEX_op_qemu_st8, { "s", "s" } },
+ { INDEX_op_qemu_st16, { "s", "s" } },
+ { INDEX_op_qemu_st32, { "s", "s" } },
+ { INDEX_op_qemu_st64, { "S", "S", "s" } },
+#else
+ { INDEX_op_qemu_ld8u, { "r", "l", "l" } },
+ { INDEX_op_qemu_ld8s, { "r", "l", "l" } },
+ { INDEX_op_qemu_ld16u, { "r", "l", "l" } },
+ { INDEX_op_qemu_ld16s, { "r", "l", "l" } },
+ { INDEX_op_qemu_ld32, { "r", "l", "l" } },
+ { INDEX_op_qemu_ld64, { "L", "L", "l", "l" } },
+
+ { INDEX_op_qemu_st8, { "s", "s", "s" } },
+ { INDEX_op_qemu_st16, { "s", "s", "s" } },
+ { INDEX_op_qemu_st32, { "s", "s", "s" } },
+ { INDEX_op_qemu_st64, { "S", "S", "s", "s" } },
+#endif
+
+ { INDEX_op_bswap16_i32, { "r", "r" } },
+ { INDEX_op_bswap32_i32, { "r", "r" } },
+
+ { INDEX_op_ext8s_i32, { "r", "r" } },
+ { INDEX_op_ext16s_i32, { "r", "r" } },
+ { INDEX_op_ext16u_i32, { "r", "r" } },
+
+ { -1 },
+};
+
+static void tcg_target_init(TCGContext *s)
+{
+#if !defined(CONFIG_USER_ONLY)
+ /* fail safe */
+ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry))
+ tcg_abort();
+#endif
+
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff);
+ tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+ (1 << TCG_REG_R0) |
+ (1 << TCG_REG_R1) |
+ (1 << TCG_REG_R2) |
+ (1 << TCG_REG_R3) |
+ (1 << TCG_REG_R12) |
+ (1 << TCG_REG_R14));
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R8);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC);
+
+ tcg_add_target_add_op_defs(arm_op_defs);
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ tcg_out_ld32u(s, COND_AL, arg, arg1, arg2);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ tcg_out_st32(s, COND_AL, arg, arg1, arg2);
+}
+
+static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ if (val > 0)
+ if (val < 0x100)
+ tcg_out_dat_imm(s, COND_AL, ARITH_ADD, reg, reg, val);
+ else
+ tcg_abort();
+ else if (val < 0) {
+ if (val > -0x100)
+ tcg_out_dat_imm(s, COND_AL, ARITH_SUB, reg, reg, -val);
+ else
+ tcg_abort();
+ }
+}
+
+static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg)
+{
+ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0));
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+ int ret, tcg_target_long arg)
+{
+ tcg_out_movi32(s, COND_AL, ret, arg);
+}
+
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ /* There is no need to save r7, it is used to store the address
+ of the env structure and is not modified by GCC. */
+
+ /* stmdb sp!, { r4 - r6, r8 - r11, lr } */
+ tcg_out32(s, (COND_AL << 28) | 0x092d4f70);
+
+ tcg_out_bx(s, COND_AL, TCG_REG_R0);
+ tb_ret_addr = s->code_ptr;
+
+ /* ldmia sp!, { r4 - r6, r8 - r11, pc } */
+ tcg_out32(s, (COND_AL << 28) | 0x08bd8f70);
+}
diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h
new file mode 100644
index 0000000..d8d7d94
--- /dev/null
+++ b/tcg/arm/tcg-target.h
@@ -0,0 +1,93 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_ARM 1
+
+#define TCG_TARGET_REG_BITS 32
+#undef TCG_TARGET_WORDS_BIGENDIAN
+#undef TCG_TARGET_STACK_GROWSUP
+
+enum {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_PC,
+};
+
+#define TCG_TARGET_NB_REGS 16
+
+#define TCG_CT_CONST_ARM 0x100
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R13
+#define TCG_TARGET_STACK_ALIGN 8
+#define TCG_TARGET_CALL_ALIGN_ARGS 1
+#define TCG_TARGET_CALL_STACK_OFFSET 0
+
+/* optional instructions */
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#undef TCG_TARGET_HAS_ext8u_i32 /* and r0, r1, #0xff */
+#define TCG_TARGET_HAS_ext16u_i32
+#define TCG_TARGET_HAS_bswap16_i32
+#define TCG_TARGET_HAS_bswap32_i32
+#define TCG_TARGET_HAS_not_i32
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_andc_i32
+// #define TCG_TARGET_HAS_orc_i32
+// #define TCG_TARGET_HAS_eqv_i32
+// #define TCG_TARGET_HAS_nand_i32
+// #define TCG_TARGET_HAS_nor_i32
+
+#define TCG_TARGET_HAS_GUEST_BASE
+
+enum {
+ /* Note: must be synced with dyngen-exec.h */
+ TCG_AREG0 = TCG_REG_R7,
+};
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+#if QEMU_GNUC_PREREQ(4, 1)
+ __builtin___clear_cache((char *) start, (char *) stop);
+#else
+ register unsigned long _beg __asm ("a1") = start;
+ register unsigned long _end __asm ("a2") = stop;
+ register unsigned long _flg __asm ("a3") = 0;
+ __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
+#endif
+}
diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c
new file mode 100644
index 0000000..7f4653e
--- /dev/null
+++ b/tcg/hppa/tcg-target.c
@@ -0,0 +1,1683 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "%r0", "%r1", "%rp", "%r3", "%r4", "%r5", "%r6", "%r7",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
+ "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23",
+ "%r24", "%r25", "%r26", "%dp", "%ret0", "%ret1", "%sp", "%r31",
+};
+#endif
+
+/* This is an 8 byte temp slot in the stack frame. */
+#define STACK_TEMP_OFS -16
+
+#ifdef CONFIG_USE_GUEST_BASE
+#define TCG_GUEST_BASE_REG TCG_REG_R16
+#else
+#define TCG_GUEST_BASE_REG TCG_REG_R0
+#endif
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+
+ TCG_REG_R17,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+
+ TCG_REG_R26,
+ TCG_REG_R25,
+ TCG_REG_R24,
+ TCG_REG_R23,
+
+ TCG_REG_RET0,
+ TCG_REG_RET1,
+};
+
+static const int tcg_target_call_iarg_regs[4] = {
+ TCG_REG_R26,
+ TCG_REG_R25,
+ TCG_REG_R24,
+ TCG_REG_R23,
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_RET0,
+ TCG_REG_RET1,
+};
+
+/* True iff val fits a signed field of width BITS. */
+static inline int check_fit_tl(tcg_target_long val, unsigned int bits)
+{
+ return (val << ((sizeof(tcg_target_long) * 8 - bits))
+ >> (sizeof(tcg_target_long) * 8 - bits)) == val;
+}
+
+/* True iff depi can be used to compute (reg | MASK).
+ Accept a bit pattern like:
+ 0....01....1
+ 1....10....0
+ 0..01..10..0
+ Copied from gcc sources. */
+static inline int or_mask_p(tcg_target_ulong mask)
+{
+ if (mask == 0 || mask == -1) {
+ return 0;
+ }
+ mask += mask & -mask;
+ return (mask & (mask - 1)) == 0;
+}
+
+/* True iff depi or extru can be used to compute (reg & mask).
+ Accept a bit pattern like these:
+ 0....01....1
+ 1....10....0
+ 1..10..01..1
+ Copied from gcc sources. */
+static inline int and_mask_p(tcg_target_ulong mask)
+{
+ return or_mask_p(~mask);
+}
+
+static int low_sign_ext(int val, int len)
+{
+ return (((val << 1) & ~(-1u << len)) | ((val >> (len - 1)) & 1));
+}
+
+static int reassemble_12(int as12)
+{
+ return (((as12 & 0x800) >> 11) |
+ ((as12 & 0x400) >> 8) |
+ ((as12 & 0x3ff) << 3));
+}
+
+static int reassemble_17(int as17)
+{
+ return (((as17 & 0x10000) >> 16) |
+ ((as17 & 0x0f800) << 5) |
+ ((as17 & 0x00400) >> 8) |
+ ((as17 & 0x003ff) << 3));
+}
+
+static int reassemble_21(int as21)
+{
+ return (((as21 & 0x100000) >> 20) |
+ ((as21 & 0x0ffe00) >> 8) |
+ ((as21 & 0x000180) << 7) |
+ ((as21 & 0x00007c) << 14) |
+ ((as21 & 0x000003) << 12));
+}
+
+/* ??? Bizzarely, there is no PCREL12F relocation type. I guess all
+ such relocations are simply fully handled by the assembler. */
+#define R_PARISC_PCREL12F R_PARISC_NONE
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ uint32_t *insn_ptr = (uint32_t *)code_ptr;
+ uint32_t insn = *insn_ptr;
+ tcg_target_long pcrel;
+
+ value += addend;
+ pcrel = (value - ((tcg_target_long)code_ptr + 8)) >> 2;
+
+ switch (type) {
+ case R_PARISC_PCREL12F:
+ assert(check_fit_tl(pcrel, 12));
+ /* ??? We assume all patches are forward. See tcg_out_brcond
+ re setting the NUL bit on the branch and eliding the nop. */
+ assert(pcrel >= 0);
+ insn &= ~0x1ffdu;
+ insn |= reassemble_12(pcrel);
+ break;
+ case R_PARISC_PCREL17F:
+ assert(check_fit_tl(pcrel, 17));
+ insn &= ~0x1f1ffdu;
+ insn |= reassemble_17(pcrel);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ *insn_ptr = insn;
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return 4;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch (ct_str[0]) {
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ break;
+ case 'L': /* qemu_ld/st constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R26);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R25);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R24);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R23);
+ break;
+ case 'Z':
+ ct->ct |= TCG_CT_CONST_0;
+ break;
+ case 'I':
+ ct->ct |= TCG_CT_CONST_S11;
+ break;
+ case 'J':
+ ct->ct |= TCG_CT_CONST_S5;
+ break;
+ case 'K':
+ ct->ct |= TCG_CT_CONST_MS11;
+ break;
+ case 'M':
+ ct->ct |= TCG_CT_CONST_AND;
+ break;
+ case 'O':
+ ct->ct |= TCG_CT_CONST_OR;
+ break;
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST) {
+ return 1;
+ } else if (ct & TCG_CT_CONST_0) {
+ return val == 0;
+ } else if (ct & TCG_CT_CONST_S5) {
+ return check_fit_tl(val, 5);
+ } else if (ct & TCG_CT_CONST_S11) {
+ return check_fit_tl(val, 11);
+ } else if (ct & TCG_CT_CONST_MS11) {
+ return check_fit_tl(-val, 11);
+ } else if (ct & TCG_CT_CONST_AND) {
+ return and_mask_p(val);
+ } else if (ct & TCG_CT_CONST_OR) {
+ return or_mask_p(val);
+ }
+ return 0;
+}
+
+#define INSN_OP(x) ((x) << 26)
+#define INSN_EXT3BR(x) ((x) << 13)
+#define INSN_EXT3SH(x) ((x) << 10)
+#define INSN_EXT4(x) ((x) << 6)
+#define INSN_EXT5(x) (x)
+#define INSN_EXT6(x) ((x) << 6)
+#define INSN_EXT7(x) ((x) << 6)
+#define INSN_EXT8A(x) ((x) << 6)
+#define INSN_EXT8B(x) ((x) << 5)
+#define INSN_T(x) (x)
+#define INSN_R1(x) ((x) << 16)
+#define INSN_R2(x) ((x) << 21)
+#define INSN_DEP_LEN(x) (32 - (x))
+#define INSN_SHDEP_CP(x) ((31 - (x)) << 5)
+#define INSN_SHDEP_P(x) ((x) << 5)
+#define INSN_COND(x) ((x) << 13)
+#define INSN_IM11(x) low_sign_ext(x, 11)
+#define INSN_IM14(x) low_sign_ext(x, 14)
+#define INSN_IM5(x) (low_sign_ext(x, 5) << 16)
+
+#define COND_NEVER 0
+#define COND_EQ 1
+#define COND_LT 2
+#define COND_LE 3
+#define COND_LTU 4
+#define COND_LEU 5
+#define COND_SV 6
+#define COND_OD 7
+#define COND_FALSE 8
+
+#define INSN_ADD (INSN_OP(0x02) | INSN_EXT6(0x18))
+#define INSN_ADDC (INSN_OP(0x02) | INSN_EXT6(0x1c))
+#define INSN_ADDI (INSN_OP(0x2d))
+#define INSN_ADDIL (INSN_OP(0x0a))
+#define INSN_ADDL (INSN_OP(0x02) | INSN_EXT6(0x28))
+#define INSN_AND (INSN_OP(0x02) | INSN_EXT6(0x08))
+#define INSN_ANDCM (INSN_OP(0x02) | INSN_EXT6(0x00))
+#define INSN_COMCLR (INSN_OP(0x02) | INSN_EXT6(0x22))
+#define INSN_COMICLR (INSN_OP(0x24))
+#define INSN_DEP (INSN_OP(0x35) | INSN_EXT3SH(3))
+#define INSN_DEPI (INSN_OP(0x35) | INSN_EXT3SH(7))
+#define INSN_EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7))
+#define INSN_EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6))
+#define INSN_LDIL (INSN_OP(0x08))
+#define INSN_LDO (INSN_OP(0x0d))
+#define INSN_MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2))
+#define INSN_OR (INSN_OP(0x02) | INSN_EXT6(0x09))
+#define INSN_SHD (INSN_OP(0x34) | INSN_EXT3SH(2))
+#define INSN_SUB (INSN_OP(0x02) | INSN_EXT6(0x10))
+#define INSN_SUBB (INSN_OP(0x02) | INSN_EXT6(0x14))
+#define INSN_SUBI (INSN_OP(0x25))
+#define INSN_VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5))
+#define INSN_VEXTRU (INSN_OP(0x34) | INSN_EXT3SH(4))
+#define INSN_VSHD (INSN_OP(0x34) | INSN_EXT3SH(0))
+#define INSN_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a))
+#define INSN_ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2))
+#define INSN_ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0))
+
+#define INSN_BL (INSN_OP(0x3a) | INSN_EXT3BR(0))
+#define INSN_BL_N (INSN_OP(0x3a) | INSN_EXT3BR(0) | 2)
+#define INSN_BLR (INSN_OP(0x3a) | INSN_EXT3BR(2))
+#define INSN_BV (INSN_OP(0x3a) | INSN_EXT3BR(6))
+#define INSN_BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2)
+#define INSN_BLE_SR4 (INSN_OP(0x39) | (1 << 13))
+
+#define INSN_LDB (INSN_OP(0x10))
+#define INSN_LDH (INSN_OP(0x11))
+#define INSN_LDW (INSN_OP(0x12))
+#define INSN_LDWM (INSN_OP(0x13))
+#define INSN_FLDDS (INSN_OP(0x0b) | INSN_EXT4(0) | (1 << 12))
+
+#define INSN_LDBX (INSN_OP(0x03) | INSN_EXT4(0))
+#define INSN_LDHX (INSN_OP(0x03) | INSN_EXT4(1))
+#define INSN_LDWX (INSN_OP(0x03) | INSN_EXT4(2))
+
+#define INSN_STB (INSN_OP(0x18))
+#define INSN_STH (INSN_OP(0x19))
+#define INSN_STW (INSN_OP(0x1a))
+#define INSN_STWM (INSN_OP(0x1b))
+#define INSN_FSTDS (INSN_OP(0x0b) | INSN_EXT4(8) | (1 << 12))
+
+#define INSN_COMBT (INSN_OP(0x20))
+#define INSN_COMBF (INSN_OP(0x22))
+#define INSN_COMIBT (INSN_OP(0x21))
+#define INSN_COMIBF (INSN_OP(0x23))
+
+/* supplied by libgcc */
+extern void *__canonicalize_funcptr_for_compare(void *);
+
+static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg)
+{
+ /* PA1.1 defines COPY as OR r,0,t; PA2.0 defines COPY as LDO 0(r),t
+ but hppa-dis.c is unaware of this definition */
+ if (ret != arg) {
+ tcg_out32(s, INSN_OR | INSN_T(ret) | INSN_R1(arg)
+ | INSN_R2(TCG_REG_R0));
+ }
+}
+
+static void tcg_out_movi(TCGContext *s, TCGType type,
+ int ret, tcg_target_long arg)
+{
+ if (check_fit_tl(arg, 14)) {
+ tcg_out32(s, INSN_LDO | INSN_R1(ret)
+ | INSN_R2(TCG_REG_R0) | INSN_IM14(arg));
+ } else {
+ uint32_t hi, lo;
+ hi = arg >> 11;
+ lo = arg & 0x7ff;
+
+ tcg_out32(s, INSN_LDIL | INSN_R2(ret) | reassemble_21(hi));
+ if (lo) {
+ tcg_out32(s, INSN_LDO | INSN_R1(ret)
+ | INSN_R2(ret) | INSN_IM14(lo));
+ }
+ }
+}
+
+static void tcg_out_ldst(TCGContext *s, int ret, int addr,
+ tcg_target_long offset, int op)
+{
+ if (!check_fit_tl(offset, 14)) {
+ uint32_t hi, lo, op;
+
+ hi = offset >> 11;
+ lo = offset & 0x7ff;
+
+ if (addr == TCG_REG_R0) {
+ op = INSN_LDIL | INSN_R2(TCG_REG_R1);
+ } else {
+ op = INSN_ADDIL | INSN_R2(addr);
+ }
+ tcg_out32(s, op | reassemble_21(hi));
+
+ addr = TCG_REG_R1;
+ offset = lo;
+ }
+
+ if (ret != addr || offset != 0 || op != INSN_LDO) {
+ tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | INSN_IM14(offset));
+ }
+}
+
+/* This function is required by tcg.c. */
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+ int arg1, tcg_target_long arg2)
+{
+ tcg_out_ldst(s, ret, arg1, arg2, INSN_LDW);
+}
+
+/* This function is required by tcg.c. */
+static inline void tcg_out_st(TCGContext *s, TCGType type, int ret,
+ int arg1, tcg_target_long arg2)
+{
+ tcg_out_ldst(s, ret, arg1, arg2, INSN_STW);
+}
+
+static void tcg_out_ldst_index(TCGContext *s, int data,
+ int base, int index, int op)
+{
+ tcg_out32(s, op | INSN_T(data) | INSN_R1(index) | INSN_R2(base));
+}
+
+static inline void tcg_out_addi2(TCGContext *s, int ret, int arg1,
+ tcg_target_long val)
+{
+ tcg_out_ldst(s, ret, arg1, val, INSN_LDO);
+}
+
+/* This function is required by tcg.c. */
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ tcg_out_addi2(s, reg, reg, val);
+}
+
+static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op)
+{
+ tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2));
+}
+
+static inline void tcg_out_arithi(TCGContext *s, int t, int r1,
+ tcg_target_long val, int op)
+{
+ assert(check_fit_tl(val, 11));
+ tcg_out32(s, op | INSN_R1(t) | INSN_R2(r1) | INSN_IM11(val));
+}
+
+static inline void tcg_out_nop(TCGContext *s)
+{
+ tcg_out_arith(s, TCG_REG_R0, TCG_REG_R0, TCG_REG_R0, INSN_OR);
+}
+
+static inline void tcg_out_mtctl_sar(TCGContext *s, int arg)
+{
+ tcg_out32(s, INSN_MTCTL | INSN_R2(11) | INSN_R1(arg));
+}
+
+/* Extract LEN bits at position OFS from ARG and place in RET.
+ Note that here the bit ordering is reversed from the PA-RISC
+ standard, such that the right-most bit is 0. */
+static inline void tcg_out_extr(TCGContext *s, int ret, int arg,
+ unsigned ofs, unsigned len, int sign)
+{
+ assert(ofs < 32 && len <= 32 - ofs);
+ tcg_out32(s, (sign ? INSN_EXTRS : INSN_EXTRU)
+ | INSN_R1(ret) | INSN_R2(arg)
+ | INSN_SHDEP_P(31 - ofs) | INSN_DEP_LEN(len));
+}
+
+/* Likewise with OFS interpreted little-endian. */
+static inline void tcg_out_dep(TCGContext *s, int ret, int arg,
+ unsigned ofs, unsigned len)
+{
+ assert(ofs < 32 && len <= 32 - ofs);
+ tcg_out32(s, INSN_DEP | INSN_R2(ret) | INSN_R1(arg)
+ | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len));
+}
+
+static inline void tcg_out_shd(TCGContext *s, int ret, int hi, int lo,
+ unsigned count)
+{
+ assert(count < 32);
+ tcg_out32(s, INSN_SHD | INSN_R1(hi) | INSN_R2(lo) | INSN_T(ret)
+ | INSN_SHDEP_CP(count));
+}
+
+static void tcg_out_vshd(TCGContext *s, int ret, int hi, int lo, int creg)
+{
+ tcg_out_mtctl_sar(s, creg);
+ tcg_out32(s, INSN_VSHD | INSN_T(ret) | INSN_R1(hi) | INSN_R2(lo));
+}
+
+static void tcg_out_ori(TCGContext *s, int ret, int arg, tcg_target_ulong m)
+{
+ int bs0, bs1;
+
+ /* Note that the argument is constrained to match or_mask_p. */
+ for (bs0 = 0; bs0 < 32; bs0++) {
+ if ((m & (1u << bs0)) != 0) {
+ break;
+ }
+ }
+ for (bs1 = bs0; bs1 < 32; bs1++) {
+ if ((m & (1u << bs1)) == 0) {
+ break;
+ }
+ }
+ assert(bs1 == 32 || (1ul << bs1) > m);
+
+ tcg_out_mov(s, TCG_TYPE_I32, ret, arg);
+ tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(-1)
+ | INSN_SHDEP_CP(31 - bs0) | INSN_DEP_LEN(bs1 - bs0));
+}
+
+static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m)
+{
+ int ls0, ls1, ms0;
+
+ /* Note that the argument is constrained to match and_mask_p. */
+ for (ls0 = 0; ls0 < 32; ls0++) {
+ if ((m & (1u << ls0)) == 0) {
+ break;
+ }
+ }
+ for (ls1 = ls0; ls1 < 32; ls1++) {
+ if ((m & (1u << ls1)) != 0) {
+ break;
+ }
+ }
+ for (ms0 = ls1; ms0 < 32; ms0++) {
+ if ((m & (1u << ms0)) == 0) {
+ break;
+ }
+ }
+ assert (ms0 == 32);
+
+ if (ls1 == 32) {
+ tcg_out_extr(s, ret, arg, 0, ls0, 0);
+ } else {
+ tcg_out_mov(s, TCG_TYPE_I32, ret, arg);
+ tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(0)
+ | INSN_SHDEP_CP(31 - ls0) | INSN_DEP_LEN(ls1 - ls0));
+ }
+}
+
+static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg)
+{
+ tcg_out_extr(s, ret, arg, 0, 8, 1);
+}
+
+static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg)
+{
+ tcg_out_extr(s, ret, arg, 0, 16, 1);
+}
+
+static void tcg_out_shli(TCGContext *s, int ret, int arg, int count)
+{
+ count &= 31;
+ tcg_out32(s, INSN_ZDEP | INSN_R2(ret) | INSN_R1(arg)
+ | INSN_SHDEP_CP(31 - count) | INSN_DEP_LEN(32 - count));
+}
+
+static void tcg_out_shl(TCGContext *s, int ret, int arg, int creg)
+{
+ tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI);
+ tcg_out_mtctl_sar(s, TCG_REG_R20);
+ tcg_out32(s, INSN_ZVDEP | INSN_R2(ret) | INSN_R1(arg) | INSN_DEP_LEN(32));
+}
+
+static void tcg_out_shri(TCGContext *s, int ret, int arg, int count)
+{
+ count &= 31;
+ tcg_out_extr(s, ret, arg, count, 32 - count, 0);
+}
+
+static void tcg_out_shr(TCGContext *s, int ret, int arg, int creg)
+{
+ tcg_out_vshd(s, ret, TCG_REG_R0, arg, creg);
+}
+
+static void tcg_out_sari(TCGContext *s, int ret, int arg, int count)
+{
+ count &= 31;
+ tcg_out_extr(s, ret, arg, count, 32 - count, 1);
+}
+
+static void tcg_out_sar(TCGContext *s, int ret, int arg, int creg)
+{
+ tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI);
+ tcg_out_mtctl_sar(s, TCG_REG_R20);
+ tcg_out32(s, INSN_VEXTRS | INSN_R1(ret) | INSN_R2(arg) | INSN_DEP_LEN(32));
+}
+
+static void tcg_out_rotli(TCGContext *s, int ret, int arg, int count)
+{
+ count &= 31;
+ tcg_out_shd(s, ret, arg, arg, 32 - count);
+}
+
+static void tcg_out_rotl(TCGContext *s, int ret, int arg, int creg)
+{
+ tcg_out_arithi(s, TCG_REG_R20, creg, 32, INSN_SUBI);
+ tcg_out_vshd(s, ret, arg, arg, TCG_REG_R20);
+}
+
+static void tcg_out_rotri(TCGContext *s, int ret, int arg, int count)
+{
+ count &= 31;
+ tcg_out_shd(s, ret, arg, arg, count);
+}
+
+static void tcg_out_rotr(TCGContext *s, int ret, int arg, int creg)
+{
+ tcg_out_vshd(s, ret, arg, arg, creg);
+}
+
+static void tcg_out_bswap16(TCGContext *s, int ret, int arg, int sign)
+{
+ if (ret != arg) {
+ tcg_out_mov(s, TCG_TYPE_I32, ret, arg); /* arg = xxAB */
+ }
+ tcg_out_dep(s, ret, ret, 16, 8); /* ret = xBAB */
+ tcg_out_extr(s, ret, ret, 8, 16, sign); /* ret = ..BA */
+}
+
+static void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp)
+{
+ /* arg = ABCD */
+ tcg_out_rotri(s, temp, arg, 16); /* temp = CDAB */
+ tcg_out_dep(s, temp, temp, 16, 8); /* temp = CBAB */
+ tcg_out_shd(s, ret, arg, temp, 8); /* ret = DCBA */
+}
+
+static void tcg_out_call(TCGContext *s, void *func)
+{
+ tcg_target_long val, hi, lo, disp;
+
+ val = (uint32_t)__canonicalize_funcptr_for_compare(func);
+ disp = (val - ((tcg_target_long)s->code_ptr + 8)) >> 2;
+
+ if (check_fit_tl(disp, 17)) {
+ tcg_out32(s, INSN_BL_N | INSN_R2(TCG_REG_RP) | reassemble_17(disp));
+ } else {
+ hi = val >> 11;
+ lo = val & 0x7ff;
+
+ tcg_out32(s, INSN_LDIL | INSN_R2(TCG_REG_R20) | reassemble_21(hi));
+ tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R20)
+ | reassemble_17(lo >> 2));
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_RP, TCG_REG_R31);
+ }
+}
+
+static void tcg_out_xmpyu(TCGContext *s, int retl, int reth,
+ int arg1, int arg2)
+{
+ /* Store both words into the stack for copy to the FPU. */
+ tcg_out_ldst(s, arg1, TCG_REG_SP, STACK_TEMP_OFS, INSN_STW);
+ tcg_out_ldst(s, arg2, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_STW);
+
+ /* Load both words into the FPU at the same time. We get away
+ with this because we can address the left and right half of the
+ FPU registers individually once loaded. */
+ /* fldds stack_temp(sp),fr22 */
+ tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_SP)
+ | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22));
+
+ /* xmpyu fr22r,fr22,fr22 */
+ tcg_out32(s, 0x3ad64796);
+
+ /* Store the 64-bit result back into the stack. */
+ /* fstds stack_temp(sp),fr22 */
+ tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_SP)
+ | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22));
+
+ /* Load the pieces of the result that the caller requested. */
+ if (reth) {
+ tcg_out_ldst(s, reth, TCG_REG_SP, STACK_TEMP_OFS, INSN_LDW);
+ }
+ if (retl) {
+ tcg_out_ldst(s, retl, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_LDW);
+ }
+}
+
+static void tcg_out_add2(TCGContext *s, int destl, int desth,
+ int al, int ah, int bl, int bh, int blconst)
+{
+ int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl);
+
+ if (blconst) {
+ tcg_out_arithi(s, tmp, al, bl, INSN_ADDI);
+ } else {
+ tcg_out_arith(s, tmp, al, bl, INSN_ADD);
+ }
+ tcg_out_arith(s, desth, ah, bh, INSN_ADDC);
+
+ tcg_out_mov(s, TCG_TYPE_I32, destl, tmp);
+}
+
+static void tcg_out_sub2(TCGContext *s, int destl, int desth, int al, int ah,
+ int bl, int bh, int alconst, int blconst)
+{
+ int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl);
+
+ if (alconst) {
+ if (blconst) {
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R20, bl);
+ bl = TCG_REG_R20;
+ }
+ tcg_out_arithi(s, tmp, bl, al, INSN_SUBI);
+ } else if (blconst) {
+ tcg_out_arithi(s, tmp, al, -bl, INSN_ADDI);
+ } else {
+ tcg_out_arith(s, tmp, al, bl, INSN_SUB);
+ }
+ tcg_out_arith(s, desth, ah, bh, INSN_SUBB);
+
+ tcg_out_mov(s, TCG_TYPE_I32, destl, tmp);
+}
+
+static void tcg_out_branch(TCGContext *s, int label_index, int nul)
+{
+ TCGLabel *l = &s->labels[label_index];
+ uint32_t op = nul ? INSN_BL_N : INSN_BL;
+
+ if (l->has_value) {
+ tcg_target_long val = l->u.value;
+
+ val -= (tcg_target_long)s->code_ptr + 8;
+ val >>= 2;
+ assert(check_fit_tl(val, 17));
+
+ tcg_out32(s, op | reassemble_17(val));
+ } else {
+ /* We need to keep the offset unchanged for retranslation. */
+ uint32_t old_insn = *(uint32_t *)s->code_ptr;
+
+ tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL17F, label_index, 0);
+ tcg_out32(s, op | (old_insn & 0x1f1ffdu));
+ }
+}
+
+static const uint8_t tcg_cond_to_cmp_cond[10] =
+{
+ [TCG_COND_EQ] = COND_EQ,
+ [TCG_COND_NE] = COND_EQ | COND_FALSE,
+ [TCG_COND_LT] = COND_LT,
+ [TCG_COND_GE] = COND_LT | COND_FALSE,
+ [TCG_COND_LE] = COND_LE,
+ [TCG_COND_GT] = COND_LE | COND_FALSE,
+ [TCG_COND_LTU] = COND_LTU,
+ [TCG_COND_GEU] = COND_LTU | COND_FALSE,
+ [TCG_COND_LEU] = COND_LEU,
+ [TCG_COND_GTU] = COND_LEU | COND_FALSE,
+};
+
+static void tcg_out_brcond(TCGContext *s, int cond, TCGArg c1,
+ TCGArg c2, int c2const, int label_index)
+{
+ TCGLabel *l = &s->labels[label_index];
+ int op, pacond;
+
+ /* Note that COMIB operates as if the immediate is the first
+ operand. We model brcond with the immediate in the second
+ to better match what targets are likely to give us. For
+ consistency, model COMB with reversed operands as well. */
+ pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)];
+
+ if (c2const) {
+ op = (pacond & COND_FALSE ? INSN_COMIBF : INSN_COMIBT);
+ op |= INSN_IM5(c2);
+ } else {
+ op = (pacond & COND_FALSE ? INSN_COMBF : INSN_COMBT);
+ op |= INSN_R1(c2);
+ }
+ op |= INSN_R2(c1);
+ op |= INSN_COND(pacond & 7);
+
+ if (l->has_value) {
+ tcg_target_long val = l->u.value;
+
+ val -= (tcg_target_long)s->code_ptr + 8;
+ val >>= 2;
+ assert(check_fit_tl(val, 12));
+
+ /* ??? Assume that all branches to defined labels are backward.
+ Which means that if the nul bit is set, the delay slot is
+ executed if the branch is taken, and not executed in fallthru. */
+ tcg_out32(s, op | reassemble_12(val));
+ tcg_out_nop(s);
+ } else {
+ /* We need to keep the offset unchanged for retranslation. */
+ uint32_t old_insn = *(uint32_t *)s->code_ptr;
+
+ tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL12F, label_index, 0);
+ /* ??? Assume that all branches to undefined labels are forward.
+ Which means that if the nul bit is set, the delay slot is
+ not executed if the branch is taken, which is what we want. */
+ tcg_out32(s, op | 2 | (old_insn & 0x1ffdu));
+ }
+}
+
+static void tcg_out_comclr(TCGContext *s, int cond, TCGArg ret,
+ TCGArg c1, TCGArg c2, int c2const)
+{
+ int op, pacond;
+
+ /* Note that COMICLR operates as if the immediate is the first
+ operand. We model setcond with the immediate in the second
+ to better match what targets are likely to give us. For
+ consistency, model COMCLR with reversed operands as well. */
+ pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)];
+
+ if (c2const) {
+ op = INSN_COMICLR | INSN_R2(c1) | INSN_R1(ret) | INSN_IM11(c2);
+ } else {
+ op = INSN_COMCLR | INSN_R2(c1) | INSN_R1(c2) | INSN_T(ret);
+ }
+ op |= INSN_COND(pacond & 7);
+ op |= pacond & COND_FALSE ? 1 << 12 : 0;
+
+ tcg_out32(s, op);
+}
+
+static void tcg_out_brcond2(TCGContext *s, int cond, TCGArg al, TCGArg ah,
+ TCGArg bl, int blconst, TCGArg bh, int bhconst,
+ int label_index)
+{
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ tcg_out_comclr(s, tcg_invert_cond(cond), TCG_REG_R0, al, bl, blconst);
+ tcg_out_brcond(s, cond, ah, bh, bhconst, label_index);
+ break;
+
+ default:
+ tcg_out_brcond(s, cond, ah, bh, bhconst, label_index);
+ tcg_out_comclr(s, TCG_COND_NE, TCG_REG_R0, ah, bh, bhconst);
+ tcg_out_brcond(s, tcg_unsigned_cond(cond),
+ al, bl, blconst, label_index);
+ break;
+ }
+}
+
+static void tcg_out_setcond(TCGContext *s, int cond, TCGArg ret,
+ TCGArg c1, TCGArg c2, int c2const)
+{
+ tcg_out_comclr(s, tcg_invert_cond(cond), ret, c1, c2, c2const);
+ tcg_out_movi(s, TCG_TYPE_I32, ret, 1);
+}
+
+static void tcg_out_setcond2(TCGContext *s, int cond, TCGArg ret,
+ TCGArg al, TCGArg ah, TCGArg bl, int blconst,
+ TCGArg bh, int bhconst)
+{
+ int scratch = TCG_REG_R20;
+
+ if (ret != al && ret != ah
+ && (blconst || ret != bl)
+ && (bhconst || ret != bh)) {
+ scratch = ret;
+ }
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ tcg_out_setcond(s, cond, scratch, al, bl, blconst);
+ tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst);
+ tcg_out_movi(s, TCG_TYPE_I32, scratch, cond == TCG_COND_NE);
+ break;
+
+ default:
+ tcg_out_setcond(s, tcg_unsigned_cond(cond), scratch, al, bl, blconst);
+ tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst);
+ tcg_out_movi(s, TCG_TYPE_I32, scratch, 0);
+ tcg_out_comclr(s, cond, TCG_REG_R0, ah, bh, bhconst);
+ tcg_out_movi(s, TCG_TYPE_I32, scratch, 1);
+ break;
+ }
+
+ tcg_out_mov(s, TCG_TYPE_I32, ret, scratch);
+}
+
+#if defined(CONFIG_SOFTMMU)
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+
+/* Load and compare a TLB entry, and branch if TLB miss. OFFSET is set to
+ the offset of the first ADDR_READ or ADDR_WRITE member of the appropriate
+ TLB for the memory index. The return value is the offset from ENV
+ contained in R1 afterward (to be used when loading ADDEND); if the
+ return value is 0, R1 is not used. */
+
+static int tcg_out_tlb_read(TCGContext *s, int r0, int r1, int addrlo,
+ int addrhi, int s_bits, int lab_miss, int offset)
+{
+ int ret;
+
+ /* Extracting the index into the TLB. The "normal C operation" is
+ r1 = addr_reg >> TARGET_PAGE_BITS;
+ r1 &= CPU_TLB_SIZE - 1;
+ r1 <<= CPU_TLB_ENTRY_BITS;
+ What this does is extract CPU_TLB_BITS beginning at TARGET_PAGE_BITS
+ and place them at CPU_TLB_ENTRY_BITS. We can combine the first two
+ operations with an EXTRU. Unfortunately, the current value of
+ CPU_TLB_ENTRY_BITS is > 3, so we can't merge that shift with the
+ add that follows. */
+ tcg_out_extr(s, r1, addrlo, TARGET_PAGE_BITS, CPU_TLB_BITS, 0);
+ tcg_out_shli(s, r1, r1, CPU_TLB_ENTRY_BITS);
+ tcg_out_arith(s, r1, r1, TCG_AREG0, INSN_ADDL);
+
+ /* Make sure that both the addr_{read,write} and addend can be
+ read with a 14-bit offset from the same base register. */
+ if (check_fit_tl(offset + CPU_TLB_SIZE, 14)) {
+ ret = 0;
+ } else {
+ ret = (offset + 0x400) & ~0x7ff;
+ offset = ret - offset;
+ tcg_out_addi2(s, TCG_REG_R1, r1, ret);
+ r1 = TCG_REG_R1;
+ }
+
+ /* Load the entry from the computed slot. */
+ if (TARGET_LONG_BITS == 64) {
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R23, r1, offset);
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset + 4);
+ } else {
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset);
+ }
+
+ /* Compute the value that ought to appear in the TLB for a hit, namely, the page
+ of the address. We include the low N bits of the address to catch unaligned
+ accesses and force them onto the slow path. Do this computation after having
+ issued the load from the TLB slot to give the load time to complete. */
+ tcg_out_andi(s, r0, addrlo, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+
+ /* If not equal, jump to lab_miss. */
+ if (TARGET_LONG_BITS == 64) {
+ tcg_out_brcond2(s, TCG_COND_NE, TCG_REG_R20, TCG_REG_R23,
+ r0, 0, addrhi, 0, lab_miss);
+ } else {
+ tcg_out_brcond(s, TCG_COND_NE, TCG_REG_R20, r0, 0, lab_miss);
+ }
+
+ return ret;
+}
+#endif
+
+static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo_reg, int datahi_reg,
+ int addr_reg, int addend_reg, int opc)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ const int bswap = 0;
+#else
+ const int bswap = 1;
+#endif
+
+ switch (opc) {
+ case 0:
+ tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX);
+ break;
+ case 0 | 4:
+ tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX);
+ tcg_out_ext8s(s, datalo_reg, datalo_reg);
+ break;
+ case 1:
+ tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX);
+ if (bswap) {
+ tcg_out_bswap16(s, datalo_reg, datalo_reg, 0);
+ }
+ break;
+ case 1 | 4:
+ tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX);
+ if (bswap) {
+ tcg_out_bswap16(s, datalo_reg, datalo_reg, 1);
+ } else {
+ tcg_out_ext16s(s, datalo_reg, datalo_reg);
+ }
+ break;
+ case 2:
+ tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDWX);
+ if (bswap) {
+ tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20);
+ }
+ break;
+ case 3:
+ if (bswap) {
+ int t = datahi_reg;
+ datahi_reg = datalo_reg;
+ datalo_reg = t;
+ }
+ /* We can't access the low-part with a reg+reg addressing mode,
+ so perform the addition now and use reg_ofs addressing mode. */
+ if (addend_reg != TCG_REG_R0) {
+ tcg_out_arith(s, TCG_REG_R20, addr_reg, addend_reg, INSN_ADD);
+ addr_reg = TCG_REG_R20;
+ }
+ /* Make sure not to clobber the base register. */
+ if (datahi_reg == addr_reg) {
+ tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW);
+ tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW);
+ } else {
+ tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW);
+ tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW);
+ }
+ if (bswap) {
+ tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20);
+ tcg_out_bswap32(s, datahi_reg, datahi_reg, TCG_REG_R20);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
+{
+ int datalo_reg = *args++;
+ /* Note that datahi_reg is only used for 64-bit loads. */
+ int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0);
+ int addrlo_reg = *args++;
+
+#if defined(CONFIG_SOFTMMU)
+ /* Note that addrhi_reg is only used for 64-bit guests. */
+ int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0);
+ int mem_index = *args;
+ int lab1, lab2, argreg, offset;
+
+ lab1 = gen_new_label();
+ lab2 = gen_new_label();
+
+ offset = offsetof(CPUState, tlb_table[mem_index][0].addr_read);
+ offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg,
+ opc & 3, lab1, offset);
+
+ /* TLB Hit. */
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25),
+ offsetof(CPUState, tlb_table[mem_index][0].addend) - offset);
+ tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, TCG_REG_R20, opc);
+ tcg_out_branch(s, lab2, 1);
+
+ /* TLB Miss. */
+ /* label1: */
+ tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr);
+
+ argreg = TCG_REG_R26;
+ tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg);
+ if (TARGET_LONG_BITS == 64) {
+ tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg);
+ }
+ tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index);
+
+ tcg_out_call(s, qemu_ld_helpers[opc & 3]);
+
+ switch (opc) {
+ case 0:
+ tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xff);
+ break;
+ case 0 | 4:
+ tcg_out_ext8s(s, datalo_reg, TCG_REG_RET0);
+ break;
+ case 1:
+ tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xffff);
+ break;
+ case 1 | 4:
+ tcg_out_ext16s(s, datalo_reg, TCG_REG_RET0);
+ break;
+ case 2:
+ case 2 | 4:
+ tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET0);
+ break;
+ case 3:
+ tcg_out_mov(s, TCG_TYPE_I32, datahi_reg, TCG_REG_RET0);
+ tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET1);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ /* label2: */
+ tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr);
+#else
+ tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg,
+ (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_R0), opc);
+#endif
+}
+
+static void tcg_out_qemu_st_direct(TCGContext *s, int datalo_reg, int datahi_reg,
+ int addr_reg, int opc)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ const int bswap = 0;
+#else
+ const int bswap = 1;
+#endif
+
+ switch (opc) {
+ case 0:
+ tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STB);
+ break;
+ case 1:
+ if (bswap) {
+ tcg_out_bswap16(s, TCG_REG_R20, datalo_reg, 0);
+ datalo_reg = TCG_REG_R20;
+ }
+ tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STH);
+ break;
+ case 2:
+ if (bswap) {
+ tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20);
+ datalo_reg = TCG_REG_R20;
+ }
+ tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STW);
+ break;
+ case 3:
+ if (bswap) {
+ tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20);
+ tcg_out_bswap32(s, TCG_REG_R23, datahi_reg, TCG_REG_R23);
+ datahi_reg = TCG_REG_R20;
+ datalo_reg = TCG_REG_R23;
+ }
+ tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_STW);
+ tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_STW);
+ break;
+ default:
+ tcg_abort();
+ }
+
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
+{
+ int datalo_reg = *args++;
+ /* Note that datahi_reg is only used for 64-bit loads. */
+ int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0);
+ int addrlo_reg = *args++;
+
+#if defined(CONFIG_SOFTMMU)
+ /* Note that addrhi_reg is only used for 64-bit guests. */
+ int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0);
+ int mem_index = *args;
+ int lab1, lab2, argreg, offset;
+
+ lab1 = gen_new_label();
+ lab2 = gen_new_label();
+
+ offset = offsetof(CPUState, tlb_table[mem_index][0].addr_write);
+ offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg,
+ opc, lab1, offset);
+
+ /* TLB Hit. */
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25),
+ offsetof(CPUState, tlb_table[mem_index][0].addend) - offset);
+
+ /* There are no indexed stores, so we must do this addition explitly.
+ Careful to avoid R20, which is used for the bswaps to follow. */
+ tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_REG_R20, INSN_ADDL);
+ tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, TCG_REG_R31, opc);
+ tcg_out_branch(s, lab2, 1);
+
+ /* TLB Miss. */
+ /* label1: */
+ tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr);
+
+ argreg = TCG_REG_R26;
+ tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg);
+ if (TARGET_LONG_BITS == 64) {
+ tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg);
+ }
+
+ switch(opc) {
+ case 0:
+ tcg_out_andi(s, argreg--, datalo_reg, 0xff);
+ tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index);
+ break;
+ case 1:
+ tcg_out_andi(s, argreg--, datalo_reg, 0xffff);
+ tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index);
+ break;
+ case 2:
+ tcg_out_mov(s, TCG_TYPE_I32, argreg--, datalo_reg);
+ tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index);
+ break;
+ case 3:
+ /* Because of the alignment required by the 64-bit data argument,
+ we will always use R23/R24. Also, we will always run out of
+ argument registers for storing mem_index, so that will have
+ to go on the stack. */
+ if (mem_index == 0) {
+ argreg = TCG_REG_R0;
+ } else {
+ argreg = TCG_REG_R20;
+ tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index);
+ }
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R23, datahi_reg);
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R24, datalo_reg);
+ tcg_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_SP,
+ TCG_TARGET_CALL_STACK_OFFSET - 4);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ tcg_out_call(s, qemu_st_helpers[opc]);
+
+ /* label2: */
+ tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr);
+#else
+ /* There are no indexed stores, so if GUEST_BASE is set we must do the add
+ explicitly. Careful to avoid R20, which is used for the bswaps to follow. */
+ if (GUEST_BASE != 0) {
+ tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_GUEST_BASE_REG, INSN_ADDL);
+ addrlo_reg = TCG_REG_R31;
+ }
+ tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, addrlo_reg, opc);
+#endif
+}
+
+static void tcg_out_exit_tb(TCGContext *s, TCGArg arg)
+{
+ if (!check_fit_tl(arg, 14)) {
+ uint32_t hi, lo;
+ hi = arg & ~0x7ff;
+ lo = arg & 0x7ff;
+ if (lo) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, hi);
+ tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18));
+ tcg_out_addi(s, TCG_REG_RET0, lo);
+ return;
+ }
+ arg = hi;
+ }
+ tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18));
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, arg);
+}
+
+static void tcg_out_goto_tb(TCGContext *s, TCGArg arg)
+{
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+ fprintf(stderr, "goto_tb direct\n");
+ tcg_abort();
+ } else {
+ /* indirect jump method */
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, TCG_REG_R0,
+ (tcg_target_long)(s->tb_next + arg));
+ tcg_out32(s, INSN_BV_N | INSN_R2(TCG_REG_R20));
+ }
+ s->tb_next_offset[arg] = s->code_ptr - s->code_buf;
+}
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
+ const int *const_args)
+{
+ switch (opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_exit_tb(s, args[0]);
+ break;
+ case INDEX_op_goto_tb:
+ tcg_out_goto_tb(s, args[0]);
+ break;
+
+ case INDEX_op_call:
+ if (const_args[0]) {
+ tcg_out_call(s, (void *)args[0]);
+ } else {
+ /* ??? FIXME: the value in the register in args[0] is almost
+ certainly a procedure descriptor, not a code address. We
+ probably need to use the millicode $$dyncall routine. */
+ tcg_abort();
+ }
+ break;
+
+ case INDEX_op_jmp:
+ fprintf(stderr, "unimplemented jmp\n");
+ tcg_abort();
+ break;
+
+ case INDEX_op_br:
+ tcg_out_branch(s, args[0], 1);
+ break;
+
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]);
+ break;
+
+ case INDEX_op_ld8u_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB);
+ break;
+ case INDEX_op_ld8s_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB);
+ tcg_out_ext8s(s, args[0], args[0]);
+ break;
+ case INDEX_op_ld16u_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH);
+ break;
+ case INDEX_op_ld16s_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH);
+ tcg_out_ext16s(s, args[0], args[0]);
+ break;
+ case INDEX_op_ld_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDW);
+ break;
+
+ case INDEX_op_st8_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_STB);
+ break;
+ case INDEX_op_st16_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_STH);
+ break;
+ case INDEX_op_st_i32:
+ tcg_out_ldst(s, args[0], args[1], args[2], INSN_STW);
+ break;
+
+ case INDEX_op_add_i32:
+ if (const_args[2]) {
+ tcg_out_addi2(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_arith(s, args[0], args[1], args[2], INSN_ADDL);
+ }
+ break;
+
+ case INDEX_op_sub_i32:
+ if (const_args[1]) {
+ if (const_args[2]) {
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1] - args[2]);
+ } else {
+ /* Recall that SUBI is a reversed subtract. */
+ tcg_out_arithi(s, args[0], args[2], args[1], INSN_SUBI);
+ }
+ } else if (const_args[2]) {
+ tcg_out_addi2(s, args[0], args[1], -args[2]);
+ } else {
+ tcg_out_arith(s, args[0], args[1], args[2], INSN_SUB);
+ }
+ break;
+
+ case INDEX_op_and_i32:
+ if (const_args[2]) {
+ tcg_out_andi(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_arith(s, args[0], args[1], args[2], INSN_AND);
+ }
+ break;
+
+ case INDEX_op_or_i32:
+ if (const_args[2]) {
+ tcg_out_ori(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_arith(s, args[0], args[1], args[2], INSN_OR);
+ }
+ break;
+
+ case INDEX_op_xor_i32:
+ tcg_out_arith(s, args[0], args[1], args[2], INSN_XOR);
+ break;
+
+ case INDEX_op_andc_i32:
+ if (const_args[2]) {
+ tcg_out_andi(s, args[0], args[1], ~args[2]);
+ } else {
+ tcg_out_arith(s, args[0], args[1], args[2], INSN_ANDCM);
+ }
+ break;
+
+ case INDEX_op_shl_i32:
+ if (const_args[2]) {
+ tcg_out_shli(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_shl(s, args[0], args[1], args[2]);
+ }
+ break;
+
+ case INDEX_op_shr_i32:
+ if (const_args[2]) {
+ tcg_out_shri(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_shr(s, args[0], args[1], args[2]);
+ }
+ break;
+
+ case INDEX_op_sar_i32:
+ if (const_args[2]) {
+ tcg_out_sari(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_sar(s, args[0], args[1], args[2]);
+ }
+ break;
+
+ case INDEX_op_rotl_i32:
+ if (const_args[2]) {
+ tcg_out_rotli(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_rotl(s, args[0], args[1], args[2]);
+ }
+ break;
+
+ case INDEX_op_rotr_i32:
+ if (const_args[2]) {
+ tcg_out_rotri(s, args[0], args[1], args[2]);
+ } else {
+ tcg_out_rotr(s, args[0], args[1], args[2]);
+ }
+ break;
+
+ case INDEX_op_mul_i32:
+ tcg_out_xmpyu(s, args[0], TCG_REG_R0, args[1], args[2]);
+ break;
+ case INDEX_op_mulu2_i32:
+ tcg_out_xmpyu(s, args[0], args[1], args[2], args[3]);
+ break;
+
+ case INDEX_op_bswap16_i32:
+ tcg_out_bswap16(s, args[0], args[1], 0);
+ break;
+ case INDEX_op_bswap32_i32:
+ tcg_out_bswap32(s, args[0], args[1], TCG_REG_R20);
+ break;
+
+ case INDEX_op_not_i32:
+ tcg_out_arithi(s, args[0], args[1], -1, INSN_SUBI);
+ break;
+ case INDEX_op_ext8s_i32:
+ tcg_out_ext8s(s, args[0], args[1]);
+ break;
+ case INDEX_op_ext16s_i32:
+ tcg_out_ext16s(s, args[0], args[1]);
+ break;
+
+ case INDEX_op_brcond_i32:
+ tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]);
+ break;
+ case INDEX_op_brcond2_i32:
+ tcg_out_brcond2(s, args[4], args[0], args[1],
+ args[2], const_args[2],
+ args[3], const_args[3], args[5]);
+ break;
+
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond(s, args[3], args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_setcond2_i32:
+ tcg_out_setcond2(s, args[5], args[0], args[1], args[2],
+ args[3], const_args[3], args[4], const_args[4]);
+ break;
+
+ case INDEX_op_add2_i32:
+ tcg_out_add2(s, args[0], args[1], args[2], args[3],
+ args[4], args[5], const_args[4]);
+ break;
+
+ case INDEX_op_sub2_i32:
+ tcg_out_sub2(s, args[0], args[1], args[2], args[3],
+ args[4], args[5], const_args[2], const_args[4]);
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+ default:
+ fprintf(stderr, "unknown opcode 0x%x\n", opc);
+ tcg_abort();
+ }
+}
+
+static const TCGTargetOpDef hppa_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "r" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "rZ", "r" } },
+ { INDEX_op_st16_i32, { "rZ", "r" } },
+ { INDEX_op_st_i32, { "rZ", "r" } },
+
+ { INDEX_op_add_i32, { "r", "rZ", "ri" } },
+ { INDEX_op_sub_i32, { "r", "rI", "ri" } },
+ { INDEX_op_and_i32, { "r", "rZ", "rM" } },
+ { INDEX_op_or_i32, { "r", "rZ", "rO" } },
+ { INDEX_op_xor_i32, { "r", "rZ", "rZ" } },
+ /* Note that the second argument will be inverted, which means
+ we want a constant whose inversion matches M, and that O = ~M.
+ See the implementation of and_mask_p. */
+ { INDEX_op_andc_i32, { "r", "rZ", "rO" } },
+
+ { INDEX_op_mul_i32, { "r", "r", "r" } },
+ { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } },
+
+ { INDEX_op_shl_i32, { "r", "r", "ri" } },
+ { INDEX_op_shr_i32, { "r", "r", "ri" } },
+ { INDEX_op_sar_i32, { "r", "r", "ri" } },
+ { INDEX_op_rotl_i32, { "r", "r", "ri" } },
+ { INDEX_op_rotr_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_bswap16_i32, { "r", "r" } },
+ { INDEX_op_bswap32_i32, { "r", "r" } },
+ { INDEX_op_not_i32, { "r", "r" } },
+
+ { INDEX_op_ext8s_i32, { "r", "r" } },
+ { INDEX_op_ext16s_i32, { "r", "r" } },
+
+ { INDEX_op_brcond_i32, { "rZ", "rJ" } },
+ { INDEX_op_brcond2_i32, { "rZ", "rZ", "rJ", "rJ" } },
+
+ { INDEX_op_setcond_i32, { "r", "rZ", "rI" } },
+ { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rI", "rI" } },
+
+ { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } },
+ { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } },
+
+#if TARGET_LONG_BITS == 32
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "L" } },
+
+ { INDEX_op_qemu_st8, { "LZ", "L" } },
+ { INDEX_op_qemu_st16, { "LZ", "L" } },
+ { INDEX_op_qemu_st32, { "LZ", "L" } },
+ { INDEX_op_qemu_st64, { "LZ", "LZ", "L" } },
+#else
+ { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } },
+
+ { INDEX_op_qemu_st8, { "LZ", "L", "L" } },
+ { INDEX_op_qemu_st16, { "LZ", "L", "L" } },
+ { INDEX_op_qemu_st32, { "LZ", "L", "L" } },
+ { INDEX_op_qemu_st64, { "LZ", "LZ", "L", "L" } },
+#endif
+ { -1 },
+};
+
+static int tcg_target_callee_save_regs[] = {
+ /* R2, the return address register, is saved specially
+ in the caller's frame. */
+ /* R3, the frame pointer, is not currently modified. */
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ /* R17 is the global env, so no need to save. */
+ TCG_REG_R18
+};
+
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ int frame_size, i;
+
+ /* Allocate space for the fixed frame marker. */
+ frame_size = -TCG_TARGET_CALL_STACK_OFFSET;
+ frame_size += TCG_TARGET_STATIC_CALL_ARGS_SIZE;
+
+ /* Allocate space for the saved registers. */
+ frame_size += ARRAY_SIZE(tcg_target_callee_save_regs) * 4;
+
+ /* Align the allocated space. */
+ frame_size = ((frame_size + TCG_TARGET_STACK_ALIGN - 1)
+ & -TCG_TARGET_STACK_ALIGN);
+
+ /* The return address is stored in the caller's frame. */
+ tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -20);
+
+ /* Allocate stack frame, saving the first register at the same time. */
+ tcg_out_ldst(s, tcg_target_callee_save_regs[0],
+ TCG_REG_SP, frame_size, INSN_STWM);
+
+ /* Save all callee saved registers. */
+ for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
+ tcg_out_st(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i],
+ TCG_REG_SP, -frame_size + i * 4);
+ }
+
+#ifdef CONFIG_USE_GUEST_BASE
+ if (GUEST_BASE != 0) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE);
+ tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG);
+ }
+#endif
+
+ /* Jump to TB, and adjust R18 to be the return address. */
+ tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R26));
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R18, TCG_REG_R31);
+
+ /* Restore callee saved registers. */
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -frame_size - 20);
+ for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
+ tcg_out_ld(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i],
+ TCG_REG_SP, -frame_size + i * 4);
+ }
+
+ /* Deallocate stack frame and return. */
+ tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_RP));
+ tcg_out_ldst(s, tcg_target_callee_save_regs[0],
+ TCG_REG_SP, -frame_size, INSN_LDWM);
+}
+
+static void tcg_target_init(TCGContext *s)
+{
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+
+ tcg_regset_clear(tcg_target_call_clobber_regs);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET0);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET1);
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* hardwired to zero */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* addil target */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_RP); /* link register */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* frame pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R18); /* return pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R19); /* clobbered w/o pic */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R20); /* reserved */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_DP); /* data pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */
+
+ tcg_add_target_add_op_defs(hppa_op_defs);
+}
diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h
new file mode 100644
index 0000000..a5cc440
--- /dev/null
+++ b/tcg/hppa/tcg-target.h
@@ -0,0 +1,119 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define TCG_TARGET_HPPA 1
+
+#if defined(_PA_RISC1_1)
+#define TCG_TARGET_REG_BITS 32
+#else
+#error unsupported
+#endif
+
+#define TCG_TARGET_WORDS_BIGENDIAN
+
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_RP,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_DP,
+ TCG_REG_RET0,
+ TCG_REG_RET1,
+ TCG_REG_SP,
+ TCG_REG_R31,
+};
+
+#define TCG_CT_CONST_0 0x0100
+#define TCG_CT_CONST_S5 0x0200
+#define TCG_CT_CONST_S11 0x0400
+#define TCG_CT_CONST_MS11 0x0800
+#define TCG_CT_CONST_AND 0x1000
+#define TCG_CT_CONST_OR 0x2000
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_SP
+#define TCG_TARGET_STACK_ALIGN 64
+#define TCG_TARGET_CALL_STACK_OFFSET -48
+#define TCG_TARGET_STATIC_CALL_ARGS_SIZE 8*4
+#define TCG_TARGET_CALL_ALIGN_ARGS 1
+#define TCG_TARGET_STACK_GROWSUP
+
+/* optional instructions */
+// #define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_bswap16_i32
+#define TCG_TARGET_HAS_bswap32_i32
+#define TCG_TARGET_HAS_not_i32
+#define TCG_TARGET_HAS_andc_i32
+// #define TCG_TARGET_HAS_orc_i32
+
+/* optional instructions automatically implemented */
+#undef TCG_TARGET_HAS_neg_i32 /* sub rd, 0, rs */
+#undef TCG_TARGET_HAS_ext8u_i32 /* and rd, rs, 0xff */
+#undef TCG_TARGET_HAS_ext16u_i32 /* and rd, rs, 0xffff */
+
+#define TCG_TARGET_HAS_GUEST_BASE
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_R17
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ start &= ~31;
+ while (start <= stop) {
+ asm volatile ("fdc 0(%0)\n\t"
+ "sync\n\t"
+ "fic 0(%%sr4, %0)\n\t"
+ "sync"
+ : : "r"(start) : "memory");
+ start += 32;
+ }
+}
diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
new file mode 100644
index 0000000..bb19a95
--- /dev/null
+++ b/tcg/i386/tcg-target.c
@@ -0,0 +1,1982 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+#if TCG_TARGET_REG_BITS == 64
+ "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
+#else
+ "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
+#endif
+};
+#endif
+
+static const int tcg_target_reg_alloc_order[] = {
+#if TCG_TARGET_REG_BITS == 64
+ TCG_REG_RBP,
+ TCG_REG_RBX,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R9,
+ TCG_REG_R8,
+ TCG_REG_RCX,
+ TCG_REG_RDX,
+ TCG_REG_RSI,
+ TCG_REG_RDI,
+ TCG_REG_RAX,
+#else
+ TCG_REG_EBX,
+ TCG_REG_ESI,
+ TCG_REG_EDI,
+ TCG_REG_EBP,
+ TCG_REG_ECX,
+ TCG_REG_EDX,
+ TCG_REG_EAX,
+#endif
+};
+
+static const int tcg_target_call_iarg_regs[] = {
+#if TCG_TARGET_REG_BITS == 64
+ TCG_REG_RDI,
+ TCG_REG_RSI,
+ TCG_REG_RDX,
+ TCG_REG_RCX,
+ TCG_REG_R8,
+ TCG_REG_R9,
+#else
+ TCG_REG_EAX,
+ TCG_REG_EDX,
+ TCG_REG_ECX
+#endif
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_EAX,
+ TCG_REG_EDX
+};
+
+static uint8_t *tb_ret_addr;
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ value += addend;
+ switch(type) {
+ case R_386_PC32:
+ value -= (uintptr_t)code_ptr;
+ if (value != (int32_t)value) {
+ tcg_abort();
+ }
+ *(uint32_t *)code_ptr = value;
+ break;
+ case R_386_PC8:
+ value -= (uintptr_t)code_ptr;
+ if (value != (int8_t)value) {
+ tcg_abort();
+ }
+ *(uint8_t *)code_ptr = value;
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ if (TCG_TARGET_REG_BITS == 64) {
+ return 6;
+ }
+
+ flags &= TCG_CALL_TYPE_MASK;
+ switch(flags) {
+ case TCG_CALL_TYPE_STD:
+ return 0;
+ case TCG_CALL_TYPE_REGPARM_1:
+ case TCG_CALL_TYPE_REGPARM_2:
+ case TCG_CALL_TYPE_REGPARM:
+ return flags - TCG_CALL_TYPE_REGPARM_1 + 1;
+ default:
+ tcg_abort();
+ }
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch(ct_str[0]) {
+ case 'a':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_EAX);
+ break;
+ case 'b':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_EBX);
+ break;
+ case 'c':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_ECX);
+ break;
+ case 'd':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_EDX);
+ break;
+ case 'S':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_ESI);
+ break;
+ case 'D':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_EDI);
+ break;
+ case 'q':
+ ct->ct |= TCG_CT_REG;
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_regset_set32(ct->u.regs, 0, 0xffff);
+ } else {
+ tcg_regset_set32(ct->u.regs, 0, 0xf);
+ }
+ break;
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_regset_set32(ct->u.regs, 0, 0xffff);
+ } else {
+ tcg_regset_set32(ct->u.regs, 0, 0xff);
+ }
+ break;
+
+ /* qemu_ld/st address constraint */
+ case 'L':
+ ct->ct |= TCG_CT_REG;
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_regset_set32(ct->u.regs, 0, 0xffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_RSI);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_RDI);
+ } else {
+ tcg_regset_set32(ct->u.regs, 0, 0xff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX);
+ }
+ break;
+
+ case 'e':
+ ct->ct |= TCG_CT_CONST_S32;
+ break;
+ case 'Z':
+ ct->ct |= TCG_CT_CONST_U32;
+ break;
+
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST) {
+ return 1;
+ }
+ if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) {
+ return 1;
+ }
+ if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) {
+ return 1;
+ }
+ return 0;
+}
+
+#if TCG_TARGET_REG_BITS == 64
+# define LOWREGMASK(x) ((x) & 7)
+#else
+# define LOWREGMASK(x) (x)
+#endif
+
+#define P_EXT 0x100 /* 0x0f opcode prefix */
+#define P_DATA16 0x200 /* 0x66 opcode prefix */
+#if TCG_TARGET_REG_BITS == 64
+# define P_ADDR32 0x400 /* 0x67 opcode prefix */
+# define P_REXW 0x800 /* Set REX.W = 1 */
+# define P_REXB_R 0x1000 /* REG field as byte register */
+# define P_REXB_RM 0x2000 /* R/M field as byte register */
+#else
+# define P_ADDR32 0
+# define P_REXW 0
+# define P_REXB_R 0
+# define P_REXB_RM 0
+#endif
+
+#define OPC_ARITH_EvIz (0x81)
+#define OPC_ARITH_EvIb (0x83)
+#define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */
+#define OPC_ADD_GvEv (OPC_ARITH_GvEv | (ARITH_ADD << 3))
+#define OPC_BSWAP (0xc8 | P_EXT)
+#define OPC_CALL_Jz (0xe8)
+#define OPC_CMP_GvEv (OPC_ARITH_GvEv | (ARITH_CMP << 3))
+#define OPC_DEC_r32 (0x48)
+#define OPC_IMUL_GvEv (0xaf | P_EXT)
+#define OPC_IMUL_GvEvIb (0x6b)
+#define OPC_IMUL_GvEvIz (0x69)
+#define OPC_INC_r32 (0x40)
+#define OPC_JCC_long (0x80 | P_EXT) /* ... plus condition code */
+#define OPC_JCC_short (0x70) /* ... plus condition code */
+#define OPC_JMP_long (0xe9)
+#define OPC_JMP_short (0xeb)
+#define OPC_LEA (0x8d)
+#define OPC_MOVB_EvGv (0x88) /* stores, more or less */
+#define OPC_MOVL_EvGv (0x89) /* stores, more or less */
+#define OPC_MOVL_GvEv (0x8b) /* loads, more or less */
+#define OPC_MOVL_EvIz (0xc7)
+#define OPC_MOVL_Iv (0xb8)
+#define OPC_MOVSBL (0xbe | P_EXT)
+#define OPC_MOVSWL (0xbf | P_EXT)
+#define OPC_MOVSLQ (0x63 | P_REXW)
+#define OPC_MOVZBL (0xb6 | P_EXT)
+#define OPC_MOVZWL (0xb7 | P_EXT)
+#define OPC_POP_r32 (0x58)
+#define OPC_PUSH_r32 (0x50)
+#define OPC_PUSH_Iv (0x68)
+#define OPC_PUSH_Ib (0x6a)
+#define OPC_RET (0xc3)
+#define OPC_SETCC (0x90 | P_EXT | P_REXB_RM) /* ... plus cc */
+#define OPC_SHIFT_1 (0xd1)
+#define OPC_SHIFT_Ib (0xc1)
+#define OPC_SHIFT_cl (0xd3)
+#define OPC_TESTL (0x85)
+#define OPC_XCHG_ax_r32 (0x90)
+
+#define OPC_GRP3_Ev (0xf7)
+#define OPC_GRP5 (0xff)
+
+/* Group 1 opcode extensions for 0x80-0x83.
+ These are also used as modifiers for OPC_ARITH. */
+#define ARITH_ADD 0
+#define ARITH_OR 1
+#define ARITH_ADC 2
+#define ARITH_SBB 3
+#define ARITH_AND 4
+#define ARITH_SUB 5
+#define ARITH_XOR 6
+#define ARITH_CMP 7
+
+/* Group 2 opcode extensions for 0xc0, 0xc1, 0xd0-0xd3. */
+#define SHIFT_ROL 0
+#define SHIFT_ROR 1
+#define SHIFT_SHL 4
+#define SHIFT_SHR 5
+#define SHIFT_SAR 7
+
+/* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */
+#define EXT3_NOT 2
+#define EXT3_NEG 3
+#define EXT3_MUL 4
+#define EXT3_IMUL 5
+#define EXT3_DIV 6
+#define EXT3_IDIV 7
+
+/* Group 5 opcode extensions for 0xff. To be used with OPC_GRP5. */
+#define EXT5_INC_Ev 0
+#define EXT5_DEC_Ev 1
+#define EXT5_CALLN_Ev 2
+#define EXT5_JMPN_Ev 4
+
+/* Condition codes to be added to OPC_JCC_{long,short}. */
+#define JCC_JMP (-1)
+#define JCC_JO 0x0
+#define JCC_JNO 0x1
+#define JCC_JB 0x2
+#define JCC_JAE 0x3
+#define JCC_JE 0x4
+#define JCC_JNE 0x5
+#define JCC_JBE 0x6
+#define JCC_JA 0x7
+#define JCC_JS 0x8
+#define JCC_JNS 0x9
+#define JCC_JP 0xa
+#define JCC_JNP 0xb
+#define JCC_JL 0xc
+#define JCC_JGE 0xd
+#define JCC_JLE 0xe
+#define JCC_JG 0xf
+
+static const uint8_t tcg_cond_to_jcc[10] = {
+ [TCG_COND_EQ] = JCC_JE,
+ [TCG_COND_NE] = JCC_JNE,
+ [TCG_COND_LT] = JCC_JL,
+ [TCG_COND_GE] = JCC_JGE,
+ [TCG_COND_LE] = JCC_JLE,
+ [TCG_COND_GT] = JCC_JG,
+ [TCG_COND_LTU] = JCC_JB,
+ [TCG_COND_GEU] = JCC_JAE,
+ [TCG_COND_LEU] = JCC_JBE,
+ [TCG_COND_GTU] = JCC_JA,
+};
+
+#if TCG_TARGET_REG_BITS == 64
+static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x)
+{
+ int rex;
+
+ if (opc & P_DATA16) {
+ /* We should never be asking for both 16 and 64-bit operation. */
+ assert((opc & P_REXW) == 0);
+ tcg_out8(s, 0x66);
+ }
+ if (opc & P_ADDR32) {
+ tcg_out8(s, 0x67);
+ }
+
+ rex = 0;
+ rex |= (opc & P_REXW) >> 8; /* REX.W */
+ rex |= (r & 8) >> 1; /* REX.R */
+ rex |= (x & 8) >> 2; /* REX.X */
+ rex |= (rm & 8) >> 3; /* REX.B */
+
+ /* P_REXB_{R,RM} indicates that the given register is the low byte.
+ For %[abcd]l we need no REX prefix, but for %{si,di,bp,sp}l we do,
+ as otherwise the encoding indicates %[abcd]h. Note that the values
+ that are ORed in merely indicate that the REX byte must be present;
+ those bits get discarded in output. */
+ rex |= opc & (r >= 4 ? P_REXB_R : 0);
+ rex |= opc & (rm >= 4 ? P_REXB_RM : 0);
+
+ if (rex) {
+ tcg_out8(s, (uint8_t)(rex | 0x40));
+ }
+
+ if (opc & P_EXT) {
+ tcg_out8(s, 0x0f);
+ }
+ tcg_out8(s, opc);
+}
+#else
+static void tcg_out_opc(TCGContext *s, int opc)
+{
+ if (opc & P_DATA16) {
+ tcg_out8(s, 0x66);
+ }
+ if (opc & P_EXT) {
+ tcg_out8(s, 0x0f);
+ }
+ tcg_out8(s, opc);
+}
+/* Discard the register arguments to tcg_out_opc early, so as not to penalize
+ the 32-bit compilation paths. This method works with all versions of gcc,
+ whereas relying on optimization may not be able to exclude them. */
+#define tcg_out_opc(s, opc, r, rm, x) (tcg_out_opc)(s, opc)
+#endif
+
+static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm)
+{
+ tcg_out_opc(s, opc, r, rm, 0);
+ tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm));
+}
+
+/* Output an opcode with a full "rm + (index<<shift) + offset" address mode.
+ We handle either RM and INDEX missing with a negative value. In 64-bit
+ mode for absolute addresses, ~RM is the size of the immediate operand
+ that will follow the instruction. */
+
+static void tcg_out_modrm_sib_offset(TCGContext *s, int opc, int r, int rm,
+ int index, int shift,
+ tcg_target_long offset)
+{
+ int mod, len;
+
+ if (index < 0 && rm < 0) {
+ if (TCG_TARGET_REG_BITS == 64) {
+ /* Try for a rip-relative addressing mode. This has replaced
+ the 32-bit-mode absolute addressing encoding. */
+ tcg_target_long pc = (tcg_target_long)s->code_ptr + 5 + ~rm;
+ tcg_target_long disp = offset - pc;
+ if (disp == (int32_t)disp) {
+ tcg_out_opc(s, opc, r, 0, 0);
+ tcg_out8(s, (LOWREGMASK(r) << 3) | 5);
+ tcg_out32(s, disp);
+ return;
+ }
+
+ /* Try for an absolute address encoding. This requires the
+ use of the MODRM+SIB encoding and is therefore larger than
+ rip-relative addressing. */
+ if (offset == (int32_t)offset) {
+ tcg_out_opc(s, opc, r, 0, 0);
+ tcg_out8(s, (LOWREGMASK(r) << 3) | 4);
+ tcg_out8(s, (4 << 3) | 5);
+ tcg_out32(s, offset);
+ return;
+ }
+
+ /* ??? The memory isn't directly addressable. */
+ tcg_abort();
+ } else {
+ /* Absolute address. */
+ tcg_out_opc(s, opc, r, 0, 0);
+ tcg_out8(s, (r << 3) | 5);
+ tcg_out32(s, offset);
+ return;
+ }
+ }
+
+ /* Find the length of the immediate addend. Note that the encoding
+ that would be used for (%ebp) indicates absolute addressing. */
+ if (rm < 0) {
+ mod = 0, len = 4, rm = 5;
+ } else if (offset == 0 && LOWREGMASK(rm) != TCG_REG_EBP) {
+ mod = 0, len = 0;
+ } else if (offset == (int8_t)offset) {
+ mod = 0x40, len = 1;
+ } else {
+ mod = 0x80, len = 4;
+ }
+
+ /* Use a single byte MODRM format if possible. Note that the encoding
+ that would be used for %esp is the escape to the two byte form. */
+ if (index < 0 && LOWREGMASK(rm) != TCG_REG_ESP) {
+ /* Single byte MODRM format. */
+ tcg_out_opc(s, opc, r, rm, 0);
+ tcg_out8(s, mod | (LOWREGMASK(r) << 3) | LOWREGMASK(rm));
+ } else {
+ /* Two byte MODRM+SIB format. */
+
+ /* Note that the encoding that would place %esp into the index
+ field indicates no index register. In 64-bit mode, the REX.X
+ bit counts, so %r12 can be used as the index. */
+ if (index < 0) {
+ index = 4;
+ } else {
+ assert(index != TCG_REG_ESP);
+ }
+
+ tcg_out_opc(s, opc, r, rm, index);
+ tcg_out8(s, mod | (LOWREGMASK(r) << 3) | 4);
+ tcg_out8(s, (shift << 6) | (LOWREGMASK(index) << 3) | LOWREGMASK(rm));
+ }
+
+ if (len == 1) {
+ tcg_out8(s, offset);
+ } else if (len == 4) {
+ tcg_out32(s, offset);
+ }
+}
+
+/* A simplification of the above with no index or shift. */
+static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r,
+ int rm, tcg_target_long offset)
+{
+ tcg_out_modrm_sib_offset(s, opc, r, rm, -1, 0, offset);
+}
+
+/* Generate dest op= src. Uses the same ARITH_* codes as tgen_arithi. */
+static inline void tgen_arithr(TCGContext *s, int subop, int dest, int src)
+{
+ /* Propagate an opcode prefix, such as P_REXW. */
+ int ext = subop & ~0x7;
+ subop &= 0x7;
+
+ tcg_out_modrm(s, OPC_ARITH_GvEv + (subop << 3) + ext, dest, src);
+}
+
+static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg)
+{
+ if (arg != ret) {
+ int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0);
+ tcg_out_modrm(s, opc, ret, arg);
+ }
+}
+
+static void tcg_out_movi(TCGContext *s, TCGType type,
+ int ret, tcg_target_long arg)
+{
+ if (arg == 0) {
+ tgen_arithr(s, ARITH_XOR, ret, ret);
+ return;
+ } else if (arg == (uint32_t)arg || type == TCG_TYPE_I32) {
+ tcg_out_opc(s, OPC_MOVL_Iv + LOWREGMASK(ret), 0, ret, 0);
+ tcg_out32(s, arg);
+ } else if (arg == (int32_t)arg) {
+ tcg_out_modrm(s, OPC_MOVL_EvIz + P_REXW, 0, ret);
+ tcg_out32(s, arg);
+ } else {
+ tcg_out_opc(s, OPC_MOVL_Iv + P_REXW + LOWREGMASK(ret), 0, ret, 0);
+ tcg_out32(s, arg);
+ tcg_out32(s, arg >> 31 >> 1);
+ }
+}
+
+static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val)
+{
+ if (val == (int8_t)val) {
+ tcg_out_opc(s, OPC_PUSH_Ib, 0, 0, 0);
+ tcg_out8(s, val);
+ } else if (val == (int32_t)val) {
+ tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0);
+ tcg_out32(s, val);
+ } else {
+ tcg_abort();
+ }
+}
+
+static inline void tcg_out_push(TCGContext *s, int reg)
+{
+ tcg_out_opc(s, OPC_PUSH_r32 + LOWREGMASK(reg), 0, reg, 0);
+}
+
+static inline void tcg_out_pop(TCGContext *s, int reg)
+{
+ tcg_out_opc(s, OPC_POP_r32 + LOWREGMASK(reg), 0, reg, 0);
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+ int arg1, tcg_target_long arg2)
+{
+ int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0);
+ tcg_out_modrm_offset(s, opc, ret, arg1, arg2);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ int opc = OPC_MOVL_EvGv + (type == TCG_TYPE_I64 ? P_REXW : 0);
+ tcg_out_modrm_offset(s, opc, arg, arg1, arg2);
+}
+
+static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count)
+{
+ /* Propagate an opcode prefix, such as P_DATA16. */
+ int ext = subopc & ~0x7;
+ subopc &= 0x7;
+
+ if (count == 1) {
+ tcg_out_modrm(s, OPC_SHIFT_1 + ext, subopc, reg);
+ } else {
+ tcg_out_modrm(s, OPC_SHIFT_Ib + ext, subopc, reg);
+ tcg_out8(s, count);
+ }
+}
+
+static inline void tcg_out_bswap32(TCGContext *s, int reg)
+{
+ tcg_out_opc(s, OPC_BSWAP + LOWREGMASK(reg), 0, reg, 0);
+}
+
+static inline void tcg_out_rolw_8(TCGContext *s, int reg)
+{
+ tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8);
+}
+
+static inline void tcg_out_ext8u(TCGContext *s, int dest, int src)
+{
+ /* movzbl */
+ assert(src < 4 || TCG_TARGET_REG_BITS == 64);
+ tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src);
+}
+
+static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw)
+{
+ /* movsbl */
+ assert(src < 4 || TCG_TARGET_REG_BITS == 64);
+ tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src);
+}
+
+static inline void tcg_out_ext16u(TCGContext *s, int dest, int src)
+{
+ /* movzwl */
+ tcg_out_modrm(s, OPC_MOVZWL, dest, src);
+}
+
+static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw)
+{
+ /* movsw[lq] */
+ tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src);
+}
+
+static inline void tcg_out_ext32u(TCGContext *s, int dest, int src)
+{
+ /* 32-bit mov zero extends. */
+ tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src);
+}
+
+static inline void tcg_out_ext32s(TCGContext *s, int dest, int src)
+{
+ tcg_out_modrm(s, OPC_MOVSLQ, dest, src);
+}
+
+static inline void tcg_out_bswap64(TCGContext *s, int reg)
+{
+ tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0);
+}
+
+static void tgen_arithi(TCGContext *s, int c, int r0,
+ tcg_target_long val, int cf)
+{
+ int rexw = 0;
+
+ if (TCG_TARGET_REG_BITS == 64) {
+ rexw = c & -8;
+ c &= 7;
+ }
+
+ /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce
+ partial flags update stalls on Pentium4 and are not recommended
+ by current Intel optimization manuals. */
+ if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) {
+ int is_inc = (c == ARITH_ADD) ^ (val < 0);
+ if (TCG_TARGET_REG_BITS == 64) {
+ /* The single-byte increment encodings are re-tasked as the
+ REX prefixes. Use the MODRM encoding. */
+ tcg_out_modrm(s, OPC_GRP5 + rexw,
+ (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0);
+ } else {
+ tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0);
+ }
+ return;
+ }
+
+ if (c == ARITH_AND) {
+ if (TCG_TARGET_REG_BITS == 64) {
+ if (val == 0xffffffffu) {
+ tcg_out_ext32u(s, r0, r0);
+ return;
+ }
+ if (val == (uint32_t)val) {
+ /* AND with no high bits set can use a 32-bit operation. */
+ rexw = 0;
+ }
+ }
+ if (val == 0xffu && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) {
+ tcg_out_ext8u(s, r0, r0);
+ return;
+ }
+ if (val == 0xffffu) {
+ tcg_out_ext16u(s, r0, r0);
+ return;
+ }
+ }
+
+ if (val == (int8_t)val) {
+ tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0);
+ tcg_out8(s, val);
+ return;
+ }
+ if (rexw == 0 || val == (int32_t)val) {
+ tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0);
+ tcg_out32(s, val);
+ return;
+ }
+
+ tcg_abort();
+}
+
+static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ if (val != 0) {
+ tgen_arithi(s, ARITH_ADD + P_REXW, reg, val, 0);
+ }
+}
+
+/* Use SMALL != 0 to force a short forward branch. */
+static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small)
+{
+ int32_t val, val1;
+ TCGLabel *l = &s->labels[label_index];
+
+ if (l->has_value) {
+ val = l->u.value - (tcg_target_long)s->code_ptr;
+ val1 = val - 2;
+ if ((int8_t)val1 == val1) {
+ if (opc == -1) {
+ tcg_out8(s, OPC_JMP_short);
+ } else {
+ tcg_out8(s, OPC_JCC_short + opc);
+ }
+ tcg_out8(s, val1);
+ } else {
+ if (small) {
+ tcg_abort();
+ }
+ if (opc == -1) {
+ tcg_out8(s, OPC_JMP_long);
+ tcg_out32(s, val - 5);
+ } else {
+ tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0);
+ tcg_out32(s, val - 6);
+ }
+ }
+ } else if (small) {
+ if (opc == -1) {
+ tcg_out8(s, OPC_JMP_short);
+ } else {
+ tcg_out8(s, OPC_JCC_short + opc);
+ }
+ tcg_out_reloc(s, s->code_ptr, R_386_PC8, label_index, -1);
+ s->code_ptr += 1;
+ } else {
+ if (opc == -1) {
+ tcg_out8(s, OPC_JMP_long);
+ } else {
+ tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0);
+ }
+ tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4);
+ s->code_ptr += 4;
+ }
+}
+
+static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2,
+ int const_arg2, int rexw)
+{
+ if (const_arg2) {
+ if (arg2 == 0) {
+ /* test r, r */
+ tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1);
+ } else {
+ tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0);
+ }
+ } else {
+ tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2);
+ }
+}
+
+static void tcg_out_brcond32(TCGContext *s, TCGCond cond,
+ TCGArg arg1, TCGArg arg2, int const_arg2,
+ int label_index, int small)
+{
+ tcg_out_cmp(s, arg1, arg2, const_arg2, 0);
+ tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small);
+}
+
+#if TCG_TARGET_REG_BITS == 64
+static void tcg_out_brcond64(TCGContext *s, TCGCond cond,
+ TCGArg arg1, TCGArg arg2, int const_arg2,
+ int label_index, int small)
+{
+ tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW);
+ tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small);
+}
+#else
+/* XXX: we implement it at the target level to avoid having to
+ handle cross basic blocks temporaries */
+static void tcg_out_brcond2(TCGContext *s, const TCGArg *args,
+ const int *const_args, int small)
+{
+ int label_next;
+ label_next = gen_new_label();
+ switch(args[4]) {
+ case TCG_COND_EQ:
+ tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2],
+ label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3],
+ args[5], small);
+ break;
+ case TCG_COND_NE:
+ tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2],
+ args[5], small);
+ tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3],
+ args[5], small);
+ break;
+ case TCG_COND_LT:
+ tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_LE:
+ tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_GT:
+ tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_GE:
+ tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_LTU:
+ tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_LEU:
+ tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_GTU:
+ tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ case TCG_COND_GEU:
+ tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3],
+ args[5], small);
+ tcg_out_jxx(s, JCC_JNE, label_next, 1);
+ tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2],
+ args[5], small);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr);
+}
+#endif
+
+static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest,
+ TCGArg arg1, TCGArg arg2, int const_arg2)
+{
+ tcg_out_cmp(s, arg1, arg2, const_arg2, 0);
+ tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest);
+ tcg_out_ext8u(s, dest, dest);
+}
+
+#if TCG_TARGET_REG_BITS == 64
+static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest,
+ TCGArg arg1, TCGArg arg2, int const_arg2)
+{
+ tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW);
+ tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest);
+ tcg_out_ext8u(s, dest, dest);
+}
+#else
+static void tcg_out_setcond2(TCGContext *s, const TCGArg *args,
+ const int *const_args)
+{
+ TCGArg new_args[6];
+ int label_true, label_over;
+
+ memcpy(new_args, args+1, 5*sizeof(TCGArg));
+
+ if (args[0] == args[1] || args[0] == args[2]
+ || (!const_args[3] && args[0] == args[3])
+ || (!const_args[4] && args[0] == args[4])) {
+ /* When the destination overlaps with one of the argument
+ registers, don't do anything tricky. */
+ label_true = gen_new_label();
+ label_over = gen_new_label();
+
+ new_args[5] = label_true;
+ tcg_out_brcond2(s, new_args, const_args+1, 1);
+
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], 0);
+ tcg_out_jxx(s, JCC_JMP, label_over, 1);
+ tcg_out_label(s, label_true, (tcg_target_long)s->code_ptr);
+
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], 1);
+ tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr);
+ } else {
+ /* When the destination does not overlap one of the arguments,
+ clear the destination first, jump if cond false, and emit an
+ increment in the true case. This results in smaller code. */
+
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], 0);
+
+ label_over = gen_new_label();
+ new_args[4] = tcg_invert_cond(new_args[4]);
+ new_args[5] = label_over;
+ tcg_out_brcond2(s, new_args, const_args+1, 1);
+
+ tgen_arithi(s, ARITH_ADD, args[0], 1, 0);
+ tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr);
+ }
+}
+#endif
+
+static void tcg_out_branch(TCGContext *s, int call, tcg_target_long dest)
+{
+ tcg_target_long disp = dest - (tcg_target_long)s->code_ptr - 5;
+
+ if (disp == (int32_t)disp) {
+ tcg_out_opc(s, call ? OPC_CALL_Jz : OPC_JMP_long, 0, 0, 0);
+ tcg_out32(s, disp);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, dest);
+ tcg_out_modrm(s, OPC_GRP5,
+ call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev, TCG_REG_R10);
+ }
+}
+
+static inline void tcg_out_calli(TCGContext *s, tcg_target_long dest)
+{
+ tcg_out_branch(s, 1, dest);
+}
+
+static void tcg_out_jmp(TCGContext *s, tcg_target_long dest)
+{
+ tcg_out_branch(s, 0, dest);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+
+/* Perform the TLB load and compare.
+
+ Inputs:
+ ADDRLO_IDX contains the index into ARGS of the low part of the
+ address; the high part of the address is at ADDR_LOW_IDX+1.
+
+ MEM_INDEX and S_BITS are the memory context and log2 size of the load.
+
+ WHICH is the offset into the CPUTLBEntry structure of the slot to read.
+ This should be offsetof addr_read or addr_write.
+
+ Outputs:
+ LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses)
+ positions of the displacements of forward jumps to the TLB miss case.
+
+ First argument register is loaded with the low part of the address.
+ In the TLB hit case, it has been adjusted as indicated by the TLB
+ and so is a host address. In the TLB miss case, it continues to
+ hold a guest address.
+
+ Second argument register is clobbered. */
+
+static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx,
+ int mem_index, int s_bits,
+ const TCGArg *args,
+ uint8_t **label_ptr, int which)
+{
+ const int addrlo = args[addrlo_idx];
+ const int r0 = tcg_target_call_iarg_regs[0];
+ const int r1 = tcg_target_call_iarg_regs[1];
+ TCGType type = TCG_TYPE_I32;
+ int rexw = 0;
+
+ if (TCG_TARGET_REG_BITS == 64 && TARGET_LONG_BITS == 64) {
+ type = TCG_TYPE_I64;
+ rexw = P_REXW;
+ }
+
+ tcg_out_mov(s, type, r1, addrlo);
+ tcg_out_mov(s, type, r0, addrlo);
+
+ tcg_out_shifti(s, SHIFT_SHR + rexw, r1,
+ TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+
+ tgen_arithi(s, ARITH_AND + rexw, r0,
+ TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0);
+ tgen_arithi(s, ARITH_AND + rexw, r1,
+ (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0);
+
+ tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r1, TCG_AREG0, r1, 0,
+ offsetof(CPUState, tlb_table[mem_index][0])
+ + which);
+
+ /* cmp 0(r1), r0 */
+ tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r0, r1, 0);
+
+ tcg_out_mov(s, type, r0, addrlo);
+
+ /* jne label1 */
+ tcg_out8(s, OPC_JCC_short + JCC_JNE);
+ label_ptr[0] = s->code_ptr;
+ s->code_ptr++;
+
+ if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) {
+ /* cmp 4(r1), addrhi */
+ tcg_out_modrm_offset(s, OPC_CMP_GvEv, args[addrlo_idx+1], r1, 4);
+
+ /* jne label1 */
+ tcg_out8(s, OPC_JCC_short + JCC_JNE);
+ label_ptr[1] = s->code_ptr;
+ s->code_ptr++;
+ }
+
+ /* TLB Hit. */
+
+ /* add addend(r1), r0 */
+ tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r0, r1,
+ offsetof(CPUTLBEntry, addend) - which);
+}
+#endif
+
+static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
+ int base, tcg_target_long ofs, int sizeop)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ const int bswap = 1;
+#else
+ const int bswap = 0;
+#endif
+ switch (sizeop) {
+ case 0:
+ tcg_out_modrm_offset(s, OPC_MOVZBL, datalo, base, ofs);
+ break;
+ case 0 | 4:
+ tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW, datalo, base, ofs);
+ break;
+ case 1:
+ tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs);
+ if (bswap) {
+ tcg_out_rolw_8(s, datalo);
+ }
+ break;
+ case 1 | 4:
+ if (bswap) {
+ tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs);
+ tcg_out_rolw_8(s, datalo);
+ tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo);
+ } else {
+ tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW, datalo, base, ofs);
+ }
+ break;
+ case 2:
+ tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ if (bswap) {
+ tcg_out_bswap32(s, datalo);
+ }
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ case 2 | 4:
+ if (bswap) {
+ tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_bswap32(s, datalo);
+ tcg_out_ext32s(s, datalo, datalo);
+ } else {
+ tcg_out_modrm_offset(s, OPC_MOVSLQ, datalo, base, ofs);
+ }
+ break;
+#endif
+ case 3:
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_out_ld(s, TCG_TYPE_I64, datalo, base, ofs);
+ if (bswap) {
+ tcg_out_bswap64(s, datalo);
+ }
+ } else {
+ if (bswap) {
+ int t = datalo;
+ datalo = datahi;
+ datahi = t;
+ }
+ if (base != datalo) {
+ tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4);
+ } else {
+ tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4);
+ tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs);
+ }
+ if (bswap) {
+ tcg_out_bswap32(s, datalo);
+ tcg_out_bswap32(s, datahi);
+ }
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and
+ EAX. It will be useful once fixed registers globals are less
+ common. */
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+ int opc)
+{
+ int data_reg, data_reg2 = 0;
+ int addrlo_idx;
+#if defined(CONFIG_SOFTMMU)
+ int mem_index, s_bits, arg_idx;
+ uint8_t *label_ptr[3];
+#endif
+
+ data_reg = args[0];
+ addrlo_idx = 1;
+ if (TCG_TARGET_REG_BITS == 32 && opc == 3) {
+ data_reg2 = args[1];
+ addrlo_idx = 2;
+ }
+
+#if defined(CONFIG_SOFTMMU)
+ mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)];
+ s_bits = opc & 3;
+
+ tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args,
+ label_ptr, offsetof(CPUTLBEntry, addr_read));
+
+ /* TLB Hit. */
+ tcg_out_qemu_ld_direct(s, data_reg, data_reg2,
+ tcg_target_call_iarg_regs[0], 0, opc);
+
+ /* jmp label2 */
+ tcg_out8(s, OPC_JMP_short);
+ label_ptr[2] = s->code_ptr;
+ s->code_ptr++;
+
+ /* TLB Miss. */
+
+ /* label1: */
+ *label_ptr[0] = s->code_ptr - label_ptr[0] - 1;
+ if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) {
+ *label_ptr[1] = s->code_ptr - label_ptr[1] - 1;
+ }
+
+ /* XXX: move that code at the end of the TB */
+ /* The first argument is already loaded with addrlo. */
+ arg_idx = 1;
+ if (TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 64) {
+ tcg_out_mov(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx++],
+ args[addrlo_idx + 1]);
+ }
+ tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx],
+ mem_index);
+ tcg_out_calli(s, (tcg_target_long)qemu_ld_helpers[s_bits]);
+
+ switch(opc) {
+ case 0 | 4:
+ tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW);
+ break;
+ case 1 | 4:
+ tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW);
+ break;
+ case 0:
+ tcg_out_ext8u(s, data_reg, TCG_REG_EAX);
+ break;
+ case 1:
+ tcg_out_ext16u(s, data_reg, TCG_REG_EAX);
+ break;
+ case 2:
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX);
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ case 2 | 4:
+ tcg_out_ext32s(s, data_reg, TCG_REG_EAX);
+ break;
+#endif
+ case 3:
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX);
+ } else if (data_reg == TCG_REG_EDX) {
+ /* xchg %edx, %eax */
+ tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0);
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EAX);
+ } else {
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX);
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EDX);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+
+ /* label2: */
+ *label_ptr[2] = s->code_ptr - label_ptr[2] - 1;
+#else
+ {
+ int32_t offset = GUEST_BASE;
+ int base = args[addrlo_idx];
+
+ if (TCG_TARGET_REG_BITS == 64) {
+ /* ??? We assume all operations have left us with register
+ contents that are zero extended. So far this appears to
+ be true. If we want to enforce this, we can either do
+ an explicit zero-extension here, or (if GUEST_BASE == 0)
+ use the ADDR32 prefix. For now, do nothing. */
+
+ if (offset != GUEST_BASE) {
+ tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE);
+ tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base);
+ base = TCG_REG_RDI, offset = 0;
+ }
+ }
+
+ tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, opc);
+ }
+#endif
+}
+
+static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
+ int base, tcg_target_long ofs, int sizeop)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ const int bswap = 1;
+#else
+ const int bswap = 0;
+#endif
+ /* ??? Ideally we wouldn't need a scratch register. For user-only,
+ we could perform the bswap twice to restore the original value
+ instead of moving to the scratch. But as it is, the L constraint
+ means that the second argument reg is definitely free here. */
+ int scratch = tcg_target_call_iarg_regs[1];
+
+ switch (sizeop) {
+ case 0:
+ tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R, datalo, base, ofs);
+ break;
+ case 1:
+ if (bswap) {
+ tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo);
+ tcg_out_rolw_8(s, scratch);
+ datalo = scratch;
+ }
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16, datalo, base, ofs);
+ break;
+ case 2:
+ if (bswap) {
+ tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo);
+ tcg_out_bswap32(s, scratch);
+ datalo = scratch;
+ }
+ tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs);
+ break;
+ case 3:
+ if (TCG_TARGET_REG_BITS == 64) {
+ if (bswap) {
+ tcg_out_mov(s, TCG_TYPE_I64, scratch, datalo);
+ tcg_out_bswap64(s, scratch);
+ datalo = scratch;
+ }
+ tcg_out_st(s, TCG_TYPE_I64, datalo, base, ofs);
+ } else if (bswap) {
+ tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi);
+ tcg_out_bswap32(s, scratch);
+ tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs);
+ tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo);
+ tcg_out_bswap32(s, scratch);
+ tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs + 4);
+ } else {
+ tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs);
+ tcg_out_st(s, TCG_TYPE_I32, datahi, base, ofs + 4);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+ int opc)
+{
+ int data_reg, data_reg2 = 0;
+ int addrlo_idx;
+#if defined(CONFIG_SOFTMMU)
+ int mem_index, s_bits;
+ int stack_adjust;
+ uint8_t *label_ptr[3];
+#endif
+
+ data_reg = args[0];
+ addrlo_idx = 1;
+ if (TCG_TARGET_REG_BITS == 32 && opc == 3) {
+ data_reg2 = args[1];
+ addrlo_idx = 2;
+ }
+
+#if defined(CONFIG_SOFTMMU)
+ mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)];
+ s_bits = opc;
+
+ tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args,
+ label_ptr, offsetof(CPUTLBEntry, addr_write));
+
+ /* TLB Hit. */
+ tcg_out_qemu_st_direct(s, data_reg, data_reg2,
+ tcg_target_call_iarg_regs[0], 0, opc);
+
+ /* jmp label2 */
+ tcg_out8(s, OPC_JMP_short);
+ label_ptr[2] = s->code_ptr;
+ s->code_ptr++;
+
+ /* TLB Miss. */
+
+ /* label1: */
+ *label_ptr[0] = s->code_ptr - label_ptr[0] - 1;
+ if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) {
+ *label_ptr[1] = s->code_ptr - label_ptr[1] - 1;
+ }
+
+ /* XXX: move that code at the end of the TB */
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_out_mov(s, (opc == 3 ? TCG_TYPE_I64 : TCG_TYPE_I32),
+ TCG_REG_RSI, data_reg);
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RDX, mem_index);
+ stack_adjust = 0;
+ } else if (TARGET_LONG_BITS == 32) {
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, data_reg);
+ if (opc == 3) {
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg2);
+ tcg_out_pushi(s, mem_index);
+ stack_adjust = 4;
+ } else {
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index);
+ stack_adjust = 0;
+ }
+ } else {
+ if (opc == 3) {
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]);
+ tcg_out_pushi(s, mem_index);
+ tcg_out_push(s, data_reg2);
+ tcg_out_push(s, data_reg);
+ stack_adjust = 12;
+ } else {
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]);
+ switch(opc) {
+ case 0:
+ tcg_out_ext8u(s, TCG_REG_ECX, data_reg);
+ break;
+ case 1:
+ tcg_out_ext16u(s, TCG_REG_ECX, data_reg);
+ break;
+ case 2:
+ tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg);
+ break;
+ }
+ tcg_out_pushi(s, mem_index);
+ stack_adjust = 4;
+ }
+ }
+
+ tcg_out_calli(s, (tcg_target_long)qemu_st_helpers[s_bits]);
+
+ if (stack_adjust == (TCG_TARGET_REG_BITS / 8)) {
+ /* Pop and discard. This is 2 bytes smaller than the add. */
+ tcg_out_pop(s, TCG_REG_ECX);
+ } else if (stack_adjust != 0) {
+ tcg_out_addi(s, TCG_REG_ESP, stack_adjust);
+ }
+
+ /* label2: */
+ *label_ptr[2] = s->code_ptr - label_ptr[2] - 1;
+#else
+ {
+ int32_t offset = GUEST_BASE;
+ int base = args[addrlo_idx];
+
+ if (TCG_TARGET_REG_BITS == 64) {
+ /* ??? We assume all operations have left us with register
+ contents that are zero extended. So far this appears to
+ be true. If we want to enforce this, we can either do
+ an explicit zero-extension here, or (if GUEST_BASE == 0)
+ use the ADDR32 prefix. For now, do nothing. */
+
+ if (offset != GUEST_BASE) {
+ tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE);
+ tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base);
+ base = TCG_REG_RDI, offset = 0;
+ }
+ }
+
+ tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, opc);
+ }
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
+ const TCGArg *args, const int *const_args)
+{
+ int c, rexw = 0;
+
+#if TCG_TARGET_REG_BITS == 64
+# define OP_32_64(x) \
+ case glue(glue(INDEX_op_, x), _i64): \
+ rexw = P_REXW; /* FALLTHRU */ \
+ case glue(glue(INDEX_op_, x), _i32)
+#else
+# define OP_32_64(x) \
+ case glue(glue(INDEX_op_, x), _i32)
+#endif
+
+ switch(opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, args[0]);
+ tcg_out_jmp(s, (tcg_target_long) tb_ret_addr);
+ break;
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+ tcg_out8(s, OPC_JMP_long); /* jmp im */
+ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+ tcg_out32(s, 0);
+ } else {
+ /* indirect jump method */
+ tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1,
+ (tcg_target_long)(s->tb_next + args[0]));
+ }
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+ case INDEX_op_call:
+ if (const_args[0]) {
+ tcg_out_calli(s, args[0]);
+ } else {
+ /* call *reg */
+ tcg_out_modrm(s, OPC_GRP5, EXT5_CALLN_Ev, args[0]);
+ }
+ break;
+ case INDEX_op_jmp:
+ if (const_args[0]) {
+ tcg_out_jmp(s, args[0]);
+ } else {
+ /* jmp *reg */
+ tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, args[0]);
+ }
+ break;
+ case INDEX_op_br:
+ tcg_out_jxx(s, JCC_JMP, args[0], 0);
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ OP_32_64(ld8u):
+ /* Note that we can ignore REXW for the zero-extend to 64-bit. */
+ tcg_out_modrm_offset(s, OPC_MOVZBL, args[0], args[1], args[2]);
+ break;
+ OP_32_64(ld8s):
+ tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, args[0], args[1], args[2]);
+ break;
+ OP_32_64(ld16u):
+ /* Note that we can ignore REXW for the zero-extend to 64-bit. */
+ tcg_out_modrm_offset(s, OPC_MOVZWL, args[0], args[1], args[2]);
+ break;
+ OP_32_64(ld16s):
+ tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, args[0], args[1], args[2]);
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_ld32u_i64:
+#endif
+ case INDEX_op_ld_i32:
+ tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]);
+ break;
+
+ OP_32_64(st8):
+ tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R,
+ args[0], args[1], args[2]);
+ break;
+ OP_32_64(st16):
+ tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16,
+ args[0], args[1], args[2]);
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_st32_i64:
+#endif
+ case INDEX_op_st_i32:
+ tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]);
+ break;
+
+ OP_32_64(add):
+ /* For 3-operand addition, use LEA. */
+ if (args[0] != args[1]) {
+ TCGArg a0 = args[0], a1 = args[1], a2 = args[2], c3 = 0;
+
+ if (const_args[2]) {
+ c3 = a2, a2 = -1;
+ } else if (a0 == a2) {
+ /* Watch out for dest = src + dest, since we've removed
+ the matching constraint on the add. */
+ tgen_arithr(s, ARITH_ADD + rexw, a0, a1);
+ break;
+ }
+
+ tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3);
+ break;
+ }
+ c = ARITH_ADD;
+ goto gen_arith;
+ OP_32_64(sub):
+ c = ARITH_SUB;
+ goto gen_arith;
+ OP_32_64(and):
+ c = ARITH_AND;
+ goto gen_arith;
+ OP_32_64(or):
+ c = ARITH_OR;
+ goto gen_arith;
+ OP_32_64(xor):
+ c = ARITH_XOR;
+ goto gen_arith;
+ gen_arith:
+ if (const_args[2]) {
+ tgen_arithi(s, c + rexw, args[0], args[2], 0);
+ } else {
+ tgen_arithr(s, c + rexw, args[0], args[2]);
+ }
+ break;
+
+ OP_32_64(mul):
+ if (const_args[2]) {
+ int32_t val;
+ val = args[2];
+ if (val == (int8_t)val) {
+ tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, args[0], args[0]);
+ tcg_out8(s, val);
+ } else {
+ tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, args[0], args[0]);
+ tcg_out32(s, val);
+ }
+ } else {
+ tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, args[0], args[2]);
+ }
+ break;
+
+ OP_32_64(div2):
+ tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]);
+ break;
+ OP_32_64(divu2):
+ tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]);
+ break;
+
+ OP_32_64(shl):
+ c = SHIFT_SHL;
+ goto gen_shift;
+ OP_32_64(shr):
+ c = SHIFT_SHR;
+ goto gen_shift;
+ OP_32_64(sar):
+ c = SHIFT_SAR;
+ goto gen_shift;
+ OP_32_64(rotl):
+ c = SHIFT_ROL;
+ goto gen_shift;
+ OP_32_64(rotr):
+ c = SHIFT_ROR;
+ goto gen_shift;
+ gen_shift:
+ if (const_args[2]) {
+ tcg_out_shifti(s, c + rexw, args[0], args[2]);
+ } else {
+ tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, args[0]);
+ }
+ break;
+
+ case INDEX_op_brcond_i32:
+ tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1],
+ args[3], 0);
+ break;
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond32(s, args[3], args[0], args[1],
+ args[2], const_args[2]);
+ break;
+
+ OP_32_64(bswap16):
+ tcg_out_rolw_8(s, args[0]);
+ break;
+ OP_32_64(bswap32):
+ tcg_out_bswap32(s, args[0]);
+ break;
+
+ OP_32_64(neg):
+ tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, args[0]);
+ break;
+ OP_32_64(not):
+ tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, args[0]);
+ break;
+
+ OP_32_64(ext8s):
+ tcg_out_ext8s(s, args[0], args[1], rexw);
+ break;
+ OP_32_64(ext16s):
+ tcg_out_ext16s(s, args[0], args[1], rexw);
+ break;
+ OP_32_64(ext8u):
+ tcg_out_ext8u(s, args[0], args[1]);
+ break;
+ OP_32_64(ext16u):
+ tcg_out_ext16u(s, args[0], args[1]);
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_qemu_ld32u:
+#endif
+ case INDEX_op_qemu_ld32:
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_brcond2_i32:
+ tcg_out_brcond2(s, args, const_args, 0);
+ break;
+ case INDEX_op_setcond2_i32:
+ tcg_out_setcond2(s, args, const_args);
+ break;
+ case INDEX_op_mulu2_i32:
+ tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_MUL, args[3]);
+ break;
+ case INDEX_op_add2_i32:
+ if (const_args[4]) {
+ tgen_arithi(s, ARITH_ADD, args[0], args[4], 1);
+ } else {
+ tgen_arithr(s, ARITH_ADD, args[0], args[4]);
+ }
+ if (const_args[5]) {
+ tgen_arithi(s, ARITH_ADC, args[1], args[5], 1);
+ } else {
+ tgen_arithr(s, ARITH_ADC, args[1], args[5]);
+ }
+ break;
+ case INDEX_op_sub2_i32:
+ if (const_args[4]) {
+ tgen_arithi(s, ARITH_SUB, args[0], args[4], 1);
+ } else {
+ tgen_arithr(s, ARITH_SUB, args[0], args[4]);
+ }
+ if (const_args[5]) {
+ tgen_arithi(s, ARITH_SBB, args[1], args[5], 1);
+ } else {
+ tgen_arithr(s, ARITH_SBB, args[1], args[5]);
+ }
+ break;
+#else /* TCG_TARGET_REG_BITS == 64 */
+ case INDEX_op_movi_i64:
+ tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ld32s_i64:
+ tcg_out_modrm_offset(s, OPC_MOVSLQ, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld_i64:
+ tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st_i64:
+ tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_qemu_ld32s:
+ tcg_out_qemu_ld(s, args, 2 | 4);
+ break;
+
+ case INDEX_op_brcond_i64:
+ tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1],
+ args[3], 0);
+ break;
+ case INDEX_op_setcond_i64:
+ tcg_out_setcond64(s, args[3], args[0], args[1],
+ args[2], const_args[2]);
+ break;
+
+ case INDEX_op_bswap64_i64:
+ tcg_out_bswap64(s, args[0]);
+ break;
+ case INDEX_op_ext32u_i64:
+ tcg_out_ext32u(s, args[0], args[1]);
+ break;
+ case INDEX_op_ext32s_i64:
+ tcg_out_ext32s(s, args[0], args[1]);
+ break;
+#endif
+
+ default:
+ tcg_abort();
+ }
+
+#undef OP_32_64
+}
+
+static const TCGTargetOpDef x86_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "ri" } },
+ { INDEX_op_br, { } },
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "q", "r" } },
+ { INDEX_op_st16_i32, { "r", "r" } },
+ { INDEX_op_st_i32, { "r", "r" } },
+
+ { INDEX_op_add_i32, { "r", "r", "ri" } },
+ { INDEX_op_sub_i32, { "r", "0", "ri" } },
+ { INDEX_op_mul_i32, { "r", "0", "ri" } },
+ { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } },
+ { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } },
+ { INDEX_op_and_i32, { "r", "0", "ri" } },
+ { INDEX_op_or_i32, { "r", "0", "ri" } },
+ { INDEX_op_xor_i32, { "r", "0", "ri" } },
+
+ { INDEX_op_shl_i32, { "r", "0", "ci" } },
+ { INDEX_op_shr_i32, { "r", "0", "ci" } },
+ { INDEX_op_sar_i32, { "r", "0", "ci" } },
+ { INDEX_op_rotl_i32, { "r", "0", "ci" } },
+ { INDEX_op_rotr_i32, { "r", "0", "ci" } },
+
+ { INDEX_op_brcond_i32, { "r", "ri" } },
+
+ { INDEX_op_bswap16_i32, { "r", "0" } },
+ { INDEX_op_bswap32_i32, { "r", "0" } },
+
+ { INDEX_op_neg_i32, { "r", "0" } },
+
+ { INDEX_op_not_i32, { "r", "0" } },
+
+ { INDEX_op_ext8s_i32, { "r", "q" } },
+ { INDEX_op_ext16s_i32, { "r", "r" } },
+ { INDEX_op_ext8u_i32, { "r", "q" } },
+ { INDEX_op_ext16u_i32, { "r", "r" } },
+
+ { INDEX_op_setcond_i32, { "q", "r", "ri" } },
+
+#if TCG_TARGET_REG_BITS == 32
+ { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } },
+ { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } },
+ { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } },
+ { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } },
+ { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } },
+#else
+ { INDEX_op_mov_i64, { "r", "r" } },
+ { INDEX_op_movi_i64, { "r" } },
+ { INDEX_op_ld8u_i64, { "r", "r" } },
+ { INDEX_op_ld8s_i64, { "r", "r" } },
+ { INDEX_op_ld16u_i64, { "r", "r" } },
+ { INDEX_op_ld16s_i64, { "r", "r" } },
+ { INDEX_op_ld32u_i64, { "r", "r" } },
+ { INDEX_op_ld32s_i64, { "r", "r" } },
+ { INDEX_op_ld_i64, { "r", "r" } },
+ { INDEX_op_st8_i64, { "r", "r" } },
+ { INDEX_op_st16_i64, { "r", "r" } },
+ { INDEX_op_st32_i64, { "r", "r" } },
+ { INDEX_op_st_i64, { "r", "r" } },
+
+ { INDEX_op_add_i64, { "r", "0", "re" } },
+ { INDEX_op_mul_i64, { "r", "0", "re" } },
+ { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } },
+ { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } },
+ { INDEX_op_sub_i64, { "r", "0", "re" } },
+ { INDEX_op_and_i64, { "r", "0", "reZ" } },
+ { INDEX_op_or_i64, { "r", "0", "re" } },
+ { INDEX_op_xor_i64, { "r", "0", "re" } },
+
+ { INDEX_op_shl_i64, { "r", "0", "ci" } },
+ { INDEX_op_shr_i64, { "r", "0", "ci" } },
+ { INDEX_op_sar_i64, { "r", "0", "ci" } },
+ { INDEX_op_rotl_i64, { "r", "0", "ci" } },
+ { INDEX_op_rotr_i64, { "r", "0", "ci" } },
+
+ { INDEX_op_brcond_i64, { "r", "re" } },
+ { INDEX_op_setcond_i64, { "r", "r", "re" } },
+
+ { INDEX_op_bswap16_i64, { "r", "0" } },
+ { INDEX_op_bswap32_i64, { "r", "0" } },
+ { INDEX_op_bswap64_i64, { "r", "0" } },
+ { INDEX_op_neg_i64, { "r", "0" } },
+ { INDEX_op_not_i64, { "r", "0" } },
+
+ { INDEX_op_ext8s_i64, { "r", "r" } },
+ { INDEX_op_ext16s_i64, { "r", "r" } },
+ { INDEX_op_ext32s_i64, { "r", "r" } },
+ { INDEX_op_ext8u_i64, { "r", "r" } },
+ { INDEX_op_ext16u_i64, { "r", "r" } },
+ { INDEX_op_ext32u_i64, { "r", "r" } },
+#endif
+
+#if TCG_TARGET_REG_BITS == 64
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+ { INDEX_op_qemu_ld32u, { "r", "L" } },
+ { INDEX_op_qemu_ld32s, { "r", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "L" } },
+
+ { INDEX_op_qemu_st8, { "L", "L" } },
+ { INDEX_op_qemu_st16, { "L", "L" } },
+ { INDEX_op_qemu_st32, { "L", "L" } },
+ { INDEX_op_qemu_st64, { "L", "L" } },
+#elif TARGET_LONG_BITS <= TCG_TARGET_REG_BITS
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "L" } },
+
+ { INDEX_op_qemu_st8, { "cb", "L" } },
+ { INDEX_op_qemu_st16, { "L", "L" } },
+ { INDEX_op_qemu_st32, { "L", "L" } },
+ { INDEX_op_qemu_st64, { "L", "L", "L" } },
+#else
+ { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } },
+
+ { INDEX_op_qemu_st8, { "cb", "L", "L" } },
+ { INDEX_op_qemu_st16, { "L", "L", "L" } },
+ { INDEX_op_qemu_st32, { "L", "L", "L" } },
+ { INDEX_op_qemu_st64, { "L", "L", "L", "L" } },
+#endif
+ { -1 },
+};
+
+static int tcg_target_callee_save_regs[] = {
+#if TCG_TARGET_REG_BITS == 64
+ TCG_REG_RBP,
+ TCG_REG_RBX,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ /* TCG_REG_R14, */ /* Currently used for the global env. */
+ TCG_REG_R15,
+#else
+ /* TCG_REG_EBP, */ /* Currently used for the global env. */
+ TCG_REG_EBX,
+ TCG_REG_ESI,
+ TCG_REG_EDI,
+#endif
+};
+
+/* Generate global QEMU prologue and epilogue code */
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ int i, frame_size, push_size, stack_addend;
+
+ /* TB prologue */
+
+ /* Save all callee saved registers. */
+ for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
+ tcg_out_push(s, tcg_target_callee_save_regs[i]);
+ }
+
+ /* Reserve some stack space. */
+ push_size = 1 + ARRAY_SIZE(tcg_target_callee_save_regs);
+ push_size *= TCG_TARGET_REG_BITS / 8;
+
+ frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE;
+ frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) &
+ ~(TCG_TARGET_STACK_ALIGN - 1);
+ stack_addend = frame_size - push_size;
+ tcg_out_addi(s, TCG_REG_ESP, -stack_addend);
+
+ /* jmp *tb. */
+ tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]);
+
+ /* TB epilogue */
+ tb_ret_addr = s->code_ptr;
+
+ tcg_out_addi(s, TCG_REG_ESP, stack_addend);
+
+ for (i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) {
+ tcg_out_pop(s, tcg_target_callee_save_regs[i]);
+ }
+ tcg_out_opc(s, OPC_RET, 0, 0, 0);
+}
+
+static void tcg_target_init(TCGContext *s)
+{
+#if !defined(CONFIG_USER_ONLY)
+ /* fail safe */
+ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry))
+ tcg_abort();
+#endif
+
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff);
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff);
+ } else {
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff);
+ }
+
+ tcg_regset_clear(tcg_target_call_clobber_regs);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX);
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RDI);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RSI);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11);
+ }
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_ESP);
+
+ tcg_add_target_add_op_defs(x86_op_defs);
+}
diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h
new file mode 100644
index 0000000..bfafbfc
--- /dev/null
+++ b/tcg/i386/tcg-target.h
@@ -0,0 +1,126 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_I386 1
+
+#if defined(__x86_64__)
+# define TCG_TARGET_REG_BITS 64
+#else
+# define TCG_TARGET_REG_BITS 32
+#endif
+//#define TCG_TARGET_WORDS_BIGENDIAN
+
+#if TCG_TARGET_REG_BITS == 64
+# define TCG_TARGET_NB_REGS 16
+#else
+# define TCG_TARGET_NB_REGS 8
+#endif
+
+enum {
+ TCG_REG_EAX = 0,
+ TCG_REG_ECX,
+ TCG_REG_EDX,
+ TCG_REG_EBX,
+ TCG_REG_ESP,
+ TCG_REG_EBP,
+ TCG_REG_ESI,
+ TCG_REG_EDI,
+
+ /* 64-bit registers; always define the symbols to avoid
+ too much if-deffing. */
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_RAX = TCG_REG_EAX,
+ TCG_REG_RCX = TCG_REG_ECX,
+ TCG_REG_RDX = TCG_REG_EDX,
+ TCG_REG_RBX = TCG_REG_EBX,
+ TCG_REG_RSP = TCG_REG_ESP,
+ TCG_REG_RBP = TCG_REG_EBP,
+ TCG_REG_RSI = TCG_REG_ESI,
+ TCG_REG_RDI = TCG_REG_EDI,
+};
+
+#define TCG_CT_CONST_S32 0x100
+#define TCG_CT_CONST_U32 0x200
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_ESP
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_CALL_STACK_OFFSET 0
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div2_i32
+#define TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_ext8u_i32
+#define TCG_TARGET_HAS_ext16u_i32
+#define TCG_TARGET_HAS_bswap16_i32
+#define TCG_TARGET_HAS_bswap32_i32
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_not_i32
+// #define TCG_TARGET_HAS_andc_i32
+// #define TCG_TARGET_HAS_orc_i32
+// #define TCG_TARGET_HAS_eqv_i32
+// #define TCG_TARGET_HAS_nand_i32
+// #define TCG_TARGET_HAS_nor_i32
+
+#if TCG_TARGET_REG_BITS == 64
+#define TCG_TARGET_HAS_div2_i64
+#define TCG_TARGET_HAS_rot_i64
+#define TCG_TARGET_HAS_ext8s_i64
+#define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+#define TCG_TARGET_HAS_ext8u_i64
+#define TCG_TARGET_HAS_ext16u_i64
+#define TCG_TARGET_HAS_ext32u_i64
+#define TCG_TARGET_HAS_bswap16_i64
+#define TCG_TARGET_HAS_bswap32_i64
+#define TCG_TARGET_HAS_bswap64_i64
+#define TCG_TARGET_HAS_neg_i64
+#define TCG_TARGET_HAS_not_i64
+// #define TCG_TARGET_HAS_andc_i64
+// #define TCG_TARGET_HAS_orc_i64
+// #define TCG_TARGET_HAS_eqv_i64
+// #define TCG_TARGET_HAS_nand_i64
+// #define TCG_TARGET_HAS_nor_i64
+#endif
+
+#define TCG_TARGET_HAS_GUEST_BASE
+
+/* Note: must be synced with dyngen-exec.h */
+#if TCG_TARGET_REG_BITS == 64
+# define TCG_AREG0 TCG_REG_R14
+#else
+# define TCG_AREG0 TCG_REG_EBP
+#endif
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c
new file mode 100644
index 0000000..8dac7f7
--- /dev/null
+++ b/tcg/ia64/tcg-target.c
@@ -0,0 +1,2390 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2009-2010 Aurelien Jarno <aurelien@aurel32.net>
+ * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Register definitions
+ */
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+ "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39",
+ "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47",
+ "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55",
+ "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63",
+};
+#endif
+
+#ifdef CONFIG_USE_GUEST_BASE
+#define TCG_GUEST_BASE_REG TCG_REG_R55
+#else
+#define TCG_GUEST_BASE_REG TCG_REG_R0
+#endif
+#ifndef GUEST_BASE
+#define GUEST_BASE 0
+#endif
+
+/* Branch registers */
+enum {
+ TCG_REG_B0 = 0,
+ TCG_REG_B1,
+ TCG_REG_B2,
+ TCG_REG_B3,
+ TCG_REG_B4,
+ TCG_REG_B5,
+ TCG_REG_B6,
+ TCG_REG_B7,
+};
+
+/* Floating point registers */
+enum {
+ TCG_REG_F0 = 0,
+ TCG_REG_F1,
+ TCG_REG_F2,
+ TCG_REG_F3,
+ TCG_REG_F4,
+ TCG_REG_F5,
+ TCG_REG_F6,
+ TCG_REG_F7,
+ TCG_REG_F8,
+ TCG_REG_F9,
+ TCG_REG_F10,
+ TCG_REG_F11,
+ TCG_REG_F12,
+ TCG_REG_F13,
+ TCG_REG_F14,
+ TCG_REG_F15,
+};
+
+/* Predicate registers */
+enum {
+ TCG_REG_P0 = 0,
+ TCG_REG_P1,
+ TCG_REG_P2,
+ TCG_REG_P3,
+ TCG_REG_P4,
+ TCG_REG_P5,
+ TCG_REG_P6,
+ TCG_REG_P7,
+ TCG_REG_P8,
+ TCG_REG_P9,
+ TCG_REG_P10,
+ TCG_REG_P11,
+ TCG_REG_P12,
+ TCG_REG_P13,
+ TCG_REG_P14,
+ TCG_REG_P15,
+};
+
+/* Application registers */
+enum {
+ TCG_REG_PFS = 64,
+};
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R34,
+ TCG_REG_R35,
+ TCG_REG_R36,
+ TCG_REG_R37,
+ TCG_REG_R38,
+ TCG_REG_R39,
+ TCG_REG_R40,
+ TCG_REG_R41,
+ TCG_REG_R42,
+ TCG_REG_R43,
+ TCG_REG_R44,
+ TCG_REG_R45,
+ TCG_REG_R46,
+ TCG_REG_R47,
+ TCG_REG_R48,
+ TCG_REG_R49,
+ TCG_REG_R50,
+ TCG_REG_R51,
+ TCG_REG_R52,
+ TCG_REG_R53,
+ TCG_REG_R54,
+ TCG_REG_R55,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_R27,
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31,
+ TCG_REG_R56,
+ TCG_REG_R57,
+ TCG_REG_R58,
+ TCG_REG_R59,
+ TCG_REG_R60,
+ TCG_REG_R61,
+ TCG_REG_R62,
+ TCG_REG_R63,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11
+};
+
+static const int tcg_target_call_iarg_regs[8] = {
+ TCG_REG_R56,
+ TCG_REG_R57,
+ TCG_REG_R58,
+ TCG_REG_R59,
+ TCG_REG_R60,
+ TCG_REG_R61,
+ TCG_REG_R62,
+ TCG_REG_R63,
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_R8,
+ TCG_REG_R9
+};
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return 8;
+}
+
+/*
+ * opcode formation
+ */
+
+/* bundle templates: stops (double bar in the IA64 manual) are marked with
+ an uppercase letter. */
+enum {
+ mii = 0x00,
+ miI = 0x01,
+ mIi = 0x02,
+ mII = 0x03,
+ mlx = 0x04,
+ mLX = 0x05,
+ mmi = 0x08,
+ mmI = 0x09,
+ Mmi = 0x0a,
+ MmI = 0x0b,
+ mfi = 0x0c,
+ mfI = 0x0d,
+ mmf = 0x0e,
+ mmF = 0x0f,
+ mib = 0x10,
+ miB = 0x11,
+ mbb = 0x12,
+ mbB = 0x13,
+ bbb = 0x16,
+ bbB = 0x17,
+ mmb = 0x18,
+ mmB = 0x19,
+ mfb = 0x1c,
+ mfB = 0x1d,
+};
+
+enum {
+ OPC_ADD_A1 = 0x10000000000ull,
+ OPC_AND_A1 = 0x10060000000ull,
+ OPC_AND_A3 = 0x10160000000ull,
+ OPC_ANDCM_A1 = 0x10068000000ull,
+ OPC_ANDCM_A3 = 0x10168000000ull,
+ OPC_ADDS_A4 = 0x10800000000ull,
+ OPC_ADDL_A5 = 0x12000000000ull,
+ OPC_ALLOC_M34 = 0x02c00000000ull,
+ OPC_BR_DPTK_FEW_B1 = 0x08400000000ull,
+ OPC_BR_SPTK_MANY_B1 = 0x08000001000ull,
+ OPC_BR_SPTK_MANY_B4 = 0x00100001000ull,
+ OPC_BR_CALL_SPTK_MANY_B5 = 0x02100001000ull,
+ OPC_BR_RET_SPTK_MANY_B4 = 0x00108001100ull,
+ OPC_BRL_SPTK_MANY_X3 = 0x18000001000ull,
+ OPC_CMP_LT_A6 = 0x18000000000ull,
+ OPC_CMP_LTU_A6 = 0x1a000000000ull,
+ OPC_CMP_EQ_A6 = 0x1c000000000ull,
+ OPC_CMP4_LT_A6 = 0x18400000000ull,
+ OPC_CMP4_LTU_A6 = 0x1a400000000ull,
+ OPC_CMP4_EQ_A6 = 0x1c400000000ull,
+ OPC_DEP_Z_I12 = 0x0a600000000ull,
+ OPC_EXTR_I11 = 0x0a400002000ull,
+ OPC_EXTR_U_I11 = 0x0a400000000ull,
+ OPC_FCVT_FX_TRUNC_S1_F10 = 0x004d0000000ull,
+ OPC_FCVT_FXU_TRUNC_S1_F10 = 0x004d8000000ull,
+ OPC_FCVT_XF_F11 = 0x000e0000000ull,
+ OPC_FMA_S1_F1 = 0x10400000000ull,
+ OPC_FNMA_S1_F1 = 0x18400000000ull,
+ OPC_FRCPA_S1_F6 = 0x00600000000ull,
+ OPC_GETF_SIG_M19 = 0x08708000000ull,
+ OPC_LD1_M1 = 0x08000000000ull,
+ OPC_LD1_M3 = 0x0a000000000ull,
+ OPC_LD2_M1 = 0x08040000000ull,
+ OPC_LD2_M3 = 0x0a040000000ull,
+ OPC_LD4_M1 = 0x08080000000ull,
+ OPC_LD4_M3 = 0x0a080000000ull,
+ OPC_LD8_M1 = 0x080c0000000ull,
+ OPC_LD8_M3 = 0x0a0c0000000ull,
+ OPC_MUX1_I3 = 0x0eca0000000ull,
+ OPC_NOP_B9 = 0x04008000000ull,
+ OPC_NOP_F16 = 0x00008000000ull,
+ OPC_NOP_I18 = 0x00008000000ull,
+ OPC_NOP_M48 = 0x00008000000ull,
+ OPC_MOV_I21 = 0x00e00100000ull,
+ OPC_MOV_RET_I21 = 0x00e00500000ull,
+ OPC_MOV_I22 = 0x00188000000ull,
+ OPC_MOV_I_I26 = 0x00150000000ull,
+ OPC_MOVL_X2 = 0x0c000000000ull,
+ OPC_OR_A1 = 0x10070000000ull,
+ OPC_SETF_EXP_M18 = 0x0c748000000ull,
+ OPC_SETF_SIG_M18 = 0x0c708000000ull,
+ OPC_SHL_I7 = 0x0f240000000ull,
+ OPC_SHR_I5 = 0x0f220000000ull,
+ OPC_SHR_U_I5 = 0x0f200000000ull,
+ OPC_SHRP_I10 = 0x0ac00000000ull,
+ OPC_SXT1_I29 = 0x000a0000000ull,
+ OPC_SXT2_I29 = 0x000a8000000ull,
+ OPC_SXT4_I29 = 0x000b0000000ull,
+ OPC_ST1_M4 = 0x08c00000000ull,
+ OPC_ST2_M4 = 0x08c40000000ull,
+ OPC_ST4_M4 = 0x08c80000000ull,
+ OPC_ST8_M4 = 0x08cc0000000ull,
+ OPC_SUB_A1 = 0x10028000000ull,
+ OPC_SUB_A3 = 0x10128000000ull,
+ OPC_UNPACK4_L_I2 = 0x0f860000000ull,
+ OPC_XMA_L_F2 = 0x1d000000000ull,
+ OPC_XOR_A1 = 0x10078000000ull,
+ OPC_ZXT1_I29 = 0x00080000000ull,
+ OPC_ZXT2_I29 = 0x00088000000ull,
+ OPC_ZXT4_I29 = 0x00090000000ull,
+};
+
+static inline uint64_t tcg_opc_a1(int qp, uint64_t opc, int r1,
+ int r2, int r3)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_a3(int qp, uint64_t opc, int r1,
+ uint64_t imm, int r3)
+{
+ return opc
+ | ((imm & 0x80) << 29) /* s */
+ | ((imm & 0x7f) << 13) /* imm7b */
+ | ((r3 & 0x7f) << 20)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_a4(int qp, uint64_t opc, int r1,
+ uint64_t imm, int r3)
+{
+ return opc
+ | ((imm & 0x2000) << 23) /* s */
+ | ((imm & 0x1f80) << 20) /* imm6d */
+ | ((imm & 0x007f) << 13) /* imm7b */
+ | ((r3 & 0x7f) << 20)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_a5(int qp, uint64_t opc, int r1,
+ uint64_t imm, int r3)
+{
+ return opc
+ | ((imm & 0x200000) << 15) /* s */
+ | ((imm & 0x1f0000) << 6) /* imm5c */
+ | ((imm & 0x00ff80) << 20) /* imm9d */
+ | ((imm & 0x00007f) << 13) /* imm7b */
+ | ((r3 & 0x03) << 20)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_a6(int qp, uint64_t opc, int p1,
+ int p2, int r2, int r3)
+{
+ return opc
+ | ((p2 & 0x3f) << 27)
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((p1 & 0x3f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_b1(int qp, uint64_t opc, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x100000) << 16) /* s */
+ | ((imm & 0x0fffff) << 13) /* imm20b */
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_b4(int qp, uint64_t opc, int b2)
+{
+ return opc
+ | ((b2 & 0x7) << 13)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_b5(int qp, uint64_t opc, int b1, int b2)
+{
+ return opc
+ | ((b2 & 0x7) << 13)
+ | ((b1 & 0x7) << 6)
+ | (qp & 0x3f);
+}
+
+
+static inline uint64_t tcg_opc_b9(int qp, uint64_t opc, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x100000) << 16) /* i */
+ | ((imm & 0x0fffff) << 6) /* imm20a */
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_f1(int qp, uint64_t opc, int f1,
+ int f3, int f4, int f2)
+{
+ return opc
+ | ((f4 & 0x7f) << 27)
+ | ((f3 & 0x7f) << 20)
+ | ((f2 & 0x7f) << 13)
+ | ((f1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_f2(int qp, uint64_t opc, int f1,
+ int f3, int f4, int f2)
+{
+ return opc
+ | ((f4 & 0x7f) << 27)
+ | ((f3 & 0x7f) << 20)
+ | ((f2 & 0x7f) << 13)
+ | ((f1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_f6(int qp, uint64_t opc, int f1,
+ int p2, int f2, int f3)
+{
+ return opc
+ | ((p2 & 0x3f) << 27)
+ | ((f3 & 0x7f) << 20)
+ | ((f2 & 0x7f) << 13)
+ | ((f1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_f10(int qp, uint64_t opc, int f1, int f2)
+{
+ return opc
+ | ((f2 & 0x7f) << 13)
+ | ((f1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_f11(int qp, uint64_t opc, int f1, int f2)
+{
+ return opc
+ | ((f2 & 0x7f) << 13)
+ | ((f1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_f16(int qp, uint64_t opc, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x100000) << 16) /* i */
+ | ((imm & 0x0fffff) << 6) /* imm20a */
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i2(int qp, uint64_t opc, int r1,
+ int r2, int r3)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i3(int qp, uint64_t opc, int r1,
+ int r2, int mbtype)
+{
+ return opc
+ | ((mbtype & 0x0f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i5(int qp, uint64_t opc, int r1,
+ int r3, int r2)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i7(int qp, uint64_t opc, int r1,
+ int r2, int r3)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i10(int qp, uint64_t opc, int r1,
+ int r2, int r3, uint64_t count)
+{
+ return opc
+ | ((count & 0x3f) << 27)
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i11(int qp, uint64_t opc, int r1,
+ int r3, uint64_t pos, uint64_t len)
+{
+ return opc
+ | ((len & 0x3f) << 27)
+ | ((r3 & 0x7f) << 20)
+ | ((pos & 0x3f) << 14)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i12(int qp, uint64_t opc, int r1,
+ int r2, uint64_t pos, uint64_t len)
+{
+ return opc
+ | ((len & 0x3f) << 27)
+ | ((pos & 0x3f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i18(int qp, uint64_t opc, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x100000) << 16) /* i */
+ | ((imm & 0x0fffff) << 6) /* imm20a */
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i21(int qp, uint64_t opc, int b1,
+ int r2, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x1ff) << 24)
+ | ((r2 & 0x7f) << 13)
+ | ((b1 & 0x7) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i22(int qp, uint64_t opc, int r1, int b2)
+{
+ return opc
+ | ((b2 & 0x7) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i26(int qp, uint64_t opc, int ar3, int r2)
+{
+ return opc
+ | ((ar3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_i29(int qp, uint64_t opc, int r1, int r3)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_l2(uint64_t imm)
+{
+ return (imm & 0x7fffffffffc00000ull) >> 22;
+}
+
+static inline uint64_t tcg_opc_l3(uint64_t imm)
+{
+ return (imm & 0x07fffffffff00000ull) >> 18;
+}
+
+static inline uint64_t tcg_opc_m1(int qp, uint64_t opc, int r1, int r3)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_m3(int qp, uint64_t opc, int r1,
+ int r3, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x100) << 28) /* s */
+ | ((imm & 0x080) << 20) /* i */
+ | ((imm & 0x07f) << 13) /* imm7b */
+ | ((r3 & 0x7f) << 20)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_m4(int qp, uint64_t opc, int r2, int r3)
+{
+ return opc
+ | ((r3 & 0x7f) << 20)
+ | ((r2 & 0x7f) << 13)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_m18(int qp, uint64_t opc, int f1, int r2)
+{
+ return opc
+ | ((r2 & 0x7f) << 13)
+ | ((f1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_m19(int qp, uint64_t opc, int r1, int f2)
+{
+ return opc
+ | ((f2 & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_m34(int qp, uint64_t opc, int r1,
+ int sof, int sol, int sor)
+{
+ return opc
+ | ((sor & 0x0f) << 27)
+ | ((sol & 0x7f) << 20)
+ | ((sof & 0x7f) << 13)
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_m48(int qp, uint64_t opc, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x100000) << 16) /* i */
+ | ((imm & 0x0fffff) << 6) /* imm20a */
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_x2(int qp, uint64_t opc,
+ int r1, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x8000000000000000ull) >> 27) /* i */
+ | (imm & 0x0000000000200000ull) /* ic */
+ | ((imm & 0x00000000001f0000ull) << 6) /* imm5c */
+ | ((imm & 0x000000000000ff80ull) << 20) /* imm9d */
+ | ((imm & 0x000000000000007full) << 13) /* imm7b */
+ | ((r1 & 0x7f) << 6)
+ | (qp & 0x3f);
+}
+
+static inline uint64_t tcg_opc_x3(int qp, uint64_t opc, uint64_t imm)
+{
+ return opc
+ | ((imm & 0x0800000000000000ull) >> 23) /* i */
+ | ((imm & 0x00000000000fffffull) << 13) /* imm20b */
+ | (qp & 0x3f);
+}
+
+
+/*
+ * Relocations
+ */
+
+static inline void reloc_pcrel21b (void *pc, tcg_target_long target)
+{
+ uint64_t imm;
+ int64_t disp;
+ int slot;
+
+ slot = (tcg_target_long) pc & 3;
+ pc = (void *)((tcg_target_long) pc & ~3);
+
+ disp = target - (tcg_target_long) pc;
+ imm = (uint64_t) disp >> 4;
+
+ switch(slot) {
+ case 0:
+ *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 8) & 0xfffffdc00003ffffull)
+ | ((imm & 0x100000) << 21) /* s */
+ | ((imm & 0x0fffff) << 18); /* imm20b */
+ break;
+ case 1:
+ *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xfffffffffffb8000ull)
+ | ((imm & 0x100000) >> 2) /* s */
+ | ((imm & 0x0fffe0) >> 5); /* imm20b */
+ *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 0) & 0x07ffffffffffffffull)
+ | ((imm & 0x00001f) << 59); /* imm20b */
+ break;
+ case 2:
+ *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xf700000fffffffffull)
+ | ((imm & 0x100000) << 39) /* s */
+ | ((imm & 0x0fffff) << 36); /* imm20b */
+ break;
+ }
+}
+
+static inline uint64_t get_reloc_pcrel21b (void *pc)
+{
+ int64_t low, high;
+ int slot;
+
+ slot = (tcg_target_long) pc & 3;
+ pc = (void *)((tcg_target_long) pc & ~3);
+
+ low = (*(uint64_t *)(pc + 0));
+ high = (*(uint64_t *)(pc + 8));
+
+ switch(slot) {
+ case 0:
+ return ((low >> 21) & 0x100000) + /* s */
+ ((low >> 18) & 0x0fffff); /* imm20b */
+ case 1:
+ return ((high << 2) & 0x100000) + /* s */
+ ((high << 5) & 0x0fffe0) + /* imm20b */
+ ((low >> 59) & 0x00001f); /* imm20b */
+ case 2:
+ return ((high >> 39) & 0x100000) + /* s */
+ ((high >> 36) & 0x0fffff); /* imm20b */
+ default:
+ tcg_abort();
+ }
+}
+
+static inline void reloc_pcrel60b (void *pc, tcg_target_long target)
+{
+ int64_t disp;
+ uint64_t imm;
+
+ disp = target - (tcg_target_long) pc;
+ imm = (uint64_t) disp >> 4;
+
+ *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xf700000fff800000ull)
+ | (imm & 0x0800000000000000ull) /* s */
+ | ((imm & 0x07fffff000000000ull) >> 36) /* imm39 */
+ | ((imm & 0x00000000000fffffull) << 36); /* imm20b */
+ *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 0) & 0x00003fffffffffffull)
+ | ((imm & 0x0000000ffff00000ull) << 28); /* imm39 */
+}
+
+static inline uint64_t get_reloc_pcrel60b (void *pc)
+{
+ int64_t low, high;
+
+ low = (*(uint64_t *)(pc + 0));
+ high = (*(uint64_t *)(pc + 8));
+
+ return ((high) & 0x0800000000000000ull) + /* s */
+ ((high >> 36) & 0x00000000000fffffull) + /* imm20b */
+ ((high << 36) & 0x07fffff000000000ull) + /* imm39 */
+ ((low >> 28) & 0x0000000ffff00000ull); /* imm39 */
+}
+
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ value += addend;
+ switch (type) {
+ case R_IA64_PCREL21B:
+ reloc_pcrel21b(code_ptr, value);
+ break;
+ case R_IA64_PCREL60B:
+ reloc_pcrel60b(code_ptr, value);
+ default:
+ tcg_abort();
+ }
+}
+
+/*
+ * Constraints
+ */
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch(ct_str[0]) {
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set(ct->u.regs, 0xffffffffffffffffull);
+ break;
+ case 'I':
+ ct->ct |= TCG_CT_CONST_S22;
+ break;
+ case 'S':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set(ct->u.regs, 0xffffffffffffffffull);
+#if defined(CONFIG_SOFTMMU)
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R56);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R57);
+#endif
+ break;
+ case 'Z':
+ /* We are cheating a bit here, using the fact that the register
+ r0 is also the register number 0. Hence there is no need
+ to check for const_args in each instruction. */
+ ct->ct |= TCG_CT_CONST_ZERO;
+ break;
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct;
+ ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST)
+ return 1;
+ else if ((ct & TCG_CT_CONST_ZERO) && val == 0)
+ return 1;
+ else if ((ct & TCG_CT_CONST_S22) && val == ((int32_t)val << 10) >> 10)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Code generation
+ */
+
+static uint8_t *tb_ret_addr;
+
+static inline void tcg_out_bundle(TCGContext *s, int template,
+ uint64_t slot0, uint64_t slot1,
+ uint64_t slot2)
+{
+ template &= 0x1f; /* 5 bits */
+ slot0 &= 0x1ffffffffffull; /* 41 bits */
+ slot1 &= 0x1ffffffffffull; /* 41 bits */
+ slot2 &= 0x1ffffffffffull; /* 41 bits */
+
+ *(uint64_t *)(s->code_ptr + 0) = (slot1 << 46) | (slot0 << 5) | template;
+ *(uint64_t *)(s->code_ptr + 8) = (slot2 << 23) | (slot1 >> 18);
+ s->code_ptr += 16;
+}
+
+static inline void tcg_out_mov(TCGContext *s, TCGType type,
+ TCGArg ret, TCGArg arg)
+{
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, 0, arg));
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+ TCGArg reg, tcg_target_long arg)
+{
+ tcg_out_bundle(s, mLX,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_l2 (arg),
+ tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg));
+}
+
+static inline void tcg_out_addi(TCGContext *s, TCGArg reg, tcg_target_long val)
+{
+ if (val == ((int32_t)val << 10) >> 10) {
+ tcg_out_bundle(s, MmI,
+ tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5,
+ TCG_REG_R2, val, TCG_REG_R0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg,
+ reg, TCG_REG_R2));
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, val);
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg,
+ reg, TCG_REG_R2));
+ }
+}
+
+static void tcg_out_br(TCGContext *s, int label_index)
+{
+ TCGLabel *l = &s->labels[label_index];
+
+ /* We pay attention here to not modify the branch target by reading
+ the existing value and using it again. This ensure that caches and
+ memory are kept coherent during retranslation. */
+ tcg_out_bundle(s, mmB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_b1 (TCG_REG_P0, OPC_BR_SPTK_MANY_B1,
+ get_reloc_pcrel21b(s->code_ptr + 2)));
+
+ if (l->has_value) {
+ reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value);
+ } else {
+ tcg_out_reloc(s, (s->code_ptr - 16) + 2,
+ R_IA64_PCREL21B, label_index, 0);
+ }
+}
+
+static inline void tcg_out_call(TCGContext *s, TCGArg addr)
+{
+ tcg_out_bundle(s, MmI,
+ tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, addr),
+ tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R3, 8, addr),
+ tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21,
+ TCG_REG_B6, TCG_REG_R2, 0));
+ tcg_out_bundle(s, mmB,
+ tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R3),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_b5 (TCG_REG_P0, OPC_BR_CALL_SPTK_MANY_B5,
+ TCG_REG_B0, TCG_REG_B6));
+}
+
+static void tcg_out_exit_tb(TCGContext *s, tcg_target_long arg)
+{
+ int64_t disp;
+ uint64_t imm;
+
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R8, arg);
+
+ disp = tb_ret_addr - s->code_ptr;
+ imm = (uint64_t)disp >> 4;
+
+ tcg_out_bundle(s, mLX,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_l3 (imm),
+ tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, imm));
+}
+
+static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg)
+{
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+ tcg_abort();
+ } else {
+ /* indirect jump method */
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2,
+ (tcg_target_long)(s->tb_next + arg));
+ tcg_out_bundle(s, MmI,
+ tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1,
+ TCG_REG_R2, TCG_REG_R2),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6,
+ TCG_REG_R2, 0));
+ tcg_out_bundle(s, mmB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4,
+ TCG_REG_B6));
+ }
+ s->tb_next_offset[arg] = s->code_ptr - s->code_buf;
+}
+
+static inline void tcg_out_jmp(TCGContext *s, TCGArg addr)
+{
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, addr, 0));
+ tcg_out_bundle(s, mmB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_b4(TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6));
+}
+
+static inline void tcg_out_ld_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg,
+ TCGArg arg1, tcg_target_long arg2)
+{
+ if (arg2 == ((int16_t)arg2 >> 2) << 2) {
+ tcg_out_bundle(s, MmI,
+ tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4,
+ TCG_REG_R2, arg2, arg1),
+ tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2);
+ tcg_out_bundle(s, MmI,
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1,
+ TCG_REG_R2, TCG_REG_R2, arg1),
+ tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+}
+
+static inline void tcg_out_st_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg,
+ TCGArg arg1, tcg_target_long arg2)
+{
+ if (arg2 == ((int16_t)arg2 >> 2) << 2) {
+ tcg_out_bundle(s, MmI,
+ tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4,
+ TCG_REG_R2, arg2, arg1),
+ tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2);
+ tcg_out_bundle(s, MmI,
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1,
+ TCG_REG_R2, TCG_REG_R2, arg1),
+ tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGArg arg,
+ TCGArg arg1, tcg_target_long arg2)
+{
+ if (type == TCG_TYPE_I32) {
+ tcg_out_ld_rel(s, OPC_LD4_M1, arg, arg1, arg2);
+ } else {
+ tcg_out_ld_rel(s, OPC_LD8_M1, arg, arg1, arg2);
+ }
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, TCGArg arg,
+ TCGArg arg1, tcg_target_long arg2)
+{
+ if (type == TCG_TYPE_I32) {
+ tcg_out_st_rel(s, OPC_ST4_M4, arg, arg1, arg2);
+ } else {
+ tcg_out_st_rel(s, OPC_ST8_M4, arg, arg1, arg2);
+ }
+}
+
+static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, TCGArg ret,
+ TCGArg arg1, int const_arg1,
+ TCGArg arg2, int const_arg2)
+{
+ uint64_t opc1, opc2;
+
+ if (const_arg1 && arg1 != 0) {
+ opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5,
+ TCG_REG_R2, arg1, TCG_REG_R0);
+ arg1 = TCG_REG_R2;
+ } else {
+ opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0);
+ }
+
+ if (const_arg2 && arg2 != 0) {
+ opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5,
+ TCG_REG_R3, arg2, TCG_REG_R0);
+ arg2 = TCG_REG_R3;
+ } else {
+ opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0);
+ }
+
+ tcg_out_bundle(s, mII,
+ opc1,
+ opc2,
+ tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2));
+}
+
+static inline void tcg_out_eqv(TCGContext *s, TCGArg ret,
+ TCGArg arg1, int const_arg1,
+ TCGArg arg2, int const_arg2)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_XOR_A1, ret, arg1, arg2),
+ tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret));
+}
+
+static inline void tcg_out_nand(TCGContext *s, TCGArg ret,
+ TCGArg arg1, int const_arg1,
+ TCGArg arg2, int const_arg2)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, ret, arg1, arg2),
+ tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret));
+}
+
+static inline void tcg_out_nor(TCGContext *s, TCGArg ret,
+ TCGArg arg1, int const_arg1,
+ TCGArg arg2, int const_arg2)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, arg2),
+ tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret));
+}
+
+static inline void tcg_out_orc(TCGContext *s, TCGArg ret,
+ TCGArg arg1, int const_arg1,
+ TCGArg arg2, int const_arg2)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, TCG_REG_R2, -1, arg2),
+ tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, TCG_REG_R2));
+}
+
+static inline void tcg_out_mul(TCGContext *s, TCGArg ret,
+ TCGArg arg1, TCGArg arg2)
+{
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F6, arg1),
+ tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F7, arg2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ tcg_out_bundle(s, mmF,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_f2 (TCG_REG_P0, OPC_XMA_L_F2, TCG_REG_F6, TCG_REG_F6,
+ TCG_REG_F7, TCG_REG_F0));
+ tcg_out_bundle(s, miI,
+ tcg_opc_m19(TCG_REG_P0, OPC_GETF_SIG_M19, ret, TCG_REG_F6),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+}
+
+static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11,
+ ret, arg1, arg2, 31 - arg2));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3,
+ TCG_REG_R3, 0x1f, arg2),
+ tcg_opc_i29(TCG_REG_P0, OPC_SXT4_I29, TCG_REG_R2, arg1),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret,
+ TCG_REG_R2, TCG_REG_R3));
+ }
+}
+
+static inline void tcg_out_sar_i64(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11,
+ ret, arg1, arg2, 63 - arg2));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, arg1, arg2));
+ }
+}
+
+static inline void tcg_out_shl_i32(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret,
+ arg1, 63 - arg2, 31 - arg2));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R2,
+ 0x1f, arg2),
+ tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret,
+ arg1, TCG_REG_R2));
+ }
+}
+
+static inline void tcg_out_shl_i64(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret,
+ arg1, 63 - arg2, 63 - arg2));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret,
+ arg1, arg2));
+ }
+}
+
+static inline void tcg_out_shr_i32(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret,
+ arg1, arg2, 31 - arg2));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3,
+ 0x1f, arg2),
+ tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, arg1),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret,
+ TCG_REG_R2, TCG_REG_R3));
+ }
+}
+
+static inline void tcg_out_shr_i64(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret,
+ arg1, arg2, 63 - arg2));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret,
+ arg1, arg2));
+ }
+}
+
+static inline void tcg_out_rotl_i32(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2,
+ TCG_REG_R2, arg1, arg1),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret,
+ TCG_REG_R2, 32 - arg2, 31));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2,
+ TCG_REG_R2, arg1, arg1),
+ tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3,
+ 0x1f, arg2));
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R3,
+ 0x20, TCG_REG_R3),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret,
+ TCG_REG_R2, TCG_REG_R3));
+ }
+}
+
+static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1,
+ arg1, 0x40 - arg2));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2,
+ 0x40, arg2),
+ tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R3,
+ arg1, arg2),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R2,
+ arg1, TCG_REG_R2));
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret,
+ TCG_REG_R2, TCG_REG_R3));
+ }
+}
+
+static inline void tcg_out_rotr_i32(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2,
+ TCG_REG_R2, arg1, arg1),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret,
+ TCG_REG_R2, arg2, 31));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3,
+ 0x1f, arg2),
+ tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2,
+ TCG_REG_R2, arg1, arg1),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret,
+ TCG_REG_R2, TCG_REG_R3));
+ }
+}
+
+static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1,
+ TCGArg arg2, int const_arg2)
+{
+ if (const_arg2) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1,
+ arg1, arg2));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2,
+ 0x40, arg2),
+ tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R3,
+ arg1, arg2),
+ tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R2,
+ arg1, TCG_REG_R2));
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret,
+ TCG_REG_R2, TCG_REG_R3));
+ }
+}
+
+static inline void tcg_out_ext(TCGContext *s, uint64_t opc_i29,
+ TCGArg ret, TCGArg arg)
+{
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i29(TCG_REG_P0, opc_i29, ret, arg));
+}
+
+static inline void tcg_out_bswap16(TCGContext *s, TCGArg ret, TCGArg arg)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 15, 15),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb));
+}
+
+static inline void tcg_out_bswap32(TCGContext *s, TCGArg ret, TCGArg arg)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 31, 31),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb));
+}
+
+static inline void tcg_out_bswap64(TCGContext *s, TCGArg ret, TCGArg arg)
+{
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, arg, 0xb));
+}
+
+static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1,
+ TCGArg arg2, int cmp4)
+{
+ uint64_t opc_eq_a6, opc_lt_a6, opc_ltu_a6;
+
+ if (cmp4) {
+ opc_eq_a6 = OPC_CMP4_EQ_A6;
+ opc_lt_a6 = OPC_CMP4_LT_A6;
+ opc_ltu_a6 = OPC_CMP4_LTU_A6;
+ } else {
+ opc_eq_a6 = OPC_CMP_EQ_A6;
+ opc_lt_a6 = OPC_CMP_LT_A6;
+ opc_ltu_a6 = OPC_CMP_LTU_A6;
+ }
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2);
+ case TCG_COND_NE:
+ return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2);
+ case TCG_COND_LT:
+ return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2);
+ case TCG_COND_LTU:
+ return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2);
+ case TCG_COND_GE:
+ return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2);
+ case TCG_COND_GEU:
+ return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2);
+ case TCG_COND_LE:
+ return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1);
+ case TCG_COND_LEU:
+ return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1);
+ case TCG_COND_GT:
+ return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1);
+ case TCG_COND_GTU:
+ return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1);
+ default:
+ tcg_abort();
+ break;
+ }
+}
+
+static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1,
+ int const_arg1, TCGArg arg2, int const_arg2,
+ int label_index, int cmp4)
+{
+ TCGLabel *l = &s->labels[label_index];
+ uint64_t opc1, opc2;
+
+ if (const_arg1 && arg1 != 0) {
+ opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2,
+ arg1, TCG_REG_R0);
+ arg1 = TCG_REG_R2;
+ } else {
+ opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0);
+ }
+
+ if (const_arg2 && arg2 != 0) {
+ opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3,
+ arg2, TCG_REG_R0);
+ arg2 = TCG_REG_R3;
+ } else {
+ opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0);
+ }
+
+ tcg_out_bundle(s, mII,
+ opc1,
+ opc2,
+ tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4));
+ tcg_out_bundle(s, mmB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_b1 (TCG_REG_P6, OPC_BR_DPTK_FEW_B1,
+ get_reloc_pcrel21b(s->code_ptr + 2)));
+
+ if (l->has_value) {
+ reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value);
+ } else {
+ tcg_out_reloc(s, (s->code_ptr - 16) + 2,
+ R_IA64_PCREL21B, label_index, 0);
+ }
+}
+
+static inline void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGArg ret,
+ TCGArg arg1, TCGArg arg2, int cmp4)
+{
+ tcg_out_bundle(s, MmI,
+ tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4),
+ tcg_opc_a5(TCG_REG_P6, OPC_ADDL_A5, ret, 1, TCG_REG_R0),
+ tcg_opc_a5(TCG_REG_P7, OPC_ADDL_A5, ret, 0, TCG_REG_R0));
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+/* Load and compare a TLB entry, and return the result in (p6, p7).
+ R2 is loaded with the address of the addend TLB entry.
+ R56 is loaded with the address, zero extented on 32-bit targets. */
+static inline void tcg_out_qemu_tlb(TCGContext *s, TCGArg addr_reg,
+ int s_bits, uint64_t offset_rw,
+ uint64_t offset_addend)
+{
+ tcg_out_bundle(s, mII,
+ tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3,
+ TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+ TCG_REG_R0),
+ tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, TCG_REG_R2,
+ addr_reg, TARGET_PAGE_BITS, CPU_TLB_BITS - 1),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R2,
+ TCG_REG_R2, 63 - CPU_TLB_ENTRY_BITS,
+ 63 - CPU_TLB_ENTRY_BITS));
+ tcg_out_bundle(s, mII,
+ tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2,
+ offset_rw, TCG_REG_R2),
+#if TARGET_LONG_BITS == 32
+ tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R56, addr_reg),
+#else
+ tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R56,
+ 0, addr_reg),
+#endif
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2,
+ TCG_REG_R2, TCG_AREG0));
+ tcg_out_bundle(s, mII,
+ tcg_opc_m3 (TCG_REG_P0,
+ (TARGET_LONG_BITS == 32
+ ? OPC_LD4_M3 : OPC_LD8_M3), TCG_REG_R57,
+ TCG_REG_R2, offset_addend - offset_rw),
+ tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, TCG_REG_R3,
+ TCG_REG_R3, TCG_REG_R56),
+ tcg_opc_a6 (TCG_REG_P0, OPC_CMP_EQ_A6, TCG_REG_P6,
+ TCG_REG_P7, TCG_REG_R3, TCG_REG_R57));
+}
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, data_reg, mem_index, s_bits, bswap;
+ uint64_t opc_ld_m1[4] = { OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 };
+ uint64_t opc_ext_i29[8] = { OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0,
+ OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 };
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ mem_index = *args;
+ s_bits = opc & 3;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 1;
+#else
+ bswap = 0;
+#endif
+
+ /* Read the TLB entry */
+ tcg_out_qemu_tlb(s, addr_reg, s_bits,
+ offsetof(CPUState, tlb_table[mem_index][0].addr_read),
+ offsetof(CPUState, tlb_table[mem_index][0].addend));
+
+ /* P6 is the fast path, and P7 the slow path */
+ tcg_out_bundle(s, mLX,
+ tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R57,
+ mem_index, TCG_REG_R0),
+ tcg_opc_l2 ((tcg_target_long) qemu_ld_helpers[s_bits]),
+ tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2,
+ (tcg_target_long) qemu_ld_helpers[s_bits]));
+ tcg_out_bundle(s, MmI,
+ tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3,
+ TCG_REG_R2, 8),
+ tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R3,
+ TCG_REG_R3, TCG_REG_R56),
+ tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6,
+ TCG_REG_R3, 0));
+ if (bswap && s_bits == 1) {
+ tcg_out_bundle(s, MmI,
+ tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits],
+ TCG_REG_R8, TCG_REG_R3),
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12,
+ TCG_REG_R8, TCG_REG_R8, 15, 15));
+ } else if (bswap && s_bits == 2) {
+ tcg_out_bundle(s, MmI,
+ tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits],
+ TCG_REG_R8, TCG_REG_R3),
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12,
+ TCG_REG_R8, TCG_REG_R8, 31, 31));
+ } else {
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits],
+ TCG_REG_R8, TCG_REG_R3),
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+ if (!bswap || s_bits == 0) {
+ tcg_out_bundle(s, miB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5,
+ TCG_REG_B0, TCG_REG_B6));
+ } else {
+ tcg_out_bundle(s, miB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3,
+ TCG_REG_R8, TCG_REG_R8, 0xb),
+ tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5,
+ TCG_REG_B0, TCG_REG_B6));
+ }
+
+ if (opc == 3) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4,
+ data_reg, 0, TCG_REG_R8));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i29(TCG_REG_P0, opc_ext_i29[opc],
+ data_reg, TCG_REG_R8));
+ }
+}
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+
+static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, data_reg, mem_index, bswap;
+ uint64_t opc_st_m4[4] = { OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 };
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ mem_index = *args;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 1;
+#else
+ bswap = 0;
+#endif
+
+ tcg_out_qemu_tlb(s, addr_reg, opc,
+ offsetof(CPUState, tlb_table[mem_index][0].addr_write),
+ offsetof(CPUState, tlb_table[mem_index][0].addend));
+
+ /* P6 is the fast path, and P7 the slow path */
+ tcg_out_bundle(s, mLX,
+ tcg_opc_a4(TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R57,
+ 0, data_reg),
+ tcg_opc_l2 ((tcg_target_long) qemu_st_helpers[opc]),
+ tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2,
+ (tcg_target_long) qemu_st_helpers[opc]));
+ tcg_out_bundle(s, MmI,
+ tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3,
+ TCG_REG_R2, 8),
+ tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R3,
+ TCG_REG_R3, TCG_REG_R56),
+ tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6,
+ TCG_REG_R3, 0));
+
+ if (!bswap || opc == 0) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1,
+ TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ } else if (opc == 1) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1,
+ TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12,
+ TCG_REG_R2, data_reg, 15, 15),
+ tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3,
+ TCG_REG_R2, TCG_REG_R2, 0xb));
+ data_reg = TCG_REG_R2;
+ } else if (opc == 2) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1,
+ TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12,
+ TCG_REG_R2, data_reg, 31, 31),
+ tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3,
+ TCG_REG_R2, TCG_REG_R2, 0xb));
+ data_reg = TCG_REG_R2;
+ } else if (opc == 3) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1,
+ TCG_REG_R1, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3,
+ TCG_REG_R2, data_reg, 0xb));
+ data_reg = TCG_REG_R2;
+ }
+
+ tcg_out_bundle(s, miB,
+ tcg_opc_m4 (TCG_REG_P6, opc_st_m4[opc],
+ data_reg, TCG_REG_R3),
+ tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R58,
+ mem_index, TCG_REG_R0),
+ tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5,
+ TCG_REG_B0, TCG_REG_B6));
+}
+
+#else /* !CONFIG_SOFTMMU */
+
+static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
+{
+ static uint64_t const opc_ld_m1[4] = {
+ OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1
+ };
+ static uint64_t const opc_sxt_i29[4] = {
+ OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0
+ };
+ int addr_reg, data_reg, s_bits, bswap;
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ s_bits = opc & 3;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 1;
+#else
+ bswap = 0;
+#endif
+
+#if TARGET_LONG_BITS == 32
+ if (GUEST_BASE != 0) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29,
+ TCG_REG_R3, addr_reg),
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2,
+ TCG_GUEST_BASE_REG, TCG_REG_R3));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29,
+ TCG_REG_R2, addr_reg),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+
+ if (!bswap || s_bits == 0) {
+ if (s_bits == opc) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits],
+ data_reg, data_reg));
+ }
+ } else if (s_bits == 3) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ data_reg, data_reg, 0xb));
+ } else {
+ if (s_bits == 1) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ data_reg, data_reg, 15, 15));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ data_reg, data_reg, 31, 31));
+ }
+ if (opc == s_bits) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ data_reg, data_reg, 0xb));
+ } else {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ data_reg, data_reg, 0xb),
+ tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits],
+ data_reg, data_reg));
+ }
+ }
+#else
+ if (GUEST_BASE != 0) {
+ tcg_out_bundle(s, MmI,
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2,
+ TCG_GUEST_BASE_REG, addr_reg),
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, TCG_REG_R2),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ } else {
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits],
+ data_reg, addr_reg),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+
+ if (bswap && s_bits == 1) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ data_reg, data_reg, 15, 15),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ data_reg, data_reg, 0xb));
+ } else if (bswap && s_bits == 2) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ data_reg, data_reg, 31, 31),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ data_reg, data_reg, 0xb));
+ } else if (bswap && s_bits == 3) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ data_reg, data_reg, 0xb));
+ }
+ if (s_bits != opc) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits],
+ data_reg, data_reg));
+ }
+#endif
+}
+
+static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
+{
+ static uint64_t const opc_st_m4[4] = {
+ OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4
+ };
+ int addr_reg, data_reg, bswap;
+#if TARGET_LONG_BITS == 64
+ uint64_t add_guest_base;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 1;
+#else
+ bswap = 0;
+#endif
+
+#if TARGET_LONG_BITS == 32
+ if (GUEST_BASE != 0) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29,
+ TCG_REG_R3, addr_reg),
+ tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2,
+ TCG_GUEST_BASE_REG, TCG_REG_R3));
+ } else {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29,
+ TCG_REG_R2, addr_reg),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+
+ if (bswap) {
+ if (opc == 1) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ TCG_REG_R3, data_reg, 15, 15),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ TCG_REG_R3, TCG_REG_R3, 0xb));
+ data_reg = TCG_REG_R3;
+ } else if (opc == 2) {
+ tcg_out_bundle(s, mII,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ TCG_REG_R3, data_reg, 31, 31),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ TCG_REG_R3, TCG_REG_R3, 0xb));
+ data_reg = TCG_REG_R3;
+ } else if (opc == 3) {
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ TCG_REG_R3, data_reg, 0xb));
+ data_reg = TCG_REG_R3;
+ }
+ }
+ tcg_out_bundle(s, mmI,
+ tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc],
+ data_reg, TCG_REG_R2),
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+#else
+ if (GUEST_BASE != 0) {
+ add_guest_base = tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2,
+ TCG_GUEST_BASE_REG, addr_reg);
+ addr_reg = TCG_REG_R2;
+ } else {
+ add_guest_base = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0);
+ }
+
+ if (!bswap || opc == 0) {
+ tcg_out_bundle(s, (GUEST_BASE ? MmI : mmI),
+ add_guest_base,
+ tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc],
+ data_reg, addr_reg),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ } else {
+ if (opc == 1) {
+ tcg_out_bundle(s, mII,
+ add_guest_base,
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ TCG_REG_R3, data_reg, 15, 15),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ TCG_REG_R3, TCG_REG_R3, 0xb));
+ data_reg = TCG_REG_R3;
+ } else if (opc == 2) {
+ tcg_out_bundle(s, mII,
+ add_guest_base,
+ tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12,
+ TCG_REG_R3, data_reg, 31, 31),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ TCG_REG_R3, TCG_REG_R3, 0xb));
+ data_reg = TCG_REG_R3;
+ } else if (opc == 3) {
+ tcg_out_bundle(s, miI,
+ add_guest_base,
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3,
+ TCG_REG_R3, data_reg, 0xb));
+ data_reg = TCG_REG_R3;
+ }
+ tcg_out_bundle(s, miI,
+ tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc],
+ data_reg, addr_reg),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0),
+ tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0));
+ }
+#endif
+}
+
+#endif
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
+ const TCGArg *args, const int *const_args)
+{
+ switch(opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_exit_tb(s, args[0]);
+ break;
+ case INDEX_op_br:
+ tcg_out_br(s, args[0]);
+ break;
+ case INDEX_op_call:
+ tcg_out_call(s, args[0]);
+ break;
+ case INDEX_op_goto_tb:
+ tcg_out_goto_tb(s, args[0]);
+ break;
+ case INDEX_op_jmp:
+ tcg_out_jmp(s, args[0]);
+ break;
+
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_movi_i64:
+ tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+
+ case INDEX_op_ld8u_i32:
+ case INDEX_op_ld8u_i64:
+ tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld8s_i32:
+ case INDEX_op_ld8s_i64:
+ tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]);
+ tcg_out_ext(s, OPC_SXT1_I29, args[0], args[0]);
+ break;
+ case INDEX_op_ld16u_i32:
+ case INDEX_op_ld16u_i64:
+ tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld16s_i32:
+ case INDEX_op_ld16s_i64:
+ tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]);
+ tcg_out_ext(s, OPC_SXT2_I29, args[0], args[0]);
+ break;
+ case INDEX_op_ld_i32:
+ case INDEX_op_ld32u_i64:
+ tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld32s_i64:
+ tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]);
+ tcg_out_ext(s, OPC_SXT4_I29, args[0], args[0]);
+ break;
+ case INDEX_op_ld_i64:
+ tcg_out_ld_rel(s, OPC_LD8_M1, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st8_i32:
+ case INDEX_op_st8_i64:
+ tcg_out_st_rel(s, OPC_ST1_M4, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st16_i32:
+ case INDEX_op_st16_i64:
+ tcg_out_st_rel(s, OPC_ST2_M4, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st_i32:
+ case INDEX_op_st32_i64:
+ tcg_out_st_rel(s, OPC_ST4_M4, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st_i64:
+ tcg_out_st_rel(s, OPC_ST8_M4, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_add_i32:
+ case INDEX_op_add_i64:
+ tcg_out_alu(s, OPC_ADD_A1, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_sub_i32:
+ case INDEX_op_sub_i64:
+ tcg_out_alu(s, OPC_SUB_A1, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+
+ case INDEX_op_and_i32:
+ case INDEX_op_and_i64:
+ tcg_out_alu(s, OPC_AND_A1, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_andc_i32:
+ case INDEX_op_andc_i64:
+ tcg_out_alu(s, OPC_ANDCM_A1, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_eqv_i32:
+ case INDEX_op_eqv_i64:
+ tcg_out_eqv(s, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_nand_i32:
+ case INDEX_op_nand_i64:
+ tcg_out_nand(s, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_nor_i32:
+ case INDEX_op_nor_i64:
+ tcg_out_nor(s, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_or_i32:
+ case INDEX_op_or_i64:
+ tcg_out_alu(s, OPC_OR_A1, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_orc_i32:
+ case INDEX_op_orc_i64:
+ tcg_out_orc(s, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+ case INDEX_op_xor_i32:
+ case INDEX_op_xor_i64:
+ tcg_out_alu(s, OPC_XOR_A1, args[0], args[1], const_args[1],
+ args[2], const_args[2]);
+ break;
+
+ case INDEX_op_mul_i32:
+ case INDEX_op_mul_i64:
+ tcg_out_mul(s, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_sar_i32:
+ tcg_out_sar_i32(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_sar_i64:
+ tcg_out_sar_i64(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_shl_i32:
+ tcg_out_shl_i32(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_shl_i64:
+ tcg_out_shl_i64(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_shr_i32:
+ tcg_out_shr_i32(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_shr_i64:
+ tcg_out_shr_i64(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_rotl_i32:
+ tcg_out_rotl_i32(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_rotl_i64:
+ tcg_out_rotl_i64(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_rotr_i32:
+ tcg_out_rotr_i32(s, args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_rotr_i64:
+ tcg_out_rotr_i64(s, args[0], args[1], args[2], const_args[2]);
+ break;
+
+ case INDEX_op_ext8s_i32:
+ case INDEX_op_ext8s_i64:
+ tcg_out_ext(s, OPC_SXT1_I29, args[0], args[1]);
+ break;
+ case INDEX_op_ext8u_i32:
+ case INDEX_op_ext8u_i64:
+ tcg_out_ext(s, OPC_ZXT1_I29, args[0], args[1]);
+ break;
+ case INDEX_op_ext16s_i32:
+ case INDEX_op_ext16s_i64:
+ tcg_out_ext(s, OPC_SXT2_I29, args[0], args[1]);
+ break;
+ case INDEX_op_ext16u_i32:
+ case INDEX_op_ext16u_i64:
+ tcg_out_ext(s, OPC_ZXT2_I29, args[0], args[1]);
+ break;
+ case INDEX_op_ext32s_i64:
+ tcg_out_ext(s, OPC_SXT4_I29, args[0], args[1]);
+ break;
+ case INDEX_op_ext32u_i64:
+ tcg_out_ext(s, OPC_ZXT4_I29, args[0], args[1]);
+ break;
+
+ case INDEX_op_bswap16_i32:
+ case INDEX_op_bswap16_i64:
+ tcg_out_bswap16(s, args[0], args[1]);
+ break;
+ case INDEX_op_bswap32_i32:
+ case INDEX_op_bswap32_i64:
+ tcg_out_bswap32(s, args[0], args[1]);
+ break;
+ case INDEX_op_bswap64_i64:
+ tcg_out_bswap64(s, args[0], args[1]);
+ break;
+
+ case INDEX_op_brcond_i32:
+ tcg_out_brcond(s, args[2], args[0], const_args[0],
+ args[1], const_args[1], args[3], 1);
+ break;
+ case INDEX_op_brcond_i64:
+ tcg_out_brcond(s, args[2], args[0], const_args[0],
+ args[1], const_args[1], args[3], 0);
+ break;
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1);
+ break;
+ case INDEX_op_setcond_i64:
+ tcg_out_setcond(s, args[3], args[0], args[1], args[2], 0);
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+ case INDEX_op_qemu_ld32u:
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+ case INDEX_op_qemu_ld32s:
+ tcg_out_qemu_ld(s, args, 2 | 4);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+ default:
+ tcg_abort();
+ }
+}
+
+static const TCGTargetOpDef ia64_op_defs[] = {
+ { INDEX_op_br, { } },
+ { INDEX_op_call, { "r" } },
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_jmp, { "r" } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "rZ", "r" } },
+ { INDEX_op_st16_i32, { "rZ", "r" } },
+ { INDEX_op_st_i32, { "rZ", "r" } },
+
+ { INDEX_op_add_i32, { "r", "rI", "rI" } },
+ { INDEX_op_sub_i32, { "r", "rI", "rI" } },
+
+ { INDEX_op_and_i32, { "r", "rI", "rI" } },
+ { INDEX_op_andc_i32, { "r", "rI", "rI" } },
+ { INDEX_op_eqv_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_nand_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_nor_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_or_i32, { "r", "rI", "rI" } },
+ { INDEX_op_orc_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_xor_i32, { "r", "rI", "rI" } },
+
+ { INDEX_op_mul_i32, { "r", "rZ", "rZ" } },
+
+ { INDEX_op_sar_i32, { "r", "rZ", "ri" } },
+ { INDEX_op_shl_i32, { "r", "rZ", "ri" } },
+ { INDEX_op_shr_i32, { "r", "rZ", "ri" } },
+ { INDEX_op_rotl_i32, { "r", "rZ", "ri" } },
+ { INDEX_op_rotr_i32, { "r", "rZ", "ri" } },
+
+ { INDEX_op_ext8s_i32, { "r", "rZ"} },
+ { INDEX_op_ext8u_i32, { "r", "rZ"} },
+ { INDEX_op_ext16s_i32, { "r", "rZ"} },
+ { INDEX_op_ext16u_i32, { "r", "rZ"} },
+
+ { INDEX_op_bswap16_i32, { "r", "rZ" } },
+ { INDEX_op_bswap32_i32, { "r", "rZ" } },
+
+ { INDEX_op_brcond_i32, { "rI", "rI" } },
+ { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } },
+
+ { INDEX_op_mov_i64, { "r", "r" } },
+ { INDEX_op_movi_i64, { "r" } },
+
+ { INDEX_op_ld8u_i64, { "r", "r" } },
+ { INDEX_op_ld8s_i64, { "r", "r" } },
+ { INDEX_op_ld16u_i64, { "r", "r" } },
+ { INDEX_op_ld16s_i64, { "r", "r" } },
+ { INDEX_op_ld32u_i64, { "r", "r" } },
+ { INDEX_op_ld32s_i64, { "r", "r" } },
+ { INDEX_op_ld_i64, { "r", "r" } },
+ { INDEX_op_st8_i64, { "rZ", "r" } },
+ { INDEX_op_st16_i64, { "rZ", "r" } },
+ { INDEX_op_st32_i64, { "rZ", "r" } },
+ { INDEX_op_st_i64, { "rZ", "r" } },
+
+ { INDEX_op_add_i64, { "r", "rI", "rI" } },
+ { INDEX_op_sub_i64, { "r", "rI", "rI" } },
+
+ { INDEX_op_and_i64, { "r", "rI", "rI" } },
+ { INDEX_op_andc_i64, { "r", "rI", "rI" } },
+ { INDEX_op_eqv_i64, { "r", "rZ", "rZ" } },
+ { INDEX_op_nand_i64, { "r", "rZ", "rZ" } },
+ { INDEX_op_nor_i64, { "r", "rZ", "rZ" } },
+ { INDEX_op_or_i64, { "r", "rI", "rI" } },
+ { INDEX_op_orc_i64, { "r", "rZ", "rZ" } },
+ { INDEX_op_xor_i64, { "r", "rI", "rI" } },
+
+ { INDEX_op_mul_i64, { "r", "rZ", "rZ" } },
+
+ { INDEX_op_sar_i64, { "r", "rZ", "ri" } },
+ { INDEX_op_shl_i64, { "r", "rZ", "ri" } },
+ { INDEX_op_shr_i64, { "r", "rZ", "ri" } },
+ { INDEX_op_rotl_i64, { "r", "rZ", "ri" } },
+ { INDEX_op_rotr_i64, { "r", "rZ", "ri" } },
+
+ { INDEX_op_ext8s_i64, { "r", "rZ"} },
+ { INDEX_op_ext8u_i64, { "r", "rZ"} },
+ { INDEX_op_ext16s_i64, { "r", "rZ"} },
+ { INDEX_op_ext16u_i64, { "r", "rZ"} },
+ { INDEX_op_ext32s_i64, { "r", "rZ"} },
+ { INDEX_op_ext32u_i64, { "r", "rZ"} },
+
+ { INDEX_op_bswap16_i64, { "r", "rZ" } },
+ { INDEX_op_bswap32_i64, { "r", "rZ" } },
+ { INDEX_op_bswap64_i64, { "r", "rZ" } },
+
+ { INDEX_op_brcond_i64, { "rI", "rI" } },
+ { INDEX_op_setcond_i64, { "r", "rZ", "rZ" } },
+
+ { INDEX_op_qemu_ld8u, { "r", "r" } },
+ { INDEX_op_qemu_ld8s, { "r", "r" } },
+ { INDEX_op_qemu_ld16u, { "r", "r" } },
+ { INDEX_op_qemu_ld16s, { "r", "r" } },
+ { INDEX_op_qemu_ld32, { "r", "r" } },
+ { INDEX_op_qemu_ld32u, { "r", "r" } },
+ { INDEX_op_qemu_ld32s, { "r", "r" } },
+ { INDEX_op_qemu_ld64, { "r", "r" } },
+
+ { INDEX_op_qemu_st8, { "SZ", "r" } },
+ { INDEX_op_qemu_st16, { "SZ", "r" } },
+ { INDEX_op_qemu_st32, { "SZ", "r" } },
+ { INDEX_op_qemu_st64, { "SZ", "r" } },
+
+ { -1 },
+};
+
+/* Generate global QEMU prologue and epilogue code */
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ int frame_size;
+
+ /* reserve some stack space */
+ frame_size = TCG_STATIC_CALL_ARGS_SIZE;
+ frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) &
+ ~(TCG_TARGET_STACK_ALIGN - 1);
+
+ /* First emit adhoc function descriptor */
+ *(uint64_t *)(s->code_ptr) = (uint64_t)s->code_ptr + 16; /* entry point */
+ s->code_ptr += 16; /* skip GP */
+
+ /* prologue */
+ tcg_out_bundle(s, mII,
+ tcg_opc_m34(TCG_REG_P0, OPC_ALLOC_M34,
+ TCG_REG_R33, 32, 24, 0),
+ tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21,
+ TCG_REG_B6, TCG_REG_R32, 0),
+ tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22,
+ TCG_REG_R32, TCG_REG_B0));
+
+ /* ??? If GUEST_BASE < 0x200000, we could load the register via
+ an ADDL in the M slot of the next bundle. */
+ if (GUEST_BASE != 0) {
+ tcg_out_bundle(s, mlx,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_l2 (GUEST_BASE),
+ tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2,
+ TCG_GUEST_BASE_REG, GUEST_BASE));
+ tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG);
+ }
+
+ tcg_out_bundle(s, miB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4,
+ TCG_REG_R12, -frame_size, TCG_REG_R12),
+ tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6));
+
+ /* epilogue */
+ tb_ret_addr = s->code_ptr;
+ tcg_out_bundle(s, miI,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21,
+ TCG_REG_B0, TCG_REG_R32, 0),
+ tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4,
+ TCG_REG_R12, frame_size, TCG_REG_R12));
+ tcg_out_bundle(s, miB,
+ tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0),
+ tcg_opc_i26(TCG_REG_P0, OPC_MOV_I_I26,
+ TCG_REG_PFS, TCG_REG_R33),
+ tcg_opc_b4 (TCG_REG_P0, OPC_BR_RET_SPTK_MANY_B4,
+ TCG_REG_B0));
+}
+
+static void tcg_target_init(TCGContext *s)
+{
+ tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32],
+ 0xffffffffffffffffull);
+ tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I64],
+ 0xffffffffffffffffull);
+
+ tcg_regset_clear(tcg_target_call_clobber_regs);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R15);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R16);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R17);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R18);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R19);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R27);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R28);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R29);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R30);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R31);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R56);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R57);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R58);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R59);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R60);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R61);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R62);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R63);
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* zero register */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* global pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* internal use */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* internal use */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R12); /* stack pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R32); /* return address */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R33); /* PFS */
+
+ /* The following 3 are not in use, are call-saved, but *not* saved
+ by the prologue. Therefore we cannot use them without modifying
+ the prologue. There doesn't seem to be any good reason to use
+ these as opposed to the windowed registers. */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R4);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R5);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6);
+
+ tcg_add_target_add_op_defs(ia64_op_defs);
+}
diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h
new file mode 100644
index 0000000..e56e88f
--- /dev/null
+++ b/tcg/ia64/tcg-target.h
@@ -0,0 +1,156 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2009-2010 Aurelien Jarno <aurelien@aurel32.net>
+ * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_IA64 1
+
+#define TCG_TARGET_REG_BITS 64
+
+/* We only map the first 64 registers */
+#define TCG_TARGET_NB_REGS 64
+enum {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_R27,
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31,
+ TCG_REG_R32,
+ TCG_REG_R33,
+ TCG_REG_R34,
+ TCG_REG_R35,
+ TCG_REG_R36,
+ TCG_REG_R37,
+ TCG_REG_R38,
+ TCG_REG_R39,
+ TCG_REG_R40,
+ TCG_REG_R41,
+ TCG_REG_R42,
+ TCG_REG_R43,
+ TCG_REG_R44,
+ TCG_REG_R45,
+ TCG_REG_R46,
+ TCG_REG_R47,
+ TCG_REG_R48,
+ TCG_REG_R49,
+ TCG_REG_R50,
+ TCG_REG_R51,
+ TCG_REG_R52,
+ TCG_REG_R53,
+ TCG_REG_R54,
+ TCG_REG_R55,
+ TCG_REG_R56,
+ TCG_REG_R57,
+ TCG_REG_R58,
+ TCG_REG_R59,
+ TCG_REG_R60,
+ TCG_REG_R61,
+ TCG_REG_R62,
+ TCG_REG_R63,
+};
+
+#define TCG_CT_CONST_ZERO 0x100
+#define TCG_CT_CONST_S22 0x200
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R12
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_CALL_STACK_OFFSET 16
+
+/* optional instructions */
+#define TCG_TARGET_HAS_andc_i32
+#define TCG_TARGET_HAS_andc_i64
+#define TCG_TARGET_HAS_bswap16_i32
+#define TCG_TARGET_HAS_bswap16_i64
+#define TCG_TARGET_HAS_bswap32_i32
+#define TCG_TARGET_HAS_bswap32_i64
+#define TCG_TARGET_HAS_bswap64_i64
+#define TCG_TARGET_HAS_eqv_i32
+#define TCG_TARGET_HAS_eqv_i64
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_ext8s_i64
+#define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+#define TCG_TARGET_HAS_ext8u_i32
+#define TCG_TARGET_HAS_ext16u_i32
+#define TCG_TARGET_HAS_ext8u_i64
+#define TCG_TARGET_HAS_ext16u_i64
+#define TCG_TARGET_HAS_ext32u_i64
+#define TCG_TARGET_HAS_nand_i32
+#define TCG_TARGET_HAS_nand_i64
+#define TCG_TARGET_HAS_nor_i32
+#define TCG_TARGET_HAS_nor_i64
+#define TCG_TARGET_HAS_orc_i32
+#define TCG_TARGET_HAS_orc_i64
+#define TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_rot_i64
+
+/* optional instructions automatically implemented */
+#undef TCG_TARGET_HAS_neg_i32 /* sub r1, r0, r3 */
+#undef TCG_TARGET_HAS_neg_i64 /* sub r1, r0, r3 */
+#undef TCG_TARGET_HAS_not_i32 /* xor r1, -1, r3 */
+#undef TCG_TARGET_HAS_not_i64 /* xor r1, -1, r3 */
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_R7
+
+/* Guest base is supported */
+#define TCG_TARGET_HAS_GUEST_BASE
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ start = start & ~(32UL - 1UL);
+ stop = (stop + (32UL - 1UL)) & ~(32UL - 1UL);
+
+ for (; start < stop; start += 32UL) {
+ asm volatile ("fc.i %0" :: "r" (start));
+ }
+ asm volatile (";;sync.i;;srlz.i;;");
+}
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
new file mode 100644
index 0000000..e04b0dc
--- /dev/null
+++ b/tcg/mips/tcg-target.c
@@ -0,0 +1,1533 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net>
+ * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if defined(TCG_TARGET_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+# define TCG_NEED_BSWAP 0
+#else
+# define TCG_NEED_BSWAP 1
+#endif
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "zero",
+ "at",
+ "v0",
+ "v1",
+ "a0",
+ "a1",
+ "a2",
+ "a3",
+ "t0",
+ "t1",
+ "t2",
+ "t3",
+ "t4",
+ "t5",
+ "t6",
+ "t7",
+ "s0",
+ "s1",
+ "s2",
+ "s3",
+ "s4",
+ "s5",
+ "s6",
+ "s7",
+ "t8",
+ "t9",
+ "k0",
+ "k1",
+ "gp",
+ "sp",
+ "fp",
+ "ra",
+};
+#endif
+
+/* check if we really need so many registers :P */
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_S0,
+ TCG_REG_S1,
+ TCG_REG_S2,
+ TCG_REG_S3,
+ TCG_REG_S4,
+ TCG_REG_S5,
+ TCG_REG_S6,
+ TCG_REG_S7,
+ TCG_REG_T1,
+ TCG_REG_T2,
+ TCG_REG_T3,
+ TCG_REG_T4,
+ TCG_REG_T5,
+ TCG_REG_T6,
+ TCG_REG_T7,
+ TCG_REG_T8,
+ TCG_REG_T9,
+ TCG_REG_A0,
+ TCG_REG_A1,
+ TCG_REG_A2,
+ TCG_REG_A3,
+ TCG_REG_V0,
+ TCG_REG_V1
+};
+
+static const int tcg_target_call_iarg_regs[4] = {
+ TCG_REG_A0,
+ TCG_REG_A1,
+ TCG_REG_A2,
+ TCG_REG_A3
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_V0,
+ TCG_REG_V1
+};
+
+static uint8_t *tb_ret_addr;
+
+static inline uint32_t reloc_lo16_val (void *pc, tcg_target_long target)
+{
+ return target & 0xffff;
+}
+
+static inline void reloc_lo16 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff)
+ | reloc_lo16_val(pc, target);
+}
+
+static inline uint32_t reloc_hi16_val (void *pc, tcg_target_long target)
+{
+ return (target >> 16) & 0xffff;
+}
+
+static inline void reloc_hi16 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff)
+ | reloc_hi16_val(pc, target);
+}
+
+static inline uint32_t reloc_pc16_val (void *pc, tcg_target_long target)
+{
+ int32_t disp;
+
+ disp = target - (tcg_target_long) pc - 4;
+ if (disp != (disp << 14) >> 14) {
+ tcg_abort ();
+ }
+
+ return (disp >> 2) & 0xffff;
+}
+
+static inline void reloc_pc16 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff)
+ | reloc_pc16_val(pc, target);
+}
+
+static inline uint32_t reloc_26_val (void *pc, tcg_target_long target)
+{
+ if ((((tcg_target_long)pc + 4) & 0xf0000000) != (target & 0xf0000000)) {
+ tcg_abort ();
+ }
+
+ return (target >> 2) & 0x3ffffff;
+}
+
+static inline void reloc_pc26 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3ffffff)
+ | reloc_26_val(pc, target);
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ value += addend;
+ switch(type) {
+ case R_MIPS_LO16:
+ reloc_lo16(code_ptr, value);
+ break;
+ case R_MIPS_HI16:
+ reloc_hi16(code_ptr, value);
+ break;
+ case R_MIPS_PC16:
+ reloc_pc16(code_ptr, value);
+ break;
+ case R_MIPS_26:
+ reloc_pc26(code_ptr, value);
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return 4;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch(ct_str[0]) {
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set(ct->u.regs, 0xffffffff);
+ break;
+ case 'C':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_clear(ct->u.regs);
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_T9);
+ break;
+ case 'L': /* qemu_ld output arg constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set(ct->u.regs, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_V0);
+ break;
+ case 'l': /* qemu_ld input arg constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set(ct->u.regs, 0xffffffff);
+#if defined(CONFIG_SOFTMMU)
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0);
+#endif
+ break;
+ case 'S': /* qemu_st constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set(ct->u.regs, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0);
+#if defined(CONFIG_SOFTMMU)
+# if TARGET_LONG_BITS == 64
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1);
+# endif
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2);
+#endif
+ break;
+ case 'I':
+ ct->ct |= TCG_CT_CONST_U16;
+ break;
+ case 'J':
+ ct->ct |= TCG_CT_CONST_S16;
+ break;
+ case 'Z':
+ /* We are cheating a bit here, using the fact that the register
+ ZERO is also the register number 0. Hence there is no need
+ to check for const_args in each instruction. */
+ ct->ct |= TCG_CT_CONST_ZERO;
+ break;
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct;
+ ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST)
+ return 1;
+ else if ((ct & TCG_CT_CONST_ZERO) && val == 0)
+ return 1;
+ else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val)
+ return 1;
+ else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val)
+ return 1;
+ else
+ return 0;
+}
+
+/* instruction opcodes */
+enum {
+ OPC_BEQ = 0x04 << 26,
+ OPC_BNE = 0x05 << 26,
+ OPC_ADDIU = 0x09 << 26,
+ OPC_SLTI = 0x0A << 26,
+ OPC_SLTIU = 0x0B << 26,
+ OPC_ANDI = 0x0C << 26,
+ OPC_ORI = 0x0D << 26,
+ OPC_XORI = 0x0E << 26,
+ OPC_LUI = 0x0F << 26,
+ OPC_LB = 0x20 << 26,
+ OPC_LH = 0x21 << 26,
+ OPC_LW = 0x23 << 26,
+ OPC_LBU = 0x24 << 26,
+ OPC_LHU = 0x25 << 26,
+ OPC_LWU = 0x27 << 26,
+ OPC_SB = 0x28 << 26,
+ OPC_SH = 0x29 << 26,
+ OPC_SW = 0x2B << 26,
+
+ OPC_SPECIAL = 0x00 << 26,
+ OPC_SLL = OPC_SPECIAL | 0x00,
+ OPC_SRL = OPC_SPECIAL | 0x02,
+ OPC_SRA = OPC_SPECIAL | 0x03,
+ OPC_SLLV = OPC_SPECIAL | 0x04,
+ OPC_SRLV = OPC_SPECIAL | 0x06,
+ OPC_SRAV = OPC_SPECIAL | 0x07,
+ OPC_JR = OPC_SPECIAL | 0x08,
+ OPC_JALR = OPC_SPECIAL | 0x09,
+ OPC_MFHI = OPC_SPECIAL | 0x10,
+ OPC_MFLO = OPC_SPECIAL | 0x12,
+ OPC_MULT = OPC_SPECIAL | 0x18,
+ OPC_MULTU = OPC_SPECIAL | 0x19,
+ OPC_DIV = OPC_SPECIAL | 0x1A,
+ OPC_DIVU = OPC_SPECIAL | 0x1B,
+ OPC_ADDU = OPC_SPECIAL | 0x21,
+ OPC_SUBU = OPC_SPECIAL | 0x23,
+ OPC_AND = OPC_SPECIAL | 0x24,
+ OPC_OR = OPC_SPECIAL | 0x25,
+ OPC_XOR = OPC_SPECIAL | 0x26,
+ OPC_NOR = OPC_SPECIAL | 0x27,
+ OPC_SLT = OPC_SPECIAL | 0x2A,
+ OPC_SLTU = OPC_SPECIAL | 0x2B,
+
+ OPC_SPECIAL3 = 0x1f << 26,
+ OPC_SEB = OPC_SPECIAL3 | 0x420,
+ OPC_SEH = OPC_SPECIAL3 | 0x620,
+};
+
+/*
+ * Type reg
+ */
+static inline void tcg_out_opc_reg(TCGContext *s, int opc, int rd, int rs, int rt)
+{
+ int32_t inst;
+
+ inst = opc;
+ inst |= (rs & 0x1F) << 21;
+ inst |= (rt & 0x1F) << 16;
+ inst |= (rd & 0x1F) << 11;
+ tcg_out32(s, inst);
+}
+
+/*
+ * Type immediate
+ */
+static inline void tcg_out_opc_imm(TCGContext *s, int opc, int rt, int rs, int imm)
+{
+ int32_t inst;
+
+ inst = opc;
+ inst |= (rs & 0x1F) << 21;
+ inst |= (rt & 0x1F) << 16;
+ inst |= (imm & 0xffff);
+ tcg_out32(s, inst);
+}
+
+/*
+ * Type branch
+ */
+static inline void tcg_out_opc_br(TCGContext *s, int opc, int rt, int rs)
+{
+ /* We pay attention here to not modify the branch target by reading
+ the existing value and using it again. This ensure that caches and
+ memory are kept coherent during retranslation. */
+ uint16_t offset = (uint16_t)(*(uint32_t *) s->code_ptr);
+
+ tcg_out_opc_imm(s, opc, rt, rs, offset);
+}
+
+/*
+ * Type sa
+ */
+static inline void tcg_out_opc_sa(TCGContext *s, int opc, int rd, int rt, int sa)
+{
+ int32_t inst;
+
+ inst = opc;
+ inst |= (rt & 0x1F) << 16;
+ inst |= (rd & 0x1F) << 11;
+ inst |= (sa & 0x1F) << 6;
+ tcg_out32(s, inst);
+
+}
+
+static inline void tcg_out_nop(TCGContext *s)
+{
+ tcg_out32(s, 0);
+}
+
+static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg)
+{
+ tcg_out_opc_reg(s, OPC_ADDU, ret, arg, TCG_REG_ZERO);
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+ int reg, int32_t arg)
+{
+ if (arg == (int16_t)arg) {
+ tcg_out_opc_imm(s, OPC_ADDIU, reg, TCG_REG_ZERO, arg);
+ } else if (arg == (uint16_t)arg) {
+ tcg_out_opc_imm(s, OPC_ORI, reg, TCG_REG_ZERO, arg);
+ } else {
+ tcg_out_opc_imm(s, OPC_LUI, reg, 0, arg >> 16);
+ tcg_out_opc_imm(s, OPC_ORI, reg, reg, arg & 0xffff);
+ }
+}
+
+static inline void tcg_out_bswap16(TCGContext *s, int ret, int arg)
+{
+ /* ret and arg can't be register at */
+ if (ret == TCG_REG_AT || arg == TCG_REG_AT) {
+ tcg_abort();
+ }
+
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0x00ff);
+
+ tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8);
+ tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+}
+
+static inline void tcg_out_bswap16s(TCGContext *s, int ret, int arg)
+{
+ /* ret and arg can't be register at */
+ if (ret == TCG_REG_AT || arg == TCG_REG_AT) {
+ tcg_abort();
+ }
+
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff);
+
+ tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24);
+ tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+}
+
+static inline void tcg_out_bswap32(TCGContext *s, int ret, int arg)
+{
+ /* ret and arg must be different and can't be register at */
+ if (ret == arg || ret == TCG_REG_AT || arg == TCG_REG_AT) {
+ tcg_abort();
+ }
+
+ tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24);
+
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 24);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, arg, 0xff00);
+ tcg_out_opc_sa(s, OPC_SLL, TCG_REG_AT, TCG_REG_AT, 8);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff00);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+}
+
+static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg)
+{
+#ifdef _MIPS_ARCH_MIPS32R2
+ tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg);
+#else
+ tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24);
+ tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24);
+#endif
+}
+
+static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg)
+{
+#ifdef _MIPS_ARCH_MIPS32R2
+ tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg);
+#else
+ tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16);
+ tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16);
+#endif
+}
+
+static inline void tcg_out_ldst(TCGContext *s, int opc, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ if (arg2 == (int16_t) arg2) {
+ tcg_out_opc_imm(s, opc, arg, arg1, arg2);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, arg2);
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_AT, TCG_REG_AT, arg1);
+ tcg_out_opc_imm(s, opc, arg, TCG_REG_AT, 0);
+ }
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ tcg_out_ldst(s, OPC_LW, arg, arg1, arg2);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ tcg_out_ldst(s, OPC_SW, arg, arg1, arg2);
+}
+
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ if (val == (int16_t)val) {
+ tcg_out_opc_imm(s, OPC_ADDIU, reg, reg, val);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, val);
+ tcg_out_opc_reg(s, OPC_ADDU, reg, reg, TCG_REG_AT);
+ }
+}
+
+static void tcg_out_brcond(TCGContext *s, TCGCond cond, int arg1,
+ int arg2, int label_index)
+{
+ TCGLabel *l = &s->labels[label_index];
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ tcg_out_opc_br(s, OPC_BEQ, arg1, arg2);
+ break;
+ case TCG_COND_NE:
+ tcg_out_opc_br(s, OPC_BNE, arg1, arg2);
+ break;
+ case TCG_COND_LT:
+ tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg1, arg2);
+ tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_LTU:
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg1, arg2);
+ tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_GE:
+ tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg1, arg2);
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_GEU:
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg1, arg2);
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_LE:
+ tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg2, arg1);
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_LEU:
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg2, arg1);
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_GT:
+ tcg_out_opc_reg(s, OPC_SLT, TCG_REG_AT, arg2, arg1);
+ tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ case TCG_COND_GTU:
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_AT, arg2, arg1);
+ tcg_out_opc_br(s, OPC_BNE, TCG_REG_AT, TCG_REG_ZERO);
+ break;
+ default:
+ tcg_abort();
+ break;
+ }
+ if (l->has_value) {
+ reloc_pc16(s->code_ptr - 4, l->u.value);
+ } else {
+ tcg_out_reloc(s, s->code_ptr - 4, R_MIPS_PC16, label_index, 0);
+ }
+ tcg_out_nop(s);
+}
+
+/* XXX: we implement it at the target level to avoid having to
+ handle cross basic blocks temporaries */
+static void tcg_out_brcond2(TCGContext *s, TCGCond cond, int arg1,
+ int arg2, int arg3, int arg4, int label_index)
+{
+ void *label_ptr;
+
+ switch(cond) {
+ case TCG_COND_NE:
+ tcg_out_brcond(s, TCG_COND_NE, arg2, arg4, label_index);
+ tcg_out_brcond(s, TCG_COND_NE, arg1, arg3, label_index);
+ return;
+ case TCG_COND_EQ:
+ break;
+ case TCG_COND_LT:
+ case TCG_COND_LE:
+ tcg_out_brcond(s, TCG_COND_LT, arg2, arg4, label_index);
+ break;
+ case TCG_COND_GT:
+ case TCG_COND_GE:
+ tcg_out_brcond(s, TCG_COND_GT, arg2, arg4, label_index);
+ break;
+ case TCG_COND_LTU:
+ case TCG_COND_LEU:
+ tcg_out_brcond(s, TCG_COND_LTU, arg2, arg4, label_index);
+ break;
+ case TCG_COND_GTU:
+ case TCG_COND_GEU:
+ tcg_out_brcond(s, TCG_COND_GTU, arg2, arg4, label_index);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ label_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BNE, arg2, arg4);
+ tcg_out_nop(s);
+
+ switch(cond) {
+ case TCG_COND_EQ:
+ tcg_out_brcond(s, TCG_COND_EQ, arg1, arg3, label_index);
+ break;
+ case TCG_COND_LT:
+ case TCG_COND_LTU:
+ tcg_out_brcond(s, TCG_COND_LTU, arg1, arg3, label_index);
+ break;
+ case TCG_COND_LE:
+ case TCG_COND_LEU:
+ tcg_out_brcond(s, TCG_COND_LEU, arg1, arg3, label_index);
+ break;
+ case TCG_COND_GT:
+ case TCG_COND_GTU:
+ tcg_out_brcond(s, TCG_COND_GTU, arg1, arg3, label_index);
+ break;
+ case TCG_COND_GE:
+ case TCG_COND_GEU:
+ tcg_out_brcond(s, TCG_COND_GEU, arg1, arg3, label_index);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ reloc_pc16(label_ptr, (tcg_target_long) s->code_ptr);
+}
+
+static void tcg_out_setcond(TCGContext *s, TCGCond cond, int ret,
+ int arg1, int arg2)
+{
+ switch (cond) {
+ case TCG_COND_EQ:
+ if (arg1 == 0) {
+ tcg_out_opc_imm(s, OPC_SLTIU, ret, arg2, 1);
+ } else if (arg2 == 0) {
+ tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1);
+ } else {
+ tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2);
+ tcg_out_opc_imm(s, OPC_SLTIU, ret, ret, 1);
+ }
+ break;
+ case TCG_COND_NE:
+ if (arg1 == 0) {
+ tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg2);
+ } else if (arg2 == 0) {
+ tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1);
+ } else {
+ tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2);
+ tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, ret);
+ }
+ break;
+ case TCG_COND_LT:
+ tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2);
+ break;
+ case TCG_COND_LTU:
+ tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2);
+ break;
+ case TCG_COND_GE:
+ tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2);
+ tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
+ break;
+ case TCG_COND_GEU:
+ tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2);
+ tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
+ break;
+ case TCG_COND_LE:
+ tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1);
+ tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
+ break;
+ case TCG_COND_LEU:
+ tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1);
+ tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1);
+ break;
+ case TCG_COND_GT:
+ tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1);
+ break;
+ case TCG_COND_GTU:
+ tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1);
+ break;
+ default:
+ tcg_abort();
+ break;
+ }
+}
+
+/* XXX: we implement it at the target level to avoid having to
+ handle cross basic blocks temporaries */
+static void tcg_out_setcond2(TCGContext *s, TCGCond cond, int ret,
+ int arg1, int arg2, int arg3, int arg4)
+{
+ switch (cond) {
+ case TCG_COND_EQ:
+ tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_AT, arg2, arg4);
+ tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg1, arg3);
+ tcg_out_opc_reg(s, OPC_AND, ret, TCG_REG_AT, TCG_REG_T0);
+ return;
+ case TCG_COND_NE:
+ tcg_out_setcond(s, TCG_COND_NE, TCG_REG_AT, arg2, arg4);
+ tcg_out_setcond(s, TCG_COND_NE, TCG_REG_T0, arg1, arg3);
+ tcg_out_opc_reg(s, OPC_OR, ret, TCG_REG_AT, TCG_REG_T0);
+ return;
+ case TCG_COND_LT:
+ case TCG_COND_LE:
+ tcg_out_setcond(s, TCG_COND_LT, TCG_REG_AT, arg2, arg4);
+ break;
+ case TCG_COND_GT:
+ case TCG_COND_GE:
+ tcg_out_setcond(s, TCG_COND_GT, TCG_REG_AT, arg2, arg4);
+ break;
+ case TCG_COND_LTU:
+ case TCG_COND_LEU:
+ tcg_out_setcond(s, TCG_COND_LTU, TCG_REG_AT, arg2, arg4);
+ break;
+ case TCG_COND_GTU:
+ case TCG_COND_GEU:
+ tcg_out_setcond(s, TCG_COND_GTU, TCG_REG_AT, arg2, arg4);
+ break;
+ default:
+ tcg_abort();
+ break;
+ }
+
+ tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg2, arg4);
+
+ switch(cond) {
+ case TCG_COND_LT:
+ case TCG_COND_LTU:
+ tcg_out_setcond(s, TCG_COND_LTU, ret, arg1, arg3);
+ break;
+ case TCG_COND_LE:
+ case TCG_COND_LEU:
+ tcg_out_setcond(s, TCG_COND_LEU, ret, arg1, arg3);
+ break;
+ case TCG_COND_GT:
+ case TCG_COND_GTU:
+ tcg_out_setcond(s, TCG_COND_GTU, ret, arg1, arg3);
+ break;
+ case TCG_COND_GE:
+ case TCG_COND_GEU:
+ tcg_out_setcond(s, TCG_COND_GEU, ret, arg1, arg3);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ tcg_out_opc_reg(s, OPC_AND, ret, ret, TCG_REG_T0);
+ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+#endif
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+ int opc)
+{
+ int addr_regl, addr_reg1, addr_meml;
+ int data_regl, data_regh, data_reg1, data_reg2;
+ int mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+ void *label1_ptr, *label2_ptr;
+ int sp_args;
+#endif
+#if TARGET_LONG_BITS == 64
+# if defined(CONFIG_SOFTMMU)
+ uint8_t *label3_ptr;
+# endif
+ int addr_regh, addr_reg2, addr_memh;
+#endif
+ data_regl = *args++;
+ if (opc == 3)
+ data_regh = *args++;
+ else
+ data_regh = 0;
+ addr_regl = *args++;
+#if TARGET_LONG_BITS == 64
+ addr_regh = *args++;
+#endif
+ mem_index = *args;
+ s_bits = opc & 3;
+
+ if (opc == 3) {
+#if defined(TCG_TARGET_WORDS_BIGENDIAN)
+ data_reg1 = data_regh;
+ data_reg2 = data_regl;
+#else
+ data_reg1 = data_regl;
+ data_reg2 = data_regh;
+#endif
+ } else {
+ data_reg1 = data_regl;
+ data_reg2 = 0;
+ }
+#if TARGET_LONG_BITS == 64
+# if defined(TCG_TARGET_WORDS_BIGENDIAN)
+ addr_reg1 = addr_regh;
+ addr_reg2 = addr_regl;
+ addr_memh = 0;
+ addr_meml = 4;
+# else
+ addr_reg1 = addr_regl;
+ addr_reg2 = addr_regh;
+ addr_memh = 4;
+ addr_meml = 0;
+# endif
+#else
+ addr_reg1 = addr_regl;
+ addr_meml = 0;
+#endif
+
+#if defined(CONFIG_SOFTMMU)
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0);
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
+ offsetof(CPUState, tlb_table[mem_index][0].addr_read) + addr_meml);
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+ tcg_out_opc_reg(s, OPC_AND, TCG_REG_T0, TCG_REG_T0, addr_regl);
+
+# if TARGET_LONG_BITS == 64
+ label3_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BNE, TCG_REG_T0, TCG_REG_AT);
+ tcg_out_nop(s);
+
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
+ offsetof(CPUState, tlb_table[mem_index][0].addr_read) + addr_memh);
+
+ label1_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BEQ, addr_regh, TCG_REG_AT);
+ tcg_out_nop(s);
+
+ reloc_pc16(label3_ptr, (tcg_target_long) s->code_ptr);
+# else
+ label1_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_T0, TCG_REG_AT);
+ tcg_out_nop(s);
+# endif
+
+ /* slow path */
+ sp_args = TCG_REG_A0;
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg1);
+# if TARGET_LONG_BITS == 64
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg2);
+# endif
+ tcg_out_movi(s, TCG_TYPE_I32, sp_args++, mem_index);
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_ld_helpers[s_bits]);
+ tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0);
+ tcg_out_nop(s);
+
+ switch(opc) {
+ case 0:
+ tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xff);
+ break;
+ case 0 | 4:
+ tcg_out_ext8s(s, data_reg1, TCG_REG_V0);
+ break;
+ case 1:
+ tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xffff);
+ break;
+ case 1 | 4:
+ tcg_out_ext16s(s, data_reg1, TCG_REG_V0);
+ break;
+ case 2:
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0);
+ break;
+ case 3:
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_V1);
+ tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0);
+ break;
+ default:
+ tcg_abort();
+ }
+
+ label2_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO);
+ tcg_out_nop(s);
+
+ /* label1: fast path */
+ reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr);
+
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0,
+ offsetof(CPUState, tlb_table[mem_index][0].addend));
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_A0, addr_regl);
+#else
+ if (GUEST_BASE == (int16_t)GUEST_BASE) {
+ tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_V0, addr_regl, GUEST_BASE);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, GUEST_BASE);
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_V0, addr_regl);
+ }
+#endif
+
+ switch(opc) {
+ case 0:
+ tcg_out_opc_imm(s, OPC_LBU, data_reg1, TCG_REG_V0, 0);
+ break;
+ case 0 | 4:
+ tcg_out_opc_imm(s, OPC_LB, data_reg1, TCG_REG_V0, 0);
+ break;
+ case 1:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0);
+ tcg_out_bswap16(s, data_reg1, TCG_REG_T0);
+ } else {
+ tcg_out_opc_imm(s, OPC_LHU, data_reg1, TCG_REG_V0, 0);
+ }
+ break;
+ case 1 | 4:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0);
+ tcg_out_bswap16s(s, data_reg1, TCG_REG_T0);
+ } else {
+ tcg_out_opc_imm(s, OPC_LH, data_reg1, TCG_REG_V0, 0);
+ }
+ break;
+ case 2:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0);
+ tcg_out_bswap32(s, data_reg1, TCG_REG_T0);
+ } else {
+ tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0);
+ }
+ break;
+ case 3:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 4);
+ tcg_out_bswap32(s, data_reg1, TCG_REG_T0);
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0);
+ tcg_out_bswap32(s, data_reg2, TCG_REG_T0);
+ } else {
+ tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0);
+ tcg_out_opc_imm(s, OPC_LW, data_reg2, TCG_REG_V0, 4);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+
+#if defined(CONFIG_SOFTMMU)
+ reloc_pc16(label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+ int opc)
+{
+ int addr_regl, addr_reg1, addr_meml;
+ int data_regl, data_regh, data_reg1, data_reg2;
+ int mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+ uint8_t *label1_ptr, *label2_ptr;
+ int sp_args;
+#endif
+#if TARGET_LONG_BITS == 64
+# if defined(CONFIG_SOFTMMU)
+ uint8_t *label3_ptr;
+# endif
+ int addr_regh, addr_reg2, addr_memh;
+#endif
+
+ data_regl = *args++;
+ if (opc == 3) {
+ data_regh = *args++;
+#if defined(TCG_TARGET_WORDS_BIGENDIAN)
+ data_reg1 = data_regh;
+ data_reg2 = data_regl;
+#else
+ data_reg1 = data_regl;
+ data_reg2 = data_regh;
+#endif
+ } else {
+ data_reg1 = data_regl;
+ data_reg2 = 0;
+ data_regh = 0;
+ }
+ addr_regl = *args++;
+#if TARGET_LONG_BITS == 64
+ addr_regh = *args++;
+# if defined(TCG_TARGET_WORDS_BIGENDIAN)
+ addr_reg1 = addr_regh;
+ addr_reg2 = addr_regl;
+ addr_memh = 0;
+ addr_meml = 4;
+# else
+ addr_reg1 = addr_regl;
+ addr_reg2 = addr_regh;
+ addr_memh = 4;
+ addr_meml = 0;
+# endif
+#else
+ addr_reg1 = addr_regl;
+ addr_meml = 0;
+#endif
+ mem_index = *args;
+ s_bits = opc;
+
+#if defined(CONFIG_SOFTMMU)
+ tcg_out_opc_sa(s, OPC_SRL, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+ tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, TCG_AREG0);
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
+ offsetof(CPUState, tlb_table[mem_index][0].addr_write) + addr_meml);
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+ tcg_out_opc_reg(s, OPC_AND, TCG_REG_T0, TCG_REG_T0, addr_regl);
+
+# if TARGET_LONG_BITS == 64
+ label3_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BNE, TCG_REG_T0, TCG_REG_AT);
+ tcg_out_nop(s);
+
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_AT, TCG_REG_A0,
+ offsetof(CPUState, tlb_table[mem_index][0].addr_write) + addr_memh);
+
+ label1_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BEQ, addr_regh, TCG_REG_AT);
+ tcg_out_nop(s);
+
+ reloc_pc16(label3_ptr, (tcg_target_long) s->code_ptr);
+# else
+ label1_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_T0, TCG_REG_AT);
+ tcg_out_nop(s);
+# endif
+
+ /* slow path */
+ sp_args = TCG_REG_A0;
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg1);
+# if TARGET_LONG_BITS == 64
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg2);
+# endif
+ switch(opc) {
+ case 0:
+ tcg_out_opc_imm(s, OPC_ANDI, sp_args++, data_reg1, 0xff);
+ break;
+ case 1:
+ tcg_out_opc_imm(s, OPC_ANDI, sp_args++, data_reg1, 0xffff);
+ break;
+ case 2:
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg1);
+ break;
+ case 3:
+ sp_args = (sp_args + 1) & ~1;
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg1);
+ tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg2);
+ break;
+ default:
+ tcg_abort();
+ }
+ if (sp_args > TCG_REG_A3) {
+ /* Push mem_index on the stack */
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, mem_index);
+ tcg_out_st(s, TCG_TYPE_I32, TCG_REG_AT, TCG_REG_SP, 16);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_I32, sp_args, mem_index);
+ }
+
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_st_helpers[s_bits]);
+ tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, TCG_REG_T9, 0);
+ tcg_out_nop(s);
+
+ label2_ptr = s->code_ptr;
+ tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO);
+ tcg_out_nop(s);
+
+ /* label1: fast path */
+ reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr);
+
+ tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0,
+ offsetof(CPUState, tlb_table[mem_index][0].addend));
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl);
+#else
+ if (GUEST_BASE == (int16_t)GUEST_BASE) {
+ tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, addr_regl, GUEST_BASE);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, GUEST_BASE);
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl);
+ }
+
+#endif
+
+ switch(opc) {
+ case 0:
+ tcg_out_opc_imm(s, OPC_SB, data_reg1, TCG_REG_A0, 0);
+ break;
+ case 1:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_bswap16(s, TCG_REG_T0, data_reg1);
+ tcg_out_opc_imm(s, OPC_SH, TCG_REG_T0, TCG_REG_A0, 0);
+ } else {
+ tcg_out_opc_imm(s, OPC_SH, data_reg1, TCG_REG_A0, 0);
+ }
+ break;
+ case 2:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_bswap32(s, TCG_REG_T0, data_reg1);
+ tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0);
+ } else {
+ tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0);
+ }
+ break;
+ case 3:
+ if (TCG_NEED_BSWAP) {
+ tcg_out_bswap32(s, TCG_REG_T0, data_reg2);
+ tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0);
+ tcg_out_bswap32(s, TCG_REG_T0, data_reg1);
+ tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 4);
+ } else {
+ tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0);
+ tcg_out_opc_imm(s, OPC_SW, data_reg2, TCG_REG_A0, 4);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+
+#if defined(CONFIG_SOFTMMU)
+ reloc_pc16(label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
+ const TCGArg *args, const int *const_args)
+{
+ switch(opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_V0, args[0]);
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, (tcg_target_long)tb_ret_addr);
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_AT, 0);
+ tcg_out_nop(s);
+ break;
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+ tcg_abort();
+ } else {
+ /* indirect jump method */
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, (tcg_target_long)(s->tb_next + args[0]));
+ tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_AT, TCG_REG_AT, 0);
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_AT, 0);
+ }
+ tcg_out_nop(s);
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+ case INDEX_op_call:
+ tcg_out_opc_reg(s, OPC_JALR, TCG_REG_RA, args[0], 0);
+ tcg_out_nop(s);
+ break;
+ case INDEX_op_jmp:
+ tcg_out_opc_reg(s, OPC_JR, 0, args[0], 0);
+ tcg_out_nop(s);
+ break;
+ case INDEX_op_br:
+ tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, args[0]);
+ break;
+
+ case INDEX_op_mov_i32:
+ tcg_out_mov(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+
+ case INDEX_op_ld8u_i32:
+ tcg_out_ldst(s, OPC_LBU, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld8s_i32:
+ tcg_out_ldst(s, OPC_LB, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld16u_i32:
+ tcg_out_ldst(s, OPC_LHU, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld16s_i32:
+ tcg_out_ldst(s, OPC_LH, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_ld_i32:
+ tcg_out_ldst(s, OPC_LW, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st8_i32:
+ tcg_out_ldst(s, OPC_SB, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st16_i32:
+ tcg_out_ldst(s, OPC_SH, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st_i32:
+ tcg_out_ldst(s, OPC_SW, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_add_i32:
+ if (const_args[2]) {
+ tcg_out_opc_imm(s, OPC_ADDIU, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_ADDU, args[0], args[1], args[2]);
+ }
+ break;
+ case INDEX_op_add2_i32:
+ if (const_args[4]) {
+ tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_AT, args[2], args[4]);
+ } else {
+ tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_AT, args[2], args[4]);
+ }
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_T0, TCG_REG_AT, args[2]);
+ if (const_args[5]) {
+ tcg_out_opc_imm(s, OPC_ADDIU, args[1], args[3], args[5]);
+ } else {
+ tcg_out_opc_reg(s, OPC_ADDU, args[1], args[3], args[5]);
+ }
+ tcg_out_opc_reg(s, OPC_ADDU, args[1], args[1], TCG_REG_T0);
+ tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT);
+ break;
+ case INDEX_op_sub_i32:
+ if (const_args[2]) {
+ tcg_out_opc_imm(s, OPC_ADDIU, args[0], args[1], -args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_SUBU, args[0], args[1], args[2]);
+ }
+ break;
+ case INDEX_op_sub2_i32:
+ if (const_args[4]) {
+ tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_AT, args[2], -args[4]);
+ } else {
+ tcg_out_opc_reg(s, OPC_SUBU, TCG_REG_AT, args[2], args[4]);
+ }
+ tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_T0, args[2], TCG_REG_AT);
+ if (const_args[5]) {
+ tcg_out_opc_imm(s, OPC_ADDIU, args[1], args[3], -args[5]);
+ } else {
+ tcg_out_opc_reg(s, OPC_SUBU, args[1], args[3], args[5]);
+ }
+ tcg_out_opc_reg(s, OPC_SUBU, args[1], args[1], TCG_REG_T0);
+ tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT);
+ break;
+ case INDEX_op_mul_i32:
+ tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]);
+ tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
+ break;
+ case INDEX_op_mulu2_i32:
+ tcg_out_opc_reg(s, OPC_MULTU, 0, args[2], args[3]);
+ tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
+ tcg_out_opc_reg(s, OPC_MFHI, args[1], 0, 0);
+ break;
+ case INDEX_op_div_i32:
+ tcg_out_opc_reg(s, OPC_DIV, 0, args[1], args[2]);
+ tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
+ break;
+ case INDEX_op_divu_i32:
+ tcg_out_opc_reg(s, OPC_DIVU, 0, args[1], args[2]);
+ tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0);
+ break;
+ case INDEX_op_rem_i32:
+ tcg_out_opc_reg(s, OPC_DIV, 0, args[1], args[2]);
+ tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0);
+ break;
+ case INDEX_op_remu_i32:
+ tcg_out_opc_reg(s, OPC_DIVU, 0, args[1], args[2]);
+ tcg_out_opc_reg(s, OPC_MFHI, args[0], 0, 0);
+ break;
+
+ case INDEX_op_and_i32:
+ if (const_args[2]) {
+ tcg_out_opc_imm(s, OPC_ANDI, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_AND, args[0], args[1], args[2]);
+ }
+ break;
+ case INDEX_op_or_i32:
+ if (const_args[2]) {
+ tcg_out_opc_imm(s, OPC_ORI, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_OR, args[0], args[1], args[2]);
+ }
+ break;
+ case INDEX_op_nor_i32:
+ tcg_out_opc_reg(s, OPC_NOR, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_not_i32:
+ tcg_out_opc_reg(s, OPC_NOR, args[0], TCG_REG_ZERO, args[1]);
+ break;
+ case INDEX_op_xor_i32:
+ if (const_args[2]) {
+ tcg_out_opc_imm(s, OPC_XORI, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_XOR, args[0], args[1], args[2]);
+ }
+ break;
+
+ case INDEX_op_sar_i32:
+ if (const_args[2]) {
+ tcg_out_opc_sa(s, OPC_SRA, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_SRAV, args[0], args[2], args[1]);
+ }
+ break;
+ case INDEX_op_shl_i32:
+ if (const_args[2]) {
+ tcg_out_opc_sa(s, OPC_SLL, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_SLLV, args[0], args[2], args[1]);
+ }
+ break;
+ case INDEX_op_shr_i32:
+ if (const_args[2]) {
+ tcg_out_opc_sa(s, OPC_SRL, args[0], args[1], args[2]);
+ } else {
+ tcg_out_opc_reg(s, OPC_SRLV, args[0], args[2], args[1]);
+ }
+ break;
+
+ case INDEX_op_ext8s_i32:
+ tcg_out_ext8s(s, args[0], args[1]);
+ break;
+ case INDEX_op_ext16s_i32:
+ tcg_out_ext16s(s, args[0], args[1]);
+ break;
+
+ case INDEX_op_brcond_i32:
+ tcg_out_brcond(s, args[2], args[0], args[1], args[3]);
+ break;
+ case INDEX_op_brcond2_i32:
+ tcg_out_brcond2(s, args[4], args[0], args[1], args[2], args[3], args[5]);
+ break;
+
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond(s, args[3], args[0], args[1], args[2]);
+ break;
+ case INDEX_op_setcond2_i32:
+ tcg_out_setcond2(s, args[5], args[0], args[1], args[2], args[3], args[4]);
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+ default:
+ tcg_abort();
+ }
+}
+
+static const TCGTargetOpDef mips_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "C" } },
+ { INDEX_op_jmp, { "r" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "rZ", "r" } },
+ { INDEX_op_st16_i32, { "rZ", "r" } },
+ { INDEX_op_st_i32, { "rZ", "r" } },
+
+ { INDEX_op_add_i32, { "r", "rZ", "rJZ" } },
+ { INDEX_op_mul_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rZ" } },
+ { INDEX_op_div_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_divu_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_rem_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_remu_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_sub_i32, { "r", "rZ", "rJZ" } },
+
+ { INDEX_op_and_i32, { "r", "rZ", "rIZ" } },
+ { INDEX_op_nor_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_not_i32, { "r", "rZ" } },
+ { INDEX_op_or_i32, { "r", "rZ", "rIZ" } },
+ { INDEX_op_xor_i32, { "r", "rZ", "rIZ" } },
+
+ { INDEX_op_shl_i32, { "r", "rZ", "riZ" } },
+ { INDEX_op_shr_i32, { "r", "rZ", "riZ" } },
+ { INDEX_op_sar_i32, { "r", "rZ", "riZ" } },
+
+ { INDEX_op_ext8s_i32, { "r", "rZ" } },
+ { INDEX_op_ext16s_i32, { "r", "rZ" } },
+
+ { INDEX_op_brcond_i32, { "rZ", "rZ" } },
+ { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } },
+ { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } },
+
+ { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } },
+ { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } },
+ { INDEX_op_brcond2_i32, { "rZ", "rZ", "rZ", "rZ" } },
+
+#if TARGET_LONG_BITS == 32
+ { INDEX_op_qemu_ld8u, { "L", "lZ" } },
+ { INDEX_op_qemu_ld8s, { "L", "lZ" } },
+ { INDEX_op_qemu_ld16u, { "L", "lZ" } },
+ { INDEX_op_qemu_ld16s, { "L", "lZ" } },
+ { INDEX_op_qemu_ld32, { "L", "lZ" } },
+ { INDEX_op_qemu_ld64, { "L", "L", "lZ" } },
+
+ { INDEX_op_qemu_st8, { "SZ", "SZ" } },
+ { INDEX_op_qemu_st16, { "SZ", "SZ" } },
+ { INDEX_op_qemu_st32, { "SZ", "SZ" } },
+ { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ" } },
+#else
+ { INDEX_op_qemu_ld8u, { "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_ld8s, { "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_ld16u, { "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_ld16s, { "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_ld32, { "L", "lZ", "lZ" } },
+ { INDEX_op_qemu_ld64, { "L", "L", "lZ", "lZ" } },
+
+ { INDEX_op_qemu_st8, { "SZ", "SZ", "SZ" } },
+ { INDEX_op_qemu_st16, { "SZ", "SZ", "SZ" } },
+ { INDEX_op_qemu_st32, { "SZ", "SZ", "SZ" } },
+ { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ", "SZ" } },
+#endif
+ { -1 },
+};
+
+static int tcg_target_callee_save_regs[] = {
+#if 0 /* used for the global env (TCG_AREG0), so no need to save */
+ TCG_REG_S0,
+#endif
+ TCG_REG_S1,
+ TCG_REG_S2,
+ TCG_REG_S3,
+ TCG_REG_S4,
+ TCG_REG_S5,
+ TCG_REG_S6,
+ TCG_REG_S7,
+ TCG_REG_GP,
+ TCG_REG_FP,
+ TCG_REG_RA, /* should be last for ABI compliance */
+};
+
+/* Generate global QEMU prologue and epilogue code */
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ int i, frame_size;
+
+ /* reserve some stack space */
+ frame_size = ARRAY_SIZE(tcg_target_callee_save_regs) * 4
+ + TCG_STATIC_CALL_ARGS_SIZE;
+ frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) &
+ ~(TCG_TARGET_STACK_ALIGN - 1);
+
+ /* TB prologue */
+ tcg_out_addi(s, TCG_REG_SP, -frame_size);
+ for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) {
+ tcg_out_st(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i],
+ TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4);
+ }
+
+ /* Call generated code */
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_A0, 0);
+ tcg_out_nop(s);
+ tb_ret_addr = s->code_ptr;
+
+ /* TB epilogue */
+ for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) {
+ tcg_out_ld(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i],
+ TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4);
+ }
+
+ tcg_out_opc_reg(s, OPC_JR, 0, TCG_REG_RA, 0);
+ tcg_out_addi(s, TCG_REG_SP, frame_size);
+}
+
+static void tcg_target_init(TCGContext *s)
+{
+ tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], 0xffffffff);
+ tcg_regset_set(tcg_target_call_clobber_regs,
+ (1 << TCG_REG_V0) |
+ (1 << TCG_REG_V1) |
+ (1 << TCG_REG_A0) |
+ (1 << TCG_REG_A1) |
+ (1 << TCG_REG_A2) |
+ (1 << TCG_REG_A3) |
+ (1 << TCG_REG_T1) |
+ (1 << TCG_REG_T2) |
+ (1 << TCG_REG_T3) |
+ (1 << TCG_REG_T4) |
+ (1 << TCG_REG_T5) |
+ (1 << TCG_REG_T6) |
+ (1 << TCG_REG_T7) |
+ (1 << TCG_REG_T8) |
+ (1 << TCG_REG_T9));
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); /* zero register */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_K0); /* kernel use only */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_K1); /* kernel use only */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_AT); /* internal use */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_T0); /* internal use */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA); /* return address */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); /* stack pointer */
+
+ tcg_add_target_add_op_defs(mips_op_defs);
+}
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
new file mode 100644
index 0000000..0028bfa
--- /dev/null
+++ b/tcg/mips/tcg-target.h
@@ -0,0 +1,110 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net>
+ * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_MIPS 1
+
+#define TCG_TARGET_REG_BITS 32
+#ifdef __MIPSEB__
+# define TCG_TARGET_WORDS_BIGENDIAN
+#endif
+
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+ TCG_REG_ZERO = 0,
+ TCG_REG_AT,
+ TCG_REG_V0,
+ TCG_REG_V1,
+ TCG_REG_A0,
+ TCG_REG_A1,
+ TCG_REG_A2,
+ TCG_REG_A3,
+ TCG_REG_T0,
+ TCG_REG_T1,
+ TCG_REG_T2,
+ TCG_REG_T3,
+ TCG_REG_T4,
+ TCG_REG_T5,
+ TCG_REG_T6,
+ TCG_REG_T7,
+ TCG_REG_S0,
+ TCG_REG_S1,
+ TCG_REG_S2,
+ TCG_REG_S3,
+ TCG_REG_S4,
+ TCG_REG_S5,
+ TCG_REG_S6,
+ TCG_REG_S7,
+ TCG_REG_T8,
+ TCG_REG_T9,
+ TCG_REG_K0,
+ TCG_REG_K1,
+ TCG_REG_GP,
+ TCG_REG_SP,
+ TCG_REG_FP,
+ TCG_REG_RA,
+};
+
+#define TCG_CT_CONST_ZERO 0x100
+#define TCG_CT_CONST_U16 0x200
+#define TCG_CT_CONST_S16 0x400
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_SP
+#define TCG_TARGET_STACK_ALIGN 8
+#define TCG_TARGET_CALL_STACK_OFFSET 16
+#define TCG_TARGET_CALL_ALIGN_ARGS 1
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_not_i32
+#define TCG_TARGET_HAS_nor_i32
+#undef TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#undef TCG_TARGET_HAS_bswap32_i32
+#undef TCG_TARGET_HAS_bswap16_i32
+#undef TCG_TARGET_HAS_andc_i32
+#undef TCG_TARGET_HAS_orc_i32
+#undef TCG_TARGET_HAS_eqv_i32
+#undef TCG_TARGET_HAS_nand_i32
+
+/* optional instructions automatically implemented */
+#undef TCG_TARGET_HAS_neg_i32 /* sub rd, zero, rt */
+#undef TCG_TARGET_HAS_ext8u_i32 /* andi rt, rs, 0xff */
+#undef TCG_TARGET_HAS_ext16u_i32 /* andi rt, rs, 0xffff */
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_S0
+
+/* guest base is supported */
+#define TCG_TARGET_HAS_GUEST_BASE
+
+#include <sys/cachectl.h>
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ cacheflush ((void *)start, stop-start, ICACHE);
+}
diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c
new file mode 100644
index 0000000..7970268
--- /dev/null
+++ b/tcg/ppc/tcg-target.c
@@ -0,0 +1,1922 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static uint8_t *tb_ret_addr;
+
+#ifdef _CALL_DARWIN
+#define LINKAGE_AREA_SIZE 24
+#define LR_OFFSET 8
+#elif defined _CALL_AIX
+#define LINKAGE_AREA_SIZE 52
+#define LR_OFFSET 8
+#else
+#define LINKAGE_AREA_SIZE 8
+#define LR_OFFSET 4
+#endif
+
+#define FAST_PATH
+
+#ifndef GUEST_BASE
+#define GUEST_BASE 0
+#endif
+
+#ifdef CONFIG_USE_GUEST_BASE
+#define TCG_GUEST_BASE_REG 30
+#else
+#define TCG_GUEST_BASE_REG 0
+#endif
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+ "r16",
+ "r17",
+ "r18",
+ "r19",
+ "r20",
+ "r21",
+ "r22",
+ "r23",
+ "r24",
+ "r25",
+ "r26",
+ "r27",
+ "r28",
+ "r29",
+ "r30",
+ "r31"
+};
+#endif
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31,
+#ifdef _CALL_DARWIN
+ TCG_REG_R2,
+#endif
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+#ifndef _CALL_DARWIN
+ TCG_REG_R11,
+#endif
+ TCG_REG_R12,
+#ifndef _CALL_SYSV
+ TCG_REG_R13,
+#endif
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_R27
+};
+
+static const int tcg_target_call_iarg_regs[] = {
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_R3,
+ TCG_REG_R4
+};
+
+static const int tcg_target_callee_save_regs[] = {
+#ifdef _CALL_DARWIN
+ TCG_REG_R11,
+ TCG_REG_R13,
+#endif
+#ifdef _CALL_AIX
+ TCG_REG_R13,
+#endif
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ /* TCG_REG_R27, */ /* currently used for the global env, so no
+ need to save */
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31
+};
+
+static uint32_t reloc_pc24_val (void *pc, tcg_target_long target)
+{
+ tcg_target_long disp;
+
+ disp = target - (tcg_target_long) pc;
+ if ((disp << 6) >> 6 != disp)
+ tcg_abort ();
+
+ return disp & 0x3fffffc;
+}
+
+static void reloc_pc24 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc)
+ | reloc_pc24_val (pc, target);
+}
+
+static uint16_t reloc_pc14_val (void *pc, tcg_target_long target)
+{
+ tcg_target_long disp;
+
+ disp = target - (tcg_target_long) pc;
+ if (disp != (int16_t) disp)
+ tcg_abort ();
+
+ return disp & 0xfffc;
+}
+
+static void reloc_pc14 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc)
+ | reloc_pc14_val (pc, target);
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ value += addend;
+ switch (type) {
+ case R_PPC_REL14:
+ reloc_pc14 (code_ptr, value);
+ break;
+ case R_PPC_REL24:
+ reloc_pc24 (code_ptr, value);
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+/* maximum number of register used for input function arguments */
+static int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return ARRAY_SIZE (tcg_target_call_iarg_regs);
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch (ct_str[0]) {
+ case 'A': case 'B': case 'C': case 'D':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A');
+ break;
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ break;
+#ifdef CONFIG_SOFTMMU
+ case 'L': /* qemu_ld constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4);
+ break;
+ case 'K': /* qemu_st[8..32] constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5);
+#if TARGET_LONG_BITS == 64
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6);
+#endif
+ break;
+ case 'M': /* qemu_st64 constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R7);
+ break;
+#else
+ case 'L':
+ case 'K':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ break;
+ case 'M':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+ break;
+#endif
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct;
+
+ ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST)
+ return 1;
+ return 0;
+}
+
+#define OPCD(opc) ((opc)<<26)
+#define XO31(opc) (OPCD(31)|((opc)<<1))
+#define XO19(opc) (OPCD(19)|((opc)<<1))
+
+#define B OPCD(18)
+#define BC OPCD(16)
+#define LBZ OPCD(34)
+#define LHZ OPCD(40)
+#define LHA OPCD(42)
+#define LWZ OPCD(32)
+#define STB OPCD(38)
+#define STH OPCD(44)
+#define STW OPCD(36)
+
+#define ADDIC OPCD(12)
+#define ADDI OPCD(14)
+#define ADDIS OPCD(15)
+#define ORI OPCD(24)
+#define ORIS OPCD(25)
+#define XORI OPCD(26)
+#define XORIS OPCD(27)
+#define ANDI OPCD(28)
+#define ANDIS OPCD(29)
+#define MULLI OPCD( 7)
+#define CMPLI OPCD(10)
+#define CMPI OPCD(11)
+#define SUBFIC OPCD( 8)
+
+#define LWZU OPCD(33)
+#define STWU OPCD(37)
+
+#define RLWIMI OPCD(20)
+#define RLWINM OPCD(21)
+#define RLWNM OPCD(23)
+
+#define BCLR XO19( 16)
+#define BCCTR XO19(528)
+#define CRAND XO19(257)
+#define CRANDC XO19(129)
+#define CRNAND XO19(225)
+#define CROR XO19(449)
+#define CRNOR XO19( 33)
+
+#define EXTSB XO31(954)
+#define EXTSH XO31(922)
+#define ADD XO31(266)
+#define ADDE XO31(138)
+#define ADDC XO31( 10)
+#define AND XO31( 28)
+#define SUBF XO31( 40)
+#define SUBFC XO31( 8)
+#define SUBFE XO31(136)
+#define OR XO31(444)
+#define XOR XO31(316)
+#define MULLW XO31(235)
+#define MULHWU XO31( 11)
+#define DIVW XO31(491)
+#define DIVWU XO31(459)
+#define CMP XO31( 0)
+#define CMPL XO31( 32)
+#define LHBRX XO31(790)
+#define LWBRX XO31(534)
+#define STHBRX XO31(918)
+#define STWBRX XO31(662)
+#define MFSPR XO31(339)
+#define MTSPR XO31(467)
+#define SRAWI XO31(824)
+#define NEG XO31(104)
+#define MFCR XO31( 19)
+#define CNTLZW XO31( 26)
+#define NOR XO31(124)
+#define ANDC XO31( 60)
+#define ORC XO31(412)
+#define EQV XO31(284)
+#define NAND XO31(476)
+
+#define LBZX XO31( 87)
+#define LHZX XO31(279)
+#define LHAX XO31(343)
+#define LWZX XO31( 23)
+#define STBX XO31(215)
+#define STHX XO31(407)
+#define STWX XO31(151)
+
+#define SPR(a,b) ((((a)<<5)|(b))<<11)
+#define LR SPR(8, 0)
+#define CTR SPR(9, 0)
+
+#define SLW XO31( 24)
+#define SRW XO31(536)
+#define SRAW XO31(792)
+
+#define TW XO31(4)
+#define TRAP (TW | TO (31))
+
+#define RT(r) ((r)<<21)
+#define RS(r) ((r)<<21)
+#define RA(r) ((r)<<16)
+#define RB(r) ((r)<<11)
+#define TO(t) ((t)<<21)
+#define SH(s) ((s)<<11)
+#define MB(b) ((b)<<6)
+#define ME(e) ((e)<<1)
+#define BO(o) ((o)<<21)
+
+#define LK 1
+
+#define TAB(t,a,b) (RT(t) | RA(a) | RB(b))
+#define SAB(s,a,b) (RS(s) | RA(a) | RB(b))
+
+#define BF(n) ((n)<<23)
+#define BI(n, c) (((c)+((n)*4))<<16)
+#define BT(n, c) (((c)+((n)*4))<<21)
+#define BA(n, c) (((c)+((n)*4))<<16)
+#define BB(n, c) (((c)+((n)*4))<<11)
+
+#define BO_COND_TRUE BO (12)
+#define BO_COND_FALSE BO (4)
+#define BO_ALWAYS BO (20)
+
+enum {
+ CR_LT,
+ CR_GT,
+ CR_EQ,
+ CR_SO
+};
+
+static const uint32_t tcg_to_bc[10] = {
+ [TCG_COND_EQ] = BC | BI (7, CR_EQ) | BO_COND_TRUE,
+ [TCG_COND_NE] = BC | BI (7, CR_EQ) | BO_COND_FALSE,
+ [TCG_COND_LT] = BC | BI (7, CR_LT) | BO_COND_TRUE,
+ [TCG_COND_GE] = BC | BI (7, CR_LT) | BO_COND_FALSE,
+ [TCG_COND_LE] = BC | BI (7, CR_GT) | BO_COND_FALSE,
+ [TCG_COND_GT] = BC | BI (7, CR_GT) | BO_COND_TRUE,
+ [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE,
+ [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE,
+ [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE,
+ [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE,
+};
+
+static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg)
+{
+ tcg_out32 (s, OR | SAB (arg, ret, arg));
+}
+
+static void tcg_out_movi(TCGContext *s, TCGType type,
+ int ret, tcg_target_long arg)
+{
+ if (arg == (int16_t) arg)
+ tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff));
+ else {
+ tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff));
+ if (arg & 0xffff)
+ tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff));
+ }
+}
+
+static void tcg_out_ldst (TCGContext *s, int ret, int addr,
+ int offset, int op1, int op2)
+{
+ if (offset == (int16_t) offset)
+ tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, offset);
+ tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0));
+ }
+}
+
+static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target)
+{
+ tcg_target_long disp;
+
+ disp = target - (tcg_target_long) s->code_ptr;
+ if ((disp << 6) >> 6 == disp)
+ tcg_out32 (s, B | (disp & 0x3fffffc) | mask);
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, (tcg_target_long) target);
+ tcg_out32 (s, MTSPR | RS (0) | CTR);
+ tcg_out32 (s, BCCTR | BO_ALWAYS | mask);
+ }
+}
+
+static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg)
+{
+#ifdef _CALL_AIX
+ int reg;
+
+ if (const_arg) {
+ reg = 2;
+ tcg_out_movi (s, TCG_TYPE_I32, reg, arg);
+ }
+ else reg = arg;
+
+ tcg_out32 (s, LWZ | RT (0) | RA (reg));
+ tcg_out32 (s, MTSPR | RA (0) | CTR);
+ tcg_out32 (s, LWZ | RT (2) | RA (reg) | 4);
+ tcg_out32 (s, BCCTR | BO_ALWAYS | LK);
+#else
+ if (const_arg) {
+ tcg_out_b (s, LK, arg);
+ }
+ else {
+ tcg_out32 (s, MTSPR | RS (arg) | LR);
+ tcg_out32 (s, BCLR | BO_ALWAYS | LK);
+ }
+#endif
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+#endif
+
+static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, data_reg, data_reg2, r0, r1, rbase, mem_index, s_bits, bswap;
+#ifdef CONFIG_SOFTMMU
+ int r2;
+ void *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+ int addr_reg2;
+#endif
+
+ data_reg = *args++;
+ if (opc == 3)
+ data_reg2 = *args++;
+ else
+ data_reg2 = 0;
+ addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+ addr_reg2 = *args++;
+#endif
+ mem_index = *args;
+ s_bits = opc & 3;
+
+#ifdef CONFIG_SOFTMMU
+ r0 = 3;
+ r1 = 4;
+ r2 = 0;
+ rbase = 0;
+
+ tcg_out32 (s, (RLWINM
+ | RA (r0)
+ | RS (addr_reg)
+ | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS))
+ | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS))
+ | ME (31 - CPU_TLB_ENTRY_BITS)
+ )
+ );
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0));
+ tcg_out32 (s, (LWZU
+ | RT (r1)
+ | RA (r0)
+ | offsetof (CPUState, tlb_table[mem_index][0].addr_read)
+ )
+ );
+ tcg_out32 (s, (RLWINM
+ | RA (r2)
+ | RS (addr_reg)
+ | SH (0)
+ | MB ((32 - s_bits) & 31)
+ | ME (31 - TARGET_PAGE_BITS)
+ )
+ );
+
+ tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1));
+#if TARGET_LONG_BITS == 64
+ tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1));
+ tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ));
+#endif
+
+ label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+ tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+ /* slow path */
+#if TARGET_LONG_BITS == 32
+ tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg);
+ tcg_out_movi (s, TCG_TYPE_I32, 4, mem_index);
+#else
+ tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2);
+ tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg);
+ tcg_out_movi (s, TCG_TYPE_I32, 5, mem_index);
+#endif
+
+ tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1);
+ switch (opc) {
+ case 0|4:
+ tcg_out32 (s, EXTSB | RA (data_reg) | RS (3));
+ break;
+ case 1|4:
+ tcg_out32 (s, EXTSH | RA (data_reg) | RS (3));
+ break;
+ case 0:
+ case 1:
+ case 2:
+ if (data_reg != 3)
+ tcg_out_mov (s, TCG_TYPE_I32, data_reg, 3);
+ break;
+ case 3:
+ if (data_reg == 3) {
+ if (data_reg2 == 4) {
+ tcg_out_mov (s, TCG_TYPE_I32, 0, 4);
+ tcg_out_mov (s, TCG_TYPE_I32, 4, 3);
+ tcg_out_mov (s, TCG_TYPE_I32, 3, 0);
+ }
+ else {
+ tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3);
+ tcg_out_mov (s, TCG_TYPE_I32, 3, 4);
+ }
+ }
+ else {
+ if (data_reg != 4) tcg_out_mov (s, TCG_TYPE_I32, data_reg, 4);
+ if (data_reg2 != 3) tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3);
+ }
+ break;
+ }
+ label2_ptr = s->code_ptr;
+ tcg_out32 (s, B);
+
+ /* label1: fast path */
+#ifdef FAST_PATH
+ reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+ /* r0 now contains &env->tlb_table[mem_index][index].addr_read */
+ tcg_out32 (s, (LWZ
+ | RT (r0)
+ | RA (r0)
+ | (offsetof (CPUTLBEntry, addend)
+ - offsetof (CPUTLBEntry, addr_read))
+ ));
+ /* r0 = env->tlb_table[mem_index][index].addend */
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+ /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else /* !CONFIG_SOFTMMU */
+ r0 = addr_reg;
+ r1 = 3;
+ rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 0;
+#else
+ bswap = 1;
+#endif
+
+ switch (opc) {
+ default:
+ case 0:
+ tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0));
+ break;
+ case 0|4:
+ tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg));
+ break;
+ case 1:
+ if (bswap)
+ tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, LHZX | TAB (data_reg, rbase, r0));
+ break;
+ case 1|4:
+ if (bswap) {
+ tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg));
+ }
+ else tcg_out32 (s, LHAX | TAB (data_reg, rbase, r0));
+ break;
+ case 2:
+ if (bswap)
+ tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, LWZX | TAB (data_reg, rbase, r0));
+ break;
+ case 3:
+ if (bswap) {
+ tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, LWBRX | TAB (data_reg2, rbase, r1));
+ }
+ else {
+#ifdef CONFIG_USE_GUEST_BASE
+ tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, LWZX | TAB (data_reg2, rbase, r0));
+ tcg_out32 (s, LWZX | TAB (data_reg, rbase, r1));
+#else
+ if (r0 == data_reg2) {
+ tcg_out32 (s, LWZ | RT (0) | RA (r0));
+ tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4);
+ tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 0);
+ }
+ else {
+ tcg_out32 (s, LWZ | RT (data_reg2) | RA (r0));
+ tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4);
+ }
+#endif
+ }
+ break;
+ }
+
+#ifdef CONFIG_SOFTMMU
+ reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, r0, r1, data_reg, data_reg2, mem_index, bswap, rbase;
+#ifdef CONFIG_SOFTMMU
+ int r2, ir;
+ void *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+ int addr_reg2;
+#endif
+
+ data_reg = *args++;
+ if (opc == 3)
+ data_reg2 = *args++;
+ else
+ data_reg2 = 0;
+ addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+ addr_reg2 = *args++;
+#endif
+ mem_index = *args;
+
+#ifdef CONFIG_SOFTMMU
+ r0 = 3;
+ r1 = 4;
+ r2 = 0;
+ rbase = 0;
+
+ tcg_out32 (s, (RLWINM
+ | RA (r0)
+ | RS (addr_reg)
+ | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS))
+ | MB (32 - (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS))
+ | ME (31 - CPU_TLB_ENTRY_BITS)
+ )
+ );
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0));
+ tcg_out32 (s, (LWZU
+ | RT (r1)
+ | RA (r0)
+ | offsetof (CPUState, tlb_table[mem_index][0].addr_write)
+ )
+ );
+ tcg_out32 (s, (RLWINM
+ | RA (r2)
+ | RS (addr_reg)
+ | SH (0)
+ | MB ((32 - opc) & 31)
+ | ME (31 - TARGET_PAGE_BITS)
+ )
+ );
+
+ tcg_out32 (s, CMP | (7 << 23) | RA (r2) | RB (r1));
+#if TARGET_LONG_BITS == 64
+ tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1));
+ tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ));
+#endif
+
+ label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+ tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+ /* slow path */
+#if TARGET_LONG_BITS == 32
+ tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg);
+ ir = 4;
+#else
+ tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2);
+ tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg);
+#ifdef TCG_TARGET_CALL_ALIGN_ARGS
+ ir = 5;
+#else
+ ir = 4;
+#endif
+#endif
+
+ switch (opc) {
+ case 0:
+ tcg_out32 (s, (RLWINM
+ | RA (ir)
+ | RS (data_reg)
+ | SH (0)
+ | MB (24)
+ | ME (31)));
+ break;
+ case 1:
+ tcg_out32 (s, (RLWINM
+ | RA (ir)
+ | RS (data_reg)
+ | SH (0)
+ | MB (16)
+ | ME (31)));
+ break;
+ case 2:
+ tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg);
+ break;
+ case 3:
+#ifdef TCG_TARGET_CALL_ALIGN_ARGS
+ ir = 5;
+#endif
+ tcg_out_mov (s, TCG_TYPE_I32, ir++, data_reg2);
+ tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg);
+ break;
+ }
+ ir++;
+
+ tcg_out_movi (s, TCG_TYPE_I32, ir, mem_index);
+ tcg_out_call (s, (tcg_target_long) qemu_st_helpers[opc], 1);
+ label2_ptr = s->code_ptr;
+ tcg_out32 (s, B);
+
+ /* label1: fast path */
+#ifdef FAST_PATH
+ reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+ tcg_out32 (s, (LWZ
+ | RT (r0)
+ | RA (r0)
+ | (offsetof (CPUTLBEntry, addend)
+ - offsetof (CPUTLBEntry, addr_write))
+ ));
+ /* r0 = env->tlb_table[mem_index][index].addend */
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+ /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else /* !CONFIG_SOFTMMU */
+ r0 = addr_reg;
+ r1 = 3;
+ rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 0;
+#else
+ bswap = 1;
+#endif
+ switch (opc) {
+ case 0:
+ tcg_out32 (s, STBX | SAB (data_reg, rbase, r0));
+ break;
+ case 1:
+ if (bswap)
+ tcg_out32 (s, STHBRX | SAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, STHX | SAB (data_reg, rbase, r0));
+ break;
+ case 2:
+ if (bswap)
+ tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, STWX | SAB (data_reg, rbase, r0));
+ break;
+ case 3:
+ if (bswap) {
+ tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0));
+ tcg_out32 (s, STWBRX | SAB (data_reg2, rbase, r1));
+ }
+ else {
+#ifdef CONFIG_USE_GUEST_BASE
+ tcg_out32 (s, STWX | SAB (data_reg2, rbase, r0));
+ tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, STWX | SAB (data_reg, rbase, r1));
+#else
+ tcg_out32 (s, STW | RS (data_reg2) | RA (r0));
+ tcg_out32 (s, STW | RS (data_reg) | RA (r0) | 4);
+#endif
+ }
+ break;
+ }
+
+#ifdef CONFIG_SOFTMMU
+ reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_target_qemu_prologue (TCGContext *s)
+{
+ int i, frame_size;
+
+ frame_size = 0
+ + LINKAGE_AREA_SIZE
+ + TCG_STATIC_CALL_ARGS_SIZE
+ + ARRAY_SIZE (tcg_target_callee_save_regs) * 4
+ ;
+ frame_size = (frame_size + 15) & ~15;
+
+#ifdef _CALL_AIX
+ {
+ uint32_t addr;
+
+ /* First emit adhoc function descriptor */
+ addr = (uint32_t) s->code_ptr + 12;
+ tcg_out32 (s, addr); /* entry point */
+ s->code_ptr += 8; /* skip TOC and environment pointer */
+ }
+#endif
+ tcg_out32 (s, MFSPR | RT (0) | LR);
+ tcg_out32 (s, STWU | RS (1) | RA (1) | (-frame_size & 0xffff));
+ for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+ tcg_out32 (s, (STW
+ | RS (tcg_target_callee_save_regs[i])
+ | RA (1)
+ | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE)
+ )
+ );
+ tcg_out32 (s, STW | RS (0) | RA (1) | (frame_size + LR_OFFSET));
+
+#ifdef CONFIG_USE_GUEST_BASE
+ if (GUEST_BASE) {
+ tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE);
+ tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG);
+ }
+#endif
+
+ tcg_out32 (s, MTSPR | RS (3) | CTR);
+ tcg_out32 (s, BCCTR | BO_ALWAYS);
+ tb_ret_addr = s->code_ptr;
+
+ for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+ tcg_out32 (s, (LWZ
+ | RT (tcg_target_callee_save_regs[i])
+ | RA (1)
+ | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE)
+ )
+ );
+ tcg_out32 (s, LWZ | RT (0) | RA (1) | (frame_size + LR_OFFSET));
+ tcg_out32 (s, MTSPR | RS (0) | LR);
+ tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size);
+ tcg_out32 (s, BCLR | BO_ALWAYS);
+}
+
+static void tcg_out_ld (TCGContext *s, TCGType type, int ret, int arg1,
+ tcg_target_long arg2)
+{
+ tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX);
+}
+
+static void tcg_out_st (TCGContext *s, TCGType type, int arg, int arg1,
+ tcg_target_long arg2)
+{
+ tcg_out_ldst (s, arg, arg1, arg2, STW, STWX);
+}
+
+static void ppc_addi (TCGContext *s, int rt, int ra, tcg_target_long si)
+{
+ if (!si && rt == ra)
+ return;
+
+ if (si == (int16_t) si)
+ tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff));
+ else {
+ uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15);
+ tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h);
+ tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff));
+ }
+}
+
+static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ ppc_addi (s, reg, reg, val);
+}
+
+static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2,
+ int const_arg2, int cr)
+{
+ int imm;
+ uint32_t op;
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ if (const_arg2) {
+ if ((int16_t) arg2 == arg2) {
+ op = CMPI;
+ imm = 1;
+ break;
+ }
+ else if ((uint16_t) arg2 == arg2) {
+ op = CMPLI;
+ imm = 1;
+ break;
+ }
+ }
+ op = CMPL;
+ imm = 0;
+ break;
+
+ case TCG_COND_LT:
+ case TCG_COND_GE:
+ case TCG_COND_LE:
+ case TCG_COND_GT:
+ if (const_arg2) {
+ if ((int16_t) arg2 == arg2) {
+ op = CMPI;
+ imm = 1;
+ break;
+ }
+ }
+ op = CMP;
+ imm = 0;
+ break;
+
+ case TCG_COND_LTU:
+ case TCG_COND_GEU:
+ case TCG_COND_LEU:
+ case TCG_COND_GTU:
+ if (const_arg2) {
+ if ((uint16_t) arg2 == arg2) {
+ op = CMPLI;
+ imm = 1;
+ break;
+ }
+ }
+ op = CMPL;
+ imm = 0;
+ break;
+
+ default:
+ tcg_abort ();
+ }
+ op |= BF (cr);
+
+ if (imm)
+ tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff));
+ else {
+ if (const_arg2) {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, arg2);
+ tcg_out32 (s, op | RA (arg1) | RB (0));
+ }
+ else
+ tcg_out32 (s, op | RA (arg1) | RB (arg2));
+ }
+
+}
+
+static void tcg_out_bc (TCGContext *s, int bc, int label_index)
+{
+ TCGLabel *l = &s->labels[label_index];
+
+ if (l->has_value)
+ tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value));
+ else {
+ uint16_t val = *(uint16_t *) &s->code_ptr[2];
+
+ /* Thanks to Andrzej Zaborowski */
+ tcg_out32 (s, bc | (val & 0xfffc));
+ tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0);
+ }
+}
+
+static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args,
+ const int *const_args)
+{
+ TCGCond cond = args[4];
+ int op;
+ struct { int bit1; int bit2; int cond2; } bits[] = {
+ [TCG_COND_LT ] = { CR_LT, CR_LT, TCG_COND_LT },
+ [TCG_COND_LE ] = { CR_LT, CR_GT, TCG_COND_LT },
+ [TCG_COND_GT ] = { CR_GT, CR_GT, TCG_COND_GT },
+ [TCG_COND_GE ] = { CR_GT, CR_LT, TCG_COND_GT },
+ [TCG_COND_LTU] = { CR_LT, CR_LT, TCG_COND_LTU },
+ [TCG_COND_LEU] = { CR_LT, CR_GT, TCG_COND_LTU },
+ [TCG_COND_GTU] = { CR_GT, CR_GT, TCG_COND_GTU },
+ [TCG_COND_GEU] = { CR_GT, CR_LT, TCG_COND_GTU },
+ }, *b = &bits[cond];
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ op = (cond == TCG_COND_EQ) ? CRAND : CRNAND;
+ tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 6);
+ tcg_out_cmp (s, cond, args[1], args[3], const_args[3], 7);
+ tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ));
+ break;
+ case TCG_COND_LT:
+ case TCG_COND_LE:
+ case TCG_COND_GT:
+ case TCG_COND_GE:
+ case TCG_COND_LTU:
+ case TCG_COND_LEU:
+ case TCG_COND_GTU:
+ case TCG_COND_GEU:
+ op = (b->bit1 != b->bit2) ? CRANDC : CRAND;
+ tcg_out_cmp (s, b->cond2, args[1], args[3], const_args[3], 5);
+ tcg_out_cmp (s, tcg_unsigned_cond (cond), args[0], args[2],
+ const_args[2], 7);
+ tcg_out32 (s, op | BT (7, CR_EQ) | BA (5, CR_EQ) | BB (7, b->bit2));
+ tcg_out32 (s, CROR | BT (7, CR_EQ) | BA (5, b->bit1) | BB (7, CR_EQ));
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+static void tcg_out_setcond (TCGContext *s, TCGCond cond, TCGArg arg0,
+ TCGArg arg1, TCGArg arg2, int const_arg2)
+{
+ int crop, sh, arg;
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ if (const_arg2) {
+ if (!arg2) {
+ arg = arg1;
+ }
+ else {
+ arg = 0;
+ if ((uint16_t) arg2 == arg2) {
+ tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2);
+ }
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, arg2);
+ tcg_out32 (s, XOR | SAB (arg1, 0, 0));
+ }
+ }
+ }
+ else {
+ arg = 0;
+ tcg_out32 (s, XOR | SAB (arg1, 0, arg2));
+ }
+ tcg_out32 (s, CNTLZW | RS (arg) | RA (0));
+ tcg_out32 (s, (RLWINM
+ | RA (arg0)
+ | RS (0)
+ | SH (27)
+ | MB (5)
+ | ME (31)
+ )
+ );
+ break;
+
+ case TCG_COND_NE:
+ if (const_arg2) {
+ if (!arg2) {
+ arg = arg1;
+ }
+ else {
+ arg = 0;
+ if ((uint16_t) arg2 == arg2) {
+ tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2);
+ }
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, arg2);
+ tcg_out32 (s, XOR | SAB (arg1, 0, 0));
+ }
+ }
+ }
+ else {
+ arg = 0;
+ tcg_out32 (s, XOR | SAB (arg1, 0, arg2));
+ }
+
+ if (arg == arg1 && arg1 == arg0) {
+ tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff);
+ tcg_out32 (s, SUBFE | TAB (arg0, 0, arg));
+ }
+ else {
+ tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff);
+ tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg));
+ }
+ break;
+
+ case TCG_COND_GT:
+ case TCG_COND_GTU:
+ sh = 30;
+ crop = 0;
+ goto crtest;
+
+ case TCG_COND_LT:
+ case TCG_COND_LTU:
+ sh = 29;
+ crop = 0;
+ goto crtest;
+
+ case TCG_COND_GE:
+ case TCG_COND_GEU:
+ sh = 31;
+ crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT);
+ goto crtest;
+
+ case TCG_COND_LE:
+ case TCG_COND_LEU:
+ sh = 31;
+ crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT);
+ crtest:
+ tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7);
+ if (crop) tcg_out32 (s, crop);
+ tcg_out32 (s, MFCR | RT (0));
+ tcg_out32 (s, (RLWINM
+ | RA (arg0)
+ | RS (0)
+ | SH (sh)
+ | MB (31)
+ | ME (31)
+ )
+ );
+ break;
+
+ default:
+ tcg_abort ();
+ }
+}
+
+static void tcg_out_setcond2 (TCGContext *s, const TCGArg *args,
+ const int *const_args)
+{
+ tcg_out_cr7eq_from_cond (s, args + 1, const_args + 1);
+ tcg_out32 (s, MFCR | RT (0));
+ tcg_out32 (s, (RLWINM
+ | RA (args[0])
+ | RS (0)
+ | SH (31)
+ | MB (31)
+ | ME (31)
+ )
+ );
+}
+
+static void tcg_out_brcond (TCGContext *s, TCGCond cond,
+ TCGArg arg1, TCGArg arg2, int const_arg2,
+ int label_index)
+{
+ tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7);
+ tcg_out_bc (s, tcg_to_bc[cond], label_index);
+}
+
+/* XXX: we implement it at the target level to avoid having to
+ handle cross basic blocks temporaries */
+static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args,
+ const int *const_args)
+{
+ tcg_out_cr7eq_from_cond (s, args, const_args);
+ tcg_out_bc (s, (BC | BI (7, CR_EQ) | BO_COND_TRUE), args[5]);
+}
+
+void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr)
+{
+ uint32_t *ptr;
+ long disp = addr - jmp_addr;
+ unsigned long patch_size;
+
+ ptr = (uint32_t *)jmp_addr;
+
+ if ((disp << 6) >> 6 != disp) {
+ ptr[0] = 0x3c000000 | (addr >> 16); /* lis 0,addr@ha */
+ ptr[1] = 0x60000000 | (addr & 0xffff); /* la 0,addr@l(0) */
+ ptr[2] = 0x7c0903a6; /* mtctr 0 */
+ ptr[3] = 0x4e800420; /* brctr */
+ patch_size = 16;
+ } else {
+ /* patch the branch destination */
+ if (disp != 16) {
+ *ptr = 0x48000000 | (disp & 0x03fffffc); /* b disp */
+ patch_size = 4;
+ } else {
+ ptr[0] = 0x60000000; /* nop */
+ ptr[1] = 0x60000000;
+ ptr[2] = 0x60000000;
+ ptr[3] = 0x60000000;
+ patch_size = 16;
+ }
+ }
+ /* flush icache */
+ flush_icache_range(jmp_addr, jmp_addr + patch_size);
+}
+
+static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
+ const int *const_args)
+{
+ switch (opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_movi (s, TCG_TYPE_I32, TCG_REG_R3, args[0]);
+ tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr);
+ break;
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+
+ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+ s->code_ptr += 16;
+ }
+ else {
+ tcg_abort ();
+ }
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+ case INDEX_op_br:
+ {
+ TCGLabel *l = &s->labels[args[0]];
+
+ if (l->has_value) {
+ tcg_out_b (s, 0, l->u.value);
+ }
+ else {
+ uint32_t val = *(uint32_t *) s->code_ptr;
+
+ /* Thanks to Andrzej Zaborowski */
+ tcg_out32 (s, B | (val & 0x3fffffc));
+ tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0);
+ }
+ }
+ break;
+ case INDEX_op_call:
+ tcg_out_call (s, args[0], const_args[0]);
+ break;
+ case INDEX_op_jmp:
+ if (const_args[0]) {
+ tcg_out_b (s, 0, args[0]);
+ }
+ else {
+ tcg_out32 (s, MTSPR | RS (args[0]) | CTR);
+ tcg_out32 (s, BCCTR | BO_ALWAYS);
+ }
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_ld8u_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+ break;
+ case INDEX_op_ld8s_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+ tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0]));
+ break;
+ case INDEX_op_ld16u_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX);
+ break;
+ case INDEX_op_ld16s_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX);
+ break;
+ case INDEX_op_ld_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX);
+ break;
+ case INDEX_op_st8_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX);
+ break;
+ case INDEX_op_st16_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX);
+ break;
+ case INDEX_op_st_i32:
+ tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX);
+ break;
+
+ case INDEX_op_add_i32:
+ if (const_args[2])
+ ppc_addi (s, args[0], args[1], args[2]);
+ else
+ tcg_out32 (s, ADD | TAB (args[0], args[1], args[2]));
+ break;
+ case INDEX_op_sub_i32:
+ if (const_args[2])
+ ppc_addi (s, args[0], args[1], -args[2]);
+ else
+ tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1]));
+ break;
+
+ case INDEX_op_and_i32:
+ if (const_args[2]) {
+ uint32_t c;
+
+ c = args[2];
+
+ if (!c) {
+ tcg_out_movi (s, TCG_TYPE_I32, args[0], 0);
+ break;
+ }
+#ifdef __PPU__
+ uint32_t t, n;
+ int mb, me;
+
+ n = c ^ -(c & 1);
+ t = n + (n & -n);
+
+ if ((t & (t - 1)) == 0) {
+ int lzc, tzc;
+
+ if ((c & 0x80000001) == 0x80000001) {
+ lzc = clz32 (n);
+ tzc = ctz32 (n);
+
+ mb = 32 - tzc;
+ me = lzc - 1;
+ }
+ else {
+ lzc = clz32 (c);
+ tzc = ctz32 (c);
+
+ mb = lzc;
+ me = 31 - tzc;
+ }
+
+ tcg_out32 (s, (RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (0)
+ | MB (mb)
+ | ME (me)
+ )
+ );
+ }
+ else
+#endif /* !__PPU__ */
+ {
+ if ((c & 0xffff) == c)
+ tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | c);
+ else if ((c & 0xffff0000) == c)
+ tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0])
+ | ((c >> 16) & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, c);
+ tcg_out32 (s, AND | SAB (args[1], args[0], 0));
+ }
+ }
+ }
+ else
+ tcg_out32 (s, AND | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_or_i32:
+ if (const_args[2]) {
+ if (args[2] & 0xffff) {
+ tcg_out32 (s, ORI | RS (args[1]) | RA (args[0])
+ | (args[2] & 0xffff));
+ if (args[2] >> 16)
+ tcg_out32 (s, ORIS | RS (args[0]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ }
+ else {
+ tcg_out32 (s, ORIS | RS (args[1]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ }
+ }
+ else
+ tcg_out32 (s, OR | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_xor_i32:
+ if (const_args[2]) {
+ if ((args[2] & 0xffff) == args[2])
+ tcg_out32 (s, XORI | RS (args[1]) | RA (args[0])
+ | (args[2] & 0xffff));
+ else if ((args[2] & 0xffff0000) == args[2])
+ tcg_out32 (s, XORIS | RS (args[1]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+ tcg_out32 (s, XOR | SAB (args[1], args[0], 0));
+ }
+ }
+ else
+ tcg_out32 (s, XOR | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_andc_i32:
+ tcg_out32 (s, ANDC | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_orc_i32:
+ tcg_out32 (s, ORC | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_eqv_i32:
+ tcg_out32 (s, EQV | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_nand_i32:
+ tcg_out32 (s, NAND | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_nor_i32:
+ tcg_out32 (s, NOR | SAB (args[1], args[0], args[2]));
+ break;
+
+ case INDEX_op_mul_i32:
+ if (const_args[2]) {
+ if (args[2] == (int16_t) args[2])
+ tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1])
+ | (args[2] & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+ tcg_out32 (s, MULLW | TAB (args[0], args[1], 0));
+ }
+ }
+ else
+ tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2]));
+ break;
+
+ case INDEX_op_div_i32:
+ tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2]));
+ break;
+
+ case INDEX_op_divu_i32:
+ tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2]));
+ break;
+
+ case INDEX_op_rem_i32:
+ tcg_out32 (s, DIVW | TAB (0, args[1], args[2]));
+ tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+ tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+ break;
+
+ case INDEX_op_remu_i32:
+ tcg_out32 (s, DIVWU | TAB (0, args[1], args[2]));
+ tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+ tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+ break;
+
+ case INDEX_op_mulu2_i32:
+ if (args[0] == args[2] || args[0] == args[3]) {
+ tcg_out32 (s, MULLW | TAB (0, args[2], args[3]));
+ tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3]));
+ tcg_out_mov (s, TCG_TYPE_I32, args[0], 0);
+ }
+ else {
+ tcg_out32 (s, MULLW | TAB (args[0], args[2], args[3]));
+ tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3]));
+ }
+ break;
+
+ case INDEX_op_shl_i32:
+ if (const_args[2]) {
+ tcg_out32 (s, (RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (args[2])
+ | MB (0)
+ | ME (31 - args[2])
+ )
+ );
+ }
+ else
+ tcg_out32 (s, SLW | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_shr_i32:
+ if (const_args[2]) {
+ tcg_out32 (s, (RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (32 - args[2])
+ | MB (args[2])
+ | ME (31)
+ )
+ );
+ }
+ else
+ tcg_out32 (s, SRW | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_sar_i32:
+ if (const_args[2])
+ tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2]));
+ else
+ tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_rotl_i32:
+ {
+ int op = 0
+ | RA (args[0])
+ | RS (args[1])
+ | MB (0)
+ | ME (31)
+ | (const_args[2] ? RLWINM | SH (args[2])
+ : RLWNM | RB (args[2]))
+ ;
+ tcg_out32 (s, op);
+ }
+ break;
+ case INDEX_op_rotr_i32:
+ if (const_args[2]) {
+ if (!args[2]) {
+ tcg_out_mov (s, TCG_TYPE_I32, args[0], args[1]);
+ }
+ else {
+ tcg_out32 (s, RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (32 - args[2])
+ | MB (0)
+ | ME (31)
+ );
+ }
+ }
+ else {
+ tcg_out32 (s, SUBFIC | RT (0) | RA (args[2]) | 32);
+ tcg_out32 (s, RLWNM
+ | RA (args[0])
+ | RS (args[1])
+ | RB (0)
+ | MB (0)
+ | ME (31)
+ );
+ }
+ break;
+
+ case INDEX_op_add2_i32:
+ if (args[0] == args[3] || args[0] == args[5]) {
+ tcg_out32 (s, ADDC | TAB (0, args[2], args[4]));
+ tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5]));
+ tcg_out_mov (s, TCG_TYPE_I32, args[0], 0);
+ }
+ else {
+ tcg_out32 (s, ADDC | TAB (args[0], args[2], args[4]));
+ tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5]));
+ }
+ break;
+ case INDEX_op_sub2_i32:
+ if (args[0] == args[3] || args[0] == args[5]) {
+ tcg_out32 (s, SUBFC | TAB (0, args[4], args[2]));
+ tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3]));
+ tcg_out_mov (s, TCG_TYPE_I32, args[0], 0);
+ }
+ else {
+ tcg_out32 (s, SUBFC | TAB (args[0], args[4], args[2]));
+ tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3]));
+ }
+ break;
+
+ case INDEX_op_brcond_i32:
+ /*
+ args[0] = r0
+ args[1] = r1
+ args[2] = cond
+ args[3] = r1 is const
+ args[4] = label_index
+ */
+ tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3]);
+ break;
+ case INDEX_op_brcond2_i32:
+ tcg_out_brcond2(s, args, const_args);
+ break;
+
+ case INDEX_op_neg_i32:
+ tcg_out32 (s, NEG | RT (args[0]) | RA (args[1]));
+ break;
+
+ case INDEX_op_not_i32:
+ tcg_out32 (s, NOR | SAB (args[1], args[0], args[1]));
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+ case INDEX_op_ext8s_i32:
+ tcg_out32 (s, EXTSB | RS (args[1]) | RA (args[0]));
+ break;
+ case INDEX_op_ext8u_i32:
+ tcg_out32 (s, RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (0)
+ | MB (24)
+ | ME (31)
+ );
+ break;
+ case INDEX_op_ext16s_i32:
+ tcg_out32 (s, EXTSH | RS (args[1]) | RA (args[0]));
+ break;
+ case INDEX_op_ext16u_i32:
+ tcg_out32 (s, RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (0)
+ | MB (16)
+ | ME (31)
+ );
+ break;
+
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond (s, args[3], args[0], args[1], args[2], const_args[2]);
+ break;
+ case INDEX_op_setcond2_i32:
+ tcg_out_setcond2 (s, args, const_args);
+ break;
+
+ case INDEX_op_bswap16_i32:
+ /* Stolen from gcc's builtin_bswap16 */
+
+ /* a1 = abcd */
+
+ /* r0 = (a1 << 8) & 0xff00 # 00d0 */
+ tcg_out32 (s, RLWINM
+ | RA (0)
+ | RS (args[1])
+ | SH (8)
+ | MB (16)
+ | ME (23)
+ );
+
+ /* a0 = rotate_left (a1, 24) & 0xff # 000c */
+ tcg_out32 (s, RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (24)
+ | MB (24)
+ | ME (31)
+ );
+
+ /* a0 = a0 | r0 # 00dc */
+ tcg_out32 (s, OR | SAB (0, args[0], args[0]));
+ break;
+
+ case INDEX_op_bswap32_i32:
+ /* Stolen from gcc's builtin_bswap32 */
+ {
+ int a0 = args[0];
+
+ /* a1 = args[1] # abcd */
+
+ if (a0 == args[1]) {
+ a0 = 0;
+ }
+
+ /* a0 = rotate_left (a1, 8) # bcda */
+ tcg_out32 (s, RLWINM
+ | RA (a0)
+ | RS (args[1])
+ | SH (8)
+ | MB (0)
+ | ME (31)
+ );
+
+ /* a0 = (a0 & ~0xff000000) | ((a1 << 24) & 0xff000000) # dcda */
+ tcg_out32 (s, RLWIMI
+ | RA (a0)
+ | RS (args[1])
+ | SH (24)
+ | MB (0)
+ | ME (7)
+ );
+
+ /* a0 = (a0 & ~0x0000ff00) | ((a1 << 24) & 0x0000ff00) # dcba */
+ tcg_out32 (s, RLWIMI
+ | RA (a0)
+ | RS (args[1])
+ | SH (24)
+ | MB (16)
+ | ME (23)
+ );
+
+ if (!a0) {
+ tcg_out_mov (s, TCG_TYPE_I32, args[0], a0);
+ }
+ }
+ break;
+
+ default:
+ tcg_dump_ops (s, stderr);
+ tcg_abort ();
+ }
+}
+
+static const TCGTargetOpDef ppc_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "ri" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "r", "r" } },
+ { INDEX_op_st16_i32, { "r", "r" } },
+ { INDEX_op_st_i32, { "r", "r" } },
+
+ { INDEX_op_add_i32, { "r", "r", "ri" } },
+ { INDEX_op_mul_i32, { "r", "r", "ri" } },
+ { INDEX_op_div_i32, { "r", "r", "r" } },
+ { INDEX_op_divu_i32, { "r", "r", "r" } },
+ { INDEX_op_rem_i32, { "r", "r", "r" } },
+ { INDEX_op_remu_i32, { "r", "r", "r" } },
+ { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } },
+ { INDEX_op_sub_i32, { "r", "r", "ri" } },
+ { INDEX_op_and_i32, { "r", "r", "ri" } },
+ { INDEX_op_or_i32, { "r", "r", "ri" } },
+ { INDEX_op_xor_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_shl_i32, { "r", "r", "ri" } },
+ { INDEX_op_shr_i32, { "r", "r", "ri" } },
+ { INDEX_op_sar_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_rotl_i32, { "r", "r", "ri" } },
+ { INDEX_op_rotr_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_brcond_i32, { "r", "ri" } },
+
+ { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } },
+ { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } },
+ { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } },
+
+ { INDEX_op_neg_i32, { "r", "r" } },
+ { INDEX_op_not_i32, { "r", "r" } },
+
+ { INDEX_op_andc_i32, { "r", "r", "r" } },
+ { INDEX_op_orc_i32, { "r", "r", "r" } },
+ { INDEX_op_eqv_i32, { "r", "r", "r" } },
+ { INDEX_op_nand_i32, { "r", "r", "r" } },
+ { INDEX_op_nor_i32, { "r", "r", "r" } },
+
+ { INDEX_op_setcond_i32, { "r", "r", "ri" } },
+ { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } },
+
+ { INDEX_op_bswap16_i32, { "r", "r" } },
+ { INDEX_op_bswap32_i32, { "r", "r" } },
+
+#if TARGET_LONG_BITS == 32
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "r", "L" } },
+
+ { INDEX_op_qemu_st8, { "K", "K" } },
+ { INDEX_op_qemu_st16, { "K", "K" } },
+ { INDEX_op_qemu_st32, { "K", "K" } },
+ { INDEX_op_qemu_st64, { "M", "M", "M" } },
+#else
+ { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "L", "L", "L" } },
+
+ { INDEX_op_qemu_st8, { "K", "K", "K" } },
+ { INDEX_op_qemu_st16, { "K", "K", "K" } },
+ { INDEX_op_qemu_st32, { "K", "K", "K" } },
+ { INDEX_op_qemu_st64, { "M", "M", "M", "M" } },
+#endif
+
+ { INDEX_op_ext8s_i32, { "r", "r" } },
+ { INDEX_op_ext8u_i32, { "r", "r" } },
+ { INDEX_op_ext16s_i32, { "r", "r" } },
+ { INDEX_op_ext16u_i32, { "r", "r" } },
+
+ { -1 },
+};
+
+static void tcg_target_init(TCGContext *s)
+{
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+ tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+ (1 << TCG_REG_R0) |
+#ifdef _CALL_DARWIN
+ (1 << TCG_REG_R2) |
+#endif
+ (1 << TCG_REG_R3) |
+ (1 << TCG_REG_R4) |
+ (1 << TCG_REG_R5) |
+ (1 << TCG_REG_R6) |
+ (1 << TCG_REG_R7) |
+ (1 << TCG_REG_R8) |
+ (1 << TCG_REG_R9) |
+ (1 << TCG_REG_R10) |
+ (1 << TCG_REG_R11) |
+ (1 << TCG_REG_R12)
+ );
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1);
+#ifndef _CALL_DARWIN
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2);
+#endif
+#ifdef _CALL_SYSV
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13);
+#endif
+
+ tcg_add_target_add_op_defs(ppc_op_defs);
+}
diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h
new file mode 100644
index 0000000..a1f8599
--- /dev/null
+++ b/tcg/ppc/tcg-target.h
@@ -0,0 +1,98 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_PPC 1
+
+#define TCG_TARGET_REG_BITS 32
+#define TCG_TARGET_WORDS_BIGENDIAN
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_R27,
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R1
+#define TCG_TARGET_STACK_ALIGN 16
+#if defined _CALL_DARWIN || defined __APPLE__
+#define TCG_TARGET_CALL_STACK_OFFSET 24
+#elif defined _CALL_AIX
+#define TCG_TARGET_CALL_STACK_OFFSET 52
+#elif defined _CALL_SYSV
+#define TCG_TARGET_CALL_ALIGN_ARGS 1
+#define TCG_TARGET_CALL_STACK_OFFSET 8
+#else
+#error Unsupported system
+#endif
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_ext8u_i32
+#define TCG_TARGET_HAS_ext16u_i32
+#define TCG_TARGET_HAS_bswap16_i32
+#define TCG_TARGET_HAS_bswap32_i32
+#define TCG_TARGET_HAS_not_i32
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_andc_i32
+#define TCG_TARGET_HAS_orc_i32
+#define TCG_TARGET_HAS_eqv_i32
+#define TCG_TARGET_HAS_nand_i32
+#define TCG_TARGET_HAS_nor_i32
+
+#define TCG_AREG0 TCG_REG_R27
+
+#define TCG_TARGET_HAS_GUEST_BASE
diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c
new file mode 100644
index 0000000..ebbee34
--- /dev/null
+++ b/tcg/ppc64/tcg-target.c
@@ -0,0 +1,1699 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define TCG_CT_CONST_U32 0x100
+
+static uint8_t *tb_ret_addr;
+
+#define FAST_PATH
+
+#if TARGET_LONG_BITS == 32
+#define LD_ADDR LWZU
+#define CMP_L 0
+#else
+#define LD_ADDR LDU
+#define CMP_L (1<<21)
+#endif
+
+#ifndef GUEST_BASE
+#define GUEST_BASE 0
+#endif
+
+#ifdef CONFIG_USE_GUEST_BASE
+#define TCG_GUEST_BASE_REG 30
+#else
+#define TCG_GUEST_BASE_REG 0
+#endif
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ "r6",
+ "r7",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+ "r16",
+ "r17",
+ "r18",
+ "r19",
+ "r20",
+ "r21",
+ "r22",
+ "r23",
+ "r24",
+ "r25",
+ "r26",
+ "r27",
+ "r28",
+ "r29",
+ "r30",
+ "r31"
+};
+#endif
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31,
+#ifdef __APPLE__
+ TCG_REG_R2,
+#endif
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+#ifndef __APPLE__
+ TCG_REG_R11,
+#endif
+ TCG_REG_R12,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_R27
+};
+
+static const int tcg_target_call_iarg_regs[] = {
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_R3
+};
+
+static const int tcg_target_callee_save_regs[] = {
+#ifdef __APPLE__
+ TCG_REG_R11,
+#endif
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ /* TCG_REG_R27, */ /* currently used for the global env, so no
+ need to save */
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31
+};
+
+static uint32_t reloc_pc24_val (void *pc, tcg_target_long target)
+{
+ tcg_target_long disp;
+
+ disp = target - (tcg_target_long) pc;
+ if ((disp << 38) >> 38 != disp)
+ tcg_abort ();
+
+ return disp & 0x3fffffc;
+}
+
+static void reloc_pc24 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc)
+ | reloc_pc24_val (pc, target);
+}
+
+static uint16_t reloc_pc14_val (void *pc, tcg_target_long target)
+{
+ tcg_target_long disp;
+
+ disp = target - (tcg_target_long) pc;
+ if (disp != (int16_t) disp)
+ tcg_abort ();
+
+ return disp & 0xfffc;
+}
+
+static void reloc_pc14 (void *pc, tcg_target_long target)
+{
+ *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc)
+ | reloc_pc14_val (pc, target);
+}
+
+static void patch_reloc (uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ value += addend;
+ switch (type) {
+ case R_PPC_REL14:
+ reloc_pc14 (code_ptr, value);
+ break;
+ case R_PPC_REL24:
+ reloc_pc24 (code_ptr, value);
+ break;
+ default:
+ tcg_abort ();
+ }
+}
+
+/* maximum number of register used for input function arguments */
+static int tcg_target_get_call_iarg_regs_count (int flags)
+{
+ return ARRAY_SIZE (tcg_target_call_iarg_regs);
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch (ct_str[0]) {
+ case 'A': case 'B': case 'C': case 'D':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set_reg (ct->u.regs, 3 + ct_str[0] - 'A');
+ break;
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32 (ct->u.regs, 0, 0xffffffff);
+ break;
+ case 'L': /* qemu_ld constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32 (ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3);
+#ifdef CONFIG_SOFTMMU
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4);
+#endif
+ break;
+ case 'S': /* qemu_st constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32 (ct->u.regs, 0, 0xffffffff);
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3);
+#ifdef CONFIG_SOFTMMU
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4);
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R5);
+#endif
+ break;
+ case 'Z':
+ ct->ct |= TCG_CT_CONST_U32;
+ break;
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static int tcg_target_const_match (tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct;
+
+ ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST)
+ return 1;
+ else if ((ct & TCG_CT_CONST_U32) && (val == (uint32_t) val))
+ return 1;
+ return 0;
+}
+
+#define OPCD(opc) ((opc)<<26)
+#define XO19(opc) (OPCD(19)|((opc)<<1))
+#define XO30(opc) (OPCD(30)|((opc)<<2))
+#define XO31(opc) (OPCD(31)|((opc)<<1))
+#define XO58(opc) (OPCD(58)|(opc))
+#define XO62(opc) (OPCD(62)|(opc))
+
+#define B OPCD( 18)
+#define BC OPCD( 16)
+#define LBZ OPCD( 34)
+#define LHZ OPCD( 40)
+#define LHA OPCD( 42)
+#define LWZ OPCD( 32)
+#define STB OPCD( 38)
+#define STH OPCD( 44)
+#define STW OPCD( 36)
+
+#define STD XO62( 0)
+#define STDU XO62( 1)
+#define STDX XO31(149)
+
+#define LD XO58( 0)
+#define LDX XO31( 21)
+#define LDU XO58( 1)
+#define LWA XO58( 2)
+#define LWAX XO31(341)
+
+#define ADDIC OPCD( 12)
+#define ADDI OPCD( 14)
+#define ADDIS OPCD( 15)
+#define ORI OPCD( 24)
+#define ORIS OPCD( 25)
+#define XORI OPCD( 26)
+#define XORIS OPCD( 27)
+#define ANDI OPCD( 28)
+#define ANDIS OPCD( 29)
+#define MULLI OPCD( 7)
+#define CMPLI OPCD( 10)
+#define CMPI OPCD( 11)
+
+#define LWZU OPCD( 33)
+#define STWU OPCD( 37)
+
+#define RLWINM OPCD( 21)
+
+#define RLDICL XO30( 0)
+#define RLDICR XO30( 1)
+#define RLDIMI XO30( 3)
+
+#define BCLR XO19( 16)
+#define BCCTR XO19(528)
+#define CRAND XO19(257)
+#define CRANDC XO19(129)
+#define CRNAND XO19(225)
+#define CROR XO19(449)
+#define CRNOR XO19( 33)
+
+#define EXTSB XO31(954)
+#define EXTSH XO31(922)
+#define EXTSW XO31(986)
+#define ADD XO31(266)
+#define ADDE XO31(138)
+#define ADDC XO31( 10)
+#define AND XO31( 28)
+#define SUBF XO31( 40)
+#define SUBFC XO31( 8)
+#define SUBFE XO31(136)
+#define OR XO31(444)
+#define XOR XO31(316)
+#define MULLW XO31(235)
+#define MULHWU XO31( 11)
+#define DIVW XO31(491)
+#define DIVWU XO31(459)
+#define CMP XO31( 0)
+#define CMPL XO31( 32)
+#define LHBRX XO31(790)
+#define LWBRX XO31(534)
+#define STHBRX XO31(918)
+#define STWBRX XO31(662)
+#define MFSPR XO31(339)
+#define MTSPR XO31(467)
+#define SRAWI XO31(824)
+#define NEG XO31(104)
+#define MFCR XO31( 19)
+#define CNTLZW XO31( 26)
+#define CNTLZD XO31( 58)
+
+#define MULLD XO31(233)
+#define MULHD XO31( 73)
+#define MULHDU XO31( 9)
+#define DIVD XO31(489)
+#define DIVDU XO31(457)
+
+#define LBZX XO31( 87)
+#define LHZX XO31(279)
+#define LHAX XO31(343)
+#define LWZX XO31( 23)
+#define STBX XO31(215)
+#define STHX XO31(407)
+#define STWX XO31(151)
+
+#define SPR(a,b) ((((a)<<5)|(b))<<11)
+#define LR SPR(8, 0)
+#define CTR SPR(9, 0)
+
+#define SLW XO31( 24)
+#define SRW XO31(536)
+#define SRAW XO31(792)
+
+#define SLD XO31( 27)
+#define SRD XO31(539)
+#define SRAD XO31(794)
+#define SRADI XO31(413<<1)
+
+#define TW XO31( 4)
+#define TRAP (TW | TO (31))
+
+#define RT(r) ((r)<<21)
+#define RS(r) ((r)<<21)
+#define RA(r) ((r)<<16)
+#define RB(r) ((r)<<11)
+#define TO(t) ((t)<<21)
+#define SH(s) ((s)<<11)
+#define MB(b) ((b)<<6)
+#define ME(e) ((e)<<1)
+#define BO(o) ((o)<<21)
+#define MB64(b) ((b)<<5)
+
+#define LK 1
+
+#define TAB(t,a,b) (RT(t) | RA(a) | RB(b))
+#define SAB(s,a,b) (RS(s) | RA(a) | RB(b))
+
+#define BF(n) ((n)<<23)
+#define BI(n, c) (((c)+((n)*4))<<16)
+#define BT(n, c) (((c)+((n)*4))<<21)
+#define BA(n, c) (((c)+((n)*4))<<16)
+#define BB(n, c) (((c)+((n)*4))<<11)
+
+#define BO_COND_TRUE BO (12)
+#define BO_COND_FALSE BO ( 4)
+#define BO_ALWAYS BO (20)
+
+enum {
+ CR_LT,
+ CR_GT,
+ CR_EQ,
+ CR_SO
+};
+
+static const uint32_t tcg_to_bc[10] = {
+ [TCG_COND_EQ] = BC | BI (7, CR_EQ) | BO_COND_TRUE,
+ [TCG_COND_NE] = BC | BI (7, CR_EQ) | BO_COND_FALSE,
+ [TCG_COND_LT] = BC | BI (7, CR_LT) | BO_COND_TRUE,
+ [TCG_COND_GE] = BC | BI (7, CR_LT) | BO_COND_FALSE,
+ [TCG_COND_LE] = BC | BI (7, CR_GT) | BO_COND_FALSE,
+ [TCG_COND_GT] = BC | BI (7, CR_GT) | BO_COND_TRUE,
+ [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE,
+ [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE,
+ [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE,
+ [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE,
+};
+
+static void tcg_out_mov (TCGContext *s, TCGType type, int ret, int arg)
+{
+ tcg_out32 (s, OR | SAB (arg, ret, arg));
+}
+
+static void tcg_out_rld (TCGContext *s, int op, int ra, int rs, int sh, int mb)
+{
+ sh = SH (sh & 0x1f) | (((sh >> 5) & 1) << 1);
+ mb = MB64 ((mb >> 5) | ((mb << 1) & 0x3f));
+ tcg_out32 (s, op | RA (ra) | RS (rs) | sh | mb);
+}
+
+static void tcg_out_movi32 (TCGContext *s, int ret, int32_t arg)
+{
+ if (arg == (int16_t) arg)
+ tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff));
+ else {
+ tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff));
+ if (arg & 0xffff)
+ tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff));
+ }
+}
+
+static void tcg_out_movi (TCGContext *s, TCGType type,
+ int ret, tcg_target_long arg)
+{
+ int32_t arg32 = arg;
+ arg = type == TCG_TYPE_I32 ? arg & 0xffffffff : arg;
+
+ if (arg == arg32) {
+ tcg_out_movi32 (s, ret, arg32);
+ }
+ else {
+ if ((uint64_t) arg >> 32) {
+ uint16_t h16 = arg >> 16;
+ uint16_t l16 = arg;
+
+ tcg_out_movi32 (s, ret, arg >> 32);
+ tcg_out_rld (s, RLDICR, ret, ret, 32, 31);
+ if (h16) tcg_out32 (s, ORIS | RS (ret) | RA (ret) | h16);
+ if (l16) tcg_out32 (s, ORI | RS (ret) | RA (ret) | l16);
+ }
+ else {
+ tcg_out_movi32 (s, ret, arg32);
+ if (arg32 < 0)
+ tcg_out_rld (s, RLDICL, ret, ret, 0, 32);
+ }
+ }
+}
+
+static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target)
+{
+ tcg_target_long disp;
+
+ disp = target - (tcg_target_long) s->code_ptr;
+ if ((disp << 38) >> 38 == disp)
+ tcg_out32 (s, B | (disp & 0x3fffffc) | mask);
+ else {
+ tcg_out_movi (s, TCG_TYPE_I64, 0, (tcg_target_long) target);
+ tcg_out32 (s, MTSPR | RS (0) | CTR);
+ tcg_out32 (s, BCCTR | BO_ALWAYS | mask);
+ }
+}
+
+static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg)
+{
+#ifdef __APPLE__
+ if (const_arg) {
+ tcg_out_b (s, LK, arg);
+ }
+ else {
+ tcg_out32 (s, MTSPR | RS (arg) | LR);
+ tcg_out32 (s, BCLR | BO_ALWAYS | LK);
+ }
+#else
+ int reg;
+
+ if (const_arg) {
+ reg = 2;
+ tcg_out_movi (s, TCG_TYPE_I64, reg, arg);
+ }
+ else reg = arg;
+
+ tcg_out32 (s, LD | RT (0) | RA (reg));
+ tcg_out32 (s, MTSPR | RA (0) | CTR);
+ tcg_out32 (s, LD | RT (11) | RA (reg) | 16);
+ tcg_out32 (s, LD | RT (2) | RA (reg) | 8);
+ tcg_out32 (s, BCCTR | BO_ALWAYS | LK);
+#endif
+}
+
+static void tcg_out_ldst (TCGContext *s, int ret, int addr,
+ int offset, int op1, int op2)
+{
+ if (offset == (int16_t) offset)
+ tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I64, 0, offset);
+ tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0));
+ }
+}
+
+static void tcg_out_ldsta (TCGContext *s, int ret, int addr,
+ int offset, int op1, int op2)
+{
+ if (offset == (int16_t) (offset & ~3))
+ tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I64, 0, offset);
+ tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0));
+ }
+}
+
+#if defined (CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+
+static void tcg_out_tlb_read (TCGContext *s, int r0, int r1, int r2,
+ int addr_reg, int s_bits, int offset)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32);
+
+ tcg_out32 (s, (RLWINM
+ | RA (r0)
+ | RS (addr_reg)
+ | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS))
+ | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS))
+ | ME (31 - CPU_TLB_ENTRY_BITS)
+ )
+ );
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0));
+ tcg_out32 (s, (LWZU | RT (r1) | RA (r0) | offset));
+ tcg_out32 (s, (RLWINM
+ | RA (r2)
+ | RS (addr_reg)
+ | SH (0)
+ | MB ((32 - s_bits) & 31)
+ | ME (31 - TARGET_PAGE_BITS)
+ )
+ );
+#else
+ tcg_out_rld (s, RLDICL, r0, addr_reg,
+ 64 - TARGET_PAGE_BITS,
+ 64 - CPU_TLB_BITS);
+ tcg_out_rld (s, RLDICR, r0, r0,
+ CPU_TLB_ENTRY_BITS,
+ 63 - CPU_TLB_ENTRY_BITS);
+
+ tcg_out32 (s, ADD | TAB (r0, r0, TCG_AREG0));
+ tcg_out32 (s, LD_ADDR | RT (r1) | RA (r0) | offset);
+
+ if (!s_bits) {
+ tcg_out_rld (s, RLDICR, r2, addr_reg, 0, 63 - TARGET_PAGE_BITS);
+ }
+ else {
+ tcg_out_rld (s, RLDICL, r2, addr_reg,
+ 64 - TARGET_PAGE_BITS,
+ TARGET_PAGE_BITS - s_bits);
+ tcg_out_rld (s, RLDICL, r2, r2, TARGET_PAGE_BITS, 0);
+ }
+#endif
+}
+#endif
+
+static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, data_reg, r0, r1, rbase, mem_index, s_bits, bswap;
+#ifdef CONFIG_SOFTMMU
+ int r2;
+ void *label1_ptr, *label2_ptr;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ mem_index = *args;
+ s_bits = opc & 3;
+
+#ifdef CONFIG_SOFTMMU
+ r0 = 3;
+ r1 = 4;
+ r2 = 0;
+ rbase = 0;
+
+ tcg_out_tlb_read (s, r0, r1, r2, addr_reg, s_bits,
+ offsetof (CPUState, tlb_table[mem_index][0].addr_read));
+
+ tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L);
+
+ label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+ tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+ /* slow path */
+ tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg);
+ tcg_out_movi (s, TCG_TYPE_I64, 4, mem_index);
+
+ tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1);
+
+ switch (opc) {
+ case 0|4:
+ tcg_out32 (s, EXTSB | RA (data_reg) | RS (3));
+ break;
+ case 1|4:
+ tcg_out32 (s, EXTSH | RA (data_reg) | RS (3));
+ break;
+ case 2|4:
+ tcg_out32 (s, EXTSW | RA (data_reg) | RS (3));
+ break;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ if (data_reg != 3)
+ tcg_out_mov (s, TCG_TYPE_I64, data_reg, 3);
+ break;
+ }
+ label2_ptr = s->code_ptr;
+ tcg_out32 (s, B);
+
+ /* label1: fast path */
+#ifdef FAST_PATH
+ reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+ /* r0 now contains &env->tlb_table[mem_index][index].addr_read */
+ tcg_out32 (s, (LD
+ | RT (r0)
+ | RA (r0)
+ | (offsetof (CPUTLBEntry, addend)
+ - offsetof (CPUTLBEntry, addr_read))
+ ));
+ /* r0 = env->tlb_table[mem_index][index].addend */
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+ /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else /* !CONFIG_SOFTMMU */
+#if TARGET_LONG_BITS == 32
+ tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32);
+#endif
+ r0 = addr_reg;
+ r1 = 3;
+ rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 0;
+#else
+ bswap = 1;
+#endif
+ switch (opc) {
+ default:
+ case 0:
+ tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0));
+ break;
+ case 0|4:
+ tcg_out32 (s, LBZX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg));
+ break;
+ case 1:
+ if (bswap)
+ tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, LHZX | TAB (data_reg, rbase, r0));
+ break;
+ case 1|4:
+ if (bswap) {
+ tcg_out32 (s, LHBRX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg));
+ }
+ else tcg_out32 (s, LHAX | TAB (data_reg, rbase, r0));
+ break;
+ case 2:
+ if (bswap)
+ tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, LWZX | TAB (data_reg, rbase, r0));
+ break;
+ case 2|4:
+ if (bswap) {
+ tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, EXTSW | RA (data_reg) | RS (data_reg));
+ }
+ else tcg_out32 (s, LWAX | TAB (data_reg, rbase, r0));
+ break;
+ case 3:
+#ifdef CONFIG_USE_GUEST_BASE
+ if (bswap) {
+ tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+ tcg_out32 (s, LWBRX | TAB (data_reg, rbase, r0));
+ tcg_out32 (s, LWBRX | TAB ( r1, rbase, r1));
+ tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0);
+ }
+ else tcg_out32 (s, LDX | TAB (data_reg, rbase, r0));
+#else
+ if (bswap) {
+ tcg_out_movi32 (s, 0, 4);
+ tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0));
+ tcg_out32 (s, LWBRX | RT ( r1) | RA (r0));
+ tcg_out_rld (s, RLDIMI, data_reg, r1, 32, 0);
+ }
+ else tcg_out32 (s, LD | RT (data_reg) | RA (r0));
+#endif
+ break;
+ }
+
+#ifdef CONFIG_SOFTMMU
+ reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc)
+{
+ int addr_reg, r0, r1, rbase, data_reg, mem_index, bswap;
+#ifdef CONFIG_SOFTMMU
+ int r2;
+ void *label1_ptr, *label2_ptr;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ mem_index = *args;
+
+#ifdef CONFIG_SOFTMMU
+ r0 = 3;
+ r1 = 4;
+ r2 = 0;
+ rbase = 0;
+
+ tcg_out_tlb_read (s, r0, r1, r2, addr_reg, opc,
+ offsetof (CPUState, tlb_table[mem_index][0].addr_write));
+
+ tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L);
+
+ label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+ tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+ /* slow path */
+ tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg);
+ tcg_out_rld (s, RLDICL, 4, data_reg, 0, 64 - (1 << (3 + opc)));
+ tcg_out_movi (s, TCG_TYPE_I64, 5, mem_index);
+
+ tcg_out_call (s, (tcg_target_long) qemu_st_helpers[opc], 1);
+
+ label2_ptr = s->code_ptr;
+ tcg_out32 (s, B);
+
+ /* label1: fast path */
+#ifdef FAST_PATH
+ reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+ tcg_out32 (s, (LD
+ | RT (r0)
+ | RA (r0)
+ | (offsetof (CPUTLBEntry, addend)
+ - offsetof (CPUTLBEntry, addr_write))
+ ));
+ /* r0 = env->tlb_table[mem_index][index].addend */
+ tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+ /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else /* !CONFIG_SOFTMMU */
+#if TARGET_LONG_BITS == 32
+ tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32);
+#endif
+ r1 = 3;
+ r0 = addr_reg;
+ rbase = GUEST_BASE ? TCG_GUEST_BASE_REG : 0;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ bswap = 0;
+#else
+ bswap = 1;
+#endif
+ switch (opc) {
+ case 0:
+ tcg_out32 (s, STBX | SAB (data_reg, rbase, r0));
+ break;
+ case 1:
+ if (bswap)
+ tcg_out32 (s, STHBRX | SAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, STHX | SAB (data_reg, rbase, r0));
+ break;
+ case 2:
+ if (bswap)
+ tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0));
+ else
+ tcg_out32 (s, STWX | SAB (data_reg, rbase, r0));
+ break;
+ case 3:
+ if (bswap) {
+ tcg_out32 (s, STWBRX | SAB (data_reg, rbase, r0));
+ tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+ tcg_out_rld (s, RLDICL, 0, data_reg, 32, 0);
+ tcg_out32 (s, STWBRX | SAB (0, rbase, r1));
+ }
+ else tcg_out32 (s, STDX | SAB (data_reg, rbase, r0));
+ break;
+ }
+
+#ifdef CONFIG_SOFTMMU
+ reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_target_qemu_prologue (TCGContext *s)
+{
+ int i, frame_size;
+#ifndef __APPLE__
+ uint64_t addr;
+#endif
+
+ frame_size = 0
+ + 8 /* back chain */
+ + 8 /* CR */
+ + 8 /* LR */
+ + 8 /* compiler doubleword */
+ + 8 /* link editor doubleword */
+ + 8 /* TOC save area */
+ + TCG_STATIC_CALL_ARGS_SIZE
+ + ARRAY_SIZE (tcg_target_callee_save_regs) * 8
+ ;
+ frame_size = (frame_size + 15) & ~15;
+
+#ifndef __APPLE__
+ /* First emit adhoc function descriptor */
+ addr = (uint64_t) s->code_ptr + 24;
+ tcg_out32 (s, addr >> 32); tcg_out32 (s, addr); /* entry point */
+ s->code_ptr += 16; /* skip TOC and environment pointer */
+#endif
+
+ /* Prologue */
+ tcg_out32 (s, MFSPR | RT (0) | LR);
+ tcg_out32 (s, STDU | RS (1) | RA (1) | (-frame_size & 0xffff));
+ for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+ tcg_out32 (s, (STD
+ | RS (tcg_target_callee_save_regs[i])
+ | RA (1)
+ | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE)
+ )
+ );
+ tcg_out32 (s, STD | RS (0) | RA (1) | (frame_size + 16));
+
+#ifdef CONFIG_USE_GUEST_BASE
+ if (GUEST_BASE) {
+ tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE);
+ tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG);
+ }
+#endif
+
+ tcg_out32 (s, MTSPR | RS (3) | CTR);
+ tcg_out32 (s, BCCTR | BO_ALWAYS);
+
+ /* Epilogue */
+ tb_ret_addr = s->code_ptr;
+
+ for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+ tcg_out32 (s, (LD
+ | RT (tcg_target_callee_save_regs[i])
+ | RA (1)
+ | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE)
+ )
+ );
+ tcg_out32 (s, LD | RT (0) | RA (1) | (frame_size + 16));
+ tcg_out32 (s, MTSPR | RS (0) | LR);
+ tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size);
+ tcg_out32 (s, BCLR | BO_ALWAYS);
+}
+
+static void tcg_out_ld (TCGContext *s, TCGType type, int ret, int arg1,
+ tcg_target_long arg2)
+{
+ if (type == TCG_TYPE_I32)
+ tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX);
+ else
+ tcg_out_ldsta (s, ret, arg1, arg2, LD, LDX);
+}
+
+static void tcg_out_st (TCGContext *s, TCGType type, int arg, int arg1,
+ tcg_target_long arg2)
+{
+ if (type == TCG_TYPE_I32)
+ tcg_out_ldst (s, arg, arg1, arg2, STW, STWX);
+ else
+ tcg_out_ldsta (s, arg, arg1, arg2, STD, STDX);
+}
+
+static void ppc_addi32 (TCGContext *s, int rt, int ra, tcg_target_long si)
+{
+ if (!si && rt == ra)
+ return;
+
+ if (si == (int16_t) si)
+ tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff));
+ else {
+ uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15);
+ tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h);
+ tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff));
+ }
+}
+
+static void ppc_addi64 (TCGContext *s, int rt, int ra, tcg_target_long si)
+{
+ /* XXX: suboptimal */
+ if (si == (int16_t) si
+ || ((((uint64_t) si >> 31) == 0) && (si & 0x8000) == 0))
+ ppc_addi32 (s, rt, ra, si);
+ else {
+ tcg_out_movi (s, TCG_TYPE_I64, 0, si);
+ tcg_out32 (s, ADD | RT (rt) | RA (ra));
+ }
+}
+
+static void tcg_out_addi (TCGContext *s, int reg, tcg_target_long val)
+{
+ ppc_addi64 (s, reg, reg, val);
+}
+
+static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2,
+ int const_arg2, int cr, int arch64)
+{
+ int imm;
+ uint32_t op;
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ if (const_arg2) {
+ if ((int16_t) arg2 == arg2) {
+ op = CMPI;
+ imm = 1;
+ break;
+ }
+ else if ((uint16_t) arg2 == arg2) {
+ op = CMPLI;
+ imm = 1;
+ break;
+ }
+ }
+ op = CMPL;
+ imm = 0;
+ break;
+
+ case TCG_COND_LT:
+ case TCG_COND_GE:
+ case TCG_COND_LE:
+ case TCG_COND_GT:
+ if (const_arg2) {
+ if ((int16_t) arg2 == arg2) {
+ op = CMPI;
+ imm = 1;
+ break;
+ }
+ }
+ op = CMP;
+ imm = 0;
+ break;
+
+ case TCG_COND_LTU:
+ case TCG_COND_GEU:
+ case TCG_COND_LEU:
+ case TCG_COND_GTU:
+ if (const_arg2) {
+ if ((uint16_t) arg2 == arg2) {
+ op = CMPLI;
+ imm = 1;
+ break;
+ }
+ }
+ op = CMPL;
+ imm = 0;
+ break;
+
+ default:
+ tcg_abort ();
+ }
+ op |= BF (cr) | (arch64 << 21);
+
+ if (imm)
+ tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff));
+ else {
+ if (const_arg2) {
+ tcg_out_movi (s, TCG_TYPE_I64, 0, arg2);
+ tcg_out32 (s, op | RA (arg1) | RB (0));
+ }
+ else
+ tcg_out32 (s, op | RA (arg1) | RB (arg2));
+ }
+
+}
+
+static void tcg_out_setcond (TCGContext *s, TCGType type, TCGCond cond,
+ TCGArg arg0, TCGArg arg1, TCGArg arg2,
+ int const_arg2)
+{
+ int crop, sh, arg;
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ if (const_arg2) {
+ if (!arg2) {
+ arg = arg1;
+ }
+ else {
+ arg = 0;
+ if ((uint16_t) arg2 == arg2) {
+ tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2);
+ }
+ else {
+ tcg_out_movi (s, type, 0, arg2);
+ tcg_out32 (s, XOR | SAB (arg1, 0, 0));
+ }
+ }
+ }
+ else {
+ arg = 0;
+ tcg_out32 (s, XOR | SAB (arg1, 0, arg2));
+ }
+
+ if (type == TCG_TYPE_I64) {
+ tcg_out32 (s, CNTLZD | RS (arg) | RA (0));
+ tcg_out_rld (s, RLDICL, arg0, 0, 58, 6);
+ }
+ else {
+ tcg_out32 (s, CNTLZW | RS (arg) | RA (0));
+ tcg_out32 (s, (RLWINM
+ | RA (arg0)
+ | RS (0)
+ | SH (27)
+ | MB (5)
+ | ME (31)
+ )
+ );
+ }
+ break;
+
+ case TCG_COND_NE:
+ if (const_arg2) {
+ if (!arg2) {
+ arg = arg1;
+ }
+ else {
+ arg = 0;
+ if ((uint16_t) arg2 == arg2) {
+ tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2);
+ }
+ else {
+ tcg_out_movi (s, type, 0, arg2);
+ tcg_out32 (s, XOR | SAB (arg1, 0, 0));
+ }
+ }
+ }
+ else {
+ arg = 0;
+ tcg_out32 (s, XOR | SAB (arg1, 0, arg2));
+ }
+
+ if (arg == arg1 && arg1 == arg0) {
+ tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff);
+ tcg_out32 (s, SUBFE | TAB (arg0, 0, arg));
+ }
+ else {
+ tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff);
+ tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg));
+ }
+ break;
+
+ case TCG_COND_GT:
+ case TCG_COND_GTU:
+ sh = 30;
+ crop = 0;
+ goto crtest;
+
+ case TCG_COND_LT:
+ case TCG_COND_LTU:
+ sh = 29;
+ crop = 0;
+ goto crtest;
+
+ case TCG_COND_GE:
+ case TCG_COND_GEU:
+ sh = 31;
+ crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT);
+ goto crtest;
+
+ case TCG_COND_LE:
+ case TCG_COND_LEU:
+ sh = 31;
+ crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT);
+ crtest:
+ tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, type == TCG_TYPE_I64);
+ if (crop) tcg_out32 (s, crop);
+ tcg_out32 (s, MFCR | RT (0));
+ tcg_out32 (s, (RLWINM
+ | RA (arg0)
+ | RS (0)
+ | SH (sh)
+ | MB (31)
+ | ME (31)
+ )
+ );
+ break;
+
+ default:
+ tcg_abort ();
+ }
+}
+
+static void tcg_out_bc (TCGContext *s, int bc, int label_index)
+{
+ TCGLabel *l = &s->labels[label_index];
+
+ if (l->has_value)
+ tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value));
+ else {
+ uint16_t val = *(uint16_t *) &s->code_ptr[2];
+
+ /* Thanks to Andrzej Zaborowski */
+ tcg_out32 (s, bc | (val & 0xfffc));
+ tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0);
+ }
+}
+
+static void tcg_out_brcond (TCGContext *s, TCGCond cond,
+ TCGArg arg1, TCGArg arg2, int const_arg2,
+ int label_index, int arch64)
+{
+ tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, arch64);
+ tcg_out_bc (s, tcg_to_bc[cond], label_index);
+}
+
+void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr)
+{
+ TCGContext s;
+ unsigned long patch_size;
+
+ s.code_ptr = (uint8_t *) jmp_addr;
+ tcg_out_b (&s, 0, addr);
+ patch_size = s.code_ptr - (uint8_t *) jmp_addr;
+ flush_icache_range (jmp_addr, jmp_addr + patch_size);
+}
+
+static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args,
+ const int *const_args)
+{
+ int c;
+
+ switch (opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_movi (s, TCG_TYPE_I64, TCG_REG_R3, args[0]);
+ tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr);
+ break;
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+
+ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+ s->code_ptr += 28;
+ }
+ else {
+ tcg_abort ();
+ }
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+ case INDEX_op_br:
+ {
+ TCGLabel *l = &s->labels[args[0]];
+
+ if (l->has_value) {
+ tcg_out_b (s, 0, l->u.value);
+ }
+ else {
+ uint32_t val = *(uint32_t *) s->code_ptr;
+
+ /* Thanks to Andrzej Zaborowski */
+ tcg_out32 (s, B | (val & 0x3fffffc));
+ tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0);
+ }
+ }
+ break;
+ case INDEX_op_call:
+ tcg_out_call (s, args[0], const_args[0]);
+ break;
+ case INDEX_op_jmp:
+ if (const_args[0]) {
+ tcg_out_b (s, 0, args[0]);
+ }
+ else {
+ tcg_out32 (s, MTSPR | RS (args[0]) | CTR);
+ tcg_out32 (s, BCCTR | BO_ALWAYS);
+ }
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi (s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_movi_i64:
+ tcg_out_movi (s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ld8u_i32:
+ case INDEX_op_ld8u_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+ break;
+ case INDEX_op_ld8s_i32:
+ case INDEX_op_ld8s_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+ tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0]));
+ break;
+ case INDEX_op_ld16u_i32:
+ case INDEX_op_ld16u_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX);
+ break;
+ case INDEX_op_ld16s_i32:
+ case INDEX_op_ld16s_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX);
+ break;
+ case INDEX_op_ld_i32:
+ case INDEX_op_ld32u_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX);
+ break;
+ case INDEX_op_ld32s_i64:
+ tcg_out_ldsta (s, args[0], args[1], args[2], LWA, LWAX);
+ break;
+ case INDEX_op_ld_i64:
+ tcg_out_ldsta (s, args[0], args[1], args[2], LD, LDX);
+ break;
+ case INDEX_op_st8_i32:
+ case INDEX_op_st8_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX);
+ break;
+ case INDEX_op_st16_i32:
+ case INDEX_op_st16_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX);
+ break;
+ case INDEX_op_st_i32:
+ case INDEX_op_st32_i64:
+ tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX);
+ break;
+ case INDEX_op_st_i64:
+ tcg_out_ldsta (s, args[0], args[1], args[2], STD, STDX);
+ break;
+
+ case INDEX_op_add_i32:
+ if (const_args[2])
+ ppc_addi32 (s, args[0], args[1], args[2]);
+ else
+ tcg_out32 (s, ADD | TAB (args[0], args[1], args[2]));
+ break;
+ case INDEX_op_sub_i32:
+ if (const_args[2])
+ ppc_addi32 (s, args[0], args[1], -args[2]);
+ else
+ tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1]));
+ break;
+
+ case INDEX_op_and_i64:
+ case INDEX_op_and_i32:
+ if (const_args[2]) {
+ if ((args[2] & 0xffff) == args[2])
+ tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | args[2]);
+ else if ((args[2] & 0xffff0000) == args[2])
+ tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ else {
+ tcg_out_movi (s, (opc == INDEX_op_and_i32
+ ? TCG_TYPE_I32
+ : TCG_TYPE_I64),
+ 0, args[2]);
+ tcg_out32 (s, AND | SAB (args[1], args[0], 0));
+ }
+ }
+ else
+ tcg_out32 (s, AND | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_or_i64:
+ case INDEX_op_or_i32:
+ if (const_args[2]) {
+ if (args[2] & 0xffff) {
+ tcg_out32 (s, ORI | RS (args[1]) | RA (args[0])
+ | (args[2] & 0xffff));
+ if (args[2] >> 16)
+ tcg_out32 (s, ORIS | RS (args[0]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ }
+ else {
+ tcg_out32 (s, ORIS | RS (args[1]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ }
+ }
+ else
+ tcg_out32 (s, OR | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_xor_i64:
+ case INDEX_op_xor_i32:
+ if (const_args[2]) {
+ if ((args[2] & 0xffff) == args[2])
+ tcg_out32 (s, XORI | RS (args[1]) | RA (args[0])
+ | (args[2] & 0xffff));
+ else if ((args[2] & 0xffff0000) == args[2])
+ tcg_out32 (s, XORIS | RS (args[1]) | RA (args[0])
+ | ((args[2] >> 16) & 0xffff));
+ else {
+ tcg_out_movi (s, (opc == INDEX_op_and_i32
+ ? TCG_TYPE_I32
+ : TCG_TYPE_I64),
+ 0, args[2]);
+ tcg_out32 (s, XOR | SAB (args[1], args[0], 0));
+ }
+ }
+ else
+ tcg_out32 (s, XOR | SAB (args[1], args[0], args[2]));
+ break;
+
+ case INDEX_op_mul_i32:
+ if (const_args[2]) {
+ if (args[2] == (int16_t) args[2])
+ tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1])
+ | (args[2] & 0xffff));
+ else {
+ tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+ tcg_out32 (s, MULLW | TAB (args[0], args[1], 0));
+ }
+ }
+ else
+ tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2]));
+ break;
+
+ case INDEX_op_div_i32:
+ tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2]));
+ break;
+
+ case INDEX_op_divu_i32:
+ tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2]));
+ break;
+
+ case INDEX_op_rem_i32:
+ tcg_out32 (s, DIVW | TAB (0, args[1], args[2]));
+ tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+ tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+ break;
+
+ case INDEX_op_remu_i32:
+ tcg_out32 (s, DIVWU | TAB (0, args[1], args[2]));
+ tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+ tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+ break;
+
+ case INDEX_op_shl_i32:
+ if (const_args[2]) {
+ tcg_out32 (s, (RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (args[2])
+ | MB (0)
+ | ME (31 - args[2])
+ )
+ );
+ }
+ else
+ tcg_out32 (s, SLW | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_shr_i32:
+ if (const_args[2]) {
+ tcg_out32 (s, (RLWINM
+ | RA (args[0])
+ | RS (args[1])
+ | SH (32 - args[2])
+ | MB (args[2])
+ | ME (31)
+ )
+ );
+ }
+ else
+ tcg_out32 (s, SRW | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_sar_i32:
+ if (const_args[2])
+ tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2]));
+ else
+ tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2]));
+ break;
+
+ case INDEX_op_brcond_i32:
+ tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 0);
+ break;
+
+ case INDEX_op_brcond_i64:
+ tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 1);
+ break;
+
+ case INDEX_op_neg_i32:
+ case INDEX_op_neg_i64:
+ tcg_out32 (s, NEG | RT (args[0]) | RA (args[1]));
+ break;
+
+ case INDEX_op_add_i64:
+ if (const_args[2])
+ ppc_addi64 (s, args[0], args[1], args[2]);
+ else
+ tcg_out32 (s, ADD | TAB (args[0], args[1], args[2]));
+ break;
+ case INDEX_op_sub_i64:
+ if (const_args[2])
+ ppc_addi64 (s, args[0], args[1], -args[2]);
+ else
+ tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1]));
+ break;
+
+ case INDEX_op_shl_i64:
+ if (const_args[2])
+ tcg_out_rld (s, RLDICR, args[0], args[1], args[2], 63 - args[2]);
+ else
+ tcg_out32 (s, SLD | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_shr_i64:
+ if (const_args[2])
+ tcg_out_rld (s, RLDICL, args[0], args[1], 64 - args[2], args[2]);
+ else
+ tcg_out32 (s, SRD | SAB (args[1], args[0], args[2]));
+ break;
+ case INDEX_op_sar_i64:
+ if (const_args[2]) {
+ int sh = SH (args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1);
+ tcg_out32 (s, SRADI | RA (args[0]) | RS (args[1]) | sh);
+ }
+ else
+ tcg_out32 (s, SRAD | SAB (args[1], args[0], args[2]));
+ break;
+
+ case INDEX_op_mul_i64:
+ tcg_out32 (s, MULLD | TAB (args[0], args[1], args[2]));
+ break;
+ case INDEX_op_div_i64:
+ tcg_out32 (s, DIVD | TAB (args[0], args[1], args[2]));
+ break;
+ case INDEX_op_divu_i64:
+ tcg_out32 (s, DIVDU | TAB (args[0], args[1], args[2]));
+ break;
+ case INDEX_op_rem_i64:
+ tcg_out32 (s, DIVD | TAB (0, args[1], args[2]));
+ tcg_out32 (s, MULLD | TAB (0, 0, args[2]));
+ tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+ break;
+ case INDEX_op_remu_i64:
+ tcg_out32 (s, DIVDU | TAB (0, args[1], args[2]));
+ tcg_out32 (s, MULLD | TAB (0, 0, args[2]));
+ tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld (s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld (s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld (s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld (s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+ case INDEX_op_qemu_ld32u:
+ tcg_out_qemu_ld (s, args, 2);
+ break;
+ case INDEX_op_qemu_ld32s:
+ tcg_out_qemu_ld (s, args, 2 | 4);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld (s, args, 3);
+ break;
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st (s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st (s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st (s, args, 2);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st (s, args, 3);
+ break;
+
+ case INDEX_op_ext8s_i32:
+ case INDEX_op_ext8s_i64:
+ c = EXTSB;
+ goto gen_ext;
+ case INDEX_op_ext16s_i32:
+ case INDEX_op_ext16s_i64:
+ c = EXTSH;
+ goto gen_ext;
+ case INDEX_op_ext32s_i64:
+ c = EXTSW;
+ goto gen_ext;
+ gen_ext:
+ tcg_out32 (s, c | RS (args[1]) | RA (args[0]));
+ break;
+
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond (s, TCG_TYPE_I32, args[3], args[0], args[1], args[2],
+ const_args[2]);
+ break;
+ case INDEX_op_setcond_i64:
+ tcg_out_setcond (s, TCG_TYPE_I64, args[3], args[0], args[1], args[2],
+ const_args[2]);
+ break;
+
+ default:
+ tcg_dump_ops (s, stderr);
+ tcg_abort ();
+ }
+}
+
+static const TCGTargetOpDef ppc_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "ri" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_mov_i64, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+ { INDEX_op_movi_i64, { "r" } },
+
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_ld_i64, { "r", "r" } },
+ { INDEX_op_st8_i32, { "r", "r" } },
+ { INDEX_op_st8_i64, { "r", "r" } },
+ { INDEX_op_st16_i32, { "r", "r" } },
+ { INDEX_op_st16_i64, { "r", "r" } },
+ { INDEX_op_st_i32, { "r", "r" } },
+ { INDEX_op_st_i64, { "r", "r" } },
+ { INDEX_op_st32_i64, { "r", "r" } },
+
+ { INDEX_op_ld8u_i64, { "r", "r" } },
+ { INDEX_op_ld8s_i64, { "r", "r" } },
+ { INDEX_op_ld16u_i64, { "r", "r" } },
+ { INDEX_op_ld16s_i64, { "r", "r" } },
+ { INDEX_op_ld32u_i64, { "r", "r" } },
+ { INDEX_op_ld32s_i64, { "r", "r" } },
+ { INDEX_op_ld_i64, { "r", "r" } },
+
+ { INDEX_op_add_i32, { "r", "r", "ri" } },
+ { INDEX_op_mul_i32, { "r", "r", "ri" } },
+ { INDEX_op_div_i32, { "r", "r", "r" } },
+ { INDEX_op_divu_i32, { "r", "r", "r" } },
+ { INDEX_op_rem_i32, { "r", "r", "r" } },
+ { INDEX_op_remu_i32, { "r", "r", "r" } },
+ { INDEX_op_sub_i32, { "r", "r", "ri" } },
+ { INDEX_op_and_i32, { "r", "r", "ri" } },
+ { INDEX_op_or_i32, { "r", "r", "ri" } },
+ { INDEX_op_xor_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_shl_i32, { "r", "r", "ri" } },
+ { INDEX_op_shr_i32, { "r", "r", "ri" } },
+ { INDEX_op_sar_i32, { "r", "r", "ri" } },
+
+ { INDEX_op_brcond_i32, { "r", "ri" } },
+ { INDEX_op_brcond_i64, { "r", "ri" } },
+
+ { INDEX_op_neg_i32, { "r", "r" } },
+
+ { INDEX_op_add_i64, { "r", "r", "ri" } },
+ { INDEX_op_sub_i64, { "r", "r", "ri" } },
+ { INDEX_op_and_i64, { "r", "r", "rZ" } },
+ { INDEX_op_or_i64, { "r", "r", "rZ" } },
+ { INDEX_op_xor_i64, { "r", "r", "rZ" } },
+
+ { INDEX_op_shl_i64, { "r", "r", "ri" } },
+ { INDEX_op_shr_i64, { "r", "r", "ri" } },
+ { INDEX_op_sar_i64, { "r", "r", "ri" } },
+
+ { INDEX_op_mul_i64, { "r", "r", "r" } },
+ { INDEX_op_div_i64, { "r", "r", "r" } },
+ { INDEX_op_divu_i64, { "r", "r", "r" } },
+ { INDEX_op_rem_i64, { "r", "r", "r" } },
+ { INDEX_op_remu_i64, { "r", "r", "r" } },
+
+ { INDEX_op_neg_i64, { "r", "r" } },
+
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+ { INDEX_op_qemu_ld32u, { "r", "L" } },
+ { INDEX_op_qemu_ld32s, { "r", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "L" } },
+
+ { INDEX_op_qemu_st8, { "S", "S" } },
+ { INDEX_op_qemu_st16, { "S", "S" } },
+ { INDEX_op_qemu_st32, { "S", "S" } },
+ { INDEX_op_qemu_st64, { "S", "S" } },
+
+ { INDEX_op_ext8s_i32, { "r", "r" } },
+ { INDEX_op_ext16s_i32, { "r", "r" } },
+ { INDEX_op_ext8s_i64, { "r", "r" } },
+ { INDEX_op_ext16s_i64, { "r", "r" } },
+ { INDEX_op_ext32s_i64, { "r", "r" } },
+
+ { INDEX_op_setcond_i32, { "r", "r", "ri" } },
+ { INDEX_op_setcond_i64, { "r", "r", "ri" } },
+
+ { -1 },
+};
+
+static void tcg_target_init (TCGContext *s)
+{
+ tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+ tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff);
+ tcg_regset_set32 (tcg_target_call_clobber_regs, 0,
+ (1 << TCG_REG_R0) |
+#ifdef __APPLE__
+ (1 << TCG_REG_R2) |
+#endif
+ (1 << TCG_REG_R3) |
+ (1 << TCG_REG_R4) |
+ (1 << TCG_REG_R5) |
+ (1 << TCG_REG_R6) |
+ (1 << TCG_REG_R7) |
+ (1 << TCG_REG_R8) |
+ (1 << TCG_REG_R9) |
+ (1 << TCG_REG_R10) |
+ (1 << TCG_REG_R11) |
+ (1 << TCG_REG_R12)
+ );
+
+ tcg_regset_clear (s->reserved_regs);
+ tcg_regset_set_reg (s->reserved_regs, TCG_REG_R0);
+ tcg_regset_set_reg (s->reserved_regs, TCG_REG_R1);
+#ifndef __APPLE__
+ tcg_regset_set_reg (s->reserved_regs, TCG_REG_R2);
+#endif
+ tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13);
+
+ tcg_add_target_add_op_defs (ppc_op_defs);
+}
diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h
new file mode 100644
index 0000000..8a6db11
--- /dev/null
+++ b/tcg/ppc64/tcg-target.h
@@ -0,0 +1,109 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_PPC64 1
+
+#define TCG_TARGET_REG_BITS 64
+#define TCG_TARGET_WORDS_BIGENDIAN
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15,
+ TCG_REG_R16,
+ TCG_REG_R17,
+ TCG_REG_R18,
+ TCG_REG_R19,
+ TCG_REG_R20,
+ TCG_REG_R21,
+ TCG_REG_R22,
+ TCG_REG_R23,
+ TCG_REG_R24,
+ TCG_REG_R25,
+ TCG_REG_R26,
+ TCG_REG_R27,
+ TCG_REG_R28,
+ TCG_REG_R29,
+ TCG_REG_R30,
+ TCG_REG_R31
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R1
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_CALL_STACK_OFFSET 48
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div_i32
+/* #define TCG_TARGET_HAS_rot_i32 */
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+/* #define TCG_TARGET_HAS_ext8u_i32 */
+/* #define TCG_TARGET_HAS_ext16u_i32 */
+/* #define TCG_TARGET_HAS_bswap16_i32 */
+/* #define TCG_TARGET_HAS_bswap32_i32 */
+/* #define TCG_TARGET_HAS_not_i32 */
+#define TCG_TARGET_HAS_neg_i32
+/* #define TCG_TARGET_HAS_andc_i32 */
+/* #define TCG_TARGET_HAS_orc_i32 */
+/* #define TCG_TARGET_HAS_eqv_i32 */
+/* #define TCG_TARGET_HAS_nand_i32 */
+/* #define TCG_TARGET_HAS_nor_i32 */
+
+#define TCG_TARGET_HAS_div_i64
+/* #define TCG_TARGET_HAS_rot_i64 */
+#define TCG_TARGET_HAS_ext8s_i64
+#define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+/* #define TCG_TARGET_HAS_ext8u_i64 */
+/* #define TCG_TARGET_HAS_ext16u_i64 */
+/* #define TCG_TARGET_HAS_ext32u_i64 */
+/* #define TCG_TARGET_HAS_bswap16_i64 */
+/* #define TCG_TARGET_HAS_bswap32_i64 */
+/* #define TCG_TARGET_HAS_bswap64_i64 */
+/* #define TCG_TARGET_HAS_not_i64 */
+#define TCG_TARGET_HAS_neg_i64
+/* #define TCG_TARGET_HAS_andc_i64 */
+/* #define TCG_TARGET_HAS_orc_i64 */
+/* #define TCG_TARGET_HAS_eqv_i64 */
+/* #define TCG_TARGET_HAS_nand_i64 */
+/* #define TCG_TARGET_HAS_nor_i64 */
+
+#define TCG_AREG0 TCG_REG_R27
+
+#define TCG_TARGET_HAS_GUEST_BASE
+#define TCG_TARGET_EXTEND_ARGS 1
diff --git a/tcg/s390/tcg-target.c b/tcg/s390/tcg-target.c
new file mode 100644
index 0000000..450fcab
--- /dev/null
+++ b/tcg/s390/tcg-target.c
@@ -0,0 +1,2324 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2009 Ulrich Hecht <uli@suse.de>
+ * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
+ * Copyright (c) 2010 Richard Henderson <rth@twiddle.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* ??? The translation blocks produced by TCG are generally small enough to
+ be entirely reachable with a 16-bit displacement. Leaving the option for
+ a 32-bit displacement here Just In Case. */
+#define USE_LONG_BRANCHES 0
+
+#define TCG_CT_CONST_32 0x0100
+#define TCG_CT_CONST_NEG 0x0200
+#define TCG_CT_CONST_ADDI 0x0400
+#define TCG_CT_CONST_MULI 0x0800
+#define TCG_CT_CONST_ANDI 0x1000
+#define TCG_CT_CONST_ORI 0x2000
+#define TCG_CT_CONST_XORI 0x4000
+#define TCG_CT_CONST_CMPI 0x8000
+
+/* Several places within the instruction set 0 means "no register"
+ rather than TCG_REG_R0. */
+#define TCG_REG_NONE 0
+
+/* A scratch register that may be be used throughout the backend. */
+#define TCG_TMP0 TCG_REG_R14
+
+#ifdef CONFIG_USE_GUEST_BASE
+#define TCG_GUEST_BASE_REG TCG_REG_R13
+#else
+#define TCG_GUEST_BASE_REG TCG_REG_R0
+#endif
+
+#ifndef GUEST_BASE
+#define GUEST_BASE 0
+#endif
+
+
+/* All of the following instructions are prefixed with their instruction
+ format, and are defined as 8- or 16-bit quantities, even when the two
+ halves of the 16-bit quantity may appear 32 bits apart in the insn.
+ This makes it easy to copy the values from the tables in Appendix B. */
+typedef enum S390Opcode {
+ RIL_AFI = 0xc209,
+ RIL_AGFI = 0xc208,
+ RIL_ALGFI = 0xc20a,
+ RIL_BRASL = 0xc005,
+ RIL_BRCL = 0xc004,
+ RIL_CFI = 0xc20d,
+ RIL_CGFI = 0xc20c,
+ RIL_CLFI = 0xc20f,
+ RIL_CLGFI = 0xc20e,
+ RIL_IIHF = 0xc008,
+ RIL_IILF = 0xc009,
+ RIL_LARL = 0xc000,
+ RIL_LGFI = 0xc001,
+ RIL_LGRL = 0xc408,
+ RIL_LLIHF = 0xc00e,
+ RIL_LLILF = 0xc00f,
+ RIL_LRL = 0xc40d,
+ RIL_MSFI = 0xc201,
+ RIL_MSGFI = 0xc200,
+ RIL_NIHF = 0xc00a,
+ RIL_NILF = 0xc00b,
+ RIL_OIHF = 0xc00c,
+ RIL_OILF = 0xc00d,
+ RIL_XIHF = 0xc006,
+ RIL_XILF = 0xc007,
+
+ RI_AGHI = 0xa70b,
+ RI_AHI = 0xa70a,
+ RI_BRC = 0xa704,
+ RI_IIHH = 0xa500,
+ RI_IIHL = 0xa501,
+ RI_IILH = 0xa502,
+ RI_IILL = 0xa503,
+ RI_LGHI = 0xa709,
+ RI_LLIHH = 0xa50c,
+ RI_LLIHL = 0xa50d,
+ RI_LLILH = 0xa50e,
+ RI_LLILL = 0xa50f,
+ RI_MGHI = 0xa70d,
+ RI_MHI = 0xa70c,
+ RI_NIHH = 0xa504,
+ RI_NIHL = 0xa505,
+ RI_NILH = 0xa506,
+ RI_NILL = 0xa507,
+ RI_OIHH = 0xa508,
+ RI_OIHL = 0xa509,
+ RI_OILH = 0xa50a,
+ RI_OILL = 0xa50b,
+
+ RIE_CGIJ = 0xec7c,
+ RIE_CGRJ = 0xec64,
+ RIE_CIJ = 0xec7e,
+ RIE_CLGRJ = 0xec65,
+ RIE_CLIJ = 0xec7f,
+ RIE_CLGIJ = 0xec7d,
+ RIE_CLRJ = 0xec77,
+ RIE_CRJ = 0xec76,
+
+ RRE_AGR = 0xb908,
+ RRE_CGR = 0xb920,
+ RRE_CLGR = 0xb921,
+ RRE_DLGR = 0xb987,
+ RRE_DLR = 0xb997,
+ RRE_DSGFR = 0xb91d,
+ RRE_DSGR = 0xb90d,
+ RRE_LGBR = 0xb906,
+ RRE_LCGR = 0xb903,
+ RRE_LGFR = 0xb914,
+ RRE_LGHR = 0xb907,
+ RRE_LGR = 0xb904,
+ RRE_LLGCR = 0xb984,
+ RRE_LLGFR = 0xb916,
+ RRE_LLGHR = 0xb985,
+ RRE_LRVR = 0xb91f,
+ RRE_LRVGR = 0xb90f,
+ RRE_LTGR = 0xb902,
+ RRE_MSGR = 0xb90c,
+ RRE_MSR = 0xb252,
+ RRE_NGR = 0xb980,
+ RRE_OGR = 0xb981,
+ RRE_SGR = 0xb909,
+ RRE_XGR = 0xb982,
+
+ RR_AR = 0x1a,
+ RR_BASR = 0x0d,
+ RR_BCR = 0x07,
+ RR_CLR = 0x15,
+ RR_CR = 0x19,
+ RR_DR = 0x1d,
+ RR_LCR = 0x13,
+ RR_LR = 0x18,
+ RR_LTR = 0x12,
+ RR_NR = 0x14,
+ RR_OR = 0x16,
+ RR_SR = 0x1b,
+ RR_XR = 0x17,
+
+ RSY_RLL = 0xeb1d,
+ RSY_RLLG = 0xeb1c,
+ RSY_SLLG = 0xeb0d,
+ RSY_SRAG = 0xeb0a,
+ RSY_SRLG = 0xeb0c,
+
+ RS_SLL = 0x89,
+ RS_SRA = 0x8a,
+ RS_SRL = 0x88,
+
+ RXY_AG = 0xe308,
+ RXY_AY = 0xe35a,
+ RXY_CG = 0xe320,
+ RXY_CY = 0xe359,
+ RXY_LB = 0xe376,
+ RXY_LG = 0xe304,
+ RXY_LGB = 0xe377,
+ RXY_LGF = 0xe314,
+ RXY_LGH = 0xe315,
+ RXY_LHY = 0xe378,
+ RXY_LLGC = 0xe390,
+ RXY_LLGF = 0xe316,
+ RXY_LLGH = 0xe391,
+ RXY_LMG = 0xeb04,
+ RXY_LRV = 0xe31e,
+ RXY_LRVG = 0xe30f,
+ RXY_LRVH = 0xe31f,
+ RXY_LY = 0xe358,
+ RXY_STCY = 0xe372,
+ RXY_STG = 0xe324,
+ RXY_STHY = 0xe370,
+ RXY_STMG = 0xeb24,
+ RXY_STRV = 0xe33e,
+ RXY_STRVG = 0xe32f,
+ RXY_STRVH = 0xe33f,
+ RXY_STY = 0xe350,
+
+ RX_A = 0x5a,
+ RX_C = 0x59,
+ RX_L = 0x58,
+ RX_LH = 0x48,
+ RX_ST = 0x50,
+ RX_STC = 0x42,
+ RX_STH = 0x40,
+} S390Opcode;
+
+#define LD_SIGNED 0x04
+#define LD_UINT8 0x00
+#define LD_INT8 (LD_UINT8 | LD_SIGNED)
+#define LD_UINT16 0x01
+#define LD_INT16 (LD_UINT16 | LD_SIGNED)
+#define LD_UINT32 0x02
+#define LD_INT32 (LD_UINT32 | LD_SIGNED)
+#define LD_UINT64 0x03
+#define LD_INT64 (LD_UINT64 | LD_SIGNED)
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
+ "%r8", "%r9", "%r10" "%r11" "%r12" "%r13" "%r14" "%r15"
+};
+#endif
+
+/* Since R6 is a potential argument register, choose it last of the
+ call-saved registers. Likewise prefer the call-clobbered registers
+ in reverse order to maximize the chance of avoiding the arguments. */
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_R13,
+ TCG_REG_R12,
+ TCG_REG_R11,
+ TCG_REG_R10,
+ TCG_REG_R9,
+ TCG_REG_R8,
+ TCG_REG_R7,
+ TCG_REG_R6,
+ TCG_REG_R14,
+ TCG_REG_R0,
+ TCG_REG_R1,
+ TCG_REG_R5,
+ TCG_REG_R4,
+ TCG_REG_R3,
+ TCG_REG_R2,
+};
+
+static const int tcg_target_call_iarg_regs[] = {
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+};
+
+static const int tcg_target_call_oarg_regs[] = {
+ TCG_REG_R2,
+ TCG_REG_R3,
+};
+
+#define S390_CC_EQ 8
+#define S390_CC_LT 4
+#define S390_CC_GT 2
+#define S390_CC_OV 1
+#define S390_CC_NE (S390_CC_LT | S390_CC_GT)
+#define S390_CC_LE (S390_CC_LT | S390_CC_EQ)
+#define S390_CC_GE (S390_CC_GT | S390_CC_EQ)
+#define S390_CC_NEVER 0
+#define S390_CC_ALWAYS 15
+
+/* Condition codes that result from a COMPARE and COMPARE LOGICAL. */
+static const uint8_t tcg_cond_to_s390_cond[10] = {
+ [TCG_COND_EQ] = S390_CC_EQ,
+ [TCG_COND_NE] = S390_CC_NE,
+ [TCG_COND_LT] = S390_CC_LT,
+ [TCG_COND_LE] = S390_CC_LE,
+ [TCG_COND_GT] = S390_CC_GT,
+ [TCG_COND_GE] = S390_CC_GE,
+ [TCG_COND_LTU] = S390_CC_LT,
+ [TCG_COND_LEU] = S390_CC_LE,
+ [TCG_COND_GTU] = S390_CC_GT,
+ [TCG_COND_GEU] = S390_CC_GE,
+};
+
+/* Condition codes that result from a LOAD AND TEST. Here, we have no
+ unsigned instruction variation, however since the test is vs zero we
+ can re-map the outcomes appropriately. */
+static const uint8_t tcg_cond_to_ltr_cond[10] = {
+ [TCG_COND_EQ] = S390_CC_EQ,
+ [TCG_COND_NE] = S390_CC_NE,
+ [TCG_COND_LT] = S390_CC_LT,
+ [TCG_COND_LE] = S390_CC_LE,
+ [TCG_COND_GT] = S390_CC_GT,
+ [TCG_COND_GE] = S390_CC_GE,
+ [TCG_COND_LTU] = S390_CC_NEVER,
+ [TCG_COND_LEU] = S390_CC_EQ,
+ [TCG_COND_GTU] = S390_CC_NE,
+ [TCG_COND_GEU] = S390_CC_ALWAYS,
+};
+
+#ifdef CONFIG_SOFTMMU
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+#endif
+
+static uint8_t *tb_ret_addr;
+
+/* A list of relevant facilities used by this translator. Some of these
+ are required for proper operation, and these are checked at startup. */
+
+#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2))
+#define FACILITY_LONG_DISP (1ULL << (63 - 18))
+#define FACILITY_EXT_IMM (1ULL << (63 - 21))
+#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34))
+
+static uint64_t facilities;
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ tcg_target_long code_ptr_tl = (tcg_target_long)code_ptr;
+ tcg_target_long pcrel2;
+
+ /* ??? Not the usual definition of "addend". */
+ pcrel2 = (value - (code_ptr_tl + addend)) >> 1;
+
+ switch (type) {
+ case R_390_PC16DBL:
+ assert(pcrel2 == (int16_t)pcrel2);
+ *(int16_t *)code_ptr = pcrel2;
+ break;
+ case R_390_PC32DBL:
+ assert(pcrel2 == (int32_t)pcrel2);
+ *(int32_t *)code_ptr = pcrel2;
+ break;
+ default:
+ tcg_abort();
+ break;
+ }
+}
+
+static int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return sizeof(tcg_target_call_iarg_regs) / sizeof(int);
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str = *pct_str;
+
+ switch (ct_str[0]) {
+ case 'r': /* all registers */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffff);
+ break;
+ case 'R': /* not R0 */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffff);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+ break;
+ case 'L': /* qemu_ld/st constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffff);
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R2);
+ tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3);
+ break;
+ case 'a': /* force R2 for division */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_clear(ct->u.regs);
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_R2);
+ break;
+ case 'b': /* force R3 for division */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_clear(ct->u.regs);
+ tcg_regset_set_reg(ct->u.regs, TCG_REG_R3);
+ break;
+ case 'N': /* force immediate negate */
+ ct->ct |= TCG_CT_CONST_NEG;
+ break;
+ case 'W': /* force 32-bit ("word") immediate */
+ ct->ct |= TCG_CT_CONST_32;
+ break;
+ case 'I':
+ ct->ct |= TCG_CT_CONST_ADDI;
+ break;
+ case 'K':
+ ct->ct |= TCG_CT_CONST_MULI;
+ break;
+ case 'A':
+ ct->ct |= TCG_CT_CONST_ANDI;
+ break;
+ case 'O':
+ ct->ct |= TCG_CT_CONST_ORI;
+ break;
+ case 'X':
+ ct->ct |= TCG_CT_CONST_XORI;
+ break;
+ case 'C':
+ ct->ct |= TCG_CT_CONST_CMPI;
+ break;
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+
+ return 0;
+}
+
+/* Immediates to be used with logical AND. This is an optimization only,
+ since a full 64-bit immediate AND can always be performed with 4 sequential
+ NI[LH][LH] instructions. What we're looking for is immediates that we
+ can load efficiently, and the immediate load plus the reg-reg AND is
+ smaller than the sequential NI's. */
+
+static int tcg_match_andi(int ct, tcg_target_ulong val)
+{
+ int i;
+
+ if (facilities & FACILITY_EXT_IMM) {
+ if (ct & TCG_CT_CONST_32) {
+ /* All 32-bit ANDs can be performed with 1 48-bit insn. */
+ return 1;
+ }
+
+ /* Zero-extensions. */
+ if (val == 0xff || val == 0xffff || val == 0xffffffff) {
+ return 1;
+ }
+ } else {
+ if (ct & TCG_CT_CONST_32) {
+ val = (uint32_t)val;
+ } else if (val == 0xffffffff) {
+ return 1;
+ }
+ }
+
+ /* Try all 32-bit insns that can perform it in one go. */
+ for (i = 0; i < 4; i++) {
+ tcg_target_ulong mask = ~(0xffffull << i*16);
+ if ((val & mask) == mask) {
+ return 1;
+ }
+ }
+
+ /* Look for 16-bit values performing the mask. These are better
+ to load with LLI[LH][LH]. */
+ for (i = 0; i < 4; i++) {
+ tcg_target_ulong mask = 0xffffull << i*16;
+ if ((val & mask) == val) {
+ return 0;
+ }
+ }
+
+ /* Look for 32-bit values performing the 64-bit mask. These
+ are better to load with LLI[LH]F, or if extended immediates
+ not available, with a pair of LLI insns. */
+ if ((ct & TCG_CT_CONST_32) == 0) {
+ if (val <= 0xffffffff || (val & 0xffffffff) == 0) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Immediates to be used with logical OR. This is an optimization only,
+ since a full 64-bit immediate OR can always be performed with 4 sequential
+ OI[LH][LH] instructions. What we're looking for is immediates that we
+ can load efficiently, and the immediate load plus the reg-reg OR is
+ smaller than the sequential OI's. */
+
+static int tcg_match_ori(int ct, tcg_target_long val)
+{
+ if (facilities & FACILITY_EXT_IMM) {
+ if (ct & TCG_CT_CONST_32) {
+ /* All 32-bit ORs can be performed with 1 48-bit insn. */
+ return 1;
+ }
+ }
+
+ /* Look for negative values. These are best to load with LGHI. */
+ if (val < 0) {
+ if (val == (int16_t)val) {
+ return 0;
+ }
+ if (facilities & FACILITY_EXT_IMM) {
+ if (val == (int32_t)val) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Immediates to be used with logical XOR. This is almost, but not quite,
+ only an optimization. XOR with immediate is only supported with the
+ extended-immediate facility. That said, there are a few patterns for
+ which it is better to load the value into a register first. */
+
+static int tcg_match_xori(int ct, tcg_target_long val)
+{
+ if ((facilities & FACILITY_EXT_IMM) == 0) {
+ return 0;
+ }
+
+ if (ct & TCG_CT_CONST_32) {
+ /* All 32-bit XORs can be performed with 1 48-bit insn. */
+ return 1;
+ }
+
+ /* Look for negative values. These are best to load with LGHI. */
+ if (val < 0 && val == (int32_t)val) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Imediates to be used with comparisons. */
+
+static int tcg_match_cmpi(int ct, tcg_target_long val)
+{
+ if (facilities & FACILITY_EXT_IMM) {
+ /* The COMPARE IMMEDIATE instruction is available. */
+ if (ct & TCG_CT_CONST_32) {
+ /* We have a 32-bit immediate and can compare against anything. */
+ return 1;
+ } else {
+ /* ??? We have no insight here into whether the comparison is
+ signed or unsigned. The COMPARE IMMEDIATE insn uses a 32-bit
+ signed immediate, and the COMPARE LOGICAL IMMEDIATE insn uses
+ a 32-bit unsigned immediate. If we were to use the (semi)
+ obvious "val == (int32_t)val" we would be enabling unsigned
+ comparisons vs very large numbers. The only solution is to
+ take the intersection of the ranges. */
+ /* ??? Another possible solution is to simply lie and allow all
+ constants here and force the out-of-range values into a temp
+ register in tgen_cmp when we have knowledge of the actual
+ comparison code in use. */
+ return val >= 0 && val <= 0x7fffffff;
+ }
+ } else {
+ /* Only the LOAD AND TEST instruction is available. */
+ return val == 0;
+ }
+}
+
+/* Test if a constant matches the constraint. */
+static int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct = arg_ct->ct;
+
+ if (ct & TCG_CT_CONST) {
+ return 1;
+ }
+
+ /* Handle the modifiers. */
+ if (ct & TCG_CT_CONST_NEG) {
+ val = -val;
+ }
+ if (ct & TCG_CT_CONST_32) {
+ val = (int32_t)val;
+ }
+
+ /* The following are mutually exclusive. */
+ if (ct & TCG_CT_CONST_ADDI) {
+ /* Immediates that may be used with add. If we have the
+ extended-immediates facility then we have ADD IMMEDIATE
+ with signed and unsigned 32-bit, otherwise we have only
+ ADD HALFWORD IMMEDIATE with a signed 16-bit. */
+ if (facilities & FACILITY_EXT_IMM) {
+ return val == (int32_t)val || val == (uint32_t)val;
+ } else {
+ return val == (int16_t)val;
+ }
+ } else if (ct & TCG_CT_CONST_MULI) {
+ /* Immediates that may be used with multiply. If we have the
+ general-instruction-extensions, then we have MULTIPLY SINGLE
+ IMMEDIATE with a signed 32-bit, otherwise we have only
+ MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */
+ if (facilities & FACILITY_GEN_INST_EXT) {
+ return val == (int32_t)val;
+ } else {
+ return val == (int16_t)val;
+ }
+ } else if (ct & TCG_CT_CONST_ANDI) {
+ return tcg_match_andi(ct, val);
+ } else if (ct & TCG_CT_CONST_ORI) {
+ return tcg_match_ori(ct, val);
+ } else if (ct & TCG_CT_CONST_XORI) {
+ return tcg_match_xori(ct, val);
+ } else if (ct & TCG_CT_CONST_CMPI) {
+ return tcg_match_cmpi(ct, val);
+ }
+
+ return 0;
+}
+
+/* Emit instructions according to the given instruction format. */
+
+static void tcg_out_insn_RR(TCGContext *s, S390Opcode op, TCGReg r1, TCGReg r2)
+{
+ tcg_out16(s, (op << 8) | (r1 << 4) | r2);
+}
+
+static void tcg_out_insn_RRE(TCGContext *s, S390Opcode op,
+ TCGReg r1, TCGReg r2)
+{
+ tcg_out32(s, (op << 16) | (r1 << 4) | r2);
+}
+
+static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2)
+{
+ tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff));
+}
+
+static void tcg_out_insn_RIL(TCGContext *s, S390Opcode op, TCGReg r1, int i2)
+{
+ tcg_out16(s, op | (r1 << 4));
+ tcg_out32(s, i2);
+}
+
+static void tcg_out_insn_RS(TCGContext *s, S390Opcode op, TCGReg r1,
+ TCGReg b2, TCGReg r3, int disp)
+{
+ tcg_out32(s, (op << 24) | (r1 << 20) | (r3 << 16) | (b2 << 12)
+ | (disp & 0xfff));
+}
+
+static void tcg_out_insn_RSY(TCGContext *s, S390Opcode op, TCGReg r1,
+ TCGReg b2, TCGReg r3, int disp)
+{
+ tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3);
+ tcg_out32(s, (op & 0xff) | (b2 << 28)
+ | ((disp & 0xfff) << 16) | ((disp & 0xff000) >> 4));
+}
+
+#define tcg_out_insn_RX tcg_out_insn_RS
+#define tcg_out_insn_RXY tcg_out_insn_RSY
+
+/* Emit an opcode with "type-checking" of the format. */
+#define tcg_out_insn(S, FMT, OP, ...) \
+ glue(tcg_out_insn_,FMT)(S, glue(glue(FMT,_),OP), ## __VA_ARGS__)
+
+
+/* emit 64-bit shifts */
+static void tcg_out_sh64(TCGContext* s, S390Opcode op, TCGReg dest,
+ TCGReg src, TCGReg sh_reg, int sh_imm)
+{
+ tcg_out_insn_RSY(s, op, dest, sh_reg, src, sh_imm);
+}
+
+/* emit 32-bit shifts */
+static void tcg_out_sh32(TCGContext* s, S390Opcode op, TCGReg dest,
+ TCGReg sh_reg, int sh_imm)
+{
+ tcg_out_insn_RS(s, op, dest, sh_reg, 0, sh_imm);
+}
+
+static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src)
+{
+ if (src != dst) {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, LR, dst, src);
+ } else {
+ tcg_out_insn(s, RRE, LGR, dst, src);
+ }
+ }
+}
+
+/* load a register with an immediate value */
+static void tcg_out_movi(TCGContext *s, TCGType type,
+ TCGReg ret, tcg_target_long sval)
+{
+ static const S390Opcode lli_insns[4] = {
+ RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH
+ };
+
+ tcg_target_ulong uval = sval;
+ int i;
+
+ if (type == TCG_TYPE_I32) {
+ uval = (uint32_t)sval;
+ sval = (int32_t)sval;
+ }
+
+ /* Try all 32-bit insns that can load it in one go. */
+ if (sval >= -0x8000 && sval < 0x8000) {
+ tcg_out_insn(s, RI, LGHI, ret, sval);
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ tcg_target_long mask = 0xffffull << i*16;
+ if ((uval & mask) == uval) {
+ tcg_out_insn_RI(s, lli_insns[i], ret, uval >> i*16);
+ return;
+ }
+ }
+
+ /* Try all 48-bit insns that can load it in one go. */
+ if (facilities & FACILITY_EXT_IMM) {
+ if (sval == (int32_t)sval) {
+ tcg_out_insn(s, RIL, LGFI, ret, sval);
+ return;
+ }
+ if (uval <= 0xffffffff) {
+ tcg_out_insn(s, RIL, LLILF, ret, uval);
+ return;
+ }
+ if ((uval & 0xffffffff) == 0) {
+ tcg_out_insn(s, RIL, LLIHF, ret, uval >> 31 >> 1);
+ return;
+ }
+ }
+
+ /* Try for PC-relative address load. */
+ if ((sval & 1) == 0) {
+ intptr_t off = (sval - (intptr_t)s->code_ptr) >> 1;
+ if (off == (int32_t)off) {
+ tcg_out_insn(s, RIL, LARL, ret, off);
+ return;
+ }
+ }
+
+ /* If extended immediates are not present, then we may have to issue
+ several instructions to load the low 32 bits. */
+ if (!(facilities & FACILITY_EXT_IMM)) {
+ /* A 32-bit unsigned value can be loaded in 2 insns. And given
+ that the lli_insns loop above did not succeed, we know that
+ both insns are required. */
+ if (uval <= 0xffffffff) {
+ tcg_out_insn(s, RI, LLILL, ret, uval);
+ tcg_out_insn(s, RI, IILH, ret, uval >> 16);
+ return;
+ }
+
+ /* If all high bits are set, the value can be loaded in 2 or 3 insns.
+ We first want to make sure that all the high bits get set. With
+ luck the low 16-bits can be considered negative to perform that for
+ free, otherwise we load an explicit -1. */
+ if (sval >> 31 >> 1 == -1) {
+ if (uval & 0x8000) {
+ tcg_out_insn(s, RI, LGHI, ret, uval);
+ } else {
+ tcg_out_insn(s, RI, LGHI, ret, -1);
+ tcg_out_insn(s, RI, IILL, ret, uval);
+ }
+ tcg_out_insn(s, RI, IILH, ret, uval >> 16);
+ return;
+ }
+ }
+
+ /* If we get here, both the high and low parts have non-zero bits. */
+
+ /* Recurse to load the lower 32-bits. */
+ tcg_out_movi(s, TCG_TYPE_I32, ret, sval);
+
+ /* Insert data into the high 32-bits. */
+ uval = uval >> 31 >> 1;
+ if (facilities & FACILITY_EXT_IMM) {
+ if (uval < 0x10000) {
+ tcg_out_insn(s, RI, IIHL, ret, uval);
+ } else if ((uval & 0xffff) == 0) {
+ tcg_out_insn(s, RI, IIHH, ret, uval >> 16);
+ } else {
+ tcg_out_insn(s, RIL, IIHF, ret, uval);
+ }
+ } else {
+ if (uval & 0xffff) {
+ tcg_out_insn(s, RI, IIHL, ret, uval);
+ }
+ if (uval & 0xffff0000) {
+ tcg_out_insn(s, RI, IIHH, ret, uval >> 16);
+ }
+ }
+}
+
+
+/* Emit a load/store type instruction. Inputs are:
+ DATA: The register to be loaded or stored.
+ BASE+OFS: The effective address.
+ OPC_RX: If the operation has an RX format opcode (e.g. STC), otherwise 0.
+ OPC_RXY: The RXY format opcode for the operation (e.g. STCY). */
+
+static void tcg_out_mem(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy,
+ TCGReg data, TCGReg base, TCGReg index,
+ tcg_target_long ofs)
+{
+ if (ofs < -0x80000 || ofs >= 0x80000) {
+ /* Combine the low 16 bits of the offset with the actual load insn;
+ the high 48 bits must come from an immediate load. */
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs & ~0xffff);
+ ofs &= 0xffff;
+
+ /* If we were already given an index register, add it in. */
+ if (index != TCG_REG_NONE) {
+ tcg_out_insn(s, RRE, AGR, TCG_TMP0, index);
+ }
+ index = TCG_TMP0;
+ }
+
+ if (opc_rx && ofs >= 0 && ofs < 0x1000) {
+ tcg_out_insn_RX(s, opc_rx, data, base, index, ofs);
+ } else {
+ tcg_out_insn_RXY(s, opc_rxy, data, base, index, ofs);
+ }
+}
+
+
+/* load data without address translation or endianness conversion */
+static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data,
+ TCGReg base, tcg_target_long ofs)
+{
+ if (type == TCG_TYPE_I32) {
+ tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs);
+ } else {
+ tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs);
+ }
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data,
+ TCGReg base, tcg_target_long ofs)
+{
+ if (type == TCG_TYPE_I32) {
+ tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs);
+ } else {
+ tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs);
+ }
+}
+
+/* load data from an absolute host address */
+static void tcg_out_ld_abs(TCGContext *s, TCGType type, TCGReg dest, void *abs)
+{
+ tcg_target_long addr = (tcg_target_long)abs;
+
+ if (facilities & FACILITY_GEN_INST_EXT) {
+ tcg_target_long disp = (addr - (tcg_target_long)s->code_ptr) >> 1;
+ if (disp == (int32_t)disp) {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RIL, LRL, dest, disp);
+ } else {
+ tcg_out_insn(s, RIL, LGRL, dest, disp);
+ }
+ return;
+ }
+ }
+
+ tcg_out_movi(s, TCG_TYPE_PTR, dest, addr & ~0xffff);
+ tcg_out_ld(s, type, dest, dest, addr & 0xffff);
+}
+
+static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
+{
+ if (facilities & FACILITY_EXT_IMM) {
+ tcg_out_insn(s, RRE, LGBR, dest, src);
+ return;
+ }
+
+ if (type == TCG_TYPE_I32) {
+ if (dest == src) {
+ tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 24);
+ } else {
+ tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 24);
+ }
+ tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 24);
+ } else {
+ tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 56);
+ tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 56);
+ }
+}
+
+static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
+{
+ if (facilities & FACILITY_EXT_IMM) {
+ tcg_out_insn(s, RRE, LLGCR, dest, src);
+ return;
+ }
+
+ if (dest == src) {
+ tcg_out_movi(s, type, TCG_TMP0, 0xff);
+ src = TCG_TMP0;
+ } else {
+ tcg_out_movi(s, type, dest, 0xff);
+ }
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, NR, dest, src);
+ } else {
+ tcg_out_insn(s, RRE, NGR, dest, src);
+ }
+}
+
+static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
+{
+ if (facilities & FACILITY_EXT_IMM) {
+ tcg_out_insn(s, RRE, LGHR, dest, src);
+ return;
+ }
+
+ if (type == TCG_TYPE_I32) {
+ if (dest == src) {
+ tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 16);
+ } else {
+ tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 16);
+ }
+ tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 16);
+ } else {
+ tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 48);
+ tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 48);
+ }
+}
+
+static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src)
+{
+ if (facilities & FACILITY_EXT_IMM) {
+ tcg_out_insn(s, RRE, LLGHR, dest, src);
+ return;
+ }
+
+ if (dest == src) {
+ tcg_out_movi(s, type, TCG_TMP0, 0xffff);
+ src = TCG_TMP0;
+ } else {
+ tcg_out_movi(s, type, dest, 0xffff);
+ }
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, NR, dest, src);
+ } else {
+ tcg_out_insn(s, RRE, NGR, dest, src);
+ }
+}
+
+static inline void tgen_ext32s(TCGContext *s, TCGReg dest, TCGReg src)
+{
+ tcg_out_insn(s, RRE, LGFR, dest, src);
+}
+
+static inline void tgen_ext32u(TCGContext *s, TCGReg dest, TCGReg src)
+{
+ tcg_out_insn(s, RRE, LLGFR, dest, src);
+}
+
+static inline void tgen32_addi(TCGContext *s, TCGReg dest, int32_t val)
+{
+ if (val == (int16_t)val) {
+ tcg_out_insn(s, RI, AHI, dest, val);
+ } else {
+ tcg_out_insn(s, RIL, AFI, dest, val);
+ }
+}
+
+static inline void tgen64_addi(TCGContext *s, TCGReg dest, int64_t val)
+{
+ if (val == (int16_t)val) {
+ tcg_out_insn(s, RI, AGHI, dest, val);
+ } else if (val == (int32_t)val) {
+ tcg_out_insn(s, RIL, AGFI, dest, val);
+ } else if (val == (uint32_t)val) {
+ tcg_out_insn(s, RIL, ALGFI, dest, val);
+ } else {
+ tcg_abort();
+ }
+
+}
+
+static void tgen64_andi(TCGContext *s, TCGReg dest, tcg_target_ulong val)
+{
+ static const S390Opcode ni_insns[4] = {
+ RI_NILL, RI_NILH, RI_NIHL, RI_NIHH
+ };
+ static const S390Opcode nif_insns[2] = {
+ RIL_NILF, RIL_NIHF
+ };
+
+ int i;
+
+ /* Look for no-op. */
+ if (val == -1) {
+ return;
+ }
+
+ /* Look for the zero-extensions. */
+ if (val == 0xffffffff) {
+ tgen_ext32u(s, dest, dest);
+ return;
+ }
+
+ if (facilities & FACILITY_EXT_IMM) {
+ if (val == 0xff) {
+ tgen_ext8u(s, TCG_TYPE_I64, dest, dest);
+ return;
+ }
+ if (val == 0xffff) {
+ tgen_ext16u(s, TCG_TYPE_I64, dest, dest);
+ return;
+ }
+
+ /* Try all 32-bit insns that can perform it in one go. */
+ for (i = 0; i < 4; i++) {
+ tcg_target_ulong mask = ~(0xffffull << i*16);
+ if ((val & mask) == mask) {
+ tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16);
+ return;
+ }
+ }
+
+ /* Try all 48-bit insns that can perform it in one go. */
+ if (facilities & FACILITY_EXT_IMM) {
+ for (i = 0; i < 2; i++) {
+ tcg_target_ulong mask = ~(0xffffffffull << i*32);
+ if ((val & mask) == mask) {
+ tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32);
+ return;
+ }
+ }
+ }
+
+ /* Perform the AND via sequential modifications to the high and low
+ parts. Do this via recursion to handle 16-bit vs 32-bit masks in
+ each half. */
+ tgen64_andi(s, dest, val | 0xffffffff00000000ull);
+ tgen64_andi(s, dest, val | 0x00000000ffffffffull);
+ } else {
+ /* With no extended-immediate facility, just emit the sequence. */
+ for (i = 0; i < 4; i++) {
+ tcg_target_ulong mask = 0xffffull << i*16;
+ if ((val & mask) != mask) {
+ tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16);
+ }
+ }
+ }
+}
+
+static void tgen64_ori(TCGContext *s, TCGReg dest, tcg_target_ulong val)
+{
+ static const S390Opcode oi_insns[4] = {
+ RI_OILL, RI_OILH, RI_OIHL, RI_OIHH
+ };
+ static const S390Opcode nif_insns[2] = {
+ RIL_OILF, RIL_OIHF
+ };
+
+ int i;
+
+ /* Look for no-op. */
+ if (val == 0) {
+ return;
+ }
+
+ if (facilities & FACILITY_EXT_IMM) {
+ /* Try all 32-bit insns that can perform it in one go. */
+ for (i = 0; i < 4; i++) {
+ tcg_target_ulong mask = (0xffffull << i*16);
+ if ((val & mask) != 0 && (val & ~mask) == 0) {
+ tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16);
+ return;
+ }
+ }
+
+ /* Try all 48-bit insns that can perform it in one go. */
+ for (i = 0; i < 2; i++) {
+ tcg_target_ulong mask = (0xffffffffull << i*32);
+ if ((val & mask) != 0 && (val & ~mask) == 0) {
+ tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32);
+ return;
+ }
+ }
+
+ /* Perform the OR via sequential modifications to the high and
+ low parts. Do this via recursion to handle 16-bit vs 32-bit
+ masks in each half. */
+ tgen64_ori(s, dest, val & 0x00000000ffffffffull);
+ tgen64_ori(s, dest, val & 0xffffffff00000000ull);
+ } else {
+ /* With no extended-immediate facility, we don't need to be so
+ clever. Just iterate over the insns and mask in the constant. */
+ for (i = 0; i < 4; i++) {
+ tcg_target_ulong mask = (0xffffull << i*16);
+ if ((val & mask) != 0) {
+ tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16);
+ }
+ }
+ }
+}
+
+static void tgen64_xori(TCGContext *s, TCGReg dest, tcg_target_ulong val)
+{
+ /* Perform the xor by parts. */
+ if (val & 0xffffffff) {
+ tcg_out_insn(s, RIL, XILF, dest, val);
+ }
+ if (val > 0xffffffff) {
+ tcg_out_insn(s, RIL, XIHF, dest, val >> 31 >> 1);
+ }
+}
+
+static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1,
+ TCGArg c2, int c2const)
+{
+ bool is_unsigned = (c > TCG_COND_GT);
+ if (c2const) {
+ if (c2 == 0) {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, LTR, r1, r1);
+ } else {
+ tcg_out_insn(s, RRE, LTGR, r1, r1);
+ }
+ return tcg_cond_to_ltr_cond[c];
+ } else {
+ if (is_unsigned) {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RIL, CLFI, r1, c2);
+ } else {
+ tcg_out_insn(s, RIL, CLGFI, r1, c2);
+ }
+ } else {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RIL, CFI, r1, c2);
+ } else {
+ tcg_out_insn(s, RIL, CGFI, r1, c2);
+ }
+ }
+ }
+ } else {
+ if (is_unsigned) {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, CLR, r1, c2);
+ } else {
+ tcg_out_insn(s, RRE, CLGR, r1, c2);
+ }
+ } else {
+ if (type == TCG_TYPE_I32) {
+ tcg_out_insn(s, RR, CR, r1, c2);
+ } else {
+ tcg_out_insn(s, RRE, CGR, r1, c2);
+ }
+ }
+ }
+ return tcg_cond_to_s390_cond[c];
+}
+
+static void tgen_setcond(TCGContext *s, TCGType type, TCGCond c,
+ TCGReg dest, TCGReg r1, TCGArg c2, int c2const)
+{
+ int cc = tgen_cmp(s, type, c, r1, c2, c2const);
+
+ /* Emit: r1 = 1; if (cc) goto over; r1 = 0; over: */
+ tcg_out_movi(s, type, dest, 1);
+ tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1);
+ tcg_out_movi(s, type, dest, 0);
+}
+
+static void tgen_gotoi(TCGContext *s, int cc, tcg_target_long dest)
+{
+ tcg_target_long off = (dest - (tcg_target_long)s->code_ptr) >> 1;
+ if (off > -0x8000 && off < 0x7fff) {
+ tcg_out_insn(s, RI, BRC, cc, off);
+ } else if (off == (int32_t)off) {
+ tcg_out_insn(s, RIL, BRCL, cc, off);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, dest);
+ tcg_out_insn(s, RR, BCR, cc, TCG_TMP0);
+ }
+}
+
+static void tgen_branch(TCGContext *s, int cc, int labelno)
+{
+ TCGLabel* l = &s->labels[labelno];
+ if (l->has_value) {
+ tgen_gotoi(s, cc, l->u.value);
+ } else if (USE_LONG_BRANCHES) {
+ tcg_out16(s, RIL_BRCL | (cc << 4));
+ tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, labelno, -2);
+ s->code_ptr += 4;
+ } else {
+ tcg_out16(s, RI_BRC | (cc << 4));
+ tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, labelno, -2);
+ s->code_ptr += 2;
+ }
+}
+
+static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc,
+ TCGReg r1, TCGReg r2, int labelno)
+{
+ TCGLabel* l = &s->labels[labelno];
+ tcg_target_long off;
+
+ if (l->has_value) {
+ off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1;
+ } else {
+ /* We need to keep the offset unchanged for retranslation. */
+ off = ((int16_t *)s->code_ptr)[1];
+ tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2);
+ }
+
+ tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2);
+ tcg_out16(s, off);
+ tcg_out16(s, cc << 12 | (opc & 0xff));
+}
+
+static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc,
+ TCGReg r1, int i2, int labelno)
+{
+ TCGLabel* l = &s->labels[labelno];
+ tcg_target_long off;
+
+ if (l->has_value) {
+ off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1;
+ } else {
+ /* We need to keep the offset unchanged for retranslation. */
+ off = ((int16_t *)s->code_ptr)[1];
+ tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2);
+ }
+
+ tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc);
+ tcg_out16(s, off);
+ tcg_out16(s, (i2 << 8) | (opc & 0xff));
+}
+
+static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c,
+ TCGReg r1, TCGArg c2, int c2const, int labelno)
+{
+ int cc;
+
+ if (facilities & FACILITY_GEN_INST_EXT) {
+ bool is_unsigned = (c > TCG_COND_GT);
+ bool in_range;
+ S390Opcode opc;
+
+ cc = tcg_cond_to_s390_cond[c];
+
+ if (!c2const) {
+ opc = (type == TCG_TYPE_I32
+ ? (is_unsigned ? RIE_CLRJ : RIE_CRJ)
+ : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ));
+ tgen_compare_branch(s, opc, cc, r1, c2, labelno);
+ return;
+ }
+
+ /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field.
+ If the immediate we've been given does not fit that range, we'll
+ fall back to separate compare and branch instructions using the
+ larger comparison range afforded by COMPARE IMMEDIATE. */
+ if (type == TCG_TYPE_I32) {
+ if (is_unsigned) {
+ opc = RIE_CLIJ;
+ in_range = (uint32_t)c2 == (uint8_t)c2;
+ } else {
+ opc = RIE_CIJ;
+ in_range = (int32_t)c2 == (int8_t)c2;
+ }
+ } else {
+ if (is_unsigned) {
+ opc = RIE_CLGIJ;
+ in_range = (uint64_t)c2 == (uint8_t)c2;
+ } else {
+ opc = RIE_CGIJ;
+ in_range = (int64_t)c2 == (int8_t)c2;
+ }
+ }
+ if (in_range) {
+ tgen_compare_imm_branch(s, opc, cc, r1, c2, labelno);
+ return;
+ }
+ }
+
+ cc = tgen_cmp(s, type, c, r1, c2, c2const);
+ tgen_branch(s, cc, labelno);
+}
+
+static void tgen_calli(TCGContext *s, tcg_target_long dest)
+{
+ tcg_target_long off = (dest - (tcg_target_long)s->code_ptr) >> 1;
+ if (off == (int32_t)off) {
+ tcg_out_insn(s, RIL, BRASL, TCG_REG_R14, off);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, dest);
+ tcg_out_insn(s, RR, BASR, TCG_REG_R14, TCG_TMP0);
+ }
+}
+
+static void tcg_out_qemu_ld_direct(TCGContext *s, int opc, TCGReg data,
+ TCGReg base, TCGReg index, int disp)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ const int bswap = 0;
+#else
+ const int bswap = 1;
+#endif
+ switch (opc) {
+ case LD_UINT8:
+ tcg_out_insn(s, RXY, LLGC, data, base, index, disp);
+ break;
+ case LD_INT8:
+ tcg_out_insn(s, RXY, LGB, data, base, index, disp);
+ break;
+ case LD_UINT16:
+ if (bswap) {
+ /* swapped unsigned halfword load with upper bits zeroed */
+ tcg_out_insn(s, RXY, LRVH, data, base, index, disp);
+ tgen_ext16u(s, TCG_TYPE_I64, data, data);
+ } else {
+ tcg_out_insn(s, RXY, LLGH, data, base, index, disp);
+ }
+ break;
+ case LD_INT16:
+ if (bswap) {
+ /* swapped sign-extended halfword load */
+ tcg_out_insn(s, RXY, LRVH, data, base, index, disp);
+ tgen_ext16s(s, TCG_TYPE_I64, data, data);
+ } else {
+ tcg_out_insn(s, RXY, LGH, data, base, index, disp);
+ }
+ break;
+ case LD_UINT32:
+ if (bswap) {
+ /* swapped unsigned int load with upper bits zeroed */
+ tcg_out_insn(s, RXY, LRV, data, base, index, disp);
+ tgen_ext32u(s, data, data);
+ } else {
+ tcg_out_insn(s, RXY, LLGF, data, base, index, disp);
+ }
+ break;
+ case LD_INT32:
+ if (bswap) {
+ /* swapped sign-extended int load */
+ tcg_out_insn(s, RXY, LRV, data, base, index, disp);
+ tgen_ext32s(s, data, data);
+ } else {
+ tcg_out_insn(s, RXY, LGF, data, base, index, disp);
+ }
+ break;
+ case LD_UINT64:
+ if (bswap) {
+ tcg_out_insn(s, RXY, LRVG, data, base, index, disp);
+ } else {
+ tcg_out_insn(s, RXY, LG, data, base, index, disp);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+static void tcg_out_qemu_st_direct(TCGContext *s, int opc, TCGReg data,
+ TCGReg base, TCGReg index, int disp)
+{
+#ifdef TARGET_WORDS_BIGENDIAN
+ const int bswap = 0;
+#else
+ const int bswap = 1;
+#endif
+ switch (opc) {
+ case LD_UINT8:
+ if (disp >= 0 && disp < 0x1000) {
+ tcg_out_insn(s, RX, STC, data, base, index, disp);
+ } else {
+ tcg_out_insn(s, RXY, STCY, data, base, index, disp);
+ }
+ break;
+ case LD_UINT16:
+ if (bswap) {
+ tcg_out_insn(s, RXY, STRVH, data, base, index, disp);
+ } else if (disp >= 0 && disp < 0x1000) {
+ tcg_out_insn(s, RX, STH, data, base, index, disp);
+ } else {
+ tcg_out_insn(s, RXY, STHY, data, base, index, disp);
+ }
+ break;
+ case LD_UINT32:
+ if (bswap) {
+ tcg_out_insn(s, RXY, STRV, data, base, index, disp);
+ } else if (disp >= 0 && disp < 0x1000) {
+ tcg_out_insn(s, RX, ST, data, base, index, disp);
+ } else {
+ tcg_out_insn(s, RXY, STY, data, base, index, disp);
+ }
+ break;
+ case LD_UINT64:
+ if (bswap) {
+ tcg_out_insn(s, RXY, STRVG, data, base, index, disp);
+ } else {
+ tcg_out_insn(s, RXY, STG, data, base, index, disp);
+ }
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+#if defined(CONFIG_SOFTMMU)
+static void tgen64_andi_tmp(TCGContext *s, TCGReg dest, tcg_target_ulong val)
+{
+ if (tcg_match_andi(0, val)) {
+ tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, val);
+ tcg_out_insn(s, RRE, NGR, dest, TCG_TMP0);
+ } else {
+ tgen64_andi(s, dest, val);
+ }
+}
+
+static void tcg_prepare_qemu_ldst(TCGContext* s, TCGReg data_reg,
+ TCGReg addr_reg, int mem_index, int opc,
+ uint16_t **label2_ptr_p, int is_store)
+{
+ const TCGReg arg0 = TCG_REG_R2;
+ const TCGReg arg1 = TCG_REG_R3;
+ int s_bits = opc & 3;
+ uint16_t *label1_ptr;
+ tcg_target_long ofs;
+
+ if (TARGET_LONG_BITS == 32) {
+ tgen_ext32u(s, arg0, addr_reg);
+ } else {
+ tcg_out_mov(s, TCG_TYPE_I64, arg0, addr_reg);
+ }
+
+ tcg_out_sh64(s, RSY_SRLG, arg1, addr_reg, TCG_REG_NONE,
+ TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+
+ tgen64_andi_tmp(s, arg0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+ tgen64_andi_tmp(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+ if (is_store) {
+ ofs = offsetof(CPUState, tlb_table[mem_index][0].addr_write);
+ } else {
+ ofs = offsetof(CPUState, tlb_table[mem_index][0].addr_read);
+ }
+ assert(ofs < 0x80000);
+
+ if (TARGET_LONG_BITS == 32) {
+ tcg_out_mem(s, RX_C, RXY_CY, arg0, arg1, TCG_AREG0, ofs);
+ } else {
+ tcg_out_mem(s, 0, RXY_CG, arg0, arg1, TCG_AREG0, ofs);
+ }
+
+ if (TARGET_LONG_BITS == 32) {
+ tgen_ext32u(s, arg0, addr_reg);
+ } else {
+ tcg_out_mov(s, TCG_TYPE_I64, arg0, addr_reg);
+ }
+
+ label1_ptr = (uint16_t*)s->code_ptr;
+
+ /* je label1 (offset will be patched in later) */
+ tcg_out_insn(s, RI, BRC, S390_CC_EQ, 0);
+
+ /* call load/store helper */
+ if (is_store) {
+ /* Make sure to zero-extend the value to the full register
+ for the calling convention. */
+ switch (opc) {
+ case LD_UINT8:
+ tgen_ext8u(s, TCG_TYPE_I64, arg1, data_reg);
+ break;
+ case LD_UINT16:
+ tgen_ext16u(s, TCG_TYPE_I64, arg1, data_reg);
+ break;
+ case LD_UINT32:
+ tgen_ext32u(s, arg1, data_reg);
+ break;
+ case LD_UINT64:
+ tcg_out_mov(s, TCG_TYPE_I64, arg1, data_reg);
+ break;
+ default:
+ tcg_abort();
+ }
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, mem_index);
+ tgen_calli(s, (tcg_target_ulong)qemu_st_helpers[s_bits]);
+ } else {
+ tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index);
+ tgen_calli(s, (tcg_target_ulong)qemu_ld_helpers[s_bits]);
+
+ /* sign extension */
+ switch (opc) {
+ case LD_INT8:
+ tgen_ext8s(s, TCG_TYPE_I64, data_reg, arg0);
+ break;
+ case LD_INT16:
+ tgen_ext16s(s, TCG_TYPE_I64, data_reg, arg0);
+ break;
+ case LD_INT32:
+ tgen_ext32s(s, data_reg, arg0);
+ break;
+ default:
+ /* unsigned -> just copy */
+ tcg_out_mov(s, TCG_TYPE_I64, data_reg, arg0);
+ break;
+ }
+ }
+
+ /* jump to label2 (end) */
+ *label2_ptr_p = (uint16_t*)s->code_ptr;
+
+ tcg_out_insn(s, RI, BRC, S390_CC_ALWAYS, 0);
+
+ /* this is label1, patch branch */
+ *(label1_ptr + 1) = ((unsigned long)s->code_ptr -
+ (unsigned long)label1_ptr) >> 1;
+
+ ofs = offsetof(CPUState, tlb_table[mem_index][0].addend);
+ assert(ofs < 0x80000);
+
+ tcg_out_mem(s, 0, RXY_AG, arg0, arg1, TCG_AREG0, ofs);
+}
+
+static void tcg_finish_qemu_ldst(TCGContext* s, uint16_t *label2_ptr)
+{
+ /* patch branch */
+ *(label2_ptr + 1) = ((unsigned long)s->code_ptr -
+ (unsigned long)label2_ptr) >> 1;
+}
+#else
+static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg,
+ TCGReg *index_reg, tcg_target_long *disp)
+{
+ if (TARGET_LONG_BITS == 32) {
+ tgen_ext32u(s, TCG_TMP0, *addr_reg);
+ *addr_reg = TCG_TMP0;
+ }
+ if (GUEST_BASE < 0x80000) {
+ *index_reg = TCG_REG_NONE;
+ *disp = GUEST_BASE;
+ } else {
+ *index_reg = TCG_GUEST_BASE_REG;
+ *disp = 0;
+ }
+}
+#endif /* CONFIG_SOFTMMU */
+
+/* load data with address translation (if applicable)
+ and endianness conversion */
+static void tcg_out_qemu_ld(TCGContext* s, const TCGArg* args, int opc)
+{
+ TCGReg addr_reg, data_reg;
+#if defined(CONFIG_SOFTMMU)
+ int mem_index;
+ uint16_t *label2_ptr;
+#else
+ TCGReg index_reg;
+ tcg_target_long disp;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+
+#if defined(CONFIG_SOFTMMU)
+ mem_index = *args;
+
+ tcg_prepare_qemu_ldst(s, data_reg, addr_reg, mem_index,
+ opc, &label2_ptr, 0);
+
+ tcg_out_qemu_ld_direct(s, opc, data_reg, TCG_REG_R2, TCG_REG_NONE, 0);
+
+ tcg_finish_qemu_ldst(s, label2_ptr);
+#else
+ tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp);
+ tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp);
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext* s, const TCGArg* args, int opc)
+{
+ TCGReg addr_reg, data_reg;
+#if defined(CONFIG_SOFTMMU)
+ int mem_index;
+ uint16_t *label2_ptr;
+#else
+ TCGReg index_reg;
+ tcg_target_long disp;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+
+#if defined(CONFIG_SOFTMMU)
+ mem_index = *args;
+
+ tcg_prepare_qemu_ldst(s, data_reg, addr_reg, mem_index,
+ opc, &label2_ptr, 1);
+
+ tcg_out_qemu_st_direct(s, opc, data_reg, TCG_REG_R2, TCG_REG_NONE, 0);
+
+ tcg_finish_qemu_ldst(s, label2_ptr);
+#else
+ tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp);
+ tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp);
+#endif
+}
+
+#if TCG_TARGET_REG_BITS == 64
+# define OP_32_64(x) \
+ case glue(glue(INDEX_op_,x),_i32): \
+ case glue(glue(INDEX_op_,x),_i64)
+#else
+# define OP_32_64(x) \
+ case glue(glue(INDEX_op_,x),_i32)
+#endif
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
+ const TCGArg *args, const int *const_args)
+{
+ S390Opcode op;
+
+ switch (opc) {
+ case INDEX_op_exit_tb:
+ /* return value */
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, args[0]);
+ tgen_gotoi(s, S390_CC_ALWAYS, (unsigned long)tb_ret_addr);
+ break;
+
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ tcg_abort();
+ } else {
+ /* load address stored at s->tb_next + args[0] */
+ tcg_out_ld_abs(s, TCG_TYPE_PTR, TCG_TMP0, s->tb_next + args[0]);
+ /* and go there */
+ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_TMP0);
+ }
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+
+ case INDEX_op_call:
+ if (const_args[0]) {
+ tgen_calli(s, args[0]);
+ } else {
+ tcg_out_insn(s, RR, BASR, TCG_REG_R14, args[0]);
+ }
+ break;
+
+ case INDEX_op_mov_i32:
+ tcg_out_mov(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+
+ OP_32_64(ld8u):
+ /* ??? LLC (RXY format) is only present with the extended-immediate
+ facility, whereas LLGC is always present. */
+ tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+
+ OP_32_64(ld8s):
+ /* ??? LB is no smaller than LGB, so no point to using it. */
+ tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+
+ OP_32_64(ld16u):
+ /* ??? LLH (RXY format) is only present with the extended-immediate
+ facility, whereas LLGH is always present. */
+ tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+
+ case INDEX_op_ld16s_i32:
+ tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+
+ case INDEX_op_ld_i32:
+ tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]);
+ break;
+
+ OP_32_64(st8):
+ tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1],
+ TCG_REG_NONE, args[2]);
+ break;
+
+ OP_32_64(st16):
+ tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1],
+ TCG_REG_NONE, args[2]);
+ break;
+
+ case INDEX_op_st_i32:
+ tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_add_i32:
+ if (const_args[2]) {
+ tgen32_addi(s, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RR, AR, args[0], args[2]);
+ }
+ break;
+ case INDEX_op_sub_i32:
+ if (const_args[2]) {
+ tgen32_addi(s, args[0], -args[2]);
+ } else {
+ tcg_out_insn(s, RR, SR, args[0], args[2]);
+ }
+ break;
+
+ case INDEX_op_and_i32:
+ if (const_args[2]) {
+ tgen64_andi(s, args[0], args[2] | 0xffffffff00000000ull);
+ } else {
+ tcg_out_insn(s, RR, NR, args[0], args[2]);
+ }
+ break;
+ case INDEX_op_or_i32:
+ if (const_args[2]) {
+ tgen64_ori(s, args[0], args[2] & 0xffffffff);
+ } else {
+ tcg_out_insn(s, RR, OR, args[0], args[2]);
+ }
+ break;
+ case INDEX_op_xor_i32:
+ if (const_args[2]) {
+ tgen64_xori(s, args[0], args[2] & 0xffffffff);
+ } else {
+ tcg_out_insn(s, RR, XR, args[0], args[2]);
+ }
+ break;
+
+ case INDEX_op_neg_i32:
+ tcg_out_insn(s, RR, LCR, args[0], args[1]);
+ break;
+
+ case INDEX_op_mul_i32:
+ if (const_args[2]) {
+ if ((int32_t)args[2] == (int16_t)args[2]) {
+ tcg_out_insn(s, RI, MHI, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RIL, MSFI, args[0], args[2]);
+ }
+ } else {
+ tcg_out_insn(s, RRE, MSR, args[0], args[2]);
+ }
+ break;
+
+ case INDEX_op_div2_i32:
+ tcg_out_insn(s, RR, DR, TCG_REG_R2, args[4]);
+ break;
+ case INDEX_op_divu2_i32:
+ tcg_out_insn(s, RRE, DLR, TCG_REG_R2, args[4]);
+ break;
+
+ case INDEX_op_shl_i32:
+ op = RS_SLL;
+ do_shift32:
+ if (const_args[2]) {
+ tcg_out_sh32(s, op, args[0], TCG_REG_NONE, args[2]);
+ } else {
+ tcg_out_sh32(s, op, args[0], args[2], 0);
+ }
+ break;
+ case INDEX_op_shr_i32:
+ op = RS_SRL;
+ goto do_shift32;
+ case INDEX_op_sar_i32:
+ op = RS_SRA;
+ goto do_shift32;
+
+ case INDEX_op_rotl_i32:
+ /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */
+ if (const_args[2]) {
+ tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]);
+ } else {
+ tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0);
+ }
+ break;
+ case INDEX_op_rotr_i32:
+ if (const_args[2]) {
+ tcg_out_sh64(s, RSY_RLL, args[0], args[1],
+ TCG_REG_NONE, (32 - args[2]) & 31);
+ } else {
+ tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]);
+ tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0);
+ }
+ break;
+
+ case INDEX_op_ext8s_i32:
+ tgen_ext8s(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_ext16s_i32:
+ tgen_ext16s(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_ext8u_i32:
+ tgen_ext8u(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+ case INDEX_op_ext16u_i32:
+ tgen_ext16u(s, TCG_TYPE_I32, args[0], args[1]);
+ break;
+
+ OP_32_64(bswap16):
+ /* The TCG bswap definition requires bits 0-47 already be zero.
+ Thus we don't need the G-type insns to implement bswap16_i64. */
+ tcg_out_insn(s, RRE, LRVR, args[0], args[1]);
+ tcg_out_sh32(s, RS_SRL, args[0], TCG_REG_NONE, 16);
+ break;
+ OP_32_64(bswap32):
+ tcg_out_insn(s, RRE, LRVR, args[0], args[1]);
+ break;
+
+ case INDEX_op_br:
+ tgen_branch(s, S390_CC_ALWAYS, args[0]);
+ break;
+
+ case INDEX_op_brcond_i32:
+ tgen_brcond(s, TCG_TYPE_I32, args[2], args[0],
+ args[1], const_args[1], args[3]);
+ break;
+ case INDEX_op_setcond_i32:
+ tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1],
+ args[2], const_args[2]);
+ break;
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, LD_UINT8);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, LD_INT8);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, LD_UINT16);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, LD_INT16);
+ break;
+ case INDEX_op_qemu_ld32:
+ /* ??? Technically we can use a non-extending instruction. */
+ tcg_out_qemu_ld(s, args, LD_UINT32);
+ break;
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, LD_UINT64);
+ break;
+
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, LD_UINT8);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, LD_UINT16);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, LD_UINT32);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, LD_UINT64);
+ break;
+
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_mov_i64:
+ tcg_out_mov(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_movi_i64:
+ tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+
+ case INDEX_op_ld16s_i64:
+ tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+ case INDEX_op_ld32u_i64:
+ tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+ case INDEX_op_ld32s_i64:
+ tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]);
+ break;
+ case INDEX_op_ld_i64:
+ tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_st32_i64:
+ tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]);
+ break;
+ case INDEX_op_st_i64:
+ tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]);
+ break;
+
+ case INDEX_op_add_i64:
+ if (const_args[2]) {
+ tgen64_addi(s, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RRE, AGR, args[0], args[2]);
+ }
+ break;
+ case INDEX_op_sub_i64:
+ if (const_args[2]) {
+ tgen64_addi(s, args[0], -args[2]);
+ } else {
+ tcg_out_insn(s, RRE, SGR, args[0], args[2]);
+ }
+ break;
+
+ case INDEX_op_and_i64:
+ if (const_args[2]) {
+ tgen64_andi(s, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RRE, NGR, args[0], args[2]);
+ }
+ break;
+ case INDEX_op_or_i64:
+ if (const_args[2]) {
+ tgen64_ori(s, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RRE, OGR, args[0], args[2]);
+ }
+ break;
+ case INDEX_op_xor_i64:
+ if (const_args[2]) {
+ tgen64_xori(s, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RRE, XGR, args[0], args[2]);
+ }
+ break;
+
+ case INDEX_op_neg_i64:
+ tcg_out_insn(s, RRE, LCGR, args[0], args[1]);
+ break;
+ case INDEX_op_bswap64_i64:
+ tcg_out_insn(s, RRE, LRVGR, args[0], args[1]);
+ break;
+
+ case INDEX_op_mul_i64:
+ if (const_args[2]) {
+ if (args[2] == (int16_t)args[2]) {
+ tcg_out_insn(s, RI, MGHI, args[0], args[2]);
+ } else {
+ tcg_out_insn(s, RIL, MSGFI, args[0], args[2]);
+ }
+ } else {
+ tcg_out_insn(s, RRE, MSGR, args[0], args[2]);
+ }
+ break;
+
+ case INDEX_op_div2_i64:
+ /* ??? We get an unnecessary sign-extension of the dividend
+ into R3 with this definition, but as we do in fact always
+ produce both quotient and remainder using INDEX_op_div_i64
+ instead requires jumping through even more hoops. */
+ tcg_out_insn(s, RRE, DSGR, TCG_REG_R2, args[4]);
+ break;
+ case INDEX_op_divu2_i64:
+ tcg_out_insn(s, RRE, DLGR, TCG_REG_R2, args[4]);
+ break;
+
+ case INDEX_op_shl_i64:
+ op = RSY_SLLG;
+ do_shift64:
+ if (const_args[2]) {
+ tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]);
+ } else {
+ tcg_out_sh64(s, op, args[0], args[1], args[2], 0);
+ }
+ break;
+ case INDEX_op_shr_i64:
+ op = RSY_SRLG;
+ goto do_shift64;
+ case INDEX_op_sar_i64:
+ op = RSY_SRAG;
+ goto do_shift64;
+
+ case INDEX_op_rotl_i64:
+ if (const_args[2]) {
+ tcg_out_sh64(s, RSY_RLLG, args[0], args[1],
+ TCG_REG_NONE, args[2]);
+ } else {
+ tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0);
+ }
+ break;
+ case INDEX_op_rotr_i64:
+ if (const_args[2]) {
+ tcg_out_sh64(s, RSY_RLLG, args[0], args[1],
+ TCG_REG_NONE, (64 - args[2]) & 63);
+ } else {
+ /* We can use the smaller 32-bit negate because only the
+ low 6 bits are examined for the rotate. */
+ tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]);
+ tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0);
+ }
+ break;
+
+ case INDEX_op_ext8s_i64:
+ tgen_ext8s(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ext16s_i64:
+ tgen_ext16s(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ext32s_i64:
+ tgen_ext32s(s, args[0], args[1]);
+ break;
+ case INDEX_op_ext8u_i64:
+ tgen_ext8u(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ext16u_i64:
+ tgen_ext16u(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ext32u_i64:
+ tgen_ext32u(s, args[0], args[1]);
+ break;
+
+ case INDEX_op_brcond_i64:
+ tgen_brcond(s, TCG_TYPE_I64, args[2], args[0],
+ args[1], const_args[1], args[3]);
+ break;
+ case INDEX_op_setcond_i64:
+ tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1],
+ args[2], const_args[2]);
+ break;
+
+ case INDEX_op_qemu_ld32u:
+ tcg_out_qemu_ld(s, args, LD_UINT32);
+ break;
+ case INDEX_op_qemu_ld32s:
+ tcg_out_qemu_ld(s, args, LD_INT32);
+ break;
+#endif /* TCG_TARGET_REG_BITS == 64 */
+
+ case INDEX_op_jmp:
+ /* This one is obsolete and never emitted. */
+ tcg_abort();
+ break;
+
+ default:
+ fprintf(stderr,"unimplemented opc 0x%x\n",opc);
+ tcg_abort();
+ }
+}
+
+static const TCGTargetOpDef s390_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "ri" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "r", "r" } },
+ { INDEX_op_st16_i32, { "r", "r" } },
+ { INDEX_op_st_i32, { "r", "r" } },
+
+ { INDEX_op_add_i32, { "r", "0", "rWI" } },
+ { INDEX_op_sub_i32, { "r", "0", "rWNI" } },
+ { INDEX_op_mul_i32, { "r", "0", "rK" } },
+
+ { INDEX_op_div2_i32, { "b", "a", "0", "1", "r" } },
+ { INDEX_op_divu2_i32, { "b", "a", "0", "1", "r" } },
+
+ { INDEX_op_and_i32, { "r", "0", "rWA" } },
+ { INDEX_op_or_i32, { "r", "0", "rWO" } },
+ { INDEX_op_xor_i32, { "r", "0", "rWX" } },
+
+ { INDEX_op_neg_i32, { "r", "r" } },
+
+ { INDEX_op_shl_i32, { "r", "0", "Ri" } },
+ { INDEX_op_shr_i32, { "r", "0", "Ri" } },
+ { INDEX_op_sar_i32, { "r", "0", "Ri" } },
+
+ { INDEX_op_rotl_i32, { "r", "r", "Ri" } },
+ { INDEX_op_rotr_i32, { "r", "r", "Ri" } },
+
+ { INDEX_op_ext8s_i32, { "r", "r" } },
+ { INDEX_op_ext8u_i32, { "r", "r" } },
+ { INDEX_op_ext16s_i32, { "r", "r" } },
+ { INDEX_op_ext16u_i32, { "r", "r" } },
+
+ { INDEX_op_bswap16_i32, { "r", "r" } },
+ { INDEX_op_bswap32_i32, { "r", "r" } },
+
+ { INDEX_op_brcond_i32, { "r", "rWC" } },
+ { INDEX_op_setcond_i32, { "r", "r", "rWC" } },
+
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+ { INDEX_op_qemu_ld64, { "r", "L" } },
+
+ { INDEX_op_qemu_st8, { "L", "L" } },
+ { INDEX_op_qemu_st16, { "L", "L" } },
+ { INDEX_op_qemu_st32, { "L", "L" } },
+ { INDEX_op_qemu_st64, { "L", "L" } },
+
+#if defined(__s390x__)
+ { INDEX_op_mov_i64, { "r", "r" } },
+ { INDEX_op_movi_i64, { "r" } },
+
+ { INDEX_op_ld8u_i64, { "r", "r" } },
+ { INDEX_op_ld8s_i64, { "r", "r" } },
+ { INDEX_op_ld16u_i64, { "r", "r" } },
+ { INDEX_op_ld16s_i64, { "r", "r" } },
+ { INDEX_op_ld32u_i64, { "r", "r" } },
+ { INDEX_op_ld32s_i64, { "r", "r" } },
+ { INDEX_op_ld_i64, { "r", "r" } },
+
+ { INDEX_op_st8_i64, { "r", "r" } },
+ { INDEX_op_st16_i64, { "r", "r" } },
+ { INDEX_op_st32_i64, { "r", "r" } },
+ { INDEX_op_st_i64, { "r", "r" } },
+
+ { INDEX_op_add_i64, { "r", "0", "rI" } },
+ { INDEX_op_sub_i64, { "r", "0", "rNI" } },
+ { INDEX_op_mul_i64, { "r", "0", "rK" } },
+
+ { INDEX_op_div2_i64, { "b", "a", "0", "1", "r" } },
+ { INDEX_op_divu2_i64, { "b", "a", "0", "1", "r" } },
+
+ { INDEX_op_and_i64, { "r", "0", "rA" } },
+ { INDEX_op_or_i64, { "r", "0", "rO" } },
+ { INDEX_op_xor_i64, { "r", "0", "rX" } },
+
+ { INDEX_op_neg_i64, { "r", "r" } },
+
+ { INDEX_op_shl_i64, { "r", "r", "Ri" } },
+ { INDEX_op_shr_i64, { "r", "r", "Ri" } },
+ { INDEX_op_sar_i64, { "r", "r", "Ri" } },
+
+ { INDEX_op_rotl_i64, { "r", "r", "Ri" } },
+ { INDEX_op_rotr_i64, { "r", "r", "Ri" } },
+
+ { INDEX_op_ext8s_i64, { "r", "r" } },
+ { INDEX_op_ext8u_i64, { "r", "r" } },
+ { INDEX_op_ext16s_i64, { "r", "r" } },
+ { INDEX_op_ext16u_i64, { "r", "r" } },
+ { INDEX_op_ext32s_i64, { "r", "r" } },
+ { INDEX_op_ext32u_i64, { "r", "r" } },
+
+ { INDEX_op_bswap16_i64, { "r", "r" } },
+ { INDEX_op_bswap32_i64, { "r", "r" } },
+ { INDEX_op_bswap64_i64, { "r", "r" } },
+
+ { INDEX_op_brcond_i64, { "r", "rC" } },
+ { INDEX_op_setcond_i64, { "r", "r", "rC" } },
+
+ { INDEX_op_qemu_ld32u, { "r", "L" } },
+ { INDEX_op_qemu_ld32s, { "r", "L" } },
+#endif
+
+ { -1 },
+};
+
+/* ??? Linux kernels provide an AUXV entry AT_HWCAP that provides most of
+ this information. However, getting at that entry is not easy this far
+ away from main. Our options are: start searching from environ, but
+ that fails as soon as someone does a setenv in between. Read the data
+ from /proc/self/auxv. Or do the probing ourselves. The only thing
+ extra that AT_HWCAP gives us is HWCAP_S390_HIGH_GPRS, which indicates
+ that the kernel saves all 64-bits of the registers around traps while
+ in 31-bit mode. But this is true of all "recent" kernels (ought to dig
+ back and see from when this might not be true). */
+
+#include <signal.h>
+
+static volatile sig_atomic_t got_sigill;
+
+static void sigill_handler(int sig)
+{
+ got_sigill = 1;
+}
+
+static void query_facilities(void)
+{
+ struct sigaction sa_old, sa_new;
+ register int r0 __asm__("0");
+ register void *r1 __asm__("1");
+ int fail;
+
+ memset(&sa_new, 0, sizeof(sa_new));
+ sa_new.sa_handler = sigill_handler;
+ sigaction(SIGILL, &sa_new, &sa_old);
+
+ /* First, try STORE FACILITY LIST EXTENDED. If this is present, then
+ we need not do any more probing. Unfortunately, this itself is an
+ extension and the original STORE FACILITY LIST instruction is
+ kernel-only, storing its results at absolute address 200. */
+ /* stfle 0(%r1) */
+ r1 = &facilities;
+ asm volatile(".word 0xb2b0,0x1000"
+ : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc");
+
+ if (got_sigill) {
+ /* STORE FACILITY EXTENDED is not available. Probe for one of each
+ kind of instruction that we're interested in. */
+ /* ??? Possibly some of these are in practice never present unless
+ the store-facility-extended facility is also present. But since
+ that isn't documented it's just better to probe for each. */
+
+ /* Test for z/Architecture. Required even in 31-bit mode. */
+ got_sigill = 0;
+ /* agr %r0,%r0 */
+ asm volatile(".word 0xb908,0x0000" : "=r"(r0) : : "cc");
+ if (!got_sigill) {
+ facilities |= FACILITY_ZARCH_ACTIVE;
+ }
+
+ /* Test for long displacement. */
+ got_sigill = 0;
+ /* ly %r0,0(%r1) */
+ r1 = &facilities;
+ asm volatile(".word 0xe300,0x1000,0x0058"
+ : "=r"(r0) : "r"(r1) : "cc");
+ if (!got_sigill) {
+ facilities |= FACILITY_LONG_DISP;
+ }
+
+ /* Test for extended immediates. */
+ got_sigill = 0;
+ /* afi %r0,0 */
+ asm volatile(".word 0xc209,0x0000,0x0000" : : : "cc");
+ if (!got_sigill) {
+ facilities |= FACILITY_EXT_IMM;
+ }
+
+ /* Test for general-instructions-extension. */
+ got_sigill = 0;
+ /* msfi %r0,1 */
+ asm volatile(".word 0xc201,0x0000,0x0001");
+ if (!got_sigill) {
+ facilities |= FACILITY_GEN_INST_EXT;
+ }
+ }
+
+ sigaction(SIGILL, &sa_old, NULL);
+
+ /* The translator currently uses these extensions unconditionally.
+ Pruning this back to the base ESA/390 architecture doesn't seem
+ worthwhile, since even the KVM target requires z/Arch. */
+ fail = 0;
+ if ((facilities & FACILITY_ZARCH_ACTIVE) == 0) {
+ fprintf(stderr, "TCG: z/Arch facility is required.\n");
+ fprintf(stderr, "TCG: Boot with a 64-bit enabled kernel.\n");
+ fail = 1;
+ }
+ if ((facilities & FACILITY_LONG_DISP) == 0) {
+ fprintf(stderr, "TCG: long-displacement facility is required.\n");
+ fail = 1;
+ }
+
+ /* So far there's just enough support for 31-bit mode to let the
+ compile succeed. This is good enough to run QEMU with KVM. */
+ if (sizeof(void *) != 8) {
+ fprintf(stderr, "TCG: 31-bit mode is not supported.\n");
+ fail = 1;
+ }
+
+ if (fail) {
+ exit(-1);
+ }
+}
+
+static void tcg_target_init(TCGContext *s)
+{
+#if !defined(CONFIG_USER_ONLY)
+ /* fail safe */
+ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) {
+ tcg_abort();
+ }
+#endif
+
+ query_facilities();
+
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff);
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff);
+
+ tcg_regset_clear(tcg_target_call_clobber_regs);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R0);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R1);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R2);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R3);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R4);
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R5);
+ /* The return register can be considered call-clobbered. */
+ tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14);
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_TMP0);
+ /* XXX many insns can't be used with R0, so we better avoid it for now */
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK);
+
+ tcg_add_target_add_op_defs(s390_op_defs);
+}
+
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ /* stmg %r6,%r15,48(%r15) (save registers) */
+ tcg_out_insn(s, RXY, STMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 48);
+
+ /* aghi %r15,-160 (stack frame) */
+ tcg_out_insn(s, RI, AGHI, TCG_REG_R15, -160);
+
+ if (GUEST_BASE >= 0x80000) {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE);
+ tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG);
+ }
+
+ /* br %r2 (go to TB) */
+ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R2);
+
+ tb_ret_addr = s->code_ptr;
+
+ /* lmg %r6,%r15,208(%r15) (restore registers) */
+ tcg_out_insn(s, RXY, LMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 208);
+
+ /* br %r14 (return) */
+ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14);
+}
+
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ tcg_abort();
+}
diff --git a/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h
new file mode 100644
index 0000000..4e45cf3
--- /dev/null
+++ b/tcg/s390/tcg-target.h
@@ -0,0 +1,109 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2009 Ulrich Hecht <uli@suse.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_S390 1
+
+#ifdef __s390x__
+#define TCG_TARGET_REG_BITS 64
+#else
+#define TCG_TARGET_REG_BITS 32
+#endif
+
+#define TCG_TARGET_WORDS_BIGENDIAN
+
+typedef enum TCGReg {
+ TCG_REG_R0 = 0,
+ TCG_REG_R1,
+ TCG_REG_R2,
+ TCG_REG_R3,
+ TCG_REG_R4,
+ TCG_REG_R5,
+ TCG_REG_R6,
+ TCG_REG_R7,
+ TCG_REG_R8,
+ TCG_REG_R9,
+ TCG_REG_R10,
+ TCG_REG_R11,
+ TCG_REG_R12,
+ TCG_REG_R13,
+ TCG_REG_R14,
+ TCG_REG_R15
+} TCGReg;
+
+#define TCG_TARGET_NB_REGS 16
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div2_i32
+#define TCG_TARGET_HAS_rot_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_ext8u_i32
+#define TCG_TARGET_HAS_ext16u_i32
+#define TCG_TARGET_HAS_bswap16_i32
+#define TCG_TARGET_HAS_bswap32_i32
+// #define TCG_TARGET_HAS_not_i32
+#define TCG_TARGET_HAS_neg_i32
+// #define TCG_TARGET_HAS_andc_i32
+// #define TCG_TARGET_HAS_orc_i32
+// #define TCG_TARGET_HAS_eqv_i32
+// #define TCG_TARGET_HAS_nand_i32
+// #define TCG_TARGET_HAS_nor_i32
+
+#if TCG_TARGET_REG_BITS == 64
+#define TCG_TARGET_HAS_div2_i64
+#define TCG_TARGET_HAS_rot_i64
+#define TCG_TARGET_HAS_ext8s_i64
+#define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+#define TCG_TARGET_HAS_ext8u_i64
+#define TCG_TARGET_HAS_ext16u_i64
+#define TCG_TARGET_HAS_ext32u_i64
+#define TCG_TARGET_HAS_bswap16_i64
+#define TCG_TARGET_HAS_bswap32_i64
+#define TCG_TARGET_HAS_bswap64_i64
+// #define TCG_TARGET_HAS_not_i64
+#define TCG_TARGET_HAS_neg_i64
+// #define TCG_TARGET_HAS_andc_i64
+// #define TCG_TARGET_HAS_orc_i64
+// #define TCG_TARGET_HAS_eqv_i64
+// #define TCG_TARGET_HAS_nand_i64
+// #define TCG_TARGET_HAS_nor_i64
+#endif
+
+#define TCG_TARGET_HAS_GUEST_BASE
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R15
+#define TCG_TARGET_STACK_ALIGN 8
+#define TCG_TARGET_CALL_STACK_OFFSET 0
+
+#define TCG_TARGET_EXTEND_ARGS 1
+
+enum {
+ /* Note: must be synced with dyngen-exec.h */
+ TCG_AREG0 = TCG_REG_R10,
+};
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c
new file mode 100644
index 0000000..5f1353a
--- /dev/null
+++ b/tcg/sparc/tcg-target.c
@@ -0,0 +1,1569 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+ "%g0",
+ "%g1",
+ "%g2",
+ "%g3",
+ "%g4",
+ "%g5",
+ "%g6",
+ "%g7",
+ "%o0",
+ "%o1",
+ "%o2",
+ "%o3",
+ "%o4",
+ "%o5",
+ "%o6",
+ "%o7",
+ "%l0",
+ "%l1",
+ "%l2",
+ "%l3",
+ "%l4",
+ "%l5",
+ "%l6",
+ "%l7",
+ "%i0",
+ "%i1",
+ "%i2",
+ "%i3",
+ "%i4",
+ "%i5",
+ "%i6",
+ "%i7",
+};
+#endif
+
+static const int tcg_target_reg_alloc_order[] = {
+ TCG_REG_L0,
+ TCG_REG_L1,
+ TCG_REG_L2,
+ TCG_REG_L3,
+ TCG_REG_L4,
+ TCG_REG_L5,
+ TCG_REG_L6,
+ TCG_REG_L7,
+ TCG_REG_I0,
+ TCG_REG_I1,
+ TCG_REG_I2,
+ TCG_REG_I3,
+ TCG_REG_I4,
+};
+
+static const int tcg_target_call_iarg_regs[6] = {
+ TCG_REG_O0,
+ TCG_REG_O1,
+ TCG_REG_O2,
+ TCG_REG_O3,
+ TCG_REG_O4,
+ TCG_REG_O5,
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+ TCG_REG_O0,
+ TCG_REG_O1,
+};
+
+static inline int check_fit_tl(tcg_target_long val, unsigned int bits)
+{
+ return (val << ((sizeof(tcg_target_long) * 8 - bits))
+ >> (sizeof(tcg_target_long) * 8 - bits)) == val;
+}
+
+static inline int check_fit_i32(uint32_t val, unsigned int bits)
+{
+ return ((val << (32 - bits)) >> (32 - bits)) == val;
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend)
+{
+ value += addend;
+ switch (type) {
+ case R_SPARC_32:
+ if (value != (uint32_t)value)
+ tcg_abort();
+ *(uint32_t *)code_ptr = value;
+ break;
+ case R_SPARC_WDISP22:
+ value -= (long)code_ptr;
+ value >>= 2;
+ if (!check_fit_tl(value, 22))
+ tcg_abort();
+ *(uint32_t *)code_ptr = ((*(uint32_t *)code_ptr) & ~0x3fffff) | value;
+ break;
+ case R_SPARC_WDISP19:
+ value -= (long)code_ptr;
+ value >>= 2;
+ if (!check_fit_tl(value, 19))
+ tcg_abort();
+ *(uint32_t *)code_ptr = ((*(uint32_t *)code_ptr) & ~0x7ffff) | value;
+ break;
+ default:
+ tcg_abort();
+ }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+ return 6;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+ const char *ct_str;
+
+ ct_str = *pct_str;
+ switch (ct_str[0]) {
+ case 'r':
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ break;
+ case 'L': /* qemu_ld/st constraint */
+ ct->ct |= TCG_CT_REG;
+ tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+ // Helper args
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_O0);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_O1);
+ tcg_regset_reset_reg(ct->u.regs, TCG_REG_O2);
+ break;
+ case 'I':
+ ct->ct |= TCG_CT_CONST_S11;
+ break;
+ case 'J':
+ ct->ct |= TCG_CT_CONST_S13;
+ break;
+ default:
+ return -1;
+ }
+ ct_str++;
+ *pct_str = ct_str;
+ return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+ const TCGArgConstraint *arg_ct)
+{
+ int ct;
+
+ ct = arg_ct->ct;
+ if (ct & TCG_CT_CONST)
+ return 1;
+ else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11))
+ return 1;
+ else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13))
+ return 1;
+ else
+ return 0;
+}
+
+#define INSN_OP(x) ((x) << 30)
+#define INSN_OP2(x) ((x) << 22)
+#define INSN_OP3(x) ((x) << 19)
+#define INSN_OPF(x) ((x) << 5)
+#define INSN_RD(x) ((x) << 25)
+#define INSN_RS1(x) ((x) << 14)
+#define INSN_RS2(x) (x)
+#define INSN_ASI(x) ((x) << 5)
+
+#define INSN_IMM11(x) ((1 << 13) | ((x) & 0x7ff))
+#define INSN_IMM13(x) ((1 << 13) | ((x) & 0x1fff))
+#define INSN_OFF19(x) (((x) >> 2) & 0x07ffff)
+#define INSN_OFF22(x) (((x) >> 2) & 0x3fffff)
+
+#define INSN_COND(x, a) (((x) << 25) | ((a) << 29))
+#define COND_N 0x0
+#define COND_E 0x1
+#define COND_LE 0x2
+#define COND_L 0x3
+#define COND_LEU 0x4
+#define COND_CS 0x5
+#define COND_NEG 0x6
+#define COND_VS 0x7
+#define COND_A 0x8
+#define COND_NE 0x9
+#define COND_G 0xa
+#define COND_GE 0xb
+#define COND_GU 0xc
+#define COND_CC 0xd
+#define COND_POS 0xe
+#define COND_VC 0xf
+#define BA (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2))
+
+#define MOVCC_ICC (1 << 18)
+#define MOVCC_XCC (1 << 18 | 1 << 12)
+
+#define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00))
+#define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10))
+#define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01))
+#define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05))
+#define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02))
+#define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12))
+#define ARITH_ORN (INSN_OP(2) | INSN_OP3(0x06))
+#define ARITH_XOR (INSN_OP(2) | INSN_OP3(0x03))
+#define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04))
+#define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14))
+#define ARITH_ADDX (INSN_OP(2) | INSN_OP3(0x10))
+#define ARITH_SUBX (INSN_OP(2) | INSN_OP3(0x0c))
+#define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a))
+#define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e))
+#define ARITH_SDIV (INSN_OP(2) | INSN_OP3(0x0f))
+#define ARITH_MULX (INSN_OP(2) | INSN_OP3(0x09))
+#define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d))
+#define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d))
+#define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c))
+
+#define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25))
+#define SHIFT_SRL (INSN_OP(2) | INSN_OP3(0x26))
+#define SHIFT_SRA (INSN_OP(2) | INSN_OP3(0x27))
+
+#define SHIFT_SLLX (INSN_OP(2) | INSN_OP3(0x25) | (1 << 12))
+#define SHIFT_SRLX (INSN_OP(2) | INSN_OP3(0x26) | (1 << 12))
+#define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12))
+
+#define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0))
+#define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0))
+#define JMPL (INSN_OP(2) | INSN_OP3(0x38))
+#define SAVE (INSN_OP(2) | INSN_OP3(0x3c))
+#define RESTORE (INSN_OP(2) | INSN_OP3(0x3d))
+#define SETHI (INSN_OP(0) | INSN_OP2(0x4))
+#define CALL INSN_OP(1)
+#define LDUB (INSN_OP(3) | INSN_OP3(0x01))
+#define LDSB (INSN_OP(3) | INSN_OP3(0x09))
+#define LDUH (INSN_OP(3) | INSN_OP3(0x02))
+#define LDSH (INSN_OP(3) | INSN_OP3(0x0a))
+#define LDUW (INSN_OP(3) | INSN_OP3(0x00))
+#define LDSW (INSN_OP(3) | INSN_OP3(0x08))
+#define LDX (INSN_OP(3) | INSN_OP3(0x0b))
+#define STB (INSN_OP(3) | INSN_OP3(0x05))
+#define STH (INSN_OP(3) | INSN_OP3(0x06))
+#define STW (INSN_OP(3) | INSN_OP3(0x04))
+#define STX (INSN_OP(3) | INSN_OP3(0x0e))
+#define LDUBA (INSN_OP(3) | INSN_OP3(0x11))
+#define LDSBA (INSN_OP(3) | INSN_OP3(0x19))
+#define LDUHA (INSN_OP(3) | INSN_OP3(0x12))
+#define LDSHA (INSN_OP(3) | INSN_OP3(0x1a))
+#define LDUWA (INSN_OP(3) | INSN_OP3(0x10))
+#define LDSWA (INSN_OP(3) | INSN_OP3(0x18))
+#define LDXA (INSN_OP(3) | INSN_OP3(0x1b))
+#define STBA (INSN_OP(3) | INSN_OP3(0x15))
+#define STHA (INSN_OP(3) | INSN_OP3(0x16))
+#define STWA (INSN_OP(3) | INSN_OP3(0x14))
+#define STXA (INSN_OP(3) | INSN_OP3(0x1e))
+
+#ifndef ASI_PRIMARY_LITTLE
+#define ASI_PRIMARY_LITTLE 0x88
+#endif
+
+static inline void tcg_out_arith(TCGContext *s, int rd, int rs1, int rs2,
+ int op)
+{
+ tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) |
+ INSN_RS2(rs2));
+}
+
+static inline void tcg_out_arithi(TCGContext *s, int rd, int rs1,
+ uint32_t offset, int op)
+{
+ tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) |
+ INSN_IMM13(offset));
+}
+
+static void tcg_out_arithc(TCGContext *s, int rd, int rs1,
+ int val2, int val2const, int op)
+{
+ tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1)
+ | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2)));
+}
+
+static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg)
+{
+ tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR);
+}
+
+static inline void tcg_out_sethi(TCGContext *s, int ret, uint32_t arg)
+{
+ tcg_out32(s, SETHI | INSN_RD(ret) | ((arg & 0xfffffc00) >> 10));
+}
+
+static inline void tcg_out_movi_imm13(TCGContext *s, int ret, uint32_t arg)
+{
+ tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR);
+}
+
+static inline void tcg_out_movi_imm32(TCGContext *s, int ret, uint32_t arg)
+{
+ if (check_fit_tl(arg, 13))
+ tcg_out_movi_imm13(s, ret, arg);
+ else {
+ tcg_out_sethi(s, ret, arg);
+ if (arg & 0x3ff)
+ tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR);
+ }
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+ int ret, tcg_target_long arg)
+{
+ /* All 32-bit constants, as well as 64-bit constants with
+ no high bits set go through movi_imm32. */
+ if (TCG_TARGET_REG_BITS == 32
+ || type == TCG_TYPE_I32
+ || (arg & ~(tcg_target_long)0xffffffff) == 0) {
+ tcg_out_movi_imm32(s, ret, arg);
+ } else if (check_fit_tl(arg, 13)) {
+ /* A 13-bit constant sign-extended to 64-bits. */
+ tcg_out_movi_imm13(s, ret, arg);
+ } else if (check_fit_tl(arg, 32)) {
+ /* A 32-bit constant sign-extended to 64-bits. */
+ tcg_out_sethi(s, ret, ~arg);
+ tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR);
+ } else {
+ tcg_out_movi_imm32(s, TCG_REG_I4, arg >> (TCG_TARGET_REG_BITS / 2));
+ tcg_out_arithi(s, TCG_REG_I4, TCG_REG_I4, 32, SHIFT_SLLX);
+ tcg_out_movi_imm32(s, ret, arg);
+ tcg_out_arith(s, ret, ret, TCG_REG_I4, ARITH_OR);
+ }
+}
+
+static inline void tcg_out_ld_raw(TCGContext *s, int ret,
+ tcg_target_long arg)
+{
+ tcg_out_sethi(s, ret, arg);
+ tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) |
+ INSN_IMM13(arg & 0x3ff));
+}
+
+static inline void tcg_out_ld_ptr(TCGContext *s, int ret,
+ tcg_target_long arg)
+{
+ if (!check_fit_tl(arg, 10))
+ tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ffULL);
+ if (TCG_TARGET_REG_BITS == 64) {
+ tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(ret) |
+ INSN_IMM13(arg & 0x3ff));
+ } else {
+ tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) |
+ INSN_IMM13(arg & 0x3ff));
+ }
+}
+
+static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset, int op)
+{
+ if (check_fit_tl(offset, 13))
+ tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(addr) |
+ INSN_IMM13(offset));
+ else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, offset);
+ tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(TCG_REG_I5) |
+ INSN_RS2(addr));
+ }
+}
+
+static inline void tcg_out_ldst_asi(TCGContext *s, int ret, int addr,
+ int offset, int op, int asi)
+{
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, offset);
+ tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(TCG_REG_I5) |
+ INSN_ASI(asi) | INSN_RS2(addr));
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+ int arg1, tcg_target_long arg2)
+{
+ if (type == TCG_TYPE_I32)
+ tcg_out_ldst(s, ret, arg1, arg2, LDUW);
+ else
+ tcg_out_ldst(s, ret, arg1, arg2, LDX);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+ int arg1, tcg_target_long arg2)
+{
+ if (type == TCG_TYPE_I32)
+ tcg_out_ldst(s, arg, arg1, arg2, STW);
+ else
+ tcg_out_ldst(s, arg, arg1, arg2, STX);
+}
+
+static inline void tcg_out_sety(TCGContext *s, int rs)
+{
+ tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs));
+}
+
+static inline void tcg_out_rdy(TCGContext *s, int rd)
+{
+ tcg_out32(s, RDY | INSN_RD(rd));
+}
+
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+ if (val != 0) {
+ if (check_fit_tl(val, 13))
+ tcg_out_arithi(s, reg, reg, val, ARITH_ADD);
+ else {
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, val);
+ tcg_out_arith(s, reg, reg, TCG_REG_I5, ARITH_ADD);
+ }
+ }
+}
+
+static inline void tcg_out_andi(TCGContext *s, int reg, tcg_target_long val)
+{
+ if (val != 0) {
+ if (check_fit_tl(val, 13))
+ tcg_out_arithi(s, reg, reg, val, ARITH_AND);
+ else {
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, val);
+ tcg_out_arith(s, reg, reg, TCG_REG_I5, ARITH_AND);
+ }
+ }
+}
+
+static void tcg_out_div32(TCGContext *s, int rd, int rs1,
+ int val2, int val2const, int uns)
+{
+ /* Load Y with the sign/zero extension of RS1 to 64-bits. */
+ if (uns) {
+ tcg_out_sety(s, TCG_REG_G0);
+ } else {
+ tcg_out_arithi(s, TCG_REG_I5, rs1, 31, SHIFT_SRA);
+ tcg_out_sety(s, TCG_REG_I5);
+ }
+
+ tcg_out_arithc(s, rd, rs1, val2, val2const,
+ uns ? ARITH_UDIV : ARITH_SDIV);
+}
+
+static inline void tcg_out_nop(TCGContext *s)
+{
+ tcg_out_sethi(s, TCG_REG_G0, 0);
+}
+
+static void tcg_out_branch_i32(TCGContext *s, int opc, int label_index)
+{
+ int32_t val;
+ TCGLabel *l = &s->labels[label_index];
+
+ if (l->has_value) {
+ val = l->u.value - (tcg_target_long)s->code_ptr;
+ tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x2)
+ | INSN_OFF22(l->u.value - (unsigned long)s->code_ptr)));
+ } else {
+ tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP22, label_index, 0);
+ tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x2) | 0));
+ }
+}
+
+#if TCG_TARGET_REG_BITS == 64
+static void tcg_out_branch_i64(TCGContext *s, int opc, int label_index)
+{
+ int32_t val;
+ TCGLabel *l = &s->labels[label_index];
+
+ if (l->has_value) {
+ val = l->u.value - (tcg_target_long)s->code_ptr;
+ tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x1) |
+ (0x5 << 19) |
+ INSN_OFF19(l->u.value - (unsigned long)s->code_ptr)));
+ } else {
+ tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP19, label_index, 0);
+ tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x1) |
+ (0x5 << 19) | 0));
+ }
+}
+#endif
+
+static const uint8_t tcg_cond_to_bcond[10] = {
+ [TCG_COND_EQ] = COND_E,
+ [TCG_COND_NE] = COND_NE,
+ [TCG_COND_LT] = COND_L,
+ [TCG_COND_GE] = COND_GE,
+ [TCG_COND_LE] = COND_LE,
+ [TCG_COND_GT] = COND_G,
+ [TCG_COND_LTU] = COND_CS,
+ [TCG_COND_GEU] = COND_CC,
+ [TCG_COND_LEU] = COND_LEU,
+ [TCG_COND_GTU] = COND_GU,
+};
+
+static void tcg_out_cmp(TCGContext *s, TCGArg c1, TCGArg c2, int c2const)
+{
+ tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC);
+}
+
+static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond,
+ TCGArg arg1, TCGArg arg2, int const_arg2,
+ int label_index)
+{
+ tcg_out_cmp(s, arg1, arg2, const_arg2);
+ tcg_out_branch_i32(s, tcg_cond_to_bcond[cond], label_index);
+ tcg_out_nop(s);
+}
+
+#if TCG_TARGET_REG_BITS == 64
+static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond,
+ TCGArg arg1, TCGArg arg2, int const_arg2,
+ int label_index)
+{
+ tcg_out_cmp(s, arg1, arg2, const_arg2);
+ tcg_out_branch_i64(s, tcg_cond_to_bcond[cond], label_index);
+ tcg_out_nop(s);
+}
+#else
+static void tcg_out_brcond2_i32(TCGContext *s, TCGCond cond,
+ TCGArg al, TCGArg ah,
+ TCGArg bl, int blconst,
+ TCGArg bh, int bhconst, int label_dest)
+{
+ int cc, label_next = gen_new_label();
+
+ tcg_out_cmp(s, ah, bh, bhconst);
+
+ /* Note that we fill one of the delay slots with the second compare. */
+ switch (cond) {
+ case TCG_COND_EQ:
+ cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0);
+ tcg_out_branch_i32(s, cc, label_next);
+ tcg_out_cmp(s, al, bl, blconst);
+ cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_EQ], 0);
+ tcg_out_branch_i32(s, cc, label_dest);
+ break;
+
+ case TCG_COND_NE:
+ cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0);
+ tcg_out_branch_i32(s, cc, label_dest);
+ tcg_out_cmp(s, al, bl, blconst);
+ tcg_out_branch_i32(s, cc, label_dest);
+ break;
+
+ default:
+ /* ??? One could fairly easily special-case 64-bit unsigned
+ compares against 32-bit zero-extended constants. For instance,
+ we know that (unsigned)AH < 0 is false and need not emit it.
+ Similarly, (unsigned)AH > 0 being true implies AH != 0, so the
+ second branch will never be taken. */
+ cc = INSN_COND(tcg_cond_to_bcond[cond], 0);
+ tcg_out_branch_i32(s, cc, label_dest);
+ tcg_out_nop(s);
+ cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0);
+ tcg_out_branch_i32(s, cc, label_next);
+ tcg_out_cmp(s, al, bl, blconst);
+ cc = INSN_COND(tcg_cond_to_bcond[tcg_unsigned_cond(cond)], 0);
+ tcg_out_branch_i32(s, cc, label_dest);
+ break;
+ }
+ tcg_out_nop(s);
+
+ tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr);
+}
+#endif
+
+static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGArg ret,
+ TCGArg c1, TCGArg c2, int c2const)
+{
+ TCGArg t;
+
+ /* For 32-bit comparisons, we can play games with ADDX/SUBX. */
+ switch (cond) {
+ case TCG_COND_EQ:
+ case TCG_COND_NE:
+ if (c2 != 0) {
+ tcg_out_arithc(s, ret, c1, c2, c2const, ARITH_XOR);
+ }
+ c1 = TCG_REG_G0, c2 = ret, c2const = 0;
+ cond = (cond == TCG_COND_EQ ? TCG_COND_LEU : TCG_COND_LTU);
+ break;
+
+ case TCG_COND_GTU:
+ case TCG_COND_GEU:
+ if (c2const && c2 != 0) {
+ tcg_out_movi_imm13(s, TCG_REG_I5, c2);
+ c2 = TCG_REG_I5;
+ }
+ t = c1, c1 = c2, c2 = t, c2const = 0;
+ cond = tcg_swap_cond(cond);
+ break;
+
+ case TCG_COND_LTU:
+ case TCG_COND_LEU:
+ break;
+
+ default:
+ tcg_out_cmp(s, c1, c2, c2const);
+#if defined(__sparc_v9__) || defined(__sparc_v8plus__)
+ tcg_out_movi_imm13(s, ret, 0);
+ tcg_out32 (s, ARITH_MOVCC | INSN_RD(ret)
+ | INSN_RS1(tcg_cond_to_bcond[cond])
+ | MOVCC_ICC | INSN_IMM11(1));
+#else
+ t = gen_new_label();
+ tcg_out_branch_i32(s, INSN_COND(tcg_cond_to_bcond[cond], 1), t);
+ tcg_out_movi_imm13(s, ret, 1);
+ tcg_out_movi_imm13(s, ret, 0);
+ tcg_out_label(s, t, (tcg_target_long)s->code_ptr);
+#endif
+ return;
+ }
+
+ tcg_out_cmp(s, c1, c2, c2const);
+ if (cond == TCG_COND_LTU) {
+ tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDX);
+ } else {
+ tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBX);
+ }
+}
+
+#if TCG_TARGET_REG_BITS == 64
+static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGArg ret,
+ TCGArg c1, TCGArg c2, int c2const)
+{
+ tcg_out_cmp(s, c1, c2, c2const);
+ tcg_out_movi_imm13(s, ret, 0);
+ tcg_out32 (s, ARITH_MOVCC | INSN_RD(ret)
+ | INSN_RS1(tcg_cond_to_bcond[cond])
+ | MOVCC_XCC | INSN_IMM11(1));
+}
+#else
+static void tcg_out_setcond2_i32(TCGContext *s, TCGCond cond, TCGArg ret,
+ TCGArg al, TCGArg ah,
+ TCGArg bl, int blconst,
+ TCGArg bh, int bhconst)
+{
+ int lab;
+
+ switch (cond) {
+ case TCG_COND_EQ:
+ tcg_out_setcond_i32(s, TCG_COND_EQ, TCG_REG_I5, al, bl, blconst);
+ tcg_out_setcond_i32(s, TCG_COND_EQ, ret, ah, bh, bhconst);
+ tcg_out_arith(s, ret, ret, TCG_REG_I5, ARITH_AND);
+ break;
+
+ case TCG_COND_NE:
+ tcg_out_setcond_i32(s, TCG_COND_NE, TCG_REG_I5, al, al, blconst);
+ tcg_out_setcond_i32(s, TCG_COND_NE, ret, ah, bh, bhconst);
+ tcg_out_arith(s, ret, ret, TCG_REG_I5, ARITH_OR);
+ break;
+
+ default:
+ lab = gen_new_label();
+
+ tcg_out_cmp(s, ah, bh, bhconst);
+ tcg_out_branch_i32(s, INSN_COND(tcg_cond_to_bcond[cond], 1), lab);
+ tcg_out_movi_imm13(s, ret, 1);
+ tcg_out_branch_i32(s, INSN_COND(COND_NE, 1), lab);
+ tcg_out_movi_imm13(s, ret, 0);
+
+ tcg_out_setcond_i32(s, tcg_unsigned_cond(cond), ret, al, bl, blconst);
+
+ tcg_out_label(s, lab, (tcg_target_long)s->code_ptr);
+ break;
+ }
+}
+#endif
+
+/* Generate global QEMU prologue and epilogue code */
+static void tcg_target_qemu_prologue(TCGContext *s)
+{
+ tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) |
+ INSN_IMM13(-TCG_TARGET_STACK_MINFRAME));
+ tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I0) |
+ INSN_RS2(TCG_REG_G0));
+ tcg_out_nop(s);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static const void * const qemu_ld_helpers[4] = {
+ __ldb_mmu,
+ __ldw_mmu,
+ __ldl_mmu,
+ __ldq_mmu,
+};
+
+static const void * const qemu_st_helpers[4] = {
+ __stb_mmu,
+ __stw_mmu,
+ __stl_mmu,
+ __stq_mmu,
+};
+#endif
+
+#if TARGET_LONG_BITS == 32
+#define TARGET_LD_OP LDUW
+#else
+#define TARGET_LD_OP LDX
+#endif
+
+#if defined(CONFIG_SOFTMMU)
+#if HOST_LONG_BITS == 32
+#define TARGET_ADDEND_LD_OP LDUW
+#else
+#define TARGET_ADDEND_LD_OP LDX
+#endif
+#endif
+
+#ifdef __arch64__
+#define HOST_LD_OP LDX
+#define HOST_ST_OP STX
+#define HOST_SLL_OP SHIFT_SLLX
+#define HOST_SRA_OP SHIFT_SRAX
+#else
+#define HOST_LD_OP LDUW
+#define HOST_ST_OP STW
+#define HOST_SLL_OP SHIFT_SLL
+#define HOST_SRA_OP SHIFT_SRA
+#endif
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+ int opc)
+{
+ int addr_reg, data_reg, arg0, arg1, arg2, mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+ uint32_t *label1_ptr, *label2_ptr;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ mem_index = *args;
+ s_bits = opc & 3;
+
+ arg0 = TCG_REG_O0;
+ arg1 = TCG_REG_O1;
+ arg2 = TCG_REG_O2;
+
+#if defined(CONFIG_SOFTMMU)
+ /* srl addr_reg, x, arg1 */
+ tcg_out_arithi(s, arg1, addr_reg, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS,
+ SHIFT_SRL);
+ /* and addr_reg, x, arg0 */
+ tcg_out_arithi(s, arg0, addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+ ARITH_AND);
+
+ /* and arg1, x, arg1 */
+ tcg_out_andi(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+ /* add arg1, x, arg1 */
+ tcg_out_addi(s, arg1, offsetof(CPUState,
+ tlb_table[mem_index][0].addr_read));
+
+ /* add env, arg1, arg1 */
+ tcg_out_arith(s, arg1, TCG_AREG0, arg1, ARITH_ADD);
+
+ /* ld [arg1], arg2 */
+ tcg_out32(s, TARGET_LD_OP | INSN_RD(arg2) | INSN_RS1(arg1) |
+ INSN_RS2(TCG_REG_G0));
+
+ /* subcc arg0, arg2, %g0 */
+ tcg_out_arith(s, TCG_REG_G0, arg0, arg2, ARITH_SUBCC);
+
+ /* will become:
+ be label1
+ or
+ be,pt %xcc label1 */
+ label1_ptr = (uint32_t *)s->code_ptr;
+ tcg_out32(s, 0);
+
+ /* mov (delay slot) */
+ tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg);
+
+ /* mov */
+ tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index);
+
+ /* XXX: move that code at the end of the TB */
+ /* qemu_ld_helper[s_bits](arg0, arg1) */
+ tcg_out32(s, CALL | ((((tcg_target_ulong)qemu_ld_helpers[s_bits]
+ - (tcg_target_ulong)s->code_ptr) >> 2)
+ & 0x3fffffff));
+ /* Store AREG0 in stack to avoid ugly glibc bugs that mangle
+ global registers */
+ // delay slot
+ tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+ TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE -
+ sizeof(long), HOST_ST_OP);
+ tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+ TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE -
+ sizeof(long), HOST_LD_OP);
+
+ /* data_reg = sign_extend(arg0) */
+ switch(opc) {
+ case 0 | 4:
+ /* sll arg0, 24/56, data_reg */
+ tcg_out_arithi(s, data_reg, arg0, (int)sizeof(tcg_target_long) * 8 - 8,
+ HOST_SLL_OP);
+ /* sra data_reg, 24/56, data_reg */
+ tcg_out_arithi(s, data_reg, data_reg,
+ (int)sizeof(tcg_target_long) * 8 - 8, HOST_SRA_OP);
+ break;
+ case 1 | 4:
+ /* sll arg0, 16/48, data_reg */
+ tcg_out_arithi(s, data_reg, arg0,
+ (int)sizeof(tcg_target_long) * 8 - 16, HOST_SLL_OP);
+ /* sra data_reg, 16/48, data_reg */
+ tcg_out_arithi(s, data_reg, data_reg,
+ (int)sizeof(tcg_target_long) * 8 - 16, HOST_SRA_OP);
+ break;
+ case 2 | 4:
+ /* sll arg0, 32, data_reg */
+ tcg_out_arithi(s, data_reg, arg0, 32, HOST_SLL_OP);
+ /* sra data_reg, 32, data_reg */
+ tcg_out_arithi(s, data_reg, data_reg, 32, HOST_SRA_OP);
+ break;
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ default:
+ /* mov */
+ tcg_out_mov(s, TCG_TYPE_REG, data_reg, arg0);
+ break;
+ }
+
+ /* will become:
+ ba label2 */
+ label2_ptr = (uint32_t *)s->code_ptr;
+ tcg_out32(s, 0);
+
+ /* nop (delay slot */
+ tcg_out_nop(s);
+
+ /* label1: */
+#if TARGET_LONG_BITS == 32
+ /* be label1 */
+ *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x2) |
+ INSN_OFF22((unsigned long)s->code_ptr -
+ (unsigned long)label1_ptr));
+#else
+ /* be,pt %xcc label1 */
+ *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x1) |
+ (0x5 << 19) | INSN_OFF19((unsigned long)s->code_ptr -
+ (unsigned long)label1_ptr));
+#endif
+
+ /* ld [arg1 + x], arg1 */
+ tcg_out_ldst(s, arg1, arg1, offsetof(CPUTLBEntry, addend) -
+ offsetof(CPUTLBEntry, addr_read), TARGET_ADDEND_LD_OP);
+
+#if TARGET_LONG_BITS == 32
+ /* and addr_reg, x, arg0 */
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, 0xffffffff);
+ tcg_out_arith(s, arg0, addr_reg, TCG_REG_I5, ARITH_AND);
+ /* add arg0, arg1, arg0 */
+ tcg_out_arith(s, arg0, arg0, arg1, ARITH_ADD);
+#else
+ /* add addr_reg, arg1, arg0 */
+ tcg_out_arith(s, arg0, addr_reg, arg1, ARITH_ADD);
+#endif
+
+#else
+ arg0 = addr_reg;
+#endif
+
+ switch(opc) {
+ case 0:
+ /* ldub [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDUB);
+ break;
+ case 0 | 4:
+ /* ldsb [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDSB);
+ break;
+ case 1:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* lduh [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDUH);
+#else
+ /* lduha [arg0] ASI_PRIMARY_LITTLE, data_reg */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, LDUHA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ case 1 | 4:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* ldsh [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDSH);
+#else
+ /* ldsha [arg0] ASI_PRIMARY_LITTLE, data_reg */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, LDSHA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ case 2:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* lduw [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDUW);
+#else
+ /* lduwa [arg0] ASI_PRIMARY_LITTLE, data_reg */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, LDUWA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ case 2 | 4:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* ldsw [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDSW);
+#else
+ /* ldswa [arg0] ASI_PRIMARY_LITTLE, data_reg */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, LDSWA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ case 3:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* ldx [arg0], data_reg */
+ tcg_out_ldst(s, data_reg, arg0, 0, LDX);
+#else
+ /* ldxa [arg0] ASI_PRIMARY_LITTLE, data_reg */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, LDXA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ default:
+ tcg_abort();
+ }
+
+#if defined(CONFIG_SOFTMMU)
+ /* label2: */
+ *label2_ptr = (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2) |
+ INSN_OFF22((unsigned long)s->code_ptr -
+ (unsigned long)label2_ptr));
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+ int opc)
+{
+ int addr_reg, data_reg, arg0, arg1, arg2, mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+ uint32_t *label1_ptr, *label2_ptr;
+#endif
+
+ data_reg = *args++;
+ addr_reg = *args++;
+ mem_index = *args;
+
+ s_bits = opc;
+
+ arg0 = TCG_REG_O0;
+ arg1 = TCG_REG_O1;
+ arg2 = TCG_REG_O2;
+
+#if defined(CONFIG_SOFTMMU)
+ /* srl addr_reg, x, arg1 */
+ tcg_out_arithi(s, arg1, addr_reg, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS,
+ SHIFT_SRL);
+
+ /* and addr_reg, x, arg0 */
+ tcg_out_arithi(s, arg0, addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+ ARITH_AND);
+
+ /* and arg1, x, arg1 */
+ tcg_out_andi(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+ /* add arg1, x, arg1 */
+ tcg_out_addi(s, arg1, offsetof(CPUState,
+ tlb_table[mem_index][0].addr_write));
+
+ /* add env, arg1, arg1 */
+ tcg_out_arith(s, arg1, TCG_AREG0, arg1, ARITH_ADD);
+
+ /* ld [arg1], arg2 */
+ tcg_out32(s, TARGET_LD_OP | INSN_RD(arg2) | INSN_RS1(arg1) |
+ INSN_RS2(TCG_REG_G0));
+
+ /* subcc arg0, arg2, %g0 */
+ tcg_out_arith(s, TCG_REG_G0, arg0, arg2, ARITH_SUBCC);
+
+ /* will become:
+ be label1
+ or
+ be,pt %xcc label1 */
+ label1_ptr = (uint32_t *)s->code_ptr;
+ tcg_out32(s, 0);
+
+ /* mov (delay slot) */
+ tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg);
+
+ /* mov */
+ tcg_out_mov(s, TCG_TYPE_REG, arg1, data_reg);
+
+ /* mov */
+ tcg_out_movi(s, TCG_TYPE_I32, arg2, mem_index);
+
+ /* XXX: move that code at the end of the TB */
+ /* qemu_st_helper[s_bits](arg0, arg1, arg2) */
+ tcg_out32(s, CALL | ((((tcg_target_ulong)qemu_st_helpers[s_bits]
+ - (tcg_target_ulong)s->code_ptr) >> 2)
+ & 0x3fffffff));
+ /* Store AREG0 in stack to avoid ugly glibc bugs that mangle
+ global registers */
+ // delay slot
+ tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+ TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE -
+ sizeof(long), HOST_ST_OP);
+ tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+ TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE -
+ sizeof(long), HOST_LD_OP);
+
+ /* will become:
+ ba label2 */
+ label2_ptr = (uint32_t *)s->code_ptr;
+ tcg_out32(s, 0);
+
+ /* nop (delay slot) */
+ tcg_out_nop(s);
+
+#if TARGET_LONG_BITS == 32
+ /* be label1 */
+ *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x2) |
+ INSN_OFF22((unsigned long)s->code_ptr -
+ (unsigned long)label1_ptr));
+#else
+ /* be,pt %xcc label1 */
+ *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x1) |
+ (0x5 << 19) | INSN_OFF19((unsigned long)s->code_ptr -
+ (unsigned long)label1_ptr));
+#endif
+
+ /* ld [arg1 + x], arg1 */
+ tcg_out_ldst(s, arg1, arg1, offsetof(CPUTLBEntry, addend) -
+ offsetof(CPUTLBEntry, addr_write), TARGET_ADDEND_LD_OP);
+
+#if TARGET_LONG_BITS == 32
+ /* and addr_reg, x, arg0 */
+ tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, 0xffffffff);
+ tcg_out_arith(s, arg0, addr_reg, TCG_REG_I5, ARITH_AND);
+ /* add arg0, arg1, arg0 */
+ tcg_out_arith(s, arg0, arg0, arg1, ARITH_ADD);
+#else
+ /* add addr_reg, arg1, arg0 */
+ tcg_out_arith(s, arg0, addr_reg, arg1, ARITH_ADD);
+#endif
+
+#else
+ arg0 = addr_reg;
+#endif
+
+ switch(opc) {
+ case 0:
+ /* stb data_reg, [arg0] */
+ tcg_out_ldst(s, data_reg, arg0, 0, STB);
+ break;
+ case 1:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* sth data_reg, [arg0] */
+ tcg_out_ldst(s, data_reg, arg0, 0, STH);
+#else
+ /* stha data_reg, [arg0] ASI_PRIMARY_LITTLE */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, STHA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ case 2:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* stw data_reg, [arg0] */
+ tcg_out_ldst(s, data_reg, arg0, 0, STW);
+#else
+ /* stwa data_reg, [arg0] ASI_PRIMARY_LITTLE */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, STWA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ case 3:
+#ifdef TARGET_WORDS_BIGENDIAN
+ /* stx data_reg, [arg0] */
+ tcg_out_ldst(s, data_reg, arg0, 0, STX);
+#else
+ /* stxa data_reg, [arg0] ASI_PRIMARY_LITTLE */
+ tcg_out_ldst_asi(s, data_reg, arg0, 0, STXA, ASI_PRIMARY_LITTLE);
+#endif
+ break;
+ default:
+ tcg_abort();
+ }
+
+#if defined(CONFIG_SOFTMMU)
+ /* label2: */
+ *label2_ptr = (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2) |
+ INSN_OFF22((unsigned long)s->code_ptr -
+ (unsigned long)label2_ptr));
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args,
+ const int *const_args)
+{
+ int c;
+
+ switch (opc) {
+ case INDEX_op_exit_tb:
+ tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, args[0]);
+ tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I7) |
+ INSN_IMM13(8));
+ tcg_out32(s, RESTORE | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_G0) |
+ INSN_RS2(TCG_REG_G0));
+ break;
+ case INDEX_op_goto_tb:
+ if (s->tb_jmp_offset) {
+ /* direct jump method */
+ tcg_out_sethi(s, TCG_REG_I5, args[0] & 0xffffe000);
+ tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I5) |
+ INSN_IMM13((args[0] & 0x1fff)));
+ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+ } else {
+ /* indirect jump method */
+ tcg_out_ld_ptr(s, TCG_REG_I5, (tcg_target_long)(s->tb_next + args[0]));
+ tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I5) |
+ INSN_RS2(TCG_REG_G0));
+ }
+ tcg_out_nop(s);
+ s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+ break;
+ case INDEX_op_call:
+ if (const_args[0])
+ tcg_out32(s, CALL | ((((tcg_target_ulong)args[0]
+ - (tcg_target_ulong)s->code_ptr) >> 2)
+ & 0x3fffffff));
+ else {
+ tcg_out_ld_ptr(s, TCG_REG_I5,
+ (tcg_target_long)(s->tb_next + args[0]));
+ tcg_out32(s, JMPL | INSN_RD(TCG_REG_O7) | INSN_RS1(TCG_REG_I5) |
+ INSN_RS2(TCG_REG_G0));
+ }
+ /* Store AREG0 in stack to avoid ugly glibc bugs that mangle
+ global registers */
+ // delay slot
+ tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+ TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE -
+ sizeof(long), HOST_ST_OP);
+ tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+ TCG_TARGET_CALL_STACK_OFFSET - TCG_STATIC_CALL_ARGS_SIZE -
+ sizeof(long), HOST_LD_OP);
+ break;
+ case INDEX_op_jmp:
+ case INDEX_op_br:
+ tcg_out_branch_i32(s, COND_A, args[0]);
+ tcg_out_nop(s);
+ break;
+ case INDEX_op_movi_i32:
+ tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]);
+ break;
+
+#if TCG_TARGET_REG_BITS == 64
+#define OP_32_64(x) \
+ glue(glue(case INDEX_op_, x), _i32): \
+ glue(glue(case INDEX_op_, x), _i64)
+#else
+#define OP_32_64(x) \
+ glue(glue(case INDEX_op_, x), _i32)
+#endif
+ OP_32_64(ld8u):
+ tcg_out_ldst(s, args[0], args[1], args[2], LDUB);
+ break;
+ OP_32_64(ld8s):
+ tcg_out_ldst(s, args[0], args[1], args[2], LDSB);
+ break;
+ OP_32_64(ld16u):
+ tcg_out_ldst(s, args[0], args[1], args[2], LDUH);
+ break;
+ OP_32_64(ld16s):
+ tcg_out_ldst(s, args[0], args[1], args[2], LDSH);
+ break;
+ case INDEX_op_ld_i32:
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_ld32u_i64:
+#endif
+ tcg_out_ldst(s, args[0], args[1], args[2], LDUW);
+ break;
+ OP_32_64(st8):
+ tcg_out_ldst(s, args[0], args[1], args[2], STB);
+ break;
+ OP_32_64(st16):
+ tcg_out_ldst(s, args[0], args[1], args[2], STH);
+ break;
+ case INDEX_op_st_i32:
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_st32_i64:
+#endif
+ tcg_out_ldst(s, args[0], args[1], args[2], STW);
+ break;
+ OP_32_64(add):
+ c = ARITH_ADD;
+ goto gen_arith;
+ OP_32_64(sub):
+ c = ARITH_SUB;
+ goto gen_arith;
+ OP_32_64(and):
+ c = ARITH_AND;
+ goto gen_arith;
+ OP_32_64(andc):
+ c = ARITH_ANDN;
+ goto gen_arith;
+ OP_32_64(or):
+ c = ARITH_OR;
+ goto gen_arith;
+ OP_32_64(orc):
+ c = ARITH_ORN;
+ goto gen_arith;
+ OP_32_64(xor):
+ c = ARITH_XOR;
+ goto gen_arith;
+ case INDEX_op_shl_i32:
+ c = SHIFT_SLL;
+ goto gen_arith;
+ case INDEX_op_shr_i32:
+ c = SHIFT_SRL;
+ goto gen_arith;
+ case INDEX_op_sar_i32:
+ c = SHIFT_SRA;
+ goto gen_arith;
+ case INDEX_op_mul_i32:
+ c = ARITH_UMUL;
+ goto gen_arith;
+
+ OP_32_64(neg):
+ c = ARITH_SUB;
+ goto gen_arith1;
+ OP_32_64(not):
+ c = ARITH_ORN;
+ goto gen_arith1;
+
+ case INDEX_op_div_i32:
+ tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 0);
+ break;
+ case INDEX_op_divu_i32:
+ tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 1);
+ break;
+
+ case INDEX_op_rem_i32:
+ case INDEX_op_remu_i32:
+ tcg_out_div32(s, TCG_REG_I5, args[1], args[2], const_args[2],
+ opc == INDEX_op_remu_i32);
+ tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2],
+ ARITH_UMUL);
+ tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB);
+ break;
+
+ case INDEX_op_brcond_i32:
+ tcg_out_brcond_i32(s, args[2], args[0], args[1], const_args[1],
+ args[3]);
+ break;
+ case INDEX_op_setcond_i32:
+ tcg_out_setcond_i32(s, args[3], args[0], args[1],
+ args[2], const_args[2]);
+ break;
+
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_brcond2_i32:
+ tcg_out_brcond2_i32(s, args[4], args[0], args[1],
+ args[2], const_args[2],
+ args[3], const_args[3], args[5]);
+ break;
+ case INDEX_op_setcond2_i32:
+ tcg_out_setcond2_i32(s, args[5], args[0], args[1], args[2],
+ args[3], const_args[3],
+ args[4], const_args[4]);
+ break;
+ case INDEX_op_add2_i32:
+ tcg_out_arithc(s, args[0], args[2], args[4], const_args[4],
+ ARITH_ADDCC);
+ tcg_out_arithc(s, args[1], args[3], args[5], const_args[5],
+ ARITH_ADDX);
+ break;
+ case INDEX_op_sub2_i32:
+ tcg_out_arithc(s, args[0], args[2], args[4], const_args[4],
+ ARITH_SUBCC);
+ tcg_out_arithc(s, args[1], args[3], args[5], const_args[5],
+ ARITH_SUBX);
+ break;
+ case INDEX_op_mulu2_i32:
+ tcg_out_arithc(s, args[0], args[2], args[3], const_args[3],
+ ARITH_UMUL);
+ tcg_out_rdy(s, args[1]);
+ break;
+#endif
+
+ case INDEX_op_qemu_ld8u:
+ tcg_out_qemu_ld(s, args, 0);
+ break;
+ case INDEX_op_qemu_ld8s:
+ tcg_out_qemu_ld(s, args, 0 | 4);
+ break;
+ case INDEX_op_qemu_ld16u:
+ tcg_out_qemu_ld(s, args, 1);
+ break;
+ case INDEX_op_qemu_ld16s:
+ tcg_out_qemu_ld(s, args, 1 | 4);
+ break;
+ case INDEX_op_qemu_ld32:
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_qemu_ld32u:
+#endif
+ tcg_out_qemu_ld(s, args, 2);
+ break;
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_qemu_ld32s:
+ tcg_out_qemu_ld(s, args, 2 | 4);
+ break;
+#endif
+ case INDEX_op_qemu_st8:
+ tcg_out_qemu_st(s, args, 0);
+ break;
+ case INDEX_op_qemu_st16:
+ tcg_out_qemu_st(s, args, 1);
+ break;
+ case INDEX_op_qemu_st32:
+ tcg_out_qemu_st(s, args, 2);
+ break;
+
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_movi_i64:
+ tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]);
+ break;
+ case INDEX_op_ld32s_i64:
+ tcg_out_ldst(s, args[0], args[1], args[2], LDSW);
+ break;
+ case INDEX_op_ld_i64:
+ tcg_out_ldst(s, args[0], args[1], args[2], LDX);
+ break;
+ case INDEX_op_st_i64:
+ tcg_out_ldst(s, args[0], args[1], args[2], STX);
+ break;
+ case INDEX_op_shl_i64:
+ c = SHIFT_SLLX;
+ goto gen_arith;
+ case INDEX_op_shr_i64:
+ c = SHIFT_SRLX;
+ goto gen_arith;
+ case INDEX_op_sar_i64:
+ c = SHIFT_SRAX;
+ goto gen_arith;
+ case INDEX_op_mul_i64:
+ c = ARITH_MULX;
+ goto gen_arith;
+ case INDEX_op_div_i64:
+ c = ARITH_SDIVX;
+ goto gen_arith;
+ case INDEX_op_divu_i64:
+ c = ARITH_UDIVX;
+ goto gen_arith;
+ case INDEX_op_rem_i64:
+ case INDEX_op_remu_i64:
+ tcg_out_arithc(s, TCG_REG_I5, args[1], args[2], const_args[2],
+ opc == INDEX_op_rem_i64 ? ARITH_SDIVX : ARITH_UDIVX);
+ tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2],
+ ARITH_MULX);
+ tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB);
+ break;
+ case INDEX_op_ext32s_i64:
+ if (const_args[1]) {
+ tcg_out_movi(s, TCG_TYPE_I64, args[0], (int32_t)args[1]);
+ } else {
+ tcg_out_arithi(s, args[0], args[1], 0, SHIFT_SRA);
+ }
+ break;
+ case INDEX_op_ext32u_i64:
+ if (const_args[1]) {
+ tcg_out_movi_imm32(s, args[0], args[1]);
+ } else {
+ tcg_out_arithi(s, args[0], args[1], 0, SHIFT_SRL);
+ }
+ break;
+
+ case INDEX_op_brcond_i64:
+ tcg_out_brcond_i64(s, args[2], args[0], args[1], const_args[1],
+ args[3]);
+ break;
+ case INDEX_op_setcond_i64:
+ tcg_out_setcond_i64(s, args[3], args[0], args[1],
+ args[2], const_args[2]);
+ break;
+
+ case INDEX_op_qemu_ld64:
+ tcg_out_qemu_ld(s, args, 3);
+ break;
+ case INDEX_op_qemu_st64:
+ tcg_out_qemu_st(s, args, 3);
+ break;
+
+#endif
+ gen_arith:
+ tcg_out_arithc(s, args[0], args[1], args[2], const_args[2], c);
+ break;
+
+ gen_arith1:
+ tcg_out_arithc(s, args[0], TCG_REG_G0, args[1], const_args[1], c);
+ break;
+
+ default:
+ fprintf(stderr, "unknown opcode 0x%x\n", opc);
+ tcg_abort();
+ }
+}
+
+static const TCGTargetOpDef sparc_op_defs[] = {
+ { INDEX_op_exit_tb, { } },
+ { INDEX_op_goto_tb, { } },
+ { INDEX_op_call, { "ri" } },
+ { INDEX_op_jmp, { "ri" } },
+ { INDEX_op_br, { } },
+
+ { INDEX_op_mov_i32, { "r", "r" } },
+ { INDEX_op_movi_i32, { "r" } },
+ { INDEX_op_ld8u_i32, { "r", "r" } },
+ { INDEX_op_ld8s_i32, { "r", "r" } },
+ { INDEX_op_ld16u_i32, { "r", "r" } },
+ { INDEX_op_ld16s_i32, { "r", "r" } },
+ { INDEX_op_ld_i32, { "r", "r" } },
+ { INDEX_op_st8_i32, { "r", "r" } },
+ { INDEX_op_st16_i32, { "r", "r" } },
+ { INDEX_op_st_i32, { "r", "r" } },
+
+ { INDEX_op_add_i32, { "r", "r", "rJ" } },
+ { INDEX_op_mul_i32, { "r", "r", "rJ" } },
+ { INDEX_op_div_i32, { "r", "r", "rJ" } },
+ { INDEX_op_divu_i32, { "r", "r", "rJ" } },
+ { INDEX_op_rem_i32, { "r", "r", "rJ" } },
+ { INDEX_op_remu_i32, { "r", "r", "rJ" } },
+ { INDEX_op_sub_i32, { "r", "r", "rJ" } },
+ { INDEX_op_and_i32, { "r", "r", "rJ" } },
+ { INDEX_op_andc_i32, { "r", "r", "rJ" } },
+ { INDEX_op_or_i32, { "r", "r", "rJ" } },
+ { INDEX_op_orc_i32, { "r", "r", "rJ" } },
+ { INDEX_op_xor_i32, { "r", "r", "rJ" } },
+
+ { INDEX_op_shl_i32, { "r", "r", "rJ" } },
+ { INDEX_op_shr_i32, { "r", "r", "rJ" } },
+ { INDEX_op_sar_i32, { "r", "r", "rJ" } },
+
+ { INDEX_op_neg_i32, { "r", "rJ" } },
+ { INDEX_op_not_i32, { "r", "rJ" } },
+
+ { INDEX_op_brcond_i32, { "r", "rJ" } },
+ { INDEX_op_setcond_i32, { "r", "r", "rJ" } },
+
+#if TCG_TARGET_REG_BITS == 32
+ { INDEX_op_brcond2_i32, { "r", "r", "rJ", "rJ" } },
+ { INDEX_op_setcond2_i32, { "r", "r", "r", "rJ", "rJ" } },
+ { INDEX_op_add2_i32, { "r", "r", "r", "r", "rJ", "rJ" } },
+ { INDEX_op_sub2_i32, { "r", "r", "r", "r", "rJ", "rJ" } },
+ { INDEX_op_mulu2_i32, { "r", "r", "r", "rJ" } },
+#endif
+
+ { INDEX_op_qemu_ld8u, { "r", "L" } },
+ { INDEX_op_qemu_ld8s, { "r", "L" } },
+ { INDEX_op_qemu_ld16u, { "r", "L" } },
+ { INDEX_op_qemu_ld16s, { "r", "L" } },
+ { INDEX_op_qemu_ld32, { "r", "L" } },
+#if TCG_TARGET_REG_BITS == 64
+ { INDEX_op_qemu_ld32u, { "r", "L" } },
+ { INDEX_op_qemu_ld32s, { "r", "L" } },
+#endif
+
+ { INDEX_op_qemu_st8, { "L", "L" } },
+ { INDEX_op_qemu_st16, { "L", "L" } },
+ { INDEX_op_qemu_st32, { "L", "L" } },
+
+#if TCG_TARGET_REG_BITS == 64
+ { INDEX_op_mov_i64, { "r", "r" } },
+ { INDEX_op_movi_i64, { "r" } },
+ { INDEX_op_ld8u_i64, { "r", "r" } },
+ { INDEX_op_ld8s_i64, { "r", "r" } },
+ { INDEX_op_ld16u_i64, { "r", "r" } },
+ { INDEX_op_ld16s_i64, { "r", "r" } },
+ { INDEX_op_ld32u_i64, { "r", "r" } },
+ { INDEX_op_ld32s_i64, { "r", "r" } },
+ { INDEX_op_ld_i64, { "r", "r" } },
+ { INDEX_op_st8_i64, { "r", "r" } },
+ { INDEX_op_st16_i64, { "r", "r" } },
+ { INDEX_op_st32_i64, { "r", "r" } },
+ { INDEX_op_st_i64, { "r", "r" } },
+ { INDEX_op_qemu_ld64, { "L", "L" } },
+ { INDEX_op_qemu_st64, { "L", "L" } },
+
+ { INDEX_op_add_i64, { "r", "r", "rJ" } },
+ { INDEX_op_mul_i64, { "r", "r", "rJ" } },
+ { INDEX_op_div_i64, { "r", "r", "rJ" } },
+ { INDEX_op_divu_i64, { "r", "r", "rJ" } },
+ { INDEX_op_rem_i64, { "r", "r", "rJ" } },
+ { INDEX_op_remu_i64, { "r", "r", "rJ" } },
+ { INDEX_op_sub_i64, { "r", "r", "rJ" } },
+ { INDEX_op_and_i64, { "r", "r", "rJ" } },
+ { INDEX_op_andc_i64, { "r", "r", "rJ" } },
+ { INDEX_op_or_i64, { "r", "r", "rJ" } },
+ { INDEX_op_orc_i64, { "r", "r", "rJ" } },
+ { INDEX_op_xor_i64, { "r", "r", "rJ" } },
+
+ { INDEX_op_shl_i64, { "r", "r", "rJ" } },
+ { INDEX_op_shr_i64, { "r", "r", "rJ" } },
+ { INDEX_op_sar_i64, { "r", "r", "rJ" } },
+
+ { INDEX_op_neg_i64, { "r", "rJ" } },
+ { INDEX_op_not_i64, { "r", "rJ" } },
+
+ { INDEX_op_ext32s_i64, { "r", "ri" } },
+ { INDEX_op_ext32u_i64, { "r", "ri" } },
+
+ { INDEX_op_brcond_i64, { "r", "rJ" } },
+ { INDEX_op_setcond_i64, { "r", "r", "rJ" } },
+#endif
+ { -1 },
+};
+
+static void tcg_target_init(TCGContext *s)
+{
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+#if TCG_TARGET_REG_BITS == 64
+ tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff);
+#endif
+ tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+ (1 << TCG_REG_G1) |
+ (1 << TCG_REG_G2) |
+ (1 << TCG_REG_G3) |
+ (1 << TCG_REG_G4) |
+ (1 << TCG_REG_G5) |
+ (1 << TCG_REG_G6) |
+ (1 << TCG_REG_G7) |
+ (1 << TCG_REG_O0) |
+ (1 << TCG_REG_O1) |
+ (1 << TCG_REG_O2) |
+ (1 << TCG_REG_O3) |
+ (1 << TCG_REG_O4) |
+ (1 << TCG_REG_O5) |
+ (1 << TCG_REG_O7));
+
+ tcg_regset_clear(s->reserved_regs);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_G0);
+#if TCG_TARGET_REG_BITS == 64
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_I4); // for internal use
+#endif
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_I5); // for internal use
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_I6);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_I7);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_O6);
+ tcg_regset_set_reg(s->reserved_regs, TCG_REG_O7);
+ tcg_add_target_add_op_defs(sparc_op_defs);
+}
diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h
new file mode 100644
index 0000000..df0785e
--- /dev/null
+++ b/tcg/sparc/tcg-target.h
@@ -0,0 +1,150 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_SPARC 1
+
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+#define TCG_TARGET_REG_BITS 64
+#else
+#define TCG_TARGET_REG_BITS 32
+#endif
+
+#define TCG_TARGET_WORDS_BIGENDIAN
+
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+ TCG_REG_G0 = 0,
+ TCG_REG_G1,
+ TCG_REG_G2,
+ TCG_REG_G3,
+ TCG_REG_G4,
+ TCG_REG_G5,
+ TCG_REG_G6,
+ TCG_REG_G7,
+ TCG_REG_O0,
+ TCG_REG_O1,
+ TCG_REG_O2,
+ TCG_REG_O3,
+ TCG_REG_O4,
+ TCG_REG_O5,
+ TCG_REG_O6,
+ TCG_REG_O7,
+ TCG_REG_L0,
+ TCG_REG_L1,
+ TCG_REG_L2,
+ TCG_REG_L3,
+ TCG_REG_L4,
+ TCG_REG_L5,
+ TCG_REG_L6,
+ TCG_REG_L7,
+ TCG_REG_I0,
+ TCG_REG_I1,
+ TCG_REG_I2,
+ TCG_REG_I3,
+ TCG_REG_I4,
+ TCG_REG_I5,
+ TCG_REG_I6,
+ TCG_REG_I7,
+};
+
+#define TCG_CT_CONST_S11 0x100
+#define TCG_CT_CONST_S13 0x200
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_I6
+#ifdef __arch64__
+// Reserve space for AREG0
+#define TCG_TARGET_STACK_MINFRAME (176 + 4 * (int)sizeof(long) + \
+ TCG_STATIC_CALL_ARGS_SIZE)
+#define TCG_TARGET_CALL_STACK_OFFSET (2047 - 16)
+#define TCG_TARGET_STACK_ALIGN 16
+#else
+// AREG0 + one word for alignment
+#define TCG_TARGET_STACK_MINFRAME (92 + (2 + 1) * (int)sizeof(long) + \
+ TCG_STATIC_CALL_ARGS_SIZE)
+#define TCG_TARGET_CALL_STACK_OFFSET TCG_TARGET_STACK_MINFRAME
+#define TCG_TARGET_STACK_ALIGN 8
+#endif
+
+#ifdef __arch64__
+#define TCG_TARGET_EXTEND_ARGS 1
+#endif
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div_i32
+// #define TCG_TARGET_HAS_rot_i32
+// #define TCG_TARGET_HAS_ext8s_i32
+// #define TCG_TARGET_HAS_ext16s_i32
+// #define TCG_TARGET_HAS_ext8u_i32
+// #define TCG_TARGET_HAS_ext16u_i32
+// #define TCG_TARGET_HAS_bswap16_i32
+// #define TCG_TARGET_HAS_bswap32_i32
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_not_i32
+#define TCG_TARGET_HAS_andc_i32
+#define TCG_TARGET_HAS_orc_i32
+// #define TCG_TARGET_HAS_eqv_i32
+// #define TCG_TARGET_HAS_nand_i32
+// #define TCG_TARGET_HAS_nor_i32
+
+#if TCG_TARGET_REG_BITS == 64
+#define TCG_TARGET_HAS_div_i64
+// #define TCG_TARGET_HAS_rot_i64
+// #define TCG_TARGET_HAS_ext8s_i64
+// #define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+// #define TCG_TARGET_HAS_ext8u_i64
+// #define TCG_TARGET_HAS_ext16u_i64
+#define TCG_TARGET_HAS_ext32u_i64
+// #define TCG_TARGET_HAS_bswap16_i64
+// #define TCG_TARGET_HAS_bswap32_i64
+// #define TCG_TARGET_HAS_bswap64_i64
+#define TCG_TARGET_HAS_neg_i64
+#define TCG_TARGET_HAS_not_i64
+#define TCG_TARGET_HAS_andc_i64
+#define TCG_TARGET_HAS_orc_i64
+// #define TCG_TARGET_HAS_eqv_i64
+// #define TCG_TARGET_HAS_nand_i64
+// #define TCG_TARGET_HAS_nor_i64
+#endif
+
+/* Note: must be synced with dyngen-exec.h */
+#ifdef CONFIG_SOLARIS
+#define TCG_AREG0 TCG_REG_G2
+#elif defined(__sparc_v9__)
+#define TCG_AREG0 TCG_REG_G5
+#else
+#define TCG_AREG0 TCG_REG_G6
+#endif
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+ unsigned long p;
+
+ p = start & ~(8UL - 1UL);
+ stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
+
+ for (; p < stop; p += 8)
+ __asm__ __volatile__("flush\t%0" : : "r" (p));
+}
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
new file mode 100644
index 0000000..207a89f
--- /dev/null
+++ b/tcg/tcg-op.h
@@ -0,0 +1,2533 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "tcg.h"
+
+int gen_new_label(void);
+
+static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 arg1)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+}
+
+static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 arg1)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+}
+
+static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg arg1)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = arg1;
+}
+
+static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+}
+
+static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+}
+
+static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGArg arg2)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = arg2;
+}
+
+static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGArg arg2)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = arg2;
+}
+
+static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg arg1, TCGArg arg2)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = arg1;
+ *gen_opparam_ptr++ = arg2;
+}
+
+static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+}
+
+static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+}
+
+static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 arg1,
+ TCGv_i32 arg2, TCGArg arg3)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = arg3;
+}
+
+static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 arg1,
+ TCGv_i64 arg2, TCGArg arg3)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = arg3;
+}
+
+static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val,
+ TCGv_ptr base, TCGArg offset)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(val);
+ *gen_opparam_ptr++ = GET_TCGV_PTR(base);
+ *gen_opparam_ptr++ = offset;
+}
+
+static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val,
+ TCGv_ptr base, TCGArg offset)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(val);
+ *gen_opparam_ptr++ = GET_TCGV_PTR(base);
+ *gen_opparam_ptr++ = offset;
+}
+
+static inline void tcg_gen_qemu_ldst_op_i64_i32(TCGOpcode opc, TCGv_i64 val,
+ TCGv_i32 addr, TCGArg mem_index)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(val);
+ *gen_opparam_ptr++ = GET_TCGV_I32(addr);
+ *gen_opparam_ptr++ = mem_index;
+}
+
+static inline void tcg_gen_qemu_ldst_op_i64_i64(TCGOpcode opc, TCGv_i64 val,
+ TCGv_i64 addr, TCGArg mem_index)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(val);
+ *gen_opparam_ptr++ = GET_TCGV_I64(addr);
+ *gen_opparam_ptr++ = mem_index;
+}
+
+static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3, TCGv_i32 arg4)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg4);
+}
+
+static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3, TCGv_i64 arg4)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg4);
+}
+
+static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3, TCGArg arg4)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = arg4;
+}
+
+static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3, TCGArg arg4)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = arg4;
+}
+
+static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGArg arg3, TCGArg arg4)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = arg3;
+ *gen_opparam_ptr++ = arg4;
+}
+
+static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGArg arg3, TCGArg arg4)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = arg3;
+ *gen_opparam_ptr++ = arg4;
+}
+
+static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg4);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg5);
+}
+
+static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg4);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg5);
+}
+
+static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg4);
+ *gen_opparam_ptr++ = arg5;
+}
+
+static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg4);
+ *gen_opparam_ptr++ = arg5;
+}
+
+static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 arg1,
+ TCGv_i32 arg2, TCGv_i32 arg3,
+ TCGArg arg4, TCGArg arg5)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = arg4;
+ *gen_opparam_ptr++ = arg5;
+}
+
+static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 arg1,
+ TCGv_i64 arg2, TCGv_i64 arg3,
+ TCGArg arg4, TCGArg arg5)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = arg4;
+ *gen_opparam_ptr++ = arg5;
+}
+
+static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5,
+ TCGv_i32 arg6)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg4);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg5);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg6);
+}
+
+static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5,
+ TCGv_i64 arg6)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg4);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg5);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg6);
+}
+
+static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2,
+ TCGv_i32 arg3, TCGv_i32 arg4,
+ TCGv_i32 arg5, TCGArg arg6)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg4);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg5);
+ *gen_opparam_ptr++ = arg6;
+}
+
+static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2,
+ TCGv_i64 arg3, TCGv_i64 arg4,
+ TCGv_i64 arg5, TCGArg arg6)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg4);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg5);
+ *gen_opparam_ptr++ = arg6;
+}
+
+static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 arg1,
+ TCGv_i32 arg2, TCGv_i32 arg3,
+ TCGv_i32 arg4, TCGArg arg5, TCGArg arg6)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I32(arg4);
+ *gen_opparam_ptr++ = arg5;
+ *gen_opparam_ptr++ = arg6;
+}
+
+static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 arg1,
+ TCGv_i64 arg2, TCGv_i64 arg3,
+ TCGv_i64 arg4, TCGArg arg5, TCGArg arg6)
+{
+ *gen_opc_ptr++ = opc;
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg1);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg2);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg3);
+ *gen_opparam_ptr++ = GET_TCGV_I64(arg4);
+ *gen_opparam_ptr++ = arg5;
+ *gen_opparam_ptr++ = arg6;
+}
+
+static inline void gen_set_label(int n)
+{
+ tcg_gen_op1i(INDEX_op_set_label, n);
+}
+
+static inline void tcg_gen_br(int label)
+{
+ tcg_gen_op1i(INDEX_op_br, label);
+}
+
+static inline void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+ if (!TCGV_EQUAL_I32(ret, arg))
+ tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg);
+}
+
+static inline void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg)
+{
+ tcg_gen_op2i_i32(INDEX_op_movi_i32, ret, arg);
+}
+
+/* A version of dh_sizemask from def-helper.h that doesn't rely on
+ preprocessor magic. */
+static inline int tcg_gen_sizemask(int n, int is_64bit, int is_signed)
+{
+ return (is_64bit << n*2) | (is_signed << (n*2 + 1));
+}
+
+/* helper calls */
+static inline void tcg_gen_helperN(void *func, int flags, int sizemask,
+ TCGArg ret, int nargs, TCGArg *args)
+{
+ TCGv_ptr fn;
+ fn = tcg_const_ptr((tcg_target_long)func);
+ tcg_gen_callN(&tcg_ctx, fn, flags, sizemask, ret,
+ nargs, args);
+ tcg_temp_free_ptr(fn);
+}
+
+/* Note: Both tcg_gen_helper32() and tcg_gen_helper64() are currently
+ reserved for helpers in tcg-runtime.c. These helpers are all const
+ and pure, hence the call to tcg_gen_callN() with TCG_CALL_CONST |
+ TCG_CALL_PURE. This may need to be adjusted if these functions
+ start to be used with other helpers. */
+static inline void tcg_gen_helper32(void *func, int sizemask, TCGv_i32 ret,
+ TCGv_i32 a, TCGv_i32 b)
+{
+ TCGv_ptr fn;
+ TCGArg args[2];
+ fn = tcg_const_ptr((tcg_target_long)func);
+ args[0] = GET_TCGV_I32(a);
+ args[1] = GET_TCGV_I32(b);
+ tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask,
+ GET_TCGV_I32(ret), 2, args);
+ tcg_temp_free_ptr(fn);
+}
+
+static inline void tcg_gen_helper64(void *func, int sizemask, TCGv_i64 ret,
+ TCGv_i64 a, TCGv_i64 b)
+{
+ TCGv_ptr fn;
+ TCGArg args[2];
+ fn = tcg_const_ptr((tcg_target_long)func);
+ args[0] = GET_TCGV_I64(a);
+ args[1] = GET_TCGV_I64(b);
+ tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask,
+ GET_TCGV_I64(ret), 2, args);
+ tcg_temp_free_ptr(fn);
+}
+
+/* 32 bit ops */
+
+static inline void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset)
+{
+ tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_add_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0 = tcg_const_i32(arg1);
+ tcg_gen_sub_i32(ret, t0, arg2);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_sub_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ if (TCGV_EQUAL_I32(arg1, arg2)) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2);
+ }
+}
+
+static inline void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_movi_i32(ret, 0);
+ } else if (arg2 == 0xffffffff) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_and_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ if (TCGV_EQUAL_I32(arg1, arg2)) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2);
+ }
+}
+
+static inline void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0xffffffff) {
+ tcg_gen_movi_i32(ret, 0xffffffff);
+ } else if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_or_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ if (TCGV_EQUAL_I32(arg1, arg2)) {
+ tcg_gen_movi_i32(ret, 0);
+ } else {
+ tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2);
+ }
+}
+
+static inline void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_xor_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_shl_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_shr_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_sar_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+ }
+}
+
+static inline void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1,
+ TCGv_i32 arg2, int label_index)
+{
+ tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_index);
+}
+
+static inline void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1,
+ int32_t arg2, int label_index)
+{
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_brcond_i32(cond, arg1, t0, label_index);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret,
+ TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond);
+}
+
+static inline void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret,
+ TCGv_i32 arg1, int32_t arg2)
+{
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_setcond_i32(cond, ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_mul_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+}
+
+#ifdef TCG_TARGET_HAS_div_i32
+static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2);
+}
+#elif defined(TCG_TARGET_HAS_div2_i32)
+static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_sari_i32(t0, arg1, 31);
+ tcg_gen_op5_i32(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_sari_i32(t0, arg1, 31);
+ tcg_gen_op5_i32(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_movi_i32(t0, 0);
+ tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2);
+ tcg_temp_free_i32(t0);
+}
+
+static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_movi_i32(t0, 0);
+ tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2);
+ tcg_temp_free_i32(t0);
+}
+#else
+static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 32-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 0, 1);
+ sizemask |= tcg_gen_sizemask(1, 0, 1);
+ sizemask |= tcg_gen_sizemask(2, 0, 1);
+
+ tcg_gen_helper32(tcg_helper_div_i32, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 32-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 0, 1);
+ sizemask |= tcg_gen_sizemask(1, 0, 1);
+ sizemask |= tcg_gen_sizemask(2, 0, 1);
+
+ tcg_gen_helper32(tcg_helper_rem_i32, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 32-bit and unsigned. */
+ sizemask |= tcg_gen_sizemask(0, 0, 0);
+ sizemask |= tcg_gen_sizemask(1, 0, 0);
+ sizemask |= tcg_gen_sizemask(2, 0, 0);
+
+ tcg_gen_helper32(tcg_helper_divu_i32, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 32-bit and unsigned. */
+ sizemask |= tcg_gen_sizemask(0, 0, 0);
+ sizemask |= tcg_gen_sizemask(1, 0, 0);
+ sizemask |= tcg_gen_sizemask(2, 0, 0);
+
+ tcg_gen_helper32(tcg_helper_remu_i32, sizemask, ret, arg1, arg2);
+}
+#endif
+
+#if TCG_TARGET_REG_BITS == 32
+
+static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ if (!TCGV_EQUAL_I64(ret, arg)) {
+ tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg));
+ }
+}
+
+static inline void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg)
+{
+ tcg_gen_movi_i32(TCGV_LOW(ret), arg);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32);
+}
+
+static inline void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), 31);
+}
+
+static inline void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+}
+
+static inline void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+}
+
+static inline void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ /* since arg2 and ret have different types, they cannot be the
+ same temporary */
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+ tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset);
+ tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4);
+#else
+ tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset);
+ tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4);
+#endif
+}
+
+static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset);
+}
+
+static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset);
+}
+
+static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+ tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset);
+}
+
+static inline void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2,
+ tcg_target_long offset)
+{
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+ tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset);
+ tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4);
+#else
+ tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset);
+ tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4);
+#endif
+}
+
+static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op6_i32(INDEX_op_add2_i32, TCGV_LOW(ret), TCGV_HIGH(ret),
+ TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2),
+ TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op6_i32(INDEX_op_sub2_i32, TCGV_LOW(ret), TCGV_HIGH(ret),
+ TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2),
+ TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ tcg_gen_andi_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2);
+ tcg_gen_andi_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32);
+}
+
+static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ tcg_gen_ori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2);
+ tcg_gen_ori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32);
+}
+
+static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ tcg_gen_xori_i32(TCGV_LOW(ret), TCGV_LOW(arg1), arg2);
+ tcg_gen_xori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32);
+}
+
+/* XXX: use generic code when basic block handling is OK or CPU
+ specific code (x86) */
+static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_shl_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ tcg_gen_shifti_i64(ret, arg1, arg2, 0, 0);
+}
+
+static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_shr_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ tcg_gen_shifti_i64(ret, arg1, arg2, 1, 0);
+}
+
+static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_sar_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1);
+}
+
+static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1,
+ TCGv_i64 arg2, int label_index)
+{
+ tcg_gen_op6ii_i32(INDEX_op_brcond2_i32,
+ TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2),
+ TCGV_HIGH(arg2), cond, label_index);
+}
+
+static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret,
+ TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret),
+ TCGV_LOW(arg1), TCGV_HIGH(arg1),
+ TCGV_LOW(arg2), TCGV_HIGH(arg2), cond);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ TCGv_i64 t0;
+ TCGv_i32 t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i32();
+
+ tcg_gen_op4_i32(INDEX_op_mulu2_i32, TCGV_LOW(t0), TCGV_HIGH(t0),
+ TCGV_LOW(arg1), TCGV_LOW(arg2));
+
+ tcg_gen_mul_i32(t1, TCGV_LOW(arg1), TCGV_HIGH(arg2));
+ tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1);
+ tcg_gen_mul_i32(t1, TCGV_HIGH(arg1), TCGV_LOW(arg2));
+ tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1);
+
+ tcg_gen_mov_i64(ret, t0);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i32(t1);
+}
+
+static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and unsigned. */
+ sizemask |= tcg_gen_sizemask(0, 1, 0);
+ sizemask |= tcg_gen_sizemask(1, 1, 0);
+ sizemask |= tcg_gen_sizemask(2, 1, 0);
+
+ tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and unsigned. */
+ sizemask |= tcg_gen_sizemask(0, 1, 0);
+ sizemask |= tcg_gen_sizemask(1, 1, 0);
+ sizemask |= tcg_gen_sizemask(2, 1, 0);
+
+ tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2);
+}
+
+#else
+
+static inline void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ if (!TCGV_EQUAL_I64(ret, arg))
+ tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg);
+}
+
+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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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)
+{
+ 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,
+ 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,
+ 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,
+ 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)
+{
+ tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ if (TCGV_EQUAL_I64(arg1, arg2)) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2);
+ }
+}
+
+static inline void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_and_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ if (TCGV_EQUAL_I64(arg1, arg2)) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2);
+ }
+}
+
+static inline void tcg_gen_ori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_or_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ if (TCGV_EQUAL_I64(arg1, arg2)) {
+ tcg_gen_movi_i64(ret, 0);
+ } else {
+ tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2);
+ }
+}
+
+static inline void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_xor_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_shl_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+ }
+}
+
+static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_shr_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+ }
+}
+
+static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_sar_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+ }
+}
+
+static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1,
+ TCGv_i64 arg2, int label_index)
+{
+ tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, label_index);
+}
+
+static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret,
+ TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond);
+}
+
+static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2);
+}
+
+#ifdef TCG_TARGET_HAS_div_i64
+static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2);
+}
+#elif defined(TCG_TARGET_HAS_div2_i64)
+static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_sari_i64(t0, arg1, 63);
+ tcg_gen_op5_i64(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_sari_i64(t0, arg1, 63);
+ tcg_gen_op5_i64(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_movi_i64(t0, 0);
+ tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_movi_i64(t0, 0);
+ tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2);
+ tcg_temp_free_i64(t0);
+}
+#else
+static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and signed. */
+ sizemask |= tcg_gen_sizemask(0, 1, 1);
+ sizemask |= tcg_gen_sizemask(1, 1, 1);
+ sizemask |= tcg_gen_sizemask(2, 1, 1);
+
+ tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and unsigned. */
+ sizemask |= tcg_gen_sizemask(0, 1, 0);
+ sizemask |= tcg_gen_sizemask(1, 1, 0);
+ sizemask |= tcg_gen_sizemask(2, 1, 0);
+
+ tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+ int sizemask = 0;
+ /* Return value and both arguments are 64-bit and unsigned. */
+ sizemask |= tcg_gen_sizemask(0, 1, 0);
+ sizemask |= tcg_gen_sizemask(1, 1, 0);
+ sizemask |= tcg_gen_sizemask(2, 1, 0);
+
+ tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2);
+}
+#endif
+
+#endif
+
+static inline void tcg_gen_addi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_add_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+ }
+}
+
+static inline void tcg_gen_subfi_i64(TCGv_i64 ret, int64_t arg1, TCGv_i64 arg2)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg1);
+ tcg_gen_sub_i64(ret, t0, arg2);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_sub_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+ }
+}
+static inline void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1,
+ int64_t arg2, int label_index)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_brcond_i64(cond, arg1, t0, label_index);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret,
+ TCGv_i64 arg1, int64_t arg2)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_setcond_i64(cond, ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+}
+
+static inline void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_mul_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+}
+
+
+/***************************************/
+/* optional operations */
+
+static inline void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_ext8s_i32
+ tcg_gen_op2_i32(INDEX_op_ext8s_i32, ret, arg);
+#else
+ tcg_gen_shli_i32(ret, arg, 24);
+ tcg_gen_sari_i32(ret, ret, 24);
+#endif
+}
+
+static inline void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_ext16s_i32
+ tcg_gen_op2_i32(INDEX_op_ext16s_i32, ret, arg);
+#else
+ tcg_gen_shli_i32(ret, arg, 16);
+ tcg_gen_sari_i32(ret, ret, 16);
+#endif
+}
+
+static inline void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_ext8u_i32
+ tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg);
+#else
+ tcg_gen_andi_i32(ret, arg, 0xffu);
+#endif
+}
+
+static inline void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_ext16u_i32
+ tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg);
+#else
+ tcg_gen_andi_i32(ret, arg, 0xffffu);
+#endif
+}
+
+/* Note: we assume the two high bytes are set to zero */
+static inline void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_bswap16_i32
+ tcg_gen_op2_i32(INDEX_op_bswap16_i32, ret, arg);
+#else
+ TCGv_i32 t0 = tcg_temp_new_i32();
+
+ tcg_gen_ext8u_i32(t0, arg);
+ tcg_gen_shli_i32(t0, t0, 8);
+ tcg_gen_shri_i32(ret, arg, 8);
+ tcg_gen_or_i32(ret, ret, t0);
+ tcg_temp_free_i32(t0);
+#endif
+}
+
+static inline void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_bswap32_i32
+ tcg_gen_op2_i32(INDEX_op_bswap32_i32, ret, arg);
+#else
+ TCGv_i32 t0, t1;
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+
+ tcg_gen_shli_i32(t0, arg, 24);
+
+ tcg_gen_andi_i32(t1, arg, 0x0000ff00);
+ tcg_gen_shli_i32(t1, t1, 8);
+ tcg_gen_or_i32(t0, t0, t1);
+
+ tcg_gen_shri_i32(t1, arg, 8);
+ tcg_gen_andi_i32(t1, t1, 0x0000ff00);
+ tcg_gen_or_i32(t0, t0, t1);
+
+ tcg_gen_shri_i32(t1, arg, 24);
+ tcg_gen_or_i32(ret, t0, t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+#endif
+}
+
+#if TCG_TARGET_REG_BITS == 32
+static inline void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_ext8s_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+}
+
+static inline void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_ext16s_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+}
+
+static inline void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+}
+
+static inline void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_ext8u_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_ext16u_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg)
+{
+ tcg_gen_mov_i32(ret, TCGV_LOW(arg));
+}
+
+static inline void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg)
+{
+ tcg_gen_mov_i32(TCGV_LOW(ret), arg);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg)
+{
+ tcg_gen_mov_i32(TCGV_LOW(ret), arg);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+}
+
+/* Note: we assume the six high bytes are set to zero */
+static inline void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg));
+ tcg_gen_bswap16_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+}
+
+/* Note: we assume the four high bytes are set to zero */
+static inline void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg));
+ tcg_gen_bswap32_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+}
+
+static inline void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+ TCGv_i32 t0, t1;
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+
+ tcg_gen_bswap32_i32(t0, TCGV_LOW(arg));
+ tcg_gen_bswap32_i32(t1, TCGV_HIGH(arg));
+ tcg_gen_mov_i32(TCGV_LOW(ret), t1);
+ tcg_gen_mov_i32(TCGV_HIGH(ret), t0);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+}
+#else
+
+static inline void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_ext8s_i64
+ tcg_gen_op2_i64(INDEX_op_ext8s_i64, ret, arg);
+#else
+ tcg_gen_shli_i64(ret, arg, 56);
+ tcg_gen_sari_i64(ret, ret, 56);
+#endif
+}
+
+static inline void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_ext16s_i64
+ tcg_gen_op2_i64(INDEX_op_ext16s_i64, ret, arg);
+#else
+ tcg_gen_shli_i64(ret, arg, 48);
+ tcg_gen_sari_i64(ret, ret, 48);
+#endif
+}
+
+static inline void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_ext32s_i64
+ tcg_gen_op2_i64(INDEX_op_ext32s_i64, ret, arg);
+#else
+ tcg_gen_shli_i64(ret, arg, 32);
+ tcg_gen_sari_i64(ret, ret, 32);
+#endif
+}
+
+static inline void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_ext8u_i64
+ tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg);
+#else
+ tcg_gen_andi_i64(ret, arg, 0xffu);
+#endif
+}
+
+static inline void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_ext16u_i64
+ tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg);
+#else
+ tcg_gen_andi_i64(ret, arg, 0xffffu);
+#endif
+}
+
+static inline void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_ext32u_i64
+ tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg);
+#else
+ tcg_gen_andi_i64(ret, arg, 0xffffffffu);
+#endif
+}
+
+/* Note: we assume the target supports move between 32 and 64 bit
+ registers. This will probably break MIPS64 targets. */
+static inline void tcg_gen_trunc_i64_i32(TCGv_i32 ret, TCGv_i64 arg)
+{
+ tcg_gen_mov_i32(ret, MAKE_TCGV_I32(GET_TCGV_I64(arg)));
+}
+
+/* Note: we assume the target supports move between 32 and 64 bit
+ registers */
+static inline void tcg_gen_extu_i32_i64(TCGv_i64 ret, TCGv_i32 arg)
+{
+ tcg_gen_ext32u_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg)));
+}
+
+/* Note: we assume the target supports move between 32 and 64 bit
+ registers */
+static inline void tcg_gen_ext_i32_i64(TCGv_i64 ret, TCGv_i32 arg)
+{
+ tcg_gen_ext32s_i64(ret, MAKE_TCGV_I64(GET_TCGV_I32(arg)));
+}
+
+/* Note: we assume the six high bytes are set to zero */
+static inline void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_bswap16_i64
+ tcg_gen_op2_i64(INDEX_op_bswap16_i64, ret, arg);
+#else
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ tcg_gen_ext8u_i64(t0, arg);
+ tcg_gen_shli_i64(t0, t0, 8);
+ tcg_gen_shri_i64(ret, arg, 8);
+ tcg_gen_or_i64(ret, ret, t0);
+ tcg_temp_free_i64(t0);
+#endif
+}
+
+/* Note: we assume the four high bytes are set to zero */
+static inline void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_bswap32_i64
+ tcg_gen_op2_i64(INDEX_op_bswap32_i64, ret, arg);
+#else
+ TCGv_i64 t0, t1;
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_shli_i64(t0, arg, 24);
+ tcg_gen_ext32u_i64(t0, t0);
+
+ tcg_gen_andi_i64(t1, arg, 0x0000ff00);
+ tcg_gen_shli_i64(t1, t1, 8);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t1, arg, 8);
+ tcg_gen_andi_i64(t1, t1, 0x0000ff00);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t1, arg, 24);
+ tcg_gen_or_i64(ret, t0, t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+#endif
+}
+
+static inline void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_bswap64_i64
+ tcg_gen_op2_i64(INDEX_op_bswap64_i64, ret, arg);
+#else
+ TCGv_i64 t0 = tcg_temp_new_i64();
+ TCGv_i64 t1 = tcg_temp_new_i64();
+
+ tcg_gen_shli_i64(t0, arg, 56);
+
+ tcg_gen_andi_i64(t1, arg, 0x0000ff00);
+ tcg_gen_shli_i64(t1, t1, 40);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_andi_i64(t1, arg, 0x00ff0000);
+ tcg_gen_shli_i64(t1, t1, 24);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_andi_i64(t1, arg, 0xff000000);
+ tcg_gen_shli_i64(t1, t1, 8);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t1, arg, 8);
+ tcg_gen_andi_i64(t1, t1, 0xff000000);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t1, arg, 24);
+ tcg_gen_andi_i64(t1, t1, 0x00ff0000);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t1, arg, 40);
+ tcg_gen_andi_i64(t1, t1, 0x0000ff00);
+ tcg_gen_or_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t1, arg, 56);
+ tcg_gen_or_i64(ret, t0, t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+#endif
+}
+
+#endif
+
+static inline void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_neg_i32
+ tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg);
+#else
+ TCGv_i32 t0 = tcg_const_i32(0);
+ tcg_gen_sub_i32(ret, t0, arg);
+ tcg_temp_free_i32(t0);
+#endif
+}
+
+static inline void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_neg_i64
+ tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg);
+#else
+ TCGv_i64 t0 = tcg_const_i64(0);
+ tcg_gen_sub_i64(ret, t0, arg);
+ tcg_temp_free_i64(t0);
+#endif
+}
+
+static inline void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg)
+{
+#ifdef TCG_TARGET_HAS_not_i32
+ tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg);
+#else
+ tcg_gen_xori_i32(ret, arg, -1);
+#endif
+}
+
+static inline void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg)
+{
+#ifdef TCG_TARGET_HAS_not_i64
+ tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg);
+#elif defined(TCG_TARGET_HAS_not_i32) && TCG_TARGET_REG_BITS == 32
+ tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg));
+ tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg));
+#else
+ tcg_gen_xori_i64(ret, arg, -1);
+#endif
+}
+
+static inline void tcg_gen_discard_i32(TCGv_i32 arg)
+{
+ tcg_gen_op1_i32(INDEX_op_discard, arg);
+}
+
+#if TCG_TARGET_REG_BITS == 32
+static inline void tcg_gen_discard_i64(TCGv_i64 arg)
+{
+ tcg_gen_discard_i32(TCGV_LOW(arg));
+ tcg_gen_discard_i32(TCGV_HIGH(arg));
+}
+#else
+static inline void tcg_gen_discard_i64(TCGv_i64 arg)
+{
+ tcg_gen_op1_i64(INDEX_op_discard, arg);
+}
+#endif
+
+static inline void tcg_gen_concat_i32_i64(TCGv_i64 dest, TCGv_i32 low, TCGv_i32 high)
+{
+#if TCG_TARGET_REG_BITS == 32
+ tcg_gen_mov_i32(TCGV_LOW(dest), low);
+ tcg_gen_mov_i32(TCGV_HIGH(dest), high);
+#else
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ /* This extension is only needed for type correctness.
+ We may be able to do better given target specific information. */
+ tcg_gen_extu_i32_i64(tmp, high);
+ tcg_gen_shli_i64(tmp, tmp, 32);
+ tcg_gen_extu_i32_i64(dest, low);
+ tcg_gen_or_i64(dest, dest, tmp);
+ tcg_temp_free_i64(tmp);
+#endif
+}
+
+static inline void tcg_gen_concat32_i64(TCGv_i64 dest, TCGv_i64 low, TCGv_i64 high)
+{
+#if TCG_TARGET_REG_BITS == 32
+ tcg_gen_concat_i32_i64(dest, TCGV_LOW(low), TCGV_LOW(high));
+#else
+ TCGv_i64 tmp = tcg_temp_new_i64();
+ tcg_gen_ext32u_i64(dest, low);
+ tcg_gen_shli_i64(tmp, high, 32);
+ tcg_gen_or_i64(dest, dest, tmp);
+ tcg_temp_free_i64(tmp);
+#endif
+}
+
+static inline void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_andc_i32
+ tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2);
+#else
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_not_i32(t0, arg2);
+ tcg_gen_and_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+#endif
+}
+
+static inline void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_andc_i64
+ tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2);
+#elif defined(TCG_TARGET_HAS_andc_i32) && TCG_TARGET_REG_BITS == 32
+ tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+#else
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_not_i64(t0, arg2);
+ tcg_gen_and_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+#endif
+}
+
+static inline void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_eqv_i32
+ tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2);
+#else
+ tcg_gen_xor_i32(ret, arg1, arg2);
+ tcg_gen_not_i32(ret, ret);
+#endif
+}
+
+static inline void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_eqv_i64
+ tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2);
+#elif defined(TCG_TARGET_HAS_eqv_i32) && TCG_TARGET_REG_BITS == 32
+ tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+#else
+ tcg_gen_xor_i64(ret, arg1, arg2);
+ tcg_gen_not_i64(ret, ret);
+#endif
+}
+
+static inline void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_nand_i32
+ tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2);
+#else
+ tcg_gen_and_i32(ret, arg1, arg2);
+ tcg_gen_not_i32(ret, ret);
+#endif
+}
+
+static inline void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_nand_i64
+ tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2);
+#elif defined(TCG_TARGET_HAS_nand_i32) && TCG_TARGET_REG_BITS == 32
+ tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+#else
+ tcg_gen_and_i64(ret, arg1, arg2);
+ tcg_gen_not_i64(ret, ret);
+#endif
+}
+
+static inline void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_nor_i32
+ tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2);
+#else
+ tcg_gen_or_i32(ret, arg1, arg2);
+ tcg_gen_not_i32(ret, ret);
+#endif
+}
+
+static inline void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_nor_i64
+ tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2);
+#elif defined(TCG_TARGET_HAS_nor_i32) && TCG_TARGET_REG_BITS == 32
+ tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+#else
+ tcg_gen_or_i64(ret, arg1, arg2);
+ tcg_gen_not_i64(ret, ret);
+#endif
+}
+
+static inline void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_orc_i32
+ tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2);
+#else
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_not_i32(t0, arg2);
+ tcg_gen_or_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+#endif
+}
+
+static inline void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_orc_i64
+ tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2);
+#elif defined(TCG_TARGET_HAS_orc_i32) && TCG_TARGET_REG_BITS == 32
+ tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2));
+ tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+#else
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_not_i64(t0, arg2);
+ tcg_gen_or_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+#endif
+}
+
+static inline void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_rot_i32
+ tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2);
+#else
+ TCGv_i32 t0, t1;
+
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+ tcg_gen_shl_i32(t0, arg1, arg2);
+ tcg_gen_subfi_i32(t1, 32, arg2);
+ tcg_gen_shr_i32(t1, arg1, t1);
+ tcg_gen_or_i32(ret, t0, t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+#endif
+}
+
+static inline void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_rot_i64
+ tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2);
+#else
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ tcg_gen_shl_i64(t0, arg1, arg2);
+ tcg_gen_subfi_i64(t1, 64, arg2);
+ tcg_gen_shr_i64(t1, arg1, t1);
+ tcg_gen_or_i64(ret, t0, t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+#endif
+}
+
+static inline void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+#ifdef TCG_TARGET_HAS_rot_i32
+ TCGv_i32 t0 = tcg_const_i32(arg2);
+ tcg_gen_rotl_i32(ret, arg1, t0);
+ tcg_temp_free_i32(t0);
+#else
+ TCGv_i32 t0, t1;
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+ tcg_gen_shli_i32(t0, arg1, arg2);
+ tcg_gen_shri_i32(t1, arg1, 32 - arg2);
+ tcg_gen_or_i32(ret, t0, t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+#endif
+ }
+}
+
+static inline void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+#ifdef TCG_TARGET_HAS_rot_i64
+ TCGv_i64 t0 = tcg_const_i64(arg2);
+ tcg_gen_rotl_i64(ret, arg1, t0);
+ tcg_temp_free_i64(t0);
+#else
+ TCGv_i64 t0, t1;
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ tcg_gen_shli_i64(t0, arg1, arg2);
+ tcg_gen_shri_i64(t1, arg1, 64 - arg2);
+ tcg_gen_or_i64(ret, t0, t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+#endif
+ }
+}
+
+static inline void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2)
+{
+#ifdef TCG_TARGET_HAS_rot_i32
+ tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2);
+#else
+ TCGv_i32 t0, t1;
+
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+ tcg_gen_shr_i32(t0, arg1, arg2);
+ tcg_gen_subfi_i32(t1, 32, arg2);
+ tcg_gen_shl_i32(t1, arg1, t1);
+ tcg_gen_or_i32(ret, t0, t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+#endif
+}
+
+static inline void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
+{
+#ifdef TCG_TARGET_HAS_rot_i64
+ tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2);
+#else
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+ tcg_gen_shr_i64(t0, arg1, arg2);
+ tcg_gen_subfi_i64(t1, 64, arg2);
+ tcg_gen_shl_i64(t1, arg1, t1);
+ tcg_gen_or_i64(ret, t0, t1);
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+#endif
+}
+
+static inline void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i32(ret, arg1);
+ } else {
+ tcg_gen_rotli_i32(ret, arg1, 32 - arg2);
+ }
+}
+
+static inline void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2)
+{
+ /* some cases can be optimized here */
+ if (arg2 == 0) {
+ tcg_gen_mov_i64(ret, arg1);
+ } else {
+ tcg_gen_rotli_i64(ret, arg1, 64 - arg2);
+ }
+}
+
+static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1,
+ TCGv_i32 arg2, unsigned int ofs,
+ unsigned int len)
+{
+#ifdef TCG_TARGET_HAS_deposit_i32
+ tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len);
+#else
+ uint32_t mask = (1u << len) - 1;
+ TCGv_i32 t1 = tcg_temp_new_i32 ();
+
+ tcg_gen_andi_i32(t1, arg2, mask);
+ tcg_gen_shli_i32(t1, t1, ofs);
+ tcg_gen_andi_i32(ret, arg1, ~(mask << ofs));
+ tcg_gen_or_i32(ret, ret, t1);
+
+ tcg_temp_free_i32(t1);
+#endif
+}
+
+static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1,
+ TCGv_i64 arg2, unsigned int ofs,
+ unsigned int len)
+{
+#ifdef TCG_TARGET_HAS_deposit_i64
+ tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len);
+#else
+ uint64_t mask = (1ull << len) - 1;
+ TCGv_i64 t1 = tcg_temp_new_i64 ();
+
+ tcg_gen_andi_i64(t1, arg2, mask);
+ tcg_gen_shli_i64(t1, t1, ofs);
+ tcg_gen_andi_i64(ret, arg1, ~(mask << ofs));
+ tcg_gen_or_i64(ret, ret, t1);
+
+ tcg_temp_free_i64(t1);
+#endif
+}
+
+/***************************************/
+/* QEMU specific operations. Their type depend on the QEMU CPU
+ type. */
+#ifndef TARGET_LONG_BITS
+#error must include QEMU headers
+#endif
+
+#if TARGET_LONG_BITS == 32
+#define TCGv TCGv_i32
+#define tcg_temp_new() tcg_temp_new_i32()
+#define tcg_global_reg_new tcg_global_reg_new_i32
+#define tcg_global_mem_new tcg_global_mem_new_i32
+#define tcg_temp_local_new() tcg_temp_local_new_i32()
+#define tcg_temp_free tcg_temp_free_i32
+#define tcg_gen_qemu_ldst_op tcg_gen_op3i_i32
+#define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i32
+#define TCGV_UNUSED(x) TCGV_UNUSED_I32(x)
+#define TCGV_EQUAL(a, b) TCGV_EQUAL_I32(a, b)
+#else
+#define TCGv TCGv_i64
+#define tcg_temp_new() tcg_temp_new_i64()
+#define tcg_global_reg_new tcg_global_reg_new_i64
+#define tcg_global_mem_new tcg_global_mem_new_i64
+#define tcg_temp_local_new() tcg_temp_local_new_i64()
+#define tcg_temp_free tcg_temp_free_i64
+#define tcg_gen_qemu_ldst_op tcg_gen_op3i_i64
+#define tcg_gen_qemu_ldst_op_i64 tcg_gen_qemu_ldst_op_i64_i64
+#define TCGV_UNUSED(x) TCGV_UNUSED_I64(x)
+#define TCGV_EQUAL(a, b) TCGV_EQUAL_I64(a, b)
+#endif
+
+/* debug info: write the PC of the corresponding QEMU CPU instruction */
+static inline void tcg_gen_debug_insn_start(uint64_t pc)
+{
+ /* XXX: must really use a 32 bit size for TCGArg in all cases */
+#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
+ tcg_gen_op2ii(INDEX_op_debug_insn_start,
+ (uint32_t)(pc), (uint32_t)(pc >> 32));
+#else
+ tcg_gen_op1i(INDEX_op_debug_insn_start, pc);
+#endif
+}
+
+static inline void tcg_gen_exit_tb(tcg_target_long val)
+{
+ tcg_gen_op1i(INDEX_op_exit_tb, val);
+}
+
+static inline void tcg_gen_goto_tb(int idx)
+{
+ tcg_gen_op1i(INDEX_op_goto_tb, idx);
+}
+
+#if TCG_TARGET_REG_BITS == 32
+static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_ld8u, ret, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld8u, TCGV_LOW(ret), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_ld8s, ret, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld8s, TCGV_LOW(ret), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_ld16u, ret, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld16u, TCGV_LOW(ret), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_ld16s, ret, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld16s, TCGV_LOW(ret), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op4i_i32(INDEX_op_qemu_ld64, TCGV_LOW(ret), TCGV_HIGH(ret), addr, mem_index);
+#else
+ tcg_gen_op5i_i32(INDEX_op_qemu_ld64, TCGV_LOW(ret), TCGV_HIGH(ret),
+ TCGV_LOW(addr), TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_st8, arg, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_st8, TCGV_LOW(arg), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_st16, arg, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_st16, TCGV_LOW(arg), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op3i_i32(INDEX_op_qemu_st32, arg, addr, mem_index);
+#else
+ tcg_gen_op4i_i32(INDEX_op_qemu_st32, TCGV_LOW(arg), TCGV_LOW(addr),
+ TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st64(TCGv_i64 arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_op4i_i32(INDEX_op_qemu_st64, TCGV_LOW(arg), TCGV_HIGH(arg), addr,
+ mem_index);
+#else
+ tcg_gen_op5i_i32(INDEX_op_qemu_st64, TCGV_LOW(arg), TCGV_HIGH(arg),
+ TCGV_LOW(addr), TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+#define tcg_gen_ld_ptr tcg_gen_ld_i32
+#define tcg_gen_discard_ptr tcg_gen_discard_i32
+
+#else /* TCG_TARGET_REG_BITS == 32 */
+
+static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld8u, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld8s, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld16u, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld16s, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index);
+#else
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32u, ret, addr, mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index);
+#else
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32s, ret, addr, mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op_i64(INDEX_op_qemu_ld64, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_st8, arg, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_st16, arg, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index)
+{
+ tcg_gen_qemu_ldst_op(INDEX_op_qemu_st32, arg, addr, mem_index);
+}
+
+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
+
+#endif /* TCG_TARGET_REG_BITS != 32 */
+
+#if TARGET_LONG_BITS == 64
+#define tcg_gen_movi_tl tcg_gen_movi_i64
+#define tcg_gen_mov_tl tcg_gen_mov_i64
+#define tcg_gen_ld8u_tl tcg_gen_ld8u_i64
+#define tcg_gen_ld8s_tl tcg_gen_ld8s_i64
+#define tcg_gen_ld16u_tl tcg_gen_ld16u_i64
+#define tcg_gen_ld16s_tl tcg_gen_ld16s_i64
+#define tcg_gen_ld32u_tl tcg_gen_ld32u_i64
+#define tcg_gen_ld32s_tl tcg_gen_ld32s_i64
+#define tcg_gen_ld_tl tcg_gen_ld_i64
+#define tcg_gen_st8_tl tcg_gen_st8_i64
+#define tcg_gen_st16_tl tcg_gen_st16_i64
+#define tcg_gen_st32_tl tcg_gen_st32_i64
+#define tcg_gen_st_tl tcg_gen_st_i64
+#define tcg_gen_add_tl tcg_gen_add_i64
+#define tcg_gen_addi_tl tcg_gen_addi_i64
+#define tcg_gen_sub_tl tcg_gen_sub_i64
+#define tcg_gen_neg_tl tcg_gen_neg_i64
+#define tcg_gen_subfi_tl tcg_gen_subfi_i64
+#define tcg_gen_subi_tl tcg_gen_subi_i64
+#define tcg_gen_and_tl tcg_gen_and_i64
+#define tcg_gen_andi_tl tcg_gen_andi_i64
+#define tcg_gen_or_tl tcg_gen_or_i64
+#define tcg_gen_ori_tl tcg_gen_ori_i64
+#define tcg_gen_xor_tl tcg_gen_xor_i64
+#define tcg_gen_xori_tl tcg_gen_xori_i64
+#define tcg_gen_not_tl tcg_gen_not_i64
+#define tcg_gen_shl_tl tcg_gen_shl_i64
+#define tcg_gen_shli_tl tcg_gen_shli_i64
+#define tcg_gen_shr_tl tcg_gen_shr_i64
+#define tcg_gen_shri_tl tcg_gen_shri_i64
+#define tcg_gen_sar_tl tcg_gen_sar_i64
+#define tcg_gen_sari_tl tcg_gen_sari_i64
+#define tcg_gen_brcond_tl tcg_gen_brcond_i64
+#define tcg_gen_brcondi_tl tcg_gen_brcondi_i64
+#define tcg_gen_setcond_tl tcg_gen_setcond_i64
+#define tcg_gen_setcondi_tl tcg_gen_setcondi_i64
+#define tcg_gen_mul_tl tcg_gen_mul_i64
+#define tcg_gen_muli_tl tcg_gen_muli_i64
+#define tcg_gen_div_tl tcg_gen_div_i64
+#define tcg_gen_rem_tl tcg_gen_rem_i64
+#define tcg_gen_divu_tl tcg_gen_divu_i64
+#define tcg_gen_remu_tl tcg_gen_remu_i64
+#define tcg_gen_discard_tl tcg_gen_discard_i64
+#define tcg_gen_trunc_tl_i32 tcg_gen_trunc_i64_i32
+#define tcg_gen_trunc_i64_tl tcg_gen_mov_i64
+#define tcg_gen_extu_i32_tl tcg_gen_extu_i32_i64
+#define tcg_gen_ext_i32_tl tcg_gen_ext_i32_i64
+#define tcg_gen_extu_tl_i64 tcg_gen_mov_i64
+#define tcg_gen_ext_tl_i64 tcg_gen_mov_i64
+#define tcg_gen_ext8u_tl tcg_gen_ext8u_i64
+#define tcg_gen_ext8s_tl tcg_gen_ext8s_i64
+#define tcg_gen_ext16u_tl tcg_gen_ext16u_i64
+#define tcg_gen_ext16s_tl tcg_gen_ext16s_i64
+#define tcg_gen_ext32u_tl tcg_gen_ext32u_i64
+#define tcg_gen_ext32s_tl tcg_gen_ext32s_i64
+#define tcg_gen_bswap16_tl tcg_gen_bswap16_i64
+#define tcg_gen_bswap32_tl tcg_gen_bswap32_i64
+#define tcg_gen_bswap64_tl tcg_gen_bswap64_i64
+#define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64
+#define tcg_gen_andc_tl tcg_gen_andc_i64
+#define tcg_gen_eqv_tl tcg_gen_eqv_i64
+#define tcg_gen_nand_tl tcg_gen_nand_i64
+#define tcg_gen_nor_tl tcg_gen_nor_i64
+#define tcg_gen_orc_tl tcg_gen_orc_i64
+#define tcg_gen_rotl_tl tcg_gen_rotl_i64
+#define tcg_gen_rotli_tl tcg_gen_rotli_i64
+#define tcg_gen_rotr_tl tcg_gen_rotr_i64
+#define tcg_gen_rotri_tl tcg_gen_rotri_i64
+#define tcg_gen_deposit_tl tcg_gen_deposit_i64
+#define tcg_const_tl tcg_const_i64
+#define tcg_const_local_tl tcg_const_local_i64
+#else
+#define tcg_gen_movi_tl tcg_gen_movi_i32
+#define tcg_gen_mov_tl tcg_gen_mov_i32
+#define tcg_gen_ld8u_tl tcg_gen_ld8u_i32
+#define tcg_gen_ld8s_tl tcg_gen_ld8s_i32
+#define tcg_gen_ld16u_tl tcg_gen_ld16u_i32
+#define tcg_gen_ld16s_tl tcg_gen_ld16s_i32
+#define tcg_gen_ld32u_tl tcg_gen_ld_i32
+#define tcg_gen_ld32s_tl tcg_gen_ld_i32
+#define tcg_gen_ld_tl tcg_gen_ld_i32
+#define tcg_gen_st8_tl tcg_gen_st8_i32
+#define tcg_gen_st16_tl tcg_gen_st16_i32
+#define tcg_gen_st32_tl tcg_gen_st_i32
+#define tcg_gen_st_tl tcg_gen_st_i32
+#define tcg_gen_add_tl tcg_gen_add_i32
+#define tcg_gen_addi_tl tcg_gen_addi_i32
+#define tcg_gen_sub_tl tcg_gen_sub_i32
+#define tcg_gen_neg_tl tcg_gen_neg_i32
+#define tcg_gen_subfi_tl tcg_gen_subfi_i32
+#define tcg_gen_subi_tl tcg_gen_subi_i32
+#define tcg_gen_and_tl tcg_gen_and_i32
+#define tcg_gen_andi_tl tcg_gen_andi_i32
+#define tcg_gen_or_tl tcg_gen_or_i32
+#define tcg_gen_ori_tl tcg_gen_ori_i32
+#define tcg_gen_xor_tl tcg_gen_xor_i32
+#define tcg_gen_xori_tl tcg_gen_xori_i32
+#define tcg_gen_not_tl tcg_gen_not_i32
+#define tcg_gen_shl_tl tcg_gen_shl_i32
+#define tcg_gen_shli_tl tcg_gen_shli_i32
+#define tcg_gen_shr_tl tcg_gen_shr_i32
+#define tcg_gen_shri_tl tcg_gen_shri_i32
+#define tcg_gen_sar_tl tcg_gen_sar_i32
+#define tcg_gen_sari_tl tcg_gen_sari_i32
+#define tcg_gen_brcond_tl tcg_gen_brcond_i32
+#define tcg_gen_brcondi_tl tcg_gen_brcondi_i32
+#define tcg_gen_setcond_tl tcg_gen_setcond_i32
+#define tcg_gen_setcondi_tl tcg_gen_setcondi_i32
+#define tcg_gen_mul_tl tcg_gen_mul_i32
+#define tcg_gen_muli_tl tcg_gen_muli_i32
+#define tcg_gen_div_tl tcg_gen_div_i32
+#define tcg_gen_rem_tl tcg_gen_rem_i32
+#define tcg_gen_divu_tl tcg_gen_divu_i32
+#define tcg_gen_remu_tl tcg_gen_remu_i32
+#define tcg_gen_discard_tl tcg_gen_discard_i32
+#define tcg_gen_trunc_tl_i32 tcg_gen_mov_i32
+#define tcg_gen_trunc_i64_tl tcg_gen_trunc_i64_i32
+#define tcg_gen_extu_i32_tl tcg_gen_mov_i32
+#define tcg_gen_ext_i32_tl tcg_gen_mov_i32
+#define tcg_gen_extu_tl_i64 tcg_gen_extu_i32_i64
+#define tcg_gen_ext_tl_i64 tcg_gen_ext_i32_i64
+#define tcg_gen_ext8u_tl tcg_gen_ext8u_i32
+#define tcg_gen_ext8s_tl tcg_gen_ext8s_i32
+#define tcg_gen_ext16u_tl tcg_gen_ext16u_i32
+#define tcg_gen_ext16s_tl tcg_gen_ext16s_i32
+#define tcg_gen_ext32u_tl tcg_gen_mov_i32
+#define tcg_gen_ext32s_tl tcg_gen_mov_i32
+#define tcg_gen_bswap16_tl tcg_gen_bswap16_i32
+#define tcg_gen_bswap32_tl tcg_gen_bswap32_i32
+#define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64
+#define tcg_gen_andc_tl tcg_gen_andc_i32
+#define tcg_gen_eqv_tl tcg_gen_eqv_i32
+#define tcg_gen_nand_tl tcg_gen_nand_i32
+#define tcg_gen_nor_tl tcg_gen_nor_i32
+#define tcg_gen_orc_tl tcg_gen_orc_i32
+#define tcg_gen_rotl_tl tcg_gen_rotl_i32
+#define tcg_gen_rotli_tl tcg_gen_rotli_i32
+#define tcg_gen_rotr_tl tcg_gen_rotr_i32
+#define tcg_gen_rotri_tl tcg_gen_rotri_i32
+#define tcg_gen_deposit_tl tcg_gen_deposit_i32
+#define tcg_const_tl tcg_const_i32
+#define tcg_const_local_tl tcg_const_local_i32
+#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
+#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
+#endif /* TCG_TARGET_REG_BITS != 32 */
diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h
new file mode 100644
index 0000000..2c7ca1a
--- /dev/null
+++ b/tcg/tcg-opc.h
@@ -0,0 +1,310 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * DEF(name, oargs, iargs, cargs, flags)
+ */
+
+/* predefined ops */
+DEF(end, 0, 0, 0, 0) /* must be kept first */
+DEF(nop, 0, 0, 0, 0)
+DEF(nop1, 0, 0, 1, 0)
+DEF(nop2, 0, 0, 2, 0)
+DEF(nop3, 0, 0, 3, 0)
+DEF(nopn, 0, 0, 1, 0) /* variable number of parameters */
+
+DEF(discard, 1, 0, 0, 0)
+
+DEF(set_label, 0, 0, 1, 0)
+DEF(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */
+DEF(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+
+DEF(mov_i32, 1, 1, 0, 0)
+DEF(movi_i32, 1, 0, 1, 0)
+DEF(setcond_i32, 1, 2, 1, 0)
+/* load/store */
+DEF(ld8u_i32, 1, 1, 1, 0)
+DEF(ld8s_i32, 1, 1, 1, 0)
+DEF(ld16u_i32, 1, 1, 1, 0)
+DEF(ld16s_i32, 1, 1, 1, 0)
+DEF(ld_i32, 1, 1, 1, 0)
+DEF(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+/* arith */
+DEF(add_i32, 1, 2, 0, 0)
+DEF(sub_i32, 1, 2, 0, 0)
+DEF(mul_i32, 1, 2, 0, 0)
+#ifdef TCG_TARGET_HAS_div_i32
+DEF(div_i32, 1, 2, 0, 0)
+DEF(divu_i32, 1, 2, 0, 0)
+DEF(rem_i32, 1, 2, 0, 0)
+DEF(remu_i32, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_div2_i32
+DEF(div2_i32, 2, 3, 0, 0)
+DEF(divu2_i32, 2, 3, 0, 0)
+#endif
+DEF(and_i32, 1, 2, 0, 0)
+DEF(or_i32, 1, 2, 0, 0)
+DEF(xor_i32, 1, 2, 0, 0)
+/* shifts/rotates */
+DEF(shl_i32, 1, 2, 0, 0)
+DEF(shr_i32, 1, 2, 0, 0)
+DEF(sar_i32, 1, 2, 0, 0)
+#ifdef TCG_TARGET_HAS_rot_i32
+DEF(rotl_i32, 1, 2, 0, 0)
+DEF(rotr_i32, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_deposit_i32
+DEF(deposit_i32, 1, 2, 2, 0)
+#endif
+
+DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+#if TCG_TARGET_REG_BITS == 32
+DEF(add2_i32, 2, 4, 0, 0)
+DEF(sub2_i32, 2, 4, 0, 0)
+DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF(mulu2_i32, 2, 2, 0, 0)
+DEF(setcond2_i32, 1, 4, 1, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext8s_i32
+DEF(ext8s_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i32
+DEF(ext16s_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i32
+DEF(ext8u_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i32
+DEF(ext16u_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap16_i32
+DEF(bswap16_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap32_i32
+DEF(bswap32_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_not_i32
+DEF(not_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_neg_i32
+DEF(neg_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_andc_i32
+DEF(andc_i32, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_orc_i32
+DEF(orc_i32, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_eqv_i32
+DEF(eqv_i32, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_nand_i32
+DEF(nand_i32, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_nor_i32
+DEF(nor_i32, 1, 2, 0, 0)
+#endif
+
+#if TCG_TARGET_REG_BITS == 64
+DEF(mov_i64, 1, 1, 0, 0)
+DEF(movi_i64, 1, 0, 1, 0)
+DEF(setcond_i64, 1, 2, 1, 0)
+/* load/store */
+DEF(ld8u_i64, 1, 1, 1, 0)
+DEF(ld8s_i64, 1, 1, 1, 0)
+DEF(ld16u_i64, 1, 1, 1, 0)
+DEF(ld16s_i64, 1, 1, 1, 0)
+DEF(ld32u_i64, 1, 1, 1, 0)
+DEF(ld32s_i64, 1, 1, 1, 0)
+DEF(ld_i64, 1, 1, 1, 0)
+DEF(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+/* arith */
+DEF(add_i64, 1, 2, 0, 0)
+DEF(sub_i64, 1, 2, 0, 0)
+DEF(mul_i64, 1, 2, 0, 0)
+#ifdef TCG_TARGET_HAS_div_i64
+DEF(div_i64, 1, 2, 0, 0)
+DEF(divu_i64, 1, 2, 0, 0)
+DEF(rem_i64, 1, 2, 0, 0)
+DEF(remu_i64, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_div2_i64
+DEF(div2_i64, 2, 3, 0, 0)
+DEF(divu2_i64, 2, 3, 0, 0)
+#endif
+DEF(and_i64, 1, 2, 0, 0)
+DEF(or_i64, 1, 2, 0, 0)
+DEF(xor_i64, 1, 2, 0, 0)
+/* shifts/rotates */
+DEF(shl_i64, 1, 2, 0, 0)
+DEF(shr_i64, 1, 2, 0, 0)
+DEF(sar_i64, 1, 2, 0, 0)
+#ifdef TCG_TARGET_HAS_rot_i64
+DEF(rotl_i64, 1, 2, 0, 0)
+DEF(rotr_i64, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_deposit_i64
+DEF(deposit_i64, 1, 2, 2, 0)
+#endif
+
+DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+#ifdef TCG_TARGET_HAS_ext8s_i64
+DEF(ext8s_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i64
+DEF(ext16s_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext32s_i64
+DEF(ext32s_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext8u_i64
+DEF(ext8u_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext16u_i64
+DEF(ext16u_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext32u_i64
+DEF(ext32u_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap16_i64
+DEF(bswap16_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap32_i64
+DEF(bswap32_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap64_i64
+DEF(bswap64_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_not_i64
+DEF(not_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_neg_i64
+DEF(neg_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_andc_i64
+DEF(andc_i64, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_orc_i64
+DEF(orc_i64, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_eqv_i64
+DEF(eqv_i64, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_nand_i64
+DEF(nand_i64, 1, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_nor_i64
+DEF(nor_i64, 1, 2, 0, 0)
+#endif
+#endif
+
+/* QEMU specific */
+#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
+DEF(debug_insn_start, 0, 0, 2, 0)
+#else
+DEF(debug_insn_start, 0, 0, 1, 0)
+#endif
+DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+/* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op
+ constants must be defined */
+#if TCG_TARGET_REG_BITS == 32
+#if TARGET_LONG_BITS == 32
+DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_ld32, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+
+#if TARGET_LONG_BITS == 32
+DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+
+#else /* TCG_TARGET_REG_BITS == 32 */
+
+DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+
+DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+
+#endif /* TCG_TARGET_REG_BITS != 32 */
+
+#undef DEF
diff --git a/tcg/tcg-runtime.h b/tcg/tcg-runtime.h
new file mode 100644
index 0000000..5615b13
--- /dev/null
+++ b/tcg/tcg-runtime.h
@@ -0,0 +1,18 @@
+#ifndef TCG_RUNTIME_H
+#define TCG_RUNTIME_H
+
+/* tcg-runtime.c */
+int32_t tcg_helper_div_i32(int32_t arg1, int32_t arg2);
+int32_t tcg_helper_rem_i32(int32_t arg1, int32_t arg2);
+uint32_t tcg_helper_divu_i32(uint32_t arg1, uint32_t arg2);
+uint32_t tcg_helper_remu_i32(uint32_t arg1, uint32_t arg2);
+
+int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_div_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_rem_i64(int64_t arg1, int64_t arg2);
+uint64_t tcg_helper_divu_i64(uint64_t arg1, uint64_t arg2);
+uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2);
+
+#endif
diff --git a/tcg/tcg.c b/tcg/tcg.c
new file mode 100644
index 0000000..5dd6a2c
--- /dev/null
+++ b/tcg/tcg.c
@@ -0,0 +1,2175 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* define it to use liveness analysis (better code) */
+#define USE_LIVENESS_ANALYSIS
+
+#include "config.h"
+
+#if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG)
+/* define it to suppress various consistency checks (faster) */
+#define NDEBUG
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#ifdef _WIN32
+#include <malloc.h>
+#endif
+#ifdef _AIX
+#include <alloca.h>
+#endif
+
+#include "qemu-common.h"
+#include "cache-utils.h"
+#include "host-utils.h"
+#include "qemu-timer.h"
+
+/* Note: the long term plan is to reduce the dependancies on the QEMU
+ CPU definitions. Currently they are used for qemu_ld/st
+ instructions */
+#define NO_CPU_IO_DEFS
+#include "cpu.h"
+#include "exec-all.h"
+
+#include "tcg-op.h"
+#include "elf.h"
+
+#if defined(CONFIG_USE_GUEST_BASE) && !defined(TCG_TARGET_HAS_GUEST_BASE)
+#error GUEST_BASE not supported on this host.
+#endif
+
+static void tcg_target_init(TCGContext *s);
+static void tcg_target_qemu_prologue(TCGContext *s);
+static void patch_reloc(uint8_t *code_ptr, int type,
+ tcg_target_long value, tcg_target_long addend);
+
+static TCGOpDef tcg_op_defs[] = {
+#define DEF(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags },
+#include "tcg-opc.h"
+#undef DEF
+};
+
+static TCGRegSet tcg_target_available_regs[2];
+static TCGRegSet tcg_target_call_clobber_regs;
+
+/* XXX: move that inside the context */
+uint16_t *gen_opc_ptr;
+TCGArg *gen_opparam_ptr;
+
+static inline void tcg_out8(TCGContext *s, uint8_t v)
+{
+ *s->code_ptr++ = v;
+}
+
+static inline void tcg_out16(TCGContext *s, uint16_t v)
+{
+ *(uint16_t *)s->code_ptr = v;
+ s->code_ptr += 2;
+}
+
+static inline void tcg_out32(TCGContext *s, uint32_t v)
+{
+ *(uint32_t *)s->code_ptr = v;
+ s->code_ptr += 4;
+}
+
+/* label relocation processing */
+
+static void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type,
+ int label_index, long addend)
+{
+ TCGLabel *l;
+ TCGRelocation *r;
+
+ l = &s->labels[label_index];
+ if (l->has_value) {
+ /* FIXME: This may break relocations on RISC targets that
+ modify instruction fields in place. The caller may not have
+ written the initial value. */
+ patch_reloc(code_ptr, type, l->u.value, addend);
+ } else {
+ /* add a new relocation entry */
+ r = tcg_malloc(sizeof(TCGRelocation));
+ r->type = type;
+ r->ptr = code_ptr;
+ r->addend = addend;
+ r->next = l->u.first_reloc;
+ l->u.first_reloc = r;
+ }
+}
+
+static void tcg_out_label(TCGContext *s, int label_index,
+ tcg_target_long value)
+{
+ TCGLabel *l;
+ TCGRelocation *r;
+
+ l = &s->labels[label_index];
+ if (l->has_value)
+ tcg_abort();
+ r = l->u.first_reloc;
+ while (r != NULL) {
+ patch_reloc(r->ptr, r->type, value, r->addend);
+ r = r->next;
+ }
+ l->has_value = 1;
+ l->u.value = value;
+}
+
+int gen_new_label(void)
+{
+ TCGContext *s = &tcg_ctx;
+ int idx;
+ TCGLabel *l;
+
+ if (s->nb_labels >= TCG_MAX_LABELS)
+ tcg_abort();
+ idx = s->nb_labels++;
+ l = &s->labels[idx];
+ l->has_value = 0;
+ l->u.first_reloc = NULL;
+ return idx;
+}
+
+#include "tcg-target.c"
+
+/* pool based memory allocation */
+void *tcg_malloc_internal(TCGContext *s, int size)
+{
+ TCGPool *p;
+ int pool_size;
+
+ if (size > TCG_POOL_CHUNK_SIZE) {
+ /* big malloc: insert a new pool (XXX: could optimize) */
+ p = qemu_malloc(sizeof(TCGPool) + size);
+ p->size = size;
+ if (s->pool_current)
+ s->pool_current->next = p;
+ else
+ s->pool_first = p;
+ p->next = s->pool_current;
+ } else {
+ p = s->pool_current;
+ if (!p) {
+ p = s->pool_first;
+ if (!p)
+ goto new_pool;
+ } else {
+ if (!p->next) {
+ new_pool:
+ pool_size = TCG_POOL_CHUNK_SIZE;
+ p = qemu_malloc(sizeof(TCGPool) + pool_size);
+ p->size = pool_size;
+ p->next = NULL;
+ if (s->pool_current)
+ s->pool_current->next = p;
+ else
+ s->pool_first = p;
+ } else {
+ p = p->next;
+ }
+ }
+ }
+ s->pool_current = p;
+ s->pool_cur = p->data + size;
+ s->pool_end = p->data + p->size;
+ return p->data;
+}
+
+void tcg_pool_reset(TCGContext *s)
+{
+ s->pool_cur = s->pool_end = NULL;
+ s->pool_current = NULL;
+}
+
+void tcg_context_init(TCGContext *s)
+{
+ int op, total_args, n;
+ TCGOpDef *def;
+ TCGArgConstraint *args_ct;
+ int *sorted_args;
+
+ memset(s, 0, sizeof(*s));
+ s->temps = s->static_temps;
+ s->nb_globals = 0;
+
+ /* Count total number of arguments and allocate the corresponding
+ space */
+ total_args = 0;
+ for(op = 0; op < NB_OPS; op++) {
+ def = &tcg_op_defs[op];
+ n = def->nb_iargs + def->nb_oargs;
+ total_args += n;
+ }
+
+ args_ct = qemu_malloc(sizeof(TCGArgConstraint) * total_args);
+ sorted_args = qemu_malloc(sizeof(int) * total_args);
+
+ for(op = 0; op < NB_OPS; op++) {
+ def = &tcg_op_defs[op];
+ def->args_ct = args_ct;
+ def->sorted_args = sorted_args;
+ n = def->nb_iargs + def->nb_oargs;
+ sorted_args += n;
+ args_ct += n;
+ }
+
+ tcg_target_init(s);
+}
+
+void tcg_prologue_init(TCGContext *s)
+{
+ /* init global prologue and epilogue */
+ s->code_buf = code_gen_prologue;
+ s->code_ptr = s->code_buf;
+ tcg_target_qemu_prologue(s);
+ flush_icache_range((unsigned long)s->code_buf,
+ (unsigned long)s->code_ptr);
+}
+
+void tcg_set_frame(TCGContext *s, int reg,
+ tcg_target_long start, tcg_target_long size)
+{
+ s->frame_start = start;
+ s->frame_end = start + size;
+ s->frame_reg = reg;
+}
+
+void tcg_func_start(TCGContext *s)
+{
+ int i;
+ tcg_pool_reset(s);
+ s->nb_temps = s->nb_globals;
+ for(i = 0; i < (TCG_TYPE_COUNT * 2); i++)
+ s->first_free_temp[i] = -1;
+ s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS);
+ s->nb_labels = 0;
+ s->current_frame_offset = s->frame_start;
+
+ gen_opc_ptr = gen_opc_buf;
+ gen_opparam_ptr = gen_opparam_buf;
+}
+
+static inline void tcg_temp_alloc(TCGContext *s, int n)
+{
+ if (n > TCG_MAX_TEMPS)
+ tcg_abort();
+}
+
+static inline int tcg_global_reg_new_internal(TCGType type, int reg,
+ const char *name)
+{
+ TCGContext *s = &tcg_ctx;
+ TCGTemp *ts;
+ int idx;
+
+#if TCG_TARGET_REG_BITS == 32
+ if (type != TCG_TYPE_I32)
+ tcg_abort();
+#endif
+ if (tcg_regset_test_reg(s->reserved_regs, reg))
+ tcg_abort();
+ idx = s->nb_globals;
+ tcg_temp_alloc(s, s->nb_globals + 1);
+ ts = &s->temps[s->nb_globals];
+ ts->base_type = type;
+ ts->type = type;
+ ts->fixed_reg = 1;
+ ts->reg = reg;
+ ts->name = name;
+ s->nb_globals++;
+ tcg_regset_set_reg(s->reserved_regs, reg);
+ return idx;
+}
+
+TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name)
+{
+ int idx;
+
+ idx = tcg_global_reg_new_internal(TCG_TYPE_I32, reg, name);
+ return MAKE_TCGV_I32(idx);
+}
+
+TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name)
+{
+ int idx;
+
+ idx = tcg_global_reg_new_internal(TCG_TYPE_I64, reg, name);
+ return MAKE_TCGV_I64(idx);
+}
+
+static inline int tcg_global_mem_new_internal(TCGType type, int reg,
+ tcg_target_long offset,
+ const char *name)
+{
+ TCGContext *s = &tcg_ctx;
+ TCGTemp *ts;
+ int idx;
+
+ idx = s->nb_globals;
+#if TCG_TARGET_REG_BITS == 32
+ if (type == TCG_TYPE_I64) {
+ char buf[64];
+ tcg_temp_alloc(s, s->nb_globals + 2);
+ ts = &s->temps[s->nb_globals];
+ ts->base_type = type;
+ ts->type = TCG_TYPE_I32;
+ ts->fixed_reg = 0;
+ ts->mem_allocated = 1;
+ ts->mem_reg = reg;
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+ ts->mem_offset = offset + 4;
+#else
+ ts->mem_offset = offset;
+#endif
+ pstrcpy(buf, sizeof(buf), name);
+ pstrcat(buf, sizeof(buf), "_0");
+ ts->name = strdup(buf);
+ ts++;
+
+ ts->base_type = type;
+ ts->type = TCG_TYPE_I32;
+ ts->fixed_reg = 0;
+ ts->mem_allocated = 1;
+ ts->mem_reg = reg;
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+ ts->mem_offset = offset;
+#else
+ ts->mem_offset = offset + 4;
+#endif
+ pstrcpy(buf, sizeof(buf), name);
+ pstrcat(buf, sizeof(buf), "_1");
+ ts->name = strdup(buf);
+
+ s->nb_globals += 2;
+ } else
+#endif
+ {
+ tcg_temp_alloc(s, s->nb_globals + 1);
+ ts = &s->temps[s->nb_globals];
+ ts->base_type = type;
+ ts->type = type;
+ ts->fixed_reg = 0;
+ ts->mem_allocated = 1;
+ ts->mem_reg = reg;
+ ts->mem_offset = offset;
+ ts->name = name;
+ s->nb_globals++;
+ }
+ return idx;
+}
+
+TCGv_i32 tcg_global_mem_new_i32(int reg, tcg_target_long offset,
+ const char *name)
+{
+ int idx;
+
+ idx = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name);
+ return MAKE_TCGV_I32(idx);
+}
+
+TCGv_i64 tcg_global_mem_new_i64(int reg, tcg_target_long offset,
+ const char *name)
+{
+ int idx;
+
+ idx = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name);
+ return MAKE_TCGV_I64(idx);
+}
+
+static inline int tcg_temp_new_internal(TCGType type, int temp_local)
+{
+ TCGContext *s = &tcg_ctx;
+ TCGTemp *ts;
+ int idx, k;
+
+ k = type;
+ if (temp_local)
+ k += TCG_TYPE_COUNT;
+ idx = s->first_free_temp[k];
+ if (idx != -1) {
+ /* There is already an available temp with the
+ right type */
+ ts = &s->temps[idx];
+ s->first_free_temp[k] = ts->next_free_temp;
+ ts->temp_allocated = 1;
+ assert(ts->temp_local == temp_local);
+ } else {
+ idx = s->nb_temps;
+#if TCG_TARGET_REG_BITS == 32
+ if (type == TCG_TYPE_I64) {
+ tcg_temp_alloc(s, s->nb_temps + 2);
+ ts = &s->temps[s->nb_temps];
+ ts->base_type = type;
+ ts->type = TCG_TYPE_I32;
+ ts->temp_allocated = 1;
+ ts->temp_local = temp_local;
+ ts->name = NULL;
+ ts++;
+ ts->base_type = TCG_TYPE_I32;
+ ts->type = TCG_TYPE_I32;
+ ts->temp_allocated = 1;
+ ts->temp_local = temp_local;
+ ts->name = NULL;
+ s->nb_temps += 2;
+ } else
+#endif
+ {
+ tcg_temp_alloc(s, s->nb_temps + 1);
+ ts = &s->temps[s->nb_temps];
+ ts->base_type = type;
+ ts->type = type;
+ ts->temp_allocated = 1;
+ ts->temp_local = temp_local;
+ ts->name = NULL;
+ s->nb_temps++;
+ }
+ }
+ return idx;
+}
+
+TCGv_i32 tcg_temp_new_internal_i32(int temp_local)
+{
+ int idx;
+
+ idx = tcg_temp_new_internal(TCG_TYPE_I32, temp_local);
+ return MAKE_TCGV_I32(idx);
+}
+
+TCGv_i64 tcg_temp_new_internal_i64(int temp_local)
+{
+ int idx;
+
+ idx = tcg_temp_new_internal(TCG_TYPE_I64, temp_local);
+ return MAKE_TCGV_I64(idx);
+}
+
+static inline void tcg_temp_free_internal(int idx)
+{
+ TCGContext *s = &tcg_ctx;
+ TCGTemp *ts;
+ int k;
+
+ assert(idx >= s->nb_globals && idx < s->nb_temps);
+ ts = &s->temps[idx];
+ assert(ts->temp_allocated != 0);
+ ts->temp_allocated = 0;
+ k = ts->base_type;
+ if (ts->temp_local)
+ k += TCG_TYPE_COUNT;
+ ts->next_free_temp = s->first_free_temp[k];
+ s->first_free_temp[k] = idx;
+}
+
+void tcg_temp_free_i32(TCGv_i32 arg)
+{
+ tcg_temp_free_internal(GET_TCGV_I32(arg));
+}
+
+void tcg_temp_free_i64(TCGv_i64 arg)
+{
+ tcg_temp_free_internal(GET_TCGV_I64(arg));
+}
+
+TCGv_i32 tcg_const_i32(int32_t val)
+{
+ TCGv_i32 t0;
+ t0 = tcg_temp_new_i32();
+ tcg_gen_movi_i32(t0, val);
+ return t0;
+}
+
+TCGv_i64 tcg_const_i64(int64_t val)
+{
+ TCGv_i64 t0;
+ t0 = tcg_temp_new_i64();
+ tcg_gen_movi_i64(t0, val);
+ return t0;
+}
+
+TCGv_i32 tcg_const_local_i32(int32_t val)
+{
+ TCGv_i32 t0;
+ t0 = tcg_temp_local_new_i32();
+ tcg_gen_movi_i32(t0, val);
+ return t0;
+}
+
+TCGv_i64 tcg_const_local_i64(int64_t val)
+{
+ TCGv_i64 t0;
+ t0 = tcg_temp_local_new_i64();
+ tcg_gen_movi_i64(t0, val);
+ return t0;
+}
+
+void tcg_register_helper(void *func, const char *name)
+{
+ TCGContext *s = &tcg_ctx;
+ int n;
+ if ((s->nb_helpers + 1) > s->allocated_helpers) {
+ n = s->allocated_helpers;
+ if (n == 0) {
+ n = 4;
+ } else {
+ n *= 2;
+ }
+ s->helpers = realloc(s->helpers, n * sizeof(TCGHelperInfo));
+ s->allocated_helpers = n;
+ }
+ s->helpers[s->nb_helpers].func = (tcg_target_ulong)func;
+ s->helpers[s->nb_helpers].name = name;
+ s->nb_helpers++;
+}
+
+/* Note: we convert the 64 bit args to 32 bit and do some alignment
+ and endian swap. Maybe it would be better to do the alignment
+ and endian swap in tcg_reg_alloc_call(). */
+void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags,
+ int sizemask, TCGArg ret, int nargs, TCGArg *args)
+{
+#ifdef TCG_TARGET_I386
+ int call_type;
+#endif
+ int i;
+ int real_args;
+ int nb_rets;
+ TCGArg *nparam;
+
+#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
+ for (i = 0; i < nargs; ++i) {
+ int is_64bit = sizemask & (1 << (i+1)*2);
+ int is_signed = sizemask & (2 << (i+1)*2);
+ if (!is_64bit) {
+ TCGv_i64 temp = tcg_temp_new_i64();
+ TCGv_i64 orig = MAKE_TCGV_I64(args[i]);
+ if (is_signed) {
+ tcg_gen_ext32s_i64(temp, orig);
+ } else {
+ tcg_gen_ext32u_i64(temp, orig);
+ }
+ args[i] = GET_TCGV_I64(temp);
+ }
+ }
+#endif /* TCG_TARGET_EXTEND_ARGS */
+
+ *gen_opc_ptr++ = INDEX_op_call;
+ nparam = gen_opparam_ptr++;
+#ifdef TCG_TARGET_I386
+ call_type = (flags & TCG_CALL_TYPE_MASK);
+#endif
+ if (ret != TCG_CALL_DUMMY_ARG) {
+#if TCG_TARGET_REG_BITS < 64
+ if (sizemask & 1) {
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+ *gen_opparam_ptr++ = ret + 1;
+ *gen_opparam_ptr++ = ret;
+#else
+ *gen_opparam_ptr++ = ret;
+ *gen_opparam_ptr++ = ret + 1;
+#endif
+ nb_rets = 2;
+ } else
+#endif
+ {
+ *gen_opparam_ptr++ = ret;
+ nb_rets = 1;
+ }
+ } else {
+ nb_rets = 0;
+ }
+ real_args = 0;
+ for (i = 0; i < nargs; i++) {
+#if TCG_TARGET_REG_BITS < 64
+ int is_64bit = sizemask & (1 << (i+1)*2);
+ if (is_64bit) {
+#ifdef TCG_TARGET_I386
+ /* REGPARM case: if the third parameter is 64 bit, it is
+ allocated on the stack */
+ if (i == 2 && call_type == TCG_CALL_TYPE_REGPARM) {
+ call_type = TCG_CALL_TYPE_REGPARM_2;
+ flags = (flags & ~TCG_CALL_TYPE_MASK) | call_type;
+ }
+#endif
+#ifdef TCG_TARGET_CALL_ALIGN_ARGS
+ /* some targets want aligned 64 bit args */
+ if (real_args & 1) {
+ *gen_opparam_ptr++ = TCG_CALL_DUMMY_ARG;
+ real_args++;
+ }
+#endif
+ /* If stack grows up, then we will be placing successive
+ arguments at lower addresses, which means we need to
+ reverse the order compared to how we would normally
+ treat either big or little-endian. For those arguments
+ that will wind up in registers, this still works for
+ HPPA (the only current STACK_GROWSUP target) since the
+ argument registers are *also* allocated in decreasing
+ order. If another such target is added, this logic may
+ have to get more complicated to differentiate between
+ stack arguments and register arguments. */
+#if defined(TCG_TARGET_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP)
+ *gen_opparam_ptr++ = args[i] + 1;
+ *gen_opparam_ptr++ = args[i];
+#else
+ *gen_opparam_ptr++ = args[i];
+ *gen_opparam_ptr++ = args[i] + 1;
+#endif
+ real_args += 2;
+ continue;
+ }
+#endif /* TCG_TARGET_REG_BITS < 64 */
+
+ *gen_opparam_ptr++ = args[i];
+ real_args++;
+ }
+ *gen_opparam_ptr++ = GET_TCGV_PTR(func);
+
+ *gen_opparam_ptr++ = flags;
+
+ *nparam = (nb_rets << 16) | (real_args + 1);
+
+ /* total parameters, needed to go backward in the instruction stream */
+ *gen_opparam_ptr++ = 1 + nb_rets + real_args + 3;
+
+#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64
+ for (i = 0; i < nargs; ++i) {
+ int is_64bit = sizemask & (1 << (i+1)*2);
+ if (!is_64bit) {
+ TCGv_i64 temp = MAKE_TCGV_I64(args[i]);
+ tcg_temp_free_i64(temp);
+ }
+ }
+#endif /* TCG_TARGET_EXTEND_ARGS */
+}
+
+#if TCG_TARGET_REG_BITS == 32
+void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1,
+ int c, int right, int arith)
+{
+ if (c == 0) {
+ tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1));
+ tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1));
+ } else if (c >= 32) {
+ c -= 32;
+ if (right) {
+ if (arith) {
+ tcg_gen_sari_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c);
+ tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31);
+ } else {
+ tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c);
+ tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+ }
+ } else {
+ tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), c);
+ tcg_gen_movi_i32(TCGV_LOW(ret), 0);
+ }
+ } else {
+ TCGv_i32 t0, t1;
+
+ t0 = tcg_temp_new_i32();
+ t1 = tcg_temp_new_i32();
+ if (right) {
+ tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c);
+ if (arith)
+ tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c);
+ else
+ tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c);
+ tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_LOW(arg1), c);
+ tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t0);
+ tcg_gen_mov_i32(TCGV_HIGH(ret), t1);
+ } else {
+ tcg_gen_shri_i32(t0, TCGV_LOW(arg1), 32 - c);
+ /* Note: ret can be the same as arg1, so we use t1 */
+ tcg_gen_shli_i32(t1, TCGV_LOW(arg1), c);
+ tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c);
+ tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0);
+ tcg_gen_mov_i32(TCGV_LOW(ret), t1);
+ }
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(t1);
+ }
+}
+#endif
+
+
+static void tcg_reg_alloc_start(TCGContext *s)
+{
+ int i;
+ TCGTemp *ts;
+ for(i = 0; i < s->nb_globals; i++) {
+ ts = &s->temps[i];
+ if (ts->fixed_reg) {
+ ts->val_type = TEMP_VAL_REG;
+ } else {
+ ts->val_type = TEMP_VAL_MEM;
+ }
+ }
+ for(i = s->nb_globals; i < s->nb_temps; i++) {
+ ts = &s->temps[i];
+ ts->val_type = TEMP_VAL_DEAD;
+ ts->mem_allocated = 0;
+ ts->fixed_reg = 0;
+ }
+ for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
+ s->reg_to_temp[i] = -1;
+ }
+}
+
+static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size,
+ int idx)
+{
+ TCGTemp *ts;
+
+ ts = &s->temps[idx];
+ if (idx < s->nb_globals) {
+ pstrcpy(buf, buf_size, ts->name);
+ } else {
+ if (ts->temp_local)
+ snprintf(buf, buf_size, "loc%d", idx - s->nb_globals);
+ else
+ snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals);
+ }
+ return buf;
+}
+
+char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg)
+{
+ return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I32(arg));
+}
+
+char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg)
+{
+ return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I64(arg));
+}
+
+static int helper_cmp(const void *p1, const void *p2)
+{
+ const TCGHelperInfo *th1 = p1;
+ const TCGHelperInfo *th2 = p2;
+ if (th1->func < th2->func)
+ return -1;
+ else if (th1->func == th2->func)
+ return 0;
+ else
+ return 1;
+}
+
+/* find helper definition (Note: A hash table would be better) */
+static TCGHelperInfo *tcg_find_helper(TCGContext *s, tcg_target_ulong val)
+{
+ int m, m_min, m_max;
+ TCGHelperInfo *th;
+ tcg_target_ulong v;
+
+ if (unlikely(!s->helpers_sorted)) {
+ qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo),
+ helper_cmp);
+ s->helpers_sorted = 1;
+ }
+
+ /* binary search */
+ m_min = 0;
+ m_max = s->nb_helpers - 1;
+ while (m_min <= m_max) {
+ m = (m_min + m_max) >> 1;
+ th = &s->helpers[m];
+ v = th->func;
+ if (v == val)
+ return th;
+ else if (val < v) {
+ m_max = m - 1;
+ } else {
+ m_min = m + 1;
+ }
+ }
+ return NULL;
+}
+
+static const char * const cond_name[] =
+{
+ [TCG_COND_EQ] = "eq",
+ [TCG_COND_NE] = "ne",
+ [TCG_COND_LT] = "lt",
+ [TCG_COND_GE] = "ge",
+ [TCG_COND_LE] = "le",
+ [TCG_COND_GT] = "gt",
+ [TCG_COND_LTU] = "ltu",
+ [TCG_COND_GEU] = "geu",
+ [TCG_COND_LEU] = "leu",
+ [TCG_COND_GTU] = "gtu"
+};
+
+void tcg_dump_ops(TCGContext *s, FILE *outfile)
+{
+ const uint16_t *opc_ptr;
+ const TCGArg *args;
+ TCGArg arg;
+ TCGOpcode c;
+ int i, k, nb_oargs, nb_iargs, nb_cargs, first_insn;
+ const TCGOpDef *def;
+ char buf[128];
+
+ first_insn = 1;
+ opc_ptr = gen_opc_buf;
+ args = gen_opparam_buf;
+ while (opc_ptr < gen_opc_ptr) {
+ c = *opc_ptr++;
+ def = &tcg_op_defs[c];
+ if (c == INDEX_op_debug_insn_start) {
+ uint64_t pc;
+#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
+ pc = ((uint64_t)args[1] << 32) | args[0];
+#else
+ pc = args[0];
+#endif
+ if (!first_insn)
+ fprintf(outfile, "\n");
+ fprintf(outfile, " ---- 0x%" PRIx64, pc);
+ first_insn = 0;
+ nb_oargs = def->nb_oargs;
+ nb_iargs = def->nb_iargs;
+ nb_cargs = def->nb_cargs;
+ } else if (c == INDEX_op_call) {
+ TCGArg arg;
+
+ /* variable number of arguments */
+ arg = *args++;
+ nb_oargs = arg >> 16;
+ nb_iargs = arg & 0xffff;
+ nb_cargs = def->nb_cargs;
+
+ fprintf(outfile, " %s ", def->name);
+
+ /* function name */
+ fprintf(outfile, "%s",
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + nb_iargs - 1]));
+ /* flags */
+ fprintf(outfile, ",$0x%" TCG_PRIlx,
+ args[nb_oargs + nb_iargs]);
+ /* nb out args */
+ fprintf(outfile, ",$%d", nb_oargs);
+ for(i = 0; i < nb_oargs; i++) {
+ fprintf(outfile, ",");
+ fprintf(outfile, "%s",
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), args[i]));
+ }
+ for(i = 0; i < (nb_iargs - 1); i++) {
+ fprintf(outfile, ",");
+ if (args[nb_oargs + i] == TCG_CALL_DUMMY_ARG) {
+ fprintf(outfile, "<dummy>");
+ } else {
+ fprintf(outfile, "%s",
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + i]));
+ }
+ }
+ } else if (c == INDEX_op_movi_i32
+#if TCG_TARGET_REG_BITS == 64
+ || c == INDEX_op_movi_i64
+#endif
+ ) {
+ tcg_target_ulong val;
+ TCGHelperInfo *th;
+
+ nb_oargs = def->nb_oargs;
+ nb_iargs = def->nb_iargs;
+ nb_cargs = def->nb_cargs;
+ fprintf(outfile, " %s %s,$", def->name,
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0]));
+ val = args[1];
+ th = tcg_find_helper(s, val);
+ if (th) {
+ fprintf(outfile, "%s", th->name);
+ } else {
+ if (c == INDEX_op_movi_i32)
+ fprintf(outfile, "0x%x", (uint32_t)val);
+ else
+ fprintf(outfile, "0x%" PRIx64 , (uint64_t)val);
+ }
+ } else {
+ fprintf(outfile, " %s ", def->name);
+ if (c == INDEX_op_nopn) {
+ /* variable number of arguments */
+ nb_cargs = *args;
+ nb_oargs = 0;
+ nb_iargs = 0;
+ } else {
+ nb_oargs = def->nb_oargs;
+ nb_iargs = def->nb_iargs;
+ nb_cargs = def->nb_cargs;
+ }
+
+ k = 0;
+ for(i = 0; i < nb_oargs; i++) {
+ if (k != 0)
+ fprintf(outfile, ",");
+ fprintf(outfile, "%s",
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++]));
+ }
+ for(i = 0; i < nb_iargs; i++) {
+ if (k != 0)
+ fprintf(outfile, ",");
+ fprintf(outfile, "%s",
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++]));
+ }
+ switch (c) {
+ case INDEX_op_brcond_i32:
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_brcond2_i32:
+#elif TCG_TARGET_REG_BITS == 64
+ case INDEX_op_brcond_i64:
+#endif
+ case INDEX_op_setcond_i32:
+#if TCG_TARGET_REG_BITS == 32
+ case INDEX_op_setcond2_i32:
+#elif TCG_TARGET_REG_BITS == 64
+ case INDEX_op_setcond_i64:
+#endif
+ if (args[k] < ARRAY_SIZE(cond_name) && cond_name[args[k]])
+ fprintf(outfile, ",%s", cond_name[args[k++]]);
+ else
+ fprintf(outfile, ",$0x%" TCG_PRIlx, args[k++]);
+ i = 1;
+ break;
+ default:
+ i = 0;
+ break;
+ }
+ for(; i < nb_cargs; i++) {
+ if (k != 0)
+ fprintf(outfile, ",");
+ arg = args[k++];
+ fprintf(outfile, "$0x%" TCG_PRIlx, arg);
+ }
+ }
+ fprintf(outfile, "\n");
+ args += nb_iargs + nb_oargs + nb_cargs;
+ }
+}
+
+/* we give more priority to constraints with less registers */
+static int get_constraint_priority(const TCGOpDef *def, int k)
+{
+ const TCGArgConstraint *arg_ct;
+
+ int i, n;
+ arg_ct = &def->args_ct[k];
+ if (arg_ct->ct & TCG_CT_ALIAS) {
+ /* an alias is equivalent to a single register */
+ n = 1;
+ } else {
+ if (!(arg_ct->ct & TCG_CT_REG))
+ return 0;
+ n = 0;
+ for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
+ if (tcg_regset_test_reg(arg_ct->u.regs, i))
+ n++;
+ }
+ }
+ return TCG_TARGET_NB_REGS - n + 1;
+}
+
+/* sort from highest priority to lowest */
+static void sort_constraints(TCGOpDef *def, int start, int n)
+{
+ int i, j, p1, p2, tmp;
+
+ for(i = 0; i < n; i++)
+ def->sorted_args[start + i] = start + i;
+ if (n <= 1)
+ return;
+ for(i = 0; i < n - 1; i++) {
+ for(j = i + 1; j < n; j++) {
+ p1 = get_constraint_priority(def, def->sorted_args[start + i]);
+ p2 = get_constraint_priority(def, def->sorted_args[start + j]);
+ if (p1 < p2) {
+ tmp = def->sorted_args[start + i];
+ def->sorted_args[start + i] = def->sorted_args[start + j];
+ def->sorted_args[start + j] = tmp;
+ }
+ }
+ }
+}
+
+void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs)
+{
+ TCGOpcode op;
+ TCGOpDef *def;
+ const char *ct_str;
+ int i, nb_args;
+
+ for(;;) {
+ if (tdefs->op == (TCGOpcode)-1)
+ break;
+ op = tdefs->op;
+ assert((unsigned)op < NB_OPS);
+ def = &tcg_op_defs[op];
+#if defined(CONFIG_DEBUG_TCG)
+ /* Duplicate entry in op definitions? */
+ assert(!def->used);
+ def->used = 1;
+#endif
+ nb_args = def->nb_iargs + def->nb_oargs;
+ for(i = 0; i < nb_args; i++) {
+ ct_str = tdefs->args_ct_str[i];
+ /* Incomplete TCGTargetOpDef entry? */
+ assert(ct_str != NULL);
+ tcg_regset_clear(def->args_ct[i].u.regs);
+ def->args_ct[i].ct = 0;
+ if (ct_str[0] >= '0' && ct_str[0] <= '9') {
+ int oarg;
+ oarg = ct_str[0] - '0';
+ assert(oarg < def->nb_oargs);
+ assert(def->args_ct[oarg].ct & TCG_CT_REG);
+ /* TCG_CT_ALIAS is for the output arguments. The input
+ argument is tagged with TCG_CT_IALIAS. */
+ def->args_ct[i] = def->args_ct[oarg];
+ def->args_ct[oarg].ct = TCG_CT_ALIAS;
+ def->args_ct[oarg].alias_index = i;
+ def->args_ct[i].ct |= TCG_CT_IALIAS;
+ def->args_ct[i].alias_index = oarg;
+ } else {
+ for(;;) {
+ if (*ct_str == '\0')
+ break;
+ switch(*ct_str) {
+ case 'i':
+ def->args_ct[i].ct |= TCG_CT_CONST;
+ ct_str++;
+ break;
+ default:
+ if (target_parse_constraint(&def->args_ct[i], &ct_str) < 0) {
+ fprintf(stderr, "Invalid constraint '%s' for arg %d of operation '%s'\n",
+ ct_str, i, def->name);
+ exit(1);
+ }
+ }
+ }
+ }
+ }
+
+ /* TCGTargetOpDef entry with too much information? */
+ assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL);
+
+ /* sort the constraints (XXX: this is just an heuristic) */
+ sort_constraints(def, 0, def->nb_oargs);
+ sort_constraints(def, def->nb_oargs, def->nb_iargs);
+
+#if 0
+ {
+ int i;
+
+ printf("%s: sorted=", def->name);
+ for(i = 0; i < def->nb_oargs + def->nb_iargs; i++)
+ printf(" %d", def->sorted_args[i]);
+ printf("\n");
+ }
+#endif
+ tdefs++;
+ }
+
+#if defined(CONFIG_DEBUG_TCG)
+ i = 0;
+ for (op = 0; op < ARRAY_SIZE(tcg_op_defs); op++) {
+ if (op < INDEX_op_call || op == INDEX_op_debug_insn_start) {
+ /* Wrong entry in op definitions? */
+ if (tcg_op_defs[op].used) {
+ fprintf(stderr, "Invalid op definition for %s\n",
+ tcg_op_defs[op].name);
+ i = 1;
+ }
+ } else {
+ /* Missing entry in op definitions? */
+ if (!tcg_op_defs[op].used) {
+ fprintf(stderr, "Missing op definition for %s\n",
+ tcg_op_defs[op].name);
+ i = 1;
+ }
+ }
+ }
+ if (i == 1) {
+ tcg_abort();
+ }
+#endif
+}
+
+#ifdef USE_LIVENESS_ANALYSIS
+
+/* set a nop for an operation using 'nb_args' */
+static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr,
+ TCGArg *args, int nb_args)
+{
+ if (nb_args == 0) {
+ *opc_ptr = INDEX_op_nop;
+ } else {
+ *opc_ptr = INDEX_op_nopn;
+ args[0] = nb_args;
+ args[nb_args - 1] = nb_args;
+ }
+}
+
+/* liveness analysis: end of function: globals are live, temps are
+ dead. */
+/* XXX: at this stage, not used as there would be little gains because
+ most TBs end with a conditional jump. */
+static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps)
+{
+ memset(dead_temps, 0, s->nb_globals);
+ memset(dead_temps + s->nb_globals, 1, s->nb_temps - s->nb_globals);
+}
+
+/* liveness analysis: end of basic block: globals are live, temps are
+ dead, local temps are live. */
+static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps)
+{
+ int i;
+ TCGTemp *ts;
+
+ memset(dead_temps, 0, s->nb_globals);
+ ts = &s->temps[s->nb_globals];
+ for(i = s->nb_globals; i < s->nb_temps; i++) {
+ if (ts->temp_local)
+ dead_temps[i] = 0;
+ else
+ dead_temps[i] = 1;
+ ts++;
+ }
+}
+
+/* Liveness analysis : update the opc_dead_iargs array to tell if a
+ given input arguments is dead. Instructions updating dead
+ temporaries are removed. */
+static void tcg_liveness_analysis(TCGContext *s)
+{
+ int i, op_index, nb_args, nb_iargs, nb_oargs, arg, nb_ops;
+ TCGOpcode op;
+ TCGArg *args;
+ const TCGOpDef *def;
+ uint8_t *dead_temps;
+ unsigned int dead_iargs;
+
+ gen_opc_ptr++; /* skip end */
+
+ nb_ops = gen_opc_ptr - gen_opc_buf;
+
+ s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t));
+
+ dead_temps = tcg_malloc(s->nb_temps);
+ memset(dead_temps, 1, s->nb_temps);
+
+ args = gen_opparam_ptr;
+ op_index = nb_ops - 1;
+ while (op_index >= 0) {
+ op = gen_opc_buf[op_index];
+ def = &tcg_op_defs[op];
+ switch(op) {
+ case INDEX_op_call:
+ {
+ int call_flags;
+
+ nb_args = args[-1];
+ args -= nb_args;
+ nb_iargs = args[0] & 0xffff;
+ nb_oargs = args[0] >> 16;
+ args++;
+ call_flags = args[nb_oargs + nb_iargs];
+
+ /* pure functions can be removed if their result is not
+ used */
+ if (call_flags & TCG_CALL_PURE) {
+ for(i = 0; i < nb_oargs; i++) {
+ arg = args[i];
+ if (!dead_temps[arg])
+ goto do_not_remove_call;
+ }
+ tcg_set_nop(s, gen_opc_buf + op_index,
+ args - 1, nb_args);
+ } else {
+ do_not_remove_call:
+
+ /* output args are dead */
+ for(i = 0; i < nb_oargs; i++) {
+ arg = args[i];
+ dead_temps[arg] = 1;
+ }
+
+ if (!(call_flags & TCG_CALL_CONST)) {
+ /* globals are live (they may be used by the call) */
+ memset(dead_temps, 0, s->nb_globals);
+ }
+
+ /* input args are live */
+ dead_iargs = 0;
+ for(i = 0; i < nb_iargs; i++) {
+ arg = args[i + nb_oargs];
+ if (arg != TCG_CALL_DUMMY_ARG) {
+ if (dead_temps[arg]) {
+ dead_iargs |= (1 << i);
+ }
+ dead_temps[arg] = 0;
+ }
+ }
+ s->op_dead_iargs[op_index] = dead_iargs;
+ }
+ args--;
+ }
+ break;
+ case INDEX_op_set_label:
+ args--;
+ /* mark end of basic block */
+ tcg_la_bb_end(s, dead_temps);
+ break;
+ case INDEX_op_debug_insn_start:
+ args -= def->nb_args;
+ break;
+ case INDEX_op_nopn:
+ nb_args = args[-1];
+ args -= nb_args;
+ break;
+ case INDEX_op_discard:
+ args--;
+ /* mark the temporary as dead */
+ dead_temps[args[0]] = 1;
+ break;
+ case INDEX_op_end:
+ break;
+ /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */
+ default:
+ args -= def->nb_args;
+ nb_iargs = def->nb_iargs;
+ nb_oargs = def->nb_oargs;
+
+ /* Test if the operation can be removed because all
+ its outputs are dead. We assume that nb_oargs == 0
+ implies side effects */
+ if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) {
+ for(i = 0; i < nb_oargs; i++) {
+ arg = args[i];
+ if (!dead_temps[arg])
+ goto do_not_remove;
+ }
+ tcg_set_nop(s, gen_opc_buf + op_index, args, def->nb_args);
+#ifdef CONFIG_PROFILER
+ s->del_op_count++;
+#endif
+ } else {
+ do_not_remove:
+
+ /* output args are dead */
+ for(i = 0; i < nb_oargs; i++) {
+ arg = args[i];
+ dead_temps[arg] = 1;
+ }
+
+ /* if end of basic block, update */
+ if (def->flags & TCG_OPF_BB_END) {
+ tcg_la_bb_end(s, dead_temps);
+ } else if (def->flags & TCG_OPF_CALL_CLOBBER) {
+ /* globals are live */
+ memset(dead_temps, 0, s->nb_globals);
+ }
+
+ /* input args are live */
+ dead_iargs = 0;
+ for(i = 0; i < nb_iargs; i++) {
+ arg = args[i + nb_oargs];
+ if (dead_temps[arg]) {
+ dead_iargs |= (1 << i);
+ }
+ dead_temps[arg] = 0;
+ }
+ s->op_dead_iargs[op_index] = dead_iargs;
+ }
+ break;
+ }
+ op_index--;
+ }
+
+ if (args != gen_opparam_buf)
+ tcg_abort();
+}
+#else
+/* dummy liveness analysis */
+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));
+}
+#endif
+
+#ifndef NDEBUG
+static void dump_regs(TCGContext *s)
+{
+ TCGTemp *ts;
+ int i;
+ char buf[64];
+
+ for(i = 0; i < s->nb_temps; i++) {
+ ts = &s->temps[i];
+ printf(" %10s: ", tcg_get_arg_str_idx(s, buf, sizeof(buf), i));
+ switch(ts->val_type) {
+ case TEMP_VAL_REG:
+ printf("%s", tcg_target_reg_names[ts->reg]);
+ break;
+ case TEMP_VAL_MEM:
+ printf("%d(%s)", (int)ts->mem_offset, tcg_target_reg_names[ts->mem_reg]);
+ break;
+ case TEMP_VAL_CONST:
+ printf("$0x%" TCG_PRIlx, ts->val);
+ break;
+ case TEMP_VAL_DEAD:
+ printf("D");
+ break;
+ default:
+ printf("???");
+ break;
+ }
+ printf("\n");
+ }
+
+ for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
+ if (s->reg_to_temp[i] >= 0) {
+ printf("%s: %s\n",
+ tcg_target_reg_names[i],
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i]));
+ }
+ }
+}
+
+static void check_regs(TCGContext *s)
+{
+ int reg, k;
+ TCGTemp *ts;
+ char buf[64];
+
+ for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
+ k = s->reg_to_temp[reg];
+ if (k >= 0) {
+ ts = &s->temps[k];
+ if (ts->val_type != TEMP_VAL_REG ||
+ ts->reg != reg) {
+ printf("Inconsistency for register %s:\n",
+ tcg_target_reg_names[reg]);
+ goto fail;
+ }
+ }
+ }
+ for(k = 0; k < s->nb_temps; k++) {
+ ts = &s->temps[k];
+ if (ts->val_type == TEMP_VAL_REG &&
+ !ts->fixed_reg &&
+ s->reg_to_temp[ts->reg] != k) {
+ printf("Inconsistency for temp %s:\n",
+ tcg_get_arg_str_idx(s, buf, sizeof(buf), k));
+ fail:
+ printf("reg state:\n");
+ dump_regs(s);
+ tcg_abort();
+ }
+ }
+}
+#endif
+
+static void temp_allocate_frame(TCGContext *s, int temp)
+{
+ TCGTemp *ts;
+ ts = &s->temps[temp];
+ s->current_frame_offset = (s->current_frame_offset + sizeof(tcg_target_long) - 1) & ~(sizeof(tcg_target_long) - 1);
+ if (s->current_frame_offset + sizeof(tcg_target_long) > s->frame_end)
+ tcg_abort();
+ ts->mem_offset = s->current_frame_offset;
+ ts->mem_reg = s->frame_reg;
+ ts->mem_allocated = 1;
+ s->current_frame_offset += sizeof(tcg_target_long);
+}
+
+/* free register 'reg' by spilling the corresponding temporary if necessary */
+static void tcg_reg_free(TCGContext *s, int reg)
+{
+ TCGTemp *ts;
+ int temp;
+
+ temp = s->reg_to_temp[reg];
+ if (temp != -1) {
+ ts = &s->temps[temp];
+ assert(ts->val_type == TEMP_VAL_REG);
+ if (!ts->mem_coherent) {
+ if (!ts->mem_allocated)
+ temp_allocate_frame(s, temp);
+ tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ }
+ ts->val_type = TEMP_VAL_MEM;
+ s->reg_to_temp[reg] = -1;
+ }
+}
+
+/* Allocate a register belonging to reg1 & ~reg2 */
+static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2)
+{
+ int i, reg;
+ TCGRegSet reg_ct;
+
+ tcg_regset_andnot(reg_ct, reg1, reg2);
+
+ /* first try free registers */
+ for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) {
+ reg = tcg_target_reg_alloc_order[i];
+ if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == -1)
+ return reg;
+ }
+
+ /* XXX: do better spill choice */
+ for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) {
+ reg = tcg_target_reg_alloc_order[i];
+ if (tcg_regset_test_reg(reg_ct, reg)) {
+ tcg_reg_free(s, reg);
+ return reg;
+ }
+ }
+
+ tcg_abort();
+}
+
+/* save a temporary to memory. 'allocated_regs' is used in case a
+ temporary registers needs to be allocated to store a constant. */
+static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs)
+{
+ TCGTemp *ts;
+ int reg;
+
+ ts = &s->temps[temp];
+ if (!ts->fixed_reg) {
+ switch(ts->val_type) {
+ case TEMP_VAL_REG:
+ tcg_reg_free(s, ts->reg);
+ break;
+ case TEMP_VAL_DEAD:
+ ts->val_type = TEMP_VAL_MEM;
+ break;
+ case TEMP_VAL_CONST:
+ reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
+ allocated_regs);
+ if (!ts->mem_allocated)
+ temp_allocate_frame(s, temp);
+ tcg_out_movi(s, ts->type, reg, ts->val);
+ tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ ts->val_type = TEMP_VAL_MEM;
+ break;
+ case TEMP_VAL_MEM:
+ break;
+ default:
+ tcg_abort();
+ }
+ }
+}
+
+/* save globals to their cannonical location and assume they can be
+ modified be the following code. 'allocated_regs' is used in case a
+ temporary registers needs to be allocated to store a constant. */
+static void save_globals(TCGContext *s, TCGRegSet allocated_regs)
+{
+ int i;
+
+ for(i = 0; i < s->nb_globals; i++) {
+ temp_save(s, i, allocated_regs);
+ }
+}
+
+/* at the end of a basic block, we assume all temporaries are dead and
+ all globals are stored at their canonical location. */
+static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
+{
+ TCGTemp *ts;
+ int i;
+
+ for(i = s->nb_globals; i < s->nb_temps; i++) {
+ ts = &s->temps[i];
+ if (ts->temp_local) {
+ temp_save(s, i, allocated_regs);
+ } else {
+ if (ts->val_type == TEMP_VAL_REG) {
+ s->reg_to_temp[ts->reg] = -1;
+ }
+ ts->val_type = TEMP_VAL_DEAD;
+ }
+ }
+
+ save_globals(s, allocated_regs);
+}
+
+#define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1)
+
+static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
+{
+ TCGTemp *ots;
+ tcg_target_ulong val;
+
+ ots = &s->temps[args[0]];
+ val = args[1];
+
+ if (ots->fixed_reg) {
+ /* for fixed registers, we do not do any constant
+ propagation */
+ tcg_out_movi(s, ots->type, ots->reg, val);
+ } else {
+ /* The movi is not explicitly generated here */
+ if (ots->val_type == TEMP_VAL_REG)
+ s->reg_to_temp[ots->reg] = -1;
+ ots->val_type = TEMP_VAL_CONST;
+ ots->val = val;
+ }
+}
+
+static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
+ const TCGArg *args,
+ unsigned int dead_iargs)
+{
+ TCGTemp *ts, *ots;
+ int reg;
+ const TCGArgConstraint *arg_ct;
+
+ ots = &s->temps[args[0]];
+ ts = &s->temps[args[1]];
+ arg_ct = &def->args_ct[0];
+
+ /* XXX: always mark arg dead if IS_DEAD_IARG(0) */
+ if (ts->val_type == TEMP_VAL_REG) {
+ if (IS_DEAD_IARG(0) && !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;
+ reg = ts->reg;
+ s->reg_to_temp[reg] = -1;
+ ts->val_type = TEMP_VAL_DEAD;
+ } else {
+ if (ots->val_type == TEMP_VAL_REG) {
+ reg = ots->reg;
+ } else {
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
+ }
+ if (ts->reg != reg) {
+ tcg_out_mov(s, ots->type, reg, ts->reg);
+ }
+ }
+ } else if (ts->val_type == TEMP_VAL_MEM) {
+ if (ots->val_type == TEMP_VAL_REG) {
+ reg = ots->reg;
+ } else {
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
+ }
+ tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ if (ots->fixed_reg) {
+ reg = ots->reg;
+ tcg_out_movi(s, ots->type, reg, ts->val);
+ } else {
+ /* propagate constant */
+ if (ots->val_type == TEMP_VAL_REG)
+ s->reg_to_temp[ots->reg] = -1;
+ ots->val_type = TEMP_VAL_CONST;
+ ots->val = ts->val;
+ return;
+ }
+ } else {
+ tcg_abort();
+ }
+ s->reg_to_temp[reg] = args[0];
+ ots->reg = reg;
+ ots->val_type = TEMP_VAL_REG;
+ ots->mem_coherent = 0;
+}
+
+static void tcg_reg_alloc_op(TCGContext *s,
+ const TCGOpDef *def, TCGOpcode opc,
+ const TCGArg *args,
+ unsigned int dead_iargs)
+{
+ TCGRegSet allocated_regs;
+ int i, k, nb_iargs, nb_oargs, reg;
+ TCGArg arg;
+ const TCGArgConstraint *arg_ct;
+ TCGTemp *ts;
+ TCGArg new_args[TCG_MAX_OP_ARGS];
+ int const_args[TCG_MAX_OP_ARGS];
+
+ nb_oargs = def->nb_oargs;
+ nb_iargs = def->nb_iargs;
+
+ /* copy constants */
+ memcpy(new_args + nb_oargs + nb_iargs,
+ args + nb_oargs + nb_iargs,
+ sizeof(TCGArg) * def->nb_cargs);
+
+ /* satisfy input constraints */
+ tcg_regset_set(allocated_regs, s->reserved_regs);
+ for(k = 0; k < nb_iargs; k++) {
+ i = def->sorted_args[nb_oargs + k];
+ arg = args[i];
+ arg_ct = &def->args_ct[i];
+ ts = &s->temps[arg];
+ if (ts->val_type == TEMP_VAL_MEM) {
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ ts->val_type = TEMP_VAL_REG;
+ ts->reg = reg;
+ ts->mem_coherent = 1;
+ s->reg_to_temp[reg] = arg;
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ if (tcg_target_const_match(ts->val, arg_ct)) {
+ /* constant is OK for instruction */
+ const_args[i] = 1;
+ new_args[i] = ts->val;
+ goto iarg_end;
+ } else {
+ /* need to move to a register */
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ tcg_out_movi(s, ts->type, reg, ts->val);
+ ts->val_type = TEMP_VAL_REG;
+ ts->reg = reg;
+ ts->mem_coherent = 0;
+ s->reg_to_temp[reg] = arg;
+ }
+ }
+ assert(ts->val_type == TEMP_VAL_REG);
+ if (arg_ct->ct & TCG_CT_IALIAS) {
+ if (ts->fixed_reg) {
+ /* if fixed register, we must allocate a new register
+ if the alias is not the same register */
+ if (arg != args[arg_ct->alias_index])
+ goto allocate_in_reg;
+ } else {
+ /* 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))
+ goto allocate_in_reg;
+ }
+ }
+ reg = ts->reg;
+ if (tcg_regset_test_reg(arg_ct->u.regs, reg)) {
+ /* nothing to do : the constraint is satisfied */
+ } else {
+ allocate_in_reg:
+ /* allocate a new register matching the constraint
+ and move the temporary register into it */
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ tcg_out_mov(s, ts->type, reg, ts->reg);
+ }
+ new_args[i] = reg;
+ const_args[i] = 0;
+ tcg_regset_set_reg(allocated_regs, reg);
+ iarg_end: ;
+ }
+
+ if (def->flags & TCG_OPF_BB_END) {
+ 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)) {
+ ts = &s->temps[arg];
+ if (!ts->fixed_reg) {
+ if (ts->val_type == TEMP_VAL_REG)
+ s->reg_to_temp[ts->reg] = -1;
+ ts->val_type = TEMP_VAL_DEAD;
+ }
+ }
+ }
+
+ if (def->flags & TCG_OPF_CALL_CLOBBER) {
+ /* XXX: permit generic clobber register list ? */
+ for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
+ if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) {
+ tcg_reg_free(s, reg);
+ }
+ }
+ /* XXX: for load/store we could do that only for the slow path
+ (i.e. when a memory callback is called) */
+
+ /* store globals and free associated registers (we assume the insn
+ can modify any global. */
+ save_globals(s, allocated_regs);
+ }
+
+ /* satisfy the output constraints */
+ tcg_regset_set(allocated_regs, s->reserved_regs);
+ for(k = 0; k < nb_oargs; k++) {
+ i = def->sorted_args[k];
+ arg = args[i];
+ arg_ct = &def->args_ct[i];
+ ts = &s->temps[arg];
+ if (arg_ct->ct & TCG_CT_ALIAS) {
+ reg = new_args[arg_ct->alias_index];
+ } else {
+ /* if fixed register, we try to use it */
+ reg = ts->reg;
+ if (ts->fixed_reg &&
+ tcg_regset_test_reg(arg_ct->u.regs, reg)) {
+ goto oarg_end;
+ }
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ }
+ tcg_regset_set_reg(allocated_regs, reg);
+ /* if a fixed register is used, then a move will be done afterwards */
+ 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;
+ }
+ oarg_end:
+ new_args[i] = reg;
+ }
+ }
+
+ /* emit instruction */
+ tcg_out_op(s, opc, new_args, const_args);
+
+ /* move the outputs in the correct register if needed */
+ for(i = 0; i < nb_oargs; i++) {
+ ts = &s->temps[args[i]];
+ reg = new_args[i];
+ if (ts->fixed_reg && ts->reg != reg) {
+ tcg_out_mov(s, ts->type, ts->reg, reg);
+ }
+ }
+}
+
+#ifdef TCG_TARGET_STACK_GROWSUP
+#define STACK_DIR(x) (-(x))
+#else
+#define STACK_DIR(x) (x)
+#endif
+
+static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
+ TCGOpcode opc, const TCGArg *args,
+ unsigned int dead_iargs)
+{
+ int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params;
+ TCGArg arg, func_arg;
+ TCGTemp *ts;
+ tcg_target_long stack_offset, call_stack_size, func_addr;
+ int const_func_arg, allocate_args;
+ TCGRegSet allocated_regs;
+ const TCGArgConstraint *arg_ct;
+
+ arg = *args++;
+
+ nb_oargs = arg >> 16;
+ nb_iargs = arg & 0xffff;
+ nb_params = nb_iargs - 1;
+
+ flags = args[nb_oargs + nb_iargs];
+
+ nb_regs = tcg_target_get_call_iarg_regs_count(flags);
+ if (nb_regs > nb_params)
+ nb_regs = nb_params;
+
+ /* assign stack slots first */
+ /* XXX: preallocate call stack */
+ call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long);
+ call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) &
+ ~(TCG_TARGET_STACK_ALIGN - 1);
+ allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE);
+ if (allocate_args) {
+ tcg_out_addi(s, TCG_REG_CALL_STACK, -STACK_DIR(call_stack_size));
+ }
+
+ stack_offset = TCG_TARGET_CALL_STACK_OFFSET;
+ for(i = nb_regs; i < nb_params; i++) {
+ arg = args[nb_oargs + i];
+#ifdef TCG_TARGET_STACK_GROWSUP
+ stack_offset -= sizeof(tcg_target_long);
+#endif
+ if (arg != TCG_CALL_DUMMY_ARG) {
+ ts = &s->temps[arg];
+ if (ts->val_type == TEMP_VAL_REG) {
+ tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset);
+ } else if (ts->val_type == TEMP_VAL_MEM) {
+ reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
+ s->reserved_regs);
+ /* XXX: not correct if reading values from the stack */
+ tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset);
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
+ s->reserved_regs);
+ /* XXX: sign extend may be needed on some targets */
+ tcg_out_movi(s, ts->type, reg, ts->val);
+ tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset);
+ } else {
+ tcg_abort();
+ }
+ }
+#ifndef TCG_TARGET_STACK_GROWSUP
+ stack_offset += sizeof(tcg_target_long);
+#endif
+ }
+
+ /* assign input registers */
+ tcg_regset_set(allocated_regs, s->reserved_regs);
+ for(i = 0; i < nb_regs; i++) {
+ arg = args[nb_oargs + i];
+ if (arg != TCG_CALL_DUMMY_ARG) {
+ ts = &s->temps[arg];
+ reg = tcg_target_call_iarg_regs[i];
+ tcg_reg_free(s, reg);
+ if (ts->val_type == TEMP_VAL_REG) {
+ if (ts->reg != reg) {
+ tcg_out_mov(s, ts->type, reg, ts->reg);
+ }
+ } else if (ts->val_type == TEMP_VAL_MEM) {
+ tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ /* XXX: sign extend ? */
+ tcg_out_movi(s, ts->type, reg, ts->val);
+ } else {
+ tcg_abort();
+ }
+ tcg_regset_set_reg(allocated_regs, reg);
+ }
+ }
+
+ /* assign function address */
+ func_arg = args[nb_oargs + nb_iargs - 1];
+ arg_ct = &def->args_ct[0];
+ ts = &s->temps[func_arg];
+ func_addr = ts->val;
+ const_func_arg = 0;
+ if (ts->val_type == TEMP_VAL_MEM) {
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+ func_arg = reg;
+ tcg_regset_set_reg(allocated_regs, reg);
+ } else if (ts->val_type == TEMP_VAL_REG) {
+ reg = ts->reg;
+ if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) {
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ tcg_out_mov(s, ts->type, reg, ts->reg);
+ }
+ func_arg = reg;
+ tcg_regset_set_reg(allocated_regs, reg);
+ } else if (ts->val_type == TEMP_VAL_CONST) {
+ if (tcg_target_const_match(func_addr, arg_ct)) {
+ const_func_arg = 1;
+ func_arg = func_addr;
+ } else {
+ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+ tcg_out_movi(s, ts->type, reg, func_addr);
+ func_arg = reg;
+ tcg_regset_set_reg(allocated_regs, reg);
+ }
+ } else {
+ tcg_abort();
+ }
+
+
+ /* 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)) {
+ ts = &s->temps[arg];
+ if (!ts->fixed_reg) {
+ if (ts->val_type == TEMP_VAL_REG)
+ s->reg_to_temp[ts->reg] = -1;
+ ts->val_type = TEMP_VAL_DEAD;
+ }
+ }
+ }
+
+ /* clobber call registers */
+ for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
+ if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) {
+ tcg_reg_free(s, reg);
+ }
+ }
+
+ /* store globals and free associated registers (we assume the call
+ can modify any global. */
+ if (!(flags & TCG_CALL_CONST)) {
+ save_globals(s, allocated_regs);
+ }
+
+ tcg_out_op(s, opc, &func_arg, &const_func_arg);
+
+ if (allocate_args) {
+ tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size));
+ }
+
+ /* assign output registers and emit moves if needed */
+ for(i = 0; i < nb_oargs; i++) {
+ arg = args[i];
+ ts = &s->temps[arg];
+ reg = tcg_target_call_oarg_regs[i];
+ assert(s->reg_to_temp[reg] == -1);
+ if (ts->fixed_reg) {
+ if (ts->reg != reg) {
+ tcg_out_mov(s, ts->type, ts->reg, reg);
+ }
+ } 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;
+ }
+ }
+
+ return nb_iargs + nb_oargs + def->nb_cargs + 1;
+}
+
+#ifdef CONFIG_PROFILER
+
+static int64_t tcg_table_op_count[NB_OPS];
+
+static void dump_op_count(void)
+{
+ int i;
+ FILE *f;
+ f = fopen("/tmp/op.log", "w");
+ for(i = INDEX_op_end; i < NB_OPS; i++) {
+ fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, tcg_table_op_count[i]);
+ }
+ fclose(f);
+}
+#endif
+
+
+static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
+ long search_pc)
+{
+ TCGOpcode opc;
+ int op_index;
+ const TCGOpDef *def;
+ unsigned int dead_iargs;
+ const TCGArg *args;
+
+#ifdef DEBUG_DISAS
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) {
+ qemu_log("OP:\n");
+ tcg_dump_ops(s, logfile);
+ qemu_log("\n");
+ }
+#endif
+
+#ifdef CONFIG_PROFILER
+ s->la_time -= profile_getclock();
+#endif
+ tcg_liveness_analysis(s);
+#ifdef CONFIG_PROFILER
+ s->la_time += profile_getclock();
+#endif
+
+#ifdef DEBUG_DISAS
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT))) {
+ qemu_log("OP after liveness analysis:\n");
+ tcg_dump_ops(s, logfile);
+ qemu_log("\n");
+ }
+#endif
+
+ tcg_reg_alloc_start(s);
+
+ s->code_buf = gen_code_buf;
+ s->code_ptr = gen_code_buf;
+
+ args = gen_opparam_buf;
+ op_index = 0;
+
+ for(;;) {
+ opc = gen_opc_buf[op_index];
+#ifdef CONFIG_PROFILER
+ tcg_table_op_count[opc]++;
+#endif
+ def = &tcg_op_defs[opc];
+#if 0
+ printf("%s: %d %d %d\n", def->name,
+ def->nb_oargs, def->nb_iargs, def->nb_cargs);
+ // dump_regs(s);
+#endif
+ switch(opc) {
+ case INDEX_op_mov_i32:
+#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);
+ break;
+ case INDEX_op_movi_i32:
+#if TCG_TARGET_REG_BITS == 64
+ case INDEX_op_movi_i64:
+#endif
+ tcg_reg_alloc_movi(s, args);
+ break;
+ case INDEX_op_debug_insn_start:
+ /* debug instruction */
+ break;
+ case INDEX_op_nop:
+ case INDEX_op_nop1:
+ case INDEX_op_nop2:
+ case INDEX_op_nop3:
+ break;
+ case INDEX_op_nopn:
+ args += args[0];
+ goto next;
+ case INDEX_op_discard:
+ {
+ TCGTemp *ts;
+ ts = &s->temps[args[0]];
+ /* mark the temporary as dead */
+ if (!ts->fixed_reg) {
+ if (ts->val_type == TEMP_VAL_REG)
+ s->reg_to_temp[ts->reg] = -1;
+ ts->val_type = TEMP_VAL_DEAD;
+ }
+ }
+ break;
+ case INDEX_op_set_label:
+ tcg_reg_alloc_bb_end(s, s->reserved_regs);
+ 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);
+ goto next;
+ case INDEX_op_end:
+ goto the_end;
+ default:
+ /* 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);
+ break;
+ }
+ args += def->nb_args;
+ next:
+ if (search_pc >= 0 && search_pc < s->code_ptr - gen_code_buf) {
+ return op_index;
+ }
+ op_index++;
+#ifndef NDEBUG
+ check_regs(s);
+#endif
+ }
+ the_end:
+ return -1;
+}
+
+int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf)
+{
+#ifdef CONFIG_PROFILER
+ {
+ int n;
+ n = (gen_opc_ptr - gen_opc_buf);
+ s->op_count += n;
+ if (n > s->op_count_max)
+ s->op_count_max = n;
+
+ s->temp_count += s->nb_temps;
+ if (s->nb_temps > s->temp_count_max)
+ s->temp_count_max = s->nb_temps;
+ }
+#endif
+
+ tcg_gen_code_common(s, gen_code_buf, -1);
+
+ /* flush instruction cache */
+ flush_icache_range((unsigned long)gen_code_buf,
+ (unsigned long)s->code_ptr);
+ return s->code_ptr - gen_code_buf;
+}
+
+/* Return the index of the micro operation such as the pc after is <
+ offset bytes from the start of the TB. The contents of gen_code_buf must
+ not be changed, though writing the same values is ok.
+ Return -1 if not found. */
+int tcg_gen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset)
+{
+ return tcg_gen_code_common(s, gen_code_buf, offset);
+}
+
+#ifdef CONFIG_PROFILER
+void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf)
+{
+ TCGContext *s = &tcg_ctx;
+ int64_t tot;
+
+ tot = s->interm_time + s->code_time;
+ cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n",
+ tot, tot / 2.4e9);
+ cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n",
+ s->tb_count,
+ s->tb_count1 - s->tb_count,
+ s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0);
+ cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n",
+ s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max);
+ cpu_fprintf(f, "deleted ops/TB %0.2f\n",
+ s->tb_count ?
+ (double)s->del_op_count / s->tb_count : 0);
+ cpu_fprintf(f, "avg temps/TB %0.2f max=%d\n",
+ s->tb_count ?
+ (double)s->temp_count / s->tb_count : 0,
+ s->temp_count_max);
+
+ cpu_fprintf(f, "cycles/op %0.1f\n",
+ s->op_count ? (double)tot / s->op_count : 0);
+ cpu_fprintf(f, "cycles/in byte %0.1f\n",
+ s->code_in_len ? (double)tot / s->code_in_len : 0);
+ cpu_fprintf(f, "cycles/out byte %0.1f\n",
+ s->code_out_len ? (double)tot / s->code_out_len : 0);
+ if (tot == 0)
+ tot = 1;
+ cpu_fprintf(f, " gen_interm time %0.1f%%\n",
+ (double)s->interm_time / tot * 100.0);
+ cpu_fprintf(f, " gen_code time %0.1f%%\n",
+ (double)s->code_time / tot * 100.0);
+ cpu_fprintf(f, "liveness/code time %0.1f%%\n",
+ (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0);
+ cpu_fprintf(f, "cpu_restore count %" PRId64 "\n",
+ s->restore_count);
+ cpu_fprintf(f, " avg cycles %0.1f\n",
+ s->restore_count ? (double)s->restore_time / s->restore_count : 0);
+
+ dump_op_count();
+}
+#else
+void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf)
+{
+ cpu_fprintf(f, "[TCG profiler not compiled]\n");
+}
+#endif
diff --git a/tcg/tcg.h b/tcg/tcg.h
new file mode 100644
index 0000000..e1afde2
--- /dev/null
+++ b/tcg/tcg.h
@@ -0,0 +1,489 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "tcg-target.h"
+#include "tcg-runtime.h"
+
+#if TCG_TARGET_REG_BITS == 32
+typedef int32_t tcg_target_long;
+typedef uint32_t tcg_target_ulong;
+#define TCG_PRIlx PRIx32
+#define TCG_PRIld PRId32
+#elif TCG_TARGET_REG_BITS == 64
+typedef int64_t tcg_target_long;
+typedef uint64_t tcg_target_ulong;
+#define TCG_PRIlx PRIx64
+#define TCG_PRIld PRId64
+#else
+#error unsupported
+#endif
+
+#if TCG_TARGET_NB_REGS <= 32
+typedef uint32_t TCGRegSet;
+#elif TCG_TARGET_NB_REGS <= 64
+typedef uint64_t TCGRegSet;
+#else
+#error unsupported
+#endif
+
+typedef enum TCGOpcode {
+#define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name,
+#include "tcg-opc.h"
+#undef DEF
+ NB_OPS,
+} TCGOpcode;
+
+#define tcg_regset_clear(d) (d) = 0
+#define tcg_regset_set(d, s) (d) = (s)
+#define tcg_regset_set32(d, reg, val32) (d) |= (val32) << (reg)
+#define tcg_regset_set_reg(d, r) (d) |= 1L << (r)
+#define tcg_regset_reset_reg(d, r) (d) &= ~(1L << (r))
+#define tcg_regset_test_reg(d, r) (((d) >> (r)) & 1)
+#define tcg_regset_or(d, a, b) (d) = (a) | (b)
+#define tcg_regset_and(d, a, b) (d) = (a) & (b)
+#define tcg_regset_andnot(d, a, b) (d) = (a) & ~(b)
+#define tcg_regset_not(d, a) (d) = ~(a)
+
+typedef struct TCGRelocation {
+ struct TCGRelocation *next;
+ int type;
+ uint8_t *ptr;
+ tcg_target_long addend;
+} TCGRelocation;
+
+typedef struct TCGLabel {
+ int has_value;
+ union {
+ tcg_target_ulong value;
+ TCGRelocation *first_reloc;
+ } u;
+} TCGLabel;
+
+typedef struct TCGPool {
+ struct TCGPool *next;
+ int size;
+ uint8_t data[0] __attribute__ ((aligned));
+} TCGPool;
+
+#define TCG_POOL_CHUNK_SIZE 32768
+
+#define TCG_MAX_LABELS 512
+
+#define TCG_MAX_TEMPS 512
+
+/* when the size of the arguments of a called function is smaller than
+ this value, they are statically allocated in the TB stack frame */
+#define TCG_STATIC_CALL_ARGS_SIZE 128
+
+typedef enum TCGType {
+ TCG_TYPE_I32,
+ TCG_TYPE_I64,
+ TCG_TYPE_COUNT, /* number of different types */
+
+ /* An alias for the size of the host register. */
+#if TCG_TARGET_REG_BITS == 32
+ TCG_TYPE_REG = TCG_TYPE_I32,
+#else
+ TCG_TYPE_REG = TCG_TYPE_I64,
+#endif
+
+ /* An alias for the size of the native pointer. We don't currently
+ support any hosts with 64-bit registers and 32-bit pointers. */
+ TCG_TYPE_PTR = TCG_TYPE_REG,
+
+ /* An alias for the size of the target "long", aka register. */
+#if TARGET_LONG_BITS == 64
+ TCG_TYPE_TL = TCG_TYPE_I64,
+#else
+ TCG_TYPE_TL = TCG_TYPE_I32,
+#endif
+} TCGType;
+
+typedef tcg_target_ulong TCGArg;
+
+/* Define a type and accessor macros for varables. Using a struct is
+ nice because it gives some level of type safely. Ideally the compiler
+ be able to see through all this. However in practice this is not true,
+ expecially on targets with braindamaged ABIs (e.g. i386).
+ 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
+ and TCGv_i64 are 32/64-bit variables respectively. TCGv and TCGv_ptr
+ are aliases for target_ulong and host pointer sized values respectively.
+ */
+
+#ifdef CONFIG_DEBUG_TCG
+#define DEBUG_TCGV 1
+#endif
+
+#ifdef DEBUG_TCGV
+
+typedef struct
+{
+ int i32;
+} TCGv_i32;
+
+typedef struct
+{
+ int i64;
+} TCGv_i64;
+
+#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 GET_TCGV_I32(t) ((t).i32)
+#define GET_TCGV_I64(t) ((t).i64)
+#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)
+#endif
+
+#else /* !DEBUG_TCGV */
+
+typedef int TCGv_i32;
+typedef int TCGv_i64;
+#define MAKE_TCGV_I32(x) (x)
+#define MAKE_TCGV_I64(x) (x)
+#define GET_TCGV_I32(t) (t)
+#define GET_TCGV_I64(t) (t)
+
+#if TCG_TARGET_REG_BITS == 32
+#define TCGV_LOW(t) (t)
+#define TCGV_HIGH(t) ((t) + 1)
+#endif
+
+#endif /* DEBUG_TCGV */
+
+#define TCGV_EQUAL_I32(a, b) (GET_TCGV_I32(a) == GET_TCGV_I32(b))
+#define TCGV_EQUAL_I64(a, b) (GET_TCGV_I64(a) == GET_TCGV_I64(b))
+
+/* Dummy definition to avoid compiler warnings. */
+#define TCGV_UNUSED_I32(x) x = MAKE_TCGV_I32(-1)
+#define TCGV_UNUSED_I64(x) x = MAKE_TCGV_I64(-1)
+
+/* call flags */
+#define TCG_CALL_TYPE_MASK 0x000f
+#define TCG_CALL_TYPE_STD 0x0000 /* standard C call */
+#define TCG_CALL_TYPE_REGPARM_1 0x0001 /* i386 style regparm call (1 reg) */
+#define TCG_CALL_TYPE_REGPARM_2 0x0002 /* i386 style regparm call (2 regs) */
+#define TCG_CALL_TYPE_REGPARM 0x0003 /* i386 style regparm call (3 regs) */
+/* A pure function only reads its arguments and TCG global variables
+ and cannot raise exceptions. Hence a call to a pure function can be
+ safely suppressed if the return value is not used. */
+#define TCG_CALL_PURE 0x0010
+/* A const function only reads its arguments and does not use TCG
+ global variables. Hence a call to such a function does not
+ save TCG global variables back to their canonical location. */
+#define TCG_CALL_CONST 0x0020
+
+/* used to align parameters */
+#define TCG_CALL_DUMMY_TCGV MAKE_TCGV_I32(-1)
+#define TCG_CALL_DUMMY_ARG ((TCGArg)(-1))
+
+typedef enum {
+ TCG_COND_EQ,
+ TCG_COND_NE,
+ TCG_COND_LT,
+ TCG_COND_GE,
+ TCG_COND_LE,
+ TCG_COND_GT,
+ /* unsigned */
+ TCG_COND_LTU,
+ TCG_COND_GEU,
+ TCG_COND_LEU,
+ TCG_COND_GTU,
+} TCGCond;
+
+/* Invert the sense of the comparison. */
+static inline TCGCond tcg_invert_cond(TCGCond c)
+{
+ return (TCGCond)(c ^ 1);
+}
+
+/* Swap the operands in a comparison. */
+static inline TCGCond tcg_swap_cond(TCGCond c)
+{
+ int mask = (c < TCG_COND_LT ? 0 : c < TCG_COND_LTU ? 7 : 15);
+ return (TCGCond)(c ^ mask);
+}
+
+static inline TCGCond tcg_unsigned_cond(TCGCond c)
+{
+ return (c >= TCG_COND_LT && c <= TCG_COND_GT ? c + 4 : c);
+}
+
+#define TEMP_VAL_DEAD 0
+#define TEMP_VAL_REG 1
+#define TEMP_VAL_MEM 2
+#define TEMP_VAL_CONST 3
+
+/* XXX: optimize memory layout */
+typedef struct TCGTemp {
+ TCGType base_type;
+ TCGType type;
+ int val_type;
+ int reg;
+ tcg_target_long val;
+ int mem_reg;
+ tcg_target_long mem_offset;
+ unsigned int fixed_reg:1;
+ unsigned int mem_coherent:1;
+ unsigned int mem_allocated:1;
+ unsigned int temp_local:1; /* If true, the temp is saved accross
+ basic blocks. Otherwise, it is not
+ preserved accross basic blocks. */
+ unsigned int temp_allocated:1; /* never used for code gen */
+ /* index of next free temp of same base type, -1 if end */
+ int next_free_temp;
+ const char *name;
+} TCGTemp;
+
+typedef struct TCGHelperInfo {
+ tcg_target_ulong func;
+ const char *name;
+} TCGHelperInfo;
+
+typedef struct TCGContext TCGContext;
+
+struct TCGContext {
+ uint8_t *pool_cur, *pool_end;
+ TCGPool *pool_first, *pool_current;
+ TCGLabel *labels;
+ int nb_labels;
+ TCGTemp *temps; /* globals first, temps after */
+ int nb_globals;
+ int nb_temps;
+ /* index of free temps, -1 if none */
+ int first_free_temp[TCG_TYPE_COUNT * 2];
+
+ /* goto_tb support */
+ uint8_t *code_buf;
+ unsigned long *tb_next;
+ uint16_t *tb_next_offset;
+ 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 */
+
+ /* tells in which temporary a given register is. It does not take
+ into account fixed registers */
+ int reg_to_temp[TCG_TARGET_NB_REGS];
+ TCGRegSet reserved_regs;
+ tcg_target_long current_frame_offset;
+ tcg_target_long frame_start;
+ tcg_target_long frame_end;
+ int frame_reg;
+
+ uint8_t *code_ptr;
+ TCGTemp static_temps[TCG_MAX_TEMPS];
+
+ TCGHelperInfo *helpers;
+ int nb_helpers;
+ int allocated_helpers;
+ int helpers_sorted;
+
+#ifdef CONFIG_PROFILER
+ /* profiling info */
+ int64_t tb_count1;
+ int64_t tb_count;
+ int64_t op_count; /* total insn count */
+ int op_count_max; /* max insn per TB */
+ int64_t temp_count;
+ int temp_count_max;
+ int64_t del_op_count;
+ int64_t code_in_len;
+ int64_t code_out_len;
+ int64_t interm_time;
+ int64_t code_time;
+ int64_t la_time;
+ int64_t restore_count;
+ int64_t restore_time;
+#endif
+};
+
+extern TCGContext tcg_ctx;
+extern uint16_t *gen_opc_ptr;
+extern TCGArg *gen_opparam_ptr;
+extern uint16_t gen_opc_buf[];
+extern TCGArg gen_opparam_buf[];
+
+/* pool based memory allocation */
+
+void *tcg_malloc_internal(TCGContext *s, int size);
+void tcg_pool_reset(TCGContext *s);
+void tcg_pool_delete(TCGContext *s);
+
+static inline void *tcg_malloc(int size)
+{
+ TCGContext *s = &tcg_ctx;
+ uint8_t *ptr, *ptr_end;
+ size = (size + sizeof(long) - 1) & ~(sizeof(long) - 1);
+ ptr = s->pool_cur;
+ ptr_end = ptr + size;
+ if (unlikely(ptr_end > s->pool_end)) {
+ return tcg_malloc_internal(&tcg_ctx, size);
+ } else {
+ s->pool_cur = ptr_end;
+ return ptr;
+ }
+}
+
+void tcg_context_init(TCGContext *s);
+void tcg_prologue_init(TCGContext *s);
+void tcg_func_start(TCGContext *s);
+
+int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf);
+int tcg_gen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset);
+
+void tcg_set_frame(TCGContext *s, int reg,
+ tcg_target_long start, tcg_target_long size);
+
+TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name);
+TCGv_i32 tcg_global_mem_new_i32(int reg, tcg_target_long offset,
+ const char *name);
+TCGv_i32 tcg_temp_new_internal_i32(int temp_local);
+static inline TCGv_i32 tcg_temp_new_i32(void)
+{
+ return tcg_temp_new_internal_i32(0);
+}
+static inline TCGv_i32 tcg_temp_local_new_i32(void)
+{
+ return tcg_temp_new_internal_i32(1);
+}
+void tcg_temp_free_i32(TCGv_i32 arg);
+char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg);
+
+TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name);
+TCGv_i64 tcg_global_mem_new_i64(int reg, tcg_target_long offset,
+ const char *name);
+TCGv_i64 tcg_temp_new_internal_i64(int temp_local);
+static inline TCGv_i64 tcg_temp_new_i64(void)
+{
+ return tcg_temp_new_internal_i64(0);
+}
+static inline TCGv_i64 tcg_temp_local_new_i64(void)
+{
+ return tcg_temp_new_internal_i64(1);
+}
+void tcg_temp_free_i64(TCGv_i64 arg);
+char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg);
+
+void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf);
+
+#define TCG_CT_ALIAS 0x80
+#define TCG_CT_IALIAS 0x40
+#define TCG_CT_REG 0x01
+#define TCG_CT_CONST 0x02 /* any constant of register size */
+
+typedef struct TCGArgConstraint {
+ uint16_t ct;
+ uint8_t alias_index;
+ union {
+ TCGRegSet regs;
+ } u;
+} TCGArgConstraint;
+
+#define TCG_MAX_OP_ARGS 16
+
+#define TCG_OPF_BB_END 0x01 /* instruction defines the end of a basic
+ block */
+#define TCG_OPF_CALL_CLOBBER 0x02 /* instruction clobbers call registers
+ and potentially update globals. */
+#define TCG_OPF_SIDE_EFFECTS 0x04 /* instruction has side effects : it
+ cannot be removed if its output
+ are not used */
+
+typedef struct TCGOpDef {
+ const char *name;
+ uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args;
+ uint8_t flags;
+ TCGArgConstraint *args_ct;
+ int *sorted_args;
+#if defined(CONFIG_DEBUG_TCG)
+ int used;
+#endif
+} TCGOpDef;
+
+typedef struct TCGTargetOpDef {
+ TCGOpcode op;
+ const char *args_ct_str[TCG_MAX_OP_ARGS];
+} TCGTargetOpDef;
+
+#define tcg_abort() \
+do {\
+ fprintf(stderr, "%s:%d: tcg fatal error\n", __FILE__, __LINE__);\
+ abort();\
+} while (0)
+
+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
+#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
+#endif
+
+void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags,
+ int sizemask, TCGArg ret, int nargs, TCGArg *args);
+
+void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1,
+ int c, int right, int arith);
+
+/* only used for debugging purposes */
+void tcg_register_helper(void *func, const char *name);
+const char *tcg_helper_get_name(TCGContext *s, void *func);
+void tcg_dump_ops(TCGContext *s, FILE *outfile);
+
+void dump_ops(const uint16_t *opc_buf, const TCGArg *opparam_buf);
+TCGv_i32 tcg_const_i32(int32_t val);
+TCGv_i64 tcg_const_i64(int64_t val);
+TCGv_i32 tcg_const_local_i32(int32_t val);
+TCGv_i64 tcg_const_local_i64(int64_t val);
+
+extern uint8_t code_gen_prologue[];
+#if defined(_ARCH_PPC) && !defined(_ARCH_PPC64)
+#define tcg_qemu_tb_exec(tb_ptr) \
+ ((long REGPARM __attribute__ ((longcall)) (*)(void *))code_gen_prologue)(tb_ptr)
+#else
+#define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM (*)(void *))code_gen_prologue)(tb_ptr)
+#endif
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..9ded4b7
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,147 @@
+-include ../config-host.mak
+-include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH)/tests)
+
+QEMU=../i386-linux-user/qemu-i386
+QEMU_X86_64=../x86_64-linux-user/qemu-x86_64
+CC_X86_64=$(CC_I386) -m64
+
+QEMU_INCLUDES += -I..
+CFLAGS=-Wall -O2 -g -fno-strict-aliasing
+#CFLAGS+=-msse2
+LDFLAGS=
+
+# TODO: automatically detect ARM and MIPS compilers, and run those too
+
+# runcom maps page 0, so it requires root privileges
+# also, pi_10.com runs indefinitely
+
+I386_TESTS=hello-i386 \
+ linux-test \
+ testthread \
+ sha1-i386 \
+ test-i386 \
+ test-mmap \
+ # runcom
+
+# native i386 compilers sometimes are not biarch. assume cross-compilers are
+ifneq ($(ARCH),i386)
+I386_TESTS+=run-test-x86_64
+endif
+
+TESTS = test_path
+ifneq ($(call find-in-path, $(CC_I386)),)
+TESTS += $(I386_TESTS)
+endif
+
+all: $(patsubst %,run-%,$(TESTS))
+
+# rules to run tests
+
+.PHONY: $(patsubst %,run-%,$(TESTS))
+
+run-%: %
+ -$(QEMU) ./$*
+
+run-hello-i386: hello-i386
+run-linux-test: linux-test
+run-testthread: testthread
+run-sha1-i386: sha1-i386
+
+run-test-i386: test-i386
+ ./test-i386 > test-i386.ref
+ -$(QEMU) test-i386 > test-i386.out
+ @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi
+
+run-test-x86_64: test-x86_64
+ ./test-x86_64 > test-x86_64.ref
+ -$(QEMU_X86_64) test-x86_64 > test-x86_64.out
+ @if diff -u test-x86_64.ref test-x86_64.out ; then echo "Auto Test OK"; fi
+
+run-test-mmap: test-mmap
+ -$(QEMU) ./test-mmap
+ -$(QEMU) -p 8192 ./test-mmap 8192
+ -$(QEMU) -p 16384 ./test-mmap 16384
+ -$(QEMU) -p 32768 ./test-mmap 32768
+
+run-runcom: runcom
+ -$(QEMU) ./runcom $(SRC_PATH)/tests/pi_10.com
+
+run-test_path: test_path
+ ./test_path
+
+# rules to compile tests
+
+test_path: test_path.o
+test_path.o: test_path.c
+
+hello-i386: hello-i386.c
+ $(CC_I386) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
+ strip $@
+
+testthread: testthread.c
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
+
+# i386/x86_64 emulation test (test various opcodes) */
+test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \
+ test-i386.h test-i386-shift.h test-i386-muldiv.h
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ \
+ $(<D)/test-i386.c $(<D)/test-i386-code16.S $(<D)/test-i386-vm86.S -lm
+
+test-x86_64: test-i386.c \
+ test-i386.h test-i386-shift.h test-i386-muldiv.h
+ $(CC_X86_64) $(CFLAGS) $(LDFLAGS) -o $@ $(<D)/test-i386.c -lm
+
+# generic Linux and CPU test
+linux-test: linux-test.c
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lm
+
+# vm86 test
+runcom: runcom.c
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+test-mmap: test-mmap.c
+ $(CC_I386) -m32 $(CFLAGS) -Wall -O2 $(LDFLAGS) -o $@ $<
+
+# speed test
+sha1-i386: sha1.c
+ $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+sha1: sha1.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+speed: sha1 sha1-i386
+ time ./sha1
+ time $(QEMU) ./sha1-i386
+
+# broken test
+# NOTE: -fomit-frame-pointer is currently needed : this is a bug in libqemu
+qruncom: qruncom.c ../ioport-user.c ../i386-user/libqemu.a
+ $(CC) $(CFLAGS) -fomit-frame-pointer $(LDFLAGS) -I../target-i386 -I.. -I../i386-user -I../fpu \
+ -o $@ $(filter %.c, $^) -L../i386-user -lqemu -lm
+
+# arm test
+hello-arm: hello-arm.o
+ arm-linux-ld -o $@ $<
+
+hello-arm.o: hello-arm.c
+ arm-linux-gcc -Wall -g -O2 -c -o $@ $<
+
+test-arm-iwmmxt: test-arm-iwmmxt.s
+ cpp < $< | arm-linux-gnu-gcc -Wall -static -march=iwmmxt -mabi=aapcs -x assembler - -o $@
+
+# MIPS test
+hello-mips: hello-mips.c
+ mips-linux-gnu-gcc -nostdlib -static -mno-abicalls -fno-PIC -mabi=32 -Wall -Wextra -g -O2 -o $@ $<
+
+hello-mipsel: hello-mips.c
+ mipsel-linux-gnu-gcc -nostdlib -static -mno-abicalls -fno-PIC -mabi=32 -Wall -Wextra -g -O2 -o $@ $<
+
+# testsuite for the CRIS port.
+test-cris:
+ $(MAKE) -C cris check
+
+clean:
+ rm -f *~ *.o test-i386.out test-i386.ref \
+ test-x86_64.log test-x86_64.ref qruncom $(TESTS)
diff --git a/tests/alpha/Makefile b/tests/alpha/Makefile
new file mode 100644
index 0000000..2b1f03d
--- /dev/null
+++ b/tests/alpha/Makefile
@@ -0,0 +1,35 @@
+CROSS=alpha-linux-gnu-
+CC=$(CROSS)gcc
+AS=$(CROSS)as
+
+SIM=../../alpha-linux-user/qemu-alpha
+
+CFLAGS=-O
+LINK=$(CC) -o $@ crt.o $< -nostdlib
+
+TESTS=test-cond test-cmov
+
+all: hello-alpha $(TESTS)
+
+hello-alpha: hello-alpha.o crt.o
+ $(LINK)
+
+test-cond: test-cond.o crt.o
+ $(LINK)
+
+test-cmov.o: test-cond.c
+ $(CC) -c $(CFLAGS) -DTEST_CMOV -o $@ $<
+
+test-cmov: test-cmov.o crt.o
+ $(LINK)
+
+test-ovf: test-ovf.o crt.o
+ $(LINK)
+
+check: $(TESTS)
+ for f in $(TESTS); do $(SIM) $$f || exit 1; done
+
+clean:
+ $(RM) *.o *~ hello-alpha $(TESTS)
+
+.PHONY: clean all check
diff --git a/tests/alpha/crt.s b/tests/alpha/crt.s
new file mode 100644
index 0000000..31af882
--- /dev/null
+++ b/tests/alpha/crt.s
@@ -0,0 +1,26 @@
+ .text
+
+ .globl _start
+ .ent _start,0
+_start:
+ .frame $15,0,$15
+ br $29,1f
+1: ldgp $29, 0($29)
+ .prologue 0
+ ldq $27,main($29) !literal!1
+ jsr $26,($27)
+ or $0,$0,$16
+ .end _start
+
+ .globl _exit
+_exit:
+ lda $0,1
+ callsys
+
+ call_pal 0
+
+ .globl write
+write:
+ lda $0,4
+ callsys
+ ret
diff --git a/tests/alpha/hello-alpha.c b/tests/alpha/hello-alpha.c
new file mode 100644
index 0000000..79892e6
--- /dev/null
+++ b/tests/alpha/hello-alpha.c
@@ -0,0 +1,5 @@
+int main (void)
+{
+ write (1, "hello\n", 6);
+ return 0;
+}
diff --git a/tests/alpha/test-cond.c b/tests/alpha/test-cond.c
new file mode 100644
index 0000000..74adffa
--- /dev/null
+++ b/tests/alpha/test-cond.c
@@ -0,0 +1,87 @@
+
+#ifdef TEST_CMOV
+
+#define TEST_COND(N) \
+int test_##N (long a) \
+{ \
+ int res = 1; \
+ \
+ asm ("cmov"#N" %1,$31,%0" \
+ : "+r" (res) : "r" (a)); \
+ return !res; \
+}
+
+#else
+
+#define TEST_COND(N) \
+int test_##N (long a) \
+{ \
+ int res = 1; \
+ \
+ asm ("b"#N" %1,1f\n\t" \
+ "addq $31,$31,%0\n\t" \
+ "1: unop\n" \
+ : "+r" (res) : "r" (a)); \
+ return res; \
+}
+
+#endif
+
+TEST_COND(eq)
+TEST_COND(ne)
+TEST_COND(ge)
+TEST_COND(gt)
+TEST_COND(lbc)
+TEST_COND(lbs)
+TEST_COND(le)
+TEST_COND(lt)
+
+static struct {
+ int (*func)(long);
+ long v;
+ int r;
+} vectors[] =
+ {
+ {test_eq, 0, 1},
+ {test_eq, 1, 0},
+
+ {test_ne, 0, 0},
+ {test_ne, 1, 1},
+
+ {test_ge, 0, 1},
+ {test_ge, 1, 1},
+ {test_ge, -1, 0},
+
+ {test_gt, 0, 0},
+ {test_gt, 1, 1},
+ {test_gt, -1, 0},
+
+ {test_lbc, 0, 1},
+ {test_lbc, 1, 0},
+ {test_lbc, -1, 0},
+
+ {test_lbs, 0, 0},
+ {test_lbs, 1, 1},
+ {test_lbs, -1, 1},
+
+ {test_le, 0, 1},
+ {test_le, 1, 0},
+ {test_le, -1, 1},
+
+ {test_lt, 0, 0},
+ {test_lt, 1, 0},
+ {test_lt, -1, 1},
+ };
+
+int main (void)
+{
+ int i;
+
+ for (i = 0; i < sizeof (vectors)/sizeof(vectors[0]); i++)
+ if ((*vectors[i].func)(vectors[i].v) != vectors[i].r) {
+ write(1, "Failed\n", 7);
+ return 1;
+ }
+ write(1, "OK\n", 3);
+ return 0;
+}
diff --git a/tests/alpha/test-ovf.c b/tests/alpha/test-ovf.c
new file mode 100644
index 0000000..01c80e7
--- /dev/null
+++ b/tests/alpha/test-ovf.c
@@ -0,0 +1,29 @@
+static long test_subqv (long a, long b)
+{
+ long res;
+
+ asm ("subq/v %1,%2,%0"
+ : "=r" (res) : "r" (a), "r" (b));
+ return res;
+}
+static struct {
+ long (*func)(long, long);
+ long a;
+ long b;
+ long r;
+} vectors[] =
+ {
+ {test_subqv, 0, 0x7d54000, 0xfffffffff82ac000L}
+ };
+
+int main (void)
+{
+ int i;
+
+ for (i = 0; i < sizeof (vectors)/sizeof(vectors[0]); i++)
+ if ((*vectors[i].func)(vectors[i].a, vectors[i].b) != vectors[i].r) {
+ write(1, "Failed\n", 7);
+ }
+ write(1, "OK\n", 3);
+ return 0;
+}
diff --git a/tests/cris/Makefile b/tests/cris/Makefile
new file mode 100644
index 0000000..b86bcad
--- /dev/null
+++ b/tests/cris/Makefile
@@ -0,0 +1,155 @@
+-include ../../config-host.mak
+
+CROSS=crisv32-axis-linux-gnu-
+SIM=../../cris-linux-user/qemu-cris -L ./
+SIMG=cris-axis-linux-gnu-run --sysroot=./
+
+CC = $(CROSS)gcc
+#AS = $(CROSS)as
+AS = $(CC) -x assembler-with-cpp
+SIZE = $(CROSS)size
+LD = $(CC)
+OBJCOPY = $(CROSS)objcopy
+
+# we rely on GCC inline:ing the stuff we tell it to in many places here.
+CFLAGS = -Winline -Wall -g -O2 -static
+NOSTDFLAGS = -nostartfiles -nostdlib
+ASFLAGS += -g -Wa,-I,$(SRC_PATH)/tests/cris/
+LDLIBS =
+NOSTDLIBS = -lgcc
+
+CRT = crt.o
+SYS = sys.o
+TESTCASES += check_abs.tst
+TESTCASES += check_addc.tst
+TESTCASES += check_addcm.tst
+TESTCASES += check_addo.tst
+TESTCASES += check_addoq.tst
+TESTCASES += check_addi.tst
+TESTCASES += check_addiv32.tst
+TESTCASES += check_addm.tst
+TESTCASES += check_addr.tst
+TESTCASES += check_addq.tst
+TESTCASES += check_addxc.tst
+TESTCASES += check_addxm.tst
+TESTCASES += check_addxr.tst
+TESTCASES += check_andc.tst
+TESTCASES += check_andm.tst
+TESTCASES += check_andr.tst
+TESTCASES += check_andq.tst
+TESTCASES += check_asr.tst
+TESTCASES += check_ba.tst
+TESTCASES += check_bas.tst
+TESTCASES += check_bcc.tst
+TESTCASES += check_bound.tst
+TESTCASES += check_boundc.tst
+TESTCASES += check_boundr.tst
+TESTCASES += check_btst.tst
+TESTCASES += check_clearfv32.tst
+TESTCASES += check_cmpc.tst
+TESTCASES += check_cmpr.tst
+TESTCASES += check_cmpq.tst
+TESTCASES += check_cmpm.tst
+TESTCASES += check_cmpxc.tst
+TESTCASES += check_cmpxm.tst
+TESTCASES += check_cmp-2.tst
+TESTCASES += check_clrjmp1.tst
+TESTCASES += check_dstep.tst
+TESTCASES += check_ftag.tst
+TESTCASES += check_int64.tst
+# check_jsr is broken.
+#TESTCASES += check_jsr.tst
+TESTCASES += check_mcp.tst
+TESTCASES += check_movei.tst
+TESTCASES += check_mover.tst
+TESTCASES += check_moverm.tst
+TESTCASES += check_moveq.tst
+TESTCASES += check_movemr.tst
+TESTCASES += check_movemrv32.tst
+TESTCASES += check_movecr.tst
+TESTCASES += check_movmp.tst
+TESTCASES += check_movpr.tst
+TESTCASES += check_movprv32.tst
+TESTCASES += check_movdelsr1.tst
+TESTCASES += check_movpmv32.tst
+TESTCASES += check_movsr.tst
+TESTCASES += check_movsm.tst
+TESTCASES += check_movscr.tst
+TESTCASES += check_movur.tst
+TESTCASES += check_movum.tst
+TESTCASES += check_movucr.tst
+TESTCASES += check_mulx.tst
+TESTCASES += check_mulv32.tst
+TESTCASES += check_neg.tst
+TESTCASES += check_not.tst
+TESTCASES += check_lz.tst
+TESTCASES += check_lapc.tst
+TESTCASES += check_lsl.tst
+TESTCASES += check_lsr.tst
+TESTCASES += check_orc.tst
+TESTCASES += check_orm.tst
+TESTCASES += check_orr.tst
+TESTCASES += check_orq.tst
+TESTCASES += check_ret.tst
+TESTCASES += check_swap.tst
+TESTCASES += check_scc.tst
+TESTCASES += check_subc.tst
+TESTCASES += check_subq.tst
+TESTCASES += check_subr.tst
+TESTCASES += check_subm.tst
+TESTCASES += check_glibc_kernelversion.tst
+TESTCASES += check_xarith.tst
+
+TESTCASES += check_hello.ctst
+TESTCASES += check_stat1.ctst
+TESTCASES += check_stat2.ctst
+TESTCASES += check_stat3.ctst
+TESTCASES += check_stat4.ctst
+TESTCASES += check_openpf1.ctst
+TESTCASES += check_openpf2.ctst
+TESTCASES += check_openpf3.ctst
+TESTCASES += check_openpf4.ctst
+TESTCASES += check_openpf5.ctst
+TESTCASES += check_mapbrk.ctst
+TESTCASES += check_mmap1.ctst
+TESTCASES += check_mmap2.ctst
+TESTCASES += check_mmap3.ctst
+TESTCASES += check_sigalrm.ctst
+TESTCASES += check_time1.ctst
+TESTCASES += check_time2.ctst
+TESTCASES += check_settls1.ctst
+
+TESTCASES += check_gcctorture_pr28634-1.ctst
+#TESTCASES += check_gcctorture_pr28634.ctst
+
+all: build
+
+%.o: $(SRC_PATH)/tests/cris/%.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+%.o: $(SRC_PATH)/tests/cris/%.s
+ $(AS) $(ASFLAGS) -c $< -o $@
+
+%.tst: %.o
+ $(CC) $(CFLAGS) $(NOSTDFLAGS) $(LDLIBS) $(NOSTDLIBS) $(CRT) $< $(SYS) -o $@
+
+%.ctst: %.o
+ $(CC) $(CFLAGS) $(LDLIBS) $< -o $@
+
+build: $(CRT) $(SYS) $(TESTCASES)
+
+check: $(CRT) $(SYS) $(TESTCASES)
+ @echo -e "\nQEMU simulator."
+ for case in $(TESTCASES); do \
+ echo -n "$$case "; \
+ $(SIM) ./$$case; \
+ done
+check-g: $(CRT) $(SYS) $(TESTCASES)
+ @echo -e "\nGDB simulator."
+ @for case in $(TESTCASES); do \
+ echo -n "$$case "; \
+ $(SIMG) $$case; \
+ done
+
+clean:
+ $(RM) -fr $(TESTCASES) $(CRT) $(SYS)
diff --git a/tests/cris/README b/tests/cris/README
new file mode 100644
index 0000000..2e65a76
--- /dev/null
+++ b/tests/cris/README
@@ -0,0 +1 @@
+Test-suite for the cris port. Heavily based on the test-suite for the CRIS port of sim by Hans-Peter Nilsson.
diff --git a/tests/cris/check_abs.c b/tests/cris/check_abs.c
new file mode 100644
index 0000000..9770a8d
--- /dev/null
+++ b/tests/cris/check_abs.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+static inline int cris_abs(int n)
+{
+ int r;
+ asm ("abs\t%1, %0\n" : "=r" (r) : "r" (n));
+ return r;
+}
+
+static inline void
+verify_abs(int val, int res,
+ const int n, const int z, const int v, const int c)
+{
+ int r;
+
+ cris_tst_cc_init();
+ r = cris_abs(val);
+ cris_tst_cc(n, z, v, c);
+ if (r != res)
+ err();
+}
+
+int main(void)
+{
+ verify_abs(-1, 1, 0, 0, 0, 0);
+ verify_abs(0x80000000, 0x80000000, 1, 0, 0, 0);
+ verify_abs(0x7fffffff, 0x7fffffff, 0, 0, 0, 0);
+ verify_abs(42, 42, 0, 0, 0, 0);
+ verify_abs(1, 1, 0, 0, 0, 0);
+ verify_abs(0xffff, 0xffff, 0, 0, 0, 0);
+ verify_abs(0xffff, 0xffff, 0, 0, 0, 0);
+ verify_abs(-31, 0x1f, 0, 0, 0, 0);
+ verify_abs(0, 0, 0, 1, 0, 0);
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_addc.c b/tests/cris/check_addc.c
new file mode 100644
index 0000000..facd1be
--- /dev/null
+++ b/tests/cris/check_addc.c
@@ -0,0 +1,58 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+static inline int cris_addc(int a, const int b)
+{
+ asm ("addc\t%1, %0\n" : "+r" (a) : "r" (b));
+ return a;
+}
+
+#define verify_addc(a, b, res, n, z, v, c) \
+{ \
+ int r; \
+ r = cris_addc((a), (b)); \
+ cris_tst_cc((n), (z), (v), (c)); \
+ if (r != (res)) \
+ err(); \
+}
+
+int main(void)
+{
+ cris_tst_cc_init();
+ asm volatile ("clearf cz");
+ verify_addc(0, 0, 0, 0, 0, 0, 0);
+
+ cris_tst_cc_init();
+ asm volatile ("setf z");
+ verify_addc(0, 0, 0, 0, 1, 0, 0);
+
+ cris_tst_cc_init();
+ asm volatile ("setf cz");
+ verify_addc(0, 0, 1, 0, 0, 0, 0);
+ cris_tst_cc_init();
+ asm volatile ("clearf c");
+ verify_addc(-1, 2, 1, 0, 0, 0, 1);
+
+ cris_tst_cc_init();
+ asm volatile ("clearf nzv");
+ asm volatile ("setf c");
+ verify_addc(-1, 2, 2, 0, 0, 0, 1);
+
+ cris_tst_cc_init();
+ asm volatile ("setf c");
+ verify_addc(0xffff, 0xffff, 0x1ffff, 0, 0, 0, 0);
+
+ cris_tst_cc_init();
+ asm volatile ("clearf nzvc");
+ verify_addc(-1, -1, 0xfffffffe, 1, 0, 0, 1);
+
+ cris_tst_cc_init();
+ asm volatile ("setf c");
+ verify_addc(0x78134452, 0x5432f789, 0xcc463bdc, 1, 0, 1, 0);
+
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_addcm.c b/tests/cris/check_addcm.c
new file mode 100644
index 0000000..7928bc9
--- /dev/null
+++ b/tests/cris/check_addcm.c
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+/* need to avoid acr as source here. */
+static inline int cris_addc_m(int a, const int *b)
+{
+ asm volatile ("addc [%1], %0\n" : "+r" (a) : "r" (b));
+ return a;
+}
+
+/* 'b' is a crisv32 constrain to avoid postinc with $acr. */
+static inline int cris_addc_pi_m(int a, int **b)
+{
+ asm volatile ("addc [%1+], %0\n" : "+r" (a), "+b" (*b));
+ return a;
+}
+
+#define verify_addc_m(a, b, res, n, z, v, c) \
+{ \
+ int r; \
+ r = cris_addc_m((a), (b)); \
+ cris_tst_cc((n), (z), (v), (c)); \
+ if (r != (res)) \
+ err(); \
+}
+
+#define verify_addc_pi_m(a, b, res, n, z, v, c) \
+{ \
+ int r; \
+ r = cris_addc_pi_m((a), (b)); \
+ cris_tst_cc((n), (z), (v), (c)); \
+ if (r != (res)) \
+ err(); \
+}
+
+int x[] = { 0, 0, 2, -1, 0xffff, -1, 0x5432f789};
+
+int main(void)
+{
+ int *p = (void *)&x[0];
+#if 1
+ cris_tst_cc_init();
+ asm volatile ("clearf cz");
+ verify_addc_m(0, p, 0, 0, 0, 0, 0);
+
+ cris_tst_cc_init();
+ asm volatile ("setf z");
+ verify_addc_m(0, p, 0, 0, 1, 0, 0);
+
+ cris_tst_cc_init();
+ asm volatile ("setf c");
+ verify_addc_m(0, p, 1, 0, 0, 0, 0);
+
+ cris_tst_cc_init();
+ asm volatile ("clearf c");
+ verify_addc_pi_m(0, &p, 0, 0, 1, 0, 0);
+
+ p = &x[1];
+ cris_tst_cc_init();
+ asm volatile ("setf c");
+ verify_addc_pi_m(0, &p, 1, 0, 0, 0, 0);
+
+ if (p != &x[2])
+ err();
+
+ cris_tst_cc_init();
+ asm volatile ("clearf c");
+ verify_addc_pi_m(-1, &p, 1, 0, 0, 0, 1);
+
+ if (p != &x[3])
+ err();
+#endif
+ p = &x[3];
+ /* TODO: investigate why this one fails. */
+ cris_tst_cc_init();
+ asm volatile ("setf c");
+ verify_addc_m(2, p, 2, 0, 0, 0, 1);
+ p += 4;
+
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_addi.s b/tests/cris/check_addi.s
new file mode 100644
index 0000000..a00dec0
--- /dev/null
+++ b/tests/cris/check_addi.s
@@ -0,0 +1,57 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 0\n1\n2\n4\nbe02460f\n69d035a6\nc16c14d4\n
+
+ .include "testutils.inc"
+ start
+ moveq 0,r3
+ moveq 0,r4
+ clearf zcvn
+ addi r4.b,r3
+ test_cc 0 0 0 0
+ checkr3 0
+
+ moveq 0,r3
+ moveq 1,r4
+ setf zcvn
+ addi r4.b,r3
+ test_cc 1 1 1 1
+ checkr3 1
+
+ moveq 0,r3
+ moveq 1,r4
+ setf cv
+ clearf zn
+ addi r4.w,r3
+ test_cc 0 0 1 1
+ checkr3 2
+
+ moveq 0,r3
+ moveq 1,r4
+ clearf cv
+ setf zn
+ addi r4.d,r3
+ test_cc 1 1 0 0
+ checkr3 4
+
+ move.d 0x12345678,r3
+ move.d 0xabcdef97,r4
+ clearf cn
+ setf zv
+ addi r4.b,r3
+ test_cc 0 1 1 0
+ checkr3 be02460f
+
+ move.d 0x12345678,r3
+ move.d 0xabcdef97,r4
+ setf cn
+ clearf zv
+ addi r4.w,r3
+ test_cc 1 0 0 1
+ checkr3 69d035a6
+
+ move.d 0x12345678,r3
+ move.d 0xabcdef97,r4
+ addi r4.d,r3
+ checkr3 c16c14d4
+
+ quit
diff --git a/tests/cris/check_addiv32.s b/tests/cris/check_addiv32.s
new file mode 100644
index 0000000..20ba25d
--- /dev/null
+++ b/tests/cris/check_addiv32.s
@@ -0,0 +1,62 @@
+# mach: crisv32
+# output: 4455aa77\n4455aa77\nee19ccff\nff22\n4455aa77\nff224455\n55aa77ff\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword 0x55aa77ff
+ .dword 0xccff2244
+ .dword 0x88ccee19
+
+ start
+ setf cv
+ moveq -1,r0
+ move.d x-32768,r5
+ move.d 32769,r6
+ addi r6.b,r5,acr
+ test_cc 0 0 1 1
+ move.d [acr],r3
+ checkr3 4455aa77
+
+ addu.w 32771,r5
+ setf znvc
+ moveq -1,r8
+ addi r8.w,r5,acr
+ test_cc 1 1 1 1
+ move.d [acr],r3
+ checkr3 4455aa77
+
+ moveq 5,r10
+ clearf znvc
+ addi r10.b,acr,acr
+ test_cc 0 0 0 0
+ move.d [acr],r3
+ checkr3 ee19ccff
+
+ subq 1,r5
+ move.d r5,r8
+ subq 1,r8
+ moveq 1,r9
+ addi r9.d,r8,acr
+ test_cc 0 0 0 0
+ movu.w [acr],r3
+ checkr3 ff22
+
+ moveq -2,r11
+ addi r11.w,acr,acr
+ move.d [acr],r3
+ checkr3 4455aa77
+
+ moveq 5,r9
+ addi r9.d,acr,acr
+ subq 18,acr
+ move.d [acr],r3
+ checkr3 ff224455
+
+ move.d -76789888/4,r12
+ addi r12.d,r5,acr
+ add.d 76789886,acr
+ move.d [acr],r3
+ checkr3 55aa77ff
+
+ quit
diff --git a/tests/cris/check_addm.s b/tests/cris/check_addm.s
new file mode 100644
index 0000000..efece9f
--- /dev/null
+++ b/tests/cris/check_addm.s
@@ -0,0 +1,96 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n781344d0\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword 2,-1,0xffff,-1,0x5432f789
+ .word 2,-1,0xffff,0xf789
+ .byte 2,0xff,0x89
+ .byte 0x7e
+
+ start
+ moveq -1,r3
+ move.d x,r5
+ add.d [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ add.d [r5],r3
+ test_cc 0 0 0 1
+ addq 4,r5
+ checkr3 1
+
+ move.d 0xffff,r3
+ add.d [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+
+ moveq -1,r3
+ add.d [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+
+ move.d 0x78134452,r3
+ add.d [r5+],r3
+ test_cc 1 0 1 0
+ checkr3 cc463bdb
+
+ moveq -1,r3
+ add.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 ffff0001
+
+ moveq 2,r3
+ add.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xffff,r3
+ add.w [r5],r3
+ test_cc 1 0 0 1
+ checkr3 fffe
+
+ move.d 0xfedaffff,r3
+ add.w [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 fedafffe
+
+ move.d 0x78134452,r3
+ add.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 78133bdb
+
+ moveq -1,r3
+ add.b [r5],r3
+ test_cc 0 0 0 1
+ addq 1,r5
+ checkr3 ffffff01
+
+ moveq 2,r3
+ add.b [r5],r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xff,r3
+ add.b [r5],r3
+ test_cc 1 0 0 1
+ checkr3 fe
+
+ move.d 0xfeda49ff,r3
+ add.b [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 feda49fe
+
+ move.d 0x78134452,r3
+ add.b [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 781344db
+
+ move.d 0x78134452,r3
+ add.b [r5],r3
+ test_cc 1 0 1 0
+ checkr3 781344d0
+
+ quit
diff --git a/tests/cris/check_addo.c b/tests/cris/check_addo.c
new file mode 100644
index 0000000..3d8e789
--- /dev/null
+++ b/tests/cris/check_addo.c
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+/* this would be better to do in asm, it's an orgy in GCC inline asm now. */
+
+#define cris_addo_b(o, v) \
+ asm volatile ("addo.b\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr");
+#define cris_addo_w(o, v) \
+ asm volatile ("addo.w\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr");
+#define cris_addo_d(o, v) \
+ asm volatile ("addo.d\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr");
+#define cris_addo_pi_b(o, v) \
+ asm volatile ("addo.b\t[%0+], %1, $acr\n" \
+ : "+b" (o): "r" (v) : "acr");
+#define cris_addo_pi_w(o, v) \
+ asm volatile ("addo.w\t[%0+], %1, $acr\n" \
+ : "+b" (o): "r" (v) : "acr");
+#define cris_addo_pi_d(o, v) \
+ asm volatile ("addo.d\t[%0+], %1, $acr\n" \
+ : "+b" (o): "r" (v) : "acr");
+
+struct {
+ uint32_t v1;
+ uint16_t v2;
+ uint32_t v3;
+ uint8_t v4;
+ uint8_t v5;
+ uint16_t v6;
+ uint32_t v7;
+} y = {
+ 32769,
+ -1,
+ 5,
+ 3, -4,
+ 2,
+ -76789887
+};
+
+static int x[3] = {0x55aa77ff, 0xccff2244, 0x88ccee19};
+
+int main(void)
+{
+ int *r;
+ unsigned char *t, *p;
+
+ /* Note, this test-case will trig an unaligned access, partly
+ to x[0] and to [x1]. */
+ t = (unsigned char *)x;
+ t -= 32768;
+ p = (unsigned char *) &y.v1;
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_pi_d(p, t);
+ cris_tst_cc(1, 1, 1, 1);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ if (*r != 0x4455aa77)
+ err();
+
+
+ t += 32770;
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_pi_w(p, t);
+ cris_tst_cc(1, 1, 1, 1);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ if (*r != 0x4455aa77)
+ err();
+
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_d(p, r);
+ cris_tst_cc(1, 1, 1, 1);
+ p += 4;
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ if (*r != 0xee19ccff)
+ err();
+
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_pi_b(p, t);
+ cris_tst_cc(0, 0, 0, 0);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ if (*(uint16_t*)r != 0xff22)
+ err();
+
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_b(p, r);
+ cris_tst_cc(1, 1, 1, 1);
+ p += 1;
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ if (*r != 0x4455aa77)
+ err();
+
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_w(p, r);
+ cris_tst_cc(1, 1, 1, 1);
+ p += 2;
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ if (*r != 0xff224455)
+ err();
+
+ mb(); /* dont reorder anything beyond here. */
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addo_pi_d(p, t);
+ cris_tst_cc(0, 0, 0, 0);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (r));
+ r = (void*)(((char *)r) + 76789885);
+ if (*r != 0x55aa77ff)
+ err();
+
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_addoq.c b/tests/cris/check_addoq.c
new file mode 100644
index 0000000..ed509e2
--- /dev/null
+++ b/tests/cris/check_addoq.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+/* this would be better to do in asm, it's an orgy in GCC inline asm now. */
+
+/* ACR will be clobbered. */
+#define cris_addoq(o, v) \
+ asm volatile ("addoq\t%1, %0, $acr\n" : : "r" (v), "i" (o) : "acr");
+
+
+int main(void)
+{
+ int x[3] = {0x55aa77ff, 0xccff2244, 0x88ccee19};
+ int *p, *t = x + 1;
+
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addoq(0, t);
+ cris_tst_cc(1, 1, 1, 1);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (p));
+ if (*p != 0xccff2244)
+ err();
+
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_addoq(4, t);
+ cris_tst_cc(0, 0, 0, 0);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (p));
+ if (*p != 0x88ccee19)
+ err();
+
+ cris_tst_cc_init();
+ asm volatile ("clearf\tzvnc\n");
+ cris_addoq(-8, t + 1);
+ cris_tst_cc(0, 0, 0, 0);
+ asm volatile ("move.d\t$acr, %0\n" : "=r" (p));
+ if (*p != 0x55aa77ff)
+ err();
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_addq.s b/tests/cris/check_addq.s
new file mode 100644
index 0000000..e6f874f
--- /dev/null
+++ b/tests/cris/check_addq.s
@@ -0,0 +1,47 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n0\n1\n100\n10000\n47\n67\na6\n80000001\n
+
+ .include "testutils.inc"
+ start
+ moveq -2,r3
+ addq 1,r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ addq 1,r3
+ test_cc 0 1 0 1
+ checkr3 0
+
+ addq 1,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xff,r3
+ addq 1,r3
+ test_cc 0 0 0 0
+ checkr3 100
+
+ move.d 0xffff,r3
+ addq 1,r3
+ test_cc 0 0 0 0
+ checkr3 10000
+
+ move.d 0x42,r3
+ addq 5,r3
+ test_cc 0 0 0 0
+ checkr3 47
+
+ addq 32,r3
+ test_cc 0 0 0 0
+ checkr3 67
+
+ addq 63,r3
+ test_cc 0 0 0 0
+ checkr3 a6
+
+ move.d 0x7ffffffe,r3
+ addq 3,r3
+ test_cc 1 0 1 0
+ checkr3 80000001
+
+ quit
diff --git a/tests/cris/check_addr.s b/tests/cris/check_addr.s
new file mode 100644
index 0000000..7f55cdc
--- /dev/null
+++ b/tests/cris/check_addr.s
@@ -0,0 +1,96 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ add.d r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ moveq -1,r4
+ add.d r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ add.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+
+ moveq -1,r4
+ move.d r4,r3
+ add.d r4,r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ add.d r4,r3
+ test_cc 1 0 1 0
+ checkr3 cc463bdb
+
+ moveq -1,r3
+ moveq 2,r4
+ add.w r4,r3
+ test_cc 0 0 0 1
+ checkr3 ffff0001
+
+ moveq 2,r3
+ moveq -1,r4
+ add.w r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ add.w r4,r3
+ test_cc 1 0 0 1
+ checkr3 fffe
+
+ move.d 0xfedaffff,r4
+ move.d r4,r3
+ add.w r4,r3
+ test_cc 1 0 0 1
+ checkr3 fedafffe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ add.w r4,r3
+ test_cc 0 0 0 1
+ checkr3 78133bdb
+
+ moveq -1,r3
+ moveq 2,r4
+ add.b r4,r3
+ test_cc 0 0 0 1
+ checkr3 ffffff01
+
+ moveq 2,r3
+ moveq -1,r4
+ add.b r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xff,r4
+ move.d r4,r3
+ add.b r4,r3
+ test_cc 1 0 0 1
+ checkr3 fe
+
+ move.d 0xfeda49ff,r4
+ move.d r4,r3
+ add.b r4,r3
+ test_cc 1 0 0 1
+ checkr3 feda49fe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ add.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 781344db
+
+ quit
diff --git a/tests/cris/check_addxc.s b/tests/cris/check_addxc.s
new file mode 100644
index 0000000..09c8355
--- /dev/null
+++ b/tests/cris/check_addxc.s
@@ -0,0 +1,91 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n101\n10001\n100fe\n1fffe\nfffe\nfffe\nfffffffe\nfe\nfffffffe\n781344db\n781343db\n78143bdb\n78133bdb\n800000ed\n0\n
+
+ .include "testutils.inc"
+ start
+ moveq 2,r3
+ adds.b 0xff,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ adds.w 0xffff,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ addu.b 0xff,r3
+ checkr3 101
+
+ moveq 2,r3
+ move.d 0xffffffff,r4
+ addu.w -1,r3
+ test_cc 0 0 0 0
+ checkr3 10001
+
+ move.d 0xffff,r3
+ addu.b -1,r3
+ test_cc 0 0 0 0
+ checkr3 100fe
+
+ move.d 0xffff,r3
+ addu.w -1,r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+
+ move.d 0xffff,r3
+ adds.b 0xff,r3
+ test_cc 0 0 0 1
+ checkr3 fffe
+
+ move.d 0xffff,r3
+ adds.w 0xffff,r3
+ test_cc 0 0 0 1
+ checkr3 fffe
+
+ moveq -1,r3
+ adds.b 0xff,r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+
+ moveq -1,r3
+ adds.w 0xff,r3
+ test_cc 0 0 0 1
+ checkr3 fe
+
+ moveq -1,r3
+ adds.w 0xffff,r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+
+ move.d 0x78134452,r3
+ addu.b 0x89,r3
+ test_cc 0 0 0 0
+ checkr3 781344db
+
+ move.d 0x78134452,r3
+ adds.b 0x89,r3
+ test_cc 0 0 0 1
+ checkr3 781343db
+
+ move.d 0x78134452,r3
+ addu.w 0xf789,r3
+ test_cc 0 0 0 0
+ checkr3 78143bdb
+
+ move.d 0x78134452,r3
+ adds.w 0xf789,r3
+ test_cc 0 0 0 1
+ checkr3 78133bdb
+
+ move.d 0x7fffffee,r3
+ addu.b 0xff,r3
+ test_cc 1 0 1 0
+ checkr3 800000ed
+
+ move.d 0x1,r3
+ adds.w 0xffff,r3
+ test_cc 0 1 0 1
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_addxm.s b/tests/cris/check_addxm.s
new file mode 100644
index 0000000..7563494
--- /dev/null
+++ b/tests/cris/check_addxm.s
@@ -0,0 +1,106 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n101\n10001\n100fe\n1fffe\nfffe\nfffe\nfffffffe\nfe\nfffffffe\n781344db\n781343db\n78143bdb\n78133bdb\n800000ed\n0\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .byte 0xff
+ .word 0xffff
+ .word 0xff
+ .word 0xffff
+ .byte 0x89
+ .word 0xf789
+ .byte 0xff
+ .word 0xffff
+
+ start
+ moveq 2,r3
+ move.d x,r5
+ adds.b [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ adds.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ subq 3,r5
+ addu.b [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 101
+
+ moveq 2,r3
+ addu.w [r5+],r3
+ subq 3,r5
+ test_cc 0 0 0 0
+ checkr3 10001
+
+ move.d 0xffff,r3
+ addu.b [r5],r3
+ test_cc 0 0 0 0
+ checkr3 100fe
+
+ move.d 0xffff,r3
+ addu.w [r5],r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+
+ move.d 0xffff,r3
+ adds.b [r5],r3
+ test_cc 0 0 0 1
+ checkr3 fffe
+
+ move.d 0xffff,r3
+ adds.w [r5],r3
+ test_cc 0 0 0 1
+ checkr3 fffe
+
+ moveq -1,r3
+ adds.b [r5],r3
+ test_cc 1 0 0 1
+ addq 3,r5
+ checkr3 fffffffe
+
+ moveq -1,r3
+ adds.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 fe
+
+ moveq -1,r3
+ adds.w [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+
+ move.d 0x78134452,r3
+ addu.b [r5],r3
+ test_cc 0 0 0 0
+ checkr3 781344db
+
+ move.d 0x78134452,r3
+ adds.b [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 781343db
+
+ move.d 0x78134452,r3
+ addu.w [r5],r3
+ test_cc 0 0 0 0
+ checkr3 78143bdb
+
+ move.d 0x78134452,r3
+ adds.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 78133bdb
+
+ move.d 0x7fffffee,r3
+ addu.b [r5+],r3
+ test_cc 1 0 1 0
+ checkr3 800000ed
+
+ move.d 0x1,r3
+ adds.w [r5+],r3
+ test_cc 0 1 0 1
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_addxr.s b/tests/cris/check_addxr.s
new file mode 100644
index 0000000..7f55cdc
--- /dev/null
+++ b/tests/cris/check_addxr.s
@@ -0,0 +1,96 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ add.d r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ moveq 2,r3
+ moveq -1,r4
+ add.d r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ add.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+
+ moveq -1,r4
+ move.d r4,r3
+ add.d r4,r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ add.d r4,r3
+ test_cc 1 0 1 0
+ checkr3 cc463bdb
+
+ moveq -1,r3
+ moveq 2,r4
+ add.w r4,r3
+ test_cc 0 0 0 1
+ checkr3 ffff0001
+
+ moveq 2,r3
+ moveq -1,r4
+ add.w r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ add.w r4,r3
+ test_cc 1 0 0 1
+ checkr3 fffe
+
+ move.d 0xfedaffff,r4
+ move.d r4,r3
+ add.w r4,r3
+ test_cc 1 0 0 1
+ checkr3 fedafffe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ add.w r4,r3
+ test_cc 0 0 0 1
+ checkr3 78133bdb
+
+ moveq -1,r3
+ moveq 2,r4
+ add.b r4,r3
+ test_cc 0 0 0 1
+ checkr3 ffffff01
+
+ moveq 2,r3
+ moveq -1,r4
+ add.b r4,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ move.d 0xff,r4
+ move.d r4,r3
+ add.b r4,r3
+ test_cc 1 0 0 1
+ checkr3 fe
+
+ move.d 0xfeda49ff,r4
+ move.d r4,r3
+ add.b r4,r3
+ test_cc 1 0 0 1
+ checkr3 feda49fe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ add.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 781344db
+
+ quit
diff --git a/tests/cris/check_andc.s b/tests/cris/check_andc.s
new file mode 100644
index 0000000..a947b77
--- /dev/null
+++ b/tests/cris/check_andc.s
@@ -0,0 +1,80 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ and.d 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ and.d -1,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ and.d 0xffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r3
+ and.d -1,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ and.d 0x5432f789,r3
+ test_move_cc 0 0 0 0
+ checkr3 50124400
+
+ moveq -1,r3
+ and.w 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0002
+
+ moveq 2,r3
+ and.w -1,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xfffff,r3
+ and.w 0xffff,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffff
+
+ move.d 0xfedaffaf,r3
+ and.w 0xff5f,r3
+ test_move_cc 1 0 0 0
+ checkr3 fedaff0f
+
+ move.d 0x78134452,r3
+ and.w 0xf789,r3
+ test_move_cc 0 0 0 0
+ checkr3 78134400
+
+ moveq -1,r3
+ and.b 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffffff02
+
+ moveq 2,r3
+ and.b -1,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xfa7,r3
+ and.b 0x5a,r3
+ test_move_cc 0 0 0 0
+ checkr3 f02
+
+ move.d 0x78134453,r3
+ and.b 0x89,r3
+ test_move_cc 0 0 0 0
+ checkr3 78134401
+
+ and.b 0,r3
+ test_move_cc 0 1 0 0
+ checkr3 78134400
+
+ quit
diff --git a/tests/cris/check_andm.s b/tests/cris/check_andm.s
new file mode 100644
index 0000000..9385886
--- /dev/null
+++ b/tests/cris/check_andm.s
@@ -0,0 +1,90 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword 2,-1,0xffff,-1,0x5432f789
+ .word 2,-1,0xffff,0xff5f,0xf789
+ .byte 2,-1,0x5a,0x89,0
+
+ start
+ moveq -1,r3
+ move.d x,r5
+ and.d [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ and.d [r5],r3
+ test_move_cc 0 0 0 0
+ addq 4,r5
+ checkr3 2
+
+ move.d 0xffff,r3
+ and.d [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r3
+ and.d [r5+],r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ and.d [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 50124400
+
+ moveq -1,r3
+ and.w [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0002
+
+ moveq 2,r3
+ and.w [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xfffff,r3
+ and.w [r5],r3
+ test_move_cc 1 0 0 0
+ addq 2,r5
+ checkr3 fffff
+
+ move.d 0xfedaffaf,r3
+ and.w [r5+],r3
+ test_move_cc 1 0 0 0
+ checkr3 fedaff0f
+
+ move.d 0x78134452,r3
+ and.w [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 78134400
+
+ moveq -1,r3
+ and.b [r5],r3
+ test_move_cc 0 0 0 0
+ addq 1,r5
+ checkr3 ffffff02
+
+ moveq 2,r3
+ and.b [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xfa7,r3
+ and.b [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 f02
+
+ move.d 0x78134453,r3
+ and.b [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 78134401
+
+ and.b [r5],r3
+ test_move_cc 0 1 0 0
+ checkr3 78134400
+
+ quit
diff --git a/tests/cris/check_andq.s b/tests/cris/check_andq.s
new file mode 100644
index 0000000..55aa7b0
--- /dev/null
+++ b/tests/cris/check_andq.s
@@ -0,0 +1,46 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\nffff\nffffffff\n1f\nffffffe0\n78134452\n0\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ andq 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ andq -1,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ andq -1,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r3
+ andq -1,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ andq 31,r3
+ test_move_cc 0 0 0 0
+ checkr3 1f
+
+ moveq -1,r3
+ andq -32,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffe0
+
+ move.d 0x78134457,r3
+ andq -14,r3
+ test_move_cc 0 0 0 0
+ checkr3 78134452
+
+ moveq 0,r3
+ andq -14,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_andr.s b/tests/cris/check_andr.s
new file mode 100644
index 0000000..61aa1dc
--- /dev/null
+++ b/tests/cris/check_andr.s
@@ -0,0 +1,95 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ and.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ moveq -1,r4
+ and.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ and.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r4
+ move.d r4,r3
+ and.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ and.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 50124400
+
+ moveq -1,r3
+ moveq 2,r4
+ and.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0002
+
+ moveq 2,r3
+ moveq -1,r4
+ and.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xfffff,r3
+ move.d 0xffff,r4
+ and.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffff
+
+ move.d 0xfedaffaf,r3
+ move.d 0xff5f,r4
+ and.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fedaff0f
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ and.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 78134400
+
+ moveq -1,r3
+ moveq 2,r4
+ and.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffffff02
+
+ moveq 2,r3
+ moveq -1,r4
+ and.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0x5a,r4
+ move.d 0xfa7,r3
+ and.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 f02
+
+ move.d 0x5432f789,r4
+ move.d 0x78134453,r3
+ and.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 78134401
+
+ moveq 0,r7
+ and.b r7,r3
+ test_move_cc 0 1 0 0
+ checkr3 78134400
+
+ quit
diff --git a/tests/cris/check_asr.s b/tests/cris/check_asr.s
new file mode 100644
index 0000000..0a02ae6
--- /dev/null
+++ b/tests/cris/check_asr.s
@@ -0,0 +1,230 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n1\nffffffff\nffffffff\n5a67f\nffffffff\nffffffff\nffffffff\nf699fc67\nffffffff\n1\nffffffff\nffffffff\n5a67f\nda67ffff\nda67ffff\nda67ffff\nda67fc67\nffffffff\nffffffff\n1\nffffffff\nffffffff\n5a670007\nda67f1ff\nda67f1ff\nda67f1ff\nda67f1e7\nffffffff\nffffffff\n1\nffffffff\nffffffff\nffffffff\n5a67f1ff\n5a67f1f9\n0\n5a670000\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ asrq 0,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ asrq 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ asrq 31,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ asrq 15,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x5a67f19f,r3
+ asrq 12,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a67f
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 f699fc67
+
+ moveq -1,r3
+ moveq 0,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ asr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ moveq 31,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 15,r4
+ asr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ asr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a67f
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67ffff
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67ffff
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67ffff
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67fc67
+
+ moveq -1,r3
+ moveq 0,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 1,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ asr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ moveq 31,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 15,r4
+ asr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x5a67719f,r3
+ moveq 12,r4
+ asr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a670007
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67f1ff
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67f1ff
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67f1ff
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67f1e7
+
+ moveq -1,r3
+ moveq 0,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 1,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ asr.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ moveq 31,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 15,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 7,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+; FIXME: was wrong.
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 5a67f1ff
+
+; FIXME: was wrong.
+ move.d 0x5a67f19f,r3
+ moveq 4,r4
+ asr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 5a67f1f9
+
+ move.d 0x5a67f19f,r3
+ asrq 31,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0x5a67419f,r3
+ moveq 16,r4
+ asr.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 5a670000
+
+ quit
diff --git a/tests/cris/check_ba.s b/tests/cris/check_ba.s
new file mode 100644
index 0000000..873a408
--- /dev/null
+++ b/tests/cris/check_ba.s
@@ -0,0 +1,93 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: a\n
+
+
+ .set smalloffset,0
+ .set largeoffset,0
+
+
+ .macro fail
+ jump _fail
+ .endm
+
+ .global main
+main:
+ moveq 0,$r3
+
+; Short forward branch.
+ ba 0f
+ addq 1,$r3
+ fail
+
+; Max short forward branch.
+1:
+ ba 2f
+ addq 1,$r3
+ fail
+
+; Short backward branch.
+0:
+ ba 1b
+ addq 1,$r3
+ fail
+
+ .space 254-2+smalloffset+1b-.,0
+ moveq 0,$r3
+
+2:
+; Transit branch (long).
+ ba 3f
+ addq 1,$r3
+ fail
+
+ moveq 0,$r3
+4:
+; Long forward branch.
+ ba 5f
+ addq 1,$r3
+ fail
+
+ .space 256-2-smalloffset+4b-.,0
+
+ moveq 0,$r3
+
+; Max short backward branch.
+3:
+ ba 4b
+ addq 1,$r3
+ fail
+
+5:
+; Max long forward branch.
+ ba 6f
+ addq 1,$r3
+ fail
+
+ .space 32766+largeoffset-2+5b-.,0
+
+ moveq 0,$r3
+6:
+; Transit branch.
+ ba 7f
+ addq 1,$r3
+ fail
+
+ moveq 0,$r3
+9:
+ jsr pass
+ nop
+
+; Transit branch.
+ moveq 0,$r3
+7:
+ ba 8f
+ addq 1,$r3
+ fail
+
+ .space 32768-largeoffset+9b-.,0
+
+8:
+; Max long backward branch.
+ ba 9b
+ addq 1,$r3
+ fail
diff --git a/tests/cris/check_bas.s b/tests/cris/check_bas.s
new file mode 100644
index 0000000..11929d4
--- /dev/null
+++ b/tests/cris/check_bas.s
@@ -0,0 +1,102 @@
+# mach: crisv32
+# output: 0\n0\n0\nfb349abc\n0\n12124243\n0\n0\neab5baad\n0\nefb37832\n
+
+ .include "testutils.inc"
+ start
+x:
+ setf zncv
+ bsr 0f
+ nop
+0:
+ test_cc 1 1 1 1
+ move srp,r3
+ sub.d 0b,r3
+ checkr3 0
+
+ bas 1f,mof
+ moveq 0,r0
+6:
+ nop
+ quit
+
+2:
+ move srp,r3
+ sub.d 3f,r3
+ checkr3 0
+ move srp,r4
+ subq 4,r4
+ move.d [r4],r3
+ checkr3 fb349abc
+
+ basc 4f,mof
+ nop
+ .dword 0x12124243
+7:
+ nop
+ quit
+
+8:
+ move mof,r3
+ sub.d 7f,r3
+ checkr3 0
+
+ move mof,r4
+ subq 4,r4
+ move.d [r4],r3
+ checkr3 eab5baad
+
+ jasc 9f,mof
+ nop
+ .dword 0xefb37832
+0:
+ quit
+
+ quit
+9:
+ move mof,r3
+ sub.d 0b,r3
+ checkr3 0
+
+ move mof,r4
+ subq 4,r4
+ move.d [r4],r3
+ checkr3 efb37832
+
+ quit
+
+4:
+ move mof,r3
+ sub.d 7b,r3
+ checkr3 0
+ move mof,r4
+ subq 4,r4
+ move.d [r4],r3
+ checkr3 12124243
+ basc 5f,bz
+ moveq 0,r3
+ .dword 0x7634aeba
+ quit
+
+ .space 32770,0
+1:
+ move mof,r3
+ sub.d 6b,r3
+ checkr3 0
+
+ bsrc 2b
+ nop
+ .dword 0xfb349abc
+3:
+
+ quit
+
+5:
+ move mof,r3
+ sub.d 7b,r3
+ checkr3 0
+ move.d 8b,r6
+ jasc r6,mof
+ nop
+ .dword 0xeab5baad
+7:
+ quit
diff --git a/tests/cris/check_bcc.s b/tests/cris/check_bcc.s
new file mode 100644
index 0000000..c57ffa6
--- /dev/null
+++ b/tests/cris/check_bcc.s
@@ -0,0 +1,197 @@
+ .global main
+ .type main, @function
+main:
+ clearf nzvc
+ setf nzv
+ bcc 0f
+ addq 1, $r3
+ jump dofail
+
+0:
+ clearf nzvc
+ setf nzv
+ bcs dofail
+ addq 1,$r3
+
+ clearf nzvc
+ setf ncv
+ bne 1f
+ addq 1, $r3
+
+fail:
+dofail:
+ jump _fail
+
+1:
+ clearf nzvc
+ setf ncv
+ beq dofail
+ addq 1,$r3
+
+ clearf nzvc
+ setf ncz
+ bvc 2f
+ addq 1,$r3
+ jump dofail
+
+2:
+ clearf nzvc
+ setf ncz
+ bvs dofail
+ addq 1,$r3
+
+ clearf nzvc
+ setf vcz
+ bpl 3f
+ addq 1,$r3
+ jump fail
+3:
+ clearf nzvc
+ setf vcz
+ bmi dofail
+ addq 1,$r3
+
+ clearf nzvc
+ setf nv
+ bls dofail
+ addq 1,$r3
+
+ clearf nzvc
+ setf nv
+ bhi 4f
+ addq 1,$r3
+ jump dofail
+
+4:
+ clearf nzvc
+ setf zc
+ bge 5f
+ addq 1,$r3
+ jump dofail
+
+5:
+ clearf nzvc
+ setf zc
+ blt dofail
+ addq 1,$r3
+
+ clearf nzvc
+ setf c
+ bgt 6f
+ addq 1,$r3
+ jump fail
+
+6:
+ clearf nzvc
+ setf c
+ ble dofail
+ addq 1,$r3
+
+;;;;;;;;;;
+
+ setf nzvc
+ clearf nzv
+ bcc dofail
+ addq 1,$r3
+
+ setf nzvc
+ clearf nzv
+ bcs 0f
+ addq 1,$r3
+ jump fail
+
+0:
+ setf nzvc
+ clearf ncv
+ bne dofail
+ addq 1,$r3
+
+ setf nzvc
+ clearf ncv
+ beq 1f
+ addq 1,$r3
+ jump fail
+
+1:
+ setf nzvc
+ clearf ncz
+ bvc dofail
+ addq 1,$r3
+
+ setf nzvc
+ clearf ncz
+ bvs 2f
+ addq 1,$r3
+ jump fail
+
+2:
+ setf nzvc
+ clearf vcz
+ bpl dofail
+ addq 1,$r3
+
+ setf nzvc
+ clearf vcz
+ bmi 3f
+ addq 1,$r3
+ jump fail
+
+3:
+ setf nzvc
+ clearf nv
+ bls 4f
+ addq 1,$r3
+ jump fail
+
+4:
+ setf nzvc
+ clearf nv
+ bhi dofail
+ addq 1,$r3
+
+ setf zvc
+ clearf nzc
+ bge dofail
+ addq 1,$r3
+
+ setf nzc
+ clearf vzc
+ blt 5f
+ addq 1,$r3
+ jump fail
+
+5:
+ setf nzvc
+ clearf c
+ bgt dofail
+ addq 1,$r3
+
+ setf nzvc
+ clearf c
+ ble 6f
+ addq 1,$r3
+ jump fail
+
+6:
+ ; do a forward branch.
+ ba 2f
+ nop
+ .fill 100
+1:
+ ba 3f
+ nop
+ .fill 800
+2:
+ ba 1b
+ nop
+ .fill 1024
+3:
+
+ moveq 31, $r0
+1: bne 1b
+ subq 1, $r0
+
+ jsr pass
+ moveq 0, $r10
+ ret
+ nop
diff --git a/tests/cris/check_bound.c b/tests/cris/check_bound.c
new file mode 100644
index 0000000..e883175
--- /dev/null
+++ b/tests/cris/check_bound.c
@@ -0,0 +1,142 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+static inline int cris_bound_b(int v, int b)
+{
+ int r = v;
+ asm ("bound.b\t%1, %0\n" : "+r" (r) : "ri" (b));
+ return r;
+}
+
+static inline int cris_bound_w(int v, int b)
+{
+ int r = v;
+ asm ("bound.w\t%1, %0\n" : "+r" (r) : "ri" (b));
+ return r;
+}
+
+static inline int cris_bound_d(int v, int b)
+{
+ int r = v;
+ asm ("bound.d\t%1, %0\n" : "+r" (r) : "ri" (b));
+ return r;
+}
+
+int main(void)
+{
+ int r;
+
+ cris_tst_cc_init();
+ r = cris_bound_d(-1, 2);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 2)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_d(2, 0xffffffff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 2)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_d(0xffff, 0xffff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xffff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_d(-1, 0xffffffff);
+ cris_tst_cc(1, 0, 0, 0);
+ if (r != 0xffffffff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_d(0x78134452, 0x5432f789);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0x5432f789)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_w(-1, 2);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 2)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_w(-1, 0xffff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xffff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_w(2, 0xffff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 2)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_w(0xfedaffff, 0xffff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xffff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_w(0x78134452, 0xf789);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xf789)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(-1, 2);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 2)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(2, 0xff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 2)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(-1, 0xff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(0xff, 0xff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(0xfeda49ff, 0xff);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xff)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(0x78134452, 0x89);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0x89)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_w(0x78134452, 0);
+ cris_tst_cc(0, 1, 0, 0);
+ if (r != 0)
+ err();
+
+ cris_tst_cc_init();
+ r = cris_bound_b(0xffff, -1);
+ cris_tst_cc(0, 0, 0, 0);
+ if (r != 0xff)
+ err();
+
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_boundc.s b/tests/cris/check_boundc.s
new file mode 100644
index 0000000..fb9e5bc
--- /dev/null
+++ b/tests/cris/check_boundc.s
@@ -0,0 +1,101 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\nffff\nffffffff\n5432f789\n2\nffff\n2\nffff\nffff\nf789\n2\n2\nff\nff\nff\n89\n0\nff\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ bound.d 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ bound.d 0xffffffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ bound.d 0xffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r3
+ bound.d 0xffffffff,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ bound.d 0x5432f789,r3
+ test_move_cc 0 0 0 0
+ checkr3 5432f789
+
+ moveq -1,r3
+ bound.w 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq -1,r3
+ bound.w 0xffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq 2,r3
+ bound.w 0xffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ bound.w 0xffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0xfedaffff,r3
+ bound.w 0xffff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0x78134452,r3
+ bound.w 0xf789,r3
+ test_move_cc 0 0 0 0
+ checkr3 f789
+
+ moveq -1,r3
+ bound.b 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ bound.b 0xff,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq -1,r3
+ bound.b 0xff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ff
+
+ move.d 0xff,r3
+ bound.b 0xff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ff
+
+ move.d 0xfeda49ff,r3
+ bound.b 0xff,r3
+ test_move_cc 0 0 0 0
+ checkr3 ff
+
+ move.d 0x78134452,r3
+ bound.b 0x89,r3
+ test_move_cc 0 0 0 0
+ checkr3 89
+
+ bound.w 0,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0xffff,r3
+ bound.b -1,r3
+ test_move_cc 0 0 0 0
+ checkr3 ff
+
+ quit
diff --git a/tests/cris/check_boundr.s b/tests/cris/check_boundr.s
new file mode 100644
index 0000000..5c50cc5
--- /dev/null
+++ b/tests/cris/check_boundr.s
@@ -0,0 +1,125 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\nffff\nffffffff\n5432f789\n2\n2\nffff\nffff\nffff\nf789\n2\n2\nff\nff\n89\nfeda4953\nfeda4962\n0\n0\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ bound.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ moveq -1,r4
+ bound.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ bound.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r4
+ move.d r4,r3
+ bound.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ bound.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5432f789
+
+ moveq -1,r3
+ moveq 2,r4
+ bound.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ moveq -1,r4
+ bound.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq -1,r3
+ bound.w r3,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ bound.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0xfedaffff,r4
+ move.d r4,r3
+ bound.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ bound.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 f789
+
+ moveq -1,r3
+ moveq 2,r4
+ bound.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ moveq 2,r3
+ moveq -1,r4
+ bound.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xff,r4
+ move.d r4,r3
+ bound.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ff
+
+ move.d 0xfeda49ff,r4
+ move.d r4,r3
+ bound.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ bound.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 89
+
+ move.d 0xfeda4956,r3
+ move.d 0xfeda4953,r4
+ bound.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 feda4953
+
+ move.d 0xfeda4962,r3
+ move.d 0xfeda4963,r4
+ bound.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 feda4962
+
+ move.d 0xfeda4956,r3
+ move.d 0,r4
+ bound.d r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0xfeda4956,r4
+ move.d 0,r3
+ bound.d r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_btst.s b/tests/cris/check_btst.s
new file mode 100644
index 0000000..e39fc8f
--- /dev/null
+++ b/tests/cris/check_btst.s
@@ -0,0 +1,96 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1111\n
+
+ .include "testutils.inc"
+ start
+ clearf nzvc
+ moveq -1,r3
+ .if 1 ;..asm.arch.cris.v32
+ .else
+ setf vc
+ .endif
+ btstq 0,r3
+ test_cc 1 0 0 0
+
+ moveq 2,r3
+ btstq 1,r3
+ test_cc 1 0 0 0
+
+ moveq 4,r3
+ btstq 1,r3
+ test_cc 0 1 0 0
+
+ moveq -1,r3
+ btstq 31,r3
+ test_cc 1 0 0 0
+
+ move.d 0x5a67f19f,r3
+ btstq 12,r3
+ test_cc 1 0 0 0
+
+ move.d 0xda67f19f,r3
+ move.d 29,r4
+ btst r4,r3
+ test_cc 0 0 0 0
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ btst r4,r3
+ test_cc 1 0 0 0
+
+ move.d 0xda67f191,r3
+ move.d 33,r4
+ btst r4,r3
+ test_cc 0 0 0 0
+
+ moveq -1,r3
+ moveq 0,r4
+ btst r4,r3
+ test_cc 1 0 0 0
+
+ moveq 2,r3
+ moveq 1,r4
+ btst r4,r3
+ test_cc 1 0 0 0
+
+ moveq -1,r3
+ moveq 31,r4
+ btst r4,r3
+ test_cc 1 0 0 0
+
+ moveq 4,r3
+ btstq 1,r3
+ test_cc 0 1 0 0
+
+ moveq -1,r3
+ moveq 15,r4
+ btst r4,r3
+ test_cc 1 0 0 0
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ btst r4,r3
+ test_cc 1 0 0 0
+
+ move.d 0x5a678000,r3
+ moveq 11,r4
+ btst r4,r3
+ test_cc 0 1 0 0
+
+ move.d 0x5a67f19f,r3
+ btst r3,r3
+ test_cc 0 0 0 0
+
+ move.d 0x1111,r3
+ checkr3 1111
+
+ ; check that X gets cleared and that only the NZ flags are touched.
+ move.d 0xff, $r0
+ move $r0, $ccs
+ btst r3,r3
+ move $ccs, $r0
+ and.d 0xff, $r0
+ cmp.d 0xe3, $r0
+ test_cc 0 1 0 0
+
+ quit
diff --git a/tests/cris/check_clearfv32.s b/tests/cris/check_clearfv32.s
new file mode 100644
index 0000000..4e91360
--- /dev/null
+++ b/tests/cris/check_clearfv32.s
@@ -0,0 +1,19 @@
+# mach: crisv32
+# output: ef\nef\n
+
+; Check that "clearf x" doesn't trivially fail.
+
+ .include "testutils.inc"
+ start
+ setf puixnzvc
+ clearf x ; Actually, x would be cleared by almost-all other insns.
+ move ccs,r3
+ and.d 0xff, $r3
+ checkr3 ef
+
+ setf puixnzvc
+ moveq 0, $r3 ; moveq should only clear the xflag.
+ move ccs,r3
+ and.d 0xff, $r3
+ checkr3 ef
+ quit
diff --git a/tests/cris/check_clrjmp1.s b/tests/cris/check_clrjmp1.s
new file mode 100644
index 0000000..45a7005
--- /dev/null
+++ b/tests/cris/check_clrjmp1.s
@@ -0,0 +1,36 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: ffffff00\n
+
+; A bug resulting in a non-effectual clear.b discovered running the GCC
+; testsuite; jump actually wrote to p0.
+
+ .include "testutils.inc"
+
+ start
+ jump 1f
+ nop
+ .p2align 8
+1:
+ move.d y,r4
+
+ .if 0 ;0 == ..asm.arch.cris.v32
+; There was a bug causing this insn to set special register p0
+; (byte-clear) to 8 (low 8 bits of location after insn).
+ jump [r4+]
+ .endif
+
+1:
+ move.d 0f,r4
+
+; The corresponding bug would cause this insn too, to set p0.
+ jump r4
+ nop
+ quit
+0:
+ moveq -1,r3
+ clear.b r3
+ checkr3 ffffff00
+ quit
+
+y:
+ .dword 1b
diff --git a/tests/cris/check_cmp-2.s b/tests/cris/check_cmp-2.s
new file mode 100644
index 0000000..414d370
--- /dev/null
+++ b/tests/cris/check_cmp-2.s
@@ -0,0 +1,15 @@
+
+
+.include "testutils.inc"
+
+ start
+
+ move.d 4294967283, $r0
+ move.d $r0, $r10
+ cmp.d $r0, $r10
+ beq 1f
+ move.d $r10, $r3
+ fail
+1:
+ pass
+ quit
diff --git a/tests/cris/check_cmpc.s b/tests/cris/check_cmpc.s
new file mode 100644
index 0000000..267c9ba
--- /dev/null
+++ b/tests/cris/check_cmpc.s
@@ -0,0 +1,86 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649282\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ cmp.d -2,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ cmp.d 1,r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ cmp.d -0xffff,r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ moveq -1,r3
+ cmp.d 1,r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ cmp.d -0x5432f789,r3
+ test_cc 1 0 1 1
+ checkr3 78134452
+
+ moveq -1,r3
+ cmp.w -2,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ cmp.w 1,r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ cmp.w 1,r3
+ test_cc 1 0 0 0
+ checkr3 ffff
+
+ move.d 0xfedaffff,r3
+ cmp.w 1,r3
+ test_cc 1 0 0 0
+ checkr3 fedaffff
+
+ move.d 0x78134452,r3
+ cmp.w 0x877,r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ moveq -1,r3
+ cmp.b -2,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ cmp.b 1,r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xff,r3
+ cmp.b 1,r3
+ test_cc 1 0 0 0
+ checkr3 ff
+
+ move.d 0xfeda49ff,r3
+ cmp.b 1,r3
+ test_cc 1 0 0 0
+ checkr3 feda49ff
+
+ move.d 0x78134452,r3
+ cmp.b 0x77,r3
+ test_cc 1 0 0 1
+ checkr3 78134452
+
+ move.d 0x85649282,r3
+ cmp.b 0x82,r3
+ test_cc 0 1 0 0
+ checkr3 85649282
+
+ quit
diff --git a/tests/cris/check_cmpm.s b/tests/cris/check_cmpm.s
new file mode 100644
index 0000000..e4dde15
--- /dev/null
+++ b/tests/cris/check_cmpm.s
@@ -0,0 +1,96 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649222\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword -2,1,-0xffff,1,-0x5432f789
+ .word -2,1,1,0x877
+ .byte -2,1,0x77
+ .byte 0x22
+
+ start
+ moveq -1,r3
+ move.d x,r5
+ cmp.d [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ cmp.d [r5],r3
+ test_cc 0 0 0 0
+ addq 4,r5
+ checkr3 2
+
+ move.d 0xffff,r3
+ cmp.d [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ moveq -1,r3
+ cmp.d [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ cmp.d [r5+],r3
+ test_cc 1 0 1 1
+ checkr3 78134452
+
+ moveq -1,r3
+ cmp.w [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ cmp.w [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ cmp.w [r5],r3
+ test_cc 1 0 0 0
+ checkr3 ffff
+
+ move.d 0xfedaffff,r3
+ cmp.w [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 fedaffff
+
+ move.d 0x78134452,r3
+ cmp.w [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ moveq -1,r3
+ cmp.b [r5],r3
+ test_cc 0 0 0 0
+ addq 1,r5
+ checkr3 ffffffff
+
+ moveq 2,r3
+ cmp.b [r5],r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xff,r3
+ cmp.b [r5],r3
+ test_cc 1 0 0 0
+ checkr3 ff
+
+ move.d 0xfeda49ff,r3
+ cmp.b [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 feda49ff
+
+ move.d 0x78134452,r3
+ cmp.b [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 78134452
+
+ move.d 0x85649222,r3
+ cmp.b [r5],r3
+ test_cc 0 1 0 0
+ checkr3 85649222
+
+ quit
diff --git a/tests/cris/check_cmpq.s b/tests/cris/check_cmpq.s
new file mode 100644
index 0000000..5469141
--- /dev/null
+++ b/tests/cris/check_cmpq.s
@@ -0,0 +1,75 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1\n1f\n1f\nffffffe1\nffffffe1\nffffffe0\n0\n0\nffffffff\nffffffff\n10000\n100\n5678900\n
+
+ .include "testutils.inc"
+ start
+ moveq 1,r3
+ cmpq 1,r3
+ test_cc 0 1 0 0
+ checkr3 1
+
+ cmpq -1,r3
+ test_cc 0 0 0 1
+ checkr3 1
+
+ cmpq 31,r3
+ test_cc 1 0 0 1
+ checkr3 1
+
+ moveq 31,r3
+ cmpq 31,r3
+ test_cc 0 1 0 0
+ checkr3 1f
+
+ cmpq -31,r3
+ test_cc 0 0 0 1
+ checkr3 1f
+
+ movs.b -31,r3
+ cmpq -31,r3
+ test_cc 0 1 0 0
+ checkr3 ffffffe1
+
+ cmpq -32,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffe1
+
+ movs.b -32,r3
+ cmpq -32,r3
+ test_cc 0 1 0 0
+ checkr3 ffffffe0
+
+ moveq 0,r3
+ cmpq 1,r3
+ test_cc 1 0 0 1
+ checkr3 0
+
+ cmpq -32,r3
+ test_cc 0 0 0 1
+ checkr3 0
+
+ moveq -1,r3
+ cmpq 1,r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ cmpq -1,r3
+ test_cc 0 1 0 0
+ checkr3 ffffffff
+
+ move.d 0x10000,r3
+ cmpq 1,r3
+ test_cc 0 0 0 0
+ checkr3 10000
+
+ move.d 0x100,r3
+ cmpq 1,r3
+ test_cc 0 0 0 0
+ checkr3 100
+
+ move.d 0x5678900,r3
+ cmpq 7,r3
+ test_cc 0 0 0 0
+ checkr3 5678900
+
+ quit
diff --git a/tests/cris/check_cmpr.s b/tests/cris/check_cmpr.s
new file mode 100644
index 0000000..b30af7a
--- /dev/null
+++ b/tests/cris/check_cmpr.s
@@ -0,0 +1,102 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649222\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq -2,r4
+ cmp.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ cmp.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ move.d -0xffff,r4
+ cmp.d r4,r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ moveq 1,r4
+ moveq -1,r3
+ cmp.d r4,r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d -0x5432f789,r4
+ move.d 0x78134452,r3
+ cmp.d r4,r3
+ test_cc 1 0 1 1
+ checkr3 78134452
+
+ moveq -1,r3
+ moveq -2,r4
+ cmp.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ cmp.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d 0xffff,r3
+ move.d -0xffff,r4
+ cmp.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 ffff
+
+ move.d 0xfedaffff,r3
+ move.d -0xfedaffff,r4
+ cmp.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 fedaffff
+
+ move.d -0x5432f789,r4
+ move.d 0x78134452,r3
+ cmp.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ moveq -1,r3
+ moveq -2,r4
+ cmp.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ cmp.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 2
+
+ move.d -0xff,r4
+ move.d 0xff,r3
+ cmp.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 ff
+
+ move.d -0xfeda49ff,r4
+ move.d 0xfeda49ff,r3
+ cmp.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 feda49ff
+
+ move.d -0x5432f789,r4
+ move.d 0x78134452,r3
+ cmp.b r4,r3
+ test_cc 1 0 0 1
+ checkr3 78134452
+
+ move.d 0x85649222,r3
+ move.d 0x77445622,r4
+ cmp.b r4,r3
+ test_cc 0 1 0 0
+ checkr3 85649222
+
+ quit
diff --git a/tests/cris/check_cmpxc.s b/tests/cris/check_cmpxc.s
new file mode 100644
index 0000000..b237a93
--- /dev/null
+++ b/tests/cris/check_cmpxc.s
@@ -0,0 +1,92 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\n2\n2\nffff\nffff\nffff\nffff\nffffffff\nffffffff\nffffffff\n78134452\n78134452\n78134452\n78134452\n4452\n80000032\n
+
+ .include "testutils.inc"
+ start
+ moveq 2,r3
+ cmps.b 0xff,r3
+ test_cc 0 0 0 1
+ checkr3 2
+
+ moveq 2,r3
+ cmps.w 0xffff,r3
+ test_cc 0 0 0 1
+ checkr3 2
+
+ moveq 2,r3
+ cmpu.b 0xff,r3
+ test_cc 1 0 0 1
+ checkr3 2
+
+ moveq 2,r3
+ move.d 0xffffffff,r4
+ cmpu.w -1,r3
+ test_cc 1 0 0 1
+ checkr3 2
+
+ move.d 0xffff,r3
+ cmpu.b -1,r3
+ test_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0xffff,r3
+ cmpu.w -1,r3
+ test_cc 0 1 0 0
+ checkr3 ffff
+
+ move.d 0xffff,r3
+ cmps.b 0xff,r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ move.d 0xffff,r3
+ cmps.w 0xffff,r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ moveq -1,r3
+ cmps.b 0xff,r3
+ test_cc 0 1 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ cmps.w 0xff,r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ cmps.w 0xffff,r3
+ test_cc 0 1 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ cmpu.b 0x89,r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ move.d 0x78134452,r3
+ cmps.b 0x89,r3
+ test_cc 0 0 0 1
+ checkr3 78134452
+
+ move.d 0x78134452,r3
+ cmpu.w 0xf789,r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ move.d 0x78134452,r3
+ cmps.w 0xf789,r3
+ test_cc 0 0 0 1
+ checkr3 78134452
+
+ move.d 0x4452,r3
+ cmps.w 0x8002,r3
+ test_cc 0 0 0 1
+ checkr3 4452
+
+ move.d 0x80000032,r3
+ cmpu.w 0x764,r3
+ test_cc 0 0 1 0
+ checkr3 80000032
+
+ quit
diff --git a/tests/cris/check_cmpxm.s b/tests/cris/check_cmpxm.s
new file mode 100644
index 0000000..87ea5bf
--- /dev/null
+++ b/tests/cris/check_cmpxm.s
@@ -0,0 +1,106 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 2\n2\n2\n2\nffff\nffff\nffff\nffff\nffffffff\nffffffff\nffffffff\n78134452\n78134452\n78134452\n78134452\n4452\n80000032\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .byte 0xff
+ .word 0xffff
+ .word 0xff
+ .word 0xffff
+ .byte 0x89
+ .word 0xf789
+ .word 0x8002
+ .word 0x764
+
+ start
+ moveq 2,r3
+ move.d x,r5
+ cmps.b [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 2
+
+ moveq 2,r3
+ cmps.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 2
+
+ moveq 2,r3
+ subq 3,r5
+ cmpu.b [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 2
+
+ moveq 2,r3
+ cmpu.w [r5+],r3
+ test_cc 1 0 0 1
+ subq 3,r5
+ checkr3 2
+
+ move.d 0xffff,r3
+ cmpu.b [r5],r3
+ test_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0xffff,r3
+ cmpu.w [r5],r3
+ test_cc 0 1 0 0
+ checkr3 ffff
+
+ move.d 0xffff,r3
+ cmps.b [r5],r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ move.d 0xffff,r3
+ cmps.w [r5],r3
+ test_cc 0 0 0 1
+ checkr3 ffff
+
+ moveq -1,r3
+ cmps.b [r5],r3
+ test_cc 0 1 0 0
+ addq 3,r5
+ checkr3 ffffffff
+
+ moveq -1,r3
+ cmps.w [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ cmps.w [r5+],r3
+ test_cc 0 1 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ cmpu.b [r5],r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ move.d 0x78134452,r3
+ cmps.b [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 78134452
+
+ move.d 0x78134452,r3
+ cmpu.w [r5],r3
+ test_cc 0 0 0 0
+ checkr3 78134452
+
+ move.d 0x78134452,r3
+ cmps.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 78134452
+
+ move.d 0x4452,r3
+ cmps.w [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 4452
+
+ move.d 0x80000032,r3
+ cmpu.w [r5+],r3
+ test_cc 0 0 1 0
+ checkr3 80000032
+
+ quit
diff --git a/tests/cris/check_dstep.s b/tests/cris/check_dstep.s
new file mode 100644
index 0000000..bd43b83
--- /dev/null
+++ b/tests/cris/check_dstep.s
@@ -0,0 +1,42 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: fffffffc\n4\nffff\nfffffffe\n9bf3911b\n0\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ dstep r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffffffc
+
+ moveq 2,r3
+ moveq -1,r4
+ dstep r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 4
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ dstep r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r4
+ move.d r4,r3
+ dstep r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffffffe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ dstep r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 9bf3911b
+
+ move.d 0xffff,r3
+ move.d 0x1fffe,r4
+ dstep r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_ftag.c b/tests/cris/check_ftag.c
new file mode 100644
index 0000000..908773a
--- /dev/null
+++ b/tests/cris/check_ftag.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+static inline void cris_ftag_i(unsigned int x)
+{
+ register unsigned int v asm("$r10") = x;
+ asm ("ftagi\t[%0]\n" : : "r" (v) );
+}
+static inline void cris_ftag_d(unsigned int x)
+{
+ register unsigned int v asm("$r10") = x;
+ asm ("ftagd\t[%0]\n" : : "r" (v) );
+}
+static inline void cris_fidx_i(unsigned int x)
+{
+ register unsigned int v asm("$r10") = x;
+ asm ("fidxi\t[%0]\n" : : "r" (v) );
+}
+static inline void cris_fidx_d(unsigned int x)
+{
+ register unsigned int v asm("$r10") = x;
+ asm ("fidxd\t[%0]\n" : : "r" (v) );
+}
+
+
+int main(void)
+{
+ cris_ftag_i(0);
+ cris_ftag_d(0);
+ cris_fidx_i(0);
+ cris_fidx_d(0);
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_gcctorture_pr28634-1.c b/tests/cris/check_gcctorture_pr28634-1.c
new file mode 100644
index 0000000..45ecd15
--- /dev/null
+++ b/tests/cris/check_gcctorture_pr28634-1.c
@@ -0,0 +1,15 @@
+/* PR rtl-optimization/28634. On targets with delayed branches,
+ dbr_schedule could do the next iteration's addition in the
+ branch delay slot, then subtract the value again if the branch
+ wasn't taken. This can lead to rounding errors. */
+int x = -1;
+int y = 1;
+int
+main (void)
+{
+ while (y > 0)
+ y += x;
+ if (y != x + 1)
+ abort ();
+ exit (0);
+}
diff --git a/tests/cris/check_gcctorture_pr28634.c b/tests/cris/check_gcctorture_pr28634.c
new file mode 100644
index 0000000..a0c5254
--- /dev/null
+++ b/tests/cris/check_gcctorture_pr28634.c
@@ -0,0 +1,15 @@
+/* PR rtl-optimization/28634. On targets with delayed branches,
+ dbr_schedule could do the next iteration's addition in the
+ branch delay slot, then subtract the value again if the branch
+ wasn't taken. This can lead to rounding errors. */
+double x = -0x1.0p53;
+double y = 1;
+int
+main (void)
+{
+ while (y > 0)
+ y += x;
+ if (y != x + 1)
+ abort ();
+ exit (0);
+}
diff --git a/tests/cris/check_glibc_kernelversion.c b/tests/cris/check_glibc_kernelversion.c
new file mode 100644
index 0000000..fcbc7b0
--- /dev/null
+++ b/tests/cris/check_glibc_kernelversion.c
@@ -0,0 +1,116 @@
+/*
+ * Check the lz insn.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+
+#define __LINUX_KERNEL_VERSION 131584
+
+#define DL_SYSDEP_OSCHECK(FATAL) \
+ do { \
+ /* Test whether the kernel is new enough. This test is only \
+ performed if the library is not compiled to run on all \
+ kernels. */ \
+ if (__LINUX_KERNEL_VERSION > 0) \
+ { \
+ char bufmem[64]; \
+ char *buf = bufmem; \
+ unsigned int version; \
+ int parts; \
+ char *cp; \
+ struct utsname uts; \
+ \
+ /* Try the uname syscall */ \
+ if (__uname (&uts)) \
+ { \
+ /* This was not successful. Now try reading the /proc \
+ filesystem. */ \
+ ssize_t reslen; \
+ int fd = __open ("/proc/sys/kernel/osrelease", O_RDONLY); \
+ if (fd == -1 \
+ || (reslen = __read (fd, bufmem, sizeof (bufmem))) <= 0) \
+ /* This also didn't work. We give up since we cannot \
+ make sure the library can actually work. */ \
+ FATAL ("FATAL: cannot determine library version\n"); \
+ __close (fd); \
+ buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0'; \
+ } \
+ else \
+ buf = uts.release; \
+ \
+ /* Now convert it into a number. The string consists of at most \
+ three parts. */ \
+ version = 0; \
+ parts = 0; \
+ cp = buf; \
+ while ((*cp >= '0') && (*cp <= '9')) \
+ { \
+ unsigned int here = *cp++ - '0'; \
+ \
+ while ((*cp >= '0') && (*cp <= '9')) \
+ { \
+ here *= 10; \
+ here += *cp++ - '0'; \
+ } \
+ \
+ ++parts; \
+ version <<= 8; \
+ version |= here; \
+ \
+ if (*cp++ != '.') \
+ /* Another part following? */ \
+ break; \
+ } \
+ \
+ if (parts < 3) \
+ version <<= 8 * (3 - parts); \
+ \
+ /* Now we can test with the required version. */ \
+ if (version < __LINUX_KERNEL_VERSION) \
+ /* Not sufficent. */ \
+ FATAL ("FATAL: kernel too old\n"); \
+ \
+ _dl_osversion = version; \
+ } \
+ } while (0)
+
+int main(void)
+{
+ char bufmem[64] = "2.6.22";
+ char *buf = bufmem;
+ unsigned int version;
+ int parts;
+ char *cp;
+
+ version = 0;
+ parts = 0;
+ cp = buf;
+ while ((*cp >= '0') && (*cp <= '9'))
+ {
+ unsigned int here = *cp++ - '0';
+
+ while ((*cp >= '0') && (*cp <= '9'))
+ {
+ here *= 10;
+ here += *cp++ - '0';
+ }
+
+ ++parts;
+ version <<= 8;
+ version |= here;
+
+ if (*cp++ != '.')
+ /* Another part following? */
+ break;
+ }
+
+ if (parts < 3)
+ version <<= 8 * (3 - parts);
+ if (version < __LINUX_KERNEL_VERSION)
+ err();
+ pass();
+ exit(0);
+}
diff --git a/tests/cris/check_hello.c b/tests/cris/check_hello.c
new file mode 100644
index 0000000..fb403ba
--- /dev/null
+++ b/tests/cris/check_hello.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <stdlib.h>
+int main ()
+{
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_int64.c b/tests/cris/check_int64.c
new file mode 100644
index 0000000..fc60017
--- /dev/null
+++ b/tests/cris/check_int64.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+
+static inline int64_t add64(const int64_t a, const int64_t b)
+{
+ return a + b;
+}
+
+static inline int64_t sub64(const int64_t a, const int64_t b)
+{
+ return a - b;
+}
+
+int main(void)
+{
+ int64_t a = 1;
+ int64_t b = 2;
+
+ /* FIXME: add some tests. */
+ a = add64(a, b);
+ if (a != 3)
+ err();
+
+ a = sub64(a, b);
+ if (a != 1)
+ err();
+
+ a = add64(a, -4);
+ if (a != -3)
+ err();
+
+ a = add64(a, 3);
+ if (a != 0)
+ err();
+
+ a = 0;
+ a = sub64(a, 1);
+ if (a != -1)
+ err();
+
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_jsr.s b/tests/cris/check_jsr.s
new file mode 100644
index 0000000..1060237
--- /dev/null
+++ b/tests/cris/check_jsr.s
@@ -0,0 +1,85 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 0\n0\n0\n0\n0\n0\n
+
+# Test that jsr Rn and jsr [PC+] work.
+
+ .include "testutils.inc"
+ start
+x:
+ move.d 0f,r6
+ setf nzvc
+ jsr r6
+ .if 1; ..asm.arch.cris.v32
+ nop
+ .endif
+0:
+ test_move_cc 1 1 1 1
+ move srp,r3
+ sub.d 0b,r3
+ checkr3 0
+
+ move.d 1f,r0
+ setf nzvc
+ jsr r0
+ .if 1 ; ..asm.arch.cris.v32
+ moveq 0,r0
+ .endif
+6:
+ nop
+ quit
+
+2:
+ test_move_cc 0 0 0 0
+ move srp,r3
+ sub.d 3f,r3
+ checkr3 0
+ jsr 4f
+ .if 1 ; ..asm.arch.cris.v32
+ nop
+ .endif
+7:
+ nop
+ quit
+
+8:
+ move srp,r3
+ sub.d 7b,r3
+ checkr3 0
+ quit
+
+4:
+ move srp,r3
+ sub.d 7b,r3
+ checkr3 0
+ move.d 5f,r3
+ jump r3
+ .if 1; ..asm.arch.cris.v32
+ moveq 0,r3
+ .endif
+ quit
+
+ .space 32770,0
+1:
+ test_move_cc 1 1 1 1
+ move srp,r3
+ sub.d 6b,r3
+ checkr3 0
+
+ clearf cznv
+ jsr 2b
+ .if 1; ..asm.arch.cris.v32
+ nop
+ .endif
+3:
+
+ quit
+
+5:
+ move srp,r3
+ sub.d 7b,r3
+ checkr3 0
+ jump 8b
+ .if 1 ; ..asm.arch.cris.v32
+ nop
+ .endif
+ quit
diff --git a/tests/cris/check_lapc.s b/tests/cris/check_lapc.s
new file mode 100644
index 0000000..9a6150b
--- /dev/null
+++ b/tests/cris/check_lapc.s
@@ -0,0 +1,78 @@
+# mach: crisv32
+# output: 0\n0\nfffffffa\nfffffffe\nffffffda\n1e\n1e\n0\n
+
+.include "testutils.inc"
+
+; To accommodate dumpr3 with more than one instruction, keep it
+; out of lapc operand ranges and difference calculations.
+
+ start
+ lapc.d 0f,r3
+0:
+ sub.d .,r3
+ checkr3 0
+
+ lapcq 0f,r3
+0:
+ sub.d .,r3
+ checkr3 0
+
+ lapc.d .,r3
+ sub.d .,r3
+ checkr3 fffffffa
+
+ lapcq .,r3
+ sub.d .,r3
+ checkr3 fffffffe
+
+0:
+ .rept 16
+ nop
+ .endr
+ lapc.d 0b,r3
+ sub.d .,r3
+ checkr3 ffffffda
+
+ setf zcvn
+ lapc.d 0f,r3
+ test_cc 1 1 1 1
+ sub.d .,r3
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+0:
+ checkr3 1e
+0:
+ lapcq 0f,r3
+ sub.d 0b,r3
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+0:
+ checkr3 1e
+ clearf cn
+ setf zv
+1:
+ lapcq .,r3
+ test_cc 0 1 1 0
+ sub.d 1b,r3
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_lsl.s b/tests/cris/check_lsl.s
new file mode 100644
index 0000000..9e2ddd7
--- /dev/null
+++ b/tests/cris/check_lsl.s
@@ -0,0 +1,217 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n4\n80000000\nffff8000\n7f19f000\n80000000\n0\n0\n699fc67c\nffffffff\n4\n80000000\nffff8000\n7f19f000\nda670000\nda670000\nda670000\nda67c67c\nffffffff\nfffafffe\n4\nffff0000\nffff8000\n5a67f000\nda67f100\nda67f100\nda67f100\nda67f17c\nfff3faff\nfff3fafe\n4\nffffff00\nffffff00\nffffff80\n5a67f100\n5a67f1f0\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ lslq 0,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ lslq 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 4
+
+ moveq -1,r3
+ lslq 31,r3
+ test_move_cc 1 0 0 0
+ checkr3 80000000
+
+ moveq -1,r3
+ lslq 15,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff8000
+
+ move.d 0x5a67f19f,r3
+ lslq 12,r3
+ test_move_cc 0 0 0 0
+ checkr3 7f19f000
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ lsl.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 80000000
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ lsl.d r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ lsl.d r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ lsl.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 699fc67c
+
+ moveq -1,r3
+ moveq 0,r4
+ lsl.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ lsl.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 4
+
+ moveq -1,r3
+ moveq 31,r4
+ lsl.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 80000000
+
+ moveq -1,r3
+ moveq 15,r4
+ lsl.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff8000
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ lsl.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 7f19f000
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ lsl.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da670000
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ lsl.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da670000
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ lsl.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da670000
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ lsl.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 da67c67c
+
+ moveq -1,r3
+ moveq 0,r4
+ lsl.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0xfffaffff,r3
+ moveq 1,r4
+ lsl.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffafffe
+
+ moveq 2,r3
+ moveq 1,r4
+ lsl.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 4
+
+ moveq -1,r3
+ moveq 31,r4
+ lsl.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffff0000
+
+ moveq -1,r3
+ moveq 15,r4
+ lsl.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff8000
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ lsl.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 5a67f000
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ lsl.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da67f100
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ lsl.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da67f100
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ lsl.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da67f100
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ lsl.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 da67f17c
+
+ move.d 0xfff3faff,r3
+ moveq 0,r4
+ lsl.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fff3faff
+
+ move.d 0xfff3faff,r3
+ moveq 1,r4
+ lsl.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fff3fafe
+
+ moveq 2,r3
+ moveq 1,r4
+ lsl.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 4
+
+ moveq -1,r3
+ moveq 31,r4
+ lsl.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffffff00
+
+ moveq -1,r3
+ moveq 15,r4
+ lsl.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffffff00
+
+ moveq -1,r3
+ moveq 7,r4
+ lsl.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffff80
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ lsl.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 5a67f100
+
+ move.d 0x5a67f19f,r3
+ moveq 4,r4
+ lsl.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 5a67f1f0
+
+ quit
diff --git a/tests/cris/check_lsr.s b/tests/cris/check_lsr.s
new file mode 100644
index 0000000..18fdbef
--- /dev/null
+++ b/tests/cris/check_lsr.s
@@ -0,0 +1,218 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\n1\n1\n1ffff\n5a67f\n1\n0\n0\n3699fc67\nffffffff\n1\n1\n1ffff\n5a67f\nda670000\nda670000\nda670000\nda673c67\nffffffff\nffff7fff\n1\nffff0000\nffff0001\n5a67000f\nda67f100\nda67f100\nda67f100\nda67f127\nffffffff\nffffff7f\n1\nffffff00\nffffff00\nffffff01\n5a67f100\n5a67f109\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ lsrq 0,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ lsrq 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ lsrq 31,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ lsrq 15,r3
+ test_move_cc 0 0 0 0
+ checkr3 1ffff
+
+ move.d 0x5a67f19f,r3
+ lsrq 12,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a67f
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ lsr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ lsr.d r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ lsr.d r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ lsr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 3699fc67
+
+ moveq -1,r3
+ moveq 0,r4
+ lsr.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq 1,r4
+ lsr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ moveq 31,r4
+ lsr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ moveq 15,r4
+ lsr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1ffff
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ lsr.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a67f
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ lsr.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da670000
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ lsr.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da670000
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ lsr.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da670000
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ lsr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 da673c67
+
+ moveq -1,r3
+ moveq 0,r4
+ lsr.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 1,r4
+ lsr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff7fff
+
+ moveq 2,r3
+ moveq 1,r4
+ lsr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+;; FIXME: this was wrong. Z should be set.
+ moveq -1,r3
+ moveq 31,r4
+ lsr.w r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffff0000
+
+ moveq -1,r3
+ moveq 15,r4
+ lsr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0001
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ lsr.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a67000f
+
+ move.d 0xda67f19f,r3
+ move.d 31,r4
+ lsr.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da67f100
+
+ move.d 0xda67f19f,r3
+ move.d 32,r4
+ lsr.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da67f100
+
+ move.d 0xda67f19f,r3
+ move.d 33,r4
+ lsr.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 da67f100
+
+ move.d 0xda67f19f,r3
+ move.d 66,r4
+ lsr.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 da67f127
+
+ moveq -1,r3
+ moveq 0,r4
+ lsr.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 1,r4
+ lsr.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffffff7f
+
+ moveq 2,r3
+ moveq 1,r4
+ lsr.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ moveq -1,r3
+ moveq 31,r4
+ lsr.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffffff00
+
+ moveq -1,r3
+ moveq 15,r4
+ lsr.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffffff00
+
+ moveq -1,r3
+ moveq 7,r4
+ lsr.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffffff01
+
+ move.d 0x5a67f19f,r3
+ moveq 12,r4
+ lsr.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 5a67f100
+
+ move.d 0x5a67f19f,r3
+ moveq 4,r4
+ lsr.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5a67f109
+
+ quit
diff --git a/tests/cris/check_lz.c b/tests/cris/check_lz.c
new file mode 100644
index 0000000..69c2e6d
--- /dev/null
+++ b/tests/cris/check_lz.c
@@ -0,0 +1,49 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+
+static inline int cris_lz(int x)
+{
+ int r;
+ asm ("lz\t%1, %0\n" : "=r" (r) : "r" (x));
+ return r;
+}
+
+void check_lz(void)
+{
+ int i;
+
+ if (cris_lz(0) != 32)
+ err();
+ if (cris_lz(1) != 31)
+ err();
+ if (cris_lz(2) != 30)
+ err();
+ if (cris_lz(4) != 29)
+ err();
+ if (cris_lz(8) != 28)
+ err();
+
+ /* try all positions with a single bit. */
+ for (i = 1; i < 32; i++) {
+ if (cris_lz(1 << (i-1)) != (32 - i))
+ err();
+ }
+
+ /* try all positions with all bits. */
+ for (i = 1; i < 32; i++) {
+ /* split up this computation to clarify it. */
+ uint32_t val;
+ val = (unsigned int)-1 >> (32 - i);
+ if (cris_lz(val) != (32 - i))
+ err();
+ }
+}
+
+int main(void)
+{
+ check_lz();
+ pass();
+ exit(0);
+}
diff --git a/tests/cris/check_mapbrk.c b/tests/cris/check_mapbrk.c
new file mode 100644
index 0000000..1aff762
--- /dev/null
+++ b/tests/cris/check_mapbrk.c
@@ -0,0 +1,39 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Basic sanity check that syscalls to implement malloc (brk, mmap2,
+ munmap) are trivially functional. */
+
+int main ()
+{
+ void *p1, *p2, *p3, *p4, *p5, *p6;
+
+ if ((p1 = malloc (8100)) == NULL
+ || (p2 = malloc (16300)) == NULL
+ || (p3 = malloc (4000)) == NULL
+ || (p4 = malloc (500)) == NULL
+ || (p5 = malloc (1023*1024)) == NULL
+ || (p6 = malloc (8191*1024)) == NULL)
+ {
+ printf ("fail\n");
+ exit (1);
+ }
+
+ free (p1);
+ free (p2);
+ free (p3);
+ free (p4);
+ free (p5);
+ free (p6);
+
+ p1 = malloc (64000);
+ if (p1 == NULL)
+ {
+ printf ("fail\n");
+ exit (1);
+ }
+ free (p1);
+
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_mcp.s b/tests/cris/check_mcp.s
new file mode 100644
index 0000000..e65ccdd
--- /dev/null
+++ b/tests/cris/check_mcp.s
@@ -0,0 +1,49 @@
+# mach: crisv32
+# output: fffffffe\n1\n1ffff\nfffffffe\ncc463bdc\n4c463bdc\n0\n
+
+ .include "testutils.inc"
+ start
+
+; Set R, clear C.
+ move 0x100,ccs
+ moveq -5,r3
+ move 2,mof
+ mcp mof,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+
+ moveq 2,r3
+ move -1,srp
+ mcp srp,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move 0xffff,srp
+ move srp,r3
+ mcp srp,r3
+ test_cc 0 0 0 0
+ checkr3 1ffff
+
+ move -1,mof
+ move mof,r3
+ mcp mof,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+
+ move 0x5432f789,mof
+ move.d 0x78134452,r3
+ mcp mof,r3
+ test_cc 1 0 1 0
+ checkr3 cc463bdc
+
+ move 0x80000000,srp
+ mcp srp,r3
+ test_cc 0 0 1 0
+ checkr3 4c463bdc
+
+ move 0xb3b9c423,srp
+ mcp srp,r3
+ test_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_mmap1.c b/tests/cris/check_mmap1.c
new file mode 100644
index 0000000..b803f0c
--- /dev/null
+++ b/tests/cris/check_mmap1.c
@@ -0,0 +1,48 @@
+/*
+#notarget: cris*-*-elf
+*/
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main (int argc, char *argv[])
+{
+ int fd = open (argv[0], O_RDONLY);
+ struct stat sb;
+ int size;
+ void *a;
+ const char *str = "a string you'll only find in the program";
+
+ if (fd == -1)
+ {
+ perror ("open");
+ abort ();
+ }
+
+ if (fstat (fd, &sb) < 0)
+ {
+ perror ("fstat");
+ abort ();
+ }
+
+ size = sb.st_size;
+
+ /* We want to test mmapping a size that isn't exactly a page. */
+ if ((size & 8191) == 0)
+ size--;
+
+ a = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (memmem (a, size, str, strlen (str) + 1) == NULL)
+ abort ();
+
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_mmap2.c b/tests/cris/check_mmap2.c
new file mode 100644
index 0000000..35139a0
--- /dev/null
+++ b/tests/cris/check_mmap2.c
@@ -0,0 +1,48 @@
+/*
+#notarget: cris*-*-elf
+*/
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main (int argc, char *argv[])
+{
+ int fd = open (argv[0], O_RDONLY);
+ struct stat sb;
+ int size;
+ void *a;
+ const char *str = "a string you'll only find in the program";
+
+ if (fd == -1)
+ {
+ perror ("open");
+ abort ();
+ }
+
+ if (fstat (fd, &sb) < 0)
+ {
+ perror ("fstat");
+ abort ();
+ }
+
+ size = sb.st_size;
+
+ /* We want to test mmapping a size that isn't exactly a page. */
+ if ((size & 8191) == 0)
+ size--;
+
+ a = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+
+ if (memmem (a, size, str, strlen (str) + 1) == NULL)
+ abort ();
+
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_mmap3.c b/tests/cris/check_mmap3.c
new file mode 100644
index 0000000..34401fa
--- /dev/null
+++ b/tests/cris/check_mmap3.c
@@ -0,0 +1,33 @@
+/*
+#notarget: cris*-*-elf
+*/
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main (int argc, char *argv[])
+{
+ volatile unsigned char *a;
+
+ /* Check that we can map a non-multiple of a page and still get a full page. */
+ a = mmap (NULL, 0x4c, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (a == NULL || a == (unsigned char *) -1)
+ abort ();
+
+ a[0] = 0xbe;
+ a[8191] = 0xef;
+ memset ((char *) a + 1, 0, 8190);
+
+ if (a[0] != 0xbe || a[8191] != 0xef)
+ abort ();
+
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_movdelsr1.s b/tests/cris/check_movdelsr1.s
new file mode 100644
index 0000000..300cc87
--- /dev/null
+++ b/tests/cris/check_movdelsr1.s
@@ -0,0 +1,33 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: aa117acd\n
+# output: eeaabb42\n
+
+; Bug with move to special register in delay slot, due to
+; special flush-insn-cache simulator use. Ordinary move worked;
+; special register caused branch to fail.
+
+ .include "testutils.inc"
+ start
+ move -1,srp
+
+ move.d 0xaa117acd,r1
+ moveq 3,r9
+ cmpq 1,r9
+ bhi 0f
+ move.d r1,r3
+
+ fail
+0:
+ checkr3 aa117acd
+
+ move.d 0xeeaabb42,r1
+ moveq 3,r9
+ cmpq 1,r9
+ bhi 0f
+ move r1,srp
+
+ fail
+0:
+ move srp,r3
+ checkr3 eeaabb42
+ quit
diff --git a/tests/cris/check_movecr.s b/tests/cris/check_movecr.s
new file mode 100644
index 0000000..da8ec26
--- /dev/null
+++ b/tests/cris/check_movecr.s
@@ -0,0 +1,37 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: ffffff42\n94\nffff4321\n9234\n76543210\n76540000\n
+
+; Move constant byte, word, dword to register. Check that no extension is
+; performed, that only part of the register is set.
+
+ .include "testutils.inc"
+ startnostack
+ moveq -1,r3
+ move.b 0x42,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffffff42
+
+ moveq 0,r3
+ move.b 0x94,r3
+ test_move_cc 1 0 0 0
+ checkr3 94
+
+ moveq -1,r3
+ move.w 0x4321,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff4321
+
+ moveq 0,r3
+ move.w 0x9234,r3
+ test_move_cc 1 0 0 0
+ checkr3 9234
+
+ move.d 0x76543210,r3
+ test_move_cc 0 0 0 0
+ checkr3 76543210
+
+ move.w 0,r3
+ test_move_cc 0 1 0 0
+ checkr3 76540000
+
+ quit
diff --git a/tests/cris/check_movei.s b/tests/cris/check_movei.s
new file mode 100644
index 0000000..bbfa633
--- /dev/null
+++ b/tests/cris/check_movei.s
@@ -0,0 +1,50 @@
+# mach: crisv32
+# output: fffffffe\n
+# output: fffffffe\n
+
+; Check basic integral-write semantics regarding flags.
+
+ .include "testutils.inc"
+ start
+
+ move.d 0, $r3
+; A write that works. Check that flags are set correspondingly.
+ move.d d,r4
+ ;; store to bring it into the tlb with the right prot bits
+ move.d r3,[r4]
+ moveq -2,r5
+ setf c
+ clearf p
+ move.d [r4],r3
+ ax
+ move.d r5,[r4]
+ move.d [r4],r3
+
+ bcc 0f
+ nop
+ fail
+
+0:
+ checkr3 fffffffe
+
+; A write that fails; check flags too.
+ move.d d,r4
+ moveq 23,r5
+ setf p
+ clearf c
+ move.d [r4],r3
+ ax
+ move.d r5,[r4]
+ move.d [r4],r3
+
+ bcs 0f
+ nop
+ fail
+
+0:
+ checkr3 fffffffe
+ quit
+
+ .data
+d:
+ .dword 42424242
diff --git a/tests/cris/check_movemr.s b/tests/cris/check_movemr.s
new file mode 100644
index 0000000..88489de
--- /dev/null
+++ b/tests/cris/check_movemr.s
@@ -0,0 +1,78 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 12345678\n10234567\n12345678\n12344567\n12344523\n76543210\nffffffaa\naa\n9911\nffff9911\n78\n56\n3456\n6712\n
+
+ .include "testutils.inc"
+ start
+
+ .data
+mem1:
+ .dword 0x12345678
+mem2:
+ .word 0x4567
+mem3:
+ .byte 0x23
+ .dword 0x76543210
+ .byte 0xaa,0x11,0x99
+
+ .text
+ move.d mem1,r2
+ move.d [r2],r3
+ test_move_cc 0 0 0 0
+ checkr3 12345678
+
+ move.d mem2,r3
+ move.d [r3],r3
+ test_move_cc 0 0 0 0
+ checkr3 10234567
+
+ move.d mem1,r2
+ move.d [r2+],r3
+ test_move_cc 0 0 0 0
+ checkr3 12345678
+
+ move.w [r2+],r3
+ test_move_cc 0 0 0 0
+ checkr3 12344567
+
+ move.b [r2+],r3
+ test_move_cc 0 0 0 0
+ checkr3 12344523
+
+ move.d [r2+],r3
+ test_move_cc 0 0 0 0
+ checkr3 76543210
+
+ movs.b [r2],r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffaa
+
+ movu.b [r2+],r3
+ test_move_cc 0 0 0 0
+ checkr3 aa
+
+ movu.w [r2],r3
+ test_move_cc 0 0 0 0
+ checkr3 9911
+
+ movs.w [r2+],r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff9911
+
+ move.d mem1,r13
+ movs.b [r13+],r3
+ test_move_cc 0 0 0 0
+ checkr3 78
+
+ movu.b [r13],r3
+ test_move_cc 0 0 0 0
+ checkr3 56
+
+ movs.w [r13+],r3
+ test_move_cc 0 0 0 0
+ checkr3 3456
+
+ movu.w [r13+],r3
+ test_move_cc 0 0 0 0
+ checkr3 6712
+
+ quit
diff --git a/tests/cris/check_movemrv32.s b/tests/cris/check_movemrv32.s
new file mode 100644
index 0000000..53950ab
--- /dev/null
+++ b/tests/cris/check_movemrv32.s
@@ -0,0 +1,96 @@
+# mach: crisv32
+# output: 15\n7\n2\nffff1234\nb\n16\nf\n2\nffffffef\nf\nffff1234\nf\nfffffff4\nd\nfffffff2\n10\nfffffff2\nd\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword 8,9,10,11
+y:
+ .dword -12,13,-14,15,16
+
+ start
+ moveq 7,r0
+ moveq 2,r1
+ move.d 0xffff1234,r2
+ moveq 21,r3
+ move.d x,r4
+ setf zcvn
+ movem r2,[r4+]
+ test_cc 1 1 1 1
+ subq 12,r4
+
+ checkr3 15
+
+ move.d [r4+],r3
+ checkr3 7
+
+ move.d [r4+],r3
+ checkr3 2
+
+ move.d [r4+],r3
+ checkr3 ffff1234
+
+ move.d [r4+],r3
+ checkr3 b
+
+ subq 16,r4
+ moveq 22,r0
+ moveq 15,r1
+ clearf zcvn
+ movem r0,[r4]
+ test_cc 0 0 0 0
+ move.d [r4+],r3
+ checkr3 16
+
+ move.d r1,r3
+ checkr3 f
+
+ move.d [r4+],r3
+ checkr3 2
+
+ subq 8,r4
+ moveq 10,r2
+ moveq -17,r0
+ clearf zc
+ setf vn
+ movem r1,[r4]
+ test_cc 1 0 1 0
+ move.d [r4+],r3
+ checkr3 ffffffef
+
+ move.d [r4+],r3
+ checkr3 f
+
+ move.d [r4+],r3
+ checkr3 ffff1234
+
+ move.d y,r4
+ setf zc
+ clearf vn
+ movem [r4+],r3
+ test_cc 0 1 0 1
+ checkr3 f
+
+ move.d r0,r3
+ checkr3 fffffff4
+
+ move.d r1,r3
+ checkr3 d
+
+ move.d r2,r3
+ checkr3 fffffff2
+
+ move.d [r4],r3
+ checkr3 10
+
+ subq 8,r4
+ setf zcvn
+ movem [r4+],r0
+ test_cc 1 1 1 1
+ move.d r0,r3
+ checkr3 fffffff2
+
+ move.d r1,r3
+ checkr3 d
+
+ quit
diff --git a/tests/cris/check_moveq.c b/tests/cris/check_moveq.c
new file mode 100644
index 0000000..9f71194
--- /dev/null
+++ b/tests/cris/check_moveq.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+#define cris_moveq(dst, src) \
+ asm volatile ("moveq %1, %0\n" : "=r" (dst) : "i" (src));
+
+
+
+int main(void)
+{
+ int t;
+
+ cris_tst_cc_init();
+ asm volatile ("setf\tzvnc\n");
+ cris_moveq(t, 10);
+ cris_tst_cc(1, 1, 1, 1);
+ if (t != 10)
+ err();
+
+ /* make sure moveq doesnt clobber the zflag. */
+ cris_tst_cc_init();
+ asm volatile ("setf vnc\n");
+ asm volatile ("clearf z\n");
+ cris_moveq(t, 0);
+ cris_tst_cc(1, 0, 1, 1);
+ if (t != 0)
+ err();
+
+ /* make sure moveq doesnt clobber the nflag.
+ Also check large immediates */
+ cris_tst_cc_init();
+ asm volatile ("setf zvc\n");
+ asm volatile ("clearf n\n");
+ cris_moveq(t, -31);
+ cris_tst_cc(0, 1, 1, 1);
+ if (t != -31)
+ err();
+
+ cris_tst_cc_init();
+ asm volatile ("setf nzvc\n");
+ cris_moveq(t, 31);
+ cris_tst_cc(1, 1, 1, 1);
+ if (t != 31)
+ err();
+
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_mover.s b/tests/cris/check_mover.s
new file mode 100644
index 0000000..b4db595
--- /dev/null
+++ b/tests/cris/check_mover.s
@@ -0,0 +1,28 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: ffffff05\nffff0005\n5\nffffff00\n
+
+; Move between registers. Check that just the subreg is copied.
+
+ .include "testutils.inc"
+ startnostack
+ moveq -30,r3
+ moveq 5,r4
+ move.b r4,r3
+ test_move_cc 0 0 0 0 ; FIXME
+ checkr3 ffffff05
+
+ move.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0005
+
+ move.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq -1,r3
+ moveq 0,r4
+ move.b r4,r3
+ test_move_cc 0 1 0 0
+ checkr3 ffffff00
+
+ quit
diff --git a/tests/cris/check_moverm.s b/tests/cris/check_moverm.s
new file mode 100644
index 0000000..eabc958
--- /dev/null
+++ b/tests/cris/check_moverm.s
@@ -0,0 +1,45 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 7823fec2\n10231879\n102318fe\n
+
+ .include "testutils.inc"
+ start
+
+ .data
+mem1:
+ .dword 0x12345678
+mem2:
+ .word 0x4567
+mem3:
+ .byte 0x23
+ .dword 0x76543210
+ .byte 0xaa,0x11,0x99
+
+ .text
+ move.d mem1,r2
+ move.d 0x7823fec2,r4
+ setf nzvc
+ move.d r4,[r2+]
+ test_cc 1 1 1 1
+ subq 4,r2
+ move.d [r2],r3
+ checkr3 7823fec2
+
+ move.d mem2,r3
+ move.d 0x45231879,r4
+ clearf nzvc
+ move.w r4,[r3]
+ test_cc 0 0 0 0
+ move.d [r3],r3
+ checkr3 10231879
+
+ move.d mem2,r2
+ moveq -2,r4
+ clearf nc
+ setf zv
+ move.b r4,[r2+]
+ test_cc 0 1 1 0
+ subq 1,r2
+ move.d [r2],r3
+ checkr3 102318fe
+
+ quit
diff --git a/tests/cris/check_movmp.s b/tests/cris/check_movmp.s
new file mode 100644
index 0000000..7fc11f0
--- /dev/null
+++ b/tests/cris/check_movmp.s
@@ -0,0 +1,131 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: ffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n
+
+# Test generic "move Ps,[]" and "move [],Pd" insns; the ones with
+# functionality common to all models.
+
+ .include "testutils.inc"
+ start
+
+ .data
+filler:
+ .byte 0xaa
+ .word 0x4433
+ .dword 0x55778866
+ .byte 0xcc
+
+ .text
+; Test that writing to zero-registers is a nop
+ .if 0
+ ; We used to just ignore the writes, but now an error is emitted. We
+ ; keep the test-code but disabled, in case we need to change this again.
+ move 0xaa,p0
+ move 0x4433,p4
+ move 0x55774433,p8
+ .endif
+
+ moveq -1,r3
+ setf zcvn
+ clear.b r3
+ test_cc 1 1 1 1
+ checkr3 ffffff00
+
+ moveq -1,r3
+ clearf zcvn
+ clear.w r3
+ test_cc 0 0 0 0
+ checkr3 ffff0000
+
+ moveq -1,r3
+ clear.d r3
+ checkr3 0
+
+; "Write" using ordinary memory references too.
+ .if 0 ; See ".if 0" above.
+ move.d filler,r6
+ move [r6],p0
+ move [r6],p4
+ move [r6],p8
+ .endif
+
+# ffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n
+
+ moveq -1,r3
+ clear.b r3
+ checkr3 ffffff00
+
+ moveq -1,r3
+ clear.w r3
+ checkr3 ffff0000
+
+ moveq -1,r3
+ clear.d r3
+ checkr3 0
+
+; And postincremented.
+ .if 0 ; See ".if 0" above.
+ move [r6+],p0
+ move [r6+],p4
+ move [r6+],p8
+ .endif
+
+# ffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n
+
+ moveq -1,r3
+ clear.b r3
+ checkr3 ffffff00
+
+ moveq -1,r3
+ clear.w r3
+ checkr3 ffff0000
+
+ moveq -1,r3
+ clear.d r3
+ checkr3 0
+
+; Now see that we can write to the registers too.
+# bb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n
+; [PC+]
+ move.d filler,r9
+ move 0xbb113344,srp
+ move srp,r3
+ checkr3 bb113344
+
+; [R+]
+ move [r9+],srp
+ move srp,r3
+ checkr3 664433aa
+
+; [R]
+ move [r9],srp
+ move srp,r3
+ checkr3 cc557788
+
+; And check writing to memory, clear and srp.
+
+ move.d filler,r9
+ move 0xabcde012,srp
+ setf zcvn
+ move srp,[r9+]
+ test_cc 1 1 1 1
+ subq 4,r9
+ move.d [r9],r3
+ checkr3 abcde012
+
+ clearf zcvn
+ clear.b [r9]
+ test_cc 0 0 0 0
+ move.d [r9],r3
+ checkr3 abcde000
+
+ addq 2,r9
+ clear.w [r9+]
+ subq 2,r9
+ move.d [r9],r3
+ checkr3 77880000
+
+ clear.d [r9]
+ move.d [r9],r3
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_movpmv32.s b/tests/cris/check_movpmv32.s
new file mode 100644
index 0000000..daf0970
--- /dev/null
+++ b/tests/cris/check_movpmv32.s
@@ -0,0 +1,35 @@
+# mach: crisv32
+# output: 11223320\nbb113344\naa557711\n
+
+# Test v32-specific special registers. FIXME: more registers.
+
+ .include "testutils.inc"
+ start
+ .data
+store:
+ .dword 0x11223344
+ .dword 0x77665544
+
+ .text
+ moveq -1,r3
+ move.d store,r4
+ move vr,[r4]
+ move [r4+],mof
+ move mof,r3
+ checkr3 11223320
+
+ moveq -1,r3
+ clearf zcvn
+ move 0xbb113344,mof
+ test_cc 0 0 0 0
+ move mof,r3
+ checkr3 bb113344
+
+ setf zcvn
+ move 0xaa557711,mof
+ test_cc 1 1 1 1
+ move mof,[r4]
+ move.d [r4],r3
+ checkr3 aa557711
+
+ quit
diff --git a/tests/cris/check_movpr.s b/tests/cris/check_movpr.s
new file mode 100644
index 0000000..eef9bdb
--- /dev/null
+++ b/tests/cris/check_movpr.s
@@ -0,0 +1,28 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: ffffff00\nffff0000\n0\nbb113344\n
+
+# Test generic "move Ps,Rd" and "move Rs,Pd" insns; the ones with
+# functionality common to all models.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ clear.b r3
+ checkr3 ffffff00
+
+ moveq -1,r3
+ clear.w r3
+ checkr3 ffff0000
+
+ moveq -1,r3
+ clear.d r3
+ checkr3 0
+
+ moveq -1,r3
+ move.d 0xbb113344,r4
+ setf zcvn
+ move r4,srp
+ move srp,r3
+ test_cc 1 1 1 1
+ checkr3 bb113344
+ quit
diff --git a/tests/cris/check_movprv32.s b/tests/cris/check_movprv32.s
new file mode 100644
index 0000000..d0d90e1
--- /dev/null
+++ b/tests/cris/check_movprv32.s
@@ -0,0 +1,21 @@
+# mach: crisv32
+# output: ffffff20\nbb113344\n
+
+# Test v32-specific special registers. FIXME: more registers.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ setf zcvn
+ move vr,r3
+ test_cc 1 1 1 1
+ checkr3 ffffff20
+
+ moveq -1,r3
+ move.d 0xbb113344,r4
+ clearf cvnz
+ move r4,mof
+ test_cc 0 0 0 0
+ move mof,r3
+ checkr3 bb113344
+ quit
diff --git a/tests/cris/check_movscr.s b/tests/cris/check_movscr.s
new file mode 100644
index 0000000..53c8ce6
--- /dev/null
+++ b/tests/cris/check_movscr.s
@@ -0,0 +1,29 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 42\nffffff85\n7685\nffff8765\n0\n
+
+; Move constant byte, word, dword to register. Check that sign-extension
+; is performed.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ movs.b 0x42,r3
+ checkr3 42
+
+ movs.b 0x85,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffff85
+
+ movs.w 0x7685,r3
+ test_move_cc 0 0 0 0
+ checkr3 7685
+
+ movs.w 0x8765,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff8765
+
+ movs.w 0,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_movsm.s b/tests/cris/check_movsm.s
new file mode 100644
index 0000000..7074336
--- /dev/null
+++ b/tests/cris/check_movsm.s
@@ -0,0 +1,44 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 5\nfffffff5\n5\nfffffff5\n0\n
+
+; Movs between registers. Check that sign-extension is performed and the
+; full register is set.
+
+ .include "testutils.inc"
+
+ .data
+x:
+ .byte 5,-11
+ .word 5,-11
+ .word 0
+
+ start
+ move.d x,r5
+
+ moveq -1,r3
+ movs.b [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq 0,r3
+ movs.b [r5],r3
+ test_move_cc 1 0 0 0
+ addq 1,r5
+ checkr3 fffffff5
+
+ moveq -1,r3
+ movs.w [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq 0,r3
+ movs.w [r5],r3
+ test_move_cc 1 0 0 0
+ addq 2,r5
+ checkr3 fffffff5
+
+ movs.w [r5],r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_movsr.s b/tests/cris/check_movsr.s
new file mode 100644
index 0000000..d1889a7
--- /dev/null
+++ b/tests/cris/check_movsr.s
@@ -0,0 +1,46 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 5\nfffffff5\n5\nfffffff5\n0\n
+
+; Movs between registers. Check that sign-extension is performed and the
+; full register is set.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r5
+ moveq 5,r4
+ move.b r4,r5
+ moveq -1,r3
+ movs.b r5,r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq 0,r5
+ moveq -11,r4
+ move.b r4,r5
+ moveq 0,r3
+ movs.b r5,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffffff5
+
+ moveq -1,r5
+ moveq 5,r4
+ move.w r4,r5
+ moveq -1,r3
+ movs.w r5,r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq 0,r5
+ moveq -11,r4
+ move.w r4,r5
+ moveq 0,r3
+ movs.w r5,r3
+ test_move_cc 1 0 0 0
+ checkr3 fffffff5
+
+ moveq 0,r5
+ movs.b r5,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_movucr.s b/tests/cris/check_movucr.s
new file mode 100644
index 0000000..7c8487d
--- /dev/null
+++ b/tests/cris/check_movucr.s
@@ -0,0 +1,33 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 42\n85\n7685\n8765\n0\n
+
+; Move constant byte, word, dword to register. Check that zero-extension
+; is performed.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ movu.b 0x42,r3
+ test_move_cc 0 0 0 0
+ checkr3 42
+
+ moveq -1,r3
+ movu.b 0x85,r3
+ test_move_cc 0 0 0 0
+ checkr3 85
+
+ moveq -1,r3
+ movu.w 0x7685,r3
+ test_move_cc 0 0 0 0
+ checkr3 7685
+
+ moveq -1,r3
+ movu.w 0x8765,r3
+ test_move_cc 0 0 0 0
+ checkr3 8765
+
+ movu.b 0,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_movum.s b/tests/cris/check_movum.s
new file mode 100644
index 0000000..038e539
--- /dev/null
+++ b/tests/cris/check_movum.s
@@ -0,0 +1,40 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 5\nf5\n5\nfff5\n0\n
+
+; Movu between registers. Check that zero-extension is performed and the
+; full register is set.
+
+ .include "testutils.inc"
+
+ .data
+x:
+ .byte 5,-11
+ .word 5,-11
+ .word 0
+
+ start
+ move.d x,r5
+
+ movu.b [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ movu.b [r5],r3
+ test_move_cc 0 0 0 0
+ addq 1,r5
+ checkr3 f5
+
+ movu.w [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ movu.w [r5],r3
+ test_move_cc 0 0 0 0
+ addq 2,r5
+ checkr3 fff5
+
+ movu.w [r5],r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_movur.s b/tests/cris/check_movur.s
new file mode 100644
index 0000000..3ecf475
--- /dev/null
+++ b/tests/cris/check_movur.s
@@ -0,0 +1,45 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 5\nf5\n5\nfff5\n0\n
+
+; Movu between registers. Check that zero-extension is performed and the
+; full register is set.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r5
+ moveq 5,r4
+ move.b r4,r5
+ moveq -1,r3
+ movu.b r5,r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq 0,r5
+ moveq -11,r4
+ move.b r4,r5
+ moveq -1,r3
+ movu.b r5,r3
+ test_move_cc 0 0 0 0
+ checkr3 f5
+
+ moveq -1,r5
+ moveq 5,r4
+ move.w r4,r5
+ moveq -1,r3
+ movu.w r5,r3
+ test_move_cc 0 0 0 0
+ checkr3 5
+
+ moveq 0,r5
+ moveq -11,r4
+ move.w r4,r5
+ moveq -1,r3
+ movu.w r5,r3
+ test_move_cc 0 0 0 0
+ checkr3 fff5
+
+ movu.w 0,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_mulv32.s b/tests/cris/check_mulv32.s
new file mode 100644
index 0000000..f379358
--- /dev/null
+++ b/tests/cris/check_mulv32.s
@@ -0,0 +1,51 @@
+# mach: crisv32
+# output: fffffffe\n
+# output: ffffffff\n
+# output: fffffffe\n
+# output: 1\n
+# output: fffffffe\n
+# output: ffffffff\n
+# output: fffffffe\n
+# output: 1\n
+
+; Check that carry is not modified on v32.
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ setf c
+ muls.d r4,r3
+ test_cc 1 0 0 1
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 2,r4
+ setf c
+ mulu.d r4,r3
+ test_cc 0 0 1 1
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 1
+
+ moveq -1,r3
+ moveq 2,r4
+ clearf c
+ muls.d r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 2,r4
+ clearf c
+ mulu.d r4,r3
+ test_cc 0 0 1 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 1
+
+ quit
diff --git a/tests/cris/check_mulx.s b/tests/cris/check_mulx.s
new file mode 100644
index 0000000..d43241a
--- /dev/null
+++ b/tests/cris/check_mulx.s
@@ -0,0 +1,246 @@
+# mach: crisv10 crisv32
+# output: fffffffe\nffffffff\nfffffffe\n1\nfffffffe\nffffffff\nfffffffe\n1\nfffe0001\n0\nfffe0001\n0\n1\n0\n1\nfffffffe\n193eade2\n277e3a49\n193eade2\n277e3a49\nfffffffe\nffffffff\n1fffe\n0\nfffffffe\nffffffff\n1fffe\n0\n1\n0\nfffe0001\n0\nfdbdade2\nffffffff\n420fade2\n0\nfffffffe\nffffffff\n1fe\n0\nfffffffe\nffffffff\n1fe\n0\n1\n0\nfe01\n0\n1\n0\nfe01\n0\nffffd9e2\nffffffff\n2be2\n0\n0\n0\n0\n0\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq 2,r4
+ muls.d r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 2,r4
+ mulu.d r4,r3
+ test_cc 0 0 1 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 1
+
+ moveq 2,r3
+ moveq -1,r4
+ muls.d r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq -1,r4
+ mulu.d r4,r3
+ test_cc 0 0 1 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 1
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ muls.d r4,r3
+ test_cc 0 0 1 0
+ checkr3 fffe0001
+ move mof,r3
+ checkr3 0
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ mulu.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 fffe0001
+ move mof,r3
+ checkr3 0
+
+ moveq -1,r4
+ move.d r4,r3
+ muls.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+ move mof,r3
+ checkr3 0
+
+ moveq -1,r4
+ move.d r4,r3
+ mulu.d r4,r3
+ test_cc 1 0 1 0
+ checkr3 1
+ move mof,r3
+ checkr3 fffffffe
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ muls.d r4,r3
+ test_cc 0 0 1 0
+ checkr3 193eade2
+ move mof,r3
+ checkr3 277e3a49
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ mulu.d r4,r3
+ test_cc 0 0 1 0
+ checkr3 193eade2
+ move mof,r3
+ checkr3 277e3a49
+
+ move.d 0xffff,r3
+ moveq 2,r4
+ muls.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 2,r4
+ mulu.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+ move mof,r3
+ checkr3 0
+
+ moveq 2,r3
+ move.d 0xffff,r4
+ muls.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq -1,r4
+ mulu.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 1fffe
+ move mof,r3
+ checkr3 0
+
+ move.d 0xffff,r4
+ move.d r4,r3
+ muls.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+ move mof,r3
+ checkr3 0
+
+ moveq -1,r4
+ move.d r4,r3
+ mulu.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 fffe0001
+ move mof,r3
+ checkr3 0
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ muls.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 fdbdade2
+ move mof,r3
+ checkr3 ffffffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ mulu.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 420fade2
+ move mof,r3
+ checkr3 0
+
+ move.d 0xff,r3
+ moveq 2,r4
+ muls.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq -1,r3
+ moveq 2,r4
+ mulu.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 1fe
+ move mof,r3
+ checkr3 0
+
+ moveq 2,r3
+ moveq -1,r4
+ muls.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+ move mof,r3
+ checkr3 ffffffff
+
+ moveq 2,r3
+ moveq -1,r4
+ mulu.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 1fe
+ move mof,r3
+ checkr3 0
+
+ move.d 0xff,r4
+ move.d r4,r3
+ muls.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+ move mof,r3
+ checkr3 0
+
+ moveq -1,r4
+ move.d r4,r3
+ mulu.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 fe01
+ move mof,r3
+ checkr3 0
+
+ move.d 0xfeda49ff,r4
+ move.d r4,r3
+ muls.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+ move mof,r3
+ checkr3 0
+
+ move.d 0xfeda49ff,r4
+ move.d r4,r3
+ mulu.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 fe01
+ move mof,r3
+ checkr3 0
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ muls.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 ffffd9e2
+ move mof,r3
+ checkr3 ffffffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ mulu.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 2be2
+ move mof,r3
+ checkr3 0
+
+ moveq 0,r3
+ move.d 0xf87f4aeb,r4
+ muls.d r4,r3
+ test_cc 0 1 0 0
+ checkr3 0
+ move mof,r3
+ checkr3 0
+
+ move.d 0xf87f4aeb,r3
+ moveq 0,r4
+ mulu.d r4,r3
+ test_cc 0 1 0 0
+ checkr3 0
+ move mof,r3
+ checkr3 0
+
+ quit
diff --git a/tests/cris/check_neg.s b/tests/cris/check_neg.s
new file mode 100644
index 0000000..963c4b6
--- /dev/null
+++ b/tests/cris/check_neg.s
@@ -0,0 +1,104 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: ffffffff\nffffffff\n0\n80000000\n1\nba987655\nffff\nffff\n0\n89ab8000\nffff0001\n45677655\nff\nff\n0\n89abae80\nffffff01\n45678955\n
+
+ .include "testutils.inc"
+ start
+ moveq 0,r3
+ moveq 1,r4
+ neg.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 1,r3
+ moveq 0,r4
+ neg.d r3,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+;; FIXME: this was wrong.
+ moveq 0,r3
+ neg.d r3,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0x80000000,r3
+ neg.d r3,r3
+ test_move_cc 1 0 0 0
+ checkr3 80000000
+
+ moveq -1,r3
+ neg.d r3,r3
+ test_move_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0x456789ab,r3
+ neg.d r3,r3
+ test_move_cc 1 0 0 0
+ checkr3 ba987655
+
+ moveq 0,r3
+ moveq 1,r4
+ neg.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff
+
+ moveq 1,r3
+ moveq 0,r4
+ neg.w r3,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff
+
+ moveq 0,r3
+ neg.w r3,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0x89ab8000,r3
+ neg.w r3,r3
+ test_move_cc 1 0 0 0
+ checkr3 89ab8000
+
+ moveq -1,r3
+ neg.w r3,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0001
+
+ move.d 0x456789ab,r3
+ neg.w r3,r3
+ test_move_cc 0 0 0 0
+ checkr3 45677655
+
+ moveq 0,r3
+ moveq 1,r4
+ neg.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ff
+
+ moveq 1,r3
+ moveq 0,r4
+ neg.b r3,r3
+ test_move_cc 1 0 0 0
+ checkr3 ff
+
+ moveq 0,r3
+ neg.b r3,r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+;; FIXME: was wrong.
+ move.d 0x89abae80,r3
+ neg.b r3,r3
+ test_move_cc 1 0 0 1
+ checkr3 89abae80
+
+ moveq -1,r3
+ neg.b r3,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffffff01
+
+ move.d 0x456789ab,r3
+ neg.b r3,r3
+ test_move_cc 0 0 0 0
+ checkr3 45678955
+
+ quit
diff --git a/tests/cris/check_not.s b/tests/cris/check_not.s
new file mode 100644
index 0000000..33bcf15
--- /dev/null
+++ b/tests/cris/check_not.s
@@ -0,0 +1,31 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: fffffffe\nfffffffd\nffff0f00\n0\n87ecbbad\n
+
+ .include "testutils.inc"
+ start
+ moveq 1,r3
+ not r3
+ test_move_cc 1 0 0 0
+ checkr3 fffffffe
+
+ moveq 2,r3
+ not r3
+ test_move_cc 1 0 0 0
+ checkr3 fffffffd
+
+ move.d 0xf0ff,r3
+ not r3
+ test_move_cc 1 0 0 0
+ checkr3 ffff0f00
+
+ moveq -1,r3
+ not r3
+ test_move_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0x78134452,r3
+ not r3
+ test_move_cc 1 0 0 0
+ checkr3 87ecbbad
+
+ quit
diff --git a/tests/cris/check_openpf1.c b/tests/cris/check_openpf1.c
new file mode 100644
index 0000000..1d71e0b
--- /dev/null
+++ b/tests/cris/check_openpf1.c
@@ -0,0 +1,38 @@
+/* Check that --sysroot is applied to open(2).
+#sim: --sysroot=@exedir@
+
+ We assume, with EXE being the name of the executable:
+ - The simulator executes with cwd the same directory where the executable
+ is located (so argv[0] contains a plain filename without directory
+ components).
+ - There's no /EXE on the host file system. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+int main (int argc, char *argv[])
+{
+ char *fnam = argv[0];
+ FILE *f;
+ if (argv[0][0] != '/')
+ {
+ fnam = malloc (strlen (argv[0]) + 2);
+ if (fnam == NULL)
+ abort ();
+ strcpy (fnam, "/");
+ strcat (fnam, argv[0]);
+ }
+
+ f = fopen (fnam, "rb");
+ if (f == NULL)
+ abort ();
+ close (f);
+
+ /* Cover another execution path. */
+ if (fopen ("/nonexistent", "rb") != NULL
+ || errno != ENOENT)
+ abort ();
+ printf ("pass\n");
+ return 0;
+}
diff --git a/tests/cris/check_openpf2.c b/tests/cris/check_openpf2.c
new file mode 100644
index 0000000..f44a8f3
--- /dev/null
+++ b/tests/cris/check_openpf2.c
@@ -0,0 +1,16 @@
+/* Check that the simulator has chdir:ed to the --sysroot argument
+#sim: --sysroot=@srcdir@
+ (or that --sysroot is applied to relative file paths). */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+int main (int argc, char *argv[])
+{
+ FILE *f = fopen ("check_openpf2.c", "rb");
+ if (f == NULL)
+ abort ();
+ close (f);
+ printf ("pass\n");
+ return 0;
+}
diff --git a/tests/cris/check_openpf3.c b/tests/cris/check_openpf3.c
new file mode 100644
index 0000000..557adee
--- /dev/null
+++ b/tests/cris/check_openpf3.c
@@ -0,0 +1,49 @@
+/* Basic file operations (rename, unlink); once without sysroot. We
+ also test that the simulator has chdir:ed to PREFIX, when defined. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifndef PREFIX
+#define PREFIX
+#endif
+
+void err (const char *s)
+{
+ perror (s);
+ abort ();
+}
+
+int main (int argc, char *argv[])
+{
+ FILE *f;
+ struct stat buf;
+
+ unlink (PREFIX "testfoo2.tmp");
+
+ f = fopen ("testfoo1.tmp", "w");
+ if (f == NULL)
+ err ("open");
+ fclose (f);
+
+ if (rename (PREFIX "testfoo1.tmp", PREFIX "testfoo2.tmp") != 0)
+ err ("rename");
+
+ if (stat (PREFIX "testfoo2.tmp", &buf) != 0
+ || !S_ISREG (buf.st_mode))
+ err ("stat 1");
+
+ if (stat ("testfoo2.tmp", &buf) != 0
+ || !S_ISREG (buf.st_mode))
+ err ("stat 2");
+
+ if (unlink (PREFIX "testfoo2.tmp") != 0)
+ err ("unlink");
+
+ printf ("pass\n");
+ return 0;
+}
diff --git a/tests/cris/check_openpf4.c b/tests/cris/check_openpf4.c
new file mode 100644
index 0000000..8bbee41
--- /dev/null
+++ b/tests/cris/check_openpf4.c
@@ -0,0 +1,5 @@
+/* Basic file operations, now *with* sysroot.
+#sim: --sysroot=@exedir@
+*/
+#define PREFIX "/"
+#include "check_openpf3.c"
diff --git a/tests/cris/check_openpf5.c b/tests/cris/check_openpf5.c
new file mode 100644
index 0000000..1f86ea2
--- /dev/null
+++ b/tests/cris/check_openpf5.c
@@ -0,0 +1,56 @@
+/* Check that TRT happens when error on too many opened files.
+#notarget: cris*-*-elf
+#sim: --sysroot=@exedir@
+*/
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+int main (int argc, char *argv[])
+{
+ int i;
+ int filemax;
+
+#ifdef OPEN_MAX
+ filemax = OPEN_MAX;
+#else
+ filemax = sysconf (_SC_OPEN_MAX);
+#endif
+
+ char *fn = malloc (strlen (argv[0]) + 2);
+ if (fn == NULL)
+ abort ();
+ strcpy (fn, "/");
+ strcat (fn, argv[0]);
+
+ for (i = 0; i < filemax + 1; i++)
+ {
+ if (open (fn, O_RDONLY) < 0)
+ {
+ /* Shouldn't happen too early. */
+ if (i < filemax - 3 - 1)
+ {
+ fprintf (stderr, "i: %d\n", i);
+ abort ();
+ }
+ if (errno != EMFILE)
+ {
+ perror ("open");
+ abort ();
+ }
+ goto ok;
+ }
+ }
+ abort ();
+
+ok:
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_orc.s b/tests/cris/check_orc.s
new file mode 100644
index 0000000..c733f03
--- /dev/null
+++ b/tests/cris/check_orc.s
@@ -0,0 +1,71 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n
+
+ .include "testutils.inc"
+ start
+ moveq 1,r3
+ or.d 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ moveq 2,r3
+ or.d 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xf0ff,r3
+ or.d 0xff0f,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r3
+ or.d -1,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ or.d 0x5432f789,r3
+ test_move_cc 0 0 0 0
+ checkr3 7c33f7db
+
+ move.d 0xffff0001,r3
+ or.w 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0003
+
+ moveq 2,r3
+ or.w 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xfedaffaf,r3
+ or.w 0xff5f,r3
+ test_move_cc 1 0 0 0
+ checkr3 fedaffff
+
+ move.d 0x78134452,r3
+ or.w 0xf789,r3
+ test_move_cc 1 0 0 0
+ checkr3 7813f7db
+
+ moveq 1,r3
+ or.b 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ moveq 2,r3
+ or.b 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xfa3,r3
+ or.b 0x4a,r3
+ test_move_cc 1 0 0 0
+ checkr3 feb
+
+ move.d 0x78134453,r3
+ or.b 0x89,r3
+ test_move_cc 1 0 0 0
+ checkr3 781344db
+
+ quit
diff --git a/tests/cris/check_orm.s b/tests/cris/check_orm.s
new file mode 100644
index 0000000..ee723a6
--- /dev/null
+++ b/tests/cris/check_orm.s
@@ -0,0 +1,75 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword 2,1,0xff0f,-1,0x5432f789
+ .word 2,1,0xff5f,0xf789
+ .byte 2,1,0x4a,0x89
+
+ start
+ moveq 1,r3
+ move.d x,r5
+ or.d [r5+],r3
+ checkr3 3
+
+ moveq 2,r3
+ or.d [r5],r3
+ addq 4,r5
+ checkr3 3
+
+ move.d 0xf0ff,r3
+ or.d [r5+],r3
+ checkr3 ffff
+
+ moveq -1,r3
+ or.d [r5+],r3
+ checkr3 ffffffff
+
+ move.d 0x78134452,r3
+ or.d [r5+],r3
+ checkr3 7c33f7db
+
+ move.d 0xffff0001,r3
+ or.w [r5+],r3
+ checkr3 ffff0003
+
+ moveq 2,r3
+ or.w [r5],r3
+ addq 2,r5
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xfedaffaf,r3
+ or.w [r5+],r3
+ test_move_cc 1 0 0 0
+ checkr3 fedaffff
+
+ move.d 0x78134452,r3
+ or.w [r5+],r3
+ test_move_cc 1 0 0 0
+ checkr3 7813f7db
+
+ moveq 1,r3
+ or.b [r5+],r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ moveq 2,r3
+ or.b [r5],r3
+ addq 1,r5
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xfa3,r3
+ or.b [r5+],r3
+ test_move_cc 1 0 0 0
+ checkr3 feb
+
+ move.d 0x78134453,r3
+ or.b [r5],r3
+ test_move_cc 1 0 0 0
+ checkr3 781344db
+
+ quit
diff --git a/tests/cris/check_orq.s b/tests/cris/check_orq.s
new file mode 100644
index 0000000..5060edc
--- /dev/null
+++ b/tests/cris/check_orq.s
@@ -0,0 +1,41 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 3\n3\nffffffff\nffffffff\n1f\nffffffe0\n7813445e\n
+
+ .include "testutils.inc"
+ start
+ moveq 1,r3
+ orq 2,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ moveq 2,r3
+ orq 1,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xf0ff,r3
+ orq -1,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 0,r3
+ orq -1,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ moveq 0,r3
+ orq 31,r3
+ test_move_cc 0 0 0 0
+ checkr3 1f
+
+ moveq 0,r3
+ orq -32,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffe0
+
+ move.d 0x78134452,r3
+ orq 12,r3
+ test_move_cc 0 0 0 0
+ checkr3 7813445e
+
+ quit
diff --git a/tests/cris/check_orr.s b/tests/cris/check_orr.s
new file mode 100644
index 0000000..a514c11
--- /dev/null
+++ b/tests/cris/check_orr.s
@@ -0,0 +1,84 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n
+
+ .include "testutils.inc"
+ start
+ moveq 1,r3
+ moveq 2,r4
+ or.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ moveq 2,r3
+ moveq 1,r4
+ or.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xff0f,r4
+ move.d 0xf0ff,r3
+ or.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff
+
+ moveq -1,r4
+ move.d r4,r3
+ or.d r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 ffffffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ or.d r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 7c33f7db
+
+ move.d 0xffff0001,r3
+ moveq 2,r4
+ or.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 ffff0003
+
+ moveq 2,r3
+ move.d 0xffff0001,r4
+ or.w r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0xfedaffaf,r3
+ move.d 0xffffff5f,r4
+ or.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 fedaffff
+
+ move.d 0x5432f789,r4
+ move.d 0x78134452,r3
+ or.w r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 7813f7db
+
+ moveq 1,r3
+ move.d 0xffffff02,r4
+ or.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ moveq 2,r3
+ moveq 1,r4
+ or.b r4,r3
+ test_move_cc 0 0 0 0
+ checkr3 3
+
+ move.d 0x4a,r4
+ move.d 0xfa3,r3
+ or.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 feb
+
+ move.d 0x5432f789,r4
+ move.d 0x78134453,r3
+ or.b r4,r3
+ test_move_cc 1 0 0 0
+ checkr3 781344db
+
+ quit
diff --git a/tests/cris/check_ret.s b/tests/cris/check_ret.s
new file mode 100644
index 0000000..b44fb25
--- /dev/null
+++ b/tests/cris/check_ret.s
@@ -0,0 +1,25 @@
+# mach: crisv3 crisv8 crisv10
+# output: 3\n
+
+# Test that ret works.
+
+ .include "testutils.inc"
+ start
+x:
+ moveq 0,r3
+ jsr z
+w:
+ quit
+y:
+ addq 1,r3
+ checkr3 3
+ quit
+
+z:
+ addq 1,r3
+ move srp,r2
+ add.d y-w,r2
+ move r2,srp
+ ret
+ addq 1,r3
+ quit
diff --git a/tests/cris/check_scc.s b/tests/cris/check_scc.s
new file mode 100644
index 0000000..4a8674c
--- /dev/null
+++ b/tests/cris/check_scc.s
@@ -0,0 +1,95 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n0\n1\n0\n1\n0\n1\n0\n0\n1\n1\n0\n1\n0\n1\n0\n1\n0\n0\n1\n0\n1\n1\n0\n1\n0\n0\n1\n1\n0\n1\n1\n0\n
+
+ .include "testutils.inc"
+
+ .macro lcheckr3 v
+ move $ccs, $r9
+ checkr3 \v
+ move $r9, $ccs
+ .endm
+
+ start
+ clearf nzvc
+ scc r3
+ lcheckr3 1
+ scs r3
+ lcheckr3 0
+ sne r3
+ lcheckr3 1
+ seq r3
+ lcheckr3 0
+ svc r3
+ lcheckr3 1
+ svs r3
+ lcheckr3 0
+ spl r3
+ lcheckr3 1
+ smi r3
+ lcheckr3 0
+ sls r3
+ lcheckr3 0
+ shi r3
+ lcheckr3 1
+ sge r3
+ lcheckr3 1
+ slt r3
+ lcheckr3 0
+ sgt r3
+ lcheckr3 1
+ sle r3
+ lcheckr3 0
+ sa r3
+ lcheckr3 1
+ setf nzvc
+ scc r3
+ lcheckr3 0
+ scs r3
+ lcheckr3 1
+ sne r3
+ lcheckr3 0
+ svc r3
+ lcheckr3 0
+ svs r3
+ lcheckr3 1
+ spl r3
+ lcheckr3 0
+ smi r3
+ lcheckr3 1
+ sls r3
+ lcheckr3 1
+ shi r3
+ lcheckr3 0
+ sge r3
+ lcheckr3 1
+ slt r3
+ lcheckr3 0
+ sgt r3
+ lcheckr3 0
+ sle r3
+ lcheckr3 1
+ sa r3
+ lcheckr3 1
+ clearf n
+ sge r3
+ lcheckr3 0
+ slt r3
+ lcheckr3 1
+
+ .if 1 ;..asm.arch.cris.v32
+ setf p
+ ssb r3
+ .else
+ moveq 1,r3
+ .endif
+ lcheckr3 1
+
+ .if 1 ;..asm.arch.cris.v32
+ clearf p
+ ssb r3
+ .else
+ moveq 0,r3
+ .endif
+ lcheckr3 0
+
+ quit
diff --git a/tests/cris/check_settls1.c b/tests/cris/check_settls1.c
new file mode 100644
index 0000000..69d2026
--- /dev/null
+++ b/tests/cris/check_settls1.c
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/syscall.h>
+
+#ifndef SYS_set_thread_area
+#define SYS_set_thread_area 243
+#endif
+
+int main (void)
+{
+ unsigned long tp, old_tp;
+ int ret;
+
+ asm volatile ("move $pid,%0" : "=r" (old_tp));
+ old_tp &= ~0xff;
+
+ ret = syscall (SYS_set_thread_area, 0xf0);
+ if (ret != -1 || errno != EINVAL) {
+ syscall (SYS_set_thread_area, old_tp);
+ perror ("Invalid thread area accepted:");
+ abort();
+ }
+
+ ret = syscall (SYS_set_thread_area, 0xeddeed00);
+ if (ret != 0) {
+ perror ("Valid thread area not accepted: ");
+ abort ();
+ }
+
+ asm volatile ("move $pid,%0" : "=r" (tp));
+ tp &= ~0xff;
+ syscall (SYS_set_thread_area, old_tp);
+
+ if (tp != 0xeddeed00) {
+ * (volatile int *) 0 = 0;
+ perror ("tls2");
+ abort ();
+ }
+
+ printf ("pass\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tests/cris/check_sigalrm.c b/tests/cris/check_sigalrm.c
new file mode 100644
index 0000000..39fa8d9
--- /dev/null
+++ b/tests/cris/check_sigalrm.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#define MAGIC (0xdeadbeef)
+
+int s = 0;
+void sighandler(int sig)
+{
+ s = MAGIC;
+}
+
+int main(int argc, char **argv)
+{
+ int p;
+
+ p = getpid();
+ signal(SIGALRM, sighandler);
+ kill(p, SIGALRM);
+ if (s != MAGIC)
+ return EXIT_FAILURE;
+
+ printf ("passed\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tests/cris/check_stat1.c b/tests/cris/check_stat1.c
new file mode 100644
index 0000000..2e2cae5
--- /dev/null
+++ b/tests/cris/check_stat1.c
@@ -0,0 +1,16 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main (void)
+{
+ struct stat buf;
+
+ if (stat (".", &buf) != 0
+ || !S_ISDIR (buf.st_mode))
+ abort ();
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_stat2.c b/tests/cris/check_stat2.c
new file mode 100644
index 0000000..e36172e
--- /dev/null
+++ b/tests/cris/check_stat2.c
@@ -0,0 +1,20 @@
+/*
+#notarget: cris*-*-elf
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main (void)
+{
+ struct stat buf;
+
+ if (lstat (".", &buf) != 0
+ || !S_ISDIR (buf.st_mode))
+ abort ();
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_stat3.c b/tests/cris/check_stat3.c
new file mode 100644
index 0000000..3b5b217
--- /dev/null
+++ b/tests/cris/check_stat3.c
@@ -0,0 +1,25 @@
+/* Simulator options:
+#sim: --sysroot=@exedir@
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int main (int argc, char *argv[])
+{
+ char path[1024] = "/";
+ struct stat buf;
+
+ strcat (path, argv[0]);
+ if (stat (".", &buf) != 0
+ || !S_ISDIR (buf.st_mode))
+ abort ();
+ if (stat (path, &buf) != 0
+ || !S_ISREG (buf.st_mode))
+ abort ();
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_stat4.c b/tests/cris/check_stat4.c
new file mode 100644
index 0000000..e1955ca
--- /dev/null
+++ b/tests/cris/check_stat4.c
@@ -0,0 +1,27 @@
+/* Simulator options:
+#notarget: cris*-*-elf
+#sim: --sysroot=@exedir@
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int main (int argc, char *argv[])
+{
+ char path[1024] = "/";
+ struct stat buf;
+
+ strcat (path, argv[0]);
+ if (lstat (".", &buf) != 0
+ || !S_ISDIR (buf.st_mode))
+ abort ();
+ if (lstat (path, &buf) != 0
+ || !S_ISREG (buf.st_mode))
+ abort ();
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_subc.s b/tests/cris/check_subc.s
new file mode 100644
index 0000000..e34b544
--- /dev/null
+++ b/tests/cris/check_subc.s
@@ -0,0 +1,87 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n
+
+ .include "testutils.inc"
+ start
+
+ moveq -1,r3
+ sub.d -2,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ moveq 2,r3
+ sub.d 1,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xffff,r3
+ sub.d -0xffff,r3
+ test_cc 0 0 0 1
+ checkr3 1fffe
+
+ moveq -1,r3
+ sub.d 1,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+
+ move.d 0x78134452,r3
+ sub.d -0x5432f789,r3
+ test_cc 1 0 1 1
+ checkr3 cc463bdb
+
+ moveq -1,r3
+ sub.w -2,r3
+ test_cc 0 0 0 0
+ checkr3 ffff0001
+
+ moveq 2,r3
+ sub.w 1,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xffff,r3
+ sub.w 1,r3
+ test_cc 1 0 0 0
+ checkr3 fffe
+
+ move.d 0xfedaffff,r3
+ sub.w 1,r3
+ test_cc 1 0 0 0
+ checkr3 fedafffe
+
+ move.d 0x78134452,r3
+ sub.w 0x877,r3
+ test_cc 0 0 0 0
+ checkr3 78133bdb
+
+ moveq -1,r3
+ sub.b -2,r3
+ test_cc 0 0 0 0
+ checkr3 ffffff01
+
+ moveq 2,r3
+ sub.b 1,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xff,r3
+ sub.b 1,r3
+ test_cc 1 0 0 0
+ checkr3 fe
+
+ move.d 0xfeda49ff,r3
+ sub.b 1,r3
+ test_cc 1 0 0 0
+ checkr3 feda49fe
+
+ move.d 0x78134452,r3
+ sub.b 0x77,r3
+ test_cc 1 0 0 1
+ checkr3 781344db
+
+ move.d 0x85649282,r3
+ sub.b 0x82,r3
+ test_cc 0 1 0 0
+ checkr3 85649200
+
+ quit
diff --git a/tests/cris/check_subm.s b/tests/cris/check_subm.s
new file mode 100644
index 0000000..e07ea02
--- /dev/null
+++ b/tests/cris/check_subm.s
@@ -0,0 +1,96 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n
+
+ .include "testutils.inc"
+ .data
+x:
+ .dword -2,1,-0xffff,1,-0x5432f789
+ .word -2,1,1,0x877
+ .byte -2,1,0x77
+ .byte 0x22
+
+ start
+ moveq -1,r3
+ move.d x,r5
+ sub.d [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ moveq 2,r3
+ sub.d [r5],r3
+ test_cc 0 0 0 0
+ addq 4,r5
+ checkr3 1
+
+ move.d 0xffff,r3
+ sub.d [r5+],r3
+ test_cc 0 0 0 1
+ checkr3 1fffe
+
+ moveq -1,r3
+ sub.d [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+
+ move.d 0x78134452,r3
+ sub.d [r5+],r3
+ test_cc 1 0 1 1
+ checkr3 cc463bdb
+
+ moveq -1,r3
+ sub.w [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 ffff0001
+
+ moveq 2,r3
+ sub.w [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xffff,r3
+ sub.w [r5],r3
+ test_cc 1 0 0 0
+ checkr3 fffe
+
+ move.d 0xfedaffff,r3
+ sub.w [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 fedafffe
+
+ move.d 0x78134452,r3
+ sub.w [r5+],r3
+ test_cc 0 0 0 0
+ checkr3 78133bdb
+
+ moveq -1,r3
+ sub.b [r5],r3
+ test_cc 0 0 0 0
+ addq 1,r5
+ checkr3 ffffff01
+
+ moveq 2,r3
+ sub.b [r5],r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xff,r3
+ sub.b [r5],r3
+ test_cc 1 0 0 0
+ checkr3 fe
+
+ move.d 0xfeda49ff,r3
+ sub.b [r5+],r3
+ test_cc 1 0 0 0
+ checkr3 feda49fe
+
+ move.d 0x78134452,r3
+ sub.b [r5+],r3
+ test_cc 1 0 0 1
+ checkr3 781344db
+
+ move.d 0x85649222,r3
+ sub.b [r5],r3
+ test_cc 0 1 0 0
+ checkr3 85649200
+
+ quit
diff --git a/tests/cris/check_subq.s b/tests/cris/check_subq.s
new file mode 100644
index 0000000..9e34fa3
--- /dev/null
+++ b/tests/cris/check_subq.s
@@ -0,0 +1,52 @@
+# mach: crisv3 crisv8 crisv10 crisv32
+# output: 0\nffffffff\nfffffffe\nffff\nff\n56788f9\n56788d9\n567889a\n0\n7ffffffc\n
+
+ .include "testutils.inc"
+ start
+ moveq 1,r3
+ subq 1,r3
+ test_cc 0 1 0 0
+ checkr3 0
+
+ subq 1,r3
+ test_cc 1 0 0 1
+ checkr3 ffffffff
+
+ subq 1,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+
+ move.d 0x10000,r3
+ subq 1,r3
+ test_cc 0 0 0 0
+ checkr3 ffff
+
+ move.d 0x100,r3
+ subq 1,r3
+ test_cc 0 0 0 0
+ checkr3 ff
+
+ move.d 0x5678900,r3
+ subq 7,r3
+ test_cc 0 0 0 0
+ checkr3 56788f9
+
+ subq 32,r3
+ test_cc 0 0 0 0
+ checkr3 56788d9
+
+ subq 63,r3
+ test_cc 0 0 0 0
+ checkr3 567889a
+
+ move.d 34,r3
+ subq 34,r3
+ test_cc 0 1 0 0
+ checkr3 0
+
+ move.d 0x80000024,r3
+ subq 40,r3
+ test_cc 0 0 1 0
+ checkr3 7ffffffc
+
+ quit
diff --git a/tests/cris/check_subr.s b/tests/cris/check_subr.s
new file mode 100644
index 0000000..742fbc8
--- /dev/null
+++ b/tests/cris/check_subr.s
@@ -0,0 +1,102 @@
+# mach: crisv0 crisv3 crisv8 crisv10 crisv32
+# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n
+
+ .include "testutils.inc"
+ start
+ moveq -1,r3
+ moveq -2,r4
+ sub.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ moveq 2,r3
+ moveq 1,r4
+ sub.d r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xffff,r3
+ move.d -0xffff,r4
+ sub.d r4,r3
+ test_cc 0 0 0 1
+ checkr3 1fffe
+
+ moveq 1,r4
+ moveq -1,r3
+ sub.d r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffffffe
+
+ move.d -0x5432f789,r4
+ move.d 0x78134452,r3
+ sub.d r4,r3
+ test_cc 1 0 1 1
+ checkr3 cc463bdb
+
+ moveq -1,r3
+ moveq -2,r4
+ sub.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 ffff0001
+
+ moveq 2,r3
+ moveq 1,r4
+ sub.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d 0xffff,r3
+ move.d -0xffff,r4
+ sub.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 fffe
+
+ move.d 0xfedaffff,r3
+ move.d -0xfedaffff,r4
+ sub.w r4,r3
+ test_cc 1 0 0 0
+ checkr3 fedafffe
+
+ move.d -0x5432f789,r4
+ move.d 0x78134452,r3
+ sub.w r4,r3
+ test_cc 0 0 0 0
+ checkr3 78133bdb
+
+ moveq -1,r3
+ moveq -2,r4
+ sub.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 ffffff01
+
+ moveq 2,r3
+ moveq 1,r4
+ sub.b r4,r3
+ test_cc 0 0 0 0
+ checkr3 1
+
+ move.d -0xff,r4
+ move.d 0xff,r3
+ sub.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 fe
+
+ move.d -0xfeda49ff,r4
+ move.d 0xfeda49ff,r3
+ sub.b r4,r3
+ test_cc 1 0 0 0
+ checkr3 feda49fe
+
+ move.d -0x5432f789,r4
+ move.d 0x78134452,r3
+ sub.b r4,r3
+ test_cc 1 0 0 1
+ checkr3 781344db
+
+ move.d 0x85649222,r3
+ move.d 0x77445622,r4
+ sub.b r4,r3
+ test_cc 0 1 0 0
+ checkr3 85649200
+
+ quit
diff --git a/tests/cris/check_swap.c b/tests/cris/check_swap.c
new file mode 100644
index 0000000..f851cbc
--- /dev/null
+++ b/tests/cris/check_swap.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "sys.h"
+#include "crisutils.h"
+
+#define N 8
+#define W 4
+#define B 2
+#define R 1
+
+static inline int cris_swap(const int mode, int x)
+{
+ switch (mode)
+ {
+ case N: asm ("swapn\t%0\n" : "+r" (x) : "0" (x)); break;
+ case W: asm ("swapw\t%0\n" : "+r" (x) : "0" (x)); break;
+ case B: asm ("swapb\t%0\n" : "+r" (x) : "0" (x)); break;
+ case R: asm ("swapr\t%0\n" : "+r" (x) : "0" (x)); break;
+ case B|R: asm ("swapbr\t%0\n" : "+r" (x) : "0" (x)); break;
+ case W|R: asm ("swapwr\t%0\n" : "+r" (x) : "0" (x)); break;
+ case W|B: asm ("swapwb\t%0\n" : "+r" (x) : "0" (x)); break;
+ case W|B|R: asm ("swapwbr\t%0\n" : "+r" (x) : "0" (x)); break;
+ case N|R: asm ("swapnr\t%0\n" : "+r" (x) : "0" (x)); break;
+ case N|B: asm ("swapnb\t%0\n" : "+r" (x) : "0" (x)); break;
+ case N|B|R: asm ("swapnbr\t%0\n" : "+r" (x) : "0" (x)); break;
+ case N|W: asm ("swapnw\t%0\n" : "+r" (x) : "0" (x)); break;
+ default:
+ err();
+ break;
+ }
+ return x;
+}
+
+/* Made this a macro to be able to pick up the location of the errors. */
+#define verify_swap(mode, val, expected, n, z) \
+do { \
+ int r; \
+ cris_tst_cc_init(); \
+ r = cris_swap(mode, val); \
+ cris_tst_mov_cc(n, z); \
+ if (r != expected) \
+ err(); \
+} while(0)
+
+void check_swap(void)
+{
+ /* Some of these numbers are borrowed from GDB's cris sim
+ testsuite. */
+ if (cris_swap(N, 0) != 0xffffffff)
+ err();
+ if (cris_swap(W, 0x12345678) != 0x56781234)
+ err();
+ if (cris_swap(B, 0x12345678) != 0x34127856)
+ err();
+
+ verify_swap(R, 0x78134452, 0x1ec8224a, 0, 0);
+ verify_swap(B, 0x78134452, 0x13785244, 0, 0);
+ verify_swap(B|R, 0x78134452, 0xc81e4a22, 1, 0);
+ verify_swap(W, 0x78134452, 0x44527813, 0, 0);
+ verify_swap(W|R, 0x78134452, 0x224a1ec8, 0, 0);
+ verify_swap(W|B|R, 0x78134452, 0x4a22c81e, 0, 0);
+ verify_swap(N, 0x78134452, 0x87ecbbad, 1, 0);
+ verify_swap(N|R, 0x78134452, 0xe137ddb5, 1, 0);
+ verify_swap(N|B, 0x78134452, 0xec87adbb, 1, 0);
+ verify_swap(N|B|R, 0x78134452, 0x37e1b5dd, 0, 0);
+ verify_swap(N|W, 0x78134452, 0xbbad87ec, 1, 0);
+ verify_swap(N|B|R, 0xffffffff, 0, 0, 1);
+}
+
+int main(void)
+{
+ check_swap();
+ pass();
+ return 0;
+}
diff --git a/tests/cris/check_time1.c b/tests/cris/check_time1.c
new file mode 100644
index 0000000..3fcf0e1
--- /dev/null
+++ b/tests/cris/check_time1.c
@@ -0,0 +1,46 @@
+/* Basic time functionality test: check that milliseconds are
+ incremented for each syscall (does not work on host). */
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+
+void err (const char *s)
+{
+ perror (s);
+ abort ();
+}
+
+int
+main (void)
+{
+ struct timeval t_m = {0, 0};
+ struct timezone t_z = {0, 0};
+ struct timeval t_m1 = {0, 0};
+ int i;
+
+ if (gettimeofday (&t_m, &t_z) != 0)
+ err ("gettimeofday");
+
+ for (i = 1; i < 10000; i++)
+ if (gettimeofday (&t_m1, NULL) != 0)
+ err ("gettimeofday 1");
+ else
+ if (t_m1.tv_sec * 1000000 + t_m1.tv_usec
+ != (t_m.tv_sec * 1000000 + t_m.tv_usec + i * 1000))
+ {
+ fprintf (stderr, "t0 (%ld, %ld), i %d, t1 (%ld, %ld)\n",
+ t_m.tv_sec, t_m.tv_usec, i, t_m1.tv_sec, t_m1.tv_usec);
+ abort ();
+ }
+
+ if (time (NULL) != t_m1.tv_sec)
+ {
+ fprintf (stderr, "time != gettod\n");
+ abort ();
+ }
+
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_time2.c b/tests/cris/check_time2.c
new file mode 100644
index 0000000..20b69b4
--- /dev/null
+++ b/tests/cris/check_time2.c
@@ -0,0 +1,18 @@
+/* CB_SYS_time doesn't implement the Linux time syscall; the return
+ value isn't written to the argument. */
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+ time_t x = (time_t) -1;
+ time_t t = time (&x);
+
+ if (t == (time_t) -1 || t != x)
+ abort ();
+ printf ("pass\n");
+ exit (0);
+}
diff --git a/tests/cris/check_xarith.s b/tests/cris/check_xarith.s
new file mode 100644
index 0000000..80038b2
--- /dev/null
+++ b/tests/cris/check_xarith.s
@@ -0,0 +1,72 @@
+
+.include "testutils.inc"
+
+ start
+
+ moveq -1, $r0
+ moveq 0, $r1
+ addq 1, $r0
+ ax
+ addq 0, $r1
+
+ move.d $r0, $r3
+ checkr3 0
+ move.d $r1, $r3
+ checkr3 1
+
+ move.d 0, $r0
+ moveq -1, $r1
+ subq 1, $r0
+ ax
+ subq 0, $r1
+
+ move.d $r0, $r3
+ checkr3 ffffffff
+ move.d $r1, $r3
+ checkr3 fffffffe
+
+
+ moveq -1, $r0
+ moveq -1, $r1
+ cmpq -1, $r0
+ ax
+ cmpq -1, $r1
+ beq 1f
+ nop
+ fail
+1:
+ cmpq 0, $r0
+ ax
+ cmpq -1, $r1
+ bne 1f
+ nop
+ fail
+1:
+
+ ;; test for broken X sequence, run it several times.
+ moveq 8, $r0
+1:
+ moveq 0, $r3
+ move.d $r0, $r1
+ andq 1, $r1
+ lslq 4, $r1
+ moveq 1, $r2
+ or.d $r1, $r2
+ ba 2f
+ move $r2, $ccs
+2:
+ addq 0, $r3
+ move.d $r0, $r4
+ move.d $r1, $r5
+ move.d $r2, $r6
+ move.d $r3, $r7
+ lsrq 4, $r1
+ move.d $r1, $r8
+ xor $r1, $r3
+ checkr3 0
+ subq 1, $r0
+ bne 1b
+ nop
+
+ pass
+ quit
diff --git a/tests/cris/crisutils.h b/tests/cris/crisutils.h
new file mode 100644
index 0000000..29b71cd
--- /dev/null
+++ b/tests/cris/crisutils.h
@@ -0,0 +1,71 @@
+static char *tst_cc_loc = NULL;
+
+#define cris_tst_cc_init() \
+do { tst_cc_loc = "test_cc failed at " CURRENT_LOCATION; } while(0)
+
+/* We need a real symbol to signal error. */
+void _err(void) {
+ if (!tst_cc_loc)
+ tst_cc_loc = "tst_cc_failed\n";
+ _fail(tst_cc_loc);
+}
+
+static inline void cris_tst_cc_n1(void)
+{
+ asm volatile ("bpl _err\n"
+ "nop\n");
+}
+static inline void cris_tst_cc_n0(void)
+{
+ asm volatile ("bmi _err\n"
+ "nop\n");
+}
+
+static inline void cris_tst_cc_z1(void)
+{
+ asm volatile ("bne _err\n"
+ "nop\n");
+}
+static inline void cris_tst_cc_z0(void)
+{
+ asm volatile ("beq _err\n"
+ "nop\n");
+}
+static inline void cris_tst_cc_v1(void)
+{
+ asm volatile ("bvc _err\n"
+ "nop\n");
+}
+static inline void cris_tst_cc_v0(void)
+{
+ asm volatile ("bvs _err\n"
+ "nop\n");
+}
+
+static inline void cris_tst_cc_c1(void)
+{
+ asm volatile ("bcc _err\n"
+ "nop\n");
+}
+static inline void cris_tst_cc_c0(void)
+{
+ asm volatile ("bcs _err\n"
+ "nop\n");
+}
+
+static inline void cris_tst_mov_cc(int n, int z)
+{
+ if (n) cris_tst_cc_n1(); else cris_tst_cc_n0();
+ if (z) cris_tst_cc_z1(); else cris_tst_cc_z0();
+ asm volatile ("" : : "g" (_err));
+}
+
+static inline void cris_tst_cc(const int n, const int z,
+ const int v, const int c)
+{
+ if (n) cris_tst_cc_n1(); else cris_tst_cc_n0();
+ if (z) cris_tst_cc_z1(); else cris_tst_cc_z0();
+ if (v) cris_tst_cc_v1(); else cris_tst_cc_v0();
+ if (c) cris_tst_cc_c1(); else cris_tst_cc_c0();
+ asm volatile ("" : : "g" (_err));
+}
diff --git a/tests/cris/crt.s b/tests/cris/crt.s
new file mode 100644
index 0000000..af027d7
--- /dev/null
+++ b/tests/cris/crt.s
@@ -0,0 +1,13 @@
+ .data
+_stack_start:
+ .space 8192, 0
+_stack_end:
+ .text
+ .global _start
+_start:
+ move.d _stack_end, $sp
+ jsr main
+ nop
+ moveq 0, $r10
+ jump exit
+ nop
diff --git a/tests/cris/sys.c b/tests/cris/sys.c
new file mode 100644
index 0000000..551c5dd
--- /dev/null
+++ b/tests/cris/sys.c
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static inline int mystrlen(char *s) {
+ int i = 0;
+ while (s[i])
+ i++;
+ return i;
+}
+
+void pass(void) {
+ char s[] = "passed.\n";
+ write (1, s, sizeof (s) - 1);
+ exit (0);
+}
+
+void _fail(char *reason) {
+ char s[] = "\nfailed: ";
+ int len = mystrlen(reason);
+ write (1, s, sizeof (s) - 1);
+ write (1, reason, len);
+ write (1, "\n", 1);
+// exit (1);
+}
+
+void *memset (void *s, int c, size_t n) {
+ char *p = s;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = c;
+ return p;
+}
+
+void exit (int status) {
+ asm volatile ("moveq 1, $r9\n" /* NR_exit. */
+ "break 13\n");
+ while(1)
+ ;
+}
+
+ssize_t write (int fd, const void *buf, size_t count) {
+ int r;
+ asm ("move.d %0, $r10\n"
+ "move.d %1, $r11\n"
+ "move.d %2, $r12\n"
+ "moveq 4, $r9\n" /* NR_write. */
+ "break 13\n" : : "r" (fd), "r" (buf), "r" (count) : "memory");
+ asm ("move.d $r10, %0\n" : "=r" (r));
+ return r;
+}
diff --git a/tests/cris/sys.h b/tests/cris/sys.h
new file mode 100644
index 0000000..c5f88e1
--- /dev/null
+++ b/tests/cris/sys.h
@@ -0,0 +1,16 @@
+#include <unistd.h>
+
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
+#define CURRENT_LOCATION __FILE__ ":" TOSTRING(__LINE__)
+
+#define err() \
+{ \
+ _fail("at " CURRENT_LOCATION " "); \
+}
+
+#define mb() asm volatile ("" : : : "memory")
+
+void pass(void);
+void _fail(char *reason);
diff --git a/tests/cris/testutils.inc b/tests/cris/testutils.inc
new file mode 100644
index 0000000..aa1641b
--- /dev/null
+++ b/tests/cris/testutils.inc
@@ -0,0 +1,117 @@
+ .syntax no_register_prefix
+
+ .macro start
+ .text
+ .global main
+main:
+ .endm
+
+ .macro quit
+ jump pass
+ nop
+ .endm
+
+ .macro pass
+ jump pass
+ nop
+ .endm
+
+ .macro startnostack
+ start
+ .endm
+
+ .macro fail
+ .data
+99:
+ .asciz " checkr3 failed\n"
+ .text
+ move.d 99b, $r10
+ jsr _fail
+ nop
+ .endm
+
+ .macro checkr3 val
+ cmp.d 0x\val, $r3
+ beq 100f
+ nop
+ .data
+99:
+ .asciz "checkr3 failed\n"
+ .text
+ move.d 99b, $r10
+ jsr _fail
+ nop
+100:
+ .endm
+
+; Test the condition codes
+ .macro test_cc N Z V C
+ .if \N
+ bpl 9f
+ nop
+ .else
+ bmi 9f
+ nop
+ .endif
+ .if \Z
+ bne 9f
+ nop
+ .else
+ beq 9f
+ nop
+ .endif
+ .if \V
+ bvc 9f
+ nop
+ .else
+ bvs 9f
+ nop
+ .endif
+ .if \C
+ bcc 9f
+ nop
+ .else
+ bcs 9f
+ nop
+ .endif
+ ba 8f
+ nop
+9:
+ .data
+99:
+ .asciz "test_move_cc failed\n"
+ .text
+ move.d 99b, $r10
+ jsr _fail
+ nop
+8:
+ .endm
+
+
+ .macro test_move_cc N Z V C
+ .if \N
+ bpl 9f
+ nop
+ .else
+ bmi 9f
+ nop
+ .endif
+ .if \Z
+ bne 9f
+ nop
+ .else
+ beq 9f
+ nop
+ .endif
+ ba 8f
+ nop
+9:
+ .data
+99:
+ .asciz "test_move_cc failed\n"
+ .text
+ move.d 99b, $r10
+ jsr _fail
+ nop
+8:
+ .endm
diff --git a/tests/hello-arm.c b/tests/hello-arm.c
new file mode 100644
index 0000000..e0daa7a
--- /dev/null
+++ b/tests/hello-arm.c
@@ -0,0 +1,113 @@
+#define __NR_SYSCALL_BASE 0x900000
+#define __NR_exit1 (__NR_SYSCALL_BASE+ 1)
+#define __NR_write (__NR_SYSCALL_BASE+ 4)
+
+#define __sys2(x) #x
+#define __sys1(x) __sys2(x)
+
+#ifndef __syscall
+#define __syscall(name) "swi\t" __sys1(__NR_##name) "\n\t"
+#endif
+
+#define __syscall_return(type, res) \
+do { \
+ return (type) (res); \
+} while (0)
+
+#define _syscall0(type,name) \
+type name(void) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ __syscall(name) \
+ "mov %0,r0" \
+ :"=r" (__res) : : "r0","lr"); \
+ __syscall_return(type,__res); \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+type name(type1 arg1) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ __syscall(name) \
+ "mov %0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)) \
+ : "r0","lr"); \
+ __syscall_return(type,__res); \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1,type2 arg2) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)) \
+ : "r0","r1","lr"); \
+ __syscall_return(type,__res); \
+}
+
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1,type2 arg2,type3 arg3) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ "mov\tr2,%3\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)) \
+ : "r0","r1","r2","lr"); \
+ __syscall_return(type,__res); \
+}
+
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ "mov\tr2,%3\n\t" \
+ "mov\tr3,%4\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)) \
+ : "r0","r1","r2","r3","lr"); \
+ __syscall_return(type,__res); \
+}
+
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5) \
+type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \
+ long __res; \
+ __asm__ __volatile__ ( \
+ "mov\tr0,%1\n\t" \
+ "mov\tr1,%2\n\t" \
+ "mov\tr2,%3\n\t" \
+ "mov\tr3,%4\n\t" \
+ "mov\tr4,%5\n\t" \
+ __syscall(name) \
+ "mov\t%0,r0" \
+ : "=r" (__res) \
+ : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)), \
+ "r" ((long)(arg5)) \
+ : "r0","r1","r2","r3","r4","lr"); \
+ __syscall_return(type,__res); \
+}
+
+_syscall1(int,exit1,int,status);
+_syscall3(int,write,int,fd,const char *,buf, int, len);
+
+void _start(void)
+{
+ write(1, "Hello World\n", 12);
+ exit1(0);
+}
diff --git a/tests/hello-i386.c b/tests/hello-i386.c
new file mode 100644
index 0000000..86afc34
--- /dev/null
+++ b/tests/hello-i386.c
@@ -0,0 +1,26 @@
+#include <asm/unistd.h>
+
+static inline volatile void exit(int status)
+{
+ int __res;
+ __asm__ volatile ("movl %%ecx,%%ebx\n"\
+ "int $0x80" \
+ : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status)));
+}
+
+static inline int write(int fd, const char * buf, int len)
+{
+ int status;
+ __asm__ volatile ("pushl %%ebx\n"\
+ "movl %%esi,%%ebx\n"\
+ "int $0x80\n" \
+ "popl %%ebx\n"\
+ : "=a" (status) \
+ : "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len)));
+}
+
+void _start(void)
+{
+ write(1, "Hello World\n", 12);
+ exit(0);
+}
diff --git a/tests/hello-mips.c b/tests/hello-mips.c
new file mode 100644
index 0000000..f825673
--- /dev/null
+++ b/tests/hello-mips.c
@@ -0,0 +1,64 @@
+/*
+* MIPS o32 Linux syscall example
+*
+* http://www.linux-mips.org/wiki/RISC/os
+* http://www.linux-mips.org/wiki/MIPSABIHistory
+* http://www.linux.com/howtos/Assembly-HOWTO/mips.shtml
+*
+* mipsel-linux-gcc -nostdlib -mno-abicalls -fno-PIC -mabi=32 \
+* -O2 -static -o hello-mips hello-mips.c
+*
+*/
+#define __NR_SYSCALL_BASE 4000
+#define __NR_exit (__NR_SYSCALL_BASE+ 1)
+#define __NR_write (__NR_SYSCALL_BASE+ 4)
+
+static inline void exit1(int status)
+{
+ register unsigned long __a0 asm("$4") = (unsigned long) status;
+
+ __asm__ __volatile__ (
+ " .set push \n"
+ " .set noreorder \n"
+ " li $2, %0 \n"
+ " syscall \n"
+ " .set pop "
+ :
+ : "i" (__NR_exit), "r" (__a0)
+ : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24",
+ "memory");
+}
+
+static inline int write(int fd, const char *buf, int len)
+{
+ register unsigned long __a0 asm("$4") = (unsigned long) fd;
+ register unsigned long __a1 asm("$5") = (unsigned long) buf;
+ register unsigned long __a2 asm("$6") = (unsigned long) len;
+ register unsigned long __a3 asm("$7");
+ unsigned long __v0;
+
+ __asm__ __volatile__ (
+ " .set push \n"
+ " .set noreorder \n"
+ " li $2, %2 \n"
+ " syscall \n"
+ " move %0, $2 \n"
+ " .set pop "
+ : "=r" (__v0), "=r" (__a3)
+ : "i" (__NR_write), "r" (__a0), "r" (__a1), "r" (__a2)
+ : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24",
+ "memory");
+
+/* if (__a3 == 0) */
+ return (int) __v0;
+/*
+ errno = __v0;
+ return -1;
+ */
+}
+
+void __start(void)
+{
+ write (1, "Hello, World!\n", 14);
+ exit1 (42);
+}
diff --git a/tests/linux-test.c b/tests/linux-test.c
new file mode 100644
index 0000000..9986e29
--- /dev/null
+++ b/tests/linux-test.c
@@ -0,0 +1,535 @@
+/*
+ * linux and CPU test
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <utime.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sched.h>
+#include <dirent.h>
+#include <setjmp.h>
+#include <sys/shm.h>
+
+#define TESTPATH "/tmp/linux-test.tmp"
+#define TESTPORT 7654
+#define STACK_SIZE 16384
+
+void error1(const char *filename, int line, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", filename, line);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+int __chk_error(const char *filename, int line, int ret)
+{
+ if (ret < 0) {
+ error1(filename, line, "%m (ret=%d, errno=%d)",
+ ret, errno);
+ }
+ return ret;
+}
+
+#define error(fmt, ...) error1(__FILE__, __LINE__, fmt, ## __VA_ARGS__)
+
+#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret))
+
+/*******************************************************/
+
+#define FILE_BUF_SIZE 300
+
+void test_file(void)
+{
+ int fd, i, len, ret;
+ uint8_t buf[FILE_BUF_SIZE];
+ uint8_t buf2[FILE_BUF_SIZE];
+ uint8_t buf3[FILE_BUF_SIZE];
+ char cur_dir[1024];
+ struct stat st;
+ struct utimbuf tbuf;
+ struct iovec vecs[2];
+ DIR *dir;
+ struct dirent *de;
+
+ /* clean up, just in case */
+ unlink(TESTPATH "/file1");
+ unlink(TESTPATH "/file2");
+ unlink(TESTPATH "/file3");
+ rmdir(TESTPATH);
+
+ if (getcwd(cur_dir, sizeof(cur_dir)) == NULL)
+ error("getcwd");
+
+ chk_error(mkdir(TESTPATH, 0755));
+
+ chk_error(chdir(TESTPATH));
+
+ /* open/read/write/close/readv/writev/lseek */
+
+ fd = chk_error(open("file1", O_WRONLY | O_TRUNC | O_CREAT, 0644));
+ for(i=0;i < FILE_BUF_SIZE; i++)
+ buf[i] = i;
+ len = chk_error(write(fd, buf, FILE_BUF_SIZE / 2));
+ if (len != (FILE_BUF_SIZE / 2))
+ error("write");
+ vecs[0].iov_base = buf + (FILE_BUF_SIZE / 2);
+ vecs[0].iov_len = 16;
+ vecs[1].iov_base = buf + (FILE_BUF_SIZE / 2) + 16;
+ vecs[1].iov_len = (FILE_BUF_SIZE / 2) - 16;
+ len = chk_error(writev(fd, vecs, 2));
+ if (len != (FILE_BUF_SIZE / 2))
+ error("writev");
+ chk_error(close(fd));
+
+ chk_error(rename("file1", "file2"));
+
+ fd = chk_error(open("file2", O_RDONLY));
+
+ len = chk_error(read(fd, buf2, FILE_BUF_SIZE));
+ if (len != FILE_BUF_SIZE)
+ error("read");
+ if (memcmp(buf, buf2, FILE_BUF_SIZE) != 0)
+ error("memcmp");
+
+#define FOFFSET 16
+ ret = chk_error(lseek(fd, FOFFSET, SEEK_SET));
+ if (ret != 16)
+ error("lseek");
+ vecs[0].iov_base = buf3;
+ vecs[0].iov_len = 32;
+ vecs[1].iov_base = buf3 + 32;
+ vecs[1].iov_len = FILE_BUF_SIZE - FOFFSET - 32;
+ len = chk_error(readv(fd, vecs, 2));
+ if (len != FILE_BUF_SIZE - FOFFSET)
+ error("readv");
+ if (memcmp(buf + FOFFSET, buf3, FILE_BUF_SIZE - FOFFSET) != 0)
+ error("memcmp");
+
+ chk_error(close(fd));
+
+ /* access */
+ chk_error(access("file2", R_OK));
+
+ /* stat/chmod/utime/truncate */
+
+ chk_error(chmod("file2", 0600));
+ tbuf.actime = 1001;
+ tbuf.modtime = 1000;
+ chk_error(truncate("file2", 100));
+ chk_error(utime("file2", &tbuf));
+ chk_error(stat("file2", &st));
+ if (st.st_size != 100)
+ error("stat size");
+ if (!S_ISREG(st.st_mode))
+ error("stat mode");
+ if ((st.st_mode & 0777) != 0600)
+ error("stat mode2");
+ if (st.st_atime != 1001 ||
+ st.st_mtime != 1000)
+ error("stat time");
+
+ chk_error(stat(TESTPATH, &st));
+ if (!S_ISDIR(st.st_mode))
+ error("stat mode");
+
+ /* fstat */
+ fd = chk_error(open("file2", O_RDWR));
+ chk_error(ftruncate(fd, 50));
+ chk_error(fstat(fd, &st));
+ chk_error(close(fd));
+
+ if (st.st_size != 50)
+ error("stat size");
+ if (!S_ISREG(st.st_mode))
+ error("stat mode");
+
+ /* symlink/lstat */
+ chk_error(symlink("file2", "file3"));
+ chk_error(lstat("file3", &st));
+ if (!S_ISLNK(st.st_mode))
+ error("stat mode");
+
+ /* getdents */
+ dir = opendir(TESTPATH);
+ if (!dir)
+ error("opendir");
+ len = 0;
+ for(;;) {
+ de = readdir(dir);
+ if (!de)
+ break;
+ if (strcmp(de->d_name, ".") != 0 &&
+ strcmp(de->d_name, "..") != 0 &&
+ strcmp(de->d_name, "file2") != 0 &&
+ strcmp(de->d_name, "file3") != 0)
+ error("readdir");
+ len++;
+ }
+ closedir(dir);
+ if (len != 4)
+ error("readdir");
+
+ chk_error(unlink("file3"));
+ chk_error(unlink("file2"));
+ chk_error(chdir(cur_dir));
+ chk_error(rmdir(TESTPATH));
+}
+
+void test_fork(void)
+{
+ int pid, status;
+
+ pid = chk_error(fork());
+ if (pid == 0) {
+ /* child */
+ exit(2);
+ }
+ chk_error(waitpid(pid, &status, 0));
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 2)
+ error("waitpid status=0x%x", status);
+}
+
+void test_time(void)
+{
+ struct timeval tv, tv2;
+ struct timespec ts, rem;
+ struct rusage rusg1, rusg2;
+ int ti, i;
+
+ chk_error(gettimeofday(&tv, NULL));
+ rem.tv_sec = 1;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 20 * 1000000;
+ chk_error(nanosleep(&ts, &rem));
+ if (rem.tv_sec != 1)
+ error("nanosleep");
+ chk_error(gettimeofday(&tv2, NULL));
+ ti = tv2.tv_sec - tv.tv_sec;
+ if (ti >= 2)
+ error("gettimeofday");
+
+ chk_error(getrusage(RUSAGE_SELF, &rusg1));
+ for(i = 0;i < 10000; i++);
+ chk_error(getrusage(RUSAGE_SELF, &rusg2));
+ if ((rusg2.ru_utime.tv_sec - rusg1.ru_utime.tv_sec) < 0 ||
+ (rusg2.ru_stime.tv_sec - rusg1.ru_stime.tv_sec) < 0)
+ error("getrusage");
+}
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int server_socket(void)
+{
+ int val, fd;
+ struct sockaddr_in sockaddr;
+
+ /* server socket */
+ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0));
+
+ val = 1;
+ chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)));
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(TESTPORT);
+ sockaddr.sin_addr.s_addr = 0;
+ chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)));
+ chk_error(listen(fd, 0));
+ return fd;
+
+}
+
+int client_socket(void)
+{
+ int fd;
+ struct sockaddr_in sockaddr;
+
+ /* server socket */
+ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(TESTPORT);
+ inet_aton("127.0.0.1", &sockaddr.sin_addr);
+ chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)));
+ return fd;
+}
+
+const char socket_msg[] = "hello socket\n";
+
+void test_socket(void)
+{
+ int server_fd, client_fd, fd, pid, ret, val;
+ struct sockaddr_in sockaddr;
+ socklen_t len;
+ char buf[512];
+
+ server_fd = server_socket();
+
+ /* test a few socket options */
+ len = sizeof(val);
+ chk_error(getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &val, &len));
+ if (val != SOCK_STREAM)
+ error("getsockopt");
+
+ pid = chk_error(fork());
+ if (pid == 0) {
+ client_fd = client_socket();
+ send(client_fd, socket_msg, sizeof(socket_msg), 0);
+ close(client_fd);
+ exit(0);
+ }
+ len = sizeof(sockaddr);
+ fd = chk_error(accept(server_fd, (struct sockaddr *)&sockaddr, &len));
+
+ ret = chk_error(recv(fd, buf, sizeof(buf), 0));
+ if (ret != sizeof(socket_msg))
+ error("recv");
+ if (memcmp(buf, socket_msg, sizeof(socket_msg)) != 0)
+ error("socket_msg");
+ chk_error(close(fd));
+ chk_error(close(server_fd));
+}
+
+#define WCOUNT_MAX 512
+
+void test_pipe(void)
+{
+ fd_set rfds, wfds;
+ int fds[2], fd_max, ret;
+ uint8_t ch;
+ int wcount, rcount;
+
+ chk_error(pipe(fds));
+ chk_error(fcntl(fds[0], F_SETFL, O_NONBLOCK));
+ chk_error(fcntl(fds[1], F_SETFL, O_NONBLOCK));
+ wcount = 0;
+ rcount = 0;
+ for(;;) {
+ FD_ZERO(&rfds);
+ fd_max = fds[0];
+ FD_SET(fds[0], &rfds);
+
+ FD_ZERO(&wfds);
+ FD_SET(fds[1], &wfds);
+ if (fds[1] > fd_max)
+ fd_max = fds[1];
+
+ ret = chk_error(select(fd_max + 1, &rfds, &wfds, NULL, NULL));
+ if (ret > 0) {
+ if (FD_ISSET(fds[0], &rfds)) {
+ chk_error(read(fds[0], &ch, 1));
+ rcount++;
+ if (rcount >= WCOUNT_MAX)
+ break;
+ }
+ if (FD_ISSET(fds[1], &wfds)) {
+ ch = 'a';
+ chk_error(write(fds[0], &ch, 1));
+ wcount++;
+ }
+ }
+ }
+ chk_error(close(fds[0]));
+ chk_error(close(fds[1]));
+}
+
+int thread1_res;
+int thread2_res;
+
+int thread1_func(void *arg)
+{
+ int i;
+ for(i=0;i<5;i++) {
+ thread1_res++;
+ usleep(10 * 1000);
+ }
+ return 0;
+}
+
+int thread2_func(void *arg)
+{
+ int i;
+ for(i=0;i<6;i++) {
+ thread2_res++;
+ usleep(10 * 1000);
+ }
+ return 0;
+}
+
+void test_clone(void)
+{
+ uint8_t *stack1, *stack2;
+ int pid1, pid2, status1, status2;
+
+ stack1 = malloc(STACK_SIZE);
+ pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE,
+ CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1"));
+
+ stack2 = malloc(STACK_SIZE);
+ pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE,
+ CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2"));
+
+ while (waitpid(pid1, &status1, 0) != pid1);
+ while (waitpid(pid2, &status2, 0) != pid2);
+ if (thread1_res != 5 ||
+ thread2_res != 6)
+ error("clone");
+}
+
+/***********************************/
+
+volatile int alarm_count;
+jmp_buf jmp_env;
+
+void sig_alarm(int sig)
+{
+ if (sig != SIGALRM)
+ error("signal");
+ alarm_count++;
+}
+
+void sig_segv(int sig, siginfo_t *info, void *puc)
+{
+ if (sig != SIGSEGV)
+ error("signal");
+ longjmp(jmp_env, 1);
+}
+
+void test_signal(void)
+{
+ struct sigaction act;
+ struct itimerval it, oit;
+
+ /* timer test */
+
+ alarm_count = 0;
+
+ act.sa_handler = sig_alarm;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ chk_error(sigaction(SIGALRM, &act, NULL));
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 10 * 1000;
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = 10 * 1000;
+ chk_error(setitimer(ITIMER_REAL, &it, NULL));
+ chk_error(getitimer(ITIMER_REAL, &oit));
+ if (oit.it_value.tv_sec != it.it_value.tv_sec ||
+ oit.it_value.tv_usec != it.it_value.tv_usec)
+ error("itimer");
+
+ while (alarm_count < 5) {
+ usleep(10 * 1000);
+ }
+
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 0;
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = 0;
+ memset(&oit, 0xff, sizeof(oit));
+ chk_error(setitimer(ITIMER_REAL, &it, &oit));
+ if (oit.it_value.tv_sec != 0 ||
+ oit.it_value.tv_usec != 10 * 1000)
+ error("setitimer");
+
+ /* SIGSEGV test */
+ act.sa_sigaction = sig_segv;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ chk_error(sigaction(SIGSEGV, &act, NULL));
+ if (setjmp(jmp_env) == 0) {
+ *(uint8_t *)0 = 0;
+ }
+
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ chk_error(sigaction(SIGSEGV, &act, NULL));
+}
+
+#define SHM_SIZE 32768
+
+void test_shm(void)
+{
+ void *ptr;
+ int shmid;
+
+ shmid = chk_error(shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0777));
+ ptr = shmat(shmid, NULL, 0);
+ if (!ptr)
+ error("shmat");
+
+ memset(ptr, 0, SHM_SIZE);
+
+ chk_error(shmctl(shmid, IPC_RMID, 0));
+ chk_error(shmdt(ptr));
+}
+
+int main(int argc, char **argv)
+{
+ test_file();
+ test_fork();
+ test_time();
+ test_socket();
+ // test_clone();
+ test_signal();
+ test_shm();
+ return 0;
+}
diff --git a/tests/pi_10.com b/tests/pi_10.com
new file mode 100644
index 0000000..8993ba1
--- /dev/null
+++ b/tests/pi_10.com
Binary files differ
diff --git a/tests/qruncom.c b/tests/qruncom.c
new file mode 100644
index 0000000..079f7a2
--- /dev/null
+++ b/tests/qruncom.c
@@ -0,0 +1,284 @@
+/*
+ * Example of use of user mode libqemu: launch a basic .com DOS
+ * executable
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <malloc.h>
+
+#include "cpu.h"
+
+//#define SIGTEST
+
+int cpu_get_pic_interrupt(CPUState *env)
+{
+ return -1;
+}
+
+uint64_t cpu_get_tsc(CPUState *env)
+{
+ return 0;
+}
+
+static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
+ unsigned long addr, unsigned int sel)
+{
+ unsigned int e1, e2;
+ e1 = (addr & 0xffff) | (sel << 16);
+ e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
+ stl((uint8_t *)ptr, e1);
+ stl((uint8_t *)ptr + 4, e2);
+}
+
+uint64_t idt_table[256];
+
+/* only dpl matters as we do only user space emulation */
+static void set_idt(int n, unsigned int dpl)
+{
+ set_gate(idt_table + n, 0, dpl, 0, 0);
+}
+
+void qemu_free(void *ptr)
+{
+ free(ptr);
+}
+
+void *qemu_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+void *qemu_mallocz(size_t size)
+{
+ void *ptr;
+ ptr = qemu_malloc(size);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void *qemu_vmalloc(size_t size)
+{
+ return memalign(4096, size);
+}
+
+void qemu_vfree(void *ptr)
+{
+ free(ptr);
+}
+
+void qemu_printf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/* XXX: this is a bug in helper2.c */
+int errno;
+
+/**********************************************/
+
+#define COM_BASE_ADDR 0x10100
+
+static void usage(void)
+{
+ printf("qruncom version 0.1 (c) 2003 Fabrice Bellard\n"
+ "usage: qruncom file.com\n"
+ "user mode libqemu demo: run simple .com DOS executables\n");
+ exit(1);
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+ return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(CPUState *env, int val)
+{
+ env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | ((env->regs[R_ESP] - 2) & 0xffff);
+ *(uint16_t *)seg_to_linear(env->segs[R_SS].selector, env->regs[R_ESP]) = val;
+}
+
+static void host_segv_handler(int host_signum, siginfo_t *info,
+ void *puc)
+{
+ if (cpu_signal_handler(host_signum, info, puc)) {
+ return;
+ }
+ abort();
+}
+
+int main(int argc, char **argv)
+{
+ uint8_t *vm86_mem;
+ const char *filename;
+ int fd, ret, seg;
+ CPUState *env;
+
+ if (argc != 2)
+ usage();
+ filename = argv[1];
+
+ vm86_mem = mmap((void *)0x00000000, 0x110000,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (vm86_mem == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ /* load the MSDOS .com executable */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ exit(1);
+ }
+ ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256);
+ if (ret < 0) {
+ perror("read");
+ exit(1);
+ }
+ close(fd);
+
+ /* install exception handler for CPU emulator */
+ {
+ struct sigaction act;
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ // act.sa_flags |= SA_ONSTACK;
+
+ act.sa_sigaction = host_segv_handler;
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ }
+
+ // cpu_set_log(CPU_LOG_TB_IN_ASM | CPU_LOG_TB_OUT_ASM | CPU_LOG_EXEC);
+
+ env = cpu_init("qemu32");
+
+ cpu_x86_set_cpl(env, 3);
+
+ env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
+ /* NOTE: hflags duplicates some of the virtual CPU state */
+ env->hflags |= HF_PE_MASK | VM_MASK;
+
+ /* flags setup : we activate the IRQs by default as in user
+ mode. We also activate the VM86 flag to run DOS code */
+ env->eflags |= IF_MASK | VM_MASK;
+
+ /* init basic registers */
+ env->eip = 0x100;
+ env->regs[R_ESP] = 0xfffe;
+ seg = (COM_BASE_ADDR - 0x100) >> 4;
+
+ cpu_x86_load_seg_cache(env, R_CS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_SS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_DS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_ES, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_FS, seg,
+ (seg << 4), 0xffff, 0);
+ cpu_x86_load_seg_cache(env, R_GS, seg,
+ (seg << 4), 0xffff, 0);
+
+ /* exception support */
+ env->idt.base = (unsigned long)idt_table;
+ env->idt.limit = sizeof(idt_table) - 1;
+ set_idt(0, 0);
+ set_idt(1, 0);
+ set_idt(2, 0);
+ set_idt(3, 3);
+ set_idt(4, 3);
+ set_idt(5, 3);
+ set_idt(6, 0);
+ set_idt(7, 0);
+ set_idt(8, 0);
+ set_idt(9, 0);
+ set_idt(10, 0);
+ set_idt(11, 0);
+ set_idt(12, 0);
+ set_idt(13, 0);
+ set_idt(14, 0);
+ set_idt(15, 0);
+ set_idt(16, 0);
+ set_idt(17, 0);
+ set_idt(18, 0);
+ set_idt(19, 0);
+
+ /* put return code */
+ *seg_to_linear(env->segs[R_CS].selector, 0) = 0xb4; /* mov ah, $0 */
+ *seg_to_linear(env->segs[R_CS].selector, 1) = 0x00;
+ *seg_to_linear(env->segs[R_CS].selector, 2) = 0xcd; /* int $0x21 */
+ *seg_to_linear(env->segs[R_CS].selector, 3) = 0x21;
+ pushw(env, 0x0000);
+
+ /* the value of these registers seem to be assumed by pi_10.com */
+ env->regs[R_ESI] = 0x100;
+ env->regs[R_ECX] = 0xff;
+ env->regs[R_EBP] = 0x0900;
+ env->regs[R_EDI] = 0xfffe;
+
+ /* inform the emulator of the mmaped memory */
+ page_set_flags(0x00000000, 0x110000,
+ PAGE_WRITE | PAGE_READ | PAGE_EXEC | PAGE_VALID);
+
+ for(;;) {
+ ret = cpu_x86_exec(env);
+ switch(ret) {
+ case EXCP0D_GPF:
+ {
+ int int_num, ah;
+ int_num = *(uint8_t *)(env->segs[R_CS].base + env->eip + 1);
+ if (int_num != 0x21)
+ goto unknown_int;
+ ah = (env->regs[R_EAX] >> 8) & 0xff;
+ switch(ah) {
+ case 0x00: /* exit */
+ exit(0);
+ case 0x02: /* write char */
+ {
+ uint8_t c = env->regs[R_EDX];
+ write(1, &c, 1);
+ }
+ break;
+ case 0x09: /* write string */
+ {
+ uint8_t c;
+ for(;;) {
+ c = *seg_to_linear(env->segs[R_DS].selector, env->regs[R_EAX]);
+ if (c == '$')
+ break;
+ write(1, &c, 1);
+ }
+ env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | '$';
+ }
+ break;
+ default:
+ unknown_int:
+ fprintf(stderr, "unsupported int 0x%02x\n", int_num);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ // exit(1);
+ }
+ env->eip += 2;
+ }
+ break;
+ default:
+ fprintf(stderr, "unhandled cpu_exec return code (0x%x)\n", ret);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit(1);
+ }
+ }
+}
diff --git a/tests/runcom.c b/tests/runcom.c
new file mode 100644
index 0000000..d60342b
--- /dev/null
+++ b/tests/runcom.c
@@ -0,0 +1,192 @@
+/*
+ * Simple example of use of vm86: launch a basic .com DOS executable
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <signal.h>
+
+#include <linux/unistd.h>
+#include <asm/vm86.h>
+
+extern int vm86 (unsigned long int subfunction,
+ struct vm86plus_struct *info);
+
+#define VIF_MASK 0x00080000
+
+//#define SIGTEST
+
+#define COM_BASE_ADDR 0x10100
+
+static void usage(void)
+{
+ printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n"
+ "usage: runcom file.com\n"
+ "VM86 Run simple .com DOS executables (linux vm86 test mode)\n");
+ exit(1);
+}
+
+static inline void set_bit(uint8_t *a, unsigned int bit)
+{
+ a[bit / 8] |= (1 << (bit % 8));
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+ return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(struct vm86_regs *r, int val)
+{
+ r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
+ *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
+}
+
+void dump_regs(struct vm86_regs *r)
+{
+ fprintf(stderr,
+ "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n"
+ "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n"
+ "EIP=%08lx EFL=%08lx\n"
+ "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n",
+ r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp,
+ r->eip, r->eflags,
+ r->cs, r->ds, r->es, r->ss, r->fs, r->gs);
+}
+
+#ifdef SIGTEST
+void alarm_handler(int sig)
+{
+ fprintf(stderr, "alarm signal=%d\n", sig);
+ alarm(1);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ uint8_t *vm86_mem;
+ const char *filename;
+ int fd, ret, seg;
+ struct vm86plus_struct ctx;
+ struct vm86_regs *r;
+
+ if (argc != 2)
+ usage();
+ filename = argv[1];
+
+ vm86_mem = mmap((void *)0x00000000, 0x110000,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (vm86_mem == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+#ifdef SIGTEST
+ {
+ struct sigaction act;
+
+ act.sa_handler = alarm_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, NULL);
+ alarm(1);
+ }
+#endif
+
+ /* load the MSDOS .com executable */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ exit(1);
+ }
+ ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256);
+ if (ret < 0) {
+ perror("read");
+ exit(1);
+ }
+ close(fd);
+
+ memset(&ctx, 0, sizeof(ctx));
+ /* init basic registers */
+ r = &ctx.regs;
+ r->eip = 0x100;
+ r->esp = 0xfffe;
+ seg = (COM_BASE_ADDR - 0x100) >> 4;
+ r->cs = seg;
+ r->ss = seg;
+ r->ds = seg;
+ r->es = seg;
+ r->fs = seg;
+ r->gs = seg;
+ r->eflags = VIF_MASK;
+
+ /* put return code */
+ set_bit((uint8_t *)&ctx.int_revectored, 0x21);
+ *seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */
+ *seg_to_linear(r->cs, 1) = 0x00;
+ *seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */
+ *seg_to_linear(r->cs, 3) = 0x21;
+ pushw(&ctx.regs, 0x0000);
+
+ /* the value of these registers seem to be assumed by pi_10.com */
+ r->esi = 0x100;
+ r->ecx = 0xff;
+ r->ebp = 0x0900;
+ r->edi = 0xfffe;
+
+ for(;;) {
+ ret = vm86(VM86_ENTER, &ctx);
+ switch(VM86_TYPE(ret)) {
+ case VM86_INTx:
+ {
+ int int_num, ah;
+
+ int_num = VM86_ARG(ret);
+ if (int_num != 0x21)
+ goto unknown_int;
+ ah = (r->eax >> 8) & 0xff;
+ switch(ah) {
+ case 0x00: /* exit */
+ exit(0);
+ case 0x02: /* write char */
+ {
+ uint8_t c = r->edx;
+ write(1, &c, 1);
+ }
+ break;
+ case 0x09: /* write string */
+ {
+ uint8_t c;
+ for(;;) {
+ c = *seg_to_linear(r->ds, r->edx);
+ if (c == '$')
+ break;
+ write(1, &c, 1);
+ }
+ r->eax = (r->eax & ~0xff) | '$';
+ }
+ break;
+ default:
+ unknown_int:
+ fprintf(stderr, "unsupported int 0x%02x\n", int_num);
+ dump_regs(&ctx.regs);
+ // exit(1);
+ }
+ }
+ break;
+ case VM86_SIGNAL:
+ /* a signal came, we just ignore that */
+ break;
+ case VM86_STI:
+ break;
+ default:
+ fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret);
+ dump_regs(&ctx.regs);
+ exit(1);
+ }
+ }
+}
diff --git a/tests/sha1.c b/tests/sha1.c
new file mode 100644
index 0000000..93b7c8e
--- /dev/null
+++ b/tests/sha1.c
@@ -0,0 +1,240 @@
+
+/* from valgrind tests */
+
+/* ================ sha1.c ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#define SHA1HANDSOFF
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+/* ================ sha1.h ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+/* ================ end of sha1.h ================ */
+#include <endian.h>
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#elif BYTE_ORDER == BIG_ENDIAN
+#define blk0(i) block->l[i]
+#else
+#error "Endianness not defined!"
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+ unsigned char c[64];
+ uint32_t l[16];
+} CHAR64LONG16;
+#ifdef SHA1HANDSOFF
+CHAR64LONG16 block[1]; /* use array to appear as a pointer */
+ memcpy(block, buffer, 64);
+#else
+ /* The following had better never be used because it causes the
+ * pointer-to-const buffer to be cast into a pointer to non-const.
+ * And the result is written through. I threw a "const" in, hoping
+ * this will cause a diagnostic.
+ */
+CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+ memset(block, '\0', sizeof(block));
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
+{
+uint32_t i;
+uint32_t j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1]++;
+ context->count[1] += (len>>29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+unsigned i;
+unsigned char finalcount[8];
+unsigned char c;
+
+#if 0 /* untested "improvement" by DHR */
+ /* Convert context->count to a sequence of bytes
+ * in finalcount. Second element first, but
+ * big-endian order within element.
+ * But we do it all backwards.
+ */
+ unsigned char *fcp = &finalcount[8];
+
+ for (i = 0; i < 2; i++)
+ {
+ uint32_t t = context->count[i];
+ int j;
+
+ for (j = 0; j < 4; t >>= 8, j++)
+ *--fcp = (unsigned char) t;
+ }
+#else
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+#endif
+ c = 0200;
+ SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ SHA1Update(context, &c, 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
+/* ================ end of sha1.c ================ */
+
+#define BUFSIZE 4096
+
+int
+main(int argc, char **argv)
+{
+ SHA1_CTX ctx;
+ unsigned char hash[20], buf[BUFSIZE];
+ int i;
+
+ for(i=0;i<BUFSIZE;i++)
+ buf[i] = i;
+
+ SHA1Init(&ctx);
+ for(i=0;i<1000;i++)
+ SHA1Update(&ctx, buf, BUFSIZE);
+ SHA1Final(hash, &ctx);
+
+ printf("SHA1=");
+ for(i=0;i<20;i++)
+ printf("%02x", hash[i]);
+ printf("\n");
+ return 0;
+}
diff --git a/tests/test-arm-iwmmxt.s b/tests/test-arm-iwmmxt.s
new file mode 100644
index 0000000..d647f94
--- /dev/null
+++ b/tests/test-arm-iwmmxt.s
@@ -0,0 +1,49 @@
+@ Checks whether iwMMXt is functional.
+.code 32
+.globl main
+
+main:
+ldr r0, =data0
+ldr r1, =data1
+ldr r2, =data2
+#ifndef FPA
+wldrd wr0, [r0, #0]
+wldrd wr1, [r0, #8]
+wldrd wr2, [r1, #0]
+wldrd wr3, [r1, #8]
+wsubb wr2, wr2, wr0
+wsubb wr3, wr3, wr1
+wldrd wr0, [r2, #0]
+wldrd wr1, [r2, #8]
+waddb wr0, wr0, wr2
+waddb wr1, wr1, wr3
+wstrd wr0, [r2, #0]
+wstrd wr1, [r2, #8]
+#else
+ldfe f0, [r0, #0]
+ldfe f1, [r0, #8]
+ldfe f2, [r1, #0]
+ldfe f3, [r1, #8]
+adfdp f2, f2, f0
+adfdp f3, f3, f1
+ldfe f0, [r2, #0]
+ldfe f1, [r2, #8]
+adfd f0, f0, f2
+adfd f1, f1, f3
+stfe f0, [r2, #0]
+stfe f1, [r2, #8]
+#endif
+mov r0, #1
+mov r1, r2
+mov r2, #0x11
+swi #0x900004
+mov r0, #0
+swi #0x900001
+
+.data
+data0:
+.string "aaaabbbbccccdddd"
+data1:
+.string "bbbbccccddddeeee"
+data2:
+.string "hvLLWs\x1fsdrs9\x1fNJ-\n"
diff --git a/tests/test-i386-code16.S b/tests/test-i386-code16.S
new file mode 100644
index 0000000..816c24b
--- /dev/null
+++ b/tests/test-i386-code16.S
@@ -0,0 +1,79 @@
+ .code16
+ .globl code16_start
+ .globl code16_end
+
+CS_SEG = 0xf
+
+code16_start:
+
+ .globl code16_func1
+
+ /* basic test */
+code16_func1 = . - code16_start
+ mov $1, %eax
+ data32 lret
+
+/* test push/pop in 16 bit mode */
+ .globl code16_func2
+code16_func2 = . - code16_start
+ xor %eax, %eax
+ mov $0x12345678, %ebx
+ movl %esp, %ecx
+ push %bx
+ subl %esp, %ecx
+ pop %ax
+ data32 lret
+
+/* test various jmp opcodes */
+ .globl code16_func3
+code16_func3 = . - code16_start
+ jmp 1f
+ nop
+1:
+ mov $4, %eax
+ mov $0x12345678, %ebx
+ xor %bx, %bx
+ jz 2f
+ add $2, %ax
+2:
+
+ call myfunc
+
+ lcall $CS_SEG, $(myfunc2 - code16_start)
+
+ ljmp $CS_SEG, $(myjmp1 - code16_start)
+myjmp1_next:
+
+ cs lcall *myfunc2_addr - code16_start
+
+ cs ljmp *myjmp2_addr - code16_start
+myjmp2_next:
+
+ data32 lret
+
+myfunc2_addr:
+ .short myfunc2 - code16_start
+ .short CS_SEG
+
+myjmp2_addr:
+ .short myjmp2 - code16_start
+ .short CS_SEG
+
+myjmp1:
+ add $8, %ax
+ jmp myjmp1_next
+
+myjmp2:
+ add $16, %ax
+ jmp myjmp2_next
+
+myfunc:
+ add $1, %ax
+ ret
+
+myfunc2:
+ add $4, %ax
+ lret
+
+
+code16_end:
diff --git a/tests/test-i386-muldiv.h b/tests/test-i386-muldiv.h
new file mode 100644
index 0000000..015f59e
--- /dev/null
+++ b/tests/test-i386-muldiv.h
@@ -0,0 +1,76 @@
+
+void glue(glue(test_, OP), b)(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm ("push %4\n\t"
+ "popf\n\t"
+ stringify(OP)"b %b2\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ stringify(OP) "b", s0, s1, res, flags & CC_MASK);
+}
+
+void glue(glue(test_, OP), w)(long op0h, long op0, long op1)
+{
+ long res, s1, flags, resh;
+ s1 = op1;
+ resh = op0h;
+ res = op0;
+ flags = 0;
+ asm ("push %5\n\t"
+ "popf\n\t"
+ stringify(OP) "w %w3\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags), "=d" (resh)
+ : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+ printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+ stringify(OP) "w", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+
+void glue(glue(test_, OP), l)(long op0h, long op0, long op1)
+{
+ long res, s1, flags, resh;
+ s1 = op1;
+ resh = op0h;
+ res = op0;
+ flags = 0;
+ asm ("push %5\n\t"
+ "popf\n\t"
+ stringify(OP) "l %k3\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags), "=d" (resh)
+ : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+ printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+ stringify(OP) "l", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+
+#if defined(__x86_64__)
+void glue(glue(test_, OP), q)(long op0h, long op0, long op1)
+{
+ long res, s1, flags, resh;
+ s1 = op1;
+ resh = op0h;
+ res = op0;
+ flags = 0;
+ asm ("push %5\n\t"
+ "popf\n\t"
+ stringify(OP) "q %3\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=a" (res), "=g" (flags), "=d" (resh)
+ : "q" (s1), "0" (res), "1" (flags), "2" (resh));
+ printf("%-10s AH=" FMTLX " AL=" FMTLX " B=" FMTLX " RH=" FMTLX " RL=" FMTLX " CC=%04lx\n",
+ stringify(OP) "q", op0h, op0, s1, resh, res, flags & CC_MASK);
+}
+#endif
+
+#undef OP
diff --git a/tests/test-i386-shift.h b/tests/test-i386-shift.h
new file mode 100644
index 0000000..3d8f84b
--- /dev/null
+++ b/tests/test-i386-shift.h
@@ -0,0 +1,185 @@
+
+#define exec_op glue(exec_, OP)
+#define exec_opq glue(glue(exec_, OP), q)
+#define exec_opl glue(glue(exec_, OP), l)
+#define exec_opw glue(glue(exec_, OP), w)
+#define exec_opb glue(glue(exec_, OP), b)
+
+#ifndef OP_SHIFTD
+
+#ifdef OP_NOBYTE
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=g" (res), "=g" (flags)\
+ : "r" (s1), "0" (res), "1" (flags));
+#else
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %%cl, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=q" (res), "=g" (flags)\
+ : "c" (s1), "0" (res), "1" (flags));
+#endif
+
+#if defined(__x86_64__)
+void exec_opq(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("q", "", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "q", s0, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_opl(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("l", "k", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "l", s0, s1, res, iflags, flags & CC_MASK);
+}
+
+void exec_opw(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("w", "w", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "w", s0, s1, res, iflags, flags & CC_MASK);
+}
+
+#else
+#define EXECSHIFT(size, rsize, res, s1, s2, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %%cl, %" rsize "5, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=g" (res), "=g" (flags)\
+ : "c" (s1), "0" (res), "1" (flags), "r" (s2));
+
+#if defined(__x86_64__)
+void exec_opq(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("q", "", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "q", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_opl(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("l", "k", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "l", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+
+void exec_opw(long s2, long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("w", "w", res, s1, s2, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " C=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "w", s0, s2, s1, res, iflags, flags & CC_MASK);
+}
+
+#endif
+
+#ifndef OP_NOBYTE
+void exec_opb(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECSHIFT("b", "b", res, s1, 0, flags);
+ /* overflow is undefined if count != 1 */
+ if (s1 != 1)
+ flags &= ~CC_O;
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n",
+ stringify(OP) "b", s0, s1, res, iflags, flags & CC_MASK);
+}
+#endif
+
+void exec_op(long s2, long s0, long s1)
+{
+ s2 = i2l(s2);
+ s0 = i2l(s0);
+#if defined(__x86_64__)
+ exec_opq(s2, s0, s1, 0);
+#endif
+ exec_opl(s2, s0, s1, 0);
+#ifdef OP_SHIFTD
+ exec_opw(s2, s0, s1, 0);
+#else
+ exec_opw(s2, s0, s1, 0);
+#endif
+#ifndef OP_NOBYTE
+ exec_opb(s0, s1, 0);
+#endif
+#ifdef OP_CC
+#if defined(__x86_64__)
+ exec_opq(s2, s0, s1, CC_C);
+#endif
+ exec_opl(s2, s0, s1, CC_C);
+ exec_opw(s2, s0, s1, CC_C);
+ exec_opb(s0, s1, CC_C);
+#endif
+}
+
+void glue(test_, OP)(void)
+{
+ int i, n;
+#if defined(__x86_64__)
+ n = 64;
+#else
+ n = 32;
+#endif
+ for(i = 0; i < n; i++)
+ exec_op(0x21ad3d34, 0x12345678, i);
+ for(i = 0; i < n; i++)
+ exec_op(0x813f3421, 0x82345679, i);
+}
+
+void *glue(_test_, OP) __init_call = glue(test_, OP);
+
+#undef OP
+#undef OP_CC
+#undef OP_SHIFTD
+#undef OP_NOBYTE
+#undef EXECSHIFT
diff --git a/tests/test-i386-ssse3.c b/tests/test-i386-ssse3.c
new file mode 100644
index 0000000..0a42bd0
--- /dev/null
+++ b/tests/test-i386-ssse3.c
@@ -0,0 +1,57 @@
+/* See if various MMX/SSE SSSE3 instructions give expected results */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+int main(int argc, char *argv[]) {
+ char hello[16];
+ const char ehlo[8] = "EHLO ";
+ uint64_t mask = 0x8080800302020001;
+
+ uint64_t a = 0x0000000000090007;
+ uint64_t b = 0x0000000000000000;
+ uint32_t c;
+ uint16_t d;
+
+ const char e[16] = "LLOaaaaaaaaaaaaa";
+ const char f[16] = "aaaaaaaaaaaaaaHE";
+
+ /* pshufb mm1/xmm1, mm2/xmm2 */
+ asm volatile ("movq (%0), %%mm0" : : "r" (ehlo) : "mm0", "mm1");
+ asm volatile ("movq %0, %%mm1" : : "m" (mask));
+ asm volatile ("pshufb %mm1, %mm0");
+ asm volatile ("movq %%mm0, %0" : "=m" (hello));
+ printf("%s\n", hello);
+
+ /* pshufb mm1/xmm1, m64/m128 */
+ asm volatile ("movq (%0), %%mm0" : : "r" (ehlo) : "mm0");
+ asm volatile ("pshufb %0, %%mm0" : : "m" (mask));
+ asm volatile ("movq %%mm0, %0" : "=m" (hello));
+ printf("%s\n", hello);
+
+ /* psubsw mm1/xmm1, m64/m128 */
+ asm volatile ("movq %0, %%mm0" : : "r" (a) : "mm0");
+ asm volatile ("phsubsw %0, %%mm0" : : "m" (b));
+ asm volatile ("movq %%mm0, %0" : "=m" (a));
+ printf("%i - %i = %i\n", 9, 7, -(int16_t) a);
+
+ /* palignr mm1/xmm1, m64/m128, imm8 */
+ asm volatile ("movdqa (%0), %%xmm0" : : "r" (e) : "xmm0");
+ asm volatile ("palignr $14, (%0), %%xmm0" : : "r" (f));
+ asm volatile ("movdqa %%xmm0, (%0)" : : "r" (hello));
+ printf("%5.5s\n", hello);
+
+#if 1 /* SSE4 */
+ /* popcnt r64, r/m64 */
+ asm volatile ("movq $0x8421000010009c63, %%rax" : : : "rax");
+ asm volatile ("popcnt %%ax, %%dx" : : : "dx");
+ asm volatile ("popcnt %%eax, %%ecx" : : : "ecx");
+ asm volatile ("popcnt %rax, %rax");
+ asm volatile ("movq %%rax, %0" : "=m" (a));
+ asm volatile ("movl %%ecx, %0" : "=m" (c));
+ asm volatile ("movw %%dx, %0" : "=m" (d));
+ printf("%i = %i\n%i = %i = %i\n", 13, (int) a, 9, c, d + 1);
+#endif
+
+ return 0;
+}
diff --git a/tests/test-i386-vm86.S b/tests/test-i386-vm86.S
new file mode 100644
index 0000000..3bb96c9
--- /dev/null
+++ b/tests/test-i386-vm86.S
@@ -0,0 +1,103 @@
+ .code16
+ .globl vm86_code_start
+ .globl vm86_code_end
+
+#define GET_OFFSET(x) ((x) - vm86_code_start + 0x100)
+
+vm86_code_start:
+ movw $GET_OFFSET(hello_world), %dx
+ movb $0x09, %ah
+ int $0x21
+
+ /* prepare int 0x90 vector */
+ xorw %ax, %ax
+ movw %ax, %es
+ es movw $GET_OFFSET(int90_test), 0x90 * 4
+ es movw %cs, 0x90 * 4 + 2
+
+ /* launch int 0x90 */
+
+ int $0x90
+
+ /* test IF support */
+ movw $GET_OFFSET(IF_msg), %dx
+ movb $0x09, %ah
+ int $0x21
+
+ pushf
+ popw %dx
+ movb $0xff, %ah
+ int $0x21
+
+ cli
+ pushf
+ popw %dx
+ movb $0xff, %ah
+ int $0x21
+
+ sti
+ pushfl
+ popl %edx
+ movb $0xff, %ah
+ int $0x21
+
+#if 0
+ movw $GET_OFFSET(IF_msg1), %dx
+ movb $0x09, %ah
+ int $0x21
+
+ pushf
+ movw %sp, %bx
+ andw $~0x200, (%bx)
+ popf
+#else
+ cli
+#endif
+
+ pushf
+ popw %dx
+ movb $0xff, %ah
+ int $0x21
+
+ pushfl
+ movw %sp, %bx
+ orw $0x200, (%bx)
+ popfl
+
+ pushfl
+ popl %edx
+ movb $0xff, %ah
+ int $0x21
+
+ movb $0x00, %ah
+ int $0x21
+
+int90_test:
+ pushf
+ pop %dx
+ movb $0xff, %ah
+ int $0x21
+
+ movw %sp, %bx
+ movw 4(%bx), %dx
+ movb $0xff, %ah
+ int $0x21
+
+ movw $GET_OFFSET(int90_msg), %dx
+ movb $0x09, %ah
+ int $0x21
+ iret
+
+int90_msg:
+ .string "INT90 started\n$"
+
+hello_world:
+ .string "Hello VM86 world\n$"
+
+IF_msg:
+ .string "VM86 IF test\n$"
+
+IF_msg1:
+ .string "If you see a diff here, your Linux kernel is buggy, please update to 2.4.20 kernel\n$"
+
+vm86_code_end:
diff --git a/tests/test-i386.c b/tests/test-i386.c
new file mode 100644
index 0000000..8f481c7
--- /dev/null
+++ b/tests/test-i386.c
@@ -0,0 +1,2764 @@
+/*
+ * x86 CPU test
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <math.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+#include <sys/mman.h>
+
+#if !defined(__x86_64__)
+//#define TEST_VM86
+#define TEST_SEGS
+#endif
+//#define LINUX_VM86_IOPL_FIX
+//#define TEST_P4_FLAGS
+#ifdef __SSE__
+#define TEST_SSE
+#define TEST_CMOV 1
+#define TEST_FCOMI 1
+#else
+#undef TEST_SSE
+#define TEST_CMOV 1
+#define TEST_FCOMI 1
+#endif
+
+#if defined(__x86_64__)
+#define FMT64X "%016lx"
+#define FMTLX "%016lx"
+#define X86_64_ONLY(x) x
+#else
+#define FMT64X "%016" PRIx64
+#define FMTLX "%08lx"
+#define X86_64_ONLY(x)
+#endif
+
+#ifdef TEST_VM86
+#include <asm/vm86.h>
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+#define CC_C 0x0001
+#define CC_P 0x0004
+#define CC_A 0x0010
+#define CC_Z 0x0040
+#define CC_S 0x0080
+#define CC_O 0x0800
+
+#define __init_call __attribute__ ((unused,__section__ ("initcall")))
+
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)
+
+#if defined(__x86_64__)
+static inline long i2l(long v)
+{
+ return v | ((v ^ 0xabcd) << 32);
+}
+#else
+static inline long i2l(long v)
+{
+ return v;
+}
+#endif
+
+#define OP add
+#include "test-i386.h"
+
+#define OP sub
+#include "test-i386.h"
+
+#define OP xor
+#include "test-i386.h"
+
+#define OP and
+#include "test-i386.h"
+
+#define OP or
+#include "test-i386.h"
+
+#define OP cmp
+#include "test-i386.h"
+
+#define OP adc
+#define OP_CC
+#include "test-i386.h"
+
+#define OP sbb
+#define OP_CC
+#include "test-i386.h"
+
+#define OP inc
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP dec
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP neg
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#define OP not
+#define OP_CC
+#define OP1
+#include "test-i386.h"
+
+#undef CC_MASK
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O)
+
+#define OP shl
+#include "test-i386-shift.h"
+
+#define OP shr
+#include "test-i386-shift.h"
+
+#define OP sar
+#include "test-i386-shift.h"
+
+#define OP rol
+#include "test-i386-shift.h"
+
+#define OP ror
+#include "test-i386-shift.h"
+
+#define OP rcr
+#define OP_CC
+#include "test-i386-shift.h"
+
+#define OP rcl
+#define OP_CC
+#include "test-i386-shift.h"
+
+#define OP shld
+#define OP_SHIFTD
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP shrd
+#define OP_SHIFTD
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+/* XXX: should be more precise ? */
+#undef CC_MASK
+#define CC_MASK (CC_C)
+
+#define OP bt
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP bts
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP btr
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+#define OP btc
+#define OP_NOBYTE
+#include "test-i386-shift.h"
+
+/* lea test (modrm support) */
+#define TEST_LEAQ(STR)\
+{\
+ asm("lea " STR ", %0"\
+ : "=r" (res)\
+ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+ printf("lea %s = " FMTLX "\n", STR, res);\
+}
+
+#define TEST_LEA(STR)\
+{\
+ asm("lea " STR ", %0"\
+ : "=r" (res)\
+ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+ printf("lea %s = " FMTLX "\n", STR, res);\
+}
+
+#define TEST_LEA16(STR)\
+{\
+ asm(".code16 ; .byte 0x67 ; leal " STR ", %0 ; .code32"\
+ : "=wq" (res)\
+ : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));\
+ printf("lea %s = %08lx\n", STR, res);\
+}
+
+
+void test_lea(void)
+{
+ long eax, ebx, ecx, edx, esi, edi, res;
+ eax = i2l(0x0001);
+ ebx = i2l(0x0002);
+ ecx = i2l(0x0004);
+ edx = i2l(0x0008);
+ esi = i2l(0x0010);
+ edi = i2l(0x0020);
+
+ TEST_LEA("0x4000");
+
+ TEST_LEA("(%%eax)");
+ TEST_LEA("(%%ebx)");
+ TEST_LEA("(%%ecx)");
+ TEST_LEA("(%%edx)");
+ TEST_LEA("(%%esi)");
+ TEST_LEA("(%%edi)");
+
+ TEST_LEA("0x40(%%eax)");
+ TEST_LEA("0x40(%%ebx)");
+ TEST_LEA("0x40(%%ecx)");
+ TEST_LEA("0x40(%%edx)");
+ TEST_LEA("0x40(%%esi)");
+ TEST_LEA("0x40(%%edi)");
+
+ TEST_LEA("0x4000(%%eax)");
+ TEST_LEA("0x4000(%%ebx)");
+ TEST_LEA("0x4000(%%ecx)");
+ TEST_LEA("0x4000(%%edx)");
+ TEST_LEA("0x4000(%%esi)");
+ TEST_LEA("0x4000(%%edi)");
+
+ TEST_LEA("(%%eax, %%ecx)");
+ TEST_LEA("(%%ebx, %%edx)");
+ TEST_LEA("(%%ecx, %%ecx)");
+ TEST_LEA("(%%edx, %%ecx)");
+ TEST_LEA("(%%esi, %%ecx)");
+ TEST_LEA("(%%edi, %%ecx)");
+
+ TEST_LEA("0x40(%%eax, %%ecx)");
+ TEST_LEA("0x4000(%%ebx, %%edx)");
+
+ TEST_LEA("(%%ecx, %%ecx, 2)");
+ TEST_LEA("(%%edx, %%ecx, 4)");
+ TEST_LEA("(%%esi, %%ecx, 8)");
+
+ TEST_LEA("(,%%eax, 2)");
+ TEST_LEA("(,%%ebx, 4)");
+ TEST_LEA("(,%%ecx, 8)");
+
+ TEST_LEA("0x40(,%%eax, 2)");
+ TEST_LEA("0x40(,%%ebx, 4)");
+ TEST_LEA("0x40(,%%ecx, 8)");
+
+
+ TEST_LEA("-10(%%ecx, %%ecx, 2)");
+ TEST_LEA("-10(%%edx, %%ecx, 4)");
+ TEST_LEA("-10(%%esi, %%ecx, 8)");
+
+ TEST_LEA("0x4000(%%ecx, %%ecx, 2)");
+ TEST_LEA("0x4000(%%edx, %%ecx, 4)");
+ TEST_LEA("0x4000(%%esi, %%ecx, 8)");
+
+#if defined(__x86_64__)
+ TEST_LEAQ("0x4000");
+ TEST_LEAQ("0x4000(%%rip)");
+
+ TEST_LEAQ("(%%rax)");
+ TEST_LEAQ("(%%rbx)");
+ TEST_LEAQ("(%%rcx)");
+ TEST_LEAQ("(%%rdx)");
+ TEST_LEAQ("(%%rsi)");
+ TEST_LEAQ("(%%rdi)");
+
+ TEST_LEAQ("0x40(%%rax)");
+ TEST_LEAQ("0x40(%%rbx)");
+ TEST_LEAQ("0x40(%%rcx)");
+ TEST_LEAQ("0x40(%%rdx)");
+ TEST_LEAQ("0x40(%%rsi)");
+ TEST_LEAQ("0x40(%%rdi)");
+
+ TEST_LEAQ("0x4000(%%rax)");
+ TEST_LEAQ("0x4000(%%rbx)");
+ TEST_LEAQ("0x4000(%%rcx)");
+ TEST_LEAQ("0x4000(%%rdx)");
+ TEST_LEAQ("0x4000(%%rsi)");
+ TEST_LEAQ("0x4000(%%rdi)");
+
+ TEST_LEAQ("(%%rax, %%rcx)");
+ TEST_LEAQ("(%%rbx, %%rdx)");
+ TEST_LEAQ("(%%rcx, %%rcx)");
+ TEST_LEAQ("(%%rdx, %%rcx)");
+ TEST_LEAQ("(%%rsi, %%rcx)");
+ TEST_LEAQ("(%%rdi, %%rcx)");
+
+ TEST_LEAQ("0x40(%%rax, %%rcx)");
+ TEST_LEAQ("0x4000(%%rbx, %%rdx)");
+
+ TEST_LEAQ("(%%rcx, %%rcx, 2)");
+ TEST_LEAQ("(%%rdx, %%rcx, 4)");
+ TEST_LEAQ("(%%rsi, %%rcx, 8)");
+
+ TEST_LEAQ("(,%%rax, 2)");
+ TEST_LEAQ("(,%%rbx, 4)");
+ TEST_LEAQ("(,%%rcx, 8)");
+
+ TEST_LEAQ("0x40(,%%rax, 2)");
+ TEST_LEAQ("0x40(,%%rbx, 4)");
+ TEST_LEAQ("0x40(,%%rcx, 8)");
+
+
+ TEST_LEAQ("-10(%%rcx, %%rcx, 2)");
+ TEST_LEAQ("-10(%%rdx, %%rcx, 4)");
+ TEST_LEAQ("-10(%%rsi, %%rcx, 8)");
+
+ TEST_LEAQ("0x4000(%%rcx, %%rcx, 2)");
+ TEST_LEAQ("0x4000(%%rdx, %%rcx, 4)");
+ TEST_LEAQ("0x4000(%%rsi, %%rcx, 8)");
+#else
+ /* limited 16 bit addressing test */
+ TEST_LEA16("0x4000");
+ TEST_LEA16("(%%bx)");
+ TEST_LEA16("(%%si)");
+ TEST_LEA16("(%%di)");
+ TEST_LEA16("0x40(%%bx)");
+ TEST_LEA16("0x40(%%si)");
+ TEST_LEA16("0x40(%%di)");
+ TEST_LEA16("0x4000(%%bx)");
+ TEST_LEA16("0x4000(%%si)");
+ TEST_LEA16("(%%bx,%%si)");
+ TEST_LEA16("(%%bx,%%di)");
+ TEST_LEA16("0x40(%%bx,%%si)");
+ TEST_LEA16("0x40(%%bx,%%di)");
+ TEST_LEA16("0x4000(%%bx,%%si)");
+ TEST_LEA16("0x4000(%%bx,%%di)");
+#endif
+}
+
+#define TEST_JCC(JCC, v1, v2)\
+{\
+ int res;\
+ asm("movl $1, %0\n\t"\
+ "cmpl %2, %1\n\t"\
+ "j" JCC " 1f\n\t"\
+ "movl $0, %0\n\t"\
+ "1:\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2));\
+ printf("%-10s %d\n", "j" JCC, res);\
+\
+ asm("movl $0, %0\n\t"\
+ "cmpl %2, %1\n\t"\
+ "set" JCC " %b0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2));\
+ printf("%-10s %d\n", "set" JCC, res);\
+ if (TEST_CMOV) {\
+ long val = i2l(1);\
+ long res = i2l(0x12345678);\
+X86_64_ONLY(\
+ asm("cmpl %2, %1\n\t"\
+ "cmov" JCC "q %3, %0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2), "m" (val), "0" (res));\
+ printf("%-10s R=" FMTLX "\n", "cmov" JCC "q", res);)\
+ asm("cmpl %2, %1\n\t"\
+ "cmov" JCC "l %k3, %k0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2), "m" (val), "0" (res));\
+ printf("%-10s R=" FMTLX "\n", "cmov" JCC "l", res);\
+ asm("cmpl %2, %1\n\t"\
+ "cmov" JCC "w %w3, %w0\n\t"\
+ : "=r" (res)\
+ : "r" (v1), "r" (v2), "r" (1), "0" (res));\
+ printf("%-10s R=" FMTLX "\n", "cmov" JCC "w", res);\
+ } \
+}
+
+/* various jump tests */
+void test_jcc(void)
+{
+ TEST_JCC("ne", 1, 1);
+ TEST_JCC("ne", 1, 0);
+
+ TEST_JCC("e", 1, 1);
+ TEST_JCC("e", 1, 0);
+
+ TEST_JCC("l", 1, 1);
+ TEST_JCC("l", 1, 0);
+ TEST_JCC("l", 1, -1);
+
+ TEST_JCC("le", 1, 1);
+ TEST_JCC("le", 1, 0);
+ TEST_JCC("le", 1, -1);
+
+ TEST_JCC("ge", 1, 1);
+ TEST_JCC("ge", 1, 0);
+ TEST_JCC("ge", -1, 1);
+
+ TEST_JCC("g", 1, 1);
+ TEST_JCC("g", 1, 0);
+ TEST_JCC("g", 1, -1);
+
+ TEST_JCC("b", 1, 1);
+ TEST_JCC("b", 1, 0);
+ TEST_JCC("b", 1, -1);
+
+ TEST_JCC("be", 1, 1);
+ TEST_JCC("be", 1, 0);
+ TEST_JCC("be", 1, -1);
+
+ TEST_JCC("ae", 1, 1);
+ TEST_JCC("ae", 1, 0);
+ TEST_JCC("ae", 1, -1);
+
+ TEST_JCC("a", 1, 1);
+ TEST_JCC("a", 1, 0);
+ TEST_JCC("a", 1, -1);
+
+
+ TEST_JCC("p", 1, 1);
+ TEST_JCC("p", 1, 0);
+
+ TEST_JCC("np", 1, 1);
+ TEST_JCC("np", 1, 0);
+
+ TEST_JCC("o", 0x7fffffff, 0);
+ TEST_JCC("o", 0x7fffffff, -1);
+
+ TEST_JCC("no", 0x7fffffff, 0);
+ TEST_JCC("no", 0x7fffffff, -1);
+
+ TEST_JCC("s", 0, 1);
+ TEST_JCC("s", 0, -1);
+ TEST_JCC("s", 0, 0);
+
+ TEST_JCC("ns", 0, 1);
+ TEST_JCC("ns", 0, -1);
+ TEST_JCC("ns", 0, 0);
+}
+
+#define TEST_LOOP(insn) \
+{\
+ for(i = 0; i < sizeof(ecx_vals) / sizeof(long); i++) {\
+ ecx = ecx_vals[i];\
+ for(zf = 0; zf < 2; zf++) {\
+ asm("test %2, %2\n\t"\
+ "movl $1, %0\n\t"\
+ insn " 1f\n\t" \
+ "movl $0, %0\n\t"\
+ "1:\n\t"\
+ : "=a" (res)\
+ : "c" (ecx), "b" (!zf)); \
+ printf("%-10s ECX=" FMTLX " ZF=%ld r=%d\n", insn, ecx, zf, res); \
+ }\
+ }\
+}
+
+void test_loop(void)
+{
+ long ecx, zf;
+ const long ecx_vals[] = {
+ 0,
+ 1,
+ 0x10000,
+ 0x10001,
+#if defined(__x86_64__)
+ 0x100000000L,
+ 0x100000001L,
+#endif
+ };
+ int i, res;
+
+#if !defined(__x86_64__)
+ TEST_LOOP("jcxz");
+ TEST_LOOP("loopw");
+ TEST_LOOP("loopzw");
+ TEST_LOOP("loopnzw");
+#endif
+
+ TEST_LOOP("jecxz");
+ TEST_LOOP("loopl");
+ TEST_LOOP("loopzl");
+ TEST_LOOP("loopnzl");
+}
+
+#undef CC_MASK
+#ifdef TEST_P4_FLAGS
+#define CC_MASK (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)
+#else
+#define CC_MASK (CC_O | CC_C)
+#endif
+
+#define OP mul
+#include "test-i386-muldiv.h"
+
+#define OP imul
+#include "test-i386-muldiv.h"
+
+void test_imulw2(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm volatile ("push %4\n\t"
+ "popf\n\t"
+ "imulw %w2, %w0\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=q" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ "imulw", s0, s1, res, flags & CC_MASK);
+}
+
+void test_imull2(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm volatile ("push %4\n\t"
+ "popf\n\t"
+ "imull %k2, %k0\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=q" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ "imull", s0, s1, res, flags & CC_MASK);
+}
+
+#if defined(__x86_64__)
+void test_imulq2(long op0, long op1)
+{
+ long res, s1, s0, flags;
+ s0 = op0;
+ s1 = op1;
+ res = s0;
+ flags = 0;
+ asm volatile ("push %4\n\t"
+ "popf\n\t"
+ "imulq %2, %0\n\t"
+ "pushf\n\t"
+ "pop %1\n\t"
+ : "=q" (res), "=g" (flags)
+ : "q" (s1), "0" (res), "1" (flags));
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",
+ "imulq", s0, s1, res, flags & CC_MASK);
+}
+#endif
+
+#define TEST_IMUL_IM(size, rsize, op0, op1)\
+{\
+ long res, flags, s1;\
+ flags = 0;\
+ res = 0;\
+ s1 = op1;\
+ asm volatile ("push %3\n\t"\
+ "popf\n\t"\
+ "imul" size " $" #op0 ", %" rsize "2, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=r" (res), "=g" (flags)\
+ : "r" (s1), "1" (flags), "0" (res));\
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CC=%04lx\n",\
+ "imul" size " im", (long)op0, (long)op1, res, flags & CC_MASK);\
+}
+
+
+#undef CC_MASK
+#define CC_MASK (0)
+
+#define OP div
+#include "test-i386-muldiv.h"
+
+#define OP idiv
+#include "test-i386-muldiv.h"
+
+void test_mul(void)
+{
+ test_imulb(0x1234561d, 4);
+ test_imulb(3, -4);
+ test_imulb(0x80, 0x80);
+ test_imulb(0x10, 0x10);
+
+ test_imulw(0, 0x1234001d, 45);
+ test_imulw(0, 23, -45);
+ test_imulw(0, 0x8000, 0x8000);
+ test_imulw(0, 0x100, 0x100);
+
+ test_imull(0, 0x1234001d, 45);
+ test_imull(0, 23, -45);
+ test_imull(0, 0x80000000, 0x80000000);
+ test_imull(0, 0x10000, 0x10000);
+
+ test_mulb(0x1234561d, 4);
+ test_mulb(3, -4);
+ test_mulb(0x80, 0x80);
+ test_mulb(0x10, 0x10);
+
+ test_mulw(0, 0x1234001d, 45);
+ test_mulw(0, 23, -45);
+ test_mulw(0, 0x8000, 0x8000);
+ test_mulw(0, 0x100, 0x100);
+
+ test_mull(0, 0x1234001d, 45);
+ test_mull(0, 23, -45);
+ test_mull(0, 0x80000000, 0x80000000);
+ test_mull(0, 0x10000, 0x10000);
+
+ test_imulw2(0x1234001d, 45);
+ test_imulw2(23, -45);
+ test_imulw2(0x8000, 0x8000);
+ test_imulw2(0x100, 0x100);
+
+ test_imull2(0x1234001d, 45);
+ test_imull2(23, -45);
+ test_imull2(0x80000000, 0x80000000);
+ test_imull2(0x10000, 0x10000);
+
+ TEST_IMUL_IM("w", "w", 45, 0x1234);
+ TEST_IMUL_IM("w", "w", -45, 23);
+ TEST_IMUL_IM("w", "w", 0x8000, 0x80000000);
+ TEST_IMUL_IM("w", "w", 0x7fff, 0x1000);
+
+ TEST_IMUL_IM("l", "k", 45, 0x1234);
+ TEST_IMUL_IM("l", "k", -45, 23);
+ TEST_IMUL_IM("l", "k", 0x8000, 0x80000000);
+ TEST_IMUL_IM("l", "k", 0x7fff, 0x1000);
+
+ test_idivb(0x12341678, 0x127e);
+ test_idivb(0x43210123, -5);
+ test_idivb(0x12340004, -1);
+
+ test_idivw(0, 0x12345678, 12347);
+ test_idivw(0, -23223, -45);
+ test_idivw(0, 0x12348000, -1);
+ test_idivw(0x12343, 0x12345678, 0x81238567);
+
+ test_idivl(0, 0x12345678, 12347);
+ test_idivl(0, -233223, -45);
+ test_idivl(0, 0x80000000, -1);
+ test_idivl(0x12343, 0x12345678, 0x81234567);
+
+ test_divb(0x12341678, 0x127e);
+ test_divb(0x43210123, -5);
+ test_divb(0x12340004, -1);
+
+ test_divw(0, 0x12345678, 12347);
+ test_divw(0, -23223, -45);
+ test_divw(0, 0x12348000, -1);
+ test_divw(0x12343, 0x12345678, 0x81238567);
+
+ test_divl(0, 0x12345678, 12347);
+ test_divl(0, -233223, -45);
+ test_divl(0, 0x80000000, -1);
+ test_divl(0x12343, 0x12345678, 0x81234567);
+
+#if defined(__x86_64__)
+ test_imulq(0, 0x1234001d1234001d, 45);
+ test_imulq(0, 23, -45);
+ test_imulq(0, 0x8000000000000000, 0x8000000000000000);
+ test_imulq(0, 0x100000000, 0x100000000);
+
+ test_mulq(0, 0x1234001d1234001d, 45);
+ test_mulq(0, 23, -45);
+ test_mulq(0, 0x8000000000000000, 0x8000000000000000);
+ test_mulq(0, 0x100000000, 0x100000000);
+
+ test_imulq2(0x1234001d1234001d, 45);
+ test_imulq2(23, -45);
+ test_imulq2(0x8000000000000000, 0x8000000000000000);
+ test_imulq2(0x100000000, 0x100000000);
+
+ TEST_IMUL_IM("q", "", 45, 0x12341234);
+ TEST_IMUL_IM("q", "", -45, 23);
+ TEST_IMUL_IM("q", "", 0x8000, 0x8000000000000000);
+ TEST_IMUL_IM("q", "", 0x7fff, 0x10000000);
+
+ test_idivq(0, 0x12345678abcdef, 12347);
+ test_idivq(0, -233223, -45);
+ test_idivq(0, 0x8000000000000000, -1);
+ test_idivq(0x12343, 0x12345678, 0x81234567);
+
+ test_divq(0, 0x12345678abcdef, 12347);
+ test_divq(0, -233223, -45);
+ test_divq(0, 0x8000000000000000, -1);
+ test_divq(0x12343, 0x12345678, 0x81234567);
+#endif
+}
+
+#define TEST_BSX(op, size, op0)\
+{\
+ long res, val, resz;\
+ val = op0;\
+ asm("xor %1, %1\n"\
+ "mov $0x12345678, %0\n"\
+ #op " %" size "2, %" size "0 ; setz %b1" \
+ : "=&r" (res), "=&q" (resz)\
+ : "r" (val));\
+ printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\
+}
+
+void test_bsx(void)
+{
+ TEST_BSX(bsrw, "w", 0);
+ TEST_BSX(bsrw, "w", 0x12340128);
+ TEST_BSX(bsfw, "w", 0);
+ TEST_BSX(bsfw, "w", 0x12340128);
+ TEST_BSX(bsrl, "k", 0);
+ TEST_BSX(bsrl, "k", 0x00340128);
+ TEST_BSX(bsfl, "k", 0);
+ TEST_BSX(bsfl, "k", 0x00340128);
+#if defined(__x86_64__)
+ TEST_BSX(bsrq, "", 0);
+ TEST_BSX(bsrq, "", 0x003401281234);
+ TEST_BSX(bsfq, "", 0);
+ TEST_BSX(bsfq, "", 0x003401281234);
+#endif
+}
+
+/**********************************************/
+
+union float64u {
+ double d;
+ uint64_t l;
+};
+
+union float64u q_nan = { .l = 0xFFF8000000000000LL };
+union float64u s_nan = { .l = 0xFFF0000000000000LL };
+
+void test_fops(double a, double b)
+{
+ printf("a=%f b=%f a+b=%f\n", a, b, a + b);
+ printf("a=%f b=%f a-b=%f\n", a, b, a - b);
+ printf("a=%f b=%f a*b=%f\n", a, b, a * b);
+ printf("a=%f b=%f a/b=%f\n", a, b, a / b);
+ printf("a=%f b=%f fmod(a, b)=%f\n", a, b, fmod(a, b));
+ printf("a=%f sqrt(a)=%f\n", a, sqrt(a));
+ printf("a=%f sin(a)=%f\n", a, sin(a));
+ printf("a=%f cos(a)=%f\n", a, cos(a));
+ printf("a=%f tan(a)=%f\n", a, tan(a));
+ printf("a=%f log(a)=%f\n", a, log(a));
+ printf("a=%f exp(a)=%f\n", a, exp(a));
+ printf("a=%f b=%f atan2(a, b)=%f\n", a, b, atan2(a, b));
+ /* just to test some op combining */
+ printf("a=%f asin(sin(a))=%f\n", a, asin(sin(a)));
+ printf("a=%f acos(cos(a))=%f\n", a, acos(cos(a)));
+ printf("a=%f atan(tan(a))=%f\n", a, atan(tan(a)));
+
+}
+
+void fpu_clear_exceptions(void)
+{
+ struct __attribute__((packed)) {
+ uint16_t fpuc;
+ uint16_t dummy1;
+ uint16_t fpus;
+ uint16_t dummy2;
+ uint16_t fptag;
+ uint16_t dummy3;
+ uint32_t ignored[4];
+ long double fpregs[8];
+ } float_env32;
+
+ asm volatile ("fnstenv %0\n" : : "m" (float_env32));
+ float_env32.fpus &= ~0x7f;
+ asm volatile ("fldenv %0\n" : : "m" (float_env32));
+}
+
+/* XXX: display exception bits when supported */
+#define FPUS_EMASK 0x0000
+//#define FPUS_EMASK 0x007f
+
+void test_fcmp(double a, double b)
+{
+ long eflags, fpus;
+
+ fpu_clear_exceptions();
+ asm("fcom %2\n"
+ "fstsw %%ax\n"
+ : "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fcom(%f %f)=%04lx \n",
+ a, b, fpus & (0x4500 | FPUS_EMASK));
+ fpu_clear_exceptions();
+ asm("fucom %2\n"
+ "fstsw %%ax\n"
+ : "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fucom(%f %f)=%04lx\n",
+ a, b, fpus & (0x4500 | FPUS_EMASK));
+ if (TEST_FCOMI) {
+ /* test f(u)comi instruction */
+ fpu_clear_exceptions();
+ asm("fcomi %3, %2\n"
+ "fstsw %%ax\n"
+ "pushf\n"
+ "pop %0\n"
+ : "=r" (eflags), "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fcomi(%f %f)=%04lx %02lx\n",
+ a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));
+ fpu_clear_exceptions();
+ asm("fucomi %3, %2\n"
+ "fstsw %%ax\n"
+ "pushf\n"
+ "pop %0\n"
+ : "=r" (eflags), "=a" (fpus)
+ : "t" (a), "u" (b));
+ printf("fucomi(%f %f)=%04lx %02lx\n",
+ a, b, fpus & FPUS_EMASK, eflags & (CC_Z | CC_P | CC_C));
+ }
+ fpu_clear_exceptions();
+ asm volatile("fxam\n"
+ "fstsw %%ax\n"
+ : "=a" (fpus)
+ : "t" (a));
+ printf("fxam(%f)=%04lx\n", a, fpus & 0x4700);
+ fpu_clear_exceptions();
+}
+
+void test_fcvt(double a)
+{
+ float fa;
+ long double la;
+ int16_t fpuc;
+ int i;
+ int64_t lla;
+ int ia;
+ int16_t wa;
+ double ra;
+
+ fa = a;
+ la = a;
+ printf("(float)%f = %f\n", a, fa);
+ printf("(long double)%f = %Lf\n", a, la);
+ printf("a=" FMT64X "\n", *(uint64_t *)&a);
+ printf("la=" FMT64X " %04x\n", *(uint64_t *)&la,
+ *(unsigned short *)((char *)(&la) + 8));
+
+ /* test all roundings */
+ asm volatile ("fstcw %0" : "=m" (fpuc));
+ for(i=0;i<4;i++) {
+ uint16_t val16;
+ val16 = (fpuc & ~0x0c00) | (i << 10);
+ asm volatile ("fldcw %0" : : "m" (val16));
+ asm volatile ("fist %0" : "=m" (wa) : "t" (a));
+ asm volatile ("fistl %0" : "=m" (ia) : "t" (a));
+ asm volatile ("fistpll %0" : "=m" (lla) : "t" (a) : "st");
+ asm volatile ("frndint ; fstl %0" : "=m" (ra) : "t" (a));
+ asm volatile ("fldcw %0" : : "m" (fpuc));
+ printf("(short)a = %d\n", wa);
+ printf("(int)a = %d\n", ia);
+ printf("(int64_t)a = " FMT64X "\n", lla);
+ printf("rint(a) = %f\n", ra);
+ }
+}
+
+#define TEST(N) \
+ asm("fld" #N : "=t" (a)); \
+ printf("fld" #N "= %f\n", a);
+
+void test_fconst(void)
+{
+ double a;
+ TEST(1);
+ TEST(l2t);
+ TEST(l2e);
+ TEST(pi);
+ TEST(lg2);
+ TEST(ln2);
+ TEST(z);
+}
+
+void test_fbcd(double a)
+{
+ unsigned short bcd[5];
+ double b;
+
+ asm("fbstp %0" : "=m" (bcd[0]) : "t" (a) : "st");
+ asm("fbld %1" : "=t" (b) : "m" (bcd[0]));
+ printf("a=%f bcd=%04x%04x%04x%04x%04x b=%f\n",
+ a, bcd[4], bcd[3], bcd[2], bcd[1], bcd[0], b);
+}
+
+#define TEST_ENV(env, save, restore)\
+{\
+ memset((env), 0xaa, sizeof(*(env)));\
+ for(i=0;i<5;i++)\
+ asm volatile ("fldl %0" : : "m" (dtab[i]));\
+ asm volatile (save " %0\n" : : "m" (*(env)));\
+ asm volatile (restore " %0\n": : "m" (*(env)));\
+ for(i=0;i<5;i++)\
+ asm volatile ("fstpl %0" : "=m" (rtab[i]));\
+ for(i=0;i<5;i++)\
+ printf("res[%d]=%f\n", i, rtab[i]);\
+ printf("fpuc=%04x fpus=%04x fptag=%04x\n",\
+ (env)->fpuc,\
+ (env)->fpus & 0xff00,\
+ (env)->fptag);\
+}
+
+void test_fenv(void)
+{
+ struct __attribute__((packed)) {
+ uint16_t fpuc;
+ uint16_t dummy1;
+ uint16_t fpus;
+ uint16_t dummy2;
+ uint16_t fptag;
+ uint16_t dummy3;
+ uint32_t ignored[4];
+ long double fpregs[8];
+ } float_env32;
+ struct __attribute__((packed)) {
+ uint16_t fpuc;
+ uint16_t fpus;
+ uint16_t fptag;
+ uint16_t ignored[4];
+ long double fpregs[8];
+ } float_env16;
+ double dtab[8];
+ double rtab[8];
+ int i;
+
+ for(i=0;i<8;i++)
+ dtab[i] = i + 1;
+
+ TEST_ENV(&float_env16, "data16 fnstenv", "data16 fldenv");
+ TEST_ENV(&float_env16, "data16 fnsave", "data16 frstor");
+ TEST_ENV(&float_env32, "fnstenv", "fldenv");
+ TEST_ENV(&float_env32, "fnsave", "frstor");
+
+ /* test for ffree */
+ for(i=0;i<5;i++)
+ asm volatile ("fldl %0" : : "m" (dtab[i]));
+ asm volatile("ffree %st(2)");
+ asm volatile ("fnstenv %0\n" : : "m" (float_env32));
+ asm volatile ("fninit");
+ printf("fptag=%04x\n", float_env32.fptag);
+}
+
+
+#define TEST_FCMOV(a, b, eflags, CC)\
+{\
+ double res;\
+ asm("push %3\n"\
+ "popf\n"\
+ "fcmov" CC " %2, %0\n"\
+ : "=t" (res)\
+ : "0" (a), "u" (b), "g" (eflags));\
+ printf("fcmov%s eflags=0x%04lx-> %f\n", \
+ CC, (long)eflags, res);\
+}
+
+void test_fcmov(void)
+{
+ double a, b;
+ long eflags, i;
+
+ a = 1.0;
+ b = 2.0;
+ for(i = 0; i < 4; i++) {
+ eflags = 0;
+ if (i & 1)
+ eflags |= CC_C;
+ if (i & 2)
+ eflags |= CC_Z;
+ TEST_FCMOV(a, b, eflags, "b");
+ TEST_FCMOV(a, b, eflags, "e");
+ TEST_FCMOV(a, b, eflags, "be");
+ TEST_FCMOV(a, b, eflags, "nb");
+ TEST_FCMOV(a, b, eflags, "ne");
+ TEST_FCMOV(a, b, eflags, "nbe");
+ }
+ TEST_FCMOV(a, b, 0, "u");
+ TEST_FCMOV(a, b, CC_P, "u");
+ TEST_FCMOV(a, b, 0, "nu");
+ TEST_FCMOV(a, b, CC_P, "nu");
+}
+
+void test_floats(void)
+{
+ test_fops(2, 3);
+ test_fops(1.4, -5);
+ test_fcmp(2, -1);
+ test_fcmp(2, 2);
+ test_fcmp(2, 3);
+ test_fcmp(2, q_nan.d);
+ test_fcmp(q_nan.d, -1);
+ test_fcmp(-1.0/0.0, -1);
+ test_fcmp(1.0/0.0, -1);
+ test_fcvt(0.5);
+ test_fcvt(-0.5);
+ test_fcvt(1.0/7.0);
+ test_fcvt(-1.0/9.0);
+ test_fcvt(32768);
+ test_fcvt(-1e20);
+ test_fcvt(-1.0/0.0);
+ test_fcvt(1.0/0.0);
+ test_fcvt(q_nan.d);
+ test_fconst();
+ test_fbcd(1234567890123456.0);
+ test_fbcd(-123451234567890.0);
+ test_fenv();
+ if (TEST_CMOV) {
+ test_fcmov();
+ }
+}
+
+/**********************************************/
+#if !defined(__x86_64__)
+
+#define TEST_BCD(op, op0, cc_in, cc_mask)\
+{\
+ int res, flags;\
+ res = op0;\
+ flags = cc_in;\
+ asm ("push %3\n\t"\
+ "popf\n\t"\
+ #op "\n\t"\
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=a" (res), "=g" (flags)\
+ : "0" (res), "1" (flags));\
+ printf("%-10s A=%08x R=%08x CCIN=%04x CC=%04x\n",\
+ #op, op0, res, cc_in, flags & cc_mask);\
+}
+
+void test_bcd(void)
+{
+ TEST_BCD(daa, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(daa, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+
+ TEST_BCD(das, 0x12340503, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340507, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340559, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340560, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x1234059f, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x123405a0, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340503, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, 0, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340503, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, CC_C, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340503, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+ TEST_BCD(das, 0x12340506, CC_C | CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_A));
+
+ TEST_BCD(aaa, 0x12340205, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x12340306, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x1234040a, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x123405fa, CC_A, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x12340205, 0, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x12340306, 0, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x1234040a, 0, (CC_C | CC_A));
+ TEST_BCD(aaa, 0x123405fa, 0, (CC_C | CC_A));
+
+ TEST_BCD(aas, 0x12340205, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x12340306, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x1234040a, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x123405fa, CC_A, (CC_C | CC_A));
+ TEST_BCD(aas, 0x12340205, 0, (CC_C | CC_A));
+ TEST_BCD(aas, 0x12340306, 0, (CC_C | CC_A));
+ TEST_BCD(aas, 0x1234040a, 0, (CC_C | CC_A));
+ TEST_BCD(aas, 0x123405fa, 0, (CC_C | CC_A));
+
+ TEST_BCD(aam, 0x12340547, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));
+ TEST_BCD(aad, 0x12340407, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));
+}
+#endif
+
+#define TEST_XCHG(op, size, opconst)\
+{\
+ long op0, op1;\
+ op0 = i2l(0x12345678);\
+ op1 = i2l(0xfbca7654);\
+ asm(#op " %" size "0, %" size "1" \
+ : "=q" (op0), opconst (op1) \
+ : "0" (op0));\
+ printf("%-10s A=" FMTLX " B=" FMTLX "\n",\
+ #op, op0, op1);\
+}
+
+#define TEST_CMPXCHG(op, size, opconst, eax)\
+{\
+ long op0, op1, op2;\
+ op0 = i2l(0x12345678);\
+ op1 = i2l(0xfbca7654);\
+ op2 = i2l(eax);\
+ asm(#op " %" size "0, %" size "1" \
+ : "=q" (op0), opconst (op1) \
+ : "0" (op0), "a" (op2));\
+ printf("%-10s EAX=" FMTLX " A=" FMTLX " C=" FMTLX "\n",\
+ #op, op2, op0, op1);\
+}
+
+void test_xchg(void)
+{
+#if defined(__x86_64__)
+ TEST_XCHG(xchgq, "", "+q");
+#endif
+ TEST_XCHG(xchgl, "k", "+q");
+ TEST_XCHG(xchgw, "w", "+q");
+ TEST_XCHG(xchgb, "b", "+q");
+
+#if defined(__x86_64__)
+ TEST_XCHG(xchgq, "", "=m");
+#endif
+ TEST_XCHG(xchgl, "k", "+m");
+ TEST_XCHG(xchgw, "w", "+m");
+ TEST_XCHG(xchgb, "b", "+m");
+
+#if defined(__x86_64__)
+ TEST_XCHG(xaddq, "", "+q");
+#endif
+ TEST_XCHG(xaddl, "k", "+q");
+ TEST_XCHG(xaddw, "w", "+q");
+ TEST_XCHG(xaddb, "b", "+q");
+
+ {
+ int res;
+ res = 0x12345678;
+ asm("xaddl %1, %0" : "=r" (res) : "0" (res));
+ printf("xaddl same res=%08x\n", res);
+ }
+
+#if defined(__x86_64__)
+ TEST_XCHG(xaddq, "", "+m");
+#endif
+ TEST_XCHG(xaddl, "k", "+m");
+ TEST_XCHG(xaddw, "w", "+m");
+ TEST_XCHG(xaddb, "b", "+m");
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "+q", 0xfbca7654);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "+q", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgw, "w", "+q", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgb, "b", "+q", 0xfbca7654);
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "+q", 0xfffefdfc);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "+q", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgw, "w", "+q", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgb, "b", "+q", 0xfffefdfc);
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "+m", 0xfbca7654);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "+m", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgw, "w", "+m", 0xfbca7654);
+ TEST_CMPXCHG(cmpxchgb, "b", "+m", 0xfbca7654);
+
+#if defined(__x86_64__)
+ TEST_CMPXCHG(cmpxchgq, "", "+m", 0xfffefdfc);
+#endif
+ TEST_CMPXCHG(cmpxchgl, "k", "+m", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgw, "w", "+m", 0xfffefdfc);
+ TEST_CMPXCHG(cmpxchgb, "b", "+m", 0xfffefdfc);
+
+ {
+ uint64_t op0, op1, op2;
+ long eax, edx;
+ long i, eflags;
+
+ for(i = 0; i < 2; i++) {
+ op0 = 0x123456789abcdLL;
+ eax = i2l(op0 & 0xffffffff);
+ edx = i2l(op0 >> 32);
+ if (i == 0)
+ op1 = 0xfbca765423456LL;
+ else
+ op1 = op0;
+ op2 = 0x6532432432434LL;
+ asm("cmpxchg8b %2\n"
+ "pushf\n"
+ "pop %3\n"
+ : "=a" (eax), "=d" (edx), "=m" (op1), "=g" (eflags)
+ : "0" (eax), "1" (edx), "m" (op1), "b" ((int)op2), "c" ((int)(op2 >> 32)));
+ printf("cmpxchg8b: eax=" FMTLX " edx=" FMTLX " op1=" FMT64X " CC=%02lx\n",
+ eax, edx, op1, eflags & CC_Z);
+ }
+ }
+}
+
+#ifdef TEST_SEGS
+/**********************************************/
+/* segmentation tests */
+
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <asm/ldt.h>
+#include <linux/version.h>
+
+static inline int modify_ldt(int func, void * ptr, unsigned long bytecount)
+{
+ return syscall(__NR_modify_ldt, func, ptr, bytecount);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 66)
+#define modify_ldt_ldt_s user_desc
+#endif
+
+#define MK_SEL(n) (((n) << 3) | 7)
+
+uint8_t seg_data1[4096];
+uint8_t seg_data2[4096];
+
+#define TEST_LR(op, size, seg, mask)\
+{\
+ int res, res2;\
+ uint16_t mseg = seg;\
+ res = 0x12345678;\
+ asm (op " %" size "2, %" size "0\n" \
+ "movl $0, %1\n"\
+ "jnz 1f\n"\
+ "movl $1, %1\n"\
+ "1:\n"\
+ : "=r" (res), "=r" (res2) : "m" (mseg), "0" (res));\
+ printf(op ": Z=%d %08x\n", res2, res & ~(mask));\
+}
+
+#define TEST_ARPL(op, size, op1, op2)\
+{\
+ long a, b, c; \
+ a = (op1); \
+ b = (op2); \
+ asm volatile(op " %" size "3, %" size "0\n"\
+ "movl $0,%1\n"\
+ "jnz 1f\n"\
+ "movl $1,%1\n"\
+ "1:\n"\
+ : "=r" (a), "=r" (c) : "0" (a), "r" (b)); \
+ printf(op size " A=" FMTLX " B=" FMTLX " R=" FMTLX " z=%ld\n",\
+ (long)(op1), (long)(op2), a, c);\
+}
+
+/* NOTE: we use Linux modify_ldt syscall */
+void test_segs(void)
+{
+ struct modify_ldt_ldt_s ldt;
+ long long ldt_table[3];
+ int res, res2;
+ char tmp;
+ struct {
+ uint32_t offset;
+ uint16_t seg;
+ } __attribute__((packed)) segoff;
+
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)&seg_data1;
+ ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ ldt.entry_number = 2;
+ ldt.base_addr = (unsigned long)&seg_data2;
+ ldt.limit = (sizeof(seg_data2) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ modify_ldt(0, &ldt_table, sizeof(ldt_table)); /* read ldt entries */
+#if 0
+ {
+ int i;
+ for(i=0;i<3;i++)
+ printf("%d: %016Lx\n", i, ldt_table[i]);
+ }
+#endif
+ /* do some tests with fs or gs */
+ asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
+
+ seg_data1[1] = 0xaa;
+ seg_data2[1] = 0x55;
+
+ asm volatile ("fs movzbl 0x1, %0" : "=r" (res));
+ printf("FS[1] = %02x\n", res);
+
+ asm volatile ("pushl %%gs\n"
+ "movl %1, %%gs\n"
+ "gs movzbl 0x1, %0\n"
+ "popl %%gs\n"
+ : "=r" (res)
+ : "r" (MK_SEL(2)));
+ printf("GS[1] = %02x\n", res);
+
+ /* tests with ds/ss (implicit segment case) */
+ tmp = 0xa5;
+ asm volatile ("pushl %%ebp\n\t"
+ "pushl %%ds\n\t"
+ "movl %2, %%ds\n\t"
+ "movl %3, %%ebp\n\t"
+ "movzbl 0x1, %0\n\t"
+ "movzbl (%%ebp), %1\n\t"
+ "popl %%ds\n\t"
+ "popl %%ebp\n\t"
+ : "=r" (res), "=r" (res2)
+ : "r" (MK_SEL(1)), "r" (&tmp));
+ printf("DS[1] = %02x\n", res);
+ printf("SS[tmp] = %02x\n", res2);
+
+ segoff.seg = MK_SEL(2);
+ segoff.offset = 0xabcdef12;
+ asm volatile("lfs %2, %0\n\t"
+ "movl %%fs, %1\n\t"
+ : "=r" (res), "=g" (res2)
+ : "m" (segoff));
+ printf("FS:reg = %04x:%08x\n", res2, res);
+
+ TEST_LR("larw", "w", MK_SEL(2), 0x0100);
+ TEST_LR("larl", "", MK_SEL(2), 0x0100);
+ TEST_LR("lslw", "w", MK_SEL(2), 0);
+ TEST_LR("lsll", "", MK_SEL(2), 0);
+
+ TEST_LR("larw", "w", 0xfff8, 0);
+ TEST_LR("larl", "", 0xfff8, 0);
+ TEST_LR("lslw", "w", 0xfff8, 0);
+ TEST_LR("lsll", "", 0xfff8, 0);
+
+ TEST_ARPL("arpl", "w", 0x12345678 | 3, 0x762123c | 1);
+ TEST_ARPL("arpl", "w", 0x12345678 | 1, 0x762123c | 3);
+ TEST_ARPL("arpl", "w", 0x12345678 | 1, 0x762123c | 1);
+}
+
+/* 16 bit code test */
+extern char code16_start, code16_end;
+extern char code16_func1;
+extern char code16_func2;
+extern char code16_func3;
+
+void test_code16(void)
+{
+ struct modify_ldt_ldt_s ldt;
+ int res, res2;
+
+ /* build a code segment */
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)&code16_start;
+ ldt.limit = &code16_end - &code16_start;
+ ldt.seg_32bit = 0;
+ ldt.contents = MODIFY_LDT_CONTENTS_CODE;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 0;
+ ldt.seg_not_present = 0;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ /* call the first function */
+ asm volatile ("lcall %1, %2"
+ : "=a" (res)
+ : "i" (MK_SEL(1)), "i" (&code16_func1): "memory", "cc");
+ printf("func1() = 0x%08x\n", res);
+ asm volatile ("lcall %2, %3"
+ : "=a" (res), "=c" (res2)
+ : "i" (MK_SEL(1)), "i" (&code16_func2): "memory", "cc");
+ printf("func2() = 0x%08x spdec=%d\n", res, res2);
+ asm volatile ("lcall %1, %2"
+ : "=a" (res)
+ : "i" (MK_SEL(1)), "i" (&code16_func3): "memory", "cc");
+ printf("func3() = 0x%08x\n", res);
+}
+#endif
+
+#if defined(__x86_64__)
+asm(".globl func_lret\n"
+ "func_lret:\n"
+ "movl $0x87654641, %eax\n"
+ "lretq\n");
+#else
+asm(".globl func_lret\n"
+ "func_lret:\n"
+ "movl $0x87654321, %eax\n"
+ "lret\n"
+
+ ".globl func_iret\n"
+ "func_iret:\n"
+ "movl $0xabcd4321, %eax\n"
+ "iret\n");
+#endif
+
+extern char func_lret;
+extern char func_iret;
+
+void test_misc(void)
+{
+ char table[256];
+ long res, i;
+
+ for(i=0;i<256;i++) table[i] = 256 - i;
+ res = 0x12345678;
+ asm ("xlat" : "=a" (res) : "b" (table), "0" (res));
+ printf("xlat: EAX=" FMTLX "\n", res);
+
+#if defined(__x86_64__)
+#if 0
+ {
+ /* XXX: see if Intel Core2 and AMD64 behavior really
+ differ. Here we implemented the Intel way which is not
+ compatible yet with QEMU. */
+ static struct __attribute__((packed)) {
+ uint64_t offset;
+ uint16_t seg;
+ } desc;
+ long cs_sel;
+
+ asm volatile ("mov %%cs, %0" : "=r" (cs_sel));
+
+ asm volatile ("push %1\n"
+ "call func_lret\n"
+ : "=a" (res)
+ : "r" (cs_sel) : "memory", "cc");
+ printf("func_lret=" FMTLX "\n", res);
+
+ desc.offset = (long)&func_lret;
+ desc.seg = cs_sel;
+
+ asm volatile ("xor %%rax, %%rax\n"
+ "rex64 lcall *(%%rcx)\n"
+ : "=a" (res)
+ : "c" (&desc)
+ : "memory", "cc");
+ printf("func_lret2=" FMTLX "\n", res);
+
+ asm volatile ("push %2\n"
+ "mov $ 1f, %%rax\n"
+ "push %%rax\n"
+ "rex64 ljmp *(%%rcx)\n"
+ "1:\n"
+ : "=a" (res)
+ : "c" (&desc), "b" (cs_sel)
+ : "memory", "cc");
+ printf("func_lret3=" FMTLX "\n", res);
+ }
+#endif
+#else
+ asm volatile ("push %%cs ; call %1"
+ : "=a" (res)
+ : "m" (func_lret): "memory", "cc");
+ printf("func_lret=" FMTLX "\n", res);
+
+ asm volatile ("pushf ; push %%cs ; call %1"
+ : "=a" (res)
+ : "m" (func_iret): "memory", "cc");
+ printf("func_iret=" FMTLX "\n", res);
+#endif
+
+#if defined(__x86_64__)
+ /* specific popl test */
+ asm volatile ("push $12345432 ; push $0x9abcdef ; pop (%%rsp) ; pop %0"
+ : "=g" (res));
+ printf("popl esp=" FMTLX "\n", res);
+#else
+ /* specific popl test */
+ asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popl (%%esp) ; popl %0"
+ : "=g" (res));
+ printf("popl esp=" FMTLX "\n", res);
+
+ /* specific popw test */
+ asm volatile ("pushl $12345432 ; pushl $0x9abcdef ; popw (%%esp) ; addl $2, %%esp ; popl %0"
+ : "=g" (res));
+ printf("popw esp=" FMTLX "\n", res);
+#endif
+}
+
+uint8_t str_buffer[4096];
+
+#define TEST_STRING1(OP, size, DF, REP)\
+{\
+ long esi, edi, eax, ecx, eflags;\
+\
+ esi = (long)(str_buffer + sizeof(str_buffer) / 2);\
+ edi = (long)(str_buffer + sizeof(str_buffer) / 2) + 16;\
+ eax = i2l(0x12345678);\
+ ecx = 17;\
+\
+ asm volatile ("push $0\n\t"\
+ "popf\n\t"\
+ DF "\n\t"\
+ REP #OP size "\n\t"\
+ "cld\n\t"\
+ "pushf\n\t"\
+ "pop %4\n\t"\
+ : "=S" (esi), "=D" (edi), "=a" (eax), "=c" (ecx), "=g" (eflags)\
+ : "0" (esi), "1" (edi), "2" (eax), "3" (ecx));\
+ printf("%-10s ESI=" FMTLX " EDI=" FMTLX " EAX=" FMTLX " ECX=" FMTLX " EFL=%04x\n",\
+ REP #OP size, esi, edi, eax, ecx,\
+ (int)(eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)));\
+}
+
+#define TEST_STRING(OP, REP)\
+ TEST_STRING1(OP, "b", "", REP);\
+ TEST_STRING1(OP, "w", "", REP);\
+ TEST_STRING1(OP, "l", "", REP);\
+ X86_64_ONLY(TEST_STRING1(OP, "q", "", REP));\
+ TEST_STRING1(OP, "b", "std", REP);\
+ TEST_STRING1(OP, "w", "std", REP);\
+ TEST_STRING1(OP, "l", "std", REP);\
+ X86_64_ONLY(TEST_STRING1(OP, "q", "std", REP))
+
+void test_string(void)
+{
+ int i;
+ for(i = 0;i < sizeof(str_buffer); i++)
+ str_buffer[i] = i + 0x56;
+ TEST_STRING(stos, "");
+ TEST_STRING(stos, "rep ");
+ TEST_STRING(lods, ""); /* to verify stos */
+ TEST_STRING(lods, "rep ");
+ TEST_STRING(movs, "");
+ TEST_STRING(movs, "rep ");
+ TEST_STRING(lods, ""); /* to verify stos */
+
+ /* XXX: better tests */
+ TEST_STRING(scas, "");
+ TEST_STRING(scas, "repz ");
+ TEST_STRING(scas, "repnz ");
+ TEST_STRING(cmps, "");
+ TEST_STRING(cmps, "repz ");
+ TEST_STRING(cmps, "repnz ");
+}
+
+#ifdef TEST_VM86
+/* VM86 test */
+
+static inline void set_bit(uint8_t *a, unsigned int bit)
+{
+ a[bit / 8] |= (1 << (bit % 8));
+}
+
+static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg)
+{
+ return (uint8_t *)((seg << 4) + (reg & 0xffff));
+}
+
+static inline void pushw(struct vm86_regs *r, int val)
+{
+ r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff);
+ *(uint16_t *)seg_to_linear(r->ss, r->esp) = val;
+}
+
+static inline int vm86(int func, struct vm86plus_struct *v86)
+{
+ return syscall(__NR_vm86, func, v86);
+}
+
+extern char vm86_code_start;
+extern char vm86_code_end;
+
+#define VM86_CODE_CS 0x100
+#define VM86_CODE_IP 0x100
+
+void test_vm86(void)
+{
+ struct vm86plus_struct ctx;
+ struct vm86_regs *r;
+ uint8_t *vm86_mem;
+ int seg, ret;
+
+ vm86_mem = mmap((void *)0x00000000, 0x110000,
+ PROT_WRITE | PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
+ if (vm86_mem == MAP_FAILED) {
+ printf("ERROR: could not map vm86 memory");
+ return;
+ }
+ memset(&ctx, 0, sizeof(ctx));
+
+ /* init basic registers */
+ r = &ctx.regs;
+ r->eip = VM86_CODE_IP;
+ r->esp = 0xfffe;
+ seg = VM86_CODE_CS;
+ r->cs = seg;
+ r->ss = seg;
+ r->ds = seg;
+ r->es = seg;
+ r->fs = seg;
+ r->gs = seg;
+ r->eflags = VIF_MASK;
+
+ /* move code to proper address. We use the same layout as a .com
+ dos program. */
+ memcpy(vm86_mem + (VM86_CODE_CS << 4) + VM86_CODE_IP,
+ &vm86_code_start, &vm86_code_end - &vm86_code_start);
+
+ /* mark int 0x21 as being emulated */
+ set_bit((uint8_t *)&ctx.int_revectored, 0x21);
+
+ for(;;) {
+ ret = vm86(VM86_ENTER, &ctx);
+ switch(VM86_TYPE(ret)) {
+ case VM86_INTx:
+ {
+ int int_num, ah, v;
+
+ int_num = VM86_ARG(ret);
+ if (int_num != 0x21)
+ goto unknown_int;
+ ah = (r->eax >> 8) & 0xff;
+ switch(ah) {
+ case 0x00: /* exit */
+ goto the_end;
+ case 0x02: /* write char */
+ {
+ uint8_t c = r->edx;
+ putchar(c);
+ }
+ break;
+ case 0x09: /* write string */
+ {
+ uint8_t c, *ptr;
+ ptr = seg_to_linear(r->ds, r->edx);
+ for(;;) {
+ c = *ptr++;
+ if (c == '$')
+ break;
+ putchar(c);
+ }
+ r->eax = (r->eax & ~0xff) | '$';
+ }
+ break;
+ case 0xff: /* extension: write eflags number in edx */
+ v = (int)r->edx;
+#ifndef LINUX_VM86_IOPL_FIX
+ v &= ~0x3000;
+#endif
+ printf("%08x\n", v);
+ break;
+ default:
+ unknown_int:
+ printf("unsupported int 0x%02x\n", int_num);
+ goto the_end;
+ }
+ }
+ break;
+ case VM86_SIGNAL:
+ /* a signal came, we just ignore that */
+ break;
+ case VM86_STI:
+ break;
+ default:
+ printf("ERROR: unhandled vm86 return code (0x%x)\n", ret);
+ goto the_end;
+ }
+ }
+ the_end:
+ printf("VM86 end\n");
+ munmap(vm86_mem, 0x110000);
+}
+#endif
+
+/* exception tests */
+#if defined(__i386__) && !defined(REG_EAX)
+#define REG_EAX EAX
+#define REG_EBX EBX
+#define REG_ECX ECX
+#define REG_EDX EDX
+#define REG_ESI ESI
+#define REG_EDI EDI
+#define REG_EBP EBP
+#define REG_ESP ESP
+#define REG_EIP EIP
+#define REG_EFL EFL
+#define REG_TRAPNO TRAPNO
+#define REG_ERR ERR
+#endif
+
+#if defined(__x86_64__)
+#define REG_EIP REG_RIP
+#endif
+
+jmp_buf jmp_env;
+int v1;
+int tab[2];
+
+void sig_handler(int sig, siginfo_t *info, void *puc)
+{
+ struct ucontext *uc = puc;
+
+ printf("si_signo=%d si_errno=%d si_code=%d",
+ info->si_signo, info->si_errno, info->si_code);
+ printf(" si_addr=0x%08lx",
+ (unsigned long)info->si_addr);
+ printf("\n");
+
+ printf("trapno=" FMTLX " err=" FMTLX,
+ (long)uc->uc_mcontext.gregs[REG_TRAPNO],
+ (long)uc->uc_mcontext.gregs[REG_ERR]);
+ printf(" EIP=" FMTLX, (long)uc->uc_mcontext.gregs[REG_EIP]);
+ printf("\n");
+ longjmp(jmp_env, 1);
+}
+
+void test_exceptions(void)
+{
+ struct sigaction act;
+ volatile int val;
+
+ act.sa_sigaction = sig_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO | SA_NODEFER;
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGTRAP, &act, NULL);
+
+ /* test division by zero reporting */
+ printf("DIVZ exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* now divide by zero */
+ v1 = 0;
+ v1 = 2 / v1;
+ }
+
+#if !defined(__x86_64__)
+ printf("BOUND exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* bound exception */
+ tab[0] = 1;
+ tab[1] = 10;
+ asm volatile ("bound %0, %1" : : "r" (11), "m" (tab[0]));
+ }
+#endif
+
+#ifdef TEST_SEGS
+ printf("segment exceptions:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* load an invalid segment */
+ asm volatile ("movl %0, %%fs" : : "r" ((0x1234 << 3) | 1));
+ }
+ if (setjmp(jmp_env) == 0) {
+ /* null data segment is valid */
+ asm volatile ("movl %0, %%fs" : : "r" (3));
+ /* null stack segment */
+ asm volatile ("movl %0, %%ss" : : "r" (3));
+ }
+
+ {
+ struct modify_ldt_ldt_s ldt;
+ ldt.entry_number = 1;
+ ldt.base_addr = (unsigned long)&seg_data1;
+ ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12;
+ ldt.seg_32bit = 1;
+ ldt.contents = MODIFY_LDT_CONTENTS_DATA;
+ ldt.read_exec_only = 0;
+ ldt.limit_in_pages = 1;
+ ldt.seg_not_present = 1;
+ ldt.useable = 1;
+ modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */
+
+ if (setjmp(jmp_env) == 0) {
+ /* segment not present */
+ asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1)));
+ }
+ }
+#endif
+
+ /* test SEGV reporting */
+ printf("PF exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ val = 1;
+ /* we add a nop to test a weird PC retrieval case */
+ asm volatile ("nop");
+ /* now store in an invalid address */
+ *(char *)0x1234 = 1;
+ }
+
+ /* test SEGV reporting */
+ printf("PF exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ val = 1;
+ /* read from an invalid address */
+ v1 = *(char *)0x1234;
+ }
+
+ /* test illegal instruction reporting */
+ printf("UD2 exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* now execute an invalid instruction */
+ asm volatile("ud2");
+ }
+ printf("lock nop exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* now execute an invalid instruction */
+ asm volatile("lock nop");
+ }
+
+ printf("INT exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0xfd");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0x01");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile (".byte 0xcd, 0x03");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0x04");
+ }
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int $0x05");
+ }
+
+ printf("INT3 exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("int3");
+ }
+
+ printf("CLI exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("cli");
+ }
+
+ printf("STI exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("cli");
+ }
+
+#if !defined(__x86_64__)
+ printf("INTO exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ /* overflow exception */
+ asm volatile ("addl $1, %0 ; into" : : "r" (0x7fffffff));
+ }
+#endif
+
+ printf("OUTB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("outb %%al, %%dx" : : "d" (0x4321), "a" (0));
+ }
+
+ printf("INB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("inb %%dx, %%al" : "=a" (val) : "d" (0x4321));
+ }
+
+ printf("REP OUTSB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("rep outsb" : : "d" (0x4321), "S" (tab), "c" (1));
+ }
+
+ printf("REP INSB exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("rep insb" : : "d" (0x4321), "D" (tab), "c" (1));
+ }
+
+ printf("HLT exception:\n");
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("hlt");
+ }
+
+ printf("single step exception:\n");
+ val = 0;
+ if (setjmp(jmp_env) == 0) {
+ asm volatile ("pushf\n"
+ "orl $0x00100, (%%esp)\n"
+ "popf\n"
+ "movl $0xabcd, %0\n"
+ "movl $0x0, %0\n" : "=m" (val) : : "cc", "memory");
+ }
+ printf("val=0x%x\n", val);
+}
+
+#if !defined(__x86_64__)
+/* specific precise single step test */
+void sig_trap_handler(int sig, siginfo_t *info, void *puc)
+{
+ struct ucontext *uc = puc;
+ printf("EIP=" FMTLX "\n", (long)uc->uc_mcontext.gregs[REG_EIP]);
+}
+
+const uint8_t sstep_buf1[4] = { 1, 2, 3, 4};
+uint8_t sstep_buf2[4];
+
+void test_single_step(void)
+{
+ struct sigaction act;
+ volatile int val;
+ int i;
+
+ val = 0;
+ act.sa_sigaction = sig_trap_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ sigaction(SIGTRAP, &act, NULL);
+ asm volatile ("pushf\n"
+ "orl $0x00100, (%%esp)\n"
+ "popf\n"
+ "movl $0xabcd, %0\n"
+
+ /* jmp test */
+ "movl $3, %%ecx\n"
+ "1:\n"
+ "addl $1, %0\n"
+ "decl %%ecx\n"
+ "jnz 1b\n"
+
+ /* movsb: the single step should stop at each movsb iteration */
+ "movl $sstep_buf1, %%esi\n"
+ "movl $sstep_buf2, %%edi\n"
+ "movl $0, %%ecx\n"
+ "rep movsb\n"
+ "movl $3, %%ecx\n"
+ "rep movsb\n"
+ "movl $1, %%ecx\n"
+ "rep movsb\n"
+
+ /* cmpsb: the single step should stop at each cmpsb iteration */
+ "movl $sstep_buf1, %%esi\n"
+ "movl $sstep_buf2, %%edi\n"
+ "movl $0, %%ecx\n"
+ "rep cmpsb\n"
+ "movl $4, %%ecx\n"
+ "rep cmpsb\n"
+
+ /* getpid() syscall: single step should skip one
+ instruction */
+ "movl $20, %%eax\n"
+ "int $0x80\n"
+ "movl $0, %%eax\n"
+
+ /* when modifying SS, trace is not done on the next
+ instruction */
+ "movl %%ss, %%ecx\n"
+ "movl %%ecx, %%ss\n"
+ "addl $1, %0\n"
+ "movl $1, %%eax\n"
+ "movl %%ecx, %%ss\n"
+ "jmp 1f\n"
+ "addl $1, %0\n"
+ "1:\n"
+ "movl $1, %%eax\n"
+ "pushl %%ecx\n"
+ "popl %%ss\n"
+ "addl $1, %0\n"
+ "movl $1, %%eax\n"
+
+ "pushf\n"
+ "andl $~0x00100, (%%esp)\n"
+ "popf\n"
+ : "=m" (val)
+ :
+ : "cc", "memory", "eax", "ecx", "esi", "edi");
+ printf("val=%d\n", val);
+ for(i = 0; i < 4; i++)
+ printf("sstep_buf2[%d] = %d\n", i, sstep_buf2[i]);
+}
+
+/* self modifying code test */
+uint8_t code[] = {
+ 0xb8, 0x1, 0x00, 0x00, 0x00, /* movl $1, %eax */
+ 0xc3, /* ret */
+};
+
+asm(".section \".data\"\n"
+ "smc_code2:\n"
+ "movl 4(%esp), %eax\n"
+ "movl %eax, smc_patch_addr2 + 1\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "nop\n"
+ "smc_patch_addr2:\n"
+ "movl $1, %eax\n"
+ "ret\n"
+ ".previous\n"
+ );
+
+typedef int FuncType(void);
+extern int smc_code2(int);
+void test_self_modifying_code(void)
+{
+ int i;
+ printf("self modifying code:\n");
+ printf("func1 = 0x%x\n", ((FuncType *)code)());
+ for(i = 2; i <= 4; i++) {
+ code[1] = i;
+ printf("func%d = 0x%x\n", i, ((FuncType *)code)());
+ }
+
+ /* more difficult test : the modified code is just after the
+ modifying instruction. It is forbidden in Intel specs, but it
+ is used by old DOS programs */
+ for(i = 2; i <= 4; i++) {
+ printf("smc_code2(%d) = %d\n", i, smc_code2(i));
+ }
+}
+#endif
+
+long enter_stack[4096];
+
+#if defined(__x86_64__)
+#define RSP "%%rsp"
+#define RBP "%%rbp"
+#else
+#define RSP "%%esp"
+#define RBP "%%ebp"
+#endif
+
+#if !defined(__x86_64__)
+/* causes an infinite loop, disable it for now. */
+#define TEST_ENTER(size, stack_type, level)
+#else
+#define TEST_ENTER(size, stack_type, level)\
+{\
+ long esp_save, esp_val, ebp_val, ebp_save, i;\
+ stack_type *ptr, *stack_end, *stack_ptr;\
+ memset(enter_stack, 0, sizeof(enter_stack));\
+ stack_end = stack_ptr = (stack_type *)(enter_stack + 4096);\
+ ebp_val = (long)stack_ptr;\
+ for(i=1;i<=32;i++)\
+ *--stack_ptr = i;\
+ esp_val = (long)stack_ptr;\
+ asm("mov " RSP ", %[esp_save]\n"\
+ "mov " RBP ", %[ebp_save]\n"\
+ "mov %[esp_val], " RSP "\n"\
+ "mov %[ebp_val], " RBP "\n"\
+ "enter" size " $8, $" #level "\n"\
+ "mov " RSP ", %[esp_val]\n"\
+ "mov " RBP ", %[ebp_val]\n"\
+ "mov %[esp_save], " RSP "\n"\
+ "mov %[ebp_save], " RBP "\n"\
+ : [esp_save] "=r" (esp_save),\
+ [ebp_save] "=r" (ebp_save),\
+ [esp_val] "=r" (esp_val),\
+ [ebp_val] "=r" (ebp_val)\
+ : "[esp_val]" (esp_val),\
+ "[ebp_val]" (ebp_val));\
+ printf("level=%d:\n", level);\
+ printf("esp_val=" FMTLX "\n", esp_val - (long)stack_end);\
+ printf("ebp_val=" FMTLX "\n", ebp_val - (long)stack_end);\
+ for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\
+ printf(FMTLX "\n", (long)ptr[0]);\
+}
+#endif
+
+static void test_enter(void)
+{
+#if defined(__x86_64__)
+ TEST_ENTER("q", uint64_t, 0);
+ TEST_ENTER("q", uint64_t, 1);
+ TEST_ENTER("q", uint64_t, 2);
+ TEST_ENTER("q", uint64_t, 31);
+#else
+ TEST_ENTER("l", uint32_t, 0);
+ TEST_ENTER("l", uint32_t, 1);
+ TEST_ENTER("l", uint32_t, 2);
+ TEST_ENTER("l", uint32_t, 31);
+#endif
+
+ TEST_ENTER("w", uint16_t, 0);
+ TEST_ENTER("w", uint16_t, 1);
+ TEST_ENTER("w", uint16_t, 2);
+ TEST_ENTER("w", uint16_t, 31);
+}
+
+#ifdef TEST_SSE
+
+typedef int __m64 __attribute__ ((__mode__ (__V2SI__)));
+typedef float __m128 __attribute__ ((__mode__(__V4SF__)));
+
+typedef union {
+ double d[2];
+ float s[4];
+ uint32_t l[4];
+ uint64_t q[2];
+ __m128 dq;
+} XMMReg;
+
+static uint64_t __attribute__((aligned(16))) test_values[4][2] = {
+ { 0x456723c698694873, 0xdc515cff944a58ec },
+ { 0x1f297ccd58bad7ab, 0x41f21efba9e3e146 },
+ { 0x007c62c2085427f8, 0x231be9e8cde7438d },
+ { 0x0f76255a085427f8, 0xc233e9e8c4c9439a },
+};
+
+#define SSE_OP(op)\
+{\
+ asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ b.q[1], b.q[0],\
+ r.q[1], r.q[0]);\
+}
+
+#define SSE_OP2(op)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ b.q[0] = test_values[2*i+1][0];\
+ b.q[1] = test_values[2*i+1][1];\
+ SSE_OP(op);\
+ }\
+}
+
+#define MMX_OP2(op)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ b.q[0] = test_values[2*i+1][0];\
+ asm volatile (#op " %2, %0" : "=y" (r.q[0]) : "0" (a.q[0]), "y" (b.q[0]));\
+ printf("%-9s: a=" FMT64X " b=" FMT64X " r=" FMT64X "\n",\
+ #op,\
+ a.q[0],\
+ b.q[0],\
+ r.q[0]);\
+ }\
+ SSE_OP2(op);\
+}
+
+#define SHUF_OP(op, ib)\
+{\
+ a.q[0] = test_values[0][0];\
+ a.q[1] = test_values[0][1];\
+ b.q[0] = test_values[1][0];\
+ b.q[1] = test_values[1][1];\
+ asm volatile (#op " $" #ib ", %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ b.q[1], b.q[0],\
+ ib,\
+ r.q[1], r.q[0]);\
+}
+
+#define PSHUF_OP(op, ib)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ asm volatile (#op " $" #ib ", %1, %0" : "=x" (r.dq) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ ib,\
+ r.q[1], r.q[0]);\
+ }\
+}
+
+#define SHIFT_IM(op, ib)\
+{\
+ int i;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ asm volatile (#op " $" #ib ", %0" : "=x" (r.dq) : "0" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " ib=%02x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ ib,\
+ r.q[1], r.q[0]);\
+ }\
+}
+
+#define SHIFT_OP(op, ib)\
+{\
+ int i;\
+ SHIFT_IM(op, ib);\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ b.q[0] = ib;\
+ b.q[1] = 0;\
+ asm volatile (#op " %2, %0" : "=x" (r.dq) : "0" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ b.q[1], b.q[0],\
+ r.q[1], r.q[0]);\
+ }\
+}
+
+#define MOVMSK(op)\
+{\
+ int i, reg;\
+ for(i=0;i<2;i++) {\
+ a.q[0] = test_values[2*i][0];\
+ a.q[1] = test_values[2*i][1];\
+ asm volatile (#op " %1, %0" : "=r" (reg) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ reg);\
+ }\
+}
+
+#define SSE_OPS(a) \
+SSE_OP(a ## ps);\
+SSE_OP(a ## ss);
+
+#define SSE_OPD(a) \
+SSE_OP(a ## pd);\
+SSE_OP(a ## sd);
+
+#define SSE_COMI(op, field)\
+{\
+ unsigned int eflags;\
+ XMMReg a, b;\
+ a.field[0] = a1;\
+ b.field[0] = b1;\
+ asm volatile (#op " %2, %1\n"\
+ "pushf\n"\
+ "pop %0\n"\
+ : "=m" (eflags)\
+ : "x" (a.dq), "x" (b.dq));\
+ printf("%-9s: a=%f b=%f cc=%04x\n",\
+ #op, a1, b1,\
+ eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\
+}
+
+void test_sse_comi(double a1, double b1)
+{
+ SSE_COMI(ucomiss, s);
+ SSE_COMI(ucomisd, d);
+ SSE_COMI(comiss, s);
+ SSE_COMI(comisd, d);
+}
+
+#define CVT_OP_XMM(op)\
+{\
+ asm volatile (#op " %1, %0" : "=x" (r.dq) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ r.q[1], r.q[0]);\
+}
+
+/* Force %xmm0 usage to avoid the case where both register index are 0
+ to test intruction decoding more extensively */
+#define CVT_OP_XMM2MMX(op)\
+{\
+ asm volatile (#op " %1, %0" : "=y" (r.q[0]) : "x" (a.dq) \
+ : "%xmm0"); \
+ asm volatile("emms\n"); \
+ printf("%-9s: a=" FMT64X "" FMT64X " r=" FMT64X "\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ r.q[0]);\
+}
+
+#define CVT_OP_MMX2XMM(op)\
+{\
+ asm volatile (#op " %1, %0" : "=x" (r.dq) : "y" (a.q[0]));\
+ asm volatile("emms\n"); \
+ printf("%-9s: a=" FMT64X " r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.q[0],\
+ r.q[1], r.q[0]);\
+}
+
+#define CVT_OP_REG2XMM(op)\
+{\
+ asm volatile (#op " %1, %0" : "=x" (r.dq) : "r" (a.l[0]));\
+ printf("%-9s: a=%08x r=" FMT64X "" FMT64X "\n",\
+ #op,\
+ a.l[0],\
+ r.q[1], r.q[0]);\
+}
+
+#define CVT_OP_XMM2REG(op)\
+{\
+ asm volatile (#op " %1, %0" : "=r" (r.l[0]) : "x" (a.dq));\
+ printf("%-9s: a=" FMT64X "" FMT64X " r=%08x\n",\
+ #op,\
+ a.q[1], a.q[0],\
+ r.l[0]);\
+}
+
+struct fpxstate {
+ uint16_t fpuc;
+ uint16_t fpus;
+ uint16_t fptag;
+ uint16_t fop;
+ uint32_t fpuip;
+ uint16_t cs_sel;
+ uint16_t dummy0;
+ uint32_t fpudp;
+ uint16_t ds_sel;
+ uint16_t dummy1;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ uint8_t fpregs1[8 * 16];
+ uint8_t xmm_regs[8 * 16];
+ uint8_t dummy2[224];
+};
+
+static struct fpxstate fpx_state __attribute__((aligned(16)));
+static struct fpxstate fpx_state2 __attribute__((aligned(16)));
+
+void test_fxsave(void)
+{
+ struct fpxstate *fp = &fpx_state;
+ struct fpxstate *fp2 = &fpx_state2;
+ int i, nb_xmm;
+ XMMReg a, b;
+ a.q[0] = test_values[0][0];
+ a.q[1] = test_values[0][1];
+ b.q[0] = test_values[1][0];
+ b.q[1] = test_values[1][1];
+
+ asm("movdqa %2, %%xmm0\n"
+ "movdqa %3, %%xmm7\n"
+#if defined(__x86_64__)
+ "movdqa %2, %%xmm15\n"
+#endif
+ " fld1\n"
+ " fldpi\n"
+ " fldln2\n"
+ " fxsave %0\n"
+ " fxrstor %0\n"
+ " fxsave %1\n"
+ " fninit\n"
+ : "=m" (*(uint32_t *)fp2), "=m" (*(uint32_t *)fp)
+ : "m" (a), "m" (b));
+ printf("fpuc=%04x\n", fp->fpuc);
+ printf("fpus=%04x\n", fp->fpus);
+ printf("fptag=%04x\n", fp->fptag);
+ for(i = 0; i < 3; i++) {
+ printf("ST%d: " FMT64X " %04x\n",
+ i,
+ *(uint64_t *)&fp->fpregs1[i * 16],
+ *(uint16_t *)&fp->fpregs1[i * 16 + 8]);
+ }
+ printf("mxcsr=%08x\n", fp->mxcsr & 0x1f80);
+#if defined(__x86_64__)
+ nb_xmm = 16;
+#else
+ nb_xmm = 8;
+#endif
+ for(i = 0; i < nb_xmm; i++) {
+ printf("xmm%d: " FMT64X "" FMT64X "\n",
+ i,
+ *(uint64_t *)&fp->xmm_regs[i * 16],
+ *(uint64_t *)&fp->xmm_regs[i * 16 + 8]);
+ }
+}
+
+void test_sse(void)
+{
+ XMMReg r, a, b;
+ int i;
+
+ MMX_OP2(punpcklbw);
+ MMX_OP2(punpcklwd);
+ MMX_OP2(punpckldq);
+ MMX_OP2(packsswb);
+ MMX_OP2(pcmpgtb);
+ MMX_OP2(pcmpgtw);
+ MMX_OP2(pcmpgtd);
+ MMX_OP2(packuswb);
+ MMX_OP2(punpckhbw);
+ MMX_OP2(punpckhwd);
+ MMX_OP2(punpckhdq);
+ MMX_OP2(packssdw);
+ MMX_OP2(pcmpeqb);
+ MMX_OP2(pcmpeqw);
+ MMX_OP2(pcmpeqd);
+
+ MMX_OP2(paddq);
+ MMX_OP2(pmullw);
+ MMX_OP2(psubusb);
+ MMX_OP2(psubusw);
+ MMX_OP2(pminub);
+ MMX_OP2(pand);
+ MMX_OP2(paddusb);
+ MMX_OP2(paddusw);
+ MMX_OP2(pmaxub);
+ MMX_OP2(pandn);
+
+ MMX_OP2(pmulhuw);
+ MMX_OP2(pmulhw);
+
+ MMX_OP2(psubsb);
+ MMX_OP2(psubsw);
+ MMX_OP2(pminsw);
+ MMX_OP2(por);
+ MMX_OP2(paddsb);
+ MMX_OP2(paddsw);
+ MMX_OP2(pmaxsw);
+ MMX_OP2(pxor);
+ MMX_OP2(pmuludq);
+ MMX_OP2(pmaddwd);
+ MMX_OP2(psadbw);
+ MMX_OP2(psubb);
+ MMX_OP2(psubw);
+ MMX_OP2(psubd);
+ MMX_OP2(psubq);
+ MMX_OP2(paddb);
+ MMX_OP2(paddw);
+ MMX_OP2(paddd);
+
+ MMX_OP2(pavgb);
+ MMX_OP2(pavgw);
+
+ asm volatile ("pinsrw $1, %1, %0" : "=y" (r.q[0]) : "r" (0x12345678));
+ printf("%-9s: r=" FMT64X "\n", "pinsrw", r.q[0]);
+
+ asm volatile ("pinsrw $5, %1, %0" : "=x" (r.dq) : "r" (0x12345678));
+ printf("%-9s: r=" FMT64X "" FMT64X "\n", "pinsrw", r.q[1], r.q[0]);
+
+ a.q[0] = test_values[0][0];
+ a.q[1] = test_values[0][1];
+ asm volatile ("pextrw $1, %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
+ printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
+
+ asm volatile ("pextrw $5, %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
+ printf("%-9s: r=%08x\n", "pextrw", r.l[0]);
+
+ asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "y" (a.q[0]));
+ printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
+
+ asm volatile ("pmovmskb %1, %0" : "=r" (r.l[0]) : "x" (a.dq));
+ printf("%-9s: r=%08x\n", "pmovmskb", r.l[0]);
+
+ {
+ r.q[0] = -1;
+ r.q[1] = -1;
+
+ a.q[0] = test_values[0][0];
+ a.q[1] = test_values[0][1];
+ b.q[0] = test_values[1][0];
+ b.q[1] = test_values[1][1];
+ asm volatile("maskmovq %1, %0" :
+ : "y" (a.q[0]), "y" (b.q[0]), "D" (&r)
+ : "memory");
+ printf("%-9s: r=" FMT64X " a=" FMT64X " b=" FMT64X "\n",
+ "maskmov",
+ r.q[0],
+ a.q[0],
+ b.q[0]);
+ asm volatile("maskmovdqu %1, %0" :
+ : "x" (a.dq), "x" (b.dq), "D" (&r)
+ : "memory");
+ printf("%-9s: r=" FMT64X "" FMT64X " a=" FMT64X "" FMT64X " b=" FMT64X "" FMT64X "\n",
+ "maskmov",
+ r.q[1], r.q[0],
+ a.q[1], a.q[0],
+ b.q[1], b.q[0]);
+ }
+
+ asm volatile ("emms");
+
+ SSE_OP2(punpcklqdq);
+ SSE_OP2(punpckhqdq);
+ SSE_OP2(andps);
+ SSE_OP2(andpd);
+ SSE_OP2(andnps);
+ SSE_OP2(andnpd);
+ SSE_OP2(orps);
+ SSE_OP2(orpd);
+ SSE_OP2(xorps);
+ SSE_OP2(xorpd);
+
+ SSE_OP2(unpcklps);
+ SSE_OP2(unpcklpd);
+ SSE_OP2(unpckhps);
+ SSE_OP2(unpckhpd);
+
+ SHUF_OP(shufps, 0x78);
+ SHUF_OP(shufpd, 0x02);
+
+ PSHUF_OP(pshufd, 0x78);
+ PSHUF_OP(pshuflw, 0x78);
+ PSHUF_OP(pshufhw, 0x78);
+
+ SHIFT_OP(psrlw, 7);
+ SHIFT_OP(psrlw, 16);
+ SHIFT_OP(psraw, 7);
+ SHIFT_OP(psraw, 16);
+ SHIFT_OP(psllw, 7);
+ SHIFT_OP(psllw, 16);
+
+ SHIFT_OP(psrld, 7);
+ SHIFT_OP(psrld, 32);
+ SHIFT_OP(psrad, 7);
+ SHIFT_OP(psrad, 32);
+ SHIFT_OP(pslld, 7);
+ SHIFT_OP(pslld, 32);
+
+ SHIFT_OP(psrlq, 7);
+ SHIFT_OP(psrlq, 32);
+ SHIFT_OP(psllq, 7);
+ SHIFT_OP(psllq, 32);
+
+ SHIFT_IM(psrldq, 16);
+ SHIFT_IM(psrldq, 7);
+ SHIFT_IM(pslldq, 16);
+ SHIFT_IM(pslldq, 7);
+
+ MOVMSK(movmskps);
+ MOVMSK(movmskpd);
+
+ /* FPU specific ops */
+
+ {
+ uint32_t mxcsr;
+ asm volatile("stmxcsr %0" : "=m" (mxcsr));
+ printf("mxcsr=%08x\n", mxcsr & 0x1f80);
+ asm volatile("ldmxcsr %0" : : "m" (mxcsr));
+ }
+
+ test_sse_comi(2, -1);
+ test_sse_comi(2, 2);
+ test_sse_comi(2, 3);
+ test_sse_comi(2, q_nan.d);
+ test_sse_comi(q_nan.d, -1);
+
+ for(i = 0; i < 2; i++) {
+ a.s[0] = 2.7;
+ a.s[1] = 3.4;
+ a.s[2] = 4;
+ a.s[3] = -6.3;
+ b.s[0] = 45.7;
+ b.s[1] = 353.4;
+ b.s[2] = 4;
+ b.s[3] = 56.3;
+ if (i == 1) {
+ a.s[0] = q_nan.d;
+ b.s[3] = q_nan.d;
+ }
+
+ SSE_OPS(add);
+ SSE_OPS(mul);
+ SSE_OPS(sub);
+ SSE_OPS(min);
+ SSE_OPS(div);
+ SSE_OPS(max);
+ SSE_OPS(sqrt);
+ SSE_OPS(cmpeq);
+ SSE_OPS(cmplt);
+ SSE_OPS(cmple);
+ SSE_OPS(cmpunord);
+ SSE_OPS(cmpneq);
+ SSE_OPS(cmpnlt);
+ SSE_OPS(cmpnle);
+ SSE_OPS(cmpord);
+
+
+ a.d[0] = 2.7;
+ a.d[1] = -3.4;
+ b.d[0] = 45.7;
+ b.d[1] = -53.4;
+ if (i == 1) {
+ a.d[0] = q_nan.d;
+ b.d[1] = q_nan.d;
+ }
+ SSE_OPD(add);
+ SSE_OPD(mul);
+ SSE_OPD(sub);
+ SSE_OPD(min);
+ SSE_OPD(div);
+ SSE_OPD(max);
+ SSE_OPD(sqrt);
+ SSE_OPD(cmpeq);
+ SSE_OPD(cmplt);
+ SSE_OPD(cmple);
+ SSE_OPD(cmpunord);
+ SSE_OPD(cmpneq);
+ SSE_OPD(cmpnlt);
+ SSE_OPD(cmpnle);
+ SSE_OPD(cmpord);
+ }
+
+ /* float to float/int */
+ a.s[0] = 2.7;
+ a.s[1] = 3.4;
+ a.s[2] = 4;
+ a.s[3] = -6.3;
+ CVT_OP_XMM(cvtps2pd);
+ CVT_OP_XMM(cvtss2sd);
+ CVT_OP_XMM2MMX(cvtps2pi);
+ CVT_OP_XMM2MMX(cvttps2pi);
+ CVT_OP_XMM2REG(cvtss2si);
+ CVT_OP_XMM2REG(cvttss2si);
+ CVT_OP_XMM(cvtps2dq);
+ CVT_OP_XMM(cvttps2dq);
+
+ a.d[0] = 2.6;
+ a.d[1] = -3.4;
+ CVT_OP_XMM(cvtpd2ps);
+ CVT_OP_XMM(cvtsd2ss);
+ CVT_OP_XMM2MMX(cvtpd2pi);
+ CVT_OP_XMM2MMX(cvttpd2pi);
+ CVT_OP_XMM2REG(cvtsd2si);
+ CVT_OP_XMM2REG(cvttsd2si);
+ CVT_OP_XMM(cvtpd2dq);
+ CVT_OP_XMM(cvttpd2dq);
+
+ /* sse/mmx moves */
+ CVT_OP_XMM2MMX(movdq2q);
+ CVT_OP_MMX2XMM(movq2dq);
+
+ /* int to float */
+ a.l[0] = -6;
+ a.l[1] = 2;
+ a.l[2] = 100;
+ a.l[3] = -60000;
+ CVT_OP_MMX2XMM(cvtpi2ps);
+ CVT_OP_MMX2XMM(cvtpi2pd);
+ CVT_OP_REG2XMM(cvtsi2ss);
+ CVT_OP_REG2XMM(cvtsi2sd);
+ CVT_OP_XMM(cvtdq2ps);
+ CVT_OP_XMM(cvtdq2pd);
+
+ /* XXX: test PNI insns */
+#if 0
+ SSE_OP2(movshdup);
+#endif
+ asm volatile ("emms");
+}
+
+#endif
+
+#define TEST_CONV_RAX(op)\
+{\
+ unsigned long a, r;\
+ a = i2l(0x8234a6f8);\
+ r = a;\
+ asm volatile(#op : "=a" (r) : "0" (r));\
+ printf("%-10s A=" FMTLX " R=" FMTLX "\n", #op, a, r);\
+}
+
+#define TEST_CONV_RAX_RDX(op)\
+{\
+ unsigned long a, d, r, rh; \
+ a = i2l(0x8234a6f8);\
+ d = i2l(0x8345a1f2);\
+ r = a;\
+ rh = d;\
+ asm volatile(#op : "=a" (r), "=d" (rh) : "0" (r), "1" (rh)); \
+ printf("%-10s A=" FMTLX " R=" FMTLX ":" FMTLX "\n", #op, a, r, rh); \
+}
+
+void test_conv(void)
+{
+ TEST_CONV_RAX(cbw);
+ TEST_CONV_RAX(cwde);
+#if defined(__x86_64__)
+ TEST_CONV_RAX(cdqe);
+#endif
+
+ TEST_CONV_RAX_RDX(cwd);
+ TEST_CONV_RAX_RDX(cdq);
+#if defined(__x86_64__)
+ TEST_CONV_RAX_RDX(cqo);
+#endif
+
+ {
+ unsigned long a, r;
+ a = i2l(0x12345678);
+ asm volatile("bswapl %k0" : "=r" (r) : "0" (a));
+ printf("%-10s: A=" FMTLX " R=" FMTLX "\n", "bswapl", a, r);
+ }
+#if defined(__x86_64__)
+ {
+ unsigned long a, r;
+ a = i2l(0x12345678);
+ asm volatile("bswapq %0" : "=r" (r) : "0" (a));
+ printf("%-10s: A=" FMTLX " R=" FMTLX "\n", "bswapq", a, r);
+ }
+#endif
+}
+
+extern void *__start_initcall;
+extern void *__stop_initcall;
+
+
+int main(int argc, char **argv)
+{
+ void **ptr;
+ void (*func)(void);
+
+ ptr = &__start_initcall;
+ while (ptr != &__stop_initcall) {
+ func = *ptr++;
+ func();
+ }
+ test_bsx();
+ test_mul();
+ test_jcc();
+ test_loop();
+ test_floats();
+#if !defined(__x86_64__)
+ test_bcd();
+#endif
+ test_xchg();
+ test_string();
+ test_misc();
+ test_lea();
+#ifdef TEST_SEGS
+ test_segs();
+ test_code16();
+#endif
+#ifdef TEST_VM86
+ test_vm86();
+#endif
+#if !defined(__x86_64__)
+ test_exceptions();
+ test_self_modifying_code();
+ test_single_step();
+#endif
+ test_enter();
+ test_conv();
+#ifdef TEST_SSE
+ test_sse();
+ test_fxsave();
+#endif
+ return 0;
+}
diff --git a/tests/test-i386.h b/tests/test-i386.h
new file mode 100644
index 0000000..75106b8
--- /dev/null
+++ b/tests/test-i386.h
@@ -0,0 +1,152 @@
+
+#define exec_op glue(exec_, OP)
+#define exec_opq glue(glue(exec_, OP), q)
+#define exec_opl glue(glue(exec_, OP), l)
+#define exec_opw glue(glue(exec_, OP), w)
+#define exec_opb glue(glue(exec_, OP), b)
+
+#define EXECOP2(size, rsize, res, s1, flags) \
+ asm ("push %4\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %" rsize "2, %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=q" (res), "=g" (flags)\
+ : "q" (s1), "0" (res), "1" (flags)); \
+ printf("%-10s A=" FMTLX " B=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \
+ stringify(OP) size, s0, s1, res, iflags, flags & CC_MASK);
+
+#define EXECOP1(size, rsize, res, flags) \
+ asm ("push %3\n\t"\
+ "popf\n\t"\
+ stringify(OP) size " %" rsize "0\n\t" \
+ "pushf\n\t"\
+ "pop %1\n\t"\
+ : "=q" (res), "=g" (flags)\
+ : "0" (res), "1" (flags)); \
+ printf("%-10s A=" FMTLX " R=" FMTLX " CCIN=%04lx CC=%04lx\n", \
+ stringify(OP) size, s0, res, iflags, flags & CC_MASK);
+
+#ifdef OP1
+#if defined(__x86_64__)
+void exec_opq(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("q", "", res, flags);
+}
+#endif
+
+void exec_opl(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("l", "k", res, flags);
+}
+
+void exec_opw(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("w", "w", res, flags);
+}
+
+void exec_opb(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP1("b", "b", res, flags);
+}
+#else
+#if defined(__x86_64__)
+void exec_opq(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("q", "", res, s1, flags);
+}
+#endif
+
+void exec_opl(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("l", "k", res, s1, flags);
+}
+
+void exec_opw(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("w", "w", res, s1, flags);
+}
+
+void exec_opb(long s0, long s1, long iflags)
+{
+ long res, flags;
+ res = s0;
+ flags = iflags;
+ EXECOP2("b", "b", res, s1, flags);
+}
+#endif
+
+void exec_op(long s0, long s1)
+{
+ s0 = i2l(s0);
+ s1 = i2l(s1);
+#if defined(__x86_64__)
+ exec_opq(s0, s1, 0);
+#endif
+ exec_opl(s0, s1, 0);
+ exec_opw(s0, s1, 0);
+ exec_opb(s0, s1, 0);
+#ifdef OP_CC
+#if defined(__x86_64__)
+ exec_opq(s0, s1, CC_C);
+#endif
+ exec_opl(s0, s1, CC_C);
+ exec_opw(s0, s1, CC_C);
+ exec_opb(s0, s1, CC_C);
+#endif
+}
+
+void glue(test_, OP)(void)
+{
+ exec_op(0x12345678, 0x812FADA);
+ exec_op(0x12341, 0x12341);
+ exec_op(0x12341, -0x12341);
+ exec_op(0xffffffff, 0);
+ exec_op(0xffffffff, -1);
+ exec_op(0xffffffff, 1);
+ exec_op(0xffffffff, 2);
+ exec_op(0x7fffffff, 0);
+ exec_op(0x7fffffff, 1);
+ exec_op(0x7fffffff, -1);
+ exec_op(0x80000000, -1);
+ exec_op(0x80000000, 1);
+ exec_op(0x80000000, -2);
+ exec_op(0x12347fff, 0);
+ exec_op(0x12347fff, 1);
+ exec_op(0x12347fff, -1);
+ exec_op(0x12348000, -1);
+ exec_op(0x12348000, 1);
+ exec_op(0x12348000, -2);
+ exec_op(0x12347f7f, 0);
+ exec_op(0x12347f7f, 1);
+ exec_op(0x12347f7f, -1);
+ exec_op(0x12348080, -1);
+ exec_op(0x12348080, 1);
+ exec_op(0x12348080, -2);
+}
+
+void *glue(_test_, OP) __init_call = glue(test_, OP);
+
+#undef OP
+#undef OP_CC
diff --git a/tests/test-mmap.c b/tests/test-mmap.c
new file mode 100644
index 0000000..fcb365f
--- /dev/null
+++ b/tests/test-mmap.c
@@ -0,0 +1,476 @@
+/*
+ * Small test program to verify simulated mmap behaviour.
+ *
+ * When running qemu-linux-user with the -p flag, you may need to tell
+ * this test program about the pagesize because getpagesize() will not reflect
+ * the -p choice. Simply pass one argument beeing the pagesize.
+ *
+ * Copyright (c) 2007 AXIS Communications AB
+ * Written by Edgar E. Iglesias.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+
+#define D(x)
+
+#define fail_unless(x) \
+do \
+{ \
+ if (!(x)) { \
+ fprintf (stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \
+ exit (EXIT_FAILURE); \
+ } \
+} while (0);
+
+unsigned char *dummybuf;
+static unsigned int pagesize;
+static unsigned int pagemask;
+int test_fd;
+size_t test_fsize;
+
+void check_aligned_anonymous_unfixed_mmaps(void)
+{
+ void *p1;
+ void *p2;
+ void *p3;
+ void *p4;
+ void *p5;
+ uintptr_t p;
+ int i;
+
+ fprintf (stderr, "%s", __func__);
+ for (i = 0; i < 0x1fff; i++)
+ {
+ size_t len;
+
+ len = pagesize + (pagesize * i & 7);
+ p1 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ p2 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ p3 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ p4 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ p5 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ /* Make sure we get pages aligned with the pagesize. The
+ target expects this. */
+ fail_unless (p1 != MAP_FAILED);
+ fail_unless (p2 != MAP_FAILED);
+ fail_unless (p3 != MAP_FAILED);
+ fail_unless (p4 != MAP_FAILED);
+ fail_unless (p5 != MAP_FAILED);
+ p = (uintptr_t) p1;
+ D(printf ("p=%x\n", p));
+ fail_unless ((p & pagemask) == 0);
+ p = (uintptr_t) p2;
+ fail_unless ((p & pagemask) == 0);
+ p = (uintptr_t) p3;
+ fail_unless ((p & pagemask) == 0);
+ p = (uintptr_t) p4;
+ fail_unless ((p & pagemask) == 0);
+ p = (uintptr_t) p5;
+ fail_unless ((p & pagemask) == 0);
+
+ /* Make sure we can read from the entire area. */
+ memcpy (dummybuf, p1, pagesize);
+ memcpy (dummybuf, p2, pagesize);
+ memcpy (dummybuf, p3, pagesize);
+ memcpy (dummybuf, p4, pagesize);
+ memcpy (dummybuf, p5, pagesize);
+
+ munmap (p1, len);
+ munmap (p2, len);
+ munmap (p3, len);
+ munmap (p4, len);
+ munmap (p5, len);
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_large_anonymous_unfixed_mmap(void)
+{
+ void *p1;
+ uintptr_t p;
+ size_t len;
+
+ fprintf (stderr, "%s", __func__);
+
+ len = 0x02000000;
+ p1 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ /* Make sure we get pages aligned with the pagesize. The
+ target expects this. */
+ fail_unless (p1 != MAP_FAILED);
+ p = (uintptr_t) p1;
+ fail_unless ((p & pagemask) == 0);
+
+ /* Make sure we can read from the entire area. */
+ memcpy (dummybuf, p1, pagesize);
+ munmap (p1, len);
+ fprintf (stderr, " passed\n");
+}
+
+void check_aligned_anonymous_unfixed_colliding_mmaps(void)
+{
+ char *p1;
+ char *p2;
+ char *p3;
+ uintptr_t p;
+ int i;
+
+ fprintf (stderr, "%s", __func__);
+ for (i = 0; i < 0x2fff; i++)
+ {
+ int nlen;
+ p1 = mmap(NULL, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ fail_unless (p1 != MAP_FAILED);
+ p = (uintptr_t) p1;
+ fail_unless ((p & pagemask) == 0);
+ memcpy (dummybuf, p1, pagesize);
+
+ p2 = mmap(NULL, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ fail_unless (p2 != MAP_FAILED);
+ p = (uintptr_t) p2;
+ fail_unless ((p & pagemask) == 0);
+ memcpy (dummybuf, p2, pagesize);
+
+
+ munmap (p1, pagesize);
+ nlen = pagesize * 8;
+ p3 = mmap(NULL, nlen, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ /* Check if the mmaped areas collide. */
+ if (p3 < p2
+ && (p3 + nlen) > p2)
+ fail_unless (0);
+
+ memcpy (dummybuf, p3, pagesize);
+
+ /* Make sure we get pages aligned with the pagesize. The
+ target expects this. */
+ fail_unless (p3 != MAP_FAILED);
+ p = (uintptr_t) p3;
+ fail_unless ((p & pagemask) == 0);
+ munmap (p2, pagesize);
+ munmap (p3, nlen);
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_aligned_anonymous_fixed_mmaps(void)
+{
+ char *addr;
+ void *p1;
+ uintptr_t p;
+ int i;
+
+ /* Find a suitable address to start with. */
+ addr = mmap(NULL, pagesize * 40, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ fprintf (stderr, "%s addr=%p", __func__, addr);
+ fail_unless (addr != MAP_FAILED);
+
+ for (i = 0; i < 40; i++)
+ {
+ /* Create submaps within our unfixed map. */
+ p1 = mmap(addr, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ -1, 0);
+ /* Make sure we get pages aligned with the pagesize.
+ The target expects this. */
+ p = (uintptr_t) p1;
+ fail_unless (p1 == addr);
+ fail_unless ((p & pagemask) == 0);
+ memcpy (dummybuf, p1, pagesize);
+ munmap (p1, pagesize);
+ addr += pagesize;
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_aligned_anonymous_fixed_mmaps_collide_with_host(void)
+{
+ char *addr;
+ void *p1;
+ uintptr_t p;
+ int i;
+
+ /* Find a suitable address to start with. Right were the x86 hosts
+ stack is. */
+ addr = ((void *)0x80000000);
+ fprintf (stderr, "%s addr=%p", __func__, addr);
+ fprintf (stderr, "FIXME: QEMU fails to track pages used by the host.");
+
+ for (i = 0; i < 20; i++)
+ {
+ /* Create submaps within our unfixed map. */
+ p1 = mmap(addr, pagesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ -1, 0);
+ /* Make sure we get pages aligned with the pagesize.
+ The target expects this. */
+ p = (uintptr_t) p1;
+ fail_unless (p1 == addr);
+ fail_unless ((p & pagemask) == 0);
+ memcpy (p1, dummybuf, pagesize);
+ munmap (p1, pagesize);
+ addr += pagesize;
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_file_unfixed_mmaps(void)
+{
+ unsigned int *p1, *p2, *p3;
+ uintptr_t p;
+ int i;
+
+ fprintf (stderr, "%s", __func__);
+ for (i = 0; i < 0x10; i++)
+ {
+ size_t len;
+
+ len = pagesize;
+ p1 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE,
+ test_fd, 0);
+ p2 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE,
+ test_fd, pagesize);
+ p3 = mmap(NULL, len, PROT_READ,
+ MAP_PRIVATE,
+ test_fd, pagesize * 2);
+
+ fail_unless (p1 != MAP_FAILED);
+ fail_unless (p2 != MAP_FAILED);
+ fail_unless (p3 != MAP_FAILED);
+
+ /* Make sure we get pages aligned with the pagesize. The
+ target expects this. */
+ p = (uintptr_t) p1;
+ fail_unless ((p & pagemask) == 0);
+ p = (uintptr_t) p2;
+ fail_unless ((p & pagemask) == 0);
+ p = (uintptr_t) p3;
+ fail_unless ((p & pagemask) == 0);
+
+ /* Verify that the file maps was made correctly. */
+ D(printf ("p1=%d p2=%d p3=%d\n", *p1, *p2, *p3));
+ fail_unless (*p1 == 0);
+ fail_unless (*p2 == (pagesize / sizeof *p2));
+ fail_unless (*p3 == ((pagesize * 2) / sizeof *p3));
+
+ memcpy (dummybuf, p1, pagesize);
+ memcpy (dummybuf, p2, pagesize);
+ memcpy (dummybuf, p3, pagesize);
+ munmap (p1, len);
+ munmap (p2, len);
+ munmap (p3, len);
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_file_unfixed_eof_mmaps(void)
+{
+ char *cp;
+ unsigned int *p1;
+ uintptr_t p;
+ int i;
+
+ fprintf (stderr, "%s", __func__);
+ for (i = 0; i < 0x10; i++)
+ {
+ p1 = mmap(NULL, pagesize, PROT_READ,
+ MAP_PRIVATE,
+ test_fd,
+ (test_fsize - sizeof *p1) & ~pagemask);
+
+ fail_unless (p1 != MAP_FAILED);
+
+ /* Make sure we get pages aligned with the pagesize. The
+ target expects this. */
+ p = (uintptr_t) p1;
+ fail_unless ((p & pagemask) == 0);
+ /* Verify that the file maps was made correctly. */
+ fail_unless (p1[(test_fsize & pagemask) / sizeof *p1 - 1]
+ == ((test_fsize - sizeof *p1) / sizeof *p1));
+
+ /* Verify that the end of page is accessable and zeroed. */
+ cp = (void *) p1;
+ fail_unless (cp[pagesize - 4] == 0);
+ munmap (p1, pagesize);
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_file_fixed_eof_mmaps(void)
+{
+ char *addr;
+ char *cp;
+ unsigned int *p1;
+ uintptr_t p;
+ int i;
+
+ /* Find a suitable address to start with. */
+ addr = mmap(NULL, pagesize * 44, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+
+ fprintf (stderr, "%s addr=%p", __func__, (void *)addr);
+ fail_unless (addr != MAP_FAILED);
+
+ for (i = 0; i < 0x10; i++)
+ {
+ /* Create submaps within our unfixed map. */
+ p1 = mmap(addr, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_FIXED,
+ test_fd,
+ (test_fsize - sizeof *p1) & ~pagemask);
+
+ fail_unless (p1 != MAP_FAILED);
+
+ /* Make sure we get pages aligned with the pagesize. The
+ target expects this. */
+ p = (uintptr_t) p1;
+ fail_unless ((p & pagemask) == 0);
+
+ /* Verify that the file maps was made correctly. */
+ fail_unless (p1[(test_fsize & pagemask) / sizeof *p1 - 1]
+ == ((test_fsize - sizeof *p1) / sizeof *p1));
+
+ /* Verify that the end of page is accessable and zeroed. */
+ cp = (void *)p1;
+ fail_unless (cp[pagesize - 4] == 0);
+ munmap (p1, pagesize);
+ addr += pagesize;
+ }
+ fprintf (stderr, " passed\n");
+}
+
+void check_file_fixed_mmaps(void)
+{
+ unsigned char *addr;
+ unsigned int *p1, *p2, *p3, *p4;
+ int i;
+
+ /* Find a suitable address to start with. */
+ addr = mmap(NULL, pagesize * 40 * 4, PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, 0);
+ fprintf (stderr, "%s addr=%p", __func__, (void *)addr);
+ fail_unless (addr != MAP_FAILED);
+
+ for (i = 0; i < 40; i++)
+ {
+ p1 = mmap(addr, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_FIXED,
+ test_fd, 0);
+ p2 = mmap(addr + pagesize, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_FIXED,
+ test_fd, pagesize);
+ p3 = mmap(addr + pagesize * 2, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_FIXED,
+ test_fd, pagesize * 2);
+ p4 = mmap(addr + pagesize * 3, pagesize, PROT_READ,
+ MAP_PRIVATE | MAP_FIXED,
+ test_fd, pagesize * 3);
+
+ /* Make sure we get pages aligned with the pagesize.
+ The target expects this. */
+ fail_unless (p1 == (void *)addr);
+ fail_unless (p2 == (void *)addr + pagesize);
+ fail_unless (p3 == (void *)addr + pagesize * 2);
+ fail_unless (p4 == (void *)addr + pagesize * 3);
+
+ /* Verify that the file maps was made correctly. */
+ fail_unless (*p1 == 0);
+ fail_unless (*p2 == (pagesize / sizeof *p2));
+ fail_unless (*p3 == ((pagesize * 2) / sizeof *p3));
+ fail_unless (*p4 == ((pagesize * 3) / sizeof *p4));
+
+ memcpy (dummybuf, p1, pagesize);
+ memcpy (dummybuf, p2, pagesize);
+ memcpy (dummybuf, p3, pagesize);
+ memcpy (dummybuf, p4, pagesize);
+
+ munmap (p1, pagesize);
+ munmap (p2, pagesize);
+ munmap (p3, pagesize);
+ munmap (p4, pagesize);
+ addr += pagesize * 4;
+ }
+ fprintf (stderr, " passed\n");
+}
+
+int main(int argc, char **argv)
+{
+ char tempname[] = "/tmp/.cmmapXXXXXX";
+ unsigned int i;
+
+ /* Trust the first argument, otherwise probe the system for our
+ pagesize. */
+ if (argc > 1)
+ pagesize = strtoul(argv[1], NULL, 0);
+ else
+ pagesize = sysconf(_SC_PAGESIZE);
+
+ /* Assume pagesize is a power of two. */
+ pagemask = pagesize - 1;
+ dummybuf = malloc (pagesize);
+ printf ("pagesize=%u pagemask=%x\n", pagesize, pagemask);
+
+ test_fd = mkstemp(tempname);
+ unlink(tempname);
+
+ /* Fill the file with int's counting from zero and up. */
+ for (i = 0; i < (pagesize * 4) / sizeof i; i++)
+ write (test_fd, &i, sizeof i);
+ /* Append a few extra writes to make the file end at non
+ page boundary. */
+ write (test_fd, &i, sizeof i); i++;
+ write (test_fd, &i, sizeof i); i++;
+ write (test_fd, &i, sizeof i); i++;
+
+ test_fsize = lseek(test_fd, 0, SEEK_CUR);
+
+ /* Run the tests. */
+ check_aligned_anonymous_unfixed_mmaps();
+ check_aligned_anonymous_unfixed_colliding_mmaps();
+ check_aligned_anonymous_fixed_mmaps();
+ check_file_unfixed_mmaps();
+ check_file_fixed_mmaps();
+ check_file_fixed_eof_mmaps();
+ check_file_unfixed_eof_mmaps();
+
+ /* Fails at the moment. */
+ /* check_aligned_anonymous_fixed_mmaps_collide_with_host(); */
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_path.c b/tests/test_path.c
new file mode 100644
index 0000000..234ed97
--- /dev/null
+++ b/tests/test_path.c
@@ -0,0 +1,160 @@
+/* Test path override code */
+#include "../config-host.h"
+#include "../qemu-malloc.c"
+#include "../cutils.c"
+#include "../path.c"
+#include "../trace.c"
+#ifdef CONFIG_SIMPLE_TRACE
+#include "../simpletrace.c"
+#endif
+
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+void qemu_log(const char *fmt, ...);
+
+/* Any log message kills the test. */
+void qemu_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "FATAL: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+#define NO_CHANGE(_path) \
+ do { \
+ if (strcmp(path(_path), _path) != 0) return __LINE__; \
+ } while(0)
+
+#define CHANGE_TO(_path, _newpath) \
+ do { \
+ if (strcmp(path(_path), _newpath) != 0) return __LINE__; \
+ } while(0)
+
+static void cleanup(void)
+{
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE2");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE3");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE4");
+ unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE5");
+ rmdir("/tmp/qemu-test_path/DIR1/DIR2");
+ rmdir("/tmp/qemu-test_path/DIR1/DIR3");
+ rmdir("/tmp/qemu-test_path/DIR1");
+ rmdir("/tmp/qemu-test_path");
+}
+
+static unsigned int do_test(void)
+{
+ if (mkdir("/tmp/qemu-test_path", 0700) != 0)
+ return __LINE__;
+
+ if (mkdir("/tmp/qemu-test_path/DIR1", 0700) != 0)
+ return __LINE__;
+
+ if (mkdir("/tmp/qemu-test_path/DIR1/DIR2", 0700) != 0)
+ return __LINE__;
+
+ if (mkdir("/tmp/qemu-test_path/DIR1/DIR3", 0700) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE2", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE3", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE4", 0600)) != 0)
+ return __LINE__;
+
+ if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE5", 0600)) != 0)
+ return __LINE__;
+
+ init_paths("/tmp/qemu-test_path");
+
+ NO_CHANGE("/tmp");
+ NO_CHANGE("/tmp/");
+ NO_CHANGE("/tmp/qemu-test_path");
+ NO_CHANGE("/tmp/qemu-test_path/");
+ NO_CHANGE("/tmp/qemu-test_path/D");
+ NO_CHANGE("/tmp/qemu-test_path/DI");
+ NO_CHANGE("/tmp/qemu-test_path/DIR");
+ NO_CHANGE("/tmp/qemu-test_path/DIR1");
+ NO_CHANGE("/tmp/qemu-test_path/DIR1/");
+
+ NO_CHANGE("/D");
+ NO_CHANGE("/DI");
+ NO_CHANGE("/DIR");
+ NO_CHANGE("/DIR2");
+ NO_CHANGE("/DIR1.");
+
+ CHANGE_TO("/DIR1", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/DIR1/", "/tmp/qemu-test_path/DIR1");
+
+ NO_CHANGE("/DIR1/D");
+ NO_CHANGE("/DIR1/DI");
+ NO_CHANGE("/DIR1/DIR");
+ NO_CHANGE("/DIR1/DIR1");
+
+ CHANGE_TO("/DIR1/DIR2", "/tmp/qemu-test_path/DIR1/DIR2");
+ CHANGE_TO("/DIR1/DIR2/", "/tmp/qemu-test_path/DIR1/DIR2");
+
+ CHANGE_TO("/DIR1/DIR3", "/tmp/qemu-test_path/DIR1/DIR3");
+ CHANGE_TO("/DIR1/DIR3/", "/tmp/qemu-test_path/DIR1/DIR3");
+
+ NO_CHANGE("/DIR1/DIR2/F");
+ NO_CHANGE("/DIR1/DIR2/FI");
+ NO_CHANGE("/DIR1/DIR2/FIL");
+ NO_CHANGE("/DIR1/DIR2/FIL.");
+
+ CHANGE_TO("/DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/FILE2", "/tmp/qemu-test_path/DIR1/DIR2/FILE2");
+ CHANGE_TO("/DIR1/DIR2/FILE3", "/tmp/qemu-test_path/DIR1/DIR2/FILE3");
+ CHANGE_TO("/DIR1/DIR2/FILE4", "/tmp/qemu-test_path/DIR1/DIR2/FILE4");
+ CHANGE_TO("/DIR1/DIR2/FILE5", "/tmp/qemu-test_path/DIR1/DIR2/FILE5");
+
+ NO_CHANGE("/DIR1/DIR2/FILE6");
+ NO_CHANGE("/DIR1/DIR2/FILE/X");
+
+ CHANGE_TO("/DIR1/../DIR1", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/DIR1/../DIR1/", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/../DIR1", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/../DIR1/", "/tmp/qemu-test_path/DIR1");
+ CHANGE_TO("/DIR1/DIR2/../DIR2", "/tmp/qemu-test_path/DIR1/DIR2");
+ CHANGE_TO("/DIR1/DIR2/../DIR2/../../DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/../DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+
+ NO_CHANGE("/DIR1/DIR2/../DIR1");
+ NO_CHANGE("/DIR1/DIR2/../FILE");
+
+ CHANGE_TO("/./DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/././DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/./DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/././DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/DIR1/DIR2/././FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+ CHANGE_TO("/./DIR1/./DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ret = do_test();
+ cleanup();
+ if (ret) {
+ fprintf(stderr, "test_path: failed on line %i\n", ret);
+ return 1;
+ }
+ return 0;
+}
diff --git a/tests/testthread.c b/tests/testthread.c
new file mode 100644
index 0000000..27e4825
--- /dev/null
+++ b/tests/testthread.c
@@ -0,0 +1,51 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sched.h>
+
+void *thread1_func(void *arg)
+{
+ int i;
+ char buf[512];
+
+ for(i=0;i<10;i++) {
+ snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
+ write(1, buf, strlen(buf));
+ usleep(100 * 1000);
+ }
+ return NULL;
+}
+
+void *thread2_func(void *arg)
+{
+ int i;
+ char buf[512];
+ for(i=0;i<20;i++) {
+ snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
+ write(1, buf, strlen(buf));
+ usleep(150 * 1000);
+ }
+ return NULL;
+}
+
+void test_pthread(void)
+{
+ pthread_t tid1, tid2;
+
+ pthread_create(&tid1, NULL, thread1_func, "hello1");
+ pthread_create(&tid2, NULL, thread2_func, "hello2");
+ pthread_join(tid1, NULL);
+ pthread_join(tid2, NULL);
+ printf("End of pthread test.\n");
+}
+
+int main(int argc, char **argv)
+{
+ test_pthread();
+ return 0;
+}
diff --git a/thunk.c b/thunk.c
new file mode 100644
index 0000000..0657188
--- /dev/null
+++ b/thunk.c
@@ -0,0 +1,288 @@
+/*
+ * Generic thunking code to convert data between host and target CPU
+ *
+ * Copyright (c) 2003 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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "qemu.h"
+#include "thunk.h"
+
+//#define DEBUG
+
+#define MAX_STRUCTS 128
+
+/* XXX: make it dynamic */
+StructEntry struct_entries[MAX_STRUCTS];
+
+static const argtype *thunk_type_next_ptr(const argtype *type_ptr);
+
+static inline const argtype *thunk_type_next(const argtype *type_ptr)
+{
+ int type;
+
+ type = *type_ptr++;
+ switch(type) {
+ case TYPE_CHAR:
+ case TYPE_SHORT:
+ case TYPE_INT:
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ return type_ptr;
+ case TYPE_PTR:
+ return thunk_type_next_ptr(type_ptr);
+ case TYPE_ARRAY:
+ return thunk_type_next_ptr(type_ptr + 1);
+ case TYPE_STRUCT:
+ return type_ptr + 1;
+ default:
+ return NULL;
+ }
+}
+
+static const argtype *thunk_type_next_ptr(const argtype *type_ptr)
+{
+ return thunk_type_next(type_ptr);
+}
+
+void thunk_register_struct(int id, const char *name, const argtype *types)
+{
+ const argtype *type_ptr;
+ StructEntry *se;
+ int nb_fields, offset, max_align, align, size, i, j;
+
+ se = struct_entries + id;
+
+ /* first we count the number of fields */
+ type_ptr = types;
+ nb_fields = 0;
+ while (*type_ptr != TYPE_NULL) {
+ type_ptr = thunk_type_next(type_ptr);
+ nb_fields++;
+ }
+ se->field_types = types;
+ se->nb_fields = nb_fields;
+ se->name = name;
+#ifdef DEBUG
+ printf("struct %s: id=%d nb_fields=%d\n",
+ se->name, id, se->nb_fields);
+#endif
+ /* now we can alloc the data */
+
+ for(i = 0;i < 2; i++) {
+ offset = 0;
+ max_align = 1;
+ se->field_offsets[i] = malloc(nb_fields * sizeof(int));
+ type_ptr = se->field_types;
+ for(j = 0;j < nb_fields; j++) {
+ size = thunk_type_size(type_ptr, i);
+ align = thunk_type_align(type_ptr, i);
+ offset = (offset + align - 1) & ~(align - 1);
+ se->field_offsets[i][j] = offset;
+ offset += size;
+ if (align > max_align)
+ max_align = align;
+ type_ptr = thunk_type_next(type_ptr);
+ }
+ offset = (offset + max_align - 1) & ~(max_align - 1);
+ se->size[i] = offset;
+ se->align[i] = max_align;
+#ifdef DEBUG
+ printf("%s: size=%d align=%d\n",
+ i == THUNK_HOST ? "host" : "target", offset, max_align);
+#endif
+ }
+}
+
+void thunk_register_struct_direct(int id, const char *name,
+ const StructEntry *se1)
+{
+ StructEntry *se;
+ se = struct_entries + id;
+ *se = *se1;
+ se->name = name;
+}
+
+
+/* now we can define the main conversion functions */
+const argtype *thunk_convert(void *dst, const void *src,
+ const argtype *type_ptr, int to_host)
+{
+ int type;
+
+ type = *type_ptr++;
+ switch(type) {
+ case TYPE_CHAR:
+ *(uint8_t *)dst = *(uint8_t *)src;
+ break;
+ case TYPE_SHORT:
+ *(uint16_t *)dst = tswap16(*(uint16_t *)src);
+ break;
+ case TYPE_INT:
+ *(uint32_t *)dst = tswap32(*(uint32_t *)src);
+ break;
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ *(uint64_t *)dst = tswap64(*(uint64_t *)src);
+ break;
+#if HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 32
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ *(uint32_t *)dst = tswap32(*(uint32_t *)src);
+ break;
+#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 32
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ if (to_host) {
+ if (type == TYPE_LONG) {
+ /* sign extension */
+ *(uint64_t *)dst = (int32_t)tswap32(*(uint32_t *)src);
+ } else {
+ *(uint64_t *)dst = tswap32(*(uint32_t *)src);
+ }
+ } else {
+ *(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff);
+ }
+ break;
+#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ *(uint64_t *)dst = tswap64(*(uint64_t *)src);
+ break;
+#elif HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 64
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ if (to_host) {
+ *(uint32_t *)dst = tswap64(*(uint64_t *)src);
+ } else {
+ if (type == TYPE_LONG) {
+ /* sign extension */
+ *(uint64_t *)dst = tswap64(*(int32_t *)src);
+ } else {
+ *(uint64_t *)dst = tswap64(*(uint32_t *)src);
+ }
+ }
+ break;
+#else
+#warning unsupported conversion
+#endif
+ case TYPE_ARRAY:
+ {
+ int array_length, i, dst_size, src_size;
+ const uint8_t *s;
+ uint8_t *d;
+
+ array_length = *type_ptr++;
+ dst_size = thunk_type_size(type_ptr, to_host);
+ src_size = thunk_type_size(type_ptr, 1 - to_host);
+ d = dst;
+ s = src;
+ for(i = 0;i < array_length; i++) {
+ thunk_convert(d, s, type_ptr, to_host);
+ d += dst_size;
+ s += src_size;
+ }
+ type_ptr = thunk_type_next(type_ptr);
+ }
+ break;
+ case TYPE_STRUCT:
+ {
+ int i;
+ const StructEntry *se;
+ const uint8_t *s;
+ uint8_t *d;
+ const argtype *field_types;
+ const int *dst_offsets, *src_offsets;
+
+ se = struct_entries + *type_ptr++;
+ if (se->convert[0] != NULL) {
+ /* specific conversion is needed */
+ (*se->convert[to_host])(dst, src);
+ } else {
+ /* standard struct conversion */
+ field_types = se->field_types;
+ dst_offsets = se->field_offsets[to_host];
+ src_offsets = se->field_offsets[1 - to_host];
+ d = dst;
+ s = src;
+ for(i = 0;i < se->nb_fields; i++) {
+ field_types = thunk_convert(d + dst_offsets[i],
+ s + src_offsets[i],
+ field_types, to_host);
+ }
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Invalid type 0x%x\n", type);
+ break;
+ }
+ return type_ptr;
+}
+
+/* from em86 */
+
+/* Utility function: Table-driven functions to translate bitmasks
+ * between X86 and Alpha formats...
+ */
+unsigned int target_to_host_bitmask(unsigned int x86_mask,
+ const bitmask_transtbl * trans_tbl)
+{
+ const bitmask_transtbl *btp;
+ unsigned int alpha_mask = 0;
+
+ for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
+ if((x86_mask & btp->x86_mask) == btp->x86_bits) {
+ alpha_mask |= btp->alpha_bits;
+ }
+ }
+ return(alpha_mask);
+}
+
+unsigned int host_to_target_bitmask(unsigned int alpha_mask,
+ const bitmask_transtbl * trans_tbl)
+{
+ const bitmask_transtbl *btp;
+ unsigned int x86_mask = 0;
+
+ for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
+ if((alpha_mask & btp->alpha_mask) == btp->alpha_bits) {
+ x86_mask |= btp->x86_bits;
+ }
+ }
+ return(x86_mask);
+}
+
+#ifndef NO_THUNK_TYPE_SIZE
+int thunk_type_size_array(const argtype *type_ptr, int is_host)
+{
+ return thunk_type_size(type_ptr, is_host);
+}
+
+int thunk_type_align_array(const argtype *type_ptr, int is_host)
+{
+ return thunk_type_align(type_ptr, is_host);
+}
+#endif /* ndef NO_THUNK_TYPE_SIZE */
diff --git a/thunk.h b/thunk.h
new file mode 100644
index 0000000..109c541
--- /dev/null
+++ b/thunk.h
@@ -0,0 +1,161 @@
+/*
+ * Generic thunking code to convert data between host and target CPU
+ *
+ * Copyright (c) 2003 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/>.
+ */
+#ifndef THUNK_H
+#define THUNK_H
+
+#include <inttypes.h>
+#include "cpu.h"
+
+/* types enums definitions */
+
+typedef enum argtype {
+ TYPE_NULL,
+ TYPE_CHAR,
+ TYPE_SHORT,
+ TYPE_INT,
+ TYPE_LONG,
+ TYPE_ULONG,
+ TYPE_PTRVOID, /* pointer on unknown data */
+ TYPE_LONGLONG,
+ TYPE_ULONGLONG,
+ TYPE_PTR,
+ TYPE_ARRAY,
+ TYPE_STRUCT,
+} argtype;
+
+#define MK_PTR(type) TYPE_PTR, type
+#define MK_ARRAY(type, size) TYPE_ARRAY, size, type
+#define MK_STRUCT(id) TYPE_STRUCT, id
+
+#define THUNK_TARGET 0
+#define THUNK_HOST 1
+
+typedef struct {
+ /* standard struct handling */
+ const argtype *field_types;
+ int nb_fields;
+ int *field_offsets[2];
+ /* special handling */
+ void (*convert[2])(void *dst, const void *src);
+ int size[2];
+ int align[2];
+ const char *name;
+} StructEntry;
+
+/* Translation table for bitmasks... */
+typedef struct bitmask_transtbl {
+ unsigned int x86_mask;
+ unsigned int x86_bits;
+ unsigned int alpha_mask;
+ unsigned int alpha_bits;
+} bitmask_transtbl;
+
+void thunk_register_struct(int id, const char *name, const argtype *types);
+void thunk_register_struct_direct(int id, const char *name,
+ const StructEntry *se1);
+const argtype *thunk_convert(void *dst, const void *src,
+ const argtype *type_ptr, int to_host);
+#ifndef NO_THUNK_TYPE_SIZE
+
+extern StructEntry struct_entries[];
+
+int thunk_type_size_array(const argtype *type_ptr, int is_host);
+int thunk_type_align_array(const argtype *type_ptr, int is_host);
+
+static inline int thunk_type_size(const argtype *type_ptr, int is_host)
+{
+ int type, size;
+ const StructEntry *se;
+
+ type = *type_ptr;
+ switch(type) {
+ case TYPE_CHAR:
+ return 1;
+ case TYPE_SHORT:
+ return 2;
+ case TYPE_INT:
+ return 4;
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ return 8;
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ case TYPE_PTR:
+ if (is_host) {
+ return HOST_LONG_SIZE;
+ } else {
+ return TARGET_ABI_BITS / 8;
+ }
+ break;
+ case TYPE_ARRAY:
+ size = type_ptr[1];
+ return size * thunk_type_size_array(type_ptr + 2, is_host);
+ case TYPE_STRUCT:
+ se = struct_entries + type_ptr[1];
+ return se->size[is_host];
+ default:
+ return -1;
+ }
+}
+
+static inline int thunk_type_align(const argtype *type_ptr, int is_host)
+{
+ int type;
+ const StructEntry *se;
+
+ type = *type_ptr;
+ switch(type) {
+ case TYPE_CHAR:
+ return 1;
+ case TYPE_SHORT:
+ return 2;
+ case TYPE_INT:
+ return 4;
+ case TYPE_LONGLONG:
+ case TYPE_ULONGLONG:
+ return 8;
+ case TYPE_LONG:
+ case TYPE_ULONG:
+ case TYPE_PTRVOID:
+ case TYPE_PTR:
+ if (is_host) {
+ return HOST_LONG_SIZE;
+ } else {
+ return TARGET_ABI_BITS / 8;
+ }
+ break;
+ case TYPE_ARRAY:
+ return thunk_type_align_array(type_ptr + 2, is_host);
+ case TYPE_STRUCT:
+ se = struct_entries + type_ptr[1];
+ return se->align[is_host];
+ default:
+ return -1;
+ }
+}
+
+#endif /* NO_THUNK_TYPE_SIZE */
+
+unsigned int target_to_host_bitmask(unsigned int x86_mask,
+ const bitmask_transtbl * trans_tbl);
+unsigned int host_to_target_bitmask(unsigned int alpha_mask,
+ const bitmask_transtbl * trans_tbl);
+
+#endif
diff --git a/trace-events b/trace-events
new file mode 100644
index 0000000..e6138ea
--- /dev/null
+++ b/trace-events
@@ -0,0 +1,256 @@
+# Trace events for debugging and performance instrumentation
+#
+# This file is processed by the tracetool script during the build.
+#
+# To add a new trace event:
+#
+# 1. Choose a name for the trace event. Declare its arguments and format
+# string.
+#
+# 2. Call the trace event from code using trace_##name, e.g. multiwrite_cb() ->
+# trace_multiwrite_cb(). The source file must #include "trace.h".
+#
+# Format of a trace event:
+#
+# [disable] <name>(<type1> <arg1>[, <type2> <arg2>] ...) "<format-string>"
+#
+# Example: qemu_malloc(size_t size) "size %zu"
+#
+# The "disable" keyword will build without the trace event.
+# In case of 'simple' trace backend, it will allow the trace event to be
+# compiled, but this would be turned off by default. It can be toggled on via
+# the monitor.
+#
+# The <name> must be a valid as a C function name.
+#
+# Types should be standard C types. Use void * for pointers because the trace
+# system may not have the necessary headers included.
+#
+# The <format-string> should be a sprintf()-compatible format string.
+
+# qemu-malloc.c
+disable qemu_malloc(size_t size, void *ptr) "size %zu ptr %p"
+disable qemu_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p"
+disable qemu_free(void *ptr) "ptr %p"
+
+# osdep.c
+disable qemu_memalign(size_t alignment, size_t size, void *ptr) "alignment %zu size %zu ptr %p"
+disable qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p"
+disable qemu_vfree(void *ptr) "ptr %p"
+
+# hw/virtio.c
+disable virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u"
+disable virtqueue_flush(void *vq, unsigned int count) "vq %p count %u"
+disable virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u"
+disable virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p"
+disable virtio_irq(void *vq) "vq %p"
+disable virtio_notify(void *vdev, void *vq) "vdev %p vq %p"
+
+# block.c
+disable multiwrite_cb(void *mcb, int ret) "mcb %p ret %d"
+disable bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d"
+disable bdrv_aio_multiwrite_earlyfail(void *mcb) "mcb %p"
+disable bdrv_aio_multiwrite_latefail(void *mcb, int i) "mcb %p i %d"
+disable bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
+disable bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p"
+
+# hw/virtio-blk.c
+disable virtio_blk_req_complete(void *req, int status) "req %p status %d"
+disable virtio_blk_rw_complete(void *req, int ret) "req %p ret %d"
+disable virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu"
+
+# posix-aio-compat.c
+disable paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d"
+
+# ioport.c
+disable cpu_in(unsigned int addr, unsigned int val) "addr %#x value %u"
+disable cpu_out(unsigned int addr, unsigned int val) "addr %#x value %u"
+
+# balloon.c
+# Since requests are raised via monitor, not many tracepoints are needed.
+disable balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu"
+
+# hw/apic.c
+disable apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d"
+disable apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t polarity, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d polarity %d trigger_mode %d"
+disable cpu_set_apic_base(uint64_t val) "%016"PRIx64""
+disable cpu_get_apic_base(uint64_t val) "%016"PRIx64""
+disable apic_mem_readl(uint64_t addr, uint32_t val) "%"PRIx64" = %08x"
+disable apic_mem_writel(uint64_t addr, uint32_t val) "%"PRIx64" = %08x"
+# coalescing
+disable apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d"
+disable apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d"
+disable apic_set_irq(int apic_irq_delivered) "coalescing %d"
+
+# hw/cs4231.c
+disable cs4231_mem_readl_dreg(uint32_t reg, uint32_t ret) "read dreg %d: 0x%02x"
+disable cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x"
+disable cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x"
+disable cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x"
+
+# hw/eccmemctl.c
+disable ecc_mem_writel_mer(uint32_t val) "Write memory enable %08x"
+disable ecc_mem_writel_mdr(uint32_t val) "Write memory delay %08x"
+disable ecc_mem_writel_mfsr(uint32_t val) "Write memory fault status %08x"
+disable ecc_mem_writel_vcr(uint32_t val) "Write slot configuration %08x"
+disable ecc_mem_writel_dr(uint32_t val) "Write diagnostic %08x"
+disable ecc_mem_writel_ecr0(uint32_t val) "Write event count 1 %08x"
+disable ecc_mem_writel_ecr1(uint32_t val) "Write event count 2 %08x"
+disable ecc_mem_readl_mer(uint32_t ret) "Read memory enable %08x"
+disable ecc_mem_readl_mdr(uint32_t ret) "Read memory delay %08x"
+disable ecc_mem_readl_mfsr(uint32_t ret) "Read memory fault status %08x"
+disable ecc_mem_readl_vcr(uint32_t ret) "Read slot configuration %08x"
+disable ecc_mem_readl_mfar0(uint32_t ret) "Read memory fault address 0 %08x"
+disable ecc_mem_readl_mfar1(uint32_t ret) "Read memory fault address 1 %08x"
+disable ecc_mem_readl_dr(uint32_t ret) "Read diagnostic %08x"
+disable ecc_mem_readl_ecr0(uint32_t ret) "Read event count 1 %08x"
+disable ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 %08x"
+disable ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = %02x"
+disable ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x"
+
+# hw/lance.c
+disable lance_mem_readw(uint64_t addr, uint32_t ret) "addr=%"PRIx64"val=0x%04x"
+disable lance_mem_writew(uint64_t addr, uint32_t val) "addr=%"PRIx64"val=0x%04x"
+
+# hw/slavio_intctl.c
+disable slavio_intctl_mem_readl(uint32_t cpu, uint64_t addr, uint32_t ret) "read cpu %d reg 0x%"PRIx64" = %x"
+disable slavio_intctl_mem_writel(uint32_t cpu, uint64_t addr, uint32_t val) "write cpu %d reg 0x%"PRIx64" = %x"
+disable slavio_intctl_mem_writel_clear(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Cleared cpu %d irq mask %x, curmask %x"
+disable slavio_intctl_mem_writel_set(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Set cpu %d irq mask %x, curmask %x"
+disable slavio_intctlm_mem_readl(uint64_t addr, uint32_t ret) "read system reg 0x%"PRIx64" = %x"
+disable slavio_intctlm_mem_writel(uint64_t addr, uint32_t val) "write system reg 0x%"PRIx64" = %x"
+disable slavio_intctlm_mem_writel_enable(uint32_t val, uint32_t intregm_disabled) "Enabled master irq mask %x, curmask %x"
+disable slavio_intctlm_mem_writel_disable(uint32_t val, uint32_t intregm_disabled) "Disabled master irq mask %x, curmask %x"
+disable slavio_intctlm_mem_writel_target(uint32_t cpu) "Set master irq cpu %d"
+disable slavio_check_interrupts(uint32_t pending, uint32_t intregm_disabled) "pending %x disabled %x"
+disable slavio_set_irq(uint32_t target_cpu, int irq, uint32_t pil, int level) "Set cpu %d irq %d -> pil %d level %d"
+disable slavio_set_timer_irq_cpu(int cpu, int level) "Set cpu %d local timer level %d"
+
+# hw/slavio_misc.c
+disable slavio_misc_update_irq_raise(void) "Raise IRQ"
+disable slavio_misc_update_irq_lower(void) "Lower IRQ"
+disable slavio_set_power_fail(int power_failing, uint8_t config) "Power fail: %d, config: %d"
+disable slavio_cfg_mem_writeb(uint32_t val) "Write config %02x"
+disable slavio_cfg_mem_readb(uint32_t ret) "Read config %02x"
+disable slavio_diag_mem_writeb(uint32_t val) "Write diag %02x"
+disable slavio_diag_mem_readb(uint32_t ret) "Read diag %02x"
+disable slavio_mdm_mem_writeb(uint32_t val) "Write modem control %02x"
+disable slavio_mdm_mem_readb(uint32_t ret) "Read modem control %02x"
+disable slavio_aux1_mem_writeb(uint32_t val) "Write aux1 %02x"
+disable slavio_aux1_mem_readb(uint32_t ret) "Read aux1 %02x"
+disable slavio_aux2_mem_writeb(uint32_t val) "Write aux2 %02x"
+disable slavio_aux2_mem_readb(uint32_t ret) "Read aux2 %02x"
+disable apc_mem_writeb(uint32_t val) "Write power management %02x"
+disable apc_mem_readb(uint32_t ret) "Read power management %02x"
+disable slavio_sysctrl_mem_writel(uint32_t val) "Write system control %08x"
+disable slavio_sysctrl_mem_readl(uint32_t ret) "Read system control %08x"
+disable slavio_led_mem_writew(uint32_t val) "Write diagnostic LED %04x"
+disable slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED %04x"
+
+# hw/slavio_timer.c
+disable slavio_timer_get_out(uint64_t limit, uint32_t counthigh, uint32_t count) "limit %"PRIx64" count %x%08x"
+disable slavio_timer_irq(uint32_t counthigh, uint32_t count) "callback: count %x%08x"
+disable slavio_timer_mem_readl_invalid(uint64_t addr) "invalid read address %"PRIx64""
+disable slavio_timer_mem_readl(uint64_t addr, uint32_t ret) "read %"PRIx64" = %08x"
+disable slavio_timer_mem_writel(uint64_t addr, uint32_t val) "write %"PRIx64" = %08x"
+disable slavio_timer_mem_writel_limit(unsigned int timer_index, uint64_t count) "processor %d user timer set to %016"PRIx64""
+disable slavio_timer_mem_writel_counter_invalid(void) "not user timer"
+disable slavio_timer_mem_writel_status_start(unsigned int timer_index) "processor %d user timer started"
+disable slavio_timer_mem_writel_status_stop(unsigned int timer_index) "processor %d user timer stopped"
+disable slavio_timer_mem_writel_mode_user(unsigned int timer_index) "processor %d changed from counter to user timer"
+disable slavio_timer_mem_writel_mode_counter(unsigned int timer_index) "processor %d changed from user timer to counter"
+disable slavio_timer_mem_writel_mode_invalid(void) "not system timer"
+disable slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address %"PRIx64""
+
+# hw/sparc32_dma.c
+disable ledma_memory_read(uint64_t addr) "DMA read addr 0x%"PRIx64""
+disable ledma_memory_write(uint64_t addr) "DMA write addr 0x%"PRIx64""
+disable sparc32_dma_set_irq_raise(void) "Raise IRQ"
+disable sparc32_dma_set_irq_lower(void) "Lower IRQ"
+disable espdma_memory_read(uint32_t addr) "DMA read addr 0x%08x"
+disable espdma_memory_write(uint32_t addr) "DMA write addr 0x%08x"
+disable sparc32_dma_mem_readl(uint64_t addr, uint32_t ret) "read dmareg %"PRIx64": 0x%08x"
+disable sparc32_dma_mem_writel(uint64_t addr, uint32_t old, uint32_t val) "write dmareg %"PRIx64": 0x%08x -> 0x%08x"
+disable sparc32_dma_enable_raise(void) "Raise DMA enable"
+disable sparc32_dma_enable_lower(void) "Lower DMA enable"
+
+# hw/sun4m.c
+disable sun4m_cpu_interrupt(unsigned int level) "Set CPU IRQ %d"
+disable sun4m_cpu_reset_interrupt(unsigned int level) "Reset CPU IRQ %d"
+disable sun4m_cpu_set_irq_raise(int level) "Raise CPU IRQ %d"
+disable sun4m_cpu_set_irq_lower(int level) "Lower CPU IRQ %d"
+
+# hw/sun4m_iommu.c
+disable sun4m_iommu_mem_readl(uint64_t addr, uint32_t ret) "read reg[%"PRIx64"] = %x"
+disable sun4m_iommu_mem_writel(uint64_t addr, uint32_t val) "write reg[%"PRIx64"] = %x"
+disable sun4m_iommu_mem_writel_ctrl(uint64_t iostart) "iostart = %"PRIx64""
+disable sun4m_iommu_mem_writel_tlbflush(uint32_t val) "tlb flush %x"
+disable sun4m_iommu_mem_writel_pgflush(uint32_t val) "page flush %x"
+disable sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags addr %"PRIx64" => pte %"PRIx64", *pte = %x"
+disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
+disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
+
+# hw/usb-desc.c
+disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
+disable usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"
+disable usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+disable usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+disable usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
+disable usb_set_addr(int addr) "dev %d"
+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"
+
+# vl.c
+disable vm_state_notify(int running, int reason) "running %d reason %d"
+
+# block/qed-l2-cache.c
+disable qed_alloc_l2_cache_entry(void *l2_cache, void *entry) "l2_cache %p entry %p"
+disable qed_unref_l2_cache_entry(void *entry, int ref) "entry %p ref %d"
+disable qed_find_l2_cache_entry(void *l2_cache, void *entry, uint64_t offset, int ref) "l2_cache %p entry %p offset %"PRIu64" ref %d"
+
+# block/qed-table.c
+disable qed_read_table(void *s, uint64_t offset, void *table) "s %p offset %"PRIu64" table %p"
+disable qed_read_table_cb(void *s, void *table, int ret) "s %p table %p ret %d"
+disable qed_write_table(void *s, uint64_t offset, void *table, unsigned int index, unsigned int n) "s %p offset %"PRIu64" table %p index %u n %u"
+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_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""
+disable qed_aio_read_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
+disable qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
+disable qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64""
+disable qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64""
+disable qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
+
+# hw/grlib_gptimer.c
+disable grlib_gptimer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run"
+disable grlib_gptimer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x"
+disable grlib_gptimer_restart(int id, uint32_t reload) "timer:%d reload val: 0x%x"
+disable grlib_gptimer_set_scaler(uint32_t scaler, uint32_t freq) "scaler:0x%x freq: 0x%x"
+disable grlib_gptimer_hit(int id) "timer:%d HIT"
+disable grlib_gptimer_readl(int id, const char *s, uint32_t val) "timer:%d %s 0x%x"
+disable grlib_gptimer_writel(int id, const char *s, uint32_t val) "timer:%d %s 0x%x"
+disable grlib_gptimer_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64""
+
+# hw/grlib_irqmp.c
+disable grlib_irqmp_check_irqs(uint32_t pend, uint32_t force, uint32_t mask, uint32_t lvl1, uint32_t lvl2) "pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n"
+disable grlib_irqmp_ack(int intno) "interrupt:%d"
+disable grlib_irqmp_set_irq(int irq) "Raise CPU IRQ %d"
+disable grlib_irqmp_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64""
+
+# hw/grlib_apbuart.c
+disable grlib_apbuart_event(int event) "event:%d"
+disable grlib_apbuart_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64""
+
+# hw/leon3.c
+disable leon3_set_irq(int intno) "Set CPU IRQ %d"
+disable leon3_reset_irq(int intno) "Reset CPU IRQ %d"
+
+# spice-qemu-char.c
+disable spice_vmc_write(ssize_t out, int len) "spice wrottn %lu of requested %zd"
+disable spice_vmc_read(int bytes, int len) "spice read %lu of requested %zd"
+disable spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
+disable spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"
diff --git a/translate-all.c b/translate-all.c
new file mode 100644
index 0000000..efcfb9a
--- /dev/null
+++ b/translate-all.c
@@ -0,0 +1,167 @@
+/*
+ * Host code generation
+ *
+ * Copyright (c) 2003 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 <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+
+#define NO_CPU_IO_DEFS
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg.h"
+#include "qemu-timer.h"
+
+/* code generation context */
+TCGContext tcg_ctx;
+
+uint16_t gen_opc_buf[OPC_BUF_SIZE];
+TCGArg gen_opparam_buf[OPPARAM_BUF_SIZE];
+
+target_ulong gen_opc_pc[OPC_BUF_SIZE];
+uint16_t gen_opc_icount[OPC_BUF_SIZE];
+uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+
+void cpu_gen_init(void)
+{
+ tcg_context_init(&tcg_ctx);
+ tcg_set_frame(&tcg_ctx, TCG_AREG0, offsetof(CPUState, temp_buf),
+ CPU_TEMP_BUF_NLONGS * sizeof(long));
+}
+
+/* return non zero if the very first instruction is invalid so that
+ the virtual CPU can trigger an exception.
+
+ '*gen_code_size_ptr' contains the size of the generated code (host
+ code).
+*/
+int cpu_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+ TCGContext *s = &tcg_ctx;
+ uint8_t *gen_code_buf;
+ int gen_code_size;
+#ifdef CONFIG_PROFILER
+ int64_t ti;
+#endif
+
+#ifdef CONFIG_PROFILER
+ s->tb_count1++; /* includes aborted translations because of
+ exceptions */
+ ti = profile_getclock();
+#endif
+ tcg_func_start(s);
+
+ gen_intermediate_code(env, tb);
+
+ /* generate machine code */
+ gen_code_buf = tb->tc_ptr;
+ tb->tb_next_offset[0] = 0xffff;
+ tb->tb_next_offset[1] = 0xffff;
+ s->tb_next_offset = tb->tb_next_offset;
+#ifdef USE_DIRECT_JUMP
+ s->tb_jmp_offset = tb->tb_jmp_offset;
+ s->tb_next = NULL;
+#else
+ s->tb_jmp_offset = NULL;
+ s->tb_next = tb->tb_next;
+#endif
+
+#ifdef CONFIG_PROFILER
+ s->tb_count++;
+ s->interm_time += profile_getclock() - ti;
+ s->code_time -= profile_getclock();
+#endif
+ gen_code_size = tcg_gen_code(s, gen_code_buf);
+ *gen_code_size_ptr = gen_code_size;
+#ifdef CONFIG_PROFILER
+ s->code_time += profile_getclock();
+ s->code_in_len += tb->size;
+ s->code_out_len += gen_code_size;
+#endif
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM)) {
+ qemu_log("OUT: [size=%d]\n", *gen_code_size_ptr);
+ log_disas(tb->tc_ptr, *gen_code_size_ptr);
+ qemu_log("\n");
+ qemu_log_flush();
+ }
+#endif
+ return 0;
+}
+
+/* The cpu state corresponding to 'searched_pc' is restored.
+ */
+int cpu_restore_state(TranslationBlock *tb,
+ CPUState *env, unsigned long searched_pc,
+ void *puc)
+{
+ TCGContext *s = &tcg_ctx;
+ int j;
+ unsigned long tc_ptr;
+#ifdef CONFIG_PROFILER
+ int64_t ti;
+#endif
+
+#ifdef CONFIG_PROFILER
+ ti = profile_getclock();
+#endif
+ tcg_func_start(s);
+
+ gen_intermediate_code_pc(env, tb);
+
+ if (use_icount) {
+ /* Reset the cycle counter to the start of the block. */
+ env->icount_decr.u16.low += tb->icount;
+ /* Clear the IO flag. */
+ env->can_do_io = 0;
+ }
+
+ /* find opc index corresponding to search_pc */
+ tc_ptr = (unsigned long)tb->tc_ptr;
+ if (searched_pc < tc_ptr)
+ return -1;
+
+ s->tb_next_offset = tb->tb_next_offset;
+#ifdef USE_DIRECT_JUMP
+ s->tb_jmp_offset = tb->tb_jmp_offset;
+ s->tb_next = NULL;
+#else
+ s->tb_jmp_offset = NULL;
+ s->tb_next = tb->tb_next;
+#endif
+ j = tcg_gen_code_search_pc(s, (uint8_t *)tc_ptr, searched_pc - tc_ptr);
+ if (j < 0)
+ return -1;
+ /* now find start of instruction before */
+ while (gen_opc_instr_start[j] == 0)
+ j--;
+ env->icount_decr.u16.low -= gen_opc_icount[j];
+
+ gen_pc_load(env, tb, searched_pc, j, puc);
+
+#ifdef CONFIG_PROFILER
+ s->restore_time += profile_getclock() - ti;
+ s->restore_count++;
+#endif
+ return 0;
+}
diff --git a/uboot_image.h b/uboot_image.h
new file mode 100644
index 0000000..9fc2760
--- /dev/null
+++ b/uboot_image.h
@@ -0,0 +1,158 @@
+/*
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program 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 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/>.
+ *
+ ********************************************************************
+ * NOTE: This header file defines an interface to U-Boot. Including
+ * this (unmodified) header file in another file is considered normal
+ * use of U-Boot, and does *not* fall under the heading of "derived
+ * work".
+ ********************************************************************
+ */
+
+#ifndef __UBOOT_IMAGE_H__
+#define __UBOOT_IMAGE_H__
+
+/*
+ * Operating System Codes
+ */
+#define IH_OS_INVALID 0 /* Invalid OS */
+#define IH_OS_OPENBSD 1 /* OpenBSD */
+#define IH_OS_NETBSD 2 /* NetBSD */
+#define IH_OS_FREEBSD 3 /* FreeBSD */
+#define IH_OS_4_4BSD 4 /* 4.4BSD */
+#define IH_OS_LINUX 5 /* Linux */
+#define IH_OS_SVR4 6 /* SVR4 */
+#define IH_OS_ESIX 7 /* Esix */
+#define IH_OS_SOLARIS 8 /* Solaris */
+#define IH_OS_IRIX 9 /* Irix */
+#define IH_OS_SCO 10 /* SCO */
+#define IH_OS_DELL 11 /* Dell */
+#define IH_OS_NCR 12 /* NCR */
+#define IH_OS_LYNXOS 13 /* LynxOS */
+#define IH_OS_VXWORKS 14 /* VxWorks */
+#define IH_OS_PSOS 15 /* pSOS */
+#define IH_OS_QNX 16 /* QNX */
+#define IH_OS_U_BOOT 17 /* Firmware */
+#define IH_OS_RTEMS 18 /* RTEMS */
+#define IH_OS_ARTOS 19 /* ARTOS */
+#define IH_OS_UNITY 20 /* Unity OS */
+
+/*
+ * CPU Architecture Codes (supported by Linux)
+ */
+#define IH_CPU_INVALID 0 /* Invalid CPU */
+#define IH_CPU_ALPHA 1 /* Alpha */
+#define IH_CPU_ARM 2 /* ARM */
+#define IH_CPU_I386 3 /* Intel x86 */
+#define IH_CPU_IA64 4 /* IA64 */
+#define IH_CPU_MIPS 5 /* MIPS */
+#define IH_CPU_MIPS64 6 /* MIPS 64 Bit */
+#define IH_CPU_PPC 7 /* PowerPC */
+#define IH_CPU_S390 8 /* IBM S390 */
+#define IH_CPU_SH 9 /* SuperH */
+#define IH_CPU_SPARC 10 /* Sparc */
+#define IH_CPU_SPARC64 11 /* Sparc 64 Bit */
+#define IH_CPU_M68K 12 /* M68K */
+#define IH_CPU_NIOS 13 /* Nios-32 */
+#define IH_CPU_MICROBLAZE 14 /* MicroBlaze */
+#define IH_CPU_NIOS2 15 /* Nios-II */
+#define IH_CPU_BLACKFIN 16 /* Blackfin */
+#define IH_CPU_AVR32 17 /* AVR32 */
+
+/*
+ * Image Types
+ *
+ * "Standalone Programs" are directly runnable in the environment
+ * provided by U-Boot; it is expected that (if they behave
+ * well) you can continue to work in U-Boot after return from
+ * the Standalone Program.
+ * "OS Kernel Images" are usually images of some Embedded OS which
+ * will take over control completely. Usually these programs
+ * will install their own set of exception handlers, device
+ * drivers, set up the MMU, etc. - this means, that you cannot
+ * expect to re-enter U-Boot except by resetting the CPU.
+ * "RAMDisk Images" are more or less just data blocks, and their
+ * parameters (address, size) are passed to an OS kernel that is
+ * being started.
+ * "Multi-File Images" contain several images, typically an OS
+ * (Linux) kernel image and one or more data images like
+ * RAMDisks. This construct is useful for instance when you want
+ * to boot over the network using BOOTP etc., where the boot
+ * server provides just a single image file, but you want to get
+ * for instance an OS kernel and a RAMDisk image.
+ *
+ * "Multi-File Images" start with a list of image sizes, each
+ * image size (in bytes) specified by an "uint32_t" in network
+ * byte order. This list is terminated by an "(uint32_t)0".
+ * Immediately after the terminating 0 follow the images, one by
+ * one, all aligned on "uint32_t" boundaries (size rounded up to
+ * a multiple of 4 bytes - except for the last file).
+ *
+ * "Firmware Images" are binary images containing firmware (like
+ * U-Boot or FPGA images) which usually will be programmed to
+ * flash memory.
+ *
+ * "Script files" are command sequences that will be executed by
+ * U-Boot's command interpreter; this feature is especially
+ * useful when you configure U-Boot to use a real shell (hush)
+ * as command interpreter (=> Shell Scripts).
+ */
+
+#define IH_TYPE_INVALID 0 /* Invalid Image */
+#define IH_TYPE_STANDALONE 1 /* Standalone Program */
+#define IH_TYPE_KERNEL 2 /* OS Kernel Image */
+#define IH_TYPE_RAMDISK 3 /* RAMDisk Image */
+#define IH_TYPE_MULTI 4 /* Multi-File Image */
+#define IH_TYPE_FIRMWARE 5 /* Firmware Image */
+#define IH_TYPE_SCRIPT 6 /* Script file */
+#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */
+#define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */
+
+/*
+ * Compression Types
+ */
+#define IH_COMP_NONE 0 /* No Compression Used */
+#define IH_COMP_GZIP 1 /* gzip Compression Used */
+#define IH_COMP_BZIP2 2 /* bzip2 Compression Used */
+
+#define IH_MAGIC 0x27051956 /* Image Magic Number */
+#define IH_NMLEN 32 /* Image Name Length */
+
+/*
+ * all data in network byte order (aka natural aka bigendian)
+ */
+
+typedef struct uboot_image_header {
+ uint32_t ih_magic; /* Image Header Magic Number */
+ uint32_t ih_hcrc; /* Image Header CRC Checksum */
+ uint32_t ih_time; /* Image Creation Timestamp */
+ uint32_t ih_size; /* Image Data Size */
+ uint32_t ih_load; /* Data Load Address */
+ uint32_t ih_ep; /* Entry Point Address */
+ uint32_t ih_dcrc; /* Image Data CRC Checksum */
+ uint8_t ih_os; /* Operating System */
+ uint8_t ih_arch; /* CPU architecture */
+ uint8_t ih_type; /* Image Type */
+ uint8_t ih_comp; /* Compression Type */
+ uint8_t ih_name[IH_NMLEN]; /* Image Name */
+} uboot_image_header_t;
+
+
+#endif /* __IMAGE_H__ */
diff --git a/ui/cocoa.m b/ui/cocoa.m
new file mode 100644
index 0000000..20f91bc
--- /dev/null
+++ b/ui/cocoa.m
@@ -0,0 +1,1014 @@
+/*
+ * QEMU Cocoa CG display driver
+ *
+ * Copyright (c) 2008 Mike Kronenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+
+#ifndef MAC_OS_X_VERSION_10_4
+#define MAC_OS_X_VERSION_10_4 1040
+#endif
+#ifndef MAC_OS_X_VERSION_10_5
+#define MAC_OS_X_VERSION_10_5 1050
+#endif
+
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); }
+#else
+#define COCOA_DEBUG(...) ((void) 0)
+#endif
+
+#define cgrect(nsrect) (*(CGRect *)&(nsrect))
+#define COCOA_MOUSE_EVENT \
+ if (isTabletEnabled) { \
+ kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \
+ } else if (isMouseGrabed) { \
+ kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
+ } else { \
+ [NSApp sendEvent:event]; \
+ }
+
+typedef struct {
+ int width;
+ int height;
+ int bitsPerComponent;
+ int bitsPerPixel;
+} QEMUScreen;
+
+int qemu_main(int argc, char **argv); // main defined in qemu/vl.c
+NSWindow *normalWindow;
+id cocoaView;
+static DisplayChangeListener *dcl;
+
+int gArgc;
+char **gArgv;
+
+// keymap conversion
+int keymap[] =
+{
+// SdlI macI macH SdlH 104xtH 104xtC sdl
+ 30, // 0 0x00 0x1e A QZ_a
+ 31, // 1 0x01 0x1f S QZ_s
+ 32, // 2 0x02 0x20 D QZ_d
+ 33, // 3 0x03 0x21 F QZ_f
+ 35, // 4 0x04 0x23 H QZ_h
+ 34, // 5 0x05 0x22 G QZ_g
+ 44, // 6 0x06 0x2c Z QZ_z
+ 45, // 7 0x07 0x2d X QZ_x
+ 46, // 8 0x08 0x2e C QZ_c
+ 47, // 9 0x09 0x2f V QZ_v
+ 0, // 10 0x0A Undefined
+ 48, // 11 0x0B 0x30 B QZ_b
+ 16, // 12 0x0C 0x10 Q QZ_q
+ 17, // 13 0x0D 0x11 W QZ_w
+ 18, // 14 0x0E 0x12 E QZ_e
+ 19, // 15 0x0F 0x13 R QZ_r
+ 21, // 16 0x10 0x15 Y QZ_y
+ 20, // 17 0x11 0x14 T QZ_t
+ 2, // 18 0x12 0x02 1 QZ_1
+ 3, // 19 0x13 0x03 2 QZ_2
+ 4, // 20 0x14 0x04 3 QZ_3
+ 5, // 21 0x15 0x05 4 QZ_4
+ 7, // 22 0x16 0x07 6 QZ_6
+ 6, // 23 0x17 0x06 5 QZ_5
+ 13, // 24 0x18 0x0d = QZ_EQUALS
+ 10, // 25 0x19 0x0a 9 QZ_9
+ 8, // 26 0x1A 0x08 7 QZ_7
+ 12, // 27 0x1B 0x0c - QZ_MINUS
+ 9, // 28 0x1C 0x09 8 QZ_8
+ 11, // 29 0x1D 0x0b 0 QZ_0
+ 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET
+ 24, // 31 0x1F 0x18 O QZ_o
+ 22, // 32 0x20 0x16 U QZ_u
+ 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET
+ 23, // 34 0x22 0x17 I QZ_i
+ 25, // 35 0x23 0x19 P QZ_p
+ 28, // 36 0x24 0x1c ENTER QZ_RETURN
+ 38, // 37 0x25 0x26 L QZ_l
+ 36, // 38 0x26 0x24 J QZ_j
+ 40, // 39 0x27 0x28 ' QZ_QUOTE
+ 37, // 40 0x28 0x25 K QZ_k
+ 39, // 41 0x29 0x27 ; QZ_SEMICOLON
+ 43, // 42 0x2A 0x2b \ QZ_BACKSLASH
+ 51, // 43 0x2B 0x33 , QZ_COMMA
+ 53, // 44 0x2C 0x35 / QZ_SLASH
+ 49, // 45 0x2D 0x31 N QZ_n
+ 50, // 46 0x2E 0x32 M QZ_m
+ 52, // 47 0x2F 0x34 . QZ_PERIOD
+ 15, // 48 0x30 0x0f TAB QZ_TAB
+ 57, // 49 0x31 0x39 SPACE QZ_SPACE
+ 41, // 50 0x32 0x29 ` QZ_BACKQUOTE
+ 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
+ 0, // 52 0x34 Undefined
+ 1, // 53 0x35 0x01 ESC QZ_ESCAPE
+ 0, // 54 0x36 QZ_RMETA
+ 0, // 55 0x37 QZ_LMETA
+ 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
+ 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
+ 56, // 58 0x3A 0x38 L ALT QZ_LALT
+ 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL
+ 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT
+ 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT
+ 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL
+ 0, // 63 0x3F Undefined
+ 0, // 64 0x40 Undefined
+ 0, // 65 0x41 Undefined
+ 0, // 66 0x42 Undefined
+ 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY
+ 0, // 68 0x44 Undefined
+ 78, // 69 0x45 0x4e KP + QZ_KP_PLUS
+ 0, // 70 0x46 Undefined
+ 69, // 71 0x47 0x45 NUM QZ_NUMLOCK
+ 0, // 72 0x48 Undefined
+ 0, // 73 0x49 Undefined
+ 0, // 74 0x4A Undefined
+ 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE
+ 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER
+ 0, // 77 0x4D undefined
+ 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS
+ 0, // 79 0x4F Undefined
+ 0, // 80 0x50 Undefined
+ 0, // 81 0x51 QZ_KP_EQUALS
+ 82, // 82 0x52 0x52 KP 0 QZ_KP0
+ 79, // 83 0x53 0x4f KP 1 QZ_KP1
+ 80, // 84 0x54 0x50 KP 2 QZ_KP2
+ 81, // 85 0x55 0x51 KP 3 QZ_KP3
+ 75, // 86 0x56 0x4b KP 4 QZ_KP4
+ 76, // 87 0x57 0x4c KP 5 QZ_KP5
+ 77, // 88 0x58 0x4d KP 6 QZ_KP6
+ 71, // 89 0x59 0x47 KP 7 QZ_KP7
+ 0, // 90 0x5A Undefined
+ 72, // 91 0x5B 0x48 KP 8 QZ_KP8
+ 73, // 92 0x5C 0x49 KP 9 QZ_KP9
+ 0, // 93 0x5D Undefined
+ 0, // 94 0x5E Undefined
+ 0, // 95 0x5F Undefined
+ 63, // 96 0x60 0x3f F5 QZ_F5
+ 64, // 97 0x61 0x40 F6 QZ_F6
+ 65, // 98 0x62 0x41 F7 QZ_F7
+ 61, // 99 0x63 0x3d F3 QZ_F3
+ 66, // 100 0x64 0x42 F8 QZ_F8
+ 67, // 101 0x65 0x43 F9 QZ_F9
+ 0, // 102 0x66 Undefined
+ 87, // 103 0x67 0x57 F11 QZ_F11
+ 0, // 104 0x68 Undefined
+ 183,// 105 0x69 0xb7 QZ_PRINT
+ 0, // 106 0x6A Undefined
+ 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK
+ 0, // 108 0x6C Undefined
+ 68, // 109 0x6D 0x44 F10 QZ_F10
+ 0, // 110 0x6E Undefined
+ 88, // 111 0x6F 0x58 F12 QZ_F12
+ 0, // 112 0x70 Undefined
+ 110,// 113 0x71 0x0 QZ_PAUSE
+ 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT
+ 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME
+ 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP
+ 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE
+ 62, // 118 0x76 0x3e F4 QZ_F4
+ 207,// 119 0x77 0xcf E0,4f END QZ_END
+ 60, // 120 0x78 0x3c F2 QZ_F2
+ 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN
+ 59, // 122 0x7A 0x3b F1 QZ_F1
+ 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT
+ 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT
+ 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN
+ 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
+/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
+
+/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
+/*
+ 219 // 0xdb e0,5b L GUI
+ 220 // 0xdc e0,5c R GUI
+ 221 // 0xdd e0,5d APPS
+ // E0,2A,E0,37 PRNT SCRN
+ // E1,1D,45,E1,9D,C5 PAUSE
+ 83 // 0x53 0x53 KP .
+// ACPI Scan Codes
+ 222 // 0xde E0, 5E Power
+ 223 // 0xdf E0, 5F Sleep
+ 227 // 0xe3 E0, 63 Wake
+// Windows Multimedia Scan Codes
+ 153 // 0x99 E0, 19 Next Track
+ 144 // 0x90 E0, 10 Previous Track
+ 164 // 0xa4 E0, 24 Stop
+ 162 // 0xa2 E0, 22 Play/Pause
+ 160 // 0xa0 E0, 20 Mute
+ 176 // 0xb0 E0, 30 Volume Up
+ 174 // 0xae E0, 2E Volume Down
+ 237 // 0xed E0, 6D Media Select
+ 236 // 0xec E0, 6C E-Mail
+ 161 // 0xa1 E0, 21 Calculator
+ 235 // 0xeb E0, 6B My Computer
+ 229 // 0xe5 E0, 65 WWW Search
+ 178 // 0xb2 E0, 32 WWW Home
+ 234 // 0xea E0, 6A WWW Back
+ 233 // 0xe9 E0, 69 WWW Forward
+ 232 // 0xe8 E0, 68 WWW Stop
+ 231 // 0xe7 E0, 67 WWW Refresh
+ 230 // 0xe6 E0, 66 WWW Favorites
+*/
+};
+
+static int cocoa_keycode_to_qemu(int keycode)
+{
+ if((sizeof(keymap)/sizeof(int)) <= keycode)
+ {
+ printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
+ return 0;
+ }
+ return keymap[keycode];
+}
+
+
+
+/*
+ ------------------------------------------------------
+ QemuCocoaView
+ ------------------------------------------------------
+*/
+@interface QemuCocoaView : NSView
+{
+ QEMUScreen screen;
+ NSWindow *fullScreenWindow;
+ float cx,cy,cw,ch,cdx,cdy;
+ CGDataProviderRef dataProviderRef;
+ int modifiers_state[256];
+ BOOL isMouseGrabed;
+ BOOL isFullscreen;
+ BOOL isAbsoluteEnabled;
+ BOOL isTabletEnabled;
+}
+- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds;
+- (void) grabMouse;
+- (void) ungrabMouse;
+- (void) toggleFullScreen:(id)sender;
+- (void) handleEvent:(NSEvent *)event;
+- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
+- (BOOL) isMouseGrabed;
+- (BOOL) isAbsoluteEnabled;
+- (float) cdx;
+- (float) cdy;
+- (QEMUScreen) gscreen;
+@end
+
+@implementation QemuCocoaView
+- (id)initWithFrame:(NSRect)frameRect
+{
+ COCOA_DEBUG("QemuCocoaView: initWithFrame\n");
+
+ self = [super initWithFrame:frameRect];
+ if (self) {
+
+ screen.bitsPerComponent = 8;
+ screen.bitsPerPixel = 32;
+ screen.width = frameRect.size.width;
+ screen.height = frameRect.size.height;
+
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ COCOA_DEBUG("QemuCocoaView: dealloc\n");
+
+ if (dataProviderRef)
+ CGDataProviderRelease(dataProviderRef);
+
+ [super dealloc];
+}
+
+- (BOOL) isOpaque
+{
+ return YES;
+}
+
+- (void) drawRect:(NSRect) rect
+{
+ COCOA_DEBUG("QemuCocoaView: drawRect\n");
+
+ // get CoreGraphic context
+ CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
+ CGContextSetShouldAntialias (viewContextRef, NO);
+
+ // draw screen bitmap directly to Core Graphics context
+ if (dataProviderRef) {
+ CGImageRef imageRef = CGImageCreate(
+ screen.width, //width
+ screen.height, //height
+ screen.bitsPerComponent, //bitsPerComponent
+ screen.bitsPerPixel, //bitsPerPixel
+ (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow
+#ifdef __LITTLE_ENDIAN__
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4
+ kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
+#else
+ CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc)
+ kCGImageAlphaNoneSkipFirst, //bitmapInfo
+#endif
+ dataProviderRef, //provider
+ NULL, //decode
+ 0, //interpolate
+ kCGRenderingIntentDefault //intent
+ );
+// test if host supports "CGImageCreateWithImageInRect" at compile time
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime
+#endif
+ // compatibility drawing code (draws everything) (OS X < 10.4)
+ CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ } else {
+ // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
+ const NSRect *rectList;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ NSInteger rectCount;
+#else
+ int rectCount;
+#endif
+ int i;
+ CGImageRef clipImageRef;
+ CGRect clipRect;
+
+ [self getRectsBeingDrawn:&rectList count:&rectCount];
+ for (i = 0; i < rectCount; i++) {
+ clipRect.origin.x = rectList[i].origin.x / cdx;
+ clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy;
+ clipRect.size.width = rectList[i].size.width / cdx;
+ clipRect.size.height = rectList[i].size.height / cdy;
+ clipImageRef = CGImageCreateWithImageInRect(
+ imageRef,
+ clipRect
+ );
+ CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
+ CGImageRelease (clipImageRef);
+ }
+ }
+#endif
+ CGImageRelease (imageRef);
+ }
+}
+
+- (void) setContentDimensions
+{
+ COCOA_DEBUG("QemuCocoaView: setContentDimensions\n");
+
+ if (isFullscreen) {
+ cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width;
+ cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height;
+ cw = screen.width * cdx;
+ ch = screen.height * cdy;
+ cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0;
+ cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0;
+ } else {
+ cx = 0;
+ cy = 0;
+ cw = screen.width;
+ ch = screen.height;
+ cdx = 1.0;
+ cdy = 1.0;
+ }
+}
+
+- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds
+{
+ COCOA_DEBUG("QemuCocoaView: resizeContent\n");
+
+ // update screenBuffer
+ if (dataProviderRef)
+ CGDataProviderRelease(dataProviderRef);
+
+ //sync host window color space with guests
+ screen.bitsPerPixel = ds_get_bits_per_pixel(ds);
+ screen.bitsPerComponent = ds_get_bytes_per_pixel(ds) * 2;
+
+ dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), w * 4 * h, NULL);
+
+ // update windows
+ if (isFullscreen) {
+ [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO];
+ } else {
+ if (qemu_name)
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:NO];
+ }
+ screen.width = w;
+ screen.height = h;
+ [normalWindow center];
+ [self setContentDimensions];
+ [self setFrame:NSMakeRect(cx, cy, cw, ch)];
+}
+
+- (void) toggleFullScreen:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
+
+ if (isFullscreen) { // switch from fullscreen to desktop
+ isFullscreen = FALSE;
+ [self ungrabMouse];
+ [self setContentDimensions];
+// test if host supports "exitFullScreenModeWithOptions" at compile time
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime
+ [self exitFullScreenModeWithOptions:nil];
+ } else {
+#endif
+ [fullScreenWindow close];
+ [normalWindow setContentView: self];
+ [normalWindow makeKeyAndOrderFront: self];
+ [NSMenu setMenuBarVisible:YES];
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ }
+#endif
+ } else { // switch from desktop to fullscreen
+ isFullscreen = TRUE;
+ [self grabMouse];
+ [self setContentDimensions];
+// test if host supports "enterFullScreenMode:withOptions" at compile time
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime
+ [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens,
+ [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting,
+ nil]];
+ } else {
+#endif
+ [NSMenu setMenuBarVisible:NO];
+ fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame]
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ [fullScreenWindow setHasShadow:NO];
+ [fullScreenWindow setContentView:self];
+ [fullScreenWindow makeKeyAndOrderFront:self];
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ }
+#endif
+ }
+}
+
+- (void) handleEvent:(NSEvent *)event
+{
+ COCOA_DEBUG("QemuCocoaView: handleEvent\n");
+
+ int buttons = 0;
+ int keycode;
+ NSPoint p = [event locationInWindow];
+
+ switch ([event type]) {
+ case NSFlagsChanged:
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
+ if (keycode) {
+ if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | 0x80);
+ } else if (is_graphic_console()) {
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ if (modifiers_state[keycode] == 0) { // keydown
+ kbd_put_keycode(keycode & 0x7f);
+ modifiers_state[keycode] = 1;
+ } else { // keyup
+ kbd_put_keycode(keycode | 0x80);
+ modifiers_state[keycode] = 0;
+ }
+ }
+ }
+
+ // release Mouse grab when pressing ctrl+alt
+ if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+ [self ungrabMouse];
+ }
+ break;
+ case NSKeyDown:
+
+ // forward command Key Combos
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ [NSApp sendEvent:event];
+ return;
+ }
+
+ // default
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ // handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
+ if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+ switch (keycode) {
+
+ // enable graphic console
+ case 0x02 ... 0x0a: // '1' to '9' keys
+ console_select(keycode - 0x02);
+ break;
+ }
+
+ // handle keys for graphic console
+ } else if (is_graphic_console()) {
+ if (keycode & 0x80) //check bit for e0 in front
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
+
+ // handlekeys for Monitor
+ } else {
+ int keysym = 0;
+ switch([event keyCode]) {
+ case 115:
+ keysym = QEMU_KEY_HOME;
+ break;
+ case 117:
+ keysym = QEMU_KEY_DELETE;
+ break;
+ case 119:
+ keysym = QEMU_KEY_END;
+ break;
+ case 123:
+ keysym = QEMU_KEY_LEFT;
+ break;
+ case 124:
+ keysym = QEMU_KEY_RIGHT;
+ break;
+ case 125:
+ keysym = QEMU_KEY_DOWN;
+ break;
+ case 126:
+ keysym = QEMU_KEY_UP;
+ break;
+ default:
+ {
+ NSString *ks = [event characters];
+ if ([ks length] > 0)
+ keysym = [ks characterAtIndex:0];
+ }
+ }
+ if (keysym)
+ kbd_put_keysym(keysym);
+ }
+ break;
+ case NSKeyUp:
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
+ if (is_graphic_console()) {
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+ }
+ break;
+ case NSMouseMoved:
+ if (isAbsoluteEnabled) {
+ if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) {
+ if (isTabletEnabled) { // if we leave the window, deactivate the tablet
+ [NSCursor unhide];
+ isTabletEnabled = FALSE;
+ }
+ } else {
+ if (!isTabletEnabled) { // if we enter the window, activate the tablet
+ [NSCursor hide];
+ isTabletEnabled = TRUE;
+ }
+ }
+ }
+ COCOA_MOUSE_EVENT
+ break;
+ case NSLeftMouseDown:
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ } else {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ COCOA_MOUSE_EVENT
+ break;
+ case NSRightMouseDown:
+ buttons |= MOUSE_EVENT_RBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSOtherMouseDown:
+ buttons |= MOUSE_EVENT_MBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSLeftMouseDragged:
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ } else {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ COCOA_MOUSE_EVENT
+ break;
+ case NSRightMouseDragged:
+ buttons |= MOUSE_EVENT_RBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSOtherMouseDragged:
+ buttons |= MOUSE_EVENT_MBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSLeftMouseUp:
+ if (isTabletEnabled) {
+ COCOA_MOUSE_EVENT
+ } else if (!isMouseGrabed) {
+ if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
+ [self grabMouse];
+ } else {
+ [NSApp sendEvent:event];
+ }
+ } else {
+ COCOA_MOUSE_EVENT
+ }
+ break;
+ case NSRightMouseUp:
+ COCOA_MOUSE_EVENT
+ break;
+ case NSOtherMouseUp:
+ COCOA_MOUSE_EVENT
+ break;
+ case NSScrollWheel:
+ if (isTabletEnabled || isMouseGrabed) {
+ kbd_mouse_event(0, 0, -[event deltaY], 0);
+ } else {
+ [NSApp sendEvent:event];
+ }
+ break;
+ default:
+ [NSApp sendEvent:event];
+ }
+}
+
+- (void) grabMouse
+{
+ COCOA_DEBUG("QemuCocoaView: grabMouse\n");
+
+ if (!isFullscreen) {
+ if (qemu_name)
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]];
+ else
+ [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"];
+ }
+ [NSCursor hide];
+ CGAssociateMouseAndMouseCursorPosition(FALSE);
+ isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
+}
+
+- (void) ungrabMouse
+{
+ COCOA_DEBUG("QemuCocoaView: ungrabMouse\n");
+
+ if (!isFullscreen) {
+ if (qemu_name)
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
+ else
+ [normalWindow setTitle:@"QEMU"];
+ }
+ [NSCursor unhide];
+ CGAssociateMouseAndMouseCursorPosition(TRUE);
+ isMouseGrabed = FALSE;
+}
+
+- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
+- (BOOL) isMouseGrabed {return isMouseGrabed;}
+- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
+- (float) cdx {return cdx;}
+- (float) cdy {return cdy;}
+- (QEMUScreen) gscreen {return screen;}
+@end
+
+
+
+/*
+ ------------------------------------------------------
+ QemuCocoaAppController
+ ------------------------------------------------------
+*/
+@interface QemuCocoaAppController : NSObject
+{
+}
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+- (void)toggleFullScreen:(id)sender;
+- (void)showQEMUDoc:(id)sender;
+- (void)showQEMUTec:(id)sender;
+@end
+
+@implementation QemuCocoaAppController
+- (id) init
+{
+ COCOA_DEBUG("QemuCocoaAppController: init\n");
+
+ self = [super init];
+ if (self) {
+
+ // create a view and add it to the window
+ cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)];
+ if(!cocoaView) {
+ fprintf(stderr, "(cocoa) can't create a view\n");
+ exit(1);
+ }
+
+ // create a window
+ normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame]
+ styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
+ backing:NSBackingStoreBuffered defer:NO];
+ if(!normalWindow) {
+ fprintf(stderr, "(cocoa) can't create window\n");
+ exit(1);
+ }
+ [normalWindow setAcceptsMouseMovedEvents:YES];
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]];
+ [normalWindow setContentView:cocoaView];
+ [normalWindow useOptimizedDrawing:YES];
+ [normalWindow makeKeyAndOrderFront:self];
+ [normalWindow center];
+
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
+
+ if (cocoaView)
+ [cocoaView release];
+ [super dealloc];
+}
+
+- (void)applicationDidFinishLaunching: (NSNotification *) note
+{
+ COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
+
+ // Display an open dialog box if no argument were passed or
+ // if qemu was launched from the finder ( the Finder passes "-psn" )
+ if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
+ NSOpenPanel *op = [[NSOpenPanel alloc] init];
+ [op setPrompt:@"Boot image"];
+ [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
+ [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
+ modalForWindow:normalWindow modalDelegate:self
+ didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
+ } else {
+ // or Launch Qemu, with the global args
+ [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
+ }
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification
+{
+ COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
+
+ qemu_system_shutdown_request();
+ exit(0);
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
+{
+ return YES;
+}
+
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+{
+ COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
+
+ int status;
+ status = qemu_main(argc, argv);
+ exit(status);
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n");
+
+ if(returnCode == NSCancelButton) {
+ exit(0);
+ } else if(returnCode == NSOKButton) {
+ const char *bin = "qemu";
+ char *img = (char*)[ [ sheet filename ] cStringUsingEncoding:NSASCIIStringEncoding];
+
+ char **argv = (char**)malloc( sizeof(char*)*3 );
+
+ asprintf(&argv[0], "%s", bin);
+ asprintf(&argv[1], "-hda");
+ asprintf(&argv[2], "%s", img);
+
+ printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
+
+ [self startEmulationWithArgc:3 argv:(char**)argv];
+ }
+}
+- (void)toggleFullScreen:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n");
+
+ [cocoaView toggleFullScreen:sender];
+}
+
+- (void)showQEMUDoc:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n");
+
+ [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html",
+ [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
+}
+
+- (void)showQEMUTec:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n");
+
+ [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html",
+ [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
+}
+@end
+
+
+
+// Dock Connection
+typedef struct CPSProcessSerNum
+{
+ UInt32 lo;
+ UInt32 hi;
+} CPSProcessSerNum;
+
+OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
+OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+int main (int argc, const char * argv[]) {
+
+ gArgc = argc;
+ gArgv = (char **)argv;
+ CPSProcessSerNum PSN;
+ int i;
+
+ /* In case we don't need to display a window, let's not do that */
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-vnc") ||
+ !strcmp(argv[i], "-nographic") ||
+ !strcmp(argv[i], "-curses")) {
+ return qemu_main(gArgc, gArgv);
+ }
+ }
+
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ [NSApplication sharedApplication];
+
+ if (!CPSGetCurrentProcess(&PSN))
+ if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+ if (!CPSSetFrontProcess(&PSN))
+ [NSApplication sharedApplication];
+
+ // Add menus
+ NSMenu *menu;
+ NSMenuItem *menuItem;
+
+ [NSApp setMainMenu:[[NSMenu alloc] init]];
+
+ // Application menu
+ menu = [[NSMenu alloc] initWithTitle:@""];
+ [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU
+ [menu addItem:[NSMenuItem separatorItem]]; //Separator
+ [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
+ menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+ [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All
+ [menu addItem:[NSMenuItem separatorItem]]; //Separator
+ [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"];
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+ [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+)
+
+ // View menu
+ menu = [[NSMenu alloc] initWithTitle:@"View"];
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ // Window menu
+ menu = [[NSMenu alloc] initWithTitle:@"Window"];
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+ [NSApp setWindowsMenu:menu];
+
+ // Help menu
+ menu = [[NSMenu alloc] initWithTitle:@"Help"];
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ // Create an Application controller
+ QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
+ [NSApp setDelegate:appController];
+
+ // Start the main event loop
+ [NSApp run];
+
+ [appController release];
+ [pool release];
+
+ return 0;
+}
+
+
+
+#pragma mark qemu
+static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
+
+ NSRect rect;
+ if ([cocoaView cdx] == 1.0) {
+ rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h);
+ } else {
+ rect = NSMakeRect(
+ x * [cocoaView cdx],
+ ([cocoaView gscreen].height - y - h) * [cocoaView cdy],
+ w * [cocoaView cdx],
+ h * [cocoaView cdy]);
+ }
+ [cocoaView setNeedsDisplayInRect:rect];
+}
+
+static void cocoa_resize(DisplayState *ds)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_resize\n");
+
+ [cocoaView resizeContentToWidth:(int)(ds_get_width(ds)) height:(int)(ds_get_height(ds)) displayState:ds];
+}
+
+static void cocoa_refresh(DisplayState *ds)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
+
+ if (kbd_mouse_is_absolute()) {
+ if (![cocoaView isAbsoluteEnabled]) {
+ if ([cocoaView isMouseGrabed]) {
+ [cocoaView ungrabMouse];
+ }
+ }
+ [cocoaView setAbsoluteEnabled:YES];
+ }
+
+ NSDate *distantPast;
+ NSEvent *event;
+ distantPast = [NSDate distantPast];
+ do {
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
+ inMode: NSDefaultRunLoopMode dequeue:YES];
+ if (event != nil) {
+ [cocoaView handleEvent:event];
+ }
+ } while(event != nil);
+ vga_hw_update();
+}
+
+static void cocoa_cleanup(void)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n");
+ qemu_free(dcl);
+}
+
+void cocoa_display_init(DisplayState *ds, int full_screen)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+
+ // register vga output callbacks
+ dcl->dpy_update = cocoa_update;
+ dcl->dpy_resize = cocoa_resize;
+ dcl->dpy_refresh = cocoa_refresh;
+
+ register_displaychangelistener(ds, dcl);
+
+ // register cleanup function
+ atexit(cocoa_cleanup);
+}
diff --git a/ui/curses.c b/ui/curses.c
new file mode 100644
index 0000000..82bc614
--- /dev/null
+++ b/ui/curses.c
@@ -0,0 +1,368 @@
+/*
+ * QEMU curses/ncurses display driver
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <curses.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#endif
+
+#ifdef __OpenBSD__
+#define resize_term resizeterm
+#endif
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+static console_ch_t screen[160 * 100];
+static WINDOW *screenpad = NULL;
+static int width, height, gwidth, gheight, invalidate;
+static int px, py, sminx, sminy, smaxx, smaxy;
+
+static void curses_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ chtype *line;
+
+ line = ((chtype *) screen) + y * width;
+ for (h += y; y < h; y ++, line += width)
+ mvwaddchnstr(screenpad, y, 0, line, width);
+
+ pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
+ refresh();
+}
+
+static void curses_calc_pad(void)
+{
+ if (is_fixedsize_console()) {
+ width = gwidth;
+ height = gheight;
+ } else {
+ width = COLS;
+ height = LINES;
+ }
+
+ if (screenpad)
+ delwin(screenpad);
+
+ clear();
+ refresh();
+
+ screenpad = newpad(height, width);
+
+ if (width > COLS) {
+ px = (width - COLS) / 2;
+ sminx = 0;
+ smaxx = COLS;
+ } else {
+ px = 0;
+ sminx = (COLS - width) / 2;
+ smaxx = sminx + width;
+ }
+
+ if (height > LINES) {
+ py = (height - LINES) / 2;
+ sminy = 0;
+ smaxy = LINES;
+ } else {
+ py = 0;
+ sminy = (LINES - height) / 2;
+ smaxy = sminy + height;
+ }
+}
+
+static void curses_resize(DisplayState *ds)
+{
+ if (ds_get_width(ds) == gwidth && ds_get_height(ds) == gheight)
+ return;
+
+ gwidth = ds_get_width(ds);
+ gheight = ds_get_height(ds);
+
+ curses_calc_pad();
+ ds->surface->width = width * FONT_WIDTH;
+ ds->surface->height = height * FONT_HEIGHT;
+}
+
+#ifndef _WIN32
+#if defined(SIGWINCH) && defined(KEY_RESIZE)
+static void curses_winch_handler(int signum)
+{
+ struct winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel; /* unused */
+ unsigned short ws_ypixel; /* unused */
+ } ws;
+
+ /* terminal size changed */
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1)
+ return;
+
+ resize_term(ws.ws_row, ws.ws_col);
+ curses_calc_pad();
+ invalidate = 1;
+
+ /* some systems require this */
+ signal(SIGWINCH, curses_winch_handler);
+}
+#endif
+#endif
+
+static void curses_cursor_position(DisplayState *ds, int x, int y)
+{
+ if (x >= 0) {
+ x = sminx + x - px;
+ y = sminy + y - py;
+
+ if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
+ move(y, x);
+ curs_set(1);
+ /* it seems that curs_set(1) must always be called before
+ * curs_set(2) for the latter to have effect */
+ if (!is_graphic_console())
+ curs_set(2);
+ return;
+ }
+ }
+
+ curs_set(0);
+}
+
+/* generic keyboard conversion */
+
+#include "curses_keys.h"
+
+static kbd_layout_t *kbd_layout = NULL;
+
+static void curses_refresh(DisplayState *ds)
+{
+ int chr, nextchr, keysym, keycode, keycode_alt;
+
+ if (invalidate) {
+ clear();
+ refresh();
+ curses_calc_pad();
+ ds->surface->width = FONT_WIDTH * width;
+ ds->surface->height = FONT_HEIGHT * height;
+ vga_hw_invalidate();
+ invalidate = 0;
+ }
+
+ vga_hw_text_update(screen);
+
+ nextchr = ERR;
+ while (1) {
+ /* while there are any pending key strokes to process */
+ if (nextchr == ERR)
+ chr = getch();
+ else {
+ chr = nextchr;
+ nextchr = ERR;
+ }
+
+ if (chr == ERR)
+ break;
+
+#ifdef KEY_RESIZE
+ /* this shouldn't occur when we use a custom SIGWINCH handler */
+ if (chr == KEY_RESIZE) {
+ clear();
+ refresh();
+ curses_calc_pad();
+ curses_update(ds, 0, 0, width, height);
+ ds->surface->width = FONT_WIDTH * width;
+ ds->surface->height = FONT_HEIGHT * height;
+ continue;
+ }
+#endif
+
+ keycode = curses2keycode[chr];
+ keycode_alt = 0;
+
+ /* alt key */
+ if (keycode == 1) {
+ nextchr = getch();
+
+ if (nextchr != ERR) {
+ chr = nextchr;
+ keycode_alt = ALT;
+ keycode = curses2keycode[nextchr];
+ nextchr = ERR;
+
+ if (keycode != -1) {
+ keycode |= ALT;
+
+ /* process keys reserved for qemu */
+ if (keycode >= QEMU_KEY_CONSOLE0 &&
+ keycode < QEMU_KEY_CONSOLE0 + 9) {
+ erase();
+ wnoutrefresh(stdscr);
+ console_select(keycode - QEMU_KEY_CONSOLE0);
+
+ invalidate = 1;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (kbd_layout) {
+ keysym = -1;
+ if (chr < CURSES_KEYS)
+ keysym = curses2keysym[chr];
+
+ if (keysym == -1) {
+ if (chr < ' ') {
+ keysym = chr + '@';
+ if (keysym >= 'A' && keysym <= 'Z')
+ keysym += 'a' - 'A';
+ keysym |= KEYSYM_CNTRL;
+ } else
+ keysym = chr;
+ }
+
+ keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK);
+ if (keycode == 0)
+ continue;
+
+ keycode |= (keysym & ~KEYSYM_MASK) >> 16;
+ keycode |= keycode_alt;
+ }
+
+ if (keycode == -1)
+ continue;
+
+ if (is_graphic_console()) {
+ /* since terminals don't know about key press and release
+ * events, we need to emit both for each key received */
+ if (keycode & SHIFT)
+ kbd_put_keycode(SHIFT_CODE);
+ if (keycode & CNTRL)
+ kbd_put_keycode(CNTRL_CODE);
+ if (keycode & ALT)
+ kbd_put_keycode(ALT_CODE);
+ if (keycode & ALTGR) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(ALT_CODE);
+ }
+ if (keycode & GREY)
+ kbd_put_keycode(GREY_CODE);
+ kbd_put_keycode(keycode & KEY_MASK);
+ if (keycode & GREY)
+ kbd_put_keycode(GREY_CODE);
+ kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
+ if (keycode & ALTGR) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+ }
+ if (keycode & ALT)
+ kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+ if (keycode & CNTRL)
+ kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
+ if (keycode & SHIFT)
+ kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
+ } else {
+ keysym = curses2qemu[chr];
+ if (keysym == -1)
+ keysym = chr;
+
+ kbd_put_keysym(keysym);
+ }
+ }
+}
+
+static void curses_atexit(void)
+{
+ endwin();
+}
+
+static void curses_setup(void)
+{
+ int i, colour_default[8] = {
+ COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
+ COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE,
+ };
+
+ /* input as raw as possible, let everything be interpreted
+ * by the guest system */
+ initscr(); noecho(); intrflush(stdscr, FALSE);
+ nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
+ start_color(); raw(); scrollok(stdscr, FALSE);
+
+ for (i = 0; i < 64; i ++)
+ init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
+}
+
+static void curses_keyboard_setup(void)
+{
+#if defined(__APPLE__)
+ /* always use generic keymaps */
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+#endif
+ if(keyboard_layout) {
+ kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ if (!kbd_layout)
+ exit(1);
+ }
+}
+
+void curses_display_init(DisplayState *ds, int full_screen)
+{
+ DisplayChangeListener *dcl;
+#ifndef _WIN32
+ if (!isatty(1)) {
+ fprintf(stderr, "We need a terminal output\n");
+ exit(1);
+ }
+#endif
+
+ curses_setup();
+ curses_keyboard_setup();
+ atexit(curses_atexit);
+
+#ifndef _WIN32
+#if defined(SIGWINCH) && defined(KEY_RESIZE)
+ /* some curses implementations provide a handler, but we
+ * want to be sure this is handled regardless of the library */
+ signal(SIGWINCH, curses_winch_handler);
+#endif
+#endif
+
+ dcl = (DisplayChangeListener *) qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = curses_update;
+ dcl->dpy_resize = curses_resize;
+ dcl->dpy_refresh = curses_refresh;
+ dcl->dpy_text_cursor = curses_cursor_position;
+ register_displaychangelistener(ds, dcl);
+ qemu_free_displaysurface(ds);
+ ds->surface = qemu_create_displaysurface_from(640, 400, 0, 0, (uint8_t*) screen);
+
+ invalidate = 1;
+}
diff --git a/ui/curses_keys.h b/ui/curses_keys.h
new file mode 100644
index 0000000..c0d5eb4
--- /dev/null
+++ b/ui/curses_keys.h
@@ -0,0 +1,509 @@
+/*
+ * Keycode and keysyms conversion tables for curses
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <curses.h>
+#include "keymaps.h"
+
+
+#define KEY_RELEASE 0x80
+#define KEY_MASK 0x7f
+#define GREY_CODE 0xe0
+#define GREY SCANCODE_GREY
+#define SHIFT_CODE 0x2a
+#define SHIFT SCANCODE_SHIFT
+#define CNTRL_CODE 0x1d
+#define CNTRL SCANCODE_CTRL
+#define ALT_CODE 0x38
+#define ALT SCANCODE_ALT
+#define ALTGR SCANCODE_ALTGR
+
+#define KEYSYM_MASK 0x0ffffff
+#define KEYSYM_SHIFT (SCANCODE_SHIFT << 16)
+#define KEYSYM_CNTRL (SCANCODE_CTRL << 16)
+#define KEYSYM_ALT (SCANCODE_ALT << 16)
+#define KEYSYM_ALTGR (SCANCODE_ALTGR << 16)
+
+/* curses won't detect a Control + Alt + 1, so use Alt + 1 */
+#define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */
+
+#define CURSES_KEYS KEY_MAX /* KEY_MAX defined in <curses.h> */
+
+static const int curses2keysym[CURSES_KEYS] = {
+ [0 ... (CURSES_KEYS - 1)] = -1,
+
+ [0x7f] = KEY_BACKSPACE,
+ ['\r'] = KEY_ENTER,
+ ['\n'] = KEY_ENTER,
+ [27] = 27,
+ [KEY_BTAB] = '\t' | KEYSYM_SHIFT,
+};
+
+static const int curses2keycode[CURSES_KEYS] = {
+ [0 ... (CURSES_KEYS - 1)] = -1,
+
+ [0x01b] = 1, /* Escape */
+ ['1'] = 2,
+ ['2'] = 3,
+ ['3'] = 4,
+ ['4'] = 5,
+ ['5'] = 6,
+ ['6'] = 7,
+ ['7'] = 8,
+ ['8'] = 9,
+ ['9'] = 10,
+ ['0'] = 11,
+ ['-'] = 12,
+ ['='] = 13,
+ [0x07f] = 14, /* Backspace */
+ [KEY_BACKSPACE] = 14, /* Backspace */
+
+ ['\t'] = 15, /* Tab */
+ ['q'] = 16,
+ ['w'] = 17,
+ ['e'] = 18,
+ ['r'] = 19,
+ ['t'] = 20,
+ ['y'] = 21,
+ ['u'] = 22,
+ ['i'] = 23,
+ ['o'] = 24,
+ ['p'] = 25,
+ ['['] = 26,
+ [']'] = 27,
+ ['\n'] = 28, /* Return */
+ ['\r'] = 28, /* Return */
+ [KEY_ENTER] = 28, /* Return */
+
+ ['a'] = 30,
+ ['s'] = 31,
+ ['d'] = 32,
+ ['f'] = 33,
+ ['g'] = 34,
+ ['h'] = 35,
+ ['j'] = 36,
+ ['k'] = 37,
+ ['l'] = 38,
+ [';'] = 39,
+ ['\''] = 40, /* Single quote */
+ ['`'] = 41,
+ ['\\'] = 43, /* Backslash */
+
+ ['z'] = 44,
+ ['x'] = 45,
+ ['c'] = 46,
+ ['v'] = 47,
+ ['b'] = 48,
+ ['n'] = 49,
+ ['m'] = 50,
+ [','] = 51,
+ ['.'] = 52,
+ ['/'] = 53,
+
+ [' '] = 57,
+
+ [KEY_F(1)] = 59, /* Function Key 1 */
+ [KEY_F(2)] = 60, /* Function Key 2 */
+ [KEY_F(3)] = 61, /* Function Key 3 */
+ [KEY_F(4)] = 62, /* Function Key 4 */
+ [KEY_F(5)] = 63, /* Function Key 5 */
+ [KEY_F(6)] = 64, /* Function Key 6 */
+ [KEY_F(7)] = 65, /* Function Key 7 */
+ [KEY_F(8)] = 66, /* Function Key 8 */
+ [KEY_F(9)] = 67, /* Function Key 9 */
+ [KEY_F(10)] = 68, /* Function Key 10 */
+ [KEY_F(11)] = 87, /* Function Key 11 */
+ [KEY_F(12)] = 88, /* Function Key 12 */
+
+ [KEY_HOME] = 71 | GREY, /* Home */
+ [KEY_UP] = 72 | GREY, /* Up Arrow */
+ [KEY_PPAGE] = 73 | GREY, /* Page Up */
+ [KEY_LEFT] = 75 | GREY, /* Left Arrow */
+ [KEY_RIGHT] = 77 | GREY, /* Right Arrow */
+ [KEY_END] = 79 | GREY, /* End */
+ [KEY_DOWN] = 80 | GREY, /* Down Arrow */
+ [KEY_NPAGE] = 81 | GREY, /* Page Down */
+ [KEY_IC] = 82 | GREY, /* Insert */
+ [KEY_DC] = 83 | GREY, /* Delete */
+
+ ['!'] = 2 | SHIFT,
+ ['@'] = 3 | SHIFT,
+ ['#'] = 4 | SHIFT,
+ ['$'] = 5 | SHIFT,
+ ['%'] = 6 | SHIFT,
+ ['^'] = 7 | SHIFT,
+ ['&'] = 8 | SHIFT,
+ ['*'] = 9 | SHIFT,
+ ['('] = 10 | SHIFT,
+ [')'] = 11 | SHIFT,
+ ['_'] = 12 | SHIFT,
+ ['+'] = 13 | SHIFT,
+
+ [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */
+ ['Q'] = 16 | SHIFT,
+ ['W'] = 17 | SHIFT,
+ ['E'] = 18 | SHIFT,
+ ['R'] = 19 | SHIFT,
+ ['T'] = 20 | SHIFT,
+ ['Y'] = 21 | SHIFT,
+ ['U'] = 22 | SHIFT,
+ ['I'] = 23 | SHIFT,
+ ['O'] = 24 | SHIFT,
+ ['P'] = 25 | SHIFT,
+ ['{'] = 26 | SHIFT,
+ ['}'] = 27 | SHIFT,
+
+ ['A'] = 30 | SHIFT,
+ ['S'] = 31 | SHIFT,
+ ['D'] = 32 | SHIFT,
+ ['F'] = 33 | SHIFT,
+ ['G'] = 34 | SHIFT,
+ ['H'] = 35 | SHIFT,
+ ['J'] = 36 | SHIFT,
+ ['K'] = 37 | SHIFT,
+ ['L'] = 38 | SHIFT,
+ [':'] = 39 | SHIFT,
+ ['"'] = 40 | SHIFT,
+ ['~'] = 41 | SHIFT,
+ ['|'] = 43 | SHIFT,
+
+ ['Z'] = 44 | SHIFT,
+ ['X'] = 45 | SHIFT,
+ ['C'] = 46 | SHIFT,
+ ['V'] = 47 | SHIFT,
+ ['B'] = 48 | SHIFT,
+ ['N'] = 49 | SHIFT,
+ ['M'] = 50 | SHIFT,
+ ['<'] = 51 | SHIFT,
+ ['>'] = 52 | SHIFT,
+ ['?'] = 53 | SHIFT,
+
+ [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */
+ [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */
+ [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */
+ [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */
+ [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */
+ [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */
+ [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */
+ [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */
+ [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */
+ [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */
+ [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */
+ [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */
+
+ ['Q' - '@'] = 16 | CNTRL, /* Control + q */
+ ['W' - '@'] = 17 | CNTRL, /* Control + w */
+ ['E' - '@'] = 18 | CNTRL, /* Control + e */
+ ['R' - '@'] = 19 | CNTRL, /* Control + r */
+ ['T' - '@'] = 20 | CNTRL, /* Control + t */
+ ['Y' - '@'] = 21 | CNTRL, /* Control + y */
+ ['U' - '@'] = 22 | CNTRL, /* Control + u */
+ /* Control + i collides with Tab */
+ ['O' - '@'] = 24 | CNTRL, /* Control + o */
+ ['P' - '@'] = 25 | CNTRL, /* Control + p */
+
+ ['A' - '@'] = 30 | CNTRL, /* Control + a */
+ ['S' - '@'] = 31 | CNTRL, /* Control + s */
+ ['D' - '@'] = 32 | CNTRL, /* Control + d */
+ ['F' - '@'] = 33 | CNTRL, /* Control + f */
+ ['G' - '@'] = 34 | CNTRL, /* Control + g */
+ ['H' - '@'] = 35 | CNTRL, /* Control + h */
+ /* Control + j collides with Return */
+ ['K' - '@'] = 37 | CNTRL, /* Control + k */
+ ['L' - '@'] = 38 | CNTRL, /* Control + l */
+
+ ['Z' - '@'] = 44 | CNTRL, /* Control + z */
+ ['X' - '@'] = 45 | CNTRL, /* Control + x */
+ ['C' - '@'] = 46 | CNTRL, /* Control + c */
+ ['V' - '@'] = 47 | CNTRL, /* Control + v */
+ ['B' - '@'] = 48 | CNTRL, /* Control + b */
+ ['N' - '@'] = 49 | CNTRL, /* Control + n */
+ /* Control + m collides with the keycode for Enter */
+
+};
+
+static const int curses2qemu[CURSES_KEYS] = {
+ [0 ... (CURSES_KEYS - 1)] = -1,
+
+ ['\n'] = '\n',
+ ['\r'] = '\n',
+
+ [0x07f] = QEMU_KEY_BACKSPACE,
+
+ [KEY_DOWN] = QEMU_KEY_DOWN,
+ [KEY_UP] = QEMU_KEY_UP,
+ [KEY_LEFT] = QEMU_KEY_LEFT,
+ [KEY_RIGHT] = QEMU_KEY_RIGHT,
+ [KEY_HOME] = QEMU_KEY_HOME,
+ [KEY_BACKSPACE] = QEMU_KEY_BACKSPACE,
+
+ [KEY_DC] = QEMU_KEY_DELETE,
+ [KEY_NPAGE] = QEMU_KEY_PAGEDOWN,
+ [KEY_PPAGE] = QEMU_KEY_PAGEUP,
+ [KEY_ENTER] = '\n',
+ [KEY_END] = QEMU_KEY_END,
+
+};
+
+static const name2keysym_t name2keysym[] = {
+ /* Plain ASCII */
+ { "space", 0x020 },
+ { "exclam", 0x021 },
+ { "quotedbl", 0x022 },
+ { "numbersign", 0x023 },
+ { "dollar", 0x024 },
+ { "percent", 0x025 },
+ { "ampersand", 0x026 },
+ { "apostrophe", 0x027 },
+ { "parenleft", 0x028 },
+ { "parenright", 0x029 },
+ { "asterisk", 0x02a },
+ { "plus", 0x02b },
+ { "comma", 0x02c },
+ { "minus", 0x02d },
+ { "period", 0x02e },
+ { "slash", 0x02f },
+ { "0", 0x030 },
+ { "1", 0x031 },
+ { "2", 0x032 },
+ { "3", 0x033 },
+ { "4", 0x034 },
+ { "5", 0x035 },
+ { "6", 0x036 },
+ { "7", 0x037 },
+ { "8", 0x038 },
+ { "9", 0x039 },
+ { "colon", 0x03a },
+ { "semicolon", 0x03b },
+ { "less", 0x03c },
+ { "equal", 0x03d },
+ { "greater", 0x03e },
+ { "question", 0x03f },
+ { "at", 0x040 },
+ { "A", 0x041 },
+ { "B", 0x042 },
+ { "C", 0x043 },
+ { "D", 0x044 },
+ { "E", 0x045 },
+ { "F", 0x046 },
+ { "G", 0x047 },
+ { "H", 0x048 },
+ { "I", 0x049 },
+ { "J", 0x04a },
+ { "K", 0x04b },
+ { "L", 0x04c },
+ { "M", 0x04d },
+ { "N", 0x04e },
+ { "O", 0x04f },
+ { "P", 0x050 },
+ { "Q", 0x051 },
+ { "R", 0x052 },
+ { "S", 0x053 },
+ { "T", 0x054 },
+ { "U", 0x055 },
+ { "V", 0x056 },
+ { "W", 0x057 },
+ { "X", 0x058 },
+ { "Y", 0x059 },
+ { "Z", 0x05a },
+ { "bracketleft", 0x05b },
+ { "backslash", 0x05c },
+ { "bracketright", 0x05d },
+ { "asciicircum", 0x05e },
+ { "underscore", 0x05f },
+ { "grave", 0x060 },
+ { "a", 0x061 },
+ { "b", 0x062 },
+ { "c", 0x063 },
+ { "d", 0x064 },
+ { "e", 0x065 },
+ { "f", 0x066 },
+ { "g", 0x067 },
+ { "h", 0x068 },
+ { "i", 0x069 },
+ { "j", 0x06a },
+ { "k", 0x06b },
+ { "l", 0x06c },
+ { "m", 0x06d },
+ { "n", 0x06e },
+ { "o", 0x06f },
+ { "p", 0x070 },
+ { "q", 0x071 },
+ { "r", 0x072 },
+ { "s", 0x073 },
+ { "t", 0x074 },
+ { "u", 0x075 },
+ { "v", 0x076 },
+ { "w", 0x077 },
+ { "x", 0x078 },
+ { "y", 0x079 },
+ { "z", 0x07a },
+ { "braceleft", 0x07b },
+ { "bar", 0x07c },
+ { "braceright", 0x07d },
+ { "asciitilde", 0x07e },
+
+ /* Latin-1 extensions */
+ { "nobreakspace", 0x0a0 },
+ { "exclamdown", 0x0a1 },
+ { "cent", 0x0a2 },
+ { "sterling", 0x0a3 },
+ { "currency", 0x0a4 },
+ { "yen", 0x0a5 },
+ { "brokenbar", 0x0a6 },
+ { "section", 0x0a7 },
+ { "diaeresis", 0x0a8 },
+ { "copyright", 0x0a9 },
+ { "ordfeminine", 0x0aa },
+ { "guillemotleft", 0x0ab },
+ { "notsign", 0x0ac },
+ { "hyphen", 0x0ad },
+ { "registered", 0x0ae },
+ { "macron", 0x0af },
+ { "degree", 0x0b0 },
+ { "plusminus", 0x0b1 },
+ { "twosuperior", 0x0b2 },
+ { "threesuperior", 0x0b3 },
+ { "acute", 0x0b4 },
+ { "mu", 0x0b5 },
+ { "paragraph", 0x0b6 },
+ { "periodcentered", 0x0b7 },
+ { "cedilla", 0x0b8 },
+ { "onesuperior", 0x0b9 },
+ { "masculine", 0x0ba },
+ { "guillemotright", 0x0bb },
+ { "onequarter", 0x0bc },
+ { "onehalf", 0x0bd },
+ { "threequarters", 0x0be },
+ { "questiondown", 0x0bf },
+ { "Agrave", 0x0c0 },
+ { "Aacute", 0x0c1 },
+ { "Acircumflex", 0x0c2 },
+ { "Atilde", 0x0c3 },
+ { "Adiaeresis", 0x0c4 },
+ { "Aring", 0x0c5 },
+ { "AE", 0x0c6 },
+ { "Ccedilla", 0x0c7 },
+ { "Egrave", 0x0c8 },
+ { "Eacute", 0x0c9 },
+ { "Ecircumflex", 0x0ca },
+ { "Ediaeresis", 0x0cb },
+ { "Igrave", 0x0cc },
+ { "Iacute", 0x0cd },
+ { "Icircumflex", 0x0ce },
+ { "Idiaeresis", 0x0cf },
+ { "ETH", 0x0d0 },
+ { "Eth", 0x0d0 },
+ { "Ntilde", 0x0d1 },
+ { "Ograve", 0x0d2 },
+ { "Oacute", 0x0d3 },
+ { "Ocircumflex", 0x0d4 },
+ { "Otilde", 0x0d5 },
+ { "Odiaeresis", 0x0d6 },
+ { "multiply", 0x0d7 },
+ { "Ooblique", 0x0d8 },
+ { "Oslash", 0x0d8 },
+ { "Ugrave", 0x0d9 },
+ { "Uacute", 0x0da },
+ { "Ucircumflex", 0x0db },
+ { "Udiaeresis", 0x0dc },
+ { "Yacute", 0x0dd },
+ { "THORN", 0x0de },
+ { "Thorn", 0x0de },
+ { "ssharp", 0x0df },
+ { "agrave", 0x0e0 },
+ { "aacute", 0x0e1 },
+ { "acircumflex", 0x0e2 },
+ { "atilde", 0x0e3 },
+ { "adiaeresis", 0x0e4 },
+ { "aring", 0x0e5 },
+ { "ae", 0x0e6 },
+ { "ccedilla", 0x0e7 },
+ { "egrave", 0x0e8 },
+ { "eacute", 0x0e9 },
+ { "ecircumflex", 0x0ea },
+ { "ediaeresis", 0x0eb },
+ { "igrave", 0x0ec },
+ { "iacute", 0x0ed },
+ { "icircumflex", 0x0ee },
+ { "idiaeresis", 0x0ef },
+ { "eth", 0x0f0 },
+ { "ntilde", 0x0f1 },
+ { "ograve", 0x0f2 },
+ { "oacute", 0x0f3 },
+ { "ocircumflex", 0x0f4 },
+ { "otilde", 0x0f5 },
+ { "odiaeresis", 0x0f6 },
+ { "division", 0x0f7 },
+ { "oslash", 0x0f8 },
+ { "ooblique", 0x0f8 },
+ { "ugrave", 0x0f9 },
+ { "uacute", 0x0fa },
+ { "ucircumflex", 0x0fb },
+ { "udiaeresis", 0x0fc },
+ { "yacute", 0x0fd },
+ { "thorn", 0x0fe },
+ { "ydiaeresis", 0x0ff },
+
+ /* Special keys */
+ { "BackSpace", KEY_BACKSPACE },
+ { "Tab", '\t' },
+ { "Return", KEY_ENTER },
+ { "Right", KEY_RIGHT },
+ { "Left", KEY_LEFT },
+ { "Up", KEY_UP },
+ { "Down", KEY_DOWN },
+ { "Page_Down", KEY_NPAGE },
+ { "Page_Up", KEY_PPAGE },
+ { "Insert", KEY_IC },
+ { "Delete", KEY_DC },
+ { "Home", KEY_HOME },
+ { "End", KEY_END },
+ { "F1", KEY_F(1) },
+ { "F2", KEY_F(2) },
+ { "F3", KEY_F(3) },
+ { "F4", KEY_F(4) },
+ { "F5", KEY_F(5) },
+ { "F6", KEY_F(6) },
+ { "F7", KEY_F(7) },
+ { "F8", KEY_F(8) },
+ { "F9", KEY_F(9) },
+ { "F10", KEY_F(10) },
+ { "F11", KEY_F(11) },
+ { "F12", KEY_F(12) },
+ { "F13", KEY_F(13) },
+ { "F14", KEY_F(14) },
+ { "F15", KEY_F(15) },
+ { "F16", KEY_F(16) },
+ { "F17", KEY_F(17) },
+ { "F18", KEY_F(18) },
+ { "F19", KEY_F(19) },
+ { "F20", KEY_F(20) },
+ { "F21", KEY_F(21) },
+ { "F22", KEY_F(22) },
+ { "F23", KEY_F(23) },
+ { "F24", KEY_F(24) },
+ { "Escape", 27 },
+
+ { NULL, 0 },
+};
diff --git a/ui/d3des.c b/ui/d3des.c
new file mode 100644
index 0000000..60c840e
--- /dev/null
+++ b/ui/d3des.c
@@ -0,0 +1,424 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC. Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software 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.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static const unsigned short bytebit[8] = {
+ 01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static const unsigned long bigbyte[24] = {
+ 0x800000L, 0x400000L, 0x200000L, 0x100000L,
+ 0x80000L, 0x40000L, 0x20000L, 0x10000L,
+ 0x8000L, 0x4000L, 0x2000L, 0x1000L,
+ 0x800L, 0x400L, 0x200L, 0x100L,
+ 0x80L, 0x40L, 0x20L, 0x10L,
+ 0x8L, 0x4L, 0x2L, 0x1L };
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static const unsigned char pc1[56] = {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
+
+static const unsigned char totrot[16] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static const unsigned char pc2[48] = {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+/* Thanks to James Gillogly & Phil Karn! */
+void deskey(unsigned char *key, int edf)
+{
+ register int i, j, l, m, n;
+ unsigned char pc1m[56], pcr[56];
+ unsigned long kn[32];
+
+ for ( j = 0; j < 56; j++ ) {
+ l = pc1[j];
+ m = l & 07;
+ pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+ }
+ for( i = 0; i < 16; i++ ) {
+ if( edf == DE1 ) m = (15 - i) << 1;
+ else m = i << 1;
+ n = m + 1;
+ kn[m] = kn[n] = 0L;
+ for( j = 0; j < 28; j++ ) {
+ l = j + totrot[i];
+ if( l < 28 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 28; j < 56; j++ ) {
+ l = j + totrot[i];
+ if( l < 56 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 0; j < 24; j++ ) {
+ if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+ if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+ }
+ }
+ cookey(kn);
+ return;
+ }
+
+static void cookey(register unsigned long *raw1)
+{
+ register unsigned long *cook, *raw0;
+ unsigned long dough[32];
+ register int i;
+
+ cook = dough;
+ for( i = 0; i < 16; i++, raw1++ ) {
+ raw0 = raw1++;
+ *cook = (*raw0 & 0x00fc0000L) << 6;
+ *cook |= (*raw0 & 0x00000fc0L) << 10;
+ *cook |= (*raw1 & 0x00fc0000L) >> 10;
+ *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+ *cook = (*raw0 & 0x0003f000L) << 12;
+ *cook |= (*raw0 & 0x0000003fL) << 16;
+ *cook |= (*raw1 & 0x0003f000L) >> 4;
+ *cook++ |= (*raw1 & 0x0000003fL);
+ }
+ usekey(dough);
+ return;
+ }
+
+void cpkey(register unsigned long *into)
+{
+ register unsigned long *from, *endp;
+
+ from = KnL, endp = &KnL[32];
+ while( from < endp ) *into++ = *from++;
+ return;
+ }
+
+void usekey(register unsigned long *from)
+{
+ register unsigned long *to, *endp;
+
+ to = KnL, endp = &KnL[32];
+ while( to < endp ) *to++ = *from++;
+ return;
+ }
+
+void des(unsigned char *inblock, unsigned char *outblock)
+{
+ unsigned long work[2];
+
+ scrunch(inblock, work);
+ desfunc(work, KnL);
+ unscrun(work, outblock);
+ return;
+ }
+
+static void scrunch(register unsigned char *outof, register unsigned long *into)
+{
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into++ |= (*outof++ & 0xffL);
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into |= (*outof & 0xffL);
+ return;
+ }
+
+static void unscrun(register unsigned long *outof, register unsigned char *into)
+{
+ *into++ = (unsigned char)((*outof >> 24) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 16) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 8) & 0xffL);
+ *into++ = (unsigned char)(*outof++ & 0xffL);
+ *into++ = (unsigned char)((*outof >> 24) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 16) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 8) & 0xffL);
+ *into = (unsigned char)(*outof & 0xffL);
+ return;
+ }
+
+static const unsigned long SP1[64] = {
+ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+ 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+ 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+ 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+ 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+ 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+ 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+ 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+ 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+ 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+ 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+ 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+ 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+ 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static const unsigned long SP2[64] = {
+ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+ 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+ 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+ 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+ 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+ 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+ 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+ 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+ 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+ 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+ 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+ 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+ 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+ 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+ 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+ 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static const unsigned long SP3[64] = {
+ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+ 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+ 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+ 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+ 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+ 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+ 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+ 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+ 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+ 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+ 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+ 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+ 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+ 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+ 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+ 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static const unsigned long SP4[64] = {
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+ 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+ 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+ 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+ 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+ 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+ 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+ 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+ 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static const unsigned long SP5[64] = {
+ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+ 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+ 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+ 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+ 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+ 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+ 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+ 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+ 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+ 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+ 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+ 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+ 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+ 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+ 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+ 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static const unsigned long SP6[64] = {
+ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+ 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+ 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+ 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+ 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+ 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+ 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+ 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+ 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+ 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+ 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+ 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+ 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+ 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static const unsigned long SP7[64] = {
+ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+ 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+ 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+ 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+ 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+ 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+ 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+ 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+ 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+ 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+ 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+ 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+ 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+ 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+ 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+ 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static const unsigned long SP8[64] = {
+ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+ 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+ 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+ 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+ 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+ 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+ 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+ 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+ 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+ 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+ 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+ 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+ 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+ 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+ 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+ 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(register unsigned long *block, register unsigned long *keys)
+{
+ register unsigned long fval, work, right, leftt;
+ register int round;
+
+ leftt = block[0];
+ right = block[1];
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+ right ^= work;
+ leftt ^= (work << 4);
+ work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+ right ^= work;
+ leftt ^= (work << 16);
+ work = ((right >> 2) ^ leftt) & 0x33333333L;
+ leftt ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+ leftt ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ leftt ^= fval;
+ work = (leftt << 28) | (leftt >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = leftt ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >> 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = (leftt << 31) | (leftt >> 1);
+ work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+ right ^= work;
+ leftt ^= (work << 8);
+ work = ((leftt >> 2) ^ right) & 0x33333333L;
+ right ^= work;
+ leftt ^= (work << 2);
+ work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+ leftt ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+ leftt ^= work;
+ right ^= (work << 4);
+ *block++ = right;
+ *block = leftt;
+ return;
+ }
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/ui/d3des.h b/ui/d3des.h
new file mode 100644
index 0000000..78d546f
--- /dev/null
+++ b/ui/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software 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.
+ */
+
+/* d3des.h -
+ *
+ * Headers and defines for d3des.c
+ * Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ * (GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0 0 /* MODE == encrypt */
+#define DE1 1 /* MODE == decrypt */
+
+void deskey(unsigned char *, int);
+/* hexkey[8] MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+void usekey(unsigned long *);
+/* cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+void cpkey(unsigned long *);
+/* cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+void des(unsigned char *, unsigned char *);
+/* from[8] to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'. They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/ui/keymaps.c b/ui/keymaps.c
new file mode 100644
index 0000000..78c7ea3
--- /dev/null
+++ b/ui/keymaps.c
@@ -0,0 +1,210 @@
+/*
+ * QEMU keysym to keycode conversion using rdesktop keymaps
+ *
+ * Copyright (c) 2004 Johannes Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "keymaps.h"
+#include "sysemu.h"
+
+static int get_keysym(const name2keysym_t *table,
+ const char *name)
+{
+ const name2keysym_t *p;
+ for(p = table; p->name != NULL; p++) {
+ if (!strcmp(p->name, name))
+ return p->keysym;
+ }
+ return 0;
+}
+
+
+static void add_to_key_range(struct key_range **krp, int code) {
+ struct key_range *kr;
+ for (kr = *krp; kr; kr = kr->next) {
+ if (code >= kr->start && code <= kr->end)
+ break;
+ if (code == kr->start - 1) {
+ kr->start--;
+ break;
+ }
+ if (code == kr->end + 1) {
+ kr->end++;
+ break;
+ }
+ }
+ if (kr == NULL) {
+ kr = qemu_mallocz(sizeof(*kr));
+ kr->start = kr->end = code;
+ kr->next = *krp;
+ *krp = kr;
+ }
+}
+
+static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) {
+ if (keysym < MAX_NORMAL_KEYCODE) {
+ //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode);
+ k->keysym2keycode[keysym] = keycode;
+ } else {
+ if (k->extra_count >= MAX_EXTRA_COUNT) {
+ fprintf(stderr,
+ "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n",
+ line, keysym);
+ } else {
+#if 0
+ fprintf(stderr, "Setting %d: %d,%d\n",
+ k->extra_count, keysym, keycode);
+#endif
+ k->keysym2keycode_extra[k->extra_count].
+ keysym = keysym;
+ k->keysym2keycode_extra[k->extra_count].
+ keycode = keycode;
+ k->extra_count++;
+ }
+ }
+}
+
+static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table,
+ const char *language,
+ kbd_layout_t * k)
+{
+ FILE *f;
+ char * filename;
+ char line[1024];
+ int len;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
+
+ if (!k)
+ k = qemu_mallocz(sizeof(kbd_layout_t));
+ if (!(filename && (f = fopen(filename, "r")))) {
+ fprintf(stderr,
+ "Could not read keymap file: '%s'\n", language);
+ return NULL;
+ }
+ qemu_free(filename);
+ for(;;) {
+ if (fgets(line, 1024, f) == NULL)
+ break;
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+ if (!strncmp(line, "map ", 4))
+ continue;
+ if (!strncmp(line, "include ", 8)) {
+ parse_keyboard_layout(table, line + 8, k);
+ } else {
+ char *end_of_keysym = line;
+ while (*end_of_keysym != 0 && *end_of_keysym != ' ')
+ end_of_keysym++;
+ if (*end_of_keysym) {
+ int keysym;
+ *end_of_keysym = 0;
+ keysym = get_keysym(table, line);
+ if (keysym == 0) {
+ // fprintf(stderr, "Warning: unknown keysym %s\n", line);
+ } else {
+ const char *rest = end_of_keysym + 1;
+ char *rest2;
+ int keycode = strtol(rest, &rest2, 0);
+
+ if (rest && strstr(rest, "numlock")) {
+ add_to_key_range(&k->keypad_range, keycode);
+ add_to_key_range(&k->numlock_range, keysym);
+ //fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode);
+ }
+
+ if (rest && strstr(rest, "shift"))
+ keycode |= SCANCODE_SHIFT;
+ if (rest && strstr(rest, "altgr"))
+ keycode |= SCANCODE_ALTGR;
+ if (rest && strstr(rest, "ctrl"))
+ keycode |= SCANCODE_CTRL;
+
+ add_keysym(line, keysym, keycode, k);
+
+ if (rest && strstr(rest, "addupper")) {
+ char *c;
+ for (c = line; *c; c++)
+ *c = toupper(*c);
+ keysym = get_keysym(table, line);
+ if (keysym)
+ add_keysym(line, keysym, keycode | SCANCODE_SHIFT, k);
+ }
+ }
+ }
+ }
+ }
+ fclose(f);
+ return k;
+}
+
+
+void *init_keyboard_layout(const name2keysym_t *table, const char *language)
+{
+ return parse_keyboard_layout(table, language, NULL);
+}
+
+
+int keysym2scancode(void *kbd_layout, int keysym)
+{
+ kbd_layout_t *k = kbd_layout;
+ if (keysym < MAX_NORMAL_KEYCODE) {
+ if (k->keysym2keycode[keysym] == 0)
+ fprintf(stderr, "Warning: no scancode found for keysym %d\n",
+ keysym);
+ return k->keysym2keycode[keysym];
+ } else {
+ int i;
+#ifdef XK_ISO_Left_Tab
+ if (keysym == XK_ISO_Left_Tab)
+ keysym = XK_Tab;
+#endif
+ for (i = 0; i < k->extra_count; i++)
+ if (k->keysym2keycode_extra[i].keysym == keysym)
+ return k->keysym2keycode_extra[i].keycode;
+ }
+ return 0;
+}
+
+int keycode_is_keypad(void *kbd_layout, int keycode)
+{
+ kbd_layout_t *k = kbd_layout;
+ struct key_range *kr;
+
+ for (kr = k->keypad_range; kr; kr = kr->next)
+ if (keycode >= kr->start && keycode <= kr->end)
+ return 1;
+ return 0;
+}
+
+int keysym_is_numlock(void *kbd_layout, int keysym)
+{
+ kbd_layout_t *k = kbd_layout;
+ struct key_range *kr;
+
+ for (kr = k->numlock_range; kr; kr = kr->next)
+ if (keysym >= kr->start && keysym <= kr->end)
+ return 1;
+ return 0;
+}
diff --git a/ui/keymaps.h b/ui/keymaps.h
new file mode 100644
index 0000000..a7600d5
--- /dev/null
+++ b/ui/keymaps.h
@@ -0,0 +1,77 @@
+/*
+ * QEMU keysym to keycode conversion using rdesktop keymaps
+ *
+ * Copyright (c) 2004 Johannes Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __QEMU_KEYMAPS_H__
+#define __QEMU_KEYMAPS_H__
+
+#include "qemu-common.h"
+
+typedef struct {
+ const char* name;
+ int keysym;
+} name2keysym_t;
+
+struct key_range {
+ int start;
+ int end;
+ struct key_range *next;
+};
+
+#define MAX_NORMAL_KEYCODE 512
+#define MAX_EXTRA_COUNT 256
+typedef struct {
+ uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
+ struct {
+ int keysym;
+ uint16_t keycode;
+ } keysym2keycode_extra[MAX_EXTRA_COUNT];
+ int extra_count;
+ struct key_range *keypad_range;
+ struct key_range *numlock_range;
+} kbd_layout_t;
+
+/* scancode without modifiers */
+#define SCANCODE_KEYMASK 0xff
+/* scancode without grey or up bit */
+#define SCANCODE_KEYCODEMASK 0x7f
+
+/* "grey" keys will usually need a 0xe0 prefix */
+#define SCANCODE_GREY 0x80
+#define SCANCODE_EMUL0 0xE0
+/* "up" flag */
+#define SCANCODE_UP 0x80
+
+/* Additional modifiers to use if not catched another way. */
+#define SCANCODE_SHIFT 0x100
+#define SCANCODE_CTRL 0x200
+#define SCANCODE_ALT 0x400
+#define SCANCODE_ALTGR 0x800
+
+
+void *init_keyboard_layout(const name2keysym_t *table, const char *language);
+int keysym2scancode(void *kbd_layout, int keysym);
+int keycode_is_keypad(void *kbd_layout, int keycode);
+int keysym_is_numlock(void *kbd_layout, int keysym);
+
+#endif /* __QEMU_KEYMAPS_H__ */
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
new file mode 100644
index 0000000..916e5dc
--- /dev/null
+++ b/ui/qemu-spice.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#ifndef QEMU_SPICE_H
+#define QEMU_SPICE_H
+
+#ifdef CONFIG_SPICE
+
+#include <spice.h>
+
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "qemu-char.h"
+
+extern int using_spice;
+
+void qemu_spice_init(void);
+void qemu_spice_input_init(void);
+void qemu_spice_audio_init(void);
+void qemu_spice_display_init(DisplayState *ds);
+int qemu_spice_add_interface(SpiceBaseInstance *sin);
+int qemu_spice_set_passwd(const char *passwd,
+ bool fail_if_connected, bool disconnect_if_connected);
+int qemu_spice_set_pw_expire(time_t expires);
+int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
+ const char *subject);
+
+void do_info_spice_print(Monitor *mon, const QObject *data);
+void do_info_spice(Monitor *mon, QObject **ret_data);
+
+CharDriverState *qemu_chr_open_spice(QemuOpts *opts);
+
+#else /* CONFIG_SPICE */
+
+#define using_spice 0
+#define qemu_spice_set_passwd(_p, _f1, _f2) (-1)
+#define qemu_spice_set_pw_expire(_e) (-1)
+static inline int qemu_spice_migrate_info(const char *h, int p, int t, const char *s)
+{ return -1; }
+
+#endif /* CONFIG_SPICE */
+
+#endif /* QEMU_SPICE_H */
diff --git a/ui/sdl.c b/ui/sdl.c
new file mode 100644
index 0000000..47ac49c
--- /dev/null
+++ b/ui/sdl.c
@@ -0,0 +1,885 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
+#undef WIN32_LEAN_AND_MEAN
+
+#include <SDL.h>
+#include <SDL_syswm.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#endif
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+#include "x_keymap.h"
+#include "sdl_zoom.h"
+
+static DisplayChangeListener *dcl;
+static SDL_Surface *real_screen;
+static SDL_Surface *guest_screen = NULL;
+static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
+static int last_vm_running;
+static int gui_saved_grab;
+static int gui_fullscreen;
+static int gui_noframe;
+static int gui_key_modifier_pressed;
+static int gui_keysym;
+static int gui_fullscreen_initial_grab;
+static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
+static uint8_t modifiers_state[256];
+static int width, height;
+static SDL_Cursor *sdl_cursor_normal;
+static SDL_Cursor *sdl_cursor_hidden;
+static int absolute_enabled = 0;
+static int guest_cursor = 0;
+static int guest_x, guest_y;
+static SDL_Cursor *guest_sprite = NULL;
+static uint8_t allocator;
+static SDL_PixelFormat host_format;
+static int scaling_active = 0;
+static Notifier mouse_mode_notifier;
+
+static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
+ SDL_Rect rec;
+ rec.x = x;
+ rec.y = y;
+ rec.w = w;
+ rec.h = h;
+
+ if (guest_screen) {
+ if (!scaling_active) {
+ SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
+ } else {
+ if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) {
+ fprintf(stderr, "Zoom blit failed\n");
+ exit(1);
+ }
+ }
+ }
+ SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h);
+}
+
+static void sdl_setdata(DisplayState *ds)
+{
+ if (guest_screen != NULL) SDL_FreeSurface(guest_screen);
+
+ guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds),
+ ds_get_bits_per_pixel(ds), ds_get_linesize(ds),
+ ds->surface->pf.rmask, ds->surface->pf.gmask,
+ ds->surface->pf.bmask, ds->surface->pf.amask);
+}
+
+static void do_sdl_resize(int new_width, int new_height, int bpp)
+{
+ int flags;
+
+ // printf("resizing to %d %d\n", w, h);
+
+ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
+ if (gui_fullscreen)
+ flags |= SDL_FULLSCREEN;
+ if (gui_noframe)
+ flags |= SDL_NOFRAME;
+
+ width = new_width;
+ height = new_height;
+ real_screen = SDL_SetVideoMode(width, height, bpp, flags);
+ if (!real_screen) {
+ fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width,
+ height, bpp, SDL_GetError());
+ exit(1);
+ }
+}
+
+static void sdl_resize(DisplayState *ds)
+{
+ if (!allocator) {
+ if (!scaling_active)
+ do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0);
+ else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds))
+ do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds));
+ sdl_setdata(ds);
+ } else {
+ if (guest_screen != NULL) {
+ SDL_FreeSurface(guest_screen);
+ guest_screen = NULL;
+ }
+ }
+}
+
+static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf)
+{
+ PixelFormat qemu_pf;
+
+ memset(&qemu_pf, 0x00, sizeof(PixelFormat));
+
+ qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel;
+ qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel;
+ qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel);
+
+ qemu_pf.rmask = sdl_pf->Rmask;
+ qemu_pf.gmask = sdl_pf->Gmask;
+ qemu_pf.bmask = sdl_pf->Bmask;
+ qemu_pf.amask = sdl_pf->Amask;
+
+ qemu_pf.rshift = sdl_pf->Rshift;
+ qemu_pf.gshift = sdl_pf->Gshift;
+ qemu_pf.bshift = sdl_pf->Bshift;
+ qemu_pf.ashift = sdl_pf->Ashift;
+
+ qemu_pf.rbits = 8 - sdl_pf->Rloss;
+ qemu_pf.gbits = 8 - sdl_pf->Gloss;
+ qemu_pf.bbits = 8 - sdl_pf->Bloss;
+ qemu_pf.abits = 8 - sdl_pf->Aloss;
+
+ qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1);
+ qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1);
+ qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1);
+ qemu_pf.amax = ((1 << qemu_pf.abits) - 1);
+
+ return qemu_pf;
+}
+
+static DisplaySurface* sdl_create_displaysurface(int width, int height)
+{
+ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
+ if (surface == NULL) {
+ fprintf(stderr, "sdl_create_displaysurface: malloc failed\n");
+ exit(1);
+ }
+
+ surface->width = width;
+ surface->height = height;
+
+ if (scaling_active) {
+ if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) {
+ surface->linesize = width * 4;
+ surface->pf = qemu_default_pixelformat(32);
+ } else {
+ surface->linesize = width * host_format.BytesPerPixel;
+ surface->pf = sdl_to_qemu_pixelformat(&host_format);
+ }
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_ALLOCATED_FLAG;
+#endif
+ surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
+
+ return surface;
+ }
+
+ if (host_format.BitsPerPixel == 16)
+ do_sdl_resize(width, height, 16);
+ else
+ do_sdl_resize(width, height, 32);
+
+ surface->pf = sdl_to_qemu_pixelformat(real_screen->format);
+ surface->linesize = real_screen->pitch;
+ surface->data = real_screen->pixels;
+
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_REALPIXELS_FLAG;
+#endif
+ allocator = 1;
+
+ return surface;
+}
+
+static void sdl_free_displaysurface(DisplaySurface *surface)
+{
+ allocator = 0;
+ if (surface == NULL)
+ return;
+
+ if (surface->flags & QEMU_ALLOCATED_FLAG)
+ qemu_free(surface->data);
+ qemu_free(surface);
+}
+
+static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height)
+{
+ sdl_free_displaysurface(surface);
+ return sdl_create_displaysurface(width, height);
+}
+
+/* generic keyboard conversion */
+
+#include "sdl_keysym.h"
+
+static kbd_layout_t *kbd_layout = NULL;
+
+static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
+{
+ int keysym;
+ /* workaround for X11+SDL bug with AltGR */
+ keysym = ev->keysym.sym;
+ if (keysym == 0 && ev->keysym.scancode == 113)
+ keysym = SDLK_MODE;
+ /* For Japanese key '\' and '|' */
+ if (keysym == 92 && ev->keysym.scancode == 133) {
+ keysym = 0xa5;
+ }
+ return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
+}
+
+/* specific keyboard conversions from scan codes */
+
+#if defined(_WIN32)
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ return ev->keysym.scancode;
+}
+
+#else
+
+#if defined(SDL_VIDEO_DRIVER_X11)
+#include <X11/XKBlib.h>
+
+static int check_for_evdev(void)
+{
+ SDL_SysWMinfo info;
+ XkbDescPtr desc = NULL;
+ int has_evdev = 0;
+ char *keycodes = NULL;
+
+ SDL_VERSION(&info.version);
+ if (!SDL_GetWMInfo(&info)) {
+ return 0;
+ }
+ desc = XkbGetKeyboard(info.info.x11.display,
+ XkbGBN_AllComponentsMask,
+ XkbUseCoreKbd);
+ if (desc && desc->names) {
+ keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
+ if (keycodes == NULL) {
+ fprintf(stderr, "could not lookup keycode name\n");
+ } else if (strstart(keycodes, "evdev", NULL)) {
+ has_evdev = 1;
+ } else if (!strstart(keycodes, "xfree86", NULL)) {
+ fprintf(stderr, "unknown keycodes `%s', please report to "
+ "qemu-devel@nongnu.org\n", keycodes);
+ }
+ }
+
+ if (desc) {
+ XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
+ }
+ if (keycodes) {
+ XFree(keycodes);
+ }
+ return has_evdev;
+}
+#else
+static int check_for_evdev(void)
+{
+ return 0;
+}
+#endif
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ int keycode;
+ static int has_evdev = -1;
+
+ if (has_evdev == -1)
+ has_evdev = check_for_evdev();
+
+ keycode = ev->keysym.scancode;
+
+ if (keycode < 9) {
+ keycode = 0;
+ } else if (keycode < 97) {
+ keycode -= 8; /* just an offset */
+ } else if (keycode < 158) {
+ /* use conversion table */
+ if (has_evdev)
+ keycode = translate_evdev_keycode(keycode - 97);
+ else
+ keycode = translate_xfree86_keycode(keycode - 97);
+ } else if (keycode == 208) { /* Hiragana_Katakana */
+ keycode = 0x70;
+ } else if (keycode == 211) { /* backslash */
+ keycode = 0x73;
+ } else {
+ keycode = 0;
+ }
+ return keycode;
+}
+
+#endif
+
+static void reset_keys(void)
+{
+ int i;
+ for(i = 0; i < 256; i++) {
+ if (modifiers_state[i]) {
+ if (i & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(i | SCANCODE_UP);
+ modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void sdl_process_key(SDL_KeyboardEvent *ev)
+{
+ int keycode, v;
+
+ if (ev->keysym.sym == SDLK_PAUSE) {
+ /* specific case */
+ v = 0;
+ if (ev->type == SDL_KEYUP)
+ v |= SCANCODE_UP;
+ kbd_put_keycode(0xe1);
+ kbd_put_keycode(0x1d | v);
+ kbd_put_keycode(0x45 | v);
+ return;
+ }
+
+ if (kbd_layout) {
+ keycode = sdl_keyevent_to_keycode_generic(ev);
+ } else {
+ keycode = sdl_keyevent_to_keycode(ev);
+ }
+
+ switch(keycode) {
+ case 0x00:
+ /* sent when leaving window: reset the modifiers state */
+ reset_keys();
+ return;
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ if (ev->type == SDL_KEYUP)
+ modifiers_state[keycode] = 0;
+ else
+ modifiers_state[keycode] = 1;
+ break;
+#define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION)
+#if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14
+ /* SDL versions before 1.2.14 don't support key up for caps/num lock. */
+ case 0x45: /* num lock */
+ case 0x3a: /* caps lock */
+ /* SDL does not send the key up event, so we generate it */
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ return;
+#endif
+ }
+
+ /* now send the key code */
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ if (ev->type == SDL_KEYUP)
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ else
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+}
+
+static void sdl_update_caption(void)
+{
+ char win_title[1024];
+ char icon_title[1024];
+ const char *status = "";
+
+ if (!vm_running)
+ status = " [Stopped]";
+ else if (gui_grab) {
+ if (alt_grab)
+ status = " - Press Ctrl-Alt-Shift to exit mouse grab";
+ else if (ctrl_grab)
+ status = " - Press Right-Ctrl to exit mouse grab";
+ else
+ status = " - Press Ctrl-Alt to exit mouse grab";
+ }
+
+ if (qemu_name) {
+ snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
+ } else {
+ snprintf(win_title, sizeof(win_title), "QEMU%s", status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU");
+ }
+
+ SDL_WM_SetCaption(win_title, icon_title);
+}
+
+static void sdl_hide_cursor(void)
+{
+ if (!cursor_hide)
+ return;
+
+ if (kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ SDL_SetCursor(sdl_cursor_hidden);
+ } else {
+ SDL_ShowCursor(0);
+ }
+}
+
+static void sdl_show_cursor(void)
+{
+ if (!cursor_hide)
+ return;
+
+ if (!kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ if (guest_cursor &&
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ SDL_SetCursor(guest_sprite);
+ else
+ SDL_SetCursor(sdl_cursor_normal);
+ }
+}
+
+static void sdl_grab_start(void)
+{
+ if (guest_cursor) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ SDL_WarpMouse(guest_x, guest_y);
+ } else
+ sdl_hide_cursor();
+
+ if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
+ gui_grab = 1;
+ sdl_update_caption();
+ } else
+ sdl_show_cursor();
+}
+
+static void sdl_grab_end(void)
+{
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+ gui_grab = 0;
+ sdl_show_cursor();
+ sdl_update_caption();
+}
+
+static void sdl_mouse_mode_change(Notifier *notify)
+{
+ if (kbd_mouse_is_absolute()) {
+ if (!absolute_enabled) {
+ sdl_hide_cursor();
+ if (gui_grab) {
+ sdl_grab_end();
+ }
+ absolute_enabled = 1;
+ }
+ } else if (absolute_enabled) {
+ sdl_show_cursor();
+ absolute_enabled = 0;
+ }
+}
+
+static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
+{
+ int buttons;
+ buttons = 0;
+ if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
+ buttons |= MOUSE_EVENT_LBUTTON;
+ if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
+ buttons |= MOUSE_EVENT_RBUTTON;
+ if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
+ buttons |= MOUSE_EVENT_MBUTTON;
+
+ if (kbd_mouse_is_absolute()) {
+ dx = x * 0x7FFF / (width - 1);
+ dy = y * 0x7FFF / (height - 1);
+ } else if (guest_cursor) {
+ x -= guest_x;
+ y -= guest_y;
+ guest_x += x;
+ guest_y += y;
+ dx = x;
+ dy = y;
+ }
+
+ kbd_mouse_event(dx, dy, dz, buttons);
+}
+
+static void toggle_full_screen(DisplayState *ds)
+{
+ gui_fullscreen = !gui_fullscreen;
+ do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel);
+ if (gui_fullscreen) {
+ scaling_active = 0;
+ gui_saved_grab = gui_grab;
+ sdl_grab_start();
+ } else {
+ if (!gui_saved_grab)
+ sdl_grab_end();
+ }
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static void sdl_refresh(DisplayState *ds)
+{
+ SDL_Event ev1, *ev = &ev1;
+ int mod_state;
+ int buttonstate = SDL_GetMouseState(NULL, NULL);
+
+ if (last_vm_running != vm_running) {
+ last_vm_running = vm_running;
+ sdl_update_caption();
+ }
+
+ vga_hw_update();
+ SDL_EnableUNICODE(!is_graphic_console());
+
+ while (SDL_PollEvent(ev)) {
+ switch (ev->type) {
+ case SDL_VIDEOEXPOSE:
+ sdl_update(ds, 0, 0, real_screen->w, real_screen->h);
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ if (ev->type == SDL_KEYDOWN) {
+ if (alt_grab) {
+ mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
+ (gui_grab_code | KMOD_LSHIFT);
+ } else if (ctrl_grab) {
+ mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
+ } else {
+ mod_state = (SDL_GetModState() & gui_grab_code) ==
+ gui_grab_code;
+ }
+ gui_key_modifier_pressed = mod_state;
+ if (gui_key_modifier_pressed) {
+ int keycode;
+ keycode = sdl_keyevent_to_keycode(&ev->key);
+ switch(keycode) {
+ case 0x21: /* 'f' key on US keyboard */
+ toggle_full_screen(ds);
+ gui_keysym = 1;
+ break;
+ case 0x16: /* 'u' key on US keyboard */
+ scaling_active = 0;
+ sdl_resize(ds);
+ vga_hw_invalidate();
+ vga_hw_update();
+ break;
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ /* Reset the modifiers sent to the current console */
+ reset_keys();
+ console_select(keycode - 0x02);
+ if (!is_graphic_console()) {
+ /* display grab if going to a text console */
+ if (gui_grab)
+ sdl_grab_end();
+ }
+ gui_keysym = 1;
+ break;
+ default:
+ break;
+ }
+ } else if (!is_graphic_console()) {
+ int keysym;
+ keysym = 0;
+ if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
+ switch(ev->key.keysym.sym) {
+ case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
+ case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
+ case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
+ case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
+ case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
+ case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
+ case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
+ case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
+ default: break;
+ }
+ } else {
+ switch(ev->key.keysym.sym) {
+ case SDLK_UP: keysym = QEMU_KEY_UP; break;
+ case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
+ case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
+ case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
+ case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
+ case SDLK_END: keysym = QEMU_KEY_END; break;
+ case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
+ case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
+ case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;
+ case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
+ default: break;
+ }
+ }
+ if (keysym) {
+ kbd_put_keysym(keysym);
+ } else if (ev->key.keysym.unicode != 0) {
+ kbd_put_keysym(ev->key.keysym.unicode);
+ }
+ }
+ } else if (ev->type == SDL_KEYUP) {
+ if (!alt_grab) {
+ mod_state = (ev->key.keysym.mod & gui_grab_code);
+ } else {
+ mod_state = (ev->key.keysym.mod &
+ (gui_grab_code | KMOD_LSHIFT));
+ }
+ if (!mod_state) {
+ if (gui_key_modifier_pressed) {
+ gui_key_modifier_pressed = 0;
+ if (gui_keysym == 0) {
+ /* exit/enter grab if pressing Ctrl-Alt */
+ if (!gui_grab) {
+ /* if the application is not active,
+ do not try to enter grab state. It
+ prevents
+ 'SDL_WM_GrabInput(SDL_GRAB_ON)'
+ from blocking all the application
+ (SDL bug). */
+ if (SDL_GetAppState() & SDL_APPACTIVE)
+ sdl_grab_start();
+ } else {
+ sdl_grab_end();
+ }
+ /* SDL does not send back all the
+ modifiers key, so we must correct it */
+ reset_keys();
+ break;
+ }
+ gui_keysym = 0;
+ }
+ }
+ }
+ if (is_graphic_console() && !gui_keysym)
+ sdl_process_key(&ev->key);
+ break;
+ case SDL_QUIT:
+ if (!no_quit)
+ qemu_system_shutdown_request();
+ break;
+ case SDL_MOUSEMOTION:
+ if (gui_grab || kbd_mouse_is_absolute() ||
+ absolute_enabled) {
+ sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
+ ev->motion.x, ev->motion.y, ev->motion.state);
+ }
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ {
+ SDL_MouseButtonEvent *bev = &ev->button;
+ if (!gui_grab && !kbd_mouse_is_absolute()) {
+ if (ev->type == SDL_MOUSEBUTTONDOWN &&
+ (bev->button == SDL_BUTTON_LEFT)) {
+ /* start grabbing all events */
+ sdl_grab_start();
+ }
+ } else {
+ int dz;
+ dz = 0;
+ if (ev->type == SDL_MOUSEBUTTONDOWN) {
+ buttonstate |= SDL_BUTTON(bev->button);
+ } else {
+ buttonstate &= ~SDL_BUTTON(bev->button);
+ }
+#ifdef SDL_BUTTON_WHEELUP
+ if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = -1;
+ } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = 1;
+ }
+#endif
+ sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
+ }
+ }
+ break;
+ case SDL_ACTIVEEVENT:
+ if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
+ !ev->active.gain && !gui_fullscreen_initial_grab) {
+ sdl_grab_end();
+ }
+ if (ev->active.state & SDL_APPACTIVE) {
+ if (ev->active.gain) {
+ /* Back to default interval */
+ dcl->gui_timer_interval = 0;
+ dcl->idle = 0;
+ } else {
+ /* Sleeping interval */
+ dcl->gui_timer_interval = 500;
+ dcl->idle = 1;
+ }
+ }
+ break;
+ case SDL_VIDEORESIZE:
+ {
+ SDL_ResizeEvent *rev = &ev->resize;
+ int bpp = real_screen->format->BitsPerPixel;
+ if (bpp != 16 && bpp != 32)
+ bpp = 32;
+ do_sdl_resize(rev->w, rev->h, bpp);
+ scaling_active = 1;
+ if (!is_buffer_shared(ds->surface)) {
+ ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds));
+ dpy_resize(ds);
+ }
+ vga_hw_invalidate();
+ vga_hw_update();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
+{
+ SDL_Rect dst = { x, y, w, h };
+ SDL_FillRect(real_screen, &dst, c);
+}
+
+static void sdl_mouse_warp(int x, int y, int on)
+{
+ if (on) {
+ if (!guest_cursor)
+ sdl_show_cursor();
+ if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ SDL_WarpMouse(x, y);
+ }
+ } else if (gui_grab)
+ sdl_hide_cursor();
+ guest_cursor = on;
+ guest_x = x, guest_y = y;
+}
+
+static void sdl_mouse_define(QEMUCursor *c)
+{
+ uint8_t *image, *mask;
+ int bpl;
+
+ if (guest_sprite)
+ SDL_FreeCursor(guest_sprite);
+
+ bpl = cursor_get_mono_bpl(c);
+ image = qemu_mallocz(bpl * c->height);
+ mask = qemu_mallocz(bpl * c->height);
+ cursor_get_mono_image(c, 0x000000, image);
+ cursor_get_mono_mask(c, 0, mask);
+ guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
+ c->hot_x, c->hot_y);
+ qemu_free(image);
+ qemu_free(mask);
+
+ if (guest_cursor &&
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ SDL_SetCursor(guest_sprite);
+}
+
+static void sdl_cleanup(void)
+{
+ if (guest_sprite)
+ SDL_FreeCursor(guest_sprite);
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
+{
+ int flags;
+ uint8_t data = 0;
+ DisplayAllocator *da;
+ const SDL_VideoInfo *vi;
+
+#if defined(__APPLE__)
+ /* always use generic keymaps */
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+#endif
+ if(keyboard_layout) {
+ kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ if (!kbd_layout)
+ exit(1);
+ }
+
+ if (no_frame)
+ gui_noframe = 1;
+
+ if (!full_screen) {
+ setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
+ }
+
+ /* Enable normal up/down events for Caps-Lock and Num-Lock keys.
+ * This requires SDL >= 1.2.14. */
+ setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
+
+ flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+ if (SDL_Init (flags)) {
+ fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
+ SDL_GetError());
+ exit(1);
+ }
+ vi = SDL_GetVideoInfo();
+ host_format = *(vi->vfmt);
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = sdl_update;
+ dcl->dpy_resize = sdl_resize;
+ dcl->dpy_refresh = sdl_refresh;
+ dcl->dpy_setdata = sdl_setdata;
+ dcl->dpy_fill = sdl_fill;
+ ds->mouse_set = sdl_mouse_warp;
+ ds->cursor_define = sdl_mouse_define;
+ register_displaychangelistener(ds, dcl);
+
+ da = qemu_mallocz(sizeof(DisplayAllocator));
+ da->create_displaysurface = sdl_create_displaysurface;
+ da->resize_displaysurface = sdl_resize_displaysurface;
+ da->free_displaysurface = sdl_free_displaysurface;
+ if (register_displayallocator(ds, da) == da) {
+ dpy_resize(ds);
+ }
+
+ mouse_mode_notifier.notify = sdl_mouse_mode_change;
+ qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
+
+ sdl_update_caption();
+ SDL_EnableKeyRepeat(250, 50);
+ gui_grab = 0;
+
+ sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+ sdl_cursor_normal = SDL_GetCursor();
+
+ atexit(sdl_cleanup);
+ if (full_screen) {
+ gui_fullscreen = 1;
+ gui_fullscreen_initial_grab = 1;
+ sdl_grab_start();
+ }
+}
diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h
new file mode 100644
index 0000000..ee90480
--- /dev/null
+++ b/ui/sdl_keysym.h
@@ -0,0 +1,277 @@
+
+#include "keymaps.h"
+
+static const name2keysym_t name2keysym[]={
+/* ascii */
+ { "space", 0x020},
+ { "exclam", 0x021},
+ { "quotedbl", 0x022},
+ { "numbersign", 0x023},
+ { "dollar", 0x024},
+ { "percent", 0x025},
+ { "ampersand", 0x026},
+ { "apostrophe", 0x027},
+ { "parenleft", 0x028},
+ { "parenright", 0x029},
+ { "asterisk", 0x02a},
+ { "plus", 0x02b},
+ { "comma", 0x02c},
+ { "minus", 0x02d},
+ { "period", 0x02e},
+ { "slash", 0x02f},
+ { "0", 0x030},
+ { "1", 0x031},
+ { "2", 0x032},
+ { "3", 0x033},
+ { "4", 0x034},
+ { "5", 0x035},
+ { "6", 0x036},
+ { "7", 0x037},
+ { "8", 0x038},
+ { "9", 0x039},
+ { "colon", 0x03a},
+ { "semicolon", 0x03b},
+ { "less", 0x03c},
+ { "equal", 0x03d},
+ { "greater", 0x03e},
+ { "question", 0x03f},
+ { "at", 0x040},
+ { "A", 0x041},
+ { "B", 0x042},
+ { "C", 0x043},
+ { "D", 0x044},
+ { "E", 0x045},
+ { "F", 0x046},
+ { "G", 0x047},
+ { "H", 0x048},
+ { "I", 0x049},
+ { "J", 0x04a},
+ { "K", 0x04b},
+ { "L", 0x04c},
+ { "M", 0x04d},
+ { "N", 0x04e},
+ { "O", 0x04f},
+ { "P", 0x050},
+ { "Q", 0x051},
+ { "R", 0x052},
+ { "S", 0x053},
+ { "T", 0x054},
+ { "U", 0x055},
+ { "V", 0x056},
+ { "W", 0x057},
+ { "X", 0x058},
+ { "Y", 0x059},
+ { "Z", 0x05a},
+ { "bracketleft", 0x05b},
+ { "backslash", 0x05c},
+ { "bracketright", 0x05d},
+ { "asciicircum", 0x05e},
+ { "underscore", 0x05f},
+ { "grave", 0x060},
+ { "a", 0x061},
+ { "b", 0x062},
+ { "c", 0x063},
+ { "d", 0x064},
+ { "e", 0x065},
+ { "f", 0x066},
+ { "g", 0x067},
+ { "h", 0x068},
+ { "i", 0x069},
+ { "j", 0x06a},
+ { "k", 0x06b},
+ { "l", 0x06c},
+ { "m", 0x06d},
+ { "n", 0x06e},
+ { "o", 0x06f},
+ { "p", 0x070},
+ { "q", 0x071},
+ { "r", 0x072},
+ { "s", 0x073},
+ { "t", 0x074},
+ { "u", 0x075},
+ { "v", 0x076},
+ { "w", 0x077},
+ { "x", 0x078},
+ { "y", 0x079},
+ { "z", 0x07a},
+ { "braceleft", 0x07b},
+ { "bar", 0x07c},
+ { "braceright", 0x07d},
+ { "asciitilde", 0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace", 0x0a0},
+{ "exclamdown", 0x0a1},
+{ "cent", 0x0a2},
+{ "sterling", 0x0a3},
+{ "currency", 0x0a4},
+{ "yen", 0x0a5},
+{ "brokenbar", 0x0a6},
+{ "section", 0x0a7},
+{ "diaeresis", 0x0a8},
+{ "copyright", 0x0a9},
+{ "ordfeminine", 0x0aa},
+{ "guillemotleft", 0x0ab},
+{ "notsign", 0x0ac},
+{ "hyphen", 0x0ad},
+{ "registered", 0x0ae},
+{ "macron", 0x0af},
+{ "degree", 0x0b0},
+{ "plusminus", 0x0b1},
+{ "twosuperior", 0x0b2},
+{ "threesuperior", 0x0b3},
+{ "acute", 0x0b4},
+{ "mu", 0x0b5},
+{ "paragraph", 0x0b6},
+{ "periodcentered", 0x0b7},
+{ "cedilla", 0x0b8},
+{ "onesuperior", 0x0b9},
+{ "masculine", 0x0ba},
+{ "guillemotright", 0x0bb},
+{ "onequarter", 0x0bc},
+{ "onehalf", 0x0bd},
+{ "threequarters", 0x0be},
+{ "questiondown", 0x0bf},
+{ "Agrave", 0x0c0},
+{ "Aacute", 0x0c1},
+{ "Acircumflex", 0x0c2},
+{ "Atilde", 0x0c3},
+{ "Adiaeresis", 0x0c4},
+{ "Aring", 0x0c5},
+{ "AE", 0x0c6},
+{ "Ccedilla", 0x0c7},
+{ "Egrave", 0x0c8},
+{ "Eacute", 0x0c9},
+{ "Ecircumflex", 0x0ca},
+{ "Ediaeresis", 0x0cb},
+{ "Igrave", 0x0cc},
+{ "Iacute", 0x0cd},
+{ "Icircumflex", 0x0ce},
+{ "Idiaeresis", 0x0cf},
+{ "ETH", 0x0d0},
+{ "Eth", 0x0d0},
+{ "Ntilde", 0x0d1},
+{ "Ograve", 0x0d2},
+{ "Oacute", 0x0d3},
+{ "Ocircumflex", 0x0d4},
+{ "Otilde", 0x0d5},
+{ "Odiaeresis", 0x0d6},
+{ "multiply", 0x0d7},
+{ "Ooblique", 0x0d8},
+{ "Oslash", 0x0d8},
+{ "Ugrave", 0x0d9},
+{ "Uacute", 0x0da},
+{ "Ucircumflex", 0x0db},
+{ "Udiaeresis", 0x0dc},
+{ "Yacute", 0x0dd},
+{ "THORN", 0x0de},
+{ "Thorn", 0x0de},
+{ "ssharp", 0x0df},
+{ "agrave", 0x0e0},
+{ "aacute", 0x0e1},
+{ "acircumflex", 0x0e2},
+{ "atilde", 0x0e3},
+{ "adiaeresis", 0x0e4},
+{ "aring", 0x0e5},
+{ "ae", 0x0e6},
+{ "ccedilla", 0x0e7},
+{ "egrave", 0x0e8},
+{ "eacute", 0x0e9},
+{ "ecircumflex", 0x0ea},
+{ "ediaeresis", 0x0eb},
+{ "igrave", 0x0ec},
+{ "iacute", 0x0ed},
+{ "icircumflex", 0x0ee},
+{ "idiaeresis", 0x0ef},
+{ "eth", 0x0f0},
+{ "ntilde", 0x0f1},
+{ "ograve", 0x0f2},
+{ "oacute", 0x0f3},
+{ "ocircumflex", 0x0f4},
+{ "otilde", 0x0f5},
+{ "odiaeresis", 0x0f6},
+{ "division", 0x0f7},
+{ "oslash", 0x0f8},
+{ "ooblique", 0x0f8},
+{ "ugrave", 0x0f9},
+{ "uacute", 0x0fa},
+{ "ucircumflex", 0x0fb},
+{ "udiaeresis", 0x0fc},
+{ "yacute", 0x0fd},
+{ "thorn", 0x0fe},
+{ "ydiaeresis", 0x0ff},
+{"EuroSign", SDLK_EURO},
+
+ /* modifiers */
+{"Control_L", SDLK_LCTRL},
+{"Control_R", SDLK_RCTRL},
+{"Alt_L", SDLK_LALT},
+{"Alt_R", SDLK_RALT},
+{"Caps_Lock", SDLK_CAPSLOCK},
+{"Meta_L", SDLK_LMETA},
+{"Meta_R", SDLK_RMETA},
+{"Shift_L", SDLK_LSHIFT},
+{"Shift_R", SDLK_RSHIFT},
+{"Super_L", SDLK_LSUPER},
+{"Super_R", SDLK_RSUPER},
+
+ /* special keys */
+{"BackSpace", SDLK_BACKSPACE},
+{"Tab", SDLK_TAB},
+{"Return", SDLK_RETURN},
+{"Right", SDLK_RIGHT},
+{"Left", SDLK_LEFT},
+{"Up", SDLK_UP},
+{"Down", SDLK_DOWN},
+{"Page_Down", SDLK_PAGEDOWN},
+{"Page_Up", SDLK_PAGEUP},
+{"Insert", SDLK_INSERT},
+{"Delete", SDLK_DELETE},
+{"Home", SDLK_HOME},
+{"End", SDLK_END},
+{"Scroll_Lock", SDLK_SCROLLOCK},
+{"F1", SDLK_F1},
+{"F2", SDLK_F2},
+{"F3", SDLK_F3},
+{"F4", SDLK_F4},
+{"F5", SDLK_F5},
+{"F6", SDLK_F6},
+{"F7", SDLK_F7},
+{"F8", SDLK_F8},
+{"F9", SDLK_F9},
+{"F10", SDLK_F10},
+{"F11", SDLK_F11},
+{"F12", SDLK_F12},
+{"F13", SDLK_F13},
+{"F14", SDLK_F14},
+{"F15", SDLK_F15},
+{"Sys_Req", SDLK_SYSREQ},
+{"KP_0", SDLK_KP0},
+{"KP_1", SDLK_KP1},
+{"KP_2", SDLK_KP2},
+{"KP_3", SDLK_KP3},
+{"KP_4", SDLK_KP4},
+{"KP_5", SDLK_KP5},
+{"KP_6", SDLK_KP6},
+{"KP_7", SDLK_KP7},
+{"KP_8", SDLK_KP8},
+{"KP_9", SDLK_KP9},
+{"KP_Add", SDLK_KP_PLUS},
+{"KP_Decimal", SDLK_KP_PERIOD},
+{"KP_Divide", SDLK_KP_DIVIDE},
+{"KP_Enter", SDLK_KP_ENTER},
+{"KP_Equal", SDLK_KP_EQUALS},
+{"KP_Multiply", SDLK_KP_MULTIPLY},
+{"KP_Subtract", SDLK_KP_MINUS},
+{"help", SDLK_HELP},
+{"Menu", SDLK_MENU},
+{"Power", SDLK_POWER},
+{"Print", SDLK_PRINT},
+{"Mode_switch", SDLK_MODE},
+{"Multi_Key", SDLK_COMPOSE},
+{"Num_Lock", SDLK_NUMLOCK},
+{"Pause", SDLK_PAUSE},
+{"Escape", SDLK_ESCAPE},
+
+{NULL, 0},
+};
diff --git a/ui/sdl_zoom.c b/ui/sdl_zoom.c
new file mode 100644
index 0000000..a986c7c
--- /dev/null
+++ b/ui/sdl_zoom.c
@@ -0,0 +1,95 @@
+/*
+ * SDL_zoom - surface scaling
+ *
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library.
+ * Modifications by Stefano Stabellini.
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sdl_zoom.h"
+#include "osdep.h"
+#include <stdint.h>
+#include <stdio.h>
+
+static int sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth,
+ SDL_Rect *dst_rect);
+static int sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth,
+ SDL_Rect *dst_rect);
+
+#define BPP 32
+#include "sdl_zoom_template.h"
+#undef BPP
+#define BPP 16
+#include "sdl_zoom_template.h"
+#undef BPP
+
+int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, int smooth,
+ SDL_Rect *in_rect)
+{
+ SDL_Rect zoom, src_rect;
+ int extra;
+
+ /* Grow the size of the modified rectangle to avoid edge artefacts */
+ src_rect.x = (in_rect->x > 0) ? (in_rect->x - 1) : 0;
+ src_rect.y = (in_rect->y > 0) ? (in_rect->y - 1) : 0;
+
+ src_rect.w = in_rect->w + 1;
+ if (src_rect.x + src_rect.w > src_sfc->w)
+ src_rect.w = src_sfc->w - src_rect.x;
+
+ src_rect.h = in_rect->h + 1;
+ if (src_rect.y + src_rect.h > src_sfc->h)
+ src_rect.h = src_sfc->h - src_rect.y;
+
+ /* (x,y) : round down */
+ zoom.x = (int)(((float)(src_rect.x * dst_sfc->w)) / (float)(src_sfc->w));
+ zoom.y = (int)(((float)(src_rect.y * dst_sfc->h)) / (float)(src_sfc->h));
+
+ /* (w,h) : round up */
+ zoom.w = (int)( ((double)((src_rect.w * dst_sfc->w) + (src_sfc->w - 1))) /
+ (double)(src_sfc->w));
+
+ zoom.h = (int)( ((double)((src_rect.h * dst_sfc->h) + (src_sfc->h - 1))) /
+ (double)(src_sfc->h));
+
+ /* Account for any (x,y) rounding by adding one-source-pixel's worth
+ * of destination pixels and then edge checking.
+ */
+
+ extra = ((dst_sfc->w-1) / src_sfc->w) + 1;
+
+ if ((zoom.x + zoom.w) < (dst_sfc->w - extra))
+ zoom.w += extra;
+ else
+ zoom.w = dst_sfc->w - zoom.x;
+
+ extra = ((dst_sfc->h-1) / src_sfc->h) + 1;
+
+ if ((zoom.y + zoom.h) < (dst_sfc->h - extra))
+ zoom.h += extra;
+ else
+ zoom.h = dst_sfc->h - zoom.y;
+
+ /* The rectangle (zoom.x, zoom.y, zoom.w, zoom.h) is the area on the
+ * destination surface that needs to be updated.
+ */
+ if (src_sfc->format->BitsPerPixel == 32)
+ sdl_zoom_rgb32(src_sfc, dst_sfc, smooth, &zoom);
+ else if (src_sfc->format->BitsPerPixel == 16)
+ sdl_zoom_rgb16(src_sfc, dst_sfc, smooth, &zoom);
+ else {
+ fprintf(stderr, "pixel format not supported\n");
+ return -1;
+ }
+
+ /* Return the rectangle of the update to the caller */
+ *in_rect = zoom;
+
+ return 0;
+}
+
diff --git a/ui/sdl_zoom.h b/ui/sdl_zoom.h
new file mode 100644
index 0000000..74955bc
--- /dev/null
+++ b/ui/sdl_zoom.h
@@ -0,0 +1,25 @@
+/*
+ * SDL_zoom - surface scaling
+ *
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library.
+ * Modifications by Stefano Stabellini.
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef SDL_zoom_h
+#define SDL_zoom_h
+
+#include <SDL.h>
+
+#define SMOOTHING_OFF 0
+#define SMOOTHING_ON 1
+
+int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc,
+ int smooth, SDL_Rect *src_rect);
+
+#endif /* SDL_zoom_h */
diff --git a/ui/sdl_zoom_template.h b/ui/sdl_zoom_template.h
new file mode 100644
index 0000000..64bbca8
--- /dev/null
+++ b/ui/sdl_zoom_template.h
@@ -0,0 +1,225 @@
+/*
+ * SDL_zoom_template - surface scaling
+ *
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library.
+ * Modifications by Stefano Stabellini.
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#if BPP == 16
+#define SDL_TYPE Uint16
+#elif BPP == 32
+#define SDL_TYPE Uint32
+#else
+#error unsupport depth
+#endif
+
+/*
+ * Simple helper functions to make the code looks nicer
+ *
+ * Assume spf = source SDL_PixelFormat
+ * dpf = dest SDL_PixelFormat
+ *
+ */
+#define getRed(color) (((color) & spf->Rmask) >> spf->Rshift)
+#define getGreen(color) (((color) & spf->Gmask) >> spf->Gshift)
+#define getBlue(color) (((color) & spf->Bmask) >> spf->Bshift)
+#define getAlpha(color) (((color) & spf->Amask) >> spf->Ashift)
+
+#define setRed(r, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Rmask))) + \
+ (((r) & (dpf->Rmask >> dpf->Rshift)) << dpf->Rshift); \
+} while (0);
+
+#define setGreen(g, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Gmask))) + \
+ (((g) & (dpf->Gmask >> dpf->Gshift)) << dpf->Gshift); \
+} while (0);
+
+#define setBlue(b, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Bmask))) + \
+ (((b) & (dpf->Bmask >> dpf->Bshift)) << dpf->Bshift); \
+} while (0);
+
+#define setAlpha(a, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Amask))) + \
+ (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \
+} while (0);
+
+static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth,
+ SDL_Rect *dst_rect)
+{
+ int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump;
+ SDL_TYPE *c00, *c01, *c10, *c11, *sp, *csp, *dp;
+ int d_gap;
+ SDL_PixelFormat *spf = src->format;
+ SDL_PixelFormat *dpf = dst->format;
+
+ if (smooth) {
+ /* For interpolation: assume source dimension is one pixel.
+ * Smaller here to avoid overflow on right and bottom edge.
+ */
+ sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
+ sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
+ } else {
+ sx = (int) (65536.0 * (float) src->w / (float) dst->w);
+ sy = (int) (65536.0 * (float) src->h / (float) dst->h);
+ }
+
+ if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
+ return (-1);
+ }
+ if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
+ free(sax);
+ return (-1);
+ }
+
+ sp = csp = (SDL_TYPE *) src->pixels;
+ dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch +
+ dst_rect->x * dst->format->BytesPerPixel);
+
+ csx = 0;
+ csax = sax;
+ for (x = 0; x <= dst->w; x++) {
+ *csax = csx;
+ csax++;
+ csx &= 0xffff;
+ csx += sx;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y <= dst->h; y++) {
+ *csay = csy;
+ csay++;
+ csy &= 0xffff;
+ csy += sy;
+ }
+
+ d_gap = dst->pitch - dst_rect->w * dst->format->BytesPerPixel;
+
+ if (smooth) {
+ csay = say;
+ for (y = 0; y < dst_rect->y; y++) {
+ csay++;
+ sstep = (*csay >> 16) * src->pitch;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + sstep);
+ }
+
+ /* Calculate sstep_jump */
+ csax = sax;
+ sstep_jump = 0;
+ for (x = 0; x < dst_rect->x; x++) {
+ csax++;
+ sstep = (*csax >> 16);
+ sstep_jump += sstep;
+ }
+
+ for (y = 0; y < dst_rect->h ; y++) {
+ /* Setup colour source pointers */
+ c00 = csp + sstep_jump;
+ c01 = c00 + 1;
+ c10 = (SDL_TYPE *) ((Uint8 *) csp + src->pitch) + sstep_jump;
+ c11 = c10 + 1;
+ csax = sax + dst_rect->x;
+
+ for (x = 0; x < dst_rect->w; x++) {
+
+ /* Interpolate colours */
+ ex = (*csax & 0xffff);
+ ey = (*csay & 0xffff);
+ t1 = ((((getRed(*c01) - getRed(*c00)) * ex) >> 16) +
+ getRed(*c00)) & (dpf->Rmask >> dpf->Rshift);
+ t2 = ((((getRed(*c11) - getRed(*c10)) * ex) >> 16) +
+ getRed(*c10)) & (dpf->Rmask >> dpf->Rshift);
+ setRed((((t2 - t1) * ey) >> 16) + t1, dp);
+ t1 = ((((getGreen(*c01) - getGreen(*c00)) * ex) >> 16) +
+ getGreen(*c00)) & (dpf->Gmask >> dpf->Gshift);
+ t2 = ((((getGreen(*c11) - getGreen(*c10)) * ex) >> 16) +
+ getGreen(*c10)) & (dpf->Gmask >> dpf->Gshift);
+ setGreen((((t2 - t1) * ey) >> 16) + t1, dp);
+ t1 = ((((getBlue(*c01) - getBlue(*c00)) * ex) >> 16) +
+ getBlue(*c00)) & (dpf->Bmask >> dpf->Bshift);
+ t2 = ((((getBlue(*c11) - getBlue(*c10)) * ex) >> 16) +
+ getBlue(*c10)) & (dpf->Bmask >> dpf->Bshift);
+ setBlue((((t2 - t1) * ey) >> 16) + t1, dp);
+ t1 = ((((getAlpha(*c01) - getAlpha(*c00)) * ex) >> 16) +
+ getAlpha(*c00)) & (dpf->Amask >> dpf->Ashift);
+ t2 = ((((getAlpha(*c11) - getAlpha(*c10)) * ex) >> 16) +
+ getAlpha(*c10)) & (dpf->Amask >> dpf->Ashift);
+ setAlpha((((t2 - t1) * ey) >> 16) + t1, dp);
+
+ /* Advance source pointers */
+ csax++;
+ sstep = (*csax >> 16);
+ c00 += sstep;
+ c01 += sstep;
+ c10 += sstep;
+ c11 += sstep;
+ /* Advance destination pointer */
+ dp++;
+ }
+ /* Advance source pointer */
+ csay++;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+ /* Advance destination pointers */
+ dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap);
+ }
+
+
+ } else {
+ csay = say;
+
+ for (y = 0; y < dst_rect->y; y++) {
+ csay++;
+ sstep = (*csay >> 16) * src->pitch;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + sstep);
+ }
+
+ /* Calculate sstep_jump */
+ csax = sax;
+ sstep_jump = 0;
+ for (x = 0; x < dst_rect->x; x++) {
+ csax++;
+ sstep = (*csax >> 16);
+ sstep_jump += sstep;
+ }
+
+ for (y = 0 ; y < dst_rect->h ; y++) {
+ sp = csp + sstep_jump;
+ csax = sax + dst_rect->x;
+
+ for (x = 0; x < dst_rect->w; x++) {
+
+ /* Draw */
+ *dp = *sp;
+
+ /* Advance source pointers */
+ csax++;
+ sstep = (*csax >> 16);
+ sp += sstep;
+
+ /* Advance destination pointer */
+ dp++;
+ }
+ /* Advance source pointers */
+ csay++;
+ sstep = (*csay >> 16) * src->pitch;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + sstep);
+
+ /* Advance destination pointer */
+ dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap);
+ }
+ }
+
+ free(sax);
+ free(say);
+ return (0);
+}
+
+#undef SDL_TYPE
+
diff --git a/ui/spice-core.c b/ui/spice-core.c
new file mode 100644
index 0000000..1aa1a5e
--- /dev/null
+++ b/ui/spice-core.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <spice.h>
+#include <spice-experimental.h>
+
+#include <netdb.h>
+
+#include "qemu-common.h"
+#include "qemu-spice.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "qemu-x509.h"
+#include "qemu_socket.h"
+#include "qint.h"
+#include "qbool.h"
+#include "qstring.h"
+#include "qjson.h"
+#include "notify.h"
+#include "migration.h"
+#include "monitor.h"
+#include "hw/hw.h"
+
+/* core bits */
+
+static SpiceServer *spice_server;
+static Notifier migration_state;
+static const char *auth = "spice";
+static char *auth_passwd;
+static time_t auth_expires = TIME_MAX;
+int using_spice = 0;
+
+struct SpiceTimer {
+ QEMUTimer *timer;
+ QTAILQ_ENTRY(SpiceTimer) next;
+};
+static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
+
+static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
+{
+ SpiceTimer *timer;
+
+ timer = qemu_mallocz(sizeof(*timer));
+ timer->timer = qemu_new_timer(rt_clock, func, opaque);
+ QTAILQ_INSERT_TAIL(&timers, timer, next);
+ return timer;
+}
+
+static void timer_start(SpiceTimer *timer, uint32_t ms)
+{
+ qemu_mod_timer(timer->timer, qemu_get_clock(rt_clock) + ms);
+}
+
+static void timer_cancel(SpiceTimer *timer)
+{
+ qemu_del_timer(timer->timer);
+}
+
+static void timer_remove(SpiceTimer *timer)
+{
+ qemu_del_timer(timer->timer);
+ qemu_free_timer(timer->timer);
+ QTAILQ_REMOVE(&timers, timer, next);
+ qemu_free(timer);
+}
+
+struct SpiceWatch {
+ int fd;
+ int event_mask;
+ SpiceWatchFunc func;
+ void *opaque;
+ QTAILQ_ENTRY(SpiceWatch) next;
+};
+static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
+
+static void watch_read(void *opaque)
+{
+ SpiceWatch *watch = opaque;
+ watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
+}
+
+static void watch_write(void *opaque)
+{
+ SpiceWatch *watch = opaque;
+ watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
+}
+
+static void watch_update_mask(SpiceWatch *watch, int event_mask)
+{
+ IOHandler *on_read = NULL;
+ IOHandler *on_write = NULL;
+
+ watch->event_mask = event_mask;
+ if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
+ on_read = watch_read;
+ }
+ if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
+ on_write = watch_write;
+ }
+ qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
+}
+
+static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
+{
+ SpiceWatch *watch;
+
+ watch = qemu_mallocz(sizeof(*watch));
+ watch->fd = fd;
+ watch->func = func;
+ watch->opaque = opaque;
+ QTAILQ_INSERT_TAIL(&watches, watch, next);
+
+ watch_update_mask(watch, event_mask);
+ return watch;
+}
+
+static void watch_remove(SpiceWatch *watch)
+{
+ watch_update_mask(watch, 0);
+ QTAILQ_REMOVE(&watches, watch, next);
+ qemu_free(watch);
+}
+
+#if SPICE_INTERFACE_CORE_MINOR >= 3
+
+typedef struct ChannelList ChannelList;
+struct ChannelList {
+ SpiceChannelEventInfo *info;
+ QTAILQ_ENTRY(ChannelList) link;
+};
+static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
+
+static void channel_list_add(SpiceChannelEventInfo *info)
+{
+ ChannelList *item;
+
+ item = qemu_mallocz(sizeof(*item));
+ item->info = info;
+ QTAILQ_INSERT_TAIL(&channel_list, item, link);
+}
+
+static void channel_list_del(SpiceChannelEventInfo *info)
+{
+ ChannelList *item;
+
+ QTAILQ_FOREACH(item, &channel_list, link) {
+ if (item->info != info) {
+ continue;
+ }
+ QTAILQ_REMOVE(&channel_list, item, link);
+ qemu_free(item);
+ return;
+ }
+}
+
+static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
+{
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ const char *family;
+
+ getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ family = inet_strfamily(addr->sa_family);
+
+ qdict_put(dict, "host", qstring_from_str(host));
+ qdict_put(dict, "port", qstring_from_str(port));
+ qdict_put(dict, "family", qstring_from_str(family));
+}
+
+static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
+{
+ int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
+
+ qdict_put(dict, "connection-id", qint_from_int(info->connection_id));
+ qdict_put(dict, "channel-type", qint_from_int(info->type));
+ qdict_put(dict, "channel-id", qint_from_int(info->id));
+ qdict_put(dict, "tls", qbool_from_int(tls));
+}
+
+static QList *channel_list_get(void)
+{
+ ChannelList *item;
+ QList *list;
+ QDict *dict;
+
+ list = qlist_new();
+ QTAILQ_FOREACH(item, &channel_list, link) {
+ dict = qdict_new();
+ add_addr_info(dict, &item->info->paddr, item->info->plen);
+ add_channel_info(dict, item->info);
+ qlist_append(list, dict);
+ }
+ return list;
+}
+
+static void channel_event(int event, SpiceChannelEventInfo *info)
+{
+ static const int qevent[] = {
+ [ SPICE_CHANNEL_EVENT_CONNECTED ] = QEVENT_SPICE_CONNECTED,
+ [ SPICE_CHANNEL_EVENT_INITIALIZED ] = QEVENT_SPICE_INITIALIZED,
+ [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED,
+ };
+ QDict *server, *client;
+ QObject *data;
+
+ client = qdict_new();
+ add_addr_info(client, &info->paddr, info->plen);
+
+ server = qdict_new();
+ add_addr_info(server, &info->laddr, info->llen);
+
+ if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
+ qdict_put(server, "auth", qstring_from_str(auth));
+ add_channel_info(client, info);
+ channel_list_add(info);
+ }
+ if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
+ channel_list_del(info);
+ }
+
+ data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
+ QOBJECT(client), QOBJECT(server));
+ monitor_protocol_event(qevent[event], data);
+ qobject_decref(data);
+}
+
+#else /* SPICE_INTERFACE_CORE_MINOR >= 3 */
+
+static QList *channel_list_get(void)
+{
+ return NULL;
+}
+
+#endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */
+
+static SpiceCoreInterface core_interface = {
+ .base.type = SPICE_INTERFACE_CORE,
+ .base.description = "qemu core services",
+ .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
+
+ .timer_add = timer_add,
+ .timer_start = timer_start,
+ .timer_cancel = timer_cancel,
+ .timer_remove = timer_remove,
+
+ .watch_add = watch_add,
+ .watch_update_mask = watch_update_mask,
+ .watch_remove = watch_remove,
+
+#if SPICE_INTERFACE_CORE_MINOR >= 3
+ .channel_event = channel_event,
+#endif
+};
+
+/* config string parsing */
+
+static int name2enum(const char *string, const char *table[], int entries)
+{
+ int i;
+
+ if (string) {
+ for (i = 0; i < entries; i++) {
+ if (!table[i]) {
+ continue;
+ }
+ if (strcmp(string, table[i]) != 0) {
+ continue;
+ }
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int parse_name(const char *string, const char *optname,
+ const char *table[], int entries)
+{
+ int value = name2enum(string, table, entries);
+
+ if (value != -1) {
+ return value;
+ }
+ fprintf(stderr, "spice: invalid %s: %s\n", optname, string);
+ exit(1);
+}
+
+#if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */
+
+static const char *stream_video_names[] = {
+ [ SPICE_STREAM_VIDEO_OFF ] = "off",
+ [ SPICE_STREAM_VIDEO_ALL ] = "all",
+ [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
+};
+#define parse_stream_video(_name) \
+ name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names))
+
+#endif /* >= 0.6.0 */
+
+static const char *compression_names[] = {
+ [ SPICE_IMAGE_COMPRESS_OFF ] = "off",
+ [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
+ [ SPICE_IMAGE_COMPRESS_AUTO_LZ ] = "auto_lz",
+ [ SPICE_IMAGE_COMPRESS_QUIC ] = "quic",
+ [ SPICE_IMAGE_COMPRESS_GLZ ] = "glz",
+ [ SPICE_IMAGE_COMPRESS_LZ ] = "lz",
+};
+#define parse_compression(_name) \
+ parse_name(_name, "image compression", \
+ compression_names, ARRAY_SIZE(compression_names))
+
+static const char *wan_compression_names[] = {
+ [ SPICE_WAN_COMPRESSION_AUTO ] = "auto",
+ [ SPICE_WAN_COMPRESSION_NEVER ] = "never",
+ [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
+};
+#define parse_wan_compression(_name) \
+ parse_name(_name, "wan compression", \
+ wan_compression_names, ARRAY_SIZE(wan_compression_names))
+
+/* functions for the rest of qemu */
+
+static void info_spice_iter(QObject *obj, void *opaque)
+{
+ QDict *client;
+ Monitor *mon = opaque;
+
+ client = qobject_to_qdict(obj);
+ monitor_printf(mon, "Channel:\n");
+ monitor_printf(mon, " address: %s:%s%s\n",
+ qdict_get_str(client, "host"),
+ qdict_get_str(client, "port"),
+ qdict_get_bool(client, "tls") ? " [tls]" : "");
+ monitor_printf(mon, " session: %" PRId64 "\n",
+ qdict_get_int(client, "connection-id"));
+ monitor_printf(mon, " channel: %d:%d\n",
+ (int)qdict_get_int(client, "channel-type"),
+ (int)qdict_get_int(client, "channel-id"));
+}
+
+void do_info_spice_print(Monitor *mon, const QObject *data)
+{
+ QDict *server;
+ QList *channels;
+ const char *host;
+ int port;
+
+ server = qobject_to_qdict(data);
+ if (qdict_get_bool(server, "enabled") == 0) {
+ monitor_printf(mon, "Server: disabled\n");
+ return;
+ }
+
+ monitor_printf(mon, "Server:\n");
+ host = qdict_get_str(server, "host");
+ port = qdict_get_try_int(server, "port", -1);
+ if (port != -1) {
+ monitor_printf(mon, " address: %s:%d\n", host, port);
+ }
+ port = qdict_get_try_int(server, "tls-port", -1);
+ if (port != -1) {
+ monitor_printf(mon, " address: %s:%d [tls]\n", host, port);
+ }
+ monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
+
+ channels = qdict_get_qlist(server, "channels");
+ if (qlist_empty(channels)) {
+ monitor_printf(mon, "Channels: none\n");
+ } else {
+ qlist_iter(channels, info_spice_iter, mon);
+ }
+}
+
+void do_info_spice(Monitor *mon, QObject **ret_data)
+{
+ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+ QDict *server;
+ QList *clist;
+ const char *addr;
+ int port, tls_port;
+
+ if (!spice_server) {
+ *ret_data = qobject_from_jsonf("{ 'enabled': false }");
+ return;
+ }
+
+ addr = qemu_opt_get(opts, "addr");
+ port = qemu_opt_get_number(opts, "port", 0);
+ tls_port = qemu_opt_get_number(opts, "tls-port", 0);
+ clist = channel_list_get();
+
+ server = qdict_new();
+ qdict_put(server, "enabled", qbool_from_int(true));
+ qdict_put(server, "auth", qstring_from_str(auth));
+ qdict_put(server, "host", qstring_from_str(addr ? addr : "0.0.0.0"));
+ if (port) {
+ qdict_put(server, "port", qint_from_int(port));
+ }
+ if (tls_port) {
+ qdict_put(server, "tls-port", qint_from_int(tls_port));
+ }
+ if (clist) {
+ qdict_put(server, "channels", clist);
+ }
+
+ *ret_data = QOBJECT(server);
+}
+
+static void migration_state_notifier(Notifier *notifier)
+{
+ int state = get_migration_state();
+
+ if (state == MIG_STATE_COMPLETED) {
+#if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */
+ spice_server_migrate_switch(spice_server);
+#endif
+ }
+}
+
+int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
+ const char *subject)
+{
+ return spice_server_migrate_info(spice_server, hostname,
+ port, tls_port, subject);
+}
+
+static int add_channel(const char *name, const char *value, void *opaque)
+{
+ int security = 0;
+ int rc;
+
+ if (strcmp(name, "tls-channel") == 0) {
+ security = SPICE_CHANNEL_SECURITY_SSL;
+ }
+ if (strcmp(name, "plaintext-channel") == 0) {
+ security = SPICE_CHANNEL_SECURITY_NONE;
+ }
+ if (security == 0) {
+ return 0;
+ }
+ if (strcmp(value, "default") == 0) {
+ rc = spice_server_set_channel_security(spice_server, NULL, security);
+ } else {
+ rc = spice_server_set_channel_security(spice_server, value, security);
+ }
+ if (rc != 0) {
+ fprintf(stderr, "spice: failed to set channel security for %s\n", value);
+ exit(1);
+ }
+ return 0;
+}
+
+void qemu_spice_init(void)
+{
+ QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+ const char *password, *str, *x509_dir, *addr,
+ *x509_key_password = NULL,
+ *x509_dh_file = NULL,
+ *tls_ciphers = NULL;
+ char *x509_key_file = NULL,
+ *x509_cert_file = NULL,
+ *x509_cacert_file = NULL;
+ int port, tls_port, len, addr_flags;
+ spice_image_compression_t compression;
+ spice_wan_compression_t wan_compr;
+
+ if (!opts) {
+ return;
+ }
+ port = qemu_opt_get_number(opts, "port", 0);
+ tls_port = qemu_opt_get_number(opts, "tls-port", 0);
+ if (!port && !tls_port) {
+ return;
+ }
+ password = qemu_opt_get(opts, "password");
+
+ if (tls_port) {
+ x509_dir = qemu_opt_get(opts, "x509-dir");
+ if (NULL == x509_dir) {
+ x509_dir = ".";
+ }
+ len = strlen(x509_dir) + 32;
+
+ str = qemu_opt_get(opts, "x509-key-file");
+ if (str) {
+ x509_key_file = qemu_strdup(str);
+ } else {
+ x509_key_file = qemu_malloc(len);
+ snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
+ }
+
+ str = qemu_opt_get(opts, "x509-cert-file");
+ if (str) {
+ x509_cert_file = qemu_strdup(str);
+ } else {
+ x509_cert_file = qemu_malloc(len);
+ snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
+ }
+
+ str = qemu_opt_get(opts, "x509-cacert-file");
+ if (str) {
+ x509_cacert_file = qemu_strdup(str);
+ } else {
+ x509_cacert_file = qemu_malloc(len);
+ snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
+ }
+
+ x509_key_password = qemu_opt_get(opts, "x509-key-password");
+ x509_dh_file = qemu_opt_get(opts, "x509-dh-file");
+ tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
+ }
+
+ addr = qemu_opt_get(opts, "addr");
+ addr_flags = 0;
+ if (qemu_opt_get_bool(opts, "ipv4", 0)) {
+ addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
+ } else if (qemu_opt_get_bool(opts, "ipv6", 0)) {
+ addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
+ }
+
+ spice_server = spice_server_new();
+ spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
+ if (port) {
+ spice_server_set_port(spice_server, port);
+ }
+ if (tls_port) {
+ spice_server_set_tls(spice_server, tls_port,
+ x509_cacert_file,
+ x509_cert_file,
+ x509_key_file,
+ x509_key_password,
+ x509_dh_file,
+ tls_ciphers);
+ }
+ if (password) {
+ spice_server_set_ticket(spice_server, password, 0, 0, 0);
+ }
+ if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
+ auth = "none";
+ spice_server_set_noauth(spice_server);
+ }
+
+ compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
+ str = qemu_opt_get(opts, "image-compression");
+ if (str) {
+ compression = parse_compression(str);
+ }
+ spice_server_set_image_compression(spice_server, compression);
+
+ wan_compr = SPICE_WAN_COMPRESSION_AUTO;
+ str = qemu_opt_get(opts, "jpeg-wan-compression");
+ if (str) {
+ wan_compr = parse_wan_compression(str);
+ }
+ spice_server_set_jpeg_compression(spice_server, wan_compr);
+
+ wan_compr = SPICE_WAN_COMPRESSION_AUTO;
+ str = qemu_opt_get(opts, "zlib-glz-wan-compression");
+ if (str) {
+ wan_compr = parse_wan_compression(str);
+ }
+ spice_server_set_zlib_glz_compression(spice_server, wan_compr);
+
+#if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */
+
+ str = qemu_opt_get(opts, "streaming-video");
+ if (str) {
+ int streaming_video = parse_stream_video(str);
+ spice_server_set_streaming_video(spice_server, streaming_video);
+ }
+
+ spice_server_set_agent_mouse
+ (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
+ spice_server_set_playback_compression
+ (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
+
+#endif /* >= 0.6.0 */
+
+ qemu_opt_foreach(opts, add_channel, NULL, 0);
+
+ spice_server_init(spice_server, &core_interface);
+ using_spice = 1;
+
+ migration_state.notify = migration_state_notifier;
+ add_migration_state_change_notifier(&migration_state);
+
+ qemu_spice_input_init();
+ qemu_spice_audio_init();
+
+ qemu_free(x509_key_file);
+ qemu_free(x509_cert_file);
+ qemu_free(x509_cacert_file);
+}
+
+int qemu_spice_add_interface(SpiceBaseInstance *sin)
+{
+ if (!spice_server) {
+ if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
+ fprintf(stderr, "Oops: spice configured but not active\n");
+ exit(1);
+ }
+ /*
+ * Create a spice server instance.
+ * It does *not* listen on the network.
+ * It handles QXL local rendering only.
+ *
+ * With a command line like '-vnc :0 -vga qxl' you'll end up here.
+ */
+ spice_server = spice_server_new();
+ spice_server_init(spice_server, &core_interface);
+ }
+ return spice_server_add_interface(spice_server, sin);
+}
+
+static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
+{
+ time_t lifetime, now = time(NULL);
+ char *passwd;
+
+ if (now < auth_expires) {
+ passwd = auth_passwd;
+ lifetime = (auth_expires - now);
+ if (lifetime > INT_MAX) {
+ lifetime = INT_MAX;
+ }
+ } else {
+ passwd = NULL;
+ lifetime = 1;
+ }
+ return spice_server_set_ticket(spice_server, passwd, lifetime,
+ fail_if_conn, disconnect_if_conn);
+}
+
+int qemu_spice_set_passwd(const char *passwd,
+ bool fail_if_conn, bool disconnect_if_conn)
+{
+ free(auth_passwd);
+ auth_passwd = strdup(passwd);
+ return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
+}
+
+int qemu_spice_set_pw_expire(time_t expires)
+{
+ auth_expires = expires;
+ return qemu_spice_set_ticket(false, false);
+}
+
+static void spice_register_config(void)
+{
+ qemu_add_opts(&qemu_spice_opts);
+}
+machine_init(spice_register_config);
+
+static void spice_initialize(void)
+{
+ qemu_spice_init();
+}
+device_init(spice_initialize);
diff --git a/ui/spice-display.c b/ui/spice-display.c
new file mode 100644
index 0000000..020b423
--- /dev/null
+++ b/ui/spice-display.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <pthread.h>
+
+#include "qemu-common.h"
+#include "qemu-spice.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "monitor.h"
+#include "console.h"
+#include "sysemu.h"
+
+#include "spice-display.h"
+
+static int debug = 0;
+
+static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...)
+{
+ va_list args;
+
+ if (level <= debug) {
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+}
+
+int qemu_spice_rect_is_empty(const QXLRect* r)
+{
+ return r->top == r->bottom || r->left == r->right;
+}
+
+void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r)
+{
+ if (qemu_spice_rect_is_empty(r)) {
+ return;
+ }
+
+ if (qemu_spice_rect_is_empty(dest)) {
+ *dest = *r;
+ return;
+ }
+
+ dest->top = MIN(dest->top, r->top);
+ dest->left = MIN(dest->left, r->left);
+ dest->bottom = MAX(dest->bottom, r->bottom);
+ dest->right = MAX(dest->right, r->right);
+}
+
+/*
+ * Called from spice server thread context (via interface_get_command).
+ *
+ * We must aquire the global qemu mutex here to make sure the
+ * DisplayState (+DisplaySurface) we are accessing doesn't change
+ * underneath us.
+ */
+SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
+{
+ SimpleSpiceUpdate *update;
+ QXLDrawable *drawable;
+ QXLImage *image;
+ QXLCommand *cmd;
+ uint8_t *src, *dst;
+ int by, bw, bh;
+
+ qemu_mutex_lock_iothread();
+ if (qemu_spice_rect_is_empty(&ssd->dirty)) {
+ qemu_mutex_unlock_iothread();
+ return NULL;
+ };
+
+ dprint(2, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__,
+ ssd->dirty.left, ssd->dirty.right,
+ ssd->dirty.top, ssd->dirty.bottom);
+
+ update = qemu_mallocz(sizeof(*update));
+ drawable = &update->drawable;
+ image = &update->image;
+ cmd = &update->ext.cmd;
+
+ bw = ssd->dirty.right - ssd->dirty.left;
+ bh = ssd->dirty.bottom - ssd->dirty.top;
+ update->bitmap = qemu_malloc(bw * bh * 4);
+
+ drawable->bbox = ssd->dirty;
+ drawable->clip.type = SPICE_CLIP_TYPE_NONE;
+ drawable->effect = QXL_EFFECT_OPAQUE;
+ drawable->release_info.id = (intptr_t)update;
+ drawable->type = QXL_DRAW_COPY;
+ drawable->surfaces_dest[0] = -1;
+ drawable->surfaces_dest[1] = -1;
+ drawable->surfaces_dest[2] = -1;
+
+ drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT;
+ drawable->u.copy.src_bitmap = (intptr_t)image;
+ drawable->u.copy.src_area.right = bw;
+ drawable->u.copy.src_area.bottom = bh;
+
+ QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++);
+ image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
+ image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN;
+ image->bitmap.stride = bw * 4;
+ image->descriptor.width = image->bitmap.x = bw;
+ image->descriptor.height = image->bitmap.y = bh;
+ image->bitmap.data = (intptr_t)(update->bitmap);
+ image->bitmap.palette = 0;
+ image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
+
+ if (ssd->conv == NULL) {
+ PixelFormat dst = qemu_default_pixelformat(32);
+ ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
+ assert(ssd->conv);
+ }
+
+ src = ds_get_data(ssd->ds) +
+ ssd->dirty.top * ds_get_linesize(ssd->ds) +
+ ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds);
+ dst = update->bitmap;
+ for (by = 0; by < bh; by++) {
+ qemu_pf_conv_run(ssd->conv, dst, src, bw);
+ src += ds_get_linesize(ssd->ds);
+ dst += image->bitmap.stride;
+ }
+
+ cmd->type = QXL_CMD_DRAW;
+ cmd->data = (intptr_t)drawable;
+
+ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
+ qemu_mutex_unlock_iothread();
+ return update;
+}
+
+/*
+ * Called from spice server thread context (via interface_release_ressource)
+ * We do *not* hold the global qemu mutex here, so extra care is needed
+ * when calling qemu functions. Qemu interfaces used:
+ * - qemu_free (underlying glibc free is re-entrant).
+ */
+void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update)
+{
+ qemu_free(update->bitmap);
+ qemu_free(update);
+}
+
+void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
+{
+ QXLDevMemSlot memslot;
+
+ dprint(1, "%s:\n", __FUNCTION__);
+
+ memset(&memslot, 0, sizeof(memslot));
+ memslot.slot_group_id = MEMSLOT_GROUP_HOST;
+ memslot.virt_end = ~0;
+ ssd->worker->add_memslot(ssd->worker, &memslot);
+}
+
+void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
+{
+ QXLDevSurfaceCreate surface;
+
+ dprint(1, "%s: %dx%d\n", __FUNCTION__,
+ ds_get_width(ssd->ds), ds_get_height(ssd->ds));
+
+ surface.format = SPICE_SURFACE_FMT_32_xRGB;
+ surface.width = ds_get_width(ssd->ds);
+ surface.height = ds_get_height(ssd->ds);
+ surface.stride = -surface.width * 4;
+ surface.mouse_mode = true;
+ surface.flags = 0;
+ surface.type = 0;
+ surface.mem = (intptr_t)ssd->buf;
+ surface.group_id = MEMSLOT_GROUP_HOST;
+
+ qemu_mutex_unlock_iothread();
+ ssd->worker->create_primary_surface(ssd->worker, 0, &surface);
+ qemu_mutex_lock_iothread();
+}
+
+void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+
+ qemu_mutex_unlock_iothread();
+ ssd->worker->destroy_primary_surface(ssd->worker, 0);
+ qemu_mutex_lock_iothread();
+}
+
+void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason)
+{
+ SimpleSpiceDisplay *ssd = opaque;
+
+ if (running) {
+ ssd->worker->start(ssd->worker);
+ } else {
+ qemu_mutex_unlock_iothread();
+ ssd->worker->stop(ssd->worker);
+ qemu_mutex_lock_iothread();
+ }
+ ssd->running = running;
+}
+
+/* display listener callbacks */
+
+void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
+ int x, int y, int w, int h)
+{
+ QXLRect update_area;
+
+ dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
+ update_area.left = x,
+ update_area.right = x + w;
+ update_area.top = y;
+ update_area.bottom = y + h;
+
+ if (qemu_spice_rect_is_empty(&ssd->dirty)) {
+ ssd->notify++;
+ }
+ qemu_spice_rect_union(&ssd->dirty, &update_area);
+}
+
+void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+
+ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
+ qemu_pf_conv_put(ssd->conv);
+ ssd->conv = NULL;
+
+ qemu_spice_destroy_host_primary(ssd);
+ qemu_spice_create_host_primary(ssd);
+
+ memset(&ssd->dirty, 0, sizeof(ssd->dirty));
+ ssd->notify++;
+}
+
+void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
+{
+ dprint(3, "%s:\n", __FUNCTION__);
+ vga_hw_update();
+ if (ssd->notify) {
+ ssd->notify = 0;
+ ssd->worker->wakeup(ssd->worker);
+ dprint(2, "%s: notify\n", __FUNCTION__);
+ }
+}
+
+/* spice display interface callbacks */
+
+static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+
+ dprint(1, "%s:\n", __FUNCTION__);
+ ssd->worker = qxl_worker;
+}
+
+static void interface_set_compression_level(QXLInstance *sin, int level)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+ /* nothing to do */
+}
+
+static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
+{
+ dprint(3, "%s:\n", __FUNCTION__);
+ /* nothing to do */
+}
+
+static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+
+ info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
+ info->memslot_id_bits = MEMSLOT_SLOT_BITS;
+ info->num_memslots = NUM_MEMSLOTS;
+ info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+ info->internal_groupslot_id = 0;
+ info->qxl_ram_size = ssd->bufsize;
+ info->n_surfaces = NUM_SURFACES;
+}
+
+static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+ SimpleSpiceUpdate *update;
+
+ dprint(3, "%s:\n", __FUNCTION__);
+ update = qemu_spice_create_update(ssd);
+ if (update == NULL) {
+ return false;
+ }
+ *ext = update->ext;
+ return true;
+}
+
+static int interface_req_cmd_notification(QXLInstance *sin)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+ return 1;
+}
+
+static void interface_release_resource(QXLInstance *sin,
+ struct QXLReleaseInfoExt ext)
+{
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+ uintptr_t id;
+
+ dprint(2, "%s:\n", __FUNCTION__);
+ id = ext.info->id;
+ qemu_spice_destroy_update(ssd, (void*)id);
+}
+
+static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+ dprint(3, "%s:\n", __FUNCTION__);
+ return false;
+}
+
+static int interface_req_cursor_notification(QXLInstance *sin)
+{
+ dprint(1, "%s:\n", __FUNCTION__);
+ return 1;
+}
+
+static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
+{
+ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+ abort();
+}
+
+static int interface_flush_resources(QXLInstance *sin)
+{
+ fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+ abort();
+ return 0;
+}
+
+static const QXLInterface dpy_interface = {
+ .base.type = SPICE_INTERFACE_QXL,
+ .base.description = "qemu simple display",
+ .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+
+ .attache_worker = interface_attach_worker,
+ .set_compression_level = interface_set_compression_level,
+ .set_mm_time = interface_set_mm_time,
+ .get_init_info = interface_get_init_info,
+
+ /* the callbacks below are called from spice server thread context */
+ .get_command = interface_get_command,
+ .req_cmd_notification = interface_req_cmd_notification,
+ .release_resource = interface_release_resource,
+ .get_cursor_command = interface_get_cursor_command,
+ .req_cursor_notification = interface_req_cursor_notification,
+ .notify_update = interface_notify_update,
+ .flush_resources = interface_flush_resources,
+};
+
+static SimpleSpiceDisplay sdpy;
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+ qemu_spice_display_update(&sdpy, x, y, w, h);
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+ qemu_spice_display_resize(&sdpy);
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+ qemu_spice_display_refresh(&sdpy);
+}
+
+static DisplayChangeListener display_listener = {
+ .dpy_update = display_update,
+ .dpy_resize = display_resize,
+ .dpy_refresh = display_refresh,
+};
+
+void qemu_spice_display_init(DisplayState *ds)
+{
+ assert(sdpy.ds == NULL);
+ sdpy.ds = ds;
+ sdpy.bufsize = (16 * 1024 * 1024);
+ sdpy.buf = qemu_malloc(sdpy.bufsize);
+ register_displaychangelistener(ds, &display_listener);
+
+ sdpy.qxl.base.sif = &dpy_interface.base;
+ qemu_spice_add_interface(&sdpy.qxl.base);
+ assert(sdpy.worker);
+
+ qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy);
+ qemu_spice_create_host_memslot(&sdpy);
+ qemu_spice_create_host_primary(&sdpy);
+}
diff --git a/ui/spice-display.h b/ui/spice-display.h
new file mode 100644
index 0000000..aef0464
--- /dev/null
+++ b/ui/spice-display.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <spice/ipc_ring.h>
+#include <spice/enums.h>
+#include <spice/qxl_dev.h>
+
+#include "pflib.h"
+
+#define NUM_MEMSLOTS 8
+#define MEMSLOT_GENERATION_BITS 8
+#define MEMSLOT_SLOT_BITS 8
+
+#define MEMSLOT_GROUP_HOST 0
+#define MEMSLOT_GROUP_GUEST 1
+#define NUM_MEMSLOTS_GROUPS 2
+
+#define NUM_SURFACES 1024
+
+typedef struct SimpleSpiceDisplay {
+ DisplayState *ds;
+ void *buf;
+ int bufsize;
+ QXLWorker *worker;
+ QXLInstance qxl;
+ uint32_t unique;
+ QemuPfConv *conv;
+
+ QXLRect dirty;
+ int notify;
+ int running;
+} SimpleSpiceDisplay;
+
+typedef struct SimpleSpiceUpdate {
+ QXLDrawable drawable;
+ QXLImage image;
+ QXLCommandExt ext;
+ uint8_t *bitmap;
+} SimpleSpiceUpdate;
+
+int qemu_spice_rect_is_empty(const QXLRect* r);
+void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r);
+
+SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy);
+void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update);
+void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd);
+void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd);
+void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd);
+void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason);
+
+void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
+ int x, int y, int w, int h);
+void qemu_spice_display_resize(SimpleSpiceDisplay *ssd);
+void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd);
diff --git a/ui/spice-input.c b/ui/spice-input.c
new file mode 100644
index 0000000..37c8578
--- /dev/null
+++ b/ui/spice-input.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program 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 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/>.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <spice.h>
+#include <spice/enums.h>
+
+#include "qemu-common.h"
+#include "qemu-spice.h"
+#include "console.h"
+
+/* keyboard bits */
+
+typedef struct QemuSpiceKbd {
+ SpiceKbdInstance sin;
+ int ledstate;
+} QemuSpiceKbd;
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin);
+static void kbd_leds(void *opaque, int l);
+
+static const SpiceKbdInterface kbd_interface = {
+ .base.type = SPICE_INTERFACE_KEYBOARD,
+ .base.description = "qemu keyboard",
+ .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR,
+ .push_scan_freg = kbd_push_key,
+ .get_leds = kbd_get_leds,
+};
+
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+{
+ kbd_put_keycode(frag);
+}
+
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
+{
+ QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin);
+ return kbd->ledstate;
+}
+
+static void kbd_leds(void *opaque, int ledstate)
+{
+ QemuSpiceKbd *kbd = opaque;
+
+ kbd->ledstate = 0;
+ if (ledstate & QEMU_SCROLL_LOCK_LED) {
+ kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK;
+ }
+ if (ledstate & QEMU_NUM_LOCK_LED) {
+ kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK;
+ }
+ if (ledstate & QEMU_CAPS_LOCK_LED) {
+ kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK;
+ }
+ spice_server_kbd_leds(&kbd->sin, ledstate);
+}
+
+/* mouse bits */
+
+typedef struct QemuSpicePointer {
+ SpiceMouseInstance mouse;
+ SpiceTabletInstance tablet;
+ int width, height, x, y;
+ Notifier mouse_mode;
+ bool absolute;
+} QemuSpicePointer;
+
+static int map_buttons(int spice_buttons)
+{
+ int qemu_buttons = 0;
+
+ /*
+ * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this
+ * isn't what we get passed in via interface callbacks for the
+ * middle and right button ...
+ */
+ if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) {
+ qemu_buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) {
+ qemu_buttons |= MOUSE_EVENT_MBUTTON;
+ }
+ if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) {
+ qemu_buttons |= MOUSE_EVENT_RBUTTON;
+ }
+ return qemu_buttons;
+}
+
+static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
+ uint32_t buttons_state)
+{
+ kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state));
+}
+
+static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
+{
+ kbd_mouse_event(0, 0, 0, map_buttons(buttons_state));
+}
+
+static const SpiceMouseInterface mouse_interface = {
+ .base.type = SPICE_INTERFACE_MOUSE,
+ .base.description = "mouse",
+ .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR,
+ .motion = mouse_motion,
+ .buttons = mouse_buttons,
+};
+
+static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ if (height < 16) {
+ height = 16;
+ }
+ if (width < 16) {
+ width = 16;
+ }
+ pointer->width = width;
+ pointer->height = height;
+}
+
+static void tablet_position(SpiceTabletInstance* sin, int x, int y,
+ uint32_t buttons_state)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ pointer->x = x * 0x7FFF / (pointer->width - 1);
+ pointer->y = y * 0x7FFF / (pointer->height - 1);
+ kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+}
+
+
+static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
+ uint32_t buttons_state)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state));
+}
+
+static void tablet_buttons(SpiceTabletInstance *sin,
+ uint32_t buttons_state)
+{
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
+
+ kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+}
+
+static const SpiceTabletInterface tablet_interface = {
+ .base.type = SPICE_INTERFACE_TABLET,
+ .base.description = "tablet",
+ .base.major_version = SPICE_INTERFACE_TABLET_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_TABLET_MINOR,
+ .set_logical_size = tablet_set_logical_size,
+ .position = tablet_position,
+ .wheel = tablet_wheel,
+ .buttons = tablet_buttons,
+};
+
+static void mouse_mode_notifier(Notifier *notifier)
+{
+ QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
+ bool is_absolute = kbd_mouse_is_absolute();
+
+ if (pointer->absolute == is_absolute) {
+ return;
+ }
+
+ if (is_absolute) {
+ qemu_spice_add_interface(&pointer->tablet.base);
+ } else {
+ spice_server_remove_interface(&pointer->tablet.base);
+ }
+ pointer->absolute = is_absolute;
+}
+
+void qemu_spice_input_init(void)
+{
+ QemuSpiceKbd *kbd;
+ QemuSpicePointer *pointer;
+
+ kbd = qemu_mallocz(sizeof(*kbd));
+ kbd->sin.base.sif = &kbd_interface.base;
+ qemu_spice_add_interface(&kbd->sin.base);
+ qemu_add_led_event_handler(kbd_leds, kbd);
+
+ pointer = qemu_mallocz(sizeof(*pointer));
+ pointer->mouse.base.sif = &mouse_interface.base;
+ pointer->tablet.base.sif = &tablet_interface.base;
+ qemu_spice_add_interface(&pointer->mouse.base);
+
+ pointer->absolute = false;
+ pointer->mouse_mode.notify = mouse_mode_notifier;
+ qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode);
+ mouse_mode_notifier(&pointer->mouse_mode);
+}
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
new file mode 100644
index 0000000..17a621a
--- /dev/null
+++ b/ui/vnc-auth-sasl.c
@@ -0,0 +1,639 @@
+/*
+ * QEMU VNC display driver: SASL auth protocol
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+
+/* Max amount of data we send/recv for SASL steps to prevent DOS */
+#define SASL_DATA_MAX_LEN (1024 * 1024)
+
+
+void vnc_sasl_client_cleanup(VncState *vs)
+{
+ if (vs->sasl.conn) {
+ vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0;
+ vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
+ vs->sasl.encoded = NULL;
+ free(vs->sasl.username);
+ free(vs->sasl.mechlist);
+ vs->sasl.username = vs->sasl.mechlist = NULL;
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ }
+}
+
+
+long vnc_client_write_sasl(VncState *vs)
+{
+ long ret;
+
+ VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
+ "Encoded: %p size %d offset %d\n",
+ vs->output.buffer, vs->output.capacity, vs->output.offset,
+ vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
+
+ if (!vs->sasl.encoded) {
+ int err;
+ err = sasl_encode(vs->sasl.conn,
+ (char *)vs->output.buffer,
+ vs->output.offset,
+ (const char **)&vs->sasl.encoded,
+ &vs->sasl.encodedLength);
+ if (err != SASL_OK)
+ return vnc_client_io_error(vs, -1, EIO);
+
+ vs->sasl.encodedOffset = 0;
+ }
+
+ ret = vnc_client_write_buf(vs,
+ vs->sasl.encoded + vs->sasl.encodedOffset,
+ vs->sasl.encodedLength - vs->sasl.encodedOffset);
+ if (!ret)
+ return 0;
+
+ vs->sasl.encodedOffset += ret;
+ if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
+ vs->output.offset = 0;
+ vs->sasl.encoded = NULL;
+ vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
+ }
+
+ /* Can't merge this block with one above, because
+ * someone might have written more unencrypted
+ * data in vs->output while we were processing
+ * SASL encoded output
+ */
+ if (vs->output.offset == 0) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+
+ return ret;
+}
+
+
+long vnc_client_read_sasl(VncState *vs)
+{
+ long ret;
+ uint8_t encoded[4096];
+ const char *decoded;
+ unsigned int decodedLen;
+ int err;
+
+ ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
+ if (!ret)
+ return 0;
+
+ err = sasl_decode(vs->sasl.conn,
+ (char *)encoded, ret,
+ &decoded, &decodedLen);
+
+ if (err != SASL_OK)
+ return vnc_client_io_error(vs, -1, -EIO);
+ VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
+ encoded, ret, decoded, decodedLen);
+ buffer_reserve(&vs->input, decodedLen);
+ buffer_append(&vs->input, decoded, decodedLen);
+ return decodedLen;
+}
+
+
+static int vnc_auth_sasl_check_access(VncState *vs)
+{
+ const void *val;
+ int err;
+ int allow;
+
+ err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
+ err, sasl_errstring(err, NULL, NULL));
+ return -1;
+ }
+ if (val == NULL) {
+ VNC_DEBUG("no client username was found, denying access\n");
+ return -1;
+ }
+ VNC_DEBUG("SASL client username %s\n", (const char *)val);
+
+ vs->sasl.username = qemu_strdup((const char*)val);
+
+ if (vs->vd->sasl.acl == NULL) {
+ VNC_DEBUG("no ACL activated, allowing access\n");
+ return 0;
+ }
+
+ allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
+
+ VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
+ allow ? "allowed" : "denied");
+ return allow ? 0 : -1;
+}
+
+static int vnc_auth_sasl_check_ssf(VncState *vs)
+{
+ const void *val;
+ int err, ssf;
+
+ if (!vs->sasl.wantSSF)
+ return 1;
+
+ err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
+ if (err != SASL_OK)
+ return 0;
+
+ ssf = *(const int *)val;
+ VNC_DEBUG("negotiated an SSF of %d\n", ssf);
+ if (ssf < 56)
+ return 0; /* 56 is good for Kerberos */
+
+ /* Only setup for read initially, because we're about to send an RPC
+ * reply which must be in plain text. When the next incoming RPC
+ * arrives, we'll switch on writes too
+ *
+ * cf qemudClientReadSASL in qemud.c
+ */
+ vs->sasl.runSSF = 1;
+
+ /* We have a SSF that's good enough */
+ return 1;
+}
+
+/*
+ * Step Msg
+ *
+ * Input from client:
+ *
+ * u32 clientin-length
+ * u8-array clientin-string
+ *
+ * Output to client:
+ *
+ * u32 serverout-length
+ * u8-array serverout-strin
+ * u8 continue
+ */
+
+static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
+
+static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t datalen = len;
+ const char *serverout;
+ unsigned int serveroutlen;
+ int err;
+ char *clientdata = NULL;
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (datalen) {
+ clientdata = (char*)data;
+ clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
+ datalen--; /* Don't count NULL byte when passing to _start() */
+ }
+
+ VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
+ clientdata, datalen);
+ err = sasl_server_step(vs->sasl.conn,
+ clientdata,
+ datalen,
+ &serverout,
+ &serveroutlen);
+ if (err != SASL_OK &&
+ err != SASL_CONTINUE) {
+ VNC_DEBUG("sasl step failed %d (%s)\n",
+ err, sasl_errdetail(vs->sasl.conn));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ if (serveroutlen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("sasl step reply data too long %d\n",
+ serveroutlen);
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
+ serveroutlen, serverout ? 0 : 1);
+
+ if (serveroutlen) {
+ vnc_write_u32(vs, serveroutlen + 1);
+ vnc_write(vs, serverout, serveroutlen + 1);
+ } else {
+ vnc_write_u32(vs, 0);
+ }
+
+ /* Whether auth is complete */
+ vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
+
+ if (err == SASL_CONTINUE) {
+ VNC_DEBUG("%s", "Authentication must continue\n");
+ /* Wait for step length */
+ vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
+ } else {
+ if (!vnc_auth_sasl_check_ssf(vs)) {
+ VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
+ goto authreject;
+ }
+
+ /* Check username whitelist ACL */
+ if (vnc_auth_sasl_check_access(vs) < 0) {
+ VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+ goto authreject;
+ }
+
+ VNC_DEBUG("Authentication successful %d\n", vs->csock);
+ vnc_write_u32(vs, 0); /* Accept auth */
+ /*
+ * Delay writing in SSF encoded mode until pending output
+ * buffer is written
+ */
+ if (vs->sasl.runSSF)
+ vs->sasl.waitWriteSSF = vs->output.offset;
+ start_client_init(vs);
+ }
+
+ return 0;
+
+ authreject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ vnc_write_u32(vs, sizeof("Authentication failed"));
+ vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return -1;
+
+ authabort:
+ vnc_client_error(vs);
+ return -1;
+}
+
+static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t steplen = read_u32(data, 0);
+ VNC_DEBUG("Got client step len %d\n", steplen);
+ if (steplen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("Too much SASL data %d\n", steplen);
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (steplen == 0)
+ return protocol_client_auth_sasl_step(vs, NULL, 0);
+ else
+ vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
+ return 0;
+}
+
+/*
+ * Start Msg
+ *
+ * Input from client:
+ *
+ * u32 clientin-length
+ * u8-array clientin-string
+ *
+ * Output to client:
+ *
+ * u32 serverout-length
+ * u8-array serverout-strin
+ * u8 continue
+ */
+
+#define SASL_DATA_MAX_LEN (1024 * 1024)
+
+static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t datalen = len;
+ const char *serverout;
+ unsigned int serveroutlen;
+ int err;
+ char *clientdata = NULL;
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (datalen) {
+ clientdata = (char*)data;
+ clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
+ datalen--; /* Don't count NULL byte when passing to _start() */
+ }
+
+ VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
+ vs->sasl.mechlist, clientdata, datalen);
+ err = sasl_server_start(vs->sasl.conn,
+ vs->sasl.mechlist,
+ clientdata,
+ datalen,
+ &serverout,
+ &serveroutlen);
+ if (err != SASL_OK &&
+ err != SASL_CONTINUE) {
+ VNC_DEBUG("sasl start failed %d (%s)\n",
+ err, sasl_errdetail(vs->sasl.conn));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ if (serveroutlen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("sasl start reply data too long %d\n",
+ serveroutlen);
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
+ serveroutlen, serverout ? 0 : 1);
+
+ if (serveroutlen) {
+ vnc_write_u32(vs, serveroutlen + 1);
+ vnc_write(vs, serverout, serveroutlen + 1);
+ } else {
+ vnc_write_u32(vs, 0);
+ }
+
+ /* Whether auth is complete */
+ vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
+
+ if (err == SASL_CONTINUE) {
+ VNC_DEBUG("%s", "Authentication must continue\n");
+ /* Wait for step length */
+ vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
+ } else {
+ if (!vnc_auth_sasl_check_ssf(vs)) {
+ VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
+ goto authreject;
+ }
+
+ /* Check username whitelist ACL */
+ if (vnc_auth_sasl_check_access(vs) < 0) {
+ VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+ goto authreject;
+ }
+
+ VNC_DEBUG("Authentication successful %d\n", vs->csock);
+ vnc_write_u32(vs, 0); /* Accept auth */
+ start_client_init(vs);
+ }
+
+ return 0;
+
+ authreject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ vnc_write_u32(vs, sizeof("Authentication failed"));
+ vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return -1;
+
+ authabort:
+ vnc_client_error(vs);
+ return -1;
+}
+
+static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t startlen = read_u32(data, 0);
+ VNC_DEBUG("Got client start len %d\n", startlen);
+ if (startlen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("Too much SASL data %d\n", startlen);
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (startlen == 0)
+ return protocol_client_auth_sasl_start(vs, NULL, 0);
+
+ vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
+ return 0;
+}
+
+static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
+{
+ char *mechname = malloc(len + 1);
+ if (!mechname) {
+ VNC_DEBUG("Out of memory reading mechname\n");
+ vnc_client_error(vs);
+ }
+ strncpy(mechname, (char*)data, len);
+ mechname[len] = '\0';
+ VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
+ mechname, vs->sasl.mechlist);
+
+ if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
+ if (vs->sasl.mechlist[len] != '\0' &&
+ vs->sasl.mechlist[len] != ',') {
+ VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
+ goto fail;
+ }
+ } else {
+ char *offset = strstr(vs->sasl.mechlist, mechname);
+ VNC_DEBUG("Two %p\n", offset);
+ if (!offset) {
+ goto fail;
+ }
+ VNC_DEBUG("Two '%s'\n", offset);
+ if (offset[-1] != ',' ||
+ (offset[len] != '\0'&&
+ offset[len] != ',')) {
+ goto fail;
+ }
+ }
+
+ free(vs->sasl.mechlist);
+ vs->sasl.mechlist = mechname;
+
+ VNC_DEBUG("Validated mechname '%s'\n", mechname);
+ vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
+ return 0;
+
+ fail:
+ vnc_client_error(vs);
+ free(mechname);
+ return -1;
+}
+
+static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t mechlen = read_u32(data, 0);
+ VNC_DEBUG("Got client mechname len %d\n", mechlen);
+ if (mechlen > 100) {
+ VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
+ vnc_client_error(vs);
+ return -1;
+ }
+ if (mechlen < 1) {
+ VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
+ vnc_client_error(vs);
+ return -1;
+ }
+ vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
+ return 0;
+}
+
+#define USES_X509_AUTH(vs) \
+ ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
+ (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+
+
+void start_auth_sasl(VncState *vs)
+{
+ const char *mechlist = NULL;
+ sasl_security_properties_t secprops;
+ int err;
+ char *localAddr, *remoteAddr;
+ int mechlistlen;
+
+ VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
+
+ /* Get local & remote client addresses in form IPADDR;PORT */
+ if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
+ goto authabort;
+
+ if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
+ free(localAddr);
+ goto authabort;
+ }
+
+ err = sasl_server_new("vnc",
+ NULL, /* FQDN - just delegates to gethostname */
+ NULL, /* User realm */
+ localAddr,
+ remoteAddr,
+ NULL, /* Callbacks, not needed */
+ SASL_SUCCESS_DATA,
+ &vs->sasl.conn);
+ free(localAddr);
+ free(remoteAddr);
+ localAddr = remoteAddr = NULL;
+
+ if (err != SASL_OK) {
+ VNC_DEBUG("sasl context setup failed %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+#ifdef CONFIG_VNC_TLS
+ /* Inform SASL that we've got an external SSF layer from TLS/x509 */
+ if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
+ vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
+ gnutls_cipher_algorithm_t cipher;
+ sasl_ssf_t ssf;
+
+ cipher = gnutls_cipher_get(vs->tls.session);
+ if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
+ VNC_DEBUG("%s", "cannot TLS get cipher size\n");
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ ssf *= 8; /* tls key size is bytes, sasl wants bits */
+
+ err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ } else
+#endif /* CONFIG_VNC_TLS */
+ vs->sasl.wantSSF = 1;
+
+ memset (&secprops, 0, sizeof secprops);
+ /* Inform SASL that we've got an external SSF layer from TLS */
+ if (strncmp(vs->vd->display, "unix:", 5) == 0
+#ifdef CONFIG_VNC_TLS
+ /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
+ is not sufficiently strong */
+ || (vs->vd->auth == VNC_AUTH_VENCRYPT &&
+ vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+#endif /* CONFIG_VNC_TLS */
+ ) {
+ /* If we've got TLS or UNIX domain sock, we don't care about SSF */
+ secprops.min_ssf = 0;
+ secprops.max_ssf = 0;
+ secprops.maxbufsize = 8192;
+ secprops.security_flags = 0;
+ } else {
+ /* Plain TCP, better get an SSF layer */
+ secprops.min_ssf = 56; /* Good enough to require kerberos */
+ secprops.max_ssf = 100000; /* Arbitrary big number */
+ secprops.maxbufsize = 8192;
+ /* Forbid any anonymous or trivially crackable auth */
+ secprops.security_flags =
+ SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
+ }
+
+ err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot set SASL security props %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ err = sasl_listmech(vs->sasl.conn,
+ NULL, /* Don't need to set user */
+ "", /* Prefix */
+ ",", /* Separator */
+ "", /* Suffix */
+ &mechlist,
+ NULL,
+ NULL);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
+ err, sasl_errdetail(vs->sasl.conn));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
+
+ if (!(vs->sasl.mechlist = strdup(mechlist))) {
+ VNC_DEBUG("Out of memory");
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ mechlistlen = strlen(mechlist);
+ vnc_write_u32(vs, mechlistlen);
+ vnc_write(vs, mechlist, mechlistlen);
+ vnc_flush(vs);
+
+ VNC_DEBUG("Wait for client mechname length\n");
+ vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
+
+ return;
+
+ authabort:
+ vnc_client_error(vs);
+ return;
+}
+
+
diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
new file mode 100644
index 0000000..fd9b18a
--- /dev/null
+++ b/ui/vnc-auth-sasl.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU VNC display driver: SASL auth protocol
+ *
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __QEMU_VNC_AUTH_SASL_H__
+#define __QEMU_VNC_AUTH_SASL_H__
+
+
+#include <sasl/sasl.h>
+
+typedef struct VncStateSASL VncStateSASL;
+typedef struct VncDisplaySASL VncDisplaySASL;
+
+#include "acl.h"
+
+struct VncStateSASL {
+ sasl_conn_t *conn;
+ /* If we want to negotiate an SSF layer with client */
+ int wantSSF :1;
+ /* If we are now running the SSF layer */
+ int runSSF :1;
+ /*
+ * If this is non-zero, then wait for that many bytes
+ * to be written plain, before switching to SSF encoding
+ * This allows the VNC auth result to finish being
+ * written in plain.
+ */
+ unsigned int waitWriteSSF;
+
+ /*
+ * Buffering encoded data to allow more clear data
+ * to be stuffed onto the output buffer
+ */
+ const uint8_t *encoded;
+ unsigned int encodedLength;
+ unsigned int encodedOffset;
+ char *username;
+ char *mechlist;
+};
+
+struct VncDisplaySASL {
+ qemu_acl *acl;
+};
+
+void vnc_sasl_client_cleanup(VncState *vs);
+
+long vnc_client_read_sasl(VncState *vs);
+long vnc_client_write_sasl(VncState *vs);
+
+void start_auth_sasl(VncState *vs);
+
+#endif /* __QEMU_VNC_AUTH_SASL_H__ */
+
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
new file mode 100644
index 0000000..07c1691
--- /dev/null
+++ b/ui/vnc-auth-vencrypt.c
@@ -0,0 +1,175 @@
+/*
+ * QEMU VNC display driver: VeNCrypt authentication setup
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+
+
+static void start_auth_vencrypt_subauth(VncState *vs)
+{
+ switch (vs->vd->subauth) {
+ case VNC_AUTH_VENCRYPT_TLSNONE:
+ case VNC_AUTH_VENCRYPT_X509NONE:
+ VNC_DEBUG("Accept TLS auth none\n");
+ vnc_write_u32(vs, 0); /* Accept auth completion */
+ start_client_init(vs);
+ break;
+
+ case VNC_AUTH_VENCRYPT_TLSVNC:
+ case VNC_AUTH_VENCRYPT_X509VNC:
+ VNC_DEBUG("Start TLS auth VNC\n");
+ start_auth_vnc(vs);
+ break;
+
+#ifdef CONFIG_VNC_SASL
+ case VNC_AUTH_VENCRYPT_TLSSASL:
+ case VNC_AUTH_VENCRYPT_X509SASL:
+ VNC_DEBUG("Start TLS auth SASL\n");
+ return start_auth_sasl(vs);
+#endif /* CONFIG_VNC_SASL */
+
+ default: /* Should not be possible, but just in case */
+ VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth);
+ vnc_write_u8(vs, 1);
+ if (vs->minor >= 8) {
+ static const char err[] = "Unsupported authentication type";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_client_error(vs);
+ }
+}
+
+static void vnc_tls_handshake_io(void *opaque);
+
+static int vnc_start_vencrypt_handshake(struct VncState *vs) {
+ int ret;
+
+ if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
+ if (!gnutls_error_is_fatal(ret)) {
+ VNC_DEBUG("Handshake interrupted (blocking)\n");
+ if (!gnutls_record_get_direction(vs->tls.session))
+ qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
+ else
+ qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
+ return 0;
+ }
+ VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (vs->vd->tls.x509verify) {
+ if (vnc_tls_validate_certificate(vs) < 0) {
+ VNC_DEBUG("Client verification failed\n");
+ vnc_client_error(vs);
+ return -1;
+ } else {
+ VNC_DEBUG("Client verification passed\n");
+ }
+ }
+
+ VNC_DEBUG("Handshake done, switching to TLS data mode\n");
+ vs->tls.wiremode = VNC_WIREMODE_TLS;
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+
+ start_auth_vencrypt_subauth(vs);
+
+ return 0;
+}
+
+static void vnc_tls_handshake_io(void *opaque) {
+ struct VncState *vs = (struct VncState *)opaque;
+
+ VNC_DEBUG("Handshake IO continue\n");
+ vnc_start_vencrypt_handshake(vs);
+}
+
+
+
+#define NEED_X509_AUTH(vs) \
+ ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+
+
+static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
+{
+ int auth = read_u32(data, 0);
+
+ if (auth != vs->vd->subauth) {
+ VNC_DEBUG("Rejecting auth %d\n", auth);
+ vnc_write_u8(vs, 0); /* Reject auth */
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
+ vnc_write_u8(vs, 1); /* Accept auth */
+ vnc_flush(vs);
+
+ if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
+ VNC_DEBUG("Failed to setup TLS\n");
+ return 0;
+ }
+
+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
+ if (vnc_start_vencrypt_handshake(vs) < 0) {
+ VNC_DEBUG("Failed to start TLS handshake\n");
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
+{
+ if (data[0] != 0 ||
+ data[1] != 2) {
+ VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
+ vnc_write_u8(vs, 1); /* Reject version */
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+ VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
+ vnc_write_u8(vs, 0); /* Accept version */
+ vnc_write_u8(vs, 1); /* Number of sub-auths */
+ vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
+ }
+ return 0;
+}
+
+
+void start_auth_vencrypt(VncState *vs)
+{
+ /* Send VeNCrypt version 0.2 */
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 2);
+
+ vnc_read_when(vs, protocol_client_vencrypt_init, 2);
+}
+
diff --git a/ui/vnc-auth-vencrypt.h b/ui/vnc-auth-vencrypt.h
new file mode 100644
index 0000000..9f674c5
--- /dev/null
+++ b/ui/vnc-auth-vencrypt.h
@@ -0,0 +1,33 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__
+#define __QEMU_VNC_AUTH_VENCRYPT_H__
+
+void start_auth_vencrypt(VncState *vs);
+
+#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */
diff --git a/ui/vnc-enc-hextile-template.h b/ui/vnc-enc-hextile-template.h
new file mode 100644
index 0000000..b9f9f5e
--- /dev/null
+++ b/ui/vnc-enc-hextile-template.h
@@ -0,0 +1,211 @@
+#define CONCAT_I(a, b) a ## b
+#define CONCAT(a, b) CONCAT_I(a, b)
+#define pixel_t CONCAT(uint, CONCAT(BPP, _t))
+#ifdef GENERIC
+#define NAME CONCAT(generic_, BPP)
+#else
+#define NAME BPP
+#endif
+
+static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
+ int x, int y, int w, int h,
+ void *last_bg_,
+ void *last_fg_,
+ int *has_bg, int *has_fg)
+{
+ VncDisplay *vd = vs->vd;
+ uint8_t *row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
+ pixel_t *irow = (pixel_t *)row;
+ int j, i;
+ pixel_t *last_bg = (pixel_t *)last_bg_;
+ pixel_t *last_fg = (pixel_t *)last_fg_;
+ pixel_t bg = 0;
+ pixel_t fg = 0;
+ int n_colors = 0;
+ int bg_count = 0;
+ int fg_count = 0;
+ int flags = 0;
+ uint8_t data[(vs->clientds.pf.bytes_per_pixel + 2) * 16 * 16];
+ int n_data = 0;
+ int n_subtiles = 0;
+
+ for (j = 0; j < h; j++) {
+ for (i = 0; i < w; i++) {
+ switch (n_colors) {
+ case 0:
+ bg = irow[i];
+ n_colors = 1;
+ break;
+ case 1:
+ if (irow[i] != bg) {
+ fg = irow[i];
+ n_colors = 2;
+ }
+ break;
+ case 2:
+ if (irow[i] != bg && irow[i] != fg) {
+ n_colors = 3;
+ } else {
+ if (irow[i] == bg)
+ bg_count++;
+ else if (irow[i] == fg)
+ fg_count++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (n_colors > 2)
+ break;
+ irow += ds_get_linesize(vs->ds) / sizeof(pixel_t);
+ }
+
+ if (n_colors > 1 && fg_count > bg_count) {
+ pixel_t tmp = fg;
+ fg = bg;
+ bg = tmp;
+ }
+
+ if (!*has_bg || *last_bg != bg) {
+ flags |= 0x02;
+ *has_bg = 1;
+ *last_bg = bg;
+ }
+
+ if (n_colors < 3 && (!*has_fg || *last_fg != fg)) {
+ flags |= 0x04;
+ *has_fg = 1;
+ *last_fg = fg;
+ }
+
+ switch (n_colors) {
+ case 1:
+ n_data = 0;
+ break;
+ case 2:
+ flags |= 0x08;
+
+ irow = (pixel_t *)row;
+
+ for (j = 0; j < h; j++) {
+ int min_x = -1;
+ for (i = 0; i < w; i++) {
+ if (irow[i] == fg) {
+ if (min_x == -1)
+ min_x = i;
+ } else if (min_x != -1) {
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ min_x = -1;
+ }
+ }
+ if (min_x != -1) {
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ }
+ irow += ds_get_linesize(vs->ds) / sizeof(pixel_t);
+ }
+ break;
+ case 3:
+ flags |= 0x18;
+
+ irow = (pixel_t *)row;
+
+ if (!*has_bg || *last_bg != bg)
+ flags |= 0x02;
+
+ for (j = 0; j < h; j++) {
+ int has_color = 0;
+ int min_x = -1;
+ pixel_t color = 0; /* shut up gcc */
+
+ for (i = 0; i < w; i++) {
+ if (!has_color) {
+ if (irow[i] == bg)
+ continue;
+ color = irow[i];
+ min_x = i;
+ has_color = 1;
+ } else if (irow[i] != color) {
+ has_color = 0;
+#ifdef GENERIC
+ vnc_convert_pixel(vs, data + n_data, color);
+ n_data += vs->clientds.pf.bytes_per_pixel;
+#else
+ memcpy(data + n_data, &color, sizeof(color));
+ n_data += sizeof(pixel_t);
+#endif
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+
+ min_x = -1;
+ if (irow[i] != bg) {
+ color = irow[i];
+ min_x = i;
+ has_color = 1;
+ }
+ }
+ }
+ if (has_color) {
+#ifdef GENERIC
+ vnc_convert_pixel(vs, data + n_data, color);
+ n_data += vs->clientds.pf.bytes_per_pixel;
+#else
+ memcpy(data + n_data, &color, sizeof(color));
+ n_data += sizeof(pixel_t);
+#endif
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ }
+ irow += ds_get_linesize(vs->ds) / sizeof(pixel_t);
+ }
+
+ /* A SubrectsColoured subtile invalidates the foreground color */
+ *has_fg = 0;
+ if (n_data > (w * h * sizeof(pixel_t))) {
+ n_colors = 4;
+ flags = 0x01;
+ *has_bg = 0;
+
+ /* we really don't have to invalidate either the bg or fg
+ but we've lost the old values. oh well. */
+ }
+ default:
+ break;
+ }
+
+ if (n_colors > 3) {
+ flags = 0x01;
+ *has_fg = 0;
+ *has_bg = 0;
+ n_colors = 4;
+ }
+
+ vnc_write_u8(vs, flags);
+ if (n_colors < 4) {
+ if (flags & 0x02)
+ vs->write_pixels(vs, &vd->server->pf, last_bg, sizeof(pixel_t));
+ if (flags & 0x04)
+ vs->write_pixels(vs, &vd->server->pf, last_fg, sizeof(pixel_t));
+ if (n_subtiles) {
+ vnc_write_u8(vs, n_subtiles);
+ vnc_write(vs, data, n_data);
+ }
+ } else {
+ for (j = 0; j < h; j++) {
+ vs->write_pixels(vs, &vd->server->pf, row,
+ w * ds_get_bytes_per_pixel(vs->ds));
+ row += ds_get_linesize(vs->ds);
+ }
+ }
+}
+
+#undef NAME
+#undef pixel_t
+#undef CONCAT_I
+#undef CONCAT
diff --git a/ui/vnc-enc-hextile.c b/ui/vnc-enc-hextile.c
new file mode 100644
index 0000000..364a491
--- /dev/null
+++ b/ui/vnc-enc-hextile.c
@@ -0,0 +1,116 @@
+/*
+ * QEMU VNC display driver: hextile encoding
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+
+static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
+{
+ ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F);
+ ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F);
+}
+
+#define BPP 8
+#include "vnc-enc-hextile-template.h"
+#undef BPP
+
+#define BPP 16
+#include "vnc-enc-hextile-template.h"
+#undef BPP
+
+#define BPP 32
+#include "vnc-enc-hextile-template.h"
+#undef BPP
+
+#define GENERIC
+#define BPP 8
+#include "vnc-enc-hextile-template.h"
+#undef BPP
+#undef GENERIC
+
+#define GENERIC
+#define BPP 16
+#include "vnc-enc-hextile-template.h"
+#undef BPP
+#undef GENERIC
+
+#define GENERIC
+#define BPP 32
+#include "vnc-enc-hextile-template.h"
+#undef BPP
+#undef GENERIC
+
+int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
+ int y, int w, int h)
+{
+ int i, j;
+ int has_fg, has_bg;
+ uint8_t *last_fg, *last_bg;
+ VncDisplay *vd = vs->vd;
+
+ last_fg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel);
+ last_bg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel);
+ has_fg = has_bg = 0;
+ for (j = y; j < (y + h); j += 16) {
+ for (i = x; i < (x + w); i += 16) {
+ vs->hextile.send_tile(vs, i, j,
+ MIN(16, x + w - i), MIN(16, y + h - j),
+ last_bg, last_fg, &has_bg, &has_fg);
+ }
+ }
+ free(last_fg);
+ free(last_bg);
+
+ return 1;
+}
+
+void vnc_hextile_set_pixel_conversion(VncState *vs, int generic)
+{
+ if (!generic) {
+ switch (vs->ds->surface->pf.bits_per_pixel) {
+ case 8:
+ vs->hextile.send_tile = send_hextile_tile_8;
+ break;
+ case 16:
+ vs->hextile.send_tile = send_hextile_tile_16;
+ break;
+ case 32:
+ vs->hextile.send_tile = send_hextile_tile_32;
+ break;
+ }
+ } else {
+ switch (vs->ds->surface->pf.bits_per_pixel) {
+ case 8:
+ vs->hextile.send_tile = send_hextile_tile_generic_8;
+ break;
+ case 16:
+ vs->hextile.send_tile = send_hextile_tile_generic_16;
+ break;
+ case 32:
+ vs->hextile.send_tile = send_hextile_tile_generic_32;
+ break;
+ }
+ }
+}
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
new file mode 100644
index 0000000..f9b982e
--- /dev/null
+++ b/ui/vnc-enc-tight.c
@@ -0,0 +1,1718 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+
+#ifdef CONFIG_VNC_PNG
+#include <png.h>
+#endif
+#ifdef CONFIG_VNC_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+#include "qemu-common.h"
+
+#include "bswap.h"
+#include "qint.h"
+#include "vnc.h"
+#include "vnc-enc-tight.h"
+#include "vnc-palette.h"
+
+/* Compression level stuff. The following array contains various
+ encoder parameters for each of 10 compression levels (0..9).
+ Last three parameters correspond to JPEG quality levels (0..9). */
+
+static const struct {
+ int max_rect_size, max_rect_width;
+ int mono_min_rect_size, gradient_min_rect_size;
+ int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
+ int gradient_threshold, gradient_threshold24;
+ int idx_max_colors_divisor;
+ int jpeg_quality, jpeg_threshold, jpeg_threshold24;
+} tight_conf[] = {
+ { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
+ { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
+ { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
+ { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
+ { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
+ { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
+ { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
+ { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
+ { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
+ { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
+};
+
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h);
+
+#ifdef CONFIG_VNC_PNG
+static const struct {
+ int png_zlib_level, png_filters;
+} tight_png_conf[] = {
+ { 0, PNG_NO_FILTERS },
+ { 1, PNG_NO_FILTERS },
+ { 2, PNG_NO_FILTERS },
+ { 3, PNG_NO_FILTERS },
+ { 4, PNG_NO_FILTERS },
+ { 5, PNG_ALL_FILTERS },
+ { 6, PNG_ALL_FILTERS },
+ { 7, PNG_ALL_FILTERS },
+ { 8, PNG_ALL_FILTERS },
+ { 9, PNG_ALL_FILTERS },
+};
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+ VncPalette *palette);
+
+static bool tight_can_send_png_rect(VncState *vs, int w, int h)
+{
+ if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) {
+ return false;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1) {
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+/*
+ * Code to guess if given rectangle is suitable for smooth image
+ * compression (by applying "gradient" filter or JPEG coder).
+ */
+
+static unsigned int
+tight_detect_smooth_image24(VncState *vs, int w, int h)
+{
+ int off;
+ int x, y, d, dx;
+ unsigned int c;
+ unsigned int stats[256];
+ int pixels = 0;
+ int pix, left[3];
+ unsigned int errors;
+ unsigned char *buf = vs->tight.tight.buffer;
+
+ /*
+ * If client is big-endian, color samples begin from the second
+ * byte (offset 1) of a 32-bit pixel value.
+ */
+ off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
+
+ memset(stats, 0, sizeof (stats));
+
+ for (y = 0, x = 0; y < h && x < w;) {
+ for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH;
+ d++) {
+ for (c = 0; c < 3; c++) {
+ left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+ }
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) {
+ for (c = 0; c < 3; c++) {
+ pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+ stats[abs(pix - left[c])]++;
+ left[c] = pix;
+ }
+ pixels++;
+ }
+ }
+ if (w > h) {
+ x += h;
+ y = 0;
+ } else {
+ x = 0;
+ y += w;
+ }
+ }
+
+ /* 95% smooth or more ... */
+ if (stats[0] * 33 / pixels >= 95) {
+ return 0;
+ }
+
+ errors = 0;
+ for (c = 1; c < 8; c++) {
+ errors += stats[c] * (c * c);
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {
+ return 0;
+ }
+ }
+ for (; c < 256; c++) {
+ errors += stats[c] * (c * c);
+ }
+ errors /= (pixels * 3 - stats[0]);
+
+ return errors;
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp) \
+ \
+ static unsigned int \
+ tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \
+ bool endian; \
+ uint##bpp##_t pix; \
+ int max[3], shift[3]; \
+ int x, y, d, dx; \
+ unsigned int c; \
+ unsigned int stats[256]; \
+ int pixels = 0; \
+ int sample, sum, left[3]; \
+ unsigned int errors; \
+ unsigned char *buf = vs->tight.tight.buffer; \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ memset(stats, 0, sizeof(stats)); \
+ \
+ y = 0, x = 0; \
+ while (y < h && x < w) { \
+ for (d = 0; d < h - y && \
+ d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \
+ if (endian) { \
+ pix = bswap##bpp(pix); \
+ } \
+ for (c = 0; c < 3; c++) { \
+ left[c] = (int)(pix >> shift[c] & max[c]); \
+ } \
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \
+ dx++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \
+ if (endian) { \
+ pix = bswap##bpp(pix); \
+ } \
+ sum = 0; \
+ for (c = 0; c < 3; c++) { \
+ sample = (int)(pix >> shift[c] & max[c]); \
+ sum += abs(sample - left[c]); \
+ left[c] = sample; \
+ } \
+ if (sum > 255) { \
+ sum = 255; \
+ } \
+ stats[sum]++; \
+ pixels++; \
+ } \
+ } \
+ if (w > h) { \
+ x += h; \
+ y = 0; \
+ } else { \
+ x = 0; \
+ y += w; \
+ } \
+ } \
+ \
+ if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \
+ return 0; \
+ } \
+ \
+ errors = 0; \
+ for (c = 1; c < 8; c++) { \
+ errors += stats[c] * (c * c); \
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \
+ return 0; \
+ } \
+ } \
+ for (; c < 256; c++) { \
+ errors += stats[c] * (c * c); \
+ } \
+ errors /= (pixels - stats[0]); \
+ \
+ return errors; \
+ }
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
+static int
+tight_detect_smooth_image(VncState *vs, int w, int h)
+{
+ unsigned int errors;
+ int compression = vs->tight.compression;
+ int quality = vs->tight.quality;
+
+ if (!vs->vd->lossy) {
+ return 0;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1 ||
+ w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) {
+ return 0;
+ }
+
+ if (vs->tight.quality != (uint8_t)-1) {
+ if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) {
+ return 0;
+ }
+ } else {
+ if (w * h < tight_conf[compression].gradient_min_rect_size) {
+ return 0;
+ }
+ }
+
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ if (vs->tight.pixel24) {
+ errors = tight_detect_smooth_image24(vs, w, h);
+ if (vs->tight.quality != (uint8_t)-1) {
+ return (errors < tight_conf[quality].jpeg_threshold24);
+ }
+ return (errors < tight_conf[compression].gradient_threshold24);
+ } else {
+ errors = tight_detect_smooth_image32(vs, w, h);
+ }
+ } else {
+ errors = tight_detect_smooth_image16(vs, w, h);
+ }
+ if (quality != -1) {
+ return (errors < tight_conf[quality].jpeg_threshold);
+ }
+ return (errors < tight_conf[compression].gradient_threshold);
+}
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
+ \
+ static int \
+ tight_fill_palette##bpp(VncState *vs, int x, int y, \
+ int max, size_t count, \
+ uint32_t *bg, uint32_t *fg, \
+ VncPalette **palette) { \
+ uint##bpp##_t *data; \
+ uint##bpp##_t c0, c1, ci; \
+ int i, n0, n1; \
+ \
+ data = (uint##bpp##_t *)vs->tight.tight.buffer; \
+ \
+ c0 = data[0]; \
+ i = 1; \
+ while (i < count && data[i] == c0) \
+ i++; \
+ if (i >= count) { \
+ *bg = *fg = c0; \
+ return 1; \
+ } \
+ \
+ if (max < 2) { \
+ return 0; \
+ } \
+ \
+ n0 = i; \
+ c1 = data[i]; \
+ n1 = 0; \
+ for (i++; i < count; i++) { \
+ ci = data[i]; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ break; \
+ } \
+ if (i >= count) { \
+ if (n0 > n1) { \
+ *bg = (uint32_t)c0; \
+ *fg = (uint32_t)c1; \
+ } else { \
+ *bg = (uint32_t)c1; \
+ *fg = (uint32_t)c0; \
+ } \
+ return 2; \
+ } \
+ \
+ if (max == 2) { \
+ return 0; \
+ } \
+ \
+ *palette = palette_new(max, bpp); \
+ palette_put(*palette, c0); \
+ palette_put(*palette, c1); \
+ palette_put(*palette, ci); \
+ \
+ for (i++; i < count; i++) { \
+ if (data[i] == ci) { \
+ continue; \
+ } else { \
+ ci = data[i]; \
+ if (!palette_put(*palette, (uint32_t)ci)) { \
+ return 0; \
+ } \
+ } \
+ } \
+ \
+ return palette_size(*palette); \
+ }
+
+DEFINE_FILL_PALETTE_FUNCTION(8)
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+static int tight_fill_palette(VncState *vs, int x, int y,
+ size_t count, uint32_t *bg, uint32_t *fg,
+ VncPalette **palette)
+{
+ int max;
+
+ max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor;
+ if (max < 2 &&
+ count >= tight_conf[vs->tight.compression].mono_min_rect_size) {
+ max = 2;
+ }
+ if (max >= 256) {
+ max = 256;
+ }
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
+ case 2:
+ return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
+ default:
+ max = 2;
+ return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
+ }
+ return 0;
+}
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \
+ VncPalette *palette) { \
+ uint##bpp##_t *src; \
+ uint##bpp##_t rgb; \
+ int i, rep; \
+ uint8_t idx; \
+ \
+ src = (uint##bpp##_t *) buf; \
+ \
+ for (i = 0; i < count; i++) { \
+ \
+ rgb = *src++; \
+ rep = 0; \
+ while (i < count && *src == rgb) { \
+ rep++, src++, i++; \
+ } \
+ idx = palette_idx(palette, rgb); \
+ /* \
+ * Should never happen, but don't break everything \
+ * if it does, use the first color instead \
+ */ \
+ if (idx == (uint8_t)-1) { \
+ idx = 0; \
+ } \
+ while (rep >= 0) { \
+ *buf++ = idx; \
+ rep--; \
+ } \
+ } \
+ }
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \
+ uint##bpp##_t bg, uint##bpp##_t fg) { \
+ uint##bpp##_t *ptr; \
+ unsigned int value, mask; \
+ int aligned_width; \
+ int x, y, bg_bits; \
+ \
+ ptr = (uint##bpp##_t *) buf; \
+ aligned_width = w - w % 8; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (x = 0; x < aligned_width; x += 8) { \
+ for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
+ if (*ptr++ != bg) { \
+ break; \
+ } \
+ } \
+ if (bg_bits == 8) { \
+ *buf++ = 0; \
+ continue; \
+ } \
+ mask = 0x80 >> bg_bits; \
+ value = mask; \
+ for (bg_bits++; bg_bits < 8; bg_bits++) { \
+ mask >>= 1; \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ \
+ mask = 0x80; \
+ value = 0; \
+ if (x >= w) { \
+ continue; \
+ } \
+ \
+ for (; x < w; x++) { \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ mask >>= 1; \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ }
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void
+tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h)
+{
+ uint32_t *buf32;
+ uint32_t pix32;
+ int shift[3];
+ int *prev;
+ int here[3], upper[3], left[3], upperleft[3];
+ int prediction;
+ int x, y, c;
+
+ buf32 = (uint32_t *)buf;
+ memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int));
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ shift[0] = vs->clientds.pf.rshift;
+ shift[1] = vs->clientds.pf.gshift;
+ shift[2] = vs->clientds.pf.bshift;
+ } else {
+ shift[0] = 24 - vs->clientds.pf.rshift;
+ shift[1] = 24 - vs->clientds.pf.gshift;
+ shift[2] = 24 - vs->clientds.pf.bshift;
+ }
+
+ for (y = 0; y < h; y++) {
+ for (c = 0; c < 3; c++) {
+ upper[c] = 0;
+ here[c] = 0;
+ }
+ prev = (int *)vs->tight.gradient.buffer;
+ for (x = 0; x < w; x++) {
+ pix32 = *buf32++;
+ for (c = 0; c < 3; c++) {
+ upperleft[c] = upper[c];
+ left[c] = here[c];
+ upper[c] = *prev;
+ here[c] = (int)(pix32 >> shift[c] & 0xFF);
+ *prev++ = here[c];
+
+ prediction = left[c] + upper[c] - upperleft[c];
+ if (prediction < 0) {
+ prediction = 0;
+ } else if (prediction > 0xFF) {
+ prediction = 0xFF;
+ }
+ *buf++ = (char)(here[c] - prediction);
+ }
+ }
+ }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
+ \
+ static void \
+ tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \
+ int w, int h) { \
+ uint##bpp##_t pix, diff; \
+ bool endian; \
+ int *prev; \
+ int max[3], shift[3]; \
+ int here[3], upper[3], left[3], upperleft[3]; \
+ int prediction; \
+ int x, y, c; \
+ \
+ memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (c = 0; c < 3; c++) { \
+ upper[c] = 0; \
+ here[c] = 0; \
+ } \
+ prev = (int *)vs->tight.gradient.buffer; \
+ for (x = 0; x < w; x++) { \
+ pix = *buf; \
+ if (endian) { \
+ pix = bswap##bpp(pix); \
+ } \
+ diff = 0; \
+ for (c = 0; c < 3; c++) { \
+ upperleft[c] = upper[c]; \
+ left[c] = here[c]; \
+ upper[c] = *prev; \
+ here[c] = (int)(pix >> shift[c] & max[c]); \
+ *prev++ = here[c]; \
+ \
+ prediction = left[c] + upper[c] - upperleft[c]; \
+ if (prediction < 0) { \
+ prediction = 0; \
+ } else if (prediction > max[c]) { \
+ prediction = max[c]; \
+ } \
+ diff |= ((here[c] - prediction) & max[c]) \
+ << shift[c]; \
+ } \
+ if (endian) { \
+ diff = bswap##bpp(diff); \
+ } \
+ *buf++ = diff; \
+ } \
+ } \
+ }
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+/*
+ * Check if a rectangle is all of the same color. If needSameColor is
+ * set to non-zero, then also check that its color equals to the
+ * *colorPtr value. The result is 1 if the test is successful, and in
+ * that case new color will be stored in *colorPtr.
+ */
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
+ \
+ static bool \
+ check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \
+ uint32_t* color, bool samecolor) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t c; \
+ int dx, dy; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ c = *fbptr; \
+ if (samecolor && (uint32_t)c != *color) { \
+ return false; \
+ } \
+ \
+ for (dy = 0; dy < h; dy++) { \
+ for (dx = 0; dx < w; dx++) { \
+ if (c != fbptr[dx]) { \
+ return false; \
+ } \
+ } \
+ fbptr = (uint##bpp##_t *) \
+ ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \
+ } \
+ \
+ *color = (uint32_t)c; \
+ return true; \
+ }
+
+DEFINE_CHECK_SOLID_FUNCTION(32)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(8)
+
+static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
+ uint32_t* color, bool samecolor)
+{
+ VncDisplay *vd = vs->vd;
+
+ switch(vd->server->pf.bytes_per_pixel) {
+ case 4:
+ return check_solid_tile32(vs, x, y, w, h, color, samecolor);
+ case 2:
+ return check_solid_tile16(vs, x, y, w, h, color, samecolor);
+ default:
+ return check_solid_tile8(vs, x, y, w, h, color, samecolor);
+ }
+}
+
+static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *w_ptr, int *h_ptr)
+{
+ int dx, dy, dw, dh;
+ int w_prev;
+ int w_best = 0, h_best = 0;
+
+ w_prev = w;
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
+
+ if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
+ break;
+ }
+
+ for (dx = x + dw; dx < x + w_prev;) {
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
+ break;
+ }
+ dx += dw;
+ }
+
+ w_prev = dx - x;
+ if (w_prev * (dy + dh - y) > w_best * h_best) {
+ w_best = w_prev;
+ h_best = dy + dh - y;
+ }
+ }
+
+ *w_ptr = w_best;
+ *h_ptr = h_best;
+}
+
+static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *x_ptr, int *y_ptr,
+ int *w_ptr, int *h_ptr)
+{
+ int cx, cy;
+
+ /* Try to extend the area upwards. */
+ for ( cy = *y_ptr - 1;
+ cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy-- );
+ *h_ptr += *y_ptr - (cy + 1);
+ *y_ptr = cy + 1;
+
+ /* ... downwards. */
+ for ( cy = *y_ptr + *h_ptr;
+ cy < y + h &&
+ check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy++ );
+ *h_ptr += cy - (*y_ptr + *h_ptr);
+
+ /* ... to the left. */
+ for ( cx = *x_ptr - 1;
+ cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx-- );
+ *w_ptr += *x_ptr - (cx + 1);
+ *x_ptr = cx + 1;
+
+ /* ... to the right. */
+ for ( cx = *x_ptr + *w_ptr;
+ cx < x + w &&
+ check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx++ );
+ *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+static int tight_init_stream(VncState *vs, int stream_id,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight.stream[stream_id];
+
+ if (zstream->opaque == NULL) {
+ int err;
+
+ VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
+ VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
+ zstream->zalloc = vnc_zlib_zalloc;
+ zstream->zfree = vnc_zlib_zfree;
+
+ err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, strategy);
+
+ if (err != Z_OK) {
+ fprintf(stderr, "VNC: error initializing zlib\n");
+ return -1;
+ }
+
+ vs->tight.levels[stream_id] = level;
+ zstream->opaque = vs;
+ }
+
+ if (vs->tight.levels[stream_id] != level) {
+ if (deflateParams(zstream, level, strategy) != Z_OK) {
+ return -1;
+ }
+ vs->tight.levels[stream_id] = level;
+ }
+ return 0;
+}
+
+static void tight_send_compact_size(VncState *vs, size_t len)
+{
+ int lpc = 0;
+ int bytes = 0;
+ char buf[3] = {0, 0, 0};
+
+ buf[bytes++] = len & 0x7F;
+ if (len > 0x7F) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 7) & 0x7F;
+ if (len > 0x3FFF) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 14) & 0xFF;
+ }
+ }
+ for (lpc = 0; lpc < bytes; lpc++) {
+ vnc_write_u8(vs, buf[lpc]);
+ }
+}
+
+static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight.stream[stream_id];
+ int previous_out;
+
+ if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
+ vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset);
+ return bytes;
+ }
+
+ if (tight_init_stream(vs, stream_id, level, strategy)) {
+ return -1;
+ }
+
+ /* reserve memory in output buffer */
+ buffer_reserve(&vs->tight.zlib, bytes + 64);
+
+ /* set pointers */
+ zstream->next_in = vs->tight.tight.buffer;
+ zstream->avail_in = vs->tight.tight.offset;
+ zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset;
+ zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset;
+ previous_out = zstream->avail_out;
+ zstream->data_type = Z_BINARY;
+
+ /* start encoding */
+ if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+ fprintf(stderr, "VNC: error during tight compression\n");
+ return -1;
+ }
+
+ vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out;
+ /* ...how much data has actually been produced by deflate() */
+ bytes = previous_out - zstream->avail_out;
+
+ tight_send_compact_size(vs, bytes);
+ vnc_write(vs, vs->tight.zlib.buffer, bytes);
+
+ buffer_reset(&vs->tight.zlib);
+
+ return bytes;
+}
+
+/*
+ * Subencoding implementations.
+ */
+static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
+{
+ uint32_t *buf32;
+ uint32_t pix;
+ int rshift, gshift, bshift;
+
+ buf32 = (uint32_t *)buf;
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ rshift = vs->clientds.pf.rshift;
+ gshift = vs->clientds.pf.gshift;
+ bshift = vs->clientds.pf.bshift;
+ } else {
+ rshift = 24 - vs->clientds.pf.rshift;
+ gshift = 24 - vs->clientds.pf.gshift;
+ bshift = 24 - vs->clientds.pf.bshift;
+ }
+
+ if (ret) {
+ *ret = count * 3;
+ }
+
+ while (count--) {
+ pix = *buf32++;
+ *buf++ = (char)(pix >> rshift);
+ *buf++ = (char)(pix >> gshift);
+ *buf++ = (char)(pix >> bshift);
+ }
+}
+
+static int send_full_color_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int stream = 0;
+ ssize_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ return send_png_rect(vs, x, y, w, h, NULL);
+ }
+#endif
+
+ vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ bytes = tight_compress_data(vs, stream, w * h * bytes,
+ tight_conf[vs->tight.compression].raw_zlib_level,
+ Z_DEFAULT_STRATEGY);
+
+ return (bytes >= 0);
+}
+
+static int send_solid_rect(VncState *vs)
+{
+ size_t bytes;
+
+ vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ vnc_write(vs, vs->tight.tight.buffer, bytes);
+ return 1;
+}
+
+static int send_mono_rect(VncState *vs, int x, int y,
+ int w, int h, uint32_t bg, uint32_t fg)
+{
+ ssize_t bytes;
+ int stream = 1;
+ int level = tight_conf[vs->tight.compression].mono_zlib_level;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ int ret;
+ int bpp = vs->clientds.pf.bytes_per_pixel * 8;
+ VncPalette *palette = palette_new(2, bpp);
+
+ palette_put(palette, bg);
+ palette_put(palette, fg);
+ ret = send_png_rect(vs, x, y, w, h, palette);
+ palette_destroy(palette);
+ return ret;
+ }
+#endif
+
+ bytes = ((w + 7) / 8) * h;
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ uint32_t buf[2] = {bg, fg};
+ size_t ret = sizeof (buf);
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, (unsigned char*)buf, 2, &ret);
+ }
+ vnc_write(vs, buf, ret);
+
+ tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ }
+ case 2:
+ vnc_write(vs, &bg, 2);
+ vnc_write(vs, &fg, 2);
+ tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ default:
+ vnc_write_u8(vs, bg);
+ vnc_write_u8(vs, fg);
+ tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg);
+ break;
+ }
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+struct palette_cb_priv {
+ VncState *vs;
+ uint8_t *header;
+#ifdef CONFIG_VNC_PNG
+ png_colorp png_palette;
+#endif
+};
+
+static void write_palette(int idx, uint32_t color, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
+
+ if (bytes == 4) {
+ ((uint32_t*)priv->header)[idx] = color;
+ } else {
+ ((uint16_t*)priv->header)[idx] = color;
+ }
+}
+
+static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h)
+{
+ int stream = 3;
+ int level = tight_conf[vs->tight.compression].gradient_zlib_level;
+ ssize_t bytes;
+
+ if (vs->clientds.pf.bytes_per_pixel == 1)
+ return send_full_color_rect(vs, x, y, w, h);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
+
+ buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int));
+
+ if (vs->tight.pixel24) {
+ tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h);
+ bytes = 3;
+ } else if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h);
+ bytes = 4;
+ } else {
+ tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h);
+ bytes = 2;
+ }
+
+ buffer_reset(&vs->tight.gradient);
+
+ bytes = w * h * bytes;
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_FILTERED);
+ return (bytes >= 0);
+}
+
+static int send_palette_rect(VncState *vs, int x, int y,
+ int w, int h, VncPalette *palette)
+{
+ int stream = 2;
+ int level = tight_conf[vs->tight.compression].idx_zlib_level;
+ int colors;
+ ssize_t bytes;
+
+#ifdef CONFIG_VNC_PNG
+ if (tight_can_send_png_rect(vs, w, h)) {
+ return send_png_rect(vs, x, y, w, h, palette);
+ }
+#endif
+
+ colors = palette_size(palette);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, colors - 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ size_t old_offset, offset;
+ uint32_t header[palette_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ old_offset = vs->output.offset;
+ palette_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+
+ if (vs->tight.pixel24) {
+ tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
+ vs->output.offset = old_offset + offset;
+ }
+
+ tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+ break;
+ }
+ case 2:
+ {
+ uint16_t header[palette_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ palette_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+ tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+ break;
+ }
+ default:
+ return -1; /* No palette for 8bits colors */
+ break;
+ }
+ bytes = w * h;
+ vs->tight.tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+#if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG)
+static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ VncDisplay *vd = vs->vd;
+ uint32_t *fbptr;
+ uint32_t pix;
+
+ fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) +
+ x * ds_get_bytes_per_pixel(vs->ds));
+
+ while (count--) {
+ pix = *fbptr++;
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift);
+ }
+}
+
+#define DEFINE_RGB_GET_ROW_FUNCTION(bpp) \
+ \
+ static void \
+ rgb_prepare_row##bpp(VncState *vs, uint8_t *dst, \
+ int x, int y, int count) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t pix; \
+ int r, g, b; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ while (count--) { \
+ pix = *fbptr++; \
+ \
+ r = (int)((pix >> vs->ds->surface->pf.rshift) \
+ & vs->ds->surface->pf.rmax); \
+ g = (int)((pix >> vs->ds->surface->pf.gshift) \
+ & vs->ds->surface->pf.gmax); \
+ b = (int)((pix >> vs->ds->surface->pf.bshift) \
+ & vs->ds->surface->pf.bmax); \
+ \
+ *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \
+ / vs->ds->surface->pf.rmax); \
+ *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \
+ / vs->ds->surface->pf.gmax); \
+ *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \
+ / vs->ds->surface->pf.bmax); \
+ } \
+ }
+
+DEFINE_RGB_GET_ROW_FUNCTION(16)
+DEFINE_RGB_GET_ROW_FUNCTION(32)
+
+static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ if (ds_get_bytes_per_pixel(vs->ds) == 4) {
+ if (vs->ds->surface->pf.rmax == 0xFF &&
+ vs->ds->surface->pf.gmax == 0xFF &&
+ vs->ds->surface->pf.bmax == 0xFF) {
+ rgb_prepare_row24(vs, dst, x, y, count);
+ } else {
+ rgb_prepare_row32(vs, dst, x, y, count);
+ }
+ } else {
+ rgb_prepare_row16(vs, dst, x, y, count);
+ }
+}
+#endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */
+
+/*
+ * JPEG compression stuff.
+ */
+#ifdef CONFIG_VNC_JPEG
+/*
+ * Destination manager implementation for JPEG library.
+ */
+
+/* This is called once per encoding */
+static void jpeg_init_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
+ cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
+}
+
+/* This is called when we ran out of buffer (shouldn't happen!) */
+static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ buffer->offset = buffer->capacity;
+ buffer_reserve(buffer, 2048);
+ jpeg_init_destination(cinfo);
+ return TRUE;
+}
+
+/* This is called when we are done processing data */
+static void jpeg_term_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight.jpeg;
+
+ buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
+}
+
+static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ struct jpeg_destination_mgr manager;
+ JSAMPROW row[1];
+ uint8_t *buf;
+ int dy;
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1)
+ return send_full_color_rect(vs, x, y, w, h);
+
+ buffer_reserve(&vs->tight.jpeg, 2048);
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ cinfo.client_data = vs;
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, true);
+
+ manager.init_destination = jpeg_init_destination;
+ manager.empty_output_buffer = jpeg_empty_output_buffer;
+ manager.term_destination = jpeg_term_destination;
+ cinfo.dest = &manager;
+
+ jpeg_start_compress(&cinfo, true);
+
+ buf = qemu_malloc(w * 3);
+ row[0] = buf;
+ for (dy = 0; dy < h; dy++) {
+ rgb_prepare_row(vs, buf, x, y + dy, w);
+ jpeg_write_scanlines(&cinfo, row, 1);
+ }
+ qemu_free(buf);
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ vnc_write_u8(vs, VNC_TIGHT_JPEG << 4);
+
+ tight_send_compact_size(vs, vs->tight.jpeg.offset);
+ vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset);
+ buffer_reset(&vs->tight.jpeg);
+
+ return 1;
+}
+#endif /* CONFIG_VNC_JPEG */
+
+/*
+ * PNG compression stuff.
+ */
+#ifdef CONFIG_VNC_PNG
+static void write_png_palette(int idx, uint32_t pix, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ png_colorp color = &priv->png_palette[idx];
+
+ if (vs->tight.pixel24)
+ {
+ color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+ color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+ color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+ }
+ else
+ {
+ int red, green, blue;
+
+ red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax;
+ green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax;
+ blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax;
+ color->red = ((red * 255 + vs->clientds.pf.rmax / 2) /
+ vs->clientds.pf.rmax);
+ color->green = ((green * 255 + vs->clientds.pf.gmax / 2) /
+ vs->clientds.pf.gmax);
+ color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) /
+ vs->clientds.pf.bmax);
+ }
+}
+
+static void png_write_data(png_structp png_ptr, png_bytep data,
+ png_size_t length)
+{
+ VncState *vs = png_get_io_ptr(png_ptr);
+
+ buffer_reserve(&vs->tight.png, vs->tight.png.offset + length);
+ memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length);
+
+ vs->tight.png.offset += length;
+}
+
+static void png_flush_data(png_structp png_ptr)
+{
+}
+
+static void *vnc_png_malloc(png_structp png_ptr, png_size_t size)
+{
+ return qemu_malloc(size);
+}
+
+static void vnc_png_free(png_structp png_ptr, png_voidp ptr)
+{
+ qemu_free(ptr);
+}
+
+static int send_png_rect(VncState *vs, int x, int y, int w, int h,
+ VncPalette *palette)
+{
+ png_byte color_type;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_colorp png_palette = NULL;
+ int level = tight_png_conf[vs->tight.compression].png_zlib_level;
+ int filters = tight_png_conf[vs->tight.compression].png_filters;
+ uint8_t *buf;
+ int dy;
+
+ png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
+ NULL, vnc_png_malloc, vnc_png_free);
+
+ if (png_ptr == NULL)
+ return -1;
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (info_ptr == NULL) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return -1;
+ }
+
+ png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data);
+ png_set_compression_level(png_ptr, level);
+ png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters);
+
+ if (palette) {
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ color_type = PNG_COLOR_TYPE_RGB;
+ }
+
+ png_set_IHDR(png_ptr, info_ptr, w, h,
+ 8, color_type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ struct palette_cb_priv priv;
+
+ png_palette = png_malloc(png_ptr, sizeof(*png_palette) *
+ palette_size(palette));
+
+ priv.vs = vs;
+ priv.png_palette = png_palette;
+ palette_iter(palette, write_png_palette, &priv);
+
+ png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette));
+
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette);
+ } else {
+ tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette);
+ }
+ }
+
+ png_write_info(png_ptr, info_ptr);
+
+ buffer_reserve(&vs->tight.png, 2048);
+ buf = qemu_malloc(w * 3);
+ for (dy = 0; dy < h; dy++)
+ {
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ memcpy(buf, vs->tight.tight.buffer + (dy * w), w);
+ } else {
+ rgb_prepare_row(vs, buf, x, y + dy, w);
+ }
+ png_write_row(png_ptr, buf);
+ }
+ qemu_free(buf);
+
+ png_write_end(png_ptr, NULL);
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_free(png_ptr, png_palette);
+ }
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ vnc_write_u8(vs, VNC_TIGHT_PNG << 4);
+
+ tight_send_compact_size(vs, vs->tight.png.offset);
+ vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset);
+ buffer_reset(&vs->tight.png);
+ return 1;
+}
+#endif /* CONFIG_VNC_PNG */
+
+static void vnc_tight_start(VncState *vs)
+{
+ buffer_reset(&vs->tight.tight);
+
+ // make the output buffer be the zlib buffer, so we can compress it later
+ vs->tight.tmp = vs->output;
+ vs->output = vs->tight.tight;
+}
+
+static void vnc_tight_stop(VncState *vs)
+{
+ // switch back to normal output/zlib buffers
+ vs->tight.tight = vs->output;
+ vs->output = vs->tight.tmp;
+}
+
+static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h,
+ int bg, int fg, int colors, VncPalette *palette)
+{
+ int ret;
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ ret = send_gradient_rect(vs, x, y, w, h);
+ } else {
+ ret = send_full_color_rect(vs, x, y, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+ } else if (colors <= 256) {
+ ret = send_palette_rect(vs, x, y, w, h, palette);
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_VNC_JPEG
+static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h,
+ int bg, int fg, int colors,
+ VncPalette *palette)
+{
+ int ret;
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_full_color_rect(vs, x, y, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, x, y, w, h, bg, fg);
+ } else if (colors <= 256) {
+ if (colors > 96 &&
+ tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight.quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_palette_rect(vs, x, y, w, h, palette);
+ }
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+#endif
+
+static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
+{
+ VncPalette *palette = NULL;
+ uint32_t bg = 0, fg = 0;
+ int colors;
+ int ret = 0;
+
+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
+
+#ifdef CONFIG_VNC_JPEG
+ if (vs->tight.quality != (uint8_t)-1) {
+ ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette);
+ } else {
+ ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+ }
+#else
+ ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette);
+#endif
+
+ palette_destroy(palette);
+ return ret;
+}
+
+static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
+{
+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ return send_solid_rect(vs);
+}
+
+static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
+{
+ int max_size, max_width;
+ int max_sub_width, max_sub_height;
+ int dx, dy;
+ int rw, rh;
+ int n = 0;
+
+ max_size = tight_conf[vs->tight.compression].max_rect_size;
+ max_width = tight_conf[vs->tight.compression].max_rect_width;
+
+ if (w > max_width || w * h > max_size) {
+ max_sub_width = (w > max_width) ? max_width : w;
+ max_sub_height = max_size / max_sub_width;
+
+ for (dy = 0; dy < h; dy += max_sub_height) {
+ for (dx = 0; dx < w; dx += max_width) {
+ rw = MIN(max_sub_width, w - dx);
+ rh = MIN(max_sub_height, h - dy);
+ n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
+ }
+ }
+ } else {
+ n += send_sub_rect(vs, x, y, w, h);
+ }
+
+ return n;
+}
+
+static int find_large_solid_color_rect(VncState *vs, int x, int y,
+ int w, int h, int max_rows)
+{
+ int dx, dy, dw, dh;
+ int n = 0;
+
+ /* Try to find large solid-color areas and send them separately. */
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ /* If a rectangle becomes too large, send its upper part now. */
+
+ if (dy - y >= max_rows) {
+ n += send_rect_simple(vs, x, y, w, max_rows);
+ y += max_rows;
+ h -= max_rows;
+ }
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
+
+ for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+ uint32_t color_value;
+ int x_best, y_best, w_best, h_best;
+
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
+ continue ;
+ }
+
+ /* Get dimensions of solid-color area. */
+
+ find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
+ color_value, &w_best, &h_best);
+
+ /* Make sure a solid rectangle is large enough
+ (or the whole rectangle is of the same color). */
+
+ if (w_best * h_best != w * h &&
+ w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
+ continue;
+ }
+
+ /* Try to extend solid rectangle to maximum size. */
+
+ x_best = dx; y_best = dy;
+ extend_solid_area(vs, x, y, w, h, color_value,
+ &x_best, &y_best, &w_best, &h_best);
+
+ /* Send rectangles at top and left to solid-color area. */
+
+ if (y_best != y) {
+ n += send_rect_simple(vs, x, y, w, y_best-y);
+ }
+ if (x_best != x) {
+ n += tight_send_framebuffer_update(vs, x, y_best,
+ x_best-x, h_best);
+ }
+
+ /* Send solid-color rectangle. */
+ n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
+
+ /* Send remaining rectangles (at right and bottom). */
+
+ if (x_best + w_best != x + w) {
+ n += tight_send_framebuffer_update(vs, x_best+w_best,
+ y_best,
+ w-(x_best-x)-w_best,
+ h_best);
+ }
+ if (y_best + h_best != y + h) {
+ n += tight_send_framebuffer_update(vs, x, y_best+h_best,
+ w, h-(y_best-y)-h_best);
+ }
+
+ /* Return after all recursive calls are done. */
+ return n;
+ }
+ }
+ return n + send_rect_simple(vs, x, y, w, h);
+}
+
+static int tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ int max_rows;
+
+ if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
+ vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
+ vs->tight.pixel24 = true;
+ } else {
+ vs->tight.pixel24 = false;
+ }
+
+ if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
+ return send_rect_simple(vs, x, y, w, h);
+
+ /* Calculate maximum number of rows in one non-solid rectangle. */
+
+ max_rows = tight_conf[vs->tight.compression].max_rect_size;
+ max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w);
+
+ return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
+}
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ vs->tight.type = VNC_ENCODING_TIGHT;
+ return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ vs->tight.type = VNC_ENCODING_TIGHT_PNG;
+ return tight_send_framebuffer_update(vs, x, y, w, h);
+}
+
+void vnc_tight_clear(VncState *vs)
+{
+ int i;
+ for (i=0; i<ARRAY_SIZE(vs->tight.stream); i++) {
+ if (vs->tight.stream[i].opaque) {
+ deflateEnd(&vs->tight.stream[i]);
+ }
+ }
+
+ buffer_free(&vs->tight.tight);
+ buffer_free(&vs->tight.zlib);
+ buffer_free(&vs->tight.gradient);
+#ifdef CONFIG_VNC_JPEG
+ buffer_free(&vs->tight.jpeg);
+#endif
+#ifdef CONFIG_VNC_PNG
+ buffer_free(&vs->tight.png);
+#endif
+}
diff --git a/ui/vnc-enc-tight.h b/ui/vnc-enc-tight.h
new file mode 100644
index 0000000..a3add78
--- /dev/null
+++ b/ui/vnc-enc-tight.h
@@ -0,0 +1,183 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/rfb/rfbproto.h
+ * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_ENCODING_TIGHT_H
+#define VNC_ENCODING_TIGHT_H
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Tight Encoding.
+ *
+ *-- The first byte of each Tight-encoded rectangle is a "compression control
+ * byte". Its format is as follows (bit 0 is the least significant one):
+ *
+ * bit 0: if 1, then compression stream 0 should be reset;
+ * bit 1: if 1, then compression stream 1 should be reset;
+ * bit 2: if 1, then compression stream 2 should be reset;
+ * bit 3: if 1, then compression stream 3 should be reset;
+ * bits 7-4: if 1000 (0x08), then the compression type is "fill",
+ * if 1001 (0x09), then the compression type is "jpeg",
+ * if 1010 (0x0A), then the compression type is "png",
+ * if 0xxx, then the compression type is "basic",
+ * values greater than 1010 are not valid.
+ *
+ * If the compression type is "basic", then bits 6..4 of the
+ * compression control byte (those xxx in 0xxx) specify the following:
+ *
+ * bits 5-4: decimal representation is the index of a particular zlib
+ * stream which should be used for decompressing the data;
+ * bit 6: if 1, then a "filter id" byte is following this byte.
+ *
+ *-- The data that follows after the compression control byte described
+ * above depends on the compression type ("fill", "jpeg", "png" or "basic").
+ *
+ *-- If the compression type is "fill", then the only pixel value follows, in
+ * client pixel format (see NOTE 1). This value applies to all pixels of the
+ * rectangle.
+ *
+ *-- If the compression type is "jpeg" or "png", the following data stream
+ * looks like this:
+ *
+ * 1..3 bytes: data size (N) in compact representation;
+ * N bytes: JPEG or PNG image.
+ *
+ * Data size is compactly represented in one, two or three bytes, according
+ * to the following scheme:
+ *
+ * 0xxxxxxx (for values 0..127)
+ * 1xxxxxxx 0yyyyyyy (for values 128..16383)
+ * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303)
+ *
+ * Here each character denotes one bit, xxxxxxx are the least significant 7
+ * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the
+ * most significant 8 bits (bits 14-21). For example, decimal value 10000
+ * should be represented as two bytes: binary 10010000 01001110, or
+ * hexadecimal 90 4E.
+ *
+ *-- If the compression type is "basic" and bit 6 of the compression control
+ * byte was set to 1, then the next (second) byte specifies "filter id" which
+ * tells the decoder what filter type was used by the encoder to pre-process
+ * pixel data before the compression. The "filter id" byte can be one of the
+ * following:
+ *
+ * 0: no filter ("copy" filter);
+ * 1: "palette" filter;
+ * 2: "gradient" filter.
+ *
+ *-- If bit 6 of the compression control byte is set to 0 (no "filter id"
+ * byte), or if the filter id is 0, then raw pixel values in the client
+ * format (see NOTE 1) will be compressed. See below details on the
+ * compression.
+ *
+ *-- The "gradient" filter pre-processes pixel data with a simple algorithm
+ * which converts each color component to a difference between a "predicted"
+ * intensity and the actual intensity. Such a technique does not affect
+ * uncompressed data size, but helps to compress photo-like images better.
+ * Pseudo-code for converting intensities to differences is the following:
+ *
+ * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
+ * if (P[i,j] < 0) then P[i,j] := 0;
+ * if (P[i,j] > MAX) then P[i,j] := MAX;
+ * D[i,j] := V[i,j] - P[i,j];
+ *
+ * Here V[i,j] is the intensity of a color component for a pixel at
+ * coordinates (i,j). MAX is the maximum value of intensity for a color
+ * component.
+ *
+ *-- The "palette" filter converts true-color pixel data to indexed colors
+ * and a palette which can consist of 2..256 colors. If the number of colors
+ * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to
+ * encode one pixel. 1-bit encoding is performed such way that the most
+ * significant bits correspond to the leftmost pixels, and each raw of pixels
+ * is aligned to the byte boundary. When "palette" filter is used, the
+ * palette is sent before the pixel data. The palette begins with an unsigned
+ * byte which value is the number of colors in the palette minus 1 (i.e. 1
+ * means 2 colors, 255 means 256 colors in the palette). Then follows the
+ * palette itself which consist of pixel values in client pixel format (see
+ * NOTE 1).
+ *
+ *-- The pixel data is compressed using the zlib library. But if the data
+ * size after applying the filter but before the compression is less then 12,
+ * then the data is sent as is, uncompressed. Four separate zlib streams
+ * (0..3) can be used and the decoder should read the actual stream id from
+ * the compression control byte (see NOTE 2).
+ *
+ * If the compression is not used, then the pixel data is sent as is,
+ * otherwise the data stream looks like this:
+ *
+ * 1..3 bytes: data size (N) in compact representation;
+ * N bytes: zlib-compressed data.
+ *
+ * Data size is compactly represented in one, two or three bytes, just like
+ * in the "jpeg" compression method (see above).
+ *
+ *-- NOTE 1. If the color depth is 24, and all three color components are
+ * 8-bit wide, then one pixel in Tight encoding is always represented by
+ * three bytes, where the first byte is red component, the second byte is
+ * green component, and the third byte is blue component of the pixel color
+ * value. This applies to colors in palettes as well.
+ *
+ *-- NOTE 2. The decoder must reset compression streams' states before
+ * decoding the rectangle, if some of bits 0,1,2,3 in the compression control
+ * byte are set to 1. Note that the decoder must reset zlib streams even if
+ * the compression type is "fill", "jpeg" or "png".
+ *
+ *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
+ * when bits-per-pixel value is either 16 or 32, not 8.
+ *
+ *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048
+ * pixels. If a rectangle is wider, it must be split into several rectangles
+ * and each one should be encoded separately.
+ *
+ */
+
+#define VNC_TIGHT_EXPLICIT_FILTER 0x04
+#define VNC_TIGHT_FILL 0x08
+#define VNC_TIGHT_JPEG 0x09
+#define VNC_TIGHT_PNG 0x0A
+#define VNC_TIGHT_MAX_SUBENCODING 0x0A
+
+/* Filters to improve compression efficiency */
+#define VNC_TIGHT_FILTER_COPY 0x00
+#define VNC_TIGHT_FILTER_PALETTE 0x01
+#define VNC_TIGHT_FILTER_GRADIENT 0x02
+
+/* Note: The following constant should not be changed. */
+#define VNC_TIGHT_MIN_TO_COMPRESS 12
+
+/* The parameters below may be adjusted. */
+#define VNC_TIGHT_MIN_SPLIT_RECT_SIZE 4096
+#define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE 2048
+#define VNC_TIGHT_MAX_SPLIT_TILE_SIZE 16
+
+#define VNC_TIGHT_JPEG_MIN_RECT_SIZE 4096
+#define VNC_TIGHT_DETECT_SUBROW_WIDTH 7
+#define VNC_TIGHT_DETECT_MIN_WIDTH 8
+#define VNC_TIGHT_DETECT_MIN_HEIGHT 8
+
+#endif /* VNC_ENCODING_TIGHT_H */
diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c
new file mode 100644
index 0000000..e32e4cd
--- /dev/null
+++ b/ui/vnc-enc-zlib.c
@@ -0,0 +1,152 @@
+/*
+ * QEMU VNC display driver: zlib encoding
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+
+#define ZALLOC_ALIGNMENT 16
+
+void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size)
+{
+ void *p;
+
+ size *= items;
+ size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
+
+ p = qemu_mallocz(size);
+
+ return (p);
+}
+
+void vnc_zlib_zfree(void *x, void *addr)
+{
+ qemu_free(addr);
+}
+
+static void vnc_zlib_start(VncState *vs)
+{
+ buffer_reset(&vs->zlib.zlib);
+
+ // make the output buffer be the zlib buffer, so we can compress it later
+ vs->zlib.tmp = vs->output;
+ vs->output = vs->zlib.zlib;
+}
+
+static int vnc_zlib_stop(VncState *vs)
+{
+ z_streamp zstream = &vs->zlib.stream;
+ int previous_out;
+
+ // switch back to normal output/zlib buffers
+ vs->zlib.zlib = vs->output;
+ vs->output = vs->zlib.tmp;
+
+ // compress the zlib buffer
+
+ // initialize the stream
+ // XXX need one stream per session
+ if (zstream->opaque != vs) {
+ int err;
+
+ VNC_DEBUG("VNC: initializing zlib stream\n");
+ VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs);
+ zstream->zalloc = vnc_zlib_zalloc;
+ zstream->zfree = vnc_zlib_zfree;
+
+ err = deflateInit2(zstream, vs->tight.compression, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+ if (err != Z_OK) {
+ fprintf(stderr, "VNC: error initializing zlib\n");
+ return -1;
+ }
+
+ vs->zlib.level = vs->tight.compression;
+ zstream->opaque = vs;
+ }
+
+ if (vs->tight.compression != vs->zlib.level) {
+ if (deflateParams(zstream, vs->tight.compression,
+ Z_DEFAULT_STRATEGY) != Z_OK) {
+ return -1;
+ }
+ vs->zlib.level = vs->tight.compression;
+ }
+
+ // reserve memory in output buffer
+ buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
+
+ // set pointers
+ zstream->next_in = vs->zlib.zlib.buffer;
+ zstream->avail_in = vs->zlib.zlib.offset;
+ zstream->next_out = vs->output.buffer + vs->output.offset;
+ zstream->avail_out = vs->output.capacity - vs->output.offset;
+ previous_out = zstream->avail_out;
+ zstream->data_type = Z_BINARY;
+
+ // start encoding
+ if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+ fprintf(stderr, "VNC: error during zlib compression\n");
+ return -1;
+ }
+
+ vs->output.offset = vs->output.capacity - zstream->avail_out;
+ return previous_out - zstream->avail_out;
+}
+
+int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ int old_offset, new_offset, bytes_written;
+
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_ZLIB);
+
+ // remember where we put in the follow-up size
+ old_offset = vs->output.offset;
+ vnc_write_s32(vs, 0);
+
+ // compress the stream
+ vnc_zlib_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ bytes_written = vnc_zlib_stop(vs);
+
+ if (bytes_written == -1)
+ return 0;
+
+ // hack in the size
+ new_offset = vs->output.offset;
+ vs->output.offset = old_offset;
+ vnc_write_u32(vs, bytes_written);
+ vs->output.offset = new_offset;
+
+ return 1;
+}
+
+void vnc_zlib_clear(VncState *vs)
+{
+ if (vs->zlib.stream.opaque) {
+ deflateEnd(&vs->zlib.stream);
+ }
+ buffer_free(&vs->zlib.zlib);
+}
diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c
new file mode 100644
index 0000000..0b5d750
--- /dev/null
+++ b/ui/vnc-jobs-async.c
@@ -0,0 +1,335 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+/*
+ * Locking:
+ *
+ * There is three levels of locking:
+ * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
+ * - VncDisplay global lock: mainly used for framebuffer updates to avoid
+ * screen corruption if the framebuffer is updated
+ * while the worker is doing something.
+ * - VncState::output lock: used to make sure the output buffer is not corrupted
+ * if two threads try to write on it at the same time
+ *
+ * While the VNC worker thread is working, the VncDisplay global lock is hold
+ * to avoid screen corruptions (this does not block vnc_refresh() because it
+ * uses trylock()) but the output lock is not hold because the thread work on
+ * its own output buffer.
+ * When the encoding job is done, the worker thread will hold the output lock
+ * and copy its output buffer in vs->output.
+*/
+
+struct VncJobQueue {
+ QemuCond cond;
+ QemuMutex mutex;
+ QemuThread thread;
+ Buffer buffer;
+ bool exit;
+ QTAILQ_HEAD(, VncJob) jobs;
+};
+
+typedef struct VncJobQueue VncJobQueue;
+
+/*
+ * We use a single global queue, but most of the functions are
+ * already reetrant, so we can easilly add more than one encoding thread
+ */
+static VncJobQueue *queue;
+
+static void vnc_lock_queue(VncJobQueue *queue)
+{
+ qemu_mutex_lock(&queue->mutex);
+}
+
+static void vnc_unlock_queue(VncJobQueue *queue)
+{
+ qemu_mutex_unlock(&queue->mutex);
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+ VncJob *job = qemu_mallocz(sizeof(VncJob));
+
+ job->vs = vs;
+ vnc_lock_queue(queue);
+ QLIST_INIT(&job->rectangles);
+ vnc_unlock_queue(queue);
+ return job;
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+ VncRectEntry *entry = qemu_mallocz(sizeof(VncRectEntry));
+
+ entry->rect.x = x;
+ entry->rect.y = y;
+ entry->rect.w = w;
+ entry->rect.h = h;
+
+ vnc_lock_queue(queue);
+ QLIST_INSERT_HEAD(&job->rectangles, entry, next);
+ vnc_unlock_queue(queue);
+ return 1;
+}
+
+void vnc_job_push(VncJob *job)
+{
+ vnc_lock_queue(queue);
+ if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
+ qemu_free(job);
+ } else {
+ QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
+ qemu_cond_broadcast(&queue->cond);
+ }
+ vnc_unlock_queue(queue);
+}
+
+static bool vnc_has_job_locked(VncState *vs)
+{
+ VncJob *job;
+
+ QTAILQ_FOREACH(job, &queue->jobs, next) {
+ if (job->vs == vs || !vs) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+ bool ret;
+
+ vnc_lock_queue(queue);
+ ret = vnc_has_job_locked(vs);
+ vnc_unlock_queue(queue);
+ return ret;
+}
+
+void vnc_jobs_clear(VncState *vs)
+{
+ VncJob *job, *tmp;
+
+ vnc_lock_queue(queue);
+ QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
+ if (job->vs == vs || !vs) {
+ QTAILQ_REMOVE(&queue->jobs, job, next);
+ }
+ }
+ vnc_unlock_queue(queue);
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+ vnc_lock_queue(queue);
+ while (vnc_has_job_locked(vs)) {
+ qemu_cond_wait(&queue->cond, &queue->mutex);
+ }
+ vnc_unlock_queue(queue);
+}
+
+/*
+ * Copy data for local use
+ */
+static void vnc_async_encoding_start(VncState *orig, VncState *local)
+{
+ local->vnc_encoding = orig->vnc_encoding;
+ local->features = orig->features;
+ local->ds = orig->ds;
+ local->vd = orig->vd;
+ local->write_pixels = orig->write_pixels;
+ local->clientds = orig->clientds;
+ local->tight = orig->tight;
+ local->zlib = orig->zlib;
+ local->hextile = orig->hextile;
+ local->output = queue->buffer;
+ local->csock = -1; /* Don't do any network work on this thread */
+
+ buffer_reset(&local->output);
+}
+
+static void vnc_async_encoding_end(VncState *orig, VncState *local)
+{
+ orig->tight = local->tight;
+ orig->zlib = local->zlib;
+ orig->hextile = local->hextile;
+}
+
+static int vnc_worker_thread_loop(VncJobQueue *queue)
+{
+ VncJob *job;
+ VncRectEntry *entry, *tmp;
+ VncState vs;
+ int n_rectangles;
+ int saved_offset;
+ bool flush;
+
+ vnc_lock_queue(queue);
+ while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
+ qemu_cond_wait(&queue->cond, &queue->mutex);
+ }
+ /* Here job can only be NULL if queue->exit is true */
+ job = QTAILQ_FIRST(&queue->jobs);
+ vnc_unlock_queue(queue);
+
+ if (queue->exit) {
+ return -1;
+ }
+
+ vnc_lock_output(job->vs);
+ if (job->vs->csock == -1 || job->vs->abort == true) {
+ goto disconnected;
+ }
+ vnc_unlock_output(job->vs);
+
+ /* Make a local copy of vs and switch output buffers */
+ vnc_async_encoding_start(job->vs, &vs);
+
+ /* Start sending rectangles */
+ n_rectangles = 0;
+ vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(&vs, 0);
+ saved_offset = vs.output.offset;
+ vnc_write_u16(&vs, 0);
+
+ vnc_lock_display(job->vs->vd);
+ QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
+ int n;
+
+ if (job->vs->csock == -1) {
+ vnc_unlock_display(job->vs->vd);
+ /* output mutex must be locked before going to
+ * disconnected:
+ */
+ vnc_lock_output(job->vs);
+ goto disconnected;
+ }
+
+ n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
+ entry->rect.w, entry->rect.h);
+
+ if (n >= 0) {
+ n_rectangles += n;
+ }
+ qemu_free(entry);
+ }
+ vnc_unlock_display(job->vs->vd);
+
+ /* Put n_rectangles at the beginning of the message */
+ vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+
+ /* Switch back buffers */
+ vnc_lock_output(job->vs);
+ if (job->vs->csock == -1) {
+ goto disconnected;
+ }
+
+ vnc_write(job->vs, vs.output.buffer, vs.output.offset);
+
+disconnected:
+ /* Copy persistent encoding data */
+ vnc_async_encoding_end(job->vs, &vs);
+ flush = (job->vs->csock != -1 && job->vs->abort != true);
+ vnc_unlock_output(job->vs);
+
+ if (flush) {
+ vnc_flush(job->vs);
+ }
+
+ vnc_lock_queue(queue);
+ QTAILQ_REMOVE(&queue->jobs, job, next);
+ vnc_unlock_queue(queue);
+ qemu_cond_broadcast(&queue->cond);
+ qemu_free(job);
+ return 0;
+}
+
+static VncJobQueue *vnc_queue_init(void)
+{
+ VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
+
+ qemu_cond_init(&queue->cond);
+ qemu_mutex_init(&queue->mutex);
+ QTAILQ_INIT(&queue->jobs);
+ return queue;
+}
+
+static void vnc_queue_clear(VncJobQueue *q)
+{
+ qemu_cond_destroy(&queue->cond);
+ qemu_mutex_destroy(&queue->mutex);
+ buffer_free(&queue->buffer);
+ qemu_free(q);
+ queue = NULL; /* Unset global queue */
+}
+
+static void *vnc_worker_thread(void *arg)
+{
+ VncJobQueue *queue = arg;
+
+ qemu_thread_self(&queue->thread);
+
+ while (!vnc_worker_thread_loop(queue)) ;
+ vnc_queue_clear(queue);
+ return NULL;
+}
+
+void vnc_start_worker_thread(void)
+{
+ VncJobQueue *q;
+
+ if (vnc_worker_thread_running())
+ return ;
+
+ q = vnc_queue_init();
+ qemu_thread_create(&q->thread, vnc_worker_thread, q);
+ queue = q; /* Set global queue */
+}
+
+bool vnc_worker_thread_running(void)
+{
+ return queue; /* Check global queue */
+}
+
+void vnc_stop_worker_thread(void)
+{
+ if (!vnc_worker_thread_running())
+ return ;
+
+ /* Remove all jobs and wake up the thread */
+ vnc_lock_queue(queue);
+ queue->exit = true;
+ vnc_unlock_queue(queue);
+ vnc_jobs_clear(NULL);
+ qemu_cond_broadcast(&queue->cond);
+}
diff --git a/ui/vnc-jobs-sync.c b/ui/vnc-jobs-sync.c
new file mode 100644
index 0000000..49b77af
--- /dev/null
+++ b/ui/vnc-jobs-sync.c
@@ -0,0 +1,73 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+
+void vnc_jobs_clear(VncState *vs)
+{
+}
+
+void vnc_jobs_join(VncState *vs)
+{
+}
+
+VncJob *vnc_job_new(VncState *vs)
+{
+ vs->job.vs = vs;
+ vs->job.rectangles = 0;
+
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vs->job.saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+ return &vs->job;
+}
+
+void vnc_job_push(VncJob *job)
+{
+ VncState *vs = job->vs;
+
+ vs->output.buffer[job->saved_offset] = (job->rectangles >> 8) & 0xFF;
+ vs->output.buffer[job->saved_offset + 1] = job->rectangles & 0xFF;
+ vnc_flush(job->vs);
+}
+
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
+{
+ int n;
+
+ n = vnc_send_framebuffer_update(job->vs, x, y, w, h);
+ if (n >= 0)
+ job->rectangles += n;
+ return n;
+}
+
+bool vnc_has_job(VncState *vs)
+{
+ return false;
+}
diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h
new file mode 100644
index 0000000..b8dab81
--- /dev/null
+++ b/ui/vnc-jobs.h
@@ -0,0 +1,87 @@
+/*
+ * QEMU VNC display driver
+ *
+ * From libvncserver/rfb/rfbproto.h
+ * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_JOBS_H
+#define VNC_JOBS_H
+
+/* Jobs */
+VncJob *vnc_job_new(VncState *vs);
+int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h);
+void vnc_job_push(VncJob *job);
+bool vnc_has_job(VncState *vs);
+void vnc_jobs_clear(VncState *vs);
+void vnc_jobs_join(VncState *vs);
+
+#ifdef CONFIG_VNC_THREAD
+
+void vnc_start_worker_thread(void);
+bool vnc_worker_thread_running(void);
+void vnc_stop_worker_thread(void);
+
+#endif /* CONFIG_VNC_THREAD */
+
+/* Locks */
+static inline int vnc_trylock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ return qemu_mutex_trylock(&vd->mutex);
+#else
+ return 0;
+#endif
+}
+
+static inline void vnc_lock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_lock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_unlock_display(VncDisplay *vd)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_unlock(&vd->mutex);
+#endif
+}
+
+static inline void vnc_lock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_lock(&vs->output_mutex);
+#endif
+}
+
+static inline void vnc_unlock_output(VncState *vs)
+{
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_unlock(&vs->output_mutex);
+#endif
+}
+
+#endif /* VNC_JOBS_H */
diff --git a/ui/vnc-palette.c b/ui/vnc-palette.c
new file mode 100644
index 0000000..bff6445
--- /dev/null
+++ b/ui/vnc-palette.c
@@ -0,0 +1,136 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc-palette.h"
+
+static VncPaletteEntry *palette_find(const VncPalette *palette,
+ uint32_t color, unsigned int hash)
+{
+ VncPaletteEntry *entry;
+
+ QLIST_FOREACH(entry, &palette->table[hash], next) {
+ if (entry->color == color) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static unsigned int palette_hash(uint32_t rgb, int bpp)
+{
+ if (bpp == 16) {
+ return ((unsigned int)(((rgb >> 8) + rgb) & 0xFF));
+ } else {
+ return ((unsigned int)(((rgb >> 16) + (rgb >> 8)) & 0xFF));
+ }
+}
+
+VncPalette *palette_new(size_t max, int bpp)
+{
+ VncPalette *palette;
+
+ palette = qemu_mallocz(sizeof(*palette));
+ palette->max = max;
+ palette->bpp = bpp;
+ return palette;
+}
+
+void palette_destroy(VncPalette *palette)
+{
+ int i;
+
+ if (palette == NULL) {
+ return ;
+ }
+
+ for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+ VncPaletteEntry *entry = QLIST_FIRST(&palette->table[i]);
+ while (entry) {
+ VncPaletteEntry *tmp = QLIST_NEXT(entry, next);
+ QLIST_REMOVE(entry, next);
+ qemu_free(entry);
+ entry = tmp;
+ }
+ }
+
+ qemu_free(palette);
+}
+
+int palette_put(VncPalette *palette, uint32_t color)
+{
+ unsigned int hash;
+ unsigned int idx = palette->size;
+ VncPaletteEntry *entry;
+
+ hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+ entry = palette_find(palette, color, hash);
+
+ if (!entry && palette->size >= palette->max) {
+ return 0;
+ }
+ if (!entry) {
+ VncPaletteEntry *entry;
+
+ entry = qemu_mallocz(sizeof(*entry));
+ entry->color = color;
+ entry->idx = idx;
+ QLIST_INSERT_HEAD(&palette->table[hash], entry, next);
+ palette->size++;
+ }
+ return palette->size;
+}
+
+int palette_idx(const VncPalette *palette, uint32_t color)
+{
+ VncPaletteEntry *entry;
+ unsigned int hash;
+
+ hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE;
+ entry = palette_find(palette, color, hash);
+ return (entry == NULL ? -1 : entry->idx);
+}
+
+size_t palette_size(const VncPalette *palette)
+{
+ return palette->size;
+}
+
+void palette_iter(const VncPalette *palette,
+ void (*iter)(int idx, uint32_t color, void *opaque),
+ void *opaque)
+{
+ int i;
+ VncPaletteEntry *entry;
+
+ for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) {
+ QLIST_FOREACH(entry, &palette->table[i], next) {
+ iter(entry->idx, entry->color, opaque);
+ }
+ }
+}
diff --git a/ui/vnc-palette.h b/ui/vnc-palette.h
new file mode 100644
index 0000000..d0645eb
--- /dev/null
+++ b/ui/vnc-palette.h
@@ -0,0 +1,63 @@
+/*
+ * QEMU VNC display driver: palette hash table
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef VNC_PALETTE_H
+#define VNC_PALETTE_H
+
+#include "qlist.h"
+#include "qemu-queue.h"
+#include <stdint.h>
+
+#define VNC_PALETTE_HASH_SIZE 256
+
+typedef struct VncPaletteEntry {
+ int idx;
+ uint32_t color;
+ QLIST_ENTRY(VncPaletteEntry) next;
+} VncPaletteEntry;
+
+typedef struct VncPalette {
+ QObject_HEAD;
+ size_t size;
+ size_t max;
+ int bpp;
+ QLIST_HEAD(,VncPaletteEntry) table[VNC_PALETTE_HASH_SIZE];
+} VncPalette;
+
+VncPalette *palette_new(size_t max, int bpp);
+void palette_destroy(VncPalette *palette);
+
+int palette_put(VncPalette *palette, uint32_t color);
+int palette_idx(const VncPalette *palette, uint32_t color);
+size_t palette_size(const VncPalette *palette);
+
+void palette_iter(const VncPalette *palette,
+ void (*iter)(int idx, uint32_t color, void *opaque),
+ void *opaque);
+
+#endif /* VNC_PALETTE_H */
diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
new file mode 100644
index 0000000..dec626c
--- /dev/null
+++ b/ui/vnc-tls.c
@@ -0,0 +1,445 @@
+/*
+ * QEMU VNC display driver: TLS helpers
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-x509.h"
+#include "vnc.h"
+#include "qemu_socket.h"
+
+#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
+/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
+static void vnc_debug_gnutls_log(int level, const char* str) {
+ VNC_DEBUG("%d %s", level, str);
+}
+#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
+
+
+#define DH_BITS 1024
+static gnutls_dh_params_t dh_params;
+
+static int vnc_tls_initialize(void)
+{
+ static int tlsinitialized = 0;
+
+ if (tlsinitialized)
+ return 1;
+
+ if (gnutls_global_init () < 0)
+ return 0;
+
+ /* XXX ought to re-generate diffie-hellmen params periodically */
+ if (gnutls_dh_params_init (&dh_params) < 0)
+ return 0;
+ if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
+ return 0;
+
+#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
+ gnutls_global_set_log_level(10);
+ gnutls_global_set_log_function(vnc_debug_gnutls_log);
+#endif
+
+ tlsinitialized = 1;
+
+ return 1;
+}
+
+static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
+ const void *data,
+ size_t len) {
+ struct VncState *vs = (struct VncState *)transport;
+ int ret;
+
+ retry:
+ ret = send(vs->csock, data, len, 0);
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto retry;
+ return -1;
+ }
+ return ret;
+}
+
+
+static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
+ void *data,
+ size_t len) {
+ struct VncState *vs = (struct VncState *)transport;
+ int ret;
+
+ retry:
+ ret = recv(vs->csock, data, len, 0);
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto retry;
+ return -1;
+ }
+ return ret;
+}
+
+
+static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
+{
+ gnutls_anon_server_credentials anon_cred;
+ int ret;
+
+ if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
+ VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
+ return NULL;
+ }
+
+ gnutls_anon_set_server_dh_params(anon_cred, dh_params);
+
+ return anon_cred;
+}
+
+
+static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
+{
+ gnutls_certificate_credentials_t x509_cred;
+ int ret;
+
+ if (!vd->tls.x509cacert) {
+ VNC_DEBUG("No CA x509 certificate specified\n");
+ return NULL;
+ }
+ if (!vd->tls.x509cert) {
+ VNC_DEBUG("No server x509 certificate specified\n");
+ return NULL;
+ }
+ if (!vd->tls.x509key) {
+ VNC_DEBUG("No server private key specified\n");
+ return NULL;
+ }
+
+ if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
+ VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
+ return NULL;
+ }
+ if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
+ vd->tls.x509cacert,
+ GNUTLS_X509_FMT_PEM)) < 0) {
+ VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
+ gnutls_certificate_free_credentials(x509_cred);
+ return NULL;
+ }
+
+ if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
+ vd->tls.x509cert,
+ vd->tls.x509key,
+ GNUTLS_X509_FMT_PEM)) < 0) {
+ VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
+ gnutls_certificate_free_credentials(x509_cred);
+ return NULL;
+ }
+
+ if (vd->tls.x509cacrl) {
+ if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
+ vd->tls.x509cacrl,
+ GNUTLS_X509_FMT_PEM)) < 0) {
+ VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
+ gnutls_certificate_free_credentials(x509_cred);
+ return NULL;
+ }
+ }
+
+ gnutls_certificate_set_dh_params (x509_cred, dh_params);
+
+ return x509_cred;
+}
+
+
+int vnc_tls_validate_certificate(struct VncState *vs)
+{
+ int ret;
+ unsigned int status;
+ const gnutls_datum_t *certs;
+ unsigned int nCerts, i;
+ time_t now;
+
+ VNC_DEBUG("Validating client certificate\n");
+ if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
+ VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
+ return -1;
+ }
+
+ if ((now = time(NULL)) == ((time_t)-1)) {
+ return -1;
+ }
+
+ if (status != 0) {
+ if (status & GNUTLS_CERT_INVALID)
+ VNC_DEBUG("The certificate is not trusted.\n");
+
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ VNC_DEBUG("The certificate hasn't got a known issuer.\n");
+
+ if (status & GNUTLS_CERT_REVOKED)
+ VNC_DEBUG("The certificate has been revoked.\n");
+
+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ VNC_DEBUG("The certificate uses an insecure algorithm\n");
+
+ return -1;
+ } else {
+ VNC_DEBUG("Certificate is valid!\n");
+ }
+
+ /* Only support x509 for now */
+ if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
+ return -1;
+
+ if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
+ return -1;
+
+ for (i = 0 ; i < nCerts ; i++) {
+ gnutls_x509_crt_t cert;
+ VNC_DEBUG ("Checking certificate chain %d\n", i);
+ if (gnutls_x509_crt_init (&cert) < 0)
+ return -1;
+
+ if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_expiration_time (cert) < now) {
+ VNC_DEBUG("The certificate has expired\n");
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_activation_time (cert) > now) {
+ VNC_DEBUG("The certificate is not yet activated\n");
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (gnutls_x509_crt_get_activation_time (cert) > now) {
+ VNC_DEBUG("The certificate is not yet activated\n");
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+
+ if (i == 0) {
+ size_t dnameSize = 1024;
+ vs->tls.dname = qemu_malloc(dnameSize);
+ requery:
+ if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize);
+ goto requery;
+ }
+ gnutls_x509_crt_deinit (cert);
+ VNC_DEBUG("Cannot get client distinguished name: %s",
+ gnutls_strerror (ret));
+ return -1;
+ }
+
+ if (vs->vd->tls.x509verify) {
+ int allow;
+ if (!vs->vd->tls.acl) {
+ VNC_DEBUG("no ACL activated, allowing access");
+ gnutls_x509_crt_deinit (cert);
+ continue;
+ }
+
+ allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
+ vs->tls.dname);
+
+ VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
+ vs->tls.dname, allow ? "allowed" : "denied");
+ if (!allow) {
+ gnutls_x509_crt_deinit (cert);
+ return -1;
+ }
+ }
+ }
+
+ gnutls_x509_crt_deinit (cert);
+ }
+
+ return 0;
+}
+
+
+int vnc_tls_client_setup(struct VncState *vs,
+ int needX509Creds) {
+ static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+ static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
+ static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
+ static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
+
+ VNC_DEBUG("Do TLS setup\n");
+ if (vnc_tls_initialize() < 0) {
+ VNC_DEBUG("Failed to init TLS\n");
+ vnc_client_error(vs);
+ return -1;
+ }
+ if (vs->tls.session == NULL) {
+ if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (gnutls_set_default_priority(vs->tls.session) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (needX509Creds) {
+ gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd);
+ if (!x509_cred) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+ if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ gnutls_certificate_free_credentials(x509_cred);
+ vnc_client_error(vs);
+ return -1;
+ }
+ if (vs->vd->tls.x509verify) {
+ VNC_DEBUG("Requesting a client certificate\n");
+ gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST);
+ }
+
+ } else {
+ gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
+ if (!anon_cred) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+ if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ gnutls_anon_free_server_credentials(anon_cred);
+ vnc_client_error(vs);
+ return -1;
+ }
+ }
+
+ gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
+ gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
+ gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
+ }
+ return 0;
+}
+
+
+void vnc_tls_client_cleanup(struct VncState *vs)
+{
+ if (vs->tls.session) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ }
+ vs->tls.wiremode = VNC_WIREMODE_CLEAR;
+ free(vs->tls.dname);
+}
+
+
+
+static int vnc_set_x509_credential(VncDisplay *vd,
+ const char *certdir,
+ const char *filename,
+ char **cred,
+ int ignoreMissing)
+{
+ struct stat sb;
+
+ if (*cred) {
+ qemu_free(*cred);
+ *cred = NULL;
+ }
+
+ *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2);
+
+ strcpy(*cred, certdir);
+ strcat(*cred, "/");
+ strcat(*cred, filename);
+
+ VNC_DEBUG("Check %s\n", *cred);
+ if (stat(*cred, &sb) < 0) {
+ qemu_free(*cred);
+ *cred = NULL;
+ if (ignoreMissing && errno == ENOENT)
+ return 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
+ const char *certdir)
+{
+ if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
+ goto cleanup;
+ if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
+ goto cleanup;
+ if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
+ goto cleanup;
+ if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
+ goto cleanup;
+
+ return 0;
+
+ cleanup:
+ qemu_free(vd->tls.x509cacert);
+ qemu_free(vd->tls.x509cacrl);
+ qemu_free(vd->tls.x509cert);
+ qemu_free(vd->tls.x509key);
+ vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
+ return -1;
+}
+
diff --git a/ui/vnc-tls.h b/ui/vnc-tls.h
new file mode 100644
index 0000000..2b93633
--- /dev/null
+++ b/ui/vnc-tls.h
@@ -0,0 +1,76 @@
+/*
+ * QEMU VNC display driver. TLS helpers
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+#ifndef __QEMU_VNC_TLS_H__
+#define __QEMU_VNC_TLS_H__
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "acl.h"
+
+enum {
+ VNC_WIREMODE_CLEAR,
+ VNC_WIREMODE_TLS,
+};
+
+typedef struct VncDisplayTLS VncDisplayTLS;
+typedef struct VncStateTLS VncStateTLS;
+
+/* Server state */
+struct VncDisplayTLS {
+ int x509verify; /* Non-zero if server requests & validates client cert */
+ qemu_acl *acl;
+
+ /* Paths to x509 certs/keys */
+ char *x509cacert;
+ char *x509cacrl;
+ char *x509cert;
+ char *x509key;
+};
+
+/* Per client state */
+struct VncStateTLS {
+ /* Whether data is being TLS encrypted yet */
+ int wiremode;
+ gnutls_session_t session;
+
+ /* Client's Distinguished Name from the x509 cert */
+ char *dname;
+};
+
+int vnc_tls_client_setup(VncState *vs, int x509Creds);
+void vnc_tls_client_cleanup(VncState *vs);
+
+int vnc_tls_validate_certificate(VncState *vs);
+
+int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
+ const char *path);
+
+
+#endif /* __QEMU_VNC_TLS_H__ */
+
diff --git a/ui/vnc.c b/ui/vnc.c
new file mode 100644
index 0000000..f4fea04
--- /dev/null
+++ b/ui/vnc.c
@@ -0,0 +1,2768 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "vnc.h"
+#include "vnc-jobs.h"
+#include "sysemu.h"
+#include "qemu_socket.h"
+#include "qemu-timer.h"
+#include "acl.h"
+#include "qemu-objects.h"
+
+#define VNC_REFRESH_INTERVAL_BASE 30
+#define VNC_REFRESH_INTERVAL_INC 50
+#define VNC_REFRESH_INTERVAL_MAX 2000
+
+#include "vnc_keysym.h"
+#include "d3des.h"
+
+#define count_bits(c, v) { \
+ for (c = 0; v; v >>= 1) \
+ { \
+ c += v & 1; \
+ } \
+}
+
+static VncDisplay *vnc_display; /* needed for info vnc */
+static DisplayChangeListener *dcl;
+
+static int vnc_cursor_define(VncState *vs);
+
+static char *addr_to_string(const char *format,
+ struct sockaddr_storage *sa,
+ socklen_t salen) {
+ char *addr;
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ int err;
+ size_t addrlen;
+
+ if ((err = getnameinfo((struct sockaddr *)sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
+ VNC_DEBUG("Cannot resolve address %d: %s\n",
+ err, gai_strerror(err));
+ return NULL;
+ }
+
+ /* Enough for the existing format + the 2 vars we're
+ * substituting in. */
+ addrlen = strlen(format) + strlen(host) + strlen(serv);
+ addr = qemu_malloc(addrlen + 1);
+ snprintf(addr, addrlen, format, host, serv);
+ addr[addrlen] = '\0';
+
+ return addr;
+}
+
+
+char *vnc_socket_local_addr(const char *format, int fd) {
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
+ return NULL;
+
+ return addr_to_string(format, &sa, salen);
+}
+
+char *vnc_socket_remote_addr(const char *format, int fd) {
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
+ return NULL;
+
+ return addr_to_string(format, &sa, salen);
+}
+
+static int put_addr_qdict(QDict *qdict, struct sockaddr_storage *sa,
+ socklen_t salen)
+{
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ int err;
+
+ if ((err = getnameinfo((struct sockaddr *)sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
+ VNC_DEBUG("Cannot resolve address %d: %s\n",
+ err, gai_strerror(err));
+ return -1;
+ }
+
+ qdict_put(qdict, "host", qstring_from_str(host));
+ qdict_put(qdict, "service", qstring_from_str(serv));
+ qdict_put(qdict, "family",qstring_from_str(inet_strfamily(sa->ss_family)));
+
+ return 0;
+}
+
+static int vnc_server_addr_put(QDict *qdict, int fd)
+{
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
+ return -1;
+ }
+
+ return put_addr_qdict(qdict, &sa, salen);
+}
+
+static int vnc_qdict_remote_addr(QDict *qdict, int fd)
+{
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
+ return -1;
+ }
+
+ return put_addr_qdict(qdict, &sa, salen);
+}
+
+static const char *vnc_auth_name(VncDisplay *vd) {
+ switch (vd->auth) {
+ case VNC_AUTH_INVALID:
+ return "invalid";
+ case VNC_AUTH_NONE:
+ return "none";
+ case VNC_AUTH_VNC:
+ return "vnc";
+ case VNC_AUTH_RA2:
+ return "ra2";
+ case VNC_AUTH_RA2NE:
+ return "ra2ne";
+ case VNC_AUTH_TIGHT:
+ return "tight";
+ case VNC_AUTH_ULTRA:
+ return "ultra";
+ case VNC_AUTH_TLS:
+ return "tls";
+ case VNC_AUTH_VENCRYPT:
+#ifdef CONFIG_VNC_TLS
+ switch (vd->subauth) {
+ case VNC_AUTH_VENCRYPT_PLAIN:
+ return "vencrypt+plain";
+ case VNC_AUTH_VENCRYPT_TLSNONE:
+ return "vencrypt+tls+none";
+ case VNC_AUTH_VENCRYPT_TLSVNC:
+ return "vencrypt+tls+vnc";
+ case VNC_AUTH_VENCRYPT_TLSPLAIN:
+ return "vencrypt+tls+plain";
+ case VNC_AUTH_VENCRYPT_X509NONE:
+ return "vencrypt+x509+none";
+ case VNC_AUTH_VENCRYPT_X509VNC:
+ return "vencrypt+x509+vnc";
+ case VNC_AUTH_VENCRYPT_X509PLAIN:
+ return "vencrypt+x509+plain";
+ case VNC_AUTH_VENCRYPT_TLSSASL:
+ return "vencrypt+tls+sasl";
+ case VNC_AUTH_VENCRYPT_X509SASL:
+ return "vencrypt+x509+sasl";
+ default:
+ return "vencrypt";
+ }
+#else
+ return "vencrypt";
+#endif
+ case VNC_AUTH_SASL:
+ return "sasl";
+ }
+ return "unknown";
+}
+
+static int vnc_server_info_put(QDict *qdict)
+{
+ if (vnc_server_addr_put(qdict, vnc_display->lsock) < 0) {
+ return -1;
+ }
+
+ qdict_put(qdict, "auth", qstring_from_str(vnc_auth_name(vnc_display)));
+ return 0;
+}
+
+static void vnc_client_cache_auth(VncState *client)
+{
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
+ QDict *qdict;
+#endif
+
+ if (!client->info) {
+ return;
+ }
+
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
+ qdict = qobject_to_qdict(client->info);
+#endif
+
+#ifdef CONFIG_VNC_TLS
+ if (client->tls.session &&
+ client->tls.dname) {
+ qdict_put(qdict, "x509_dname", qstring_from_str(client->tls.dname));
+ }
+#endif
+#ifdef CONFIG_VNC_SASL
+ if (client->sasl.conn &&
+ client->sasl.username) {
+ qdict_put(qdict, "sasl_username",
+ qstring_from_str(client->sasl.username));
+ }
+#endif
+}
+
+static void vnc_client_cache_addr(VncState *client)
+{
+ QDict *qdict;
+
+ qdict = qdict_new();
+ if (vnc_qdict_remote_addr(qdict, client->csock) < 0) {
+ QDECREF(qdict);
+ /* XXX: how to report the error? */
+ return;
+ }
+
+ client->info = QOBJECT(qdict);
+}
+
+static void vnc_qmp_event(VncState *vs, MonitorEvent event)
+{
+ QDict *server;
+ QObject *data;
+
+ if (!vs->info) {
+ return;
+ }
+
+ server = qdict_new();
+ if (vnc_server_info_put(server) < 0) {
+ QDECREF(server);
+ return;
+ }
+
+ data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
+ vs->info, QOBJECT(server));
+
+ monitor_protocol_event(event, data);
+
+ qobject_incref(vs->info);
+ qobject_decref(data);
+}
+
+static void info_vnc_iter(QObject *obj, void *opaque)
+{
+ QDict *client;
+ Monitor *mon = opaque;
+
+ client = qobject_to_qdict(obj);
+ monitor_printf(mon, "Client:\n");
+ monitor_printf(mon, " address: %s:%s\n",
+ qdict_get_str(client, "host"),
+ qdict_get_str(client, "service"));
+
+#ifdef CONFIG_VNC_TLS
+ monitor_printf(mon, " x509_dname: %s\n",
+ qdict_haskey(client, "x509_dname") ?
+ qdict_get_str(client, "x509_dname") : "none");
+#endif
+#ifdef CONFIG_VNC_SASL
+ monitor_printf(mon, " username: %s\n",
+ qdict_haskey(client, "sasl_username") ?
+ qdict_get_str(client, "sasl_username") : "none");
+#endif
+}
+
+void do_info_vnc_print(Monitor *mon, const QObject *data)
+{
+ QDict *server;
+ QList *clients;
+
+ server = qobject_to_qdict(data);
+ if (qdict_get_bool(server, "enabled") == 0) {
+ monitor_printf(mon, "Server: disabled\n");
+ return;
+ }
+
+ monitor_printf(mon, "Server:\n");
+ monitor_printf(mon, " address: %s:%s\n",
+ qdict_get_str(server, "host"),
+ qdict_get_str(server, "service"));
+ monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
+
+ clients = qdict_get_qlist(server, "clients");
+ if (qlist_empty(clients)) {
+ monitor_printf(mon, "Client: none\n");
+ } else {
+ qlist_iter(clients, info_vnc_iter, mon);
+ }
+}
+
+void do_info_vnc(Monitor *mon, QObject **ret_data)
+{
+ if (vnc_display == NULL || vnc_display->display == NULL) {
+ *ret_data = qobject_from_jsonf("{ 'enabled': false }");
+ } else {
+ QList *clist;
+ VncState *client;
+
+ clist = qlist_new();
+ QTAILQ_FOREACH(client, &vnc_display->clients, next) {
+ if (client->info) {
+ /* incref so that it's not freed by upper layers */
+ qobject_incref(client->info);
+ qlist_append_obj(clist, client->info);
+ }
+ }
+
+ *ret_data = qobject_from_jsonf("{ 'enabled': true, 'clients': %p }",
+ QOBJECT(clist));
+ assert(*ret_data != NULL);
+
+ if (vnc_server_info_put(qobject_to_qdict(*ret_data)) < 0) {
+ qobject_decref(*ret_data);
+ *ret_data = NULL;
+ }
+ }
+}
+
+/* TODO
+ 1) Get the queue working for IO.
+ 2) there is some weirdness when using the -S option (the screen is grey
+ and not totally invalidated
+ 3) resolutions > 1024
+*/
+
+static int vnc_update_client(VncState *vs, int has_dirty);
+static int vnc_update_client_sync(VncState *vs, int has_dirty);
+static void vnc_disconnect_start(VncState *vs);
+static void vnc_disconnect_finish(VncState *vs);
+static void vnc_init_timer(VncDisplay *vd);
+static void vnc_remove_timer(VncDisplay *vd);
+
+static void vnc_colordepth(VncState *vs);
+static void framebuffer_update_request(VncState *vs, int incremental,
+ int x_position, int y_position,
+ int w, int h);
+static void vnc_refresh(void *opaque);
+static int vnc_refresh_server_surface(VncDisplay *vd);
+
+static inline void vnc_set_bit(uint32_t *d, int k)
+{
+ d[k >> 5] |= 1 << (k & 0x1f);
+}
+
+static inline void vnc_clear_bit(uint32_t *d, int k)
+{
+ d[k >> 5] &= ~(1 << (k & 0x1f));
+}
+
+static inline void vnc_set_bits(uint32_t *d, int n, int nb_words)
+{
+ int j;
+
+ j = 0;
+ while (n >= 32) {
+ d[j++] = -1;
+ n -= 32;
+ }
+ if (n > 0)
+ d[j++] = (1 << n) - 1;
+ while (j < nb_words)
+ d[j++] = 0;
+}
+
+static inline int vnc_get_bit(const uint32_t *d, int k)
+{
+ return (d[k >> 5] >> (k & 0x1f)) & 1;
+}
+
+static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
+ int nb_words)
+{
+ int i;
+ for(i = 0; i < nb_words; i++) {
+ if ((d1[i] & d2[i]) != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ int i;
+ VncDisplay *vd = ds->opaque;
+ struct VncSurface *s = &vd->guest;
+
+ h += y;
+
+ /* round x down to ensure the loop only spans one 16-pixel block per,
+ iteration. otherwise, if (x % 16) != 0, the last iteration may span
+ two 16-pixel blocks but we only mark the first as dirty
+ */
+ w += (x % 16);
+ x -= (x % 16);
+
+ x = MIN(x, s->ds->width);
+ y = MIN(y, s->ds->height);
+ w = MIN(x + w, s->ds->width) - x;
+ h = MIN(h, s->ds->height);
+
+ for (; y < h; y++)
+ for (i = 0; i < w; i += 16)
+ vnc_set_bit(s->dirty[y], (x + i) / 16);
+}
+
+void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+ int32_t encoding)
+{
+ vnc_write_u16(vs, x);
+ vnc_write_u16(vs, y);
+ vnc_write_u16(vs, w);
+ vnc_write_u16(vs, h);
+
+ vnc_write_s32(vs, encoding);
+}
+
+void buffer_reserve(Buffer *buffer, size_t len)
+{
+ if ((buffer->capacity - buffer->offset) < len) {
+ buffer->capacity += (len + 1024);
+ buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity);
+ if (buffer->buffer == NULL) {
+ fprintf(stderr, "vnc: out of memory\n");
+ exit(1);
+ }
+ }
+}
+
+int buffer_empty(Buffer *buffer)
+{
+ return buffer->offset == 0;
+}
+
+uint8_t *buffer_end(Buffer *buffer)
+{
+ return buffer->buffer + buffer->offset;
+}
+
+void buffer_reset(Buffer *buffer)
+{
+ buffer->offset = 0;
+}
+
+void buffer_free(Buffer *buffer)
+{
+ qemu_free(buffer->buffer);
+ buffer->offset = 0;
+ buffer->capacity = 0;
+ buffer->buffer = NULL;
+}
+
+void buffer_append(Buffer *buffer, const void *data, size_t len)
+{
+ memcpy(buffer->buffer + buffer->offset, data, len);
+ buffer->offset += len;
+}
+
+static void vnc_desktop_resize(VncState *vs)
+{
+ DisplayState *ds = vs->ds;
+
+ if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
+ return;
+ }
+ if (vs->client_width == ds_get_width(ds) &&
+ vs->client_height == ds_get_height(ds)) {
+ return;
+ }
+ vs->client_width = ds_get_width(ds);
+ vs->client_height = ds_get_height(ds);
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
+ VNC_ENCODING_DESKTOPRESIZE);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+}
+
+#ifdef CONFIG_VNC_THREAD
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+ VncState *vs;
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = true;
+ vnc_unlock_output(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_jobs_join(vs);
+ }
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_lock_output(vs);
+ vs->abort = false;
+ vnc_unlock_output(vs);
+ }
+}
+#else
+static void vnc_abort_display_jobs(VncDisplay *vd)
+{
+}
+#endif
+
+static void vnc_dpy_resize(DisplayState *ds)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs;
+
+ vnc_abort_display_jobs(vd);
+
+ /* server surface */
+ if (!vd->server)
+ vd->server = qemu_mallocz(sizeof(*vd->server));
+ if (vd->server->data)
+ qemu_free(vd->server->data);
+ *(vd->server) = *(ds->surface);
+ vd->server->data = qemu_mallocz(vd->server->linesize *
+ vd->server->height);
+
+ /* guest surface */
+ if (!vd->guest.ds)
+ vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds));
+ if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel)
+ console_color_init(ds);
+ *(vd->guest.ds) = *(ds->surface);
+ memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty));
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_colordepth(vs);
+ vnc_desktop_resize(vs);
+ if (vs->vd->cursor) {
+ vnc_cursor_define(vs);
+ }
+ memset(vs->dirty, 0xFF, sizeof(vs->dirty));
+ }
+}
+
+/* fastest code */
+static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf,
+ void *pixels, int size)
+{
+ vnc_write(vs, pixels, size);
+}
+
+/* slowest but generic code. */
+void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
+{
+ uint8_t r, g, b;
+ VncDisplay *vd = vs->vd;
+
+ r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >>
+ vd->server->pf.rbits);
+ g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >>
+ vd->server->pf.gbits);
+ b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >>
+ vd->server->pf.bbits);
+ v = (r << vs->clientds.pf.rshift) |
+ (g << vs->clientds.pf.gshift) |
+ (b << vs->clientds.pf.bshift);
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 1:
+ buf[0] = v;
+ break;
+ case 2:
+ if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ buf[0] = v >> 8;
+ buf[1] = v;
+ } else {
+ buf[1] = v >> 8;
+ buf[0] = v;
+ }
+ break;
+ default:
+ case 4:
+ if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ buf[0] = v >> 24;
+ buf[1] = v >> 16;
+ buf[2] = v >> 8;
+ buf[3] = v;
+ } else {
+ buf[3] = v >> 24;
+ buf[2] = v >> 16;
+ buf[1] = v >> 8;
+ buf[0] = v;
+ }
+ break;
+ }
+}
+
+static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf,
+ void *pixels1, int size)
+{
+ uint8_t buf[4];
+
+ if (pf->bytes_per_pixel == 4) {
+ uint32_t *pixels = pixels1;
+ int n, i;
+ n = size >> 2;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ }
+ } else if (pf->bytes_per_pixel == 2) {
+ uint16_t *pixels = pixels1;
+ int n, i;
+ n = size >> 1;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ }
+ } else if (pf->bytes_per_pixel == 1) {
+ uint8_t *pixels = pixels1;
+ int n, i;
+ n = size;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ }
+ } else {
+ fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n");
+ }
+}
+
+int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ int i;
+ uint8_t *row;
+ VncDisplay *vd = vs->vd;
+
+ row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
+ for (i = 0; i < h; i++) {
+ vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds));
+ row += ds_get_linesize(vs->ds);
+ }
+ return 1;
+}
+
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ int n = 0;
+
+ switch(vs->vnc_encoding) {
+ case VNC_ENCODING_ZLIB:
+ n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ case VNC_ENCODING_HEXTILE:
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
+ n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ case VNC_ENCODING_TIGHT:
+ n = vnc_tight_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ case VNC_ENCODING_TIGHT_PNG:
+ n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ default:
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
+ n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ }
+ return n;
+}
+
+static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ /* send bitblit op to the vnc client */
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
+ vnc_write_u16(vs, src_x);
+ vnc_write_u16(vs, src_y);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+}
+
+static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs, *vn;
+ uint8_t *src_row;
+ uint8_t *dst_row;
+ int i,x,y,pitch,depth,inc,w_lim,s;
+ int cmp_bytes;
+
+ vnc_refresh_server_surface(vd);
+ QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
+ vs->force_update = 1;
+ vnc_update_client_sync(vs, 1);
+ /* vs might be free()ed here */
+ }
+ }
+
+ /* do bitblit op on the local surface too */
+ pitch = ds_get_linesize(vd->ds);
+ depth = ds_get_bytes_per_pixel(vd->ds);
+ src_row = vd->server->data + pitch * src_y + depth * src_x;
+ dst_row = vd->server->data + pitch * dst_y + depth * dst_x;
+ y = dst_y;
+ inc = 1;
+ if (dst_y > src_y) {
+ /* copy backwards */
+ src_row += pitch * (h-1);
+ dst_row += pitch * (h-1);
+ pitch = -pitch;
+ y = dst_y + h - 1;
+ inc = -1;
+ }
+ w_lim = w - (16 - (dst_x % 16));
+ if (w_lim < 0)
+ w_lim = w;
+ else
+ w_lim = w - (w_lim % 16);
+ for (i = 0; i < h; i++) {
+ for (x = 0; x <= w_lim;
+ x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
+ if (x == w_lim) {
+ if ((s = w - w_lim) == 0)
+ break;
+ } else if (!x) {
+ s = (16 - (dst_x % 16));
+ s = MIN(s, w_lim);
+ } else {
+ s = 16;
+ }
+ cmp_bytes = s * depth;
+ if (memcmp(src_row, dst_row, cmp_bytes) == 0)
+ continue;
+ memmove(dst_row, src_row, cmp_bytes);
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
+ vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16));
+ }
+ }
+ }
+ src_row += pitch - w * depth;
+ dst_row += pitch - w * depth;
+ y += inc;
+ }
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
+ vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
+ }
+ }
+}
+
+static void vnc_mouse_set(int x, int y, int visible)
+{
+ /* can we ask the client(s) to move the pointer ??? */
+}
+
+static int vnc_cursor_define(VncState *vs)
+{
+ QEMUCursor *c = vs->vd->cursor;
+ PixelFormat pf = qemu_default_pixelformat(32);
+ int isize;
+
+ if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0); /* padding */
+ vnc_write_u16(vs, 1); /* # of rects */
+ vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height,
+ VNC_ENCODING_RICH_CURSOR);
+ isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
+ vnc_write_pixels_generic(vs, &pf, c->data, isize);
+ vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+ vnc_unlock_output(vs);
+ return 0;
+ }
+ return -1;
+}
+
+static void vnc_dpy_cursor_define(QEMUCursor *c)
+{
+ VncDisplay *vd = vnc_display;
+ VncState *vs;
+
+ cursor_put(vd->cursor);
+ qemu_free(vd->cursor_mask);
+
+ vd->cursor = c;
+ cursor_get(vd->cursor);
+ vd->cursor_msize = cursor_get_mono_bpl(c) * c->height;
+ vd->cursor_mask = qemu_mallocz(vd->cursor_msize);
+ cursor_get_mono_mask(c, 0, vd->cursor_mask);
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_cursor_define(vs);
+ }
+}
+
+static int find_and_clear_dirty_height(struct VncState *vs,
+ int y, int last_x, int x)
+{
+ int h;
+ VncDisplay *vd = vs->vd;
+
+ for (h = 1; h < (vd->server->height - y); h++) {
+ int tmp_x;
+ if (!vnc_get_bit(vs->dirty[y + h], last_x))
+ break;
+ for (tmp_x = last_x; tmp_x < x; tmp_x++)
+ vnc_clear_bit(vs->dirty[y + h], tmp_x);
+ }
+
+ return h;
+}
+
+#ifdef CONFIG_VNC_THREAD
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ int ret = vnc_update_client(vs, has_dirty);
+ vnc_jobs_join(vs);
+ return ret;
+}
+#else
+static int vnc_update_client_sync(VncState *vs, int has_dirty)
+{
+ return vnc_update_client(vs, has_dirty);
+}
+#endif
+
+static int vnc_update_client(VncState *vs, int has_dirty)
+{
+ if (vs->need_update && vs->csock != -1) {
+ VncDisplay *vd = vs->vd;
+ VncJob *job;
+ int y;
+ int width, height;
+ int n = 0;
+
+
+ if (vs->output.offset && !vs->audio_cap && !vs->force_update)
+ /* kernel send buffers are full -> drop frames to throttle */
+ return 0;
+
+ if (!has_dirty && !vs->audio_cap && !vs->force_update)
+ return 0;
+
+ /*
+ * Send screen updates to the vnc client using the server
+ * surface and server dirty map. guest surface updates
+ * happening in parallel don't disturb us, the next pass will
+ * send them to the client.
+ */
+ job = vnc_job_new(vs);
+
+ width = MIN(vd->server->width, vs->client_width);
+ height = MIN(vd->server->height, vs->client_height);
+
+ for (y = 0; y < height; y++) {
+ int x;
+ int last_x = -1;
+ for (x = 0; x < width / 16; x++) {
+ if (vnc_get_bit(vs->dirty[y], x)) {
+ if (last_x == -1) {
+ last_x = x;
+ }
+ vnc_clear_bit(vs->dirty[y], x);
+ } else {
+ if (last_x != -1) {
+ int h = find_and_clear_dirty_height(vs, y, last_x, x);
+
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
+ }
+ last_x = -1;
+ }
+ }
+ if (last_x != -1) {
+ int h = find_and_clear_dirty_height(vs, y, last_x, x);
+ n += vnc_job_add_rect(job, last_x * 16, y,
+ (x - last_x) * 16, h);
+ }
+ }
+
+ vnc_job_push(job);
+ vs->force_update = 0;
+ return n;
+ }
+
+ if (vs->csock == -1)
+ vnc_disconnect_finish(vs);
+
+ return 0;
+}
+
+/* audio */
+static void audio_capture_notify(void *opaque, audcnotification_e cmd)
+{
+ VncState *vs = opaque;
+
+ switch (cmd) {
+ case AUD_CNOTIFY_DISABLE:
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+ break;
+
+ case AUD_CNOTIFY_ENABLE:
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+ break;
+ }
+}
+
+static void audio_capture_destroy(void *opaque)
+{
+}
+
+static void audio_capture(void *opaque, void *buf, int size)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
+ vnc_write_u32(vs, size);
+ vnc_write(vs, buf, size);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+}
+
+static void audio_add(VncState *vs)
+{
+ struct audio_capture_ops ops;
+
+ if (vs->audio_cap) {
+ monitor_printf(default_mon, "audio already running\n");
+ return;
+ }
+
+ ops.notify = audio_capture_notify;
+ ops.destroy = audio_capture_destroy;
+ ops.capture = audio_capture;
+
+ vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs);
+ if (!vs->audio_cap) {
+ monitor_printf(default_mon, "Failed to add audio capture\n");
+ }
+}
+
+static void audio_del(VncState *vs)
+{
+ if (vs->audio_cap) {
+ AUD_del_capture(vs->audio_cap, vs);
+ vs->audio_cap = NULL;
+ }
+}
+
+static void vnc_disconnect_start(VncState *vs)
+{
+ if (vs->csock == -1)
+ return;
+ qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+ closesocket(vs->csock);
+ vs->csock = -1;
+}
+
+static void vnc_disconnect_finish(VncState *vs)
+{
+ vnc_jobs_join(vs); /* Wait encoding jobs */
+
+ vnc_lock_output(vs);
+ vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
+
+ buffer_free(&vs->input);
+ buffer_free(&vs->output);
+
+ qobject_decref(vs->info);
+
+ vnc_zlib_clear(vs);
+ vnc_tight_clear(vs);
+
+#ifdef CONFIG_VNC_TLS
+ vnc_tls_client_cleanup(vs);
+#endif /* CONFIG_VNC_TLS */
+#ifdef CONFIG_VNC_SASL
+ vnc_sasl_client_cleanup(vs);
+#endif /* CONFIG_VNC_SASL */
+ audio_del(vs);
+
+ QTAILQ_REMOVE(&vs->vd->clients, vs, next);
+
+ if (QTAILQ_EMPTY(&vs->vd->clients)) {
+ dcl->idle = 1;
+ }
+
+ qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
+ vnc_remove_timer(vs->vd);
+ if (vs->vd->lock_key_sync)
+ qemu_remove_led_event_handler(vs->led);
+ vnc_unlock_output(vs);
+
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_destroy(&vs->output_mutex);
+#endif
+ qemu_free(vs);
+}
+
+int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+{
+ if (ret == 0 || ret == -1) {
+ if (ret == -1) {
+ switch (last_errno) {
+ case EINTR:
+ case EAGAIN:
+#ifdef _WIN32
+ case WSAEWOULDBLOCK:
+#endif
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
+ ret, ret < 0 ? last_errno : 0);
+ vnc_disconnect_start(vs);
+
+ return 0;
+ }
+ return ret;
+}
+
+
+void vnc_client_error(VncState *vs)
+{
+ VNC_DEBUG("Closing down client sock: protocol error\n");
+ vnc_disconnect_start(vs);
+}
+
+
+/*
+ * Called to write a chunk of data to the client socket. The data may
+ * be the raw data, or may have already been encoded by SASL.
+ * The data will be written either straight onto the socket, or
+ * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
+ *
+ * NB, it is theoretically possible to have 2 layers of encryption,
+ * both SASL, and this TLS layer. It is highly unlikely in practice
+ * though, since SASL encryption will typically be a no-op if TLS
+ * is active
+ *
+ * Returns the number of bytes written, which may be less than
+ * the requested 'datalen' if the socket would block. Returns
+ * -1 on error, and disconnects the client socket.
+ */
+long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+{
+ long ret;
+#ifdef CONFIG_VNC_TLS
+ if (vs->tls.session) {
+ ret = gnutls_write(vs->tls.session, data, datalen);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EIO;
+ ret = -1;
+ }
+ } else
+#endif /* CONFIG_VNC_TLS */
+ ret = send(vs->csock, (const void *)data, datalen, 0);
+ VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
+ return vnc_client_io_error(vs, ret, socket_error());
+}
+
+
+/*
+ * Called to write buffered data to the client socket, when not
+ * using any SASL SSF encryption layers. Will write as much data
+ * as possible without blocking. If all buffered data is written,
+ * will switch the FD poll() handler back to read monitoring.
+ *
+ * Returns the number of bytes written, which may be less than
+ * the buffered output data if the socket would block. Returns
+ * -1 on error, and disconnects the client socket.
+ */
+static long vnc_client_write_plain(VncState *vs)
+{
+ long ret;
+
+#ifdef CONFIG_VNC_SASL
+ VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
+ vs->output.buffer, vs->output.capacity, vs->output.offset,
+ vs->sasl.waitWriteSSF);
+
+ if (vs->sasl.conn &&
+ vs->sasl.runSSF &&
+ vs->sasl.waitWriteSSF) {
+ ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
+ if (ret)
+ vs->sasl.waitWriteSSF -= ret;
+ } else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
+ if (!ret)
+ return 0;
+
+ memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
+ vs->output.offset -= ret;
+
+ if (vs->output.offset == 0) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+
+ return ret;
+}
+
+
+/*
+ * First function called whenever there is data to be written to
+ * the client socket. Will delegate actual work according to whether
+ * SASL SSF layers are enabled (thus requiring encryption calls)
+ */
+static void vnc_client_write_locked(void *opaque)
+{
+ VncState *vs = opaque;
+
+#ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn &&
+ vs->sasl.runSSF &&
+ !vs->sasl.waitWriteSSF) {
+ vnc_client_write_sasl(vs);
+ } else
+#endif /* CONFIG_VNC_SASL */
+ vnc_client_write_plain(vs);
+}
+
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+ vnc_lock_output(vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(opaque);
+ } else if (vs->csock != -1) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+ vnc_unlock_output(vs);
+}
+
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+{
+ vs->read_handler = func;
+ vs->read_handler_expect = expecting;
+}
+
+
+/*
+ * Called to read a chunk of data from the client socket. The data may
+ * be the raw data, or may need to be further decoded by SASL.
+ * The data will be read either straight from to the socket, or
+ * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
+ *
+ * NB, it is theoretically possible to have 2 layers of encryption,
+ * both SASL, and this TLS layer. It is highly unlikely in practice
+ * though, since SASL encryption will typically be a no-op if TLS
+ * is active
+ *
+ * Returns the number of bytes read, which may be less than
+ * the requested 'datalen' if the socket would block. Returns
+ * -1 on error, and disconnects the client socket.
+ */
+long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+{
+ long ret;
+#ifdef CONFIG_VNC_TLS
+ if (vs->tls.session) {
+ ret = gnutls_read(vs->tls.session, data, datalen);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EIO;
+ ret = -1;
+ }
+ } else
+#endif /* CONFIG_VNC_TLS */
+ ret = recv(vs->csock, (void *)data, datalen, 0);
+ VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
+ return vnc_client_io_error(vs, ret, socket_error());
+}
+
+
+/*
+ * Called to read data from the client socket to the input buffer,
+ * when not using any SASL SSF encryption layers. Will read as much
+ * data as possible without blocking.
+ *
+ * Returns the number of bytes read. Returns -1 on error, and
+ * disconnects the client socket.
+ */
+static long vnc_client_read_plain(VncState *vs)
+{
+ int ret;
+ VNC_DEBUG("Read plain %p size %zd offset %zd\n",
+ vs->input.buffer, vs->input.capacity, vs->input.offset);
+ buffer_reserve(&vs->input, 4096);
+ ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
+ if (!ret)
+ return 0;
+ vs->input.offset += ret;
+ return ret;
+}
+
+
+/*
+ * First function called whenever there is more data to be read from
+ * the client socket. Will delegate actual work according to whether
+ * SASL SSF layers are enabled (thus requiring decryption calls)
+ */
+void vnc_client_read(void *opaque)
+{
+ VncState *vs = opaque;
+ long ret;
+
+#ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn && vs->sasl.runSSF)
+ ret = vnc_client_read_sasl(vs);
+ else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_read_plain(vs);
+ if (!ret) {
+ if (vs->csock == -1)
+ vnc_disconnect_finish(vs);
+ return;
+ }
+
+ while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+ size_t len = vs->read_handler_expect;
+ int ret;
+
+ ret = vs->read_handler(vs, vs->input.buffer, len);
+ if (vs->csock == -1) {
+ vnc_disconnect_finish(vs);
+ return;
+ }
+
+ if (!ret) {
+ memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
+ vs->input.offset -= len;
+ } else {
+ vs->read_handler_expect = ret;
+ }
+ }
+}
+
+void vnc_write(VncState *vs, const void *data, size_t len)
+{
+ buffer_reserve(&vs->output, len);
+
+ if (vs->csock != -1 && buffer_empty(&vs->output)) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+ }
+
+ buffer_append(&vs->output, data, len);
+}
+
+void vnc_write_s32(VncState *vs, int32_t value)
+{
+ vnc_write_u32(vs, *(uint32_t *)&value);
+}
+
+void vnc_write_u32(VncState *vs, uint32_t value)
+{
+ uint8_t buf[4];
+
+ buf[0] = (value >> 24) & 0xFF;
+ buf[1] = (value >> 16) & 0xFF;
+ buf[2] = (value >> 8) & 0xFF;
+ buf[3] = value & 0xFF;
+
+ vnc_write(vs, buf, 4);
+}
+
+void vnc_write_u16(VncState *vs, uint16_t value)
+{
+ uint8_t buf[2];
+
+ buf[0] = (value >> 8) & 0xFF;
+ buf[1] = value & 0xFF;
+
+ vnc_write(vs, buf, 2);
+}
+
+void vnc_write_u8(VncState *vs, uint8_t value)
+{
+ vnc_write(vs, (char *)&value, 1);
+}
+
+void vnc_flush(VncState *vs)
+{
+ vnc_lock_output(vs);
+ if (vs->csock != -1 && vs->output.offset) {
+ vnc_client_write_locked(vs);
+ }
+ vnc_unlock_output(vs);
+}
+
+uint8_t read_u8(uint8_t *data, size_t offset)
+{
+ return data[offset];
+}
+
+uint16_t read_u16(uint8_t *data, size_t offset)
+{
+ return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
+}
+
+int32_t read_s32(uint8_t *data, size_t offset)
+{
+ return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
+ (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+uint32_t read_u32(uint8_t *data, size_t offset)
+{
+ return ((data[offset] << 24) | (data[offset + 1] << 16) |
+ (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
+{
+}
+
+static void check_pointer_type_change(Notifier *notifier)
+{
+ VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
+ int absolute = kbd_mouse_is_absolute();
+
+ if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, absolute, 0,
+ ds_get_width(vs->ds), ds_get_height(vs->ds),
+ VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+ }
+ vs->absolute = absolute;
+}
+
+static void pointer_event(VncState *vs, int button_mask, int x, int y)
+{
+ int buttons = 0;
+ int dz = 0;
+
+ if (button_mask & 0x01)
+ buttons |= MOUSE_EVENT_LBUTTON;
+ if (button_mask & 0x02)
+ buttons |= MOUSE_EVENT_MBUTTON;
+ if (button_mask & 0x04)
+ buttons |= MOUSE_EVENT_RBUTTON;
+ if (button_mask & 0x08)
+ dz = -1;
+ if (button_mask & 0x10)
+ dz = 1;
+
+ if (vs->absolute) {
+ kbd_mouse_event(ds_get_width(vs->ds) > 1 ?
+ x * 0x7FFF / (ds_get_width(vs->ds) - 1) : 0x4000,
+ ds_get_height(vs->ds) > 1 ?
+ y * 0x7FFF / (ds_get_height(vs->ds) - 1) : 0x4000,
+ dz, buttons);
+ } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
+ x -= 0x7FFF;
+ y -= 0x7FFF;
+
+ kbd_mouse_event(x, y, dz, buttons);
+ } else {
+ if (vs->last_x != -1)
+ kbd_mouse_event(x - vs->last_x,
+ y - vs->last_y,
+ dz, buttons);
+ vs->last_x = x;
+ vs->last_y = y;
+ }
+}
+
+static void reset_keys(VncState *vs)
+{
+ int i;
+ for(i = 0; i < 256; i++) {
+ if (vs->modifiers_state[i]) {
+ if (i & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(i | SCANCODE_UP);
+ vs->modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void press_key(VncState *vs, int keysym)
+{
+ int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(keycode | SCANCODE_UP);
+}
+
+static void kbd_leds(void *opaque, int ledstate)
+{
+ VncState *vs = opaque;
+ int caps, num;
+
+ caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
+ num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0;
+
+ if (vs->modifiers_state[0x3a] != caps) {
+ vs->modifiers_state[0x3a] = caps;
+ }
+ if (vs->modifiers_state[0x45] != num) {
+ vs->modifiers_state[0x45] = num;
+ }
+}
+
+static void do_key_event(VncState *vs, int down, int keycode, int sym)
+{
+ /* QEMU console switch */
+ switch(keycode) {
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ if (down)
+ vs->modifiers_state[keycode] = 1;
+ else
+ vs->modifiers_state[keycode] = 0;
+ break;
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
+ /* Reset the modifiers sent to the current console */
+ reset_keys(vs);
+ console_select(keycode - 0x02);
+ return;
+ }
+ break;
+ case 0x3a: /* CapsLock */
+ case 0x45: /* NumLock */
+ if (down)
+ vs->modifiers_state[keycode] ^= 1;
+ break;
+ }
+
+ if (down && vs->vd->lock_key_sync &&
+ keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
+ /* If the numlock state needs to change then simulate an additional
+ keypress before sending this one. This will happen if the user
+ toggles numlock away from the VNC window.
+ */
+ if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
+ if (!vs->modifiers_state[0x45]) {
+ vs->modifiers_state[0x45] = 1;
+ press_key(vs, 0xff7f);
+ }
+ } else {
+ if (vs->modifiers_state[0x45]) {
+ vs->modifiers_state[0x45] = 0;
+ press_key(vs, 0xff7f);
+ }
+ }
+ }
+
+ if (down && vs->vd->lock_key_sync &&
+ ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) {
+ /* If the capslock state needs to change then simulate an additional
+ keypress before sending this one. This will happen if the user
+ toggles capslock away from the VNC window.
+ */
+ int uppercase = !!(sym >= 'A' && sym <= 'Z');
+ int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]);
+ int capslock = !!(vs->modifiers_state[0x3a]);
+ if (capslock) {
+ if (uppercase == shift) {
+ vs->modifiers_state[0x3a] = 0;
+ press_key(vs, 0xffe5);
+ }
+ } else {
+ if (uppercase != shift) {
+ vs->modifiers_state[0x3a] = 1;
+ press_key(vs, 0xffe5);
+ }
+ }
+ }
+
+ if (is_graphic_console()) {
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ if (down)
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+ else
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ } else {
+ /* QEMU console emulation */
+ if (down) {
+ int numlock = vs->modifiers_state[0x45];
+ switch (keycode) {
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ break;
+ case 0xc8:
+ kbd_put_keysym(QEMU_KEY_UP);
+ break;
+ case 0xd0:
+ kbd_put_keysym(QEMU_KEY_DOWN);
+ break;
+ case 0xcb:
+ kbd_put_keysym(QEMU_KEY_LEFT);
+ break;
+ case 0xcd:
+ kbd_put_keysym(QEMU_KEY_RIGHT);
+ break;
+ case 0xd3:
+ kbd_put_keysym(QEMU_KEY_DELETE);
+ break;
+ case 0xc7:
+ kbd_put_keysym(QEMU_KEY_HOME);
+ break;
+ case 0xcf:
+ kbd_put_keysym(QEMU_KEY_END);
+ break;
+ case 0xc9:
+ kbd_put_keysym(QEMU_KEY_PAGEUP);
+ break;
+ case 0xd1:
+ kbd_put_keysym(QEMU_KEY_PAGEDOWN);
+ break;
+
+ case 0x47:
+ kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME);
+ break;
+ case 0x48:
+ kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP);
+ break;
+ case 0x49:
+ kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP);
+ break;
+ case 0x4b:
+ kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT);
+ break;
+ case 0x4c:
+ kbd_put_keysym('5');
+ break;
+ case 0x4d:
+ kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT);
+ break;
+ case 0x4f:
+ kbd_put_keysym(numlock ? '1' : QEMU_KEY_END);
+ break;
+ case 0x50:
+ kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN);
+ break;
+ case 0x51:
+ kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN);
+ break;
+ case 0x52:
+ kbd_put_keysym('0');
+ break;
+ case 0x53:
+ kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE);
+ break;
+
+ case 0xb5:
+ kbd_put_keysym('/');
+ break;
+ case 0x37:
+ kbd_put_keysym('*');
+ break;
+ case 0x4a:
+ kbd_put_keysym('-');
+ break;
+ case 0x4e:
+ kbd_put_keysym('+');
+ break;
+ case 0x9c:
+ kbd_put_keysym('\n');
+ break;
+
+ default:
+ kbd_put_keysym(sym);
+ break;
+ }
+ }
+ }
+}
+
+static void key_event(VncState *vs, int down, uint32_t sym)
+{
+ int keycode;
+ int lsym = sym;
+
+ if (lsym >= 'A' && lsym <= 'Z' && is_graphic_console()) {
+ lsym = lsym - 'A' + 'a';
+ }
+
+ keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK;
+ do_key_event(vs, down, keycode, sym);
+}
+
+static void ext_key_event(VncState *vs, int down,
+ uint32_t sym, uint16_t keycode)
+{
+ /* if the user specifies a keyboard layout, always use it */
+ if (keyboard_layout)
+ key_event(vs, down, sym);
+ else
+ do_key_event(vs, down, keycode, sym);
+}
+
+static void framebuffer_update_request(VncState *vs, int incremental,
+ int x_position, int y_position,
+ int w, int h)
+{
+ if (y_position > ds_get_height(vs->ds))
+ y_position = ds_get_height(vs->ds);
+ if (y_position + h >= ds_get_height(vs->ds))
+ h = ds_get_height(vs->ds) - y_position;
+
+ int i;
+ vs->need_update = 1;
+ if (!incremental) {
+ vs->force_update = 1;
+ for (i = 0; i < h; i++) {
+ vnc_set_bits(vs->dirty[y_position + i],
+ (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
+ }
+ }
+}
+
+static void send_ext_key_event_ack(VncState *vs)
+{
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
+ VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+}
+
+static void send_ext_audio_ack(VncState *vs)
+{
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
+ VNC_ENCODING_AUDIO);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+}
+
+static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
+{
+ int i;
+ unsigned int enc = 0;
+
+ vs->features = 0;
+ vs->vnc_encoding = 0;
+ vs->tight.compression = 9;
+ vs->tight.quality = -1; /* Lossless by default */
+ vs->absolute = -1;
+
+ /*
+ * Start from the end because the encodings are sent in order of preference.
+ * This way the prefered encoding (first encoding defined in the array)
+ * will be set at the end of the loop.
+ */
+ for (i = n_encodings - 1; i >= 0; i--) {
+ enc = encodings[i];
+ switch (enc) {
+ case VNC_ENCODING_RAW:
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_COPYRECT:
+ vs->features |= VNC_FEATURE_COPYRECT_MASK;
+ break;
+ case VNC_ENCODING_HEXTILE:
+ vs->features |= VNC_FEATURE_HEXTILE_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_TIGHT:
+ vs->features |= VNC_FEATURE_TIGHT_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_TIGHT_PNG:
+ vs->features |= VNC_FEATURE_TIGHT_PNG_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_ZLIB:
+ vs->features |= VNC_FEATURE_ZLIB_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_DESKTOPRESIZE:
+ vs->features |= VNC_FEATURE_RESIZE_MASK;
+ break;
+ case VNC_ENCODING_POINTER_TYPE_CHANGE:
+ vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK;
+ break;
+ case VNC_ENCODING_RICH_CURSOR:
+ vs->features |= VNC_FEATURE_RICH_CURSOR_MASK;
+ break;
+ case VNC_ENCODING_EXT_KEY_EVENT:
+ send_ext_key_event_ack(vs);
+ break;
+ case VNC_ENCODING_AUDIO:
+ send_ext_audio_ack(vs);
+ break;
+ case VNC_ENCODING_WMVi:
+ vs->features |= VNC_FEATURE_WMVI_MASK;
+ break;
+ case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
+ vs->tight.compression = (enc & 0x0F);
+ break;
+ case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
+ vs->tight.quality = (enc & 0x0F);
+ break;
+ default:
+ VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
+ break;
+ }
+ }
+ vnc_desktop_resize(vs);
+ check_pointer_type_change(&vs->mouse_mode_notifier);
+}
+
+static void set_pixel_conversion(VncState *vs)
+{
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) &&
+ !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) {
+ vs->write_pixels = vnc_write_pixels_copy;
+ vnc_hextile_set_pixel_conversion(vs, 0);
+ } else {
+ vs->write_pixels = vnc_write_pixels_generic;
+ vnc_hextile_set_pixel_conversion(vs, 1);
+ }
+}
+
+static void set_pixel_format(VncState *vs,
+ int bits_per_pixel, int depth,
+ int big_endian_flag, int true_color_flag,
+ int red_max, int green_max, int blue_max,
+ int red_shift, int green_shift, int blue_shift)
+{
+ if (!true_color_flag) {
+ vnc_client_error(vs);
+ return;
+ }
+
+ vs->clientds = *(vs->vd->guest.ds);
+ vs->clientds.pf.rmax = red_max;
+ count_bits(vs->clientds.pf.rbits, red_max);
+ vs->clientds.pf.rshift = red_shift;
+ vs->clientds.pf.rmask = red_max << red_shift;
+ vs->clientds.pf.gmax = green_max;
+ count_bits(vs->clientds.pf.gbits, green_max);
+ vs->clientds.pf.gshift = green_shift;
+ vs->clientds.pf.gmask = green_max << green_shift;
+ vs->clientds.pf.bmax = blue_max;
+ count_bits(vs->clientds.pf.bbits, blue_max);
+ vs->clientds.pf.bshift = blue_shift;
+ vs->clientds.pf.bmask = blue_max << blue_shift;
+ vs->clientds.pf.bits_per_pixel = bits_per_pixel;
+ vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8;
+ vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
+ vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00;
+
+ set_pixel_conversion(vs);
+
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static void pixel_format_message (VncState *vs) {
+ char pad[3] = { 0, 0, 0 };
+
+ vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */
+ vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */
+
+#ifdef HOST_WORDS_BIGENDIAN
+ vnc_write_u8(vs, 1); /* big-endian-flag */
+#else
+ vnc_write_u8(vs, 0); /* big-endian-flag */
+#endif
+ vnc_write_u8(vs, 1); /* true-color-flag */
+ vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */
+ vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */
+ vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */
+ vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */
+ vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */
+ vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */
+
+ vnc_hextile_set_pixel_conversion(vs, 0);
+
+ vs->clientds = *(vs->ds->surface);
+ vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG;
+ vs->write_pixels = vnc_write_pixels_copy;
+
+ vnc_write(vs, pad, 3); /* padding */
+}
+
+static void vnc_dpy_setdata(DisplayState *ds)
+{
+ /* We don't have to do anything */
+}
+
+static void vnc_colordepth(VncState *vs)
+{
+ if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
+ /* Sending a WMVi message to notify the client*/
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
+ ds_get_height(vs->ds), VNC_ENCODING_WMVi);
+ pixel_format_message(vs);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+ } else {
+ set_pixel_conversion(vs);
+ }
+}
+
+static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
+{
+ int i;
+ uint16_t limit;
+ VncDisplay *vd = vs->vd;
+
+ if (data[0] > 3) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ if (!qemu_timer_expired(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval))
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval);
+ }
+
+ switch (data[0]) {
+ case VNC_MSG_CLIENT_SET_PIXEL_FORMAT:
+ if (len == 1)
+ return 20;
+
+ set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
+ read_u8(data, 6), read_u8(data, 7),
+ read_u16(data, 8), read_u16(data, 10),
+ read_u16(data, 12), read_u8(data, 14),
+ read_u8(data, 15), read_u8(data, 16));
+ break;
+ case VNC_MSG_CLIENT_SET_ENCODINGS:
+ if (len == 1)
+ return 4;
+
+ if (len == 4) {
+ limit = read_u16(data, 2);
+ if (limit > 0)
+ return 4 + (limit * 4);
+ } else
+ limit = read_u16(data, 2);
+
+ for (i = 0; i < limit; i++) {
+ int32_t val = read_s32(data, 4 + (i * 4));
+ memcpy(data + 4 + (i * 4), &val, sizeof(val));
+ }
+
+ set_encodings(vs, (int32_t *)(data + 4), limit);
+ break;
+ case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST:
+ if (len == 1)
+ return 10;
+
+ framebuffer_update_request(vs,
+ read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
+ read_u16(data, 6), read_u16(data, 8));
+ break;
+ case VNC_MSG_CLIENT_KEY_EVENT:
+ if (len == 1)
+ return 8;
+
+ key_event(vs, read_u8(data, 1), read_u32(data, 4));
+ break;
+ case VNC_MSG_CLIENT_POINTER_EVENT:
+ if (len == 1)
+ return 6;
+
+ pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
+ break;
+ case VNC_MSG_CLIENT_CUT_TEXT:
+ if (len == 1)
+ return 8;
+
+ if (len == 8) {
+ uint32_t dlen = read_u32(data, 4);
+ if (dlen > 0)
+ return 8 + dlen;
+ }
+
+ client_cut_text(vs, read_u32(data, 4), data + 8);
+ break;
+ case VNC_MSG_CLIENT_QEMU:
+ if (len == 1)
+ return 2;
+
+ switch (read_u8(data, 1)) {
+ case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT:
+ if (len == 2)
+ return 12;
+
+ ext_key_event(vs, read_u16(data, 2),
+ read_u32(data, 4), read_u32(data, 8));
+ break;
+ case VNC_MSG_CLIENT_QEMU_AUDIO:
+ if (len == 2)
+ return 4;
+
+ switch (read_u16 (data, 2)) {
+ case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
+ audio_add(vs);
+ break;
+ case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
+ audio_del(vs);
+ break;
+ case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
+ if (len == 4)
+ return 10;
+ switch (read_u8(data, 4)) {
+ case 0: vs->as.fmt = AUD_FMT_U8; break;
+ case 1: vs->as.fmt = AUD_FMT_S8; break;
+ case 2: vs->as.fmt = AUD_FMT_U16; break;
+ case 3: vs->as.fmt = AUD_FMT_S16; break;
+ case 4: vs->as.fmt = AUD_FMT_U32; break;
+ case 5: vs->as.fmt = AUD_FMT_S32; break;
+ default:
+ printf("Invalid audio format %d\n", read_u8(data, 4));
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.nchannels = read_u8(data, 5);
+ if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
+ printf("Invalid audio channel coount %d\n",
+ read_u8(data, 5));
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.freq = read_u32(data, 6);
+ break;
+ default:
+ printf ("Invalid audio message %d\n", read_u8(data, 4));
+ vnc_client_error(vs);
+ break;
+ }
+ break;
+
+ default:
+ printf("Msg: %d\n", read_u16(data, 0));
+ vnc_client_error(vs);
+ break;
+ }
+ break;
+ default:
+ printf("Msg: %d\n", data[0]);
+ vnc_client_error(vs);
+ break;
+ }
+
+ vnc_read_when(vs, protocol_client_msg, 1);
+ return 0;
+}
+
+static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
+{
+ char buf[1024];
+ int size;
+
+ vs->client_width = ds_get_width(vs->ds);
+ vs->client_height = ds_get_height(vs->ds);
+ vnc_write_u16(vs, vs->client_width);
+ vnc_write_u16(vs, vs->client_height);
+
+ pixel_format_message(vs);
+
+ if (qemu_name)
+ size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
+ else
+ size = snprintf(buf, sizeof(buf), "QEMU");
+
+ vnc_write_u32(vs, size);
+ vnc_write(vs, buf, size);
+ vnc_flush(vs);
+
+ vnc_client_cache_auth(vs);
+ vnc_qmp_event(vs, QEVENT_VNC_INITIALIZED);
+
+ vnc_read_when(vs, protocol_client_msg, 1);
+
+ return 0;
+}
+
+void start_client_init(VncState *vs)
+{
+ vnc_read_when(vs, protocol_client_init, 1);
+}
+
+static void make_challenge(VncState *vs)
+{
+ int i;
+
+ srand(time(NULL)+getpid()+getpid()*987654+rand());
+
+ for (i = 0 ; i < sizeof(vs->challenge) ; i++)
+ vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
+}
+
+static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
+{
+ unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
+ int i, j, pwlen;
+ unsigned char key[8];
+ time_t now = time(NULL);
+
+ if (!vs->vd->password) {
+ VNC_DEBUG("No password configured on server");
+ goto reject;
+ }
+ if (vs->vd->expires < now) {
+ VNC_DEBUG("Password is expired");
+ goto reject;
+ }
+
+ memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
+
+ /* Calculate the expected challenge response */
+ pwlen = strlen(vs->vd->password);
+ for (i=0; i<sizeof(key); i++)
+ key[i] = i<pwlen ? vs->vd->password[i] : 0;
+ deskey(key, EN0);
+ for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
+ des(response+j, response+j);
+
+ /* Compare expected vs actual challenge response */
+ if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
+ VNC_DEBUG("Client challenge reponse did not match\n");
+ goto reject;
+ } else {
+ VNC_DEBUG("Accepting VNC challenge response\n");
+ vnc_write_u32(vs, 0); /* Accept auth */
+ vnc_flush(vs);
+
+ start_client_init(vs);
+ }
+ return 0;
+
+reject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return 0;
+}
+
+void start_auth_vnc(VncState *vs)
+{
+ make_challenge(vs);
+ /* Send client a 'random' challenge */
+ vnc_write(vs, vs->challenge, sizeof(vs->challenge));
+ vnc_flush(vs);
+
+ vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
+}
+
+
+static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
+{
+ /* We only advertise 1 auth scheme at a time, so client
+ * must pick the one we sent. Verify this */
+ if (data[0] != vs->vd->auth) { /* Reject auth */
+ VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
+ vnc_write_u32(vs, 1);
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_client_error(vs);
+ } else { /* Accept requested auth */
+ VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
+ switch (vs->vd->auth) {
+ case VNC_AUTH_NONE:
+ VNC_DEBUG("Accept auth none\n");
+ if (vs->minor >= 8) {
+ vnc_write_u32(vs, 0); /* Accept auth completion */
+ vnc_flush(vs);
+ }
+ start_client_init(vs);
+ break;
+
+ case VNC_AUTH_VNC:
+ VNC_DEBUG("Start VNC auth\n");
+ start_auth_vnc(vs);
+ break;
+
+#ifdef CONFIG_VNC_TLS
+ case VNC_AUTH_VENCRYPT:
+ VNC_DEBUG("Accept VeNCrypt auth\n");;
+ start_auth_vencrypt(vs);
+ break;
+#endif /* CONFIG_VNC_TLS */
+
+#ifdef CONFIG_VNC_SASL
+ case VNC_AUTH_SASL:
+ VNC_DEBUG("Accept SASL auth\n");
+ start_auth_sasl(vs);
+ break;
+#endif /* CONFIG_VNC_SASL */
+
+ default: /* Should not be possible, but just in case */
+ VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth);
+ vnc_write_u8(vs, 1);
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_client_error(vs);
+ }
+ }
+ return 0;
+}
+
+static int protocol_version(VncState *vs, uint8_t *version, size_t len)
+{
+ char local[13];
+
+ memcpy(local, version, 12);
+ local[12] = 0;
+
+ if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
+ VNC_DEBUG("Malformed protocol version %s\n", local);
+ vnc_client_error(vs);
+ return 0;
+ }
+ VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
+ if (vs->major != 3 ||
+ (vs->minor != 3 &&
+ vs->minor != 4 &&
+ vs->minor != 5 &&
+ vs->minor != 7 &&
+ vs->minor != 8)) {
+ VNC_DEBUG("Unsupported client version\n");
+ vnc_write_u32(vs, VNC_AUTH_INVALID);
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return 0;
+ }
+ /* Some broken clients report v3.4 or v3.5, which spec requires to be treated
+ * as equivalent to v3.3 by servers
+ */
+ if (vs->minor == 4 || vs->minor == 5)
+ vs->minor = 3;
+
+ if (vs->minor == 3) {
+ if (vs->vd->auth == VNC_AUTH_NONE) {
+ VNC_DEBUG("Tell client auth none\n");
+ vnc_write_u32(vs, vs->vd->auth);
+ vnc_flush(vs);
+ start_client_init(vs);
+ } else if (vs->vd->auth == VNC_AUTH_VNC) {
+ VNC_DEBUG("Tell client VNC auth\n");
+ vnc_write_u32(vs, vs->vd->auth);
+ vnc_flush(vs);
+ start_auth_vnc(vs);
+ } else {
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
+ vnc_write_u32(vs, VNC_AUTH_INVALID);
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ }
+ } else {
+ VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
+ vnc_write_u8(vs, 1); /* num auth */
+ vnc_write_u8(vs, vs->vd->auth);
+ vnc_read_when(vs, protocol_client_auth, 1);
+ vnc_flush(vs);
+ }
+
+ return 0;
+}
+
+static int vnc_refresh_server_surface(VncDisplay *vd)
+{
+ int y;
+ uint8_t *guest_row;
+ uint8_t *server_row;
+ int cmp_bytes;
+ uint32_t width_mask[VNC_DIRTY_WORDS];
+ VncState *vs;
+ int has_dirty = 0;
+
+ /*
+ * Walk through the guest dirty map.
+ * Check and copy modified bits from guest to server surface.
+ * Update server dirty map.
+ */
+ vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS);
+ cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds);
+ guest_row = vd->guest.ds->data;
+ server_row = vd->server->data;
+ for (y = 0; y < vd->guest.ds->height; y++) {
+ if (vnc_and_bits(vd->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) {
+ int x;
+ uint8_t *guest_ptr;
+ uint8_t *server_ptr;
+
+ guest_ptr = guest_row;
+ server_ptr = server_row;
+
+ for (x = 0; x < vd->guest.ds->width;
+ x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
+ if (!vnc_get_bit(vd->guest.dirty[y], (x / 16)))
+ continue;
+ vnc_clear_bit(vd->guest.dirty[y], (x / 16));
+ if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
+ continue;
+ memcpy(server_ptr, guest_ptr, cmp_bytes);
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_set_bit(vs->dirty[y], (x / 16));
+ }
+ has_dirty++;
+ }
+ }
+ guest_row += ds_get_linesize(vd->ds);
+ server_row += ds_get_linesize(vd->ds);
+ }
+ return has_dirty;
+}
+
+static void vnc_refresh(void *opaque)
+{
+ VncDisplay *vd = opaque;
+ VncState *vs, *vn;
+ int has_dirty, rects = 0;
+
+ vga_hw_update();
+
+ if (vnc_trylock_display(vd)) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) +
+ vd->timer_interval);
+ return;
+ }
+
+ has_dirty = vnc_refresh_server_surface(vd);
+ vnc_unlock_display(vd);
+
+ QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
+ rects += vnc_update_client(vs, has_dirty);
+ /* vs might be free()ed here */
+ }
+
+ /* vd->timer could be NULL now if the last client disconnected,
+ * in this case don't update the timer */
+ if (vd->timer == NULL)
+ return;
+
+ if (has_dirty && rects) {
+ vd->timer_interval /= 2;
+ if (vd->timer_interval < VNC_REFRESH_INTERVAL_BASE)
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ } else {
+ vd->timer_interval += VNC_REFRESH_INTERVAL_INC;
+ if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX)
+ vd->timer_interval = VNC_REFRESH_INTERVAL_MAX;
+ }
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval);
+}
+
+static void vnc_init_timer(VncDisplay *vd)
+{
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) {
+ vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd);
+ vnc_dpy_resize(vd->ds);
+ vnc_refresh(vd);
+ }
+}
+
+static void vnc_remove_timer(VncDisplay *vd)
+{
+ if (vd->timer != NULL && QTAILQ_EMPTY(&vd->clients)) {
+ qemu_del_timer(vd->timer);
+ qemu_free_timer(vd->timer);
+ vd->timer = NULL;
+ }
+}
+
+static void vnc_connect(VncDisplay *vd, int csock)
+{
+ VncState *vs = qemu_mallocz(sizeof(VncState));
+ vs->csock = csock;
+
+ VNC_DEBUG("New client on socket %d\n", csock);
+ dcl->idle = 0;
+ socket_set_nonblock(vs->csock);
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+
+ vnc_client_cache_addr(vs);
+ vnc_qmp_event(vs, QEVENT_VNC_CONNECTED);
+
+ vs->vd = vd;
+ vs->ds = vd->ds;
+ vs->last_x = -1;
+ vs->last_y = -1;
+
+ vs->as.freq = 44100;
+ vs->as.nchannels = 2;
+ vs->as.fmt = AUD_FMT_S16;
+ vs->as.endianness = 0;
+
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->output_mutex);
+#endif
+
+ QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
+
+ vga_hw_update();
+
+ vnc_write(vs, "RFB 003.008\n", 12);
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_version, 12);
+ reset_keys(vs);
+ if (vs->vd->lock_key_sync)
+ vs->led = qemu_add_led_event_handler(kbd_leds, vs);
+
+ vs->mouse_mode_notifier.notify = check_pointer_type_change;
+ qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
+
+ vnc_init_timer(vd);
+
+ /* vs might be free()ed here */
+}
+
+static void vnc_listen_read(void *opaque)
+{
+ VncDisplay *vs = opaque;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+
+ /* Catch-up */
+ vga_hw_update();
+
+ int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+ if (csock != -1) {
+ vnc_connect(vs, csock);
+ }
+}
+
+void vnc_display_init(DisplayState *ds)
+{
+ VncDisplay *vs = qemu_mallocz(sizeof(*vs));
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+
+ ds->opaque = vs;
+ dcl->idle = 1;
+ vnc_display = vs;
+
+ vs->lsock = -1;
+
+ vs->ds = ds;
+ QTAILQ_INIT(&vs->clients);
+ vs->expires = TIME_MAX;
+
+ if (keyboard_layout)
+ vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ else
+ vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
+
+ if (!vs->kbd_layout)
+ exit(1);
+
+#ifdef CONFIG_VNC_THREAD
+ qemu_mutex_init(&vs->mutex);
+ vnc_start_worker_thread();
+#endif
+
+ dcl->dpy_copy = vnc_dpy_copy;
+ dcl->dpy_update = vnc_dpy_update;
+ dcl->dpy_resize = vnc_dpy_resize;
+ dcl->dpy_setdata = vnc_dpy_setdata;
+ register_displaychangelistener(ds, dcl);
+ ds->mouse_set = vnc_mouse_set;
+ ds->cursor_define = vnc_dpy_cursor_define;
+}
+
+
+void vnc_display_close(DisplayState *ds)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs)
+ return;
+ if (vs->display) {
+ qemu_free(vs->display);
+ vs->display = NULL;
+ }
+ if (vs->lsock != -1) {
+ qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL);
+ close(vs->lsock);
+ vs->lsock = -1;
+ }
+ vs->auth = VNC_AUTH_INVALID;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ vs->tls.x509verify = 0;
+#endif
+}
+
+int vnc_display_disable_login(DisplayState *ds)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs) {
+ return -1;
+ }
+
+ if (vs->password) {
+ qemu_free(vs->password);
+ }
+
+ vs->password = NULL;
+ vs->auth = VNC_AUTH_VNC;
+
+ return 0;
+}
+
+int vnc_display_password(DisplayState *ds, const char *password)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs) {
+ return -1;
+ }
+
+ if (!password) {
+ /* This is not the intention of this interface but err on the side
+ of being safe */
+ return vnc_display_disable_login(ds);
+ }
+
+ if (vs->password) {
+ qemu_free(vs->password);
+ vs->password = NULL;
+ }
+ vs->password = qemu_strdup(password);
+ vs->auth = VNC_AUTH_VNC;
+
+ return 0;
+}
+
+int vnc_display_pw_expire(DisplayState *ds, time_t expires)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ vs->expires = expires;
+ return 0;
+}
+
+char *vnc_display_local_addr(DisplayState *ds)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ return vnc_socket_local_addr("%s:%s", vs->lsock);
+}
+
+int vnc_display_open(DisplayState *ds, const char *display)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+ const char *options;
+ int password = 0;
+ int reverse = 0;
+#ifdef CONFIG_VNC_TLS
+ int tls = 0, x509 = 0;
+#endif
+#ifdef CONFIG_VNC_SASL
+ int sasl = 0;
+ int saslErr;
+#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
+ int acl = 0;
+#endif
+ int lock_key_sync = 1;
+
+ if (!vnc_display)
+ return -1;
+ vnc_display_close(ds);
+ if (strcmp(display, "none") == 0)
+ return 0;
+
+ if (!(vs->display = strdup(display)))
+ return -1;
+
+ options = display;
+ while ((options = strchr(options, ','))) {
+ options++;
+ if (strncmp(options, "password", 8) == 0) {
+ password = 1; /* Require password auth */
+ } else if (strncmp(options, "reverse", 7) == 0) {
+ reverse = 1;
+ } else if (strncmp(options, "no-lock-key-sync", 9) == 0) {
+ lock_key_sync = 0;
+#ifdef CONFIG_VNC_SASL
+ } else if (strncmp(options, "sasl", 4) == 0) {
+ sasl = 1; /* Require SASL auth */
+#endif
+#ifdef CONFIG_VNC_TLS
+ } else if (strncmp(options, "tls", 3) == 0) {
+ tls = 1; /* Require TLS */
+ } else if (strncmp(options, "x509", 4) == 0) {
+ char *start, *end;
+ x509 = 1; /* Require x509 certificates */
+ if (strncmp(options, "x509verify", 10) == 0)
+ vs->tls.x509verify = 1; /* ...and verify client certs */
+
+ /* Now check for 'x509=/some/path' postfix
+ * and use that to setup x509 certificate/key paths */
+ start = strchr(options, '=');
+ end = strchr(options, ',');
+ if (start && (!end || (start < end))) {
+ int len = end ? end-(start+1) : strlen(start+1);
+ char *path = qemu_strndup(start + 1, len);
+
+ VNC_DEBUG("Trying certificate path '%s'\n", path);
+ if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
+ fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
+ qemu_free(path);
+ qemu_free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+ qemu_free(path);
+ } else {
+ fprintf(stderr, "No certificate path provided\n");
+ qemu_free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+#endif
+#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
+ } else if (strncmp(options, "acl", 3) == 0) {
+ acl = 1;
+#endif
+ } else if (strncmp(options, "lossy", 5) == 0) {
+ vs->lossy = true;
+ }
+ }
+
+#ifdef CONFIG_VNC_TLS
+ if (acl && x509 && vs->tls.x509verify) {
+ if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
+ fprintf(stderr, "Failed to create x509 dname ACL\n");
+ exit(1);
+ }
+ }
+#endif
+#ifdef CONFIG_VNC_SASL
+ if (acl && sasl) {
+ if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) {
+ fprintf(stderr, "Failed to create username ACL\n");
+ exit(1);
+ }
+ }
+#endif
+
+ /*
+ * Combinations we support here:
+ *
+ * - no-auth (clear text, no auth)
+ * - password (clear text, weak auth)
+ * - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI)
+ * - tls (encrypt, weak anonymous creds, no auth)
+ * - tls + password (encrypt, weak anonymous creds, weak auth)
+ * - tls + sasl (encrypt, weak anonymous creds, good auth)
+ * - tls + x509 (encrypt, good x509 creds, no auth)
+ * - tls + x509 + password (encrypt, good x509 creds, weak auth)
+ * - tls + x509 + sasl (encrypt, good x509 creds, good auth)
+ *
+ * NB1. TLS is a stackable auth scheme.
+ * NB2. the x509 schemes have option to validate a client cert dname
+ */
+ if (password) {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 password auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ }
+ } else {
+#endif /* CONFIG_VNC_TLS */
+ VNC_DEBUG("Initializing VNC server with password auth\n");
+ vs->auth = VNC_AUTH_VNC;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif /* CONFIG_VNC_TLS */
+#ifdef CONFIG_VNC_SASL
+ } else if (sasl) {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ }
+ } else {
+#endif /* CONFIG_VNC_TLS */
+ VNC_DEBUG("Initializing VNC server with SASL auth\n");
+ vs->auth = VNC_AUTH_SASL;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif /* CONFIG_VNC_TLS */
+#endif /* CONFIG_VNC_SASL */
+ } else {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 no auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS no auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+ }
+ } else {
+#endif
+ VNC_DEBUG("Initializing VNC server with no auth\n");
+ vs->auth = VNC_AUTH_NONE;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif
+ }
+
+#ifdef CONFIG_VNC_SASL
+ if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
+ fprintf(stderr, "Failed to initialize SASL auth %s",
+ sasl_errstring(saslErr, NULL, NULL));
+ free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+#endif
+ vs->lock_key_sync = lock_key_sync;
+
+ if (reverse) {
+ /* connect to viewer */
+ if (strncmp(display, "unix:", 5) == 0)
+ vs->lsock = unix_connect(display+5);
+ else
+ vs->lsock = inet_connect(display, SOCK_STREAM);
+ if (-1 == vs->lsock) {
+ free(vs->display);
+ vs->display = NULL;
+ return -1;
+ } else {
+ int csock = vs->lsock;
+ vs->lsock = -1;
+ vnc_connect(vs, csock);
+ }
+ return 0;
+
+ } else {
+ /* listen for connects */
+ char *dpy;
+ dpy = qemu_malloc(256);
+ if (strncmp(display, "unix:", 5) == 0) {
+ pstrcpy(dpy, 256, "unix:");
+ vs->lsock = unix_listen(display+5, dpy+5, 256-5);
+ } else {
+ vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900);
+ }
+ if (-1 == vs->lsock) {
+ free(dpy);
+ return -1;
+ } else {
+ free(vs->display);
+ vs->display = dpy;
+ }
+ }
+ return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
+}
diff --git a/ui/vnc.h b/ui/vnc.h
new file mode 100644
index 0000000..4f895be
--- /dev/null
+++ b/ui/vnc.h
@@ -0,0 +1,502 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2009 Red Hat, Inc
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __QEMU_VNC_H
+#define __QEMU_VNC_H
+
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#ifdef CONFIG_VNC_THREAD
+#include "qemu-thread.h"
+#endif
+#include "console.h"
+#include "monitor.h"
+#include "audio/audio.h"
+#include <zlib.h>
+#include <stdbool.h>
+
+#include "keymaps.h"
+
+// #define _VNC_DEBUG 1
+
+#ifdef _VNC_DEBUG
+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define VNC_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+/*****************************************************************************
+ *
+ * Core data structures
+ *
+ *****************************************************************************/
+
+typedef struct Buffer
+{
+ size_t capacity;
+ size_t offset;
+ uint8_t *buffer;
+} Buffer;
+
+typedef struct VncState VncState;
+typedef struct VncJob VncJob;
+typedef struct VncRect VncRect;
+typedef struct VncRectEntry VncRectEntry;
+
+typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
+
+typedef void VncWritePixels(VncState *vs, struct PixelFormat *pf, void *data, int size);
+
+typedef void VncSendHextileTile(VncState *vs,
+ int x, int y, int w, int h,
+ void *last_bg,
+ void *last_fg,
+ int *has_bg, int *has_fg);
+
+#define VNC_MAX_WIDTH 2560
+#define VNC_MAX_HEIGHT 2048
+#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
+
+#define VNC_AUTH_CHALLENGE_SIZE 16
+
+typedef struct VncDisplay VncDisplay;
+
+#ifdef CONFIG_VNC_TLS
+#include "vnc-tls.h"
+#include "vnc-auth-vencrypt.h"
+#endif
+#ifdef CONFIG_VNC_SASL
+#include "vnc-auth-sasl.h"
+#endif
+
+struct VncSurface
+{
+ uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+ DisplaySurface *ds;
+};
+
+struct VncDisplay
+{
+ QTAILQ_HEAD(, VncState) clients;
+ QEMUTimer *timer;
+ int timer_interval;
+ int lsock;
+ DisplayState *ds;
+ kbd_layout_t *kbd_layout;
+ int lock_key_sync;
+#ifdef CONFIG_VNC_THREAD
+ QemuMutex mutex;
+#endif
+
+ QEMUCursor *cursor;
+ int cursor_msize;
+ uint8_t *cursor_mask;
+
+ struct VncSurface guest; /* guest visible surface (aka ds->surface) */
+ DisplaySurface *server; /* vnc server surface */
+
+ char *display;
+ char *password;
+ time_t expires;
+ int auth;
+ bool lossy;
+#ifdef CONFIG_VNC_TLS
+ int subauth; /* Used by VeNCrypt */
+ VncDisplayTLS tls;
+#endif
+#ifdef CONFIG_VNC_SASL
+ VncDisplaySASL sasl;
+#endif
+};
+
+typedef struct VncTight {
+ int type;
+ uint8_t quality;
+ uint8_t compression;
+ uint8_t pixel24;
+ Buffer tight;
+ Buffer tmp;
+ Buffer zlib;
+ Buffer gradient;
+#ifdef CONFIG_VNC_JPEG
+ Buffer jpeg;
+#endif
+#ifdef CONFIG_VNC_PNG
+ Buffer png;
+#endif
+ int levels[4];
+ z_stream stream[4];
+} VncTight;
+
+typedef struct VncHextile {
+ VncSendHextileTile *send_tile;
+} VncHextile;
+
+typedef struct VncZlib {
+ Buffer zlib;
+ Buffer tmp;
+ z_stream stream;
+ int level;
+} VncZlib;
+
+#ifdef CONFIG_VNC_THREAD
+struct VncRect
+{
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+struct VncRectEntry
+{
+ struct VncRect rect;
+ QLIST_ENTRY(VncRectEntry) next;
+};
+
+struct VncJob
+{
+ VncState *vs;
+
+ QLIST_HEAD(, VncRectEntry) rectangles;
+ QTAILQ_ENTRY(VncJob) next;
+};
+#else
+struct VncJob
+{
+ VncState *vs;
+ int rectangles;
+ size_t saved_offset;
+};
+#endif
+
+struct VncState
+{
+ int csock;
+
+ DisplayState *ds;
+ uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+
+ VncDisplay *vd;
+ int need_update;
+ int force_update;
+ uint32_t features;
+ int absolute;
+ int last_x;
+ int last_y;
+ int client_width;
+ int client_height;
+
+ uint32_t vnc_encoding;
+
+ int major;
+ int minor;
+
+ char challenge[VNC_AUTH_CHALLENGE_SIZE];
+#ifdef CONFIG_VNC_TLS
+ VncStateTLS tls;
+#endif
+#ifdef CONFIG_VNC_SASL
+ VncStateSASL sasl;
+#endif
+
+ QObject *info;
+
+ Buffer output;
+ Buffer input;
+ /* current output mode information */
+ VncWritePixels *write_pixels;
+ DisplaySurface clientds;
+
+ CaptureVoiceOut *audio_cap;
+ struct audsettings as;
+
+ VncReadEvent *read_handler;
+ size_t read_handler_expect;
+ /* input */
+ uint8_t modifiers_state[256];
+ QEMUPutLEDEntry *led;
+
+ bool abort;
+#ifndef CONFIG_VNC_THREAD
+ VncJob job;
+#else
+ QemuMutex output_mutex;
+#endif
+
+ /* Encoding specific, if you add something here, don't forget to
+ * update vnc_async_encoding_start()
+ */
+ VncTight tight;
+ VncZlib zlib;
+ VncHextile hextile;
+
+
+ Notifier mouse_mode_notifier;
+
+ QTAILQ_ENTRY(VncState) next;
+};
+
+
+/*****************************************************************************
+ *
+ * Authentication modes
+ *
+ *****************************************************************************/
+
+enum {
+ VNC_AUTH_INVALID = 0,
+ VNC_AUTH_NONE = 1,
+ VNC_AUTH_VNC = 2,
+ VNC_AUTH_RA2 = 5,
+ VNC_AUTH_RA2NE = 6,
+ VNC_AUTH_TIGHT = 16,
+ VNC_AUTH_ULTRA = 17,
+ VNC_AUTH_TLS = 18, /* Supported in GTK-VNC & VINO */
+ VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */
+ VNC_AUTH_SASL = 20, /* Supported in GTK-VNC & VINO */
+};
+
+enum {
+ VNC_AUTH_VENCRYPT_PLAIN = 256,
+ VNC_AUTH_VENCRYPT_TLSNONE = 257,
+ VNC_AUTH_VENCRYPT_TLSVNC = 258,
+ VNC_AUTH_VENCRYPT_TLSPLAIN = 259,
+ VNC_AUTH_VENCRYPT_X509NONE = 260,
+ VNC_AUTH_VENCRYPT_X509VNC = 261,
+ VNC_AUTH_VENCRYPT_X509PLAIN = 262,
+ VNC_AUTH_VENCRYPT_X509SASL = 263,
+ VNC_AUTH_VENCRYPT_TLSSASL = 264,
+};
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define VNC_ENCODING_RAW 0x00000000
+#define VNC_ENCODING_COPYRECT 0x00000001
+#define VNC_ENCODING_RRE 0x00000002
+#define VNC_ENCODING_CORRE 0x00000004
+#define VNC_ENCODING_HEXTILE 0x00000005
+#define VNC_ENCODING_ZLIB 0x00000006
+#define VNC_ENCODING_TIGHT 0x00000007
+#define VNC_ENCODING_ZLIBHEX 0x00000008
+#define VNC_ENCODING_TRLE 0x0000000f
+#define VNC_ENCODING_ZRLE 0x00000010
+#define VNC_ENCODING_ZYWRLE 0x00000011
+#define VNC_ENCODING_COMPRESSLEVEL0 0xFFFFFF00 /* -256 */
+#define VNC_ENCODING_QUALITYLEVEL0 0xFFFFFFE0 /* -32 */
+#define VNC_ENCODING_XCURSOR 0xFFFFFF10 /* -240 */
+#define VNC_ENCODING_RICH_CURSOR 0xFFFFFF11 /* -239 */
+#define VNC_ENCODING_POINTER_POS 0xFFFFFF18 /* -232 */
+#define VNC_ENCODING_LASTRECT 0xFFFFFF20 /* -224 */
+#define VNC_ENCODING_DESKTOPRESIZE 0xFFFFFF21 /* -223 */
+#define VNC_ENCODING_POINTER_TYPE_CHANGE 0XFFFFFEFF /* -257 */
+#define VNC_ENCODING_EXT_KEY_EVENT 0XFFFFFEFE /* -258 */
+#define VNC_ENCODING_AUDIO 0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_TIGHT_PNG 0xFFFFFEFC /* -260 */
+#define VNC_ENCODING_WMVi 0x574D5669
+
+/*****************************************************************************
+ *
+ * Other tight constants
+ *
+ *****************************************************************************/
+
+/*
+ * Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC.
+ */
+
+#define VNC_TIGHT_CCB_RESET_MASK (0x0f)
+#define VNC_TIGHT_CCB_TYPE_MASK (0x0f << 4)
+#define VNC_TIGHT_CCB_TYPE_FILL (0x08 << 4)
+#define VNC_TIGHT_CCB_TYPE_JPEG (0x09 << 4)
+#define VNC_TIGHT_CCB_TYPE_PNG (0x0A << 4)
+#define VNC_TIGHT_CCB_BASIC_MAX (0x07 << 4)
+#define VNC_TIGHT_CCB_BASIC_ZLIB (0x03 << 4)
+#define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4)
+
+/*****************************************************************************
+ *
+ * Features
+ *
+ *****************************************************************************/
+
+#define VNC_FEATURE_RESIZE 0
+#define VNC_FEATURE_HEXTILE 1
+#define VNC_FEATURE_POINTER_TYPE_CHANGE 2
+#define VNC_FEATURE_WMVI 3
+#define VNC_FEATURE_TIGHT 4
+#define VNC_FEATURE_ZLIB 5
+#define VNC_FEATURE_COPYRECT 6
+#define VNC_FEATURE_RICH_CURSOR 7
+#define VNC_FEATURE_TIGHT_PNG 8
+
+#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE)
+#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE)
+#define VNC_FEATURE_POINTER_TYPE_CHANGE_MASK (1 << VNC_FEATURE_POINTER_TYPE_CHANGE)
+#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI)
+#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT)
+#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB)
+#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT)
+#define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR)
+#define VNC_FEATURE_TIGHT_PNG_MASK (1 << VNC_FEATURE_TIGHT_PNG)
+
+
+/* Client -> Server message IDs */
+#define VNC_MSG_CLIENT_SET_PIXEL_FORMAT 0
+#define VNC_MSG_CLIENT_SET_ENCODINGS 2
+#define VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST 3
+#define VNC_MSG_CLIENT_KEY_EVENT 4
+#define VNC_MSG_CLIENT_POINTER_EVENT 5
+#define VNC_MSG_CLIENT_CUT_TEXT 6
+#define VNC_MSG_CLIENT_VMWARE_0 127
+#define VNC_MSG_CLIENT_CALL_CONTROL 249
+#define VNC_MSG_CLIENT_XVP 250
+#define VNC_MSG_CLIENT_SET_DESKTOP_SIZE 251
+#define VNC_MSG_CLIENT_TIGHT 252
+#define VNC_MSG_CLIENT_GII 253
+#define VNC_MSG_CLIENT_VMWARE_1 254
+#define VNC_MSG_CLIENT_QEMU 255
+
+/* Server -> Client message IDs */
+#define VNC_MSG_SERVER_FRAMEBUFFER_UPDATE 0
+#define VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES 1
+#define VNC_MSG_SERVER_BELL 2
+#define VNC_MSG_SERVER_CUT_TEXT 3
+#define VNC_MSG_SERVER_VMWARE_0 127
+#define VNC_MSG_SERVER_CALL_CONTROL 249
+#define VNC_MSG_SERVER_XVP 250
+#define VNC_MSG_SERVER_TIGHT 252
+#define VNC_MSG_SERVER_GII 253
+#define VNC_MSG_SERVER_VMWARE_1 254
+#define VNC_MSG_SERVER_QEMU 255
+
+
+
+/* QEMU client -> server message IDs */
+#define VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT 0
+#define VNC_MSG_CLIENT_QEMU_AUDIO 1
+
+/* QEMU server -> client message IDs */
+#define VNC_MSG_SERVER_QEMU_AUDIO 1
+
+
+
+/* QEMU client -> server audio message IDs */
+#define VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE 0
+#define VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE 1
+#define VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT 2
+
+/* QEMU server -> client audio message IDs */
+#define VNC_MSG_SERVER_QEMU_AUDIO_END 0
+#define VNC_MSG_SERVER_QEMU_AUDIO_BEGIN 1
+#define VNC_MSG_SERVER_QEMU_AUDIO_DATA 2
+
+
+/*****************************************************************************
+ *
+ * Internal APIs
+ *
+ *****************************************************************************/
+
+/* Event loop functions */
+void vnc_client_read(void *opaque);
+void vnc_client_write(void *opaque);
+
+long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
+long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+
+/* Protocol I/O functions */
+void vnc_write(VncState *vs, const void *data, size_t len);
+void vnc_write_u32(VncState *vs, uint32_t value);
+void vnc_write_s32(VncState *vs, int32_t value);
+void vnc_write_u16(VncState *vs, uint16_t value);
+void vnc_write_u8(VncState *vs, uint8_t value);
+void vnc_flush(VncState *vs);
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting);
+
+
+/* Buffer I/O functions */
+uint8_t read_u8(uint8_t *data, size_t offset);
+uint16_t read_u16(uint8_t *data, size_t offset);
+int32_t read_s32(uint8_t *data, size_t offset);
+uint32_t read_u32(uint8_t *data, size_t offset);
+
+/* Protocol stage functions */
+void vnc_client_error(VncState *vs);
+int vnc_client_io_error(VncState *vs, int ret, int last_errno);
+
+void start_client_init(VncState *vs);
+void start_auth_vnc(VncState *vs);
+
+/* Buffer management */
+void buffer_reserve(Buffer *buffer, size_t len);
+int buffer_empty(Buffer *buffer);
+uint8_t *buffer_end(Buffer *buffer);
+void buffer_reset(Buffer *buffer);
+void buffer_free(Buffer *buffer);
+void buffer_append(Buffer *buffer, const void *data, size_t len);
+
+
+/* Misc helpers */
+
+char *vnc_socket_local_addr(const char *format, int fd);
+char *vnc_socket_remote_addr(const char *format, int fd);
+
+static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
+ return (vs->features & (1 << feature));
+}
+
+/* Framebuffer */
+void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+ int32_t encoding);
+
+void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v);
+
+/* Encodings */
+int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+
+int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+
+int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
+ int y, int w, int h);
+void vnc_hextile_set_pixel_conversion(VncState *vs, int generic);
+
+void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size);
+void vnc_zlib_zfree(void *x, void *addr);
+int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+void vnc_zlib_clear(VncState *vs);
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h);
+void vnc_tight_clear(VncState *vs);
+
+#endif /* __QEMU_VNC_H */
diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h
new file mode 100644
index 0000000..55cb87e
--- /dev/null
+++ b/ui/vnc_keysym.h
@@ -0,0 +1,324 @@
+
+#include "keymaps.h"
+
+static const name2keysym_t name2keysym[]={
+/* ascii */
+ { "space", 0x020},
+ { "exclam", 0x021},
+ { "quotedbl", 0x022},
+ { "numbersign", 0x023},
+ { "dollar", 0x024},
+ { "percent", 0x025},
+ { "ampersand", 0x026},
+ { "apostrophe", 0x027},
+ { "parenleft", 0x028},
+ { "parenright", 0x029},
+ { "asterisk", 0x02a},
+ { "plus", 0x02b},
+ { "comma", 0x02c},
+ { "minus", 0x02d},
+ { "period", 0x02e},
+ { "slash", 0x02f},
+ { "0", 0x030},
+ { "1", 0x031},
+ { "2", 0x032},
+ { "3", 0x033},
+ { "4", 0x034},
+ { "5", 0x035},
+ { "6", 0x036},
+ { "7", 0x037},
+ { "8", 0x038},
+ { "9", 0x039},
+ { "colon", 0x03a},
+ { "semicolon", 0x03b},
+ { "less", 0x03c},
+ { "equal", 0x03d},
+ { "greater", 0x03e},
+ { "question", 0x03f},
+ { "at", 0x040},
+ { "A", 0x041},
+ { "B", 0x042},
+ { "C", 0x043},
+ { "D", 0x044},
+ { "E", 0x045},
+ { "F", 0x046},
+ { "G", 0x047},
+ { "H", 0x048},
+ { "I", 0x049},
+ { "J", 0x04a},
+ { "K", 0x04b},
+ { "L", 0x04c},
+ { "M", 0x04d},
+ { "N", 0x04e},
+ { "O", 0x04f},
+ { "P", 0x050},
+ { "Q", 0x051},
+ { "R", 0x052},
+ { "S", 0x053},
+ { "T", 0x054},
+ { "U", 0x055},
+ { "V", 0x056},
+ { "W", 0x057},
+ { "X", 0x058},
+ { "Y", 0x059},
+ { "Z", 0x05a},
+ { "bracketleft", 0x05b},
+ { "backslash", 0x05c},
+ { "bracketright", 0x05d},
+ { "asciicircum", 0x05e},
+ { "underscore", 0x05f},
+ { "grave", 0x060},
+ { "a", 0x061},
+ { "b", 0x062},
+ { "c", 0x063},
+ { "d", 0x064},
+ { "e", 0x065},
+ { "f", 0x066},
+ { "g", 0x067},
+ { "h", 0x068},
+ { "i", 0x069},
+ { "j", 0x06a},
+ { "k", 0x06b},
+ { "l", 0x06c},
+ { "m", 0x06d},
+ { "n", 0x06e},
+ { "o", 0x06f},
+ { "p", 0x070},
+ { "q", 0x071},
+ { "r", 0x072},
+ { "s", 0x073},
+ { "t", 0x074},
+ { "u", 0x075},
+ { "v", 0x076},
+ { "w", 0x077},
+ { "x", 0x078},
+ { "y", 0x079},
+ { "z", 0x07a},
+ { "braceleft", 0x07b},
+ { "bar", 0x07c},
+ { "braceright", 0x07d},
+ { "asciitilde", 0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace", 0x0a0},
+{ "exclamdown", 0x0a1},
+{ "cent", 0x0a2},
+{ "sterling", 0x0a3},
+{ "currency", 0x0a4},
+{ "yen", 0x0a5},
+{ "brokenbar", 0x0a6},
+{ "section", 0x0a7},
+{ "diaeresis", 0x0a8},
+{ "copyright", 0x0a9},
+{ "ordfeminine", 0x0aa},
+{ "guillemotleft", 0x0ab},
+{ "notsign", 0x0ac},
+{ "hyphen", 0x0ad},
+{ "registered", 0x0ae},
+{ "macron", 0x0af},
+{ "degree", 0x0b0},
+{ "plusminus", 0x0b1},
+{ "twosuperior", 0x0b2},
+{ "threesuperior", 0x0b3},
+{ "acute", 0x0b4},
+{ "mu", 0x0b5},
+{ "paragraph", 0x0b6},
+{ "periodcentered", 0x0b7},
+{ "cedilla", 0x0b8},
+{ "onesuperior", 0x0b9},
+{ "masculine", 0x0ba},
+{ "guillemotright", 0x0bb},
+{ "onequarter", 0x0bc},
+{ "onehalf", 0x0bd},
+{ "threequarters", 0x0be},
+{ "questiondown", 0x0bf},
+{ "Agrave", 0x0c0},
+{ "Aacute", 0x0c1},
+{ "Acircumflex", 0x0c2},
+{ "Atilde", 0x0c3},
+{ "Adiaeresis", 0x0c4},
+{ "Aring", 0x0c5},
+{ "AE", 0x0c6},
+{ "Ccedilla", 0x0c7},
+{ "Egrave", 0x0c8},
+{ "Eacute", 0x0c9},
+{ "Ecircumflex", 0x0ca},
+{ "Ediaeresis", 0x0cb},
+{ "Igrave", 0x0cc},
+{ "Iacute", 0x0cd},
+{ "Icircumflex", 0x0ce},
+{ "Idiaeresis", 0x0cf},
+{ "ETH", 0x0d0},
+{ "Eth", 0x0d0},
+{ "Ntilde", 0x0d1},
+{ "Ograve", 0x0d2},
+{ "Oacute", 0x0d3},
+{ "Ocircumflex", 0x0d4},
+{ "Otilde", 0x0d5},
+{ "Odiaeresis", 0x0d6},
+{ "multiply", 0x0d7},
+{ "Ooblique", 0x0d8},
+{ "Oslash", 0x0d8},
+{ "Ugrave", 0x0d9},
+{ "Uacute", 0x0da},
+{ "Ucircumflex", 0x0db},
+{ "Udiaeresis", 0x0dc},
+{ "Yacute", 0x0dd},
+{ "THORN", 0x0de},
+{ "Thorn", 0x0de},
+{ "ssharp", 0x0df},
+{ "agrave", 0x0e0},
+{ "aacute", 0x0e1},
+{ "acircumflex", 0x0e2},
+{ "atilde", 0x0e3},
+{ "adiaeresis", 0x0e4},
+{ "aring", 0x0e5},
+{ "ae", 0x0e6},
+{ "ccedilla", 0x0e7},
+{ "egrave", 0x0e8},
+{ "eacute", 0x0e9},
+{ "ecircumflex", 0x0ea},
+{ "ediaeresis", 0x0eb},
+{ "igrave", 0x0ec},
+{ "iacute", 0x0ed},
+{ "icircumflex", 0x0ee},
+{ "idiaeresis", 0x0ef},
+{ "eth", 0x0f0},
+{ "ntilde", 0x0f1},
+{ "ograve", 0x0f2},
+{ "oacute", 0x0f3},
+{ "ocircumflex", 0x0f4},
+{ "otilde", 0x0f5},
+{ "odiaeresis", 0x0f6},
+{ "division", 0x0f7},
+{ "oslash", 0x0f8},
+{ "ooblique", 0x0f8},
+{ "ugrave", 0x0f9},
+{ "uacute", 0x0fa},
+{ "ucircumflex", 0x0fb},
+{ "udiaeresis", 0x0fc},
+{ "yacute", 0x0fd},
+{ "thorn", 0x0fe},
+{ "ydiaeresis", 0x0ff},
+{"EuroSign", 0x20ac}, /* XK_EuroSign */
+
+ /* modifiers */
+{"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */
+{"Control_L", 0xffe3}, /* XK_Control_L */
+{"Control_R", 0xffe4}, /* XK_Control_R */
+{"Alt_L", 0xffe9}, /* XK_Alt_L */
+{"Alt_R", 0xffea}, /* XK_Alt_R */
+{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */
+{"Meta_L", 0xffe7}, /* XK_Meta_L */
+{"Meta_R", 0xffe8}, /* XK_Meta_R */
+{"Shift_L", 0xffe1}, /* XK_Shift_L */
+{"Shift_R", 0xffe2}, /* XK_Shift_R */
+{"Super_L", 0xffeb}, /* XK_Super_L */
+{"Super_R", 0xffec}, /* XK_Super_R */
+
+ /* special keys */
+{"BackSpace", 0xff08}, /* XK_BackSpace */
+{"Tab", 0xff09}, /* XK_Tab */
+{"Return", 0xff0d}, /* XK_Return */
+{"Right", 0xff53}, /* XK_Right */
+{"Left", 0xff51}, /* XK_Left */
+{"Up", 0xff52}, /* XK_Up */
+{"Down", 0xff54}, /* XK_Down */
+{"Page_Down", 0xff56}, /* XK_Page_Down */
+{"Page_Up", 0xff55}, /* XK_Page_Up */
+{"Insert", 0xff63}, /* XK_Insert */
+{"Delete", 0xffff}, /* XK_Delete */
+{"Home", 0xff50}, /* XK_Home */
+{"End", 0xff57}, /* XK_End */
+{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */
+{"KP_Home", 0xff95},
+{"KP_Left", 0xff96},
+{"KP_Up", 0xff97},
+{"KP_Right", 0xff98},
+{"KP_Down", 0xff99},
+{"KP_Prior", 0xff9a},
+{"KP_Page_Up", 0xff9a},
+{"KP_Next", 0xff9b},
+{"KP_Page_Down", 0xff9b},
+{"KP_End", 0xff9c},
+{"KP_Begin", 0xff9d},
+{"KP_Insert", 0xff9e},
+{"KP_Delete", 0xff9f},
+{"F1", 0xffbe}, /* XK_F1 */
+{"F2", 0xffbf}, /* XK_F2 */
+{"F3", 0xffc0}, /* XK_F3 */
+{"F4", 0xffc1}, /* XK_F4 */
+{"F5", 0xffc2}, /* XK_F5 */
+{"F6", 0xffc3}, /* XK_F6 */
+{"F7", 0xffc4}, /* XK_F7 */
+{"F8", 0xffc5}, /* XK_F8 */
+{"F9", 0xffc6}, /* XK_F9 */
+{"F10", 0xffc7}, /* XK_F10 */
+{"F11", 0xffc8}, /* XK_F11 */
+{"F12", 0xffc9}, /* XK_F12 */
+{"F13", 0xffca}, /* XK_F13 */
+{"F14", 0xffcb}, /* XK_F14 */
+{"F15", 0xffcc}, /* XK_F15 */
+{"Sys_Req", 0xff15}, /* XK_Sys_Req */
+{"KP_0", 0xffb0}, /* XK_KP_0 */
+{"KP_1", 0xffb1}, /* XK_KP_1 */
+{"KP_2", 0xffb2}, /* XK_KP_2 */
+{"KP_3", 0xffb3}, /* XK_KP_3 */
+{"KP_4", 0xffb4}, /* XK_KP_4 */
+{"KP_5", 0xffb5}, /* XK_KP_5 */
+{"KP_6", 0xffb6}, /* XK_KP_6 */
+{"KP_7", 0xffb7}, /* XK_KP_7 */
+{"KP_8", 0xffb8}, /* XK_KP_8 */
+{"KP_9", 0xffb9}, /* XK_KP_9 */
+{"KP_Add", 0xffab}, /* XK_KP_Add */
+{"KP_Separator", 0xffac},/* XK_KP_Separator */
+{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */
+{"KP_Divide", 0xffaf}, /* XK_KP_Divide */
+{"KP_Enter", 0xff8d}, /* XK_KP_Enter */
+{"KP_Equal", 0xffbd}, /* XK_KP_Equal */
+{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */
+{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */
+{"help", 0xff6a}, /* XK_Help */
+{"Menu", 0xff67}, /* XK_Menu */
+{"Print", 0xff61}, /* XK_Print */
+{"Mode_switch", 0xff7e}, /* XK_Mode_switch */
+{"Num_Lock", 0xff7f}, /* XK_Num_Lock */
+{"Pause", 0xff13}, /* XK_Pause */
+{"Escape", 0xff1b}, /* XK_Escape */
+
+/* dead keys */
+{"dead_grave", 0xfe50}, /* XK_dead_grave */
+{"dead_acute", 0xfe51}, /* XK_dead_acute */
+{"dead_circumflex", 0xfe52}, /* XK_dead_circumflex */
+{"dead_tilde", 0xfe53}, /* XK_dead_tilde */
+{"dead_macron", 0xfe54}, /* XK_dead_macron */
+{"dead_breve", 0xfe55}, /* XK_dead_breve */
+{"dead_abovedot", 0xfe56}, /* XK_dead_abovedot */
+{"dead_diaeresis", 0xfe57}, /* XK_dead_diaeresis */
+{"dead_abovering", 0xfe58}, /* XK_dead_abovering */
+{"dead_doubleacute", 0xfe59}, /* XK_dead_doubleacute */
+{"dead_caron", 0xfe5a}, /* XK_dead_caron */
+{"dead_cedilla", 0xfe5b}, /* XK_dead_cedilla */
+{"dead_ogonek", 0xfe5c}, /* XK_dead_ogonek */
+{"dead_iota", 0xfe5d}, /* XK_dead_iota */
+{"dead_voiced_sound", 0xfe5e}, /* XK_dead_voiced_sound */
+{"dead_semivoiced_sound", 0xfe5f}, /* XK_dead_semivoiced_sound */
+{"dead_belowdot", 0xfe60}, /* XK_dead_belowdot */
+{"dead_hook", 0xfe61}, /* XK_dead_hook */
+{"dead_horn", 0xfe62}, /* XK_dead_horn */
+
+
+ /* localized keys */
+{"BackApostrophe", 0xff21},
+{"Muhenkan", 0xff22},
+{"Katakana", 0xff27},
+{"Hankaku", 0xff29},
+{"Zenkaku_Hankaku", 0xff2a},
+{"Henkan_Mode_Real", 0xff23},
+{"Henkan_Mode_Ultra", 0xff3e},
+{"backslash_ja", 0xffa5},
+{"Katakana_Real", 0xff25},
+{"Eisu_toggle", 0xff30},
+
+{NULL,0},
+};
diff --git a/ui/x_keymap.c b/ui/x_keymap.c
new file mode 100644
index 0000000..b9b0944
--- /dev/null
+++ b/ui/x_keymap.c
@@ -0,0 +1,168 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "x_keymap.h"
+
+static const uint8_t x_keycode_to_pc_keycode[115] = {
+ 0xc7, /* 97 Home */
+ 0xc8, /* 98 Up */
+ 0xc9, /* 99 PgUp */
+ 0xcb, /* 100 Left */
+ 0x4c, /* 101 KP-5 */
+ 0xcd, /* 102 Right */
+ 0xcf, /* 103 End */
+ 0xd0, /* 104 Down */
+ 0xd1, /* 105 PgDn */
+ 0xd2, /* 106 Ins */
+ 0xd3, /* 107 Del */
+ 0x9c, /* 108 Enter */
+ 0x9d, /* 109 Ctrl-R */
+ 0x0, /* 110 Pause */
+ 0xb7, /* 111 Print */
+ 0xb5, /* 112 Divide */
+ 0xb8, /* 113 Alt-R */
+ 0xc6, /* 114 Break */
+ 0x0, /* 115 */
+ 0x0, /* 116 */
+ 0x0, /* 117 */
+ 0x0, /* 118 */
+ 0x0, /* 119 */
+ 0x0, /* 120 */
+ 0x0, /* 121 */
+ 0x0, /* 122 */
+ 0x0, /* 123 */
+ 0x0, /* 124 */
+ 0x0, /* 125 */
+ 0x0, /* 126 */
+ 0x0, /* 127 */
+ 0x0, /* 128 */
+ 0x79, /* 129 Henkan */
+ 0x0, /* 130 */
+ 0x7b, /* 131 Muhenkan */
+ 0x0, /* 132 */
+ 0x7d, /* 133 Yen */
+ 0x0, /* 134 */
+ 0x0, /* 135 */
+ 0x47, /* 136 KP_7 */
+ 0x48, /* 137 KP_8 */
+ 0x49, /* 138 KP_9 */
+ 0x4b, /* 139 KP_4 */
+ 0x4c, /* 140 KP_5 */
+ 0x4d, /* 141 KP_6 */
+ 0x4f, /* 142 KP_1 */
+ 0x50, /* 143 KP_2 */
+ 0x51, /* 144 KP_3 */
+ 0x52, /* 145 KP_0 */
+ 0x53, /* 146 KP_. */
+ 0x47, /* 147 KP_HOME */
+ 0x48, /* 148 KP_UP */
+ 0x49, /* 149 KP_PgUp */
+ 0x4b, /* 150 KP_Left */
+ 0x4c, /* 151 KP_ */
+ 0x4d, /* 152 KP_Right */
+ 0x4f, /* 153 KP_End */
+ 0x50, /* 154 KP_Down */
+ 0x51, /* 155 KP_PgDn */
+ 0x52, /* 156 KP_Ins */
+ 0x53, /* 157 KP_Del */
+};
+
+/* This table is generated based off the xfree86 -> scancode mapping above
+ * and the keycode mappings in /usr/share/X11/xkb/keycodes/evdev
+ * and /usr/share/X11/xkb/keycodes/xfree86
+ */
+
+static const uint8_t evdev_keycode_to_pc_keycode[61] = {
+ 0, /* 97 EVDEV - RO ("Internet" Keyboards) */
+ 0, /* 98 EVDEV - KATA (Katakana) */
+ 0, /* 99 EVDEV - HIRA (Hiragana) */
+ 0x79, /* 100 EVDEV - HENK (Henkan) */
+ 0x70, /* 101 EVDEV - HKTG (Hiragana/Katakana toggle) */
+ 0x7b, /* 102 EVDEV - MUHE (Muhenkan) */
+ 0, /* 103 EVDEV - JPCM (KPJPComma) */
+ 0x9c, /* 104 KPEN */
+ 0x9d, /* 105 RCTL */
+ 0xb5, /* 106 KPDV */
+ 0xb7, /* 107 PRSC */
+ 0xb8, /* 108 RALT */
+ 0, /* 109 EVDEV - LNFD ("Internet" Keyboards) */
+ 0xc7, /* 110 HOME */
+ 0xc8, /* 111 UP */
+ 0xc9, /* 112 PGUP */
+ 0xcb, /* 113 LEFT */
+ 0xcd, /* 114 RGHT */
+ 0xcf, /* 115 END */
+ 0xd0, /* 116 DOWN */
+ 0xd1, /* 117 PGDN */
+ 0xd2, /* 118 INS */
+ 0xd3, /* 119 DELE */
+ 0, /* 120 EVDEV - I120 ("Internet" Keyboards) */
+ 0, /* 121 EVDEV - MUTE */
+ 0, /* 122 EVDEV - VOL- */
+ 0, /* 123 EVDEV - VOL+ */
+ 0, /* 124 EVDEV - POWR */
+ 0, /* 125 EVDEV - KPEQ */
+ 0, /* 126 EVDEV - I126 ("Internet" Keyboards) */
+ 0, /* 127 EVDEV - PAUS */
+ 0, /* 128 EVDEV - ???? */
+ 0, /* 129 EVDEV - I129 ("Internet" Keyboards) */
+ 0xf1, /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */
+ 0xf2, /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */
+ 0x7d, /* 132 AE13 (Yen)*/
+ 0xdb, /* 133 EVDEV - LWIN */
+ 0xdc, /* 134 EVDEV - RWIN */
+ 0xdd, /* 135 EVDEV - MENU */
+ 0, /* 136 EVDEV - STOP */
+ 0, /* 137 EVDEV - AGAI */
+ 0, /* 138 EVDEV - PROP */
+ 0, /* 139 EVDEV - UNDO */
+ 0, /* 140 EVDEV - FRNT */
+ 0, /* 141 EVDEV - COPY */
+ 0, /* 142 EVDEV - OPEN */
+ 0, /* 143 EVDEV - PAST */
+ 0, /* 144 EVDEV - FIND */
+ 0, /* 145 EVDEV - CUT */
+ 0, /* 146 EVDEV - HELP */
+ 0, /* 147 EVDEV - I147 */
+ 0, /* 148 EVDEV - I148 */
+ 0, /* 149 EVDEV - I149 */
+ 0, /* 150 EVDEV - I150 */
+ 0, /* 151 EVDEV - I151 */
+ 0, /* 152 EVDEV - I152 */
+ 0, /* 153 EVDEV - I153 */
+ 0, /* 154 EVDEV - I154 */
+ 0, /* 155 EVDEV - I156 */
+ 0, /* 156 EVDEV - I157 */
+ 0, /* 157 EVDEV - I158 */
+};
+
+uint8_t translate_xfree86_keycode(const int key)
+{
+ return x_keycode_to_pc_keycode[key];
+}
+
+uint8_t translate_evdev_keycode(const int key)
+{
+ return evdev_keycode_to_pc_keycode[key];
+}
diff --git a/ui/x_keymap.h b/ui/x_keymap.h
new file mode 100644
index 0000000..afde2e9
--- /dev/null
+++ b/ui/x_keymap.h
@@ -0,0 +1,32 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_X_KEYMAP_H
+#define QEMU_X_KEYMAP_H
+
+uint8_t translate_xfree86_keycode(const int key);
+
+uint8_t translate_evdev_keycode(const int key);
+
+#endif
diff --git a/usb-bsd.c b/usb-bsd.c
new file mode 100644
index 0000000..abcb60c
--- /dev/null
+++ b/usb-bsd.c
@@ -0,0 +1,643 @@
+/*
+ * BSD host USB redirector
+ *
+ * Copyright (c) 2006 Lonnie Mendez
+ * Portions of code and concepts borrowed from
+ * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "monitor.h"
+#include "hw/usb.h"
+
+/* usb.h declares these */
+#undef USB_SPEED_HIGH
+#undef USB_SPEED_FULL
+#undef USB_SPEED_LOW
+
+#include <sys/ioctl.h>
+#ifndef __DragonFly__
+#include <dev/usb/usb.h>
+#else
+#include <bus/usb/usb.h>
+#endif
+#include <signal.h>
+
+/* This value has maximum potential at 16.
+ * You should also set hw.usb.debug to gain
+ * more detailed view.
+ */
+//#define DEBUG
+#define UGEN_DEBUG_LEVEL 0
+
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+ int vendor_id, int product_id,
+ const char *product_name, int speed);
+static int usb_host_find_device(int *pbus_num, int *paddr,
+ const char *devname);
+
+typedef struct USBHostDevice {
+ USBDevice dev;
+ int ep_fd[USB_MAX_ENDPOINTS];
+ int devfd;
+ char devpath[32];
+} USBHostDevice;
+
+
+#if 0
+static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
+{
+ char buf[32];
+ int fd;
+
+ /* Get the address for this endpoint */
+ ep = UE_GET_ADDR(ep);
+
+ if (dev->ep_fd[ep] < 0) {
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
+#else
+ snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
+#endif
+ /* Try to open it O_RDWR first for those devices which have in and out
+ * endpoints with the same address (eg 0x02 and 0x82)
+ */
+ fd = open(buf, O_RDWR);
+ if (fd < 0 && errno == ENXIO)
+ fd = open(buf, mode);
+ if (fd < 0) {
+#ifdef DEBUG
+ printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
+ buf, strerror(errno));
+#endif
+ }
+ dev->ep_fd[ep] = fd;
+ }
+
+ return dev->ep_fd[ep];
+}
+
+static void ensure_eps_closed(USBHostDevice *dev)
+{
+ int epnum = 1;
+
+ if (!dev)
+ return;
+
+ while (epnum < USB_MAX_ENDPOINTS) {
+ if (dev->ep_fd[epnum] >= 0) {
+ close(dev->ep_fd[epnum]);
+ dev->ep_fd[epnum] = -1;
+ }
+ epnum++;
+ }
+}
+#endif
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+#if 0
+ USBHostDevice *s = (USBHostDevice *)dev;
+#endif
+}
+
+#if 0
+/* XXX:
+ * -check device states against transfer requests
+ * and return appropriate response
+ */
+static int usb_host_handle_control(USBDevice *dev,
+ int request,
+ int value,
+ int index,
+ int length,
+ uint8_t *data)
+{
+ USBHostDevice *s = (USBHostDevice *)dev;
+ struct usb_ctl_request req;
+ struct usb_alt_interface aiface;
+ int ret, timeout = 50;
+
+ if ((request >> 8) == UT_WRITE_DEVICE &&
+ (request & 0xff) == UR_SET_ADDRESS) {
+
+ /* specific SET_ADDRESS support */
+ dev->addr = value;
+ return 0;
+ } else if ((request >> 8) == UT_WRITE_DEVICE &&
+ (request & 0xff) == UR_SET_CONFIG) {
+
+ ensure_eps_closed(s); /* can't do this without all eps closed */
+
+ ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
+ if (ret < 0) {
+#ifdef DEBUG
+ printf("handle_control: failed to set configuration - %s\n",
+ strerror(errno));
+#endif
+ return USB_RET_STALL;
+ }
+
+ return 0;
+ } else if ((request >> 8) == UT_WRITE_INTERFACE &&
+ (request & 0xff) == UR_SET_INTERFACE) {
+
+ aiface.uai_interface_index = index;
+ aiface.uai_alt_no = value;
+
+ ensure_eps_closed(s); /* can't do this without all eps closed */
+ ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
+ if (ret < 0) {
+#ifdef DEBUG
+ printf("handle_control: failed to set alternate interface - %s\n",
+ strerror(errno));
+#endif
+ return USB_RET_STALL;
+ }
+
+ return 0;
+ } else {
+ req.ucr_request.bmRequestType = request >> 8;
+ req.ucr_request.bRequest = request & 0xff;
+ USETW(req.ucr_request.wValue, value);
+ USETW(req.ucr_request.wIndex, index);
+ USETW(req.ucr_request.wLength, length);
+ req.ucr_data = data;
+ req.ucr_flags = USBD_SHORT_XFER_OK;
+
+ ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if (ret < 0 && errno != EINVAL) {
+#else
+ if (ret < 0) {
+#endif
+#ifdef DEBUG
+ printf("handle_control: setting timeout failed - %s\n",
+ strerror(errno));
+#endif
+ }
+
+ ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
+ /* ugen returns EIO for usbd_do_request_ no matter what
+ * happens with the transfer */
+ if (ret < 0) {
+#ifdef DEBUG
+ printf("handle_control: error after request - %s\n",
+ strerror(errno));
+#endif
+ return USB_RET_NAK; // STALL
+ } else {
+ return req.ucr_actlen;
+ }
+ }
+}
+
+static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+{
+ USBHostDevice *s = (USBHostDevice *)dev;
+ int ret, fd, mode;
+ int one = 1, shortpacket = 0, timeout = 50;
+ sigset_t new_mask, old_mask;
+ uint8_t devep = p->devep;
+
+ /* protect data transfers from SIGALRM signal */
+ sigemptyset(&new_mask);
+ sigaddset(&new_mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
+
+ if (p->pid == USB_TOKEN_IN) {
+ devep |= 0x80;
+ mode = O_RDONLY;
+ shortpacket = 1;
+ } else {
+ mode = O_WRONLY;
+ }
+
+ fd = ensure_ep_open(s, devep, mode);
+ if (fd < 0) {
+ sigprocmask(SIG_SETMASK, &old_mask, NULL);
+ return USB_RET_NODEV;
+ }
+
+ if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
+#ifdef DEBUG
+ printf("handle_data: failed to set timeout - %s\n",
+ strerror(errno));
+#endif
+ }
+
+ if (shortpacket) {
+ if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
+#ifdef DEBUG
+ printf("handle_data: failed to set short xfer mode - %s\n",
+ strerror(errno));
+#endif
+ sigprocmask(SIG_SETMASK, &old_mask, NULL);
+ }
+ }
+
+ if (p->pid == USB_TOKEN_IN)
+ ret = read(fd, p->data, p->len);
+ else
+ ret = write(fd, p->data, p->len);
+
+ sigprocmask(SIG_SETMASK, &old_mask, NULL);
+
+ if (ret < 0) {
+#ifdef DEBUG
+ printf("handle_data: error after %s data - %s\n",
+ pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
+#endif
+ switch(errno) {
+ case ETIMEDOUT:
+ case EINTR:
+ return USB_RET_NAK;
+ default:
+ return USB_RET_STALL;
+ }
+ } else {
+ return ret;
+ }
+}
+#endif
+
+static void usb_host_handle_destroy(USBDevice *opaque)
+{
+ USBHostDevice *s = (USBHostDevice *)opaque;
+ int i;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ if (s->ep_fd[i] >= 0)
+ close(s->ep_fd[i]);
+
+ if (s->devfd < 0)
+ return;
+
+ close(s->devfd);
+
+ qemu_free(s);
+}
+
+static int usb_host_initfn(USBDevice *dev)
+{
+ return 0;
+}
+
+USBDevice *usb_host_device_open(const char *devname)
+{
+ struct usb_device_info bus_info, dev_info;
+ USBDevice *d = NULL;
+ USBHostDevice *dev, *ret = NULL;
+ char ctlpath[PATH_MAX + 1];
+ char buspath[PATH_MAX + 1];
+ int bfd, dfd, bus, address, i;
+ int ugendebug = UGEN_DEBUG_LEVEL;
+
+ if (usb_host_find_device(&bus, &address, devname) < 0) {
+ goto fail;
+ }
+
+ snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
+
+ bfd = open(buspath, O_RDWR);
+ if (bfd < 0) {
+#ifdef DEBUG
+ printf("usb_host_device_open: failed to open usb bus - %s\n",
+ strerror(errno));
+#endif
+ goto fail;
+ }
+
+ bus_info.udi_addr = address;
+ if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
+#ifdef DEBUG
+ printf("usb_host_device_open: failed to grab bus information - %s\n",
+ strerror(errno));
+#endif
+ goto fail_bfd;
+ }
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
+#else
+ snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
+#endif
+
+ dfd = open(ctlpath, O_RDWR);
+ if (dfd < 0) {
+ dfd = open(ctlpath, O_RDONLY);
+ if (dfd < 0) {
+#ifdef DEBUG
+ printf("usb_host_device_open: failed to open usb device %s - %s\n",
+ ctlpath, strerror(errno));
+#endif
+ }
+ goto fail_dfd;
+ }
+
+ if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
+#ifdef DEBUG
+ printf("usb_host_device_open: failed to grab device info - %s\n",
+ strerror(errno));
+#endif
+ goto fail_dfd;
+ }
+
+ d = usb_create(NULL /* FIXME */, "usb-host");
+ dev = DO_UPCAST(USBHostDevice, dev, d);
+
+ if (dev_info.udi_speed == 1) {
+ dev->dev.speed = USB_SPEED_LOW - 1;
+ } else {
+ dev->dev.speed = USB_SPEED_FULL - 1;
+ }
+
+ if (strncmp(dev_info.udi_product, "product", 7) != 0) {
+ pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+ dev_info.udi_product);
+ } else {
+ snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+ "host:%s", devname);
+ }
+
+ pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
+ pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]);
+
+ /* Mark the endpoints as not yet open */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
+ dev->ep_fd[i] = -1;
+ }
+
+ ioctl(dfd, USB_SETDEBUG, &ugendebug);
+
+ ret = (USBDevice *)dev;
+
+fail_dfd:
+ close(dfd);
+fail_bfd:
+ close(bfd);
+fail:
+ return ret;
+}
+
+static struct USBDeviceInfo usb_host_dev_info = {
+ .product_desc = "USB Host Device",
+ .qdev.name = "usb-host",
+ .qdev.size = sizeof(USBHostDevice),
+ .init = usb_host_initfn,
+ .handle_packet = usb_generic_handle_packet,
+ .handle_reset = usb_host_handle_reset,
+#if 0
+ .handle_control = usb_host_handle_control,
+ .handle_data = usb_host_handle_data,
+#endif
+ .handle_destroy = usb_host_handle_destroy,
+};
+
+static void usb_host_register_devices(void)
+{
+ usb_qdev_register(&usb_host_dev_info);
+}
+device_init(usb_host_register_devices)
+
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+ struct usb_device_info bus_info;
+ struct usb_device_info dev_info;
+ uint16_t vendor_id, product_id, class_id, speed;
+ int bfd, dfd, bus, address;
+ char busbuf[20], devbuf[20], product_name[256];
+ int ret = 0;
+
+ for (bus = 0; bus < 10; bus++) {
+
+ snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
+ bfd = open(busbuf, O_RDWR);
+ if (bfd < 0)
+ continue;
+
+ for (address = 1; address < 127; address++) {
+
+ bus_info.udi_addr = address;
+ if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
+ continue;
+
+ /* only list devices that can be used by generic layer */
+ if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
+ continue;
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
+#else
+ snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
+#endif
+
+ dfd = open(devbuf, O_RDONLY);
+ if (dfd < 0) {
+#ifdef DEBUG
+ printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
+ strerror(errno));
+#endif
+ continue;
+ }
+
+ if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
+ printf("usb_host_scan: couldn't get device information for %s - %s\n",
+ devbuf, strerror(errno));
+
+ // XXX: might need to fixup endianess of word values before copying over
+
+ vendor_id = dev_info.udi_vendorNo;
+ product_id = dev_info.udi_productNo;
+ class_id = dev_info.udi_class;
+ speed = dev_info.udi_speed;
+
+ if (strncmp(dev_info.udi_product, "product", 7) != 0)
+ pstrcpy(product_name, sizeof(product_name),
+ dev_info.udi_product);
+ else
+ product_name[0] = '\0';
+
+ ret = func(opaque, bus, address, class_id, vendor_id,
+ product_id, product_name, speed);
+
+ close(dfd);
+
+ if (ret)
+ goto the_end;
+ }
+
+ close(bfd);
+ }
+
+the_end:
+ return ret;
+}
+
+typedef struct FindDeviceState {
+ int vendor_id;
+ int product_id;
+ int bus_num;
+ int addr;
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
+ int class_id,
+ int vendor_id, int product_id,
+ const char *product_name, int speed)
+{
+ FindDeviceState *s = opaque;
+ if (vendor_id == s->vendor_id &&
+ product_id == s->product_id) {
+ s->bus_num = bus_num;
+ s->addr = addr;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/* the syntax is :
+ 'bus.addr' (decimal numbers) or
+ 'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr,
+ const char *devname)
+{
+ const char *p;
+ int ret;
+ FindDeviceState fs;
+
+ p = strchr(devname, '.');
+ if (p) {
+ *pbus_num = strtoul(devname, NULL, 0);
+ *paddr = strtoul(p + 1, NULL, 0);
+ return 0;
+ }
+ p = strchr(devname, ':');
+ if (p) {
+ fs.vendor_id = strtoul(devname, NULL, 16);
+ fs.product_id = strtoul(p + 1, NULL, 16);
+ ret = usb_host_scan(&fs, usb_host_find_device_scan);
+ if (ret) {
+ *pbus_num = fs.bus_num;
+ *paddr = fs.addr;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+ int class;
+ const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+ { USB_CLASS_AUDIO, "Audio"},
+ { USB_CLASS_COMM, "Communication"},
+ { USB_CLASS_HID, "HID"},
+ { USB_CLASS_HUB, "Hub" },
+ { USB_CLASS_PHYSICAL, "Physical" },
+ { USB_CLASS_PRINTER, "Printer" },
+ { USB_CLASS_MASS_STORAGE, "Storage" },
+ { USB_CLASS_CDC_DATA, "Data" },
+ { USB_CLASS_APP_SPEC, "Application Specific" },
+ { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+ { USB_CLASS_STILL_IMAGE, "Still Image" },
+ { USB_CLASS_CSCID, "Smart Card" },
+ { USB_CLASS_CONTENT_SEC, "Content Security" },
+ { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+ const struct usb_class_info *p;
+ for (p = usb_class_info; p->class != -1; p++) {
+ if (p->class == class)
+ break;
+ }
+ 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,
+ const char *product_name,
+ int speed)
+{
+ const char *class_str, *speed_str;
+
+ switch(speed) {
+ case USB_SPEED_LOW:
+ speed_str = "1.5";
+ break;
+ case USB_SPEED_FULL:
+ speed_str = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed_str = "480";
+ break;
+ default:
+ speed_str = "?";
+ break;
+ }
+
+ monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n",
+ bus_num, addr, speed_str);
+ class_str = usb_class_str(class_id);
+ if (class_str)
+ monitor_printf(mon, " %s:", class_str);
+ else
+ monitor_printf(mon, " Class %02x:", class_id);
+ monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+ if (product_name[0] != '\0')
+ monitor_printf(mon, ", %s", product_name);
+ monitor_printf(mon, "\n");
+}
+
+static int usb_host_info_device(void *opaque,
+ int bus_num, int addr,
+ 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,
+ product_name, speed);
+ return 0;
+}
+
+void usb_host_info(Monitor *mon)
+{
+ usb_host_scan(mon, usb_host_info_device);
+}
+
+/* XXX add this */
+int usb_host_device_close(const char *devname)
+{
+ return 0;
+}
diff --git a/usb-linux.c b/usb-linux.c
new file mode 100644
index 0000000..ccf7073
--- /dev/null
+++ b/usb-linux.c
@@ -0,0 +1,1722 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ * Support for host device auto connect & disconnect
+ * Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ * to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "monitor.h"
+#include "sysemu.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#include "hw/usb.h"
+
+/* We redefine it to avoid version problems */
+struct usb_ctrltransfer {
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint32_t timeout;
+ 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);
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#define USBDBG_DEVOPENED "husb: opened %s/devices\n"
+
+#define USBPROCBUS_PATH "/proc/bus/usb"
+#define PRODUCT_NAME_SZ 32
+#define MAX_ENDPOINTS 16
+#define USBDEVBUS_PATH "/dev/bus/usb"
+#define USBSYSBUS_PATH "/sys/bus/usb"
+
+static char *usb_host_device_path;
+
+#define USB_FS_NONE 0
+#define USB_FS_PROC 1
+#define USB_FS_DEV 2
+#define USB_FS_SYS 3
+
+static int usb_fs_type;
+
+/* endpoint association data */
+struct endp_data {
+ uint8_t type;
+ uint8_t halted;
+};
+
+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 contigious 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;
+ uint32_t vendor_id;
+ uint32_t product_id;
+};
+
+typedef struct USBHostDevice {
+ USBDevice dev;
+ int fd;
+
+ uint8_t descr[1024];
+ int descr_len;
+ int configuration;
+ int ninterfaces;
+ int closing;
+ Notifier exit;
+
+ struct ctrl_struct ctrl;
+ struct endp_data endp_table[MAX_ENDPOINTS];
+
+ /* Host side address */
+ int bus_num;
+ int addr;
+ int devpath;
+ struct USBAutoFilter match;
+
+ QTAILQ_ENTRY(USBHostDevice) next;
+} USBHostDevice;
+
+static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
+
+static int usb_host_close(USBHostDevice *dev);
+static int parse_filter(const char *spec, struct USBAutoFilter *f);
+static void usb_host_auto_check(void *unused);
+static int usb_host_read_file(char *line, size_t line_size,
+ const char *device_file, const char *device_name);
+
+static int is_isoc(USBHostDevice *s, int ep)
+{
+ return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO;
+}
+
+static int is_halted(USBHostDevice *s, int ep)
+{
+ return s->endp_table[ep - 1].halted;
+}
+
+static void clear_halt(USBHostDevice *s, int ep)
+{
+ s->endp_table[ep - 1].halted = 0;
+}
+
+static void set_halt(USBHostDevice *s, int ep)
+{
+ s->endp_table[ep - 1].halted = 1;
+}
+
+/*
+ * Async URB state.
+ * We always allocate one isoc descriptor even for bulk transfers
+ * to simplify allocation and casts.
+ */
+typedef struct AsyncURB
+{
+ struct usbdevfs_urb urb;
+ struct usbdevfs_iso_packet_desc isocpd;
+
+ USBPacket *packet;
+ USBHostDevice *hdev;
+} AsyncURB;
+
+static AsyncURB *async_alloc(void)
+{
+ return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+}
+
+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;
+ AsyncURB *aurb;
+
+ while (1) {
+ USBPacket *p;
+
+ int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+ if (r < 0) {
+ if (errno == EAGAIN) {
+ return;
+ }
+ if (errno == ENODEV && !s->closing) {
+ printf("husb: device %d.%d disconnected\n",
+ s->bus_num, s->addr);
+ usb_host_close(s);
+ usb_host_auto_check(NULL);
+ return;
+ }
+
+ DPRINTF("husb: async. reap urb failed errno %d\n", errno);
+ return;
+ }
+
+ p = aurb->packet;
+
+ DPRINTF("husb: async completed. aurb %p status %d alen %d\n",
+ aurb, aurb->urb.status, aurb->urb.actual_length);
+
+ if (p) {
+ 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:
+ set_halt(s, p->devep);
+ p->len = USB_RET_STALL;
+ break;
+
+ default:
+ p->len = USB_RET_NAK;
+ break;
+ }
+
+ usb_packet_complete(p);
+ }
+
+ async_free(aurb);
+ }
+}
+
+static void async_cancel(USBPacket *unused, void *opaque)
+{
+ AsyncURB *aurb = opaque;
+ USBHostDevice *s = aurb->hdev;
+
+ DPRINTF("husb: async cancel. aurb %p\n", aurb);
+
+ /* 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);
+ }
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
+{
+ int dev_descr_len, config_descr_len;
+ int interface, nb_interfaces;
+ int ret, i;
+
+ if (configuration == 0) /* address state - ignore */
+ return 1;
+
+ DPRINTF("husb: claiming interfaces. config %d\n", configuration);
+
+ i = 0;
+ dev_descr_len = dev->descr[0];
+ if (dev_descr_len > dev->descr_len) {
+ goto fail;
+ }
+
+ i += dev_descr_len;
+ while (i < dev->descr_len) {
+ DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n",
+ i, dev->descr_len,
+ dev->descr[i], dev->descr[i+1]);
+
+ if (dev->descr[i+1] != USB_DT_CONFIG) {
+ i += dev->descr[i];
+ continue;
+ }
+ config_descr_len = dev->descr[i];
+
+ printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration);
+
+ if (configuration < 0 || configuration == dev->descr[i + 5]) {
+ configuration = dev->descr[i + 5];
+ break;
+ }
+
+ i += config_descr_len;
+ }
+
+ if (i >= dev->descr_len) {
+ fprintf(stderr,
+ "husb: update iface failed. no matching configuration\n");
+ goto fail;
+ }
+ nb_interfaces = dev->descr[i + 4];
+
+#ifdef USBDEVFS_DISCONNECT
+ /* earlier Linux 2.4 do not support that */
+ {
+ struct usbdevfs_ioctl ctrl;
+ for (interface = 0; interface < nb_interfaces; interface++) {
+ ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+ ctrl.ifno = interface;
+ ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+ if (ret < 0 && errno != ENODATA) {
+ perror("USBDEVFS_DISCONNECT");
+ goto fail;
+ }
+ }
+ }
+#endif
+
+ /* XXX: only grab if all interfaces are free */
+ for (interface = 0; interface < nb_interfaces; interface++) {
+ ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+ if (ret < 0) {
+ if (errno == EBUSY) {
+ printf("husb: update iface. device already grabbed\n");
+ } else {
+ perror("husb: failed to claim interface");
+ }
+ fail:
+ return 0;
+ }
+ }
+
+ printf("husb: %d interfaces claimed for configuration %d\n",
+ nb_interfaces, configuration);
+
+ dev->ninterfaces = nb_interfaces;
+ dev->configuration = configuration;
+ return 1;
+}
+
+static int usb_host_release_interfaces(USBHostDevice *s)
+{
+ int ret, i;
+
+ DPRINTF("husb: releasing interfaces\n");
+
+ for (i = 0; i < s->ninterfaces; i++) {
+ ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
+ if (ret < 0) {
+ perror("husb: failed to release interface");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+ USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+ DPRINTF("husb: reset device %u.%u\n", s->bus_num, s->addr);
+
+ ioctl(s->fd, USBDEVFS_RESET);
+
+ usb_host_claim_interfaces(s, s->configuration);
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+ USBHostDevice *s = (USBHostDevice *)dev;
+
+ usb_host_close(s);
+ QTAILQ_REMOVE(&hostdevs, s, next);
+ qemu_remove_exit_notifier(&s->exit);
+}
+
+static int usb_linux_update_endp_table(USBHostDevice *s);
+
+static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
+{
+ struct usbdevfs_urb *urb;
+ AsyncURB *aurb;
+ int ret;
+
+ aurb = async_alloc();
+ aurb->hdev = s;
+ aurb->packet = p;
+
+ urb = &aurb->urb;
+
+ if (p->pid == USB_TOKEN_IN) {
+ urb->endpoint = p->devep | 0x80;
+ } else {
+ urb->endpoint = p->devep;
+ }
+
+ if (is_halted(s, p->devep)) {
+ ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
+ if (ret < 0) {
+ DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n",
+ urb->endpoint, errno);
+ return USB_RET_NAK;
+ }
+ clear_halt(s, p->devep);
+ }
+
+ urb->buffer = p->data;
+ urb->buffer_length = p->len;
+
+ if (is_isoc(s, p->devep)) {
+ /* Setup ISOC transfer */
+ urb->type = USBDEVFS_URB_TYPE_ISO;
+ urb->flags = USBDEVFS_URB_ISO_ASAP;
+ urb->number_of_packets = 1;
+ urb->iso_frame_desc[0].length = p->len;
+ } else {
+ /* Setup bulk transfer */
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ }
+
+ urb->usercontext = s;
+
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+ DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n",
+ urb->endpoint, p->len, 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;
+ }
+ }
+
+ usb_defer_packet(p, async_cancel, aurb);
+ return USB_RET_ASYNC;
+}
+
+static int ctrl_error(void)
+{
+ if (errno == ETIMEDOUT) {
+ return USB_RET_NAK;
+ } else {
+ return USB_RET_STALL;
+ }
+}
+
+static int usb_host_set_address(USBHostDevice *s, int addr)
+{
+ DPRINTF("husb: ctrl set addr %u\n", addr);
+ s->dev.addr = addr;
+ return 0;
+}
+
+static int usb_host_set_config(USBHostDevice *s, int config)
+{
+ usb_host_release_interfaces(s);
+
+ int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+
+ DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+
+ if (ret < 0) {
+ return ctrl_error();
+ }
+ usb_host_claim_interfaces(s, config);
+ return 0;
+}
+
+static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
+{
+ struct usbdevfs_setinterface si;
+ int ret;
+
+ si.interface = iface;
+ si.altsetting = alt;
+ ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+
+ DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n",
+ iface, alt, ret, errno);
+
+ if (ret < 0) {
+ return ctrl_error();
+ }
+ usb_linux_update_endp_table(s);
+ return 0;
+}
+
+static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
+{
+ struct usbdevfs_urb *urb;
+ AsyncURB *aurb;
+ int ret, value, index;
+ int buffer_len;
+
+ /*
+ * 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);
+
+ 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);
+
+ if (s->ctrl.req.bRequestType == 0) {
+ switch (s->ctrl.req.bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ return usb_host_set_address(s, value);
+
+ case 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) {
+ 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));
+ return USB_RET_STALL;
+ }
+
+ aurb = async_alloc();
+ aurb->hdev = s;
+ aurb->packet = p;
+
+ /*
+ * Setup ctrl transfer.
+ *
+ * s->ctrl is layed out such that data buffer immediately follows
+ * 'req' struct which is exactly what usbdevfs expects.
+ */
+ urb = &aurb->urb;
+
+ urb->type = USBDEVFS_URB_TYPE_CONTROL;
+ urb->endpoint = p->devep;
+
+ urb->buffer = &s->ctrl.req;
+ urb->buffer_length = buffer_len;
+
+ urb->usercontext = s;
+
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+ DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, 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;
+ }
+ }
+
+ usb_defer_packet(p, async_cancel, aurb);
+ 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;
+ struct usb_ctrltransfer ct;
+ int ret;
+
+ if (usb_fs_type == USB_FS_SYS) {
+ char device_name[32], line[1024];
+ int configuration;
+
+ sprintf(device_name, "%d-%d", s->bus_num, s->devpath);
+
+ if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
+ device_name)) {
+ goto usbdevfs;
+ }
+ if (sscanf(line, "%d", &configuration) != 1) {
+ goto usbdevfs;
+ }
+ return configuration;
+ }
+
+usbdevfs:
+ ct.bRequestType = USB_DIR_IN;
+ ct.bRequest = USB_REQ_GET_CONFIGURATION;
+ ct.wValue = 0;
+ ct.wIndex = 0;
+ ct.wLength = 1;
+ ct.data = &configuration;
+ ct.timeout = 50;
+
+ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ if (ret < 0) {
+ perror("usb_linux_get_configuration");
+ return -1;
+ }
+
+ /* in address state */
+ if (configuration == 0) {
+ return -1;
+ }
+
+ return configuration;
+}
+
+/* returns 1 on problem encountered or 0 for success */
+static int usb_linux_update_endp_table(USBHostDevice *s)
+{
+ uint8_t *descriptors;
+ uint8_t devep, type, configuration, alt_interface;
+ struct usb_ctrltransfer ct;
+ int interface, ret, length, i;
+
+ i = usb_linux_get_configuration(s);
+ if (i < 0)
+ return 1;
+ configuration = i;
+
+ /* get the desired configuration, interface, and endpoint descriptors
+ * from device description */
+ descriptors = &s->descr[18];
+ length = s->descr_len - 18;
+ i = 0;
+
+ if (descriptors[i + 1] != USB_DT_CONFIG ||
+ descriptors[i + 5] != configuration) {
+ DPRINTF("invalid descriptor data - configuration\n");
+ return 1;
+ }
+ i += descriptors[i];
+
+ while (i < length) {
+ if (descriptors[i + 1] != USB_DT_INTERFACE ||
+ (descriptors[i + 1] == USB_DT_INTERFACE &&
+ descriptors[i + 4] == 0)) {
+ i += descriptors[i];
+ continue;
+ }
+
+ interface = descriptors[i + 2];
+
+ ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
+ ct.bRequest = USB_REQ_GET_INTERFACE;
+ ct.wValue = 0;
+ ct.wIndex = interface;
+ ct.wLength = 1;
+ ct.data = &alt_interface;
+ ct.timeout = 50;
+
+ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+ if (ret < 0) {
+ alt_interface = interface;
+ }
+
+ /* the current interface descriptor is the active interface
+ * and has endpoints */
+ if (descriptors[i + 3] != alt_interface) {
+ i += descriptors[i];
+ continue;
+ }
+
+ /* advance to the endpoints */
+ while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
+ i += descriptors[i];
+ }
+
+ if (i >= length)
+ break;
+
+ while (i < length) {
+ if (descriptors[i + 1] != USB_DT_ENDPOINT) {
+ break;
+ }
+
+ devep = descriptors[i + 2];
+ switch (descriptors[i + 3] & 0x3) {
+ case 0x00:
+ type = USBDEVFS_URB_TYPE_CONTROL;
+ break;
+ case 0x01:
+ type = USBDEVFS_URB_TYPE_ISO;
+ break;
+ case 0x02:
+ type = USBDEVFS_URB_TYPE_BULK;
+ break;
+ case 0x03:
+ type = USBDEVFS_URB_TYPE_INTERRUPT;
+ break;
+ default:
+ DPRINTF("usb_host: malformed endpoint type\n");
+ type = USBDEVFS_URB_TYPE_BULK;
+ }
+ s->endp_table[(devep & 0xf) - 1].type = type;
+ s->endp_table[(devep & 0xf) - 1].halted = 0;
+
+ i += descriptors[i];
+ }
+ }
+ return 0;
+}
+
+static int usb_host_open(USBHostDevice *dev, int bus_num,
+ int addr, int devpath, const char *prod_name)
+{
+ int fd = -1, ret;
+ struct usbdevfs_connectinfo ci;
+ char buf[1024];
+
+ if (dev->fd != -1) {
+ goto fail;
+ }
+ printf("husb: open device %d.%d\n", bus_num, addr);
+
+ if (!usb_host_device_path) {
+ perror("husb: USB Host Device Path not set");
+ goto fail;
+ }
+ snprintf(buf, sizeof(buf), "%s/%03d/%03d", usb_host_device_path,
+ bus_num, addr);
+ fd = open(buf, O_RDWR | O_NONBLOCK);
+ if (fd < 0) {
+ perror(buf);
+ goto fail;
+ }
+ DPRINTF("husb: opened %s\n", buf);
+
+ dev->bus_num = bus_num;
+ dev->addr = addr;
+ dev->devpath = devpath;
+ dev->fd = fd;
+
+ /* read the device description */
+ dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
+ if (dev->descr_len <= 0) {
+ perror("husb: reading device data failed");
+ goto fail;
+ }
+
+#ifdef DEBUG
+ {
+ int x;
+ printf("=== begin dumping device descriptor data ===\n");
+ for (x = 0; x < dev->descr_len; x++) {
+ printf("%02x ", dev->descr[x]);
+ }
+ printf("\n=== end dumping device descriptor data ===\n");
+ }
+#endif
+
+
+ /*
+ * Initial configuration is -1 which makes us claim first
+ * available config. We used to start with 1, which does not
+ * always work. I've seen devices where first config starts
+ * with 2.
+ */
+ if (!usb_host_claim_interfaces(dev, -1)) {
+ goto fail;
+ }
+
+ ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
+ if (ret < 0) {
+ perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
+ goto fail;
+ }
+
+ printf("husb: grabbed usb device %d.%d\n", bus_num, addr);
+
+ ret = usb_linux_update_endp_table(dev);
+ if (ret) {
+ goto fail;
+ }
+
+ if (ci.slow) {
+ dev->dev.speed = USB_SPEED_LOW;
+ } else {
+ dev->dev.speed = USB_SPEED_HIGH;
+ }
+
+ if (!prod_name || prod_name[0] == '\0') {
+ snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+ "host:%d.%d", bus_num, addr);
+ } else {
+ pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc),
+ prod_name);
+ }
+
+ /* USB devio uses 'write' flag to check for async completions */
+ qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
+
+ usb_device_attach(&dev->dev);
+ return 0;
+
+fail:
+ dev->fd = -1;
+ if (fd != -1) {
+ close(fd);
+ }
+ return -1;
+}
+
+static int usb_host_close(USBHostDevice *dev)
+{
+ if (dev->fd == -1) {
+ return -1;
+ }
+
+ qemu_set_fd_handler(dev->fd, NULL, NULL, NULL);
+ dev->closing = 1;
+ async_complete(dev);
+ dev->closing = 0;
+ usb_device_detach(&dev->dev);
+ ioctl(dev->fd, USBDEVFS_RESET);
+ close(dev->fd);
+ dev->fd = -1;
+ return 0;
+}
+
+static void usb_host_exit_notifier(struct Notifier* n)
+{
+ USBHostDevice *s = container_of(n, USBHostDevice, exit);
+
+ if (s->fd != -1) {
+ ioctl(s->fd, USBDEVFS_RESET);
+ }
+}
+
+static int usb_host_initfn(USBDevice *dev)
+{
+ USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+
+ dev->auto_attach = 0;
+ s->fd = -1;
+ QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+ s->exit.notify = usb_host_exit_notifier;
+ qemu_add_exit_notifier(&s->exit);
+ usb_host_auto_check(NULL);
+ return 0;
+}
+
+static struct USBDeviceInfo usb_host_dev_info = {
+ .product_desc = "USB Host Device",
+ .qdev.name = "usb-host",
+ .qdev.size = sizeof(USBHostDevice),
+ .init = usb_host_initfn,
+ .handle_packet = usb_host_handle_packet,
+ .handle_reset = usb_host_handle_reset,
+ .handle_destroy = usb_host_handle_destroy,
+ .usbdevice_name = "host",
+ .usbdevice_init = usb_host_device_open,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0),
+ DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0),
+ DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
+ DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void usb_host_register_devices(void)
+{
+ usb_qdev_register(&usb_host_dev_info);
+}
+device_init(usb_host_register_devices)
+
+USBDevice *usb_host_device_open(const char *devname)
+{
+ struct USBAutoFilter filter;
+ USBDevice *dev;
+ char *p;
+
+ dev = usb_create(NULL /* FIXME */, "usb-host");
+
+ if (strstr(devname, "auto:")) {
+ if (parse_filter(devname, &filter) < 0) {
+ goto fail;
+ }
+ } else {
+ if ((p = strchr(devname, '.'))) {
+ filter.bus_num = strtoul(devname, NULL, 0);
+ filter.addr = strtoul(p + 1, NULL, 0);
+ filter.vendor_id = 0;
+ filter.product_id = 0;
+ } else if ((p = strchr(devname, ':'))) {
+ filter.bus_num = 0;
+ filter.addr = 0;
+ filter.vendor_id = strtoul(devname, NULL, 16);
+ filter.product_id = strtoul(p + 1, NULL, 16);
+ } else {
+ goto fail;
+ }
+ }
+
+ qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num);
+ qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr);
+ qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id);
+ qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
+ qdev_init_nofail(&dev->qdev);
+ return dev;
+
+fail:
+ qdev_free(&dev->qdev);
+ return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+#if 0
+ char product_name[PRODUCT_NAME_SZ];
+ int bus_num, addr;
+ USBHostDevice *s;
+
+ if (strstr(devname, "auto:")) {
+ return usb_host_auto_del(devname);
+ }
+ if (usb_host_find_device(&bus_num, &addr, product_name,
+ sizeof(product_name), devname) < 0) {
+ return -1;
+ }
+ s = hostdev_find(bus_num, addr);
+ if (s) {
+ usb_device_delete_addr(s->bus_num, s->dev.addr);
+ return 0;
+ }
+#endif
+
+ return -1;
+}
+
+static int get_tag_value(char *buf, int buf_size,
+ const char *str, const char *tag,
+ const char *stopchars)
+{
+ const char *p;
+ char *q;
+ p = strstr(str, tag);
+ if (!p) {
+ return -1;
+ }
+ p += strlen(tag);
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ q = buf;
+ while (*p != '\0' && !strchr(stopchars, *p)) {
+ if ((q - buf) < (buf_size - 1)) {
+ *q++ = *p;
+ }
+ p++;
+ }
+ *q = '\0';
+ return q - buf;
+}
+
+/*
+ * Use /proc/bus/usb/devices or /dev/bus/usb/devices file to determine
+ * host's USB devices. This is legacy support since many distributions
+ * are moving to /sys/bus/usb
+ */
+static int usb_host_scan_dev(void *opaque, USBScanFunc *func)
+{
+ FILE *f = NULL;
+ char line[1024];
+ char buf[1024];
+ int bus_num, addr, speed, device_count, class_id, product_id, vendor_id;
+ char product_name[512];
+ int ret = 0;
+
+ if (!usb_host_device_path) {
+ perror("husb: USB Host Device Path not set");
+ goto the_end;
+ }
+ snprintf(line, sizeof(line), "%s/devices", usb_host_device_path);
+ f = fopen(line, "r");
+ if (!f) {
+ perror("husb: cannot open devices file");
+ goto the_end;
+ }
+
+ device_count = 0;
+ bus_num = addr = speed = class_id = product_id = vendor_id = 0;
+ for(;;) {
+ if (fgets(line, sizeof(line), f) == NULL) {
+ break;
+ }
+ if (strlen(line) > 0) {
+ line[strlen(line) - 1] = '\0';
+ }
+ if (line[0] == 'T' && line[1] == ':') {
+ if (device_count && (vendor_id || product_id)) {
+ /* New device. Add the previously discovered device. */
+ ret = func(opaque, bus_num, addr, 0, class_id, vendor_id,
+ product_id, product_name, speed);
+ if (ret) {
+ goto the_end;
+ }
+ }
+ if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0) {
+ goto fail;
+ }
+ bus_num = atoi(buf);
+ if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) {
+ goto fail;
+ }
+ addr = atoi(buf);
+ if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) {
+ goto fail;
+ }
+ if (!strcmp(buf, "480")) {
+ speed = USB_SPEED_HIGH;
+ } else if (!strcmp(buf, "1.5")) {
+ speed = USB_SPEED_LOW;
+ } else {
+ speed = USB_SPEED_FULL;
+ }
+ product_name[0] = '\0';
+ class_id = 0xff;
+ device_count++;
+ product_id = 0;
+ vendor_id = 0;
+ } else if (line[0] == 'P' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0) {
+ goto fail;
+ }
+ vendor_id = strtoul(buf, NULL, 16);
+ if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0) {
+ goto fail;
+ }
+ product_id = strtoul(buf, NULL, 16);
+ } else if (line[0] == 'S' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0) {
+ goto fail;
+ }
+ pstrcpy(product_name, sizeof(product_name), buf);
+ } else if (line[0] == 'D' && line[1] == ':') {
+ if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0) {
+ goto fail;
+ }
+ class_id = strtoul(buf, NULL, 16);
+ }
+ fail: ;
+ }
+ if (device_count && (vendor_id || product_id)) {
+ /* Add the last device. */
+ ret = func(opaque, bus_num, addr, 0, class_id, vendor_id,
+ product_id, product_name, speed);
+ }
+ the_end:
+ if (f) {
+ fclose(f);
+ }
+ return ret;
+}
+
+/*
+ * Read sys file-system device file
+ *
+ * @line address of buffer to put file contents in
+ * @line_size size of line
+ * @device_file path to device file (printf format string)
+ * @device_name device being opened (inserted into device_file)
+ *
+ * @return 0 failed, 1 succeeded ('line' contains data)
+ */
+static int usb_host_read_file(char *line, size_t line_size,
+ const char *device_file, const char *device_name)
+{
+ FILE *f;
+ int ret = 0;
+ char filename[PATH_MAX];
+
+ snprintf(filename, PATH_MAX, USBSYSBUS_PATH "/devices/%s/%s", device_name,
+ device_file);
+ f = fopen(filename, "r");
+ if (f) {
+ ret = fgets(line, line_size, f) != NULL;
+ fclose(f);
+ }
+
+ return ret;
+}
+
+/*
+ * Use /sys/bus/usb/devices/ directory to determine host's USB
+ * devices.
+ *
+ * This code is based on Robert Schiele's original patches posted to
+ * the Novell bug-tracker https://bugzilla.novell.com/show_bug.cgi?id=241950
+ */
+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 ret = 0;
+ char product_name[512];
+ struct dirent *de;
+
+ dir = opendir(USBSYSBUS_PATH "/devices");
+ if (!dir) {
+ perror("husb: cannot open devices directory");
+ goto the_end;
+ }
+
+ 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 (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
+ goto the_end;
+ }
+ if (sscanf(line, "%d", &addr) != 1) {
+ goto the_end;
+ }
+ if (!usb_host_read_file(line, sizeof(line), "bDeviceClass",
+ de->d_name)) {
+ goto the_end;
+ }
+ if (sscanf(line, "%x", &class_id) != 1) {
+ goto the_end;
+ }
+
+ if (!usb_host_read_file(line, sizeof(line), "idVendor",
+ de->d_name)) {
+ goto the_end;
+ }
+ if (sscanf(line, "%x", &vendor_id) != 1) {
+ goto the_end;
+ }
+ if (!usb_host_read_file(line, sizeof(line), "idProduct",
+ de->d_name)) {
+ goto the_end;
+ }
+ if (sscanf(line, "%x", &product_id) != 1) {
+ goto the_end;
+ }
+ if (!usb_host_read_file(line, sizeof(line), "product",
+ de->d_name)) {
+ *product_name = 0;
+ } else {
+ if (strlen(line) > 0) {
+ line[strlen(line) - 1] = '\0';
+ }
+ pstrcpy(product_name, sizeof(product_name), line);
+ }
+
+ if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) {
+ goto the_end;
+ }
+ if (!strcmp(line, "480\n")) {
+ speed = USB_SPEED_HIGH;
+ } else if (!strcmp(line, "1.5\n")) {
+ speed = USB_SPEED_LOW;
+ } else {
+ speed = USB_SPEED_FULL;
+ }
+
+ ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id,
+ product_id, product_name, speed);
+ if (ret) {
+ goto the_end;
+ }
+ }
+ }
+ the_end:
+ if (dir) {
+ closedir(dir);
+ }
+ return ret;
+}
+
+/*
+ * Determine how to access the host's USB devices and call the
+ * specific support function.
+ */
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+ Monitor *mon = cur_mon;
+ FILE *f = NULL;
+ DIR *dir = NULL;
+ int ret = 0;
+ const char *fs_type[] = {"unknown", "proc", "dev", "sys"};
+ char devpath[PATH_MAX];
+
+ /* only check the host once */
+ if (!usb_fs_type) {
+ dir = opendir(USBSYSBUS_PATH "/devices");
+ if (dir) {
+ /* devices found in /dev/bus/usb/ (yes - not a mistake!) */
+ strcpy(devpath, USBDEVBUS_PATH);
+ usb_fs_type = USB_FS_SYS;
+ closedir(dir);
+ DPRINTF(USBDBG_DEVOPENED, USBSYSBUS_PATH);
+ goto found_devices;
+ }
+ f = fopen(USBPROCBUS_PATH "/devices", "r");
+ if (f) {
+ /* devices found in /proc/bus/usb/ */
+ strcpy(devpath, USBPROCBUS_PATH);
+ usb_fs_type = USB_FS_PROC;
+ fclose(f);
+ DPRINTF(USBDBG_DEVOPENED, USBPROCBUS_PATH);
+ goto found_devices;
+ }
+ /* try additional methods if an access method hasn't been found yet */
+ f = fopen(USBDEVBUS_PATH "/devices", "r");
+ if (f) {
+ /* devices found in /dev/bus/usb/ */
+ strcpy(devpath, USBDEVBUS_PATH);
+ usb_fs_type = USB_FS_DEV;
+ fclose(f);
+ DPRINTF(USBDBG_DEVOPENED, USBDEVBUS_PATH);
+ goto found_devices;
+ }
+ found_devices:
+ if (!usb_fs_type) {
+ if (mon) {
+ monitor_printf(mon, "husb: unable to access USB devices\n");
+ }
+ return -ENOENT;
+ }
+
+ /* the module setting (used later for opening devices) */
+ usb_host_device_path = qemu_mallocz(strlen(devpath)+1);
+ strcpy(usb_host_device_path, devpath);
+ if (mon) {
+ monitor_printf(mon, "husb: using %s file-system with %s\n",
+ fs_type[usb_fs_type], usb_host_device_path);
+ }
+ }
+
+ switch (usb_fs_type) {
+ case USB_FS_PROC:
+ case USB_FS_DEV:
+ ret = usb_host_scan_dev(opaque, func);
+ break;
+ case USB_FS_SYS:
+ ret = usb_host_scan_sys(opaque, func);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static QEMUTimer *usb_auto_timer;
+
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
+ int class_id, int vendor_id, int product_id,
+ const char *product_name, int speed)
+{
+ struct USBAutoFilter *f;
+ struct USBHostDevice *s;
+
+ /* Ignore hubs */
+ if (class_id == 9)
+ return 0;
+
+ QTAILQ_FOREACH(s, &hostdevs, next) {
+ f = &s->match;
+
+ if (f->bus_num > 0 && f->bus_num != bus_num) {
+ continue;
+ }
+ if (f->addr > 0 && f->addr != addr) {
+ continue;
+ }
+
+ if (f->vendor_id > 0 && f->vendor_id != vendor_id) {
+ continue;
+ }
+
+ if (f->product_id > 0 && f->product_id != product_id) {
+ continue;
+ }
+ /* We got a match */
+
+ /* Already attached ? */
+ if (s->fd != -1) {
+ return 0;
+ }
+ DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
+
+ usb_host_open(s, bus_num, addr, devpath, product_name);
+ }
+
+ return 0;
+}
+
+static void usb_host_auto_check(void *unused)
+{
+ struct USBHostDevice *s;
+ int unconnected = 0;
+
+ usb_host_scan(NULL, usb_host_auto_scan);
+
+ QTAILQ_FOREACH(s, &hostdevs, next) {
+ if (s->fd == -1) {
+ unconnected++;
+ }
+ }
+
+ if (unconnected == 0) {
+ /* nothing to watch */
+ if (usb_auto_timer) {
+ qemu_del_timer(usb_auto_timer);
+ }
+ return;
+ }
+
+ if (!usb_auto_timer) {
+ usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_check, NULL);
+ if (!usb_auto_timer) {
+ return;
+ }
+ }
+ qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+}
+
+/*
+ * Autoconnect filter
+ * Format:
+ * auto:bus:dev[:vid:pid]
+ * auto:bus.dev[:vid:pid]
+ *
+ * bus - bus number (dec, * means any)
+ * dev - device number (dec, * means any)
+ * vid - vendor id (hex, * means any)
+ * pid - product id (hex, * means any)
+ *
+ * See 'lsusb' output.
+ */
+static int parse_filter(const char *spec, struct USBAutoFilter *f)
+{
+ enum { BUS, DEV, VID, PID, DONE };
+ const char *p = spec;
+ int i;
+
+ f->bus_num = 0;
+ f->addr = 0;
+ f->vendor_id = 0;
+ f->product_id = 0;
+
+ for (i = BUS; i < DONE; i++) {
+ p = strpbrk(p, ":.");
+ if (!p) {
+ break;
+ }
+ p++;
+
+ if (*p == '*') {
+ continue;
+ }
+ switch(i) {
+ case BUS: f->bus_num = strtol(p, NULL, 10); break;
+ case DEV: f->addr = strtol(p, NULL, 10); break;
+ case VID: f->vendor_id = strtol(p, NULL, 16); break;
+ case PID: f->product_id = strtol(p, NULL, 16); break;
+ }
+ }
+
+ if (i < DEV) {
+ fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+ int class;
+ const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+ { USB_CLASS_AUDIO, "Audio"},
+ { USB_CLASS_COMM, "Communication"},
+ { USB_CLASS_HID, "HID"},
+ { USB_CLASS_HUB, "Hub" },
+ { USB_CLASS_PHYSICAL, "Physical" },
+ { USB_CLASS_PRINTER, "Printer" },
+ { USB_CLASS_MASS_STORAGE, "Storage" },
+ { USB_CLASS_CDC_DATA, "Data" },
+ { USB_CLASS_APP_SPEC, "Application Specific" },
+ { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+ { USB_CLASS_STILL_IMAGE, "Still Image" },
+ { USB_CLASS_CSCID, "Smart Card" },
+ { USB_CLASS_CONTENT_SEC, "Content Security" },
+ { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+ const struct usb_class_info *p;
+ for(p = usb_class_info; p->class != -1; p++) {
+ if (p->class == class) {
+ break;
+ }
+ }
+ 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,
+ const char *product_name,
+ int speed)
+{
+ const char *class_str, *speed_str;
+
+ switch(speed) {
+ case USB_SPEED_LOW:
+ speed_str = "1.5";
+ break;
+ case USB_SPEED_FULL:
+ speed_str = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed_str = "480";
+ break;
+ default:
+ speed_str = "?";
+ break;
+ }
+
+ monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n",
+ bus_num, addr, speed_str);
+ class_str = usb_class_str(class_id);
+ if (class_str) {
+ monitor_printf(mon, " %s:", class_str);
+ } else {
+ monitor_printf(mon, " Class %02x:", class_id);
+ }
+ monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id);
+ if (product_name[0] != '\0') {
+ monitor_printf(mon, ", %s", product_name);
+ }
+ monitor_printf(mon, "\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+ int devpath, 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,
+ product_name, speed);
+ return 0;
+}
+
+static void dec2str(int val, char *str, size_t size)
+{
+ if (val == 0) {
+ snprintf(str, size, "*");
+ } else {
+ snprintf(str, size, "%d", val);
+ }
+}
+
+static void hex2str(int val, char *str, size_t size)
+{
+ if (val == 0) {
+ snprintf(str, size, "*");
+ } else {
+ snprintf(str, size, "%04x", val);
+ }
+}
+
+void usb_host_info(Monitor *mon)
+{
+ struct USBAutoFilter *f;
+ struct USBHostDevice *s;
+
+ usb_host_scan(mon, usb_host_info_device);
+
+ if (QTAILQ_EMPTY(&hostdevs)) {
+ return;
+ }
+
+ monitor_printf(mon, " Auto filters:\n");
+ QTAILQ_FOREACH(s, &hostdevs, next) {
+ char bus[10], addr[10], vid[10], pid[10];
+ f = &s->match;
+ dec2str(f->bus_num, bus, sizeof(bus));
+ 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);
+ }
+}
diff --git a/usb-stub.c b/usb-stub.c
new file mode 100644
index 0000000..9c3fcea
--- /dev/null
+++ b/usb-stub.c
@@ -0,0 +1,52 @@
+/*
+ * Stub host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ * Support for host device auto connect & disconnect
+ * Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ * to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "console.h"
+#include "hw/usb.h"
+#include "monitor.h"
+
+void usb_host_info(Monitor *mon)
+{
+ monitor_printf(mon, "USB host devices not supported\n");
+}
+
+/* XXX: modify configure to compile the right host driver */
+USBDevice *usb_host_device_open(const char *devname)
+{
+ return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+ return 0;
+}
diff --git a/version.rc b/version.rc
new file mode 100644
index 0000000..82e10ec
--- /dev/null
+++ b/version.rc
@@ -0,0 +1,28 @@
+#include <winver.h>
+#include "config-host.h"
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION CONFIG_FILEVERSION
+PRODUCTVERSION CONFIG_PRODUCTVERSION
+FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+FILEOS VOS_NT_WINDOWS32
+FILETYPE VFT_APP
+FILESUBTYPE VFT2_UNKNOWN
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "CompanyName", "http://www.qemu.org"
+ VALUE "FileDescription", "QEMU machine emulators and tools"
+ VALUE "FileVersion", QEMU_VERSION
+ VALUE "LegalCopyright", "Copyright various authors. Released under the GNU General Public License."
+ VALUE "LegalTrademarks", "QEMU is a trademark of Fabrice Bellard."
+ VALUE "ProductName", "QEMU"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 1252
+ }
+}
diff --git a/vgafont.h b/vgafont.h
new file mode 100644
index 0000000..3606dd7
--- /dev/null
+++ b/vgafont.h
@@ -0,0 +1,4611 @@
+static const uint8_t vgafont16[256 * 16] = {
+
+ /* 0 0x00 '^@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x81, /* 10000001 */
+ 0xa5, /* 10100101 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0x81, /* 10000001 */
+ 0x81, /* 10000001 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 2 0x02 '^B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xdb, /* 11011011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 3 0x03 '^C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0xe7, /* 11100111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 6 0x06 '^F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 7 0x07 '^G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^H' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xe7, /* 11100111 */
+ 0xc3, /* 11000011 */
+ 0xc3, /* 11000011 */
+ 0xe7, /* 11100111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 9 0x09 '^I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x42, /* 01000010 */
+ 0x42, /* 01000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^J' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xc3, /* 11000011 */
+ 0x99, /* 10011001 */
+ 0xbd, /* 10111101 */
+ 0xbd, /* 10111101 */
+ 0x99, /* 10011001 */
+ 0xc3, /* 11000011 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 11 0x0b '^K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x1a, /* 00011010 */
+ 0x32, /* 00110010 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 12 0x0c '^L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 13 0x0d '^M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x33, /* 00110011 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x70, /* 01110000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 14 0x0e '^N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x7f, /* 01111111 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x63, /* 01100011 */
+ 0x67, /* 01100111 */
+ 0xe7, /* 11100111 */
+ 0xe6, /* 11100110 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 15 0x0f '^O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xdb, /* 11011011 */
+ 0x3c, /* 00111100 */
+ 0xe7, /* 11100111 */
+ 0x3c, /* 00111100 */
+ 0xdb, /* 11011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 16 0x10 '^P' */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0xf0, /* 11110000 */
+ 0xf8, /* 11111000 */
+ 0xfe, /* 11111110 */
+ 0xf8, /* 11111000 */
+ 0xf0, /* 11110000 */
+ 0xe0, /* 11100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^Q' */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0e, /* 00001110 */
+ 0x1e, /* 00011110 */
+ 0x3e, /* 00111110 */
+ 0xfe, /* 11111110 */
+ 0x3e, /* 00111110 */
+ 0x1e, /* 00011110 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 19 0x13 '^S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7f, /* 01111111 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7b, /* 01111011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^U' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 22 0x16 '^V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 24 0x18 '^X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xfe, /* 11111110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^]' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00101000 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x28, /* 00101000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^^' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0x7c, /* 01111100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x24, /* 00100100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x86, /* 10000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0xff, /* 11111111 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x02, /* 00000010 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x80, /* 10000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x38, /* 00111000 */
+ 0x78, /* 01111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x3c, /* 00111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x1c, /* 00011100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xfc, /* 11111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 60 0x3c '<' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xde, /* 11011110 */
+ 0xdc, /* 11011100 */
+ 0xc0, /* 11000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xde, /* 11011110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x66, /* 01100110 */
+ 0x3a, /* 00111010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe6, /* 11100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xee, /* 11101110 */
+ 0xfe, /* 11111110 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xde, /* 11011110 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 82 0x52 'R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfc, /* 11111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x5a, /* 01011010 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0xee, /* 11101110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x7c, /* 01111100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x7c, /* 01111100 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc2, /* 11000010 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x80, /* 10000000 */
+ 0xc0, /* 11000000 */
+ 0xe0, /* 11100000 */
+ 0x70, /* 01110000 */
+ 0x38, /* 00111000 */
+ 0x1c, /* 00011100 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x02, /* 00000010 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 96 0x60 '`' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x36, /* 00110110 */
+ 0x32, /* 00110010 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0xcc, /* 11001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 104 0x68 'h' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x6c, /* 01101100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+
+ /* 107 0x6b 'k' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xe0, /* 11100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x78, /* 01111000 */
+ 0x78, /* 01111000 */
+ 0x6c, /* 01101100 */
+ 0x66, /* 01100110 */
+ 0xe6, /* 11100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0xfe, /* 11111110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x7c, /* 01111100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x1e, /* 00011110 */
+ 0x00, /* 00000000 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x66, /* 01100110 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x38, /* 00111000 */
+ 0x0c, /* 00001100 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0xfc, /* 11111100 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x36, /* 00110110 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xd6, /* 11010110 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x70, /* 01110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x0e, /* 00001110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 128 0x80 '€' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0xc2, /* 11000010 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc2, /* 11000010 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 129 0x81 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '‚' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 'ƒ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '„' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 'Â…' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '†' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '‡' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 136 0x88 'ˆ' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '‰' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a 'Š' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '‹' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c 'Œ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d 'Â' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e 'ÂŽ' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f 'Â' */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 'Â' */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x66, /* 01100110 */
+ 0x62, /* 01100010 */
+ 0x68, /* 01101000 */
+ 0x78, /* 01111000 */
+ 0x68, /* 01101000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '‘' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xec, /* 11101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x6e, /* 01101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 'Â’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3e, /* 00111110 */
+ 0x6c, /* 01101100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xfe, /* 11111110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xce, /* 11001110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '“' */
+ 0x00, /* 00000000 */
+ 0x10, /* 00010000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '”' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '•' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '–' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '—' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '˜' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7e, /* 01111110 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x78, /* 01111000 */
+ 0x00, /* 00000000 */
+
+ /* 153 0x99 '™' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a 'š' */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '›' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 156 0x9c 'œ' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x64, /* 01100100 */
+ 0x60, /* 01100000 */
+ 0xf0, /* 11110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xe6, /* 11100110 */
+ 0xfc, /* 11111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 158 0x9e 'ž' */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xf8, /* 11111000 */
+ 0xc4, /* 11000100 */
+ 0xcc, /* 11001100 */
+ 0xde, /* 11011110 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 159 0x9f 'Ÿ' */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 ' ' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0x0c, /* 00001100 */
+ 0x7c, /* 01111100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '¡' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '¢' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '£' */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x00, /* 00000000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '¤' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xdc, /* 11011100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 'Â¥' */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0xc6, /* 11000110 */
+ 0xe6, /* 11100110 */
+ 0xf6, /* 11110110 */
+ 0xfe, /* 11111110 */
+ 0xde, /* 11011110 */
+ 0xce, /* 11001110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '¦' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '§' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '¨' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x7c, /* 01111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '©' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa 'ª' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '«' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xdc, /* 11011100 */
+ 0x86, /* 10000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x3e, /* 00111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 172 0xac '¬' */
+ 0x00, /* 00000000 */
+ 0x60, /* 01100000 */
+ 0xe0, /* 11100000 */
+ 0x62, /* 01100010 */
+ 0x66, /* 01100110 */
+ 0x6c, /* 01101100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x66, /* 01100110 */
+ 0xce, /* 11001110 */
+ 0x9a, /* 10011010 */
+ 0x3f, /* 00111111 */
+ 0x06, /* 00000110 */
+ 0x06, /* 00000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 173 0xad '­' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '®' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '¯' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xd8, /* 11011000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x6c, /* 01101100 */
+ 0xd8, /* 11011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '°' */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+ 0x11, /* 00010001 */
+ 0x44, /* 01000100 */
+
+ /* 177 0xb1 '±' */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+ 0x55, /* 01010101 */
+ 0xaa, /* 10101010 */
+
+ /* 178 0xb2 '²' */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+ 0xdd, /* 11011101 */
+ 0x77, /* 01110111 */
+
+ /* 179 0xb3 '³' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 180 0xb4 '´' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 181 0xb5 'µ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 182 0xb6 '¶' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 183 0xb7 '·' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 184 0xb8 '¸' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 185 0xb9 '¹' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 186 0xba 'º' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 187 0xbb '»' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x06, /* 00000110 */
+ 0xf6, /* 11110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 188 0xbc '¼' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf6, /* 11110110 */
+ 0x06, /* 00000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '½' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '¾' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '¿' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xf8, /* 11111000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 192 0xc0 'À' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 'Â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 195 0xc3 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 196 0xc4 'Ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 'Ã…' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 198 0xc6 'Æ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 199 0xc7 'Ç' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 200 0xc8 'È' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 'É' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 202 0xca 'Ê' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb 'Ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 204 0xcc 'Ì' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x37, /* 00110111 */
+ 0x30, /* 00110000 */
+ 0x37, /* 00110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 205 0xcd 'Ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce 'ÃŽ' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xf7, /* 11110111 */
+ 0x00, /* 00000000 */
+ 0xf7, /* 11110111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 207 0xcf 'Ã' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 'Ã' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 'Ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 210 0xd2 'Ã’' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 211 0xd3 'Ó' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x3f, /* 00111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 'Ô' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 'Õ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 214 0xd6 'Ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3f, /* 00111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 215 0xd7 '×' */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0xff, /* 11111111 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+
+ /* 216 0xd8 'Ø' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0xff, /* 11111111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 217 0xd9 'Ù' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xf8, /* 11111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda 'Ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1f, /* 00011111 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 219 0xdb 'Û' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 220 0xdc 'Ü' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+
+ /* 221 0xdd 'Ã' */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+ 0xf0, /* 11110000 */
+
+ /* 222 0xde 'Þ' */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+ 0x0f, /* 00001111 */
+
+ /* 223 0xdf 'ß' */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0xff, /* 11111111 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 'à' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xdc, /* 11011100 */
+ 0x76, /* 01110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 'á' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 01111000 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xcc, /* 11001100 */
+ 0xd8, /* 11011000 */
+ 0xcc, /* 11001100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xcc, /* 11001100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 'â' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 'ã' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 'ä' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 'Ã¥' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 'æ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+
+ /* 231 0xe7 'ç' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 'è' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 233 0xe9 'é' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xfe, /* 11111110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea 'ê' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0xee, /* 11101110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb 'ë' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1e, /* 00011110 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x3e, /* 00111110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x66, /* 01100110 */
+ 0x3c, /* 00111100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec 'ì' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed 'í' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x03, /* 00000011 */
+ 0x06, /* 00000110 */
+ 0x7e, /* 01111110 */
+ 0xdb, /* 11011011 */
+ 0xdb, /* 11011011 */
+ 0xf3, /* 11110011 */
+ 0x7e, /* 01111110 */
+ 0x60, /* 01100000 */
+ 0xc0, /* 11000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 238 0xee 'î' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 00011100 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x7c, /* 01111100 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef 'ï' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 01111100 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0xc6, /* 11000110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 'ð' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0xfe, /* 11111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 'ñ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x7e, /* 01111110 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 'ò' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x06, /* 00000110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 'ó' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x30, /* 00110000 */
+ 0x60, /* 01100000 */
+ 0x30, /* 00110000 */
+ 0x18, /* 00011000 */
+ 0x0c, /* 00001100 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 'ô' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0e, /* 00001110 */
+ 0x1b, /* 00011011 */
+ 0x1b, /* 00011011 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+
+ /* 245 0xf5 'õ' */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0xd8, /* 11011000 */
+ 0x70, /* 01110000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 246 0xf6 'ö' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '÷' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x76, /* 01110110 */
+ 0xdc, /* 11011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 'ø' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00111000 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x38, /* 00111000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 'ù' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa 'ú' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 00011000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb 'û' */
+ 0x00, /* 00000000 */
+ 0x0f, /* 00001111 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0x0c, /* 00001100 */
+ 0xec, /* 11101100 */
+ 0x6c, /* 01101100 */
+ 0x6c, /* 01101100 */
+ 0x3c, /* 00111100 */
+ 0x1c, /* 00011100 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 252 0xfc 'ü' */
+ 0x00, /* 00000000 */
+ 0x6c, /* 01101100 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x36, /* 00110110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd 'ý' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00111100 */
+ 0x66, /* 01100110 */
+ 0x0c, /* 00001100 */
+ 0x18, /* 00011000 */
+ 0x32, /* 00110010 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe 'þ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x7e, /* 01111110 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff 'ÿ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+};
diff --git a/vl.c b/vl.c
new file mode 100644
index 0000000..6da06bb
--- /dev/null
+++ b/vl.c
@@ -0,0 +1,3208 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <zlib.h>
+
+/* Needed early for CONFIG_BSD etc. */
+#include "config-host.h"
+
+#ifndef _WIN32
+#include <libgen.h>
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <sys/select.h>
+#ifdef CONFIG_SIMPLE_TRACE
+#include "trace.h"
+#endif
+
+#ifdef CONFIG_BSD
+#include <sys/stat.h>
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#include <libutil.h>
+#include <sys/sysctl.h>
+#else
+#include <util.h>
+#endif
+#else
+#ifdef __linux__
+#include <pty.h>
+#include <malloc.h>
+
+#include <linux/ppdev.h>
+#include <linux/parport.h>
+#endif
+#ifdef __sun__
+#include <sys/stat.h>
+#include <sys/ethernet.h>
+#include <sys/sockio.h>
+#include <netinet/arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h> // must come after ip.h
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <syslog.h>
+#include <stropts.h>
+#endif
+#endif
+#endif
+
+#if defined(__OpenBSD__)
+#include <util.h>
+#endif
+
+#if defined(CONFIG_VDE)
+#include <libvdeplug.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifdef CONFIG_SDL
+#if defined(__APPLE__) || defined(main)
+#include <SDL.h>
+int qemu_main(int argc, char **argv, char **envp);
+int main(int argc, char **argv)
+{
+ return qemu_main(argc, argv, NULL);
+}
+#undef main
+#define main qemu_main
+#endif
+#endif /* CONFIG_SDL */
+
+#ifdef CONFIG_COCOA
+#undef main
+#define main qemu_main
+#endif /* CONFIG_COCOA */
+
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/usb.h"
+#include "hw/pcmcia.h"
+#include "hw/pc.h"
+#include "hw/isa.h"
+#include "hw/baum.h"
+#include "hw/bt.h"
+#include "hw/watchdog.h"
+#include "hw/smbios.h"
+#include "hw/xen.h"
+#include "hw/qdev.h"
+#include "hw/loader.h"
+#include "bt-host.h"
+#include "net.h"
+#include "net/slirp.h"
+#include "monitor.h"
+#include "console.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "cache-utils.h"
+#include "block.h"
+#include "blockdev.h"
+#include "block-migration.h"
+#include "dma.h"
+#include "audio/audio.h"
+#include "migration.h"
+#include "kvm.h"
+#include "qemu-option.h"
+#include "qemu-config.h"
+#include "qemu-objects.h"
+#include "qemu-options.h"
+#include "hw/device-assignment.h"
+#ifdef CONFIG_VIRTFS
+#include "fsdev/qemu-fsdev.h"
+#endif
+
+#include "disas.h"
+
+#include "qemu_socket.h"
+
+#include "slirp/libslirp.h"
+
+#include "trace.h"
+#include "qemu-queue.h"
+#include "cpus.h"
+#include "arch_init.h"
+
+#include "ui/qemu-spice.h"
+
+//#define DEBUG_NET
+//#define DEBUG_SLIRP
+
+#define DEFAULT_RAM_SIZE 128
+
+#define MAX_VIRTIO_CONSOLES 1
+
+static const char *data_dir;
+const char *bios_name = NULL;
+enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
+DisplayType display_type = DT_DEFAULT;
+int display_remote = 0;
+const char* keyboard_layout = NULL;
+ram_addr_t ram_size;
+const char *mem_path = NULL;
+#ifdef MAP_POPULATE
+int mem_prealloc = 0; /* force preallocation of physical target memory */
+#endif
+int nb_nics;
+NICInfo nd_table[MAX_NICS];
+int vm_running;
+int autostart;
+int incoming_expected; /* Started with -incoming and waiting for incoming */
+static int rtc_utc = 1;
+static int rtc_date_offset = -1; /* -1 means no change */
+QEMUClock *rtc_clock;
+int vga_interface_type = VGA_NONE;
+static int full_screen = 0;
+#ifdef CONFIG_SDL
+static int no_frame = 0;
+#endif
+int no_quit = 0;
+CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
+int win2k_install_hack = 0;
+int rtc_td_hack = 0;
+int usb_enabled = 0;
+int singlestep = 0;
+int smp_cpus = 1;
+int max_cpus = 0;
+int smp_cores = 1;
+int smp_threads = 1;
+const char *vnc_display;
+int acpi_enabled = 1;
+int no_hpet = 0;
+int fd_bootchk = 1;
+int no_reboot = 0;
+int no_shutdown = 0;
+int cursor_hide = 1;
+int graphic_rotate = 0;
+uint8_t irq0override = 1;
+const char *watchdog;
+QEMUOptionRom option_rom[MAX_OPTION_ROMS];
+int nb_option_roms;
+int semihosting_enabled = 0;
+int time_drift_fix = 0;
+unsigned int kvm_shadow_memory = 0;
+int old_param = 0;
+const char *qemu_name;
+int alt_grab = 0;
+int ctrl_grab = 0;
+unsigned int nb_prom_envs = 0;
+const char *prom_envs[MAX_PROM_ENVS];
+const char *nvram = NULL;
+int boot_menu;
+
+typedef struct FWBootEntry FWBootEntry;
+
+struct FWBootEntry {
+ QTAILQ_ENTRY(FWBootEntry) link;
+ int32_t bootindex;
+ DeviceState *dev;
+ char *suffix;
+};
+
+QTAILQ_HEAD(, FWBootEntry) fw_boot_order = QTAILQ_HEAD_INITIALIZER(fw_boot_order);
+
+int nb_numa_nodes;
+uint64_t node_mem[MAX_NODES];
+uint64_t node_cpumask[MAX_NODES];
+
+static QEMUTimer *nographic_timer;
+
+uint8_t qemu_uuid[16];
+
+static QEMUBootSetHandler *boot_set_handler;
+static void *boot_set_opaque;
+
+static NotifierList exit_notifiers =
+ NOTIFIER_LIST_INITIALIZER(exit_notifiers);
+
+static NotifierList machine_init_done_notifiers =
+ NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers);
+
+int kvm_allowed = -1;
+uint32_t xen_domid;
+enum xen_mode xen_mode = XEN_EMULATE;
+
+static int default_serial = 1;
+static int default_parallel = 1;
+static int default_virtcon = 1;
+static int default_monitor = 1;
+static int default_vga = 1;
+static int default_floppy = 1;
+static int default_cdrom = 1;
+static int default_sdcard = 1;
+
+static struct {
+ const char *driver;
+ int *flag;
+} default_list[] = {
+ { .driver = "isa-serial", .flag = &default_serial },
+ { .driver = "isa-parallel", .flag = &default_parallel },
+ { .driver = "isa-fdc", .flag = &default_floppy },
+ { .driver = "ide-drive", .flag = &default_cdrom },
+ { .driver = "virtio-serial-pci", .flag = &default_virtcon },
+ { .driver = "virtio-serial-s390", .flag = &default_virtcon },
+ { .driver = "virtio-serial", .flag = &default_virtcon },
+ { .driver = "VGA", .flag = &default_vga },
+ { .driver = "cirrus-vga", .flag = &default_vga },
+ { .driver = "vmware-svga", .flag = &default_vga },
+};
+
+static int default_driver_check(QemuOpts *opts, void *opaque)
+{
+ const char *driver = qemu_opt_get(opts, "driver");
+ int i;
+
+ if (!driver)
+ return 0;
+ for (i = 0; i < ARRAY_SIZE(default_list); i++) {
+ if (strcmp(default_list[i].driver, driver) != 0)
+ continue;
+ *(default_list[i].flag) = 0;
+ }
+ return 0;
+}
+
+/***********************************************************/
+/* real time host monotonic timer */
+
+/***********************************************************/
+/* host time/date access */
+void qemu_get_timedate(struct tm *tm, int offset)
+{
+ time_t ti;
+ struct tm *ret;
+
+ time(&ti);
+ ti += offset;
+ if (rtc_date_offset == -1) {
+ if (rtc_utc)
+ ret = gmtime(&ti);
+ else
+ ret = localtime(&ti);
+ } else {
+ ti -= rtc_date_offset;
+ ret = gmtime(&ti);
+ }
+
+ memcpy(tm, ret, sizeof(struct tm));
+}
+
+int qemu_timedate_diff(struct tm *tm)
+{
+ time_t seconds;
+
+ if (rtc_date_offset == -1)
+ if (rtc_utc)
+ seconds = mktimegm(tm);
+ else
+ seconds = mktime(tm);
+ else
+ seconds = mktimegm(tm) + rtc_date_offset;
+
+ return seconds - time(NULL);
+}
+
+void rtc_change_mon_event(struct tm *tm)
+{
+ QObject *data;
+
+ data = qobject_from_jsonf("{ 'offset': %d }", qemu_timedate_diff(tm));
+ monitor_protocol_event(QEVENT_RTC_CHANGE, data);
+ qobject_decref(data);
+}
+
+static void configure_rtc_date_offset(const char *startdate, int legacy)
+{
+ time_t rtc_start_date;
+ struct tm tm;
+
+ if (!strcmp(startdate, "now") && legacy) {
+ rtc_date_offset = -1;
+ } else {
+ if (sscanf(startdate, "%d-%d-%dT%d:%d:%d",
+ &tm.tm_year,
+ &tm.tm_mon,
+ &tm.tm_mday,
+ &tm.tm_hour,
+ &tm.tm_min,
+ &tm.tm_sec) == 6) {
+ /* OK */
+ } else if (sscanf(startdate, "%d-%d-%d",
+ &tm.tm_year,
+ &tm.tm_mon,
+ &tm.tm_mday) == 3) {
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ } else {
+ goto date_fail;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+ rtc_start_date = mktimegm(&tm);
+ if (rtc_start_date == -1) {
+ date_fail:
+ fprintf(stderr, "Invalid date format. Valid formats are:\n"
+ "'2006-06-17T16:01:21' or '2006-06-17'\n");
+ exit(1);
+ }
+ rtc_date_offset = time(NULL) - rtc_start_date;
+ }
+}
+
+static void configure_rtc(QemuOpts *opts)
+{
+ const char *value;
+
+ value = qemu_opt_get(opts, "base");
+ if (value) {
+ if (!strcmp(value, "utc")) {
+ rtc_utc = 1;
+ } else if (!strcmp(value, "localtime")) {
+ rtc_utc = 0;
+ } else {
+ configure_rtc_date_offset(value, 0);
+ }
+ }
+ value = qemu_opt_get(opts, "clock");
+ if (value) {
+ if (!strcmp(value, "host")) {
+ rtc_clock = host_clock;
+ } else if (!strcmp(value, "vm")) {
+ rtc_clock = vm_clock;
+ } else {
+ fprintf(stderr, "qemu: invalid option value '%s'\n", value);
+ exit(1);
+ }
+ }
+ value = qemu_opt_get(opts, "driftfix");
+ if (value) {
+ if (!strcmp(value, "slew")) {
+ rtc_td_hack = 1;
+ } else if (!strcmp(value, "none")) {
+ rtc_td_hack = 0;
+ } else {
+ fprintf(stderr, "qemu: invalid option value '%s'\n", value);
+ exit(1);
+ }
+ }
+}
+
+/***********************************************************/
+/* Bluetooth support */
+static int nb_hcis;
+static int cur_hci;
+static struct HCIInfo *hci_table[MAX_NICS];
+
+static struct bt_vlan_s {
+ struct bt_scatternet_s net;
+ int id;
+ struct bt_vlan_s *next;
+} *first_bt_vlan;
+
+/* find or alloc a new bluetooth "VLAN" */
+static struct bt_scatternet_s *qemu_find_bt_vlan(int id)
+{
+ struct bt_vlan_s **pvlan, *vlan;
+ for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) {
+ if (vlan->id == id)
+ return &vlan->net;
+ }
+ vlan = qemu_mallocz(sizeof(struct bt_vlan_s));
+ vlan->id = id;
+ pvlan = &first_bt_vlan;
+ while (*pvlan != NULL)
+ pvlan = &(*pvlan)->next;
+ *pvlan = vlan;
+ return &vlan->net;
+}
+
+static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len)
+{
+}
+
+static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr)
+{
+ return -ENOTSUP;
+}
+
+static struct HCIInfo null_hci = {
+ .cmd_send = null_hci_send,
+ .sco_send = null_hci_send,
+ .acl_send = null_hci_send,
+ .bdaddr_set = null_hci_addr_set,
+};
+
+struct HCIInfo *qemu_next_hci(void)
+{
+ if (cur_hci == nb_hcis)
+ return &null_hci;
+
+ return hci_table[cur_hci++];
+}
+
+static struct HCIInfo *hci_init(const char *str)
+{
+ char *endp;
+ struct bt_scatternet_s *vlan = 0;
+
+ if (!strcmp(str, "null"))
+ /* null */
+ return &null_hci;
+ else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':'))
+ /* host[:hciN] */
+ return bt_host_hci(str[4] ? str + 5 : "hci0");
+ else if (!strncmp(str, "hci", 3)) {
+ /* hci[,vlan=n] */
+ if (str[3]) {
+ if (!strncmp(str + 3, ",vlan=", 6)) {
+ vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0));
+ if (*endp)
+ vlan = 0;
+ }
+ } else
+ vlan = qemu_find_bt_vlan(0);
+ if (vlan)
+ return bt_new_hci(vlan);
+ }
+
+ fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str);
+
+ return 0;
+}
+
+static int bt_hci_parse(const char *str)
+{
+ struct HCIInfo *hci;
+ bdaddr_t bdaddr;
+
+ if (nb_hcis >= MAX_NICS) {
+ fprintf(stderr, "qemu: Too many bluetooth HCIs (max %i).\n", MAX_NICS);
+ return -1;
+ }
+
+ hci = hci_init(str);
+ if (!hci)
+ return -1;
+
+ bdaddr.b[0] = 0x52;
+ bdaddr.b[1] = 0x54;
+ bdaddr.b[2] = 0x00;
+ bdaddr.b[3] = 0x12;
+ bdaddr.b[4] = 0x34;
+ bdaddr.b[5] = 0x56 + nb_hcis;
+ hci->bdaddr_set(hci, bdaddr.b);
+
+ hci_table[nb_hcis++] = hci;
+
+ return 0;
+}
+
+static void bt_vhci_add(int vlan_id)
+{
+ struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id);
+
+ if (!vlan->slave)
+ fprintf(stderr, "qemu: warning: adding a VHCI to "
+ "an empty scatternet %i\n", vlan_id);
+
+ bt_vhci_init(bt_new_hci(vlan));
+}
+
+static struct bt_device_s *bt_device_add(const char *opt)
+{
+ struct bt_scatternet_s *vlan;
+ int vlan_id = 0;
+ char *endp = strstr(opt, ",vlan=");
+ int len = (endp ? endp - opt : strlen(opt)) + 1;
+ char devname[10];
+
+ pstrcpy(devname, MIN(sizeof(devname), len), opt);
+
+ if (endp) {
+ vlan_id = strtol(endp + 6, &endp, 0);
+ if (*endp) {
+ fprintf(stderr, "qemu: unrecognised bluetooth vlan Id\n");
+ return 0;
+ }
+ }
+
+ vlan = qemu_find_bt_vlan(vlan_id);
+
+ if (!vlan->slave)
+ fprintf(stderr, "qemu: warning: adding a slave device to "
+ "an empty scatternet %i\n", vlan_id);
+
+ if (!strcmp(devname, "keyboard"))
+ return bt_keyboard_init(vlan);
+
+ fprintf(stderr, "qemu: unsupported bluetooth device `%s'\n", devname);
+ return 0;
+}
+
+static int bt_parse(const char *opt)
+{
+ const char *endp, *p;
+ int vlan;
+
+ if (strstart(opt, "hci", &endp)) {
+ if (!*endp || *endp == ',') {
+ if (*endp)
+ if (!strstart(endp, ",vlan=", 0))
+ opt = endp + 1;
+
+ return bt_hci_parse(opt);
+ }
+ } else if (strstart(opt, "vhci", &endp)) {
+ if (!*endp || *endp == ',') {
+ if (*endp) {
+ if (strstart(endp, ",vlan=", &p)) {
+ vlan = strtol(p, (char **) &endp, 0);
+ if (*endp) {
+ fprintf(stderr, "qemu: bad scatternet '%s'\n", p);
+ return 1;
+ }
+ } else {
+ fprintf(stderr, "qemu: bad parameter '%s'\n", endp + 1);
+ return 1;
+ }
+ } else
+ vlan = 0;
+
+ bt_vhci_add(vlan);
+ return 0;
+ }
+ } else if (strstart(opt, "device:", &endp))
+ return !bt_device_add(endp);
+
+ fprintf(stderr, "qemu: bad bluetooth parameter '%s'\n", opt);
+ return 1;
+}
+
+/***********************************************************/
+/* QEMU Block devices */
+
+#define HD_OPTS "media=disk"
+#define CDROM_OPTS "media=cdrom"
+#define FD_OPTS ""
+#define PFLASH_OPTS ""
+#define MTD_OPTS ""
+#define SD_OPTS ""
+
+static int drive_init_func(QemuOpts *opts, void *opaque)
+{
+ int *use_scsi = opaque;
+
+ return drive_init(opts, *use_scsi) == NULL;
+}
+
+static int drive_enable_snapshot(QemuOpts *opts, void *opaque)
+{
+ if (NULL == qemu_opt_get(opts, "snapshot")) {
+ qemu_opt_set(opts, "snapshot", "on");
+ }
+ return 0;
+}
+
+static void default_drive(int enable, int snapshot, int use_scsi,
+ BlockInterfaceType type, int index,
+ const char *optstr)
+{
+ QemuOpts *opts;
+
+ if (type == IF_DEFAULT) {
+ type = use_scsi ? IF_SCSI : IF_IDE;
+ }
+
+ if (!enable || drive_get_by_index(type, index)) {
+ return;
+ }
+
+ opts = drive_add(type, index, NULL, optstr);
+ if (snapshot) {
+ drive_enable_snapshot(opts, NULL);
+ }
+ if (!drive_init(opts, use_scsi)) {
+ exit(1);
+ }
+}
+
+void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
+{
+ boot_set_handler = func;
+ boot_set_opaque = opaque;
+}
+
+int qemu_boot_set(const char *boot_devices)
+{
+ if (!boot_set_handler) {
+ return -EINVAL;
+ }
+ return boot_set_handler(boot_set_opaque, boot_devices);
+}
+
+static void validate_bootdevices(char *devices)
+{
+ /* We just do some generic consistency checks */
+ const char *p;
+ int bitmap = 0;
+
+ for (p = devices; *p != '\0'; p++) {
+ /* Allowed boot devices are:
+ * a-b: floppy disk drives
+ * c-f: IDE disk drives
+ * g-m: machine implementation dependant drives
+ * n-p: network devices
+ * It's up to each machine implementation to check if the given boot
+ * devices match the actual hardware implementation and firmware
+ * features.
+ */
+ if (*p < 'a' || *p > 'p') {
+ fprintf(stderr, "Invalid boot device '%c'\n", *p);
+ exit(1);
+ }
+ if (bitmap & (1 << (*p - 'a'))) {
+ fprintf(stderr, "Boot device '%c' was given twice\n", *p);
+ exit(1);
+ }
+ bitmap |= 1 << (*p - 'a');
+ }
+}
+
+static void restore_boot_devices(void *opaque)
+{
+ char *standard_boot_devices = opaque;
+ static int first = 1;
+
+ /* Restore boot order and remove ourselves after the first boot */
+ if (first) {
+ first = 0;
+ return;
+ }
+
+ qemu_boot_set(standard_boot_devices);
+
+ qemu_unregister_reset(restore_boot_devices, standard_boot_devices);
+ qemu_free(standard_boot_devices);
+}
+
+void add_boot_device_path(int32_t bootindex, DeviceState *dev,
+ const char *suffix)
+{
+ FWBootEntry *node, *i;
+
+ if (bootindex < 0) {
+ return;
+ }
+
+ assert(dev != NULL || suffix != NULL);
+
+ node = qemu_mallocz(sizeof(FWBootEntry));
+ node->bootindex = bootindex;
+ node->suffix = suffix ? qemu_strdup(suffix) : NULL;
+ node->dev = dev;
+
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ if (i->bootindex == bootindex) {
+ fprintf(stderr, "Two devices with same boot index %d\n", bootindex);
+ exit(1);
+ } else if (i->bootindex < bootindex) {
+ continue;
+ }
+ QTAILQ_INSERT_BEFORE(i, node, link);
+ return;
+ }
+ QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
+}
+
+/*
+ * This function returns null terminated string that consist of new line
+ * separated device pathes.
+ *
+ * memory pointed by "size" is assigned total length of the array in bytes
+ *
+ */
+char *get_boot_devices_list(uint32_t *size)
+{
+ FWBootEntry *i;
+ uint32_t total = 0;
+ char *list = NULL;
+
+ QTAILQ_FOREACH(i, &fw_boot_order, link) {
+ char *devpath = NULL, *bootpath;
+ int len;
+
+ if (i->dev) {
+ devpath = qdev_get_fw_dev_path(i->dev);
+ assert(devpath);
+ }
+
+ if (i->suffix && devpath) {
+ size_t bootpathlen = strlen(devpath) + strlen(i->suffix) + 1;
+
+ bootpath = qemu_malloc(bootpathlen);
+ snprintf(bootpath, bootpathlen, "%s%s", devpath, i->suffix);
+ qemu_free(devpath);
+ } else if (devpath) {
+ bootpath = devpath;
+ } else {
+ bootpath = qemu_strdup(i->suffix);
+ assert(bootpath);
+ }
+
+ if (total) {
+ list[total-1] = '\n';
+ }
+ len = strlen(bootpath) + 1;
+ list = qemu_realloc(list, total + len);
+ memcpy(&list[total], bootpath, len);
+ total += len;
+ qemu_free(bootpath);
+ }
+
+ *size = total;
+
+ return list;
+}
+
+static void numa_add(const char *optarg)
+{
+ char option[128];
+ char *endptr;
+ unsigned long long value, endvalue;
+ int nodenr;
+
+ optarg = get_opt_name(option, 128, optarg, ',') + 1;
+ if (!strcmp(option, "node")) {
+ if (get_param_value(option, 128, "nodeid", optarg) == 0) {
+ nodenr = nb_numa_nodes;
+ } else {
+ nodenr = strtoull(option, NULL, 10);
+ }
+
+ if (get_param_value(option, 128, "mem", optarg) == 0) {
+ node_mem[nodenr] = 0;
+ } else {
+ int64_t sval;
+ sval = strtosz(option, NULL);
+ if (sval < 0) {
+ fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg);
+ exit(1);
+ }
+ node_mem[nodenr] = sval;
+ }
+ if (get_param_value(option, 128, "cpus", optarg) == 0) {
+ node_cpumask[nodenr] = 0;
+ } else {
+ value = strtoull(option, &endptr, 10);
+ if (value >= 64) {
+ value = 63;
+ fprintf(stderr, "only 64 CPUs in NUMA mode supported.\n");
+ } else {
+ if (*endptr == '-') {
+ endvalue = strtoull(endptr+1, &endptr, 10);
+ if (endvalue >= 63) {
+ endvalue = 62;
+ fprintf(stderr,
+ "only 63 CPUs in NUMA mode supported.\n");
+ }
+ value = (2ULL << endvalue) - (1ULL << value);
+ } else {
+ value = 1ULL << value;
+ }
+ }
+ node_cpumask[nodenr] = value;
+ }
+ nb_numa_nodes++;
+ }
+ return;
+}
+
+static void smp_parse(const char *optarg)
+{
+ int smp, sockets = 0, threads = 0, cores = 0;
+ char *endptr;
+ char option[128];
+
+ smp = strtoul(optarg, &endptr, 10);
+ if (endptr != optarg) {
+ if (*endptr == ',') {
+ endptr++;
+ }
+ }
+ if (get_param_value(option, 128, "sockets", endptr) != 0)
+ sockets = strtoull(option, NULL, 10);
+ if (get_param_value(option, 128, "cores", endptr) != 0)
+ cores = strtoull(option, NULL, 10);
+ if (get_param_value(option, 128, "threads", endptr) != 0)
+ threads = strtoull(option, NULL, 10);
+ if (get_param_value(option, 128, "maxcpus", endptr) != 0)
+ max_cpus = strtoull(option, NULL, 10);
+
+ /* compute missing values, prefer sockets over cores over threads */
+ if (smp == 0 || sockets == 0) {
+ sockets = sockets > 0 ? sockets : 1;
+ cores = cores > 0 ? cores : 1;
+ threads = threads > 0 ? threads : 1;
+ if (smp == 0) {
+ smp = cores * threads * sockets;
+ }
+ } else {
+ if (cores == 0) {
+ threads = threads > 0 ? threads : 1;
+ cores = smp / (sockets * threads);
+ } else {
+ threads = smp / (cores * sockets);
+ }
+ }
+ smp_cpus = smp;
+ smp_cores = cores > 0 ? cores : 1;
+ smp_threads = threads > 0 ? threads : 1;
+ if (max_cpus == 0)
+ max_cpus = smp_cpus;
+}
+
+/***********************************************************/
+/* USB devices */
+
+static int usb_device_add(const char *devname)
+{
+ const char *p;
+ USBDevice *dev = NULL;
+
+ if (!usb_enabled)
+ return -1;
+
+ /* drivers with .usbdevice_name entry in USBDeviceInfo */
+ dev = usbdevice_create(devname);
+ if (dev)
+ goto done;
+
+ /* the other ones */
+ if (strstart(devname, "host:", &p)) {
+ dev = usb_host_device_open(p);
+ } else if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) {
+ dev = usb_bt_init(devname[2] ? hci_init(p) :
+ bt_new_hci(qemu_find_bt_vlan(0)));
+ } else {
+ return -1;
+ }
+ if (!dev)
+ return -1;
+
+done:
+ return 0;
+}
+
+static int usb_device_del(const char *devname)
+{
+ int bus_num, addr;
+ const char *p;
+
+ if (strstart(devname, "host:", &p))
+ return usb_host_device_close(p);
+
+ if (!usb_enabled)
+ return -1;
+
+ p = strchr(devname, '.');
+ if (!p)
+ return -1;
+ bus_num = strtoul(devname, NULL, 0);
+ addr = strtoul(p + 1, NULL, 0);
+
+ return usb_device_delete_addr(bus_num, addr);
+}
+
+static int usb_parse(const char *cmdline)
+{
+ int r;
+ r = usb_device_add(cmdline);
+ if (r < 0) {
+ fprintf(stderr, "qemu: could not add USB device '%s'\n", cmdline);
+ }
+ return r;
+}
+
+void do_usb_add(Monitor *mon, const QDict *qdict)
+{
+ const char *devname = qdict_get_str(qdict, "devname");
+ if (usb_device_add(devname) < 0) {
+ error_report("could not add USB device '%s'", devname);
+ }
+}
+
+void do_usb_del(Monitor *mon, const QDict *qdict)
+{
+ const char *devname = qdict_get_str(qdict, "devname");
+ if (usb_device_del(devname) < 0) {
+ error_report("could not delete USB device '%s'", devname);
+ }
+}
+
+/***********************************************************/
+/* PCMCIA/Cardbus */
+
+static struct pcmcia_socket_entry_s {
+ PCMCIASocket *socket;
+ struct pcmcia_socket_entry_s *next;
+} *pcmcia_sockets = 0;
+
+void pcmcia_socket_register(PCMCIASocket *socket)
+{
+ struct pcmcia_socket_entry_s *entry;
+
+ entry = qemu_malloc(sizeof(struct pcmcia_socket_entry_s));
+ entry->socket = socket;
+ entry->next = pcmcia_sockets;
+ pcmcia_sockets = entry;
+}
+
+void pcmcia_socket_unregister(PCMCIASocket *socket)
+{
+ struct pcmcia_socket_entry_s *entry, **ptr;
+
+ ptr = &pcmcia_sockets;
+ for (entry = *ptr; entry; ptr = &entry->next, entry = *ptr)
+ if (entry->socket == socket) {
+ *ptr = entry->next;
+ qemu_free(entry);
+ }
+}
+
+void pcmcia_info(Monitor *mon)
+{
+ struct pcmcia_socket_entry_s *iter;
+
+ if (!pcmcia_sockets)
+ monitor_printf(mon, "No PCMCIA sockets\n");
+
+ for (iter = pcmcia_sockets; iter; iter = iter->next)
+ monitor_printf(mon, "%s: %s\n", iter->socket->slot_string,
+ iter->socket->attached ? iter->socket->card_string :
+ "Empty");
+}
+
+/***********************************************************/
+/* I/O handling */
+
+typedef struct IOHandlerRecord {
+ int fd;
+ IOCanReadHandler *fd_read_poll;
+ IOHandler *fd_read;
+ IOHandler *fd_write;
+ int deleted;
+ void *opaque;
+ /* temporary data */
+ struct pollfd *ufd;
+ QLIST_ENTRY(IOHandlerRecord) next;
+} IOHandlerRecord;
+
+static QLIST_HEAD(, IOHandlerRecord) io_handlers =
+ QLIST_HEAD_INITIALIZER(io_handlers);
+
+
+/* XXX: fd_read_poll should be suppressed, but an API change is
+ necessary in the character devices to suppress fd_can_read(). */
+int qemu_set_fd_handler2(int fd,
+ IOCanReadHandler *fd_read_poll,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque)
+{
+ IOHandlerRecord *ioh;
+
+ if (!fd_read && !fd_write) {
+ QLIST_FOREACH(ioh, &io_handlers, next) {
+ if (ioh->fd == fd) {
+ ioh->deleted = 1;
+ break;
+ }
+ }
+ } else {
+ QLIST_FOREACH(ioh, &io_handlers, next) {
+ if (ioh->fd == fd)
+ goto found;
+ }
+ ioh = qemu_mallocz(sizeof(IOHandlerRecord));
+ QLIST_INSERT_HEAD(&io_handlers, ioh, next);
+ found:
+ ioh->fd = fd;
+ ioh->fd_read_poll = fd_read_poll;
+ ioh->fd_read = fd_read;
+ ioh->fd_write = fd_write;
+ ioh->opaque = opaque;
+ ioh->deleted = 0;
+ }
+ qemu_notify_event();
+ return 0;
+}
+
+int qemu_set_fd_handler(int fd,
+ IOHandler *fd_read,
+ IOHandler *fd_write,
+ void *opaque)
+{
+ return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
+}
+
+/***********************************************************/
+/* machine registration */
+
+static QEMUMachine *first_machine = NULL;
+QEMUMachine *current_machine = NULL;
+
+int qemu_register_machine(QEMUMachine *m)
+{
+ QEMUMachine **pm;
+ pm = &first_machine;
+ while (*pm != NULL)
+ pm = &(*pm)->next;
+ m->next = NULL;
+ *pm = m;
+ return 0;
+}
+
+static QEMUMachine *find_machine(const char *name)
+{
+ QEMUMachine *m;
+
+ for(m = first_machine; m != NULL; m = m->next) {
+ if (!strcmp(m->name, name))
+ return m;
+ if (m->alias && !strcmp(m->alias, name))
+ return m;
+ }
+ return NULL;
+}
+
+static QEMUMachine *find_default_machine(void)
+{
+ QEMUMachine *m;
+
+ for(m = first_machine; m != NULL; m = m->next) {
+ if (m->is_default) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
+/***********************************************************/
+/* main execution loop */
+
+static void gui_update(void *opaque)
+{
+ uint64_t interval = GUI_REFRESH_INTERVAL;
+ DisplayState *ds = opaque;
+ DisplayChangeListener *dcl = ds->listeners;
+
+ qemu_flush_coalesced_mmio_buffer();
+ dpy_refresh(ds);
+
+ while (dcl != NULL) {
+ if (dcl->gui_timer_interval &&
+ dcl->gui_timer_interval < interval)
+ interval = dcl->gui_timer_interval;
+ dcl = dcl->next;
+ }
+ qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock));
+}
+
+static void nographic_update(void *opaque)
+{
+ uint64_t interval = GUI_REFRESH_INTERVAL;
+
+ qemu_flush_coalesced_mmio_buffer();
+ qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock));
+}
+
+struct vm_change_state_entry {
+ VMChangeStateHandler *cb;
+ void *opaque;
+ QLIST_ENTRY (vm_change_state_entry) entries;
+};
+
+static QLIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head;
+
+VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
+ void *opaque)
+{
+ VMChangeStateEntry *e;
+
+ e = qemu_mallocz(sizeof (*e));
+
+ e->cb = cb;
+ e->opaque = opaque;
+ QLIST_INSERT_HEAD(&vm_change_state_head, e, entries);
+ return e;
+}
+
+void qemu_del_vm_change_state_handler(VMChangeStateEntry *e)
+{
+ QLIST_REMOVE (e, entries);
+ qemu_free (e);
+}
+
+void vm_state_notify(int running, int reason)
+{
+ VMChangeStateEntry *e;
+
+ trace_vm_state_notify(running, reason);
+
+ for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) {
+ e->cb(e->opaque, running, reason);
+ }
+}
+
+void vm_start(void)
+{
+ if (!vm_running) {
+ cpu_enable_ticks();
+ vm_running = 1;
+ vm_state_notify(1, 0);
+ resume_all_vcpus();
+ monitor_protocol_event(QEVENT_RESUME, NULL);
+ }
+}
+
+/* reset/shutdown handler */
+
+typedef struct QEMUResetEntry {
+ QTAILQ_ENTRY(QEMUResetEntry) entry;
+ QEMUResetHandler *func;
+ void *opaque;
+} QEMUResetEntry;
+
+static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers =
+ QTAILQ_HEAD_INITIALIZER(reset_handlers);
+static int reset_requested;
+static int shutdown_requested;
+static int powerdown_requested;
+int debug_requested;
+int vmstop_requested;
+
+int qemu_no_shutdown(void)
+{
+ int r = no_shutdown;
+ no_shutdown = 0;
+ return r;
+}
+
+int qemu_shutdown_requested(void)
+{
+ int r = shutdown_requested;
+ shutdown_requested = 0;
+ return r;
+}
+
+int qemu_reset_requested(void)
+{
+ int r = reset_requested;
+ reset_requested = 0;
+ return r;
+}
+
+int qemu_powerdown_requested(void)
+{
+ int r = powerdown_requested;
+ powerdown_requested = 0;
+ return r;
+}
+
+static int qemu_debug_requested(void)
+{
+ int r = debug_requested;
+ debug_requested = 0;
+ return r;
+}
+
+static int qemu_vmstop_requested(void)
+{
+ int r = vmstop_requested;
+ vmstop_requested = 0;
+ return r;
+}
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque)
+{
+ QEMUResetEntry *re = qemu_mallocz(sizeof(QEMUResetEntry));
+
+ re->func = func;
+ re->opaque = opaque;
+ QTAILQ_INSERT_TAIL(&reset_handlers, re, entry);
+}
+
+void qemu_unregister_reset(QEMUResetHandler *func, void *opaque)
+{
+ QEMUResetEntry *re;
+
+ QTAILQ_FOREACH(re, &reset_handlers, entry) {
+ if (re->func == func && re->opaque == opaque) {
+ QTAILQ_REMOVE(&reset_handlers, re, entry);
+ qemu_free(re);
+ return;
+ }
+ }
+}
+
+void qemu_system_reset(void)
+{
+ QEMUResetEntry *re, *nre;
+
+ /* reset all devices */
+ QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) {
+ re->func(re->opaque);
+ }
+ monitor_protocol_event(QEVENT_RESET, NULL);
+ cpu_synchronize_all_post_reset();
+}
+
+void qemu_system_reset_request(void)
+{
+ if (no_reboot) {
+ shutdown_requested = 1;
+ } else {
+ reset_requested = 1;
+ }
+ if (cpu_single_env) {
+ cpu_single_env->stopped = 1;
+ cpu_exit(cpu_single_env);
+ }
+ qemu_notify_event();
+}
+
+void qemu_system_shutdown_request(void)
+{
+ shutdown_requested = 1;
+ qemu_notify_event();
+}
+
+void qemu_system_powerdown_request(void)
+{
+ powerdown_requested = 1;
+ qemu_notify_event();
+}
+
+void main_loop_wait(int nonblocking)
+{
+ IOHandlerRecord *ioh;
+ fd_set rfds, wfds, xfds;
+ int ret, nfds;
+ struct timeval tv;
+ int timeout;
+
+ if (nonblocking)
+ timeout = 0;
+ else {
+ timeout = qemu_calculate_timeout();
+ qemu_bh_update_timeout(&timeout);
+ }
+
+ os_host_main_loop_wait(&timeout);
+
+ /* poll any events */
+ /* XXX: separate device handlers from system ones */
+ nfds = -1;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+ QLIST_FOREACH(ioh, &io_handlers, next) {
+ if (ioh->deleted)
+ continue;
+ if (ioh->fd_read &&
+ (!ioh->fd_read_poll ||
+ ioh->fd_read_poll(ioh->opaque) != 0)) {
+ FD_SET(ioh->fd, &rfds);
+ if (ioh->fd > nfds)
+ nfds = ioh->fd;
+ }
+ if (ioh->fd_write) {
+ FD_SET(ioh->fd, &wfds);
+ if (ioh->fd > nfds)
+ nfds = ioh->fd;
+ }
+ }
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+
+ slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+
+ qemu_mutex_unlock_iothread();
+ ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
+ qemu_mutex_lock_iothread();
+ if (ret > 0) {
+ IOHandlerRecord *pioh;
+
+ QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
+ if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) {
+ ioh->fd_read(ioh->opaque);
+ if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque)))
+ FD_CLR(ioh->fd, &rfds);
+ }
+ if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
+ ioh->fd_write(ioh->opaque);
+ }
+
+ /* Do this last in case read/write handlers marked it for deletion */
+ if (ioh->deleted) {
+ QLIST_REMOVE(ioh, next);
+ qemu_free(ioh);
+ }
+ }
+ }
+
+ slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
+
+ qemu_run_all_timers();
+
+ /* Check bottom-halves last in case any of the earlier events triggered
+ them. */
+ qemu_bh_poll();
+
+}
+
+static int vm_can_run(void)
+{
+ if (powerdown_requested)
+ return 0;
+ if (reset_requested)
+ return 0;
+ if (shutdown_requested)
+ return 0;
+ if (debug_requested)
+ return 0;
+ return 1;
+}
+
+qemu_irq qemu_system_powerdown;
+
+static void main_loop(void)
+{
+ int r;
+
+ if (kvm_enabled()) {
+ kvm_main_loop();
+ cpu_disable_ticks();
+ return;
+ }
+
+ qemu_main_loop_start();
+
+ for (;;) {
+ do {
+ bool nonblocking = false;
+#ifdef CONFIG_PROFILER
+ int64_t ti;
+#endif
+#ifndef CONFIG_IOTHREAD
+ nonblocking = cpu_exec_all();
+#endif
+#ifdef CONFIG_PROFILER
+ ti = profile_getclock();
+#endif
+ main_loop_wait(nonblocking);
+#ifdef CONFIG_PROFILER
+ dev_time += profile_getclock() - ti;
+#endif
+ } while (vm_can_run());
+
+ if ((r = qemu_debug_requested())) {
+ vm_stop(r);
+ }
+ if (qemu_shutdown_requested()) {
+ monitor_protocol_event(QEVENT_SHUTDOWN, NULL);
+ if (no_shutdown) {
+ vm_stop(0);
+ no_shutdown = 0;
+ } else
+ break;
+ }
+ if (qemu_reset_requested()) {
+ pause_all_vcpus();
+ qemu_system_reset();
+ resume_all_vcpus();
+ }
+ if (qemu_powerdown_requested()) {
+ monitor_protocol_event(QEVENT_POWERDOWN, NULL);
+ qemu_irq_raise(qemu_system_powerdown);
+ }
+ if ((r = qemu_vmstop_requested())) {
+ vm_stop(r);
+ }
+ }
+ bdrv_close_all();
+ pause_all_vcpus();
+}
+
+static void version(void)
+{
+ printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n");
+}
+
+static void help(int exitcode)
+{
+ const char *options_help =
+#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
+ opt_help
+#define DEFHEADING(text) stringify(text) "\n"
+#include "qemu-options.def"
+#undef DEF
+#undef DEFHEADING
+#undef GEN_DOCS
+ ;
+ version();
+ printf("usage: %s [options] [disk_image]\n"
+ "\n"
+ "'disk_image' is a raw hard disk image for IDE hard disk 0\n"
+ "\n"
+ "%s\n"
+ "During emulation, the following keys are useful:\n"
+ "ctrl-alt-f toggle full screen\n"
+ "ctrl-alt-n switch to virtual console 'n'\n"
+ "ctrl-alt toggle mouse and keyboard grab\n"
+ "\n"
+ "When using -nographic, press 'ctrl-a h' to get some help.\n",
+ "qemu",
+ options_help);
+ exit(exitcode);
+}
+
+#define HAS_ARG 0x0001
+
+typedef struct QEMUOption {
+ const char *name;
+ int flags;
+ int index;
+ uint32_t arch_mask;
+} QEMUOption;
+
+static const QEMUOption qemu_options[] = {
+ { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },
+#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
+ { option, opt_arg, opt_enum, arch_mask },
+#define DEFHEADING(text)
+#include "qemu-options.def"
+#undef DEF
+#undef DEFHEADING
+#undef GEN_DOCS
+ { NULL },
+};
+static void select_vgahw (const char *p)
+{
+ const char *opts;
+
+ default_vga = 0;
+ vga_interface_type = VGA_NONE;
+ if (strstart(p, "std", &opts)) {
+ vga_interface_type = VGA_STD;
+ } else if (strstart(p, "cirrus", &opts)) {
+ vga_interface_type = VGA_CIRRUS;
+ } else if (strstart(p, "vmware", &opts)) {
+ vga_interface_type = VGA_VMWARE;
+ } else if (strstart(p, "xenfb", &opts)) {
+ vga_interface_type = VGA_XENFB;
+ } else if (strstart(p, "qxl", &opts)) {
+ vga_interface_type = VGA_QXL;
+ } else if (!strstart(p, "none", &opts)) {
+ invalid_vga:
+ fprintf(stderr, "Unknown vga type: %s\n", p);
+ exit(1);
+ }
+ while (*opts) {
+ const char *nextopt;
+
+ if (strstart(opts, ",retrace=", &nextopt)) {
+ opts = nextopt;
+ if (strstart(opts, "dumb", &nextopt))
+ vga_retrace_method = VGA_RETRACE_DUMB;
+ else if (strstart(opts, "precise", &nextopt))
+ vga_retrace_method = VGA_RETRACE_PRECISE;
+ else goto invalid_vga;
+ } else goto invalid_vga;
+ opts = nextopt;
+ }
+}
+
+static int balloon_parse(const char *arg)
+{
+ QemuOpts *opts;
+
+ if (strcmp(arg, "none") == 0) {
+ return 0;
+ }
+
+ if (!strncmp(arg, "virtio", 6)) {
+ if (arg[6] == ',') {
+ /* have params -> parse them */
+ opts = qemu_opts_parse(qemu_find_opts("device"), arg+7, 0);
+ if (!opts)
+ return -1;
+ } else {
+ /* create empty opts */
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ }
+ qemu_opt_set(opts, "driver", "virtio-balloon-pci");
+ return 0;
+ }
+
+ return -1;
+}
+
+char *qemu_find_file(int type, const char *name)
+{
+ int len;
+ const char *subdir;
+ char *buf;
+
+ /* If name contains path separators then try it as a straight path. */
+ if ((strchr(name, '/') || strchr(name, '\\'))
+ && access(name, R_OK) == 0) {
+ return qemu_strdup(name);
+ }
+ switch (type) {
+ case QEMU_FILE_TYPE_BIOS:
+ subdir = "";
+ break;
+ case QEMU_FILE_TYPE_KEYMAP:
+ subdir = "keymaps/";
+ break;
+ default:
+ abort();
+ }
+ len = strlen(data_dir) + strlen(name) + strlen(subdir) + 2;
+ buf = qemu_mallocz(len);
+ snprintf(buf, len, "%s/%s%s", data_dir, subdir, name);
+ if (access(buf, R_OK)) {
+ qemu_free(buf);
+ return NULL;
+ }
+ return buf;
+}
+
+static int device_help_func(QemuOpts *opts, void *opaque)
+{
+ return qdev_device_help(opts);
+}
+
+static int device_init_func(QemuOpts *opts, void *opaque)
+{
+ DeviceState *dev;
+
+ dev = qdev_device_add(opts);
+ if (!dev)
+ return -1;
+ return 0;
+}
+
+static int chardev_init_func(QemuOpts *opts, void *opaque)
+{
+ CharDriverState *chr;
+
+ chr = qemu_chr_open_opts(opts, NULL);
+ if (!chr)
+ return -1;
+ return 0;
+}
+
+#ifdef CONFIG_VIRTFS
+static int fsdev_init_func(QemuOpts *opts, void *opaque)
+{
+ int ret;
+ ret = qemu_fsdev_add(opts);
+
+ return ret;
+}
+#endif
+
+static int mon_init_func(QemuOpts *opts, void *opaque)
+{
+ CharDriverState *chr;
+ const char *chardev;
+ const char *mode;
+ int flags;
+
+ mode = qemu_opt_get(opts, "mode");
+ if (mode == NULL) {
+ mode = "readline";
+ }
+ if (strcmp(mode, "readline") == 0) {
+ flags = MONITOR_USE_READLINE;
+ } else if (strcmp(mode, "control") == 0) {
+ flags = MONITOR_USE_CONTROL;
+ } else {
+ fprintf(stderr, "unknown monitor mode \"%s\"\n", mode);
+ exit(1);
+ }
+
+ if (qemu_opt_get_bool(opts, "pretty", 0))
+ flags |= MONITOR_USE_PRETTY;
+
+ if (qemu_opt_get_bool(opts, "default", 0))
+ flags |= MONITOR_IS_DEFAULT;
+
+ chardev = qemu_opt_get(opts, "chardev");
+ chr = qemu_chr_find(chardev);
+ if (chr == NULL) {
+ fprintf(stderr, "chardev \"%s\" not found\n", chardev);
+ exit(1);
+ }
+
+ monitor_init(chr, flags);
+ return 0;
+}
+
+static void monitor_parse(const char *optarg, const char *mode)
+{
+ static int monitor_device_index = 0;
+ QemuOpts *opts;
+ const char *p;
+ char label[32];
+ int def = 0;
+
+ if (strstart(optarg, "chardev:", &p)) {
+ snprintf(label, sizeof(label), "%s", p);
+ } else {
+ snprintf(label, sizeof(label), "compat_monitor%d",
+ monitor_device_index);
+ if (monitor_device_index == 0) {
+ def = 1;
+ }
+ opts = qemu_chr_parse_compat(label, optarg);
+ if (!opts) {
+ fprintf(stderr, "parse error: %s\n", optarg);
+ exit(1);
+ }
+ }
+
+ opts = qemu_opts_create(qemu_find_opts("mon"), label, 1);
+ if (!opts) {
+ fprintf(stderr, "duplicate chardev: %s\n", label);
+ exit(1);
+ }
+ qemu_opt_set(opts, "mode", mode);
+ qemu_opt_set(opts, "chardev", label);
+ if (def)
+ qemu_opt_set(opts, "default", "on");
+ monitor_device_index++;
+}
+
+struct device_config {
+ enum {
+ DEV_USB, /* -usbdevice */
+ DEV_BT, /* -bt */
+ DEV_SERIAL, /* -serial */
+ DEV_PARALLEL, /* -parallel */
+ DEV_VIRTCON, /* -virtioconsole */
+ DEV_DEBUGCON, /* -debugcon */
+ } type;
+ const char *cmdline;
+ QTAILQ_ENTRY(device_config) next;
+};
+QTAILQ_HEAD(, device_config) device_configs = QTAILQ_HEAD_INITIALIZER(device_configs);
+
+static void add_device_config(int type, const char *cmdline)
+{
+ struct device_config *conf;
+
+ conf = qemu_mallocz(sizeof(*conf));
+ conf->type = type;
+ conf->cmdline = cmdline;
+ QTAILQ_INSERT_TAIL(&device_configs, conf, next);
+}
+
+static int foreach_device_config(int type, int (*func)(const char *cmdline))
+{
+ struct device_config *conf;
+ int rc;
+
+ QTAILQ_FOREACH(conf, &device_configs, next) {
+ if (conf->type != type)
+ continue;
+ rc = func(conf->cmdline);
+ if (0 != rc)
+ return rc;
+ }
+ return 0;
+}
+
+static int serial_parse(const char *devname)
+{
+ static int index = 0;
+ char label[32];
+
+ if (strcmp(devname, "none") == 0)
+ return 0;
+ if (index == MAX_SERIAL_PORTS) {
+ fprintf(stderr, "qemu: too many serial ports\n");
+ exit(1);
+ }
+ snprintf(label, sizeof(label), "serial%d", index);
+ serial_hds[index] = qemu_chr_open(label, devname, NULL);
+ if (!serial_hds[index]) {
+ fprintf(stderr, "qemu: could not open serial device '%s': %s\n",
+ devname, strerror(errno));
+ return -1;
+ }
+ index++;
+ return 0;
+}
+
+static int parallel_parse(const char *devname)
+{
+ static int index = 0;
+ char label[32];
+
+ if (strcmp(devname, "none") == 0)
+ return 0;
+ if (index == MAX_PARALLEL_PORTS) {
+ fprintf(stderr, "qemu: too many parallel ports\n");
+ exit(1);
+ }
+ snprintf(label, sizeof(label), "parallel%d", index);
+ parallel_hds[index] = qemu_chr_open(label, devname, NULL);
+ if (!parallel_hds[index]) {
+ fprintf(stderr, "qemu: could not open parallel device '%s': %s\n",
+ devname, strerror(errno));
+ return -1;
+ }
+ index++;
+ return 0;
+}
+
+static int virtcon_parse(const char *devname)
+{
+ QemuOptsList *device = qemu_find_opts("device");
+ static int index = 0;
+ char label[32];
+ QemuOpts *bus_opts, *dev_opts;
+
+ if (strcmp(devname, "none") == 0)
+ return 0;
+ if (index == MAX_VIRTIO_CONSOLES) {
+ fprintf(stderr, "qemu: too many virtio consoles\n");
+ exit(1);
+ }
+
+ bus_opts = qemu_opts_create(device, NULL, 0);
+ qemu_opt_set(bus_opts, "driver", "virtio-serial");
+
+ dev_opts = qemu_opts_create(device, NULL, 0);
+ qemu_opt_set(dev_opts, "driver", "virtconsole");
+
+ snprintf(label, sizeof(label), "virtcon%d", index);
+ virtcon_hds[index] = qemu_chr_open(label, devname, NULL);
+ if (!virtcon_hds[index]) {
+ fprintf(stderr, "qemu: could not open virtio console '%s': %s\n",
+ devname, strerror(errno));
+ return -1;
+ }
+ qemu_opt_set(dev_opts, "chardev", label);
+
+ index++;
+ return 0;
+}
+
+static int debugcon_parse(const char *devname)
+{
+ QemuOpts *opts;
+
+ if (!qemu_chr_open("debugcon", devname, NULL)) {
+ exit(1);
+ }
+ opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1);
+ if (!opts) {
+ fprintf(stderr, "qemu: already have a debugcon device\n");
+ exit(1);
+ }
+ qemu_opt_set(opts, "driver", "isa-debugcon");
+ qemu_opt_set(opts, "chardev", "debugcon");
+ return 0;
+}
+
+void qemu_add_exit_notifier(Notifier *notify)
+{
+ notifier_list_add(&exit_notifiers, notify);
+}
+
+void qemu_remove_exit_notifier(Notifier *notify)
+{
+ notifier_list_remove(&exit_notifiers, notify);
+}
+
+static void qemu_run_exit_notifiers(void)
+{
+ notifier_list_notify(&exit_notifiers);
+}
+
+void qemu_add_machine_init_done_notifier(Notifier *notify)
+{
+ notifier_list_add(&machine_init_done_notifiers, notify);
+}
+
+static void qemu_run_machine_init_done_notifiers(void)
+{
+ notifier_list_notify(&machine_init_done_notifiers);
+}
+
+static const QEMUOption *lookup_opt(int argc, char **argv,
+ const char **poptarg, int *poptind)
+{
+ const QEMUOption *popt;
+ int optind = *poptind;
+ char *r = argv[optind];
+ const char *optarg;
+
+ loc_set_cmdline(argv, optind, 1);
+ optind++;
+ /* Treat --foo the same as -foo. */
+ if (r[1] == '-')
+ r++;
+ popt = qemu_options;
+ for(;;) {
+ if (!popt->name) {
+ error_report("invalid option");
+ exit(1);
+ }
+ if (!strcmp(popt->name, r + 1))
+ break;
+ popt++;
+ }
+ if (popt->flags & HAS_ARG) {
+ if (optind >= argc) {
+ error_report("requires an argument");
+ exit(1);
+ }
+ optarg = argv[optind++];
+ loc_set_cmdline(argv, optind - 2, 2);
+ } else {
+ optarg = NULL;
+ }
+
+ *poptarg = optarg;
+ *poptind = optind;
+
+ return popt;
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ const char *gdbstub_dev = NULL;
+ int i;
+ int snapshot, linux_boot;
+ const char *icount_option = NULL;
+ const char *initrd_filename;
+ const char *kernel_filename, *kernel_cmdline;
+ char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */
+ DisplayState *ds;
+ DisplayChangeListener *dcl;
+ int cyls, heads, secs, translation;
+ QemuOpts *hda_opts = NULL, *opts;
+ QemuOptsList *olist;
+ int optind;
+ const char *optarg;
+ const char *loadvm = NULL;
+ QEMUMachine *machine;
+ const char *cpu_model;
+ int tb_size;
+ const char *pid_file = NULL;
+ const char *incoming = NULL;
+ int show_vnc_port = 0;
+ int defconfig = 1;
+
+#ifdef CONFIG_SIMPLE_TRACE
+ const char *trace_file = NULL;
+#endif
+ atexit(qemu_run_exit_notifiers);
+ error_set_progname(argv[0]);
+
+ init_clocks();
+
+ qemu_cache_utils_init(envp);
+
+ QLIST_INIT (&vm_change_state_head);
+ os_setup_early_signal_handling();
+
+ module_call_init(MODULE_INIT_MACHINE);
+ machine = find_default_machine();
+ cpu_model = NULL;
+ initrd_filename = NULL;
+ ram_size = 0;
+ snapshot = 0;
+ kernel_filename = NULL;
+ kernel_cmdline = "";
+ cyls = heads = secs = 0;
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+
+ for (i = 0; i < MAX_NODES; i++) {
+ node_mem[i] = 0;
+ node_cpumask[i] = 0;
+ }
+
+ nb_numa_nodes = 0;
+ nb_nics = 0;
+
+ tb_size = 0;
+ autostart= 1;
+
+ /* first pass of option parsing */
+ optind = 1;
+ while (optind < argc) {
+ if (argv[optind][0] != '-') {
+ /* disk image */
+ optind++;
+ continue;
+ } else {
+ const QEMUOption *popt;
+
+ popt = lookup_opt(argc, argv, &optarg, &optind);
+ switch (popt->index) {
+ case QEMU_OPTION_nodefconfig:
+ defconfig=0;
+ break;
+ }
+ }
+ }
+
+ if (defconfig) {
+ int ret;
+
+ ret = qemu_read_config_file(CONFIG_QEMU_CONFDIR "/qemu.conf");
+ if (ret < 0 && ret != -ENOENT) {
+ exit(1);
+ }
+
+ ret = qemu_read_config_file(arch_config_name);
+ if (ret < 0 && ret != -ENOENT) {
+ exit(1);
+ }
+ }
+ cpudef_init();
+
+ /* second pass of option parsing */
+ optind = 1;
+ for(;;) {
+ if (optind >= argc)
+ break;
+ if (argv[optind][0] != '-') {
+ hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
+ } else {
+ const QEMUOption *popt;
+
+ popt = lookup_opt(argc, argv, &optarg, &optind);
+ if (!(popt->arch_mask & arch_type)) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ switch(popt->index) {
+ case QEMU_OPTION_M:
+ machine = find_machine(optarg);
+ if (!machine) {
+ QEMUMachine *m;
+ printf("Supported machines are:\n");
+ for(m = first_machine; m != NULL; m = m->next) {
+ if (m->alias)
+ printf("%-10s %s (alias of %s)\n",
+ m->alias, m->desc, m->name);
+ printf("%-10s %s%s\n",
+ m->name, m->desc,
+ m->is_default ? " (default)" : "");
+ }
+ exit(*optarg != '?');
+ }
+ break;
+ case QEMU_OPTION_cpu:
+ /* hw initialization will check this */
+ if (*optarg == '?') {
+ list_cpus(stdout, &fprintf, optarg);
+ exit(0);
+ } else {
+ cpu_model = optarg;
+ }
+ break;
+ case QEMU_OPTION_initrd:
+ initrd_filename = optarg;
+ break;
+ case QEMU_OPTION_hda:
+ {
+ char buf[256];
+ if (cyls == 0)
+ snprintf(buf, sizeof(buf), "%s", HD_OPTS);
+ else
+ snprintf(buf, sizeof(buf),
+ "%s,cyls=%d,heads=%d,secs=%d%s",
+ HD_OPTS , cyls, heads, secs,
+ translation == BIOS_ATA_TRANSLATION_LBA ?
+ ",trans=lba" :
+ translation == BIOS_ATA_TRANSLATION_NONE ?
+ ",trans=none" : "");
+ drive_add(IF_DEFAULT, 0, optarg, buf);
+ break;
+ }
+ case QEMU_OPTION_hdb:
+ case QEMU_OPTION_hdc:
+ case QEMU_OPTION_hdd:
+ drive_add(IF_DEFAULT, popt->index - QEMU_OPTION_hda, optarg,
+ HD_OPTS);
+ break;
+ case QEMU_OPTION_drive:
+ if (drive_def(optarg) == NULL) {
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_set:
+ if (qemu_set_option(optarg) != 0)
+ exit(1);
+ break;
+ case QEMU_OPTION_global:
+ if (qemu_global_option(optarg) != 0)
+ exit(1);
+ break;
+ case QEMU_OPTION_mtdblock:
+ drive_add(IF_MTD, -1, optarg, MTD_OPTS);
+ break;
+ case QEMU_OPTION_sd:
+ drive_add(IF_SD, 0, optarg, SD_OPTS);
+ break;
+ case QEMU_OPTION_pflash:
+ drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS);
+ break;
+ case QEMU_OPTION_snapshot:
+ snapshot = 1;
+ break;
+ case QEMU_OPTION_hdachs:
+ {
+ const char *p;
+ p = optarg;
+ cyls = strtol(p, (char **)&p, 0);
+ if (cyls < 1 || cyls > 16383)
+ goto chs_fail;
+ if (*p != ',')
+ goto chs_fail;
+ p++;
+ heads = strtol(p, (char **)&p, 0);
+ if (heads < 1 || heads > 16)
+ goto chs_fail;
+ if (*p != ',')
+ goto chs_fail;
+ p++;
+ secs = strtol(p, (char **)&p, 0);
+ if (secs < 1 || secs > 63)
+ goto chs_fail;
+ if (*p == ',') {
+ p++;
+ if (!strcmp(p, "none"))
+ translation = BIOS_ATA_TRANSLATION_NONE;
+ else if (!strcmp(p, "lba"))
+ translation = BIOS_ATA_TRANSLATION_LBA;
+ else if (!strcmp(p, "auto"))
+ translation = BIOS_ATA_TRANSLATION_AUTO;
+ else
+ goto chs_fail;
+ } else if (*p != '\0') {
+ chs_fail:
+ fprintf(stderr, "qemu: invalid physical CHS format\n");
+ exit(1);
+ }
+ if (hda_opts != NULL) {
+ char num[16];
+ snprintf(num, sizeof(num), "%d", cyls);
+ qemu_opt_set(hda_opts, "cyls", num);
+ snprintf(num, sizeof(num), "%d", heads);
+ qemu_opt_set(hda_opts, "heads", num);
+ snprintf(num, sizeof(num), "%d", secs);
+ qemu_opt_set(hda_opts, "secs", num);
+ if (translation == BIOS_ATA_TRANSLATION_LBA)
+ qemu_opt_set(hda_opts, "trans", "lba");
+ if (translation == BIOS_ATA_TRANSLATION_NONE)
+ qemu_opt_set(hda_opts, "trans", "none");
+ }
+ }
+ break;
+ case QEMU_OPTION_numa:
+ if (nb_numa_nodes >= MAX_NODES) {
+ fprintf(stderr, "qemu: too many NUMA nodes\n");
+ exit(1);
+ }
+ numa_add(optarg);
+ break;
+ case QEMU_OPTION_nographic:
+ display_type = DT_NOGRAPHIC;
+ break;
+#ifdef CONFIG_CURSES
+ case QEMU_OPTION_curses:
+ display_type = DT_CURSES;
+ break;
+#endif
+ case QEMU_OPTION_portrait:
+ graphic_rotate = 1;
+ break;
+ case QEMU_OPTION_kernel:
+ kernel_filename = optarg;
+ break;
+ case QEMU_OPTION_append:
+ kernel_cmdline = optarg;
+ break;
+ case QEMU_OPTION_cdrom:
+ drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS);
+ break;
+ case QEMU_OPTION_boot:
+ {
+ static const char * const params[] = {
+ "order", "once", "menu", NULL
+ };
+ char buf[sizeof(boot_devices)];
+ char *standard_boot_devices;
+ int legacy = 0;
+
+ if (!strchr(optarg, '=')) {
+ legacy = 1;
+ pstrcpy(buf, sizeof(buf), optarg);
+ } else if (check_params(buf, sizeof(buf), params, optarg) < 0) {
+ fprintf(stderr,
+ "qemu: unknown boot parameter '%s' in '%s'\n",
+ buf, optarg);
+ exit(1);
+ }
+
+ if (legacy ||
+ get_param_value(buf, sizeof(buf), "order", optarg)) {
+ validate_bootdevices(buf);
+ pstrcpy(boot_devices, sizeof(boot_devices), buf);
+ }
+ if (!legacy) {
+ if (get_param_value(buf, sizeof(buf),
+ "once", optarg)) {
+ validate_bootdevices(buf);
+ standard_boot_devices = qemu_strdup(boot_devices);
+ pstrcpy(boot_devices, sizeof(boot_devices), buf);
+ qemu_register_reset(restore_boot_devices,
+ standard_boot_devices);
+ }
+ if (get_param_value(buf, sizeof(buf),
+ "menu", optarg)) {
+ if (!strcmp(buf, "on")) {
+ boot_menu = 1;
+ } else if (!strcmp(buf, "off")) {
+ boot_menu = 0;
+ } else {
+ fprintf(stderr,
+ "qemu: invalid option value '%s'\n",
+ buf);
+ exit(1);
+ }
+ }
+ }
+ }
+ break;
+ case QEMU_OPTION_fda:
+ case QEMU_OPTION_fdb:
+ drive_add(IF_FLOPPY, popt->index - QEMU_OPTION_fda,
+ optarg, FD_OPTS);
+ break;
+ case QEMU_OPTION_no_fd_bootchk:
+ fd_bootchk = 0;
+ break;
+ case QEMU_OPTION_netdev:
+ if (net_client_parse(qemu_find_opts("netdev"), optarg) == -1) {
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_net:
+ if (net_client_parse(qemu_find_opts("net"), optarg) == -1) {
+ exit(1);
+ }
+ break;
+#ifdef CONFIG_SLIRP
+ case QEMU_OPTION_tftp:
+ legacy_tftp_prefix = optarg;
+ break;
+ case QEMU_OPTION_bootp:
+ legacy_bootp_filename = optarg;
+ break;
+ case QEMU_OPTION_redir:
+ if (net_slirp_redir(optarg) < 0)
+ exit(1);
+ break;
+#endif
+ case QEMU_OPTION_bt:
+ add_device_config(DEV_BT, optarg);
+ break;
+ case QEMU_OPTION_audio_help:
+ if (!(audio_available())) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ AUD_help ();
+ exit (0);
+ break;
+ case QEMU_OPTION_soundhw:
+ if (!(audio_available())) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ select_soundhw (optarg);
+ break;
+ case QEMU_OPTION_h:
+ help(0);
+ break;
+ case QEMU_OPTION_version:
+ version();
+ exit(0);
+ break;
+ case QEMU_OPTION_m: {
+ int64_t value;
+
+ value = strtosz(optarg, NULL);
+ if (value < 0) {
+ fprintf(stderr, "qemu: invalid ram size: %s\n", optarg);
+ exit(1);
+ }
+
+ /* On 32-bit hosts, QEMU is limited by virtual address space */
+ if (value > (2047 << 20) && HOST_LONG_BITS == 32) {
+ fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n");
+ exit(1);
+ }
+ if (value != (uint64_t)(ram_addr_t)value) {
+ fprintf(stderr, "qemu: ram size too large\n");
+ exit(1);
+ }
+ ram_size = value;
+ break;
+ }
+ case QEMU_OPTION_mempath:
+ mem_path = optarg;
+ break;
+#ifdef MAP_POPULATE
+ case QEMU_OPTION_mem_prealloc:
+ mem_prealloc = 1;
+ break;
+#endif
+ case QEMU_OPTION_d:
+ set_cpu_log(optarg);
+ break;
+ case QEMU_OPTION_s:
+ gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT;
+ break;
+ case QEMU_OPTION_gdb:
+ gdbstub_dev = optarg;
+ break;
+ case QEMU_OPTION_L:
+ data_dir = optarg;
+ break;
+ case QEMU_OPTION_bios:
+ bios_name = optarg;
+ break;
+ case QEMU_OPTION_singlestep:
+ singlestep = 1;
+ break;
+ case QEMU_OPTION_S:
+ autostart = 0;
+ break;
+ case QEMU_OPTION_k:
+ keyboard_layout = optarg;
+ break;
+ case QEMU_OPTION_localtime:
+ rtc_utc = 0;
+ break;
+ case QEMU_OPTION_vga:
+ select_vgahw (optarg);
+ break;
+ case QEMU_OPTION_g:
+ {
+ const char *p;
+ int w, h, depth;
+ p = optarg;
+ w = strtol(p, (char **)&p, 10);
+ if (w <= 0) {
+ graphic_error:
+ fprintf(stderr, "qemu: invalid resolution or depth\n");
+ exit(1);
+ }
+ if (*p != 'x')
+ goto graphic_error;
+ p++;
+ h = strtol(p, (char **)&p, 10);
+ if (h <= 0)
+ goto graphic_error;
+ if (*p == 'x') {
+ p++;
+ depth = strtol(p, (char **)&p, 10);
+ if (depth != 8 && depth != 15 && depth != 16 &&
+ depth != 24 && depth != 32)
+ goto graphic_error;
+ } else if (*p == '\0') {
+ depth = graphic_depth;
+ } else {
+ goto graphic_error;
+ }
+
+ graphic_width = w;
+ graphic_height = h;
+ graphic_depth = depth;
+ }
+ break;
+ case QEMU_OPTION_echr:
+ {
+ char *r;
+ term_escape_char = strtol(optarg, &r, 0);
+ if (r == optarg)
+ printf("Bad argument to echr\n");
+ break;
+ }
+ case QEMU_OPTION_monitor:
+ monitor_parse(optarg, "readline");
+ default_monitor = 0;
+ break;
+ case QEMU_OPTION_qmp:
+ monitor_parse(optarg, "control");
+ default_monitor = 0;
+ break;
+ case QEMU_OPTION_mon:
+ opts = qemu_opts_parse(qemu_find_opts("mon"), optarg, 1);
+ if (!opts) {
+ exit(1);
+ }
+ default_monitor = 0;
+ break;
+ case QEMU_OPTION_chardev:
+ opts = qemu_opts_parse(qemu_find_opts("chardev"), optarg, 1);
+ if (!opts) {
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_fsdev:
+ olist = qemu_find_opts("fsdev");
+ if (!olist) {
+ fprintf(stderr, "fsdev is not supported by this qemu build.\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(olist, optarg, 1);
+ if (!opts) {
+ fprintf(stderr, "parse error: %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_virtfs: {
+ char *arg_fsdev = NULL;
+ char *arg_9p = NULL;
+ int len = 0;
+
+ olist = qemu_find_opts("virtfs");
+ if (!olist) {
+ fprintf(stderr, "virtfs is not supported by this qemu build.\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(olist, optarg, 1);
+ if (!opts) {
+ fprintf(stderr, "parse error: %s\n", optarg);
+ exit(1);
+ }
+
+ if (qemu_opt_get(opts, "fstype") == NULL ||
+ qemu_opt_get(opts, "mount_tag") == NULL ||
+ qemu_opt_get(opts, "path") == NULL ||
+ qemu_opt_get(opts, "security_model") == NULL) {
+ fprintf(stderr, "Usage: -virtfs fstype,path=/share_path/,"
+ "security_model=[mapped|passthrough|none],"
+ "mnt_tag=tag.\n");
+ exit(1);
+ }
+
+ len = strlen(",id=,path=,security_model=");
+ len += strlen(qemu_opt_get(opts, "fstype"));
+ len += strlen(qemu_opt_get(opts, "mount_tag"));
+ len += strlen(qemu_opt_get(opts, "path"));
+ len += strlen(qemu_opt_get(opts, "security_model"));
+ arg_fsdev = qemu_malloc((len + 1) * sizeof(*arg_fsdev));
+
+ snprintf(arg_fsdev, (len + 1) * sizeof(*arg_fsdev),
+ "%s,id=%s,path=%s,security_model=%s",
+ qemu_opt_get(opts, "fstype"),
+ qemu_opt_get(opts, "mount_tag"),
+ qemu_opt_get(opts, "path"),
+ qemu_opt_get(opts, "security_model"));
+
+ len = strlen("virtio-9p-pci,fsdev=,mount_tag=");
+ len += 2*strlen(qemu_opt_get(opts, "mount_tag"));
+ arg_9p = qemu_malloc((len + 1) * sizeof(*arg_9p));
+
+ snprintf(arg_9p, (len + 1) * sizeof(*arg_9p),
+ "virtio-9p-pci,fsdev=%s,mount_tag=%s",
+ qemu_opt_get(opts, "mount_tag"),
+ qemu_opt_get(opts, "mount_tag"));
+
+ if (!qemu_opts_parse(qemu_find_opts("fsdev"), arg_fsdev, 1)) {
+ fprintf(stderr, "parse error [fsdev]: %s\n", optarg);
+ exit(1);
+ }
+
+ if (!qemu_opts_parse(qemu_find_opts("device"), arg_9p, 1)) {
+ fprintf(stderr, "parse error [device]: %s\n", optarg);
+ exit(1);
+ }
+
+ qemu_free(arg_fsdev);
+ qemu_free(arg_9p);
+ break;
+ }
+ case QEMU_OPTION_serial:
+ add_device_config(DEV_SERIAL, optarg);
+ default_serial = 0;
+ if (strncmp(optarg, "mon:", 4) == 0) {
+ default_monitor = 0;
+ }
+ break;
+ case QEMU_OPTION_watchdog:
+ if (watchdog) {
+ fprintf(stderr,
+ "qemu: only one watchdog option may be given\n");
+ return 1;
+ }
+ watchdog = optarg;
+ break;
+ case QEMU_OPTION_watchdog_action:
+ if (select_watchdog_action(optarg) == -1) {
+ fprintf(stderr, "Unknown -watchdog-action parameter\n");
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_virtiocon:
+ add_device_config(DEV_VIRTCON, optarg);
+ default_virtcon = 0;
+ if (strncmp(optarg, "mon:", 4) == 0) {
+ default_monitor = 0;
+ }
+ break;
+ case QEMU_OPTION_parallel:
+ add_device_config(DEV_PARALLEL, optarg);
+ default_parallel = 0;
+ if (strncmp(optarg, "mon:", 4) == 0) {
+ default_monitor = 0;
+ }
+ break;
+ case QEMU_OPTION_debugcon:
+ add_device_config(DEV_DEBUGCON, optarg);
+ break;
+ case QEMU_OPTION_loadvm:
+ loadvm = optarg;
+ break;
+ case QEMU_OPTION_full_screen:
+ full_screen = 1;
+ break;
+#ifdef CONFIG_SDL
+ case QEMU_OPTION_no_frame:
+ no_frame = 1;
+ break;
+ case QEMU_OPTION_alt_grab:
+ alt_grab = 1;
+ break;
+ case QEMU_OPTION_ctrl_grab:
+ ctrl_grab = 1;
+ break;
+ case QEMU_OPTION_no_quit:
+ no_quit = 1;
+ break;
+ case QEMU_OPTION_sdl:
+ display_type = DT_SDL;
+ break;
+#endif
+ case QEMU_OPTION_pidfile:
+ pid_file = optarg;
+ break;
+ case QEMU_OPTION_win2k_hack:
+ win2k_install_hack = 1;
+ break;
+ case QEMU_OPTION_rtc_td_hack:
+ rtc_td_hack = 1;
+ break;
+ case QEMU_OPTION_acpitable:
+ do_acpitable_option(optarg);
+ break;
+ case QEMU_OPTION_smbios:
+ do_smbios_option(optarg);
+ break;
+ case QEMU_OPTION_enable_kvm:
+ kvm_allowed = 1;
+ break;
+ case QEMU_OPTION_no_kvm:
+ kvm_allowed = 0;
+#ifdef CONFIG_NO_CPU_EMULATION
+ fprintf(stderr, "cpu emulation not configured\n");
+ exit(1);
+#endif
+ break;
+#ifdef CONFIG_KVM
+ case QEMU_OPTION_no_kvm_irqchip: {
+ kvm_irqchip = 0;
+ kvm_pit = 0;
+ break;
+ }
+ case QEMU_OPTION_no_kvm_pit: {
+ kvm_pit = 0;
+ break;
+ }
+ case QEMU_OPTION_no_kvm_pit_reinjection: {
+ kvm_pit_reinject = 0;
+ break;
+ }
+ case QEMU_OPTION_enable_nesting: {
+ kvm_nested = 1;
+ break;
+ }
+#endif
+ case QEMU_OPTION_usb:
+ usb_enabled = 1;
+ break;
+ case QEMU_OPTION_usbdevice:
+ usb_enabled = 1;
+ add_device_config(DEV_USB, optarg);
+ break;
+ case QEMU_OPTION_device:
+ if (!qemu_opts_parse(qemu_find_opts("device"), optarg, 1)) {
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_smp:
+ smp_parse(optarg);
+ if (smp_cpus < 1) {
+ fprintf(stderr, "Invalid number of CPUs\n");
+ exit(1);
+ }
+ if (max_cpus < smp_cpus) {
+ fprintf(stderr, "maxcpus must be equal to or greater than "
+ "smp\n");
+ exit(1);
+ }
+ if (max_cpus > 255) {
+ fprintf(stderr, "Unsupported number of maxcpus\n");
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_vnc:
+ display_remote++;
+ vnc_display = optarg;
+ break;
+ case QEMU_OPTION_no_acpi:
+ acpi_enabled = 0;
+ break;
+ case QEMU_OPTION_no_hpet:
+ no_hpet = 1;
+ break;
+ case QEMU_OPTION_balloon:
+ if (balloon_parse(optarg) < 0) {
+ fprintf(stderr, "Unknown -balloon argument %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_no_reboot:
+ no_reboot = 1;
+ break;
+ case QEMU_OPTION_no_shutdown:
+ no_shutdown = 1;
+ break;
+ case QEMU_OPTION_show_cursor:
+ cursor_hide = 0;
+ break;
+ case QEMU_OPTION_uuid:
+ if(qemu_uuid_parse(optarg, qemu_uuid) < 0) {
+ fprintf(stderr, "Fail to parse UUID string."
+ " Wrong format.\n");
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_option_rom:
+ if (nb_option_roms >= MAX_OPTION_ROMS) {
+ fprintf(stderr, "Too many option ROMs\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(qemu_find_opts("option-rom"), optarg, 1);
+ option_rom[nb_option_roms].name = qemu_opt_get(opts, "romfile");
+ option_rom[nb_option_roms].bootindex =
+ qemu_opt_get_number(opts, "bootindex", -1);
+ if (!option_rom[nb_option_roms].name) {
+ fprintf(stderr, "Option ROM file is not specified\n");
+ exit(1);
+ }
+ nb_option_roms++;
+ break;
+ case QEMU_OPTION_semihosting:
+ semihosting_enabled = 1;
+ break;
+ case QEMU_OPTION_tdf:
+ time_drift_fix = 1;
+ break;
+ case QEMU_OPTION_kvm_shadow_memory:
+ kvm_shadow_memory = (int64_t)atoi(optarg) * 1024 * 1024 / 4096;
+ break;
+ case QEMU_OPTION_name:
+ qemu_name = qemu_strdup(optarg);
+ {
+ char *p = strchr(qemu_name, ',');
+ if (p != NULL) {
+ *p++ = 0;
+ if (strncmp(p, "process=", 8)) {
+ fprintf(stderr, "Unknown subargument %s to -name\n", p);
+ exit(1);
+ }
+ p += 8;
+ os_set_proc_name(p);
+ }
+ }
+ break;
+ case QEMU_OPTION_prom_env:
+ if (nb_prom_envs >= MAX_PROM_ENVS) {
+ fprintf(stderr, "Too many prom variables\n");
+ exit(1);
+ }
+ prom_envs[nb_prom_envs] = optarg;
+ nb_prom_envs++;
+ break;
+ case QEMU_OPTION_old_param:
+ old_param = 1;
+ break;
+ case QEMU_OPTION_clock:
+ configure_alarms(optarg);
+ break;
+ case QEMU_OPTION_startdate:
+ configure_rtc_date_offset(optarg, 1);
+ break;
+ case QEMU_OPTION_rtc:
+ opts = qemu_opts_parse(qemu_find_opts("rtc"), optarg, 0);
+ if (!opts) {
+ exit(1);
+ }
+ configure_rtc(opts);
+ break;
+ case QEMU_OPTION_tb_size:
+ tb_size = strtol(optarg, NULL, 0);
+ if (tb_size < 0)
+ tb_size = 0;
+ break;
+ case QEMU_OPTION_icount:
+ icount_option = optarg;
+ break;
+ case QEMU_OPTION_incoming:
+ incoming = optarg;
+ incoming_expected = true;
+ break;
+ case QEMU_OPTION_nodefaults:
+ default_serial = 0;
+ default_parallel = 0;
+ default_virtcon = 0;
+ default_monitor = 0;
+ default_vga = 0;
+ default_net = 0;
+ default_floppy = 0;
+ default_cdrom = 0;
+ default_sdcard = 0;
+ break;
+#ifndef _WIN32
+ case QEMU_OPTION_nvram:
+ nvram = optarg;
+ break;
+#endif
+ case QEMU_OPTION_xen_domid:
+ if (!(xen_available())) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ xen_domid = atoi(optarg);
+ break;
+ case QEMU_OPTION_xen_create:
+ if (!(xen_available())) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ xen_mode = XEN_CREATE;
+ break;
+ case QEMU_OPTION_xen_attach:
+ if (!(xen_available())) {
+ printf("Option %s not supported for this target\n", popt->name);
+ exit(1);
+ }
+ xen_mode = XEN_ATTACH;
+ break;
+#ifdef CONFIG_SIMPLE_TRACE
+ case QEMU_OPTION_trace:
+ opts = qemu_opts_parse(qemu_find_opts("trace"), optarg, 0);
+ if (opts) {
+ trace_file = qemu_opt_get(opts, "file");
+ }
+ break;
+#endif
+ case QEMU_OPTION_readconfig:
+ {
+ int ret = qemu_read_config_file(optarg);
+ if (ret < 0) {
+ fprintf(stderr, "read config %s: %s\n", optarg,
+ strerror(-ret));
+ exit(1);
+ }
+ break;
+ }
+ case QEMU_OPTION_spice:
+ olist = qemu_find_opts("spice");
+ if (!olist) {
+ fprintf(stderr, "spice is not supported by this qemu build.\n");
+ exit(1);
+ }
+ opts = qemu_opts_parse(olist, optarg, 0);
+ if (!opts) {
+ fprintf(stderr, "parse error: %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case QEMU_OPTION_writeconfig:
+ {
+ FILE *fp;
+ if (strcmp(optarg, "-") == 0) {
+ fp = stdout;
+ } else {
+ fp = fopen(optarg, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "open %s: %s\n", optarg, strerror(errno));
+ exit(1);
+ }
+ }
+ qemu_config_write(fp);
+ fclose(fp);
+ break;
+ }
+ default:
+ os_parse_cmd_args(popt->index, optarg);
+ }
+ }
+ }
+ loc_set_none();
+
+ /* If no data_dir is specified then try to find it relative to the
+ executable path. */
+ if (!data_dir) {
+ data_dir = os_find_datadir(argv[0]);
+ }
+ /* If all else fails use the install patch specified when building. */
+ if (!data_dir) {
+ data_dir = CONFIG_QEMU_DATADIR;
+ }
+
+#ifdef CONFIG_SIMPLE_TRACE
+ /*
+ * Set the trace file name, if specified.
+ */
+ st_set_trace_file(trace_file);
+#endif
+ /*
+ * Default to max_cpus = smp_cpus, in case the user doesn't
+ * specify a max_cpus value.
+ */
+ if (!max_cpus)
+ max_cpus = smp_cpus;
+
+ machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */
+ if (smp_cpus > machine->max_cpus) {
+ fprintf(stderr, "Number of SMP cpus requested (%d), exceeds max cpus "
+ "supported by machine `%s' (%d)\n", smp_cpus, machine->name,
+ machine->max_cpus);
+ exit(1);
+ }
+
+ qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0);
+ qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0);
+
+ if (machine->no_serial) {
+ default_serial = 0;
+ }
+ if (machine->no_parallel) {
+ default_parallel = 0;
+ }
+ if (!machine->use_virtcon) {
+ default_virtcon = 0;
+ }
+ if (machine->no_vga) {
+ default_vga = 0;
+ }
+ if (machine->no_floppy) {
+ default_floppy = 0;
+ }
+ if (machine->no_cdrom) {
+ default_cdrom = 0;
+ }
+ if (machine->no_sdcard) {
+ default_sdcard = 0;
+ }
+
+ if (display_type == DT_NOGRAPHIC) {
+ if (default_parallel)
+ add_device_config(DEV_PARALLEL, "null");
+ if (default_serial && default_monitor) {
+ add_device_config(DEV_SERIAL, "mon:stdio");
+ } else if (default_virtcon && default_monitor) {
+ add_device_config(DEV_VIRTCON, "mon:stdio");
+ } else {
+ if (default_serial)
+ add_device_config(DEV_SERIAL, "stdio");
+ if (default_virtcon)
+ add_device_config(DEV_VIRTCON, "stdio");
+ if (default_monitor)
+ monitor_parse("stdio", "readline");
+ }
+ } else {
+ if (default_serial)
+ add_device_config(DEV_SERIAL, "vc:80Cx24C");
+ if (default_parallel)
+ add_device_config(DEV_PARALLEL, "vc:80Cx24C");
+ if (default_monitor)
+ monitor_parse("vc:80Cx24C", "readline");
+ if (default_virtcon)
+ add_device_config(DEV_VIRTCON, "vc:80Cx24C");
+ }
+ if (default_vga)
+ vga_interface_type = VGA_CIRRUS;
+
+ socket_init();
+
+ if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0)
+ exit(1);
+#ifdef CONFIG_VIRTFS
+ if (qemu_opts_foreach(qemu_find_opts("fsdev"), fsdev_init_func, NULL, 1) != 0) {
+ exit(1);
+ }
+#endif
+
+ os_daemonize();
+
+ if (pid_file && qemu_create_pidfile(pid_file) != 0) {
+ os_pidfile_error();
+ exit(1);
+ }
+
+ if (kvm_allowed) {
+ int ret = kvm_init();
+ if (ret < 0) {
+ if (kvm_allowed > 0) {
+ if (!kvm_available()) {
+ printf("KVM not supported for this target\n");
+ } else {
+ fprintf(stderr, "failed to initialize KVM: %s\n", strerror(-ret));
+ }
+ exit(1);
+ }
+ fprintf(stderr, "Could not initialize KVM, will disable KVM support\n");
+ }
+ kvm_allowed = ret >= 0;
+ }
+
+ if (qemu_init_main_loop()) {
+ fprintf(stderr, "qemu_init_main_loop failed\n");
+ exit(1);
+ }
+ linux_boot = (kernel_filename != NULL);
+
+ if (!linux_boot && *kernel_cmdline != '\0') {
+ fprintf(stderr, "-append only allowed with -kernel option\n");
+ exit(1);
+ }
+
+ if (!linux_boot && initrd_filename != NULL) {
+ fprintf(stderr, "-initrd only allowed with -kernel option\n");
+ exit(1);
+ }
+
+ os_set_line_buffering();
+
+ if (init_timer_alarm() < 0) {
+ fprintf(stderr, "could not initialize alarm timer\n");
+ exit(1);
+ }
+ configure_icount(icount_option);
+
+ if (net_init_clients() < 0) {
+ exit(1);
+ }
+
+ /* init the bluetooth world */
+ if (foreach_device_config(DEV_BT, bt_parse))
+ exit(1);
+
+ /* init the memory */
+ if (ram_size == 0)
+ ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;
+
+ /* init the dynamic translator */
+ cpu_exec_init_all(tb_size * 1024 * 1024);
+
+ bdrv_init_with_whitelist();
+
+ blk_mig_init();
+
+ /* open the virtual block devices */
+ if (snapshot)
+ qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0);
+ if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine->use_scsi, 1) != 0)
+ exit(1);
+
+ default_drive(default_cdrom, snapshot, machine->use_scsi,
+ IF_DEFAULT, 2, CDROM_OPTS);
+ default_drive(default_floppy, snapshot, machine->use_scsi,
+ IF_FLOPPY, 0, FD_OPTS);
+ default_drive(default_sdcard, snapshot, machine->use_scsi,
+ IF_SD, 0, SD_OPTS);
+
+ register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
+ ram_load, NULL);
+
+ if (nb_numa_nodes > 0) {
+ int i;
+
+ if (nb_numa_nodes > smp_cpus) {
+ nb_numa_nodes = smp_cpus;
+ }
+
+ /* If no memory size if given for any node, assume the default case
+ * and distribute the available memory equally across all nodes
+ */
+ for (i = 0; i < nb_numa_nodes; i++) {
+ if (node_mem[i] != 0)
+ break;
+ }
+ if (i == nb_numa_nodes) {
+ uint64_t usedmem = 0;
+
+ /* On Linux, the each node's border has to be 8MB aligned,
+ * the final node gets the rest.
+ */
+ for (i = 0; i < nb_numa_nodes - 1; i++) {
+ node_mem[i] = (ram_size / nb_numa_nodes) & ~((1 << 23UL) - 1);
+ usedmem += node_mem[i];
+ }
+ node_mem[i] = ram_size - usedmem;
+ }
+
+ for (i = 0; i < nb_numa_nodes; i++) {
+ if (node_cpumask[i] != 0)
+ break;
+ }
+ /* assigning the VCPUs round-robin is easier to implement, guest OSes
+ * must cope with this anyway, because there are BIOSes out there in
+ * real machines which also use this scheme.
+ */
+ if (i == nb_numa_nodes) {
+ for (i = 0; i < smp_cpus; i++) {
+ node_cpumask[i % nb_numa_nodes] |= 1 << i;
+ }
+ }
+ }
+
+ if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) {
+ exit(1);
+ }
+
+ if (foreach_device_config(DEV_SERIAL, serial_parse) < 0)
+ exit(1);
+ if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0)
+ exit(1);
+ if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0)
+ exit(1);
+ if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0)
+ exit(1);
+
+ module_call_init(MODULE_INIT_DEVICE);
+
+ if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) != 0)
+ exit(0);
+
+ if (watchdog) {
+ i = select_watchdog(watchdog);
+ if (i > 0)
+ exit (i == 1 ? 1 : 0);
+ }
+
+ if (machine->compat_props) {
+ qdev_prop_register_global_list(machine->compat_props);
+ }
+ qemu_add_globals();
+
+ machine->init(ram_size, boot_devices,
+ kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
+
+ cpu_synchronize_all_post_init();
+
+ /* must be after terminal init, SDL library changes signal handlers */
+ os_setup_signal_handling();
+
+ set_numa_modes();
+
+ current_machine = machine;
+
+ /* init USB devices */
+ if (usb_enabled) {
+ if (foreach_device_config(DEV_USB, usb_parse) < 0)
+ exit(1);
+ }
+
+ /* init generic devices */
+ if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0)
+ exit(1);
+
+ net_check_clients();
+
+ /* just use the first displaystate for the moment */
+ ds = get_displaystate();
+
+ if (using_spice)
+ display_remote++;
+ if (display_type == DT_DEFAULT && !display_remote) {
+#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
+ display_type = DT_SDL;
+#else
+ vnc_display = "localhost:0,to=99";
+ show_vnc_port = 1;
+#endif
+ }
+
+
+ /* init local displays */
+ switch (display_type) {
+ case DT_NOGRAPHIC:
+ break;
+#if defined(CONFIG_CURSES)
+ case DT_CURSES:
+ curses_display_init(ds, full_screen);
+ break;
+#endif
+#if defined(CONFIG_SDL)
+ case DT_SDL:
+ sdl_display_init(ds, full_screen, no_frame);
+ break;
+#elif defined(CONFIG_COCOA)
+ case DT_SDL:
+ cocoa_display_init(ds, full_screen);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /* init remote displays */
+ if (vnc_display) {
+ vnc_display_init(ds);
+ if (vnc_display_open(ds, vnc_display) < 0)
+ exit(1);
+
+ if (show_vnc_port) {
+ printf("VNC server running on `%s'\n", vnc_display_local_addr(ds));
+ }
+ }
+#ifdef CONFIG_SPICE
+ if (using_spice && !qxl_enabled) {
+ qemu_spice_display_init(ds);
+ }
+#endif
+
+ /* display setup */
+ dpy_resize(ds);
+ dcl = ds->listeners;
+ while (dcl != NULL) {
+ if (dcl->dpy_refresh != NULL) {
+ ds->gui_timer = qemu_new_timer(rt_clock, gui_update, ds);
+ qemu_mod_timer(ds->gui_timer, qemu_get_clock(rt_clock));
+ break;
+ }
+ dcl = dcl->next;
+ }
+ if (ds->gui_timer == NULL) {
+ nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
+ qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
+ }
+ text_consoles_set_display(ds);
+
+ if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) {
+ fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n",
+ gdbstub_dev);
+ exit(1);
+ }
+
+ qdev_machine_creation_done();
+
+ if (rom_load_all() != 0) {
+ fprintf(stderr, "rom loading failed\n");
+ exit(1);
+ }
+
+ /* TODO: once all bus devices are qdevified, this should be done
+ * when bus is created by qdev.c */
+ qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
+ qemu_run_machine_init_done_notifiers();
+
+ qemu_system_reset();
+ if (loadvm) {
+ if (load_vmstate(loadvm) < 0) {
+ autostart = 0;
+ }
+ }
+
+ if (incoming) {
+ int ret = qemu_start_incoming_migration(incoming);
+ if (ret < 0) {
+ fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n",
+ incoming, ret);
+ exit(ret);
+ }
+ } else if (autostart) {
+ vm_start();
+ }
+
+ os_setup_post();
+
+ main_loop();
+ quit_timers();
+ net_cleanup();
+
+ return 0;
+}
diff --git a/x86_64.ld b/x86_64.ld
new file mode 100644
index 0000000..46d8d4d
--- /dev/null
+++ b/x86_64.ld
@@ -0,0 +1,182 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x60000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt :
+ {
+ *(.rel.plt)
+ PROVIDE_HIDDEN (__rel_iplt_start = .);
+ *(.rel.iplt)
+ PROVIDE_HIDDEN (__rel_iplt_end = .);
+ }
+ .rela.plt :
+ {
+ *(.rela.plt)
+ PROVIDE_HIDDEN (__rela_iplt_start = .);
+ *(.rela.iplt)
+ PROVIDE_HIDDEN (__rela_iplt_end = .);
+ }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x90909090
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x90909090
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x90909090
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN (0x100000) - ((0x100000 - .) & (0x100000 - 1)); . = DATA_SEGMENT_ALIGN (0x100000, 0x1000);
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(64 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { *(.preinit_array) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { *(.init_array) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { *(.fini_array) }
+ PROVIDE (__fini_array_end = .);
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table) }
+ .dynamic : { *(.dynamic) }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .got : { *(.got.plt) *(.got) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(64 / 8);
+ }
+ . = ALIGN(64 / 8);
+ _end = .;
+ PROVIDE (end = .);
+ . = DATA_SEGMENT_END (.);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+}